Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions impeller/toolkit/egl/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down
69 changes: 66 additions & 3 deletions impeller/toolkit/egl/context.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,38 +16,101 @@ 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 {
kDidMakeCurrent,
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;
Expand Down
6 changes: 3 additions & 3 deletions impeller/toolkit/egl/display.cc
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ std::unique_ptr<Context> Display::CreateContext(const Config& config,
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 {
Expand Down Expand Up @@ -182,7 +182,7 @@ std::unique_ptr<Surface> Display::CreateWindowSurface(
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,
Expand All @@ -203,7 +203,7 @@ std::unique_ptr<Surface> Display::CreatePixelBufferSurface(const Config& config,
IMPELLER_LOG_EGL_ERROR;
return nullptr;
}
return std::make_unique<Surface>(display_, surface);
return std::unique_ptr<Surface>(new Surface(display_, surface));
}

} // namespace egl
Expand Down
60 changes: 60 additions & 0 deletions impeller/toolkit/egl/display.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<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);

Expand Down
6 changes: 6 additions & 0 deletions impeller/toolkit/egl/egl.h
Original file line number Diff line number Diff line change
Expand Up @@ -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__);
Expand Down
23 changes: 21 additions & 2 deletions impeller/toolkit/egl/surface.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down