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

Commit 698c878

Browse files
committed
Render advanced blends
Fix validation failures Allow for multiple passes against the on screen target Add fancy color wheel blend test Tweak test Fix compare URL Improve comments
1 parent ce80cd2 commit 698c878

27 files changed

+371
-129
lines changed

impeller/aiks/aiks_context.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,13 @@ bool AiksContext::IsValid() const {
2828
return is_valid_;
2929
}
3030

31-
bool AiksContext::Render(const Picture& picture, RenderPass& parent_pass) {
31+
bool AiksContext::Render(const Picture& picture, RenderTarget& render_target) {
3232
if (!IsValid()) {
3333
return false;
3434
}
3535

3636
if (picture.pass) {
37-
return picture.pass->Render(*content_context_, parent_pass);
37+
return picture.pass->Render(*content_context_, render_target);
3838
}
3939

4040
return true;

impeller/aiks/aiks_context.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include "flutter/fml/macros.h"
1010
#include "impeller/entity/contents/content_context.h"
1111
#include "impeller/renderer/context.h"
12+
#include "impeller/renderer/render_target.h"
1213

1314
namespace impeller {
1415

@@ -23,7 +24,7 @@ class AiksContext {
2324

2425
bool IsValid() const;
2526

26-
bool Render(const Picture& picture, RenderPass& parent_pass);
27+
bool Render(const Picture& picture, RenderTarget& render_target);
2728

2829
private:
2930
std::shared_ptr<Context> context_;

impeller/aiks/aiks_playground.cc

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ AiksPlayground::~AiksPlayground() = default;
1414

1515
bool AiksPlayground::OpenPlaygroundHere(const Picture& picture) {
1616
return OpenPlaygroundHere(
17-
[&picture](AiksContext& renderer, RenderPass& pass) -> bool {
18-
return renderer.Render(picture, pass);
17+
[&picture](AiksContext& renderer, RenderTarget& render_target) -> bool {
18+
return renderer.Render(picture, render_target);
1919
});
2020
}
2121

@@ -31,8 +31,8 @@ bool AiksPlayground::OpenPlaygroundHere(AiksPlaygroundCallback callback) {
3131
}
3232

3333
return Playground::OpenPlaygroundHere(
34-
[&renderer, &callback](RenderPass& pass) -> bool {
35-
return callback(renderer, pass);
34+
[&renderer, &callback](RenderTarget& render_target) -> bool {
35+
return callback(renderer, render_target);
3636
});
3737
}
3838

impeller/aiks/aiks_playground.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ namespace impeller {
1414
class AiksPlayground : public Playground {
1515
public:
1616
using AiksPlaygroundCallback =
17-
std::function<bool(AiksContext& renderer, RenderPass& pass)>;
17+
std::function<bool(AiksContext& renderer, RenderTarget& render_target)>;
1818

1919
AiksPlayground();
2020

impeller/aiks/aiks_unittests.cc

Lines changed: 121 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
// found in the LICENSE file.
44

55
#include <array>
6+
#include <cmath>
7+
#include <tuple>
68

79
#include "flutter/testing/testing.h"
810
#include "impeller/aiks/aiks_playground.h"
@@ -467,6 +469,121 @@ TEST_P(AiksTest, PaintBlendModeIsRespected) {
467469
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
468470
}
469471

472+
TEST_P(AiksTest, CanDrawWithAdvancedBlend) {
473+
// Compare with https://fiddle.skia.org/c/@BlendModes
474+
475+
std::vector<const char*> blend_mode_names;
476+
std::vector<Entity::BlendMode> blend_mode_values;
477+
{
478+
const std::vector<std::tuple<const char*, Entity::BlendMode>> blends = {
479+
// Pipeline blends (Porter-Duff/alpha blends)
480+
{"Clear", Entity::BlendMode::kClear},
481+
{"Source", Entity::BlendMode::kSource},
482+
{"Destination", Entity::BlendMode::kDestination},
483+
{"SourceOver", Entity::BlendMode::kSourceOver},
484+
{"DestinationOver", Entity::BlendMode::kDestinationOver},
485+
{"SourceIn", Entity::BlendMode::kSourceIn},
486+
{"DestinationIn", Entity::BlendMode::kDestinationIn},
487+
{"SourceOut", Entity::BlendMode::kSourceOut},
488+
{"DestinationOut", Entity::BlendMode::kDestinationOut},
489+
{"SourceATop", Entity::BlendMode::kSourceATop},
490+
{"DestinationATop", Entity::BlendMode::kDestinationATop},
491+
{"Xor", Entity::BlendMode::kXor},
492+
{"Plus", Entity::BlendMode::kPlus},
493+
{"Modulate", Entity::BlendMode::kModulate},
494+
// Advanced blends (non Porter-Duff/color blends)
495+
{"Screen", Entity::BlendMode::kScreen},
496+
};
497+
assert(blends.size() ==
498+
static_cast<size_t>(Entity::BlendMode::kLastAdvancedBlendMode) + 1);
499+
for (const auto& [name, mode] : blends) {
500+
blend_mode_names.push_back(name);
501+
blend_mode_values.push_back(mode);
502+
}
503+
}
504+
505+
auto draw_color_wheel = [](Canvas& canvas) {
506+
/// color_wheel_sampler: r=0 -> fuchsia, r=2pi/3 -> yellow, r=4pi/3 ->
507+
/// cyan domain: r >= 0 (because modulo used is non euclidean)
508+
auto color_wheel_sampler = [](Radians r) {
509+
Scalar x = r.radians / k2Pi + 1;
510+
511+
// https://www.desmos.com/calculator/6nhjelyoaj
512+
auto color_cycle = [](Scalar x) {
513+
Scalar cycle = std::fmod(x, 6.0f);
514+
return std::max(0.0f, std::min(1.0f, 2 - std::abs(2 - cycle)));
515+
};
516+
return Color(color_cycle(6 * x + 1), //
517+
color_cycle(6 * x - 1), //
518+
color_cycle(6 * x - 3), //
519+
1);
520+
};
521+
522+
Paint paint;
523+
524+
// Draw a fancy color wheel for the backdrop.
525+
// https://www.desmos.com/calculator/xw7kafthwd
526+
const int max_dist = 900;
527+
for (int i = 0; i <= 900; i++) {
528+
Radians r(kPhi / k2Pi * i);
529+
Scalar distance = r.radians / std::powf(4.12, 0.0026 * r.radians);
530+
Scalar normalized_distance = static_cast<Scalar>(i) / max_dist;
531+
532+
paint.color =
533+
color_wheel_sampler(r).WithAlpha(1.0f - normalized_distance);
534+
Point position(distance * std::sin(r.radians),
535+
-distance * std::cos(r.radians));
536+
537+
canvas.DrawCircle(position, 9 + normalized_distance * 3, paint);
538+
}
539+
};
540+
541+
bool first_frame = true;
542+
auto callback = [&](AiksContext& renderer, RenderTarget& render_target) {
543+
if (first_frame) {
544+
first_frame = false;
545+
ImGui::SetNextWindowSize({350, 200});
546+
ImGui::SetNextWindowPos({325, 550});
547+
}
548+
549+
ImGui::Begin("Controls");
550+
static int current_blend_index = 3;
551+
ImGui::ListBox("Blending mode", &current_blend_index,
552+
blend_mode_names.data(), blend_mode_names.size());
553+
ImGui::End();
554+
555+
Canvas canvas;
556+
Paint paint;
557+
// Default blend is kSourceOver.
558+
paint.color = Color::White();
559+
canvas.DrawPaint(paint);
560+
561+
canvas.Translate(Vector2(500, 400));
562+
canvas.Scale(Vector2(3, 3));
563+
564+
draw_color_wheel(canvas);
565+
566+
// Draw 3 circles to a subpass and blend it in.
567+
canvas.SaveLayer({.blend_mode = blend_mode_values[current_blend_index]});
568+
{
569+
paint.blend_mode = Entity::BlendMode::kPlus;
570+
const Scalar x = std::sin(k2Pi / 3);
571+
const Scalar y = -std::cos(k2Pi / 3);
572+
paint.color = Color::Red();
573+
canvas.DrawCircle(Point(-x, y) * 45, 65, paint);
574+
paint.color = Color::Green();
575+
canvas.DrawCircle(Point(0, -1) * 45, 65, paint);
576+
paint.color = Color::Blue();
577+
canvas.DrawCircle(Point(x, y) * 45, 65, paint);
578+
}
579+
canvas.Restore();
580+
581+
return renderer.Render(canvas.EndRecordingAsPicture(), render_target);
582+
};
583+
584+
ASSERT_TRUE(OpenPlaygroundHere(callback));
585+
}
586+
470587
TEST_P(AiksTest, TransformMultipliesCorrectly) {
471588
Canvas canvas;
472589
ASSERT_MATRIX_NEAR(canvas.GetCurrentTransformation(), Matrix());
@@ -509,7 +626,7 @@ TEST_P(AiksTest, TransformMultipliesCorrectly) {
509626
TEST_P(AiksTest, SolidStrokesRenderCorrectly) {
510627
// Compare with https://fiddle.skia.org/c/027392122bec8ac2b5d5de00a4b9bbe2
511628
bool first_frame = true;
512-
auto callback = [&](AiksContext& renderer, RenderPass& pass) {
629+
auto callback = [&](AiksContext& renderer, RenderTarget& render_target) {
513630
if (first_frame) {
514631
first_frame = false;
515632
ImGui::SetNextWindowSize({480, 100});
@@ -573,14 +690,14 @@ TEST_P(AiksTest, SolidStrokesRenderCorrectly) {
573690
canvas.Translate({-240, 60});
574691
}
575692

576-
return renderer.Render(canvas.EndRecordingAsPicture(), pass);
693+
return renderer.Render(canvas.EndRecordingAsPicture(), render_target);
577694
};
578695

579696
ASSERT_TRUE(OpenPlaygroundHere(callback));
580697
}
581698

582699
TEST_P(AiksTest, CoverageOriginShouldBeAccountedForInSubpasses) {
583-
auto callback = [](AiksContext& renderer, RenderPass& pass) {
700+
auto callback = [](AiksContext& renderer, RenderTarget& render_target) {
584701
Canvas canvas;
585702
Paint alpha;
586703
alpha.color = Color::Red().WithAlpha(0.5);
@@ -605,7 +722,7 @@ TEST_P(AiksTest, CoverageOriginShouldBeAccountedForInSubpasses) {
605722

606723
canvas.Restore();
607724

608-
return renderer.Render(canvas.EndRecordingAsPicture(), pass);
725+
return renderer.Render(canvas.EndRecordingAsPicture(), render_target);
609726
};
610727

611728
ASSERT_TRUE(OpenPlaygroundHere(callback));

impeller/display_list/display_list_playground.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,14 +36,14 @@ bool DisplayListPlayground::OpenPlaygroundHere(
3636
return false;
3737
}
3838
return Playground::OpenPlaygroundHere(
39-
[&context, &callback](RenderPass& pass) -> bool {
39+
[&context, &callback](RenderTarget& render_target) -> bool {
4040
auto list = callback();
4141

4242
DisplayListDispatcher dispatcher;
4343
list->Dispatch(dispatcher);
4444
auto picture = dispatcher.EndRecordingAsPicture();
4545

46-
return context.Render(picture, pass);
46+
return context.Render(picture, render_target);
4747
});
4848
}
4949

impeller/entity/contents/content_context.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include <sstream>
88

99
#include "impeller/renderer/command_buffer.h"
10+
#include "impeller/renderer/formats.h"
1011
#include "impeller/renderer/render_pass.h"
1112
#include "impeller/renderer/render_target.h"
1213

impeller/entity/contents/filters/inputs/texture_filter_input.cc

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,4 @@ std::optional<Rect> TextureFilterInput::GetCoverage(
2727
.TransformBounds(GetTransform(entity));
2828
}
2929

30-
Matrix TextureFilterInput::GetLocalTransform(const Entity& entity) const {
31-
// Compute the local transform such that the texture is centered.
32-
return Matrix::MakeTranslation(-Point(texture_->GetSize()) / 2);
33-
}
34-
3530
} // namespace impeller

impeller/entity/contents/filters/inputs/texture_filter_input.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,6 @@ class TextureFilterInput final : public FilterInput {
2222
// |FilterInput|
2323
std::optional<Rect> GetCoverage(const Entity& entity) const override;
2424

25-
// |FilterInput|
26-
Matrix GetLocalTransform(const Entity& entity) const override;
27-
2825
private:
2926
TextureFilterInput(std::shared_ptr<Texture> texture);
3027

impeller/entity/entity.cc

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
#include "impeller/base/validation.h"
1010
#include "impeller/entity/contents/content_context.h"
11+
#include "impeller/entity/contents/filters/filter_contents.h"
1112
#include "impeller/renderer/render_pass.h"
1213

1314
namespace impeller {
@@ -61,10 +62,6 @@ void Entity::IncrementStencilDepth(uint32_t increment) {
6162
}
6263

6364
void Entity::SetBlendMode(BlendMode blend_mode) {
64-
if (blend_mode_ > BlendMode::kLastPipelineBlendMode) {
65-
VALIDATION_LOG << "Non-pipeline blend modes are not supported by the "
66-
"entity blend mode setting.";
67-
}
6865
blend_mode_ = blend_mode;
6966
}
7067

0 commit comments

Comments
 (0)