diff --git a/src/main/java/me/cortex/voxy/client/VoxyClient.java b/src/main/java/me/cortex/voxy/client/VoxyClient.java index 3fd1f22b..5ee279ec 100644 --- a/src/main/java/me/cortex/voxy/client/VoxyClient.java +++ b/src/main/java/me/cortex/voxy/client/VoxyClient.java @@ -6,11 +6,17 @@ import me.cortex.voxy.commonImpl.VoxyCommon; import net.fabricmc.api.ClientModInitializer; import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback; import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents; +import net.fabricmc.loader.api.FabricLoader; + +import java.util.HashSet; +import java.util.function.Consumer; +import java.util.function.Function; public class VoxyClient implements ClientModInitializer { + private static final HashSet FREX = new HashSet<>(); + @Override public void onInitializeClient() { - ClientLifecycleEvents.CLIENT_STARTED.register(client->{ boolean systemSupported = Capabilities.INSTANCE.compute && Capabilities.INSTANCE.indirectParameters; if (systemSupported) { @@ -26,5 +32,17 @@ public class VoxyClient implements ClientModInitializer { dispatcher.register(VoxyCommands.register()); } }); + + FabricLoader.getInstance() + .getEntrypoints("frex_flawless_frames", Consumer.class) + .forEach(api -> ((Consumer>>)api).accept(name->active->{if (active) { + FREX.add(name); + } else { + FREX.remove(name); + }})); + } + + public static boolean isFrexActive() { + return !FREX.isEmpty(); } } diff --git a/src/main/java/me/cortex/voxy/client/core/VoxyRenderSystem.java b/src/main/java/me/cortex/voxy/client/core/VoxyRenderSystem.java index ecf1be1f..f53f6cf9 100644 --- a/src/main/java/me/cortex/voxy/client/core/VoxyRenderSystem.java +++ b/src/main/java/me/cortex/voxy/client/core/VoxyRenderSystem.java @@ -4,6 +4,7 @@ import com.mojang.blaze3d.opengl.GlConst; import com.mojang.blaze3d.opengl.GlStateManager; import com.mojang.blaze3d.systems.RenderSystem; import me.cortex.voxy.client.TimingStatistics; +import me.cortex.voxy.client.VoxyClient; import me.cortex.voxy.client.config.VoxyConfig; import me.cortex.voxy.client.core.gl.Capabilities; import me.cortex.voxy.client.core.gl.GlBuffer; @@ -233,7 +234,7 @@ public class VoxyRenderSystem { //Tick upload stream (this is ok to do here as upload ticking is just memory management) UploadStream.INSTANCE.tick(); - this.renderDistanceTracker.setCenterAndProcess(cameraX, cameraZ); + while (this.renderDistanceTracker.setCenterAndProcess(cameraX, cameraZ) && VoxyClient.isFrexActive());//While FF is active, run until everything is processed //Done here as is allows less gl state resetup this.renderer.tickModelService(Math.max(3_000_000-(System.nanoTime()-startTime), 500_000)); diff --git a/src/main/java/me/cortex/voxy/client/core/gl/Capabilities.java b/src/main/java/me/cortex/voxy/client/core/gl/Capabilities.java index f949ce9c..92608dba 100644 --- a/src/main/java/me/cortex/voxy/client/core/gl/Capabilities.java +++ b/src/main/java/me/cortex/voxy/client/core/gl/Capabilities.java @@ -22,6 +22,8 @@ public class Capabilities { public final long totalDynamicMemory;//Bytes, total allocation memory - dedicated memory public final boolean compute; public final boolean indirectParameters; + public final boolean isIntel; + public Capabilities() { var cap = GL.getCapabilities(); this.compute = cap.glDispatchComputeIndirect != 0; @@ -42,6 +44,7 @@ public class Capabilities { this.ssboMaxSize = glGetInteger64(GL_MAX_SHADER_STORAGE_BLOCK_SIZE); this.isMesa = glGetString(GL_VERSION).toLowerCase().contains("mesa"); + this.isIntel = glGetString(GL_VENDOR).toLowerCase().contains("intel"); if (this.canQueryGpuMemory) { this.totalDedicatedMemory = glGetInteger64(GL_GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX)*1024;//Since its in Kb diff --git a/src/main/java/me/cortex/voxy/client/core/gl/shader/Shader.java b/src/main/java/me/cortex/voxy/client/core/gl/shader/Shader.java index 5dac487a..3d117f25 100644 --- a/src/main/java/me/cortex/voxy/client/core/gl/shader/Shader.java +++ b/src/main/java/me/cortex/voxy/client/core/gl/shader/Shader.java @@ -1,5 +1,6 @@ package me.cortex.voxy.client.core.gl.shader; +import me.cortex.voxy.client.core.gl.Capabilities; import me.cortex.voxy.client.core.gl.GlBuffer; import me.cortex.voxy.client.core.gl.GlDebug; import me.cortex.voxy.common.Logger; @@ -147,6 +148,7 @@ public class Shader extends TrackedObject { } public T compile() { + this.defineIf("IS_INTEL", Capabilities.INSTANCE.isIntel); return this.constructor.make(this, this.compileToProgram()); } diff --git a/src/main/java/me/cortex/voxy/client/core/rendering/RenderDistanceTracker.java b/src/main/java/me/cortex/voxy/client/core/rendering/RenderDistanceTracker.java index a6caeaa2..e83059a5 100644 --- a/src/main/java/me/cortex/voxy/client/core/rendering/RenderDistanceTracker.java +++ b/src/main/java/me/cortex/voxy/client/core/rendering/RenderDistanceTracker.java @@ -35,7 +35,7 @@ public class RenderDistanceTracker { this.tracker = new RingTracker(this.tracker, renderDistance, ((int)this.posX)>>9, ((int)this.posZ)>>9, true);//Steal from previous tracker } - public void setCenterAndProcess(double x, double z) { + public boolean setCenterAndProcess(double x, double z) { double dx = this.posX-x; double dz = this.posZ-z; if (CHECK_DISTANCE_BLOCKS*CHECK_DISTANCE_BLOCKS>9, ((int)z)>>9); } - this.tracker.process(this.processRate, this::add, this::rem); + return this.tracker.process(this.processRate, this::add, this::rem)!=0; } private void add(int x, int z) { 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 67775e83..a6058c61 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.RenderStatistics; import me.cortex.voxy.client.TimingStatistics; +import me.cortex.voxy.client.VoxyClient; import me.cortex.voxy.client.core.gl.Capabilities; import me.cortex.voxy.client.core.gl.GlTexture; import me.cortex.voxy.client.core.model.ModelBakerySubsystem; @@ -14,6 +15,7 @@ import me.cortex.voxy.client.core.rendering.section.geometry.*; import me.cortex.voxy.client.core.rendering.section.IUsesMeshlets; import me.cortex.voxy.client.core.rendering.section.MDICSectionRenderer; import me.cortex.voxy.client.core.rendering.util.DownloadStream; +import me.cortex.voxy.client.core.rendering.util.UploadStream; import me.cortex.voxy.common.Logger; import me.cortex.voxy.common.world.WorldEngine; import me.cortex.voxy.common.thread.ServiceThreadPool; @@ -117,18 +119,12 @@ public class RenderService, J extends Vi this.sectionRenderer.renderOpaque(viewport, depthBoundTexture); TimingStatistics.G.stop(); - //NOTE: need to do the upload and download tick here, after the section renderer renders the world, to ensure "stable" - // sections - - - //FIXME: we only want to tick once per full frame, this is due to how the data of sections is updated - // we basicly need the data to stay stable from one frame to the next, till after renderOpaque - // this is because e.g. shadows, cause this pipeline to be invoked multiple times - // which may cause the geometry to become outdated resulting in corruption rendering in renderOpaque - //TODO: Need to find a proper way to fix this (if there even is one) - { - TimingStatistics.main.stop(); - TimingStatistics.dynamic.start(); + do { + //NOTE: need to do the upload and download tick here, after the section renderer renders the world, to ensure "stable" + // sections + { + TimingStatistics.main.stop(); + TimingStatistics.dynamic.start(); /* this.sectionUpdateQueue.consume(128); @@ -143,29 +139,40 @@ public class RenderService, J extends Vi }*/ - TimingStatistics.D.start(); - //Tick download stream - DownloadStream.INSTANCE.tick(); - TimingStatistics.D.stop(); + TimingStatistics.D.start(); + //Tick download stream + DownloadStream.INSTANCE.tick(); + TimingStatistics.D.stop(); - this.nodeManager.tick(this.traversal.getNodeBuffer(), this.nodeCleaner); - //glFlush(); + this.nodeManager.tick(this.traversal.getNodeBuffer(), this.nodeCleaner); + //glFlush(); - this.nodeCleaner.tick(this.traversal.getNodeBuffer());//Probably do this here?? + this.nodeCleaner.tick(this.traversal.getNodeBuffer());//Probably do this here?? - TimingStatistics.dynamic.stop(); - TimingStatistics.main.start(); - } + TimingStatistics.dynamic.stop(); + TimingStatistics.main.start(); + } - glMemoryBarrier(GL_FRAMEBUFFER_BARRIER_BIT|GL_PIXEL_BUFFER_BARRIER_BIT); + glMemoryBarrier(GL_FRAMEBUFFER_BARRIER_BIT | GL_PIXEL_BUFFER_BARRIER_BIT); + + int depthBuffer = glGetFramebufferAttachmentParameteri(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME); + if (depthBuffer == 0) { + depthBuffer = glGetFramebufferAttachmentParameteri(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME); + } + + TimingStatistics.I.start(); + this.traversal.doTraversal(viewport, depthBuffer); + TimingStatistics.I.stop(); + + + if (VoxyClient.isFrexActive()) {//If frex is running we must tick everything to ensure correctness + UploadStream.INSTANCE.tick(); + //Done here as is allows less gl state resetup + this.tickModelService(100_000_000); + glFinish(); + } + } while (VoxyClient.isFrexActive() && (this.nodeManager.hasWork() || this.renderGen.getTaskCount()!=0 || !this.modelService.areQueuesEmpty())); - int depthBuffer = glGetFramebufferAttachmentParameteri(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME); - if (depthBuffer == 0) { - depthBuffer = glGetFramebufferAttachmentParameteri(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME); - } - TimingStatistics.I.start(); - this.traversal.doTraversal(viewport, depthBuffer); - TimingStatistics.I.stop(); TimingStatistics.H.start(); this.sectionRenderer.buildDrawCalls(viewport); diff --git a/src/main/java/me/cortex/voxy/client/core/rendering/hierachical/AsyncNodeManager.java b/src/main/java/me/cortex/voxy/client/core/rendering/hierachical/AsyncNodeManager.java index c21646c0..e92e873c 100644 --- a/src/main/java/me/cortex/voxy/client/core/rendering/hierachical/AsyncNodeManager.java +++ b/src/main/java/me/cortex/voxy/client/core/rendering/hierachical/AsyncNodeManager.java @@ -717,6 +717,10 @@ public class AsyncNodeManager { debug.add("UC/GC: " + (this.getUsedGeometryCapacity()/(1<<20))+"/"+(this.getGeometryCapacity()/(1<<20))); } + public boolean hasWork() { + return this.workCounter.get()!=0 && RESULT_HANDLE.get(this) != null; + } + //Results object, which is to be synced between the render thread and worker thread private static final class SyncResults { //Contains diff --git a/src/main/resources/assets/voxy/shaders/util/prefixsum/inital3.comp b/src/main/resources/assets/voxy/shaders/util/prefixsum/inital3.comp index 9bb1dbf3..7d12751b 100644 --- a/src/main/resources/assets/voxy/shaders/util/prefixsum/inital3.comp +++ b/src/main/resources/assets/voxy/shaders/util/prefixsum/inital3.comp @@ -43,12 +43,20 @@ void main() { barrier(); + #ifdef IS_INTEL + uint val = subgroupExclusiveAdd(warpPrefixSum[gl_SubgroupInvocationID]); + barrier(); + if (gl_SubgroupID == 0) { + warpPrefixSum[gl_SubgroupInvocationID] = val; + } + #else if (gl_SubgroupID == 0) { uint val = warpPrefixSum[gl_SubgroupInvocationID]; subgroupBarrier(); //Use warp to do entire add in 1 reduction warpPrefixSum[gl_SubgroupInvocationID] = subgroupExclusiveAdd(val); } + #endif barrier(); //Add the computed sum across all threads and warps