Skip to content

Commit aa5c61d

Browse files
committed
Update additional samples to use new sync
1 parent 1b8ccfe commit aa5c61d

10 files changed

Lines changed: 631 additions & 715 deletions

File tree

examples/computeparticles/computeparticles.cpp

Lines changed: 307 additions & 357 deletions
Large diffs are not rendered by default.

examples/computeraytracing/computeraytracing.cpp

Lines changed: 231 additions & 230 deletions
Large diffs are not rendered by default.

examples/computeshader/computeshader.cpp

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

shaders/glsl/computeparticles/particle.comp

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
/* Copyright (c) 2016-2025, Sascha Willems
2+
*
3+
* SPDX-License-Identifier: MIT
4+
*
5+
*/
6+
17
#version 450
28

39
struct Particle
@@ -7,15 +13,17 @@ struct Particle
713
vec4 gradientPos;
814
};
915

10-
// Binding 0 : Position storage buffer
11-
layout(std140, binding = 0) buffer Pos
12-
{
13-
Particle particles[ ];
16+
layout(std140, binding = 0) readonly buffer ParticleSSBOIn {
17+
Particle particlesIn[ ];
18+
};
19+
20+
layout(std140, binding = 1) buffer ParticleSSBOOut {
21+
Particle particlesOut[ ];
1422
};
1523

1624
layout (local_size_x = 256) in;
1725

18-
layout (binding = 1) uniform UBO
26+
layout (binding = 2) uniform UBO
1927
{
2028
float deltaT;
2129
float destX;
@@ -48,9 +56,10 @@ void main()
4856
if (index >= ubo.particleCount)
4957
return;
5058

51-
// Read position and velocity
52-
vec2 vVel = particles[index].vel.xy;
53-
vec2 vPos = particles[index].pos.xy;
59+
// Read position and velocity from previous frame
60+
vec2 vVel = particlesIn[index].vel.xy;
61+
vec2 vPos = particlesIn[index].pos.xy;
62+
vec4 gPos = particlesIn[index].gradientPos;
5463

5564
vec2 destPos = vec2(ubo.destX, ubo.destY);
5665

@@ -65,12 +74,12 @@ void main()
6574
if ((vPos.x < -1.0) || (vPos.x > 1.0) || (vPos.y < -1.0) || (vPos.y > 1.0))
6675
vVel = (-vVel * 0.1) + attraction(vPos, destPos) * 12;
6776
else
68-
particles[index].pos.xy = vPos;
77+
particlesOut[index].pos.xy = vPos;
6978

7079
// Write back
71-
particles[index].vel.xy = vVel;
72-
particles[index].gradientPos.x += 0.02 * ubo.deltaT;
73-
if (particles[index].gradientPos.x > 1.0)
74-
particles[index].gradientPos.x -= 1.0;
80+
particlesOut[index].vel.xy = vVel;
81+
particlesOut[index].gradientPos.x = gPos.x + 0.02 * ubo.deltaT;
82+
if (particlesOut[index].gradientPos.x > 1.0)
83+
particlesOut[index].gradientPos.x -= 1.0;
7584
}
7685

364 Bytes
Binary file not shown.

shaders/glsl/computeparticles/particle.vert

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,6 @@ layout (location = 1) in vec4 inGradientPos;
66
layout (location = 0) out vec4 outColor;
77
layout (location = 1) out float outGradientPos;
88

9-
out gl_PerVertex
10-
{
11-
vec4 gl_Position;
12-
float gl_PointSize;
13-
};
14-
159
void main ()
1610
{
1711
gl_PointSize = 8.0;

shaders/hlsl/computeparticles/particle.comp

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,10 @@ struct Particle
66
float2 vel;
77
float4 gradientPos;
88
};
9-
10-
// Binding 0 : Position storage buffer
11-
RWStructuredBuffer<Particle> particles : register(u0);
9+
// Previous particles storage buffer
10+
RWStructuredBuffer<Particle> particlesIn : register(u0);
11+
// Current particles storage buffer
12+
RWStructuredBuffer<Particle> particlesOut : register(u1);
1213

1314
struct UBO
1415
{
@@ -17,8 +18,7 @@ struct UBO
1718
float destY;
1819
int particleCount;
1920
};
20-
21-
cbuffer ubo : register(b1) { UBO ubo; }
21+
cbuffer ubo : register(b2) { UBO ubo; }
2222

2323
float2 attraction(float2 pos, float2 attractPos)
2424
{
@@ -46,9 +46,10 @@ void main(uint3 GlobalInvocationID : SV_DispatchThreadID)
4646
if (index >= ubo.particleCount)
4747
return;
4848

49-
// Read position and velocity
50-
float2 vVel = particles[index].vel.xy;
51-
float2 vPos = particles[index].pos.xy;
49+
// Read position and velocity from previous frame
50+
float2 vVel = particlesIn[index].vel.xy;
51+
float2 vPos = particlesIn[index].pos.xy;
52+
float4 gPos = particlesIn[index].gradientPos;
5253

5354
float2 destPos = float2(ubo.destX, ubo.destY);
5455

@@ -63,12 +64,12 @@ void main(uint3 GlobalInvocationID : SV_DispatchThreadID)
6364
if ((vPos.x < -1.0) || (vPos.x > 1.0) || (vPos.y < -1.0) || (vPos.y > 1.0))
6465
vVel = (-vVel * 0.1) + attraction(vPos, destPos) * 12;
6566
else
66-
particles[index].pos.xy = vPos;
67+
particlesIn[index].pos.xy = vPos;
6768

6869
// Write back
69-
particles[index].vel.xy = vVel;
70-
particles[index].gradientPos.x += 0.02 * ubo.deltaT;
71-
if (particles[index].gradientPos.x > 1.0)
72-
particles[index].gradientPos.x -= 1.0;
70+
particlesOut[index].vel.xy = vVel;
71+
particlesOut[index].gradientPos.x = gPos.x + 0.02 * ubo.deltaT;
72+
if (particlesOut[index].gradientPos.x > 1.0)
73+
particlesOut[index].gradientPos.x -= 1.0;
7374
}
7475

-2.05 KB
Binary file not shown.
156 Bytes
Binary file not shown.

shaders/slang/computeparticles/particle.slang

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,10 @@ struct Particle
2727
float2 vel;
2828
float4 gradientPos;
2929
};
30-
// Binding 0 : Position storage buffer
31-
[[vk::binding(0, 0)]] RWStructuredBuffer<Particle> particles;
30+
// Previous particles storage buffer
31+
[[vk::binding(0, 0)]] RWStructuredBuffer<Particle> particlesIn;
32+
// Current particles storage buffer
33+
[[vk::binding(1, 0)]] RWStructuredBuffer<Particle> particlesOut;
3234

