Flutter Engine
The Flutter Engine
reactor_gles.cc
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
6
7#include <algorithm>
8
9#include "flutter/fml/trace_event.h"
10#include "fml/logging.h"
12
13namespace impeller {
14
15ReactorGLES::ReactorGLES(std::unique_ptr<ProcTableGLES> gl)
16 : proc_table_(std::move(gl)) {
17 if (!proc_table_ || !proc_table_->IsValid()) {
18 VALIDATION_LOG << "Proc table was invalid.";
19 return;
20 }
21 can_set_debug_labels_ = proc_table_->GetDescription()->HasDebugExtension();
22 is_valid_ = true;
23}
24
26
28 return is_valid_;
29}
30
31ReactorGLES::WorkerID ReactorGLES::AddWorker(std::weak_ptr<Worker> worker) {
32 Lock lock(workers_mutex_);
33 auto id = WorkerID{};
34 workers_[id] = std::move(worker);
35 return id;
36}
37
39 Lock lock(workers_mutex_);
40 return workers_.erase(worker) == 1;
41}
42
43bool ReactorGLES::HasPendingOperations() const {
44 Lock ops_lock(ops_mutex_);
45 return !ops_.empty();
46}
47
50 return *proc_table_;
51}
52
53std::optional<GLuint> ReactorGLES::GetGLHandle(const HandleGLES& handle) const {
54 ReaderLock handles_lock(handles_mutex_);
55 if (auto found = handles_.find(handle); found != handles_.end()) {
56 if (found->second.pending_collection) {
58 << "Attempted to acquire a handle that was pending collection.";
59 return std::nullopt;
60 }
61 if (!found->second.name.has_value()) {
62 VALIDATION_LOG << "Attempt to acquire a handle outside of an operation.";
63 return std::nullopt;
64 }
65 return found->second.name;
66 }
67 VALIDATION_LOG << "Attempted to acquire an invalid GL handle.";
68 return std::nullopt;
69}
70
72 if (!operation) {
73 return false;
74 }
75 {
76 Lock ops_lock(ops_mutex_);
77 ops_.emplace_back(std::move(operation));
78 }
79 // Attempt a reaction if able but it is not an error if this isn't possible.
80 [[maybe_unused]] auto result = React();
81 return true;
82}
83
84static std::optional<GLuint> CreateGLHandle(const ProcTableGLES& gl,
86 GLuint handle = GL_NONE;
87 switch (type) {
89 return std::nullopt;
91 gl.GenTextures(1u, &handle);
92 return handle;
94 gl.GenBuffers(1u, &handle);
95 return handle;
97 return gl.CreateProgram();
99 gl.GenRenderbuffers(1u, &handle);
100 return handle;
102 gl.GenFramebuffers(1u, &handle);
103 return handle;
104 }
105 return std::nullopt;
106}
107
110 GLuint handle) {
111 switch (type) {
113 return false;
115 gl.DeleteTextures(1u, &handle);
116 return true;
118 gl.DeleteBuffers(1u, &handle);
119 return true;
121 gl.DeleteProgram(handle);
122 return true;
124 gl.DeleteRenderbuffers(1u, &handle);
125 return true;
127 gl.DeleteFramebuffers(1u, &handle);
128 return true;
129 }
130 return false;
131}
132
134 if (type == HandleType::kUnknown) {
135 return HandleGLES::DeadHandle();
136 }
137 auto new_handle = HandleGLES::Create(type);
138 if (new_handle.IsDead()) {
139 return HandleGLES::DeadHandle();
140 }
141 WriterLock handles_lock(handles_mutex_);
142 auto gl_handle = CanReactOnCurrentThread()
144 : std::nullopt;
145 handles_[new_handle] = LiveHandle{gl_handle};
146 return new_handle;
147}
148
150 WriterLock handles_lock(handles_mutex_);
151 if (auto found = handles_.find(handle); found != handles_.end()) {
152 found->second.pending_collection = true;
153 }
154}
155
157 if (!CanReactOnCurrentThread()) {
158 return false;
159 }
160 TRACE_EVENT0("impeller", "ReactorGLES::React");
161 while (HasPendingOperations()) {
162 // Both the raster thread and the IO thread can flush queued operations.
163 // Ensure that execution of the ops is serialized.
164 Lock execution_lock(ops_execution_mutex_);
165
166 if (!ReactOnce()) {
167 return false;
168 }
169 }
170 return true;
171}
172
174 switch (type) {
187 }
189}
190
191bool ReactorGLES::ReactOnce() {
192 if (!IsValid()) {
193 return false;
194 }
195 TRACE_EVENT0("impeller", __FUNCTION__);
196 return ConsolidateHandles() && FlushOps();
197}
198
199bool ReactorGLES::ConsolidateHandles() {
200 TRACE_EVENT0("impeller", __FUNCTION__);
201 const auto& gl = GetProcTable();
202 WriterLock handles_lock(handles_mutex_);
203 std::vector<HandleGLES> handles_to_delete;
204 for (auto& handle : handles_) {
205 // Collect dead handles.
206 if (handle.second.pending_collection) {
207 // This could be false if the handle was created and collected without
208 // use. We still need to get rid of map entry.
209 if (handle.second.name.has_value()) {
210 CollectGLHandle(gl, handle.first.type, handle.second.name.value());
211 }
212 handles_to_delete.push_back(handle.first);
213 continue;
214 }
215 // Create live handles.
216 if (!handle.second.name.has_value()) {
217 auto gl_handle = CreateGLHandle(gl, handle.first.type);
218 if (!gl_handle) {
219 VALIDATION_LOG << "Could not create GL handle.";
220 return false;
221 }
222 handle.second.name = gl_handle;
223 }
224 // Set pending debug labels.
225 if (handle.second.pending_debug_label.has_value()) {
226 if (gl.SetDebugLabel(ToDebugResourceType(handle.first.type),
227 handle.second.name.value(),
228 handle.second.pending_debug_label.value())) {
229 handle.second.pending_debug_label = std::nullopt;
230 }
231 }
232 }
233 for (const auto& handle_to_delete : handles_to_delete) {
234 handles_.erase(handle_to_delete);
235 }
236 return true;
237}
238
239bool ReactorGLES::FlushOps() {
240 TRACE_EVENT0("impeller", __FUNCTION__);
241
242#ifdef IMPELLER_DEBUG
243 // glDebugMessageControl sometimes must be called before glPushDebugGroup:
244 // https://github.com/flutter/flutter/issues/135715#issuecomment-1740153506
245 SetupDebugGroups();
246#endif
247
248 // Do NOT hold the ops or handles locks while performing operations in case
249 // the ops enqueue more ops.
250 decltype(ops_) ops;
251 {
252 Lock ops_lock(ops_mutex_);
253 std::swap(ops_, ops);
254 }
255 for (const auto& op : ops) {
256 TRACE_EVENT0("impeller", "ReactorGLES::Operation");
257 op(*this);
258 }
259 return true;
260}
261
262void ReactorGLES::SetupDebugGroups() {
263 // Setup of a default active debug group: Filter everything in.
264 if (proc_table_->DebugMessageControlKHR.IsAvailable()) {
265 proc_table_->DebugMessageControlKHR(GL_DONT_CARE, // source
266 GL_DONT_CARE, // type
267 GL_DONT_CARE, // severity
268 0, // count
269 nullptr, // ids
270 GL_TRUE); // enabled
271 }
272}
273
274void ReactorGLES::SetDebugLabel(const HandleGLES& handle, std::string label) {
275 if (!can_set_debug_labels_) {
276 return;
277 }
278 if (handle.IsDead()) {
279 return;
280 }
281 WriterLock handles_lock(handles_mutex_);
282 if (auto found = handles_.find(handle); found != handles_.end()) {
283 found->second.pending_debug_label = std::move(label);
284 }
285}
286
287bool ReactorGLES::CanReactOnCurrentThread() const {
288 std::vector<WorkerID> dead_workers;
289 Lock lock(workers_mutex_);
290 for (const auto& worker : workers_) {
291 auto worker_ptr = worker.second.lock();
292 if (!worker_ptr) {
293 dead_workers.push_back(worker.first);
294 continue;
295 }
296 if (worker_ptr->CanReactorReactOnCurrentThreadNow(*this)) {
297 return true;
298 }
299 }
300 for (const auto& worker_id : dead_workers) {
301 workers_.erase(worker_id);
302 }
303 return false;
304}
305
306} // namespace impeller
SkPathOp ops[]
static void operation(T operation, uint32_t &a, uint32_t b, uint32_t c, uint32_t d, uint32_t x, uint8_t s, uint32_t t)
Definition: SkMD5.cpp:144
void swap(sk_sp< T > &a, sk_sp< T > &b)
Definition: SkRefCnt.h:341
GLenum type
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
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
void CollectHandle(HandleGLES handle)
Collect a reactor handle.
std::function< void(const ReactorGLES &reactor)> Operation
Definition: reactor_gles.h:194
bool React()
Perform a reaction on the current thread if able.
~ReactorGLES()
Destroy a reactor.
const ProcTableGLES & GetProcTable() const
Get the OpenGL proc. table the reactor uses to manage handles.
Definition: reactor_gles.cc:48
HandleGLES CreateHandle(HandleType type)
Create a reactor handle.
void SetDebugLabel(const HandleGLES &handle, std::string label)
Set the debug label on a reactor handle.
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
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
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
bool IsValid() const
If this is a valid reactor. Invalid reactors must be discarded immediately.
Definition: reactor_gles.cc:27
GAsyncResult * result
#define FML_UNREACHABLE()
Definition: logging.h:109
#define FML_DCHECK(condition)
Definition: logging.h:103
static bool CollectGLHandle(const ProcTableGLES &gl, HandleType type, GLuint handle)
static DebugResourceType ToDebugResourceType(HandleType type)
static std::optional< GLuint > CreateGLHandle(const ProcTableGLES &gl, HandleType type)
Definition: reactor_gles.cc:84
gl
Definition: malisc.py:41
Definition: ref_ptr.h:256
constexpr bool IsDead() const
Definition: handle_gles.h:39
static HandleGLES DeadHandle()
Definition: handle_gles.h:35
const uintptr_t id
#define TRACE_EVENT0(category_group, name)
Definition: trace_event.h:131
#define VALIDATION_LOG
Definition: validation.h:73