minecraft-src/com/mojang/blaze3d/buffers/GpuBuffer.java
2025-07-04 02:49:36 +03:00

136 lines
3.9 KiB
Java

package com.mojang.blaze3d.buffers;
import com.mojang.blaze3d.platform.GlStateManager;
import com.mojang.jtracy.MemoryPool;
import com.mojang.jtracy.TracyClient;
import java.nio.ByteBuffer;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import org.jetbrains.annotations.Nullable;
@Environment(EnvType.CLIENT)
public class GpuBuffer implements AutoCloseable {
private static final MemoryPool MEMORY_POOl = TracyClient.createMemoryPool("GPU Buffers");
private final BufferType type;
private final BufferUsage usage;
private boolean closed;
private boolean initialized = false;
public final int handle;
public int size;
public GpuBuffer(BufferType type, BufferUsage usage, int size) {
this.type = type;
this.size = size;
this.usage = usage;
this.handle = GlStateManager._glGenBuffers();
}
public GpuBuffer(BufferType type, BufferUsage usage, ByteBuffer buffer) {
this(type, usage, buffer.remaining());
this.write(buffer, 0);
}
public void resize(int size) {
if (this.closed) {
throw new IllegalStateException("Buffer already closed");
} else {
if (this.initialized) {
MEMORY_POOl.free(this.handle);
}
this.size = size;
if (this.usage.writable) {
this.initialized = false;
} else {
this.bind();
GlStateManager._glBufferData(this.type.id, size, this.usage.id);
MEMORY_POOl.malloc(this.handle, size);
this.initialized = true;
}
}
}
public void write(ByteBuffer buffer, int offset) {
if (this.closed) {
throw new IllegalStateException("Buffer already closed");
} else if (!this.usage.writable) {
throw new IllegalStateException("Buffer is not writable");
} else {
int i = buffer.remaining();
if (i + offset > this.size) {
throw new IllegalArgumentException(
"Cannot write more data than this buffer can hold (attempting to write " + i + " bytes at offset " + offset + " to " + this.size + " size buffer)"
);
} else {
this.bind();
if (this.initialized) {
GlStateManager._glBufferSubData(this.type.id, offset, buffer);
} else if (offset == 0 && i == this.size) {
GlStateManager._glBufferData(this.type.id, buffer, this.usage.id);
MEMORY_POOl.malloc(this.handle, this.size);
this.initialized = true;
} else {
GlStateManager._glBufferData(this.type.id, this.size, this.usage.id);
GlStateManager._glBufferSubData(this.type.id, offset, buffer);
MEMORY_POOl.malloc(this.handle, this.size);
this.initialized = true;
}
}
}
}
@Nullable
public GpuBuffer.ReadView read() {
return this.read(0, this.size);
}
@Nullable
public GpuBuffer.ReadView read(int offset, int length) {
if (this.closed) {
throw new IllegalStateException("Buffer already closed");
} else if (!this.usage.readable) {
throw new IllegalStateException("Buffer is not readable");
} else if (offset + length > this.size) {
throw new IllegalArgumentException(
"Cannot read more data than this buffer can hold (attempting to read " + length + " bytes at offset " + offset + " from " + this.size + " size buffer)"
);
} else {
this.bind();
ByteBuffer byteBuffer = GlStateManager._glMapBufferRange(this.type.id, offset, length, 1);
return byteBuffer == null ? null : new GpuBuffer.ReadView(this.type.id, byteBuffer);
}
}
public void close() {
if (!this.closed) {
this.closed = true;
GlStateManager._glDeleteBuffers(this.handle);
if (this.initialized) {
MEMORY_POOl.free(this.handle);
}
}
}
public void bind() {
GlStateManager._glBindBuffer(this.type.id, this.handle);
}
@Environment(EnvType.CLIENT)
public static class ReadView implements AutoCloseable {
private final int target;
private final ByteBuffer data;
protected ReadView(int target, ByteBuffer data) {
this.target = target;
this.data = data;
}
public ByteBuffer data() {
return this.data;
}
public void close() {
GlStateManager._glUnmapBuffer(this.target);
}
}
}