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

[Impeller] implemented golden image tests for opengles #50146

Merged
merged 13 commits into from
Jan 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions ci/builders/mac_host_engine.json
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@
"--prebuilt-dart-sdk",
"--build-embedder-examples",
"--enable-impeller-vulkan",
"--enable-impeller-opengles",
"--use-glfw-swiftshader"
],
"name": "host_release",
Expand Down
2 changes: 2 additions & 0 deletions impeller/golden_tests/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ if (is_mac) {
"//flutter/impeller/aiks:aiks_unittests_golden",
"//flutter/impeller/fixtures",
"//flutter/third_party/swiftshader",
"//third_party/angle:libEGL",
"//third_party/angle:libGLESv2",
"//third_party/googletest:gtest",
]
}
Expand Down
87 changes: 45 additions & 42 deletions impeller/golden_tests/golden_playground_test_mac.cc
Original file line number Diff line number Diff line change
Expand Up @@ -55,47 +55,33 @@ const std::unique_ptr<PlaygroundImpl>& GetSharedVulkanPlayground(
}
} // namespace

#define IMP_AIKSTEST(name) \
"impeller_Play_AiksTest_" #name "_Metal", \
"impeller_Play_AiksTest_" #name "_OpenGLES", \
"impeller_Play_AiksTest_" #name "_Vulkan"

// If you add a new playground test to the aiks unittests and you do not want it
// to also be a golden test, then add the test name here.
static const std::vector<std::string> kSkipTests = {
"impeller_Play_AiksTest_CanDrawPaintMultipleTimesInteractive_Metal",
"impeller_Play_AiksTest_CanDrawPaintMultipleTimesInteractive_Vulkan",
"impeller_Play_AiksTest_CanRenderLinearGradientManyColorsUnevenStops_Metal",
"impeller_Play_AiksTest_CanRenderLinearGradientManyColorsUnevenStops_"
"Vulkan",
"impeller_Play_AiksTest_CanRenderRadialGradient_Metal",
"impeller_Play_AiksTest_CanRenderRadialGradient_Vulkan",
"impeller_Play_AiksTest_CanRenderRadialGradientManyColors_Metal",
"impeller_Play_AiksTest_CanRenderRadialGradientManyColors_Vulkan",
"impeller_Play_AiksTest_CanRenderBackdropBlurInteractive_Metal",
"impeller_Play_AiksTest_CanRenderBackdropBlurInteractive_Vulkan",
"impeller_Play_AiksTest_ClippedBlurFilterRendersCorrectlyInteractive_Metal",
"impeller_Play_AiksTest_ClippedBlurFilterRendersCorrectlyInteractive_"
"Vulkan",
"impeller_Play_AiksTest_CoverageOriginShouldBeAccountedForInSubpasses_"
"Metal",
"impeller_Play_AiksTest_CoverageOriginShouldBeAccountedForInSubpasses_"
"Vulkan",
"impeller_Play_AiksTest_GaussianBlurRotatedAndClippedInteractive_Metal",
"impeller_Play_AiksTest_GaussianBlurRotatedAndClippedInteractive_Vulkan",
"impeller_Play_AiksTest_GradientStrokesRenderCorrectly_Metal",
"impeller_Play_AiksTest_GradientStrokesRenderCorrectly_Vulkan",
"impeller_Play_AiksTest_ColorWheel_Metal",
"impeller_Play_AiksTest_ColorWheel_Vulkan",
"impeller_Play_AiksTest_SceneColorSource_Metal",
"impeller_Play_AiksTest_SceneColorSource_Vulkan",
"impeller_Play_AiksTest_SolidStrokesRenderCorrectly_Metal",
"impeller_Play_AiksTest_SolidStrokesRenderCorrectly_Vulkan",
"impeller_Play_AiksTest_TextFrameSubpixelAlignment_Metal",
"impeller_Play_AiksTest_TextFrameSubpixelAlignment_Vulkan",
IMP_AIKSTEST(CanDrawPaintMultipleTimesInteractive),
IMP_AIKSTEST(CanRenderLinearGradientManyColorsUnevenStops),
IMP_AIKSTEST(CanRenderRadialGradient),
IMP_AIKSTEST(CanRenderRadialGradientManyColors),
IMP_AIKSTEST(CanRenderBackdropBlurInteractive),
IMP_AIKSTEST(ClippedBlurFilterRendersCorrectlyInteractive),
IMP_AIKSTEST(CoverageOriginShouldBeAccountedForInSubpasses),
IMP_AIKSTEST(GaussianBlurRotatedAndClippedInteractive),
IMP_AIKSTEST(GradientStrokesRenderCorrectly),
IMP_AIKSTEST(ColorWheel),
IMP_AIKSTEST(SceneColorSource),
IMP_AIKSTEST(SolidStrokesRenderCorrectly),
IMP_AIKSTEST(TextFrameSubpixelAlignment),
// TextRotated is flakey and we can't seem to get it to stabilize on Skia
// Gold.
"impeller_Play_AiksTest_TextRotated_Metal",
"impeller_Play_AiksTest_TextRotated_Vulkan",
IMP_AIKSTEST(TextRotated),
// Runtime stage based tests get confused with a Metal context.
"impeller_Play_AiksTest_CanRenderClippedRuntimeEffects_Vulkan",
"impeller_Play_AiksTest_CaptureContext_Metal",
"impeller_Play_AiksTest_CaptureContext_Vulkan",
IMP_AIKSTEST(CaptureContext),
};

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

