diff --git a/src/main/java/me/cortex/voxy/client/config/VoxyConfig.java b/src/main/java/me/cortex/voxy/client/config/VoxyConfig.java index a4bba2bd..f70afefc 100644 --- a/src/main/java/me/cortex/voxy/client/config/VoxyConfig.java +++ b/src/main/java/me/cortex/voxy/client/config/VoxyConfig.java @@ -50,7 +50,9 @@ public class VoxyConfig { e.printStackTrace(); } } - return new VoxyConfig(); + var config = new VoxyConfig(); + config.defaultSaveConfig = ContextSelectionSystem.DEFAULT_STORAGE_CONFIG; + return config; } public void save() { //Unsafe, todo: fixme! needs to be atomic! diff --git a/src/main/java/me/cortex/voxy/client/core/AbstractRenderWorldInteractor.java b/src/main/java/me/cortex/voxy/client/core/AbstractRenderWorldInteractor.java deleted file mode 100644 index 21d2af57..00000000 --- a/src/main/java/me/cortex/voxy/client/core/AbstractRenderWorldInteractor.java +++ /dev/null @@ -1,17 +0,0 @@ -package me.cortex.voxy.client.core; - -import me.cortex.voxy.client.core.rendering.building.BuiltSection; -import me.cortex.voxy.client.core.rendering.building.RenderGenerationService; -import me.cortex.voxy.common.world.WorldSection; - -public interface AbstractRenderWorldInteractor { - void processBuildResult(BuiltSection section); - - void sectionUpdated(WorldSection worldSection); - - void setRenderGen(RenderGenerationService renderService); - - void initPosition(int x, int z); - - void setCenter(int x, int y, int z); -} diff --git a/src/main/java/me/cortex/voxy/client/core/DefaultRenderWorldInteractor.java b/src/main/java/me/cortex/voxy/client/core/DefaultRenderWorldInteractor.java deleted file mode 100644 index 6901f809..00000000 --- a/src/main/java/me/cortex/voxy/client/core/DefaultRenderWorldInteractor.java +++ /dev/null @@ -1,66 +0,0 @@ -package me.cortex.voxy.client.core; - -import me.cortex.voxy.client.config.VoxyConfig; -import me.cortex.voxy.client.core.rendering.AbstractFarWorldRenderer; -import me.cortex.voxy.client.core.rendering.IRenderInterface; -import me.cortex.voxy.client.core.rendering.RenderTracker; -import me.cortex.voxy.client.core.rendering.building.BuiltSection; -import me.cortex.voxy.client.core.rendering.building.RenderGenerationService; -import me.cortex.voxy.client.saver.ContextSelectionSystem; -import me.cortex.voxy.common.world.WorldEngine; -import me.cortex.voxy.common.world.WorldSection; -import net.minecraft.client.MinecraftClient; - -public class DefaultRenderWorldInteractor implements AbstractRenderWorldInteractor { - private final DistanceTracker distanceTracker; - private final RenderTracker renderTracker; - - - public DefaultRenderWorldInteractor(ContextSelectionSystem.WorldConfig cfg, WorldEngine world, IRenderInterface renderer) { - this.renderTracker = new RenderTracker(world, (AbstractFarWorldRenderer) renderer); - - //To get to chunk scale multiply the scale by 2, the scale is after how many chunks does the lods halve - int q = VoxyConfig.CONFIG.qualityScale; - int minY = MinecraftClient.getInstance().world.getBottomSectionCoord()/2; - int maxY = MinecraftClient.getInstance().world.getTopSectionCoord()/2; - - if (cfg.minYOverride != Integer.MAX_VALUE) { - minY = cfg.minYOverride; - } - - if (cfg.maxYOverride != Integer.MIN_VALUE) { - maxY = cfg.maxYOverride; - } - - this.distanceTracker = new DistanceTracker(this.renderTracker, new int[]{q,q,q,q}, - (VoxyConfig.CONFIG.renderDistance<0?VoxyConfig.CONFIG.renderDistance:((VoxyConfig.CONFIG.renderDistance+1)/2)), - minY, maxY); - - System.out.println("Distance tracker initialized"); - } - - @Override - public void processBuildResult(BuiltSection section) { - this.renderTracker.processBuildResult(section); - } - - @Override - public void sectionUpdated(WorldSection worldSection) { - this.renderTracker.sectionUpdated(worldSection); - } - - @Override - public void setRenderGen(RenderGenerationService renderService) { - this.renderTracker.setRenderGen(renderService); - } - - @Override - public void initPosition(int x, int z) { - this.distanceTracker.init(x,z); - } - - @Override - public void setCenter(int x, int y, int z) { - this.distanceTracker.setCenter(x,y,z); - } -} diff --git a/src/main/java/me/cortex/voxy/client/core/ViewportSelector.java b/src/main/java/me/cortex/voxy/client/core/ViewportSelector.java index ebfe9b4a..f791e6be 100644 --- a/src/main/java/me/cortex/voxy/client/core/ViewportSelector.java +++ b/src/main/java/me/cortex/voxy/client/core/ViewportSelector.java @@ -1,6 +1,5 @@ package me.cortex.voxy.client.core; -import me.cortex.voxy.client.core.rendering.AbstractFarWorldRenderer; import me.cortex.voxy.client.core.rendering.Viewport; import net.fabricmc.loader.api.FabricLoader; import org.vivecraft.client_vr.ClientDataHolderVR; @@ -9,7 +8,7 @@ import java.util.HashMap; import java.util.Map; import java.util.function.Supplier; -public class ViewportSelector { +public class ViewportSelector > { public static final boolean VIVECRAFT_INSTALLED = FabricLoader.getInstance().isModLoaded("vivecraft"); private final Supplier creator; diff --git a/src/main/java/me/cortex/voxy/client/core/VoxelCore.java b/src/main/java/me/cortex/voxy/client/core/VoxelCore.java index 22c05b5f..203893d7 100644 --- a/src/main/java/me/cortex/voxy/client/core/VoxelCore.java +++ b/src/main/java/me/cortex/voxy/client/core/VoxelCore.java @@ -3,7 +3,7 @@ package me.cortex.voxy.client.core; import com.mojang.blaze3d.systems.RenderSystem; import me.cortex.voxy.client.Voxy; import me.cortex.voxy.client.config.VoxyConfig; -import me.cortex.voxy.client.core.model.ModelManager; +import me.cortex.voxy.client.core.model.OffThreadModelBakerySystem; import me.cortex.voxy.client.core.rendering.*; import me.cortex.voxy.client.core.rendering.building.RenderGenerationService; import me.cortex.voxy.client.core.rendering.post.PostProcessing; @@ -54,11 +54,8 @@ import static org.lwjgl.opengl.GL30C.GL_DRAW_FRAMEBUFFER_BINDING; //REMOVE setRenderGen like holy hell public class VoxelCore { private final WorldEngine world; - private final RenderGenerationService renderGen; - private final ModelManager modelManager; - private final AbstractRenderWorldInteractor interactor; - private final IRenderInterface renderer; + private final RenderService renderer; private final ViewportSelector viewportSelector; private final PostProcessing postProcessing; @@ -73,46 +70,12 @@ public class VoxelCore { //Trigger the shared index buffer loading SharedIndexBuffer.INSTANCE.id(); Capabilities.init();//Ensure clinit is called - this.modelManager = new ModelManager(16); - this.renderer = this.createRenderBackend(); + + this.renderer = new RenderService(this.world); System.out.println("Using " + this.renderer.getClass().getSimpleName()); this.viewportSelector = new ViewportSelector<>(this.renderer::createViewport); - - //Ungodly hacky code - if (this.renderer instanceof AbstractRenderWorldInteractor) { - this.interactor = (AbstractRenderWorldInteractor) this.renderer; - } else { - this.interactor = new DefaultRenderWorldInteractor(cfg, this.world, this.renderer); - } - System.out.println("Renderer initialized"); - - this.renderGen = new RenderGenerationService(this.world, this.modelManager, VoxyConfig.CONFIG.renderThreads, this.interactor::processBuildResult, this.renderer.generateMeshlets()); - this.world.setDirtyCallback(this.interactor::sectionUpdated); - this.interactor.setRenderGen(this.renderGen); - System.out.println("Render tracker and generator initialized"); - - - this.postProcessing = new PostProcessing(); - this.world.getMapper().setCallbacks(this.renderer::addBlockState, this.renderer::addBiome); - - - ////Resave the db incase it failed a recovery - //this.world.getMapper().forceResaveStates(); - - var biomeRegistry = MinecraftClient.getInstance().world.getRegistryManager().get(RegistryKeys.BIOME); - for (var biome : this.world.getMapper().getBiomeEntries()) { - //this.renderer.getModelManager().addBiome(biome.id, biomeRegistry.get(new Identifier(biome.biome))); - this.renderer.addBiome(biome); - } - - for (var state : this.world.getMapper().getStateEntries()) { - //this.renderer.getModelManager().addEntry(state.id, state.state); - this.renderer.addBlockState(state); - } - //this.renderer.getModelManager().updateEntry(0, Blocks.GRASS_BLOCK.getDefaultState()); - System.out.println("Voxy core initialized"); } @@ -120,16 +83,8 @@ public class VoxelCore { this.world.ingestService.enqueueIngest(worldChunk); } - boolean firstTime = true; public void renderSetup(Frustum frustum, Camera camera) { - if (this.firstTime) { - //TODO: remove initPosition - this.interactor.initPosition(camera.getBlockPos().getX(), camera.getBlockPos().getZ()); - this.firstTime = false; - //this.renderTracker.addLvl0(0,6,0); - } - this.interactor.setCenter(camera.getBlockPos().getX(), camera.getBlockPos().getY(), camera.getBlockPos().getZ()); - this.renderer.setupRender(frustum, camera); + this.renderer.setup(camera); } private static Matrix4f makeProjectionMatrix(float near, float far) { @@ -147,6 +102,7 @@ public class VoxelCore { return projection; } + //TODO: Make a reverse z buffer private static Matrix4f computeProjectionMat() { return new Matrix4f(RenderSystem.getProjectionMatrix()).mulLocal( makeProjectionMatrix(0.05f, MinecraftClient.getInstance().gameRenderer.getFarPlaneDistance()).invert() @@ -160,16 +116,9 @@ public class VoxelCore { matrices.push(); matrices.translate(-cameraX, -cameraY, -cameraZ); matrices.pop(); - //this.renderer.getModelManager().updateEntry(0, Blocks.DIRT_PATH.getDefaultState()); - - //this.renderer.getModelManager().updateEntry(0, Blocks.COMPARATOR.getDefaultState()); - //this.renderer.getModelManager().updateEntry(0, Blocks.OAK_LEAVES.getDefaultState()); - - //var fb = Iris.getPipelineManager().getPipelineNullable().getSodiumTerrainPipeline().getTerrainSolidFramebuffer(); - //fb.bind(); var projection = computeProjectionMat(); - //var projection = RenderSystem.getProjectionMatrix();//computeProjectionMat(); + var viewport = this.viewportSelector.getViewport(); viewport .setProjection(projection) @@ -183,7 +132,7 @@ public class VoxelCore { this.renderer.renderFarAwayOpaque(viewport); //Compute the SSAO of the rendered terrain - //this.postProcessing.computeSSAO(projection, matrices); + this.postProcessing.computeSSAO(projection, matrices); //We can render the translucent directly after as it is the furthest translucent objects this.renderer.renderFarAwayTranslucent(viewport); @@ -202,9 +151,8 @@ public class VoxelCore { debug.add("Saving service tasks: " + this.world.savingService.getTaskCount()); debug.add("Render service tasks: " + this.renderGen.getTaskCount()); */ - debug.add("I/S/R tasks: " + this.world.ingestService.getTaskCount() + "/"+this.world.savingService.getTaskCount()+"/"+this.renderGen.getTaskCount()); - debug.add("Loaded cache sizes: " + Arrays.toString(this.world.getLoadedSectionCacheSizes())); - debug.add("Mesh cache count: " + this.renderGen.getMeshCacheCount()); + debug.add("I/S tasks: " + this.world.ingestService.getTaskCount() + "/"+this.world.savingService.getTaskCount()); + debug.add("SCS: " + Arrays.toString(this.world.getLoadedSectionCacheSizes())); this.renderer.addDebugData(debug); } @@ -226,11 +174,9 @@ public class VoxelCore { try {this.importer.shutdown();this.importer = null;} catch (Exception e) {e.printStackTrace();} } System.out.println("Shutting down voxel core"); - try {this.renderGen.shutdown();} catch (Exception e) {e.printStackTrace();} - System.out.println("Render gen shut down"); try {this.world.shutdown();} catch (Exception e) {e.printStackTrace();} System.out.println("World engine shut down"); - try {this.renderer.shutdown(); this.viewportSelector.free(); this.modelManager.free();} catch (Exception e) {e.printStackTrace();} + try {this.renderer.shutdown(); this.viewportSelector.free();} catch (Exception e) {e.printStackTrace();} System.out.println("Renderer shut down"); if (this.postProcessing!=null){try {this.postProcessing.shutdown();} catch (Exception e) {e.printStackTrace();}} System.out.println("Voxel core shut down"); @@ -264,20 +210,4 @@ public class VoxelCore { public WorldEngine getWorldEngine() { return this.world; } - - - - private IRenderInterface createRenderBackend() { - if (true) { - return new Gl46HierarchicalRenderer(this.modelManager); - } else if (false) { - return new Gl46MeshletsFarWorldRenderer(this.modelManager, VoxyConfig.CONFIG.geometryBufferSize, VoxyConfig.CONFIG.maxSections); - } else { - if (VoxyConfig.CONFIG.useMeshShaders()) { - return new NvMeshFarWorldRenderer(this.modelManager, VoxyConfig.CONFIG.geometryBufferSize, VoxyConfig.CONFIG.maxSections); - } else { - return new Gl46FarWorldRenderer(this.modelManager, VoxyConfig.CONFIG.geometryBufferSize, VoxyConfig.CONFIG.maxSections); - } - } - } } diff --git a/src/main/java/me/cortex/voxy/client/core/model/BakedBlockEntityModel.java b/src/main/java/me/cortex/voxy/client/core/model/BakedBlockEntityModel.java index 9b34a6d7..e1264c8f 100644 --- a/src/main/java/me/cortex/voxy/client/core/model/BakedBlockEntityModel.java +++ b/src/main/java/me/cortex/voxy/client/core/model/BakedBlockEntityModel.java @@ -89,6 +89,10 @@ public class BakedBlockEntityModel { .texture(Float.intBitsToFloat(vert[7]), Float.intBitsToFloat(vert[8])); } } + + public boolean isEmpty() { + return this.vertices.isEmpty(); + } } private final List layers; @@ -99,6 +103,7 @@ public class BakedBlockEntityModel { public void renderOut() { var vc = Tessellator.getInstance(); for (var layer : this.layers) { + if (layer.isEmpty()) continue; var bb = vc.begin(VertexFormat.DrawMode.QUADS, VertexFormats.POSITION_TEXTURE_COLOR); if (layer.layer instanceof RenderLayer.MultiPhase mp) { Identifier textureId = mp.phases.texture.getId().orElse(null); diff --git a/src/main/java/me/cortex/voxy/client/core/model/ModelManager.java b/src/main/java/me/cortex/voxy/client/core/model/ModelFactory.java similarity index 80% rename from src/main/java/me/cortex/voxy/client/core/model/ModelManager.java rename to src/main/java/me/cortex/voxy/client/core/model/ModelFactory.java index 954f5636..24c763a0 100644 --- a/src/main/java/me/cortex/voxy/client/core/model/ModelManager.java +++ b/src/main/java/me/cortex/voxy/client/core/model/ModelFactory.java @@ -3,7 +3,6 @@ package me.cortex.voxy.client.core.model; import com.mojang.blaze3d.platform.GlConst; import com.mojang.blaze3d.platform.GlStateManager; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; -import it.unimi.dsi.fastutil.objects.ObjectOpenCustomHashSet; import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; import it.unimi.dsi.fastutil.objects.ObjectSet; import me.cortex.voxy.client.core.IGetVoxelCore; @@ -11,9 +10,7 @@ import me.cortex.voxy.client.core.gl.GlBuffer; import me.cortex.voxy.client.core.gl.GlTexture; import me.cortex.voxy.client.core.rendering.util.UploadStream; import me.cortex.voxy.common.world.other.Mapper; -import net.minecraft.block.Block; import net.minecraft.block.BlockState; -import net.minecraft.block.Blocks; import net.minecraft.block.FluidBlock; import net.minecraft.block.entity.BlockEntity; import net.minecraft.client.MinecraftClient; @@ -38,6 +35,7 @@ import org.lwjgl.system.MemoryUtil; import java.util.*; import java.util.stream.Stream; +import static me.cortex.voxy.client.core.model.ModelStore.MODEL_SIZE; import static org.lwjgl.opengl.GL11.*; import static org.lwjgl.opengl.GL11C.GL_NEAREST; import static org.lwjgl.opengl.GL11C.GL_NEAREST_MIPMAP_LINEAR; @@ -55,7 +53,13 @@ import static org.lwjgl.opengl.GL45C.glTextureSubImage2D; //TODO: support more than 65535 states, what should actually happen is a blockstate is registered, the model data is generated, then compared // to all other models already loaded, if it is a duplicate, create a mapping from the id to the already loaded id, this will help with meshing aswell // as leaves and such will be able to be merged -public class ModelManager { + + + +//TODO: NOTE!!! is it worth even uploading as a 16x16 texture, since automatic lod selection... doing 8x8 textures might be perfectly ok!!! +// this _quarters_ the memory requirements for the texture atlas!!! WHICH IS HUGE saving +public class ModelFactory { + public static final int MODEL_TEXTURE_SIZE = 16; //TODO: replace the fluid BlockState with a client model id integer of the fluidState, requires looking up // the fluid state in the mipper @@ -65,14 +69,11 @@ public class ModelManager { } } - public static final int MODEL_SIZE = 64; + private final Biome DEFAULT_BIOME = MinecraftClient.getInstance().world.getRegistryManager().get(RegistryKeys.BIOME).get(BiomeKeys.PLAINS); + public final ModelTextureBakery bakery; - private final GlBuffer modelBuffer; - private final GlBuffer modelColourBuffer; - private final GlTexture textures; private final int blockSampler = glGenSamplers(); - private final int modelTextureSize; //Model data might also contain a constant colour if the colour resolver produces a constant colour, this saves space in the // section buffer reverse indexing @@ -108,21 +109,20 @@ public class ModelManager { private final int[] idMappings; private final Object2IntOpenHashMap modelTexture2id = new Object2IntOpenHashMap<>(); + private final Mapper mapper; private final List biomes = new ArrayList<>(); private final List> modelsRequiringBiomeColours = new ArrayList<>(); private static final ObjectSet LOGGED_SELF_CULLING_WARNING = new ObjectOpenHashSet<>(); - public ModelManager(int modelTextureSize) { - this.modelTextureSize = modelTextureSize; - this.bakery = new ModelTextureBakery(modelTextureSize, modelTextureSize); - this.modelBuffer = new GlBuffer(MODEL_SIZE * (1<<16)); - this.modelColourBuffer = new GlBuffer(4 * (1<<16)); + //TODO: NOTE!!! is it worth even uploading as a 16x16 texture, since automatic lod selection... doing 8x8 textures might be perfectly ok!!! + // this _quarters_ the memory requirements for the texture atlas!!! WHICH IS HUGE saving + public ModelFactory(Mapper mapper) { + this.mapper = mapper; + this.bakery = new ModelTextureBakery(MODEL_TEXTURE_SIZE, MODEL_TEXTURE_SIZE); - //TODO: figure out how to do mipping :blobfox_pineapple: - this.textures = new GlTexture().store(GL_RGBA8, 4, modelTextureSize*3*256,modelTextureSize*2*256); this.metadataCache = new long[1<<16]; this.fluidStateLUT = new int[1<<16]; this.idMappings = new int[1<<20];//Max of 1 million blockstates mapping to 65k model states @@ -141,15 +141,23 @@ public class ModelManager { + public void addEntry(int blockId) { + if (this.idMappings[blockId] != -1) { + System.err.println("Block id already added: " + blockId); + return; + } + this.addEntry(blockId, this.mapper.getBlockStateFromBlockId(blockId)); + } + //TODO: what i need to do is seperate out fluid states from blockStates //TODO: so need a few things, per face sizes and offsets, the sizes should be computed from the pixels and find the minimum bounding pixel // while the depth is computed from the depth buffer data - public int addEntry(int blockId, BlockState blockState) { + public void addEntry(int blockId, BlockState blockState) { if (this.idMappings[blockId] != -1) { System.err.println("Block id already added: " + blockId + " for state: " + blockState); - return this.idMappings[blockId]; + return; } boolean isFluid = blockState.getBlock() instanceof FluidBlock; @@ -162,13 +170,13 @@ public class ModelManager { //Insert into the fluid LUT var fluidState = blockState.getFluidState().getBlockState(); - //TODO:FIXME: PASS IN THE Mapper instead of grabbing it!!! THIS IS CRTICIAL TO FIX - int fluidStateId = ((IGetVoxelCore)MinecraftClient.getInstance().worldRenderer).getVoxelCore().getWorldEngine().getMapper().getIdForBlockState(fluidState); + int fluidStateId = this.mapper.getIdForBlockState(fluidState); clientFluidStateId = this.idMappings[fluidStateId]; if (clientFluidStateId == -1) { - clientFluidStateId = this.addEntry(fluidStateId, fluidState); + this.addEntry(fluidStateId, fluidState); + clientFluidStateId = this.idMappings[fluidStateId]; } } @@ -178,10 +186,11 @@ public class ModelManager { if (possibleDuplicate != -1) {//Duplicate found this.idMappings[blockId] = possibleDuplicate; modelId = possibleDuplicate; - return possibleDuplicate; + return; } else {//Not a duplicate so create a new entry modelId = this.modelTexture2id.size(); - this.idMappings[blockId] = modelId; + //NOTE: we set the mapping at the very end so that race conditions with this and getMetadata dont occur + //this.idMappings[blockId] = modelId; this.modelTexture2id.put(entry, modelId); } } @@ -208,7 +217,8 @@ public class ModelManager { - long uploadPtr = UploadStream.INSTANCE.upload(this.modelBuffer, (long) modelId * MODEL_SIZE, MODEL_SIZE); + final long uploadPtrConst = MemoryUtil.nmemAlloc(MODEL_SIZE); + long uploadPtr = uploadPtrConst; //TODO: implement; @@ -282,7 +292,7 @@ public class ModelManager { int writeCount = TextureUtils.getWrittenPixelCount(textureData[face], checkMode); boolean faceCoversFullBlock = faceSize[0] == 0 && faceSize[2] == 0 && - faceSize[1] == (this.modelTextureSize-1) && faceSize[3] == (this.modelTextureSize-1); + faceSize[1] == (MODEL_TEXTURE_SIZE-1) && faceSize[3] == (MODEL_TEXTURE_SIZE-1); metadata |= faceCoversFullBlock?2:0; @@ -294,7 +304,7 @@ public class ModelManager { occludesFace &= offset < 0.1;//If the face is rendered far away from the other face, then it doesnt occlude if (occludesFace) { - occludesFace &= ((float)writeCount)/(this.modelTextureSize * this.modelTextureSize) > 0.9;// only occlude if the face covers more than 90% of the face + occludesFace &= ((float)writeCount)/(MODEL_TEXTURE_SIZE * MODEL_TEXTURE_SIZE) > 0.9;// only occlude if the face covers more than 90% of the face } metadata |= occludesFace?1:0; @@ -313,7 +323,7 @@ public class ModelManager { //Scale face size from 0->this.modelTextureSize-1 to 0->15 for (int i = 0; i < 4; i++) { - faceSize[i] = Math.round((((float)faceSize[i])/(this.modelTextureSize-1))*15); + faceSize[i] = Math.round((((float)faceSize[i])/(MODEL_TEXTURE_SIZE-1))*15); } int faceModelData = 0; @@ -348,20 +358,23 @@ public class ModelManager { //modelFlags |= blockRenderLayer == RenderLayer.getSolid()?0:1;// should discard alpha MemoryUtil.memPutInt(uploadPtr, modelFlags); + + int[] biomeData = null; + int biomeIndex = -1; //Temporary override to always be non biome specific if (colourProvider == null) { MemoryUtil.memPutInt(uploadPtr + 4, -1);//Set the default to nothing so that its faster on the gpu } else if (!hasBiomeColourResolver) { - Biome defaultBiome = MinecraftClient.getInstance().world.getRegistryManager().get(RegistryKeys.BIOME).get(BiomeKeys.PLAINS); - MemoryUtil.memPutInt(uploadPtr + 4, captureColourConstant(colourProvider, blockState, defaultBiome)|0xFF000000); + MemoryUtil.memPutInt(uploadPtr + 4, captureColourConstant(colourProvider, blockState, DEFAULT_BIOME)|0xFF000000); } else if (!this.biomes.isEmpty()) { //Populate the list of biomes for the model state - int biomeIndex = this.modelsRequiringBiomeColours.size() * this.biomes.size(); + biomeIndex = this.modelsRequiringBiomeColours.size() * this.biomes.size(); MemoryUtil.memPutInt(uploadPtr + 4, biomeIndex); this.modelsRequiringBiomeColours.add(new Pair<>(modelId, blockState)); - long clrUploadPtr = UploadStream.INSTANCE.upload(this.modelColourBuffer, biomeIndex * 4L, 4L * this.biomes.size()); - for (var biome : this.biomes) { - MemoryUtil.memPutInt(clrUploadPtr, captureColourConstant(colourProvider, blockState, biome)|0xFF000000); clrUploadPtr += 4; + //long clrUploadPtr = UploadStream.INSTANCE.upload(this.modelColourBuffer, biomeIndex * 4L, 4L * this.biomes.size()); + biomeData = new int[this.biomes.size()]; + for (int biomeId = 0; biomeId < this.biomes.size(); biomeId++) { + biomeData[biomeId] = captureColourConstant(colourProvider, blockState, this.biomes.get(biomeId))|0xFF000000; } } @@ -371,13 +384,21 @@ public class ModelManager { //TODO - this.putTextures(modelId, textureData); + var textureUpload = this.putTextures(modelId, textureData); //glGenerateTextureMipmap(this.textures.id); - return modelId; + + //Set the mapping at the very end + this.idMappings[blockId] = modelId; + + + new NewModelBufferDelta(modelId, uploadPtrConst, biomeIndex, biomeData, textureUpload); } public void addBiome(int id, Biome biome) { + throw new IllegalStateException("IMPLEMENT"); + + /* this.biomes.add(biome); if (this.biomes.size()-1 != id) { throw new IllegalStateException("Biome ordering not consistent with biome id for biome " + biome + " expected id: " + (this.biomes.size()-1) + " got id: " + id); @@ -391,12 +412,13 @@ public class ModelManager { } //Populate the list of biomes for the model state int biomeIndex = (i++) * this.biomes.size(); - MemoryUtil.memPutInt( UploadStream.INSTANCE.upload(this.modelBuffer, (entry.getLeft()*MODEL_SIZE)+ 4*6 + 4, 4), biomeIndex); + MemoryUtil.memPutInt(UploadStream.INSTANCE.upload(this.modelBuffer, (entry.getLeft()* MODEL_SIZE)+ 4*6 + 4, 4), biomeIndex); long clrUploadPtr = UploadStream.INSTANCE.upload(this.modelColourBuffer, biomeIndex * 4L, 4L * this.biomes.size()); for (var biomeE : this.biomes) { MemoryUtil.memPutInt(clrUploadPtr, captureColourConstant(colourProvider, entry.getRight(), biomeE)|0xFF000000); clrUploadPtr += 4; } } + */ } @@ -506,95 +528,22 @@ public class ModelManager { return biomeDependent[0]; } - - - - public static boolean faceExists(long metadata, int face) { - return ((metadata>>(8*face))&0xFF)!=0xFF; - } - - public static boolean faceCanBeOccluded(long metadata, int face) { - return ((metadata>>(8*face))&0b100)==0b100; - } - - public static boolean faceOccludes(long metadata, int face) { - return faceExists(metadata, face) && ((metadata>>(8*face))&0b1)==0b1; - } - - public static boolean faceUsesSelfLighting(long metadata, int face) { - return ((metadata>>(8*face))&0b1000) != 0; - } - - public static boolean isDoubleSided(long metadata) { - return ((metadata>>(8*6))&4) != 0; - } - - public static boolean isTranslucent(long metadata) { - return ((metadata>>(8*6))&2) != 0; - } - - public static boolean containsFluid(long metadata) { - return ((metadata>>(8*6))&8) != 0; - } - - public static boolean isFluid(long metadata) { - return ((metadata>>(8*6))&16) != 0; - } - - public static boolean isBiomeColoured(long metadata) { - return ((metadata>>(8*6))&1) != 0; - } - - //NOTE: this might need to be moved to per face - public static boolean cullsSame(long metadata) { - return ((metadata>>(8*6))&32) != 0; - } - - - - - - - - - - private float[] computeModelDepth(ColourDepthTextureData[] textures, int checkMode) { float[] res = new float[6]; for (var dir : Direction.values()) { var data = textures[dir.getId()]; float fd = TextureUtils.computeDepth(data, TextureUtils.DEPTH_MODE_AVG, checkMode);//Compute the min float depth, smaller means closer to the camera, range 0-1 - int depth = Math.round(fd * this.modelTextureSize); + int depth = Math.round(fd * MODEL_TEXTURE_SIZE); //If fd is -1, it means that there was nothing rendered on that face and it should be discarded if (fd < -0.1) { res[dir.ordinal()] = -1; } else { - res[dir.ordinal()] = ((float) depth)/this.modelTextureSize; + res[dir.ordinal()] = ((float) depth)/MODEL_TEXTURE_SIZE; } } return res; } - public long getModelMetadata(int blockId) { - int map = this.idMappings[blockId]; - if (map == -1) { - try { - Thread.sleep(100); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - map = this.idMappings[blockId]; - } - if (map == -1) { - throw new IdNotYetComputedException(blockId); - } - return this.metadataCache[map]; - } - - public long getModelMetadataFromClientId(int clientId) { - return this.metadataCache[clientId]; - } - public int getModelId(int blockId) { int map = this.idMappings[blockId]; if (map == -1) { @@ -603,6 +552,10 @@ public class ModelManager { return map; } + public boolean hasModelForBlockId(int blockId) { + return this.idMappings[blockId] != -1; + } + public int getFluidClientStateId(int clientBlockStateId) { int map = this.fluidStateLUT[clientBlockStateId]; if (map == -1) { @@ -611,24 +564,24 @@ public class ModelManager { return map; } - private void putTextures(int id, ColourDepthTextureData[] textures) { - int X = (id&0xFF) * this.modelTextureSize*3; - int Y = ((id>>8)&0xFF) * this.modelTextureSize*2; + public long getModelMetadataFromClientId(int clientId) { + return this.metadataCache[clientId]; + } + private ModelTextureUpload putTextures(int id, ColourDepthTextureData[] textures) { + int X = (id&0xFF) * MODEL_TEXTURE_SIZE*3; + int Y = ((id>>8)&0xFF) * MODEL_TEXTURE_SIZE*2; + + int texIndex = 0; + int[][] texData = new int[6*4][]; for (int subTex = 0; subTex < 6; subTex++) { - int x = X + (subTex>>1)*this.modelTextureSize; - int y = Y + (subTex&1)*this.modelTextureSize; - GlStateManager._pixelStore(GlConst.GL_UNPACK_ROW_LENGTH, 0); - GlStateManager._pixelStore(GlConst.GL_UNPACK_SKIP_PIXELS, 0); - GlStateManager._pixelStore(GlConst.GL_UNPACK_SKIP_ROWS, 0); - GlStateManager._pixelStore(GlConst.GL_UNPACK_ALIGNMENT, 4); var current = textures[subTex].colour(); var next = new int[current.length>>1]; for (int i = 0; i < 4; i++) { - glTextureSubImage2D(this.textures.id, i, x>>i, y>>i, this.modelTextureSize>>i, this.modelTextureSize>>i, GL_RGBA, GL_UNSIGNED_BYTE, current); + texData[texIndex++] = Arrays.copyOf(current, current.length); - int size = this.modelTextureSize>>(i+1); + int size = MODEL_TEXTURE_SIZE>>(i+1); for (int pX = 0; pX < size; pX++) { for (int pY = 0; pY < size; pY++) { int C00 = current[(pY*2)*size+pX*2]; @@ -643,29 +596,15 @@ public class ModelManager { next = new int[current.length>>1]; } } - } - - public int getBufferId() { - return this.modelBuffer.id; - } - - public int getTextureId() { - return this.textures.id; + return new ModelTextureUpload(id, texData); } public int getSamplerId() { return this.blockSampler; } - public int getColourBufferId() { - return this.modelColourBuffer.id; - } - public void free() { this.bakery.free(); - this.modelBuffer.free(); - this.modelColourBuffer.free(); - this.textures.free(); glDeleteSamplers(this.blockSampler); } diff --git a/src/main/java/me/cortex/voxy/client/core/model/ModelQueries.java b/src/main/java/me/cortex/voxy/client/core/model/ModelQueries.java new file mode 100644 index 00000000..7b34b8ed --- /dev/null +++ b/src/main/java/me/cortex/voxy/client/core/model/ModelQueries.java @@ -0,0 +1,44 @@ +package me.cortex.voxy.client.core.model; + +public abstract class ModelQueries { + public static boolean faceExists(long metadata, int face) { + return ((metadata>>(8*face))&0xFF)!=0xFF; + } + + public static boolean faceCanBeOccluded(long metadata, int face) { + return ((metadata>>(8*face))&0b100)==0b100; + } + + public static boolean faceOccludes(long metadata, int face) { + return faceExists(metadata, face) && ((metadata>>(8*face))&0b1)==0b1; + } + + public static boolean faceUsesSelfLighting(long metadata, int face) { + return ((metadata>>(8*face))&0b1000) != 0; + } + + public static boolean isDoubleSided(long metadata) { + return ((metadata>>(8*6))&4) != 0; + } + + public static boolean isTranslucent(long metadata) { + return ((metadata>>(8*6))&2) != 0; + } + + public static boolean containsFluid(long metadata) { + return ((metadata>>(8*6))&8) != 0; + } + + public static boolean isFluid(long metadata) { + return ((metadata>>(8*6))&16) != 0; + } + + public static boolean isBiomeColoured(long metadata) { + return ((metadata>>(8*6))&1) != 0; + } + + //NOTE: this might need to be moved to per face + public static boolean cullsSame(long metadata) { + return ((metadata>>(8*6))&32) != 0; + } +} diff --git a/src/main/java/me/cortex/voxy/client/core/model/ModelStore.java b/src/main/java/me/cortex/voxy/client/core/model/ModelStore.java new file mode 100644 index 00000000..53701091 --- /dev/null +++ b/src/main/java/me/cortex/voxy/client/core/model/ModelStore.java @@ -0,0 +1,26 @@ +package me.cortex.voxy.client.core.model; + +import me.cortex.voxy.client.core.gl.GlBuffer; +import me.cortex.voxy.client.core.gl.GlTexture; + +import static org.lwjgl.opengl.GL11.GL_RGBA8; + +public class ModelStore { + public static final int MODEL_SIZE = 64; + private final GlBuffer modelBuffer; + private final GlBuffer modelColourBuffer; + private final GlTexture textures; + + public ModelStore(int modelTextureSize) { + this.modelBuffer = new GlBuffer(MODEL_SIZE * (1<<16)); + this.modelColourBuffer = new GlBuffer(4 * (1<<16)); + this.textures = new GlTexture().store(GL_RGBA8, 4, modelTextureSize*3*256,modelTextureSize*2*256); + } + + + public void free() { + this.modelBuffer.free(); + this.modelColourBuffer.free(); + this.textures.free(); + } +} diff --git a/src/main/java/me/cortex/voxy/client/core/model/ModelTextureUpload.java b/src/main/java/me/cortex/voxy/client/core/model/ModelTextureUpload.java new file mode 100644 index 00000000..70ec6c5f --- /dev/null +++ b/src/main/java/me/cortex/voxy/client/core/model/ModelTextureUpload.java @@ -0,0 +1,30 @@ +package me.cortex.voxy.client.core.model; + +import com.mojang.blaze3d.platform.GlConst; +import com.mojang.blaze3d.platform.GlStateManager; + +import static me.cortex.voxy.client.core.model.ModelFactory.MODEL_TEXTURE_SIZE; +import static org.lwjgl.opengl.ARBDirectStateAccess.glTextureSubImage2D; +import static org.lwjgl.opengl.GL11.GL_RGBA; +import static org.lwjgl.opengl.GL11.GL_UNSIGNED_BYTE; + +public record ModelTextureUpload(int id, int[][] data) { + + public void upload(int textureId) { + GlStateManager._pixelStore(GlConst.GL_UNPACK_ROW_LENGTH, 0); + GlStateManager._pixelStore(GlConst.GL_UNPACK_SKIP_PIXELS, 0); + GlStateManager._pixelStore(GlConst.GL_UNPACK_SKIP_ROWS, 0); + GlStateManager._pixelStore(GlConst.GL_UNPACK_ALIGNMENT, 4); + + int i = 0; + int X = (this.id&0xFF) * MODEL_TEXTURE_SIZE*3; + int Y = ((this.id>>8)&0xFF) * MODEL_TEXTURE_SIZE*2; + for (int face = 0; face < 6; face++) { + int x = X + (face>>1)*MODEL_TEXTURE_SIZE; + int y = Y + (face&1)*MODEL_TEXTURE_SIZE; + for (int mip = 0; mip < 4; mip++) { + glTextureSubImage2D(id, mip, x >> mip, y >> mip, MODEL_TEXTURE_SIZE >> mip, MODEL_TEXTURE_SIZE >> mip, GL_RGBA, GL_UNSIGNED_BYTE, this.data[i++]); + } + } + } +} diff --git a/src/main/java/me/cortex/voxy/client/core/model/NewModelBufferDelta.java b/src/main/java/me/cortex/voxy/client/core/model/NewModelBufferDelta.java new file mode 100644 index 00000000..c512cd14 --- /dev/null +++ b/src/main/java/me/cortex/voxy/client/core/model/NewModelBufferDelta.java @@ -0,0 +1,17 @@ +package me.cortex.voxy.client.core.model; + +import org.lwjgl.system.MemoryUtil; + +public record NewModelBufferDelta(int modelClientId, long modelBufferChangesPtr, int biomeIndex, int[] biomeData, ModelTextureUpload textureUpload) { + public static NewModelBufferDelta empty(int modelClientId) { + return new NewModelBufferDelta(modelClientId, 0, -1, null, null); + } + + public boolean isEmpty() { + return this.modelBufferChangesPtr == 0; + } + + public void free() { + MemoryUtil.nmemFree(this.modelBufferChangesPtr); + } +} diff --git a/src/main/java/me/cortex/voxy/client/core/model/OffThreadBakerySystem.java b/src/main/java/me/cortex/voxy/client/core/model/OffThreadBakerySystem.java deleted file mode 100644 index 8d86ea34..00000000 --- a/src/main/java/me/cortex/voxy/client/core/model/OffThreadBakerySystem.java +++ /dev/null @@ -1,7 +0,0 @@ -package me.cortex.voxy.client.core.model; - - -//Uses an off thread gl context to do the model baking on -public class OffThreadBakerySystem { - -} diff --git a/src/main/java/me/cortex/voxy/client/core/model/OffThreadModelBakerySystem.java b/src/main/java/me/cortex/voxy/client/core/model/OffThreadModelBakerySystem.java new file mode 100644 index 00000000..8b6f9c99 --- /dev/null +++ b/src/main/java/me/cortex/voxy/client/core/model/OffThreadModelBakerySystem.java @@ -0,0 +1,93 @@ +package me.cortex.voxy.client.core.model; + + +import it.unimi.dsi.fastutil.ints.IntArrayFIFOQueue; +import me.cortex.voxy.common.world.other.Mapper; +import net.minecraft.client.MinecraftClient; +import org.lwjgl.glfw.GLFW; +import org.lwjgl.opengl.GL; + +import java.lang.invoke.VarHandle; +import java.util.List; +import java.util.concurrent.ConcurrentLinkedDeque; +import java.util.concurrent.Semaphore; + +//Uses an off thread gl context to do the model baking on +public class OffThreadModelBakerySystem { + //NOTE: Create a static final context offthread and dont close it, just reuse the context, since context creation is expensive + private static final long GL_CTX; + static { + GLFW.glfwMakeContextCurrent(0L); + GLFW.glfwWindowHint(GLFW.GLFW_VISIBLE, 0); + GL_CTX = GLFW.glfwCreateWindow(1, 1, "", 0, MinecraftClient.getInstance().getWindow().getHandle()); + GLFW.glfwMakeContextCurrent(GL_CTX); + GL.createCapabilities(); + GLFW.glfwMakeContextCurrent(MinecraftClient.getInstance().getWindow().getHandle()); + } + + + + private final ModelStore storage = new ModelStore(16); + public final ModelFactory factory; + private final ConcurrentLinkedDeque bufferDeltas = new ConcurrentLinkedDeque<>(); + private final IntArrayFIFOQueue blockQueue = new IntArrayFIFOQueue(); + private final Semaphore queueCounter = new Semaphore(0); + + + + private final Thread bakingThread; + private volatile boolean running = true; + public OffThreadModelBakerySystem(Mapper mapper) { + this.factory = new ModelFactory(mapper); + this.bakingThread = new Thread(this::bakeryThread); + this.bakingThread.setName("Baking thread"); + this.bakingThread.setPriority(3); + this.bakingThread.start(); + } + + private void bakeryThread() { + GLFW.glfwMakeContextCurrent(GL_CTX); + + //FIXME: tile entities will probably need to be baked on the main render thread + while (true) { + this.queueCounter.acquireUninterruptibly(); + if (!this.running) break; + int blockId; + synchronized (this.blockQueue) { + blockId = this.blockQueue.dequeueInt(); + VarHandle.fullFence();//Ensure memory coherancy + } + + this.factory.addEntry(blockId); + } + } + + //Changes to the block table/atlas must be run on the main render thread + public void syncChanges() { + //TODO: FIXME!! the ordering of the uploads here and the order that _both_ model AND biome are done in the bakery thread MUST be the same!!! + } + + public void shutdown() { + this.running = false; + this.queueCounter.release(999); + try { + this.bakingThread.join(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + this.factory.free(); + this.storage.free(); + } + + public void requestBlockBake(int blockId) { + synchronized (this.blockQueue) { + this.blockQueue.enqueue(blockId); + VarHandle.fullFence();//Ensure memory coherancy + this.queueCounter.release(1); + } + } + + public void addDebugData(List debug) { + + } +} diff --git a/src/main/java/me/cortex/voxy/client/core/rendering/Gl46MeshletViewport.java b/src/main/java/me/cortex/voxy/client/core/rendering/Gl46MeshletViewport.java deleted file mode 100644 index 8429566a..00000000 --- a/src/main/java/me/cortex/voxy/client/core/rendering/Gl46MeshletViewport.java +++ /dev/null @@ -1,19 +0,0 @@ -package me.cortex.voxy.client.core.rendering; - -import me.cortex.voxy.client.core.gl.GlBuffer; - -import static org.lwjgl.opengl.GL30C.GL_R8UI; -import static org.lwjgl.opengl.GL30C.GL_RED_INTEGER; -import static org.lwjgl.opengl.GL42.GL_UNSIGNED_BYTE; -import static org.lwjgl.opengl.GL45C.nglClearNamedBufferData; - -public class Gl46MeshletViewport extends Viewport { - GlBuffer visibilityBuffer; - public Gl46MeshletViewport(Gl46MeshletsFarWorldRenderer renderer) { - this.visibilityBuffer = new GlBuffer(renderer.maxSections*4L).zero(); - } - - protected void delete0() { - this.visibilityBuffer.free(); - } -} diff --git a/src/main/java/me/cortex/voxy/client/core/rendering/Gl46MeshletsFarWorldRenderer.java b/src/main/java/me/cortex/voxy/client/core/rendering/Gl46MeshletsFarWorldRenderer.java deleted file mode 100644 index a35a6fbd..00000000 --- a/src/main/java/me/cortex/voxy/client/core/rendering/Gl46MeshletsFarWorldRenderer.java +++ /dev/null @@ -1,242 +0,0 @@ -package me.cortex.voxy.client.core.rendering; - -import me.cortex.voxy.client.core.gl.GlBuffer; -import me.cortex.voxy.client.core.gl.shader.PrintfInjector; -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.ModelManager; -import me.cortex.voxy.client.core.rendering.building.RenderDataFactory; -import me.cortex.voxy.client.core.rendering.util.UploadStream; -import me.cortex.voxy.client.mixin.joml.AccessFrustumIntersection; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.render.RenderLayer; -import org.joml.Matrix4f; -import org.joml.Vector3f; -import org.lwjgl.system.MemoryUtil; - -import static org.lwjgl.opengl.ARBDirectStateAccess.glGetNamedFramebufferAttachmentParameteriv; -import static org.lwjgl.opengl.ARBDirectStateAccess.glTextureParameteri; -import static org.lwjgl.opengl.ARBIndirectParameters.GL_PARAMETER_BUFFER_ARB; -import static org.lwjgl.opengl.GL11.*; -import static org.lwjgl.opengl.GL14C.glBlendFuncSeparate; -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.GL30C.GL_RED_INTEGER; -import static org.lwjgl.opengl.GL31.GL_UNIFORM_BUFFER; -import static org.lwjgl.opengl.GL31.glDrawElementsInstanced; -import static org.lwjgl.opengl.GL33.glBindSampler; -import static org.lwjgl.opengl.GL40.glDrawElementsIndirect; -import static org.lwjgl.opengl.GL40C.GL_DRAW_INDIRECT_BUFFER; -import static org.lwjgl.opengl.GL42.GL_FRAMEBUFFER_BARRIER_BIT; -import static org.lwjgl.opengl.GL42.glMemoryBarrier; -import static org.lwjgl.opengl.GL43.*; -import static org.lwjgl.opengl.GL45.glBindTextureUnit; -import static org.lwjgl.opengl.GL45.nglClearNamedBufferSubData; -import static org.lwjgl.opengl.GL45C.nglClearNamedBufferData; -import static org.lwjgl.opengl.NVMeshShader.glDrawMeshTasksNV; -import static org.lwjgl.opengl.NVRepresentativeFragmentTest.GL_REPRESENTATIVE_FRAGMENT_TEST_NV; - -//TODO: NOTE -// can do "meshlet compaction" -// that is, meshlets are refered not by the meshlet id but by an 8 byte alligned index, (the quad index) -// this means that non full meshlets can have the tailing data be truncated and used in the next meshlet -// as long as the number of quads in the meshlet is stored in the header -// the shader can cull the verticies of any quad that has its index over the expected quuad count -// this could potentially result in a fair bit of memory savings (especially if used in normal mc terrain rendering) -public class Gl46MeshletsFarWorldRenderer extends AbstractFarWorldRenderer { - - private final Shader lodShader = Shader.make() - .define("QUADS_PER_MESHLET", RenderDataFactory.QUADS_PER_MESHLET) - .add(ShaderType.VERTEX, "voxy:lod/gl46mesh/quads.vert") - .add(ShaderType.FRAGMENT, "voxy:lod/gl46mesh/quads.frag") - .compile(); - - private final Shader cullShader = Shader.make() - .define("QUADS_PER_MESHLET", RenderDataFactory.QUADS_PER_MESHLET) - .add(ShaderType.VERTEX, "voxy:lod/gl46mesh/cull.vert") - .add(ShaderType.FRAGMENT, "voxy:lod/gl46mesh/cull.frag") - .compile(); - - private final Shader meshletGenerator = Shader.make() - .define("QUADS_PER_MESHLET", RenderDataFactory.QUADS_PER_MESHLET) - .add(ShaderType.COMPUTE, "voxy:lod/gl46mesh/cmdgen.comp") - .compile(); - - private final Shader meshletCuller = Shader.make() - .define("QUADS_PER_MESHLET", RenderDataFactory.QUADS_PER_MESHLET) - .add(ShaderType.COMPUTE, "voxy:lod/gl46mesh/meshletculler.comp") - .compile(); - - private final GlBuffer glDrawIndirect; - private final GlBuffer meshletBuffer; - private final HiZBuffer hiZBuffer = new HiZBuffer(); - private final int hizSampler = glGenSamplers(); - - public Gl46MeshletsFarWorldRenderer(ModelManager modelManager, int geometrySize, int maxSections) { - super(modelManager, new DefaultGeometryManager(alignUp(geometrySize*8L, 8* (RenderDataFactory.QUADS_PER_MESHLET+2)), maxSections, 8*(RenderDataFactory.QUADS_PER_MESHLET+2))); - this.glDrawIndirect = new GlBuffer(4*(4+5)).zero(); - this.meshletBuffer = new GlBuffer(4*1000000).zero();//TODO: Make max meshlet count configurable, not just 1 million (even tho thats a max of 126 million quads per frame) - - glSamplerParameteri(this.hizSampler, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);//This is so that using the shadow sampler works correctly - glTextureParameteri(this.hizSampler, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTextureParameteri(this.hizSampler, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE); - glTextureParameteri(this.hizSampler, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL); - glTextureParameteri(this.hizSampler, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTextureParameteri(this.hizSampler, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTextureParameteri(this.hizSampler, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - - /* - nglClearNamedBufferData(this.meshletBuffer.id, GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT, 0); - nglClearNamedBufferData(this.glDrawIndirect.id, GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT, 0); - */ - } - - protected void bindResources(Gl46MeshletViewport viewport, boolean bindToDrawIndirect, boolean bindToDispatchIndirect, boolean bindHiz) { - glBindBufferBase(GL_UNIFORM_BUFFER, 0, this.uniformBuffer.id); - glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, this.geometry.geometryId()); - glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, bindToDrawIndirect?0:this.glDrawIndirect.id); - glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 3, this.meshletBuffer.id); - glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 4, this.geometry.metaId()); - glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 5, viewport.visibilityBuffer.id); - glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 6, this.models.getBufferId()); - glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 7, this.models.getColourBufferId()); - glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 8, this.lightDataBuffer.id);//Lighting LUT - glBindBuffer(GL_DRAW_INDIRECT_BUFFER, bindToDrawIndirect?this.glDrawIndirect.id:0); - glBindBuffer(GL_DISPATCH_INDIRECT_BUFFER, bindToDispatchIndirect?this.glDrawIndirect.id:0); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, SharedIndexBuffer.INSTANCE_BYTE.id()); - - if (!bindHiz) { - //Bind the texture atlas - glBindSampler(0, this.models.getSamplerId()); - glBindTextureUnit(0, this.models.getTextureId()); - } else { - //Bind the hiz buffer - glBindSampler(0, this.hizSampler); - glBindTextureUnit(0, this.hiZBuffer.getHizTextureId()); - } - } - - private void updateUniformBuffer(Gl46MeshletViewport viewport) { - long ptr = UploadStream.INSTANCE.upload(this.uniformBuffer, 0, this.uniformBuffer.size()); - - //TODO:FIXME remove this.sx/sy/sz and make it based on the viewport!!! - - var mat = new Matrix4f(viewport.projection).mul(viewport.modelView); - var innerTranslation = new Vector3f((float) (viewport.cameraX-(this.sx<<5)), (float) (viewport.cameraY-(this.sy<<5)), (float) (viewport.cameraZ-(this.sz<<5))); - mat.translate(-innerTranslation.x, -innerTranslation.y, -innerTranslation.z); - mat.getToAddress(ptr); ptr += 4*4*4; - MemoryUtil.memPutInt(ptr, this.sx); ptr += 4; - MemoryUtil.memPutInt(ptr, this.sy); ptr += 4; - MemoryUtil.memPutInt(ptr, this.sz); ptr += 4; - MemoryUtil.memPutInt(ptr, this.geometry.getSectionCount()); ptr += 4; - var planes = ((AccessFrustumIntersection)this.frustum).getPlanes(); - for (var plane : planes) { - plane.getToAddress(ptr); ptr += 4*4; - } - innerTranslation.getToAddress(ptr); ptr += 4*3; - MemoryUtil.memPutInt(ptr, viewport.frameId++); ptr += 4; - MemoryUtil.memPutInt(ptr, viewport.width); ptr += 4; - MemoryUtil.memPutInt(ptr, viewport.height); ptr += 4; - } - - @Override - public void renderFarAwayOpaque(Gl46MeshletViewport viewport) { - if (this.geometry.getSectionCount() == 0) { - return; - } - - {//Mark all of the updated sections as being visible from last frame - for (int id : this.updatedSectionIds) { - long ptr = UploadStream.INSTANCE.upload(viewport.visibilityBuffer, id * 4L, 4); - MemoryUtil.memPutInt(ptr, viewport.frameId - 1);//(visible from last frame) - } - } - - glDisable(GL_BLEND); - glEnable(GL_DEPTH_TEST); - RenderLayer.getCutoutMipped().startDrawing(); - - this.updateUniformBuffer(viewport); - UploadStream.INSTANCE.commit(); - glBindVertexArray(AbstractFarWorldRenderer.STATIC_VAO); - - nglClearNamedBufferSubData(this.glDrawIndirect.id, GL_R32UI, 0, 4*4, GL_RED_INTEGER, GL_UNSIGNED_INT, 0); - - this.lodShader.bind(); - this.bindResources(viewport, true, false, false); - glDisable(GL_CULL_FACE); - glProvokingVertex(GL_FIRST_VERTEX_CONVENTION); - glDrawElementsIndirect(GL_TRIANGLES, GL_UNSIGNED_BYTE, 4*4); - glEnable(GL_CULL_FACE); - - glMemoryBarrier(GL_PIXEL_BUFFER_BARRIER_BIT | GL_FRAMEBUFFER_BARRIER_BIT); - - this.cullShader.bind(); - this.bindResources(viewport, false, false, false); - glDepthMask(false); - glColorMask(false, false, false, false); - glDrawElementsInstanced(GL_TRIANGLES, 6 * 2 * 3, GL_UNSIGNED_BYTE, (1 << 8) * 6, this.geometry.getSectionCount()); - glColorMask(true, true, true, true); - glDepthMask(true); - - glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT); - - this.meshletGenerator.bind(); - this.bindResources(viewport, false, false, false); - glDispatchCompute((this.geometry.getSectionCount()+63)/64, 1, 1); - - glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT | GL_COMMAND_BARRIER_BIT); - - var i = new int[1]; - glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, i); - this.hiZBuffer.buildMipChain(i[0], MinecraftClient.getInstance().getFramebuffer().textureWidth, MinecraftClient.getInstance().getFramebuffer().textureHeight); - - this.meshletCuller.bind(); - this.bindResources(viewport, false, true, true); - glDispatchComputeIndirect(0); - - - - glBindVertexArray(0); - glBindSampler(0, 0); - glBindTextureUnit(0, 0); - RenderLayer.getCutoutMipped().endDrawing(); - } - - - @Override - public void renderFarAwayTranslucent(Gl46MeshletViewport viewport) { - - } - - @Override - protected Gl46MeshletViewport createViewport0() { - return new Gl46MeshletViewport(this); - } - - @Override - public void shutdown() { - super.shutdown(); - this.lodShader.free(); - this.cullShader.free(); - this.meshletGenerator.free(); - this.meshletCuller.free(); - - this.glDrawIndirect.free(); - this.meshletBuffer.free(); - - this.hiZBuffer.free(); - glDeleteSamplers(this.hizSampler); - } - - public static long alignUp(long n, long alignment) { - return (n + alignment - 1) & -alignment; - } - - @Override - public boolean generateMeshlets() { - return true; - } -} diff --git a/src/main/java/me/cortex/voxy/client/core/rendering/HiZBuffer.java b/src/main/java/me/cortex/voxy/client/core/rendering/HiZBuffer.java index a73bebfc..5a24587b 100644 --- a/src/main/java/me/cortex/voxy/client/core/rendering/HiZBuffer.java +++ b/src/main/java/me/cortex/voxy/client/core/rendering/HiZBuffer.java @@ -4,10 +4,10 @@ import me.cortex.voxy.client.core.gl.GlFramebuffer; import me.cortex.voxy.client.core.gl.GlTexture; import me.cortex.voxy.client.core.gl.shader.Shader; import me.cortex.voxy.client.core.gl.shader.ShaderType; +import me.cortex.voxy.client.core.rendering.geometry.OLD.AbstractFarWorldRenderer; import org.lwjgl.opengl.GL11; import static org.lwjgl.opengl.ARBDirectStateAccess.*; -import static org.lwjgl.opengl.GL11.glScaled; import static org.lwjgl.opengl.GL11C.*; import static org.lwjgl.opengl.GL30C.*; import static org.lwjgl.opengl.GL33.glBindSampler; @@ -17,7 +17,6 @@ import static org.lwjgl.opengl.GL33C.glSamplerParameteri; import static org.lwjgl.opengl.GL42C.GL_FRAMEBUFFER_BARRIER_BIT; import static org.lwjgl.opengl.GL42C.glMemoryBarrier; import static org.lwjgl.opengl.GL43C.glCopyImageSubData; -import static org.lwjgl.opengl.GL45C.glNamedFramebufferTexture; import static org.lwjgl.opengl.GL45C.glTextureBarrier; public class HiZBuffer { diff --git a/src/main/java/me/cortex/voxy/client/core/rendering/IRenderInterface.java b/src/main/java/me/cortex/voxy/client/core/rendering/IRenderInterface.java deleted file mode 100644 index d2972b6f..00000000 --- a/src/main/java/me/cortex/voxy/client/core/rendering/IRenderInterface.java +++ /dev/null @@ -1,32 +0,0 @@ -package me.cortex.voxy.client.core.rendering; - - -import me.cortex.voxy.client.core.rendering.hierarchical.HierarchicalOcclusionRenderer; -import me.cortex.voxy.common.world.other.Mapper; -import net.minecraft.client.render.Camera; -import net.minecraft.client.render.Frustum; - -import java.util.List; - -import static org.lwjgl.opengl.GL30.*; - -public interface IRenderInterface { - - T createViewport(); - - void setupRender(Frustum frustum, Camera camera); - - void renderFarAwayOpaque(T viewport); - - void renderFarAwayTranslucent(T viewport); - - void addDebugData(List debug); - - void shutdown(); - - void addBlockState(Mapper.StateEntry stateEntry); - - void addBiome(Mapper.BiomeEntry biomeEntry); - - boolean generateMeshlets(); -} diff --git a/src/main/java/me/cortex/voxy/client/core/rendering/NvMeshFarWorldRenderer.java b/src/main/java/me/cortex/voxy/client/core/rendering/NvMeshFarWorldRenderer.java deleted file mode 100644 index ee38f7b6..00000000 --- a/src/main/java/me/cortex/voxy/client/core/rendering/NvMeshFarWorldRenderer.java +++ /dev/null @@ -1,189 +0,0 @@ -package me.cortex.voxy.client.core.rendering; - -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.ModelManager; -import me.cortex.voxy.client.core.rendering.util.UploadStream; -import me.cortex.voxy.client.mixin.joml.AccessFrustumIntersection; -import net.minecraft.client.render.Camera; -import net.minecraft.client.render.Frustum; -import net.minecraft.client.render.RenderLayer; -import org.joml.Matrix4f; -import org.joml.Vector3f; -import org.lwjgl.opengl.GL11C; -import org.lwjgl.system.MemoryUtil; - -import java.util.List; - -import static org.lwjgl.opengl.ARBIndirectParameters.GL_PARAMETER_BUFFER_ARB; -import static org.lwjgl.opengl.ARBIndirectParameters.glMultiDrawElementsIndirectCountARB; -import static org.lwjgl.opengl.GL11.*; -import static org.lwjgl.opengl.GL14C.glBlendFuncSeparate; -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.GL31.glDrawElementsInstanced; -import static org.lwjgl.opengl.GL33.glBindSampler; -import static org.lwjgl.opengl.GL40C.GL_DRAW_INDIRECT_BUFFER; -import static org.lwjgl.opengl.GL42.GL_FRAMEBUFFER_BARRIER_BIT; -import static org.lwjgl.opengl.GL42.glMemoryBarrier; -import static org.lwjgl.opengl.GL43.GL_SHADER_STORAGE_BARRIER_BIT; -import static org.lwjgl.opengl.GL43.GL_SHADER_STORAGE_BUFFER; -import static org.lwjgl.opengl.GL45.glBindTextureUnit; -import static org.lwjgl.opengl.NVMeshShader.glDrawMeshTasksNV; -import static org.lwjgl.opengl.NVRepresentativeFragmentTest.GL_REPRESENTATIVE_FRAGMENT_TEST_NV; - -public class NvMeshFarWorldRenderer extends AbstractFarWorldRenderer { - private final Shader terrain = Shader.make() - .add(ShaderType.TASK, "voxy:lod/nvmesh/primary.task") - .add(ShaderType.MESH, "voxy:lod/nvmesh/primary.mesh") - .add(ShaderType.FRAGMENT, "voxy:lod/nvmesh/primary.frag") - .compile(); - - private final Shader translucent = Shader.make() - .add(ShaderType.TASK, "voxy:lod/nvmesh/translucent.task") - .add(ShaderType.MESH, "voxy:lod/nvmesh/translucent.mesh") - .add(ShaderType.FRAGMENT, "voxy:lod/nvmesh/primary.frag") - .compile(); - - private final Shader cull = Shader.make() - .add(ShaderType.VERTEX, "voxy:lod/nvmesh/cull.vert") - .add(ShaderType.FRAGMENT, "voxy:lod/nvmesh/cull.frag") - .compile(); - - public NvMeshFarWorldRenderer(ModelManager modelManager, int geometrySize, int maxSections) { - super(modelManager, new DefaultGeometryManager(geometrySize*8L, maxSections)); - } - - - private void updateUniform(NvMeshViewport viewport) { - long ptr = UploadStream.INSTANCE.upload(this.uniformBuffer, 0, this.uniformBuffer.size()); - - var mat = new Matrix4f(viewport.projection).mul(viewport.modelView); - mat.getToAddress(ptr); ptr += 4*4*4; - var innerTranslation = new Vector3f((float) (viewport.cameraX-(this.sx<<5)), (float) (viewport.cameraY-(this.sy<<5)), (float) (viewport.cameraZ-(this.sz<<5))); - MemoryUtil.memPutInt(ptr, this.sx); ptr += 4; - MemoryUtil.memPutInt(ptr, this.sy); ptr += 4; - MemoryUtil.memPutInt(ptr, this.sz); ptr += 4; - MemoryUtil.memPutInt(ptr, this.geometry.getSectionCount()); ptr += 4; - innerTranslation.getToAddress(ptr); ptr += 4*3; - MemoryUtil.memPutInt(ptr, viewport.frameId++); ptr += 4; - } - - private void bindResources(NvMeshViewport viewport) { - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, SharedIndexBuffer.INSTANCE.id()); - glBindBufferBase(GL_UNIFORM_BUFFER, 0, this.uniformBuffer.id); - glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, this.geometry.geometryId()); - glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, this.geometry.metaId()); - glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 3, viewport.visibilityBuffer.id); - glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 4, this.models.getBufferId()); - glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 5, this.models.getColourBufferId()); - glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 6, this.lightDataBuffer.id);//Lighting LUT - - //Bind the texture atlas - glBindSampler(0, this.models.getSamplerId()); - glBindTextureUnit(0, this.models.getTextureId()); - } - - @Override - public void renderFarAwayOpaque(NvMeshViewport viewport) { - {//TODO: move all this code into a common super method renderFarAwayTranslucent and make the current method renderFarAwayTranslucent0 - if (this.geometry.getSectionCount() == 0) { - return; - } - - {//Mark all of the updated sections as being visible from last frame - for (int id : this.updatedSectionIds) { - long ptr = UploadStream.INSTANCE.upload(viewport.visibilityBuffer, id * 4L, 4); - MemoryUtil.memPutInt(ptr, viewport.frameId - 1);//(visible from last frame) - } - } - } - - glDisable(GL_BLEND); - glEnable(GL_DEPTH_TEST); - - //Update and upload data - this.updateUniform(viewport); - UploadStream.INSTANCE.commit(); - - - this.terrain.bind(); - - RenderLayer.getCutoutMipped().startDrawing(); - - glBindVertexArray(AbstractFarWorldRenderer.STATIC_VAO); - this.bindResources(viewport); - - glDisable(GL_CULL_FACE); - glDrawMeshTasksNV(0, this.geometry.getSectionCount()); - glEnable(GL_CULL_FACE); - - glMemoryBarrier(GL_FRAMEBUFFER_BARRIER_BIT|GL_SHADER_STORAGE_BARRIER_BIT); - - this.cull.bind(); - this.bindResources(viewport); - glColorMask(false, false, false, false); - glDepthMask(false); - glEnable(GL_REPRESENTATIVE_FRAGMENT_TEST_NV); - glDrawElementsInstanced(GL_TRIANGLES, 6 * 2 * 3, GL_UNSIGNED_BYTE, (1 << 16) * 6 * 2, this.geometry.getSectionCount()); - glDisable(GL_REPRESENTATIVE_FRAGMENT_TEST_NV); - glDepthMask(true); - glColorMask(true, true, true, true); - - glBindVertexArray(0); - glBindSampler(0, 0); - glBindTextureUnit(0, 0); - RenderLayer.getCutoutMipped().endDrawing(); - } - - @Override - public void renderFarAwayTranslucent(NvMeshViewport viewport) { - if (this.geometry.getSectionCount()==0) { - return; - } - RenderLayer.getTranslucent().startDrawing(); - glBindVertexArray(AbstractFarWorldRenderer.STATIC_VAO); - glDisable(GL_CULL_FACE); - glEnable(GL_BLEND); - - //TODO: maybe change this so the alpha isnt applied in the same way or something?? since atm the texture bakery uses a very hacky - // blend equation to make it avoid double applying translucency - glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); - - - glBindSampler(0, this.models.getSamplerId()); - glBindTextureUnit(0, this.models.getTextureId()); - - this.translucent.bind(); - this.bindResources(viewport); - - glDrawMeshTasksNV(0, this.geometry.getSectionCount()); - - glEnable(GL_CULL_FACE); - glBindVertexArray(0); - - - glBindSampler(0, 0); - glBindTextureUnit(0, 0); - glDisable(GL_BLEND); - - RenderLayer.getTranslucent().endDrawing(); - } - - @Override - protected NvMeshViewport createViewport0() { - return new NvMeshViewport(this); - } - - - @Override - public void shutdown() { - super.shutdown(); - this.terrain.free(); - this.translucent.free(); - this.cull.free(); - } -} diff --git a/src/main/java/me/cortex/voxy/client/core/rendering/NvMeshViewport.java b/src/main/java/me/cortex/voxy/client/core/rendering/NvMeshViewport.java deleted file mode 100644 index 02532b36..00000000 --- a/src/main/java/me/cortex/voxy/client/core/rendering/NvMeshViewport.java +++ /dev/null @@ -1,19 +0,0 @@ -package me.cortex.voxy.client.core.rendering; - -import me.cortex.voxy.client.core.gl.GlBuffer; - -import static org.lwjgl.opengl.GL30C.GL_R8UI; -import static org.lwjgl.opengl.GL30C.GL_RED_INTEGER; -import static org.lwjgl.opengl.GL42.GL_UNSIGNED_BYTE; -import static org.lwjgl.opengl.GL45C.glClearNamedBufferData; - -public class NvMeshViewport extends Viewport { - GlBuffer visibilityBuffer; - public NvMeshViewport(NvMeshFarWorldRenderer renderer) { - this.visibilityBuffer = new GlBuffer(renderer.maxSections*4L).zero(); - } - - protected void delete0() { - this.visibilityBuffer.free(); - } -} diff --git a/src/main/java/me/cortex/voxy/client/core/rendering/RenderService.java b/src/main/java/me/cortex/voxy/client/core/rendering/RenderService.java new file mode 100644 index 00000000..77a79976 --- /dev/null +++ b/src/main/java/me/cortex/voxy/client/core/rendering/RenderService.java @@ -0,0 +1,56 @@ +package me.cortex.voxy.client.core.rendering; + +import me.cortex.voxy.client.config.VoxyConfig; +import me.cortex.voxy.client.core.model.OffThreadModelBakerySystem; +import me.cortex.voxy.client.core.rendering.building.BuiltSection; +import me.cortex.voxy.client.core.rendering.building.RenderGenerationService; +import me.cortex.voxy.common.world.WorldEngine; +import net.minecraft.client.render.Camera; + +import java.util.List; + +public class RenderService { + private final OffThreadModelBakerySystem modelService; + private final RenderGenerationService renderGen; + + public RenderService(WorldEngine world) { + this.modelService = new OffThreadModelBakerySystem(world.getMapper()); + this.renderGen = new RenderGenerationService(world, this.modelService, VoxyConfig.CONFIG.renderThreads, this::consumeRenderBuildResult, false); + + this.renderGen.enqueueTask(0,0,0,0); + } + + private void consumeRenderBuildResult(BuiltSection section) { + System.err.println("Section " + WorldEngine.pprintPos(section.position)); + section.free(); + } + + public void setup(Camera camera) { + this.modelService.syncChanges(); + } + + public void renderFarAwayOpaque(Viewport viewport) { + + } + + public void renderFarAwayTranslucent(Viewport viewport) { + + } + + public void addDebugData(List debug) { + this.modelService.addDebugData(debug); + this.renderGen.addDebugData(debug); + } + + public Viewport createViewport() { + return new RenderServiceViewport(); + } + + + + + public void shutdown() { + this.modelService.shutdown(); + this.renderGen.shutdown(); + } +} diff --git a/src/main/java/me/cortex/voxy/client/core/rendering/RenderServiceViewport.java b/src/main/java/me/cortex/voxy/client/core/rendering/RenderServiceViewport.java new file mode 100644 index 00000000..8d993840 --- /dev/null +++ b/src/main/java/me/cortex/voxy/client/core/rendering/RenderServiceViewport.java @@ -0,0 +1,10 @@ +package me.cortex.voxy.client.core.rendering; + +import me.cortex.voxy.client.core.gl.GlBuffer; + +public class RenderServiceViewport extends Viewport { + @Override + protected void delete0() { + + } +} diff --git a/src/main/java/me/cortex/voxy/client/core/rendering/RenderTracker.java b/src/main/java/me/cortex/voxy/client/core/rendering/RenderTracker.java index 349fd5b0..43042936 100644 --- a/src/main/java/me/cortex/voxy/client/core/rendering/RenderTracker.java +++ b/src/main/java/me/cortex/voxy/client/core/rendering/RenderTracker.java @@ -4,12 +4,9 @@ import it.unimi.dsi.fastutil.longs.LongOpenHashSet; import it.unimi.dsi.fastutil.longs.LongSet; 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.geometry.OLD.AbstractFarWorldRenderer; import me.cortex.voxy.common.world.WorldEngine; import me.cortex.voxy.common.world.WorldSection; -import net.minecraft.client.MinecraftClient; -import net.minecraft.util.math.Direction; - -import java.util.concurrent.ConcurrentHashMap; //Tracks active sections, dispatches updates to the build system, everything related to rendering flows through here public class RenderTracker { diff --git a/src/main/java/me/cortex/voxy/client/core/rendering/Viewport.java b/src/main/java/me/cortex/voxy/client/core/rendering/Viewport.java index d6564835..f90467b7 100644 --- a/src/main/java/me/cortex/voxy/client/core/rendering/Viewport.java +++ b/src/main/java/me/cortex/voxy/client/core/rendering/Viewport.java @@ -5,7 +5,7 @@ import org.joml.Matrix4f; public abstract class Viewport > { public int width; public int height; - int frameId; + public int frameId; public Matrix4f projection; public Matrix4f modelView; public double cameraX; diff --git a/src/main/java/me/cortex/voxy/client/core/rendering/building/RenderDataFactory.java b/src/main/java/me/cortex/voxy/client/core/rendering/building/RenderDataFactory.java index c736114d..00e696d0 100644 --- a/src/main/java/me/cortex/voxy/client/core/rendering/building/RenderDataFactory.java +++ b/src/main/java/me/cortex/voxy/client/core/rendering/building/RenderDataFactory.java @@ -2,22 +2,21 @@ package me.cortex.voxy.client.core.rendering.building; import it.unimi.dsi.fastutil.longs.LongArrayList; import me.cortex.voxy.client.core.Capabilities; -import me.cortex.voxy.client.core.model.ModelManager; +import me.cortex.voxy.client.core.model.ModelFactory; +import me.cortex.voxy.client.core.model.ModelQueries; import me.cortex.voxy.client.core.util.Mesher2D; import me.cortex.voxy.common.util.MemoryBuffer; import me.cortex.voxy.common.world.WorldEngine; import me.cortex.voxy.common.world.WorldSection; import me.cortex.voxy.common.world.other.Mapper; -import net.minecraft.block.FluidBlock; import org.lwjgl.system.MemoryUtil; -import java.lang.reflect.Array; import java.util.Arrays; public class RenderDataFactory { private final WorldEngine world; - private final ModelManager modelMan; + private final ModelFactory modelMan; private final Mesher2D negativeMesher = new Mesher2D(5, 15); private final Mesher2D positiveMesher = new Mesher2D(5, 15); @@ -39,7 +38,7 @@ public class RenderDataFactory { private int maxX; private int maxY; private int maxZ; - public RenderDataFactory(WorldEngine world, ModelManager modelManager, boolean emitMeshlets) { + public RenderDataFactory(WorldEngine world, ModelFactory modelManager, boolean emitMeshlets) { this.world = world; this.modelMan = modelManager; this.generateMeshlets = emitMeshlets; @@ -333,14 +332,13 @@ public class RenderDataFactory { if (Mapper.isAir(self)) continue; int selfBlockId = Mapper.getBlockId(self); - long selfMetadata = this.modelMan.getModelMetadata(selfBlockId); - - + int selfClientModelId = this.modelMan.getModelId(selfBlockId); + long selfMetadata = this.modelMan.getModelMetadataFromClientId(selfClientModelId); boolean putFace = false; //Branch into 2 paths, the + direction and -direction, doing it at once makes it much faster as it halves the number of loops - if (ModelManager.faceExists(selfMetadata, axisId<<1) || ModelManager.containsFluid(selfMetadata)) {//- direction + if (ModelQueries.faceExists(selfMetadata, axisId<<1) || ModelQueries.containsFluid(selfMetadata)) {//- direction long facingState = Mapper.AIR; //Need to access the other connecting section if (primary == 0) { @@ -359,14 +357,16 @@ public class RenderDataFactory { facingState = this.sectionCache[WorldSection.getIndex(x-aX, y-aY, z-aZ)]; } - if (!ModelManager.isFluid(selfMetadata)) { - putFace |= this.putFaceIfCan(this.negativeMesher, (axisId << 1), (axisId << 1)|1, self, selfMetadata, selfBlockId, facingState, a, b); + int facingClientModelId = this.modelMan.getModelId(Mapper.getBlockId(facingState)); + long facingMetadata = this.modelMan.getModelMetadataFromClientId(facingClientModelId); + if (!ModelQueries.isFluid(selfMetadata)) { + putFace |= this.putFaceIfCan(this.negativeMesher, (axisId << 1), (axisId << 1)|1, self, selfMetadata, selfClientModelId, selfBlockId, facingState, facingMetadata, a, b); } - if (ModelManager.containsFluid(selfMetadata)) { - putFace |= this.putFluidFaceIfCan(this.negativeFluidMesher, (axisId << 1), (axisId << 1)|1, self, selfMetadata, selfBlockId, facingState, a, b); + if (ModelQueries.containsFluid(selfMetadata)) { + putFace |= this.putFluidFaceIfCan(this.negativeFluidMesher, (axisId << 1), (axisId << 1)|1, self, selfMetadata, selfClientModelId, selfBlockId, facingState, facingMetadata, facingClientModelId, a, b); } } - if (ModelManager.faceExists(selfMetadata, (axisId<<1)|1) || ModelManager.containsFluid(selfMetadata)) {//+ direction + if (ModelQueries.faceExists(selfMetadata, (axisId<<1)|1) || ModelQueries.containsFluid(selfMetadata)) {//+ direction long facingState = Mapper.AIR; //Need to access the other connecting section if (primary == 31) { @@ -384,11 +384,14 @@ public class RenderDataFactory { } else { facingState = this.sectionCache[WorldSection.getIndex(x+aX, y+aY, z+aZ)]; } - if (!ModelManager.isFluid(selfMetadata)) { - putFace |= this.putFaceIfCan(this.positiveMesher, (axisId << 1) | 1, (axisId << 1), self, selfMetadata, selfBlockId, facingState, a, b); + + int facingClientModelId = this.modelMan.getModelId(Mapper.getBlockId(facingState)); + long facingMetadata = this.modelMan.getModelMetadataFromClientId(facingClientModelId); + if (!ModelQueries.isFluid(selfMetadata)) { + putFace |= this.putFaceIfCan(this.positiveMesher, (axisId << 1) | 1, (axisId << 1), self, selfMetadata, selfClientModelId, selfBlockId, facingState, facingMetadata, a, b); } - if (ModelManager.containsFluid(selfMetadata)) { - putFace |= this.putFluidFaceIfCan(this.positiveFluidMesher, (axisId << 1) | 1, (axisId << 1), self, selfMetadata, selfBlockId, facingState, a, b); + if (ModelQueries.containsFluid(selfMetadata)) { + putFace |= this.putFluidFaceIfCan(this.positiveFluidMesher, (axisId << 1) | 1, (axisId << 1), self, selfMetadata, selfClientModelId, selfBlockId, facingState, facingMetadata, facingClientModelId, a, b); } } @@ -414,15 +417,13 @@ public class RenderDataFactory { //Returns true if a face was placed - private boolean putFluidFaceIfCan(Mesher2D mesher, int face, int opposingFace, long self, long metadata, int selfBlockId, long facingState, int a, int b) { - long facingMetadata = this.modelMan.getModelMetadata(Mapper.getBlockId(facingState)); - - int selfFluidClientId = this.modelMan.getFluidClientStateId(this.modelMan.getModelId(selfBlockId)); + private boolean putFluidFaceIfCan(Mesher2D mesher, int face, int opposingFace, long self, long metadata, int selfClientModelId, int selfBlockId, long facingState, long facingMetadata, int facingClientModelId, int a, int b) { + int selfFluidClientId = this.modelMan.getFluidClientStateId(selfClientModelId); long selfFluidMetadata = this.modelMan.getModelMetadataFromClientId(selfFluidClientId); int facingFluidClientId = -1; - if (ModelManager.containsFluid(facingMetadata)) { - facingFluidClientId = this.modelMan.getFluidClientStateId(this.modelMan.getModelId(Mapper.getBlockId(facingState))); + if (ModelQueries.containsFluid(facingMetadata)) { + facingFluidClientId = this.modelMan.getFluidClientStateId(facingClientModelId); } //If both of the states are the same, then dont render the fluid face @@ -431,18 +432,19 @@ public class RenderDataFactory { } if (facingFluidClientId != -1) { + //TODO: OPTIMIZE if (this.world.getMapper().getBlockStateFromBlockId(selfBlockId).getBlock() == this.world.getMapper().getBlockStateFromBlockId(Mapper.getBlockId(facingState)).getBlock()) { return false; } } - if (ModelManager.faceOccludes(facingMetadata, opposingFace)) { + if (ModelQueries.faceOccludes(facingMetadata, opposingFace)) { return false; } //if the model has a fluid state but is not a liquid need to see if the solid state had a face rendered and that face is occluding, if so, dont render the fluid state face - if ((!ModelManager.isFluid(metadata)) && ModelManager.faceOccludes(metadata, face)) { + if ((!ModelQueries.isFluid(metadata)) && ModelQueries.faceOccludes(metadata, face)) { return false; } @@ -458,32 +460,28 @@ public class RenderDataFactory { long otherFlags = 0; - otherFlags |= ModelManager.isTranslucent(selfFluidMetadata)?1L<<33:0; - otherFlags |= ModelManager.isDoubleSided(selfFluidMetadata)?1L<<34:0; - mesher.put(a, b, ((long)selfFluidClientId) | (((long) Mapper.getLightId(ModelManager.faceUsesSelfLighting(selfFluidMetadata, face)?self:facingState))<<16) | ((((long) Mapper.getBiomeId(self))<<24) * (ModelManager.isBiomeColoured(selfFluidMetadata)?1:0)) | otherFlags); + otherFlags |= ModelQueries.isTranslucent(selfFluidMetadata)?1L<<33:0; + otherFlags |= ModelQueries.isDoubleSided(selfFluidMetadata)?1L<<34:0; + mesher.put(a, b, ((long)selfFluidClientId) | (((long) Mapper.getLightId(ModelQueries.faceUsesSelfLighting(selfFluidMetadata, face)?self:facingState))<<16) | ((((long) Mapper.getBiomeId(self))<<24) * (ModelQueries.isBiomeColoured(selfFluidMetadata)?1:0)) | otherFlags); return true; } //Returns true if a face was placed - private boolean putFaceIfCan(Mesher2D mesher, int face, int opposingFace, long self, long metadata, int selfBlockId, long facingState, int a, int b) { - long facingMetadata = this.modelMan.getModelMetadata(Mapper.getBlockId(facingState)); - + private boolean putFaceIfCan(Mesher2D mesher, int face, int opposingFace, long self, long metadata, int clientModelId, int selfBlockId, long facingState, long facingMetadata, int a, int b) { //If face can be occluded and is occluded from the facing block, then dont render the face - if (ModelManager.faceCanBeOccluded(metadata, face) && ModelManager.faceOccludes(facingMetadata, opposingFace)) { + if (ModelQueries.faceCanBeOccluded(metadata, face) && ModelQueries.faceOccludes(facingMetadata, opposingFace)) { return false; } - if (ModelManager.cullsSame(metadata) && selfBlockId == Mapper.getBlockId(facingState)) { + if (ModelQueries.cullsSame(metadata) && selfBlockId == Mapper.getBlockId(facingState)) { //If we are facing a block, and we are both the same state, dont render that face return false; } - - int clientModelId = this.modelMan.getModelId(selfBlockId); long otherFlags = 0; - otherFlags |= ModelManager.isTranslucent(metadata)?1L<<33:0; - otherFlags |= ModelManager.isDoubleSided(metadata)?1L<<34:0; - mesher.put(a, b, ((long)clientModelId) | (((long) Mapper.getLightId(ModelManager.faceUsesSelfLighting(metadata, face)?self:facingState))<<16) | ((((long) Mapper.getBiomeId(self))<<24) * (ModelManager.isBiomeColoured(metadata)?1:0)) | otherFlags); + otherFlags |= ModelQueries.isTranslucent(metadata)?1L<<33:0; + otherFlags |= ModelQueries.isDoubleSided(metadata)?1L<<34:0; + mesher.put(a, b, ((long)clientModelId) | (((long) Mapper.getLightId(ModelQueries.faceUsesSelfLighting(metadata, face)?self:facingState))<<16) | ((((long) Mapper.getBiomeId(self))<<24) * (ModelQueries.isBiomeColoured(metadata)?1:0)) | otherFlags); return true; } diff --git a/src/main/java/me/cortex/voxy/client/core/rendering/building/RenderGenerationService.java b/src/main/java/me/cortex/voxy/client/core/rendering/building/RenderGenerationService.java index 0d9cb4cf..b04a58b1 100644 --- a/src/main/java/me/cortex/voxy/client/core/rendering/building/RenderGenerationService.java +++ b/src/main/java/me/cortex/voxy/client/core/rendering/building/RenderGenerationService.java @@ -1,23 +1,26 @@ package me.cortex.voxy.client.core.rendering.building; +import it.unimi.dsi.fastutil.ints.IntOpenHashSet; import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap; import me.cortex.voxy.client.core.model.IdNotYetComputedException; -import me.cortex.voxy.client.core.model.ModelManager; +import me.cortex.voxy.client.core.model.ModelFactory; +import me.cortex.voxy.client.core.model.OffThreadModelBakerySystem; import me.cortex.voxy.common.world.WorldEngine; import me.cortex.voxy.common.world.WorldSection; +import me.cortex.voxy.common.world.other.Mapper; import net.minecraft.client.MinecraftClient; import net.minecraft.text.Text; -import java.util.concurrent.ConcurrentHashMap; +import java.lang.ref.WeakReference; +import java.util.List; import java.util.concurrent.Semaphore; import java.util.function.Consumer; import java.util.function.Supplier; -import java.util.function.ToIntFunction; //TODO: Add a render cache public class RenderGenerationService { public interface TaskChecker {boolean check(int lvl, int x, int y, int z);} - private record BuildTask(long position, Supplier sectionSupplier) {} + private record BuildTask(long position, Supplier sectionSupplier, boolean[] hasDoneModelRequest) {} private volatile boolean running = true; private final Thread[] workers; @@ -26,15 +29,15 @@ public class RenderGenerationService { private final Semaphore taskCounter = new Semaphore(0); private final WorldEngine world; - private final ModelManager modelManager; + private final OffThreadModelBakerySystem modelBakery; private final Consumer resultConsumer; private final BuiltSectionMeshCache meshCache = new BuiltSectionMeshCache(); private final boolean emitMeshlets; - public RenderGenerationService(WorldEngine world, ModelManager modelManager, int workers, Consumer consumer, boolean emitMeshlets) { + public RenderGenerationService(WorldEngine world, OffThreadModelBakerySystem modelBakery, int workers, Consumer consumer, boolean emitMeshlets) { this.emitMeshlets = emitMeshlets; this.world = world; - this.modelManager = modelManager; + this.modelBakery = modelBakery; this.resultConsumer = consumer; this.workers = new Thread[workers]; for (int i = 0; i < workers; i++) { @@ -45,10 +48,26 @@ public class RenderGenerationService { } } + //NOTE: the biomes are always fully populated/kept up to date + + //Asks the Model system to bake all blocks that currently dont have a model + private void computeAndRequestRequiredModels(WorldSection section) { + var raw = section.copyData();//TODO: replace with copyDataTo and use a "thread local"/context array to reduce allocation rates + IntOpenHashSet seen = new IntOpenHashSet(128); + for (long state : raw) { + int block = Mapper.getBlockId(state); + if (!this.modelBakery.factory.hasModelForBlockId(block)) { + if (seen.add(block)) { + this.modelBakery.requestBlockBake(block); + } + } + } + } + //TODO: add a generated render data cache private void renderWorker() { //Thread local instance of the factory - var factory = new RenderDataFactory(this.world, this.modelManager, this.emitMeshlets); + var factory = new RenderDataFactory(this.world, this.modelBakery.factory, this.emitMeshlets); while (this.running) { this.taskCounter.acquireUninterruptibly(); if (!this.running) break; @@ -67,17 +86,28 @@ public class RenderGenerationService { try { mesh = factory.generateMesh(section); } catch (IdNotYetComputedException e) { - try { - Thread.sleep(100); - } catch (InterruptedException ex) { - throw new RuntimeException(ex); + if (task.hasDoneModelRequest[0]) { + try { + Thread.sleep(100); + } catch (InterruptedException ex) { + throw new RuntimeException(ex); + } + } else { + this.computeAndRequestRequiredModels(section); } //We need to reinsert the build task into the queue //System.err.println("Render task failed to complete due to un-computed client id"); synchronized (this.taskQueue) { - this.taskQueue.computeIfAbsent(section.key, key->{this.taskCounter.release(); return task;}); + var queuedTask = this.taskQueue.computeIfAbsent(section.key, (a)->task); + queuedTask.hasDoneModelRequest[0] = true;//Mark (or remark) the section as having chunks requested + + if (queuedTask == task) {//use the == not .equal to see if we need to release a permit + this.taskCounter.release();//Since we put in queue, release permit + } } } + + //TODO: if the section was _not_ built, maybe dont release it, or release it with the hint section.release(); if (mesh != null) { //TODO: if the mesh is null, need to clear the cache at that point @@ -136,7 +166,7 @@ public class RenderGenerationService { } else { return null; } - }); + }, new boolean[1]); }); } } @@ -196,4 +226,8 @@ public class RenderGenerationService { } this.meshCache.free(); } + + public void addDebugData(List debug) { + + } } diff --git a/src/main/java/me/cortex/voxy/client/core/rendering/geometry/AbstractGeometryRenderer.java b/src/main/java/me/cortex/voxy/client/core/rendering/geometry/AbstractGeometryRenderer.java new file mode 100644 index 00000000..5545d61b --- /dev/null +++ b/src/main/java/me/cortex/voxy/client/core/rendering/geometry/AbstractGeometryRenderer.java @@ -0,0 +1,6 @@ +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 { +} diff --git a/src/main/java/me/cortex/voxy/client/core/rendering/AbstractFarWorldRenderer.java b/src/main/java/me/cortex/voxy/client/core/rendering/geometry/OLD/AbstractFarWorldRenderer.java similarity index 95% rename from src/main/java/me/cortex/voxy/client/core/rendering/AbstractFarWorldRenderer.java rename to src/main/java/me/cortex/voxy/client/core/rendering/geometry/OLD/AbstractFarWorldRenderer.java index fb2fed06..77e715ae 100644 --- a/src/main/java/me/cortex/voxy/client/core/rendering/AbstractFarWorldRenderer.java +++ b/src/main/java/me/cortex/voxy/client/core/rendering/geometry/OLD/AbstractFarWorldRenderer.java @@ -1,4 +1,4 @@ -package me.cortex.voxy.client.core.rendering; +package me.cortex.voxy.client.core.rendering.geometry.OLD; //NOTE: an idea on how to do it is so that any render section, we _keep_ aquired (yes this will be very memory intensive) // could maybe tosomething else @@ -6,7 +6,8 @@ package me.cortex.voxy.client.core.rendering; import com.mojang.blaze3d.systems.RenderSystem; import it.unimi.dsi.fastutil.ints.IntArrayList; import me.cortex.voxy.client.core.gl.GlBuffer; -import me.cortex.voxy.client.core.model.ModelManager; +import me.cortex.voxy.client.core.model.ModelFactory; +import me.cortex.voxy.client.core.rendering.Viewport; import me.cortex.voxy.client.core.rendering.building.BuiltSection; import me.cortex.voxy.client.core.rendering.util.DownloadStream; import me.cortex.voxy.client.core.rendering.util.UploadStream; @@ -19,7 +20,6 @@ import net.minecraft.util.Identifier; import org.joml.FrustumIntersection; import org.lwjgl.system.MemoryUtil; -import java.util.ArrayList; import java.util.List; import java.util.concurrent.ConcurrentLinkedDeque; @@ -35,12 +35,12 @@ import static org.lwjgl.opengl.GL30.*; //Todo: tinker with having the compute shader where each thread is a position to render? maybe idk -public abstract class AbstractFarWorldRenderer implements IRenderInterface { +public abstract class AbstractFarWorldRenderer { public static final int STATIC_VAO = glGenVertexArrays(); protected final GlBuffer uniformBuffer; protected final J geometry; - protected final ModelManager models; + protected final ModelFactory models; protected final GlBuffer lightDataBuffer; protected final int maxSections; @@ -58,7 +58,7 @@ public abstract class AbstractFarWorldRenderer blockStateUpdates = new ConcurrentLinkedDeque<>(); private final ConcurrentLinkedDeque biomeUpdates = new ConcurrentLinkedDeque<>(); - public AbstractFarWorldRenderer(ModelManager models, J geometry) { + public AbstractFarWorldRenderer(ModelFactory models, J geometry) { this.maxSections = geometry.getMaxSections(); this.uniformBuffer = new GlBuffer(1024); this.lightDataBuffer = new GlBuffer(256*4);//256 of uint diff --git a/src/main/java/me/cortex/voxy/client/core/rendering/AbstractGeometryManager.java b/src/main/java/me/cortex/voxy/client/core/rendering/geometry/OLD/AbstractGeometryManager.java similarity index 93% rename from src/main/java/me/cortex/voxy/client/core/rendering/AbstractGeometryManager.java rename to src/main/java/me/cortex/voxy/client/core/rendering/geometry/OLD/AbstractGeometryManager.java index b1adcfc6..29d5d485 100644 --- a/src/main/java/me/cortex/voxy/client/core/rendering/AbstractGeometryManager.java +++ b/src/main/java/me/cortex/voxy/client/core/rendering/geometry/OLD/AbstractGeometryManager.java @@ -1,4 +1,4 @@ -package me.cortex.voxy.client.core.rendering; +package me.cortex.voxy.client.core.rendering.geometry.OLD; import it.unimi.dsi.fastutil.ints.IntArrayList; import me.cortex.voxy.client.core.rendering.building.BuiltSection; diff --git a/src/main/java/me/cortex/voxy/client/core/rendering/DefaultGeometryManager.java b/src/main/java/me/cortex/voxy/client/core/rendering/geometry/OLD/DefaultGeometryManager.java similarity index 98% rename from src/main/java/me/cortex/voxy/client/core/rendering/DefaultGeometryManager.java rename to src/main/java/me/cortex/voxy/client/core/rendering/geometry/OLD/DefaultGeometryManager.java index 8dbc6036..daffa753 100644 --- a/src/main/java/me/cortex/voxy/client/core/rendering/DefaultGeometryManager.java +++ b/src/main/java/me/cortex/voxy/client/core/rendering/geometry/OLD/DefaultGeometryManager.java @@ -1,4 +1,4 @@ -package me.cortex.voxy.client.core.rendering; +package me.cortex.voxy.client.core.rendering.geometry.OLD; import it.unimi.dsi.fastutil.ints.IntArrayList; import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap; @@ -12,8 +12,6 @@ import net.minecraft.client.MinecraftClient; import net.minecraft.text.Text; import org.lwjgl.system.MemoryUtil; -import java.util.concurrent.ConcurrentLinkedDeque; - public class DefaultGeometryManager extends AbstractGeometryManager { private static final int SECTION_METADATA_SIZE = 32; private final Long2IntOpenHashMap pos2id = new Long2IntOpenHashMap(); diff --git a/src/main/java/me/cortex/voxy/client/core/rendering/Gl46FarWorldRenderer.java b/src/main/java/me/cortex/voxy/client/core/rendering/geometry/OLD/Gl46FarWorldRenderer.java similarity index 92% rename from src/main/java/me/cortex/voxy/client/core/rendering/Gl46FarWorldRenderer.java rename to src/main/java/me/cortex/voxy/client/core/rendering/geometry/OLD/Gl46FarWorldRenderer.java index 81428e0d..0ad7c2a9 100644 --- a/src/main/java/me/cortex/voxy/client/core/rendering/Gl46FarWorldRenderer.java +++ b/src/main/java/me/cortex/voxy/client/core/rendering/geometry/OLD/Gl46FarWorldRenderer.java @@ -1,30 +1,23 @@ -package me.cortex.voxy.client.core.rendering; +package me.cortex.voxy.client.core.rendering.geometry.OLD; 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.ModelManager; -import me.cortex.voxy.client.core.rendering.util.DownloadStream; +import me.cortex.voxy.client.core.model.ModelFactory; +import me.cortex.voxy.client.core.rendering.SharedIndexBuffer; import me.cortex.voxy.client.core.rendering.util.UploadStream; import me.cortex.voxy.client.mixin.joml.AccessFrustumIntersection; -import net.minecraft.block.Blocks; import net.minecraft.client.render.RenderLayer; -import net.minecraft.client.util.math.MatrixStack; import org.joml.Matrix4f; import org.joml.Vector3f; -import org.lwjgl.opengl.GL11C; import org.lwjgl.system.MemoryUtil; -import java.util.List; - import static org.lwjgl.opengl.ARBIndirectParameters.GL_PARAMETER_BUFFER_ARB; import static org.lwjgl.opengl.ARBIndirectParameters.glMultiDrawElementsIndirectCountARB; import static org.lwjgl.opengl.GL11.GL_TRIANGLES; import static org.lwjgl.opengl.GL11.GL_UNSIGNED_SHORT; -import static org.lwjgl.opengl.GL11.glGetInteger; import static org.lwjgl.opengl.GL14C.glBlendFuncSeparate; import static org.lwjgl.opengl.GL30.glBindVertexArray; -import static org.lwjgl.opengl.GL30C.GL_R8UI; import static org.lwjgl.opengl.GL30C.GL_RED_INTEGER; import static org.lwjgl.opengl.GL40C.GL_DRAW_INDIRECT_BUFFER; import static org.lwjgl.opengl.GL42.*; @@ -55,7 +48,7 @@ public class Gl46FarWorldRenderer extends AbstractFarWorldRenderer, AbstractRenderWorldInteractor { +public class Gl46HierarchicalRenderer { private final HierarchicalOcclusionRenderer sectionSelector; private final MeshManager meshManager = new MeshManager(); @@ -76,25 +51,25 @@ public class Gl46HierarchicalRenderer implements IRenderInterface buildResults = new ConcurrentLinkedDeque<>(); - private final ModelManager modelManager; + private final ModelFactory modelManager; private RenderGenerationService sectionGenerationService; private Consumer resultConsumer; - public Gl46HierarchicalRenderer(ModelManager model) { + public Gl46HierarchicalRenderer(ModelFactory model) { this.modelManager = model; this.sectionSelector = new HierarchicalOcclusionRenderer(new INodeInteractor() { - @Override + public void watchUpdates(long pos) { //System.err.println("Watch: " + pos); } - @Override + public void unwatchUpdates(long pos) { //System.err.println("Unwatch: " + pos); } - @Override + public void requestMesh(long pos) { Gl46HierarchicalRenderer.this.sectionGenerationService.enqueueTask( WorldEngine.getLevel(pos), @@ -104,14 +79,13 @@ public class Gl46HierarchicalRenderer implements IRenderInterface mesh) { Gl46HierarchicalRenderer.this.resultConsumer = mesh; } }, this.meshManager, this.printf); } - @Override public void setupRender(Frustum frustum, Camera camera) { {//Tick upload and download queues UploadStream.INSTANCE.tick(); @@ -143,7 +117,7 @@ public class Gl46HierarchicalRenderer implements IRenderInterface debug) { debug.add("Printf Queue: "); debug.addAll(this.printfQueue); @@ -185,12 +159,12 @@ public class Gl46HierarchicalRenderer implements IRenderInterface { public Gl46HierarchicalViewport(Gl46HierarchicalRenderer renderer) { diff --git a/src/main/java/me/cortex/voxy/client/core/rendering/Gl46Viewport.java b/src/main/java/me/cortex/voxy/client/core/rendering/geometry/OLD/Gl46Viewport.java similarity index 66% rename from src/main/java/me/cortex/voxy/client/core/rendering/Gl46Viewport.java rename to src/main/java/me/cortex/voxy/client/core/rendering/geometry/OLD/Gl46Viewport.java index fbe1c985..d6d599ad 100644 --- a/src/main/java/me/cortex/voxy/client/core/rendering/Gl46Viewport.java +++ b/src/main/java/me/cortex/voxy/client/core/rendering/geometry/OLD/Gl46Viewport.java @@ -1,13 +1,10 @@ -package me.cortex.voxy.client.core.rendering; +package me.cortex.voxy.client.core.rendering.geometry.OLD; import me.cortex.voxy.client.core.gl.GlBuffer; +import me.cortex.voxy.client.core.rendering.Viewport; import static org.lwjgl.opengl.ARBIndirectParameters.glMultiDrawElementsIndirectCountARB; -import static org.lwjgl.opengl.GL30C.GL_R8UI; -import static org.lwjgl.opengl.GL30C.GL_RED_INTEGER; -import static org.lwjgl.opengl.GL42.GL_UNSIGNED_BYTE; import static org.lwjgl.opengl.GL45C.glClearNamedBufferData; -import static org.lwjgl.opengl.GL45C.nglClearNamedBufferData; public class Gl46Viewport extends Viewport { GlBuffer visibilityBuffer; diff --git a/src/main/java/me/cortex/voxy/client/core/rendering/hierarchical/DebugRenderer.java b/src/main/java/me/cortex/voxy/client/core/rendering/hierarchical/DebugRenderer.java index 83e1ca2c..2a616c49 100644 --- a/src/main/java/me/cortex/voxy/client/core/rendering/hierarchical/DebugRenderer.java +++ b/src/main/java/me/cortex/voxy/client/core/rendering/hierarchical/DebugRenderer.java @@ -3,8 +3,8 @@ package me.cortex.voxy.client.core.rendering.hierarchical; 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.rendering.AbstractFarWorldRenderer; -import me.cortex.voxy.client.core.rendering.Gl46HierarchicalViewport; +import me.cortex.voxy.client.core.rendering.geometry.OLD.AbstractFarWorldRenderer; +import me.cortex.voxy.client.core.rendering.geometry.OLD.Gl46HierarchicalViewport; import me.cortex.voxy.client.core.rendering.SharedIndexBuffer; import me.cortex.voxy.client.core.rendering.util.UploadStream; import net.minecraft.util.math.MathHelper; diff --git a/src/main/java/me/cortex/voxy/client/core/rendering/hierarchical/HierarchicalOcclusionRenderer.java b/src/main/java/me/cortex/voxy/client/core/rendering/hierarchical/HierarchicalOcclusionRenderer.java index f946861c..14b73494 100644 --- a/src/main/java/me/cortex/voxy/client/core/rendering/hierarchical/HierarchicalOcclusionRenderer.java +++ b/src/main/java/me/cortex/voxy/client/core/rendering/hierarchical/HierarchicalOcclusionRenderer.java @@ -4,10 +4,9 @@ import me.cortex.voxy.client.core.gl.GlBuffer; import me.cortex.voxy.client.core.gl.shader.PrintfInjector; import me.cortex.voxy.client.core.gl.shader.Shader; import me.cortex.voxy.client.core.gl.shader.ShaderType; -import me.cortex.voxy.client.core.rendering.Gl46HierarchicalViewport; +import me.cortex.voxy.client.core.rendering.geometry.OLD.Gl46HierarchicalViewport; import me.cortex.voxy.client.core.rendering.HiZBuffer; import me.cortex.voxy.client.core.rendering.util.UploadStream; -import me.cortex.voxy.client.mixin.joml.AccessFrustumIntersection; import net.minecraft.util.math.MathHelper; import org.joml.Matrix4f; import org.joml.Vector3f; @@ -88,6 +87,8 @@ public class HierarchicalOcclusionRenderer { UploadStream.INSTANCE.commit(); + //FIXME: need to have the hiz respect the stencil mask aswell to mask away normal terrain, (much increase perf) + //Make hiz this.hiz.buildMipChain(depthBuffer, viewport.width, viewport.height); glMemoryBarrier(GL_PIXEL_BUFFER_BARRIER_BIT); diff --git a/src/main/java/me/cortex/voxy/client/core/rendering/section/AbstractSectionRenderer.java b/src/main/java/me/cortex/voxy/client/core/rendering/section/AbstractSectionRenderer.java index 14361f5c..0e440f79 100644 --- a/src/main/java/me/cortex/voxy/client/core/rendering/section/AbstractSectionRenderer.java +++ b/src/main/java/me/cortex/voxy/client/core/rendering/section/AbstractSectionRenderer.java @@ -2,7 +2,7 @@ package me.cortex.voxy.client.core.rendering.section; import me.cortex.voxy.client.core.gl.GlBuffer; -import me.cortex.voxy.client.core.rendering.Gl46HierarchicalViewport; +import me.cortex.voxy.client.core.rendering.geometry.OLD.Gl46HierarchicalViewport; //Takes in mesh ids from the hierachical traversal and may perform more culling then renders it public abstract class AbstractSectionRenderer { diff --git a/src/main/java/me/cortex/voxy/client/core/rendering/section/MDICSectionRenderer.java b/src/main/java/me/cortex/voxy/client/core/rendering/section/MDICSectionRenderer.java index 5c24e1e2..565daada 100644 --- a/src/main/java/me/cortex/voxy/client/core/rendering/section/MDICSectionRenderer.java +++ b/src/main/java/me/cortex/voxy/client/core/rendering/section/MDICSectionRenderer.java @@ -2,7 +2,7 @@ package me.cortex.voxy.client.core.rendering.section; import me.cortex.voxy.client.core.gl.GlBuffer; -import me.cortex.voxy.client.core.rendering.Gl46HierarchicalViewport; +import me.cortex.voxy.client.core.rendering.geometry.OLD.Gl46HierarchicalViewport; //Uses MDIC to render the sections public class MDICSectionRenderer extends AbstractSectionRenderer { diff --git a/src/main/java/me/cortex/voxy/client/core/rendering/util/BufferArena.java b/src/main/java/me/cortex/voxy/client/core/rendering/util/BufferArena.java index 0edf233c..7485c68c 100644 --- a/src/main/java/me/cortex/voxy/client/core/rendering/util/BufferArena.java +++ b/src/main/java/me/cortex/voxy/client/core/rendering/util/BufferArena.java @@ -3,6 +3,7 @@ package me.cortex.voxy.client.core.rendering.util; import me.cortex.voxy.client.core.gl.GlBuffer; import me.cortex.voxy.client.core.util.AllocationArena; import me.cortex.voxy.common.util.MemoryBuffer; +import me.cortex.voxy.common.util.UnsafeUtil; import org.lwjgl.system.MemoryUtil; public class BufferArena { @@ -32,7 +33,7 @@ public class BufferArena { return -1; } long uploadPtr = UploadStream.INSTANCE.upload(this.buffer, addr * this.elementSize, buffer.size); - MemoryUtil.memCopy(buffer.address, uploadPtr, buffer.size); + UnsafeUtil.memcpy(buffer.address, uploadPtr, buffer.size); this.used += size; return addr; } diff --git a/src/main/java/me/cortex/voxy/client/core/rendering/util/DeferredUpload.java b/src/main/java/me/cortex/voxy/client/core/rendering/util/DeferredUpload.java new file mode 100644 index 00000000..1c1b1105 --- /dev/null +++ b/src/main/java/me/cortex/voxy/client/core/rendering/util/DeferredUpload.java @@ -0,0 +1,25 @@ +package me.cortex.voxy.client.core.rendering.util; + +import me.cortex.voxy.client.core.gl.GlBuffer; +import me.cortex.voxy.common.util.UnsafeUtil; +import org.lwjgl.system.MemoryUtil; + +//Just a utility for making a deferred upload (make on other thread then upload on render thread) +public final class DeferredUpload { + public final long ptr; + private final long size; + private final long offset; + private final GlBuffer buffer; + public DeferredUpload(GlBuffer buffer, long offset, long size) { + this.ptr = MemoryUtil.nmemAlloc(size); + this.offset = offset; + this.buffer = buffer; + this.size = size; + } + + public void upload() { + long upPtr = UploadStream.INSTANCE.upload(this.buffer, this.offset, this.size); + UnsafeUtil.memcpy(this.ptr, upPtr, this.size); + MemoryUtil.nmemFree(this.ptr); + } +} diff --git a/src/main/java/me/cortex/voxy/client/mixin/minecraft/MixinRenderSystem.java b/src/main/java/me/cortex/voxy/client/mixin/minecraft/MixinRenderSystem.java new file mode 100644 index 00000000..0a1f0cab --- /dev/null +++ b/src/main/java/me/cortex/voxy/client/mixin/minecraft/MixinRenderSystem.java @@ -0,0 +1,15 @@ +package me.cortex.voxy.client.mixin.minecraft; + +import com.mojang.blaze3d.systems.RenderSystem; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(RenderSystem.class) +public class MixinRenderSystem { + @Inject(method = {"assertOnRenderThread", "assertOnRenderThreadOrInit"}, at = @At("HEAD"), cancellable = true) + private static void cancelAssert(CallbackInfo ci) { + ci.cancel(); + } +} diff --git a/src/main/java/me/cortex/voxy/client/mixin/sodium/MixinRenderSectionManager.java b/src/main/java/me/cortex/voxy/client/mixin/sodium/MixinRenderSectionManager.java index 8fdcf1cc..a74f168f 100644 --- a/src/main/java/me/cortex/voxy/client/mixin/sodium/MixinRenderSectionManager.java +++ b/src/main/java/me/cortex/voxy/client/mixin/sodium/MixinRenderSectionManager.java @@ -17,9 +17,9 @@ public class MixinRenderSectionManager { @Inject(method = "onChunkRemoved", at = @At("HEAD")) private void injectIngest(int x, int z, CallbackInfo ci) { - var core = ((IGetVoxelCore)(world.worldRenderer)).getVoxelCore(); + var core = ((IGetVoxelCore)(this.world.worldRenderer)).getVoxelCore(); if (core != null && VoxyConfig.CONFIG.ingestEnabled) { - core.enqueueIngest(world.getChunk(x, z)); + core.enqueueIngest(this.world.getChunk(x, z)); } } } diff --git a/src/main/java/me/cortex/voxy/common/storage/StorageBackend.java b/src/main/java/me/cortex/voxy/common/storage/StorageBackend.java index 235b7302..d6569f9d 100644 --- a/src/main/java/me/cortex/voxy/common/storage/StorageBackend.java +++ b/src/main/java/me/cortex/voxy/common/storage/StorageBackend.java @@ -5,8 +5,10 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.List; +import java.util.function.LongConsumer; public abstract class StorageBackend { + public abstract void iterateStoredSectionPositions(LongConsumer consumer); public abstract ByteBuffer getSectionData(long key); diff --git a/src/main/java/me/cortex/voxy/common/storage/inmemory/MemoryStorageBackend.java b/src/main/java/me/cortex/voxy/common/storage/inmemory/MemoryStorageBackend.java index 863db42e..acd43707 100644 --- a/src/main/java/me/cortex/voxy/common/storage/inmemory/MemoryStorageBackend.java +++ b/src/main/java/me/cortex/voxy/common/storage/inmemory/MemoryStorageBackend.java @@ -13,6 +13,7 @@ import org.apache.commons.lang3.stream.Streams; import org.lwjgl.system.MemoryUtil; import java.nio.ByteBuffer; +import java.util.function.LongConsumer; public class MemoryStorageBackend extends StorageBackend { private final Long2ObjectMap[] maps; @@ -22,6 +23,19 @@ public class MemoryStorageBackend extends StorageBackend { this(4); } + private Long2ObjectMap getMap(long key) { + return this.maps[(int) (RandomSeed.mixStafford13(RandomSeed.mixStafford13(key)^key)&(this.maps.length-1))]; + } + + @Override + public void iterateStoredSectionPositions(LongConsumer consumer) { + for (var map : this.maps) { + synchronized (map) { + map.keySet().forEach(consumer); + } + } + } + public MemoryStorageBackend(int slicesBitCount) { this.maps = new Long2ObjectMap[1< getMap(long key) { - return this.maps[(int) (RandomSeed.mixStafford13(RandomSeed.mixStafford13(key)^key)&(this.maps.length-1))]; - } - @Override public ByteBuffer getSectionData(long key) { var map = this.getMap(key); diff --git a/src/main/java/me/cortex/voxy/common/storage/lmdb/LMDBStorageBackend.java b/src/main/java/me/cortex/voxy/common/storage/lmdb/LMDBStorageBackend.java index c41259b1..1764ac70 100644 --- a/src/main/java/me/cortex/voxy/common/storage/lmdb/LMDBStorageBackend.java +++ b/src/main/java/me/cortex/voxy/common/storage/lmdb/LMDBStorageBackend.java @@ -11,6 +11,7 @@ import java.nio.ByteBuffer; import java.util.Objects; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.ReentrantLock; +import java.util.function.LongConsumer; import java.util.function.Supplier; import static org.lwjgl.util.lmdb.LMDB.*; @@ -82,6 +83,11 @@ public class LMDBStorageBackend extends StorageBackend { } } + @Override + public void iterateStoredSectionPositions(LongConsumer consumer) { + throw new IllegalStateException("Not yet implemented"); + } + //TODO: make batch get and updates public ByteBuffer getSectionData(long key) { return this.synchronizedTransaction(() -> this.sectionDatabase.transaction(MDB_RDONLY, transaction->{ diff --git a/src/main/java/me/cortex/voxy/common/storage/other/DelegatingStorageAdaptor.java b/src/main/java/me/cortex/voxy/common/storage/other/DelegatingStorageAdaptor.java index 43ac97c5..6b2a3f2d 100644 --- a/src/main/java/me/cortex/voxy/common/storage/other/DelegatingStorageAdaptor.java +++ b/src/main/java/me/cortex/voxy/common/storage/other/DelegatingStorageAdaptor.java @@ -10,6 +10,7 @@ import org.lwjgl.system.MemoryUtil; import java.nio.ByteBuffer; import java.util.List; +import java.util.function.LongConsumer; public class DelegatingStorageAdaptor extends StorageBackend { protected final StorageBackend delegate; @@ -17,6 +18,9 @@ public class DelegatingStorageAdaptor extends StorageBackend { this.delegate = delegate; } + @Override + public void iterateStoredSectionPositions(LongConsumer consumer) {this.delegate.iterateStoredSectionPositions(consumer);} + @Override public ByteBuffer getSectionData(long key) { return this.delegate.getSectionData(key); diff --git a/src/main/java/me/cortex/voxy/common/storage/other/FragmentedStorageBackendAdaptor.java b/src/main/java/me/cortex/voxy/common/storage/other/FragmentedStorageBackendAdaptor.java index e22553bf..476bd843 100644 --- a/src/main/java/me/cortex/voxy/common/storage/other/FragmentedStorageBackendAdaptor.java +++ b/src/main/java/me/cortex/voxy/common/storage/other/FragmentedStorageBackendAdaptor.java @@ -12,6 +12,7 @@ import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.function.LongConsumer; //Segments the section data into multiple dbs public class FragmentedStorageBackendAdaptor extends StorageBackend { @@ -29,6 +30,11 @@ public class FragmentedStorageBackendAdaptor extends StorageBackend { return (int) (RandomSeed.mixStafford13(RandomSeed.mixStafford13(key)^key)&(this.backends.length-1)); } + @Override + public void iterateStoredSectionPositions(LongConsumer consumer) { + throw new IllegalStateException("Not yet implemented"); + } + //TODO: reencode the key to be shifted one less OR // use like a mix64 to shuffle the key in getSegmentId so that // multiple layers of spliced storage backends can be stacked diff --git a/src/main/java/me/cortex/voxy/common/storage/other/ReadonlyCachingLayer.java b/src/main/java/me/cortex/voxy/common/storage/other/ReadonlyCachingLayer.java index 79788941..810a746b 100644 --- a/src/main/java/me/cortex/voxy/common/storage/other/ReadonlyCachingLayer.java +++ b/src/main/java/me/cortex/voxy/common/storage/other/ReadonlyCachingLayer.java @@ -7,6 +7,7 @@ import me.cortex.voxy.common.storage.config.StorageConfig; import java.nio.ByteBuffer; import java.util.List; +import java.util.function.LongConsumer; public class ReadonlyCachingLayer extends StorageBackend { private final StorageBackend cache; @@ -30,6 +31,11 @@ public class ReadonlyCachingLayer extends StorageBackend { return result; } + @Override + public void iterateStoredSectionPositions(LongConsumer consumer) { + throw new IllegalStateException("Not yet implemented"); + } + @Override public void setSectionData(long key, ByteBuffer data) { this.cache.setSectionData(key, data); diff --git a/src/main/java/me/cortex/voxy/common/storage/redis/RedisStorageBackend.java b/src/main/java/me/cortex/voxy/common/storage/redis/RedisStorageBackend.java index f3f7279e..58200d84 100644 --- a/src/main/java/me/cortex/voxy/common/storage/redis/RedisStorageBackend.java +++ b/src/main/java/me/cortex/voxy/common/storage/redis/RedisStorageBackend.java @@ -9,6 +9,7 @@ import redis.clients.jedis.JedisPool; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; +import java.util.function.LongConsumer; public class RedisStorageBackend extends StorageBackend { private final JedisPool pool; @@ -29,6 +30,11 @@ public class RedisStorageBackend extends StorageBackend { this.MAPPINGS = (prefix+"id_mappings").getBytes(StandardCharsets.UTF_8); } + @Override + public void iterateStoredSectionPositions(LongConsumer consumer) { + throw new IllegalStateException("Not yet implemented"); + } + @Override public ByteBuffer getSectionData(long key) { try (var jedis = this.pool.getResource()) { diff --git a/src/main/java/me/cortex/voxy/common/storage/rocksdb/RocksDBStorageBackend.java b/src/main/java/me/cortex/voxy/common/storage/rocksdb/RocksDBStorageBackend.java index 443ab8c7..bd523a43 100644 --- a/src/main/java/me/cortex/voxy/common/storage/rocksdb/RocksDBStorageBackend.java +++ b/src/main/java/me/cortex/voxy/common/storage/rocksdb/RocksDBStorageBackend.java @@ -14,6 +14,7 @@ import java.nio.file.Files; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.function.LongConsumer; public class RocksDBStorageBackend extends StorageBackend { private final RocksDB db; @@ -80,6 +81,11 @@ public class RocksDBStorageBackend extends StorageBackend { } } + @Override + public void iterateStoredSectionPositions(LongConsumer consumer) { + throw new IllegalStateException("Not yet implemented"); + } + @Override public ByteBuffer getSectionData(long key) { try { diff --git a/src/main/java/me/cortex/voxy/common/util/MemoryBuffer.java b/src/main/java/me/cortex/voxy/common/util/MemoryBuffer.java index 3b8377c4..6fe7fdde 100644 --- a/src/main/java/me/cortex/voxy/common/util/MemoryBuffer.java +++ b/src/main/java/me/cortex/voxy/common/util/MemoryBuffer.java @@ -13,7 +13,7 @@ public class MemoryBuffer extends TrackedObject { public void cpyTo(long dst) { super.assertNotFreed(); - MemoryUtil.memCopy(this.address, dst, this.size); + UnsafeUtil.memcpy(this.address, dst, this.size); } @Override diff --git a/src/main/java/me/cortex/voxy/common/util/UnsafeUtil.java b/src/main/java/me/cortex/voxy/common/util/UnsafeUtil.java new file mode 100644 index 00000000..d963147b --- /dev/null +++ b/src/main/java/me/cortex/voxy/common/util/UnsafeUtil.java @@ -0,0 +1,21 @@ +package me.cortex.voxy.common.util; + +import sun.misc.Unsafe; + +import java.lang.reflect.Field; + +public class UnsafeUtil { + private static final Unsafe UNSAFE; + static { + try { + Field field = Unsafe.class.getDeclaredField("theUnsafe"); + field.setAccessible(true); + UNSAFE = (Unsafe)field.get(null); + } catch (Exception e) {throw new RuntimeException(e);} + } + + public static void memcpy(long src, long dst, long length) { + UNSAFE.copyMemory(src, dst, length); + } + +} diff --git a/src/main/java/me/cortex/voxy/common/world/ActiveSectionTracker.java b/src/main/java/me/cortex/voxy/common/world/ActiveSectionTracker.java index dcf3a6b1..1003ff84 100644 --- a/src/main/java/me/cortex/voxy/common/world/ActiveSectionTracker.java +++ b/src/main/java/me/cortex/voxy/common/world/ActiveSectionTracker.java @@ -1,5 +1,6 @@ package me.cortex.voxy.common.world; +import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap; import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; import me.cortex.voxy.common.util.VolatileHolder; import me.cortex.voxy.common.world.other.Mapper; @@ -14,7 +15,10 @@ public class ActiveSectionTracker { private final Long2ObjectOpenHashMap>[] loadedSectionCache; private final SectionLoader loader; - //private final SectionDataCache dataCache; + + //private static final int SECONDARY_CACHE_CAPACITY = 256; + //private final Long2ObjectLinkedOpenHashMap secondaryDataCache = new Long2ObjectLinkedOpenHashMap<>(SECONDARY_CACHE_CAPACITY*2);//Its x2 due to race conditions + @SuppressWarnings("unchecked") public ActiveSectionTracker(int numSlicesBits, SectionLoader loader) { @@ -43,6 +47,7 @@ public class ActiveSectionTracker { return section; } } + //If this thread was the one to create the reference then its the thread to load the section if (isLoader) { var section = new WorldSection(lvl, x, y, z, this); @@ -84,11 +89,13 @@ public class ActiveSectionTracker { void tryUnload(WorldSection section) { var cache = this.loadedSectionCache[this.getCacheArrayIndex(section.key)]; + boolean removed = false; synchronized (cache) { if (section.trySetFreed()) { if (cache.remove(section.key).obj != section) { throw new IllegalStateException("Removed section not the same as the referenced section in the cache"); } + removed = true; } } } diff --git a/src/main/java/me/cortex/voxy/common/world/SectionDataCache.java b/src/main/java/me/cortex/voxy/common/world/SectionDataCache.java deleted file mode 100644 index 7610efce..00000000 --- a/src/main/java/me/cortex/voxy/common/world/SectionDataCache.java +++ /dev/null @@ -1,48 +0,0 @@ -package me.cortex.voxy.common.world; - - -import it.unimi.dsi.fastutil.longs.Long2ObjectMap; -import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; -import it.unimi.dsi.fastutil.objects.Reference2LongOpenHashMap; - -import java.lang.ref.ReferenceQueue; -import java.lang.ref.SoftReference; - -public class SectionDataCache { - private record MapPair(Reference2LongOpenHashMap> ref2pos, Long2ObjectMap> pos2ref) { - public MapPair() { - this(new Reference2LongOpenHashMap<>(), new Long2ObjectOpenHashMap<>()); - } - } - private final MapPair[] maps; - private final ReferenceQueue cleanupQueue; - public SectionDataCache(int sliceBits) { - this.cleanupQueue = new ReferenceQueue<>(); - this.maps = new MapPair[1<(section.data)); - return -1; - } - //var data = entry.data.get(); - //if (data == null) { - // map.remove(section.key); - // return -1; - //} - //System.arraycopy(data, 0, section.data, 0, data.length); - return 0; - } - } -} diff --git a/src/main/java/me/cortex/voxy/common/world/WorldEngine.java b/src/main/java/me/cortex/voxy/common/world/WorldEngine.java index c9d0f99b..e2214ba9 100644 --- a/src/main/java/me/cortex/voxy/common/world/WorldEngine.java +++ b/src/main/java/me/cortex/voxy/common/world/WorldEngine.java @@ -95,6 +95,10 @@ public class WorldEngine { return (int) ((id<<12)>>40); } + public static String pprintPos(long pos) { + return getLevel(pos)+"@["+getX(pos)+", "+getY(pos)+", " + getZ(pos)+"]"; + } + //Marks a section as dirty, enqueuing it for saving and or render data rebuilding public void markDirty(WorldSection section) { if (this.dirtyCallback != null) { diff --git a/src/main/java/me/cortex/voxy/common/world/WorldSection.java b/src/main/java/me/cortex/voxy/common/world/WorldSection.java index fc9a6a3e..602fc320 100644 --- a/src/main/java/me/cortex/voxy/common/world/WorldSection.java +++ b/src/main/java/me/cortex/voxy/common/world/WorldSection.java @@ -64,6 +64,7 @@ public final class WorldSection { return this.atomicState.get()>>1; } + //TODO: add the ability to hint to the tracker that yes the section is unloaded, try to cache it in a secondary cache since it will be reused/needed later public int release() { int state = this.atomicState.addAndGet(-2); if (state < 1) { diff --git a/src/main/resources/voxy.mixins.json b/src/main/resources/voxy.mixins.json index efe79540..0f4b4a69 100644 --- a/src/main/resources/voxy.mixins.json +++ b/src/main/resources/voxy.mixins.json @@ -7,6 +7,7 @@ "minecraft.MixinClientChunkManager", "minecraft.MixinDebugHud", "minecraft.MixinMinecraftClient", + "minecraft.MixinRenderSystem", "minecraft.MixinWorldRenderer", "nvidium.MixinRenderPipeline", "sodium.MixinDefaultChunkRenderer",