diff --git a/engine/src/flutter/lib/ui/painting/image_decoder_impeller.cc b/engine/src/flutter/lib/ui/painting/image_decoder_impeller.cc index e72d63bc7a..40b6d9e01c 100644 --- a/engine/src/flutter/lib/ui/painting/image_decoder_impeller.cc +++ b/engine/src/flutter/lib/ui/painting/image_decoder_impeller.cc @@ -405,7 +405,7 @@ void ImageDecoderImpeller::UploadTextureToPrivate( const SkImageInfo& image_info, const std::shared_ptr& bitmap, const std::optional& resize_info, - const std::shared_ptr& gpu_disabled_switch) { + const std::shared_ptr& gpu_disabled_switch) { TRACE_EVENT0("impeller", __FUNCTION__); if (!context) { result(nullptr, "No Impeller context is available"); diff --git a/engine/src/flutter/lib/ui/painting/image_decoder_impeller.h b/engine/src/flutter/lib/ui/painting/image_decoder_impeller.h index 3a1e5172a9..61952a2184 100644 --- a/engine/src/flutter/lib/ui/painting/image_decoder_impeller.h +++ b/engine/src/flutter/lib/ui/painting/image_decoder_impeller.h @@ -88,7 +88,7 @@ class ImageDecoderImpeller final : public ImageDecoder { const SkImageInfo& image_info, const std::shared_ptr& bitmap, const std::optional& resize_info, - const std::shared_ptr& gpu_disabled_switch); + const std::shared_ptr& gpu_disabled_switch); /// @brief Create a texture from the provided bitmap. /// @param context The Impeller graphics context. diff --git a/engine/src/flutter/lib/ui/painting/image_decoder_unittests.cc b/engine/src/flutter/lib/ui/painting/image_decoder_unittests.cc index 968c09d344..c89386a487 100644 --- a/engine/src/flutter/lib/ui/painting/image_decoder_unittests.cc +++ b/engine/src/flutter/lib/ui/painting/image_decoder_unittests.cc @@ -385,6 +385,9 @@ TEST_F(ImageDecoderFixtureTest, ImpellerUploadToSharedNoGpu) { ASSERT_EQ(no_gpu_access_context->command_buffer_count_, 0ul); ASSERT_EQ(result.second, ""); EXPECT_EQ(no_gpu_access_context->DidDisposeResources(), true); + EXPECT_EQ( + result.first->impeller_texture()->GetTextureDescriptor().storage_mode, + impeller::StorageMode::kHostVisible); no_gpu_access_context->FlushTasks(/*fail=*/true); } @@ -1007,174 +1010,6 @@ TEST_F(ImageDecoderFixtureTest, PostTaskSync(runners.GetIOTaskRunner(), [&]() { io_manager.reset(); }); } -TEST_F(ImageDecoderFixtureTest, MultiFrameCodecDidAccessGpuDisabledSyncSwitch) { - auto settings = CreateSettingsForFixture(); - auto vm_ref = DartVMRef::Create(settings); - auto vm_data = vm_ref.GetVMData(); - - auto gif_mapping = flutter::testing::OpenFixtureAsSkData("hello_loop_2.gif"); - - ASSERT_TRUE(gif_mapping); - - ImageGeneratorRegistry registry; - std::shared_ptr gif_generator = - registry.CreateCompatibleGenerator(gif_mapping); - ASSERT_TRUE(gif_generator); - - TaskRunners runners(GetCurrentTestName(), // label - CreateNewThread("platform"), // platform - CreateNewThread("raster"), // raster - CreateNewThread("ui"), // ui - CreateNewThread("io") // io - ); - - std::unique_ptr io_manager; - fml::RefPtr codec; - fml::AutoResetWaitableEvent latch; - - auto validate_frame_callback = [&latch](Dart_NativeArguments args) { - EXPECT_FALSE(Dart_IsNull(Dart_GetNativeArgument(args, 0))); - latch.Signal(); - }; - - AddNativeCallback("ValidateFrameCallback", - CREATE_NATIVE_ENTRY(validate_frame_callback)); - // Setup the IO manager. - PostTaskSync(runners.GetIOTaskRunner(), [&]() { - io_manager = std::make_unique(runners.GetIOTaskRunner()); - }); - - auto isolate = RunDartCodeInIsolate(vm_ref, settings, runners, "main", {}, - GetDefaultKernelFilePath(), - io_manager->GetWeakIOManager()); - - PostTaskSync(runners.GetUITaskRunner(), [&]() { - fml::AutoResetWaitableEvent isolate_latch; - - EXPECT_TRUE(isolate->RunInIsolateScope([&]() -> bool { - Dart_Handle library = Dart_RootLibrary(); - if (Dart_IsError(library)) { - isolate_latch.Signal(); - return false; - } - Dart_Handle closure = - Dart_GetField(library, Dart_NewStringFromCString("frameCallback")); - if (Dart_IsError(closure) || !Dart_IsClosure(closure)) { - isolate_latch.Signal(); - return false; - } - - EXPECT_FALSE(io_manager->did_access_is_gpu_disabled_sync_switch_); - codec = fml::MakeRefCounted(std::move(gif_generator)); - codec->getNextFrame(closure); - isolate_latch.Signal(); - return true; - })); - isolate_latch.Wait(); - }); - - PostTaskSync(runners.GetIOTaskRunner(), [&]() { - EXPECT_TRUE(io_manager->did_access_is_gpu_disabled_sync_switch_); - }); - - latch.Wait(); - - // Destroy the Isolate - isolate = nullptr; - - // Destroy the MultiFrameCodec - PostTaskSync(runners.GetUITaskRunner(), [&]() { codec = nullptr; }); - - // Destroy the IO manager - PostTaskSync(runners.GetIOTaskRunner(), [&]() { io_manager.reset(); }); -} - -TEST_F(ImageDecoderFixtureTest, MultiFrameCodecIsPausedWhenGPUIsUnavailable) { - auto settings = CreateSettingsForFixture(); - settings.enable_impeller = true; - auto vm_ref = DartVMRef::Create(settings); - auto vm_data = vm_ref.GetVMData(); - - auto gif_mapping = flutter::testing::OpenFixtureAsSkData("hello_loop_2.gif"); - - ASSERT_TRUE(gif_mapping); - - ImageGeneratorRegistry registry; - std::shared_ptr gif_generator = - registry.CreateCompatibleGenerator(gif_mapping); - ASSERT_TRUE(gif_generator); - - TaskRunners runners(GetCurrentTestName(), // label - CreateNewThread("platform"), // platform - CreateNewThread("raster"), // raster - CreateNewThread("ui"), // ui - CreateNewThread("io") // io - ); - - std::unique_ptr io_manager; - fml::RefPtr codec; - fml::AutoResetWaitableEvent latch; - - auto validate_frame_callback = [&latch](Dart_NativeArguments args) { - EXPECT_FALSE(Dart_IsNull(Dart_GetNativeArgument(args, 0))); - latch.Signal(); - }; - - AddNativeCallback("ValidateFrameCallback", - CREATE_NATIVE_ENTRY(validate_frame_callback)); - - // Setup the IO manager. - PostTaskSync(runners.GetIOTaskRunner(), [&]() { - io_manager = std::make_unique(runners.GetIOTaskRunner()); - // Mark GPU disabled. - io_manager->SetGpuDisabled(true); - }); - - auto isolate = RunDartCodeInIsolate(vm_ref, settings, runners, "main", {}, - GetDefaultKernelFilePath(), - io_manager->GetWeakIOManager()); - - PostTaskSync(runners.GetUITaskRunner(), [&]() { - fml::AutoResetWaitableEvent isolate_latch; - - EXPECT_TRUE(isolate->RunInIsolateScope([&]() -> bool { - Dart_Handle library = Dart_RootLibrary(); - if (Dart_IsError(library)) { - isolate_latch.Signal(); - return false; - } - Dart_Handle closure = - Dart_GetField(library, Dart_NewStringFromCString("frameCallback")); - if (Dart_IsError(closure) || !Dart_IsClosure(closure)) { - isolate_latch.Signal(); - return false; - } - - EXPECT_FALSE(io_manager->did_access_is_gpu_disabled_sync_switch_); - codec = fml::MakeRefCounted(std::move(gif_generator)); - codec->getNextFrame(closure); - isolate_latch.Signal(); - return true; - })); - isolate_latch.Wait(); - }); - - PostTaskSync(runners.GetIOTaskRunner(), [&]() { - EXPECT_TRUE(io_manager->did_access_is_gpu_disabled_sync_switch_); - }); - - latch.Wait(); - - // Destroy the Isolate - isolate = nullptr; - - // Destroy the MultiFrameCodec - PostTaskSync(runners.GetUITaskRunner(), [&]() { codec = nullptr; }); - - // Destroy the IO manager - PostTaskSync(runners.GetIOTaskRunner(), [&]() { io_manager.reset(); }); -} - TEST_F(ImageDecoderFixtureTest, NullCheckBuffer) { auto context = std::make_shared(); auto allocator = ImpellerAllocator(context->GetResourceAllocator()); diff --git a/engine/src/flutter/lib/ui/painting/multi_frame_codec.cc b/engine/src/flutter/lib/ui/painting/multi_frame_codec.cc index 73b5550c9e..ddcd42dc5a 100644 --- a/engine/src/flutter/lib/ui/painting/multi_frame_codec.cc +++ b/engine/src/flutter/lib/ui/painting/multi_frame_codec.cc @@ -6,10 +6,12 @@ #include +#include "display_list/image/dl_image.h" #include "flutter/fml/make_copyable.h" #include "flutter/lib/ui/painting/display_list_image_gpu.h" #include "flutter/lib/ui/painting/image.h" #if IMPELLER_SUPPORTS_RENDERING +#include "flutter/impeller/renderer/context.h" #include "flutter/lib/ui/painting/image_decoder_impeller.h" #endif // IMPELLER_SUPPORTS_RENDERING #include "third_party/dart/runtime/include/dart_api.h" @@ -144,10 +146,36 @@ MultiFrameCodec::State::GetNextFrameImage( #if IMPELLER_SUPPORTS_RENDERING if (is_impeller_enabled_) { +#ifdef FML_OS_IOS // This is safe regardless of whether the GPU is available or not because // without mipmap creation there is no command buffer encoding done. return ImageDecoderImpeller::UploadTextureToStorage( impeller_context, std::make_shared(bitmap)); +#else + sk_sp dl_image; + std::string error_message; + auto mapping = std::make_unique( + reinterpret_cast(bitmap.getAddr(0, 0)), // data + bitmap.dimensions().area() * info.bytesPerPixel(), // size + [bitmap](auto, auto) mutable { bitmap.reset(); } // proc + ); + std::shared_ptr device_buffer = + impeller_context->GetResourceAllocator()->CreateBufferWithCopy( + *mapping); + if (!device_buffer) { + return std::make_pair(nullptr, "Failed to allocate staging buffer."); + } + + ImageDecoderImpeller::UploadTextureToPrivate( + [&](sk_sp image, std::string message) { + dl_image = std::move(image); + error_message = std::move(message); + }, + impeller_context, device_buffer, info, + std::make_shared(bitmap), std::nullopt, + gpu_disable_sync_switch); + return std::make_pair(dl_image, error_message); +#endif } #endif // IMPELLER_SUPPORTS_RENDERING