Skip to content

Commit

Permalink
Add compute queue double buffering for in-parallel compute / graphics…
Browse files Browse the repository at this point in the history
… queue processing
  • Loading branch information
SRSaunders committed May 16, 2024
1 parent 9eba7d8 commit d522bde
Showing 1 changed file with 47 additions and 14 deletions.
61 changes: 47 additions & 14 deletions examples/computecloth/computecloth.cpp
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -70,14 +70,17 @@ class VulkanExample : public VulkanExampleBase
} graphics;

// Resources for the compute part of the example
// SRS - Number of compute command buffers: set to 1 for serialized processing or 2 for in-parallel with graphics queue
#define COMPUTE_CMD_BUFFERS 2
struct Compute {
struct Semaphores {
typedef struct Semaphores_t {
VkSemaphore ready{ VK_NULL_HANDLE };
VkSemaphore complete{ VK_NULL_HANDLE };
} semaphores;
} semaphores_t;
std::array<semaphores_t, COMPUTE_CMD_BUFFERS> semaphores{};
VkQueue queue{ VK_NULL_HANDLE };
VkCommandPool commandPool{ VK_NULL_HANDLE };
std::array<VkCommandBuffer, 2> commandBuffers{};
std::array<VkCommandBuffer, COMPUTE_CMD_BUFFERS> commandBuffers{};
VkDescriptorSetLayout descriptorSetLayout{ VK_NULL_HANDLE };
std::array<VkDescriptorSet, 2> descriptorSets{ VK_NULL_HANDLE };
VkPipelineLayout pipelineLayout{ VK_NULL_HANDLE };
Expand Down Expand Up @@ -126,8 +129,10 @@ class VulkanExample : public VulkanExampleBase
vkDestroyPipelineLayout(device, compute.pipelineLayout, nullptr);
vkDestroyDescriptorSetLayout(device, compute.descriptorSetLayout, nullptr);
vkDestroyPipeline(device, compute.pipeline, nullptr);
vkDestroySemaphore(device, compute.semaphores.ready, nullptr);
vkDestroySemaphore(device, compute.semaphores.complete, nullptr);
for (uint32_t i = 0; i < compute.semaphores.size(); i++) {
vkDestroySemaphore(device, compute.semaphores[i].ready, nullptr);
vkDestroySemaphore(device, compute.semaphores[i].complete, nullptr);
}
vkDestroyCommandPool(device, compute.commandPool, nullptr);

// SSBOs
Expand Down Expand Up @@ -302,7 +307,7 @@ class VulkanExample : public VulkanExampleBase
VkCommandBufferBeginInfo cmdBufInfo = vks::initializers::commandBufferBeginInfo();
cmdBufInfo.flags = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT;

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

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

Expand Down Expand Up @@ -606,13 +611,15 @@ class VulkanExample : public VulkanExampleBase
VK_CHECK_RESULT(vkCreateCommandPool(device, &cmdPoolInfo, nullptr, &compute.commandPool));

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

// Semaphores for graphics / compute synchronization
VkSemaphoreCreateInfo semaphoreCreateInfo = vks::initializers::semaphoreCreateInfo();
VK_CHECK_RESULT(vkCreateSemaphore(device, &semaphoreCreateInfo, nullptr, &compute.semaphores.ready));
VK_CHECK_RESULT(vkCreateSemaphore(device, &semaphoreCreateInfo, nullptr, &compute.semaphores.complete));
for (uint32_t i = 0; i < compute.semaphores.size(); i++) {
VK_CHECK_RESULT(vkCreateSemaphore(device, &semaphoreCreateInfo, nullptr, &compute.semaphores[i].ready));
VK_CHECK_RESULT(vkCreateSemaphore(device, &semaphoreCreateInfo, nullptr, &compute.semaphores[i].complete));
}

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

static bool firstDraw = true;
static uint32_t computeSubmitIndex{ 0 }, graphicsSubmitIndex{ 0 };
if (COMPUTE_CMD_BUFFERS > 1) // should be constexpr, but requires C++17
{
// SRS - if we are double buffering the compute queue, swap the compute command buffer indices
graphicsSubmitIndex = computeSubmitIndex;
computeSubmitIndex = 1 - graphicsSubmitIndex;
}

VkSubmitInfo computeSubmitInfo = vks::initializers::submitInfo();
VkPipelineStageFlags computeWaitDstStageMask = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
if (!firstDraw) {
computeSubmitInfo.waitSemaphoreCount = 1;
computeSubmitInfo.pWaitSemaphores = &compute.semaphores.ready;
computeSubmitInfo.pWaitSemaphores = &compute.semaphores[computeSubmitIndex].ready;
computeSubmitInfo.pWaitDstStageMask = &computeWaitDstStageMask;
}
else {
firstDraw = false;
if (COMPUTE_CMD_BUFFERS > 1) // should be constexpr, but requires C++17
{
// SRS - if we are double buffering the compute queue, submit extra command buffer at start
computeSubmitInfo.signalSemaphoreCount = 1;
computeSubmitInfo.pSignalSemaphores = &compute.semaphores[graphicsSubmitIndex].complete;
computeSubmitInfo.commandBufferCount = 1;
computeSubmitInfo.pCommandBuffers = &compute.commandBuffers[graphicsSubmitIndex];

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

// Add an extra set of acquire and release barriers to the graphics queue,
// so that when the second compute command buffer executes for the first time
// it doesn't complain about a lack of a corresponding "acquire" to its "release" and vice versa
VkCommandBuffer barrierCmd = vulkanDevice->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true);
addComputeToGraphicsBarriers(barrierCmd, 0, VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_VERTEX_INPUT_BIT);
addGraphicsToComputeBarriers(barrierCmd, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, 0, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT);
vulkanDevice->flushCommandBuffer(barrierCmd, queue, true);
}
}
computeSubmitInfo.signalSemaphoreCount = 1;
computeSubmitInfo.pSignalSemaphores = &compute.semaphores.complete;
computeSubmitInfo.pSignalSemaphores = &compute.semaphores[computeSubmitIndex].complete;
computeSubmitInfo.commandBufferCount = 1;
computeSubmitInfo.pCommandBuffers = &compute.commandBuffers[readSet];
computeSubmitInfo.pCommandBuffers = &compute.commandBuffers[computeSubmitIndex];

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

Expand All @@ -678,10 +711,10 @@ class VulkanExample : public VulkanExampleBase
submitPipelineStages, VK_PIPELINE_STAGE_VERTEX_INPUT_BIT
};
VkSemaphore waitSemaphores[2] = {
semaphores.presentComplete, compute.semaphores.complete
semaphores.presentComplete, compute.semaphores[graphicsSubmitIndex].complete
};
VkSemaphore signalSemaphores[2] = {
semaphores.renderComplete, compute.semaphores.ready
semaphores.renderComplete, compute.semaphores[graphicsSubmitIndex].ready
};

submitInfo.waitSemaphoreCount = 2;
Expand Down

0 comments on commit d522bde

Please sign in to comment.