From 0c74e997ae829947d2d11fbe3e56dbbc9c9f0057 Mon Sep 17 00:00:00 2001 From: Martin Fouilleul Date: Wed, 15 Mar 2023 13:02:18 +0100 Subject: [PATCH] [mtl surface] fix crash on exit when metal GPU frame capture is enabled. Apparently, if we release resources (on exit) before all GPU work is completed, libMetalCapture crashes on bad access. To work around that, in mg_mtl_surface_destroy(), we enqueue a last (empty) command buffer and call waitUntilCompleted on it, to ensure all previous command buffers are also completed --- src/mtl_canvas.m | 2 +- src/mtl_surface.m | 16 ++++++++++++---- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/mtl_canvas.m b/src/mtl_canvas.m index 884af9e..136329d 100644 --- a/src/mtl_canvas.m +++ b/src/mtl_canvas.m @@ -103,7 +103,7 @@ void mg_mtl_canvas_begin(mg_canvas_backend* interface, mg_color clearColor) } @autoreleasepool { - if(surface->commandBuffer == nil || surface->commandBuffer == nil) + if(surface->commandBuffer == nil) { mg_mtl_surface_acquire_drawable_and_command_buffer(surface); } diff --git a/src/mtl_surface.m b/src/mtl_surface.m index e963024..656ab7b 100644 --- a/src/mtl_surface.m +++ b/src/mtl_surface.m @@ -42,6 +42,13 @@ void mg_mtl_surface_destroy(mg_surface_data* interface) @autoreleasepool { + //NOTE: when GPU frame capture is enabled, if we release resources before all work is completed, + // libMetalCapture crashes... the following hack avoids this crash by enqueuing a last (empty) + // command buffer and waiting for it to complete, ensuring all previous buffers have completed + id endBuffer = [surface->commandQueue commandBuffer]; + [endBuffer commit]; + [endBuffer waitUntilCompleted]; + [surface->commandQueue release]; [surface->mtlLayer removeFromSuperlayer]; [surface->mtlLayer release]; @@ -107,13 +114,14 @@ void mg_mtl_surface_present(mg_surface_data* interface) //NOTE(martin): present drawable and commit command buffer [surface->commandBuffer presentDrawable: surface->drawable]; [surface->commandBuffer commit]; - [surface->commandBuffer waitUntilCompleted]; +// [surface->commandBuffer waitUntilCompleted]; - //NOTE(martin): acquire next frame resources - [surface->commandBuffer release]; - surface->commandBuffer = nil; + //TODO: do we really need this? [surface->drawable release]; surface->drawable = nil; + + [surface->commandBuffer release]; + surface->commandBuffer = nil; } }