161 lines
4.1 KiB
Java
161 lines
4.1 KiB
Java
package com.mojang.blaze3d.vertex;
|
|
|
|
import com.mojang.jtracy.MemoryPool;
|
|
import com.mojang.jtracy.TracyClient;
|
|
import com.mojang.logging.LogUtils;
|
|
import java.nio.ByteBuffer;
|
|
import net.fabricmc.api.EnvType;
|
|
import net.fabricmc.api.Environment;
|
|
import org.jetbrains.annotations.Nullable;
|
|
import org.lwjgl.system.MemoryUtil;
|
|
import org.lwjgl.system.MemoryUtil.MemoryAllocator;
|
|
import org.slf4j.Logger;
|
|
|
|
@Environment(EnvType.CLIENT)
|
|
public class ByteBufferBuilder implements AutoCloseable {
|
|
private static final MemoryPool MEMORY_POOL = TracyClient.createMemoryPool("ByteBufferBuilder");
|
|
private static final Logger LOGGER = LogUtils.getLogger();
|
|
private static final MemoryAllocator ALLOCATOR = MemoryUtil.getAllocator(false);
|
|
private static final int MAX_GROWTH_SIZE = 2097152;
|
|
private static final int BUFFER_FREED_GENERATION = -1;
|
|
long pointer;
|
|
private int capacity;
|
|
private int writeOffset;
|
|
private int nextResultOffset;
|
|
private int resultCount;
|
|
private int generation;
|
|
|
|
public ByteBufferBuilder(int capacity) {
|
|
this.capacity = capacity;
|
|
this.pointer = ALLOCATOR.malloc(capacity);
|
|
MEMORY_POOL.malloc(this.pointer, capacity);
|
|
if (this.pointer == 0L) {
|
|
throw new OutOfMemoryError("Failed to allocate " + capacity + " bytes");
|
|
}
|
|
}
|
|
|
|
public long reserve(int bytes) {
|
|
int i = this.writeOffset;
|
|
int j = i + bytes;
|
|
this.ensureCapacity(j);
|
|
this.writeOffset = j;
|
|
return this.pointer + i;
|
|
}
|
|
|
|
private void ensureCapacity(int size) {
|
|
if (size > this.capacity) {
|
|
int i = Math.min(this.capacity, 2097152);
|
|
int j = Math.max(this.capacity + i, size);
|
|
this.resize(j);
|
|
}
|
|
}
|
|
|
|
private void resize(int newSize) {
|
|
MEMORY_POOL.free(this.pointer);
|
|
this.pointer = ALLOCATOR.realloc(this.pointer, newSize);
|
|
MEMORY_POOL.malloc(this.pointer, newSize);
|
|
LOGGER.debug("Needed to grow BufferBuilder buffer: Old size {} bytes, new size {} bytes.", this.capacity, newSize);
|
|
if (this.pointer == 0L) {
|
|
throw new OutOfMemoryError("Failed to resize buffer from " + this.capacity + " bytes to " + newSize + " bytes");
|
|
} else {
|
|
this.capacity = newSize;
|
|
}
|
|
}
|
|
|
|
@Nullable
|
|
public ByteBufferBuilder.Result build() {
|
|
this.checkOpen();
|
|
int i = this.nextResultOffset;
|
|
int j = this.writeOffset - i;
|
|
if (j == 0) {
|
|
return null;
|
|
} else {
|
|
this.nextResultOffset = this.writeOffset;
|
|
this.resultCount++;
|
|
return new ByteBufferBuilder.Result(i, j, this.generation);
|
|
}
|
|
}
|
|
|
|
public void clear() {
|
|
if (this.resultCount > 0) {
|
|
LOGGER.warn("Clearing BufferBuilder with unused batches");
|
|
}
|
|
|
|
this.discard();
|
|
}
|
|
|
|
public void discard() {
|
|
this.checkOpen();
|
|
if (this.resultCount > 0) {
|
|
this.discardResults();
|
|
this.resultCount = 0;
|
|
}
|
|
}
|
|
|
|
boolean isValid(int generation) {
|
|
return generation == this.generation;
|
|
}
|
|
|
|
void freeResult() {
|
|
if (--this.resultCount <= 0) {
|
|
this.discardResults();
|
|
}
|
|
}
|
|
|
|
private void discardResults() {
|
|
int i = this.writeOffset - this.nextResultOffset;
|
|
if (i > 0) {
|
|
MemoryUtil.memCopy(this.pointer + this.nextResultOffset, this.pointer, i);
|
|
}
|
|
|
|
this.writeOffset = i;
|
|
this.nextResultOffset = 0;
|
|
this.generation++;
|
|
}
|
|
|
|
public void close() {
|
|
if (this.pointer != 0L) {
|
|
MEMORY_POOL.free(this.pointer);
|
|
ALLOCATOR.free(this.pointer);
|
|
this.pointer = 0L;
|
|
this.generation = -1;
|
|
}
|
|
}
|
|
|
|
private void checkOpen() {
|
|
if (this.pointer == 0L) {
|
|
throw new IllegalStateException("Buffer has been freed");
|
|
}
|
|
}
|
|
|
|
@Environment(EnvType.CLIENT)
|
|
public class Result implements AutoCloseable {
|
|
private final int offset;
|
|
private final int capacity;
|
|
private final int generation;
|
|
private boolean closed;
|
|
|
|
Result(final int offset, final int capacity, final int generation) {
|
|
this.offset = offset;
|
|
this.capacity = capacity;
|
|
this.generation = generation;
|
|
}
|
|
|
|
public ByteBuffer byteBuffer() {
|
|
if (!ByteBufferBuilder.this.isValid(this.generation)) {
|
|
throw new IllegalStateException("Buffer is no longer valid");
|
|
} else {
|
|
return MemoryUtil.memByteBuffer(ByteBufferBuilder.this.pointer + this.offset, this.capacity);
|
|
}
|
|
}
|
|
|
|
public void close() {
|
|
if (!this.closed) {
|
|
this.closed = true;
|
|
if (ByteBufferBuilder.this.isValid(this.generation)) {
|
|
ByteBufferBuilder.this.freeResult();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|