[Impeller] add additional setup method that caches more pipelines, warms internal shader code (#50521)
Part of https://github.com/flutter/flutter/issues/138236
Fixes a number of issues related to startup performance by adding an "InitializeCommonlyUsedShadersIfNeeded" method to content context.
On the first frame of a flutter application renders, the backend will populate the glyph atlas for the first time. On the Vulkan backend, this executes vkCmdCopyBufferToImage. The first time this runs, Arm Mali drivers will generate an additional shader program. Creates a 1x1 texture and sets the contents to force this code to compile eagerly.

The first time a render pass is constructed, a shader is complied. This does not seem to depend on the properties of the render pass, so we just create a trivial one and submit it.

Finally there are a few missing shader variants. Lets just go ahead a populate that cache a bit more
diff --git a/impeller/aiks/testing/context_spy.cc b/impeller/aiks/testing/context_spy.cc
index 325dc33..bb3d528 100644
--- a/impeller/aiks/testing/context_spy.cc
+++ b/impeller/aiks/testing/context_spy.cc
@@ -34,6 +34,11 @@
return real_context->IsValid();
});
+ ON_CALL(*mock_context, GetBackendType)
+ .WillByDefault([real_context]() -> Context::BackendType {
+ return real_context->GetBackendType();
+ });
+
ON_CALL(*mock_context, GetCapabilities)
.WillByDefault(
[real_context]() -> const std::shared_ptr<const Capabilities>& {
diff --git a/impeller/entity/contents/content_context.cc b/impeller/entity/contents/content_context.cc
index 1d73fcf..725dc5b 100644
--- a/impeller/entity/contents/content_context.cc
+++ b/impeller/entity/contents/content_context.cc
@@ -6,7 +6,9 @@
#include <memory>
+#include "fml/trace_event.h"
#include "impeller/base/strings.h"
+#include "impeller/base/validation.h"
#include "impeller/core/formats.h"
#include "impeller/entity/contents/framebuffer_blend_contents.h"
#include "impeller/entity/entity.h"
@@ -460,6 +462,7 @@
*context_, clip_pipeline_descriptor));
is_valid_ = true;
+ InitializeCommonlyUsedShadersIfNeeded();
}
ContentContext::~ContentContext() = default;
@@ -608,4 +611,64 @@
GetContext()->GetCommandQueue()->Submit(buffers);
}
+void ContentContext::InitializeCommonlyUsedShadersIfNeeded() const {
+ if (GetContext()->GetBackendType() == Context::BackendType::kOpenGLES) {
+ // TODO(jonahwilliams): The OpenGL Embedder Unittests hang if this code
+ // runs.
+ return;
+ }
+ TRACE_EVENT0("flutter", "InitializeCommonlyUsedShadersIfNeeded");
+
+ // Initialize commonly used shaders that aren't defaults. These settings were
+ // chosen based on the knowledge that we mix and match triangle and
+ // triangle-strip geometry, and also have fairly agressive srcOver to src
+ // blend mode conversions.
+ auto options = ContentContextOptions{
+ .sample_count = SampleCount::kCount4,
+ .color_attachment_pixel_format =
+ context_->GetCapabilities()->GetDefaultColorFormat()};
+
+ for (const auto mode : {BlendMode::kSource, BlendMode::kSourceOver}) {
+ for (const auto geometry :
+ {PrimitiveType::kTriangle, PrimitiveType::kTriangleStrip}) {
+ options.blend_mode = mode;
+ options.primitive_type = geometry;
+ CreateIfNeeded(solid_fill_pipelines_, options);
+ CreateIfNeeded(texture_pipelines_, options);
+ if (GetContext()->GetCapabilities()->SupportsSSBO()) {
+ CreateIfNeeded(linear_gradient_ssbo_fill_pipelines_, options);
+ CreateIfNeeded(radial_gradient_ssbo_fill_pipelines_, options);
+ CreateIfNeeded(sweep_gradient_ssbo_fill_pipelines_, options);
+ CreateIfNeeded(conical_gradient_ssbo_fill_pipelines_, options);
+ }
+ }
+ }
+
+ options.blend_mode = BlendMode::kDestination;
+ options.primitive_type = PrimitiveType::kTriangleStrip;
+ for (const auto stencil_mode :
+ {ContentContextOptions::StencilMode::kLegacyClipIncrement,
+ ContentContextOptions::StencilMode::kLegacyClipDecrement,
+ ContentContextOptions::StencilMode::kLegacyClipRestore}) {
+ options.stencil_mode = stencil_mode;
+ CreateIfNeeded(clip_pipelines_, options);
+ }
+
+ // On ARM devices, the initial usage of vkCmdCopyBufferToImage has been
+ // observed to take 10s of ms as an internal shader is compiled to perform
+ // the operation. Similarly, the initial render pass can also take 10s of ms
+ // for a similar reason. Because the context object is initialized far
+ // before the first frame, create a trivial texture and render pass to force
+ // the driver to compiler these shaders before the frame begins.
+ TextureDescriptor desc;
+ desc.size = {1, 1};
+ desc.storage_mode = StorageMode::kHostVisible;
+ desc.format = context_->GetCapabilities()->GetDefaultColorFormat();
+ auto texture = GetContext()->GetResourceAllocator()->CreateTexture(desc);
+ uint32_t color = 0;
+ if (!texture->SetContents(reinterpret_cast<uint8_t*>(&color), 4u)) {
+ VALIDATION_LOG << "Failed to set bootstrap texture.";
+ }
+}
+
} // namespace impeller
diff --git a/impeller/entity/contents/content_context.h b/impeller/entity/contents/content_context.h
index 64ad969..4b813e9 100644
--- a/impeller/entity/contents/content_context.h
+++ b/impeller/entity/contents/content_context.h
@@ -816,6 +816,14 @@
std::shared_ptr<Context> context_;
std::shared_ptr<LazyGlyphAtlas> lazy_glyph_atlas_;
+ /// Run backend specific additional setup and create common shader variants.
+ ///
+ /// This bootstrap is intended to improve the performance of several
+ /// first frame benchmarks that are tracked in the flutter device lab.
+ /// The workload includes initializing commonly used but not default
+ /// shader variants, as well as forcing driver initialization.
+ void InitializeCommonlyUsedShadersIfNeeded() const;
+
struct RuntimeEffectPipelineKey {
std::string unique_entrypoint_name;
ContentContextOptions options;
@@ -1007,6 +1015,16 @@
std::shared_ptr<Pipeline<PipelineDescriptor>> GetPipeline(
Variants<TypedPipeline>& container,
ContentContextOptions opts) const {
+ TypedPipeline* pipeline = CreateIfNeeded(container, opts);
+ if (!pipeline) {
+ return nullptr;
+ }
+ return pipeline->WaitAndGet();
+ }
+
+ template <class TypedPipeline>
+ TypedPipeline* CreateIfNeeded(Variants<TypedPipeline>& container,
+ ContentContextOptions opts) const {
if (!IsValid()) {
return nullptr;
}
@@ -1015,16 +1033,17 @@
opts.wireframe = true;
}
- if (auto found = container.Get(opts)) {
- return found->WaitAndGet();
+ if (TypedPipeline* found = container.Get(opts)) {
+ return found;
}
- auto prototype = container.GetDefault();
+ TypedPipeline* prototype = container.GetDefault();
// The prototype must always be initialized in the constructor.
FML_CHECK(prototype != nullptr);
- auto pipeline = prototype->WaitAndGet();
+ std::shared_ptr<Pipeline<PipelineDescriptor>> pipeline =
+ prototype->WaitAndGet();
if (!pipeline) {
return nullptr;
}
@@ -1036,10 +1055,10 @@
desc.SetLabel(
SPrintF("%s V#%zu", desc.GetLabel().c_str(), variants_count));
});
- auto variant = std::make_unique<TypedPipeline>(std::move(variant_future));
- auto variant_pipeline = variant->WaitAndGet();
+ std::unique_ptr<TypedPipeline> variant =
+ std::make_unique<TypedPipeline>(std::move(variant_future));
container.Set(opts, std::move(variant));
- return variant_pipeline;
+ return container.Get(opts);
}
bool is_valid_ = false;
diff --git a/impeller/entity/contents/content_context_unittests.cc b/impeller/entity/contents/content_context_unittests.cc
index ca56680..aaa71db 100644
--- a/impeller/entity/contents/content_context_unittests.cc
+++ b/impeller/entity/contents/content_context_unittests.cc
@@ -3,28 +3,73 @@
// found in the LICENSE file.
#include <cstdint>
+#include <future>
+#include <memory>
+#include <utility>
+#include <vector>
#include "fml/logging.h"
#include "gtest/gtest.h"
+#include "impeller/base/backend_cast.h"
+#include "impeller/base/comparable.h"
#include "impeller/core/allocator.h"
#include "impeller/core/device_buffer_descriptor.h"
+#include "impeller/core/texture_descriptor.h"
#include "impeller/entity/contents/content_context.h"
+#include "impeller/entity/contents/test/recording_render_pass.h"
#include "impeller/geometry/color.h"
#include "impeller/renderer/capabilities.h"
+#include "impeller/renderer/command_buffer.h"
#include "impeller/renderer/command_queue.h"
#include "impeller/renderer/pipeline.h"
#include "impeller/renderer/pipeline_descriptor.h"
+#include "impeller/renderer/pipeline_library.h"
+#include "impeller/renderer/render_pass.h"
+#include "impeller/renderer/shader_function.h"
+#include "impeller/renderer/shader_library.h"
namespace impeller {
namespace testing {
-class FakeAllocator : public Allocator {
+namespace {
+class FakeTexture : public Texture {
+ public:
+ explicit FakeTexture(const TextureDescriptor& desc) : Texture(desc) {}
+
+ ~FakeTexture() override {}
+
+ void SetLabel(std::string_view label) {}
+
+ bool IsValid() const override { return true; }
+
+ ISize GetSize() const override { return {1, 1}; }
+
+ Scalar GetYCoordScale() const override { return 1.0; }
+
+ bool OnSetContents(const uint8_t* contents,
+ size_t length,
+ size_t slice) override {
+ did_set_contents = true;
+ return true;
+ }
+
+ bool OnSetContents(std::shared_ptr<const fml::Mapping> mapping,
+ size_t slice) override {
+ did_set_contents = true;
+ return true;
+ }
+
+ bool did_set_contents = false;
+};
+
+class FakeAllocator : public Allocator,
+ public BackendCast<FakeAllocator, Allocator> {
public:
FakeAllocator() : Allocator() {}
uint16_t MinimumBytesPerRow(PixelFormat format) const override { return 0; }
- ISize GetMaxTextureSizeSupported() const override { return ISize(); }
+ ISize GetMaxTextureSizeSupported() const override { return ISize(1, 1); }
std::shared_ptr<DeviceBuffer> OnCreateBuffer(
const DeviceBufferDescriptor& desc) override {
@@ -32,65 +77,203 @@
}
std::shared_ptr<Texture> OnCreateTexture(
const TextureDescriptor& desc) override {
+ if (desc.size == ISize{1, 1}) {
+ auto result = std::make_shared<FakeTexture>(desc);
+ textures.push_back(result);
+ return result;
+ }
return nullptr;
}
+
+ std::vector<std::shared_ptr<FakeTexture>> textures = {};
+};
+
+class FakePipeline : public Pipeline<PipelineDescriptor> {
+ public:
+ FakePipeline(std::weak_ptr<PipelineLibrary> library,
+ const PipelineDescriptor& desc)
+ : Pipeline(std::move(library), desc) {}
+
+ ~FakePipeline() override {}
+
+ bool IsValid() const override { return true; }
+};
+
+class FakeComputePipeline : public Pipeline<ComputePipelineDescriptor> {
+ public:
+ FakeComputePipeline(std::weak_ptr<PipelineLibrary> library,
+ const ComputePipelineDescriptor& desc)
+ : Pipeline(std::move(library), desc) {}
+
+ ~FakeComputePipeline() override {}
+
+ bool IsValid() const override { return true; }
+};
+
+class FakePipelineLibrary : public PipelineLibrary {
+ public:
+ FakePipelineLibrary() {}
+
+ ~FakePipelineLibrary() override {}
+
+ bool IsValid() const override { return true; }
+
+ PipelineFuture<PipelineDescriptor> GetPipeline(
+ PipelineDescriptor descriptor) override {
+ auto pipeline =
+ std::make_shared<FakePipeline>(weak_from_this(), descriptor);
+ std::promise<std::shared_ptr<Pipeline<PipelineDescriptor>>> promise;
+ promise.set_value(std::move(pipeline));
+ return PipelineFuture<PipelineDescriptor>{
+ .descriptor = descriptor,
+ .future =
+ std::shared_future<std::shared_ptr<Pipeline<PipelineDescriptor>>>(
+ promise.get_future())};
+ }
+
+ PipelineFuture<ComputePipelineDescriptor> GetPipeline(
+ ComputePipelineDescriptor descriptor) override {
+ auto pipeline =
+ std::make_shared<FakeComputePipeline>(weak_from_this(), descriptor);
+ std::promise<std::shared_ptr<Pipeline<ComputePipelineDescriptor>>> promise;
+ promise.set_value(std::move(pipeline));
+ return PipelineFuture<ComputePipelineDescriptor>{
+ .descriptor = descriptor,
+ .future = std::shared_future<
+ std::shared_ptr<Pipeline<ComputePipelineDescriptor>>>(
+ promise.get_future())};
+ }
+
+ void RemovePipelinesWithEntryPoint(
+ std::shared_ptr<const ShaderFunction> function) {}
+};
+
+class FakeShaderFunction : public ShaderFunction {
+ public:
+ FakeShaderFunction(UniqueID parent_library_id,
+ std::string name,
+ ShaderStage stage)
+ : ShaderFunction(parent_library_id, std::move(name), stage){};
+
+ ~FakeShaderFunction() override {}
+};
+
+class FakeShaderLibrary : public ShaderLibrary {
+ public:
+ ~FakeShaderLibrary() override {}
+
+ bool IsValid() const override { return true; }
+
+ std::shared_ptr<const ShaderFunction> GetFunction(std::string_view name,
+ ShaderStage stage) {
+ return std::make_shared<FakeShaderFunction>(UniqueID{}, std::string(name),
+ stage);
+ }
+
+ void RegisterFunction(std::string name,
+ ShaderStage stage,
+ std::shared_ptr<fml::Mapping> code,
+ RegistrationCallback callback) override {}
+
+ void UnregisterFunction(std::string name, ShaderStage stage) override {}
+};
+
+class FakeCommandBuffer : public CommandBuffer {
+ public:
+ explicit FakeCommandBuffer(std::weak_ptr<const Context> context)
+ : CommandBuffer(std::move(context)) {}
+
+ ~FakeCommandBuffer() {}
+
+ bool IsValid() const override { return true; }
+
+ void SetLabel(const std::string& label) const override {}
+
+ std::shared_ptr<RenderPass> OnCreateRenderPass(
+ RenderTarget render_target) override {
+ return std::make_shared<RecordingRenderPass>(nullptr, context_.lock(),
+ render_target);
+ }
+
+ std::shared_ptr<BlitPass> OnCreateBlitPass() override { FML_UNREACHABLE() }
+
+ virtual bool OnSubmitCommands(CompletionCallback callback) { return true; }
+
+ void OnWaitUntilScheduled() {}
+
+ std::shared_ptr<ComputePass> OnCreateComputePass() override {
+ FML_UNREACHABLE();
+ }
};
-class FakeContext : public Context {
+class FakeContext : public Context,
+ public std::enable_shared_from_this<FakeContext> {
public:
- FakeContext() : Context(), allocator_(std::make_shared<FakeAllocator>()) {}
+ explicit FakeContext(const std::string& gpu_model = "")
+ : Context(),
+ allocator_(std::make_shared<FakeAllocator>()),
+ capabilities_(
+ std::shared_ptr<Capabilities>(CapabilitiesBuilder().Build())),
+ pipelines_(std::make_shared<FakePipelineLibrary>()),
+ queue_(std::make_shared<CommandQueue>()),
+ shader_library_(std::make_shared<FakeShaderLibrary>()),
+ gpu_model_(gpu_model) {}
BackendType GetBackendType() const override { return BackendType::kVulkan; }
- std::string DescribeGpuModel() const override { return ""; }
- bool IsValid() const override { return false; }
+ std::string DescribeGpuModel() const override { return gpu_model_; }
+ bool IsValid() const override { return true; }
const std::shared_ptr<const Capabilities>& GetCapabilities() const override {
return capabilities_;
}
std::shared_ptr<Allocator> GetResourceAllocator() const override {
return allocator_;
}
- std::shared_ptr<ShaderLibrary> GetShaderLibrary() const { return nullptr; }
+ std::shared_ptr<ShaderLibrary> GetShaderLibrary() const {
+ return shader_library_;
+ }
std::shared_ptr<SamplerLibrary> GetSamplerLibrary() const { return nullptr; }
std::shared_ptr<PipelineLibrary> GetPipelineLibrary() const {
- return nullptr;
+ return pipelines_;
}
- std::shared_ptr<CommandQueue> GetCommandQueue() const { FML_UNREACHABLE(); }
- std::shared_ptr<CommandBuffer> CreateCommandBuffer() const { return nullptr; }
+ std::shared_ptr<CommandQueue> GetCommandQueue() const { return queue_; }
+ std::shared_ptr<CommandBuffer> CreateCommandBuffer() const {
+ return std::make_shared<FakeCommandBuffer>(shared_from_this());
+ }
void Shutdown() {}
private:
std::shared_ptr<Allocator> allocator_;
std::shared_ptr<const Capabilities> capabilities_;
+ std::shared_ptr<FakePipelineLibrary> pipelines_;
+ std::shared_ptr<CommandQueue> queue_;
+ std::shared_ptr<ShaderLibrary> shader_library_;
+ std::string gpu_model_;
};
-
-class FakePipeline : public Pipeline<PipelineDescriptor> {
- public:
- FakePipeline() : Pipeline({}, PipelineDescriptor{}) {}
-
- bool IsValid() const override { return false; }
-};
-
-static std::shared_ptr<FakePipeline> CreateFakePipelineCallback() {
- return std::make_shared<FakePipeline>();
-}
+} // namespace
TEST(ContentContext, CachesPipelines) {
auto context = std::make_shared<FakeContext>();
+
+ auto create_callback = [&]() {
+ return std::make_shared<FakePipeline>(context->GetPipelineLibrary(),
+ PipelineDescriptor{});
+ };
+
ContentContext content_context(context, nullptr);
ContentContextOptions optionsA{.blend_mode = BlendMode::kSourceOver};
ContentContextOptions optionsB{.blend_mode = BlendMode::kSource};
auto pipelineA = content_context.GetCachedRuntimeEffectPipeline(
- "A", optionsA, CreateFakePipelineCallback);
+ "A", optionsA, create_callback);
auto pipelineA2 = content_context.GetCachedRuntimeEffectPipeline(
- "A", optionsA, CreateFakePipelineCallback);
+ "A", optionsA, create_callback);
auto pipelineA3 = content_context.GetCachedRuntimeEffectPipeline(
- "A", optionsB, CreateFakePipelineCallback);
+ "A", optionsB, create_callback);
auto pipelineB = content_context.GetCachedRuntimeEffectPipeline(
- "B", optionsB, CreateFakePipelineCallback);
+ "B", optionsB, create_callback);
ASSERT_EQ(pipelineA.get(), pipelineA2.get());
ASSERT_NE(pipelineA.get(), pipelineA3.get());
@@ -103,39 +286,58 @@
ContentContextOptions optionsA{.blend_mode = BlendMode::kSourceOver};
ContentContextOptions optionsB{.blend_mode = BlendMode::kSource};
+ auto create_callback = [&]() {
+ return std::make_shared<FakePipeline>(context->GetPipelineLibrary(),
+ PipelineDescriptor{});
+ };
+
auto pipelineA = content_context.GetCachedRuntimeEffectPipeline(
- "A", optionsA, CreateFakePipelineCallback);
+ "A", optionsA, create_callback);
auto pipelineA2 = content_context.GetCachedRuntimeEffectPipeline(
- "A", optionsB, CreateFakePipelineCallback);
+ "A", optionsB, create_callback);
auto pipelineB = content_context.GetCachedRuntimeEffectPipeline(
- "B", optionsB, CreateFakePipelineCallback);
+ "B", optionsB, create_callback);
ASSERT_TRUE(pipelineA);
ASSERT_TRUE(pipelineA2);
ASSERT_TRUE(pipelineB);
ASSERT_EQ(pipelineA, content_context.GetCachedRuntimeEffectPipeline(
- "A", optionsA, CreateFakePipelineCallback));
+ "A", optionsA, create_callback));
ASSERT_EQ(pipelineA2, content_context.GetCachedRuntimeEffectPipeline(
- "A", optionsB, CreateFakePipelineCallback));
+ "A", optionsB, create_callback));
ASSERT_EQ(pipelineB, content_context.GetCachedRuntimeEffectPipeline(
- "B", optionsB, CreateFakePipelineCallback));
+ "B", optionsB, create_callback));
content_context.ClearCachedRuntimeEffectPipeline("A");
ASSERT_NE(pipelineA, content_context.GetCachedRuntimeEffectPipeline(
- "A", optionsA, CreateFakePipelineCallback));
+ "A", optionsA, create_callback));
ASSERT_NE(pipelineA2, content_context.GetCachedRuntimeEffectPipeline(
- "A", optionsB, CreateFakePipelineCallback));
+ "A", optionsB, create_callback));
ASSERT_EQ(pipelineB, content_context.GetCachedRuntimeEffectPipeline(
- "B", optionsB, CreateFakePipelineCallback));
+ "B", optionsB, create_callback));
content_context.ClearCachedRuntimeEffectPipeline("B");
ASSERT_NE(pipelineB, content_context.GetCachedRuntimeEffectPipeline(
- "B", optionsB, CreateFakePipelineCallback));
+ "B", optionsB, create_callback));
+}
+
+TEST(ContentContext, InitializeCommonlyUsedShadersIfNeeded) {
+ auto context = std::make_shared<FakeContext>("Mali G70");
+ ContentContext content_context(context, nullptr);
+
+ FakeAllocator& fake_allocator =
+ FakeAllocator::Cast(*context->GetResourceAllocator());
+
+#if IMPELLER_ENABLE_3D
+ EXPECT_EQ(fake_allocator.textures.size(), 2u);
+#else
+ EXPECT_EQ(fake_allocator.textures.size(), 1u);
+#endif // IMPELLER_ENABLE_3D
}
} // namespace testing
diff --git a/impeller/entity/contents/test/recording_render_pass.cc b/impeller/entity/contents/test/recording_render_pass.cc
index d7708ed..1891381 100644
--- a/impeller/entity/contents/test/recording_render_pass.cc
+++ b/impeller/entity/contents/test/recording_render_pass.cc
@@ -10,7 +10,7 @@
RecordingRenderPass::RecordingRenderPass(
std::shared_ptr<RenderPass> delegate,
- const std::shared_ptr<Context>& context,
+ const std::shared_ptr<const Context>& context,
const RenderTarget& render_target)
: RenderPass(context, render_target), delegate_(std::move(delegate)) {}
@@ -18,57 +18,77 @@
void RecordingRenderPass::SetPipeline(
const std::shared_ptr<Pipeline<PipelineDescriptor>>& pipeline) {
pending_.pipeline = pipeline;
- delegate_->SetPipeline(pipeline);
+ if (delegate_) {
+ delegate_->SetPipeline(pipeline);
+ }
}
void RecordingRenderPass::SetCommandLabel(std::string_view label) {
#ifdef IMPELLER_DEBUG
pending_.label = std::string(label);
#endif // IMPELLER_DEBUG
- delegate_->SetCommandLabel(label);
+ if (delegate_) {
+ delegate_->SetCommandLabel(label);
+ }
}
// |RenderPass|
void RecordingRenderPass::SetStencilReference(uint32_t value) {
pending_.stencil_reference = value;
- delegate_->SetStencilReference(value);
+ if (delegate_) {
+ delegate_->SetStencilReference(value);
+ }
}
// |RenderPass|
void RecordingRenderPass::SetBaseVertex(uint64_t value) {
pending_.base_vertex = value;
- delegate_->SetBaseVertex(value);
+ if (delegate_) {
+ delegate_->SetBaseVertex(value);
+ }
}
// |RenderPass|
void RecordingRenderPass::SetViewport(Viewport viewport) {
pending_.viewport = viewport;
- delegate_->SetViewport(viewport);
+ if (delegate_) {
+ delegate_->SetViewport(viewport);
+ }
}
// |RenderPass|
void RecordingRenderPass::SetScissor(IRect scissor) {
pending_.scissor = scissor;
- delegate_->SetScissor(scissor);
+ if (delegate_) {
+ delegate_->SetScissor(scissor);
+ }
}
// |RenderPass|
void RecordingRenderPass::SetInstanceCount(size_t count) {
pending_.instance_count = count;
- delegate_->SetInstanceCount(count);
+ if (delegate_) {
+ delegate_->SetInstanceCount(count);
+ }
}
// |RenderPass|
bool RecordingRenderPass::SetVertexBuffer(VertexBuffer buffer) {
pending_.vertex_buffer = buffer;
- return delegate_->SetVertexBuffer(buffer);
+ if (delegate_) {
+ return delegate_->SetVertexBuffer(buffer);
+ }
+ return true;
}
// |RenderPass|
fml::Status RecordingRenderPass::Draw() {
commands_.emplace_back(std::move(pending_));
pending_ = {};
- return delegate_->Draw();
+ if (delegate_) {
+ return delegate_->Draw();
+ }
+ return fml::Status();
}
// |RenderPass|
@@ -78,7 +98,10 @@
// |RenderPass|
bool RecordingRenderPass::OnEncodeCommands(const Context& context) const {
- return delegate_->EncodeCommands();
+ if (delegate_) {
+ return delegate_->EncodeCommands();
+ }
+ return true;
}
// |RenderPass|
@@ -88,7 +111,10 @@
const ShaderMetadata& metadata,
BufferView view) {
pending_.BindResource(stage, type, slot, metadata, view);
- return delegate_->BindResource(stage, type, slot, metadata, view);
+ if (delegate_) {
+ return delegate_->BindResource(stage, type, slot, metadata, view);
+ }
+ return true;
}
// |RenderPass|
@@ -99,7 +125,10 @@
const std::shared_ptr<const ShaderMetadata>& metadata,
BufferView view) {
pending_.BindResource(stage, type, slot, metadata, view);
- return delegate_->BindResource(stage, type, slot, metadata, view);
+ if (delegate_) {
+ return delegate_->BindResource(stage, type, slot, metadata, view);
+ }
+ return true;
}
// |RenderPass|
@@ -111,7 +140,11 @@
std::shared_ptr<const Texture> texture,
const std::unique_ptr<const Sampler>& sampler) {
pending_.BindResource(stage, type, slot, metadata, texture, sampler);
- return delegate_->BindResource(stage, type, slot, metadata, texture, sampler);
+ if (delegate_) {
+ return delegate_->BindResource(stage, type, slot, metadata, texture,
+ sampler);
+ }
+ return true;
}
} // namespace impeller
diff --git a/impeller/entity/contents/test/recording_render_pass.h b/impeller/entity/contents/test/recording_render_pass.h
index 146b9fc..12fd10c 100644
--- a/impeller/entity/contents/test/recording_render_pass.h
+++ b/impeller/entity/contents/test/recording_render_pass.h
@@ -12,7 +12,7 @@
class RecordingRenderPass : public RenderPass {
public:
explicit RecordingRenderPass(std::shared_ptr<RenderPass> delegate,
- const std::shared_ptr<Context>& context,
+ const std::shared_ptr<const Context>& context,
const RenderTarget& render_target);
~RecordingRenderPass() = default;
diff --git a/impeller/entity/contents/tiled_texture_contents_unittests.cc b/impeller/entity/contents/tiled_texture_contents_unittests.cc
index ac0b9a3..5100a56 100644
--- a/impeller/entity/contents/tiled_texture_contents_unittests.cc
+++ b/impeller/entity/contents/tiled_texture_contents_unittests.cc
@@ -44,8 +44,8 @@
const std::vector<Command>& commands = recording_pass->GetCommands();
ASSERT_EQ(commands.size(), 1u);
- ASSERT_STREQ(commands[0].pipeline->GetDescriptor().GetLabel().c_str(),
- "TextureFill Pipeline V#1");
+ EXPECT_TRUE(commands[0].pipeline->GetDescriptor().GetLabel().find(
+ "TextureFill Pipeline") != std::string::npos);
if (GetParam() == PlaygroundBackend::kMetal) {
recording_pass->EncodeCommands();
@@ -84,8 +84,8 @@
const std::vector<Command>& commands = render_pass->GetCommands();
ASSERT_EQ(commands.size(), 1u);
- ASSERT_STREQ(commands[0].pipeline->GetDescriptor().GetLabel().c_str(),
- "TiledTextureFillExternal Pipeline V#1");
+ EXPECT_TRUE(commands[0].pipeline->GetDescriptor().GetLabel().find(
+ "TiledTextureFillExternal Pipeline") != std::string::npos);
}
#endif
diff --git a/impeller/entity/entity_unittests.cc b/impeller/entity/entity_unittests.cc
index 4222715..e5a6adc 100644
--- a/impeller/entity/entity_unittests.cc
+++ b/impeller/entity/entity_unittests.cc
@@ -2608,6 +2608,8 @@
std::vector<TextureDescriptor> GetDescriptors() const { return allocated_; }
+ void ResetDescriptors() { allocated_.clear(); }
+
private:
std::vector<TextureDescriptor> allocated_;
};
@@ -2645,6 +2647,7 @@
auto content_context = ContentContext(
GetContext(), TypographerContextSkia::Make(), test_allocator);
pass->AddEntity(std::move(entity));
+ test_allocator->ResetDescriptors();
EXPECT_TRUE(pass->Render(content_context, rt));
@@ -2656,18 +2659,22 @@
EXPECT_EQ(test_allocator->GetDescriptors()[4].size, ISize(200, 200));
EXPECT_EQ(test_allocator->GetDescriptors()[5].size, ISize(200, 200));
- } else if (test_allocator->GetDescriptors().size() == 9u) {
+ } else if (test_allocator->GetDescriptors().size() == 7u) {
// Onscreen render target.
EXPECT_EQ(test_allocator->GetDescriptors()[0].size, ISize(1000, 1000));
EXPECT_EQ(test_allocator->GetDescriptors()[1].size, ISize(1000, 1000));
EXPECT_EQ(test_allocator->GetDescriptors()[2].size, ISize(1000, 1000));
- EXPECT_EQ(test_allocator->GetDescriptors()[3].size, ISize(1000, 1000));
- EXPECT_EQ(test_allocator->GetDescriptors()[4].size, ISize(1000, 1000));
- EXPECT_EQ(test_allocator->GetDescriptors()[5].size, ISize(200, 200));
+ EXPECT_EQ(test_allocator->GetDescriptors()[3].size, ISize(200, 200));
+ EXPECT_EQ(test_allocator->GetDescriptors()[4].size, ISize(200, 200));
EXPECT_EQ(test_allocator->GetDescriptors()[5].size, ISize(200, 200));
EXPECT_EQ(test_allocator->GetDescriptors()[6].size, ISize(200, 200));
- EXPECT_EQ(test_allocator->GetDescriptors()[7].size, ISize(200, 200));
+ } else if (test_allocator->GetDescriptors().size() == 4u) {
+ EXPECT_EQ(test_allocator->GetDescriptors()[0].size, ISize(1000, 1000));
+ EXPECT_EQ(test_allocator->GetDescriptors()[1].size, ISize(1000, 1000));
+
+ EXPECT_EQ(test_allocator->GetDescriptors()[2].size, ISize(200, 200));
+ EXPECT_EQ(test_allocator->GetDescriptors()[3].size, ISize(200, 200));
} else {
EXPECT_TRUE(false);
}
diff --git a/impeller/renderer/shader_library.h b/impeller/renderer/shader_library.h
index a28817c..1421ea3 100644
--- a/impeller/renderer/shader_library.h
+++ b/impeller/renderer/shader_library.h
@@ -9,7 +9,6 @@
#include <memory>
#include <string_view>
-#include "flutter/fml/macros.h"
#include "fml/mapping.h"
#include "impeller/core/shader_types.h"