blob: 3f926b5025bcdf2cd41a029adf7c885a3c92464c [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/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/renderer/render_target.h"
namespace flutter {
namespace {
sk_sp<DlImage> DoMakeRasterSnapshot(
const sk_sp<DisplayList>& display_list,
SkISize size,
const std::shared_ptr<impeller::AiksContext>& context) {
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;
}
#if EXPERIMENTAL_CANVAS
// Do not use the render target cache as the lifecycle of this texture
// will outlive a particular frame.
impeller::ISize impeller_size = impeller::ISize(size.width(), size.height());
impeller::RenderTargetAllocator render_target_allocator =
impeller::RenderTargetAllocator(
context->GetContext()->GetResourceAllocator());
impeller::RenderTarget target;
if (context->GetContext()->GetCapabilities()->SupportsOffscreenMSAA()) {
target = render_target_allocator.CreateOffscreenMSAA(
*context->GetContext(), // context
impeller_size, // size
/*mip_count=*/1,
"Picture Snapshot MSAA", // label
impeller::RenderTarget::
kDefaultColorAttachmentConfigMSAA // color_attachment_config
);
} else {
target = render_target_allocator.CreateOffscreen(
*context->GetContext(), // context
impeller_size, // size
/*mip_count=*/1,
"Picture Snapshot", // label
impeller::RenderTarget::
kDefaultColorAttachmentConfig // color_attachment_config
);
}
impeller::TextFrameDispatcher collector(context->GetContentContext(),
impeller::Matrix());
display_list->Dispatch(collector, SkIRect::MakeSize(size));
impeller::ExperimentalDlDispatcher impeller_dispatcher(
context->GetContentContext(), target,
display_list->root_has_backdrop_filter(),
display_list->max_root_blend_mode(),
impeller::IRect::MakeSize(impeller_size));
display_list->Dispatch(impeller_dispatcher, SkIRect::MakeSize(size));
impeller_dispatcher.FinishRecording();
context->GetContentContext().GetLazyGlyphAtlas()->ResetTextFrames();
return impeller::DlImageImpeller::Make(target.GetRenderTargetTexture(),
DlImage::OwningContext::kRaster);
#else
impeller::DlDispatcher dispatcher;
display_list->Dispatch(dispatcher);
impeller::Picture picture = dispatcher.EndRecordingAsPicture();
std::shared_ptr<impeller::Image> image =
picture.ToImage(*context, render_target_size);
if (image) {
return impeller::DlImageImpeller::Make(image->GetTexture(),
DlImage::OwningContext::kRaster);
}
#endif // EXPERIMENTAL_CANVAS
return nullptr;
}
sk_sp<DlImage> DoMakeRasterSnapshot(
sk_sp<DisplayList> display_list,
SkISize picture_size,
const std::shared_ptr<const fml::SyncSwitch>& sync_switch,
const std::shared_ptr<impeller::AiksContext>& context) {
sk_sp<DlImage> result;
sync_switch->Execute(fml::SyncSwitch::Handlers()
.SetIfTrue([&] {
// Do nothing.
})
.SetIfFalse([&] {
result = DoMakeRasterSnapshot(
display_list, picture_size, context);
}));
return result;
}
} // namespace
void SnapshotControllerImpeller::MakeRasterSnapshot(
sk_sp<DisplayList> display_list,
SkISize picture_size,
std::function<void(const sk_sp<DlImage>&)> callback) {
sk_sp<DlImage> result;
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 = std::move(callback)] {
callback(DoMakeRasterSnapshot(display_list, picture_size,
sync_switch, context));
});
} else {
callback(nullptr);
}
})
.SetIfFalse([&] {
callback(DoMakeRasterSnapshot(display_list, picture_size,
GetDelegate().GetAiksContext()));
}));
}
sk_sp<DlImage> SnapshotControllerImpeller::MakeRasterSnapshotSync(
sk_sp<DisplayList> display_list,
SkISize picture_size) {
return DoMakeRasterSnapshot(display_list, picture_size,
GetDelegate().GetIsGpuDisabledSyncSwitch(),
GetDelegate().GetAiksContext());
}
void SnapshotControllerImpeller::CacheRuntimeStage(
const std::shared_ptr<impeller::RuntimeStage>& runtime_stage) {
impeller::RuntimeEffectContents runtime_effect;
runtime_effect.SetRuntimeStage(runtime_stage);
auto context = GetDelegate().GetAiksContext();
if (!context) {
return;
}
runtime_effect.BootstrapShader(context->GetContentContext());
}
sk_sp<SkImage> SnapshotControllerImpeller::ConvertToRasterImage(
sk_sp<SkImage> image) {
FML_UNREACHABLE();
}
} // namespace flutter