@@ -72,17 +72,17 @@ class VulkanExample : public VulkanExampleBase
7272 // Resources for the compute part of the example
7373 // Number of compute command buffers: set to 1 for serialized processing or 2 for in-parallel with graphics queue
7474 struct Compute {
75- typedef struct Semaphores_t {
75+ typedef struct ComputeSemaphores {
7676 VkSemaphore ready{ VK_NULL_HANDLE };
7777 VkSemaphore complete{ VK_NULL_HANDLE };
78- } semaphores_t ;
79- std::array<semaphores_t , maxConcurrentFrames> semaphores{};
78+ };
79+ std::array<ComputeSemaphores , maxConcurrentFrames> semaphores{};
8080 std::array<VkFence, maxConcurrentFrames> fences{};
8181 VkQueue queue{ VK_NULL_HANDLE };
8282 VkCommandPool commandPool{ VK_NULL_HANDLE };
8383 std::array<VkCommandBuffer, maxConcurrentFrames> commandBuffers{};
8484 VkDescriptorSetLayout descriptorSetLayout{ VK_NULL_HANDLE };
85- std::array<VkDescriptorSet, 2 > descriptorSets{ VK_NULL_HANDLE };
85+ std::array<VkDescriptorSet, maxConcurrentFrames > descriptorSets{ VK_NULL_HANDLE };
8686 VkPipelineLayout pipelineLayout{ VK_NULL_HANDLE };
8787 VkPipeline pipeline{ VK_NULL_HANDLE };
8888 struct UniformData {
@@ -100,13 +100,12 @@ class VulkanExample : public VulkanExampleBase
100100 glm::vec4 gravity{ 0 .0f , 9 .8f , 0 .0f , 0 .0f };
101101 glm::ivec2 particleCount{ 0 };
102102 } uniformData;
103- // @todo: no need to duplicate? Only set up once
103+ // No need to duplicate Only set up once at application start
104104 vks::Buffer uniformBuffer;
105105 } compute;
106106
107107 VulkanExample () : VulkanExampleBase()
108108 {
109- useNewSync = true ;
110109 title = " Compute shader cloth simulation" ;
111110 camera.type = Camera::CameraType::lookat;
112111 camera.setPerspective (60 .0f , (float )width / (float )height, 0 .1f , 512 .0f );
@@ -133,9 +132,12 @@ class VulkanExample : public VulkanExampleBase
133132 vkDestroyPipelineLayout (device, compute.pipelineLayout , nullptr );
134133 vkDestroyDescriptorSetLayout (device, compute.descriptorSetLayout , nullptr );
135134 vkDestroyPipeline (device, compute.pipeline , nullptr );
136- for (uint32_t i = 0 ; i < compute.semaphores .size (); i++) {
137- vkDestroySemaphore (device, compute.semaphores [i].ready , nullptr );
138- vkDestroySemaphore (device, compute.semaphores [i].complete , nullptr );
135+ for (auto & fence : compute.fences ) {
136+ vkDestroyFence (device, fence, nullptr );
137+ }
138+ for (auto & semaphore : compute.semaphores ) {
139+ vkDestroySemaphore (device, semaphore.ready , nullptr );
140+ vkDestroySemaphore (device, semaphore.complete , nullptr );
139141 }
140142 vkDestroyCommandPool (device, compute.commandPool , nullptr );
141143
@@ -340,23 +342,26 @@ class VulkanExample : public VulkanExampleBase
340342 stagingBuffer.destroy ();
341343 }
342344
343- // Prepare the resources used for the graphics part of the sample
344- void prepareGraphics ()
345+ void prepareDescriptorPool ()
345346 {
346- // Uniform buffers for passing data to the vertex shader
347- for (auto & buffer : graphics.uniformBuffers ) {
348- vulkanDevice->createBuffer (VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &buffer, sizeof (Graphics::UniformData));
349- VK_CHECK_RESULT (buffer.map ());
350- }
351-
352- // Descriptor pool
347+ // This is shared between graphics and compute
353348 std::vector<VkDescriptorPoolSize> poolSizes = {
354349 vks::initializers::descriptorPoolSize (VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, maxConcurrentFrames * 3 ),
355350 vks::initializers::descriptorPoolSize (VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, maxConcurrentFrames * 4 ),
356351 vks::initializers::descriptorPoolSize (VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, maxConcurrentFrames * 2 )
357352 };
358353 VkDescriptorPoolCreateInfo descriptorPoolInfo = vks::initializers::descriptorPoolCreateInfo (poolSizes, maxConcurrentFrames * 3 );
359354 VK_CHECK_RESULT (vkCreateDescriptorPool (device, &descriptorPoolInfo, nullptr , &descriptorPool));
355+ }
356+
357+ // Prepare the resources used for the graphics part of the sample
358+ void prepareGraphics ()
359+ {
360+ // Uniform buffers for passing data to the vertex shader
361+ for (auto & buffer : graphics.uniformBuffers ) {
362+ vulkanDevice->createBuffer (VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &buffer, sizeof (Graphics::UniformData));
363+ VK_CHECK_RESULT (buffer.map ());
364+ }
360365
361366 // Descriptor layout
362367 std::vector<VkDescriptorSetLayoutBinding> setLayoutBindings = {
@@ -511,22 +516,27 @@ class VulkanExample : public VulkanExampleBase
511516 cmdPoolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
512517 VK_CHECK_RESULT (vkCreateCommandPool (device, &cmdPoolInfo, nullptr , &compute.commandPool ));
513518
514- // Create a command buffer for compute operations
519+ // Create command buffers for compute operations
515520 VkCommandBufferAllocateInfo cmdBufAllocateInfo = vks::initializers::commandBufferAllocateInfo (compute.commandPool , VK_COMMAND_BUFFER_LEVEL_PRIMARY, static_cast <uint32_t >(compute.commandBuffers .size ()));
516521 VK_CHECK_RESULT (vkAllocateCommandBuffers (device, &cmdBufAllocateInfo, &compute.commandBuffers [0 ]));
517522
523+ // Fences to check for command buffer completion
524+ for (auto & fence : compute.fences ) {
525+ VkFenceCreateInfo fenceCreateInfo = vks::initializers::fenceCreateInfo (VK_FENCE_CREATE_SIGNALED_BIT);
526+ VK_CHECK_RESULT (vkCreateFence (device, &fenceCreateInfo, nullptr , &fence));
527+ }
528+
518529 // Semaphores for graphics / compute synchronization
519530 VkSemaphoreCreateInfo semaphoreCreateInfo = vks::initializers::semaphoreCreateInfo ();
520531 for (uint32_t i = 0 ; i < compute.semaphores .size (); i++) {
521532 VK_CHECK_RESULT (vkCreateSemaphore (device, &semaphoreCreateInfo, nullptr , &compute.semaphores [i].ready ));
522533 VK_CHECK_RESULT (vkCreateSemaphore (device, &semaphoreCreateInfo, nullptr , &compute.semaphores [i].complete ));
523534 }
524-
525- // @todo
526- for (auto & fence : compute.fences ) {
527- VkFenceCreateInfo fenceCreateInfo = vks::initializers::fenceCreateInfo (VK_FENCE_CREATE_SIGNALED_BIT);
528- VK_CHECK_RESULT (vkCreateFence (device, &fenceCreateInfo, nullptr , &fence));
529- }
535+ // Signal first used ready semaphore
536+ VkSubmitInfo computeSubmitInfo = vks::initializers::submitInfo ();
537+ computeSubmitInfo.signalSemaphoreCount = 1 ;
538+ computeSubmitInfo.pSignalSemaphores = &compute.semaphores [-1 % maxConcurrentFrames].ready ;
539+ VK_CHECK_RESULT (vkQueueSubmit (compute.queue , 1 , &computeSubmitInfo, VK_NULL_HANDLE));
530540 }
531541
532542 void updateComputeUBO ()
@@ -540,13 +550,11 @@ class VulkanExample : public VulkanExampleBase
540550 std::uniform_real_distribution<float > rd (1 .0f , 12 .0f );
541551 compute.uniformData .gravity .x = cos (glm::radians (-timer * 360 .0f )) * (rd (rndEngine) - rd (rndEngine));
542552 compute.uniformData .gravity .z = sin (glm::radians (timer * 360 .0f )) * (rd (rndEngine) - rd (rndEngine));
543- }
544- else {
553+ } else {
545554 compute.uniformData .gravity .x = 0 .0f ;
546555 compute.uniformData .gravity .z = 0 .0f ;
547556 }
548- }
549- else {
557+ } else {
550558 compute.uniformData .deltaT = 0 .0f ;
551559 }
552560 memcpy (compute.uniformBuffer .mapped , &compute.uniformData , sizeof (Compute::UniformData));
@@ -558,98 +566,14 @@ class VulkanExample : public VulkanExampleBase
558566 graphics.uniformData .view = camera.matrices .view ;
559567 memcpy (graphics.uniformBuffers [currentBuffer].mapped , &graphics.uniformData , sizeof (Graphics::UniformData));
560568 }
561-
562- void draw ()
563- {
564- // As we use both graphics and compute, frame submission is a bit more involved
565- // We'll be using semaphores to synchronize between the compute shader updating the cloth and the graphics pipeline drawing it
566-
567- static bool firstDraw = true ;
568- // static uint32_t computeSubmitIndex{ 0 }, graphicsSubmitIndex{ 0 };
569- // if (maxConcurrentFrames > 1)
570- // {
571- // // SRS - if we are double buffering the compute queue, swap the compute command buffer indices
572- // graphicsSubmitIndex = computeSubmitIndex;
573- // computeSubmitIndex = 1 - graphicsSubmitIndex;
574- // }
575-
576- VK_CHECK_RESULT (vkWaitForFences (device, 1 , &compute.fences [currentBuffer], VK_TRUE, UINT64_MAX));
577- VK_CHECK_RESULT (vkResetFences (device, 1 , &compute.fences [currentBuffer]));
578-
579- buildComputeCommandBuffer ();
580-
581- updateComputeUBO ();
582-
583- VkSubmitInfo computeSubmitInfo = vks::initializers::submitInfo ();
584- VkPipelineStageFlags computeWaitDstStageMask = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
585- if (!firstDraw) {
586- computeSubmitInfo.waitSemaphoreCount = 1 ;
587- computeSubmitInfo.pWaitSemaphores = &compute.semaphores [((int )currentBuffer - 1 ) % maxConcurrentFrames].ready ;
588- computeSubmitInfo.pWaitDstStageMask = &computeWaitDstStageMask;
589- }
590- else {
591- firstDraw = false ;
592- computeSubmitInfo.signalSemaphoreCount = 1 ;
593- computeSubmitInfo.pSignalSemaphores = &compute.semaphores [currentBuffer].complete ;
594- computeSubmitInfo.commandBufferCount = 1 ;
595- computeSubmitInfo.pCommandBuffers = &compute.commandBuffers [currentBuffer];
596-
597- VK_CHECK_RESULT (vkQueueSubmit (compute.queue , 1 , &computeSubmitInfo, compute.fences [currentBuffer]));
598-
599- VK_CHECK_RESULT (vkWaitForFences (device, 1 , &compute.fences [currentBuffer], VK_TRUE, UINT64_MAX));
600- VK_CHECK_RESULT (vkResetFences (device, 1 , &compute.fences [currentBuffer]));
601-
602- // Add an extra set of acquire and release barriers to the graphics queue,
603- // so that when the second compute command buffer executes for the first time
604- // it doesn't complain about a lack of a corresponding "acquire" to its "release" and vice versa
605- VkCommandBuffer barrierCmd = vulkanDevice->createCommandBuffer (VK_COMMAND_BUFFER_LEVEL_PRIMARY, true );
606- addComputeToGraphicsBarriers (barrierCmd, 0 , VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_VERTEX_INPUT_BIT);
607- addGraphicsToComputeBarriers (barrierCmd, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, 0 , VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT);
608- vulkanDevice->flushCommandBuffer (barrierCmd, queue, true );
609- }
610- computeSubmitInfo.signalSemaphoreCount = 1 ;
611- computeSubmitInfo.pSignalSemaphores = &compute.semaphores [currentBuffer].complete ;
612- computeSubmitInfo.commandBufferCount = 1 ;
613- computeSubmitInfo.pCommandBuffers = &compute.commandBuffers [currentBuffer];
614-
615- VK_CHECK_RESULT (vkQueueSubmit (compute.queue , 1 , &computeSubmitInfo, compute.fences [currentBuffer]));
616-
617- // Submit graphics commands
618- VulkanExampleBase::prepareFrame ();
619-
620- updateGraphicsUBO ();
621-
622- buildGraphicsCommandBuffer ();
623-
624- VkPipelineStageFlags waitDstStageMask[2 ] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_VERTEX_INPUT_BIT };
625- VkSemaphore waitSemaphores[2 ] = { presentCompleteSemaphores[currentBuffer], compute.semaphores [currentBuffer].complete };
626- VkSemaphore signalSemaphores[2 ] = { renderCompleteSemaphores[currentImageIndex], compute.semaphores [currentBuffer].ready };
627-
628- VkSubmitInfo submitInfo = vks::initializers::submitInfo ();
629- submitInfo.waitSemaphoreCount = 2 ;
630- submitInfo.pWaitDstStageMask = waitDstStageMask;
631- submitInfo.pWaitSemaphores = waitSemaphores;
632- submitInfo.signalSemaphoreCount = 2 ;
633- submitInfo.pSignalSemaphores = signalSemaphores;
634- submitInfo.commandBufferCount = 1 ;
635- submitInfo.pCommandBuffers = &drawCmdBuffers[currentBuffer];
636- VK_CHECK_RESULT (vkQueueSubmit (queue, 1 , &submitInfo, waitFences[currentBuffer]));
637-
638- VulkanExampleBase::submitFrame (VK_NULL_HANDLE, true );
639- }
640-
641569 void prepare ()
642570 {
643571 VulkanExampleBase::prepare ();
644- // Make sure the code works properly both with different queues families for graphics and compute and the same queue family
645- // You can use DEBUG_FORCE_SHARED_GRAPHICS_COMPUTE_QUEUE preprocessor define to force graphics and compute from the same queue family
646- #ifdef DEBUG_FORCE_SHARED_GRAPHICS_COMPUTE_QUEUE
647- vulkanDevice->queueFamilyIndices .compute = vulkanDevice->queueFamilyIndices .graphics ;
648- #endif
649572 // Check whether the compute queue family is distinct from the graphics queue family
650573 dedicatedComputeQueue = vulkanDevice->queueFamilyIndices .graphics != vulkanDevice->queueFamilyIndices .compute ;
651574 loadAssets ();
652575 prepareStorageBuffers ();
576+ prepareDescriptorPool ();
653577 prepareGraphics ();
654578 prepareCompute ();
655579 prepared = true ;
@@ -733,36 +657,71 @@ class VulkanExample : public VulkanExampleBase
733657 vkCmdPushConstants (cmdBuffer, compute.pipelineLayout , VK_SHADER_STAGE_COMPUTE_BIT, 0 , sizeof (uint32_t ), &calculateNormals);
734658
735659 // Dispatch the compute job
736- // SRS - Iterations **must** be an even number, so that readSet starts at 1 and the final result ends up in output.buffer with readSet equal to 0
737660 const uint32_t iterations = 64 ;
738661 for (uint32_t j = 0 ; j < iterations; j++) {
739662 readSet = 1 - readSet;
740663 vkCmdBindDescriptorSets (cmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, compute.pipelineLayout , 0 , 1 , &compute.descriptorSets [readSet], 0 , 0 );
741-
742664 if (j == iterations - 1 ) {
743665 calculateNormals = 1 ;
744666 vkCmdPushConstants (cmdBuffer, compute.pipelineLayout , VK_SHADER_STAGE_COMPUTE_BIT, 0 , sizeof (uint32_t ), &calculateNormals);
745667 }
746-
747668 vkCmdDispatch (cmdBuffer, cloth.gridsize .x / 10 , cloth.gridsize .y / 10 , 1 );
748-
749669 // Don't add a barrier on the last iteration of the loop, since we'll have an explicit release to the graphics queue
750670 if (j != iterations - 1 ) {
751671 addComputeToComputeBarriers (cmdBuffer, readSet);
752672 }
753-
754673 }
755674
756- // release the storage buffers back to the graphics queue
675+ // Release the storage buffers back to the graphics queue
757676 addComputeToGraphicsBarriers (cmdBuffer, VK_ACCESS_SHADER_WRITE_BIT, 0 , VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT);
677+
758678 vkEndCommandBuffer (cmdBuffer);
759679 }
760680
761681 virtual void render ()
762682 {
763683 if (!prepared)
764684 return ;
765- draw ();
685+
686+ VK_CHECK_RESULT (vkWaitForFences (device, 1 , &compute.fences [currentBuffer], VK_TRUE, UINT64_MAX));
687+ VK_CHECK_RESULT (vkResetFences (device, 1 , &compute.fences [currentBuffer]));
688+
689+ updateComputeUBO ();
690+ buildComputeCommandBuffer ();
691+
692+ VkPipelineStageFlags computeWaitDstStageMask = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
693+ VkSubmitInfo computeSubmitInfo = vks::initializers::submitInfo ();
694+ computeSubmitInfo.waitSemaphoreCount = 1 ;
695+ computeSubmitInfo.pWaitSemaphores = &compute.semaphores [((int )currentBuffer - 1 ) % maxConcurrentFrames].ready ;
696+ computeSubmitInfo.pWaitDstStageMask = &computeWaitDstStageMask;
697+ computeSubmitInfo.signalSemaphoreCount = 1 ;
698+ computeSubmitInfo.pSignalSemaphores = &compute.semaphores [currentBuffer].complete ;
699+ computeSubmitInfo.commandBufferCount = 1 ;
700+ computeSubmitInfo.pCommandBuffers = &compute.commandBuffers [currentBuffer];
701+
702+ VK_CHECK_RESULT (vkQueueSubmit (compute.queue , 1 , &computeSubmitInfo, compute.fences [currentBuffer]));
703+
704+ // Submit graphics commands
705+ VulkanExampleBase::prepareFrame ();
706+
707+ updateGraphicsUBO ();
708+ buildGraphicsCommandBuffer ();
709+
710+ VkPipelineStageFlags waitDstStageMask[2 ] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_VERTEX_INPUT_BIT };
711+ VkSemaphore waitSemaphores[2 ] = { presentCompleteSemaphores[currentBuffer], compute.semaphores [currentBuffer].complete };
712+ VkSemaphore signalSemaphores[2 ] = { renderCompleteSemaphores[currentImageIndex], compute.semaphores [currentBuffer].ready };
713+
714+ VkSubmitInfo submitInfo = vks::initializers::submitInfo ();
715+ submitInfo.waitSemaphoreCount = 2 ;
716+ submitInfo.pWaitDstStageMask = waitDstStageMask;
717+ submitInfo.pWaitSemaphores = waitSemaphores;
718+ submitInfo.signalSemaphoreCount = 2 ;
719+ submitInfo.pSignalSemaphores = signalSemaphores;
720+ submitInfo.commandBufferCount = 1 ;
721+ submitInfo.pCommandBuffers = &drawCmdBuffers[currentBuffer];
722+ VK_CHECK_RESULT (vkQueueSubmit (queue, 1 , &submitInfo, waitFences[currentBuffer]));
723+
724+ VulkanExampleBase::submitFrame (VK_NULL_HANDLE, true );
766725 }
767726
768727 virtual void OnUpdateUIOverlay (vks::UIOverlay* overlay)
0 commit comments