Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

[Impeller] Exploit dynamic state in OpenGL for fewer program links. #53764

Merged
merged 6 commits into from
Jul 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions ci/licenses_golden/licenses_flutter
Original file line number Diff line number Diff line change
Expand Up @@ -42231,6 +42231,8 @@ ORIGIN: ../../../flutter/impeller/renderer/backend/gles/surface_gles.cc + ../../
ORIGIN: ../../../flutter/impeller/renderer/backend/gles/surface_gles.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/renderer/backend/gles/texture_gles.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/renderer/backend/gles/texture_gles.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/renderer/backend/gles/unique_handle_gles.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/renderer/backend/gles/unique_handle_gles.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/renderer/backend/metal/allocator_mtl.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/renderer/backend/metal/allocator_mtl.mm + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/renderer/backend/metal/blit_pass_mtl.h + ../../../flutter/LICENSE
Expand Down Expand Up @@ -45097,6 +45099,8 @@ FILE: ../../../flutter/impeller/renderer/backend/gles/surface_gles.cc
FILE: ../../../flutter/impeller/renderer/backend/gles/surface_gles.h
FILE: ../../../flutter/impeller/renderer/backend/gles/texture_gles.cc
FILE: ../../../flutter/impeller/renderer/backend/gles/texture_gles.h
FILE: ../../../flutter/impeller/renderer/backend/gles/unique_handle_gles.cc
FILE: ../../../flutter/impeller/renderer/backend/gles/unique_handle_gles.h
FILE: ../../../flutter/impeller/renderer/backend/metal/allocator_mtl.h
FILE: ../../../flutter/impeller/renderer/backend/metal/allocator_mtl.mm
FILE: ../../../flutter/impeller/renderer/backend/metal/blit_pass_mtl.h
Expand Down
2 changes: 2 additions & 0 deletions impeller/fixtures/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ impeller_shaders("shader_fixtures") {
"sepia.frag",
"sepia.vert",
"simple.vert",
"spec_constant.frag",
"spec_constant.vert",
"stage1.comp",
"stage2.comp",
"swizzle.frag",
Expand Down
19 changes: 19 additions & 0 deletions impeller/fixtures/spec_constant.frag
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// 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.

in vec4 v_color;
in vec4 v_color2;

layout(constant_id = 0) const float some_fraction = 1.0;

out vec4 frag_color;

uniform FragInfo {
float time;
}
frag_info;

void main() {
frag_color = mix(v_color, v_color2, some_fraction);
}
16 changes: 16 additions & 0 deletions impeller/fixtures/spec_constant.vert
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// 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.

in vec2 position;
in vec4 color;
in vec4 color2;

out vec4 v_color;
out vec4 v_color2;

void main() {
gl_Position = vec4(position, 0.0, 1.0);
v_color = color;
v_color2 = color2;
}
4 changes: 4 additions & 0 deletions impeller/renderer/backend/gles/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,13 @@ impeller_component("gles_unittests") {
"test/mock_gles.cc",
"test/mock_gles.h",
"test/mock_gles_unittests.cc",
"test/pipeline_library_gles_unittests.cc",
"test/proc_table_gles_unittests.cc",
"test/specialization_constants_unittests.cc",
]
deps = [
":gles",
"//flutter/impeller/playground:playground_test",
"//flutter/testing:testing_lib",
]
}
Expand Down Expand Up @@ -80,6 +82,8 @@ impeller_component("gles") {
"surface_gles.h",
"texture_gles.cc",
"texture_gles.h",
"unique_handle_gles.cc",
"unique_handle_gles.h",
]

if (!is_android && !is_fuchsia) {
Expand Down
22 changes: 22 additions & 0 deletions impeller/renderer/backend/gles/handle_gles.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,35 @@ std::string HandleTypeToString(HandleType type);

class ReactorGLES;

//------------------------------------------------------------------------------
/// @brief Represents a handle to an underlying OpenGL object. Unlike
/// OpenGL object handles, these handles can be collected on any
/// thread as long as their destruction is scheduled in a reactor.
///
struct HandleGLES {
HandleType type = HandleType::kUnknown;
std::optional<UniqueID> name;

//----------------------------------------------------------------------------
/// @brief Creates a dead handle.
///
/// @return The handle.
///
static HandleGLES DeadHandle() {
return HandleGLES{HandleType::kUnknown, std::nullopt};
}

//----------------------------------------------------------------------------
/// @brief Determines if the handle is dead.
///
/// @return True if dead, False otherwise.
///
constexpr bool IsDead() const { return !name.has_value(); }

//----------------------------------------------------------------------------
/// @brief Get the hash value of this handle. Handles can be used as map
/// keys.
///
struct Hash {
std::size_t operator()(const HandleGLES& handle) const {
return fml::HashCombine(
Expand All @@ -46,6 +65,9 @@ struct HandleGLES {
}
};

//----------------------------------------------------------------------------
/// @brief A comparer used to test the equality of two handles.
///
struct Equal {
bool operator()(const HandleGLES& lhs, const HandleGLES& rhs) const {
return lhs.type == rhs.type && lhs.name == rhs.name;
Expand Down
24 changes: 12 additions & 12 deletions impeller/renderer/backend/gles/pipeline_gles.cc
Original file line number Diff line number Diff line change
Expand Up @@ -8,30 +8,30 @@ namespace impeller {

PipelineGLES::PipelineGLES(ReactorGLES::Ref reactor,
std::weak_ptr<PipelineLibrary> library,
const PipelineDescriptor& desc)
const PipelineDescriptor& desc,
std::shared_ptr<UniqueHandleGLES> handle)
: Pipeline(std::move(library), desc),
reactor_(std::move(reactor)),
handle_(reactor_ ? reactor_->CreateHandle(HandleType::kProgram)
: HandleGLES::DeadHandle()),
is_valid_(!handle_.IsDead()) {
handle_(std::move(handle)),
is_valid_(handle_->IsValid()) {
if (is_valid_) {
reactor_->SetDebugLabel(handle_, GetDescriptor().GetLabel());
reactor_->SetDebugLabel(handle_->Get(), GetDescriptor().GetLabel());
}
}

// |Pipeline|
PipelineGLES::~PipelineGLES() {
if (!handle_.IsDead()) {
reactor_->CollectHandle(handle_);
}
}
PipelineGLES::~PipelineGLES() = default;

// |Pipeline|
bool PipelineGLES::IsValid() const {
return is_valid_;
}

const HandleGLES& PipelineGLES::GetProgramHandle() const {
return handle_->Get();
}

const std::shared_ptr<UniqueHandleGLES> PipelineGLES::GetSharedHandle() const {
return handle_;
}

Expand All @@ -58,10 +58,10 @@ bool PipelineGLES::BuildVertexDescriptor(const ProcTableGLES& gl,
}

[[nodiscard]] bool PipelineGLES::BindProgram() const {
if (handle_.IsDead()) {
if (!handle_->IsValid()) {
return false;
}
auto handle = reactor_->GetGLHandle(handle_);
auto handle = reactor_->GetGLHandle(handle_->Get());
if (!handle.has_value()) {
return false;
}
Expand Down
9 changes: 6 additions & 3 deletions impeller/renderer/backend/gles/pipeline_gles.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@

#include "impeller/base/backend_cast.h"
#include "impeller/renderer/backend/gles/buffer_bindings_gles.h"
#include "impeller/renderer/backend/gles/handle_gles.h"
#include "impeller/renderer/backend/gles/reactor_gles.h"
#include "impeller/renderer/backend/gles/unique_handle_gles.h"
#include "impeller/renderer/pipeline.h"

namespace impeller {
Expand All @@ -24,6 +24,8 @@ class PipelineGLES final

const HandleGLES& GetProgramHandle() const;

const std::shared_ptr<UniqueHandleGLES> GetSharedHandle() const;

[[nodiscard]] bool BindProgram() const;

[[nodiscard]] bool UnbindProgram() const;
Expand All @@ -37,7 +39,7 @@ class PipelineGLES final
friend PipelineLibraryGLES;

ReactorGLES::Ref reactor_;
HandleGLES handle_;
std::shared_ptr<UniqueHandleGLES> handle_;
std::unique_ptr<BufferBindingsGLES> buffer_bindings_;
bool is_valid_ = false;

Expand All @@ -46,7 +48,8 @@ class PipelineGLES final

PipelineGLES(ReactorGLES::Ref reactor,
std::weak_ptr<PipelineLibrary> library,
const PipelineDescriptor& desc);
const PipelineDescriptor& desc,
std::shared_ptr<UniqueHandleGLES> handle);

PipelineGLES(const PipelineGLES&) = delete;

Expand Down
147 changes: 104 additions & 43 deletions impeller/renderer/backend/gles/pipeline_library_gles.cc
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,79 @@ bool PipelineLibraryGLES::IsValid() const {
return reactor_ != nullptr;
}

std::shared_ptr<PipelineGLES> PipelineLibraryGLES::CreatePipeline(
const std::weak_ptr<PipelineLibrary>& weak_library,
const PipelineDescriptor& desc,
const std::shared_ptr<const ShaderFunction>& vert_function,
const std::shared_ptr<const ShaderFunction>& frag_function) {
auto strong_library = weak_library.lock();

if (!strong_library) {
VALIDATION_LOG << "Library was collected before a pending pipeline "
"creation could finish.";
return nullptr;
}

auto& library = PipelineLibraryGLES::Cast(*strong_library);

const auto& reactor = library.GetReactor();

if (!reactor) {
return nullptr;
}

auto program_key = ProgramKey{vert_function, frag_function,
desc.GetSpecializationConstants()};

auto cached_program = library.GetProgramForKey(program_key);

const auto has_cached_program = !!cached_program;

auto pipeline = std::shared_ptr<PipelineGLES>(new PipelineGLES(
reactor, //
weak_library, //
desc, //
has_cached_program
? std::move(cached_program)
: std::make_shared<UniqueHandleGLES>(reactor, HandleType::kProgram)));

auto program = reactor->GetGLHandle(pipeline->GetProgramHandle());

if (!program.has_value()) {
VALIDATION_LOG << "Could not obtain program handle.";
return nullptr;
}

const auto link_result = !has_cached_program ? LinkProgram(*reactor, //
pipeline, //
vert_function, //
frag_function //
)
: true;

if (!link_result) {
VALIDATION_LOG << "Could not link pipeline program.";
return nullptr;
}

if (!pipeline->BuildVertexDescriptor(reactor->GetProcTable(),
program.value())) {
VALIDATION_LOG << "Could not build pipeline vertex descriptors.";
return nullptr;
}

if (!pipeline->IsValid()) {
VALIDATION_LOG << "Pipeline validation checks failed.";
return nullptr;
}

if (!has_cached_program) {
library.SetProgramForKey(program_key, pipeline->GetSharedHandle());
}

return pipeline;
}

// |PipelineLibrary|
PipelineFuture<PipelineDescriptor> PipelineLibraryGLES::GetPipeline(
PipelineDescriptor descriptor,
Expand Down Expand Up @@ -209,49 +282,16 @@ PipelineFuture<PipelineDescriptor> PipelineLibraryGLES::GetPipeline(
auto pipeline_future =
PipelineFuture<PipelineDescriptor>{descriptor, promise->get_future()};
pipelines_[descriptor] = pipeline_future;
auto weak_this = weak_from_this();

auto result = reactor_->AddOperation(
[promise, weak_this, reactor_ptr = reactor_, descriptor, vert_function,
frag_function](const ReactorGLES& reactor) {
auto strong_this = weak_this.lock();
if (!strong_this) {
promise->set_value(nullptr);
VALIDATION_LOG << "Library was collected before a pending pipeline "
"creation could finish.";
return;
}
auto pipeline = std::shared_ptr<PipelineGLES>(
new PipelineGLES(reactor_ptr, strong_this, descriptor));
auto program = reactor.GetGLHandle(pipeline->GetProgramHandle());
if (!program.has_value()) {
promise->set_value(nullptr);
VALIDATION_LOG << "Could not obtain program handle.";
return;
}
const auto link_result = LinkProgram(reactor, //
pipeline, //
vert_function, //
frag_function //
);
if (!link_result) {
promise->set_value(nullptr);
VALIDATION_LOG << "Could not link pipeline program.";
return;
}
if (!pipeline->BuildVertexDescriptor(reactor.GetProcTable(),
program.value())) {
promise->set_value(nullptr);
VALIDATION_LOG << "Could not build pipeline vertex descriptors.";
return;
}
if (!pipeline->IsValid()) {
promise->set_value(nullptr);
VALIDATION_LOG << "Pipeline validation checks failed.";
return;
}
promise->set_value(std::move(pipeline));
});

const auto result = reactor_->AddOperation([promise, //
weak_this = weak_from_this(), //
descriptor, //
vert_function, //
frag_function //
](const ReactorGLES& reactor) {
promise->set_value(
CreatePipeline(weak_this, descriptor, vert_function, frag_function));
});
FML_CHECK(result);

return pipeline_future;
Expand Down Expand Up @@ -280,4 +320,25 @@ void PipelineLibraryGLES::RemovePipelinesWithEntryPoint(
// |PipelineLibrary|
PipelineLibraryGLES::~PipelineLibraryGLES() = default;

const ReactorGLES::Ref& PipelineLibraryGLES::GetReactor() const {
return reactor_;
}

std::shared_ptr<UniqueHandleGLES> PipelineLibraryGLES::GetProgramForKey(
const ProgramKey& key) {
Lock lock(programs_mutex_);
auto found = programs_.find(key);
if (found != programs_.end()) {
return found->second;
}
return nullptr;
}

void PipelineLibraryGLES::SetProgramForKey(
const ProgramKey& key,
std::shared_ptr<UniqueHandleGLES> program) {
Lock lock(programs_mutex_);
programs_[key] = std::move(program);
}

} // namespace impeller
Loading