struct GoldenPlaygroundTest::GoldenPlaygroundTestImpl {
std::unique_ptr<PlaygroundImpl> test_vulkan_playground;
std::unique_ptr<PlaygroundImpl> test_opengl_playground;
std::unique_ptr<testing::Screenshotter> screenshotter;
ISize window_size = ISize{1024, 768};
};
Expand Down Expand Up @@ -172,13 +159,29 @@ void GoldenPlaygroundTest::SetUp() {
std::filesystem::path icd_path = target_path / "vk_swiftshader_icd.json";
setenv("VK_ICD_FILENAMES", icd_path.c_str(), 1);

if (GetBackend() != PlaygroundBackend::kMetal &&
GetBackend() != PlaygroundBackend::kVulkan) {
GTEST_SKIP_("GoldenPlaygroundTest doesn't support this backend type.");
return;
}

bool enable_vulkan_validations = ShouldTestHaveVulkanValidations();
switch (GetParam()) {
case PlaygroundBackend::kMetal:
pimpl_->screenshotter = std::make_unique<testing::MetalScreenshotter>();
break;
case PlaygroundBackend::kVulkan: {
const std::unique_ptr<PlaygroundImpl>& playground =
GetSharedVulkanPlayground(enable_vulkan_validations);
pimpl_->screenshotter =
std::make_unique<testing::VulkanScreenshotter>(playground);
break;
}
case PlaygroundBackend::kOpenGLES: {
FML_CHECK(::glfwInit() == GLFW_TRUE);
PlaygroundSwitches playground_switches;
playground_switches.use_angle = true;
pimpl_->test_opengl_playground = PlaygroundImpl::Create(
PlaygroundBackend::kOpenGLES, playground_switches);
pimpl_->screenshotter = std::make_unique<testing::VulkanScreenshotter>(
pimpl_->test_opengl_playground);
break;
}
}
if (GetParam() == PlaygroundBackend::kMetal) {
pimpl_->screenshotter = std::make_unique<testing::MetalScreenshotter>();
} else if (GetParam() == PlaygroundBackend::kVulkan) {
Expand Down Expand Up @@ -272,8 +275,8 @@ std::shared_ptr<Context> GoldenPlaygroundTest::MakeContext() const {
pimpl_->test_vulkan_playground);
return pimpl_->test_vulkan_playground->GetContext();
} else {
FML_CHECK(false);
return nullptr;
/// On OpenGL we create a context for each test.
return GetContext();
}
}

Expand Down
63 changes: 46 additions & 17 deletions impeller/golden_tests/vulkan_screenshotter.mm
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,22 @@

#include "flutter/fml/synchronization/waitable_event.h"
#include "flutter/impeller/golden_tests/metal_screenshot.h"
#include "impeller/renderer/backend/vulkan/surface_context_vk.h"
#include "impeller/renderer/backend/vulkan/texture_vk.h"
#define GLFW_INCLUDE_NONE
#include "third_party/glfw/include/GLFW/glfw3.h"

