blob: 6a74ffd288cb9836aff4ac4f768dcf453484f63f [file] [log] [blame]
// 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