mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
[Impeller] Remove blend mode restrictions on clear color optimization (flutter/engine#43348)
Now that all of the CPU blend modes work, we can allow back-to-back fullscreen solid color draws to get blended together regardless of their blend mode/color opacity.
This commit is contained in:
parent
a0dc7abbe9
commit
a3745e962c
@ -1989,11 +1989,13 @@ TEST_P(AiksTest, OpacityPeepHoleApplicationTest) {
|
||||
TEST_P(AiksTest, DrawPaintAbsorbsClears) {
|
||||
Canvas canvas;
|
||||
canvas.DrawPaint({.color = Color::Red(), .blend_mode = BlendMode::kSource});
|
||||
canvas.DrawPaint(
|
||||
{.color = Color::CornflowerBlue(), .blend_mode = BlendMode::kSource});
|
||||
canvas.DrawPaint({.color = Color::CornflowerBlue().WithAlpha(0.75),
|
||||
.blend_mode = BlendMode::kSourceOver});
|
||||
|
||||
Picture picture = canvas.EndRecordingAsPicture();
|
||||
ASSERT_EQ(picture.pass->GetClearColor(), Color::CornflowerBlue());
|
||||
auto expected = Color::Red().Blend(Color::CornflowerBlue().WithAlpha(0.75),
|
||||
BlendMode::kSourceOver);
|
||||
ASSERT_EQ(picture.pass->GetClearColor(), expected);
|
||||
|
||||
std::shared_ptr<ContextSpy> spy = ContextSpy::Make();
|
||||
std::shared_ptr<Context> real_context = GetContext();
|
||||
@ -2010,8 +2012,9 @@ TEST_P(AiksTest, DrawRectAbsorbsClears) {
|
||||
Canvas canvas;
|
||||
canvas.DrawRect({0, 0, 300, 300},
|
||||
{.color = Color::Red(), .blend_mode = BlendMode::kSource});
|
||||
canvas.DrawRect({0, 0, 300, 300}, {.color = Color::CornflowerBlue(),
|
||||
.blend_mode = BlendMode::kSource});
|
||||
canvas.DrawRect({0, 0, 300, 300},
|
||||
{.color = Color::CornflowerBlue().WithAlpha(0.75),
|
||||
.blend_mode = BlendMode::kSourceOver});
|
||||
|
||||
std::shared_ptr<ContextSpy> spy = ContextSpy::Make();
|
||||
Picture picture = canvas.EndRecordingAsPicture();
|
||||
@ -2029,9 +2032,9 @@ TEST_P(AiksTest, DrawRectAbsorbsClearsNegativeRRect) {
|
||||
Canvas canvas;
|
||||
canvas.DrawRRect({0, 0, 300, 300}, 5.0,
|
||||
{.color = Color::Red(), .blend_mode = BlendMode::kSource});
|
||||
canvas.DrawRRect(
|
||||
{0, 0, 300, 300}, 5.0,
|
||||
{.color = Color::CornflowerBlue(), .blend_mode = BlendMode::kSource});
|
||||
canvas.DrawRRect({0, 0, 300, 300}, 5.0,
|
||||
{.color = Color::CornflowerBlue().WithAlpha(0.75),
|
||||
.blend_mode = BlendMode::kSourceOver});
|
||||
|
||||
std::shared_ptr<ContextSpy> spy = ContextSpy::Make();
|
||||
Picture picture = canvas.EndRecordingAsPicture();
|
||||
@ -2069,8 +2072,9 @@ TEST_P(AiksTest, DrawRectAbsorbsClearsNegative) {
|
||||
Canvas canvas;
|
||||
canvas.DrawRect({0, 0, 300, 300},
|
||||
{.color = Color::Red(), .blend_mode = BlendMode::kSource});
|
||||
canvas.DrawRect({0, 0, 300, 300}, {.color = Color::CornflowerBlue(),
|
||||
.blend_mode = BlendMode::kSource});
|
||||
canvas.DrawRect({0, 0, 300, 300},
|
||||
{.color = Color::CornflowerBlue().WithAlpha(0.75),
|
||||
.blend_mode = BlendMode::kSourceOver});
|
||||
|
||||
std::shared_ptr<ContextSpy> spy = ContextSpy::Make();
|
||||
Picture picture = canvas.EndRecordingAsPicture();
|
||||
@ -2089,7 +2093,8 @@ TEST_P(AiksTest, CollapsedDrawPaintInSubpass) {
|
||||
canvas.DrawPaint(
|
||||
{.color = Color::Yellow(), .blend_mode = BlendMode::kSource});
|
||||
canvas.SaveLayer({.blend_mode = BlendMode::kMultiply});
|
||||
canvas.DrawPaint({.color = Color::Red(), .blend_mode = BlendMode::kSource});
|
||||
canvas.DrawPaint({.color = Color::CornflowerBlue().WithAlpha(0.75),
|
||||
.blend_mode = BlendMode::kSourceOver});
|
||||
|
||||
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
|
||||
}
|
||||
@ -2171,7 +2176,7 @@ static Picture BlendModeTest(BlendMode blend_mode,
|
||||
canvas.DrawPaint({.color = Color::Black()});
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
/// 1. Save layer blending (top left).
|
||||
/// 1. Save layer blending (top squares).
|
||||
///
|
||||
|
||||
canvas.Save();
|
||||
@ -2201,7 +2206,7 @@ static Picture BlendModeTest(BlendMode blend_mode,
|
||||
canvas.RestoreToCount(0);
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
/// 2. CPU blend modes (top left).
|
||||
/// 2. CPU blend modes (bottom squares).
|
||||
///
|
||||
|
||||
canvas.Save();
|
||||
|
||||
@ -118,6 +118,11 @@ void Contents::SetInheritedOpacity(Scalar opacity) {
|
||||
"Contents::CanAcceptOpacity returns false.";
|
||||
}
|
||||
|
||||
std::optional<Color> Contents::AsBackgroundColor(const Entity& entity,
|
||||
ISize target_size) const {
|
||||
return {};
|
||||
}
|
||||
|
||||
bool Contents::ShouldRender(const Entity& entity,
|
||||
const std::optional<Rect>& stencil_coverage) const {
|
||||
if (!stencil_coverage.has_value()) {
|
||||
|
||||
@ -117,10 +117,14 @@ class Contents {
|
||||
/// Use of this method is invalid if CanAcceptOpacity returns false.
|
||||
virtual void SetInheritedOpacity(Scalar opacity);
|
||||
|
||||
/// @brief Returns a color if this Contents will flood the given `target_size`
|
||||
/// with a color. This output color is the "Source" color that will be
|
||||
/// used for the Entity's blend operation.
|
||||
///
|
||||
/// This is useful for absorbing full screen solid color draws into
|
||||
/// subpass clear colors.
|
||||
virtual std::optional<Color> AsBackgroundColor(const Entity& entity,
|
||||
ISize target_size) const {
|
||||
return {};
|
||||
}
|
||||
ISize target_size) const;
|
||||
|
||||
private:
|
||||
std::optional<Rect> coverage_hint_;
|
||||
|
||||
@ -104,12 +104,6 @@ std::unique_ptr<SolidColorContents> SolidColorContents::Make(const Path& path,
|
||||
std::optional<Color> SolidColorContents::AsBackgroundColor(
|
||||
const Entity& entity,
|
||||
ISize target_size) const {
|
||||
if (!(GetColor().IsOpaque() &&
|
||||
(entity.GetBlendMode() == BlendMode::kSource ||
|
||||
entity.GetBlendMode() == BlendMode::kSourceOver))) {
|
||||
return {};
|
||||
}
|
||||
|
||||
Rect target_rect = Rect::MakeSize(target_size);
|
||||
return GetGeometry()->CoversArea(entity.GetTransformation(), target_rect)
|
||||
? GetColor()
|
||||
|
||||
@ -124,6 +124,10 @@ bool Entity::SetInheritedOpacity(Scalar alpha) {
|
||||
return true;
|
||||
}
|
||||
|
||||
std::optional<Color> Entity::AsBackgroundColor(ISize target_size) const {
|
||||
return contents_->AsBackgroundColor(*this, target_size);
|
||||
}
|
||||
|
||||
/// @brief Returns true if the blend mode is "destructive", meaning that even
|
||||
/// fully transparent source colors would result in the destination
|
||||
/// getting changed.
|
||||
|
||||
@ -92,6 +92,8 @@ class Entity {
|
||||
|
||||
bool SetInheritedOpacity(Scalar alpha);
|
||||
|
||||
std::optional<Color> AsBackgroundColor(ISize target_size) const;
|
||||
|
||||
private:
|
||||
Matrix transformation_;
|
||||
std::shared_ptr<Contents> contents_;
|
||||
|
||||
@ -37,13 +37,13 @@
|
||||
namespace impeller {
|
||||
|
||||
namespace {
|
||||
std::optional<Color> AsBackgroundColor(const EntityPass::Element& element,
|
||||
ISize target_size) {
|
||||
std::tuple<std::optional<Color>, BlendMode> ElementAsBackgroundColor(
|
||||
const EntityPass::Element& element,
|
||||
ISize target_size) {
|
||||
if (const Entity* entity = std::get_if<Entity>(&element)) {
|
||||
std::optional<Color> entity_color =
|
||||
entity->GetContents()->AsBackgroundColor(*entity, target_size);
|
||||
std::optional<Color> entity_color = entity->AsBackgroundColor(target_size);
|
||||
if (entity_color.has_value()) {
|
||||
return entity_color.value();
|
||||
return {entity_color.value(), entity->GetBlendMode()};
|
||||
}
|
||||
}
|
||||
return {};
|
||||
@ -251,7 +251,7 @@ bool EntityPass::Render(ContentContext& renderer,
|
||||
if (!supports_onscreen_backdrop_reads && reads_from_onscreen_backdrop) {
|
||||
auto offscreen_target = CreateRenderTarget(
|
||||
renderer, root_render_target.GetRenderTargetSize(), true,
|
||||
GetClearColor(render_target.GetRenderTargetSize()).Premultiply());
|
||||
GetClearColor(render_target.GetRenderTargetSize()));
|
||||
|
||||
if (!OnRender(renderer, // renderer
|
||||
offscreen_target.GetRenderTarget()
|
||||
@ -356,8 +356,7 @@ bool EntityPass::Render(ContentContext& renderer,
|
||||
}
|
||||
|
||||
// Set up the clear color of the root pass.
|
||||
color0.clear_color =
|
||||
GetClearColor(render_target.GetRenderTargetSize()).Premultiply();
|
||||
color0.clear_color = GetClearColor(render_target.GetRenderTargetSize());
|
||||
root_render_target.SetColorAttachment(color0, 0);
|
||||
|
||||
EntityPassTarget pass_target(
|
||||
@ -723,13 +722,12 @@ bool EntityPass::OnRender(
|
||||
for (const auto& element : elements_) {
|
||||
// Skip elements that are incorporated into the clear color.
|
||||
if (is_collapsing_clear_colors) {
|
||||
std::optional<Color> entity_color =
|
||||
AsBackgroundColor(element, root_pass_size);
|
||||
auto [entity_color, _] =
|
||||
ElementAsBackgroundColor(element, root_pass_size);
|
||||
if (entity_color.has_value()) {
|
||||
continue;
|
||||
} else {
|
||||
is_collapsing_clear_colors = false;
|
||||
}
|
||||
is_collapsing_clear_colors = false;
|
||||
}
|
||||
|
||||
EntityResult result =
|
||||
@ -922,14 +920,14 @@ void EntityPass::SetBlendMode(BlendMode blend_mode) {
|
||||
Color EntityPass::GetClearColor(ISize target_size) const {
|
||||
Color result = Color::BlackTransparent();
|
||||
for (const Element& element : elements_) {
|
||||
std::optional<Color> entity_color = AsBackgroundColor(element, target_size);
|
||||
if (entity_color.has_value()) {
|
||||
result = entity_color.value();
|
||||
} else {
|
||||
auto [entity_color, blend_mode] =
|
||||
ElementAsBackgroundColor(element, target_size);
|
||||
if (!entity_color.has_value()) {
|
||||
break;
|
||||
}
|
||||
result = result.Blend(entity_color.value(), blend_mode);
|
||||
}
|
||||
return result;
|
||||
return result.Premultiply();
|
||||
}
|
||||
|
||||
void EntityPass::SetBackdropFilter(BackdropFilterProc proc) {
|
||||
|
||||
@ -138,4 +138,8 @@ std::unique_ptr<Geometry> Geometry::MakeRect(Rect rect) {
|
||||
return std::make_unique<RectGeometry>(rect);
|
||||
}
|
||||
|
||||
bool Geometry::CoversArea(const Matrix& transform, const Rect& rect) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace impeller
|
||||
|
||||
@ -90,9 +90,7 @@ class Geometry {
|
||||
|
||||
/// @return `true` if this geometry will completely cover all fragments in
|
||||
/// `rect` when the `transform` is applied to it.
|
||||
virtual bool CoversArea(const Matrix& transform, const Rect& rect) const {
|
||||
return false;
|
||||
}
|
||||
virtual bool CoversArea(const Matrix& transform, const Rect& rect) const;
|
||||
};
|
||||
|
||||
} // namespace impeller
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user