mirror of
https://github.com/Jozufozu/Flywheel.git
synced 2024-12-27 23:47:09 +01:00
Errors and pools
- GlException and error checking WIP - Put all models within a material in a ModelPool - Instancers directly accept IModels - ModelPools are very WIP
This commit is contained in:
parent
f4f6087e08
commit
2137f9e46d
20 changed files with 513 additions and 69 deletions
|
@ -1,6 +1,8 @@
|
|||
package com.jozufozu.flywheel.backend.gl.attrib;
|
||||
|
||||
import org.lwjgl.opengl.GL20;
|
||||
import org.lwjgl.opengl.GL44;
|
||||
import org.lwjgl.opengl.GL46;
|
||||
|
||||
import com.jozufozu.flywheel.backend.gl.GlNumericType;
|
||||
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
package com.jozufozu.flywheel.backend.gl.buffer;
|
||||
|
||||
import com.jozufozu.flywheel.backend.Backend;
|
||||
import com.jozufozu.flywheel.backend.gl.error.GlError;
|
||||
import com.jozufozu.flywheel.backend.gl.error.GlException;
|
||||
import com.jozufozu.flywheel.util.StringUtil;
|
||||
|
||||
public class MappedBufferRange extends MappedBuffer {
|
||||
|
||||
|
@ -27,6 +30,12 @@ public class MappedBufferRange extends MappedBuffer {
|
|||
protected void checkAndMap() {
|
||||
if (!mapped) {
|
||||
setInternal(Backend.getInstance().compat.mapBufferRange.mapBuffer(owner.type, offset, length, access));
|
||||
|
||||
GlError error = GlError.poll();
|
||||
|
||||
if (error != null) {
|
||||
throw new GlException(error, StringUtil.args("mapBufferRange", owner.type, offset, length, access));
|
||||
}
|
||||
mapped = true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,10 @@ package com.jozufozu.flywheel.backend.gl.buffer;
|
|||
|
||||
import org.lwjgl.opengl.GL15;
|
||||
|
||||
import com.jozufozu.flywheel.backend.gl.error.GlError;
|
||||
import com.jozufozu.flywheel.backend.gl.error.GlException;
|
||||
import com.jozufozu.flywheel.util.StringUtil;
|
||||
|
||||
public class MappedFullBuffer extends MappedBuffer {
|
||||
|
||||
MappedBufferUsage usage;
|
||||
|
@ -15,6 +19,13 @@ public class MappedFullBuffer extends MappedBuffer {
|
|||
protected void checkAndMap() {
|
||||
if (!mapped) {
|
||||
setInternal(GL15.glMapBuffer(owner.type.glEnum, usage.glEnum));
|
||||
|
||||
GlError error = GlError.poll();
|
||||
|
||||
if (error != null) {
|
||||
throw new GlException(error, StringUtil.args("mapBuffer", owner.type, usage));
|
||||
}
|
||||
|
||||
mapped = true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,9 @@ import java.nio.ByteBuffer;
|
|||
|
||||
import com.jozufozu.flywheel.backend.Backend;
|
||||
import com.jozufozu.flywheel.backend.gl.GlFence;
|
||||
import com.jozufozu.flywheel.backend.gl.error.GlError;
|
||||
import com.jozufozu.flywheel.backend.gl.error.GlException;
|
||||
import com.jozufozu.flywheel.util.StringUtil;
|
||||
|
||||
|
||||
public class PersistentGlBuffer extends GlBuffer {
|
||||
|
@ -35,11 +38,19 @@ public class PersistentGlBuffer extends GlBuffer {
|
|||
if (buffer != null) {
|
||||
deleteInternal(handle());
|
||||
_create();
|
||||
|
||||
bind();
|
||||
}
|
||||
|
||||
fence.clear();
|
||||
|
||||
Backend.getInstance().compat.bufferStorage.bufferStorage(type.glEnum, size, flags);
|
||||
Backend.getInstance().compat.bufferStorage.bufferStorage(type, size, flags);
|
||||
|
||||
GlError error = GlError.poll();
|
||||
|
||||
if (error != null) {
|
||||
throw new GlException(error, StringUtil.args("bufferStorage", type, size, flags));
|
||||
}
|
||||
|
||||
buffer = new PersistentMappedBuffer(this);
|
||||
}
|
||||
|
|
|
@ -2,25 +2,42 @@ package com.jozufozu.flywheel.backend.gl.buffer;
|
|||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import org.lwjgl.opengl.GL15;
|
||||
import org.lwjgl.opengl.GL44;
|
||||
import org.lwjgl.opengl.GL46;
|
||||
|
||||
import com.jozufozu.flywheel.backend.Backend;
|
||||
import com.jozufozu.flywheel.backend.gl.error.GlError;
|
||||
import com.jozufozu.flywheel.backend.gl.error.GlException;
|
||||
import com.jozufozu.flywheel.util.StringUtil;
|
||||
|
||||
public class PersistentMappedBuffer extends MappedBuffer {
|
||||
|
||||
private final long offset;
|
||||
private final long length;
|
||||
PersistentGlBuffer owner;
|
||||
|
||||
public PersistentMappedBuffer(PersistentGlBuffer buffer) {
|
||||
super(buffer);
|
||||
owner = buffer;
|
||||
offset = 0;
|
||||
length = owner.size;
|
||||
|
||||
ByteBuffer byteBuffer = Backend.getInstance().compat.mapBufferRange.mapBuffer(owner.type, 0, owner.size, owner.flags);
|
||||
ByteBuffer byteBuffer = Backend.getInstance().compat.mapBufferRange.mapBuffer(owner.type, offset, length, owner.flags);
|
||||
|
||||
GlError error = GlError.poll();
|
||||
|
||||
if (error != null) {
|
||||
throw new GlException(error, StringUtil.args("mapBuffer", owner.type, offset, length, owner.flags));
|
||||
}
|
||||
|
||||
setInternal(byteBuffer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MappedBuffer position(int p) {
|
||||
if (p < offset || p >= offset + length) {
|
||||
throw new IndexOutOfBoundsException("Index " + p + " is not mapped");
|
||||
}
|
||||
return super.position(p - (int) offset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush() {
|
||||
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
package com.jozufozu.flywheel.backend.gl.error;
|
||||
|
||||
import org.lwjgl.opengl.GL20;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
|
||||
public enum GlError {
|
||||
INVALID_ENUM(GL20.GL_INVALID_ENUM),
|
||||
INVALID_VALUE(GL20.GL_INVALID_VALUE),
|
||||
INVALID_OPERATION(GL20.GL_INVALID_OPERATION),
|
||||
INVALID_FRAMEBUFFER_OPERATION(GL30.GL_INVALID_FRAMEBUFFER_OPERATION),
|
||||
OUT_OF_MEMORY(GL20.GL_OUT_OF_MEMORY),
|
||||
STACK_UNDERFLOW(GL20.GL_STACK_UNDERFLOW),
|
||||
STACK_OVERFLOW(GL20.GL_STACK_OVERFLOW),
|
||||
;
|
||||
|
||||
private static final Int2ObjectMap<GlError> errorLookup = new Int2ObjectArrayMap<>();
|
||||
|
||||
static {
|
||||
errorLookup.defaultReturnValue(null);
|
||||
for (GlError value : values()) {
|
||||
errorLookup.put(value.glEnum, value);
|
||||
}
|
||||
}
|
||||
|
||||
final int glEnum;
|
||||
|
||||
GlError(int glEnum) {
|
||||
this.glEnum = glEnum;
|
||||
}
|
||||
|
||||
|
||||
public static GlError poll() {
|
||||
return errorLookup.get(GL20.glGetError());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
package com.jozufozu.flywheel.backend.gl.error;
|
||||
|
||||
public class GlException extends RuntimeException {
|
||||
|
||||
final GlError errorCode;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String s = getClass().getName();
|
||||
String message = getLocalizedMessage();
|
||||
String withCode = s + ": " + errorCode;
|
||||
return (message != null) ? (withCode + ": " + message) : withCode;
|
||||
}
|
||||
|
||||
public GlException(GlError errorCode) {
|
||||
this.errorCode = errorCode;
|
||||
}
|
||||
|
||||
public GlException(GlError errorCode, String message) {
|
||||
super(message);
|
||||
this.errorCode = errorCode;
|
||||
}
|
||||
|
||||
public GlException(GlError errorCode, String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
this.errorCode = errorCode;
|
||||
}
|
||||
|
||||
public GlException(GlError errorCode, Throwable cause) {
|
||||
super(cause);
|
||||
this.errorCode = errorCode;
|
||||
}
|
||||
|
||||
public GlException(GlError errorCode, String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
|
||||
super(message, cause, enableSuppression, writableStackTrace);
|
||||
this.errorCode = errorCode;
|
||||
}
|
||||
}
|
|
@ -4,6 +4,8 @@ import org.lwjgl.opengl.ARBBufferStorage;
|
|||
import org.lwjgl.opengl.GL44;
|
||||
import org.lwjgl.opengl.GLCapabilities;
|
||||
|
||||
import com.jozufozu.flywheel.backend.gl.buffer.GlBufferType;
|
||||
|
||||
public enum BufferStorage implements GlVersioned {
|
||||
|
||||
GL44CORE {
|
||||
|
@ -13,8 +15,8 @@ public enum BufferStorage implements GlVersioned {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void bufferStorage(int target, long size, int flags) {
|
||||
GL44.glBufferStorage(target, size, flags);
|
||||
public void bufferStorage(GlBufferType target, long size, int flags) {
|
||||
GL44.glBufferStorage(target.glEnum, size, flags);
|
||||
}
|
||||
},
|
||||
ARB {
|
||||
|
@ -24,8 +26,8 @@ public enum BufferStorage implements GlVersioned {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void bufferStorage(int target, long size, int flags) {
|
||||
ARBBufferStorage.glBufferStorage(target, size, flags);
|
||||
public void bufferStorage(GlBufferType target, long size, int flags) {
|
||||
ARBBufferStorage.glBufferStorage(target.glEnum, size, flags);
|
||||
}
|
||||
},
|
||||
UNSUPPORTED {
|
||||
|
@ -35,10 +37,10 @@ public enum BufferStorage implements GlVersioned {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void bufferStorage(int target, long size, int flags) {
|
||||
public void bufferStorage(GlBufferType target, long size, int flags) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
};
|
||||
|
||||
public abstract void bufferStorage(int target, long size, int flags);
|
||||
public abstract void bufferStorage(GlBufferType target, long size, int flags);
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ import org.lwjgl.system.MemoryUtil;
|
|||
import com.jozufozu.flywheel.backend.gl.versioned.framebuffer.Blit;
|
||||
import com.jozufozu.flywheel.backend.gl.versioned.framebuffer.Framebuffer;
|
||||
import com.jozufozu.flywheel.backend.gl.versioned.instancing.DrawInstanced;
|
||||
import com.jozufozu.flywheel.backend.gl.versioned.instancing.BaseVertex;
|
||||
import com.jozufozu.flywheel.backend.gl.versioned.instancing.InstancedArrays;
|
||||
import com.jozufozu.flywheel.backend.gl.versioned.instancing.VertexArrayObject;
|
||||
|
||||
|
@ -32,6 +33,7 @@ public class GlCompat {
|
|||
public final BufferStorage bufferStorage;
|
||||
|
||||
public final RGPixelFormat pixelFormat;
|
||||
public final BaseVertex baseVertex;
|
||||
|
||||
public GlCompat(GLCapabilities caps) {
|
||||
mapBufferRange = getLatest(MapBufferRange.class, caps);
|
||||
|
@ -39,6 +41,7 @@ public class GlCompat {
|
|||
vao = getLatest(VertexArrayObject.class, caps);
|
||||
instancedArrays = getLatest(InstancedArrays.class, caps);
|
||||
drawInstanced = getLatest(DrawInstanced.class, caps);
|
||||
baseVertex = getLatest(BaseVertex.class, caps);
|
||||
blit = getLatest(Blit.class, caps);
|
||||
fbo = getLatest(Framebuffer.class, caps);
|
||||
bufferStorage = getLatest(BufferStorage.class, caps);
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
package com.jozufozu.flywheel.backend.gl.versioned.instancing;
|
||||
|
||||
import org.lwjgl.opengl.ARBDrawElementsBaseVertex;
|
||||
import org.lwjgl.opengl.ARBDrawInstanced;
|
||||
import org.lwjgl.opengl.EXTDrawInstanced;
|
||||
import org.lwjgl.opengl.GL32;
|
||||
import org.lwjgl.opengl.GLCapabilities;
|
||||
|
||||
import com.jozufozu.flywheel.backend.gl.GlNumericType;
|
||||
import com.jozufozu.flywheel.backend.gl.GlPrimitive;
|
||||
import com.jozufozu.flywheel.backend.gl.versioned.GlVersioned;
|
||||
|
||||
public enum BaseVertex implements GlVersioned {
|
||||
GL31_CORE {
|
||||
@Override
|
||||
public boolean supported(GLCapabilities caps) {
|
||||
return caps.OpenGL31;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawElementsInstancedBaseVertex(GlPrimitive mode, int elementCount, GlNumericType type, long indices, int instanceCount, int baseVertex) {
|
||||
GL32.glDrawElementsInstancedBaseVertex(mode.glEnum, elementCount, type.getGlEnum(), indices, instanceCount, baseVertex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawElementsBaseVertex(GlPrimitive mode, int elementCount, GlNumericType type, long indices, int baseVertex) {
|
||||
GL32.glDrawElementsBaseVertex(mode.glEnum, elementCount, type.getGlEnum(), indices, baseVertex);
|
||||
}
|
||||
},
|
||||
ARB {
|
||||
@Override
|
||||
public boolean supported(GLCapabilities caps) {
|
||||
return caps.GL_ARB_draw_elements_base_vertex;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawElementsInstancedBaseVertex(GlPrimitive mode, int elementCount, GlNumericType type, long indices, int instanceCount, int baseVertex) {
|
||||
ARBDrawElementsBaseVertex.glDrawElementsInstancedBaseVertex(mode.glEnum, elementCount, type.getGlEnum(), indices, instanceCount, baseVertex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawElementsBaseVertex(GlPrimitive mode, int elementCount, GlNumericType type, long indices, int baseVertex) {
|
||||
ARBDrawElementsBaseVertex.glDrawElementsBaseVertex(mode.glEnum, elementCount, type.getGlEnum(), indices, baseVertex);
|
||||
}
|
||||
},
|
||||
UNSUPPORTED {
|
||||
@Override
|
||||
public boolean supported(GLCapabilities caps) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
public void drawElementsInstancedBaseVertex(GlPrimitive mode, int elementCount, GlNumericType type, long indices, int instanceCount, int baseVertex) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
|
||||
public void drawElementsBaseVertex(GlPrimitive mode, int elementCount, GlNumericType type, long indices, int baseVertex) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
|
@ -3,6 +3,7 @@ package com.jozufozu.flywheel.backend.gl.versioned.instancing;
|
|||
import org.lwjgl.opengl.ARBDrawInstanced;
|
||||
import org.lwjgl.opengl.EXTDrawInstanced;
|
||||
import org.lwjgl.opengl.GL31;
|
||||
import org.lwjgl.opengl.GL46;
|
||||
import org.lwjgl.opengl.GLCapabilities;
|
||||
|
||||
import com.jozufozu.flywheel.backend.gl.GlNumericType;
|
||||
|
@ -10,7 +11,7 @@ import com.jozufozu.flywheel.backend.gl.GlPrimitive;
|
|||
import com.jozufozu.flywheel.backend.gl.versioned.GlVersioned;
|
||||
|
||||
public enum DrawInstanced implements GlVersioned {
|
||||
GL31_DRAW_INSTANCED {
|
||||
GL31_CORE {
|
||||
@Override
|
||||
public boolean supported(GLCapabilities caps) {
|
||||
return caps.OpenGL31;
|
||||
|
@ -26,7 +27,7 @@ public enum DrawInstanced implements GlVersioned {
|
|||
GL31.glDrawElementsInstanced(mode.glEnum, elementCount, type.getGlEnum(), indices, primcount);
|
||||
}
|
||||
},
|
||||
ARB_DRAW_INSTANCED {
|
||||
ARB {
|
||||
@Override
|
||||
public boolean supported(GLCapabilities caps) {
|
||||
return caps.GL_ARB_draw_instanced;
|
||||
|
@ -42,7 +43,7 @@ public enum DrawInstanced implements GlVersioned {
|
|||
ARBDrawInstanced.glDrawElementsInstancedARB(mode.glEnum, elementCount, type.getGlEnum(), indices, primcount);
|
||||
}
|
||||
},
|
||||
EXT_DRAW_INSTANCED {
|
||||
EXT {
|
||||
@Override
|
||||
public boolean supported(GLCapabilities caps) {
|
||||
return caps.GL_EXT_draw_instanced;
|
||||
|
|
|
@ -2,7 +2,6 @@ package com.jozufozu.flywheel.backend.instancing;
|
|||
|
||||
import java.util.ArrayList;
|
||||
import java.util.BitSet;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import com.jozufozu.flywheel.backend.Backend;
|
||||
import com.jozufozu.flywheel.backend.gl.GlVertexArray;
|
||||
|
@ -11,8 +10,8 @@ import com.jozufozu.flywheel.backend.gl.buffer.GlBuffer;
|
|||
import com.jozufozu.flywheel.backend.gl.buffer.GlBufferType;
|
||||
import com.jozufozu.flywheel.backend.gl.buffer.MappedBuffer;
|
||||
import com.jozufozu.flywheel.backend.material.MaterialSpec;
|
||||
import com.jozufozu.flywheel.backend.model.ModelPool;
|
||||
import com.jozufozu.flywheel.backend.model.IBufferedModel;
|
||||
import com.jozufozu.flywheel.backend.model.IndexedModel;
|
||||
import com.jozufozu.flywheel.core.model.IModel;
|
||||
import com.jozufozu.flywheel.util.AttribUtil;
|
||||
|
||||
|
@ -36,25 +35,27 @@ import com.jozufozu.flywheel.util.AttribUtil;
|
|||
*/
|
||||
public class Instancer<D extends InstanceData> {
|
||||
|
||||
protected final Supplier<IModel> gen;
|
||||
protected final VertexFormat instanceFormat;
|
||||
protected final IInstanceFactory<D> factory;
|
||||
private final ModelPool modelAllocator;
|
||||
private final IModel modelData;
|
||||
private final VertexFormat instanceFormat;
|
||||
private final IInstanceFactory<D> factory;
|
||||
|
||||
protected IBufferedModel model;
|
||||
protected GlVertexArray vao;
|
||||
protected GlBuffer instanceVBO;
|
||||
protected int glBufferSize = -1;
|
||||
protected int glInstanceCount = 0;
|
||||
private IBufferedModel model;
|
||||
private GlVertexArray vao;
|
||||
private GlBuffer instanceVBO;
|
||||
private int glBufferSize = -1;
|
||||
private int glInstanceCount = 0;
|
||||
private boolean deleted;
|
||||
private boolean initialized;
|
||||
|
||||
protected final ArrayList<D> data = new ArrayList<>();
|
||||
private final ArrayList<D> data = new ArrayList<>();
|
||||
|
||||
boolean anyToRemove;
|
||||
boolean anyToUpdate;
|
||||
|
||||
public Instancer(Supplier<IModel> model, MaterialSpec<D> spec) {
|
||||
this.gen = model;
|
||||
public Instancer(ModelPool modelAllocator, IModel model, MaterialSpec<D> spec) {
|
||||
this.modelAllocator = modelAllocator;
|
||||
this.modelData = model;
|
||||
this.factory = spec.getInstanceFactory();
|
||||
this.instanceFormat = spec.getInstanceFormat();
|
||||
}
|
||||
|
@ -80,7 +81,6 @@ public class Instancer<D extends InstanceData> {
|
|||
}
|
||||
|
||||
public void render() {
|
||||
if (!isInitialized()) init();
|
||||
if (invalid()) return;
|
||||
|
||||
vao.bind();
|
||||
|
@ -98,28 +98,28 @@ public class Instancer<D extends InstanceData> {
|
|||
return deleted || model == null;
|
||||
}
|
||||
|
||||
private void init() {
|
||||
public void init() {
|
||||
if (isInitialized()) return;
|
||||
|
||||
initialized = true;
|
||||
IModel iModel = gen.get();
|
||||
|
||||
if (iModel.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
model = new IndexedModel(iModel);
|
||||
vao = new GlVertexArray();
|
||||
instanceVBO = GlBuffer.requestPersistent(GlBufferType.ARRAY_BUFFER);
|
||||
|
||||
model = modelAllocator.alloc(modelData)
|
||||
.setReallocCallback(arenaModel -> {
|
||||
vao.bind();
|
||||
|
||||
model.setupState();
|
||||
|
||||
vao.unbind();
|
||||
});
|
||||
|
||||
vao.bind();
|
||||
|
||||
// bind the model's vbo to our vao
|
||||
model.setupState();
|
||||
|
||||
instanceVBO = GlBuffer.requestPersistent(GlBufferType.ARRAY_BUFFER);
|
||||
AttribUtil.enableArrays(model.getAttributeCount() + instanceFormat.getAttributeCount());
|
||||
|
||||
vao.unbind();
|
||||
|
||||
model.clearState();
|
||||
}
|
||||
|
||||
public boolean isInitialized() {
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package com.jozufozu.flywheel.backend.material;
|
||||
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import com.google.common.cache.Cache;
|
||||
|
@ -9,6 +8,7 @@ import com.google.common.cache.CacheBuilder;
|
|||
import com.jozufozu.flywheel.backend.RenderWork;
|
||||
import com.jozufozu.flywheel.backend.instancing.InstanceData;
|
||||
import com.jozufozu.flywheel.backend.instancing.Instancer;
|
||||
import com.jozufozu.flywheel.backend.model.ModelPool;
|
||||
import com.jozufozu.flywheel.core.PartialModel;
|
||||
import com.jozufozu.flywheel.core.model.BlockModel;
|
||||
import com.jozufozu.flywheel.core.model.IModel;
|
||||
|
@ -25,12 +25,14 @@ import net.minecraft.util.Direction;
|
|||
*/
|
||||
public class InstanceMaterial<D extends InstanceData> {
|
||||
|
||||
final ModelPool modelPool;
|
||||
protected final Cache<Object, Instancer<D>> models;
|
||||
protected final MaterialSpec<D> spec;
|
||||
|
||||
public InstanceMaterial(MaterialSpec<D> spec) {
|
||||
this.spec = spec;
|
||||
|
||||
modelPool = new ModelPool(spec.getModelFormat(), spec.getModelFormat().getStride() * 64);
|
||||
this.models = CacheBuilder.newBuilder()
|
||||
.removalListener(notification -> {
|
||||
Instancer<?> instancer = (Instancer<?>) notification.getValue();
|
||||
|
@ -48,7 +50,7 @@ public class InstanceMaterial<D extends InstanceData> {
|
|||
*/
|
||||
public Instancer<D> model(Object key, Supplier<IModel> modelSupplier) {
|
||||
try {
|
||||
return models.get(key, () -> new Instancer<>(modelSupplier, spec));
|
||||
return models.get(key, () -> new Instancer<>(modelPool, modelSupplier.get(), spec));
|
||||
} catch (ExecutionException e) {
|
||||
throw new RuntimeException("error creating instancer", e);
|
||||
}
|
||||
|
@ -79,6 +81,7 @@ public class InstanceMaterial<D extends InstanceData> {
|
|||
|
||||
public void delete() {
|
||||
models.invalidateAll();
|
||||
modelPool.delete();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -90,11 +93,4 @@ public class InstanceMaterial<D extends InstanceData> {
|
|||
.forEach(Instancer::clear);
|
||||
}
|
||||
|
||||
public void forEachInstancer(Consumer<Instancer<D>> f) {
|
||||
for (Instancer<D> model : models.asMap()
|
||||
.values()) {
|
||||
f.accept(model);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -17,7 +17,6 @@ import com.jozufozu.flywheel.core.shader.WorldProgram;
|
|||
import com.jozufozu.flywheel.util.WeakHashSet;
|
||||
|
||||
import net.minecraft.client.renderer.ActiveRenderInfo;
|
||||
import net.minecraft.client.renderer.RenderType;
|
||||
import net.minecraft.inventory.container.PlayerContainer;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package com.jozufozu.flywheel.backend.material;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
|
@ -24,6 +25,14 @@ public class MaterialRenderer<P extends WorldProgram> {
|
|||
public void render(Matrix4f viewProjection, double camX, double camY, double camZ) {
|
||||
if (material.nothingToRender()) return;
|
||||
|
||||
Collection<? extends Instancer<?>> instancers = material.models.asMap()
|
||||
.values();
|
||||
|
||||
// initialize all uninitialized instancers...
|
||||
instancers.forEach(Instancer::init);
|
||||
// ...and then flush the model arena in case anything was marked for upload
|
||||
material.modelPool.flush();
|
||||
|
||||
P program = this.program.get();
|
||||
|
||||
program.bind();
|
||||
|
@ -32,7 +41,7 @@ public class MaterialRenderer<P extends WorldProgram> {
|
|||
|
||||
setupFunc.accept(program);
|
||||
|
||||
material.forEachInstancer(Instancer::render);
|
||||
instancers.forEach(Instancer::render);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -2,6 +2,8 @@ package com.jozufozu.flywheel.backend.model;
|
|||
|
||||
import static org.lwjgl.opengl.GL20.glDrawArrays;
|
||||
|
||||
import org.lwjgl.opengl.GL44;
|
||||
|
||||
import com.jozufozu.flywheel.backend.Backend;
|
||||
import com.jozufozu.flywheel.backend.gl.GlPrimitive;
|
||||
import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat;
|
||||
|
@ -37,6 +39,10 @@ public class BufferedModel implements IBufferedModel {
|
|||
vbo.unbind();
|
||||
}
|
||||
|
||||
public boolean isDeleted() {
|
||||
return deleted;
|
||||
}
|
||||
|
||||
public VertexFormat getFormat() {
|
||||
return model.format();
|
||||
}
|
||||
|
@ -45,10 +51,6 @@ public class BufferedModel implements IBufferedModel {
|
|||
return model.vertexCount();
|
||||
}
|
||||
|
||||
public boolean valid() {
|
||||
return getVertexCount() > 0 && !deleted;
|
||||
}
|
||||
|
||||
/**
|
||||
* The VBO/VAO should be bound externally.
|
||||
*/
|
||||
|
|
|
@ -8,8 +8,6 @@ public interface IBufferedModel {
|
|||
|
||||
int getVertexCount();
|
||||
|
||||
boolean valid();
|
||||
|
||||
/**
|
||||
* The VBO/VAO should be bound externally.
|
||||
*/
|
||||
|
@ -24,8 +22,14 @@ public interface IBufferedModel {
|
|||
*/
|
||||
void drawInstances(int instanceCount);
|
||||
|
||||
boolean isDeleted();
|
||||
|
||||
void delete();
|
||||
|
||||
default boolean valid() {
|
||||
return getVertexCount() > 0 && !isDeleted();
|
||||
}
|
||||
|
||||
default int getAttributeCount() {
|
||||
return getFormat().getAttributeCount();
|
||||
}
|
||||
|
|
229
src/main/java/com/jozufozu/flywheel/backend/model/ModelPool.java
Normal file
229
src/main/java/com/jozufozu/flywheel/backend/model/ModelPool.java
Normal file
|
@ -0,0 +1,229 @@
|
|||
package com.jozufozu.flywheel.backend.model;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.jozufozu.flywheel.backend.Backend;
|
||||
import com.jozufozu.flywheel.backend.gl.GlPrimitive;
|
||||
import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat;
|
||||
import com.jozufozu.flywheel.backend.gl.buffer.GlBuffer;
|
||||
import com.jozufozu.flywheel.backend.gl.buffer.GlBufferType;
|
||||
import com.jozufozu.flywheel.backend.gl.buffer.MappedBuffer;
|
||||
import com.jozufozu.flywheel.backend.gl.buffer.MappedGlBuffer;
|
||||
import com.jozufozu.flywheel.core.model.IModel;
|
||||
import com.jozufozu.flywheel.util.AttribUtil;
|
||||
import com.jozufozu.flywheel.util.StringUtil;
|
||||
|
||||
public class ModelPool {
|
||||
|
||||
protected final VertexFormat format;
|
||||
|
||||
private final List<PooledModel> models = new ArrayList<>();
|
||||
|
||||
private final List<PooledModel> pendingUpload = new ArrayList<>();
|
||||
|
||||
private final GlBuffer vbo;
|
||||
private int bufferSize;
|
||||
|
||||
private int vertices;
|
||||
|
||||
private boolean dirty;
|
||||
private boolean anyToRemove;
|
||||
|
||||
public ModelPool(VertexFormat format, int initialSize) {
|
||||
this.format = format;
|
||||
bufferSize = initialSize;
|
||||
|
||||
vbo = new MappedGlBuffer(GlBufferType.ARRAY_BUFFER);
|
||||
|
||||
vbo.bind();
|
||||
vbo.alloc(bufferSize);
|
||||
vbo.unbind();
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocate a model in the arena.
|
||||
*
|
||||
* @param model The model to allocate.
|
||||
* @return A handle to the allocated model.
|
||||
*/
|
||||
public PooledModel alloc(IModel model) {
|
||||
PooledModel bufferedModel = new PooledModel(model, vertices);
|
||||
vertices += model.vertexCount();
|
||||
models.add(bufferedModel);
|
||||
pendingUpload.add(bufferedModel);
|
||||
|
||||
setDirty();
|
||||
return bufferedModel;
|
||||
}
|
||||
|
||||
public void flush() {
|
||||
if (dirty) {
|
||||
if (anyToRemove) processDeletions();
|
||||
|
||||
vbo.bind();
|
||||
if (realloc()) {
|
||||
uploadAll();
|
||||
} else {
|
||||
uploadPending();
|
||||
}
|
||||
vbo.unbind();
|
||||
|
||||
dirty = false;
|
||||
pendingUpload.clear();
|
||||
}
|
||||
}
|
||||
|
||||
private void processDeletions() {
|
||||
|
||||
// remove deleted models
|
||||
models.removeIf(PooledModel::isDeleted);
|
||||
|
||||
// re-evaluate first vertex for each model
|
||||
int vertices = 0;
|
||||
for (PooledModel model : models) {
|
||||
if (model.first != vertices)
|
||||
pendingUpload.add(model);
|
||||
|
||||
model.first = vertices;
|
||||
|
||||
vertices += model.getVertexCount();
|
||||
}
|
||||
|
||||
this.vertices = vertices;
|
||||
this.anyToRemove = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assumes vbo is bound.
|
||||
*
|
||||
* @return true if the buffer was reallocated
|
||||
*/
|
||||
private boolean realloc() {
|
||||
int neededSize = vertices * format.getStride();
|
||||
if (neededSize > bufferSize) {
|
||||
bufferSize = neededSize + 128;
|
||||
vbo.alloc(bufferSize);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void uploadAll() {
|
||||
MappedBuffer buffer = vbo.getBuffer(0, bufferSize);
|
||||
|
||||
for (PooledModel model : models) {
|
||||
model.model.buffer(buffer);
|
||||
if (model.callback != null)
|
||||
model.callback.invoke(model);
|
||||
}
|
||||
|
||||
buffer.flush();
|
||||
}
|
||||
|
||||
private void uploadPending() {
|
||||
MappedBuffer buffer = vbo.getBuffer(0, bufferSize);
|
||||
|
||||
int stride = format.getStride();
|
||||
for (PooledModel model : pendingUpload) {
|
||||
int pos = model.first * stride;
|
||||
buffer.position(pos);
|
||||
model.model.buffer(buffer);
|
||||
if (model.callback != null)
|
||||
model.callback.invoke(model);
|
||||
}
|
||||
pendingUpload.clear();
|
||||
|
||||
buffer.flush();
|
||||
}
|
||||
|
||||
private void setDirty() {
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
public void delete() {
|
||||
vbo.delete();
|
||||
}
|
||||
|
||||
public class PooledModel implements IBufferedModel {
|
||||
|
||||
private final ElementBuffer ebo;
|
||||
private Callback callback;
|
||||
|
||||
private final IModel model;
|
||||
private int first;
|
||||
|
||||
private boolean remove;
|
||||
|
||||
public PooledModel(IModel model, int first) {
|
||||
this.model = model;
|
||||
this.first = first;
|
||||
ebo = model.createEBO();
|
||||
}
|
||||
|
||||
@Override
|
||||
public VertexFormat getFormat() {
|
||||
return model.format();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getVertexCount() {
|
||||
return model.vertexCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setupState() {
|
||||
vbo.bind();
|
||||
ebo.bind();
|
||||
AttribUtil.enableArrays(getAttributeCount());
|
||||
getFormat().vertexAttribPointers(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearState() {
|
||||
AttribUtil.disableArrays(getAttributeCount());
|
||||
ebo.unbind();
|
||||
vbo.unbind();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawCall() {
|
||||
Backend.getInstance().compat.baseVertex.drawElementsBaseVertex(GlPrimitive.TRIANGLES, ebo.elementCount, ebo.eboIndexType, 0, first);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawInstances(int instanceCount) {
|
||||
if (!valid()) return;
|
||||
|
||||
ebo.bind();
|
||||
|
||||
Backend.log.info(StringUtil.args("drawElementsInstancedBaseVertex", GlPrimitive.TRIANGLES, ebo.elementCount, ebo.eboIndexType, 0, instanceCount, first));
|
||||
|
||||
Backend.getInstance().compat.baseVertex.drawElementsInstancedBaseVertex(GlPrimitive.TRIANGLES, ebo.elementCount, ebo.eboIndexType, 0, instanceCount, first);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDeleted() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete() {
|
||||
setDirty();
|
||||
anyToRemove = true;
|
||||
remove = true;
|
||||
}
|
||||
|
||||
public PooledModel setReallocCallback(Callback callback) {
|
||||
this.callback = callback;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface Callback {
|
||||
void invoke(PooledModel arenaModel);
|
||||
}
|
||||
}
|
|
@ -30,14 +30,14 @@ import net.minecraftforge.fml.common.Mod;
|
|||
@Mod.EventBusSubscriber(Dist.CLIENT)
|
||||
public class QuadConverter {
|
||||
|
||||
public static final int STARTING_CAPACITY = 42;
|
||||
public static final int STARTING_CAPACITY = 42; // 255 / 6 = 42
|
||||
|
||||
private static QuadConverter INSTANCE;
|
||||
|
||||
@Nonnull
|
||||
public static QuadConverter getInstance() {
|
||||
if (INSTANCE == null) {
|
||||
INSTANCE = new QuadConverter(STARTING_CAPACITY); // 255 / 6 = 42
|
||||
INSTANCE = new QuadConverter(STARTING_CAPACITY);
|
||||
}
|
||||
|
||||
return INSTANCE;
|
||||
|
@ -157,14 +157,14 @@ public class QuadConverter {
|
|||
* </pre>
|
||||
*/
|
||||
private static GlNumericType getSmallestIndexType(int indexCount) {
|
||||
indexCount = indexCount >>> 8;
|
||||
if (indexCount == 0) {
|
||||
return GlNumericType.UBYTE;
|
||||
}
|
||||
indexCount = indexCount >>> 8;
|
||||
if (indexCount == 0) {
|
||||
return GlNumericType.USHORT;
|
||||
}
|
||||
// indexCount = indexCount >>> 8;
|
||||
// if (indexCount == 0) {
|
||||
// return GlNumericType.UBYTE;
|
||||
// }
|
||||
// indexCount = indexCount >>> 8;
|
||||
// if (indexCount == 0) {
|
||||
// return GlNumericType.USHORT;
|
||||
// }
|
||||
|
||||
return GlNumericType.UINT;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,17 @@
|
|||
package com.jozufozu.flywheel.util;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class StringUtil {
|
||||
|
||||
public static String args(String functionName, Object... args) {
|
||||
|
||||
return functionName + '(' + Arrays.stream(args)
|
||||
.map(Object::toString)
|
||||
.collect(Collectors.joining(", ")) + ')';
|
||||
}
|
||||
|
||||
public static String trimEnd(String value) {
|
||||
int len = value.length();
|
||||
int st = 0;
|
||||
|
|
Loading…
Reference in a new issue