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

Commit 467b801

Browse files
authored
[Impeller] adds a plus advanced blend for f16 pixel formats (#51589)
fixes flutter/flutter#142549 [C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style
1 parent 00b0905 commit 467b801

30 files changed

+364
-179
lines changed

display_list/testing/dl_test_surface_metal.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ sk_sp<DlImage> DlMetalSurfaceProvider::MakeImpellerImage(
115115

116116
void DlMetalSurfaceProvider::InitScreenShotter() const {
117117
if (!snapshotter_) {
118-
snapshotter_.reset(new MetalScreenshotter());
118+
snapshotter_.reset(new MetalScreenshotter(/*enable_wide_gamut=*/false));
119119
auto typographer = impeller::TypographerContextSkia::Make();
120120
aiks_context_.reset(new impeller::AiksContext(
121121
snapshotter_->GetPlayground().GetContext(), typographer));

impeller/aiks/aiks_unittests.cc

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1101,6 +1101,76 @@ TEST_P(AiksTest, PaintBlendModeIsRespected) {
11011101
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
11021102
}
11031103

1104+
// This makes sure the WideGamut named tests use 16bit float pixel format.
1105+
TEST_P(AiksTest, F16WideGamut) {
1106+
if (GetParam() != PlaygroundBackend::kMetal) {
1107+
GTEST_SKIP_("This backend doesn't yet support wide gamut.");
1108+
}
1109+
EXPECT_EQ(GetContext()->GetCapabilities()->GetDefaultColorFormat(),
1110+
PixelFormat::kR16G16B16A16Float);
1111+
EXPECT_FALSE(IsAlphaClampedToOne(
1112+
GetContext()->GetCapabilities()->GetDefaultColorFormat()));
1113+
}
1114+
1115+
TEST_P(AiksTest, NotF16) {
1116+
EXPECT_TRUE(IsAlphaClampedToOne(
1117+
GetContext()->GetCapabilities()->GetDefaultColorFormat()));
1118+
}
1119+
1120+
// Bug: https://github.com/flutter/flutter/issues/142549
1121+
TEST_P(AiksTest, BlendModePlusAlphaWideGamut) {
1122+
if (GetParam() != PlaygroundBackend::kMetal) {
1123+
GTEST_SKIP_("This backend doesn't yet support wide gamut.");
1124+
}
1125+
EXPECT_EQ(GetContext()->GetCapabilities()->GetDefaultColorFormat(),
1126+
PixelFormat::kR16G16B16A16Float);
1127+
auto texture = CreateTextureForFixture("airplane.jpg",
1128+
/*enable_mipmapping=*/true);
1129+
1130+
Canvas canvas;
1131+
canvas.Scale(GetContentScale());
1132+
canvas.DrawPaint({.color = Color(0.9, 1.0, 0.9, 1.0)});
1133+
canvas.SaveLayer({});
1134+
Paint paint;
1135+
paint.blend_mode = BlendMode::kPlus;
1136+
paint.color = Color::Red();
1137+
canvas.DrawRect(Rect::MakeXYWH(100, 100, 400, 400), paint);
1138+
paint.color = Color::White();
1139+
canvas.DrawImageRect(
1140+
std::make_shared<Image>(texture), Rect::MakeSize(texture->GetSize()),
1141+
Rect::MakeXYWH(100, 100, 400, 400).Expand(-100, -100), paint);
1142+
canvas.Restore();
1143+
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
1144+
}
1145+
1146+
// Bug: https://github.com/flutter/flutter/issues/142549
1147+
TEST_P(AiksTest, BlendModePlusAlphaColorFilterWideGamut) {
1148+
if (GetParam() != PlaygroundBackend::kMetal) {
1149+
GTEST_SKIP_("This backend doesn't yet support wide gamut.");
1150+
}
1151+
EXPECT_EQ(GetContext()->GetCapabilities()->GetDefaultColorFormat(),
1152+
PixelFormat::kR16G16B16A16Float);
1153+
auto texture = CreateTextureForFixture("airplane.jpg",
1154+
/*enable_mipmapping=*/true);
1155+
1156+
Canvas canvas;
1157+
canvas.Scale(GetContentScale());
1158+
canvas.DrawPaint({.color = Color(0.1, 0.2, 0.1, 1.0)});
1159+
canvas.SaveLayer({
1160+
.color_filter =
1161+
ColorFilter::MakeBlend(BlendMode::kPlus, Color(Vector4{1, 0, 0, 1})),
1162+
});
1163+
Paint paint;
1164+
paint.color = Color::Red();
1165+
canvas.DrawRect(Rect::MakeXYWH(100, 100, 400, 400), paint);
1166+
paint.color = Color::White();
1167+
canvas.DrawImageRect(
1168+
std::make_shared<Image>(texture), Rect::MakeSize(texture->GetSize()),
1169+
Rect::MakeXYWH(100, 100, 400, 400).Expand(-100, -100), paint);
1170+
canvas.Restore();
1171+
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
1172+
}
1173+
11041174
TEST_P(AiksTest, ColorWheel) {
11051175
// Compare with https://fiddle.skia.org/c/@BlendModes
11061176

impeller/compiler/shader_lib/impeller/color.glsl

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,4 +46,12 @@ f16vec4 IPHalfPremultiply(f16vec4 color) {
4646
return f16vec4(color.rgb * color.a, color.a);
4747
}
4848

49+
/// Performs the plus blend on `src` and `dst` which are premultiplied colors.
50+
//`max` determines the values the results are clamped to.
51+
f16vec4 IPHalfPlusBlend(f16vec4 src, f16vec4 dst) {
52+
float16_t min = 0.0hf;
53+
float16_t max = 1.0hf;
54+
return clamp(dst + src, min, max);
55+
}
56+
4957
#endif

impeller/core/formats.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,14 @@ constexpr bool IsStencilWritable(PixelFormat format) {
138138
}
139139
}
140140

141+
/// Returns `true` if the pixel format has an implicit `clamp(x, 0, 1)` in the
142+
/// pixel format. This is important for example when performing the `Plus` blend
143+
/// where we don't want alpha values over 1.0.
144+
constexpr bool IsAlphaClampedToOne(PixelFormat pixel_format) {
145+
return !(pixel_format == PixelFormat::kR32G32B32A32Float ||
146+
pixel_format == PixelFormat::kR16G16B16A16Float);
147+
}
148+
141149
constexpr const char* PixelFormatToString(PixelFormat format) {
142150
switch (format) {
143151
case PixelFormat::kUnknown:

impeller/entity/contents/content_context.cc

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,8 @@ void ContentContextOptions::ApplyToPipelineDescriptor(
124124
color0.src_color_blend_factor = BlendFactor::kOneMinusDestinationAlpha;
125125
break;
126126
case BlendMode::kPlus:
127+
// The kPlusAdvanced should be used instead.
128+
FML_DCHECK(IsAlphaClampedToOne(color_attachment_pixel_format));
127129
color0.dst_alpha_blend_factor = BlendFactor::kOne;
128130
color0.dst_color_blend_factor = BlendFactor::kOne;
129131
color0.src_alpha_blend_factor = BlendFactor::kOne;
@@ -324,6 +326,10 @@ ContentContext::ContentContext(
324326
framebuffer_blend_lighten_pipelines_.CreateDefault(
325327
*context_, options_trianglestrip,
326328
{static_cast<Scalar>(BlendSelectValues::kLighten), supports_decal});
329+
framebuffer_blend_plus_advanced_pipelines_.CreateDefault(
330+
*context_, options_trianglestrip,
331+
{static_cast<Scalar>(BlendSelectValues::kPlusAdvanced),
332+
supports_decal});
327333
framebuffer_blend_luminosity_pipelines_.CreateDefault(
328334
*context_, options_trianglestrip,
329335
{static_cast<Scalar>(BlendSelectValues::kLuminosity), supports_decal});
@@ -371,6 +377,9 @@ ContentContext::ContentContext(
371377
blend_lighten_pipelines_.CreateDefault(
372378
*context_, options_trianglestrip,
373379
{static_cast<Scalar>(BlendSelectValues::kLighten), supports_decal});
380+
blend_plus_advanced_pipelines_.CreateDefault(
381+
*context_, options_trianglestrip,
382+
{static_cast<Scalar>(BlendSelectValues::kPlusAdvanced), supports_decal});
374383
blend_luminosity_pipelines_.CreateDefault(
375384
*context_, options_trianglestrip,
376385
{static_cast<Scalar>(BlendSelectValues::kLuminosity), supports_decal});

impeller/entity/contents/content_context.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,8 @@ using BlendHuePipeline =
197197
RenderPipelineT<AdvancedBlendVertexShader, AdvancedBlendFragmentShader>;
198198
using BlendLightenPipeline =
199199
RenderPipelineT<AdvancedBlendVertexShader, AdvancedBlendFragmentShader>;
200+
using BlendPlusAdvancedPipeline =
201+
RenderPipelineT<AdvancedBlendVertexShader, AdvancedBlendFragmentShader>;
200202
using BlendLuminosityPipeline =
201203
RenderPipelineT<AdvancedBlendVertexShader, AdvancedBlendFragmentShader>;
202204
using BlendMultiplyPipeline =
@@ -237,6 +239,9 @@ using FramebufferBlendHuePipeline =
237239
using FramebufferBlendLightenPipeline =
238240
RenderPipelineT<FramebufferBlendVertexShader,
239241
FramebufferBlendFragmentShader>;
242+
using FramebufferBlendPlusAdvancedPipeline =
243+
RenderPipelineT<FramebufferBlendVertexShader,
244+
FramebufferBlendFragmentShader>;
240245
using FramebufferBlendLuminosityPipeline =
241246
RenderPipelineT<FramebufferBlendVertexShader,
242247
FramebufferBlendFragmentShader>;
@@ -640,6 +645,11 @@ class ContentContext {
640645
return GetPipeline(blend_lighten_pipelines_, opts);
641646
}
642647

648+
std::shared_ptr<Pipeline<PipelineDescriptor>> GetBlendPlusAdvancedPipeline(
649+
ContentContextOptions opts) const {
650+
return GetPipeline(blend_plus_advanced_pipelines_, opts);
651+
}
652+
643653
std::shared_ptr<Pipeline<PipelineDescriptor>> GetBlendLuminosityPipeline(
644654
ContentContextOptions opts) const {
645655
return GetPipeline(blend_luminosity_pipelines_, opts);
@@ -725,6 +735,12 @@ class ContentContext {
725735
return GetPipeline(framebuffer_blend_lighten_pipelines_, opts);
726736
}
727737

738+
std::shared_ptr<Pipeline<PipelineDescriptor>>
739+
GetFramebufferBlendPlusAdvancedPipeline(ContentContextOptions opts) const {
740+
FML_DCHECK(GetDeviceCapabilities().SupportsFramebufferFetch());
741+
return GetPipeline(framebuffer_blend_plus_advanced_pipelines_, opts);
742+
}
743+
728744
std::shared_ptr<Pipeline<PipelineDescriptor>>
729745
GetFramebufferBlendLuminosityPipeline(ContentContextOptions opts) const {
730746
FML_DCHECK(GetDeviceCapabilities().SupportsFramebufferFetch());
@@ -989,6 +1005,7 @@ class ContentContext {
9891005
mutable Variants<BlendHardLightPipeline> blend_hardlight_pipelines_;
9901006
mutable Variants<BlendHuePipeline> blend_hue_pipelines_;
9911007
mutable Variants<BlendLightenPipeline> blend_lighten_pipelines_;
1008+
mutable Variants<BlendPlusAdvancedPipeline> blend_plus_advanced_pipelines_;
9921009
mutable Variants<BlendLuminosityPipeline> blend_luminosity_pipelines_;
9931010
mutable Variants<BlendMultiplyPipeline> blend_multiply_pipelines_;
9941011
mutable Variants<BlendOverlayPipeline> blend_overlay_pipelines_;
@@ -1014,6 +1031,8 @@ class ContentContext {
10141031
framebuffer_blend_hue_pipelines_;
10151032
mutable Variants<FramebufferBlendLightenPipeline>
10161033
framebuffer_blend_lighten_pipelines_;
1034+
mutable Variants<FramebufferBlendPlusAdvancedPipeline>
1035+
framebuffer_blend_plus_advanced_pipelines_;
10171036
mutable Variants<FramebufferBlendLuminosityPipeline>
10181037
framebuffer_blend_luminosity_pipelines_;
10191038
mutable Variants<FramebufferBlendMultiplyPipeline>

impeller/entity/contents/filters/blend_filter_contents.cc

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,9 @@ std::optional<Entity> BlendFilterContents::CreateForegroundAdvancedBlend(
338338
case BlendMode::kColor:
339339
pass.SetPipeline(renderer.GetBlendColorPipeline(options));
340340
break;
341+
case BlendMode::kPlusAdvanced:
342+
pass.SetPipeline(renderer.GetBlendPlusAdvancedPipeline(options));
343+
break;
341344
case BlendMode::kLuminosity:
342345
pass.SetPipeline(renderer.GetBlendLuminosityPipeline(options));
343346
break;
@@ -583,6 +586,7 @@ void BlendFilterContents::SetBlendMode(BlendMode blend_mode) {
583586
BLEND_CASE(Hue)
584587
BLEND_CASE(Saturation)
585588
BLEND_CASE(Color)
589+
BLEND_CASE(PlusAdvanced)
586590
BLEND_CASE(Luminosity)
587591
default:
588592
FML_UNREACHABLE();
@@ -611,19 +615,26 @@ std::optional<Entity> BlendFilterContents::RenderFilter(
611615
std::nullopt, GetAbsorbOpacity(), GetAlpha());
612616
}
613617

614-
if (blend_mode_ <= Entity::kLastPipelineBlendMode) {
615-
return PipelineBlend(inputs, renderer, entity, coverage, blend_mode_,
618+
BlendMode blend_mode = blend_mode_;
619+
if (blend_mode == BlendMode::kPlus &&
620+
!IsAlphaClampedToOne(
621+
renderer.GetContext()->GetCapabilities()->GetDefaultColorFormat())) {
622+
blend_mode = BlendMode::kPlusAdvanced;
623+
}
624+
625+
if (blend_mode <= Entity::kLastPipelineBlendMode) {
626+
return PipelineBlend(inputs, renderer, entity, coverage, blend_mode,
616627
foreground_color_, GetAbsorbOpacity(), GetAlpha());
617628
}
618629

619-
if (blend_mode_ <= Entity::kLastAdvancedBlendMode) {
630+
if (blend_mode <= Entity::kLastAdvancedBlendMode) {
620631
if (inputs.size() == 1 && foreground_color_.has_value() &&
621632
GetAbsorbOpacity() == ColorFilterContents::AbsorbOpacity::kYes) {
622633
return CreateForegroundAdvancedBlend(
623634
inputs[0], renderer, entity, coverage, foreground_color_.value(),
624-
blend_mode_, GetAlpha(), GetAbsorbOpacity());
635+
blend_mode, GetAlpha(), GetAbsorbOpacity());
625636
}
626-
return advanced_blend_proc_(inputs, renderer, entity, coverage, blend_mode_,
637+
return advanced_blend_proc_(inputs, renderer, entity, coverage, blend_mode,
627638
foreground_color_, GetAbsorbOpacity(),
628639
GetAlpha());
629640
}

impeller/entity/contents/framebuffer_blend_contents.cc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,10 @@ bool FramebufferBlendContents::Render(const ContentContext& renderer,
118118
case BlendMode::kColor:
119119
pass.SetPipeline(renderer.GetFramebufferBlendColorPipeline(options));
120120
break;
121+
case BlendMode::kPlusAdvanced:
122+
pass.SetPipeline(
123+
renderer.GetFramebufferBlendPlusAdvancedPipeline(options));
124+
break;
121125
case BlendMode::kLuminosity:
122126
pass.SetPipeline(renderer.GetFramebufferBlendLuminosityPipeline(options));
123127
break;

impeller/entity/contents/framebuffer_blend_contents.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ enum class BlendSelectValues {
2727
kHue,
2828
kSaturation,
2929
kColor,
30+
kPlusAdvanced,
3031
kLuminosity,
3132
};
3233

impeller/entity/entity_pass.cc

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -941,6 +941,13 @@ bool EntityPass::OnRender(
941941
/// Setup advanced blends.
942942
///
943943

944+
if (result.entity.GetBlendMode() == BlendMode::kPlus &&
945+
!IsAlphaClampedToOne(pass_context.GetPassTarget()
946+
.GetRenderTarget()
947+
.GetRenderTargetPixelFormat())) {
948+
result.entity.SetBlendMode(BlendMode::kPlusAdvanced);
949+
}
950+
944951
if (result.entity.GetBlendMode() > Entity::kLastPipelineBlendMode) {
945952
if (renderer.GetDeviceCapabilities().SupportsFramebufferFetch()) {
946953
auto src_contents = result.entity.GetContents();

0 commit comments

Comments
 (0)