blob: 36b5b11d358e8405574a209cd8e8134b090b8073 [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_;
}
bool ReactorGLES::HasPendingOperations() const {
return !pending_operations_.empty() || !gl_handles_to_collect_.empty();
}
const ProcTableGLES& ReactorGLES::GetProcTable() const {
FML_DCHECK(IsValid());
return *proc_table_;
}
std::optional<GLuint> ReactorGLES::GetGLHandle(const GLESHandle& handle) const {
auto found = live_gl_handles_.find(handle);
if (found != live_gl_handles_.end()) {
return found->second;
}
return std::nullopt;
}
bool ReactorGLES::AddOperation(Operation operation) {
if (!operation) {
return false;
}
pending_operations_.emplace_back(std::move(operation));
return React();
}
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;
}
GLESHandle ReactorGLES::CreateHandle(HandleType type) {
if (type == HandleType::kUnknown) {
return GLESHandle::DeadHandle();
}
auto new_handle = GLESHandle::Create(type);
if (new_handle.IsDead()) {
return GLESHandle::DeadHandle();
}
live_gl_handles_[new_handle] =
in_reaction_ ? CreateGLHandle(GetProcTable(), type) : std::nullopt;
return new_handle;
}
void ReactorGLES::CollectHandle(GLESHandle handle) {
auto live_handle = live_gl_handles_.find(handle);
if (live_handle == live_gl_handles_.end()) {
return;
}
if (live_handle->second.has_value()) {
gl_handles_to_collect_[live_handle->first] = live_handle->second.value();
}
live_gl_handles_.erase(live_handle);
}
bool ReactorGLES::React() {
TRACE_EVENT0("impeller", "ReactorGLES::React");
in_reaction_ = true;
fml::ScopedCleanupClosure reset_in_reaction([&]() { in_reaction_ = false; });
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;
}
const auto& gl = GetProcTable();
//----------------------------------------------------------------------------
/// Collect all the handles for whom there is a GL handle sibling.
///
for (const auto& handle_to_collect : gl_handles_to_collect_) {
if (!CollectGLHandle(gl, // proc table
handle_to_collect.first.type, // handle type
handle_to_collect.second // GL handle name
)) {
VALIDATION_LOG << "Could not collect GL handle.";
return false;
}
}
gl_handles_to_collect_.clear();
//----------------------------------------------------------------------------
/// Make sure all pending handles have a GL handle sibling.
///
for (auto& live_handle : live_gl_handles_) {
if (live_handle.second.has_value()) {
// Already a realized GL handle.
continue;
}
auto gl_handle = CreateGLHandle(gl, live_handle.first.type);
if (!gl_handle.has_value()) {
VALIDATION_LOG << "Could not create GL handle.";
return false;
}
live_handle.second = gl_handle;
}
if (can_set_debug_labels_) {
for (const auto& label : pending_debug_labels_) {
auto live_handle = live_gl_handles_.find(label.first);
if (live_handle == live_gl_handles_.end() ||
!live_handle->second.has_value()) {
continue;
}
gl.SetDebugLabel(ToDebugResourceType(label.first.type), // type
live_handle->second.value(), // name
label.second // label
);
}
}
pending_debug_labels_.clear();
//----------------------------------------------------------------------------
/// Flush all pending operations in order.
///
auto operations = std::move(pending_operations_);
for (const auto& operation : operations) {
operation(*this);
}
pending_operations_.clear();
return true;
}
void ReactorGLES::SetDebugLabel(const GLESHandle& handle, std::string label) {
if (!can_set_debug_labels_) {
return;
}
if (label.empty()) {
return;
}
if (handle.IsDead()) {
return;
}
if (in_reaction_) {
if (auto found = live_gl_handles_.find(handle);
found != live_gl_handles_.end() && found->second.has_value()) {
GetProcTable().SetDebugLabel(
ToDebugResourceType(found->first.type), // type
found->second.value(), // name
label // label
);
return;
}
}
pending_debug_labels_[handle] = std::move(label);
}
} // namespace impeller