Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
144 changes: 114 additions & 30 deletions src/main/java/net/vulkanmod/render/chunk/buffer/UploadManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,17 @@
import net.vulkanmod.vulkan.memory.buffer.Buffer;
import net.vulkanmod.vulkan.memory.buffer.StagingBuffer;
import net.vulkanmod.vulkan.queue.CommandPool;
import net.vulkanmod.vulkan.queue.Queue;
import net.vulkanmod.vulkan.queue.TransferQueue;
import org.lwjgl.system.MemoryStack;
import org.lwjgl.vulkan.VkBufferCopy;
import org.lwjgl.vulkan.VkBufferMemoryBarrier;
import org.lwjgl.vulkan.VkCommandBuffer;
import org.lwjgl.vulkan.VkMemoryBarrier;

import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

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

Expand All @@ -25,59 +28,138 @@ public static void createInstance() {
INSTANCE = new UploadManager();
}

Queue queue = DeviceManager.getTransferQueue();
TransferQueue queue = DeviceManager.getTransferQueue();
CommandPool.CommandBuffer commandBuffer;

LongOpenHashSet dstBuffers = new LongOpenHashSet();

private final List<PendingCopy> pendingCopies = new ArrayList<>();
private long lastFence;

public void submitUploads() {
if (this.commandBuffer == null)
return;
flush();
}

public void recordUpload(Buffer buffer, long dstOffset, long bufferSize, ByteBuffer src) {
StagingBuffer stagingBuffer = Vulkan.getStagingBuffer();
boolean copied = stagingBuffer.copyBuffer((int) bufferSize, src, buffer, dstOffset);

if (copied) {
pendingCopies.add(new PendingCopy(buffer, dstOffset, bufferSize, stagingBuffer.getOffset()));
}
}

public boolean hasPendingWork() {
return !pendingCopies.isEmpty() || this.commandBuffer != null;
}

public long flush() {
if (pendingCopies.isEmpty() && this.commandBuffer == null) {
return 0L;
}

beginCommands();
VkCommandBuffer vkCmdBuffer = this.commandBuffer.getHandle();

List<PendingCopy> consolidated = consolidateCopies();

for (PendingCopy copy : consolidated) {
if (!this.dstBuffers.add(copy.dstBuffer.getId())) {
try (MemoryStack stack = MemoryStack.stackPush()) {
VkMemoryBarrier.Buffer barrier = VkMemoryBarrier.calloc(1, stack);
barrier.sType$Default();
barrier.srcAccessMask(VK_ACCESS_TRANSFER_WRITE_BIT);
barrier.dstAccessMask(VK_ACCESS_TRANSFER_WRITE_BIT);

vkCmdPipelineBarrier(vkCmdBuffer,
VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
0,
barrier,
null,
null);
}

this.dstBuffers.clear();
}

StagingBuffer stagingBuffer = Vulkan.getStagingBuffer();
TransferQueue.uploadBufferCmd(vkCmdBuffer,
stagingBuffer.getId(), copy.srcOffset,
copy.dstBuffer.getId(), copy.dstOffset,
copy.bufferSize);
}

this.queue.submitCommands(this.commandBuffer);
long fence = this.queue.submitCommands(this.commandBuffer);

Synchronization.INSTANCE.addCommandBuffer(this.commandBuffer);

this.lastFence = fence;
this.commandBuffer = null;
this.dstBuffers.clear();
this.pendingCopies.clear();

return fence;
}

