Flutter Engine
The 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
9#include "flutter/impeller/golden_tests/golden_playground_test.h"
10
11#include "flutter/impeller/aiks/picture.h"
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 "flutter/third_party/abseil-cpp/absl/base/no_destructor.h"
18
19#define GLFW_INCLUDE_NONE
20#include "third_party/glfw/include/GLFW/glfw3.h"
21
22namespace impeller {
23
24namespace {
25std::unique_ptr<PlaygroundImpl> MakeVulkanPlayground(bool enable_validations) {
26 FML_CHECK(::glfwInit() == GLFW_TRUE);
27 PlaygroundSwitches playground_switches;
28 playground_switches.enable_vulkan_validation = enable_validations;
30 playground_switches);
31}
32
33// Returns a static instance to a playground that can be used across tests.
34const std::unique_ptr<PlaygroundImpl>& GetSharedVulkanPlayground(
35 bool enable_validations) {
36 if (enable_validations) {
37 static absl::NoDestructor<std::unique_ptr<PlaygroundImpl>>
38 vulkan_validation_playground(
39 MakeVulkanPlayground(/*enable_validations=*/true));
40 // TODO(https://github.com/flutter/flutter/issues/142237): This can be
41 // removed when the thread local storage is removed.
42 static fml::ScopedCleanupClosure context_cleanup(
43 [&] { (*vulkan_validation_playground)->GetContext()->Shutdown(); });
44 return *vulkan_validation_playground;
45 } else {
46 static absl::NoDestructor<std::unique_ptr<PlaygroundImpl>>
47 vulkan_playground(MakeVulkanPlayground(/*enable_validations=*/false));
48 // TODO(https://github.com/flutter/flutter/issues/142237): This can be
49 // removed when the thread local storage is removed.
50 static fml::ScopedCleanupClosure context_cleanup(
51 [&] { (*vulkan_playground)->GetContext()->Shutdown(); });
52 return *vulkan_playground;
53 }
54}
55} // namespace
56
57#define IMP_AIKSTEST(name) \
58 "impeller_Play_AiksTest_" #name "_Metal", \
59 "impeller_Play_AiksTest_" #name "_OpenGLES", \
60 "impeller_Play_AiksTest_" #name "_Vulkan"
61
62// If you add a new playground test to the aiks unittests and you do not want it
63// to also be a golden test, then add the test name here.
64static const std::vector<std::string> kSkipTests = {
65 // TextRotated is flakey and we can't seem to get it to stabilize on Skia
66 // Gold.
67 IMP_AIKSTEST(TextRotated),
68 // Runtime stage based tests get confused with a Metal context.
69 "impeller_Play_AiksTest_CanRenderClippedRuntimeEffects_Vulkan",
70};
71
72namespace {
73std::string GetTestName() {
74 std::string suite_name =
75 ::testing::UnitTest::GetInstance()->current_test_suite()->name();
76 std::string test_name =
77 ::testing::UnitTest::GetInstance()->current_test_info()->name();
78 std::stringstream ss;
79 ss << "impeller_" << suite_name << "_" << test_name;
80 std::string result = ss.str();
81 // Make sure there are no slashes in the test name.
82 std::replace(result.begin(), result.end(), '/', '_');
83 return result;
84}
85
86std::string GetGoldenFilename() {
87 return GetTestName() + ".png";
88}
89
90bool SaveScreenshot(std::unique_ptr<testing::Screenshot> screenshot) {
91 if (!screenshot || !screenshot->GetBytes()) {
92 FML_LOG(ERROR) << "Failed to collect screenshot for test " << GetTestName();
93 return false;
94 }
95 std::string test_name = GetTestName();
96 std::string filename = GetGoldenFilename();
98 test_name, filename, screenshot->GetWidth(), screenshot->GetHeight());
99 if (!screenshot->WriteToPNG(
101 FML_LOG(ERROR) << "Failed to write screenshot to " << filename;
102 return false;
103 }
104 return true;
105}
106
107} // namespace
108
110 std::unique_ptr<PlaygroundImpl> test_vulkan_playground;
111 std::unique_ptr<PlaygroundImpl> test_opengl_playground;
112 std::unique_ptr<testing::Screenshotter> screenshotter;
113 ISize window_size = ISize{1024, 768};
114};
115
119
121
123 std::shared_ptr<TypographerContext> typographer_context) {
124 typographer_context_ = std::move(typographer_context);
125};
126
128 ASSERT_FALSE(dlopen("/usr/local/lib/libMoltenVK.dylib", RTLD_NOLOAD));
129}
130
131namespace {
132bool DoesSupportWideGamutTests() {
133#ifdef __arm64__
134 return true;
135#else
136 return false;
137#endif
138}
139} // namespace
140
142 std::filesystem::path testing_assets_path =
144 std::filesystem::path target_path = testing_assets_path.parent_path()
145 .parent_path()
146 .parent_path()
147 .parent_path();
148 std::filesystem::path icd_path = target_path / "vk_swiftshader_icd.json";
149 setenv("VK_ICD_FILENAMES", icd_path.c_str(), 1);
150
151 std::string test_name = GetTestName();
152 bool enable_wide_gamut = test_name.find("WideGamut_") != std::string::npos;
153 switch (GetParam()) {
155 if (!DoesSupportWideGamutTests()) {
156 GTEST_SKIP_(
157 "This metal device doesn't support wide gamut golden tests.");
158 }
159 pimpl_->screenshotter =
160 std::make_unique<testing::MetalScreenshotter>(enable_wide_gamut);
161 break;
163 if (enable_wide_gamut) {
164 GTEST_SKIP_("Vulkan doesn't support wide gamut golden tests.");
165 }
166 const std::unique_ptr<PlaygroundImpl>& playground =
167 GetSharedVulkanPlayground(/*enable_validations=*/true);
168 pimpl_->screenshotter =
169 std::make_unique<testing::VulkanScreenshotter>(playground);
170 break;
171 }
173 if (enable_wide_gamut) {
174 GTEST_SKIP_("OpenGLES doesn't support wide gamut golden tests.");
175 }
176 FML_CHECK(::glfwInit() == GLFW_TRUE);
177 PlaygroundSwitches playground_switches;
178 playground_switches.use_angle = true;
179 pimpl_->test_opengl_playground = PlaygroundImpl::Create(
180 PlaygroundBackend::kOpenGLES, playground_switches);
181 pimpl_->screenshotter = std::make_unique<testing::VulkanScreenshotter>(
182 pimpl_->test_opengl_playground);
183 break;
184 }
185 }
186
187 if (std::find(kSkipTests.begin(), kSkipTests.end(), test_name) !=
188 kSkipTests.end()) {
189 GTEST_SKIP_(
190 "GoldenPlaygroundTest doesn't support interactive playground tests "
191 "yet.");
192 }
193
195 "gpu_string", GetContext()->DescribeGpuModel());
196}
197
199 return GetParam();
200}
201
203 AiksContext renderer(GetContext(), typographer_context_);
204
205 auto screenshot = pimpl_->screenshotter->MakeScreenshot(renderer, picture,
206 pimpl_->window_size);
207 return SaveScreenshot(std::move(screenshot));
208}
209
212 callback) { // NOLINT(performance-unnecessary-value-param)
213 AiksContext renderer(GetContext(), typographer_context_);
214
215 std::optional<Picture> picture;
216 std::unique_ptr<testing::Screenshot> screenshot;
217 for (int i = 0; i < 2; ++i) {
218 picture = callback(renderer);
219 if (!picture.has_value()) {
220 return false;
221 }
222 screenshot = pimpl_->screenshotter->MakeScreenshot(
223 renderer, picture.value(), pimpl_->window_size);
224 }
225
226 return SaveScreenshot(std::move(screenshot));
227}
228
230 bool* p_open,
231 ImGuiWindowFlags flags) {
232 return false;
233}
234
236 const char* fixture_name,
237 bool enable_mipmapping) const {
238 std::shared_ptr<fml::Mapping> mapping =
241 enable_mipmapping);
242 if (result) {
243 result->SetLabel(fixture_name);
244 }
245 return result;
246}
247
249 const char* asset_name) const {
250 const std::shared_ptr<fml::Mapping> fixture =
252 if (!fixture || fixture->GetSize() == 0) {
253 return {};
254 }
255 return RuntimeStage::DecodeRuntimeStages(fixture);
256}
257
258std::shared_ptr<Context> GoldenPlaygroundTest::GetContext() const {
259 return pimpl_->screenshotter->GetPlayground().GetContext();
260}
261
262std::shared_ptr<Context> GoldenPlaygroundTest::MakeContext() const {
263 if (GetParam() == PlaygroundBackend::kMetal) {
264 /// On Metal we create a context for each test.
265 return GetContext();
266 } else if (GetParam() == PlaygroundBackend::kVulkan) {
267 bool enable_vulkan_validations = true;
268 FML_CHECK(!pimpl_->test_vulkan_playground)
269 << "We don't support creating multiple contexts for one test";
270 pimpl_->test_vulkan_playground =
271 MakeVulkanPlayground(enable_vulkan_validations);
272 pimpl_->screenshotter = std::make_unique<testing::VulkanScreenshotter>(
273 pimpl_->test_vulkan_playground);
274 return pimpl_->test_vulkan_playground->GetContext();
275 } else {
276 /// On OpenGL we create a context for each test.
277 return GetContext();
278 }
279}
280
282 return pimpl_->screenshotter->GetPlayground().GetContentScale();
283}
284
286 return 0.0f;
287}
288
290 return pimpl_->window_size;
291}
292
293void GoldenPlaygroundTest::GoldenPlaygroundTest::SetWindowSize(ISize size) {
294 pimpl_->window_size = size;
295}
296
298 const std::shared_ptr<Capabilities>& capabilities) {
299 return pimpl_->screenshotter->GetPlayground().SetCapabilities(capabilities);
300}
301
302} // namespace impeller
static std::unique_ptr< SkEncoder > Make(SkWStream *dst, const SkPixmap *src, const SkYUVAPixmaps *srcYUVA, const SkColorSpace *srcYUVAColorSpace, const SkJpegEncoder::Options &options)
Wraps a closure that is invoked in the destructor unless released by the caller.
Definition closure.h:32
std::function< std::optional< Picture >(AiksContext &renderer)> AiksPlaygroundCallback
fml::Status SetCapabilities(const std::shared_ptr< Capabilities > &capabilities)
void SetTypographerContext(std::shared_ptr< TypographerContext > typographer_context)
std::shared_ptr< Context > MakeContext() const
RuntimeStage::Map OpenAssetAsRuntimeStage(const char *asset_name) const
static bool ImGuiBegin(const char *name, bool *p_open, ImGuiWindowFlags flags)
std::shared_ptr< Context > GetContext() const
std::shared_ptr< Texture > CreateTextureForFixture(const char *fixture_name, bool enable_mipmapping=false) const
static std::unique_ptr< PlaygroundImpl > Create(PlaygroundBackend backend, PlaygroundSwitches switches)
static std::shared_ptr< Texture > CreateTextureForMapping(const std::shared_ptr< Context > &context, std::shared_ptr< fml::Mapping > mapping, bool enable_mipmapping=false)
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()
FlutterSemanticsFlag flags
FlKeyEvent uint64_t FlKeyResponderAsyncCallback callback
GAsyncResult * result
#define GLFW_TRUE
#define FML_LOG(severity)
Definition logging.h:82
#define FML_CHECK(condition)
Definition logging.h:85
const char * name
Definition fuchsia.cc:50
#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.
Definition testing.cc:59
static const std::vector< std::string > kSkipTests
float Scalar
Definition scalar.h:18
PlaygroundBackend
Definition playground.h:29
#define ERROR(message)