Work on basic memory manager for section rendering
This commit is contained in:
@@ -5,12 +5,17 @@ import me.cortex.voxy.client.core.gl.shader.ShaderType;
|
||||
import org.lwjgl.opengl.GL;
|
||||
import org.lwjgl.opengl.GL20C;
|
||||
|
||||
import static org.lwjgl.opengl.GL32.glGetInteger64;
|
||||
import static org.lwjgl.opengl.GL32C.glGetInteger64i;
|
||||
import static org.lwjgl.opengl.GL43C.GL_MAX_SHADER_STORAGE_BLOCK_SIZE;
|
||||
|
||||
public class Capabilities {
|
||||
|
||||
public static final Capabilities INSTANCE = new Capabilities();
|
||||
|
||||
public final boolean meshShaders;
|
||||
public final boolean INT64_t;
|
||||
public final long ssboMaxSize;
|
||||
public Capabilities() {
|
||||
var cap = GL.getCapabilities();
|
||||
this.meshShaders = cap.GL_NV_mesh_shader && cap.GL_NV_representative_fragment_test;
|
||||
@@ -24,6 +29,8 @@ public class Capabilities {
|
||||
uint64_t a = 1234;
|
||||
}
|
||||
""");
|
||||
|
||||
this.ssboMaxSize = glGetInteger64(GL_MAX_SHADER_STORAGE_BLOCK_SIZE);
|
||||
}
|
||||
|
||||
public static void init() {
|
||||
|
||||
@@ -21,13 +21,13 @@ import java.util.List;
|
||||
import static org.lwjgl.opengl.ARBDirectStateAccess.glGetNamedFramebufferAttachmentParameteri;
|
||||
import static org.lwjgl.opengl.GL42.*;
|
||||
|
||||
public class RenderService<T extends AbstractSectionRenderer<J>, J extends Viewport<J>> {
|
||||
private static AbstractSectionRenderer<?> createSectionRenderer() {
|
||||
return new MDICSectionRenderer();
|
||||
public class RenderService<T extends AbstractSectionRenderer<J, ?>, J extends Viewport<J>> {
|
||||
private static AbstractSectionRenderer<?, ?> createSectionRenderer(int maxSectionCount, long geometryCapacity) {
|
||||
return new MDICSectionRenderer(maxSectionCount, geometryCapacity);
|
||||
}
|
||||
|
||||
private final ViewportSelector<?> viewportSelector;
|
||||
private final AbstractSectionRenderer<J> sectionRenderer;
|
||||
private final AbstractSectionRenderer<J, ?> sectionRenderer;
|
||||
|
||||
private final HierarchicalNodeManager nodeManager;
|
||||
private final HierarchicalOcclusionTraverser traversal;
|
||||
@@ -37,15 +37,19 @@ public class RenderService<T extends AbstractSectionRenderer<J>, J extends Viewp
|
||||
|
||||
public RenderService(WorldEngine world) {
|
||||
this.modelService = new ModelBakerySubsystem(world.getMapper());
|
||||
|
||||
//Max sections: ~500k
|
||||
//Max geometry: 1 gb
|
||||
this.sectionRenderer = (T) createSectionRenderer(1<<19, 1<<30);
|
||||
|
||||
this.nodeManager = new HierarchicalNodeManager(1<<21);
|
||||
|
||||
this.sectionRenderer = (T) createSectionRenderer();
|
||||
this.viewportSelector = new ViewportSelector<>(this.sectionRenderer::createViewport);
|
||||
this.renderGen = new RenderGenerationService(world, this.modelService, VoxyConfig.CONFIG.renderThreads, this::consumeBuiltSection, this.sectionRenderer instanceof IUsesMeshlets);
|
||||
this.renderGen = new RenderGenerationService(world, this.modelService, VoxyConfig.CONFIG.renderThreads, this::consumeBuiltSection, this.sectionRenderer.getGeometryManager() instanceof IUsesMeshlets);
|
||||
|
||||
this.traversal = new HierarchicalOcclusionTraverser(this.nodeManager, 512);
|
||||
|
||||
world.setDirtyCallback(section -> System.out.println("Section updated!!: " + WorldEngine.pprintPos(section.key)));
|
||||
world.setDirtyCallback(this.nodeManager::sectionUpdate);
|
||||
|
||||
/*
|
||||
for(int x = -200; x<=200;x++) {
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
package me.cortex.voxy.client.core.rendering.geometry;
|
||||
|
||||
//The geometry renderer, takes a list of section ids to render (gpu side buffer) and renders them
|
||||
// Also manages base geometry info
|
||||
public abstract class AbstractGeometryRenderer {
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package me.cortex.voxy.client.core.rendering.hierachical2;
|
||||
|
||||
|
||||
import me.cortex.voxy.client.core.rendering.building.BuiltSection;
|
||||
import me.cortex.voxy.common.world.WorldSection;
|
||||
import me.jellysquid.mods.sodium.client.util.MathUtil;
|
||||
import org.lwjgl.system.MemoryUtil;
|
||||
|
||||
@@ -33,4 +34,9 @@ public class HierarchicalNodeManager {
|
||||
public void processBuildResult(BuiltSection section) {
|
||||
|
||||
}
|
||||
|
||||
//Called when a section is updated in the world engine
|
||||
public void sectionUpdate(WorldSection section) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,26 @@
|
||||
package me.cortex.voxy.client.core.rendering.section;
|
||||
|
||||
import me.cortex.voxy.client.core.rendering.building.BuiltSection;
|
||||
import me.jellysquid.mods.sodium.client.util.MathUtil;
|
||||
|
||||
//Does not care about the position of the sections, multiple sections that have the same position can be uploaded
|
||||
// it is up to the traversal system to manage what sections exist in the geometry buffer
|
||||
// the system is basicly "dumb" as in it just follows orders
|
||||
public abstract class AbstractSectionGeometryManager {
|
||||
public final int maxSections;
|
||||
public final long geometryCapacity;
|
||||
protected AbstractSectionGeometryManager(int maxSections, long geometryCapacity) {
|
||||
if (!MathUtil.isPowerOfTwo(maxSections)) {//TODO: Maybe not do this, as it isnt a strict requirement
|
||||
throw new IllegalArgumentException("Max sections should be a power of 2");
|
||||
}
|
||||
this.maxSections = maxSections;
|
||||
this.geometryCapacity = geometryCapacity;
|
||||
}
|
||||
|
||||
public int uploadSection(BuiltSection section) {return this.uploadReplaceSection(-1, section);}
|
||||
public abstract int uploadReplaceSection(int oldId, BuiltSection section);
|
||||
public abstract void removeSection(int id);
|
||||
public void free() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,12 +3,24 @@ package me.cortex.voxy.client.core.rendering.section;
|
||||
|
||||
import me.cortex.voxy.client.core.gl.GlBuffer;
|
||||
import me.cortex.voxy.client.core.rendering.Viewport;
|
||||
import me.cortex.voxy.client.core.rendering.geometry.OLD.AbstractGeometryManager;
|
||||
|
||||
//Takes in mesh ids from the hierachical traversal and may perform more culling then renders it
|
||||
public abstract class AbstractSectionRenderer <T extends Viewport<T>> {
|
||||
public abstract class AbstractSectionRenderer <T extends Viewport<T>, J extends AbstractSectionGeometryManager> {
|
||||
private final J geometryManager;
|
||||
protected AbstractSectionRenderer(J geometryManager) {
|
||||
this.geometryManager = geometryManager;
|
||||
}
|
||||
|
||||
public abstract void renderOpaque(T viewport);
|
||||
public abstract void buildDrawCallsAndRenderTemporal(T viewport, GlBuffer sectionRenderList);
|
||||
public abstract void renderTranslucent(T viewport);
|
||||
public abstract T createViewport();
|
||||
public abstract void free();
|
||||
public void free() {
|
||||
this.geometryManager.free();
|
||||
}
|
||||
|
||||
public J getGeometryManager() {
|
||||
return this.geometryManager;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
package me.cortex.voxy.client.core.rendering.section;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||
import me.cortex.voxy.client.core.gl.GlBuffer;
|
||||
import me.cortex.voxy.client.core.rendering.building.BuiltSection;
|
||||
import me.cortex.voxy.client.core.rendering.geometry.OLD.DefaultGeometryManager;
|
||||
import me.cortex.voxy.client.core.rendering.util.BufferArena;
|
||||
import me.cortex.voxy.common.util.HierarchicalBitSet;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.text.Text;
|
||||
import org.lwjgl.system.MemoryUtil;
|
||||
|
||||
public class BasicSectionGeometryManager extends AbstractSectionGeometryManager {
|
||||
private static final int SECTION_METADATA_SIZE = 32;
|
||||
private final GlBuffer sectionMetadataBuffer;
|
||||
private final BufferArena geometry;
|
||||
private final HierarchicalBitSet allocationSet;
|
||||
private final ObjectArrayList<SectionMeta> sectionMetadata = new ObjectArrayList<>(1<<15);
|
||||
|
||||
//These are section ids that need to be written to the gpu buffer
|
||||
private final IntOpenHashSet invalidatedSectionIds = new IntOpenHashSet();
|
||||
|
||||
public BasicSectionGeometryManager(int maxSectionCount, long geometryCapacity) {
|
||||
super(maxSectionCount, geometryCapacity);
|
||||
this.allocationSet = new HierarchicalBitSet(maxSectionCount);
|
||||
this.sectionMetadataBuffer = new GlBuffer((long) maxSectionCount * SECTION_METADATA_SIZE);
|
||||
this.geometry = new BufferArena(geometryCapacity, 8);//8 Cause a quad is 8 bytes
|
||||
}
|
||||
|
||||
@Override
|
||||
public int uploadReplaceSection(int oldId, BuiltSection sectionData) {
|
||||
//Free the old id and replace it with a new one
|
||||
// if oldId is -1, then treat it as not previously existing
|
||||
|
||||
//Free the old data if oldId is supplied
|
||||
if (oldId != -1) {
|
||||
//Its here just for future optimization potential
|
||||
this.removeSection(oldId);
|
||||
}
|
||||
|
||||
int newId = this.allocationSet.allocateNext();
|
||||
if (newId == HierarchicalBitSet.SET_FULL) {
|
||||
throw new IllegalStateException("Tried adding section when section count is already at capacity");
|
||||
}
|
||||
|
||||
|
||||
|
||||
return newId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeSection(int id) {
|
||||
if (!this.allocationSet.free(id)) {
|
||||
throw new IllegalStateException("Id was not already allocated");
|
||||
}
|
||||
var oldMetadata = this.sectionMetadata.set(id, null);
|
||||
this.geometry.free(oldMetadata.geometryPtr);
|
||||
this.invalidatedSectionIds.add(id);
|
||||
}
|
||||
|
||||
private SectionMeta createMeta(BuiltSection geometry) {
|
||||
int geometryPtr = (int) this.geometry.upload(geometry.geometryBuffer);
|
||||
if (geometryPtr == -1) {
|
||||
throw new IllegalStateException("Unable to upload section geometry as geometry buffer is full");
|
||||
}
|
||||
//8 bytes per quad
|
||||
return new SectionMeta(geometry.position, geometry.aabb, geometryPtr, (int) (geometry.geometryBuffer.size/8), geometry.offsets);
|
||||
}
|
||||
|
||||
private record SectionMeta(long position, int aabb, int geometryPtr, int itemCount, int[] offsets) {
|
||||
public void writeMetadata(long ptr) {
|
||||
//Split the long into 2 ints to solve endian issues
|
||||
MemoryUtil.memPutInt(ptr, (int) (this.position>>32)); ptr += 4;
|
||||
MemoryUtil.memPutInt(ptr, (int) this.position); ptr += 4;
|
||||
MemoryUtil.memPutInt(ptr, (int) this.aabb); ptr += 4;
|
||||
MemoryUtil.memPutInt(ptr, this.geometryPtr + this.offsets[0]); ptr += 4;
|
||||
|
||||
MemoryUtil.memPutInt(ptr, (this.offsets[1]-this.offsets[0])|((this.offsets[2]-this.offsets[1])<<16)); ptr += 4;
|
||||
MemoryUtil.memPutInt(ptr, (this.offsets[3]-this.offsets[2])|((this.offsets[4]-this.offsets[3])<<16)); ptr += 4;
|
||||
MemoryUtil.memPutInt(ptr, (this.offsets[5]-this.offsets[4])|((this.offsets[6]-this.offsets[5])<<16)); ptr += 4;
|
||||
MemoryUtil.memPutInt(ptr, (this.offsets[7]-this.offsets[6])|((this.itemCount -this.offsets[7])<<16)); ptr += 4;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void free() {
|
||||
super.free();
|
||||
this.sectionMetadataBuffer.free();
|
||||
this.geometry.free();
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
package me.cortex.voxy.client.core.rendering.section;
|
||||
|
||||
//A dummy empty interface to mark that the class uses meshlets
|
||||
//A dummy empty interface to mark that the geometry manager as using meshlets, so that the mesh generator emits as meshlets
|
||||
public interface IUsesMeshlets {
|
||||
}
|
||||
|
||||
@@ -2,10 +2,15 @@ package me.cortex.voxy.client.core.rendering.section;
|
||||
|
||||
|
||||
import me.cortex.voxy.client.core.gl.GlBuffer;
|
||||
import me.cortex.voxy.client.core.rendering.geometry.OLD.AbstractGeometryManager;
|
||||
import me.cortex.voxy.client.core.rendering.geometry.OLD.Gl46HierarchicalViewport;
|
||||
|
||||
//Uses MDIC to render the sections
|
||||
public class MDICSectionRenderer extends AbstractSectionRenderer<BasicViewport> {
|
||||
public class MDICSectionRenderer extends AbstractSectionRenderer<BasicViewport, BasicSectionGeometryManager> {
|
||||
public MDICSectionRenderer(int maxSectionCount, long geometryCapacity) {
|
||||
super(new BasicSectionGeometryManager(maxSectionCount, geometryCapacity));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderOpaque(BasicViewport viewport) {
|
||||
|
||||
@@ -28,6 +33,6 @@ public class MDICSectionRenderer extends AbstractSectionRenderer<BasicViewport>
|
||||
|
||||
@Override
|
||||
public void free() {
|
||||
|
||||
super.free();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package me.cortex.voxy.client.core.rendering.util;
|
||||
|
||||
import me.cortex.voxy.client.core.Capabilities;
|
||||
import me.cortex.voxy.client.core.gl.GlBuffer;
|
||||
import me.cortex.voxy.client.core.util.AllocationArena;
|
||||
import me.cortex.voxy.common.util.MemoryBuffer;
|
||||
@@ -17,6 +18,9 @@ public class BufferArena {
|
||||
if (capacity%elementSize != 0) {
|
||||
throw new IllegalArgumentException("Capacity not a multiple of element size");
|
||||
}
|
||||
if (capacity > Capabilities.INSTANCE.ssboMaxSize) {
|
||||
throw new IllegalArgumentException("Buffer is bigger than max ssbo size");
|
||||
}
|
||||
this.size = capacity;
|
||||
this.elementSize = elementSize;
|
||||
this.buffer = new GlBuffer(capacity);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package me.cortex.voxy.common.util;
|
||||
|
||||
public class HierarchicalBitSet {
|
||||
public static final int SET_FULL = -1;
|
||||
private final int limit;
|
||||
private int cnt;
|
||||
//If a bit is 1 it means all children are also set
|
||||
@@ -24,7 +25,7 @@ public class HierarchicalBitSet {
|
||||
return -1;
|
||||
}
|
||||
if (this.cnt+1>this.limit) {
|
||||
return -2;//Limit reached
|
||||
return -1;//Limit reached
|
||||
}
|
||||
int idx = Long.numberOfTrailingZeros(~this.A);
|
||||
long bp = this.B[idx];
|
||||
|
||||
Reference in New Issue
Block a user