Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit b307a6c

Browse files
authored
Fix Shell::Screenshot for Impeller (#50072)
Fixes flutter/flutter#141571 Shell::Screenshot was impelemnted in a Skia-only way and would crash when invoked. Adds test coverage for the breaking path on iOS that ends up calling `FlutterView drawLayer`.
1 parent 65d1291 commit b307a6c

9 files changed

Lines changed: 343 additions & 63 deletions

File tree

shell/common/rasterizer.cc

Lines changed: 166 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <memory>
99
#include <utility>
1010

11+
#include "display_list/dl_builder.h"
1112
#include "flow/frame_timings.h"
1213
#include "flutter/common/constants.h"
1314
#include "flutter/common/graphics/persistent_cache.h"
@@ -17,6 +18,7 @@
1718
#include "flutter/shell/common/base64.h"
1819
#include "flutter/shell/common/serialization_callbacks.h"
1920
#include "fml/make_copyable.h"
21+
#include "fml/synchronization/waitable_event.h"
2022
#include "third_party/skia/include/core/SkColorSpace.h"
2123
#include "third_party/skia/include/core/SkData.h"
2224
#include "third_party/skia/include/core/SkImage.h"
@@ -34,6 +36,12 @@
3436
#include "third_party/skia/include/gpu/GrTypes.h"
3537
#include "third_party/skia/include/gpu/ganesh/SkSurfaceGanesh.h"
3638

39+
#if IMPELLER_SUPPORTS_RENDERING
40+
#include "impeller/aiks/aiks_context.h" // nogncheck
41+
#include "impeller/core/formats.h" // nogncheck
42+
#include "impeller/display_list/dl_dispatcher.h" // nogncheck
43+
#endif
44+
3745
namespace flutter {
3846

3947
// The rasterizer will tell Skia to purge cached resources that have not been
@@ -802,28 +810,143 @@ static sk_sp<SkData> ScreenshotLayerTreeAsPicture(
802810
return recorder.finishRecordingAsPicture()->serialize(&procs);
803811
}
804812

805-
sk_sp<SkData> Rasterizer::ScreenshotLayerTreeAsImage(
806-
flutter::LayerTree* tree,
813+
static void RenderFrameForScreenshot(
807814
flutter::CompositorContext& compositor_context,
815+
DlCanvas* canvas,
816+
flutter::LayerTree* tree,
808817
GrDirectContext* surface_context,
818+
const std::shared_ptr<impeller::AiksContext>& aiks_context) {
819+
// There is no root surface transformation for the screenshot layer. Reset
820+
// the matrix to identity.
821+
SkMatrix root_surface_transformation;
822+
root_surface_transformation.reset();
823+
824+
auto frame = compositor_context.AcquireFrame(
825+
surface_context, // skia context
826+
canvas, // canvas
827+
nullptr, // view embedder
828+
root_surface_transformation, // root surface transformation
829+
false, // instrumentation enabled
830+
true, // render buffer readback supported
831+
nullptr, // thread merger
832+
aiks_context.get() // aiks context
833+
);
834+
canvas->Clear(DlColor::kTransparent());
835+
frame->Raster(*tree, true, nullptr);
836+
canvas->Flush();
837+
}
838+
839+
#if IMPELLER_SUPPORTS_RENDERING
840+
Rasterizer::ScreenshotFormat ToScreenshotFormat(impeller::PixelFormat format) {
841+
switch (format) {
842+
case impeller::PixelFormat::kUnknown:
843+
case impeller::PixelFormat::kA8UNormInt:
844+
case impeller::PixelFormat::kR8UNormInt:
845+
case impeller::PixelFormat::kR8G8UNormInt:
846+
case impeller::PixelFormat::kR8G8B8A8UNormIntSRGB:
847+
case impeller::PixelFormat::kB8G8R8A8UNormIntSRGB:
848+
case impeller::PixelFormat::kB10G10R10XRSRGB:
849+
case impeller::PixelFormat::kS8UInt:
850+
case impeller::PixelFormat::kD24UnormS8Uint:
851+
case impeller::PixelFormat::kD32FloatS8UInt:
852+
case impeller::PixelFormat::kR32G32B32A32Float:
853+
case impeller::PixelFormat::kB10G10R10XR:
854+
case impeller::PixelFormat::kB10G10R10A10XR:
855+
FML_DCHECK(false);
856+
return Rasterizer::ScreenshotFormat::kUnknown;
857+
case impeller::PixelFormat::kR8G8B8A8UNormInt:
858+
return Rasterizer::ScreenshotFormat::kR8G8B8A8UNormInt;
859+
case impeller::PixelFormat::kB8G8R8A8UNormInt:
860+
return Rasterizer::ScreenshotFormat::kB8G8R8A8UNormInt;
861+
case impeller::PixelFormat::kR16G16B16A16Float:
862+
return Rasterizer::ScreenshotFormat::kR16G16B16A16Float;
863+
}
864+
}
865+
866+
static std::pair<sk_sp<SkData>, Rasterizer::ScreenshotFormat>
867+
ScreenshotLayerTreeAsImageImpeller(
868+
const std::shared_ptr<impeller::AiksContext>& aiks_context,
869+
flutter::LayerTree* tree,
870+
flutter::CompositorContext& compositor_context,
871+
bool compressed) {
872+
if (compressed) {
873+
FML_LOG(ERROR) << "Compressed screenshots not supported for Impeller";
874+
return {nullptr, Rasterizer::ScreenshotFormat::kUnknown};
875+
}
876+
877+
DisplayListBuilder builder(SkRect::MakeSize(
878+
SkSize::Make(tree->frame_size().fWidth, tree->frame_size().fHeight)));
879+
880+
RenderFrameForScreenshot(compositor_context, &builder, tree, nullptr,
881+
aiks_context);
882+
883+
impeller::DlDispatcher dispatcher;
884+
builder.Build()->Dispatch(dispatcher);
885+
const auto& picture = dispatcher.EndRecordingAsPicture();
886+
const auto& image = picture.ToImage(
887+
*aiks_context,
888+
impeller::ISize(tree->frame_size().fWidth, tree->frame_size().fHeight));
889+
const auto& texture = image->GetTexture();
890+
impeller::DeviceBufferDescriptor buffer_desc;
891+
buffer_desc.storage_mode = impeller::StorageMode::kHostVisible;
892+
buffer_desc.size =
893+
texture->GetTextureDescriptor().GetByteSizeOfBaseMipLevel();
894+
auto impeller_context = aiks_context->GetContext();
895+
auto buffer =
896+
impeller_context->GetResourceAllocator()->CreateBuffer(buffer_desc);
897+
auto command_buffer = impeller_context->CreateCommandBuffer();
898+
command_buffer->SetLabel("BlitTextureToBuffer Command Buffer");
899+
auto pass = command_buffer->CreateBlitPass();
900+
pass->AddCopy(texture, buffer);
901+
pass->EncodeCommands(impeller_context->GetResourceAllocator());
902+
fml::AutoResetWaitableEvent latch;
903+
sk_sp<SkData> sk_data;
904+
auto completion = [buffer, &buffer_desc, &sk_data,
905+
&latch](impeller::CommandBuffer::Status status) {
906+
if (status != impeller::CommandBuffer::Status::kCompleted) {
907+
FML_LOG(ERROR) << "Failed to complete blit pass.";
908+
latch.Signal();
909+
return;
910+
}
911+
sk_data = SkData::MakeWithCopy(buffer->OnGetContents(), buffer_desc.size);
912+
913+
latch.Signal();
914+
};
915+
916+
if (!command_buffer->SubmitCommands(completion)) {
917+
FML_LOG(ERROR) << "Failed to submit commands.";
918+
}
919+
latch.Wait();
920+
return std::make_pair(
921+
sk_data, ToScreenshotFormat(texture->GetTextureDescriptor().format));
922+
}
923+
#endif
924+
925+
std::pair<sk_sp<SkData>, Rasterizer::ScreenshotFormat>
926+
Rasterizer::ScreenshotLayerTreeAsImage(
927+
flutter::LayerTree* tree,
928+
flutter::CompositorContext& compositor_context,
809929
bool compressed) {
930+
#if IMPELLER_SUPPORTS_RENDERING
931+
if (delegate_.GetSettings().enable_impeller) {
932+
return ScreenshotLayerTreeAsImageImpeller(GetAiksContext(), tree,
933+
compositor_context, compressed);
934+
}
935+
#endif // IMPELLER_SUPPORTS_RENDERING
936+
937+
GrDirectContext* surface_context = GetGrContext();
810938
// Attempt to create a snapshot surface depending on whether we have access
811939
// to a valid GPU rendering context.
812940
std::unique_ptr<OffscreenSurface> snapshot_surface =
813941
std::make_unique<OffscreenSurface>(surface_context, tree->frame_size());
814942

815943
if (!snapshot_surface->IsValid()) {
816944
FML_LOG(ERROR) << "Screenshot: unable to create snapshot surface";
817-
return nullptr;
945+
return {nullptr, ScreenshotFormat::kUnknown};
818946
}
819947

820948
// Draw the current layer tree into the snapshot surface.
821-
auto* canvas = snapshot_surface->GetCanvas();
822-
823-
// There is no root surface transformation for the screenshot layer. Reset
824-
// the matrix to identity.
825-
SkMatrix root_surface_transformation;
826-
root_surface_transformation.reset();
949+
DlCanvas* canvas = snapshot_surface->GetCanvas();
827950

828951
// snapshot_surface->makeImageSnapshot needs the GL context to be set if the
829952
// render context is GL. frame->Raster() pops the gl context in platforms
@@ -832,29 +955,26 @@ sk_sp<SkData> Rasterizer::ScreenshotLayerTreeAsImage(
832955
auto context_switch = surface_->MakeRenderContextCurrent();
833956
if (!context_switch->GetResult()) {
834957
FML_LOG(ERROR) << "Screenshot: unable to make image screenshot";
835-
return nullptr;
958+
return {nullptr, ScreenshotFormat::kUnknown};
836959
}
837960

838-
auto frame = compositor_context.AcquireFrame(
839-
surface_context, // skia context
840-
canvas, // canvas
841-
nullptr, // view embedder
842-
root_surface_transformation, // root surface transformation
843-
false, // instrumentation enabled
844-
true, // render buffer readback supported
845-
nullptr, // thread merger
846-
nullptr // aiks context
847-
);
848-
canvas->Clear(DlColor::kTransparent());
849-
frame->Raster(*tree, true, nullptr);
850-
canvas->Flush();
961+
RenderFrameForScreenshot(compositor_context, canvas, tree, surface_context,
962+
nullptr);
851963

852-
return snapshot_surface->GetRasterData(compressed);
964+
return std::make_pair(snapshot_surface->GetRasterData(compressed),
965+
ScreenshotFormat::kUnknown);
853966
}
854967

855968
Rasterizer::Screenshot Rasterizer::ScreenshotLastLayerTree(
856969
Rasterizer::ScreenshotType type,
857970
bool base64_encode) {
971+
if (delegate_.GetSettings().enable_impeller &&
972+
type == ScreenshotType::SkiaPicture) {
973+
FML_DCHECK(false);
974+
FML_LOG(ERROR) << "Last layer tree cannot be screenshotted as a "
975+
"SkiaPicture when using Impeller.";
976+
return {};
977+
}
858978
// TODO(dkwingsmt): Support screenshotting all last layer trees
859979
// when the shell protocol supports multi-views.
860980
// https://github.com/flutter/flutter/issues/135534
@@ -865,48 +985,49 @@ Rasterizer::Screenshot Rasterizer::ScreenshotLastLayerTree(
865985
return {};
866986
}
867987

868-
sk_sp<SkData> data = nullptr;
988+
std::pair<sk_sp<SkData>, ScreenshotFormat> data{nullptr,
989+
ScreenshotFormat::kUnknown};
869990
std::string format;
870991

871-
GrDirectContext* surface_context =
872-
surface_ ? surface_->GetContext() : nullptr;
873-
874992
switch (type) {
875993
case ScreenshotType::SkiaPicture:
876994
format = "ScreenshotType::SkiaPicture";
877-
data = ScreenshotLayerTreeAsPicture(layer_tree, *compositor_context_);
995+
data.first =
996+
ScreenshotLayerTreeAsPicture(layer_tree, *compositor_context_);
878997
break;
879998
case ScreenshotType::UncompressedImage:
880999
format = "ScreenshotType::UncompressedImage";
881-
data = ScreenshotLayerTreeAsImage(layer_tree, *compositor_context_,
882-
surface_context, false);
1000+
data =
1001+
ScreenshotLayerTreeAsImage(layer_tree, *compositor_context_, false);
8831002
break;
8841003
case ScreenshotType::CompressedImage:
8851004
format = "ScreenshotType::CompressedImage";
886-
data = ScreenshotLayerTreeAsImage(layer_tree, *compositor_context_,
887-
surface_context, true);
1005+
data = ScreenshotLayerTreeAsImage(layer_tree, *compositor_context_, true);
8881006
break;
8891007
case ScreenshotType::SurfaceData: {
8901008
Surface::SurfaceData surface_data = surface_->GetSurfaceData();
8911009
format = surface_data.pixel_format;
892-
data = surface_data.data;
1010+
data.first = surface_data.data;
8931011
break;
8941012
}
8951013
}
8961014

897-
if (data == nullptr) {
1015+
if (data.first == nullptr) {
8981016
FML_LOG(ERROR) << "Screenshot data was null.";
8991017
return {};
9001018
}
9011019

9021020
if (base64_encode) {
903-
size_t b64_size = Base64::EncodedSize(data->size());
1021+
size_t b64_size = Base64::EncodedSize(data.first->size());
9041022
auto b64_data = SkData::MakeUninitialized(b64_size);
905-
Base64::Encode(data->data(), data->size(), b64_data->writable_data());
906-
return Rasterizer::Screenshot{b64_data, layer_tree->frame_size(), format};
1023+
Base64::Encode(data.first->data(), data.first->size(),
1024+
b64_data->writable_data());
1025+
return Rasterizer::Screenshot{b64_data, layer_tree->frame_size(), format,
1026+
data.second};
9071027
}
9081028

909-
return Rasterizer::Screenshot{data, layer_tree->frame_size(), format};
1029+
return Rasterizer::Screenshot{data.first, layer_tree->frame_size(), format,
1030+
data.second};
9101031
}
9111032

9121033
void Rasterizer::SetNextFrameCallback(const fml::closure& callback) {
@@ -977,8 +1098,12 @@ Rasterizer::Screenshot::Screenshot() {}
9771098

9781099
Rasterizer::Screenshot::Screenshot(sk_sp<SkData> p_data,
9791100
SkISize p_size,
980-
const std::string& p_format)
981-
: data(std::move(p_data)), frame_size(p_size), format(p_format) {}
1101+
const std::string& p_format,
1102+
ScreenshotFormat p_pixel_format)
1103+
: data(std::move(p_data)),
1104+
frame_size(p_size),
1105+
format(p_format),
1106+
pixel_format(p_pixel_format) {}
9821107

9831108
Rasterizer::Screenshot::Screenshot(const Screenshot& other) = default;
9841109

shell/common/rasterizer.h

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@
2525
#include "flutter/fml/time/time_delta.h"
2626
#include "flutter/fml/time/time_point.h"
2727
#if IMPELLER_SUPPORTS_RENDERING
28-
// GN is having trouble understanding how this works in the Fuchsia builds.
2928
#include "impeller/aiks/aiks_context.h" // nogncheck
29+
#include "impeller/core/formats.h" // nogncheck
3030
#include "impeller/renderer/context.h" // nogncheck
3131
#include "impeller/typographer/backends/skia/typographer_context_skia.h" // nogncheck
3232
#endif // IMPELLER_SUPPORTS_RENDERING
@@ -357,9 +357,10 @@ class Rasterizer final : public SnapshotDelegate,
357357
SkiaPicture,
358358

359359
//--------------------------------------------------------------------------
360-
/// A format used to denote uncompressed image data. This format
360+
/// A format used to denote uncompressed image data. For Skia, this format
361361
/// is 32 bits per pixel, 8 bits per component and
362-
/// denoted by the `kN32_SkColorType ` Skia color type.
362+
/// denoted by the `kN32_SkColorType ` Skia color type. For Impeller, its
363+
/// format is specified in Screenshot::pixel_format.
363364
///
364365
UncompressedImage,
365366

@@ -377,6 +378,18 @@ class Rasterizer final : public SnapshotDelegate,
377378
// NOLINTEND(readability-identifier-naming)
378379
};
379380

381+
// Specifies the format of pixel data in a Screenshot.
382+
enum class ScreenshotFormat {
383+
// Unknown format, or Skia default.
384+
kUnknown,
385+
// RGBA 8 bits per channel.
386+
kR8G8B8A8UNormInt,
387+
// BGRA 8 bits per channel.
388+
kB8G8R8A8UNormInt,
389+
// RGBA 16 bit floating point per channel.
390+
kR16G16B16A16Float,
391+
};
392+
380393
//----------------------------------------------------------------------------
381394
/// @brief A POD type used to return the screenshot data along with the
382395
/// size of the frame.
@@ -400,6 +413,13 @@ class Rasterizer final : public SnapshotDelegate,
400413
///
401414
std::string format;
402415

416+
//--------------------------------------------------------------------------
417+
/// The pixel format of the data in `data`.
418+
///
419+
/// If the impeller backend is not used, this value is always kUnknown and
420+
/// the data is in RGBA8888 format.
421+
ScreenshotFormat pixel_format = ScreenshotFormat::kUnknown;
422+
403423
//--------------------------------------------------------------------------
404424
/// @brief Creates an empty screenshot
405425
///
@@ -411,10 +431,12 @@ class Rasterizer final : public SnapshotDelegate,
411431
/// @param[in] p_data The screenshot data
412432
/// @param[in] p_size The screenshot size.
413433
/// @param[in] p_format The screenshot format.
434+
/// @param[in] p_pixel_format The screenshot format.
414435
///
415436
Screenshot(sk_sp<SkData> p_data,
416437
SkISize p_size,
417-
const std::string& p_format);
438+
const std::string& p_format,
439+
ScreenshotFormat p_pixel_format);
418440

419441
//--------------------------------------------------------------------------
420442
/// @brief The copy constructor for a screenshot.
@@ -663,10 +685,9 @@ class Rasterizer final : public SnapshotDelegate,
663685
return delegate_.GetIsGpuDisabledSyncSwitch();
664686
}
665687

666-
sk_sp<SkData> ScreenshotLayerTreeAsImage(
688+
std::pair<sk_sp<SkData>, ScreenshotFormat> ScreenshotLayerTreeAsImage(
667689
flutter::LayerTree* tree,
668690
flutter::CompositorContext& compositor_context,
669-
GrDirectContext* surface_context,
670691
bool compressed);
671692

672693
// This method starts with the frame timing recorder at build end. This

0 commit comments

Comments
 (0)