| // 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. |
| |
| #include "flutter/shell/common/snapshot_controller_impeller.h" |
| |
| #include <algorithm> |
| |
| #include "flutter/flow/surface.h" |
| #include "flutter/fml/build_config.h" |
| #include "flutter/fml/trace_event.h" |
| #include "flutter/impeller/display_list/dl_dispatcher.h" |
| #include "flutter/impeller/display_list/dl_image_impeller.h" |
| #include "flutter/impeller/geometry/size.h" |
| #include "flutter/shell/common/snapshot_controller.h" |
| #include "impeller/entity/contents/runtime_effect_contents.h" |
| |
| namespace flutter { |
| |
| namespace { |
| |
| sk_sp<DlImage> DoMakeRasterSnapshot( |
| const sk_sp<DisplayList>& display_list, |
| DlISize size, |
| const std::shared_ptr<impeller::AiksContext>& context, |
| SnapshotPixelFormat pixel_format) { |
| TRACE_EVENT0("flutter", __FUNCTION__); |
| if (!context) { |
| return nullptr; |
| } |
| // Determine render target size. |
| auto max_size = context->GetContext() |
| ->GetResourceAllocator() |
| ->GetMaxTextureSizeSupported(); |
| double scale_factor_x = |
| static_cast<double>(max_size.width) / static_cast<double>(size.width); |
| double scale_factor_y = |
| static_cast<double>(max_size.height) / static_cast<double>(size.height); |
| double scale_factor = std::min({1.0, scale_factor_x, scale_factor_y}); |
| |
| auto render_target_size = impeller::ISize(size.width, size.height); |
| |
| // Scale down the render target size to the max supported by the |
| // GPU if necessary. Exceeding the max would otherwise cause a |
| // null result. |
| if (scale_factor < 1.0) { |
| render_target_size.width *= scale_factor; |
| render_target_size.height *= scale_factor; |
| } |
| |
| std::optional<impeller::PixelFormat> impeller_pixel_format; |
| switch (pixel_format) { |
| case SnapshotPixelFormat::kDontCare: |
| impeller_pixel_format = std::nullopt; |
| break; |
| case SnapshotPixelFormat::kRGBA32Float: |
| impeller_pixel_format = impeller::PixelFormat::kR32G32B32A32Float; |
| break; |
| case SnapshotPixelFormat::kR32Float: |
| impeller_pixel_format = impeller::PixelFormat::kR32Float; |
| break; |
| } |
| |
| return impeller::DlImageImpeller::Make( |
| impeller::DisplayListToTexture(display_list, render_target_size, *context, |
| /*reset_host_buffer=*/false, |
| /*generate_mips=*/true, |
| impeller_pixel_format), |
| DlImage::OwningContext::kRaster); |
| } |
| |
| sk_sp<DlImage> DoMakeRasterSnapshot( |
| const sk_sp<DisplayList>& display_list, |
| DlISize size, |
| const SnapshotController::Delegate& delegate, |
| SnapshotPixelFormat pixel_format) { |
| // Ensure that the current thread has a rendering context. This must be done |
| // before calling GetAiksContext because constructing the AiksContext may |
| // invoke graphics APIs. |
| std::unique_ptr<Surface> pbuffer_surface; |
| if (delegate.GetSurface()) { |
| delegate.GetSurface()->MakeRenderContextCurrent(); |
| } else if (delegate.GetSnapshotSurfaceProducer()) { |
| pbuffer_surface = |
| delegate.GetSnapshotSurfaceProducer()->CreateSnapshotSurface(); |
| if (pbuffer_surface) { |
| pbuffer_surface->MakeRenderContextCurrent(); |
| } |
| } |
| |
| return DoMakeRasterSnapshot(display_list, size, delegate.GetAiksContext(), |
| pixel_format); |
| } |
| |
| sk_sp<DlImage> DoMakeRasterSnapshot( |
| sk_sp<DisplayList> display_list, |
| DlISize picture_size, |
| const std::shared_ptr<const fml::SyncSwitch>& sync_switch, |
| const std::shared_ptr<impeller::AiksContext>& context, |
| SnapshotPixelFormat pixel_format) { |
| sk_sp<DlImage> result; |
| sync_switch->Execute(fml::SyncSwitch::Handlers() |
| .SetIfTrue([&] { |
| // Do nothing. |
| }) |
| .SetIfFalse([&] { |
| result = DoMakeRasterSnapshot( |
| display_list, picture_size, context, |
| pixel_format); |
| })); |
| |
| return result; |
| } |
| } // namespace |
| |
| void SnapshotControllerImpeller::MakeRasterSnapshot( |
| sk_sp<DisplayList> display_list, |
| DlISize picture_size, |
| std::function<void(const sk_sp<DlImage>&)> callback, |
| SnapshotPixelFormat pixel_format) { |
| std::shared_ptr<const fml::SyncSwitch> sync_switch = |
| GetDelegate().GetIsGpuDisabledSyncSwitch(); |
| sync_switch->Execute( |
| fml::SyncSwitch::Handlers() |
| .SetIfTrue([&] { |
| std::shared_ptr<impeller::AiksContext> context = |
| GetDelegate().GetAiksContext(); |
| if (context) { |
| context->GetContext()->StoreTaskForGPU( |
| [context, sync_switch, display_list = std::move(display_list), |
| picture_size, callback, pixel_format] { |
| callback(DoMakeRasterSnapshot(display_list, picture_size, |
| sync_switch, context, |
| pixel_format)); |
| }, |
| [callback]() { callback(nullptr); }); |
| } else { |
| #if FML_OS_IOS_SIMULATOR |
| callback(impeller::DlImageImpeller::Make( |
| nullptr, DlImage::OwningContext::kRaster, |
| /*is_fake_image=*/true)); |
| #else |
| callback(nullptr); |
| |
| #endif // FML_OS_IOS_SIMULATOR |
| } |
| }) |
| .SetIfFalse([&] { |
| #if FML_OS_IOS_SIMULATOR |
| if (!GetDelegate().GetAiksContext()) { |
| callback(impeller::DlImageImpeller::Make( |
| nullptr, DlImage::OwningContext::kRaster, |
| /*is_fake_image=*/true)); |
| return; |
| } |
| #endif |
| callback(DoMakeRasterSnapshot(display_list, picture_size, |
| GetDelegate(), pixel_format)); |
| })); |
| } |
| |
| sk_sp<DlImage> SnapshotControllerImpeller::MakeRasterSnapshotSync( |
| sk_sp<DisplayList> display_list, |
| DlISize picture_size, |
| SnapshotPixelFormat pixel_format) { |
| return DoMakeRasterSnapshot(display_list, picture_size, GetDelegate(), |
| pixel_format); |
| } |
| |
| sk_sp<DlImage> SnapshotControllerImpeller::MakeTextureImage( |
| sk_sp<SkImage> image, |
| SnapshotPixelFormat pixel_format) { |
| auto aiks_context = GetDelegate().GetAiksContext(); |
| if (!aiks_context) { |
| return nullptr; |
| } |
| auto context = aiks_context->GetContext(); |
| if (!context) { |
| return nullptr; |
| } |
| |
| impeller::TextureDescriptor desc; |
| desc.storage_mode = impeller::StorageMode::kDevicePrivate; |
| desc.format = impeller::PixelFormat::kR8G8B8A8UNormInt; |
| desc.size = impeller::ISize(image->width(), image->height()); |
| desc.mip_count = 1; |
| |
| auto texture = context->GetResourceAllocator()->CreateTexture(desc); |
| if (!texture) { |
| return nullptr; |
| } |
| |
| size_t byte_size = image->width() * image->height() * 4; |
| auto buffer = context->GetResourceAllocator()->CreateBuffer( |
| impeller::DeviceBufferDescriptor{ |
| .storage_mode = impeller::StorageMode::kHostVisible, |
| .size = byte_size, |
| }); |
| |
| if (!buffer) { |
| return nullptr; |
| } |
| |
| { |
| uint8_t* map = buffer->OnGetContents(); |
| if (!map) { |
| return nullptr; |
| } |
| SkImageInfo info = |
| SkImageInfo::Make(image->width(), image->height(), |
| kRGBA_8888_SkColorType, kPremul_SkAlphaType); |
| if (!image->readPixels(info, map, image->width() * 4, 0, 0)) { |
| return nullptr; |
| } |
| buffer->Flush(impeller::Range(0, byte_size)); |
| } |
| |
| auto command_buffer = context->CreateCommandBuffer(); |
| if (!command_buffer) { |
| return nullptr; |
| } |
| auto blit_pass = command_buffer->CreateBlitPass(); |
| if (!blit_pass) { |
| return nullptr; |
| } |
| |
| blit_pass->AddCopy( |
| impeller::BufferView{buffer, impeller::Range(0, byte_size)}, texture); |
| blit_pass->EncodeCommands(); |
| |
| if (!context->GetCommandQueue()->Submit({command_buffer}).ok()) { |
| return nullptr; |
| } |
| |
| return impeller::DlImageImpeller::Make(texture, |
| DlImage::OwningContext::kRaster); |
| } |
| |
| void SnapshotControllerImpeller::CacheRuntimeStage( |
| const std::shared_ptr<impeller::RuntimeStage>& runtime_stage) { |
| if (!GetDelegate().IsAiksContextInitialized()) { |
| return; |
| } |
| auto context = GetDelegate().GetAiksContext(); |
| if (!context) { |
| return; |
| } |
| impeller::RuntimeEffectContents runtime_effect; |
| runtime_effect.SetRuntimeStage(runtime_stage); |
| runtime_effect.BootstrapShader(context->GetContentContext()); |
| } |
| |
| sk_sp<SkImage> SnapshotControllerImpeller::ConvertToRasterImage( |
| sk_sp<SkImage> image) { |
| FML_UNREACHABLE(); |
| } |
| |
| bool SnapshotControllerImpeller::MakeRenderContextCurrent() { |
| const std::unique_ptr<Surface>& surface = GetDelegate().GetSurface(); |
| if (!surface) { |
| // Some backends (such as Metal) can operate without a surface and do not |
| // require MakeRenderContextCurrent. |
| return true; |
| } |
| return surface->MakeRenderContextCurrent()->GetResult(); |
| } |
| |
| } // namespace flutter |