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};
236 if (handle.untracked_id_.has_value()) {
237 LiveHandle live_handle(GLStorage{.integer = handle.untracked_id_.value()});
238 live_handle.pending_collection =
true;
240 handles_[handle] = std::move(live_handle);
243 if (
auto found = handles_.find(handle); found != handles_.end()) {
244 if (!found->second.pending_collection) {
245 handles_to_collect_count_ += 1;
247 found->second.pending_collection =
true;
253 if (!CanReactOnCurrentThread()) {
256 TRACE_EVENT0(
"impeller",
"ReactorGLES::React");
257 while (HasPendingOperations()) {
285 bool ReactorGLES::ReactOnce() {
289 TRACE_EVENT0(
"impeller", __FUNCTION__);
290 return ConsolidateHandles() && FlushOps();
293 bool ReactorGLES::ConsolidateHandles() {
294 TRACE_EVENT0(
"impeller", __FUNCTION__);
296 std::vector<std::tuple<HandleGLES, std::optional<GLStorage>>>
298 std::vector<std::tuple<DebugResourceType, GLint, std::string>>
301 WriterLock handles_lock(handles_mutex_);
302 handles_to_delete.reserve(handles_to_collect_count_);
303 handles_to_collect_count_ = 0;
304 for (
auto& handle : handles_) {
306 if (handle.second.pending_collection) {
307 handles_to_delete.emplace_back(
308 std::make_tuple(handle.first, handle.second.name));
312 if (!handle.second.name.has_value()) {
313 auto gl_handle = CreateGLHandle(gl, handle.first.
GetType());
318 handle.second.name = gl_handle;
321 if (handle.second.pending_debug_label.has_value() &&
323 handles_to_name.emplace_back(std::make_tuple(
325 handle.second.name.value().handle,
326 std::move(handle.second.pending_debug_label.value())));
327 handle.second.pending_debug_label = std::nullopt;
330 for (
const auto& handle_to_delete : handles_to_delete) {
331 handles_.erase(std::get<0>(handle_to_delete));
335 for (
const auto& handle : handles_to_name) {
336 gl.SetDebugLabel(std::get<0>(handle), std::get<1>(handle),
337 std::get<2>(handle));
339 for (
const auto& handle : handles_to_delete) {
340 const std::optional<GLStorage>& storage = std::get<1>(handle);
343 if (storage.has_value()) {
344 CollectGLHandle(gl, std::get<0>(handle).GetType(), storage.value());
351 bool ReactorGLES::FlushOps() {
352 TRACE_EVENT0(
"impeller", __FUNCTION__);
354 #ifdef IMPELLER_DEBUG
362 decltype(ops_)::mapped_type ops;
363 auto thread_id = std::this_thread::get_id();
365 Lock ops_lock(ops_mutex_);
366 std::swap(ops_[thread_id], ops);
368 for (
const auto& op : ops) {
369 TRACE_EVENT0(
"impeller",
"ReactorGLES::Operation");
375 void ReactorGLES::SetupDebugGroups() {
377 if (can_set_debug_labels_) {
378 proc_table_->DebugMessageControlKHR(GL_DONT_CARE,
388 std::string_view label) {
390 if (!can_set_debug_labels_) {
396 if (handle.untracked_id_.has_value()) {
397 FML_DCHECK(CanReactOnCurrentThread());
400 handle.untracked_id_.value(), label);
403 if (
auto found = handles_.find(handle); found != handles_.end()) {
404 found->second.pending_debug_label = label;
409 bool ReactorGLES::CanReactOnCurrentThread()
const {
410 std::vector<WorkerID> dead_workers;
411 Lock lock(workers_mutex_);
412 for (
const auto& worker : workers_) {
413 auto worker_ptr = worker.second.lock();
415 dead_workers.push_back(worker.first);
418 if (worker_ptr->CanReactorReactOnCurrentThreadNow(*
this)) {
422 for (
const auto& worker_id : dead_workers) {
423 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)