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

Commit 234fcd7

Browse files
committed
[Impeller] implements a retry mechanism for dart:ui/Image.toByteData.
1 parent 93f02f7 commit 234fcd7

7 files changed

Lines changed: 212 additions & 12 deletions

File tree

impeller/renderer/backend/metal/BUILD.gn

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ impeller_component("metal") {
88
sources = [
99
"allocator_mtl.h",
1010
"allocator_mtl.mm",
11+
"app_state_notifier.h",
12+
"app_state_notifier.mm",
1113
"blit_command_mtl.h",
1214
"blit_command_mtl.mm",
1315
"blit_pass_mtl.h",
@@ -54,4 +56,7 @@ impeller_component("metal") {
5456
]
5557

5658
frameworks = [ "Metal.framework" ]
59+
if (is_ios) {
60+
frameworks += [ "UIKit.framework" ]
61+
}
5762
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
#pragma once
6+
7+
#include <functional>
8+
#include <memory>
9+
10+
namespace impeller {
11+
12+
class AppStateNotifier {
13+
public:
14+
struct AppStateNotifierImpl;
15+
16+
AppStateNotifier();
17+
18+
/// Callback is executed on the platform thread.
19+
void SetAppDidBecomeActiveCallback(std::function<void()> callback) {
20+
app_did_become_active_callback_ = callback;
21+
}
22+
23+
void OnAppBecameActive();
24+
25+
private:
26+
static void DeleteImpl(AppStateNotifierImpl* impl);
27+
std::function<void()> app_did_become_active_callback_;
28+
std::unique_ptr<AppStateNotifierImpl, decltype(&AppStateNotifier::DeleteImpl)>
29+
impl_;
30+
};
31+
32+
} // namespace impeller
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
#include "impeller/renderer/backend/metal/app_state_notifier.h"
6+
7+
#import <Foundation/Foundation.h>
8+
9+
#include "flutter/fml/logging.h"
10+
11+
#if !__has_feature(objc_arc)
12+
#error ARC must be enabled !
13+
#endif
14+
15+
#ifdef FML_OS_IOS
16+
#import <UIKit/UIKit.h>
17+
#endif
18+
19+
@interface ImpellerAppStateNotifier : NSObject
20+
@property(nonatomic, assign) impeller::AppStateNotifier* parent;
21+
- (instancetype)initWithParent:(impeller::AppStateNotifier*)parent
22+
NS_DESIGNATED_INITIALIZER;
23+
- (void)onAppBecameActive:(NSNotification*)notification;
24+
@end
25+
26+
#ifdef FML_OS_IOS
27+
@implementation ImpellerAppStateNotifier
28+
- (instancetype)initWithParent:(impeller::AppStateNotifier*)parent {
29+
self = [super init];
30+
if (self) {
31+
_parent = parent;
32+
[[NSNotificationCenter defaultCenter]
33+
addObserver:self
34+
selector:@selector(onAppBecameActive:)
35+
name:UIApplicationDidBecomeActiveNotification
36+
object:nil];
37+
}
38+
return self;
39+
}
40+
41+
- (void)dealloc {
42+
[[NSNotificationCenter defaultCenter] removeObserver:self];
43+
}
44+
45+
- (void)onAppBecameActive:(NSNotification*)notification {
46+
_parent->OnAppBecameActive();
47+
}
48+
49+
@end
50+
#else
51+
52+
@implementation ImpellerAppStateNotifier
53+
54+
- (instancetype)init {
55+
self = [self initWithParent:nil];
56+
return self;
57+
}
58+
59+
- (instancetype)initWithParent:(impeller::AppStateNotifier*)parent {
60+
// Note: Not supported on macOS.
61+
return [super init];
62+
}
63+
64+
- (void)onAppBecameActive:(NSNotification*)notification {
65+
}
66+
@end
67+
68+
#endif // FML_OS_IOS
69+
70+
namespace impeller {
71+
72+
struct AppStateNotifier::AppStateNotifierImpl {
73+
ImpellerAppStateNotifier* notifier_;
74+
};
75+
76+
AppStateNotifier::AppStateNotifier()
77+
: impl_(
78+
new AppStateNotifierImpl{
79+
.notifier_ =
80+
[[ImpellerAppStateNotifier alloc] initWithParent:this],
81+
},
82+
&AppStateNotifier::DeleteImpl) {}
83+
84+
void AppStateNotifier::OnAppBecameActive() {
85+
if (app_did_become_active_callback_) {
86+
app_did_become_active_callback_();
87+
}
88+
}
89+
90+
void AppStateNotifier::DeleteImpl(AppStateNotifierImpl* impl) {
91+
delete impl;
92+
}
93+
94+
} // namespace impeller

impeller/renderer/backend/metal/context_mtl.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include "impeller/base/backend_cast.h"
1616
#include "impeller/core/sampler.h"
1717
#include "impeller/renderer/backend/metal/allocator_mtl.h"
18+
#include "impeller/renderer/backend/metal/app_state_notifier.h"
1819
#include "impeller/renderer/backend/metal/command_buffer_mtl.h"
1920
#include "impeller/renderer/backend/metal/pipeline_library_mtl.h"
2021
#include "impeller/renderer/backend/metal/shader_library_mtl.h"
@@ -93,6 +94,9 @@ class ContextMTL final : public Context,
9394

9495
std::shared_ptr<const fml::SyncSwitch> GetIsGpuDisabledSyncSwitch() const;
9596

97+
// |Context|
98+
void StoreTaskForGPU(std::function<void()> task) override;
99+
96100
private:
97101
id<MTLDevice> device_ = nullptr;
98102
id<MTLCommandQueue> command_queue_ = nullptr;
@@ -103,6 +107,8 @@ class ContextMTL final : public Context,
103107
std::shared_ptr<const Capabilities> device_capabilities_;
104108
std::shared_ptr<fml::ConcurrentMessageLoop> raster_message_loop_;
105109
std::shared_ptr<const fml::SyncSwitch> is_gpu_disabled_sync_switch_;
110+
std::vector<std::function<void()>> tasks_awaiting_gpu_;
111+
AppStateNotifier app_state_notifier_;
106112
bool is_valid_ = false;
107113

108114
ContextMTL(
@@ -114,6 +120,8 @@ class ContextMTL final : public Context,
114120
std::shared_ptr<CommandBuffer> CreateCommandBufferInQueue(
115121
id<MTLCommandQueue> queue) const;
116122

123+
void FlushTasksAwaitingGPU();
124+
117125
FML_DISALLOW_COPY_AND_ASSIGN(ContextMTL);
118126
};
119127

impeller/renderer/backend/metal/context_mtl.mm

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,9 @@ static bool DeviceSupportsComputeSubgroups(id<MTLDevice> device) {
8383
return;
8484
}
8585

86+
app_state_notifier_.SetAppDidBecomeActiveCallback(
87+
[this] { this->FlushTasksAwaitingGPU(); });
88+
8689
// Worker task runner.
8790
{
8891
raster_message_loop_ = fml::ConcurrentMessageLoop::Create(
@@ -376,4 +379,15 @@ new ContextMTL(device, command_queue,
376379
return buffer;
377380
}
378381

382+
void ContextMTL::StoreTaskForGPU(std::function<void()> task) {
383+
tasks_awaiting_gpu_.emplace_back(std::move(task));
384+
}
385+
386+
void ContextMTL::FlushTasksAwaitingGPU() {
387+
for (const auto& task : tasks_awaiting_gpu_) {
388+
task();
389+
}
390+
tasks_awaiting_gpu_.clear();
391+
}
392+
379393
} // namespace impeller

impeller/renderer/context.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,12 @@ class Context {
176176

177177
CaptureContext capture;
178178

179+
/// Threadsafe.
180+
/// `task` should be executed on the platform thread.
181+
virtual void StoreTaskForGPU(std::function<void()> task) {
182+
FML_CHECK(false && "not supported in this context");
183+
}
184+
179185
protected:
180186
Context();
181187

lib/ui/painting/image_encoding_impeller.cc

Lines changed: 53 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -56,21 +56,23 @@ sk_sp<SkImage> ConvertBufferToSkImage(
5656
return raster_image;
5757
}
5858

59-
void DoConvertImageToRasterImpeller(
59+
[[nodiscard]] fml::Status DoConvertImageToRasterImpeller(
6060
const sk_sp<DlImage>& dl_image,
61-
std::function<void(fml::StatusOr<sk_sp<SkImage>>)> encode_task,
61+
const std::function<void(fml::StatusOr<sk_sp<SkImage>>)>& encode_task,
6262
const std::shared_ptr<const fml::SyncSwitch>& is_gpu_disabled_sync_switch,
6363
const std::shared_ptr<impeller::Context>& impeller_context) {
64+
fml::Status result;
6465
is_gpu_disabled_sync_switch->Execute(
6566
fml::SyncSwitch::Handlers()
66-
.SetIfTrue([&encode_task] {
67-
encode_task(
68-
fml::Status(fml::StatusCode::kUnavailable, "GPU unavailable."));
67+
.SetIfTrue([&result] {
68+
result =
69+
fml::Status(fml::StatusCode::kUnavailable, "GPU unavailable.");
6970
})
7071
.SetIfFalse([&dl_image, &encode_task, &impeller_context] {
7172
ImageEncodingImpeller::ConvertDlImageToSkImage(
7273
dl_image, std::move(encode_task), impeller_context);
7374
}));
75+
return result;
7476
}
7577

7678
} // namespace
@@ -153,18 +155,57 @@ void ImageEncodingImpeller::ConvertImageToRaster(
153155
};
154156

155157
if (dl_image->owning_context() != DlImage::OwningContext::kRaster) {
156-
DoConvertImageToRasterImpeller(dl_image, std::move(encode_task),
157-
is_gpu_disabled_sync_switch,
158-
impeller_context);
158+
fml::Status status = DoConvertImageToRasterImpeller(
159+
dl_image, encode_task, is_gpu_disabled_sync_switch, impeller_context);
160+
if (!status.ok()) {
161+
if (status.code() == fml::StatusCode::kUnavailable) {
162+
impeller_context->StoreTaskForGPU([dl_image = std::move(dl_image),
163+
encode_task = std::move(encode_task),
164+
is_gpu_disabled_sync_switch,
165+
impeller_context] {
166+
fml::Status retry_status = DoConvertImageToRasterImpeller(
167+
dl_image, encode_task, is_gpu_disabled_sync_switch,
168+
impeller_context);
169+
if (!retry_status.ok()) {
170+
encode_task(retry_status);
171+
}
172+
});
173+
} else {
174+
encode_task(status);
175+
}
176+
}
159177
return;
160178
}
161179

162180
raster_task_runner->PostTask([dl_image, encode_task = std::move(encode_task),
163181
io_task_runner, is_gpu_disabled_sync_switch,
164-
impeller_context]() mutable {
165-
DoConvertImageToRasterImpeller(dl_image, std::move(encode_task),
166-
is_gpu_disabled_sync_switch,
167-
impeller_context);
182+
impeller_context,
183+
raster_task_runner]() mutable {
184+
fml::Status status = DoConvertImageToRasterImpeller(
185+
dl_image, std::move(encode_task), is_gpu_disabled_sync_switch,
186+
impeller_context);
187+
if (!status.ok()) {
188+
if (status.code() == fml::StatusCode::kUnavailable) {
189+
impeller_context->StoreTaskForGPU(
190+
[dl_image = std::move(dl_image),
191+
encode_task = std::move(encode_task), is_gpu_disabled_sync_switch,
192+
impeller_context, raster_task_runner] {
193+
raster_task_runner->PostTask(
194+
[dl_image = std::move(dl_image),
195+
encode_task = std::move(encode_task),
196+
is_gpu_disabled_sync_switch, impeller_context] {
197+
fml::Status retry_status = DoConvertImageToRasterImpeller(
198+
dl_image, encode_task, is_gpu_disabled_sync_switch,
199+
impeller_context);
200+
if (!retry_status.ok()) {
201+
encode_task(retry_status);
202+
}
203+
});
204+
});
205+
} else {
206+
encode_task(status);
207+
}
208+
}
168209
});
169210
}
170211

0 commit comments

Comments
 (0)