[Impeller] store GLES bindings on render pass w/ offsets instead of per-command. (flutter/engine#56910)

To reduce heap fragmentation from tons of little vectors.
This commit is contained in:
Jonah Williams 2024-12-04 22:13:30 -08:00 committed by GitHub
parent 4453a2b85b
commit 7b1b6d13f2
19 changed files with 200 additions and 351 deletions

View File

@ -229,8 +229,6 @@ impeller_component("entity_test_helpers") {
testonly = true testonly = true
sources = [ sources = [
"contents/test/contents_test_helpers.cc",
"contents/test/contents_test_helpers.h",
"contents/test/recording_render_pass.cc", "contents/test/recording_render_pass.cc",
"contents/test/recording_render_pass.h", "contents/test/recording_render_pass.h",
] ]

View File

@ -24,6 +24,38 @@
namespace impeller { namespace impeller {
namespace {
constexpr char kPaddingType = 0;
constexpr char kFloatType = 1;
} // namespace
// static
BufferView RuntimeEffectContents::EmplaceVulkanUniform(
const std::shared_ptr<const std::vector<uint8_t>>& input_data,
HostBuffer& host_buffer,
const RuntimeUniformDescription& uniform) {
// TODO(jonahwilliams): rewrite this to emplace directly into
// HostBuffer.
std::vector<float> uniform_buffer;
uniform_buffer.reserve(uniform.struct_layout.size());
size_t uniform_byte_index = 0u;
for (char byte_type : uniform.struct_layout) {
if (byte_type == kPaddingType) {
uniform_buffer.push_back(0.f);
} else {
FML_DCHECK(byte_type == kFloatType);
uniform_buffer.push_back(reinterpret_cast<const float*>(
input_data->data())[uniform_byte_index++]);
}
}
size_t alignment = std::max(sizeof(float) * uniform_buffer.size(),
DefaultUniformAlignment());
return host_buffer.Emplace(
reinterpret_cast<const void*>(uniform_buffer.data()),
sizeof(float) * uniform_buffer.size(), alignment);
}
void RuntimeEffectContents::SetRuntimeStage( void RuntimeEffectContents::SetRuntimeStage(
std::shared_ptr<RuntimeStage> runtime_stage) { std::shared_ptr<RuntimeStage> runtime_stage) {
runtime_stage_ = std::move(runtime_stage); runtime_stage_ = std::move(runtime_stage);
@ -251,30 +283,11 @@ bool RuntimeEffectContents::Render(const ContentContext& renderer,
uniform_slot.binding = uniform.location; uniform_slot.binding = uniform.location;
uniform_slot.name = uniform.name.c_str(); uniform_slot.name = uniform.name.c_str();
// TODO(jonahwilliams): rewrite this to emplace directly into pass.BindResource(
// HostBuffer. ShaderStage::kFragment, DescriptorType::kUniformBuffer,
std::vector<float> uniform_buffer; uniform_slot, nullptr,
uniform_buffer.reserve(uniform.struct_layout.size()); EmplaceVulkanUniform(uniform_data_,
size_t uniform_byte_index = 0u; renderer.GetTransientsBuffer(), uniform));
for (const auto& byte_type : uniform.struct_layout) {
if (byte_type == 0) {
uniform_buffer.push_back(0.f);
} else if (byte_type == 1) {
uniform_buffer.push_back(reinterpret_cast<float*>(
uniform_data_->data())[uniform_byte_index++]);
} else {
FML_UNREACHABLE();
}
}
size_t alignment = std::max(sizeof(float) * uniform_buffer.size(),
DefaultUniformAlignment());
BufferView buffer_view = renderer.GetTransientsBuffer().Emplace(
reinterpret_cast<const void*>(uniform_buffer.data()),
sizeof(float) * uniform_buffer.size(), alignment);
pass.BindResource(ShaderStage::kFragment,
DescriptorType::kUniformBuffer, uniform_slot,
nullptr, std::move(buffer_view));
} }
} }
} }

View File

