diff --git a/src/main/java/net/vulkanmod/render/chunk/buffer/AreaBuffer.java b/src/main/java/net/vulkanmod/render/chunk/buffer/AreaBuffer.java index 54ca22d65..f0b609832 100644 --- a/src/main/java/net/vulkanmod/render/chunk/buffer/AreaBuffer.java +++ b/src/main/java/net/vulkanmod/render/chunk/buffer/AreaBuffer.java @@ -1,7 +1,6 @@ package net.vulkanmod.render.chunk.buffer; import it.unimi.dsi.fastutil.ints.Int2ReferenceOpenHashMap; -import it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap; import net.vulkanmod.Initializer; import net.vulkanmod.render.chunk.util.Util; import net.vulkanmod.vulkan.memory.*; @@ -20,8 +19,6 @@ public class AreaBuffer { private final Int2ReferenceOpenHashMap usedSegments = new Int2ReferenceOpenHashMap<>(); - private final Reference2ReferenceOpenHashMap parametersMap = new Reference2ReferenceOpenHashMap<>(); - Segment first, last; private Buffer buffer; @@ -29,35 +26,32 @@ public class AreaBuffer { int size, used = 0; int segments = 0; - public AreaBuffer(Usage usage, int size, int elementSize) { - - this.usage = usage.index; + public AreaBuffer(Usage usage, int elementCount, int elementSize) { + this.usage = usage.usage; this.elementSize = elementSize; - this.buffer = this.allocateBuffer(size); - this.size = size; + this.size = elementCount * elementSize; + this.buffer = this.allocateBuffer(); - Segment s = new Segment(0, size); + Segment s = new Segment(0, elementCount); segments++; last = first = s; } - private Buffer allocateBuffer(int size) { - int bufferSize = size; - + private Buffer allocateBuffer() { Buffer buffer; - if(this.usage == Usage.VERTEX.index) { - buffer = new VertexBuffer(bufferSize, MEMORY_TYPE); + if (this.usage == Usage.VERTEX.usage) { + buffer = new VertexBuffer(this.size, MEMORY_TYPE); } else { - buffer = new IndexBuffer(bufferSize, MEMORY_TYPE); + buffer = new IndexBuffer(this.size, MEMORY_TYPE); } return buffer; } public Segment upload(ByteBuffer byteBuffer, int oldOffset, DrawBuffers.DrawParameters drawParameters) { // Free old segment - if(oldOffset != -1) { + if (oldOffset != -1) { // Need to delay segment freeing since it might be still used by prev frames in flight // this.setSegmentFree(oldOffset); MemoryManager.getInstance().addToFreeSegment(this, oldOffset); @@ -65,19 +59,18 @@ public Segment upload(ByteBuffer byteBuffer, int oldOffset, DrawBuffers.DrawPara int size = byteBuffer.remaining(); - if(size % elementSize != 0) - throw new RuntimeException("unaligned byteBuffer"); + if (DEBUG && size % elementSize != 0) + throw new RuntimeException("Unaligned buffer"); Segment segment = findSegment(size); - if(segment.size - size > 0) { + if (segment.size - size > 0) { Segment s1 = new Segment(segment.offset + size, segment.size - size); segments++; - if(segment.next != null) { + if (segment.next != null) { s1.bindNext(segment.next); - } - else + } else this.last = s1; segment.bindNext(s1); @@ -88,10 +81,10 @@ public Segment upload(ByteBuffer byteBuffer, int oldOffset, DrawBuffers.DrawPara segment.free = false; this.usedSegments.put(segment.offset, segment); - this.parametersMap.put(segment, drawParameters); + segment.drawParameters = drawParameters; Buffer dst = this.buffer; - UploadManager.INSTANCE.recordUpload(dst.getId(), segment.offset, size, byteBuffer); + UploadManager.INSTANCE.recordUpload(dst, segment.offset, size, byteBuffer); this.used += size; @@ -102,18 +95,16 @@ public Segment findSegment(int size) { Segment segment = null; Segment segment1 = this.first; - while(segment1 != null) { - - if(segment1.isFree() && segment1.size >= size) { - - if(segment == null || segment1.size < segment.size) + while (segment1 != null) { + if (segment1.isFree() && segment1.size >= size) { + if (segment == null || segment1.size < segment.size) segment = segment1; } segment1 = segment1.next; } - if(segment == null || segment.size < size) { + if (segment == null || segment.size < size) { return this.reallocate(size); } @@ -122,112 +113,126 @@ public Segment findSegment(int size) { public Segment reallocate(int uploadSize) { int oldSize = this.size; - int increment = this.size >> 1; - //Try to increase size up to 8 times - for(int i = 0; i < 8 && increment <= uploadSize; ++i) { - increment *= 2; - } + int minIncrement = this.size >> 3; + minIncrement = Util.align(minIncrement, this.elementSize); + + int increment = Math.max(minIncrement, uploadSize << 1); - if(increment < uploadSize) + if (increment < uploadSize) throw new RuntimeException(String.format("Size increment %d < %d (Upload size)", increment, uploadSize)); int newSize = oldSize + increment; - Buffer dst = this.allocateBuffer(newSize); - - UploadManager.INSTANCE.copyBuffer(this.buffer, dst); - - // TODO -// defrag(dst); + this.size = newSize; + Buffer dst = this.allocateBuffer(); - if(DEBUG) - checkSegments(); + moveUsedSegments(dst); this.buffer.freeBuffer(); this.buffer = dst; - this.size = newSize; - - int offset = Util.align(oldSize, elementSize); - - if(last.isFree()) + if (last.isFree()) { last.size += increment; + } else { - Segment segment = new Segment(offset, increment); + int offset = last.offset + last.size; + Segment segment = new Segment(offset, newSize - offset); segments++; last.bindNext(segment); last = segment; } + + if (DEBUG) + checkSegments(); + return last; } - void defrag(Buffer dst) { - int srcOffset, dstOffset, size = 0; + void moveUsedSegments(Buffer dst) { + int srcOffset, dstOffset, uploadSize; + int usedCount = 0; - Segment s = this.first; - while(s != null) { - if(!s.isFree()) { - s = s.next; - continue; - } + dstOffset = 0; + int currOffset = dstOffset; - mergeFreeSubsequent(s); + Segment segment = this.first; + Segment prevUsed = null; - Segment src = s.next; + srcOffset = -1; + uploadSize = 0; - if(src == null) { - return; - } + while (segment != null) { + if (!segment.isFree()) { + usedCount++; + + if (segment.offset != srcOffset + uploadSize) { + + if (srcOffset == -1) { + dstOffset = 0; + this.first = segment; + segment.prev = null; + } else { + UploadManager.INSTANCE.copyBuffer(this.buffer, srcOffset, dst, dstOffset, uploadSize); - dstOffset = s.offset; - srcOffset = src.offset; + dstOffset += uploadSize; + } - while(src != null && !src.isFree()) { - //Swap segments - if(s.prev != null) - s.prev.bindNext(src); - if(src.next != null) - s.bindNext(src.next); + srcOffset = segment.offset; + uploadSize = segment.size; - src.bindNext(s); + } else { + uploadSize += segment.size; + } - src.offset = s.offset; - s.offset += src.size; + this.usedSegments.remove(segment.offset); + segment.offset = currOffset; + currOffset += segment.size; + updateDrawParams(segment); + this.usedSegments.put(segment.offset, segment); - //Update draw parameters - updateDrawParams(src); + if (prevUsed != null) { + prevUsed.bindNext(segment); + } - size += src.size; - src = s.next; + prevUsed = segment; } - UploadManager.INSTANCE.copyBuffer(this.buffer, srcOffset, dst, dstOffset, size); + segment = segment.next; + } + if (uploadSize > 0) { + UploadManager.INSTANCE.copyBuffer(this.buffer, srcOffset, dst, dstOffset, uploadSize); } + if (prevUsed != null) { + prevUsed.next = null; + this.last = prevUsed; + + this.segments = usedCount; + } } public void setSegmentFree(int offset) { Segment segment = usedSegments.remove(offset * elementSize); - if(segment == null) + if (segment == null) return; this.used -= segment.size; segment.free = true; - parametersMap.remove(segment); + segment.drawParameters = null; Segment next = segment.next; - if(next != null && next.isFree()) { + if (next != null && next.isFree()) { mergeSegments(segment, next); } Segment prev = segment.prev; - if(prev != null && prev.isFree()) { + if (prev != null && prev.isFree()) { mergeSegments(prev, segment); } } @@ -235,10 +240,9 @@ public void setSegmentFree(int offset) { private void mergeSegments(Segment segment, Segment next) { segment.size += next.size; - if(next.next != null) { + if (next.next != null) { next.next.prev = segment; - } - else { + } else { this.last = segment; } @@ -246,23 +250,13 @@ private void mergeSegments(Segment segment, Segment next) { this.segments--; } - private void mergeFreeSubsequent(Segment segment) { - Segment next = segment.next; - while(next != null && next.isFree()) { - mergeSegments(segment, next); - - next = segment.next; - } - } - private void updateDrawParams(Segment segment) { - var params = this.parametersMap.get(segment); + DrawBuffers.DrawParameters params = segment.drawParameters; int elementOffset = segment.offset / elementSize; - if(this.usage == Usage.VERTEX.index) { + if (this.usage == Usage.VERTEX.usage) { params.vertexOffset = elementOffset; - } - else { + } else { params.firstIndex = elementOffset; } } @@ -281,27 +275,62 @@ public int fragmentation() { public void checkSegments() { Segment segment = first; + Segment prev = null; int i = 0; + int usedSegments = 0; + + if (segment.offset != 0) + LOGGER.error(String.format("expected first offset 0 but got %d", segment.offset)); + + while (segment != null) { + if (i >= this.segments) { + LOGGER.error("Count is greater than segments"); + break; + } + + if (segment.prev != prev) { + LOGGER.error(String.format("expected previous segment not matching (segment %d)", i)); + } + + if (!segment.isFree()) { + usedSegments++; + } + + if (segment.offset % elementSize != 0) { + LOGGER.error(String.format("offset %d misaligned (segment %d)", segment.offset, i)); + } - while(segment != null) { Segment next = segment.next; - if(next != null) { + if (next != null) { int offset = segment.offset + segment.size; if (offset != next.offset) LOGGER.error(String.format("expected offset %d but got %d (segment %d)", offset, next.offset, i)); - if(next.prev != segment) + if (next.prev != segment) LOGGER.error(String.format("segment pointer not correct (segment %d)", i)); - } - else { - if(segment != this.last) + + } else { + if (segment != this.last) LOGGER.error(String.format("segment has no next pointer and it's not last (segment %d)", i)); + else { + int segmentEnd = segment.offset + segment.size; + if (segment.offset + segment.size != this.size) + LOGGER.error(String.format("last segment end (%d) does not match buffer size (%d)", segmentEnd, this.size)); + } + } + prev = segment; segment = next; i++; } + + if (i != this.segments) + LOGGER.error("Count do not match segments"); + + if (usedSegments != this.usedSegments.size()) + LOGGER.error("Counted used segment do not match used segments map size"); } public int getSize() { @@ -315,6 +344,7 @@ public int getUsed() { public static class Segment { int offset, size; boolean free = true; + DrawBuffers.DrawParameters drawParameters; Segment next, prev; @@ -323,11 +353,6 @@ private Segment(int offset, int size) { this.size = size; } - public void reset() { - this.offset = -1; - this.size = -1; - } - public int getOffset() { return offset; } @@ -349,27 +374,16 @@ public void bindNext(Segment s) { s.prev = this; } - public void merge() { - Segment next = this.next; - - this.size += next.size; - - if(next.next != null) { - next.next.prev = this; - } - - this.next = next.next; - } } public enum Usage { VERTEX(0), INDEX(1); - final int index; + final int usage; Usage(int i) { - index = i; + usage = i; } } diff --git a/src/main/java/net/vulkanmod/render/chunk/buffer/DrawBuffers.java b/src/main/java/net/vulkanmod/render/chunk/buffer/DrawBuffers.java index 54a152b13..c99ca9c1b 100644 --- a/src/main/java/net/vulkanmod/render/chunk/buffer/DrawBuffers.java +++ b/src/main/java/net/vulkanmod/render/chunk/buffer/DrawBuffers.java @@ -21,7 +21,6 @@ import static org.lwjgl.vulkan.VK10.*; public class DrawBuffers { - private static final int VERTEX_SIZE = PipelineManager.TERRAIN_VERTEX_FORMAT.getVertexSize(); private static final int INDEX_SIZE = Short.BYTES; private final int index; @@ -53,7 +52,7 @@ public void upload(RenderSection section, UploadBuffer buffer, TerrainRenderType if (!buffer.autoIndices) { if (this.indexBuffer == null) - this.indexBuffer = new AreaBuffer(AreaBuffer.Usage.INDEX, 786432 /*RenderType.SMALL_BUFFER_SIZE*/, INDEX_SIZE); + this.indexBuffer = new AreaBuffer(AreaBuffer.Usage.INDEX, 60000, INDEX_SIZE); AreaBuffer.Segment segment = this.indexBuffer.upload(buffer.getIndexBuffer(), drawParameters.firstIndex, drawParameters); firstIndex = segment.offset / INDEX_SIZE; @@ -69,8 +68,15 @@ public void upload(RenderSection section, UploadBuffer buffer, TerrainRenderType private AreaBuffer getAreaBufferOrAlloc(TerrainRenderType renderType) { this.allocated = true; + int initialSize = switch (renderType) { + case SOLID -> 30000; + case CUTOUT -> 100000; + case CUTOUT_MIPPED -> 300000; + case TRANSLUCENT, TRIPWIRE -> 60000; + }; + return this.vertexBuffers.computeIfAbsent( - renderType, renderType1 -> new AreaBuffer(AreaBuffer.Usage.VERTEX, renderType.initialSize, VERTEX_SIZE)); + renderType, renderType1 -> new AreaBuffer(AreaBuffer.Usage.VERTEX, initialSize, VERTEX_SIZE)); } public AreaBuffer getAreaBuffer(TerrainRenderType r) { @@ -209,9 +215,9 @@ public static class DrawParameters { public DrawParameters() {} public void reset(ChunkArea chunkArea, TerrainRenderType r) { - int segmentOffset = vertexOffset * VERTEX_SIZE; AreaBuffer areaBuffer = chunkArea.getDrawBuffers().getAreaBuffer(r); - if (areaBuffer != null && segmentOffset != -1) { + if (areaBuffer != null && this.vertexOffset != -1) { + int segmentOffset = this.vertexOffset * VERTEX_SIZE; areaBuffer.setSegmentFree(segmentOffset); } diff --git a/src/main/java/net/vulkanmod/render/chunk/buffer/UploadManager.java b/src/main/java/net/vulkanmod/render/chunk/buffer/UploadManager.java index 422d79834..2e37a2b4e 100644 --- a/src/main/java/net/vulkanmod/render/chunk/buffer/UploadManager.java +++ b/src/main/java/net/vulkanmod/render/chunk/buffer/UploadManager.java @@ -1,6 +1,5 @@ package net.vulkanmod.render.chunk.buffer; -import it.unimi.dsi.fastutil.longs.LongOpenHashSet; import net.vulkanmod.vulkan.Synchronization; import net.vulkanmod.vulkan.Vulkan; import net.vulkanmod.vulkan.device.DeviceManager; @@ -10,6 +9,7 @@ import net.vulkanmod.vulkan.queue.Queue; import net.vulkanmod.vulkan.queue.TransferQueue; import org.lwjgl.system.MemoryStack; +import org.lwjgl.vulkan.VkBufferMemoryBarrier; import org.lwjgl.vulkan.VkCommandBuffer; import org.lwjgl.vulkan.VkMemoryBarrier; @@ -27,43 +27,26 @@ public static void createInstance() { Queue queue = DeviceManager.getTransferQueue(); CommandPool.CommandBuffer commandBuffer; - LongOpenHashSet dstBuffers = new LongOpenHashSet(); - public void submitUploads() { if (this.commandBuffer == null) return; - queue.submitCommands(this.commandBuffer); + this.queue.submitCommands(this.commandBuffer); + + Synchronization.INSTANCE.addCommandBuffer(this.commandBuffer); + + this.commandBuffer = null; } - public void recordUpload(long bufferId, long dstOffset, long bufferSize, ByteBuffer src) { - if (this.commandBuffer == null) - this.commandBuffer = queue.beginCommands(); + public void recordUpload(Buffer buffer, long dstOffset, long bufferSize, ByteBuffer src) { + beginCommands(); VkCommandBuffer commandBuffer = this.commandBuffer.getHandle(); StagingBuffer stagingBuffer = Vulkan.getStagingBuffer(); stagingBuffer.copyBuffer((int) bufferSize, src); - if (!this.dstBuffers.add(bufferId)) { - 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); - } - - this.dstBuffers.clear(); - } - - TransferQueue.uploadBufferCmd(commandBuffer, stagingBuffer.getId(), stagingBuffer.getOffset(), bufferId, dstOffset, bufferSize); + TransferQueue.uploadBufferCmd(commandBuffer, stagingBuffer.getId(), stagingBuffer.getOffset(), buffer.getId(), dstOffset, bufferSize); } public void copyBuffer(Buffer src, Buffer dst) { @@ -71,39 +54,42 @@ public void copyBuffer(Buffer src, Buffer dst) { } public void copyBuffer(Buffer src, int srcOffset, Buffer dst, int dstOffset, int size) { - if (this.commandBuffer == null) - this.commandBuffer = queue.beginCommands(); + beginCommands(); VkCommandBuffer commandBuffer = this.commandBuffer.getHandle(); 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); + + VkBufferMemoryBarrier.Buffer bufferMemoryBarriers = VkBufferMemoryBarrier.calloc(1, stack); + VkBufferMemoryBarrier bufferMemoryBarrier = bufferMemoryBarriers.get(0); + bufferMemoryBarrier.sType$Default(); + bufferMemoryBarrier.buffer(src.getId()); + bufferMemoryBarrier.srcAccessMask(VK_ACCESS_TRANSFER_WRITE_BIT); + bufferMemoryBarrier.dstAccessMask(VK_ACCESS_TRANSFER_READ_BIT); + bufferMemoryBarrier.size(VK_WHOLE_SIZE); vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, barrier, - null, + bufferMemoryBarriers, null); } - this.dstBuffers.clear(); - this.dstBuffers.add(dst.getId()); - TransferQueue.uploadBufferCmd(commandBuffer, src.getId(), srcOffset, dst.getId(), dstOffset, size); } - public void waitUploads() { - if (this.commandBuffer == null) - return; + public void syncUploads() { + submitUploads(); - Synchronization.INSTANCE.addCommandBuffer(this.commandBuffer); + Synchronization.INSTANCE.waitFences(); + } - this.commandBuffer = null; - this.dstBuffers.clear(); + private void beginCommands() { + if (this.commandBuffer == null) + this.commandBuffer = queue.beginCommands(); } } diff --git a/src/main/java/net/vulkanmod/render/vertex/TerrainRenderType.java b/src/main/java/net/vulkanmod/render/vertex/TerrainRenderType.java index 258d55781..bd42c0752 100644 --- a/src/main/java/net/vulkanmod/render/vertex/TerrainRenderType.java +++ b/src/main/java/net/vulkanmod/render/vertex/TerrainRenderType.java @@ -7,11 +7,11 @@ import java.util.EnumSet; public enum TerrainRenderType { - SOLID(0.0f, 262144 /*BIG_BUFFER_SIZE*/), - CUTOUT_MIPPED(0.5f, 262144 /*MEDIUM_BUFFER_SIZE*/), - CUTOUT(0.1f, 131072 /*SMALL_BUFFER_SIZE*/), - TRANSLUCENT(0.0f, 131072 /*SMALL_BUFFER_SIZE*/), - TRIPWIRE(0.1f, 131072 /*SMALL_BUFFER_SIZE*/); + SOLID(0.0f), + CUTOUT_MIPPED(0.5f), + CUTOUT(0.1f), + TRANSLUCENT(0.0f), + TRIPWIRE(0.1f); public static final TerrainRenderType[] VALUES = TerrainRenderType.values(); @@ -28,11 +28,9 @@ public enum TerrainRenderType { } public final float alphaCutout; - public final int initialSize; - TerrainRenderType(float alphaCutout, int initialSize) { + TerrainRenderType(float alphaCutout) { this.alphaCutout = alphaCutout; - this.initialSize = initialSize; } public void setCutoutUniform() {