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 203893d7..216e8abf 100644 --- a/src/main/java/me/cortex/voxy/client/core/VoxelCore.java +++ b/src/main/java/me/cortex/voxy/client/core/VoxelCore.java @@ -2,10 +2,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.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; import me.cortex.voxy.client.core.rendering.util.DownloadStream; import me.cortex.voxy.client.core.util.IrisUtil; 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 e1264c8f..369c10e5 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 @@ -101,21 +101,26 @@ 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); - if (textureId == null) { - System.err.println("ERROR: Empty texture id for layer: " + layer); - } else { - var texture = MinecraftClient.getInstance().getTextureManager().getTexture(textureId); - glBindTexture(GL_TEXTURE_2D, texture.getGlId()); + //TODO:FIXME: CANT RUN ON RENDER THREAD + if (false) { + System.err.println("Model entity baking not yet supported offthread baking"); + } else { + 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); + if (textureId == null) { + System.err.println("ERROR: Empty texture id for layer: " + layer); + } else { + var texture = MinecraftClient.getInstance().getTextureManager().getTexture(textureId); + glBindTexture(GL_TEXTURE_2D, texture.getGlId()); + } } + layer.putInto(bb); + BufferRenderer.draw(bb.end()); } - layer.putInto(bb); - BufferRenderer.draw(bb.end()); } } 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 24c763a0..3a9593f0 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 @@ -143,7 +143,6 @@ public class ModelFactory { 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)); @@ -156,7 +155,7 @@ public class ModelFactory { // while the depth is computed from the depth buffer data public void addEntry(int blockId, BlockState blockState) { if (this.idMappings[blockId] != -1) { - System.err.println("Block id already added: " + blockId + " for state: " + blockState); + //System.err.println("Block id already added: " + blockId + " for state: " + blockState); return; } diff --git a/src/main/java/me/cortex/voxy/client/core/model/ModelTextureBakery.java b/src/main/java/me/cortex/voxy/client/core/model/ModelTextureBakery.java index 63b534a7..0cc8051d 100644 --- a/src/main/java/me/cortex/voxy/client/core/model/ModelTextureBakery.java +++ b/src/main/java/me/cortex/voxy/client/core/model/ModelTextureBakery.java @@ -15,6 +15,7 @@ import net.minecraft.client.MinecraftClient; import net.minecraft.client.gl.GlUniform; import net.minecraft.client.render.*; import net.minecraft.client.render.model.BakedModel; +import net.minecraft.client.util.BufferAllocator; import net.minecraft.client.util.math.MatrixStack; import net.minecraft.fluid.FluidState; import net.minecraft.util.Identifier; @@ -118,15 +119,14 @@ public class ModelTextureBakery { var entityModel = state.hasBlockEntity()?BakedBlockEntityModel.bake(state):null; int oldFB = GlStateManager.getBoundFramebuffer(); - var oldProjection = new Matrix4f(RenderSystem.getProjectionMatrix()); GL11C.glViewport(0, 0, this.width, this.height); - RenderSystem.setProjectionMatrix(new Matrix4f().identity().set(new float[]{ + var projection = new Matrix4f().identity().set(new float[]{ 2,0,0,0, 0, 2,0,0, 0,0, -1f,0, -1,-1,0,1, - }), VertexSorter.BY_Z); + }); @@ -176,7 +176,7 @@ public class ModelTextureBakery { var faces = new ColourDepthTextureData[FACE_VIEWS.size()]; for (int i = 0; i < faces.length; i++) { - faces[i] = captureView(state, model, entityModel, FACE_VIEWS.get(i), randomValue, i, renderFluid, texId); + faces[i] = captureView(state, model, entityModel, FACE_VIEWS.get(i), randomValue, i, renderFluid, texId, projection); //glBlitNamedFramebuffer(this.framebuffer.id, oldFB, 0,0,16,16,300*(i>>1),300*(i&1),300*(i>>1)+256,300*(i&1)+256, GL_COLOR_BUFFER_BIT, GL_NEAREST); } @@ -184,7 +184,6 @@ public class ModelTextureBakery { glDisable(GL_STENCIL_TEST); glDisable(GL_BLEND); - RenderSystem.setProjectionMatrix(oldProjection, VertexSorter.BY_DISTANCE); glBindFramebuffer(GL_FRAMEBUFFER, oldFB); GL11C.glViewport(GlStateManager.Viewport.getX(), GlStateManager.Viewport.getY(), GlStateManager.Viewport.getWidth(), GlStateManager.Viewport.getHeight()); @@ -194,13 +193,11 @@ public class ModelTextureBakery { return faces; } - - private ColourDepthTextureData captureView(BlockState state, BakedModel model, BakedBlockEntityModel blockEntityModel, MatrixStack stack, long randomValue, int face, boolean renderFluid, int textureId) { - var vc = Tessellator.getInstance(); + private final BufferAllocator allocator = new BufferAllocator(786432); + private ColourDepthTextureData captureView(BlockState state, BakedModel model, BakedBlockEntityModel blockEntityModel, MatrixStack stack, long randomValue, int face, boolean renderFluid, int textureId, Matrix4f projection) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); - float[] mat = new float[4*4]; - new Matrix4f(RenderSystem.getProjectionMatrix()).mul(stack.peek().getPositionMatrix()).get(mat); + new Matrix4f(projection).mul(stack.peek().getPositionMatrix()).get(mat); glUniformMatrix4fv(1, false, mat); @@ -208,7 +205,7 @@ public class ModelTextureBakery { blockEntityModel.renderOut(); } - var bb = vc.begin(VertexFormat.DrawMode.QUADS, VertexFormats.POSITION_TEXTURE_COLOR); + var bb = new BufferBuilder(this.allocator, VertexFormat.DrawMode.QUADS, VertexFormats.POSITION_TEXTURE_COLOR); if (!renderFluid) { renderQuads(bb, state, model, new MatrixStack(), randomValue); } else { @@ -282,6 +279,7 @@ public class ModelTextureBakery { glBindTexture(GL_TEXTURE_2D, textureId); try { + //System.err.println("REPLACE THE UPLOADING WITH THREAD SAFE VARIENT"); BufferRenderer.draw(bb.end()); } catch (IllegalStateException e) { System.err.println("Got empty buffer builder! for block " + state); @@ -311,5 +309,6 @@ public class ModelTextureBakery { this.colourTex.free(); this.depthTex.free(); this.rasterShader.free(); + this.allocator.close(); } } 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 index 8b6f9c99..a0ddfad8 100644 --- a/src/main/java/me/cortex/voxy/client/core/model/OffThreadModelBakerySystem.java +++ b/src/main/java/me/cortex/voxy/client/core/model/OffThreadModelBakerySystem.java @@ -2,10 +2,12 @@ package me.cortex.voxy.client.core.model; import it.unimi.dsi.fastutil.ints.IntArrayFIFOQueue; +import it.unimi.dsi.fastutil.ints.IntLinkedOpenHashSet; import me.cortex.voxy.common.world.other.Mapper; import net.minecraft.client.MinecraftClient; import org.lwjgl.glfw.GLFW; import org.lwjgl.opengl.GL; +import org.lwjgl.opengl.GLCapabilities; import java.lang.invoke.VarHandle; import java.util.List; @@ -16,13 +18,16 @@ import java.util.concurrent.Semaphore; 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; + private static final GLCapabilities GL_CAPS; static { + var caps = GL.getCapabilities(); 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(); + GL_CAPS = GL.createCapabilities(); GLFW.glfwMakeContextCurrent(MinecraftClient.getInstance().getWindow().getHandle()); + GL.setCapabilities(caps); } @@ -30,7 +35,7 @@ public class OffThreadModelBakerySystem { 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 IntLinkedOpenHashSet blockIdQueue = new IntLinkedOpenHashSet(); private final Semaphore queueCounter = new Semaphore(0); @@ -47,14 +52,15 @@ public class OffThreadModelBakerySystem { private void bakeryThread() { GLFW.glfwMakeContextCurrent(GL_CTX); + GL.setCapabilities(GL_CAPS); //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(); + synchronized (this.blockIdQueue) { + blockId = this.blockIdQueue.removeFirstInt(); VarHandle.fullFence();//Ensure memory coherancy } @@ -80,10 +86,11 @@ public class OffThreadModelBakerySystem { } public void requestBlockBake(int blockId) { - synchronized (this.blockQueue) { - this.blockQueue.enqueue(blockId); - VarHandle.fullFence();//Ensure memory coherancy - this.queueCounter.release(1); + synchronized (this.blockIdQueue) { + if (this.blockIdQueue.add(blockId)) { + VarHandle.fullFence();//Ensure memory coherancy + this.queueCounter.release(1); + } } } diff --git a/src/main/java/me/cortex/voxy/client/core/model/OnThreadModelBakerySystem.java b/src/main/java/me/cortex/voxy/client/core/model/OnThreadModelBakerySystem.java new file mode 100644 index 00000000..6387263f --- /dev/null +++ b/src/main/java/me/cortex/voxy/client/core/model/OnThreadModelBakerySystem.java @@ -0,0 +1,56 @@ +package me.cortex.voxy.client.core.model; + + +import it.unimi.dsi.fastutil.ints.IntLinkedOpenHashSet; +import me.cortex.voxy.common.world.other.Mapper; +import net.minecraft.client.MinecraftClient; +import org.lwjgl.glfw.GLFW; +import org.lwjgl.opengl.GL; +import org.lwjgl.opengl.GLCapabilities; + +import java.lang.invoke.VarHandle; +import java.util.List; +import java.util.concurrent.ConcurrentLinkedDeque; +import java.util.concurrent.Semaphore; +public class OnThreadModelBakerySystem { + + private final ModelStore storage = new ModelStore(16); + public final ModelFactory factory; + private final IntLinkedOpenHashSet blockIdQueue = new IntLinkedOpenHashSet(); + + public OnThreadModelBakerySystem(Mapper mapper) { + this.factory = new ModelFactory(mapper); + } + + public void tick() { + if (!this.blockIdQueue.isEmpty()) { + int blockId = -1; + synchronized (this.blockIdQueue) { + if (!this.blockIdQueue.isEmpty()) { + blockId = this.blockIdQueue.removeFirstInt(); + VarHandle.fullFence();//Ensure memory coherancy + } + } + if (blockId != -1) { + this.factory.addEntry(blockId); + } + } + } + + public void shutdown() { + this.factory.free(); + this.storage.free(); + } + + public void requestBlockBake(int blockId) { + synchronized (this.blockIdQueue) { + if (this.blockIdQueue.add(blockId)) { + VarHandle.fullFence();//Ensure memory coherancy + } + } + } + + public void addDebugData(List debug) { + + } +} 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 77a79976..8ceeb919 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 @@ -2,6 +2,7 @@ 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.model.OnThreadModelBakerySystem; 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; @@ -10,14 +11,19 @@ import net.minecraft.client.render.Camera; import java.util.List; public class RenderService { - private final OffThreadModelBakerySystem modelService; + private final OnThreadModelBakerySystem modelService; private final RenderGenerationService renderGen; public RenderService(WorldEngine world) { - this.modelService = new OffThreadModelBakerySystem(world.getMapper()); + this.modelService = new OnThreadModelBakerySystem(world.getMapper()); this.renderGen = new RenderGenerationService(world, this.modelService, VoxyConfig.CONFIG.renderThreads, this::consumeRenderBuildResult, false); - - this.renderGen.enqueueTask(0,0,0,0); + for(int x = -10; x<=10;x++) { + for (int z = -10; z <= 10; z++) { + for (int y = -3; y <= 3; y++) { + this.renderGen.enqueueTask(0, x, y, z); + } + } + } } private void consumeRenderBuildResult(BuiltSection section) { @@ -26,7 +32,7 @@ public class RenderService { } public void setup(Camera camera) { - this.modelService.syncChanges(); + this.modelService.tick(); } public void renderFarAwayOpaque(Viewport viewport) { 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 b04a58b1..f187c831 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 @@ -5,6 +5,7 @@ import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap; import me.cortex.voxy.client.core.model.IdNotYetComputedException; import me.cortex.voxy.client.core.model.ModelFactory; import me.cortex.voxy.client.core.model.OffThreadModelBakerySystem; +import me.cortex.voxy.client.core.model.OnThreadModelBakerySystem; import me.cortex.voxy.common.world.WorldEngine; import me.cortex.voxy.common.world.WorldSection; import me.cortex.voxy.common.world.other.Mapper; @@ -29,12 +30,12 @@ public class RenderGenerationService { private final Semaphore taskCounter = new Semaphore(0); private final WorldEngine world; - private final OffThreadModelBakerySystem modelBakery; + private final OnThreadModelBakerySystem modelBakery; private final Consumer resultConsumer; private final BuiltSectionMeshCache meshCache = new BuiltSectionMeshCache(); private final boolean emitMeshlets; - public RenderGenerationService(WorldEngine world, OffThreadModelBakerySystem modelBakery, int workers, Consumer consumer, boolean emitMeshlets) { + public RenderGenerationService(WorldEngine world, OnThreadModelBakerySystem modelBakery, int workers, Consumer consumer, boolean emitMeshlets) { this.emitMeshlets = emitMeshlets; this.world = world; this.modelBakery = modelBakery;