@ -8,6 +8,7 @@
#include <memory> #include <memory>
#include <vector> #include <vector>
#include "impeller/core/host_buffer.h"
#include "impeller/core/sampler_descriptor.h" #include "impeller/core/sampler_descriptor.h"
#include "impeller/entity/contents/color_source_contents.h" #include "impeller/entity/contents/color_source_contents.h"
#include "impeller/runtime_stage/runtime_stage.h" #include "impeller/runtime_stage/runtime_stage.h"
@ -35,6 +36,12 @@ class RuntimeEffectContents final : public ColorSourceContents {
/// Load the runtime effect and ensure a default PSO is initialized. /// Load the runtime effect and ensure a default PSO is initialized.
bool BootstrapShader(const ContentContext& renderer) const; bool BootstrapShader(const ContentContext& renderer) const;
// Visible for testing
static BufferView EmplaceVulkanUniform(
const std::shared_ptr<const std::vector<uint8_t>>& input_data,
HostBuffer& host_buffer,
const RuntimeUniformDescription& uniform);
private: private:
bool RegisterShader(const ContentContext& renderer) const; bool RegisterShader(const ContentContext& renderer) const;

View File

@ -1,11 +0,0 @@
// 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/entity/contents/test/contents_test_helpers.h"
namespace impeller::testing {
//
} // namespace impeller::testing

View File

@ -1,49 +0,0 @@
// 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.
#ifndef FLUTTER_IMPELLER_ENTITY_CONTENTS_TEST_CONTENTS_TEST_HELPERS_H_
#define FLUTTER_IMPELLER_ENTITY_CONTENTS_TEST_CONTENTS_TEST_HELPERS_H_
#include "impeller/renderer/command.h"
namespace impeller::testing {
/// @brief Retrieve the [VertInfo] struct data from the provided [command].
template <typename T>
typename T::VertInfo* GetVertInfo(const Command& command) {
auto resource = std::find_if(command.vertex_bindings.buffers.begin(),
command.vertex_bindings.buffers.end(),
[](const BufferAndUniformSlot& data) {
return data.slot.ext_res_0 == 0u;
});
if (resource == command.vertex_bindings.buffers.end()) {
return nullptr;
}
auto data = (resource->view.resource.buffer->OnGetContents() +
resource->view.resource.range.offset);
return reinterpret_cast<typename T::VertInfo*>(data);
}
/// @brief Retrieve the [FragInfo] struct data from the provided [command].
template <typename T>
typename T::FragInfo* GetFragInfo(const Command& command) {
auto resource = std::find_if(command.fragment_bindings.buffers.begin(),
command.fragment_bindings.buffers.end(),
[](const BufferAndUniformSlot& data) {
return data.slot.ext_res_0 == 0u ||
data.slot.binding == 64;
});
if (resource == command.fragment_bindings.buffers.end()) {
return nullptr;
}
auto data = (resource->view.resource.buffer->OnGetContents() +
resource->view.resource.range.offset);
return reinterpret_cast<typename T::FragInfo*>(data);
}
} // namespace impeller::testing
#endif // FLUTTER_IMPELLER_ENTITY_CONTENTS_TEST_CONTENTS_TEST_HELPERS_H_

View File

@ -110,7 +110,6 @@ bool RecordingRenderPass::BindResource(ShaderStage stage,
const ShaderUniformSlot& slot, const ShaderUniformSlot& slot,
const ShaderMetadata* metadata, const ShaderMetadata* metadata,
BufferView view) { BufferView view) {
pending_.BindResource(stage, type, slot, metadata, view);
if (delegate_) { if (delegate_) {
return delegate_->BindResource(stage, type, slot, metadata, view); return delegate_->BindResource(stage, type, slot, metadata, view);
} }
@ -124,7 +123,6 @@ bool RecordingRenderPass::BindDynamicResource(
const ShaderUniformSlot& slot, const ShaderUniformSlot& slot,
std::unique_ptr<ShaderMetadata> metadata, std::unique_ptr<ShaderMetadata> metadata,
BufferView view) { BufferView view) {
pending_.BindResource(stage, type, slot, metadata.get(), view);
if (delegate_) { if (delegate_) {
return delegate_->BindDynamicResource(stage, type, slot, return delegate_->BindDynamicResource(stage, type, slot,
std::move(metadata), view); std::move(metadata), view);
@ -140,7 +138,6 @@ bool RecordingRenderPass::BindDynamicResource(
std::unique_ptr<ShaderMetadata> metadata, std::unique_ptr<ShaderMetadata> metadata,
std::shared_ptr<const Texture> texture, std::shared_ptr<const Texture> texture,
const std::unique_ptr<const Sampler>& sampler) { const std::unique_ptr<const Sampler>& sampler) {
pending_.BindResource(stage, type, slot, metadata.get(), texture, sampler);
if (delegate_) { if (delegate_) {
return delegate_->BindDynamicResource( return delegate_->BindDynamicResource(
stage, type, slot, std::move(metadata), texture, sampler); stage, type, slot, std::move(metadata), texture, sampler);
@ -155,7 +152,6 @@ bool RecordingRenderPass::BindResource(
const ShaderMetadata* metadata, const ShaderMetadata* metadata,
std::shared_ptr<const Texture> texture, std::shared_ptr<const Texture> texture,
const std::unique_ptr<const Sampler>& sampler) { const std::unique_ptr<const Sampler>& sampler) {
pending_.BindResource(stage, type, slot, metadata, texture, sampler);
if (delegate_) { if (delegate_) {
return delegate_->BindResource(stage, type, slot, metadata, texture, return delegate_->BindResource(stage, type, slot, metadata, texture,
sampler); sampler);

View File

@ -1843,27 +1843,16 @@ TEST_P(EntityTest, RuntimeEffectSetsRightSizeWhenUniformIsStruct) {
auto uniform_data = std::make_shared<std::vector<uint8_t>>(); auto uniform_data = std::make_shared<std::vector<uint8_t>>();
uniform_data->resize(sizeof(FragUniforms)); uniform_data->resize(sizeof(FragUniforms));
memcpy(uniform_data->data(), &frag_uniforms, sizeof(FragUniforms)); memcpy(uniform_data->data(), &frag_uniforms, sizeof(FragUniforms));
contents->SetUniformData(uniform_data);
Entity entity; auto buffer_view = RuntimeEffectContents::EmplaceVulkanUniform(
entity.SetContents(contents); uniform_data, GetContentContext()->GetTransientsBuffer(),
runtime_stage->GetUniforms()[0]);
auto context = GetContentContext();
RenderTarget target = context->GetRenderTargetCache()->CreateOffscreen(
*context->GetContext(), {1, 1}, 1u);
testing::MockRenderPass pass(GetContext(), target);
ASSERT_TRUE(contents->Render(*context, entity, pass));
ASSERT_EQ(pass.GetCommands().size(), 1u);
const auto& command = pass.GetCommands()[0];
ASSERT_EQ(command.fragment_bindings.buffers.size(), 1u);
// 16 bytes: // 16 bytes:
// 8 bytes for iResolution // 8 bytes for iResolution
// 4 bytes for iTime // 4 bytes for iTime
// 4 bytes padding // 4 bytes padding
EXPECT_EQ( EXPECT_EQ(buffer_view.GetRange().length, 16u);
command.fragment_bindings.buffers[0].view.resource.GetRange().length,
16u);
} }
TEST_P(EntityTest, ColorFilterWithForegroundColorAdvancedBlend) { TEST_P(EntityTest, ColorFilterWithForegroundColorAdvancedBlend) {

View File

@ -13,6 +13,7 @@
#include "impeller/renderer/backend/gles/formats_gles.h" #include "impeller/renderer/backend/gles/formats_gles.h"
#include "impeller/renderer/backend/gles/sampler_gles.h" #include "impeller/renderer/backend/gles/sampler_gles.h"
#include "impeller/renderer/backend/gles/texture_gles.h" #include "impeller/renderer/backend/gles/texture_gles.h"
#include "impeller/renderer/command.h"
namespace impeller { namespace impeller {
@ -179,27 +180,23 @@ bool BufferBindingsGLES::BindVertexAttributes(const ProcTableGLES& gl,
return true; return true;
} }
bool BufferBindingsGLES::BindUniformData(const ProcTableGLES& gl, bool BufferBindingsGLES::BindUniformData(
const Bindings& vertex_bindings, const ProcTableGLES& gl,
const Bindings& fragment_bindings) { const std::vector<TextureAndSampler>& bound_textures,
for (const auto& buffer : vertex_bindings.buffers) { const std::vector<BufferResource>& bound_buffers,
if (!BindUniformBuffer(gl, buffer.view)) { Range texture_range,
Range buffer_range) {
for (auto i = 0u; i < buffer_range.length; i++) {
if (!BindUniformBuffer(gl, bound_buffers[buffer_range.offset + i])) {
return false; return false;
} }
} }
for (const auto& buffer : fragment_bindings.buffers) {
if (!BindUniformBuffer(gl, buffer.view)) {
return false;
}
}
std::optional<size_t> next_unit_index = std::optional<size_t> next_unit_index =
BindTextures(gl, vertex_bindings, ShaderStage::kVertex); BindTextures(gl, bound_textures, texture_range, ShaderStage::kVertex);
if (!next_unit_index.has_value()) { if (!next_unit_index.has_value()) {
return false; return false;
} }
if (!BindTextures(gl, bound_textures, texture_range, ShaderStage::kFragment,
if (!BindTextures(gl, fragment_bindings, ShaderStage::kFragment,
*next_unit_index) *next_unit_index)
.has_value()) { .has_value()) {
return false; return false;
@ -389,11 +386,16 @@ bool BufferBindingsGLES::BindUniformBuffer(const ProcTableGLES& gl,
std::optional<size_t> BufferBindingsGLES::BindTextures( std::optional<size_t> BufferBindingsGLES::BindTextures(
const ProcTableGLES& gl, const ProcTableGLES& gl,
const Bindings& bindings, const std::vector<TextureAndSampler>& bound_textures,
Range texture_range,
ShaderStage stage, ShaderStage stage,
size_t unit_start_index) { size_t unit_start_index) {
size_t active_index = unit_start_index; size_t active_index = unit_start_index;
for (const auto& data : bindings.sampled_images) { for (auto i = 0u; i < texture_range.length; i++) {
const TextureAndSampler& data = bound_textures[texture_range.offset + i];
if (data.stage != stage) {
continue;
}
const auto& texture_gles = TextureGLES::Cast(*data.texture.resource); const auto& texture_gles = TextureGLES::Cast(*data.texture.resource);
if (data.texture.GetMetadata() == nullptr) { if (data.texture.GetMetadata() == nullptr) {
VALIDATION_LOG << "No metadata found for texture binding."; VALIDATION_LOG << "No metadata found for texture binding.";

View File

@ -42,8 +42,10 @@ class BufferBindingsGLES {
size_t vertex_offset); size_t vertex_offset);
bool BindUniformData(const ProcTableGLES& gl, bool BindUniformData(const ProcTableGLES& gl,
const Bindings& vertex_bindings, const std::vector<TextureAndSampler>& bound_textures,
const Bindings& fragment_bindings); const std::vector<BufferResource>& bound_buffers,
Range texture_range,
Range buffer_range);
bool UnbindVertexAttributes(const ProcTableGLES& gl); bool UnbindVertexAttributes(const ProcTableGLES& gl);
@ -75,10 +77,12 @@ class BufferBindingsGLES {
bool BindUniformBuffer(const ProcTableGLES& gl, const BufferResource& buffer); bool BindUniformBuffer(const ProcTableGLES& gl, const BufferResource& buffer);
std::optional<size_t> BindTextures(const ProcTableGLES& gl, std::optional<size_t> BindTextures(
const Bindings& bindings, const ProcTableGLES& gl,
ShaderStage stage, const std::vector<TextureAndSampler>& bound_textures,
size_t unit_start_index = 0); Range texture_range,
ShaderStage stage,
size_t unit_start_index = 0);
BufferBindingsGLES(const BufferBindingsGLES&) = delete; BufferBindingsGLES(const BufferBindingsGLES&) = delete;

View File

@ -7,7 +7,7 @@
#include "impeller/renderer/backend/gles/buffer_bindings_gles.h" #include "impeller/renderer/backend/gles/buffer_bindings_gles.h"
#include "impeller/renderer/backend/gles/device_buffer_gles.h" #include "impeller/renderer/backend/gles/device_buffer_gles.h"
#include "impeller/renderer/backend/gles/test/mock_gles.h" #include "impeller/renderer/backend/gles/test/mock_gles.h"
#include "impeller/renderer/testing/mocks.h" #include "impeller/renderer/command.h"
namespace impeller { namespace impeller {
namespace testing { namespace testing {
@ -18,7 +18,8 @@ TEST(BufferBindingsGLESTest, BindUniformData) {
uniform_bindings["SHADERMETADATA.FOOBAR"] = 1; uniform_bindings["SHADERMETADATA.FOOBAR"] = 1;
bindings.SetUniformBindings(std::move(uniform_bindings)); bindings.SetUniformBindings(std::move(uniform_bindings));
std::shared_ptr<MockGLES> mock_gl = MockGLES::Init(); std::shared_ptr<MockGLES> mock_gl = MockGLES::Init();
Bindings vertex_bindings; std::vector<BufferResource> bound_buffers;
std::vector<TextureAndSampler> bound_textures;
ShaderMetadata shader_metadata = { ShaderMetadata shader_metadata = {
.name = "shader_metadata", .name = "shader_metadata",
@ -33,14 +34,11 @@ TEST(BufferBindingsGLESTest, BindUniformData) {
DeviceBufferGLES device_buffer(DeviceBufferDescriptor{.size = sizeof(float)}, DeviceBufferGLES device_buffer(DeviceBufferDescriptor{.size = sizeof(float)},
reactor, backing_store); reactor, backing_store);
BufferView buffer_view(&device_buffer, Range(0, sizeof(float))); BufferView buffer_view(&device_buffer, Range(0, sizeof(float)));
vertex_bindings.buffers.push_back(BufferAndUniformSlot{ bound_buffers.push_back(BufferResource(&shader_metadata, buffer_view));
.slot =
ShaderUniformSlot{ EXPECT_TRUE(bindings.BindUniformData(mock_gl->GetProcTable(), bound_textures,
.name = "foobar", .ext_res_0 = 0, .set = 0, .binding = 0}, bound_buffers, Range{0, 0},
.view = BufferResource(&shader_metadata, buffer_view)}); Range{0, 1}));
Bindings fragment_bindings;
EXPECT_TRUE(bindings.BindUniformData(mock_gl->GetProcTable(), vertex_bindings,
fragment_bindings));
std::vector<std::string> captured_calls = mock_gl->GetCapturedCalls(); std::vector<std::string> captured_calls = mock_gl->GetCapturedCalls();
EXPECT_TRUE(std::find(captured_calls.begin(), captured_calls.end(), EXPECT_TRUE(std::find(captured_calls.begin(), captured_calls.end(),
"glUniform1fv") != captured_calls.end()); "glUniform1fv") != captured_calls.end());

View File

@ -18,6 +18,7 @@
#include "impeller/renderer/backend/gles/gpu_tracer_gles.h" #include "impeller/renderer/backend/gles/gpu_tracer_gles.h"
#include "impeller/renderer/backend/gles/pipeline_gles.h" #include "impeller/renderer/backend/gles/pipeline_gles.h"
#include "impeller/renderer/backend/gles/texture_gles.h" #include "impeller/renderer/backend/gles/texture_gles.h"
#include "impeller/renderer/command.h"
namespace impeller { namespace impeller {
@ -188,9 +189,10 @@ void RenderPassGLES::ResetGLState(const ProcTableGLES& gl) {
[[nodiscard]] bool EncodeCommandsInReactor( [[nodiscard]] bool EncodeCommandsInReactor(
const RenderPassData& pass_data, const RenderPassData& pass_data,
const std::shared_ptr<Allocator>& transients_allocator,
const ReactorGLES& reactor, const ReactorGLES& reactor,
const std::vector<Command>& commands, const std::vector<Command>& commands,
const std::vector<TextureAndSampler>& bound_textures,
const std::vector<BufferResource>& bound_buffers,
const std::shared_ptr<GPUTracerGLES>& tracer) { const std::shared_ptr<GPUTracerGLES>& tracer) {
TRACE_EVENT0("impeller", "RenderPassGLES::EncodeCommandsInReactor"); TRACE_EVENT0("impeller", "RenderPassGLES::EncodeCommandsInReactor");
@ -456,10 +458,13 @@ void RenderPassGLES::ResetGLState(const ProcTableGLES& gl) {
//-------------------------------------------------------------------------- //--------------------------------------------------------------------------
/// Bind uniform data. /// Bind uniform data.
/// ///
if (!vertex_desc_gles->BindUniformData(gl, // if (!vertex_desc_gles->BindUniformData(
command.vertex_bindings, // gl, //
command.fragment_bindings // bound_textures, //
)) { bound_buffers, //
/*texture_range=*/command.bound_textures, //
/*buffer_range=*/command.bound_buffers //
)) {
return false; return false;
} }
@ -602,13 +607,18 @@ bool RenderPassGLES::OnEncodeCommands(const Context& context) const {
CanDiscardAttachmentWhenDone(stencil0->store_action); CanDiscardAttachmentWhenDone(stencil0->store_action);
} }
std::shared_ptr<const RenderPassGLES> shared_this = shared_from_this();
auto tracer = ContextGLES::Cast(context).GetGPUTracer();
return reactor_->AddOperation( return reactor_->AddOperation(
[pass_data, allocator = context.GetResourceAllocator(), [pass_data = std::move(pass_data), render_pass = shared_from_this(),
render_pass = std::move(shared_this), tracer](const auto& reactor) { tracer =
auto result = EncodeCommandsInReactor(*pass_data, allocator, reactor, ContextGLES::Cast(context).GetGPUTracer()](const auto& reactor) {
render_pass->commands_, tracer); auto result = EncodeCommandsInReactor(
/*pass_data=*/*pass_data, //
/*reactor=*/reactor, //
/*commands=*/render_pass->commands_, //
/*bound_textures=*/render_pass->bound_textures_, //
/*bound_buffers=*/render_pass->bound_buffers_, //
/*tracer=*/tracer //
);
FML_CHECK(result) FML_CHECK(result)
<< "Must be able to encode GL commands without error."; << "Must be able to encode GL commands without error.";
}, },

View File

@ -48,9 +48,6 @@ class RenderPassMTL final : public RenderPass {
const RenderTarget& target, const RenderTarget& target,
id<MTLCommandBuffer> buffer); id<MTLCommandBuffer> buffer);
// |RenderPass|
void ReserveCommands(size_t command_count) override {}
// |RenderPass| // |RenderPass|
bool IsValid() const override; bool IsValid() const override;

View File

@ -92,9 +92,6 @@ class RenderPassVK final : public RenderPass {
// |RenderPass| // |RenderPass|
fml::Status Draw() override; fml::Status Draw() override;
// |RenderPass|
void ReserveCommands(size_t command_count) override {}
// |ResourceBinder| // |ResourceBinder|
bool BindResource(ShaderStage stage, bool BindResource(ShaderStage stage,
DescriptorType type, DescriptorType type,

View File

@ -4,11 +4,8 @@
#include "impeller/renderer/command.h" #include "impeller/renderer/command.h"
#include <utility>
#include "impeller/base/validation.h" #include "impeller/base/validation.h"
#include "impeller/core/formats.h" #include "impeller/core/formats.h"
#include "impeller/renderer/vertex_descriptor.h"
namespace impeller { namespace impeller {
@ -26,112 +23,4 @@ bool Command::BindVertices(const VertexBuffer& buffer) {
return true; return true;
} }
bool Command::BindResource(ShaderStage stage,
DescriptorType type,
const ShaderUniformSlot& slot,
const ShaderMetadata* metadata,
BufferView view) {
FML_DCHECK(slot.ext_res_0 != VertexDescriptor::kReservedVertexBufferIndex);
if (!view) {
return false;
}
BufferResource resouce = BufferResource(metadata, std::move(view));
return BindBuffer(stage, slot, std::move(resouce));
}
bool Command::BindDynamicResource(ShaderStage stage,
DescriptorType type,
const ShaderUniformSlot& slot,
std::unique_ptr<ShaderMetadata> metadata,
BufferView view) {
FML_DCHECK(slot.ext_res_0 != VertexDescriptor::kReservedVertexBufferIndex);
if (!view) {
return false;
}
BufferResource resouce =
BufferResource::MakeDynamic(std::move(metadata), std::move(view));
return BindBuffer(stage, slot, std::move(resouce));
}
bool Command::BindResource(ShaderStage stage,
DescriptorType type,
const SampledImageSlot& slot,
const ShaderMetadata* metadata,
std::shared_ptr<const Texture> texture,
const std::unique_ptr<const Sampler>& sampler) {
if (!sampler) {
return false;
}
if (!texture || !texture->IsValid()) {
return false;
}
TextureResource resource = TextureResource(metadata, std::move(texture));
return BindTexture(stage, slot, std::move(resource), sampler);
}
bool Command::BindDynamicResource(
ShaderStage stage,
DescriptorType type,
const SampledImageSlot& slot,
std::unique_ptr<ShaderMetadata> metadata,
std::shared_ptr<const Texture> texture,
const std::unique_ptr<const Sampler>& sampler) {
if (!sampler) {
return false;
}
if (!texture || !texture->IsValid()) {
return false;
}
TextureResource resource =
TextureResource::MakeDynamic(std::move(metadata), std::move(texture));
return BindTexture(stage, slot, std::move(resource), sampler);
}
bool Command::BindBuffer(ShaderStage stage,
const ShaderUniformSlot& slot,
BufferResource resource) {
BufferAndUniformSlot data =
BufferAndUniformSlot{.slot = slot, .view = std::move(resource)};
switch (stage) {
case ShaderStage::kVertex:
vertex_bindings.buffers.push_back(std::move(data));
return true;
case ShaderStage::kFragment:
fragment_bindings.buffers.push_back(std::move(data));
return true;
case ShaderStage::kCompute:
VALIDATION_LOG << "Use ComputeCommands for compute shader stages.";
case ShaderStage::kUnknown:
return false;
}
return false;
}
bool Command::BindTexture(ShaderStage stage,
const SampledImageSlot& slot,
TextureResource resource,
const std::unique_ptr<const Sampler>& sampler) {
TextureAndSampler data = TextureAndSampler{
.slot = slot,
.texture = std::move(resource),
.sampler = &sampler,
};
switch (stage) {
case ShaderStage::kVertex:
vertex_bindings.sampled_images.push_back(std::move(data));
return true;
case ShaderStage::kFragment:
fragment_bindings.sampled_images.push_back((std::move(data)));
return true;
case ShaderStage::kCompute:
VALIDATION_LOG << "Use ComputeCommands for compute shader stages.";
case ShaderStage::kUnknown:
return false;
}
}
} // namespace impeller } // namespace impeller

View File

@ -12,7 +12,6 @@
#include "impeller/core/buffer_view.h" #include "impeller/core/buffer_view.h"
#include "impeller/core/formats.h" #include "impeller/core/formats.h"
#include "impeller/core/resource_binder.h"
#include "impeller/core/sampler.h" #include "impeller/core/sampler.h"
#include "impeller/core/shader_types.h" #include "impeller/core/shader_types.h"
#include "impeller/core/texture.h" #include "impeller/core/texture.h"
@ -22,12 +21,6 @@
namespace impeller { namespace impeller {
#ifdef IMPELLER_DEBUG
#define DEBUG_COMMAND_INFO(obj, arg) obj.label = arg;
#else
#define DEBUG_COMMAND_INFO(obj, arg)
#endif // IMPELLER_DEBUG
template <class T> template <class T>
class Resource { class Resource {
public: public:
@ -65,21 +58,11 @@ using TextureResource = Resource<std::shared_ptr<const Texture>>;
/// @brief combines the texture, sampler and sampler slot information. /// @brief combines the texture, sampler and sampler slot information.
struct TextureAndSampler { struct TextureAndSampler {
SampledImageSlot slot; SampledImageSlot slot;
ShaderStage stage;
TextureResource texture; TextureResource texture;
const std::unique_ptr<const Sampler>* sampler; const std::unique_ptr<const Sampler>* sampler;
}; };
/// @brief combines the buffer resource and its uniform slot information.
struct BufferAndUniformSlot {
ShaderUniformSlot slot;
BufferResource view;
};
struct Bindings {
std::vector<TextureAndSampler> sampled_images;
std::vector<BufferAndUniformSlot> buffers;
};
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
/// @brief An object used to specify work to the GPU along with references /// @brief An object used to specify work to the GPU along with references
/// to resources the GPU will used when doing said work. /// to resources the GPU will used when doing said work.
@ -94,21 +77,16 @@ struct Bindings {
/// referenced in commands views into buffers managed by other /// referenced in commands views into buffers managed by other
/// allocators and resource managers. /// allocators and resource managers.
/// ///
struct Command : public ResourceBinder { struct Command {
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
/// The pipeline to use for this command. /// The pipeline to use for this command.
/// ///
std::shared_ptr<Pipeline<PipelineDescriptor>> pipeline; std::shared_ptr<Pipeline<PipelineDescriptor>> pipeline;
//----------------------------------------------------------------------------
/// The buffer, texture, and sampler bindings used by the vertex pipeline /// An offset into render pass storage where bound buffers/texture metadata is
/// stage. /// stored.
/// Range bound_buffers = Range{0, 0};
Bindings vertex_bindings; Range bound_textures = Range{0, 0};
//----------------------------------------------------------------------------
/// The buffer, texture, and sampler bindings used by the fragment pipeline
/// stage.
///
Bindings fragment_bindings;
#ifdef IMPELLER_DEBUG #ifdef IMPELLER_DEBUG
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
@ -184,45 +162,7 @@ struct Command : public ResourceBinder {
/// ///
bool BindVertices(const VertexBuffer& buffer); bool BindVertices(const VertexBuffer& buffer);
// |ResourceBinder|
bool BindResource(ShaderStage stage,
DescriptorType type,
const ShaderUniformSlot& slot,
const ShaderMetadata* metadata,
BufferView view) override;
// |ResourceBinder|
bool BindResource(ShaderStage stage,
DescriptorType type,
const SampledImageSlot& slot,
const ShaderMetadata* metadata,
std::shared_ptr<const Texture> texture,
const std::unique_ptr<const Sampler>& sampler) override;
bool BindDynamicResource(ShaderStage stage,
DescriptorType type,
const ShaderUniformSlot& slot,
std::unique_ptr<ShaderMetadata> metadata,
BufferView view);
bool BindDynamicResource(ShaderStage stage,
DescriptorType type,
const SampledImageSlot& slot,
std::unique_ptr<ShaderMetadata> metadata,
std::shared_ptr<const Texture> texture,
const std::unique_ptr<const Sampler>& sampler);
bool IsValid() const { return pipeline && pipeline->IsValid(); } bool IsValid() const { return pipeline && pipeline->IsValid(); }
private:
bool BindBuffer(ShaderStage stage,
const ShaderUniformSlot& slot,
BufferResource resource);
bool BindTexture(ShaderStage stage,
const SampledImageSlot& slot,
TextureResource resource,
const std::unique_ptr<const Sampler>& sampler);
}; };
} // namespace impeller } // namespace impeller

View File

@ -194,8 +194,12 @@ bool RenderPass::ValidateIndexBuffer(const BufferView& index_buffer,
} }
fml::Status RenderPass::Draw() { fml::Status RenderPass::Draw() {
pending_.bound_buffers.offset = bound_buffers_start_.value_or(0u);
pending_.bound_textures.offset = bound_textures_start_.value_or(0u);
auto result = AddCommand(std::move(pending_)); auto result = AddCommand(std::move(pending_));
pending_ = Command{}; pending_ = Command{};
bound_textures_start_ = std::nullopt;
bound_buffers_start_ = std::nullopt;
if (result) { if (result) {
return fml::Status(); return fml::Status();
} }
@ -209,7 +213,12 @@ bool RenderPass::BindResource(ShaderStage stage,
const ShaderUniformSlot& slot, const ShaderUniformSlot& slot,
const ShaderMetadata* metadata, const ShaderMetadata* metadata,
BufferView view) { BufferView view) {
return pending_.BindResource(stage, type, slot, metadata, view); FML_DCHECK(slot.ext_res_0 != VertexDescriptor::kReservedVertexBufferIndex);
if (!view) {
return false;
}
BufferResource resouce = BufferResource(metadata, std::move(view));
return BindBuffer(stage, slot, std::move(resouce));
} }
// |ResourceBinder| // |ResourceBinder|
@ -219,8 +228,14 @@ bool RenderPass::BindResource(ShaderStage stage,
const ShaderMetadata* metadata, const ShaderMetadata* metadata,
std::shared_ptr<const Texture> texture, std::shared_ptr<const Texture> texture,
const std::unique_ptr<const Sampler>& sampler) { const std::unique_ptr<const Sampler>& sampler) {
return pending_.BindResource(stage, type, slot, metadata, std::move(texture), if (!sampler) {
sampler); return false;
}
if (!texture || !texture->IsValid()) {
return false;
}
TextureResource resource = TextureResource(metadata, std::move(texture));
return BindTexture(stage, slot, std::move(resource), sampler);
} }
bool RenderPass::BindDynamicResource(ShaderStage stage, bool RenderPass::BindDynamicResource(ShaderStage stage,
@ -228,8 +243,14 @@ bool RenderPass::BindDynamicResource(ShaderStage stage,
const ShaderUniformSlot& slot, const ShaderUniformSlot& slot,
std::unique_ptr<ShaderMetadata> metadata, std::unique_ptr<ShaderMetadata> metadata,
BufferView view) { BufferView view) {
return pending_.BindDynamicResource(stage, type, slot, std::move(metadata), FML_DCHECK(slot.ext_res_0 != VertexDescriptor::kReservedVertexBufferIndex);
std::move(view)); if (!view) {
return false;
}
BufferResource resouce =
BufferResource::MakeDynamic(std::move(metadata), std::move(view));
return BindBuffer(stage, slot, std::move(resouce));
} }
bool RenderPass::BindDynamicResource( bool RenderPass::BindDynamicResource(
@ -239,8 +260,46 @@ bool RenderPass::BindDynamicResource(
std::unique_ptr<ShaderMetadata> metadata, std::unique_ptr<ShaderMetadata> metadata,
std::shared_ptr<const Texture> texture, std::shared_ptr<const Texture> texture,
const std::unique_ptr<const Sampler>& sampler) { const std::unique_ptr<const Sampler>& sampler) {
return pending_.BindDynamicResource(stage, type, slot, std::move(metadata), if (!sampler) {
std::move(texture), sampler); return false;
}
if (!texture || !texture->IsValid()) {
return false;
}
TextureResource resource =
TextureResource::MakeDynamic(std::move(metadata), std::move(texture));
return BindTexture(stage, slot, std::move(resource), sampler);
}
bool RenderPass::BindBuffer(ShaderStage stage,
const ShaderUniformSlot& slot,
BufferResource resource) {
if (!bound_buffers_start_.has_value()) {
bound_buffers_start_ = bound_buffers_.size();
}
pending_.bound_buffers.length++;
bound_buffers_.push_back(std::move(resource));
return true;
}
bool RenderPass::BindTexture(ShaderStage stage,
const SampledImageSlot& slot,
TextureResource resource,
const std::unique_ptr<const Sampler>& sampler) {
TextureAndSampler data = TextureAndSampler{
.stage = stage,
.texture = std::move(resource),
.sampler = &sampler,
};
if (!bound_textures_start_.has_value()) {
bound_textures_start_ = bound_textures_.size();
}
pending_.bound_textures.length++;
bound_textures_.push_back(std::move(data));
return true;
} }
} // namespace impeller } // namespace impeller

View File

@ -18,9 +18,6 @@
namespace impeller { namespace impeller {
class HostBuffer;
class Allocator;
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
/// @brief Render passes encode render commands directed as one specific /// @brief Render passes encode render commands directed as one specific
/// render target into an underlying command buffer. /// render target into an underlying command buffer.
@ -46,13 +43,6 @@ class RenderPass : public ResourceBinder {
void SetLabel(std::string_view label); void SetLabel(std::string_view label);
/// @brief Reserve [command_count] commands in the HAL command buffer.
///
/// Note: this is not the native command buffer.
virtual void ReserveCommands(size_t command_count) {
commands_.reserve(command_count);
}
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
/// The pipeline to use for this command. /// The pipeline to use for this command.
virtual void SetPipeline( virtual void SetPipeline(
@ -202,6 +192,7 @@ class RenderPass : public ResourceBinder {
std::shared_ptr<const Texture> texture, std::shared_ptr<const Texture> texture,
const std::unique_ptr<const Sampler>& sampler); const std::unique_ptr<const Sampler>& sampler);
/// @brief Bind with dynamically generated shader metadata.
virtual bool BindDynamicResource(ShaderStage stage, virtual bool BindDynamicResource(ShaderStage stage,
DescriptorType type, DescriptorType type,
const ShaderUniformSlot& slot, const ShaderUniformSlot& slot,
@ -253,6 +244,8 @@ class RenderPass : public ResourceBinder {
const ISize render_target_size_; const ISize render_target_size_;
const RenderTarget render_target_; const RenderTarget render_target_;
std::vector<Command> commands_; std::vector<Command> commands_;
std::vector<BufferResource> bound_buffers_;
std::vector<TextureAndSampler> bound_textures_;
const Matrix orthographic_; const Matrix orthographic_;
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
@ -284,7 +277,18 @@ class RenderPass : public ResourceBinder {
RenderPass& operator=(const RenderPass&) = delete; RenderPass& operator=(const RenderPass&) = delete;
bool BindBuffer(ShaderStage stage,
const ShaderUniformSlot& slot,
BufferResource resource);
bool BindTexture(ShaderStage stage,
const SampledImageSlot& slot,
TextureResource resource,
const std::unique_ptr<const Sampler>& sampler);
Command pending_; Command pending_;
std::optional<size_t> bound_buffers_start_ = std::nullopt;
std::optional<size_t> bound_textures_start_ = std::nullopt;
}; };
} // namespace impeller } // namespace impeller

View File

@ -417,7 +417,7 @@ static bool BindUniform(
uniform_map->insert_or_assign( uniform_map->insert_or_assign(
uniform_struct, uniform_struct,
impeller::BufferAndUniformSlot{ flutter::gpu::RenderPass::BufferAndUniformSlot{
.slot = uniform_struct->slot, .slot = uniform_struct->slot,
.view = impeller::BufferResource{ .view = impeller::BufferResource{
&uniform_struct->metadata, &uniform_struct->metadata,

View File

@ -13,6 +13,7 @@
#include "flutter/lib/ui/dart_wrapper.h" #include "flutter/lib/ui/dart_wrapper.h"
#include "fml/memory/ref_ptr.h" #include "fml/memory/ref_ptr.h"
#include "impeller/core/formats.h" #include "impeller/core/formats.h"
#include "impeller/core/shader_types.h"
#include "impeller/renderer/command.h" #include "impeller/renderer/command.h"
#include "impeller/renderer/render_pass.h" #include "impeller/renderer/render_pass.h"
#include "impeller/renderer/render_target.h" #include "impeller/renderer/render_target.h"
@ -56,9 +57,14 @@ class RenderPass : public RefCountedDartWrappable<RenderPass> {
bool Draw(); bool Draw();
struct BufferAndUniformSlot {
impeller::ShaderUniformSlot slot;
impeller::BufferResource view;
};
using BufferUniformMap = using BufferUniformMap =
std::unordered_map<const flutter::gpu::Shader::UniformBinding*, std::unordered_map<const flutter::gpu::Shader::UniformBinding*,
impeller::BufferAndUniformSlot>; BufferAndUniformSlot>;
using TextureUniformMap = using TextureUniformMap =
std::unordered_map<const flutter::gpu::Shader::TextureBinding*, std::unordered_map<const flutter::gpu::Shader::TextureBinding*,
impeller::TextureAndSampler>; impeller::TextureAndSampler>;