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

Commit 0b0fab8

Browse files
[Impeller] distinguish between no clear color and transparent black clear color. (#49038)
If we clear to transparent black, we're not forcing the pass to be constructed. Change the entity pass API so that we can tell the difference between clearing transparent black and not having a clear color. In flutter/flutter#139571 , the app is creating a layer that is clearing to a transparent color, which is getting skipped. That invalid texture is fed into a blend which produces either black or magenta error texture. Fixes flutter/flutter#139571
1 parent fdae5f5 commit 0b0fab8

File tree

4 files changed

+56
-23
lines changed

4 files changed

+56
-23
lines changed

impeller/aiks/aiks_unittests.cc

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2953,19 +2953,18 @@ TEST_P(AiksTest, ClearColorOptimizationDoesNotApplyForBackdropFilters) {
29532953
Picture picture = canvas.EndRecordingAsPicture();
29542954

29552955
std::optional<Color> actual_color;
2956+
bool found_subpass = false;
29562957
picture.pass->IterateAllElements([&](EntityPass::Element& element) -> bool {
29572958
if (auto subpass = std::get_if<std::unique_ptr<EntityPass>>(&element)) {
29582959
actual_color = subpass->get()->GetClearColor();
2960+
found_subpass = true;
29592961
}
29602962
// Fail if the first element isn't a subpass.
29612963
return true;
29622964
});
29632965

2964-
ASSERT_TRUE(actual_color.has_value());
2965-
if (!actual_color) {
2966-
return;
2967-
}
2968-
ASSERT_EQ(actual_color.value(), Color::BlackTransparent());
2966+
EXPECT_TRUE(found_subpass);
2967+
EXPECT_FALSE(actual_color.has_value());
29692968
}
29702969

29712970
TEST_P(AiksTest, CollapsedDrawPaintInSubpass) {
@@ -4625,5 +4624,27 @@ TEST_P(AiksTest, GaussianBlurRotatedAndClipped) {
46254624
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
46264625
}
46274626

4627+
TEST_P(AiksTest, SubpassWithClearColorOptimization) {
4628+
Canvas canvas;
4629+
4630+
// Use a non-srcOver blend mode to ensure that we don't detect this as an
4631+
// opacity peephole optimization.
4632+
canvas.SaveLayer(
4633+
{.color = Color::Blue().WithAlpha(0.5), .blend_mode = BlendMode::kSource},
4634+
Rect::MakeLTRB(0, 0, 200, 200));
4635+
canvas.DrawPaint(
4636+
{.color = Color::BlackTransparent(), .blend_mode = BlendMode::kSource});
4637+
canvas.Restore();
4638+
4639+
canvas.SaveLayer(
4640+
{.color = Color::Blue(), .blend_mode = BlendMode::kDestinationOver});
4641+
canvas.Restore();
4642+
4643+
// This playground should appear blank on CI since we are only drawing
4644+
// transparent black. If the clear color optimization is broken, the texture
4645+
// will be filled with NaNs and may produce a magenta texture on macOS or iOS.
4646+
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
4647+
}
4648+
46284649
} // namespace testing
46294650
} // namespace impeller

impeller/aiks/paint_pass_delegate.cc

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
#include "impeller/entity/contents/texture_contents.h"
1111
#include "impeller/entity/entity_pass.h"
1212
#include "impeller/geometry/color.h"
13-
#include "impeller/geometry/path_builder.h"
1413

