[Impeller] Document the EGL toolkit. (#52469)
No change in functionality. Just adds docs.
diff --git a/impeller/toolkit/egl/config.h b/impeller/toolkit/egl/config.h
index 45ae789..26acbac 100644
--- a/impeller/toolkit/egl/config.h
+++ b/impeller/toolkit/egl/config.h
@@ -52,10 +52,17 @@
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 @@
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 c9511ce..446265f 100644
--- a/impeller/toolkit/egl/context.h
+++ b/impeller/toolkit/egl/context.h
@@ -16,19 +16,57 @@
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 @@
kWillClearCurrent,
};
using LifecycleListener = std::function<void(LifecycleEvent)>;
+ //----------------------------------------------------------------------------
+ /// @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<UniqueID> 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<UniqueID, LifecycleListener> 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 7cabc31..fed9b17 100644
--- a/impeller/toolkit/egl/display.cc
+++ b/impeller/toolkit/egl/display.cc
@@ -66,7 +66,7 @@
return nullptr;
}
- return std::make_unique<Context>(display_, context);
+ return std::unique_ptr<Context>(new Context(display_, context));
}
std::unique_ptr<Config> Display::ChooseConfig(ConfigDescriptor config) const {
@@ -182,7 +182,7 @@
IMPELLER_LOG_EGL_ERROR;
return nullptr;
}
- return std::make_unique<Surface>(display_, surface);
+ return std::unique_ptr<Surface>(new Surface(display_, surface));
}
std::unique_ptr<Surface> Display::CreatePixelBufferSurface(const Config& config,
@@ -203,7 +203,7 @@
IMPELLER_LOG_EGL_ERROR;
return nullptr;
}
- return std::make_unique<Surface>(display_, surface);
+ return std::unique_ptr<Surface>(new Surface(display_, surface));
}
} // namespace egl
diff --git a/impeller/toolkit/egl/display.h b/impeller/toolkit/egl/display.h
index 3e9b623..9f79828 100644
--- a/impeller/toolkit/egl/display.h
+++ b/impeller/toolkit/egl/display.h
@@ -18,23 +18,83 @@
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<Config> 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<Context> 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<Surface> 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<Surface>
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 fc22e1c..fd38811 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<void*(const char*)> CreateProcAddressResolver();
#define IMPELLER_LOG_EGL_ERROR LogEGLError(__FILE__, __LINE__);
diff --git a/impeller/toolkit/egl/surface.h b/impeller/toolkit/egl/surface.h
index a8ca441..5cc5816 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;