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

[Impeller] Prevent passes from reading their own resolve texture on the Vulkan and GL backends #40418

Merged
merged 3 commits into from
Mar 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions ci/licenses_golden/licenses_flutter
Original file line number Diff line number Diff line change
Expand Up @@ -1193,6 +1193,8 @@ ORIGIN: ../../../flutter/impeller/entity/entity_pass.cc + ../../../flutter/LICEN
ORIGIN: ../../../flutter/impeller/entity/entity_pass.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/entity_pass_delegate.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/entity_pass_delegate.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/entity_pass_target.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/entity_pass_target.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/entity_playground.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/entity_playground.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/geometry.cc + ../../../flutter/LICENSE
Expand Down Expand Up @@ -3726,6 +3728,8 @@ FILE: ../../../flutter/impeller/entity/entity_pass.cc
FILE: ../../../flutter/impeller/entity/entity_pass.h
FILE: ../../../flutter/impeller/entity/entity_pass_delegate.cc
FILE: ../../../flutter/impeller/entity/entity_pass_delegate.h
FILE: ../../../flutter/impeller/entity/entity_pass_target.cc
FILE: ../../../flutter/impeller/entity/entity_pass_target.h
FILE: ../../../flutter/impeller/entity/entity_playground.cc
FILE: ../../../flutter/impeller/entity/entity_playground.h
FILE: ../../../flutter/impeller/entity/geometry.cc
Expand Down
2 changes: 2 additions & 0 deletions impeller/entity/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,8 @@ impeller_component("entity") {
"entity_pass.h",
"entity_pass_delegate.cc",
"entity_pass_delegate.h",
"entity_pass_target.cc",
"entity_pass_target.h",
"geometry.cc",
"geometry.h",
"inline_pass_context.cc",
Expand Down
83 changes: 50 additions & 33 deletions impeller/entity/entity_pass.cc
Original file line number Diff line number Diff line change
Expand Up @@ -142,18 +142,19 @@ EntityPass* EntityPass::AddSubpass(std::unique_ptr<EntityPass> pass) {
return subpass_pointer;
}

static RenderTarget CreateRenderTarget(ContentContext& renderer,
ISize size,
bool readable) {
static EntityPassTarget CreateRenderTarget(ContentContext& renderer,
ISize size,
bool readable) {
auto context = renderer.GetContext();

/// All of the load/store actions are managed by `InlinePassContext` when
/// `RenderPasses` are created, so we just set them to `kDontCare` here.
/// What's important is the `StorageMode` of the textures, which cannot be
/// changed for the lifetime of the textures.

RenderTarget target;
if (context->GetDeviceCapabilities().SupportsOffscreenMSAA()) {
return RenderTarget::CreateOffscreenMSAA(
target = RenderTarget::CreateOffscreenMSAA(
*context, // context
size, // size
"EntityPass", // label
Expand All @@ -170,24 +171,27 @@ static RenderTarget CreateRenderTarget(ContentContext& renderer,
.store_action = StoreAction::kDontCare,
} // stencil_attachment_config
);
} else {
target = RenderTarget::CreateOffscreen(
*context, // context
size, // size
"EntityPass", // label
RenderTarget::AttachmentConfig{
.storage_mode = StorageMode::kDevicePrivate,
.load_action = LoadAction::kDontCare,
.store_action = StoreAction::kDontCare,
}, // color_attachment_config
RenderTarget::AttachmentConfig{
.storage_mode = readable ? StorageMode::kDevicePrivate
: StorageMode::kDeviceTransient,
.load_action = LoadAction::kDontCare,
.store_action = StoreAction::kDontCare,
} // stencil_attachment_config
);
}

return RenderTarget::CreateOffscreen(
*context, // context
size, // size
"EntityPass", // label
RenderTarget::AttachmentConfig{
.storage_mode = StorageMode::kDevicePrivate,
.load_action = LoadAction::kDontCare,
.store_action = StoreAction::kDontCare,
}, // color_attachment_config
RenderTarget::AttachmentConfig{
.storage_mode = readable ? StorageMode::kDevicePrivate
: StorageMode::kDeviceTransient,
.load_action = LoadAction::kDontCare,
.store_action = StoreAction::kDontCare,
} // stencil_attachment_config
);
return EntityPassTarget(
target, renderer.GetDeviceCapabilities().SupportsReadFromResolve());
}

uint32_t EntityPass::GetTotalPassReads(ContentContext& renderer) const {
Expand All @@ -202,7 +206,8 @@ bool EntityPass::Render(ContentContext& renderer,
if (GetTotalPassReads(renderer) > 0) {
auto offscreen_target =
CreateRenderTarget(renderer, render_target.GetRenderTargetSize(), true);
if (!OnRender(renderer, offscreen_target.GetRenderTargetSize(),
if (!OnRender(renderer,
offscreen_target.GetRenderTarget().GetRenderTargetSize(),
offscreen_target, Point(), Point(), 0)) {
return false;
}
Expand All @@ -215,8 +220,9 @@ bool EntityPass::Render(ContentContext& renderer,
.SupportsTextureToTextureBlits()) {
auto blit_pass = command_buffer->CreateBlitPass();

blit_pass->AddCopy(offscreen_target.GetRenderTargetTexture(),
render_target.GetRenderTargetTexture());
blit_pass->AddCopy(
offscreen_target.GetRenderTarget().GetRenderTargetTexture(),
render_target.GetRenderTargetTexture());

if (!blit_pass->EncodeCommands(
renderer.GetContext()->GetResourceAllocator())) {
Expand All @@ -227,9 +233,11 @@ bool EntityPass::Render(ContentContext& renderer,
render_pass->SetLabel("EntityPass Root Render Pass");

{
auto size_rect = Rect::MakeSize(offscreen_target.GetRenderTargetSize());
auto size_rect = Rect::MakeSize(
offscreen_target.GetRenderTarget().GetRenderTargetSize());
auto contents = TextureContents::MakeRect(size_rect);
contents->SetTexture(offscreen_target.GetRenderTargetTexture());
contents->SetTexture(
offscreen_target.GetRenderTarget().GetRenderTargetTexture());
contents->SetSourceRect(size_rect);

Entity entity;
Expand All @@ -250,7 +258,11 @@ bool EntityPass::Render(ContentContext& renderer,
return true;
}

return OnRender(renderer, render_target.GetRenderTargetSize(), render_target,
EntityPassTarget pass_target(
render_target,
renderer.GetDeviceCapabilities().SupportsReadFromResolve());

return OnRender(renderer, render_target.GetRenderTargetSize(), pass_target,
Point(), Point(), 0);
}

Expand Down Expand Up @@ -296,7 +308,7 @@ EntityPass::EntityResult EntityPass::GetEntityForElement(
subpass->delegate_->CanCollapseIntoParentPass(subpass)) {
// Directly render into the parent target and move on.
if (!subpass->OnRender(renderer, root_pass_size,
pass_context.GetRenderTarget(), position, position,
pass_context.GetPassTarget(), position, position,
pass_depth, stencil_depth_, nullptr,
pass_context.GetRenderPass(pass_depth))) {
return EntityPass::EntityResult::Failure();
Expand All @@ -322,8 +334,9 @@ EntityPass::EntityResult EntityPass::GetEntityForElement(
auto subpass_coverage =
GetSubpassCoverage(*subpass, Rect::MakeSize(root_pass_size));
if (subpass->cover_whole_screen_) {
subpass_coverage = Rect(
position, Size(pass_context.GetRenderTarget().GetRenderTargetSize()));
subpass_coverage = Rect(position, Size(pass_context.GetPassTarget()
.GetRenderTarget()
.GetRenderTargetSize()));
}
if (backdrop_filter_contents) {
auto backdrop_coverage = backdrop_filter_contents->GetCoverage(Entity{});
Expand All @@ -350,7 +363,8 @@ EntityPass::EntityResult EntityPass::GetEntityForElement(
ISize(subpass_coverage->size), //
subpass->GetTotalPassReads(renderer) > 0);

auto subpass_texture = subpass_target.GetRenderTargetTexture();
auto subpass_texture =
subpass_target.GetRenderTarget().GetRenderTargetTexture();

if (!subpass_texture) {
return EntityPass::EntityResult::Failure();
Expand Down Expand Up @@ -400,7 +414,7 @@ struct StencilLayer {

bool EntityPass::OnRender(ContentContext& renderer,
ISize root_pass_size,
const RenderTarget& render_target,
EntityPassTarget& pass_target,
Point position,
Point parent_position,
uint32_t pass_depth,
Expand All @@ -411,15 +425,16 @@ bool EntityPass::OnRender(ContentContext& renderer,
TRACE_EVENT0("impeller", "EntityPass::OnRender");

auto context = renderer.GetContext();
InlinePassContext pass_context(context, render_target,
InlinePassContext pass_context(context, pass_target,
GetTotalPassReads(renderer),
std::move(collapsed_parent_pass));
if (!pass_context.IsValid()) {
return false;
}

std::vector<StencilLayer> stencil_stack = {StencilLayer{
.coverage = Rect::MakeSize(render_target.GetRenderTargetSize()),
.coverage =
Rect::MakeSize(pass_target.GetRenderTarget().GetRenderTargetSize()),
.stencil_depth = stencil_depth_floor}};

auto render_element = [&stencil_depth_floor, &pass_context, &pass_depth,
Expand All @@ -433,6 +448,8 @@ bool EntityPass::OnRender(ContentContext& renderer,
// If the pass context returns a texture, we need to draw it to the current
// pass. We do this because it's faster and takes significantly less memory
// than storing/loading large MSAA textures.
// Also, it's not possible to blit the non-MSAA resolve texture of the
// previous pass to MSAA textures (let alone a transient one).
if (result.backdrop_texture) {
auto size_rect = Rect::MakeSize(result.pass->GetRenderTargetSize());
auto msaa_backdrop_contents = TextureContents::MakeRect(size_rect);
Expand Down
3 changes: 2 additions & 1 deletion impeller/entity/entity_pass.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "impeller/entity/entity_pass_delegate.h"
#include "impeller/entity/inline_pass_context.h"
#include "impeller/renderer/render_target.h"
#include "impeller/renderer/texture.h"
#include "impeller/typographer/lazy_glyph_atlas.h"

namespace impeller {
Expand Down Expand Up @@ -111,7 +112,7 @@ class EntityPass {

bool OnRender(ContentContext& renderer,
ISize root_pass_size,
const RenderTarget& render_target,
EntityPassTarget& render_target,
Point position,
Point parent_position,
uint32_t pass_depth,
Expand Down
55 changes: 55 additions & 0 deletions impeller/entity/entity_pass_target.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// 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/entity_pass_target.h"

#include "impeller/renderer/texture.h"

namespace impeller {

EntityPassTarget::EntityPassTarget(const RenderTarget& render_target,
bool supports_read_from_resolve)
: target_(render_target),
supports_read_from_resolve_(supports_read_from_resolve) {}

std::shared_ptr<Texture> EntityPassTarget::Flip(Allocator& allocator) {
auto color0 = target_.GetColorAttachments().find(0)->second;

if (supports_read_from_resolve_ && color0.resolve_texture) {
// Just return the current resolve texture, which is safe to read in the
// next render pass that'll resolve to `target_`.
//
// Note that this can only be done when MSAA is being used.
return color0.resolve_texture;
}

if (!secondary_color_texture_) {
// The second texture is allocated lazily to avoid unused allocations.
TextureDescriptor new_descriptor = color0.texture->GetTextureDescriptor();
secondary_color_texture_ = allocator.CreateTexture(new_descriptor);

if (!secondary_color_texture_) {
return nullptr;
}
}

std::swap(color0.resolve_texture ? color0.resolve_texture : color0.texture,
secondary_color_texture_);

target_.SetColorAttachment(color0, 0);

// Return the previous backdrop texture, which is safe to read in the next
// render pass that attaches `target_`.
return secondary_color_texture_;
}

const RenderTarget& EntityPassTarget::GetRenderTarget() const {
return target_;
}

bool EntityPassTarget::IsValid() const {
return !target_.GetColorAttachments().empty();
}

} // namespace impeller
43 changes: 43 additions & 0 deletions impeller/entity/entity_pass_target.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// 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.

#pragma once

#include "fml/macros.h"
#include "impeller/renderer/render_target.h"

namespace impeller {

class InlinePassContext;

class EntityPassTarget {
public:
explicit EntityPassTarget(const RenderTarget& render_target,
bool supports_read_from_resolve);

/// @brief Flips the backdrop and returns a readable texture that can be
/// bound/sampled to restore the previous pass.
///
/// After this method is called, a new `RenderPass` that attaches the
/// result of `GetRenderTarget` is guaranteed to be able to read the
/// previous pass's backdrop texture (which is returned by this
/// method).
std::shared_ptr<Texture> Flip(Allocator& allocator);

const RenderTarget& GetRenderTarget() const;

bool IsValid() const;

private:
RenderTarget target_;
std::shared_ptr<Texture> secondary_color_texture_;

bool supports_read_from_resolve_;

friend InlinePassContext;

FML_DISALLOW_ASSIGN(EntityPassTarget);
};

} // namespace impeller
Loading