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

Commit fd437e7

Browse files
[Impeller] kick off registration and initial PSO compilation of runtime effect earlier. (#52381)
I thought about changing this API so that it blocks on compilation, but I think that isn't necesasary as the workload can happen while the UI thread is building. Plus, even blocking on shader completion does not guarantee that we won't need to compile a variant anyway. While I was at it I moved the descriptor sets computation to the runtime effect constructor. With this change the pipleine variant used during the frame is produced much faster than the initial 12ms compilation. ![image](https://github.com/flutter/engine/assets/8975114/42626676-0a71-4503-a3e9-4109c36fbbef) Fixes flutter/flutter#113719 Fixes flutter/flutter#141222
1 parent a09295f commit fd437e7

18 files changed

+208
-98
lines changed

impeller/compiler/reflector.cc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,8 @@ std::shared_ptr<RuntimeStageData::Shader> Reflector::GenerateRuntimeStageData()
348348
uniform_description.name = compiler_->get_name(var.self);
349349
uniform_description.location = compiler_->get_decoration(
350350
var.self, spv::Decoration::DecorationLocation);
351+
uniform_description.binding =
352+
compiler_->get_decoration(var.self, spv::Decoration::DecorationBinding);
351353
uniform_description.type = spir_type.basetype;
352354
uniform_description.rows = spir_type.vecsize;
353355
uniform_description.columns = spir_type.columns;
@@ -410,6 +412,7 @@ std::shared_ptr<RuntimeStageData::Shader> Reflector::GenerateRuntimeStageData()
410412
.name = ubo.name,
411413
.location = 64, // Magic constant that must match the descriptor set
412414
// location for fragment programs.
415+
.binding = 64,
413416
.type = spirv_cross::SPIRType::Struct,
414417
.struct_layout = std::move(struct_layout),
415418
.struct_float_count = float_count,

impeller/compiler/types.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ enum class SourceLanguage {
4848
struct UniformDescription {
4949
std::string name;
5050
size_t location = 0u;
51+
size_t binding = 0u;
5152
spirv_cross::SPIRType::BaseType type = spirv_cross::SPIRType::BaseType::Float;
5253
size_t rows = 0u;
5354
size_t columns = 0u;

impeller/core/runtime_types.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ struct RuntimeUniformDimensions {
3939
struct RuntimeUniformDescription {
4040
std::string name;
4141
size_t location = 0u;
42+
/// Location, but for Vulkan.
43+
size_t binding = 0u;
4244
RuntimeUniformType type = RuntimeUniformType::kFloat;
4345
RuntimeUniformDimensions dimensions = {};
4446
size_t bit_width = 0u;

impeller/entity/contents/runtime_effect_contents.cc

Lines changed: 90 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
#include "impeller/entity/contents/runtime_effect_contents.h"
66

7+
#include <algorithm>
78
#include <future>
89
#include <memory>
910

@@ -19,6 +20,7 @@
1920
#include "impeller/renderer/pipeline_library.h"
2021
#include "impeller/renderer/render_pass.h"
2122
#include "impeller/renderer/shader_function.h"
23+
#include "impeller/renderer/vertex_descriptor.h"
2224

2325
namespace impeller {
2426

@@ -65,18 +67,22 @@ static std::shared_ptr<ShaderMetadata> MakeShaderMetadata(
6567
return metadata;
6668
}
6769

68-
bool RuntimeEffectContents::Render(const ContentContext& renderer,
69-
const Entity& entity,
70-
RenderPass& pass) const {
70+
bool RuntimeEffectContents::BootstrapShader(
71+
const ContentContext& renderer) const {
72+
if (!RegisterShader(renderer)) {
73+
return false;
74+
}
75+
ContentContextOptions options;
76+
options.color_attachment_pixel_format =
77+
renderer.GetContext()->GetCapabilities()->GetDefaultColorFormat();
78+
return !!CreatePipeline(renderer, options);
79+
}
80+
81+
bool RuntimeEffectContents::RegisterShader(
82+
const ContentContext& renderer) const {
7183
const std::shared_ptr<Context>& context = renderer.GetContext();
7284
const std::shared_ptr<ShaderLibrary>& library = context->GetShaderLibrary();
7385

74-
//--------------------------------------------------------------------------
75-
/// Get or register shader.
76-
///
77-
78-
// TODO(113719): Register the shader function earlier.
79-
8086
std::shared_ptr<const ShaderFunction> function = library->GetFunction(
8187
runtime_stage_->GetEntrypoint(), ShaderStage::kFragment);
8288

@@ -123,29 +129,75 @@ bool RuntimeEffectContents::Render(const ContentContext& renderer,
123129

124130
runtime_stage_->SetClean();
125131
}
132+
return true;
133+
}
126134

127-
//--------------------------------------------------------------------------
128-
/// Set up the command. Defer setting up the pipeline until the descriptor set
129-
/// layouts are known from the uniforms.
130-
///
131-
135+
std::shared_ptr<Pipeline<PipelineDescriptor>>
136+
RuntimeEffectContents::CreatePipeline(const ContentContext& renderer,
137+
ContentContextOptions options) const {
138+
const std::shared_ptr<Context>& context = renderer.GetContext();
139+
const std::shared_ptr<ShaderLibrary>& library = context->GetShaderLibrary();
132140
const std::shared_ptr<const Capabilities>& caps = context->GetCapabilities();
133141
const auto color_attachment_format = caps->GetDefaultColorFormat();
134142
const auto stencil_attachment_format = caps->GetDefaultDepthStencilFormat();
135143

136144
using VS = RuntimeEffectVertexShader;
137145

138-
//--------------------------------------------------------------------------
139-
/// Fragment stage uniforms.
140-
///
146+
PipelineDescriptor desc;
147+
desc.SetLabel("Runtime Stage");
148+
desc.AddStageEntrypoint(
149+
library->GetFunction(VS::kEntrypointName, ShaderStage::kVertex));
150+
desc.AddStageEntrypoint(library->GetFunction(runtime_stage_->GetEntrypoint(),
151+
ShaderStage::kFragment));
152+
153+
std::shared_ptr<VertexDescriptor> vertex_descriptor =
154+
std::make_shared<VertexDescriptor>();
155+
vertex_descriptor->SetStageInputs(VS::kAllShaderStageInputs,
156+
VS::kInterleavedBufferLayout);
157+
vertex_descriptor->RegisterDescriptorSetLayouts(VS::kDescriptorSetLayouts);
158+
vertex_descriptor->RegisterDescriptorSetLayouts(
159+
runtime_stage_->GetDescriptorSetLayouts().data(),
160+
runtime_stage_->GetDescriptorSetLayouts().size());
161+
desc.SetVertexDescriptor(std::move(vertex_descriptor));
162+
desc.SetColorAttachmentDescriptor(
163+
0u, {.format = color_attachment_format, .blending_enabled = true});
164+
165+
desc.SetStencilAttachmentDescriptors(StencilAttachmentDescriptor{});
166+
desc.SetStencilPixelFormat(stencil_attachment_format);
167+
168+
desc.SetDepthStencilAttachmentDescriptor(DepthAttachmentDescriptor{});
169+
desc.SetDepthPixelFormat(stencil_attachment_format);
170+
171+
options.ApplyToPipelineDescriptor(desc);
172+
auto pipeline = context->GetPipelineLibrary()->GetPipeline(desc).Get();
173+
if (!pipeline) {
174+
VALIDATION_LOG << "Failed to get or create runtime effect pipeline.";
175+
return nullptr;
176+
}
141177

142-
std::vector<DescriptorSetLayout> descriptor_set_layouts;
178+
return pipeline;
179+
}
143180

144-
BindFragmentCallback bind_callback = [this, &renderer, &context,
145-
&descriptor_set_layouts](
146-
RenderPass& pass) {
147-
descriptor_set_layouts.clear();
181+
bool RuntimeEffectContents::Render(const ContentContext& renderer,
182+
const Entity& entity,
183+
RenderPass& pass) const {
184+
const std::shared_ptr<Context>& context = renderer.GetContext();
185+
const std::shared_ptr<ShaderLibrary>& library = context->GetShaderLibrary();
148186

187+
//--------------------------------------------------------------------------
188+
/// Get or register shader. Flutter will do this when the runtime effect
189+
/// is first loaded, but this check is added to supporting testing of the
190+
/// Aiks API and non-flutter usage of Impeller.
191+
///
192+
if (!RegisterShader(renderer)) {
193+
return false;
194+
}
195+
196+
//--------------------------------------------------------------------------
197+
/// Fragment stage uniforms.
198+
///
199+
BindFragmentCallback bind_callback = [this, &renderer,
200+
&context](RenderPass& pass) {
149201
size_t minimum_sampler_index = 100000000;
150202
size_t buffer_index = 0;
151203
size_t buffer_offset = 0;
@@ -173,9 +225,10 @@ bool RuntimeEffectContents::Render(const ContentContext& renderer,
173225
Context::BackendType::kVulkan)
174226
<< "Uniform " << uniform.name
175227
<< " had unexpected type kFloat for Vulkan backend.";
228+
176229
size_t alignment =
177230
std::max(uniform.bit_width / 8, DefaultUniformAlignment());
178-
auto buffer_view = renderer.GetTransientsBuffer().Emplace(
231+
BufferView buffer_view = renderer.GetTransientsBuffer().Emplace(
179232
uniform_data_->data() + buffer_offset, uniform.GetSize(),
180233
alignment);
181234

@@ -184,23 +237,20 @@ bool RuntimeEffectContents::Render(const ContentContext& renderer,
184237
uniform_slot.ext_res_0 = uniform.location;
185238
pass.BindResource(ShaderStage::kFragment,
186239
DescriptorType::kUniformBuffer, uniform_slot,
187-
metadata, buffer_view);
240+
metadata, std::move(buffer_view));
188241
buffer_index++;
189242
buffer_offset += uniform.GetSize();
190243
break;
191244
}
192245
case kStruct: {
193246
FML_DCHECK(renderer.GetContext()->GetBackendType() ==
194247
Context::BackendType::kVulkan);
195-
descriptor_set_layouts.emplace_back(DescriptorSetLayout{
196-
static_cast<uint32_t>(uniform.location),
197-
DescriptorType::kUniformBuffer,
198-
ShaderStage::kFragment,
199-
});
200248
ShaderUniformSlot uniform_slot;
201249
uniform_slot.name = uniform.name.c_str();
202250
uniform_slot.binding = uniform.location;
203251

252+
// TODO(jonahwilliams): rewrite this to emplace directly into
253+
// HostBuffer.
204254
std::vector<float> uniform_buffer;
205255
uniform_buffer.reserve(uniform.struct_layout.size());
206256
size_t uniform_byte_index = 0u;
@@ -214,16 +264,15 @@ bool RuntimeEffectContents::Render(const ContentContext& renderer,
214264
FML_UNREACHABLE();
215265
}
216266
}
217-
218267
size_t alignment = std::max(sizeof(float) * uniform_buffer.size(),
219268
DefaultUniformAlignment());
220269

221-
auto buffer_view = renderer.GetTransientsBuffer().Emplace(
270+
BufferView buffer_view = renderer.GetTransientsBuffer().Emplace(
222271
reinterpret_cast<const void*>(uniform_buffer.data()),
223272
sizeof(float) * uniform_buffer.size(), alignment);
224273
pass.BindResource(ShaderStage::kFragment,
225274
DescriptorType::kUniformBuffer, uniform_slot,
226-
ShaderMetadata{}, buffer_view);
275+
ShaderMetadata{}, std::move(buffer_view));
227276
}
228277
}
229278
}
@@ -243,20 +292,7 @@ bool RuntimeEffectContents::Render(const ContentContext& renderer,
243292

244293
SampledImageSlot image_slot;
245294
image_slot.name = uniform.name.c_str();
246-
247-
uint32_t sampler_binding_location = 0u;
248-
if (!descriptor_set_layouts.empty()) {
249-
sampler_binding_location =
250-
descriptor_set_layouts.back().binding + 1;
251-
}
252-
253-
descriptor_set_layouts.emplace_back(DescriptorSetLayout{
254-
sampler_binding_location,
255-
DescriptorType::kSampledImage,
256-
ShaderStage::kFragment,
257-
});
258-
259-
image_slot.binding = sampler_binding_location;
295+
image_slot.binding = uniform.binding;
260296
image_slot.texture_index = uniform.location - minimum_sampler_index;
261297
pass.BindResource(ShaderStage::kFragment,
262298
DescriptorType::kSampledImage, image_slot,
@@ -273,47 +309,15 @@ bool RuntimeEffectContents::Render(const ContentContext& renderer,
273309
};
274310

275311
/// Now that the descriptor set layouts are known, get the pipeline.
312+
using VS = RuntimeEffectVertexShader;
276313

277-
PipelineBuilderCallback pipeline_callback = [&](ContentContextOptions
278-
options) {
279-
// Pipeline creation callback for the cache handler to call.
280-
auto create_callback =
281-
[&]() -> std::shared_ptr<Pipeline<PipelineDescriptor>> {
282-
PipelineDescriptor desc;
283-
desc.SetLabel("Runtime Stage");
284-
desc.AddStageEntrypoint(
285-
library->GetFunction(VS::kEntrypointName, ShaderStage::kVertex));
286-
desc.AddStageEntrypoint(library->GetFunction(
287-
runtime_stage_->GetEntrypoint(), ShaderStage::kFragment));
288-
auto vertex_descriptor = std::make_shared<VertexDescriptor>();
289-
vertex_descriptor->SetStageInputs(VS::kAllShaderStageInputs,
290-
VS::kInterleavedBufferLayout);
291-
vertex_descriptor->RegisterDescriptorSetLayouts(
292-
VS::kDescriptorSetLayouts);
293-
vertex_descriptor->RegisterDescriptorSetLayouts(
294-
descriptor_set_layouts.data(), descriptor_set_layouts.size());
295-
desc.SetVertexDescriptor(std::move(vertex_descriptor));
296-
desc.SetColorAttachmentDescriptor(
297-
0u, {.format = color_attachment_format, .blending_enabled = true});
298-
299-
desc.SetStencilAttachmentDescriptors(StencilAttachmentDescriptor{});
300-
desc.SetStencilPixelFormat(stencil_attachment_format);
301-
302-
desc.SetDepthStencilAttachmentDescriptor(DepthAttachmentDescriptor{});
303-
desc.SetDepthPixelFormat(stencil_attachment_format);
304-
305-
options.ApplyToPipelineDescriptor(desc);
306-
auto pipeline = context->GetPipelineLibrary()->GetPipeline(desc).Get();
307-
if (!pipeline) {
308-
VALIDATION_LOG << "Failed to get or create runtime effect pipeline.";
309-
return nullptr;
310-
}
311-
312-
return pipeline;
313-
};
314-
return renderer.GetCachedRuntimeEffectPipeline(
315-
runtime_stage_->GetEntrypoint(), options, create_callback);
316-
};
314+
PipelineBuilderCallback pipeline_callback =
315+
[&](ContentContextOptions options) {
316+
// Pipeline creation callback for the cache handler to call.
317+
return renderer.GetCachedRuntimeEffectPipeline(
318+
runtime_stage_->GetEntrypoint(), options,
319+
[&]() { return CreatePipeline(renderer, options); });
320+
};
317321

318322
return ColorSourceContents::DrawGeometry<VS>(renderer, entity, pass,
319323
pipeline_callback,

impeller/entity/contents/runtime_effect_contents.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,16 @@ class RuntimeEffectContents final : public ColorSourceContents {
3535
const Entity& entity,
3636
RenderPass& pass) const override;
3737

38+
/// Load the runtime effect and ensure a default PSO is initialized.
39+
bool BootstrapShader(const ContentContext& renderer) const;
40+
3841
private:
42+
bool RegisterShader(const ContentContext& renderer) const;
43+
44+
std::shared_ptr<Pipeline<PipelineDescriptor>> CreatePipeline(
45+
const ContentContext& renderer,
46+
ContentContextOptions options) const;
47+
3948
std::shared_ptr<RuntimeStage> runtime_stage_;
4049
std::shared_ptr<std::vector<uint8_t>> uniform_data_;
4150
std::vector<TextureInput> texture_inputs_;

impeller/entity/entity_unittests.cc

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2242,6 +2242,20 @@ TEST_P(EntityTest, RuntimeEffectCanSuccessfullyRender) {
22422242
.has_value());
22432243
}
22442244

2245+
TEST_P(EntityTest, RuntimeEffectCanPrecache) {
2246+
auto runtime_stages =
2247+
OpenAssetAsRuntimeStage("runtime_stage_example.frag.iplr");
2248+
auto runtime_stage =
2249+
runtime_stages[PlaygroundBackendToRuntimeStageBackend(GetBackend())];
2250+
ASSERT_TRUE(runtime_stage);
2251+
ASSERT_TRUE(runtime_stage->IsDirty());
2252+
2253+
auto contents = std::make_shared<RuntimeEffectContents>();
2254+
contents->SetRuntimeStage(runtime_stage);
2255+
2256+
EXPECT_TRUE(contents->BootstrapShader(*GetContentContext()));
2257+
}
2258+
22452259
TEST_P(EntityTest, RuntimeEffectSetsRightSizeWhenUniformIsStruct) {
22462260
if (GetBackend() != PlaygroundBackend::kVulkan) {
22472261
GTEST_SKIP() << "Test only applies to Vulkan";

0 commit comments

Comments
 (0)