Flutter Engine
image_decoder_unittests.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 "flutter/lib/ui/painting/image_decoder.h"
6 
7 #include "flutter/common/task_runners.h"
8 #include "flutter/fml/mapping.h"
9 #include "flutter/fml/synchronization/waitable_event.h"
10 #include "flutter/lib/ui/painting/multi_frame_codec.h"
11 #include "flutter/runtime/dart_vm.h"
12 #include "flutter/runtime/dart_vm_lifecycle.h"
13 #include "flutter/testing/dart_isolate_runner.h"
14 #include "flutter/testing/elf_loader.h"
15 #include "flutter/testing/fixture_test.h"
16 #include "flutter/testing/post_task_sync.h"
17 #include "flutter/testing/test_dart_native_resolver.h"
18 #include "flutter/testing/test_gl_surface.h"
19 #include "flutter/testing/testing.h"
20 
21 namespace flutter {
22 namespace testing {
23 
24 class TestIOManager final : public IOManager {
25  public:
27  bool has_gpu_context = true)
28  : gl_surface_(SkISize::Make(1, 1)),
29  gl_context_(has_gpu_context ? gl_surface_.CreateGrContext() : nullptr),
30  weak_gl_context_factory_(
31  has_gpu_context
32  ? std::make_unique<fml::WeakPtrFactory<GrDirectContext>>(
33  gl_context_.get())
34  : nullptr),
35  unref_queue_(fml::MakeRefCounted<SkiaUnrefQueue>(
36  task_runner,
37  fml::TimeDelta::FromNanoseconds(0))),
38  runner_(task_runner),
39  is_gpu_disabled_sync_switch_(std::make_shared<fml::SyncSwitch>()),
40  weak_factory_(this) {
41  FML_CHECK(task_runner->RunsTasksOnCurrentThread())
42  << "The IO manager must be initialized its primary task runner. The "
43  "test harness may not be set up correctly/safely.";
44  weak_prototype_ = weak_factory_.GetWeakPtr();
45  }
46 
47  ~TestIOManager() override {
50  [&latch, queue = unref_queue_]() {
51  queue->Drain();
52  latch.Signal();
53  });
54  latch.Wait();
55  }
56 
57  // |IOManager|
59  return weak_prototype_;
60  }
61 
62  // |IOManager|
64  return weak_gl_context_factory_ ? weak_gl_context_factory_->GetWeakPtr()
66  }
67 
68  // |IOManager|
70  return unref_queue_;
71  }
72 
73  // |IOManager|
74  std::shared_ptr<const fml::SyncSwitch> GetIsGpuDisabledSyncSwitch() override {
76  return is_gpu_disabled_sync_switch_;
77  }
78 
80 
81  private:
82  TestGLSurface gl_surface_;
83  sk_sp<GrDirectContext> gl_context_;
84  std::unique_ptr<fml::WeakPtrFactory<GrDirectContext>>
85  weak_gl_context_factory_;
86  fml::RefPtr<SkiaUnrefQueue> unref_queue_;
87  fml::WeakPtr<TestIOManager> weak_prototype_;
89  std::shared_ptr<fml::SyncSwitch> is_gpu_disabled_sync_switch_;
91 
92  FML_DISALLOW_COPY_AND_ASSIGN(TestIOManager);
93 };
94 
95 static sk_sp<SkData> OpenFixtureAsSkData(const char* name) {
96  auto fixtures_directory =
98  if (!fixtures_directory.is_valid()) {
99  return nullptr;
100  }
101 
102  auto fixture_mapping =
103  fml::FileMapping::CreateReadOnly(fixtures_directory, name);
104 
105  if (!fixture_mapping) {
106  return nullptr;
107  }
108 
109  SkData::ReleaseProc on_release = [](const void* ptr, void* context) -> void {
110  delete reinterpret_cast<fml::FileMapping*>(context);
111  };
112 
113  auto data = SkData::MakeWithProc(fixture_mapping->GetMapping(),
114  fixture_mapping->GetSize(), on_release,
115  fixture_mapping.get());
116 
117  if (!data) {
118  return nullptr;
119  }
120  // The data is now owned by Skia.
121  fixture_mapping.release();
122  return data;
123 }
124 
126 
127 TEST_F(ImageDecoderFixtureTest, CanCreateImageDecoder) {
129  auto thread_task_runner = CreateNewThread();
130  TaskRunners runners(GetCurrentTestName(), // label
131  thread_task_runner, // platform
132  thread_task_runner, // raster
133  thread_task_runner, // ui
134  thread_task_runner // io
135 
136  );
137 
138  PostTaskSync(runners.GetIOTaskRunner(), [&]() {
140  ImageDecoder decoder(std::move(runners), loop->GetTaskRunner(),
141  manager.GetWeakIOManager());
142  });
143 }
144 
145 /// An Image generator that pretends it can't recognize the data it was given.
147  public:
148  UnknownImageGenerator() : info_(SkImageInfo::MakeUnknown()){};
149  ~UnknownImageGenerator() = default;
150  const SkImageInfo& GetInfo() { return info_; }
151 
152  unsigned int GetFrameCount() const { return 1; }
153 
154  unsigned int GetPlayCount() const { return 1; }
155 
156  const ImageGenerator::FrameInfo GetFrameInfo(unsigned int frame_index) const {
157  return {std::nullopt, 0, SkCodecAnimation::DisposalMethod::kKeep};
158  }
159 
160  SkISize GetScaledDimensions(float scale) {
161  return SkISize::Make(info_.width(), info_.height());
162  }
163 
164  bool GetPixels(const SkImageInfo& info,
165  void* pixels,
166  size_t row_bytes,
167  unsigned int frame_index,
168  std::optional<unsigned int> prior_frame) {
169  return false;
170  };
171 
172  private:
173  SkImageInfo info_;
174 };
175 
176 TEST_F(ImageDecoderFixtureTest, InvalidImageResultsError) {
178  auto thread_task_runner = CreateNewThread();
179  TaskRunners runners(GetCurrentTestName(), // label
180  thread_task_runner, // platform
181  thread_task_runner, // raster
182  thread_task_runner, // ui
183  thread_task_runner // io
184  );
185 
187  thread_task_runner->PostTask([&]() {
189  ImageDecoder decoder(runners, loop->GetTaskRunner(),
190  manager.GetWeakIOManager());
191 
192  auto data = OpenFixtureAsSkData("ThisDoesNotExist.jpg");
193  ASSERT_FALSE(data);
194 
195  fml::RefPtr<ImageDescriptor> image_descriptor =
196  fml::MakeRefCounted<ImageDescriptor>(
197  std::move(data), std::make_unique<UnknownImageGenerator>());
198 
200  ASSERT_TRUE(runners.GetUITaskRunner()->RunsTasksOnCurrentThread());
201  ASSERT_FALSE(image.skia_object());
202  latch.Signal();
203  };
204  decoder.Decode(image_descriptor, 0, 0, callback);
205  });
206  latch.Wait();
207 }
208 
209 TEST_F(ImageDecoderFixtureTest, ValidImageResultsInSuccess) {
211  TaskRunners runners(GetCurrentTestName(), // label
212  CreateNewThread("platform"), // platform
213  CreateNewThread("raster"), // raster
214  CreateNewThread("ui"), // ui
215  CreateNewThread("io") // io
216  );
217 
219 
220  std::unique_ptr<TestIOManager> io_manager;
221 
222  auto release_io_manager = [&]() {
223  io_manager.reset();
224  latch.Signal();
225  };
226  auto decode_image = [&]() {
227  std::unique_ptr<ImageDecoder> image_decoder =
228  std::make_unique<ImageDecoder>(runners, loop->GetTaskRunner(),
229  io_manager->GetWeakIOManager());
230 
231  auto data = OpenFixtureAsSkData("DashInNooglerHat.jpg");
232 
233  ASSERT_TRUE(data);
234  ASSERT_GE(data->size(), 0u);
235 
236  ImageGeneratorRegistry registry;
237  std::shared_ptr<ImageGenerator> generator =
238  registry.CreateCompatibleGenerator(data);
239  ASSERT_TRUE(generator);
240 
241  auto descriptor = fml::MakeRefCounted<ImageDescriptor>(
242  std::move(data), std::move(generator));
243 
245  ASSERT_TRUE(runners.GetUITaskRunner()->RunsTasksOnCurrentThread());
246  ASSERT_TRUE(image.skia_object());
247  EXPECT_TRUE(io_manager->did_access_is_gpu_disabled_sync_switch_);
248  runners.GetIOTaskRunner()->PostTask(release_io_manager);
249  };
250  EXPECT_FALSE(io_manager->did_access_is_gpu_disabled_sync_switch_);
251  image_decoder->Decode(descriptor, descriptor->width(), descriptor->height(),
252  callback);
253  };
254 
255  auto setup_io_manager_and_decode = [&]() {
256  io_manager = std::make_unique<TestIOManager>(runners.GetIOTaskRunner());
257  runners.GetUITaskRunner()->PostTask(decode_image);
258  };
259 
260  runners.GetIOTaskRunner()->PostTask(setup_io_manager_and_decode);
261  latch.Wait();
262 }
263 
264 TEST_F(ImageDecoderFixtureTest, ExifDataIsRespectedOnDecode) {
266  TaskRunners runners(GetCurrentTestName(), // label
267  CreateNewThread("platform"), // platform
268  CreateNewThread("raster"), // raster
269  CreateNewThread("ui"), // ui
270  CreateNewThread("io") // io
271  );
272 
274 
275  std::unique_ptr<IOManager> io_manager;
276 
277  auto release_io_manager = [&]() {
278  io_manager.reset();
279  latch.Signal();
280  };
281 
282  SkISize decoded_size = SkISize::MakeEmpty();
283  auto decode_image = [&]() {
284  std::unique_ptr<ImageDecoder> image_decoder =
285  std::make_unique<ImageDecoder>(runners, loop->GetTaskRunner(),
286  io_manager->GetWeakIOManager());
287 
288  auto data = OpenFixtureAsSkData("Horizontal.jpg");
289 
290  ASSERT_TRUE(data);
291  ASSERT_GE(data->size(), 0u);
292 
293  ImageGeneratorRegistry registry;
294  std::shared_ptr<ImageGenerator> generator =
295  registry.CreateCompatibleGenerator(data);
296  ASSERT_TRUE(generator);
297 
298  auto descriptor = fml::MakeRefCounted<ImageDescriptor>(
299  std::move(data), std::move(generator));
300 
302  ASSERT_TRUE(runners.GetUITaskRunner()->RunsTasksOnCurrentThread());
303  ASSERT_TRUE(image.skia_object());
304  decoded_size = image.skia_object()->dimensions();
305  runners.GetIOTaskRunner()->PostTask(release_io_manager);
306  };
307  image_decoder->Decode(descriptor, descriptor->width(), descriptor->height(),
308  callback);
309  };
310 
311  auto setup_io_manager_and_decode = [&]() {
312  io_manager = std::make_unique<TestIOManager>(runners.GetIOTaskRunner());
313  runners.GetUITaskRunner()->PostTask(decode_image);
314  };
315 
316  runners.GetIOTaskRunner()->PostTask(setup_io_manager_and_decode);
317 
318  latch.Wait();
319 
320  ASSERT_EQ(decoded_size.width(), 600);
321  ASSERT_EQ(decoded_size.height(), 200);
322 }
323 
324 TEST_F(ImageDecoderFixtureTest, CanDecodeWithoutAGPUContext) {
326  TaskRunners runners(GetCurrentTestName(), // label
327  CreateNewThread("platform"), // platform
328  CreateNewThread("raster"), // raster
329  CreateNewThread("ui"), // ui
330  CreateNewThread("io") // io
331  );
332 
334 
335  std::unique_ptr<IOManager> io_manager;
336 
337  auto release_io_manager = [&]() {
338  io_manager.reset();
339  latch.Signal();
340  };
341 
342  auto decode_image = [&]() {
343  std::unique_ptr<ImageDecoder> image_decoder =
344  std::make_unique<ImageDecoder>(runners, loop->GetTaskRunner(),
345  io_manager->GetWeakIOManager());
346 
347  auto data = OpenFixtureAsSkData("DashInNooglerHat.jpg");
348 
349  ASSERT_TRUE(data);
350  ASSERT_GE(data->size(), 0u);
351 
352  ImageGeneratorRegistry registry;
353  std::shared_ptr<ImageGenerator> generator =
354  registry.CreateCompatibleGenerator(data);
355  ASSERT_TRUE(generator);
356 
357  auto descriptor = fml::MakeRefCounted<ImageDescriptor>(
358  std::move(data), std::move(generator));
359 
361  ASSERT_TRUE(runners.GetUITaskRunner()->RunsTasksOnCurrentThread());
362  ASSERT_TRUE(image.skia_object());
363  runners.GetIOTaskRunner()->PostTask(release_io_manager);
364  };
365  image_decoder->Decode(descriptor, descriptor->width(), descriptor->height(),
366  callback);
367  };
368 
369  auto setup_io_manager_and_decode = [&]() {
370  io_manager =
371  std::make_unique<TestIOManager>(runners.GetIOTaskRunner(), false);
372  runners.GetUITaskRunner()->PostTask(decode_image);
373  };
374 
375  runners.GetIOTaskRunner()->PostTask(setup_io_manager_and_decode);
376 
377  latch.Wait();
378 }
379 
380 TEST_F(ImageDecoderFixtureTest, CanDecodeWithResizes) {
381  const auto image_dimensions =
382  SkImage::MakeFromEncoded(OpenFixtureAsSkData("DashInNooglerHat.jpg"))
383  ->dimensions();
384 
385  ASSERT_FALSE(image_dimensions.isEmpty());
386 
387  ASSERT_NE(image_dimensions.width(), image_dimensions.height());
388 
390  TaskRunners runners(GetCurrentTestName(), // label
391  CreateNewThread("platform"), // platform
392  CreateNewThread("raster"), // raster
393  CreateNewThread("ui"), // ui
394  CreateNewThread("io") // io
395  );
396 
398  std::unique_ptr<IOManager> io_manager;
399  std::unique_ptr<ImageDecoder> image_decoder;
400 
401  // Setup the IO manager.
402  PostTaskSync(runners.GetIOTaskRunner(), [&]() {
403  io_manager = std::make_unique<TestIOManager>(runners.GetIOTaskRunner());
404  });
405 
406  // Setup the image decoder.
407  PostTaskSync(runners.GetUITaskRunner(), [&]() {
408  image_decoder = std::make_unique<ImageDecoder>(
409  runners, loop->GetTaskRunner(), io_manager->GetWeakIOManager());
410  });
411 
412  // Setup a generic decoding utility that gives us the final decoded size.
413  auto decoded_size = [&](uint32_t target_width,
414  uint32_t target_height) -> SkISize {
415  SkISize final_size = SkISize::MakeEmpty();
416  runners.GetUITaskRunner()->PostTask([&]() {
417  auto data = OpenFixtureAsSkData("DashInNooglerHat.jpg");
418 
419  ASSERT_TRUE(data);
420  ASSERT_GE(data->size(), 0u);
421 
422  ImageGeneratorRegistry registry;
423  std::shared_ptr<ImageGenerator> generator =
425  ASSERT_TRUE(generator);
426 
427  auto descriptor = fml::MakeRefCounted<ImageDescriptor>(
428  std::move(data), std::move(generator));
429 
431  ASSERT_TRUE(runners.GetUITaskRunner()->RunsTasksOnCurrentThread());
432  ASSERT_TRUE(image.skia_object());
433  final_size = image.skia_object()->dimensions();
434  latch.Signal();
435  };
436  image_decoder->Decode(descriptor, target_width, target_height, callback);
437  });
438  latch.Wait();
439  return final_size;
440  };
441 
442  ASSERT_EQ(SkISize::Make(3024, 4032), image_dimensions);
443  ASSERT_EQ(decoded_size(3024, 4032), image_dimensions);
444  ASSERT_EQ(decoded_size(100, 100), SkISize::Make(100, 100));
445 
446  // Destroy the IO manager
447  PostTaskSync(runners.GetIOTaskRunner(), [&]() { io_manager.reset(); });
448 
449  // Destroy the image decoder
450  PostTaskSync(runners.GetUITaskRunner(), [&]() { image_decoder.reset(); });
451 }
452 
453 // TODO(https://github.com/flutter/flutter/issues/81232) - disabled due to
454 // flakiness
455 TEST_F(ImageDecoderFixtureTest, DISABLED_CanResizeWithoutDecode) {
456  SkImageInfo info = {};
457  size_t row_bytes;
458  sk_sp<SkData> decompressed_data;
459  SkISize image_dimensions = SkISize::MakeEmpty();
460  {
461  auto image =
462  SkImage::MakeFromEncoded(OpenFixtureAsSkData("DashInNooglerHat.jpg"))
463  ->makeRasterImage();
464  image_dimensions = image->dimensions();
465  SkPixmap pixmap;
466  ASSERT_TRUE(image->peekPixels(&pixmap));
467  info = SkImageInfo::MakeN32Premul(image_dimensions);
468  row_bytes = pixmap.rowBytes();
469  decompressed_data =
470  SkData::MakeWithCopy(pixmap.writable_addr(), pixmap.computeByteSize());
471  }
472 
473  // This is not susceptible to changes in the underlying image decoder.
474  ASSERT_EQ(decompressed_data->size(), 48771072u);
475  ASSERT_EQ(decompressed_data->size(),
476  image_dimensions.width() * image_dimensions.height() * 4u);
477  ASSERT_EQ(row_bytes, image_dimensions.width() * 4u);
478  ASSERT_FALSE(image_dimensions.isEmpty());
479  ASSERT_NE(image_dimensions.width(), image_dimensions.height());
480 
482  TaskRunners runners(GetCurrentTestName(), // label
483  CreateNewThread("platform"), // platform
484  CreateNewThread("raster"), // raster
485  CreateNewThread("ui"), // ui
486  CreateNewThread("io") // io
487  );
488 
490  std::unique_ptr<IOManager> io_manager;
491  std::unique_ptr<ImageDecoder> image_decoder;
492 
493  // Setup the IO manager.
494  PostTaskSync(runners.GetIOTaskRunner(), [&]() {
495  io_manager = std::make_unique<TestIOManager>(runners.GetIOTaskRunner());
496  });
497 
498  // Setup the image decoder.
499  PostTaskSync(runners.GetUITaskRunner(), [&]() {
500  image_decoder = std::make_unique<ImageDecoder>(
501  runners, loop->GetTaskRunner(), io_manager->GetWeakIOManager());
502  });
503 
504  // Setup a generic decoding utility that gives us the final decoded size.
505  auto decoded_size = [&](uint32_t target_width,
506  uint32_t target_height) -> SkISize {
507  SkISize final_size = SkISize::MakeEmpty();
508  runners.GetUITaskRunner()->PostTask([&]() {
509  ASSERT_TRUE(decompressed_data);
510  ASSERT_GE(decompressed_data->size(), 0u);
511 
512  auto descriptor = fml::MakeRefCounted<ImageDescriptor>(decompressed_data,
513  info, row_bytes);
514 
516  ASSERT_TRUE(runners.GetUITaskRunner()->RunsTasksOnCurrentThread());
517  ASSERT_TRUE(image.skia_object());
518  final_size = image.skia_object()->dimensions();
519  latch.Signal();
520  };
521  image_decoder->Decode(descriptor, target_width, target_height, callback);
522  });
523  latch.Wait();
524  return final_size;
525  };
526 
527  ASSERT_EQ(SkISize::Make(3024, 4032), image_dimensions);
528  ASSERT_EQ(decoded_size(3024, 4032), image_dimensions);
529  ASSERT_EQ(decoded_size(100, 100), SkISize::Make(100, 100));
530 
531  // Destroy the IO manager
532  PostTaskSync(runners.GetIOTaskRunner(), [&]() { io_manager.reset(); });
533 
534  // Destroy the image decoder
535  PostTaskSync(runners.GetUITaskRunner(), [&]() { image_decoder.reset(); });
536 }
537 
538 // Verifies https://skia-review.googlesource.com/c/skia/+/259161 is present in
539 // Flutter.
540 TEST(ImageDecoderTest,
541  VerifyCodecRepeatCountsForGifAndWebPAreConsistentWithLoopCounts) {
542  auto gif_mapping = OpenFixtureAsSkData("hello_loop_2.gif");
543  auto webp_mapping = OpenFixtureAsSkData("hello_loop_2.webp");
544 
545  ASSERT_TRUE(gif_mapping);
546  ASSERT_TRUE(webp_mapping);
547 
548  ImageGeneratorRegistry registry;
549 
550  auto gif_generator = registry.CreateCompatibleGenerator(gif_mapping);
551  auto webp_generator = registry.CreateCompatibleGenerator(webp_mapping);
552 
553  ASSERT_TRUE(gif_generator);
554  ASSERT_TRUE(webp_generator);
555 
556  // Both fixtures have a loop count of 2.
557  ASSERT_EQ(gif_generator->GetPlayCount(), static_cast<unsigned int>(2));
558  ASSERT_EQ(webp_generator->GetPlayCount(), static_cast<unsigned int>(2));
559 }
560 
561 TEST(ImageDecoderTest, VerifySimpleDecoding) {
562  auto data = OpenFixtureAsSkData("Horizontal.jpg");
563  auto image = SkImage::MakeFromEncoded(data);
564  ASSERT_TRUE(image != nullptr);
565  ASSERT_EQ(SkISize::Make(600, 200), image->dimensions());
566 
567  ImageGeneratorRegistry registry;
568  std::shared_ptr<ImageGenerator> generator =
570  ASSERT_TRUE(generator);
571 
572  auto descriptor = fml::MakeRefCounted<ImageDescriptor>(std::move(data),
573  std::move(generator));
574 
575  ASSERT_EQ(ImageFromCompressedData(descriptor.get(), 6, 2,
577  ->dimensions(),
578  SkISize::Make(6, 2));
579 }
580 
581 TEST(ImageDecoderTest, VerifySubpixelDecodingPreservesExifOrientation) {
582  auto data = OpenFixtureAsSkData("Horizontal.jpg");
583 
584  ImageGeneratorRegistry registry;
585  std::shared_ptr<ImageGenerator> generator =
587  ASSERT_TRUE(generator);
588  auto descriptor =
589  fml::MakeRefCounted<ImageDescriptor>(data, std::move(generator));
590 
591  auto image = SkImage::MakeFromEncoded(data);
592  ASSERT_TRUE(image != nullptr);
593  ASSERT_EQ(SkISize::Make(600, 200), image->dimensions());
594 
595  auto decode = [descriptor](uint32_t target_width, uint32_t target_height) {
596  return ImageFromCompressedData(descriptor.get(), target_width,
597  target_height, fml::tracing::TraceFlow(""));
598  };
599 
600  auto expected_data = OpenFixtureAsSkData("Horizontal.png");
601  ASSERT_TRUE(expected_data != nullptr);
602  ASSERT_FALSE(expected_data->isEmpty());
603 
604  auto assert_image = [&](auto decoded_image) {
605  ASSERT_EQ(decoded_image->dimensions(), SkISize::Make(300, 100));
606  ASSERT_TRUE(decoded_image->encodeToData(SkEncodedImageFormat::kPNG, 100)
607  ->equals(expected_data.get()));
608  };
609 
610  assert_image(decode(300, 100));
611 }
612 
614  MultiFrameCodecCanBeCollectedBeforeIOTasksFinish) {
615  // This test verifies that the MultiFrameCodec safely shares state between
616  // tasks on the IO and UI runners, and does not allow unsafe memory access if
617  // the UI object is collected while the IO thread still has pending decode
618  // work. This could happen in a real application if the engine is collected
619  // while a multi-frame image is decoding. To exercise this, the test:
620  // - Starts a Dart VM
621  // - Latches the IO task runner
622  // - Create a MultiFrameCodec for an animated gif pointed to a callback
623  // in the Dart fixture
624  // - Calls getNextFrame on the UI task runner
625  // - Collects the MultiFrameCodec object before unlatching the IO task
626  // runner.
627  // - Unlatches the IO task runner
628  auto settings = CreateSettingsForFixture();
629  auto vm_ref = DartVMRef::Create(settings);
630  auto vm_data = vm_ref.GetVMData();
631 
632  auto gif_mapping = OpenFixtureAsSkData("hello_loop_2.gif");
633 
634  ASSERT_TRUE(gif_mapping);
635 
636  ImageGeneratorRegistry registry;
637  std::shared_ptr<ImageGenerator> gif_generator =
638  registry.CreateCompatibleGenerator(gif_mapping);
639  ASSERT_TRUE(gif_generator);
640 
641  TaskRunners runners(GetCurrentTestName(), // label
642  CreateNewThread("platform"), // platform
643  CreateNewThread("raster"), // raster
644  CreateNewThread("ui"), // ui
645  CreateNewThread("io") // io
646  );
647 
649  std::unique_ptr<TestIOManager> io_manager;
650 
651  // Setup the IO manager.
652  PostTaskSync(runners.GetIOTaskRunner(), [&]() {
653  io_manager = std::make_unique<TestIOManager>(runners.GetIOTaskRunner());
654  });
655 
656  auto isolate = RunDartCodeInIsolate(vm_ref, settings, runners, "main", {},
658  io_manager->GetWeakIOManager());
659 
660  // Latch the IO task runner.
661  runners.GetIOTaskRunner()->PostTask([&]() { io_latch.Wait(); });
662 
663  PostTaskSync(runners.GetUITaskRunner(), [&]() {
664  fml::AutoResetWaitableEvent isolate_latch;
666  EXPECT_TRUE(isolate->RunInIsolateScope([&]() -> bool {
667  Dart_Handle library = Dart_RootLibrary();
668  if (Dart_IsError(library)) {
669  isolate_latch.Signal();
670  return false;
671  }
672  Dart_Handle closure =
673  Dart_GetField(library, Dart_NewStringFromCString("frameCallback"));
674  if (Dart_IsError(closure) || !Dart_IsClosure(closure)) {
675  isolate_latch.Signal();
676  return false;
677  }
678 
679  codec = fml::MakeRefCounted<MultiFrameCodec>(std::move(gif_generator));
680  codec->getNextFrame(closure);
681  codec = nullptr;
682  isolate_latch.Signal();
683  return true;
684  }));
685  isolate_latch.Wait();
686 
687  EXPECT_FALSE(codec);
688 
689  io_latch.Signal();
690  });
691 
692  // Destroy the IO manager
693  PostTaskSync(runners.GetIOTaskRunner(), [&]() { io_manager.reset(); });
694 }
695 
696 TEST_F(ImageDecoderFixtureTest, MultiFrameCodecDidAccessGpuDisabledSyncSwitch) {
697  auto settings = CreateSettingsForFixture();
698  auto vm_ref = DartVMRef::Create(settings);
699  auto vm_data = vm_ref.GetVMData();
700 
701  auto gif_mapping = OpenFixtureAsSkData("hello_loop_2.gif");
702 
703  ASSERT_TRUE(gif_mapping);
704 
705  ImageGeneratorRegistry registry;
706  std::shared_ptr<ImageGenerator> gif_generator =
707  registry.CreateCompatibleGenerator(gif_mapping);
708  ASSERT_TRUE(gif_generator);
709 
710  TaskRunners runners(GetCurrentTestName(), // label
711  CreateNewThread("platform"), // platform
712  CreateNewThread("raster"), // raster
713  CreateNewThread("ui"), // ui
714  CreateNewThread("io") // io
715  );
716 
717  std::unique_ptr<TestIOManager> io_manager;
719 
720  // Setup the IO manager.
721  PostTaskSync(runners.GetIOTaskRunner(), [&]() {
722  io_manager = std::make_unique<TestIOManager>(runners.GetIOTaskRunner());
723  });
724 
725  auto isolate = RunDartCodeInIsolate(vm_ref, settings, runners, "main", {},
727  io_manager->GetWeakIOManager());
728 
729  PostTaskSync(runners.GetUITaskRunner(), [&]() {
730  fml::AutoResetWaitableEvent isolate_latch;
731 
732  EXPECT_TRUE(isolate->RunInIsolateScope([&]() -> bool {
733  Dart_Handle library = Dart_RootLibrary();
734  if (Dart_IsError(library)) {
735  isolate_latch.Signal();
736  return false;
737  }
738  Dart_Handle closure =
739  Dart_GetField(library, Dart_NewStringFromCString("frameCallback"));
740  if (Dart_IsError(closure) || !Dart_IsClosure(closure)) {
741  isolate_latch.Signal();
742  return false;
743  }
744 
745  EXPECT_FALSE(io_manager->did_access_is_gpu_disabled_sync_switch_);
746  codec = fml::MakeRefCounted<MultiFrameCodec>(std::move(gif_generator));
747  codec->getNextFrame(closure);
748  isolate_latch.Signal();
749  return true;
750  }));
751  isolate_latch.Wait();
752  });
753 
754  PostTaskSync(runners.GetIOTaskRunner(), [&]() {
755  EXPECT_TRUE(io_manager->did_access_is_gpu_disabled_sync_switch_);
756  });
757 
758  // Destroy the Isolate
759  isolate = nullptr;
760 
761  // Destroy the MultiFrameCodec
762  PostTaskSync(runners.GetUITaskRunner(), [&]() { codec = nullptr; });
763 
764  // Destroy the IO manager
765  PostTaskSync(runners.GetIOTaskRunner(), [&]() { io_manager.reset(); });
766 }
767 
768 } // namespace testing
769 } // namespace flutter
bool GetPixels(const SkImageInfo &info, void *pixels, size_t row_bytes, unsigned int frame_index, std::optional< unsigned int > prior_frame)
Decode the image into a given buffer. This method is currently always used for sub-pixel image decodi...
const ImageGenerator::FrameInfo GetFrameInfo(unsigned int frame_index) const
Get information about a single frame in the context of a multi-frame image, useful for animation and ...
std::shared_ptr< const fml::SyncSwitch > GetIsGpuDisabledSyncSwitch() override
std::string GetDefaultKernelFilePath()
Returns the default path to kernel_blob.bin. This file is within the directory returned by GetFixture...
Definition: testing.cc:17
Info about a single frame in the context of a multi-frame image, useful for animation and blending...
An Image generator that pretends it can&#39;t recognize the data it was given.
Keeps a priority-ordered registry of image generator builders to be used when decoding images...
static void RunNowOrPostTask(fml::RefPtr< fml::TaskRunner > runner, const fml::closure &task)
Definition: task_runner.cc:55
std::string GetCurrentTestName()
Gets the name of the currently running test. This is useful in generating logs or assets based on tes...
Definition: testing.cc:13
SkISize GetScaledDimensions(float scale)
Given a scale value, find the closest image size that can be used for efficiently decoding the image...
unsigned int GetPlayCount() const
The number of times an animated image should play through before playback stops.
unsigned int GetFrameCount() const
Get the number of frames that the encoded image stores. This method is always expected to be called b...
Definition: ref_ptr.h:252
fml::WeakPtr< GrDirectContext > GetResourceContext() const override
DEF_SWITCHES_START aot vmservice shared library name
Definition: switches.h:32
TestIOManager(fml::RefPtr< fml::TaskRunner > task_runner, bool has_gpu_context=true)
const SkImageInfo & GetInfo()
Returns basic information about the contents of the encoded image. This information can almost always...
std::unique_ptr< AutoIsolateShutdown > RunDartCodeInIsolate(DartVMRef &vm_ref, const Settings &settings, const TaskRunners &task_runners, std::string entrypoint, const std::vector< std::string > &args, const std::string &kernel_file_path, fml::WeakPtr< IOManager > io_manager, std::shared_ptr< VolatilePathTracker > volatile_path_tracker)
FlKeyEvent FlKeyResponderAsyncCallback callback
Definition: ascii_trie.cc:9
static DartVMRef Create(Settings settings, fml::RefPtr< const DartSnapshot > vm_snapshot=nullptr, fml::RefPtr< const DartSnapshot > isolate_snapshot=nullptr)
fml::UniqueFD OpenDirectory(const char *path, bool create_if_necessary, FilePermission permission)
Definition: file_posix.cc:96
std::function< void(SkiaGPUObject< SkImage >)> ImageResult
Definition: image_decoder.h:41
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 observatory The hostname IP address on which the Dart Observatory should be served If not defaults to or::depending on whether ipv6 is specified disable Disable the Dart Observatory The observatory is never available in release mode Bind to the IPv6 localhost address for the Dart Observatory Ignored if observatory 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 skia deterministic Skips the call to thus avoiding swapping out some Skia function pointers based on available CPU features This is used to obtain deterministic behavior in Skia rendering disable service auth Disable the requirement for authentication codes for communicating with the VM service start Start the application paused in the Dart debugger trace Trace Skia calls This is useful when debugging the GPU threed By Skia tracing is not enabled to reduce the number of traced events trace Filters out all trace events except those that are specified in this comma separated list of allowed prefixes cache Only cache the shader in SkSL instead of binary or GLSL This should only be used during development phases The generated SkSLs can later be used in the release build for shader precompilation at launch in order to eliminate the shader compile jank trace Trace to the system 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 prefetched default font manager
Definition: switches.h:177
fml::RefPtr< flutter::SkiaUnrefQueue > GetSkiaUnrefQueue() const override
std::function< void()> closure
Definition: closure.h:14
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: switches.h:41
std::shared_ptr< ImageGenerator > CreateCompatibleGenerator(sk_sp< SkData > buffer)
Walks the list of image generator builders in descending priority order until a compatible ImageGener...
static std::shared_ptr< ConcurrentMessageLoop > Create(size_t worker_count=std::thread::hardware_concurrency())
const char * GetFixturesPath()
Returns the directory containing the test fixture for the target if this target has fixtures configur...
sk_sp< SkImage > ImageFromCompressedData(ImageDescriptor *descriptor, uint32_t target_width, uint32_t target_height, const fml::tracing::TraceFlow &flow)
fml::WeakPtr< IOManager > GetWeakIOManager() const override
TEST_F(BackdropFilterLayerTest, PaintingEmptyLayerDies)
fml::RefPtr< fml::TaskRunner > GetIOTaskRunner() const
Definition: task_runners.cc:38
fml::RefPtr< fml::TaskRunner > GetUITaskRunner() const
Definition: task_runners.cc:34
RefPtr< T > MakeRefCounted(Args &&... args)
Definition: ref_ptr.h:244
The minimal interface necessary for defining a decoder that can be used for both single and multi-fra...
static sk_sp< SkData > OpenFixtureAsSkData(const char *name)
#define FML_CHECK(condition)
Definition: logging.h:68
void PostTaskSync(fml::RefPtr< fml::TaskRunner > task_runner, const std::function< void()> &function)
virtual bool RunsTasksOnCurrentThread()
Definition: task_runner.cc:43
static std::unique_ptr< FileMapping > CreateReadOnly(const std::string &path)
Definition: mapping.cc:18
virtual void PostTask(const fml::closure &task) override
Definition: task_runner.cc:24
TEST(DisplayListCanvas, DrawPaint)