Flutter Engine
 
Loading...
Searching...
No Matches
embedder_unittests_util.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#define FML_USED_ON_EMBEDDER
6
7#include <limits>
8#include <utility>
9
12
13#include "third_party/skia/include/core/SkCPURecorder.h"
14#include "third_party/skia/include/core/SkImage.h"
15#include "third_party/skia/include/core/SkSurface.h"
16#include "third_party/skia/include/encode/SkPngEncoder.h"
17#include "third_party/skia/include/gpu/ganesh/SkSurfaceGanesh.h"
18
19namespace flutter {
20namespace testing {
21
22sk_sp<SkSurface> CreateRenderSurface(const FlutterLayer& layer,
23 GrDirectContext* context) {
24 const auto image_info =
25 SkImageInfo::MakeN32Premul(layer.size.width, layer.size.height);
26 auto surface = context ? SkSurfaces::RenderTarget(
27 context, // context
28 skgpu::Budgeted::kNo, // budgeted
29 image_info, // image info
30 1, // sample count
31 kTopLeft_GrSurfaceOrigin, // surface origin
32 nullptr, // surface properties
33 false // mipmaps
34
35 )
36 : SkSurfaces::Raster(image_info);
37 FML_CHECK(surface != nullptr);
38 return surface;
39}
40
41// Normalizes the color-space, color-type and alpha-type for comparison.
42static sk_sp<SkData> NormalizeImage(const sk_sp<SkImage>& image) {
43 // To avoid clipping, convert to a very wide gamut, and a high bit depth.
44 sk_sp<SkColorSpace> norm_colorspace = SkColorSpace::MakeRGB(
45 SkNamedTransferFn::kRec2020, SkNamedGamut::kRec2020);
46 SkImageInfo norm_image_info =
47 SkImageInfo::Make(image->width(), image->height(),
48 SkColorType::kR16G16B16A16_unorm_SkColorType,
49 SkAlphaType::kUnpremul_SkAlphaType, norm_colorspace);
50 size_t row_bytes = norm_image_info.minRowBytes();
51 size_t size = norm_image_info.computeByteSize(row_bytes);
52 sk_sp<SkData> data = SkData::MakeUninitialized(size);
53 if (!data) {
54 FML_CHECK(false) << "Unable to allocate data.";
55 }
56
57 bool success = image->readPixels(norm_image_info, data->writable_data(),
58 row_bytes, 0, 0);
59 if (!success) {
60 FML_CHECK(false) << "Unable to read pixels.";
61 }
62
63 return data;
64}
65
66bool RasterImagesAreSame(const sk_sp<SkImage>& a, const sk_sp<SkImage>& b) {
67 if (!a || !b) {
68 return false;
69 }
70
71 FML_CHECK(!a->isTextureBacked());
72 FML_CHECK(!b->isTextureBacked());
73
74 sk_sp<SkData> normalized_a = NormalizeImage(a);
75 sk_sp<SkData> normalized_b = NormalizeImage(b);
76
77 return normalized_a->equals(normalized_b.get());
78}
79
81 const std::string& name) {
82 switch (backend) {
84 return "vk_" + name;
85 default:
86 return name;
87 }
88}
89
109
112 bool opengl_framebuffer) {
113 switch (backend) {
115 backing_store.type = kFlutterBackingStoreTypeVulkan;
116 break;
118 if (opengl_framebuffer) {
119 backing_store.type = kFlutterBackingStoreTypeOpenGL;
121 } else {
122 backing_store.type = kFlutterBackingStoreTypeOpenGL;
124 }
125 break;
127 backing_store.type = kFlutterBackingStoreTypeMetal;
128 break;
131 break;
132 }
133}
134
135bool WriteImageToDisk(const fml::UniqueFD& directory,
136 const std::string& name,
137 const sk_sp<SkImage>& image) {
138 if (!image) {
139 return false;
140 }
141
142 auto data = SkPngEncoder::Encode(nullptr, image.get(), {});
143
144 if (!data) {
145 return false;
146 }
147
148 fml::NonOwnedMapping mapping(static_cast<const uint8_t*>(data->data()),
149 data->size());
150 return WriteAtomically(directory, name.c_str(), mapping);
151}
152
153bool ImageMatchesFixture(const std::string& fixture_file_name,
154 const sk_sp<SkImage>& scene_image) {
155 fml::FileMapping fixture_image_mapping(OpenFixture(fixture_file_name));
156
157 FML_CHECK(fixture_image_mapping.GetSize() != 0u)
158 << "Could not find fixture: " << fixture_file_name;
159
160 auto encoded_image = SkData::MakeWithoutCopy(
161 fixture_image_mapping.GetMapping(), fixture_image_mapping.GetSize());
162 auto fixture_image =
163 SkImages::DeferredFromEncodedData(std::move(encoded_image))
164 ->makeRasterImage(nullptr);
165
166 FML_CHECK(fixture_image) << "Could not create image from fixture: "
167 << fixture_file_name;
168
169 FML_CHECK(scene_image) << "Invalid scene image.";
170
171 auto scene_image_subset = scene_image->makeSubset(
172 skcpu::Recorder::TODO(),
173 SkIRect::MakeWH(fixture_image->width(), fixture_image->height()), {});
174
175 FML_CHECK(scene_image_subset)
176 << "Could not create image subset for fixture comparison: "
177 << scene_image_subset;
178
179 const auto images_are_same =
180 RasterImagesAreSame(scene_image_subset, fixture_image);
181
182 // If the images are not the same, this predicate is going to indicate test
183 // failure. Dump both the actual image and the expectation to disk to the
184 // test author can figure out what went wrong.
185 if (!images_are_same) {
186 const auto fixtures_path = GetFixturesPath();
187
188 const auto actual_file_name = "actual_" + fixture_file_name;
189 const auto expect_file_name = "expectation_" + fixture_file_name;
190
191 auto fixtures_fd = OpenFixturesDirectory();
192
193 FML_CHECK(
194 WriteImageToDisk(fixtures_fd, actual_file_name, scene_image_subset))
195 << "Could not write file to disk: " << actual_file_name;
196
197 FML_CHECK(WriteImageToDisk(fixtures_fd, expect_file_name, fixture_image))
198 << "Could not write file to disk: " << expect_file_name;
199
200 FML_LOG(ERROR) << "Image did not match expectation." << std::endl
201 << "Expected:"
202 << fml::paths::JoinPaths({fixtures_path, expect_file_name})
203 << std::endl
204 << "Got:"
205 << fml::paths::JoinPaths({fixtures_path, actual_file_name})
206 << std::endl;
207 }
208 return images_are_same;
209}
210
211bool ImageMatchesFixture(const std::string& fixture_file_name,
212 std::future<sk_sp<SkImage>>& scene_image) {
213 return ImageMatchesFixture(fixture_file_name, scene_image.get());
214}
215
216bool SurfacePixelDataMatchesBytes(SkSurface* surface,
217 const std::vector<uint8_t>& bytes) {
218 SkPixmap pixmap;
219 auto ok = surface->peekPixels(&pixmap);
220 if (!ok) {
221 return false;
222 }
223
224 auto matches = (pixmap.rowBytes() == bytes.size()) &&
225 (memcmp(bytes.data(), pixmap.addr(), bytes.size()) == 0);
226
227 if (!matches) {
228 FML_LOG(ERROR) << "SkImage pixel data didn't match bytes.";
229
230 {
231 const uint8_t* addr = static_cast<const uint8_t*>(pixmap.addr());
232 std::stringstream stream;
233 for (size_t i = 0; i < pixmap.computeByteSize(); ++i) {
234 stream << "0x" << std::setfill('0') << std::setw(2) << std::uppercase
235 << std::hex << static_cast<int>(addr[i]);
236 if (i != pixmap.computeByteSize() - 1) {
237 stream << ", ";
238 }
239 }
240 FML_LOG(ERROR) << " Actual: " << stream.str();
241 }
242 {
243 std::stringstream stream;
244 for (auto b = bytes.begin(); b != bytes.end(); ++b) {
245 stream << "0x" << std::setfill('0') << std::setw(2) << std::uppercase
246 << std::hex << static_cast<int>(*b);
247 if (b != bytes.end() - 1) {
248 stream << ", ";
249 }
250 }
251 FML_LOG(ERROR) << " Expected: " << stream.str();
252 }
253 }
254
255 return matches;
256}
257
258bool SurfacePixelDataMatchesBytes(std::future<SkSurface*>& surface_future,
259 const std::vector<uint8_t>& bytes) {
260 return SurfacePixelDataMatchesBytes(surface_future.get(), bytes);
261}
262
264 const FlutterPlatformViewMutation** mutations,
265 size_t count,
267 const std::function<void(const FlutterPlatformViewMutation& mutation)>&
268 handler) {
269 if (mutations == nullptr) {
270 return;
271 }
272
273 for (size_t i = 0; i < count; ++i) {
274 const FlutterPlatformViewMutation* mutation = mutations[i];
275 if (mutation->type != type) {
276 continue;
277 }
278
279 handler(*mutation);
280 }
281}
282
286 const std::function<void(const FlutterPlatformViewMutation& mutation)>&
287 handler) {
288 return FilterMutationsByType(view->mutations, view->mutations_count, type,
289 handler);
290}
291
293 const FlutterPlatformViewMutation** mutations,
294 size_t count) {
295 DlMatrix collected;
296
299 [&](const auto& mutation) {
300 collected = collected * DlMatrixMake(mutation.transformation);
301 });
302
303 return collected;
304}
305
310
311} // namespace testing
312} // namespace flutter
GLenum type
size_t GetSize() const override
const uint8_t * GetMapping() const override
FlutterPlatformViewMutationType
Definition embedder.h:1997
@ kFlutterPlatformViewMutationTypeTransformation
Definition embedder.h:2009
@ kFlutterOpenGLTargetTypeFramebuffer
Definition embedder.h:417
@ kFlutterOpenGLTargetTypeTexture
Definition embedder.h:414
@ kFlutterBackingStoreTypeMetal
Specifies a Metal backing store. This is backed by a Metal texture.
Definition embedder.h:2055
@ kFlutterBackingStoreTypeVulkan
Specifies a Vulkan backing store. This is backed by a Vulkan VkImage.
Definition embedder.h:2057
@ kFlutterBackingStoreTypeSoftware
Specified an software allocation for Flutter to render into using the CPU.
Definition embedder.h:2053
@ kFlutterBackingStoreTypeOpenGL
Definition embedder.h:2051
flutter::DlMatrix DlMatrixMake(const FlutterTransformation &xformation)
FlutterVulkanImage * image
VkSurfaceKHR surface
Definition main.cc:65
FlView * view
const gchar FlBinaryMessengerMessageHandler handler
#define FML_LOG(severity)
Definition logging.h:101
#define FML_CHECK(condition)
Definition logging.h:104
const char * GetFixturesPath()
Returns the directory containing the test fixture for the target if this target has fixtures configur...
bool WriteImageToDisk(const fml::UniqueFD &directory, const std::string &name, const sk_sp< SkImage > &image)
sk_sp< SkSurface > CreateRenderSurface(const FlutterLayer &layer, GrDirectContext *context)
void FilterMutationsByType(const FlutterPlatformViewMutation **mutations, size_t count, FlutterPlatformViewMutationType type, const std::function< void(const FlutterPlatformViewMutation &mutation)> &handler)
static sk_sp< SkData > NormalizeImage(const sk_sp< SkImage > &image)
bool SurfacePixelDataMatchesBytes(SkSurface *surface, const std::vector< uint8_t > &bytes)
fml::UniqueFD OpenFixture(const std::string &fixture_name)
Opens a fixture of the given file name.
Definition testing.cc:36
fml::UniqueFD OpenFixturesDirectory()
Opens the fixtures directory for the unit-test harness.
Definition testing.cc:22
void ConfigureBackingStore(FlutterBackingStore &backing_store, EmbedderTestContextType backend, bool opengl_framebuffer)
Configures per-backend properties for a given backing store.
bool RasterImagesAreSame(const sk_sp< SkImage > &a, const sk_sp< SkImage > &b)
std::string FixtureNameForBackend(EmbedderTestContextType backend, const std::string &name)
Prepends a prefix to the name which is unique to the test context type. This is useful for tests that...
bool ImageMatchesFixture(const std::string &fixture_file_name, const sk_sp< SkImage > &scene_image)
EmbedderTestBackingStoreProducer::RenderTargetType GetRenderTargetFromBackend(EmbedderTestContextType backend, bool opengl_framebuffer)
Resolves a render target type for a given backend description. This is useful for tests that use Embe...
DlMatrix GetTotalMutationTransformationMatrix(const FlutterPlatformViewMutation **mutations, size_t count)
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
DEF_SWITCHES_START aot vmservice shared library name
Definition switch_defs.h:27
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot data
Definition switch_defs.h:36
std::string JoinPaths(std::initializer_list< std::string > components)
Definition paths.cc:14
FlutterBackingStoreType type
Specifies the type of backing store.
Definition embedder.h:2071
FlutterOpenGLBackingStore open_gl
The description of the OpenGL backing store.
Definition embedder.h:2077
FlutterSize size
The size of the layer (in physical pixels).
Definition embedder.h:2147
FlutterOpenGLTargetType type
Definition embedder.h:1914
FlutterPlatformViewMutationType type
The type of the mutation described by the subsequent union.
Definition embedder.h:2014
double height
Definition embedder.h:629
double width
Definition embedder.h:628
A 4x4 matrix using column-major storage.
Definition matrix.h:37