diff --git a/gradle.properties b/gradle.properties index c630631b..e23b4f30 100644 --- a/gradle.properties +++ b/gradle.properties @@ -7,7 +7,7 @@ yarn_mappings=1.19.4+build.1 loader_version=0.14.19 fabric_version=0.76.0+1.19.4 # Mod Properties -mod_version=0.1.2-alpha +mod_version=0.1.3-alpha maven_group=me.cortex archives_base_name=nvidium # Dependencies diff --git a/src/main/java/me/cortex/nvidium/RenderPipeline.java b/src/main/java/me/cortex/nvidium/RenderPipeline.java index e7ed282b..6427bb9e 100644 --- a/src/main/java/me/cortex/nvidium/RenderPipeline.java +++ b/src/main/java/me/cortex/nvidium/RenderPipeline.java @@ -7,12 +7,14 @@ import me.cortex.nvidium.gl.RenderDevice; import me.cortex.nvidium.gl.buffers.IDeviceMappedBuffer; import me.cortex.nvidium.managers.SectionManager; +import me.cortex.nvidium.managers.VisibilityTracker; import me.cortex.nvidium.renderers.PrimaryTerrainRasterizer; import me.cortex.nvidium.renderers.RegionRasterizer; import me.cortex.nvidium.renderers.SectionRasterizer; import me.cortex.nvidium.renderers.TranslucentTerrainRasterizer; import me.cortex.nvidium.util.DownloadTaskStream; import me.cortex.nvidium.util.TickableManager; +import me.cortex.nvidium.util.UploadingBufferStream; import me.jellysquid.mods.sodium.client.SodiumClientMod; import me.jellysquid.mods.sodium.client.render.chunk.ChunkCameraContext; import me.jellysquid.mods.sodium.client.render.chunk.ChunkRenderMatrices; @@ -57,6 +59,7 @@ public class RenderPipeline { private static final RenderDevice device = new RenderDevice(); public final SectionManager sectionManager; + public final VisibilityTracker visibilityTracker; private final PrimaryTerrainRasterizer terrainRasterizer; private final RegionRasterizer regionRasterizer; @@ -70,12 +73,17 @@ public class RenderPipeline { private final IDeviceMappedBuffer sectionVisibility; private final IDeviceMappedBuffer terrainCommandBuffer; + private final UploadingBufferStream uploadStream; private final DownloadTaskStream downloadStream; private final int bufferSizesMB; public RenderPipeline() { - sectionManager = new SectionManager(device, MinecraftClient.getInstance().options.getClampedViewDistance(), 24, SodiumClientMod.options().advanced.cpuRenderAheadLimit+1, CompactChunkVertex.STRIDE); + int frames = SodiumClientMod.options().advanced.cpuRenderAheadLimit+1; + this.uploadStream = new UploadingBufferStream(device, frames, 160000000); + this.downloadStream = new DownloadTaskStream(device, frames, 16000000); + sectionManager = new SectionManager(device, uploadStream, MinecraftClient.getInstance().options.getClampedViewDistance(), 24, CompactChunkVertex.STRIDE); + visibilityTracker = new VisibilityTracker(downloadStream, frames, sectionManager.getRegionManager()); terrainRasterizer = new PrimaryTerrainRasterizer(); regionRasterizer = new RegionRasterizer(); sectionRasterizer = new SectionRasterizer(); @@ -90,7 +98,6 @@ public RenderPipeline() { cbs += maxRegions * 256L * 2; terrainCommandBuffer = device.createDeviceOnlyMappedBuffer(maxRegions*8L*7); cbs += maxRegions*8L*7; - downloadStream = new DownloadTaskStream(device, SodiumClientMod.options().advanced.cpuRenderAheadLimit+1, 16000000); bufferSizesMB = cbs/(1024*1024); } @@ -111,6 +118,8 @@ public void renderFrame(Frustum frustum, ChunkRenderMatrices crm, ChunkCameraCon int visibleRegions = 0; int playerRegion = -1; int playerRegionId = -1; + + short[] vregions; //Enqueue all the visible regions { var rm = sectionManager.getRegionManager(); @@ -130,11 +139,13 @@ public void renderFrame(Frustum frustum, ChunkRenderMatrices crm, ChunkCameraCon if (visibleRegions == 0) return; long addr = sectionManager.uploadStream.getUpload(sceneUniform, SCENE_SIZE, visibleRegions*2); int j = 0; + vregions = new short[regions.size()]; for (int i : regions) { if (((short)i) == playerRegionId) { playerRegion = j; } MemoryUtil.memPutShort(addr+((long) j <<1), (short) i); + vregions[j] = ((short)i); j++; } } @@ -215,6 +226,9 @@ public void renderFrame(Frustum frustum, ChunkRenderMatrices crm, ChunkCameraCon } } + {//Tick the visibility tracker + visibilityTracker.onFrame(vregions, regionVisibility); + } /* {//Download the region visibility from the gpu, used for determining culling downloadStream.download(regionVisibility, 0, visibleRegions, addr->{ @@ -287,6 +301,8 @@ public void renderTranslucent() { public void delete() { sectionManager.delete(); + visibilityTracker.delete(); + sceneUniform.delete(); regionVisibility.delete(); sectionVisibility.delete(); @@ -296,6 +312,9 @@ public void delete() { regionRasterizer.delete(); sectionRasterizer.delete(); translucencyTerrainRasterizer.delete(); + + downloadStream.delete(); + uploadStream.delete(); } public int getOtherBufferSizesMB() { diff --git a/src/main/java/me/cortex/nvidium/managers/RegionManager.java b/src/main/java/me/cortex/nvidium/managers/RegionManager.java index 5f9453a2..8d88e0da 100644 --- a/src/main/java/me/cortex/nvidium/managers/RegionManager.java +++ b/src/main/java/me/cortex/nvidium/managers/RegionManager.java @@ -8,7 +8,6 @@ import me.cortex.nvidium.util.UploadingBufferStream; import me.jellysquid.mods.sodium.client.util.frustum.Frustum; import net.minecraft.util.math.ChunkSectionPos; -import org.joml.Vector3i; import org.lwjgl.system.MemoryUtil; import java.util.BitSet; @@ -45,6 +44,27 @@ public boolean regionIsAtPos(int regionId, int x, int y, int z) { return region.rx == x && region.ry == y && region.rz == z; } + public void markVisible(short regionId, int frame) { + var region = regions[regionId]; + //Rare case since this is called N frames behind, the region might not exist + // or worse be a completely different region + if (region == null) { + return; + } + region.lastSeenVisible = frame; + region.lastSeenFrustum = frame; + } + + public void markFrustum(short regionId, int frame) { + var region = regions[regionId]; + //Rare case since this is called N frames behind, the region might not exist + // or worse be a completely different region + if (region == null) { + return; + } + region.lastSeenFrustum = frame; + } + //IDEA: make it so that sections are packed into regions, that is the local index of a chunk is hard coded to its position, and just 256 sections are processed when a region is visible, this has some overhead but means that the exact amount being processed each time is known and the same private static final class Region { private final int rx; @@ -104,6 +124,10 @@ public long getPackedData() { (ry<<2)+minY, (rz<<3)+minZ); } + + public int getVisibilityDelta() { + return lastSeenFrustum - lastSeenVisible; + } } public static long getRegionKey(int sectionX, int sectionY, int sectionZ) { @@ -164,6 +188,7 @@ public void removeSectionIndex(UploadingBufferStream uploadStream, int sectionId } } + //TODO: need to batch changes, cause in alot of cases the region is updated multiple times a frame private void updateRegion(UploadingBufferStream uploadingStream, Region region) { long segment = uploadingStream.getUpload(regionBuffer, (long) region.id * META_SIZE, META_SIZE); MemoryUtil.memPutLong(segment, region.getPackedData()); @@ -205,4 +230,18 @@ public int distance(int regionId, int camChunkX, int camChunkY, int camChunkZ) { Math.abs((region.ry<<2)+2-camChunkY)+ Math.abs((region.rz<<3)+4-camChunkZ); } + + public int findMaxSeenDelta() { + int delta = Integer.MIN_VALUE; + int id = -1; + for (int i = 0; i < maxRegionIndex(); i++) { + var region = regions[i]; + if (region == null) continue; + if (delta < region.getVisibilityDelta()) { + id = region.id; + delta = region.getVisibilityDelta(); + } + } + return id; + } } diff --git a/src/main/java/me/cortex/nvidium/managers/SectionManager.java b/src/main/java/me/cortex/nvidium/managers/SectionManager.java index f3fe04f2..7614239d 100644 --- a/src/main/java/me/cortex/nvidium/managers/SectionManager.java +++ b/src/main/java/me/cortex/nvidium/managers/SectionManager.java @@ -35,12 +35,10 @@ public class SectionManager { private final int formatSize; private final int bufferSize; - public SectionManager(RenderDevice device, int rd, int height, int frames, int quadVertexSize) { + public SectionManager(RenderDevice device, UploadingBufferStream uploadStream, int rd, int height, int quadVertexSize) { this.device = device; - - int bs = 0; - this.uploadStream = new UploadingBufferStream(device, frames, 160000000); - bs += 160000000; + this.uploadStream = uploadStream; + int bs = 16000000; //int widthSquared = (rd*2+1)*(rd*2+1); //int maxRegions = (int) Math.ceil((((double) widthSquared*height)/256))*2; @@ -130,7 +128,6 @@ public void commitChanges() { } public void delete() { - uploadStream.delete(); sectionBuffer.delete(); terrainAreana.delete(); regionManager.delete(); diff --git a/src/main/java/me/cortex/nvidium/managers/VisibilityTracker.java b/src/main/java/me/cortex/nvidium/managers/VisibilityTracker.java new file mode 100644 index 00000000..44cd2084 --- /dev/null +++ b/src/main/java/me/cortex/nvidium/managers/VisibilityTracker.java @@ -0,0 +1,43 @@ +package me.cortex.nvidium.managers; + +import me.cortex.nvidium.gl.buffers.Buffer; +import me.cortex.nvidium.util.DownloadTaskStream; +import org.lwjgl.system.MemoryUtil; + +//NOTE: the visibility tracker and RenderPipeline must be kept in frame perfect sync else all hell breaks loose +public class VisibilityTracker { + private final DownloadTaskStream downloadStream; + private int frameId; + private final short[][] regions; + private final byte[] frameIds; + private final RegionManager regionManager; + public VisibilityTracker(DownloadTaskStream downloadStream, int frames, RegionManager regionManager) { + this.downloadStream = downloadStream; + this.regionManager = regionManager; + regions = new short[frames][]; + frameIds = new byte[frames]; + } + + public void onFrame(short[] visibleRegions, Buffer regionVisibility) { + frameIds[(frameId)% regions.length] = (byte) (frameId-1); + regions[(frameId++)% regions.length] = visibleRegions; + downloadStream.download(regionVisibility, 0, visibleRegions.length, this::onDownload); + } + + private void onDownload(long ptr) { + int pframe = frameId - (this.regions.length - 1); + var regions = this.regions[pframe%this.regions.length]; + byte frame = frameIds[pframe%this.regions.length]; + for (int i = 0; i < regions.length; i++) { + if (MemoryUtil.memGetByte(ptr + i) == frame) { + regionManager.markVisible(regions[i], pframe); + } else { + regionManager.markFrustum(regions[i], pframe); + } + } + } + + public void delete() { + + } +} diff --git a/src/main/resources/assets/cortex/shaders/terrain/frag.frag b/src/main/resources/assets/cortex/shaders/terrain/frag.frag index 4451d4ad..3125cd29 100644 --- a/src/main/resources/assets/cortex/shaders/terrain/frag.frag +++ b/src/main/resources/assets/cortex/shaders/terrain/frag.frag @@ -15,7 +15,7 @@ layout(location = 0) out vec4 colour; layout(location = 1) in Interpolants { vec4 tint; - vec2 uv; + vec3 uv_bias; }; layout(binding = 0) uniform sampler2D tex_diffuse; @@ -26,7 +26,7 @@ void main() { //uint uid = gl_PrimitiveID*132471+123571; //colour = vec4(float((uid>>0)&7)/7, float((uid>>3)&7)/7, float((uid>>6)&7)/7, 1.0); //colour = vec4(1.0,1.0,0,1); - colour = texture(tex_diffuse, uv); + colour = texture(tex_diffuse, uv_bias.xy, uv_bias.z); if (colour.a < 0.05f) discard; colour.xyz *= tint.xyz; colour.xyz *= tint.w; diff --git a/src/main/resources/assets/cortex/shaders/terrain/mesh.glsl b/src/main/resources/assets/cortex/shaders/terrain/mesh.glsl index a1fc798f..aaae4e1f 100644 --- a/src/main/resources/assets/cortex/shaders/terrain/mesh.glsl +++ b/src/main/resources/assets/cortex/shaders/terrain/mesh.glsl @@ -24,7 +24,7 @@ taskNV in Task { layout(location=1) out Interpolants { vec4 tint; - vec2 uv; + vec3 uv_bias; } OUT[]; @@ -60,8 +60,9 @@ void main() { gl_PrimitiveIndicesNV[primId+1] = (isA?1:3)+idxBase; gl_PrimitiveIndicesNV[primId+2] = (isA?2:0)+idxBase; - OUT[(gl_LocalInvocationID.x<<1)|0].uv = vec2(A.g,A.h)/65535; - OUT[(gl_LocalInvocationID.x<<1)|1].uv = vec2(B.g,B.h)/65535; + uint material = uint(A.d>>8)&0xff; + OUT[(gl_LocalInvocationID.x<<1)|0].uv_bias = vec3(vec2(A.g,A.h)/65535, 0.0f);//(material&1u)!=0u?0.0f:-900.0f);//Temporary untill sodium 0.5 + OUT[(gl_LocalInvocationID.x<<1)|1].uv_bias = vec3(vec2(B.g,B.h)/65535, 0.0f);//(material&1u)!=0u?0.0f:-900.0f);//Temporary untill sodium 0.5 vec4 tintA = vec4(A.e&int16_t(0xFF),(A.e>>8)&int16_t(0xFF),A.f&int16_t(0xFF),(A.f>>8)&int16_t(0xFF))/255;