Separate separate attributes

- Add new GL43 vertex array impl
- GlNumericType cleaning
- Make GlBuffer growth more abstract
- Organize VertexArrayGL3, track bound ebo
This commit is contained in:
Jozufozu 2023-05-07 15:38:45 -07:00
parent e2bcc5f325
commit aa38ae4125
11 changed files with 175 additions and 95 deletions

View file

@ -56,7 +56,7 @@ public class BufferLayout {
private static int calculateStride(List<VertexAttribute> layoutItems) {
int stride = 0;
for (var spec : layoutItems) {
stride += spec.getByteWidth();
stride += spec.byteWidth();
}
return stride;
}

View file

@ -41,7 +41,7 @@ public class GPUInstancer<I extends Instance> extends AbstractInstancer<I> {
}
vbo = new GlBuffer(GlBufferUsage.DYNAMIC_DRAW);
vbo.growthMargin(instanceStride * 16);
vbo.growthFunction(l -> Math.max(l + (long) instanceStride * 16, (long) (l * 1.6)));
}
public void update() {
@ -70,7 +70,7 @@ public class GPUInstancer<I extends Instance> extends AbstractInstancer<I> {
int count = instances.size();
for (int i = changed.nextSetBit(0); i >= 0 && i < count; i = changed.nextSetBit(i + 1)) {
writer.write(ptr + instanceStride * i, instances.get(i));
writer.write(ptr + (long) instanceStride * i, instances.get(i));
}
changed.clear();

View file

@ -40,7 +40,7 @@ public class InstancedMeshPool {
this.vertexType = vertexType;
int stride = vertexType.getLayout().getStride();
vbo = new GlBuffer();
vbo.growthMargin(stride * 32);
vbo.growthFunction(l -> Math.max(l + stride * 32L, (long) (l * 1.6)));
}
public VertexType getVertexType() {

View file

@ -1,12 +1,5 @@
package com.jozufozu.flywheel.gl;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.Locale;
import java.util.Map;
import java.util.stream.Collectors;
import org.jetbrains.annotations.Nullable;
import org.lwjgl.opengl.GL11;
public enum GlNumericType {
@ -20,13 +13,9 @@ public enum GlNumericType {
DOUBLE(8, "double", GL11.GL_DOUBLE),
;
private static final GlNumericType[] VALUES = values();
private static final Map<String, GlNumericType> NAME_LOOKUP = Arrays.stream(VALUES)
.collect(Collectors.toMap(GlNumericType::getTypeName, type -> type));
private final int byteWidth;
private final String typeName;
private final int glEnum;
public final int byteWidth;
public final String typeName;
public final int glEnum;
GlNumericType(int bytes, String name, int glEnum) {
this.byteWidth = bytes;
@ -34,31 +23,16 @@ public enum GlNumericType {
this.glEnum = glEnum;
}
public int getByteWidth() {
return this.byteWidth;
public int byteWidth() {
return byteWidth;
}
public String getTypeName() {
return this.typeName;
public String typeName() {
return typeName;
}
public int getGlEnum() {
return this.glEnum;
}
public void castAndBuffer(ByteBuffer buf, int val) {
if (this == UBYTE || this == BYTE) {
buf.put((byte) val);
} else if (this == USHORT || this == SHORT) {
buf.putShort((short) val);
} else if (this == UINT || this == INT) {
buf.putInt(val);
}
}
@Nullable
public static GlNumericType byName(String name) {
return name == null ? null : NAME_LOOKUP.get(name.toLowerCase(Locale.ROOT));
public int glEnum() {
return glEnum;
}
@Override

View file

@ -15,6 +15,8 @@ public abstract class GlVertexArray extends GlObject {
public static GlVertexArray create() {
if (GlVertexArrayDSA.SUPPORTED) {
return new GlVertexArrayDSA();
} else if (GlVertexArraySeparateAttributes.SUPPORTED) {
return new GlVertexArraySeparateAttributes();
} else if (GlVertexArrayGL3.Core33.SUPPORTED) {
return new GlVertexArrayGL3.Core33();
} else if (GlVertexArrayGL3.ARB.SUPPORTED) {

View file

@ -57,11 +57,9 @@ public class GlVertexArrayDSA extends GlVertexArray {
if (!attribute.equals(attributes[attribIndex])) {
if (attribute instanceof VertexAttribute.Float f) {
GL45C.glVertexArrayAttribFormat(handle, attribIndex, f.size(), f.type()
.getGlEnum(), f.normalized(), offset);
GL45C.glVertexArrayAttribFormat(handle, attribIndex, f.size(), f.type().glEnum, f.normalized(), offset);
} else if (attribute instanceof VertexAttribute.Int vi) {
GL45C.glVertexArrayAttribIFormat(handle, attribIndex, vi.size(), vi.type()
.getGlEnum(), offset);
GL45C.glVertexArrayAttribIFormat(handle, attribIndex, vi.size(), vi.type().glEnum, offset);
}
attributes[attribIndex] = attribute;
}
@ -72,7 +70,7 @@ public class GlVertexArrayDSA extends GlVertexArray {
}
attribIndex++;
offset += attribute.getByteWidth();
offset += attribute.byteWidth();
}
}

View file

@ -23,7 +23,8 @@ public abstract class GlVertexArrayGL3 extends GlVertexArray {
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;
private int requestedElementBuffer = 0;
private int boundElementBuffer = 0;
public GlVertexArrayGL3() {
handle(GL30.glGenVertexArrays());
@ -33,42 +34,11 @@ public abstract class GlVertexArrayGL3 extends GlVertexArray {
public void bindForDraw() {
super.bindForDraw();
for (int attribIndex = attributeDirty.nextSetBit(0); attribIndex < MAX_ATTRIB_BINDINGS && attribIndex >= 0; attribIndex = attributeDirty.nextSetBit(attribIndex + 1)) {
maybeUpdateAttributes();
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();
maybeUpdateEBOBinding();
}
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) {
@ -83,7 +53,6 @@ public abstract class GlVertexArrayGL3 extends GlVertexArray {
}
}
}
@Override
public void setBindingDivisor(int bindingIndex, int divisor) {
if (bindingDivisors[bindingIndex] != divisor) {
@ -104,15 +73,59 @@ public abstract class GlVertexArrayGL3 extends GlVertexArray {
attributeDirty.set(attribIndex);
attribIndex++;
offset += attribute.getByteWidth();
offset += attribute.byteWidth();
}
}
@Override
public void setElementBuffer(int ebo) {
elementBufferBinding = ebo;
requestedElementBuffer = ebo;
}
private void maybeUpdateEBOBinding() {
if (requestedElementBuffer != boundElementBuffer) {
GlBufferType.ELEMENT_ARRAY_BUFFER.bind(requestedElementBuffer);
boundElementBuffer = requestedElementBuffer;
}
}
private void maybeUpdateAttributes() {
for (int attribIndex = attributeDirty.nextSetBit(0); attribIndex < MAX_ATTRIB_BINDINGS && attribIndex >= 0; attribIndex = attributeDirty.nextSetBit(attribIndex + 1)) {
updateAttribute(attribIndex);
}
attributeDirty.clear();
}
private void updateAttribute(int attribIndex) {
int bindingIndex = attributeBindings[attribIndex];
var attribute = attributes[attribIndex];
if (bindingIndex == -1 || attribute == null) {
return;
}
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()
.glEnum(), f.normalized(), stride, offset);
} else if (attribute instanceof VertexAttribute.Int vi) {
GL32.glVertexAttribIPointer(attribIndex, vi.size(), vi.type()
.glEnum(), stride, offset);
}
int divisor = bindingDivisors[bindingIndex];
if (divisor != 0) {
setDivisor(attribIndex, divisor);
}
}
protected abstract void setDivisor(int attribIndex, int divisor);
public static class Core33 extends GlVertexArrayGL3 {
public static final boolean SUPPORTED = isSupported();

View file

@ -0,0 +1,93 @@
package com.jozufozu.flywheel.gl.array;
import java.util.BitSet;
import java.util.List;
import org.lwjgl.opengl.GL43C;
import org.lwjgl.system.Checks;
import com.jozufozu.flywheel.gl.GlCompat;
import com.jozufozu.flywheel.gl.GlStateTracker;
import com.jozufozu.flywheel.gl.buffer.GlBufferType;
import com.jozufozu.flywheel.util.FlwUtil;
public class GlVertexArraySeparateAttributes 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 GlVertexArraySeparateAttributes() {
handle(GL43C.glGenVertexArrays());
}
@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) {
GlStateTracker.bindVao(handle());
GL43C.glBindVertexBuffer(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) {
GL43C.glVertexBindingDivisor(bindingIndex, divisor);
bindingDivisors[bindingIndex] = divisor;
}
}
@Override
public void bindAttributes(final int bindingIndex, final int startAttribIndex, List<VertexAttribute> vertexAttributes) {
GlStateTracker.bindVao(handle());
int attribIndex = startAttribIndex;
int offset = 0;
for (var attribute : vertexAttributes) {
if (!attributeEnabled.get(attribIndex)) {
GL43C.glEnableVertexAttribArray(attribIndex);
attributeEnabled.set(attribIndex);
}
if (!attribute.equals(attributes[attribIndex])) {
if (attribute instanceof VertexAttribute.Float f) {
GL43C.glVertexAttribFormat(attribIndex, f.size(), f.type().glEnum, f.normalized(), offset);
} else if (attribute instanceof VertexAttribute.Int vi) {
GL43C.glVertexAttribIFormat(attribIndex, vi.size(), vi.type().glEnum, offset);
}
attributes[attribIndex] = attribute;
}
if (attributeBindings[attribIndex] != bindingIndex) {
GL43C.glVertexAttribBinding(attribIndex, bindingIndex);
attributeBindings[attribIndex] = bindingIndex;
}
attribIndex++;
offset += attribute.byteWidth();
}
}
@Override
public void setElementBuffer(int ebo) {
if (elementBufferBinding != ebo) {
GlStateTracker.bindVao(handle());
GlBufferType.ELEMENT_ARRAY_BUFFER.bind(ebo);
elementBufferBinding = ebo;
}
}
private static boolean isSupported() {
var c = GlCompat.CAPABILITIES;
return GlCompat.ALLOW_DSA && Checks.checkFunctions(c.glBindVertexBuffer, c.glVertexBindingDivisor, c.glEnableVertexAttribArray, c.glVertexAttribFormat, c.glVertexAttribIFormat, c.glVertexAttribBinding);
}
}

View file

@ -3,7 +3,7 @@ package com.jozufozu.flywheel.gl.array;
import com.jozufozu.flywheel.gl.GlNumericType;
public sealed interface VertexAttribute {
int getByteWidth();
int byteWidth();
/**
* A bindable attribute in a vertex array.
@ -14,8 +14,8 @@ public sealed interface VertexAttribute {
*/
record Float(GlNumericType type, int size, boolean normalized) implements VertexAttribute {
@Override
public int getByteWidth() {
return size * type.getByteWidth();
public int byteWidth() {
return size * type.byteWidth();
}
}
@ -27,8 +27,8 @@ public sealed interface VertexAttribute {
*/
record Int(GlNumericType type, int size) implements VertexAttribute {
@Override
public int getByteWidth() {
return size * type.getByteWidth();
public int byteWidth() {
return size * type.byteWidth();
}
}
}

View file

@ -1,7 +1,5 @@
package com.jozufozu.flywheel.gl.buffer;
import static org.lwjgl.opengl.GL15.glDeleteBuffers;
import org.lwjgl.system.MemoryUtil;
import com.jozufozu.flywheel.gl.GlObject;
@ -9,6 +7,8 @@ import com.jozufozu.flywheel.lib.memory.FlwMemoryTracker;
import com.jozufozu.flywheel.lib.memory.MemoryBlock;
import com.mojang.blaze3d.platform.GlStateManager;
import it.unimi.dsi.fastutil.longs.LongUnaryOperator;
public class GlBuffer extends GlObject {
public static final Buffer IMPL = new Buffer.DSA().fallback();
protected final GlBufferUsage usage;
@ -17,9 +17,9 @@ public class GlBuffer extends GlObject {
*/
protected long size;
/**
* How much extra room to give the buffer when we reallocate.
* A mapping to adjust the size of the buffer when allocating.
*/
protected int growthMargin;
protected LongUnaryOperator growthFunction = LongUnaryOperator.identity();
public GlBuffer() {
this(GlBufferUsage.STATIC_DRAW);
@ -70,7 +70,7 @@ public class GlBuffer extends GlObject {
int newHandle = IMPL.create();
IMPL.data(newHandle, size, MemoryUtil.NULL, usage.glEnum);
IMPL.copyData(oldHandle, newHandle, 0, 0, oldSize);
glDeleteBuffers(oldHandle);
GlStateManager._glDeleteBuffers(oldHandle);
handle(newHandle);
FlwMemoryTracker._allocGPUMemory(size);
@ -80,7 +80,7 @@ public class GlBuffer extends GlObject {
* Increase the size of the buffer to at least the given capacity.
*/
private void increaseSize(long capacity) {
size = capacity + growthMargin;
size = growthFunction.apply(capacity);
}
public void upload(MemoryBlock directBuffer) {
@ -94,8 +94,8 @@ public class GlBuffer extends GlObject {
return new MappedBuffer(handle(), size);
}
public void growthMargin(int growthMargin) {
this.growthMargin = growthMargin;
public void growthFunction(LongUnaryOperator growthFunction) {
this.growthFunction = growthFunction;
}
public long size() {

View file

@ -58,7 +58,7 @@ public class QuadConverter {
}
private void grow(int quads) {
int byteSize = quads * 6 * GlNumericType.UINT.getByteWidth();
int byteSize = quads * 6 * GlNumericType.UINT.byteWidth();
final long ptr = MemoryUtil.nmemAlloc(byteSize);
fillBuffer(ptr, quads);