Should use separate attribute format correctly

- Switch to object oriented vertex array impl
- Expose vao api similar to separate attribute format
- For DSA, directly call methods
- For 33, defer state changes until bindForDraw is called
- Inline instanced mesh #drawInstances method in favor of more
  fine-grained control over binding order
- Move buffer binding to GlStateTracker
- Remove raw bindVAO(0) from indirect engine
- Make VertexAttribute a sealed interface.
- Fix ebo restore state by always updating it and never restoring it
- Remove restore state wrapping in DrawCall
- Also misc cleanup.
This commit is contained in:
Jozufozu 2023-05-06 16:14:30 -07:00
parent 19824e7a3c
commit e2bcc5f325
29 changed files with 398 additions and 405 deletions

View file

@ -1,11 +1,9 @@
package com.jozufozu.flywheel.backend.engine.indirect;
import static org.lwjgl.opengl.GL15.glBindBuffer;
import static org.lwjgl.opengl.GL15.glDeleteBuffers;
import static org.lwjgl.opengl.GL15.nglDeleteBuffers;
import static org.lwjgl.opengl.GL30.GL_MAP_FLUSH_EXPLICIT_BIT;
import static org.lwjgl.opengl.GL30.GL_MAP_WRITE_BIT;
import static org.lwjgl.opengl.GL40.GL_DRAW_INDIRECT_BUFFER;
import static org.lwjgl.opengl.GL43.GL_SHADER_STORAGE_BUFFER;
import static org.lwjgl.opengl.GL44.GL_DYNAMIC_STORAGE_BIT;
import static org.lwjgl.opengl.GL44.GL_MAP_PERSISTENT_BIT;
@ -21,6 +19,7 @@ import static org.lwjgl.opengl.GL45.nglNamedBufferSubData;
import org.lwjgl.system.MemoryUtil;
import org.lwjgl.system.Pointer;
import com.jozufozu.flywheel.gl.buffer.GlBufferType;
import com.jozufozu.flywheel.lib.memory.FlwMemoryTracker;
import com.jozufozu.flywheel.lib.memory.MemoryBlock;
@ -49,22 +48,22 @@ public class IndirectBuffers {
private static final long BATCH_SIZE_OFFSET = TARGET_SIZE_OFFSET + PTR_SIZE;
private static final long DRAW_SIZE_OFFSET = BATCH_SIZE_OFFSET + PTR_SIZE;
final MemoryBlock buffers;
final long objectStride;
int object;
int target;
int batch;
int draw;
private final MemoryBlock buffers;
private final long objectStride;
private int object;
private int target;
private int batch;
private int draw;
long objectPtr;
long batchPtr;
long drawPtr;
int maxObjectCount = 0;
int maxDrawCount = 0;
private int maxObjectCount = 0;
private int maxDrawCount = 0;
float objectGrowthFactor = 2f;
float drawGrowthFactor = 2f;
private static final float OBJECT_GROWTH_FACTOR = 2f;
private static final float DRAW_GROWTH_FACTOR = 2f;
IndirectBuffers(long objectStride) {
this.objectStride = objectStride;
@ -81,20 +80,11 @@ public class IndirectBuffers {
}
void updateCounts(int objectCount, int drawCount) {
if (objectCount > maxObjectCount) {
var newObjectCount = maxObjectCount;
while (newObjectCount <= objectCount) {
newObjectCount *= objectGrowthFactor;
}
createObjectStorage(newObjectCount);
createObjectStorage((int) (objectCount * OBJECT_GROWTH_FACTOR));
}
if (drawCount > maxDrawCount) {
var newDrawCount = maxDrawCount;
while (newDrawCount <= drawCount) {
newDrawCount *= drawGrowthFactor;
}
createDrawStorage(newDrawCount);
createDrawStorage((int) (drawCount * DRAW_GROWTH_FACTOR));
}
final long objectSize = objectStride * objectCount;
@ -114,7 +104,7 @@ public class IndirectBuffers {
var targetSize = INT_SIZE * objectCount;
if (maxObjectCount > 0) {
var ptr = buffers.ptr();
final long ptr = buffers.ptr();
nglCreateBuffers(3, ptr);
int objectNew = MemoryUtil.memGetInt(ptr);
@ -181,21 +171,17 @@ public class IndirectBuffers {
}
public void bindForCompute() {
multiBind(BUFFER_COUNT);
multiBind();
}
public void bindForDraw() {
multiBind(BUFFER_COUNT);
glBindBuffer(GL_DRAW_INDIRECT_BUFFER, draw);
multiBind();
GlBufferType.DRAW_INDIRECT_BUFFER.bind(draw);
}
private void multiBind(int bufferCount) {
if (bufferCount > BUFFER_COUNT) {
throw new IllegalArgumentException("Can't bind more than " + BUFFER_COUNT + " buffers");
}
private void multiBind() {
final long ptr = buffers.ptr();
nglBindBuffersRange(GL_SHADER_STORAGE_BUFFER, 0, bufferCount, ptr, ptr + OFFSET_OFFSET, ptr + SIZE_OFFSET);
nglBindBuffersRange(GL_SHADER_STORAGE_BUFFER, 0, IndirectBuffers.BUFFER_COUNT, ptr, ptr + OFFSET_OFFSET, ptr + SIZE_OFFSET);
}
void flushBatchIDs(long length) {

View file

@ -1,6 +1,5 @@
package com.jozufozu.flywheel.backend.engine.indirect;
import static org.lwjgl.opengl.GL30.glBindVertexArray;
import static org.lwjgl.opengl.GL42.GL_COMMAND_BARRIER_BIT;
import static org.lwjgl.opengl.GL42.glMemoryBarrier;
import static org.lwjgl.opengl.GL43.GL_SHADER_STORAGE_BARRIER_BIT;
@ -9,6 +8,7 @@ import static org.lwjgl.opengl.GL43.glDispatchCompute;
import com.jozufozu.flywheel.api.event.RenderStage;
import com.jozufozu.flywheel.api.instance.Instance;
import com.jozufozu.flywheel.api.instance.InstanceType;
import com.jozufozu.flywheel.api.layout.BufferLayout;
import com.jozufozu.flywheel.api.vertex.VertexType;
import com.jozufozu.flywheel.backend.compile.IndirectPrograms;
import com.jozufozu.flywheel.backend.engine.UniformBuffer;
@ -51,7 +51,7 @@ public class IndirectCullingGroup<I extends Instance> {
meshPool = new IndirectMeshPool(vertexType, 1024);
vertexArray = new GlVertexArray();
vertexArray = GlVertexArray.create();
elementBuffer = QuadConverter.getInstance()
.quads2Tris(2048).glBuffer;
@ -64,7 +64,9 @@ public class IndirectCullingGroup<I extends Instance> {
private void setupVertexArray() {
vertexArray.setElementBuffer(elementBuffer);
vertexArray.bindAttributes(vertexType.getLayout(), meshPool.vbo, 0, 0);
BufferLayout type = vertexType.getLayout();
vertexArray.bindVertexBuffer(0, meshPool.vbo, 0, type.getStride());
vertexArray.bindAttributes(0, 0, type.attributes());
}
void beginFrame() {
@ -116,7 +118,6 @@ public class IndirectCullingGroup<I extends Instance> {
memoryBarrier();
drawSet.submit(stage);
glBindVertexArray(0);
}
private void memoryBarrier() {

View file

@ -1,6 +1,5 @@
package com.jozufozu.flywheel.backend.engine.instancing;
import com.jozufozu.flywheel.gl.GlStateTracker;
import com.jozufozu.flywheel.gl.array.GlVertexArray;
public class DrawCall {
@ -15,7 +14,7 @@ public class DrawCall {
this.mesh = mesh;
meshAttributes = this.mesh.getAttributeCount();
vao = new GlVertexArray();
vao = GlVertexArray.create();
}
public boolean isInvalid() {
@ -27,15 +26,19 @@ public class DrawCall {
return;
}
try (var ignored = GlStateTracker.getRestoreState()) {
instancer.update();
instancer.update();
instancer.bindToVAO(vao, meshAttributes);
if (instancer.getInstanceCount() > 0) {
mesh.drawInstances(vao, instancer.getInstanceCount());
}
int instanceCount = instancer.getInstanceCount();
if (instanceCount <= 0 || mesh.isEmpty()) {
return;
}
instancer.bindToVAO(vao, meshAttributes);
mesh.setup(vao);
vao.bindForDraw();
mesh.draw(instanceCount);
}
public void delete() {

View file

@ -79,16 +79,14 @@ public class GPUInstancer<I extends Instance> extends AbstractInstancer<I> {
}
}
public void bindToVAO(GlVertexArray vao, int attributeOffset) {
public void bindToVAO(GlVertexArray vao, int startAttrib) {
if (!boundTo.add(vao)) {
return;
}
vao.bindAttributes(instanceFormat, vbo.handle(), attributeOffset, 0L);
for (int i = 0; i < instanceFormat.getAttributeCount(); i++) {
vao.setAttributeDivisor(attributeOffset + i, 1);
}
vao.bindVertexBuffer(1, vbo.handle(), 0L, instanceStride);
vao.setBindingDivisor(1, 1);
vao.bindAttributes(1, startAttrib, instanceFormat.attributes());
}
public void delete() {

View file

@ -11,6 +11,7 @@ import org.jetbrains.annotations.Nullable;
import org.lwjgl.opengl.GL32;
import com.jozufozu.flywheel.Flywheel;
import com.jozufozu.flywheel.api.layout.BufferLayout;
import com.jozufozu.flywheel.api.model.Mesh;
import com.jozufozu.flywheel.api.vertex.VertexType;
import com.jozufozu.flywheel.gl.GlPrimitive;
@ -172,7 +173,7 @@ public class InstancedMeshPool {
return deleted;
}
private boolean isEmpty() {
public boolean isEmpty() {
return mesh.isEmpty() || isDeleted();
}
@ -182,25 +183,16 @@ public class InstancedMeshPool {
boundTo.clear();
}
public void drawInstances(GlVertexArray vao, int instanceCount) {
if (isEmpty()) {
return;
}
setup(vao);
draw(instanceCount);
}
private void setup(GlVertexArray vao) {
public void setup(GlVertexArray vao) {
if (boundTo.add(vao)) {
vao.bindAttributes(vertexType.getLayout(), InstancedMeshPool.this.vbo.handle(), 0, byteIndex);
BufferLayout type = vertexType.getLayout();
vao.bindVertexBuffer(0, InstancedMeshPool.this.vbo.handle(), byteIndex, type.getStride());
vao.bindAttributes(0, 0, type.attributes());
vao.setElementBuffer(ebo.glBuffer);
}
vao.bindForDraw();
}
private void draw(int instanceCount) {
public void draw(int instanceCount) {
if (instanceCount > 1) {
GL32.glDrawElementsInstanced(GlPrimitive.TRIANGLES.glEnum, ebo.getElementCount(), ebo.getEboIndexType().asGLType, 0, instanceCount);
} else {

View file

@ -8,8 +8,6 @@ import org.lwjgl.opengl.GL20C;
import org.lwjgl.opengl.GLCapabilities;
import org.lwjgl.system.MemoryStack;
import com.jozufozu.flywheel.gl.array.GlVertexArray;
import net.minecraft.Util;
/**
@ -19,6 +17,7 @@ import net.minecraft.Util;
* system.
*/
public class GlCompat {
public static final boolean ALLOW_DSA = true;
public static final GLCapabilities CAPABILITIES = GL.createCapabilities();
private static final boolean amd = _decideIfWeAreAMDWindows();
private static final boolean supportsIndirect = _decideIfWeSupportIndirect();
@ -31,7 +30,7 @@ public class GlCompat {
}
public static boolean supportsInstancing() {
return GlVertexArray.IMPL != null;
return true;
}
public static boolean supportsIndirect() {

View file

@ -17,6 +17,7 @@ public enum GlNumericType {
SHORT(2, "short", GL11.GL_SHORT),
UINT(4, "uint", GL11.GL_UNSIGNED_INT),
INT(4, "int", GL11.GL_INT),
DOUBLE(8, "double", GL11.GL_DOUBLE),
;
private static final GlNumericType[] VALUES = values();

View file

@ -6,18 +6,18 @@ public abstract class GlObject {
private int handle = INVALID_HANDLE;
protected final void setHandle(int handle) {
protected final void handle(int handle) {
this.handle = handle;
}
public final int handle() {
this.checkHandle();
checkHandle();
return this.handle;
}
protected final void checkHandle() {
if (this.isInvalid()) {
if (isInvalid()) {
throw new IllegalStateException("handle is not valid.");
}
}
@ -31,7 +31,7 @@ public abstract class GlObject {
}
public void delete() {
if (this.isInvalid()) {
if (isInvalid()) {
throw new IllegalStateException("handle already deleted.");
}

View file

@ -45,6 +45,12 @@ public class GlStateTracker {
}
}
public static void bindBuffer(GlBufferType type, int buffer) {
if (BUFFERS[type.ordinal()] != buffer || type == GlBufferType.ELEMENT_ARRAY_BUFFER) {
GlStateManager._glBindBuffer(type.glEnum, buffer);
}
}
public record State(int[] buffers, int vao, int program) implements AutoCloseable {
public void restore() {
if (vao != GlStateTracker.vao) {
@ -54,7 +60,7 @@ public class GlStateTracker {
GlBufferType[] values = GlBufferType.values();
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]);
}
}

View file

@ -7,7 +7,7 @@ public class GlTexture extends GlObject {
public GlTexture(int textureType) {
this.textureType = textureType;
setHandle(GL20.glGenTextures());
handle(GL20.glGenTextures());
}
@Override

View file

@ -1,99 +1,43 @@
package com.jozufozu.flywheel.gl.array;
import java.util.List;
import org.lwjgl.opengl.GL32;
import com.jozufozu.flywheel.api.layout.BufferLayout;
import com.jozufozu.flywheel.gl.GlObject;
import com.jozufozu.flywheel.gl.GlStateTracker;
import com.mojang.blaze3d.platform.GlStateManager;
public class GlVertexArray extends GlObject {
public static final VertexArray IMPL = new VertexArray.DSA().fallback();
private static final int MAX_ATTRIBS = GL32.glGetInteger(GL32.GL_MAX_VERTEX_ATTRIBS);
public abstract class GlVertexArray extends GlObject {
protected static final int MAX_ATTRIBS = GL32.glGetInteger(GL32.GL_MAX_VERTEX_ATTRIBS);
protected static final int MAX_ATTRIB_BINDINGS = 16;
/**
* Whether each attribute is enabled.
*/
private final boolean[] enabled = new boolean[MAX_ATTRIBS];
/**
* Each attribute's divisor.
*/
private final int[] divisors = new int[MAX_ATTRIBS];
/**
* Each attribute's data type.
*/
private final VertexAttribute[] attributes = new VertexAttribute[MAX_ATTRIBS];
/**
* The VBO to which each attribute is bound.
*/
private final int[] targets = new int[MAX_ATTRIBS];
/**
* Each attribute's offset.
*/
private final long[] offsets = new long[MAX_ATTRIBS];
/**
* Each attribute's stride.
*/
private final int[] strides = new int[MAX_ATTRIBS];
private int elementBufferBinding = 0;
public GlVertexArray() {
setHandle(IMPL.create());
public static GlVertexArray create() {
if (GlVertexArrayDSA.SUPPORTED) {
return new GlVertexArrayDSA();
} else if (GlVertexArrayGL3.Core33.SUPPORTED) {
return new GlVertexArrayGL3.Core33();
} else if (GlVertexArrayGL3.ARB.SUPPORTED) {
return new GlVertexArrayGL3.ARB();
} else {
return new GlVertexArrayGL3.Core();
}
}
public void bindForDraw() {
GlStateTracker.bindVao(handle());
}
public static void unbind() {
GlStateManager._glBindVertexArray(0);
}
public abstract void bindVertexBuffer(int bindingIndex, int vbo, long offset, int stride);
public void bindAttributes(BufferLayout type, final int vbo, final int startAttrib, final long startOffset) {
final int vao = handle();
final int stride = type.getStride();
public abstract void setBindingDivisor(int bindingIndex, int divisor);
int index = startAttrib;
long offset = startOffset;
for (var attribute : type.attributes()) {
if (!enabled[index]) {
IMPL.enableAttrib(vao, index);
enabled[index] = true;
}
public abstract void bindAttributes(int bindingIndex, int startAttribIndex, List<VertexAttribute> vertexAttributes);
if (shouldSetupAttrib(index, vbo, stride, offset, attribute)) {
IMPL.setupAttrib(vao, index, vbo, stride, offset, attribute);
targets[index] = vbo;
attributes[index] = attribute;
offsets[index] = offset;
strides[index] = stride;
}
index++;
offset += attribute.getByteWidth();
}
}
private boolean shouldSetupAttrib(int index, int vbo, int stride, long offset, VertexAttribute attribute) {
return targets[index] != vbo || offsets[index] != offset || strides[index] != stride || !attribute.equals(attributes[index]);
}
public abstract void setElementBuffer(int ebo);
@Override
protected void deleteInternal(int handle) {
GlStateManager._glDeleteVertexArrays(handle);
}
public void setAttributeDivisor(int index, int divisor) {
if (divisors[index] != divisor) {
IMPL.setAttribDivisor(handle(), index, divisor);
divisors[index] = divisor;
}
}
public void setElementBuffer(int ebo) {
if (elementBufferBinding != ebo) {
IMPL.setElementBuffer(handle(), ebo);
elementBufferBinding = ebo;
}
}
}

View file

@ -0,0 +1,91 @@
package com.jozufozu.flywheel.gl.array;
import java.util.BitSet;
import java.util.List;
import org.lwjgl.opengl.GL45C;
import org.lwjgl.system.Checks;
import com.jozufozu.flywheel.gl.GlCompat;
import com.jozufozu.flywheel.util.FlwUtil;
public class GlVertexArrayDSA extends GlVertexArray {
public static final boolean SUPPORTED = isSupported();
private final BitSet attributeEnabled = new BitSet(MAX_ATTRIBS);
private final VertexAttribute[] attributes = new VertexAttribute[MAX_ATTRIBS];
private final int[] attributeBindings = FlwUtil.initArray(MAX_ATTRIBS, -1);
private final int[] bindingBuffers = new int[MAX_ATTRIB_BINDINGS];
private final long[] bindingOffsets = new long[MAX_ATTRIB_BINDINGS];
private final int[] bindingStrides = new int[MAX_ATTRIB_BINDINGS];
private final int[] bindingDivisors = new int[MAX_ATTRIB_BINDINGS];
private int elementBufferBinding = 0;
public GlVertexArrayDSA() {
handle(GL45C.glCreateVertexArrays());
}
@Override
public void bindVertexBuffer(final int bindingIndex, final int vbo, final long offset, final int stride) {
if (bindingBuffers[bindingIndex] != vbo || bindingOffsets[bindingIndex] != offset || bindingStrides[bindingIndex] != stride) {
GL45C.glVertexArrayVertexBuffer(handle(), bindingIndex, vbo, offset, stride);
bindingBuffers[bindingIndex] = vbo;
bindingOffsets[bindingIndex] = offset;
bindingStrides[bindingIndex] = stride;
}
}
@Override
public void setBindingDivisor(final int bindingIndex, final int divisor) {
if (bindingDivisors[bindingIndex] != divisor) {
GL45C.glVertexArrayBindingDivisor(handle(), bindingIndex, divisor);
bindingDivisors[bindingIndex] = divisor;
}
}
@Override
public void bindAttributes(final int bindingIndex, final int startAttribIndex, List<VertexAttribute> vertexAttributes) {
final int handle = handle();
int attribIndex = startAttribIndex;
int offset = 0;
for (var attribute : vertexAttributes) {
if (!attributeEnabled.get(attribIndex)) {
GL45C.glEnableVertexArrayAttrib(handle, attribIndex);
attributeEnabled.set(attribIndex);
}
if (!attribute.equals(attributes[attribIndex])) {
if (attribute instanceof VertexAttribute.Float f) {
GL45C.glVertexArrayAttribFormat(handle, attribIndex, f.size(), f.type()
.getGlEnum(), f.normalized(), offset);
} else if (attribute instanceof VertexAttribute.Int vi) {
GL45C.glVertexArrayAttribIFormat(handle, attribIndex, vi.size(), vi.type()
.getGlEnum(), offset);
}
attributes[attribIndex] = attribute;
}
if (attributeBindings[attribIndex] != bindingIndex) {
GL45C.glVertexArrayAttribBinding(handle, attribIndex, bindingIndex);
attributeBindings[attribIndex] = bindingIndex;
}
attribIndex++;
offset += attribute.getByteWidth();
}
}
@Override
public void setElementBuffer(int ebo) {
if (elementBufferBinding != ebo) {
GL45C.glVertexArrayElementBuffer(handle(), ebo);
elementBufferBinding = ebo;
}
}
private static boolean isSupported() {
var c = GlCompat.CAPABILITIES;
return GlCompat.ALLOW_DSA && Checks.checkFunctions(c.glCreateVertexArrays, c.glVertexArrayElementBuffer, c.glVertexArrayVertexBuffer, c.glVertexArrayBindingDivisor, c.glVertexArrayAttribBinding, c.glEnableVertexArrayAttrib, c.glVertexArrayAttribFormat, c.glVertexArrayAttribIFormat);
}
}

View file

@ -0,0 +1,153 @@
package com.jozufozu.flywheel.gl.array;
import java.util.BitSet;
import java.util.List;
import org.lwjgl.opengl.ARBInstancedArrays;
import org.lwjgl.opengl.GL20C;
import org.lwjgl.opengl.GL30;
import org.lwjgl.opengl.GL32;
import org.lwjgl.opengl.GL33C;
import org.lwjgl.system.Checks;
import com.jozufozu.flywheel.gl.GlCompat;
import com.jozufozu.flywheel.gl.buffer.GlBufferType;
import com.jozufozu.flywheel.util.FlwUtil;
public abstract class GlVertexArrayGL3 extends GlVertexArray {
private final BitSet attributeDirty = new BitSet(MAX_ATTRIBS);
private final int[] attributeOffsets = new int[MAX_ATTRIBS];
private final VertexAttribute[] attributes = new VertexAttribute[MAX_ATTRIBS];
private final int[] attributeBindings = FlwUtil.initArray(MAX_ATTRIBS, -1);
private final int[] bindingBuffers = new int[MAX_ATTRIB_BINDINGS];
private final long[] bindingOffsets = new long[MAX_ATTRIB_BINDINGS];
private final int[] bindingStrides = new int[MAX_ATTRIB_BINDINGS];
private final int[] bindingDivisors = new int[MAX_ATTRIB_BINDINGS];
private int elementBufferBinding = 0;
public GlVertexArrayGL3() {
handle(GL30.glGenVertexArrays());
}
@Override
public void bindForDraw() {
super.bindForDraw();
for (int attribIndex = attributeDirty.nextSetBit(0); attribIndex < MAX_ATTRIB_BINDINGS && attribIndex >= 0; attribIndex = attributeDirty.nextSetBit(attribIndex + 1)) {
int bindingIndex = attributeBindings[attribIndex];
var attribute = attributes[attribIndex];
if (bindingIndex == -1 || attribute == null) {
continue;
}
GlBufferType.ARRAY_BUFFER.bind(bindingBuffers[bindingIndex]);
GL20C.glEnableVertexAttribArray(attribIndex);
long offset = bindingOffsets[bindingIndex] + attributeOffsets[attribIndex];
int stride = bindingStrides[bindingIndex];
if (attribute instanceof VertexAttribute.Float f) {
GL32.glVertexAttribPointer(attribIndex, f.size(), f.type()
.getGlEnum(), f.normalized(), stride, offset);
} else if (attribute instanceof VertexAttribute.Int vi) {
GL32.glVertexAttribIPointer(attribIndex, vi.size(), vi.type()
.getGlEnum(), stride, offset);
}
int divisor = bindingDivisors[bindingIndex];
if (divisor != 0) {
setDivisor(attribIndex, divisor);
}
}
GlBufferType.ELEMENT_ARRAY_BUFFER.bind(elementBufferBinding);
attributeDirty.clear();
}
protected abstract void setDivisor(int attribIndex, int divisor);
@Override
public void bindVertexBuffer(int bindingIndex, int vbo, long offset, int stride) {
if (bindingBuffers[bindingIndex] != vbo || bindingOffsets[bindingIndex] != offset || bindingStrides[bindingIndex] != stride) {
bindingBuffers[bindingIndex] = vbo;
bindingOffsets[bindingIndex] = offset;
bindingStrides[bindingIndex] = stride;
for (int attribIndex = 0; attribIndex < attributeBindings.length; attribIndex++) {
if (attributeBindings[attribIndex] == bindingIndex) {
attributeDirty.set(attribIndex);
}
}
}
}
@Override
public void setBindingDivisor(int bindingIndex, int divisor) {
if (bindingDivisors[bindingIndex] != divisor) {
bindingDivisors[bindingIndex] = divisor;
}
}
@Override
public void bindAttributes(int bindingIndex, int startAttribIndex, List<VertexAttribute> vertexAttributes) {
int attribIndex = startAttribIndex;
int offset = 0;
for (VertexAttribute attribute : vertexAttributes) {
attributeBindings[attribIndex] = bindingIndex;
attributes[attribIndex] = attribute;
attributeOffsets[attribIndex] = offset;
attributeDirty.set(attribIndex);
attribIndex++;
offset += attribute.getByteWidth();
}
}
@Override
public void setElementBuffer(int ebo) {
elementBufferBinding = ebo;
}
public static class Core33 extends GlVertexArrayGL3 {
public static final boolean SUPPORTED = isSupported();
@Override
protected void setDivisor(int attribIndex, int divisor) {
GL33C.glVertexAttribDivisor(attribIndex, divisor);
}
private static boolean isSupported() {
return Checks.checkFunctions(GlCompat.CAPABILITIES.glVertexAttribDivisor);
}
}
public static class ARB extends GlVertexArrayGL3 {
public static final boolean SUPPORTED = isSupported();
@Override
protected void setDivisor(int attribIndex, int divisor) {
ARBInstancedArrays.glVertexAttribDivisorARB(attribIndex, divisor);
}
private static boolean isSupported() {
return Checks.checkFunctions(GlCompat.CAPABILITIES.glVertexAttribDivisorARB);
}
}
public static class Core extends GlVertexArrayGL3 {
@Override
protected void setDivisor(int attribIndex, int divisor) {
throw new UnsupportedOperationException("Instanced arrays are not supported");
}
@Override
public void setBindingDivisor(int bindingIndex, int divisor) {
throw new UnsupportedOperationException("Instanced arrays are not supported");
}
}
}

View file

@ -1,133 +0,0 @@
package com.jozufozu.flywheel.gl.array;
import org.lwjgl.opengl.ARBInstancedArrays;
import org.lwjgl.opengl.GL20C;
import org.lwjgl.opengl.GL30C;
import org.lwjgl.opengl.GL33C;
import org.lwjgl.opengl.GL45C;
import org.lwjgl.system.Checks;
import com.jozufozu.flywheel.gl.GlCompat;
import com.jozufozu.flywheel.gl.GlStateTracker;
import com.jozufozu.flywheel.gl.buffer.GlBufferType;
public interface VertexArray {
int create();
void setElementBuffer(int vao, int elementBuffer);
void enableAttrib(int vao, int index);
void setupAttrib(int vao, int index, int vbo, int stride, long offset, VertexAttribute attribute);
void setAttribDivisor(int vao, int attrib, int divisor);
abstract class GL3 implements VertexArray {
@Override
public int create() {
return GL30C.glGenVertexArrays();
}
@Override
public void setElementBuffer(int vao, int elementBuffer) {
GlStateTracker.bindVao(vao);
GlBufferType.ELEMENT_ARRAY_BUFFER.bind(elementBuffer);
}
@Override
public void enableAttrib(int vao, int index) {
GlStateTracker.bindVao(vao);
GL20C.glEnableVertexAttribArray(index);
}
@Override
public void setupAttrib(int vao, int index, int vbo, int stride, long offset, VertexAttribute attribute) {
GlStateTracker.bindVao(vao);
GlBufferType.ARRAY_BUFFER.bind(vbo);
attribute.setup(offset, index, stride);
}
}
class InstancedArraysARB extends GL3 {
@Override
public void setAttribDivisor(int vao, int attrib, int divisor) {
GlStateTracker.bindVao(vao);
ARBInstancedArrays.glVertexAttribDivisorARB(attrib, divisor);
}
public VertexArray fallback() {
if (Checks.checkFunctions(GlCompat.CAPABILITIES.glVertexAttribDivisorARB)) {
return this;
}
// null signals that we don't support instancing.
return null;
}
}
class InstancedArraysCore extends GL3 {
@Override
public void setAttribDivisor(int vao, int attrib, int divisor) {
GlStateTracker.bindVao(vao);
GL33C.glVertexAttribDivisor(attrib, divisor);
}
public VertexArray fallback() {
// We know vertex arrays are supported because minecraft required GL32.
if (Checks.checkFunctions(GlCompat.CAPABILITIES.glVertexAttribDivisor)) {
return this;
}
return new InstancedArraysARB().fallback();
}
}
class DSA implements VertexArray {
@Override
public int create() {
return GL45C.glCreateVertexArrays();
}
@Override
public void setElementBuffer(int vao, int elementBuffer) {
GL45C.glVertexArrayElementBuffer(vao, elementBuffer);
}
@Override
public void enableAttrib(int vao, int index) {
GL45C.glEnableVertexArrayAttrib(vao, index);
}
@Override
public void setupAttrib(int vao, int index, int vbo, int stride, long offset, VertexAttribute attribute) {
GL45C.glVertexArrayVertexBuffer(vao, index, vbo, offset, stride);
attribute.setupDSA(vao, index);
}
@Override
public void setAttribDivisor(int vao, int attrib, int divisor) {
GL45C.glVertexArrayBindingDivisor(vao, attrib, divisor);
}
public VertexArray fallback() {
var c = GlCompat.CAPABILITIES;
if (Checks.checkFunctions(c.glCreateVertexArrays, c.glVertexArrayElementBuffer, c.glEnableVertexArrayAttrib, c.glVertexArrayVertexBuffer, c.glVertexArrayAttribFormat, c.glVertexArrayAttribIFormat)) {
if (Checks.checkFunctions(c.glVertexArrayBindingDivisor)) {
return this;
} else if (Checks.checkFunctions(c.glVertexArrayVertexAttribDivisorEXT)) {
// Seems like this may happen when a driver supports
// ARB_direct_state_access but not core instanced arrays?
return new InstancedArraysEXTDSA();
}
}
return new InstancedArraysCore().fallback();
}
}
class InstancedArraysEXTDSA extends DSA {
@Override
public void setAttribDivisor(int vao, int attrib, int divisor) {
ARBInstancedArrays.glVertexArrayVertexAttribDivisorEXT(vao, attrib, divisor);
}
}
}

View file

@ -1,22 +1,34 @@
package com.jozufozu.flywheel.gl.array;
public interface VertexAttribute {
import com.jozufozu.flywheel.gl.GlNumericType;
public sealed interface VertexAttribute {
int getByteWidth();
/**
* Apply this vertex attribute to the bound vertex array.
* A bindable attribute in a vertex array.
*
* @param offset The byte offset to the first element of the attribute.
* @param i The attribute index.
* @param stride The byte stride between consecutive elements of the attribute.
* @param type The type of the attribute, e.g. GL_FLOAT.
* @param size The number of components in the attribute, e.g. 3 for a vec3.
* @param normalized Whether the data is normalized.
*/
void setup(long offset, int i, int stride);
record Float(GlNumericType type, int size, boolean normalized) implements VertexAttribute {
@Override
public int getByteWidth() {
return size * type.getByteWidth();
}
}
/**
* Use DSA to apply this vertex attribute to the given vertex array.
* A bindable attribute in a vertex array.
*
* @param vaobj The vertex array object to modify.
* @param i The attribute index.
* @param type The type of the attribute, e.g. GL_INT.
* @param size The number of components in the attribute, e.g. 3 for a vec3.
*/
void setupDSA(int vaobj, int i);
record Int(GlNumericType type, int size) implements VertexAttribute {
@Override
public int getByteWidth() {
return size * type.getByteWidth();
}
}
}

View file

@ -1,30 +0,0 @@
package com.jozufozu.flywheel.gl.array;
import org.lwjgl.opengl.GL32;
import org.lwjgl.opengl.GL45;
import com.jozufozu.flywheel.gl.GlNumericType;
/**
* A bindable attribute in a vertex array.
*
* @param type The type of the attribute, e.g. GL_FLOAT.
* @param size The number of components in the attribute, e.g. 3 for a vec3.
* @param normalized Whether the data is normalized.
*/
public record VertexAttributeF(GlNumericType type, int size, boolean normalized) implements VertexAttribute {
@Override
public int getByteWidth() {
return size * type.getByteWidth();
}
@Override
public void setup(long offset, int i, int stride) {
GL32.glVertexAttribPointer(i, size(), type().getGlEnum(), normalized(), stride, offset);
}
@Override
public void setupDSA(int vaobj, int i) {
GL45.glVertexArrayAttribFormat(vaobj, i, size(), type().getGlEnum(), normalized(), 0);
}
}

View file

@ -1,29 +0,0 @@
package com.jozufozu.flywheel.gl.array;
import org.lwjgl.opengl.GL32;
import org.lwjgl.opengl.GL45;
import com.jozufozu.flywheel.gl.GlNumericType;
/**
* A bindable attribute in a vertex array.
*
* @param type The type of the attribute, e.g. GL_INT.
* @param size The number of components in the attribute, e.g. 3 for a vec3.
*/
public record VertexAttributeI(GlNumericType type, int size) implements VertexAttribute {
@Override
public int getByteWidth() {
return size * type.getByteWidth();
}
@Override
public void setup(long offset, int i, int stride) {
GL32.glVertexAttribIPointer(i, size(), type().getGlEnum(), stride, offset);
}
@Override
public void setupDSA(int vaobj, int i) {
GL45.glVertexArrayAttribIFormat(vaobj, i, size(), type().getGlEnum(), 0);
}
}

View file

@ -46,12 +46,16 @@ public interface Buffer {
}
public Buffer fallback() {
var c = GlCompat.CAPABILITIES;
if (Checks.checkFunctions(c.glCreateBuffers, c.glNamedBufferData, c.glCopyNamedBufferSubData, c.glMapNamedBufferRange, c.glUnmapNamedBuffer)) {
if (GlCompat.ALLOW_DSA && dsaMethodsAvailable()) {
return this;
}
return new Core();
}
private static boolean dsaMethodsAvailable() {
var c = GlCompat.CAPABILITIES;
return Checks.checkFunctions(c.glCreateBuffers, c.glNamedBufferData, c.glCopyNamedBufferSubData, c.glMapNamedBufferRange, c.glUnmapNamedBuffer);
}
}
class Core implements Buffer {

View file

@ -13,10 +13,6 @@ public class ElementBuffer {
this.glBuffer = backing;
}
public void bind() {
GlBufferType.ELEMENT_ARRAY_BUFFER.bind(glBuffer);
}
public int getElementCount() {
return elementCount;
}

View file

@ -26,7 +26,7 @@ public class GlBuffer extends GlObject {
}
public GlBuffer(GlBufferUsage usage) {
setHandle(IMPL.create());
handle(IMPL.create());
this.usage = usage;
}
@ -71,7 +71,7 @@ public class GlBuffer extends GlObject {
IMPL.data(newHandle, size, MemoryUtil.NULL, usage.glEnum);
IMPL.copyData(oldHandle, newHandle, 0, 0, oldSize);
glDeleteBuffers(oldHandle);
setHandle(newHandle);
handle(newHandle);
FlwMemoryTracker._allocGPUMemory(size);
}

View file

@ -9,7 +9,6 @@ import org.lwjgl.opengl.GL42;
import org.lwjgl.opengl.GL43;
import com.jozufozu.flywheel.gl.GlStateTracker;
import com.mojang.blaze3d.platform.GlStateManager;
public enum GlBufferType {
ARRAY_BUFFER(GL15C.GL_ARRAY_BUFFER, GL15C.GL_ARRAY_BUFFER_BINDING),
@ -55,16 +54,6 @@ public enum GlBufferType {
}
public void bind(int buffer) {
if (getBoundBuffer() != buffer) {
GlStateManager._glBindBuffer(glEnum, buffer);
}
}
public void unbind() {
bind(0);
}
public int getBoundBuffer() {
return GlStateTracker.getBuffer(this);
GlStateTracker.bindBuffer(this, buffer);
}
}

View file

@ -14,7 +14,7 @@ public class GlProgram extends GlObject {
private static final Logger LOGGER = LogUtils.getLogger();
public GlProgram(int handle) {
setHandle(handle);
handle(handle);
}
public void bind() {

View file

@ -13,7 +13,7 @@ public class GlShader extends GlObject {
this.type = type;
this.name = name;
setHandle(handle);
handle(handle);
}
@Override

View file

@ -1,8 +1,7 @@
package com.jozufozu.flywheel.lib.layout;
import com.jozufozu.flywheel.gl.GlNumericType;
import com.jozufozu.flywheel.gl.array.VertexAttributeF;
import com.jozufozu.flywheel.gl.array.VertexAttributeI;
import com.jozufozu.flywheel.gl.array.VertexAttribute;
public class CommonItems {
private static final String VEC2_TYPE = "vec2";
@ -17,51 +16,51 @@ public class CommonItems {
private static final String LIGHT_COORD_TYPE = "LightCoord";
public static final VecInput LIGHT_COORD = VecInput.builder()
.vertexAttribute(new VertexAttributeI(GlNumericType.USHORT, 2))
.vertexAttribute(new VertexAttribute.Int(GlNumericType.USHORT, 2))
.typeName(IVEC2_TYPE)
.packedTypeName(LIGHT_COORD_TYPE)
.unpackingFunction(expr -> expr.callFunction("unpackLightCoord"))
.build();
public static final VecInput FLOAT = VecInput.builder()
.vertexAttribute(new VertexAttributeF(GlNumericType.FLOAT, 1, false))
.vertexAttribute(new VertexAttribute.Float(GlNumericType.FLOAT, 1, false))
.typeName(FLOAT_TYPE)
.packedTypeName(FLOAT_TYPE)
.build();
public static final VecInput NORM_3x8 = VecInput.builder()
.vertexAttribute(new VertexAttributeF(GlNumericType.BYTE, 3, true))
.vertexAttribute(new VertexAttribute.Float(GlNumericType.BYTE, 3, true))
.typeName(VEC3_TYPE)
.packedTypeName(UINT_TYPE)
.unpackingFunction(expr -> expr.callFunction("unpackSnorm4x8")
.swizzle("xyz"))
.build();
public static final VecInput UNORM_4x8 = VecInput.builder()
.vertexAttribute(new VertexAttributeF(GlNumericType.UBYTE, 4, true))
.vertexAttribute(new VertexAttribute.Float(GlNumericType.UBYTE, 4, true))
.typeName(VEC4_TYPE)
.packedTypeName(UINT_TYPE)
.unpackingFunction(expr -> expr.callFunction("unpackUnorm4x8"))
.build();
public static final VecInput UNORM_3x8 = VecInput.builder()
.vertexAttribute(new VertexAttributeF(GlNumericType.UBYTE, 3, true))
.vertexAttribute(new VertexAttribute.Float(GlNumericType.UBYTE, 3, true))
.typeName(VEC3_TYPE)
.packedTypeName(UINT_TYPE)
.unpackingFunction(expr -> expr.callFunction("unpackUnorm4x8")
.swizzle("xyz"))
.build();
public static final VecInput VEC4 = VecInput.builder()
.vertexAttribute(new VertexAttributeF(GlNumericType.FLOAT, 4, false))
.vertexAttribute(new VertexAttribute.Float(GlNumericType.FLOAT, 4, false))
.typeName(VEC4_TYPE)
.packedTypeName(VEC4F_TYPE)
.unpackingFunction(expr -> expr.callFunction("unpackVec4F"))
.build();
public static final VecInput VEC3 = VecInput.builder()
.vertexAttribute(new VertexAttributeF(GlNumericType.FLOAT, 3, false))
.vertexAttribute(new VertexAttribute.Float(GlNumericType.FLOAT, 3, false))
.typeName(VEC3_TYPE)
.packedTypeName(VEC3F_TYPE)
.unpackingFunction(expr -> expr.callFunction("unpackVec3F"))
.build();
public static final VecInput VEC2 = VecInput.builder()
.vertexAttribute(new VertexAttributeF(GlNumericType.FLOAT, 2, false))
.vertexAttribute(new VertexAttribute.Float(GlNumericType.FLOAT, 2, false))
.typeName(VEC2_TYPE)
.packedTypeName(VEC2F_TYPE)
.unpackingFunction(expr -> expr.callFunction("unpackVec2F"))

View file

@ -5,7 +5,6 @@ import java.util.function.Consumer;
import com.jozufozu.flywheel.api.layout.InputType;
import com.jozufozu.flywheel.gl.GlNumericType;
import com.jozufozu.flywheel.gl.array.VertexAttribute;
import com.jozufozu.flywheel.gl.array.VertexAttributeF;
import com.jozufozu.flywheel.glsl.generate.GlslExpr;
public record MatInput(int rows, int cols, String typeName, String packedTypeName,
@ -14,7 +13,7 @@ public record MatInput(int rows, int cols, String typeName, String packedTypeNam
@Override
public void provideAttributes(Consumer<VertexAttribute> consumer) {
for (int i = 0; i < rows; i++) {
consumer.accept(new VertexAttributeF(GlNumericType.FLOAT, cols, false));
consumer.accept(new VertexAttribute.Float(GlNumericType.FLOAT, cols, false));
}
}

View file

@ -127,7 +127,7 @@ public class SimpleMaterial implements Material {
}
public static GlStateShard fromVanilla(RenderStateShard vanillaShard) {
return new GlStateShard(() -> vanillaShard.setupRenderState(), () -> vanillaShard.clearRenderState());
return new GlStateShard(vanillaShard::setupRenderState, vanillaShard::clearRenderState);
}
public Runnable getSetup() {

View file

@ -47,16 +47,20 @@ public class FullscreenQuad {
Flywheel.LOGGER.error("Could not create fullscreen quad.", e);
}
vao = new GlVertexArray();
vao = GlVertexArray.create();
vao.bindAttributes(LAYOUT, vbo.handle(), 0, 0L);
}
vao.bindVertexBuffer(0, vbo.handle(), 0L, LAYOUT.getStride());
vao.bindAttributes(0, 0, LAYOUT.attributes());
}
}
/**
* Draw the fullscreen quad.<br>
* note: may bind a VAO, but will not restore prior state.
*/
public void draw() {
vao.bindForDraw();
glDrawArrays(GL_TRIANGLES, 0, 6);
GlVertexArray.unbind();
}
public void delete() {

View file

@ -2,15 +2,14 @@ package com.jozufozu.flywheel.lib.util;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.lwjgl.opengl.GL32;
import org.lwjgl.opengl.GL32C;
import org.lwjgl.system.MemoryUtil;
import com.jozufozu.flywheel.api.event.ReloadRenderersEvent;
import com.jozufozu.flywheel.gl.GlNumericType;
import com.jozufozu.flywheel.gl.buffer.ElementBuffer;
import com.jozufozu.flywheel.gl.buffer.GlBufferType;
import com.jozufozu.flywheel.gl.buffer.GlBuffer;
import com.jozufozu.flywheel.gl.buffer.GlBufferUsage;
import com.mojang.blaze3d.platform.GlStateManager;
import com.mojang.blaze3d.vertex.VertexFormat;
import it.unimi.dsi.fastutil.ints.Int2ReferenceArrayMap;
@ -41,7 +40,7 @@ public class QuadConverter {
private int quadCapacity;
public QuadConverter() {
this.ebo = GL32.glGenBuffers();
this.ebo = GlBuffer.IMPL.create();
this.quadCapacity = 0;
}
@ -64,11 +63,7 @@ public class QuadConverter {
fillBuffer(ptr, quads);
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);
GlBuffer.IMPL.data(ebo, byteSize, ptr, GlBufferUsage.STATIC_DRAW.glEnum);
MemoryUtil.nmemFree(ptr);
@ -76,7 +71,7 @@ public class QuadConverter {
}
public void delete() {
GL32.glDeleteBuffers(ebo);
GlStateManager._glDeleteBuffers(ebo);
this.cache.clear();
this.quadCapacity = 0;
}

View file

@ -1,5 +1,6 @@
package com.jozufozu.flywheel.util;
import java.util.Arrays;
import java.util.Collections;
import java.util.Set;
import java.util.WeakHashMap;
@ -49,8 +50,20 @@ public final class FlwUtil {
public static PoseStack copyPoseStack(PoseStack stack) {
PoseStack copy = new PoseStack();
copy.last().pose().load(stack.last().pose());
copy.last().normal().load(stack.last().normal());
copy.last()
.pose()
.load(stack.last()
.pose());
copy.last()
.normal()
.load(stack.last()
.normal());
return copy;
}
public static int[] initArray(int size, int fill) {
var out = new int[size];
Arrays.fill(out, fill);
return out;
}
}