From 564b0996f92e16c64c54eb3fa72589a4882e6378 Mon Sep 17 00:00:00 2001 From: Jozufozu Date: Sun, 21 Aug 2022 16:47:15 -0700 Subject: [PATCH] Indirectly packed - Use glsl structs to pack vec/mat data in ssbos - Support RenderStages - Dynamically sized draw storage --- .editorconfig | 6 +- .../instancing/indirect/IndirectBuffers.java | 37 ++- .../indirect/IndirectCullingGroup.java | 194 ++++++++++++++ .../instancing/indirect/IndirectDraw.java | 52 ++++ ...derLists.java => IndirectDrawManager.java} | 9 +- .../instancing/indirect/IndirectDrawSet.java | 54 ++++ .../instancing/indirect/IndirectEngine.java | 22 +- .../indirect/IndirectInstancer.java | 31 ++- .../instancing/indirect/IndirectList.java | 246 ------------------ .../instancing/indirect/IndirectModel.java | 4 +- .../flywheel/core/model/BlockMesh.java | 0 .../oriented/OrientedStorageWriter.java | 20 +- .../transformed/TransformedStorageWriter.java | 14 +- .../core/uniform/FrustumProvider.java | 3 - .../flywheel/instance/oriented_indirect.glsl | 24 +- .../instance/transformed_indirect.glsl | 13 +- .../assets/flywheel/flywheel/util/types.glsl | 48 +++- 17 files changed, 471 insertions(+), 306 deletions(-) create mode 100644 src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectCullingGroup.java create mode 100644 src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectDraw.java rename src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/{RenderLists.java => IndirectDrawManager.java} (58%) create mode 100644 src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectDrawSet.java delete mode 100644 src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectList.java delete mode 100644 src/main/java/com/jozufozu/flywheel/core/model/BlockMesh.java diff --git a/.editorconfig b/.editorconfig index 1eeca6433..2c468ead4 100644 --- a/.editorconfig +++ b/.editorconfig @@ -4,7 +4,7 @@ root = true [*] indent_style = space indent_size = 4 -continuation_indent_size = 8 +ij_continuation_indent_size = 8 end_of_line = lf charset = utf-8 trim_trailing_whitespace = true @@ -16,6 +16,10 @@ indent_size = 2 [*.java] indent_style = tab +ij_java_blank_lines_before_class_end = 0 +ij_java_blank_lines_after_anonymous_class_header = 0 +ij_java_blank_lines_after_class_header = 0 +ij_java_blank_lines_before_method_body = 0 ij_java_else_on_new_line = false ij_continuation_indent_size = 8 ij_java_class_count_to_use_import_on_demand = 99 diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectBuffers.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectBuffers.java index bde48b05a..dae12ac7b 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectBuffers.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectBuffers.java @@ -1,6 +1,22 @@ package com.jozufozu.flywheel.backend.instancing.indirect; -import static org.lwjgl.opengl.GL46.*; +import static org.lwjgl.opengl.GL46.GL_DRAW_INDIRECT_BUFFER; +import static org.lwjgl.opengl.GL46.GL_DYNAMIC_STORAGE_BIT; +import static org.lwjgl.opengl.GL46.GL_MAP_FLUSH_EXPLICIT_BIT; +import static org.lwjgl.opengl.GL46.GL_MAP_PERSISTENT_BIT; +import static org.lwjgl.opengl.GL46.GL_MAP_WRITE_BIT; +import static org.lwjgl.opengl.GL46.GL_SHADER_STORAGE_BUFFER; +import static org.lwjgl.opengl.GL46.glBindBuffer; +import static org.lwjgl.opengl.GL46.glCopyNamedBufferSubData; +import static org.lwjgl.opengl.GL46.glDeleteBuffers; +import static org.lwjgl.opengl.GL46.glFlushMappedNamedBufferRange; +import static org.lwjgl.opengl.GL46.glGenBuffers; +import static org.lwjgl.opengl.GL46.glNamedBufferStorage; +import static org.lwjgl.opengl.GL46.nglBindBuffersRange; +import static org.lwjgl.opengl.GL46.nglCreateBuffers; +import static org.lwjgl.opengl.GL46.nglDeleteBuffers; +import static org.lwjgl.opengl.GL46.nglMapNamedBufferRange; +import static org.lwjgl.opengl.GL46.nglNamedBufferSubData; import org.lwjgl.system.MemoryUtil; import org.lwjgl.system.Pointer; @@ -135,10 +151,23 @@ public class IndirectBuffers { void createDrawStorage(int drawCount) { freeDrawStorage(); + var drawSize = DRAW_COMMAND_STRIDE * drawCount; - glNamedBufferStorage(draw, drawSize, SUB_DATA_BITS); - drawPtr = MemoryUtil.nmemAlloc(drawSize); - // drawPtr = nglMapNamedBufferRange(draw, 0, drawSize, MAP_BITS); + if (maxDrawCount > 0) { + int drawNew = glGenBuffers(); + + glNamedBufferStorage(drawNew, drawSize, SUB_DATA_BITS); + + glDeleteBuffers(draw); + + MemoryUtil.memPutInt(buffers.ptr() + INT_SIZE * 3, drawNew); + draw = drawNew; + drawPtr = MemoryUtil.nmemRealloc(drawPtr, drawSize); + } else { + + glNamedBufferStorage(draw, drawSize, SUB_DATA_BITS); + drawPtr = MemoryUtil.nmemAlloc(drawSize); + } maxDrawCount = drawCount; FlwMemoryTracker._allocGPUMemory(maxDrawCount * DRAW_COMMAND_STRIDE); } diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectCullingGroup.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectCullingGroup.java new file mode 100644 index 000000000..05794682f --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectCullingGroup.java @@ -0,0 +1,194 @@ +package com.jozufozu.flywheel.backend.instancing.indirect; + +import static org.lwjgl.opengl.GL42.GL_COMMAND_BARRIER_BIT; +import static org.lwjgl.opengl.GL42.glMemoryBarrier; +import static org.lwjgl.opengl.GL43.GL_SHADER_STORAGE_BARRIER_BIT; +import static org.lwjgl.opengl.GL46.glBindVertexArray; +import static org.lwjgl.opengl.GL46.glCreateVertexArrays; +import static org.lwjgl.opengl.GL46.glDeleteVertexArrays; +import static org.lwjgl.opengl.GL46.glDispatchCompute; +import static org.lwjgl.opengl.GL46.glEnableVertexArrayAttrib; +import static org.lwjgl.opengl.GL46.glVertexArrayElementBuffer; +import static org.lwjgl.opengl.GL46.glVertexArrayVertexBuffer; + +import com.jozufozu.flywheel.api.RenderStage; +import com.jozufozu.flywheel.api.instancer.InstancedPart; +import com.jozufozu.flywheel.api.struct.StorageBufferWriter; +import com.jozufozu.flywheel.api.struct.StructType; +import com.jozufozu.flywheel.api.vertex.VertexType; +import com.jozufozu.flywheel.backend.gl.shader.GlProgram; +import com.jozufozu.flywheel.backend.instancing.PipelineCompiler; +import com.jozufozu.flywheel.core.Components; +import com.jozufozu.flywheel.core.Materials; +import com.jozufozu.flywheel.core.QuadConverter; +import com.jozufozu.flywheel.core.uniform.UniformBuffer; + +public class IndirectCullingGroup { + + private static final int BARRIER_BITS = GL_SHADER_STORAGE_BARRIER_BIT | GL_COMMAND_BARRIER_BIT; + + final StorageBufferWriter storageBufferWriter; + final GlProgram compute; + final GlProgram draw; + private final VertexType vertexType; + private final long objectStride; + + final IndirectBuffers buffers; + + final IndirectMeshPool meshPool; + private final int elementBuffer; + + int vertexArray; + + final IndirectDrawSet drawSet = new IndirectDrawSet<>(); + + private boolean hasCulledThisFrame; + private boolean needsMemoryBarrier; + private int instanceCountThisFrame; + + IndirectCullingGroup(StructType structType, VertexType vertexType) { + this.vertexType = vertexType; + storageBufferWriter = structType.getStorageBufferWriter(); + + objectStride = storageBufferWriter.getAlignment(); + buffers = new IndirectBuffers(objectStride); + buffers.createBuffers(); + buffers.createObjectStorage(128); + buffers.createDrawStorage(2); + + meshPool = new IndirectMeshPool(vertexType, 1024); + + vertexArray = glCreateVertexArrays(); + + elementBuffer = QuadConverter.getInstance() + .quads2Tris(2048).buffer.handle(); + setupVertexArray(); + + var indirectShader = structType.getIndirectShader(); + compute = ComputeCullerCompiler.INSTANCE.get(indirectShader); + draw = PipelineCompiler.INSTANCE.get(new PipelineCompiler.Context(vertexType, Materials.CHEST, indirectShader, Components.WORLD, Components.INDIRECT)); + } + + private void setupVertexArray() { + glVertexArrayElementBuffer(vertexArray, elementBuffer); + + var meshLayout = vertexType.getLayout(); + var meshAttribs = meshLayout.getAttributeCount(); + + var attributes = meshLayout.getAttributes(); + + long offset = 0; + for (int i = 0; i < meshAttribs; i++) { + var attribute = attributes.get(i); + glEnableVertexArrayAttrib(vertexArray, i); + glVertexArrayVertexBuffer(vertexArray, i, meshPool.vbo, offset, meshLayout.getStride()); + attribute.format(vertexArray, i); + offset += attribute.getByteWidth(); + } + } + + void beginFrame() { + hasCulledThisFrame = false; + needsMemoryBarrier = true; + instanceCountThisFrame = calculateTotalInstanceCountAndPrepareBatches(); + } + + void submit(RenderStage stage) { + if (drawSet.isEmpty()) { + return; + } + + if (instanceCountThisFrame == 0) { + return; + } + + cull(); + dispatchDraw(stage); + } + + private void cull() { + if (hasCulledThisFrame) { + return; + } + + buffers.updateCounts(instanceCountThisFrame, drawSet.size()); + meshPool.uploadAll(); + uploadInstanceData(); + uploadIndirectCommands(); + + UniformBuffer.getInstance() + .sync(); + + compute.bind(); + buffers.bindAll(); + + var groupCount = (instanceCountThisFrame + 31) >> 5; // ceil(instanceCount / 32) + glDispatchCompute(groupCount, 1, 1); + hasCulledThisFrame = true; + } + + private void dispatchDraw(RenderStage stage) { + if (!drawSet.contains(stage)) { + return; + } + + draw.bind(); + glBindVertexArray(vertexArray); + buffers.bindObjectAndTarget(); + buffers.bindIndirectBuffer(); + + memoryBarrier(); + + drawSet.submit(stage); + glBindVertexArray(0); + } + + private void memoryBarrier() { + if (needsMemoryBarrier) { + glMemoryBarrier(BARRIER_BITS); + needsMemoryBarrier = false; + } + } + + private void uploadInstanceData() { + long objectPtr = buffers.objectPtr; + long batchIDPtr = buffers.batchPtr; + + for (int i = 0, batchesSize = drawSet.indirectDraws.size(); i < batchesSize; i++) { + var batch = drawSet.indirectDraws.get(i); + var instanceCount = batch.instancer.getInstanceCount(); + batch.writeObjects(objectPtr, batchIDPtr, i); + + objectPtr += instanceCount * objectStride; + batchIDPtr += instanceCount * IndirectBuffers.INT_SIZE; + } + + buffers.flushObjects(objectPtr - buffers.objectPtr); + buffers.flushBatchIDs(batchIDPtr - buffers.batchPtr); + } + + private void uploadIndirectCommands() { + long writePtr = buffers.drawPtr; + for (var batch : drawSet.indirectDraws) { + batch.writeIndirectCommand(writePtr); + writePtr += IndirectBuffers.DRAW_COMMAND_STRIDE; + } + buffers.flushDrawCommands(writePtr - buffers.drawPtr); + } + + private int calculateTotalInstanceCountAndPrepareBatches() { + int baseInstance = 0; + for (var batch : drawSet.indirectDraws) { + batch.prepare(baseInstance); + baseInstance += batch.instancer.instanceCount; + } + return baseInstance; + } + + public void delete() { + glDeleteVertexArrays(vertexArray); + buffers.delete(); + meshPool.delete(); + } + +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectDraw.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectDraw.java new file mode 100644 index 000000000..c4d3cbaea --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectDraw.java @@ -0,0 +1,52 @@ +package com.jozufozu.flywheel.backend.instancing.indirect; + +import org.lwjgl.system.MemoryUtil; + +import com.jozufozu.flywheel.api.instancer.InstancedPart; +import com.jozufozu.flywheel.api.material.Material; + +public final class IndirectDraw { + final IndirectInstancer instancer; + final IndirectMeshPool.BufferedMesh mesh; + final Material material; + int baseInstance = -1; + + boolean needsFullWrite = true; + + IndirectDraw(IndirectInstancer instancer, Material material, IndirectMeshPool.BufferedMesh mesh) { + this.instancer = instancer; + this.material = material; + this.mesh = mesh; + } + + public void prepare(int baseInstance) { + instancer.update(); + if (baseInstance == this.baseInstance) { + needsFullWrite = false; + return; + } + this.baseInstance = baseInstance; + needsFullWrite = true; + } + + void writeObjects(long objectPtr, long batchIDPtr, int batchID) { + if (needsFullWrite) { + instancer.writeFull(objectPtr, batchIDPtr, batchID); + } else if (instancer.anyToUpdate) { + instancer.writeSparse(objectPtr, batchIDPtr, batchID); + } + instancer.anyToUpdate = false; + } + + public void writeIndirectCommand(long ptr) { + var boundingSphere = mesh.mesh.getBoundingSphere(); + + MemoryUtil.memPutInt(ptr, mesh.getIndexCount()); // count + MemoryUtil.memPutInt(ptr + 4, 0); // instanceCount - to be incremented by the compute shader + MemoryUtil.memPutInt(ptr + 8, 0); // firstIndex - all models share the same index buffer + MemoryUtil.memPutInt(ptr + 12, mesh.getBaseVertex()); // baseVertex + MemoryUtil.memPutInt(ptr + 16, baseInstance); // baseInstance + + boundingSphere.getToAddress(ptr + 20); // boundingSphere + } +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/RenderLists.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectDrawManager.java similarity index 58% rename from src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/RenderLists.java rename to src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectDrawManager.java index 1d823e0fb..956694f59 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/RenderLists.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectDrawManager.java @@ -10,15 +10,14 @@ import com.jozufozu.flywheel.api.vertex.VertexType; import com.jozufozu.flywheel.core.model.Mesh; import com.jozufozu.flywheel.util.Pair; -public class RenderLists { +public class IndirectDrawManager { - public final Map, VertexType>, IndirectList> lists = new HashMap<>(); + public final Map, VertexType>, IndirectCullingGroup> lists = new HashMap<>(); @SuppressWarnings("unchecked") public void add(IndirectInstancer instancer, Material material, Mesh mesh) { - var indirectList = (IndirectList) lists.computeIfAbsent(Pair.of(instancer.structType, mesh.getVertexType()), - p -> new IndirectList<>(p.first(), p.second())); + var indirectList = (IndirectCullingGroup) lists.computeIfAbsent(Pair.of(instancer.type, mesh.getVertexType()), p -> new IndirectCullingGroup<>(p.first(), p.second())); - indirectList.add(instancer, material, mesh); + indirectList.drawSet.add(instancer, material, indirectList.meshPool.alloc(mesh)); } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectDrawSet.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectDrawSet.java new file mode 100644 index 000000000..5bf8c18fc --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectDrawSet.java @@ -0,0 +1,54 @@ +package com.jozufozu.flywheel.backend.instancing.indirect; + +import static org.lwjgl.opengl.GL11.GL_TRIANGLES; +import static org.lwjgl.opengl.GL11.GL_UNSIGNED_INT; +import static org.lwjgl.opengl.GL43.glMultiDrawElementsIndirect; + +import java.util.ArrayList; +import java.util.List; + +import com.jozufozu.flywheel.api.RenderStage; +import com.jozufozu.flywheel.api.instancer.InstancedPart; +import com.jozufozu.flywheel.api.material.Material; + +public class IndirectDrawSet { + + final List> indirectDraws = new ArrayList<>(); + + public boolean isEmpty() { + return indirectDraws.isEmpty(); + } + + public int size() { + return indirectDraws.size(); + } + + public void add(IndirectInstancer instancer, Material material, IndirectMeshPool.BufferedMesh bufferedMesh) { + indirectDraws.add(new IndirectDraw<>(instancer, material, bufferedMesh)); + } + + public void submit(RenderStage stage) { + final int stride = (int) IndirectBuffers.DRAW_COMMAND_STRIDE; + for (int i = 0, indirectDrawsSize = indirectDraws.size(); i < indirectDrawsSize; i++) { + var batch = indirectDraws.get(i); + var material = batch.material; + + if (material.getRenderStage() != stage) { + continue; + } + material.setup(); + glMultiDrawElementsIndirect(GL_TRIANGLES, GL_UNSIGNED_INT, i * stride, 1, stride); + material.clear(); + } + } + + public boolean contains(RenderStage stage) { + for (var draw : indirectDraws) { + if (draw.material.getRenderStage() == stage) { + return true; + } + } + + return false; + } +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectEngine.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectEngine.java index a48c46954..f6585868f 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectEngine.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectEngine.java @@ -9,6 +9,7 @@ import org.jetbrains.annotations.NotNull; import org.lwjgl.opengl.GL32; import com.jozufozu.flywheel.api.RenderStage; +import com.jozufozu.flywheel.api.context.ContextShader; import com.jozufozu.flywheel.api.instancer.InstancedPart; import com.jozufozu.flywheel.api.struct.StructType; import com.jozufozu.flywheel.backend.gl.GlTextureUnit; @@ -16,7 +17,6 @@ import com.jozufozu.flywheel.backend.instancing.Engine; import com.jozufozu.flywheel.backend.instancing.InstanceManager; import com.jozufozu.flywheel.backend.instancing.TaskEngine; import com.jozufozu.flywheel.core.RenderContext; -import com.jozufozu.flywheel.api.context.ContextShader; import com.jozufozu.flywheel.util.WeakHashSet; import com.mojang.blaze3d.systems.RenderSystem; @@ -38,7 +38,7 @@ public class IndirectEngine implements Engine { protected final Map, IndirectFactory> factories = new HashMap<>(); protected final List> uninitializedModels = new ArrayList<>(); - protected final RenderLists renderLists = new RenderLists(); + protected final IndirectDrawManager indirectDrawManager = new IndirectDrawManager(); /** * The set of instance managers that are attached to this engine. @@ -65,16 +65,11 @@ public class IndirectEngine implements Engine { @Override public void renderStage(TaskEngine taskEngine, RenderContext context, RenderStage stage) { - if (stage != RenderStage.AFTER_SOLID_TERRAIN) { - return; - } - setup(); - for (IndirectList list : renderLists.lists.values()) { + for (var list : indirectDrawManager.lists.values()) { list.submit(stage); } - } private void setup() { @@ -93,7 +88,8 @@ public class IndirectEngine implements Engine { factories.values() .forEach(IndirectFactory::delete); - renderLists.lists.values().forEach(IndirectList::delete); + indirectDrawManager.lists.values() + .forEach(IndirectCullingGroup::delete); factories.clear(); } @@ -126,9 +122,13 @@ public class IndirectEngine implements Engine { @Override public void beginFrame(TaskEngine taskEngine, RenderContext context) { for (var model : uninitializedModels) { - model.init(renderLists); + model.init(indirectDrawManager); } uninitializedModels.clear(); + + for (IndirectCullingGroup value : indirectDrawManager.lists.values()) { + value.beginFrame(); + } } private void shiftListeners(int cX, int cY, int cZ) { @@ -141,7 +141,7 @@ public class IndirectEngine implements Engine { @Override public void addDebugInfo(List info) { - info.add("GL33 Instanced Arrays"); + info.add("GL46 Indirect"); info.add("Origin: " + originCoordinate.getX() + ", " + originCoordinate.getY() + ", " + originCoordinate.getZ()); } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectInstancer.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectInstancer.java index 2a2cef2bb..c1c24ba46 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectInstancer.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectInstancer.java @@ -1,5 +1,7 @@ package com.jozufozu.flywheel.backend.instancing.indirect; +import org.lwjgl.system.MemoryUtil; + import com.jozufozu.flywheel.api.instancer.InstancedPart; import com.jozufozu.flywheel.api.struct.StructType; import com.jozufozu.flywheel.backend.instancing.AbstractInstancer; @@ -8,7 +10,6 @@ import com.jozufozu.flywheel.core.layout.BufferLayout; public class IndirectInstancer extends AbstractInstancer { public final BufferLayout instanceFormat; - public final StructType structType; public final IndirectModel parent; int instanceCount = 0; @@ -18,7 +19,6 @@ public class IndirectInstancer extends AbstractInstance super(type); this.parent = parent; this.instanceFormat = type.getLayout(); - this.structType = type; } @Override @@ -40,6 +40,33 @@ public class IndirectInstancer extends AbstractInstance anyToRemove = false; } + public void writeSparse(long objectPtr, long batchIDPtr, int batchID) { + var storageBufferWriter = this.type.getStorageBufferWriter(); + long objectStride = storageBufferWriter.getAlignment(); + for (int i = 0, size = data.size(); i < size; i++) { + final var element = data.get(i); + if (element.checkDirtyAndClear()) { + storageBufferWriter.write(objectPtr + i * objectStride, element); + + MemoryUtil.memPutInt(batchIDPtr + i * IndirectBuffers.INT_SIZE, batchID); + } + } + } + + public void writeFull(long objectPtr, long batchIDPtr, int batchID) { + var storageBufferWriter = this.type.getStorageBufferWriter(); + var objectStride = storageBufferWriter.getAlignment(); + for (var object : data) { + // write object + storageBufferWriter.write(objectPtr, object); + objectPtr += objectStride; + + // write batchID + MemoryUtil.memPutInt(batchIDPtr, batchID); + batchIDPtr += IndirectBuffers.INT_SIZE; + } + } + @Override public void delete() { // noop diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectList.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectList.java deleted file mode 100644 index 7109e4d83..000000000 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectList.java +++ /dev/null @@ -1,246 +0,0 @@ -package com.jozufozu.flywheel.backend.instancing.indirect; - -import static org.lwjgl.opengl.GL46.*; - -import java.util.ArrayList; -import java.util.List; - -import org.lwjgl.system.MemoryUtil; - -import com.jozufozu.flywheel.api.RenderStage; -import com.jozufozu.flywheel.api.instancer.InstancedPart; -import com.jozufozu.flywheel.api.material.Material; -import com.jozufozu.flywheel.api.struct.StorageBufferWriter; -import com.jozufozu.flywheel.api.struct.StructType; -import com.jozufozu.flywheel.api.vertex.VertexType; -import com.jozufozu.flywheel.backend.gl.shader.GlProgram; -import com.jozufozu.flywheel.backend.instancing.PipelineCompiler; -import com.jozufozu.flywheel.core.Components; -import com.jozufozu.flywheel.core.Materials; -import com.jozufozu.flywheel.core.QuadConverter; -import com.jozufozu.flywheel.core.model.Mesh; -import com.jozufozu.flywheel.core.uniform.UniformBuffer; - -public class IndirectList { - - final StorageBufferWriter storageBufferWriter; - final GlProgram compute; - final GlProgram draw; - private final VertexType vertexType; - private final long objectStride; - - final IndirectBuffers buffers; - - final IndirectMeshPool meshPool; - private final int elementBuffer; - - int vertexArray; - - final List batches = new ArrayList<>(); - - IndirectList(StructType structType, VertexType vertexType) { - this.vertexType = vertexType; - storageBufferWriter = structType.getStorageBufferWriter(); - - objectStride = storageBufferWriter.getAlignment(); - buffers = new IndirectBuffers(objectStride); - buffers.createBuffers(); - buffers.createObjectStorage(128); - buffers.createDrawStorage(16); - - meshPool = new IndirectMeshPool(vertexType, 1024); - - vertexArray = glCreateVertexArrays(); - - elementBuffer = QuadConverter.getInstance() - .quads2Tris(2048).buffer.handle(); - setupVertexArray(); - - var indirectShader = structType.getIndirectShader(); - compute = ComputeCullerCompiler.INSTANCE.get(indirectShader); - draw = PipelineCompiler.INSTANCE.get(new PipelineCompiler.Context(vertexType, Materials.CHEST, indirectShader, Components.WORLD, Components.INDIRECT)); - } - - private void setupVertexArray() { - glVertexArrayElementBuffer(vertexArray, elementBuffer); - - var meshLayout = vertexType.getLayout(); - var meshAttribs = meshLayout.getAttributeCount(); - - var attributes = meshLayout.getAttributes(); - - long offset = 0; - for (int i = 0; i < meshAttribs; i++) { - var attribute = attributes.get(i); - glEnableVertexArrayAttrib(vertexArray, i); - glVertexArrayVertexBuffer(vertexArray, i, meshPool.vbo, offset, meshLayout.getStride()); - attribute.format(vertexArray, i); - offset += attribute - .getByteWidth(); - } - } - - public void add(IndirectInstancer instancer, Material material, Mesh mesh) { - batches.add(new Batch(instancer, material, meshPool.alloc(mesh))); - } - - void submit(RenderStage stage) { - if (batches.isEmpty()) { - return; - } - int instanceCountThisFrame = calculateTotalInstanceCountAndPrepareBatches(); - - if (instanceCountThisFrame == 0) { - return; - } - - // TODO: Sort meshes by material and draw many contiguous sections of the draw indirect buffer, - // adjusting uniforms/textures accordingly - buffers.updateCounts(instanceCountThisFrame, batches.size()); - meshPool.uploadAll(); - uploadInstanceData(); - uploadIndirectCommands(); - - UniformBuffer.getInstance().sync(); - - dispatchCompute(instanceCountThisFrame); - glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT); - dispatchDraw(); - } - - private void dispatchDraw() { - draw.bind(); - glVertexArrayElementBuffer(vertexArray, elementBuffer); - glBindVertexArray(vertexArray); - buffers.bindIndirectBuffer(); - - final int stride = (int) IndirectBuffers.DRAW_COMMAND_STRIDE; - long offset = 0; - for (var batch : batches) { - - batch.material.setup(); - glMultiDrawElementsIndirect(GL_TRIANGLES, GL_UNSIGNED_INT, offset, 1, stride); - batch.material.clear(); - offset += stride; - } - } - - private void dispatchCompute(int instanceCount) { - compute.bind(); - buffers.bindAll(); - - var groupCount = (instanceCount + 31) >> 5; // ceil(instanceCount / 32) - glDispatchCompute(groupCount, 1, 1); - } - - private void uploadInstanceData() { - long objectPtr = buffers.objectPtr; - long batchIDPtr = buffers.batchPtr; - - for (int i = 0, batchesSize = batches.size(); i < batchesSize; i++) { - var batch = batches.get(i); - var instanceCount = batch.instancer.getInstanceCount(); - batch.write(objectPtr, batchIDPtr, i); - - objectPtr += instanceCount * objectStride; - batchIDPtr += instanceCount * IndirectBuffers.INT_SIZE; - } - - buffers.flushObjects(objectPtr - buffers.objectPtr); - buffers.flushBatchIDs(batchIDPtr - buffers.batchPtr); - } - - private void uploadIndirectCommands() { - long writePtr = buffers.drawPtr; - for (var batch : batches) { - batch.writeIndirectCommand(writePtr); - writePtr += IndirectBuffers.DRAW_COMMAND_STRIDE; - } - buffers.flushDrawCommands(writePtr - buffers.drawPtr); - } - - private int calculateTotalInstanceCountAndPrepareBatches() { - int baseInstance = 0; - for (var batch : batches) { - batch.prepare(baseInstance); - baseInstance += batch.instancer.instanceCount; - } - return baseInstance; - } - - public void delete() { - glDeleteVertexArrays(vertexArray); - buffers.delete(); - meshPool.delete(); - } - - private final class Batch { - final IndirectInstancer instancer; - final IndirectMeshPool.BufferedMesh mesh; - final Material material; - int baseInstance = -1; - - boolean needsFullWrite = true; - - private Batch(IndirectInstancer instancer, Material material, IndirectMeshPool.BufferedMesh mesh) { - this.instancer = instancer; - this.material = material; - this.mesh = mesh; - } - - public void prepare(int baseInstance) { - instancer.update(); - if (baseInstance == this.baseInstance) { - needsFullWrite = false; - return; - } - this.baseInstance = baseInstance; - needsFullWrite = true; - } - - private void write(long objectPtr, long batchIDPtr, int batchID) { - if (needsFullWrite) { - writeFull(objectPtr, batchIDPtr, batchID); - } else if (instancer.anyToUpdate) { - writeSparse(objectPtr, batchIDPtr, batchID); - } - instancer.anyToUpdate = false; - } - - private void writeSparse(long objectPtr, long batchIDPtr, int batchID) { - var all = instancer.getAll(); - for (int i = 0; i < all.size(); i++) { - final var element = all.get(i); - if (element.checkDirtyAndClear()) { - storageBufferWriter.write(objectPtr + i * objectStride, element); - - MemoryUtil.memPutInt(batchIDPtr + i * IndirectBuffers.INT_SIZE, batchID); - } - } - } - - private void writeFull(long objectPtr, long batchIDPtr, int batchID) { - for (var object : this.instancer.getAll()) { - // write object - storageBufferWriter.write(objectPtr, object); - objectPtr += objectStride; - - // write batchID - MemoryUtil.memPutInt(batchIDPtr, batchID); - batchIDPtr += IndirectBuffers.INT_SIZE; - } - } - - public void writeIndirectCommand(long ptr) { - var boundingSphere = mesh.mesh.getBoundingSphere(); - - MemoryUtil.memPutInt(ptr, mesh.getIndexCount()); // count - MemoryUtil.memPutInt(ptr + 4, 0); // instanceCount - to be incremented by the compute shader - MemoryUtil.memPutInt(ptr + 8, 0); // firstIndex - all models share the same index buffer - MemoryUtil.memPutInt(ptr + 12, mesh.getBaseVertex()); // baseVertex - MemoryUtil.memPutInt(ptr + 16, baseInstance); // baseInstance - - boundingSphere.getToAddress(ptr + 20); // boundingSphere - } - } -} diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectModel.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectModel.java index 88e5107ea..29638638b 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectModel.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectModel.java @@ -14,12 +14,12 @@ public class IndirectModel { this.instancer = new IndirectInstancer<>(this, type); } - public void init(RenderLists renderLists) { + public void init(IndirectDrawManager indirectDrawManager) { var materialMeshMap = this.model.getMeshes(); for (var entry : materialMeshMap.entrySet()) { var material = entry.getKey(); var mesh = entry.getValue(); - renderLists.add(instancer, material, mesh); + indirectDrawManager.add(instancer, material, mesh); return; // TODO: support multiple meshes per model } diff --git a/src/main/java/com/jozufozu/flywheel/core/model/BlockMesh.java b/src/main/java/com/jozufozu/flywheel/core/model/BlockMesh.java deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/main/java/com/jozufozu/flywheel/core/structs/oriented/OrientedStorageWriter.java b/src/main/java/com/jozufozu/flywheel/core/structs/oriented/OrientedStorageWriter.java index ffea61b53..17183e8cc 100644 --- a/src/main/java/com/jozufozu/flywheel/core/structs/oriented/OrientedStorageWriter.java +++ b/src/main/java/com/jozufozu/flywheel/core/structs/oriented/OrientedStorageWriter.java @@ -23,21 +23,21 @@ public class OrientedStorageWriter implements StorageBufferWriter MemoryUtil.memPutFloat(ptr + 20, d.posY); MemoryUtil.memPutFloat(ptr + 24, d.posZ); - MemoryUtil.memPutFloat(ptr + 32, d.pivotX); - MemoryUtil.memPutFloat(ptr + 36, d.pivotY); - MemoryUtil.memPutFloat(ptr + 40, d.pivotZ); + MemoryUtil.memPutFloat(ptr + 28, d.pivotX); + MemoryUtil.memPutFloat(ptr + 32, d.pivotY); + MemoryUtil.memPutFloat(ptr + 36, d.pivotZ); - MemoryUtil.memPutShort(ptr + 44, d.skyLight); - MemoryUtil.memPutShort(ptr + 46, d.blockLight); + MemoryUtil.memPutShort(ptr + 40, d.skyLight); + MemoryUtil.memPutShort(ptr + 42, d.blockLight); - MemoryUtil.memPutByte(ptr + 48, d.r); - MemoryUtil.memPutByte(ptr + 49, d.g); - MemoryUtil.memPutByte(ptr + 50, d.b); - MemoryUtil.memPutByte(ptr + 51, d.a); + MemoryUtil.memPutByte(ptr + 44, d.r); + MemoryUtil.memPutByte(ptr + 45, d.g); + MemoryUtil.memPutByte(ptr + 46, d.b); + MemoryUtil.memPutByte(ptr + 47, d.a); } @Override public int getAlignment() { - return 64; + return 48; } } diff --git a/src/main/java/com/jozufozu/flywheel/core/structs/transformed/TransformedStorageWriter.java b/src/main/java/com/jozufozu/flywheel/core/structs/transformed/TransformedStorageWriter.java index 1f05a3e1d..23894b7ad 100644 --- a/src/main/java/com/jozufozu/flywheel/core/structs/transformed/TransformedStorageWriter.java +++ b/src/main/java/com/jozufozu/flywheel/core/structs/transformed/TransformedStorageWriter.java @@ -16,16 +16,16 @@ public class TransformedStorageWriter implements StorageBufferWriter> 16) & 0xFFFFu), float(i.light & 0xFFFFu)) / 15.0; } -#endif + #endif diff --git a/src/main/resources/assets/flywheel/flywheel/instance/transformed_indirect.glsl b/src/main/resources/assets/flywheel/flywheel/instance/transformed_indirect.glsl index 0fbf19511..298a5d5f4 100644 --- a/src/main/resources/assets/flywheel/flywheel/instance/transformed_indirect.glsl +++ b/src/main/resources/assets/flywheel/flywheel/instance/transformed_indirect.glsl @@ -1,22 +1,23 @@ #use "flywheel:api/vertex.glsl" +#use "flywheel:util/types.glsl" #define FLW_INSTANCE_STRUCT Instance struct Instance { - mat4 pose; - mat3 normal; + Mat4F pose; + Mat3F normal; uint color; uint light; }; void flw_transformBoundingSphere(in Instance i, inout vec3 center, inout float radius) { - center = (i.pose * vec4(center, 1.0)).xyz; + center = (unpackMat4F(i.pose) * vec4(center, 1.0)).xyz; } #ifdef VERTEX_SHADER void flw_instanceVertex(Instance i) { - flw_vertexPos = i.pose * flw_vertexPos; - flw_vertexNormal = i.normal * flw_vertexNormal; + flw_vertexPos = unpackMat4F(i.pose) * flw_vertexPos; + flw_vertexNormal = unpackMat3F(i.normal) * flw_vertexNormal; flw_vertexColor = unpackUnorm4x8(i.color); flw_vertexLight = vec2(float((i.light >> 16) & 0xFFFFu), float(i.light & 0xFFFFu)) / 15.0; } -#endif + #endif diff --git a/src/main/resources/assets/flywheel/flywheel/util/types.glsl b/src/main/resources/assets/flywheel/flywheel/util/types.glsl index a6f58174b..3a93dbda4 100644 --- a/src/main/resources/assets/flywheel/flywheel/util/types.glsl +++ b/src/main/resources/assets/flywheel/flywheel/util/types.glsl @@ -1,3 +1,4 @@ +// Types intended for use is SSBOs to achieve tighter data packing. struct Vec3F { float x; @@ -5,13 +6,58 @@ struct Vec3F { float z; }; +struct Vec4F { + float x; + float y; + float z; + float w; +}; + +struct Mat4F { + Vec4F c0; + Vec4F c1; + Vec4F c2; + Vec4F c3; +}; + +struct Mat3F { + Vec3F c0; + Vec3F c1; + Vec3F c2; +}; + // 4-aligned instead of a 16-aligned vec4 struct BoundingSphere { Vec3F center; float radius; }; +vec3 unpackVec3F(in Vec3F v) { + return vec3(v.x, v.y, v.z); +} + +vec4 unpackVec4F(in Vec4F v) { + return vec4(v.x, v.y, v.z, v.w); +} + +mat4 unpackMat4F(in Mat4F m) { + return mat4( + unpackVec4F(m.c0), + unpackVec4F(m.c1), + unpackVec4F(m.c2), + unpackVec4F(m.c3) + ); +} + +mat3 unpackMat3F(in Mat3F m) { + return mat3( + unpackVec3F(m.c0), + unpackVec3F(m.c1), + unpackVec3F(m.c2) + ); +} + void unpackBoundingSphere(in BoundingSphere sphere, out vec3 center, out float radius) { - center = vec3(sphere.center.x, sphere.center.y, sphere.center.z); + center = unpackVec3F(sphere.center); radius = sphere.radius; }