Merge branch '1.18/next' into 1.18/culling

# Conflicts:
#	src/main/java/com/jozufozu/flywheel/api/struct/StructType.java
#	src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/GPUInstancer.java
#	src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/MeshPool.java
#	src/main/java/com/jozufozu/flywheel/core/hardcoded/ModelPart.java
#	src/main/java/com/jozufozu/flywheel/core/model/Mesh.java
#	src/main/java/com/jozufozu/flywheel/core/model/ModelUtil.java
#	src/main/java/com/jozufozu/flywheel/core/model/SimpleMesh.java
#	src/main/java/com/jozufozu/flywheel/core/structs/model/TransformedWriterUnsafe.java
#	src/main/java/com/jozufozu/flywheel/core/structs/oriented/OrientedType.java
#	src/main/java/com/jozufozu/flywheel/core/structs/transformed/TransformedStorageWriter.java
#	src/main/java/com/jozufozu/flywheel/core/structs/transformed/TransformedType.java
#	src/main/java/com/jozufozu/flywheel/core/uniform/ViewProvider.java
This commit is contained in:
Jozufozu 2022-08-14 23:16:18 -07:00
commit de09331e9b
90 changed files with 1717 additions and 1802 deletions

View file

@ -22,7 +22,7 @@ public interface Material {
VertexTransformer getVertexTransformer(); VertexTransformer getVertexTransformer();
public interface VertexTransformer { interface VertexTransformer {
void transform(MutableVertexList vertexList, ClientLevel level); void transform(MutableVertexList vertexList, ClientLevel level);
} }
} }

View file

