Flutter Engine Uber Docs
Docs for the entire Flutter Engine repo.
 
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,
67 const sk_sp<SkImage>& b,
68 int allowable_different_pixels) {
69 if (!a || !b) {
70 return false;
71 }
72
73 FML_CHECK(!a->isTextureBacked());
74 FML_CHECK(!b->isTextureBacked());
75
76 sk_sp<SkData> normalized_a = NormalizeImage(a);
77 sk_sp<SkData> normalized_b = NormalizeImage(b);
78
79 if (normalized_a->size() != normalized_b->size()) {
80 return false;
81 }
82 using NormalizedPixel = uint64_t;
83 FML_CHECK(normalized_a->size() % sizeof(NormalizedPixel) == 0);
84 int diff_count = 0;
85 for (size_t i = 0; i < normalized_a->size(); i += sizeof(NormalizedPixel)) {
86 const NormalizedPixel* pixel_a =
87 reinterpret_cast<const NormalizedPixel*>(normalized_a->bytes() + i);
88 const NormalizedPixel* pixel_b =
89 reinterpret_cast<const NormalizedPixel*>(normalized_b->bytes() + i);
90 if (*pixel_a != *pixel_b) {
91 diff_count++;
92 if (diff_count > allowable_different_pixels) {
93 return false;
94 }
95 }
96 }
97
98 return true;
99}
100
102 const std::string& name) {
103 switch (backend) {
105 return "vk_" + name;
106 default:
107 return name;
108 }
109}
110
130
133 bool opengl_framebuffer) {
134 switch (backend) {
136 backing_store.type = kFlutterBackingStoreTypeVulkan;
137 break;
139 if (opengl_framebuffer) {
140 backing_store.type = kFlutterBackingStoreTypeOpenGL;
142 } else {
143 backing_store.type = kFlutterBackingStoreTypeOpenGL;
145 }
146 break;
148 backing_store.type = kFlutterBackingStoreTypeMetal;
149 break;
152 break;
153 }
154}
155
156bool WriteImageToDisk(const fml::UniqueFD& directory,
157 const std::string& name,
158 const sk_sp<SkImage>& image) {
159 if (!image) {
160 return false;
161 }
162
163 auto data = SkPngEncoder::Encode(nullptr, image.get(), {});
164
165 if (!data) {
166 return false;
167 }
168
169 fml::NonOwnedMapping mapping(static_cast<const uint8_t*>(data->data()),
170 data->size());
171 return WriteAtomically(directory, name.c_str(), mapping);
172}
173
174bool ImageMatchesFixture(const std::string& fixture_file_name,
175 const sk_sp<SkImage>& scene_image,
176 int allowable_different_pixels) {
177 fml::FileMapping fixture_image_mapping(OpenFixture(fixture_file_name));
178
179 FML_CHECK(fixture_image_mapping.GetSize() != 0u)
180 << "Could not find fixture: " << fixture_file_name;
181
182 auto encoded_image = SkData::MakeWithoutCopy(
183 fixture_image_mapping.GetMapping(), fixture_image_mapping.GetSize());
184 auto fixture_image =
185 SkImages::DeferredFromEncodedData(std::move(encoded_image))
186 ->makeRasterImage(nullptr);
187
188 FML_CHECK(fixture_image) << "Could not create image from fixture: "
189 << fixture_file_name;
190
191 FML_CHECK(scene_image) << "Invalid scene image.";
192
193 auto scene_image_subset = scene_image->makeSubset(
194 skcpu::Recorder::TODO(),
195 SkIRect::MakeWH(fixture_image->width(), fixture_image->height()), {});
196
197 FML_CHECK(scene_image_subset)
198 << "Could not create image subset for fixture comparison: "
199 << scene_image_subset;
200
201 const auto images_are_same = RasterImagesAreSame(
202 scene_image_subset, fixture_image, allowable_different_pixels);
203
204 // If the images are not the same, this predicate is going to indicate test
205 // failure. Dump both the actual image and the expectation to disk to the
206 // test author can figure out what went wrong.
207 if (!images_are_same) {
208 const auto fixtures_path = GetFixturesPath();
209
210 const auto actual_file_name = "actual_" + fixture_file_name;
211 const auto expect_file_name = "expectation_" + fixture_file_name;
212
213 auto fixtures_fd = OpenFixturesDirectory();
214
215 FML_CHECK(
216 WriteImageToDisk(fixtures_fd, actual_file_name, scene_image_subset))
217 << "Could not write file to disk: " << actual_file_name;
218
219 FML_CHECK(WriteImageToDisk(fixtures_fd, expect_file_name, fixture_image))
220 << "Could not write file to disk: " << expect_file_name;
221
222 FML_LOG(ERROR) << "Image did not match expectation." << std::endl
223 << "Expected:"
224 << fml::paths::JoinPaths({fixtures_path, expect_file_name})
225 << std::endl
226 << "Got:"
227 << fml::paths::JoinPaths({fixtures_path, actual_file_name})
228 << std::endl;
229 }
230 return images_are_same;
231}
232
233bool ImageMatchesFixture(const std::string& fixture_file_name,
234 std::future<sk_sp<SkImage>>& scene_image,
235 int allowable_different_pixels) {
236 return ImageMatchesFixture(fixture_file_name, scene_image.get(),
237 allowable_different_pixels);
238}
239
240bool SurfacePixelDataMatchesBytes(SkSurface* surface,
241 const std::vector<uint8_t>& bytes) {
242 SkPixmap pixmap;
243 auto ok = surface->peekPixels(&pixmap);
244 if (!ok) {
245 return false;
246 }
247
248 auto matches = (pixmap.rowBytes() == bytes.size()) &&
249 (memcmp(bytes.data(), pixmap.addr(), bytes.size()) == 0);
250
251 if (!matches) {
252 FML_LOG(ERROR) << "SkImage pixel data didn't match bytes.";
253
254 {
255 const uint8_t* addr = static_cast<const uint8_t*>(pixmap.addr());
256 std::stringstream stream;
257 for (size_t i = 0; i < pixmap.computeByteSize(); ++i) {
258 stream << "0x" << std::setfill('0') << std::setw(2) << std::uppercase
259 << std::hex << static_cast<int>(addr[i]);
260 if (i != pixmap.computeByteSize() - 1) {
261 stream << ", ";
262 }
263 }
264 FML_LOG(ERROR) << " Actual: " << stream.str();
265 }
266 {
267 std::stringstream stream;
268 for (auto b = bytes.begin(); b != bytes.end(); ++b) {
269 stream << "0x" << std::setfill('0') << std::setw(2) << std::uppercase
270 << std::hex << static_cast<int>(*b);
271 if (b != bytes.end() - 1) {
272 stream << ", ";
273 }
274 }
275 FML_LOG(ERROR) << " Expected: " << stream.str();
276 }
277 }
278
279 return matches;
280}
281
282bool SurfacePixelDataMatchesBytes(std::future<SkSurface*>& surface_future,
283 const std::vector<uint8_t>& bytes) {
284 return SurfacePixelDataMatchesBytes(surface_future.get(), bytes);
285}
286
288 const FlutterPlatformViewMutation** mutations,
289 size_t count,
291 const std::function<void(const FlutterPlatformViewMutation& mutation)>&
292 handler) {
293 if (mutations == nullptr) {
294 return;
295 }
296
297 for (size_t i = 0; i < count; ++i) {
298 const FlutterPlatformViewMutation* mutation = mutations[i];
299 if (mutation->type != type) {
300 continue;
301 }
302
303 handler(*mutation);
304 }
305}
306
308 const FlutterPlatformView* view,
310 const std::function<void(const FlutterPlatformViewMutation& mutation)>&
311 handler) {
312 return FilterMutationsByType(view->mutations, view->mutations_count, type,
313 handler);
314}
315
317 const FlutterPlatformViewMutation** mutations,
318 size_t count) {
319 DlMatrix collected;
320
323 [&](const auto& mutation) {
324 collected = collected * DlMatrixMake(mutation.transformation);
325 });
326
327 return collected;
328}
329
334
335} // namespace testing
336} // namespace flutter
size_t GetSize() const override
const uint8_t * GetMapping() const override
FlutterPlatformViewMutationType
Definition embedder.h:2035
@ kFlutterPlatformViewMutationTypeTransformation
Definition embedder.h:2047
@ kFlutterOpenGLTargetTypeFramebuffer
Definition embedder.h:424
@ kFlutterOpenGLTargetTypeTexture
Definition embedder.h:421
@ kFlutterBackingStoreTypeMetal
Specifies a Metal backing store. This is backed by a Metal texture.
Definition embedder.h:2093
@ kFlutterBackingStoreTypeVulkan
Specifies a Vulkan backing store. This is backed by a Vulkan VkImage.
Definition embedder.h:2095
@ kFlutterBackingStoreTypeSoftware
Specified an software allocation for Flutter to render into using the CPU.
Definition embedder.h:2091
@ kFlutterBackingStoreTypeOpenGL
Definition embedder.h:2089
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)
bool ImageMatchesFixture(const std::string &fixture_file_name, const sk_sp< SkImage > &scene_image, int allowable_different_pixels)
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.
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...
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...
bool RasterImagesAreSame(const sk_sp< SkImage > &a, const sk_sp< SkImage > &b, int allowable_different_pixels)
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
impeller::ShaderType type
FlutterBackingStoreType type
Specifies the type of backing store.
Definition embedder.h:2109
FlutterOpenGLBackingStore open_gl
The description of the OpenGL backing store.
Definition embedder.h:2115
FlutterSize size
The size of the layer (in physical pixels).
Definition embedder.h:2185
FlutterOpenGLTargetType type
Definition embedder.h:1952
FlutterPlatformViewMutationType type
The type of the mutation described by the subsequent union.
Definition embedder.h:2052
double height
Definition embedder.h:636
double width
Definition embedder.h:635
A 4x4 matrix using column-major storage.
Definition matrix.h:37