InDSAanced vertex arrays

- Make GlCompat static
- Expand InstancedArrays compat to encompass all vertex array ops
- GlVertexArray calls into VertexArray compat layer
- VertexAttributes still use 2 separate interface methods
This commit is contained in:
Jozufozu 2023-04-24 21:54:59 -07:00
parent aef676517a
commit ef7c259f43
13 changed files with 221 additions and 188 deletions

View file

@ -32,8 +32,7 @@ public class Backends {
.engineMessage(new TextComponent("Using Instancing Engine").withStyle(ChatFormatting.GREEN))
.engineFactory(level -> new InstancingEngine(256, Contexts.WORLD))
.fallback(() -> Backends.BATCHING)
.supported(() -> !ShadersModHandler.isShaderPackInUse() && GlCompat.getInstance()
.instancedArraysSupported() && InstancingPrograms.allLoaded())
.supported(() -> !ShadersModHandler.isShaderPackInUse() && GlCompat.supportsInstancing() && InstancingPrograms.allLoaded())
.register(Flywheel.rl("instancing"));
/**
@ -43,8 +42,7 @@ public class Backends {
.engineMessage(new TextComponent("Using Indirect Engine").withStyle(ChatFormatting.GREEN))
.engineFactory(level -> new IndirectEngine(256))
.fallback(() -> Backends.INSTANCING)
.supported(() -> !ShadersModHandler.isShaderPackInUse() && GlCompat.getInstance()
.supportsIndirect() && IndirectPrograms.allLoaded())
.supported(() -> !ShadersModHandler.isShaderPackInUse() && GlCompat.supportsIndirect() && IndirectPrograms.allLoaded())
.register(Flywheel.rl("indirect"));
public static void init() {

View file

@ -1,15 +1,10 @@
package com.jozufozu.flywheel.backend.engine.indirect;
import static org.lwjgl.opengl.GL30.glBindVertexArray;
import static org.lwjgl.opengl.GL30.glDeleteVertexArrays;
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;
import static org.lwjgl.opengl.GL43.glDispatchCompute;
import static org.lwjgl.opengl.GL45.glCreateVertexArrays;
import static org.lwjgl.opengl.GL45.glEnableVertexArrayAttrib;
import static org.lwjgl.opengl.GL45.glVertexArrayElementBuffer;
import static org.lwjgl.opengl.GL45.glVertexArrayVertexBuffer;
import com.jozufozu.flywheel.api.event.RenderStage;
import com.jozufozu.flywheel.api.instance.Instance;
@ -17,6 +12,7 @@ import com.jozufozu.flywheel.api.instance.InstanceType;
import com.jozufozu.flywheel.api.vertex.VertexType;
import com.jozufozu.flywheel.backend.compile.IndirectPrograms;
import com.jozufozu.flywheel.backend.engine.UniformBuffer;
import com.jozufozu.flywheel.gl.array.GlVertexArray;
import com.jozufozu.flywheel.gl.shader.GlProgram;
import com.jozufozu.flywheel.lib.context.Contexts;
import com.jozufozu.flywheel.lib.util.QuadConverter;
@ -35,7 +31,7 @@ public class IndirectCullingGroup<I extends Instance> {
final IndirectMeshPool meshPool;
private final int elementBuffer;
int vertexArray;
GlVertexArray vertexArray;
final IndirectDrawSet<I> drawSet = new IndirectDrawSet<>();
@ -55,7 +51,7 @@ public class IndirectCullingGroup<I extends Instance> {
meshPool = new IndirectMeshPool(vertexType, 1024);
vertexArray = glCreateVertexArrays();
vertexArray = new GlVertexArray();
elementBuffer = QuadConverter.getInstance()
.quads2Tris(2048).glBuffer;
@ -67,21 +63,8 @@ public class IndirectCullingGroup<I extends Instance> {
}
private void setupVertexArray() {
glVertexArrayElementBuffer(vertexArray, elementBuffer);
var meshLayout = vertexType.getLayout();
var meshAttribs = meshLayout.getAttributeCount();
var attributes = meshLayout.attributes();
long offset = 0;
for (int i = 0; i < meshAttribs; i++) {
var attribute = attributes.get(i);
glEnableVertexArrayAttrib(vertexArray, i);
glVertexArrayVertexBuffer(vertexArray, i, meshPool.vbo, offset, meshLayout.getStride());
attribute.format(vertexArray, i);
offset += attribute.getByteWidth();
}
vertexArray.setElementBuffer(elementBuffer);
vertexArray.bindAttributes(vertexType.getLayout(), meshPool.vbo, 0, 0);
}
void beginFrame() {
@ -127,7 +110,7 @@ public class IndirectCullingGroup<I extends Instance> {
}
UniformBuffer.syncAndBind(draw);
glBindVertexArray(vertexArray);
vertexArray.bindForDraw();
buffers.bindForDraw();
memoryBarrier();
@ -180,7 +163,7 @@ public class IndirectCullingGroup<I extends Instance> {
}
public void delete() {
glDeleteVertexArrays(vertexArray);
vertexArray.delete();
buffers.delete();
meshPool.delete();
}

View file

@ -16,7 +16,6 @@ public class DrawCall {
meshAttributes = this.mesh.getAttributeCount();
vao = new GlVertexArray();
vao.enableArrays(meshAttributes + this.instancer.getAttributeCount());
}
public boolean isInvalid() {

View file

@ -90,7 +90,7 @@ public class GPUInstancer<I extends Instance> extends AbstractInstancer<I> {
return;
}
vao.bindAttributes(vbo, attributeOffset, instanceFormat, 0L);
vao.bindAttributes(instanceFormat, vbo.handle(), attributeOffset, 0L);
for (int i = 0; i < instanceFormat.getAttributeCount(); i++) {
vao.setAttributeDivisor(attributeOffset + i, 1);

View file

@ -221,11 +221,10 @@ public class InstancedMeshPool {
private void setup(GlVertexArray vao) {
if (boundTo.add(vao)) {
vao.enableArrays(getAttributeCount());
vao.bindAttributes(InstancedMeshPool.this.vbo, 0, vertexType.getLayout(), byteIndex);
vao.bindAttributes(vertexType.getLayout(), InstancedMeshPool.this.vbo.handle(), 0, byteIndex);
vao.setElementBuffer(ebo.glBuffer);
}
vao.bindElementArray(ebo.glBuffer);
vao.bind();
vao.bindForDraw();
}
private void draw(int instanceCount) {

View file

@ -1,13 +1,12 @@
package com.jozufozu.flywheel.gl.array;
import org.lwjgl.opengl.GL20;
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.buffer.GlBuffer;
import com.jozufozu.flywheel.gl.buffer.GlBufferType;
import com.jozufozu.flywheel.gl.versioned.GlCompat;
import com.mojang.blaze3d.platform.GlStateManager;
@ -24,22 +23,18 @@ public class GlVertexArray extends GlObject {
* Each attribute's divisor.
*/
private final int[] divisors = new int[MAX_ATTRIBS];
/**
* The VBO to which each attribute is bound.
*/
private final int[] targets = 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.
*/
@ -49,16 +44,16 @@ public class GlVertexArray extends GlObject {
public GlVertexArray() {
setHandle(GlStateManager._glGenVertexArrays());
setHandle(GlCompat.vertexArray.create());
}
public void bind() {
public void bindForDraw() {
if (!isBound()) {
GlStateManager._glBindVertexArray(handle());
}
}
public boolean isBound() {
private boolean isBound() {
return handle() == GlStateTracker.getVertexArray();
}
@ -66,62 +61,21 @@ public class GlVertexArray extends GlObject {
GlStateManager._glBindVertexArray(0);
}
/**
* @param buffer The buffer where the data is stored.
* @param startAttrib The first attribute to be used by the data.
* @param type The format of the attributes.
* @param offset The offset in bytes to the start of the data.
*/
public void bindAttributes(GlBuffer buffer, int startAttrib, BufferLayout type, long offset) {
bind();
int targetBuffer = buffer.handle();
GlBufferType.ARRAY_BUFFER.bind(targetBuffer);
int i = startAttrib;
public void bindAttributes(BufferLayout type, int vbo, int startAttrib, long startOffset) {
final int stride = type.getStride();
for (var attribute : type.attributes()) {
targets[i] = targetBuffer;
attributes[i] = attribute;
offsets[i] = offset;
strides[i] = stride;
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;
attribute.pointer(offset, i, stride);
GlCompat.vertexArray.setupAttrib(handle(), stride, vbo, startOffset, attribute, index);
i++;
offset += attribute.getByteWidth();
}
}
public void enableArrays(int count) {
bind();
for (int i = 0; i < count; i++) {
enable(i);
}
}
public void disableArrays(int count) {
bind();
for (int i = 0; i < count; i++) {
disable(i);
}
}
private void enable(int i) {
if (!enabled[i]) {
GL20.glEnableVertexAttribArray(i);
enabled[i] = true;
}
}
private void disable(int i) {
if (enabled[i]) {
GL20.glDisableVertexAttribArray(i);
enabled[i] = false;
startOffset += attribute.getByteWidth();
}
}
@ -131,16 +85,14 @@ public class GlVertexArray extends GlObject {
public void setAttributeDivisor(int index, int divisor) {
if (divisors[index] != divisor) {
bind();
GlCompat.getInstance().instancedArrays.vertexAttribDivisor(index, divisor);
GlCompat.vertexArray.setAttribDivisor(handle(), index, divisor);
divisors[index] = divisor;
}
}
public void bindElementArray(int ebo) {
public void setElementBuffer(int ebo) {
if (elementBufferBinding != ebo) {
bind();
GlBufferType.ELEMENT_ARRAY_BUFFER.bind(ebo);
GlCompat.vertexArray.setElementBuffer(handle(), ebo);
elementBufferBinding = ebo;
}
}

View file

@ -5,16 +5,18 @@ public interface VertexAttribute {
/**
* Apply this vertex attribute to the bound vertex array.
*
* @param offset The byte offset to the first element of the attribute.
* @param i The attribute index.
* @param i The attribute index.
* @param stride The byte stride between consecutive elements of the attribute.
*/
void pointer(long offset, int i, int stride);
void setup(long offset, int i, int stride);
/**
* Use DSA to apply this vertex attribute to the given vertex array.
*
* @param vaobj The vertex array object to modify.
* @param i The attribute index.
* @param i The attribute index.
*/
void format(int vaobj, int i);
void setupDSA(int vaobj, int i);
}

View file

@ -20,12 +20,12 @@ public record VertexAttributeF(GlNumericType type, int size, boolean normalized)
}
@Override
public void pointer(long offset, int i, int stride) {
public void setup(long offset, int i, int stride) {
GL32.glVertexAttribPointer(i, size(), type().getGlEnum(), normalized(), stride, offset);
}
@Override
public void format(int vaobj, int i) {
public void setupDSA(int vaobj, int i) {
GL45.glVertexArrayAttribFormat(vaobj, i, size(), type().getGlEnum(), normalized(), 0);
}
}

View file

@ -19,12 +19,12 @@ public record VertexAttributeI(GlNumericType type, int size) implements VertexAt
}
@Override
public void pointer(long offset, int i, int stride) {
public void setup(long offset, int i, int stride) {
GL32.glVertexAttribIPointer(i, size(), type().getGlEnum(), stride, offset);
}
@Override
public void format(int vaobj, int i) {
public void setupDSA(int vaobj, int i) {
GL45.glVertexArrayAttribIFormat(vaobj, i, size(), type().getGlEnum(), 0);
}
}

View file

@ -18,40 +18,35 @@ import net.minecraft.Util;
* system.
*/
public class GlCompat {
public static final VertexArray vertexArray;
public static final BufferStorage bufferStorage;
public static final boolean amd;
public static final boolean supportsIndirect;
private static GlCompat instance;
public static GlCompat getInstance() {
if (instance == null) {
instance = new GlCompat();
}
return instance;
}
public final InstancedArrays instancedArrays;
public final BufferStorage bufferStorage;
public final boolean amd;
public final boolean supportsIndirect;
private GlCompat() {
static {
GLCapabilities caps = GL.createCapabilities();
instancedArrays = getLatest(InstancedArrays.class, caps);
bufferStorage = getLatest(BufferStorage.class, caps);
supportsIndirect = true;
vertexArray = getLatest(VertexArray.class, caps);
supportsIndirect = caps.OpenGL46;
amd = _isAmdWindows();
}
public boolean onAMDWindows() {
private GlCompat() {
}
public static boolean onAMDWindows() {
return amd;
}
public boolean instancedArraysSupported() {
return instancedArrays != InstancedArrays.UNSUPPORTED;
public static boolean supportsInstancing() {
return vertexArray != VertexArray.UNSUPPORTED;
}
public boolean bufferStorageSupported() {
public static boolean supportsIndirect() {
return supportsIndirect;
}
public static boolean bufferStorageSupported() {
return bufferStorage != BufferStorage.UNSUPPORTED;
}
@ -63,7 +58,7 @@ public class GlCompat {
* @param <V> The type of the versioning enum.
* @return The first defined enum variant to return true.
*/
public static <V extends Enum<V> & GlVersioned> V getLatest(Class<V> clazz, GLCapabilities caps) {
private static <V extends Enum<V> & GlVersioned> V getLatest(Class<V> clazz, GLCapabilities caps) {
V[] constants = clazz.getEnumConstants();
V last = constants[constants.length - 1];
if (!last.supported(caps)) {
@ -111,9 +106,5 @@ public class GlCompat {
// vendor string I got was "ATI Technologies Inc."
return vendor.contains("ATI") || vendor.contains("AMD");
}
public boolean supportsIndirect() {
return supportsIndirect;
}
}

View file

@ -1,43 +0,0 @@
package com.jozufozu.flywheel.gl.versioned;
import org.lwjgl.opengl.ARBInstancedArrays;
import org.lwjgl.opengl.GL33;
import org.lwjgl.opengl.GLCapabilities;
public enum InstancedArrays implements GlVersioned {
GL33_INSTANCED_ARRAYS {
@Override
public boolean supported(GLCapabilities caps) {
return caps.OpenGL33;
}
@Override
public void vertexAttribDivisor(int index, int divisor) {
GL33.glVertexAttribDivisor(index, divisor);
}
},
ARB_INSTANCED_ARRAYS {
@Override
public boolean supported(GLCapabilities caps) {
return caps.GL_ARB_instanced_arrays;
}
@Override
public void vertexAttribDivisor(int index, int divisor) {
ARBInstancedArrays.glVertexAttribDivisorARB(index, divisor);
}
},
UNSUPPORTED {
@Override
public boolean supported(GLCapabilities caps) {
return true;
}
@Override
public void vertexAttribDivisor(int index, int divisor) {
throw new UnsupportedOperationException();
}
};
public abstract void vertexAttribDivisor(int index, int divisor);
}

View file

@ -0,0 +1,154 @@
package com.jozufozu.flywheel.gl.versioned;
import org.lwjgl.opengl.ARBInstancedArrays;
import org.lwjgl.opengl.GL30;
import org.lwjgl.opengl.GL33;
import org.lwjgl.opengl.GL45C;
import org.lwjgl.opengl.GLCapabilities;
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 enum VertexArray implements GlVersioned {
DSA {
@Override
public boolean supported(GLCapabilities caps) {
// The static methods from GL45 and ARBDirectStateAccess all point to GL45C.
return caps.OpenGL45 || caps.GL_ARB_direct_state_access;
}
@Override
public int create() {
return GL45C.glCreateVertexArrays();
}
@Override
public void setElementBuffer(int vao, int elementBuffer) {
GL45C.glVertexArrayElementBuffer(vao, elementBuffer);
}
@Override
public void setupAttrib(int vao, int stride, int vbo, long offset, VertexAttribute attribute, int index) {
GL45C.glEnableVertexArrayAttrib(vao, index);
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);
}
},
GL_33 {
@Override
public boolean supported(GLCapabilities caps) {
return caps.OpenGL33;
}
@Override
public int create() {
return GL33.glGenVertexArrays();
}
@Override
public void setElementBuffer(int vao, int elementBuffer) {
if (vao != GlStateTracker.getVertexArray()) {
GlStateManager._glBindVertexArray(vao);
}
GlBufferType.ELEMENT_ARRAY_BUFFER.bind(elementBuffer);
}
@Override
public void setupAttrib(int vao, int stride, int vbo, long offset, VertexAttribute attribute, int index) {
if (vao != GlStateTracker.getVertexArray()) {
GlStateManager._glBindVertexArray(vao);
}
GlBufferType.ARRAY_BUFFER.bind(vbo);
GL33.glEnableVertexAttribArray(index);
attribute.setup(offset, index, stride);
}
@Override
public void setAttribDivisor(int vao, int attrib, int divisor) {
if (vao != GlStateTracker.getVertexArray()) {
GlStateManager._glBindVertexArray(vao);
}
GL33.glVertexAttribDivisor(attrib, divisor);
}
},
ARB_INSTANCED_ARRAYS {
@Override
public boolean supported(GLCapabilities caps) {
return caps.GL_ARB_instanced_arrays;
}
@Override
public int create() {
return GL30.glGenVertexArrays();
}
@Override
public void setElementBuffer(int vao, int elementBuffer) {
if (vao != GlStateTracker.getVertexArray()) {
GlStateManager._glBindVertexArray(vao);
}
GlBufferType.ELEMENT_ARRAY_BUFFER.bind(elementBuffer);
}
@Override
public void setupAttrib(int vao, int stride, int vbo, long offset, VertexAttribute attribute, int index) {
if (vao != GlStateTracker.getVertexArray()) {
GlStateManager._glBindVertexArray(vao);
}
GlBufferType.ARRAY_BUFFER.bind(vbo);
GL30.glEnableVertexAttribArray(index);
attribute.setup(offset, index, stride);
}
@Override
public void setAttribDivisor(int vao, int attrib, int divisor) {
if (vao != GlStateTracker.getVertexArray()) {
GlStateManager._glBindVertexArray(vao);
}
ARBInstancedArrays.glVertexAttribDivisorARB(attrib, divisor);
}
},
UNSUPPORTED {
@Override
public boolean supported(GLCapabilities caps) {
return true;
}
@Override
public int create() {
throw new UnsupportedOperationException("Cannot use vertex arrays");
}
@Override
public void setElementBuffer(int vao, int elementBuffer) {
throw new UnsupportedOperationException("Cannot use vertex arrays");
}
@Override
public void setupAttrib(int vao, int stride, int vbo, long offset, VertexAttribute attribute, int index) {
throw new UnsupportedOperationException("Cannot use vertex arrays");
}
@Override
public void setAttribDivisor(int vao, int attrib, int divisor) {
throw new UnsupportedOperationException("Cannot use vertex arrays");
}
};
public abstract int create();
public abstract void setElementBuffer(int vao, int elementBuffer);
public abstract void setupAttrib(int vao, int stride, int vbo, long offset, VertexAttribute attribute, int index);
public abstract void setAttribDivisor(int vao, int attrib, int divisor);
}

View file

@ -50,14 +50,12 @@ public class FullscreenQuad {
vao = new GlVertexArray();
vao.enableArrays(1);
vao.bindAttributes(vbo, 0, LAYOUT, 0L);
vao.bindAttributes(LAYOUT, vbo.handle(), 0, 0L);
}
}
public void draw() {
vao.bind();
vao.bindForDraw();
glDrawArrays(GL_TRIANGLES, 0, 6);
GlVertexArray.unbind();
}