From c8814f123bbf52ab071ee5b0625811fadcbc70ef Mon Sep 17 00:00:00 2001 From: JozsefA Date: Mon, 24 May 2021 17:50:13 -0700 Subject: [PATCH] More sane models - BufferedModel is no longer abstract. - InstancedModel no longer inherits from BufferedModel, it accepts one as input. - Replace usage of IndexedModel with BufferedModel --- .../flywheel/backend/core/BufferedModel.java | 124 ++++++++++-------- .../flywheel/backend/core/IndexedModel.java | 67 ++++------ .../backend/gl/attrib/VertexFormat.java | 2 +- .../backend/instancing/InstancedModel.java | 100 +++++++------- .../backend/instancing/RenderMaterial.java | 11 +- .../jozufozu/flywheel/util/AttribUtil.java | 26 ++++ .../render/RenderedContraption.java | 16 +-- 7 files changed, 191 insertions(+), 155 deletions(-) create mode 100644 src/main/java/com/jozufozu/flywheel/util/AttribUtil.java diff --git a/src/main/java/com/jozufozu/flywheel/backend/core/BufferedModel.java b/src/main/java/com/jozufozu/flywheel/backend/core/BufferedModel.java index 28825202c..74c187d76 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/core/BufferedModel.java +++ b/src/main/java/com/jozufozu/flywheel/backend/core/BufferedModel.java @@ -1,90 +1,98 @@ package com.jozufozu.flywheel.backend.core; +import static org.lwjgl.opengl.GL20.GL_COLOR_ARRAY; +import static org.lwjgl.opengl.GL20.GL_INDEX_ARRAY; +import static org.lwjgl.opengl.GL20.GL_NORMAL_ARRAY; +import static org.lwjgl.opengl.GL20.GL_QUADS; +import static org.lwjgl.opengl.GL20.GL_TEXTURE_COORD_ARRAY; +import static org.lwjgl.opengl.GL20.GL_VERTEX_ARRAY; +import static org.lwjgl.opengl.GL20.glDisableClientState; +import static org.lwjgl.opengl.GL20.glDrawArrays; + import java.nio.ByteBuffer; -import org.lwjgl.opengl.GL20; - -import com.jozufozu.flywheel.backend.RenderWork; 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.util.AttribUtil; -public abstract class BufferedModel { +public class BufferedModel { protected final ByteBuffer data; - protected final VertexFormat modelFormat; + protected final VertexFormat format; protected final int vertexCount; - protected GlBuffer modelVBO; - private boolean initialized; // lazy init + protected GlBuffer vbo; private boolean removed; - protected BufferedModel(VertexFormat modelFormat, ByteBuffer data, int vertices) { + public BufferedModel(VertexFormat format, ByteBuffer data, int vertices) { this.data = data; - this.modelFormat = modelFormat; + this.format = format; this.vertexCount = vertices; + + vbo = new GlBuffer(GlBufferType.ARRAY_BUFFER); + + vbo.bind(); + // allocate the buffer on the gpu + vbo.alloc(this.data.capacity()); + + // mirror it in system memory so we can write to it, and upload our model. + vbo.getBuffer(0, this.data.capacity()) + .put(this.data) + .flush(); + vbo.unbind(); + } + + public VertexFormat getFormat() { + return format; + } + + public int getVertexCount() { + return vertexCount; + } + + public void bindBuffer() { + vbo.bind(); + } + + public void unbindBuffer() { + vbo.unbind(); } /** * Renders this model, checking first if there is anything to render. */ - public final void render() { + public void render() { if (vertexCount <= 0 || removed) return; - if (!initialized) { - // Lazily acquire resources in order to get around initialization order, as #getTotalShaderAttributeCount - // might depend on fields in subclasses. - init(); - initialized = true; - } + // TODO: minecraft sometimes leaves its state dirty on launch. this is a hack + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_NORMAL_ARRAY); + glDisableClientState(GL_COLOR_ARRAY); + glDisableClientState(GL_INDEX_ARRAY); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); - doRender(); + vbo.bind(); + + AttribUtil.enableArrays(getAttributeCount()); + format.vertexAttribPointers(0); + + glDrawArrays(GL_QUADS, 0, vertexCount); + + AttribUtil.disableArrays(getAttributeCount()); + + vbo.unbind(); } - /** - * Set up any state and make the draw calls. - */ - protected abstract void doRender(); + public void delete() { + if (removed) return; - public final void delete() { removed = true; - if (initialized) { - RenderWork.enqueue(this::deleteInternal); - } + vbo.delete(); } - protected void init() { - modelVBO = new GlBuffer(GlBufferType.ARRAY_BUFFER); - - modelVBO.bind(); - initModel(); - modelVBO.unbind(); + public int getAttributeCount() { + return format.getAttributeCount(); } - protected void initModel() { - // allocate the buffer on the gpu - modelVBO.alloc(data.capacity()); - - // mirror it in system memory so we can write to it - MappedBuffer buffer = modelVBO.getBuffer(0, data.capacity()); - buffer.put(data); - buffer.flush(); - } - - protected int getTotalShaderAttributeCount() { - return modelFormat.getShaderAttributeCount(); - } - - protected void setupAttributes() { - int numAttributes = getTotalShaderAttributeCount(); - for (int i = 0; i <= numAttributes; i++) { - GL20.glEnableVertexAttribArray(i); - } - - modelFormat.vertexAttribPointers(0); - } - - protected void deleteInternal() { - modelVBO.delete(); - } } + diff --git a/src/main/java/com/jozufozu/flywheel/backend/core/IndexedModel.java b/src/main/java/com/jozufozu/flywheel/backend/core/IndexedModel.java index 1372a13ca..cb76197d7 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/core/IndexedModel.java +++ b/src/main/java/com/jozufozu/flywheel/backend/core/IndexedModel.java @@ -8,62 +8,49 @@ import com.jozufozu.flywheel.backend.gl.GlPrimitiveType; 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.util.AttribUtil; public class IndexedModel extends BufferedModel { protected GlPrimitiveType eboIndexType; protected GlBuffer ebo; - public IndexedModel(VertexFormat modelFormat, ByteBuffer buf, int vertices) { + public IndexedModel(VertexFormat modelFormat, ByteBuffer buf, int vertices, ByteBuffer indices, GlPrimitiveType indexType) { super(modelFormat, buf, vertices); - } - @Override - protected void init() { - super.init(); - - createEBO(); - } - - @Override - protected void doRender() { - modelVBO.bind(); - ebo.bind(); - - setupAttributes(); - GL20.glDrawElements(GL20.GL_QUADS, vertexCount, eboIndexType.getGlConstant(), 0); - - int numAttributes = getTotalShaderAttributeCount(); - for (int i = 0; i <= numAttributes; i++) { - GL20.glDisableVertexAttribArray(i); - } - - ebo.unbind(); - modelVBO.unbind(); - } - - protected final void createEBO() { ebo = new GlBuffer(GlBufferType.ELEMENT_ARRAY_BUFFER); - eboIndexType = GlPrimitiveType.UINT; // TODO: choose this based on the number of vertices + this.eboIndexType = indexType; - int indicesSize = vertexCount * eboIndexType.getSize(); + int indicesSize = vertexCount * indexType.getSize(); ebo.bind(); ebo.alloc(indicesSize); - MappedBuffer indices = ebo.getBuffer(0, indicesSize); - for (int i = 0; i < vertexCount; i++) { - indices.putInt(i); - } - indices.flush(); + ebo.getBuffer(0, indicesSize) + .put(indices) + .flush(); ebo.unbind(); } - @Override - protected void deleteInternal() { - super.deleteInternal(); - ebo.delete(); - } + public void render() { + vbo.bind(); + ebo.bind(); + + AttribUtil.enableArrays(getAttributeCount()); + format.vertexAttribPointers(0); + + GL20.glDrawElements(GL20.GL_QUADS, vertexCount, eboIndexType.getGlConstant(), 0); + + AttribUtil.disableArrays(getAttributeCount()); + + ebo.unbind(); + vbo.unbind(); + } + + @Override + public void delete() { + super.delete(); + ebo.delete(); + } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/gl/attrib/VertexFormat.java b/src/main/java/com/jozufozu/flywheel/backend/gl/attrib/VertexFormat.java index 9b325e6ed..9d48fd142 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/gl/attrib/VertexFormat.java +++ b/src/main/java/com/jozufozu/flywheel/backend/gl/attrib/VertexFormat.java @@ -22,7 +22,7 @@ public class VertexFormat { this.stride = stride; } - public int getShaderAttributeCount() { + public int getAttributeCount() { return numAttributes; } diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/InstancedModel.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/InstancedModel.java index a70108cbe..69a46bfac 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/InstancedModel.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/InstancedModel.java @@ -1,7 +1,6 @@ package com.jozufozu.flywheel.backend.instancing; -import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.BitSet; @@ -14,31 +13,64 @@ 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.util.AttribUtil; -public class InstancedModel extends BufferedModel { +public class InstancedModel { public final InstancedTileRenderer renderer; + protected final BufferedModel model; + protected final VertexFormat instanceFormat; protected final InstanceFactory factory; protected GlVertexArray vao; protected GlBuffer instanceVBO; protected int glBufferSize = -1; protected int glInstanceCount = 0; + private boolean deleted; protected final ArrayList data = new ArrayList<>(); boolean anyToRemove; boolean anyToUpdate; - public InstancedModel(VertexFormat modelFormat, ByteBuffer buf, int vertices, InstancedTileRenderer renderer, VertexFormat instanceFormat, InstanceFactory factory) { - super(modelFormat, buf, vertices); + public InstancedModel(BufferedModel model, InstancedTileRenderer renderer, VertexFormat instanceFormat, InstanceFactory factory) { + this.model = model; this.factory = factory; this.instanceFormat = instanceFormat; this.renderer = renderer; + + if (model.getVertexCount() <= 0) + throw new IllegalArgumentException("Refusing to instance a model with no vertices."); + + vao = new GlVertexArray(); + instanceVBO = new GlBuffer(GlBufferType.ARRAY_BUFFER); + + vao.bind(); + + // bind the model's vbo to our vao + model.bindBuffer(); + model.getFormat().vertexAttribPointers(0); + model.unbindBuffer(); + + // enable all the attribute arrays in our vao. we only need to do this once + AttribUtil.enableArrays(model.getAttributeCount() + this.instanceFormat.getAttributeCount()); + vao.unbind(); } - public synchronized D createInstance() { + public void render() { + if (deleted) return; + + vao.bind(); + renderSetup(); + + if (glInstanceCount > 0) + Backend.compat.drawInstanced.drawArraysInstanced(GL11.GL_QUADS, 0, model.getVertexCount(), glInstanceCount); + + vao.unbind(); + } + + public D createInstance() { D instanceData = factory.create(this); instanceData.dirty = true; anyToUpdate = true; @@ -47,39 +79,17 @@ public class InstancedModel extends BufferedModel { return instanceData; } - @Override - protected void init() { - vao = new GlVertexArray(); - instanceVBO = new GlBuffer(GlBufferType.ARRAY_BUFFER); + public void delete() { + if (deleted) return; - vao.bind(); - super.init(); - vao.unbind(); - } + deleted = true; - @Override - protected void initModel() { - super.initModel(); - setupAttributes(); - } - - protected void deleteInternal() { - super.deleteInternal(); + model.delete(); instanceVBO.delete(); vao.delete(); } - protected void doRender() { - vao.bind(); - renderSetup(); - - if (glInstanceCount > 0) - Backend.compat.drawInstanced.drawArraysInstanced(GL11.GL_QUADS, 0, vertexCount, glInstanceCount); - - vao.unbind(); - } - protected void renderSetup() { if (anyToRemove) { removeDeletedInstances(); @@ -96,23 +106,12 @@ public class InstancedModel extends BufferedModel { updateBuffer(); } + glInstanceCount = data.size(); } - glInstanceCount = data.size(); - informAttribDivisors(); instanceVBO.unbind(); - this.anyToRemove = false; - this.anyToUpdate = false; - } - - private void informAttribDivisors() { - int staticAttributes = modelFormat.getShaderAttributeCount(); - instanceFormat.vertexAttribPointers(staticAttributes); - - for (int i = 0; i < instanceFormat.getShaderAttributeCount(); i++) { - Backend.compat.instancedArrays.vertexAttribDivisor(i + staticAttributes, 1); - } + anyToRemove = anyToUpdate = false; } private void clearBufferTail() { @@ -185,6 +184,9 @@ public class InstancedModel extends BufferedModel { buffer.flush(); glInstanceCount = size; + + informAttribDivisors(); + return true; } return false; @@ -222,7 +224,13 @@ public class InstancedModel extends BufferedModel { } - protected int getTotalShaderAttributeCount() { - return instanceFormat.getShaderAttributeCount() + super.getTotalShaderAttributeCount(); + private void informAttribDivisors() { + int staticAttributes = model.getAttributeCount(); + instanceFormat.vertexAttribPointers(staticAttributes); + + for (int i = 0; i < instanceFormat.getAttributeCount(); i++) { + Backend.compat.instancedArrays.vertexAttribDivisor(i + staticAttributes, 1); + } } + } diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/RenderMaterial.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/RenderMaterial.java index 25c85b84b..0729eb6da 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/RenderMaterial.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/RenderMaterial.java @@ -12,6 +12,8 @@ import org.lwjgl.opengl.GL11; import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; +import com.jozufozu.flywheel.backend.RenderWork; +import com.jozufozu.flywheel.backend.core.BufferedModel; import com.jozufozu.flywheel.backend.core.PartialModel; import com.jozufozu.flywheel.backend.core.shader.ShaderCallback; import com.jozufozu.flywheel.backend.core.shader.WorldProgram; @@ -45,7 +47,10 @@ public class RenderMaterial

{ this.spec = spec; this.models = CacheBuilder.newBuilder() - .removalListener(notification -> ((InstancedModel) notification.getValue()).delete()) + .removalListener(notification -> { + InstancedModel model = (InstancedModel) notification.getValue(); + RenderWork.enqueue(model::delete); + }) .build(); } @@ -140,7 +145,9 @@ public class RenderMaterial

{ to.rewind(); - return new InstancedModel<>(format, to, vertexCount, renderer, spec.getInstanceFormat(), spec.getInstanceFactory()); + BufferedModel bufferedModel = new BufferedModel(format, to, vertexCount); + + return new InstancedModel<>(bufferedModel, renderer, spec.getInstanceFormat(), spec.getInstanceFactory()); } private static final Direction[] dirs; diff --git a/src/main/java/com/jozufozu/flywheel/util/AttribUtil.java b/src/main/java/com/jozufozu/flywheel/util/AttribUtil.java new file mode 100644 index 000000000..bb8cd1ce4 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/util/AttribUtil.java @@ -0,0 +1,26 @@ +package com.jozufozu.flywheel.util; + +import org.lwjgl.opengl.GL20; + +public class AttribUtil { + + public static void enableArrays(int count) { + enableArrays(0, count); + } + + public static void enableArrays(int start, int end) { + for (int i = start; i <= end; i++) { + GL20.glEnableVertexAttribArray(i); + } + } + + public static void disableArrays(int count) { + disableArrays(0, count); + } + + public static void disableArrays(int start, int end) { + for (int i = start; i <= end; i++) { + GL20.glDisableVertexAttribArray(i); + } + } +} diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/RenderedContraption.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/RenderedContraption.java index 513fa4913..d8a4aae08 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/RenderedContraption.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/RenderedContraption.java @@ -10,7 +10,7 @@ import java.util.Map; import javax.annotation.Nullable; import com.jozufozu.flywheel.backend.Backend; -import com.jozufozu.flywheel.backend.core.IndexedModel; +import com.jozufozu.flywheel.backend.core.BufferedModel; import com.jozufozu.flywheel.backend.gl.attrib.CommonAttributes; import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat; import com.jozufozu.flywheel.backend.instancing.IInstanceRendered; @@ -45,7 +45,7 @@ public class RenderedContraption extends ContraptionWorldHolder { private final ContraptionLighter lighter; public final ContraptionKineticRenderer kinetics; - private final Map renderLayers = new HashMap<>(); + private final Map renderLayers = new HashMap<>(); private Matrix4f model; private AxisAlignedBB lightBox; @@ -67,7 +67,7 @@ public class RenderedContraption extends ContraptionWorldHolder { } public void doRenderLayer(RenderType layer, ContraptionProgram shader) { - IndexedModel structure = renderLayers.get(layer); + BufferedModel structure = renderLayers.get(layer); if (structure != null) { setup(shader); structure.render(); @@ -108,7 +108,7 @@ public class RenderedContraption extends ContraptionWorldHolder { } void invalidate() { - for (IndexedModel buffer : renderLayers.values()) { + for (BufferedModel buffer : renderLayers.values()) { buffer.delete(); } renderLayers.clear(); @@ -119,7 +119,7 @@ public class RenderedContraption extends ContraptionWorldHolder { } private void buildLayers() { - for (IndexedModel buffer : renderLayers.values()) { + for (BufferedModel buffer : renderLayers.values()) { buffer.delete(); } @@ -128,7 +128,7 @@ public class RenderedContraption extends ContraptionWorldHolder { List blockLayers = RenderType.getBlockLayers(); for (RenderType layer : blockLayers) { - IndexedModel layerModel = buildStructureModel(renderWorld, contraption, layer); + BufferedModel layerModel = buildStructureModel(renderWorld, contraption, layer); if (layerModel != null) renderLayers.put(layer, layerModel); } } @@ -153,7 +153,7 @@ public class RenderedContraption extends ContraptionWorldHolder { } @Nullable - private static IndexedModel buildStructureModel(PlacementSimulationWorld renderWorld, Contraption c, RenderType layer) { + private static BufferedModel buildStructureModel(PlacementSimulationWorld renderWorld, Contraption c, RenderType layer) { BufferBuilderReader reader = new BufferBuilderReader(ContraptionRenderDispatcher.buildStructure(renderWorld, c, layer)); int vertexCount = reader.getVertexCount(); @@ -192,6 +192,6 @@ public class RenderedContraption extends ContraptionWorldHolder { to.rewind(); - return new IndexedModel(format, to, vertexCount); + return new BufferedModel(format, to, vertexCount); } }