Skip to content

Commit 5f159c7

Browse files
committed
Finish sync rework
1 parent 093e73c commit 5f159c7

1 file changed

Lines changed: 80 additions & 121 deletions

File tree

examples/computecloth/computecloth.cpp

Lines changed: 80 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)