[Impeller] implemented golden image tests for opengles (flutter/engine#50146)
fixes https://github.com/flutter/flutter/issues/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
This commit is contained in:
@@ -159,6 +159,7 @@
|
||||
"--prebuilt-dart-sdk",
|
||||
"--build-embedder-examples",
|
||||
"--enable-impeller-vulkan",
|
||||
"--enable-impeller-opengles",
|
||||
"--use-glfw-swiftshader"
|
||||
],
|
||||
"name": "host_release",
|
||||
|
||||
@@ -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",
|
||||
]
|
||||
}
|
||||
|
||||
@@ -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 = {
|
||||
@@ -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};
|
||||
};
|
||||
@@ -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) {
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,8 +6,6 @@
|
||||
|
||||
#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"
|
||||
|
||||
@@ -15,6 +13,15 @@ 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) {
|
||||
@@ -49,21 +56,45 @@ std::unique_ptr<Screenshot> ReadTexture(
|
||||
// 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
|
||||
|
||||
@@ -84,8 +115,6 @@ std::unique_ptr<Screenshot> VulkanScreenshotter::MakeScreenshot(
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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);
|
||||
@@ -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.";
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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" ]
|
||||
}
|
||||
|
||||
@@ -518,12 +518,16 @@ def run_cc_tests(build_dir, executable_filter, coverage, capture_core_dump):
|
||||
'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.
|
||||
@@ -558,7 +562,10 @@ def run_cc_tests(build_dir, executable_filter, coverage, capture_core_dump):
|
||||
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,
|
||||
)
|
||||
@@ -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'
|
||||
|
||||
@@ -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
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user