@@ -207,6 +207,60 @@ pub const StagingRing = struct {
207207 };
208208 }
209209
210+ /// Non-blocking check if a frame's fence is signaled
211+ /// Returns true if fence is signaled (GPU work complete) or no fence exists
212+ pub fn isFenceReady (self : * const Self , frame_index : usize ) bool {
213+ const vkGetFenceStatus = vk .vkGetFenceStatus orelse return true ;
214+
215+ if (frame_index >= MAX_FRAMES_IN_FLIGHT ) return true ;
216+
217+ if (self .frame_allocations [frame_index ]) | alloc | {
218+ if (alloc .fence != null ) {
219+ return vkGetFenceStatus (self .device , alloc .fence ) == vk .VK_SUCCESS ;
220+ }
221+ }
222+ return true ; // No fence means ready
223+ }
224+
225+ /// Non-blocking check if any fence is signaled
226+ pub fn checkFence (self : * const Self , fence : vk.VkFence ) bool {
227+ const vkGetFenceStatus = vk .vkGetFenceStatus orelse return true ;
228+ if (fence == null ) return true ;
229+ return vkGetFenceStatus (self .device , fence ) == vk .VK_SUCCESS ;
230+ }
231+
232+ /// Try to begin a new frame without blocking
233+ /// Returns false if previous frame's fence is not ready (caller should skip)
234+ /// Returns true and advances frame if ready
235+ pub fn tryBeginFrame (self : * Self , frame_fence : vk.VkFence ) ! bool {
236+ const vkGetFenceStatus = vk .vkGetFenceStatus orelse return error .VulkanFunctionNotLoaded ;
237+
238+ const next_frame = (self .current_frame + 1 ) % MAX_FRAMES_IN_FLIGHT ;
239+
240+ // Check if next frame slot's fence is ready (non-blocking)
241+ if (self .frame_allocations [next_frame ]) | old_alloc | {
242+ if (old_alloc .fence != null ) {
243+ if (vkGetFenceStatus (self .device , old_alloc .fence ) != vk .VK_SUCCESS ) {
244+ // Fence not ready, caller should skip this frame's staging
245+ return false ;
246+ }
247+ }
248+ self .frame_allocations [next_frame ] = null ;
249+ }
250+
251+ // Fence ready, advance frame
252+ self .current_frame = next_frame ;
253+
254+ // Record start of this frame's allocations
255+ self .frame_allocations [self .current_frame ] = FrameAllocation {
256+ .start = self .write_pos ,
257+ .end = self .write_pos ,
258+ .fence = frame_fence ,
259+ };
260+
261+ return true ;
262+ }
263+
210264 /// Stage data for upload to a destination buffer
211265 /// Returns the offset in the staging buffer where data was written
212266 pub fn stage (
@@ -299,6 +353,19 @@ pub const StagingRing = struct {
299353 self .pending_copies .clearRetainingCapacity ();
300354 }
301355
356+ /// Get the current number of pending copies (for tracking before staging)
357+ pub fn getPendingCount (self : * const Self ) usize {
358+ return self .pending_copies .items .len ;
359+ }
360+
361+ /// Cancel pending copies added after a certain point (used when staging fails partway)
362+ /// This prevents copy commands referencing freed buffer regions
363+ pub fn cancelPendingCopiesAfter (self : * Self , count : usize ) void {
364+ if (count < self .pending_copies .items .len ) {
365+ self .pending_copies .shrinkRetainingCapacity (count );
366+ }
367+ }
368+
302369 fn findMemoryType (
303370 physical_device : vk.VkPhysicalDevice ,
304371 type_filter : u32 ,
0 commit comments