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 ef7c259f43
commit f1f289124d
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.uniform.ShaderUniforms;
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.lib.math.MoreMath;
import com.jozufozu.flywheel.lib.math.RenderMath;
@ -35,7 +34,7 @@ public class UniformBuffer {
private final GlBuffer buffer;
private UniformBuffer() {
buffer = new GlBuffer(GlBufferType.UNIFORM_BUFFER);
buffer = new GlBuffer();
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.gl.array.GlVertexArray;
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.MappedBuffer;
@ -41,7 +40,7 @@ public class GPUInstancer<I extends Instance> extends AbstractInstancer<I> {
return;
}
vbo = new GlBuffer(GlBufferType.ARRAY_BUFFER, GlBufferUsage.DYNAMIC_DRAW);
vbo = new GlBuffer(GlBufferUsage.DYNAMIC_DRAW);
vbo.setGrowthMargin(instanceStride * 16);
}
@ -65,16 +64,11 @@ public class GPUInstancer<I extends Instance> extends AbstractInstancer<I> {
return;
}
int count = instances.size();
long clearStart = instanceStride * (long) count;
long clearLength = vbo.getSize() - clearStart;
try (MappedBuffer buf = vbo.map()) {
buf.clear(clearStart, clearLength);
long ptr = buf.getPtr();
long ptr = buf.ptr();
InstanceWriter<I> writer = type.getWriter();
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));
}

View File

