diff --git a/impeller/toolkit/egl/config.h b/impeller/toolkit/egl/config.h index 45ae789e9ed2b..26acbac868c4f 100644 --- a/impeller/toolkit/egl/config.h +++ b/impeller/toolkit/egl/config.h @@ -52,10 +52,17 @@ struct ConfigDescriptor { SurfaceType surface_type = SurfaceType::kPBuffer; }; +class Display; + +//------------------------------------------------------------------------------ +/// @brief An EGL config. These are returned by the display to indicate +/// support for a specific config descriptor. +/// +/// There is no ability to construct these manually except for +/// testing. +/// class Config { public: - Config(ConfigDescriptor descriptor, EGLConfig config); - ~Config(); bool IsValid() const; @@ -64,6 +71,9 @@ class Config { const EGLConfig& GetHandle() const; + // Do not use. Only for testing. + Config(ConfigDescriptor descriptor, EGLConfig config); + private: const ConfigDescriptor desc_; EGLConfig config_ = nullptr; diff --git a/impeller/toolkit/egl/context.h b/impeller/toolkit/egl/context.h index c9511ce351832..446265f8991c9 100644 --- a/impeller/toolkit/egl/context.h +++ b/impeller/toolkit/egl/context.h @@ -16,19 +16,57 @@ namespace impeller { namespace egl { class Surface; - +class Display; + +//------------------------------------------------------------------------------ +/// @brief An instance of an EGL context. +/// +/// An EGL context can only be used on a single thread at a given +/// time. A thread can only have a single context current at any +/// given time. +/// +/// Context cannot be created directly. Only a valid instance of an +/// egl::Display can create a context. +/// class Context { public: - Context(EGLDisplay display, EGLContext context); - ~Context(); + //---------------------------------------------------------------------------- + /// @brief Determines if a valid context could be created. The context + /// still needs to be made current on the thread for it to be + /// useful. + /// + /// @return True if valid, False otherwise. + /// bool IsValid() const; + //---------------------------------------------------------------------------- + /// @brief Get the underlying handle to the EGL context. + /// + /// @return The handle. + /// const EGLContext& GetHandle() const; + //---------------------------------------------------------------------------- + /// @brief Make the context current on the calling thread. It is the + /// caller responsibility to ensure that any context previously + /// current on the thread must be cleared via `ClearCurrent`. + /// + /// @important The config used to create the surface must match the config + /// used to create this context instance. + /// + /// @param[in] surface The surface to use to make the context current. + /// + /// @return If the context could be made current on the callers thread. + /// bool MakeCurrent(const Surface& surface) const; + //---------------------------------------------------------------------------- + /// @brief Clear the thread association of this context. + /// + /// @return If the thread association could be cleared. + /// bool ClearCurrent() const; enum class LifecycleEvent { @@ -36,18 +74,43 @@ class Context { kWillClearCurrent, }; using LifecycleListener = std::function; + //---------------------------------------------------------------------------- + /// @brief Add a listener that gets invoked when the context is made and + /// cleared current from the thread. Applications typically use + /// this to manage workers that schedule OpenGL API calls that + /// need to be careful about the context being current when + /// called. + /// + /// @param[in] listener The listener + /// + /// @return A unique ID for the listener that can used used in + /// `RemoveLifecycleListener` to remove a previously added + /// listener. + /// std::optional AddLifecycleListener( const LifecycleListener& listener); + //---------------------------------------------------------------------------- + /// @brief Remove a previously added context listener. + /// + /// @param[in] id The identifier obtained via a previous call to + /// `AddLifecycleListener`. + /// + /// @return True if the listener could be removed. + /// bool RemoveLifecycleListener(UniqueID id); private: + friend class Display; + EGLDisplay display_ = EGL_NO_DISPLAY; EGLContext context_ = EGL_NO_CONTEXT; mutable RWMutex listeners_mutex_; std::map listeners_ IPLR_GUARDED_BY( listeners_mutex_); + Context(EGLDisplay display, EGLContext context); + void DispatchLifecyleEvent(LifecycleEvent event) const; Context(const Context&) = delete; diff --git a/impeller/toolkit/egl/display.cc b/impeller/toolkit/egl/display.cc index 7cabc314e65dd..fed9b1787d833 100644 --- a/impeller/toolkit/egl/display.cc +++ b/impeller/toolkit/egl/display.cc @@ -66,7 +66,7 @@ std::unique_ptr Display::CreateContext(const Config& config, return nullptr; } - return std::make_unique(display_, context); + return std::unique_ptr(new Context(display_, context)); } std::unique_ptr Display::ChooseConfig(ConfigDescriptor config) const { @@ -182,7 +182,7 @@ std::unique_ptr Display::CreateWindowSurface( IMPELLER_LOG_EGL_ERROR; return nullptr; } - return std::make_unique(display_, surface); + return std::unique_ptr(new Surface(display_, surface)); } std::unique_ptr Display::CreatePixelBufferSurface(const Config& config, @@ -203,7 +203,7 @@ std::unique_ptr Display::CreatePixelBufferSurface(const Config& config, IMPELLER_LOG_EGL_ERROR; return nullptr; } - return std::make_unique(display_, surface); + return std::unique_ptr(new Surface(display_, surface)); } } // namespace egl diff --git a/impeller/toolkit/egl/display.h b/impeller/toolkit/egl/display.h index 3e9b6236c676d..9f798283e5f14 100644 --- a/impeller/toolkit/egl/display.h +++ b/impeller/toolkit/egl/display.h @@ -18,23 +18,83 @@ namespace egl { class Context; class Surface; +//------------------------------------------------------------------------------ +/// @brief A connection to an EGL display. Only one connection per +/// application instance is sufficient. +/// +/// The display connection is used to first choose a config from +/// among the available, create a context from that config, and then +/// use that context with a surface on one (and only one) thread at +/// a time. +/// class Display { public: Display(); virtual ~Display(); + //---------------------------------------------------------------------------- + /// @return True if the display connection is valid. + /// virtual bool IsValid() const; + //---------------------------------------------------------------------------- + /// @brief Choose a config that most closely matches a given descriptor. + /// If there are no matches, this method returns `nullptr`. + /// + /// @param[in] config The configuration + /// + /// @return A config that matches a descriptor if one is available. + /// `nullptr` otherwise. + /// virtual std::unique_ptr ChooseConfig(ConfigDescriptor config) const; + //---------------------------------------------------------------------------- + /// @brief Create a context with a supported config. The supported config + /// can be obtained via a successful call to `ChooseConfig`. + /// + /// @param[in] config The configuration. + /// @param[in] share_context The share context. Context within the same + /// share-group use the same handle table. The + /// contexts should still only be used exclusively + /// on each thread however. + /// + /// @return A context if one can be created. `nullptr` otherwise. + /// virtual std::unique_ptr CreateContext(const Config& config, const Context* share_context); + //---------------------------------------------------------------------------- + /// @brief Create a window surface. The window is an opaque pointer whose + /// value value is platform specific. For instance, ANativeWindow + /// on Android. + /// + /// @param[in] config A valid configuration. One can be obtained via + /// `ChooseConfig`. + /// @param[in] window An opaque pointer to a platform specific window + /// handle. + /// + /// @return A valid window surface if one can be created. `nullptr` + /// otherwise. + /// virtual std::unique_ptr CreateWindowSurface( const Config& config, EGLNativeWindowType window); + //---------------------------------------------------------------------------- + /// @brief Create an offscreen pixelbuffer surface. These are of limited + /// use except in the context where applications need to render to + /// a texture in an offscreen context. In such cases, a 1x1 pixel + /// buffer surface is created to obtain a surface that can be used + /// to make the context current on the background thread. + /// + /// @param[in] config The configuration + /// @param[in] width The width + /// @param[in] height The height + /// + /// @return A valid pixel buffer surface if one can be created. `nullptr` + /// otherwise. + /// virtual std::unique_ptr CreatePixelBufferSurface(const Config& config, size_t width, size_t height); diff --git a/impeller/toolkit/egl/egl.h b/impeller/toolkit/egl/egl.h index fc22e1cdf002d..fd388113a5a90 100644 --- a/impeller/toolkit/egl/egl.h +++ b/impeller/toolkit/egl/egl.h @@ -14,6 +14,12 @@ namespace impeller { namespace egl { +//------------------------------------------------------------------------------ +/// @brief Creates a proc address resolver that resolves function pointers +/// to EGL and OpenGL (ES) procs. +/// +/// @return The resolver if one can be created. +/// std::function CreateProcAddressResolver(); #define IMPELLER_LOG_EGL_ERROR LogEGLError(__FILE__, __LINE__); diff --git a/impeller/toolkit/egl/surface.h b/impeller/toolkit/egl/surface.h index a8ca441660733..5cc581666a38a 100644 --- a/impeller/toolkit/egl/surface.h +++ b/impeller/toolkit/egl/surface.h @@ -11,22 +11,41 @@ namespace impeller { namespace egl { +//------------------------------------------------------------------------------ +/// @brief An instance of an EGL surface. There is no ability to create +/// surfaces directly. Instead, one must be created using a Display +/// connection. +/// class Surface { public: - Surface(EGLDisplay display, EGLSurface surface); - ~Surface(); + //---------------------------------------------------------------------------- + /// @return True if this is a valid surface. + /// bool IsValid() const; + //---------------------------------------------------------------------------- + /// @return Get the handle to the underlying surface. + /// const EGLSurface& GetHandle() const; + //---------------------------------------------------------------------------- + /// @brief Present the surface. For an offscreen pixel buffer surface, + /// this is a no-op. + /// + /// @return True if the surface could be presented. + /// bool Present() const; private: + friend class Display; + EGLDisplay display_ = EGL_NO_DISPLAY; EGLSurface surface_ = EGL_NO_SURFACE; + Surface(EGLDisplay display, EGLSurface surface); + Surface(const Surface&) = delete; Surface& operator=(const Surface&) = delete;