diff --git a/impeller/renderer/backend/gles/reactor_gles.h b/impeller/renderer/backend/gles/reactor_gles.h index 801542b4fb27a..3715e47a14fd8 100644 --- a/impeller/renderer/backend/gles/reactor_gles.h +++ b/impeller/renderer/backend/gles/reactor_gles.h @@ -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; + //---------------------------------------------------------------------------- + /// @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 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); - 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 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; + + //---------------------------------------------------------------------------- + /// @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: