mirror of
https://github.com/Jozufozu/Flywheel.git
synced 2024-12-27 07:26:48 +01:00
So many draws
- Sort draws in an IndirectDrawSet by RenderStage and Material - Loop through ahead of time and determine what individual draws can be grouped together into a MultiDraw - Contiguous segments with the same RenderStage and Material can be merged into one MultiDraw call - Add material ID lookup for sorting purposes
This commit is contained in:
parent
671e1a002c
commit
6b9b2a9927
5 changed files with 86 additions and 29 deletions
|
@ -2,7 +2,6 @@ package com.jozufozu.flywheel.backend.instancing;
|
|||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
|
|
@ -3,7 +3,13 @@ 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.*;
|
||||
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;
|
||||
|
@ -142,7 +148,8 @@ public class IndirectCullingGroup<T extends InstancedPart> {
|
|||
|
||||
for (int i = 0, batchesSize = drawSet.indirectDraws.size(); i < batchesSize; i++) {
|
||||
var batch = drawSet.indirectDraws.get(i);
|
||||
var instanceCount = batch.instancer.getInstanceCount();
|
||||
var instanceCount = batch.instancer()
|
||||
.getInstanceCount();
|
||||
batch.writeObjects(objectPtr, batchIDPtr, i);
|
||||
|
||||
objectPtr += instanceCount * objectStride;
|
||||
|
@ -166,7 +173,7 @@ public class IndirectCullingGroup<T extends InstancedPart> {
|
|||
int baseInstance = 0;
|
||||
for (var batch : drawSet.indirectDraws) {
|
||||
batch.prepare(baseInstance);
|
||||
baseInstance += batch.instancer.instanceCount;
|
||||
baseInstance += batch.instancer().instanceCount;
|
||||
}
|
||||
return baseInstance;
|
||||
}
|
||||
|
|
|
@ -8,10 +8,10 @@ import com.jozufozu.flywheel.api.material.Material;
|
|||
import com.jozufozu.flywheel.core.ComponentRegistry;
|
||||
|
||||
public final class IndirectDraw<T extends InstancedPart> {
|
||||
final IndirectInstancer<T> instancer;
|
||||
final IndirectMeshPool.BufferedMesh mesh;
|
||||
final Material material;
|
||||
final RenderStage stage;
|
||||
private final IndirectInstancer<T> instancer;
|
||||
private final IndirectMeshPool.BufferedMesh mesh;
|
||||
private final Material material;
|
||||
private final RenderStage stage;
|
||||
int baseInstance = -1;
|
||||
|
||||
final int vertexMaterialID;
|
||||
|
@ -62,4 +62,20 @@ public final class IndirectDraw<T extends InstancedPart> {
|
|||
MemoryUtil.memPutInt(ptr + 40, fragmentMaterialID); // fragmentMaterialID
|
||||
|
||||
}
|
||||
|
||||
public IndirectInstancer<T> instancer() {
|
||||
return instancer;
|
||||
}
|
||||
|
||||
public IndirectMeshPool.BufferedMesh mesh() {
|
||||
return mesh;
|
||||
}
|
||||
|
||||
public Material material() {
|
||||
return material;
|
||||
}
|
||||
|
||||
public RenderStage stage() {
|
||||
return stage;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,17 +5,23 @@ import static org.lwjgl.opengl.GL11.GL_UNSIGNED_INT;
|
|||
import static org.lwjgl.opengl.GL43.glMultiDrawElementsIndirect;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.EnumMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.jozufozu.flywheel.api.RenderStage;
|
||||
import com.jozufozu.flywheel.api.instancer.InstancedPart;
|
||||
import com.jozufozu.flywheel.api.material.Material;
|
||||
import com.jozufozu.flywheel.core.ComponentRegistry;
|
||||
import com.jozufozu.flywheel.util.Textures;
|
||||
|
||||
public class IndirectDrawSet<T extends InstancedPart> {
|
||||
|
||||
final List<IndirectDraw<T>> indirectDraws = new ArrayList<>();
|
||||
|
||||
final Map<RenderStage, List<MultiDraw>> multiDraws = new EnumMap<>(RenderStage.class);
|
||||
|
||||
public boolean isEmpty() {
|
||||
return indirectDraws.isEmpty();
|
||||
}
|
||||
|
@ -26,31 +32,52 @@ public class IndirectDrawSet<T extends InstancedPart> {
|
|||
|
||||
public void add(IndirectInstancer<T> instancer, Material material, RenderStage stage, IndirectMeshPool.BufferedMesh bufferedMesh) {
|
||||
indirectDraws.add(new IndirectDraw<>(instancer, material, stage, bufferedMesh));
|
||||
determineMultiDraws();
|
||||
}
|
||||
|
||||
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);
|
||||
if (batch.stage != stage) {
|
||||
continue;
|
||||
}
|
||||
if (!multiDraws.containsKey(stage)) {
|
||||
return;
|
||||
}
|
||||
|
||||
var material = batch.material;
|
||||
material.setup();
|
||||
Textures.bindActiveTextures();
|
||||
glMultiDrawElementsIndirect(GL_TRIANGLES, GL_UNSIGNED_INT, i * stride, 1, stride);
|
||||
material.clear();
|
||||
for (var multiDraw : multiDraws.get(stage)) {
|
||||
multiDraw.submit();
|
||||
}
|
||||
}
|
||||
|
||||
public void determineMultiDraws() {
|
||||
// TODO: Better material equality. Really we only need to bin by the results of the setup method.
|
||||
multiDraws.clear();
|
||||
// sort by stage, then material
|
||||
indirectDraws.sort(Comparator.comparing(IndirectDraw<T>::stage)
|
||||
.thenComparing(draw -> ComponentRegistry.materials.getMaterialID(draw.material())));
|
||||
|
||||
for (int start = 0, i = 0; i < indirectDraws.size(); i++) {
|
||||
var draw = indirectDraws.get(i);
|
||||
var material = draw.material();
|
||||
var stage = draw.stage();
|
||||
|
||||
// if the next draw call has a different RenderStage or Material, start a new MultiDraw
|
||||
if (i == indirectDraws.size() - 1 || stage != indirectDraws.get(i + 1)
|
||||
.stage() || !material.equals(indirectDraws.get(i + 1)
|
||||
.material())) {
|
||||
multiDraws.computeIfAbsent(stage, s -> new ArrayList<>())
|
||||
.add(new MultiDraw(material, start, i + 1));
|
||||
start = i + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean contains(RenderStage stage) {
|
||||
for (var draw : indirectDraws) {
|
||||
if (draw.stage == stage) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return multiDraws.containsKey(stage);
|
||||
}
|
||||
|
||||
return false;
|
||||
private record MultiDraw(Material material, int start, int end) {
|
||||
void submit() {
|
||||
material.setup();
|
||||
Textures.bindActiveTextures();
|
||||
glMultiDrawElementsIndirect(GL_TRIANGLES, GL_UNSIGNED_INT, start * IndirectBuffers.DRAW_COMMAND_STRIDE, end - start, (int) IndirectBuffers.DRAW_COMMAND_STRIDE);
|
||||
material.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -83,14 +83,18 @@ public class ComponentRegistry {
|
|||
public static class MaterialRegistry {
|
||||
|
||||
private final Set<Material> materials = new HashSet<>();
|
||||
private final List<Material> materialsOrdered = new ArrayList<>();
|
||||
private final MaterialSources vertexSources = new MaterialSources();
|
||||
private final MaterialSources fragmentSources = new MaterialSources();
|
||||
|
||||
public <T extends Material> T add(T material) {
|
||||
materials.add(material);
|
||||
|
||||
vertexSources.register(material.vertexShader());
|
||||
fragmentSources.register(material.fragmentShader());
|
||||
if (materials.add(material)) {
|
||||
materialsOrdered.add(material);
|
||||
vertexSources.register(material.vertexShader());
|
||||
fragmentSources.register(material.fragmentShader());
|
||||
} else {
|
||||
throw new IllegalArgumentException("Material already registered: " + material);
|
||||
}
|
||||
|
||||
return material;
|
||||
}
|
||||
|
@ -117,6 +121,10 @@ public class ComponentRegistry {
|
|||
return fragmentSources.orderedSources.indexOf(material.fragmentShader());
|
||||
}
|
||||
|
||||
public int getMaterialID(Material material) {
|
||||
return materialsOrdered.indexOf(material);
|
||||
}
|
||||
|
||||
private static class MaterialSources {
|
||||
private final Set<ResourceLocation> registered = new HashSet<>();
|
||||
private final List<ResourceLocation> orderedSources = new ArrayList<>();
|
||||
|
|
Loading…
Reference in a new issue