mirror of
https://github.com/Jozufozu/Flywheel.git
synced 2025-02-06 18:24:59 +01:00
Memcpy: Batching Edition
- Add BatchedMeshPool - Rename MeshPool to InstancedMeshPool - Rename/remove/add methods to VertexList/ReusableVertexList
This commit is contained in:
parent
d7613fc5e4
commit
5ac6065205
19 changed files with 341 additions and 105 deletions
|
@ -5,7 +5,7 @@ public interface ReusableVertexList extends MutableVertexList {
|
|||
|
||||
void ptr(long ptr);
|
||||
|
||||
void shiftPtr(int vertices);
|
||||
int vertexStride();
|
||||
|
||||
void setVertexCount(int vertexCount);
|
||||
void vertexCount(int vertexCount);
|
||||
}
|
||||
|
|
|
@ -70,12 +70,12 @@ public interface VertexList {
|
|||
}
|
||||
|
||||
default void writeAll(MutableVertexList dst) {
|
||||
write(dst, 0, 0, getVertexCount());
|
||||
write(dst, 0, 0, vertexCount());
|
||||
}
|
||||
|
||||
int getVertexCount();
|
||||
int vertexCount();
|
||||
|
||||
default boolean isEmpty() {
|
||||
return getVertexCount() == 0;
|
||||
return vertexCount() == 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,208 @@
|
|||
package com.jozufozu.flywheel.backend.instancing.batching;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.lwjgl.system.MemoryUtil;
|
||||
|
||||
import com.jozufozu.flywheel.Flywheel;
|
||||
import com.jozufozu.flywheel.api.vertex.ReusableVertexList;
|
||||
import com.jozufozu.flywheel.api.vertex.VertexListProvider;
|
||||
import com.jozufozu.flywheel.backend.memory.MemoryBlock;
|
||||
import com.jozufozu.flywheel.core.model.Mesh;
|
||||
import com.mojang.blaze3d.vertex.VertexFormat;
|
||||
|
||||
public class BatchedMeshPool {
|
||||
private final VertexFormat vertexFormat;
|
||||
private final ReusableVertexList vertexList;
|
||||
private final int growthMargin;
|
||||
|
||||
private final Map<Mesh, BufferedMesh> meshes = new HashMap<>();
|
||||
private final List<BufferedMesh> allBuffered = new ArrayList<>();
|
||||
private final List<BufferedMesh> pendingUpload = new ArrayList<>();
|
||||
|
||||
private MemoryBlock memory;
|
||||
private long byteSize;
|
||||
|
||||
private boolean dirty;
|
||||
private boolean anyToRemove;
|
||||
|
||||
/**
|
||||
* Create a new mesh pool.
|
||||
*/
|
||||
public BatchedMeshPool(VertexFormat vertexFormat) {
|
||||
this.vertexFormat = vertexFormat;
|
||||
vertexList = VertexListProvider.get(vertexFormat).createVertexList();
|
||||
growthMargin = vertexList.vertexStride() * 32;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocate a mesh in the arena.
|
||||
*
|
||||
* @param mesh The mesh to allocate.
|
||||
* @return A handle to the allocated mesh.
|
||||
*/
|
||||
public BufferedMesh alloc(Mesh mesh) {
|
||||
return meshes.computeIfAbsent(mesh, m -> {
|
||||
BufferedMesh bufferedMesh = new BufferedMesh(m, byteSize, m.getVertexCount() * vertexList.vertexStride());
|
||||
byteSize += bufferedMesh.size();
|
||||
allBuffered.add(bufferedMesh);
|
||||
pendingUpload.add(bufferedMesh);
|
||||
|
||||
dirty = true;
|
||||
return bufferedMesh;
|
||||
});
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public BufferedMesh get(Mesh mesh) {
|
||||
return meshes.get(mesh);
|
||||
}
|
||||
|
||||
public void flush() {
|
||||
if (dirty) {
|
||||
if (anyToRemove) {
|
||||
processDeletions();
|
||||
}
|
||||
|
||||
realloc();
|
||||
uploadPending();
|
||||
|
||||
dirty = false;
|
||||
pendingUpload.clear();
|
||||
}
|
||||
}
|
||||
|
||||
private void processDeletions() {
|
||||
// remove deleted meshes
|
||||
allBuffered.removeIf(bufferedMesh -> {
|
||||
boolean deleted = bufferedMesh.isDeleted();
|
||||
if (deleted) {
|
||||
meshes.remove(bufferedMesh.mesh);
|
||||
}
|
||||
return deleted;
|
||||
});
|
||||
|
||||
// re-evaluate first vertex for each mesh
|
||||
int byteIndex = 0;
|
||||
for (BufferedMesh mesh : allBuffered) {
|
||||
if (mesh.byteIndex != byteIndex) {
|
||||
pendingUpload.add(mesh);
|
||||
}
|
||||
|
||||
mesh.byteIndex = byteIndex;
|
||||
|
||||
byteIndex += mesh.size();
|
||||
}
|
||||
|
||||
this.byteSize = byteIndex;
|
||||
this.anyToRemove = false;
|
||||
}
|
||||
|
||||
private void realloc() {
|
||||
if (byteSize < 0) {
|
||||
throw new IllegalArgumentException("Size " + byteSize + " < 0");
|
||||
}
|
||||
|
||||
if (byteSize == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (memory == null) {
|
||||
memory = MemoryBlock.malloc(byteSize);
|
||||
} else if (byteSize > memory.size()) {
|
||||
memory = memory.realloc(byteSize + growthMargin);
|
||||
}
|
||||
}
|
||||
|
||||
private void uploadPending() {
|
||||
try {
|
||||
for (BufferedMesh mesh : pendingUpload) {
|
||||
mesh.buffer(vertexList);
|
||||
}
|
||||
|
||||
pendingUpload.clear();
|
||||
} catch (Exception e) {
|
||||
Flywheel.LOGGER.error("Error uploading pooled meshes:", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void delete() {
|
||||
if (memory != null) {
|
||||
memory.free();
|
||||
}
|
||||
meshes.clear();
|
||||
allBuffered.clear();
|
||||
pendingUpload.clear();
|
||||
}
|
||||
|
||||
public VertexFormat getVertexFormat() {
|
||||
return vertexFormat;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "BatchedMeshPool{" + "vertexFormat=" + vertexFormat + ", byteSize=" + byteSize + ", meshCount=" + meshes.size() + '}';
|
||||
}
|
||||
|
||||
public class BufferedMesh {
|
||||
private final Mesh mesh;
|
||||
private long byteIndex;
|
||||
private int byteSize;
|
||||
private boolean deleted;
|
||||
|
||||
private BufferedMesh(Mesh mesh, long byteIndex, int byteSize) {
|
||||
this.mesh = mesh;
|
||||
this.byteIndex = byteIndex;
|
||||
this.byteSize = byteSize;
|
||||
}
|
||||
|
||||
private void buffer(ReusableVertexList vertexList) {
|
||||
vertexList.ptr(ptr());
|
||||
vertexList.vertexCount(mesh.getVertexCount());
|
||||
|
||||
mesh.write(vertexList);
|
||||
}
|
||||
|
||||
public void copyTo(long ptr) {
|
||||
if (isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
MemoryUtil.memCopy(ptr(), ptr, byteSize);
|
||||
}
|
||||
|
||||
private boolean isEmpty() {
|
||||
return mesh.isEmpty() || isDeleted();
|
||||
}
|
||||
|
||||
private long ptr() {
|
||||
return BatchedMeshPool.this.memory.ptr() + byteIndex;
|
||||
}
|
||||
|
||||
public void delete() {
|
||||
deleted = true;
|
||||
BatchedMeshPool.this.dirty = true;
|
||||
BatchedMeshPool.this.anyToRemove = true;
|
||||
}
|
||||
|
||||
public Mesh getMesh() {
|
||||
return mesh;
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return byteSize;
|
||||
}
|
||||
|
||||
public VertexFormat getVertexFormat() {
|
||||
return vertexFormat;
|
||||
}
|
||||
|
||||
public boolean isDeleted() {
|
||||
return deleted;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,6 +4,7 @@ import java.util.ArrayList;
|
|||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumMap;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
@ -14,7 +15,9 @@ import com.google.common.collect.ArrayListMultimap;
|
|||
import com.google.common.collect.ImmutableListMultimap;
|
||||
import com.google.common.collect.ListMultimap;
|
||||
import com.jozufozu.flywheel.api.RenderStage;
|
||||
import com.jozufozu.flywheel.core.model.Mesh;
|
||||
import com.jozufozu.flywheel.core.model.Model;
|
||||
import com.mojang.blaze3d.vertex.VertexFormat;
|
||||
|
||||
import net.minecraft.client.renderer.RenderType;
|
||||
|
||||
|
@ -23,6 +26,7 @@ public class BatchingTransformManager {
|
|||
private final List<CPUInstancer<?>> allInstancers = new ArrayList<>();
|
||||
private final Map<RenderStage, TransformSet> transformSets = new EnumMap<>(RenderStage.class);
|
||||
private final Map<RenderStage, TransformSet> transformSetsView = Collections.unmodifiableMap(transformSets);
|
||||
private final Map<VertexFormat, BatchedMeshPool> meshPools = new HashMap<>();
|
||||
|
||||
public TransformSet get(RenderStage stage) {
|
||||
return transformSets.getOrDefault(stage, TransformSet.EMPTY);
|
||||
|
@ -41,9 +45,17 @@ public class BatchingTransformManager {
|
|||
add(model.instancer(), model.model());
|
||||
}
|
||||
uninitializedModels.clear();
|
||||
|
||||
for (var pool : meshPools.values()) {
|
||||
pool.flush();
|
||||
}
|
||||
}
|
||||
|
||||
public void delete() {
|
||||
meshPools.values()
|
||||
.forEach(BatchedMeshPool::delete);
|
||||
meshPools.clear();
|
||||
|
||||
allInstancers.forEach(CPUInstancer::delete);
|
||||
allInstancers.clear();
|
||||
}
|
||||
|
@ -55,9 +67,9 @@ public class BatchingTransformManager {
|
|||
private void add(CPUInstancer<?> instancer, Model model) {
|
||||
var meshes = model.getMeshes();
|
||||
for (var entry : meshes.entrySet()) {
|
||||
TransformCall<?> transformCall = new TransformCall<>(instancer, entry.getKey(), entry.getValue());
|
||||
var material = transformCall.getMaterial();
|
||||
var material = entry.getKey();
|
||||
var renderType = material.getBatchingRenderType();
|
||||
TransformCall<?> transformCall = new TransformCall<>(instancer, material, alloc(entry.getValue(), renderType.format()));
|
||||
|
||||
transformSets.computeIfAbsent(material.getRenderStage(), TransformSet::new)
|
||||
.put(renderType, transformCall);
|
||||
|
@ -65,6 +77,11 @@ public class BatchingTransformManager {
|
|||
allInstancers.add(instancer);
|
||||
}
|
||||
|
||||
private BatchedMeshPool.BufferedMesh alloc(Mesh mesh, VertexFormat format) {
|
||||
return meshPools.computeIfAbsent(format, BatchedMeshPool::new)
|
||||
.alloc(mesh);
|
||||
}
|
||||
|
||||
public static class TransformSet implements Iterable<Map.Entry<RenderType, Collection<TransformCall<?>>>> {
|
||||
public static final TransformSet EMPTY = new TransformSet(ImmutableListMultimap.of());
|
||||
|
||||
|
|
|
@ -72,9 +72,8 @@ public class DrawBuffer {
|
|||
}
|
||||
|
||||
ReusableVertexList vertexList = provider.createVertexList();
|
||||
vertexList.ptr(memory.ptr());
|
||||
vertexList.shiftPtr(startVertex);
|
||||
vertexList.setVertexCount(vertexCount);
|
||||
vertexList.ptr(memory.ptr() + startVertex * vertexList.vertexStride());
|
||||
vertexList.vertexCount(vertexCount);
|
||||
return vertexList;
|
||||
}
|
||||
|
||||
|
@ -91,6 +90,10 @@ public class DrawBuffer {
|
|||
bufferBuilder.flywheel$injectForRender(buffer, format, vertexCount);
|
||||
}
|
||||
|
||||
public VertexFormat getVertexFormat() {
|
||||
return format;
|
||||
}
|
||||
|
||||
public boolean isPrepared() {
|
||||
return prepared;
|
||||
}
|
||||
|
|
|
@ -8,8 +8,8 @@ import com.jozufozu.flywheel.api.struct.StructType;
|
|||
import com.jozufozu.flywheel.api.vertex.MutableVertexList;
|
||||
import com.jozufozu.flywheel.api.vertex.ReusableVertexList;
|
||||
import com.jozufozu.flywheel.backend.instancing.TaskEngine;
|
||||
import com.jozufozu.flywheel.core.model.Mesh;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import com.mojang.blaze3d.vertex.VertexFormat;
|
||||
import com.mojang.math.Matrix3f;
|
||||
import com.mojang.math.Matrix4f;
|
||||
import com.mojang.math.Vector3f;
|
||||
|
@ -18,23 +18,22 @@ import com.mojang.math.Vector4f;
|
|||
import net.minecraft.client.multiplayer.ClientLevel;
|
||||
|
||||
public class TransformCall<D extends InstancedPart> {
|
||||
|
||||
private final CPUInstancer<D> instancer;
|
||||
private final Material material;
|
||||
private final Mesh mesh;
|
||||
private final BatchedMeshPool.BufferedMesh bufferedMesh;
|
||||
|
||||
public TransformCall(CPUInstancer<D> instancer, Material material, Mesh mesh) {
|
||||
public TransformCall(CPUInstancer<D> instancer, Material material, BatchedMeshPool.BufferedMesh mesh) {
|
||||
this.instancer = instancer;
|
||||
this.material = material;
|
||||
this.mesh = mesh;
|
||||
this.bufferedMesh = mesh;
|
||||
}
|
||||
|
||||
public Material getMaterial() {
|
||||
return material;
|
||||
}
|
||||
|
||||
public Mesh getMesh() {
|
||||
return mesh;
|
||||
public VertexFormat getVertexFormat() {
|
||||
return bufferedMesh.getVertexFormat();
|
||||
}
|
||||
|
||||
void submitTasks(TaskEngine pool, DrawBuffer buffer, int startVertex, PoseStack stack, ClientLevel level) {
|
||||
|
@ -47,7 +46,7 @@ public class TransformCall<D extends InstancedPart> {
|
|||
instances -= 512;
|
||||
int start = Math.max(instances, 0);
|
||||
|
||||
int vertexCount = mesh.getVertexCount() * (end - start);
|
||||
int vertexCount = bufferedMesh.getMesh().getVertexCount() * (end - start);
|
||||
ReusableVertexList sub = buffer.slice(startVertex, vertexCount);
|
||||
startVertex += vertexCount;
|
||||
|
||||
|
@ -65,23 +64,24 @@ public class TransformCall<D extends InstancedPart> {
|
|||
|
||||
private void transformList(ReusableVertexList vertexList, List<D> parts, PoseStack stack, ClientLevel level) {
|
||||
long anchorPtr = vertexList.ptr();
|
||||
int totalVertexCount = vertexList.getVertexCount();
|
||||
int totalVertexCount = vertexList.vertexCount();
|
||||
|
||||
int meshVertexCount = mesh.getVertexCount();
|
||||
vertexList.setVertexCount(meshVertexCount);
|
||||
int meshVertexCount = bufferedMesh.getMesh().getVertexCount();
|
||||
int meshByteSize = bufferedMesh.size();
|
||||
vertexList.vertexCount(meshVertexCount);
|
||||
|
||||
StructType.VertexTransformer<D> structVertexTransformer = instancer.type.getVertexTransformer();
|
||||
|
||||
for (D d : parts) {
|
||||
mesh.write(vertexList);
|
||||
bufferedMesh.copyTo(vertexList.ptr());
|
||||
|
||||
structVertexTransformer.transform(vertexList, d, level);
|
||||
|
||||
vertexList.shiftPtr(meshVertexCount);
|
||||
vertexList.ptr(vertexList.ptr() + meshByteSize);
|
||||
}
|
||||
|
||||
vertexList.ptr(anchorPtr);
|
||||
vertexList.setVertexCount(totalVertexCount);
|
||||
vertexList.vertexCount(totalVertexCount);
|
||||
material.getVertexTransformer().transform(vertexList, level);
|
||||
applyPoseStack(vertexList, stack);
|
||||
}
|
||||
|
@ -93,7 +93,7 @@ public class TransformCall<D extends InstancedPart> {
|
|||
Matrix4f modelMatrix = stack.last().pose();
|
||||
Matrix3f normalMatrix = stack.last().normal();
|
||||
|
||||
for (int i = 0; i < vertexList.getVertexCount(); i++) {
|
||||
for (int i = 0; i < vertexList.vertexCount(); i++) {
|
||||
pos.set(
|
||||
vertexList.x(i),
|
||||
vertexList.y(i),
|
||||
|
@ -119,6 +119,6 @@ public class TransformCall<D extends InstancedPart> {
|
|||
}
|
||||
|
||||
public int getTotalVertexCount() {
|
||||
return mesh.getVertexCount() * instancer.getInstanceCount();
|
||||
return bufferedMesh.getMesh().getVertexCount() * instancer.getInstanceCount();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,10 +10,10 @@ public class DrawCall {
|
|||
final GPUInstancer<?> instancer;
|
||||
final Material material;
|
||||
private final int meshAttributes;
|
||||
MeshPool.BufferedMesh bufferedMesh;
|
||||
InstancedMeshPool.BufferedMesh bufferedMesh;
|
||||
GlVertexArray vao;
|
||||
|
||||
DrawCall(GPUInstancer<?> instancer, Material material, MeshPool.BufferedMesh mesh) {
|
||||
DrawCall(GPUInstancer<?> instancer, Material material, InstancedMeshPool.BufferedMesh mesh) {
|
||||
this.instancer = instancer;
|
||||
this.material = material;
|
||||
this.vao = new GlVertexArray();
|
||||
|
|
|
@ -19,16 +19,14 @@ import com.jozufozu.flywheel.backend.gl.buffer.GlBufferType;
|
|||
import com.jozufozu.flywheel.backend.gl.buffer.MappedBuffer;
|
||||
import com.jozufozu.flywheel.core.model.Mesh;
|
||||
|
||||
public class MeshPool {
|
||||
|
||||
public class InstancedMeshPool {
|
||||
private final VertexType vertexType;
|
||||
|
||||
private final Map<Mesh, BufferedMesh> meshes = new HashMap<>();
|
||||
private final List<BufferedMesh> allBuffered = new ArrayList<>();
|
||||
|
||||
private final List<BufferedMesh> pendingUpload = new ArrayList<>();
|
||||
|
||||
private final GlBuffer vbo;
|
||||
|
||||
private long byteSize;
|
||||
|
||||
private boolean dirty;
|
||||
|
@ -37,7 +35,7 @@ public class MeshPool {
|
|||
/**
|
||||
* Create a new mesh pool.
|
||||
*/
|
||||
public MeshPool(VertexType vertexType) {
|
||||
public InstancedMeshPool(VertexType vertexType) {
|
||||
this.vertexType = vertexType;
|
||||
int stride = vertexType.getStride();
|
||||
this.vbo = new GlBuffer(GlBufferType.ARRAY_BUFFER);
|
||||
|
@ -46,10 +44,10 @@ public class MeshPool {
|
|||
}
|
||||
|
||||
/**
|
||||
* Allocate a model in the arena.
|
||||
* Allocate a mesh in the arena.
|
||||
*
|
||||
* @param mesh The model to allocate.
|
||||
* @return A handle to the allocated model.
|
||||
* @param mesh The mesh to allocate.
|
||||
* @return A handle to the allocated mesh.
|
||||
*/
|
||||
public BufferedMesh alloc(Mesh mesh) {
|
||||
return meshes.computeIfAbsent(mesh, m -> {
|
||||
|
@ -57,13 +55,13 @@ public class MeshPool {
|
|||
throw new IllegalArgumentException("Mesh has wrong vertex type");
|
||||
}
|
||||
|
||||
BufferedMesh bufferedModel = new BufferedMesh(m, byteSize);
|
||||
byteSize += m.size();
|
||||
allBuffered.add(bufferedModel);
|
||||
pendingUpload.add(bufferedModel);
|
||||
BufferedMesh bufferedMesh = new BufferedMesh(m, byteSize);
|
||||
byteSize += bufferedMesh.size();
|
||||
allBuffered.add(bufferedMesh);
|
||||
pendingUpload.add(bufferedMesh);
|
||||
|
||||
dirty = true;
|
||||
return bufferedModel;
|
||||
return bufferedMesh;
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -90,7 +88,6 @@ public class MeshPool {
|
|||
}
|
||||
|
||||
private void processDeletions() {
|
||||
|
||||
// remove deleted meshes
|
||||
allBuffered.removeIf(bufferedMesh -> {
|
||||
boolean deleted = bufferedMesh.isDeleted();
|
||||
|
@ -100,16 +97,16 @@ public class MeshPool {
|
|||
return deleted;
|
||||
});
|
||||
|
||||
// re-evaluate first vertex for each model
|
||||
// re-evaluate first vertex for each mesh
|
||||
int byteIndex = 0;
|
||||
for (BufferedMesh model : allBuffered) {
|
||||
if (model.byteIndex != byteIndex) {
|
||||
pendingUpload.add(model);
|
||||
for (BufferedMesh mesh : allBuffered) {
|
||||
if (mesh.byteIndex != byteIndex) {
|
||||
pendingUpload.add(mesh);
|
||||
}
|
||||
|
||||
model.byteIndex = byteIndex;
|
||||
mesh.byteIndex = byteIndex;
|
||||
|
||||
byteIndex += model.mesh.size();
|
||||
byteIndex += mesh.size();
|
||||
}
|
||||
|
||||
this.byteSize = byteIndex;
|
||||
|
@ -130,14 +127,13 @@ public class MeshPool {
|
|||
long ptr = mapped.getPtr();
|
||||
|
||||
int byteIndex = 0;
|
||||
for (BufferedMesh model : allBuffered) {
|
||||
model.byteIndex = byteIndex;
|
||||
for (BufferedMesh mesh : allBuffered) {
|
||||
mesh.byteIndex = byteIndex;
|
||||
|
||||
model.buffer(ptr);
|
||||
mesh.buffer(ptr);
|
||||
|
||||
byteIndex += model.mesh.size();
|
||||
byteIndex += mesh.size();
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
Flywheel.LOGGER.error("Error uploading pooled meshes:", e);
|
||||
}
|
||||
|
@ -145,10 +141,12 @@ public class MeshPool {
|
|||
|
||||
private void uploadPending() {
|
||||
try (MappedBuffer mapped = vbo.map()) {
|
||||
long buffer = mapped.getPtr();
|
||||
for (BufferedMesh model : pendingUpload) {
|
||||
model.buffer(buffer);
|
||||
long ptr = mapped.getPtr();
|
||||
|
||||
for (BufferedMesh mesh : pendingUpload) {
|
||||
mesh.buffer(ptr);
|
||||
}
|
||||
|
||||
pendingUpload.clear();
|
||||
} catch (Exception e) {
|
||||
Flywheel.LOGGER.error("Error uploading pooled meshes:", e);
|
||||
|
@ -162,32 +160,41 @@ public class MeshPool {
|
|||
pendingUpload.clear();
|
||||
}
|
||||
|
||||
public VertexType getVertexType() {
|
||||
return vertexType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "MeshPool{" + "vertexType=" + vertexType + ", byteSize=" + byteSize + ", meshCount=" + meshes.size() + '}';
|
||||
return "InstancedMeshPool{" + "vertexType=" + vertexType + ", byteSize=" + byteSize + ", meshCount=" + meshes.size() + '}';
|
||||
}
|
||||
|
||||
public class BufferedMesh {
|
||||
|
||||
private final ElementBuffer ebo;
|
||||
private final Mesh mesh;
|
||||
private final ElementBuffer ebo;
|
||||
private long byteIndex;
|
||||
private boolean deleted;
|
||||
|
||||
private final Set<GlVertexArray> boundTo = new HashSet<>();
|
||||
|
||||
public BufferedMesh(Mesh mesh, long byteIndex) {
|
||||
private BufferedMesh(Mesh mesh, long byteIndex) {
|
||||
this.mesh = mesh;
|
||||
this.byteIndex = byteIndex;
|
||||
this.ebo = mesh.createEBO();
|
||||
}
|
||||
|
||||
private void buffer(long ptr) {
|
||||
mesh.write(ptr + byteIndex);
|
||||
|
||||
boundTo.clear();
|
||||
}
|
||||
|
||||
public void drawCall(GlVertexArray vao) {
|
||||
drawInstances(vao, 1);
|
||||
}
|
||||
|
||||
public void drawInstances(GlVertexArray vao, int instanceCount) {
|
||||
if (hasAnythingToRender()) {
|
||||
if (isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -196,50 +203,51 @@ public class MeshPool {
|
|||
draw(instanceCount);
|
||||
}
|
||||
|
||||
private boolean hasAnythingToRender() {
|
||||
private boolean isEmpty() {
|
||||
return mesh.isEmpty() || isDeleted();
|
||||
}
|
||||
|
||||
private void setup(GlVertexArray vao) {
|
||||
if (boundTo.add(vao)) {
|
||||
vao.enableArrays(getAttributeCount());
|
||||
vao.bindAttributes(InstancedMeshPool.this.vbo, 0, vertexType.getLayout(), byteIndex);
|
||||
}
|
||||
vao.bindElementArray(ebo.buffer);
|
||||
vao.bind();
|
||||
}
|
||||
|
||||
private void draw(int instanceCount) {
|
||||
if (instanceCount > 1) {
|
||||
GL32.glDrawElementsInstanced(GlPrimitive.TRIANGLES.glEnum, this.ebo.elementCount, this.ebo.eboIndexType.getGlEnum(), 0, instanceCount);
|
||||
GL32.glDrawElementsInstanced(GlPrimitive.TRIANGLES.glEnum, ebo.elementCount, ebo.eboIndexType.getGlEnum(), 0, instanceCount);
|
||||
} else {
|
||||
GL32.glDrawElements(GlPrimitive.TRIANGLES.glEnum, this.ebo.elementCount, this.ebo.eboIndexType.getGlEnum(), 0);
|
||||
GL32.glDrawElements(GlPrimitive.TRIANGLES.glEnum, ebo.elementCount, ebo.eboIndexType.getGlEnum(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
private void setup(GlVertexArray vao) {
|
||||
if (this.boundTo.add(vao)) {
|
||||
vao.enableArrays(getAttributeCount());
|
||||
vao.bindAttributes(MeshPool.this.vbo, 0, vertexType.getLayout(), this.byteIndex);
|
||||
}
|
||||
vao.bindElementArray(this.ebo.buffer);
|
||||
vao.bind();
|
||||
}
|
||||
|
||||
public boolean isDeleted() {
|
||||
return this.deleted;
|
||||
}
|
||||
|
||||
public void delete() {
|
||||
MeshPool.this.dirty = true;
|
||||
MeshPool.this.anyToRemove = true;
|
||||
this.deleted = true;
|
||||
deleted = true;
|
||||
InstancedMeshPool.this.dirty = true;
|
||||
InstancedMeshPool.this.anyToRemove = true;
|
||||
}
|
||||
|
||||
private void buffer(long ptr) {
|
||||
this.mesh.write(ptr + byteIndex);
|
||||
public Mesh getMesh() {
|
||||
return mesh;
|
||||
}
|
||||
|
||||
this.boundTo.clear();
|
||||
public int size() {
|
||||
return mesh.size();
|
||||
}
|
||||
|
||||
public VertexType getVertexType() {
|
||||
return vertexType;
|
||||
}
|
||||
|
||||
public int getAttributeCount() {
|
||||
return vertexType.getLayout().getAttributeCount();
|
||||
}
|
||||
|
||||
public VertexType getVertexType() {
|
||||
return vertexType;
|
||||
public boolean isDeleted() {
|
||||
return deleted;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -23,7 +23,7 @@ public class InstancingDrawManager {
|
|||
private final List<UninitializedModel> uninitializedModels = new ArrayList<>();
|
||||
private final List<GPUInstancer<?>> allInstancers = new ArrayList<>();
|
||||
private final Map<RenderStage, DrawSet> renderLists = new EnumMap<>(RenderStage.class);
|
||||
private final Map<VertexType, MeshPool> meshPools = new HashMap<>();
|
||||
private final Map<VertexType, InstancedMeshPool> meshPools = new HashMap<>();
|
||||
|
||||
public DrawSet get(RenderStage stage) {
|
||||
return renderLists.getOrDefault(stage, DrawSet.EMPTY);
|
||||
|
@ -49,7 +49,7 @@ public class InstancingDrawManager {
|
|||
|
||||
public void delete() {
|
||||
meshPools.values()
|
||||
.forEach(MeshPool::delete);
|
||||
.forEach(InstancedMeshPool::delete);
|
||||
meshPools.clear();
|
||||
|
||||
renderLists.values()
|
||||
|
@ -77,8 +77,8 @@ public class InstancingDrawManager {
|
|||
allInstancers.add(instancer);
|
||||
}
|
||||
|
||||
private MeshPool.BufferedMesh alloc(Mesh mesh) {
|
||||
return meshPools.computeIfAbsent(mesh.getVertexType(), MeshPool::new)
|
||||
private InstancedMeshPool.BufferedMesh alloc(Mesh mesh) {
|
||||
return meshPools.computeIfAbsent(mesh.getVertexType(), InstancedMeshPool::new)
|
||||
.alloc(mesh);
|
||||
}
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@ public final class Materials {
|
|||
}
|
||||
|
||||
DiffuseLightCalculator diffuseCalc = DiffuseLightCalculator.forLevel(level);
|
||||
for (int i = 0; i < vertexList.getVertexCount(); i++) {
|
||||
for (int i = 0; i < vertexList.vertexCount(); i++) {
|
||||
float diffuse = diffuseCalc.getDiffuse(vertexList.normalX(i), vertexList.normalY(i), vertexList.normalZ(i), true);
|
||||
vertexList.r(i, (byte) Mth.clamp((int) (Byte.toUnsignedInt(vertexList.r(i)) * diffuse), 0, 255));
|
||||
vertexList.g(i, (byte) Mth.clamp((int) (Byte.toUnsignedInt(vertexList.g(i)) * diffuse), 0, 255));
|
||||
|
|
|
@ -35,7 +35,7 @@ public class ModelPart implements Mesh {
|
|||
|
||||
vertexList = getVertexType().createVertexList();
|
||||
vertexList.ptr(ptr);
|
||||
vertexList.setVertexCount(vertexCount);
|
||||
vertexList.vertexCount(vertexCount);
|
||||
}
|
||||
|
||||
public static PartBuilder builder(String name, int sizeU, int sizeV) {
|
||||
|
|
|
@ -62,8 +62,8 @@ public class ModelUtil {
|
|||
ReusableVertexList dstList = dstVertexType.createVertexList();
|
||||
srcList.ptr(srcPtr);
|
||||
dstList.ptr(dstPtr);
|
||||
srcList.setVertexCount(vertexCount);
|
||||
dstList.setVertexCount(vertexCount);
|
||||
srcList.vertexCount(vertexCount);
|
||||
dstList.vertexCount(vertexCount);
|
||||
|
||||
srcList.writeAll(dstList);
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ public class SimpleMesh implements Mesh {
|
|||
|
||||
vertexList = getVertexType().createVertexList();
|
||||
vertexList.ptr(contents.ptr());
|
||||
vertexList.setVertexCount(vertexCount);
|
||||
vertexList.vertexCount(vertexCount);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -56,7 +56,7 @@ public class OrientedType implements StructType<OrientedPart> {
|
|||
Matrix3f normalMatrix = new Matrix3f(q);
|
||||
|
||||
int light = struct.getPackedLight();
|
||||
for (int i = 0; i < vertexList.getVertexCount(); i++) {
|
||||
for (int i = 0; i < vertexList.vertexCount(); i++) {
|
||||
pos.set(
|
||||
vertexList.x(i),
|
||||
vertexList.y(i),
|
||||
|
|
|
@ -43,7 +43,7 @@ public class TransformedType implements StructType<TransformedPart> {
|
|||
Vector3f normal = new Vector3f();
|
||||
|
||||
int light = struct.getPackedLight();
|
||||
for (int i = 0; i < vertexList.getVertexCount(); i++) {
|
||||
for (int i = 0; i < vertexList.vertexCount(); i++) {
|
||||
pos.set(
|
||||
vertexList.x(i),
|
||||
vertexList.y(i),
|
||||
|
|
|
@ -17,12 +17,12 @@ public abstract class AbstractVertexList implements ReusableVertexList {
|
|||
}
|
||||
|
||||
@Override
|
||||
public int getVertexCount() {
|
||||
public int vertexCount() {
|
||||
return vertexCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVertexCount(int vertexCount) {
|
||||
public void vertexCount(int vertexCount) {
|
||||
this.vertexCount = vertexCount;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -153,7 +153,7 @@ public class BlockVertexList extends AbstractVertexList {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void shiftPtr(int vertices) {
|
||||
ptr += vertices * STRIDE;
|
||||
public int vertexStride() {
|
||||
return STRIDE;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -203,7 +203,7 @@ public class InferredVertexListImpl extends AbstractVertexList implements Reusab
|
|||
}
|
||||
|
||||
@Override
|
||||
public void shiftPtr(int vertices) {
|
||||
ptr += vertices * stride;
|
||||
public int vertexStride() {
|
||||
return stride;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -148,7 +148,7 @@ public class PosTexNormalVertexList extends AbstractVertexList {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void shiftPtr(int vertices) {
|
||||
ptr += vertices * STRIDE;
|
||||
public int vertexStride() {
|
||||
return STRIDE;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue