deps: update zig-objc

This update also fixes a memory leak that was caused by blocks not being
deallocated and just collecting every single frame, slowly accumulating
memory until OOM.
This commit is contained in:
Qwerasd
2025-07-02 16:16:33 -06:00
parent 1f733c9e7f
commit 8ed08aaecf
7 changed files with 30 additions and 37 deletions

View File

@@ -28,7 +28,7 @@ pub const Options = struct {
/// MTLCommandBuffer
buffer: objc.Object,
block: CompletionBlock,
block: CompletionBlock.Context,
/// Begin encoding a frame.
pub fn begin(
@@ -47,7 +47,7 @@ pub fn begin(
// Create our block to register for completion updates.
// The block is deallocated by the objC runtime on success.
const block = try CompletionBlock.init(
const block = CompletionBlock.init(
.{
.renderer = renderer,
.target = target,
@@ -55,7 +55,6 @@ pub fn begin(
},
&bufferCompleted,
);
errdefer block.deinit();
return .{ .buffer = buffer, .block = block };
}
@@ -114,24 +113,23 @@ pub inline fn complete(self: *Self, sync: bool) void {
// If we don't need to complete synchronously,
// we add our block as a completion handler.
//
// It will be deallocated by the objc runtime on success.
// It will be copied when we add the handler, and then the
// copy will be deallocated by the objc runtime on success.
if (!sync) {
self.buffer.msgSend(
void,
objc.sel("addCompletedHandler:"),
.{self.block.context},
.{&self.block},
);
}
self.buffer.msgSend(void, objc.sel("commit"), .{});
// If we need to complete synchronously, we wait until
// the buffer is completed and call the callback directly,
// deiniting the block after we're done.
// the buffer is completed and invoke the block directly.
if (sync) {
self.buffer.msgSend(void, "waitUntilCompleted", .{});
self.block.context.sync = true;
bufferCompleted(self.block.context, self.buffer.value);
self.block.deinit();
self.block.sync = true;
CompletionBlock.invoke(&self.block, .{self.buffer.value});
}
}

View File

@@ -54,13 +54,11 @@ pub inline fn setSurface(self: *IOSurfaceLayer, surface: *IOSurface) !void {
//
// We release in the callback after setting the contents.
surface.retain();
// We also need to retain the layer itself to make sure it
// isn't destroyed before the callback completes, since if
// that happens it will try to interact with a deallocated
// object.
_ = self.layer.retain();
// NOTE: Since `self.layer` is passed as an `objc.c.id`, it's
// automatically retained when the block is copied, so we
// don't need to retain it ourselves like with the surface.
var block = try SetSurfaceBlock.init(.{
var block = SetSurfaceBlock.init(.{
.layer = self.layer.value,
.surface = surface,
}, &setSurfaceCallback);
@@ -68,15 +66,15 @@ pub inline fn setSurface(self: *IOSurfaceLayer, surface: *IOSurface) !void {
// We check if we're on the main thread and run the block directly if so.
const NSThread = objc.getClass("NSThread").?;
if (NSThread.msgSend(bool, "isMainThread", .{})) {
setSurfaceCallback(block.context);
block.deinit();
setSurfaceCallback(&block);
} else {
// NOTE: The block will automatically be deallocated by the objc
// runtime once it's executed, so there's no need to deinit it.
// NOTE: The block will be copied when we pass it to dispatch_async,
// and then automatically be deallocated by the objc runtime
// once it's executed.
macos.dispatch.dispatch_async(
@ptrCast(macos.dispatch.queue.getMain()),
@ptrCast(block.context),
@ptrCast(&block),
);
}
}
@@ -100,10 +98,7 @@ fn setSurfaceCallback(
const surface: *IOSurface = block.surface;
// See explanation of why we retain and release in `setSurface`.
defer {
surface.release();
layer.release();
}
defer surface.release();
// We check to see if the surface is the appropriate size for
// the layer, if it's not then we discard it. This is because