Skip to content

Commit 5bd19de

Browse files
committed
Merge branch 'master' of https://github.com/SaschaWillems/Vulkan
2 parents e847c44 + affe260 commit 5bd19de

File tree

1 file changed

+67
-23
lines changed

1 file changed

+67
-23
lines changed

examples/computecloth/computecloth.cpp

100644100755
Lines changed: 67 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -70,14 +70,17 @@ class VulkanExample : public VulkanExampleBase
7070
} graphics;
7171

7272
// Resources for the compute part of the example
73+
// SRS - Number of compute command buffers: set to 1 for serialized processing or 2 for in-parallel with graphics queue
74+
#define COMPUTE_CMD_BUFFERS 2
7375
struct Compute {
74-
struct Semaphores {
76+
typedef struct Semaphores_t {
7577
VkSemaphore ready{ VK_NULL_HANDLE };
7678
VkSemaphore complete{ VK_NULL_HANDLE };
77-
} semaphores;
79+
} semaphores_t;
80+
std::array<semaphores_t, COMPUTE_CMD_BUFFERS> semaphores{};
7881
VkQueue queue{ VK_NULL_HANDLE };
7982
VkCommandPool commandPool{ VK_NULL_HANDLE };
80-
std::array<VkCommandBuffer, 2> commandBuffers{};
83+
std::array<VkCommandBuffer, COMPUTE_CMD_BUFFERS> commandBuffers{};
8184
VkDescriptorSetLayout descriptorSetLayout{ VK_NULL_HANDLE };
8285
std::array<VkDescriptorSet, 2> descriptorSets{ VK_NULL_HANDLE };
8386
VkPipelineLayout pipelineLayout{ VK_NULL_HANDLE };
@@ -126,8 +129,10 @@ class VulkanExample : public VulkanExampleBase
126129
vkDestroyPipelineLayout(device, compute.pipelineLayout, nullptr);
127130
vkDestroyDescriptorSetLayout(device, compute.descriptorSetLayout, nullptr);
128131
vkDestroyPipeline(device, compute.pipeline, nullptr);
129-
vkDestroySemaphore(device, compute.semaphores.ready, nullptr);
130-
vkDestroySemaphore(device, compute.semaphores.complete, nullptr);
132+
for (uint32_t i = 0; i < compute.semaphores.size(); i++) {
133+
vkDestroySemaphore(device, compute.semaphores[i].ready, nullptr);
134+
vkDestroySemaphore(device, compute.semaphores[i].complete, nullptr);
135+
}
131136
vkDestroyCommandPool(device, compute.commandPool, nullptr);
132137

133138
// SSBOs
@@ -176,7 +181,7 @@ class VulkanExample : public VulkanExampleBase
176181
}
177182
}
178183

