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

Commit cc916b9

Browse files
Prevent app from accessing the GPU in the background in MultiFrameCodec (#28159)
1 parent b4b44f2 commit cc916b9

File tree

4 files changed

+123
-17
lines changed

4 files changed

+123
-17
lines changed

lib/ui/fixtures/ui_test.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ void createPath() {
197197
void _validatePath(Path path) native 'ValidatePath';
198198

199199
@pragma('vm:entry-point')
200-
void frameCallback(FrameInfo info) {
200+
void frameCallback(_Image, int) {
201201
print('called back');
202202
}
203203

lib/ui/painting/image_decoder_unittests.cc

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -731,5 +731,94 @@ TEST_F(ImageDecoderFixtureTest,
731731
latch.Wait();
732732
}
733733

734+
TEST_F(ImageDecoderFixtureTest, MultiFrameCodecDidAccessGpuDisabledSyncSwitch) {
735+
auto settings = CreateSettingsForFixture();
736+
auto vm_ref = DartVMRef::Create(settings);
737+
auto vm_data = vm_ref.GetVMData();
738+
739+
auto gif_mapping = OpenFixtureAsSkData("hello_loop_2.gif");
740+
741+
ASSERT_TRUE(gif_mapping);
742+
743+
ImageGeneratorRegistry registry;
744+
std::shared_ptr<ImageGenerator> gif_generator =
745+
registry.CreateCompatibleGenerator(gif_mapping);
746+
ASSERT_TRUE(gif_generator);
747+
748+
TaskRunners runners(GetCurrentTestName(), // label
749+
CreateNewThread("platform"), // platform
750+
CreateNewThread("raster"), // raster
751+
CreateNewThread("ui"), // ui
752+
CreateNewThread("io") // io
753+
);
754+
755+
fml::AutoResetWaitableEvent latch;
756+
std::unique_ptr<TestIOManager> io_manager;
757+
fml::RefPtr<MultiFrameCodec> codec;
758+
759+
// Setup the IO manager.
760+
runners.GetIOTaskRunner()->PostTask([&]() {
761+
io_manager = std::make_unique<TestIOManager>(runners.GetIOTaskRunner());
762+
latch.Signal();
763+
});
764+
latch.Wait();
765+
766+
auto isolate = RunDartCodeInIsolate(vm_ref, settings, runners, "main", {},
767+
GetDefaultKernelFilePath(),
768+
io_manager->GetWeakIOManager());
769+
770+
runners.GetUITaskRunner()->PostTask([&]() {
771+
fml::AutoResetWaitableEvent isolate_latch;
772+
773+
EXPECT_TRUE(isolate->RunInIsolateScope([&]() -> bool {
774+
Dart_Handle library = Dart_RootLibrary();
775+
if (Dart_IsError(library)) {
776+
isolate_latch.Signal();
777+
return false;
778+
}
779+
Dart_Handle closure =
780+
Dart_GetField(library, Dart_NewStringFromCString("frameCallback"));
781+
if (Dart_IsError(closure) || !Dart_IsClosure(closure)) {
782+
isolate_latch.Signal();
783+
return false;
784+
}
785+
786+
EXPECT_FALSE(io_manager->did_access_is_gpu_disabled_sync_switch_);
787+
codec = fml::MakeRefCounted<MultiFrameCodec>(std::move(gif_generator));
788+
codec->getNextFrame(closure);
789+
isolate_latch.Signal();
790+
return true;
791+
}));
792+
isolate_latch.Wait();
793+
794+
latch.Signal();
795+
});
796+
latch.Wait();
797+
798+
runners.GetIOTaskRunner()->PostTask([&]() {
799+
EXPECT_TRUE(io_manager->did_access_is_gpu_disabled_sync_switch_);
800+
latch.Signal();
801+
});
802+
latch.Wait();
803+
804+
// Destroy the Isolate
805+
isolate = nullptr;
806+
807+
// Destroy the MultiFrameCodec
808+
runners.GetUITaskRunner()->PostTask([&]() {
809+
codec = nullptr;
810+
latch.Signal();
811+
});
812+
latch.Wait();
813+
814+
// Destroy the IO manager
815+
runners.GetIOTaskRunner()->PostTask([&]() {
816+
io_manager.reset();
817+
latch.Signal();
818+
});
819+
820+
latch.Wait();
821+
}
822+
734823
} // namespace testing
735824
} // namespace flutter

lib/ui/painting/multi_frame_codec.cc

Lines changed: 30 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,8 @@ static bool CopyToBitmap(SkBitmap* dst,
7676
}
7777

7878
sk_sp<SkImage> MultiFrameCodec::State::GetNextFrameImage(
79-
fml::WeakPtr<GrDirectContext> resourceContext) {
79+
fml::WeakPtr<GrDirectContext> resourceContext,
80+
const std::shared_ptr<const fml::SyncSwitch>& gpu_disable_sync_switch) {
8081
SkBitmap bitmap = SkBitmap();
8182
SkImageInfo info = generator_->GetInfo().makeColorType(kN32_SkColorType);
8283
if (info.alphaType() == kUnpremul_SkAlphaType) {
@@ -122,29 +123,43 @@ sk_sp<SkImage> MultiFrameCodec::State::GetNextFrameImage(
122123
lastRequiredFrame_ = std::make_unique<SkBitmap>(bitmap);
123124
lastRequiredFrameIndex_ = nextFrameIndex_;
124125
}
125-
126-
if (resourceContext) {
127-
SkPixmap pixmap(bitmap.info(), bitmap.pixelRef()->pixels(),
128-
bitmap.pixelRef()->rowBytes());
129-
return SkImage::MakeCrossContextFromPixmap(resourceContext.get(), pixmap,
130-
true);
131-
} else {
132-
// Defer decoding until time of draw later on the raster thread. Can happen
133-
// when GL operations are currently forbidden such as in the background
134-
// on iOS.
135-
return SkImage::MakeFromBitmap(bitmap);
136-
}
126+
sk_sp<SkImage> result;
127+
128+
gpu_disable_sync_switch->Execute(
129+
fml::SyncSwitch::Handlers()
130+
.SetIfTrue([&result, &bitmap] {
131+
// Defer decoding until time of draw later on the raster thread. Can
132+
// happen when GL operations are currently forbidden such as in the
133+
// background on iOS.
134+
result = SkImage::MakeFromBitmap(bitmap);
135+
})
136+
.SetIfFalse([&result, &resourceContext, &bitmap] {
137+
if (resourceContext) {
138+
SkPixmap pixmap(bitmap.info(), bitmap.pixelRef()->pixels(),
139+
bitmap.pixelRef()->rowBytes());
140+
result = SkImage::MakeCrossContextFromPixmap(
141+
resourceContext.get(), pixmap, true);
142+
} else {
143+
// Defer decoding until time of draw later on the raster thread.
144+
// Can happen when GL operations are currently forbidden such as
145+
// in the background on iOS.
146+
result = SkImage::MakeFromBitmap(bitmap);
147+
}
148+
}));
149+
return result;
137150
}
138151

139152
void MultiFrameCodec::State::GetNextFrameAndInvokeCallback(
140153
std::unique_ptr<DartPersistentValue> callback,
141154
fml::RefPtr<fml::TaskRunner> ui_task_runner,
142155
fml::WeakPtr<GrDirectContext> resourceContext,
143156
fml::RefPtr<flutter::SkiaUnrefQueue> unref_queue,
157+
const std::shared_ptr<const fml::SyncSwitch>& gpu_disable_sync_switch,
144158
size_t trace_id) {
145159
fml::RefPtr<CanvasImage> image = nullptr;
146160
int duration = 0;
147-
sk_sp<SkImage> skImage = GetNextFrameImage(resourceContext);
161+
sk_sp<SkImage> skImage =
162+
GetNextFrameImage(resourceContext, gpu_disable_sync_switch);
148163
if (skImage) {
149164
image = CanvasImage::Create();
150165
image->set_image({skImage, std::move(unref_queue)});
@@ -200,7 +215,7 @@ Dart_Handle MultiFrameCodec::getNextFrame(Dart_Handle callback_handle) {
200215
state->GetNextFrameAndInvokeCallback(
201216
std::move(callback), std::move(ui_task_runner),
202217
io_manager->GetResourceContext(), io_manager->GetSkiaUnrefQueue(),
203-
trace_id);
218+
io_manager->GetIsGpuDisabledSyncSwitch(), trace_id);
204219
}));
205220

206221
return Dart_Null();

lib/ui/painting/multi_frame_codec.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,13 +54,15 @@ class MultiFrameCodec : public Codec {
5454
int lastRequiredFrameIndex_ = -1;
5555

5656
sk_sp<SkImage> GetNextFrameImage(
57-
fml::WeakPtr<GrDirectContext> resourceContext);
57+
fml::WeakPtr<GrDirectContext> resourceContext,
58+
const std::shared_ptr<const fml::SyncSwitch>& gpu_disable_sync_switch);
5859

5960
void GetNextFrameAndInvokeCallback(
6061
std::unique_ptr<DartPersistentValue> callback,
6162
fml::RefPtr<fml::TaskRunner> ui_task_runner,
6263
fml::WeakPtr<GrDirectContext> resourceContext,
6364
fml::RefPtr<flutter::SkiaUnrefQueue> unref_queue,
65+
const std::shared_ptr<const fml::SyncSwitch>& gpu_disable_sync_switch,
6466
size_t trace_id);
6567
};
6668

0 commit comments

Comments
 (0)