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

Commit a3ba6c9

Browse files
authored
[Impeller] Exploit dynamic state in OpenGL for fewer program links. (#53764)
Fixes flutter/flutter#145125
1 parent e243b58 commit a3ba6c9

13 files changed

+424
-61
lines changed

ci/licenses_golden/licenses_flutter

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42231,6 +42231,8 @@ ORIGIN: ../../../flutter/impeller/renderer/backend/gles/surface_gles.cc + ../../
4223142231
ORIGIN: ../../../flutter/impeller/renderer/backend/gles/surface_gles.h + ../../../flutter/LICENSE
4223242232
ORIGIN: ../../../flutter/impeller/renderer/backend/gles/texture_gles.cc + ../../../flutter/LICENSE
4223342233
ORIGIN: ../../../flutter/impeller/renderer/backend/gles/texture_gles.h + ../../../flutter/LICENSE
42234+
ORIGIN: ../../../flutter/impeller/renderer/backend/gles/unique_handle_gles.cc + ../../../flutter/LICENSE
42235+
ORIGIN: ../../../flutter/impeller/renderer/backend/gles/unique_handle_gles.h + ../../../flutter/LICENSE
4223442236
ORIGIN: ../../../flutter/impeller/renderer/backend/metal/allocator_mtl.h + ../../../flutter/LICENSE
4223542237
ORIGIN: ../../../flutter/impeller/renderer/backend/metal/allocator_mtl.mm + ../../../flutter/LICENSE
4223642238
ORIGIN: ../../../flutter/impeller/renderer/backend/metal/blit_pass_mtl.h + ../../../flutter/LICENSE
@@ -45097,6 +45099,8 @@ FILE: ../../../flutter/impeller/renderer/backend/gles/surface_gles.cc
4509745099
FILE: ../../../flutter/impeller/renderer/backend/gles/surface_gles.h
4509845100
FILE: ../../../flutter/impeller/renderer/backend/gles/texture_gles.cc
4509945101
FILE: ../../../flutter/impeller/renderer/backend/gles/texture_gles.h
45102+
FILE: ../../../flutter/impeller/renderer/backend/gles/unique_handle_gles.cc
45103+
FILE: ../../../flutter/impeller/renderer/backend/gles/unique_handle_gles.h
4510045104
FILE: ../../../flutter/impeller/renderer/backend/metal/allocator_mtl.h
4510145105
FILE: ../../../flutter/impeller/renderer/backend/metal/allocator_mtl.mm
4510245106
FILE: ../../../flutter/impeller/renderer/backend/metal/blit_pass_mtl.h

impeller/fixtures/BUILD.gn

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ impeller_shaders("shader_fixtures") {
3838
"sepia.frag",
3939
"sepia.vert",
4040
"simple.vert",
41+
"spec_constant.frag",
42+
"spec_constant.vert",
4143
"stage1.comp",
4244
"stage2.comp",
4345
"swizzle.frag",

impeller/fixtures/spec_constant.frag

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
in vec4 v_color;
6+
in vec4 v_color2;
7+
8+
layout(constant_id = 0) const float some_fraction = 1.0;
9+
10+
out vec4 frag_color;
11+
12+
uniform FragInfo {
13+
float time;
14+
}
15+
frag_info;
16+
17+
void main() {
18+
frag_color = mix(v_color, v_color2, some_fraction);
19+
}

impeller/fixtures/spec_constant.vert

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
in vec2 position;
6+
in vec4 color;
7+
in vec4 color2;
8+
9+
out vec4 v_color;
10+
out vec4 v_color2;
11+
12+
void main() {
13+
gl_Position = vec4(position, 0.0, 1.0);
14+
v_color = color;
15+
v_color2 = color2;
16+
}

impeller/renderer/backend/gles/BUILD.gn

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,13 @@ impeller_component("gles_unittests") {
2020
"test/mock_gles.cc",
2121
"test/mock_gles.h",
2222
"test/mock_gles_unittests.cc",
23+
"test/pipeline_library_gles_unittests.cc",
2324
"test/proc_table_gles_unittests.cc",
2425
"test/specialization_constants_unittests.cc",
2526
]
2627
deps = [
2728
":gles",
29+
"//flutter/impeller/playground:playground_test",
2830
"//flutter/testing:testing_lib",
2931
]
3032
}
@@ -80,6 +82,8 @@ impeller_component("gles") {
8082
"surface_gles.h",
8183
"texture_gles.cc",
8284
"texture_gles.h",
85+
"unique_handle_gles.cc",
86+
"unique_handle_gles.h",
8387
]
8488

8589
if (!is_android && !is_fuchsia) {

impeller/renderer/backend/gles/handle_gles.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,16 +28,35 @@ std::string HandleTypeToString(HandleType type);
2828

2929
class ReactorGLES;
3030

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

40+
//----------------------------------------------------------------------------
41+
/// @brief Creates a dead handle.
42+
///
43+
/// @return The handle.
44+
///
3545
static HandleGLES DeadHandle() {
3646
return HandleGLES{HandleType::kUnknown, std::nullopt};
3747
}
3848

49+
//----------------------------------------------------------------------------
50+
/// @brief Determines if the handle is dead.
51+
///
52+
/// @return True if dead, False otherwise.
53+
///
3954
constexpr bool IsDead() const { return !name.has_value(); }
4055

56+
//----------------------------------------------------------------------------
57+
/// @brief Get the hash value of this handle. Handles can be used as map
58+
/// keys.
59+
///
4160
struct Hash {
4261
std::size_t operator()(const HandleGLES& handle) const {
4362
return fml::HashCombine(
@@ -46,6 +65,9 @@ struct HandleGLES {
4665
}
4766
};
4867

68+
//----------------------------------------------------------------------------
69+
/// @brief A comparer used to test the equality of two handles.
70+
///
4971
struct Equal {
5072
bool operator()(const HandleGLES& lhs, const HandleGLES& rhs) const {
5173
return lhs.type == rhs.type && lhs.name == rhs.name;

impeller/renderer/backend/gles/pipeline_gles.cc

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,30 +8,30 @@ namespace impeller {
88

99
PipelineGLES::PipelineGLES(ReactorGLES::Ref reactor,
1010
std::weak_ptr<PipelineLibrary> library,
11-
const PipelineDescriptor& desc)
11+
const PipelineDescriptor& desc,
12+
std::shared_ptr<UniqueHandleGLES> handle)
1213
: Pipeline(std::move(library), desc),
1314
reactor_(std::move(reactor)),
14-
handle_(reactor_ ? reactor_->CreateHandle(HandleType::kProgram)
15-
: HandleGLES::DeadHandle()),
16-
is_valid_(!handle_.IsDead()) {
15+
handle_(std::move(handle)),
16+
is_valid_(handle_->IsValid()) {
1717
if (is_valid_) {
18-
reactor_->SetDebugLabel(handle_, GetDescriptor().GetLabel());
18+
reactor_->SetDebugLabel(handle_->Get(), GetDescriptor().GetLabel());
1919
}
2020
}
2121

2222
// |Pipeline|
23-
PipelineGLES::~PipelineGLES() {
24-
if (!handle_.IsDead()) {
25-
reactor_->CollectHandle(handle_);
26-
}
27-
}
23+
PipelineGLES::~PipelineGLES() = default;
2824

2925
// |Pipeline|
3026
bool PipelineGLES::IsValid() const {
3127
return is_valid_;
3228
}
3329

3430
const HandleGLES& PipelineGLES::GetProgramHandle() const {
31+
return handle_->Get();
32+
}
33+
34+
const std::shared_ptr<UniqueHandleGLES> PipelineGLES::GetSharedHandle() const {
3535
return handle_;
3636
}
3737

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

6060
[[nodiscard]] bool PipelineGLES::BindProgram() const {
61-
if (handle_.IsDead()) {
61+
if (!handle_->IsValid()) {
6262
return false;
6363
}
64-
auto handle = reactor_->GetGLHandle(handle_);
64+
auto handle = reactor_->GetGLHandle(handle_->Get());
6565
if (!handle.has_value()) {
6666
return false;
6767
}

impeller/renderer/backend/gles/pipeline_gles.h

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77

88
#include "impeller/base/backend_cast.h"
99
#include "impeller/renderer/backend/gles/buffer_bindings_gles.h"
10-
#include "impeller/renderer/backend/gles/handle_gles.h"
1110
#include "impeller/renderer/backend/gles/reactor_gles.h"
11+
#include "impeller/renderer/backend/gles/unique_handle_gles.h"
1212
#include "impeller/renderer/pipeline.h"
1313

1414
namespace impeller {
@@ -24,6 +24,8 @@ class PipelineGLES final
2424

2525
const HandleGLES& GetProgramHandle() const;
2626

27+
const std::shared_ptr<UniqueHandleGLES> GetSharedHandle() const;
28+
2729
[[nodiscard]] bool BindProgram() const;
2830

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

3941
ReactorGLES::Ref reactor_;
40-
HandleGLES handle_;
42+
std::shared_ptr<UniqueHandleGLES> handle_;
4143
std::unique_ptr<BufferBindingsGLES> buffer_bindings_;
4244
bool is_valid_ = false;
4345

@@ -46,7 +48,8 @@ class PipelineGLES final
4648

4749
PipelineGLES(ReactorGLES::Ref reactor,
4850
std::weak_ptr<PipelineLibrary> library,
49-
const PipelineDescriptor& desc);
51+
const PipelineDescriptor& desc,
52+
std::shared_ptr<UniqueHandleGLES> handle);
5053

5154
PipelineGLES(const PipelineGLES&) = delete;
5255

impeller/renderer/backend/gles/pipeline_library_gles.cc

Lines changed: 104 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,79 @@ bool PipelineLibraryGLES::IsValid() const {
179179
return reactor_ != nullptr;
180180
}
181181

182+
std::shared_ptr<PipelineGLES> PipelineLibraryGLES::CreatePipeline(
183+
const std::weak_ptr<PipelineLibrary>& weak_library,
184+
const PipelineDescriptor& desc,
185+
const std::shared_ptr<const ShaderFunction>& vert_function,
186+
const std::shared_ptr<const ShaderFunction>& frag_function) {
187+
auto strong_library = weak_library.lock();
188+
189+
if (!strong_library) {
190+
VALIDATION_LOG << "Library was collected before a pending pipeline "
191+
"creation could finish.";
192+
return nullptr;
193+
}
194+
195+
auto& library = PipelineLibraryGLES::Cast(*strong_library);
196+
197+
const auto& reactor = library.GetReactor();
198+
199+
if (!reactor) {
200+
return nullptr;
201+
}
202+
203+
auto program_key = ProgramKey{vert_function, frag_function,
204+
desc.GetSpecializationConstants()};
205+
206+
auto cached_program = library.GetProgramForKey(program_key);
207+
208+
const auto has_cached_program = !!cached_program;
209+
210+
auto pipeline = std::shared_ptr<PipelineGLES>(new PipelineGLES(
211+
reactor, //
212+
weak_library, //
213+
desc, //
214+
has_cached_program
215+
? std::move(cached_program)
216+
: std::make_shared<UniqueHandleGLES>(reactor, HandleType::kProgram)));
217+
218+
auto program = reactor->GetGLHandle(pipeline->GetProgramHandle());
219+
220+
if (!program.has_value()) {
221+
VALIDATION_LOG << "Could not obtain program handle.";
222+
return nullptr;
223+
}
224+
225+
const auto link_result = !has_cached_program ? LinkProgram(*reactor, //
226+
pipeline, //
227+
vert_function, //
228+
frag_function //
229+
)
230+
: true;
231+
232+
if (!link_result) {
233+
VALIDATION_LOG << "Could not link pipeline program.";
234+
return nullptr;
235+
}
236+
237+
if (!pipeline->BuildVertexDescriptor(reactor->GetProcTable(),
238+
program.value())) {
239+
VALIDATION_LOG << "Could not build pipeline vertex descriptors.";
240+
return nullptr;
241+
}
242+
243+
if (!pipeline->IsValid()) {
244+
VALIDATION_LOG << "Pipeline validation checks failed.";
245+
return nullptr;
246+
}
247+
248+
if (!has_cached_program) {
249+
library.SetProgramForKey(program_key, pipeline->GetSharedHandle());
250+
}
251+
252+
return pipeline;
253+
}
254+
182255
// |PipelineLibrary|
183256
PipelineFuture<PipelineDescriptor> PipelineLibraryGLES::GetPipeline(
184257
PipelineDescriptor descriptor,
@@ -209,49 +282,16 @@ PipelineFuture<PipelineDescriptor> PipelineLibraryGLES::GetPipeline(
209282
auto pipeline_future =
210283
PipelineFuture<PipelineDescriptor>{descriptor, promise->get_future()};
211284
pipelines_[descriptor] = pipeline_future;
212-
auto weak_this = weak_from_this();
213-
214-
auto result = reactor_->AddOperation(
215-
[promise, weak_this, reactor_ptr = reactor_, descriptor, vert_function,
216-
frag_function](const ReactorGLES& reactor) {
217-
auto strong_this = weak_this.lock();
218-
if (!strong_this) {
219-
promise->set_value(nullptr);
220-
VALIDATION_LOG << "Library was collected before a pending pipeline "
221-
"creation could finish.";
222-
return;
223-
}
224-
auto pipeline = std::shared_ptr<PipelineGLES>(
225-
new PipelineGLES(reactor_ptr, strong_this, descriptor));
226-
auto program = reactor.GetGLHandle(pipeline->GetProgramHandle());
227-
if (!program.has_value()) {
228-
promise->set_value(nullptr);
229-
VALIDATION_LOG << "Could not obtain program handle.";
230-
return;
231-
}
232-
const auto link_result = LinkProgram(reactor, //
233-
pipeline, //
234-
vert_function, //
235-
frag_function //
236-
);
237-
if (!link_result) {
238-
promise->set_value(nullptr);
239-
VALIDATION_LOG << "Could not link pipeline program.";
240-
return;
241-
}
242-
if (!pipeline->BuildVertexDescriptor(reactor.GetProcTable(),
243-
program.value())) {
244-
promise->set_value(nullptr);
245-
VALIDATION_LOG << "Could not build pipeline vertex descriptors.";
246-
return;
247-
}
248-
if (!pipeline->IsValid()) {
249-
promise->set_value(nullptr);
250-
VALIDATION_LOG << "Pipeline validation checks failed.";
251-
return;
252-
}
253-
promise->set_value(std::move(pipeline));
254-
});
285+
286+
const auto result = reactor_->AddOperation([promise, //
287+
weak_this = weak_from_this(), //
288+
descriptor, //
289+
vert_function, //
290+
frag_function //
291+
](const ReactorGLES& reactor) {
292+
promise->set_value(
293+
CreatePipeline(weak_this, descriptor, vert_function, frag_function));
294+
});
255295
FML_CHECK(result);
256296

257297
return pipeline_future;
@@ -280,4 +320,25 @@ void PipelineLibraryGLES::RemovePipelinesWithEntryPoint(
280320
// |PipelineLibrary|
281321
PipelineLibraryGLES::~PipelineLibraryGLES() = default;
282322

323+
const ReactorGLES::Ref& PipelineLibraryGLES::GetReactor() const {
324+
return reactor_;
325+
}
326+
327+
std::shared_ptr<UniqueHandleGLES> PipelineLibraryGLES::GetProgramForKey(
328+
const ProgramKey& key) {
329+
Lock lock(programs_mutex_);
330+
auto found = programs_.find(key);
331+
if (found != programs_.end()) {
332+
return found->second;
333+
}
334+
return nullptr;
335+
}
336+
337+
void PipelineLibraryGLES::SetProgramForKey(
338+
const ProgramKey& key,
339+
std::shared_ptr<UniqueHandleGLES> program) {
340+
Lock lock(programs_mutex_);
341+
programs_[key] = std::move(program);
342+
}
343+
283344
} // namespace impeller

0 commit comments

Comments
 (0)