Buffing the old works

- GlBuffer no longer has an explicit bind method or binding target
- Use READ/WRITE BUFFER for all data operations
- Make MappedBuffer simpler and more RAII
- Remove dead methods from GlBuffer
- Don't actually need to clear the tail of GPUInstancers on shrink
- Implement actual capability check for indirect
  (I think I check all the extensions)
This commit is contained in:
Jozufozu 2023-04-29 21:53:56 -07:00
parent 684d9c7913
commit 9d1c4b3e92
7 changed files with 79 additions and 158 deletions

View File

@ -9,7 +9,6 @@ import com.google.common.collect.ImmutableList;
import com.jozufozu.flywheel.api.event.ReloadRenderersEvent; import com.jozufozu.flywheel.api.event.ReloadRenderersEvent;
import com.jozufozu.flywheel.api.uniform.ShaderUniforms; import com.jozufozu.flywheel.api.uniform.ShaderUniforms;
import com.jozufozu.flywheel.gl.buffer.GlBuffer; import com.jozufozu.flywheel.gl.buffer.GlBuffer;
import com.jozufozu.flywheel.gl.buffer.GlBufferType;
import com.jozufozu.flywheel.gl.shader.GlProgram; import com.jozufozu.flywheel.gl.shader.GlProgram;
import com.jozufozu.flywheel.lib.math.MoreMath; import com.jozufozu.flywheel.lib.math.MoreMath;
import com.jozufozu.flywheel.lib.math.RenderMath; import com.jozufozu.flywheel.lib.math.RenderMath;
@ -35,7 +34,7 @@ public class UniformBuffer {
private final GlBuffer buffer; private final GlBuffer buffer;
private UniformBuffer() { private UniformBuffer() {
buffer = new GlBuffer(GlBufferType.UNIFORM_BUFFER); buffer = new GlBuffer();
providerSet = new ProviderSet(ShaderUniforms.REGISTRY.getAll()); providerSet = new ProviderSet(ShaderUniforms.REGISTRY.getAll());
} }

View File

@ -11,7 +11,6 @@ import com.jozufozu.flywheel.api.layout.BufferLayout;
import com.jozufozu.flywheel.backend.engine.AbstractInstancer; import com.jozufozu.flywheel.backend.engine.AbstractInstancer;
import com.jozufozu.flywheel.gl.array.GlVertexArray; import com.jozufozu.flywheel.gl.array.GlVertexArray;
import com.jozufozu.flywheel.gl.buffer.GlBuffer; import com.jozufozu.flywheel.gl.buffer.GlBuffer;
import com.jozufozu.flywheel.gl.buffer.GlBufferType;
import com.jozufozu.flywheel.gl.buffer.GlBufferUsage; import com.jozufozu.flywheel.gl.buffer.GlBufferUsage;
import com.jozufozu.flywheel.gl.buffer.MappedBuffer; import com.jozufozu.flywheel.gl.buffer.MappedBuffer;
@ -41,7 +40,7 @@ public class GPUInstancer<I extends Instance> extends AbstractInstancer<I> {
return; return;
} }
vbo = new GlBuffer(GlBufferType.ARRAY_BUFFER, GlBufferUsage.DYNAMIC_DRAW); vbo = new GlBuffer(GlBufferUsage.DYNAMIC_DRAW);
vbo.setGrowthMargin(instanceStride * 16); vbo.setGrowthMargin(instanceStride * 16);
} }
@ -65,16 +64,11 @@ public class GPUInstancer<I extends Instance> extends AbstractInstancer<I> {
return; return;
} }
int count = instances.size();
long clearStart = instanceStride * (long) count;
long clearLength = vbo.getSize() - clearStart;
try (MappedBuffer buf = vbo.map()) { try (MappedBuffer buf = vbo.map()) {
buf.clear(clearStart, clearLength); long ptr = buf.ptr();
long ptr = buf.getPtr();
InstanceWriter<I> writer = type.getWriter(); InstanceWriter<I> writer = type.getWriter();
int count = instances.size();
for (int i = changed.nextSetBit(0); i >= 0 && i < count; i = changed.nextSetBit(i + 1)) { 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 + instanceStride * i, instances.get(i));
} }

