Flutter Engine
 
Loading...
Searching...
No Matches
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
11
15#include "fml/closure.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
26namespace impeller {
27
28namespace {
29std::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;
34 playground_switches);
35}
36
37// Returns a static instance to a playground that can be used across tests.
38const 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.
69static 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
77namespace {
78std::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
91std::string GetGoldenFilename(const std::string& postfix) {
92 return GetTestName() + postfix + ".png";
93}
94} // namespace
95
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);
106 test_name, filename, screenshot->GetWidth(), screenshot->GetHeight());
107 if (!screenshot->WriteToPNG(
109 FML_LOG(ERROR) << "Failed to write screenshot to " << filename;
110 return false;
111 }
112 return true;
113}
114
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
125
127
129 std::shared_ptr<TypographerContext> typographer_context) {
130 typographer_context_ = std::move(typographer_context);
131};
132
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
142namespace {
143bool DoesSupportWideGamutTests() {
144#ifdef __arm64__
145 return true;
146#else
147 return false;
148#endif
149}
150} // namespace
151
153 std::filesystem::path testing_assets_path =
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()) {
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;
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 }
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
218 "gpu_string", GetContext()->DescribeGpuModel());
219}
220
222 return GetParam();
223}
224
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
246 const sk_sp<flutter::DisplayList>& list) {
247 return OpenPlaygroundHere([&list]() { return list; });
248}
249
251 bool* p_open,
252 ImGuiWindowFlags flags) {
253 return false;
254}
255
257 const char* fixture_name,
258 bool enable_mipmapping) const {
259 std::shared_ptr<fml::Mapping> mapping =
261 auto result = Playground::CreateTextureForMapping(GetContext(), mapping,
262 enable_mipmapping);
263 if (result) {
264 result->SetLabel(fixture_name);
265 }
266 return result;
267}
268
270 const char* fixture_name,
271 bool enable_mipmapping) const {
272 std::shared_ptr<Texture> texture =
273 CreateTextureForFixture(fixture_name, enable_mipmapping);
275}
276
278 const char* asset_name) const {
279 const std::shared_ptr<fml::Mapping> fixture =
281 if (!fixture || fixture->GetSize() == 0) {
282 return {};
283 }
284 return RuntimeStage::DecodeRuntimeStages(fixture);
285}
286
287std::shared_ptr<Context> GoldenPlaygroundTest::GetContext() const {
288 if (!pimpl_->screenshotter) {
289 return nullptr;
290 }
291 return pimpl_->screenshotter->GetPlayground().GetContext();
292}
293
294std::shared_ptr<Context> GoldenPlaygroundTest::MakeContext() const {
295 if (GetParam() == PlaygroundBackend::kMetal) {
296 /// On Metal we create a context for each test.
297 return GetContext();
298 } else if (GetParam() == PlaygroundBackend::kVulkan) {
299 bool enable_vulkan_validations = true;
300 FML_CHECK(!pimpl_->test_vulkan_playground)
301 << "We don't support creating multiple contexts for one test";
302 pimpl_->test_vulkan_playground =
303 MakeVulkanPlayground(enable_vulkan_validations);
304 pimpl_->screenshotter = std::make_unique<testing::VulkanScreenshotter>(
305 pimpl_->test_vulkan_playground);
306 return pimpl_->test_vulkan_playground->GetContext();
307 } else {
308 /// On OpenGL we create a context for each test.
309 return GetContext();
310 }
311}
312
314 return pimpl_->screenshotter->GetPlayground().GetContentScale();
315}
316
318 return 0.0f;
319}
320
322 return pimpl_->window_size;
323}
324
325void GoldenPlaygroundTest::GoldenPlaygroundTest::SetWindowSize(ISize size) {
326 pimpl_->window_size = size;
327}
328
330 const std::shared_ptr<Capabilities>& capabilities) {
331 return pimpl_->screenshotter->GetPlayground().SetCapabilities(capabilities);
332}
333
334std::unique_ptr<testing::Screenshot> GoldenPlaygroundTest::MakeScreenshot(
335 const sk_sp<flutter::DisplayList>& list) {
336 AiksContext renderer(GetContext(), typographer_context_);
337 Point content_scale =
338 pimpl_->screenshotter->GetPlayground().GetContentScale();
339
340 ISize physical_window_size(
341 std::round(pimpl_->window_size.width * content_scale.x),
342 std::round(pimpl_->window_size.height * content_scale.y));
343 return pimpl_->screenshotter->MakeScreenshot(
344 renderer, DisplayListToTexture(list, physical_window_size, renderer));
345}
346
347} // namespace impeller
Wraps a closure that is invoked in the destructor unless released by the caller.
Definition closure.h:32
static sk_sp< DlImageImpeller > Make(std::shared_ptr< Texture > texture, OwningContext owning_context=OwningContext::kIO)
sk_sp< flutter::DlImage > CreateDlImageForFixture(const char *fixture_name, bool enable_mipmapping=false) const
fml::Status SetCapabilities(const std::shared_ptr< Capabilities > &capabilities)
void SetTypographerContext(std::shared_ptr< TypographerContext > typographer_context)
std::shared_ptr< Context > MakeContext() const
static bool SaveScreenshot(std::unique_ptr< testing::Screenshot > screenshot, const std::string &postfix="")
std::unique_ptr< testing::Screenshot > MakeScreenshot(const sk_sp< flutter::DisplayList > &list)
RuntimeStage::Map OpenAssetAsRuntimeStage(const char *asset_name) const
bool OpenPlaygroundHere(Picture picture)
static bool ImGuiBegin(const char *name, bool *p_open, ImGuiWindowFlags flags)
std::function< sk_sp< flutter::DisplayList >()> AiksDlPlaygroundCallback
std::shared_ptr< Context > GetContext() const
std::shared_ptr< Texture > CreateTextureForFixture(const char *fixture_name, bool enable_mipmapping=false) const
static std::shared_ptr< Texture > CreateTextureForMapping(const std::shared_ptr< Context > &context, std::shared_ptr< fml::Mapping > mapping, bool enable_mipmapping=false)
static std::unique_ptr< PlaygroundImpl > Create(PlaygroundBackend backend, PlaygroundSwitches switches)
std::map< RuntimeStageBackend, std::shared_ptr< RuntimeStage > > Map
static Map DecodeRuntimeStages(const std::shared_ptr< fml::Mapping > &payload)
static GoldenDigest * Instance()
void AddDimension(const std::string &name, const std::string &value)
void AddImage(const std::string &test_name, const std::string &filename, int32_t width, int32_t height)
std::string GetFilenamePath(const std::string &filename) const
static WorkingDirectory * Instance()
#define GLFW_TRUE
FlutterDesktopBinaryReply callback
#define FML_LOG(severity)
Definition logging.h:101
#define FML_CHECK(condition)
Definition logging.h:104
const char * name
Definition fuchsia.cc:49
#define IMP_AIKSTEST(name)
FlTexture * texture
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.
Definition testing.cc:58
std::shared_ptr< Texture > DisplayListToTexture(const sk_sp< flutter::DisplayList > &display_list, ISize size, AiksContext &context, bool reset_host_buffer, bool generate_mips)
Render the provided display list to a texture with the given size.
static const std::vector< std::string > kSkipTests
float Scalar
Definition scalar.h:19
PlaygroundBackend
Definition playground.h:27
bool antialiased_lines
When turned on DrawLine will use the experimental antialiased path.
Definition flags.h:14