| 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 "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"
16 #include "impeller/typographer/backends/skia/typographer_context_skia.h"
17 #include "impeller/typographer/typographer_context.h"
18
19 #define GLFW_INCLUDE_NONE
20 #include "third_party/glfw/include/GLFW/glfw3.h"
21
22 namespace impeller {
23
24 namespace {
25 std::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;
29 return PlaygroundImpl::Create(PlaygroundBackend::kVulkan,
30 playground_switches);
31 }
32
33 // Returns a static instance to a playground that can be used across tests.
34 const 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.
64 static 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
72 namespace {
73 std::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
86 std::string GetGoldenFilename() {
87 return GetTestName() + ".png";
88 }
89
90 bool 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();
97 testing::GoldenDigest::Instance()->AddImage(
98 test_name, filename, screenshot->GetWidth(), screenshot->GetHeight());
99 if (!screenshot->WriteToPNG(
100 testing::WorkingDirectory::Instance()->GetFilenamePath(filename))) {
101 FML_LOG(ERROR) << "Failed to write screenshot to " << filename;
102 return false;
103 }
104 return true;
105 }
106
107 } // namespace
108
109 struct GoldenPlaygroundTest::GoldenPlaygroundTestImpl {
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
116 GoldenPlaygroundTest::GoldenPlaygroundTest()
117 : typographer_context_(TypographerContextSkia::Make()),
118 pimpl_(new GoldenPlaygroundTest::GoldenPlaygroundTestImpl()) {}
119
120 GoldenPlaygroundTest::~GoldenPlaygroundTest() = default;
121
122 void GoldenPlaygroundTest::SetTypographerContext(
123 std::shared_ptr<TypographerContext> typographer_context) {
124 typographer_context_ = std::move(typographer_context);
125 };
126
127 void GoldenPlaygroundTest::TearDown() {
128 ASSERT_FALSE(dlopen("/usr/local/lib/libMoltenVK.dylib", RTLD_NOLOAD));
129 }
130
131 void GoldenPlaygroundTest::SetUp() {
132 std::filesystem::path testing_assets_path =
133 flutter::testing::GetTestingAssetsPath();
134 std::filesystem::path target_path = testing_assets_path.parent_path()
135 .parent_path()
136 .parent_path()
137 .parent_path();
138 std::filesystem::path icd_path = target_path / "vk_swiftshader_icd.json";
139 setenv("VK_ICD_FILENAMES", icd_path.c_str(), 1);
140
141 switch (GetParam()) {
142 case PlaygroundBackend::kMetal:
143 pimpl_->screenshotter = std::make_unique<testing::MetalScreenshotter>();
144 break;
145 case PlaygroundBackend::kVulkan: {
146 const std::unique_ptr<PlaygroundImpl>& playground =
147 GetSharedVulkanPlayground(/*enable_validations=*/true);
148 pimpl_->screenshotter =
149 std::make_unique<testing::VulkanScreenshotter>(playground);
150 break;
151 }
152 case PlaygroundBackend::kOpenGLES: {
153 FML_CHECK(::glfwInit() == GLFW_TRUE);
154 PlaygroundSwitches playground_switches;
155 playground_switches.use_angle = true;
156 pimpl_->test_opengl_playground = PlaygroundImpl::Create(
157 PlaygroundBackend::kOpenGLES, playground_switches);
158 pimpl_->screenshotter = std::make_unique<testing::VulkanScreenshotter>(
159 pimpl_->test_opengl_playground);
160 break;
161 }
162 }
163 if (GetParam() == PlaygroundBackend::kMetal) {
164 pimpl_->screenshotter = std::make_unique<testing::MetalScreenshotter>();
165 } else if (GetParam() == PlaygroundBackend::kVulkan) {
166 const std::unique_ptr<PlaygroundImpl>& playground =
167 GetSharedVulkanPlayground(/*enable_validations=*/true);
168 pimpl_->screenshotter =
169 std::make_unique<testing::VulkanScreenshotter>(playground);
170 }
171
172 std::string test_name = GetTestName();
173 if (std::find(kSkipTests.begin(), kSkipTests.end(), test_name) !=
174 kSkipTests.end()) {
175 GTEST_SKIP_(
176 "GoldenPlaygroundTest doesn't support interactive playground tests "
177 "yet.");
178 }
179
180 testing::GoldenDigest::Instance()->AddDimension(
181 "gpu_string", GetContext()->DescribeGpuModel());
182 }
183
184 PlaygroundBackend GoldenPlaygroundTest::GetBackend() const {
185 return GetParam();
186 }
187
188 bool GoldenPlaygroundTest::OpenPlaygroundHere(Picture picture) {
189 AiksContext renderer(GetContext(), typographer_context_);
190
191 auto screenshot = pimpl_->screenshotter->MakeScreenshot(renderer, picture,
192 pimpl_->window_size);
193 return SaveScreenshot(std::move(screenshot));
194 }
195
196 bool GoldenPlaygroundTest::OpenPlaygroundHere(
197 AiksPlaygroundCallback
198 callback) { // NOLINT(performance-unnecessary-value-param)
199 AiksContext renderer(GetContext(), typographer_context_);
200
201 std::optional<Picture> picture;
202 std::unique_ptr<testing::Screenshot> screenshot;
203 for (int i = 0; i < 2; ++i) {
204 picture = callback(renderer);
205 if (!picture.has_value()) {
206 return false;
207 }
208 screenshot = pimpl_->screenshotter->MakeScreenshot(
209 renderer, picture.value(), pimpl_->window_size);
210 }
211
212 return SaveScreenshot(std::move(screenshot));
213 }
214
215 bool GoldenPlaygroundTest::ImGuiBegin(const char* name,
216 bool* p_open,
217 ImGuiWindowFlags flags) {
218 return false;
219 }
220
221 std::shared_ptr<Texture> GoldenPlaygroundTest::CreateTextureForFixture(
222 const char* fixture_name,
223 bool enable_mipmapping) const {
224 std::shared_ptr<fml::Mapping> mapping =
225 flutter::testing::OpenFixtureAsMapping(fixture_name);
226 auto result = Playground::CreateTextureForMapping(GetContext(), mapping,
227 enable_mipmapping);
228 if (result) {
229 result->SetLabel(fixture_name);
230 }
231 return result;
232 }
233
234 RuntimeStage::Map GoldenPlaygroundTest::OpenAssetAsRuntimeStage(
235 const char* asset_name) const {
236 const std::shared_ptr<fml::Mapping> fixture =
237 flutter::testing::OpenFixtureAsMapping(asset_name);
238 if (!fixture || fixture->GetSize() == 0) {
239 return {};
240 }
241 return RuntimeStage::DecodeRuntimeStages(fixture);
242 }
243
244 std::shared_ptr<Context> GoldenPlaygroundTest::GetContext() const {
245 return pimpl_->screenshotter->GetPlayground().GetContext();
246 }
247
248 std::shared_ptr<Context> GoldenPlaygroundTest::MakeContext() const {
249 if (GetParam() == PlaygroundBackend::kMetal) {
250 /// On Metal we create a context for each test.
251 return GetContext();
252 } else if (GetParam() == PlaygroundBackend::kVulkan) {
253 bool enable_vulkan_validations = true;
254 FML_CHECK(!pimpl_->test_vulkan_playground)
255 << "We don't support creating multiple contexts for one test";
256 pimpl_->test_vulkan_playground =
257 MakeVulkanPlayground(enable_vulkan_validations);
258 pimpl_->screenshotter = std::make_unique<testing::VulkanScreenshotter>(
259 pimpl_->test_vulkan_playground);
260 return pimpl_->test_vulkan_playground->GetContext();
261 } else {
262 /// On OpenGL we create a context for each test.
263 return GetContext();
264 }
265 }
266
267 Point GoldenPlaygroundTest::GetContentScale() const {
268 return pimpl_->screenshotter->GetPlayground().GetContentScale();
269 }
270
271 Scalar GoldenPlaygroundTest::GetSecondsElapsed() const {
272 return 0.0f;
273 }
274
275 ISize GoldenPlaygroundTest::GetWindowSize() const {
276 return pimpl_->window_size;
277 }
278
279 void GoldenPlaygroundTest::GoldenPlaygroundTest::SetWindowSize(ISize size) {
280 pimpl_->window_size = size;
281 }
282
283 fml::Status GoldenPlaygroundTest::SetCapabilities(
284 const std::shared_ptr<Capabilities>& capabilities) {
285 return pimpl_->screenshotter->GetPlayground().SetCapabilities(capabilities);
286 }
287
288 } // namespace impeller
impeller::GoldenPlaygroundTest::ImGuiBegin
static bool ImGuiBegin(const char *name, bool *p_open, ImGuiWindowFlags flags)
Definition: golden_playground_test_mac.cc:215
impeller::GoldenPlaygroundTest::GetContentScale
Point GetContentScale() const
Definition: golden_playground_test_mac.cc:267
impeller::GoldenPlaygroundTest::AiksPlaygroundCallback
std::function< std::optional< Picture >(AiksContext &renderer)> AiksPlaygroundCallback
Definition: golden_playground_test.h:27
impeller::PlaygroundBackend::kVulkan
@ kVulkan
impeller::GoldenPlaygroundTest::SetCapabilities
fml::Status SetCapabilities(const std::shared_ptr< Capabilities > &capabilities)
Definition: golden_playground_test_mac.cc:283
impeller::Scalar
float Scalar
Definition: scalar.h:18
impeller::AiksContext
Definition: aiks_context.h:20
vulkan_screenshotter.h
impeller::GoldenPlaygroundTest::MakeContext
std::shared_ptr< Context > MakeContext() const
Definition: golden_playground_test_mac.cc:248
golden_digest.h
impeller::RuntimeStage::DecodeRuntimeStages
static Map DecodeRuntimeStages(const std::shared_ptr< fml::Mapping > &payload)
Definition: runtime_stage.cc:60
impeller::PlaygroundBackend::kMetal
@ kMetal
impeller::PlaygroundBackend
PlaygroundBackend
Definition: playground.h:29
impeller::GoldenPlaygroundTest::OpenPlaygroundHere
bool OpenPlaygroundHere(Picture picture)
Definition: golden_playground_test_mac.cc:188
typographer_context.h
picture.h
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::GoldenPlaygroundTest::GetContext
std::shared_ptr< Context > GetContext() const
Definition: golden_playground_test_mac.cc:244
impeller::GoldenPlaygroundTest::GoldenPlaygroundTestImpl
Definition: golden_playground_test_mac.cc:109
IMP_AIKSTEST
#define IMP_AIKSTEST(name)
Definition: golden_playground_test_mac.cc:57
impeller::GoldenPlaygroundTest::~GoldenPlaygroundTest
~GoldenPlaygroundTest() override
typographer_context_skia.h
impeller::GoldenPlaygroundTest::SetTypographerContext
void SetTypographerContext(std::shared_ptr< TypographerContext > typographer_context)
Definition: golden_playground_test_mac.cc:122
impeller::Picture
Definition: picture.h:20
impeller::TSize< int64_t >
impeller::GoldenPlaygroundTest::GoldenPlaygroundTest
GoldenPlaygroundTest()
Definition: golden_playground_test_mac.cc:116
impeller::GoldenPlaygroundTest::GetBackend
PlaygroundBackend GetBackend() const
Definition: golden_playground_test_mac.cc:184
impeller::kSkipTests
static const std::vector< std::string > kSkipTests
Definition: golden_playground_test_mac.cc:64
impeller::GoldenPlaygroundTest::GoldenPlaygroundTestImpl::test_vulkan_playground
std::unique_ptr< PlaygroundImpl > test_vulkan_playground
Definition: golden_playground_test_mac.cc:110
impeller::GoldenPlaygroundTest::SetUp
void SetUp()
Definition: golden_playground_test_mac.cc:131
impeller::GoldenPlaygroundTest::GoldenPlaygroundTestImpl::screenshotter
std::unique_ptr< testing::Screenshotter > screenshotter
Definition: golden_playground_test_mac.cc:112
impeller::GoldenPlaygroundTest
Definition: golden_playground_test.h:23
impeller::testing::WorkingDirectory::GetFilenamePath
std::string GetFilenamePath(const std::string &filename) const
Definition: working_directory.cc:23
impeller::GoldenPlaygroundTest::CreateTextureForFixture
std::shared_ptr< Texture > CreateTextureForFixture(const char *fixture_name, bool enable_mipmapping=false) const
Definition: golden_playground_test_mac.cc:221
golden_playground_test.h
impeller::TypographerContextSkia
Definition: typographer_context_skia.h:12
impeller::PlaygroundImpl::Create
static std::unique_ptr< PlaygroundImpl > Create(PlaygroundBackend backend, PlaygroundSwitches switches)
Definition: playground_impl.cc:25
impeller::PlaygroundSwitches::use_angle
bool use_angle
Definition: switches.h:34
impeller::PlaygroundBackend::kOpenGLES
@ kOpenGLES
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:436
impeller::GoldenPlaygroundTest::GetSecondsElapsed
Scalar GetSecondsElapsed() const
Definition: golden_playground_test_mac.cc:271
impeller::PlaygroundSwitches
Definition: switches.h:16
impeller::GoldenPlaygroundTest::TearDown
void TearDown()
Definition: golden_playground_test_mac.cc:127
impeller::TPoint< Scalar >
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::WorkingDirectory::Instance
static WorkingDirectory * Instance()
Definition: working_directory.cc:16
impeller::GoldenPlaygroundTest::GoldenPlaygroundTestImpl::test_opengl_playground
std::unique_ptr< PlaygroundImpl > test_opengl_playground
Definition: golden_playground_test_mac.cc:111
impeller::GoldenPlaygroundTest::GoldenPlaygroundTestImpl::window_size
ISize window_size
Definition: golden_playground_test_mac.cc:113
metal_screenshotter.h
impeller
Definition: aiks_blur_unittests.cc:20
impeller::GoldenPlaygroundTest::GetWindowSize
ISize GetWindowSize() const
Definition: golden_playground_test_mac.cc:275
impeller::GoldenPlaygroundTest::OpenAssetAsRuntimeStage
RuntimeStage::Map OpenAssetAsRuntimeStage(const char *asset_name) const
Definition: golden_playground_test_mac.cc:234
impeller::RuntimeStage::Map
std::map< RuntimeStageBackend, std::shared_ptr< RuntimeStage > > Map
Definition: runtime_stage.h:23