72 {
73
74
76
77 "impeller_Play_AiksTest_CanRenderClippedRuntimeEffects_Vulkan",
78};
79
80namespace {
81std::string GetTestName() {
82 std::string suite_name =
83 ::testing::UnitTest::GetInstance()->current_test_suite()->name();
84 std::string test_name =
85 ::testing::UnitTest::GetInstance()->current_test_info()->name();
86 std::stringstream ss;
87 ss << "impeller_" << suite_name << "_" << test_name;
88 std::string result = ss.str();
89
90 std::replace(result.begin(), result.end(), '/', '_');
91 return result;
92}
93
94std::string GetGoldenFilename(const std::string& postfix) {
95 return GetTestName() + postfix + ".png";
96}
97}
98
99bool GoldenPlaygroundTest::SaveScreenshot(
100 std::unique_ptr<testing::Screenshot> screenshot,
101 const std::string& postfix) {
102 if (!screenshot || !screenshot->GetBytes()) {
103 FML_LOG(ERROR) <<
"Failed to collect screenshot for test " << GetTestName();
104 return false;
105 }
106 std::string test_name = GetTestName();
107 std::string filename = GetGoldenFilename(postfix);
108 testing::GoldenDigest::Instance()->AddImage(
109 test_name, filename, screenshot->GetWidth(), screenshot->GetHeight());
110 if (!screenshot->WriteToPNG(
111 testing::WorkingDirectory::Instance()->GetFilenamePath(filename))) {
112 FML_LOG(ERROR) <<
"Failed to write screenshot to " << filename;
113 return false;
114 }
115 return true;
116}
117
118struct GoldenPlaygroundTest::GoldenPlaygroundTestImpl {
119 std::unique_ptr<PlaygroundImpl> test_vulkan_playground;
120 std::unique_ptr<PlaygroundImpl> test_opengl_playground;
121 std::unique_ptr<testing::Screenshotter> screenshotter;
123};
124
125GoldenPlaygroundTest::GoldenPlaygroundTest()
126 : typographer_context_(TypographerContextSkia::Make()),
127 pimpl_(new GoldenPlaygroundTest::GoldenPlaygroundTestImpl()) {}
128
129GoldenPlaygroundTest::~GoldenPlaygroundTest() = default;
130
131void GoldenPlaygroundTest::SetTypographerContext(
132 std::shared_ptr<TypographerContext> typographer_context) {
133 typographer_context_ = std::move(typographer_context);
134};
135
136void GoldenPlaygroundTest::TearDown() {
137 ASSERT_FALSE(dlopen("/usr/local/lib/libMoltenVK.dylib", RTLD_NOLOAD));
138
139 auto context = GetContext();
140 if (context) {
141 context->DisposeThreadLocalCachedResources();
142 }
143}
144
145namespace {
146bool DoesSupportWideGamutTests() {
147#ifdef __arm64__
148 return true;
149#else
150 return false;
151#endif
152}
153}
154
155void GoldenPlaygroundTest::SetUp() {
156 std::filesystem::path testing_assets_path =
158 std::filesystem::path target_path = testing_assets_path.parent_path()
159 .parent_path()
160 .parent_path()
161 .parent_path();
162 std::filesystem::path icd_path = target_path / "vk_swiftshader_icd.json";
163 setenv("VK_ICD_FILENAMES", icd_path.c_str(), 1);
164
165 std::string test_name = GetTestName();
166 PlaygroundSwitches switches;
167 switches.enable_wide_gamut =
168 test_name.find("WideGamut_") != std::string::npos;
169 switches.flags.antialiased_lines =
170 test_name.find("ExperimentAntialiasLines_") != std::string::npos;
171 switch (GetParam()) {
172 case PlaygroundBackend::kMetalSDF:
173 switches.flags.use_sdfs = true;
174 [[fallthrough]];
175 case PlaygroundBackend::kMetal:
176 if (!DoesSupportWideGamutTests()) {
177 GTEST_SKIP()
178 << "This metal device doesn't support wide gamut golden tests.";
179 }
180 pimpl_->screenshotter =
181 std::make_unique<testing::MetalScreenshotter>(switches);
182 break;
183 case PlaygroundBackend::kVulkan: {
184 if (switches.enable_wide_gamut) {
185 GTEST_SKIP() << "Vulkan doesn't support wide gamut golden tests.";
186 }
187 if (switches.flags.antialiased_lines) {
188 GTEST_SKIP()
189 << "Vulkan doesn't support antialiased lines golden tests.";
190 }
191 const std::unique_ptr<PlaygroundImpl>& playground =
192 GetSharedVulkanPlayground(true);
193 pimpl_->screenshotter =
194 std::make_unique<testing::VulkanScreenshotter>(playground);
195 break;
196 }
197 case PlaygroundBackend::kOpenGLES: {
198 if (switches.enable_wide_gamut) {
199 GTEST_SKIP() << "OpenGLES doesn't support wide gamut golden tests.";
200 }
201 if (switches.flags.antialiased_lines) {
202 GTEST_SKIP()
203 << "OpenGLES doesn't support antialiased lines golden tests.";
204 }
206 PlaygroundSwitches playground_switches;
207 playground_switches.use_angle = true;
208 pimpl_->test_opengl_playground = PlaygroundImpl::Create(
209 PlaygroundBackend::kOpenGLES, playground_switches);
210 pimpl_->screenshotter = std::make_unique<testing::VulkanScreenshotter>(
211 pimpl_->test_opengl_playground);
212 break;
213 }
214 }
215
218 GTEST_SKIP()
219 << "GoldenPlaygroundTest doesn't support interactive playground tests "
220 "yet.";
221 }
222
223 testing::GoldenDigest::Instance()->AddDimension(
224 "gpu_string", GetContext()->DescribeGpuModel());
225}
226
228 return GetParam();
229}
230
231bool GoldenPlaygroundTest::OpenPlaygroundHere(
232 const AiksDlPlaygroundCallback&
callback) {
233 AiksContext renderer(GetContext(), typographer_context_);
234
235 std::unique_ptr<testing::Screenshot> screenshot;
236 Point content_scale =
237 pimpl_->screenshotter->GetPlayground().GetContentScale();
238
239 ISize physical_window_size(
240 std::round(pimpl_->window_size.width * content_scale.x),
241 std::round(pimpl_->window_size.height * content_scale.y));
242 for (
int i = 0;
i < 2; ++
i) {
246 screenshot = pimpl_->screenshotter->MakeScreenshot(renderer,
texture);
247 }
248 return SaveScreenshot(std::move(screenshot));
249}
250
251bool GoldenPlaygroundTest::OpenPlaygroundHere(
252 const sk_sp<flutter::DisplayList>& list) {
253 return OpenPlaygroundHere([&list]() { return list; });
254}
255
256bool GoldenPlaygroundTest::OpenPlaygroundHere(
257 const Playground::SinglePassCallback&
callback) {
258 AiksContext renderer(GetContext(), typographer_context_);
259 std::shared_ptr<Context> context = GetContext();
260 Point content_scale =
261 pimpl_->screenshotter->GetPlayground().GetContentScale();
262 ISize size(std::round(pimpl_->window_size.width * content_scale.x),
263 std::round(pimpl_->window_size.height * content_scale.y));
264
265 std::unique_ptr<testing::Screenshot> screenshot;
266
267
268 for (
int i = 0;
i < 2; ++
i) {
269 RenderTargetAllocator render_target_allocator(
270 context->GetResourceAllocator());
271 RenderTarget render_target = render_target_allocator.CreateOffscreen(
272 *context, size, 1, "Golden Render Pass",
273 RenderTarget::kDefaultColorAttachmentConfig,
274 std::nullopt);
275 if (!render_target.IsValid()) {
276 return false;
277 }
278 std::shared_ptr<CommandBuffer> command_buffer =
279 context->CreateCommandBuffer();
280 if (!command_buffer) {
281 return false;
282 }
283 std::shared_ptr<RenderPass> render_pass =
284 command_buffer->CreateRenderPass(render_target);
285 if (!render_pass) {
286 return false;
287 }
289 return false;
290 }
291 if (!render_pass->EncodeCommands()) {
292 return false;
293 }
294 if (!context->GetCommandQueue()->Submit({command_buffer}).ok()) {
295 return false;
296 }
297 screenshot = pimpl_->screenshotter->MakeScreenshot(
298 renderer, render_target.GetRenderTargetTexture());
299 }
300 return SaveScreenshot(std::move(screenshot));
301}
302
303bool GoldenPlaygroundTest::ImGuiBegin(
const char*
name,
304 bool* p_open,
305 ImGuiWindowFlags flags) {
306 return false;
307}
308
309std::shared_ptr<Texture> GoldenPlaygroundTest::CreateTextureForFixture(
310 const char* fixture_name,
311 bool enable_mipmapping) const {
312 std::shared_ptr<fml::Mapping> mapping =
314 auto result = Playground::CreateTextureForMapping(GetContext(), mapping,
315 enable_mipmapping);
316 if (result) {
317 result->SetLabel(fixture_name);
318 }
319 return result;
320}
321
322sk_sp<flutter::DlImage> GoldenPlaygroundTest::CreateDlImageForFixture(
323 const char* fixture_name,
324 bool enable_mipmapping) const {
325 std::shared_ptr<Texture>
texture =
326 CreateTextureForFixture(fixture_name, enable_mipmapping);
327 return DlImageImpeller::Make(
texture);
328}
329
330absl::StatusOr<RuntimeStage::Map> GoldenPlaygroundTest::OpenAssetAsRuntimeStage(
331 const char* asset_name) const {
332 const std::shared_ptr<fml::Mapping> fixture =
334 if (!fixture || fixture->GetSize() == 0) {
335 return absl::NotFoundError("Asset not found or empty.");
336 }
337 return RuntimeStage::DecodeRuntimeStages(fixture);
338}
339
340std::shared_ptr<Context> GoldenPlaygroundTest::GetContext() const {
341 if (!pimpl_->screenshotter) {
342 return nullptr;
343 }
344 return pimpl_->screenshotter->GetPlayground().GetContext();
345}
346
347std::shared_ptr<Context> GoldenPlaygroundTest::MakeContext() const {
348 if (GetParam() == PlaygroundBackend::kMetal) {
349
350 return GetContext();
351 } else if (GetParam() == PlaygroundBackend::kVulkan) {
352 bool enable_vulkan_validations = true;
353 FML_CHECK(!pimpl_->test_vulkan_playground)
354 << "We don't support creating multiple contexts for one test";
355 pimpl_->test_vulkan_playground =
356 MakeVulkanPlayground(enable_vulkan_validations);
357 pimpl_->screenshotter = std::make_unique<testing::VulkanScreenshotter>(
358 pimpl_->test_vulkan_playground);
359 return pimpl_->test_vulkan_playground->GetContext();
360 } else {
361
362 return GetContext();
363 }
364}
365
366Point GoldenPlaygroundTest::GetContentScale()
const {
367 return pimpl_->screenshotter->GetPlayground().GetContentScale();
368}
369
370Scalar GoldenPlaygroundTest::GetSecondsElapsed()
const {
371 return 0.0f;
372}
373
374ISize GoldenPlaygroundTest::GetWindowSize()
const {
375 return pimpl_->window_size;
376}
377
378IRect GoldenPlaygroundTest::GetWindowBounds()
const {
379 return IRect::MakeSize(pimpl_->window_size);
380}
381
382void GoldenPlaygroundTest::GoldenPlaygroundTest::SetWindowSize(ISize size) {
383 pimpl_->window_size =
size;
384}
385
387 const std::shared_ptr<Capabilities>& capabilities) {
388 return pimpl_->screenshotter->GetPlayground().SetCapabilities(capabilities);
389}
390
391std::unique_ptr<testing::Screenshot> GoldenPlaygroundTest::MakeScreenshot(
392 const sk_sp<flutter::DisplayList>& list) {
393 AiksContext renderer(GetContext(), typographer_context_);
394 Point content_scale =
395 pimpl_->screenshotter->GetPlayground().GetContentScale();
396
397 ISize physical_window_size(
398 std::round(pimpl_->window_size.width * content_scale.x),
399 std::round(pimpl_->window_size.height * content_scale.y));
400 return pimpl_->screenshotter->MakeScreenshot(
402}
403
405 return pimpl_->screenshotter->GetPlayground().GetRuntimeStageBackend();
406}
407
408}
FlutterDesktopBinaryReply callback
#define FML_LOG(severity)
#define FML_CHECK(condition)
#define IMP_AIKSTEST(name)
const char * GetTestingAssetsPath()
Returns the directory containing assets shared across all tests.
std::unique_ptr< fml::Mapping > OpenFixtureAsMapping(const std::string &fixture_name)
Opens a fixture of the given file name and returns a mapping to its contents.
it will be possible to load the file into Perfetto s trace viewer use test Running tests that layout and measure text will not yield consistent results across various platforms Enabling this option will make font resolution default to the Ahem test font on all disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
static const std::vector< std::string > kSkipTests
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.