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

Commit cf05ab4

Browse files
authored
[embedder] Ensure FlutterMetalTexture cleanup call (#38038)
This ensures FlutterMetalTexture.destruction_callback gets called. FlutterRendererConfig.get_next_drawable_callback holds a callback used by the embedder API to request a drawable; in the case of Metal, this drawable is a FlutterMetalTexture. FlutterMetalTexture.destruction_callback should be called when it's safe to release resources associated with the FlutterMetalTexture. This callback is not currently invoked for textures returned via FlutterRendererConfig.get_next_drawable_callback; instead we unpack the returned struct and pass it on. In the compositor codepath, we do create an SkSurface that triggers the destruction callback, here: https://github.com/flutter/engine/blob/303e26e96561d9b76f2344e97a5fc32eb6dfdb9a/shell/platform/embedder/embedder.cc#L868-L881 Issue: flutter/flutter#116381
1 parent f9067ed commit cf05ab4

7 files changed

+104
-9
lines changed

shell/gpu/gpu_surface_metal_delegate.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,13 @@ typedef void* GPUCAMetalLayerHandle;
2626
// expected to be id<MTLTexture>
2727
typedef const void* GPUMTLTextureHandle;
2828

29+
typedef void (*GPUMTLDestructionCallback)(void* /* destruction_context */);
30+
2931
struct GPUMTLTextureInfo {
3032
int64_t texture_id;
3133
GPUMTLTextureHandle texture;
34+
GPUMTLDestructionCallback destruction_callback;
35+
void* destruction_context;
3236
};
3337

3438
enum class MTLRenderTargetType { kMTLTexture, kCAMetalLayer };

shell/gpu/gpu_surface_metal_skia.mm

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,15 @@
3838
MsaaSampleCount sample_cnt,
3939
SkColorType color_type,
4040
sk_sp<SkColorSpace> color_space,
41-
const SkSurfaceProps* props) {
41+
const SkSurfaceProps* props,
42+
SkSurface::TextureReleaseProc release_proc,
43+
SkSurface::ReleaseContext release_context) {
4244
GrMtlTextureInfo info;
4345
info.fTexture.reset([texture retain]);
4446
GrBackendTexture backend_texture(texture.width, texture.height, GrMipmapped::kNo, info);
45-
return SkSurface::MakeFromBackendTexture(context, backend_texture, origin,
46-
static_cast<int>(sample_cnt), color_type,
47-
std::move(color_space), props);
47+
return SkSurface::MakeFromBackendTexture(
48+
context, backend_texture, origin, static_cast<int>(sample_cnt), color_type,
49+
std::move(color_space), props, release_proc, release_context);
4850
}
4951
} // namespace
5052

@@ -137,7 +139,9 @@
137139
msaa_samples_, // sample count
138140
kBGRA_8888_SkColorType, // color type
139141
nullptr, // colorspace
140-
nullptr // surface properties
142+
nullptr, // surface properties
143+
nullptr, // release proc
144+
nullptr // release context
141145
);
142146

143147
if (!surface) {
@@ -203,9 +207,10 @@
203207
return nullptr;
204208
}
205209

206-
sk_sp<SkSurface> surface =
207-
CreateSurfaceFromMetalTexture(context_.get(), mtl_texture, kTopLeft_GrSurfaceOrigin,
208-
msaa_samples_, kBGRA_8888_SkColorType, nullptr, nullptr);
210+
sk_sp<SkSurface> surface = CreateSurfaceFromMetalTexture(
211+
context_.get(), mtl_texture, kTopLeft_GrSurfaceOrigin, msaa_samples_, kBGRA_8888_SkColorType,
212+
nullptr, nullptr, static_cast<SkSurface::TextureReleaseProc>(texture.destruction_callback),
213+
texture.destruction_context);
209214

210215
if (!surface) {
211216
FML_LOG(ERROR) << "Could not create the SkSurface from the metal texture.";

shell/platform/embedder/embedder.cc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -477,6 +477,8 @@ InferMetalPlatformViewCreationCallback(
477477
FlutterMetalTexture metal_texture = ptr(user_data, &frame_info);
478478
texture_info.texture_id = metal_texture.texture_id;
479479
texture_info.texture = metal_texture.texture;
480+
texture_info.destruction_callback = metal_texture.destruction_callback;
481+
texture_info.destruction_context = metal_texture.user_data;
480482
return texture_info;
481483
};
482484

shell/platform/embedder/fixtures/main.dart

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -498,6 +498,25 @@ void can_composite_platform_views_with_platform_layer_on_bottom() {
498498
PlatformDispatcher.instance.scheduleFrame();
499499
}
500500

501+
@pragma('vm:external-name', 'SignalBeginFrame')
502+
external void signalBeginFrame();
503+
504+
@pragma('vm:entry-point')
505+
void texture_destruction_callback_called_without_custom_compositor() async {
506+
PlatformDispatcher.instance.onBeginFrame = (Duration duration) {
507+
Color red = Color.fromARGB(127, 255, 0, 0);
508+
Size size = Size(50.0, 150.0);
509+
SceneBuilder builder = SceneBuilder();
510+
builder.pushOffset(0.0, 0.0);
511+
builder.addPicture(
512+
Offset(10.0, 10.0), CreateColoredBox(red, size)); // red - flutter
513+
builder.pop();
514+
PlatformDispatcher.instance.views.first.render(builder.build());
515+
};
516+
PlatformDispatcher.instance.scheduleFrame();
517+
}
518+
519+
501520
@pragma('vm:entry-point')
502521
void can_render_scene_without_custom_compositor() {
503522
PlatformDispatcher.instance.onBeginFrame = (Duration duration) {

shell/platform/embedder/tests/embedder_test_context_metal.cc

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,10 +71,22 @@ bool EmbedderTestContextMetal::PopulateExternalTexture(
7171
}
7272
}
7373

74+
TestMetalContext::TextureInfo EmbedderTestContextMetal::GetTextureInfo() {
75+
return metal_surface_->GetTextureInfo();
76+
}
77+
78+
void EmbedderTestContextMetal::SetNextDrawableCallback(
79+
NextDrawableCallback next_drawable_callback) {
80+
next_drawable_callback_ = std::move(next_drawable_callback);
81+
}
82+
7483
FlutterMetalTexture EmbedderTestContextMetal::GetNextDrawable(
7584
const FlutterFrameInfo* frame_info) {
76-
auto texture_info = metal_surface_->GetTextureInfo();
85+
if (next_drawable_callback_ != nullptr) {
86+
return next_drawable_callback_(frame_info);
87+
}
7788

89+
auto texture_info = metal_surface_->GetTextureInfo();
7890
FlutterMetalTexture texture;
7991
texture.struct_size = sizeof(FlutterMetalTexture);
8092
texture.texture_id = texture_info.texture_id;

shell/platform/embedder/tests/embedder_test_context_metal.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ class EmbedderTestContextMetal : public EmbedderTestContext {
2020
size_t h,
2121
FlutterMetalExternalTexture* output)>;
2222

23+
using NextDrawableCallback =
24+
std::function<FlutterMetalTexture(const FlutterFrameInfo* frame_info)>;
25+
2326
explicit EmbedderTestContextMetal(std::string assets_path = "");
2427

2528
~EmbedderTestContextMetal() override;
@@ -45,6 +48,12 @@ class EmbedderTestContextMetal : public EmbedderTestContext {
4548

4649
TestMetalContext* GetTestMetalContext();
4750

51+
// Returns the TextureInfo for the test Metal surface.
52+
TestMetalContext::TextureInfo GetTextureInfo();
53+
54+
// Override the default handling for GetNextDrawable.
55+
void SetNextDrawableCallback(NextDrawableCallback next_drawable_callback);
56+
4857
FlutterMetalTexture GetNextDrawable(const FlutterFrameInfo* frame_info);
4958

5059
private:
@@ -56,6 +65,7 @@ class EmbedderTestContextMetal : public EmbedderTestContext {
5665
std::unique_ptr<TestMetalContext> metal_context_;
5766
std::unique_ptr<TestMetalSurface> metal_surface_;
5867
size_t present_count_ = 0;
68+
NextDrawableCallback next_drawable_callback_ = nullptr;
5969

6070
void SetupSurface(SkISize surface_size) override;
6171

shell/platform/embedder/tests/embedder_unittests_metal.mm

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,49 @@ GrBackendTexture backend_texture(texture_size.width(), texture_size.height(), Gr
227227
ASSERT_TRUE(ImageMatchesFixture("scene_without_custom_compositor.png", rendered_scene));
228228
}
229229

230+
TEST_F(EmbedderTest, TextureDestructionCallbackCalledWithoutCustomCompositorMetal) {
231+
EmbedderTestContextMetal& context = reinterpret_cast<EmbedderTestContextMetal&>(
232+
GetEmbedderContext(EmbedderTestContextType::kMetalContext));
233+
EmbedderConfigBuilder builder(context);
234+
builder.SetMetalRendererConfig(SkISize::Make(800, 600));
235+
builder.SetDartEntrypoint("texture_destruction_callback_called_without_custom_compositor");
236+
237+
struct CollectContext {
238+
int collect_count = 0;
239+
fml::AutoResetWaitableEvent latch;
240+
};
241+
242+
auto collect_context = std::make_unique<CollectContext>();
243+
context.SetNextDrawableCallback([&context, &collect_context](const FlutterFrameInfo* frame_info) {
244+
auto texture_info = context.GetTextureInfo();
245+
FlutterMetalTexture texture;
246+
texture.struct_size = sizeof(FlutterMetalTexture);
247+
texture.texture_id = texture_info.texture_id;
248+
texture.texture = reinterpret_cast<FlutterMetalTextureHandle>(texture_info.texture);
249+
texture.user_data = collect_context.get();
250+
texture.destruction_callback = [](void* user_data) {
251+
CollectContext* callback_collect_context = reinterpret_cast<CollectContext*>(user_data);
252+
callback_collect_context->collect_count++;
253+
callback_collect_context->latch.Signal();
254+
};
255+
return texture;
256+
});
257+
258+
auto engine = builder.LaunchEngine();
259+
260+
// Send a window metrics events so frames may be scheduled.
261+
FlutterWindowMetricsEvent event = {};
262+
event.struct_size = sizeof(event);
263+
event.width = 800;
264+
event.height = 600;
265+
event.pixel_ratio = 1.0;
266+
ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), kSuccess);
267+
ASSERT_TRUE(engine.is_valid());
268+
269+
collect_context->latch.Wait();
270+
EXPECT_EQ(collect_context->collect_count, 1);
271+
}
272+
230273
TEST_F(EmbedderTest, CompositorMustBeAbleToRenderKnownSceneMetal) {
231274
auto& context = GetEmbedderContext(EmbedderTestContextType::kMetalContext);
232275

0 commit comments

Comments
 (0)