@@ -34,7 +34,6 @@ class VulkanExample : public VulkanExampleBase
3434 std::vector<DescriptorSets> descriptorSets;
3535 VkPipelineLayout pipelineLayout{ VK_NULL_HANDLE }; // Layout of the graphics pipeline
3636 VkPipeline pipeline{ VK_NULL_HANDLE }; // Image display pipeline
37- std::vector<VkSemaphore> semaphores; // Execution dependency between compute & graphic submission
3837 // Used to pass data to the graphics shaders
3938 struct UniformData {
4039 glm::mat4 projection;
@@ -47,9 +46,8 @@ class VulkanExample : public VulkanExampleBase
4746 struct Compute {
4847 VkQueue queue{ VK_NULL_HANDLE }; // Separate queue for compute commands (queue family may differ from the one used for graphics)
4948 VkCommandPool commandPool{ VK_NULL_HANDLE }; // Use a separate command pool (queue family may differ from the one used for graphics)
50- VkCommandBuffer commandBuffer{ VK_NULL_HANDLE }; // Command buffer storing the dispatch commands and barriers
51- std::vector<VkSemaphore> semaphores; // Execution dependency between compute & graphic submission
52- VkFence waitFence{ VK_NULL_HANDLE }; // Used to wait for the compute command buffer to finish
49+ std::vector<VkCommandBuffer> commandBuffers; // Command buffers storing the dispatch commands and barriers
50+ std::vector<VkFence> fences; // Synchronization fence to avoid rewriting compute CB if still in use
5351 VkDescriptorSetLayout descriptorSetLayout{ VK_NULL_HANDLE }; // Compute shader binding layout
5452 VkDescriptorSet descriptorSet{ VK_NULL_HANDLE }; // Compute shader bindings
5553 VkPipelineLayout pipelineLayout{ VK_NULL_HANDLE }; // Layout of the compute pipeline
@@ -83,20 +81,16 @@ class VulkanExample : public VulkanExampleBase
8381 vkDestroyPipeline (device, graphics.pipeline , nullptr );
8482 vkDestroyPipelineLayout (device, graphics.pipelineLayout , nullptr );
8583 vkDestroyDescriptorSetLayout (device, graphics.descriptorSetLayout , nullptr );
86- for (auto & semaphore : graphics.semaphores ) {
87- vkDestroySemaphore (device, semaphore, nullptr );
88- }
8984 for (auto & buffer : graphics.uniformBuffers ) {
9085 buffer.destroy ();
9186 }
9287 // Compute
9388 for (auto & pipeline : compute.pipelines ) {
9489 vkDestroyPipeline (device, pipeline, nullptr );
9590 }
96- for (auto & semaphore : compute.semaphores ) {
97- vkDestroySemaphore (device, semaphore , nullptr );
91+ for (auto & fence : compute.fences ) {
92+ vkDestroyFence (device, fence , nullptr );
9893 }
99- vkDestroyFence (device, compute.waitFence , nullptr );
10094 vkDestroyPipelineLayout (device, compute.pipelineLayout , nullptr );
10195 vkDestroyDescriptorSetLayout (device, compute.descriptorSetLayout , nullptr );
10296 vkDestroyCommandPool (device, compute.commandPool , nullptr );
@@ -255,20 +249,6 @@ class VulkanExample : public VulkanExampleBase
255249 // Prepare the graphics resources used to display the ray traced output of the compute shader
256250 void prepareGraphics ()
257251 {
258- // Create a semaphore for compute & graphics sync
259- graphics.semaphores .resize (swapChain.images .size ());
260- for (auto & semaphore : graphics.semaphores ) {
261- VkSemaphoreCreateInfo semaphoreCreateInfo = vks::initializers::semaphoreCreateInfo ();
262- VK_CHECK_RESULT (vkCreateSemaphore (device, &semaphoreCreateInfo, nullptr , &semaphore));
263- // Signal the semaphore
264- // @todo: maybe remove
265- VkSubmitInfo submitInfo = vks::initializers::submitInfo ();
266- submitInfo.signalSemaphoreCount = 1 ;
267- submitInfo.pSignalSemaphores = &semaphore;
268- VK_CHECK_RESULT (vkQueueSubmit (queue, 1 , &submitInfo, VK_NULL_HANDLE));
269- }
270- VK_CHECK_RESULT (vkQueueWaitIdle (queue));
271-
272252 // Setup descriptors
273253
274254 // The graphics pipeline uses two sets with two bindings
@@ -355,19 +335,32 @@ class VulkanExample : public VulkanExampleBase
355335
356336 void prepareCompute ()
357337 {
358- // Sync objects required to sync graphics and compute submissions
359- // These are used in addition to the sync objects defined in the sample base class
360- compute.semaphores .resize (swapChain.images .size ());
361- for (auto & semaphore : compute.semaphores ) {
362- VkSemaphoreCreateInfo semaphoreCreateInfo = vks::initializers::semaphoreCreateInfo ();
363- VK_CHECK_RESULT (vkCreateSemaphore (device, &semaphoreCreateInfo, nullptr , &semaphore));
364- }
365- VkFenceCreateInfo fenceCreateInfo = vks::initializers::fenceCreateInfo (VK_FENCE_CREATE_SIGNALED_BIT);
366- VK_CHECK_RESULT (vkCreateFence (device, &fenceCreateInfo, nullptr , &compute.waitFence ));
367-
368338 // Get a compute queue from the device
369339 vkGetDeviceQueue (device, vulkanDevice->queueFamilyIndices .compute , 0 , &compute.queue );
370340
341+ // Separate command pool as queue family for compute may be different from the graphics one
342+ VkCommandPoolCreateInfo cmdPoolInfo = {};
343+ cmdPoolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
344+ cmdPoolInfo.queueFamilyIndex = vulkanDevice->queueFamilyIndices .compute ;
345+ cmdPoolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
346+ VK_CHECK_RESULT (vkCreateCommandPool (device, &cmdPoolInfo, nullptr , &compute.commandPool ));
347+
348+ // Some objects need to be duplicated per frames in flight
349+
350+ // Create command buffers for compute operations
351+ compute.commandBuffers .resize (maxConcurrentFrames);
352+ VkCommandBufferAllocateInfo cmdBufAllocateInfo = vks::initializers::commandBufferAllocateInfo (compute.commandPool , VK_COMMAND_BUFFER_LEVEL_PRIMARY, 1 );
353+ for (auto & commandBuffer : compute.commandBuffers ) {
354+ VK_CHECK_RESULT (vkAllocateCommandBuffers (device, &cmdBufAllocateInfo, &commandBuffer));
355+ }
356+
357+ // Fences for compute CB sync
358+ compute.fences .resize (maxConcurrentFrames);
359+ for (auto & fence : compute.fences ) {
360+ VkFenceCreateInfo fenceCreateInfo = vks::initializers::fenceCreateInfo (VK_FENCE_CREATE_SIGNALED_BIT);
361+ VK_CHECK_RESULT (vkCreateFence (device, &fenceCreateInfo, nullptr , &fence));
362+ }
363+
371364 // Create compute pipeline
372365 // Compute pipelines are created separate from graphics pipelines even if they use the same queue
373366
@@ -404,17 +397,6 @@ class VulkanExample : public VulkanExampleBase
404397 VK_CHECK_RESULT (vkCreateComputePipelines (device, pipelineCache, 1 , &computePipelineCreateInfo, nullptr , &pipeline));
405398 compute.pipelines .push_back (pipeline);
406399 }
407-
408- // Separate command pool as queue family for compute may be different than graphics
409- VkCommandPoolCreateInfo cmdPoolInfo = {};
410- cmdPoolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
411- cmdPoolInfo.queueFamilyIndex = vulkanDevice->queueFamilyIndices .compute ;
412- cmdPoolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
413- VK_CHECK_RESULT (vkCreateCommandPool (device, &cmdPoolInfo, nullptr , &compute.commandPool ));
414-
415- // Create a command buffer for compute operations
416- VkCommandBufferAllocateInfo cmdBufAllocateInfo = vks::initializers::commandBufferAllocateInfo ( compute.commandPool , VK_COMMAND_BUFFER_LEVEL_PRIMARY, 1 );
417- VK_CHECK_RESULT (vkAllocateCommandBuffers (device, &cmdBufAllocateInfo, &compute.commandBuffer ));
418400 }
419401
420402 void prepareUniformBuffers ()
@@ -450,15 +432,17 @@ class VulkanExample : public VulkanExampleBase
450432
451433 void buildComputeCommandBuffer ()
452434 {
435+ VkCommandBuffer cmdBuffer = compute.commandBuffers [currentBuffer];
436+ vkResetCommandBuffer (cmdBuffer, 0 );
453437 VkCommandBufferBeginInfo cmdBufInfo = vks::initializers::commandBufferBeginInfo ();
454- VK_CHECK_RESULT (vkBeginCommandBuffer (compute. commandBuffer , &cmdBufInfo));
455- vkCmdBindPipeline (compute. commandBuffer , VK_PIPELINE_BIND_POINT_COMPUTE, compute.pipelines [compute.pipelineIndex ]);
456- vkCmdBindDescriptorSets (compute. commandBuffer , VK_PIPELINE_BIND_POINT_COMPUTE, compute.pipelineLayout , 0 , 1 , &compute.descriptorSet , 0 , 0 );
457- vkCmdDispatch (compute. commandBuffer , storageImage.width / 16 , storageImage.height / 16 , 1 );
458- vkEndCommandBuffer (compute. commandBuffer );
438+ VK_CHECK_RESULT (vkBeginCommandBuffer (cmdBuffer , &cmdBufInfo));
439+ vkCmdBindPipeline (cmdBuffer , VK_PIPELINE_BIND_POINT_COMPUTE, compute.pipelines [compute.pipelineIndex ]);
440+ vkCmdBindDescriptorSets (cmdBuffer , VK_PIPELINE_BIND_POINT_COMPUTE, compute.pipelineLayout , 0 , 1 , &compute.descriptorSet , 0 , 0 );
441+ vkCmdDispatch (cmdBuffer , storageImage.width / 16 , storageImage.height / 16 , 1 );
442+ vkEndCommandBuffer (cmdBuffer );
459443 }
460444
461- void buildCommandBuffer ()
445+ void buildGraphicsCommandBuffer ()
462446 {
463447 VkCommandBuffer cmdBuffer = drawCmdBuffers[currentBuffer];
464448 vkResetCommandBuffer (cmdBuffer, 0 );
@@ -539,45 +523,19 @@ class VulkanExample : public VulkanExampleBase
539523 if (!prepared)
540524 return ;
541525
542- VulkanExampleBase::prepareFrame ();
543-
544- updateUniformBuffers ();
545-
546- // Alsow wait for comnpute to finish (graphics wait is done in prepareFrame)
547- VK_CHECK_RESULT (vkWaitForFences (device, 1 , &compute.waitFence , VK_TRUE, UINT64_MAX));
548- VK_CHECK_RESULT (vkResetFences (device, 1 , &compute.waitFence ));
549-
526+ // Use a fence to ensure that compute command buffer has finished executing before using it again
527+ vkWaitForFences (device, 1 , &compute.fences [currentBuffer], VK_TRUE, UINT64_MAX);
528+ vkResetFences (device, 1 , &compute.fences [currentBuffer]);
550529 buildComputeCommandBuffer ();
551530
552- const VkPipelineStageFlags waitStageMask = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
553-
554- // Submit compute commands
555531 VkSubmitInfo computeSubmitInfo = vks::initializers::submitInfo ();
556532 computeSubmitInfo.commandBufferCount = 1 ;
557- computeSubmitInfo.pCommandBuffers = &compute.commandBuffer ;
558- computeSubmitInfo.waitSemaphoreCount = 1 ;
559- computeSubmitInfo.pWaitSemaphores = &graphics.semaphores [currentBuffer];
560- computeSubmitInfo.pWaitDstStageMask = &waitStageMask;
561- computeSubmitInfo.signalSemaphoreCount = 1 ;
562- computeSubmitInfo.pSignalSemaphores = &compute.semaphores [currentBuffer];
563- VK_CHECK_RESULT (vkQueueSubmit (compute.queue , 1 , &computeSubmitInfo, compute.waitFence ));
564-
565- buildCommandBuffer ();
566-
567- VkPipelineStageFlags graphicsWaitStageMasks[] = { VK_PIPELINE_STAGE_VERTEX_INPUT_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT };
568- VkSemaphore graphicsWaitSemaphores[] = { compute.semaphores [currentBuffer], presentCompleteSemaphores[currentBuffer] };
569- VkSemaphore graphicsSignalSemaphores[] = { graphics.semaphores [currentBuffer], renderCompleteSemaphores[currentImageIndex]};
570-
571- // Submit graphics commands
572- submitInfo.commandBufferCount = 1 ;
573- submitInfo.pCommandBuffers = &drawCmdBuffers[currentBuffer];
574- submitInfo.waitSemaphoreCount = 2 ;
575- submitInfo.pWaitSemaphores = graphicsWaitSemaphores;
576- submitInfo.pWaitDstStageMask = graphicsWaitStageMasks;
577- submitInfo.signalSemaphoreCount = 2 ;
578- submitInfo.pSignalSemaphores = graphicsSignalSemaphores;
579- VK_CHECK_RESULT (vkQueueSubmit (queue, 1 , &submitInfo, waitFences[currentBuffer]));
533+ computeSubmitInfo.pCommandBuffers = &compute.commandBuffers [currentBuffer];
534+ VK_CHECK_RESULT (vkQueueSubmit (compute.queue , 1 , &computeSubmitInfo, compute.fences [currentBuffer]));
580535
536+ VulkanExampleBase::prepareFrame ();
537+ updateUniformBuffers ();
538+ buildGraphicsCommandBuffer ();
581539 VulkanExampleBase::submitFrame ();
582540 }
583541
0 commit comments