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.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
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.GL_COMMAND_BARRIER_BIT;
|
||||||
import static org.lwjgl.opengl.GL42.glMemoryBarrier;
|
import static org.lwjgl.opengl.GL42.glMemoryBarrier;
|
||||||
import static org.lwjgl.opengl.GL43.GL_SHADER_STORAGE_BARRIER_BIT;
|
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.RenderStage;
|
||||||
import com.jozufozu.flywheel.api.instancer.InstancedPart;
|
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++) {
|
for (int i = 0, batchesSize = drawSet.indirectDraws.size(); i < batchesSize; i++) {
|
||||||
var batch = drawSet.indirectDraws.get(i);
|
var batch = drawSet.indirectDraws.get(i);
|
||||||
var instanceCount = batch.instancer.getInstanceCount();
|
var instanceCount = batch.instancer()
|
||||||
|
.getInstanceCount();
|
||||||
batch.writeObjects(objectPtr, batchIDPtr, i);
|
batch.writeObjects(objectPtr, batchIDPtr, i);
|
||||||
|
|
||||||
objectPtr += instanceCount * objectStride;
|
objectPtr += instanceCount * objectStride;
|
||||||
|
@ -166,7 +173,7 @@ public class IndirectCullingGroup<T extends InstancedPart> {
|
||||||
int baseInstance = 0;
|
int baseInstance = 0;
|
||||||
for (var batch : drawSet.indirectDraws) {
|
for (var batch : drawSet.indirectDraws) {
|
||||||
batch.prepare(baseInstance);
|
batch.prepare(baseInstance);
|
||||||
baseInstance += batch.instancer.instanceCount;
|
baseInstance += batch.instancer().instanceCount;
|
||||||
}
|
}
|
||||||
return baseInstance;
|
return baseInstance;
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,10 +8,10 @@ import com.jozufozu.flywheel.api.material.Material;
|
||||||
import com.jozufozu.flywheel.core.ComponentRegistry;
|
import com.jozufozu.flywheel.core.ComponentRegistry;
|
||||||
|
|
||||||
public final class IndirectDraw<T extends InstancedPart> {
|
public final class IndirectDraw<T extends InstancedPart> {
|
||||||
final IndirectInstancer<T> instancer;
|
private final IndirectInstancer<T> instancer;
|
||||||
final IndirectMeshPool.BufferedMesh mesh;
|
private final IndirectMeshPool.BufferedMesh mesh;
|
||||||
final Material material;
|
private final Material material;
|
||||||
final RenderStage stage;
|
private final RenderStage stage;
|
||||||
int baseInstance = -1;
|
int baseInstance = -1;
|
||||||
|
|
||||||
final int vertexMaterialID;
|
final int vertexMaterialID;
|
||||||
|
@ -62,4 +62,20 @@ public final class IndirectDraw<T extends InstancedPart> {
|
||||||
MemoryUtil.memPutInt(ptr + 40, fragmentMaterialID); // fragmentMaterialID
|
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 static org.lwjgl.opengl.GL43.glMultiDrawElementsIndirect;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.EnumMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.RenderStage;
|
import com.jozufozu.flywheel.api.RenderStage;
|
||||||
import com.jozufozu.flywheel.api.instancer.InstancedPart;
|
import com.jozufozu.flywheel.api.instancer.InstancedPart;
|
||||||
import com.jozufozu.flywheel.api.material.Material;
|
import com.jozufozu.flywheel.api.material.Material;
|
||||||
|
import com.jozufozu.flywheel.core.ComponentRegistry;
|
||||||
import com.jozufozu.flywheel.util.Textures;
|
import com.jozufozu.flywheel.util.Textures;
|
||||||
|
|
||||||
public class IndirectDrawSet<T extends InstancedPart> {
|
public class IndirectDrawSet<T extends InstancedPart> {
|
||||||
|
|
||||||
final List<IndirectDraw<T>> indirectDraws = new ArrayList<>();
|
final List<IndirectDraw<T>> indirectDraws = new ArrayList<>();
|
||||||
|
|
||||||
|
final Map<RenderStage, List<MultiDraw>> multiDraws = new EnumMap<>(RenderStage.class);
|
||||||
|
|
||||||
public boolean isEmpty() {
|
public boolean isEmpty() {
|
||||||
return indirectDraws.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) {
|
public void add(IndirectInstancer<T> instancer, Material material, RenderStage stage, IndirectMeshPool.BufferedMesh bufferedMesh) {
|
||||||
indirectDraws.add(new IndirectDraw<>(instancer, material, stage, bufferedMesh));
|
indirectDraws.add(new IndirectDraw<>(instancer, material, stage, bufferedMesh));
|
||||||
|
determineMultiDraws();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void submit(RenderStage stage) {
|
public void submit(RenderStage stage) {
|
||||||
final int stride = (int) IndirectBuffers.DRAW_COMMAND_STRIDE;
|
if (!multiDraws.containsKey(stage)) {
|
||||||
for (int i = 0, indirectDrawsSize = indirectDraws.size(); i < indirectDrawsSize; i++) {
|
return;
|
||||||
var batch = indirectDraws.get(i);
|
}
|
||||||
if (batch.stage != stage) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var material = batch.material;
|
for (var multiDraw : multiDraws.get(stage)) {
|
||||||
material.setup();
|
multiDraw.submit();
|
||||||
Textures.bindActiveTextures();
|
}
|
||||||
glMultiDrawElementsIndirect(GL_TRIANGLES, GL_UNSIGNED_INT, i * stride, 1, stride);
|
}
|
||||||
material.clear();
|
|
||||||
|
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) {
|
public boolean contains(RenderStage stage) {
|
||||||
for (var draw : indirectDraws) {
|
return multiDraws.containsKey(stage);
|
||||||
if (draw.stage == stage) {
|
}
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
public static class MaterialRegistry {
|
||||||
|
|
||||||
private final Set<Material> materials = new HashSet<>();
|
private final Set<Material> materials = new HashSet<>();
|
||||||
|
private final List<Material> materialsOrdered = new ArrayList<>();
|
||||||
private final MaterialSources vertexSources = new MaterialSources();
|
private final MaterialSources vertexSources = new MaterialSources();
|
||||||
private final MaterialSources fragmentSources = new MaterialSources();
|
private final MaterialSources fragmentSources = new MaterialSources();
|
||||||
|
|
||||||
public <T extends Material> T add(T material) {
|
public <T extends Material> T add(T material) {
|
||||||
materials.add(material);
|
if (materials.add(material)) {
|
||||||
|
materialsOrdered.add(material);
|
||||||
vertexSources.register(material.vertexShader());
|
vertexSources.register(material.vertexShader());
|
||||||
fragmentSources.register(material.fragmentShader());
|
fragmentSources.register(material.fragmentShader());
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("Material already registered: " + material);
|
||||||
|
}
|
||||||
|
|
||||||
return material;
|
return material;
|
||||||
}
|
}
|
||||||
|
@ -117,6 +121,10 @@ public class ComponentRegistry {
|
||||||
return fragmentSources.orderedSources.indexOf(material.fragmentShader());
|
return fragmentSources.orderedSources.indexOf(material.fragmentShader());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getMaterialID(Material material) {
|
||||||
|
return materialsOrdered.indexOf(material);
|
||||||
|
}
|
||||||
|
|
||||||
private static class MaterialSources {
|
private static class MaterialSources {
|
||||||
private final Set<ResourceLocation> registered = new HashSet<>();
|
private final Set<ResourceLocation> registered = new HashSet<>();
|
||||||
private final List<ResourceLocation> orderedSources = new ArrayList<>();
|
private final List<ResourceLocation> orderedSources = new ArrayList<>();
|
||||||
|
|
Loading…
Reference in a new issue