mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
[embedder] Ensure FlutterMetalTexture cleanup call (flutter/engine#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:
bbdb5d6a3e/shell/platform/embedder/embedder.cc (L868-L881)
Issue: https://github.com/flutter/flutter/issues/116381
This commit is contained in:
parent
83ca5f4334
commit
d260fc82cd
@ -26,9 +26,13 @@ typedef void* GPUCAMetalLayerHandle;
|
||||
// expected to be id<MTLTexture>
|
||||
typedef const void* GPUMTLTextureHandle;
|
||||
|
||||
typedef void (*GPUMTLDestructionCallback)(void* /* destruction_context */);
|
||||
|
||||
struct GPUMTLTextureInfo {
|
||||
int64_t texture_id;
|
||||
GPUMTLTextureHandle texture;
|
||||
GPUMTLDestructionCallback destruction_callback;
|
||||
void* destruction_context;
|
||||
};
|
||||
|
||||
enum class MTLRenderTargetType { kMTLTexture, kCAMetalLayer };
|
||||
|
||||
@ -38,13 +38,15 @@ sk_sp<SkSurface> CreateSurfaceFromMetalTexture(GrDirectContext* context,
|
||||
MsaaSampleCount sample_cnt,
|
||||
SkColorType color_type,
|
||||
sk_sp<SkColorSpace> color_space,
|
||||
const SkSurfaceProps* props) {
|
||||
const SkSurfaceProps* props,
|
||||
SkSurface::TextureReleaseProc release_proc,
|
||||
SkSurface::ReleaseContext release_context) {
|
||||
GrMtlTextureInfo info;
|
||||
info.fTexture.reset([texture retain]);
|
||||
GrBackendTexture backend_texture(texture.width, texture.height, GrMipmapped::kNo, info);
|
||||
return SkSurface::MakeFromBackendTexture(context, backend_texture, origin,
|
||||
static_cast<int>(sample_cnt), color_type,
|
||||
std::move(color_space), props);
|
||||
return SkSurface::MakeFromBackendTexture(
|
||||
context, backend_texture, origin, static_cast<int>(sample_cnt), color_type,
|
||||
std::move(color_space), props, release_proc, release_context);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
@ -137,7 +139,9 @@ std::unique_ptr<SurfaceFrame> GPUSurfaceMetalSkia::AcquireFrameFromCAMetalLayer(
|
||||
msaa_samples_, // sample count
|
||||
kBGRA_8888_SkColorType, // color type
|
||||
nullptr, // colorspace
|
||||
nullptr // surface properties
|
||||
nullptr, // surface properties
|
||||
nullptr, // release proc
|
||||
nullptr // release context
|
||||
);
|
||||
|
||||
if (!surface) {
|
||||
@ -203,9 +207,10 @@ std::unique_ptr<SurfaceFrame> GPUSurfaceMetalSkia::AcquireFrameFromMTLTexture(
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
sk_sp<SkSurface> surface =
|
||||
CreateSurfaceFromMetalTexture(context_.get(), mtl_texture, kTopLeft_GrSurfaceOrigin,
|
||||
msaa_samples_, kBGRA_8888_SkColorType, nullptr, nullptr);
|
||||
sk_sp<SkSurface> surface = CreateSurfaceFromMetalTexture(
|
||||
context_.get(), mtl_texture, kTopLeft_GrSurfaceOrigin, msaa_samples_, kBGRA_8888_SkColorType,
|
||||
nullptr, nullptr, static_cast<SkSurface::TextureReleaseProc>(texture.destruction_callback),
|
||||
texture.destruction_context);
|
||||
|
||||
if (!surface) {
|
||||
FML_LOG(ERROR) << "Could not create the SkSurface from the metal texture.";
|
||||
|
||||
@ -477,6 +477,8 @@ InferMetalPlatformViewCreationCallback(
|
||||
FlutterMetalTexture metal_texture = ptr(user_data, &frame_info);
|
||||
texture_info.texture_id = metal_texture.texture_id;
|
||||
texture_info.texture = metal_texture.texture;
|
||||
texture_info.destruction_callback = metal_texture.destruction_callback;
|
||||
texture_info.destruction_context = metal_texture.user_data;
|
||||
return texture_info;
|
||||
};
|
||||
|
||||
|
||||
@ -498,6 +498,25 @@ void can_composite_platform_views_with_platform_layer_on_bottom() {
|
||||
PlatformDispatcher.instance.scheduleFrame();
|
||||
}
|
||||
|
||||
@pragma('vm:external-name', 'SignalBeginFrame')
|
||||
external void signalBeginFrame();
|
||||
|
||||
@pragma('vm:entry-point')
|
||||
void texture_destruction_callback_called_without_custom_compositor() async {
|
||||
PlatformDispatcher.instance.onBeginFrame = (Duration duration) {
|
||||
Color red = Color.fromARGB(127, 255, 0, 0);
|
||||
Size size = Size(50.0, 150.0);
|
||||
SceneBuilder builder = SceneBuilder();
|
||||
builder.pushOffset(0.0, 0.0);
|
||||
builder.addPicture(
|
||||
Offset(10.0, 10.0), CreateColoredBox(red, size)); // red - flutter
|
||||
builder.pop();
|
||||
PlatformDispatcher.instance.views.first.render(builder.build());
|
||||
};
|
||||
PlatformDispatcher.instance.scheduleFrame();
|
||||
}
|
||||
|
||||
|
||||
@pragma('vm:entry-point')
|
||||
void can_render_scene_without_custom_compositor() {
|
||||
PlatformDispatcher.instance.onBeginFrame = (Duration duration) {
|
||||
|
||||
@ -71,10 +71,22 @@ bool EmbedderTestContextMetal::PopulateExternalTexture(
|
||||
}
|
||||
}
|
||||
|
||||
TestMetalContext::TextureInfo EmbedderTestContextMetal::GetTextureInfo() {
|
||||
return metal_surface_->GetTextureInfo();
|
||||
}
|
||||
|
||||
void EmbedderTestContextMetal::SetNextDrawableCallback(
|
||||
NextDrawableCallback next_drawable_callback) {
|
||||
next_drawable_callback_ = std::move(next_drawable_callback);
|
||||
}
|
||||
|
||||
FlutterMetalTexture EmbedderTestContextMetal::GetNextDrawable(
|
||||
const FlutterFrameInfo* frame_info) {
|
||||
auto texture_info = metal_surface_->GetTextureInfo();
|
||||
if (next_drawable_callback_ != nullptr) {
|
||||
return next_drawable_callback_(frame_info);
|
||||
}
|
||||
|
||||
auto texture_info = metal_surface_->GetTextureInfo();
|
||||
FlutterMetalTexture texture;
|
||||
texture.struct_size = sizeof(FlutterMetalTexture);
|
||||
texture.texture_id = texture_info.texture_id;
|
||||
|
||||
@ -20,6 +20,9 @@ class EmbedderTestContextMetal : public EmbedderTestContext {
|
||||
size_t h,
|
||||
FlutterMetalExternalTexture* output)>;
|
||||
|
||||
using NextDrawableCallback =
|
||||
std::function<FlutterMetalTexture(const FlutterFrameInfo* frame_info)>;
|
||||
|
||||
explicit EmbedderTestContextMetal(std::string assets_path = "");
|
||||
|
||||
~EmbedderTestContextMetal() override;
|
||||
@ -45,6 +48,12 @@ class EmbedderTestContextMetal : public EmbedderTestContext {
|
||||
|
||||
TestMetalContext* GetTestMetalContext();
|
||||
|
||||
// Returns the TextureInfo for the test Metal surface.
|
||||
TestMetalContext::TextureInfo GetTextureInfo();
|
||||
|
||||
// Override the default handling for GetNextDrawable.
|
||||
void SetNextDrawableCallback(NextDrawableCallback next_drawable_callback);
|
||||
|
||||
FlutterMetalTexture GetNextDrawable(const FlutterFrameInfo* frame_info);
|
||||
|
||||
private:
|
||||
@ -56,6 +65,7 @@ class EmbedderTestContextMetal : public EmbedderTestContext {
|
||||
std::unique_ptr<TestMetalContext> metal_context_;
|
||||
std::unique_ptr<TestMetalSurface> metal_surface_;
|
||||
size_t present_count_ = 0;
|
||||
NextDrawableCallback next_drawable_callback_ = nullptr;
|
||||
|
||||
void SetupSurface(SkISize surface_size) override;
|
||||
|
||||
|
||||
@ -227,6 +227,49 @@ TEST_F(EmbedderTest, CanRenderSceneWithoutCustomCompositorMetal) {
|
||||
ASSERT_TRUE(ImageMatchesFixture("scene_without_custom_compositor.png", rendered_scene));
|
||||
}
|
||||
|
||||
TEST_F(EmbedderTest, TextureDestructionCallbackCalledWithoutCustomCompositorMetal) {
|
||||
EmbedderTestContextMetal& context = reinterpret_cast<EmbedderTestContextMetal&>(
|
||||
GetEmbedderContext(EmbedderTestContextType::kMetalContext));
|
||||
EmbedderConfigBuilder builder(context);
|
||||
builder.SetMetalRendererConfig(SkISize::Make(800, 600));
|
||||
builder.SetDartEntrypoint("texture_destruction_callback_called_without_custom_compositor");
|
||||
|
||||
struct CollectContext {
|
||||
int collect_count = 0;
|
||||
fml::AutoResetWaitableEvent latch;
|
||||
};
|
||||
|
||||
auto collect_context = std::make_unique<CollectContext>();
|
||||
context.SetNextDrawableCallback([&context, &collect_context](const FlutterFrameInfo* frame_info) {
|
||||
auto texture_info = context.GetTextureInfo();
|
||||
FlutterMetalTexture texture;
|
||||
texture.struct_size = sizeof(FlutterMetalTexture);
|
||||
texture.texture_id = texture_info.texture_id;
|
||||
texture.texture = reinterpret_cast<FlutterMetalTextureHandle>(texture_info.texture);
|
||||
texture.user_data = collect_context.get();
|
||||
texture.destruction_callback = [](void* user_data) {
|
||||
CollectContext* callback_collect_context = reinterpret_cast<CollectContext*>(user_data);
|
||||
callback_collect_context->collect_count++;
|
||||
callback_collect_context->latch.Signal();
|
||||
};
|
||||
return texture;
|
||||
});
|
||||
|
||||
auto engine = builder.LaunchEngine();
|
||||
|
||||
// Send a window metrics events so frames may be scheduled.
|
||||
FlutterWindowMetricsEvent event = {};
|
||||
event.struct_size = sizeof(event);
|
||||
event.width = 800;
|
||||
event.height = 600;
|
||||
event.pixel_ratio = 1.0;
|
||||
ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), kSuccess);
|
||||
ASSERT_TRUE(engine.is_valid());
|
||||
|
||||
collect_context->latch.Wait();
|
||||
EXPECT_EQ(collect_context->collect_count, 1);
|
||||
}
|
||||
|
||||
TEST_F(EmbedderTest, CompositorMustBeAbleToRenderKnownSceneMetal) {
|
||||
auto& context = GetEmbedderContext(EmbedderTestContextType::kMetalContext);
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user