[Impeller] Add backdrop filter support; refactor EntityPass (flutter/engine#33887)

This commit is contained in:
Brandon DeRosier 2022-06-15 12:43:02 -07:00 committed by GitHub
parent 34246f2872
commit 2ededef19a
10 changed files with 508 additions and 214 deletions

View File

@ -568,6 +568,8 @@ FILE: ../../../flutter/impeller/entity/entity_pass_delegate.h
FILE: ../../../flutter/impeller/entity/entity_playground.cc
FILE: ../../../flutter/impeller/entity/entity_playground.h
FILE: ../../../flutter/impeller/entity/entity_unittests.cc
FILE: ../../../flutter/impeller/entity/inline_pass_context.cc
FILE: ../../../flutter/impeller/entity/inline_pass_context.h
FILE: ../../../flutter/impeller/entity/shaders/blending/advanced_blend.glsl
FILE: ../../../flutter/impeller/entity/shaders/blending/advanced_blend.vert
FILE: ../../../flutter/impeller/entity/shaders/blending/advanced_blend_color.frag

View File

@ -265,14 +265,17 @@ size_t Canvas::GetStencilDepth() const {
return xformation_stack_.back().stencil_depth;
}
void Canvas::SaveLayer(Paint paint, std::optional<Rect> bounds) {
void Canvas::SaveLayer(Paint paint,
std::optional<Rect> bounds,
std::optional<Paint::ImageFilterProc> backdrop_filter) {
Save(true, paint.blend_mode);
auto& new_layer_pass = GetCurrentPass();
new_layer_pass.SetDelegate(
std::make_unique<PaintPassDelegate>(paint, bounds));
new_layer_pass.SetBackdropFilter(backdrop_filter);
if (bounds.has_value()) {
if (bounds.has_value() && !backdrop_filter.has_value()) {
// Render target switches due to a save layer can be elided. In such cases
// where passes are collapsed into their parent, the clipping effect to
// the size of the render target that would have been allocated will be

View File

@ -35,7 +35,10 @@ class Canvas {
void Save();
void SaveLayer(Paint paint, std::optional<Rect> bounds = std::nullopt);
void SaveLayer(
Paint paint,
std::optional<Rect> bounds = std::nullopt,
std::optional<Paint::ImageFilterProc> backdrop_filter = std::nullopt);
bool Restore();

View File

@ -324,9 +324,12 @@ void DisplayListDispatcher::setMaskFilter(const flutter::DlMaskFilter* filter) {
}
}
// |flutter::Dispatcher|
void DisplayListDispatcher::setImageFilter(
static std::optional<Paint::ImageFilterProc> ToImageFilterProc(
const flutter::DlImageFilter* filter) {
if (filter == nullptr) {
return std::nullopt;
}
switch (filter->type()) {
case flutter::DlImageFilterType::kBlur: {
auto blur = filter->asBlur();
@ -338,7 +341,7 @@ void DisplayListDispatcher::setImageFilter(
UNIMPLEMENTED;
}
paint_.image_filter = [sigma_x, sigma_y](FilterInput::Ref input) {
return [sigma_x, sigma_y](FilterInput::Ref input) {
return FilterContents::MakeGaussianBlur(input, sigma_x, sigma_y);
};
@ -350,11 +353,16 @@ void DisplayListDispatcher::setImageFilter(
case flutter::DlImageFilterType::kComposeFilter:
case flutter::DlImageFilterType::kColorFilter:
case flutter::DlImageFilterType::kUnknown:
UNIMPLEMENTED;
break;
return std::nullopt;
}
}
// |flutter::Dispatcher|
void DisplayListDispatcher::setImageFilter(
const flutter::DlImageFilter* filter) {
paint_.image_filter = ToImageFilterProc(filter);
}
// |flutter::Dispatcher|
void DisplayListDispatcher::save() {
canvas_.Save();
@ -371,11 +379,8 @@ static std::optional<Rect> ToRect(const SkRect* rect) {
void DisplayListDispatcher::saveLayer(const SkRect* bounds,
const flutter::SaveLayerOptions options,
const flutter::DlImageFilter* backdrop) {
if (backdrop) {
UNIMPLEMENTED;
}
canvas_.SaveLayer(options.renders_with_attributes() ? paint_ : Paint{},
ToRect(bounds));
auto paint = options.renders_with_attributes() ? paint_ : Paint{};
canvas_.SaveLayer(paint, ToRect(bounds), ToImageFilterProc(backdrop));
}
// |flutter::Dispatcher|

View File

@ -3,8 +3,10 @@
// found in the LICENSE file.
#include "display_list/display_list_blend_mode.h"
#include "display_list/display_list_color.h"
#include "display_list/display_list_color_filter.h"
#include "display_list/display_list_image_filter.h"
#include "display_list/display_list_paint.h"
#include "display_list/display_list_tile_mode.h"
#include "gtest/gtest.h"
#include "third_party/imgui/imgui.h"
@ -285,5 +287,64 @@ TEST_P(DisplayListTest, CanDrawWithImageBlurFilter) {
ASSERT_TRUE(OpenPlaygroundHere(callback));
}
TEST_P(DisplayListTest, CanDrawBackdropFilter) {
auto texture = CreateTextureForFixture("embarcadero.jpg");
bool first_frame = true;
auto callback = [&]() {
if (first_frame) {
first_frame = false;
ImGui::SetNextWindowSize({400, 100});
ImGui::SetNextWindowPos({300, 650});
}
static float sigma[] = {10, 10};
static bool use_bounds = true;
static bool draw_circle = true;
ImGui::Begin("Controls");
ImGui::SliderFloat2("Sigma", sigma, 0, 100);
ImGui::Checkbox("Use SaveLayer bounds", &use_bounds);
ImGui::Checkbox("Draw child element", &draw_circle);
ImGui::End();
flutter::DisplayListBuilder builder;
Vector2 scale = GetContentScale();
builder.scale(scale.x, scale.y);
auto filter = flutter::DlBlurImageFilter(sigma[0], sigma[1],
flutter::DlTileMode::kClamp);
std::optional<SkRect> bounds;
if (use_bounds) {
auto [p1, p2] = IMPELLER_PLAYGROUND_LINE(
Point(250, 150), Point(800, 600), 20, Color::White(), Color::White());
bounds = SkRect::MakeLTRB(p1.x, p1.y, p2.x, p2.y);
}
builder.drawImage(DlImageImpeller::Make(texture), SkPoint::Make(200, 200),
SkSamplingOptions{}, true);
builder.saveLayer(bounds.has_value() ? &bounds.value() : nullptr, nullptr,
&filter);
if (draw_circle) {
auto circle_center =
IMPELLER_PLAYGROUND_POINT(Point(500, 400), 20, Color::Red());
builder.setStyle(flutter::DlDrawStyle::kStroke);
builder.setStrokeCap(flutter::DlStrokeCap::kButt);
builder.setStrokeJoin(flutter::DlStrokeJoin::kBevel);
builder.setStrokeWidth(10);
builder.setColor(flutter::DlColor::kRed().withAlpha(100));
builder.drawCircle({circle_center.x, circle_center.y}, 100);
}
return builder.Build();
};
ASSERT_TRUE(OpenPlaygroundHere(callback));
}
} // namespace testing
} // namespace impeller

View File

@ -87,6 +87,8 @@ impeller_component("entity") {
"entity_pass.h",
"entity_pass_delegate.cc",
"entity_pass_delegate.h",
"inline_pass_context.cc",
"inline_pass_context.h",
]
public_deps = [

View File

@ -4,15 +4,18 @@
#include "impeller/entity/entity_pass.h"
#include <memory>
#include <variant>
#include "flutter/fml/logging.h"
#include "flutter/fml/macros.h"
#include "flutter/fml/trace_event.h"
#include "impeller/base/validation.h"
#include "impeller/entity/contents/content_context.h"
#include "impeller/entity/contents/filters/filter_contents.h"
#include "impeller/entity/contents/filters/inputs/filter_input.h"
#include "impeller/entity/contents/texture_contents.h"
#include "impeller/entity/inline_pass_context.h"
#include "impeller/geometry/path_builder.h"
#include "impeller/renderer/allocator.h"
#include "impeller/renderer/command.h"
@ -36,7 +39,7 @@ void EntityPass::SetDelegate(std::unique_ptr<EntityPassDelegate> delegate) {
void EntityPass::AddEntity(Entity entity) {
if (entity.GetBlendMode() > Entity::BlendMode::kLastPipelineBlendMode) {
contains_advanced_blends_ = true;
reads_from_pass_texture_ = true;
}
elements_.emplace_back(std::move(entity));
@ -122,8 +125,9 @@ EntityPass* EntityPass::AddSubpass(std::unique_ptr<EntityPass> pass) {
FML_DCHECK(pass->superpass_ == nullptr);
pass->superpass_ = this;
if (pass->blend_mode_ > Entity::BlendMode::kLastPipelineBlendMode) {
contains_advanced_blends_ = true;
if (pass->blend_mode_ > Entity::BlendMode::kLastPipelineBlendMode ||
pass->backdrop_filter_proc_.has_value()) {
reads_from_pass_texture_ = true;
}
auto subpass_pointer = pass.get();
@ -133,13 +137,13 @@ EntityPass* EntityPass::AddSubpass(std::unique_ptr<EntityPass> pass) {
bool EntityPass::Render(ContentContext& renderer,
RenderTarget render_target) const {
if (contains_advanced_blends_) {
if (reads_from_pass_texture_) {
auto offscreen_target = RenderTarget::CreateOffscreen(
*renderer.GetContext(), render_target.GetRenderTargetSize(),
"EntityPass", //
StorageMode::kDevicePrivate, LoadAction::kClear, StoreAction::kStore,
StorageMode::kDevicePrivate, LoadAction::kClear, StoreAction::kStore);
if (!RenderInternal(renderer, offscreen_target, Point(), 0)) {
if (!OnRender(renderer, offscreen_target, Point(), Point(), 0)) {
return false;
}
@ -174,231 +178,252 @@ bool EntityPass::Render(ContentContext& renderer,
return true;
}
return RenderInternal(renderer, render_target, Point(), 0);
return OnRender(renderer, render_target, Point(), Point(), 0);
}
bool EntityPass::RenderInternal(ContentContext& renderer,
RenderTarget render_target,
Point position,
uint32_t pass_depth,
size_t stencil_depth_floor) const {
TRACE_EVENT0("impeller", "EntityPass::Render");
EntityPass::EntityResult EntityPass::GetEntityForElement(
const EntityPass::Element& element,
ContentContext& renderer,
InlinePassContext& pass_context,
Point position,
uint32_t pass_depth,
size_t stencil_depth_floor) const {
Entity element_entity;
//--------------------------------------------------------------------------
/// Setup entity element.
///
if (const auto& entity = std::get_if<Entity>(&element)) {
element_entity = *entity;
if (!position.IsZero()) {
// If the pass image is going to be rendered with a non-zero position,
// apply the negative translation to entity copies before rendering them
// so that they'll end up rendering to the correct on-screen position.
element_entity.SetTransformation(
Matrix::MakeTranslation(Vector3(-position)) *
element_entity.GetTransformation());
}
}
//--------------------------------------------------------------------------
/// Setup subpass element.
///
else if (const auto& subpass_ptr =
std::get_if<std::unique_ptr<EntityPass>>(&element)) {
auto subpass = subpass_ptr->get();
if (subpass->delegate_->CanElide()) {
return EntityPass::EntityResult::Skip();
}
if (subpass->delegate_->CanCollapseIntoParentPass() &&
!subpass->backdrop_filter_proc_.has_value()) {
// Directly render into the parent target and move on.
if (!subpass->OnRender(renderer, pass_context.GetRenderTarget(), position,
position, stencil_depth_floor)) {
return EntityPass::EntityResult::Failure();
}
return EntityPass::EntityResult::Skip();
}
std::shared_ptr<Contents> backdrop_contents = nullptr;
if (subpass->backdrop_filter_proc_.has_value()) {
auto texture = pass_context.GetTexture();
// Render the backdrop texture before any of the pass elements.
const auto& proc = subpass->backdrop_filter_proc_.value();
backdrop_contents = proc(FilterInput::Make(std::move(texture)));
// The subpass will need to read from the current pass texture when
// rendering the backdrop, so if there's an active pass, end it prior to
// rendering the subpass.
pass_context.EndPass();
}
auto subpass_coverage = GetSubpassCoverage(*subpass);
if (backdrop_contents) {
auto backdrop_coverage = backdrop_contents->GetCoverage(Entity{});
if (backdrop_coverage.has_value()) {
backdrop_coverage->origin += position;
if (subpass_coverage.has_value()) {
subpass_coverage = subpass_coverage->Union(backdrop_coverage.value());
} else {
subpass_coverage = backdrop_coverage;
}
}
}
if (!subpass_coverage.has_value()) {
return EntityPass::EntityResult::Skip();
}
if (subpass_coverage->size.IsEmpty()) {
// It is not an error to have an empty subpass. But subpasses that can't
// create their intermediates must trip errors.
return EntityPass::EntityResult::Skip();
}
RenderTarget subpass_target;
if (subpass->reads_from_pass_texture_) {
subpass_target = RenderTarget::CreateOffscreen(
*renderer.GetContext(), ISize::Ceil(subpass_coverage->size),
"EntityPass", StorageMode::kDevicePrivate, LoadAction::kClear,
StoreAction::kStore, StorageMode::kDevicePrivate, LoadAction::kClear,
StoreAction::kStore);
} else {
subpass_target = RenderTarget::CreateOffscreen(
*renderer.GetContext(), ISize::Ceil(subpass_coverage->size),
"EntityPass", StorageMode::kDevicePrivate, LoadAction::kClear,
StoreAction::kStore, StorageMode::kDeviceTransient,
LoadAction::kClear, StoreAction::kDontCare);
}
auto subpass_texture = subpass_target.GetRenderTargetTexture();
if (!subpass_texture) {
return EntityPass::EntityResult::Failure();
}
auto offscreen_texture_contents =
subpass->delegate_->CreateContentsForSubpassTarget(subpass_texture);
if (!offscreen_texture_contents) {
// This is an error because the subpass delegate said the pass couldn't
// be collapsed into its parent. Yet, when asked how it want's to
// postprocess the offscreen texture, it couldn't give us an answer.
//
// Theoretically, we could collapse the pass now. But that would be
// wasteful as we already have the offscreen texture and we don't want
// to discard it without ever using it. Just make the delegate do the
// right thing.
return EntityPass::EntityResult::Failure();
}
// Stencil textures aren't shared between EntityPasses (as much of the
// time they are transient).
if (!subpass->OnRender(renderer, subpass_target, subpass_coverage->origin,
position, ++pass_depth, subpass->stencil_depth_,
backdrop_contents)) {
return EntityPass::EntityResult::Failure();
}
element_entity.SetContents(std::move(offscreen_texture_contents));
element_entity.SetStencilDepth(subpass->stencil_depth_);
element_entity.SetBlendMode(subpass->blend_mode_);
// Once we have filters being applied for SaveLayer, some special sauce
// may be needed here (or in PaintPassDelegate) to ensure the filter
// parameters are transformed by the `xformation_` matrix, while
// continuing to apply only the subpass offset to the offscreen texture.
element_entity.SetTransformation(
Matrix::MakeTranslation(Vector3(subpass_coverage->origin - position)));
} else {
FML_UNREACHABLE();
}
return EntityPass::EntityResult::Success(element_entity);
}
bool EntityPass::OnRender(ContentContext& renderer,
RenderTarget render_target,
Point position,
Point parent_position,
uint32_t pass_depth,
size_t stencil_depth_floor,
std::shared_ptr<Contents> backdrop_contents) const {
TRACE_EVENT0("impeller", "EntityPass::OnRender");
auto context = renderer.GetContext();
InlinePassContext pass_context(context, render_target);
if (!pass_context.IsValid()) {
return false;
}
std::shared_ptr<CommandBuffer> command_buffer;
std::shared_ptr<RenderPass> pass;
uint32_t pass_count = 0;
auto render_element = [&stencil_depth_floor, &pass_context, &pass_depth,
&renderer](Entity element_entity) {
element_entity.SetStencilDepth(element_entity.GetStencilDepth() -
stencil_depth_floor);
auto end_pass = [&command_buffer, &pass, &context]() {
if (!pass->EncodeCommands(context->GetTransientsAllocator())) {
auto pass = pass_context.GetRenderPass(pass_depth);
if (!element_entity.Render(renderer, *pass)) {
return false;
}
if (!command_buffer->SubmitCommands()) {
return false;
}
return true;
};
if (backdrop_filter_proc_.has_value()) {
if (!backdrop_contents) {
return false;
}
Entity backdrop_entity;
backdrop_entity.SetContents(std::move(backdrop_contents));
backdrop_entity.SetTransformation(
Matrix::MakeTranslation(Vector3(parent_position - position)));
render_element(backdrop_entity);
}
for (const auto& element : elements_) {
Entity element_entity;
EntityResult result =
GetEntityForElement(element, renderer, pass_context, position,
pass_depth, stencil_depth_floor);
// =========================================================================
// Setup entity element for rendering ======================================
// =========================================================================
if (const auto& entity = std::get_if<Entity>(&element)) {
element_entity = *entity;
if (!position.IsZero()) {
// If the pass image is going to be rendered with a non-zero position,
// apply the negative translation to entity copies before rendering them
// so that they'll end up rendering to the correct on-screen position.
element_entity.SetTransformation(
Matrix::MakeTranslation(Vector3(-position)) *
element_entity.GetTransformation());
}
}
// =========================================================================
// Setup subpass element for rendering =====================================
// =========================================================================
else if (const auto& subpass_ptr =
std::get_if<std::unique_ptr<EntityPass>>(&element)) {
auto subpass = subpass_ptr->get();
if (subpass->delegate_->CanElide()) {
continue;
}
if (subpass->delegate_->CanCollapseIntoParentPass()) {
// Directly render into the parent target and move on.
if (!subpass->RenderInternal(renderer, render_target, position,
pass_depth, stencil_depth_floor)) {
return false;
}
continue;
}
const auto subpass_coverage = GetSubpassCoverage(*subpass);
if (!subpass_coverage.has_value()) {
continue;
}
if (subpass_coverage->size.IsEmpty()) {
// It is not an error to have an empty subpass. But subpasses that can't
// create their intermediates must trip errors.
continue;
}
RenderTarget subpass_target;
if (subpass->contains_advanced_blends_) {
subpass_target = RenderTarget::CreateOffscreen(
*context, ISize::Ceil(subpass_coverage->size), "EntityPass",
StorageMode::kDevicePrivate, LoadAction::kClear,
StoreAction::kStore, StorageMode::kDevicePrivate,
LoadAction::kClear, StoreAction::kStore);
} else {
subpass_target = RenderTarget::CreateOffscreen(
*context, ISize::Ceil(subpass_coverage->size), "EntityPass",
StorageMode::kDevicePrivate, LoadAction::kClear,
StoreAction::kStore, StorageMode::kDeviceTransient,
LoadAction::kClear, StoreAction::kDontCare);
}
auto subpass_texture = subpass_target.GetRenderTargetTexture();
if (!subpass_texture) {
switch (result.status) {
case EntityResult::kSuccess:
break;
case EntityResult::kFailure:
return false;
}
case EntityResult::kSkip:
continue;
};
auto offscreen_texture_contents =
subpass->delegate_->CreateContentsForSubpassTarget(subpass_texture);
//--------------------------------------------------------------------------
/// Setup advanced blends.
///
if (!offscreen_texture_contents) {
// This is an error because the subpass delegate said the pass couldn't
// be collapsed into its parent. Yet, when asked how it want's to
// postprocess the offscreen texture, it couldn't give us an answer.
//
// Theoretically, we could collapse the pass now. But that would be
// wasteful as we already have the offscreen texture and we don't want
// to discard it without ever using it. Just make the delegate do the
// right thing.
return false;
}
// Stencil textures aren't shared between EntityPasses (as much of the
// time they are transient).
if (!subpass->RenderInternal(renderer, subpass_target,
subpass_coverage->origin, ++pass_depth,
subpass->stencil_depth_)) {
return false;
}
element_entity.SetContents(std::move(offscreen_texture_contents));
element_entity.SetStencilDepth(subpass->stencil_depth_);
element_entity.SetBlendMode(subpass->blend_mode_);
// Once we have filters being applied for SaveLayer, some special sauce
// may be needed here (or in PaintPassDelegate) to ensure the filter
// parameters are transformed by the `xformation_` matrix, while
// continuing to apply only the subpass offset to the offscreen texture.
element_entity.SetTransformation(Matrix::MakeTranslation(
Vector3(subpass_coverage->origin - position)));
} else {
FML_UNREACHABLE();
}
// =========================================================================
// Configure the RenderPass ================================================
// =========================================================================
if (pass && element_entity.GetBlendMode() >
Entity::BlendMode::kLastPipelineBlendMode) {
if (result.entity.GetBlendMode() >
Entity::BlendMode::kLastPipelineBlendMode) {
// End the active pass and flush the buffer before rendering "advanced"
// blends. Advanced blends work by binding the current render target
// texture as an input ("destination"), blending with a second texture
// input ("source"), writing the result to an intermediate texture, and
// finally copying the data from the intermediate texture back to the
// render target texture. And so all of the commands that have written to
// the render target texture so far need to execute before it's bound for
// blending (otherwise the blend pass will end up executing before all the
// previous commands in the active pass).
if (!end_pass()) {
// render target texture. And so all of the commands that have written
// to the render target texture so far need to execute before it's bound
// for blending (otherwise the blend pass will end up executing before
// all the previous commands in the active pass).
if (!pass_context.EndPass()) {
return false;
}
// Resetting these handles triggers a new pass to get created below
pass = nullptr;
command_buffer = nullptr;
// Amend an advanced blend to the contents.
if (render_target.GetColorAttachments().empty()) {
// Amend an advanced blend filter to the contents, attaching the pass
// texture.
auto texture = pass_context.GetTexture();
if (!texture) {
return false;
}
auto color0 = render_target.GetColorAttachments().find(0)->second;
FilterInput::Vector inputs = {
FilterInput::Make(element_entity.GetContents()),
FilterInput::Make(
color0.resolve_texture ? color0.resolve_texture : color0.texture,
element_entity.GetTransformation().Invert())};
element_entity.SetContents(
FilterContents::MakeBlend(element_entity.GetBlendMode(), inputs));
element_entity.SetBlendMode(Entity::BlendMode::kSourceOver);
FilterInput::Make(result.entity.GetContents()),
FilterInput::Make(texture,
result.entity.GetTransformation().Invert())};
result.entity.SetContents(
FilterContents::MakeBlend(result.entity.GetBlendMode(), inputs));
result.entity.SetBlendMode(Entity::BlendMode::kSourceOver);
}
// Create a new render pass to render the element if one isn't active.
if (!pass) {
command_buffer = context->CreateRenderCommandBuffer();
if (!command_buffer) {
return false;
}
//--------------------------------------------------------------------------
/// Render the Element.
///
command_buffer->SetLabel(
"EntityPass Command Buffer: Depth=" + std::to_string(pass_depth) +
" Count=" + std::to_string(pass_count));
// Never clear the texture for subsequent passes.
if (pass_count > 0) {
if (!render_target.GetColorAttachments().empty()) {
auto color0 = render_target.GetColorAttachments().find(0)->second;
color0.load_action = LoadAction::kLoad;
render_target.SetColorAttachment(color0, 0);
}
if (auto stencil = render_target.GetStencilAttachment();
stencil.has_value()) {
stencil->load_action = LoadAction::kLoad;
render_target.SetStencilAttachment(stencil.value());
}
}
pass = command_buffer->CreateRenderPass(render_target);
if (!pass) {
return false;
}
pass->SetLabel(
"EntityPass Render Pass: Depth=" + std::to_string(pass_depth) +
" Count=" + std::to_string(pass_count));
++pass_count;
}
// =========================================================================
// Render the element ======================================================
// =========================================================================
element_entity.SetStencilDepth(element_entity.GetStencilDepth() -
stencil_depth_floor);
if (!element_entity.Render(renderer, *pass)) {
if (!render_element(result.entity)) {
return false;
}
}
if (pass) {
return end_pass();
}
return true;
}
@ -455,4 +480,11 @@ void EntityPass::SetBlendMode(Entity::BlendMode blend_mode) {
blend_mode_ = blend_mode;
}
void EntityPass::SetBackdropFilter(std::optional<BackdropFilterProc> proc) {
backdrop_filter_proc_ = proc;
if (superpass_) {
superpass_->reads_from_pass_texture_ = true;
}
}
} // namespace impeller

View File

@ -4,14 +4,17 @@
#pragma once
#include <functional>
#include <memory>
#include <optional>
#include <vector>
#include "flutter/fml/macros.h"
#include "impeller/entity/contents/contents.h"
#include "impeller/entity/contents/filters/filter_contents.h"
#include "impeller/entity/entity.h"
#include "impeller/entity/entity_pass_delegate.h"
#include "impeller/entity/inline_pass_context.h"
#include "impeller/renderer/render_target.h"
#include "impeller/typographer/lazy_glyph_atlas.h"
@ -22,6 +25,8 @@ class ContentContext;
class EntityPass {
public:
using Element = std::variant<Entity, std::unique_ptr<EntityPass>>;
using BackdropFilterProc =
std::function<std::shared_ptr<FilterContents>(FilterInput::Ref)>;
EntityPass();
@ -53,16 +58,50 @@ class EntityPass {
void SetBlendMode(Entity::BlendMode blend_mode);
void SetBackdropFilter(std::optional<BackdropFilterProc> proc);
std::optional<Rect> GetSubpassCoverage(const EntityPass& subpass) const;
std::optional<Rect> GetElementsCoverage() const;
private:
bool RenderInternal(ContentContext& renderer,
RenderTarget render_target,
Point position,
uint32_t pass_depth,
size_t stencil_depth_floor = 0) const;
struct EntityResult {
enum Status {
/// The entity was successfully resolved and can be rendered.
kSuccess,
/// An unexpected rendering error occurred while resolving the Entity.
kFailure,
/// The entity should be skipped because rendering it will contribute
/// nothing to the frame.
kSkip,
};
/// @brief The resulting entity that should be rendered. If `std::nullopt`,
/// there is nothing to render.
Entity entity;
/// @brief This is set to `false` if there was an unexpected rendering
/// error while resolving the Entity.
Status status = kFailure;
static EntityResult Success(Entity e) { return {e, kSuccess}; }
static EntityResult Failure() { return {{}, kFailure}; }
static EntityResult Skip() { return {{}, kSkip}; }
};
EntityResult GetEntityForElement(const EntityPass::Element& element,
ContentContext& renderer,
InlinePassContext& pass_context,
Point position,
uint32_t pass_depth,
size_t stencil_depth_floor) const;
bool OnRender(ContentContext& renderer,
RenderTarget render_target,
Point position,
Point parent_position,
uint32_t pass_depth,
size_t stencil_depth_floor = 0,
std::shared_ptr<Contents> backdrop_contents = nullptr) const;
std::vector<Element> elements_;
@ -70,7 +109,16 @@ class EntityPass {
Matrix xformation_;
size_t stencil_depth_ = 0u;
Entity::BlendMode blend_mode_ = Entity::BlendMode::kSourceOver;
bool contains_advanced_blends_ = false;
/// This flag is set to `true` whenever an entity is added to the pass that
/// requires reading the pass texture during rendering. This can happen in the
/// following scenarios:
/// 1. An entity with an "advanced blend" is added to the pass.
/// 2. A subpass with a backdrop filter is added to the pass.
bool reads_from_pass_texture_ = false;
std::optional<BackdropFilterProc> backdrop_filter_proc_ = std::nullopt;
std::unique_ptr<EntityPassDelegate> delegate_ =
EntityPassDelegate::MakeDefault();
std::shared_ptr<LazyGlyphAtlas> lazy_glyph_atlas_ =

View File

@ -0,0 +1,101 @@
// 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/inline_pass_context.h"
#include "impeller/renderer/command_buffer.h"
namespace impeller {
InlinePassContext::InlinePassContext(std::shared_ptr<Context> context,
RenderTarget render_target)
: context_(context), render_target_(render_target) {}
InlinePassContext::~InlinePassContext() {
EndPass();
}
bool InlinePassContext::IsValid() const {
return !render_target_.GetColorAttachments().empty();
}
bool InlinePassContext::IsActive() const {
return pass_ != nullptr;
}
std::shared_ptr<Texture> InlinePassContext::GetTexture() {
if (!IsValid()) {
return nullptr;
}
auto color0 = render_target_.GetColorAttachments().find(0)->second;
return color0.resolve_texture ? color0.resolve_texture : color0.texture;
}
bool InlinePassContext::EndPass() {
if (!IsActive()) {
return true;
}
if (!pass_->EncodeCommands(context_->GetTransientsAllocator())) {
return false;
}
if (!command_buffer_->SubmitCommands()) {
return false;
}
pass_ = nullptr;
command_buffer_ = nullptr;
return true;
}
const RenderTarget& InlinePassContext::GetRenderTarget() const {
return render_target_;
}
std::shared_ptr<RenderPass> InlinePassContext::GetRenderPass(
uint32_t pass_depth) {
// Create a new render pass if one isn't active.
if (!IsActive()) {
command_buffer_ = context_->CreateRenderCommandBuffer();
if (!command_buffer_) {
return nullptr;
}
command_buffer_->SetLabel(
"EntityPass Command Buffer: Depth=" + std::to_string(pass_depth) +
" Count=" + std::to_string(pass_count_));
// Never clear the texture for subsequent passes.
if (pass_count_ > 0) {
if (!render_target_.GetColorAttachments().empty()) {
auto color0 = render_target_.GetColorAttachments().find(0)->second;
color0.load_action = LoadAction::kLoad;
render_target_.SetColorAttachment(color0, 0);
}
if (auto stencil = render_target_.GetStencilAttachment();
stencil.has_value()) {
stencil->load_action = LoadAction::kLoad;
render_target_.SetStencilAttachment(stencil.value());
}
}
pass_ = command_buffer_->CreateRenderPass(render_target_);
if (!pass_) {
return nullptr;
}
pass_->SetLabel(
"EntityPass Render Pass: Depth=" + std::to_string(pass_depth) +
" Count=" + std::to_string(pass_count_));
++pass_count_;
}
return pass_;
}
} // namespace impeller

View File

@ -0,0 +1,37 @@
// 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 "impeller/renderer/context.h"
#include "impeller/renderer/render_pass.h"
#include "impeller/renderer/render_target.h"
namespace impeller {
class InlinePassContext {
public:
InlinePassContext(std::shared_ptr<Context> context,
RenderTarget render_target);
~InlinePassContext();
bool IsValid() const;
bool IsActive() const;
std::shared_ptr<Texture> GetTexture();
bool EndPass();
const RenderTarget& GetRenderTarget() const;
std::shared_ptr<RenderPass> GetRenderPass(uint32_t pass_depth);
private:
std::shared_ptr<Context> context_;
RenderTarget render_target_;
std::shared_ptr<CommandBuffer> command_buffer_;
std::shared_ptr<RenderPass> pass_;
uint32_t pass_count_ = 0;
FML_DISALLOW_COPY_AND_ASSIGN(InlinePassContext);
};
} // namespace impeller