7#include "flutter/lib/ui/painting/image_encoding.h"
8#include "flutter/lib/ui/painting/image_encoding_impl.h"
10#include "flutter/common/task_runners.h"
11#include "flutter/fml/synchronization/waitable_event.h"
12#include "flutter/lib/ui/painting/image.h"
13#include "flutter/runtime/dart_vm.h"
14#include "flutter/shell/common/shell_test.h"
15#include "flutter/shell/common/thread_host.h"
16#include "flutter/testing/testing.h"
17#include "gmock/gmock.h"
18#include "gtest/gtest.h"
20#if IMPELLER_SUPPORTS_RENDERING
21#include "flutter/lib/ui/painting/image_encoding_impeller.h"
28#pragma GCC diagnostic ignored "-Wunreachable-code"
36class MockDlImage :
public DlImage {
39 MOCK_METHOD(std::shared_ptr<impeller::Texture>,
43 MOCK_METHOD(
bool, isOpaque, (), (
const,
override));
44 MOCK_METHOD(
bool, isTextureBacked, (), (
const,
override));
45 MOCK_METHOD(
bool, isUIThreadSafe, (), (
const,
override));
46 MOCK_METHOD(
SkISize, dimensions, (), (
const,
override));
47 MOCK_METHOD(
size_t, GetApproximateByteSize, (), (
const,
override));
113 AddNativeCallback(
"ValidateExternal",
116 std::unique_ptr<Shell>
shell = CreateShell(
settings, task_runners);
118 ASSERT_TRUE(
shell->IsSetup());
120 configuration.SetEntrypoint(
"encodeImageProducesExternalUint8List");
122 shell->RunEngine(std::move(configuration), [&](
auto result) {
126 message_latch.
Wait();
127 DestroyShell(std::move(
shell), task_runners);
161 auto is_gpu_disabled_sync_switch =
162 std::make_shared<const MockSyncSwitch>();
163 EXPECT_CALL(*is_gpu_disabled_sync_switch,
Execute)
168 io_manager->GetResourceContext(),
169 is_gpu_disabled_sync_switch);
180 std::unique_ptr<Shell>
shell = CreateShell(
settings, task_runners);
182 ASSERT_TRUE(
shell->IsSetup());
184 configuration.SetEntrypoint(
"encodeImageProducesExternalUint8List");
186 shell->RunEngine(std::move(configuration), [&](
auto result) {
190 message_latch.
Wait();
191 DestroyShell(std::move(
shell), task_runners);
194#if IMPELLER_SUPPORTS_RENDERING
195using ::impeller::testing::MockAllocator;
196using ::impeller::testing::MockBlitPass;
197using ::impeller::testing::MockCommandBuffer;
198using ::impeller::testing::MockCommandQueue;
199using ::impeller::testing::MockDeviceBuffer;
200using ::impeller::testing::MockImpellerContext;
201using ::impeller::testing::MockTexture;
203using ::testing::DoAll;
205using ::testing::InvokeArgument;
206using ::testing::Return;
209std::shared_ptr<impeller::Context> MakeConvertDlImageToSkImageContext(
210 std::vector<uint8_t>&
buffer) {
211 auto context = std::make_shared<MockImpellerContext>();
212 auto command_buffer = std::make_shared<MockCommandBuffer>(context);
213 auto allocator = std::make_shared<MockAllocator>();
214 auto blit_pass = std::make_shared<MockBlitPass>();
215 auto command_queue = std::make_shared<MockCommandQueue>();
218 auto device_buffer = std::make_shared<MockDeviceBuffer>(device_buffer_desc);
219 EXPECT_CALL(*allocator, OnCreateBuffer).WillOnce(Return(device_buffer));
220 EXPECT_CALL(*blit_pass, IsValid).WillRepeatedly(Return(
true));
221 EXPECT_CALL(*command_buffer, IsValid).WillRepeatedly(Return(
true));
222 EXPECT_CALL(*command_buffer, OnCreateBlitPass).WillOnce(Return(blit_pass));
223 EXPECT_CALL(*context, GetResourceAllocator).WillRepeatedly(Return(allocator));
225 EXPECT_CALL(*device_buffer, OnGetContents).WillOnce(Return(
buffer.data()));
226 EXPECT_CALL(*command_queue, Submit(_, _))
230 EXPECT_CALL(*context, GetCommandQueue).WillRepeatedly(Return(command_queue));
235TEST_F(ShellTest, EncodeImageRetries) {
237 GTEST_SKIP() <<
"Only works on macos currently.";
239 Settings
settings = CreateSettingsForFixture();
241 TaskRunners task_runners(
"test",
248 std::unique_ptr<Shell>
shell = CreateShell({
250 .task_runners = task_runners,
271 ASSERT_TRUE(
shell->IsSetup());
273 configuration.SetEntrypoint(
"toByteDataRetries");
275 shell->RunEngine(std::move(configuration), [&](
auto result) {
279 message_latch.
Wait();
280 DestroyShell(std::move(
shell), task_runners);
283TEST_F(ShellTest, ToImageRetries) {
285 GTEST_SKIP() <<
"Only works on macos currently.";
287 Settings
settings = CreateSettingsForFixture();
289 TaskRunners task_runners(
"test",
296 std::unique_ptr<Shell>
shell = CreateShell({
298 .task_runners = task_runners,
319 ASSERT_TRUE(
shell->IsSetup());
321 configuration.SetEntrypoint(
"toImageRetries");
323 shell->RunEngine(std::move(configuration), [&](
auto result) {
327 message_latch.
Wait();
328 DestroyShell(std::move(
shell), task_runners);
331TEST_F(ShellTest, EncodeImageFailsWithoutGPUImpeller) {
336 Settings
settings = CreateSettingsForFixture();
338 TaskRunners task_runners(
"test",
353 AddNativeCallback(
"ValidateError",
356 std::unique_ptr<Shell>
shell = CreateShell({
358 .task_runners = task_runners,
366 TurnOffGPU(
shell.get(),
true);
372 task_runners.GetIOTaskRunner()->PostTask([&] {
373 std::shared_ptr<impeller::Context> impeller_context =
374 shell->GetIOManager()->GetImpellerContext();
378 impeller_context->StoreTaskForGPU([] {});
383 AddNativeCallback(
"FlushGpuAwaitingTasks",
386 ASSERT_TRUE(
shell->IsSetup());
388 configuration.SetEntrypoint(
"toByteDataWithoutGPU");
390 shell->RunEngine(std::move(configuration), [&](
auto result) {
394 message_latch.
Wait();
395 DestroyShell(std::move(
shell), task_runners);
398TEST(ImageEncodingImpellerTest, ConvertDlImageToSkImage16Float) {
400 EXPECT_CALL(*
image, dimensions)
404 auto texture = std::make_shared<MockTexture>(
desc);
405 EXPECT_CALL(*
image, impeller_texture).WillOnce(Return(
texture));
406 std::vector<uint8_t>
buffer;
407 buffer.reserve(100 * 100 * 8);
408 auto context = MakeConvertDlImageToSkImageContext(
buffer);
409 bool did_call =
false;
414 ASSERT_TRUE(
image.ok());
415 ASSERT_TRUE(
image.value());
425TEST(ImageEncodingImpellerTest, ConvertDlImageToSkImage10XR) {
427 EXPECT_CALL(*
image, dimensions)
431 auto texture = std::make_shared<MockTexture>(
desc);
432 EXPECT_CALL(*
image, impeller_texture).WillOnce(Return(
texture));
433 std::vector<uint8_t>
buffer;
434 buffer.reserve(100 * 100 * 4);
435 auto context = MakeConvertDlImageToSkImageContext(
buffer);
436 bool did_call =
false;
441 ASSERT_TRUE(
image.ok());
442 ASSERT_TRUE(
image.value());
452TEST(ImageEncodingImpellerTest, PngEncoding10XR) {
463 paint.setAntiAlias(
true);
475struct PngMemoryReader {
481void PngMemoryRead(png_structp png_ptr,
483 png_size_t byte_count_to_read) {
484 PngMemoryReader* memory_reader =
485 reinterpret_cast<PngMemoryReader*
>(png_get_io_ptr(png_ptr));
486 if (memory_reader->offset + byte_count_to_read > memory_reader->size) {
487 png_error(png_ptr,
"Read error in PngMemoryRead");
489 memcpy(out_bytes, memory_reader->data + memory_reader->offset,
491 memory_reader->offset += byte_count_to_read;
497 png_create_read_struct(PNG_LIBPNG_VER_STRING,
nullptr,
nullptr,
nullptr);
502 png_infop
info = png_create_info_struct(png);
504 png_destroy_read_struct(&png,
nullptr,
nullptr);
509 [&png, &
info]() { png_destroy_read_struct(&png, &
info,
nullptr); });
511 if (setjmp(png_jmpbuf(png))) {
515 PngMemoryReader memory_reader = {
516 .data = png_data, .offset = 0, .size = png_size};
517 png_set_read_fn(png, &memory_reader, PngMemoryRead);
519 png_read_info(png,
info);
521 int width = png_get_image_width(png,
info);
524 png_byte bit_depth = png_get_bit_depth(png,
info);
526 if (bit_depth == 16) {
527 png_set_strip_16(png);
530 png_set_palette_to_rgb(png);
533 png_read_update_info(png,
info);
535 std::vector<png_bytep> row_pointers;
536 row_pointers.reserve(
height);
539 row_pointers.push_back(
540 reinterpret_cast<png_bytep
>(
result.data() +
i *
width));
543 png_read_image(png, row_pointers.data());
549TEST(ImageEncodingImpellerTest, PngEncodingBGRA10XR) {
560 paint.setAntiAlias(
true);
568 ASSERT_TRUE(
png.ok());
570 ReadPngFromMemory(
png.value()->bytes(),
png.value()->size());
571 ASSERT_TRUE(pixels.
ok());
572 EXPECT_EQ(pixels.
value()[0], 0xff0000ff);
573 int middle = 100 * 50 + 50;
574 EXPECT_EQ(pixels.
value()[middle], 0xffff0000);
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
@ kRGBA_F16_SkColorType
pixel with half floats for red, green, blue, alpha;
@ kBGRA_10101010_XR_SkColorType
pixel with 10 bits each for blue, green, red, alpha; in 64-bit word, extended range
@ kBGR_101010x_XR_SkColorType
pixel with 10 bits each for blue, green, red; in 32-bit word, extended range
constexpr SkColor SK_ColorBLUE
constexpr SkColor SK_ColorRED
constexpr SkColor SK_ColorWHITE
void clear(SkColor color)
void drawCircle(SkScalar cx, SkScalar cy, SkScalar radius, const SkPaint &paint)
SkColorSpace * colorSpace() const
SkColorType colorType() const
sk_sp< DlImage > image() const
static void ConvertDlImageToSkImage(const sk_sp< DlImage > &dl_image, std::function< void(fml::StatusOr< sk_sp< SkImage > >)> encode_task, const std::shared_ptr< impeller::Context > &impeller_context)
static RunConfiguration InferFromSettings(const Settings &settings, const fml::RefPtr< fml::TaskRunner > &io_worker=nullptr, IsolateLaunchType launch_type=IsolateLaunchType::kNewGroup)
Attempts to infer a run configuration from the settings object. This tries to create a run configurat...
fml::RefPtr< fml::TaskRunner > GetIOTaskRunner() const
fml::WeakPtr< IOManager > GetIOManager() const
static UIDartState * Current()
MOCK_METHOD(void, Execute,(const Handlers &handlers),(const))
MOCK_METHOD(void, SetSwitch,(bool value))
Wraps a closure that is invoked in the destructor unless released by the caller.
virtual void PostTask(const fml::closure &task) override
static constexpr int32_t kMaxTasksAwaitingGPU
DART_EXPORT Dart_TypedData_Type Dart_GetTypeOfExternalTypedData(Dart_Handle object)
DART_EXPORT bool Dart_IsBoolean(Dart_Handle object)
DART_EXPORT Dart_Handle Dart_IntegerToInt64(Dart_Handle integer, int64_t *value)
struct _Dart_Handle * Dart_Handle
DART_EXPORT Dart_Handle Dart_GetNativeArgument(Dart_NativeArguments args, int index)
DART_EXPORT Dart_Handle Dart_GetNativeInstanceField(Dart_Handle obj, int index, intptr_t *value)
struct _Dart_NativeArguments * Dart_NativeArguments
DART_EXPORT bool Dart_IsNull(Dart_Handle object)
DART_EXPORT Dart_Handle Dart_NewStringFromCString(const char *str)
DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle Dart_GetField(Dart_Handle container, Dart_Handle name)
DART_EXPORT bool Dart_IsError(Dart_Handle handle)
DART_EXPORT const char * Dart_GetError(Dart_Handle handle)
DART_EXPORT Dart_Handle Dart_BooleanValue(Dart_Handle boolean_obj, bool *value)
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
Dart_NativeFunction function
fml::RefPtr< fml::TaskRunner > CreateNewThread(const std::string &name)
fml::RefPtr< fml::TaskRunner > GetCurrentTaskRunner()
sk_sp< const SkImage > image
SK_API sk_sp< SkSurface > Raster(const SkImageInfo &imageInfo, size_t rowBytes, const SkSurfaceProps *surfaceProps)
ObjectPtr Invoke(const Library &lib, const char *name)
TEST_F(DisplayListTest, Defaults)
TEST(DisplayListComplexity, EmptyDisplayList)
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot data
sk_sp< SkImage > ConvertToRasterUsingResourceContext(const sk_sp< SkImage > &image, const fml::WeakPtr< GrDirectContext > &resource_context, const std::shared_ptr< const SyncSwitch > &is_gpu_disabled_sync_switch)
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service A custom Dart VM Service port The default is to pick a randomly available open port disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode disable vm service Disable mDNS Dart VM Service publication Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set endless trace buffer
it will be possible to load the file into Perfetto s trace viewer 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 of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service A custom Dart VM Service port The default is to pick a randomly available open port disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode disable vm service Disable mDNS Dart VM Service publication Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set endless trace Enable an endless trace buffer The default is a ring buffer This is useful when very old events need to viewed For during application launch Memory usage will continue to grow indefinitely however Start app with an specific route defined on the framework flutter assets Path to the Flutter assets directory enable service port Allow the VM service to fallback to automatic port selection if binding to a specified port fails trace Trace early application lifecycle Automatically switches to an endless trace buffer trace skia Filters out all Skia trace event categories except those that are specified in this comma separated list dump skp on shader Automatically dump the skp that triggers new shader compilations This is useful for writing custom ShaderWarmUp to reduce jank By this is not enabled to reduce the overhead purge persistent Remove all existing persistent cache This is mainly for debugging purposes such as reproducing the shader compilation jank trace to Write the timeline trace to a file at the specified path The file will be in Perfetto s proto format
Dart_Handle EncodeImage(CanvasImage *canvas_image, int format, Dart_Handle callback_handle)
static id< MTLCommandBuffer > CreateCommandBuffer(id< MTLCommandQueue > queue)
static constexpr SkISize Make(int32_t w, int32_t h)
static SkImageInfo Make(int width, int height, SkColorType ct, SkAlphaType at)
Handlers & SetIfTrue(const std::function< void()> &handler)
std::function< void()> true_handler
std::function< void()> false_handler
Handlers & SetIfFalse(const std::function< void()> &handler)
A lightweight object that describes the attributes of a texture that can then used an allocator to cr...
#define CREATE_NATIVE_ENTRY(native_entry)
#define EXPECT_TRUE(handle)