Skip to content

Commit 9be5fe8

Browse files
committed
Improve cmd buffer sync
- Use semaphores to sync cmd buffers in the same frame
1 parent da9395d commit 9be5fe8

5 files changed

Lines changed: 80 additions & 15 deletions

File tree

src/main/java/net/vulkanmod/render/texture/ImageUploadHelper.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ public void submitCommands() {
2121
return;
2222
}
2323

24-
long fence = queue.submitCommands(this.currentCmdBuffer);
25-
Synchronization.INSTANCE.addCommandBuffer(this.currentCmdBuffer);
24+
queue.submitCommands(this.currentCmdBuffer, true);
25+
Synchronization.INSTANCE.addCommandBuffer(this.currentCmdBuffer, true);
2626

2727
this.currentCmdBuffer = null;
2828
}

src/main/java/net/vulkanmod/vulkan/Renderer.java

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -338,9 +338,20 @@ private void submitFrame() {
338338
VkSubmitInfo submitInfo = VkSubmitInfo.calloc(stack);
339339
submitInfo.sType(VK_STRUCTURE_TYPE_SUBMIT_INFO);
340340

341-
submitInfo.waitSemaphoreCount(1);
342-
submitInfo.pWaitSemaphores(stack.longs(imageAvailableSemaphores.get(currentFrame)));
343-
submitInfo.pWaitDstStageMask(stack.ints(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT));
341+
Synchronization.INSTANCE.addWaitSemaphore(imageAvailableSemaphores.get(currentFrame));
342+
var waitSemaphores = Synchronization.INSTANCE.getWaitSemaphores(stack);
343+
int waitSemaphoreCount = waitSemaphores.limit();
344+
IntBuffer waitDstStageMask = stack.mallocInt(waitSemaphoreCount);
345+
346+
for (int i = 0; i < waitSemaphoreCount - 1; i++) {
347+
waitDstStageMask.put(i, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT);
348+
}
349+
// Image available semaphore mask
350+
waitDstStageMask.put(waitSemaphoreCount - 1, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT);
351+
352+
submitInfo.pWaitSemaphores(waitSemaphores);
353+
submitInfo.waitSemaphoreCount(waitSemaphores.limit());
354+
submitInfo.pWaitDstStageMask(waitDstStageMask);
344355
submitInfo.pSignalSemaphores(stack.longs(renderFinishedSemaphores.get(currentFrame)));
345356
submitInfo.pCommandBuffers(stack.pointers(currentCmdBuffer));
346357

@@ -370,6 +381,9 @@ private void submitFrame() {
370381
throw new RuntimeException("Failed to present rendered frame: %s".formatted(VkResult.decode(vkResult)));
371382
}
372383

384+
// Semaphore waited command buffers will be reset right after waiting this command buffer's fence
385+
Synchronization.INSTANCE.scheduleCbReset();
386+
373387
currentFrame = (currentFrame + 1) % framesNum;
374388
}
375389
}
@@ -414,10 +428,10 @@ public void submitUploads() {
414428
if (transferCb.isRecording()) {
415429
final var transferQueue = DeviceManager.getTransferQueue();
416430
try (MemoryStack stack = MemoryStack.stackPush()) {
417-
transferCb.submitCommands(stack, transferQueue.vkQueue(), false);
431+
transferCb.submitCommands(stack, transferQueue.vkQueue(), true);
418432
}
419433

420-
Synchronization.INSTANCE.addCommandBuffer(transferCb);
434+
Synchronization.INSTANCE.addCommandBuffer(transferCb, true);
421435

422436
transferCbs.set(currentFrame, transferQueue.getCommandPool().getCommandBuffer());
423437
}

src/main/java/net/vulkanmod/vulkan/Synchronization.java

Lines changed: 49 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,21 @@
11
package net.vulkanmod.vulkan;
22

3+
import it.unimi.dsi.fastutil.longs.LongArrayList;
34
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
5+
import net.vulkanmod.vulkan.memory.MemoryManager;
46
import net.vulkanmod.vulkan.queue.CommandPool;
57
import net.vulkanmod.vulkan.util.VUtil;
8+
import org.lwjgl.system.MemoryStack;
69
import org.lwjgl.system.MemoryUtil;
710
import org.lwjgl.vulkan.VkDevice;
811

912
import java.nio.LongBuffer;
1013

1114
import static org.lwjgl.vulkan.VK10.*;
1215

16+
/***
17+
* Synchronization utility to sync in frame ops that need to be completed before executing main cmd buffer.
18+
*/
1319
public class Synchronization {
1420
private static final int ALLOCATION_SIZE = 50;
1521

@@ -18,15 +24,28 @@ public class Synchronization {
1824
private final LongBuffer fences;
1925
private int idx = 0;
2026

21-
private ObjectArrayList<CommandPool.CommandBuffer> commandBuffers = new ObjectArrayList<>();
27+
private final ObjectArrayList<CommandPool.CommandBuffer> fenceCbs = new ObjectArrayList<>();
28+
29+
private final LongArrayList semaphores = new LongArrayList();
30+
private final ObjectArrayList<CommandPool.CommandBuffer> semaphoreCbs = new ObjectArrayList<>();
2231

2332
Synchronization(int allocSize) {
2433
this.fences = MemoryUtil.memAllocLong(allocSize);
2534
}
2635

27-
public synchronized void addCommandBuffer(CommandPool.CommandBuffer commandBuffer) {
28-
this.addFence(commandBuffer.getFence());
29-
this.commandBuffers.add(commandBuffer);
36+
public void addCommandBuffer(CommandPool.CommandBuffer commandBuffer) {
37+
addCommandBuffer(commandBuffer, false);
38+
}
39+
40+
public synchronized void addCommandBuffer(CommandPool.CommandBuffer commandBuffer, boolean useSemaphore) {
41+
if (!useSemaphore) {
42+
this.addFence(commandBuffer.getFence());
43+
this.fenceCbs.add(commandBuffer);
44+
}
45+
else {
46+
this.semaphores.add(commandBuffer.getSemaphore());
47+
this.semaphoreCbs.add(commandBuffer);
48+
}
3049
}
3150

3251
public synchronized void addFence(long fence) {
@@ -47,13 +66,37 @@ public synchronized void waitFences() {
4766

4867
vkWaitForFences(device, fences, true, VUtil.UINT64_MAX);
4968

50-
this.commandBuffers.forEach(CommandPool.CommandBuffer::reset);
51-
this.commandBuffers.clear();
69+
this.fenceCbs.forEach(CommandPool.CommandBuffer::reset);
70+
this.fenceCbs.clear();
5271

5372
fences.limit(ALLOCATION_SIZE);
5473
idx = 0;
5574
}
5675

76+
public synchronized void addWaitSemaphore(long semaphore) {
77+
this.semaphores.add(semaphore);
78+
}
79+
80+
public LongBuffer getWaitSemaphores(MemoryStack stack) {
81+
var buffer = stack.mallocLong(this.semaphores.size())
82+
.put(this.semaphores.elements(), 0, this.semaphores.size());
83+
buffer.flip();
84+
85+
this.semaphores.clear();
86+
return buffer;
87+
}
88+
89+
public void scheduleCbReset() {
90+
final var frameSemaphoreCbs = this.semaphoreCbs.clone();
91+
MemoryManager.getInstance().addFrameOp(
92+
() -> {
93+
frameSemaphoreCbs.forEach(CommandPool.CommandBuffer::reset);
94+
}
95+
);
96+
97+
this.semaphoreCbs.clear();
98+
}
99+
57100
public static void waitFence(long fence) {
58101
VkDevice device = Vulkan.getVkDevice();
59102

src/main/java/net/vulkanmod/vulkan/queue/CommandPool.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,10 @@ public long getFence() {
130130
return fence;
131131
}
132132

133+
public long getSemaphore() {
134+
return semaphore;
135+
}
136+
133137
public boolean isSubmitted() {
134138
return submitted;
135139
}

src/main/java/net/vulkanmod/vulkan/queue/Queue.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,13 @@ public synchronized CommandPool.CommandBuffer beginCommands() {
4747
this.commandPool = new CommandPool(familyIndex);
4848
}
4949

50-
public synchronized long submitCommands(CommandPool.CommandBuffer commandBuffer) {
50+
public long submitCommands(CommandPool.CommandBuffer commandBuffer) {
51+
return submitCommands(commandBuffer, false);
52+
}
53+
54+
public synchronized long submitCommands(CommandPool.CommandBuffer commandBuffer, boolean useSemaphore) {
5155
try (MemoryStack stack = stackPush()) {
52-
return commandBuffer.submitCommands(stack, vkQueue, false);
56+
return commandBuffer.submitCommands(stack, vkQueue, useSemaphore);
5357
}
5458
}
5559

0 commit comments

Comments
 (0)