| 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 "flutter/third_party/abseil-cpp/absl/container/flat_hash_map.h"
13 #include "fml/closure.h"
14 #include "impeller/base/thread.h"
15 #include "impeller/renderer/backend/gles/handle_gles.h"
16 #include "impeller/renderer/backend/gles/proc_table_gles.h"
17
18 namespace impeller {
19
20 //------------------------------------------------------------------------------
21 /// @brief The reactor attempts to make thread-safe usage of OpenGL ES
22 /// easier to reason about.
23 ///
24 /// In the other Impeller backends (like Metal and Vulkan),
25 /// resources can be created, used, and deleted on any thread with
26 /// relatively few restrictions. However, OpenGL resources can only
27 /// be created, used, and deleted on a thread on which an OpenGL
28 /// context (or one in the same sharegroup) is current.
29 ///
30 /// There aren't too many OpenGL contexts to go around and making
31 /// the caller reason about the timing and threading requirement
32 /// only when the OpenGL backend is in use is tedious. To work
33 /// around this tedium, there is an abstraction between the
34 /// resources and their handles in OpenGL. The reactor is this
35 /// abstraction.
36 ///
37 /// The reactor is thread-safe and can created, used, and collected
38 /// on any thread.
39 ///
40 /// Reactor handles `HandleGLES` can be created, used, and collected
41 /// on any thread. These handles can be to textures, buffers, etc..
42 ///
43 /// Operations added to the reactor are guaranteed to run on a
44 /// worker within a finite amount of time unless the reactor itself
45 /// is torn down or there are no workers. These operations may run
46 /// on the calling thread immediately if a worker is active on the
47 /// current thread and can perform reactions. The operations are
48 /// guaranteed to run with an OpenGL context current and all reactor
49 /// handles having live OpenGL handle counterparts.
50 ///
51 /// Creating a handle in the reactor doesn't mean an OpenGL handle
52 /// is created immediately. OpenGL handles become live before the
53 /// next reaction. Similarly, dropping the last reference to a
54 /// reactor handle means that the OpenGL handle will be deleted at
55 /// some point in the near future.
56 ///
57 class ReactorGLES {
58 public:
59 using WorkerID = UniqueID;
60
61 //----------------------------------------------------------------------------
62 /// @brief A delegate implemented by a thread on which an OpenGL context
63 /// is current. There may be multiple workers for the reactor to
64 /// perform reactions on. In that case, it is the workers
65 /// responsibility to ensure that all of them use either the same
66 /// OpenGL context or multiple OpenGL contexts in the same
67 /// sharegroup.
68 ///
69 class Worker {
70 public:
71 virtual ~Worker() = default;
72
73 //--------------------------------------------------------------------------
74 /// @brief Determines the ability of the worker to service a reaction
75 /// on the current thread. The OpenGL context must be current on
76 /// the thread if the worker says it is able to service a
77 /// reaction.
78 ///
79 /// @param[in] reactor The reactor
80 ///
81 /// @return If the worker is able to service a reaction. The reactor
82 /// assumes the context is already current if true.
83 ///
84 virtual bool CanReactorReactOnCurrentThreadNow(
85 const ReactorGLES& reactor) const = 0;
86 };
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 std::optional<GLsync> GetGLFence(const HandleGLES& handle) const;
161
162 //----------------------------------------------------------------------------
163 /// @brief Create a reactor handle.
164 ///
165 /// This can be called on any thread. Even one that doesn't have
166 /// an OpenGL context.
167 ///
168 /// @param[in] type The type of handle to create.
169 /// @param[in] external_handle An already created GL handle if one exists.
170 ///
171 /// @return The reactor handle.
172 ///
173 HandleGLES CreateHandle(HandleType type, GLuint external_handle = GL_NONE);
174
175 /// @brief Create a handle that is not managed by `ReactorGLES`.
176 /// @details This behaves just like `CreateHandle` but it doesn't add the
177 /// handle to ReactorGLES::handles_ and the creation is executed
178 /// synchronously, so it must be called from a proper thread. The benefit of
179 /// this is that it avoid synchronization and hash table lookups when
180 /// creating/accessing the handle.
181 /// @param type The type of handle to create.
182 /// @return The reactor handle.
183 HandleGLES CreateUntrackedHandle(HandleType type) const;
184
185 //----------------------------------------------------------------------------
186 /// @brief Collect a reactor handle.
187 ///
188 /// This can be called on any thread. Even one that doesn't have
189 /// an OpenGL context.
190 ///
191 /// @param[in] handle The reactor handle handle
192 ///
193 void CollectHandle(HandleGLES handle);
194
195 //----------------------------------------------------------------------------
196 /// @brief Set the debug label on a reactor handle.
197 ///
198 /// This call ensures that the OpenGL debug label is propagated to
199 /// even the OpenGL handle hasn't been created at the time the
200 /// caller sets the label.
201 ///
202 /// @param[in] handle The handle
203 /// @param[in] label The label
204 ///
205 void SetDebugLabel(const HandleGLES& handle, std::string_view label);
206
207 //----------------------------------------------------------------------------
208 /// @brief Whether the device is capable of writing debug labels.
209 ///
210 /// This function is useful for short circuiting expensive debug
211 /// labeling.
212 bool CanSetDebugLabels() const;
213
214 using Operation = std::function<void(const ReactorGLES& reactor)>;
215
216 //----------------------------------------------------------------------------
217 /// @brief Adds an operation that the reactor runs on a worker that
218 /// ensures that an OpenGL context is current.
219 ///
220 /// This operation is not guaranteed to run immediately. It will
221 /// complete in a finite amount of time on any thread as long as
222 /// there is a reactor worker and the reactor itself is not being
223 /// torn down.
224 ///
225 /// @param[in] operation The operation
226 /// @param[in] defer If false, the reactor attempts to React after
227 /// adding this operation.
228 ///
229 /// @return If the operation was successfully queued for completion.
230 ///
231 [[nodiscard]] bool AddOperation(Operation operation, bool defer = false);
232
233 //----------------------------------------------------------------------------
234 /// @brief Register a cleanup callback that will be invokved with the
235 /// provided user data when the handle is destroyed.
236 ///
237 /// This operation is not guaranteed to run immediately. It will
238 /// complete in a finite amount of time on any thread as long as
239 /// there is a reactor worker and the reactor itself is not being
240 /// torn down.
241 ///
242 /// @param[in] handle The handle to attach the cleanup to.
243 /// @param[in] callback The cleanup callback to execute.
244 ///
245 /// @return If the operation was successfully queued for completion.
246 ///
247 bool RegisterCleanupCallback(const HandleGLES& handle,
248 const fml::closure& callback);
249
250 //----------------------------------------------------------------------------
251 /// @brief Perform a reaction on the current thread if able.
252 ///
253 /// It is safe to call this simultaneously from multiple threads
254 /// at the same time.
255 ///
256 /// @return If a reaction was performed on the calling thread.
257 ///
258 [[nodiscard]] bool React();
259
260 private:
261 /// @brief Storage for either a GL handle or sync fence.
262 struct GLStorage {
263 union {
264 GLuint handle;
265 GLsync sync;
266 uint64_t integer;
267 };
268 };
269 static_assert(sizeof(GLStorage) == sizeof(uint64_t));
270
271 struct LiveHandle {
272 std::optional<GLStorage> name;
273 std::optional<std::string> pending_debug_label;
274 bool pending_collection = false;
275 fml::ScopedCleanupClosure callback = {};
276
277 LiveHandle() = default;
278
279 explicit LiveHandle(std::optional<GLStorage> p_name) : name(p_name) {}
280
281 constexpr bool IsLive() const { return name.has_value(); }
282 };
283
284 std::unique_ptr<ProcTableGLES> proc_table_;
285
286 mutable Mutex ops_mutex_;
287 std::map<std::thread::id, std::vector<Operation>> ops_ IPLR_GUARDED_BY(
288 ops_mutex_);
289
290 using LiveHandles = absl::flat_hash_map<const HandleGLES,
291 LiveHandle,
292 HandleGLES::Hash,
293 HandleGLES::Equal>;
294 mutable RWMutex handles_mutex_;
295 LiveHandles handles_ IPLR_GUARDED_BY(handles_mutex_);
296 int32_t handles_to_collect_count_ IPLR_GUARDED_BY(handles_mutex_) = 0;
297
298 mutable Mutex workers_mutex_;
299 mutable std::map<WorkerID, std::weak_ptr<Worker>> workers_ IPLR_GUARDED_BY(
300 workers_mutex_);
301
302 bool can_set_debug_labels_ = false;
303 bool is_valid_ = false;
304
305 bool ReactOnce();
306
307 bool HasPendingOperations() const;
308
309 bool CanReactOnCurrentThread() const;
310
311 bool ConsolidateHandles();
312
313 bool FlushOps();
314
315 void SetupDebugGroups();
316
317 std::optional<GLStorage> GetHandle(const HandleGLES& handle) const;
318
319 static std::optional<GLStorage> CreateGLHandle(const ProcTableGLES& gl,
320 HandleType type);
321
322 static bool CollectGLHandle(const ProcTableGLES& gl,
323 HandleType type,
324 GLStorage handle);
325
326 ReactorGLES(const ReactorGLES&) = delete;
327
328 ReactorGLES& operator=(const ReactorGLES&) = delete;
329 };
330
331 } // namespace impeller
332
333 #endif // FLUTTER_IMPELLER_RENDERER_BACKEND_GLES_REACTOR_GLES_H_
type
GLenum type
Definition: blit_command_gles.cc:151
impeller::HandleGLES
Represents a handle to an underlying OpenGL object. Unlike OpenGL object handles, these handles can b...
Definition: handle_gles.h:37
impeller::ProcTableGLES
Definition: proc_table_gles.h:282
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:69
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::Worker::~Worker
virtual ~Worker()=default
impeller::ReactorGLES
The reactor attempts to make thread-safe usage of OpenGL ES easier to reason about.
Definition: reactor_gles.h:57
impeller::ReactorGLES::RegisterCleanupCallback
bool RegisterCleanupCallback(const HandleGLES &handle, const fml::closure &callback)
Register a cleanup callback that will be invokved with the provided user data when the handle is dest...
Definition: reactor_gles.cc:189
impeller::ReactorGLES::CanSetDebugLabels
bool CanSetDebugLabels() const
Whether the device is capable of writing debug labels.
Definition: reactor_gles.cc:99
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:151
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:73
impeller::ReactorGLES::CreateUntrackedHandle
HandleGLES CreateUntrackedHandle(HandleType type) const
Create a handle that is not managed by ReactorGLES.
Definition: reactor_gles.cc:203
impeller::ReactorGLES::GetGLFence
std::optional< GLsync > GetGLFence(const HandleGLES &handle) const
Definition: reactor_gles.cc:162
impeller::ReactorGLES::CollectHandle
void CollectHandle(HandleGLES handle)
Collect a reactor handle.
Definition: reactor_gles.cc:235
impeller::ReactorGLES::CreateHandle
HandleGLES CreateHandle(HandleType type, GLuint external_handle=GL_NONE)
Create a reactor handle.
Definition: reactor_gles.cc:214
impeller::ReactorGLES::Operation
std::function< void(const ReactorGLES &reactor)> Operation
Definition: reactor_gles.h:214
impeller::ReactorGLES::React
bool React()
Perform a reaction on the current thread if able.
Definition: reactor_gles.cc:252
impeller::ReactorGLES::~ReactorGLES
~ReactorGLES()
Destroy a reactor.
Definition: reactor_gles.cc:83
impeller::ReactorGLES::GetProcTable
const ProcTableGLES & GetProcTable() const
Get the OpenGL proc. table the reactor uses to manage handles.
Definition: reactor_gles.cc:122
impeller::ReactorGLES::AddOperation
bool AddOperation(Operation operation, bool defer=false)
Adds an operation that the reactor runs on a worker that ensures that an OpenGL context is current.
Definition: reactor_gles.cc:173
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:110
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:103
impeller::ReactorGLES::SetDebugLabel
void SetDebugLabel(const HandleGLES &handle, std::string_view label)
Set the debug label on a reactor handle.
Definition: reactor_gles.cc:387
impeller::ReactorGLES::IsValid
bool IsValid() const
If this is a valid reactor. Invalid reactors must be discarded immediately.
Definition: reactor_gles.cc:95
handle_gles.h
impeller
Definition: allocation.cc:12
impeller::HandleType
HandleType
Definition: handle_gles.h:18
proc_table_gles.h
impeller::UniqueID
Definition: comparable.h:16
thread.h