9 #include "flutter/fml/trace_event.h"
10 #include "fml/closure.h"
11 #include "fml/logging.h"
17 std::optional<ReactorGLES::GLStorage> ReactorGLES::CreateGLHandle(
18 const ProcTableGLES& gl,
20 GLStorage handle = GLStorage{.handle = GL_NONE};
25 gl.GenTextures(1u, &handle.handle);
28 gl.GenBuffers(1u, &handle.handle);
31 return GLStorage{.handle = gl.CreateProgram()};
33 gl.GenRenderbuffers(1u, &handle.handle);
36 gl.GenFramebuffers(1u, &handle.handle);
39 return GLStorage{.sync = gl.FenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0)};
45 bool ReactorGLES::CollectGLHandle(
const ProcTableGLES& gl,
47 ReactorGLES::GLStorage handle) {
52 gl.DeleteTextures(1u, &handle.handle);
55 gl.DeleteBuffers(1u, &handle.handle);
58 gl.DeleteProgram(handle.handle);
61 gl.DeleteRenderbuffers(1u, &handle.handle);
64 gl.DeleteFramebuffers(1u, &handle.handle);
67 gl.DeleteSync(handle.sync);
74 : proc_table_(
std::move(gl)) {
75 if (!proc_table_ || !proc_table_->IsValid()) {
79 can_set_debug_labels_ = proc_table_->GetDescription()->HasDebugExtension();
84 if (CanReactOnCurrentThread()) {
85 for (
auto& handle : handles_) {
86 if (handle.second.name.has_value()) {
87 CollectGLHandle(*proc_table_, handle.first.GetType(),
88 handle.second.name.value());
100 return can_set_debug_labels_;
104 Lock lock(workers_mutex_);
106 workers_[id] = std::move(worker);
111 Lock lock(workers_mutex_);
112 return workers_.erase(worker) == 1;
115 bool ReactorGLES::HasPendingOperations()
const {
116 auto thread_id = std::this_thread::get_id();
117 Lock ops_lock(ops_mutex_);
118 auto it = ops_.find(thread_id);
119 return it != ops_.end() ? !it->second.empty() :
false;
127 std::optional<ReactorGLES::GLStorage> ReactorGLES::GetHandle(
129 if (handle.untracked_id_.has_value()) {
130 return ReactorGLES::GLStorage{.integer = handle.untracked_id_.value()};
133 ReaderLock handles_lock(handles_mutex_);
134 if (
auto found = handles_.find(handle); found != handles_.end()) {
135 if (found->second.pending_collection) {
137 <<
"Attempted to acquire a handle that was pending collection.";
140 std::optional<ReactorGLES::GLStorage> name = found->second.name;
141 if (!name.has_value()) {
142 VALIDATION_LOG <<
"Attempt to acquire a handle outside of an operation.";
155 std::optional<ReactorGLES::GLStorage> gl_handle = GetHandle(handle);
156 if (gl_handle.has_value()) {
157 return gl_handle->handle;
166 std::optional<ReactorGLES::GLStorage> gl_handle = GetHandle(handle);
167 if (gl_handle.has_value()) {
168 return gl_handle->sync;
177 auto thread_id = std::this_thread::get_id();
179 Lock ops_lock(ops_mutex_);
180 ops_[thread_id].emplace_back(std::move(operation));
184 [[maybe_unused]]
auto result =
React();
190 const fml::closure& callback) {
194 FML_DCHECK(!handle.untracked_id_.has_value());
196 if (
auto found = handles_.find(handle); found != handles_.end()) {
197 found->second.callback = fml::ScopedCleanupClosure(callback);
204 FML_DCHECK(CanReactOnCurrentThread());
205 auto new_handle = HandleGLES::Create(type);
206 std::optional<ReactorGLES::GLStorage> gl_handle =
208 if (gl_handle.has_value()) {
209 new_handle.untracked_id_ = gl_handle.value().integer;
218 auto new_handle = HandleGLES::Create(type);
219 if (new_handle.IsDead()) {
223 std::optional<ReactorGLES::GLStorage> gl_handle;
224 if (external_handle != GL_NONE) {
225 gl_handle = ReactorGLES::GLStorage{.handle = external_handle};
226 }
else if (CanReactOnCurrentThread()) {
231 handles_[new_handle] = LiveHandle{gl_handle};
239 if (handle.untracked_id_.has_value()) {
240 LiveHandle live_handle(GLStorage{.integer = handle.untracked_id_.value()});
241 live_handle.pending_collection =
true;
243 handles_[handle] = std::move(live_handle);
246 if (
auto found = handles_.find(handle); found != handles_.end()) {
247 if (!found->second.pending_collection) {
248 handles_to_collect_count_ += 1;
250 found->second.pending_collection =
true;
256 if (!CanReactOnCurrentThread()) {
259 TRACE_EVENT0(
"impeller",
"ReactorGLES::React");
260 while (HasPendingOperations()) {
288 bool ReactorGLES::ReactOnce() {
292 TRACE_EVENT0(
"impeller", __FUNCTION__);
293 return ConsolidateHandles() && FlushOps();
296 bool ReactorGLES::ConsolidateHandles() {
297 TRACE_EVENT0(
"impeller", __FUNCTION__);
299 std::thread::id current_thread = std::this_thread::get_id();
300 std::vector<std::tuple<HandleGLES, std::optional<GLStorage>>>
302 std::vector<std::tuple<DebugResourceType, GLint, std::string>>
305 WriterLock handles_lock(handles_mutex_);
306 handles_to_delete.reserve(handles_to_collect_count_);
307 handles_to_collect_count_ = 0;
308 for (
auto& handle : handles_) {
310 if (handle.second.pending_collection) {
314 const auto& owner_thread = handle.first.owner_thread_;
315 if (owner_thread.has_value() && *owner_thread != current_thread) {
318 std::optional<GLStorage> storage;
320 if (!handle.second.callback) {
321 storage = handle.second.name;
323 handles_to_delete.emplace_back(std::make_tuple(handle.first, storage));
327 if (!handle.second.name.has_value()) {
328 auto gl_handle = CreateGLHandle(gl, handle.first.
GetType());
333 handle.second.name = gl_handle;
336 if (handle.second.pending_debug_label.has_value() &&
338 handles_to_name.emplace_back(std::make_tuple(
340 handle.second.name.value().handle,
341 std::move(handle.second.pending_debug_label.value())));
342 handle.second.pending_debug_label = std::nullopt;
345 for (
const auto& handle_to_delete : handles_to_delete) {
346 handles_.erase(std::get<0>(handle_to_delete));
350 for (
const auto& handle : handles_to_name) {
351 gl.SetDebugLabel(std::get<0>(handle), std::get<1>(handle),
352 std::get<2>(handle));
354 for (
const auto& handle : handles_to_delete) {
355 const std::optional<GLStorage>& storage = std::get<1>(handle);
358 if (storage.has_value()) {
359 CollectGLHandle(gl, std::get<0>(handle).GetType(), storage.value());
366 bool ReactorGLES::FlushOps() {
367 TRACE_EVENT0(
"impeller", __FUNCTION__);
369 #ifdef IMPELLER_DEBUG
377 decltype(ops_)::mapped_type ops;
378 auto thread_id = std::this_thread::get_id();
380 Lock ops_lock(ops_mutex_);
381 std::swap(ops_[thread_id], ops);
383 for (
const auto& op : ops) {
384 TRACE_EVENT0(
"impeller",
"ReactorGLES::Operation");
390 void ReactorGLES::SetupDebugGroups() {
392 if (can_set_debug_labels_) {
393 proc_table_->DebugMessageControlKHR(GL_DONT_CARE,
403 std::string_view label) {
405 if (!can_set_debug_labels_) {
411 if (handle.untracked_id_.has_value()) {
412 FML_DCHECK(CanReactOnCurrentThread());
415 handle.untracked_id_.value(), label);
418 if (
auto found = handles_.find(handle); found != handles_.end()) {
419 found->second.pending_debug_label = label;
424 bool ReactorGLES::CanReactOnCurrentThread()
const {
425 std::vector<WorkerID> dead_workers;
426 Lock lock(workers_mutex_);
427 for (
const auto& worker : workers_) {
428 auto worker_ptr = worker.second.lock();
430 dead_workers.push_back(worker.first);
433 if (worker_ptr->CanReactorReactOnCurrentThreadNow(*
this)) {
437 for (
const auto& worker_id : dead_workers) {
438 workers_.erase(worker_id);
Represents a handle to an underlying OpenGL object. Unlike OpenGL object handles, these handles can b...
constexpr bool IsDead() const
Determines if the handle is dead.
HandleType GetType() const
static HandleGLES DeadHandle()
Creates a dead handle.
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...
bool CanSetDebugLabels() const
Whether the device is capable of writing debug labels.
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...
ReactorGLES(std::unique_ptr< ProcTableGLES > gl)
Create a new reactor. There are expensive and only one per application instance is necessary.
HandleGLES CreateUntrackedHandle(HandleType type) const
Create a handle that is not managed by ReactorGLES.
std::optional< GLsync > GetGLFence(const HandleGLES &handle) const
void CollectHandle(HandleGLES handle)
Collect a reactor handle.
HandleGLES CreateHandle(HandleType type, GLuint external_handle=GL_NONE)
Create a reactor handle.
std::function< void(const ReactorGLES &reactor)> Operation
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.
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.
bool RemoveWorker(WorkerID id)
Remove a previously added worker from the reactor. If the reactor has no workers, pending added opera...
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 ...
void SetDebugLabel(const HandleGLES &handle, std::string_view label)
Set the debug label on a reactor handle.
bool IsValid() const
If this is a valid reactor. Invalid reactors must be discarded immediately.
static DebugResourceType ToDebugResourceType(HandleType type)