State your state

- Address a few bugs with rubidium
 - Ignore EBOs when restoring state
 - ElementBuffer deals in raw gl handles
 - Document all state changes/restores/resets

Co-authored-by: PepperCode1 <44146161+peppercode1@users.noreply.github.com>
This commit is contained in:
Jozufozu 2022-10-22 12:36:13 -07:00
parent 6c11958b9e
commit 9280ae50dd
13 changed files with 114 additions and 84 deletions

View file

@ -39,7 +39,7 @@ public class GlStateTracker {
return new State(BUFFERS.clone(), vao, program); return new State(BUFFERS.clone(), vao, program);
} }
public static record State(int[] buffers, int vao, int program) { public record State(int[] buffers, int vao, int program) {
public void restore() { public void restore() {
if (vao != GlStateTracker.vao) { if (vao != GlStateTracker.vao) {
GlStateManager._glBindVertexArray(vao); GlStateManager._glBindVertexArray(vao);
@ -48,7 +48,7 @@ public class GlStateTracker {
GlBufferType[] values = GlBufferType.values(); GlBufferType[] values = GlBufferType.values();
for (int i = 0; i < values.length; i++) { for (int i = 0; i < values.length; i++) {
if (buffers[i] != GlStateTracker.BUFFERS[i]) { if (buffers[i] != GlStateTracker.BUFFERS[i] && values[i] != GlBufferType.ELEMENT_ARRAY_BUFFER) {
GlStateManager._glBindBuffer(values[i].glEnum, buffers[i]); GlStateManager._glBindBuffer(values[i].glEnum, buffers[i]);
} }
} }

View file

@ -45,6 +45,7 @@ public class GPUInstancer<D extends InstanceData> extends AbstractInstancer<D> {
public void render() { public void render() {
if (invalid()) return; if (invalid()) return;
// XXX VAO is bound and not reset or restored
vao.bind(); vao.bind();
renderSetup(); renderSetup();
@ -68,12 +69,15 @@ public class GPUInstancer<D extends InstanceData> extends AbstractInstancer<D> {
vao = new GlVertexArray(); vao = new GlVertexArray();
// XXX Callback seems unnecessary. Remove and extract code to run after alloc call?
model = modelAllocator.alloc(modelData, arenaModel -> { model = modelAllocator.alloc(modelData, arenaModel -> {
// XXX VAO is bound and not reset or restored
vao.bind(); vao.bind();
arenaModel.setupState(vao); arenaModel.setupState(vao);
}); });
// XXX VAO is already guaranteed to be bound in model callback
vao.bind(); vao.bind();
vao.enableArrays(model.getAttributeCount() + instanceFormat.getAttributeCount()); vao.enableArrays(model.getAttributeCount() + instanceFormat.getAttributeCount());
@ -108,6 +112,7 @@ public class GPUInstancer<D extends InstanceData> extends AbstractInstancer<D> {
removeDeletedInstances(); removeDeletedInstances();
} }
// XXX ARRAY_BUFFER is bound and reset
instanceVBO.bind(); instanceVBO.bind();
if (!realloc()) { if (!realloc()) {

View file

@ -68,15 +68,23 @@ public class InstancedMaterialGroup<P extends WorldProgram> implements MaterialG
return vertexCount; return vertexCount;
} }
// XXX Overriden in CrumblingGroup
// XXX Runs inside of restore state
public void render(Matrix4f viewProjection, double camX, double camY, double camZ, RenderLayer layer) { public void render(Matrix4f viewProjection, double camX, double camY, double camZ, RenderLayer layer) {
type.setupRenderState(); type.setupRenderState();
Textures.bindActiveTextures(); Textures.bindActiveTextures(); // XXX Changes active unit and bound textures
renderAll(viewProjection, camX, camY, camZ, layer); renderAll(viewProjection, camX, camY, camZ, layer); // XXX May change ARRAY_BUFFER binding (reset or not reset), VAO binding (not reset), shader binding (not reset), call Model.createEBO
type.clearRenderState(); type.clearRenderState();
// XXX Should texture bindings be reset or restored?
// XXX Should the active unit be reset or restored?
// XXX Should the VAO binding be reset or restored?
// XXX Should the ARRAY_BUFFER binding be reset or restored?
// XXX Should the shader binding be reset or restored?
} }
// XXX Internal GL state changes are inconsistent; sometimes bindings are reset to 0, sometimes not
protected void renderAll(Matrix4f viewProjection, double camX, double camY, double camZ, RenderLayer layer) { protected void renderAll(Matrix4f viewProjection, double camX, double camY, double camZ, RenderLayer layer) {
initializeInstancers(); initializeInstancers(); // XXX May change ARRAY_BUFFER binding (reset or not reset), VAO binding (not reset), call Model.createEBO
vertexCount = 0; vertexCount = 0;
instanceCount = 0; instanceCount = 0;
@ -88,6 +96,7 @@ public class InstancedMaterialGroup<P extends WorldProgram> implements MaterialG
P program = owner.context.getProgram(ProgramContext.create(entry.getKey() P program = owner.context.getProgram(ProgramContext.create(entry.getKey()
.getProgramSpec(), Formats.POS_TEX_NORMAL, layer)); .getProgramSpec(), Formats.POS_TEX_NORMAL, layer));
// XXX Shader is bound and not reset or restored
program.bind(); program.bind();
program.uploadViewProjection(viewProjection); program.uploadViewProjection(viewProjection);
program.uploadCameraPos(camX, camY, camZ); program.uploadCameraPos(camX, camY, camZ);
@ -95,7 +104,7 @@ public class InstancedMaterialGroup<P extends WorldProgram> implements MaterialG
setup(program); setup(program);
for (GPUInstancer<?> instancer : material.getAllInstancers()) { for (GPUInstancer<?> instancer : material.getAllInstancers()) {
instancer.render(); instancer.render(); // XXX May change VAO binding (not reset), ARRAY_BUFFER binding (reset)
vertexCount += instancer.getVertexCount(); vertexCount += instancer.getVertexCount();
instanceCount += instancer.getInstanceCount(); instanceCount += instancer.getInstanceCount();
} }
@ -103,19 +112,19 @@ public class InstancedMaterialGroup<P extends WorldProgram> implements MaterialG
} }
private void initializeInstancers() { private void initializeInstancers() {
ModelAllocator allocator = getModelAllocator(); ModelAllocator allocator = getModelAllocator(); // XXX May change ARRAY_BUFFER binding (not reset)
// initialize all uninitialized instancers... // initialize all uninitialized instancers...
for (InstancedMaterial<?> material : materials.values()) { for (InstancedMaterial<?> material : materials.values()) {
for (GPUInstancer<?> instancer : material.uninitialized) { for (GPUInstancer<?> instancer : material.uninitialized) {
instancer.init(allocator); instancer.init(allocator); // XXX May change VAO binding (not reset), ARRAY_BUFFER binding (not reset), call Model.createEBO
} }
material.uninitialized.clear(); material.uninitialized.clear();
} }
if (allocator instanceof ModelPool pool) { if (allocator instanceof ModelPool pool) {
// ...and then flush the model arena in case anything was marked for upload // ...and then flush the model arena in case anything was marked for upload
pool.flush(); pool.flush(); // XXX May change ARRAY_BUFFER binding (reset)
} }
} }

View file

@ -74,6 +74,7 @@ public class InstancingEngine<P extends WorldProgram> implements Engine {
*/ */
@Override @Override
public void render(TaskEngine taskEngine, RenderLayerEvent event) { public void render(TaskEngine taskEngine, RenderLayerEvent event) {
// XXX Restore state
GlStateTracker.State restoreState = GlStateTracker.getRestoreState(); GlStateTracker.State restoreState = GlStateTracker.getRestoreState();
double camX; double camX;

View file

@ -1,27 +1,29 @@
package com.jozufozu.flywheel.backend.model; package com.jozufozu.flywheel.backend.model;
import com.jozufozu.flywheel.backend.gl.GlNumericType; import com.jozufozu.flywheel.backend.gl.buffer.GlBufferType;
import com.jozufozu.flywheel.backend.gl.buffer.GlBuffer;
import com.mojang.blaze3d.vertex.VertexBuffer;
import com.mojang.blaze3d.vertex.VertexFormat; import com.mojang.blaze3d.vertex.VertexFormat;
public class ElementBuffer { public class ElementBuffer {
private final GlBuffer buffer; protected final int elementCount;
public final int elementCount; protected final VertexFormat.IndexType eboIndexType;
public final VertexFormat.IndexType eboIndexType; private final int glBuffer;
public ElementBuffer(GlBuffer backing, int elementCount, VertexFormat.IndexType indexType) { public ElementBuffer(int backing, int elementCount, VertexFormat.IndexType indexType) {
this.buffer = backing;
this.eboIndexType = indexType;
this.elementCount = elementCount; this.elementCount = elementCount;
this.eboIndexType = indexType;
this.glBuffer = backing;
} }
public void bind() { public void bind() {
buffer.bind(); GlBufferType.ELEMENT_ARRAY_BUFFER.bind(glBuffer);
} }
public void unbind() { public int getElementCount() {
buffer.unbind(); return elementCount;
}
public VertexFormat.IndexType getEboIndexType() {
return eboIndexType;
} }
} }

View file

@ -59,15 +59,16 @@ public class IndexedModel implements BufferedModel {
* The VBO/VAO should be bound externally. * The VBO/VAO should be bound externally.
*/ */
public void setupState(GlVertexArray vao) { public void setupState(GlVertexArray vao) {
// XXX ARRAY_BUFFER is bound and not reset or restored
vbo.bind(); vbo.bind();
vao.enableArrays(getAttributeCount()); vao.enableArrays(getAttributeCount());
vao.bindAttributes(0, getType().getLayout()); vao.bindAttributes(0, getType().getLayout());
ebo.bind();
} }
@Override @Override
public void drawCall() { public void drawCall() {
ebo.bind(); GL20.glDrawElements(primitiveMode.glEnum, ebo.getElementCount(), ebo.getEboIndexType().asGLType, 0);
GL20.glDrawElements(primitiveMode.glEnum, ebo.elementCount, ebo.eboIndexType.asGLType, 0);
} }
/** /**
@ -77,9 +78,7 @@ public class IndexedModel implements BufferedModel {
public void drawInstances(int instanceCount) { public void drawInstances(int instanceCount) {
if (!valid()) return; if (!valid()) return;
ebo.bind(); GL31.glDrawElementsInstanced(primitiveMode.glEnum, ebo.getElementCount(), ebo.getEboIndexType().asGLType, 0, instanceCount);
GL31.glDrawElementsInstanced(primitiveMode.glEnum, ebo.elementCount, ebo.eboIndexType.asGLType, 0, instanceCount);
} }
public boolean isDeleted() { public boolean isDeleted() {

View file

@ -42,6 +42,7 @@ public class ModelPool implements ModelAllocator {
vbo = new MappedGlBuffer(GlBufferType.ARRAY_BUFFER); vbo = new MappedGlBuffer(GlBufferType.ARRAY_BUFFER);
// XXX ARRAY_BUFFER is bound and not reset or restored
vbo.bind(); vbo.bind();
vbo.setGrowthMargin(stride * 64); vbo.setGrowthMargin(stride * 64);
} }
@ -68,6 +69,7 @@ public class ModelPool implements ModelAllocator {
if (dirty) { if (dirty) {
if (anyToRemove) processDeletions(); if (anyToRemove) processDeletions();
// XXX ARRAY_BUFFER is bound and reset
vbo.bind(); vbo.bind();
if (realloc()) { if (realloc()) {
uploadAll(); uploadAll();
@ -182,25 +184,25 @@ public class ModelPool implements ModelAllocator {
@Override @Override
public void setupState(GlVertexArray vao) { public void setupState(GlVertexArray vao) {
// XXX ARRAY_BUFFER is bound and not reset or restored
vbo.bind(); vbo.bind();
vao.enableArrays(getAttributeCount()); vao.enableArrays(getAttributeCount());
vao.bindAttributes(0, vertexType.getLayout()); vao.bindAttributes(0, vertexType.getLayout());
ebo.bind();
} }
@Override @Override
public void drawCall() { public void drawCall() {
GL32.glDrawElementsBaseVertex(GlPrimitive.TRIANGLES.glEnum, ebo.elementCount, ebo.eboIndexType.asGLType, 0, first); GL32.glDrawElementsBaseVertex(GlPrimitive.TRIANGLES.glEnum, ebo.getElementCount(), ebo.getEboIndexType().asGLType, 0, first);
} }
@Override @Override
public void drawInstances(int instanceCount) { public void drawInstances(int instanceCount) {
if (!valid()) return; if (!valid()) return;
ebo.bind();
//Backend.log.info(StringUtil.args("drawElementsInstancedBaseVertex", GlPrimitive.TRIANGLES, ebo.elementCount, ebo.eboIndexType, 0, instanceCount, first)); //Backend.log.info(StringUtil.args("drawElementsInstancedBaseVertex", GlPrimitive.TRIANGLES, ebo.elementCount, ebo.eboIndexType, 0, instanceCount, first));
GL32.glDrawElementsInstancedBaseVertex(GlPrimitive.TRIANGLES.glEnum, ebo.elementCount, ebo.eboIndexType.asGLType, 0, instanceCount, first); GL32.glDrawElementsInstancedBaseVertex(GlPrimitive.TRIANGLES.glEnum, ebo.getElementCount(), ebo.getEboIndexType().asGLType, 0, instanceCount, first);
} }
@Override @Override

View file

@ -58,6 +58,7 @@ public class VBOModel implements BufferedModel {
* The VBO/VAO should be bound externally. * The VBO/VAO should be bound externally.
*/ */
public void setupState(GlVertexArray vao) { public void setupState(GlVertexArray vao) {
// XXX ARRAY_BUFFER is bound and not reset or restored
vbo.bind(); vbo.bind();
vao.enableArrays(getAttributeCount()); vao.enableArrays(getAttributeCount());
vao.bindAttributes(0, getLayout()); vao.bindAttributes(0, getLayout());

View file

@ -1,21 +1,21 @@
package com.jozufozu.flywheel.core; package com.jozufozu.flywheel.core;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.lwjgl.opengl.GL32;
import org.lwjgl.opengl.GL32C;
import org.lwjgl.system.MemoryUtil; import org.lwjgl.system.MemoryUtil;
import com.jozufozu.flywheel.backend.gl.GlNumericType; import com.jozufozu.flywheel.backend.gl.GlNumericType;
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.GlBufferUsage;
import com.jozufozu.flywheel.backend.gl.buffer.MappedGlBuffer;
import com.jozufozu.flywheel.backend.model.ElementBuffer; import com.jozufozu.flywheel.backend.model.ElementBuffer;
import com.jozufozu.flywheel.event.ReloadRenderersEvent; import com.jozufozu.flywheel.event.ReloadRenderersEvent;
import com.mojang.blaze3d.vertex.VertexFormat; import com.mojang.blaze3d.vertex.VertexFormat;
import it.unimi.dsi.fastutil.ints.Int2ReferenceArrayMap;
import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
/** /**
* A class to manage EBOs that index quads as triangles. * A class to manage EBOs that index quads as triangles.
*/ */
@ -37,76 +37,74 @@ public class QuadConverter {
return INSTANCE; return INSTANCE;
} }
private final MappedGlBuffer ebo; private final Int2ReferenceMap<ElementBuffer> cache = new Int2ReferenceArrayMap<>();
private final int ebo;
private int quadCapacity; private int quadCapacity;
public QuadConverter() { public QuadConverter() {
this.ebo = new MappedGlBuffer(GlBufferType.ELEMENT_ARRAY_BUFFER); this.ebo = GL32.glGenBuffers();
this.quadCapacity = 0; this.quadCapacity = 0;
} }
public ElementBuffer quads2Tris(int quads) { public ElementBuffer quads2Tris(int quads) {
int indexCount = quads * 6;
if (quads > quadCapacity) { if (quads > quadCapacity) {
ebo.bind(); grow(quads * 2);
ebo.ensureCapacity((long) indexCount * GlNumericType.UINT.getByteWidth());
try (MappedBuffer map = ebo.getBuffer()) {
ByteBuffer indices = map.unwrap();
fillBuffer(indices, quads);
} }
ebo.unbind();
return cache.computeIfAbsent(quads, this::createElementBuffer);
}
@NotNull
private ElementBuffer createElementBuffer(int quads) {
return new ElementBuffer(ebo, quads * 6, VertexFormat.IndexType.INT);
}
private void grow(int quads) {
int byteSize = quads * 6 * GlNumericType.UINT.getByteWidth();
final long ptr = MemoryUtil.nmemAlloc(byteSize);
fillBuffer(ptr, quads);
// XXX ARRAY_BUFFER is bound and reset
final var bufferType = GlBufferType.ARRAY_BUFFER;
final int oldBuffer = bufferType.getBoundBuffer();
bufferType.bind(ebo);
GL32C.nglBufferData(bufferType.glEnum, byteSize, ptr, GlBufferUsage.STATIC_DRAW.glEnum);
bufferType.bind(oldBuffer);
MemoryUtil.nmemFree(ptr);
this.quadCapacity = quads; this.quadCapacity = quads;
} }
return new ElementBuffer(ebo, indexCount, VertexFormat.IndexType.INT);
}
public void delete() { public void delete() {
ebo.delete(); GL32.glDeleteBuffers(ebo);
this.quadCapacity = 0; this.quadCapacity = 0;
} }
private void fillBuffer(ByteBuffer indices, int quads) { private void fillBuffer(long ptr, int quads) {
long addr = MemoryUtil.memAddress(indices);
int numVertices = 4 * quads; int numVertices = 4 * quads;
int baseVertex = 0; int baseVertex = 0;
while (baseVertex < numVertices) { while (baseVertex < numVertices) {
// writeQuadIndices(indices, baseVertex); writeQuadIndicesUnsafe(ptr, baseVertex);
writeQuadIndicesUnsafe(addr, baseVertex);
baseVertex += 4; baseVertex += 4;
addr += 6 * 4; ptr += 6 * 4;
} }
// ((Buffer) indices).flip();
} }
private void writeQuadIndices(ByteBuffer indices, int baseVertex) { private void writeQuadIndicesUnsafe(long ptr, int baseVertex) {
// triangle a // triangle a
indices.putInt(baseVertex); MemoryUtil.memPutInt(ptr, baseVertex);
indices.putInt(baseVertex + 1); MemoryUtil.memPutInt(ptr + 4, baseVertex + 1);
indices.putInt(baseVertex + 2); MemoryUtil.memPutInt(ptr + 8, baseVertex + 2);
// triangle b // triangle b
indices.putInt(baseVertex); MemoryUtil.memPutInt(ptr + 12, baseVertex);
indices.putInt(baseVertex + 2); MemoryUtil.memPutInt(ptr + 16, baseVertex + 2);
indices.putInt(baseVertex + 3); MemoryUtil.memPutInt(ptr + 20, baseVertex + 3);
} }
private void writeQuadIndicesUnsafe(long addr, int baseVertex) { // make sure this gets reset first, so it has a chance to repopulate
// triangle a
MemoryUtil.memPutInt(addr, baseVertex);
MemoryUtil.memPutInt(addr + 4, baseVertex + 1);
MemoryUtil.memPutInt(addr + 8, baseVertex + 2);
// triangle b
MemoryUtil.memPutInt(addr + 12, baseVertex);
MemoryUtil.memPutInt(addr + 16, baseVertex + 2);
MemoryUtil.memPutInt(addr + 20, baseVertex + 3);
}
// make sure this gets reset first so it has a chance to repopulate
public static void onRendererReload(ReloadRenderersEvent event) { public static void onRendererReload(ReloadRenderersEvent event) {
if (INSTANCE != null) { if (INSTANCE != null) {
INSTANCE.delete(); INSTANCE.delete();

View file

@ -18,6 +18,7 @@ public class CrumblingGroup<P extends CrumblingProgram> extends InstancedMateria
super(owner, type); super(owner, type);
} }
// XXX See notes of overriden method
@Override @Override
public void render(Matrix4f viewProjection, double camX, double camY, double camZ, RenderLayer layer) { public void render(Matrix4f viewProjection, double camX, double camY, double camZ, RenderLayer layer) {
type.setupRenderState(); type.setupRenderState();

View file

@ -65,6 +65,7 @@ public class CrumblingRenderer {
Vec3 cameraPos = camera.getPosition(); Vec3 cameraPos = camera.getPosition();
// XXX Restore state
GlStateTracker.State restoreState = GlStateTracker.getRestoreState(); GlStateTracker.State restoreState = GlStateTracker.getRestoreState();
CrumblingRenderer.renderBreaking(activeStages, new RenderLayerEvent(level, null, stack, null, cameraPos.x, cameraPos.y, cameraPos.z)); CrumblingRenderer.renderBreaking(activeStages, new RenderLayerEvent(level, null, stack, null, cameraPos.x, cameraPos.y, cameraPos.z));
restoreState.restore(); restoreState.restore();
@ -87,6 +88,7 @@ public class CrumblingRenderer {
instanceManager.beginFrame(SerialTaskEngine.INSTANCE, info); instanceManager.beginFrame(SerialTaskEngine.INSTANCE, info);
// XXX Each call applies another restore state even though we are already inside of a restore state
materials.render(SerialTaskEngine.INSTANCE, event); materials.render(SerialTaskEngine.INSTANCE, event);
instanceManager.invalidate(); instanceManager.invalidate();
@ -94,6 +96,9 @@ public class CrumblingRenderer {
} }
// XXX Inconsistent GL state cleanup
// If texture binding and active unit need to be restored, store them in variables before GL state is changed
// instead of guessing that unit 0 and crumbling tex 0 are correct
GlTextureUnit.T0.makeActive(); GlTextureUnit.T0.makeActive();
AbstractTexture breaking = textureManager.getTexture(ModelBakery.BREAKING_LOCATIONS.get(0)); AbstractTexture breaking = textureManager.getTexture(ModelBakery.BREAKING_LOCATIONS.get(0));
if (breaking != null) RenderSystem.bindTexture(breaking.getId()); if (breaking != null) RenderSystem.bindTexture(breaking.getId());

View file

@ -3,13 +3,14 @@ package com.jozufozu.flywheel.core.model;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.function.Supplier; import java.util.function.Supplier;
import org.lwjgl.opengl.GL15;
import org.lwjgl.opengl.GL32;
import org.lwjgl.system.MemoryUtil; import org.lwjgl.system.MemoryUtil;
import com.jozufozu.flywheel.api.vertex.VertexList; import com.jozufozu.flywheel.api.vertex.VertexList;
import com.jozufozu.flywheel.api.vertex.VertexType; import com.jozufozu.flywheel.api.vertex.VertexType;
import com.jozufozu.flywheel.backend.gl.buffer.GlBufferType; import com.jozufozu.flywheel.backend.gl.buffer.GlBufferType;
import com.jozufozu.flywheel.backend.gl.buffer.GlBufferUsage; import com.jozufozu.flywheel.backend.gl.buffer.GlBufferUsage;
import com.jozufozu.flywheel.backend.gl.buffer.MappedGlBuffer;
import com.jozufozu.flywheel.backend.model.ElementBuffer; import com.jozufozu.flywheel.backend.model.ElementBuffer;
import com.jozufozu.flywheel.core.Formats; import com.jozufozu.flywheel.core.Formats;
import com.jozufozu.flywheel.core.QuadConverter; import com.jozufozu.flywheel.core.QuadConverter;
@ -64,10 +65,15 @@ public class BlockModel implements Model {
ByteBuffer indexBuffer = MemoryTracker.create(src.capacity()); ByteBuffer indexBuffer = MemoryTracker.create(src.capacity());
MemoryUtil.memCopy(src, indexBuffer); MemoryUtil.memCopy(src, indexBuffer);
eboSupplier = () -> { eboSupplier = () -> {
int vbo = GL32.glGenBuffers();
MappedGlBuffer vbo = new MappedGlBuffer(GlBufferType.ELEMENT_ARRAY_BUFFER, GlBufferUsage.STATIC_DRAW); // XXX ARRAY_BUFFER is bound and restored
var bufferType = GlBufferType.ARRAY_BUFFER;
vbo.upload(indexBuffer); var oldBuffer = bufferType.getBoundBuffer();
bufferType.bind(vbo);
GL15.glBufferData(bufferType.glEnum, indexBuffer, GlBufferUsage.STATIC_DRAW.glEnum);
bufferType.bind(oldBuffer);
MemoryUtil.memFree(indexBuffer);
return new ElementBuffer(vbo, drawState.indexCount(), drawState.indexType()); return new ElementBuffer(vbo, drawState.indexCount(), drawState.indexType());
}; };

View file

@ -45,6 +45,7 @@ public interface Model {
return Formats.POS_TEX_NORMAL; return Formats.POS_TEX_NORMAL;
} }
// XXX Since this is public API (technically) we cannot make assumptions about what GL state this method can use or modify unless a contract is established.
/** /**
* Create an element buffer object that indexes the vertices of this model. * Create an element buffer object that indexes the vertices of this model.
* *