@ -17,7 +17,6 @@ import com.jozufozu.flywheel.gl.GlPrimitive;
import com.jozufozu.flywheel.gl.array.GlVertexArray;
import com.jozufozu.flywheel.gl.buffer.ElementBuffer;
import com.jozufozu.flywheel.gl.buffer.GlBuffer;
import com.jozufozu.flywheel.gl.buffer.GlBufferType;
import com.jozufozu.flywheel.gl.buffer.MappedBuffer;
public class InstancedMeshPool {
@ -39,7 +38,7 @@ public class InstancedMeshPool {
public InstancedMeshPool(VertexType vertexType) {
this.vertexType = vertexType;
int stride = vertexType.getLayout().getStride();
vbo = new GlBuffer(GlBufferType.ARRAY_BUFFER);
vbo = new GlBuffer();
vbo.setGrowthMargin(stride * 32);
}
@ -75,20 +74,20 @@ public class InstancedMeshPool {
}
public void flush() {
if (dirty) {
if (anyToRemove) {
processDeletions();
}
if (realloc()) {
uploadAll();
} else {
uploadPending();
}
dirty = false;
pendingUpload.clear();
if (!dirty) {
return;
}
if (anyToRemove) {
processDeletions();
}
vbo.ensureCapacity(byteSize);
uploadPending();
dirty = false;
pendingUpload.clear();
}
private void processDeletions() {
@ -117,35 +116,9 @@ public class InstancedMeshPool {
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() {
try (MappedBuffer mapped = vbo.map()) {
long ptr = mapped.getPtr();
long ptr = mapped.ptr();
for (BufferedMesh mesh : pendingUpload) {
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.glGenBuffers;
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 org.lwjgl.system.MemoryUtil;
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.MemoryBlock;
public class GlBuffer extends GlObject {
public final GlBufferType type;
protected final GlBufferUsage usage;
/**
* The size (in bytes) of the buffer on the GPU.
@ -29,16 +21,18 @@ public class GlBuffer extends GlObject {
*/
protected int growthMargin;
public GlBuffer(GlBufferType type) {
this(type, GlBufferUsage.STATIC_DRAW);
public GlBuffer() {
this(GlBufferUsage.STATIC_DRAW);
}
public GlBuffer(GlBufferType type, GlBufferUsage usage) {
public GlBuffer(GlBufferUsage usage) {
setHandle(glGenBuffers());
this.type = type;
this.usage = usage;
}
/**
* @return true if the buffer was recreated.
*/
public boolean ensureCapacity(long size) {
if (size < 0) {
throw new IllegalArgumentException("Size " + size + " < 0");
@ -50,8 +44,8 @@ public class GlBuffer extends GlObject {
if (this.size == 0) {
this.size = size;
bind();
glBufferData(type.glEnum, size, usage.glEnum);
GlBufferType.COPY_WRITE_BUFFER.bind(handle());
glBufferData(GlBufferType.COPY_WRITE_BUFFER.glEnum, size, usage.glEnum);
FlwMemoryTracker._allocGPUMemory(size);
return true;
@ -76,36 +70,25 @@ public class GlBuffer extends GlObject {
var newHandle = glGenBuffers();
GlBufferType.COPY_READ_BUFFER.bind(oldHandle);
type.bind(newHandle);
GlBufferType.COPY_WRITE_BUFFER.bind(newHandle);
glBufferData(type.glEnum, newSize, usage.glEnum);
glCopyBufferSubData(GlBufferType.COPY_READ_BUFFER.glEnum, type.glEnum, 0, 0, oldSize);
glBufferData(GlBufferType.COPY_WRITE_BUFFER.glEnum, newSize, usage.glEnum);
glCopyBufferSubData(GlBufferType.COPY_READ_BUFFER.glEnum, GlBufferType.COPY_WRITE_BUFFER.glEnum, 0, 0, oldSize);
glDeleteBuffers(oldHandle);
setHandle(newHandle);
}
public void upload(MemoryBlock directBuffer) {
bind();
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();
FlwMemoryTracker._allocGPUMemory(size);
}
public MappedBuffer map() {
bind();
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;
return new MappedBuffer(handle(), size);
}
public void setGrowthMargin(int growthMargin) {
@ -116,18 +99,6 @@ public class GlBuffer extends GlObject {
return size;
}
public GlBufferType getType() {
return type;
}
public void bind() {
type.bind(handle());
}
public void unbind() {
type.unbind();
}
protected void deleteInternal(int handle) {
glDeleteBuffers(handle);
FlwMemoryTracker._freeGPUMemory(size);

View File

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

View File

@ -1,7 +1,7 @@
package com.jozufozu.flywheel.gl.versioned;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.Optional;
import org.lwjgl.PointerBuffer;
import org.lwjgl.opengl.GL;
@ -18,17 +18,18 @@ import net.minecraft.Util;
* system.
*/
public class GlCompat {
private static final GLCapabilities caps;
public static final VertexArray vertexArray;
public static final BufferStorage bufferStorage;
public static final boolean amd;
public static final boolean supportsIndirect;
static {
GLCapabilities caps = GL.createCapabilities();
bufferStorage = getLatest(BufferStorage.class, caps);
vertexArray = getLatest(VertexArray.class, caps);
supportsIndirect = caps.OpenGL46;
amd = _isAmdWindows();
caps = GL.createCapabilities();
bufferStorage = getLatest(BufferStorage.class);
vertexArray = getLatest(VertexArray.class);
supportsIndirect = _decideIfWeSupportIndirect();
amd = _decideIfWeAreAMDWindows();
}
private GlCompat() {
@ -46,29 +47,30 @@ public class GlCompat {
return supportsIndirect;
}
public static boolean bufferStorageSupported() {
return bufferStorage != BufferStorage.UNSUPPORTED;
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);
}
/**
* 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 clazz The class of the versioning enum.
* @return The first defined enum variant to return true.
*/
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)) {
throw new IllegalStateException("");
private static <V extends Enum<V> & GlVersioned> V getLatest(Class<V> clazz) {
for (V it : clazz.getEnumConstants()) {
if (it.supported(GlCompat.caps)) {
return Optional.of(it)
.get();
}
}
return Arrays.stream(constants)
.filter(it -> it.supported(caps))
.findFirst()
.get();
throw new IllegalStateException("Invalid versioned enum, must provide at least one supported constant");
}
/**
@ -92,7 +94,7 @@ public class GlCompat {
}
}
private static boolean _isAmdWindows() {
private static boolean _decideIfWeAreAMDWindows() {
if (Util.getPlatform() != Util.OS.WINDOWS) {
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.array.GlVertexArray;
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.lib.layout.CommonItems;
import com.jozufozu.flywheel.util.Lazy;
@ -35,10 +34,10 @@ public class FullscreenQuad {
private FullscreenQuad() {
try (var restoreState = GlStateTracker.getRestoreState()) {
vbo = new GlBuffer(GlBufferType.ARRAY_BUFFER);
vbo = new GlBuffer();
vbo.ensureCapacity(bufferSize);
try (MappedBuffer buffer = vbo.map()) {
var ptr = buffer.getPtr();
var ptr = buffer.ptr();
for (var i = 0; i < vertices.length; i++) {
MemoryUtil.memPutFloat(ptr + i * Float.BYTES, vertices[i]);