3335
struct UBO
3436
{
@@ -37,7 +39,7 @@ struct UBO
3739
float destY;
3840
int particleCount;
3941
};
40-
[[vk::binding(1, 0)]] ConstantBuffer<UBO> ubo;
42+
[[vk::binding(2, 0)]] ConstantBuffer<UBO> ubo;
4143

4244
struct PushConsts
4345
{
@@ -90,9 +92,10 @@ void computeMain(uint3 GlobalInvocationID : SV_DispatchThreadID)
9092
return;
9193
}
9294

93-
// Read position and velocity
94-
float2 vVel = particles[index].vel.xy;
95-
float2 vPos = particles[index].pos.xy;
95+
// Read position and velocity from previous frame
96+
float2 vVel = particlesIn[index].vel.xy;
97+
float2 vPos = particlesIn[index].pos.xy;
98+
float4 gPos = particlesIn[index].gradientPos;
9699

97100
float2 destPos = float2(ubo.destX, ubo.destY);
98101

@@ -107,14 +110,14 @@ void computeMain(uint3 GlobalInvocationID : SV_DispatchThreadID)
107110
if ((vPos.x < -1.0) || (vPos.x > 1.0) || (vPos.y < -1.0) || (vPos.y > 1.0)) {
108111
vVel = (-vVel * 0.1) + attraction(vPos, destPos) * 12;
109112
} else {
110-
particles[index].pos.xy = vPos;
113+
particlesOut[index].pos.xy = vPos;
111114
}
112115

113116
// Write back
114-
particles[index].vel.xy = vVel;
115-
particles[index].gradientPos.x += 0.02 * ubo.deltaT;
116-
if (particles[index].gradientPos.x > 1.0) {
117-
particles[index].gradientPos.x -= 1.0;
117+
particlesOut[index].vel.xy = vVel;
118+
particlesOut[index].gradientPos.x = gPos.x + 0.02 * ubo.deltaT;
119+
if (particlesOut[index].gradientPos.x > 1.0) {
120+
particlesOut[index].gradientPos.x -= 1.0;
118121
}
119122
}
120123

0 commit comments

Comments
 (0)