View File

@ -17,7 +17,6 @@ import com.jozufozu.flywheel.gl.GlPrimitive;
import com.jozufozu.flywheel.gl.array.GlVertexArray; import com.jozufozu.flywheel.gl.array.GlVertexArray;
import com.jozufozu.flywheel.gl.buffer.ElementBuffer; import com.jozufozu.flywheel.gl.buffer.ElementBuffer;
import com.jozufozu.flywheel.gl.buffer.GlBuffer; import com.jozufozu.flywheel.gl.buffer.GlBuffer;
import com.jozufozu.flywheel.gl.buffer.GlBufferType;
import com.jozufozu.flywheel.gl.buffer.MappedBuffer; import com.jozufozu.flywheel.gl.buffer.MappedBuffer;
public class InstancedMeshPool { public class InstancedMeshPool {
@ -39,7 +38,7 @@ public class InstancedMeshPool {
public InstancedMeshPool(VertexType vertexType) { public InstancedMeshPool(VertexType vertexType) {
this.vertexType = vertexType; this.vertexType = vertexType;
int stride = vertexType.getLayout().getStride(); int stride = vertexType.getLayout().getStride();
vbo = new GlBuffer(GlBufferType.ARRAY_BUFFER); vbo = new GlBuffer();
vbo.setGrowthMargin(stride * 32); vbo.setGrowthMargin(stride * 32);
} }
@ -75,21 +74,21 @@ public class InstancedMeshPool {
} }
public void flush() { public void flush() {
if (dirty) { if (!dirty) {
return;
}
if (anyToRemove) { if (anyToRemove) {
processDeletions(); processDeletions();
} }
if (realloc()) { vbo.ensureCapacity(byteSize);
uploadAll();
} else {
uploadPending(); uploadPending();
}
dirty = false; dirty = false;
pendingUpload.clear(); pendingUpload.clear();
} }
}
private void processDeletions() { private void processDeletions() {
// remove deleted meshes // remove deleted meshes
@ -117,35 +116,9 @@ public class InstancedMeshPool {
this.anyToRemove = false; this.anyToRemove = false;
} }
/**
* Assumes vbo is bound.
*
* @return true if the buffer was reallocated
*/
private boolean realloc() {
return vbo.ensureCapacity(byteSize);
}
private void uploadAll() {
try (MappedBuffer mapped = vbo.map()) {
long ptr = mapped.getPtr();
int byteIndex = 0;
for (BufferedMesh mesh : allBuffered) {
mesh.byteIndex = byteIndex;
mesh.buffer(ptr);
byteIndex += mesh.size();
}
} catch (Exception e) {
Flywheel.LOGGER.error("Error uploading pooled meshes:", e);
}
}
private void uploadPending() { private void uploadPending() {
try (MappedBuffer mapped = vbo.map()) { try (MappedBuffer mapped = vbo.map()) {
long ptr = mapped.getPtr(); long ptr = mapped.ptr();
for (BufferedMesh mesh : pendingUpload) { for (BufferedMesh mesh : pendingUpload) {
mesh.buffer(ptr); mesh.buffer(ptr);

View File

@ -4,21 +4,13 @@ import static org.lwjgl.opengl.GL15.glBufferData;
import static org.lwjgl.opengl.GL15.glDeleteBuffers; import static org.lwjgl.opengl.GL15.glDeleteBuffers;
import static org.lwjgl.opengl.GL15.glGenBuffers; import static org.lwjgl.opengl.GL15.glGenBuffers;
import static org.lwjgl.opengl.GL15.nglBufferData; import static org.lwjgl.opengl.GL15.nglBufferData;
import static org.lwjgl.opengl.GL30.GL_MAP_WRITE_BIT;
import static org.lwjgl.opengl.GL30.nglMapBufferRange;
import static org.lwjgl.opengl.GL31.glCopyBufferSubData; import static org.lwjgl.opengl.GL31.glCopyBufferSubData;
import org.lwjgl.system.MemoryUtil;
import com.jozufozu.flywheel.gl.GlObject; import com.jozufozu.flywheel.gl.GlObject;
import com.jozufozu.flywheel.gl.error.GlError;
import com.jozufozu.flywheel.gl.error.GlException;
import com.jozufozu.flywheel.lib.memory.FlwMemoryTracker; import com.jozufozu.flywheel.lib.memory.FlwMemoryTracker;
import com.jozufozu.flywheel.lib.memory.MemoryBlock; import com.jozufozu.flywheel.lib.memory.MemoryBlock;
public class GlBuffer extends GlObject { public class GlBuffer extends GlObject {
public final GlBufferType type;
protected final GlBufferUsage usage; protected final GlBufferUsage usage;
/** /**
* The size (in bytes) of the buffer on the GPU. * The size (in bytes) of the buffer on the GPU.
@ -29,16 +21,18 @@ public class GlBuffer extends GlObject {
*/ */
protected int growthMargin; protected int growthMargin;
public GlBuffer(GlBufferType type) { public GlBuffer() {
this(type, GlBufferUsage.STATIC_DRAW); this(GlBufferUsage.STATIC_DRAW);
} }
public GlBuffer(GlBufferType type, GlBufferUsage usage) { public GlBuffer(GlBufferUsage usage) {
setHandle(glGenBuffers()); setHandle(glGenBuffers());
this.type = type;
this.usage = usage; this.usage = usage;
} }
/**
* @return true if the buffer was recreated.
*/
public boolean ensureCapacity(long size) { public boolean ensureCapacity(long size) {
if (size < 0) { if (size < 0) {
throw new IllegalArgumentException("Size " + size + " < 0"); throw new IllegalArgumentException("Size " + size + " < 0");
@ -50,8 +44,8 @@ public class GlBuffer extends GlObject {
if (this.size == 0) { if (this.size == 0) {
this.size = size; this.size = size;
bind(); GlBufferType.COPY_WRITE_BUFFER.bind(handle());
glBufferData(type.glEnum, size, usage.glEnum); glBufferData(GlBufferType.COPY_WRITE_BUFFER.glEnum, size, usage.glEnum);
FlwMemoryTracker._allocGPUMemory(size); FlwMemoryTracker._allocGPUMemory(size);
return true; return true;
@ -76,36 +70,25 @@ public class GlBuffer extends GlObject {
var newHandle = glGenBuffers(); var newHandle = glGenBuffers();
GlBufferType.COPY_READ_BUFFER.bind(oldHandle); GlBufferType.COPY_READ_BUFFER.bind(oldHandle);
type.bind(newHandle); GlBufferType.COPY_WRITE_BUFFER.bind(newHandle);
glBufferData(type.glEnum, newSize, usage.glEnum); glBufferData(GlBufferType.COPY_WRITE_BUFFER.glEnum, newSize, usage.glEnum);
glCopyBufferSubData(GlBufferType.COPY_READ_BUFFER.glEnum, type.glEnum, 0, 0, oldSize); glCopyBufferSubData(GlBufferType.COPY_READ_BUFFER.glEnum, GlBufferType.COPY_WRITE_BUFFER.glEnum, 0, 0, oldSize);
glDeleteBuffers(oldHandle); glDeleteBuffers(oldHandle);
setHandle(newHandle); setHandle(newHandle);
} }
public void upload(MemoryBlock directBuffer) { public void upload(MemoryBlock directBuffer) {
bind();
FlwMemoryTracker._freeGPUMemory(size); FlwMemoryTracker._freeGPUMemory(size);
nglBufferData(type.glEnum, directBuffer.size(), directBuffer.ptr(), usage.glEnum); GlBufferType.COPY_WRITE_BUFFER.bind(handle());
nglBufferData(GlBufferType.COPY_WRITE_BUFFER.glEnum, directBuffer.size(), directBuffer.ptr(), usage.glEnum);
this.size = directBuffer.size(); this.size = directBuffer.size();
FlwMemoryTracker._allocGPUMemory(size); FlwMemoryTracker._allocGPUMemory(size);
} }
public MappedBuffer map() { public MappedBuffer map() {
bind(); return new MappedBuffer(handle(), size);
long ptr = nglMapBufferRange(type.glEnum, 0, size, GL_MAP_WRITE_BIT);
if (ptr == MemoryUtil.NULL) {
throw new GlException(GlError.poll(), "Could not map buffer");
}
return new MappedBuffer(this, ptr, 0, size);
}
public boolean isPersistent() {
return false;
} }
public void setGrowthMargin(int growthMargin) { public void setGrowthMargin(int growthMargin) {
@ -116,18 +99,6 @@ public class GlBuffer extends GlObject {
return size; return size;
} }
public GlBufferType getType() {
return type;
}
public void bind() {
type.bind(handle());
}
public void unbind() {
type.unbind();
}
protected void deleteInternal(int handle) { protected void deleteInternal(int handle) {
glDeleteBuffers(handle); glDeleteBuffers(handle);
FlwMemoryTracker._freeGPUMemory(size); FlwMemoryTracker._freeGPUMemory(size);

View File

@ -1,59 +1,42 @@
package com.jozufozu.flywheel.gl.buffer; 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 static org.lwjgl.system.MemoryUtil.NULL;
import org.lwjgl.opengl.GL15; import org.lwjgl.opengl.GL15;
import org.lwjgl.system.MemoryUtil; import org.lwjgl.system.MemoryUtil;
public class MappedBuffer implements AutoCloseable { import com.jozufozu.flywheel.gl.error.GlError;
import com.jozufozu.flywheel.gl.error.GlException;
private final long offset; public class MappedBuffer implements AutoCloseable {
private final long length; private final int glBuffer;
private final GlBuffer owner;
private final boolean persistent;
private long ptr; private long ptr;
public MappedBuffer(GlBuffer owner, long ptr, long offset, long length) { public MappedBuffer(int glBuffer, long size) {
this.ptr = ptr; this.glBuffer = glBuffer;
this.owner = owner;
this.offset = offset; GlBufferType.COPY_READ_BUFFER.bind(glBuffer);
this.length = length; ptr = nglMapBufferRange(GlBufferType.COPY_READ_BUFFER.glEnum, 0, size, GL_MAP_WRITE_BIT);
persistent = owner.isPersistent();
if (ptr == MemoryUtil.NULL) {
throw new GlException(GlError.poll(), "Could not map buffer");
}
} }
/** public long ptr() {
* Make the changes in client memory available to the GPU. return ptr;
*/
public void flush() {
if (persistent) return;
if (ptr == NULL) return;
owner.bind();
GL15.glUnmapBuffer(owner.getType().glEnum);
ptr = NULL;
} }
@Override @Override
public void close() { public void close() {
flush(); if (ptr == NULL) {
}
public long getPtr() {
return ptr;
}
public void clear(long clearStart, long clearLength) {
if (clearLength <= 0) {
return; return;
} }
if (clearStart < offset || clearStart + clearLength > offset + length) { GlBufferType.COPY_READ_BUFFER.bind(glBuffer);
throw new IndexOutOfBoundsException("Clear range [" + clearStart + "," + (clearStart + clearLength) + "] is not mapped"); GL15.glUnmapBuffer(GlBufferType.COPY_READ_BUFFER.glEnum);
} ptr = NULL;
long addr = ptr + clearStart;
MemoryUtil.memSet(addr, 0, clearLength);
} }
} }

View File

@ -1,7 +1,7 @@
package com.jozufozu.flywheel.gl.versioned; package com.jozufozu.flywheel.gl.versioned;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.Arrays; import java.util.Optional;
import org.lwjgl.PointerBuffer; import org.lwjgl.PointerBuffer;
import org.lwjgl.opengl.GL; import org.lwjgl.opengl.GL;
@ -18,17 +18,18 @@ import net.minecraft.Util;
* system. * system.
*/ */
public class GlCompat { public class GlCompat {
private static final GLCapabilities caps;
public static final VertexArray vertexArray; public static final VertexArray vertexArray;
public static final BufferStorage bufferStorage; public static final BufferStorage bufferStorage;
public static final boolean amd; public static final boolean amd;
public static final boolean supportsIndirect; public static final boolean supportsIndirect;
static { static {
GLCapabilities caps = GL.createCapabilities(); caps = GL.createCapabilities();
bufferStorage = getLatest(BufferStorage.class, caps); bufferStorage = getLatest(BufferStorage.class);
vertexArray = getLatest(VertexArray.class, caps); vertexArray = getLatest(VertexArray.class);
supportsIndirect = caps.OpenGL46; supportsIndirect = _decideIfWeSupportIndirect();
amd = _isAmdWindows(); amd = _decideIfWeAreAMDWindows();
} }
private GlCompat() { private GlCompat() {
@ -46,30 +47,31 @@ public class GlCompat {
return supportsIndirect; return supportsIndirect;
} }
public static boolean bufferStorageSupported() { private static boolean _decideIfWeSupportIndirect() {
return bufferStorage != BufferStorage.UNSUPPORTED; 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);
} }
/** /**
* Get the most compatible version of a specific OpenGL feature by iterating over enum constants in order. * Get the most compatible version of a specific OpenGL feature by iterating over enum constants in order.
* *
* @param clazz The class of the versioning enum.
* @param caps The current system's supported features.
* @param <V> The type of the versioning enum. * @param <V> The type of the versioning enum.
* @param clazz The class of the versioning enum.
* @return The first defined enum variant to return true. * @return The first defined enum variant to return true.
*/ */
private 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) {
V[] constants = clazz.getEnumConstants(); for (V it : clazz.getEnumConstants()) {
V last = constants[constants.length - 1]; if (it.supported(GlCompat.caps)) {
if (!last.supported(caps)) { return Optional.of(it)
throw new IllegalStateException("");
}
return Arrays.stream(constants)
.filter(it -> it.supported(caps))
.findFirst()
.get(); .get();
} }
}
throw new IllegalStateException("Invalid versioned enum, must provide at least one supported constant");
}
/** /**
* Modified from: * Modified from:
@ -92,7 +94,7 @@ public class GlCompat {
} }
} }
private static boolean _isAmdWindows() { private static boolean _decideIfWeAreAMDWindows() {
if (Util.getPlatform() != Util.OS.WINDOWS) { if (Util.getPlatform() != Util.OS.WINDOWS) {
return false; return false;
} }

View File

@ -10,7 +10,6 @@ import com.jozufozu.flywheel.api.layout.BufferLayout;
import com.jozufozu.flywheel.gl.GlStateTracker; import com.jozufozu.flywheel.gl.GlStateTracker;
import com.jozufozu.flywheel.gl.array.GlVertexArray; import com.jozufozu.flywheel.gl.array.GlVertexArray;
import com.jozufozu.flywheel.gl.buffer.GlBuffer; import com.jozufozu.flywheel.gl.buffer.GlBuffer;
import com.jozufozu.flywheel.gl.buffer.GlBufferType;
import com.jozufozu.flywheel.gl.buffer.MappedBuffer; import com.jozufozu.flywheel.gl.buffer.MappedBuffer;
import com.jozufozu.flywheel.lib.layout.CommonItems; import com.jozufozu.flywheel.lib.layout.CommonItems;
import com.jozufozu.flywheel.util.Lazy; import com.jozufozu.flywheel.util.Lazy;
@ -35,10 +34,10 @@ public class FullscreenQuad {
private FullscreenQuad() { private FullscreenQuad() {
try (var restoreState = GlStateTracker.getRestoreState()) { try (var restoreState = GlStateTracker.getRestoreState()) {
vbo = new GlBuffer(GlBufferType.ARRAY_BUFFER); vbo = new GlBuffer();
vbo.ensureCapacity(bufferSize); vbo.ensureCapacity(bufferSize);
try (MappedBuffer buffer = vbo.map()) { try (MappedBuffer buffer = vbo.map()) {
var ptr = buffer.getPtr(); var ptr = buffer.ptr();
for (var i = 0; i < vertices.length; i++) { for (var i = 0; i < vertices.length; i++) {
MemoryUtil.memPutFloat(ptr + i * Float.BYTES, vertices[i]); MemoryUtil.memPutFloat(ptr + i * Float.BYTES, vertices[i]);