mirror of
https://github.com/Jozufozu/Flywheel.git
synced 2024-12-27 07:26:48 +01:00
Burning a house to kill a spider
- Fix garbage indices on instancing engine - glDrawElements* wants indices as a byte offset, we were giving it a word offset - Rename BufferedMesh -> PooledMesh - PooledMesh no longer stores things it can fetch from the inner mesh - Use one vao for all drawcalls in instancing engine - PooledMesh issues *BaseVertex calls - Move crumbling logic into InstancedDrawManager - Move programs acquisition into InstancedDrawManager - Move owned gl objects into InstancedDrawManager
This commit is contained in:
parent
dd998058a3
commit
94ae32b9a7
9 changed files with 250 additions and 292 deletions
|
@ -1,7 +1,6 @@
|
||||||
package com.jozufozu.flywheel.backend.engine;
|
package com.jozufozu.flywheel.backend.engine;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.model.IndexSequence;
|
import com.jozufozu.flywheel.api.model.IndexSequence;
|
||||||
import com.jozufozu.flywheel.backend.gl.GlNumericType;
|
|
||||||
import com.jozufozu.flywheel.backend.gl.array.GlVertexArray;
|
import com.jozufozu.flywheel.backend.gl.array.GlVertexArray;
|
||||||
import com.jozufozu.flywheel.backend.gl.buffer.GlBuffer;
|
import com.jozufozu.flywheel.backend.gl.buffer.GlBuffer;
|
||||||
import com.jozufozu.flywheel.lib.memory.MemoryBlock;
|
import com.jozufozu.flywheel.lib.memory.MemoryBlock;
|
||||||
|
@ -60,7 +59,7 @@ public class IndexPool {
|
||||||
totalIndexCount += count;
|
totalIndexCount += count;
|
||||||
}
|
}
|
||||||
|
|
||||||
final var indexBlock = MemoryBlock.malloc(totalIndexCount * GlNumericType.UINT.byteWidth());
|
final var indexBlock = MemoryBlock.malloc(totalIndexCount * Integer.BYTES);
|
||||||
final long indexPtr = indexBlock.ptr();
|
final long indexPtr = indexBlock.ptr();
|
||||||
|
|
||||||
int firstIndex = 0;
|
int firstIndex = 0;
|
||||||
|
@ -70,7 +69,7 @@ public class IndexPool {
|
||||||
|
|
||||||
firstIndices.put(indexSequence, firstIndex);
|
firstIndices.put(indexSequence, firstIndex);
|
||||||
|
|
||||||
indexSequence.fill(indexPtr + (long) firstIndex * GlNumericType.UINT.byteWidth(), indexCount);
|
indexSequence.fill(indexPtr + (long) firstIndex * Integer.BYTES, indexCount);
|
||||||
|
|
||||||
firstIndex += indexCount;
|
firstIndex += indexCount;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,6 @@ import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.lwjgl.opengl.GL32;
|
import org.lwjgl.opengl.GL32;
|
||||||
|
@ -17,13 +16,11 @@ import com.jozufozu.flywheel.backend.gl.array.GlVertexArray;
|
||||||
import com.jozufozu.flywheel.backend.gl.buffer.GlBuffer;
|
import com.jozufozu.flywheel.backend.gl.buffer.GlBuffer;
|
||||||
import com.jozufozu.flywheel.lib.memory.MemoryBlock;
|
import com.jozufozu.flywheel.lib.memory.MemoryBlock;
|
||||||
|
|
||||||
import it.unimi.dsi.fastutil.objects.ReferenceArraySet;
|
|
||||||
|
|
||||||
public class MeshPool {
|
public class MeshPool {
|
||||||
private final VertexView vertexView;
|
private final VertexView vertexView;
|
||||||
private final Map<Mesh, BufferedMesh> meshes = new HashMap<>();
|
private final Map<Mesh, PooledMesh> meshes = new HashMap<>();
|
||||||
private final List<BufferedMesh> meshList = new ArrayList<>();
|
private final List<PooledMesh> meshList = new ArrayList<>();
|
||||||
private final List<BufferedMesh> recentlyAllocated = new ArrayList<>();
|
private final List<PooledMesh> recentlyAllocated = new ArrayList<>();
|
||||||
|
|
||||||
private final GlBuffer vbo;
|
private final GlBuffer vbo;
|
||||||
private final IndexPool indexPool;
|
private final IndexPool indexPool;
|
||||||
|
@ -46,12 +43,12 @@ public class MeshPool {
|
||||||
* @param mesh The model to allocate.
|
* @param mesh The model to allocate.
|
||||||
* @return A handle to the allocated model.
|
* @return A handle to the allocated model.
|
||||||
*/
|
*/
|
||||||
public BufferedMesh alloc(Mesh mesh) {
|
public PooledMesh alloc(Mesh mesh) {
|
||||||
return meshes.computeIfAbsent(mesh, this::_alloc);
|
return meshes.computeIfAbsent(mesh, this::_alloc);
|
||||||
}
|
}
|
||||||
|
|
||||||
private BufferedMesh _alloc(Mesh m) {
|
private PooledMesh _alloc(Mesh m) {
|
||||||
BufferedMesh bufferedModel = new BufferedMesh(m);
|
PooledMesh bufferedModel = new PooledMesh(m);
|
||||||
meshList.add(bufferedModel);
|
meshList.add(bufferedModel);
|
||||||
recentlyAllocated.add(bufferedModel);
|
recentlyAllocated.add(bufferedModel);
|
||||||
|
|
||||||
|
@ -60,7 +57,7 @@ public class MeshPool {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public BufferedMesh get(Mesh mesh) {
|
public MeshPool.PooledMesh get(Mesh mesh) {
|
||||||
return meshes.get(mesh);
|
return meshes.get(mesh);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,14 +72,15 @@ public class MeshPool {
|
||||||
|
|
||||||
// Might want to shrink the index pool if something was removed.
|
// Might want to shrink the index pool if something was removed.
|
||||||
indexPool.reset();
|
indexPool.reset();
|
||||||
for (BufferedMesh mesh : meshList) {
|
for (PooledMesh mesh : meshList) {
|
||||||
indexPool.updateCount(mesh.mesh.indexSequence(), mesh.indexCount());
|
indexPool.updateCount(mesh.mesh.indexSequence(), mesh.indexCount());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Otherwise, just update the index with the new counts.
|
// Otherwise, just update the index with the new counts.
|
||||||
for (BufferedMesh mesh : recentlyAllocated) {
|
for (PooledMesh mesh : recentlyAllocated) {
|
||||||
indexPool.updateCount(mesh.mesh.indexSequence(), mesh.indexCount());
|
indexPool.updateCount(mesh.mesh.indexSequence(), mesh.indexCount());
|
||||||
}
|
}
|
||||||
|
recentlyAllocated.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Always need to flush the index pool.
|
// Always need to flush the index pool.
|
||||||
|
@ -94,10 +92,10 @@ public class MeshPool {
|
||||||
|
|
||||||
private void processDeletions() {
|
private void processDeletions() {
|
||||||
// remove deleted meshes
|
// remove deleted meshes
|
||||||
meshList.removeIf(bufferedMesh -> {
|
meshList.removeIf(pooledMesh -> {
|
||||||
boolean deleted = bufferedMesh.deleted();
|
boolean deleted = pooledMesh.deleted();
|
||||||
if (deleted) {
|
if (deleted) {
|
||||||
meshes.remove(bufferedMesh.mesh);
|
meshes.remove(pooledMesh.mesh);
|
||||||
}
|
}
|
||||||
return deleted;
|
return deleted;
|
||||||
});
|
});
|
||||||
|
@ -105,7 +103,7 @@ public class MeshPool {
|
||||||
|
|
||||||
private void uploadAll() {
|
private void uploadAll() {
|
||||||
long neededSize = 0;
|
long neededSize = 0;
|
||||||
for (BufferedMesh mesh : meshList) {
|
for (PooledMesh mesh : meshList) {
|
||||||
neededSize += mesh.byteSize();
|
neededSize += mesh.byteSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,18 +112,15 @@ public class MeshPool {
|
||||||
|
|
||||||
int byteIndex = 0;
|
int byteIndex = 0;
|
||||||
int baseVertex = 0;
|
int baseVertex = 0;
|
||||||
for (BufferedMesh mesh : meshList) {
|
for (PooledMesh mesh : meshList) {
|
||||||
mesh.byteIndex = byteIndex;
|
|
||||||
mesh.baseVertex = baseVertex;
|
mesh.baseVertex = baseVertex;
|
||||||
|
|
||||||
vertexView.ptr(vertexPtr + mesh.byteIndex);
|
vertexView.ptr(vertexPtr + byteIndex);
|
||||||
vertexView.vertexCount(mesh.vertexCount);
|
vertexView.vertexCount(mesh.vertexCount());
|
||||||
mesh.mesh.write(vertexView);
|
mesh.mesh.write(vertexView);
|
||||||
|
|
||||||
byteIndex += mesh.byteSize();
|
byteIndex += mesh.byteSize();
|
||||||
baseVertex += mesh.vertexCount();
|
baseVertex += mesh.vertexCount();
|
||||||
|
|
||||||
mesh.boundTo.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
vbo.upload(vertexBlock);
|
vbo.upload(vertexBlock);
|
||||||
|
@ -146,29 +141,24 @@ public class MeshPool {
|
||||||
meshList.clear();
|
meshList.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
public class BufferedMesh {
|
public class PooledMesh {
|
||||||
|
public static final int INVALID_BASE_VERTEX = -1;
|
||||||
private final Mesh mesh;
|
private final Mesh mesh;
|
||||||
private final int vertexCount;
|
|
||||||
private final int byteSize;
|
|
||||||
|
|
||||||
private long byteIndex;
|
private int baseVertex = INVALID_BASE_VERTEX;
|
||||||
private int baseVertex;
|
|
||||||
|
|
||||||
private int referenceCount = 0;
|
private int referenceCount = 0;
|
||||||
private final Set<GlVertexArray> boundTo = new ReferenceArraySet<>();
|
|
||||||
|
|
||||||
private BufferedMesh(Mesh mesh) {
|
private PooledMesh(Mesh mesh) {
|
||||||
this.mesh = mesh;
|
this.mesh = mesh;
|
||||||
vertexCount = mesh.vertexCount();
|
|
||||||
byteSize = vertexCount * InternalVertex.STRIDE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int vertexCount() {
|
public int vertexCount() {
|
||||||
return vertexCount;
|
return mesh.vertexCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
public int byteSize() {
|
public int byteSize() {
|
||||||
return byteSize;
|
return mesh.vertexCount() * InternalVertex.STRIDE;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int indexCount() {
|
public int indexCount() {
|
||||||
|
@ -180,7 +170,11 @@ public class MeshPool {
|
||||||
}
|
}
|
||||||
|
|
||||||
public int firstIndex() {
|
public int firstIndex() {
|
||||||
return indexPool.firstIndex(mesh.indexSequence());
|
return MeshPool.this.indexPool.firstIndex(mesh.indexSequence());
|
||||||
|
}
|
||||||
|
|
||||||
|
public long firstIndexByteOffset() {
|
||||||
|
return (long) firstIndex() * Integer.BYTES;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean deleted() {
|
public boolean deleted() {
|
||||||
|
@ -188,31 +182,22 @@ public class MeshPool {
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean invalid() {
|
public boolean invalid() {
|
||||||
return mesh.vertexCount() == 0 || deleted() || byteIndex == -1;
|
return mesh.vertexCount() == 0 || baseVertex == INVALID_BASE_VERTEX || deleted();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void draw(int instanceCount) {
|
public void draw(int instanceCount) {
|
||||||
if (instanceCount > 1) {
|
if (instanceCount > 1) {
|
||||||
GL32.glDrawElementsInstanced(GlPrimitive.TRIANGLES.glEnum, mesh.indexCount(), GL32.GL_UNSIGNED_INT, firstIndex(), instanceCount);
|
GL32.glDrawElementsInstancedBaseVertex(GlPrimitive.TRIANGLES.glEnum, mesh.indexCount(), GL32.GL_UNSIGNED_INT, firstIndexByteOffset(), instanceCount, baseVertex);
|
||||||
} else {
|
} else {
|
||||||
GL32.glDrawElements(GlPrimitive.TRIANGLES.glEnum, mesh.indexCount(), GL32.GL_UNSIGNED_INT, firstIndex());
|
GL32.glDrawElementsBaseVertex(GlPrimitive.TRIANGLES.glEnum, mesh.indexCount(), GL32.GL_UNSIGNED_INT, firstIndexByteOffset(), baseVertex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setup(GlVertexArray vao) {
|
|
||||||
if (!boundTo.add(vao)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
MeshPool.this.indexPool.bind(vao);
|
|
||||||
vao.bindVertexBuffer(0, MeshPool.this.vbo.handle(), byteIndex, InternalVertex.STRIDE);
|
|
||||||
vao.bindAttributes(0, 0, InternalVertex.ATTRIBUTES);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void acquire() {
|
public void acquire() {
|
||||||
referenceCount++;
|
referenceCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void drop() {
|
public void release() {
|
||||||
if (--referenceCount == 0) {
|
if (--referenceCount == 0) {
|
||||||
MeshPool.this.dirty = true;
|
MeshPool.this.dirty = true;
|
||||||
MeshPool.this.anyToRemove = true;
|
MeshPool.this.anyToRemove = true;
|
||||||
|
|
|
@ -182,8 +182,8 @@ public class IndirectCullingGroup<I extends Instance> {
|
||||||
instancers.add(instancer);
|
instancers.add(instancer);
|
||||||
|
|
||||||
for (var entry : model.meshes()) {
|
for (var entry : model.meshes()) {
|
||||||
MeshPool.BufferedMesh bufferedMesh = meshPool.alloc(entry.mesh());
|
MeshPool.PooledMesh pooledMesh = meshPool.alloc(entry.mesh());
|
||||||
var draw = new IndirectDraw(instancer, entry.material(), bufferedMesh, stage);
|
var draw = new IndirectDraw(instancer, entry.material(), pooledMesh, stage);
|
||||||
indirectDraws.add(draw);
|
indirectDraws.add(draw);
|
||||||
instancer.addDraw(draw);
|
instancer.addDraw(draw);
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@ import com.jozufozu.flywheel.backend.engine.MeshPool;
|
||||||
public class IndirectDraw {
|
public class IndirectDraw {
|
||||||
private final IndirectInstancer<?> model;
|
private final IndirectInstancer<?> model;
|
||||||
private final Material material;
|
private final Material material;
|
||||||
private final MeshPool.BufferedMesh mesh;
|
private final MeshPool.PooledMesh mesh;
|
||||||
private final RenderStage stage;
|
private final RenderStage stage;
|
||||||
|
|
||||||
private final int materialVertexIndex;
|
private final int materialVertexIndex;
|
||||||
|
@ -20,7 +20,7 @@ public class IndirectDraw {
|
||||||
private final int packedMaterialProperties;
|
private final int packedMaterialProperties;
|
||||||
private boolean deleted;
|
private boolean deleted;
|
||||||
|
|
||||||
public IndirectDraw(IndirectInstancer<?> model, Material material, MeshPool.BufferedMesh mesh, RenderStage stage) {
|
public IndirectDraw(IndirectInstancer<?> model, Material material, MeshPool.PooledMesh mesh, RenderStage stage) {
|
||||||
this.model = model;
|
this.model = model;
|
||||||
this.material = material;
|
this.material = material;
|
||||||
this.mesh = mesh;
|
this.mesh = mesh;
|
||||||
|
@ -42,7 +42,7 @@ public class IndirectDraw {
|
||||||
return material;
|
return material;
|
||||||
}
|
}
|
||||||
|
|
||||||
public MeshPool.BufferedMesh mesh() {
|
public MeshPool.PooledMesh mesh() {
|
||||||
return mesh;
|
return mesh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,7 +85,7 @@ public class IndirectDraw {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
mesh.drop();
|
mesh.release();
|
||||||
|
|
||||||
deleted = true;
|
deleted = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,30 +1,22 @@
|
||||||
package com.jozufozu.flywheel.backend.engine.instancing;
|
package com.jozufozu.flywheel.backend.engine.instancing;
|
||||||
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.backend.engine.InstanceHandleImpl;
|
import com.jozufozu.flywheel.backend.engine.InstanceHandleImpl;
|
||||||
import com.jozufozu.flywheel.backend.engine.MeshPool;
|
import com.jozufozu.flywheel.backend.engine.MeshPool;
|
||||||
import com.jozufozu.flywheel.backend.gl.TextureBuffer;
|
import com.jozufozu.flywheel.backend.gl.TextureBuffer;
|
||||||
import com.jozufozu.flywheel.backend.gl.array.GlVertexArray;
|
|
||||||
|
|
||||||
public class DrawCall {
|
public class DrawCall {
|
||||||
public final ShaderState shaderState;
|
public final ShaderState shaderState;
|
||||||
private final InstancedInstancer<?> instancer;
|
private final InstancedInstancer<?> instancer;
|
||||||
private final MeshPool.BufferedMesh mesh;
|
private final MeshPool.PooledMesh mesh;
|
||||||
|
|
||||||
private final GlVertexArray vao;
|
|
||||||
@Nullable
|
|
||||||
private GlVertexArray vaoScratch;
|
|
||||||
private boolean deleted;
|
private boolean deleted;
|
||||||
|
|
||||||
public DrawCall(InstancedInstancer<?> instancer, MeshPool.BufferedMesh mesh, ShaderState shaderState) {
|
public DrawCall(InstancedInstancer<?> instancer, MeshPool.PooledMesh mesh, ShaderState shaderState) {
|
||||||
this.instancer = instancer;
|
this.instancer = instancer;
|
||||||
this.mesh = mesh;
|
this.mesh = mesh;
|
||||||
this.shaderState = shaderState;
|
this.shaderState = shaderState;
|
||||||
|
|
||||||
mesh.acquire();
|
mesh.acquire();
|
||||||
|
|
||||||
vao = GlVertexArray.create();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean deleted() {
|
public boolean deleted() {
|
||||||
|
@ -37,9 +29,6 @@ public class DrawCall {
|
||||||
}
|
}
|
||||||
|
|
||||||
instancer.bind(buffer);
|
instancer.bind(buffer);
|
||||||
mesh.setup(vao);
|
|
||||||
|
|
||||||
vao.bindForDraw();
|
|
||||||
|
|
||||||
mesh.draw(instancer.instanceCount());
|
mesh.draw(instancer.instanceCount());
|
||||||
}
|
}
|
||||||
|
@ -54,36 +43,17 @@ public class DrawCall {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var vao = lazyScratchVao();
|
|
||||||
|
|
||||||
instancer.bind(buffer);
|
instancer.bind(buffer);
|
||||||
mesh.setup(vao);
|
|
||||||
|
|
||||||
vao.bindForDraw();
|
|
||||||
|
|
||||||
mesh.draw(1);
|
mesh.draw(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
private GlVertexArray lazyScratchVao() {
|
|
||||||
if (vaoScratch == null) {
|
|
||||||
vaoScratch = GlVertexArray.create();
|
|
||||||
}
|
|
||||||
return vaoScratch;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void delete() {
|
public void delete() {
|
||||||
if (deleted) {
|
if (deleted) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
vao.delete();
|
mesh.release();
|
||||||
|
|
||||||
if (vaoScratch != null) {
|
|
||||||
vaoScratch.delete();
|
|
||||||
vaoScratch = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
mesh.drop();
|
|
||||||
|
|
||||||
deleted = true;
|
deleted = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,119 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend.engine.instancing;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.function.Consumer;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.backend.Engine;
|
|
||||||
import com.jozufozu.flywheel.api.context.TextureSource;
|
|
||||||
import com.jozufozu.flywheel.api.instance.Instance;
|
|
||||||
import com.jozufozu.flywheel.backend.compile.InstancingPrograms;
|
|
||||||
import com.jozufozu.flywheel.backend.engine.CommonCrumbling;
|
|
||||||
import com.jozufozu.flywheel.backend.engine.InstanceHandleImpl;
|
|
||||||
import com.jozufozu.flywheel.backend.engine.MaterialRenderState;
|
|
||||||
import com.jozufozu.flywheel.backend.engine.textures.TextureBinder;
|
|
||||||
import com.jozufozu.flywheel.backend.engine.uniform.Uniforms;
|
|
||||||
import com.jozufozu.flywheel.backend.gl.GlStateTracker;
|
|
||||||
import com.jozufozu.flywheel.backend.gl.GlTextureUnit;
|
|
||||||
import com.jozufozu.flywheel.backend.gl.TextureBuffer;
|
|
||||||
import com.jozufozu.flywheel.lib.context.ContextShaders;
|
|
||||||
import com.jozufozu.flywheel.lib.context.Contexts;
|
|
||||||
import com.jozufozu.flywheel.lib.material.SimpleMaterial;
|
|
||||||
|
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap;
|
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
|
||||||
import net.minecraft.client.resources.model.ModelBakery;
|
|
||||||
|
|
||||||
public class InstancedCrumbling {
|
|
||||||
public static void render(List<Engine.CrumblingBlock> crumblingBlocks, InstancingPrograms programs, TextureSource textureSource, TextureBuffer instanceTexture) {
|
|
||||||
// Sort draw calls into buckets, so we don't have to do as many shader binds.
|
|
||||||
var byShaderState = doCrumblingSort(crumblingBlocks);
|
|
||||||
|
|
||||||
if (byShaderState.isEmpty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var crumblingMaterial = SimpleMaterial.builder();
|
|
||||||
|
|
||||||
try (var state = GlStateTracker.getRestoreState()) {
|
|
||||||
TextureBinder.bindLightAndOverlay();
|
|
||||||
|
|
||||||
for (var shaderStateEntry : byShaderState.entrySet()) {
|
|
||||||
var byProgress = shaderStateEntry.getValue();
|
|
||||||
|
|
||||||
if (byProgress.isEmpty()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
ShaderState shader = shaderStateEntry.getKey();
|
|
||||||
|
|
||||||
CommonCrumbling.applyCrumblingProperties(crumblingMaterial, shader.material());
|
|
||||||
|
|
||||||
var program = programs.get(shader.instanceType(), ContextShaders.CRUMBLING);
|
|
||||||
program.bind();
|
|
||||||
|
|
||||||
Uniforms.bindForDraw();
|
|
||||||
InstancingEngine.uploadMaterialUniform(program, crumblingMaterial);
|
|
||||||
|
|
||||||
MaterialRenderState.setup(crumblingMaterial);
|
|
||||||
|
|
||||||
for (var progressEntry : byProgress.int2ObjectEntrySet()) {
|
|
||||||
var drawCalls = progressEntry.getValue();
|
|
||||||
|
|
||||||
if (drawCalls.isEmpty()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var context = Contexts.CRUMBLING.get(progressEntry.getIntKey());
|
|
||||||
context.prepare(crumblingMaterial, program, textureSource);
|
|
||||||
|
|
||||||
GlTextureUnit.T3.makeActive();
|
|
||||||
program.setSamplerBinding("_flw_instances", 3);
|
|
||||||
|
|
||||||
for (Consumer<TextureBuffer> drawCall : drawCalls) {
|
|
||||||
drawCall.accept(instanceTexture);
|
|
||||||
}
|
|
||||||
|
|
||||||
TextureBinder.resetTextureBindings();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MaterialRenderState.reset();
|
|
||||||
TextureBinder.resetLightAndOverlay();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Map<ShaderState, Int2ObjectMap<List<Consumer<TextureBuffer>>>> doCrumblingSort(List<Engine.CrumblingBlock> instances) {
|
|
||||||
Map<ShaderState, Int2ObjectMap<List<Consumer<TextureBuffer>>>> out = new HashMap<>();
|
|
||||||
|
|
||||||
for (Engine.CrumblingBlock triple : instances) {
|
|
||||||
int progress = triple.progress();
|
|
||||||
|
|
||||||
if (progress < 0 || progress >= ModelBakery.DESTROY_TYPES.size()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Instance instance : triple.instances()) {
|
|
||||||
// Filter out instances that weren't created by this engine.
|
|
||||||
// If all is well, we probably shouldn't take the `continue`
|
|
||||||
// branches but better to do checked casts.
|
|
||||||
if (!(instance.handle() instanceof InstanceHandleImpl impl)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!(impl.instancer instanceof InstancedInstancer<?> instancer)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (DrawCall draw : instancer.drawCalls()) {
|
|
||||||
out.computeIfAbsent(draw.shaderState, $ -> new Int2ObjectArrayMap<>())
|
|
||||||
.computeIfAbsent(progress, $ -> new ArrayList<>())
|
|
||||||
.add(buf -> draw.renderOne(buf, impl));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,31 +1,72 @@
|
||||||
package com.jozufozu.flywheel.backend.engine.instancing;
|
package com.jozufozu.flywheel.backend.engine.instancing;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.EnumMap;
|
import java.util.EnumMap;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
import org.lwjgl.opengl.GL32;
|
||||||
|
|
||||||
import com.google.common.collect.ArrayListMultimap;
|
import com.google.common.collect.ArrayListMultimap;
|
||||||
import com.google.common.collect.ImmutableListMultimap;
|
import com.google.common.collect.ImmutableListMultimap;
|
||||||
import com.google.common.collect.ListMultimap;
|
import com.google.common.collect.ListMultimap;
|
||||||
|
import com.jozufozu.flywheel.api.backend.Engine;
|
||||||
import com.jozufozu.flywheel.api.event.RenderStage;
|
import com.jozufozu.flywheel.api.event.RenderStage;
|
||||||
import com.jozufozu.flywheel.api.instance.Instance;
|
import com.jozufozu.flywheel.api.instance.Instance;
|
||||||
|
import com.jozufozu.flywheel.api.material.Material;
|
||||||
|
import com.jozufozu.flywheel.backend.ShaderIndices;
|
||||||
|
import com.jozufozu.flywheel.backend.compile.InstancingPrograms;
|
||||||
|
import com.jozufozu.flywheel.backend.engine.CommonCrumbling;
|
||||||
|
import com.jozufozu.flywheel.backend.engine.InstanceHandleImpl;
|
||||||
import com.jozufozu.flywheel.backend.engine.InstancerKey;
|
import com.jozufozu.flywheel.backend.engine.InstancerKey;
|
||||||
import com.jozufozu.flywheel.backend.engine.InstancerStorage;
|
import com.jozufozu.flywheel.backend.engine.InstancerStorage;
|
||||||
|
import com.jozufozu.flywheel.backend.engine.MaterialEncoder;
|
||||||
|
import com.jozufozu.flywheel.backend.engine.MaterialRenderState;
|
||||||
import com.jozufozu.flywheel.backend.engine.MeshPool;
|
import com.jozufozu.flywheel.backend.engine.MeshPool;
|
||||||
|
import com.jozufozu.flywheel.backend.engine.textures.TextureBinder;
|
||||||
|
import com.jozufozu.flywheel.backend.engine.textures.TextureSourceImpl;
|
||||||
|
import com.jozufozu.flywheel.backend.engine.uniform.Uniforms;
|
||||||
|
import com.jozufozu.flywheel.backend.gl.GlStateTracker;
|
||||||
|
import com.jozufozu.flywheel.backend.gl.GlTextureUnit;
|
||||||
|
import com.jozufozu.flywheel.backend.gl.TextureBuffer;
|
||||||
|
import com.jozufozu.flywheel.backend.gl.array.GlVertexArray;
|
||||||
|
import com.jozufozu.flywheel.backend.gl.shader.GlProgram;
|
||||||
|
import com.jozufozu.flywheel.lib.context.ContextShaders;
|
||||||
|
import com.jozufozu.flywheel.lib.context.Contexts;
|
||||||
|
import com.jozufozu.flywheel.lib.material.SimpleMaterial;
|
||||||
|
|
||||||
|
import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap;
|
||||||
|
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||||
|
import net.minecraft.client.resources.model.ModelBakery;
|
||||||
|
|
||||||
public class InstancedDrawManager extends InstancerStorage<InstancedInstancer<?>> {
|
public class InstancedDrawManager extends InstancerStorage<InstancedInstancer<?>> {
|
||||||
/**
|
/**
|
||||||
* The set of draw calls to make in each {@link RenderStage}.
|
* The set of draw calls to make in each {@link RenderStage}.
|
||||||
*/
|
*/
|
||||||
private final Map<RenderStage, DrawSet> drawSets = new EnumMap<>(RenderStage.class);
|
private final Map<RenderStage, DrawSet> drawSets = new EnumMap<>(RenderStage.class);
|
||||||
|
private final InstancingPrograms programs;
|
||||||
/**
|
/**
|
||||||
* A map of vertex types to their mesh pools.
|
* A map of vertex types to their mesh pools.
|
||||||
*/
|
*/
|
||||||
private final MeshPool meshPool = new MeshPool();
|
private final MeshPool meshPool;
|
||||||
|
private final GlVertexArray vao;
|
||||||
|
private final TextureSourceImpl textures;
|
||||||
|
private final TextureBuffer instanceTexture;
|
||||||
|
|
||||||
public DrawSet get(RenderStage stage) {
|
public InstancedDrawManager(InstancingPrograms programs) {
|
||||||
return drawSets.getOrDefault(stage, DrawSet.EMPTY);
|
programs.acquire();
|
||||||
|
this.programs = programs;
|
||||||
|
|
||||||
|
meshPool = new MeshPool();
|
||||||
|
vao = GlVertexArray.create();
|
||||||
|
textures = new TextureSourceImpl();
|
||||||
|
instanceTexture = new TextureBuffer();
|
||||||
|
|
||||||
|
meshPool.bind(vao);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void flush() {
|
public void flush() {
|
||||||
|
@ -52,17 +93,70 @@ public class InstancedDrawManager extends InstancerStorage<InstancedInstancer<?>
|
||||||
meshPool.flush();
|
meshPool.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void renderStage(RenderStage stage) {
|
||||||
|
var drawSet = drawSets.getOrDefault(stage, DrawSet.EMPTY);
|
||||||
|
|
||||||
|
if (drawSet.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try (var state = GlStateTracker.getRestoreState()) {
|
||||||
|
render(drawSet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void delete() {
|
public void delete() {
|
||||||
instancers.values()
|
instancers.values()
|
||||||
.forEach(InstancedInstancer::delete);
|
.forEach(InstancedInstancer::delete);
|
||||||
|
|
||||||
super.delete();
|
|
||||||
|
|
||||||
meshPool.delete();
|
|
||||||
|
|
||||||
drawSets.values()
|
drawSets.values()
|
||||||
.forEach(DrawSet::delete);
|
.forEach(DrawSet::delete);
|
||||||
drawSets.clear();
|
drawSets.clear();
|
||||||
|
|
||||||
|
meshPool.delete();
|
||||||
|
instanceTexture.delete();
|
||||||
|
programs.release();
|
||||||
|
vao.delete();
|
||||||
|
|
||||||
|
super.delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void render(InstancedDrawManager.DrawSet drawSet) {
|
||||||
|
Uniforms.bindForDraw();
|
||||||
|
vao.bindForDraw();
|
||||||
|
TextureBinder.bindLightAndOverlay();
|
||||||
|
|
||||||
|
for (var entry : drawSet) {
|
||||||
|
var shader = entry.getKey();
|
||||||
|
var drawCalls = entry.getValue();
|
||||||
|
|
||||||
|
if (drawCalls.isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var context = shader.context();
|
||||||
|
var material = shader.material();
|
||||||
|
|
||||||
|
var program = programs.get(shader.instanceType(), context.contextShader());
|
||||||
|
program.bind();
|
||||||
|
|
||||||
|
uploadMaterialUniform(program, material);
|
||||||
|
|
||||||
|
context.prepare(material, program, textures);
|
||||||
|
MaterialRenderState.setup(material);
|
||||||
|
|
||||||
|
GlTextureUnit.T3.makeActive();
|
||||||
|
|
||||||
|
program.setSamplerBinding("_flw_instances", 3);
|
||||||
|
|
||||||
|
for (var drawCall : drawCalls) {
|
||||||
|
drawCall.render(instanceTexture);
|
||||||
|
}
|
||||||
|
TextureBinder.resetTextureBindings();
|
||||||
|
}
|
||||||
|
|
||||||
|
MaterialRenderState.reset();
|
||||||
|
TextureBinder.resetLightAndOverlay();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -89,6 +183,106 @@ public class InstancedDrawManager extends InstancerStorage<InstancedInstancer<?>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void renderCrumbling(List<Engine.CrumblingBlock> crumblingBlocks) {
|
||||||
|
// Sort draw calls into buckets, so we don't have to do as many shader binds.
|
||||||
|
var byShaderState = doCrumblingSort(crumblingBlocks);
|
||||||
|
|
||||||
|
if (byShaderState.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var crumblingMaterial = SimpleMaterial.builder();
|
||||||
|
|
||||||
|
try (var state = GlStateTracker.getRestoreState()) {
|
||||||
|
Uniforms.bindForDraw();
|
||||||
|
vao.bindForDraw();
|
||||||
|
TextureBinder.bindLightAndOverlay();
|
||||||
|
|
||||||
|
for (var shaderStateEntry : byShaderState.entrySet()) {
|
||||||
|
var byProgress = shaderStateEntry.getValue();
|
||||||
|
|
||||||
|
if (byProgress.isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ShaderState shader = shaderStateEntry.getKey();
|
||||||
|
|
||||||
|
CommonCrumbling.applyCrumblingProperties(crumblingMaterial, shader.material());
|
||||||
|
|
||||||
|
var program = programs.get(shader.instanceType(), ContextShaders.CRUMBLING);
|
||||||
|
program.bind();
|
||||||
|
|
||||||
|
uploadMaterialUniform(program, crumblingMaterial);
|
||||||
|
|
||||||
|
MaterialRenderState.setup(crumblingMaterial);
|
||||||
|
|
||||||
|
for (var progressEntry : byProgress.int2ObjectEntrySet()) {
|
||||||
|
var drawCalls = progressEntry.getValue();
|
||||||
|
|
||||||
|
if (drawCalls.isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var context = Contexts.CRUMBLING.get(progressEntry.getIntKey());
|
||||||
|
context.prepare(crumblingMaterial, program, textures);
|
||||||
|
|
||||||
|
GlTextureUnit.T3.makeActive();
|
||||||
|
program.setSamplerBinding("_flw_instances", 3);
|
||||||
|
|
||||||
|
for (Consumer<TextureBuffer> drawCall : drawCalls) {
|
||||||
|
drawCall.accept(instanceTexture);
|
||||||
|
}
|
||||||
|
|
||||||
|
TextureBinder.resetTextureBindings();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MaterialRenderState.reset();
|
||||||
|
TextureBinder.resetLightAndOverlay();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Map<ShaderState, Int2ObjectMap<List<Consumer<TextureBuffer>>>> doCrumblingSort(List<Engine.CrumblingBlock> instances) {
|
||||||
|
Map<ShaderState, Int2ObjectMap<List<Consumer<TextureBuffer>>>> out = new HashMap<>();
|
||||||
|
|
||||||
|
for (Engine.CrumblingBlock triple : instances) {
|
||||||
|
int progress = triple.progress();
|
||||||
|
|
||||||
|
if (progress < 0 || progress >= ModelBakery.DESTROY_TYPES.size()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Instance instance : triple.instances()) {
|
||||||
|
// Filter out instances that weren't created by this engine.
|
||||||
|
// If all is well, we probably shouldn't take the `continue`
|
||||||
|
// branches but better to do checked casts.
|
||||||
|
if (!(instance.handle() instanceof InstanceHandleImpl impl)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!(impl.instancer instanceof InstancedInstancer<?> instancer)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (DrawCall draw : instancer.drawCalls()) {
|
||||||
|
out.computeIfAbsent(draw.shaderState, $ -> new Int2ObjectArrayMap<>())
|
||||||
|
.computeIfAbsent(progress, $ -> new ArrayList<>())
|
||||||
|
.add(buf -> draw.renderOne(buf, impl));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void uploadMaterialUniform(GlProgram program, Material material) {
|
||||||
|
int uniformLocation = program.getUniformLocation("_flw_packedMaterial");
|
||||||
|
int vertexIndex = ShaderIndices.getVertexShaderIndex(material.shaders());
|
||||||
|
int fragmentIndex = ShaderIndices.getFragmentShaderIndex(material.shaders());
|
||||||
|
int packedFogAndCutout = MaterialEncoder.packFogAndCutout(material);
|
||||||
|
int packedMaterialProperties = MaterialEncoder.packProperties(material);
|
||||||
|
GL32.glUniform4ui(uniformLocation, vertexIndex, fragmentIndex, packedFogAndCutout, packedMaterialProperties);
|
||||||
|
}
|
||||||
|
|
||||||
public static class DrawSet implements Iterable<Map.Entry<ShaderState, Collection<DrawCall>>> {
|
public static class DrawSet implements Iterable<Map.Entry<ShaderState, Collection<DrawCall>>> {
|
||||||
public static final DrawSet EMPTY = new DrawSet(ImmutableListMultimap.of());
|
public static final DrawSet EMPTY = new DrawSet(ImmutableListMultimap.of());
|
||||||
|
|
||||||
|
|
|
@ -2,43 +2,28 @@ package com.jozufozu.flywheel.backend.engine.instancing;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.lwjgl.opengl.GL32;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.event.RenderContext;
|
import com.jozufozu.flywheel.api.event.RenderContext;
|
||||||
import com.jozufozu.flywheel.api.event.RenderStage;
|
import com.jozufozu.flywheel.api.event.RenderStage;
|
||||||
import com.jozufozu.flywheel.api.material.Material;
|
|
||||||
import com.jozufozu.flywheel.api.task.Plan;
|
import com.jozufozu.flywheel.api.task.Plan;
|
||||||
import com.jozufozu.flywheel.api.task.TaskExecutor;
|
import com.jozufozu.flywheel.api.task.TaskExecutor;
|
||||||
import com.jozufozu.flywheel.backend.ShaderIndices;
|
|
||||||
import com.jozufozu.flywheel.backend.compile.InstancingPrograms;
|
import com.jozufozu.flywheel.backend.compile.InstancingPrograms;
|
||||||
import com.jozufozu.flywheel.backend.engine.AbstractEngine;
|
import com.jozufozu.flywheel.backend.engine.AbstractEngine;
|
||||||
import com.jozufozu.flywheel.backend.engine.AbstractInstancer;
|
import com.jozufozu.flywheel.backend.engine.AbstractInstancer;
|
||||||
import com.jozufozu.flywheel.backend.engine.InstancerStorage;
|
import com.jozufozu.flywheel.backend.engine.InstancerStorage;
|
||||||
import com.jozufozu.flywheel.backend.engine.MaterialEncoder;
|
|
||||||
import com.jozufozu.flywheel.backend.engine.MaterialRenderState;
|
|
||||||
import com.jozufozu.flywheel.backend.engine.textures.TextureBinder;
|
|
||||||
import com.jozufozu.flywheel.backend.engine.textures.TextureSourceImpl;
|
|
||||||
import com.jozufozu.flywheel.backend.engine.uniform.Uniforms;
|
import com.jozufozu.flywheel.backend.engine.uniform.Uniforms;
|
||||||
import com.jozufozu.flywheel.backend.gl.GlStateTracker;
|
import com.jozufozu.flywheel.backend.gl.GlStateTracker;
|
||||||
import com.jozufozu.flywheel.backend.gl.GlTextureUnit;
|
|
||||||
import com.jozufozu.flywheel.backend.gl.TextureBuffer;
|
|
||||||
import com.jozufozu.flywheel.backend.gl.shader.GlProgram;
|
|
||||||
import com.jozufozu.flywheel.lib.task.Flag;
|
import com.jozufozu.flywheel.lib.task.Flag;
|
||||||
import com.jozufozu.flywheel.lib.task.NamedFlag;
|
import com.jozufozu.flywheel.lib.task.NamedFlag;
|
||||||
import com.jozufozu.flywheel.lib.task.SyncedPlan;
|
import com.jozufozu.flywheel.lib.task.SyncedPlan;
|
||||||
|
|
||||||
public class InstancingEngine extends AbstractEngine {
|
public class InstancingEngine extends AbstractEngine {
|
||||||
private final InstancingPrograms programs;
|
private final InstancedDrawManager drawManager;
|
||||||
private final TextureSourceImpl textures = new TextureSourceImpl();
|
|
||||||
private final TextureBuffer instanceTexture = new TextureBuffer();
|
|
||||||
private final InstancedDrawManager drawManager = new InstancedDrawManager();
|
|
||||||
private final Flag flushFlag = new NamedFlag("flushed");
|
private final Flag flushFlag = new NamedFlag("flushed");
|
||||||
|
|
||||||
public InstancingEngine(InstancingPrograms programs, int maxOriginDistance) {
|
public InstancingEngine(InstancingPrograms programs, int maxOriginDistance) {
|
||||||
super(maxOriginDistance);
|
super(maxOriginDistance);
|
||||||
programs.acquire();
|
drawManager = new InstancedDrawManager(programs);
|
||||||
this.programs = programs;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Plan<RenderContext> createFramePlan() {
|
public Plan<RenderContext> createFramePlan() {
|
||||||
|
@ -60,16 +45,7 @@ public class InstancingEngine extends AbstractEngine {
|
||||||
flushFlag.lower();
|
flushFlag.lower();
|
||||||
}
|
}
|
||||||
|
|
||||||
var drawSet = drawManager.get(stage);
|
drawManager.renderStage(stage);
|
||||||
|
|
||||||
if (drawSet.isEmpty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try (var state = GlStateTracker.getRestoreState()) {
|
|
||||||
Uniforms.bindForDraw();
|
|
||||||
render(drawSet);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -77,7 +53,7 @@ public class InstancingEngine extends AbstractEngine {
|
||||||
// Need to wait for flush before we can inspect instancer state.
|
// Need to wait for flush before we can inspect instancer state.
|
||||||
executor.syncUntil(flushFlag::isRaised);
|
executor.syncUntil(flushFlag::isRaised);
|
||||||
|
|
||||||
InstancedCrumbling.render(crumblingBlocks, programs, textures, instanceTexture);
|
drawManager.renderCrumbling(crumblingBlocks);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -88,52 +64,5 @@ public class InstancingEngine extends AbstractEngine {
|
||||||
@Override
|
@Override
|
||||||
public void delete() {
|
public void delete() {
|
||||||
drawManager.delete();
|
drawManager.delete();
|
||||||
programs.release();
|
|
||||||
instanceTexture.delete();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void render(InstancedDrawManager.DrawSet drawSet) {
|
|
||||||
TextureBinder.bindLightAndOverlay();
|
|
||||||
|
|
||||||
for (var entry : drawSet) {
|
|
||||||
var shader = entry.getKey();
|
|
||||||
var drawCalls = entry.getValue();
|
|
||||||
|
|
||||||
if (drawCalls.isEmpty()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var context = shader.context();
|
|
||||||
var material = shader.material();
|
|
||||||
|
|
||||||
var program = programs.get(shader.instanceType(), context.contextShader());
|
|
||||||
program.bind();
|
|
||||||
|
|
||||||
uploadMaterialUniform(program, material);
|
|
||||||
|
|
||||||
context.prepare(material, program, textures);
|
|
||||||
MaterialRenderState.setup(material);
|
|
||||||
|
|
||||||
GlTextureUnit.T3.makeActive();
|
|
||||||
|
|
||||||
program.setSamplerBinding("_flw_instances", 3);
|
|
||||||
|
|
||||||
for (var drawCall : drawCalls) {
|
|
||||||
drawCall.render(instanceTexture);
|
|
||||||
}
|
|
||||||
TextureBinder.resetTextureBindings();
|
|
||||||
}
|
|
||||||
|
|
||||||
MaterialRenderState.reset();
|
|
||||||
TextureBinder.resetLightAndOverlay();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void uploadMaterialUniform(GlProgram program, Material material) {
|
|
||||||
int uniformLocation = program.getUniformLocation("_flw_packedMaterial");
|
|
||||||
int vertexIndex = ShaderIndices.getVertexShaderIndex(material.shaders());
|
|
||||||
int fragmentIndex = ShaderIndices.getFragmentShaderIndex(material.shaders());
|
|
||||||
int packedFogAndCutout = MaterialEncoder.packFogAndCutout(material);
|
|
||||||
int packedMaterialProperties = MaterialEncoder.packProperties(material);
|
|
||||||
GL32.glUniform4ui(uniformLocation, vertexIndex, fragmentIndex, packedFogAndCutout, packedMaterialProperties);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -98,7 +98,7 @@ public class LineModelBuilder {
|
||||||
|
|
||||||
public static class LineMesh implements Mesh {
|
public static class LineMesh implements Mesh {
|
||||||
public static final IndexSequence INDEX_SEQUENCE = (ptr, count) -> {
|
public static final IndexSequence INDEX_SEQUENCE = (ptr, count) -> {
|
||||||
int numVertices = 4 * (count / 6);
|
int numVertices = 2 * count / 3;
|
||||||
int baseVertex = 0;
|
int baseVertex = 0;
|
||||||
while (baseVertex < numVertices) {
|
while (baseVertex < numVertices) {
|
||||||
// triangle a
|
// triangle a
|
||||||
|
|
Loading…
Reference in a new issue