@@ -18,6 +18,9 @@ namespace ZEngine::Hardwares
1818
1919 Device = device;
2020
21+ BufferredFrameCount = buffered_frame_size;
22+ FrameContextPoolSize = BufferredFrameCount * FrameContextPoolSizeFactor;
23+
2124 Specifications::AttachmentSpecification attachment_specification = {.BindPoint = Specifications::PipelineBindPoint::GRAPHIC};
2225 attachment_specification.ColorsMap .init (&Arena, 2 );
2326 attachment_specification.ColorsMap [0 ] = {};
@@ -28,15 +31,15 @@ namespace ZEngine::Hardwares
2831 attachment_specification.ColorsMap [0 ].Final = ImageLayout::PRESENT_SRC;
2932 attachment_specification.ColorsMap [0 ].ReferenceLayout = ImageLayout::COLOR_ATTACHMENT_OPTIMAL;
3033 SwapchainAttachment = ZPushStructCtorArgs (&Arena, RenderPasses::Attachment, Device, attachment_specification);
31- BufferredFrameCount = buffered_frame_size;
32- IdleFrameThreshold.store (BufferredFrameCount * 3 * 3 , std::memory_order_release);
33- FrameContexts.init (&Arena, BufferredFrameCount, BufferredFrameCount);
3434
35- for (uint32_t i = 0 ; i < BufferredFrameCount; ++i)
35+ IdleFrameThreshold.store (BufferredFrameCount * 3 * 3 * 3 , std::memory_order_release);
36+ FrameContexts.init (&Arena, FrameContextPoolSize, FrameContextPoolSize);
37+
38+ for (uint32_t i = 0 ; i < FrameContextPoolSize; ++i)
3639 {
3740 auto & frame = FrameContexts[i];
3841
39- frame.Index = i ;
42+ frame.Index = (i % BufferredFrameCount) ;
4043 frame.Acquired = ZPushStructCtorArgs (&Arena, Primitives::Semaphore, Device);
4144 frame.Fence = ZPushStructCtorArgs (&Arena, Primitives::Fence, Device, true );
4245 }
@@ -107,24 +110,33 @@ namespace ZEngine::Hardwares
107110 {
108111 SwapchainFramebuffers.init (&Arena, SwapchainImageCount, SwapchainImageCount);
109112 }
110- }
111113
112- void DeviceSwapchain::Clear ()
113- {
114- for (VkImageView image_view : SwapchainImageViews)
114+ scratch = ZGetScratch (&Arena);
115+
116+ Array<VkImage> swapchain_images = {};
117+ swapchain_images.init (scratch.Arena , SwapchainImageCount, SwapchainImageCount);
118+ ZENGINE_VALIDATE_ASSERT (vkGetSwapchainImagesKHR (Device->LogicalDevice , SwapchainHandle, &SwapchainImageCount, swapchain_images.data ()) == VK_SUCCESS, " Failed to get VkImages from Swapchain" )
119+ for (int i = 0 ; i < SwapchainImageCount; ++i)
115120 {
116- if (image_view)
117- {
118- Device->EnqueueForDeletion (DeviceResourceType::IMAGEVIEW, image_view);
119- }
121+ SwapchainImageViews[i] = Device->CreateImageView (swapchain_images[i], Device->SurfaceFormat .format , VK_IMAGE_VIEW_TYPE_2D, VK_IMAGE_ASPECT_COLOR_BIT);
122+
123+ Array<VkImageView> fb_images_views;
124+ fb_images_views.init (scratch.Arena , 1 );
125+ fb_images_views.push (SwapchainImageViews[i]);
126+ SwapchainFramebuffers[i] = Device->CreateFramebuffer (ArrayView{fb_images_views}, SwapchainAttachment->GetHandle (), SwapchainImageWidth, SwapchainImageHeight);
120127 }
121128
122- for (VkFramebuffer framebuffer : SwapchainFramebuffers)
129+ ZReleaseScratch (scratch);
130+ }
131+
132+ void DeviceSwapchain::Clear ()
133+ {
134+ for (uint32_t i = 0 ; i < SwapchainImageCount; ++i)
123135 {
124- if (framebuffer)
125- {
126- Device-> EnqueueForDeletion (DeviceResourceType::FRAMEBUFFER, framebuffer) ;
127- }
136+ Device-> EnqueueForDeletion (DeviceResourceType::IMAGEVIEW, SwapchainImageViews[i]);
137+ Device-> EnqueueForDeletion (DeviceResourceType::FRAMEBUFFER, SwapchainFramebuffers[i]);
138+ SwapchainImageViews[i] = VK_NULL_HANDLE ;
139+ SwapchainFramebuffers[i] = VK_NULL_HANDLE;
128140 }
129141
130142 for (uint32_t i = 0 ; i < SwapchainImageCount; ++i)
@@ -147,6 +159,17 @@ namespace ZEngine::Hardwares
147159 {
148160 if (HasRecreationPending)
149161 {
162+ /*
163+ * On macOS:
164+ * Because of ASYNCHRONOUS communication between MoltenVK and Metal:
165+ * 1. vkDeviceWaitIdle() only guarantees Vulkan sees GPU idle
166+ * 2. Metal's CAMetalLayer may still have pending present operations
167+ * 3. Semaphores can appear "stuck" in Submitted state due to Metal async completion
168+ *
169+ * Pool of BufferredFrameCount * 4 = 12 contexts rotates every resize.
170+ * Skipping 3 ensures we land on fully Idle semaphores/fences even under Metal async.
171+ *
172+ */
150173#ifdef __APPLE__
151174 vkDeviceWaitIdle (Device->LogicalDevice );
152175#endif
@@ -157,26 +180,23 @@ namespace ZEngine::Hardwares
157180 ImageInFlights[i]->Wait (UINT64_MAX);
158181 }
159182 }
160- Clear ();
161- Create ();
162- AsPresentSource ();
163183
164- for (auto & frame : FrameContexts )
184+ for (int i = 0 ; i < FrameContextPoolSizeFactor; ++i )
165185 {
166- *( frame. Fence ) = Rendering::Primitives::Fence (Device, true ) ;
186+ FrameContext& frame = FrameContexts[i + FrameContextOffset] ;
167187 frame.Acquired ->SetState (Primitives::SemaphoreState::Idle);
168188 }
169189
190+ FrameContextOffset = (FrameContextOffset + FrameContextPoolSizeFactor) % FrameContextPoolSize;
191+ Clear ();
192+ Create ();
193+
170194 HasRecreationPending = false ;
171195 ZENGINE_CORE_WARN (" Swapchain has been re-created" )
172196 }
173197
174- FrameContext& frame = FrameContexts[frame_context_idx];
198+ FrameContext& frame = FrameContexts[frame_context_idx + FrameContextOffset ];
175199
176- if (!(frame.Fence )->IsSignaled ())
177- {
178- frame.Fence ->Wait (UINT64_MAX);
179- }
180200 frame.Fence ->Reset ();
181201
182202 ZENGINE_VALIDATE_ASSERT (frame.Acquired ->GetState () != Primitives::SemaphoreState::Submitted, " " )
@@ -207,43 +227,25 @@ namespace ZEngine::Hardwares
207227
208228 void DeviceSwapchain::AsPresentSource ()
209229 {
210- auto command_buffer_info = Device->CommandBufferMgr ->GetInstantCommandBuffer (Rendering::QueueType::GRAPHIC_QUEUE, (CurrentFrame == nullptr ) ? 0u : CurrentFrame->Index , 0 , 2 , true );
211-
212- auto scratch = ZGetScratch (&Arena);
213-
214- Array<VkImage> SwapchainImages = {};
215- SwapchainImages.init (scratch.Arena , SwapchainImageCount, SwapchainImageCount);
216- ZENGINE_VALIDATE_ASSERT (vkGetSwapchainImagesKHR (Device->LogicalDevice , SwapchainHandle, &SwapchainImageCount, SwapchainImages.data ()) == VK_SUCCESS, " Failed to get VkImages from Swapchain" )
217-
218- for (int i = 0 ; i < SwapchainImages.size (); ++i)
219- {
220- Rendering::Specifications::ImageMemoryBarrierSpecification barrier_spec = {};
221- barrier_spec.ImageHandle = SwapchainImages[i];
222- barrier_spec.OldLayout = Specifications::ImageLayout::UNDEFINED;
223- barrier_spec.NewLayout = Specifications::ImageLayout::PRESENT_SRC;
224- barrier_spec.ImageAspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
225- barrier_spec.SourceAccessMask = 0 ;
226- barrier_spec.DestinationAccessMask = VK_ACCESS_MEMORY_READ_BIT;
227- barrier_spec.SourceStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
228- barrier_spec.DestinationStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
229- barrier_spec.LayerCount = 1 ;
230-
231- Rendering::Primitives::ImageMemoryBarrier barrier{barrier_spec};
232- command_buffer_info.Buffer ->TransitionImageLayout (barrier);
233- }
234- Device->CommandBufferMgr ->EndInstantCommandBuffer (command_buffer_info);
235-
236- for (int i = 0 ; i < SwapchainImageCount; ++i)
237- {
238- SwapchainImageViews[i] = Device->CreateImageView (SwapchainImages[i], Device->SurfaceFormat .format , VK_IMAGE_VIEW_TYPE_2D, VK_IMAGE_ASPECT_COLOR_BIT);
239-
240- Array<VkImageView> fb_images_views;
241- fb_images_views.init (scratch.Arena , 1 );
242- fb_images_views.push (SwapchainImageViews[i]);
243- SwapchainFramebuffers[i] = Device->CreateFramebuffer (ArrayView{fb_images_views}, SwapchainAttachment->GetHandle (), SwapchainImageWidth, SwapchainImageHeight);
244- }
245-
246- ZReleaseScratch (scratch);
230+ // auto command_buffer_info = Device->CommandBufferMgr->GetInstantCommandBuffer(Rendering::QueueType::GRAPHIC_QUEUE, (CurrentFrame == nullptr) ? 0u : CurrentFrame->Index, 0, 2, true);
231+
232+ // for (int i = 0; i < SwapchainImages.size(); ++i)
233+ // {
234+ // Rendering::Specifications::ImageMemoryBarrierSpecification barrier_spec = {};
235+ // barrier_spec.ImageHandle = SwapchainImages[i];
236+ // barrier_spec.OldLayout = Specifications::ImageLayout::UNDEFINED;
237+ // barrier_spec.NewLayout = Specifications::ImageLayout::PRESENT_SRC;
238+ // barrier_spec.ImageAspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
239+ // barrier_spec.SourceAccessMask = 0;
240+ // barrier_spec.DestinationAccessMask = VK_ACCESS_MEMORY_READ_BIT;
241+ // barrier_spec.SourceStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
242+ // barrier_spec.DestinationStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
243+ // barrier_spec.LayerCount = 1;
244+
245+ // Rendering::Primitives::ImageMemoryBarrier barrier{barrier_spec};
246+ // command_buffer_info.Buffer->TransitionImageLayout(barrier);
247+ // }
248+ // Device->CommandBufferMgr->EndInstantCommandBuffer(command_buffer_info);
247249 }
248250
249251 void DeviceSwapchain::Present ()
@@ -336,6 +338,8 @@ namespace ZEngine::Hardwares
336338
337339 CurrentFrame->Acquired ->SetState (Rendering::Primitives::SemaphoreState::Idle);
338340
341+ IdleFrameCount.fetch_add (1 );
342+
339343 if (present_result == VK_ERROR_OUT_OF_DATE_KHR || present_result == VK_SUBOPTIMAL_KHR)
340344 {
341345 HasRecreationPending = true ;
@@ -346,7 +350,5 @@ namespace ZEngine::Hardwares
346350 }
347351
348352 ZENGINE_VALIDATE_ASSERT (present_result == VK_SUCCESS || present_result == VK_SUBOPTIMAL_KHR, " Failed to present current frame on Window" )
349-
350- IdleFrameCount.fetch_add (1 );
351353 }
352354} // namespace ZEngine::Hardwares
0 commit comments