Buffer buffers

- Do not map buffers
- Strip down GlBuffer, so it only contains upload methods
- Remove buffer mapping and copy wrappers
- Add subdata wrapper
This commit is contained in:
Jozufozu 2024-02-20 15:23:07 -06:00
parent c61feb1c14
commit aac12754cc
5 changed files with 56 additions and 171 deletions

View file

@ -7,7 +7,6 @@ import java.util.Set;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import com.jozufozu.flywheel.Flywheel;
import com.jozufozu.flywheel.api.context.Context; import com.jozufozu.flywheel.api.context.Context;
import com.jozufozu.flywheel.api.instance.Instance; import com.jozufozu.flywheel.api.instance.Instance;
import com.jozufozu.flywheel.api.instance.InstanceType; import com.jozufozu.flywheel.api.instance.InstanceType;
@ -18,7 +17,7 @@ import com.jozufozu.flywheel.backend.gl.array.GlVertexArray;
import com.jozufozu.flywheel.backend.gl.array.VertexAttribute; import com.jozufozu.flywheel.backend.gl.array.VertexAttribute;
import com.jozufozu.flywheel.backend.gl.buffer.GlBuffer; import com.jozufozu.flywheel.backend.gl.buffer.GlBuffer;
import com.jozufozu.flywheel.backend.gl.buffer.GlBufferUsage; import com.jozufozu.flywheel.backend.gl.buffer.GlBufferUsage;
import com.jozufozu.flywheel.backend.gl.buffer.MappedBuffer; import com.jozufozu.flywheel.lib.memory.MemoryBlock;
public class InstancedInstancer<I extends Instance> extends AbstractInstancer<I> { public class InstancedInstancer<I extends Instance> extends AbstractInstancer<I> {
private final List<VertexAttribute> instanceAttributes; private final List<VertexAttribute> instanceAttributes;
@ -45,7 +44,6 @@ public class InstancedInstancer<I extends Instance> extends AbstractInstancer<I>
} }
vbo = new GlBuffer(GlBufferUsage.DYNAMIC_DRAW); vbo = new GlBuffer(GlBufferUsage.DYNAMIC_DRAW);
vbo.growthFunction(l -> Math.max(l + (long) instanceStride * 16, (long) (l * 1.6)));
} }
public void update() { public void update() {
@ -59,28 +57,61 @@ public class InstancedInstancer<I extends Instance> extends AbstractInstancer<I>
} }
int byteSize = instanceStride * instances.size(); int byteSize = instanceStride * instances.size();
if (vbo.ensureCapacity(byteSize)) { if (needsToGrow(byteSize)) {
// The vbo has moved, so we need to re-bind attributes // TODO: Should this memory block be persistent?
boundTo.clear(); var temp = MemoryBlock.malloc(increaseSize(byteSize));
writeAll(temp.ptr());
vbo.upload(temp);
temp.free();
} else {
writeChanged();
} }
try (MappedBuffer buf = vbo.map()) { changed.clear();
writeChanged(buf.ptr());
changed.clear();
} catch (Exception e) {
Flywheel.LOGGER.error("Error updating InstancedInstancer:", e);
}
} }
private void writeChanged(long ptr) { private void writeChanged() {
changed.forEachSetSpan((startInclusive, endInclusive) -> { changed.forEachSetSpan((startInclusive, endInclusive) -> {
var temp = MemoryBlock.malloc((long) instanceStride * (endInclusive - startInclusive + 1));
long ptr = temp.ptr();
for (int i = startInclusive; i <= endInclusive; i++) { for (int i = startInclusive; i <= endInclusive; i++) {
writer.write(ptr + (long) instanceStride * i, instances.get(i)); writer.write(ptr, instances.get(i));
ptr += instanceStride;
} }
vbo.uploadSpan((long) startInclusive * instanceStride, temp);
temp.free();
}); });
} }
private void writeAll(long ptr) {
for (I instance : instances) {
writer.write(ptr, instance);
ptr += instanceStride;
}
}
private long increaseSize(long capacity) {
return Math.max(capacity + (long) instanceStride * 16, (long) (capacity * 1.6));
}
public boolean needsToGrow(long capacity) {
if (capacity < 0) {
throw new IllegalArgumentException("Size " + capacity + " < 0");
}
if (capacity == 0) {
return false;
}
return capacity > vbo.size();
}
/** /**
* Bind this instancer's vbo to the given vao if it hasn't already been bound. * Bind this instancer's vbo to the given vao if it hasn't already been bound.
* @param vao The vao to bind to. * @param vao The vao to bind to.

View file

@ -1,8 +1,6 @@
package com.jozufozu.flywheel.backend.gl.buffer; package com.jozufozu.flywheel.backend.gl.buffer;
import org.lwjgl.opengl.GL15; import org.lwjgl.opengl.GL15;
import org.lwjgl.opengl.GL30;
import org.lwjgl.opengl.GL31;
import org.lwjgl.opengl.GL45C; import org.lwjgl.opengl.GL45C;
import org.lwjgl.system.Checks; import org.lwjgl.system.Checks;
@ -15,11 +13,7 @@ public interface Buffer {
void data(int vbo, long size, long ptr, int glEnum); void data(int vbo, long size, long ptr, int glEnum);
void copyData(int src, int dst, long srcOffset, long dstOffset, long size); void subData(int vbo, long offset, long size, long ptr);
long mapRange(int handle, int offset, long size, int access);
void unmap(int handle);
class DSA implements Buffer { class DSA implements Buffer {
@Override @Override
@ -33,18 +27,8 @@ public interface Buffer {
} }
@Override @Override
public void copyData(int src, int dst, long srcOffset, long dstOffset, long size) { public void subData(int vbo, long offset, long size, long ptr) {
GL45C.glCopyNamedBufferSubData(src, dst, srcOffset, dstOffset, size); GL45C.nglNamedBufferSubData(vbo, offset, size, ptr);
}
@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() { public Buffer fallback() {
@ -73,23 +57,9 @@ public interface Buffer {
} }
@Override @Override
public void copyData(int src, int dst, long size, long srcOffset, long dstOffset) { public void subData(int vbo, long offset, long size, long ptr) {
GlBufferType.COPY_READ_BUFFER.bind(src); GlBufferType.COPY_WRITE_BUFFER.bind(vbo);
GlBufferType.COPY_WRITE_BUFFER.bind(dst); GL15.nglBufferSubData(GlBufferType.COPY_WRITE_BUFFER.glEnum, offset, size, ptr);
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,24 +1,16 @@
package com.jozufozu.flywheel.backend.gl.buffer; package com.jozufozu.flywheel.backend.gl.buffer;
import org.lwjgl.system.MemoryUtil;
import com.jozufozu.flywheel.backend.gl.GlObject; import com.jozufozu.flywheel.backend.gl.GlObject;
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;
import com.mojang.blaze3d.platform.GlStateManager; import com.mojang.blaze3d.platform.GlStateManager;
import it.unimi.dsi.fastutil.longs.LongUnaryOperator;
public class GlBuffer extends GlObject { public class GlBuffer extends GlObject {
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.
*/ */
protected long size; protected long size;
/**
* A mapping to adjust the size of the buffer when allocating.
*/
protected LongUnaryOperator growthFunction = LongUnaryOperator.identity();
public GlBuffer() { public GlBuffer() {
this(GlBufferUsage.STATIC_DRAW); this(GlBufferUsage.STATIC_DRAW);
@ -29,63 +21,9 @@ public class GlBuffer extends GlObject {
this.usage = usage; this.usage = usage;
} }
/**
* @return true if the buffer was recreated.
*/
public boolean ensureCapacity(long capacity) {
if (capacity < 0) {
throw new IllegalArgumentException("Size " + capacity + " < 0");
}
if (capacity == 0) {
return false;
}
if (size == 0) {
alloc(capacity);
return true;
}
if (capacity > size) {
realloc(capacity);
return true;
}
return false;
}
private void alloc(long capacity) {
increaseSize(capacity);
Buffer.IMPL.data(handle(), size, MemoryUtil.NULL, usage.glEnum);
FlwMemoryTracker._allocGPUMemory(size);
}
private void realloc(long capacity) {
FlwMemoryTracker._freeGPUMemory(size);
var oldSize = size;
increaseSize(capacity);
int oldHandle = handle();
int newHandle = Buffer.IMPL.create();
Buffer.IMPL.data(newHandle, size, MemoryUtil.NULL, usage.glEnum);
Buffer.IMPL.copyData(oldHandle, newHandle, 0, 0, oldSize);
GlStateManager._glDeleteBuffers(oldHandle);
handle(newHandle);
FlwMemoryTracker._allocGPUMemory(size);
}
/**
* Increase the size of the buffer to at least the given capacity.
*/
private void increaseSize(long capacity) {
size = growthFunction.apply(capacity);
}
public void upload(MemoryBlock memoryBlock) { public void upload(MemoryBlock memoryBlock) {
upload(memoryBlock.ptr(), memoryBlock.size()); upload(memoryBlock.ptr(), memoryBlock.size());
} }
public void upload(long ptr, long size) { public void upload(long ptr, long size) {
FlwMemoryTracker._freeGPUMemory(this.size); FlwMemoryTracker._freeGPUMemory(this.size);
Buffer.IMPL.data(handle(), size, ptr, usage.glEnum); Buffer.IMPL.data(handle(), size, ptr, usage.glEnum);
@ -93,12 +31,12 @@ public class GlBuffer extends GlObject {
FlwMemoryTracker._allocGPUMemory(this.size); FlwMemoryTracker._allocGPUMemory(this.size);
} }
public MappedBuffer map() { public void uploadSpan(long offset, MemoryBlock memoryBlock) {
return new MappedBuffer(handle(), size); uploadSpan(offset, memoryBlock.ptr(), memoryBlock.size());
} }
public void growthFunction(LongUnaryOperator growthFunction) { public void uploadSpan(long offset, long ptr, long size) {
this.growthFunction = growthFunction; Buffer.IMPL.subData(handle(), offset, size, ptr);
} }
public long size() { public long size() {

View file

@ -1,38 +0,0 @@
package com.jozufozu.flywheel.backend.gl.buffer;
import static org.lwjgl.opengl.GL30.GL_MAP_WRITE_BIT;
import static org.lwjgl.system.MemoryUtil.NULL;
import org.lwjgl.system.MemoryUtil;
import com.jozufozu.flywheel.backend.gl.error.GlError;
import com.jozufozu.flywheel.backend.gl.error.GlException;
public class MappedBuffer implements AutoCloseable {
private final int glBuffer;
private long ptr;
public MappedBuffer(int glBuffer, long size) {
this.glBuffer = glBuffer;
ptr = Buffer.IMPL.mapRange(glBuffer, 0, size, GL_MAP_WRITE_BIT);
if (ptr == MemoryUtil.NULL) {
throw new GlException(GlError.poll(), "Could not map buffer");
}
}
public long ptr() {
return ptr;
}
@Override
public void close() {
if (ptr == NULL) {
return;
}
Buffer.IMPL.unmap(glBuffer);
ptr = NULL;
}
}

View file

@ -1,16 +0,0 @@
package com.jozufozu.flywheel.backend.gl.buffer;
import org.lwjgl.opengl.GL15C;
public enum MappedBufferUsage {
READ_ONLY(GL15C.GL_READ_ONLY),
WRITE_ONLY(GL15C.GL_WRITE_ONLY),
READ_WRITE(GL15C.GL_READ_WRITE),
;
int glEnum;
MappedBufferUsage(int glEnum) {
this.glEnum = glEnum;
}
}