blob: f4d9ca9396233ec6c710352172632dfd49e79952 [file] [log] [blame]
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "impeller/renderer/backend/gles/reactor_gles.h"
#include <algorithm>
#include "flutter/fml/trace_event.h"
#include "impeller/base/validation.h"
namespace impeller {
ReactorGLES::ReactorGLES(std::unique_ptr<ProcTableGLES> gl)
: proc_table_(std::move(gl)) {
if (!proc_table_ || !proc_table_->IsValid()) {
VALIDATION_LOG << "Proc table was invalid.";
return;
}
can_set_debug_labels_ = proc_table_->GetDescription()->HasDebugExtension();
is_valid_ = true;
}
ReactorGLES::~ReactorGLES() = default;
bool ReactorGLES::IsValid() const {
return is_valid_;
}
ReactorGLES::WorkerID ReactorGLES::AddWorker(std::weak_ptr<Worker> worker) {
Lock lock(workers_mutex_);
auto id = WorkerID{};
workers_[id] = std::move(worker);
return id;
}
bool ReactorGLES::RemoveWorker(WorkerID worker) {
Lock lock(workers_mutex_);
return workers_.erase(worker) == 1;
}
bool ReactorGLES::HasPendingOperations() const {
Lock ops_lock(ops_mutex_);
return !ops_.empty();
}
const ProcTableGLES& ReactorGLES::GetProcTable() const {
FML_DCHECK(IsValid());
return *proc_table_;
}
std::optional<GLuint> ReactorGLES::GetGLHandle(const HandleGLES& handle) const {
ReaderLock handles_lock(handles_mutex_);
if (auto found = handles_.find(handle); found != handles_.end()) {
if (found->second.pending_collection) {
VALIDATION_LOG
<< "Attempted to acquire a handle that was pending collection.";
return std::nullopt;
}
if (!found->second.name.has_value()) {
VALIDATION_LOG << "Attempt to acquire a handle outside of an operation.";
return std::nullopt;
}
return found->second.name;
}
VALIDATION_LOG << "Attempted to acquire an invalid GL handle.";
return std::nullopt;
}
bool ReactorGLES::AddOperation(Operation operation) {
if (!operation) {
return false;
}
{
Lock ops_lock(ops_mutex_);
ops_.emplace_back(std::move(operation));
}
// Attempt a reaction if able but it is not an error if this isn't possible.
[[maybe_unused]] auto result = React();
return true;
}
static std::optional<GLuint> CreateGLHandle(const ProcTableGLES& gl,
HandleType type) {
GLuint handle = GL_NONE;
switch (type) {
case HandleType::kUnknown:
return std::nullopt;
case HandleType::kTexture:
gl.GenTextures(1u, &handle);
return handle;
case HandleType::kBuffer:
gl.GenBuffers(1u, &handle);
return handle;
case HandleType::kProgram:
return gl.CreateProgram();
case HandleType::kRenderBuffer:
gl.GenRenderbuffers(1u, &handle);
return handle;
case HandleType::kFrameBuffer:
gl.GenFramebuffers(1u, &handle);
return handle;
}
return std::nullopt;
}
static bool CollectGLHandle(const ProcTableGLES& gl,
HandleType type,
GLuint handle) {
switch (type) {
case HandleType::kUnknown:
return false;
case HandleType::kTexture:
gl.DeleteTextures(1u, &handle);
return true;
case HandleType::kBuffer:
gl.DeleteBuffers(1u, &handle);
return true;
case HandleType::kProgram:
gl.DeleteProgram(handle);
return true;
case HandleType::kRenderBuffer:
gl.DeleteRenderbuffers(1u, &handle);
return true;
case HandleType::kFrameBuffer:
gl.DeleteFramebuffers(1u, &handle);
return true;
}
return false;
}
HandleGLES ReactorGLES::CreateHandle(HandleType type) {
if (type == HandleType::kUnknown) {
return HandleGLES::DeadHandle();
}
auto new_handle = HandleGLES::Create(type);
if (new_handle.IsDead()) {
return HandleGLES::DeadHandle();
}
WriterLock handles_lock(handles_mutex_);
auto gl_handle = CanReactOnCurrentThread()
? CreateGLHandle(GetProcTable(), type)
: std::nullopt;
handles_[new_handle] = LiveHandle{gl_handle};
return new_handle;
}
void ReactorGLES::CollectHandle(HandleGLES handle) {
WriterLock handles_lock(handles_mutex_);
if (auto found = handles_.find(handle); found != handles_.end()) {
found->second.pending_collection = true;
}
}
bool ReactorGLES::React() {
if (!CanReactOnCurrentThread()) {
return false;
}
TRACE_EVENT0("impeller", "ReactorGLES::React");
while (HasPendingOperations()) {
if (!ReactOnce()) {
return false;
}
}
return true;
}
static DebugResourceType ToDebugResourceType(HandleType type) {
switch (type) {
case HandleType::kUnknown:
FML_UNREACHABLE();
case HandleType::kTexture:
return DebugResourceType::kTexture;
case HandleType::kBuffer:
return DebugResourceType::kBuffer;
case HandleType::kProgram:
return DebugResourceType::kProgram;
case HandleType::kRenderBuffer:
return DebugResourceType::kRenderBuffer;
case HandleType::kFrameBuffer:
return DebugResourceType::kFrameBuffer;
}
FML_UNREACHABLE();
}
bool ReactorGLES::ReactOnce() {
if (!IsValid()) {
return false;
}
TRACE_EVENT0("impeller", __FUNCTION__);
return ConsolidateHandles() && FlushOps();
}
bool ReactorGLES::ConsolidateHandles() {
TRACE_EVENT0("impeller", __FUNCTION__);
const auto& gl = GetProcTable();
WriterLock handles_lock(handles_mutex_);
std::vector<HandleGLES> handles_to_delete;
for (auto& handle : handles_) {
// Collect dead handles.
if (handle.second.pending_collection) {
// This could be false if the handle was created and collected without
// use. We still need to get rid of map entry.
if (handle.second.name.has_value()) {
CollectGLHandle(gl, handle.first.type, handle.second.name.value());
}
handles_to_delete.push_back(handle.first);
continue;
}
// Create live handles.
if (!handle.second.name.has_value()) {
auto gl_handle = CreateGLHandle(gl, handle.first.type);
if (!gl_handle) {
VALIDATION_LOG << "Could not create GL handle.";
return false;
}
handle.second.name = gl_handle;
}
// Set pending debug labels.
if (handle.second.pending_debug_label.has_value()) {
if (gl.SetDebugLabel(ToDebugResourceType(handle.first.type),
handle.second.name.value(),
handle.second.pending_debug_label.value())) {
handle.second.pending_debug_label = std::nullopt;
}
}
}
for (const auto& handle_to_delete : handles_to_delete) {
handles_.erase(handle_to_delete);
}
return true;
}
bool ReactorGLES::FlushOps() {
TRACE_EVENT0("impeller", __FUNCTION__);
// Do NOT hold the ops or handles locks while performing operations in case
// the ops enqueue more ops.
decltype(ops_) ops;
{
Lock ops_lock(ops_mutex_);
std::swap(ops_, ops);
}
for (const auto& op : ops) {
TRACE_EVENT0("impeller", "ReactorGLES::Operation");
op(*this);
}
return true;
}
void ReactorGLES::SetDebugLabel(const HandleGLES& handle, std::string label) {
if (!can_set_debug_labels_) {
return;
}
if (handle.IsDead()) {
return;
}
WriterLock handles_lock(handles_mutex_);
if (auto found = handles_.find(handle); found != handles_.end()) {
found->second.pending_debug_label = std::move(label);
}
}
bool ReactorGLES::CanReactOnCurrentThread() const {
std::vector<WorkerID> dead_workers;
Lock lock(workers_mutex_);
for (const auto& worker : workers_) {
auto worker_ptr = worker.second.lock();
if (!worker_ptr) {
dead_workers.push_back(worker.first);
continue;
}
if (worker_ptr->CanReactorReactOnCurrentThreadNow(*this)) {
return true;
}
}
for (const auto& worker_id : dead_workers) {
workers_.erase(worker_id);
}
return false;
}
} // namespace impeller