b0fdd27b
Guard offscreen renderer against shared-state hazards
a73x 2026-04-17 15:20
renderToOffscreen now waits on in_flight_fence before touching the shared instance buffer, preventing the host from overwriting bytes still being read by an in-flight swapchain draw. readbackOffscreen now asserts target.format is BGRA8, since the BGRA→RGBA swizzle assumes that layout. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
diff --git a/src/renderer.zig b/src/renderer.zig index 118e483..0d67a9b 100644 --- a/src/renderer.zig +++ b/src/renderer.zig @@ -1781,6 +1781,12 @@ pub const Context = struct { instance_data: []const Instance, push: PushConstants, ) !void { // Wait for any in-flight swapchain frame to complete before touching the // shared instance buffer. (drawCells and renderToOffscreen share // self.instance_memory; without this wait the host would overwrite bytes // the GPU is still reading.) _ = try self.vkd.waitForFences(self.device, 1, @ptrCast(&self.in_flight_fence), .true, std.math.maxInt(u64)); // 1. Upload instances (same path drawCells uses) try self.uploadInstances(instance_data); @@ -1915,6 +1921,8 @@ pub const Context = struct { target: *const OffscreenTarget, out_rgba: []u8, ) !void { std.debug.assert(target.format == .b8g8r8a8_unorm); // swizzle below assumes BGRA8 std.debug.assert(out_rgba.len == target.width * target.height * 4); if (out_rgba.len < target.readback_size) return error.BufferTooSmall; const mapped = try self.vkd.mapMemory(