[Impeller] Playground expanded role (#188889)
Currently the set of Playground tests in the Impeller directory is the
main way in which we test changes to the renderer. Unfortunately, these
tests don't test much code unless they are either run in the
`impeller_golden_tests` executable, or if they are run with
`--enable_playground`. Without those conditions the test harness simply
allows the caller to construct a DisplayList and then immediately
returns true without seeing if it successfully renders anything. This PR
expands the role of these Playground tests in 2 ways:
- The tests can now generate golden images directly from
`impeller_unittests` without needing a separate executable
(`impeller_golden_tests`). If you specify the flag
`--golden_output_dir=<directory>` then golden images for any test marked
as a golden test will be written there along with a digest of the files
written.
- The tests will always render their target at least once for each
Playground test, whether or not goldens are being generated or the
playground is enabled, expanding the amount of code that is tested on
each run.
Note that some tests were found to fail when you actually tried to
render something. Some of them with trivial fixes are fixed in this PR
while others which require more investigation have been deferred to
follow-on issues, such as:
The Interop RuntimeEffect test:
https://github.com/flutter/flutter/issues/188882
The StencilMask tests (with playground window):
https://github.com/flutter/flutter/issues/188884
The MassiveScalingTexture test:
https://github.com/flutter/flutter/issues/189286
The SepiaTone subpass tests:
https://github.com/flutter/flutter/issues/189287
## Pre-launch Checklist
- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [AI contribution guidelines] and understand my
responsibilities, or I am not using AI tools.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [x] I signed the [CLA].
- [ ] I listed at least one issue that this PR fixes in the description
above.
- [x] I updated/added relevant documentation (doc comments with `///`).
- [x] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [x] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [x] All existing and new tests are passing.
If you need help, consider asking for advice on the #hackers-new channel
on [Discord].
If this change needs to override an active code freeze, provide a
comment explaining why. The code freeze workflow can be overridden by
code reviewers. See pinned issues for any active code freezes with
guidance.
**Note**: The Flutter team is currently trialing the use of [Gemini Code
Assist for
GitHub](https://developers.google.com/gemini-code-assist/docs/review-github-code).
Comments from the `gemini-code-assist` bot should not be taken as
authoritative feedback from the Flutter team. If you find its comments
useful you can update your code accordingly, but if you are unsure or
disagree with the feedback, please feel free to wait for a Flutter team
member's review for guidance on which automated comments should be
addressed.
diff --git a/engine/src/flutter/display_list/testing/BUILD.gn b/engine/src/flutter/display_list/testing/BUILD.gn
index caab882..994afe0 100644
--- a/engine/src/flutter/display_list/testing/BUILD.gn
+++ b/engine/src/flutter/display_list/testing/BUILD.gn
@@ -76,7 +76,7 @@
]
if (is_mac) {
- deps += [ "//flutter/impeller/golden_tests:metal_screenshot" ]
+ deps += [ "//flutter/impeller/testing:metal_screenshot" ]
}
public_configs = [ ":surface_provider_config" ]
@@ -123,6 +123,8 @@
deps += [
"//flutter/impeller/display_list",
"//flutter/impeller/playground",
+ "//flutter/impeller/testing:metal_screenshot",
+ "//flutter/impeller/testing:screenshot",
]
}
deps += [ "//flutter/testing:metal" ]
diff --git a/engine/src/flutter/display_list/testing/impeller/dl_test_surface_instance_impeller.cc b/engine/src/flutter/display_list/testing/impeller/dl_test_surface_instance_impeller.cc
index 8ad5c0e..4e676c5 100644
--- a/engine/src/flutter/display_list/testing/impeller/dl_test_surface_instance_impeller.cc
+++ b/engine/src/flutter/display_list/testing/impeller/dl_test_surface_instance_impeller.cc
@@ -8,6 +8,8 @@
#include "flutter/fml/safe_math.h"
#include "flutter/impeller/display_list/dl_dispatcher.h"
#include "flutter/impeller/display_list/dl_image_impeller.h"
+#include "flutter/impeller/testing/screenshot.h"
+#include "flutter/impeller/testing/screenshotter.h"
#include "flutter/impeller/typographer/backends/skia/typographer_context_skia.h"
namespace {
@@ -123,9 +125,10 @@
std::unique_ptr<DlPixelData> DlSurfaceInstanceImpeller::SnapshotToPixelData()
const {
+ std::shared_ptr<impeller::Context> context = aiks_context_.GetContext();
std::unique_ptr<impeller::testing::Screenshot> snapshot =
- snapshotter_.MakeScreenshot(aiks_context_,
- GetRenderTarget().GetRenderTargetTexture());
+ impeller::testing::Screenshotter::MakeScreenshot(
+ context, GetRenderTarget().GetRenderTargetTexture());
return snapshot ? std::make_unique<DlImpellerPixelData>(std::move(snapshot))
: nullptr;
}
@@ -169,8 +172,5 @@
DlSurfaceInstanceImpeller::typographer_context_ =
impeller::TypographerContextSkia::Make();
-impeller::testing::MetalScreenshotter DlSurfaceInstanceImpeller::snapshotter_ =
- impeller::testing::MetalScreenshotter(impeller::PlaygroundSwitches());
-
} // namespace testing
} // namespace flutter
diff --git a/engine/src/flutter/display_list/testing/impeller/dl_test_surface_instance_impeller.h b/engine/src/flutter/display_list/testing/impeller/dl_test_surface_instance_impeller.h
index 5460af1..c7dea95 100644
--- a/engine/src/flutter/display_list/testing/impeller/dl_test_surface_instance_impeller.h
+++ b/engine/src/flutter/display_list/testing/impeller/dl_test_surface_instance_impeller.h
@@ -9,9 +9,9 @@
#include "flutter/display_list/dl_builder.h"
#include "flutter/impeller/display_list/aiks_context.h"
-#include "flutter/impeller/golden_tests/metal_screenshotter.h"
#include "flutter/impeller/playground/playground_impl.h"
#include "flutter/impeller/renderer/surface.h"
+#include "flutter/impeller/testing/metal/metal_screenshotter.h"
#include "flutter/impeller/typographer/typographer_context.h"
namespace flutter {
@@ -70,7 +70,6 @@
void DoRenderDisplayList(const sk_sp<DisplayList>& display_list);
static std::shared_ptr<impeller::TypographerContext> typographer_context_;
- static impeller::testing::MetalScreenshotter snapshotter_;
};
} // namespace testing
diff --git a/engine/src/flutter/impeller/BUILD.gn b/engine/src/flutter/impeller/BUILD.gn
index 0f8b679..b9416e8 100644
--- a/engine/src/flutter/impeller/BUILD.gn
+++ b/engine/src/flutter/impeller/BUILD.gn
@@ -49,12 +49,27 @@
}
}
+source_set("impeller_unittesting") {
+ testonly = true
+
+ # Informs run_all_unittests to perform impeller specific initialization
+ defines = [ "IMPELLER_UNITTEST_SUITE=1" ]
+
+ sources = [ "../testing/run_all_unittests.cc" ]
+
+ deps = [
+ "testing:impeller_unittest_suite",
+ "//flutter/testing:testing_lib",
+ ]
+}
+
impeller_component("impeller_unittests") {
target_type = "executable"
testonly = true
deps = [
+ ":impeller_unittesting",
"base:base_unittests",
"compiler:compiler_unittests",
"core:allocator_unittests",
@@ -63,6 +78,7 @@
"runtime_stage:runtime_stage_unittests",
"shader_archive:shader_archive_unittests",
"tessellator:tessellator_unittests",
+ "testing:impeller_unittest_suite",
"toolkit/interop:interop_unittests",
]
@@ -107,6 +123,9 @@
testonly = true
- deps = [ "renderer:renderer_dart_unittests" ]
+ deps = [
+ "renderer:renderer_dart_unittests",
+ "//flutter/testing",
+ ]
}
}
diff --git a/engine/src/flutter/impeller/base/BUILD.gn b/engine/src/flutter/impeller/base/BUILD.gn
index eb7c3d5..85b1bb5 100644
--- a/engine/src/flutter/impeller/base/BUILD.gn
+++ b/engine/src/flutter/impeller/base/BUILD.gn
@@ -45,6 +45,6 @@
]
deps = [
":base",
- "//flutter/testing",
+ "//flutter/testing:testing_lib",
]
}
diff --git a/engine/src/flutter/impeller/display_list/BUILD.gn b/engine/src/flutter/impeller/display_list/BUILD.gn
index 2911737..d3aa29b 100644
--- a/engine/src/flutter/impeller/display_list/BUILD.gn
+++ b/engine/src/flutter/impeller/display_list/BUILD.gn
@@ -117,8 +117,8 @@
":display_list",
"../playground:playground_test",
"//flutter/display_list/testing:display_list_testing",
- "//flutter/impeller/golden_tests:screenshot",
"//flutter/impeller/renderer/testing:mocks",
+ "//flutter/impeller/testing:screenshot",
"//flutter/txt",
]
if (defined(invoker.public_configs)) {
diff --git a/engine/src/flutter/impeller/display_list/aiks_dl_basic_unittests.cc b/engine/src/flutter/impeller/display_list/aiks_dl_basic_unittests.cc
index 75814ba..62276d6 100644
--- a/engine/src/flutter/impeller/display_list/aiks_dl_basic_unittests.cc
+++ b/engine/src/flutter/impeller/display_list/aiks_dl_basic_unittests.cc
@@ -482,8 +482,8 @@
Scalar bottom_left = 60.f;
Scalar bottom_right = 80.f;
auto callback = [&]() -> sk_sp<DisplayList> {
- if (AiksTest::ImGuiBegin("Controls", nullptr,
- ImGuiWindowFlags_AlwaysAutoResize)) {
+ if (IsPlaygroundEnabled()) {
+ ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
ImGui::SliderFloat("top_left", &top_left, 0, 250);
ImGui::SliderFloat("top_right", &top_right, 0, 250);
ImGui::SliderFloat("bottom_left", &bottom_left, 0, 250);
@@ -738,8 +738,8 @@
static float stroked_alpha = 255.0;
static float stroked_scale[2] = {1.0, 1.0};
- if (AiksTest::ImGuiBegin("Controls", nullptr,
- ImGuiWindowFlags_AlwaysAutoResize)) {
+ if (IsPlaygroundEnabled()) {
+ ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
ImGui::SliderFloat("Stroked Radius", &stroked_radius, 0, 500);
ImGui::SliderFloat("Stroked Width", &stroke_width, 0, 500);
ImGui::SliderFloat("Stroked Width Fine", &stroke_width_fine, 0, 5);
@@ -2328,9 +2328,17 @@
// exceed the max texture size. See
// https://github.com/flutter/flutter/issues/128912
TEST_P(AiksTest, MassiveScalingMatrixImageFilter) {
- if (GetBackend() == PlaygroundBackend::kVulkan) {
- GTEST_SKIP() << "Swiftshader is running out of memory on this example.";
+ switch (GetBackend()) {
+ case PlaygroundBackend::kMetal:
+ case PlaygroundBackend::kMetalSDF:
+ break;
+ case PlaygroundBackend::kOpenGLES:
+ case PlaygroundBackend::kOpenGLESSDF:
+ case PlaygroundBackend::kVulkan:
+ GTEST_SKIP() << "Platform is running out of memory on this example "
+ << "(see https://github.com/flutter/flutter/issues/189286).";
}
+
DisplayListBuilder builder(DlRect::MakeSize(DlSize(1000, 1000)));
auto filter = DlImageFilter::MakeMatrix(
@@ -2355,8 +2363,8 @@
Scalar corner = 1.f;
bool stroked = true;
auto callback = [&]() -> sk_sp<DisplayList> {
- if (AiksTest::ImGuiBegin("Controls", nullptr,
- ImGuiWindowFlags_AlwaysAutoResize)) {
+ if (IsPlaygroundEnabled()) {
+ ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
ImGui::SliderFloat("width", &width, 0, 200);
ImGui::SliderFloat("height", &height, 0, 200);
ImGui::SliderFloat("corner", &corner, 0, 1);
@@ -2435,8 +2443,8 @@
bool diff_clip = false;
auto callback = [&]() -> sk_sp<DisplayList> {
- if (AiksTest::ImGuiBegin("Controls", nullptr,
- ImGuiWindowFlags_AlwaysAutoResize)) {
+ if (IsPlaygroundEnabled()) {
+ ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
ImGui::SliderInt("perspective%", &perspective, 0, 100);
ImGui::Checkbox("use clip", &use_clip);
if (use_clip) {
diff --git a/engine/src/flutter/impeller/display_list/aiks_dl_blend_unittests.cc b/engine/src/flutter/impeller/display_list/aiks_dl_blend_unittests.cc
index d890b77..f3e501c 100644
--- a/engine/src/flutter/impeller/display_list/aiks_dl_blend_unittests.cc
+++ b/engine/src/flutter/impeller/display_list/aiks_dl_blend_unittests.cc
@@ -198,8 +198,8 @@
TEST_P(AiksTest, ColorFilterBlend) {
bool has_color_filter = true;
auto callback = [&]() -> sk_sp<DisplayList> {
- if (AiksTest::ImGuiBegin("Controls", nullptr,
- ImGuiWindowFlags_AlwaysAutoResize)) {
+ if (IsPlaygroundEnabled()) {
+ ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
ImGui::Checkbox("has color filter", &has_color_filter);
ImGui::End();
}
@@ -250,8 +250,8 @@
TEST_P(AiksTest, ColorFilterAdvancedBlend) {
bool has_color_filter = true;
auto callback = [&]() -> sk_sp<DisplayList> {
- if (AiksTest::ImGuiBegin("Controls", nullptr,
- ImGuiWindowFlags_AlwaysAutoResize)) {
+ if (IsPlaygroundEnabled()) {
+ ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
ImGui::Checkbox("has color filter", &has_color_filter);
ImGui::End();
}
@@ -311,9 +311,6 @@
GTEST_SKIP()
<< "This backend doesn't yet support setting device capabilities.";
}
- if (!WillRenderSomething()) {
- GTEST_SKIP() << "This test requires playgrounds.";
- }
std::shared_ptr<const Capabilities> old_capabilities =
GetContext()->GetCapabilities();
@@ -343,8 +340,8 @@
bool has_color_filter = true;
auto callback = [&]() -> sk_sp<DisplayList> {
- if (AiksTest::ImGuiBegin("Controls", nullptr,
- ImGuiWindowFlags_AlwaysAutoResize)) {
+ if (IsPlaygroundEnabled()) {
+ ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
ImGui::Checkbox("has color filter", &has_color_filter);
ImGui::End();
}
@@ -509,13 +506,14 @@
ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
}
-static sk_sp<DisplayList> BlendModeTest(Vector2 content_scale,
+static sk_sp<DisplayList> BlendModeTest(AiksTest* test,
+ Vector2 content_scale,
BlendMode blend_mode,
const sk_sp<DlImageImpeller>& src_image,
const sk_sp<DlImageImpeller>& dst_image,
Scalar src_alpha) {
- if (AiksTest::ImGuiBegin("Controls", nullptr,
- ImGuiWindowFlags_AlwaysAutoResize)) {
+ if (test->IsPlaygroundEnabled()) {
+ ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
ImGui::SliderFloat("Source alpha", &src_alpha, 0, 1);
ImGui::End();
}
@@ -670,7 +668,7 @@
auto dst_image = \
DlImageImpeller::Make(CreateTextureForFixture("blend_mode_dst.png")); \
auto callback = [&]() -> sk_sp<DisplayList> { \
- return BlendModeTest(GetContentScale(), BlendMode::k##blend_mode, \
+ return BlendModeTest(this, GetContentScale(), BlendMode::k##blend_mode, \
src_image, dst_image, /*src_alpha=*/1.0); \
}; \
OpenPlaygroundHere(callback); \
@@ -684,7 +682,7 @@
auto dst_image = \
DlImageImpeller::Make(CreateTextureForFixture("blend_mode_dst.png")); \
auto callback = [&]() -> sk_sp<DisplayList> { \
- return BlendModeTest(GetContentScale(), BlendMode::k##blend_mode, \
+ return BlendModeTest(this, GetContentScale(), BlendMode::k##blend_mode, \
src_image, dst_image, /*src_alpha=*/0.5); \
}; \
OpenPlaygroundHere(callback); \
@@ -699,8 +697,8 @@
static Color foreground = Color::Color::OrangeRed().WithAlpha(0.5);
static int current_blend_index = 3;
- if (AiksTest::ImGuiBegin("Controls", nullptr,
- ImGuiWindowFlags_AlwaysAutoResize)) {
+ if (IsPlaygroundEnabled()) {
+ ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
ImGui::ColorEdit4("Background", reinterpret_cast<float*>(&background));
ImGui::ColorEdit4("Foreground", reinterpret_cast<float*>(&foreground));
ImGui::ListBox("Blend mode", ¤t_blend_index,
@@ -835,8 +833,8 @@
static DlColor color1 = DlColor::kGreen();
static DlColor color2 = DlColor::kBlue();
- if (AiksTest::ImGuiBegin("Controls", nullptr,
- ImGuiWindowFlags_AlwaysAutoResize)) {
+ if (IsPlaygroundEnabled()) {
+ ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
ImGui::Checkbox("Cache the wheel", &cache_the_wheel);
ImGui::ListBox("Blending mode", ¤t_blend_index,
blend_modes.blend_mode_names.data(),
diff --git a/engine/src/flutter/impeller/display_list/aiks_dl_blur_unittests.cc b/engine/src/flutter/impeller/display_list/aiks_dl_blur_unittests.cc
index f6da6fe..30368a7 100644
--- a/engine/src/flutter/impeller/display_list/aiks_dl_blur_unittests.cc
+++ b/engine/src/flutter/impeller/display_list/aiks_dl_blur_unittests.cc
@@ -531,8 +531,8 @@
TEST_P(AiksTest, BlurHasNoEdge) {
Scalar sigma = 47.6;
auto callback = [&]() -> sk_sp<DisplayList> {
- if (AiksTest::ImGuiBegin("Controls", nullptr,
- ImGuiWindowFlags_AlwaysAutoResize)) {
+ if (IsPlaygroundEnabled()) {
+ ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
ImGui::SliderFloat("Sigma", &sigma, 0, 50);
ImGui::End();
}
@@ -828,8 +828,8 @@
TEST_P(AiksTest, MaskBlurTexture) {
Scalar sigma = 30;
auto callback = [&]() -> sk_sp<DisplayList> {
- if (AiksTest::ImGuiBegin("Controls", nullptr,
- ImGuiWindowFlags_AlwaysAutoResize)) {
+ if (IsPlaygroundEnabled()) {
+ ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
ImGui::SliderFloat("Sigma", &sigma, 0, 500);
ImGui::End();
}
@@ -857,8 +857,8 @@
TEST_P(AiksTest, MaskBlurDoesntStretchContents) {
Scalar sigma = 70;
auto callback = [&]() -> sk_sp<DisplayList> {
- if (AiksTest::ImGuiBegin("Controls", nullptr,
- ImGuiWindowFlags_AlwaysAutoResize)) {
+ if (IsPlaygroundEnabled()) {
+ ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
ImGui::SliderFloat("Sigma", &sigma, 0, 500);
ImGui::End();
}
@@ -959,8 +959,8 @@
Scalar freq = 0.1;
Scalar amp = 50.0;
auto callback = [&]() -> sk_sp<DisplayList> {
- if (AiksTest::ImGuiBegin("Controls", nullptr,
- ImGuiWindowFlags_AlwaysAutoResize)) {
+ if (IsPlaygroundEnabled()) {
+ ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
ImGui::SliderFloat("Sigma", &sigma, 0, 200);
ImGui::SliderFloat("Frequency", &freq, 0.01, 2.0);
ImGui::SliderFloat("Amplitude", &, 1, 100);
@@ -1148,8 +1148,8 @@
static float scale = 0.6;
static int selected_tile_mode = 3;
- if (AiksTest::ImGuiBegin("Controls", nullptr,
- ImGuiWindowFlags_AlwaysAutoResize)) {
+ if (IsPlaygroundEnabled()) {
+ ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
ImGui::SliderFloat("Rotation (degrees)", &rotation, -180, 180);
ImGui::SliderFloat("Scale", &scale, 0, 2.0);
ImGui::Combo("Tile mode", &selected_tile_mode, tile_mode_names,
@@ -1259,8 +1259,8 @@
static float scale = 0.6;
static int selected_tile_mode = 3;
- if (AiksTest::ImGuiBegin("Controls", nullptr,
- ImGuiWindowFlags_AlwaysAutoResize)) {
+ if (IsPlaygroundEnabled()) {
+ ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
ImGui::SliderFloat("Rotation (degrees)", &rotation, -180, 180);
ImGui::SliderFloat("Scale", &scale, 0, 2.0);
ImGui::Combo("Tile mode", &selected_tile_mode, tile_mode_names,
diff --git a/engine/src/flutter/impeller/display_list/aiks_dl_gradient_unittests.cc b/engine/src/flutter/impeller/display_list/aiks_dl_gradient_unittests.cc
index fab952b..27dbad6 100644
--- a/engine/src/flutter/impeller/display_list/aiks_dl_gradient_unittests.cc
+++ b/engine/src/flutter/impeller/display_list/aiks_dl_gradient_unittests.cc
@@ -430,8 +430,8 @@
static int selected_tile_mode = 0;
static Matrix matrix;
- if (AiksTest::ImGuiBegin("Controls", nullptr,
- ImGuiWindowFlags_AlwaysAutoResize)) {
+ if (IsPlaygroundEnabled()) {
+ ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
ImGui::Combo("Tile mode", &selected_tile_mode, tile_mode_names,
sizeof(tile_mode_names) / sizeof(char*));
std::string label = "##1";
@@ -501,8 +501,8 @@
static int selected_tile_mode = 0;
static Matrix matrix;
- if (AiksTest::ImGuiBegin("Controls", nullptr,
- ImGuiWindowFlags_AlwaysAutoResize)) {
+ if (IsPlaygroundEnabled()) {
+ ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
ImGui::Combo("Tile mode", &selected_tile_mode, tile_mode_names,
sizeof(tile_mode_names) / sizeof(char*));
std::string label = "##1";
@@ -546,8 +546,8 @@
0, 0, 1, 0, //
0, 0, 0, 1 //
};
- if (AiksTest::ImGuiBegin("Controls", nullptr,
- ImGuiWindowFlags_AlwaysAutoResize)) {
+ if (IsPlaygroundEnabled()) {
+ ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
ImGui::Combo("Tile mode", &selected_tile_mode, tile_mode_names,
sizeof(tile_mode_names) / sizeof(char*));
std::string label = "##1";
@@ -762,8 +762,8 @@
static int selected_tile_mode = 0;
static float alpha = 1;
- if (AiksTest::ImGuiBegin("Controls", nullptr,
- ImGuiWindowFlags_AlwaysAutoResize)) {
+ if (IsPlaygroundEnabled()) {
+ ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
ImGui::SliderFloat("Scale", &scale, 0, 6);
ImGui::Checkbox("Circle clip", &add_circle_clip);
ImGui::SliderFloat("Alpha", &alpha, 0, 1);
diff --git a/engine/src/flutter/impeller/display_list/aiks_dl_path_unittests.cc b/engine/src/flutter/impeller/display_list/aiks_dl_path_unittests.cc
index 0aef34a..7204fa7 100644
--- a/engine/src/flutter/impeller/display_list/aiks_dl_path_unittests.cc
+++ b/engine/src/flutter/impeller/display_list/aiks_dl_path_unittests.cc
@@ -276,8 +276,8 @@
bool invert_colors = false;
auto callback = [=]() mutable -> sk_sp<DisplayList> {
- if (AiksTest::ImGuiBegin("Controls", nullptr,
- ImGuiWindowFlags_AlwaysAutoResize)) {
+ if (test->IsPlaygroundEnabled()) {
+ ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
ImGui::SliderFloat("Scale", &scale, 0, 6);
ImGui::SliderFloat("Rotate", &rotation, 0, 90);
ImGui::SliderFloat("Offset", &offset, 0, 2);
@@ -524,8 +524,8 @@
static float scale = 3;
static bool add_circle_clip = true;
- if (AiksTest::ImGuiBegin("Controls", nullptr,
- ImGuiWindowFlags_AlwaysAutoResize)) {
+ if (IsPlaygroundEnabled()) {
+ ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
ImGui::ColorEdit4("Color", reinterpret_cast<float*>(&color));
ImGui::SliderFloat("Scale", &scale, 0, 6);
ImGui::Checkbox("Circle clip", &add_circle_clip);
@@ -675,13 +675,15 @@
TEST_P(AiksTest, ScaleExperimentAntialiasLines) {
// Must be called before any methods that use the context to ensure that
// this test is run with the antialias flag.
- EnsureContextSupportsAntialiasLines();
+ if (!EnsureContextSupportsAntialiasLines()) {
+ GTEST_SKIP() << "This backend doesn't yet support experimental AA lines.";
+ }
Scalar scale = 5.0;
Scalar line_width = 10.f;
auto callback = [&]() -> sk_sp<DisplayList> {
- if (AiksTest::ImGuiBegin("Controls", nullptr,
- ImGuiWindowFlags_AlwaysAutoResize)) {
+ if (IsPlaygroundEnabled()) {
+ ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
ImGui::SliderFloat("Scale", &scale, 0.001, 5);
ImGui::SliderFloat("Width", &line_width, 1, 20);
@@ -731,15 +733,17 @@
TEST_P(AiksTest, HexagonExperimentAntialiasLines) {
// Must be called before any methods that use the context to ensure that
// this test is run with the antialias flag.
- EnsureContextSupportsAntialiasLines();
+ if (!EnsureContextSupportsAntialiasLines()) {
+ GTEST_SKIP() << "This backend doesn't yet support experimental AA lines.";
+ }
float scale = 5.0f;
float line_width = 10.f;
float rotation = 0.f;
auto callback = [&]() -> sk_sp<DisplayList> {
- if (AiksTest::ImGuiBegin("Controls", nullptr,
- ImGuiWindowFlags_AlwaysAutoResize)) {
+ if (IsPlaygroundEnabled()) {
+ ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
// Use ImGui::SliderFloat for consistency
ImGui::SliderFloat("Scale", &scale, 0.001f, 5.0f);
ImGui::SliderFloat("Width", &line_width, 1.0f, 20.0f);
@@ -798,7 +802,9 @@
TEST_P(AiksTest, SimpleExperimentAntialiasLines) {
// Must be called before any methods that use the context to ensure that
// this test is run with the antialias flag.
- EnsureContextSupportsAntialiasLines();
+ if (!EnsureContextSupportsAntialiasLines()) {
+ GTEST_SKIP() << "This backend doesn't yet support experimental AA lines.";
+ }
DisplayListBuilder builder;
builder.Scale(GetContentScale().x, GetContentScale().y);
@@ -937,8 +943,8 @@
DlScalar start_angle = 0;
DlScalar end_angle = 90;
auto callback = [&]() -> sk_sp<DisplayList> {
- if (AiksTest::ImGuiBegin("Controls", nullptr,
- ImGuiWindowFlags_AlwaysAutoResize)) {
+ if (IsPlaygroundEnabled()) {
+ ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
ImGui::SliderFloat("Stroke Width", &stroke_width, 1, 300);
ImGui::SliderFloat("Aspect", &aspect, 0.5, 2.0);
ImGui::SliderFloat("Start Angle", &start_angle, 0, 360);
diff --git a/engine/src/flutter/impeller/display_list/aiks_dl_primitive_shape_unittests.cc b/engine/src/flutter/impeller/display_list/aiks_dl_primitive_shape_unittests.cc
index 8e1e3aa..2399b69 100644
--- a/engine/src/flutter/impeller/display_list/aiks_dl_primitive_shape_unittests.cc
+++ b/engine/src/flutter/impeller/display_list/aiks_dl_primitive_shape_unittests.cc
@@ -145,9 +145,13 @@
// estimate the pixel size can get complicated in a shader that is performing
// some of its operations on local space values and some in device space.
TEST_P(AiksTest, PrimitiveShapePlayground) {
- if (IsGoldenTest()) {
- GTEST_SKIP() << "PrimitiveShapePlayground does not produce a golden image";
+ if (!IsPlaygroundEnabled()) {
+ GTEST_SKIP() << "PrimitiveShapePlayground is only for on-screen use";
}
+ // In the odd case that we are being run with a playground and the
+ // golden flag set, we don't want to write out the golden either.
+ // Just do the playground part...
+ SetEnableWriteGolden(false);
RenderParameters params{
.render_type = RenderType::kRectangle,
@@ -159,36 +163,35 @@
int render_type_index = static_cast<int>(RenderType::kRectangle);
auto callback = [&]() -> sk_sp<DisplayList> {
- if (AiksTest::ImGuiBegin("Controls", nullptr,
- ImGuiWindowFlags_AlwaysAutoResize)) {
- ImGui::SliderFloat("Stroke", ¶ms.stroke_width, 0.0f, 30.0f);
- ImGui::SliderFloat("X Scale", ¶ms.scale_x, 1.0f, 3.0f);
- ImGui::SliderFloat("Y Scale", ¶ms.scale_y, 1.0f, 3.0f);
- ImGui::SliderFloat("X Skew", ¶ms.skew_x, 0.0f, 1.0f);
- ImGui::SliderFloat("Y Skew", ¶ms.skew_y, 0.0f, 1.0f);
- ImGui::SliderFloat("Rotation", ¶ms.degrees, 0.0f, 360.0f);
- ImGui::ListBox(
- "Shape Type", &render_type_index,
- [](void* data, int index) {
- switch (static_cast<RenderType>(index)) {
- case RenderType::kSquare:
- return "Square";
- case RenderType::kRectangle:
- return "Rectangle";
- case RenderType::kCircle:
- return "Circle";
- case RenderType::kOval:
- return "Oval";
- case RenderType::kLine:
- return "Line";
- case RenderType::kValidCount:
- case RenderType::kInvalid:
- FML_UNREACHABLE();
- }
- },
- nullptr, static_cast<int>(RenderType::kValidCount), -1);
- ImGui::End();
- }
+ FML_CHECK(IsPlaygroundEnabled());
+ ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
+ ImGui::SliderFloat("Stroke", ¶ms.stroke_width, 0.0f, 30.0f);
+ ImGui::SliderFloat("X Scale", ¶ms.scale_x, 1.0f, 3.0f);
+ ImGui::SliderFloat("Y Scale", ¶ms.scale_y, 1.0f, 3.0f);
+ ImGui::SliderFloat("X Skew", ¶ms.skew_x, 0.0f, 1.0f);
+ ImGui::SliderFloat("Y Skew", ¶ms.skew_y, 0.0f, 1.0f);
+ ImGui::SliderFloat("Rotation", ¶ms.degrees, 0.0f, 360.0f);
+ ImGui::ListBox(
+ "Shape Type", &render_type_index,
+ [](void* data, int index) {
+ switch (static_cast<RenderType>(index)) {
+ case RenderType::kSquare:
+ return "Square";
+ case RenderType::kRectangle:
+ return "Rectangle";
+ case RenderType::kCircle:
+ return "Circle";
+ case RenderType::kOval:
+ return "Oval";
+ case RenderType::kLine:
+ return "Line";
+ case RenderType::kValidCount:
+ case RenderType::kInvalid:
+ FML_UNREACHABLE();
+ }
+ },
+ nullptr, static_cast<int>(RenderType::kValidCount), -1);
+ ImGui::End();
// Translate our "Gui int variable" to the appropriate enum field value.
params.render_type = static_cast<RenderType>(render_type_index);
diff --git a/engine/src/flutter/impeller/display_list/aiks_dl_runtime_effect_unittests.cc b/engine/src/flutter/impeller/display_list/aiks_dl_runtime_effect_unittests.cc
index 73a4cd5..cf20da9 100644
--- a/engine/src/flutter/impeller/display_list/aiks_dl_runtime_effect_unittests.cc
+++ b/engine/src/flutter/impeller/display_list/aiks_dl_runtime_effect_unittests.cc
@@ -52,6 +52,12 @@
// Regression test for https://github.com/flutter/flutter/issues/126701 .
TEST_P(AiksTest, CanRenderClippedRuntimeEffects) {
+ // This test used to be excluded on Vulkan for impeller_golden_tests
+ // but has been running fine so far with the playground-based golden
+ // mechanism, so we will let it run as the underlying problem may
+ // have been fixed since the exclusion was added.
+ // https://github.com/flutter/flutter/blame/ad80825c24d770a19e33f67800fc0338a3b89ec7/engine/src/flutter/impeller/golden_tests/golden_playground_test_mac.cc#L109
+
struct FragUniforms {
Vector2 iResolution;
Scalar iTime;
@@ -223,8 +229,8 @@
bool compare = false;
auto callback = [&]() -> sk_sp<DisplayList> {
- if (AiksTest::ImGuiBegin("Controls", nullptr,
- ImGuiWindowFlags_AlwaysAutoResize)) {
+ if (IsPlaygroundEnabled()) {
+ ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
ImGui::SliderFloat("xoffset", &xoffset, -50, 50);
ImGui::SliderFloat("yoffset", &yoffset, -50, 50);
ImGui::SliderFloat("xscale", &xscale, 0, 1);
@@ -306,8 +312,8 @@
Scalar sigma = 20.0;
auto callback = [&]() -> sk_sp<DisplayList> {
- if (AiksTest::ImGuiBegin("Controls", nullptr,
- ImGuiWindowFlags_AlwaysAutoResize)) {
+ if (IsPlaygroundEnabled()) {
+ ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
ImGui::SliderFloat("sigma", &sigma, 0, 20);
ImGui::End();
}
@@ -370,8 +376,8 @@
Scalar sigma = 5.0;
auto callback = [&]() -> sk_sp<DisplayList> {
- if (AiksTest::ImGuiBegin("Controls", nullptr,
- ImGuiWindowFlags_AlwaysAutoResize)) {
+ if (IsPlaygroundEnabled()) {
+ ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
ImGui::SliderFloat("sigma", &sigma, 0, 20);
ImGui::End();
}
@@ -436,8 +442,8 @@
Vector2 circle_origin = Vector2(30.f, 30.f);
auto callback = [&]() -> sk_sp<DisplayList> {
- if (AiksTest::ImGuiBegin("Controls", nullptr,
- ImGuiWindowFlags_AlwaysAutoResize)) {
+ if (IsPlaygroundEnabled()) {
+ ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
ImGui::SliderFloat("sigma", &sigma, 0, 20);
ImGui::SliderFloat("clip_x", &clip_origin.x, 0, 2048.f);
ImGui::SliderFloat("clip_y", &clip_origin.y, 0, 1536.f);
@@ -584,8 +590,8 @@
Scalar rotation = 45;
auto callback = [&]() -> sk_sp<DisplayList> {
- if (AiksTest::ImGuiBegin("Controls", nullptr,
- ImGuiWindowFlags_AlwaysAutoResize)) {
+ if (IsPlaygroundEnabled()) {
+ ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
ImGui::SliderFloat("rotation", &rotation, 0, 360);
ImGui::End();
}
diff --git a/engine/src/flutter/impeller/display_list/aiks_dl_text_unittests.cc b/engine/src/flutter/impeller/display_list/aiks_dl_text_unittests.cc
index b4c3c4e..548c525 100644
--- a/engine/src/flutter/impeller/display_list/aiks_dl_text_unittests.cc
+++ b/engine/src/flutter/impeller/display_list/aiks_dl_text_unittests.cc
@@ -198,8 +198,8 @@
TEST_P(AiksTest, MassiveScaleConvertToPath) {
Scalar scale = 16.0;
auto callback = [&]() -> sk_sp<DisplayList> {
- if (AiksTest::ImGuiBegin("Controls", nullptr,
- ImGuiWindowFlags_AlwaysAutoResize)) {
+ if (IsPlaygroundEnabled()) {
+ ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
ImGui::SliderFloat("Scale", &scale, 4, 20);
ImGui::End();
}
@@ -227,8 +227,8 @@
Scalar offsetx = -500.0;
Scalar offsety = 700.0;
auto callback = [&]() -> sk_sp<DisplayList> {
- if (AiksTest::ImGuiBegin("Controls", nullptr,
- ImGuiWindowFlags_AlwaysAutoResize)) {
+ if (IsPlaygroundEnabled()) {
+ ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
ImGui::SliderFloat("scale", &scale, 1.f, 300.f);
ImGui::SliderFloat("offsetx", &offsetx, -600.f, 100.f);
ImGui::SliderFloat("offsety", &offsety, 600.f, 2048.f);
@@ -255,8 +255,8 @@
Scalar fine_scale = 0.f;
bool is_subpixel = false;
auto callback = [&]() -> sk_sp<DisplayList> {
- if (AiksTest::ImGuiBegin("Controls", nullptr,
- ImGuiWindowFlags_AlwaysAutoResize)) {
+ if (IsPlaygroundEnabled()) {
+ ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
ImGui::SliderFloat("Fine Scale", &fine_scale, -1, 1);
ImGui::Checkbox("subpixel", &is_subpixel);
ImGui::End();
@@ -285,8 +285,8 @@
float foffset[2] = {200, 200};
auto callback = [&]() -> sk_sp<DisplayList> {
- if (AiksTest::ImGuiBegin("Controls", nullptr,
- ImGuiWindowFlags_AlwaysAutoResize)) {
+ if (IsPlaygroundEnabled()) {
+ ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
ImGui::SliderFloat("pivotx", &fpivot[0], 0, 300);
ImGui::SliderFloat("pivoty", &fpivot[1], 0, 300);
ImGui::SliderFloat("rotation", &rotation, 0, 360);
@@ -328,8 +328,8 @@
static float phase_variation = 0.2;
static float speed = 0.5;
static float magnitude = 100;
- if (AiksTest::ImGuiBegin("Controls", nullptr,
- ImGuiWindowFlags_AlwaysAutoResize)) {
+ if (IsPlaygroundEnabled()) {
+ ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
ImGui::SliderFloat("Font size", &font_size, 5, 50);
ImGui::SliderFloat("Phase variation", &phase_variation, 0, 1);
ImGui::SliderFloat("Oscillation speed", &speed, 0, 2);
@@ -489,6 +489,16 @@
}
TEST_P(AiksTest, TextRotated) {
+ // This test used to be intentionally excluded for impeller_golden_tests
+ // but when the SDF backend variants were added, the explicit exclusions
+ // for those backends were not added to the exclusion list. So it has
+ // actually been running just fine for a while now generating goldens
+ // for the SDF backends.
+ // We will let it run on all backends now in the playground-based golden
+ // mechanism as the underlying flakiness may have been resolved since the
+ // exclusion was added.
+ // https://github.com/flutter/flutter/blame/ad80825c24d770a19e33f67800fc0338a3b89ec7/engine/src/flutter/impeller/golden_tests/golden_playground_test_mac.cc#L107
+
DisplayListBuilder builder;
builder.Scale(GetContentScale().x, GetContentScale().y);
diff --git a/engine/src/flutter/impeller/display_list/aiks_dl_unittests.cc b/engine/src/flutter/impeller/display_list/aiks_dl_unittests.cc
index c6109f7..87ae0fc 100644
--- a/engine/src/flutter/impeller/display_list/aiks_dl_unittests.cc
+++ b/engine/src/flutter/impeller/display_list/aiks_dl_unittests.cc
@@ -633,8 +633,8 @@
TEST_P(AiksTest, MatrixImageFilterMagnify) {
Scalar scale = 2.0;
auto callback = [&]() -> sk_sp<DisplayList> {
- if (AiksTest::ImGuiBegin("Controls", nullptr,
- ImGuiWindowFlags_AlwaysAutoResize)) {
+ if (IsPlaygroundEnabled()) {
+ ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
ImGui::SliderFloat("Scale", &scale, 1, 2);
ImGui::End();
}
diff --git a/engine/src/flutter/impeller/display_list/aiks_playground.cc b/engine/src/flutter/impeller/display_list/aiks_playground.cc
index 2d91301..e851dd6 100644
--- a/engine/src/flutter/impeller/display_list/aiks_playground.cc
+++ b/engine/src/flutter/impeller/display_list/aiks_playground.cc
@@ -27,10 +27,7 @@
PlaygroundTest::TearDown();
}
-bool AiksPlayground::ImGuiBegin(const char* name,
- bool* p_open,
- ImGuiWindowFlags flags) {
- ImGui::Begin(name, p_open, flags);
+bool AiksPlaygroundWithGoldens::IsGoldenTestSuite() const {
return true;
}
diff --git a/engine/src/flutter/impeller/display_list/aiks_playground.h b/engine/src/flutter/impeller/display_list/aiks_playground.h
index 13eb790..0a42e5a 100644
--- a/engine/src/flutter/impeller/display_list/aiks_playground.h
+++ b/engine/src/flutter/impeller/display_list/aiks_playground.h
@@ -30,12 +30,6 @@
bool OpenPlaygroundHere(const sk_sp<flutter::DisplayList>& list);
- static bool ImGuiBegin(const char* name,
- bool* p_open,
- ImGuiWindowFlags flags);
-
- bool IsGoldenTest() { return false; }
-
private:
std::shared_ptr<TypographerContext> typographer_context_;
@@ -44,6 +38,11 @@
AiksPlayground& operator=(const AiksPlayground&) = delete;
};
+class AiksPlaygroundWithGoldens : public AiksPlayground {
+ public:
+ bool IsGoldenTestSuite() const override;
+};
+
} // namespace impeller
#endif // FLUTTER_IMPELLER_DISPLAY_LIST_AIKS_PLAYGROUND_H_
diff --git a/engine/src/flutter/impeller/display_list/aiks_unittests.h b/engine/src/flutter/impeller/display_list/aiks_unittests.h
index ddc02e4..d24e706 100644
--- a/engine/src/flutter/impeller/display_list/aiks_unittests.h
+++ b/engine/src/flutter/impeller/display_list/aiks_unittests.h
@@ -14,7 +14,7 @@
#ifdef IMPELLER_GOLDEN_TESTS
using AiksTest = GoldenPlaygroundTest;
#else
-using AiksTest = AiksPlayground;
+using AiksTest = AiksPlaygroundWithGoldens;
#endif
} // namespace testing
diff --git a/engine/src/flutter/impeller/display_list/canvas_unittests.cc b/engine/src/flutter/impeller/display_list/canvas_unittests.cc
index b8a4b56..4556823 100644
--- a/engine/src/flutter/impeller/display_list/canvas_unittests.cc
+++ b/engine/src/flutter/impeller/display_list/canvas_unittests.cc
@@ -420,12 +420,12 @@
static Scalar radius = 200;
// Define the ImGui
- ImGui::Begin("Shadow", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
- {
+ if (IsPlaygroundEnabled()) {
+ ImGui::Begin("Shadow", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
ImGui::SliderFloat("Sigma", &sigma, 0, 100);
ImGui::SliderFloat("Radius", &radius, 0, 1000);
+ ImGui::End();
}
- ImGui::End();
static PlaygroundPoint right_reference_var(
ctm * (right_center + default_size / 2), 30, Color::White());
@@ -464,6 +464,7 @@
TextureDescriptor desc;
desc.size = {100, 100};
desc.format = context.GetDeviceCapabilities().GetDefaultColorFormat();
+ desc.usage = TextureUsage::kRenderTarget;
auto texture =
context.GetContext()->GetResourceAllocator()->CreateTexture(desc);
diff --git a/engine/src/flutter/impeller/display_list/dl_golden_blur_unittests.cc b/engine/src/flutter/impeller/display_list/dl_golden_blur_unittests.cc
index 0ea0ee9..748d2dc 100644
--- a/engine/src/flutter/impeller/display_list/dl_golden_blur_unittests.cc
+++ b/engine/src/flutter/impeller/display_list/dl_golden_blur_unittests.cc
@@ -9,7 +9,7 @@
#include "flutter/impeller/display_list/testing/render_text_in_canvas.h"
#include "flutter/impeller/display_list/testing/rmse.h"
#include "flutter/impeller/geometry/round_rect.h"
-#include "flutter/impeller/golden_tests/screenshot.h"
+#include "flutter/impeller/testing/screenshot.h"
#include "flutter/testing/testing.h"
#include "gtest/gtest.h"
@@ -31,12 +31,14 @@
/*respect_ctm=*/true);
ASSERT_TRUE(RenderTextInCanvasSkia(canvas, "hello world",
"Roboto-Regular.ttf", //
- DlPoint(101, 101), options));
+ DlPoint(101, 101), options)
+ .ok());
options.mask_filter = nullptr;
options.color = DlColor::kRed();
ASSERT_TRUE(RenderTextInCanvasSkia(canvas, "hello world",
"Roboto-Regular.ttf", //
- DlPoint(100, 100), options));
+ DlPoint(100, 100), options)
+ .ok());
};
DisplayListBuilder builder;
@@ -58,12 +60,14 @@
/*respect_ctm=*/false);
ASSERT_TRUE(RenderTextInCanvasSkia(canvas, "hello world",
"Roboto-Regular.ttf", //
- DlPoint(101, 101), options));
+ DlPoint(101, 101), options)
+ .ok());
options.mask_filter = nullptr;
options.color = DlColor::kRed();
ASSERT_TRUE(RenderTextInCanvasSkia(canvas, "hello world",
"Roboto-Regular.ttf", //
- DlPoint(100, 100), options));
+ DlPoint(100, 100), options)
+ .ok());
};
DisplayListBuilder builder;
diff --git a/engine/src/flutter/impeller/display_list/dl_golden_unittests.cc b/engine/src/flutter/impeller/display_list/dl_golden_unittests.cc
index 830f761..b688040 100644
--- a/engine/src/flutter/impeller/display_list/dl_golden_unittests.cc
+++ b/engine/src/flutter/impeller/display_list/dl_golden_unittests.cc
@@ -4,6 +4,8 @@
#include "impeller/display_list/dl_golden_unittests.h"
+#include <utility>
+
#include "display_list/dl_color.h"
#include "display_list/dl_paint.h"
#include "display_list/geometry/dl_geometry_types.h"
@@ -391,9 +393,14 @@
return max_y;
}
-int32_t CalculateSpaceBetweenUI(const impeller::testing::Screenshot* img) {
+std::optional<int32_t> CalculateSpaceBetweenUI(
+ const impeller::testing::Screenshot* img,
+ int32_t y) {
+ if (y < 0 || std::cmp_greater_equal(y, img->GetHeight())) {
+ return {};
+ }
const uint32_t* ptr = reinterpret_cast<const uint32_t*>(img->GetBytes());
- ptr += img->GetWidth() * static_cast<int32_t>(img->GetHeight() / 2.0);
+ ptr += img->GetWidth() * y;
std::vector<size_t> boundaries;
uint32_t value = *ptr++;
for (size_t i = 1; i < img->GetWidth(); ++i) {
@@ -403,7 +410,9 @@
value = *ptr++;
}
- assert(boundaries.size() == 6);
+ if (boundaries.size() != 6) {
+ return {};
+ }
return boundaries[4] - boundaries[3];
}
} // namespace
@@ -418,11 +427,14 @@
paint.setColor(DlColor::ARGB(1, 0, 0, 0));
builder.DrawPaint(paint);
builder.Scale(scale, scale);
- RenderTextInCanvasSkia(&builder, text, "Roboto-Regular.ttf",
- DlPoint::MakeXY(10, 300),
- TextRenderOptions{
- .font_size = font_size,
- });
+ if (!RenderTextInCanvasSkia(&builder, text, "Roboto-Regular.ttf",
+ DlPoint::MakeXY(10, 300),
+ TextRenderOptions{
+ .font_size = font_size,
+ })
+ .ok()) {
+ return nullptr;
+ }
return builder.Build();
};
@@ -443,35 +455,41 @@
TEST_P(DlGoldenTest, MaintainsSpace) {
SetWindowSize(impeller::ISize(1024, 200));
impeller::Scalar font_size = 300;
- auto callback = [&](const char* text,
- impeller::Scalar scale) -> sk_sp<DisplayList> {
- DisplayListBuilder builder;
- DlPaint paint;
- paint.setColor(DlColor::ARGB(1, 0, 0, 0));
- builder.DrawPaint(paint);
- builder.Scale(scale, scale);
- RenderTextInCanvasSkia(&builder, text, "Roboto-Regular.ttf",
- DlPoint::MakeXY(10, 300),
- TextRenderOptions{
- .font_size = font_size,
- });
- return builder.Build();
- };
+ int32_t middle = 0;
std::optional<int32_t> last_space;
for (int i = 0; i <= 100; ++i) {
Scalar scale = 0.440 + i / 1000.0;
+ absl::StatusOr<DlRect> text_bounds_or;
+ sk_sp<DisplayList> display_list;
+ {
+ DisplayListBuilder builder;
+ DlPaint paint;
+ paint.setColor(DlColor::ARGB(1, 0, 0, 0));
+ builder.DrawPaint(paint);
+ builder.Scale(scale, scale);
+ text_bounds_or = RenderTextInCanvasSkia(
+ &builder, "ui", "Roboto-Regular.ttf", DlPoint::MakeXY(10, 300),
+ TextRenderOptions{
+ .font_size = font_size,
+ });
+ display_list = builder.Build();
+ }
+ ASSERT_TRUE(text_bounds_or.ok());
+ ASSERT_TRUE(display_list);
std::unique_ptr<impeller::testing::Screenshot> right =
- MakeScreenshot(callback("ui", scale));
+ MakeScreenshot(display_list);
if (!right) {
GTEST_SKIP() << "making screenshots not supported.";
}
+ middle = std::rint(scale * text_bounds_or->GetCenter().y);
- int32_t space = CalculateSpaceBetweenUI(right.get());
- if (last_space.has_value()) {
- int32_t diff = abs(space - *last_space);
+ std::optional<int32_t> space = CalculateSpaceBetweenUI(right.get(), middle);
+ ASSERT_TRUE(space.has_value());
+ if (space.has_value() && last_space.has_value()) {
+ int32_t diff = abs(*space - *last_space);
EXPECT_TRUE(diff <= 1)
- << "i:" << i << " space:" << space << " last_space:" << *last_space;
+ << "i:" << i << " space:" << *space << " last_space:" << *last_space;
}
last_space = space;
}
@@ -518,12 +536,13 @@
DlPaint paint;
paint.setColor(DlColor::ARGB(1, 0, 0, 0));
builder.DrawPaint(paint);
- RenderTextInCanvasSkia(&builder, "ui", "Roboto-Regular.ttf",
- DlPoint::MakeXY(offset_x, 180),
- TextRenderOptions{
- .font_size = font_size,
- .is_subpixel = true,
- });
+ EXPECT_TRUE(RenderTextInCanvasSkia(&builder, "ui", "Roboto-Regular.ttf",
+ DlPoint::MakeXY(offset_x, 180),
+ TextRenderOptions{
+ .font_size = font_size,
+ .is_subpixel = true,
+ })
+ .ok());
return builder.Build();
};
@@ -558,12 +577,13 @@
DlPaint paint;
paint.setColor(DlColor::ARGB(1, 0, 0, 0));
builder.DrawPaint(paint);
- RenderTextInCanvasSkia(&builder, "ui", "Roboto-Regular.ttf",
- DlPoint::MakeXY(offset_x, 180),
- TextRenderOptions{
- .font_size = font_size,
- .is_subpixel = true,
- });
+ EXPECT_TRUE(RenderTextInCanvasSkia(&builder, "ui", "Roboto-Regular.ttf",
+ DlPoint::MakeXY(offset_x, 180),
+ TextRenderOptions{
+ .font_size = font_size,
+ .is_subpixel = true,
+ })
+ .ok());
return builder.Build();
};
@@ -600,12 +620,13 @@
paint.setColor(DlColor::ARGB(1, 0, 0, 0));
builder.DrawPaint(paint);
builder.Translate(offset_x, 180);
- RenderTextInCanvasSkia(&builder, "ui", "Roboto-Regular.ttf",
- DlPoint::MakeXY(0, 0),
- TextRenderOptions{
- .font_size = font_size,
- .is_subpixel = true,
- });
+ EXPECT_TRUE(RenderTextInCanvasSkia(&builder, "ui", "Roboto-Regular.ttf",
+ DlPoint::MakeXY(0, 0),
+ TextRenderOptions{
+ .font_size = font_size,
+ .is_subpixel = true,
+ })
+ .ok());
return builder.Build();
};
diff --git a/engine/src/flutter/impeller/display_list/dl_golden_unittests.h b/engine/src/flutter/impeller/display_list/dl_golden_unittests.h
index 2722066..ac3f17d 100644
--- a/engine/src/flutter/impeller/display_list/dl_golden_unittests.h
+++ b/engine/src/flutter/impeller/display_list/dl_golden_unittests.h
@@ -14,7 +14,7 @@
#ifdef IMPELLER_GOLDEN_TESTS
using DlGoldenTest = impeller::GoldenPlaygroundTest;
#else
-using DlGoldenTest = impeller::DlPlayground;
+using DlGoldenTest = impeller::DlPlaygroundWithGoldens;
#endif
} // namespace testing
diff --git a/engine/src/flutter/impeller/display_list/dl_playground.cc b/engine/src/flutter/impeller/display_list/dl_playground.cc
index 6528d9c..e93121d 100644
--- a/engine/src/flutter/impeller/display_list/dl_playground.cc
+++ b/engine/src/flutter/impeller/display_list/dl_playground.cc
@@ -8,6 +8,7 @@
#include "impeller/display_list/aiks_context.h"
#include "impeller/display_list/dl_dispatcher.h"
#include "impeller/display_list/dl_image_impeller.h"
+#include "impeller/testing/screenshotter.h"
#include "impeller/typographer/backends/skia/typographer_context_skia.h"
#include "third_party/imgui/imgui.h"
#include "third_party/skia/include/core/SkData.h"
@@ -30,10 +31,6 @@
}
bool DlPlayground::OpenPlaygroundHere(DisplayListPlaygroundCallback callback) {
- if (!IsPlaygroundEnabled()) {
- return true;
- }
-
AiksContext context(GetContext(), TypographerContextSkia::Make());
if (!context.IsValid()) {
return false;
@@ -54,7 +51,15 @@
std::unique_ptr<testing::Screenshot> DlPlayground::MakeScreenshot(
const sk_sp<flutter::DisplayList>& list) {
- return nullptr;
+ std::shared_ptr<Context> context = GetContext();
+ AiksContext renderer(context, GetTypographerContext());
+ Point content_scale = GetContentScale();
+
+ ISize physical_window_size(
+ std::round(GetWindowSize().width * content_scale.x),
+ std::round(GetWindowSize().height * content_scale.y));
+ return testing::Screenshotter::MakeScreenshot(
+ context, DisplayListToTexture(list, physical_window_size, renderer));
}
SkFont DlPlayground::CreateTestFontOfSize(Scalar scalar) {
diff --git a/engine/src/flutter/impeller/display_list/dl_playground.h b/engine/src/flutter/impeller/display_list/dl_playground.h
index b6057a1..ad94623 100644
--- a/engine/src/flutter/impeller/display_list/dl_playground.h
+++ b/engine/src/flutter/impeller/display_list/dl_playground.h
@@ -7,7 +7,7 @@
#include "flutter/display_list/display_list.h"
#include "flutter/display_list/dl_builder.h"
-#include "flutter/impeller/golden_tests/screenshot.h"
+#include "flutter/impeller/testing/screenshot.h"
#include "impeller/playground/playground_test.h"
#include "third_party/skia/include/core/SkFont.h"
@@ -45,6 +45,11 @@
DlPlayground& operator=(const DlPlayground&) = delete;
};
+class DlPlaygroundWithGoldens : public DlPlayground {
+ protected:
+ bool IsGoldenTestSuite() const override { return true; }
+};
+
} // namespace impeller
#endif // FLUTTER_IMPELLER_DISPLAY_LIST_DL_PLAYGROUND_H_
diff --git a/engine/src/flutter/impeller/display_list/dl_unittests.cc b/engine/src/flutter/impeller/display_list/dl_unittests.cc
index 02b948d..9477144 100644
--- a/engine/src/flutter/impeller/display_list/dl_unittests.cc
+++ b/engine/src/flutter/impeller/display_list/dl_unittests.cc
@@ -24,6 +24,7 @@
#include "impeller/display_list/dl_dispatcher.h"
#include "impeller/display_list/dl_image_impeller.h"
#include "impeller/display_list/dl_playground.h"
+#include "impeller/display_list/dl_text_impeller.h"
#include "impeller/entity/contents/clip_contents.h"
#include "impeller/entity/contents/solid_color_contents.h"
#include "impeller/entity/contents/solid_rrect_blur_contents.h"
@@ -53,7 +54,7 @@
TEST_P(DisplayListTest, CanDrawTextBlob) {
flutter::DisplayListBuilder builder;
- builder.DrawText(flutter::DlTextSkia::Make(
+ builder.DrawText(flutter::DlTextImpeller::MakeFromBlob(
SkTextBlob::MakeFromString("Hello", CreateTestFont())),
100, 100, flutter::DlPaint(flutter::DlColor::kBlue()));
ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
@@ -66,21 +67,22 @@
flutter::DlColor::kRed()};
const float stops[2] = {0.0, 1.0};
- auto linear = flutter::DlColorSource::MakeLinear({0.0, 0.0}, {300.0, 300.0},
- 2, colors.data(), stops,
- flutter::DlTileMode::kClamp);
+ auto linear = flutter::DlColorSource::MakeLinear(
+ {0.0, 0.0}, {300.0, 300.0}, 2, colors.data(), stops,
+ flutter::DlTileMode::kMirror);
flutter::DlPaint paint;
paint.setColorSource(linear);
- builder.DrawText(flutter::DlTextSkia::Make(SkTextBlob::MakeFromString(
- "Hello World", CreateTestFont())),
- 100, 100, paint);
+ builder.DrawText(
+ flutter::DlTextImpeller::MakeFromBlob(
+ SkTextBlob::MakeFromString("Hello World", CreateTestFont())),
+ 100, 100, paint);
ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
}
TEST_P(DisplayListTest, CanDrawTextWithSaveLayer) {
flutter::DisplayListBuilder builder;
- builder.DrawText(flutter::DlTextSkia::Make(
+ builder.DrawText(flutter::DlTextImpeller::MakeFromBlob(
SkTextBlob::MakeFromString("Hello", CreateTestFont())),
100, 100, flutter::DlPaint(flutter::DlColor::kRed()));
@@ -88,9 +90,10 @@
float alpha = 0.5;
save_paint.setAlpha(static_cast<uint8_t>(255 * alpha));
builder.SaveLayer(std::nullopt, &save_paint);
- builder.DrawText(flutter::DlTextSkia::Make(SkTextBlob::MakeFromString(
- "Hello with half alpha", CreateTestFontOfSize(100))),
- 100, 300, flutter::DlPaint(flutter::DlColor::kRed()));
+ builder.DrawText(
+ flutter::DlTextImpeller::MakeFromBlob(SkTextBlob::MakeFromString(
+ "Hello with half alpha", CreateTestFontOfSize(100))),
+ 100, 300, flutter::DlPaint(flutter::DlColor::kRed()));
builder.Restore();
ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
}
@@ -163,14 +166,16 @@
const char* cap_names[] = {"Butt", "Round", "Square"};
flutter::DlStrokeCap cap;
- ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
- ImGui::SliderFloat("Start angle", &start_angle, -360, 360);
- ImGui::SliderFloat("Sweep angle", &sweep_angle, -360, 360);
- ImGui::SliderFloat("Stroke width", &stroke_width, 0, 300);
- ImGui::Combo("Cap", &selected_cap, cap_names,
- sizeof(cap_names) / sizeof(char*));
- ImGui::Checkbox("Use center", &use_center);
- ImGui::End();
+ if (IsPlaygroundEnabled()) {
+ ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
+ ImGui::SliderFloat("Start angle", &start_angle, -360, 360);
+ ImGui::SliderFloat("Sweep angle", &sweep_angle, -360, 360);
+ ImGui::SliderFloat("Stroke width", &stroke_width, 0, 300);
+ ImGui::Combo("Cap", &selected_cap, cap_names,
+ sizeof(cap_names) / sizeof(char*));
+ ImGui::Checkbox("Use center", &use_center);
+ ImGui::End();
+ }
switch (selected_cap) {
case 0:
@@ -227,13 +232,15 @@
const char* stroke_types[] = {"Butte", "Round", "Square"};
const char* join_type[] = {"kMiter", "Round", "kBevel"};
- ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
- ImGui::Combo("Cap", &selected_stroke_type, stroke_types,
- sizeof(stroke_types) / sizeof(char*));
- ImGui::Combo("Join", &selected_join_type, join_type,
- sizeof(join_type) / sizeof(char*));
- ImGui::SliderFloat("Stroke Width", &stroke_width, 10.0f, 50.0f);
- ImGui::End();
+ if (IsPlaygroundEnabled()) {
+ ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
+ ImGui::Combo("Cap", &selected_stroke_type, stroke_types,
+ sizeof(stroke_types) / sizeof(char*));
+ ImGui::Combo("Join", &selected_join_type, join_type,
+ sizeof(join_type) / sizeof(char*));
+ ImGui::SliderFloat("Stroke Width", &stroke_width, 10.0f, 50.0f);
+ ImGui::End();
+ }
flutter::DlStrokeCap cap;
flutter::DlStrokeJoin join;
@@ -466,9 +473,10 @@
auto filter =
flutter::DlBlurMaskFilter(flutter::DlBlurStyle::kSolid, 10.0f);
paint.setMaskFilter(&filter);
- builder.DrawText(flutter::DlTextSkia::Make(SkTextBlob::MakeFromString(
- "Testing", CreateTestFont())),
- 220, 170, paint);
+ builder.DrawText(
+ flutter::DlTextImpeller::MakeFromBlob(
+ SkTextBlob::MakeFromString("Testing", CreateTestFont())),
+ 220, 170, paint);
}
ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
@@ -480,9 +488,10 @@
paint.setDrawStyle(flutter::DlDrawStyle::kStroke);
paint.setColor(flutter::DlColor::kRed());
- builder.DrawText(flutter::DlTextSkia::Make(SkTextBlob::MakeFromString(
- "stoked about stroked text", CreateTestFont())),
- 250, 250, paint);
+ builder.DrawText(
+ flutter::DlTextImpeller::MakeFromBlob(SkTextBlob::MakeFromString(
+ "stoked about stroked text", CreateTestFont())),
+ 250, 250, paint);
ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
}
@@ -492,7 +501,7 @@
flutter::DisplayListBuilder builder;
flutter::DlPaint paint;
auto const& text_blob = SkTextBlob::MakeFromString("00000", CreateTestFont());
- auto text = flutter::DlTextSkia::Make(text_blob);
+ auto text = flutter::DlTextImpeller::MakeFromBlob(text_blob);
// https://api.flutter.dev/flutter/material/Colors/blue-constant.html.
auto const& mat_blue = flutter::DlColor(0xFF2196f3);
@@ -584,9 +593,11 @@
auto callback = [&]() {
static float sigma[] = {10, 10};
- ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
- ImGui::SliderFloat2("Sigma", sigma, 0, 100);
- ImGui::End();
+ if (IsPlaygroundEnabled()) {
+ ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
+ ImGui::SliderFloat2("Sigma", sigma, 0, 100);
+ ImGui::End();
+ }
flutter::DisplayListBuilder builder;
flutter::DlPaint paint;
@@ -663,17 +674,19 @@
static bool draw_circle = true;
static bool add_clip = true;
- ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
- ImGui::SliderFloat2("Sigma", sigma, 0, 100);
- ImGui::SliderFloat("Scale", &ctm_scale, 0, 10);
- ImGui::NewLine();
- ImGui::TextWrapped(
- "If everything is working correctly, none of the options below should "
- "impact the filter's appearance.");
- ImGui::Checkbox("Use SaveLayer bounds", &use_bounds);
- ImGui::Checkbox("Draw child element", &draw_circle);
- ImGui::Checkbox("Add pre-clip", &add_clip);
- ImGui::End();
+ if (IsPlaygroundEnabled()) {
+ ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
+ ImGui::SliderFloat2("Sigma", sigma, 0, 100);
+ ImGui::SliderFloat("Scale", &ctm_scale, 0, 10);
+ ImGui::NewLine();
+ ImGui::TextWrapped(
+ "If everything is working correctly, none of the options below "
+ "should impact the filter's appearance.");
+ ImGui::Checkbox("Use SaveLayer bounds", &use_bounds);
+ ImGui::Checkbox("Draw child element", &draw_circle);
+ ImGui::Checkbox("Add pre-clip", &add_clip);
+ ImGui::End();
+ }
flutter::DisplayListBuilder builder;
@@ -736,16 +749,18 @@
static bool use_bounds = true;
static int selected_tile_mode = 0;
- ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
- ImGui::SliderFloat("Background scale", &bg_scale, 0, 10);
- ImGui::SliderFloat("Sigma", &sigma, 0, 100);
- ImGui::SliderFloat("Bounds rotate", &rotate_degree, -200, 200);
- ImGui::SliderFloat("Bounds scale", &bounds_scale, 0.5f, 2.0f);
- ImGui::Combo("Tile mode", &selected_tile_mode, tile_mode_names,
- sizeof(tile_mode_names) / sizeof(char*));
- ImGui::NewLine();
- ImGui::Checkbox("Bounded blur", &use_bounds);
- ImGui::End();
+ if (IsPlaygroundEnabled()) {
+ ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
+ ImGui::SliderFloat("Background scale", &bg_scale, 0, 10);
+ ImGui::SliderFloat("Sigma", &sigma, 0, 100);
+ ImGui::SliderFloat("Bounds rotate", &rotate_degree, -200, 200);
+ ImGui::SliderFloat("Bounds scale", &bounds_scale, 0.5f, 2.0f);
+ ImGui::Combo("Tile mode", &selected_tile_mode, tile_mode_names,
+ sizeof(tile_mode_names) / sizeof(char*));
+ ImGui::NewLine();
+ ImGui::Checkbox("Bounded blur", &use_bounds);
+ ImGui::End();
+ }
// Draw from top right to bottom left.
static PlaygroundPoint blur_point_a(Point(410, 30), 10, Color::White());
@@ -1058,8 +1073,8 @@
static bool enable_savelayer = true;
- ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
- {
+ if (IsPlaygroundEnabled()) {
+ ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
ImGui::Combo("Filter type", &selected_matrix_type, matrix_type_names,
sizeof(matrix_type_names) / sizeof(char*));
@@ -1084,8 +1099,8 @@
"issues. If the rendered image gets cut off when this setting is "
"enabled, there's a coverage bug in the filter.");
ImGui::Checkbox("Render in layer", &enable_savelayer);
+ ImGui::End();
}
- ImGui::End();
flutter::DisplayListBuilder builder;
flutter::DlPaint paint;
@@ -1150,10 +1165,12 @@
static float translation[2] = {0, 0};
static bool enable_save_layer = true;
- ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
- ImGui::SliderFloat2("Translation", translation, -130, 130);
- ImGui::Checkbox("Enable save layer", &enable_save_layer);
- ImGui::End();
+ if (IsPlaygroundEnabled()) {
+ ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
+ ImGui::SliderFloat2("Translation", translation, -130, 130);
+ ImGui::Checkbox("Enable save layer", &enable_save_layer);
+ ImGui::End();
+ }
flutter::DisplayListBuilder builder;
builder.Save();
@@ -1446,8 +1463,8 @@
static float stroked_alpha = 255.0;
static float stroked_scale[2] = {1.0, 1.0};
- ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
- {
+ if (IsPlaygroundEnabled()) {
+ ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
ImGui::SliderFloat("Filled Radius", &filled_radius, 0, 500);
ImGui::SliderFloat("Filled Alpha", &filled_alpha, 0, 255);
ImGui::SliderFloat2("Filled Scale", filled_scale, 0, 10.0);
@@ -1456,8 +1473,8 @@
ImGui::SliderFloat("Stroked Width Fine", &stroke_width_fine, 0, 5);
ImGui::SliderFloat("Stroked Alpha", &stroked_alpha, 0, 10.0);
ImGui::SliderFloat2("Stroked Scale", stroked_scale, 0, 10.0);
+ ImGui::End();
}
- ImGui::End();
flutter::DisplayListBuilder builder;
flutter::DlPaint paint;
@@ -1598,8 +1615,8 @@
static float color2[4] = {0.0f, 0.0f, 1.0f, 1.0f};
static float src_color[4] = {1.0f, 1.0f, 1.0f, 1.0f};
- ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
- {
+ if (IsPlaygroundEnabled()) {
+ ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
ImGui::ListBox("Blending mode", ¤t_blend_index,
blend_mode_names.data(), blend_mode_names.size());
ImGui::SliderFloat("Source alpha", &src_alpha, 0, 1);
@@ -1608,8 +1625,8 @@
ImGui::ColorEdit4("Color C", color2);
ImGui::ColorEdit4("Source Color", src_color);
ImGui::SliderFloat("Destination alpha", &dst_alpha, 0, 1);
+ ImGui::End();
}
- ImGui::End();
std::vector<DlPoint> positions = {DlPoint(100, 300), //
DlPoint(200, 100), //
diff --git a/engine/src/flutter/impeller/display_list/testing/render_text_in_canvas.cc b/engine/src/flutter/impeller/display_list/testing/render_text_in_canvas.cc
index a3aaebf..fba9024 100644
--- a/engine/src/flutter/impeller/display_list/testing/render_text_in_canvas.cc
+++ b/engine/src/flutter/impeller/display_list/testing/render_text_in_canvas.cc
@@ -11,15 +11,16 @@
namespace flutter {
namespace testing {
-bool RenderTextInCanvasSkia(DlCanvas* canvas,
- const std::string& text,
- const std::string_view& font_fixture,
- DlPoint position,
- const TextRenderOptions& options) {
+absl::StatusOr<DlRect> RenderTextInCanvasSkia(
+ DlCanvas* canvas,
+ const std::string& text,
+ const std::string_view& font_fixture,
+ DlPoint position,
+ const TextRenderOptions& options) {
auto c_font_fixture = std::string(font_fixture);
auto mapping = flutter::testing::OpenFixtureAsSkData(c_font_fixture.c_str());
if (!mapping) {
- return false;
+ return absl::NotFoundError("Font fixture not found");
}
sk_sp<SkFontMgr> font_mgr = txt::GetDefaultFontManager();
SkFont sk_font(font_mgr->makeFromData(mapping), options.font_size);
@@ -28,7 +29,7 @@
}
auto blob = SkTextBlob::MakeFromString(text.c_str(), sk_font);
if (!blob) {
- return false;
+ return absl::InvalidArgumentError("String could not be converted to Blob");
}
auto frame = impeller::MakeTextFrameFromTextBlobSkia(blob);
@@ -42,7 +43,7 @@
// options.stroke ? Paint::Style::kStroke : Paint::Style::kFill;
canvas->DrawText(DlTextImpeller::Make(frame), position.x, position.y,
text_paint);
- return true;
+ return frame->GetBounds().Shift(position);
}
} // namespace testing
} // namespace flutter
diff --git a/engine/src/flutter/impeller/display_list/testing/render_text_in_canvas.h b/engine/src/flutter/impeller/display_list/testing/render_text_in_canvas.h
index 7edaead..3242896 100644
--- a/engine/src/flutter/impeller/display_list/testing/render_text_in_canvas.h
+++ b/engine/src/flutter/impeller/display_list/testing/render_text_in_canvas.h
@@ -11,6 +11,7 @@
#include "flutter/display_list/dl_color.h"
#include "flutter/display_list/effects/dl_mask_filter.h"
#include "impeller/typographer/backends/skia/text_frame_skia.h"
+#include "third_party/abseil-cpp/absl/status/statusor.h"
namespace flutter {
namespace testing {
@@ -23,11 +24,12 @@
bool is_subpixel = false;
};
-bool RenderTextInCanvasSkia(DlCanvas* canvas,
- const std::string& text,
- const std::string_view& font_fixture,
- DlPoint position,
- const TextRenderOptions& options = {});
+absl::StatusOr<DlRect> RenderTextInCanvasSkia(
+ DlCanvas* canvas,
+ const std::string& text,
+ const std::string_view& font_fixture,
+ DlPoint position,
+ const TextRenderOptions& options = {});
} // namespace testing
} // namespace flutter
diff --git a/engine/src/flutter/impeller/display_list/testing/rmse.h b/engine/src/flutter/impeller/display_list/testing/rmse.h
index aadb830..75d19b5 100644
--- a/engine/src/flutter/impeller/display_list/testing/rmse.h
+++ b/engine/src/flutter/impeller/display_list/testing/rmse.h
@@ -5,7 +5,7 @@
#ifndef FLUTTER_IMPELLER_DISPLAY_LIST_TESTING_RMSE_H_
#define FLUTTER_IMPELLER_DISPLAY_LIST_TESTING_RMSE_H_
-#include "flutter/impeller/golden_tests/screenshot.h"
+#include "flutter/impeller/testing/screenshot.h"
namespace flutter {
namespace testing {
diff --git a/engine/src/flutter/impeller/entity/BUILD.gn b/engine/src/flutter/impeller/entity/BUILD.gn
index 79ed382..03f12e1 100644
--- a/engine/src/flutter/impeller/entity/BUILD.gn
+++ b/engine/src/flutter/impeller/entity/BUILD.gn
@@ -318,7 +318,7 @@
"//flutter/display_list/testing:display_list_testing",
"//flutter/impeller/renderer/testing:mocks",
"//flutter/impeller/typographer/backends/skia:typographer_skia_backend",
- "//flutter/testing",
+ "//flutter/testing:testing_lib",
"//flutter/txt",
]
}
diff --git a/engine/src/flutter/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc b/engine/src/flutter/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc
index a2de219..917c11d 100644
--- a/engine/src/flutter/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc
+++ b/engine/src/flutter/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc
@@ -742,7 +742,10 @@
mask_blur_style_(mask_blur_style),
mask_geometry_(mask_geometry) {
// This is supposed to be enforced at a higher level.
- FML_DCHECK(mask_blur_style == BlurStyle::kNormal || mask_geometry);
+ FML_DCHECK(mask_blur_style == BlurStyle::kNormal ||
+ mask_blur_style == BlurStyle::kSolid ||
+ // mask_geometry is used for Inner and Outer modes only
+ mask_geometry);
}
// This value was extracted from Skia, see:
diff --git a/engine/src/flutter/impeller/entity/entity_unittests.cc b/engine/src/flutter/impeller/entity/entity_unittests.cc
index 718c4a7..476af2b 100644
--- a/engine/src/flutter/impeller/entity/entity_unittests.cc
+++ b/engine/src/flutter/impeller/entity/entity_unittests.cc
@@ -204,16 +204,16 @@
static Scalar miter_limit = 1.41421357;
static Scalar width = 30;
- ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
- {
+ if (IsPlaygroundEnabled()) {
+ ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
ImGui::SliderFloat("Miter limit", &miter_limit, 0, 30);
ImGui::SliderFloat("Stroke width", &width, 0, 100);
if (ImGui::Button("Reset")) {
miter_limit = 1.41421357;
width = 30;
}
+ ImGui::End();
}
- ImGui::End();
auto world_matrix = Matrix::MakeScale(GetContentScale());
auto render_path = [width = width, &context, &pass, &world_matrix](
@@ -393,10 +393,16 @@
const char* input_axis[] = {"X", "Y", "Z"};
static int rotation_axis_index = 0;
static float rotation = 0;
- ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
- ImGui::SliderFloat("Rotation", &rotation, -kPi, kPi);
- ImGui::Combo("Rotation Axis", &rotation_axis_index, input_axis,
- sizeof(input_axis) / sizeof(char*));
+ if (IsPlaygroundEnabled()) {
+ ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
+ ImGui::SliderFloat("Rotation", &rotation, -kPi, kPi);
+ ImGui::Combo("Rotation Axis", &rotation_axis_index, input_axis,
+ sizeof(input_axis) / sizeof(char*));
+ if (ImGui::Button("Reset")) {
+ rotation = 0;
+ }
+ ImGui::End();
+ }
Matrix rotation_matrix;
switch (rotation_axis_index) {
case 0:
@@ -413,10 +419,6 @@
break;
}
- if (ImGui::Button("Reset")) {
- rotation = 0;
- }
- ImGui::End();
Matrix current_transform =
Matrix::MakeScale(GetContentScale())
.MakeTranslation(
@@ -833,14 +835,16 @@
return pass.Draw().ok();
};
- ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
static Color color1(1, 0, 0, 0.5), color2(0, 1, 0, 0.5);
- ImGui::ColorEdit4("Color 1", reinterpret_cast<float*>(&color1));
- ImGui::ColorEdit4("Color 2", reinterpret_cast<float*>(&color2));
static int current_blend_index = 3;
- ImGui::ListBox("Blending mode", ¤t_blend_index,
- blend_mode_names.data(), blend_mode_names.size());
- ImGui::End();
+ if (IsPlaygroundEnabled()) {
+ ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
+ ImGui::ColorEdit4("Color 1", reinterpret_cast<float*>(&color1));
+ ImGui::ColorEdit4("Color 2", reinterpret_cast<float*>(&color2));
+ ImGui::ListBox("Blending mode", ¤t_blend_index,
+ blend_mode_names.data(), blend_mode_names.size());
+ ImGui::End();
+ }
BlendMode selected_mode = blend_mode_values[current_blend_index];
@@ -870,9 +874,11 @@
auto callback = [&](ContentContext& context, RenderPass& pass) -> bool {
static float scale = 20;
- ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
- ImGui::SliderFloat("Scale", &scale, 1, 100);
- ImGui::End();
+ if (IsPlaygroundEnabled()) {
+ ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
+ ImGui::SliderFloat("Scale", &scale, 1, 100);
+ ImGui::End();
+ }
Entity entity;
entity.SetTransform(Matrix::MakeScale(GetContentScale()));
@@ -972,8 +978,8 @@
static_cast<float>(boston->GetSize().width),
static_cast<float>(boston->GetSize().height)};
- ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
- {
+ if (IsPlaygroundEnabled()) {
+ ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
ImGui::Combo("Input type", &selected_input_type, input_type_names,
sizeof(input_type_names) / sizeof(char*));
if (selected_input_type == 0) {
@@ -1012,8 +1018,8 @@
ImGui::SliderFloat2("Scale", scale, 0, 3);
ImGui::SliderFloat2("Skew", skew, -3, 3);
ImGui::SliderFloat4("Path XYWH", path_rect, -1000, 1000);
+ ImGui::End();
}
- ImGui::End();
auto blur_sigma_x = Sigma{blur_amount_coarse[0] + blur_amount_fine[0]};
auto blur_sigma_y = Sigma{blur_amount_coarse[1] + blur_amount_fine[1]};
@@ -1136,8 +1142,8 @@
static_cast<float>(boston->GetSize().height)};
static float effect_transform_scale = 1;
- ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
- {
+ if (IsPlaygroundEnabled()) {
+ ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
ImGui::Combo("Morphology type", &selected_morphology_type,
morphology_type_names,
sizeof(morphology_type_names) / sizeof(char*));
@@ -1154,8 +1160,8 @@
ImGui::SliderFloat4("Path XYWH", path_rect, -1000, 1000);
ImGui::SliderFloat("Effect transform scale", &effect_transform_scale, 0,
3);
+ ImGui::End();
}
- ImGui::End();
std::shared_ptr<Contents> input;
Size input_size;
@@ -1201,16 +1207,19 @@
cover_entity.Render(context, pass);
// Renders a green bounding rect of the target filter.
- Entity bounds_entity;
- std::unique_ptr<Geometry> bounds_geom = Geometry::MakeFillPath(
- flutter::DlPath::MakeRect(contents->GetCoverage(entity).value()));
- auto bounds_contents =
- std::make_shared<SolidColorContents>(bounds_geom.get());
- bounds_contents->SetColor(bounds_color);
- bounds_entity.SetContents(std::move(bounds_contents));
- bounds_entity.SetTransform(Matrix());
+ std::optional<Rect> contents_coverage = contents->GetCoverage(entity);
+ if (contents_coverage.has_value()) {
+ std::unique_ptr<Geometry> bounds_geom = Geometry::MakeFillPath(
+ flutter::DlPath::MakeRect(contents_coverage.value()));
+ auto bounds_contents =
+ std::make_shared<SolidColorContents>(bounds_geom.get());
+ bounds_contents->SetColor(bounds_color);
+ Entity bounds_entity;
+ bounds_entity.SetContents(std::move(bounds_contents));
+ bounds_entity.SetTransform(Matrix());
- bounds_entity.Render(context, pass);
+ bounds_entity.Render(context, pass);
+ }
return true;
};
@@ -1438,16 +1447,18 @@
static PlaygroundPoint bottom_right_point(Point(600, 400), 30,
Color::White());
- ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
- ImGui::SliderFloat("Corner radius", &corner_radius, 0, 300);
- ImGui::SliderFloat("Blur radius", &blur_radius, 0, 300);
- ImGui::ColorEdit4("Color", reinterpret_cast<Scalar*>(&color));
- ImGui::Checkbox("Show coverage", &show_coverage);
- if (show_coverage) {
- ImGui::ColorEdit4("Coverage color",
- reinterpret_cast<Scalar*>(&coverage_color));
+ if (IsPlaygroundEnabled()) {
+ ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
+ ImGui::SliderFloat("Corner radius", &corner_radius, 0, 300);
+ ImGui::SliderFloat("Blur radius", &blur_radius, 0, 300);
+ ImGui::ColorEdit4("Color", reinterpret_cast<Scalar*>(&color));
+ ImGui::Checkbox("Show coverage", &show_coverage);
+ if (show_coverage) {
+ ImGui::ColorEdit4("Coverage color",
+ reinterpret_cast<Scalar*>(&coverage_color));
+ }
+ ImGui::End();
}
- ImGui::End();
auto [top_left, bottom_right] =
DrawPlaygroundLine(top_left_point, bottom_right_point);
@@ -1527,8 +1538,8 @@
static float skew[2] = {0, 0};
// Define the ImGui
- ImGui::Begin("Color Matrix", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
- {
+ if (IsPlaygroundEnabled()) {
+ ImGui::Begin("Color Matrix", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
std::string label = "##1";
for (int i = 0; i < 20; i += 5) {
ImGui::InputScalarN(label.c_str(), ImGuiDataType_Float,
@@ -1542,8 +1553,8 @@
ImGui::SliderFloat("Rotation", &rotation, 0, kPi * 2);
ImGui::SliderFloat2("Scale", &scale[0], 0, 3);
ImGui::SliderFloat2("Skew", &skew[0], -3, 3);
+ ImGui::End();
}
- ImGui::End();
// Set the color matrix filter.
auto filter = ColorFilterContents::MakeColorMatrix(
@@ -1750,11 +1761,12 @@
}
TEST_P(EntityTest, YUVToRGBFilter) {
- if (GetParam() == PlaygroundBackend::kOpenGLES ||
- GetParam() == PlaygroundBackend::kOpenGLESSDF) {
- // TODO(114588) : Support YUV to RGB filter on OpenGLES backend.
- GTEST_SKIP()
- << "YUV to RGB filter is not supported on OpenGLES backend yet.";
+ if (GetParam() != PlaygroundBackend::kMetal &&
+ GetParam() != PlaygroundBackend::kMetalSDF) {
+ // @see (114588) : Support YUV to RGB filter on OpenGLES backend.
+ // The issue was closed as low priority as the support is only really
+ // needed for iOS. Also, Vulkan isn't supported either.
+ GTEST_SKIP() << "YUV to RGB filter is only supported on Metal backends.";
}
auto callback = [&](ContentContext& context, RenderPass& pass) -> bool {
@@ -2497,13 +2509,15 @@
static int degree = 4;
static Color color = Color::Red();
- ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
- ImGui::SliderFloat("Alpha", &alpha, 0, 100);
- ImGui::SliderFloat("Beta", &beta, 0, 100);
- ImGui::SliderInt("Degreee", °ree, 1, 20);
- ImGui::SliderFloat("Radius", &radius, 0, 400);
- ImGui::ColorEdit4("Color", reinterpret_cast<float*>(&color));
- ImGui::End();
+ if (IsPlaygroundEnabled()) {
+ ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
+ ImGui::SliderFloat("Alpha", &alpha, 0, 100);
+ ImGui::SliderFloat("Beta", &beta, 0, 100);
+ ImGui::SliderInt("Degreee", °ree, 1, 20);
+ ImGui::SliderFloat("Radius", &radius, 0, 400);
+ ImGui::ColorEdit4("Color", reinterpret_cast<float*>(&color));
+ ImGui::End();
+ }
std::unique_ptr<SuperellipseGeometry> geom =
std::make_unique<SuperellipseGeometry>(Point{400, 400}, radius, degree,
@@ -2565,8 +2579,8 @@
radius_br[1] = radius_br[0];
}
- ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
- {
+ if (IsPlaygroundEnabled()) {
+ ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
ImGui::Combo("Style", &style_index, style_options,
sizeof(style_options) / sizeof(char*));
ImGui::SliderFloat2("Center", ¢er.x, 0, 1000);
@@ -2594,10 +2608,9 @@
radius_br = radius_tr;
}
}
+ ImGui::End();
}
- ImGui::End();
-
RoundingRadii radii{
.top_left = {radius_tl[0], radius_tl[1]},
.top_right = {radius_tr[0], radius_tr[1]},
@@ -2650,13 +2663,13 @@
// Scale so that "corner radius" is as long as half the canvas.
float scale = screen_canvas_size / 2 / corner_radius;
- ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
- {
+ if (IsPlaygroundEnabled()) {
+ ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
ImGui::SliderFloat("log(Ratio)", &logarithm_of_ratio, 1.0, 8.0);
ImGui::LabelText("Ratio", "%.2g", static_cast<double>(ratio));
ImGui::Text(" where Ratio = RectSize / CornerRadius");
+ ImGui::End();
}
- ImGui::End();
auto top_right = Vector2(screen_canvas_size * 1.3f, screen_canvas_padding);
auto transform = Matrix::MakeTranslation(top_right) *
@@ -2709,16 +2722,18 @@
screen_offset_y * GetContentScale().y / scale / rect_size;
label = std::format("- {:.2g}", portion_of_rect);
}
- ImGui::GetBackgroundDrawList()->AddText(
- nullptr, font_size,
- // Draw the ruler at around the flat part of the curve, which is
- // somewhere to the left of the top right corner.
- //
- // Offset vertically by font_size/2 so that the hyphen aligns with the
- // top of shape.
- {screen_top_right.x - 500,
- screen_top_right.y + screen_offset_y - font_size / 2},
- IM_COL32_WHITE, label.c_str());
+ if (IsPlaygroundEnabled()) {
+ ImGui::GetBackgroundDrawList()->AddText(
+ nullptr, font_size,
+ // Draw the ruler at around the flat part of the curve, which is
+ // somewhere to the left of the top right corner.
+ //
+ // Offset vertically by font_size/2 so that the hyphen aligns with
+ // the top of shape.
+ {screen_top_right.x - 500,
+ screen_top_right.y + screen_offset_y - font_size / 2},
+ IM_COL32_WHITE, label.c_str());
+ }
}
return success;
};
diff --git a/engine/src/flutter/impeller/golden_tests/BUILD.gn b/engine/src/flutter/impeller/golden_tests/BUILD.gn
index c873869..22dad40 100644
--- a/engine/src/flutter/impeller/golden_tests/BUILD.gn
+++ b/engine/src/flutter/impeller/golden_tests/BUILD.gn
@@ -12,11 +12,11 @@
deps = [
":digest",
- ":screenshot",
"//flutter/display_list",
"//flutter/fml",
"//flutter/impeller/display_list",
"//flutter/impeller/playground",
+ "//flutter/impeller/testing:screenshot",
"//flutter/impeller/typographer/backends/skia:typographer_skia_backend",
"//flutter/testing:testing_lib",
]
@@ -24,7 +24,7 @@
if (is_mac) {
sources += [ "golden_playground_test_mac.cc" ]
deps += [
- ":metal_screenshot",
+ ":metal_golden_screenshot",
"//third_party/abseil-cpp/absl/base:no_destructor",
]
} else {
@@ -45,33 +45,27 @@
deps = [ "//flutter/fml" ]
}
-impeller_component("screenshot") {
- testonly = true
- sources = [ "screenshot.h" ]
-}
-
if (is_mac) {
- impeller_component("metal_screenshot") {
+ impeller_component("metal_golden_screenshot") {
testonly = true
sources = [
- "metal_screenshot.h",
- "metal_screenshot.mm",
- "metal_screenshotter.h",
- "metal_screenshotter.mm",
- "screenshotter.h",
- "vulkan_screenshotter.h",
- "vulkan_screenshotter.mm",
+ "golden_screenshotter.h",
+ "metal_golden_screenshotter.h",
+ "metal_golden_screenshotter.mm",
+ "vulkan_golden_screenshotter.h",
+ "vulkan_golden_screenshotter.mm",
]
deps = [
- ":screenshot",
"//flutter/display_list",
"//flutter/fml",
"//flutter/impeller/display_list",
"//flutter/impeller/playground",
"//flutter/impeller/renderer/backend/metal",
"//flutter/impeller/renderer/backend/vulkan",
+ "//flutter/impeller/testing:metal_screenshot",
+ "//flutter/impeller/testing:screenshot",
]
}
@@ -87,7 +81,7 @@
deps = [
":digest",
- ":metal_screenshot",
+ ":metal_golden_screenshot",
"//flutter/display_list",
"//flutter/impeller/display_list",
"//flutter/impeller/display_list:aiks_unittests_golden",
diff --git a/engine/src/flutter/impeller/golden_tests/golden_playground_test.h b/engine/src/flutter/impeller/golden_tests/golden_playground_test.h
index f07a01d..d9726e6 100644
--- a/engine/src/flutter/impeller/golden_tests/golden_playground_test.h
+++ b/engine/src/flutter/impeller/golden_tests/golden_playground_test.h
@@ -10,8 +10,8 @@
#include "flutter/display_list/display_list.h"
#include "flutter/display_list/image/dl_image.h"
#include "flutter/impeller/display_list/aiks_context.h"
-#include "flutter/impeller/golden_tests/screenshot.h"
#include "flutter/impeller/runtime_stage/runtime_stage.h"
+#include "flutter/impeller/testing/screenshot.h"
#include "flutter/testing/testing.h"
#include "impeller/playground/playground.h"
#include "impeller/typographer/typographer_context.h"
@@ -69,10 +69,6 @@
static bool SaveScreenshot(std::unique_ptr<testing::Screenshot> screenshot,
const std::string& postfix = "");
- static bool ImGuiBegin(const char* name,
- bool* p_open,
- ImGuiWindowFlags flags);
-
std::shared_ptr<Texture> CreateTextureForFixture(
const char* fixture_name,
bool enable_mipmapping = false) const;
@@ -99,30 +95,51 @@
[[nodiscard]] fml::Status SetCapabilities(
const std::shared_ptr<Capabilities>& capabilities);
- /// Returns true if `OpenPlaygroundHere` will actually render anything.
- bool WillRenderSomething() const { return true; }
-
RuntimeStageBackend GetRuntimeStageBackend() const;
- bool IsGoldenTest() { return true; }
+ /// @brief Sets a particular test to either write a golden or not.
+ ///
+ /// For purposes of the GoldenPlayground test harness, we don't maintain
+ /// a flag for this status, all tests are assumed to be golden tests and
+ /// passing false here means we should just skip this test entirely
+ /// (enforced in the implementation with a GTEST_SKIP).
+ void SetEnableWriteGolden(bool write_golden);
+
+ bool IsPlaygroundEnabled() const { return false; }
+
+ /// @brief Initializes the provided |PipelineDescriptor| with appropriate
+ /// default values to match the conditions under which a pipeline
+ /// will be rendered.
+ bool InitializePipelineDescriptorForRendering(PipelineDescriptor& desc) const;
protected:
void SetWindowSize(ISize size);
- // See |Playground::PlatformSupportsWideGamutTests|
- [[nodiscard]] bool PlatformSupportsWideGamutTests() const;
+ /// @brief Returns true if the rendering path supports MSAA rendering.
+ ///
+ /// In the case of goldens, all tests are rendered to a non-MSAA backend.
+ bool RenderingSupportsMSAA() const { return false; }
+
+ /// @brief Returns the default sample count of the rendering path.
+ ///
+ /// In the case of goldens, all tests are rendered to a non-MSAA backend.
+ SampleCount GetDefaultSampleCount() const { return SampleCount::kCount1; }
// See |Playground::EnsureContextIsUnique|
// GoldenPlaygroundTest uses context replacement on the fly to support this.
void EnsureContextIsUnique() {}
// See |Playground::EnsureContextSupportsWideGamut|
- // GoldenPlaygroundTest uses name matching to support this.
+ // GoldenPlaygroundTest uses testname matching in |Setup| to manage this.
+ // In particular, it will automatically GTEST_SKIP if the platform doesn't
+ // support wide gamuts.
[[nodiscard]] bool EnsureContextSupportsWideGamut() { return true; }
// See |Playground::EnsureContextSupportsAntialiasLines|
- // GoldenPlaygroundTest uses name matching to support this.
- void EnsureContextSupportsAntialiasLines() {}
+ // GoldenPlaygroundTest uses testname matching in |Setup| to manage this.
+ // In particular, it will automatically GTEST_SKIP if the platform doesn't
+ // support experimental AA lines.
+ [[nodiscard]] bool EnsureContextSupportsAntialiasLines() { return true; }
private:
#if FML_OS_MACOSX
diff --git a/engine/src/flutter/impeller/golden_tests/golden_playground_test_mac.cc b/engine/src/flutter/impeller/golden_tests/golden_playground_test_mac.cc
index 23211d8..13b89d1 100644
--- a/engine/src/flutter/impeller/golden_tests/golden_playground_test_mac.cc
+++ b/engine/src/flutter/impeller/golden_tests/golden_playground_test_mac.cc
@@ -10,12 +10,14 @@
#include "flutter/impeller/golden_tests/golden_playground_test.h"
#include "flutter/impeller/golden_tests/golden_digest.h"
-#include "flutter/impeller/golden_tests/metal_screenshotter.h"
-#include "flutter/impeller/golden_tests/vulkan_screenshotter.h"
+#include "flutter/impeller/golden_tests/golden_screenshotter.h"
+#include "flutter/impeller/golden_tests/metal_golden_screenshotter.h"
+#include "flutter/impeller/golden_tests/vulkan_golden_screenshotter.h"
#include "fml/closure.h"
#include "impeller/display_list/aiks_context.h"
#include "impeller/display_list/dl_dispatcher.h"
#include "impeller/display_list/dl_image_impeller.h"
+#include "impeller/playground/playground_impl.h"
#include "impeller/renderer/command_buffer.h"
#include "impeller/renderer/command_queue.h"
#include "impeller/renderer/render_pass.h"
@@ -150,7 +152,7 @@
struct GoldenPlaygroundTest::GoldenPlaygroundTestImpl {
std::unique_ptr<PlaygroundImpl> test_vulkan_playground;
std::unique_ptr<PlaygroundImpl> test_opengl_playground;
- std::unique_ptr<testing::Screenshotter> screenshotter;
+ std::unique_ptr<testing::GoldenScreenshotter> screenshotter;
ISize window_size = ISize{1024, 768};
};
@@ -184,10 +186,6 @@
}
} // namespace
-bool GoldenPlaygroundTest::PlatformSupportsWideGamutTests() const {
- return DoesSupportWideGamutTests() && GetParam() == PlaygroundBackend::kMetal;
-}
-
void GoldenPlaygroundTest::SetUp() {
std::filesystem::path testing_assets_path =
flutter::testing::GetTestingAssetsPath();
@@ -214,7 +212,7 @@
<< "This metal device doesn't support wide gamut golden tests.";
}
pimpl_->screenshotter =
- std::make_unique<testing::MetalScreenshotter>(switches);
+ std::make_unique<testing::MetalGoldenScreenshotter>(switches);
break;
case PlaygroundBackend::kVulkan: {
if (switches.enable_wide_gamut) {
@@ -227,7 +225,7 @@
const std::unique_ptr<PlaygroundImpl>& playground =
GetSharedVulkanPlayground(/*enable_validations=*/true);
pimpl_->screenshotter =
- std::make_unique<testing::VulkanScreenshotter>(playground);
+ std::make_unique<testing::VulkanGoldenScreenshotter>(playground);
break;
}
case PlaygroundBackend::kOpenGLESSDF:
@@ -246,7 +244,7 @@
::glfwMakeContextCurrent(
reinterpret_cast<GLFWwindow*>(playground->GetWindowHandle()));
pimpl_->screenshotter =
- std::make_unique<testing::VulkanScreenshotter>(playground);
+ std::make_unique<testing::VulkanGoldenScreenshotter>(playground);
break;
}
}
@@ -338,12 +336,6 @@
return SaveScreenshot(std::move(screenshot));
}
-bool GoldenPlaygroundTest::ImGuiBegin(const char* name,
- bool* p_open,
- ImGuiWindowFlags flags) {
- return false;
-}
-
std::shared_ptr<Texture> GoldenPlaygroundTest::CreateTextureForFixture(
const char* fixture_name,
bool enable_mipmapping) const {
@@ -393,8 +385,9 @@
<< "We don't support creating multiple contexts for one test";
pimpl_->test_vulkan_playground =
MakeVulkanPlayground(enable_vulkan_validations);
- pimpl_->screenshotter = std::make_unique<testing::VulkanScreenshotter>(
- pimpl_->test_vulkan_playground);
+ pimpl_->screenshotter =
+ std::make_unique<testing::VulkanGoldenScreenshotter>(
+ pimpl_->test_vulkan_playground);
return pimpl_->test_vulkan_playground->GetContext();
} else if (GetParam() == PlaygroundBackend::kOpenGLES ||
GetParam() == PlaygroundBackend::kOpenGLESSDF) {
@@ -402,8 +395,9 @@
<< "We don't support creating multiple contexts for one test";
bool use_sdfs = (GetParam() == PlaygroundBackend::kOpenGLESSDF);
pimpl_->test_opengl_playground = MakeOpenGLESPlayground(use_sdfs);
- pimpl_->screenshotter = std::make_unique<testing::VulkanScreenshotter>(
- pimpl_->test_opengl_playground);
+ pimpl_->screenshotter =
+ std::make_unique<testing::VulkanGoldenScreenshotter>(
+ pimpl_->test_opengl_playground);
return pimpl_->test_opengl_playground->GetContext();
} else {
FML_CHECK(false);
@@ -453,4 +447,24 @@
return pimpl_->screenshotter->GetPlayground().GetRuntimeStageBackend();
}
+void GoldenPlaygroundTest::SetEnableWriteGolden(bool write_golden) {
+ if (!write_golden) {
+ // If we don't write a golden, we really don't have anything to do.
+ GTEST_SKIP() << "Test does not want a golden image written";
+ }
+}
+
+bool GoldenPlaygroundTest::InitializePipelineDescriptorForRendering(
+ PipelineDescriptor& desc) const {
+ // Match the golden harness render target: single-sampled, no depth/stencil.
+ // `ClearStencilAttachments` also resets the stencil pixel format on the
+ // pipeline, which Metal validation requires to match the target's lack of a
+ // stencil texture; `SetStencilAttachmentDescriptors(nullopt)` alone leaves
+ // the format set and trips that validation.
+ desc.SetSampleCount(SampleCount::kCount1);
+ desc.ClearStencilAttachments();
+ desc.ClearDepthAttachment();
+ return true;
+}
+
} // namespace impeller
diff --git a/engine/src/flutter/impeller/golden_tests/screenshotter.h b/engine/src/flutter/impeller/golden_tests/golden_screenshotter.h
similarity index 63%
rename from engine/src/flutter/impeller/golden_tests/screenshotter.h
rename to engine/src/flutter/impeller/golden_tests/golden_screenshotter.h
index ae6c8a5..79a0e0a 100644
--- a/engine/src/flutter/impeller/golden_tests/screenshotter.h
+++ b/engine/src/flutter/impeller/golden_tests/golden_screenshotter.h
@@ -2,11 +2,11 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef FLUTTER_IMPELLER_GOLDEN_TESTS_SCREENSHOTTER_H_
-#define FLUTTER_IMPELLER_GOLDEN_TESTS_SCREENSHOTTER_H_
+#ifndef FLUTTER_IMPELLER_GOLDEN_TESTS_GOLDEN_SCREENSHOTTER_H_
+#define FLUTTER_IMPELLER_GOLDEN_TESTS_GOLDEN_SCREENSHOTTER_H_
-#include "flutter/impeller/golden_tests/screenshot.h"
#include "flutter/impeller/playground/playground_impl.h"
+#include "flutter/impeller/testing/screenshot.h"
#include "impeller/display_list/aiks_context.h"
namespace impeller {
@@ -14,13 +14,13 @@
/// Converts `Picture`s and `DisplayList`s to `MetalScreenshot`s with the
/// playground backend.
-class Screenshotter {
+class GoldenScreenshotter {
public:
- virtual ~Screenshotter() = default;
+ virtual ~GoldenScreenshotter() = default;
virtual std::unique_ptr<Screenshot> MakeScreenshot(
const AiksContext& aiks_context,
- const std::shared_ptr<Texture> texture) = 0;
+ const std::shared_ptr<Texture>& texture) = 0;
virtual PlaygroundImpl& GetPlayground() = 0;
};
@@ -28,4 +28,4 @@
} // namespace testing
} // namespace impeller
-#endif // FLUTTER_IMPELLER_GOLDEN_TESTS_SCREENSHOTTER_H_
+#endif // FLUTTER_IMPELLER_GOLDEN_TESTS_GOLDEN_SCREENSHOTTER_H_
diff --git a/engine/src/flutter/impeller/golden_tests/golden_tests.cc b/engine/src/flutter/impeller/golden_tests/golden_tests.cc
index d345611..21cb5d3 100644
--- a/engine/src/flutter/impeller/golden_tests/golden_tests.cc
+++ b/engine/src/flutter/impeller/golden_tests/golden_tests.cc
@@ -15,8 +15,7 @@
#include "impeller/display_list/aiks_context.h"
#include "impeller/display_list/dl_dispatcher.h" // nogncheck
#include "impeller/golden_tests/golden_digest.h"
-#include "impeller/golden_tests/metal_screenshot.h"
-#include "impeller/golden_tests/metal_screenshotter.h"
+#include "impeller/golden_tests/metal_golden_screenshotter.h"
#include "impeller/golden_tests/working_directory.h"
namespace impeller {
@@ -59,9 +58,9 @@
class GoldenTests : public ::testing::Test {
public:
GoldenTests()
- : screenshotter_(new MetalScreenshotter(GetPlaygroundSwitches())) {}
+ : screenshotter_(new MetalGoldenScreenshotter(GetPlaygroundSwitches())) {}
- MetalScreenshotter& Screenshotter() { return *screenshotter_; }
+ MetalGoldenScreenshotter& Screenshotter() { return *screenshotter_; }
void SetUp() override {
testing::GoldenDigest::Instance()->AddDimension(
@@ -74,7 +73,7 @@
// autorelease pool.
fml::ScopedNSAutoreleasePool autorelease_pool_;
- std::unique_ptr<MetalScreenshotter> screenshotter_;
+ std::unique_ptr<MetalGoldenScreenshotter> screenshotter_;
};
TEST_F(GoldenTests, ConicalGradient) {
diff --git a/engine/src/flutter/impeller/golden_tests/metal_golden_screenshotter.h b/engine/src/flutter/impeller/golden_tests/metal_golden_screenshotter.h
new file mode 100644
index 0000000..69aa402
--- /dev/null
+++ b/engine/src/flutter/impeller/golden_tests/metal_golden_screenshotter.h
@@ -0,0 +1,35 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef FLUTTER_IMPELLER_GOLDEN_TESTS_METAL_GOLDEN_SCREENSHOTTER_H_
+#define FLUTTER_IMPELLER_GOLDEN_TESTS_METAL_GOLDEN_SCREENSHOTTER_H_
+
+#include "flutter/impeller/golden_tests/golden_screenshotter.h"
+#include "impeller/display_list/aiks_context.h"
+
+namespace impeller {
+namespace testing {
+
+/// Converts `Picture`s and `DisplayList`s to `MetalScreenshot`s with the
+/// playground backend.
+class MetalGoldenScreenshotter : public GoldenScreenshotter {
+ public:
+ explicit MetalGoldenScreenshotter(const PlaygroundSwitches& switches);
+
+ ~MetalGoldenScreenshotter();
+
+ std::unique_ptr<Screenshot> MakeScreenshot(
+ const AiksContext& aiks_context,
+ const std::shared_ptr<Texture>& texture) override;
+
+ PlaygroundImpl& GetPlayground() override;
+
+ private:
+ std::unique_ptr<PlaygroundImpl> playground_;
+};
+
+} // namespace testing
+} // namespace impeller
+
+#endif // FLUTTER_IMPELLER_GOLDEN_TESTS_METAL_GOLDEN_SCREENSHOTTER_H_
diff --git a/engine/src/flutter/impeller/golden_tests/metal_golden_screenshotter.mm b/engine/src/flutter/impeller/golden_tests/metal_golden_screenshotter.mm
new file mode 100644
index 0000000..ac87ed1
--- /dev/null
+++ b/engine/src/flutter/impeller/golden_tests/metal_golden_screenshotter.mm
@@ -0,0 +1,36 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <dlfcn.h>
+#include <filesystem>
+#include <memory>
+
+#include "flutter/impeller/golden_tests/metal_golden_screenshotter.h"
+
+#include "flutter/impeller/testing/metal/metal_screenshotter.h"
+#include "third_party/glfw/include/GLFW/glfw3.h"
+
+namespace impeller {
+namespace testing {
+
+MetalGoldenScreenshotter::MetalGoldenScreenshotter(
+ const PlaygroundSwitches& switches) {
+ FML_CHECK(::glfwInit() == GLFW_TRUE);
+ playground_ = PlaygroundImpl::Create(PlaygroundBackend::kMetal, switches);
+}
+
+MetalGoldenScreenshotter::~MetalGoldenScreenshotter() = default;
+
+std::unique_ptr<Screenshot> MetalGoldenScreenshotter::MakeScreenshot(
+ const AiksContext& aiks_context,
+ const std::shared_ptr<Texture>& texture) {
+ return MetalScreenshotter::MakeScreenshot(aiks_context.GetContext(), texture);
+}
+
+PlaygroundImpl& MetalGoldenScreenshotter::GetPlayground() {
+ return *playground_;
+}
+
+} // namespace testing
+} // namespace impeller
diff --git a/engine/src/flutter/impeller/golden_tests/metal_screenshotter.h b/engine/src/flutter/impeller/golden_tests/metal_screenshotter.h
deleted file mode 100644
index 2630811..0000000
--- a/engine/src/flutter/impeller/golden_tests/metal_screenshotter.h
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright 2013 The Flutter Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef FLUTTER_IMPELLER_GOLDEN_TESTS_METAL_SCREENSHOTTER_H_
-#define FLUTTER_IMPELLER_GOLDEN_TESTS_METAL_SCREENSHOTTER_H_
-
-#include "flutter/impeller/golden_tests/metal_screenshot.h"
-#include "flutter/impeller/golden_tests/screenshotter.h"
-#include "flutter/impeller/playground/playground_impl.h"
-#include "impeller/display_list/aiks_context.h"
-
-namespace impeller {
-namespace testing {
-
-/// Converts `Picture`s and `DisplayList`s to `MetalScreenshot`s with the
-/// playground backend.
-class MetalScreenshotter : public Screenshotter {
- public:
- explicit MetalScreenshotter(const PlaygroundSwitches& switches);
-
- std::unique_ptr<Screenshot> MakeScreenshot(
- const AiksContext& aiks_context,
- const std::shared_ptr<Texture> texture) override;
-
- PlaygroundImpl& GetPlayground() override { return *playground_; }
-
- private:
- std::unique_ptr<PlaygroundImpl> playground_;
-};
-
-} // namespace testing
-} // namespace impeller
-
-#endif // FLUTTER_IMPELLER_GOLDEN_TESTS_METAL_SCREENSHOTTER_H_
diff --git a/engine/src/flutter/impeller/golden_tests/vulkan_golden_screenshotter.h b/engine/src/flutter/impeller/golden_tests/vulkan_golden_screenshotter.h
new file mode 100644
index 0000000..b645717
--- /dev/null
+++ b/engine/src/flutter/impeller/golden_tests/vulkan_golden_screenshotter.h
@@ -0,0 +1,36 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef FLUTTER_IMPELLER_GOLDEN_TESTS_VULKAN_GOLDEN_SCREENSHOTTER_H_
+#define FLUTTER_IMPELLER_GOLDEN_TESTS_VULKAN_GOLDEN_SCREENSHOTTER_H_
+
+#include "flutter/impeller/golden_tests/golden_screenshotter.h"
+#include "impeller/display_list/aiks_context.h"
+
+namespace impeller {
+namespace testing {
+
+/// Converts `Picture`s and `DisplayList`s to `MetalScreenshot`s with the
+/// playground backend.
+class VulkanGoldenScreenshotter : public GoldenScreenshotter {
+ public:
+ explicit VulkanGoldenScreenshotter(
+ const std::unique_ptr<PlaygroundImpl>& playground);
+
+ ~VulkanGoldenScreenshotter();
+
+ std::unique_ptr<Screenshot> MakeScreenshot(
+ const AiksContext& aiks_context,
+ const std::shared_ptr<Texture>& texture) override;
+
+ PlaygroundImpl& GetPlayground() override;
+
+ private:
+ const std::unique_ptr<PlaygroundImpl>& playground_;
+};
+
+} // namespace testing
+} // namespace impeller
+
+#endif // FLUTTER_IMPELLER_GOLDEN_TESTS_VULKAN_GOLDEN_SCREENSHOTTER_H_
diff --git a/engine/src/flutter/impeller/golden_tests/vulkan_golden_screenshotter.mm b/engine/src/flutter/impeller/golden_tests/vulkan_golden_screenshotter.mm
new file mode 100644
index 0000000..bb77635
--- /dev/null
+++ b/engine/src/flutter/impeller/golden_tests/vulkan_golden_screenshotter.mm
@@ -0,0 +1,35 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "flutter/impeller/golden_tests/vulkan_golden_screenshotter.h"
+
+#include "flutter/fml/synchronization/waitable_event.h"
+#include "flutter/impeller/testing/vulkan/vulkan_screenshotter.h"
+#define GLFW_INCLUDE_NONE
+#include "third_party/glfw/include/GLFW/glfw3.h"
+
+namespace impeller {
+namespace testing {
+
+VulkanGoldenScreenshotter::VulkanGoldenScreenshotter(
+ const std::unique_ptr<PlaygroundImpl>& playground)
+ : playground_(playground) {
+ FML_CHECK(playground_);
+}
+
+VulkanGoldenScreenshotter::~VulkanGoldenScreenshotter() = default;
+
+std::unique_ptr<Screenshot> VulkanGoldenScreenshotter::MakeScreenshot(
+ const AiksContext& aiks_context,
+ const std::shared_ptr<Texture>& texture) {
+ return VulkanScreenshotter::MakeScreenshot(aiks_context.GetContext(),
+ texture);
+}
+
+PlaygroundImpl& VulkanGoldenScreenshotter::GetPlayground() {
+ return *playground_;
+}
+
+} // namespace testing
+} // namespace impeller
diff --git a/engine/src/flutter/impeller/golden_tests/vulkan_screenshotter.h b/engine/src/flutter/impeller/golden_tests/vulkan_screenshotter.h
deleted file mode 100644
index 42af7c6..0000000
--- a/engine/src/flutter/impeller/golden_tests/vulkan_screenshotter.h
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright 2013 The Flutter Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef FLUTTER_IMPELLER_GOLDEN_TESTS_VULKAN_SCREENSHOTTER_H_
-#define FLUTTER_IMPELLER_GOLDEN_TESTS_VULKAN_SCREENSHOTTER_H_
-
-#include "flutter/impeller/golden_tests/metal_screenshot.h"
-#include "flutter/impeller/golden_tests/screenshotter.h"
-#include "flutter/impeller/playground/playground_impl.h"
-
-namespace impeller {
-namespace testing {
-
-/// Converts `Picture`s and `DisplayList`s to `MetalScreenshot`s with the
-/// playground backend.
-class VulkanScreenshotter : public Screenshotter {
- public:
- explicit VulkanScreenshotter(
- const std::unique_ptr<PlaygroundImpl>& playground);
-
- std::unique_ptr<Screenshot> MakeScreenshot(
- const AiksContext& aiks_context,
- const std::shared_ptr<Texture> texture) override;
-
- PlaygroundImpl& GetPlayground() override { return *playground_; }
-
- private:
- const std::unique_ptr<PlaygroundImpl>& playground_;
-};
-
-} // namespace testing
-} // namespace impeller
-
-#endif // FLUTTER_IMPELLER_GOLDEN_TESTS_VULKAN_SCREENSHOTTER_H_
diff --git a/engine/src/flutter/impeller/playground/BUILD.gn b/engine/src/flutter/impeller/playground/BUILD.gn
index eb0ab23..74ff182 100644
--- a/engine/src/flutter/impeller/playground/BUILD.gn
+++ b/engine/src/flutter/impeller/playground/BUILD.gn
@@ -22,6 +22,8 @@
deps = [
"../entity",
"../renderer/backend",
+ "//flutter/impeller/testing:golden_digest_manager",
+ "//flutter/impeller/testing:screenshot",
"//flutter/impeller/typographer/backends/skia:typographer_skia_backend",
"//flutter/testing:test_swiftshader_utils",
]
@@ -72,6 +74,8 @@
"AppKit.framework",
"QuartzCore.framework",
]
+
+ deps += [ "//flutter/impeller/testing:metal_screenshot" ]
}
}
@@ -87,6 +91,7 @@
public_deps = [
":playground",
+ "//flutter/impeller/testing:golden_digest_manager",
"//flutter/testing:testing_lib",
]
}
diff --git a/engine/src/flutter/impeller/playground/backend/gles/playground_impl_gles.cc b/engine/src/flutter/impeller/playground/backend/gles/playground_impl_gles.cc
index 13e8a9c..62c2755 100644
--- a/engine/src/flutter/impeller/playground/backend/gles/playground_impl_gles.cc
+++ b/engine/src/flutter/impeller/playground/backend/gles/playground_impl_gles.cc
@@ -30,22 +30,6 @@
#include "impeller/renderer/backend/gles/context_gles.h"
#include "impeller/renderer/backend/gles/surface_gles.h"
-namespace {
-
-class GLESPlaygroundEnvironment : public ::testing::Environment {
- public:
- std::unique_ptr<impeller::PlaygroundImplGLES::ShareableContext>
- shared_context;
- std::unique_ptr<impeller::PlaygroundImplGLES::ShareableContext>
- shared_context_sdf;
-};
-
-GLESPlaygroundEnvironment* g_gles_playground_env =
- static_cast<GLESPlaygroundEnvironment*>(
- ::testing::AddGlobalTestEnvironment(new GLESPlaygroundEnvironment()));
-
-} // namespace
-
namespace impeller {
class PlaygroundImplGLES::ReactorWorker final : public ReactorGLES::Worker {
@@ -82,7 +66,7 @@
ShareableContext(UniqueHandle window,
std::shared_ptr<ReactorWorker> worker,
std::shared_ptr<ContextGLES> context,
- PlaygroundSwitches switches)
+ const PlaygroundSwitches& switches)
: window(std::move(window)),
worker(std::move(worker)),
context(std::move(context)),
@@ -106,9 +90,19 @@
std::shared_ptr<ReactorWorker> worker;
std::shared_ptr<ContextGLES> context;
- PlaygroundSwitches switches;
+ const PlaygroundSwitches switches;
};
+std::unique_ptr<PlaygroundImplGLES::ShareableContext>
+ PlaygroundImplGLES::shared_context_msaa_;
+std::unique_ptr<PlaygroundImplGLES::ShareableContext>
+ PlaygroundImplGLES::shared_context_sdf_;
+
+void PlaygroundImplGLES::OnTearDownTestEnvironment() {
+ shared_context_msaa_.reset();
+ shared_context_sdf_.reset();
+}
+
void PlaygroundImplGLES::DestroyWindowHandle(WindowHandle handle) {
if (!handle) {
return;
@@ -119,7 +113,7 @@
static std::vector<std::shared_ptr<fml::Mapping>>
ShaderLibraryMappingsForPlayground(bool is_gles3);
-PlaygroundImplGLES::PlaygroundImplGLES(PlaygroundSwitches switches)
+PlaygroundImplGLES::PlaygroundImplGLES(const PlaygroundSwitches& switches)
: PlaygroundImpl(switches),
handle_(nullptr, &DestroyWindowHandle),
use_angle_(switches.use_angle) {
@@ -155,10 +149,8 @@
return unique_context_;
}
- FML_CHECK(g_gles_playground_env != nullptr);
std::unique_ptr<PlaygroundImplGLES::ShareableContext>& shared_context =
- switches_.flags.use_sdfs ? g_gles_playground_env->shared_context_sdf
- : g_gles_playground_env->shared_context;
+ switches_.flags.use_sdfs ? shared_context_sdf_ : shared_context_msaa_;
// If the switches have values that result in a different GLES context than
// the existing shared context, reset the shared context to create a new one.
diff --git a/engine/src/flutter/impeller/playground/backend/gles/playground_impl_gles.h b/engine/src/flutter/impeller/playground/backend/gles/playground_impl_gles.h
index de3b7ec..4c6c57f 100644
--- a/engine/src/flutter/impeller/playground/backend/gles/playground_impl_gles.h
+++ b/engine/src/flutter/impeller/playground/backend/gles/playground_impl_gles.h
@@ -17,13 +17,15 @@
public:
struct ShareableContext;
- explicit PlaygroundImplGLES(PlaygroundSwitches switches);
+ explicit PlaygroundImplGLES(const PlaygroundSwitches& switches);
~PlaygroundImplGLES();
fml::Status SetCapabilities(
const std::shared_ptr<Capabilities>& capabilities) override;
+ static void OnTearDownTestEnvironment();
+
private:
class ReactorWorker;
@@ -65,6 +67,12 @@
static std::unique_ptr<ShareableContext> MakeShareableContext(
const PlaygroundSwitches& switches);
+ // These store longer-term shared contexts that are shared between
+ // tests in the playground suite while the flags that affect the
+ // construction of the context remain consistent.
+ static std::unique_ptr<ShareableContext> shared_context_msaa_;
+ static std::unique_ptr<ShareableContext> shared_context_sdf_;
+
RuntimeStageBackend GetRuntimeStageBackend() const override;
PlaygroundImplGLES(const PlaygroundImplGLES&) = delete;
diff --git a/engine/src/flutter/impeller/playground/backend/metal/playground_impl_mtl.h b/engine/src/flutter/impeller/playground/backend/metal/playground_impl_mtl.h
index 4ba9a08..5dbae51 100644
--- a/engine/src/flutter/impeller/playground/backend/metal/playground_impl_mtl.h
+++ b/engine/src/flutter/impeller/playground/backend/metal/playground_impl_mtl.h
@@ -19,7 +19,7 @@
class PlaygroundImplMTL final : public PlaygroundImpl {
public:
- explicit PlaygroundImplMTL(PlaygroundSwitches switches);
+ explicit PlaygroundImplMTL(const PlaygroundSwitches& switches);
~PlaygroundImplMTL();
diff --git a/engine/src/flutter/impeller/playground/backend/metal/playground_impl_mtl.mm b/engine/src/flutter/impeller/playground/backend/metal/playground_impl_mtl.mm
index 12c596f..237c51a 100644
--- a/engine/src/flutter/impeller/playground/backend/metal/playground_impl_mtl.mm
+++ b/engine/src/flutter/impeller/playground/backend/metal/playground_impl_mtl.mm
@@ -62,7 +62,7 @@
::glfwDestroyWindow(reinterpret_cast<GLFWwindow*>(handle));
}
-PlaygroundImplMTL::PlaygroundImplMTL(PlaygroundSwitches switches)
+PlaygroundImplMTL::PlaygroundImplMTL(const PlaygroundSwitches& switches)
: PlaygroundImpl(switches),
handle_(nullptr, &DestroyWindowHandle),
data_(std::make_unique<Data>()),
diff --git a/engine/src/flutter/impeller/playground/backend/vulkan/playground_impl_vk.cc b/engine/src/flutter/impeller/playground/backend/vulkan/playground_impl_vk.cc
index ab95717..cc7bfe6 100644
--- a/engine/src/flutter/impeller/playground/backend/vulkan/playground_impl_vk.cc
+++ b/engine/src/flutter/impeller/playground/backend/vulkan/playground_impl_vk.cc
@@ -63,7 +63,7 @@
::glfwDestroyWindow(reinterpret_cast<GLFWwindow*>(handle));
}
-PlaygroundImplVK::PlaygroundImplVK(PlaygroundSwitches switches)
+PlaygroundImplVK::PlaygroundImplVK(const PlaygroundSwitches& switches)
: PlaygroundImpl(switches), handle_(nullptr, &DestroyWindowHandle) {
FML_CHECK(IsVulkanDriverPresent());
diff --git a/engine/src/flutter/impeller/playground/backend/vulkan/playground_impl_vk.h b/engine/src/flutter/impeller/playground/backend/vulkan/playground_impl_vk.h
index 9ea956f..9162afb 100644
--- a/engine/src/flutter/impeller/playground/backend/vulkan/playground_impl_vk.h
+++ b/engine/src/flutter/impeller/playground/backend/vulkan/playground_impl_vk.h
@@ -14,7 +14,7 @@
public:
static bool IsVulkanDriverPresent();
- explicit PlaygroundImplVK(PlaygroundSwitches switches);
+ explicit PlaygroundImplVK(const PlaygroundSwitches& switches);
~PlaygroundImplVK();
diff --git a/engine/src/flutter/impeller/playground/playground.cc b/engine/src/flutter/impeller/playground/playground.cc
index b2331c6..5868bff 100644
--- a/engine/src/flutter/impeller/playground/playground.cc
+++ b/engine/src/flutter/impeller/playground/playground.cc
@@ -15,6 +15,7 @@
#include "impeller/renderer/command_buffer.h"
#include "impeller/renderer/render_target.h"
#include "impeller/runtime_stage/runtime_stage.h"
+#include "third_party/googletest/googletest/include/gtest/gtest.h"
#define GLFW_INCLUDE_NONE
#include "third_party/glfw/include/GLFW/glfw3.h"
@@ -31,6 +32,8 @@
#include "impeller/renderer/backend/gles/context_gles.h"
#include "impeller/renderer/context.h"
#include "impeller/renderer/render_pass.h"
+#include "impeller/testing/golden_digest_manager.h"
+#include "impeller/testing/screenshotter.h"
#include "impeller/typographer/backends/skia/typographer_context_skia.h"
#include "third_party/imgui/backends/imgui_impl_glfw.h"
#include "third_party/imgui/imgui.h"
@@ -45,6 +48,25 @@
namespace impeller {
+namespace {
+std::string GetTestName() {
+ std::string suite_name =
+ ::testing::UnitTest::GetInstance()->current_test_suite()->name();
+ std::string test_name =
+ ::testing::UnitTest::GetInstance()->current_test_info()->name();
+ std::stringstream ss;
+ ss << "impeller_" << suite_name << "_" << test_name;
+ std::string result = ss.str();
+ // Make sure there are no slashes in the test name.
+ std::replace(result.begin(), result.end(), '/', '_');
+ return result;
+}
+
+std::string GetGoldenFilename(const std::string& postfix = "") {
+ return GetTestName() + postfix + ".png";
+}
+} // namespace
+
std::string PlaygroundBackendToString(PlaygroundBackend backend) {
switch (backend) {
case PlaygroundBackend::kMetal:
@@ -87,6 +109,10 @@
});
}
+testing::GoldenDigestManager* Playground::GetGoldenDigestManager() const {
+ return nullptr;
+}
+
Playground::Playground(PlaygroundBackend backend,
const PlaygroundSwitches& switches)
: backend_(backend), switches_(switches) {
@@ -103,12 +129,55 @@
bool Playground::PlatformSupportsWideGamutTests() const {
#if __arm64__ && FML_OS_MACOSX
- return backend_ == PlaygroundBackend::kMetal;
+ switch (backend_) {
+ case PlaygroundBackend::kMetal:
+ case PlaygroundBackend::kMetalSDF:
+ return true;
+ case PlaygroundBackend::kOpenGLES:
+ case PlaygroundBackend::kOpenGLESSDF:
+ case PlaygroundBackend::kVulkan:
+ return false;
+ }
#else
return false;
#endif
}
+bool Playground::RenderingSupportsMSAA() const {
+ // We could call GetContext(), but we don't want to cause it to be
+ // created just yet. So, we make some assumptions here. If they are
+ // insufficient then we should beef them up rather than just calling
+ // GetContext() if we can.
+ // Also, technically, we should check if it supports OffscreenMSAA
+ // which might be a subset of supporting MSAA on screen, but for now
+ // they seem to be closely related.
+ switch (backend_) {
+ case PlaygroundBackend::kMetal:
+ case PlaygroundBackend::kMetalSDF:
+ return true;
+ case PlaygroundBackend::kOpenGLES:
+ case PlaygroundBackend::kOpenGLESSDF:
+ return false;
+ case PlaygroundBackend::kVulkan:
+ return true;
+ }
+}
+
+SampleCount Playground::GetDefaultSampleCount() const {
+ return RenderingSupportsMSAA() ? SampleCount::kCount4 : SampleCount::kCount1;
+}
+
+bool Playground::InitializePipelineDescriptorForRendering(
+ PipelineDescriptor& desc) const {
+ // Match the golden/verirication harness render target:
+ // - msaa or single samples depending on the Context
+ // - no depth or stencil
+ desc.SetSampleCount(GetDefaultSampleCount());
+ desc.ClearStencilAttachments();
+ desc.ClearDepthAttachment();
+ return true;
+}
+
bool Playground::EnsureContextSupportsWideGamut() {
FML_CHECK(!context_) << "Must be called before a context is created.";
if (!PlatformSupportsWideGamutTests()) {
@@ -118,9 +187,10 @@
return true;
}
-void Playground::EnsureContextSupportsAntialiasLines() {
+bool Playground::EnsureContextSupportsAntialiasLines() {
FML_CHECK(!context_) << "Must be called before a context is created.";
switches_.flags.antialiased_lines = true;
+ return true;
}
std::shared_ptr<Context> Playground::GetContext() const {
@@ -284,19 +354,120 @@
cursor_position_ = pos;
}
+bool Playground::ShouldWriteGoldenImage() {
+ return should_write_golden_;
+}
+
+void Playground::SetEnableWriteGolden(bool write_golden) {
+ should_write_golden_ = write_golden;
+}
+
+bool Playground::RenderImage(const RenderCallback& callback,
+ bool write_result) {
+ std::shared_ptr<Context> context = GetContext();
+ if (!context) {
+ return false;
+ }
+
+ AiksContext renderer(context, typographer_context_);
+ Point content_scale = GetContentScale();
+ ISize size(std::round(GetWindowSize().width * content_scale.x),
+ std::round(GetWindowSize().height * content_scale.y));
+
+ std::string label =
+ write_result ? "Golden Render Pass" : "Playground Verification Pass";
+ RenderTargetAllocator render_target_allocator(
+ context->GetResourceAllocator());
+ RenderTarget render_target;
+ if (context->GetCapabilities()->SupportsOffscreenMSAA()) {
+ render_target = render_target_allocator.CreateOffscreenMSAA(
+ *context, size, /*mip_count=*/1, label + " (MSAA)",
+ RenderTarget::kDefaultColorAttachmentConfigMSAA,
+ /*stencil_attachment_config=*/std::nullopt);
+ } else {
+ render_target = render_target_allocator.CreateOffscreen(
+ *context, size, /*mip_count=*/1, label,
+ RenderTarget::kDefaultColorAttachmentConfig,
+ /*stencil_attachment_config=*/std::nullopt);
+ }
+ if (!render_target.IsValid()) {
+ return false;
+ }
+ if (!callback(render_target)) {
+ return false;
+ }
+ if (write_result && !WriteGoldenImage(render_target)) {
+ return false;
+ }
+ return true;
+}
+
+bool Playground::WriteGoldenImage(const RenderTarget& render_target,
+ const std::string& postfix) {
+ testing::GoldenDigestManager* digest = GetGoldenDigestManager();
+ if (!digest) {
+ FML_LOG(ERROR) << "Golden image has no working directory";
+ return false;
+ }
+
+ std::shared_ptr<Context> context = GetContext();
+ if (!context) {
+ return false;
+ }
+
+ digest->AddDimension("gpu_string", context->DescribeGpuModel());
+
+ std::string test_name = GetTestName();
+
+ std::unique_ptr<testing::Screenshot> screenshot =
+ testing::Screenshotter::MakeScreenshot(
+ context, render_target.GetRenderTargetTexture());
+ if (!screenshot || !screenshot->GetBytes()) {
+ FML_LOG(ERROR) << "Failed to collect screenshot for test " << test_name;
+ return false;
+ }
+
+ std::string filename = GetGoldenFilename(postfix);
+ std::string filenamepath = digest->GetFullPath(filename);
+ if (!screenshot->WriteToPNG(filenamepath)) {
+ FML_LOG(ERROR) << "Failed to write screenshot to " << filenamepath;
+ return false;
+ }
+ digest->AddImage(test_name, filename, //
+ screenshot->GetWidth(), screenshot->GetHeight());
+
+ return true;
+}
+
bool Playground::OpenPlaygroundHere(
const Playground::RenderCallback& render_callback) {
std::shared_ptr<Context> context = GetContext();
FML_CHECK(context);
- if (!switches_.enable_playground) {
- return true;
- }
-
if (!render_callback) {
return true;
}
+ auto window = reinterpret_cast<GLFWwindow*>(impl_->GetWindowHandle());
+ if (!window) {
+ return false;
+ }
+ ::glfwSetWindowSize(window, GetWindowSize().width, GetWindowSize().height);
+
+ bool writing_golden = GetGoldenDigestManager() && should_write_golden_;
+ if (!switches_.enable_playground || writing_golden) {
+ bool success = RenderImage(render_callback, false);
+ if (success && writing_golden) {
+ // Render twice for a golden result so the second pass observes warmed
+ // pipeline and resource caches.
+ success = RenderImage(render_callback, true);
+ }
+ if (!success || !switches_.enable_playground) {
+ return success;
+ }
+ }
+ FML_CHECK(switches_.enable_playground);
+
IMGUI_CHECKVERSION();
ImGui::CreateContext();
fml::ScopedCleanupClosure destroy_imgui_context(
@@ -308,10 +479,6 @@
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
io.ConfigWindowsResizeFromEdges = true;
- auto window = reinterpret_cast<GLFWwindow*>(GetImpl()->GetWindowHandle());
- if (!window) {
- return false;
- }
::glfwSetWindowTitle(window, GetWindowTitle().c_str());
::glfwSetWindowUserPointer(window, this);
::glfwSetWindowSizeCallback(
@@ -339,7 +506,6 @@
ImGui::SetNextWindowPos({10, 10});
- ::glfwSetWindowSize(window, GetWindowSize().width, GetWindowSize().height);
::glfwSetWindowPos(window, 200, 100);
::glfwShowWindow(window);
@@ -420,7 +586,7 @@
return true;
}
-bool Playground::OpenPlaygroundHere(SinglePassCallback pass_callback) {
+bool Playground::OpenPlaygroundHere(const SinglePassCallback& pass_callback) {
return OpenPlaygroundHere(
[context = GetContext(), &pass_callback](RenderTarget& render_target) {
auto buffer = context->CreateCommandBuffer();
@@ -603,10 +769,6 @@
return GetImpl()->SetCapabilities(capabilities);
}
-bool Playground::WillRenderSomething() const {
- return switches_.enable_playground;
-}
-
Playground::GLProcAddressResolver Playground::CreateGLProcAddressResolver()
const {
return GetImpl()->CreateGLProcAddressResolver();
diff --git a/engine/src/flutter/impeller/playground/playground.h b/engine/src/flutter/impeller/playground/playground.h
index dc2f704..664e231 100644
--- a/engine/src/flutter/impeller/playground/playground.h
+++ b/engine/src/flutter/impeller/playground/playground.h
@@ -24,6 +24,10 @@
class PlaygroundImpl;
+namespace testing {
+class GoldenDigestManager;
+}
+
enum class PlaygroundBackend {
kMetal,
kMetalSDF,
@@ -69,9 +73,17 @@
using RenderCallback = std::function<bool(RenderTarget& render_target)>;
+ /// @brief Whether this instance will write a golden image of the output
+ /// from |OpenPlaygroundHere|.
+ bool ShouldWriteGoldenImage();
+
+ /// @brief Sets a particular test to either write a golden or not, false
+ /// by default.
+ void SetEnableWriteGolden(bool write_golden);
+
bool OpenPlaygroundHere(const RenderCallback& render_callback);
- bool OpenPlaygroundHere(SinglePassCallback pass_callback);
+ bool OpenPlaygroundHere(const SinglePassCallback& pass_callback);
static std::shared_ptr<CompressedImage> LoadFixtureImageCompressed(
std::shared_ptr<fml::Mapping> mapping);
@@ -101,9 +113,6 @@
[[nodiscard]] fml::Status SetCapabilities(
const std::shared_ptr<Capabilities>& capabilities);
- /// Returns true if `OpenPlaygroundHere` will actually render anything.
- bool WillRenderSomething() const;
-
using GLProcAddressResolver = std::function<void*(const char* proc_name)>;
GLProcAddressResolver CreateGLProcAddressResolver() const;
@@ -118,6 +127,11 @@
RuntimeStageBackend GetRuntimeStageBackend() const;
+ /// @brief Initializes the provided |PipelineDescriptor| with appropriate
+ /// default values to match the conditions under which a pipeline
+ /// will be rendered.
+ bool InitializePipelineDescriptorForRendering(PipelineDescriptor& desc) const;
+
protected:
// This method could override testing::Test::TearDown() directly, but
// since we don't inherit from that Test class the override would not
@@ -138,6 +152,12 @@
/// @brief Returns true if the platform can support wide gamuts.
bool PlatformSupportsWideGamutTests() const;
+ /// @brief Returns true if the rendering path supports MSAA rendering.
+ bool RenderingSupportsMSAA() const;
+
+ /// @brief Returns the default sample count of the rendering path.
+ SampleCount GetDefaultSampleCount() const;
+
/// @brief Make sure that when the context is later created that it
/// will support wide gamuts if the platform supports it.
/// Returns whether the platform supports wide gamut.
@@ -156,7 +176,10 @@
///
/// Must be called before any other method except for the Ensure family
/// of methods.
- virtual void EnsureContextSupportsAntialiasLines();
+ ///
+ /// Callers should abort (such as via GTEST_SKIP) if the method returns
+ /// false if their behavior depends on the experimental AA lines.
+ [[nodiscard]] virtual bool EnsureContextSupportsAntialiasLines();
/// @brief Return an unmodifiable reference to the current switches.
/// The switches might change at the start of a test as it
@@ -171,6 +194,8 @@
void SetWindowSize(ISize size);
+ virtual testing::GoldenDigestManager* GetGoldenDigestManager() const;
+
private:
const PlaygroundBackend backend_;
PlaygroundSwitches switches_;
@@ -194,6 +219,7 @@
Point cursor_position_;
ISize window_size_ = ISize{1024, 768};
std::shared_ptr<HostBuffer> host_buffer_;
+ bool should_write_golden_ = false;
std::unique_ptr<PlaygroundImpl>& GetImpl() const;
@@ -203,6 +229,13 @@
void SetCursorPosition(Point pos);
+ [[nodiscard]]
+ bool RenderImage(const RenderCallback& render_callback, bool write_image);
+
+ [[nodiscard]]
+ bool WriteGoldenImage(const RenderTarget& render_target,
+ const std::string& postfix = "");
+
Playground(const Playground&) = delete;
Playground& operator=(const Playground&) = delete;
diff --git a/engine/src/flutter/impeller/playground/playground_impl.cc b/engine/src/flutter/impeller/playground/playground_impl.cc
index 22a9909..ba74554 100644
--- a/engine/src/flutter/impeller/playground/playground_impl.cc
+++ b/engine/src/flutter/impeller/playground/playground_impl.cc
@@ -58,7 +58,13 @@
FML_UNREACHABLE();
}
-PlaygroundImpl::PlaygroundImpl(PlaygroundSwitches switches)
+void PlaygroundImpl::OnTearDownTestEnvironment() {
+#if IMPELLER_ENABLE_OPENGLES
+ PlaygroundImplGLES::OnTearDownTestEnvironment();
+#endif // IMPELLER_ENABLE_OPENGLES
+}
+
+PlaygroundImpl::PlaygroundImpl(const PlaygroundSwitches& switches)
: switches_(switches) {}
PlaygroundImpl::~PlaygroundImpl() = default;
diff --git a/engine/src/flutter/impeller/playground/playground_impl.h b/engine/src/flutter/impeller/playground/playground_impl.h
index 1a26d38..ca6d27b 100644
--- a/engine/src/flutter/impeller/playground/playground_impl.h
+++ b/engine/src/flutter/impeller/playground/playground_impl.h
@@ -45,10 +45,12 @@
[[nodiscard]]
virtual RuntimeStageBackend GetRuntimeStageBackend() const = 0;
+ static void OnTearDownTestEnvironment();
+
protected:
const PlaygroundSwitches switches_;
- explicit PlaygroundImpl(PlaygroundSwitches switches);
+ explicit PlaygroundImpl(const PlaygroundSwitches& switches);
private:
PlaygroundImpl(const PlaygroundImpl&) = delete;
diff --git a/engine/src/flutter/impeller/playground/playground_test.cc b/engine/src/flutter/impeller/playground/playground_test.cc
index 84e6f2d..bdafbe2 100644
--- a/engine/src/flutter/impeller/playground/playground_test.cc
+++ b/engine/src/flutter/impeller/playground/playground_test.cc
@@ -2,11 +2,18 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "flutter/fml/time/time_point.h"
+#include "impeller/playground/playground_test.h"
+#ifndef FML_OS_WIN
+#include <wordexp.h>
+#endif
+
+#include "flutter/fml/file.h"
+#include "flutter/fml/time/time_point.h"
#include "impeller/base/timing.h"
#include "impeller/base/validation.h"
-#include "impeller/playground/playground_test.h"
+#include "impeller/playground/playground_impl.h"
+#include "impeller/testing/golden_digest_manager.h"
namespace impeller {
@@ -43,12 +50,132 @@
GTEST_SKIP() << "Skipping due to user action.";
return;
}
+
+ SetEnableWriteGolden(IsGoldenTestSuite());
}
void PlaygroundTest::TearDown() {
Playground::TearDownContextData();
}
+namespace {
+
+class PlaygroundTestEnvironment : public ::testing::Environment {
+ public:
+ static std::optional<std::string> ValidateGoldenDirectory(
+ const std::string& dir) {
+#ifdef FML_OS_WIN
+ return dir;
+#else // FML_OS_WIN
+ wordexp_t wordexp_result;
+ int code = wordexp(dir.c_str(), &wordexp_result, 0);
+ FML_CHECK(code == 0) << "Could not parse golden output directory: " << dir;
+ FML_CHECK(wordexp_result.we_wordc == 1u)
+ << "Exactly one directory must be specified for Golden image output: "
+ << dir;
+ std::optional<std::string> working_dir = wordexp_result.we_wordv[0];
+ wordfree(&wordexp_result);
+
+ FML_CHECK(working_dir) << "Unrecognized golden output directory: " << dir;
+ fml::UniqueFD directory = fml::OpenDirectory(
+ working_dir->c_str(), false, fml::FilePermission::kReadWrite);
+ FML_CHECK(fml::IsDirectory(directory))
+ << "Golden output directory must be a directory with read/write"
+ << " permissions: " << dir;
+ return working_dir;
+#endif // FML_OS_WIN
+ }
+
+ void SetUp() override {
+ const fml::CommandLine& args = ::flutter::testing::GetArgsForProcess();
+ std::string golden_output_dir;
+ if (args.GetOptionValue("golden_output_dir", &golden_output_dir)) {
+ const std::optional<std::string> validated_dir =
+ ValidateGoldenDirectory(golden_output_dir);
+ if (validated_dir) {
+ golden_manager_.emplace(*validated_dir);
+ } else {
+ FML_CHECK(validated_dir)
+ << "Did not recognize golden output directory: "
+ << golden_output_dir;
+ }
+ }
+ }
+
+ void TearDown() override {
+ if (golden_manager_) {
+ if (::testing::UnitTest::GetInstance()->Passed()) {
+ golden_manager_->Write();
+ } else {
+ FML_LOG(ERROR)
+ << ::testing::UnitTest::GetInstance()->failed_test_count()
+ << " tests failed, golden digest will not be written";
+ golden_manager_->ClearDigestData();
+ }
+ golden_manager_.reset();
+ }
+ PlaygroundImpl::OnTearDownTestEnvironment();
+ }
+
+ static testing::GoldenDigestManager* GetGoldenDigestManager() {
+ return golden_manager_ ? &golden_manager_.value() : nullptr;
+ }
+
+ private:
+ static std::optional<testing::GoldenDigestManager> golden_manager_;
+};
+
+std::optional<testing::GoldenDigestManager>
+ PlaygroundTestEnvironment::golden_manager_;
+
+} // namespace
+
+// Change these declarations to #defines to enable swiftshader or metal
+// validation.
+#undef APPLY_METAL_VALIDATION
+#undef ENABLE_VK_SWIFTSHADER
+
+void PlaygroundTest::SetupTestEnvironment() {
+#ifdef ENABLE_VK_SWIFTSHADER
+ // Make sure environment is set up for VK swiftshader
+ std::filesystem::path testing_assets_path =
+ flutter::testing::GetTestingAssetsPath();
+ std::filesystem::path target_path = testing_assets_path.parent_path()
+ .parent_path()
+ .parent_path()
+ .parent_path();
+ std::filesystem::path icd_path = target_path / "vk_swiftshader_icd.json";
+ setenv("VK_ICD_FILENAMES", icd_path.c_str(), 1);
+#endif
+
+#ifdef APPLY_METAL_VALIDATION
+ // https://developer.apple.com/documentation/metal/diagnosing_metal_programming_issues_early?language=objc
+ // Enables all shader validation tests.
+ setenv("MTL_SHADER_VALIDATION", "1", true);
+ // Validates accesses to device and constant memory.
+ setenv("MTL_SHADER_VALIDATION_GLOBAL_MEMORY", "1", true);
+ // Validates accesses to threadgroup memory.
+ setenv("MTL_SHADER_VALIDATION_THREADGROUP_MEMORY", "1", true);
+ // Validates that texture references are not nil.
+ setenv("MTL_SHADER_VALIDATION_TEXTURE_USAGE", "1", true);
+ // Enables metal validation.
+ setenv("METAL_DEBUG_ERROR_MODE", "0", true);
+ // Enables metal validation.
+ setenv("METAL_DEVICE_WRAPPER_TYPE", "1", true);
+#endif
+
+ ::testing::AddGlobalTestEnvironment(new PlaygroundTestEnvironment());
+}
+
+impeller::testing::GoldenDigestManager* PlaygroundTest::GetGoldenDigestManager()
+ const {
+ return PlaygroundTestEnvironment::GetGoldenDigestManager();
+}
+
+bool PlaygroundTest::IsGoldenTestSuite() const {
+ return false;
+}
+
PlaygroundBackend PlaygroundTest::GetBackend() const {
return GetParam();
}
diff --git a/engine/src/flutter/impeller/playground/playground_test.h b/engine/src/flutter/impeller/playground/playground_test.h
index 309c8e7..74c1b87 100644
--- a/engine/src/flutter/impeller/playground/playground_test.h
+++ b/engine/src/flutter/impeller/playground/playground_test.h
@@ -27,6 +27,8 @@
virtual ~PlaygroundTest();
+ static void SetupTestEnvironment();
+
void SetUp() override;
void TearDown() override;
@@ -43,6 +45,19 @@
// |Playground|
std::string GetWindowTitle() const override;
+ testing::GoldenDigestManager* GetGoldenDigestManager() const override;
+
+ protected:
+ /// @brief This method is overridden on a test suite basis and establishes
+ /// whether a given set of tests is intended to generate goldens.
+ ///
+ /// The return value of this method is used to set the default value of
+ /// |Playground::ShouldWriteGoldenImage|, but an individual test is still
+ /// allowed to enable a golden image by calling |SetEnableWriteGolden|.
+ ///
+ /// @return false by default unless overridden in a subclass
+ virtual bool IsGoldenTestSuite() const;
+
private:
// |Playground|
bool ShouldKeepRendering() const override;
@@ -56,6 +71,11 @@
PlaygroundTest& operator=(const PlaygroundTest&) = delete;
};
+class PlaygroundTestWithGoldens : public PlaygroundTest {
+ protected:
+ bool IsGoldenTestSuite() const override { return true; }
+};
+
#define INSTANTIATE_PLAYGROUND_SUITE(playground) \
[[maybe_unused]] const char* kYouInstantiated##playground##MultipleTimes = \
""; \
diff --git a/engine/src/flutter/impeller/renderer/BUILD.gn b/engine/src/flutter/impeller/renderer/BUILD.gn
index 4c91907..ee339a9 100644
--- a/engine/src/flutter/impeller/renderer/BUILD.gn
+++ b/engine/src/flutter/impeller/renderer/BUILD.gn
@@ -173,7 +173,7 @@
"//flutter/lib/gpu",
"//flutter/lib/snapshot",
"//flutter/runtime",
- "//flutter/testing",
"//flutter/testing:fixture_test",
+ "//flutter/testing:testing_lib",
]
}
diff --git a/engine/src/flutter/impeller/renderer/backend/vulkan/BUILD.gn b/engine/src/flutter/impeller/renderer/backend/vulkan/BUILD.gn
index a7860e7..13e3573 100644
--- a/engine/src/flutter/impeller/renderer/backend/vulkan/BUILD.gn
+++ b/engine/src/flutter/impeller/renderer/backend/vulkan/BUILD.gn
@@ -202,7 +202,7 @@
":mock_vulkan",
":unittests_fixtures",
":vulkan",
- "//flutter/testing",
+ "//flutter/testing:testing_lib",
]
defines = [ "TESTING" ]
@@ -215,7 +215,10 @@
output_name = "impeller_vulkan_android_unittests"
- deps = [ ":unittests_lib" ]
+ deps = [
+ ":unittests_lib",
+ "//flutter/testing",
+ ]
}
native_activity_apk("vulkan_android_apk_unittests") {
diff --git a/engine/src/flutter/impeller/renderer/backend/vulkan/descriptor_pool_vk.cc b/engine/src/flutter/impeller/renderer/backend/vulkan/descriptor_pool_vk.cc
index 4417896..6d6cf2b 100644
--- a/engine/src/flutter/impeller/renderer/backend/vulkan/descriptor_pool_vk.cc
+++ b/engine/src/flutter/impeller/renderer/backend/vulkan/descriptor_pool_vk.cc
@@ -80,7 +80,9 @@
vk::DescriptorSet set;
auto result = context_vk.GetDevice().allocateDescriptorSets(&set_info, &set);
- if (result == vk::Result::eErrorOutOfPoolMemory) {
+ if (result == vk::Result::eErrorOutOfPoolMemory ||
+ // FragmentedPool == OutOfMemory AND most likely due to fragmentation
+ result == vk::Result::eErrorFragmentedPool) {
// If the pool ran out of memory, we need to create a new pool.
CreateNewPool(context_vk);
set_info.setDescriptorPool(pools_.back().get());
diff --git a/engine/src/flutter/impeller/renderer/renderer_dart_unittests.cc b/engine/src/flutter/impeller/renderer/renderer_dart_unittests.cc
index 7d8171c..36653e0 100644
--- a/engine/src/flutter/impeller/renderer/renderer_dart_unittests.cc
+++ b/engine/src/flutter/impeller/renderer/renderer_dart_unittests.cc
@@ -287,10 +287,12 @@
TEST_P(RendererDartTest, CanRunDartInPlaygroundFrame) {
SinglePassCallback callback = [&](RenderPass& pass) {
- ImGui::Begin("Dart test", nullptr);
- ImGui::Text(
- "This test executes Dart code during the playground frame callback.");
- ImGui::End();
+ if (IsPlaygroundEnabled()) {
+ ImGui::Begin("Dart test", nullptr);
+ ImGui::Text(
+ "This test executes Dart code during the playground frame callback.");
+ ImGui::End();
+ }
return RunDartFunction("sayHi");
};
diff --git a/engine/src/flutter/impeller/renderer/renderer_golden_unittests.cc b/engine/src/flutter/impeller/renderer/renderer_golden_unittests.cc
index 8d2e48b..ab657c8 100644
--- a/engine/src/flutter/impeller/renderer/renderer_golden_unittests.cc
+++ b/engine/src/flutter/impeller/renderer/renderer_golden_unittests.cc
@@ -7,8 +7,6 @@
// golden harness and have their output uploaded to Skia Gold. They only build
// as part of the golden test executable.
-#ifdef IMPELLER_GOLDEN_TESTS
-
#include <array>
#include <cstdint>
#include <vector>
@@ -44,7 +42,12 @@
namespace impeller {
namespace testing {
+#ifdef IMPELLER_GOLDEN_TESTS
using RendererGoldenTest = GoldenPlaygroundTest;
+#else
+using RendererGoldenTest = PlaygroundTestWithGoldens;
+#endif
+
INSTANTIATE_PLAYGROUND_SUITE(RendererGoldenTest);
// Ported from RendererTest.BabysFirstTriangle. Draws a single gradient
@@ -59,14 +62,7 @@
auto desc = PipelineBuilder<VS, FS>::MakeDefaultPipelineDescriptor(*context);
ASSERT_TRUE(desc.has_value());
- // Match the golden harness render target: single-sampled, no depth/stencil.
- // `ClearStencilAttachments` also resets the stencil pixel format on the
- // pipeline, which Metal validation requires to match the target's lack of a
- // stencil texture; `SetStencilAttachmentDescriptors(nullopt)` alone leaves
- // the format set and trips that validation.
- desc->SetSampleCount(SampleCount::kCount1);
- desc->ClearStencilAttachments();
- desc->ClearDepthAttachment();
+ ASSERT_TRUE(InitializePipelineDescriptorForRendering(*desc));
auto pipeline = context->GetPipelineLibrary()->GetPipeline(desc).Get();
ASSERT_TRUE(pipeline);
@@ -112,10 +108,7 @@
auto desc = PipelineBuilder<VS, FS>::MakeDefaultPipelineDescriptor(*context);
ASSERT_TRUE(desc.has_value());
- // Match the golden harness render target: single-sampled, no depth/stencil.
- desc->SetSampleCount(SampleCount::kCount1);
- desc->ClearStencilAttachments();
- desc->ClearDepthAttachment();
+ ASSERT_TRUE(InitializePipelineDescriptorForRendering(*desc));
// Per-instance data is laid out contiguously, one record per instance.
struct InstanceData {
@@ -211,7 +204,7 @@
// through the golden harness. The pixel format and the raw block bytes are the
// only things that differ between the compressed families; the texture upload,
// pipeline, quad, and draw are shared by every compressed-format golden below.
-static void DrawCompressedTextureGolden(GoldenPlaygroundTest& test,
+static void DrawCompressedTextureGolden(RendererGoldenTest& test,
PixelFormat format,
const std::vector<uint8_t>& block_data,
ISize size) {
@@ -233,9 +226,7 @@
auto desc = PipelineBuilder<VS, FS>::MakeDefaultPipelineDescriptor(*context);
ASSERT_TRUE(desc.has_value());
- desc->SetSampleCount(SampleCount::kCount1);
- desc->ClearStencilAttachments();
- desc->ClearDepthAttachment();
+ ASSERT_TRUE(test.InitializePipelineDescriptorForRendering(*desc));
auto pipeline = context->GetPipelineLibrary()->GetPipeline(desc).Get();
ASSERT_TRUE(pipeline);
@@ -386,7 +377,7 @@
// so the golden would have been blank there. The base is four colored quadrants
// and the second level is solid orange, so LOD 0 renders the quadrants and LOD
// 1 renders solid orange.
-static void DrawManuallyMippedTextureGolden(GoldenPlaygroundTest& test,
+static void DrawManuallyMippedTextureGolden(RendererGoldenTest& test,
float lod) {
using VS = MipmapsVertexShader;
using FS = MipmapsFragmentShader;
@@ -459,9 +450,7 @@
auto desc = PipelineBuilder<VS, FS>::MakeDefaultPipelineDescriptor(*context);
ASSERT_TRUE(desc.has_value());
- desc->SetSampleCount(SampleCount::kCount1);
- desc->ClearStencilAttachments();
- desc->ClearDepthAttachment();
+ ASSERT_TRUE(test.InitializePipelineDescriptorForRendering(*desc));
auto pipeline = context->GetPipelineLibrary()->GetPipeline(desc).Get();
ASSERT_TRUE(pipeline);
@@ -517,5 +506,3 @@
} // namespace impeller
// NOLINTEND(bugprone-unchecked-optional-access)
-
-#endif // IMPELLER_GOLDEN_TESTS
diff --git a/engine/src/flutter/impeller/renderer/renderer_unittests.cc b/engine/src/flutter/impeller/renderer/renderer_unittests.cc
index a66c332..e0b024b 100644
--- a/engine/src/flutter/impeller/renderer/renderer_unittests.cc
+++ b/engine/src/flutter/impeller/renderer/renderer_unittests.cc
@@ -74,8 +74,10 @@
using BoxPipelineBuilder = PipelineBuilder<VS, FS>;
auto desc = BoxPipelineBuilder::MakeDefaultPipelineDescriptor(*context);
ASSERT_TRUE(desc.has_value());
+ ASSERT_TRUE(InitializePipelineDescriptorForRendering(*desc));
desc->SetSampleCount(SampleCount::kCount4);
- desc->SetStencilAttachmentDescriptors(std::nullopt);
+ desc->ClearStencilAttachments();
+ desc->ClearDepthAttachment();
// Vertex buffer.
VertexBufferBuilder<VS::PerVertexData> vertex_builder;
@@ -96,10 +98,12 @@
auto [data_host_buffer, indexes_host_buffer] = createHostBuffers(context);
SinglePassCallback callback = [&](RenderPass& pass) {
- ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
static bool wireframe;
- ImGui::Checkbox("Wireframe", &wireframe);
- ImGui::End();
+ if (IsPlaygroundEnabled()) {
+ ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
+ ImGui::Checkbox("Wireframe", &wireframe);
+ ImGui::End();
+ }
desc->SetPolygonMode(wireframe ? PolygonMode::kLine : PolygonMode::kFill);
auto pipeline = context->GetPipelineLibrary()->GetPipeline(desc).Get();
@@ -141,10 +145,9 @@
ASSERT_TRUE(context);
auto desc = PipelineBuilder<VS, FS>::MakeDefaultPipelineDescriptor(*context);
ASSERT_TRUE(desc.has_value());
+ ASSERT_TRUE(InitializePipelineDescriptorForRendering(*desc));
desc->SetCullMode(CullMode::kBackFace);
desc->SetWindingOrder(WindingOrder::kCounterClockwise);
- desc->SetSampleCount(SampleCount::kCount4);
- desc->ClearStencilAttachments();
// Setup the vertex layout to take two bindings. The first for positions and
// the second for colors.
@@ -206,10 +209,12 @@
static Degrees fov_y(60);
static Scalar distance = 10;
- ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
- ImGui::SliderFloat("Field of view", &fov_y.degrees, 0, 180);
- ImGui::SliderFloat("Camera distance", &distance, 0, 30);
- ImGui::End();
+ if (IsPlaygroundEnabled()) {
+ ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
+ ImGui::SliderFloat("Field of view", &fov_y.degrees, 0, 180);
+ ImGui::SliderFloat("Camera distance", &distance, 0, 30);
+ ImGui::End();
+ }
pass.SetCommandLabel("Perspective Cube");
pass.SetPipeline(pipeline);
@@ -253,8 +258,7 @@
using BoxPipelineBuilder = PipelineBuilder<VS, FS>;
auto desc = BoxPipelineBuilder::MakeDefaultPipelineDescriptor(*context);
ASSERT_TRUE(desc.has_value());
- desc->SetSampleCount(SampleCount::kCount4);
- desc->SetStencilAttachmentDescriptors(std::nullopt);
+ ASSERT_TRUE(InitializePipelineDescriptorForRendering(*desc));
auto box_pipeline =
context->GetPipelineLibrary()->GetPipeline(std::move(desc)).Get();
ASSERT_TRUE(box_pipeline);
@@ -444,16 +448,12 @@
VS::PerVertexData{Point{110, 110}},
});
- ASSERT_NE(GetContext(), nullptr);
- auto pipeline =
- GetContext()
- ->GetPipelineLibrary()
- ->GetPipeline(PipelineBuilder<VS, FS>::MakeDefaultPipelineDescriptor(
- *GetContext())
- ->SetSampleCount(SampleCount::kCount4)
- .SetStencilAttachmentDescriptors(std::nullopt))
-
- .Get();
+ std::shared_ptr<Context> context = GetContext();
+ ASSERT_TRUE(context);
+ auto desc = PipelineBuilder<VS, FS>::MakeDefaultPipelineDescriptor(*context);
+ ASSERT_TRUE(desc.has_value());
+ ASSERT_TRUE(InitializePipelineDescriptorForRendering(*desc));
+ auto pipeline = GetContext()->GetPipelineLibrary()->GetPipeline(desc).Get();
ASSERT_TRUE(pipeline && pipeline->IsValid());
static constexpr size_t kInstancesCount = 5u;
@@ -489,8 +489,9 @@
TEST_P(RendererTest, CanBlitTextureToTexture) {
if (GetBackend() == PlaygroundBackend::kOpenGLES ||
- GetBackend() == PlaygroundBackend::kOpenGLESSDF) {
- GTEST_SKIP() << "Mipmap test shader not supported on GLES.";
+ GetBackend() == PlaygroundBackend::kOpenGLESSDF ||
+ GetBackend() == PlaygroundBackend::kVulkan) {
+ GTEST_SKIP() << "Mipmap test shader not supported on GLES or Vulkan.";
}
auto context = GetContext();
ASSERT_TRUE(context);
@@ -499,8 +500,7 @@
using FS = MipmapsFragmentShader;
auto desc = PipelineBuilder<VS, FS>::MakeDefaultPipelineDescriptor(*context);
ASSERT_TRUE(desc.has_value());
- desc->SetSampleCount(SampleCount::kCount4);
- desc->SetStencilAttachmentDescriptors(std::nullopt);
+ ASSERT_TRUE(InitializePipelineDescriptorForRendering(*desc));
auto mipmaps_pipeline =
context->GetPipelineLibrary()->GetPipeline(std::move(desc)).Get();
ASSERT_TRUE(mipmaps_pipeline);
@@ -552,7 +552,13 @@
pass->SetLabel("Playground Blit Pass");
// Blit `bridge` to the top left corner of the texture.
- pass->AddCopy(bridge, texture);
+ // The bridge image is larger than the texture which can fail
+ // if Metal validation is enabled as it is in run_tests.py.
+ IRect bridge_bounds = IRect::MakeSize(bridge->GetSize());
+ IRect texture_bounds = IRect::MakeSize(texture->GetSize());
+ std::optional<IRect> blit_bounds =
+ bridge_bounds.Intersection(texture_bounds);
+ pass->AddCopy(bridge, texture, blit_bounds);
if (!pass->EncodeCommands()) {
return false;
@@ -610,8 +616,7 @@
using FS = MipmapsFragmentShader;
auto desc = PipelineBuilder<VS, FS>::MakeDefaultPipelineDescriptor(*context);
ASSERT_TRUE(desc.has_value());
- desc->SetSampleCount(SampleCount::kCount4);
- desc->SetStencilAttachmentDescriptors(std::nullopt);
+ ASSERT_TRUE(InitializePipelineDescriptorForRendering(*desc));
auto mipmaps_pipeline =
context->GetPipelineLibrary()->GetPipeline(std::move(desc)).Get();
ASSERT_TRUE(mipmaps_pipeline);
@@ -740,8 +745,7 @@
using FS = MipmapsFragmentShader;
auto desc = PipelineBuilder<VS, FS>::MakeDefaultPipelineDescriptor(*context);
ASSERT_TRUE(desc.has_value());
- desc->SetSampleCount(SampleCount::kCount4);
- desc->SetStencilAttachmentDescriptors(std::nullopt);
+ ASSERT_TRUE(InitializePipelineDescriptorForRendering(*desc));
auto mipmaps_pipeline =
context->GetPipelineLibrary()->GetPipeline(std::move(desc)).Get();
ASSERT_TRUE(mipmaps_pipeline);
@@ -780,13 +784,15 @@
static int selected_min_filter = 0;
static float lod = 4.5;
- ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
- ImGui::Combo("Mip filter", &selected_mip_filter, mip_filter_names,
- sizeof(mip_filter_names) / sizeof(char*));
- ImGui::Combo("Min filter", &selected_min_filter, min_filter_names,
- sizeof(min_filter_names) / sizeof(char*));
- ImGui::SliderFloat("LOD", &lod, 0, boston->GetMipCount() - 1);
- ImGui::End();
+ if (IsPlaygroundEnabled()) {
+ ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
+ ImGui::Combo("Mip filter", &selected_mip_filter, mip_filter_names,
+ sizeof(mip_filter_names) / sizeof(char*));
+ ImGui::Combo("Min filter", &selected_min_filter, min_filter_names,
+ sizeof(min_filter_names) / sizeof(char*));
+ ImGui::SliderFloat("LOD", &lod, 0, boston->GetMipCount() - 1);
+ ImGui::End();
+ }
auto buffer = context->CreateCommandBuffer();
if (!buffer) {
@@ -859,8 +865,7 @@
auto pipeline_descriptor =
PipelineBuilder<VS, FS>::MakeDefaultPipelineDescriptor(*context);
ASSERT_TRUE(pipeline_descriptor.has_value());
- pipeline_descriptor->SetSampleCount(SampleCount::kCount4);
- pipeline_descriptor->SetStencilAttachmentDescriptors(std::nullopt);
+ ASSERT_TRUE(InitializePipelineDescriptorForRendering(*pipeline_descriptor));
auto pipeline =
context->GetPipelineLibrary()->GetPipeline(pipeline_descriptor).Get();
ASSERT_TRUE(pipeline && pipeline->IsValid());
@@ -922,8 +927,7 @@
auto pipeline_descriptor =
PipelineBuilder<VS, FS>::MakeDefaultPipelineDescriptor(*context);
ASSERT_TRUE(pipeline_descriptor.has_value());
- pipeline_descriptor->SetSampleCount(SampleCount::kCount4);
- pipeline_descriptor->SetStencilAttachmentDescriptors(std::nullopt);
+ ASSERT_TRUE(InitializePipelineDescriptorForRendering(*pipeline_descriptor));
auto pipeline =
context->GetPipelineLibrary()->GetPipeline(pipeline_descriptor).Get();
ASSERT_TRUE(pipeline && pipeline->IsValid());
@@ -938,13 +942,15 @@
auto size = pass.GetRenderTargetSize();
- ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
- ImGui::SliderFloat("Speed", &speed, 0.0, 10.0);
- ImGui::SliderFloat("Planet Size", &planet_size, 0.1, 1000);
- ImGui::Checkbox("Show Normals", &show_normals);
- ImGui::Checkbox("Show Noise", &show_noise);
- ImGui::InputFloat("Seed Value", &seed_value);
- ImGui::End();
+ if (IsPlaygroundEnabled()) {
+ ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
+ ImGui::SliderFloat("Speed", &speed, 0.0, 10.0);
+ ImGui::SliderFloat("Planet Size", &planet_size, 0.1, 1000);
+ ImGui::Checkbox("Show Normals", &show_normals);
+ ImGui::Checkbox("Show Noise", &show_noise);
+ ImGui::InputFloat("Seed Value", &seed_value);
+ ImGui::End();
+ }
pass.SetPipeline(pipeline);
pass.SetCommandLabel("Planet scene");
@@ -988,8 +994,7 @@
auto pipeline_descriptor =
PipelineBuilder<VS, FS>::MakeDefaultPipelineDescriptor(*context);
ASSERT_TRUE(pipeline_descriptor.has_value());
- pipeline_descriptor->SetSampleCount(SampleCount::kCount4);
- pipeline_descriptor->SetStencilAttachmentDescriptors(std::nullopt);
+ ASSERT_TRUE(InitializePipelineDescriptorForRendering(*pipeline_descriptor));
auto pipeline =
context->GetPipelineLibrary()->GetPipeline(pipeline_descriptor).Get();
ASSERT_TRUE(pipeline && pipeline->IsValid());
@@ -1046,8 +1051,7 @@
auto pipeline_descriptor =
PipelineBuilder<VS, FS>::MakeDefaultPipelineDescriptor(*context);
ASSERT_TRUE(pipeline_descriptor.has_value());
- pipeline_descriptor->SetSampleCount(SampleCount::kCount4);
- pipeline_descriptor->SetStencilAttachmentDescriptors(std::nullopt);
+ ASSERT_TRUE(InitializePipelineDescriptorForRendering(*pipeline_descriptor));
auto pipeline =
context->GetPipelineLibrary()->GetPipeline(pipeline_descriptor).Get();
ASSERT_TRUE(pipeline && pipeline->IsValid());
@@ -1263,17 +1267,21 @@
return false;
}
pass->SetLabel("Stencil Buffer");
- ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
- ImGui::SliderInt("Stencil Write Value", &stencil_reference_write, 0,
- 0xFF);
- ImGui::SliderInt("Stencil Compare Value", &stencil_reference_read, 0,
- 0xFF);
- ImGui::Checkbox("Back face mode", &mirror);
- ImGui::ListBox("Front face compare function", ¤t_front_compare,
- CompareFunctionUI().labels(), CompareFunctionUI().size());
- ImGui::ListBox("Back face compare function", ¤t_back_compare,
- CompareFunctionUI().labels(), CompareFunctionUI().size());
- ImGui::End();
+ if (IsPlaygroundEnabled()) {
+ ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
+ ImGui::SliderInt("Stencil Write Value", &stencil_reference_write, 0,
+ 0xFF);
+ ImGui::SliderInt("Stencil Compare Value", &stencil_reference_read, 0,
+ 0xFF);
+ ImGui::Checkbox("Back face mode", &mirror);
+ ImGui::ListBox("Front face compare function", ¤t_front_compare,
+ CompareFunctionUI().labels(),
+ CompareFunctionUI().size());
+ ImGui::ListBox("Back face compare function", ¤t_back_compare,
+ CompareFunctionUI().labels(),
+ CompareFunctionUI().size());
+ ImGui::End();
+ }
StencilAttachmentDescriptor front;
front.stencil_compare =
@@ -1322,6 +1330,10 @@
data_host_buffer->Reset();
return true;
};
+
+ if ((true)) { // Disables trailing code without compiler warning.
+ GTEST_SKIP() << "See: https://github.com/flutter/flutter/issues/188884";
+ }
OpenPlaygroundHere(callback);
}
@@ -1361,6 +1373,7 @@
template <class VertexShader, class FragmentShader>
std::shared_ptr<Pipeline<PipelineDescriptor>> CreateDefaultPipeline(
+ RendererTest* test,
const std::shared_ptr<Context>& context) {
using TexturePipelineBuilder = PipelineBuilder<VertexShader, FragmentShader>;
auto pipeline_desc =
@@ -1368,8 +1381,9 @@
if (!pipeline_desc.has_value()) {
return nullptr;
}
- pipeline_desc->SetSampleCount(SampleCount::kCount4);
- pipeline_desc->SetStencilAttachmentDescriptors(std::nullopt);
+ if (!test->InitializePipelineDescriptorForRendering(*pipeline_desc)) {
+ return nullptr;
+ }
auto pipeline =
context->GetPipelineLibrary()->GetPipeline(pipeline_desc).Get();
if (!pipeline || !pipeline->IsValid()) {
@@ -1396,8 +1410,9 @@
}
// Create pipelines.
- auto texture_pipeline = CreateDefaultPipeline<TextureVS, TextureFS>(context);
- auto sepia_pipeline = CreateDefaultPipeline<SepiaVS, SepiaFS>(context);
+ auto texture_pipeline =
+ CreateDefaultPipeline<TextureVS, TextureFS>(this, context);
+ auto sepia_pipeline = CreateDefaultPipeline<SepiaVS, SepiaFS>(this, context);
ASSERT_TRUE(texture_pipeline);
ASSERT_TRUE(sepia_pipeline);
@@ -1468,6 +1483,16 @@
}
TEST_P(RendererTest, CanSepiaToneThenSwizzleWithSubpasses) {
+ switch (GetBackend()) {
+ case PlaygroundBackend::kMetal:
+ case PlaygroundBackend::kMetalSDF:
+ case PlaygroundBackend::kVulkan:
+ break;
+ case PlaygroundBackend::kOpenGLES:
+ case PlaygroundBackend::kOpenGLESSDF:
+ GTEST_SKIP() << "Platform is crashing in CI on this example "
+ << "(see https://github.com/flutter/flutter/issues/189287).";
+ }
// Define shader types
using TextureVS = TextureVertexShader;
using TextureFS = TextureFragmentShader;
@@ -1488,9 +1513,11 @@
}
// Create pipelines.
- auto texture_pipeline = CreateDefaultPipeline<TextureVS, TextureFS>(context);
- auto swizzle_pipeline = CreateDefaultPipeline<SwizzleVS, SwizzleFS>(context);
- auto sepia_pipeline = CreateDefaultPipeline<SepiaVS, SepiaFS>(context);
+ auto texture_pipeline =
+ CreateDefaultPipeline<TextureVS, TextureFS>(this, context);
+ auto swizzle_pipeline =
+ CreateDefaultPipeline<SwizzleVS, SwizzleFS>(this, context);
+ auto sepia_pipeline = CreateDefaultPipeline<SepiaVS, SepiaFS>(this, context);
ASSERT_TRUE(texture_pipeline);
ASSERT_TRUE(swizzle_pipeline);
diff --git a/engine/src/flutter/impeller/runtime_stage/BUILD.gn b/engine/src/flutter/impeller/runtime_stage/BUILD.gn
index ff059a5..a25519a 100644
--- a/engine/src/flutter/impeller/runtime_stage/BUILD.gn
+++ b/engine/src/flutter/impeller/runtime_stage/BUILD.gn
@@ -53,6 +53,6 @@
deps = [
":runtime_stage",
"../playground:playground_test",
- "//flutter/testing",
+ "//flutter/testing:testing_lib",
]
}
diff --git a/engine/src/flutter/impeller/shader_archive/BUILD.gn b/engine/src/flutter/impeller/shader_archive/BUILD.gn
index dc23d67..d6f7207 100644
--- a/engine/src/flutter/impeller/shader_archive/BUILD.gn
+++ b/engine/src/flutter/impeller/shader_archive/BUILD.gn
@@ -53,6 +53,6 @@
deps = [
":shader_archive",
"//flutter/fml",
- "//flutter/testing",
+ "//flutter/testing:testing_lib",
]
}
diff --git a/engine/src/flutter/impeller/tessellator/BUILD.gn b/engine/src/flutter/impeller/tessellator/BUILD.gn
index 92e73fc..599b352 100644
--- a/engine/src/flutter/impeller/tessellator/BUILD.gn
+++ b/engine/src/flutter/impeller/tessellator/BUILD.gn
@@ -74,6 +74,6 @@
":tessellator_libtess",
"../geometry:geometry_asserts",
"../playground:playground_test",
- "//flutter/testing",
+ "//flutter/testing:testing_lib",
]
}
diff --git a/engine/src/flutter/impeller/testing/BUILD.gn b/engine/src/flutter/impeller/testing/BUILD.gn
new file mode 100644
index 0000000..9ff12aa
--- /dev/null
+++ b/engine/src/flutter/impeller/testing/BUILD.gn
@@ -0,0 +1,66 @@
+# Copyright 2013 The Flutter Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//flutter/common/config.gni")
+import("//flutter/impeller/tools/impeller.gni")
+
+impeller_component("screenshot") {
+ testonly = true
+
+ sources = [
+ "screenshot.h",
+ "screenshotter.cc",
+ "screenshotter.h",
+ ]
+
+ deps = [ "//flutter/impeller/display_list" ]
+}
+
+impeller_component("golden_digest_manager") {
+ testonly = true
+
+ sources = [
+ "golden_digest_manager.cc",
+ "golden_digest_manager.h",
+ ]
+
+ deps = [ "//flutter/fml" ]
+}
+
+impeller_component("impeller_unittest_suite") {
+ testonly = true
+
+ sources = [
+ "impeller_unittest_suite.cc",
+ "impeller_unittest_suite.h",
+ ]
+
+ deps = [
+ "//flutter/fml",
+ "//flutter/impeller/playground:playground_test",
+ ]
+}
+
+if (is_mac) {
+ impeller_component("metal_screenshot") {
+ testonly = true
+
+ sources = [
+ "metal/metal_screenshot.h",
+ "metal/metal_screenshot.mm",
+ "metal/metal_screenshotter.h",
+ "metal/metal_screenshotter.mm",
+ "vulkan/vulkan_screenshotter.h",
+ "vulkan/vulkan_screenshotter.mm",
+ ]
+
+ deps = [
+ ":screenshot",
+ "//flutter/fml",
+ "//flutter/impeller/display_list",
+ "//flutter/impeller/renderer/backend/metal",
+ "//flutter/impeller/renderer/backend/vulkan",
+ ]
+ }
+}
diff --git a/engine/src/flutter/impeller/testing/golden_digest_manager.cc b/engine/src/flutter/impeller/testing/golden_digest_manager.cc
new file mode 100644
index 0000000..225b9f0
--- /dev/null
+++ b/engine/src/flutter/impeller/testing/golden_digest_manager.cc
@@ -0,0 +1,132 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "flutter/impeller/testing/golden_digest_manager.h"
+
+#include <fstream>
+#include <sstream>
+
+#include "flutter/fml/logging.h"
+#include "flutter/fml/paths.h"
+
+static const double kMaxDiffPixelsPercent = 0.01;
+static const int32_t kMaxColorDelta = 8;
+
+namespace impeller {
+namespace testing {
+
+GoldenDigestManager::GoldenDigestManager(
+ const std::string& working_directory_path)
+ : working_directory_(working_directory_path) {}
+
+GoldenDigestManager::~GoldenDigestManager() {
+ if (entries_written_ < entries_.size() ||
+ dimensions_written_ < dimensions_.size()) {
+ FML_LOG(WARNING) //
+ << "golden digest at " << working_directory_ //
+ << " incomplete, with only " //
+ << dimensions_written_ << " out of " //
+ << dimensions_.size() << " dimensions " //
+ << "and " //
+ << entries_written_ << " out of " //
+ << entries_.size() << " image entries" //
+ << " written";
+ }
+}
+
+const std::string& GoldenDigestManager::GetWorkingDirectory() const {
+ return working_directory_;
+}
+
+std::string GoldenDigestManager::GetFullPath(
+ const std::string& filename) const {
+ return fml::paths::JoinPaths({working_directory_, filename});
+}
+
+void GoldenDigestManager::AddDimension(const std::string& name,
+ const std::string& value) {
+ std::stringstream ss;
+ ss << "\"" << value << "\"";
+ dimensions_[name] = ss.str();
+}
+
+void GoldenDigestManager::AddImage(const std::string& test_name,
+ const std::string& filename,
+ int32_t width,
+ int32_t height) {
+ entries_.push_back({test_name, filename, width, height, kMaxDiffPixelsPercent,
+ kMaxColorDelta});
+}
+
+bool GoldenDigestManager::Write() {
+ std::ofstream fout;
+ fout.open(GetFullPath("digest.json"));
+ if (!fout.good()) {
+ return false;
+ }
+
+ // If we wrote some stuff before, this will be an update on top of that
+ // work, rewriting the file again from the beginning. This allows test
+ // suites to defensively write the digest after every group of tests.
+ dimensions_written_ = 0u;
+ entries_written_ = 0u;
+
+ fout << "{" << std::endl;
+ fout << " \"dimensions\": {" << std::endl;
+ bool is_first = true;
+ for (const auto& dimension : dimensions_) {
+ if (!is_first) {
+ fout << "," << std::endl;
+ }
+ is_first = false;
+ fout << " \"" << dimension.first << "\": " << dimension.second;
+ dimensions_written_++;
+ }
+ fout << std::endl << " }," << std::endl;
+ fout << " \"entries\":" << std::endl;
+
+ fout << " [" << std::endl;
+ is_first = true;
+ for (const auto& entry : entries_) {
+ if (!is_first) {
+ fout << "," << std::endl;
+ }
+ is_first = false;
+
+ fout << " { " << "\"testName\" : \"" << entry.test_name << "\", "
+ << "\"filename\" : \"" << entry.filename << "\", "
+ << "\"width\" : " << entry.width << ", "
+ << "\"height\" : " << entry.height << ", ";
+
+ if (entry.max_diff_pixels_percent ==
+ static_cast<int64_t>(entry.max_diff_pixels_percent)) {
+ fout << "\"maxDiffPixelsPercent\" : " << entry.max_diff_pixels_percent
+ << ".0, ";
+ } else {
+ fout << "\"maxDiffPixelsPercent\" : " << entry.max_diff_pixels_percent
+ << ", ";
+ }
+
+ fout << "\"maxColorDelta\":" << entry.max_color_delta << " ";
+ fout << "}";
+ entries_written_++;
+ }
+ fout << std::endl << " ]" << std::endl;
+
+ fout << "}" << std::endl;
+
+ fout.close();
+ return true;
+}
+
+void GoldenDigestManager::ClearDigestData() {
+ dimensions_.clear();
+ entries_.clear();
+
+ dimensions_written_ = 0u;
+ entries_written_ = 0u;
+}
+
+} // namespace testing
+} // namespace impeller
diff --git a/engine/src/flutter/impeller/testing/golden_digest_manager.h b/engine/src/flutter/impeller/testing/golden_digest_manager.h
new file mode 100644
index 0000000..977a99b
--- /dev/null
+++ b/engine/src/flutter/impeller/testing/golden_digest_manager.h
@@ -0,0 +1,79 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef FLUTTER_IMPELLER_TESTING_GOLDEN_DIGEST_MANAGER_H_
+#define FLUTTER_IMPELLER_TESTING_GOLDEN_DIGEST_MANAGER_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+namespace impeller {
+namespace testing {
+
+/// Manages a global variable for tracking a directory containing
+/// instances of golden images.
+class GoldenDigestManager {
+ public:
+ /// Constructs a GoldenDigest that will accumulate golden image filenames
+ /// and Dimension key/value pairs for the specified working directory and
+ /// write them to a file named "digest.json" in that directory when
+ /// |Write| is called.
+ explicit GoldenDigestManager(const std::string& working_directory);
+
+ ~GoldenDigestManager();
+
+ const std::string& GetWorkingDirectory() const;
+
+ std::string GetFullPath(const std::string& filename) const;
+
+ void AddDimension(const std::string& name, const std::string& value);
+
+ void AddImage(const std::string& test_name,
+ const std::string& golden_filename,
+ int32_t width,
+ int32_t height);
+
+ /// Writes a "digest.json" file to the working directory.
+ ///
+ /// (Failing to call either this method or the |ClearDigestData| method
+ /// causes a warning to be written to the console about unwritten data
+ /// left in the digest manater.)
+ ///
+ /// Returns `true` on success.
+ bool Write();
+
+ /// Clears all accumulated dimension and golden image data if the digest
+ /// should not be written.
+ ///
+ /// (Failing to call either this method or the |Write| method causes
+ /// a warning to be written to the console about unwritten data left
+ /// in the digest manater.)
+ void ClearDigestData();
+
+ private:
+ GoldenDigestManager(const GoldenDigestManager&) = delete;
+
+ GoldenDigestManager& operator=(const GoldenDigestManager&) = delete;
+
+ struct Entry {
+ std::string test_name;
+ std::string filename;
+ int32_t width;
+ int32_t height;
+ double max_diff_pixels_percent;
+ int32_t max_color_delta;
+ };
+
+ std::string working_directory_;
+ std::vector<Entry> entries_;
+ std::map<std::string, std::string> dimensions_;
+ size_t entries_written_ = 0u;
+ size_t dimensions_written_ = 0u;
+};
+
+} // namespace testing
+} // namespace impeller
+
+#endif // FLUTTER_IMPELLER_TESTING_GOLDEN_DIGEST_MANAGER_H_
diff --git a/engine/src/flutter/impeller/testing/impeller_unittest_suite.cc b/engine/src/flutter/impeller/testing/impeller_unittest_suite.cc
new file mode 100644
index 0000000..9aefcbe
--- /dev/null
+++ b/engine/src/flutter/impeller/testing/impeller_unittest_suite.cc
@@ -0,0 +1,18 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "flutter/impeller/testing/impeller_unittest_suite.h"
+
+#include "flutter/fml/logging.h"
+#include "flutter/impeller/playground/playground_test.h"
+
+namespace impeller {
+namespace testing {
+
+void ImpellerUnittestSetup() {
+ ::impeller::PlaygroundTest::SetupTestEnvironment();
+}
+
+} // namespace testing
+} // namespace impeller
diff --git a/engine/src/flutter/impeller/testing/impeller_unittest_suite.h b/engine/src/flutter/impeller/testing/impeller_unittest_suite.h
new file mode 100644
index 0000000..f60a5d4
--- /dev/null
+++ b/engine/src/flutter/impeller/testing/impeller_unittest_suite.h
@@ -0,0 +1,16 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef FLUTTER_IMPELLER_TESTING_IMPELLER_UNITTEST_SUITE_H_
+#define FLUTTER_IMPELLER_TESTING_IMPELLER_UNITTEST_SUITE_H_
+
+namespace impeller {
+namespace testing {
+
+void ImpellerUnittestSetup();
+
+} // namespace testing
+} // namespace impeller
+
+#endif // FLUTTER_IMPELLER_TESTING_IMPELLER_UNITTEST_SUITE_H_
diff --git a/engine/src/flutter/impeller/golden_tests/metal_screenshot.h b/engine/src/flutter/impeller/testing/metal/metal_screenshot.h
similarity index 85%
rename from engine/src/flutter/impeller/golden_tests/metal_screenshot.h
rename to engine/src/flutter/impeller/testing/metal/metal_screenshot.h
index 739c769..cd460db 100644
--- a/engine/src/flutter/impeller/golden_tests/metal_screenshot.h
+++ b/engine/src/flutter/impeller/testing/metal/metal_screenshot.h
@@ -2,10 +2,10 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef FLUTTER_IMPELLER_GOLDEN_TESTS_METAL_SCREENSHOT_H_
-#define FLUTTER_IMPELLER_GOLDEN_TESTS_METAL_SCREENSHOT_H_
+#ifndef FLUTTER_IMPELLER_TESTING_METAL_METAL_SCREENSHOT_H_
+#define FLUTTER_IMPELLER_TESTING_METAL_METAL_SCREENSHOT_H_
-#include "flutter/impeller/golden_tests/screenshot.h"
+#include "flutter/impeller/testing/screenshot.h"
#include <CoreFoundation/CoreFoundation.h>
#include <CoreImage/CoreImage.h>
@@ -55,4 +55,4 @@
} // namespace testing
} // namespace impeller
-#endif // FLUTTER_IMPELLER_GOLDEN_TESTS_METAL_SCREENSHOT_H_
+#endif // FLUTTER_IMPELLER_TESTING_METAL_METAL_SCREENSHOT_H_
diff --git a/engine/src/flutter/impeller/golden_tests/metal_screenshot.mm b/engine/src/flutter/impeller/testing/metal/metal_screenshot.mm
similarity index 96%
rename from engine/src/flutter/impeller/golden_tests/metal_screenshot.mm
rename to engine/src/flutter/impeller/testing/metal/metal_screenshot.mm
index 7062372..00fbc3d 100644
--- a/engine/src/flutter/impeller/golden_tests/metal_screenshot.mm
+++ b/engine/src/flutter/impeller/testing/metal/metal_screenshot.mm
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "impeller/golden_tests/metal_screenshot.h"
+#include "impeller/testing/metal/metal_screenshot.h"
namespace impeller {
namespace testing {
diff --git a/engine/src/flutter/impeller/testing/metal/metal_screenshotter.h b/engine/src/flutter/impeller/testing/metal/metal_screenshotter.h
new file mode 100644
index 0000000..5bac590
--- /dev/null
+++ b/engine/src/flutter/impeller/testing/metal/metal_screenshotter.h
@@ -0,0 +1,32 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef FLUTTER_IMPELLER_TESTING_METAL_METAL_SCREENSHOTTER_H_
+#define FLUTTER_IMPELLER_TESTING_METAL_METAL_SCREENSHOTTER_H_
+
+#include "flutter/impeller/testing/screenshotter.h"
+#include "impeller/display_list/aiks_context.h"
+
+namespace impeller {
+namespace testing {
+
+/// Converts `Picture`s and `DisplayList`s to `MetalScreenshot`s with the
+/// playground backend.
+class MetalScreenshotter : public Screenshotter {
+ public:
+ explicit MetalScreenshotter();
+
+ static std::unique_ptr<Screenshot> MakeScreenshot(
+ const std::shared_ptr<Context>& context,
+ const std::shared_ptr<Texture>& texture);
+
+ std::unique_ptr<Screenshot> MakeScreenshot(
+ const AiksContext& aiks_context,
+ const std::shared_ptr<Texture>& texture) override;
+};
+
+} // namespace testing
+} // namespace impeller
+
+#endif // FLUTTER_IMPELLER_TESTING_METAL_METAL_SCREENSHOTTER_H_
diff --git a/engine/src/flutter/impeller/golden_tests/metal_screenshotter.mm b/engine/src/flutter/impeller/testing/metal/metal_screenshotter.mm
similarity index 75%
rename from engine/src/flutter/impeller/golden_tests/metal_screenshotter.mm
rename to engine/src/flutter/impeller/testing/metal/metal_screenshotter.mm
index b0a819d..baa3dfa 100644
--- a/engine/src/flutter/impeller/golden_tests/metal_screenshotter.mm
+++ b/engine/src/flutter/impeller/testing/metal/metal_screenshotter.mm
@@ -2,28 +2,36 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "flutter/impeller/golden_tests/metal_screenshotter.h"
+#include "flutter/impeller/testing/metal/metal_screenshotter.h"
#include <CoreImage/CoreImage.h>
#include "impeller/renderer/backend/metal/context_mtl.h"
#include "impeller/renderer/backend/metal/texture_mtl.h"
#define GLFW_INCLUDE_NONE
#include "flutter/fml/synchronization/waitable_event.h"
-#include "third_party/glfw/include/GLFW/glfw3.h"
+#include "flutter/impeller/testing/metal/metal_screenshot.h"
namespace impeller {
namespace testing {
-MetalScreenshotter::MetalScreenshotter(const PlaygroundSwitches& switches) {
- FML_CHECK(::glfwInit() == GLFW_TRUE);
- playground_ = PlaygroundImpl::Create(PlaygroundBackend::kMetal, switches);
-}
+MetalScreenshotter::MetalScreenshotter() {}
std::unique_ptr<Screenshot> MetalScreenshotter::MakeScreenshot(
const AiksContext& aiks_context,
- const std::shared_ptr<Texture> texture) {
+ const std::shared_ptr<Texture>& texture) {
+ return MakeScreenshot(aiks_context.GetContext(), texture);
+}
+
+std::unique_ptr<Screenshot> Screenshotter::MakeMetalScreenshot(
+ std::shared_ptr<Context>& context,
+ const std::shared_ptr<Texture>& texture) {
+ return MetalScreenshotter::MakeScreenshot(context, texture);
+}
+
+std::unique_ptr<Screenshot> MetalScreenshotter::MakeScreenshot(
+ const std::shared_ptr<Context>& context,
+ const std::shared_ptr<Texture>& texture) {
@autoreleasepool {
- std::shared_ptr<Context> context = aiks_context.GetContext();
fml::AutoResetWaitableEvent latch;
if (auto cmd_buffer = context->CreateCommandBuffer()) {
if (context->GetCommandQueue()
diff --git a/engine/src/flutter/impeller/golden_tests/screenshot.h b/engine/src/flutter/impeller/testing/screenshot.h
similarity index 85%
rename from engine/src/flutter/impeller/golden_tests/screenshot.h
rename to engine/src/flutter/impeller/testing/screenshot.h
index 4772609..91467b8 100644
--- a/engine/src/flutter/impeller/golden_tests/screenshot.h
+++ b/engine/src/flutter/impeller/testing/screenshot.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef FLUTTER_IMPELLER_GOLDEN_TESTS_SCREENSHOT_H_
-#define FLUTTER_IMPELLER_GOLDEN_TESTS_SCREENSHOT_H_
+#ifndef FLUTTER_IMPELLER_TESTING_SCREENSHOT_H_
+#define FLUTTER_IMPELLER_TESTING_SCREENSHOT_H_
#include <cstddef>
#include <cstdint>
@@ -37,4 +37,4 @@
} // namespace testing
} // namespace impeller
-#endif // FLUTTER_IMPELLER_GOLDEN_TESTS_SCREENSHOT_H_
+#endif // FLUTTER_IMPELLER_TESTING_SCREENSHOT_H_
diff --git a/engine/src/flutter/impeller/testing/screenshotter.cc b/engine/src/flutter/impeller/testing/screenshotter.cc
new file mode 100644
index 0000000..52ec17f
--- /dev/null
+++ b/engine/src/flutter/impeller/testing/screenshotter.cc
@@ -0,0 +1,52 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "flutter/impeller/testing/screenshotter.h"
+
+namespace impeller {
+namespace testing {
+
+#ifndef FML_OS_MACOSX
+std::unique_ptr<Screenshot> Screenshotter::MakeMetalScreenshot(
+ std::shared_ptr<Context>& context,
+ const std::shared_ptr<Texture>& texture) {
+ FML_LOG(INFO) << "Screenshot not supported for Metal on this platform";
+ return nullptr;
+}
+
+std::unique_ptr<Screenshot> Screenshotter::MakeOpenGLScreenshot(
+ std::shared_ptr<Context>& context,
+ const std::shared_ptr<Texture>& texture) {
+ FML_LOG(INFO) << "Screenshot not supported for OpenGL on this platform";
+ return nullptr;
+}
+
+std::unique_ptr<Screenshot> Screenshotter::MakeVulkanScreenshot(
+ std::shared_ptr<Context>& context,
+ const std::shared_ptr<Texture>& texture) {
+ FML_LOG(INFO) << "Screenshot not supported for Vulkan on this platform";
+ return nullptr;
+}
+#endif // FML_OS_MACOSX
+
+std::unique_ptr<Screenshot> Screenshotter::MakeScreenshot(
+ std::shared_ptr<Context>& context,
+ const std::shared_ptr<Texture>& texture) {
+ if (!context || !texture) {
+ return nullptr;
+ }
+ switch (context->GetBackendType()) {
+ case Context::BackendType::kMetal:
+ return MakeMetalScreenshot(context, texture);
+
+ case Context::BackendType::kOpenGLES:
+ return MakeOpenGLScreenshot(context, texture);
+
+ case Context::BackendType::kVulkan:
+ return MakeVulkanScreenshot(context, texture);
+ }
+}
+
+} // namespace testing
+} // namespace impeller
diff --git a/engine/src/flutter/impeller/testing/screenshotter.h b/engine/src/flutter/impeller/testing/screenshotter.h
new file mode 100644
index 0000000..4059849
--- /dev/null
+++ b/engine/src/flutter/impeller/testing/screenshotter.h
@@ -0,0 +1,43 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef FLUTTER_IMPELLER_TESTING_SCREENSHOTTER_H_
+#define FLUTTER_IMPELLER_TESTING_SCREENSHOTTER_H_
+
+#include "flutter/impeller/testing/screenshot.h"
+#include "impeller/display_list/aiks_context.h"
+
+namespace impeller {
+namespace testing {
+
+/// Converts `Picture`s and `DisplayList`s to `MetalScreenshot`s with the
+/// playground backend.
+class Screenshotter {
+ public:
+ virtual ~Screenshotter() = default;
+
+ static std::unique_ptr<Screenshot> MakeScreenshot(
+ std::shared_ptr<Context>& context,
+ const std::shared_ptr<Texture>& texture);
+
+ virtual std::unique_ptr<Screenshot> MakeScreenshot(
+ const AiksContext& aiks_context,
+ const std::shared_ptr<Texture>& texture) = 0;
+
+ private:
+ static std::unique_ptr<Screenshot> MakeMetalScreenshot(
+ std::shared_ptr<Context>& context,
+ const std::shared_ptr<Texture>& texture);
+ static std::unique_ptr<Screenshot> MakeOpenGLScreenshot(
+ std::shared_ptr<Context>& context,
+ const std::shared_ptr<Texture>& texture);
+ static std::unique_ptr<Screenshot> MakeVulkanScreenshot(
+ std::shared_ptr<Context>& context,
+ const std::shared_ptr<Texture>& texture);
+};
+
+} // namespace testing
+} // namespace impeller
+
+#endif // FLUTTER_IMPELLER_TESTING_SCREENSHOTTER_H_
diff --git a/engine/src/flutter/impeller/testing/vulkan/vulkan_screenshotter.h b/engine/src/flutter/impeller/testing/vulkan/vulkan_screenshotter.h
new file mode 100644
index 0000000..73d634f
--- /dev/null
+++ b/engine/src/flutter/impeller/testing/vulkan/vulkan_screenshotter.h
@@ -0,0 +1,31 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef FLUTTER_IMPELLER_TESTING_VULKAN_VULKAN_SCREENSHOTTER_H_
+#define FLUTTER_IMPELLER_TESTING_VULKAN_VULKAN_SCREENSHOTTER_H_
+
+#include "flutter/impeller/testing/screenshotter.h"
+
+namespace impeller {
+namespace testing {
+
+/// Converts `Picture`s and `DisplayList`s to `MetalScreenshot`s with the
+/// playground backend.
+class VulkanScreenshotter : public Screenshotter {
+ public:
+ explicit VulkanScreenshotter();
+
+ static std::unique_ptr<Screenshot> MakeScreenshot(
+ const std::shared_ptr<Context>& context,
+ const std::shared_ptr<Texture>& texture);
+
+ std::unique_ptr<Screenshot> MakeScreenshot(
+ const AiksContext& aiks_context,
+ const std::shared_ptr<Texture>& texture) override;
+};
+
+} // namespace testing
+} // namespace impeller
+
+#endif // FLUTTER_IMPELLER_TESTING_VULKAN_VULKAN_SCREENSHOTTER_H_
diff --git a/engine/src/flutter/impeller/golden_tests/vulkan_screenshotter.mm b/engine/src/flutter/impeller/testing/vulkan/vulkan_screenshotter.mm
similarity index 79%
rename from engine/src/flutter/impeller/golden_tests/vulkan_screenshotter.mm
rename to engine/src/flutter/impeller/testing/vulkan/vulkan_screenshotter.mm
index 49c7d30..4b5eb75 100644
--- a/engine/src/flutter/impeller/golden_tests/vulkan_screenshotter.mm
+++ b/engine/src/flutter/impeller/testing/vulkan/vulkan_screenshotter.mm
@@ -2,12 +2,11 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "flutter/impeller/golden_tests/vulkan_screenshotter.h"
+#include "flutter/impeller/testing/vulkan/vulkan_screenshotter.h"
#include "flutter/fml/synchronization/waitable_event.h"
-#include "flutter/impeller/golden_tests/metal_screenshot.h"
+#include "flutter/impeller/testing/metal/metal_screenshot.h"
#define GLFW_INCLUDE_NONE
-#include "third_party/glfw/include/GLFW/glfw3.h"
namespace impeller {
namespace testing {
@@ -81,15 +80,29 @@
}
} // namespace
-VulkanScreenshotter::VulkanScreenshotter(
- const std::unique_ptr<PlaygroundImpl>& playground)
- : playground_(playground) {
- FML_CHECK(playground_);
+VulkanScreenshotter::VulkanScreenshotter() {}
+
+std::unique_ptr<Screenshot> VulkanScreenshotter::MakeScreenshot(
+ const std::shared_ptr<Context>& context,
+ const std::shared_ptr<Texture>& texture) {
+ return ReadTexture(context, texture);
+}
+
+std::unique_ptr<Screenshot> Screenshotter::MakeOpenGLScreenshot(
+ std::shared_ptr<Context>& context,
+ const std::shared_ptr<Texture>& texture) {
+ return ReadTexture(context, texture);
+}
+
+std::unique_ptr<Screenshot> Screenshotter::MakeVulkanScreenshot(
+ std::shared_ptr<Context>& context,
+ const std::shared_ptr<Texture>& texture) {
+ return ReadTexture(context, texture);
}
std::unique_ptr<Screenshot> VulkanScreenshotter::MakeScreenshot(
const AiksContext& aiks_context,
- const std::shared_ptr<Texture> texture) {
+ const std::shared_ptr<Texture>& texture) {
return ReadTexture(aiks_context.GetContext(), texture);
}
diff --git a/engine/src/flutter/impeller/toolkit/interop/impeller_unittests.cc b/engine/src/flutter/impeller/toolkit/interop/impeller_unittests.cc
index 8c82c20..2c16960 100644
--- a/engine/src/flutter/impeller/toolkit/interop/impeller_unittests.cc
+++ b/engine/src/flutter/impeller/toolkit/interop/impeller_unittests.cc
@@ -659,6 +659,11 @@
}
TEST_P(InteropPlaygroundTest, CanCreateFragmentProgramColorFilters) {
+ if (GetBackend() == PlaygroundBackend::kOpenGLES ||
+ GetBackend() == PlaygroundBackend::kOpenGLESSDF) {
+ GTEST_SKIP() << "See: https://github.com/flutter/flutter/issues/188882";
+ }
+
auto iplr = OpenAssetAsHPPMapping("interop_runtime_stage_cs.frag.iplr");
ASSERT_TRUE(!!iplr);
auto program = hpp::FragmentProgram::WithData(std::move(iplr));
diff --git a/engine/src/flutter/testing/run_all_unittests.cc b/engine/src/flutter/testing/run_all_unittests.cc
index 48f4c0d..53d5470 100644
--- a/engine/src/flutter/testing/run_all_unittests.cc
+++ b/engine/src/flutter/testing/run_all_unittests.cc
@@ -14,6 +14,10 @@
#include "flutter/testing/test_timeout_listener.h"
#include "gtest/gtest.h"
+#ifdef IMPELLER_UNITTEST_SUITE
+#include "flutter/impeller/testing/impeller_unittest_suite.h"
+#endif // IMPELLER_UNITTEST_SUITE
+
#ifdef FML_OS_IOS
#include <asl.h>
#endif // FML_OS_IOS
@@ -65,6 +69,10 @@
return RUN_ALL_TESTS();
}
+#ifdef IMPELLER_UNITTEST_SUITE
+ impeller::testing::ImpellerUnittestSetup();
+#endif // IMPELLER_UNITTEST_SUITE
+
auto timeout_listener =
new flutter::testing::TestTimeoutListener(timeout.value());
auto& listeners = ::testing::UnitTest::GetInstance()->listeners();