public void recordUpload(Buffer buffer, long dstOffset, long bufferSize, ByteBuffer src) {
StagingBuffer stagingBuffer = Vulkan.getStagingBuffer();
stagingBuffer.copyBuffer((int) bufferSize, src);
public long getLastFence() {
return lastFence;
}

beginCommands();
VkCommandBuffer commandBuffer = this.commandBuffer.getHandle();

if (!this.dstBuffers.add(buffer.getId())) {
try (MemoryStack stack = MemoryStack.stackPush()) {
VkMemoryBarrier.Buffer barrier = VkMemoryBarrier.calloc(1, stack);
barrier.sType$Default();
barrier.srcAccessMask(VK_ACCESS_TRANSFER_WRITE_BIT);
barrier.dstAccessMask(VK_ACCESS_TRANSFER_WRITE_BIT);

vkCmdPipelineBarrier(commandBuffer,
VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
0,
barrier,
null,
null);
}
public void recordUploadFallback(Buffer dst, long dstOffset, long bufferSize, ByteBuffer src) {
StagingBuffer tempStaging = new StagingBuffer(bufferSize);
tempStaging.copyBuffer((int) bufferSize, src);

TransferQueue transferQueue = DeviceManager.getTransferQueue();
transferQueue.uploadBufferImmediate(
tempStaging.getId(), 0L,
dst.getId(), dstOffset,
bufferSize);

tempStaging.scheduleFree();
}

this.dstBuffers.clear();
private List<PendingCopy> consolidateCopies() {
if (pendingCopies.size() <= 1) {
return new ArrayList<>(pendingCopies);
}

TransferQueue.uploadBufferCmd(commandBuffer, stagingBuffer.getId(), stagingBuffer.getOffset(), buffer.getId(), dstOffset, bufferSize);
List<PendingCopy> sorted = new ArrayList<>(pendingCopies);
sorted.sort(Comparator.comparingLong((PendingCopy c) -> c.dstBuffer.getId())
.thenComparingLong(c -> c.dstOffset));

List<PendingCopy> merged = new ArrayList<>();
PendingCopy current = sorted.get(0);

for (int i = 1; i < sorted.size(); i++) {
PendingCopy next = sorted.get(i);

if (current.dstBuffer == next.dstBuffer
&& current.dstOffset + current.bufferSize == next.dstOffset
&& current.srcOffset + current.bufferSize == next.srcOffset) {
current = new PendingCopy(current.dstBuffer, current.dstOffset,
current.bufferSize + next.bufferSize, current.srcOffset);
} else {
merged.add(current);
current = next;
}
}
merged.add(current);

return merged;
}

public void copyBuffer(Buffer src, Buffer dst) {
copyBuffer(src, 0, dst, 0, src.getBufferSize());
}

public void copyBuffer(Buffer src, long srcOffset, Buffer dst, long dstOffset, long size) {
if (!this.pendingCopies.isEmpty()) {
flush();
}

beginCommands();

VkCommandBuffer commandBuffer = this.commandBuffer.getHandle();
VkCommandBuffer vkCmdBuffer = this.commandBuffer.getHandle();

try (MemoryStack stack = MemoryStack.stackPush()) {
VkMemoryBarrier.Buffer barrier = VkMemoryBarrier.calloc(1, stack);
Expand All @@ -91,7 +173,7 @@ public void copyBuffer(Buffer src, long srcOffset, Buffer dst, long dstOffset, l
bufferMemoryBarrier.dstAccessMask(VK_ACCESS_TRANSFER_READ_BIT);
bufferMemoryBarrier.size(VK_WHOLE_SIZE);

vkCmdPipelineBarrier(commandBuffer,
vkCmdPipelineBarrier(vkCmdBuffer,
VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
0,
barrier,
Expand All @@ -101,7 +183,7 @@ public void copyBuffer(Buffer src, long srcOffset, Buffer dst, long dstOffset, l

this.dstBuffers.add(dst.getId());

TransferQueue.uploadBufferCmd(commandBuffer, src.getId(), srcOffset, dst.getId(), dstOffset, size);
TransferQueue.uploadBufferCmd(vkCmdBuffer, src.getId(), srcOffset, dst.getId(), dstOffset, size);
}

public void syncUploads() {
Expand All @@ -115,4 +197,6 @@ private void beginCommands() {
this.commandBuffer = queue.beginCommands();
}

record PendingCopy(Buffer dstBuffer, long dstOffset, long bufferSize, long srcOffset) {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,18 @@ public ImageUploadHelper() {
queue = DeviceManager.getGraphicsQueue();
}

public void submitCommands() {
public long submitCommands() {
if (this.currentCmdBuffer == null) {
return;
return 0L;
}

SpriteUpdateUtil.transitionLayouts();

queue.submitCommands(this.currentCmdBuffer, true);
long fence = queue.submitCommands(this.currentCmdBuffer, true);
Synchronization.INSTANCE.addCommandBuffer(this.currentCmdBuffer, true);

this.currentCmdBuffer = null;
return fence;
}

public CommandPool.CommandBuffer getOrStartCommandBuffer() {
Expand Down
122 changes: 111 additions & 11 deletions src/main/java/net/vulkanmod/vulkan/memory/buffer/StagingBuffer.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,19 @@
import org.lwjgl.system.MemoryUtil;

import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import static org.lwjgl.system.libc.LibCString.nmemcpy;
import static org.lwjgl.vulkan.VK10.*;
import static org.lwjgl.vulkan.VK10.VK_BUFFER_USAGE_TRANSFER_SRC_BIT;

public class StagingBuffer extends Buffer {
private static final long DEFAULT_SIZE = 64 * 1024 * 1024;

private final List<FencedRegion> fencedRegions = new ArrayList<>();
private int flushStartOffset;

public StagingBuffer() {
this(DEFAULT_SIZE);
}
Expand All @@ -33,8 +39,13 @@ public void copyBuffer(int size, long scrPtr) {
throw new IllegalArgumentException("Upload size is greater than staging buffer size.");
}

if (size > this.bufferSize - this.usedBytes) {
submitUploads();
if (size > getRemaining()) {
flush();
reclaimCompleted();
}

if (this.usedBytes + size > this.bufferSize) {
this.usedBytes = 0;
}

nmemcpy(this.dataPtr + this.usedBytes, scrPtr, size);
Expand All @@ -43,23 +54,112 @@ public void copyBuffer(int size, long scrPtr) {
this.usedBytes += size;
}

public boolean copyBuffer(int size, ByteBuffer src, Buffer dstBuffer, long dstOffset) {
long srcPtr = MemoryUtil.memAddress(src);

if (size > this.bufferSize) {
throw new IllegalArgumentException("Upload size is greater than staging buffer size.");
}

if (size > getRemaining()) {
flush();
reclaimCompleted();

if (size > getRemaining()) {
UploadManager.INSTANCE.recordUploadFallback(dstBuffer, dstOffset, size, src);
return false;
}
}

if (this.usedBytes + size > this.bufferSize) {
this.usedBytes = 0;
}

nmemcpy(this.dataPtr + this.usedBytes, srcPtr, size);

this.offset = this.usedBytes;
this.usedBytes += size;
return true;
}

public void align(int alignment) {
long alignedOffset = Util.align(usedBytes, alignment);

if (alignedOffset > this.bufferSize) {
submitUploads();
if (alignedOffset + alignment > this.bufferSize) {
flush();
reclaimCompleted();
alignedOffset = 0;
}

this.usedBytes = alignedOffset;
}

private void submitUploads() {
// Submit and wait all recorded uploads before resetting the buffer
UploadManager.INSTANCE.submitUploads();
ImageUploadHelper.INSTANCE.submitCommands();
Synchronization.INSTANCE.waitFences();
public void flush() {
long uploadFence = UploadManager.INSTANCE.flush();
long imageFence = ImageUploadHelper.INSTANCE.submitCommands();

int end = (int) this.usedBytes;

if (end > flushStartOffset) {
if (uploadFence != 0) {
fencedRegions.add(new FencedRegion(uploadFence, flushStartOffset, end));
}
if (imageFence != 0) {
fencedRegions.add(new FencedRegion(imageFence, flushStartOffset, end));
}
} else if (end < flushStartOffset) {
if (uploadFence != 0) {
fencedRegions.add(new FencedRegion(uploadFence, flushStartOffset, (int) this.bufferSize));
fencedRegions.add(new FencedRegion(uploadFence, 0, end));
}
if (imageFence != 0) {
fencedRegions.add(new FencedRegion(imageFence, flushStartOffset, (int) this.bufferSize));
fencedRegions.add(new FencedRegion(imageFence, 0, end));
}
}

flushStartOffset = end;
}

public void reclaimCompleted() {
Iterator<FencedRegion> it = fencedRegions.iterator();
while (it.hasNext()) {
FencedRegion region = it.next();
if (Synchronization.checkFenceStatus(region.fence)) {
it.remove();
}
}
}

public long getRemaining() {
long remaining = this.bufferSize - this.usedBytes;

this.reset();
for (FencedRegion region : fencedRegions) {
if (region.startOffset <= this.usedBytes && this.usedBytes < region.endOffset) {
return 0L;
}
if (this.usedBytes < region.startOffset && region.startOffset - this.usedBytes < remaining) {
remaining = region.startOffset - this.usedBytes;
}
}

return remaining;
}

void submitUploads() {
flush();
reclaimCompleted();
}

static class FencedRegion {
final long fence;
final int startOffset;
final int endOffset;

FencedRegion(long fence, int startOffset, int endOffset) {
this.fence = fence;
this.startOffset = startOffset;
this.endOffset = endOffset;
}
}
}