| // Copyright 2013 The Flutter Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #define FML_USED_ON_EMBEDDER |
| |
| #include "flutter/common/task_runners.h" |
| #include "flutter/fml/synchronization/waitable_event.h" |
| #include "flutter/lib/ui/painting/canvas.h" |
| #include "flutter/lib/ui/painting/image.h" |
| #include "flutter/lib/ui/painting/picture.h" |
| #include "flutter/lib/ui/painting/picture_recorder.h" |
| #include "flutter/runtime/dart_vm.h" |
| #include "flutter/shell/common/shell_test.h" |
| #include "flutter/shell/common/thread_host.h" |
| #include "flutter/testing/testing.h" |
| |
| namespace flutter { |
| namespace testing { |
| |
| class ImageDisposeTest : public ShellTest { |
| public: |
| template <class T> |
| T* GetNativePeer(Dart_Handle handle) { |
| intptr_t peer = 0; |
| auto native_handle = Dart_GetNativeInstanceField( |
| handle, tonic::DartWrappable::kPeerIndex, &peer); |
| EXPECT_FALSE(Dart_IsError(native_handle)) << Dart_GetError(native_handle); |
| return reinterpret_cast<T*>(peer); |
| } |
| |
| // Used to wait on Dart callbacks or Shell task runner flushing |
| fml::AutoResetWaitableEvent message_latch_; |
| |
| sk_sp<DisplayList> current_display_list_; |
| sk_sp<DlImage> current_image_; |
| }; |
| |
| TEST_F(ImageDisposeTest, ImageReleasedAfterFrameAndDisposePictureAndLayer) { |
| auto native_capture_image_and_picture = [&](Dart_NativeArguments args) { |
| auto image_handle = Dart_GetNativeArgument(args, 0); |
| auto native_image_handle = |
| Dart_GetField(image_handle, Dart_NewStringFromCString("_image")); |
| ASSERT_FALSE(Dart_IsError(native_image_handle)) |
| << Dart_GetError(native_image_handle); |
| ASSERT_FALSE(Dart_IsNull(native_image_handle)); |
| CanvasImage* image = GetNativePeer<CanvasImage>(native_image_handle); |
| Picture* picture = GetNativePeer<Picture>(Dart_GetNativeArgument(args, 1)); |
| ASSERT_FALSE(image->image()->unique()); |
| ASSERT_FALSE(picture->display_list()->unique()); |
| current_display_list_ = picture->display_list(); |
| current_image_ = image->image(); |
| }; |
| |
| auto native_finish = [&](Dart_NativeArguments args) { |
| message_latch_.Signal(); |
| }; |
| |
| Settings settings = CreateSettingsForFixture(); |
| auto task_runner = CreateNewThread(); |
| TaskRunners task_runners("test", // label |
| GetCurrentTaskRunner(), // platform |
| task_runner, // raster |
| task_runner, // ui |
| task_runner // io |
| ); |
| |
| AddNativeCallback("CaptureImageAndPicture", |
| CREATE_NATIVE_ENTRY(native_capture_image_and_picture)); |
| AddNativeCallback("Finish", CREATE_NATIVE_ENTRY(native_finish)); |
| |
| std::unique_ptr<Shell> shell = CreateShell(std::move(settings), task_runners); |
| |
| ASSERT_TRUE(shell->IsSetup()); |
| |
| SetViewportMetrics(shell.get(), 800, 600); |
| |
| shell->GetPlatformView()->NotifyCreated(); |
| |
| auto configuration = RunConfiguration::InferFromSettings(settings); |
| configuration.SetEntrypoint("pumpImage"); |
| |
| shell->RunEngine(std::move(configuration), [&](auto result) { |
| ASSERT_EQ(result, Engine::RunStatus::Success); |
| }); |
| |
| message_latch_.Wait(); |
| |
| ASSERT_TRUE(current_display_list_); |
| ASSERT_TRUE(current_image_); |
| |
| // Force a drain the SkiaUnrefQueue. The engine does this normally as frames |
| // pump, but we force it here to make the test more deterministic. |
| message_latch_.Reset(); |
| task_runner->PostTask([&, io_manager = shell->GetIOManager()]() { |
| io_manager->GetSkiaUnrefQueue()->Drain(); |
| message_latch_.Signal(); |
| }); |
| message_latch_.Wait(); |
| |
| if (current_display_list_) { |
| EXPECT_TRUE(current_display_list_->unique()); |
| current_display_list_.reset(); |
| } |
| |
| EXPECT_TRUE(current_image_->unique()); |
| current_image_.reset(); |
| |
| shell->GetPlatformView()->NotifyDestroyed(); |
| DestroyShell(std::move(shell), std::move(task_runners)); |
| } |
| |
| } // namespace testing |
| } // namespace flutter |