1514
namespace impeller {
1615

impeller/entity/entity_pass.cc

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
#include "impeller/entity/contents/filters/color_filter_contents.h"
2020
#include "impeller/entity/contents/filters/inputs/filter_input.h"
2121
#include "impeller/entity/contents/framebuffer_blend_contents.h"
22-
#include "impeller/entity/contents/solid_color_contents.h"
2322
#include "impeller/entity/contents/texture_contents.h"
2423
#include "impeller/entity/entity.h"
2524
#include "impeller/entity/inline_pass_context.h"
@@ -340,9 +339,9 @@ bool EntityPass::Render(ContentContext& renderer,
340339
// and then blit the results onto the onscreen texture. If using this branch,
341340
// there's no need to set up a stencil attachment on the root render target.
342341
if (reads_from_onscreen_backdrop) {
343-
auto offscreen_target =
344-
CreateRenderTarget(renderer, root_render_target.GetRenderTargetSize(),
345-
GetClearColor(render_target.GetRenderTargetSize()));
342+
auto offscreen_target = CreateRenderTarget(
343+
renderer, root_render_target.GetRenderTargetSize(),
344+
GetClearColorOrDefault(render_target.GetRenderTargetSize()));
346345

347346
if (!OnRender(renderer, // renderer
348347
capture, // capture
@@ -447,7 +446,8 @@ bool EntityPass::Render(ContentContext& renderer,
447446
}
448447

449448
// Set up the clear color of the root pass.
450-
color0.clear_color = GetClearColor(render_target.GetRenderTargetSize());
449+
color0.clear_color =
450+
GetClearColorOrDefault(render_target.GetRenderTargetSize());
451451
root_render_target.SetColorAttachment(color0, 0);
452452

453453
EntityPassTarget pass_target(
@@ -600,9 +600,9 @@ EntityPass::EntityResult EntityPass::GetEntityForElement(
600600
}
601601

602602
auto subpass_target = CreateRenderTarget(
603-
renderer, // renderer
604-
subpass_size, // size
605-
subpass->GetClearColor(subpass_size)); // clear_color
603+
renderer, // renderer
604+
subpass_size, // size
605+
subpass->GetClearColorOrDefault(subpass_size)); // clear_color
606606

607607
if (!subpass_target.IsValid()) {
608608
VALIDATION_LOG << "Subpass render target is invalid.";
@@ -845,8 +845,7 @@ bool EntityPass::OnRender(
845845
}
846846
auto clear_color_size = pass_target.GetRenderTarget().GetRenderTargetSize();
847847

848-
if (!collapsed_parent_pass &&
849-
!GetClearColor(clear_color_size).IsTransparent()) {
848+
if (!collapsed_parent_pass && GetClearColor(clear_color_size).has_value()) {
850849
// Force the pass context to create at least one new pass if the clear color
851850
// is present.
852851
pass_context.GetRenderPass(pass_depth);
@@ -958,7 +957,7 @@ bool EntityPass::OnRender(
958957
// If all previous elements were skipped due to clear color
959958
// optimization, then provide the clear color as the foreground of the
960959
// advanced blend.
961-
foreground_color = GetClearColor(clear_color_size);
960+
foreground_color = GetClearColorOrDefault(clear_color_size);
962961
coverage = Rect::MakeSize(clear_color_size);
963962
} else {
964963
coverage = result.entity.GetCoverage();
@@ -1139,21 +1138,29 @@ void EntityPass::SetBlendMode(BlendMode blend_mode) {
11391138
flood_clip_ = Entity::IsBlendModeDestructive(blend_mode);
11401139
}
11411140

1142-
Color EntityPass::GetClearColor(ISize target_size) const {
1143-
Color result = Color::BlackTransparent();
1141+
Color EntityPass::GetClearColorOrDefault(ISize size) const {
1142+
return GetClearColor(size).value_or(Color::BlackTransparent());
1143+
}
1144+
1145+
std::optional<Color> EntityPass::GetClearColor(ISize target_size) const {
11441146
if (backdrop_filter_proc_) {
1145-
return result;
1147+
return std::nullopt;
11461148
}
11471149

1150+
std::optional<Color> result = std::nullopt;
11481151
for (const Element& element : elements_) {
11491152
auto [entity_color, blend_mode] =
11501153
ElementAsBackgroundColor(element, target_size);
11511154
if (!entity_color.has_value()) {
11521155
break;
11531156
}
1154-
result = result.Blend(entity_color.value(), blend_mode);
1157+
result = result.value_or(Color::BlackTransparent())
1158+
.Blend(entity_color.value(), blend_mode);
1159+
}
1160+
if (result.has_value()) {
1161+
return result->Premultiply();
11551162
}
1156-
return result.Premultiply();
1163+
return result;
11571164
}
11581165

11591166
void EntityPass::SetBackdropFilter(BackdropFilterProc proc) {

impeller/entity/entity_pass.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,13 @@ class EntityPass {
136136

137137
void SetBlendMode(BlendMode blend_mode);
138138

139-
Color GetClearColor(ISize size = ISize::Infinite()) const;
139+
/// @brief Return the premultiplied clear color of the pass entities, if any.
140+
std::optional<Color> GetClearColor(ISize size = ISize::Infinite()) const;
141+
142+
/// @brief Return the premultiplied clear color of the pass entities.
143+
///
144+
/// If the entity pass has no clear color, this will return transparent black.
145+
Color GetClearColorOrDefault(ISize size = ISize::Infinite()) const;
140146

141147
void SetBackdropFilter(BackdropFilterProc proc);
142148

0 commit comments

Comments
 (0)