Skip to content

Commit

Permalink
[Impeller] Fix remaining problems with advanced blends (flutter#33254)
Browse files Browse the repository at this point in the history
  • Loading branch information
bdero authored May 11, 2022
1 parent 63a60cf commit 4596e39
Show file tree
Hide file tree
Showing 11 changed files with 109 additions and 42 deletions.
1 change: 1 addition & 0 deletions ci/licenses_golden/licenses_flutter
Original file line number Diff line number Diff line change
Expand Up @@ -501,6 +501,7 @@ FILE: ../../../flutter/impeller/entity/contents/filters/inputs/filter_contents_f
FILE: ../../../flutter/impeller/entity/contents/filters/inputs/filter_contents_filter_input.h
FILE: ../../../flutter/impeller/entity/contents/filters/inputs/filter_input.cc
FILE: ../../../flutter/impeller/entity/contents/filters/inputs/filter_input.h
FILE: ../../../flutter/impeller/entity/contents/filters/inputs/filter_input_unittests.cc
FILE: ../../../flutter/impeller/entity/contents/filters/inputs/texture_filter_input.cc
FILE: ../../../flutter/impeller/entity/contents/filters/inputs/texture_filter_input.h
FILE: ../../../flutter/impeller/entity/contents/linear_gradient_contents.cc
Expand Down
55 changes: 28 additions & 27 deletions impeller/aiks/canvas.cc
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,21 @@ void Canvas::Save() {
Save(false);
}

void Canvas::Save(bool create_subpass, Entity::BlendMode blend_mode) {
auto entry = CanvasStackEntry{};
entry.xformation = xformation_stack_.back().xformation;
entry.stencil_depth = xformation_stack_.back().stencil_depth;
if (create_subpass) {
entry.is_subpass = true;
auto subpass = std::make_unique<EntityPass>();
subpass->SetBlendMode(blend_mode);
current_pass_ = GetCurrentPass().AddSubpass(std::move(subpass));
current_pass_->SetTransformation(xformation_stack_.back().xformation);
current_pass_->SetStencilDepth(xformation_stack_.back().stencil_depth);
}
xformation_stack_.emplace_back(std::move(entry));
}

bool Canvas::Restore() {
FML_DCHECK(xformation_stack_.size() > 0);
if (xformation_stack_.size() == 1) {
Expand Down Expand Up @@ -137,23 +152,6 @@ void Canvas::DrawCircle(Point center, Scalar radius, Paint paint) {
std::move(paint));
}

void Canvas::SaveLayer(Paint paint, std::optional<Rect> bounds) {
Save(true);
GetCurrentPass().SetBlendMode(paint.blend_mode);

GetCurrentPass().SetDelegate(
std::make_unique<PaintPassDelegate>(paint, bounds));

if (bounds.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
// absent. Explicitly add back a clip to reproduce that behavior. Since
// clips never require a render target switch, this is a cheap operation.
ClipPath(PathBuilder{}.AddRect(bounds.value()).TakePath());
}
}

void Canvas::ClipPath(Path path, Entity::ClipOperation clip_op) {
auto contents = std::make_shared<ClipContents>();
contents->SetPath(std::move(path));
Expand Down Expand Up @@ -264,17 +262,20 @@ size_t Canvas::GetStencilDepth() const {
return xformation_stack_.back().stencil_depth;
}

void Canvas::Save(bool create_subpass) {
auto entry = CanvasStackEntry{};
entry.xformation = xformation_stack_.back().xformation;
entry.stencil_depth = xformation_stack_.back().stencil_depth;
if (create_subpass) {
entry.is_subpass = true;
current_pass_ = GetCurrentPass().AddSubpass(std::make_unique<EntityPass>());
current_pass_->SetTransformation(xformation_stack_.back().xformation);
current_pass_->SetStencilDepth(xformation_stack_.back().stencil_depth);
void Canvas::SaveLayer(Paint paint, std::optional<Rect> bounds) {
Save(true, paint.blend_mode);

GetCurrentPass().SetDelegate(
std::make_unique<PaintPassDelegate>(paint, bounds));

if (bounds.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
// absent. Explicitly add back a clip to reproduce that behavior. Since
// clips never require a render target switch, this is a cheap operation.
ClipPath(PathBuilder{}.AddRect(bounds.value()).TakePath());
}
xformation_stack_.emplace_back(std::move(entry));
}

void Canvas::DrawTextFrame(TextFrame text_frame, Point position, Paint paint) {
Expand Down
3 changes: 2 additions & 1 deletion impeller/aiks/canvas.h
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,8 @@ class Canvas {

size_t GetStencilDepth() const;

void Save(bool create_subpass);
void Save(bool create_subpass,
Entity::BlendMode = Entity::BlendMode::kSourceOver);

void RestoreClip();

Expand Down
1 change: 1 addition & 0 deletions impeller/entity/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ impeller_component("entity_unittests") {
testonly = true

sources = [
"contents/filters/inputs/filter_input_unittests.cc",
"entity_playground.cc",
"entity_playground.h",
"entity_unittests.cc",
Expand Down
11 changes: 9 additions & 2 deletions impeller/entity/contents/filters/inputs/filter_input.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

#include "impeller/entity/contents/filters/inputs/filter_input.h"

#include <memory>

#include "flutter/fml/logging.h"
#include "impeller/entity/contents/filters/filter_contents.h"
#include "impeller/entity/contents/filters/inputs/contents_filter_input.h"
Expand All @@ -26,13 +28,18 @@ FilterInput::Ref FilterInput::Make(Variant input) {
}

if (auto texture = std::get_if<std::shared_ptr<Texture>>(&input)) {
return std::static_pointer_cast<FilterInput>(
std::shared_ptr<TextureFilterInput>(new TextureFilterInput(*texture)));
return Make(*texture, Matrix());
}

FML_UNREACHABLE();
}

FilterInput::Ref FilterInput::Make(std::shared_ptr<Texture> texture,
Matrix local_transform) {
return std::shared_ptr<TextureFilterInput>(
new TextureFilterInput(texture, local_transform));
}

FilterInput::Vector FilterInput::Make(std::initializer_list<Variant> inputs) {
FilterInput::Vector result;
result.reserve(inputs.size());
Expand Down
3 changes: 3 additions & 0 deletions impeller/entity/contents/filters/inputs/filter_input.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ class FilterInput {

static FilterInput::Ref Make(Variant input);

static FilterInput::Ref Make(std::shared_ptr<Texture> input,
Matrix local_transform);

static FilterInput::Vector Make(std::initializer_list<Variant> inputs);

virtual Variant GetInput() const = 0;
Expand Down
29 changes: 29 additions & 0 deletions impeller/entity/contents/filters/inputs/filter_input_unittests.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// 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 <memory>
#include "flutter/testing/testing.h"
#include "gtest/gtest.h"
#include "impeller/entity/contents/filters/inputs/filter_input.h"
#include "impeller/entity/entity.h"
#include "impeller/geometry/geometry_unittests.h"

namespace impeller {
namespace testing {

TEST(FilterInputTest, CanSetLocalTransformForTexture) {
std::shared_ptr<Texture> texture = nullptr;
auto input =
FilterInput::Make(texture, Matrix::MakeTranslation({1.0, 0.0, 0.0}));
Entity e;
e.SetTransformation(Matrix::MakeTranslation({0.0, 2.0, 0.0}));

ASSERT_MATRIX_NEAR(input->GetLocalTransform(e),
Matrix::MakeTranslation({1.0, 0.0, 0.0}));
ASSERT_MATRIX_NEAR(input->GetTransform(e),
Matrix::MakeTranslation({1.0, 2.0, 0.0}));
}

} // namespace testing
} // namespace impeller
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@

namespace impeller {

TextureFilterInput::TextureFilterInput(std::shared_ptr<Texture> texture)
: texture_(texture) {}
TextureFilterInput::TextureFilterInput(std::shared_ptr<Texture> texture,
Matrix local_transform)
: texture_(texture), local_transform_(local_transform) {}

TextureFilterInput::~TextureFilterInput() = default;

Expand All @@ -27,4 +28,8 @@ std::optional<Rect> TextureFilterInput::GetCoverage(
.TransformBounds(GetTransform(entity));
}

Matrix TextureFilterInput::GetLocalTransform(const Entity& entity) const {
return local_transform_;
}

} // namespace impeller
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

#include "impeller/entity/contents/filters/inputs/filter_input.h"

#include "impeller/geometry/matrix.h"

namespace impeller {

class TextureFilterInput final : public FilterInput {
Expand All @@ -22,10 +24,15 @@ class TextureFilterInput final : public FilterInput {
// |FilterInput|
std::optional<Rect> GetCoverage(const Entity& entity) const override;

// |FilterInput|
Matrix GetLocalTransform(const Entity& entity) const override;

private:
TextureFilterInput(std::shared_ptr<Texture> texture);
TextureFilterInput(std::shared_ptr<Texture> texture,
Matrix local_transform = Matrix());

std::shared_ptr<Texture> texture_;
Matrix local_transform_;

friend FilterInput;
};
Expand Down
24 changes: 17 additions & 7 deletions impeller/entity/entity_pass.cc
Original file line number Diff line number Diff line change
Expand Up @@ -119,10 +119,12 @@ EntityPass* EntityPass::AddSubpass(std::unique_ptr<EntityPass> pass) {
}
FML_DCHECK(pass->superpass_ == nullptr);
pass->superpass_ = this;
auto subpass_pointer = pass.get();

if (pass->blend_mode_ > Entity::BlendMode::kLastPipelineBlendMode) {
contains_advanced_blends_ = true;
}

auto subpass_pointer = pass.get();
elements_.emplace_back(std::move(pass));
return subpass_pointer;
}
Expand All @@ -137,11 +139,17 @@ bool EntityPass::Render(ContentContext& renderer,
}

auto command_buffer = renderer.GetContext()->CreateRenderCommandBuffer();
auto render_pass = command_buffer->CreateRenderPass(offscreen_target);
command_buffer->SetLabel("EntityPass Root Command Buffer");
auto render_pass = command_buffer->CreateRenderPass(render_target);
render_pass->SetLabel("EntityPass Root Render Pass");

{
auto size_rect =
Rect::MakeSize(Size(offscreen_target.GetRenderTargetSize()));
auto contents = std::make_shared<TextureContents>();
contents->SetPath(PathBuilder{}.AddRect(size_rect).TakePath());
contents->SetTexture(offscreen_target.GetRenderTargetTexture());
contents->SetSourceRect(size_rect);

Entity entity;
entity.SetContents(contents);
Expand Down Expand Up @@ -307,11 +315,13 @@ bool EntityPass::RenderInternal(ContentContext& renderer,
}
auto color0 = render_target.GetColorAttachments().find(0)->second;

auto input = FilterInput::Make({
element_entity.GetContents(),
color0.resolve_texture ? color0.resolve_texture : color0.texture,
});
element_entity.SetContents(FilterContents::MakeBlend(blend_mode_, input));
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::kSource);
}

Expand Down
6 changes: 4 additions & 2 deletions impeller/entity/shaders/texture_blend_screen.frag
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,11 @@ vec4 Unpremultiply(vec4 color) {
}

void main() {
vec4 dst = texture(texture_sampler_dst, v_dst_texture_coords);
vec4 dst = SampleWithBorder(texture_sampler_dst, v_dst_texture_coords);
vec4 d = Unpremultiply(dst);
vec4 src = SampleWithBorder(texture_sampler_src, v_src_texture_coords);
vec4 s = Unpremultiply(src);
frag_color = 1 - ((1 - s) * (1 - d));

vec3 color = d.rgb + s.rgb - (d.rgb * s.rgb);
frag_color = vec4(color, d.a - s.a + s.a);
}

0 comments on commit 4596e39

Please sign in to comment.