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"
11 
12 namespace impeller {
13 
14 ReactorGLES::ReactorGLES(std::unique_ptr<ProcTableGLES> gl)
15  : proc_table_(std::move(gl)) {
16  if (!proc_table_ || !proc_table_->IsValid()) {
17  VALIDATION_LOG << "Proc table was invalid.";
18  return;
19  }
20  can_set_debug_labels_ = proc_table_->GetDescription()->HasDebugExtension();
21  is_valid_ = true;
22 }
23 
24 ReactorGLES::~ReactorGLES() = default;
25 
26 bool ReactorGLES::IsValid() const {
27  return is_valid_;
28 }
29 
30 ReactorGLES::WorkerID ReactorGLES::AddWorker(std::weak_ptr<Worker> worker) {
31  Lock lock(workers_mutex_);
32  auto id = WorkerID{};
33  workers_[id] = std::move(worker);
34  return id;
35 }
36 
38  Lock lock(workers_mutex_);
39  return workers_.erase(worker) == 1;
40 }
41 
42 bool ReactorGLES::HasPendingOperations() const {
43  Lock ops_lock(ops_mutex_);
44  return !ops_.empty();
45 }
46 
48  FML_DCHECK(IsValid());
49  return *proc_table_;
50 }
51 
52 std::optional<GLuint> ReactorGLES::GetGLHandle(const HandleGLES& handle) const {
53  ReaderLock handles_lock(handles_mutex_);
54  if (auto found = handles_.find(handle); found != handles_.end()) {
55  if (found->second.pending_collection) {
57  << "Attempted to acquire a handle that was pending collection.";
58  return std::nullopt;
59  }
60  if (!found->second.name.has_value()) {
61  VALIDATION_LOG << "Attempt to acquire a handle outside of an operation.";
62  return std::nullopt;
63  }
64  return found->second.name;
65  }
66  VALIDATION_LOG << "Attempted to acquire an invalid GL handle.";
67  return std::nullopt;
68 }
69 
71  if (!operation) {
72  return false;
73  }
74  {
75  Lock ops_lock(ops_mutex_);
76  ops_.emplace_back(std::move(operation));
77  }
78  // Attempt a reaction if able but it is not an error if this isn't possible.
79  [[maybe_unused]] auto result = React();
80  return true;
81 }
82 
83 static std::optional<GLuint> CreateGLHandle(const ProcTableGLES& gl,
84  HandleType type) {
85  GLuint handle = GL_NONE;
86  switch (type) {
88  return std::nullopt;
90  gl.GenTextures(1u, &handle);
91  return handle;
93  gl.GenBuffers(1u, &handle);
94  return handle;
96  return gl.CreateProgram();
98  gl.GenRenderbuffers(1u, &handle);
99  return handle;
101  gl.GenFramebuffers(1u, &handle);
102  return handle;
103  }
104  return std::nullopt;
105 }
106 
107 static bool CollectGLHandle(const ProcTableGLES& gl,
108  HandleType type,
109  GLuint handle) {
110  switch (type) {
112  return false;
114  gl.DeleteTextures(1u, &handle);
115  return true;
116  case HandleType::kBuffer:
117  gl.DeleteBuffers(1u, &handle);
118  return true;
120  gl.DeleteProgram(handle);
121  return true;
123  gl.DeleteRenderbuffers(1u, &handle);
124  return true;
126  gl.DeleteFramebuffers(1u, &handle);
127  return true;
128  }
129  return false;
130 }
131 
133  if (type == HandleType::kUnknown) {
134  return HandleGLES::DeadHandle();
135  }
136  auto new_handle = HandleGLES::Create(type);
137  if (new_handle.IsDead()) {
138  return HandleGLES::DeadHandle();
139  }
140  WriterLock handles_lock(handles_mutex_);
141  auto gl_handle = CanReactOnCurrentThread()
142  ? CreateGLHandle(GetProcTable(), type)
143  : std::nullopt;
144  handles_[new_handle] = LiveHandle{gl_handle};
145  return new_handle;
146 }
147 
149  WriterLock handles_lock(handles_mutex_);
150  if (auto found = handles_.find(handle); found != handles_.end()) {
151  found->second.pending_collection = true;
152  }
153 }
154 
156  if (!CanReactOnCurrentThread()) {
157  return false;
158  }
159  TRACE_EVENT0("impeller", "ReactorGLES::React");
160  while (HasPendingOperations()) {
161  if (!ReactOnce()) {
162  return false;
163  }
164  }
165  return true;
166 }
167 
169  switch (type) {
171  FML_UNREACHABLE();
174  case HandleType::kBuffer:
182  }
183  FML_UNREACHABLE();
184 }
185 
186 bool ReactorGLES::ReactOnce() {
187  if (!IsValid()) {
188  return false;
189  }
190  TRACE_EVENT0("impeller", __FUNCTION__);
191  return ConsolidateHandles() && FlushOps();
192 }
193 
194 bool ReactorGLES::ConsolidateHandles() {
195  TRACE_EVENT0("impeller", __FUNCTION__);
196  const auto& gl = GetProcTable();
197  WriterLock handles_lock(handles_mutex_);
198  std::vector<HandleGLES> handles_to_delete;
199  for (auto& handle : handles_) {
200  // Collect dead handles.
201  if (handle.second.pending_collection) {
202  // This could be false if the handle was created and collected without
203  // use. We still need to get rid of map entry.
204  if (handle.second.name.has_value()) {
205  CollectGLHandle(gl, handle.first.type, handle.second.name.value());
206  }
207  handles_to_delete.push_back(handle.first);
208  continue;
209  }
210  // Create live handles.
211  if (!handle.second.name.has_value()) {
212  auto gl_handle = CreateGLHandle(gl, handle.first.type);
213  if (!gl_handle) {
214  VALIDATION_LOG << "Could not create GL handle.";
215  return false;
216  }
217  handle.second.name = gl_handle;
218  }
219  // Set pending debug labels.
220  if (handle.second.pending_debug_label.has_value()) {
221  if (gl.SetDebugLabel(ToDebugResourceType(handle.first.type),
222  handle.second.name.value(),
223  handle.second.pending_debug_label.value())) {
224  handle.second.pending_debug_label = std::nullopt;
225  }
226  }
227  }
228  for (const auto& handle_to_delete : handles_to_delete) {
229  handles_.erase(handle_to_delete);
230  }
231  return true;
232 }
233 
234 bool ReactorGLES::FlushOps() {
235  TRACE_EVENT0("impeller", __FUNCTION__);
236  // Do NOT hold the ops or handles locks while performing operations in case
237  // the ops enqueue more ops.
238  decltype(ops_) ops;
239  {
240  Lock ops_lock(ops_mutex_);
241  std::swap(ops_, ops);
242  }
243  for (const auto& op : ops) {
244  TRACE_EVENT0("impeller", "ReactorGLES::Operation");
245  op(*this);
246  }
247  return true;
248 }
249 
250 void ReactorGLES::SetDebugLabel(const HandleGLES& handle, std::string label) {
251  if (!can_set_debug_labels_) {
252  return;
253  }
254  if (handle.IsDead()) {
255  return;
256  }
257  WriterLock handles_lock(handles_mutex_);
258  if (auto found = handles_.find(handle); found != handles_.end()) {
259  found->second.pending_debug_label = std::move(label);
260  }
261 }
262 
263 bool ReactorGLES::CanReactOnCurrentThread() const {
264  std::vector<WorkerID> dead_workers;
265  Lock lock(workers_mutex_);
266  for (const auto& worker : workers_) {
267  auto worker_ptr = worker.second.lock();
268  if (!worker_ptr) {
269  dead_workers.push_back(worker.first);
270  continue;
271  }
272  if (worker_ptr->CanReactorReactOnCurrentThreadNow(*this)) {
273  return true;
274  }
275  }
276  for (const auto& worker_id : dead_workers) {
277  workers_.erase(worker_id);
278  }
279  return false;
280 }
281 
282 } // namespace impeller
impeller::ReactorGLES::Operation
std::function< void(const ReactorGLES &reactor)> Operation
Definition: reactor_gles.h:53
impeller::ReactorGLES::GetProcTable
const ProcTableGLES & GetProcTable() const
Definition: reactor_gles.cc:47
impeller::HandleGLES::DeadHandle
static HandleGLES DeadHandle()
Definition: handle_gles.h:38
impeller::ReactorGLES::ReactorGLES
ReactorGLES(std::unique_ptr< ProcTableGLES > gl)
Definition: reactor_gles.cc:14
impeller::ReactorGLES::SetDebugLabel
void SetDebugLabel(const HandleGLES &handle, std::string label)
Definition: reactor_gles.cc:250
impeller::HandleType::kRenderBuffer
@ kRenderBuffer
impeller::WriterLock
Definition: thread.h:83
impeller::ReactorGLES::CreateHandle
HandleGLES CreateHandle(HandleType type)
Definition: reactor_gles.cc:132
impeller::HandleType
HandleType
Definition: handle_gles.h:21
impeller::HandleType::kTexture
@ kTexture
impeller::Lock
Definition: thread.h:54
impeller::ToDebugResourceType
static DebugResourceType ToDebugResourceType(HandleType type)
Definition: reactor_gles.cc:168
impeller::DebugResourceType::kBuffer
@ kBuffer
impeller::HandleType::kUnknown
@ kUnknown
impeller::DebugResourceType::kProgram
@ kProgram
impeller::HandleGLES::IsDead
constexpr bool IsDead() const
Definition: handle_gles.h:42
validation.h
impeller::ReactorGLES::React
bool React()
Definition: reactor_gles.cc:155
impeller::DebugResourceType::kTexture
@ kTexture
impeller::ReaderLock
Definition: thread.h:68
impeller::ReactorGLES::~ReactorGLES
~ReactorGLES()
impeller::HandleGLES
Definition: handle_gles.h:34
impeller::ProcTableGLES
Definition: proc_table_gles.h:188
impeller::ReactorGLES::RemoveWorker
bool RemoveWorker(WorkerID)
Definition: reactor_gles.cc:37
impeller::ReactorGLES::IsValid
bool IsValid() const
Definition: reactor_gles.cc:26
impeller::DebugResourceType::kRenderBuffer
@ kRenderBuffer
impeller::HandleType::kProgram
@ kProgram
impeller::CreateGLHandle
static std::optional< GLuint > CreateGLHandle(const ProcTableGLES &gl, HandleType type)
Definition: reactor_gles.cc:83
VALIDATION_LOG
#define VALIDATION_LOG
Definition: validation.h:60
impeller::ReactorGLES::CollectHandle
void CollectHandle(HandleGLES handle)
Definition: reactor_gles.cc:148
reactor_gles.h
impeller::DebugResourceType
DebugResourceType
Definition: proc_table_gles.h:179
impeller::ReactorGLES::AddOperation
bool AddOperation(Operation operation)
Definition: reactor_gles.cc:70
impeller::ReactorGLES::AddWorker
WorkerID AddWorker(std::weak_ptr< Worker > worker)
Definition: reactor_gles.cc:30
std
Definition: comparable.h:98
impeller::CollectGLHandle
static bool CollectGLHandle(const ProcTableGLES &gl, HandleType type, GLuint handle)
Definition: reactor_gles.cc:107
impeller::HandleType::kFrameBuffer
@ kFrameBuffer
impeller::DebugResourceType::kFrameBuffer
@ kFrameBuffer
impeller::UniqueID
Definition: comparable.h:19
impeller::HandleType::kBuffer
@ kBuffer
impeller
Definition: aiks_context.cc:10
impeller::ReactorGLES::GetGLHandle
std::optional< GLuint > GetGLHandle(const HandleGLES &handle) const
Definition: reactor_gles.cc:52