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

Commit ba3218b

Browse files
authored
[Impeller] implemented golden image tests for opengles (#50146)
fixes flutter/flutter#142354 This sets up impeller_golden_tests to run with ANGLE. [C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style
1 parent 5b89189 commit ba3218b

File tree

10 files changed

+149
-76
lines changed

10 files changed

+149
-76
lines changed

ci/builders/mac_host_engine.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@
159159
"--prebuilt-dart-sdk",
160160
"--build-embedder-examples",
161161
"--enable-impeller-vulkan",
162+
"--enable-impeller-opengles",
162163
"--use-glfw-swiftshader"
163164
],
164165
"name": "host_release",

impeller/golden_tests/BUILD.gn

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,8 @@ if (is_mac) {
8484
"//flutter/impeller/aiks:aiks_unittests_golden",
8585
"//flutter/impeller/fixtures",
8686
"//flutter/third_party/swiftshader",
87+
"//third_party/angle:libEGL",
88+
"//third_party/angle:libGLESv2",
8789
"//third_party/googletest:gtest",
8890
]
8991
}

impeller/golden_tests/golden_playground_test_mac.cc

Lines changed: 45 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -55,47 +55,33 @@ const std::unique_ptr<PlaygroundImpl>& GetSharedVulkanPlayground(
5555
}
5656
} // namespace
5757

58+
#define IMP_AIKSTEST(name) \
59+
"impeller_Play_AiksTest_" #name "_Metal", \
60+
"impeller_Play_AiksTest_" #name "_OpenGLES", \
61+
"impeller_Play_AiksTest_" #name "_Vulkan"
62+
5863
// If you add a new playground test to the aiks unittests and you do not want it
5964
// to also be a golden test, then add the test name here.
6065
static const std::vector<std::string> kSkipTests = {
61-
"impeller_Play_AiksTest_CanDrawPaintMultipleTimesInteractive_Metal",
62-
"impeller_Play_AiksTest_CanDrawPaintMultipleTimesInteractive_Vulkan",
63-
"impeller_Play_AiksTest_CanRenderLinearGradientManyColorsUnevenStops_Metal",
64-
"impeller_Play_AiksTest_CanRenderLinearGradientManyColorsUnevenStops_"
65-
"Vulkan",
66-
"impeller_Play_AiksTest_CanRenderRadialGradient_Metal",
67-
"impeller_Play_AiksTest_CanRenderRadialGradient_Vulkan",
68-
"impeller_Play_AiksTest_CanRenderRadialGradientManyColors_Metal",
69-
"impeller_Play_AiksTest_CanRenderRadialGradientManyColors_Vulkan",
70-
"impeller_Play_AiksTest_CanRenderBackdropBlurInteractive_Metal",
71-
"impeller_Play_AiksTest_CanRenderBackdropBlurInteractive_Vulkan",
72-
"impeller_Play_AiksTest_ClippedBlurFilterRendersCorrectlyInteractive_Metal",
73-
"impeller_Play_AiksTest_ClippedBlurFilterRendersCorrectlyInteractive_"
74-
"Vulkan",
75-
"impeller_Play_AiksTest_CoverageOriginShouldBeAccountedForInSubpasses_"
76-
"Metal",
77-
"impeller_Play_AiksTest_CoverageOriginShouldBeAccountedForInSubpasses_"
78-
"Vulkan",
79-
"impeller_Play_AiksTest_GaussianBlurRotatedAndClippedInteractive_Metal",
80-
"impeller_Play_AiksTest_GaussianBlurRotatedAndClippedInteractive_Vulkan",
81-
"impeller_Play_AiksTest_GradientStrokesRenderCorrectly_Metal",
82-
"impeller_Play_AiksTest_GradientStrokesRenderCorrectly_Vulkan",
83-
"impeller_Play_AiksTest_ColorWheel_Metal",
84-
"impeller_Play_AiksTest_ColorWheel_Vulkan",
85-
"impeller_Play_AiksTest_SceneColorSource_Metal",
86-
"impeller_Play_AiksTest_SceneColorSource_Vulkan",
87-
"impeller_Play_AiksTest_SolidStrokesRenderCorrectly_Metal",
88-
"impeller_Play_AiksTest_SolidStrokesRenderCorrectly_Vulkan",
89-
"impeller_Play_AiksTest_TextFrameSubpixelAlignment_Metal",
90-
"impeller_Play_AiksTest_TextFrameSubpixelAlignment_Vulkan",
66+
IMP_AIKSTEST(CanDrawPaintMultipleTimesInteractive),
67+
IMP_AIKSTEST(CanRenderLinearGradientManyColorsUnevenStops),
68+
IMP_AIKSTEST(CanRenderRadialGradient),
69+
IMP_AIKSTEST(CanRenderRadialGradientManyColors),
70+
IMP_AIKSTEST(CanRenderBackdropBlurInteractive),
71+
IMP_AIKSTEST(ClippedBlurFilterRendersCorrectlyInteractive),
72+
IMP_AIKSTEST(CoverageOriginShouldBeAccountedForInSubpasses),
73+
IMP_AIKSTEST(GaussianBlurRotatedAndClippedInteractive),
74+
IMP_AIKSTEST(GradientStrokesRenderCorrectly),
75+
IMP_AIKSTEST(ColorWheel),
76+
IMP_AIKSTEST(SceneColorSource),
77+
IMP_AIKSTEST(SolidStrokesRenderCorrectly),
78+
IMP_AIKSTEST(TextFrameSubpixelAlignment),
9179
// TextRotated is flakey and we can't seem to get it to stabilize on Skia
9280
// Gold.
93-
"impeller_Play_AiksTest_TextRotated_Metal",
94-
"impeller_Play_AiksTest_TextRotated_Vulkan",
81+
IMP_AIKSTEST(TextRotated),
9582
// Runtime stage based tests get confused with a Metal context.
9683
"impeller_Play_AiksTest_CanRenderClippedRuntimeEffects_Vulkan",
97-
"impeller_Play_AiksTest_CaptureContext_Metal",
98-
"impeller_Play_AiksTest_CaptureContext_Vulkan",
84+
IMP_AIKSTEST(CaptureContext),
9985
};
10086

