[Impeller] migrate more AIKS test to DL. (flutter/engine#54267)

Part of https://github.com/flutter/flutter/issues/142054
This commit is contained in:
Jonah Williams 2024-08-01 11:35:18 -07:00 committed by GitHub
parent a9ee9d3a31
commit 88b005388d
2 changed files with 307 additions and 228 deletions

View File

@ -1437,152 +1437,6 @@ TEST_P(AiksTest, MatrixImageFilterMagnify) {
ASSERT_TRUE(OpenPlaygroundHere(callback));
}
// Render a white circle at the top left corner of the screen.
TEST_P(AiksTest, MatrixImageFilterDoesntCullWhenTranslatedFromOffscreen) {
Canvas canvas;
canvas.Scale(GetContentScale());
canvas.Translate({100, 100});
// Draw a circle in a SaveLayer at -300, but move it back on-screen with a
// +300 translation applied by a SaveLayer image filter.
canvas.SaveLayer({
.image_filter = std::make_shared<MatrixImageFilter>(
Matrix::MakeTranslation({300, 0}), SamplerDescriptor{}),
});
canvas.DrawCircle({-300, 0}, 100, {.color = Color::Green()});
canvas.Restore();
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
}
// Render a white circle at the top left corner of the screen.
TEST_P(AiksTest,
MatrixImageFilterDoesntCullWhenScaledAndTranslatedFromOffscreen) {
Canvas canvas;
canvas.Scale(GetContentScale());
canvas.Translate({100, 100});
// Draw a circle in a SaveLayer at -300, but move it back on-screen with a
// +300 translation applied by a SaveLayer image filter.
canvas.SaveLayer({
.image_filter = std::make_shared<MatrixImageFilter>(
Matrix::MakeTranslation({300, 0}) * Matrix::MakeScale({2, 2, 2}),
SamplerDescriptor{}),
});
canvas.DrawCircle({-150, 0}, 50, {.color = Color::Green()});
canvas.Restore();
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
}
// This should be solid red, if you see a little red box this is broken.
TEST_P(AiksTest, ClearColorOptimizationWhenSubpassIsBiggerThanParentPass) {
SetWindowSize({400, 400});
Canvas canvas;
canvas.Scale(GetContentScale());
canvas.DrawRect(Rect::MakeLTRB(200, 200, 300, 300), {.color = Color::Red()});
canvas.SaveLayer({
.image_filter = std::make_shared<MatrixImageFilter>(
Matrix::MakeScale({2, 2, 1}), SamplerDescriptor{}),
});
// Draw a rectangle that would fully cover the parent pass size, but not
// the subpass that it is rendered in.
canvas.DrawRect(Rect::MakeLTRB(0, 0, 400, 400), {.color = Color::Green()});
// Draw a bigger rectangle to force the subpass to be bigger.
canvas.DrawRect(Rect::MakeLTRB(0, 0, 800, 800), {.color = Color::Red()});
canvas.Restore();
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
}
TEST_P(AiksTest, EmptySaveLayerIgnoresPaint) {
Canvas canvas;
canvas.Scale(GetContentScale());
canvas.DrawPaint(Paint{.color = Color::Red()});
canvas.ClipRect(Rect::MakeXYWH(100, 100, 200, 200));
canvas.SaveLayer(Paint{.color = Color::Blue()});
canvas.Restore();
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
}
TEST_P(AiksTest, EmptySaveLayerRendersWithClear) {
Canvas canvas;
canvas.Scale(GetContentScale());
auto image = std::make_shared<Image>(CreateTextureForFixture("airplane.jpg"));
canvas.DrawImage(image, {10, 10}, {});
canvas.ClipRect(Rect::MakeXYWH(100, 100, 200, 200));
canvas.SaveLayer(Paint{.blend_mode = BlendMode::kClear});
canvas.Restore();
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
}
TEST_P(AiksTest, SubpassWithClearColorOptimization) {
Canvas canvas;
// Use a non-srcOver blend mode to ensure that we don't detect this as an
// opacity peephole optimization.
canvas.SaveLayer(
{.color = Color::Blue().WithAlpha(0.5), .blend_mode = BlendMode::kSource},
Rect::MakeLTRB(0, 0, 200, 200));
canvas.DrawPaint(
{.color = Color::BlackTransparent(), .blend_mode = BlendMode::kSource});
canvas.Restore();
canvas.SaveLayer(
{.color = Color::Blue(), .blend_mode = BlendMode::kDestinationOver});
canvas.Restore();
// This playground should appear blank on CI since we are only drawing
// transparent black. If the clear color optimization is broken, the texture
// will be filled with NaNs and may produce a magenta texture on macOS or iOS.
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
}
TEST_P(AiksTest, ImageColorSourceEffectTransform) {
// Compare with https://fiddle.skia.org/c/6cdc5aefb291fda3833b806ca347a885
Canvas canvas;
auto texture = CreateTextureForFixture("monkey.png");
canvas.DrawPaint({.color = Color::White()});
// Translation
{
Paint paint;
paint.color_source = ColorSource::MakeImage(
texture, Entity::TileMode::kRepeat, Entity::TileMode::kRepeat, {},
Matrix::MakeTranslation({50, 50}));
canvas.DrawRect(Rect::MakeLTRB(0, 0, 100, 100), paint);
}
// Rotation/skew
{
canvas.Save();
canvas.Rotate(Degrees(45));
Paint paint;
paint.color_source = ColorSource::MakeImage(
texture, Entity::TileMode::kRepeat, Entity::TileMode::kRepeat, {},
Matrix(1, -1, 0, 0, //
1, 1, 0, 0, //
0, 0, 1, 0, //
0, 0, 0, 1) //
);
canvas.DrawRect(Rect::MakeLTRB(100, 0, 200, 100), paint);
canvas.Restore();
}
// Scale
{
canvas.Translate(Vector2(100, 0));
canvas.Scale(Vector2(100, 100));
Paint paint;
paint.color_source = ColorSource::MakeImage(
texture, Entity::TileMode::kRepeat, Entity::TileMode::kRepeat, {},
Matrix::MakeScale(Vector2(0.005, 0.005)));
canvas.DrawRect(Rect::MakeLTRB(0, 0, 1, 1), paint);
}
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
}
TEST_P(AiksTest, CorrectClipDepthAssignedToEntities) {
Canvas canvas; // Depth 1 (base pass)
canvas.DrawRRect(Rect::MakeLTRB(0, 0, 100, 100), {10, 10}, {}); // Depth 2
@ -1630,86 +1484,6 @@ TEST_P(AiksTest, CorrectClipDepthAssignedToEntities) {
}
}
TEST_P(AiksTest, CanDrawPerspectiveTransformWithClips) {
// Avoiding `GetSecondsElapsed()` to reduce risk of golden flakiness.
int time = 0;
auto callback = [&](AiksContext& renderer) -> std::optional<Picture> {
Canvas canvas;
canvas.Save();
{
canvas.Translate({300, 300});
// 1. Draw/restore a clip before drawing the image, which will get drawn
// to the depth buffer behind the image.
canvas.Save();
{
canvas.DrawPaint({.color = Color::Green()});
canvas.ClipRect(Rect::MakeLTRB(-180, -180, 180, 180),
Entity::ClipOperation::kDifference);
canvas.DrawPaint({.color = Color::Black()});
}
canvas.Restore(); // Restore rectangle difference clip.
canvas.Save();
{
// 2. Draw an oval clip that applies to the image, which will get drawn
// in front of the image on the depth buffer.
canvas.ClipOval(Rect::MakeLTRB(-200, -200, 200, 200));
// 3. Draw the rotating image with a perspective transform.
canvas.Transform(
Matrix(1.0, 0.0, 0.0, 0.0, //
0.0, 1.0, 0.0, 0.0, //
0.0, 0.0, 1.0, 0.003, //
0.0, 0.0, 0.0, 1.0) * //
Matrix::MakeRotationY({Radians{-1.0f + (time++ / 60.0f)}}));
auto image =
std::make_shared<Image>(CreateTextureForFixture("airplane.jpg"));
canvas.DrawImage(image, -Point(image->GetSize()) / 2, {});
}
canvas.Restore(); // Restore oval intersect clip.
// 4. Draw a semi-translucent blue circle atop all previous draws.
canvas.DrawCircle({}, 230, {.color = Color::Blue().WithAlpha(0.4)});
}
canvas.Restore(); // Restore translation.
return canvas.EndRecordingAsPicture();
};
ASSERT_TRUE(OpenPlaygroundHere(callback));
}
TEST_P(AiksTest, CanRenderClippedBackdropFilter) {
Canvas canvas;
Paint paint;
canvas.Scale(GetContentScale());
// Draw something interesting in the background.
std::vector<Color> colors = {Color{0.9568, 0.2627, 0.2118, 1.0},
Color{0.1294, 0.5882, 0.9529, 1.0}};
std::vector<Scalar> stops = {
0.0,
1.0,
};
paint.color_source = ColorSource::MakeLinearGradient(
{0, 0}, {100, 100}, std::move(colors), std::move(stops),
Entity::TileMode::kRepeat, {});
canvas.DrawPaint(paint);
Rect clip_rect = Rect::MakeLTRB(50, 50, 400, 300);
// Draw a clipped SaveLayer, where the clip coverage and SaveLayer size are
// the same.
canvas.ClipRRect(clip_rect, Size(100, 100),
Entity::ClipOperation::kIntersect);
canvas.SaveLayer({}, clip_rect,
ImageFilter::MakeFromColorFilter(*ColorFilter::MakeBlend(
BlendMode::kExclusion, Color::Red())));
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
}
TEST_P(AiksTest, MipmapGenerationWorksCorrectly) {
TextureDescriptor texture_descriptor;
texture_descriptor.size = ISize{1024, 1024};

View File

@ -2,6 +2,12 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "display_list/display_list.h"
#include "display_list/dl_sampling_options.h"
#include "display_list/dl_tile_mode.h"
#include "display_list/effects/dl_color_filter.h"
#include "display_list/effects/dl_color_source.h"
#include "display_list/effects/dl_image_filter.h"
#include "flutter/impeller/aiks/aiks_unittests.h"
#include "flutter/display_list/dl_blend_mode.h"
@ -12,10 +18,17 @@
#include "flutter/impeller/geometry/scalar.h"
#include "flutter/testing/display_list_testing.h"
#include "flutter/testing/testing.h"
#include "include/core/SkMatrix.h"
namespace impeller {
namespace testing {
namespace {
SkM44 FromImpellerMatrix(const Matrix& matrix) {
return SkM44::ColMajor(matrix.m);
}
} // namespace
using namespace flutter;
TEST_P(AiksTest, CanRenderColoredRect) {
@ -334,8 +347,14 @@ TEST_P(AiksTest, CanRenderDifferentShapesWithSameColorSource) {
1.0,
};
paint.setColorSource(DlColorSource::MakeLinear({0, 0}, {100, 100}, 2, colors,
stops, DlTileMode::kRepeat));
paint.setColorSource(DlColorSource::MakeLinear(
/*start_point=*/{0, 0}, //
/*end_point=*/{100, 100}, //
/*stop_count=*/2, //
/*colors=*/colors, //
/*stops=*/stops, //
/*tile_mode=*/DlTileMode::kRepeat //
));
builder.Save();
builder.Translate(100, 100);
@ -762,5 +781,291 @@ TEST_P(AiksTest, SolidColorCirclesOvalsRRectsMaskBlurCorrectly) {
ASSERT_TRUE(OpenPlaygroundHere(dl));
}
TEST_P(AiksTest, CanRenderClippedBackdropFilter) {
DisplayListBuilder builder;
builder.Scale(GetContentScale().x, GetContentScale().y);
// Draw something interesting in the background.
std::vector<DlColor> colors = {DlColor::RGBA(0.9568, 0.2627, 0.2118, 1.0),
DlColor::RGBA(0.1294, 0.5882, 0.9529, 1.0)};
std::vector<Scalar> stops = {
0.0,
1.0,
};
DlPaint paint;
paint.setColorSource(DlColorSource::MakeLinear(
/*start_point=*/{0, 0}, //
/*end_point=*/{100, 100}, //
/*stop_count=*/2, //
/*colors=*/colors.data(), //
/*stops=*/stops.data(), //
/*tile_mode=*/DlTileMode::kRepeat //
));
builder.DrawPaint(paint);
SkRect clip_rect = SkRect::MakeLTRB(50, 50, 400, 300);
SkRRect clip_rrect = SkRRect::MakeRectXY(clip_rect, 100, 100);
// Draw a clipped SaveLayer, where the clip coverage and SaveLayer size are
// the same.
builder.ClipRRect(clip_rrect, DlCanvas::ClipOp::kIntersect);
DlPaint save_paint;
auto backdrop_filter = std::make_shared<DlColorFilterImageFilter>(
DlBlendColorFilter::Make(DlColor::kRed(), DlBlendMode::kExclusion));
builder.SaveLayer(&clip_rect, &save_paint, backdrop_filter.get());
ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
}
TEST_P(AiksTest, CanDrawPerspectiveTransformWithClips) {
// Avoiding `GetSecondsElapsed()` to reduce risk of golden flakiness.
int time = 0;
auto callback = [&]() -> sk_sp<DisplayList> {
DisplayListBuilder builder;
builder.Save();
{
builder.Translate(300, 300);
// 1. Draw/restore a clip before drawing the image, which will get drawn
// to the depth buffer behind the image.
builder.Save();
{
DlPaint paint;
paint.setColor(DlColor::kGreen());
builder.DrawPaint(paint);
builder.ClipRect(SkRect::MakeLTRB(-180, -180, 180, 180),
DlCanvas::ClipOp::kDifference);
paint.setColor(DlColor::kBlack());
builder.DrawPaint(paint);
}
builder.Restore(); // Restore rectangle difference clip.
builder.Save();
{
// 2. Draw an oval clip that applies to the image, which will get drawn
// in front of the image on the depth buffer.
builder.ClipOval(SkRect::MakeLTRB(-200, -200, 200, 200));
Matrix result =
Matrix(1.0, 0.0, 0.0, 0.0, //
0.0, 1.0, 0.0, 0.0, //
0.0, 0.0, 1.0, 0.003, //
0.0, 0.0, 0.0, 1.0) *
Matrix::MakeRotationY({Radians{-1.0f + (time++ / 60.0f)}});
// 3. Draw the rotating image with a perspective transform.
builder.Transform(FromImpellerMatrix(result));
auto image =
DlImageImpeller::Make(CreateTextureForFixture("airplane.jpg"));
auto position = -SkPoint::Make(image->dimensions().fWidth,
image->dimensions().fHeight) *
0.5;
builder.DrawImage(image, position, {});
}
builder.Restore(); // Restore oval intersect clip.
// 4. Draw a semi-translucent blue circle atop all previous draws.
DlPaint paint;
paint.setColor(DlColor::kBlue().modulateOpacity(0.4));
builder.DrawCircle({}, 230, paint);
}
builder.Restore(); // Restore translation.
return builder.Build();
};
ASSERT_TRUE(OpenPlaygroundHere(callback));
}
TEST_P(AiksTest, ImageColorSourceEffectTransform) {
// Compare with https://fiddle.skia.org/c/6cdc5aefb291fda3833b806ca347a885
DisplayListBuilder builder;
auto texture = DlImageImpeller::Make(CreateTextureForFixture("monkey.png"));
DlPaint paint;
paint.setColor(DlColor::kWhite());
builder.DrawPaint(paint);
// Translation
{
SkMatrix matrix = SkMatrix::Translate(50, 50);
DlPaint paint;
paint.setColorSource(std::make_shared<DlImageColorSource>(
texture, DlTileMode::kRepeat, DlTileMode::kRepeat,
DlImageSampling::kNearestNeighbor, &matrix));
builder.DrawRect(SkRect::MakeLTRB(0, 0, 100, 100), paint);
}
// Rotation/skew
{
builder.Save();
builder.Rotate(45);
DlPaint paint;
Matrix impeller_matrix(1, -1, 0, 0, //
1, 1, 0, 0, //
0, 0, 1, 0, //
0, 0, 0, 1);
SkMatrix matrix = SkM44::ColMajor(impeller_matrix.m).asM33();
paint.setColorSource(std::make_shared<DlImageColorSource>(
texture, DlTileMode::kRepeat, DlTileMode::kRepeat,
DlImageSampling::kNearestNeighbor, &matrix));
builder.DrawRect(SkRect::MakeLTRB(100, 0, 200, 100), paint);
builder.Restore();
}
// Scale
{
builder.Translate(100, 0);
builder.Scale(100, 100);
DlPaint paint;
SkMatrix matrix = SkMatrix::Scale(0.005, 0.005);
paint.setColorSource(std::make_shared<DlImageColorSource>(
texture, DlTileMode::kRepeat, DlTileMode::kRepeat,
DlImageSampling::kNearestNeighbor, &matrix));
builder.DrawRect(SkRect::MakeLTRB(0, 0, 1, 1), paint);
}
ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
}
TEST_P(AiksTest, SubpassWithClearColorOptimization) {
DisplayListBuilder builder;
// Use a non-srcOver blend mode to ensure that we don't detect this as an
// opacity peephole optimization.
DlPaint paint;
paint.setColor(DlColor::kBlue().modulateOpacity(0.5));
paint.setBlendMode(DlBlendMode::kSrc);
SkRect bounds = SkRect::MakeLTRB(0, 0, 200, 200);
builder.SaveLayer(&bounds, &paint);
paint.setColor(DlColor::kTransparent());
paint.setBlendMode(DlBlendMode::kSrc);
builder.DrawPaint(paint);
builder.Restore();
paint.setColor(DlColor::kBlue());
paint.setBlendMode(DlBlendMode::kDstOver);
builder.SaveLayer(nullptr, &paint);
builder.Restore();
// This playground should appear blank on CI since we are only drawing
// transparent black. If the clear color optimization is broken, the texture
// will be filled with NaNs and may produce a magenta texture on macOS or iOS.
ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
}
// Render a white circle at the top left corner of the screen.
TEST_P(AiksTest, MatrixImageFilterDoesntCullWhenTranslatedFromOffscreen) {
DisplayListBuilder builder;
builder.Scale(GetContentScale().x, GetContentScale().y);
builder.Translate(100, 100);
// Draw a circle in a SaveLayer at -300, but move it back on-screen with a
// +300 translation applied by a SaveLayer image filter.
DlPaint paint;
SkMatrix translate = SkMatrix::Translate(300, 0);
paint.setImageFilter(
DlMatrixImageFilter::Make(translate, DlImageSampling::kLinear));
builder.SaveLayer(nullptr, &paint);
DlPaint circle_paint;
circle_paint.setColor(DlColor::kGreen());
builder.DrawCircle({-300, 0}, 100, circle_paint);
builder.Restore();
ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
}
// Render a white circle at the top left corner of the screen.
TEST_P(AiksTest,
MatrixImageFilterDoesntCullWhenScaledAndTranslatedFromOffscreen) {
DisplayListBuilder builder;
builder.Scale(GetContentScale().x, GetContentScale().y);
builder.Translate(100, 100);
// Draw a circle in a SaveLayer at -300, but move it back on-screen with a
// +300 translation applied by a SaveLayer image filter.
DlPaint paint;
paint.setImageFilter(DlMatrixImageFilter::Make(
SkMatrix::Translate(300, 0) * SkMatrix::Scale(2, 2),
DlImageSampling::kNearestNeighbor));
builder.SaveLayer(nullptr, &paint);
DlPaint circle_paint;
circle_paint.setColor(DlColor::kGreen());
builder.DrawCircle({-150, 0}, 50, circle_paint);
builder.Restore();
ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
}
// This should be solid red, if you see a little red box this is broken.
TEST_P(AiksTest, ClearColorOptimizationWhenSubpassIsBiggerThanParentPass) {
SetWindowSize({400, 400});
DisplayListBuilder builder;
builder.Scale(GetContentScale().x, GetContentScale().y);
DlPaint paint;
paint.setColor(DlColor::kRed());
builder.DrawRect(SkRect::MakeLTRB(200, 200, 300, 300), paint);
paint.setImageFilter(DlMatrixImageFilter::Make(SkMatrix::Scale(2, 2),
DlImageSampling::kLinear));
builder.SaveLayer(nullptr, &paint);
// Draw a rectangle that would fully cover the parent pass size, but not
// the subpass that it is rendered in.
paint.setColor(DlColor::kGreen());
builder.DrawRect(SkRect::MakeLTRB(0, 0, 400, 400), paint);
// Draw a bigger rectangle to force the subpass to be bigger.
paint.setColor(DlColor::kRed());
builder.DrawRect(SkRect::MakeLTRB(0, 0, 800, 800), paint);
builder.Restore();
ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
}
TEST_P(AiksTest, EmptySaveLayerIgnoresPaint) {
DisplayListBuilder builder;
builder.Scale(GetContentScale().x, GetContentScale().y);
DlPaint paint;
paint.setColor(DlColor::kRed());
builder.DrawPaint(paint);
builder.ClipRect(SkRect::MakeXYWH(100, 100, 200, 200));
paint.setColor(DlColor::kBlue());
builder.SaveLayer(nullptr, &paint);
builder.Restore();
ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
}
TEST_P(AiksTest, EmptySaveLayerRendersWithClear) {
DisplayListBuilder builder;
builder.Scale(GetContentScale().x, GetContentScale().y);
auto image = DlImageImpeller::Make(CreateTextureForFixture("airplane.jpg"));
builder.DrawImage(image, {10, 10}, {});
builder.ClipRect(SkRect::MakeXYWH(100, 100, 200, 200));
DlPaint paint;
paint.setBlendMode(DlBlendMode::kClear);
builder.SaveLayer(nullptr, &paint);
builder.Restore();
ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
}
} // namespace testing
} // namespace impeller