mirror of
https://github.com/Jozufozu/Flywheel.git
synced 2025-01-12 23:36:09 +01:00
Indirectly transformed
- Indirect rendering supports TransformedType - Use single object to manage all ssbos
This commit is contained in:
parent
e7339dc7ef
commit
56ded52193
17 changed files with 259 additions and 159 deletions
|
@ -9,7 +9,7 @@ minecraft_version = 1.18.2
|
|||
forge_version = 40.1.68
|
||||
|
||||
# build dependency versions
|
||||
forgegradle_version = 5.1.+
|
||||
forgegradle_version = 5.1.53
|
||||
mixingradle_version = 0.7-SNAPSHOT
|
||||
mixin_version = 0.8.5
|
||||
librarian_version = 1.+
|
||||
|
|
|
@ -38,6 +38,8 @@ public interface StructType<S extends InstancedPart> {
|
|||
|
||||
StorageBufferWriter<S> getStorageBufferWriter();
|
||||
|
||||
FileResolution getIndirectShader();
|
||||
|
||||
public interface VertexTransformer<S extends InstancedPart> {
|
||||
void transform(MutableVertexList vertexList, S struct, ClientLevel level);
|
||||
}
|
||||
|
|
|
@ -54,8 +54,7 @@ public class Backend {
|
|||
}
|
||||
|
||||
public static void refresh() {
|
||||
// TODO: Revert when done testing
|
||||
TYPE = BackendType.INDIRECT; // chooseEngine();
|
||||
TYPE = chooseEngine();
|
||||
}
|
||||
|
||||
public static boolean isOn() {
|
||||
|
|
|
@ -39,7 +39,7 @@ public class GlCompat {
|
|||
instancedArrays = getLatest(InstancedArrays.class, caps);
|
||||
bufferStorage = getLatest(BufferStorage.class, caps);
|
||||
|
||||
supportsIndirect = caps.OpenGL46;
|
||||
supportsIndirect = true;
|
||||
|
||||
amd = _isAmdWindows();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,134 @@
|
|||
package com.jozufozu.flywheel.backend.instancing.indirect;
|
||||
|
||||
import static org.lwjgl.opengl.GL46.*;
|
||||
|
||||
import org.lwjgl.system.MemoryUtil;
|
||||
import org.lwjgl.system.Pointer;
|
||||
|
||||
public class IndirectBuffers {
|
||||
public static final int BUFFER_COUNT = 4;
|
||||
public static final long INT_SIZE = Integer.BYTES;
|
||||
public static final long PTR_SIZE = Pointer.POINTER_SIZE;
|
||||
|
||||
// DRAW COMMAND
|
||||
public static final long DRAW_COMMAND_STRIDE = 36;
|
||||
public static final long DRAW_COMMAND_OFFSET = 0;
|
||||
|
||||
// BITS
|
||||
private static final int SUB_DATA_BITS = GL_DYNAMIC_STORAGE_BIT;
|
||||
private static final int PERSISTENT_BITS = GL_MAP_PERSISTENT_BIT | GL_MAP_WRITE_BIT;
|
||||
private static final int MAP_BITS = PERSISTENT_BITS | GL_MAP_FLUSH_EXPLICIT_BIT;
|
||||
private static final int GPU_ONLY_BITS = 0;
|
||||
|
||||
// OFFSETS
|
||||
private static final long OFFSET_OFFSET = BUFFER_COUNT * INT_SIZE;
|
||||
private static final long SIZE_OFFSET = OFFSET_OFFSET + BUFFER_COUNT * PTR_SIZE;
|
||||
private static final long BUFFERS_SIZE_BYTES = SIZE_OFFSET + BUFFER_COUNT * PTR_SIZE;
|
||||
|
||||
private static final long OBJECT_SIZE_OFFSET = SIZE_OFFSET;
|
||||
private static final long TARGET_SIZE_OFFSET = OBJECT_SIZE_OFFSET + PTR_SIZE;
|
||||
private static final long BATCH_SIZE_OFFSET = TARGET_SIZE_OFFSET + PTR_SIZE;
|
||||
private static final long DRAW_SIZE_OFFSET = BATCH_SIZE_OFFSET + PTR_SIZE;
|
||||
|
||||
final long buffers;
|
||||
final long objectStride;
|
||||
int object;
|
||||
int target;
|
||||
int batch;
|
||||
int draw;
|
||||
|
||||
long objectPtr;
|
||||
long batchPtr;
|
||||
long drawPtr;
|
||||
|
||||
int maxObjectCount;
|
||||
int maxDrawCount;
|
||||
|
||||
IndirectBuffers(long objectStride) {
|
||||
this.objectStride = objectStride;
|
||||
this.buffers = MemoryUtil.nmemAlloc(BUFFERS_SIZE_BYTES);
|
||||
|
||||
if (this.buffers == MemoryUtil.NULL) {
|
||||
throw new OutOfMemoryError();
|
||||
}
|
||||
|
||||
MemoryUtil.memSet(this.buffers, 0, BUFFERS_SIZE_BYTES);
|
||||
}
|
||||
|
||||
void createBuffers() {
|
||||
nglCreateBuffers(4, buffers);
|
||||
object = MemoryUtil.memGetInt(buffers);
|
||||
target = MemoryUtil.memGetInt(buffers + 4);
|
||||
batch = MemoryUtil.memGetInt(buffers + 8);
|
||||
draw = MemoryUtil.memGetInt(buffers + 12);
|
||||
}
|
||||
|
||||
void updateCounts(int objectCount, int drawCount) {
|
||||
|
||||
if (objectCount > maxObjectCount) {
|
||||
createObjectStorage(objectCount);
|
||||
}
|
||||
if (drawCount > maxDrawCount) {
|
||||
createDrawStorage(drawCount);
|
||||
}
|
||||
|
||||
long objectSize = objectStride * objectCount;
|
||||
long targetSize = INT_SIZE * objectCount;
|
||||
long drawSize = DRAW_COMMAND_STRIDE * drawCount;
|
||||
|
||||
MemoryUtil.memPutAddress(buffers + OBJECT_SIZE_OFFSET, objectSize);
|
||||
MemoryUtil.memPutAddress(buffers + TARGET_SIZE_OFFSET, targetSize);
|
||||
MemoryUtil.memPutAddress(buffers + BATCH_SIZE_OFFSET, targetSize);
|
||||
MemoryUtil.memPutAddress(buffers + DRAW_SIZE_OFFSET, drawSize);
|
||||
}
|
||||
|
||||
void createObjectStorage(int objectCount) {
|
||||
var objectSize = objectStride * objectCount;
|
||||
var targetSize = INT_SIZE * objectCount;
|
||||
|
||||
glNamedBufferStorage(object, objectSize, PERSISTENT_BITS);
|
||||
glNamedBufferStorage(target, targetSize, GPU_ONLY_BITS);
|
||||
glNamedBufferStorage(batch, targetSize, PERSISTENT_BITS);
|
||||
|
||||
objectPtr = nglMapNamedBufferRange(object, 0, objectSize, MAP_BITS);
|
||||
batchPtr = nglMapNamedBufferRange(batch, 0, targetSize, MAP_BITS);
|
||||
maxObjectCount = objectCount;
|
||||
}
|
||||
|
||||
void createDrawStorage(int drawCount) {
|
||||
var drawSize = DRAW_COMMAND_STRIDE * drawCount;
|
||||
glNamedBufferStorage(draw, drawSize, SUB_DATA_BITS);
|
||||
drawPtr = MemoryUtil.nmemAlloc(drawSize);
|
||||
// drawPtr = nglMapNamedBufferRange(draw, 0, drawSize, MAP_BITS);
|
||||
maxDrawCount = drawCount;
|
||||
}
|
||||
|
||||
public void bindAll() {
|
||||
bindN(BUFFER_COUNT);
|
||||
}
|
||||
|
||||
public void bindObjectAndTarget() {
|
||||
bindN(2);
|
||||
}
|
||||
|
||||
private void bindN(int bufferCount) {
|
||||
nglBindBuffersRange(GL_SHADER_STORAGE_BUFFER, 0, bufferCount, buffers, buffers + OFFSET_OFFSET, buffers + SIZE_OFFSET);
|
||||
}
|
||||
|
||||
void bindIndirectBuffer() {
|
||||
glBindBuffer(GL_DRAW_INDIRECT_BUFFER, draw);
|
||||
}
|
||||
|
||||
void flushBatchIDs(long length) {
|
||||
glFlushMappedNamedBufferRange(batch, 0, length);
|
||||
}
|
||||
|
||||
void flushObjects(long length) {
|
||||
glFlushMappedNamedBufferRange(object, 0, length);
|
||||
}
|
||||
|
||||
void flushDrawCommands(long length) {
|
||||
nglNamedBufferSubData(draw, 0, length, drawPtr);
|
||||
// glFlushMappedNamedBufferRange(this.draw, 0, length);
|
||||
}
|
||||
}
|
|
@ -70,12 +70,14 @@ public class IndirectEngine implements Engine {
|
|||
|
||||
@Override
|
||||
public void renderStage(TaskEngine taskEngine, RenderContext context, RenderStage stage) {
|
||||
var groups = renderLists.get(stage);
|
||||
if (stage != RenderStage.AFTER_SOLID_TERRAIN) {
|
||||
return;
|
||||
}
|
||||
|
||||
setup();
|
||||
|
||||
for (var group : groups) {
|
||||
group.submit();
|
||||
for (IndirectList<?> list : renderLists.lists.values()) {
|
||||
list.submit(stage);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -41,30 +41,6 @@ public class IndirectInstancer<D extends InstancedPart> extends AbstractInstance
|
|||
anyToRemove = false;
|
||||
}
|
||||
|
||||
void writeAll(final StructWriter<D> writer) {
|
||||
anyToUpdate = false;
|
||||
|
||||
for (var instance : data) {
|
||||
writer.write(instance);
|
||||
}
|
||||
}
|
||||
|
||||
void writeChanged(final StructWriter<D> writer) {
|
||||
if (!anyToUpdate) {
|
||||
return;
|
||||
}
|
||||
|
||||
anyToUpdate = false;
|
||||
|
||||
final int size = data.size();
|
||||
|
||||
if (size == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
writeChangedUnchecked(writer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete() {
|
||||
// noop
|
||||
|
|
|
@ -5,12 +5,15 @@ import static org.lwjgl.opengl.GL46.*;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.lwjgl.system.MemoryStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
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;
|
||||
|
@ -18,76 +21,37 @@ 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;
|
||||
import com.jozufozu.flywheel.core.vertex.Formats;
|
||||
|
||||
public class IndirectList<T extends InstancedPart> {
|
||||
|
||||
private static final long DRAW_COMMAND_STRIDE = 36;
|
||||
private static final long DRAW_COMMAND_OFFSET = 0;
|
||||
|
||||
final StorageBufferWriter<T> storageBufferWriter;
|
||||
final GlProgram compute;
|
||||
final GlProgram draw;
|
||||
private final StructType<T> type;
|
||||
private final long maxObjectCount;
|
||||
private final StructType<T> structType;
|
||||
private final VertexType vertexType;
|
||||
private final long objectStride;
|
||||
private final int maxBatchCount;
|
||||
private final long objectClientStorage;
|
||||
private final long batchIDClientStorage;
|
||||
private final int elementBuffer;
|
||||
|
||||
/**
|
||||
* Stores raw instance data per-object.
|
||||
*/
|
||||
int objectBuffer;
|
||||
int targetBuffer;
|
||||
int batchBuffer;
|
||||
|
||||
/**
|
||||
* Stores drawIndirect structs.
|
||||
*/
|
||||
int drawBuffer;
|
||||
int debugBuffer;
|
||||
final IndirectBuffers buffers;
|
||||
|
||||
final IndirectMeshPool meshPool;
|
||||
private final int elementBuffer;
|
||||
|
||||
int vertexArray;
|
||||
|
||||
final int[] shaderStorageBuffers = new int[5];
|
||||
|
||||
final List<Batch<T>> batches = new ArrayList<>();
|
||||
|
||||
IndirectList(StructType<T> structType) {
|
||||
type = structType;
|
||||
storageBufferWriter = type.getStorageBufferWriter();
|
||||
|
||||
if (storageBufferWriter == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
|
||||
glCreateBuffers(shaderStorageBuffers);
|
||||
objectBuffer = shaderStorageBuffers[0];
|
||||
targetBuffer = shaderStorageBuffers[1];
|
||||
batchBuffer = shaderStorageBuffers[2];
|
||||
drawBuffer = shaderStorageBuffers[3];
|
||||
debugBuffer = shaderStorageBuffers[4];
|
||||
meshPool = new IndirectMeshPool(Formats.BLOCK, 1024);
|
||||
|
||||
// FIXME: Resizable buffers
|
||||
maxObjectCount = 64 * 64 * 64;
|
||||
maxBatchCount = 64;
|
||||
IndirectList(StructType<T> structType, VertexType vertexType) {
|
||||
this.structType = structType;
|
||||
this.vertexType = vertexType;
|
||||
storageBufferWriter = this.structType.getStorageBufferWriter();
|
||||
|
||||
objectStride = storageBufferWriter.getAlignment();
|
||||
int persistentBits = GL_MAP_PERSISTENT_BIT | GL_MAP_WRITE_BIT;
|
||||
glNamedBufferStorage(objectBuffer, objectStride * maxObjectCount, persistentBits);
|
||||
glNamedBufferStorage(targetBuffer, 4 * maxObjectCount, 0);
|
||||
glNamedBufferStorage(batchBuffer, 4 * maxObjectCount, persistentBits);
|
||||
glNamedBufferStorage(drawBuffer, DRAW_COMMAND_STRIDE * maxBatchCount, GL_DYNAMIC_STORAGE_BIT);
|
||||
glNamedBufferStorage(debugBuffer, 4 * maxObjectCount, 0);
|
||||
buffers = new IndirectBuffers(objectStride);
|
||||
buffers.createBuffers();
|
||||
buffers.createObjectStorage(64 * 64 * 64);
|
||||
buffers.createDrawStorage(64);
|
||||
|
||||
int mapBits = persistentBits | GL_MAP_FLUSH_EXPLICIT_BIT;
|
||||
objectClientStorage = nglMapNamedBufferRange(objectBuffer, 0, objectStride * maxObjectCount, mapBits);
|
||||
batchIDClientStorage = nglMapNamedBufferRange(batchBuffer, 0, 4 * maxObjectCount, mapBits);
|
||||
meshPool = new IndirectMeshPool(vertexType, 1024);
|
||||
|
||||
vertexArray = glCreateVertexArrays();
|
||||
|
||||
|
@ -95,14 +59,15 @@ public class IndirectList<T extends InstancedPart> {
|
|||
.quads2Tris(2048).buffer.handle();
|
||||
setupVertexArray();
|
||||
|
||||
compute = ComputeCullerCompiler.INSTANCE.get(Components.Files.ORIENTED_INDIRECT);
|
||||
draw = PipelineCompiler.INSTANCE.get(new PipelineCompiler.Context(Formats.BLOCK, Materials.BELL, Components.Files.ORIENTED_INDIRECT, Components.WORLD, Components.INDIRECT));
|
||||
var indirectShader = this.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 = Formats.BLOCK.getLayout();
|
||||
var meshLayout = vertexType.getLayout();
|
||||
var meshAttribs = meshLayout.getAttributeCount();
|
||||
|
||||
var attributes = meshLayout.getAttributes();
|
||||
|
@ -118,17 +83,23 @@ public class IndirectList<T extends InstancedPart> {
|
|||
}
|
||||
}
|
||||
|
||||
public void add(Mesh mesh, IndirectInstancer<T> instancer) {
|
||||
batches.add(new Batch<>(instancer, meshPool.alloc(mesh)));
|
||||
public void add(IndirectInstancer<T> instancer, Material material, Mesh mesh) {
|
||||
batches.add(new Batch<>(instancer, material, meshPool.alloc(mesh)));
|
||||
}
|
||||
|
||||
void submit() {
|
||||
void submit(RenderStage stage) {
|
||||
if (batches.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
int instanceCountThisFrame = calculateTotalInstanceCount();
|
||||
|
||||
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();
|
||||
|
@ -136,39 +107,38 @@ public class IndirectList<T extends InstancedPart> {
|
|||
UniformBuffer.getInstance().sync();
|
||||
|
||||
dispatchCompute(instanceCountThisFrame);
|
||||
issueMemoryBarrier();
|
||||
glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT);
|
||||
dispatchDraw();
|
||||
}
|
||||
|
||||
private void dispatchDraw() {
|
||||
draw.bind();
|
||||
Materials.BELL.setup();
|
||||
glVertexArrayElementBuffer(vertexArray, elementBuffer);
|
||||
glBindVertexArray(vertexArray);
|
||||
glBindBuffer(GL_DRAW_INDIRECT_BUFFER, drawBuffer);
|
||||
glMultiDrawElementsIndirect(GL_TRIANGLES, GL_UNSIGNED_INT, DRAW_COMMAND_OFFSET, batches.size(), (int) DRAW_COMMAND_STRIDE);
|
||||
Materials.BELL.clear();
|
||||
}
|
||||
buffers.bindIndirectBuffer();
|
||||
|
||||
private static void issueMemoryBarrier() {
|
||||
glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT);
|
||||
final int stride = (int) IndirectBuffers.DRAW_COMMAND_STRIDE;
|
||||
long offset = 0;
|
||||
for (Batch<T> 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();
|
||||
glBindBufferRange(GL_SHADER_STORAGE_BUFFER, 0, objectBuffer, 0, instanceCount * objectStride);
|
||||
glBindBufferRange(GL_SHADER_STORAGE_BUFFER, 1, targetBuffer, 0, instanceCount * 4L);
|
||||
glBindBufferRange(GL_SHADER_STORAGE_BUFFER, 2, batchBuffer, 0, instanceCount * 4L);
|
||||
glBindBufferRange(GL_SHADER_STORAGE_BUFFER, 3, drawBuffer, 0, batches.size() * DRAW_COMMAND_STRIDE);
|
||||
glBindBufferRange(GL_SHADER_STORAGE_BUFFER, 4, debugBuffer, 0, instanceCount * 4L);
|
||||
buffers.bindAll();
|
||||
|
||||
var groupCount = (instanceCount + 31) >> 5; // ceil(totalInstanceCount / 32)
|
||||
var groupCount = (instanceCount + 31) >> 5; // ceil(instanceCount / 32)
|
||||
glDispatchCompute(groupCount, 1, 1);
|
||||
}
|
||||
|
||||
private void uploadInstanceData() {
|
||||
long objectPtr = objectClientStorage;
|
||||
long batchIDPtr = batchIDClientStorage;
|
||||
long objectPtr = buffers.objectPtr;
|
||||
long batchIDPtr = buffers.batchPtr;
|
||||
int baseInstance = 0;
|
||||
int batchID = 0;
|
||||
for (var batch : batches) {
|
||||
|
@ -181,27 +151,23 @@ public class IndirectList<T extends InstancedPart> {
|
|||
|
||||
// write batchID
|
||||
MemoryUtil.memPutInt(batchIDPtr, batchID);
|
||||
batchIDPtr += 4;
|
||||
batchIDPtr += IndirectBuffers.INT_SIZE;
|
||||
}
|
||||
baseInstance += batch.instancer.instanceCount;
|
||||
batchID++;
|
||||
}
|
||||
|
||||
glFlushMappedNamedBufferRange(objectBuffer, 0, objectPtr - objectClientStorage);
|
||||
glFlushMappedNamedBufferRange(batchBuffer, 0, batchIDPtr - batchIDClientStorage);
|
||||
buffers.flushObjects(objectPtr - buffers.objectPtr);
|
||||
buffers.flushBatchIDs(batchIDPtr - buffers.batchPtr);
|
||||
}
|
||||
|
||||
private void uploadIndirectCommands() {
|
||||
try (var stack = MemoryStack.stackPush()) {
|
||||
long size = batches.size() * DRAW_COMMAND_STRIDE;
|
||||
long basePtr = stack.nmalloc((int) size);
|
||||
long writePtr = basePtr;
|
||||
for (Batch<T> batch : batches) {
|
||||
batch.writeIndirectCommand(writePtr);
|
||||
writePtr += DRAW_COMMAND_STRIDE;
|
||||
}
|
||||
nglNamedBufferSubData(drawBuffer, 0, size, basePtr);
|
||||
long writePtr = buffers.drawPtr;
|
||||
for (Batch<T> batch : batches) {
|
||||
batch.writeIndirectCommand(writePtr);
|
||||
writePtr += IndirectBuffers.DRAW_COMMAND_STRIDE;
|
||||
}
|
||||
buffers.flushDrawCommands(writePtr - buffers.drawPtr);
|
||||
}
|
||||
|
||||
private int calculateTotalInstanceCount() {
|
||||
|
@ -216,10 +182,12 @@ public class IndirectList<T extends InstancedPart> {
|
|||
private static final class Batch<T extends InstancedPart> {
|
||||
final IndirectInstancer<T> instancer;
|
||||
final IndirectMeshPool.BufferedMesh mesh;
|
||||
int baseInstance;
|
||||
final Material material;
|
||||
int baseInstance = -1;
|
||||
|
||||
private Batch(IndirectInstancer<T> instancer, IndirectMeshPool.BufferedMesh mesh) {
|
||||
private Batch(IndirectInstancer<T> instancer, Material material, IndirectMeshPool.BufferedMesh mesh) {
|
||||
this.instancer = instancer;
|
||||
this.material = material;
|
||||
this.mesh = mesh;
|
||||
}
|
||||
|
||||
|
|
|
@ -77,6 +77,8 @@ public class IndirectMeshPool {
|
|||
baseVertex += model.mesh.getVertexCount();
|
||||
}
|
||||
|
||||
clientStorage.rewind();
|
||||
|
||||
glNamedBufferSubData(vbo, 0, clientStorage);
|
||||
}
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ public class InstancedModel<D extends InstancedPart> {
|
|||
for (var entry : materialMeshMap.entrySet()) {
|
||||
var material = entry.getKey();
|
||||
var mesh = entry.getValue();
|
||||
renderLists.add(material.getRenderStage(), type, mesh, instancer);
|
||||
renderLists.add(instancer, material, mesh);
|
||||
|
||||
return; // TODO: support multiple meshes per model
|
||||
}
|
||||
|
|
|
@ -1,40 +1,24 @@
|
|||
package com.jozufozu.flywheel.backend.instancing.indirect;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumMap;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.common.collect.ArrayListMultimap;
|
||||
import com.google.common.collect.ImmutableListMultimap;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ListMultimap;
|
||||
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.StructType;
|
||||
import com.jozufozu.flywheel.api.vertex.VertexType;
|
||||
import com.jozufozu.flywheel.core.model.Mesh;
|
||||
import com.jozufozu.flywheel.util.Pair;
|
||||
|
||||
public class RenderLists {
|
||||
|
||||
public final Map<RenderStage, Map<StructType<?>, IndirectList<?>>> renderLists = new EnumMap<>(RenderStage.class);
|
||||
|
||||
public Collection<IndirectList<?>> get(RenderStage stage) {
|
||||
var renderList = renderLists.get(stage);
|
||||
if (renderList == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return renderList.values();
|
||||
}
|
||||
public final Map<Pair<StructType<?>, VertexType>, IndirectList<?>> lists = new HashMap<>();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <D extends InstancedPart> void add(RenderStage stage, StructType<D> type, Mesh mesh, IndirectInstancer<D> instancer) {
|
||||
var indirectList = (IndirectList<D>) renderLists.computeIfAbsent(stage, $ -> new HashMap<>())
|
||||
.computeIfAbsent(type, IndirectList::new);
|
||||
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()),
|
||||
p -> new IndirectList<>(p.first(), p.second()));
|
||||
|
||||
indirectList.add(mesh, instancer);
|
||||
indirectList.add(instancer, material, mesh);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
package com.jozufozu.flywheel.core.structs.model;
|
||||
|
||||
import org.lwjgl.system.MemoryUtil;
|
||||
|
||||
import com.jozufozu.flywheel.api.struct.StorageBufferWriter;
|
||||
import com.jozufozu.flywheel.util.extension.MatrixExtension;
|
||||
|
||||
public class TransformedStorageWriter implements StorageBufferWriter<TransformedPart> {
|
||||
|
||||
public static final TransformedStorageWriter INSTANCE = new TransformedStorageWriter();
|
||||
|
||||
private TransformedStorageWriter() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(long ptr, TransformedPart instance) {
|
||||
MatrixExtension.writeUnsafe(instance.model, ptr);
|
||||
MatrixExtension.writeUnsafe(instance.normal, ptr + 64);
|
||||
MemoryUtil.memPutByte(ptr + 112, instance.r);
|
||||
MemoryUtil.memPutByte(ptr + 113, instance.g);
|
||||
MemoryUtil.memPutByte(ptr + 114, instance.b);
|
||||
MemoryUtil.memPutByte(ptr + 115, instance.a);
|
||||
MemoryUtil.memPutShort(ptr + 116, instance.skyLight);
|
||||
MemoryUtil.memPutShort(ptr + 118, instance.blockLight);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAlignment() {
|
||||
return 128;
|
||||
}
|
||||
}
|
|
@ -36,7 +36,7 @@ public class TransformedType implements StructType<TransformedPart> {
|
|||
|
||||
@Override
|
||||
public StorageBufferWriter<TransformedPart> getStorageBufferWriter() {
|
||||
return null; // TODO
|
||||
return TransformedStorageWriter.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -44,6 +44,11 @@ public class TransformedType implements StructType<TransformedPart> {
|
|||
return Components.Files.TRANSFORMED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileResolution getIndirectShader() {
|
||||
return Components.Files.TRANSFORMED_INDIRECT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public VertexTransformer<? extends TransformedPart> getVertexTransformer() {
|
||||
return (vertexList, struct, level) -> {
|
||||
|
|
|
@ -47,6 +47,11 @@ public class OrientedType implements StructType<OrientedPart> {
|
|||
return Components.Files.ORIENTED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileResolution getIndirectShader() {
|
||||
return Components.Files.ORIENTED_INDIRECT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public VertexTransformer<? extends OrientedPart> getVertexTransformer() {
|
||||
return (vertexList, struct, level) -> {
|
||||
|
|
|
@ -2,6 +2,7 @@ package com.jozufozu.flywheel.util.extension;
|
|||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import com.mojang.math.Matrix3f;
|
||||
import com.mojang.math.Matrix4f;
|
||||
|
||||
/**
|
||||
|
@ -24,4 +25,8 @@ public interface MatrixExtension {
|
|||
static void writeUnsafe(Matrix4f matrix, long ptr) {
|
||||
((MatrixExtension) (Object) matrix).flywheel$writeUnsafe(ptr);
|
||||
}
|
||||
|
||||
static void writeUnsafe(Matrix3f matrix, long ptr) {
|
||||
((MatrixExtension) (Object) matrix).flywheel$writeUnsafe(ptr);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#use "flywheel:api/vertex.glsl"
|
||||
#define FLW_INSTANCE_STRUCT Instance
|
||||
|
||||
#define FLW_INSTANCE_STRUCT Instance
|
||||
struct Instance {
|
||||
mat4 pose;
|
||||
mat3 normal;
|
||||
|
|
|
@ -31,24 +31,11 @@ layout(std430, binding = 3) restrict buffer DrawCommands {
|
|||
MeshDrawCommand drawCommands[];
|
||||
};
|
||||
|
||||
layout(std430, binding = 4) restrict writeonly buffer DebugVisibility {
|
||||
uint objectVisibilityBits[];
|
||||
};
|
||||
|
||||
// 83 - 27 = 56 spirv instruction results
|
||||
bool testSphere(vec3 center, float radius) {
|
||||
bvec4 xyInside = greaterThanEqual(fma(flw_planes.xyX, center.xxxx, fma(flw_planes.xyY, center.yyyy, fma(flw_planes.xyZ, center.zzzz, flw_planes.xyW))), -radius.xxxx);
|
||||
bvec2 zInside = greaterThanEqual(fma(flw_planes.zX, center.xx, fma(flw_planes.zY, center.yy, fma(flw_planes.zZ, center.zz, flw_planes.zW))), -radius.xx);
|
||||
|
||||
uint debug = uint(xyInside.x);
|
||||
debug |= uint(xyInside.y) << 1;
|
||||
debug |= uint(xyInside.z) << 2;
|
||||
debug |= uint(xyInside.w) << 3;
|
||||
debug |= uint(zInside.x) << 4;
|
||||
debug |= uint(zInside.y) << 5;
|
||||
|
||||
objectVisibilityBits[flw_objectID] = debug;
|
||||
|
||||
return all(xyInside) && all(zInside);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue