17std::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)};
45bool 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;
115bool 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;
127std::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();
194 FML_DCHECK(!handle.untracked_id_.has_value());
196 if (
auto found = handles_.find(handle); found != handles_.end()) {
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()) {
260 while (HasPendingOperations()) {
288bool ReactorGLES::ReactOnce() {
293 return ConsolidateHandles() && FlushOps();
296bool ReactorGLES::ConsolidateHandles() {
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 handles_to_delete.emplace_back(
319 std::make_tuple(handle.first, handle.second.name));
323 if (!handle.second.name.has_value()) {
324 auto gl_handle = CreateGLHandle(gl, handle.first.
GetType());
329 handle.second.name = gl_handle;
332 if (handle.second.pending_debug_label.has_value() &&
334 handles_to_name.emplace_back(std::make_tuple(
336 handle.second.name.value().handle,
337 std::move(handle.second.pending_debug_label.value())));
338 handle.second.pending_debug_label = std::nullopt;
341 for (
const auto& handle_to_delete : handles_to_delete) {
342 handles_.erase(std::get<0>(handle_to_delete));
346 for (
const auto& handle : handles_to_name) {
347 gl.SetDebugLabel(std::get<0>(handle), std::get<1>(handle),
348 std::get<2>(handle));
350 for (
const auto& handle : handles_to_delete) {
351 const std::optional<GLStorage>& storage = std::get<1>(handle);
354 if (storage.has_value()) {
355 CollectGLHandle(gl, std::get<0>(handle).GetType(), storage.value());
362bool ReactorGLES::FlushOps() {
373 decltype(ops_)::mapped_type ops;
374 auto thread_id = std::this_thread::get_id();
376 Lock ops_lock(ops_mutex_);
377 std::swap(ops_[thread_id], ops);
379 for (
const auto& op : ops) {
386void ReactorGLES::SetupDebugGroups() {
388 if (can_set_debug_labels_) {
389 proc_table_->DebugMessageControlKHR(GL_DONT_CARE,
399 std::string_view label) {
401 if (!can_set_debug_labels_) {
407 if (handle.untracked_id_.has_value()) {
411 handle.untracked_id_.value(), label);
414 if (
auto found = handles_.find(handle); found != handles_.end()) {
415 found->second.pending_debug_label = label;
420bool ReactorGLES::CanReactOnCurrentThread()
const {
421 std::vector<WorkerID> dead_workers;
422 Lock lock(workers_mutex_);
423 for (
const auto& worker : workers_) {
424 auto worker_ptr = worker.second.lock();
426 dead_workers.push_back(worker.first);
429 if (worker_ptr->CanReactorReactOnCurrentThreadNow(*
this)) {
433 for (
const auto& worker_id : dead_workers) {
434 workers_.erase(worker_id);
Wraps a closure that is invoked in the destructor unless released by the caller.
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.
FlutterDesktopBinaryReply callback
#define FML_UNREACHABLE()
#define FML_DCHECK(condition)
std::function< void()> closure
static DebugResourceType ToDebugResourceType(HandleType type)
#define TRACE_EVENT0(category_group, name)