[Impeller] avoid heap allocation in RenderTarget object. (flutter/engine#56829)

All render targets created by flutter have a single color attachment. Lets specialize that case in impeller::RenderTarget by creating a field for that attachment description instead of placing it in a hashmap.
This commit is contained in:
Jonah Williams 2024-12-04 09:28:13 -08:00 committed by GitHub
parent e05a44277f
commit 240ce64b1f
21 changed files with 289 additions and 169 deletions

View File

@ -14,6 +14,7 @@
#include "flutter/fml/logging.h"
#include "flutter/fml/trace_event.h"
#include "impeller/base/validation.h"
#include "impeller/core/formats.h"
#include "impeller/display_list/color_filter.h"
#include "impeller/display_list/image_filter.h"
#include "impeller/display_list/skia_conversions.h"
@ -853,7 +854,7 @@ void Canvas::DrawAtlas(const std::shared_ptr<AtlasContents>& atlas_contents,
void Canvas::SetupRenderPass() {
renderer_.GetRenderTargetCache()->Start();
auto color0 = render_target_.GetColorAttachments().find(0u)->second;
ColorAttachment color0 = render_target_.GetColorAttachment(0);
auto& stencil_attachment = render_target_.GetStencilAttachment();
auto& depth_attachment = render_target_.GetDepthAttachment();
@ -1460,8 +1461,7 @@ void Canvas::AddRenderEntityToCurrentPass(Entity& entity, bool reuse_depth) {
RenderTarget& render_target = render_passes_.back()
.inline_pass_context->GetPassTarget()
.GetRenderTarget();
ColorAttachment attachment =
render_target.GetColorAttachments().find(0u)->second;
ColorAttachment attachment = render_target.GetColorAttachment(0);
// Attachment.clear color needs to be premultiplied at all times, but the
// Color::Blend function requires unpremultiplied colors.
attachment.clear_color = attachment.clear_color.Unpremultiply()
@ -1588,8 +1588,7 @@ std::shared_ptr<Texture> Canvas::FlipBackdrop(Point global_pass_position,
}
if (should_use_onscreen) {
ColorAttachment color0 =
render_target_.GetColorAttachments().find(0u)->second;
ColorAttachment color0 = render_target_.GetColorAttachment(0);
// When MSAA is being used, we end up overriding the entire backdrop by
// drawing the previous pass texture, and so we don't have to clear it and
// can use kDontCare.

View File

@ -19,7 +19,7 @@ EntityPassTarget::EntityPassTarget(const RenderTarget& render_target,
std::shared_ptr<Texture> EntityPassTarget::Flip(
const ContentContext& renderer) {
auto color0 = target_.GetColorAttachments().find(0)->second;
ColorAttachment color0 = target_.GetColorAttachment(0);
if (!color0.resolve_texture) {
VALIDATION_LOG << "EntityPassTarget Flip should never be called for a "
"non-MSAA target.";

View File

@ -31,20 +31,14 @@ TEST_P(EntityPassTargetTest, SwapWithMSAATexture) {
auto entity_pass_target = EntityPassTarget(render_target, false, false);
auto color0 = entity_pass_target.GetRenderTarget()
.GetColorAttachments()
.find(0u)
->second;
auto color0 = entity_pass_target.GetRenderTarget().GetColorAttachment(0);
auto msaa_tex = color0.texture;
auto resolve_tex = color0.resolve_texture;
FML_DCHECK(content_context);
entity_pass_target.Flip(*content_context);
color0 = entity_pass_target.GetRenderTarget()
.GetColorAttachments()
.find(0u)
->second;
color0 = entity_pass_target.GetRenderTarget().GetColorAttachment(0);
ASSERT_EQ(msaa_tex, color0.texture);
ASSERT_NE(resolve_tex, color0.resolve_texture);
@ -89,10 +83,7 @@ TEST_P(EntityPassTargetTest, SwapWithMSAAImplicitResolve) {
auto entity_pass_target = EntityPassTarget(render_target, false, true);
auto color0 = entity_pass_target.GetRenderTarget()
.GetColorAttachments()
.find(0u)
->second;
auto color0 = entity_pass_target.GetRenderTarget().GetColorAttachment(0);
auto msaa_tex = color0.texture;
auto resolve_tex = color0.resolve_texture;
@ -101,10 +92,7 @@ TEST_P(EntityPassTargetTest, SwapWithMSAAImplicitResolve) {
FML_DCHECK(content_context);
entity_pass_target.Flip(*content_context);
color0 = entity_pass_target.GetRenderTarget()
.GetColorAttachments()
.find(0u)
->second;
color0 = entity_pass_target.GetRenderTarget().GetColorAttachment(0);
ASSERT_NE(msaa_tex, color0.texture);
ASSERT_NE(resolve_tex, color0.resolve_texture);

View File

@ -86,20 +86,13 @@ const std::shared_ptr<RenderPass>& InlinePassContext::GetRenderPass() {
return pass_;
}
if (pass_target_.GetRenderTarget().GetColorAttachments().empty()) {
VALIDATION_LOG << "Color attachment unexpectedly missing from the "
"EntityPass render target.";
return pass_;
}
command_buffer_->SetLabel("EntityPass Command Buffer");
{
// If the pass target has a resolve texture, then we're using MSAA.
bool is_msaa = pass_target_.GetRenderTarget()
.GetColorAttachments()
.find(0)
->second.resolve_texture != nullptr;
bool is_msaa =
pass_target_.GetRenderTarget().GetColorAttachment(0).resolve_texture !=
nullptr;
if (pass_count_ > 0 && is_msaa) {
pass_target_.Flip(renderer_);
}
@ -107,8 +100,7 @@ const std::shared_ptr<RenderPass>& InlinePassContext::GetRenderPass() {
// Find the color attachment a second time, since the target may have just
// flipped.
auto color0 =
pass_target_.GetRenderTarget().GetColorAttachments().find(0)->second;
ColorAttachment color0 = pass_target_.GetRenderTarget().GetColorAttachment(0);
bool is_msaa = color0.resolve_texture != nullptr;
if (pass_count_ > 0) {

View File

@ -3,6 +3,7 @@
// found in the LICENSE file.
#include "impeller/entity/render_target_cache.h"
#include "impeller/core/formats.h"
#include "impeller/renderer/render_target.h"
namespace impeller {
@ -52,10 +53,10 @@ RenderTarget RenderTargetCache::CreateOffscreen(
const auto other_config = render_target_data.config;
if (!render_target_data.used_this_frame && other_config == config) {
render_target_data.used_this_frame = true;
auto color0 = render_target_data.render_target.GetColorAttachments()
.find(0u)
->second;
auto depth = render_target_data.render_target.GetDepthAttachment();
ColorAttachment color0 =
render_target_data.render_target.GetColorAttachment(0);
std::optional<DepthAttachment> depth =
render_target_data.render_target.GetDepthAttachment();
std::shared_ptr<Texture> depth_tex = depth ? depth->texture : nullptr;
return RenderTargetAllocator::CreateOffscreen(
context, size, mip_count, label, color_attachment_config,
@ -102,10 +103,10 @@ RenderTarget RenderTargetCache::CreateOffscreenMSAA(
const auto other_config = render_target_data.config;
if (!render_target_data.used_this_frame && other_config == config) {
render_target_data.used_this_frame = true;
auto color0 = render_target_data.render_target.GetColorAttachments()
.find(0u)
->second;
auto depth = render_target_data.render_target.GetDepthAttachment();
ColorAttachment color0 =
render_target_data.render_target.GetColorAttachment(0);
std::optional<DepthAttachment> depth =
render_target_data.render_target.GetDepthAttachment();
std::shared_ptr<Texture> depth_tex = depth ? depth->texture : nullptr;
return RenderTargetAllocator::CreateOffscreenMSAA(
context, size, mip_count, label, color_attachment_config,

View File

@ -7,6 +7,7 @@
#include "flutter/testing/testing.h"
#include "impeller/base/validation.h"
#include "impeller/core/allocator.h"
#include "impeller/core/formats.h"
#include "impeller/core/texture_descriptor.h"
#include "impeller/entity/entity_playground.h"
#include "impeller/entity/render_target_cache.h"
@ -104,8 +105,8 @@ TEST_P(RenderTargetCacheTest, CachedTextureGetsNewAttachmentConfig) {
*GetContext(), {100, 100}, 1, "Offscreen2", color_attachment_config);
render_target_cache.End();
auto color1 = target1.GetColorAttachments().find(0)->second;
auto color2 = target2.GetColorAttachments().find(0)->second;
ColorAttachment color1 = target1.GetColorAttachment(0);
ColorAttachment color2 = target2.GetColorAttachment(0);
// The second color attachment should reuse the first attachment's texture
// but with attributes from the second AttachmentConfig.
EXPECT_EQ(color2.texture, color1.texture);

View File

@ -284,12 +284,7 @@ bool Playground::OpenPlaygroundHere(
}
buffer->SetLabel("ImGui Command Buffer");
if (render_target.GetColorAttachments().empty()) {
VALIDATION_LOG << "render target attachments are empty.";
return false;
}
auto color0 = render_target.GetColorAttachments().find(0)->second;
auto color0 = render_target.GetColorAttachment(0);
color0.load_action = LoadAction::kLoad;
if (color0.resolve_texture) {
color0.texture = color0.resolve_texture;
@ -297,7 +292,6 @@ bool Playground::OpenPlaygroundHere(
color0.store_action = StoreAction::kStore;
}
render_target.SetColorAttachment(color0, 0);
render_target.SetStencilAttachment(std::nullopt);
render_target.SetDepthAttachment(std::nullopt);

View File

@ -11,6 +11,7 @@
#include "fml/closure.h"
#include "fml/logging.h"
#include "impeller/base/validation.h"
#include "impeller/core/formats.h"
#include "impeller/renderer/backend/gles/context_gles.h"
#include "impeller/renderer/backend/gles/device_buffer_gles.h"
#include "impeller/renderer/backend/gles/formats_gles.h"
@ -550,9 +551,11 @@ bool RenderPassGLES::OnEncodeCommands(const Context& context) const {
if (!render_target.HasColorAttachment(0u)) {
return false;
}
const auto& color0 = render_target.GetColorAttachments().at(0u);
const auto& depth0 = render_target.GetDepthAttachment();
const auto& stencil0 = render_target.GetStencilAttachment();
const ColorAttachment& color0 = render_target.GetColorAttachment(0);
const std::optional<DepthAttachment>& depth0 =
render_target.GetDepthAttachment();
const std::optional<StencilAttachment>& stencil0 =
render_target.GetStencilAttachment();
auto pass_data = std::make_shared<RenderPassData>();
pass_data->label = label_;

View File

@ -22,7 +22,7 @@ TEST_P(SurfaceGLESTest, CanWrapNonZeroFBO) {
ASSERT_TRUE(surface->IsValid());
ASSERT_TRUE(surface->GetRenderTarget().HasColorAttachment(0));
const auto& texture = TextureGLES::Cast(
*(surface->GetRenderTarget().GetColorAttachments().at(0).texture));
*(surface->GetRenderTarget().GetColorAttachment(0).texture));
auto wrapped = texture.GetFBO();
ASSERT_TRUE(wrapped.has_value());
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)

View File

@ -104,15 +104,15 @@ static MTLRenderPassDescriptor* ToMTLRenderPassDescriptor(
const RenderTarget& desc) {
auto result = [MTLRenderPassDescriptor renderPassDescriptor];
const auto& colors = desc.GetColorAttachments();
bool configured_attachment = desc.IterateAllColorAttachments(
[&result](size_t index, const ColorAttachment& attachment) -> bool {
return ConfigureColorAttachment(attachment,
result.colorAttachments[index]);
});
for (const auto& color : colors) {
if (!ConfigureColorAttachment(color.second,
result.colorAttachments[color.first])) {
VALIDATION_LOG << "Could not configure color attachment at index "
<< color.first;
return nil;
}
if (!configured_attachment) {
VALIDATION_LOG << "Could not configure color attachments";
return nil;
}
const auto& depth = desc.GetDepthAttachment();

View File

@ -7,6 +7,7 @@
#include <unordered_map>
#include "fml/concurrent_message_loop.h"
#include "impeller/core/formats.h"
#include "impeller/renderer/backend/vulkan/command_queue_vk.h"
#include "impeller/renderer/backend/vulkan/descriptor_pool_vk.h"
#include "impeller/renderer/backend/vulkan/render_pass_builder_vk.h"
@ -666,15 +667,18 @@ void ContextVK::InitializeCommonlyUsedShadersIfNeeded() const {
rt_allocator.CreateOffscreenMSAA(*this, {1, 1}, 1);
RenderPassBuilderVK builder;
for (const auto& [bind_point, color] : render_target.GetColorAttachments()) {
builder.SetColorAttachment(
bind_point, //
color.texture->GetTextureDescriptor().format, //
color.texture->GetTextureDescriptor().sample_count, //
color.load_action, //
color.store_action //
);
}
render_target.IterateAllColorAttachments(
[&builder](size_t index, const ColorAttachment& attachment) -> bool {
builder.SetColorAttachment(
index, //
attachment.texture->GetTextureDescriptor().format, //
attachment.texture->GetTextureDescriptor().sample_count, //
attachment.load_action, //
attachment.store_action //
);
return true;
});
if (auto depth = render_target.GetDepthAttachment(); depth.has_value()) {
builder.SetDepthStencilAttachment(

View File

@ -48,14 +48,27 @@ RenderPassBuilderVK& RenderPassBuilderVK::SetColorAttachment(
desc.initialLayout = vk::ImageLayout::eUndefined;
}
desc.finalLayout = vk::ImageLayout::eGeneral;
colors_[index] = desc;
if (StoreActionPerformsResolve(store_action)) {
desc.storeOp = ToVKAttachmentStoreOp(store_action, true);
desc.samples = vk::SampleCountFlagBits::e1;
resolves_[index] = desc;
const bool performs_resolves = StoreActionPerformsResolve(store_action);
if (index == 0u) {
color0_ = desc;
if (performs_resolves) {
desc.storeOp = ToVKAttachmentStoreOp(store_action, true);
desc.samples = vk::SampleCountFlagBits::e1;
color0_resolve_ = desc;
} else {
color0_resolve_ = std::nullopt;
}
} else {
resolves_.erase(index);
colors_[index] = desc;
if (performs_resolves) {
desc.storeOp = ToVKAttachmentStoreOp(store_action, true);
desc.samples = vk::SampleCountFlagBits::e1;
resolves_[index] = desc;
} else {
resolves_.erase(index);
}
}
return *this;
}
@ -100,8 +113,11 @@ vk::UniqueRenderPass RenderPassBuilderVK::Build(
const vk::Device& device) const {
// This must be less than `VkPhysicalDeviceLimits::maxColorAttachments` but we
// are not checking.
const auto color_attachments_count =
auto color_attachments_count =
colors_.empty() ? 0u : colors_.rbegin()->first + 1u;
if (color0_.has_value()) {
color_attachments_count++;
}
std::vector<vk::AttachmentDescription> attachments;
@ -111,6 +127,22 @@ vk::UniqueRenderPass RenderPassBuilderVK::Build(
kUnusedAttachmentReference);
vk::AttachmentReference depth_stencil_ref = kUnusedAttachmentReference;
if (color0_.has_value()) {
vk::AttachmentReference color_ref;
color_ref.attachment = attachments.size();
color_ref.layout = vk::ImageLayout::eGeneral;
color_refs[0] = color_ref;
attachments.push_back(color0_.value());
if (color0_resolve_.has_value()) {
vk::AttachmentReference resolve_ref;
resolve_ref.attachment = attachments.size();
resolve_ref.layout = vk::ImageLayout::eGeneral;
resolve_refs[0] = resolve_ref;
attachments.push_back(color0_resolve_.value());
}
}
for (const auto& color : colors_) {
vk::AttachmentReference color_ref;
color_ref.attachment = attachments.size();
@ -207,4 +239,16 @@ RenderPassBuilderVK::GetDepthStencil() const {
return depth_stencil_;
}
// Visible for testing.
std::optional<vk::AttachmentDescription> RenderPassBuilderVK::GetColor0()
const {
return color0_;
}
// Visible for testing.
std::optional<vk::AttachmentDescription> RenderPassBuilderVK::GetColor0Resolve()
const {
return color0_resolve_;
}
} // namespace impeller

View File

@ -8,6 +8,7 @@
#include <map>
#include <optional>
#include "flutter/fml/macros.h"
#include "impeller/core/formats.h"
#include "impeller/renderer/backend/vulkan/context_vk.h"
#include "impeller/renderer/backend/vulkan/vk.h"
@ -54,10 +55,20 @@ class RenderPassBuilderVK {
// Visible for testing.
const std::optional<vk::AttachmentDescription>& GetDepthStencil() const;
// Visible for testing.
std::optional<vk::AttachmentDescription> GetColor0() const;
// Visible for testing.
std::optional<vk::AttachmentDescription> GetColor0Resolve() const;
private:
std::optional<vk::AttachmentDescription> color0_;
std::optional<vk::AttachmentDescription> color0_resolve_;
std::optional<vk::AttachmentDescription> depth_stencil_;
// Color attachment 0 is stored in the field above and not in these maps.
std::map<size_t, vk::AttachmentDescription> colors_;
std::map<size_t, vk::AttachmentDescription> resolves_;
std::optional<vk::AttachmentDescription> depth_stencil_;
};
//------------------------------------------------------------------------------

View File

@ -40,9 +40,12 @@ TEST(RenderPassBuilder, RenderPassWithLoadOpUsesCurrentLayout) {
EXPECT_TRUE(!!render_pass);
auto maybe_color = builder.GetColorAttachments().find(0u);
ASSERT_NE(maybe_color, builder.GetColorAttachments().end());
auto color = maybe_color->second;
std::optional<vk::AttachmentDescription> maybe_color = builder.GetColor0();
ASSERT_TRUE(maybe_color.has_value());
if (!maybe_color.has_value()) {
return;
}
vk::AttachmentDescription color = maybe_color.value();
EXPECT_EQ(color.initialLayout, vk::ImageLayout::eColorAttachmentOptimal);
EXPECT_EQ(color.finalLayout, vk::ImageLayout::eGeneral);
@ -66,21 +69,25 @@ TEST(RenderPassBuilder, CreatesRenderPassWithCombinedDepthStencil) {
EXPECT_TRUE(!!render_pass);
auto maybe_color = builder.GetColorAttachments().find(0u);
ASSERT_NE(maybe_color, builder.GetColorAttachments().end());
auto color = maybe_color->second;
std::optional<vk::AttachmentDescription> maybe_color = builder.GetColor0();
ASSERT_TRUE(maybe_color.has_value());
if (!maybe_color.has_value()) {
return;
}
vk::AttachmentDescription color = maybe_color.value();
EXPECT_EQ(color.initialLayout, vk::ImageLayout::eUndefined);
EXPECT_EQ(color.finalLayout, vk::ImageLayout::eGeneral);
EXPECT_EQ(color.loadOp, vk::AttachmentLoadOp::eClear);
EXPECT_EQ(color.storeOp, vk::AttachmentStoreOp::eStore);
auto maybe_depth_stencil = builder.GetDepthStencil();
std::optional<vk::AttachmentDescription> maybe_depth_stencil =
builder.GetDepthStencil();
ASSERT_TRUE(maybe_depth_stencil.has_value());
if (!maybe_depth_stencil.has_value()) {
return;
}
auto depth_stencil = maybe_depth_stencil.value();
vk::AttachmentDescription depth_stencil = maybe_depth_stencil.value();
EXPECT_EQ(depth_stencil.initialLayout, vk::ImageLayout::eUndefined);
EXPECT_EQ(depth_stencil.finalLayout,
@ -106,12 +113,13 @@ TEST(RenderPassBuilder, CreatesRenderPassWithOnlyStencil) {
EXPECT_TRUE(!!render_pass);
auto maybe_depth_stencil = builder.GetDepthStencil();
std::optional<vk::AttachmentDescription> maybe_depth_stencil =
builder.GetDepthStencil();
ASSERT_TRUE(maybe_depth_stencil.has_value());
if (!maybe_depth_stencil.has_value()) {
return;
}
auto depth_stencil = maybe_depth_stencil.value();
vk::AttachmentDescription depth_stencil = maybe_depth_stencil.value();
EXPECT_EQ(depth_stencil.initialLayout, vk::ImageLayout::eUndefined);
EXPECT_EQ(depth_stencil.finalLayout,
@ -135,9 +143,12 @@ TEST(RenderPassBuilder, CreatesMSAAResolveWithCorrectStore) {
EXPECT_TRUE(!!render_pass);
auto maybe_color = builder.GetColorAttachments().find(0u);
ASSERT_NE(maybe_color, builder.GetColorAttachments().end());
auto color = maybe_color->second;
auto maybe_color = builder.GetColor0();
ASSERT_TRUE(maybe_color.has_value());
if (!maybe_color.has_value()) {
return;
}
vk::AttachmentDescription color = maybe_color.value();
// MSAA Texture.
EXPECT_EQ(color.initialLayout, vk::ImageLayout::eUndefined);
@ -145,9 +156,12 @@ TEST(RenderPassBuilder, CreatesMSAAResolveWithCorrectStore) {
EXPECT_EQ(color.loadOp, vk::AttachmentLoadOp::eClear);
EXPECT_EQ(color.storeOp, vk::AttachmentStoreOp::eDontCare);
auto maybe_resolve = builder.GetResolves().find(0u);
ASSERT_NE(maybe_resolve, builder.GetResolves().end());
auto resolve = maybe_resolve->second;
auto maybe_resolve = builder.GetColor0Resolve();
ASSERT_TRUE(maybe_resolve.has_value());
if (!maybe_resolve.has_value()) {
return;
}
vk::AttachmentDescription resolve = maybe_resolve.value();
// MSAA Resolve Texture.
EXPECT_EQ(resolve.initialLayout, vk::ImageLayout::eUndefined);

View File

@ -23,8 +23,7 @@ TEST_P(RendererTest, CachesRenderPassAndFramebuffer) {
auto render_target =
allocator->CreateOffscreenMSAA(*GetContext(), {100, 100}, 1);
auto resolve_texture =
render_target.GetColorAttachments().find(0u)->second.resolve_texture;
auto resolve_texture = render_target.GetColorAttachment(0).resolve_texture;
auto& texture_vk = TextureVK::Cast(*resolve_texture);
EXPECT_EQ(texture_vk.GetCachedFramebuffer(), nullptr);

View File

@ -56,12 +56,14 @@ static std::vector<vk::ClearValue> GetVKClearValues(
const RenderTarget& target) {
std::vector<vk::ClearValue> clears;
for (const auto& [_, color] : target.GetColorAttachments()) {
clears.emplace_back(VKClearValueFromColor(color.clear_color));
if (color.resolve_texture) {
clears.emplace_back(VKClearValueFromColor(color.clear_color));
}
}
target.IterateAllColorAttachments(
[&clears](size_t index, const ColorAttachment& attachment) -> bool {
clears.emplace_back(VKClearValueFromColor(attachment.clear_color));
if (attachment.resolve_texture) {
clears.emplace_back(VKClearValueFromColor(attachment.clear_color));
}
return true;
});
const auto& depth = target.GetDepthAttachment();
const auto& stencil = target.GetStencilAttachment();
@ -83,22 +85,24 @@ SharedHandleVK<vk::RenderPass> RenderPassVK::CreateVKRenderPass(
const std::shared_ptr<CommandBufferVK>& command_buffer) const {
RenderPassBuilderVK builder;
for (const auto& [bind_point, color] : render_target_.GetColorAttachments()) {
builder.SetColorAttachment(
bind_point, //
color.texture->GetTextureDescriptor().format, //
color.texture->GetTextureDescriptor().sample_count, //
color.load_action, //
color.store_action, //
TextureVK::Cast(*color.texture).GetLayout() //
);
TextureVK::Cast(*color.texture)
.SetLayoutWithoutEncoding(vk::ImageLayout::eGeneral);
if (color.resolve_texture) {
TextureVK::Cast(*color.resolve_texture)
.SetLayoutWithoutEncoding(vk::ImageLayout::eGeneral);
}
}
render_target_.IterateAllColorAttachments(
[&](size_t bind_point, const ColorAttachment& attachment) -> bool {
builder.SetColorAttachment(
bind_point, //
attachment.texture->GetTextureDescriptor().format, //
attachment.texture->GetTextureDescriptor().sample_count, //
attachment.load_action, //
attachment.store_action, //
TextureVK::Cast(*attachment.texture).GetLayout() //
);
TextureVK::Cast(*attachment.texture)
.SetLayoutWithoutEncoding(vk::ImageLayout::eGeneral);
if (attachment.resolve_texture) {
TextureVK::Cast(*attachment.resolve_texture)
.SetLayoutWithoutEncoding(vk::ImageLayout::eGeneral);
}
return true;
});
if (auto depth = render_target_.GetDepthAttachment(); depth.has_value()) {
builder.SetDepthStencilAttachment(
@ -137,10 +141,9 @@ RenderPassVK::RenderPassVK(const std::shared_ptr<const Context>& context,
const RenderTarget& target,
std::shared_ptr<CommandBufferVK> command_buffer)
: RenderPass(context, target), command_buffer_(std::move(command_buffer)) {
color_image_vk_ =
render_target_.GetColorAttachments().find(0u)->second.texture;
resolve_image_vk_ =
render_target_.GetColorAttachments().find(0u)->second.resolve_texture;
const ColorAttachment& color0 = render_target_.GetColorAttachment(0);
color_image_vk_ = color0.texture;
resolve_image_vk_ = color0.resolve_texture;
const auto& vk_context = ContextVK::Cast(*context);
command_buffer_vk_ = command_buffer_->GetCommandBuffer();
@ -254,16 +257,19 @@ SharedHandleVK<vk::Framebuffer> RenderPassVK::CreateVKFramebuffer(
// This bit must be consistent to ensure compatibility with the pass created
// earlier. Follow this order: Color attachments, then depth-stencil, then
// stencil.
for (const auto& [_, color] : render_target_.GetColorAttachments()) {
// The bind point doesn't matter here since that information is present in
// the render pass.
attachments.emplace_back(
TextureVK::Cast(*color.texture).GetRenderTargetView());
if (color.resolve_texture) {
attachments.emplace_back(
TextureVK::Cast(*color.resolve_texture).GetRenderTargetView());
}
}
render_target_.IterateAllColorAttachments(
[&attachments](size_t index, const ColorAttachment& attachment) -> bool {
// The bind point doesn't matter here since that information is present
// in the render pass.
attachments.emplace_back(
TextureVK::Cast(*attachment.texture).GetRenderTargetView());
if (attachment.resolve_texture) {
attachments.emplace_back(TextureVK::Cast(*attachment.resolve_texture)
.GetRenderTargetView());
}
return true;
});
if (auto depth = render_target_.GetDepthAttachment(); depth.has_value()) {
attachments.emplace_back(
TextureVK::Cast(*depth->texture).GetRenderTargetView());

View File

@ -86,8 +86,7 @@ TEST(SwapchainTest, CachesRenderPassOnSwapchainImage) {
auto& depth = render_target.GetDepthAttachment();
depth_stencil_textures.push_back(depth.has_value() ? depth->texture
: nullptr);
msaa_textures.push_back(
render_target.GetColorAttachments().find(0u)->second.texture);
msaa_textures.push_back(render_target.GetColorAttachment(0).texture);
}
for (auto i = 1; i < 3; i++) {

View File

@ -28,6 +28,7 @@ bool RenderTarget::IsValid() const {
return false;
}
#ifndef NDEBUG
// Validate that all attachments are of the same size.
{
std::optional<ISize> size;
@ -87,12 +88,34 @@ bool RenderTarget::IsValid() const {
return false;
}
}
#endif // NDEBUG
return true;
}
bool RenderTarget::IterateAllColorAttachments(
const std::function<bool(size_t index, const ColorAttachment& attachment)>&
iterator) const {
if (color0_.has_value()) {
if (!iterator(0, color0_.value())) {
return false;
}
}
for (const auto& [index, attachment] : colors_) {
if (!iterator(index, attachment)) {
return false;
}
}
return true;
}
void RenderTarget::IterateAllAttachments(
const std::function<bool(const Attachment& attachment)>& iterator) const {
if (color0_.has_value()) {
if (!iterator(color0_.value())) {
return;
}
}
for (const auto& color : colors_) {
if (!iterator(color.second)) {
return;
@ -113,13 +136,16 @@ void RenderTarget::IterateAllAttachments(
}
SampleCount RenderTarget::GetSampleCount() const {
if (auto found = colors_.find(0u); found != colors_.end()) {
return found->second.texture->GetTextureDescriptor().sample_count;
if (color0_.has_value()) {
return color0_.value().texture->GetTextureDescriptor().sample_count;
}
return SampleCount::kCount1;
}
bool RenderTarget::HasColorAttachment(size_t index) const {
if (index == 0u) {
return color0_.has_value();
}
if (auto found = colors_.find(index); found != colors_.end()) {
return true;
}
@ -127,6 +153,12 @@ bool RenderTarget::HasColorAttachment(size_t index) const {
}
std::optional<ISize> RenderTarget::GetColorAttachmentSize(size_t index) const {
if (index == 0u) {
if (color0_.has_value()) {
return color0_.value().texture->GetSize();
}
return std::nullopt;
}
auto found = colors_.find(index);
if (found == colors_.end()) {
@ -142,12 +174,10 @@ ISize RenderTarget::GetRenderTargetSize() const {
}
std::shared_ptr<Texture> RenderTarget::GetRenderTargetTexture() const {
auto found = colors_.find(0u);
if (found == colors_.end()) {
if (!color0_.has_value()) {
return nullptr;
}
return found->second.resolve_texture ? found->second.resolve_texture
: found->second.texture;
return color0_->resolve_texture ? color0_->resolve_texture : color0_->texture;
}
PixelFormat RenderTarget::GetRenderTargetPixelFormat() const {
@ -169,7 +199,12 @@ size_t RenderTarget::GetMaxColorAttacmentBindIndex() const {
RenderTarget& RenderTarget::SetColorAttachment(
const ColorAttachment& attachment,
size_t index) {
if (attachment.IsValid()) {
if (!attachment.IsValid()) {
return *this;
}
if (index == 0u) {
color0_ = attachment;
} else {
colors_[index] = attachment;
}
return *this;
@ -195,9 +230,18 @@ RenderTarget& RenderTarget::SetStencilAttachment(
return *this;
}
const std::map<size_t, ColorAttachment>& RenderTarget::GetColorAttachments()
const {
return colors_;
ColorAttachment RenderTarget::GetColorAttachment(size_t index) const {
if (index == 0) {
if (color0_.has_value()) {
return color0_.value();
}
return ColorAttachment{};
}
std::map<size_t, ColorAttachment>::const_iterator it = colors_.find(index);
if (it != colors_.end()) {
return it->second;
}
return ColorAttachment{};
}
const std::optional<DepthAttachment>& RenderTarget::GetDepthAttachment() const {
@ -219,6 +263,9 @@ size_t RenderTarget::GetTotalAttachmentCount() const {
count++;
}
}
if (color0_.has_value()) {
count++;
}
if (depth_.has_value()) {
count++;
}
@ -231,6 +278,10 @@ size_t RenderTarget::GetTotalAttachmentCount() const {
std::string RenderTarget::ToString() const {
std::stringstream stream;
if (color0_.has_value()) {
stream << SPrintF("Color[%d]=(%s)", 0,
ColorAttachmentToString(color0_.value()).c_str());
}
for (const auto& [index, color] : colors_) {
stream << SPrintF("Color[%zu]=(%s)", index,
ColorAttachmentToString(color).c_str());
@ -248,6 +299,18 @@ std::string RenderTarget::ToString() const {
return stream.str();
}
RenderTargetConfig RenderTarget::ToConfig() const {
if (!color0_.has_value()) {
return RenderTargetConfig{};
}
const auto& color_attachment = color0_.value();
return RenderTargetConfig{
.size = color_attachment.texture->GetSize(),
.mip_count = color_attachment.texture->GetMipCount(),
.has_msaa = color_attachment.resolve_texture != nullptr,
.has_depth_stencil = depth_.has_value() && stencil_.has_value()};
}
RenderTargetAllocator::RenderTargetAllocator(
std::shared_ptr<Allocator> allocator)
: allocator_(std::move(allocator)) {}

View File

@ -102,6 +102,13 @@ class RenderTarget final {
RenderTarget& SetColorAttachment(const ColorAttachment& attachment,
size_t index);
/// @brief Get the color attachment at [index].
///
/// This function does not validate whether or not the attachment was
/// previously defined and will return a default constructed attachment
/// if none is set.
ColorAttachment GetColorAttachment(size_t index) const;
RenderTarget& SetDepthAttachment(std::optional<DepthAttachment> attachment);
RenderTarget& SetStencilAttachment(
@ -109,32 +116,32 @@ class RenderTarget final {
size_t GetMaxColorAttacmentBindIndex() const;
const std::map<size_t, ColorAttachment>& GetColorAttachments() const;
const std::optional<DepthAttachment>& GetDepthAttachment() const;
const std::optional<StencilAttachment>& GetStencilAttachment() const;
size_t GetTotalAttachmentCount() const;
bool IterateAllColorAttachments(
const std::function<bool(size_t index,
const ColorAttachment& attachment)>& iterator)
const;
void IterateAllAttachments(
const std::function<bool(const Attachment& attachment)>& iterator) const;
std::string ToString() const;
RenderTargetConfig ToConfig() const {
auto& color_attachment = GetColorAttachments().find(0)->second;
return RenderTargetConfig{
.size = color_attachment.texture->GetSize(),
.mip_count = color_attachment.texture->GetMipCount(),
.has_msaa = color_attachment.resolve_texture != nullptr,
.has_depth_stencil = depth_.has_value() && stencil_.has_value()};
}
RenderTargetConfig ToConfig() const;
private:
std::map<size_t, ColorAttachment> colors_;
std::optional<ColorAttachment> color0_;
std::optional<DepthAttachment> depth_;
std::optional<StencilAttachment> stencil_;
// Note: color0 is stored in the color0_ field above and not in this map,
// to avoid heap allocations for the commonly created render target formats
// in Flutter.
std::map<size_t, ColorAttachment> colors_;
};
/// @brief a wrapper around the impeller [Allocator] instance that can be used

View File

@ -589,10 +589,6 @@ TEST_P(RendererTest, CanBlitTextureToTexture) {
}
pass->SetLabel("Playground Blit Pass");
if (render_target.GetColorAttachments().empty()) {
return false;
}
// Blit `bridge` to the top left corner of the texture.
pass->AddCopy(bridge, texture);
@ -709,10 +705,6 @@ TEST_P(RendererTest, CanBlitTextureToBuffer) {
}
pass->SetLabel("Playground Blit Pass");
if (render_target.GetColorAttachments().empty()) {
return false;
}
// Blit `bridge` to the top left corner of the texture.
pass->AddCopy(bridge, device_buffer);
pass->EncodeCommands(context->GetResourceAllocator());

View File

@ -105,10 +105,13 @@ RenderPass::GetOrCreatePipeline() {
pipeline_desc.SetSampleCount(render_target_.GetSampleCount());
for (const auto& it : render_target_.GetColorAttachments()) {
auto& color = GetColorAttachmentDescriptor(it.first);
color.format = render_target_.GetRenderTargetPixelFormat();
}
render_target_.IterateAllColorAttachments(
[&](size_t index, const impeller::ColorAttachment& attachment) -> bool {
auto& color = GetColorAttachmentDescriptor(index);
color.format = render_target_.GetRenderTargetPixelFormat();
return true;
});
pipeline_desc.SetColorAttachmentDescriptors(color_descriptors_);
{