@ -1,7 +1,5 @@
package com.jozufozu.flywheel.api.struct; package com.jozufozu.flywheel.api.struct;
import java.nio.ByteBuffer;
import com.jozufozu.flywheel.api.instancer.InstancedPart; import com.jozufozu.flywheel.api.instancer.InstancedPart;
import com.jozufozu.flywheel.api.vertex.MutableVertexList; import com.jozufozu.flywheel.api.vertex.MutableVertexList;
import com.jozufozu.flywheel.core.layout.BufferLayout; import com.jozufozu.flywheel.core.layout.BufferLayout;
@ -25,22 +23,17 @@ public interface StructType<S extends InstancedPart> {
*/ */
BufferLayout getLayout(); BufferLayout getLayout();
/** StructWriter<S> getWriter();
* Create a {@link StructWriter} that will consume instances of S and write them to the given buffer.
*
* @param backing The buffer that the StructWriter will write to.
*/
StructWriter<S> getWriter(ByteBuffer backing);
FileResolution getInstanceShader(); FileResolution getInstanceShader();
VertexTransformer<? extends S> getVertexTransformer(); VertexTransformer<S> getVertexTransformer();
StorageBufferWriter<S> getStorageBufferWriter(); StorageBufferWriter<S> getStorageBufferWriter();
FileResolution getIndirectShader(); FileResolution getIndirectShader();
public interface VertexTransformer<S extends InstancedPart> { interface VertexTransformer<S extends InstancedPart> {
void transform(MutableVertexList vertexList, S struct, ClientLevel level); void transform(MutableVertexList vertexList, S struct, ClientLevel level);
} }

View file

@ -1,17 +1,13 @@
package com.jozufozu.flywheel.api.struct; package com.jozufozu.flywheel.api.struct;
/** import com.jozufozu.flywheel.api.instancer.InstancedPart;
* StructWriters can quickly consume many instances of S and write them to some backing buffer.
*/
public interface StructWriter<S> {
/** /**
* Write the given struct to the backing array. * StructWriters can quickly consume many instances of S and write them to some memory address.
*/ */
void write(S struct); public interface StructWriter<S extends InstancedPart> {
/** /**
* Seek to the given position. The next write will occur there. * Write the given struct to the given memory address.
*/ */
void seek(int pos); void write(long ptr, S struct);
} }

View file

@ -1,18 +1,16 @@
package com.jozufozu.flywheel.api.uniform; package com.jozufozu.flywheel.api.uniform;
import java.nio.ByteBuffer;
import com.jozufozu.flywheel.core.source.FileResolution; 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 getActualByteSize(); public abstract int getActualByteSize();
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

@ -0,0 +1,11 @@
package com.jozufozu.flywheel.api.vertex;
public interface ReusableVertexList extends MutableVertexList {
long ptr();
void ptr(long ptr);
void shiftPtr(int vertices);
void setVertexCount(int vertexCount);
}

View file

@ -44,6 +44,37 @@ public interface VertexList extends PointSet {
float normalZ(int index); float normalZ(int index);
default void write(MutableVertexList dst, int srcIndex, int dstIndex) {
dst.x(dstIndex, x(srcIndex));
dst.y(dstIndex, y(srcIndex));
dst.z(dstIndex, z(srcIndex));
dst.r(dstIndex, r(srcIndex));
dst.g(dstIndex, g(srcIndex));
dst.b(dstIndex, b(srcIndex));
dst.a(dstIndex, a(srcIndex));
dst.u(dstIndex, u(srcIndex));
dst.v(dstIndex, v(srcIndex));
dst.overlay(dstIndex, overlay(srcIndex));
dst.light(dstIndex, light(srcIndex));
dst.normalX(dstIndex, normalX(srcIndex));
dst.normalY(dstIndex, normalY(srcIndex));
dst.normalZ(dstIndex, normalZ(srcIndex));
}
default void write(MutableVertexList dst, int srcStartIndex, int dstStartIndex, int vertexCount) {
for (int i = 0; i < vertexCount; i++) {
write(dst, srcStartIndex + i, dstStartIndex + i);
}
}
default void writeAll(MutableVertexList dst) {
write(dst, 0, 0, getVertexCount());
}
int getVertexCount(); int getVertexCount();
default boolean isEmpty() { default boolean isEmpty() {

View file

@ -0,0 +1,5 @@
package com.jozufozu.flywheel.api.vertex;
public interface VertexListProvider {
ReusableVertexList createVertexList();
}

View file

@ -1,38 +1,18 @@
package com.jozufozu.flywheel.api.vertex; package com.jozufozu.flywheel.api.vertex;
import java.nio.ByteBuffer;
import com.jozufozu.flywheel.core.layout.BufferLayout; import com.jozufozu.flywheel.core.layout.BufferLayout;
import com.jozufozu.flywheel.core.source.FileResolution; import com.jozufozu.flywheel.core.source.FileResolution;
/** /**
* A vertex type containing metadata about a specific vertex layout. * A vertex type containing metadata about a specific vertex layout.
*/ */
public interface VertexType { public interface VertexType extends VertexListProvider {
/** /**
* The layout of this type of vertex when buffered. * The layout of this type of vertex when buffered.
*/ */
BufferLayout getLayout(); BufferLayout getLayout();
/**
* Create a writer backed by the given ByteBuffer.
*
* <p>
* Implementors are encouraged to override the return type for ergonomics.
* </p>
*/
VertexWriter createWriter(ByteBuffer buffer);
/**
* Create a view of the given ByteBuffer as if it were already filled with vertices.
*
* <p>
* Implementors are encouraged to override the return type for ergonomics.
* </p>
*/
VertexList createReader(ByteBuffer buffer, int vertexCount);
FileResolution getLayoutShader(); FileResolution getLayoutShader();
default int getStride() { default int getStride() {

View file

@ -1,15 +0,0 @@
package com.jozufozu.flywheel.api.vertex;
public interface VertexWriter {
void writeVertex(VertexList list, int index);
void seek(long offset);
VertexList intoReader(int vertices);
default void writeVertexList(VertexList list) {
for (int i = 0; i < list.getVertexCount(); i++) {
this.writeVertex(list, i);
}
}
}

View file

@ -1,57 +0,0 @@
package com.jozufozu.flywheel.backend;
import java.lang.ref.Cleaner;
import java.nio.ByteBuffer;
import org.lwjgl.system.MemoryUtil;
public class FlywheelMemory {
public static final Cleaner CLEANER = Cleaner.create();
private static int gpuMemory = 0;
private static int cpuMemory = 0;
public static void _freeCPUMemory(long size) {
cpuMemory -= size;
}
public static void _allocCPUMemory(long size) {
cpuMemory += size;
}
public static void _freeGPUMemory(long size) {
gpuMemory -= size;
}
public static void _allocGPUMemory(long size) {
gpuMemory += size;
}
public static int getGPUMemory() {
return gpuMemory;
}
public static int getCPUMemory() {
return cpuMemory;
}
public static Cleaner.Cleanable track(Object owner, ByteBuffer buffer) {
return CLEANER.register(owner, new Tracked(buffer));
}
public static class Tracked implements Runnable {
private final ByteBuffer buffer;
public Tracked(ByteBuffer buffer) {
this.buffer = buffer;
_allocCPUMemory(buffer.capacity());
}
public void run() {
_freeCPUMemory(buffer.capacity());
MemoryUtil.memFree(buffer);
}
}
}

View file

@ -1,49 +1,111 @@
package com.jozufozu.flywheel.backend.gl.buffer; package com.jozufozu.flywheel.backend.gl.buffer;
import java.nio.ByteBuffer; 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.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 +128,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

@ -8,7 +8,6 @@ import org.lwjgl.opengl.GL;
import org.lwjgl.opengl.GL20C; import org.lwjgl.opengl.GL20C;
import org.lwjgl.opengl.GLCapabilities; import org.lwjgl.opengl.GLCapabilities;
import org.lwjgl.system.MemoryStack; import org.lwjgl.system.MemoryStack;
import org.lwjgl.system.MemoryUtil;
import net.minecraft.Util; import net.minecraft.Util;
@ -78,7 +77,7 @@ public class GlCompat {
} }
/** /**
* Copied from: * Modified from:
* <br> <a href="https://github.com/grondag/canvas/commit/820bf754092ccaf8d0c169620c2ff575722d7d96">canvas</a> * <br> <a href="https://github.com/grondag/canvas/commit/820bf754092ccaf8d0c169620c2ff575722d7d96">canvas</a>
* *
* <p>Identical in function to {@link GL20C#glShaderSource(int, CharSequence)} but * <p>Identical in function to {@link GL20C#glShaderSource(int, CharSequence)} but
@ -90,18 +89,11 @@ public class GlCompat {
* <p>Hat tip to fewizz for the find and the fix. * <p>Hat tip to fewizz for the find and the fix.
*/ */
public static void safeShaderSource(int glId, CharSequence source) { public static void safeShaderSource(int glId, CharSequence source) {
final MemoryStack stack = MemoryStack.stackGet(); try (MemoryStack stack = MemoryStack.stackPush()) {
final int stackPointer = stack.getPointer(); final ByteBuffer sourceBuffer = stack.UTF8(source, true);
try {
final ByteBuffer sourceBuffer = MemoryUtil.memUTF8(source, true);
final PointerBuffer pointers = stack.mallocPointer(1); final PointerBuffer pointers = stack.mallocPointer(1);
pointers.put(sourceBuffer); pointers.put(sourceBuffer);
GL20C.nglShaderSource(glId, 1, pointers.address0(), 0); GL20C.nglShaderSource(glId, 1, pointers.address0(), 0);
org.lwjgl.system.APIUtil.apiArrayFree(pointers.address0(), 1);
} finally {
stack.setPointer(stackPointer);
} }
} }

View file

@ -7,7 +7,6 @@ import java.util.List;
import com.jozufozu.flywheel.api.instancer.InstancedPart; import com.jozufozu.flywheel.api.instancer.InstancedPart;
import com.jozufozu.flywheel.api.instancer.Instancer; import com.jozufozu.flywheel.api.instancer.Instancer;
import com.jozufozu.flywheel.api.struct.StructType; import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.api.struct.StructWriter;
public abstract class AbstractInstancer<D extends InstancedPart> implements Instancer<D> { public abstract class AbstractInstancer<D extends InstancedPart> implements Instancer<D> {
@ -114,22 +113,6 @@ public abstract class AbstractInstancer<D extends InstancedPart> implements Inst
return instanceData; return instanceData;
} }
protected void writeChangedUnchecked(StructWriter<D> writer) {
boolean sequential = true;
for (int i = 0; i < data.size(); i++) {
final D element = data.get(i);
if (element.checkDirtyAndClear()) {
if (!sequential) {
writer.seek(i);
}
writer.write(element);
sequential = true;
} else {
sequential = false;
}
}
}
public abstract void delete(); public abstract void delete();
@Override @Override

View file

@ -28,7 +28,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;
@ -148,4 +148,8 @@ public class InstanceWorld {
.forEach(entities::add); .forEach(entities::add);
} }
@Override
public void close() {
delete();
}
} }

View file

@ -13,9 +13,7 @@ import com.jozufozu.flywheel.backend.instancing.InstanceManager;
import com.jozufozu.flywheel.backend.instancing.TaskEngine; import com.jozufozu.flywheel.backend.instancing.TaskEngine;
import com.jozufozu.flywheel.core.RenderContext; import com.jozufozu.flywheel.core.RenderContext;
import com.jozufozu.flywheel.util.FlwUtil; import com.jozufozu.flywheel.util.FlwUtil;
import com.mojang.blaze3d.platform.Lighting;
import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.math.Matrix4f;
import net.minecraft.client.Camera; import net.minecraft.client.Camera;
import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.multiplayer.ClientLevel;
@ -68,19 +66,11 @@ public class BatchingEngine implements Engine {
@Override @Override
public void renderStage(TaskEngine taskEngine, RenderContext context, RenderStage stage) { public void renderStage(TaskEngine taskEngine, RenderContext context, RenderStage stage) {
// FIXME: properly support material stages // FIXME: properly support material stages
// This also breaks block outlines on batched block entities
if (stage != RenderStage.AFTER_FINAL_END_BATCH) { if (stage != RenderStage.AFTER_FINAL_END_BATCH) {
return; return;
} }
// FIXME: this probably breaks some vanilla stuff but it works much better for flywheel
Matrix4f mat = new Matrix4f();
mat.setIdentity();
if (context.level().effects().constantAmbientLight()) {
Lighting.setupNetherLevel(mat);
} else {
Lighting.setupLevel(mat);
}
batchTracker.endBatch(); batchTracker.endBatch();
} }

View file

@ -2,28 +2,35 @@ package com.jozufozu.flywheel.backend.instancing.batching;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import org.lwjgl.system.MemoryUtil; import com.jozufozu.flywheel.api.vertex.ReusableVertexList;
import com.jozufozu.flywheel.api.vertex.VertexListProvider;
import com.mojang.blaze3d.platform.MemoryTracker; import com.jozufozu.flywheel.backend.memory.MemoryBlock;
import com.jozufozu.flywheel.core.vertex.VertexListProviderRegistry;
import com.mojang.blaze3d.vertex.VertexFormat;
import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.RenderType;
/** /**
* A byte buffer that can be used to draw vertices through multiple {@link MutableVertexListImpl}s. * A byte buffer that can be used to draw vertices through multiple {@link ReusableVertexList}s.
* *
* The number of vertices needs to be known ahead of time. * The number of vertices needs to be known ahead of time.
*/ */
public class DrawBuffer { public class DrawBuffer {
private final RenderType parent; private final RenderType parent;
private final VertexFormatInfo formatInfo; private final VertexFormat format;
private final int stride;
private final VertexListProvider provider;
private MemoryBlock memory;
private ByteBuffer buffer;
private ByteBuffer backingBuffer;
private int expectedVertices; private int expectedVertices;
private long ptr;
public DrawBuffer(RenderType parent) { public DrawBuffer(RenderType parent) {
this.parent = parent; this.parent = parent;
formatInfo = new VertexFormatInfo(parent.format()); format = parent.format();
stride = format.getVertexSize();
provider = VertexListProviderRegistry.getOrInfer(format);
} }
/** /**
@ -33,29 +40,32 @@ public class DrawBuffer {
*/ */
public void prepare(int vertexCount) { public void prepare(int vertexCount) {
if (expectedVertices != 0) { if (expectedVertices != 0) {
throw new IllegalStateException("Already drawing"); throw new IllegalStateException("Already drawing!");
} }
this.expectedVertices = vertexCount; this.expectedVertices = vertexCount;
// Add one extra vertex to uphold the vanilla assumption that BufferBuilders have at least // Add one extra vertex to uphold the vanilla assumption that BufferBuilders have at least
// enough buffer space for one more vertex. Sodium checks for this extra space when popNextBuffer // enough buffer space for one more vertex. Rubidium checks for this extra space when popNextBuffer
// is called and reallocates the buffer if there is not space for one more vertex. // is called and reallocates the buffer if there is not space for one more vertex.
int byteSize = formatInfo.stride * (vertexCount + 1); int byteSize = stride * (vertexCount + 1);
if (backingBuffer == null) { if (memory == null) {
backingBuffer = MemoryTracker.create(byteSize); memory = MemoryBlock.malloc(byteSize);
} else if (byteSize > backingBuffer.capacity()) { buffer = memory.asBuffer();
backingBuffer = MemoryTracker.resize(backingBuffer, byteSize); } else if (byteSize > memory.size()) {
memory = memory.realloc(byteSize);
buffer = memory.asBuffer();
} }
backingBuffer.clear(); memory.clear();
ptr = MemoryUtil.memAddress(backingBuffer);
MemoryUtil.memSet(ptr, 0, byteSize);
} }
public MutableVertexListImpl slice(int startVertex, int vertexCount) { public ReusableVertexList slice(int startVertex, int vertexCount) {
return new MutableVertexListImpl(ptr + startVertex * formatInfo.stride, formatInfo, vertexCount); ReusableVertexList vertexList = provider.createVertexList();
vertexList.ptr(memory.ptr() + startVertex * stride);
vertexList.setVertexCount(vertexCount);
return vertexList;
} }
/** /**
@ -63,7 +73,8 @@ public class DrawBuffer {
* @param bufferBuilder The buffer builder to inject into. * @param bufferBuilder The buffer builder to inject into.
*/ */
public void inject(BufferBuilderExtension bufferBuilder) { public void inject(BufferBuilderExtension bufferBuilder) {
bufferBuilder.flywheel$injectForRender(backingBuffer, formatInfo.format, expectedVertices); buffer.clear();
bufferBuilder.flywheel$injectForRender(buffer, format, expectedVertices);
} }
public int getVertexCount() { public int getVertexCount() {
@ -78,11 +89,16 @@ 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.
*/ */
public void reset() { public void reset() {
this.expectedVertices = 0; this.expectedVertices = 0;
} }
public void free() {
buffer = null;
memory.free();
}
} }

View file

@ -1,206 +0,0 @@
package com.jozufozu.flywheel.backend.instancing.batching;
import org.lwjgl.system.MemoryUtil;
import com.jozufozu.flywheel.api.vertex.MutableVertexList;
import com.jozufozu.flywheel.util.RenderMath;
public class MutableVertexListImpl extends VertexFormatInfo implements MutableVertexList {
private final long anchorPtr;
private final int totalVertexCount;
private long ptr;
private int vertexCount;
public MutableVertexListImpl(long ptr, VertexFormatInfo formatInfo, int vertexCount) {
super(formatInfo);
anchorPtr = ptr;
totalVertexCount = vertexCount;
setFullRange();
}
public void setRange(int startVertex, int vertexCount) {
ptr = anchorPtr + startVertex * stride;
this.vertexCount = vertexCount;
}
public void setFullRange() {
ptr = anchorPtr;
vertexCount = totalVertexCount;
}
@Override
public float x(int index) {
if (positionOffset < 0) return 0;
return MemoryUtil.memGetFloat(ptr + index * stride + positionOffset);
}
@Override
public float y(int index) {
if (positionOffset < 0) return 0;
return MemoryUtil.memGetFloat(ptr + index * stride + positionOffset + 4);
}
@Override
public float z(int index) {
if (positionOffset < 0) return 0;
return MemoryUtil.memGetFloat(ptr + index * stride + positionOffset + 8);
}
@Override
public byte r(int index) {
if (colorOffset < 0) return 0;
return MemoryUtil.memGetByte(ptr + index * stride + colorOffset);
}
@Override
public byte g(int index) {
if (colorOffset < 0) return 0;
return MemoryUtil.memGetByte(ptr + index * stride + colorOffset + 1);
}
@Override
public byte b(int index) {
if (colorOffset < 0) return 0;
return MemoryUtil.memGetByte(ptr + index * stride + colorOffset + 2);
}
@Override
public byte a(int index) {
if (colorOffset < 0) return 0;
return MemoryUtil.memGetByte(ptr + index * stride + colorOffset + 3);
}
@Override
public float u(int index) {
if (textureOffset < 0) return 0;
return MemoryUtil.memGetFloat(ptr + index * stride + textureOffset);
}
@Override
public float v(int index) {
if (textureOffset < 0) return 0;
return MemoryUtil.memGetFloat(ptr + index * stride + textureOffset + 4);
}
@Override
public int overlay(int index) {
if (overlayOffset < 0) return 0;
return MemoryUtil.memGetInt(ptr + index * stride + overlayOffset);
}
@Override
public int light(int index) {
if (lightOffset < 0) return 0;
return MemoryUtil.memGetInt(ptr + index * stride + lightOffset);
}
@Override
public float normalX(int index) {
if (normalOffset < 0) return 0;
return RenderMath.f(MemoryUtil.memGetByte(ptr + index * stride + normalOffset));
}
@Override
public float normalY(int index) {
if (normalOffset < 0) return 0;
return RenderMath.f(MemoryUtil.memGetByte(ptr + index * stride + normalOffset + 1));
}
@Override
public float normalZ(int index) {
if (normalOffset < 0) return 0;
return RenderMath.f(MemoryUtil.memGetByte(ptr + index * stride + normalOffset + 2));
}
@Override
public int getVertexCount() {
return vertexCount;
}
@Override
public void x(int index, float x) {
if (positionOffset < 0) return;
MemoryUtil.memPutFloat(ptr + index * stride + positionOffset, x);
}
@Override
public void y(int index, float y) {
if (positionOffset < 0) return;
MemoryUtil.memPutFloat(ptr + index * stride + positionOffset + 4, y);
}
@Override
public void z(int index, float z) {
if (positionOffset < 0) return;
MemoryUtil.memPutFloat(ptr + index * stride + positionOffset + 8, z);
}
@Override
public void r(int index, byte r) {
if (colorOffset < 0) return;
MemoryUtil.memPutByte(ptr + index * stride + colorOffset, r);
}
@Override
public void g(int index, byte g) {
if (colorOffset < 0) return;
MemoryUtil.memPutByte(ptr + index * stride + colorOffset + 1, g);
}
@Override
public void b(int index, byte b) {
if (colorOffset < 0) return;
MemoryUtil.memPutByte(ptr + index * stride + colorOffset + 2, b);
}
@Override
public void a(int index, byte a) {
if (colorOffset < 0) return;
MemoryUtil.memPutByte(ptr + index * stride + colorOffset + 3, a);
}
@Override
public void u(int index, float u) {
if (textureOffset < 0) return;
MemoryUtil.memPutFloat(ptr + index * stride + textureOffset, u);
}
@Override
public void v(int index, float v) {
if (textureOffset < 0) return;
MemoryUtil.memPutFloat(ptr + index * stride + textureOffset + 4, v);
}
@Override
public void overlay(int index, int overlay) {
if (overlayOffset < 0) return;
MemoryUtil.memPutInt(ptr + index * stride + overlayOffset, overlay);
}
@Override
public void light(int index, int light) {
if (lightOffset < 0) return;
MemoryUtil.memPutInt(ptr + index * stride + lightOffset, light);
}
@Override
public void normalX(int index, float normalX) {
if (normalOffset < 0) return;
MemoryUtil.memPutByte(ptr + index * stride + normalOffset, RenderMath.nb(normalX));
}
@Override
public void normalY(int index, float normalY) {
if (normalOffset < 0) return;
MemoryUtil.memPutByte(ptr + index * stride + normalOffset + 1, RenderMath.nb(normalY));
}
@Override
public void normalZ(int index, float normalZ) {
if (normalOffset < 0) return;
MemoryUtil.memPutByte(ptr + index * stride + normalOffset + 2, RenderMath.nb(normalZ));
}
}

View file

@ -5,9 +5,8 @@ import java.util.List;
import com.jozufozu.flywheel.api.instancer.InstancedPart; import com.jozufozu.flywheel.api.instancer.InstancedPart;
import com.jozufozu.flywheel.api.material.Material; import com.jozufozu.flywheel.api.material.Material;
import com.jozufozu.flywheel.api.struct.StructType; import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.api.struct.StructType.VertexTransformer;
import com.jozufozu.flywheel.api.vertex.MutableVertexList; import com.jozufozu.flywheel.api.vertex.MutableVertexList;
import com.jozufozu.flywheel.api.vertex.VertexList; import com.jozufozu.flywheel.api.vertex.ReusableVertexList;
import com.jozufozu.flywheel.backend.instancing.TaskEngine; import com.jozufozu.flywheel.backend.instancing.TaskEngine;
import com.jozufozu.flywheel.core.model.Mesh; import com.jozufozu.flywheel.core.model.Mesh;
import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.PoseStack;
@ -49,80 +48,50 @@ public class TransformSet<D extends InstancedPart> {
int start = Math.max(instances, 0); int start = Math.max(instances, 0);
int vertexCount = mesh.getVertexCount() * (end - start); int vertexCount = mesh.getVertexCount() * (end - start);
MutableVertexListImpl sub = buffer.slice(startVertex, vertexCount); ReusableVertexList sub = buffer.slice(startVertex, vertexCount);
startVertex += vertexCount; startVertex += vertexCount;
pool.submit(() -> drawRange(sub, start, end, stack, level)); pool.submit(() -> drawRange(sub, start, end, stack, level));
} }
} }
private void drawRange(MutableVertexListImpl vertexList, int from, int to, PoseStack stack, ClientLevel level) { private void drawRange(ReusableVertexList vertexList, int from, int to, PoseStack stack, ClientLevel level) {
drawList(vertexList, instancer.getRange(from, to), stack, level); drawList(vertexList, instancer.getRange(from, to), stack, level);
} }
void drawAll(MutableVertexListImpl vertexList, PoseStack stack, ClientLevel level) { void drawAll(ReusableVertexList vertexList, PoseStack stack, ClientLevel level) {
drawList(vertexList, instancer.getAll(), stack, level); drawList(vertexList, instancer.getAll(), stack, level);
} }
private void drawList(MutableVertexListImpl vertexList, List<D> list, PoseStack stack, ClientLevel level) { private void drawList(ReusableVertexList vertexList, List<D> list, PoseStack stack, ClientLevel level) {
int startVertex = 0; long anchorPtr = vertexList.ptr();
int meshVertexCount = mesh.getVertexCount(); int totalVertexCount = vertexList.getVertexCount();
VertexList meshReader = mesh.getReader(); int meshVertexCount = mesh.getVertexCount();
@SuppressWarnings("unchecked") vertexList.setVertexCount(meshVertexCount);
StructType.VertexTransformer<D> structVertexTransformer = (VertexTransformer<D>) instancer.type.getVertexTransformer();
StructType.VertexTransformer<D> structVertexTransformer = instancer.type.getVertexTransformer();
for (D d : list) { for (D d : list) {
vertexList.setRange(startVertex, meshVertexCount); mesh.write(vertexList);
writeMesh(vertexList, meshReader);
structVertexTransformer.transform(vertexList, d, level); structVertexTransformer.transform(vertexList, d, level);
startVertex += meshVertexCount; vertexList.shiftPtr(meshVertexCount);
} }
vertexList.setFullRange(); vertexList.ptr(anchorPtr);
vertexList.setVertexCount(totalVertexCount);
material.getVertexTransformer().transform(vertexList, level); material.getVertexTransformer().transform(vertexList, level);
applyPoseStack(vertexList, stack, false); applyPoseStack(vertexList, stack);
} }
// TODO: remove this private static void applyPoseStack(MutableVertexList vertexList, PoseStack stack) {
// The VertexWriter API and VertexFormat conversion needs to be rewritten to make this unnecessary
private static void writeMesh(MutableVertexList vertexList, VertexList meshReader) {
for (int i = 0; i < meshReader.getVertexCount(); i++) {
vertexList.x(i, meshReader.x(i));
vertexList.y(i, meshReader.y(i));
vertexList.z(i, meshReader.z(i));
vertexList.r(i, meshReader.r(i));
vertexList.g(i, meshReader.g(i));
vertexList.b(i, meshReader.b(i));
vertexList.a(i, meshReader.a(i));
vertexList.u(i, meshReader.u(i));
vertexList.v(i, meshReader.v(i));
vertexList.overlay(i, meshReader.overlay(i));
vertexList.light(i, meshReader.light(i));
vertexList.normalX(i, meshReader.normalX(i));
vertexList.normalY(i, meshReader.normalY(i));
vertexList.normalZ(i, meshReader.normalZ(i));
}
}
private static void applyPoseStack(MutableVertexList vertexList, PoseStack stack, boolean applyNormalMatrix) {
Vector4f pos = new Vector4f(); Vector4f pos = new Vector4f();
Vector3f normal = new Vector3f(); Vector3f normal = new Vector3f();
Matrix4f modelMatrix = stack.last().pose(); Matrix4f modelMatrix = stack.last().pose();
Matrix3f normalMatrix; Matrix3f normalMatrix = stack.last().normal();
if (applyNormalMatrix) {
normalMatrix = stack.last().normal();
} else {
normalMatrix = null;
}
for (int i = 0; i < vertexList.getVertexCount(); i++) { for (int i = 0; i < vertexList.getVertexCount(); i++) {
pos.set( pos.set(
@ -136,7 +105,6 @@ public class TransformSet<D extends InstancedPart> {
vertexList.y(i, pos.y()); vertexList.y(i, pos.y());
vertexList.z(i, pos.z()); vertexList.z(i, pos.z());
if (applyNormalMatrix) {
normal.set( normal.set(
vertexList.normalX(i), vertexList.normalX(i),
vertexList.normalY(i), vertexList.normalY(i),
@ -149,7 +117,6 @@ public class TransformSet<D extends InstancedPart> {
vertexList.normalZ(i, normal.z()); vertexList.normalZ(i, normal.z());
} }
} }
}
public int getTotalVertexCount() { public int getTotalVertexCount() {
return mesh.getVertexCount() * instancer.getInstanceCount(); return mesh.getVertexCount() * instancer.getInstanceCount();

View file

@ -10,8 +10,8 @@ import com.jozufozu.flywheel.api.instancer.InstancerFactory;
import com.jozufozu.flywheel.api.instancer.InstancerManager; import com.jozufozu.flywheel.api.instancer.InstancerManager;
import com.jozufozu.flywheel.backend.instancing.AbstractInstance; import com.jozufozu.flywheel.backend.instancing.AbstractInstance;
import com.jozufozu.flywheel.core.structs.StructTypes; import com.jozufozu.flywheel.core.structs.StructTypes;
import com.jozufozu.flywheel.core.structs.model.TransformedPart;
import com.jozufozu.flywheel.core.structs.oriented.OrientedPart; import com.jozufozu.flywheel.core.structs.oriented.OrientedPart;
import com.jozufozu.flywheel.core.structs.transformed.TransformedPart;
import com.jozufozu.flywheel.util.box.GridAlignedBB; import com.jozufozu.flywheel.util.box.GridAlignedBB;
import com.jozufozu.flywheel.util.box.ImmutableBox; import com.jozufozu.flywheel.util.box.ImmutableBox;
import com.jozufozu.flywheel.util.joml.FrustumIntersection; import com.jozufozu.flywheel.util.joml.FrustumIntersection;

View file

@ -13,6 +13,7 @@ import org.lwjgl.system.MemoryUtil;
import com.jozufozu.flywheel.api.vertex.VertexType; import com.jozufozu.flywheel.api.vertex.VertexType;
import com.jozufozu.flywheel.backend.instancing.instancing.ElementBuffer; import com.jozufozu.flywheel.backend.instancing.instancing.ElementBuffer;
import com.jozufozu.flywheel.backend.memory.MemoryBlock;
import com.jozufozu.flywheel.core.model.Mesh; import com.jozufozu.flywheel.core.model.Mesh;
public class IndirectMeshPool { public class IndirectMeshPool {
@ -23,7 +24,7 @@ public class IndirectMeshPool {
final VertexType vertexType; final VertexType vertexType;
final int vbo; final int vbo;
private final ByteBuffer clientStorage; private final MemoryBlock clientStorage;
private boolean dirty; private boolean dirty;
@ -35,7 +36,7 @@ public class IndirectMeshPool {
vbo = glCreateBuffers(); vbo = glCreateBuffers();
var byteCapacity = type.byteOffset(vertexCapacity); var byteCapacity = type.byteOffset(vertexCapacity);
glNamedBufferStorage(vbo, byteCapacity, GL_DYNAMIC_STORAGE_BIT); glNamedBufferStorage(vbo, byteCapacity, GL_DYNAMIC_STORAGE_BIT);
clientStorage = MemoryUtil.memAlloc(byteCapacity); clientStorage = MemoryBlock.malloc(byteCapacity);
} }
/** /**
@ -65,24 +66,25 @@ public class IndirectMeshPool {
} }
dirty = false; dirty = false;
final long ptr = clientStorage.ptr();
int byteIndex = 0; int byteIndex = 0;
int baseVertex = 0; int baseVertex = 0;
for (BufferedMesh model : meshList) { for (BufferedMesh model : meshList) {
model.byteIndex = byteIndex; model.byteIndex = byteIndex;
model.baseVertex = baseVertex; model.baseVertex = baseVertex;
model.buffer(clientStorage); model.buffer(ptr);
byteIndex += model.getByteSize(); byteIndex += model.getByteSize();
baseVertex += model.mesh.getVertexCount(); baseVertex += model.mesh.getVertexCount();
} }
clientStorage.rewind(); nglNamedBufferSubData(vbo, 0, byteIndex, ptr);
glNamedBufferSubData(vbo, 0, clientStorage);
} }
public void delete() { public void delete() {
clientStorage.free();
glDeleteBuffers(vbo); glDeleteBuffers(vbo);
meshes.clear(); meshes.clear();
meshList.clear(); meshList.clear();
@ -101,10 +103,8 @@ public class IndirectMeshPool {
vertexCount = mesh.getVertexCount(); vertexCount = mesh.getVertexCount();
} }
private void buffer(ByteBuffer buffer) { private void buffer(long ptr) {
var writer = IndirectMeshPool.this.vertexType.createWriter(buffer); this.mesh.write(ptr + byteIndex);
writer.seek(this.byteIndex);
writer.writeVertexList(this.mesh.getReader());
} }
public int getByteSize() { public int getByteSize() {

View file

@ -6,12 +6,12 @@ import java.util.Set;
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.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.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.instancing.AbstractInstancer; import com.jozufozu.flywheel.backend.instancing.AbstractInstancer;
import com.jozufozu.flywheel.core.layout.BufferLayout; import com.jozufozu.flywheel.core.layout.BufferLayout;
@ -42,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);
} }
@ -88,7 +88,16 @@ public class GPUInstancer<D extends InstancedPart> extends AbstractInstancer<D>
buf.clear(clearStart, clearLength); buf.clear(clearStart, clearLength);
if (size > 0) { if (size > 0) {
writeChangedUnchecked(structType.getWriter(buf.unwrap())); final long ptr = buf.getPtr();
final long stride = structType.getLayout().getStride();
final StructWriter<D> writer = structType.getWriter();
for (int i = 0; i < size; i++) {
final D element = data.get(i);
if (element.checkDirtyAndClear()) {
writer.write(ptr + i * stride, element);
}
}
} }
} catch (Exception e) { } catch (Exception e) {
Flywheel.LOGGER.error("Error updating GPUInstancer:", e); Flywheel.LOGGER.error("Error updating GPUInstancer:", e);

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;
@ -18,7 +17,7 @@ import com.jozufozu.flywheel.backend.gl.array.GlVertexArray;
import com.jozufozu.flywheel.backend.gl.buffer.GlBuffer; 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.core.layout.BufferLayout;
import com.jozufozu.flywheel.core.model.Mesh; import com.jozufozu.flywheel.core.model.Mesh;
import com.jozufozu.flywheel.core.vertex.Formats; import com.jozufozu.flywheel.core.vertex.Formats;
import com.jozufozu.flywheel.event.ReloadRenderersEvent; import com.jozufozu.flywheel.event.ReloadRenderersEvent;
@ -27,7 +26,7 @@ public class MeshPool {
private static MeshPool allocator; private static MeshPool allocator;
static MeshPool getInstance() { public static MeshPool getInstance() {
if (allocator == null) { if (allocator == null) {
allocator = new MeshPool(); allocator = new MeshPool();
} }
@ -58,7 +57,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);
} }
@ -71,9 +70,8 @@ public class MeshPool {
*/ */
public BufferedMesh alloc(Mesh mesh) { public BufferedMesh alloc(Mesh mesh) {
return meshes.computeIfAbsent(mesh, m -> { return meshes.computeIfAbsent(mesh, m -> {
// FIXME: culling experiments fixing everything to Formats.BLOCK BufferedMesh bufferedModel = new BufferedMesh(m, byteSize, vertexCount);
BufferedMesh bufferedModel = new BufferedMesh(Formats.BLOCK, m, byteSize, vertexCount); byteSize += m.size();
byteSize += bufferedModel.getByteSize();
vertexCount += bufferedModel.mesh.getVertexCount(); vertexCount += bufferedModel.mesh.getVertexCount();
allBuffered.add(bufferedModel); allBuffered.add(bufferedModel);
pendingUpload.add(bufferedModel); pendingUpload.add(bufferedModel);
@ -147,7 +145,7 @@ 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;
int baseVertex = 0; int baseVertex = 0;
@ -155,26 +153,26 @@ public class MeshPool {
model.byteIndex = byteIndex; model.byteIndex = byteIndex;
model.baseVertex = baseVertex; model.baseVertex = baseVertex;
model.buffer(buffer); model.buffer(ptr);
byteIndex += model.getByteSize(); byteIndex += model.getByteSize();
baseVertex += model.mesh.getVertexCount(); baseVertex += model.mesh.getVertexCount();
} }
} catch (Exception e) { } catch (Exception e) {
Flywheel.LOGGER.error("Error uploading pooled models:", e); Flywheel.LOGGER.error("Error uploading pooled meshes:", e);
} }
} }
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);
} }
pendingUpload.clear(); pendingUpload.clear();
} catch (Exception e) { } catch (Exception e) {
Flywheel.LOGGER.error("Error uploading pooled models:", e); Flywheel.LOGGER.error("Error uploading pooled meshes:", e);
} }
} }
@ -207,14 +205,6 @@ public class MeshPool {
this.type = mesh.getVertexType(); this.type = mesh.getVertexType();
} }
public BufferedMesh(VertexType type, Mesh mesh, long byteIndex, int baseVertex) {
this.mesh = mesh;
this.byteIndex = byteIndex;
this.baseVertex = baseVertex;
this.ebo = mesh.createEBO();
this.type = type;
}
public void drawCall(GlVertexArray vao) { public void drawCall(GlVertexArray vao) {
drawInstances(vao, 1); drawInstances(vao, 1);
} }
@ -228,7 +218,7 @@ public class MeshPool {
} }
private boolean hasAnythingToRender() { private boolean hasAnythingToRender() {
return mesh.getVertexCount() <= 0 || isDeleted(); return mesh.isEmpty() || isDeleted();
} }
private void draw(int instanceCount) { private void draw(int instanceCount) {
@ -258,10 +248,8 @@ public class MeshPool {
this.deleted = true; this.deleted = true;
} }
private void buffer(ByteBuffer buffer) { private void buffer(long ptr) {
var writer = type.createWriter(buffer); this.mesh.write(ptr + byteIndex);
writer.seek(this.byteIndex);
writer.writeVertexList(this.mesh.getReader());
this.boundTo.clear(); this.boundTo.clear();
this.gpuResident = true; this.gpuResident = true;

View file

@ -0,0 +1,111 @@
package com.jozufozu.flywheel.backend.memory;
import java.lang.ref.Cleaner;
import java.nio.ByteBuffer;
import org.lwjgl.system.MemoryUtil;
public class FlwMemoryTracker {
static final Cleaner CLEANER = Cleaner.create();
private static long cpuMemory = 0;
private static long gpuMemory = 0;
public static long malloc(long size) {
long ptr = MemoryUtil.nmemAlloc(size);
if (ptr == MemoryUtil.NULL) {
throw new OutOfMemoryError("Failed to allocate " + size + " bytes");
}
return ptr;
}
/**
* @deprecated Use {@link MemoryBlock#malloc(long)} or {@link MemoryBlock#mallocTracked(long)} and
* {@link MemoryBlock#asBuffer()} instead. This method should only be used if specifically a {@linkplain ByteBuffer} is needed and it is
* short-lived.
*/
@Deprecated
public static ByteBuffer mallocBuffer(int size) {
ByteBuffer buffer = MemoryUtil.memByteBuffer(malloc(size), size);
_allocCPUMemory(buffer.capacity());
return buffer;
}
public static long calloc(long num, long size) {
long ptr = MemoryUtil.nmemCalloc(num, size);
if (ptr == MemoryUtil.NULL) {
throw new OutOfMemoryError("Failed to allocate " + num + " elements of size " + size + " bytes");
}
return ptr;
}
/**
* @deprecated Use {@link MemoryBlock#calloc(long, long)} or {@link MemoryBlock#callocTracked(long, long)} and
* {@link MemoryBlock#asBuffer()} instead. This method should only be used if specifically a {@linkplain ByteBuffer} is needed and it is
* short-lived.
*/
@Deprecated
public static ByteBuffer callocBuffer(int num, int size) {
ByteBuffer buffer = MemoryUtil.memByteBuffer(calloc(num, size), num * size);
_allocCPUMemory(buffer.capacity());
return buffer;
}
public static long realloc(long ptr, long size) {
ptr = MemoryUtil.nmemRealloc(ptr, size);
if (ptr == MemoryUtil.NULL) {
throw new OutOfMemoryError("Failed to reallocate " + size + " bytes for address 0x" + Long.toHexString(ptr));
}
return ptr;
}
/**
* @deprecated Use {@link MemoryBlock#realloc(long)} or {@link MemoryBlock#reallocTracked(long)} instead. This method
* should only be used if specifically a {@linkplain ByteBuffer} is needed and it is short-lived.
*/
@Deprecated
public static ByteBuffer reallocBuffer(ByteBuffer buffer, int size) {
ByteBuffer newBuffer = MemoryUtil.memByteBuffer(realloc(MemoryUtil.memAddress(buffer), size), size);
_freeCPUMemory(buffer.capacity());
_allocCPUMemory(newBuffer.capacity());
return newBuffer;
}
public static void free(long ptr) {
MemoryUtil.nmemFree(ptr);
}
/**
* @deprecated Use {@link MemoryBlock#free} instead. This method should only be used if specifically a {@linkplain ByteBuffer} is needed and
* it is short-lived.
*/
@Deprecated
public static void freeBuffer(ByteBuffer buffer) {
free(MemoryUtil.memAddress(buffer));
_freeCPUMemory(buffer.capacity());
}
public static void _allocCPUMemory(long size) {
cpuMemory += size;
}
public static void _freeCPUMemory(long size) {
cpuMemory -= size;
}
public static void _allocGPUMemory(long size) {
gpuMemory += size;
}
public static void _freeGPUMemory(long size) {
gpuMemory -= size;
}
public static long getCPUMemory() {
return cpuMemory;
}
public static long getGPUMemory() {
return gpuMemory;
}
}

View file

@ -0,0 +1,43 @@
package com.jozufozu.flywheel.backend.memory;
import java.nio.ByteBuffer;
public sealed interface MemoryBlock permits MemoryBlockImpl {
long ptr();
long size();
boolean isFreed();
boolean isTracked();
void copyTo(long ptr, long bytes);
void copyTo(long ptr);
void clear();
ByteBuffer asBuffer();
MemoryBlock realloc(long size);
MemoryBlock reallocTracked(long size);
void free();
static MemoryBlock malloc(long size) {
return MemoryBlockImpl.mallocBlock(size);
}
static MemoryBlock mallocTracked(long size) {
return TrackedMemoryBlockImpl.mallocBlockTracked(size);
}
static MemoryBlock calloc(long num, long size) {
return MemoryBlockImpl.callocBlock(num, size);
}
static MemoryBlock callocTracked(long num, long size) {
return TrackedMemoryBlockImpl.callocBlockTracked(num, size);
}
}

View file

@ -0,0 +1,100 @@
package com.jozufozu.flywheel.backend.memory;
import java.nio.ByteBuffer;
import org.lwjgl.system.MemoryUtil;
sealed class MemoryBlockImpl implements MemoryBlock permits TrackedMemoryBlockImpl {
final long ptr;
final long size;
boolean freed;
MemoryBlockImpl(long ptr, long size) {
this.ptr = ptr;
this.size = size;
}
@Override
public long ptr() {
return ptr;
}
@Override
public long size() {
return size;
}
@Override
public boolean isFreed() {
return freed;
}
@Override
public boolean isTracked() {
return false;
}
@Override
public void copyTo(long ptr, long bytes) {
MemoryUtil.memCopy(this.ptr, ptr, bytes);
}
@Override
public void copyTo(long ptr) {
copyTo(ptr, size);
}
@Override
public void clear() {
MemoryUtil.memSet(ptr, 0, size);
}
@Override
public ByteBuffer asBuffer() {
int intSize = (int) size;
if (intSize != size) {
throw new UnsupportedOperationException("Cannot create buffer with long capacity!");
}
return MemoryUtil.memByteBuffer(ptr, intSize);
}
void freeInner() {
FlwMemoryTracker._freeCPUMemory(size);
freed = true;
}
@Override
public MemoryBlock realloc(long size) {
MemoryBlock block = new MemoryBlockImpl(FlwMemoryTracker.realloc(ptr, size), size);
FlwMemoryTracker._allocCPUMemory(block.size());
freeInner();
return block;
}
@Override
public MemoryBlock reallocTracked(long size) {
MemoryBlock block = new TrackedMemoryBlockImpl(FlwMemoryTracker.realloc(ptr, size), size, FlwMemoryTracker.CLEANER);
FlwMemoryTracker._allocCPUMemory(block.size());
freeInner();
return block;
}
@Override
public void free() {
FlwMemoryTracker.free(ptr);
freeInner();
}
static MemoryBlock mallocBlock(long size) {
MemoryBlock block = new MemoryBlockImpl(FlwMemoryTracker.malloc(size), size);
FlwMemoryTracker._allocCPUMemory(block.size());
return block;
}
static MemoryBlock callocBlock(long num, long size) {
MemoryBlock block = new MemoryBlockImpl(FlwMemoryTracker.calloc(num, size), num * size);
FlwMemoryTracker._allocCPUMemory(block.size());
return block;
}
}

View file

@ -0,0 +1,58 @@
package com.jozufozu.flywheel.backend.memory;
import java.lang.ref.Cleaner;
final class TrackedMemoryBlockImpl extends MemoryBlockImpl {
final CleaningAction cleaningAction;
final Cleaner.Cleanable cleanable;
TrackedMemoryBlockImpl(long ptr, long size, Cleaner cleaner) {
super(ptr, size);
cleaningAction = new CleaningAction(ptr, size);
cleanable = cleaner.register(this, cleaningAction);
}
@Override
public boolean isTracked() {
return true;
}
@Override
void freeInner() {
super.freeInner();
cleaningAction.freed = true;
cleanable.clean();
}
static MemoryBlock mallocBlockTracked(long size) {
MemoryBlock block = new TrackedMemoryBlockImpl(FlwMemoryTracker.malloc(size), size, FlwMemoryTracker.CLEANER);
FlwMemoryTracker._allocCPUMemory(block.size());
return block;
}
static MemoryBlock callocBlockTracked(long num, long size) {
MemoryBlock block = new TrackedMemoryBlockImpl(FlwMemoryTracker.calloc(num, size), num * size, FlwMemoryTracker.CLEANER);
FlwMemoryTracker._allocCPUMemory(block.size());
return block;
}
static class CleaningAction implements Runnable {
final long ptr;
final long size;
boolean freed;
CleaningAction(long ptr, long size) {
this.ptr = ptr;
this.size = size;
}
@Override
public void run() {
if (!freed) {
FlwMemoryTracker.free(ptr);
FlwMemoryTracker._freeCPUMemory(size);
}
}
}
}

View file

@ -1,40 +0,0 @@
package com.jozufozu.flywheel.backend.struct;
import java.nio.ByteBuffer;
import com.jozufozu.flywheel.api.instancer.InstancedPart;
import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.api.struct.StructWriter;
public abstract class BufferWriter<S extends InstancedPart> implements StructWriter<S> {
protected final ByteBuffer backingBuffer;
protected final int stride;
protected BufferWriter(StructType<S> structType, ByteBuffer byteBuffer) {
this.backingBuffer = byteBuffer;
this.stride = structType.getLayout().getStride();
}
@Override
public final void write(S struct) {
writeInternal(struct);
advance();
}
/**
* Advances the write pointer forward by the stride of one vertex.
* This will always be called after a struct is written, implementors need not call it themselves.
*
* @see #write
*/
protected abstract void advance();
protected abstract void writeInternal(S s);
@Override
public void seek(int pos) {
backingBuffer.position(pos * stride);
}
}

View file

@ -1,45 +0,0 @@
package com.jozufozu.flywheel.backend.struct;
import java.nio.ByteBuffer;
import org.lwjgl.system.MemoryUtil;
import com.jozufozu.flywheel.api.instancer.InstancedPart;
import com.jozufozu.flywheel.api.struct.StructType;
/**
* This class copied/adapted from jellysquid's
*
* An unsafe {@link BufferWriter} implementation which uses direct memory operations to enable fast blitting of
* data into memory buffers. Only available on JVMs which support {@link sun.misc.Unsafe}, but generally produces much
* better optimized code than other implementations. The implementation does not check for invalid memory accesses,
* meaning that errors can corrupt process memory.
*/
public abstract class UnsafeBufferWriter<S extends InstancedPart> extends BufferWriter<S> {
/**
* The write pointer into the buffer storage. This is advanced by the stride every time
* {@link UnsafeBufferWriter#advance()} is called.
*/
protected long writePointer;
protected UnsafeBufferWriter(StructType<S> structType, ByteBuffer byteBuffer) {
super(structType, byteBuffer);
acquireWritePointer();
}
@Override
public void seek(int pos) {
super.seek(pos);
acquireWritePointer();
}
@Override
protected void advance() {
this.writePointer += this.stride;
}
private void acquireWritePointer() {
this.writePointer = MemoryUtil.memAddress(this.backingBuffer, this.backingBuffer.position());
}
}

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.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.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,15 +1,13 @@
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;
import com.jozufozu.flywheel.backend.gl.GlNumericType; import com.jozufozu.flywheel.backend.gl.GlNumericType;
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.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

@ -2,74 +2,86 @@ package com.jozufozu.flywheel.core.hardcoded;
import java.util.List; import java.util.List;
import org.jetbrains.annotations.NotNull; import com.jozufozu.flywheel.api.vertex.MutableVertexList;
import org.lwjgl.system.MemoryStack; import com.jozufozu.flywheel.api.vertex.ReusableVertexList;
import com.jozufozu.flywheel.backend.memory.MemoryBlock;
import com.jozufozu.flywheel.api.vertex.VertexList;
import com.jozufozu.flywheel.core.model.Mesh; import com.jozufozu.flywheel.core.model.Mesh;
import com.jozufozu.flywheel.core.model.ModelUtil; import com.jozufozu.flywheel.core.model.ModelUtil;
import com.jozufozu.flywheel.core.vertex.Formats; import com.jozufozu.flywheel.core.vertex.Formats;
import com.jozufozu.flywheel.core.vertex.PosTexNormalVertex; import com.jozufozu.flywheel.core.vertex.PosTexNormalVertex;
import com.jozufozu.flywheel.core.vertex.PosTexNormalWriterUnsafe;
import com.jozufozu.flywheel.util.joml.Vector4f; import com.jozufozu.flywheel.util.joml.Vector4f;
import com.jozufozu.flywheel.util.joml.Vector4fc; import com.jozufozu.flywheel.util.joml.Vector4fc;
public class ModelPart implements Mesh { public class ModelPart implements Mesh {
private final int vertexCount;
private final int vertices; private final MemoryBlock contents;
private final ReusableVertexList vertexList;
private final String name; private final String name;
private final VertexList reader; private final Vector4f boundingSphere;
private final @NotNull Vector4f boundingSphere;
public ModelPart(List<PartBuilder.CuboidBuilder> cuboids, String name) { public ModelPart(List<PartBuilder.CuboidBuilder> cuboids, String name) {
this.name = name; this.name = name;
{ this.vertexCount = countVertices(cuboids);
int vertices = 0;
contents = MemoryBlock.malloc(size());
long ptr = contents.ptr();
VertexWriter writer = new VertexWriterImpl(ptr);
for (PartBuilder.CuboidBuilder cuboid : cuboids) { for (PartBuilder.CuboidBuilder cuboid : cuboids) {
vertices += cuboid.vertices(); cuboid.write(writer);
}
this.vertices = vertices;
} }
try (var stack = MemoryStack.stackPush()) { vertexList = getVertexType().createVertexList();
PosTexNormalWriterUnsafe writer = getVertexType().createWriter(stack.malloc(size())); vertexList.ptr(ptr);
for (PartBuilder.CuboidBuilder cuboid : cuboids) { vertexList.setVertexCount(vertexCount);
cuboid.buffer(writer);
}
reader = writer.intoReader(this.vertices); boundingSphere = ModelUtil.computeBoundingSphere(vertexList);
}
boundingSphere = ModelUtil.computeBoundingSphere(reader);
} }
public static PartBuilder builder(String name, int sizeU, int sizeV) { public static PartBuilder builder(String name, int sizeU, int sizeV) {
return new PartBuilder(name, sizeU, sizeV); return new PartBuilder(name, sizeU, sizeV);
} }
@Override
public String name() {
return name;
}
@Override
public int getVertexCount() {
return vertices;
}
@Override
public VertexList getReader() {
return reader;
}
@Override @Override
public PosTexNormalVertex getVertexType() { public PosTexNormalVertex getVertexType() {
return Formats.POS_TEX_NORMAL; return Formats.POS_TEX_NORMAL;
} }
@Override
public int getVertexCount() {
return vertexCount;
}
@Override
public void write(long ptr) {
contents.copyTo(ptr);
}
@Override
public void write(MutableVertexList dst) {
vertexList.writeAll(dst);
}
@Override
public void close() {
contents.free();
}
@Override
public String name() {
return name;
}
@Override @Override
public Vector4fc getBoundingSphere() { public Vector4fc getBoundingSphere() {
return boundingSphere; return boundingSphere;
} }
private static int countVertices(List<PartBuilder.CuboidBuilder> cuboids) {
int vertices = 0;
for (PartBuilder.CuboidBuilder cuboid : cuboids) {
vertices += cuboid.vertices();
}
return vertices;
}
} }

View file

@ -5,7 +5,6 @@ import java.util.EnumSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import com.jozufozu.flywheel.core.vertex.PosTexNormalWriterUnsafe;
import com.mojang.math.Matrix3f; import com.mojang.math.Matrix3f;
import com.mojang.math.Quaternion; import com.mojang.math.Quaternion;
import com.mojang.math.Vector3f; import com.mojang.math.Vector3f;
@ -160,8 +159,7 @@ public class PartBuilder {
return visibleFaces.size() * 4; return visibleFaces.size() * 4;
} }
public void buffer(PosTexNormalWriterUnsafe buffer) { public void write(VertexWriter writer) {
float sizeX = posX2 - posX1; float sizeX = posX2 - posX1;
float sizeY = posY2 - posY1; float sizeY = posY2 - posY1;
float sizeZ = posZ2 - posZ1; float sizeZ = posZ2 - posZ1;
@ -219,28 +217,27 @@ public class PartBuilder {
float f12 = getV((float)textureOffsetV + sizeZ + sizeY); float f12 = getV((float)textureOffsetV + sizeZ + sizeY);
if (invertYZ) { if (invertYZ) {
quad(buffer, new Vector3f[]{hlh, llh, lll, hll}, f6, f11, f7, f10, down); quad(writer, new Vector3f[]{hlh, llh, lll, hll}, f6, f11, f7, f10, down);
quad(buffer, new Vector3f[]{hhl, lhl, lhh, hhh}, f5, f10, f6, f11, up); quad(writer, new Vector3f[]{hhl, lhl, lhh, hhh}, f5, f10, f6, f11, up);
quad(buffer, new Vector3f[]{lll, llh, lhh, lhl}, f5, f12, f4, f11, west); quad(writer, new Vector3f[]{lll, llh, lhh, lhl}, f5, f12, f4, f11, west);
quad(buffer, new Vector3f[]{hll, lll, lhl, hhl}, f9, f12, f8, f11, north); quad(writer, new Vector3f[]{hll, lll, lhl, hhl}, f9, f12, f8, f11, north);
quad(buffer, new Vector3f[]{hlh, hll, hhl, hhh}, f8, f12, f6, f11, east); quad(writer, new Vector3f[]{hlh, hll, hhl, hhh}, f8, f12, f6, f11, east);
quad(buffer, new Vector3f[]{llh, hlh, hhh, lhh}, f6, f12, f5, f11, south); quad(writer, new Vector3f[]{llh, hlh, hhh, lhh}, f6, f12, f5, f11, south);
} else { } else {
quad(buffer, new Vector3f[]{hlh, llh, lll, hll}, f5, f10, f6, f11, down); quad(writer, new Vector3f[]{hlh, llh, lll, hll}, f5, f10, f6, f11, down);
quad(buffer, new Vector3f[]{hhl, lhl, lhh, hhh}, f6, f11, f7, f10, up); quad(writer, new Vector3f[]{hhl, lhl, lhh, hhh}, f6, f11, f7, f10, up);
quad(buffer, new Vector3f[]{lll, llh, lhh, lhl}, f4, f11, f5, f12, west); quad(writer, new Vector3f[]{lll, llh, lhh, lhl}, f4, f11, f5, f12, west);
quad(buffer, new Vector3f[]{hll, lll, lhl, hhl}, f5, f11, f6, f12, north); quad(writer, new Vector3f[]{hll, lll, lhl, hhl}, f5, f11, f6, f12, north);
quad(buffer, new Vector3f[]{hlh, hll, hhl, hhh}, f6, f11, f8, f12, east); quad(writer, new Vector3f[]{hlh, hll, hhl, hhh}, f6, f11, f8, f12, east);
quad(buffer, new Vector3f[]{llh, hlh, hhh, lhh}, f8, f11, f9, f12, south); quad(writer, new Vector3f[]{llh, hlh, hhh, lhh}, f8, f11, f9, f12, south);
} }
} }
public void quad(PosTexNormalWriterUnsafe buffer, Vector3f[] vertices, float minU, float minV, float maxU, float maxV, Vector3f normal) { public void quad(VertexWriter writer, Vector3f[] vertices, float minU, float minV, float maxU, float maxV, Vector3f normal) {
buffer.putVertex(vertices[0].x(), vertices[0].y(), vertices[0].z(), normal.x(), normal.y(), normal.z(), maxU, minV); writer.putVertex(vertices[0].x(), vertices[0].y(), vertices[0].z(), maxU, minV, normal.x(), normal.y(), normal.z());
buffer.putVertex(vertices[1].x(), vertices[1].y(), vertices[1].z(), normal.x(), normal.y(), normal.z(), minU, minV); writer.putVertex(vertices[1].x(), vertices[1].y(), vertices[1].z(), minU, minV, normal.x(), normal.y(), normal.z());
buffer.putVertex(vertices[2].x(), vertices[2].y(), vertices[2].z(), normal.x(), normal.y(), normal.z(), minU, maxV); writer.putVertex(vertices[2].x(), vertices[2].y(), vertices[2].z(), minU, maxV, normal.x(), normal.y(), normal.z());
buffer.putVertex(vertices[3].x(), vertices[3].y(), vertices[3].z(), normal.x(), normal.y(), normal.z(), maxU, maxV); writer.putVertex(vertices[3].x(), vertices[3].y(), vertices[3].z(), maxU, maxV, normal.x(), normal.y(), normal.z());
} }
public float getU(float u) { public float getU(float u) {
@ -258,5 +255,4 @@ public class PartBuilder {
} }
} }
} }

View file

@ -0,0 +1,5 @@
package com.jozufozu.flywheel.core.hardcoded;
public interface VertexWriter {
void putVertex(float x, float y, float z, float u, float v, float nX, float nY, float nZ);
}

View file

@ -0,0 +1,27 @@
package com.jozufozu.flywheel.core.hardcoded;
import org.lwjgl.system.MemoryUtil;
import com.jozufozu.flywheel.util.RenderMath;
public class VertexWriterImpl implements VertexWriter {
private long ptr;
public VertexWriterImpl(long ptr) {
this.ptr = ptr;
}
@Override
public void putVertex(float x, float y, float z, float u, float v, float nX, float nY, float nZ) {
MemoryUtil.memPutFloat(ptr, x);
MemoryUtil.memPutFloat(ptr + 4, y);
MemoryUtil.memPutFloat(ptr + 8, z);
MemoryUtil.memPutFloat(ptr + 12, u);
MemoryUtil.memPutFloat(ptr + 16, v);
MemoryUtil.memPutByte(ptr + 20, RenderMath.nb(nX));
MemoryUtil.memPutByte(ptr + 21, RenderMath.nb(nY));
MemoryUtil.memPutByte(ptr + 22, RenderMath.nb(nZ));
ptr += 23;
}
}

View file

@ -1,26 +1,39 @@
package com.jozufozu.flywheel.core.layout; package com.jozufozu.flywheel.core.layout;
import com.jozufozu.flywheel.backend.gl.GlNumericType; import com.jozufozu.flywheel.backend.gl.GlNumericType;
import com.jozufozu.flywheel.backend.gl.array.VertexAttributeF;
import com.jozufozu.flywheel.backend.gl.array.VertexAttributeI; import com.jozufozu.flywheel.backend.gl.array.VertexAttributeI;
public class CommonItems { public class CommonItems {
public static final PrimitiveItem VEC4 = new PrimitiveItem(GlNumericType.FLOAT, 4); public static final PrimitiveItem VEC4 = primitiveF(GlNumericType.FLOAT, 4);
public static final PrimitiveItem VEC3 = new PrimitiveItem(GlNumericType.FLOAT, 3); public static final PrimitiveItem VEC3 = primitiveF(GlNumericType.FLOAT, 3);
public static final PrimitiveItem VEC2 = new PrimitiveItem(GlNumericType.FLOAT, 2); public static final PrimitiveItem VEC2 = primitiveF(GlNumericType.FLOAT, 2);
public static final PrimitiveItem FLOAT = new PrimitiveItem(GlNumericType.FLOAT, 1); public static final PrimitiveItem FLOAT = primitiveF(GlNumericType.FLOAT, 1);
public static final PrimitiveItem QUATERNION = new PrimitiveItem(GlNumericType.FLOAT, 4); public static final PrimitiveItem QUATERNION = primitiveF(GlNumericType.FLOAT, 4);
public static final PrimitiveItem NORMAL = new PrimitiveItem(GlNumericType.BYTE, 3, true); public static final PrimitiveItem NORMAL = primitiveF(GlNumericType.BYTE, 3, true);
public static final PrimitiveItem UV = new PrimitiveItem(GlNumericType.FLOAT, 2); public static final PrimitiveItem UV = primitiveF(GlNumericType.FLOAT, 2);
public static final PrimitiveItem RGBA = new PrimitiveItem(GlNumericType.UBYTE, 4, true); public static final PrimitiveItem RGBA = primitiveF(GlNumericType.UBYTE, 4, true);
public static final PrimitiveItem RGB = new PrimitiveItem(GlNumericType.UBYTE, 3, true); public static final PrimitiveItem RGB = primitiveF(GlNumericType.UBYTE, 3, true);
public static final PrimitiveItem LIGHT = new PrimitiveItem(new VertexAttributeI(GlNumericType.UBYTE, 2)); public static final PrimitiveItem LIGHT = primitiveI(GlNumericType.UBYTE, 2);
public static final PrimitiveItem LIGHT_SHORT = new PrimitiveItem(new VertexAttributeI(GlNumericType.USHORT, 2)); public static final PrimitiveItem LIGHT_SHORT = primitiveI(GlNumericType.USHORT, 2);
public static final PrimitiveItem NORMALIZED_BYTE = new PrimitiveItem(GlNumericType.BYTE, 1, true); public static final PrimitiveItem NORMALIZED_BYTE = primitiveF(GlNumericType.BYTE, 1, true);
public static final MatrixItem MAT3 = new MatrixItem(3, 3); public static final MatrixItem MAT3 = new MatrixItem(3, 3);
public static final MatrixItem MAT4 = new MatrixItem(4, 4); public static final MatrixItem MAT4 = new MatrixItem(4, 4);
private static PrimitiveItem primitiveF(GlNumericType type, int count, boolean normalized) {
return new PrimitiveItem(new VertexAttributeF(type, count, normalized));
}
private static PrimitiveItem primitiveF(GlNumericType type, int count) {
return primitiveF(type, count, false);
}
private static PrimitiveItem primitiveI(GlNumericType type, int count) {
return new PrimitiveItem(new VertexAttributeI(type, count));
}
} }

View file

@ -2,22 +2,12 @@ package com.jozufozu.flywheel.core.layout;
import java.util.function.Consumer; import java.util.function.Consumer;
import com.jozufozu.flywheel.backend.gl.GlNumericType;
import com.jozufozu.flywheel.backend.gl.array.VertexAttribute; import com.jozufozu.flywheel.backend.gl.array.VertexAttribute;
import com.jozufozu.flywheel.backend.gl.array.VertexAttributeF;
public class PrimitiveItem implements LayoutItem { public class PrimitiveItem implements LayoutItem {
private final VertexAttribute attribute; private final VertexAttribute attribute;
public PrimitiveItem(GlNumericType type, int count) {
this(type, count, false);
}
public PrimitiveItem(GlNumericType type, int count, boolean normalized) {
this(new VertexAttributeF(type, count, normalized));
}
public PrimitiveItem(VertexAttribute attribute) { public PrimitiveItem(VertexAttribute attribute) {
this.attribute = attribute; this.attribute = attribute;
} }

View file

@ -1,71 +1,56 @@
package com.jozufozu.flywheel.core.model; package com.jozufozu.flywheel.core.model;
import java.nio.ByteBuffer; import com.jozufozu.flywheel.api.vertex.MutableVertexList;
import com.jozufozu.flywheel.api.vertex.VertexList;
import com.jozufozu.flywheel.api.vertex.VertexType; import com.jozufozu.flywheel.api.vertex.VertexType;
import com.jozufozu.flywheel.api.vertex.VertexWriter;
import com.jozufozu.flywheel.backend.instancing.instancing.ElementBuffer; import com.jozufozu.flywheel.backend.instancing.instancing.ElementBuffer;
import com.jozufozu.flywheel.core.QuadConverter; import com.jozufozu.flywheel.core.QuadConverter;
import com.jozufozu.flywheel.util.joml.Vector4fc; import com.jozufozu.flywheel.util.joml.Vector4fc;
/** /**
* A mesh that can be rendered by flywheel. * A holder for arbitrary vertex data that can be written to memory or a vertex list.
*
* <p>
* It is expected that the following assertion will not fail:
* </p>
*
* <pre>{@code
* Mesh mesh = ...;
* VecBuffer into = ...;
*
* int initial = VecBuffer.unwrap().position();
*
* mesh.buffer(into);
*
* int final = VecBuffer.unwrap().position();
*
* assert mesh.size() == final - initial;
* }</pre>
*/ */
public interface Mesh { public interface Mesh {
/**
* A name uniquely identifying this model.
*/
String name();
VertexType getVertexType(); VertexType getVertexType();
VertexList getReader();
Vector4fc getBoundingSphere(); Vector4fc getBoundingSphere();
/** /**
* @return The number of vertices the model has. * @return The number of vertices this mesh has.
*/ */
default int getVertexCount() { int getVertexCount();
return getReader().getVertexCount();
}
/** /**
* Is there nothing to render? * Is there nothing to render?
* @return true if there are no vertices. * @return true if there are no vertices.
*/ */
default boolean isEmpty() { default boolean isEmpty() {
return getReader().isEmpty(); return getVertexCount() == 0;
} }
/** /**
* The size in bytes that this model's data takes up. * The size in bytes that this mesh's data takes up.
*/ */
default int size() { default int size() {
return getVertexType().byteOffset(getVertexCount()); return getVertexType().byteOffset(getVertexCount());
} }
/** /**
* Create an element buffer object that indexes the vertices of this model. * Write this mesh into memory. The written data will use the format defined by {@link #getVertexType()} and the amount of
* bytes written will be the same as the return value of {@link #size()}.
* @param ptr The address to which data is written to.
*/
void write(long ptr);
/**
* Write this mesh into a vertex list. Vertices with index {@literal <}0 or {@literal >=}{@link #getVertexCount()} will not be
* modified.
* @param vertexList The vertex list to which data is written to.
*/
void write(MutableVertexList vertexList);
/**
* Create an element buffer object that indexes the vertices of this mesh.
* *
* <p> * <p>
* Very often models in minecraft are made up of sequential quads, which is a very predictable pattern. * Very often models in minecraft are made up of sequential quads, which is a very predictable pattern.
@ -79,9 +64,10 @@ public interface Mesh {
.quads2Tris(getVertexCount() / 4); .quads2Tris(getVertexCount() / 4);
} }
default void writeInto(ByteBuffer buffer, long byteIndex) { void close();
VertexWriter writer = getVertexType().createWriter(buffer);
writer.seek(byteIndex); /**
writer.writeVertexList(getReader()); * A name uniquely identifying this mesh.
} */
String name();
} }

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

@ -5,16 +5,23 @@ 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 com.dreizak.miniball.highdim.Miniball; import com.dreizak.miniball.highdim.Miniball;
import com.jozufozu.flywheel.Flywheel; import com.jozufozu.flywheel.Flywheel;
import com.jozufozu.flywheel.api.material.Material; import com.jozufozu.flywheel.api.material.Material;
import com.jozufozu.flywheel.api.vertex.ReusableVertexList;
import com.jozufozu.flywheel.api.vertex.VertexList; import com.jozufozu.flywheel.api.vertex.VertexList;
import com.jozufozu.flywheel.api.vertex.VertexType;
import com.jozufozu.flywheel.backend.memory.MemoryBlock;
import com.jozufozu.flywheel.core.Materials; import com.jozufozu.flywheel.core.Materials;
import com.jozufozu.flywheel.core.vertex.Formats; import com.jozufozu.flywheel.core.vertex.Formats;
import com.jozufozu.flywheel.util.joml.Vector4f; import com.jozufozu.flywheel.util.joml.Vector4f;
import com.mojang.blaze3d.vertex.BufferBuilder; import com.mojang.blaze3d.vertex.BufferBuilder;
import com.mojang.blaze3d.vertex.DefaultVertexFormat; import com.mojang.blaze3d.vertex.DefaultVertexFormat;
import com.jozufozu.flywheel.core.vertex.VertexListProviderRegistry;
import com.mojang.blaze3d.vertex.BufferBuilder.DrawState;
import com.mojang.blaze3d.vertex.VertexFormat;
import com.mojang.datafixers.util.Pair; import com.mojang.datafixers.util.Pair;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
@ -46,15 +53,27 @@ public class ModelUtil {
return dispatcher; return dispatcher;
} }
public static VertexList createVertexList(BufferBuilder bufferBuilder) { public static Pair<VertexType, MemoryBlock> convertBlockBuffer(Pair<DrawState, ByteBuffer> pair) {
Pair<BufferBuilder.DrawState, ByteBuffer> pair = bufferBuilder.popNextBuffer(); DrawState drawState = pair.getFirst();
BufferBuilder.DrawState drawState = pair.getFirst(); int vertexCount = drawState.vertexCount();
VertexFormat srcFormat = drawState.format();
VertexType dstVertexType = Formats.BLOCK;
if (drawState.format() != DefaultVertexFormat.BLOCK) { ByteBuffer src = pair.getSecond();
throw new RuntimeException("Cannot use BufferBuilder with " + drawState.format()); MemoryBlock dst = MemoryBlock.malloc(src.capacity());
} long srcPtr = MemoryUtil.memAddress(src);
long dstPtr = dst.ptr();
return Formats.BLOCK.createReader(pair.getSecond(), drawState.vertexCount()); ReusableVertexList srcList = VertexListProviderRegistry.getOrInfer(srcFormat).createVertexList();
ReusableVertexList dstList = dstVertexType.createVertexList();
srcList.ptr(srcPtr);
dstList.ptr(dstPtr);
srcList.setVertexCount(vertexCount);
dstList.setVertexCount(vertexCount);
srcList.writeAll(dstList);
return Pair.of(dstVertexType, dst);
} }
@Nullable @Nullable

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

@ -1,27 +1,37 @@
package com.jozufozu.flywheel.core.model; package com.jozufozu.flywheel.core.model;
import com.jozufozu.flywheel.api.vertex.VertexList; import com.jozufozu.flywheel.api.vertex.MutableVertexList;
import com.jozufozu.flywheel.api.vertex.ReusableVertexList;
import com.jozufozu.flywheel.api.vertex.VertexType; import com.jozufozu.flywheel.api.vertex.VertexType;
import com.jozufozu.flywheel.backend.memory.MemoryBlock;
import com.jozufozu.flywheel.util.joml.Vector4f; import com.jozufozu.flywheel.util.joml.Vector4f;
import com.jozufozu.flywheel.util.joml.Vector4fc; import com.jozufozu.flywheel.util.joml.Vector4fc;
public class SimpleMesh implements Mesh { public class SimpleMesh implements Mesh {
private final VertexList reader;
private final VertexType vertexType; private final VertexType vertexType;
private final int vertexCount;
private final MemoryBlock contents;
private final ReusableVertexList vertexList;
private final String name; private final String name;
private final Vector4f boundingSphere; private final Vector4f boundingSphere;
public SimpleMesh(VertexList reader, VertexType vertexType, String name) { public SimpleMesh(VertexType vertexType, MemoryBlock contents, String name) {
this.reader = reader;
this.vertexType = vertexType; this.vertexType = vertexType;
this.contents = contents;
this.name = name; this.name = name;
boundingSphere = ModelUtil.computeBoundingSphere(reader); int bytes = (int) contents.size();
int stride = vertexType.getStride();
if (bytes % stride != 0) {
throw new IllegalArgumentException("MemoryBlock contains non-whole amount of vertices!");
} }
vertexCount = bytes / stride;
@Override vertexList = getVertexType().createVertexList();
public String name() { vertexList.ptr(contents.ptr());
return name; vertexList.setVertexCount(vertexCount);
boundingSphere = ModelUtil.computeBoundingSphere(vertexList);
} }
@Override @Override
@ -30,8 +40,28 @@ public class SimpleMesh implements Mesh {
} }
@Override @Override
public VertexList getReader() { public int getVertexCount() {
return reader; return vertexCount;
}
@Override
public void write(long ptr) {
contents.copyTo(ptr);
}
@Override
public void write(MutableVertexList dst) {
vertexList.writeAll(dst);
}
@Override
public void close() {
contents.free();
}
@Override
public String name() {
return name;
} }
@Override @Override

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

@ -4,6 +4,8 @@ import java.util.function.BiFunction;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.jozufozu.flywheel.api.material.Material; import com.jozufozu.flywheel.api.material.Material;
import com.jozufozu.flywheel.api.vertex.VertexType;
import com.jozufozu.flywheel.backend.memory.MemoryBlock;
import com.jozufozu.flywheel.core.model.Mesh; import com.jozufozu.flywheel.core.model.Mesh;
import com.jozufozu.flywheel.core.model.ModelUtil; import com.jozufozu.flywheel.core.model.ModelUtil;
import com.jozufozu.flywheel.core.model.SimpleMesh; import com.jozufozu.flywheel.core.model.SimpleMesh;
@ -12,13 +14,13 @@ import com.jozufozu.flywheel.core.model.buffering.ModelBufferingUtil.BufferFacto
import com.jozufozu.flywheel.core.model.buffering.ModelBufferingUtil.ResultConsumer; import com.jozufozu.flywheel.core.model.buffering.ModelBufferingUtil.ResultConsumer;
import com.jozufozu.flywheel.core.model.buffering.ModelBufferingUtil.ShadeSeparatedBufferFactory; import com.jozufozu.flywheel.core.model.buffering.ModelBufferingUtil.ShadeSeparatedBufferFactory;
import com.jozufozu.flywheel.core.model.buffering.ModelBufferingUtil.ShadeSeparatedResultConsumer; import com.jozufozu.flywheel.core.model.buffering.ModelBufferingUtil.ShadeSeparatedResultConsumer;
import com.jozufozu.flywheel.core.vertex.Formats;
import com.jozufozu.flywheel.core.virtual.VirtualEmptyBlockGetter; import com.jozufozu.flywheel.core.virtual.VirtualEmptyBlockGetter;
import com.jozufozu.flywheel.core.virtual.VirtualEmptyModelData; import com.jozufozu.flywheel.core.virtual.VirtualEmptyModelData;
import com.mojang.blaze3d.vertex.BufferBuilder; import com.mojang.blaze3d.vertex.BufferBuilder;
import com.mojang.blaze3d.vertex.DefaultVertexFormat; import com.mojang.blaze3d.vertex.DefaultVertexFormat;
import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexFormat; import com.mojang.blaze3d.vertex.VertexFormat;
import com.mojang.datafixers.util.Pair;
import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.resources.model.BakedModel; import net.minecraft.client.resources.model.BakedModel;
@ -28,9 +30,10 @@ import net.minecraft.world.level.block.state.BlockState;
import net.minecraftforge.client.model.data.IModelData; import net.minecraftforge.client.model.data.IModelData;
public class BakedModelBuilder { public class BakedModelBuilder {
private static final int STARTING_CAPACITY = 64;
private final BakedModel bakedModel; private final BakedModel bakedModel;
private boolean shadeSeparated = true; private boolean shadeSeparated = true;
private VertexFormat vertexFormat;
private BlockAndTintGetter renderWorld; private BlockAndTintGetter renderWorld;
private BlockState blockState; private BlockState blockState;
private PoseStack poseStack; private PoseStack poseStack;
@ -46,11 +49,6 @@ public class BakedModelBuilder {
return this; return this;
} }
public BakedModelBuilder vertexFormat(VertexFormat vertexFormat) {
this.vertexFormat = vertexFormat;
return this;
}
public BakedModelBuilder renderWorld(BlockAndTintGetter renderWorld) { public BakedModelBuilder renderWorld(BlockAndTintGetter renderWorld) {
this.renderWorld = renderWorld; this.renderWorld = renderWorld;
return this; return this;
@ -80,9 +78,6 @@ public class BakedModelBuilder {
public TessellatedModel build() { public TessellatedModel build() {
ModelBufferingObjects objects = ModelBufferingObjects.THREAD_LOCAL.get(); ModelBufferingObjects objects = ModelBufferingObjects.THREAD_LOCAL.get();
if (vertexFormat == null) {
vertexFormat = DefaultVertexFormat.BLOCK;
}
if (renderWorld == null) { if (renderWorld == null) {
renderWorld = VirtualEmptyBlockGetter.INSTANCE; renderWorld = VirtualEmptyBlockGetter.INSTANCE;
} }
@ -103,29 +98,31 @@ public class BakedModelBuilder {
if (shadeSeparated) { if (shadeSeparated) {
ShadeSeparatedBufferFactory<BufferBuilder> bufferFactory = (renderType, shaded) -> { ShadeSeparatedBufferFactory<BufferBuilder> bufferFactory = (renderType, shaded) -> {
BufferBuilder buffer = new BufferBuilder(64); BufferBuilder buffer = new BufferBuilder(STARTING_CAPACITY);
buffer.begin(VertexFormat.Mode.QUADS, vertexFormat); buffer.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.BLOCK);
return buffer; return buffer;
}; };
ShadeSeparatedResultConsumer<BufferBuilder> resultConsumer = (renderType, shaded, buffer) -> { ShadeSeparatedResultConsumer<BufferBuilder> resultConsumer = (renderType, shaded, buffer) -> {
buffer.end(); buffer.end();
Material material = materialFunc.apply(renderType, shaded); Material material = materialFunc.apply(renderType, shaded);
if (material != null) { if (material != null) {
meshMapBuilder.put(material, new SimpleMesh(ModelUtil.createVertexList(buffer), Formats.BLOCK, "bakedModel=" + bakedModel.toString() + ",renderType=" + renderType.toString() + ",shaded=" + shaded)); Pair<VertexType, MemoryBlock> pair = ModelUtil.convertBlockBuffer(buffer.popNextBuffer());
meshMapBuilder.put(material, new SimpleMesh(pair.getFirst(), pair.getSecond(), "bakedModel=" + bakedModel.toString() + ",renderType=" + renderType.toString() + ",shaded=" + shaded));
} }
}; };
ModelBufferingUtil.bufferSingleShadeSeparated(ModelUtil.VANILLA_RENDERER.getModelRenderer(), renderWorld, bakedModel, blockState, poseStack, bufferFactory, objects.shadeSeparatingBufferWrapper, objects.random, modelData, resultConsumer); ModelBufferingUtil.bufferSingleShadeSeparated(ModelUtil.VANILLA_RENDERER.getModelRenderer(), renderWorld, bakedModel, blockState, poseStack, bufferFactory, objects.shadeSeparatingBufferWrapper, objects.random, modelData, resultConsumer);
} else { } else {
BufferFactory<BufferBuilder> bufferFactory = (renderType) -> { BufferFactory<BufferBuilder> bufferFactory = (renderType) -> {
BufferBuilder buffer = new BufferBuilder(64); BufferBuilder buffer = new BufferBuilder(STARTING_CAPACITY);
buffer.begin(VertexFormat.Mode.QUADS, vertexFormat); buffer.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.BLOCK);
return buffer; return buffer;
}; };
ResultConsumer<BufferBuilder> resultConsumer = (renderType, buffer) -> { ResultConsumer<BufferBuilder> resultConsumer = (renderType, buffer) -> {
buffer.end(); buffer.end();
Material material = materialFunc.apply(renderType, false); Material material = materialFunc.apply(renderType, false);
if (material != null) { if (material != null) {
meshMapBuilder.put(material, new SimpleMesh(ModelUtil.createVertexList(buffer), Formats.BLOCK, "bakedModel=" + bakedModel.toString() + ",renderType=" + renderType.toString())); Pair<VertexType, MemoryBlock> pair = ModelUtil.convertBlockBuffer(buffer.popNextBuffer());
meshMapBuilder.put(material, new SimpleMesh(pair.getFirst(), pair.getSecond(), "bakedModel=" + bakedModel.toString() + ",renderType=" + renderType.toString()));
} }
}; };
ModelBufferingUtil.bufferSingle(ModelUtil.VANILLA_RENDERER.getModelRenderer(), renderWorld, bakedModel, blockState, poseStack, bufferFactory, objects.bufferWrapper, objects.random, modelData, resultConsumer); ModelBufferingUtil.bufferSingle(ModelUtil.VANILLA_RENDERER.getModelRenderer(), renderWorld, bakedModel, blockState, poseStack, bufferFactory, objects.bufferWrapper, objects.random, modelData, resultConsumer);

View file

@ -4,6 +4,8 @@ import java.util.function.BiFunction;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.jozufozu.flywheel.api.material.Material; import com.jozufozu.flywheel.api.material.Material;
import com.jozufozu.flywheel.api.vertex.VertexType;
import com.jozufozu.flywheel.backend.memory.MemoryBlock;
import com.jozufozu.flywheel.core.model.Mesh; import com.jozufozu.flywheel.core.model.Mesh;
import com.jozufozu.flywheel.core.model.ModelUtil; import com.jozufozu.flywheel.core.model.ModelUtil;
import com.jozufozu.flywheel.core.model.SimpleMesh; import com.jozufozu.flywheel.core.model.SimpleMesh;
@ -12,13 +14,13 @@ import com.jozufozu.flywheel.core.model.buffering.ModelBufferingUtil.BufferFacto
import com.jozufozu.flywheel.core.model.buffering.ModelBufferingUtil.ResultConsumer; import com.jozufozu.flywheel.core.model.buffering.ModelBufferingUtil.ResultConsumer;
import com.jozufozu.flywheel.core.model.buffering.ModelBufferingUtil.ShadeSeparatedBufferFactory; import com.jozufozu.flywheel.core.model.buffering.ModelBufferingUtil.ShadeSeparatedBufferFactory;
import com.jozufozu.flywheel.core.model.buffering.ModelBufferingUtil.ShadeSeparatedResultConsumer; import com.jozufozu.flywheel.core.model.buffering.ModelBufferingUtil.ShadeSeparatedResultConsumer;
import com.jozufozu.flywheel.core.vertex.Formats;
import com.jozufozu.flywheel.core.virtual.VirtualEmptyBlockGetter; import com.jozufozu.flywheel.core.virtual.VirtualEmptyBlockGetter;
import com.jozufozu.flywheel.core.virtual.VirtualEmptyModelData; import com.jozufozu.flywheel.core.virtual.VirtualEmptyModelData;
import com.mojang.blaze3d.vertex.BufferBuilder; import com.mojang.blaze3d.vertex.BufferBuilder;
import com.mojang.blaze3d.vertex.DefaultVertexFormat; import com.mojang.blaze3d.vertex.DefaultVertexFormat;
import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexFormat; import com.mojang.blaze3d.vertex.VertexFormat;
import com.mojang.datafixers.util.Pair;
import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.RenderType;
import net.minecraft.world.level.BlockAndTintGetter; import net.minecraft.world.level.BlockAndTintGetter;
@ -26,9 +28,10 @@ import net.minecraft.world.level.block.state.BlockState;
import net.minecraftforge.client.model.data.IModelData; import net.minecraftforge.client.model.data.IModelData;
public class BlockModelBuilder { public class BlockModelBuilder {
private static final int STARTING_CAPACITY = 64;
private final BlockState state; private final BlockState state;
private boolean shadeSeparated = true; private boolean shadeSeparated = true;
private VertexFormat vertexFormat;
private BlockAndTintGetter renderWorld; private BlockAndTintGetter renderWorld;
private PoseStack poseStack; private PoseStack poseStack;
private IModelData modelData; private IModelData modelData;
@ -43,11 +46,6 @@ public class BlockModelBuilder {
return this; return this;
} }
public BlockModelBuilder vertexFormat(VertexFormat vertexFormat) {
this.vertexFormat = vertexFormat;
return this;
}
public BlockModelBuilder renderWorld(BlockAndTintGetter renderWorld) { public BlockModelBuilder renderWorld(BlockAndTintGetter renderWorld) {
this.renderWorld = renderWorld; this.renderWorld = renderWorld;
return this; return this;
@ -72,9 +70,6 @@ public class BlockModelBuilder {
public TessellatedModel build() { public TessellatedModel build() {
ModelBufferingObjects objects = ModelBufferingObjects.THREAD_LOCAL.get(); ModelBufferingObjects objects = ModelBufferingObjects.THREAD_LOCAL.get();
if (vertexFormat == null) {
vertexFormat = DefaultVertexFormat.BLOCK;
}
if (renderWorld == null) { if (renderWorld == null) {
renderWorld = VirtualEmptyBlockGetter.INSTANCE; renderWorld = VirtualEmptyBlockGetter.INSTANCE;
} }
@ -92,29 +87,31 @@ public class BlockModelBuilder {
if (shadeSeparated) { if (shadeSeparated) {
ShadeSeparatedBufferFactory<BufferBuilder> bufferFactory = (renderType, shaded) -> { ShadeSeparatedBufferFactory<BufferBuilder> bufferFactory = (renderType, shaded) -> {
BufferBuilder buffer = new BufferBuilder(64); BufferBuilder buffer = new BufferBuilder(STARTING_CAPACITY);
buffer.begin(VertexFormat.Mode.QUADS, vertexFormat); buffer.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.BLOCK);
return buffer; return buffer;
}; };
ShadeSeparatedResultConsumer<BufferBuilder> resultConsumer = (renderType, shaded, buffer) -> { ShadeSeparatedResultConsumer<BufferBuilder> resultConsumer = (renderType, shaded, buffer) -> {
buffer.end(); buffer.end();
Material material = materialFunc.apply(renderType, shaded); Material material = materialFunc.apply(renderType, shaded);
if (material != null) { if (material != null) {
meshMapBuilder.put(material, new SimpleMesh(ModelUtil.createVertexList(buffer), Formats.BLOCK, "state=" + state.toString() + ",renderType=" + renderType.toString() + ",shaded=" + shaded)); Pair<VertexType, MemoryBlock> pair = ModelUtil.convertBlockBuffer(buffer.popNextBuffer());
meshMapBuilder.put(material, new SimpleMesh(pair.getFirst(), pair.getSecond(), "state=" + state.toString() + ",renderType=" + renderType.toString() + ",shaded=" + shaded));
} }
}; };
ModelBufferingUtil.bufferBlockShadeSeparated(ModelUtil.VANILLA_RENDERER, renderWorld, state, poseStack, bufferFactory, objects.shadeSeparatingBufferWrapper, objects.random, modelData, resultConsumer); ModelBufferingUtil.bufferBlockShadeSeparated(ModelUtil.VANILLA_RENDERER, renderWorld, state, poseStack, bufferFactory, objects.shadeSeparatingBufferWrapper, objects.random, modelData, resultConsumer);
} else { } else {
BufferFactory<BufferBuilder> bufferFactory = (renderType) -> { BufferFactory<BufferBuilder> bufferFactory = (renderType) -> {
BufferBuilder buffer = new BufferBuilder(64); BufferBuilder buffer = new BufferBuilder(STARTING_CAPACITY);
buffer.begin(VertexFormat.Mode.QUADS, vertexFormat); buffer.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.BLOCK);
return buffer; return buffer;
}; };
ResultConsumer<BufferBuilder> resultConsumer = (renderType, buffer) -> { ResultConsumer<BufferBuilder> resultConsumer = (renderType, buffer) -> {
buffer.end(); buffer.end();
Material material = materialFunc.apply(renderType, false); Material material = materialFunc.apply(renderType, false);
if (material != null) { if (material != null) {
meshMapBuilder.put(material, new SimpleMesh(ModelUtil.createVertexList(buffer), Formats.BLOCK, "state=" + state.toString() + ",renderType=" + renderType.toString())); Pair<VertexType, MemoryBlock> pair = ModelUtil.convertBlockBuffer(buffer.popNextBuffer());
meshMapBuilder.put(material, new SimpleMesh(pair.getFirst(), pair.getSecond(), "state=" + state.toString() + ",renderType=" + renderType.toString()));
} }
}; };
ModelBufferingUtil.bufferBlock(ModelUtil.VANILLA_RENDERER, renderWorld, state, poseStack, bufferFactory, objects.bufferWrapper, objects.random, modelData, resultConsumer); ModelBufferingUtil.bufferBlock(ModelUtil.VANILLA_RENDERER, renderWorld, state, poseStack, bufferFactory, objects.bufferWrapper, objects.random, modelData, resultConsumer);

View file

@ -7,6 +7,8 @@ import java.util.function.BiFunction;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.jozufozu.flywheel.api.material.Material; import com.jozufozu.flywheel.api.material.Material;
import com.jozufozu.flywheel.api.vertex.VertexType;
import com.jozufozu.flywheel.backend.memory.MemoryBlock;
import com.jozufozu.flywheel.core.model.Mesh; import com.jozufozu.flywheel.core.model.Mesh;
import com.jozufozu.flywheel.core.model.ModelUtil; import com.jozufozu.flywheel.core.model.ModelUtil;
import com.jozufozu.flywheel.core.model.SimpleMesh; import com.jozufozu.flywheel.core.model.SimpleMesh;
@ -15,12 +17,12 @@ import com.jozufozu.flywheel.core.model.buffering.ModelBufferingUtil.BufferFacto
import com.jozufozu.flywheel.core.model.buffering.ModelBufferingUtil.ResultConsumer; import com.jozufozu.flywheel.core.model.buffering.ModelBufferingUtil.ResultConsumer;
import com.jozufozu.flywheel.core.model.buffering.ModelBufferingUtil.ShadeSeparatedBufferFactory; import com.jozufozu.flywheel.core.model.buffering.ModelBufferingUtil.ShadeSeparatedBufferFactory;
import com.jozufozu.flywheel.core.model.buffering.ModelBufferingUtil.ShadeSeparatedResultConsumer; import com.jozufozu.flywheel.core.model.buffering.ModelBufferingUtil.ShadeSeparatedResultConsumer;
import com.jozufozu.flywheel.core.vertex.Formats;
import com.jozufozu.flywheel.core.virtual.VirtualEmptyBlockGetter; import com.jozufozu.flywheel.core.virtual.VirtualEmptyBlockGetter;
import com.mojang.blaze3d.vertex.BufferBuilder; import com.mojang.blaze3d.vertex.BufferBuilder;
import com.mojang.blaze3d.vertex.DefaultVertexFormat; import com.mojang.blaze3d.vertex.DefaultVertexFormat;
import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexFormat; import com.mojang.blaze3d.vertex.VertexFormat;
import com.mojang.datafixers.util.Pair;
import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.RenderType;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
@ -29,6 +31,8 @@ import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemp
import net.minecraftforge.client.model.data.IModelData; import net.minecraftforge.client.model.data.IModelData;
public class MultiBlockModelBuilder { public class MultiBlockModelBuilder {
private static final int STARTING_CAPACITY = 1024;
private final Collection<StructureTemplate.StructureBlockInfo> blocks; private final Collection<StructureTemplate.StructureBlockInfo> blocks;
private boolean shadeSeparated = true; private boolean shadeSeparated = true;
private VertexFormat vertexFormat; private VertexFormat vertexFormat;
@ -46,11 +50,6 @@ public class MultiBlockModelBuilder {
return this; return this;
} }
public MultiBlockModelBuilder vertexFormat(VertexFormat vertexFormat) {
this.vertexFormat = vertexFormat;
return this;
}
public MultiBlockModelBuilder renderWorld(BlockAndTintGetter renderWorld) { public MultiBlockModelBuilder renderWorld(BlockAndTintGetter renderWorld) {
this.renderWorld = renderWorld; this.renderWorld = renderWorld;
return this; return this;
@ -95,29 +94,31 @@ public class MultiBlockModelBuilder {
if (shadeSeparated) { if (shadeSeparated) {
ShadeSeparatedBufferFactory<BufferBuilder> bufferFactory = (renderType, shaded) -> { ShadeSeparatedBufferFactory<BufferBuilder> bufferFactory = (renderType, shaded) -> {
BufferBuilder buffer = new BufferBuilder(1024); BufferBuilder buffer = new BufferBuilder(STARTING_CAPACITY);
buffer.begin(VertexFormat.Mode.QUADS, vertexFormat); buffer.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.BLOCK);
return buffer; return buffer;
}; };
ShadeSeparatedResultConsumer<BufferBuilder> resultConsumer = (renderType, shaded, buffer) -> { ShadeSeparatedResultConsumer<BufferBuilder> resultConsumer = (renderType, shaded, buffer) -> {
buffer.end(); buffer.end();
Material material = materialFunc.apply(renderType, shaded); Material material = materialFunc.apply(renderType, shaded);
if (material != null) { if (material != null) {
meshMapBuilder.put(material, new SimpleMesh(ModelUtil.createVertexList(buffer), Formats.BLOCK, "renderType=" + renderType.toString() + ",shaded=" + shaded)); Pair<VertexType, MemoryBlock> pair = ModelUtil.convertBlockBuffer(buffer.popNextBuffer());
meshMapBuilder.put(material, new SimpleMesh(pair.getFirst(), pair.getSecond(), "renderType=" + renderType.toString() + ",shaded=" + shaded));
} }
}; };
ModelBufferingUtil.bufferMultiBlockShadeSeparated(blocks, ModelUtil.VANILLA_RENDERER, renderWorld, poseStack, bufferFactory, objects.shadeSeparatingBufferWrapper, objects.random, modelDataMap, resultConsumer); ModelBufferingUtil.bufferMultiBlockShadeSeparated(blocks, ModelUtil.VANILLA_RENDERER, renderWorld, poseStack, bufferFactory, objects.shadeSeparatingBufferWrapper, objects.random, modelDataMap, resultConsumer);
} else { } else {
BufferFactory<BufferBuilder> bufferFactory = (renderType) -> { BufferFactory<BufferBuilder> bufferFactory = (renderType) -> {
BufferBuilder buffer = new BufferBuilder(1024); BufferBuilder buffer = new BufferBuilder(STARTING_CAPACITY);
buffer.begin(VertexFormat.Mode.QUADS, vertexFormat); buffer.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.BLOCK);
return buffer; return buffer;
}; };
ResultConsumer<BufferBuilder> resultConsumer = (renderType, buffer) -> { ResultConsumer<BufferBuilder> resultConsumer = (renderType, buffer) -> {
buffer.end(); buffer.end();
Material material = materialFunc.apply(renderType, false); Material material = materialFunc.apply(renderType, false);
if (material != null) { if (material != null) {
meshMapBuilder.put(material, new SimpleMesh(ModelUtil.createVertexList(buffer), Formats.BLOCK, "renderType=" + renderType.toString())); Pair<VertexType, MemoryBlock> pair = ModelUtil.convertBlockBuffer(buffer.popNextBuffer());
meshMapBuilder.put(material, new SimpleMesh(pair.getFirst(), pair.getSecond(), "renderType=" + renderType.toString()));
} }
}; };
ModelBufferingUtil.bufferMultiBlock(blocks, ModelUtil.VANILLA_RENDERER, renderWorld, poseStack, bufferFactory, objects.bufferWrapper, objects.random, modelDataMap, resultConsumer); ModelBufferingUtil.bufferMultiBlock(blocks, ModelUtil.VANILLA_RENDERER, renderWorld, poseStack, bufferFactory, objects.bufferWrapper, objects.random, modelDataMap, resultConsumer);

View file

@ -0,0 +1,17 @@
package com.jozufozu.flywheel.core.structs;
import org.lwjgl.system.MemoryUtil;
import com.jozufozu.flywheel.api.struct.StructWriter;
public abstract class ColoredLitWriter<D extends ColoredLitPart> implements StructWriter<D> {
@Override
public void write(long ptr, D d) {
MemoryUtil.memPutByte(ptr, d.blockLight);
MemoryUtil.memPutByte(ptr + 1, d.skyLight);
MemoryUtil.memPutByte(ptr + 2, d.r);
MemoryUtil.memPutByte(ptr + 3, d.g);
MemoryUtil.memPutByte(ptr + 4, d.b);
MemoryUtil.memPutByte(ptr + 5, d.a);
}
}

View file

@ -1,26 +0,0 @@
package com.jozufozu.flywheel.core.structs;
import java.nio.ByteBuffer;
import org.lwjgl.system.MemoryUtil;
import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.backend.struct.UnsafeBufferWriter;
public abstract class ColoredLitWriterUnsafe<D extends ColoredLitPart> extends UnsafeBufferWriter<D> {
public ColoredLitWriterUnsafe(StructType<D> structType, ByteBuffer byteBuffer) {
super(structType, byteBuffer);
}
@Override
protected void writeInternal(D d) {
long ptr = writePointer;
MemoryUtil.memPutByte(ptr, d.blockLight);
MemoryUtil.memPutByte(ptr + 1, d.skyLight);
MemoryUtil.memPutByte(ptr + 2, d.r);
MemoryUtil.memPutByte(ptr + 3, d.g);
MemoryUtil.memPutByte(ptr + 4, d.b);
MemoryUtil.memPutByte(ptr + 5, d.a);
}
}

View file

@ -2,10 +2,10 @@ package com.jozufozu.flywheel.core.structs;
import com.jozufozu.flywheel.api.struct.StructType; import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.core.ComponentRegistry; import com.jozufozu.flywheel.core.ComponentRegistry;
import com.jozufozu.flywheel.core.structs.model.TransformedPart;
import com.jozufozu.flywheel.core.structs.model.TransformedType;
import com.jozufozu.flywheel.core.structs.oriented.OrientedPart; import com.jozufozu.flywheel.core.structs.oriented.OrientedPart;
import com.jozufozu.flywheel.core.structs.oriented.OrientedType; import com.jozufozu.flywheel.core.structs.oriented.OrientedType;
import com.jozufozu.flywheel.core.structs.transformed.TransformedPart;
import com.jozufozu.flywheel.core.structs.transformed.TransformedType;
public class StructTypes { public class StructTypes {
public static final StructType<TransformedPart> TRANSFORMED = ComponentRegistry.register(new TransformedType()); public static final StructType<TransformedPart> TRANSFORMED = ComponentRegistry.register(new TransformedType());

View file

@ -1,23 +0,0 @@
package com.jozufozu.flywheel.core.structs.model;
import java.nio.ByteBuffer;
import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.core.structs.ColoredLitWriterUnsafe;
import com.jozufozu.flywheel.util.extension.MatrixExtension;
public class TransformedWriterUnsafe extends ColoredLitWriterUnsafe<TransformedPart> {
public TransformedWriterUnsafe(StructType<TransformedPart> structType, ByteBuffer byteBuffer) {
super(structType, byteBuffer);
}
@Override
protected void writeInternal(TransformedPart d) {
super.writeInternal(d);
long ptr = writePointer + 6;
((MatrixExtension) (Object) d.model).flywheel$writeUnsafe(ptr);
((MatrixExtension) (Object) d.normal).flywheel$writeUnsafe(ptr + 4 * 16);
}
}

View file

@ -1,6 +0,0 @@
@ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault
package com.jozufozu.flywheel.core.structs.model;
import javax.annotation.ParametersAreNonnullByDefault;
import net.minecraft.MethodsReturnNonnullByDefault;

View file

@ -1,8 +1,5 @@
package com.jozufozu.flywheel.core.structs.oriented; package com.jozufozu.flywheel.core.structs.oriented;
import java.nio.ByteBuffer;
import com.jozufozu.flywheel.api.struct.StorageBufferWriter;
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.core.Components; import com.jozufozu.flywheel.core.Components;
@ -33,8 +30,8 @@ public class OrientedType implements StructType<OrientedPart> {
} }
@Override @Override
public StructWriter<OrientedPart> getWriter(ByteBuffer backing) { public StructWriter<OrientedPart> getWriter() {
return new OrientedWriterUnsafe(this, backing); return OrientedWriter.INSTANCE;
} }
@Override @Override
@ -53,7 +50,7 @@ public class OrientedType implements StructType<OrientedPart> {
} }
@Override @Override
public VertexTransformer<? extends OrientedPart> getVertexTransformer() { public VertexTransformer<OrientedPart> getVertexTransformer() {
return (vertexList, struct, level) -> { return (vertexList, struct, level) -> {
Vector4f pos = new Vector4f(); Vector4f pos = new Vector4f();
Vector3f normal = new Vector3f(); Vector3f normal = new Vector3f();

View file

@ -1,21 +1,15 @@
package com.jozufozu.flywheel.core.structs.oriented; package com.jozufozu.flywheel.core.structs.oriented;
import java.nio.ByteBuffer;
import org.lwjgl.system.MemoryUtil; import org.lwjgl.system.MemoryUtil;
import com.jozufozu.flywheel.api.struct.StructType; import com.jozufozu.flywheel.core.structs.ColoredLitWriter;
import com.jozufozu.flywheel.core.structs.ColoredLitWriterUnsafe;
public class OrientedWriterUnsafe extends ColoredLitWriterUnsafe<OrientedPart> { public class OrientedWriter extends ColoredLitWriter<OrientedPart> {
public OrientedWriterUnsafe(StructType<OrientedPart> structType, ByteBuffer byteBuffer) { public static final OrientedWriter INSTANCE = new OrientedWriter();
super(structType, byteBuffer);
}
@Override @Override
protected void writeInternal(OrientedPart d) { public void write(long ptr, OrientedPart d) {
long ptr = writePointer; super.write(ptr, d);
super.writeInternal(d);
MemoryUtil.memPutFloat(ptr + 6, d.posX); MemoryUtil.memPutFloat(ptr + 6, d.posX);
MemoryUtil.memPutFloat(ptr + 10, d.posY); MemoryUtil.memPutFloat(ptr + 10, d.posY);

View file

@ -1,4 +1,4 @@
package com.jozufozu.flywheel.core.structs.model; package com.jozufozu.flywheel.core.structs.transformed;
import com.jozufozu.flywheel.core.structs.ColoredLitPart; import com.jozufozu.flywheel.core.structs.ColoredLitPart;
import com.jozufozu.flywheel.core.structs.StructTypes; import com.jozufozu.flywheel.core.structs.StructTypes;

View file

@ -1,4 +1,4 @@
package com.jozufozu.flywheel.core.structs.model; package com.jozufozu.flywheel.core.structs.transformed;
import org.lwjgl.system.MemoryUtil; import org.lwjgl.system.MemoryUtil;

View file

@ -1,6 +1,4 @@
package com.jozufozu.flywheel.core.structs.model; package com.jozufozu.flywheel.core.structs.transformed;
import java.nio.ByteBuffer;
import com.jozufozu.flywheel.api.struct.StorageBufferWriter; import com.jozufozu.flywheel.api.struct.StorageBufferWriter;
import com.jozufozu.flywheel.api.struct.StructType; import com.jozufozu.flywheel.api.struct.StructType;
@ -30,8 +28,8 @@ public class TransformedType implements StructType<TransformedPart> {
} }
@Override @Override
public StructWriter<TransformedPart> getWriter(ByteBuffer backing) { public StructWriter<TransformedPart> getWriter() {
return new TransformedWriterUnsafe(this, backing); return TransformedWriter.INSTANCE;
} }
@Override @Override
@ -50,7 +48,7 @@ public class TransformedType implements StructType<TransformedPart> {
} }
@Override @Override
public VertexTransformer<? extends TransformedPart> getVertexTransformer() { public VertexTransformer<TransformedPart> getVertexTransformer() {
return (vertexList, struct, level) -> { return (vertexList, struct, level) -> {
Vector4f pos = new Vector4f(); Vector4f pos = new Vector4f();
Vector3f normal = new Vector3f(); Vector3f normal = new Vector3f();

View file

@ -0,0 +1,17 @@
package com.jozufozu.flywheel.core.structs.transformed;
import com.jozufozu.flywheel.core.structs.ColoredLitWriter;
import com.jozufozu.flywheel.util.extension.MatrixExtension;
public class TransformedWriter extends ColoredLitWriter<TransformedPart> {
public static final TransformedWriter INSTANCE = new TransformedWriter();
@Override
public void write(long ptr, TransformedPart d) {
super.write(ptr, d);
ptr += 6;
((MatrixExtension) (Object) d.model).flywheel$writeUnsafe(ptr);
((MatrixExtension) (Object) d.normal).flywheel$writeUnsafe(ptr + 4 * 16);
}
}

View file

@ -1,5 +1,5 @@
@ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault @ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault
package com.jozufozu.flywheel.backend.struct; package com.jozufozu.flywheel.core.structs.transformed;
import javax.annotation.ParametersAreNonnullByDefault; import javax.annotation.ParametersAreNonnullByDefault;

View file

@ -9,22 +9,18 @@ import com.mojang.blaze3d.systems.RenderSystem;
public class FogProvider extends UniformProvider { public class FogProvider extends UniformProvider {
@Override @Override
public int getActualByteSize() { public int getActualByteSize() {
return 16 + 8 + 4; return 16 + 8 + 4;
} }
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

@ -37,7 +37,7 @@ public class FrustumProvider extends UniformProvider {
} }
public void update(RenderContext context) { public void update(RenderContext context) {
if (buffer == null) { if (ptr == MemoryUtil.NULL) {
return; return;
} }
@ -51,8 +51,6 @@ public class FrustumProvider extends UniformProvider {
var shiftedCuller = RenderContext.createCuller(context.viewProjection(), -camX, -camY, -camZ); var shiftedCuller = RenderContext.createCuller(context.viewProjection(), -camX, -camY, -camZ);
long ptr = MemoryUtil.memAddress(buffer);
shiftedCuller.getJozuPackedPlanes(ptr); shiftedCuller.getJozuPackedPlanes(ptr);
notifier.signalChanged(); notifier.signalChanged();

View file

@ -1,20 +1,18 @@
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;
import org.lwjgl.opengl.GL32; import org.lwjgl.opengl.GL32;
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.GlBuffer;
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.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;
import com.mojang.blaze3d.platform.MemoryTracker;
public class UniformBuffer { public class UniformBuffer {
@ -33,13 +31,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 +55,7 @@ public class UniformBuffer {
allocatedProviders = builder.build(); allocatedProviders = builder.build();
data = MemoryTracker.create(totalBytes); data = MemoryBlock.mallocTracked(totalBytes);
changedBytes = new BitSet(totalBytes); changedBytes = new BitSet(totalBytes);
for (Allocated p : allocatedProviders) { for (Allocated p : allocatedProviders) {
@ -112,8 +110,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);
MatrixExtension.writeUnsafe(vp, ptr); MatrixExtension.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

@ -1,49 +1,28 @@
package com.jozufozu.flywheel.core.vertex; package com.jozufozu.flywheel.core.vertex;
import java.nio.Buffer; import com.jozufozu.flywheel.api.vertex.ReusableVertexList;
import java.nio.ByteBuffer;
import org.lwjgl.system.MemoryUtil; public abstract class AbstractVertexList implements ReusableVertexList {
protected long ptr;
import com.jozufozu.flywheel.api.vertex.VertexList; protected int vertexCount;
import com.mojang.blaze3d.platform.MemoryTracker;
import com.mojang.blaze3d.vertex.BufferBuilder;
public abstract class AbstractVertexList implements VertexList, AutoCloseable {
protected final ByteBuffer contents;
protected final long base;
protected final int vertexCount;
protected AbstractVertexList(ByteBuffer copyFrom, int vertexCount) {
this.contents = MemoryTracker.create(copyFrom.capacity());
this.vertexCount = vertexCount;
this.base = MemoryUtil.memAddress(this.contents);
init(copyFrom);
}
public AbstractVertexList(BufferBuilder builder) {
var pair = builder.popNextBuffer();
ByteBuffer copyFrom = pair.getSecond();
this.contents = MemoryTracker.create(copyFrom.capacity());
this.vertexCount = pair.getFirst().vertexCount();
this.base = MemoryUtil.memAddress(this.contents);
init(copyFrom);
}
private void init(ByteBuffer copyFrom) {
this.contents.order(copyFrom.order());
this.contents.put(copyFrom);
((Buffer) this.contents).flip();
}
@Override
public void close() {
MemoryUtil.memFree(contents);
}
@Override @Override
public int getVertexCount() { public int getVertexCount() {
return vertexCount; return vertexCount;
} }
@Override
public void setVertexCount(int vertexCount) {
this.vertexCount = vertexCount;
}
@Override
public long ptr() {
return ptr;
}
@Override
public void ptr(long ptr) {
this.ptr = ptr;
}
} }

View file

@ -1,7 +1,5 @@
package com.jozufozu.flywheel.core.vertex; package com.jozufozu.flywheel.core.vertex;
import java.nio.ByteBuffer;
import com.jozufozu.flywheel.api.vertex.VertexType; import com.jozufozu.flywheel.api.vertex.VertexType;
import com.jozufozu.flywheel.core.Components; import com.jozufozu.flywheel.core.Components;
import com.jozufozu.flywheel.core.layout.BufferLayout; import com.jozufozu.flywheel.core.layout.BufferLayout;
@ -9,7 +7,6 @@ import com.jozufozu.flywheel.core.layout.CommonItems;
import com.jozufozu.flywheel.core.source.FileResolution; import com.jozufozu.flywheel.core.source.FileResolution;
public class BlockVertex implements VertexType { public class BlockVertex implements VertexType {
public static final BufferLayout FORMAT = BufferLayout.builder() public static final BufferLayout FORMAT = BufferLayout.builder()
.addItems(CommonItems.VEC3, .addItems(CommonItems.VEC3,
CommonItems.RGBA, CommonItems.RGBA,
@ -24,18 +21,13 @@ public class BlockVertex implements VertexType {
return FORMAT; return FORMAT;
} }
@Override
public BlockWriterUnsafe createWriter(ByteBuffer buffer) {
return new BlockWriterUnsafe(this, buffer);
}
@Override
public BlockVertexListUnsafe createReader(ByteBuffer buffer, int vertexCount) {
return new BlockVertexListUnsafe(buffer, vertexCount);
}
@Override @Override
public FileResolution getLayoutShader() { public FileResolution getLayoutShader() {
return Components.Files.BLOCK_LAYOUT; return Components.Files.BLOCK_LAYOUT;
} }
@Override
public BlockVertexList createVertexList() {
return new BlockVertexList();
}
} }

View file

@ -1,72 +1,61 @@
package com.jozufozu.flywheel.core.vertex; package com.jozufozu.flywheel.core.vertex;
import org.lwjgl.system.MemoryUtil;
import com.jozufozu.flywheel.util.RenderMath; import com.jozufozu.flywheel.util.RenderMath;
import com.mojang.blaze3d.vertex.BufferBuilder;
import net.minecraft.client.renderer.texture.OverlayTexture; import net.minecraft.client.renderer.texture.OverlayTexture;
public class BlockVertexList extends AbstractVertexList { public class BlockVertexList extends AbstractVertexList {
protected static final int STRIDE = 32;
private final int stride; protected long idxPtr(int index) {
return ptr + index * STRIDE;
public BlockVertexList(BufferBuilder builder) {
super(builder);
this.stride = builder.getVertexFormat()
.getVertexSize();
}
@Override
public boolean isEmpty() {
return vertexCount == 0;
}
private int vertIdx(int vertexIndex) {
return vertexIndex * stride;
} }
@Override @Override
public float x(int index) { public float x(int index) {
return contents.getFloat(vertIdx(index)); return MemoryUtil.memGetFloat(idxPtr(index));
} }
@Override @Override
public float y(int index) { public float y(int index) {
return contents.getFloat(vertIdx(index) + 4); return MemoryUtil.memGetFloat(idxPtr(index) + 4);
} }
@Override @Override
public float z(int index) { public float z(int index) {
return contents.getFloat(vertIdx(index) + 8); return MemoryUtil.memGetFloat(idxPtr(index) + 8);
} }
@Override @Override
public byte r(int index) { public byte r(int index) {
return contents.get(vertIdx(index) + 12); return MemoryUtil.memGetByte(idxPtr(index) + 12);
} }
@Override @Override
public byte g(int index) { public byte g(int index) {
return contents.get(vertIdx(index) + 13); return MemoryUtil.memGetByte(idxPtr(index) + 13);
} }
@Override @Override
public byte b(int index) { public byte b(int index) {
return contents.get(vertIdx(index) + 14); return MemoryUtil.memGetByte(idxPtr(index) + 14);
} }
@Override @Override
public byte a(int index) { public byte a(int index) {
return contents.get(vertIdx(index) + 15); return MemoryUtil.memGetByte(idxPtr(index) + 15);
} }
@Override @Override
public float u(int index) { public float u(int index) {
return contents.getFloat(vertIdx(index) + 16); return MemoryUtil.memGetFloat(idxPtr(index) + 16);
} }
@Override @Override
public float v(int index) { public float v(int index) {
return contents.getFloat(vertIdx(index) + 20); return MemoryUtil.memGetFloat(idxPtr(index) + 20);
} }
@Override @Override
@ -76,22 +65,95 @@ public class BlockVertexList extends AbstractVertexList {
@Override @Override
public int light(int index) { public int light(int index) {
return contents.getInt(vertIdx(index) + 24); return MemoryUtil.memGetInt(idxPtr(index) + 24) << 4;
} }
@Override @Override
public float normalX(int index) { public float normalX(int index) {
return RenderMath.f(contents.get(vertIdx(index) + 28)); return RenderMath.f(MemoryUtil.memGetByte(idxPtr(index) + 28));
} }
@Override @Override
public float normalY(int index) { public float normalY(int index) {
return RenderMath.f(contents.get(vertIdx(index) + 29)); return RenderMath.f(MemoryUtil.memGetByte(idxPtr(index) + 29));
} }
@Override @Override
public float normalZ(int index) { public float normalZ(int index) {
return RenderMath.f(contents.get(vertIdx(index) + 30)); return RenderMath.f(MemoryUtil.memGetByte(idxPtr(index) + 30));
} }
@Override
public void x(int index, float x) {
MemoryUtil.memPutFloat(idxPtr(index), x);
}
@Override
public void y(int index, float y) {
MemoryUtil.memPutFloat(idxPtr(index) + 4, y);
}
@Override
public void z(int index, float z) {
MemoryUtil.memPutFloat(idxPtr(index) + 8, z);
}
@Override
public void r(int index, byte r) {
MemoryUtil.memPutByte(idxPtr(index) + 12, r);
}
@Override
public void g(int index, byte g) {
MemoryUtil.memPutByte(idxPtr(index) + 13, g);
}
@Override
public void b(int index, byte b) {
MemoryUtil.memPutByte(idxPtr(index) + 14, b);
}
@Override
public void a(int index, byte a) {
MemoryUtil.memPutByte(idxPtr(index) + 15, a);
}
@Override
public void u(int index, float u) {
MemoryUtil.memPutFloat(idxPtr(index) + 16, u);
}
@Override
public void v(int index, float v) {
MemoryUtil.memPutFloat(idxPtr(index) + 20, v);
}
@Override
public void overlay(int index, int overlay) {
}
@Override
public void light(int index, int light) {
MemoryUtil.memPutInt(idxPtr(index) + 24, light >> 4);
}
@Override
public void normalX(int index, float normalX) {
MemoryUtil.memPutByte(idxPtr(index) + 28, RenderMath.nb(normalX));
}
@Override
public void normalY(int index, float normalY) {
MemoryUtil.memPutByte(idxPtr(index) + 29, RenderMath.nb(normalY));
}
@Override
public void normalZ(int index, float normalZ) {
MemoryUtil.memPutByte(idxPtr(index) + 30, RenderMath.nb(normalZ));
}
@Override
public void shiftPtr(int vertices) {
ptr += vertices * STRIDE;
}
} }

View file

@ -1,91 +0,0 @@
package com.jozufozu.flywheel.core.vertex;
import java.nio.ByteBuffer;
import org.lwjgl.system.MemoryUtil;
import com.jozufozu.flywheel.util.RenderMath;
import net.minecraft.client.renderer.texture.OverlayTexture;
public class BlockVertexListUnsafe extends AbstractVertexList {
public BlockVertexListUnsafe(ByteBuffer copyFrom, int vertexCount) {
super(copyFrom, vertexCount);
}
private long ptr(long index) {
return base + index * 32;
}
@Override
public float x(int index) {
return MemoryUtil.memGetFloat(ptr(index));
}
@Override
public float y(int index) {
return MemoryUtil.memGetFloat(ptr(index) + 4);
}
@Override
public float z(int index) {
return MemoryUtil.memGetFloat(ptr(index) + 8);
}
@Override
public byte r(int index) {
return MemoryUtil.memGetByte(ptr(index) + 12);
}
@Override
public byte g(int index) {
return MemoryUtil.memGetByte(ptr(index) + 13);
}
@Override
public byte b(int index) {
return MemoryUtil.memGetByte(ptr(index) + 14);
}
@Override
public byte a(int index) {
return MemoryUtil.memGetByte(ptr(index) + 15);
}
@Override
public float u(int index) {
return MemoryUtil.memGetFloat(ptr(index) + 16);
}
@Override
public float v(int index) {
return MemoryUtil.memGetFloat(ptr(index) + 20);
}
@Override
public int overlay(int index) {
return OverlayTexture.NO_OVERLAY;
}
@Override
public int light(int index) {
return MemoryUtil.memGetInt(ptr(index) + 24);
}
@Override
public float normalX(int index) {
return RenderMath.f(MemoryUtil.memGetByte(ptr(index) + 28));
}
@Override
public float normalY(int index) {
return RenderMath.f(MemoryUtil.memGetByte(ptr(index) + 29));
}
@Override
public float normalZ(int index) {
return RenderMath.f(MemoryUtil.memGetByte(ptr(index) + 30));
}
}

View file

@ -1,56 +0,0 @@
package com.jozufozu.flywheel.core.vertex;
import java.nio.ByteBuffer;
import org.lwjgl.system.MemoryUtil;
import com.jozufozu.flywheel.api.vertex.VertexList;
import com.jozufozu.flywheel.util.RenderMath;
public class BlockWriterUnsafe extends VertexWriterUnsafe<BlockVertex> {
public BlockWriterUnsafe(BlockVertex type, ByteBuffer buffer) {
super(type, buffer);
}
@Override
public void writeVertex(VertexList list, int i) {
float x = list.x(i);
float y = list.y(i);
float z = list.z(i);
float xN = list.normalX(i);
float yN = list.normalY(i);
float zN = list.normalZ(i);
float u = list.u(i);
float v = list.v(i);
byte r = list.r(i);
byte g = list.g(i);
byte b = list.b(i);
byte a = list.a(i);
int light = list.light(i);
putVertex(x, y, z, u, v, r, g, b, a, light, xN, yN, zN);
}
public void putVertex(float x, float y, float z, float u, float v, byte r, byte g, byte b, byte a, int light, float nX, float nY, float nZ) {
MemoryUtil.memPutFloat(ptr, x);
MemoryUtil.memPutFloat(ptr + 4, y);
MemoryUtil.memPutFloat(ptr + 8, z);
MemoryUtil.memPutByte(ptr + 12, r);
MemoryUtil.memPutByte(ptr + 13, g);
MemoryUtil.memPutByte(ptr + 14, b);
MemoryUtil.memPutByte(ptr + 15, a);
MemoryUtil.memPutFloat(ptr + 16, u);
MemoryUtil.memPutFloat(ptr + 20, v);
MemoryUtil.memPutInt(ptr + 24, (light >> 4) & 0xF000F);
MemoryUtil.memPutByte(ptr + 28, RenderMath.nb(nX));
MemoryUtil.memPutByte(ptr + 29, RenderMath.nb(nY));
MemoryUtil.memPutByte(ptr + 30, RenderMath.nb(nZ));
ptr += 32;
}
}

View file

@ -1,9 +1,9 @@
package com.jozufozu.flywheel.backend.instancing.batching; package com.jozufozu.flywheel.core.vertex;
import com.mojang.blaze3d.vertex.VertexFormat; import com.mojang.blaze3d.vertex.VertexFormat;
import com.mojang.blaze3d.vertex.VertexFormatElement; import com.mojang.blaze3d.vertex.VertexFormatElement;
public class VertexFormatInfo { public class InferredVertexFormatInfo {
public final VertexFormat format; public final VertexFormat format;
public final int stride; public final int stride;
@ -14,7 +14,7 @@ public class VertexFormatInfo {
public final int lightOffset; public final int lightOffset;
public final int normalOffset; public final int normalOffset;
public VertexFormatInfo(VertexFormat format) { public InferredVertexFormatInfo(VertexFormat format) {
this.format = format; this.format = format;
stride = format.getVertexSize(); stride = format.getVertexSize();
@ -51,7 +51,7 @@ public class VertexFormatInfo {
this.normalOffset = normalOffset; this.normalOffset = normalOffset;
} }
protected VertexFormatInfo(VertexFormatInfo formatInfo) { protected InferredVertexFormatInfo(InferredVertexFormatInfo formatInfo) {
format = formatInfo.format; format = formatInfo.format;
stride = formatInfo.stride; stride = formatInfo.stride;
positionOffset = formatInfo.positionOffset; positionOffset = formatInfo.positionOffset;

View file

@ -0,0 +1,214 @@
package com.jozufozu.flywheel.core.vertex;
import org.lwjgl.system.MemoryUtil;
import com.jozufozu.flywheel.api.vertex.ReusableVertexList;
import com.jozufozu.flywheel.util.RenderMath;
import net.minecraft.client.renderer.texture.OverlayTexture;
public final class InferredVertexListImpl extends InferredVertexFormatInfo implements ReusableVertexList {
private long ptr;
private int vertexCount;
public InferredVertexListImpl(InferredVertexFormatInfo formatInfo) {
super(formatInfo);
}
private long idxPtr(int index) {
return ptr + index * stride;
}
@Override
public float x(int index) {
if (positionOffset < 0) return 0;
return MemoryUtil.memGetFloat(idxPtr(index) + positionOffset);
}
@Override
public float y(int index) {
if (positionOffset < 0) return 0;
return MemoryUtil.memGetFloat(idxPtr(index) + positionOffset + 4);
}
@Override
public float z(int index) {
if (positionOffset < 0) return 0;
return MemoryUtil.memGetFloat(idxPtr(index) + positionOffset + 8);
}
@Override
public byte r(int index) {
if (colorOffset < 0) return 0;
return MemoryUtil.memGetByte(idxPtr(index) + colorOffset);
}
@Override
public byte g(int index) {
if (colorOffset < 0) return 0;
return MemoryUtil.memGetByte(idxPtr(index) + colorOffset + 1);
}
@Override
public byte b(int index) {
if (colorOffset < 0) return 0;
return MemoryUtil.memGetByte(idxPtr(index) + colorOffset + 2);
}
@Override
public byte a(int index) {
if (colorOffset < 0) return 0;
return MemoryUtil.memGetByte(idxPtr(index) + colorOffset + 3);
}
@Override
public float u(int index) {
if (textureOffset < 0) return 0;
return MemoryUtil.memGetFloat(idxPtr(index) + textureOffset);
}
@Override
public float v(int index) {
if (textureOffset < 0) return 0;
return MemoryUtil.memGetFloat(idxPtr(index) + textureOffset + 4);
}
@Override
public int overlay(int index) {
if (overlayOffset < 0) return OverlayTexture.NO_OVERLAY;
return MemoryUtil.memGetInt(idxPtr(index) + overlayOffset);
}
@Override
public int light(int index) {
if (lightOffset < 0) return 0;
return MemoryUtil.memGetInt(idxPtr(index) + lightOffset);
}
@Override
public float normalX(int index) {
if (normalOffset < 0) return 0;
return RenderMath.f(MemoryUtil.memGetByte(idxPtr(index) + normalOffset));
}
@Override
public float normalY(int index) {
if (normalOffset < 0) return 0;
return RenderMath.f(MemoryUtil.memGetByte(idxPtr(index) + normalOffset + 1));
}
@Override
public float normalZ(int index) {
if (normalOffset < 0) return 0;
return RenderMath.f(MemoryUtil.memGetByte(idxPtr(index) + normalOffset + 2));
}
@Override
public void x(int index, float x) {
if (positionOffset < 0) return;
MemoryUtil.memPutFloat(idxPtr(index) + positionOffset, x);
}
@Override
public void y(int index, float y) {
if (positionOffset < 0) return;
MemoryUtil.memPutFloat(idxPtr(index) + positionOffset + 4, y);
}
@Override
public void z(int index, float z) {
if (positionOffset < 0) return;
MemoryUtil.memPutFloat(idxPtr(index) + positionOffset + 8, z);
}
@Override
public void r(int index, byte r) {
if (colorOffset < 0) return;
MemoryUtil.memPutByte(idxPtr(index) + colorOffset, r);
}
@Override
public void g(int index, byte g) {
if (colorOffset < 0) return;
MemoryUtil.memPutByte(idxPtr(index) + colorOffset + 1, g);
}
@Override
public void b(int index, byte b) {
if (colorOffset < 0) return;
MemoryUtil.memPutByte(idxPtr(index) + colorOffset + 2, b);
}
@Override
public void a(int index, byte a) {
if (colorOffset < 0) return;
MemoryUtil.memPutByte(idxPtr(index) + colorOffset + 3, a);
}
@Override
public void u(int index, float u) {
if (textureOffset < 0) return;
MemoryUtil.memPutFloat(idxPtr(index) + textureOffset, u);
}
@Override
public void v(int index, float v) {
if (textureOffset < 0) return;
MemoryUtil.memPutFloat(idxPtr(index) + textureOffset + 4, v);
}
@Override
public void overlay(int index, int overlay) {
if (overlayOffset < 0) return;
MemoryUtil.memPutInt(idxPtr(index) + overlayOffset, overlay);
}
@Override
public void light(int index, int light) {
if (lightOffset < 0) return;
MemoryUtil.memPutInt(idxPtr(index) + lightOffset, light);
}
@Override
public void normalX(int index, float normalX) {
if (normalOffset < 0) return;
MemoryUtil.memPutByte(idxPtr(index) + normalOffset, RenderMath.nb(normalX));
}
@Override
public void normalY(int index, float normalY) {
if (normalOffset < 0) return;
MemoryUtil.memPutByte(idxPtr(index) + normalOffset + 1, RenderMath.nb(normalY));
}
@Override
public void normalZ(int index, float normalZ) {
if (normalOffset < 0) return;
MemoryUtil.memPutByte(idxPtr(index) + normalOffset + 2, RenderMath.nb(normalZ));
}
@Override
public int getVertexCount() {
return vertexCount;
}
@Override
public long ptr() {
return ptr;
}
@Override
public void ptr(long ptr) {
this.ptr = ptr;
}
@Override
public void shiftPtr(int vertices) {
ptr += vertices * stride;
}
@Override
public void setVertexCount(int vertexCount) {
this.vertexCount = vertexCount;
}
}

View file

@ -0,0 +1,20 @@
package com.jozufozu.flywheel.core.vertex;
import com.jozufozu.flywheel.api.vertex.ReusableVertexList;
import com.jozufozu.flywheel.api.vertex.VertexListProvider;
import com.mojang.blaze3d.vertex.VertexFormat;
public class InferredVertexListProviderImpl implements VertexListProvider {
private final VertexFormat format;
private final InferredVertexFormatInfo formatInfo;
public InferredVertexListProviderImpl(VertexFormat format) {
this.format = format;
formatInfo = new InferredVertexFormatInfo(format);
}
@Override
public ReusableVertexList createVertexList() {
return new InferredVertexListImpl(formatInfo);
}
}

View file

@ -1,7 +1,5 @@
package com.jozufozu.flywheel.core.vertex; package com.jozufozu.flywheel.core.vertex;
import java.nio.ByteBuffer;
import com.jozufozu.flywheel.api.vertex.VertexType; import com.jozufozu.flywheel.api.vertex.VertexType;
import com.jozufozu.flywheel.core.Components; import com.jozufozu.flywheel.core.Components;
import com.jozufozu.flywheel.core.layout.BufferLayout; import com.jozufozu.flywheel.core.layout.BufferLayout;
@ -9,7 +7,6 @@ import com.jozufozu.flywheel.core.layout.CommonItems;
import com.jozufozu.flywheel.core.source.FileResolution; import com.jozufozu.flywheel.core.source.FileResolution;
public class PosTexNormalVertex implements VertexType { public class PosTexNormalVertex implements VertexType {
public static final BufferLayout FORMAT = BufferLayout.builder() public static final BufferLayout FORMAT = BufferLayout.builder()
.addItems(CommonItems.VEC3, CommonItems.UV, CommonItems.NORMAL) .addItems(CommonItems.VEC3, CommonItems.UV, CommonItems.NORMAL)
.build(); .build();
@ -19,18 +16,13 @@ public class PosTexNormalVertex implements VertexType {
return FORMAT; return FORMAT;
} }
@Override
public PosTexNormalWriterUnsafe createWriter(ByteBuffer buffer) {
return new PosTexNormalWriterUnsafe(this, buffer);
}
@Override
public PosTexNormalVertexListUnsafe createReader(ByteBuffer buffer, int vertexCount) {
return new PosTexNormalVertexListUnsafe(buffer, vertexCount);
}
@Override @Override
public FileResolution getLayoutShader() { public FileResolution getLayoutShader() {
return Components.Files.POS_TEX_NORMAL_LAYOUT; return Components.Files.POS_TEX_NORMAL_LAYOUT;
} }
@Override
public PosTexNormalVertexList createVertexList() {
return new PosTexNormalVertexList();
}
} }

View file

@ -0,0 +1,154 @@
package com.jozufozu.flywheel.core.vertex;
import org.lwjgl.system.MemoryUtil;
import com.jozufozu.flywheel.util.RenderMath;
import net.minecraft.client.renderer.texture.OverlayTexture;
public class PosTexNormalVertexList extends AbstractVertexList {
protected static final int STRIDE = 23;
protected long idxPtr(long idx) {
return ptr + idx * STRIDE;
}
@Override
public float x(int index) {
return MemoryUtil.memGetFloat(idxPtr(index));
}
@Override
public float y(int index) {
return MemoryUtil.memGetFloat(idxPtr(index) + 4);
}
@Override
public float z(int index) {
return MemoryUtil.memGetFloat(idxPtr(index) + 8);
}
@Override
public byte r(int index) {
return (byte) 0xFF;
}
@Override
public byte g(int index) {
return (byte) 0xFF;
}
@Override
public byte b(int index) {
return (byte) 0xFF;
}
@Override
public byte a(int index) {
return (byte) 0xFF;
}
@Override
public float u(int index) {
return MemoryUtil.memGetFloat(idxPtr(index) + 12);
}
@Override
public float v(int index) {
return MemoryUtil.memGetFloat(idxPtr(index) + 16);
}
@Override
public int overlay(int index) {
return OverlayTexture.NO_OVERLAY;
}
@Override
public int light(int index) {
return 0;
}
@Override
public float normalX(int index) {
return RenderMath.f(MemoryUtil.memGetByte(idxPtr(index) + 20));
}
@Override
public float normalY(int index) {
return RenderMath.f(MemoryUtil.memGetByte(idxPtr(index) + 21));
}
@Override
public float normalZ(int index) {
return RenderMath.f(MemoryUtil.memGetByte(idxPtr(index) + 22));
}
@Override
public void x(int index, float x) {
MemoryUtil.memPutFloat(idxPtr(index), x);
}
@Override
public void y(int index, float y) {
MemoryUtil.memPutFloat(idxPtr(index) + 4, y);
}
@Override
public void z(int index, float z) {
MemoryUtil.memPutFloat(idxPtr(index) + 8, z);
}
@Override
public void r(int index, byte r) {
}
@Override
public void g(int index, byte g) {
}
@Override
public void b(int index, byte b) {
}
@Override
public void a(int index, byte a) {
}
@Override
public void u(int index, float u) {
MemoryUtil.memPutFloat(idxPtr(index) + 12, u);
}
@Override
public void v(int index, float v) {
MemoryUtil.memPutFloat(idxPtr(index) + 16, v);
}
@Override
public void overlay(int index, int overlay) {
}
@Override
public void light(int index, int light) {
}
@Override
public void normalX(int index, float normalX) {
MemoryUtil.memPutByte(idxPtr(index) + 20, RenderMath.nb(normalX));
}
@Override
public void normalY(int index, float normalY) {
MemoryUtil.memPutByte(idxPtr(index) + 21, RenderMath.nb(normalY));
}
@Override
public void normalZ(int index, float normalZ) {
MemoryUtil.memPutByte(idxPtr(index) + 22, RenderMath.nb(normalZ));
}
@Override
public void shiftPtr(int vertices) {
ptr += vertices * STRIDE;
}
}

View file

@ -1,90 +0,0 @@
package com.jozufozu.flywheel.core.vertex;
import java.nio.ByteBuffer;
import org.lwjgl.system.MemoryUtil;
import com.jozufozu.flywheel.util.RenderMath;
import net.minecraft.client.renderer.texture.OverlayTexture;
public class PosTexNormalVertexListUnsafe extends AbstractVertexList {
public PosTexNormalVertexListUnsafe(ByteBuffer copyFrom, int vertexCount) {
super(copyFrom, vertexCount);
}
private long ptr(long idx) {
return base + idx * 23;
}
@Override
public float x(int index) {
return MemoryUtil.memGetFloat(ptr(index));
}
@Override
public float y(int index) {
return MemoryUtil.memGetFloat(ptr(index) + 4);
}
@Override
public float z(int index) {
return MemoryUtil.memGetFloat(ptr(index) + 8);
}
@Override
public byte r(int index) {
return (byte) 0xFF;
}
@Override
public byte g(int index) {
return (byte) 0xFF;
}
@Override
public byte b(int index) {
return (byte) 0xFF;
}
@Override
public byte a(int index) {
return (byte) 0xFF;
}
@Override
public float u(int index) {
return MemoryUtil.memGetFloat(ptr(index) + 12);
}
@Override
public float v(int index) {
return MemoryUtil.memGetFloat(ptr(index) + 16);
}
@Override
public int overlay(int index) {
return OverlayTexture.NO_OVERLAY;
}
@Override
public int light(int index) {
return 0;
}
@Override
public float normalX(int index) {
return RenderMath.f(MemoryUtil.memGetByte(ptr(index) + 20));
}
@Override
public float normalY(int index) {
return RenderMath.f(MemoryUtil.memGetByte(ptr(index) + 21));
}
@Override
public float normalZ(int index) {
return RenderMath.f(MemoryUtil.memGetByte(ptr(index) + 22));
}
}

View file

@ -1,44 +0,0 @@
package com.jozufozu.flywheel.core.vertex;
import java.nio.ByteBuffer;
import org.lwjgl.system.MemoryUtil;
import com.jozufozu.flywheel.api.vertex.VertexList;
import com.jozufozu.flywheel.util.RenderMath;
public class PosTexNormalWriterUnsafe extends VertexWriterUnsafe<PosTexNormalVertex> {
public PosTexNormalWriterUnsafe(PosTexNormalVertex type, ByteBuffer buffer) {
super(type, buffer);
}
@Override
public void writeVertex(VertexList list, int i) {
float x = list.x(i);
float y = list.y(i);
float z = list.z(i);
float u = list.u(i);
float v = list.v(i);
float xN = list.normalX(i);
float yN = list.normalY(i);
float zN = list.normalZ(i);
putVertex(x, y, z, xN, yN, zN, u, v);
}
public void putVertex(float x, float y, float z, float nX, float nY, float nZ, float u, float v) {
MemoryUtil.memPutFloat(ptr, x);
MemoryUtil.memPutFloat(ptr + 4, y);
MemoryUtil.memPutFloat(ptr + 8, z);
MemoryUtil.memPutFloat(ptr + 12, u);
MemoryUtil.memPutFloat(ptr + 16, v);
MemoryUtil.memPutByte(ptr + 20, RenderMath.nb(nX));
MemoryUtil.memPutByte(ptr + 21, RenderMath.nb(nY));
MemoryUtil.memPutByte(ptr + 22, RenderMath.nb(nZ));
ptr += 23;
}
}

View file

@ -1,41 +0,0 @@
package com.jozufozu.flywheel.core.vertex;
import java.lang.ref.Cleaner;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import org.lwjgl.system.MemoryUtil;
import com.jozufozu.flywheel.api.vertex.VertexList;
import com.jozufozu.flywheel.backend.FlywheelMemory;
import com.mojang.blaze3d.platform.MemoryTracker;
public abstract class TrackedVertexList implements VertexList, AutoCloseable {
protected final ByteBuffer contents;
protected final long base;
protected final int vertexCount;
private final Cleaner.Cleanable cleanable;
protected TrackedVertexList(ByteBuffer copyFrom, int vertexCount) {
this.contents = MemoryTracker.create(copyFrom.capacity());
this.contents.order(copyFrom.order());
this.contents.put(copyFrom);
((Buffer) this.contents).flip();
this.cleanable = FlywheelMemory.track(this, this.contents);
this.base = MemoryUtil.memAddress(this.contents);
this.vertexCount = vertexCount;
}
@Override
public void close() {
cleanable.clean();
}
@Override
public int getVertexCount() {
return vertexCount;
}
}

View file

@ -0,0 +1,55 @@
package com.jozufozu.flywheel.core.vertex;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.jetbrains.annotations.Nullable;
import com.jozufozu.flywheel.api.vertex.VertexListProvider;
import com.mojang.blaze3d.vertex.VertexFormat;
public class VertexListProviderRegistry {
private static final Map<VertexFormat, Holder> HOLDERS = new ConcurrentHashMap<>();
private static Holder getOrCreateHolder(VertexFormat format) {
return HOLDERS.computeIfAbsent(format, Holder::new);
}
public static void register(VertexFormat format, VertexListProvider provider) {
getOrCreateHolder(format).registeredProvider = provider;
}
@Nullable
public static VertexListProvider get(VertexFormat format) {
return getOrCreateHolder(format).get();
}
public static VertexListProvider getOrInfer(VertexFormat format) {
return getOrCreateHolder(format).getOrInfer();
}
private static class Holder {
public final VertexFormat format;
public VertexListProvider registeredProvider;
public VertexListProvider inferredProvider;
public Holder(VertexFormat format) {
this.format = format;
}
@Nullable
public VertexListProvider get() {
return registeredProvider;
}
public VertexListProvider getOrInfer() {
if (registeredProvider != null) {
return registeredProvider;
}
if (inferredProvider == null) {
inferredProvider = new InferredVertexListProviderImpl(format);
}
return inferredProvider;
}
}
}

View file

@ -1,33 +0,0 @@
package com.jozufozu.flywheel.core.vertex;
import java.nio.ByteBuffer;
import org.lwjgl.system.MemoryUtil;
import com.jozufozu.flywheel.api.vertex.VertexList;
import com.jozufozu.flywheel.api.vertex.VertexType;
import com.jozufozu.flywheel.api.vertex.VertexWriter;
public abstract class VertexWriterUnsafe<V extends VertexType> implements VertexWriter {
public final V type;
protected final ByteBuffer buffer;
protected long ptr;
protected VertexWriterUnsafe(V type, ByteBuffer buffer) {
this.type = type;
this.buffer = buffer;
this.ptr = MemoryUtil.memAddress(buffer);
}
@Override
public void seek(long offset) {
buffer.position((int) offset);
ptr = MemoryUtil.memAddress(buffer);
}
@Override
public VertexList intoReader(int vertices) {
return type.createReader(buffer, vertices);
}
}

View file

@ -4,9 +4,10 @@ import java.util.ArrayList;
import com.jozufozu.flywheel.Flywheel; import com.jozufozu.flywheel.Flywheel;
import com.jozufozu.flywheel.backend.Backend; import com.jozufozu.flywheel.backend.Backend;
import com.jozufozu.flywheel.backend.FlywheelMemory;
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.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,10 +25,7 @@ public class ForgeEvents {
InstancedRenderDispatcher.getDebugString(debug); InstancedRenderDispatcher.getDebugString(debug);
// TODO: compress into one line debug.add("Memory Usage: CPU: " + StringUtil.formatBytes(FlwMemoryTracker.getCPUMemory()) + ", GPU: " + StringUtil.formatBytes(FlwMemoryTracker.getGPUMemory()));
debug.add("Memory used:");
debug.add("GPU: " + FlywheelMemory.getGPUMemory());
debug.add("CPU: " + FlywheelMemory.getCPUMemory());
} }
} }

View file

@ -73,7 +73,7 @@ public class GPULightVolume extends LightVolume {
public void bind() { public void bind() {
// just in case something goes wrong, or we accidentally call this before this volume is properly disposed of. // just in case something goes wrong, or we accidentally call this before this volume is properly disposed of.
if (lightData == null || lightData.capacity() == 0) return; if (lightData == null || lightData.size() == 0) return;
textureUnit.makeActive(); textureUnit.makeActive();
glTexture.bind(); glTexture.bind();
@ -93,7 +93,7 @@ public class GPULightVolume extends LightVolume {
int sizeY = box.sizeY(); int sizeY = box.sizeY();
int sizeZ = box.sizeZ(); int sizeZ = box.sizeZ();
glTexSubImage3D(GL_TEXTURE_3D, 0, 0, 0, 0, sizeX, sizeY, sizeZ, GL30.GL_RG, GL_UNSIGNED_BYTE, lightData); glTexSubImage3D(GL_TEXTURE_3D, 0, 0, 0, 0, sizeX, sizeY, sizeZ, GL30.GL_RG, GL_UNSIGNED_BYTE, lightData.ptr());
glPixelStorei(GL_UNPACK_ALIGNMENT, 4); // 4 is the default glPixelStorei(GL_UNPACK_ALIGNMENT, 4); // 4 is the default
bufferDirty = false; bufferDirty = false;

View file

@ -1,9 +1,8 @@
package com.jozufozu.flywheel.light; package com.jozufozu.flywheel.light;
import java.nio.ByteBuffer;
import org.lwjgl.system.MemoryUtil; import org.lwjgl.system.MemoryUtil;
import com.jozufozu.flywheel.backend.memory.MemoryBlock;
import com.jozufozu.flywheel.util.box.GridAlignedBB; import com.jozufozu.flywheel.util.box.GridAlignedBB;
import com.jozufozu.flywheel.util.box.ImmutableBox; import com.jozufozu.flywheel.util.box.ImmutableBox;
@ -15,13 +14,53 @@ public class LightVolume implements ImmutableBox, LightListener {
protected final BlockAndTintGetter level; protected final BlockAndTintGetter level;
protected final GridAlignedBB box = new GridAlignedBB(); protected final GridAlignedBB box = new GridAlignedBB();
protected ByteBuffer lightData; protected MemoryBlock lightData;
public LightVolume(BlockAndTintGetter level, ImmutableBox sampleVolume) { public LightVolume(BlockAndTintGetter level, ImmutableBox sampleVolume) {
this.level = level; this.level = level;
this.setBox(sampleVolume); this.setBox(sampleVolume);
this.lightData = MemoryUtil.memAlloc(this.box.volume() * 2); this.lightData = MemoryBlock.malloc(this.box.volume() * 2);
}
@Override
public ImmutableBox getVolume() {
return box;
}
@Override
public int getMinX() {
return box.getMinX();
}
@Override
public int getMinY() {
return box.getMinY();
}
@Override
public int getMinZ() {
return box.getMinZ();
}
@Override
public int getMaxX() {
return box.getMaxX();
}
@Override
public int getMaxY() {
return box.getMaxY();
}
@Override
public int getMaxZ() {
return box.getMaxZ();
}
@Override
public boolean isListenerInvalid() {
return lightData == null;
} }
protected void setBox(ImmutableBox box) { protected void setBox(ImmutableBox box) {
@ -30,47 +69,143 @@ public class LightVolume implements ImmutableBox, LightListener {
public short getPackedLight(int x, int y, int z) { public short getPackedLight(int x, int y, int z) {
if (box.contains(x, y, z)) { if (box.contains(x, y, z)) {
return lightData.getShort(worldPosToBufferIndex(x, y, z)); return MemoryUtil.memGetShort(worldPosToPtr(x, y, z));
} else { } else {
return 0; return 0;
} }
} }
public int getMinX() {
return box.getMinX();
}
public int getMinY() {
return box.getMinY();
}
public int getMinZ() {
return box.getMinZ();
}
public int getMaxX() {
return box.getMaxX();
}
public int getMaxY() {
return box.getMaxY();
}
public int getMaxZ() {
return box.getMaxZ();
}
public void move(ImmutableBox newSampleVolume) { public void move(ImmutableBox newSampleVolume) {
if (lightData == null) return; if (lightData == null) return;
setBox(newSampleVolume); setBox(newSampleVolume);
int neededCapacity = box.volume() * 2; int neededCapacity = box.volume() * 2;
if (neededCapacity > lightData.capacity()) { if (neededCapacity > lightData.size()) {
lightData = MemoryUtil.memRealloc(lightData, neededCapacity); lightData = lightData.realloc(neededCapacity);
} }
initialize(); initialize();
} }
/**
* Completely (re)populate this volume with block and sky lighting data.
* This is expensive and should be avoided.
*/
public void initialize() {
if (lightData == null) return;
copyLight(getVolume());
markDirty();
}
protected void markDirty() {
// noop
}
public void delete() {
lightData.free();
lightData = null;
}
/**
* Copy all light from the world into this volume.
*
* @param worldVolume the region in the world to copy data from.
*/
public void copyLight(ImmutableBox worldVolume) {
BlockPos.MutableBlockPos pos = new BlockPos.MutableBlockPos();
int xShift = box.getMinX();
int yShift = box.getMinY();
int zShift = box.getMinZ();
worldVolume.forEachContained((x, y, z) -> {
pos.set(x, y, z);
int block = this.level.getBrightness(LightLayer.BLOCK, pos);
int sky = this.level.getBrightness(LightLayer.SKY, pos);
writeLight(x - xShift, y - yShift, z - zShift, block, sky);
});
}
protected void writeLight(int x, int y, int z, int block, int sky) {
byte b = (byte) ((block & 0xF) << 4);
byte s = (byte) ((sky & 0xF) << 4);
long ptr = boxPosToPtr(x, y, z);
MemoryUtil.memPutByte(ptr, b);
MemoryUtil.memPutByte(ptr + 1, s);
}
/**
* Copy block light from the world into this volume.
*
* @param worldVolume the region in the world to copy data from.
*/
public void copyBlock(ImmutableBox worldVolume) {
var pos = new BlockPos.MutableBlockPos();
int xShift = box.getMinX();
int yShift = box.getMinY();
int zShift = box.getMinZ();
worldVolume.forEachContained((x, y, z) -> {
int light = this.level.getBrightness(LightLayer.BLOCK, pos.set(x, y, z));
writeBlock(x - xShift, y - yShift, z - zShift, light);
});
}
protected void writeBlock(int x, int y, int z, int block) {
byte b = (byte) ((block & 0xF) << 4);
MemoryUtil.memPutByte(boxPosToPtr(x, y, z), b);
}
/**
* Copy sky light from the world into this volume.
*
* @param worldVolume the region in the world to copy data from.
*/
public void copySky(ImmutableBox worldVolume) {
var pos = new BlockPos.MutableBlockPos();
int xShift = box.getMinX();
int yShift = box.getMinY();
int zShift = box.getMinZ();
worldVolume.forEachContained((x, y, z) -> {
int light = this.level.getBrightness(LightLayer.SKY, pos.set(x, y, z));
writeSky(x - xShift, y - yShift, z - zShift, light);
});
}
protected void writeSky(int x, int y, int z, int sky) {
byte s = (byte) ((sky & 0xF) << 4);
MemoryUtil.memPutByte(boxPosToPtr(x, y, z) + 1, s);
}
protected long worldPosToPtr(int x, int y, int z) {
return lightData.ptr() + worldPosToPtrOffset(x, y, z);
}
protected long boxPosToPtr(int x, int y, int z) {
return lightData.ptr() + boxPosToPtrOffset(x, y, z);
}
protected int worldPosToPtrOffset(int x, int y, int z) {
x -= box.getMinX();
y -= box.getMinY();
z -= box.getMinZ();
return boxPosToPtrOffset(x, y, z);
}
protected int boxPosToPtrOffset(int x, int y, int z) {
return (x + box.sizeX() * (y + z * box.sizeY())) * 2;
}
@Override @Override
public void onLightUpdate(LightLayer type, ImmutableBox changedVolume) { public void onLightUpdate(LightLayer type, ImmutableBox changedVolume) {
if (lightData == null) return; if (lightData == null) return;
@ -96,126 +231,4 @@ public class LightVolume implements ImmutableBox, LightListener {
markDirty(); markDirty();
} }
/**
* Completely (re)populate this volume with block and sky lighting data.
* This is expensive and should be avoided.
*/
public void initialize() {
if (lightData == null) return;
copyLight(getVolume());
markDirty();
}
/**
* Copy block light from the world into this volume.
*
* @param worldVolume the region in the world to copy data from.
*/
public void copyBlock(ImmutableBox worldVolume) {
var pos = new BlockPos.MutableBlockPos();
int xShift = box.getMinX();
int yShift = box.getMinY();
int zShift = box.getMinZ();
worldVolume.forEachContained((x, y, z) -> {
int light = this.level.getBrightness(LightLayer.BLOCK, pos.set(x, y, z));
writeBlock(x - xShift, y - yShift, z - zShift, light);
});
}
/**
* Copy sky light from the world into this volume.
*
* @param worldVolume the region in the world to copy data from.
*/
public void copySky(ImmutableBox worldVolume) {
var pos = new BlockPos.MutableBlockPos();
int xShift = box.getMinX();
int yShift = box.getMinY();
int zShift = box.getMinZ();
worldVolume.forEachContained((x, y, z) -> {
int light = this.level.getBrightness(LightLayer.SKY, pos.set(x, y, z));
writeSky(x - xShift, y - yShift, z - zShift, light);
});
}
/**
* Copy all light from the world into this volume.
*
* @param worldVolume the region in the world to copy data from.
*/
public void copyLight(ImmutableBox worldVolume) {
BlockPos.MutableBlockPos pos = new BlockPos.MutableBlockPos();
int xShift = box.getMinX();
int yShift = box.getMinY();
int zShift = box.getMinZ();
worldVolume.forEachContained((x, y, z) -> {
pos.set(x, y, z);
int block = this.level.getBrightness(LightLayer.BLOCK, pos);
int sky = this.level.getBrightness(LightLayer.SKY, pos);
writeLight(x - xShift, y - yShift, z - zShift, block, sky);
});
}
public void delete() {
MemoryUtil.memFree(lightData);
lightData = null;
}
protected void markDirty() {
// noop
}
protected void writeLight(int x, int y, int z, int block, int sky) {
byte b = (byte) ((block & 0xF) << 4);
byte s = (byte) ((sky & 0xF) << 4);
int i = boxPosToBufferIndex(x, y, z);
lightData.put(i, b);
lightData.put(i + 1, s);
}
protected void writeBlock(int x, int y, int z, int block) {
byte b = (byte) ((block & 0xF) << 4);
lightData.put(boxPosToBufferIndex(x, y, z), b);
}
protected void writeSky(int x, int y, int z, int sky) {
byte b = (byte) ((sky & 0xF) << 4);
lightData.put(boxPosToBufferIndex(x, y, z) + 1, b);
}
protected int worldPosToBufferIndex(int x, int y, int z) {
x -= box.getMinX();
y -= box.getMinY();
z -= box.getMinZ();
return boxPosToBufferIndex(x, y, z);
}
protected int boxPosToBufferIndex(int x, int y, int z) {
return (x + box.sizeX() * (y + z * box.sizeY())) * 2;
}
@Override
public ImmutableBox getVolume() {
return box;
}
@Override
public boolean isListenerInvalid() {
return lightData == null;
}
} }

View file

@ -15,19 +15,33 @@ import java.util.stream.Collectors;
import org.lwjgl.system.MemoryUtil; import org.lwjgl.system.MemoryUtil;
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) + " KiB";
} else if (bytes < 1024 * 1024 * 1024) {
return THREE_DECIMAL_PLACES.format(bytes / 1024f / 1024f) + " MiB";
} else {
return THREE_DECIMAL_PLACES.format(bytes / 1024f / 1024f / 1024f) + " GiB";
}
}
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";
} }
} }
@ -56,11 +70,11 @@ public class StringUtil {
((Buffer) bytebuffer).rewind(); ((Buffer) bytebuffer).rewind();
return MemoryUtil.memASCII(bytebuffer, i); return MemoryUtil.memASCII(bytebuffer, i);
} catch (IOException ignored) { } catch (IOException ignored) {
//
} finally { } finally {
if (bytebuffer != null) { if (bytebuffer != null) {
MemoryUtil.memFree(bytebuffer); FlwMemoryTracker.freeBuffer(bytebuffer);
} }
} }
return null; return null;
@ -75,12 +89,12 @@ public class StringUtil {
} }
private static ByteBuffer readInputStream(InputStream is) throws IOException { private static ByteBuffer readInputStream(InputStream is) throws IOException {
ByteBuffer bytebuffer = MemoryUtil.memAlloc(8192); ByteBuffer bytebuffer = FlwMemoryTracker.mallocBuffer(8192);
ReadableByteChannel readablebytechannel = Channels.newChannel(is); ReadableByteChannel readablebytechannel = Channels.newChannel(is);
while (readablebytechannel.read(bytebuffer) != -1) { while (readablebytechannel.read(bytebuffer) != -1) {
if (bytebuffer.remaining() == 0) { if (bytebuffer.remaining() == 0) {
bytebuffer = MemoryUtil.memRealloc(bytebuffer, bytebuffer.capacity() * 2); bytebuffer = FlwMemoryTracker.reallocBuffer(bytebuffer, bytebuffer.capacity() * 2);
} }
} }
return bytebuffer; return bytebuffer;
@ -88,7 +102,7 @@ public class StringUtil {
private static ByteBuffer readFileInputStream(FileInputStream fileinputstream) throws IOException { private static ByteBuffer readFileInputStream(FileInputStream fileinputstream) throws IOException {
FileChannel filechannel = fileinputstream.getChannel(); FileChannel filechannel = fileinputstream.getChannel();
ByteBuffer bytebuffer = MemoryUtil.memAlloc((int) filechannel.size() + 1); ByteBuffer bytebuffer = FlwMemoryTracker.mallocBuffer((int) filechannel.size() + 1);
while (filechannel.read(bytebuffer) != -1) { while (filechannel.read(bytebuffer) != -1) {
} }

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) {
}
}
} }
} }
} }

View file

@ -15,8 +15,8 @@ import com.jozufozu.flywheel.core.Materials;
import com.jozufozu.flywheel.core.hardcoded.ModelPart; import com.jozufozu.flywheel.core.hardcoded.ModelPart;
import com.jozufozu.flywheel.core.model.SimpleLazyModel; import com.jozufozu.flywheel.core.model.SimpleLazyModel;
import com.jozufozu.flywheel.core.structs.StructTypes; import com.jozufozu.flywheel.core.structs.StructTypes;
import com.jozufozu.flywheel.core.structs.model.TransformedPart;
import com.jozufozu.flywheel.core.structs.oriented.OrientedPart; import com.jozufozu.flywheel.core.structs.oriented.OrientedPart;
import com.jozufozu.flywheel.core.structs.transformed.TransformedPart;
import com.jozufozu.flywheel.util.AnimationTickHolder; import com.jozufozu.flywheel.util.AnimationTickHolder;
import com.mojang.math.Quaternion; import com.mojang.math.Quaternion;
import com.mojang.math.Vector3f; import com.mojang.math.Vector3f;

View file

@ -12,7 +12,7 @@ import com.jozufozu.flywheel.core.model.Mesh;
import com.jozufozu.flywheel.core.model.Models; import com.jozufozu.flywheel.core.model.Models;
import com.jozufozu.flywheel.core.model.SimpleLazyModel; import com.jozufozu.flywheel.core.model.SimpleLazyModel;
import com.jozufozu.flywheel.core.structs.StructTypes; import com.jozufozu.flywheel.core.structs.StructTypes;
import com.jozufozu.flywheel.core.structs.model.TransformedPart; import com.jozufozu.flywheel.core.structs.transformed.TransformedPart;
import com.jozufozu.flywheel.util.AnimationTickHolder; import com.jozufozu.flywheel.util.AnimationTickHolder;
import com.jozufozu.flywheel.util.transform.TransformStack; import com.jozufozu.flywheel.util.transform.TransformStack;
import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.PoseStack;

View file

@ -12,7 +12,7 @@ import com.jozufozu.flywheel.core.Materials;
import com.jozufozu.flywheel.core.hardcoded.ModelPart; import com.jozufozu.flywheel.core.hardcoded.ModelPart;
import com.jozufozu.flywheel.core.model.SimpleLazyModel; import com.jozufozu.flywheel.core.model.SimpleLazyModel;
import com.jozufozu.flywheel.core.structs.StructTypes; import com.jozufozu.flywheel.core.structs.StructTypes;
import com.jozufozu.flywheel.core.structs.model.TransformedPart; import com.jozufozu.flywheel.core.structs.transformed.TransformedPart;
import com.jozufozu.flywheel.util.AnimationTickHolder; import com.jozufozu.flywheel.util.AnimationTickHolder;
import com.jozufozu.flywheel.util.transform.TransformStack; import com.jozufozu.flywheel.util.transform.TransformStack;
import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.PoseStack;

View file

@ -13,7 +13,7 @@ import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher;
import com.jozufozu.flywheel.backend.instancing.effect.Effect; import com.jozufozu.flywheel.backend.instancing.effect.Effect;
import com.jozufozu.flywheel.core.model.Models; import com.jozufozu.flywheel.core.model.Models;
import com.jozufozu.flywheel.core.structs.StructTypes; import com.jozufozu.flywheel.core.structs.StructTypes;
import com.jozufozu.flywheel.core.structs.model.TransformedPart; import com.jozufozu.flywheel.core.structs.transformed.TransformedPart;
import com.jozufozu.flywheel.event.ReloadRenderersEvent; import com.jozufozu.flywheel.event.ReloadRenderersEvent;
import com.jozufozu.flywheel.util.AnimationTickHolder; import com.jozufozu.flywheel.util.AnimationTickHolder;
import com.jozufozu.flywheel.util.box.GridAlignedBB; import com.jozufozu.flywheel.util.box.GridAlignedBB;