[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

This commit is contained in:
Martin Fouilleul 2023-03-15 13:02:18 +01:00
parent 5ce53001e2
commit 0c74e997ae
2 changed files with 13 additions and 5 deletions

View File

@ -103,7 +103,7 @@ void mg_mtl_canvas_begin(mg_canvas_backend* interface, mg_color clearColor)
} }
@autoreleasepool @autoreleasepool
{ {
if(surface->commandBuffer == nil || surface->commandBuffer == nil) if(surface->commandBuffer == nil)
{ {
mg_mtl_surface_acquire_drawable_and_command_buffer(surface); mg_mtl_surface_acquire_drawable_and_command_buffer(surface);
} }

View File

@ -42,6 +42,13 @@ void mg_mtl_surface_destroy(mg_surface_data* interface)
@autoreleasepool @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<MTLCommandBuffer> endBuffer = [surface->commandQueue commandBuffer];
[endBuffer commit];
[endBuffer waitUntilCompleted];
[surface->commandQueue release]; [surface->commandQueue release];
[surface->mtlLayer removeFromSuperlayer]; [surface->mtlLayer removeFromSuperlayer];
[surface->mtlLayer release]; [surface->mtlLayer release];
@ -107,13 +114,14 @@ void mg_mtl_surface_present(mg_surface_data* interface)
//NOTE(martin): present drawable and commit command buffer //NOTE(martin): present drawable and commit command buffer
[surface->commandBuffer presentDrawable: surface->drawable]; [surface->commandBuffer presentDrawable: surface->drawable];
[surface->commandBuffer commit]; [surface->commandBuffer commit];
[surface->commandBuffer waitUntilCompleted]; // [surface->commandBuffer waitUntilCompleted];
//NOTE(martin): acquire next frame resources //TODO: do we really need this?
[surface->commandBuffer release];
surface->commandBuffer = nil;
[surface->drawable release]; [surface->drawable release];
surface->drawable = nil; surface->drawable = nil;
[surface->commandBuffer release];
surface->commandBuffer = nil;
} }
} }