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::vector<std::tuple<HandleGLES, std::optional<GLStorage>>>
301 std::vector<std::tuple<DebugResourceType, GLint, std::string>>
304 WriterLock handles_lock(handles_mutex_);
305 handles_to_delete.reserve(handles_to_collect_count_);
306 handles_to_collect_count_ = 0;
307 for (
auto& handle : handles_) {
309 if (handle.second.pending_collection) {
310 handles_to_delete.emplace_back(
311 std::make_tuple(handle.first, handle.second.name));
315 if (!handle.second.name.has_value()) {
316 auto gl_handle = CreateGLHandle(gl, handle.first.
GetType());
321 handle.second.name = gl_handle;
324 if (handle.second.pending_debug_label.has_value() &&
326 handles_to_name.emplace_back(std::make_tuple(
328 handle.second.name.value().handle,
329 std::move(handle.second.pending_debug_label.value())));
330 handle.second.pending_debug_label = std::nullopt;
333 for (
const auto& handle_to_delete : handles_to_delete) {
334 handles_.erase(std::get<0>(handle_to_delete));
338 for (
const auto& handle : handles_to_name) {
339 gl.SetDebugLabel(std::get<0>(handle), std::get<1>(handle),
340 std::get<2>(handle));
342 for (
const auto& handle : handles_to_delete) {
343 const std::optional<GLStorage>& storage = std::get<1>(handle);
346 if (storage.has_value()) {
347 CollectGLHandle(gl, std::get<0>(handle).GetType(), storage.value());
354 bool ReactorGLES::FlushOps() {
355 TRACE_EVENT0(
"impeller", __FUNCTION__);
357 #ifdef IMPELLER_DEBUG
365 decltype(ops_)::mapped_type ops;
366 auto thread_id = std::this_thread::get_id();
368 Lock ops_lock(ops_mutex_);
369 std::swap(ops_[thread_id], ops);
371 for (
const auto& op : ops) {
372 TRACE_EVENT0(
"impeller",
"ReactorGLES::Operation");
378 void ReactorGLES::SetupDebugGroups() {
380 if (can_set_debug_labels_) {
381 proc_table_->DebugMessageControlKHR(GL_DONT_CARE,
391 std::string_view label) {
393 if (!can_set_debug_labels_) {
399 if (handle.untracked_id_.has_value()) {
400 FML_DCHECK(CanReactOnCurrentThread());
403 handle.untracked_id_.value(), label);
406 if (
auto found = handles_.find(handle); found != handles_.end()) {
407 found->second.pending_debug_label = label;
412 bool ReactorGLES::CanReactOnCurrentThread()
const {
413 std::vector<WorkerID> dead_workers;
414 Lock lock(workers_mutex_);
415 for (
const auto& worker : workers_) {
416 auto worker_ptr = worker.second.lock();
418 dead_workers.push_back(worker.first);
421 if (worker_ptr->CanReactorReactOnCurrentThreadNow(*
this)) {
425 for (
const auto& worker_id : dead_workers) {
426 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)