[Impeller] Document ReactorGLES. (flutter/engine#47070)

This commit is contained in:
Chinmay Garde 2023-10-18 15:13:22 -07:00 committed by GitHub
parent 688a695f2b
commit 34497469a9

View File

@ -15,43 +15,207 @@
namespace impeller {
//------------------------------------------------------------------------------
/// @brief The reactor attempts to make thread-safe usage of OpenGL ES
/// easier to reason about.
///
/// In the other Impeller backends (like Metal and Vulkan),
/// resources can be created, used, and deleted on any thread with
/// relatively few restrictions. However, OpenGL resources can only
/// be created, used, and deleted on a thread on which an OpenGL
/// context (or one in the same sharegroup) is current.
///
/// There aren't too many OpenGL contexts to go around and making
/// the caller reason about the timing and threading requirement
/// only when the OpenGL backend is in use is tedious. To work
/// around this tedium, there is an abstraction between the
/// resources and their handles in OpenGL. The reactor is this
/// abstraction.
///
/// The reactor is thread-safe and can created, used, and collected
/// on any thread.
///
/// Reactor handles `HandleGLES` can be created, used, and collected
/// on any thread. These handles can be to textures, buffers, etc..
///
/// Operations added to the reactor are guaranteed to run on a
/// worker within a finite amount of time unless the reactor itself
/// is torn down or there are no workers. These operations may run
/// on the calling thread immediately if a worker is active on the
/// current thread and can perform reactions. The operations are
/// guaranteed to run with an OpenGL context current and all reactor
/// handles having live OpenGL handle counterparts.
///
/// Creating a handle in the reactor doesn't mean an OpenGL handle
/// is created immediately. OpenGL handles become live before the
/// next reaction. Similarly, dropping the last reference to a
/// reactor handle means that the OpenGL handle will be deleted at
/// some point in the near future.
///
class ReactorGLES {
public:
using WorkerID = UniqueID;
//----------------------------------------------------------------------------
/// @brief A delegate implemented by a thread on which an OpenGL context
/// is current. There may be multiple workers for the reactor to
/// perform reactions on. In that case, it is the workers
/// responsibility to ensure that all of them use either the same
/// OpenGL context or multiple OpenGL contexts in the same
/// sharegroup.
///
class Worker {
public:
virtual ~Worker() = default;
//--------------------------------------------------------------------------
/// @brief Determines the ability of the worker to service a reaction
/// on the current thread. The OpenGL context must be current on
/// the thread if the worker says it is able to service a
/// reaction.
///
/// @param[in] reactor The reactor
///
/// @return If the worker is able to service a reaction. The reactor
/// assumes the context is already current if true.
///
virtual bool CanReactorReactOnCurrentThreadNow(
const ReactorGLES& reactor) const = 0;
};
using Ref = std::shared_ptr<ReactorGLES>;
//----------------------------------------------------------------------------
/// @brief Create a new reactor. There are expensive and only one per
/// application instance is necessary.
///
/// @param[in] gl The proc table for GL access. This is necessary for the
/// reactor to be able to create and collect OpenGL handles.
///
explicit ReactorGLES(std::unique_ptr<ProcTableGLES> gl);
//----------------------------------------------------------------------------
/// @brief Destroy a reactor.
///
~ReactorGLES();
//----------------------------------------------------------------------------
/// @brief If this is a valid reactor. Invalid reactors must be discarded
/// immediately.
///
/// @return If this reactor is valid.
///
bool IsValid() const;
//----------------------------------------------------------------------------
/// @brief Adds a worker to the reactor. Each new worker must ensure that
/// the context it manages is the same as the other workers in the
/// reactor or in the same sharegroup.
///
/// @param[in] worker The worker
///
/// @return The worker identifier. This identifier can be used to remove
/// the worker from the reactor later.
///
WorkerID AddWorker(std::weak_ptr<Worker> worker);
bool RemoveWorker(WorkerID);
//----------------------------------------------------------------------------
/// @brief Remove a previously added worker from the reactor. If the
/// reactor has no workers, pending added operations will never
/// run.
///
/// @param[in] id The worker identifier previously returned by `AddWorker`.
///
/// @return If a worker with the given identifer was successfully removed
/// from the reactor.
///
bool RemoveWorker(WorkerID id);
//----------------------------------------------------------------------------
/// @brief Get the OpenGL proc. table the reactor uses to manage handles.
///
/// @return The proc table.
///
const ProcTableGLES& GetProcTable() const;
//----------------------------------------------------------------------------
/// @brief Returns the OpenGL handle for a reactor handle if one is
/// available. This is typically only safe to call within a
/// reaction. That is, within a `ReactorGLES::Operation`.
///
/// Asking for the OpenGL handle before the reactor has a chance
/// to reactor will return `std::nullopt`.
///
/// This can be called on any thread but is typically useless
/// outside of a reaction since the handle is useless outside of a
/// reactor operation.
///
/// @param[in] handle The reactor handle.
///
/// @return The OpenGL handle if the reactor has had a chance to react.
/// `std::nullopt` otherwise.
///
std::optional<GLuint> GetGLHandle(const HandleGLES& handle) const;
//----------------------------------------------------------------------------
/// @brief Create a reactor handle.
///
/// This can be called on any thread. Even one that doesn't have
/// an OpenGL context.
///
/// @param[in] type The type of handle to create.
///
/// @return The reactor handle.
///
HandleGLES CreateHandle(HandleType type);
//----------------------------------------------------------------------------
/// @brief Collect a reactor handle.
///
/// This can be called on any thread. Even one that doesn't have
/// an OpenGL context.
///
/// @param[in] handle The reactor handle handle
///
void CollectHandle(HandleGLES handle);
//----------------------------------------------------------------------------
/// @brief Set the debug label on a reactor handle.
///
/// This call ensures that the OpenGL debug label is propagated to
/// even the OpenGL handle hasn't been created at the time the
/// caller sets the label.
///
/// @param[in] handle The handle
/// @param[in] label The label
///
void SetDebugLabel(const HandleGLES& handle, std::string label);
using Operation = std::function<void(const ReactorGLES& reactor)>;
//----------------------------------------------------------------------------
/// @brief Adds an operation that the reactor runs on a worker that
/// ensures that an OpenGL context is current.
///
/// This operation is not guaranteed to run immediately. It will
/// complete in a finite amount of time on any thread as long as
/// there is a reactor worker and the reactor itself is not being
/// torn down.
///
/// @param[in] operation The operation
///
/// @return If the operation was successfully queued for completion.
///
[[nodiscard]] bool AddOperation(Operation operation);
//----------------------------------------------------------------------------
/// @brief Perform a reaction on the current thread if able.
///
/// It is safe to call this simultaneously from multiple threads
/// at the same time.
///
/// @return If a reaction was performed on the calling thread.
///
[[nodiscard]] bool React();
private: