Go through GlVertexArray

- VAO state is now handled by GlVertexArray objects
 - IndexedModel no longer inherits from VBOModel
 - BufferedModel doesn't need #clearState
 - Likely fixes crash on intel drivers
This commit is contained in:
Jozufozu 2021-12-27 19:14:19 -08:00
parent 4c4d6b77fc
commit b5a9741caf
11 changed files with 137 additions and 162 deletions

View file

@ -18,9 +18,7 @@ public abstract class GlObject {
protected final void checkHandle() { protected final void checkHandle() {
if (this.isInvalid()) { if (this.isInvalid()) {
String descriptor = getDescriptor(); throw new IllegalStateException("handle is not valid.");
String message = (descriptor == null ? "" : (descriptor + " ")) + "handle is not valid.";
throw new IllegalStateException(message);
} }
} }
@ -33,10 +31,8 @@ public abstract class GlObject {
} }
public void delete() { public void delete() {
if (isInvalid()) { if (this.isInvalid()) {
String descriptor = getDescriptor(); throw new IllegalStateException("handle already deleted.");
String message = (descriptor == null ? "" : (descriptor + " ")) + "handle already deleted.";
throw new IllegalStateException(message);
} }
deleteInternal(handle); deleteInternal(handle);
@ -45,7 +41,4 @@ public abstract class GlObject {
protected abstract void deleteInternal(int handle); protected abstract void deleteInternal(int handle);
protected String getDescriptor() {
return "";
}
} }

View file

@ -1,7 +1,10 @@
package com.jozufozu.flywheel.backend.gl; package com.jozufozu.flywheel.backend.gl;
import org.lwjgl.opengl.GL20;
import com.jozufozu.flywheel.core.layout.BufferLayout;
import com.jozufozu.flywheel.core.layout.LayoutItem;
import com.jozufozu.flywheel.mixin.BufferUploaderAccessor; import com.jozufozu.flywheel.mixin.BufferUploaderAccessor;
import com.jozufozu.flywheel.util.AttribUtil;
import com.mojang.blaze3d.platform.GlStateManager; import com.mojang.blaze3d.platform.GlStateManager;
public class GlVertexArray extends GlObject { public class GlVertexArray extends GlObject {
@ -20,11 +23,28 @@ public class GlVertexArray extends GlObject {
BufferUploaderAccessor.flywheel$setLastVAO(0); BufferUploaderAccessor.flywheel$setLastVAO(0);
} }
public void enableArrays(int count) {
for (int i = 0; i < count; i++) {
GL20.glEnableVertexAttribArray(i);
}
}
public void disableArrays(int count) {
for (int i = 0; i < count; i++) {
GL20.glDisableVertexAttribArray(i);
}
}
public void bindAttributes(int startIndex, BufferLayout type) {
int offset = 0;
for (LayoutItem spec : type.getLayoutItems()) {
spec.vertexAttribPointer(type.getStride(), startIndex, offset);
startIndex += spec.getAttributeCount();
offset += spec.getSize();
}
}
protected void deleteInternal(int handle) { protected void deleteInternal(int handle) {
GlStateManager._glDeleteVertexArrays(handle); GlStateManager._glDeleteVertexArrays(handle);
} }
public void enableArrays(int count) {
AttribUtil.enableArrays(count);
}
} }

View file

@ -8,13 +8,13 @@ import com.jozufozu.flywheel.api.struct.Instanced;
import com.jozufozu.flywheel.api.struct.StructWriter; import com.jozufozu.flywheel.api.struct.StructWriter;
import com.jozufozu.flywheel.backend.Backend; import com.jozufozu.flywheel.backend.Backend;
import com.jozufozu.flywheel.backend.gl.GlVertexArray; import com.jozufozu.flywheel.backend.gl.GlVertexArray;
import com.jozufozu.flywheel.core.layout.BufferLayout;
import com.jozufozu.flywheel.backend.gl.buffer.GlBuffer; import com.jozufozu.flywheel.backend.gl.buffer.GlBuffer;
import com.jozufozu.flywheel.backend.gl.buffer.GlBufferType; import com.jozufozu.flywheel.backend.gl.buffer.GlBufferType;
import com.jozufozu.flywheel.backend.gl.buffer.MappedBuffer; import com.jozufozu.flywheel.backend.gl.buffer.MappedBuffer;
import com.jozufozu.flywheel.backend.instancing.AbstractInstancer; import com.jozufozu.flywheel.backend.instancing.AbstractInstancer;
import com.jozufozu.flywheel.backend.model.BufferedModel; import com.jozufozu.flywheel.backend.model.BufferedModel;
import com.jozufozu.flywheel.backend.model.ModelAllocator; import com.jozufozu.flywheel.backend.model.ModelAllocator;
import com.jozufozu.flywheel.core.layout.BufferLayout;
import com.jozufozu.flywheel.core.model.Model; import com.jozufozu.flywheel.core.model.Model;
public class GPUInstancer<D extends InstanceData> extends AbstractInstancer<D> { public class GPUInstancer<D extends InstanceData> extends AbstractInstancer<D> {
@ -73,14 +73,14 @@ public class GPUInstancer<D extends InstanceData> extends AbstractInstancer<D> {
model = modelAllocator.alloc(modelData, arenaModel -> { model = modelAllocator.alloc(modelData, arenaModel -> {
vao.bind(); vao.bind();
arenaModel.setupState(); arenaModel.setupState(vao);
}); });
vao.bind(); vao.bind();
vao.enableArrays(model.getAttributeCount() + instanceFormat.getAttributeCount());
instanceVBO = GlBuffer.requestPersistent(GlBufferType.ARRAY_BUFFER); instanceVBO = GlBuffer.requestPersistent(GlBufferType.ARRAY_BUFFER);
instanceVBO.setGrowthMargin(instanceFormat.getStride() * 16); instanceVBO.setGrowthMargin(instanceFormat.getStride() * 16);
vao.enableArrays(model.getAttributeCount() + instanceFormat.getAttributeCount());
} }
public boolean isInitialized() { public boolean isInitialized() {
@ -180,19 +180,19 @@ public class GPUInstancer<D extends InstanceData> extends AbstractInstancer<D> {
glInstanceCount = size; glInstanceCount = size;
informAttribDivisors(); bindInstanceAttributes();
return true; return true;
} }
return false; return false;
} }
private void informAttribDivisors() { private void bindInstanceAttributes() {
int staticAttributes = model.getAttributeCount(); int attributeBaseIndex = model.getAttributeCount();
instanceFormat.vertexAttribPointers(staticAttributes); vao.bindAttributes(attributeBaseIndex, instanceFormat);
for (int i = 0; i < instanceFormat.getAttributeCount(); i++) { for (int i = 0; i < instanceFormat.getAttributeCount(); i++) {
Backend.getInstance().compat.instancedArrays.vertexAttribDivisor(i + staticAttributes, 1); Backend.getInstance().compat.instancedArrays.vertexAttribDivisor(attributeBaseIndex + i, 1);
} }
} }
} }

View file

@ -1,46 +1,54 @@
package com.jozufozu.flywheel.backend.model; package com.jozufozu.flywheel.backend.model;
import java.util.function.Supplier;
import com.jozufozu.flywheel.backend.gl.GlVertexArray; import com.jozufozu.flywheel.backend.gl.GlVertexArray;
import com.jozufozu.flywheel.core.model.Model; import com.jozufozu.flywheel.core.model.Model;
public class ArrayModelRenderer extends ModelRenderer { public class ArrayModelRenderer {
private final Model model;
protected GlVertexArray vao; protected GlVertexArray vao;
protected BufferedModel vbo;
protected boolean initialized;
public ArrayModelRenderer(Supplier<Model> model) { public ArrayModelRenderer(Model model) {
super(model); this.model = model;
} }
@Override /**
* Renders this model, checking first if there is anything to render.
*/
public void draw() { public void draw() {
if (!initialized) init(); if (!initialized) init();
if (!isValid()) return; if (!isValid()) return;
vao.bind(); vao.bind();
model.drawCall(); vbo.drawCall();
} }
@Override
protected void init() { protected void init() {
initialized = true; initialized = true;
Model model = modelSupplier.get();
if (model.empty()) return; if (model.empty()) return;
this.model = new IndexedModel(model); this.vbo = new IndexedModel(model);
vao = new GlVertexArray(); vao = new GlVertexArray();
vao.bind(); vao.bind();
// bind the model's vbo to our vao // bind the model's vbo to our vao
this.model.setupState(); this.vbo.setupState(vao);
GlVertexArray.unbind(); GlVertexArray.unbind();
}
this.model.clearState(); public void delete() {
if (vbo != null)
vbo.delete();
}
protected boolean isValid() {
return vbo != null && vbo.valid();
} }
} }

View file

@ -1,7 +1,8 @@
package com.jozufozu.flywheel.backend.model; package com.jozufozu.flywheel.backend.model;
import com.jozufozu.flywheel.core.layout.BufferLayout;
import com.jozufozu.flywheel.api.vertex.VertexType; import com.jozufozu.flywheel.api.vertex.VertexType;
import com.jozufozu.flywheel.backend.gl.GlVertexArray;
import com.jozufozu.flywheel.core.layout.BufferLayout;
public interface BufferedModel { public interface BufferedModel {
@ -10,11 +11,9 @@ public interface BufferedModel {
int getVertexCount(); int getVertexCount();
/** /**
* The VBO/VAO should be bound externally. * The VAO must be bound externally.
*/ */
void setupState(); void setupState(GlVertexArray vao);
void clearState();
void drawCall(); void drawCall();
@ -27,7 +26,7 @@ public interface BufferedModel {
void delete(); void delete();
default BufferLayout getFormat() { default BufferLayout getLayout() {
return getType().getLayout(); return getType().getLayout();
} }
@ -36,6 +35,6 @@ public interface BufferedModel {
} }
default int getAttributeCount() { default int getAttributeCount() {
return getFormat().getAttributeCount(); return getType().getLayout().getAttributeCount();
} }
} }

View file

@ -3,7 +3,14 @@ package com.jozufozu.flywheel.backend.model;
import org.lwjgl.opengl.GL20; import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GL31; import org.lwjgl.opengl.GL31;
import com.jozufozu.flywheel.Flywheel;
import com.jozufozu.flywheel.api.vertex.VertexType;
import com.jozufozu.flywheel.backend.gl.GlPrimitive; import com.jozufozu.flywheel.backend.gl.GlPrimitive;
import com.jozufozu.flywheel.backend.gl.GlVertexArray;
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.Model; import com.jozufozu.flywheel.core.model.Model;
/** /**
@ -11,26 +18,43 @@ import com.jozufozu.flywheel.core.model.Model;
* *
* <br><em>This should be favored over a normal BufferedModel.</em> * <br><em>This should be favored over a normal BufferedModel.</em>
*/ */
public class IndexedModel extends VBOModel { public class IndexedModel implements BufferedModel {
protected final Model model;
protected final GlPrimitive primitiveMode;
protected ElementBuffer ebo; protected ElementBuffer ebo;
protected GlBuffer vbo;
protected boolean deleted;
public IndexedModel(Model model) { public IndexedModel(Model model) {
super(GlPrimitive.TRIANGLES, model); this.model = model;
this.primitiveMode = GlPrimitive.TRIANGLES;
vbo = new MappedGlBuffer(GlBufferType.ARRAY_BUFFER);
vbo.bind();
// allocate the buffer on the gpu
vbo.ensureCapacity(model.size());
// mirror it in system memory, so we can write to it, and upload our model.
try (MappedBuffer buffer = vbo.getBuffer()) {
model.writeInto(buffer.unwrap());
} catch (Exception e) {
Flywheel.LOGGER.error(String.format("Error uploading model '%s':", model.name()), e);
}
vbo.unbind();
this.ebo = model.createEBO(); this.ebo = model.createEBO();
} }
@Override /**
public void setupState() { * The VBO/VAO should be bound externally.
super.setupState(); */
ebo.bind(); public void setupState(GlVertexArray vao) {
} vbo.bind();
vao.enableArrays(getAttributeCount());
@Override vao.bindAttributes(0, getType().getLayout());
public void clearState() {
super.clearState();
ebo.unbind();
} }
@Override @Override
@ -39,6 +63,9 @@ public class IndexedModel extends VBOModel {
GL20.glDrawElements(primitiveMode.glEnum, ebo.elementCount, ebo.eboIndexType.getGlEnum(), 0); GL20.glDrawElements(primitiveMode.glEnum, ebo.elementCount, ebo.eboIndexType.getGlEnum(), 0);
} }
/**
* Draws many instances of this model, assuming the appropriate state is already bound.
*/
@Override @Override
public void drawInstances(int instanceCount) { public void drawInstances(int instanceCount) {
if (!valid()) return; if (!valid()) return;
@ -47,4 +74,24 @@ public class IndexedModel extends VBOModel {
GL31.glDrawElementsInstanced(primitiveMode.glEnum, ebo.elementCount, ebo.eboIndexType.getGlEnum(), 0, instanceCount); GL31.glDrawElementsInstanced(primitiveMode.glEnum, ebo.elementCount, ebo.eboIndexType.getGlEnum(), 0, instanceCount);
} }
public boolean isDeleted() {
return deleted;
}
@Override
public VertexType getType() {
return model.getType();
}
public int getVertexCount() {
return model.vertexCount();
}
public void delete() {
if (deleted) return;
deleted = true;
vbo.delete();
}
} }

