So unsafe it wrapped back around

- Convert buffer mapping to unsafe
 - Implement gpu memory tracking
 - Fix massive gl resource leak
 - Fix GPU memory leak caused by not deleting gl resources
 - Fix CPU memory leak caused by not deleting meshes in Models
 - Remove PersistentGlBuffer, merge GlBuffer and MappedGlBuffer
This commit is contained in:
Jozufozu 2022-08-13 23:18:44 -07:00
parent 293d6ee59c
commit a1553b04e7
22 changed files with 191 additions and 359 deletions

View file

@ -6,13 +6,13 @@ import com.jozufozu.flywheel.core.source.FileResolution;
public abstract class UniformProvider { public abstract class UniformProvider {
protected ByteBuffer buffer; protected long ptr;
protected Notifier notifier; protected Notifier notifier;
public abstract int getSize(); public abstract int getSize();
public void updatePtr(ByteBuffer backing, Notifier notifier) { public void updatePtr(long ptr, Notifier notifier) {
this.buffer = backing; this.ptr = ptr;
this.notifier = notifier; this.notifier = notifier;
} }

View file

@ -5,8 +5,8 @@ import org.lwjgl.opengl.GL32;
import com.jozufozu.flywheel.backend.gl.GlObject; import com.jozufozu.flywheel.backend.gl.GlObject;
import com.jozufozu.flywheel.backend.gl.GlStateTracker; import com.jozufozu.flywheel.backend.gl.GlStateTracker;
import com.jozufozu.flywheel.backend.gl.buffer.GlBuffer;
import com.jozufozu.flywheel.backend.gl.buffer.GlBufferType; import com.jozufozu.flywheel.backend.gl.buffer.GlBufferType;
import com.jozufozu.flywheel.backend.gl.buffer.GlBuffer;
import com.jozufozu.flywheel.backend.gl.versioned.GlCompat; import com.jozufozu.flywheel.backend.gl.versioned.GlCompat;
import com.jozufozu.flywheel.core.layout.BufferLayout; import com.jozufozu.flywheel.core.layout.BufferLayout;
import com.mojang.blaze3d.platform.GlStateManager; import com.mojang.blaze3d.platform.GlStateManager;

View file

@ -1,49 +1,105 @@
package com.jozufozu.flywheel.backend.gl.buffer; package com.jozufozu.flywheel.backend.gl.buffer;
import java.nio.ByteBuffer; import static org.lwjgl.opengl.GL32.*;
import org.lwjgl.opengl.GL20; import org.lwjgl.system.MemoryUtil;
import com.jozufozu.flywheel.backend.gl.GlObject; import com.jozufozu.flywheel.backend.gl.GlObject;
import com.jozufozu.flywheel.backend.gl.versioned.GlCompat; import com.jozufozu.flywheel.backend.gl.error.GlError;
import com.jozufozu.flywheel.backend.gl.error.GlException;
import com.jozufozu.flywheel.backend.memory.FlwMemoryTracker;
import com.jozufozu.flywheel.backend.memory.MemoryBlock;
public abstract class GlBuffer extends GlObject { public class GlBuffer extends GlObject {
/**
* Request a Persistent mapped buffer.
*
* <p>
* If Persistent buffers are supported, this will provide one. Otherwise it will fall back to a classic mapped
* buffer.
* </p>
*
* @param type The type of buffer you want.
* @return A buffer that will be persistent if the driver supports it.
*/
public static GlBuffer requestPersistent(GlBufferType type) {
if (GlCompat.getInstance()
.bufferStorageSupported()) {
return new PersistentGlBuffer(type);
} else {
return new MappedGlBuffer(type);
}
}
public final GlBufferType type; public final GlBufferType type;
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;
/** /**
* How much extra room to give the buffer when we reallocate. * How much extra room to give the buffer when we reallocate.
*/ */
protected int growthMargin; protected int growthMargin;
public GlBuffer(GlBufferType type) { public GlBuffer(GlBufferType type) {
setHandle(GL20.glGenBuffers()); this(type, GlBufferUsage.STATIC_DRAW);
}
public GlBuffer(GlBufferType type, GlBufferUsage usage) {
setHandle(glGenBuffers());
this.type = type; this.type = type;
this.usage = usage;
}
public boolean ensureCapacity(long size) {
if (size < 0) {
throw new IllegalArgumentException("Size " + size + " < 0");
}
if (size == 0) {
return false;
}
if (this.size == 0) {
this.size = size;
bind();
glBufferData(type.glEnum, size, usage.glEnum);
FlwMemoryTracker._allocGPUMemory(size);
return true;
}
if (size > this.size) {
var oldSize = this.size;
this.size = size + growthMargin;
realloc(oldSize, this.size);
return true;
}
return false;
}
private void realloc(long oldSize, long newSize) {
FlwMemoryTracker._freeGPUMemory(oldSize);
FlwMemoryTracker._allocGPUMemory(newSize);
var oldHandle = handle();
var newHandle = glGenBuffers();
GlBufferType.COPY_READ_BUFFER.bind(oldHandle);
type.bind(newHandle);
glBufferData(type.glEnum, newSize, usage.glEnum);
glCopyBufferSubData(GlBufferType.COPY_READ_BUFFER.glEnum, type.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);
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;
} }
public void setGrowthMargin(int growthMargin) { public void setGrowthMargin(int growthMargin) {
@ -66,24 +122,8 @@ public abstract class GlBuffer extends GlObject {
type.unbind(); type.unbind();
} }
public abstract void upload(ByteBuffer directBuffer);
public abstract MappedBuffer map();
/**
* Ensure that the buffer has at least enough room to store {@code size} bytes.
*
* @return {@code true} if the buffer moved.
*/
public abstract boolean ensureCapacity(long size);
protected void deleteInternal(int handle) { protected void deleteInternal(int handle) {
GL20.glDeleteBuffers(handle); glDeleteBuffers(handle);
FlwMemoryTracker._freeGPUMemory(size);
} }
/**
* Indicates that this buffer need not be #flush()'d for its contents to sync.
* @return true if this buffer is persistently mapped.
*/
public abstract boolean isPersistent();
} }

View file

@ -1,6 +1,6 @@
package com.jozufozu.flywheel.backend.gl.buffer; package com.jozufozu.flywheel.backend.gl.buffer;
import java.nio.ByteBuffer; 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;
@ -11,10 +11,10 @@ public class MappedBuffer implements AutoCloseable {
private final long length; private final long length;
private final GlBuffer owner; private final GlBuffer owner;
private final boolean persistent; private final boolean persistent;
private ByteBuffer internal; private long ptr;
public MappedBuffer(GlBuffer owner, ByteBuffer internal, long offset, long length) { public MappedBuffer(GlBuffer owner, long ptr, long offset, long length) {
this.internal = internal; this.ptr = ptr;
this.owner = owner; this.owner = owner;
this.offset = offset; this.offset = offset;
this.length = length; this.length = length;
@ -27,19 +27,11 @@ public class MappedBuffer implements AutoCloseable {
public void flush() { public void flush() {
if (persistent) return; if (persistent) return;
if (internal == null) return; if (ptr == NULL) return;
owner.bind(); owner.bind();
GL15.glUnmapBuffer(owner.getType().glEnum); GL15.glUnmapBuffer(owner.getType().glEnum);
internal = null; ptr = NULL;
}
public MappedBuffer position(int p) {
if (p < offset || p >= offset + length) {
throw new IndexOutOfBoundsException("Index " + p + " is not mapped");
}
internal.position(p - (int) offset);
return this;
} }
@Override @Override
@ -47,12 +39,8 @@ public class MappedBuffer implements AutoCloseable {
flush(); flush();
} }
public ByteBuffer unwrap() { public long getPtr() {
return internal; return ptr;
}
public long getMemAddress() {
return MemoryUtil.memAddress(internal);
} }
public void clear(long clearStart, long clearLength) { public void clear(long clearStart, long clearLength) {
@ -64,7 +52,7 @@ public class MappedBuffer implements AutoCloseable {
throw new IndexOutOfBoundsException("Clear range [" + clearStart + "," + (clearStart + clearLength) + "] is not mapped"); throw new IndexOutOfBoundsException("Clear range [" + clearStart + "," + (clearStart + clearLength) + "] is not mapped");
} }
long addr = MemoryUtil.memAddress(unwrap()) + clearStart; long addr = ptr + clearStart;
MemoryUtil.memSet(addr, 0, clearLength); MemoryUtil.memSet(addr, 0, clearLength);
} }

View file

@ -1,91 +0,0 @@
package com.jozufozu.flywheel.backend.gl.buffer;
import java.nio.ByteBuffer;
import org.lwjgl.opengl.GL30;
import org.lwjgl.opengl.GL32;
import com.jozufozu.flywheel.backend.gl.error.GlError;
import com.jozufozu.flywheel.backend.gl.error.GlException;
public class MappedGlBuffer extends GlBuffer {
protected final GlBufferUsage usage;
public MappedGlBuffer(GlBufferType type) {
this(type, GlBufferUsage.STATIC_DRAW);
}
public MappedGlBuffer(GlBufferType type, GlBufferUsage usage) {
super(type);
this.usage = usage;
}
@Override
public boolean ensureCapacity(long size) {
if (size < 0) {
throw new IllegalArgumentException("Size " + size + " < 0");
}
if (size == 0) {
return false;
}
if (this.size == 0) {
this.size = size;
bind();
GL32.glBufferData(type.glEnum, size, usage.glEnum);
return true;
}
if (size > this.size) {
var oldSize = this.size;
this.size = size + growthMargin;
realloc(oldSize, this.size);
return true;
}
return false;
}
private void realloc(long oldSize, long newSize) {
var oldHandle = handle();
var newHandle = GL32.glGenBuffers();
GlBufferType.COPY_READ_BUFFER.bind(oldHandle);
type.bind(newHandle);
GL32.glBufferData(type.glEnum, newSize, usage.glEnum);
GL32.glCopyBufferSubData(GlBufferType.COPY_READ_BUFFER.glEnum, type.glEnum, 0, 0, oldSize);
delete();
setHandle(newHandle);
}
@Override
public void upload(ByteBuffer directBuffer) {
bind();
GL32.glBufferData(type.glEnum, directBuffer, usage.glEnum);
this.size = directBuffer.capacity();
}
@Override
public MappedBuffer map() {
bind();
ByteBuffer byteBuffer = GL30.glMapBufferRange(type.glEnum, 0, size, GL30.GL_MAP_WRITE_BIT);
if (byteBuffer == null) {
throw new GlException(GlError.poll(), "Could not map buffer");
}
return new MappedBuffer(this, byteBuffer, 0, size);
}
@Override
public boolean isPersistent() {
return false;
}
}

View file

@ -1,124 +0,0 @@
package com.jozufozu.flywheel.backend.gl.buffer;
import static org.lwjgl.opengl.GL30.GL_MAP_WRITE_BIT;
import static org.lwjgl.opengl.GL44.GL_MAP_COHERENT_BIT;
import static org.lwjgl.opengl.GL44.GL_MAP_PERSISTENT_BIT;
import java.nio.ByteBuffer;
import org.jetbrains.annotations.Nullable;
import org.lwjgl.opengl.GL32;
import org.lwjgl.system.MemoryUtil;
import com.jozufozu.flywheel.backend.gl.error.GlError;
import com.jozufozu.flywheel.backend.gl.error.GlException;
import com.jozufozu.flywheel.backend.gl.versioned.GlCompat;
public class PersistentGlBuffer extends GlBuffer {
@Nullable
private MappedBuffer access;
private final int storageFlags;
public PersistentGlBuffer(GlBufferType type) {
super(type);
storageFlags = GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT;
}
@Override
public boolean ensureCapacity(long size) {
if (size < 0) {
throw new IllegalArgumentException("Size " + size + " < 0");
}
if (size == 0) {
return false;
}
if (this.size == 0) {
this.size = size;
bind();
GlCompat.getInstance().bufferStorage.bufferStorage(type, this.size, storageFlags);
return true;
}
if (size > this.size) {
var oldSize = this.size;
this.size = size + growthMargin;
realloc(this.size, oldSize);
access = null;
return true;
}
return false;
}
@Override
public void upload(ByteBuffer directBuffer) {
ensureCapacity(directBuffer.capacity());
var access = getWriteAccess();
ByteBuffer ourBuffer = access.unwrap();
ourBuffer.reset();
MemoryUtil.memCopy(directBuffer, ourBuffer);
int uploadSize = directBuffer.remaining();
int ourSize = ourBuffer.capacity();
if (uploadSize < ourSize) {
long clearFrom = access.getMemAddress() + uploadSize;
MemoryUtil.memSet(clearFrom, 0, ourSize - uploadSize);
}
}
private void mapToClientMemory() {
bind();
ByteBuffer byteBuffer = GL32.glMapBufferRange(type.glEnum, 0, size, storageFlags);
if (byteBuffer == null) {
throw new GlException(GlError.poll(), "Could not map buffer");
}
access = new MappedBuffer(this, byteBuffer, 0, size);
}
private void realloc(long newSize, long oldSize) {
int oldHandle = handle();
int newHandle = GL32.glGenBuffers();
GlBufferType.COPY_READ_BUFFER.bind(oldHandle);
type.bind(newHandle);
GlCompat.getInstance().bufferStorage.bufferStorage(type, newSize, storageFlags);
GL32.glCopyBufferSubData(GlBufferType.COPY_READ_BUFFER.glEnum, type.glEnum, 0, 0, oldSize);
delete();
setHandle(newHandle);
}
@Override
public MappedBuffer map() {
return getWriteAccess()
.position(0);
}
private MappedBuffer getWriteAccess() {
if (access == null) {
mapToClientMemory();
}
return access;
}
@Override
public boolean isPersistent() {
return true;
}
}

View file

@ -27,7 +27,7 @@ import net.minecraft.world.level.block.entity.BlockEntity;
* The instancer manager is shared between the different instance managers. * The instancer manager is shared between the different instance managers.
* </p> * </p>
*/ */
public class InstanceWorld { public class InstanceWorld implements AutoCloseable {
protected final Engine engine; protected final Engine engine;
protected final InstanceManager<Entity> entities; protected final InstanceManager<Entity> entities;
protected final InstanceManager<BlockEntity> blockEntities; protected final InstanceManager<BlockEntity> blockEntities;
@ -149,4 +149,8 @@ public class InstanceWorld {
.forEach(entities::add); .forEach(entities::add);
} }
@Override
public void close() {
delete();
}
} }

View file

@ -89,7 +89,7 @@ public class DrawBuffer {
} }
/** /**
* Reset the draw buffer to have no vertices. * Reset the draw buffer to have no vertices.<p>
* *
* Does not clear the backing buffer. * Does not clear the backing buffer.
*/ */

View file

@ -3,18 +3,15 @@ package com.jozufozu.flywheel.backend.instancing.instancing;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import org.lwjgl.system.MemoryUtil;
import com.jozufozu.flywheel.Flywheel; import com.jozufozu.flywheel.Flywheel;
import com.jozufozu.flywheel.api.instancer.InstancedPart; import com.jozufozu.flywheel.api.instancer.InstancedPart;
import com.jozufozu.flywheel.api.struct.StructType; import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.api.struct.StructWriter; import com.jozufozu.flywheel.api.struct.StructWriter;
import com.jozufozu.flywheel.backend.gl.array.GlVertexArray; import com.jozufozu.flywheel.backend.gl.array.GlVertexArray;
import com.jozufozu.flywheel.backend.gl.buffer.GlBuffer;
import com.jozufozu.flywheel.backend.gl.buffer.GlBufferType; import com.jozufozu.flywheel.backend.gl.buffer.GlBufferType;
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.backend.gl.buffer.MappedBuffer;
import com.jozufozu.flywheel.backend.gl.buffer.MappedGlBuffer; import com.jozufozu.flywheel.backend.gl.buffer.GlBuffer;
import com.jozufozu.flywheel.backend.instancing.AbstractInstancer; import com.jozufozu.flywheel.backend.instancing.AbstractInstancer;
import com.jozufozu.flywheel.core.layout.BufferLayout; import com.jozufozu.flywheel.core.layout.BufferLayout;
@ -45,7 +42,7 @@ public class GPUInstancer<D extends InstancedPart> extends AbstractInstancer<D>
public void init() { public void init() {
if (vbo != null) return; if (vbo != null) return;
vbo = new MappedGlBuffer(GlBufferType.ARRAY_BUFFER, GlBufferUsage.DYNAMIC_DRAW); vbo = new GlBuffer(GlBufferType.ARRAY_BUFFER, GlBufferUsage.DYNAMIC_DRAW);
vbo.setGrowthMargin(instanceFormat.getStride() * 16); vbo.setGrowthMargin(instanceFormat.getStride() * 16);
} }
@ -91,8 +88,8 @@ public class GPUInstancer<D extends InstancedPart> extends AbstractInstancer<D>
buf.clear(clearStart, clearLength); buf.clear(clearStart, clearLength);
if (size > 0) { if (size > 0) {
final long ptr = MemoryUtil.memAddress(buf.unwrap()); final long ptr = buf.getPtr();
final int stride = structType.getLayout().getStride(); final long stride = structType.getLayout().getStride();
final StructWriter<D> writer = structType.getWriter(); final StructWriter<D> writer = structType.getWriter();
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {

View file

@ -1,6 +1,5 @@
package com.jozufozu.flywheel.backend.instancing.instancing; package com.jozufozu.flywheel.backend.instancing.instancing;
import java.nio.ByteBuffer;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
@ -10,16 +9,14 @@ import java.util.Set;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.lwjgl.opengl.GL32; import org.lwjgl.opengl.GL32;
import org.lwjgl.system.MemoryUtil;
import com.jozufozu.flywheel.Flywheel; import com.jozufozu.flywheel.Flywheel;
import com.jozufozu.flywheel.api.vertex.VertexType; import com.jozufozu.flywheel.api.vertex.VertexType;
import com.jozufozu.flywheel.backend.gl.GlPrimitive; import com.jozufozu.flywheel.backend.gl.GlPrimitive;
import com.jozufozu.flywheel.backend.gl.array.GlVertexArray; import com.jozufozu.flywheel.backend.gl.array.GlVertexArray;
import com.jozufozu.flywheel.backend.gl.buffer.GlBuffer;
import com.jozufozu.flywheel.backend.gl.buffer.GlBufferType; import com.jozufozu.flywheel.backend.gl.buffer.GlBufferType;
import com.jozufozu.flywheel.backend.gl.buffer.MappedBuffer; import com.jozufozu.flywheel.backend.gl.buffer.MappedBuffer;
import com.jozufozu.flywheel.backend.gl.buffer.MappedGlBuffer; import com.jozufozu.flywheel.backend.gl.buffer.GlBuffer;
import com.jozufozu.flywheel.core.layout.BufferLayout; import com.jozufozu.flywheel.core.layout.BufferLayout;
import com.jozufozu.flywheel.core.model.Mesh; import com.jozufozu.flywheel.core.model.Mesh;
import com.jozufozu.flywheel.event.ReloadRenderersEvent; import com.jozufozu.flywheel.event.ReloadRenderersEvent;
@ -58,7 +55,7 @@ public class MeshPool {
* Create a new mesh pool. * Create a new mesh pool.
*/ */
public MeshPool() { public MeshPool() {
vbo = new MappedGlBuffer(GlBufferType.ARRAY_BUFFER); vbo = new GlBuffer(GlBufferType.ARRAY_BUFFER);
vbo.setGrowthMargin(2048); vbo.setGrowthMargin(2048);
} }
@ -141,13 +138,13 @@ public class MeshPool {
private void uploadAll() { private void uploadAll() {
try (MappedBuffer mapped = vbo.map()) { try (MappedBuffer mapped = vbo.map()) {
ByteBuffer buffer = mapped.unwrap(); long ptr = mapped.getPtr();
int byteIndex = 0; int byteIndex = 0;
for (BufferedMesh model : allBuffered) { for (BufferedMesh model : allBuffered) {
model.byteIndex = byteIndex; model.byteIndex = byteIndex;
model.buffer(buffer); model.buffer(ptr);
byteIndex += model.mesh.size(); byteIndex += model.mesh.size();
} }
@ -159,7 +156,7 @@ public class MeshPool {
private void uploadPending() { private void uploadPending() {
try (MappedBuffer mapped = vbo.map()) { try (MappedBuffer mapped = vbo.map()) {
ByteBuffer buffer = mapped.unwrap(); long buffer = mapped.getPtr();
for (BufferedMesh model : pendingUpload) { for (BufferedMesh model : pendingUpload) {
model.buffer(buffer); model.buffer(buffer);
} }
@ -240,10 +237,8 @@ public class MeshPool {
this.deleted = true; this.deleted = true;
} }
private void buffer(ByteBuffer buffer) { private void buffer(long ptr) {
buffer.position((int) this.byteIndex); this.mesh.write(ptr + byteIndex);
long ptr = MemoryUtil.memAddress(buffer);
this.mesh.write(ptr);
this.boundTo.clear(); this.boundTo.clear();
this.gpuResident = true; this.gpuResident = true;

View file

@ -3,13 +3,14 @@ package com.jozufozu.flywheel.core;
import static org.lwjgl.opengl.GL11.GL_TRIANGLES; import static org.lwjgl.opengl.GL11.GL_TRIANGLES;
import static org.lwjgl.opengl.GL11.glDrawArrays; import static org.lwjgl.opengl.GL11.glDrawArrays;
import org.lwjgl.system.MemoryUtil;
import com.jozufozu.flywheel.Flywheel; import com.jozufozu.flywheel.Flywheel;
import com.jozufozu.flywheel.backend.gl.GlStateTracker; import com.jozufozu.flywheel.backend.gl.GlStateTracker;
import com.jozufozu.flywheel.backend.gl.array.GlVertexArray; import com.jozufozu.flywheel.backend.gl.array.GlVertexArray;
import com.jozufozu.flywheel.backend.gl.buffer.GlBuffer;
import com.jozufozu.flywheel.backend.gl.buffer.GlBufferType; import com.jozufozu.flywheel.backend.gl.buffer.GlBufferType;
import com.jozufozu.flywheel.backend.gl.buffer.MappedBuffer; import com.jozufozu.flywheel.backend.gl.buffer.MappedBuffer;
import com.jozufozu.flywheel.backend.gl.buffer.MappedGlBuffer; import com.jozufozu.flywheel.backend.gl.buffer.GlBuffer;
import com.jozufozu.flywheel.core.layout.BufferLayout; import com.jozufozu.flywheel.core.layout.BufferLayout;
import com.jozufozu.flywheel.core.layout.CommonItems; import com.jozufozu.flywheel.core.layout.CommonItems;
import com.jozufozu.flywheel.util.Lazy; import com.jozufozu.flywheel.util.Lazy;
@ -34,13 +35,14 @@ public class FullscreenQuad {
private FullscreenQuad() { private FullscreenQuad() {
try (var restoreState = GlStateTracker.getRestoreState()) { try (var restoreState = GlStateTracker.getRestoreState()) {
vbo = new MappedGlBuffer(GlBufferType.ARRAY_BUFFER); vbo = new GlBuffer(GlBufferType.ARRAY_BUFFER);
vbo.ensureCapacity(bufferSize); vbo.ensureCapacity(bufferSize);
try (MappedBuffer buffer = vbo.map()) { try (MappedBuffer buffer = vbo.map()) {
var ptr = buffer.getPtr();
buffer.unwrap() for (var i = 0; i < vertices.length; i++) {
.asFloatBuffer() MemoryUtil.memPutFloat(ptr + i * Float.BYTES, vertices[i]);
.put(vertices); }
} catch (Exception e) { } catch (Exception e) {
Flywheel.LOGGER.error("Could not create fullscreen quad.", e); Flywheel.LOGGER.error("Could not create fullscreen quad.", e);

View file

@ -1,7 +1,5 @@
package com.jozufozu.flywheel.core; package com.jozufozu.flywheel.core;
import java.nio.ByteBuffer;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.lwjgl.system.MemoryUtil; import org.lwjgl.system.MemoryUtil;
@ -9,7 +7,7 @@ import org.lwjgl.system.MemoryUtil;
import com.jozufozu.flywheel.backend.gl.GlNumericType; import com.jozufozu.flywheel.backend.gl.GlNumericType;
import com.jozufozu.flywheel.backend.gl.buffer.GlBufferType; import com.jozufozu.flywheel.backend.gl.buffer.GlBufferType;
import com.jozufozu.flywheel.backend.gl.buffer.MappedBuffer; import com.jozufozu.flywheel.backend.gl.buffer.MappedBuffer;
import com.jozufozu.flywheel.backend.gl.buffer.MappedGlBuffer; import com.jozufozu.flywheel.backend.gl.buffer.GlBuffer;
import com.jozufozu.flywheel.backend.instancing.instancing.ElementBuffer; import com.jozufozu.flywheel.backend.instancing.instancing.ElementBuffer;
import com.jozufozu.flywheel.event.ReloadRenderersEvent; import com.jozufozu.flywheel.event.ReloadRenderersEvent;
@ -34,11 +32,11 @@ public class QuadConverter {
return INSTANCE; return INSTANCE;
} }
private final MappedGlBuffer ebo; private final GlBuffer ebo;
private int quadCapacity; private int quadCapacity;
public QuadConverter() { public QuadConverter() {
this.ebo = new MappedGlBuffer(GlBufferType.ELEMENT_ARRAY_BUFFER); this.ebo = new GlBuffer(GlBufferType.ELEMENT_ARRAY_BUFFER);
this.quadCapacity = 0; this.quadCapacity = 0;
} }
@ -49,9 +47,7 @@ public class QuadConverter {
ebo.ensureCapacity((long) indexCount * GlNumericType.UINT.getByteWidth()); ebo.ensureCapacity((long) indexCount * GlNumericType.UINT.getByteWidth());
try (MappedBuffer map = ebo.map()) { try (MappedBuffer map = ebo.map()) {
ByteBuffer indices = map.unwrap(); fillBuffer(map.getPtr(), quads);
fillBuffer(indices, quads);
} }
ebo.unbind(); ebo.unbind();
@ -66,32 +62,18 @@ public class QuadConverter {
this.quadCapacity = 0; this.quadCapacity = 0;
} }
private void fillBuffer(ByteBuffer indices, int quads) { private void fillBuffer(long addr, int quads) {
long addr = MemoryUtil.memAddress(indices);
int numVertices = 4 * quads; int numVertices = 4 * quads;
int baseVertex = 0; int baseVertex = 0;
while (baseVertex < numVertices) { while (baseVertex < numVertices) {
// writeQuadIndices(indices, baseVertex); writeQuadIndices(addr, baseVertex);
writeQuadIndicesUnsafe(addr, baseVertex);
baseVertex += 4; baseVertex += 4;
addr += 6 * 4; addr += 6 * 4;
} }
// ((Buffer) indices).flip();
} }
private void writeQuadIndices(ByteBuffer indices, int baseVertex) { private void writeQuadIndices(long addr, int baseVertex) {
// triangle a
indices.putInt(baseVertex);
indices.putInt(baseVertex + 1);
indices.putInt(baseVertex + 2);
// triangle b
indices.putInt(baseVertex);
indices.putInt(baseVertex + 2);
indices.putInt(baseVertex + 3);
}
private void writeQuadIndicesUnsafe(long addr, int baseVertex) {
// triangle a // triangle a
MemoryUtil.memPutInt(addr, baseVertex); MemoryUtil.memPutInt(addr, baseVertex);
MemoryUtil.memPutInt(addr + 4, baseVertex + 1); MemoryUtil.memPutInt(addr + 4, baseVertex + 1);
@ -102,7 +84,7 @@ public class QuadConverter {
MemoryUtil.memPutInt(addr + 20, baseVertex + 3); MemoryUtil.memPutInt(addr + 20, baseVertex + 3);
} }
// make sure this gets reset first so it has a chance to repopulate // make sure this gets reset first, so it has a chance to repopulate
public static void onRendererReload(ReloadRenderersEvent event) { public static void onRendererReload(ReloadRenderersEvent event) {
if (INSTANCE != null) { if (INSTANCE != null) {
INSTANCE.delete(); INSTANCE.delete();

View file

@ -7,6 +7,8 @@ import com.jozufozu.flywheel.api.material.Material;
public interface Model { public interface Model {
Map<Material, Mesh> getMeshes(); Map<Material, Mesh> getMeshes();
void delete();
default int getVertexCount() { default int getVertexCount() {
int size = 0; int size = 0;
for (Mesh mesh : getMeshes().values()) { for (Mesh mesh : getMeshes().values()) {

View file

@ -1,5 +1,6 @@
package com.jozufozu.flywheel.core.model; package com.jozufozu.flywheel.core.model;
import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@ -41,8 +42,16 @@ public class Models {
} }
public static void onReload(ReloadRenderersEvent event) { public static void onReload(ReloadRenderersEvent event) {
deleteAll(BLOCK_STATE.values());
deleteAll(PARTIAL.values());
deleteAll(PARTIAL_DIR.values());
BLOCK_STATE.clear(); BLOCK_STATE.clear();
PARTIAL.clear(); PARTIAL.clear();
PARTIAL_DIR.clear(); PARTIAL_DIR.clear();
} }
private static void deleteAll(Collection<Model> values) {
values.forEach(Model::delete);
}
} }

View file

@ -28,6 +28,11 @@ public class SimpleLazyModel implements Model {
return ImmutableMap.of(material, supplier.get()); return ImmutableMap.of(material, supplier.get());
} }
@Override
public void delete() {
supplier.ifPresent(Mesh::close);
}
public int getVertexCount() { public int getVertexCount() {
return supplier.map(Mesh::getVertexCount) return supplier.map(Mesh::getVertexCount)
.orElse(0); .orElse(0);

View file

@ -19,6 +19,12 @@ public class TessellatedModel implements Model {
return meshes; return meshes;
} }
@Override
public void delete() {
meshes.values()
.forEach(Mesh::close);
}
public boolean isShadeSeparated() { public boolean isShadeSeparated() {
return shadeSeparated; return shadeSeparated;
} }

View file

@ -15,14 +15,12 @@ public class FogProvider extends UniformProvider {
} }
public void update() { public void update() {
if (buffer == null) { if (ptr == MemoryUtil.NULL) {
return; return;
} }
var color = RenderSystem.getShaderFogColor(); var color = RenderSystem.getShaderFogColor();
long ptr = MemoryUtil.memAddress(buffer);
MemoryUtil.memPutFloat(ptr, color[0]); MemoryUtil.memPutFloat(ptr, color[0]);
MemoryUtil.memPutFloat(ptr + 4, color[1]); MemoryUtil.memPutFloat(ptr + 4, color[1]);
MemoryUtil.memPutFloat(ptr + 8, color[2]); MemoryUtil.memPutFloat(ptr + 8, color[2]);

View file

@ -1,6 +1,5 @@
package com.jozufozu.flywheel.core.uniform; package com.jozufozu.flywheel.core.uniform;
import java.nio.ByteBuffer;
import java.util.BitSet; import java.util.BitSet;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
@ -11,8 +10,8 @@ import org.lwjgl.system.MemoryUtil;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.jozufozu.flywheel.api.uniform.UniformProvider; import com.jozufozu.flywheel.api.uniform.UniformProvider;
import com.jozufozu.flywheel.backend.gl.buffer.GlBufferType; import com.jozufozu.flywheel.backend.gl.buffer.GlBufferType;
import com.jozufozu.flywheel.backend.gl.buffer.MappedGlBuffer; import com.jozufozu.flywheel.backend.gl.buffer.GlBuffer;
import com.jozufozu.flywheel.backend.memory.FlwMemoryTracker; import com.jozufozu.flywheel.backend.memory.MemoryBlock;
import com.jozufozu.flywheel.core.ComponentRegistry; import com.jozufozu.flywheel.core.ComponentRegistry;
import com.jozufozu.flywheel.util.RenderMath; import com.jozufozu.flywheel.util.RenderMath;
@ -33,13 +32,13 @@ public class UniformBuffer {
return instance; return instance;
} }
private final MappedGlBuffer buffer; private final GlBuffer buffer;
private final ByteBuffer data; private final MemoryBlock data;
private final BitSet changedBytes; private final BitSet changedBytes;
private UniformBuffer() { private UniformBuffer() {
buffer = new MappedGlBuffer(GlBufferType.UNIFORM_BUFFER); buffer = new GlBuffer(GlBufferType.UNIFORM_BUFFER);
Collection<UniformProvider> providers = ComponentRegistry.getAllUniformProviders(); Collection<UniformProvider> providers = ComponentRegistry.getAllUniformProviders();
@ -57,7 +56,7 @@ public class UniformBuffer {
allocatedProviders = builder.build(); allocatedProviders = builder.build();
data = FlwMemoryTracker.mallocBuffer(totalBytes); data = MemoryBlock.mallocTracked(totalBytes);
changedBytes = new BitSet(totalBytes); changedBytes = new BitSet(totalBytes);
for (Allocated p : allocatedProviders) { for (Allocated p : allocatedProviders) {
@ -108,8 +107,8 @@ public class UniformBuffer {
changedBytes.set(offset, offset + size); changedBytes.set(offset, offset + size);
} }
private void updatePtr(ByteBuffer bufferBase) { private void updatePtr(MemoryBlock bufferBase) {
provider.updatePtr(MemoryUtil.memSlice(bufferBase, offset, size), this); provider.updatePtr(bufferBase.ptr() + offset, this);
} }
public UniformProvider provider() { public UniformProvider provider() {

View file

@ -31,7 +31,7 @@ public class ViewProvider extends UniformProvider {
} }
public void update(RenderContext context) { public void update(RenderContext context) {
if (buffer == null) { if (ptr == MemoryUtil.NULL) {
return; return;
} }
@ -52,8 +52,6 @@ public class ViewProvider extends UniformProvider {
var vp = context.viewProjection().copy(); var vp = context.viewProjection().copy();
vp.multiplyWithTranslation(-camX, -camY, -camZ); vp.multiplyWithTranslation(-camX, -camY, -camZ);
long ptr = MemoryUtil.memAddress(buffer);
MatrixWrite.writeUnsafe(vp, ptr); MatrixWrite.writeUnsafe(vp, ptr);
MemoryUtil.memPutFloat(ptr + 64, camX); MemoryUtil.memPutFloat(ptr + 64, camX);
MemoryUtil.memPutFloat(ptr + 68, camY); MemoryUtil.memPutFloat(ptr + 68, camY);

View file

@ -7,6 +7,7 @@ import com.jozufozu.flywheel.backend.Backend;
import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher; import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher;
import com.jozufozu.flywheel.backend.memory.FlwMemoryTracker; import com.jozufozu.flywheel.backend.memory.FlwMemoryTracker;
import com.jozufozu.flywheel.light.LightUpdater; import com.jozufozu.flywheel.light.LightUpdater;
import com.jozufozu.flywheel.util.StringUtil;
import com.jozufozu.flywheel.util.WorldAttached; import com.jozufozu.flywheel.util.WorldAttached;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
@ -24,7 +25,7 @@ public class ForgeEvents {
InstancedRenderDispatcher.getDebugString(debug); InstancedRenderDispatcher.getDebugString(debug);
debug.add("Memory Usage: CPU: " + FlwMemoryTracker.getCPUMemory() / 1024 + "KiB GPU: " + FlwMemoryTracker.getGPUMemory() / 1024 + "KiB"); debug.add("Memory Usage: CPU: " + StringUtil.formatBytes(FlwMemoryTracker.getCPUMemory()) + ", GPU: " + StringUtil.formatBytes(FlwMemoryTracker.getGPUMemory()));
} }
} }

View file

@ -19,17 +19,29 @@ import com.jozufozu.flywheel.backend.memory.FlwMemoryTracker;
public class StringUtil { public class StringUtil {
private static final NumberFormat timeFormat = new DecimalFormat("#0.000"); private static final NumberFormat THREE_DECIMAL_PLACES = new DecimalFormat("#0.000");
public static String formatBytes(long bytes) {
if (bytes < 1024) {
return bytes + " B";
} else if (bytes < 1024 * 1024) {
return THREE_DECIMAL_PLACES.format(bytes / 1024f) + " KB";
} else if (bytes < 1024 * 1024 * 1024) {
return THREE_DECIMAL_PLACES.format(bytes / 1024f / 1024f) + " MB";
} else {
return THREE_DECIMAL_PLACES.format(bytes / 1024f / 1024f / 1024f) + " GB";
}
}
public static String formatTime(long ns) { public static String formatTime(long ns) {
if (ns < 1000) { if (ns < 1000) {
return ns + " ns"; return ns + " ns";
} else if (ns < 1000000) { } else if (ns < 1000000) {
return timeFormat.format(ns / 1000.) + " μs"; return THREE_DECIMAL_PLACES.format(ns / 1000f) + " μs";
} else if (ns < 1000000000) { } else if (ns < 1000000000) {
return timeFormat.format(ns / 1000000.) + " ms"; return THREE_DECIMAL_PLACES.format(ns / 1000000f) + " ms";
} else { } else {
return timeFormat.format(ns / 1000000000.) + " s"; return THREE_DECIMAL_PLACES.format(ns / 1000000000f) + " s";
} }
} }

View file

@ -36,7 +36,16 @@ public class WorldAttached<T> {
i.remove(); i.remove();
} else { } else {
// Prevent leaks // Prevent leaks
map.remove(world); Object attached = map.remove(world);
// No, *really* prevent leaks
if (attached instanceof AutoCloseable closeable) {
try {
closeable.close();
} catch (Exception ignored) {
}
}
} }
} }
} }