_basic_ Rendering
This commit is contained in:
@@ -74,4 +74,8 @@ public class ModelBakerySubsystem {
|
||||
public void addDebugData(List<String> debug) {
|
||||
debug.add("MQ/IF/MC: " + this.blockIdQueue.size() + "/" + this.factory.getInflightCount() + "/" + this.factory.getBakedCount());//Model bake queue/in flight/model baked count
|
||||
}
|
||||
|
||||
public ModelStore getStore() {
|
||||
return this.storage;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,9 +8,11 @@ import static org.lwjgl.opengl.GL11C.GL_NEAREST;
|
||||
import static org.lwjgl.opengl.GL11C.GL_NEAREST_MIPMAP_LINEAR;
|
||||
import static org.lwjgl.opengl.GL12C.GL_TEXTURE_MAX_LOD;
|
||||
import static org.lwjgl.opengl.GL12C.GL_TEXTURE_MIN_LOD;
|
||||
import static org.lwjgl.opengl.GL33.glDeleteSamplers;
|
||||
import static org.lwjgl.opengl.GL33.glGenSamplers;
|
||||
import static org.lwjgl.opengl.GL30.glBindBufferBase;
|
||||
import static org.lwjgl.opengl.GL33.*;
|
||||
import static org.lwjgl.opengl.GL33C.glSamplerParameteri;
|
||||
import static org.lwjgl.opengl.GL43.GL_SHADER_STORAGE_BUFFER;
|
||||
import static org.lwjgl.opengl.GL45.glBindTextureUnit;
|
||||
|
||||
public class ModelStore {
|
||||
public static final int MODEL_SIZE = 64;
|
||||
@@ -39,4 +41,12 @@ public class ModelStore {
|
||||
this.textures.free();
|
||||
glDeleteSamplers(this.blockSampler);
|
||||
}
|
||||
|
||||
|
||||
public void bind(int modelBindingIndex, int colourBindingIndex, int textureBindingIndex) {
|
||||
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, modelBindingIndex, this.modelBuffer.id);
|
||||
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, colourBindingIndex, this.modelColourBuffer.id);
|
||||
glBindTextureUnit(textureBindingIndex, this.textures.id);
|
||||
glBindSampler(textureBindingIndex, this.blockSampler);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
package me.cortex.voxy.client.core.rendering;
|
||||
|
||||
import me.cortex.voxy.client.core.gl.GlBuffer;
|
||||
import me.cortex.voxy.client.core.rendering.util.UploadStream;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import org.lwjgl.system.MemoryUtil;
|
||||
|
||||
import static org.lwjgl.opengl.ARBUniformBufferObject.glBindBufferBase;
|
||||
import static org.lwjgl.opengl.GL43.GL_SHADER_STORAGE_BUFFER;
|
||||
|
||||
public class LightMapHelper {
|
||||
private static final GlBuffer LIGHT_MAP_BUFFER = new GlBuffer(256*4);
|
||||
public static void tickLightmap() {
|
||||
long upload = UploadStream.INSTANCE.upload(LIGHT_MAP_BUFFER, 0, 256*4);
|
||||
var lmt = MinecraftClient.getInstance().gameRenderer.getLightmapTextureManager().texture.getImage();
|
||||
for (int light = 0; light < 256; light++) {
|
||||
int x = light&0xF;
|
||||
int y = ((light>>4)&0xF);
|
||||
int sample = lmt.getColor(x,y);
|
||||
sample = ((sample&0xFF0000)>>16)|(sample&0xFF00)|((sample&0xFF)<<16);
|
||||
MemoryUtil.memPutInt(upload + (((x<<4)|(15-y))*4), sample|(0xFF<<28));//Skylight is inverted
|
||||
}
|
||||
}
|
||||
|
||||
public static void bind(int lightingBufferIndex) {
|
||||
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, lightingBufferIndex, LIGHT_MAP_BUFFER.id);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
package me.cortex.voxy.client.core.rendering;
|
||||
|
||||
import me.cortex.voxy.client.config.VoxyConfig;
|
||||
import me.cortex.voxy.client.core.gl.shader.PrintfInjector;
|
||||
import me.cortex.voxy.client.core.model.ModelBakerySubsystem;
|
||||
import me.cortex.voxy.client.core.model.ModelStore;
|
||||
import me.cortex.voxy.client.core.rendering.building.BuiltSection;
|
||||
import me.cortex.voxy.client.core.rendering.building.RenderGenerationService;
|
||||
import me.cortex.voxy.client.core.rendering.hierachical2.HierarchicalNodeManager;
|
||||
@@ -15,15 +15,16 @@ import me.cortex.voxy.client.core.rendering.util.UploadStream;
|
||||
import me.cortex.voxy.common.world.WorldEngine;
|
||||
import net.minecraft.client.render.Camera;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ConcurrentLinkedDeque;
|
||||
|
||||
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(int maxSectionCount, long geometryCapacity) {
|
||||
return new MDICSectionRenderer(maxSectionCount, geometryCapacity);
|
||||
public static final int STATIC_VAO = glGenVertexArrays();
|
||||
|
||||
private static AbstractSectionRenderer<?, ?> createSectionRenderer(ModelStore store, int maxSectionCount, long geometryCapacity) {
|
||||
return new MDICSectionRenderer(store, maxSectionCount, geometryCapacity);
|
||||
}
|
||||
|
||||
private final ViewportSelector<?> viewportSelector;
|
||||
@@ -34,26 +35,28 @@ public class RenderService<T extends AbstractSectionRenderer<J, ?>, J extends Vi
|
||||
private final ModelBakerySubsystem modelService;
|
||||
private final RenderGenerationService renderGen;
|
||||
|
||||
private final ConcurrentLinkedDeque<BuiltSection> sectionBuildResultQueue = new ConcurrentLinkedDeque<>();
|
||||
|
||||
|
||||
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.sectionRenderer = (T) createSectionRenderer(this.modelService.getStore(),1<<19, (1L<<30)-1024);
|
||||
|
||||
this.nodeManager = new HierarchicalNodeManager(1<<21);
|
||||
this.nodeManager = new HierarchicalNodeManager(1<<21, this.sectionRenderer.getGeometryManager());
|
||||
|
||||
this.viewportSelector = new ViewportSelector<>(this.sectionRenderer::createViewport);
|
||||
this.renderGen = new RenderGenerationService(world, this.modelService, VoxyConfig.CONFIG.renderThreads, this::consumeBuiltSection, this.sectionRenderer.getGeometryManager() instanceof IUsesMeshlets);
|
||||
this.renderGen = new RenderGenerationService(world, this.modelService, VoxyConfig.CONFIG.renderThreads, this.sectionBuildResultQueue::add, this.sectionRenderer.getGeometryManager() instanceof IUsesMeshlets);
|
||||
|
||||
this.traversal = new HierarchicalOcclusionTraverser(this.nodeManager, 512);
|
||||
|
||||
world.setDirtyCallback(this.nodeManager::sectionUpdate);
|
||||
|
||||
|
||||
for(int x = -200; x<=200;x++) {
|
||||
for (int z = -200; z <= 200; z++) {
|
||||
for(int x = -10; x<=10;x++) {
|
||||
for (int z = -10; z <= 10; z++) {
|
||||
for (int y = -3; y <= 3; y++) {
|
||||
this.renderGen.enqueueTask(0, x, y, z);
|
||||
}
|
||||
@@ -61,14 +64,13 @@ public class RenderService<T extends AbstractSectionRenderer<J, ?>, J extends Vi
|
||||
}
|
||||
}
|
||||
|
||||
//Cant do a lambda in the constructor cause "this.nodeManager" could be null??? even tho this does the exact same thing, java is stupid
|
||||
private void consumeBuiltSection(BuiltSection section) {this.nodeManager.processBuildResult(section);}
|
||||
|
||||
public void setup(Camera camera) {
|
||||
this.modelService.tick();
|
||||
}
|
||||
|
||||
public void renderFarAwayOpaque(J viewport) {
|
||||
LightMapHelper.tickLightmap();
|
||||
|
||||
//Render previous geometry with the abstract renderer
|
||||
//Execute the hieracial selector
|
||||
// render delta sections
|
||||
@@ -78,9 +80,23 @@ public class RenderService<T extends AbstractSectionRenderer<J, ?>, J extends Vi
|
||||
|
||||
this.sectionRenderer.renderOpaque(viewport);
|
||||
|
||||
|
||||
//NOTE: need to do the upload and download tick here, after the section renderer renders the world, to ensure "stable"
|
||||
// sections
|
||||
DownloadStream.INSTANCE.tick();
|
||||
|
||||
|
||||
//FIXME: we only want to tick once per full frame, this is due to how the data of sections is updated
|
||||
// we basicly need the data to stay stable from one frame to the next, till after renderOpaque
|
||||
// this is because e.g. shadows, cause this pipeline to be invoked multiple times
|
||||
// which may cause the geometry to become outdated resulting in corruption rendering in renderOpaque
|
||||
//TODO: Need to find a proper way to fix this (if there even is one)
|
||||
if (true /* firstInvocationThisFrame */) {
|
||||
DownloadStream.INSTANCE.tick();
|
||||
//Process the build results here (this is done atomically/on the render thread)
|
||||
while (!this.sectionBuildResultQueue.isEmpty()) {
|
||||
this.nodeManager.processBuildResult(this.sectionBuildResultQueue.poll());
|
||||
}
|
||||
}
|
||||
UploadStream.INSTANCE.tick();
|
||||
|
||||
glMemoryBarrier(GL_FRAMEBUFFER_BARRIER_BIT|GL_PIXEL_BUFFER_BARRIER_BIT);
|
||||
@@ -98,6 +114,7 @@ public class RenderService<T extends AbstractSectionRenderer<J, ?>, J extends Vi
|
||||
public void addDebugData(List<String> debug) {
|
||||
this.modelService.addDebugData(debug);
|
||||
this.renderGen.addDebugData(debug);
|
||||
this.sectionRenderer.addDebug(debug);
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
@@ -106,6 +123,9 @@ public class RenderService<T extends AbstractSectionRenderer<J, ?>, J extends Vi
|
||||
this.viewportSelector.free();
|
||||
this.sectionRenderer.free();
|
||||
this.traversal.free();
|
||||
//Release all the unprocessed built geometry
|
||||
this.sectionBuildResultQueue.forEach(BuiltSection::free);
|
||||
this.sectionBuildResultQueue.clear();
|
||||
}
|
||||
|
||||
public Viewport<?> getViewport() {
|
||||
|
||||
@@ -25,6 +25,7 @@ public class SharedIndexBuffer {
|
||||
|
||||
quadIndexBuff.free();
|
||||
cubeBuff.free();
|
||||
UploadStream.INSTANCE.commit();
|
||||
}
|
||||
|
||||
private SharedIndexBuffer(boolean type2) {
|
||||
|
||||
@@ -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.client.core.rendering.section.AbstractSectionGeometryManager;
|
||||
import me.cortex.voxy.common.world.WorldSection;
|
||||
import me.jellysquid.mods.sodium.client.util.MathUtil;
|
||||
import org.lwjgl.system.MemoryUtil;
|
||||
@@ -11,8 +12,9 @@ public class HierarchicalNodeManager {
|
||||
public static final int NODE_MSK = ((1<<24)-1);
|
||||
public final int maxNodeCount;
|
||||
private final long[] localNodeData;
|
||||
private final AbstractSectionGeometryManager geometryManager;
|
||||
|
||||
public HierarchicalNodeManager(int maxNodeCount) {
|
||||
public HierarchicalNodeManager(int maxNodeCount, AbstractSectionGeometryManager geometryManager) {
|
||||
if (!MathUtil.isPowerOfTwo(maxNodeCount)) {
|
||||
throw new IllegalArgumentException("Max node count must be a power of 2");
|
||||
}
|
||||
@@ -21,6 +23,7 @@ public class HierarchicalNodeManager {
|
||||
}
|
||||
this.maxNodeCount = maxNodeCount;
|
||||
this.localNodeData = new long[maxNodeCount*4];
|
||||
this.geometryManager = geometryManager;
|
||||
}
|
||||
|
||||
public void processRequestQueue(int count, long ptr) {
|
||||
@@ -32,7 +35,11 @@ public class HierarchicalNodeManager {
|
||||
}
|
||||
|
||||
public void processBuildResult(BuiltSection section) {
|
||||
section.free();
|
||||
if (!section.isEmpty()) {
|
||||
this.geometryManager.uploadSection(section);
|
||||
} else {
|
||||
section.free();
|
||||
}
|
||||
}
|
||||
|
||||
//Called when a section is updated in the world engine
|
||||
|
||||
@@ -17,10 +17,11 @@ public abstract class AbstractSectionGeometryManager {
|
||||
this.geometryCapacity = geometryCapacity;
|
||||
}
|
||||
|
||||
//Note, calling uploadSection or uploadReplaceSection will free the supplied BuiltSection
|
||||
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() {
|
||||
void tick() {}
|
||||
|
||||
}
|
||||
public void free() {}
|
||||
}
|
||||
|
||||
@@ -2,14 +2,19 @@ package me.cortex.voxy.client.core.rendering.section;
|
||||
|
||||
|
||||
import me.cortex.voxy.client.core.gl.GlBuffer;
|
||||
import me.cortex.voxy.client.core.model.ModelStore;
|
||||
import me.cortex.voxy.client.core.rendering.Viewport;
|
||||
import me.cortex.voxy.client.core.rendering.geometry.OLD.AbstractGeometryManager;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
//Takes in mesh ids from the hierachical traversal and may perform more culling then renders it
|
||||
public abstract class AbstractSectionRenderer <T extends Viewport<T>, J extends AbstractSectionGeometryManager> {
|
||||
private final J geometryManager;
|
||||
protected AbstractSectionRenderer(J geometryManager) {
|
||||
protected final J geometryManager;
|
||||
protected final ModelStore modelStore;
|
||||
protected AbstractSectionRenderer(ModelStore modelStore, J geometryManager) {
|
||||
this.geometryManager = geometryManager;
|
||||
this.modelStore = modelStore;
|
||||
}
|
||||
|
||||
public abstract void renderOpaque(T viewport);
|
||||
@@ -23,4 +28,6 @@ public abstract class AbstractSectionRenderer <T extends Viewport<T>, J extends
|
||||
public J getGeometryManager() {
|
||||
return this.geometryManager;
|
||||
}
|
||||
|
||||
public void addDebug(List<String> lines) {}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
package me.cortex.voxy.client.core.rendering.section;
|
||||
|
||||
import it.unimi.dsi.fastutil.Pair;
|
||||
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.client.core.rendering.util.UploadStream;
|
||||
import me.cortex.voxy.common.util.HierarchicalBitSet;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.text.Text;
|
||||
@@ -30,6 +32,10 @@ public class BasicSectionGeometryManager extends AbstractSectionGeometryManager
|
||||
|
||||
@Override
|
||||
public int uploadReplaceSection(int oldId, BuiltSection sectionData) {
|
||||
if (sectionData.isEmpty()) {
|
||||
throw new IllegalArgumentException("sectionData is empty, cannot upload nothing");
|
||||
}
|
||||
|
||||
//Free the old id and replace it with a new one
|
||||
// if oldId is -1, then treat it as not previously existing
|
||||
|
||||
@@ -43,9 +49,22 @@ public class BasicSectionGeometryManager extends AbstractSectionGeometryManager
|
||||
if (newId == HierarchicalBitSet.SET_FULL) {
|
||||
throw new IllegalStateException("Tried adding section when section count is already at capacity");
|
||||
}
|
||||
if (newId > this.sectionMetadata.size()) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
var newMeta = createMeta(sectionData);
|
||||
//Release the section data as its not needed anymore
|
||||
sectionData.free();
|
||||
|
||||
if (newId == this.sectionMetadata.size()) {
|
||||
this.sectionMetadata.add(newMeta);
|
||||
} else {
|
||||
this.sectionMetadata.set(newId, newMeta);
|
||||
}
|
||||
|
||||
//Invalidate the section id
|
||||
this.invalidatedSectionIds.add(newId);
|
||||
return newId;
|
||||
}
|
||||
|
||||
@@ -83,10 +102,44 @@ public class BasicSectionGeometryManager extends AbstractSectionGeometryManager
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void tick() {
|
||||
//Upload all invalidated bits
|
||||
if (!this.invalidatedSectionIds.isEmpty()) {
|
||||
for (int id : this.invalidatedSectionIds) {
|
||||
var meta = this.sectionMetadata.get(id);
|
||||
long ptr = UploadStream.INSTANCE.upload(this.sectionMetadataBuffer, (long) id *SECTION_METADATA_SIZE, SECTION_METADATA_SIZE);
|
||||
if (meta == null) {//We need to clear the gpu side buffer
|
||||
MemoryUtil.memSet(ptr, 0, SECTION_METADATA_SIZE);
|
||||
} else {
|
||||
meta.writeMetadata(ptr);
|
||||
}
|
||||
}
|
||||
this.invalidatedSectionIds.clear();
|
||||
UploadStream.INSTANCE.commit();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void free() {
|
||||
super.free();
|
||||
this.sectionMetadataBuffer.free();
|
||||
this.geometry.free();
|
||||
}
|
||||
|
||||
int getSectionCount() {
|
||||
return this.allocationSet.getCount();
|
||||
}
|
||||
|
||||
long getGeometryUsed() {
|
||||
return this.geometry.getUsedBytes();
|
||||
}
|
||||
|
||||
int getGeometryBufferId() {
|
||||
return this.geometry.id();
|
||||
}
|
||||
|
||||
int getMetadataBufferId() {
|
||||
return this.sectionMetadataBuffer.id;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,23 +2,131 @@ package me.cortex.voxy.client.core.rendering.section;
|
||||
|
||||
|
||||
import me.cortex.voxy.client.core.gl.GlBuffer;
|
||||
import me.cortex.voxy.client.core.gl.shader.Shader;
|
||||
import me.cortex.voxy.client.core.gl.shader.ShaderType;
|
||||
import me.cortex.voxy.client.core.model.ModelStore;
|
||||
import me.cortex.voxy.client.core.rendering.LightMapHelper;
|
||||
import me.cortex.voxy.client.core.rendering.RenderService;
|
||||
import me.cortex.voxy.client.core.rendering.SharedIndexBuffer;
|
||||
import me.cortex.voxy.client.core.rendering.geometry.OLD.AbstractFarWorldRenderer;
|
||||
import me.cortex.voxy.client.core.rendering.geometry.OLD.AbstractGeometryManager;
|
||||
import me.cortex.voxy.client.core.rendering.geometry.OLD.Gl46HierarchicalViewport;
|
||||
import me.cortex.voxy.client.core.rendering.util.UploadStream;
|
||||
import me.cortex.voxy.client.mixin.joml.AccessFrustumIntersection;
|
||||
import net.minecraft.client.render.RenderLayer;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import org.joml.Matrix4f;
|
||||
import org.joml.Vector3f;
|
||||
import org.lwjgl.system.MemoryUtil;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.lwjgl.opengl.ARBIndirectParameters.GL_PARAMETER_BUFFER_ARB;
|
||||
import static org.lwjgl.opengl.GL11.*;
|
||||
import static org.lwjgl.opengl.GL15.GL_ELEMENT_ARRAY_BUFFER;
|
||||
import static org.lwjgl.opengl.GL15.glBindBuffer;
|
||||
import static org.lwjgl.opengl.GL30.glBindBufferBase;
|
||||
import static org.lwjgl.opengl.GL30.glBindVertexArray;
|
||||
import static org.lwjgl.opengl.GL31.GL_UNIFORM_BUFFER;
|
||||
import static org.lwjgl.opengl.GL32.glDrawElementsInstancedBaseVertex;
|
||||
import static org.lwjgl.opengl.GL33.glBindSampler;
|
||||
import static org.lwjgl.opengl.GL40C.GL_DRAW_INDIRECT_BUFFER;
|
||||
import static org.lwjgl.opengl.GL42.glDrawElementsInstancedBaseVertexBaseInstance;
|
||||
import static org.lwjgl.opengl.GL43.GL_SHADER_STORAGE_BUFFER;
|
||||
import static org.lwjgl.opengl.GL45.glBindTextureUnit;
|
||||
|
||||
//Uses MDIC to render the sections
|
||||
public class MDICSectionRenderer extends AbstractSectionRenderer<BasicViewport, BasicSectionGeometryManager> {
|
||||
public MDICSectionRenderer(int maxSectionCount, long geometryCapacity) {
|
||||
super(new BasicSectionGeometryManager(maxSectionCount, geometryCapacity));
|
||||
private final Shader terrainShader = Shader.make()
|
||||
.add(ShaderType.VERTEX, "voxy:lod/gl46/quads2.vert")
|
||||
.add(ShaderType.FRAGMENT, "voxy:lod/gl46/quads.frag")
|
||||
.compile();
|
||||
private final GlBuffer uniform = new GlBuffer(1024).zero();
|
||||
|
||||
public MDICSectionRenderer(ModelStore modelStore, int maxSectionCount, long geometryCapacity) {
|
||||
super(modelStore, new BasicSectionGeometryManager(maxSectionCount, geometryCapacity));
|
||||
}
|
||||
|
||||
|
||||
private void uploadUniformBuffer(BasicViewport viewport) {
|
||||
long ptr = UploadStream.INSTANCE.upload(this.uniform, 0, 1024);
|
||||
|
||||
int sx = MathHelper.floor(viewport.cameraX)>>5;
|
||||
int sy = MathHelper.floor(viewport.cameraY)>>5;
|
||||
int sz = MathHelper.floor(viewport.cameraZ)>>5;
|
||||
|
||||
var mat = new Matrix4f(viewport.projection).mul(viewport.modelView);
|
||||
var innerTranslation = new Vector3f((float) (viewport.cameraX-(sx<<5)), (float) (viewport.cameraY-(sy<<5)), (float) (viewport.cameraZ-(sz<<5)));
|
||||
mat.translate(-innerTranslation.x, -innerTranslation.y, -innerTranslation.z);
|
||||
mat.getToAddress(ptr); ptr += 4*4*4;
|
||||
MemoryUtil.memPutInt(ptr, sx); ptr += 4;
|
||||
MemoryUtil.memPutInt(ptr, sy); ptr += 4;
|
||||
MemoryUtil.memPutInt(ptr, sz); ptr += 4;
|
||||
MemoryUtil.memPutInt(ptr, viewport.frameId++); ptr += 4;
|
||||
innerTranslation.getToAddress(ptr); ptr += 4*3;
|
||||
|
||||
UploadStream.INSTANCE.commit();
|
||||
}
|
||||
|
||||
|
||||
private void bindRenderingBuffers() {
|
||||
glBindBufferBase(GL_UNIFORM_BUFFER, 0, this.uniform.id);
|
||||
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, this.geometryManager.getGeometryBufferId());
|
||||
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, this.geometryManager.getMetadataBufferId());
|
||||
this.modelStore.bind(3, 4, 0);
|
||||
LightMapHelper.bind(5);
|
||||
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, SharedIndexBuffer.INSTANCE.id());
|
||||
//glBindBuffer(GL_DRAW_INDIRECT_BUFFER, this.glCommandBuffer.id);
|
||||
//glBindBuffer(GL_PARAMETER_BUFFER_ARB, this.glCommandCountBuffer.id);
|
||||
|
||||
}
|
||||
|
||||
//Prep the terrain draw calls for this frame, also sets up the
|
||||
// remaining render pipeline for this frame
|
||||
private void prepTerrainCallsAndPrep() {
|
||||
|
||||
}
|
||||
|
||||
private void renderTerrain() {
|
||||
RenderLayer.getCutoutMipped().startDrawing();
|
||||
|
||||
glDisable(GL_CULL_FACE);
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
this.terrainShader.bind();
|
||||
glBindVertexArray(RenderService.STATIC_VAO);//Needs to be before binding
|
||||
this.bindRenderingBuffers();
|
||||
|
||||
glDrawElementsInstancedBaseVertexBaseInstance(GL_TRIANGLES, 1000*6, GL_UNSIGNED_SHORT, 0,1,0,0);
|
||||
|
||||
|
||||
glEnable(GL_CULL_FACE);
|
||||
|
||||
|
||||
glBindVertexArray(0);
|
||||
glBindSampler(0, 0);
|
||||
glBindTextureUnit(0, 0);
|
||||
RenderLayer.getCutoutMipped().endDrawing();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderOpaque(BasicViewport viewport) {
|
||||
|
||||
if (this.geometryManager.getSectionCount() == 0) return;
|
||||
this.uploadUniformBuffer(viewport);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void buildDrawCallsAndRenderTemporal(BasicViewport viewport, GlBuffer sectionRenderList) {
|
||||
//Can do a sneeky trick, since the sectionRenderList is a list to things to render, it invokes the culler
|
||||
// which only marks visible sections
|
||||
|
||||
|
||||
|
||||
//Tick the geometry manager to upload all invalidated metadata changes to the gpu
|
||||
this.geometryManager.tick();
|
||||
|
||||
|
||||
this.renderTerrain();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -26,6 +134,12 @@ public class MDICSectionRenderer extends AbstractSectionRenderer<BasicViewport,
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addDebug(List<String> lines) {
|
||||
super.addDebug(lines);
|
||||
lines.add("NC/GS: " + this.geometryManager.getSectionCount() + "/" + (this.geometryManager.getGeometryUsed()/(1024*1024)));//Node count/geometry size (MB)
|
||||
}
|
||||
|
||||
@Override
|
||||
public BasicViewport createViewport() {
|
||||
return new BasicViewport();
|
||||
@@ -34,5 +148,7 @@ public class MDICSectionRenderer extends AbstractSectionRenderer<BasicViewport,
|
||||
@Override
|
||||
public void free() {
|
||||
super.free();
|
||||
this.uniform.free();
|
||||
this.terrainShader.free();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ public class BufferArena {
|
||||
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");
|
||||
throw new IllegalArgumentException("Buffer is bigger than max ssbo size (requested " + capacity + " but has max of " + Capabilities.INSTANCE.ssboMaxSize+")");
|
||||
}
|
||||
this.size = capacity;
|
||||
this.elementSize = elementSize;
|
||||
@@ -58,4 +58,8 @@ public class BufferArena {
|
||||
public float usage() {
|
||||
return (float) ((double)this.used/(this.buffer.size()/this.elementSize));
|
||||
}
|
||||
|
||||
public long getUsedBytes() {
|
||||
return this.used*this.elementSize;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,6 +118,7 @@ public class WorldEngine {
|
||||
public void insertUpdate(VoxelizedSection section) {//TODO: add a bitset of levels to update and if it should force update
|
||||
//The >>1 is cause the world sections size is 32x32x32 vs the 16x16x16 of the voxelized section
|
||||
for (int lvl = 0; lvl < this.maxMipLevels; lvl++) {
|
||||
int nonAirCountDelta = 0;
|
||||
var worldSection = this.acquire(lvl, section.x >> (lvl + 1), section.y >> (lvl + 1), section.z >> (lvl + 1));
|
||||
int msk = (1<<(lvl+1))-1;
|
||||
int bx = (section.x&msk)<<(4-lvl);
|
||||
@@ -129,11 +130,19 @@ public class WorldEngine {
|
||||
for (int x = bx; x < (16>>lvl)+bx; x++) {
|
||||
long newId = section.get(lvl, x-bx, y-by, z-bz);
|
||||
long oldId = worldSection.set(x, y, z, newId);
|
||||
nonAirCountDelta += Mapper.isAir(oldId)==Mapper.isAir(newId)?0:(Mapper.isAir(newId)?-1:1 );
|
||||
didChange |= newId != oldId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Branch into 2 paths, if at lod 0, update the atomic count, if that update resulted in a state transition
|
||||
// then aquire the next lod, lock it, recheck our counter, if it is still ok, then atomically update the parent metadata
|
||||
//if not lod 0 check that the current occupied state matches the parent lod bit
|
||||
// if it doesnt, aquire and lock the next lod level
|
||||
// and do the update propagation
|
||||
|
||||
|
||||
//Need to release the section after using it
|
||||
if (didChange) {
|
||||
//Mark the section as dirty (enqueuing saving and geometry rebuild) and move to parent mip level
|
||||
|
||||
@@ -6,6 +6,8 @@ import java.util.Arrays;
|
||||
import java.util.Deque;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.concurrent.atomic.AtomicLongFieldUpdater;
|
||||
|
||||
//Represents a loaded world section at a specific detail level
|
||||
// holds a 32x32x32 region of detail
|
||||
@@ -21,7 +23,16 @@ public final class WorldSection {
|
||||
public final int z;
|
||||
public final long key;
|
||||
|
||||
|
||||
//Serialized states
|
||||
long metadata;
|
||||
long[] data = null;
|
||||
|
||||
|
||||
//Computed on load, updated on insertion
|
||||
private final AtomicInteger nonAirCount = new AtomicInteger(0);
|
||||
|
||||
|
||||
private final ActiveSectionTracker tracker;
|
||||
public final AtomicBoolean inSaveQueue = new AtomicBoolean();
|
||||
|
||||
|
||||
@@ -52,7 +52,9 @@ public class Mapper {
|
||||
|
||||
|
||||
public static boolean isAir(long id) {
|
||||
return ((id>>27)&((1<<20)-1)) == 0;
|
||||
int bId = getBlockId(id);
|
||||
//Note: air can mean void, cave or normal air, as the block state is remapped during ingesting
|
||||
return bId == 0;
|
||||
}
|
||||
|
||||
public static int getBlockId(long id) {
|
||||
@@ -185,6 +187,9 @@ public class Mapper {
|
||||
|
||||
//TODO: replace lambda with a class cached lambda ref (cause doing this:: still does a lambda allocation)
|
||||
public int getIdForBlockState(BlockState state) {
|
||||
if (state.isAir()) {
|
||||
return 0;
|
||||
}
|
||||
return this.block2stateEntry.computeIfAbsent(state, this::registerNewBlockState).id;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,15 +1,10 @@
|
||||
#line 1
|
||||
struct Frustum {
|
||||
vec4 planes[6];
|
||||
};
|
||||
|
||||
layout(binding = 0, std140) uniform SceneUniform {
|
||||
mat4 MVP;
|
||||
ivec3 baseSectionPos;
|
||||
int sectionCount;
|
||||
Frustum frustum;
|
||||
vec3 cameraSubPos;
|
||||
uint frameId;
|
||||
vec3 cameraSubPos;
|
||||
};
|
||||
|
||||
struct BlockModel {
|
||||
@@ -39,44 +34,63 @@ struct DrawCommand {
|
||||
uint baseInstance;
|
||||
};
|
||||
|
||||
layout(binding = 0) uniform sampler2D blockModelAtlas;
|
||||
|
||||
#ifdef BLOCK_MODEL_TEXTURE_BINDING
|
||||
layout(binding = BLOCK_MODEL_TEXTURE_BINDING) uniform sampler2D blockModelAtlas;
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef Quad
|
||||
#define Quad ivec2
|
||||
#endif
|
||||
layout(binding = 1, std430) readonly restrict buffer QuadBuffer {
|
||||
#ifdef QUAD_BUFFER_BINDING
|
||||
layout(binding = QUAD_BUFFER_BINDING, std430) readonly restrict buffer QuadBuffer {
|
||||
Quad quadData[];
|
||||
};
|
||||
#endif
|
||||
|
||||
layout(binding = 2, std430) writeonly restrict buffer DrawBuffer {
|
||||
#ifdef DRAW_BUFFER_BINDING
|
||||
layout(binding = DRAW_BUFFER_BINDING, std430) writeonly restrict buffer DrawBuffer {
|
||||
DrawCommand cmdBuffer[];
|
||||
};
|
||||
#endif
|
||||
|
||||
layout(binding = 3, std430) restrict buffer DrawCommandCountBuffer {
|
||||
#ifdef DRAW_COUNT_BUFFER_BINDING
|
||||
layout(binding = DRAW_COUNT_BUFFER_BINDING, std430) restrict buffer DrawCommandCountBuffer {
|
||||
uint opaqueDrawCount;
|
||||
uint translucentDrawCount;
|
||||
};
|
||||
#endif
|
||||
|
||||
layout(binding = 4, std430) readonly restrict buffer SectionBuffer {
|
||||
#ifdef SECTION_METADA_BUFFER_BINDING
|
||||
layout(binding = SECTION_METADA_BUFFER_BINDING, std430) readonly restrict buffer SectionBuffer {
|
||||
SectionMeta sectionData[];
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifndef VISIBILITY_ACCESS
|
||||
#define VISIBILITY_ACCESS readonly
|
||||
#endif
|
||||
layout(binding = 5, std430) VISIBILITY_ACCESS restrict buffer VisibilityBuffer {
|
||||
#ifdef VISIBILITY_BUFFER_BINDING
|
||||
layout(binding = VISIBILITY_BUFFER_BINDING, std430) VISIBILITY_ACCESS restrict buffer VisibilityBuffer {
|
||||
uint visibilityData[];
|
||||
};
|
||||
#endif
|
||||
|
||||
layout(binding = 6, std430) readonly restrict buffer ModelBuffer {
|
||||
#ifdef MODEL_BUFFER_BINDING
|
||||
layout(binding = MODEL_BUFFER_BINDING, std430) readonly restrict buffer ModelBuffer {
|
||||
BlockModel modelData[];
|
||||
};
|
||||
#endif
|
||||
|
||||
layout(binding = 7, std430) readonly restrict buffer ModelColourBuffer {
|
||||
#ifdef MODEL_COLOUR_BUFFER_BINDING
|
||||
layout(binding = MODEL_COLOUR_BUFFER_BINDING, std430) readonly restrict buffer ModelColourBuffer {
|
||||
uint colourData[];
|
||||
};
|
||||
#endif
|
||||
|
||||
layout(binding = 8, std430) readonly restrict buffer LightingBuffer {
|
||||
#ifdef LIGHTING_BUFFER_BINDING
|
||||
layout(binding = LIGHTING_BUFFER_BINDING, std430) readonly restrict buffer LightingBuffer {
|
||||
uint lightData[];
|
||||
};
|
||||
|
||||
@@ -86,5 +100,5 @@ vec4 getLighting(uint index) {
|
||||
arr = arr & uvec4(0xFF);
|
||||
return vec4(arr)*vec4(1.0f/255.0f);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ layout(local_size_x = 128) in;
|
||||
|
||||
#import <voxy:lod/quad_format.glsl>
|
||||
#import <voxy:lod/gl46/bindings.glsl>
|
||||
#import <voxy:lod/gl46/frustum.glsl>
|
||||
#import <voxy:lod/section.glsl>
|
||||
#line 11
|
||||
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
bool testFrustumPoint(vec4 plane, vec3 min, vec3 max) {
|
||||
vec3 point = mix(max, min, lessThan(plane.xyz, vec3(0))) * plane.xyz;
|
||||
return (point.x + point.y + point.z) >= -plane.w;
|
||||
}
|
||||
|
||||
bool testFrustum(Frustum frust, vec3 min, vec3 max) {
|
||||
return testFrustumPoint(frust.planes[0], min, max) &&
|
||||
testFrustumPoint(frust.planes[1], min, max) &&
|
||||
testFrustumPoint(frust.planes[2], min, max) &&
|
||||
testFrustumPoint(frust.planes[3], min, max) &&
|
||||
testFrustumPoint(frust.planes[4], min, max) &&
|
||||
testFrustumPoint(frust.planes[5], min, max);
|
||||
}
|
||||
@@ -1,10 +1,16 @@
|
||||
#version 460 core
|
||||
#extension GL_ARB_gpu_shader_int64 : enable
|
||||
|
||||
#define QUAD_BUFFER_BINDING 1
|
||||
#define SECTION_METADA_BUFFER_BINDING 2
|
||||
#define MODEL_BUFFER_BINDING 3
|
||||
#define MODEL_COLOUR_BUFFER_BINDING 4
|
||||
#define LIGHTING_BUFFER_BINDING 5
|
||||
|
||||
|
||||
#import <voxy:lod/quad_format.glsl>
|
||||
#import <voxy:lod/gl46/bindings.glsl>
|
||||
#import <voxy:lod/block_model.glsl>
|
||||
#line 8
|
||||
|
||||
//#define DEBUG_RENDER
|
||||
|
||||
|
||||
Reference in New Issue
Block a user