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 
6 
7 #include <algorithm>
8 
9 #include "flutter/fml/trace_event.h"
10 #include "fml/logging.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 
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 
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) {
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 
84 static std::optional<GLuint> CreateGLHandle(const ProcTableGLES& gl,
85  HandleType type) {
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 
108 static bool CollectGLHandle(const ProcTableGLES& gl,
109  HandleType type,
110  GLuint handle) {
111  switch (type) {
113  return false;
115  gl.DeleteTextures(1u, &handle);
116  return true;
117  case HandleType::kBuffer:
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()
143  ? CreateGLHandle(GetProcTable(), type)
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) {
176  FML_UNREACHABLE();
179  case HandleType::kBuffer:
187  }
188  FML_UNREACHABLE();
189 }
190 
191 bool ReactorGLES::ReactOnce() {
192  if (!IsValid()) {
193  return false;
194  }
195  TRACE_EVENT0("impeller", __FUNCTION__);
196  return ConsolidateHandles() && FlushOps();
197 }
198 
199 bool 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 
239 bool 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 
262 void 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 
274 void 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 
287 bool 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
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()
Definition: handle_gles.h:39
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::SetDebugLabel
void SetDebugLabel(const HandleGLES &handle, std::string label)
Set the debug label on a reactor handle.
Definition: reactor_gles.cc:274
impeller::HandleType::kRenderBuffer
@ kRenderBuffer
impeller::WriterLock
Definition: thread.h:116
impeller::ReactorGLES::CreateHandle
HandleGLES CreateHandle(HandleType type)
Create a reactor handle.
Definition: reactor_gles.cc:133
impeller::HandleType
HandleType
Definition: handle_gles.h:22
impeller::HandleType::kTexture
@ kTexture
impeller::Lock
Definition: thread.h:75
impeller::ToDebugResourceType
static DebugResourceType ToDebugResourceType(HandleType type)
Definition: reactor_gles.cc:173
impeller::DebugResourceType::kBuffer
@ kBuffer
impeller::HandleType::kUnknown
@ kUnknown
impeller::DebugResourceType::kProgram
@ kProgram
impeller::HandleGLES::IsDead
constexpr bool IsDead() const
Definition: handle_gles.h:43
validation.h
impeller::ReactorGLES::React
bool React()
Perform a reaction on the current thread if able.
Definition: reactor_gles.cc:156
impeller::DebugResourceType::kTexture
@ kTexture
impeller::ReaderLock
Definition: thread.h:95
impeller::ReactorGLES::~ReactorGLES
~ReactorGLES()
Destroy a reactor.
impeller::HandleGLES
Definition: handle_gles.h:35
impeller::ProcTableGLES
Definition: proc_table_gles.h:229
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:73
impeller::ReactorGLES::CollectHandle
void CollectHandle(HandleGLES handle)
Collect a reactor handle.
Definition: reactor_gles.cc:149
reactor_gles.h
impeller::DebugResourceType
DebugResourceType
Definition: proc_table_gles.h:220
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: aiks_blur_unittests.cc:20
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