Apply directly to the buffer

- Add DSA abstraction to GlBuffer
- Move backing impls to their respective classes
- Remove versioned package, move impls to appropriate package
- Make fallback methods take no arguments
- Do VAO check/bind in GlStateTracker
- GlVertexArray actually checks the tracked info when binding attributes
- Add separate enable* and setup* methods in VertexArray
- Add support for DSA VAO with ARB instanced arrays incase there are
  some particularly cursed drivers out there
- Misc. cleanup
This commit is contained in:
Jozufozu 2023-05-01 23:43:01 -07:00
parent 12419fd50b
commit cda7eca655
13 changed files with 227 additions and 139 deletions

View file

@ -7,7 +7,7 @@ import com.jozufozu.flywheel.backend.compile.InstancingPrograms;
import com.jozufozu.flywheel.backend.engine.batching.BatchingEngine;
import com.jozufozu.flywheel.backend.engine.indirect.IndirectEngine;
import com.jozufozu.flywheel.backend.engine.instancing.InstancingEngine;
import com.jozufozu.flywheel.gl.versioned.GlCompat;
import com.jozufozu.flywheel.gl.GlCompat;
import com.jozufozu.flywheel.lib.backend.SimpleBackend;
import com.jozufozu.flywheel.lib.context.Contexts;
import com.jozufozu.flywheel.lib.util.ShadersModHandler;

View file

@ -10,9 +10,9 @@ import org.jetbrains.annotations.NotNull;
import org.lwjgl.opengl.GL20;
import com.jozufozu.flywheel.Flywheel;
import com.jozufozu.flywheel.gl.GlCompat;
import com.jozufozu.flywheel.gl.shader.GlShader;
import com.jozufozu.flywheel.gl.shader.ShaderType;
import com.jozufozu.flywheel.gl.versioned.GlCompat;
import com.jozufozu.flywheel.glsl.GLSLVersion;
import com.jozufozu.flywheel.glsl.SourceComponent;
import com.jozufozu.flywheel.glsl.SourceFile;

View file

