| Flutter Impeller |

golden_playground_test_mac.cc
Go to the documentation of this file.
1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <dlfcn.h>
6 #include <filesystem>
7 #include <memory>
8
9 #include "display_list/display_list.h"
10 #include "flutter/impeller/golden_tests/golden_playground_test.h"
11
12 #include "flutter/impeller/golden_tests/golden_digest.h"
13 #include "flutter/impeller/golden_tests/metal_screenshotter.h"
14 #include "flutter/impeller/golden_tests/vulkan_screenshotter.h"
15 #include "fml/closure.h"
16 #include "impeller/display_list/aiks_context.h"
17 #include "impeller/display_list/dl_dispatcher.h"
18 #include "impeller/display_list/dl_image_impeller.h"
19 #include "impeller/typographer/backends/skia/typographer_context_skia.h"
20 #include "impeller/typographer/typographer_context.h"
21 #include "third_party/abseil-cpp/absl/base/no_destructor.h"
22
23 #define GLFW_INCLUDE_NONE
24 #include "third_party/glfw/include/GLFW/glfw3.h"
25
26 namespace impeller {
27
28 namespace {
29 std::unique_ptr<PlaygroundImpl> MakeVulkanPlayground(bool enable_validations) {
30 FML_CHECK(::glfwInit() == GLFW_TRUE);
31 PlaygroundSwitches playground_switches;
32 playground_switches.enable_vulkan_validation = enable_validations;
33 return PlaygroundImpl::Create(PlaygroundBackend::kVulkan,
34 playground_switches);
35 }
36
37 // Returns a static instance to a playground that can be used across tests.
38 const std::unique_ptr<PlaygroundImpl>& GetSharedVulkanPlayground(
39 bool enable_validations) {
40 if (enable_validations) {
41 static absl::NoDestructor<std::unique_ptr<PlaygroundImpl>>
42 vulkan_validation_playground(
43 MakeVulkanPlayground(/*enable_validations=*/true));
44 // TODO(142237): This can be removed when the thread local storage is
45 // removed.
46 static fml::ScopedCleanupClosure context_cleanup(
47 [&] { (*vulkan_validation_playground)->GetContext()->Shutdown(); });
48 return *vulkan_validation_playground;
49 } else {
50 static absl::NoDestructor<std::unique_ptr<PlaygroundImpl>>
51 vulkan_playground(MakeVulkanPlayground(/*enable_validations=*/false));
52 // TODO(142237): This can be removed when the thread local storage is
53 // removed.
54 static fml::ScopedCleanupClosure context_cleanup(
55 [&] { (*vulkan_playground)->GetContext()->Shutdown(); });
56 return *vulkan_playground;
57 }
58 }
59
60 } // namespace
61
62 #define IMP_AIKSTEST(name) \
63 "impeller_Play_AiksTest_" #name "_Metal", \
64 "impeller_Play_AiksTest_" #name "_OpenGLES", \
65 "impeller_Play_AiksTest_" #name "_Vulkan"
66
67 // If you add a new playground test to the aiks unittests and you do not want it
68 // to also be a golden test, then add the test name here.
69 static const std::vector<std::string> kSkipTests = {
70 // TextRotated is flakey and we can't seem to get it to stabilize on Skia
71 // Gold.
72 IMP_AIKSTEST(TextRotated),
73 // Runtime stage based tests get confused with a Metal context.
74 "impeller_Play_AiksTest_CanRenderClippedRuntimeEffects_Vulkan",
75 };
76
77 namespace {
78 std::string GetTestName() {
79 std::string suite_name =
80 ::testing::UnitTest::GetInstance()->current_test_suite()->name();
81 std::string test_name =
82 ::testing::UnitTest::GetInstance()->current_test_info()->name();
83 std::stringstream ss;
84 ss << "impeller_" << suite_name << "_" << test_name;
85 std::string result = ss.str();
86 // Make sure there are no slashes in the test name.
87 std::replace(result.begin(), result.end(), '/', '_');
88 return result;
89 }
90
91 std::string GetGoldenFilename(const std::string& postfix) {
92 return GetTestName() + postfix + ".png";
93 }
94 } // namespace
95
96 bool GoldenPlaygroundTest::SaveScreenshot(
97 std::unique_ptr<testing::Screenshot> screenshot,
98 const std::string& postfix) {
99 if (!screenshot || !screenshot->GetBytes()) {
100 FML_LOG(ERROR) << "Failed to collect screenshot for test " << GetTestName();
101 return false;
102 }
103 std::string test_name = GetTestName();
104 std::string filename = GetGoldenFilename(postfix);
105 testing::GoldenDigest::Instance()->AddImage(
106 test_name, filename, screenshot->GetWidth(), screenshot->GetHeight());
107 if (!screenshot->WriteToPNG(
108 testing::WorkingDirectory::Instance()->GetFilenamePath(filename))) {
109 FML_LOG(ERROR) << "Failed to write screenshot to " << filename;
110 return false;
111 }
112 return true;
113 }
114
115 struct GoldenPlaygroundTest::GoldenPlaygroundTestImpl {
116 std::unique_ptr<PlaygroundImpl> test_vulkan_playground;
117 std::unique_ptr<PlaygroundImpl> test_opengl_playground;
118 std::unique_ptr<testing::Screenshotter> screenshotter;
119 ISize window_size = ISize{1024, 768};
120 };
121
122 GoldenPlaygroundTest::GoldenPlaygroundTest()
123 : typographer_context_(TypographerContextSkia::Make()),
124 pimpl_(new GoldenPlaygroundTest::GoldenPlaygroundTestImpl()) {}
125
126 GoldenPlaygroundTest::~GoldenPlaygroundTest() = default;
127
128 void GoldenPlaygroundTest::SetTypographerContext(
129 std::shared_ptr<TypographerContext> typographer_context) {
130 typographer_context_ = std::move(typographer_context);
131 };
132
133 void GoldenPlaygroundTest::TearDown() {
134 ASSERT_FALSE(dlopen("/usr/local/lib/libMoltenVK.dylib", RTLD_NOLOAD));
135
136 auto context = GetContext();
137 if (context) {
138 context->DisposeThreadLocalCachedResources();
139 }
140 }
141
142 namespace {
143 bool DoesSupportWideGamutTests() {
144 #ifdef __arm64__
145 return true;
146 #else
147 return false;
148 #endif
149 }
150 } // namespace
151
152 void GoldenPlaygroundTest::SetUp() {
153 std::filesystem::path testing_assets_path =
154 flutter::testing::GetTestingAssetsPath();
155 std::filesystem::path target_path = testing_assets_path.parent_path()
156 .parent_path()
157 .parent_path()
158 .parent_path();
159 std::filesystem::path icd_path = target_path / "vk_swiftshader_icd.json";
160 setenv("VK_ICD_FILENAMES", icd_path.c_str(), 1);
161
162 std::string test_name = GetTestName();
163 PlaygroundSwitches switches;
164 switches.enable_wide_gamut =
165 test_name.find("WideGamut_") != std::string::npos;
166 switches.flags.antialiased_lines =
167 test_name.find("ExperimentAntialiasLines_") != std::string::npos;
168 switch (GetParam()) {
169 case PlaygroundBackend::kMetal:
170 if (!DoesSupportWideGamutTests()) {
171 GTEST_SKIP()
172 << "This metal device doesn't support wide gamut golden tests.";
173 }
174 pimpl_->screenshotter =
175 std::make_unique<testing::MetalScreenshotter>(switches);
176 break;
177 case PlaygroundBackend::kVulkan: {
178 if (switches.enable_wide_gamut) {
179 GTEST_SKIP() << "Vulkan doesn't support wide gamut golden tests.";
180 }
181 if (switches.flags.antialiased_lines) {
182 GTEST_SKIP()
183 << "Vulkan doesn't support antialiased lines golden tests.";
184 }
185 const std::unique_ptr<PlaygroundImpl>& playground =
186 GetSharedVulkanPlayground(/*enable_validations=*/true);
187 pimpl_->screenshotter =
188 std::make_unique<testing::VulkanScreenshotter>(playground);
189 break;
190 }
191 case PlaygroundBackend::kOpenGLES: {
192 if (switches.enable_wide_gamut) {
193 GTEST_SKIP() << "OpenGLES doesn't support wide gamut golden tests.";
194 }
195 if (switches.flags.antialiased_lines) {
196 GTEST_SKIP()
197 << "OpenGLES doesn't support antialiased lines golden tests.";
198 }
199 FML_CHECK(::glfwInit() == GLFW_TRUE);
200 PlaygroundSwitches playground_switches;
201 playground_switches.use_angle = true;
202 pimpl_->test_opengl_playground = PlaygroundImpl::Create(
203 PlaygroundBackend::kOpenGLES, playground_switches);
204 pimpl_->screenshotter = std::make_unique<testing::VulkanScreenshotter>(
205 pimpl_->test_opengl_playground);
206 break;
207 }
208 }
209
210 if (std::find(kSkipTests.begin(), kSkipTests.end(), test_name) !=
211 kSkipTests.end()) {
212 GTEST_SKIP()
213 << "GoldenPlaygroundTest doesn't support interactive playground tests "
214 "yet.";
215 }
216
217 testing::GoldenDigest::Instance()->AddDimension(
218 "gpu_string", GetContext()->DescribeGpuModel());
219 }
220
221 PlaygroundBackend GoldenPlaygroundTest::GetBackend() const {
222 return GetParam();
223 }
224
225 bool GoldenPlaygroundTest::OpenPlaygroundHere(
226 const AiksDlPlaygroundCallback& callback) {
227 AiksContext renderer(GetContext(), typographer_context_);
228
229 std::unique_ptr<testing::Screenshot> screenshot;
230 Point content_scale =
231 pimpl_->screenshotter->GetPlayground().GetContentScale();
232
233 ISize physical_window_size(
234 std::round(pimpl_->window_size.width * content_scale.x),
235 std::round(pimpl_->window_size.height * content_scale.y));
236 for (int i = 0; i < 2; ++i) {
237 auto display_list = callback();
238 auto texture =
239 DisplayListToTexture(display_list, physical_window_size, renderer);
240 screenshot = pimpl_->screenshotter->MakeScreenshot(renderer, texture);
241 }
242 return SaveScreenshot(std::move(screenshot));
243 }
244
245 bool GoldenPlaygroundTest::OpenPlaygroundHere(
246 const sk_sp<flutter::DisplayList>& list) {
247 return OpenPlaygroundHere([&list]() { return list; });
248 }
249
250 bool GoldenPlaygroundTest::ImGuiBegin(const char* name,
251 bool* p_open,
252 ImGuiWindowFlags flags) {
253 return false;
254 }
255
256 std::shared_ptr<Texture> GoldenPlaygroundTest::CreateTextureForFixture(
257 const char* fixture_name,
258 bool enable_mipmapping) const {
259 std::shared_ptr<fml::Mapping> mapping =
260 flutter::testing::OpenFixtureAsMapping(fixture_name);
261 auto result = Playground::CreateTextureForMapping(GetContext(), mapping,
262 enable_mipmapping);
263 if (result) {
264 result->SetLabel(fixture_name);
265 }
266 return result;
267 }
268
269 sk_sp<flutter::DlImage> GoldenPlaygroundTest::CreateDlImageForFixture(
270 const char* fixture_name,
271 bool enable_mipmapping) const {
272 std::shared_ptr<Texture> texture =
273 CreateTextureForFixture(fixture_name, enable_mipmapping);
274 return DlImageImpeller::Make(texture);
275 }
276
277 RuntimeStage::Map GoldenPlaygroundTest::OpenAssetAsRuntimeStage(
278 const char* asset_name) const {
279 const std::shared_ptr<fml::Mapping> fixture =
280 flutter::testing::OpenFixtureAsMapping(asset_name);
281 if (!fixture || fixture->GetSize() == 0) {
282 return {};
283 }
284 auto stages = RuntimeStage::DecodeRuntimeStages(fixture);
285 if (!stages.ok()) {
286 return {};
287 }
288 return stages.value();
289 }
290
291 std::shared_ptr<Context> GoldenPlaygroundTest::GetContext() const {
292 if (!pimpl_->screenshotter) {
293 return nullptr;
294 }
295 return pimpl_->screenshotter->GetPlayground().GetContext();
296 }
297
298 std::shared_ptr<Context> GoldenPlaygroundTest::MakeContext() const {
299 if (GetParam() == PlaygroundBackend::kMetal) {
300 /// On Metal we create a context for each test.
301 return GetContext();
302 } else if (GetParam() == PlaygroundBackend::kVulkan) {
303 bool enable_vulkan_validations = true;
304 FML_CHECK(!pimpl_->test_vulkan_playground)
305 << "We don't support creating multiple contexts for one test";
306 pimpl_->test_vulkan_playground =
307 MakeVulkanPlayground(enable_vulkan_validations);
308 pimpl_->screenshotter = std::make_unique<testing::VulkanScreenshotter>(
309 pimpl_->test_vulkan_playground);
310 return pimpl_->test_vulkan_playground->GetContext();
311 } else {
312 /// On OpenGL we create a context for each test.
313 return GetContext();
314 }
315 }
316
317 Point GoldenPlaygroundTest::GetContentScale() const {
318 return pimpl_->screenshotter->GetPlayground().GetContentScale();
319 }
320
321 Scalar GoldenPlaygroundTest::GetSecondsElapsed() const {
322 return 0.0f;
323 }
324
325 ISize GoldenPlaygroundTest::GetWindowSize() const {
326 return pimpl_->window_size;
327 }
328
329 void GoldenPlaygroundTest::GoldenPlaygroundTest::SetWindowSize(ISize size) {
330 pimpl_->window_size = size;
331 }
332
333 fml::Status GoldenPlaygroundTest::SetCapabilities(
334 const std::shared_ptr<Capabilities>& capabilities) {
335 return pimpl_->screenshotter->GetPlayground().SetCapabilities(capabilities);
336 }
337
338 std::unique_ptr<testing::Screenshot> GoldenPlaygroundTest::MakeScreenshot(
339 const sk_sp<flutter::DisplayList>& list) {
340 AiksContext renderer(GetContext(), typographer_context_);
341 Point content_scale =
342 pimpl_->screenshotter->GetPlayground().GetContentScale();
343
344 ISize physical_window_size(
345 std::round(pimpl_->window_size.width * content_scale.x),
346 std::round(pimpl_->window_size.height * content_scale.y));
347 return pimpl_->screenshotter->MakeScreenshot(
348 renderer, DisplayListToTexture(list, physical_window_size, renderer));
349 }
350
351 } // namespace impeller
aiks_context.h
impeller::AiksContext
Definition: aiks_context.h:20
impeller::DlImageImpeller::Make
static sk_sp< DlImageImpeller > Make(std::shared_ptr< Texture > texture, OwningContext owning_context=OwningContext::kIO)
Definition: dl_image_impeller.cc:23
impeller::GoldenPlaygroundTest
Definition: golden_playground_test.h:27
impeller::GoldenPlaygroundTest::GetBackend
PlaygroundBackend GetBackend() const
Definition: golden_playground_test_mac.cc:221
impeller::GoldenPlaygroundTest::GetWindowSize
ISize GetWindowSize() const
Definition: golden_playground_test_mac.cc:325
impeller::GoldenPlaygroundTest::CreateDlImageForFixture
sk_sp< flutter::DlImage > CreateDlImageForFixture(const char *fixture_name, bool enable_mipmapping=false) const
Definition: golden_playground_test_mac.cc:269
impeller::GoldenPlaygroundTest::GetContentScale
Point GetContentScale() const
Definition: golden_playground_test_mac.cc:317
impeller::GoldenPlaygroundTest::SetCapabilities
fml::Status SetCapabilities(const std::shared_ptr< Capabilities > &capabilities)
Definition: golden_playground_test_mac.cc:333
impeller::GoldenPlaygroundTest::SetTypographerContext
void SetTypographerContext(std::shared_ptr< TypographerContext > typographer_context)
Definition: golden_playground_test_mac.cc:128
impeller::GoldenPlaygroundTest::MakeContext
std::shared_ptr< Context > MakeContext() const
Definition: golden_playground_test_mac.cc:298
impeller::GoldenPlaygroundTest::SaveScreenshot
static bool SaveScreenshot(std::unique_ptr< testing::Screenshot > screenshot, const std::string &postfix="")
Definition: golden_playground_test_mac.cc:96
impeller::GoldenPlaygroundTest::TearDown
void TearDown()
Definition: golden_playground_test_mac.cc:133
impeller::GoldenPlaygroundTest::MakeScreenshot
std::unique_ptr< testing::Screenshot > MakeScreenshot(const sk_sp< flutter::DisplayList > &list)
Definition: golden_playground_test_mac.cc:338
impeller::GoldenPlaygroundTest::GetSecondsElapsed
Scalar GetSecondsElapsed() const
Definition: golden_playground_test_mac.cc:321
impeller::GoldenPlaygroundTest::OpenAssetAsRuntimeStage
RuntimeStage::Map OpenAssetAsRuntimeStage(const char *asset_name) const
Definition: golden_playground_test_mac.cc:277
impeller::GoldenPlaygroundTest::OpenPlaygroundHere
bool OpenPlaygroundHere(Picture picture)
impeller::GoldenPlaygroundTest::ImGuiBegin
static bool ImGuiBegin(const char *name, bool *p_open, ImGuiWindowFlags flags)
Definition: golden_playground_test_mac.cc:250
impeller::GoldenPlaygroundTest::AiksDlPlaygroundCallback
std::function< sk_sp< flutter::DisplayList >()> AiksDlPlaygroundCallback
Definition: golden_playground_test.h:32
impeller::GoldenPlaygroundTest::SetUp
void SetUp()
Definition: golden_playground_test_mac.cc:152
impeller::GoldenPlaygroundTest::~GoldenPlaygroundTest
~GoldenPlaygroundTest() override
impeller::GoldenPlaygroundTest::GetContext
std::shared_ptr< Context > GetContext() const
Definition: golden_playground_test_mac.cc:291
impeller::GoldenPlaygroundTest::GoldenPlaygroundTest
GoldenPlaygroundTest()
Definition: golden_playground_test_mac.cc:122
impeller::GoldenPlaygroundTest::CreateTextureForFixture
std::shared_ptr< Texture > CreateTextureForFixture(const char *fixture_name, bool enable_mipmapping=false) const
Definition: golden_playground_test_mac.cc:256
impeller::Playground::CreateTextureForMapping
static std::shared_ptr< Texture > CreateTextureForMapping(const std::shared_ptr< Context > &context, std::shared_ptr< fml::Mapping > mapping, bool enable_mipmapping=false)
Definition: playground.cc:433
impeller::PlaygroundImpl::Create
static std::unique_ptr< PlaygroundImpl > Create(PlaygroundBackend backend, PlaygroundSwitches switches)
Definition: playground_impl.cc:25
impeller::RuntimeStage::Map
std::map< RuntimeStageBackend, std::shared_ptr< RuntimeStage > > Map
Definition: runtime_stage.h:25
impeller::RuntimeStage::DecodeRuntimeStages
static absl::StatusOr< Map > DecodeRuntimeStages(const std::shared_ptr< fml::Mapping > &payload)
Definition: runtime_stage.cc:63
impeller::TypographerContextSkia
Definition: typographer_context_skia.h:12
impeller::testing::GoldenDigest::Instance
static GoldenDigest * Instance()
Definition: golden_digest.cc:18
impeller::testing::GoldenDigest::AddDimension
void AddDimension(const std::string &name, const std::string &value)
Definition: golden_digest.cc:27
impeller::testing::GoldenDigest::AddImage
void AddImage(const std::string &test_name, const std::string &filename, int32_t width, int32_t height)
Definition: golden_digest.cc:34
impeller::testing::WorkingDirectory::GetFilenamePath
std::string GetFilenamePath(const std::string &filename) const
Definition: working_directory.cc:23
impeller::testing::WorkingDirectory::Instance
static WorkingDirectory * Instance()
Definition: working_directory.cc:16
dl_dispatcher.h
dl_image_impeller.h
golden_digest.h
golden_playground_test.h
IMP_AIKSTEST
#define IMP_AIKSTEST(name)
Definition: golden_playground_test_mac.cc:62
metal_screenshotter.h
impeller
Definition: allocation.cc:12
impeller::kSkipTests
static const std::vector< std::string > kSkipTests
Definition: golden_playground_test_mac.cc:69
impeller::Scalar
float Scalar
Definition: scalar.h:19
impeller::DisplayListToTexture
std::shared_ptr< Texture > DisplayListToTexture(const sk_sp< flutter::DisplayList > &display_list, ISize size, AiksContext &context, bool reset_host_buffer, bool generate_mips, std::optional< PixelFormat > target_pixel_format)
Render the provided display list to a texture with the given size.
Definition: dl_dispatcher.cc:1220
impeller::PlaygroundBackend
PlaygroundBackend
Definition: playground.h:27
impeller::PlaygroundBackend::kOpenGLES
@ kOpenGLES
impeller::PlaygroundBackend::kMetal
@ kMetal
impeller::PlaygroundBackend::kVulkan
@ kVulkan
impeller::Flags::antialiased_lines
bool antialiased_lines
When turned on DrawLine will use the experimental antialiased path.
Definition: flags.h:14
impeller::GoldenPlaygroundTest::GoldenPlaygroundTestImpl
Definition: golden_playground_test_mac.cc:115
impeller::GoldenPlaygroundTest::GoldenPlaygroundTestImpl::test_vulkan_playground
std::unique_ptr< PlaygroundImpl > test_vulkan_playground
Definition: golden_playground_test_mac.cc:116
impeller::GoldenPlaygroundTest::GoldenPlaygroundTestImpl::screenshotter
std::unique_ptr< testing::Screenshotter > screenshotter
Definition: golden_playground_test_mac.cc:118
impeller::GoldenPlaygroundTest::GoldenPlaygroundTestImpl::test_opengl_playground
std::unique_ptr< PlaygroundImpl > test_opengl_playground
Definition: golden_playground_test_mac.cc:117
impeller::GoldenPlaygroundTest::GoldenPlaygroundTestImpl::window_size
ISize window_size
Definition: golden_playground_test_mac.cc:119
impeller::PlaygroundSwitches
Definition: switches.h:16
impeller::PlaygroundSwitches::use_angle
bool use_angle
Definition: switches.h:34
impeller::PlaygroundSwitches::flags
Flags flags
Definition: switches.h:38
impeller::PlaygroundSwitches::enable_wide_gamut
bool enable_wide_gamut
Definition: switches.h:36
impeller::TPoint< Scalar >
impeller::TPoint::y
Type y
Definition: point.h:31
impeller::TPoint::x
Type x
Definition: point.h:30
impeller::TSize
Definition: size.h:25
typographer_context.h
typographer_context_skia.h
vulkan_screenshotter.h