View file

@ -6,15 +6,15 @@ import java.util.List;
import org.lwjgl.opengl.GL32; import org.lwjgl.opengl.GL32;
import com.jozufozu.flywheel.Flywheel; import com.jozufozu.flywheel.Flywheel;
import com.jozufozu.flywheel.api.vertex.VertexType;
import com.jozufozu.flywheel.api.vertex.VertexWriter;
import com.jozufozu.flywheel.backend.gl.GlPrimitive; import com.jozufozu.flywheel.backend.gl.GlPrimitive;
import com.jozufozu.flywheel.backend.gl.GlVertexArray;
import com.jozufozu.flywheel.backend.gl.buffer.GlBuffer; import com.jozufozu.flywheel.backend.gl.buffer.GlBuffer;
import com.jozufozu.flywheel.backend.gl.buffer.GlBufferType; import com.jozufozu.flywheel.backend.gl.buffer.GlBufferType;
import com.jozufozu.flywheel.backend.gl.buffer.MappedBuffer; import com.jozufozu.flywheel.backend.gl.buffer.MappedBuffer;
import com.jozufozu.flywheel.backend.gl.buffer.MappedGlBuffer; import com.jozufozu.flywheel.backend.gl.buffer.MappedGlBuffer;
import com.jozufozu.flywheel.core.model.Model; import com.jozufozu.flywheel.core.model.Model;
import com.jozufozu.flywheel.api.vertex.VertexType;
import com.jozufozu.flywheel.api.vertex.VertexWriter;
import com.jozufozu.flywheel.util.AttribUtil;
public class ModelPool implements ModelAllocator { public class ModelPool implements ModelAllocator {
@ -178,18 +178,10 @@ public class ModelPool implements ModelAllocator {
} }
@Override @Override
public void setupState() { public void setupState(GlVertexArray vao) {
vbo.bind(); vbo.bind();
ebo.bind(); vao.enableArrays(getAttributeCount());
AttribUtil.enableArrays(getAttributeCount()); vao.bindAttributes(0, vertexType.getLayout());
ModelPool.this.vertexType.getLayout().vertexAttribPointers(0);
}
@Override
public void clearState() {
AttribUtil.disableArrays(getAttributeCount());
ebo.unbind();
vbo.unbind();
} }
@Override @Override

View file

@ -1,47 +0,0 @@
package com.jozufozu.flywheel.backend.model;
import java.util.function.Supplier;
import com.jozufozu.flywheel.core.model.Model;
public class ModelRenderer {
protected Supplier<Model> modelSupplier;
protected BufferedModel model;
protected boolean initialized;
public ModelRenderer(Supplier<Model> modelSupplier) {
this.modelSupplier = modelSupplier;
}
/**
* Renders this model, checking first if there is anything to render.
*/
public void draw() {
if (!initialized) init();
if (!isValid()) return;
model.setupState();
model.drawCall();
model.clearState();
}
public void delete() {
if (model != null)
model.delete();
}
protected void init() {
initialized = true;
Model model = modelSupplier.get();
if (model.empty()) return;
this.model = new IndexedModel(model);
}
protected boolean isValid() {
return model != null && model.valid();
}
}

View file

@ -5,14 +5,14 @@ import static org.lwjgl.opengl.GL11.glDrawArrays;
import org.lwjgl.opengl.GL31; import org.lwjgl.opengl.GL31;
import com.jozufozu.flywheel.Flywheel; import com.jozufozu.flywheel.Flywheel;
import com.jozufozu.flywheel.api.vertex.VertexType;
import com.jozufozu.flywheel.backend.gl.GlPrimitive; import com.jozufozu.flywheel.backend.gl.GlPrimitive;
import com.jozufozu.flywheel.backend.gl.GlVertexArray;
import com.jozufozu.flywheel.backend.gl.buffer.GlBuffer; import com.jozufozu.flywheel.backend.gl.buffer.GlBuffer;
import com.jozufozu.flywheel.backend.gl.buffer.GlBufferType; import com.jozufozu.flywheel.backend.gl.buffer.GlBufferType;
import com.jozufozu.flywheel.backend.gl.buffer.MappedBuffer; import com.jozufozu.flywheel.backend.gl.buffer.MappedBuffer;
import com.jozufozu.flywheel.backend.gl.buffer.MappedGlBuffer; import com.jozufozu.flywheel.backend.gl.buffer.MappedGlBuffer;
import com.jozufozu.flywheel.core.model.Model; import com.jozufozu.flywheel.core.model.Model;
import com.jozufozu.flywheel.api.vertex.VertexType;
import com.jozufozu.flywheel.util.AttribUtil;
public class VBOModel implements BufferedModel { public class VBOModel implements BufferedModel {
@ -57,15 +57,10 @@ public class VBOModel implements BufferedModel {
/** /**
* The VBO/VAO should be bound externally. * The VBO/VAO should be bound externally.
*/ */
public void setupState() { public void setupState(GlVertexArray vao) {
vbo.bind(); vbo.bind();
AttribUtil.enableArrays(getAttributeCount()); vao.enableArrays(getAttributeCount());
getFormat().vertexAttribPointers(0); vao.bindAttributes(0, getLayout());
}
public void clearState() {
AttribUtil.disableArrays(getAttributeCount());
vbo.unbind();
} }
public void drawCall() { public void drawCall() {

View file

@ -9,7 +9,7 @@ import com.jozufozu.flywheel.api.vertex.VertexType;
* Classic Vertex Format struct with a clever name. * Classic Vertex Format struct with a clever name.
* *
* <p> * <p>
* Used for vertices and instance. Describes the layout of a datatype in a buffer object. * Used for vertices and instances. Describes the layout of a datatype in a buffer object.
* </p> * </p>
* *
* @see com.jozufozu.flywheel.api.struct.StructType * @see com.jozufozu.flywheel.api.struct.StructType
@ -34,6 +34,10 @@ public class BufferLayout {
this.stride = stride; this.stride = stride;
} }
public List<LayoutItem> getLayoutItems() {
return allAttributes;
}
public int getAttributeCount() { public int getAttributeCount() {
return numAttributes; return numAttributes;
} }
@ -42,15 +46,6 @@ public class BufferLayout {
return stride; return stride;
} }
public void vertexAttribPointers(int index) {
int offset = 0;
for (LayoutItem spec : this.allAttributes) {
spec.vertexAttribPointer(stride, index, offset);
index += spec.getAttributeCount();
offset += spec.getSize();
}
}
public static Builder builder() { public static Builder builder() {
return new Builder(); return new Builder();
} }

View file

@ -1,27 +0,0 @@
package com.jozufozu.flywheel.util;
import org.lwjgl.opengl.GL20;
// TODO: move this functionality into GlVertexArray and track it
public class AttribUtil {
public static void enableArrays(int count) {
enableArrays(0, count);
}
public static void enableArrays(int fromInclusive, int toExclusive) {
for (int i = fromInclusive; i < toExclusive; i++) {
GL20.glEnableVertexAttribArray(i);
}
}
public static void disableArrays(int count) {
disableArrays(0, count);
}
public static void disableArrays(int fromInclusive, int toExclusive) {
for (int i = fromInclusive; i < toExclusive; i++) {
GL20.glDisableVertexAttribArray(i);
}
}
}