@ -41,7 +41,7 @@ public class GPUInstancer<I extends Instance> extends AbstractInstancer<I> {
}
vbo = new GlBuffer(GlBufferUsage.DYNAMIC_DRAW);
vbo.setGrowthMargin(instanceStride * 16);
vbo.growthMargin(instanceStride * 16);
}
public void update() {

View file

@ -39,7 +39,7 @@ public class InstancedMeshPool {
this.vertexType = vertexType;
int stride = vertexType.getLayout().getStride();
vbo = new GlBuffer();
vbo.setGrowthMargin(stride * 32);
vbo.growthMargin(stride * 32);
}
public VertexType getVertexType() {

View file

@ -1,4 +1,4 @@
package com.jozufozu.flywheel.gl.versioned;
package com.jozufozu.flywheel.gl;
import java.nio.ByteBuffer;
@ -8,6 +8,8 @@ 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;
/**
@ -17,17 +19,9 @@ import net.minecraft.Util;
* system.
*/
public class GlCompat {
private static final GLCapabilities caps;
public static final VertexArray vertexArray;
public static final boolean amd;
public static final boolean supportsIndirect;
static {
caps = GL.createCapabilities();
vertexArray = VertexArray.DSA.INSTANCE.fallback(caps);
supportsIndirect = _decideIfWeSupportIndirect();
amd = _decideIfWeAreAMDWindows();
}
public static final GLCapabilities CAPABILITIES = GL.createCapabilities();
private static final boolean amd = _decideIfWeAreAMDWindows();
private static final boolean supportsIndirect = _decideIfWeSupportIndirect();
private GlCompat() {
}
@ -37,7 +31,7 @@ public class GlCompat {
}
public static boolean supportsInstancing() {
return caps.OpenGL33 || caps.GL_ARB_instanced_arrays;
return GlVertexArray.IMPL != null;
}
public static boolean supportsIndirect() {
@ -45,12 +39,7 @@ public class GlCompat {
}
private static boolean _decideIfWeSupportIndirect() {
return caps.OpenGL46 || (
caps.GL_ARB_compute_shader &&
caps.GL_ARB_shader_draw_parameters &&
caps.GL_ARB_base_instance &&
caps.GL_ARB_multi_draw_indirect &&
caps.GL_ARB_direct_state_access);
return CAPABILITIES.OpenGL46 || (CAPABILITIES.GL_ARB_compute_shader && CAPABILITIES.GL_ARB_shader_draw_parameters && CAPABILITIES.GL_ARB_base_instance && CAPABILITIES.GL_ARB_multi_draw_indirect && CAPABILITIES.GL_ARB_direct_state_access);
}
/**

View file

@ -39,6 +39,12 @@ public class GlStateTracker {
return new State(BUFFERS.clone(), vao, program);
}
public static void bindVao(int vao) {
if (vao != GlStateTracker.vao) {
GlStateManager._glBindVertexArray(vao);
}
}
public record State(int[] buffers, int vao, int program) implements AutoCloseable {
public void restore() {
if (vao != GlStateTracker.vao) {

View file

@ -1,18 +1,14 @@
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.jozufozu.flywheel.gl.versioned.GlCompat;
import com.mojang.blaze3d.platform.GlStateManager;
@SuppressWarnings("MismatchedReadAndWriteOfArray")
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);
/**
@ -42,57 +38,61 @@ public class GlVertexArray extends GlObject {
private int elementBufferBinding = 0;
public GlVertexArray() {
setHandle(GlCompat.vertexArray.create());
setHandle(IMPL.create());
}
public void bindForDraw() {
if (!isBound()) {
GlStateManager._glBindVertexArray(handle());
}
}
private boolean isBound() {
return handle() == GlStateTracker.getVertexArray();
GlStateTracker.bindVao(handle());
}
public static void unbind() {
GlStateManager._glBindVertexArray(0);
}
public void bindAttributes(BufferLayout type, int vbo, int startAttrib, long startOffset) {
public void bindAttributes(BufferLayout type, final int vbo, final int startAttrib, final long startOffset) {
final int vao = handle();
final int stride = type.getStride();
List<VertexAttribute> vertexAttributes = type.attributes();
for (int i = 0; i < vertexAttributes.size(); i++) {
var attribute = vertexAttributes.get(i);
int index = i + startAttrib;
targets[index] = vbo;
attributes[index] = attribute;
offsets[index] = startOffset;
strides[index] = stride;
int index = startAttrib;
long offset = startOffset;
for (var attribute : type.attributes()) {
if (!enabled[index]) {
IMPL.enableAttrib(vao, index);
enabled[index] = true;
}
GlCompat.vertexArray.setupAttrib(handle(), index, vbo, stride, startOffset, attribute);
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;
}
startOffset += attribute.getByteWidth();
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]);
}
protected void deleteInternal(int handle) {
GlStateManager._glDeleteVertexArrays(handle);
}
public void setAttributeDivisor(int index, int divisor) {
if (divisors[index] != divisor) {
GlCompat.vertexArray.setAttribDivisor(handle(), index, divisor);
IMPL.setAttribDivisor(handle(), index, divisor);
divisors[index] = divisor;
}
}
public void setElementBuffer(int ebo) {
if (elementBufferBinding != ebo) {
GlCompat.vertexArray.setElementBuffer(handle(), ebo);
IMPL.setElementBuffer(handle(), ebo);
elementBufferBinding = ebo;
}
}

View file

@ -1,23 +1,23 @@
package com.jozufozu.flywheel.gl.versioned;
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.opengl.GLCapabilities;
import org.lwjgl.system.Checks;
import com.jozufozu.flywheel.gl.GlCompat;
import com.jozufozu.flywheel.gl.GlStateTracker;
import com.jozufozu.flywheel.gl.array.VertexAttribute;
import com.jozufozu.flywheel.gl.buffer.GlBufferType;
import com.mojang.blaze3d.platform.GlStateManager;
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);
@ -30,69 +30,58 @@ public interface VertexArray {
@Override
public void setElementBuffer(int vao, int elementBuffer) {
if (vao != GlStateTracker.getVertexArray()) {
GlStateManager._glBindVertexArray(vao);
}
GlStateTracker.bindVao(vao);
GlBufferType.ELEMENT_ARRAY_BUFFER.bind(elementBuffer);
}
@Override
public void setupAttrib(int vao, int index, int vbo, int stride, long offset, VertexAttribute attribute) {
if (vao != GlStateTracker.getVertexArray()) {
GlStateManager._glBindVertexArray(vao);
}
GlBufferType.ARRAY_BUFFER.bind(vbo);
public void enableAttrib(int vao, int index) {
GlStateTracker.bindVao(vao);
GL20C.glEnableVertexAttribArray(index);
attribute.setup(offset, index, stride);
}
@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 {
public static InstancedArraysARB INSTANCE = new InstancedArraysARB();
@Override
public void setAttribDivisor(int vao, int attrib, int divisor) {
if (vao != GlStateTracker.getVertexArray()) {
GlStateManager._glBindVertexArray(vao);
}
GlStateTracker.bindVao(vao);
ARBInstancedArrays.glVertexAttribDivisorARB(attrib, divisor);
}
public VertexArray fallback(GLCapabilities caps) {
return isSupported(caps) ? this : null;
}
private boolean isSupported(GLCapabilities caps) {
return Checks.checkFunctions(caps.glVertexAttribDivisorARB);
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 {
public static InstancedArraysCore INSTANCE = new InstancedArraysCore();
@Override
public void setAttribDivisor(int vao, int attrib, int divisor) {
if (vao != GlStateTracker.getVertexArray()) {
GlStateManager._glBindVertexArray(vao);
}
GlStateTracker.bindVao(vao);
GL33C.glVertexAttribDivisor(attrib, divisor);
}
public VertexArray fallback(GLCapabilities caps) {
return isSupported(caps) ? this : InstancedArraysARB.INSTANCE.fallback(caps);
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();
}
private static boolean isSupported(GLCapabilities caps) {
// We know vertex arrays are supported because minecraft required GL32.
return Checks.checkFunctions(caps.glVertexAttribDivisor);
}
}
class DSA implements VertexArray {
public static final DSA INSTANCE = new DSA();
@Override
public int create() {
return GL45C.glCreateVertexArrays();
@ -104,8 +93,12 @@ public interface VertexArray {
}
@Override
public void setupAttrib(int vao, int index, int vbo, int stride, long offset, VertexAttribute attribute) {
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);
}
@ -115,12 +108,26 @@ public interface VertexArray {
GL45C.glVertexArrayBindingDivisor(vao, attrib, divisor);
}
public VertexArray fallback(GLCapabilities caps) {
return isSupported(caps) ? this : InstancedArraysCore.INSTANCE.fallback(caps);
}
public VertexArray fallback() {
var c = GlCompat.CAPABILITIES;
if (Checks.checkFunctions(c.glCreateVertexArrays, c.glVertexArrayElementBuffer, c.glEnableVertexArrayAttrib, c.glVertexArrayVertexBuffer, c.glVertexArrayAttribFormat, c.glVertexArrayAttribIFormat)) {
private static boolean isSupported(GLCapabilities caps) {
return Checks.checkFunctions(caps.glCreateVertexArrays, caps.glVertexArrayElementBuffer, caps.glEnableVertexArrayAttrib, caps.glVertexArrayVertexBuffer, caps.glVertexArrayBindingDivisor, caps.glVertexArrayAttribFormat, caps.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

@ -13,7 +13,6 @@ import com.jozufozu.flywheel.gl.GlNumericType;
* @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();

View file

@ -12,7 +12,6 @@ import com.jozufozu.flywheel.gl.GlNumericType;
* @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();

View file

@ -0,0 +1,89 @@
package com.jozufozu.flywheel.gl.buffer;
import org.lwjgl.opengl.GL15;
import org.lwjgl.opengl.GL30;
import org.lwjgl.opengl.GL31;
import org.lwjgl.opengl.GL45C;
import org.lwjgl.system.Checks;
import com.jozufozu.flywheel.gl.GlCompat;
public interface Buffer {
int create();
void data(int vbo, long size, long ptr, int glEnum);
void copyData(int src, int dst, long srcOffset, long dstOffset, long size);
long mapRange(int handle, int offset, long size, int access);
void unmap(int handle);
class DSA implements Buffer {
@Override
public int create() {
return GL45C.glCreateBuffers();
}
@Override
public void data(int vbo, long size, long ptr, int glEnum) {
GL45C.nglNamedBufferData(vbo, size, ptr, glEnum);
}
@Override
public void copyData(int src, int dst, long srcOffset, long dstOffset, long size) {
GL45C.glCopyNamedBufferSubData(src, dst, srcOffset, dstOffset, size);
}
@Override
public long mapRange(int handle, int offset, long size, int access) {
return GL45C.nglMapNamedBufferRange(handle, offset, size, access);
}
@Override
public void unmap(int handle) {
GL45C.glUnmapNamedBuffer(handle);
}
public Buffer fallback() {
var c = GlCompat.CAPABILITIES;
if (Checks.checkFunctions(c.glCreateBuffers, c.glNamedBufferData, c.glCopyNamedBufferSubData, c.glMapNamedBufferRange, c.glUnmapNamedBuffer)) {
return this;
}
return new Core();
}
}
class Core implements Buffer {
@Override
public int create() {
return GL15.glGenBuffers();
}
@Override
public void data(int vbo, long size, long ptr, int glEnum) {
GlBufferType.COPY_WRITE_BUFFER.bind(vbo);
GL15.nglBufferData(GlBufferType.COPY_WRITE_BUFFER.glEnum, size, ptr, glEnum);
}
@Override
public void copyData(int src, int dst, long size, long srcOffset, long dstOffset) {
GlBufferType.COPY_READ_BUFFER.bind(src);
GlBufferType.COPY_WRITE_BUFFER.bind(dst);
GL31.glCopyBufferSubData(GlBufferType.COPY_READ_BUFFER.glEnum, GlBufferType.COPY_WRITE_BUFFER.glEnum, srcOffset, dstOffset, size);
}
@Override
public long mapRange(int handle, int offset, long size, int access) {
GlBufferType.COPY_READ_BUFFER.bind(handle);
return GL30.nglMapBufferRange(GlBufferType.COPY_READ_BUFFER.glEnum, 0, size, access);
}
@Override
public void unmap(int handle) {
GlBufferType.COPY_READ_BUFFER.bind(handle);
GL15.glUnmapBuffer(GlBufferType.COPY_READ_BUFFER.glEnum);
}
}
}

View file

@ -1,16 +1,16 @@
package com.jozufozu.flywheel.gl.buffer;
import static org.lwjgl.opengl.GL15.glBufferData;
import static org.lwjgl.opengl.GL15.glDeleteBuffers;
import static org.lwjgl.opengl.GL15.glGenBuffers;
import static org.lwjgl.opengl.GL15.nglBufferData;
import static org.lwjgl.opengl.GL31.glCopyBufferSubData;
import org.lwjgl.system.MemoryUtil;
import com.jozufozu.flywheel.gl.GlObject;
import com.jozufozu.flywheel.lib.memory.FlwMemoryTracker;
import com.jozufozu.flywheel.lib.memory.MemoryBlock;
import com.mojang.blaze3d.platform.GlStateManager;
public class GlBuffer extends GlObject {
public static final Buffer IMPL = new Buffer.DSA().fallback();
protected final GlBufferUsage usage;
/**
* The size (in bytes) of the buffer on the GPU.
@ -26,64 +26,67 @@ public class GlBuffer extends GlObject {
}
public GlBuffer(GlBufferUsage usage) {
setHandle(glGenBuffers());
setHandle(IMPL.create());
this.usage = usage;
}
/**
* @return true if the buffer was recreated.
*/
public boolean ensureCapacity(long size) {
if (size < 0) {
throw new IllegalArgumentException("Size " + size + " < 0");
public boolean ensureCapacity(long capacity) {
if (capacity < 0) {
throw new IllegalArgumentException("Size " + capacity + " < 0");
}
if (size == 0) {
if (capacity == 0) {
return false;
}
if (this.size == 0) {
this.size = size;
GlBufferType.COPY_WRITE_BUFFER.bind(handle());
glBufferData(GlBufferType.COPY_WRITE_BUFFER.glEnum, size, usage.glEnum);
FlwMemoryTracker._allocGPUMemory(size);
if (size == 0) {
alloc(capacity);
return true;
}
if (size > this.size) {
var oldSize = this.size;
this.size = size + growthMargin;
realloc(oldSize, this.size);
if (capacity > size) {
realloc(capacity);
return true;
}
return false;
}
private void realloc(long oldSize, long newSize) {
FlwMemoryTracker._freeGPUMemory(oldSize);
FlwMemoryTracker._allocGPUMemory(newSize);
var oldHandle = handle();
var newHandle = glGenBuffers();
private void alloc(long capacity) {
increaseSize(capacity);
IMPL.data(handle(), size, MemoryUtil.NULL, usage.glEnum);
FlwMemoryTracker._allocGPUMemory(size);
}
GlBufferType.COPY_READ_BUFFER.bind(oldHandle);
GlBufferType.COPY_WRITE_BUFFER.bind(newHandle);
glBufferData(GlBufferType.COPY_WRITE_BUFFER.glEnum, newSize, usage.glEnum);
glCopyBufferSubData(GlBufferType.COPY_READ_BUFFER.glEnum, GlBufferType.COPY_WRITE_BUFFER.glEnum, 0, 0, oldSize);
private void realloc(long capacity) {
FlwMemoryTracker._freeGPUMemory(size);
var oldSize = size;
increaseSize(capacity);
int oldHandle = handle();
int newHandle = IMPL.create();
IMPL.data(newHandle, size, MemoryUtil.NULL, usage.glEnum);
IMPL.copyData(oldHandle, newHandle, 0, 0, oldSize);
glDeleteBuffers(oldHandle);
setHandle(newHandle);
FlwMemoryTracker._allocGPUMemory(size);
}
/**
* Increase the size of the buffer to at least the given capacity.
*/
private void increaseSize(long capacity) {
size = capacity + growthMargin;
}
public void upload(MemoryBlock directBuffer) {
FlwMemoryTracker._freeGPUMemory(size);
GlBufferType.COPY_WRITE_BUFFER.bind(handle());
nglBufferData(GlBufferType.COPY_WRITE_BUFFER.glEnum, directBuffer.size(), directBuffer.ptr(), usage.glEnum);
this.size = directBuffer.size();
IMPL.data(handle(), directBuffer.size(), directBuffer.ptr(), usage.glEnum);
size = directBuffer.size();
FlwMemoryTracker._allocGPUMemory(size);
}
@ -91,16 +94,16 @@ public class GlBuffer extends GlObject {
return new MappedBuffer(handle(), size);
}
public void setGrowthMargin(int growthMargin) {
public void growthMargin(int growthMargin) {
this.growthMargin = growthMargin;
}
public long getSize() {
public long size() {
return size;
}
protected void deleteInternal(int handle) {
glDeleteBuffers(handle);
GlStateManager._glDeleteBuffers(handle);
FlwMemoryTracker._freeGPUMemory(size);
}
}

View file

@ -1,10 +1,8 @@
package com.jozufozu.flywheel.gl.buffer;
import static org.lwjgl.opengl.GL30.GL_MAP_WRITE_BIT;
import static org.lwjgl.opengl.GL30.nglMapBufferRange;
import static org.lwjgl.system.MemoryUtil.NULL;
import org.lwjgl.opengl.GL15;
import org.lwjgl.system.MemoryUtil;
import com.jozufozu.flywheel.gl.error.GlError;
@ -17,8 +15,7 @@ public class MappedBuffer implements AutoCloseable {
public MappedBuffer(int glBuffer, long size) {
this.glBuffer = glBuffer;
GlBufferType.COPY_READ_BUFFER.bind(glBuffer);
ptr = nglMapBufferRange(GlBufferType.COPY_READ_BUFFER.glEnum, 0, size, GL_MAP_WRITE_BIT);
ptr = GlBuffer.IMPL.mapRange(glBuffer, 0, size, GL_MAP_WRITE_BIT);
if (ptr == MemoryUtil.NULL) {
throw new GlException(GlError.poll(), "Could not map buffer");
@ -35,8 +32,7 @@ public class MappedBuffer implements AutoCloseable {
return;
}
GlBufferType.COPY_READ_BUFFER.bind(glBuffer);
GL15.glUnmapBuffer(GlBufferType.COPY_READ_BUFFER.glEnum);
GlBuffer.IMPL.unmap(glBuffer);
ptr = NULL;
}
}