10187
static const std::vector<std::string> kVulkanDenyValidationTests = {
@@ -143,6 +129,7 @@ bool ShouldTestHaveVulkanValidations() {
143129

144130
struct GoldenPlaygroundTest::GoldenPlaygroundTestImpl {
145131
std::unique_ptr<PlaygroundImpl> test_vulkan_playground;
132+
std::unique_ptr<PlaygroundImpl> test_opengl_playground;
146133
std::unique_ptr<testing::Screenshotter> screenshotter;
147134
ISize window_size = ISize{1024, 768};
148135
};
@@ -172,13 +159,29 @@ void GoldenPlaygroundTest::SetUp() {
172159
std::filesystem::path icd_path = target_path / "vk_swiftshader_icd.json";
173160
setenv("VK_ICD_FILENAMES", icd_path.c_str(), 1);
174161

175-
if (GetBackend() != PlaygroundBackend::kMetal &&
176-
GetBackend() != PlaygroundBackend::kVulkan) {
177-
GTEST_SKIP_("GoldenPlaygroundTest doesn't support this backend type.");
178-
return;
179-
}
180-
181162
bool enable_vulkan_validations = ShouldTestHaveVulkanValidations();
163+
switch (GetParam()) {
164+
case PlaygroundBackend::kMetal:
165+
pimpl_->screenshotter = std::make_unique<testing::MetalScreenshotter>();
166+
break;
167+
case PlaygroundBackend::kVulkan: {
168+
const std::unique_ptr<PlaygroundImpl>& playground =
169+
GetSharedVulkanPlayground(enable_vulkan_validations);
170+
pimpl_->screenshotter =
171+
std::make_unique<testing::VulkanScreenshotter>(playground);
172+
break;
173+
}
174+
case PlaygroundBackend::kOpenGLES: {
175+
FML_CHECK(::glfwInit() == GLFW_TRUE);
176+
PlaygroundSwitches playground_switches;
177+
playground_switches.use_angle = true;
178+
pimpl_->test_opengl_playground = PlaygroundImpl::Create(
179+
PlaygroundBackend::kOpenGLES, playground_switches);
180+
pimpl_->screenshotter = std::make_unique<testing::VulkanScreenshotter>(
181+
pimpl_->test_opengl_playground);
182+
break;
183+
}
184+
}
182185
if (GetParam() == PlaygroundBackend::kMetal) {
183186
pimpl_->screenshotter = std::make_unique<testing::MetalScreenshotter>();
184187
} else if (GetParam() == PlaygroundBackend::kVulkan) {
@@ -272,8 +275,8 @@ std::shared_ptr<Context> GoldenPlaygroundTest::MakeContext() const {
272275
pimpl_->test_vulkan_playground);
273276
return pimpl_->test_vulkan_playground->GetContext();
274277
} else {
275-
FML_CHECK(false);
276-
return nullptr;
278+
/// On OpenGL we create a context for each test.
279+
return GetContext();
277280
}
278281
}
279282

impeller/golden_tests/vulkan_screenshotter.mm

Lines changed: 46 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,22 @@
66

77
#include "flutter/fml/synchronization/waitable_event.h"
88
#include "flutter/impeller/golden_tests/metal_screenshot.h"
9-
#include "impeller/renderer/backend/vulkan/surface_context_vk.h"
10-
#include "impeller/renderer/backend/vulkan/texture_vk.h"
119
#define GLFW_INCLUDE_NONE
1210
#include "third_party/glfw/include/GLFW/glfw3.h"
1311

1412
namespace impeller {
1513
namespace testing {
1614

1715
namespace {
16+
17+
using CGContextPtr = std::unique_ptr<std::remove_pointer<CGContextRef>::type,
18+
decltype(&CGContextRelease)>;
19+
using CGImagePtr = std::unique_ptr<std::remove_pointer<CGImageRef>::type,
20+
decltype(&CGImageRelease)>;
21+
using CGColorSpacePtr =
22+
std::unique_ptr<std::remove_pointer<CGColorSpaceRef>::type,
23+
decltype(&CGColorSpaceRelease)>;
24+
1825
std::unique_ptr<Screenshot> ReadTexture(
1926
const std::shared_ptr<Context>& surface_context,
2027
const std::shared_ptr<Texture>& texture) {
@@ -49,21 +56,45 @@
4956
// TODO(gaaclarke): Replace CoreImage requirement with something
5057
// crossplatform.
5158

52-
CGColorSpaceRef color_space = CGColorSpaceCreateDeviceRGB();
59+
CGColorSpacePtr color_space(CGColorSpaceCreateDeviceRGB(),
60+
&CGColorSpaceRelease);
5361
CGBitmapInfo bitmap_info =
54-
kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little;
55-
CGContextRef context = CGBitmapContextCreate(
56-
device_buffer->OnGetContents(), texture->GetSize().width,
57-
texture->GetSize().height,
58-
/*bitsPerComponent=*/8,
59-
/*bytesPerRow=*/texture->GetTextureDescriptor().GetBytesPerRow(),
60-
color_space, bitmap_info);
62+
texture->GetTextureDescriptor().format == PixelFormat::kB8G8R8A8UNormInt
63+
? kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little
64+
: kCGImageAlphaPremultipliedLast;
65+
CGContextPtr context(
66+
CGBitmapContextCreate(
67+
device_buffer->OnGetContents(), texture->GetSize().width,
68+
texture->GetSize().height,
69+
/*bitsPerComponent=*/8,
70+
/*bytesPerRow=*/texture->GetTextureDescriptor().GetBytesPerRow(),
71+
color_space.get(), bitmap_info),
72+
&CGContextRelease);
6173
FML_CHECK(context);
62-
CGImageRef image_ref = CGBitmapContextCreateImage(context);
63-
FML_CHECK(image_ref);
64-
CGContextRelease(context);
65-
CGColorSpaceRelease(color_space);
66-
return std::make_unique<MetalScreenshot>(image_ref);
74+
CGImagePtr image(CGBitmapContextCreateImage(context.get()), &CGImageRelease);
75+
FML_CHECK(image);
76+
77+
// TODO(tbd): Perform the flip at the blit stage to avoid this slow
78+
// copy.
79+
if (texture->GetYCoordScale() == -1) {
80+
CGContextPtr flipped_context(
81+
CGBitmapContextCreate(
82+
nullptr, texture->GetSize().width, texture->GetSize().height,
83+
/*bitsPerComponent=*/8,
84+
/*bytesPerRow=*/0, color_space.get(), bitmap_info),
85+
&CGContextRelease);
86+
CGContextTranslateCTM(flipped_context.get(), 0, texture->GetSize().height);
87+
CGContextScaleCTM(flipped_context.get(), 1.0, -1.0);
88+
CGContextDrawImage(
89+
flipped_context.get(),
90+
CGRectMake(0, 0, texture->GetSize().width, texture->GetSize().height),
91+
image.get());
92+
CGImagePtr flipped_image(CGBitmapContextCreateImage(flipped_context.get()),
93+
&CGImageRelease);
94+
image.swap(flipped_image);
95+
}
96+
97+
return std::make_unique<MetalScreenshot>(image.release());
6798
}
6899
} // namespace
69100

@@ -84,8 +115,6 @@
84115
aiks_context,
85116
ISize(size.width * content_scale.x, size.height * content_scale.y));
86117
std::shared_ptr<Texture> texture = image->GetTexture();
87-
FML_CHECK(aiks_context.GetContext()->GetBackendType() ==
88-
Context::BackendType::kVulkan);
89118
return ReadTexture(aiks_context.GetContext(), texture);
90119
}
91120

impeller/playground/backend/gles/playground_impl_gles.cc

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@
44

55
#include "impeller/playground/backend/gles/playground_impl_gles.h"
66

7+
#define IMPELLER_PLAYGROUND_SUPPORTS_ANGLE FML_OS_MACOSX
8+
9+
#if IMPELLER_PLAYGROUND_SUPPORTS_ANGLE
10+
#include <dlfcn.h>
11+
#endif
12+
713
#define GLFW_INCLUDE_NONE
814
#include "third_party/glfw/include/GLFW/glfw3.h"
915

@@ -58,12 +64,26 @@ void PlaygroundImplGLES::DestroyWindowHandle(WindowHandle handle) {
5864
PlaygroundImplGLES::PlaygroundImplGLES(PlaygroundSwitches switches)
5965
: PlaygroundImpl(switches),
6066
handle_(nullptr, &DestroyWindowHandle),
61-
worker_(std::shared_ptr<ReactorWorker>(new ReactorWorker())) {
67+
worker_(std::shared_ptr<ReactorWorker>(new ReactorWorker())),
68+
use_angle_(switches.use_angle) {
69+
if (use_angle_) {
70+
#if IMPELLER_PLAYGROUND_SUPPORTS_ANGLE
71+
angle_glesv2_ = dlopen("libGLESv2.dylib", RTLD_LAZY);
72+
#endif
73+
FML_CHECK(angle_glesv2_ != nullptr);
74+
}
75+
6276
::glfwDefaultWindowHints();
6377

6478
#if FML_OS_MACOSX
65-
// ES Profiles are not supported on Mac.
66-
::glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API);
79+
if (use_angle_) {
80+
::glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API);
81+
::glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
82+
::glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
83+
::glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
84+
} else {
85+
::glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API);
86+
}
6787
#else // FML_OS_MACOSX
6888
::glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
6989
::glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
@@ -113,9 +133,18 @@ ShaderLibraryMappingsForPlayground() {
113133

114134
// |PlaygroundImpl|
115135
std::shared_ptr<Context> PlaygroundImplGLES::GetContext() const {
116-
auto resolver = [](const char* name) -> void* {
117-
return reinterpret_cast<void*>(::glfwGetProcAddress(name));
118-
};
136+
auto resolver = use_angle_ ? [](const char* name) -> void* {
137+
void* symbol = nullptr;
138+
#if IMPELLER_PLAYGROUND_SUPPORTS_ANGLE
139+
void* angle_glesv2 = dlopen("libGLESv2.dylib", RTLD_LAZY);
140+
symbol = dlsym(angle_glesv2, name);
141+
#endif
142+
FML_CHECK(symbol);
143+
return symbol;
144+
}
145+
: [](const char* name) -> void* {
146+
return reinterpret_cast<void*>(::glfwGetProcAddress(name));
147+
};
119148
auto gl = std::make_unique<ProcTableGLES>(resolver);
120149
if (!gl->IsValid()) {
121150
FML_LOG(ERROR) << "Proc table when creating a playground was invalid.";

impeller/playground/backend/gles/playground_impl_gles.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ class PlaygroundImplGLES final : public PlaygroundImpl {
2626
using UniqueHandle = std::unique_ptr<void, decltype(&DestroyWindowHandle)>;
2727
UniqueHandle handle_;
2828
std::shared_ptr<ReactorWorker> worker_;
29+
const bool use_angle_;
30+
void* angle_glesv2_;
2931

3032
// |PlaygroundImpl|
3133
std::shared_ptr<Context> GetContext() const override;

impeller/playground/switches.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ struct PlaygroundSwitches {
2020
// rendered in the playground.
2121
std::optional<std::chrono::milliseconds> timeout;
2222
bool enable_vulkan_validation = false;
23+
bool use_angle = false;
2324

2425
PlaygroundSwitches();
2526

impeller/tools/impeller.gni

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -641,11 +641,7 @@ template("_impeller_shaders_gles") {
641641
if (impeller_enable_metal || impeller_enable_vulkan) {
642642
intermediates_subdir = "gles"
643643
}
644-
if (is_mac) {
645-
shader_target_flags = [ "--opengl-desktop" ]
646-
} else {
647-
shader_target_flags = [ "--opengl-es" ]
648-
}
644+
shader_target_flags = [ "--opengl-es" ]
649645

650646
defines = [ "IMPELLER_TARGET_OPENGLES" ]
651647
}

testing/run_tests.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -518,12 +518,16 @@ def make_test(name, flags=None, extra_env=None):
518518
'METAL_DEBUG_ERROR_MODE': '0', # Enables metal validation.
519519
'METAL_DEVICE_WRAPPER_TYPE': '1', # Enables metal validation.
520520
})
521+
mac_impeller_unittests_flags = shuffle_flags + [
522+
'--enable_vulkan_validation',
523+
'--gtest_filter=-*OpenGLES' # These are covered in the golden tests.
524+
]
521525
# Impeller tests are only supported on macOS for now.
522526
run_engine_executable(
523527
build_dir,
524528
'impeller_unittests',
525529
executable_filter,
526-
shuffle_flags + ['--enable_vulkan_validation'],
530+
mac_impeller_unittests_flags,
527531
coverage=coverage,
528532
extra_env=extra_env,
529533
# TODO(https://github.com/flutter/flutter/issues/123733): Remove this allowlist.
@@ -558,7 +562,10 @@ def make_test(name, flags=None, extra_env=None):
558562
build_dir,
559563
'impeller_dart_unittests',
560564
executable_filter,
561-
shuffle_flags + ['--enable_vulkan_validation'],
565+
shuffle_flags + [
566+
'--enable_vulkan_validation',
567+
'--gtest_filter=-*OpenGLES', # TODO(tbd)
568+
],
562569
coverage=coverage,
563570
extra_env=extra_env,
564571
)
@@ -1107,7 +1114,7 @@ def run_impeller_golden_tests(build_dir: str):
11071114
'golden_tests_harvester'
11081115
)
11091116
with tempfile.TemporaryDirectory(prefix='impeller_golden') as temp_dir:
1110-
run_cmd([tests_path, '--working_dir=%s' % temp_dir])
1117+
run_cmd([tests_path, '--working_dir=%s' % temp_dir], cwd=build_dir)
11111118
with DirectoryChange(harvester_path):
11121119
run_cmd(['dart', 'pub', 'get'])
11131120
bin_path = Path('.').joinpath('bin'

tools/gn

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -717,9 +717,8 @@ def to_gn_args(args):
717717
# gen_snapshot, but the build defines otherwise make it look like the build is
718718
# for a host Windows build and make GN think we will be building ANGLE.
719719
# Angle is not used on Mac hosts as there are no tests for the OpenGL backend.
720-
if (is_host_build(args) and
721-
gn_args['host_os'] != 'mac') or (args.target_os == 'android' and
722-
get_host_os() == 'win'):
720+
if is_host_build(args) or (args.target_os == 'android' and
721+
get_host_os() == 'win'):
723722
# Do not build unnecessary parts of the ANGLE tree.
724723
gn_args['angle_build_all'] = False
725724
gn_args['angle_has_astc_encoder'] = False
@@ -728,6 +727,10 @@ def to_gn_args(args):
728727
# https://github.com/flutter/flutter/issues/114107
729728
if get_host_os() == 'win':
730729
gn_args['angle_force_context_check_every_call'] = True
730+
if get_host_os() == 'mac':
731+
gn_args['angle_enable_metal'] = True
732+
gn_args['angle_enable_gl'] = False
733+
gn_args['angle_enable_vulkan'] = False
731734

732735
# ANGLE and SwiftShader share build flags to enable X11 and Wayland,
733736
# but we only need these enabled for SwiftShader.

0 commit comments

Comments
 (0)