From c0a578cdd5c757ababba5232f9973021233c9e21 Mon Sep 17 00:00:00 2001 From: mcrcortex <18544518+MCRcortex@users.noreply.github.com> Date: Thu, 6 Mar 2025 14:37:23 +1000 Subject: [PATCH] aaa --- .../client/{Voxy.java => VoxyClient.java} | 14 +- .../cortex/voxy/client/config/VoxyConfig.java | 1 + .../config/VoxyConfigScreenFactory.java | 11 +- .../voxy/client/core/IGetVoxelCore.java | 9 -- .../client/core/IGetVoxyRenderSystem.java | 9 ++ .../me/cortex/voxy/client/core/VoxelCore.java | 135 +--------------- .../voxy/client/core/WorldImportWrapper.java | 4 +- .../voxy/client/core/model/ModelFactory.java | 8 - .../core/rendering/PrintfDebugUtil.java | 1 - .../client/core/rendering/RenderService.java | 8 + .../core/rendering/VoxyRenderSystem.java | 152 ++++++++++++++++++ .../client/mixin/minecraft/MixinDebugHud.java | 15 +- .../mixin/minecraft/MixinMinecraftClient.java | 16 ++ .../mixin/minecraft/MixinWorldRenderer.java | 101 ++++++------ .../sodium/MixinDefaultChunkRenderer.java | 12 +- .../sodium/MixinRenderSectionManager.java | 10 +- .../client/saver/ContextSelectionSystem.java | 4 - .../client/terrain/WorldImportCommand.java | 9 +- .../java/me/cortex/voxy/common/Logger.java | 9 +- .../voxy/common/config/Serialization.java | 15 +- .../voxy/common/thread/ServiceSlice.java | 2 +- .../common/world/ActiveSectionTracker.java | 13 +- .../cortex/voxy/common/world/WorldEngine.java | 42 ++--- .../voxy/common/world/WorldSection.java | 4 + .../voxy/common/world/other/Mapper.java | 4 + .../world/service/SectionSavingService.java | 26 +-- .../world/service/VoxelIngestService.java | 27 ++-- .../me/cortex/voxy/commonImpl/VoxyCommon.java | 25 ++- .../cortex/voxy/commonImpl/VoxyInstance.java | 75 +++++++++ .../commonImpl/importers/WorldImporter.java | 10 +- .../mixin/chunky/MixinFabricWorld.java | 23 +-- src/main/resources/client.voxy.mixins.json | 3 +- src/main/resources/common.voxy.mixins.json | 1 + src/main/resources/fabric.mod.json | 2 +- 34 files changed, 471 insertions(+), 329 deletions(-) rename src/main/java/me/cortex/voxy/client/{Voxy.java => VoxyClient.java} (64%) delete mode 100644 src/main/java/me/cortex/voxy/client/core/IGetVoxelCore.java create mode 100644 src/main/java/me/cortex/voxy/client/core/IGetVoxyRenderSystem.java create mode 100644 src/main/java/me/cortex/voxy/client/core/rendering/VoxyRenderSystem.java create mode 100644 src/main/java/me/cortex/voxy/commonImpl/VoxyInstance.java rename src/main/java/me/cortex/voxy/{client => commonImpl}/mixin/chunky/MixinFabricWorld.java (59%) diff --git a/src/main/java/me/cortex/voxy/client/Voxy.java b/src/main/java/me/cortex/voxy/client/VoxyClient.java similarity index 64% rename from src/main/java/me/cortex/voxy/client/Voxy.java rename to src/main/java/me/cortex/voxy/client/VoxyClient.java index 90910d59..18f82d14 100644 --- a/src/main/java/me/cortex/voxy/client/Voxy.java +++ b/src/main/java/me/cortex/voxy/client/VoxyClient.java @@ -5,21 +5,15 @@ import me.cortex.voxy.client.saver.ContextSelectionSystem; import me.cortex.voxy.client.terrain.WorldImportCommand; import net.fabricmc.api.ClientModInitializer; import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback; +import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientWorldEvents; import net.minecraft.client.world.ClientWorld; -public class Voxy implements ClientModInitializer { +public class VoxyClient implements ClientModInitializer { @Override public void onInitializeClient() { + /* ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> { dispatcher.register(WorldImportCommand.register()); - }); - } - - - private static final ContextSelectionSystem SELECTOR = new ContextSelectionSystem(); - - public static VoxelCore createVoxelCore(ClientWorld world) { - var selection = SELECTOR.getBestSelectionOrCreate(world); - return new VoxelCore(selection); + });*/ } } 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 983ddbe4..583183e8 100644 --- a/src/main/java/me/cortex/voxy/client/config/VoxyConfig.java +++ b/src/main/java/me/cortex/voxy/client/config/VoxyConfig.java @@ -21,6 +21,7 @@ public class VoxyConfig { public static VoxyConfig CONFIG = loadOrCreate(); public boolean enabled = true; + public boolean enableRendering = true; public boolean ingestEnabled = true; //public int renderDistance = 128; public int serviceThreads = Math.max(Runtime.getRuntime().availableProcessors()/2, 1); diff --git a/src/main/java/me/cortex/voxy/client/config/VoxyConfigScreenFactory.java b/src/main/java/me/cortex/voxy/client/config/VoxyConfigScreenFactory.java index 7181e0cf..412968e4 100644 --- a/src/main/java/me/cortex/voxy/client/config/VoxyConfigScreenFactory.java +++ b/src/main/java/me/cortex/voxy/client/config/VoxyConfigScreenFactory.java @@ -2,7 +2,8 @@ package me.cortex.voxy.client.config; import com.terraformersmc.modmenu.api.ConfigScreenFactory; import com.terraformersmc.modmenu.api.ModMenuApi; -import me.cortex.voxy.client.core.IGetVoxelCore; +import me.cortex.voxy.client.core.IGetVoxyRenderSystem; +import me.cortex.voxy.commonImpl.VoxyCommon; import me.shedaniel.clothconfig2.api.ConfigBuilder; import me.shedaniel.clothconfig2.api.ConfigCategory; import me.shedaniel.clothconfig2.api.ConfigEntryBuilder; @@ -35,9 +36,13 @@ public class VoxyConfigScreenFactory implements ModMenuApi { builder.setSavingRunnable(() -> { //After saving the core should be reloaded/reset - var world = (IGetVoxelCore)MinecraftClient.getInstance().worldRenderer; + var world = MinecraftClient.getInstance().worldRenderer; if (world != null && ON_SAVE_RELOAD) { - world.reloadVoxelCore(); + //Reload voxy + ((IGetVoxyRenderSystem)world).shutdownRenderer(); + VoxyCommon.shutdownInstance(); + VoxyCommon.createInstance(); + ((IGetVoxyRenderSystem)world).createRenderer(); } ON_SAVE_RELOAD = false; VoxyConfig.CONFIG.save(); diff --git a/src/main/java/me/cortex/voxy/client/core/IGetVoxelCore.java b/src/main/java/me/cortex/voxy/client/core/IGetVoxelCore.java deleted file mode 100644 index e0689363..00000000 --- a/src/main/java/me/cortex/voxy/client/core/IGetVoxelCore.java +++ /dev/null @@ -1,9 +0,0 @@ -package me.cortex.voxy.client.core; - -import me.cortex.voxy.client.core.VoxelCore; - -public interface IGetVoxelCore { - VoxelCore getVoxelCore(); - - void reloadVoxelCore(); -} diff --git a/src/main/java/me/cortex/voxy/client/core/IGetVoxyRenderSystem.java b/src/main/java/me/cortex/voxy/client/core/IGetVoxyRenderSystem.java new file mode 100644 index 00000000..37df74c6 --- /dev/null +++ b/src/main/java/me/cortex/voxy/client/core/IGetVoxyRenderSystem.java @@ -0,0 +1,9 @@ +package me.cortex.voxy.client.core; + +import me.cortex.voxy.client.core.rendering.VoxyRenderSystem; + +public interface IGetVoxyRenderSystem { + VoxyRenderSystem getVoxyRenderSystem(); + void shutdownRenderer(); + void createRenderer(); +} 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 fd115371..84bc3402 100644 --- a/src/main/java/me/cortex/voxy/client/core/VoxelCore.java +++ b/src/main/java/me/cortex/voxy/client/core/VoxelCore.java @@ -66,29 +66,18 @@ import static org.lwjgl.opengl.GL30C.*; //REMOVE setRenderGen like holy hell public class VoxelCore { private final WorldEngine world; - - private final RenderService renderer; - private final PostProcessing postProcessing; - private final ServiceThreadPool serviceThreadPool; - + public final ServiceThreadPool serviceThreadPool; public final WorldImportWrapper importer; public VoxelCore(ContextSelectionSystem.Selection worldSelection) { var cfg = worldSelection.getConfig(); this.serviceThreadPool = new ServiceThreadPool(VoxyConfig.CONFIG.serviceThreads); - this.world = worldSelection.createEngine(this.serviceThreadPool); + this.world = null;//worldSelection.createEngine(this.serviceThreadPool); Logger.info("Initializing voxy core"); this.importer = new WorldImportWrapper(this.serviceThreadPool, this.world); - //Trigger the shared index buffer loading - SharedIndexBuffer.INSTANCE.id(); - Capabilities.init();//Ensure clinit is called - - this.renderer = new RenderService(this.world, this.serviceThreadPool); - this.postProcessing = new PostProcessing(); - Logger.info("Voxy core initialized"); //this.verifyTopNodeChildren(0,0,0); @@ -99,130 +88,16 @@ public class VoxelCore { //this.testFullMesh(); } - public void enqueueIngest(WorldChunk worldChunk) { - this.world.ingestService.enqueueIngest(worldChunk); - } - - public void renderSetup(Frustum frustum, Camera camera) { - this.renderer.setup(camera); - PrintfDebugUtil.tick(); - } - - private static Matrix4f makeProjectionMatrix(float near, float far) { - //TODO: use the existing projection matrix use mulLocal by the inverse of the projection and then mulLocal our projection - - var projection = new Matrix4f(); - var client = MinecraftClient.getInstance(); - var gameRenderer = client.gameRenderer;//tickCounter.getTickDelta(true); - - float fov = gameRenderer.getFov(gameRenderer.getCamera(), client.getRenderTickCounter().getTickDelta(true), true); - - projection.setPerspective(fov * 0.01745329238474369f, - (float) client.getWindow().getFramebufferWidth() / (float)client.getWindow().getFramebufferHeight(), - near, far); - 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() - ).mulLocal(makeProjectionMatrix(16, 16*3000)); - } - - //private static final ModelTextureBakery mtb = new ModelTextureBakery(16, 16); - //private static final RawDownloadStream downstream = new RawDownloadStream(1<<20); - public void renderOpaque(MatrixStack matrices, double cameraX, double cameraY, double cameraZ) { - /* - int allocation = downstream.download(2*4*6*16*16, ptr->{ - }); - mtb.renderFacesToStream(Blocks.WHITE_STAINED_GLASS.getDefaultState(), 123456, false, downstream.getBufferId(), allocation); - downstream.submit(); - downstream.tick(); - */ - //if (true) return; - - - if (IrisUtil.irisShadowActive()) { - return; - } - - if (false) { - float CHANGE_PER_SECOND = 30; - //Auto fps targeting - if (MinecraftClient.getInstance().getCurrentFps() < 45) { - VoxyConfig.CONFIG.subDivisionSize = Math.min(VoxyConfig.CONFIG.subDivisionSize + CHANGE_PER_SECOND / Math.max(1f, MinecraftClient.getInstance().getCurrentFps()), 256); - } - - if (55 < MinecraftClient.getInstance().getCurrentFps()) { - VoxyConfig.CONFIG.subDivisionSize = Math.max(VoxyConfig.CONFIG.subDivisionSize - CHANGE_PER_SECOND / Math.max(1f, MinecraftClient.getInstance().getCurrentFps()), 30); - } - } - - //Do some very cheeky stuff for MiB - if (false) { - int sector = (((int)Math.floor(cameraX)>>4)+512)>>10; - cameraX -= sector<<14;//10+4 - cameraY += (16+(256-32-sector*30))*16; - } - - matrices.push(); - matrices.translate(-cameraX, -cameraY, -cameraZ); - matrices.pop(); - - var projection = computeProjectionMat();//RenderSystem.getProjectionMatrix(); - //var projection = RenderSystem.getProjectionMatrix(); - - var viewport = this.renderer.getViewport(); - viewport - .setProjection(projection) - .setModelView(matrices.peek().getPositionMatrix()) - .setCamera(cameraX, cameraY, cameraZ) - .setScreenSize(MinecraftClient.getInstance().getFramebuffer().textureWidth, MinecraftClient.getInstance().getFramebuffer().textureHeight); - viewport.frameId++; - - int boundFB = GL11.glGetInteger(GL_DRAW_FRAMEBUFFER_BINDING); - if (boundFB == 0) { - throw new IllegalStateException("Cannot use the default framebuffer as cannot source from it"); - } - //TODO: use the raw depth buffer texture instead - //int boundDepthBuffer = glGetNamedFramebufferAttachmentParameteri(boundFB, GL_DEPTH_STENCIL_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME); - - //TODO:FIXME!!! ?? - this.postProcessing.setup(MinecraftClient.getInstance().getFramebuffer().textureWidth, MinecraftClient.getInstance().getFramebuffer().textureHeight, boundFB); - - this.renderer.renderFarAwayOpaque(viewport); - - //Compute the SSAO of the rendered terrain, TODO: fix it breaking depth or breaking _something_ am not sure what - this.postProcessing.computeSSAO(projection, matrices); - - //We can render the translucent directly after as it is the furthest translucent objects - this.renderer.renderFarAwayTranslucent(viewport); - - - this.postProcessing.renderPost(projection, RenderSystem.getProjectionMatrix(), boundFB); - - - - } - - public void addDebugInfo(List debug) { debug.add(""); debug.add(""); - debug.add("Voxy Core: " + VoxyCommon.MOD_VERSION); - debug.add("MemoryBuffer, Count/Size (mb): " + MemoryBuffer.getCount() + "/" + (MemoryBuffer.getTotalSize()/1_000_000)); - debug.add("GlBuffer, Count/Size (mb): " + GlBuffer.getCount() + "/" + (GlBuffer.getTotalSize()/1_000_000)); /* debug.add("Ingest service tasks: " + this.world.ingestService.getTaskCount()); debug.add("Saving service tasks: " + this.world.savingService.getTaskCount()); debug.add("Render service tasks: " + this.renderGen.getTaskCount()); */ - debug.add("I/S tasks: " + this.world.ingestService.getTaskCount() + "/"+this.world.savingService.getTaskCount()); this.world.addDebugData(debug); - this.renderer.addDebugData(debug); - PrintfDebugUtil.addToOut(debug); } //Note: when doing translucent rendering, only need to sort when generating the geometry, or when crossing into the center zone @@ -230,8 +105,6 @@ public class VoxelCore { // since they are AABBS crossing the normal is impossible without one of the axis being equal public void shutdown() { - Logger.info("Flushing download stream"); - DownloadStream.INSTANCE.flushWaitClear(); //if (Thread.currentThread() != this.shutdownThread) { // Runtime.getRuntime().removeShutdownHook(this.shutdownThread); @@ -239,10 +112,6 @@ public class VoxelCore { //this.world.getMapper().forceResaveStates(); this.importer.shutdown(); - Logger.info("Shutting down rendering"); - try {this.renderer.shutdown();} catch (Exception e) {Logger.error("Error shutting down renderer", e);} - Logger.info("Shutting down post processor"); - if (this.postProcessing!=null){try {this.postProcessing.shutdown();} catch (Exception e) {Logger.error("Error shutting down post processor", e);}} Logger.info("Shutting down world engine"); try {this.world.shutdown();} catch (Exception e) {Logger.error("Error shutting down world engine", e);} Logger.info("Shutting down service thread pool"); diff --git a/src/main/java/me/cortex/voxy/client/core/WorldImportWrapper.java b/src/main/java/me/cortex/voxy/client/core/WorldImportWrapper.java index b9feabbf..bd876f6b 100644 --- a/src/main/java/me/cortex/voxy/client/core/WorldImportWrapper.java +++ b/src/main/java/me/cortex/voxy/client/core/WorldImportWrapper.java @@ -4,6 +4,7 @@ import me.cortex.voxy.client.taskbar.Taskbar; import me.cortex.voxy.common.Logger; import me.cortex.voxy.common.thread.ServiceThreadPool; import me.cortex.voxy.common.world.WorldEngine; +import me.cortex.voxy.commonImpl.VoxyCommon; import me.cortex.voxy.commonImpl.importers.WorldImporter; import net.minecraft.client.MinecraftClient; import net.minecraft.client.gui.hud.ClientBossBar; @@ -28,7 +29,6 @@ public class WorldImportWrapper { } public void shutdown() { - Logger.info("Shutting down importer"); if (this.importer != null) { try { @@ -58,7 +58,7 @@ public class WorldImportWrapper { } public boolean createWorldImporter(World mcWorld, IImporterFactory factory) { if (this.importer == null) { - this.importer = new WorldImporter(this.world, mcWorld, this.pool); + this.importer = new WorldImporter(this.world, mcWorld, this.pool, VoxyCommon.getInstance().getSavingService()); } if (this.importer.isBusy()) { return false; diff --git a/src/main/java/me/cortex/voxy/client/core/model/ModelFactory.java b/src/main/java/me/cortex/voxy/client/core/model/ModelFactory.java index 4bc079cd..ffe0ddf3 100644 --- a/src/main/java/me/cortex/voxy/client/core/model/ModelFactory.java +++ b/src/main/java/me/cortex/voxy/client/core/model/ModelFactory.java @@ -6,9 +6,6 @@ import it.unimi.dsi.fastutil.ints.IntOpenHashSet; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; import it.unimi.dsi.fastutil.objects.ObjectSet; -import me.cortex.voxy.client.core.IGetVoxelCore; -import me.cortex.voxy.client.core.gl.GlBuffer; -import me.cortex.voxy.client.core.gl.GlTexture; import me.cortex.voxy.client.core.rendering.util.RawDownloadStream; import me.cortex.voxy.client.core.rendering.util.UploadStream; import me.cortex.voxy.common.Logger; @@ -40,13 +37,8 @@ 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; -import static org.lwjgl.opengl.GL12C.GL_TEXTURE_MAX_LOD; -import static org.lwjgl.opengl.GL12C.GL_TEXTURE_MIN_LOD; import static org.lwjgl.opengl.GL33.glDeleteSamplers; import static org.lwjgl.opengl.GL33.glGenSamplers; -import static org.lwjgl.opengl.GL33C.glSamplerParameteri; import static org.lwjgl.opengl.GL45C.glTextureSubImage2D; //Manages the storage and updating of model states, textures and colours diff --git a/src/main/java/me/cortex/voxy/client/core/rendering/PrintfDebugUtil.java b/src/main/java/me/cortex/voxy/client/core/rendering/PrintfDebugUtil.java index 0a2b6727..05f7a58d 100644 --- a/src/main/java/me/cortex/voxy/client/core/rendering/PrintfDebugUtil.java +++ b/src/main/java/me/cortex/voxy/client/core/rendering/PrintfDebugUtil.java @@ -1,6 +1,5 @@ package me.cortex.voxy.client.core.rendering; -import me.cortex.voxy.client.Voxy; import me.cortex.voxy.client.core.gl.shader.IShaderProcessor; import me.cortex.voxy.client.core.gl.shader.PrintfInjector; import me.cortex.voxy.client.core.gl.shader.ShaderType; 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 index b37da893..41841ac5 100644 --- a/src/main/java/me/cortex/voxy/client/core/rendering/RenderService.java +++ b/src/main/java/me/cortex/voxy/client/core/rendering/RenderService.java @@ -44,7 +44,10 @@ public class RenderService, J extends Vi private final MessageQueue sectionUpdateQueue; private final MessageQueue geometryUpdateQueue; + private final WorldEngine world; + public RenderService(WorldEngine world, ServiceThreadPool serviceThreadPool) { + this.world = world; this.modelService = new ModelBakerySubsystem(world.getMapper()); //Max sections: ~500k @@ -224,6 +227,11 @@ public class RenderService, J extends Vi } public void shutdown() { + //Cleanup callbacks + this.world.setDirtyCallback(null); + this.world.getMapper().setBiomeCallback(null); + this.world.getMapper().setStateCallback(null); + this.modelService.shutdown(); this.renderGen.shutdown(); this.viewportSelector.free(); diff --git a/src/main/java/me/cortex/voxy/client/core/rendering/VoxyRenderSystem.java b/src/main/java/me/cortex/voxy/client/core/rendering/VoxyRenderSystem.java new file mode 100644 index 00000000..f89c524f --- /dev/null +++ b/src/main/java/me/cortex/voxy/client/core/rendering/VoxyRenderSystem.java @@ -0,0 +1,152 @@ +package me.cortex.voxy.client.core.rendering; + +import com.mojang.blaze3d.systems.RenderSystem; +import me.cortex.voxy.client.config.VoxyConfig; +import me.cortex.voxy.client.core.gl.Capabilities; +import me.cortex.voxy.client.core.gl.GlBuffer; +import me.cortex.voxy.client.core.rendering.post.PostProcessing; +import me.cortex.voxy.client.core.rendering.util.DownloadStream; +import me.cortex.voxy.client.core.util.IrisUtil; +import me.cortex.voxy.common.Logger; +import me.cortex.voxy.common.thread.ServiceThreadPool; +import me.cortex.voxy.common.world.WorldEngine; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.render.Camera; +import net.minecraft.client.render.Frustum; +import net.minecraft.client.util.math.MatrixStack; +import org.joml.Matrix4f; +import org.lwjgl.opengl.GL11; + +import java.util.List; + +import static org.lwjgl.opengl.GL30C.GL_DRAW_FRAMEBUFFER_BINDING; + +public class VoxyRenderSystem { + private final RenderService renderer; + private final PostProcessing postProcessing; + + public VoxyRenderSystem(WorldEngine world, ServiceThreadPool threadPool) { + //Trigger the shared index buffer loading + SharedIndexBuffer.INSTANCE.id(); + Capabilities.init();//Ensure clinit is called + + this.renderer = new RenderService(world, threadPool); + this.postProcessing = new PostProcessing(); + } + + + public void renderSetup(Frustum frustum, Camera camera) { + this.renderer.setup(camera); + PrintfDebugUtil.tick(); + } + + private static Matrix4f makeProjectionMatrix(float near, float far) { + //TODO: use the existing projection matrix use mulLocal by the inverse of the projection and then mulLocal our projection + + var projection = new Matrix4f(); + var client = MinecraftClient.getInstance(); + var gameRenderer = client.gameRenderer;//tickCounter.getTickDelta(true); + + float fov = gameRenderer.getFov(gameRenderer.getCamera(), client.getRenderTickCounter().getTickDelta(true), true); + + projection.setPerspective(fov * 0.01745329238474369f, + (float) client.getWindow().getFramebufferWidth() / (float)client.getWindow().getFramebufferHeight(), + near, far); + 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() + ).mulLocal(makeProjectionMatrix(16, 16*3000)); + } + + //private static final ModelTextureBakery mtb = new ModelTextureBakery(16, 16); + //private static final RawDownloadStream downstream = new RawDownloadStream(1<<20); + public void renderOpaque(MatrixStack matrices, double cameraX, double cameraY, double cameraZ) { + /* + int allocation = downstream.download(2*4*6*16*16, ptr->{ + }); + mtb.renderFacesToStream(Blocks.WHITE_STAINED_GLASS.getDefaultState(), 123456, false, downstream.getBufferId(), allocation); + downstream.submit(); + downstream.tick(); + */ + //if (true) return; + + + if (IrisUtil.irisShadowActive()) { + return; + } + + if (false) { + float CHANGE_PER_SECOND = 30; + //Auto fps targeting + if (MinecraftClient.getInstance().getCurrentFps() < 45) { + VoxyConfig.CONFIG.subDivisionSize = Math.min(VoxyConfig.CONFIG.subDivisionSize + CHANGE_PER_SECOND / Math.max(1f, MinecraftClient.getInstance().getCurrentFps()), 256); + } + + if (55 < MinecraftClient.getInstance().getCurrentFps()) { + VoxyConfig.CONFIG.subDivisionSize = Math.max(VoxyConfig.CONFIG.subDivisionSize - CHANGE_PER_SECOND / Math.max(1f, MinecraftClient.getInstance().getCurrentFps()), 30); + } + } + + //Do some very cheeky stuff for MiB + if (false) { + int sector = (((int)Math.floor(cameraX)>>4)+512)>>10; + cameraX -= sector<<14;//10+4 + cameraY += (16+(256-32-sector*30))*16; + } + + matrices.push(); + matrices.translate(-cameraX, -cameraY, -cameraZ); + matrices.pop(); + + var projection = computeProjectionMat();//RenderSystem.getProjectionMatrix(); + //var projection = RenderSystem.getProjectionMatrix(); + + var viewport = this.renderer.getViewport(); + viewport + .setProjection(projection) + .setModelView(matrices.peek().getPositionMatrix()) + .setCamera(cameraX, cameraY, cameraZ) + .setScreenSize(MinecraftClient.getInstance().getFramebuffer().textureWidth, MinecraftClient.getInstance().getFramebuffer().textureHeight); + viewport.frameId++; + + int boundFB = GL11.glGetInteger(GL_DRAW_FRAMEBUFFER_BINDING); + if (boundFB == 0) { + throw new IllegalStateException("Cannot use the default framebuffer as cannot source from it"); + } + //TODO: use the raw depth buffer texture instead + //int boundDepthBuffer = glGetNamedFramebufferAttachmentParameteri(boundFB, GL_DEPTH_STENCIL_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME); + + //TODO:FIXME!!! ?? + this.postProcessing.setup(MinecraftClient.getInstance().getFramebuffer().textureWidth, MinecraftClient.getInstance().getFramebuffer().textureHeight, boundFB); + + this.renderer.renderFarAwayOpaque(viewport); + + //Compute the SSAO of the rendered terrain, TODO: fix it breaking depth or breaking _something_ am not sure what + this.postProcessing.computeSSAO(projection, matrices); + + //We can render the translucent directly after as it is the furthest translucent objects + this.renderer.renderFarAwayTranslucent(viewport); + + + this.postProcessing.renderPost(projection, RenderSystem.getProjectionMatrix(), boundFB); + } + + public void addDebugInfo(List debug) { + debug.add("GlBuffer, Count/Size (mb): " + GlBuffer.getCount() + "/" + (GlBuffer.getTotalSize()/1_000_000)); + this.renderer.addDebugData(debug); + PrintfDebugUtil.addToOut(debug); + } + + public void shutdown() { + Logger.info("Flushing download stream"); + DownloadStream.INSTANCE.flushWaitClear(); + Logger.info("Shutting down rendering"); + try {this.renderer.shutdown();} catch (Exception e) {Logger.error("Error shutting down renderer", e);} + Logger.info("Shutting down post processor"); + if (this.postProcessing!=null){try {this.postProcessing.shutdown();} catch (Exception e) {Logger.error("Error shutting down post processor", e);}} + } +} diff --git a/src/main/java/me/cortex/voxy/client/mixin/minecraft/MixinDebugHud.java b/src/main/java/me/cortex/voxy/client/mixin/minecraft/MixinDebugHud.java index 0a968da8..1fadd363 100644 --- a/src/main/java/me/cortex/voxy/client/mixin/minecraft/MixinDebugHud.java +++ b/src/main/java/me/cortex/voxy/client/mixin/minecraft/MixinDebugHud.java @@ -1,6 +1,7 @@ package me.cortex.voxy.client.mixin.minecraft; -import me.cortex.voxy.client.core.IGetVoxelCore; +import me.cortex.voxy.client.core.IGetVoxyRenderSystem; +import me.cortex.voxy.commonImpl.VoxyCommon; import net.minecraft.client.MinecraftClient; import net.minecraft.client.gui.hud.DebugHud; import org.spongepowered.asm.mixin.Mixin; @@ -15,9 +16,15 @@ public class MixinDebugHud { @Inject(method = "getRightText", at = @At("TAIL")) private void injectDebug(CallbackInfoReturnable> cir) { var ret = cir.getReturnValue(); - var core = ((IGetVoxelCore) MinecraftClient.getInstance().worldRenderer).getVoxelCore(); - if (core != null) { - core.addDebugInfo(ret); + var instance = VoxyCommon.getInstance(); + if (instance != null) { + ret.add(""); + ret.add(""); + instance.addDebug(ret); + } + var renderer = ((IGetVoxyRenderSystem) MinecraftClient.getInstance().worldRenderer).getVoxyRenderSystem(); + if (renderer != null) { + renderer.addDebugInfo(ret); } } } diff --git a/src/main/java/me/cortex/voxy/client/mixin/minecraft/MixinMinecraftClient.java b/src/main/java/me/cortex/voxy/client/mixin/minecraft/MixinMinecraftClient.java index 8c3f0159..02e677ac 100644 --- a/src/main/java/me/cortex/voxy/client/mixin/minecraft/MixinMinecraftClient.java +++ b/src/main/java/me/cortex/voxy/client/mixin/minecraft/MixinMinecraftClient.java @@ -1,7 +1,11 @@ package me.cortex.voxy.client.mixin.minecraft; +import me.cortex.voxy.client.config.VoxyConfig; +import me.cortex.voxy.commonImpl.VoxyCommon; import net.minecraft.client.MinecraftClient; import net.minecraft.client.RunArgs; +import net.minecraft.client.gui.screen.DownloadingTerrainScreen; +import net.minecraft.client.world.ClientWorld; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; @@ -13,4 +17,16 @@ public class MixinMinecraftClient { private void injectRenderDoc(RunArgs args, CallbackInfo ci) { //System.load("C:\\Program Files\\RenderDoc\\renderdoc.dll"); } + + @Inject(method = "disconnect(Lnet/minecraft/client/gui/screen/Screen;Z)V", at = @At("TAIL")) + private void voxy$injectWorldClose(CallbackInfo ci) { + VoxyCommon.shutdownInstance(); + } + + @Inject(method = "joinWorld", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/MinecraftClient;setWorld(Lnet/minecraft/client/world/ClientWorld;)V", shift = At.Shift.BEFORE)) + private void voxy$injectInitialization(ClientWorld world, DownloadingTerrainScreen.WorldEntryReason worldEntryReason, CallbackInfo ci) { + if (VoxyConfig.CONFIG.enabled) { + VoxyCommon.createInstance(); + } + } } diff --git a/src/main/java/me/cortex/voxy/client/mixin/minecraft/MixinWorldRenderer.java b/src/main/java/me/cortex/voxy/client/mixin/minecraft/MixinWorldRenderer.java index 72074874..bdfdd830 100644 --- a/src/main/java/me/cortex/voxy/client/mixin/minecraft/MixinWorldRenderer.java +++ b/src/main/java/me/cortex/voxy/client/mixin/minecraft/MixinWorldRenderer.java @@ -1,12 +1,15 @@ package me.cortex.voxy.client.mixin.minecraft; -import me.cortex.voxy.client.Voxy; -import me.cortex.voxy.client.core.IGetVoxelCore; +import me.cortex.voxy.client.VoxyClient; import me.cortex.voxy.client.config.VoxyConfig; -import me.cortex.voxy.client.core.VoxelCore; +import me.cortex.voxy.client.core.IGetVoxyRenderSystem; +import me.cortex.voxy.client.core.rendering.VoxyRenderSystem; +import me.cortex.voxy.common.Logger; +import me.cortex.voxy.common.world.WorldEngine; +import me.cortex.voxy.commonImpl.VoxyCommon; +import me.cortex.voxy.commonImpl.VoxyInstance; import net.minecraft.client.render.*; import net.minecraft.client.util.ObjectAllocator; -import net.minecraft.client.util.math.MatrixStack; import net.minecraft.client.world.ClientWorld; import org.jetbrains.annotations.Nullable; import org.joml.Matrix4f; @@ -15,81 +18,71 @@ import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @Mixin(WorldRenderer.class) -public abstract class MixinWorldRenderer implements IGetVoxelCore { +public abstract class MixinWorldRenderer implements IGetVoxyRenderSystem { @Shadow private Frustum frustum; - @Shadow private @Nullable ClientWorld world; - @Unique private VoxelCore core; + @Unique private VoxyRenderSystem renderer; @Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/WorldRenderer;setupTerrain(Lnet/minecraft/client/render/Camera;Lnet/minecraft/client/render/Frustum;ZZ)V", shift = At.Shift.AFTER)) private void injectSetup(ObjectAllocator allocator, RenderTickCounter tickCounter, boolean renderBlockOutline, Camera camera, GameRenderer gameRenderer, Matrix4f positionMatrix, Matrix4f projectionMatrix, CallbackInfo ci) { - if (this.core != null) { - this.core.renderSetup(this.frustum, camera); + if (this.renderer != null) { + this.renderer.renderSetup(this.frustum, camera); } } - @Unique - public void populateCore() { - if (this.core != null) { - throw new IllegalStateException("Trying to create new core while a core already exists"); - } - this.core = Voxy.createVoxelCore(this.world); - } - - public VoxelCore getVoxelCore() { - return this.core; + @Override + public VoxyRenderSystem getVoxyRenderSystem() { + return this.renderer; } @Inject(method = "reload()V", at = @At("TAIL")) - private void resetVoxelCore(CallbackInfo ci) { - if (this.world != null && this.core != null) { - this.core.shutdown(); - this.core = null; - if (VoxyConfig.CONFIG.enabled) { - this.populateCore(); - } + private void reloadVoxyRenderer(CallbackInfo ci) { + this.shutdownRenderer(); + if (this.world != null) { + this.createRenderer(); } } @Inject(method = "setWorld", at = @At("TAIL")) private void initVoxelCore(ClientWorld world, CallbackInfo ci) { if (world == null) { - if (this.core != null) { - this.core.shutdown(); - this.core = null; - } - return; - } - - if (this.core != null) { - this.core.shutdown(); - this.core = null; - } - if (VoxyConfig.CONFIG.enabled) { - this.populateCore(); - } - } - - @Override - public void reloadVoxelCore() { - if (this.core != null) { - this.core.shutdown(); - this.core = null; - } - if (this.world != null && VoxyConfig.CONFIG.enabled) { - this.populateCore(); + this.shutdownRenderer(); } } @Inject(method = "close", at = @At("HEAD")) private void injectClose(CallbackInfo ci) { - if (this.core != null) { - this.core.shutdown(); - this.core = null; + this.shutdownRenderer(); + } + + @Override + public void shutdownRenderer() { + if (this.renderer != null) { + this.renderer.shutdown(); + this.renderer = null; } } + + @Override + public void createRenderer() { + if (this.renderer != null) throw new IllegalStateException("Cannot have multiple renderers"); + if ((!VoxyConfig.CONFIG.enableRendering)||(!VoxyConfig.CONFIG.enabled)) { + Logger.info("Not creating renderer due to disabled"); + return; + } + var instance = VoxyCommon.getInstance(); + if (instance == null) { + Logger.error("Not creating renderer due to null instance"); + return; + } + WorldEngine world = instance.getOrMakeWorld(this.world); + if (world == null) { + Logger.error("Null world selected"); + return; + } + this.renderer = new VoxyRenderSystem(world, instance.getThreadPool()); + } } diff --git a/src/main/java/me/cortex/voxy/client/mixin/sodium/MixinDefaultChunkRenderer.java b/src/main/java/me/cortex/voxy/client/mixin/sodium/MixinDefaultChunkRenderer.java index 8076825f..9329d58f 100644 --- a/src/main/java/me/cortex/voxy/client/mixin/sodium/MixinDefaultChunkRenderer.java +++ b/src/main/java/me/cortex/voxy/client/mixin/sodium/MixinDefaultChunkRenderer.java @@ -1,7 +1,6 @@ package me.cortex.voxy.client.mixin.sodium; -import me.cortex.voxy.client.core.IGetVoxelCore; -import me.cortex.voxy.client.core.util.IrisUtil; +import me.cortex.voxy.client.core.IGetVoxyRenderSystem; import net.caffeinemc.mods.sodium.client.gl.device.CommandList; import net.caffeinemc.mods.sodium.client.render.chunk.ChunkRenderMatrices; import net.caffeinemc.mods.sodium.client.render.chunk.DefaultChunkRenderer; @@ -9,13 +8,10 @@ import net.caffeinemc.mods.sodium.client.render.chunk.lists.ChunkRenderListItera import net.caffeinemc.mods.sodium.client.render.chunk.terrain.DefaultTerrainRenderPasses; import net.caffeinemc.mods.sodium.client.render.chunk.terrain.TerrainRenderPass; import net.caffeinemc.mods.sodium.client.render.viewport.CameraTransform; -import net.fabricmc.loader.api.FabricLoader; import net.minecraft.client.MinecraftClient; import net.minecraft.client.util.math.MatrixStack; import org.joml.Matrix4f; -import org.spongepowered.asm.mixin.FabricUtil; import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @@ -26,12 +22,12 @@ public class MixinDefaultChunkRenderer { @Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/caffeinemc/mods/sodium/client/render/chunk/ShaderChunkRenderer;end(Lnet/caffeinemc/mods/sodium/client/render/chunk/terrain/TerrainRenderPass;)V", shift = At.Shift.BEFORE)) private void injectRender(ChunkRenderMatrices matrices, CommandList commandList, ChunkRenderListIterable renderLists, TerrainRenderPass renderPass, CameraTransform camera, CallbackInfo ci) { if (renderPass == DefaultTerrainRenderPasses.CUTOUT) { - var core = ((IGetVoxelCore) MinecraftClient.getInstance().worldRenderer).getVoxelCore(); - if (core != null) { + var renderer = ((IGetVoxyRenderSystem) MinecraftClient.getInstance().worldRenderer).getVoxyRenderSystem(); + if (renderer != null) { var stack = new MatrixStack(); stack.loadIdentity(); stack.multiplyPositionMatrix(new Matrix4f(matrices.modelView())); - core.renderOpaque(stack, camera.x, camera.y, camera.z); + renderer.renderOpaque(stack, camera.x, camera.y, camera.z); } } } 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 857073f3..d3ef29ee 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 @@ -1,7 +1,7 @@ package me.cortex.voxy.client.mixin.sodium; import me.cortex.voxy.client.config.VoxyConfig; -import me.cortex.voxy.client.core.IGetVoxelCore; +import me.cortex.voxy.commonImpl.VoxyCommon; import net.caffeinemc.mods.sodium.client.render.chunk.RenderSectionManager; import net.minecraft.client.world.ClientWorld; import org.spongepowered.asm.mixin.Final; @@ -13,14 +13,14 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @Mixin(value = RenderSectionManager.class, remap = false) public class MixinRenderSectionManager { - @Shadow @Final private ClientWorld level; @Inject(method = "onChunkRemoved", at = @At("HEAD")) private void injectIngest(int x, int z, CallbackInfo ci) { - var core = ((IGetVoxelCore)(this.level.worldRenderer)).getVoxelCore(); - if (core != null && VoxyConfig.CONFIG.ingestEnabled) { - core.enqueueIngest(this.level.getChunk(x, z)); + //TODO: Am not quite sure if this is right + var instance = VoxyCommon.getInstance(); + if (instance != null && VoxyConfig.CONFIG.ingestEnabled) { + instance.getIngestService().enqueueIngest(this.level.getChunk(x, z)); } } } diff --git a/src/main/java/me/cortex/voxy/client/saver/ContextSelectionSystem.java b/src/main/java/me/cortex/voxy/client/saver/ContextSelectionSystem.java index ad6d3fd8..4ef3512b 100644 --- a/src/main/java/me/cortex/voxy/client/saver/ContextSelectionSystem.java +++ b/src/main/java/me/cortex/voxy/client/saver/ContextSelectionSystem.java @@ -104,10 +104,6 @@ public class ContextSelectionSystem { return this.config.sectionStorageConfig.build(ctx); } - public WorldEngine createEngine(ServiceThreadPool serviceThreadPool) { - return new WorldEngine(this.createSectionStorageBackend(), serviceThreadPool, VoxyConfig.CONFIG.secondaryLruCacheSize); - } - //Saves the config for the world selection or something, need to figure out how to make it work with dimensional configs maybe? // or just have per world config, cause when creating the world engine doing the string substitution would // make it automatically select the right id diff --git a/src/main/java/me/cortex/voxy/client/terrain/WorldImportCommand.java b/src/main/java/me/cortex/voxy/client/terrain/WorldImportCommand.java index 20b1d068..4428b2bf 100644 --- a/src/main/java/me/cortex/voxy/client/terrain/WorldImportCommand.java +++ b/src/main/java/me/cortex/voxy/client/terrain/WorldImportCommand.java @@ -5,8 +5,8 @@ import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.context.CommandContext; import com.mojang.brigadier.suggestion.Suggestions; import com.mojang.brigadier.suggestion.SuggestionsBuilder; -import me.cortex.voxy.client.core.IGetVoxelCore; -import me.cortex.voxy.client.core.VoxelCore; +import me.cortex.voxy.commonImpl.VoxyCommon; +import me.cortex.voxy.commonImpl.VoxyInstance; import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager; import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; import net.minecraft.client.MinecraftClient; @@ -20,8 +20,9 @@ import java.util.concurrent.CompletableFuture; public class WorldImportCommand { + /* public static LiteralArgumentBuilder register() { - return ClientCommandManager.literal("voxy").requires((ctx)-> ((IGetVoxelCore)MinecraftClient.getInstance().worldRenderer).getVoxelCore() != null) + return ClientCommandManager.literal("voxy").requires((ctx)-> VoxyCommon.getInstance() != null) .then(ClientCommandManager.literal("import") .then(ClientCommandManager.literal("world") .then(ClientCommandManager.argument("world_name", StringArgumentType.string()) @@ -138,5 +139,5 @@ public class WorldImportCommand { String finalInnerDir = innerDir; return core.importer.createWorldImporter(instance.player.clientWorld, (importer, up, done)->importer.importZippedRegionDirectoryAsyncStart(zip, finalInnerDir, up, done))?0:1; - } + }*/ } \ No newline at end of file diff --git a/src/main/java/me/cortex/voxy/common/Logger.java b/src/main/java/me/cortex/voxy/common/Logger.java index 983dd5d9..9029f7fd 100644 --- a/src/main/java/me/cortex/voxy/common/Logger.java +++ b/src/main/java/me/cortex/voxy/common/Logger.java @@ -1,5 +1,8 @@ package me.cortex.voxy.common; +import me.cortex.voxy.commonImpl.VoxyCommon; +import net.minecraft.client.MinecraftClient; +import net.minecraft.text.Text; import org.slf4j.LoggerFactory; import java.util.stream.Collectors; @@ -16,7 +19,11 @@ public class Logger { } } var stackEntry = new Throwable().getStackTrace()[1]; - LOGGER.error("["+stackEntry.getClassName()+"]: "+ Stream.of(args).map(Object::toString).collect(Collectors.joining(" ")), throwable); + String error = "["+stackEntry.getClassName()+"]: "+ Stream.of(args).map(Object::toString).collect(Collectors.joining(" ")); + LOGGER.error(error, throwable); + if (!VoxyCommon.IS_DEDICATED_SERVER) { + MinecraftClient.getInstance().executeSync(()->{var player = MinecraftClient.getInstance().player; if (player != null) player.sendMessage(Text.literal(error), true);}); + } } public static void warn(Object... args) { diff --git a/src/main/java/me/cortex/voxy/common/config/Serialization.java b/src/main/java/me/cortex/voxy/common/config/Serialization.java index 71382090..a5f48047 100644 --- a/src/main/java/me/cortex/voxy/common/config/Serialization.java +++ b/src/main/java/me/cortex/voxy/common/config/Serialization.java @@ -4,6 +4,7 @@ import com.google.gson.*; import com.google.gson.reflect.TypeToken; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonWriter; +import me.cortex.voxy.common.Logger; import net.fabricmc.loader.api.FabricLoader; import java.io.BufferedReader; @@ -132,20 +133,19 @@ public class Serialization { nameMethod.setAccessible(true); } catch (NoSuchMethodException e) {} if (nameMethod == null) { - System.err.println("WARNING: Config class " + clzName + " doesnt contain a getConfigTypeName and thus wont be serializable"); + Logger.error("WARNING: Config class " + clzName + " doesnt contain a getConfigTypeName and thus wont be serializable"); continue outer; } count++; String name = (String) nameMethod.invoke(null); serializers.computeIfAbsent(clz, GsonConfigSerialization::new) .register(name, (Class) original); - System.out.println("Registered " + original.getSimpleName() + " as " + name + " for config type " + clz.getSimpleName()); + Logger.info("Registered " + original.getSimpleName() + " as " + name + " for config type " + clz.getSimpleName()); break; } } } catch (Exception e) { - System.err.println("Error while setting up config serialization"); - e.printStackTrace(); + Logger.error("Error while setting up config serialization", e); } } @@ -155,7 +155,7 @@ public class Serialization { } GSON = builder.create(); - System.out.println("Registered " + count + " config types"); + Logger.info("Registered " + count + " config types"); } private static List collectAllClasses(String pack) { @@ -173,7 +173,7 @@ public class Serialization { } }).collect(Collectors.toList()); } catch (Exception e) { - System.err.println("Failed to collect classes in package: " + pack); + Logger.error("Failed to collect classes in package: " + pack, e); return List.of(); } } @@ -191,8 +191,7 @@ public class Serialization { return Stream.of(); } }).collect(Collectors.toList()); - } catch ( - IOException e) { + } catch (IOException e) { throw new RuntimeException(e); } } diff --git a/src/main/java/me/cortex/voxy/common/thread/ServiceSlice.java b/src/main/java/me/cortex/voxy/common/thread/ServiceSlice.java index ed1fb9db..ad551a86 100644 --- a/src/main/java/me/cortex/voxy/common/thread/ServiceSlice.java +++ b/src/main/java/me/cortex/voxy/common/thread/ServiceSlice.java @@ -93,7 +93,7 @@ public class ServiceSlice extends TrackedObject { //Tells the system that a single instance of this service needs executing public void execute() { if (!this.alive) { - System.err.println("Tried to do work on a dead service"); + Logger.error("Tried to do work on a dead service: " + this.name); return; } this.jobCount.release(); 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 411d8dc6..6f8a13be 100644 --- a/src/main/java/me/cortex/voxy/common/world/ActiveSectionTracker.java +++ b/src/main/java/me/cortex/voxy/common/world/ActiveSectionTracker.java @@ -4,6 +4,7 @@ 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; +import org.jetbrains.annotations.Nullable; import java.util.Arrays; @@ -18,10 +19,18 @@ public class ActiveSectionTracker { private final SectionLoader loader; private final int maxLRUSectionPerSlice; - private final Long2ObjectLinkedOpenHashMap[] lruSecondaryCache; + private final Long2ObjectLinkedOpenHashMap[] lruSecondaryCache;//TODO: THIS NEEDS TO BECOME A GLOBAL STATIC CACHE + @Nullable + public final WorldEngine engine; + + public ActiveSectionTracker(int numSlicesBits, SectionLoader loader, int cacheSize) { + this(numSlicesBits, loader, cacheSize, null); + } @SuppressWarnings("unchecked") - public ActiveSectionTracker(int numSlicesBits, SectionLoader loader, int cacheSize) { + public ActiveSectionTracker(int numSlicesBits, SectionLoader loader, int cacheSize, WorldEngine engine) { + this.engine = engine; + this.loader = loader; this.loadedSectionCache = new Long2ObjectOpenHashMap[1< saveQueue = new ConcurrentLinkedDeque<>(); - private final WorldEngine world; + private record SaveEntry(WorldEngine engine, WorldSection section) {} + private final ConcurrentLinkedDeque saveQueue = new ConcurrentLinkedDeque<>(); - public SectionSavingService(WorldEngine worldEngine, ServiceThreadPool threadPool) { - this.world = worldEngine; + public SectionSavingService(ServiceThreadPool threadPool) { this.threads = threadPool.createServiceNoCleanup("Section saving service", 100, () -> this::processJob); } private void processJob() { - var section = this.saveQueue.pop(); + var task = this.saveQueue.pop(); + var section = task.section; section.assertNotFree(); try { section.inSaveQueue.set(false); - this.world.storage.saveSection(section); + task.engine.storage.saveSection(section); } catch (Exception e) { - String err = "Voxy saver had an exception while executing please check logs and report error"; - Logger.error(err, e); - MinecraftClient.getInstance().executeSync(()->MinecraftClient.getInstance().player.sendMessage(Text.literal(err), true)); + Logger.error("Voxy saver had an exception while executing please check logs and report error", e); } section.release(); } public void enqueueSave(WorldSection section) { + if (section._getSectionTracker() != null && section._getSectionTracker().engine != null) { + this.enqueueSave(section._getSectionTracker().engine, section); + } else { + Logger.error("Tried saving world section, but did not have world associated"); + } + } + + public void enqueueSave(WorldEngine in, WorldSection section) { //If its not enqueued for saving then enqueue it if (!section.inSaveQueue.getAndSet(true)) { //Acquire the section for use section.acquire(); - this.saveQueue.add(section); + this.saveQueue.add(new SaveEntry(in, section)); this.threads.execute(); } } diff --git a/src/main/java/me/cortex/voxy/common/world/service/VoxelIngestService.java b/src/main/java/me/cortex/voxy/common/world/service/VoxelIngestService.java index c912288f..537dfd2c 100644 --- a/src/main/java/me/cortex/voxy/common/world/service/VoxelIngestService.java +++ b/src/main/java/me/cortex/voxy/common/world/service/VoxelIngestService.java @@ -1,12 +1,14 @@ package me.cortex.voxy.common.world.service; import it.unimi.dsi.fastutil.Pair; +import me.cortex.voxy.common.Logger; import me.cortex.voxy.common.voxelization.ILightingSupplier; import me.cortex.voxy.common.voxelization.VoxelizedSection; import me.cortex.voxy.common.voxelization.WorldConversionFactory; import me.cortex.voxy.common.world.WorldEngine; import me.cortex.voxy.common.thread.ServiceSlice; import me.cortex.voxy.common.thread.ServiceThreadPool; +import me.cortex.voxy.commonImpl.IVoxyWorldGetter; import net.minecraft.util.math.ChunkSectionPos; import net.minecraft.world.LightType; import net.minecraft.world.chunk.ChunkNibbleArray; @@ -20,12 +22,10 @@ import java.util.concurrent.ConcurrentLinkedDeque; public class VoxelIngestService { private static final ThreadLocal SECTION_CACHE = ThreadLocal.withInitial(VoxelizedSection::createEmpty); private final ServiceSlice threads; - private record IngestSection(int cx, int cy, int cz, ChunkSection section, ChunkNibbleArray blockLight, ChunkNibbleArray skyLight){} + private record IngestSection(int cx, int cy, int cz, WorldEngine world, ChunkSection section, ChunkNibbleArray blockLight, ChunkNibbleArray skyLight){} private final ConcurrentLinkedDeque ingestQueue = new ConcurrentLinkedDeque<>(); - private final WorldEngine world; - public VoxelIngestService(WorldEngine world, ServiceThreadPool pool) { - this.world = world; + public VoxelIngestService(ServiceThreadPool pool) { this.threads = pool.createServiceNoCleanup("Ingest service", 100, ()-> this::processJob); } @@ -35,7 +35,7 @@ public class VoxelIngestService { var vs = SECTION_CACHE.get().setPosition(task.cx, task.cy, task.cz); if (section.isEmpty() && task.blockLight==null && task.skyLight==null) {//If the chunk section has lighting data, propagate it - this.world.insertUpdate(vs.zero()); + task.world.insertUpdate(vs.zero()); } else { ILightingSupplier supplier = (x,y,z) -> (byte) 0; var sla = task.skyLight; @@ -65,13 +65,13 @@ public class VoxelIngestService { } VoxelizedSection csec = WorldConversionFactory.convert( SECTION_CACHE.get(), - this.world.getMapper(), + task.world.getMapper(), section.getBlockStateContainer(), section.getBiomeContainer(), supplier ); - WorldConversionFactory.mipSection(csec, this.world.getMapper()); - this.world.insertUpdate(csec); + WorldConversionFactory.mipSection(csec, task.world.getMapper()); + task.world.insertUpdate(csec); } } @@ -80,6 +80,15 @@ public class VoxelIngestService { } public void enqueueIngest(WorldChunk chunk) { + var engine = ((IVoxyWorldGetter)chunk.getWorld()).getWorldEngine(); + if (engine == null) { + Logger.error("Could not ingest chunk as does not have world engine"); + return; + } + this.enqueueIngest(engine, chunk); + } + + public void enqueueIngest(WorldEngine engine, WorldChunk chunk) { var lightingProvider = chunk.getWorld().getLightingProvider(); var blp = lightingProvider.get(LightType.BLOCK); var slp = lightingProvider.get(LightType.SKY); @@ -107,7 +116,7 @@ public class VoxelIngestService { continue; } - this.ingestQueue.add(new IngestSection(chunk.getPos().x, i, chunk.getPos().z, section, bl, sl)); + this.ingestQueue.add(new IngestSection(chunk.getPos().x, i, chunk.getPos().z, engine, section, bl, sl)); this.threads.execute(); } } diff --git a/src/main/java/me/cortex/voxy/commonImpl/VoxyCommon.java b/src/main/java/me/cortex/voxy/commonImpl/VoxyCommon.java index 61a64d90..36e9ce44 100644 --- a/src/main/java/me/cortex/voxy/commonImpl/VoxyCommon.java +++ b/src/main/java/me/cortex/voxy/commonImpl/VoxyCommon.java @@ -7,6 +7,8 @@ import net.fabricmc.loader.api.FabricLoader; import net.fabricmc.loader.api.ModContainer; public class VoxyCommon implements ModInitializer { + private static VoxyInstance INSTANCE; + public static final String MOD_VERSION; public static final boolean IS_DEDICATED_SERVER; @@ -27,11 +29,6 @@ public class VoxyCommon implements ModInitializer { @Override public void onInitialize() { - //this.serviceThreadPool = new ServiceThreadPool(VoxyConfig.CONFIG.serviceThreads); - - //TODO: need to have a common config with server/client configs deriving from it - // maybe server/client extend it? or something? cause like client needs server config (at least partially sometimes) - // but server doesnt need client config } public static void breakpoint() { @@ -44,4 +41,22 @@ public class VoxyCommon implements ModInitializer { public static boolean isVerificationFlagOn(String name) { return (!GlobalVerificationDisableOverride) && System.getProperty("voxy."+name, "true").equals("true"); } + + public static VoxyInstance getInstance() { + return INSTANCE; + } + + public static void shutdownInstance() { + if (INSTANCE != null) { + INSTANCE.shutdown(); + INSTANCE = null; + } + } + + public static void createInstance() { + if (INSTANCE != null) { + throw new IllegalStateException("Cannot create multiple instances"); + } + INSTANCE = new VoxyInstance(12); + } } diff --git a/src/main/java/me/cortex/voxy/commonImpl/VoxyInstance.java b/src/main/java/me/cortex/voxy/commonImpl/VoxyInstance.java new file mode 100644 index 00000000..ea376c57 --- /dev/null +++ b/src/main/java/me/cortex/voxy/commonImpl/VoxyInstance.java @@ -0,0 +1,75 @@ +package me.cortex.voxy.commonImpl; + +import me.cortex.voxy.client.saver.ContextSelectionSystem; +import me.cortex.voxy.common.Logger; +import me.cortex.voxy.common.thread.ServiceThreadPool; +import me.cortex.voxy.common.util.MemoryBuffer; +import me.cortex.voxy.common.world.WorldEngine; +import me.cortex.voxy.common.world.service.SectionSavingService; +import me.cortex.voxy.common.world.service.VoxelIngestService; +import net.minecraft.client.world.ClientWorld; + +import java.util.List; + +public class VoxyInstance { + private final ServiceThreadPool threadPool; + private final SectionSavingService savingService; + private final VoxelIngestService ingestService; + + public VoxyInstance(int threadCount) { + this.threadPool = new ServiceThreadPool(threadCount); + this.savingService = new SectionSavingService(this.threadPool); + this.ingestService = new VoxelIngestService(this.threadPool); + } + + public void addDebug(List debug) { + debug.add("Voxy Core: " + VoxyCommon.MOD_VERSION); + debug.add("MemoryBuffer, Count/Size (mb): " + MemoryBuffer.getCount() + "/" + (MemoryBuffer.getTotalSize()/1_000_000)); + debug.add("I/S: " + this.ingestService.getTaskCount() + "/" + this.savingService.getTaskCount()); + } + + public void shutdown() { + Logger.info("Shutdown voxy instance"); + try {this.ingestService.shutdown();} catch (Exception e) {Logger.error(e);} + try {this.savingService.shutdown();} catch (Exception e) {Logger.error(e);} + try {this.threadPool.shutdown();} catch (Exception e) {Logger.error(e);} + } + + public ServiceThreadPool getThreadPool() { + return this.threadPool; + } + + public VoxelIngestService getIngestService() { + return this.ingestService; + } + + public SectionSavingService getSavingService() { + return this.savingService; + } + + public void flush() { + try { + while (this.ingestService.getTaskCount() != 0) { + Thread.sleep(10); + } + while (this.savingService.getTaskCount() != 0) { + Thread.sleep(10); + } + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + private static final ContextSelectionSystem SELECTOR = new ContextSelectionSystem(); + + public WorldEngine getOrMakeWorld(ClientWorld world) { + var vworld = ((IVoxyWorldGetter)world).getWorldEngine(); + if (vworld == null) { + vworld = new WorldEngine(SELECTOR.getBestSelectionOrCreate(world).createSectionStorageBackend(), 1024); + vworld.setSaveCallback(this.savingService::enqueueSave); + ((IVoxyWorldSetter)world).setWorldEngine(vworld); + } + return vworld; + } + +} \ No newline at end of file diff --git a/src/main/java/me/cortex/voxy/commonImpl/importers/WorldImporter.java b/src/main/java/me/cortex/voxy/commonImpl/importers/WorldImporter.java index 90b1b3be..7ae10bb6 100644 --- a/src/main/java/me/cortex/voxy/commonImpl/importers/WorldImporter.java +++ b/src/main/java/me/cortex/voxy/commonImpl/importers/WorldImporter.java @@ -9,6 +9,7 @@ import me.cortex.voxy.common.voxelization.WorldConversionFactory; import me.cortex.voxy.common.world.WorldEngine; import me.cortex.voxy.common.thread.ServiceSlice; import me.cortex.voxy.common.thread.ServiceThreadPool; +import me.cortex.voxy.common.world.service.SectionSavingService; import net.minecraft.block.Block; import net.minecraft.block.BlockState; import net.minecraft.block.Blocks; @@ -37,6 +38,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.concurrent.ConcurrentLinkedDeque; import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.BooleanSupplier; import java.util.function.Consumer; import java.util.function.Predicate; @@ -57,9 +59,13 @@ public class WorldImporter { private final ServiceSlice threadPool; private volatile boolean isRunning; - public WorldImporter(WorldEngine worldEngine, World mcWorld, ServiceThreadPool servicePool) { + public WorldImporter(WorldEngine worldEngine, World mcWorld, ServiceThreadPool servicePool, SectionSavingService savingService) { + this(worldEngine, mcWorld, servicePool, ()->savingService.getTaskCount() < 4000); + } + + public WorldImporter(WorldEngine worldEngine, World mcWorld, ServiceThreadPool servicePool, BooleanSupplier runChecker) { this.world = worldEngine; - this.threadPool = servicePool.createServiceNoCleanup("World importer", 1, ()->()->this.jobQueue.poll().run(), ()->this.world.savingService.getTaskCount() < 4000); + this.threadPool = servicePool.createServiceNoCleanup("World importer", 1, ()->()->this.jobQueue.poll().run(), runChecker); var biomeRegistry = mcWorld.getRegistryManager().getOrThrow(RegistryKeys.BIOME); var defaultBiome = biomeRegistry.getOrThrow(BiomeKeys.PLAINS); diff --git a/src/main/java/me/cortex/voxy/client/mixin/chunky/MixinFabricWorld.java b/src/main/java/me/cortex/voxy/commonImpl/mixin/chunky/MixinFabricWorld.java similarity index 59% rename from src/main/java/me/cortex/voxy/client/mixin/chunky/MixinFabricWorld.java rename to src/main/java/me/cortex/voxy/commonImpl/mixin/chunky/MixinFabricWorld.java index 49ece333..ea1ceb4b 100644 --- a/src/main/java/me/cortex/voxy/client/mixin/chunky/MixinFabricWorld.java +++ b/src/main/java/me/cortex/voxy/commonImpl/mixin/chunky/MixinFabricWorld.java @@ -1,31 +1,18 @@ -package me.cortex.voxy.client.mixin.chunky; +package me.cortex.voxy.commonImpl.mixin.chunky; import com.llamalad7.mixinextras.injector.wrapoperation.Operation; import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; -import me.cortex.voxy.client.Voxy; -import me.cortex.voxy.client.config.VoxyConfig; -import me.cortex.voxy.client.core.IGetVoxelCore; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.util.math.MatrixStack; -import net.minecraft.server.world.ChunkHolder; +import me.cortex.voxy.commonImpl.VoxyCommon; import net.minecraft.server.world.OptionalChunk; -import net.minecraft.server.world.ServerChunkLoadingManager; -import net.minecraft.server.world.ServerWorld; import net.minecraft.world.chunk.Chunk; import net.minecraft.world.chunk.ChunkStatus; import net.minecraft.world.chunk.WorldChunk; -import org.joml.Matrix4f; import org.popcraft.chunky.mixin.ServerChunkCacheMixin; import org.popcraft.chunky.platform.FabricWorld; -import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Redirect; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Executor; -import java.util.function.BiConsumer; @Mixin(FabricWorld.class) public class MixinFabricWorld { @@ -34,9 +21,9 @@ public class MixinFabricWorld { var future = original.call(instance, i, j, chunkStatus, b); return future.thenApplyAsync(res->{ res.ifPresent(chunk -> { - var core = ((IGetVoxelCore)(MinecraftClient.getInstance().worldRenderer)).getVoxelCore(); - if (core != null && VoxyConfig.CONFIG.ingestEnabled) { - core.enqueueIngest((WorldChunk) chunk); + var voxyInstance = VoxyCommon.getInstance(); + if (voxyInstance != null) { + voxyInstance.getIngestService().enqueueIngest((WorldChunk) chunk); } }); return res; diff --git a/src/main/resources/client.voxy.mixins.json b/src/main/resources/client.voxy.mixins.json index 6045b3fd..6f99c93d 100644 --- a/src/main/resources/client.voxy.mixins.json +++ b/src/main/resources/client.voxy.mixins.json @@ -3,12 +3,11 @@ "package": "me.cortex.voxy.client.mixin", "compatibilityLevel": "JAVA_17", "client": [ - "chunky.MixinFabricWorld", "joml.AccessFrustumIntersection", "minecraft.MixinClientCommonNetworkHandler", - "minecraft.MixinThreadExecutor", "minecraft.MixinDebugHud", "minecraft.MixinMinecraftClient", + "minecraft.MixinThreadExecutor", "minecraft.MixinWorldRenderer", "sodium.MixinDefaultChunkRenderer", "sodium.MixinRenderSectionManager" diff --git a/src/main/resources/common.voxy.mixins.json b/src/main/resources/common.voxy.mixins.json index 2fb690da..cd40384b 100644 --- a/src/main/resources/common.voxy.mixins.json +++ b/src/main/resources/common.voxy.mixins.json @@ -6,6 +6,7 @@ "defaultRequire": 1 }, "mixins": [ + "chunky.MixinFabricWorld", "minecraft.MixinWorld" ] } diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index de72ba32..5a3130b2 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -18,7 +18,7 @@ "environment": "*", "entrypoints": { "client": [ - "me.cortex.voxy.client.Voxy" + "me.cortex.voxy.client.VoxyClient" ], "modmenu": [ "me.cortex.voxy.client.config.VoxyConfigScreenFactory"