179-
void addComputeToComputeBarriers(VkCommandBuffer commandBuffer)
184+
void addComputeToComputeBarriers(VkCommandBuffer commandBuffer, uint32_t readSet)
180185
{
181186
VkBufferMemoryBarrier bufferBarrier = vks::initializers::bufferMemoryBarrier();
182187
bufferBarrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT;
@@ -185,10 +190,20 @@ class VulkanExample : public VulkanExampleBase
185190
bufferBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
186191
bufferBarrier.size = VK_WHOLE_SIZE;
187192
std::vector<VkBufferMemoryBarrier> bufferBarriers;
188-
bufferBarrier.buffer = storageBuffers.input.buffer;
189-
bufferBarriers.push_back(bufferBarrier);
190-
bufferBarrier.buffer = storageBuffers.output.buffer;
191-
bufferBarriers.push_back(bufferBarrier);
193+
if (readSet == 0)
194+
{
195+
// SRS - we have written to output.buffer and need a memory barrier before reading it
196+
// - don't need a memory barrier for input.buffer, the execution barrier is enough
197+
bufferBarrier.buffer = storageBuffers.output.buffer;
198+
bufferBarriers.push_back(bufferBarrier);
199+
}
200+
else //if (readSet == 1)
201+
{
202+
// SRS - we have written to input.buffer and need a memory barrier before reading it
203+
// - don't need a memory barrier for output.buffer, the execution barrier is enough
204+
bufferBarrier.buffer = storageBuffers.input.buffer;
205+
bufferBarriers.push_back(bufferBarrier);
206+
}
192207
vkCmdPipelineBarrier(
193208
commandBuffer,
194209
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
@@ -280,7 +295,7 @@ class VulkanExample : public VulkanExampleBase
280295
vkCmdEndRenderPass(drawCmdBuffers[i]);
281296

282297
// release the storage buffers to the compute queue
283-
addGraphicsToComputeBarriers(drawCmdBuffers[i], VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT, 0, VK_PIPELINE_STAGE_VERTEX_INPUT_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT);
298+
addGraphicsToComputeBarriers(drawCmdBuffers[i], VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, 0, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT);
284299

285300
VK_CHECK_RESULT(vkEndCommandBuffer(drawCmdBuffers[i]));
286301
}
@@ -292,19 +307,20 @@ class VulkanExample : public VulkanExampleBase
292307
VkCommandBufferBeginInfo cmdBufInfo = vks::initializers::commandBufferBeginInfo();
293308
cmdBufInfo.flags = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT;
294309

295-
for (uint32_t i = 0; i < 2; i++) {
310+
for (uint32_t i = 0; i < compute.commandBuffers.size(); i++) {
296311

297312
VK_CHECK_RESULT(vkBeginCommandBuffer(compute.commandBuffers[i], &cmdBufInfo));
298313

299314
// Acquire the storage buffers from the graphics queue
300-
addGraphicsToComputeBarriers(compute.commandBuffers[i], 0, VK_ACCESS_SHADER_WRITE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT);
315+
addGraphicsToComputeBarriers(compute.commandBuffers[i], 0, VK_ACCESS_SHADER_READ_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT);
301316

302317
vkCmdBindPipeline(compute.commandBuffers[i], VK_PIPELINE_BIND_POINT_COMPUTE, compute.pipeline);
303318

304319
uint32_t calculateNormals = 0;
305320
vkCmdPushConstants(compute.commandBuffers[i], compute.pipelineLayout, VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof(uint32_t), &calculateNormals);
306321

307322
// Dispatch the compute job
323+
// 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
308324
const uint32_t iterations = 64;
309325
for (uint32_t j = 0; j < iterations; j++) {
310326
readSet = 1 - readSet;
@@ -319,7 +335,7 @@ class VulkanExample : public VulkanExampleBase
319335

320336
// Don't add a barrier on the last iteration of the loop, since we'll have an explicit release to the graphics queue
321337
if (j != iterations - 1) {
322-
addComputeToComputeBarriers(compute.commandBuffers[i]);
338+
addComputeToComputeBarriers(compute.commandBuffers[i], readSet);
323339
}
324340

325341
}
@@ -386,7 +402,7 @@ class VulkanExample : public VulkanExampleBase
386402
// Add an initial release barrier to the graphics queue,
387403
// so that when the compute command buffer executes for the first time
388404
// it doesn't complain about a lack of a corresponding "release" to its "acquire"
389-
addGraphicsToComputeBarriers(copyCmd, VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT, 0, VK_PIPELINE_STAGE_VERTEX_INPUT_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT);
405+
addGraphicsToComputeBarriers(copyCmd, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, 0, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT);
390406
vulkanDevice->flushCommandBuffer(copyCmd, queue, true);
391407

392408
stagingBuffer.destroy();
@@ -595,13 +611,15 @@ class VulkanExample : public VulkanExampleBase
595611
VK_CHECK_RESULT(vkCreateCommandPool(device, &cmdPoolInfo, nullptr, &compute.commandPool));
596612

597613
// Create a command buffer for compute operations
598-
VkCommandBufferAllocateInfo cmdBufAllocateInfo = vks::initializers::commandBufferAllocateInfo(compute.commandPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY, 2);
614+
VkCommandBufferAllocateInfo cmdBufAllocateInfo = vks::initializers::commandBufferAllocateInfo(compute.commandPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY, static_cast<uint32_t>(compute.commandBuffers.size()));
599615
VK_CHECK_RESULT(vkAllocateCommandBuffers(device, &cmdBufAllocateInfo, &compute.commandBuffers[0]));
600616

601617
// Semaphores for graphics / compute synchronization
602618
VkSemaphoreCreateInfo semaphoreCreateInfo = vks::initializers::semaphoreCreateInfo();
603-
VK_CHECK_RESULT(vkCreateSemaphore(device, &semaphoreCreateInfo, nullptr, &compute.semaphores.ready));
604-
VK_CHECK_RESULT(vkCreateSemaphore(device, &semaphoreCreateInfo, nullptr, &compute.semaphores.complete));
619+
for (uint32_t i = 0; i < compute.semaphores.size(); i++) {
620+
VK_CHECK_RESULT(vkCreateSemaphore(device, &semaphoreCreateInfo, nullptr, &compute.semaphores[i].ready));
621+
VK_CHECK_RESULT(vkCreateSemaphore(device, &semaphoreCreateInfo, nullptr, &compute.semaphores[i].complete));
622+
}
605623

606624
// Build a single command buffer containing the compute dispatch commands
607625
buildComputeCommandBuffer();
@@ -643,20 +661,46 @@ class VulkanExample : public VulkanExampleBase
643661
// We'll be using semaphores to synchronize between the compute shader updating the cloth and the graphics pipeline drawing it
644662

645663
static bool firstDraw = true;
664+
static uint32_t computeSubmitIndex{ 0 }, graphicsSubmitIndex{ 0 };
665+
if (COMPUTE_CMD_BUFFERS > 1) // should be constexpr, but requires C++17
666+
{
667+
// SRS - if we are double buffering the compute queue, swap the compute command buffer indices
668+
graphicsSubmitIndex = computeSubmitIndex;
669+
computeSubmitIndex = 1 - graphicsSubmitIndex;
670+
}
671+
646672
VkSubmitInfo computeSubmitInfo = vks::initializers::submitInfo();
647673
VkPipelineStageFlags computeWaitDstStageMask = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
648674
if (!firstDraw) {
649675
computeSubmitInfo.waitSemaphoreCount = 1;
650-
computeSubmitInfo.pWaitSemaphores = &compute.semaphores.ready;
676+
computeSubmitInfo.pWaitSemaphores = &compute.semaphores[computeSubmitIndex].ready;
651677
computeSubmitInfo.pWaitDstStageMask = &computeWaitDstStageMask;
652678
}
653679
else {
654680
firstDraw = false;
681+
if (COMPUTE_CMD_BUFFERS > 1) // should be constexpr, but requires C++17
682+
{
683+
// SRS - if we are double buffering the compute queue, submit extra command buffer at start
684+
computeSubmitInfo.signalSemaphoreCount = 1;
685+
computeSubmitInfo.pSignalSemaphores = &compute.semaphores[graphicsSubmitIndex].complete;
686+
computeSubmitInfo.commandBufferCount = 1;
687+
computeSubmitInfo.pCommandBuffers = &compute.commandBuffers[graphicsSubmitIndex];
688+
689+
VK_CHECK_RESULT(vkQueueSubmit(compute.queue, 1, &computeSubmitInfo, VK_NULL_HANDLE));
690+
691+
// Add an extra set of acquire and release barriers to the graphics queue,
692+
// so that when the second compute command buffer executes for the first time
693+
// it doesn't complain about a lack of a corresponding "acquire" to its "release" and vice versa
694+
VkCommandBuffer barrierCmd = vulkanDevice->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true);
695+
addComputeToGraphicsBarriers(barrierCmd, 0, VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_VERTEX_INPUT_BIT);
696+
addGraphicsToComputeBarriers(barrierCmd, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, 0, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT);
697+
vulkanDevice->flushCommandBuffer(barrierCmd, queue, true);
698+
}
655699
}
656700
computeSubmitInfo.signalSemaphoreCount = 1;
657-
computeSubmitInfo.pSignalSemaphores = &compute.semaphores.complete;
701+
computeSubmitInfo.pSignalSemaphores = &compute.semaphores[computeSubmitIndex].complete;
658702
computeSubmitInfo.commandBufferCount = 1;
659-
computeSubmitInfo.pCommandBuffers = &compute.commandBuffers[readSet];
703+
computeSubmitInfo.pCommandBuffers = &compute.commandBuffers[computeSubmitIndex];
660704

661705
VK_CHECK_RESULT(vkQueueSubmit(compute.queue, 1, &computeSubmitInfo, VK_NULL_HANDLE));
662706

@@ -667,10 +711,10 @@ class VulkanExample : public VulkanExampleBase
667711
submitPipelineStages, VK_PIPELINE_STAGE_VERTEX_INPUT_BIT
668712
};
669713
VkSemaphore waitSemaphores[2] = {
670-
semaphores.presentComplete, compute.semaphores.complete
714+
semaphores.presentComplete, compute.semaphores[graphicsSubmitIndex].complete
671715
};
672716
VkSemaphore signalSemaphores[2] = {
673-
semaphores.renderComplete, compute.semaphores.ready
717+
semaphores.renderComplete, compute.semaphores[graphicsSubmitIndex].ready
674718
};
675719

676720
submitInfo.waitSemaphoreCount = 2;

0 commit comments

Comments
 (0)