| Flutter Impeller |

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
5 #include "impeller/renderer/backend/gles/reactor_gles.h"
6
7 #include <algorithm>
8
9 #include "flutter/fml/trace_event.h"
10 #include "fml/logging.h"
11 #include "impeller/base/validation.h"
12
13 namespace impeller {
14
15 ReactorGLES::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
25 ReactorGLES::~ReactorGLES() = default;
26
27 bool ReactorGLES::IsValid() const {
28 return is_valid_;
29 }
30
31 ReactorGLES::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
38 bool ReactorGLES::RemoveWorker(WorkerID worker) {
39 Lock lock(workers_mutex_);
40 return workers_.erase(worker) == 1;
41 }
42
43 bool ReactorGLES::HasPendingOperations() const {
44 Lock ops_lock(ops_mutex_);
45 return !ops_.empty();
46 }
47
48 const ProcTableGLES& ReactorGLES::GetProcTable() const {
49 FML_DCHECK(IsValid());
50 return *proc_table_;
51 }
52
53 std::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) {
57 VALIDATION_LOG
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
71 bool ReactorGLES::AddOperation(Operation operation) {
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
84 static std::optional<GLuint> CreateGLHandle(const ProcTableGLES& gl,
85 HandleType type) {
86 GLuint handle = GL_NONE;
87 switch (type) {
88 case HandleType::kUnknown:
89 return std::nullopt;
90 case HandleType::kTexture:
91 gl.GenTextures(1u, &handle);
92 return handle;
93 case HandleType::kBuffer:
94 gl.GenBuffers(1u, &handle);
95 return handle;
96 case HandleType::kProgram:
97 return gl.CreateProgram();
98 case HandleType::kRenderBuffer:
99 gl.GenRenderbuffers(1u, &handle);
100 return handle;
101 case HandleType::kFrameBuffer:
102 gl.GenFramebuffers(1u, &handle);
103 return handle;
104 }
105 return std::nullopt;
106 }
107
108 static bool CollectGLHandle(const ProcTableGLES& gl,
109 HandleType type,
110 GLuint handle) {
111 switch (type) {
112 case HandleType::kUnknown:
113 return false;
114 case HandleType::kTexture:
115 gl.DeleteTextures(1u, &handle);
116 return true;
117 case HandleType::kBuffer:
118 gl.DeleteBuffers(1u, &handle);
119 return true;
120 case HandleType::kProgram:
121 gl.DeleteProgram(handle);
122 return true;
123 case HandleType::kRenderBuffer:
124 gl.DeleteRenderbuffers(1u, &handle);
125 return true;
126 case HandleType::kFrameBuffer:
127 gl.DeleteFramebuffers(1u, &handle);
128 return true;
129 }
130 return false;
131 }
132
133 HandleGLES ReactorGLES::CreateHandle(HandleType type, GLuint external_handle) {
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
143 std::optional<GLuint> gl_handle;
144 if (external_handle != GL_NONE) {
145 gl_handle = external_handle;
146 } else if (CanReactOnCurrentThread()) {
147 gl_handle = CreateGLHandle(GetProcTable(), type);
148 }
149 handles_[new_handle] = LiveHandle{gl_handle};
150 return new_handle;
151 }
152
153 void ReactorGLES::CollectHandle(HandleGLES handle) {
154 WriterLock handles_lock(handles_mutex_);
155 if (auto found = handles_.find(handle); found != handles_.end()) {
156 found->second.pending_collection = true;
157 }
158 }
159
160 bool ReactorGLES::React() {
161 if (!CanReactOnCurrentThread()) {
162 return false;
163 }
164 TRACE_EVENT0("impeller", "ReactorGLES::React");
165 while (HasPendingOperations()) {
166 // Both the raster thread and the IO thread can flush queued operations.
167 // Ensure that execution of the ops is serialized.
168 Lock execution_lock(ops_execution_mutex_);
169
170 if (!ReactOnce()) {
171 return false;
172 }
173 }
174 return true;
175 }
176
177 static DebugResourceType ToDebugResourceType(HandleType type) {
178 switch (type) {
179 case HandleType::kUnknown:
180 FML_UNREACHABLE();
181 case HandleType::kTexture:
182 return DebugResourceType::kTexture;
183 case HandleType::kBuffer:
184 return DebugResourceType::kBuffer;
185 case HandleType::kProgram:
186 return DebugResourceType::kProgram;
187 case HandleType::kRenderBuffer:
188 return DebugResourceType::kRenderBuffer;
189 case HandleType::kFrameBuffer:
190 return DebugResourceType::kFrameBuffer;
191 }
192 FML_UNREACHABLE();
193 }
194
195 bool ReactorGLES::ReactOnce() {
196 if (!IsValid()) {
197 return false;
198 }
199 TRACE_EVENT0("impeller", __FUNCTION__);
200 return ConsolidateHandles() && FlushOps();
201 }
202
203 bool ReactorGLES::ConsolidateHandles() {
204 TRACE_EVENT0("impeller", __FUNCTION__);
205 const auto& gl = GetProcTable();
206 WriterLock handles_lock(handles_mutex_);
207 std::vector<HandleGLES> handles_to_delete;
208 for (auto& handle : handles_) {
209 // Collect dead handles.
210 if (handle.second.pending_collection) {
211 // This could be false if the handle was created and collected without
212 // use. We still need to get rid of map entry.
213 if (handle.second.name.has_value()) {
214 CollectGLHandle(gl, handle.first.type, handle.second.name.value());
215 }
216 handles_to_delete.push_back(handle.first);
217 continue;
218 }
219 // Create live handles.
220 if (!handle.second.name.has_value()) {
221 auto gl_handle = CreateGLHandle(gl, handle.first.type);
222 if (!gl_handle) {
223 VALIDATION_LOG << "Could not create GL handle.";
224 return false;
225 }
226 handle.second.name = gl_handle;
227 }
228 // Set pending debug labels.
229 if (handle.second.pending_debug_label.has_value()) {
230 if (gl.SetDebugLabel(ToDebugResourceType(handle.first.type),
231 handle.second.name.value(),
232 handle.second.pending_debug_label.value())) {
233 handle.second.pending_debug_label = std::nullopt;
234 }
235 }
236 }
237 for (const auto& handle_to_delete : handles_to_delete) {
238 handles_.erase(handle_to_delete);
239 }
240 return true;
241 }
242
243 bool ReactorGLES::FlushOps() {
244 TRACE_EVENT0("impeller", __FUNCTION__);
245
246 #ifdef IMPELLER_DEBUG
247 // glDebugMessageControl sometimes must be called before glPushDebugGroup:
248 // https://github.com/flutter/flutter/issues/135715#issuecomment-1740153506
249 SetupDebugGroups();
250 #endif
251
252 // Do NOT hold the ops or handles locks while performing operations in case
253 // the ops enqueue more ops.
254 decltype(ops_) ops;
255 {
256 Lock ops_lock(ops_mutex_);
257 std::swap(ops_, ops);
258 }
259 for (const auto& op : ops) {
260 TRACE_EVENT0("impeller", "ReactorGLES::Operation");
261 op(*this);
262 }
263 return true;
264 }
265
266 void ReactorGLES::SetupDebugGroups() {
267 // Setup of a default active debug group: Filter everything in.
268 if (proc_table_->DebugMessageControlKHR.IsAvailable()) {
269 proc_table_->DebugMessageControlKHR(GL_DONT_CARE, // source
270 GL_DONT_CARE, // type
271 GL_DONT_CARE, // severity
272 0, // count
273 nullptr, // ids
274 GL_TRUE); // enabled
275 }
276 }
277
278 void ReactorGLES::SetDebugLabel(const HandleGLES& handle, std::string label) {
279 if (!can_set_debug_labels_) {
280 return;
281 }
282 if (handle.IsDead()) {
283 return;
284 }
285 WriterLock handles_lock(handles_mutex_);
286 if (auto found = handles_.find(handle); found != handles_.end()) {
287 found->second.pending_debug_label = std::move(label);
288 }
289 }
290
291 bool ReactorGLES::CanReactOnCurrentThread() const {
292 std::vector<WorkerID> dead_workers;
293 Lock lock(workers_mutex_);
294 for (const auto& worker : workers_) {
295 auto worker_ptr = worker.second.lock();
296 if (!worker_ptr) {
297 dead_workers.push_back(worker.first);
298 continue;
299 }
300 if (worker_ptr->CanReactorReactOnCurrentThreadNow(*this)) {
301 return true;
302 }
303 }
304 for (const auto& worker_id : dead_workers) {
305 workers_.erase(worker_id);
306 }
307 return false;
308 }
309
310 } // namespace impeller
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::HandleGLES::DeadHandle
static HandleGLES DeadHandle()
Creates a dead handle.
Definition: handle_gles.h:45
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::HandleType::kRenderBuffer
@ kRenderBuffer
impeller::WriterLock
Definition: thread.h:113
impeller::HandleType
HandleType
Definition: handle_gles.h:18
impeller::HandleType::kTexture
@ kTexture
impeller::Lock
Definition: thread.h:72
impeller::ToDebugResourceType
static DebugResourceType ToDebugResourceType(HandleType type)
Definition: reactor_gles.cc:177
impeller::DebugResourceType::kBuffer
@ kBuffer
impeller::HandleType::kUnknown
@ kUnknown
impeller::DebugResourceType::kProgram
@ kProgram
impeller::HandleGLES::IsDead
constexpr bool IsDead() const
Determines if the handle is dead.
Definition: handle_gles.h:54
validation.h
impeller::ReactorGLES::React
bool React()
Perform a reaction on the current thread if able.
Definition: reactor_gles.cc:160
impeller::DebugResourceType::kTexture
@ kTexture
impeller::ReaderLock
Definition: thread.h:92
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
impeller::DebugResourceType::kRenderBuffer
@ kRenderBuffer
impeller::HandleType::kProgram
@ kProgram
impeller::CreateGLHandle
static std::optional< GLuint > CreateGLHandle(const ProcTableGLES &gl, HandleType type)
Definition: reactor_gles.cc:84
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
VALIDATION_LOG
#define VALIDATION_LOG
Definition: validation.h:91
impeller::ReactorGLES::CollectHandle
void CollectHandle(HandleGLES handle)
Collect a reactor handle.
Definition: reactor_gles.cc:153
reactor_gles.h
impeller::DebugResourceType
DebugResourceType
Definition: proc_table_gles.h:217
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
std
Definition: comparable.h:95
impeller::CollectGLHandle
static bool CollectGLHandle(const ProcTableGLES &gl, HandleType type, GLuint handle)
Definition: reactor_gles.cc:108
impeller::HandleType::kFrameBuffer
@ kFrameBuffer
impeller::DebugResourceType::kFrameBuffer
@ kFrameBuffer
impeller::UniqueID
Definition: comparable.h:16
impeller::HandleType::kBuffer
@ kBuffer
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