Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions lib/ui/fixtures/ui_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,28 @@ Future<void> toByteDataRetries() async {
}
}

@pragma('vm:entry-point')
Future<void> toImageRetries() async {
final PictureRecorder pictureRecorder = PictureRecorder();
final Canvas canvas = Canvas(pictureRecorder);
final Paint paint = Paint()
..color = Color.fromRGBO(255, 255, 255, 1.0)
..style = PaintingStyle.fill;
final Offset c = Offset(50.0, 50.0);
canvas.drawCircle(c, 25.0, paint);
final Picture picture = pictureRecorder.endRecording();
_turnOffGPU(true);
Future<void>.delayed(Duration(milliseconds: 10), () {
_turnOffGPU(false);
});
try {
final Image image = await picture.toImage(100, 100);
_validateNotNull(image);
} catch (error) {
_validateNotNull(null);
}
}

@pragma('vm:entry-point')
Future<void> pumpImage() async {
const int width = 60;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ void DlDeferredImageGPUImpeller::ImageWrapper::SnapshotDisplayList(
SkRect::MakeWH(wrapper->size_.width(), wrapper->size_.height()),
wrapper->texture_registry_);
}
auto snapshot = snapshot_delegate->MakeRasterSnapshot(
auto snapshot = snapshot_delegate->MakeRasterSnapshotSync(
wrapper->display_list_, wrapper->size_);
if (!snapshot) {
std::scoped_lock lock(wrapper->error_mutex_);
Expand Down
51 changes: 49 additions & 2 deletions lib/ui/painting/image_encoding_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -232,8 +232,7 @@ std::shared_ptr<impeller::Context> MakeConvertDlImageToSkImageContext(

TEST_F(ShellTest, EncodeImageRetries) {
#ifndef FML_OS_MACOSX
// Only works on macos currently.
GTEST_SKIP();
GTEST_SKIP() << "Only works on macos currently.";
#endif
Settings settings = CreateSettingsForFixture();
settings.enable_impeller = true;
Expand Down Expand Up @@ -279,6 +278,54 @@ TEST_F(ShellTest, EncodeImageRetries) {
DestroyShell(std::move(shell), task_runners);
}

TEST_F(ShellTest, ToImageRetries) {
#ifndef FML_OS_MACOSX
GTEST_SKIP() << "Only works on macos currently.";
#endif
Settings settings = CreateSettingsForFixture();
settings.enable_impeller = true;
TaskRunners task_runners("test", // label
GetCurrentTaskRunner(), // platform
CreateNewThread(), // raster
CreateNewThread(), // ui
CreateNewThread() // io
);

std::unique_ptr<Shell> shell = CreateShell({
.settings = settings,
.task_runners = task_runners,
});

auto turn_off_gpu = [&](Dart_NativeArguments args) {
auto handle = Dart_GetNativeArgument(args, 0);
bool value = true;
ASSERT_TRUE(Dart_IsBoolean(handle));
Dart_BooleanValue(handle, &value);
TurnOffGPU(shell.get(), value);
};

AddNativeCallback("TurnOffGPU", CREATE_NATIVE_ENTRY(turn_off_gpu));

auto validate_not_null = [&](Dart_NativeArguments args) {
auto handle = Dart_GetNativeArgument(args, 0);
EXPECT_FALSE(Dart_IsNull(handle));
message_latch.Signal();
};

AddNativeCallback("ValidateNotNull", CREATE_NATIVE_ENTRY(validate_not_null));

ASSERT_TRUE(shell->IsSetup());
auto configuration = RunConfiguration::InferFromSettings(settings);
configuration.SetEntrypoint("toImageRetries");

shell->RunEngine(std::move(configuration), [&](auto result) {
ASSERT_EQ(result, Engine::RunStatus::Success);
});

message_latch.Wait();
DestroyShell(std::move(shell), task_runners);
}

TEST_F(ShellTest, EncodeImageFailsWithoutGPUImpeller) {
#ifndef FML_OS_MACOSX
// Only works on macos currently.
Expand Down
18 changes: 8 additions & 10 deletions lib/ui/painting/picture.cc
Original file line number Diff line number Diff line change
Expand Up @@ -203,22 +203,20 @@ Dart_Handle Picture::DoRasterizeToImage(const sk_sp<DisplayList>& display_list,
layer_tree = std::move(layer_tree)]() mutable {
auto picture_bounds = SkISize::Make(width, height);
sk_sp<DlImage> image;
sk_sp<DisplayList> snapshot_display_list = display_list;
if (layer_tree) {
FML_DCHECK(picture_bounds == layer_tree->frame_size());
auto display_list =
snapshot_display_list =
layer_tree->Flatten(SkRect::MakeWH(width, height),
snapshot_delegate->GetTextureRegistry(),
snapshot_delegate->GetGrContext());

image = snapshot_delegate->MakeRasterSnapshot(display_list,
picture_bounds);
} else {
image = snapshot_delegate->MakeRasterSnapshot(display_list,
picture_bounds);
}

fml::TaskRunner::RunNowOrPostTask(
ui_task_runner, [ui_task, image]() { ui_task(image); });
snapshot_delegate->MakeRasterSnapshot(
snapshot_display_list, picture_bounds,
[ui_task_runner, ui_task](const sk_sp<DlImage>& image) {
fml::TaskRunner::RunNowOrPostTask(
ui_task_runner, [ui_task, image]() { ui_task(image); });
});
}));

return Dart_Null();
Expand Down
9 changes: 7 additions & 2 deletions lib/ui/snapshot_delegate.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,13 @@ class SnapshotDelegate {

virtual GrDirectContext* GetGrContext() = 0;

virtual sk_sp<DlImage> MakeRasterSnapshot(sk_sp<DisplayList> display_list,
SkISize picture_size) = 0;
virtual void MakeRasterSnapshot(
sk_sp<DisplayList> display_list,
SkISize picture_size,
std::function<void(sk_sp<DlImage>)> callback) = 0;

virtual sk_sp<DlImage> MakeRasterSnapshotSync(sk_sp<DisplayList> display_list,
SkISize picture_size) = 0;

virtual sk_sp<SkImage> ConvertToRasterImage(sk_sp<SkImage> image) = 0;

Expand Down
16 changes: 13 additions & 3 deletions shell/common/rasterizer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -422,9 +422,19 @@ std::unique_ptr<Rasterizer::GpuImageResult> Rasterizer::MakeSkiaGpuImage(
return result;
}

sk_sp<DlImage> Rasterizer::MakeRasterSnapshot(sk_sp<DisplayList> display_list,
SkISize picture_size) {
return snapshot_controller_->MakeRasterSnapshot(display_list, picture_size);
void Rasterizer::MakeRasterSnapshot(
sk_sp<DisplayList> display_list,
SkISize picture_size,
std::function<void(sk_sp<DlImage>)> callback) {
return snapshot_controller_->MakeRasterSnapshot(display_list, picture_size,
callback);
}

sk_sp<DlImage> Rasterizer::MakeRasterSnapshotSync(
sk_sp<DisplayList> display_list,
SkISize picture_size) {
return snapshot_controller_->MakeRasterSnapshotSync(display_list,
picture_size);
}

sk_sp<SkImage> Rasterizer::ConvertToRasterImage(sk_sp<SkImage> image) {
Expand Down
10 changes: 8 additions & 2 deletions shell/common/rasterizer.h
Original file line number Diff line number Diff line change
Expand Up @@ -643,8 +643,14 @@ class Rasterizer final : public SnapshotDelegate,
const SkImageInfo& image_info) override;

// |SnapshotDelegate|
sk_sp<DlImage> MakeRasterSnapshot(sk_sp<DisplayList> display_list,
SkISize picture_size) override;
void MakeRasterSnapshot(
sk_sp<DisplayList> display_list,
SkISize picture_size,
std::function<void(sk_sp<DlImage>)> callback) override;

// |SnapshotDelegate|
sk_sp<DlImage> MakeRasterSnapshotSync(sk_sp<DisplayList> display_list,
SkISize picture_size) override;

// |SnapshotDelegate|
sk_sp<SkImage> ConvertToRasterImage(sk_sp<SkImage> image) override;
Expand Down
2 changes: 1 addition & 1 deletion shell/common/shell_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2450,7 +2450,7 @@ TEST_F(ShellTest, RasterizerMakeRasterSnapshot) {
shell->GetTaskRunners().GetRasterTaskRunner(), [&shell, &latch]() {
SnapshotDelegate* delegate =
reinterpret_cast<Rasterizer*>(shell->GetRasterizer().get());
sk_sp<DlImage> image = delegate->MakeRasterSnapshot(
sk_sp<DlImage> image = delegate->MakeRasterSnapshotSync(
MakeSizedDisplayList(50, 50), SkISize::Make(50, 50));
EXPECT_NE(image, nullptr);

Expand Down
9 changes: 7 additions & 2 deletions shell/common/snapshot_controller.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,16 @@ class SnapshotController {

virtual ~SnapshotController() = default;

virtual void MakeRasterSnapshot(
sk_sp<DisplayList> display_list,
SkISize picture_size,
std::function<void(const sk_sp<DlImage>&)> callback) = 0;

// Note that this image is not guaranteed to be UIThreadSafe and must
// be converted to a DlImageGPU if it is to be handed back to the UI
// thread.
virtual sk_sp<DlImage> MakeRasterSnapshot(sk_sp<DisplayList> display_list,
SkISize size) = 0;
virtual sk_sp<DlImage> MakeRasterSnapshotSync(sk_sp<DisplayList> display_list,
SkISize picture_size) = 0;

virtual sk_sp<SkImage> ConvertToRasterImage(sk_sp<SkImage> image) = 0;

Expand Down
78 changes: 60 additions & 18 deletions shell/common/snapshot_controller_impeller.cc
Original file line number Diff line number Diff line change
Expand Up @@ -15,29 +15,15 @@

namespace flutter {

sk_sp<DlImage> SnapshotControllerImpeller::MakeRasterSnapshot(
sk_sp<DisplayList> display_list,
SkISize size) {
sk_sp<DlImage> result;
GetDelegate().GetIsGpuDisabledSyncSwitch()->Execute(
fml::SyncSwitch::Handlers()
.SetIfTrue([&] {
// Do nothing.
})
.SetIfFalse(
[&] { result = DoMakeRasterSnapshot(display_list, size); }));

return result;
}

sk_sp<DlImage> SnapshotControllerImpeller::DoMakeRasterSnapshot(
namespace {
sk_sp<DlImage> DoMakeRasterSnapshot(
const sk_sp<DisplayList>& display_list,
SkISize size) {
SkISize size,
const std::shared_ptr<impeller::AiksContext>& context) {
TRACE_EVENT0("flutter", __FUNCTION__);
impeller::DlDispatcher dispatcher;
display_list->Dispatch(dispatcher);
impeller::Picture picture = dispatcher.EndRecordingAsPicture();
auto context = GetDelegate().GetAiksContext();
if (context) {
auto max_size = context->GetContext()
->GetResourceAllocator()
Expand Down Expand Up @@ -70,6 +56,62 @@ sk_sp<DlImage> SnapshotControllerImpeller::DoMakeRasterSnapshot(
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),
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried to store a weak_ptr to the context here and it didn't work. It looks like AiksContext doesn't have the proper lifespan.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you be a bit more specific? Does the AiksContext die during backgrounding?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, it doesn't have to do with backgrounding. They don't seem to have a lifespan that is as long lived as the impeller context. I didn't look into what their lifespan was. I decided practically it was fine to have a strong retain on it while we don't have the GPU since that means we will not be executing anyways so the idea that the toImage result will no longer will be relevant when the GPU comes back is low.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SG

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;
Expand Down
12 changes: 7 additions & 5 deletions shell/common/snapshot_controller_impeller.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,20 @@ class SnapshotControllerImpeller : public SnapshotController {
const SnapshotController::Delegate& delegate)
: SnapshotController(delegate) {}

sk_sp<DlImage> MakeRasterSnapshot(sk_sp<DisplayList> display_list,
SkISize size) override;
void MakeRasterSnapshot(
sk_sp<DisplayList> display_list,
SkISize picture_size,
std::function<void(const sk_sp<DlImage>&)> callback) override;

sk_sp<DlImage> MakeRasterSnapshotSync(sk_sp<DisplayList> display_list,
SkISize picture_size) override;

sk_sp<SkImage> ConvertToRasterImage(sk_sp<SkImage> image) override;

void CacheRuntimeStage(
const std::shared_ptr<impeller::RuntimeStage>& runtime_stage) override;

private:
sk_sp<DlImage> DoMakeRasterSnapshot(const sk_sp<DisplayList>& display_list,
SkISize size);

FML_DISALLOW_COPY_AND_ASSIGN(SnapshotControllerImpeller);
};

Expand Down
9 changes: 8 additions & 1 deletion shell/common/snapshot_controller_skia.cc
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,13 @@ sk_sp<SkImage> DrawSnapshot(
}
} // namespace

void SnapshotControllerSkia::MakeRasterSnapshot(
sk_sp<DisplayList> display_list,
SkISize picture_size,
std::function<void(const sk_sp<DlImage>&)> callback) {
callback(MakeRasterSnapshotSync(display_list, picture_size));
}

sk_sp<DlImage> SnapshotControllerSkia::DoMakeRasterSnapshot(
SkISize size,
std::function<void(SkCanvas*)> draw_callback) {
Expand Down Expand Up @@ -129,7 +136,7 @@ sk_sp<DlImage> SnapshotControllerSkia::DoMakeRasterSnapshot(
return DlImage::Make(result);
}

sk_sp<DlImage> SnapshotControllerSkia::MakeRasterSnapshot(
sk_sp<DlImage> SnapshotControllerSkia::MakeRasterSnapshotSync(
sk_sp<DisplayList> display_list,
SkISize size) {
return DoMakeRasterSnapshot(size, [display_list](SkCanvas* canvas) {
Expand Down
9 changes: 7 additions & 2 deletions shell/common/snapshot_controller_skia.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,13 @@ class SnapshotControllerSkia : public SnapshotController {
explicit SnapshotControllerSkia(const SnapshotController::Delegate& delegate)
: SnapshotController(delegate) {}

sk_sp<DlImage> MakeRasterSnapshot(sk_sp<DisplayList> display_list,
SkISize size) override;
void MakeRasterSnapshot(
sk_sp<DisplayList> display_list,
SkISize picture_size,
std::function<void(const sk_sp<DlImage>&)> callback) override;

sk_sp<DlImage> MakeRasterSnapshotSync(sk_sp<DisplayList> display_list,
SkISize size) override;

virtual sk_sp<SkImage> ConvertToRasterImage(sk_sp<SkImage> image) override;

Expand Down