mirror of
https://github.com/Jozufozu/Flywheel.git
synced 2024-12-25 06:26:56 +01:00
Indirectly packed
- Use glsl structs to pack vec/mat data in ssbos - Support RenderStages - Dynamically sized draw storage
This commit is contained in:
parent
9b3c4d4992
commit
564b0996f9
17 changed files with 471 additions and 306 deletions
|
@ -4,7 +4,7 @@ root = true
|
||||||
[*]
|
[*]
|
||||||
indent_style = space
|
indent_style = space
|
||||||
indent_size = 4
|
indent_size = 4
|
||||||
continuation_indent_size = 8
|
ij_continuation_indent_size = 8
|
||||||
end_of_line = lf
|
end_of_line = lf
|
||||||
charset = utf-8
|
charset = utf-8
|
||||||
trim_trailing_whitespace = true
|
trim_trailing_whitespace = true
|
||||||
|
@ -16,6 +16,10 @@ indent_size = 2
|
||||||
|
|
||||||
[*.java]
|
[*.java]
|
||||||
indent_style = tab
|
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_java_else_on_new_line = false
|
||||||
ij_continuation_indent_size = 8
|
ij_continuation_indent_size = 8
|
||||||
ij_java_class_count_to_use_import_on_demand = 99
|
ij_java_class_count_to_use_import_on_demand = 99
|
||||||
|
|
|
@ -1,6 +1,22 @@
|
||||||
package com.jozufozu.flywheel.backend.instancing.indirect;
|
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.MemoryUtil;
|
||||||
import org.lwjgl.system.Pointer;
|
import org.lwjgl.system.Pointer;
|
||||||
|
@ -135,10 +151,23 @@ public class IndirectBuffers {
|
||||||
|
|
||||||
void createDrawStorage(int drawCount) {
|
void createDrawStorage(int drawCount) {
|
||||||
freeDrawStorage();
|
freeDrawStorage();
|
||||||
|
|
||||||
var drawSize = DRAW_COMMAND_STRIDE * drawCount;
|
var drawSize = DRAW_COMMAND_STRIDE * drawCount;
|
||||||
glNamedBufferStorage(draw, drawSize, SUB_DATA_BITS);
|
if (maxDrawCount > 0) {
|
||||||
drawPtr = MemoryUtil.nmemAlloc(drawSize);
|
int drawNew = glGenBuffers();
|
||||||
// drawPtr = nglMapNamedBufferRange(draw, 0, drawSize, MAP_BITS);
|
|
||||||
|
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;
|
maxDrawCount = drawCount;
|
||||||
FlwMemoryTracker._allocGPUMemory(maxDrawCount * DRAW_COMMAND_STRIDE);
|
FlwMemoryTracker._allocGPUMemory(maxDrawCount * DRAW_COMMAND_STRIDE);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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<T extends InstancedPart> {
|
||||||
|
|
||||||
|
private static final int BARRIER_BITS = GL_SHADER_STORAGE_BARRIER_BIT | GL_COMMAND_BARRIER_BIT;
|
||||||
|
|
||||||
|
final StorageBufferWriter<T> 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<T> drawSet = new IndirectDrawSet<>();
|
||||||
|
|
||||||
|
private boolean hasCulledThisFrame;
|
||||||
|
private boolean needsMemoryBarrier;
|
||||||
|
private int instanceCountThisFrame;
|
||||||
|
|
||||||
|
IndirectCullingGroup(StructType<T> 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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<T extends InstancedPart> {
|
||||||
|
final IndirectInstancer<T> instancer;
|
||||||
|
final IndirectMeshPool.BufferedMesh mesh;
|
||||||
|
final Material material;
|
||||||
|
int baseInstance = -1;
|
||||||
|
|
||||||
|
boolean needsFullWrite = true;
|
||||||
|
|
||||||
|
IndirectDraw(IndirectInstancer<T> 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
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,15 +10,14 @@ import com.jozufozu.flywheel.api.vertex.VertexType;
|
||||||
import com.jozufozu.flywheel.core.model.Mesh;
|
import com.jozufozu.flywheel.core.model.Mesh;
|
||||||
import com.jozufozu.flywheel.util.Pair;
|
import com.jozufozu.flywheel.util.Pair;
|
||||||
|
|
||||||
public class RenderLists {
|
public class IndirectDrawManager {
|
||||||
|
|
||||||
public final Map<Pair<StructType<?>, VertexType>, IndirectList<?>> lists = new HashMap<>();
|
public final Map<Pair<StructType<?>, VertexType>, IndirectCullingGroup<?>> lists = new HashMap<>();
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public <D extends InstancedPart> void add(IndirectInstancer<D> instancer, Material material, Mesh mesh) {
|
public <D extends InstancedPart> void add(IndirectInstancer<D> instancer, Material material, Mesh mesh) {
|
||||||
var indirectList = (IndirectList<D>) lists.computeIfAbsent(Pair.of(instancer.structType, mesh.getVertexType()),
|
var indirectList = (IndirectCullingGroup<D>) lists.computeIfAbsent(Pair.of(instancer.type, mesh.getVertexType()), p -> new IndirectCullingGroup<>(p.first(), p.second()));
|
||||||
p -> new IndirectList<>(p.first(), p.second()));
|
|
||||||
|
|
||||||
indirectList.add(instancer, material, mesh);
|
indirectList.drawSet.add(instancer, material, indirectList.meshPool.alloc(mesh));
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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<T extends InstancedPart> {
|
||||||
|
|
||||||
|
final List<IndirectDraw<T>> indirectDraws = new ArrayList<>();
|
||||||
|
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return indirectDraws.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int size() {
|
||||||
|
return indirectDraws.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(IndirectInstancer<T> 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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,6 +9,7 @@ import org.jetbrains.annotations.NotNull;
|
||||||
import org.lwjgl.opengl.GL32;
|
import org.lwjgl.opengl.GL32;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.RenderStage;
|
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.instancer.InstancedPart;
|
||||||
import com.jozufozu.flywheel.api.struct.StructType;
|
import com.jozufozu.flywheel.api.struct.StructType;
|
||||||
import com.jozufozu.flywheel.backend.gl.GlTextureUnit;
|
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.InstanceManager;
|
||||||
import com.jozufozu.flywheel.backend.instancing.TaskEngine;
|
import com.jozufozu.flywheel.backend.instancing.TaskEngine;
|
||||||
import com.jozufozu.flywheel.core.RenderContext;
|
import com.jozufozu.flywheel.core.RenderContext;
|
||||||
import com.jozufozu.flywheel.api.context.ContextShader;
|
|
||||||
import com.jozufozu.flywheel.util.WeakHashSet;
|
import com.jozufozu.flywheel.util.WeakHashSet;
|
||||||
import com.mojang.blaze3d.systems.RenderSystem;
|
import com.mojang.blaze3d.systems.RenderSystem;
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ public class IndirectEngine implements Engine {
|
||||||
protected final Map<StructType<?>, IndirectFactory<?>> factories = new HashMap<>();
|
protected final Map<StructType<?>, IndirectFactory<?>> factories = new HashMap<>();
|
||||||
|
|
||||||
protected final List<IndirectModel<?>> uninitializedModels = new ArrayList<>();
|
protected final List<IndirectModel<?>> 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.
|
* The set of instance managers that are attached to this engine.
|
||||||
|
@ -65,16 +65,11 @@ public class IndirectEngine implements Engine {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void renderStage(TaskEngine taskEngine, RenderContext context, RenderStage stage) {
|
public void renderStage(TaskEngine taskEngine, RenderContext context, RenderStage stage) {
|
||||||
if (stage != RenderStage.AFTER_SOLID_TERRAIN) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setup();
|
setup();
|
||||||
|
|
||||||
for (IndirectList<?> list : renderLists.lists.values()) {
|
for (var list : indirectDrawManager.lists.values()) {
|
||||||
list.submit(stage);
|
list.submit(stage);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setup() {
|
private void setup() {
|
||||||
|
@ -93,7 +88,8 @@ public class IndirectEngine implements Engine {
|
||||||
factories.values()
|
factories.values()
|
||||||
.forEach(IndirectFactory::delete);
|
.forEach(IndirectFactory::delete);
|
||||||
|
|
||||||
renderLists.lists.values().forEach(IndirectList::delete);
|
indirectDrawManager.lists.values()
|
||||||
|
.forEach(IndirectCullingGroup::delete);
|
||||||
|
|
||||||
factories.clear();
|
factories.clear();
|
||||||
}
|
}
|
||||||
|
@ -126,9 +122,13 @@ public class IndirectEngine implements Engine {
|
||||||
@Override
|
@Override
|
||||||
public void beginFrame(TaskEngine taskEngine, RenderContext context) {
|
public void beginFrame(TaskEngine taskEngine, RenderContext context) {
|
||||||
for (var model : uninitializedModels) {
|
for (var model : uninitializedModels) {
|
||||||
model.init(renderLists);
|
model.init(indirectDrawManager);
|
||||||
}
|
}
|
||||||
uninitializedModels.clear();
|
uninitializedModels.clear();
|
||||||
|
|
||||||
|
for (IndirectCullingGroup<?> value : indirectDrawManager.lists.values()) {
|
||||||
|
value.beginFrame();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void shiftListeners(int cX, int cY, int cZ) {
|
private void shiftListeners(int cX, int cY, int cZ) {
|
||||||
|
@ -141,7 +141,7 @@ public class IndirectEngine implements Engine {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addDebugInfo(List<String> info) {
|
public void addDebugInfo(List<String> info) {
|
||||||
info.add("GL33 Instanced Arrays");
|
info.add("GL46 Indirect");
|
||||||
info.add("Origin: " + originCoordinate.getX() + ", " + originCoordinate.getY() + ", " + originCoordinate.getZ());
|
info.add("Origin: " + originCoordinate.getX() + ", " + originCoordinate.getY() + ", " + originCoordinate.getZ());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
package com.jozufozu.flywheel.backend.instancing.indirect;
|
package com.jozufozu.flywheel.backend.instancing.indirect;
|
||||||
|
|
||||||
|
import org.lwjgl.system.MemoryUtil;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.instancer.InstancedPart;
|
import com.jozufozu.flywheel.api.instancer.InstancedPart;
|
||||||
import com.jozufozu.flywheel.api.struct.StructType;
|
import com.jozufozu.flywheel.api.struct.StructType;
|
||||||
import com.jozufozu.flywheel.backend.instancing.AbstractInstancer;
|
import com.jozufozu.flywheel.backend.instancing.AbstractInstancer;
|
||||||
|
@ -8,7 +10,6 @@ import com.jozufozu.flywheel.core.layout.BufferLayout;
|
||||||
public class IndirectInstancer<D extends InstancedPart> extends AbstractInstancer<D> {
|
public class IndirectInstancer<D extends InstancedPart> extends AbstractInstancer<D> {
|
||||||
|
|
||||||
public final BufferLayout instanceFormat;
|
public final BufferLayout instanceFormat;
|
||||||
public final StructType<D> structType;
|
|
||||||
public final IndirectModel<D> parent;
|
public final IndirectModel<D> parent;
|
||||||
int instanceCount = 0;
|
int instanceCount = 0;
|
||||||
|
|
||||||
|
@ -18,7 +19,6 @@ public class IndirectInstancer<D extends InstancedPart> extends AbstractInstance
|
||||||
super(type);
|
super(type);
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
this.instanceFormat = type.getLayout();
|
this.instanceFormat = type.getLayout();
|
||||||
this.structType = type;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -40,6 +40,33 @@ public class IndirectInstancer<D extends InstancedPart> extends AbstractInstance
|
||||||
anyToRemove = false;
|
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
|
@Override
|
||||||
public void delete() {
|
public void delete() {
|
||||||
// noop
|
// noop
|
||||||
|
|
|
@ -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<T extends InstancedPart> {
|
|
||||||
|
|
||||||
final StorageBufferWriter<T> 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<Batch> batches = new ArrayList<>();
|
|
||||||
|
|
||||||
IndirectList(StructType<T> 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<T> 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<T> instancer;
|
|
||||||
final IndirectMeshPool.BufferedMesh mesh;
|
|
||||||
final Material material;
|
|
||||||
int baseInstance = -1;
|
|
||||||
|
|
||||||
boolean needsFullWrite = true;
|
|
||||||
|
|
||||||
private Batch(IndirectInstancer<T> 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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -14,12 +14,12 @@ public class IndirectModel<D extends InstancedPart> {
|
||||||
this.instancer = new IndirectInstancer<>(this, type);
|
this.instancer = new IndirectInstancer<>(this, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void init(RenderLists renderLists) {
|
public void init(IndirectDrawManager indirectDrawManager) {
|
||||||
var materialMeshMap = this.model.getMeshes();
|
var materialMeshMap = this.model.getMeshes();
|
||||||
for (var entry : materialMeshMap.entrySet()) {
|
for (var entry : materialMeshMap.entrySet()) {
|
||||||
var material = entry.getKey();
|
var material = entry.getKey();
|
||||||
var mesh = entry.getValue();
|
var mesh = entry.getValue();
|
||||||
renderLists.add(instancer, material, mesh);
|
indirectDrawManager.add(instancer, material, mesh);
|
||||||
|
|
||||||
return; // TODO: support multiple meshes per model
|
return; // TODO: support multiple meshes per model
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,21 +23,21 @@ public class OrientedStorageWriter implements StorageBufferWriter<OrientedPart>
|
||||||
MemoryUtil.memPutFloat(ptr + 20, d.posY);
|
MemoryUtil.memPutFloat(ptr + 20, d.posY);
|
||||||
MemoryUtil.memPutFloat(ptr + 24, d.posZ);
|
MemoryUtil.memPutFloat(ptr + 24, d.posZ);
|
||||||
|
|
||||||
MemoryUtil.memPutFloat(ptr + 32, d.pivotX);
|
MemoryUtil.memPutFloat(ptr + 28, d.pivotX);
|
||||||
MemoryUtil.memPutFloat(ptr + 36, d.pivotY);
|
MemoryUtil.memPutFloat(ptr + 32, d.pivotY);
|
||||||
MemoryUtil.memPutFloat(ptr + 40, d.pivotZ);
|
MemoryUtil.memPutFloat(ptr + 36, d.pivotZ);
|
||||||
|
|
||||||
MemoryUtil.memPutShort(ptr + 44, d.skyLight);
|
MemoryUtil.memPutShort(ptr + 40, d.skyLight);
|
||||||
MemoryUtil.memPutShort(ptr + 46, d.blockLight);
|
MemoryUtil.memPutShort(ptr + 42, d.blockLight);
|
||||||
|
|
||||||
MemoryUtil.memPutByte(ptr + 48, d.r);
|
MemoryUtil.memPutByte(ptr + 44, d.r);
|
||||||
MemoryUtil.memPutByte(ptr + 49, d.g);
|
MemoryUtil.memPutByte(ptr + 45, d.g);
|
||||||
MemoryUtil.memPutByte(ptr + 50, d.b);
|
MemoryUtil.memPutByte(ptr + 46, d.b);
|
||||||
MemoryUtil.memPutByte(ptr + 51, d.a);
|
MemoryUtil.memPutByte(ptr + 47, d.a);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getAlignment() {
|
public int getAlignment() {
|
||||||
return 64;
|
return 48;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,16 +16,16 @@ public class TransformedStorageWriter implements StorageBufferWriter<Transformed
|
||||||
public void write(long ptr, TransformedPart instance) {
|
public void write(long ptr, TransformedPart instance) {
|
||||||
MatrixExtension.writeUnsafe(instance.model, ptr);
|
MatrixExtension.writeUnsafe(instance.model, ptr);
|
||||||
MatrixExtension.writeUnsafe(instance.normal, ptr + 64);
|
MatrixExtension.writeUnsafe(instance.normal, ptr + 64);
|
||||||
MemoryUtil.memPutByte(ptr + 112, instance.r);
|
MemoryUtil.memPutByte(ptr + 100, instance.r);
|
||||||
MemoryUtil.memPutByte(ptr + 113, instance.g);
|
MemoryUtil.memPutByte(ptr + 101, instance.g);
|
||||||
MemoryUtil.memPutByte(ptr + 114, instance.b);
|
MemoryUtil.memPutByte(ptr + 102, instance.b);
|
||||||
MemoryUtil.memPutByte(ptr + 115, instance.a);
|
MemoryUtil.memPutByte(ptr + 103, instance.a);
|
||||||
MemoryUtil.memPutShort(ptr + 116, instance.skyLight);
|
MemoryUtil.memPutShort(ptr + 104, instance.skyLight);
|
||||||
MemoryUtil.memPutShort(ptr + 118, instance.blockLight);
|
MemoryUtil.memPutShort(ptr + 106, instance.blockLight);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getAlignment() {
|
public int getAlignment() {
|
||||||
return 128;
|
return 108;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,10 +8,7 @@ import com.jozufozu.flywheel.core.Components;
|
||||||
import com.jozufozu.flywheel.core.RenderContext;
|
import com.jozufozu.flywheel.core.RenderContext;
|
||||||
import com.jozufozu.flywheel.core.source.FileResolution;
|
import com.jozufozu.flywheel.core.source.FileResolution;
|
||||||
import com.jozufozu.flywheel.event.BeginFrameEvent;
|
import com.jozufozu.flywheel.event.BeginFrameEvent;
|
||||||
import com.jozufozu.flywheel.util.extension.MatrixExtension;
|
|
||||||
import com.jozufozu.flywheel.util.joml.FrustumIntersection;
|
|
||||||
|
|
||||||
import net.minecraft.client.multiplayer.ClientLevel;
|
|
||||||
import net.minecraft.core.Vec3i;
|
import net.minecraft.core.Vec3i;
|
||||||
import net.minecraft.world.phys.Vec3;
|
import net.minecraft.world.phys.Vec3;
|
||||||
import net.minecraftforge.common.MinecraftForge;
|
import net.minecraftforge.common.MinecraftForge;
|
||||||
|
|
|
@ -1,24 +1,32 @@
|
||||||
#use "flywheel:api/vertex.glsl"
|
#use "flywheel:api/vertex.glsl"
|
||||||
#use "flywheel:util/quaternion.glsl"
|
#use "flywheel:util/quaternion.glsl"
|
||||||
|
#use "flywheel:util/types.glsl"
|
||||||
|
|
||||||
#define FLW_INSTANCE_STRUCT Instance
|
#define FLW_INSTANCE_STRUCT Instance
|
||||||
struct Instance {
|
struct Instance {
|
||||||
vec4 rotation;
|
Vec4F rotation;
|
||||||
vec3 pos;
|
Vec3F pos;
|
||||||
vec3 pivot;
|
Vec3F pivot;
|
||||||
uint light;
|
uint light;
|
||||||
uint color;
|
uint color;
|
||||||
};
|
};
|
||||||
|
|
||||||
void flw_transformBoundingSphere(in Instance i, inout vec3 center, inout float radius) {
|
void flw_transformBoundingSphere(in Instance i, inout vec3 center, inout float radius) {
|
||||||
center = rotateVertexByQuat(center - i.pivot, i.rotation) + i.pivot + i.pos;
|
vec4 rotation = unpackVec4F(i.rotation);
|
||||||
|
vec3 pivot = unpackVec3F(i.pivot);
|
||||||
|
vec3 pos = unpackVec3F(i.pos);
|
||||||
|
|
||||||
|
center = rotateVertexByQuat(center - pivot, rotation) + pivot + pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef VERTEX_SHADER
|
#ifdef VERTEX_SHADER
|
||||||
void flw_instanceVertex(Instance i) {
|
void flw_instanceVertex(Instance i) {
|
||||||
flw_vertexPos = vec4(rotateVertexByQuat(flw_vertexPos.xyz - i.pivot, i.rotation) + i.pivot + i.pos, 1.0);
|
vec4 rotation = unpackVec4F(i.rotation);
|
||||||
flw_vertexNormal = rotateVertexByQuat(flw_vertexNormal, i.rotation);
|
vec3 pivot = unpackVec3F(i.pivot);
|
||||||
|
vec3 pos = unpackVec3F(i.pos);
|
||||||
|
flw_vertexPos = vec4(rotateVertexByQuat(flw_vertexPos.xyz - pivot, rotation) + pivot + pos, 1.0);
|
||||||
|
flw_vertexNormal = rotateVertexByQuat(flw_vertexNormal, rotation);
|
||||||
flw_vertexColor = unpackUnorm4x8(i.color);
|
flw_vertexColor = unpackUnorm4x8(i.color);
|
||||||
flw_vertexLight = vec2(float((i.light >> 16) & 0xFFFFu), float(i.light & 0xFFFFu)) / 15.0;
|
flw_vertexLight = vec2(float((i.light >> 16) & 0xFFFFu), float(i.light & 0xFFFFu)) / 15.0;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,22 +1,23 @@
|
||||||
#use "flywheel:api/vertex.glsl"
|
#use "flywheel:api/vertex.glsl"
|
||||||
|
#use "flywheel:util/types.glsl"
|
||||||
|
|
||||||
#define FLW_INSTANCE_STRUCT Instance
|
#define FLW_INSTANCE_STRUCT Instance
|
||||||
struct Instance {
|
struct Instance {
|
||||||
mat4 pose;
|
Mat4F pose;
|
||||||
mat3 normal;
|
Mat3F normal;
|
||||||
uint color;
|
uint color;
|
||||||
uint light;
|
uint light;
|
||||||
};
|
};
|
||||||
|
|
||||||
void flw_transformBoundingSphere(in Instance i, inout vec3 center, inout float radius) {
|
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
|
#ifdef VERTEX_SHADER
|
||||||
void flw_instanceVertex(Instance i) {
|
void flw_instanceVertex(Instance i) {
|
||||||
flw_vertexPos = i.pose * flw_vertexPos;
|
flw_vertexPos = unpackMat4F(i.pose) * flw_vertexPos;
|
||||||
flw_vertexNormal = i.normal * flw_vertexNormal;
|
flw_vertexNormal = unpackMat3F(i.normal) * flw_vertexNormal;
|
||||||
flw_vertexColor = unpackUnorm4x8(i.color);
|
flw_vertexColor = unpackUnorm4x8(i.color);
|
||||||
flw_vertexLight = vec2(float((i.light >> 16) & 0xFFFFu), float(i.light & 0xFFFFu)) / 15.0;
|
flw_vertexLight = vec2(float((i.light >> 16) & 0xFFFFu), float(i.light & 0xFFFFu)) / 15.0;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
// Types intended for use is SSBOs to achieve tighter data packing.
|
||||||
|
|
||||||
struct Vec3F {
|
struct Vec3F {
|
||||||
float x;
|
float x;
|
||||||
|
@ -5,13 +6,58 @@ struct Vec3F {
|
||||||
float z;
|
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
|
// 4-aligned instead of a 16-aligned vec4
|
||||||
struct BoundingSphere {
|
struct BoundingSphere {
|
||||||
Vec3F center;
|
Vec3F center;
|
||||||
float radius;
|
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) {
|
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;
|
radius = sphere.radius;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue