🔍 Module Scanned
src/engine/graphics/vulkan/ (automated audit scan)
📝 Summary
The captureScreenshot function in screenshot.zig directly calls vkQueueSubmit on the graphics queue without going through the thread-safe submitGuarded wrapper that all other queue submissions use. Additionally, it swallows all errors with a generic catch-all, making debugging impossible.
📍 Location
- File:
src/engine/graphics/vulkan/screenshot.zig:157
- Function/Scope:
captureScreenshot
🔴 Severity: Medium
- Critical: Crashes, data corruption, GPU device loss
- High: Memory leaks, race conditions, incorrect rendering, broken features
- Medium: Performance degradation, missing error handling, suboptimal patterns
- Low: Code style, dead code, minor improvements
💥 Impact
If captureScreenshot is ever called concurrently with the normal render loop (e.g., from a background thread, or if the screenshot path is triggered during a frame), it would race with the guarded submitGuarded calls in FrameManager.endFrame(), breaking the mutex-based serialization guarantee and causing undefined Vulkan behavior.
Even if called only from the main thread, the function silently swallows all errors via a catch-all, meaning users cannot diagnose why screenshot capture failed.
🔎 Evidence
screenshot.zig:157:
Utils.checkVk(c.vkQueueSubmit(device.queue, 1, &submit_info, fence)) catch {
log.log.err("screenshot: failed to submit command buffer", .{});
return false;
};
Compare to the proper pattern in frame_manager.zig:195:
try self.vulkan_device.submitGuarded(submit_info, self.in_flight_fences[self.current_frame]);
The submitGuarded function (in modules/engine-graphics/src/vulkan_device.zig:463-477) properly:
- Acquires
device.mutex before submission
- Checks for
VK_ERROR_DEVICE_LOST and logs detailed fault info
- Returns a typed error for proper handling
screenshot.zig does none of this - it directly submits without mutex protection and catches all errors to a boolean false.
🛠️ Proposed Fix
Modify captureScreenshot to use the device mutex around the queue submission:
device.mutex.lock();
const result = c.vkQueueSubmit(device.queue, 1, &submit_info, fence);
device.mutex.unlock();
if (result == c.VK_ERROR_DEVICE_LOST) {
log.log.err("screenshot: GPU device lost during command buffer submit", .{});
return false;
}
if (result != c.VK_SUCCESS) {
log.log.err("screenshot: vkQueueSubmit failed with result: {}", .{result});
return false;
}
Also consider surfacing the error rather than returning bool so callers can handle specific failure modes.
✅ Acceptance Criteria
📚 References
modules/engine-graphics/src/vulkan_device.zig:463-477 — submitGuarded implementation showing correct pattern
src/engine/graphics/vulkan/frame_manager.zig:195 — Correct usage of submitGuarded
- Vulkan spec §7.2 "Thread Safety" — Queue submission synchronization requirements
🔍 Module Scanned
src/engine/graphics/vulkan/(automated audit scan)📝 Summary
The
captureScreenshotfunction inscreenshot.zigdirectly callsvkQueueSubmiton the graphics queue without going through the thread-safesubmitGuardedwrapper that all other queue submissions use. Additionally, it swallows all errors with a generic catch-all, making debugging impossible.📍 Location
src/engine/graphics/vulkan/screenshot.zig:157captureScreenshot🔴 Severity: Medium
💥 Impact
If
captureScreenshotis ever called concurrently with the normal render loop (e.g., from a background thread, or if the screenshot path is triggered during a frame), it would race with the guardedsubmitGuardedcalls inFrameManager.endFrame(), breaking the mutex-based serialization guarantee and causing undefined Vulkan behavior.Even if called only from the main thread, the function silently swallows all errors via a catch-all, meaning users cannot diagnose why screenshot capture failed.
🔎 Evidence
screenshot.zig:157:
Compare to the proper pattern in
frame_manager.zig:195:The
submitGuardedfunction (inmodules/engine-graphics/src/vulkan_device.zig:463-477) properly:device.mutexbefore submissionVK_ERROR_DEVICE_LOSTand logs detailed fault infoscreenshot.zigdoes none of this - it directly submits without mutex protection and catches all errors to a boolean false.🛠️ Proposed Fix
Modify
captureScreenshotto use the device mutex around the queue submission:Also consider surfacing the error rather than returning
boolso callers can handle specific failure modes.✅ Acceptance Criteria
captureScreenshotacquiresdevice.mutexbefore callingvkQueueSubmitVK_ERROR_DEVICE_LOSTis handled specially to provide diagnostic infonix develop --command zig build test📚 References
modules/engine-graphics/src/vulkan_device.zig:463-477—submitGuardedimplementation showing correct patternsrc/engine/graphics/vulkan/frame_manager.zig:195— Correct usage ofsubmitGuarded