Flutter Impeller
reactor_gles.h
Go to the documentation of this file.
1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef FLUTTER_IMPELLER_RENDERER_BACKEND_GLES_REACTOR_GLES_H_
6 #define FLUTTER_IMPELLER_RENDERER_BACKEND_GLES_REACTOR_GLES_H_
7 
8 #include <functional>
9 #include <memory>
10 #include <vector>
11 
12 #include "impeller/base/thread.h"
15 
16 namespace impeller {
17 
18 //------------------------------------------------------------------------------
19 /// @brief The reactor attempts to make thread-safe usage of OpenGL ES
20 /// easier to reason about.
21 ///
22 /// In the other Impeller backends (like Metal and Vulkan),
23 /// resources can be created, used, and deleted on any thread with
24 /// relatively few restrictions. However, OpenGL resources can only
25 /// be created, used, and deleted on a thread on which an OpenGL
26 /// context (or one in the same sharegroup) is current.
27 ///
28 /// There aren't too many OpenGL contexts to go around and making
29 /// the caller reason about the timing and threading requirement
30 /// only when the OpenGL backend is in use is tedious. To work
31 /// around this tedium, there is an abstraction between the
32 /// resources and their handles in OpenGL. The reactor is this
33 /// abstraction.
34 ///
35 /// The reactor is thread-safe and can created, used, and collected
36 /// on any thread.
37 ///
38 /// Reactor handles `HandleGLES` can be created, used, and collected
39 /// on any thread. These handles can be to textures, buffers, etc..
40 ///
41 /// Operations added to the reactor are guaranteed to run on a
42 /// worker within a finite amount of time unless the reactor itself
43 /// is torn down or there are no workers. These operations may run
44 /// on the calling thread immediately if a worker is active on the
45 /// current thread and can perform reactions. The operations are
46 /// guaranteed to run with an OpenGL context current and all reactor
47 /// handles having live OpenGL handle counterparts.
48 ///
49 /// Creating a handle in the reactor doesn't mean an OpenGL handle
50 /// is created immediately. OpenGL handles become live before the
51 /// next reaction. Similarly, dropping the last reference to a
52 /// reactor handle means that the OpenGL handle will be deleted at
53 /// some point in the near future.
54 ///
55 class ReactorGLES {
56  public:
57  using WorkerID = UniqueID;
58 
59  //----------------------------------------------------------------------------
60  /// @brief A delegate implemented by a thread on which an OpenGL context
61  /// is current. There may be multiple workers for the reactor to
62  /// perform reactions on. In that case, it is the workers
63  /// responsibility to ensure that all of them use either the same
64  /// OpenGL context or multiple OpenGL contexts in the same
65  /// sharegroup.
66  ///
67  class Worker {
68  public:
69  virtual ~Worker() = default;
70 
71  //--------------------------------------------------------------------------
72  /// @brief Determines the ability of the worker to service a reaction
73  /// on the current thread. The OpenGL context must be current on
74  /// the thread if the worker says it is able to service a
75  /// reaction.
76  ///
77  /// @param[in] reactor The reactor
78  ///
79  /// @return If the worker is able to service a reaction. The reactor
80  /// assumes the context is already current if true.
81  ///
83  const ReactorGLES& reactor) const = 0;
84  };
85 
86  using Ref = std::shared_ptr<ReactorGLES>;
87 
88  //----------------------------------------------------------------------------
89  /// @brief Create a new reactor. There are expensive and only one per
90  /// application instance is necessary.
91  ///
92  /// @param[in] gl The proc table for GL access. This is necessary for the
93  /// reactor to be able to create and collect OpenGL handles.
94  ///
95  explicit ReactorGLES(std::unique_ptr<ProcTableGLES> gl);
96 
97  //----------------------------------------------------------------------------
98  /// @brief Destroy a reactor.
99  ///
100  ~ReactorGLES();
101 
102  //----------------------------------------------------------------------------
103  /// @brief If this is a valid reactor. Invalid reactors must be discarded
104  /// immediately.
105  ///
106  /// @return If this reactor is valid.
107  ///
108  bool IsValid() const;
109 
110  //----------------------------------------------------------------------------
111  /// @brief Adds a worker to the reactor. Each new worker must ensure that
112  /// the context it manages is the same as the other workers in the
113  /// reactor or in the same sharegroup.
114  ///
115  /// @param[in] worker The worker
116  ///
117  /// @return The worker identifier. This identifier can be used to remove
118  /// the worker from the reactor later.
119  ///
120  WorkerID AddWorker(std::weak_ptr<Worker> worker);
121 
122  //----------------------------------------------------------------------------
123  /// @brief Remove a previously added worker from the reactor. If the
124  /// reactor has no workers, pending added operations will never
125  /// run.
126  ///
127  /// @param[in] id The worker identifier previously returned by `AddWorker`.
128  ///
129  /// @return If a worker with the given identifer was successfully removed
130  /// from the reactor.
131  ///
132  bool RemoveWorker(WorkerID id);
133 
134  //----------------------------------------------------------------------------
135  /// @brief Get the OpenGL proc. table the reactor uses to manage handles.
136  ///
137  /// @return The proc table.
138  ///
139  const ProcTableGLES& GetProcTable() const;
140 
141  //----------------------------------------------------------------------------
142  /// @brief Returns the OpenGL handle for a reactor handle if one is
143  /// available. This is typically only safe to call within a
144  /// reaction. That is, within a `ReactorGLES::Operation`.
145  ///
146  /// Asking for the OpenGL handle before the reactor has a chance
147  /// to reactor will return `std::nullopt`.
148  ///
149  /// This can be called on any thread but is typically useless
150  /// outside of a reaction since the handle is useless outside of a
151  /// reactor operation.
152  ///
153  /// @param[in] handle The reactor handle.
154  ///
155  /// @return The OpenGL handle if the reactor has had a chance to react.
156  /// `std::nullopt` otherwise.
157  ///
158  std::optional<GLuint> GetGLHandle(const HandleGLES& handle) const;
159 
160  //----------------------------------------------------------------------------
161  /// @brief Create a reactor handle.
162  ///
163  /// This can be called on any thread. Even one that doesn't have
164  /// an OpenGL context.
165  ///
166  /// @param[in] type The type of handle to create.
167  /// @param[in] external_handle An already created GL handle if one exists.
168  ///
169  /// @return The reactor handle.
170  ///
171  HandleGLES CreateHandle(HandleType type, GLuint external_handle = GL_NONE);
172 
173  //----------------------------------------------------------------------------
174  /// @brief Collect a reactor handle.
175  ///
176  /// This can be called on any thread. Even one that doesn't have
177  /// an OpenGL context.
178  ///
179  /// @param[in] handle The reactor handle handle
180  ///
181  void CollectHandle(HandleGLES handle);
182 
183  //----------------------------------------------------------------------------
184  /// @brief Set the debug label on a reactor handle.
185  ///
186  /// This call ensures that the OpenGL debug label is propagated to
187  /// even the OpenGL handle hasn't been created at the time the
188  /// caller sets the label.
189  ///
190  /// @param[in] handle The handle
191  /// @param[in] label The label
192  ///
193  void SetDebugLabel(const HandleGLES& handle, std::string label);
194 
195  using Operation = std::function<void(const ReactorGLES& reactor)>;
196 
197  //----------------------------------------------------------------------------
198  /// @brief Adds an operation that the reactor runs on a worker that
199  /// ensures that an OpenGL context is current.
200  ///
201  /// This operation is not guaranteed to run immediately. It will
202  /// complete in a finite amount of time on any thread as long as
203  /// there is a reactor worker and the reactor itself is not being
204  /// torn down.
205  ///
206  /// @param[in] operation The operation
207  ///
208  /// @return If the operation was successfully queued for completion.
209  ///
210  [[nodiscard]] bool AddOperation(Operation operation);
211 
212  //----------------------------------------------------------------------------
213  /// @brief Perform a reaction on the current thread if able.
214  ///
215  /// It is safe to call this simultaneously from multiple threads
216  /// at the same time.
217  ///
218  /// @return If a reaction was performed on the calling thread.
219  ///
220  [[nodiscard]] bool React();
221 
222  private:
223  struct LiveHandle {
224  std::optional<GLuint> name;
225  std::optional<std::string> pending_debug_label;
226  bool pending_collection = false;
227 
228  LiveHandle() = default;
229 
230  explicit LiveHandle(std::optional<GLuint> p_name) : name(p_name) {}
231 
232  constexpr bool IsLive() const { return name.has_value(); }
233  };
234 
235  std::unique_ptr<ProcTableGLES> proc_table_;
236 
237  Mutex ops_execution_mutex_;
238  mutable Mutex ops_mutex_;
239  std::vector<Operation> ops_ IPLR_GUARDED_BY(ops_mutex_);
240 
241  // Make sure the container is one where erasing items during iteration doesn't
242  // invalidate other iterators.
243  using LiveHandles = std::unordered_map<HandleGLES,
244  LiveHandle,
245  HandleGLES::Hash,
246  HandleGLES::Equal>;
247  mutable RWMutex handles_mutex_;
248  LiveHandles handles_ IPLR_GUARDED_BY(handles_mutex_);
249 
250  mutable Mutex workers_mutex_;
251  mutable std::map<WorkerID, std::weak_ptr<Worker>> workers_ IPLR_GUARDED_BY(
252  workers_mutex_);
253 
254  bool can_set_debug_labels_ = false;
255  bool is_valid_ = false;
256 
257  bool ReactOnce() IPLR_REQUIRES(ops_execution_mutex_);
258 
259  bool HasPendingOperations() const;
260 
261  bool CanReactOnCurrentThread() const;
262 
263  bool ConsolidateHandles();
264 
265  bool FlushOps();
266 
267  void SetupDebugGroups();
268 
269  ReactorGLES(const ReactorGLES&) = delete;
270 
271  ReactorGLES& operator=(const ReactorGLES&) = delete;
272 };
273 
274 } // namespace impeller
275 
276 #endif // FLUTTER_IMPELLER_RENDERER_BACKEND_GLES_REACTOR_GLES_H_
impeller::ReactorGLES::Operation
std::function< void(const ReactorGLES &reactor)> Operation
Definition: reactor_gles.h:195
impeller::ReactorGLES::GetProcTable
const ProcTableGLES & GetProcTable() const
Get the OpenGL proc. table the reactor uses to manage handles.
Definition: reactor_gles.cc:48
impeller::ReactorGLES::ReactorGLES
ReactorGLES(std::unique_ptr< ProcTableGLES > gl)
Create a new reactor. There are expensive and only one per application instance is necessary.
Definition: reactor_gles.cc:15
impeller::ReactorGLES::CreateHandle
HandleGLES CreateHandle(HandleType type, GLuint external_handle=GL_NONE)
Create a reactor handle.
Definition: reactor_gles.cc:133
impeller::ReactorGLES::SetDebugLabel
void SetDebugLabel(const HandleGLES &handle, std::string label)
Set the debug label on a reactor handle.
Definition: reactor_gles.cc:278
impeller::ReactorGLES::Worker::CanReactorReactOnCurrentThreadNow
virtual bool CanReactorReactOnCurrentThreadNow(const ReactorGLES &reactor) const =0
Determines the ability of the worker to service a reaction on the current thread. The OpenGL context ...
impeller::ReactorGLES::Ref
std::shared_ptr< ReactorGLES > Ref
Definition: reactor_gles.h:86
impeller::HandleType
HandleType
Definition: handle_gles.h:18
impeller::ReactorGLES::Worker::~Worker
virtual ~Worker()=default
impeller::ReactorGLES::Worker
A delegate implemented by a thread on which an OpenGL context is current. There may be multiple worke...
Definition: reactor_gles.h:67
impeller::ReactorGLES::React
bool React()
Perform a reaction on the current thread if able.
Definition: reactor_gles.cc:160
impeller::ReactorGLES::~ReactorGLES
~ReactorGLES()
Destroy a reactor.
type
GLenum type
Definition: blit_command_gles.cc:127
impeller::HandleGLES
Represents a handle to an underlying OpenGL object. Unlike OpenGL object handles, these handles can b...
Definition: handle_gles.h:36
impeller::ProcTableGLES
Definition: proc_table_gles.h:226
impeller::ReactorGLES::IsValid
bool IsValid() const
If this is a valid reactor. Invalid reactors must be discarded immediately.
Definition: reactor_gles.cc:27
proc_table_gles.h
impeller::ReactorGLES::RemoveWorker
bool RemoveWorker(WorkerID id)
Remove a previously added worker from the reactor. If the reactor has no workers, pending added opera...
Definition: reactor_gles.cc:38
impeller::ReactorGLES::CollectHandle
void CollectHandle(HandleGLES handle)
Collect a reactor handle.
Definition: reactor_gles.cc:153
impeller::ReactorGLES::AddOperation
bool AddOperation(Operation operation)
Adds an operation that the reactor runs on a worker that ensures that an OpenGL context is current.
Definition: reactor_gles.cc:71
impeller::ReactorGLES::AddWorker
WorkerID AddWorker(std::weak_ptr< Worker > worker)
Adds a worker to the reactor. Each new worker must ensure that the context it manages is the same as ...
Definition: reactor_gles.cc:31
handle_gles.h
impeller::ReactorGLES
The reactor attempts to make thread-safe usage of OpenGL ES easier to reason about.
Definition: reactor_gles.h:55
impeller::UniqueID
Definition: comparable.h:16
thread.h
impeller
Definition: allocation.cc:12
impeller::ReactorGLES::GetGLHandle
std::optional< GLuint > GetGLHandle(const HandleGLES &handle) const
Returns the OpenGL handle for a reactor handle if one is available. This is typically only safe to ca...
Definition: reactor_gles.cc:53
IPLR_REQUIRES
#define IPLR_REQUIRES(...)
Definition: thread_safety.h:30