namespace impeller {
namespace testing {

namespace {

using CGContextPtr = std::unique_ptr<std::remove_pointer<CGContextRef>::type,
decltype(&CGContextRelease)>;
using CGImagePtr = std::unique_ptr<std::remove_pointer<CGImageRef>::type,
decltype(&CGImageRelease)>;
using CGColorSpacePtr =
std::unique_ptr<std::remove_pointer<CGColorSpaceRef>::type,
decltype(&CGColorSpaceRelease)>;

std::unique_ptr<Screenshot> ReadTexture(
const std::shared_ptr<Context>& surface_context,
const std::shared_ptr<Texture>& texture) {
Expand Down Expand Up @@ -49,21 +56,45 @@
// TODO(gaaclarke): Replace CoreImage requirement with something
// crossplatform.

CGColorSpaceRef color_space = CGColorSpaceCreateDeviceRGB();
CGColorSpacePtr color_space(CGColorSpaceCreateDeviceRGB(),
&CGColorSpaceRelease);
CGBitmapInfo bitmap_info =
kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little;
CGContextRef context = CGBitmapContextCreate(
device_buffer->OnGetContents(), texture->GetSize().width,
texture->GetSize().height,
/*bitsPerComponent=*/8,
/*bytesPerRow=*/texture->GetTextureDescriptor().GetBytesPerRow(),
color_space, bitmap_info);
texture->GetTextureDescriptor().format == PixelFormat::kB8G8R8A8UNormInt
? kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little
: kCGImageAlphaPremultipliedLast;
CGContextPtr context(
CGBitmapContextCreate(
device_buffer->OnGetContents(), texture->GetSize().width,
texture->GetSize().height,
/*bitsPerComponent=*/8,
/*bytesPerRow=*/texture->GetTextureDescriptor().GetBytesPerRow(),
color_space.get(), bitmap_info),
&CGContextRelease);
FML_CHECK(context);
CGImageRef image_ref = CGBitmapContextCreateImage(context);
FML_CHECK(image_ref);
CGContextRelease(context);
CGColorSpaceRelease(color_space);
return std::make_unique<MetalScreenshot>(image_ref);
CGImagePtr image(CGBitmapContextCreateImage(context.get()), &CGImageRelease);
FML_CHECK(image);

// TODO(tbd): Perform the flip at the blit stage to avoid this slow
// copy.
if (texture->GetYCoordScale() == -1) {
CGContextPtr flipped_context(
CGBitmapContextCreate(
nullptr, texture->GetSize().width, texture->GetSize().height,
/*bitsPerComponent=*/8,
/*bytesPerRow=*/0, color_space.get(), bitmap_info),
&CGContextRelease);
CGContextTranslateCTM(flipped_context.get(), 0, texture->GetSize().height);
CGContextScaleCTM(flipped_context.get(), 1.0, -1.0);
CGContextDrawImage(
flipped_context.get(),
CGRectMake(0, 0, texture->GetSize().width, texture->GetSize().height),
image.get());
CGImagePtr flipped_image(CGBitmapContextCreateImage(flipped_context.get()),
&CGImageRelease);
image.swap(flipped_image);
}

return std::make_unique<MetalScreenshot>(image.release());
}
} // namespace

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

Expand Down
41 changes: 35 additions & 6 deletions impeller/playground/backend/gles/playground_impl_gles.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@

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

#define IMPELLER_PLAYGROUND_SUPPORTS_ANGLE FML_OS_MACOSX

#if IMPELLER_PLAYGROUND_SUPPORTS_ANGLE
#include <dlfcn.h>
#endif

#define GLFW_INCLUDE_NONE
#include "third_party/glfw/include/GLFW/glfw3.h"

Expand Down Expand Up @@ -58,12 +64,26 @@ void PlaygroundImplGLES::DestroyWindowHandle(WindowHandle handle) {
PlaygroundImplGLES::PlaygroundImplGLES(PlaygroundSwitches switches)
: PlaygroundImpl(switches),
handle_(nullptr, &DestroyWindowHandle),
worker_(std::shared_ptr<ReactorWorker>(new ReactorWorker())) {
worker_(std::shared_ptr<ReactorWorker>(new ReactorWorker())),
use_angle_(switches.use_angle) {
if (use_angle_) {
#if IMPELLER_PLAYGROUND_SUPPORTS_ANGLE
angle_glesv2_ = dlopen("libGLESv2.dylib", RTLD_LAZY);
#endif
FML_CHECK(angle_glesv2_ != nullptr);
}

::glfwDefaultWindowHints();

#if FML_OS_MACOSX
// ES Profiles are not supported on Mac.
::glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API);
if (use_angle_) {
::glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API);
::glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
::glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
::glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
} else {
::glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API);
}
#else // FML_OS_MACOSX
::glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
::glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
Expand Down Expand Up @@ -113,9 +133,18 @@ ShaderLibraryMappingsForPlayground() {

// |PlaygroundImpl|
std::shared_ptr<Context> PlaygroundImplGLES::GetContext() const {
auto resolver = [](const char* name) -> void* {
return reinterpret_cast<void*>(::glfwGetProcAddress(name));
};
auto resolver = use_angle_ ? [](const char* name) -> void* {
void* symbol = nullptr;
#if IMPELLER_PLAYGROUND_SUPPORTS_ANGLE
void* angle_glesv2 = dlopen("libGLESv2.dylib", RTLD_LAZY);
symbol = dlsym(angle_glesv2, name);
#endif
FML_CHECK(symbol);
return symbol;
}
: [](const char* name) -> void* {
return reinterpret_cast<void*>(::glfwGetProcAddress(name));
};
auto gl = std::make_unique<ProcTableGLES>(resolver);
if (!gl->IsValid()) {
FML_LOG(ERROR) << "Proc table when creating a playground was invalid.";
Expand Down
2 changes: 2 additions & 0 deletions impeller/playground/backend/gles/playground_impl_gles.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ class PlaygroundImplGLES final : public PlaygroundImpl {
using UniqueHandle = std::unique_ptr<void, decltype(&DestroyWindowHandle)>;
UniqueHandle handle_;
std::shared_ptr<ReactorWorker> worker_;
const bool use_angle_;
void* angle_glesv2_;

// |PlaygroundImpl|
std::shared_ptr<Context> GetContext() const override;
Expand Down
1 change: 1 addition & 0 deletions impeller/playground/switches.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ struct PlaygroundSwitches {
// rendered in the playground.
std::optional<std::chrono::milliseconds> timeout;
bool enable_vulkan_validation = false;
bool use_angle = false;

PlaygroundSwitches();

Expand Down
6 changes: 1 addition & 5 deletions impeller/tools/impeller.gni
Original file line number Diff line number Diff line change
Expand Up @@ -641,11 +641,7 @@ template("_impeller_shaders_gles") {
if (impeller_enable_metal || impeller_enable_vulkan) {
intermediates_subdir = "gles"
}
if (is_mac) {
shader_target_flags = [ "--opengl-desktop" ]
} else {
shader_target_flags = [ "--opengl-es" ]
}
shader_target_flags = [ "--opengl-es" ]

defines = [ "IMPELLER_TARGET_OPENGLES" ]
}
Expand Down
13 changes: 10 additions & 3 deletions testing/run_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -518,12 +518,16 @@ def make_test(name, flags=None, extra_env=None):
'METAL_DEBUG_ERROR_MODE': '0', # Enables metal validation.
'METAL_DEVICE_WRAPPER_TYPE': '1', # Enables metal validation.
})
mac_impeller_unittests_flags = shuffle_flags + [
'--enable_vulkan_validation',
'--gtest_filter=-*OpenGLES' # These are covered in the golden tests.
]
# Impeller tests are only supported on macOS for now.
run_engine_executable(
build_dir,
'impeller_unittests',
executable_filter,
shuffle_flags + ['--enable_vulkan_validation'],
mac_impeller_unittests_flags,
coverage=coverage,
extra_env=extra_env,
# TODO(https://github.com/flutter/flutter/issues/123733): Remove this allowlist.
Expand Down Expand Up @@ -558,7 +562,10 @@ def make_test(name, flags=None, extra_env=None):
build_dir,
'impeller_dart_unittests',
executable_filter,
shuffle_flags + ['--enable_vulkan_validation'],
shuffle_flags + [
'--enable_vulkan_validation',
'--gtest_filter=-*OpenGLES', # TODO(tbd)
],
coverage=coverage,
extra_env=extra_env,
)
Expand Down Expand Up @@ -1107,7 +1114,7 @@ def run_impeller_golden_tests(build_dir: str):
'golden_tests_harvester'
)
with tempfile.TemporaryDirectory(prefix='impeller_golden') as temp_dir:
run_cmd([tests_path, '--working_dir=%s' % temp_dir])
run_cmd([tests_path, '--working_dir=%s' % temp_dir], cwd=build_dir)
with DirectoryChange(harvester_path):
run_cmd(['dart', 'pub', 'get'])
bin_path = Path('.').joinpath('bin'
Expand Down
9 changes: 6 additions & 3 deletions tools/gn
Original file line number Diff line number Diff line change
Expand Up @@ -717,9 +717,8 @@ def to_gn_args(args):
# gen_snapshot, but the build defines otherwise make it look like the build is
# for a host Windows build and make GN think we will be building ANGLE.
# Angle is not used on Mac hosts as there are no tests for the OpenGL backend.
if (is_host_build(args) and
gn_args['host_os'] != 'mac') or (args.target_os == 'android' and
get_host_os() == 'win'):
if is_host_build(args) or (args.target_os == 'android' and
get_host_os() == 'win'):
# Do not build unnecessary parts of the ANGLE tree.
gn_args['angle_build_all'] = False
gn_args['angle_has_astc_encoder'] = False
Expand All @@ -728,6 +727,10 @@ def to_gn_args(args):
# https://github.com/flutter/flutter/issues/114107
if get_host_os() == 'win':
gn_args['angle_force_context_check_every_call'] = True
if get_host_os() == 'mac':
gn_args['angle_enable_metal'] = True
gn_args['angle_enable_gl'] = False
gn_args['angle_enable_vulkan'] = False

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