├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── src └── main │ ├── java │ └── me │ │ └── cortex │ │ └── nvidium │ │ ├── renderers │ │ ├── Phase.java │ │ ├── SortRegionSectionPhase.java │ │ ├── RegionRasterizer.java │ │ ├── SectionRasterizer.java │ │ ├── TemporalTerrainRasterizer.java │ │ ├── PrimaryTerrainRasterizer.java │ │ └── TranslucentTerrainRasterizer.java │ │ ├── gl │ │ ├── IResource.java │ │ ├── buffers │ │ │ ├── IClientMappedBuffer.java │ │ │ ├── IDeviceMappedBuffer.java │ │ │ ├── Buffer.java │ │ │ ├── PersistentClientMappedBuffer.java │ │ │ ├── DeviceOnlyMappedBuffer.java │ │ │ └── PersistentSparseAddressableBuffer.java │ │ ├── shader │ │ │ ├── IShaderProcessor.java │ │ │ ├── ShaderType.java │ │ │ └── Shader.java │ │ ├── GlObject.java │ │ ├── GlFence.java │ │ ├── RenderDevice.java │ │ ├── images │ │ │ └── DepthOnlyFrameBuffer.java │ │ └── TrackedObject.java │ │ ├── config │ │ ├── TranslucencySortingLevel.java │ │ ├── StatisticsLoggingLevel.java │ │ ├── NvidiumConfigStore.java │ │ └── NvidiumConfig.java │ │ ├── sodiumCompat │ │ ├── IRepackagedResult.java │ │ ├── INvidiumWorldRendererGetter.java │ │ ├── NvidiumOptionFlags.java │ │ ├── INvidiumWorldRendererSetter.java │ │ ├── IRenderSectionExtension.java │ │ ├── IrisCheck.java │ │ ├── RepackagedSectionOutput.java │ │ ├── ShaderLoader.java │ │ └── NvidiumCompactChunkVertex.java │ │ ├── mixin │ │ ├── minecraft │ │ │ ├── TextureAtlasAccessor.java │ │ │ ├── MixinGameRenderer.java │ │ │ ├── MixinGlDevice.java │ │ │ └── MixinFogRenderer.java │ │ └── sodium │ │ │ ├── SodiumWorldRendererAccessor.java │ │ │ ├── MixinChunkJobQueue.java │ │ │ ├── MixinRenderSection.java │ │ │ ├── MixinOptionFlag.java │ │ │ ├── MixinChunkBuildOutput.java │ │ │ ├── MixinChunkBuilderMeshingTask.java │ │ │ ├── MixinRenderRegionManager.java │ │ │ ├── MixinSodiumOptionsGUI.java │ │ │ └── MixinSodiumWorldRenderer.java │ │ ├── util │ │ ├── IdProvider.java │ │ ├── TickableManager.java │ │ ├── DownloadTaskStream.java │ │ ├── BufferArena.java │ │ ├── GPUTiming.java │ │ ├── UploadingBufferStream.java │ │ └── SegmentedManager.java │ │ ├── Nvidium.java │ │ ├── managers │ │ ├── RegionVisibilityTracker.java │ │ └── AsyncOcclusionTracker.java │ │ ├── api0 │ │ └── NvidiumAPI.java │ │ └── NvidiumWorldRenderer.java │ └── resources │ ├── assets │ └── nvidium │ │ ├── nvidium.png │ │ ├── shaders │ │ ├── terrain │ │ │ ├── vertex_format │ │ │ │ ├── vertex_format.glsl │ │ │ │ ├── nvidium_vertex_format.glsl │ │ │ │ └── sodium_vertex_format.glsl │ │ │ ├── task.glsl │ │ │ ├── temporal_task.glsl │ │ │ ├── task_common2.glsl │ │ │ ├── task_common.glsl │ │ │ ├── translucent │ │ │ │ ├── task.glsl │ │ │ │ └── mesh.glsl │ │ │ ├── frag.frag │ │ │ └── mesh.glsl │ │ ├── occlusion │ │ │ ├── queries │ │ │ │ └── region │ │ │ │ │ ├── fragment.frag │ │ │ │ │ └── mesh.glsl │ │ │ ├── region_raster │ │ │ │ ├── fragment.frag │ │ │ │ └── mesh.glsl │ │ │ ├── section_raster │ │ │ │ ├── fragment.glsl │ │ │ │ ├── task.glsl │ │ │ │ └── mesh.glsl │ │ │ └── scene.glsl │ │ └── sorting │ │ │ ├── sorting_network.glsl │ │ │ └── region_section_sorter.comp │ │ └── lang │ │ └── en_us.json │ ├── fabric.mod.json │ └── nvidium.mixins.json ├── settings.gradle ├── README.md ├── gradle.properties ├── .gitignore ├── gradlew.bat ├── LICENSE.txt └── gradlew /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drouarb/nvidium/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /src/main/java/me/cortex/nvidium/renderers/Phase.java: -------------------------------------------------------------------------------- 1 | package me.cortex.nvidium.renderers; 2 | 3 | public abstract class Phase { 4 | } 5 | -------------------------------------------------------------------------------- /src/main/resources/assets/nvidium/nvidium.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drouarb/nvidium/HEAD/src/main/resources/assets/nvidium/nvidium.png -------------------------------------------------------------------------------- /src/main/java/me/cortex/nvidium/gl/IResource.java: -------------------------------------------------------------------------------- 1 | package me.cortex.nvidium.gl; 2 | 3 | public interface IResource { 4 | void delete(); 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/me/cortex/nvidium/gl/buffers/IClientMappedBuffer.java: -------------------------------------------------------------------------------- 1 | package me.cortex.nvidium.gl.buffers; 2 | 3 | public interface IClientMappedBuffer extends Buffer { 4 | long clientAddress(); 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/me/cortex/nvidium/gl/buffers/IDeviceMappedBuffer.java: -------------------------------------------------------------------------------- 1 | package me.cortex.nvidium.gl.buffers; 2 | 3 | public interface IDeviceMappedBuffer extends Buffer { 4 | long getDeviceAddress(); 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/me/cortex/nvidium/gl/shader/IShaderProcessor.java: -------------------------------------------------------------------------------- 1 | package me.cortex.nvidium.gl.shader; 2 | 3 | public interface IShaderProcessor { 4 | String process(ShaderType type, String source); 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/me/cortex/nvidium/config/TranslucencySortingLevel.java: -------------------------------------------------------------------------------- 1 | package me.cortex.nvidium.config; 2 | 3 | public enum TranslucencySortingLevel { 4 | NONE, 5 | SECTIONS, 6 | QUADS, 7 | SODIUM 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/me/cortex/nvidium/gl/buffers/Buffer.java: -------------------------------------------------------------------------------- 1 | package me.cortex.nvidium.gl.buffers; 2 | 3 | import me.cortex.nvidium.gl.IResource; 4 | 5 | public interface Buffer extends IResource { 6 | int getId(); 7 | long getSize(); 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/me/cortex/nvidium/sodiumCompat/IRepackagedResult.java: -------------------------------------------------------------------------------- 1 | package me.cortex.nvidium.sodiumCompat; 2 | 3 | public interface IRepackagedResult { 4 | RepackagedSectionOutput getOutput(); 5 | void set(RepackagedSectionOutput output); 6 | } 7 | -------------------------------------------------------------------------------- /src/main/java/me/cortex/nvidium/config/StatisticsLoggingLevel.java: -------------------------------------------------------------------------------- 1 | package me.cortex.nvidium.config; 2 | 3 | public enum StatisticsLoggingLevel { 4 | NONE, 5 | FRUSTUM, 6 | REGIONS, 7 | SECTIONS, 8 | QUADS, 9 | CULL 10 | } 11 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | maven { 4 | name = 'Fabric' 5 | url = 'https://maven.fabricmc.net/' 6 | } 7 | mavenCentral() 8 | gradlePluginPortal() 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/me/cortex/nvidium/sodiumCompat/INvidiumWorldRendererGetter.java: -------------------------------------------------------------------------------- 1 | package me.cortex.nvidium.sodiumCompat; 2 | 3 | import me.cortex.nvidium.NvidiumWorldRenderer; 4 | 5 | public interface INvidiumWorldRendererGetter { 6 | NvidiumWorldRenderer getRenderer(); 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/me/cortex/nvidium/sodiumCompat/NvidiumOptionFlags.java: -------------------------------------------------------------------------------- 1 | package me.cortex.nvidium.sodiumCompat; 2 | 3 | import net.caffeinemc.mods.sodium.client.gui.options.OptionFlag; 4 | 5 | public class NvidiumOptionFlags { 6 | public static OptionFlag REQUIRES_SHADER_RELOAD; 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/me/cortex/nvidium/sodiumCompat/INvidiumWorldRendererSetter.java: -------------------------------------------------------------------------------- 1 | package me.cortex.nvidium.sodiumCompat; 2 | 3 | import me.cortex.nvidium.NvidiumWorldRenderer; 4 | 5 | public interface INvidiumWorldRendererSetter { 6 | void setWorldRenderer(NvidiumWorldRenderer renderer); 7 | } 8 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /src/main/java/me/cortex/nvidium/sodiumCompat/IRenderSectionExtension.java: -------------------------------------------------------------------------------- 1 | package me.cortex.nvidium.sodiumCompat; 2 | 3 | public interface IRenderSectionExtension { 4 | boolean isSubmittedRebuild(); 5 | void isSubmittedRebuild(boolean state); 6 | 7 | boolean isSeen(); 8 | void isSeen(boolean state); 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/me/cortex/nvidium/gl/GlObject.java: -------------------------------------------------------------------------------- 1 | package me.cortex.nvidium.gl; 2 | 3 | public abstract class GlObject extends TrackedObject implements IResource { 4 | protected final int id; 5 | 6 | protected GlObject(int id) { 7 | this.id = id; 8 | } 9 | 10 | public int getId() { 11 | return id; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Nvidium 2 | 3 | [![Modrinth](https://img.shields.io/modrinth/dt/nvidium?logo=modrinth)](https://modrinth.com/mod/nvidium) 4 | 5 | Nvidium is an alternate rendering backing for sodium, it uses cutting edge nvidia features to render huge amounts of 6 | terrain geometry at very playable framerates. 7 | 8 | ### Requires sodium and an nvidia gtx 1600 series or newer to run (turing+ architecture) -------------------------------------------------------------------------------- /src/main/java/me/cortex/nvidium/mixin/minecraft/TextureAtlasAccessor.java: -------------------------------------------------------------------------------- 1 | package me.cortex.nvidium.mixin.minecraft; 2 | 3 | import net.minecraft.client.renderer.texture.TextureAtlas; 4 | import org.spongepowered.asm.mixin.Mixin; 5 | import org.spongepowered.asm.mixin.gen.Accessor; 6 | 7 | @Mixin(TextureAtlas.class) 8 | public interface TextureAtlasAccessor { 9 | @Accessor 10 | int getWidth(); 11 | 12 | @Accessor 13 | int getHeight(); 14 | } 15 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Done to increase the memory available to gradle. 2 | org.gradle.jvmargs=-Xmx1G 3 | # Fabric Properties 4 | # check these on https://modmuss50.me/fabric.html 5 | 6 | minecraft_version=1.21.10 7 | loader_version=0.17.2 8 | parchmentmc_mappings=2025.10.12 9 | 10 | # Fabric API 11 | fabric_version=0.136.0+1.21.10 12 | 13 | # Mod Properties 14 | mod_version=0.4.2-beta3-1.21.9-1.21.10 15 | maven_group=me.cortex 16 | archives_base_name=nvidium 17 | # Dependencies 18 | # check this on https://modmuss50.me/fabric.html 19 | -------------------------------------------------------------------------------- /src/main/java/me/cortex/nvidium/mixin/sodium/SodiumWorldRendererAccessor.java: -------------------------------------------------------------------------------- 1 | package me.cortex.nvidium.mixin.sodium; 2 | 3 | import net.caffeinemc.mods.sodium.client.render.SodiumWorldRenderer; 4 | import net.caffeinemc.mods.sodium.client.render.chunk.RenderSectionManager; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.gen.Accessor; 7 | 8 | @Mixin(value = SodiumWorldRenderer.class, remap = false) 9 | public interface SodiumWorldRendererAccessor { 10 | @Accessor 11 | RenderSectionManager getRenderSectionManager(); 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/me/cortex/nvidium/sodiumCompat/IrisCheck.java: -------------------------------------------------------------------------------- 1 | package me.cortex.nvidium.sodiumCompat; 2 | 3 | import net.fabricmc.loader.api.FabricLoader; 4 | import net.irisshaders.iris.api.v0.IrisApi; 5 | 6 | public class IrisCheck { 7 | public static final boolean IRIS_LOADED = FabricLoader.getInstance().isModLoaded("iris"); 8 | 9 | private static boolean checkIrisShaders() { 10 | return IrisApi.getInstance().isShaderPackInUse(); 11 | } 12 | 13 | public static boolean checkIrisShouldDisable() { 14 | return !(IRIS_LOADED && checkIrisShaders()); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/me/cortex/nvidium/sodiumCompat/RepackagedSectionOutput.java: -------------------------------------------------------------------------------- 1 | package me.cortex.nvidium.sodiumCompat; 2 | 3 | import net.caffeinemc.mods.sodium.client.util.NativeBuffer; 4 | import org.joml.Vector3i; 5 | 6 | //Computed on the build thread instead of the render thread saving alot of 1% lows 7 | public record RepackagedSectionOutput(int quads, 8 | NativeBuffer geometry, 9 | short[] offsets, 10 | Vector3i min, 11 | Vector3i size) { 12 | public void delete() { 13 | geometry.free(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/me/cortex/nvidium/config/NvidiumConfigStore.java: -------------------------------------------------------------------------------- 1 | package me.cortex.nvidium.config; 2 | 3 | import me.cortex.nvidium.Nvidium; 4 | import me.cortex.nvidium.config.NvidiumConfig; 5 | import net.caffeinemc.mods.sodium.client.gui.options.storage.OptionStorage; 6 | 7 | public class NvidiumConfigStore implements OptionStorage { 8 | private final NvidiumConfig config; 9 | 10 | public NvidiumConfigStore() { 11 | config = Nvidium.config; 12 | } 13 | 14 | @Override 15 | public NvidiumConfig getData() { 16 | return config; 17 | } 18 | 19 | @Override 20 | public void save() { 21 | config.save(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/me/cortex/nvidium/mixin/sodium/MixinChunkJobQueue.java: -------------------------------------------------------------------------------- 1 | package me.cortex.nvidium.mixin.sodium; 2 | 3 | import org.spongepowered.asm.mixin.Mixin; 4 | import org.spongepowered.asm.mixin.injection.At; 5 | import org.spongepowered.asm.mixin.injection.Inject; 6 | import org.spongepowered.asm.mixin.injection.Redirect; 7 | 8 | @Mixin(targets = {"net.caffeinemc.mods.sodium.client.render.chunk.compile.executor.ChunkJobQueue"},remap = false) 9 | public class MixinChunkJobQueue { 10 | @Redirect(method = "shutdown", at = @At(value = "INVOKE", target = "Ljava/lang/Runtime;availableProcessors()I")) 11 | private int returnAlot(Runtime instance) { 12 | return Integer.MAX_VALUE>>1; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/me/cortex/nvidium/gl/shader/ShaderType.java: -------------------------------------------------------------------------------- 1 | package me.cortex.nvidium.gl.shader; 2 | 3 | 4 | import static org.lwjgl.opengl.GL20.GL_FRAGMENT_SHADER; 5 | import static org.lwjgl.opengl.GL20.GL_VERTEX_SHADER; 6 | import static org.lwjgl.opengl.GL43C.GL_COMPUTE_SHADER; 7 | import static org.lwjgl.opengl.NVMeshShader.GL_MESH_SHADER_NV; 8 | import static org.lwjgl.opengl.NVMeshShader.GL_TASK_SHADER_NV; 9 | 10 | public enum ShaderType { 11 | VERTEX(GL_VERTEX_SHADER), 12 | FRAGMENT(GL_FRAGMENT_SHADER), 13 | COMPUTE(GL_COMPUTE_SHADER), 14 | MESH(GL_MESH_SHADER_NV), 15 | TASK(GL_TASK_SHADER_NV); 16 | public final int gl; 17 | ShaderType(int glEnum) { 18 | gl = glEnum; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/resources/fabric.mod.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 1, 3 | "id": "nvidium", 4 | "version": "${version}", 5 | "custom": { 6 | "commit": "${commit}", 7 | "buildtime": "${buildtime}" 8 | }, 9 | "name": "Nvidium", 10 | "description": "Replacement rendering engine for sodium", 11 | "authors": [ 12 | "Cortex" 13 | ], 14 | "contributors": [ 15 | "drouarb" 16 | ], 17 | "contact": {}, 18 | "license": "LGPL-3.0", 19 | "icon": "assets/nvidium/nvidium.png", 20 | "environment": "client", 21 | "entrypoints": {}, 22 | "mixins": [ 23 | "nvidium.mixins.json" 24 | ], 25 | "depends": { 26 | "fabricloader": ">=0.17.2", 27 | "minecraft": ["1.21.9", "1.21.10"], 28 | "sodium": ["=0.7.2", "=0.7.3"] 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/resources/assets/nvidium/shaders/terrain/vertex_format/vertex_format.glsl: -------------------------------------------------------------------------------- 1 | #define COLOR_SCALE 1.0 / 255.0 2 | 3 | #ifdef USE_SODIUM_VERTEX_FORMAT 4 | #import 5 | #else 6 | #import 7 | #endif 8 | 9 | float getVertexAlphaCutoff(uint v) { 10 | return (float[](0.0f, 0.1f,0.1f,1.0f))[v]; 11 | } 12 | 13 | vec4 sampleLight(vec2 uv) { 14 | //Its divided by 16 to match sodium/vanilla (it can never be 1 which is funny) 15 | return vec4(texture(tex_light, uv).rgb, 1); 16 | } 17 | 18 | vec3 computeMultiplier(Vertex V) { 19 | vec4 tint = decodeVertexColour(V); 20 | tint *= sampleLight(decodeLightUV(V)); 21 | tint *= tint.w; 22 | return tint.xyz; 23 | } -------------------------------------------------------------------------------- /src/main/resources/nvidium.mixins.json: -------------------------------------------------------------------------------- 1 | { 2 | "required": true, 3 | "package": "me.cortex.nvidium.mixin", 4 | "compatibilityLevel": "JAVA_17", 5 | "client": [ 6 | "minecraft.MixinFogRenderer", 7 | "minecraft.MixinGameRenderer", 8 | "minecraft.MixinGlDevice", 9 | "minecraft.TextureAtlasAccessor", 10 | "sodium.MixinChunkBuilderMeshingTask", 11 | "sodium.MixinChunkBuildOutput", 12 | "sodium.MixinChunkJobQueue", 13 | "sodium.MixinOptionFlag", 14 | "sodium.MixinRenderRegionManager", 15 | "sodium.MixinRenderSection", 16 | "sodium.MixinRenderSectionManager", 17 | "sodium.MixinSodiumOptionsGUI", 18 | "sodium.MixinSodiumWorldRenderer", 19 | "sodium.SodiumWorldRendererAccessor" 20 | ], 21 | "injectors": { 22 | "defaultRequire": 1 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/me/cortex/nvidium/mixin/minecraft/MixinGameRenderer.java: -------------------------------------------------------------------------------- 1 | package me.cortex.nvidium.mixin.minecraft; 2 | 3 | import me.cortex.nvidium.Nvidium; 4 | import net.minecraft.client.renderer.GameRenderer; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.injection.At; 7 | import org.spongepowered.asm.mixin.injection.Inject; 8 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 9 | 10 | @Mixin(GameRenderer.class) 11 | public class MixinGameRenderer { 12 | @Inject(method = "getDepthFar()F", at = @At("HEAD"), cancellable = true) 13 | public void method_32796(CallbackInfoReturnable cir) { 14 | if (Nvidium.IS_ENABLED) { 15 | cir.setReturnValue(16 * 512f); 16 | cir.cancel(); 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /src/main/resources/assets/nvidium/shaders/occlusion/queries/region/fragment.frag: -------------------------------------------------------------------------------- 1 | #version 460 2 | #extension GL_ARB_shading_language_include : enable 3 | #pragma optionNV(unroll all) 4 | #define UNROLL_LOOP 5 | #extension GL_NV_gpu_shader5 : require 6 | #extension GL_NV_bindless_texture : require 7 | #extension GL_NV_shader_buffer_load : require 8 | 9 | #import 10 | layout(early_fragment_tests) in; 11 | 12 | #ifdef DEBUG 13 | layout(location = 0) out vec4 colour; 14 | void main() { 15 | uint uid = gl_PrimitiveID*132471+123571; 16 | colour = vec4(float((uid>>0)&7)/7, float((uid>>3)&7)/7, float((uid>>6)&7)/7, 1.0); 17 | regionVisibility[gl_PrimitiveID] = uint8_t(1); 18 | } 19 | #else 20 | void main() { 21 | regionVisibility[gl_PrimitiveID] = uint8_t(1); 22 | } 23 | #endif -------------------------------------------------------------------------------- /src/main/resources/assets/nvidium/shaders/occlusion/region_raster/fragment.frag: -------------------------------------------------------------------------------- 1 | #version 460 2 | #extension GL_ARB_shading_language_include : enable 3 | #pragma optionNV(unroll all) 4 | #define UNROLL_LOOP 5 | #extension GL_NV_gpu_shader5 : require 6 | #extension GL_NV_bindless_texture : require 7 | #extension GL_NV_shader_buffer_load : require 8 | 9 | #import 10 | layout(early_fragment_tests) in; 11 | 12 | #ifdef DEBUG 13 | layout(location = 0) out vec4 colour; 14 | void main() { 15 | uint uid = gl_PrimitiveID*132471+123571; 16 | colour = vec4(float((uid>>0)&7)/7, float((uid>>3)&7)/7, float((uid>>6)&7)/7, 1.0); 17 | regionVisibility[gl_PrimitiveID] = uint8_t(1); 18 | } 19 | #else 20 | void main() { 21 | regionVisibility[gl_PrimitiveID] = uint8_t(1); 22 | } 23 | #endif -------------------------------------------------------------------------------- /src/main/java/me/cortex/nvidium/util/IdProvider.java: -------------------------------------------------------------------------------- 1 | package me.cortex.nvidium.util; 2 | 3 | import it.unimi.dsi.fastutil.ints.IntAVLTreeSet; 4 | import it.unimi.dsi.fastutil.ints.IntSortedSet; 5 | 6 | public class IdProvider { 7 | private int cid = 0; 8 | private final IntSortedSet free = new IntAVLTreeSet(Integer::compareTo); 9 | 10 | public int provide() { 11 | if (free.isEmpty()) { 12 | return cid++; 13 | } 14 | int ret = free.firstInt(); 15 | free.remove(ret); 16 | return ret; 17 | } 18 | 19 | public void release(int id) { 20 | free.add(id); 21 | while ((!free.isEmpty()) && free.lastInt()+1 == cid) { 22 | free.remove(--cid); 23 | } 24 | } 25 | 26 | public int maxIndex() { 27 | return cid; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/resources/assets/nvidium/shaders/occlusion/section_raster/fragment.glsl: -------------------------------------------------------------------------------- 1 | #version 460 2 | #extension GL_ARB_shading_language_include : enable 3 | #pragma optionNV(unroll all) 4 | #define UNROLL_LOOP 5 | #extension GL_NV_gpu_shader5 : require 6 | #extension GL_NV_bindless_texture : require 7 | #extension GL_NV_shader_buffer_load : require 8 | 9 | #import 10 | layout(early_fragment_tests) in; 11 | 12 | #ifdef DEBUG 13 | layout(location = 0) out vec4 colour; 14 | void main() { 15 | uint uid = bitfieldReverse(gl_PrimitiveID*132471+123571); 16 | colour = vec4(float((uid>>0)&7)/7, float((uid>>3)&7)/7, float((uid>>6)&7)/7, 1.0); 17 | sectionVisibility[gl_PrimitiveID>>8] = uint8_t(gl_PrimitiveID); 18 | } 19 | #else 20 | void main() { 21 | sectionVisibility[gl_PrimitiveID>>8] = uint8_t(gl_PrimitiveID); 22 | } 23 | #endif -------------------------------------------------------------------------------- /src/main/java/me/cortex/nvidium/mixin/minecraft/MixinGlDevice.java: -------------------------------------------------------------------------------- 1 | package me.cortex.nvidium.mixin.minecraft; 2 | 3 | import me.cortex.nvidium.Nvidium; 4 | import org.spongepowered.asm.mixin.Mixin; 5 | import org.spongepowered.asm.mixin.injection.At; 6 | import org.spongepowered.asm.mixin.injection.Inject; 7 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 8 | import com.mojang.blaze3d.opengl.GlDevice; 9 | import java.util.function.BiFunction; 10 | 11 | @Mixin(GlDevice.class) 12 | public class MixinGlDevice { 13 | @Inject(method = "", at = @At(value = "INVOKE", target = "Lorg/lwjgl/opengl/GL;createCapabilities()Lorg/lwjgl/opengl/GLCapabilities;", shift = At.Shift.AFTER), remap = false) 14 | private void init(long contextId, int debugVerbosity, boolean sync, BiFunction shaderSourceGetter, boolean renderDebugLabels, CallbackInfo ci) { 15 | Nvidium.checkSystemIsCapable(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/me/cortex/nvidium/renderers/SortRegionSectionPhase.java: -------------------------------------------------------------------------------- 1 | package me.cortex.nvidium.renderers; 2 | 3 | import me.cortex.nvidium.gl.shader.Shader; 4 | import me.cortex.nvidium.sodiumCompat.ShaderLoader; 5 | import net.minecraft.resources.ResourceLocation; 6 | 7 | import static me.cortex.nvidium.gl.shader.ShaderType.*; 8 | import static org.lwjgl.opengl.GL43C.glDispatchCompute; 9 | 10 | public class SortRegionSectionPhase extends Phase { 11 | private final Shader shader = Shader.make() 12 | .addSource(COMPUTE, ShaderLoader.parse(ResourceLocation.fromNamespaceAndPath("nvidium", "sorting/region_section_sorter.comp"))) 13 | .compile(); 14 | 15 | public SortRegionSectionPhase() { 16 | } 17 | 18 | public void dispatch(int sortingRegionCount) { 19 | shader.bind(); 20 | glDispatchCompute(sortingRegionCount, 1, 1); 21 | } 22 | 23 | public void delete() { 24 | shader.delete(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/me/cortex/nvidium/mixin/sodium/MixinRenderSection.java: -------------------------------------------------------------------------------- 1 | package me.cortex.nvidium.mixin.sodium; 2 | 3 | import me.cortex.nvidium.sodiumCompat.IRenderSectionExtension; 4 | import net.caffeinemc.mods.sodium.client.render.chunk.RenderSection; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.Unique; 7 | 8 | @Mixin(value = RenderSection.class, remap = false) 9 | public class MixinRenderSection implements IRenderSectionExtension { 10 | @Unique private volatile boolean isEnqueued; 11 | @Unique private volatile boolean isSeen; 12 | 13 | @Override 14 | public boolean isSubmittedRebuild() { 15 | return isEnqueued; 16 | } 17 | 18 | @Override 19 | public void isSubmittedRebuild(boolean state) { 20 | isEnqueued = state; 21 | } 22 | 23 | @Override 24 | public boolean isSeen() { 25 | return isSeen; 26 | } 27 | 28 | @Override 29 | public void isSeen(boolean state) { 30 | isSeen = state; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/me/cortex/nvidium/gl/GlFence.java: -------------------------------------------------------------------------------- 1 | package me.cortex.nvidium.gl; 2 | 3 | import static org.lwjgl.opengl.GL32.*; 4 | 5 | public class GlFence extends TrackedObject { 6 | private final long fence; 7 | private boolean signaled; 8 | 9 | public GlFence() { 10 | this.fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); 11 | } 12 | 13 | public boolean signaled() { 14 | this.assertNotFreed(); 15 | if (!this.signaled) { 16 | int ret = glClientWaitSync(this.fence, 0, 0); 17 | if (ret == GL_ALREADY_SIGNALED || ret == GL_CONDITION_SATISFIED) { 18 | this.signaled = true; 19 | } else if (ret != GL_TIMEOUT_EXPIRED) { 20 | throw new IllegalStateException("Poll for fence failed, ret: " + ret + " glError: " + glGetError()); 21 | } 22 | } 23 | return this.signaled; 24 | } 25 | 26 | @Override 27 | public void free() { 28 | super.free0(); 29 | glDeleteSync(this.fence); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/me/cortex/nvidium/renderers/RegionRasterizer.java: -------------------------------------------------------------------------------- 1 | package me.cortex.nvidium.renderers; 2 | 3 | import me.cortex.nvidium.gl.shader.Shader; 4 | import me.cortex.nvidium.sodiumCompat.ShaderLoader; 5 | import net.minecraft.resources.ResourceLocation; 6 | 7 | import static me.cortex.nvidium.gl.shader.ShaderType.FRAGMENT; 8 | import static me.cortex.nvidium.gl.shader.ShaderType.MESH; 9 | import static org.lwjgl.opengl.NVMeshShader.glDrawMeshTasksNV; 10 | 11 | public class RegionRasterizer extends Phase { 12 | private final Shader shader = Shader.make() 13 | .addSource(MESH, ShaderLoader.parse(ResourceLocation.fromNamespaceAndPath("nvidium", "occlusion/region_raster/mesh.glsl"))) 14 | .addSource(FRAGMENT, ShaderLoader.parse(ResourceLocation.fromNamespaceAndPath("nvidium", "occlusion/region_raster/fragment.frag"))) 15 | .compile(); 16 | 17 | public void raster(int regionCount) { 18 | shader.bind(); 19 | glDrawMeshTasksNV(0,regionCount); 20 | } 21 | 22 | public void delete() { 23 | shader.delete(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/me/cortex/nvidium/renderers/SectionRasterizer.java: -------------------------------------------------------------------------------- 1 | package me.cortex.nvidium.renderers; 2 | 3 | import me.cortex.nvidium.gl.shader.Shader; 4 | import me.cortex.nvidium.sodiumCompat.ShaderLoader; 5 | import net.minecraft.resources.ResourceLocation; 6 | 7 | import static me.cortex.nvidium.gl.shader.ShaderType.*; 8 | import static org.lwjgl.opengl.NVMeshShader.glDrawMeshTasksNV; 9 | 10 | public class SectionRasterizer extends Phase { 11 | 12 | private final Shader shader = Shader.make() 13 | .addSource(TASK, ShaderLoader.parse(ResourceLocation.fromNamespaceAndPath("nvidium", "occlusion/section_raster/task.glsl"))) 14 | .addSource(MESH, ShaderLoader.parse(ResourceLocation.fromNamespaceAndPath("nvidium", "occlusion/section_raster/mesh.glsl"))) 15 | .addSource(FRAGMENT, ShaderLoader.parse(ResourceLocation.fromNamespaceAndPath("nvidium", "occlusion/section_raster/fragment.glsl"))).compile(); 16 | 17 | public void raster(int regionCount) { 18 | shader.bind(); 19 | glDrawMeshTasksNV(0,regionCount); 20 | } 21 | 22 | public void delete() { 23 | shader.delete(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/me/cortex/nvidium/mixin/sodium/MixinOptionFlag.java: -------------------------------------------------------------------------------- 1 | package me.cortex.nvidium.mixin.sodium; 2 | 3 | import me.cortex.nvidium.sodiumCompat.NvidiumOptionFlags; 4 | import net.caffeinemc.mods.sodium.client.gui.options.OptionFlag; 5 | import org.apache.commons.lang3.ArrayUtils; 6 | import org.spongepowered.asm.mixin.Final; 7 | import org.spongepowered.asm.mixin.Mixin; 8 | import org.spongepowered.asm.mixin.Mutable; 9 | import org.spongepowered.asm.mixin.Shadow; 10 | import org.spongepowered.asm.mixin.gen.Invoker; 11 | 12 | @Mixin(value = OptionFlag.class, remap = false) 13 | public class MixinOptionFlag { 14 | @Shadow 15 | @Final 16 | @Mutable 17 | private static OptionFlag[] $VALUES = ArrayUtils.addAll(MixinOptionFlag.$VALUES, NvidiumOptionFlags.REQUIRES_SHADER_RELOAD); 18 | 19 | public MixinOptionFlag() { 20 | } 21 | 22 | @Invoker("") 23 | public static OptionFlag optionFlagCreator(String internalName, int internalId) { 24 | throw new AssertionError(); 25 | } 26 | 27 | static { 28 | NvidiumOptionFlags.REQUIRES_SHADER_RELOAD = optionFlagCreator("REQUIRES_SHADER_RELOAD", $VALUES.length); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/resources/assets/nvidium/shaders/terrain/vertex_format/nvidium_vertex_format.glsl: -------------------------------------------------------------------------------- 1 | #define MODEL_SCALE 32.0 / 65536.0 2 | #define MODEL_ORIGIN 8.0 3 | 4 | vec3 decodeVertexPosition(Vertex v) { 5 | uvec3 packed_position = uvec3( 6 | (v.x >> 0) & 0xFFFFu, 7 | (v.x >> 16) & 0xFFFFu, 8 | (v.y >> 0) & 0xFFFFu 9 | ); 10 | 11 | return (vec3(packed_position) * MODEL_SCALE) - MODEL_ORIGIN; 12 | } 13 | 14 | vec4 decodeVertexColour(Vertex v) { 15 | uvec3 packed_color = (uvec3(v.z) >> uvec3(0, 8, 16)) & uvec3(0xFFu); 16 | return vec4(vec3(packed_color) * COLOR_SCALE, 1); 17 | } 18 | 19 | vec2 decodeVertexUV(Vertex v) { 20 | return vec2(v.w&0xffff,v.w>>16)*(1.0f/(TEXTURE_MAX_SCALE)); 21 | } 22 | 23 | bool hasMipping(Vertex v) { 24 | return ((v.y>>16)&1)!=0; 25 | } 26 | 27 | float decodeVertexAlphaCutoff(Vertex v) { 28 | return (float[](0.0f, 0.1f,0.5f))[((v.y>>16)&int16_t(3))]; 29 | } 30 | 31 | uint rawVertexAlphaCutoff(Vertex v) { 32 | return uint((v.y>>17)&int16_t(3)); 33 | } 34 | 35 | vec2 decodeLightUV(Vertex v) { 36 | uvec2 light = uvec2(v.y>>24, v.z>>24) & uvec2(0xFFu); 37 | return vec2(light)/256.0; 38 | } -------------------------------------------------------------------------------- /src/main/java/me/cortex/nvidium/mixin/sodium/MixinChunkBuildOutput.java: -------------------------------------------------------------------------------- 1 | package me.cortex.nvidium.mixin.sodium; 2 | 3 | import me.cortex.nvidium.sodiumCompat.IRepackagedResult; 4 | import me.cortex.nvidium.sodiumCompat.RepackagedSectionOutput; 5 | import net.caffeinemc.mods.sodium.client.render.chunk.compile.ChunkBuildOutput; 6 | import org.spongepowered.asm.mixin.Mixin; 7 | import org.spongepowered.asm.mixin.Unique; 8 | import org.spongepowered.asm.mixin.injection.At; 9 | import org.spongepowered.asm.mixin.injection.Inject; 10 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 11 | 12 | @Mixin(value = ChunkBuildOutput.class, remap = false) 13 | public class MixinChunkBuildOutput implements IRepackagedResult { 14 | @Unique private RepackagedSectionOutput repackagedSectionOutput; 15 | 16 | public RepackagedSectionOutput getOutput() { 17 | return repackagedSectionOutput; 18 | } 19 | 20 | public void set(RepackagedSectionOutput output) { 21 | repackagedSectionOutput = output; 22 | } 23 | 24 | @Inject(method = "destroy", at = @At("HEAD")) 25 | private void cleanup(CallbackInfo ci) { 26 | if (repackagedSectionOutput != null) { 27 | repackagedSectionOutput.delete(); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/me/cortex/nvidium/util/TickableManager.java: -------------------------------------------------------------------------------- 1 | package me.cortex.nvidium.util; 2 | 3 | import java.lang.ref.WeakReference; 4 | import java.util.Iterator; 5 | import java.util.LinkedHashSet; 6 | import java.util.LinkedList; 7 | import java.util.List; 8 | import java.util.Set; 9 | 10 | public class TickableManager { 11 | private static final Set UPLOADERS = new LinkedHashSet<>(); 12 | private static final Set DOWNLOADERS = new LinkedHashSet<>(); 13 | public static void register(UploadingBufferStream stream) { 14 | UPLOADERS.add(stream); 15 | } 16 | public static void register(DownloadTaskStream stream) { 17 | DOWNLOADERS.add(stream); 18 | } 19 | public static void remove(UploadingBufferStream stream) { 20 | UPLOADERS.remove(stream); 21 | } 22 | public static void remove(DownloadTaskStream stream) { 23 | DOWNLOADERS.remove(stream); 24 | } 25 | 26 | public static void TickAll() {//Should be called at the very end of the frame 27 | var iter = UPLOADERS.iterator(); 28 | while (iter.hasNext()) { 29 | iter.next().tick(); 30 | } 31 | 32 | var iter2 = DOWNLOADERS.iterator(); 33 | while (iter2.hasNext()) { 34 | iter2.next().tick(); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/me/cortex/nvidium/gl/RenderDevice.java: -------------------------------------------------------------------------------- 1 | package me.cortex.nvidium.gl; 2 | 3 | import me.cortex.nvidium.gl.buffers.*; 4 | 5 | import static org.lwjgl.opengl.ARBDirectStateAccess.glCopyNamedBufferSubData; 6 | import static org.lwjgl.opengl.ARBDirectStateAccess.glFlushMappedNamedBufferRange; 7 | import static org.lwjgl.opengl.GL15C.glIsBuffer; 8 | import static org.lwjgl.opengl.GL42C.glMemoryBarrier; 9 | 10 | public class RenderDevice { 11 | public PersistentClientMappedBuffer createClientMappedBuffer(long size) { 12 | return new PersistentClientMappedBuffer(size); 13 | } 14 | 15 | public void flush(IClientMappedBuffer buffer, long offset, int size) { 16 | int id = ((GlObject)buffer).getId(); 17 | glFlushMappedNamedBufferRange(id, offset, size); 18 | } 19 | 20 | public void barrier(int flags) { 21 | glMemoryBarrier(flags); 22 | } 23 | 24 | public void copyBuffer(Buffer src, Buffer dst, long srcOffset, long dstOffset, long size) { 25 | glCopyNamedBufferSubData(((GlObject)src).getId(), ((GlObject)dst).getId(), srcOffset, dstOffset, size); 26 | } 27 | 28 | public PersistentSparseAddressableBuffer createSparseBuffer(long totalSize) { 29 | return new PersistentSparseAddressableBuffer(totalSize); 30 | } 31 | 32 | public IDeviceMappedBuffer createDeviceOnlyMappedBuffer(long size) { 33 | return new DeviceOnlyMappedBuffer(size); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/me/cortex/nvidium/gl/buffers/PersistentClientMappedBuffer.java: -------------------------------------------------------------------------------- 1 | package me.cortex.nvidium.gl.buffers; 2 | 3 | 4 | import me.cortex.nvidium.gl.GlObject; 5 | 6 | import static org.lwjgl.opengl.ARBDirectStateAccess.*; 7 | import static org.lwjgl.opengl.GL30C.*; 8 | import static org.lwjgl.opengl.GL44.GL_CLIENT_STORAGE_BIT; 9 | import static org.lwjgl.opengl.GL44.GL_MAP_PERSISTENT_BIT; 10 | import static org.lwjgl.opengl.NVShaderBufferLoad.*; 11 | 12 | public class PersistentClientMappedBuffer extends GlObject implements IClientMappedBuffer { 13 | public final long addr; 14 | public final long size; 15 | 16 | public PersistentClientMappedBuffer(long size) { 17 | super(glCreateBuffers()); 18 | this.size = size; 19 | glNamedBufferStorage(id, size, GL_MAP_PERSISTENT_BIT| (GL_CLIENT_STORAGE_BIT|GL_MAP_WRITE_BIT)); 20 | addr = nglMapNamedBufferRange(id, 0, size, GL_MAP_PERSISTENT_BIT|(GL_MAP_UNSYNCHRONIZED_BIT|GL_MAP_FLUSH_EXPLICIT_BIT|GL_MAP_WRITE_BIT)); 21 | } 22 | 23 | @Override 24 | public long clientAddress() { 25 | return addr; 26 | } 27 | 28 | @Override 29 | public void delete() { 30 | super.free0(); 31 | glUnmapNamedBuffer(id); 32 | glDeleteBuffers(id); 33 | } 34 | 35 | @Override 36 | public void free() { 37 | this.delete(); 38 | } 39 | 40 | @Override 41 | public long getSize() { 42 | return size; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/me/cortex/nvidium/mixin/minecraft/MixinFogRenderer.java: -------------------------------------------------------------------------------- 1 | package me.cortex.nvidium.mixin.minecraft; 2 | 3 | import me.cortex.nvidium.Nvidium; 4 | import net.minecraft.client.renderer.fog.FogRenderer; 5 | import net.minecraft.util.Mth; 6 | import org.spongepowered.asm.mixin.Mixin; 7 | import org.spongepowered.asm.mixin.injection.*; 8 | 9 | @Mixin(FogRenderer.class) 10 | public class MixinFogRenderer { 11 | @ModifyVariable( 12 | method = "setupFog(Lnet/minecraft/client/Camera;IZLnet/minecraft/client/DeltaTracker;FLnet/minecraft/client/multiplayer/ClientLevel;)Lorg/joml/Vector4f;", 13 | at = @At(value = "STORE"), 14 | ordinal = 2 15 | ) 16 | private float modifyFogRD(float viewDistance) { 17 | if (Nvidium.IS_ENABLED && Nvidium.config.region_keep_distance != 32) { 18 | return Math.max(viewDistance, Nvidium.config.region_keep_distance * 16); 19 | } 20 | return viewDistance; 21 | } 22 | 23 | @ModifyArg( 24 | method = "setupFog(Lnet/minecraft/client/Camera;IZLnet/minecraft/client/DeltaTracker;FLnet/minecraft/client/multiplayer/ClientLevel;)Lorg/joml/Vector4f;", 25 | at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/fog/FogRenderer;updateBuffer(Ljava/nio/ByteBuffer;ILorg/joml/Vector4f;FFFFFF)V"), 26 | index = 7 27 | ) 28 | private float clampSkyEnd(float skyEnd) { 29 | return Mth.clamp(skyEnd, 2 * 16, 32 * 16); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/me/cortex/nvidium/gl/buffers/DeviceOnlyMappedBuffer.java: -------------------------------------------------------------------------------- 1 | package me.cortex.nvidium.gl.buffers; 2 | 3 | 4 | import me.cortex.nvidium.gl.GlObject; 5 | 6 | import static org.lwjgl.opengl.ARBDirectStateAccess.glCreateBuffers; 7 | import static org.lwjgl.opengl.ARBDirectStateAccess.glNamedBufferStorage; 8 | import static org.lwjgl.opengl.GL15C.GL_READ_WRITE; 9 | import static org.lwjgl.opengl.GL15C.glDeleteBuffers; 10 | import static org.lwjgl.opengl.NVShaderBufferLoad.*; 11 | 12 | public class DeviceOnlyMappedBuffer extends GlObject implements IDeviceMappedBuffer { 13 | public final long size; 14 | public final long addr; 15 | public DeviceOnlyMappedBuffer(long size) { 16 | super(glCreateBuffers()); 17 | this.size = size; 18 | glNamedBufferStorage(id, size, 0); 19 | long[] holder = new long[1]; 20 | glGetNamedBufferParameterui64vNV(id, GL_BUFFER_GPU_ADDRESS_NV, holder); 21 | glMakeNamedBufferResidentNV(id, GL_READ_WRITE); 22 | addr = holder[0]; 23 | if (addr == 0) { 24 | throw new IllegalStateException(); 25 | } 26 | } 27 | 28 | @Override 29 | public void delete() { 30 | super.free0(); 31 | glMakeNamedBufferNonResidentNV(id); 32 | glDeleteBuffers(id); 33 | } 34 | 35 | @Override 36 | public long getDeviceAddress() { 37 | return addr; 38 | } 39 | 40 | @Override 41 | public void free() { 42 | this.delete(); 43 | } 44 | 45 | @Override 46 | public long getSize() { 47 | return this.size; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/me/cortex/nvidium/gl/images/DepthOnlyFrameBuffer.java: -------------------------------------------------------------------------------- 1 | package me.cortex.nvidium.gl.images; 2 | 3 | import com.mojang.blaze3d.opengl.GlConst; 4 | import com.mojang.blaze3d.opengl.GlStateManager; 5 | 6 | import static org.lwjgl.opengl.ARBDirectStateAccess.*; 7 | import static org.lwjgl.opengl.GL11C.GL_TEXTURE_2D; 8 | import static org.lwjgl.opengl.GL11C.glDeleteTextures; 9 | import static org.lwjgl.opengl.GL30C.*; 10 | 11 | public class DepthOnlyFrameBuffer { 12 | public final int width; 13 | public final int height; 14 | private final int fid; 15 | private final int did; 16 | public DepthOnlyFrameBuffer(int width, int height) { 17 | this.width = width; 18 | this.height = height; 19 | fid = glCreateFramebuffers(); 20 | did = glCreateTextures(GL_TEXTURE_2D); 21 | glTextureStorage2D(did, 1, GL_DEPTH_COMPONENT32F, width, height); 22 | glNamedFramebufferTexture(fid, GL_DEPTH_ATTACHMENT, did, 0); 23 | if (glCheckNamedFramebufferStatus(fid, GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { 24 | throw new IllegalStateException("ERROR: " + glCheckFramebufferStatus(GL_FRAMEBUFFER)); 25 | } 26 | } 27 | 28 | public int getDepthBuffer() { 29 | return did; 30 | } 31 | 32 | public void bind(boolean setViewport) { 33 | GlStateManager._glBindFramebuffer(GlConst.GL_FRAMEBUFFER, fid); 34 | if (setViewport) { 35 | GlStateManager._viewport(0, 0, width, height); 36 | } 37 | } 38 | 39 | public void delete() { 40 | glDeleteFramebuffers(fid); 41 | glDeleteTextures(did); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/me/cortex/nvidium/mixin/sodium/MixinChunkBuilderMeshingTask.java: -------------------------------------------------------------------------------- 1 | package me.cortex.nvidium.mixin.sodium; 2 | 3 | import me.cortex.nvidium.Nvidium; 4 | import me.cortex.nvidium.sodiumCompat.IRepackagedResult; 5 | import me.cortex.nvidium.sodiumCompat.SodiumResultCompatibility; 6 | import net.caffeinemc.mods.sodium.client.render.chunk.compile.ChunkBuildContext; 7 | import net.caffeinemc.mods.sodium.client.render.chunk.compile.ChunkBuildOutput; 8 | import net.caffeinemc.mods.sodium.client.render.chunk.compile.tasks.ChunkBuilderMeshingTask; 9 | import net.caffeinemc.mods.sodium.client.util.task.CancellationToken; 10 | import org.spongepowered.asm.mixin.Mixin; 11 | import org.spongepowered.asm.mixin.injection.At; 12 | import org.spongepowered.asm.mixin.injection.Inject; 13 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 14 | 15 | @Mixin(value = ChunkBuilderMeshingTask.class, remap = false) 16 | public class MixinChunkBuilderMeshingTask { 17 | @Inject(method = "execute(Lnet/caffeinemc/mods/sodium/client/render/chunk/compile/ChunkBuildContext;Lnet/caffeinemc/mods/sodium/client/util/task/CancellationToken;)Lnet/caffeinemc/mods/sodium/client/render/chunk/compile/ChunkBuildOutput;", at = @At("TAIL")) 18 | private void repackageResults(ChunkBuildContext buildContext, CancellationToken cancellationToken, CallbackInfoReturnable cir) { 19 | if (Nvidium.IS_ENABLED) { 20 | var result = cir.getReturnValue(); 21 | if (result != null) { 22 | ((IRepackagedResult) result).set(SodiumResultCompatibility.repackage(result)); 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/resources/assets/nvidium/shaders/terrain/vertex_format/sodium_vertex_format.glsl: -------------------------------------------------------------------------------- 1 | const uint POSITION_BITS = 20u; 2 | const uint POSITION_MAX_COORD = 1u << POSITION_BITS; 3 | const uint POSITION_MAX_VALUE = POSITION_MAX_COORD - 1u; 4 | 5 | const uint TEXTURE_BITS = 15u; 6 | const uint TEXTURE_MAX_COORD = 1u << TEXTURE_BITS; 7 | const uint TEXTURE_MAX_VALUE = TEXTURE_MAX_COORD - 1u; 8 | 9 | const float VERTEX_SCALE = 32.0 / float(POSITION_MAX_COORD); 10 | const float VERTEX_OFFSET = -8.0; 11 | 12 | uvec3 _deinterleave_u20x3(Vertex v) { 13 | uvec3 hi = (uvec3(v.hi) >> uvec3(0u, 10u, 20u)) & 0x3FFu; 14 | uvec3 lo = (uvec3(v.lo) >> uvec3(0u, 10u, 20u)) & 0x3FFu; 15 | 16 | return (hi << 10u) | lo; 17 | } 18 | 19 | vec3 decodeVertexPosition(Vertex v) { 20 | return (_deinterleave_u20x3(v) * VERTEX_SCALE) + VERTEX_OFFSET; 21 | } 22 | 23 | vec2 decodeVertexRawUV(Vertex v) { 24 | return vec2(v.u & TEXTURE_MAX_VALUE, v.v & TEXTURE_MAX_VALUE) / float(TEXTURE_MAX_COORD); 25 | } 26 | 27 | vec2 decodeVertexUVBias(Vertex v) { 28 | return mix(vec2(-1.0), vec2(1.0), bvec2(uvec2(v.u, v.v) >> TEXTURE_BITS)); 29 | } 30 | 31 | vec2 decodeVertexUV(Vertex v) { 32 | return (decodeVertexUVBias(v) * texCoordShrink) + decodeVertexRawUV(v); 33 | } 34 | 35 | vec2 decodeLightUV(Vertex v) { 36 | return vec2(v.blockLight, v.skyLight)/256.0; 37 | } 38 | 39 | bool hasMipping(Vertex v) { 40 | return bool(int(v.material) & 1); 41 | } 42 | 43 | uint rawVertexAlphaCutoff(Vertex v) { 44 | return (int(v.material) >> 1) & 3; 45 | } 46 | 47 | vec4 decodeVertexColour(Vertex v) { 48 | uvec3 packed_color = (uvec3(v.color) >> uvec3(0, 8, 16)) & uvec3(0xFFu); 49 | return vec4(vec3(packed_color) * COLOR_SCALE, 1); 50 | } -------------------------------------------------------------------------------- /src/main/resources/assets/nvidium/shaders/terrain/task.glsl: -------------------------------------------------------------------------------- 1 | #version 460 2 | 3 | #extension GL_ARB_shading_language_include : enable 4 | #pragma optionNV(unroll all) 5 | #define UNROLL_LOOP 6 | #extension GL_NV_mesh_shader : require 7 | #extension GL_NV_gpu_shader5 : require 8 | #extension GL_NV_bindless_texture : require 9 | 10 | #extension GL_KHR_shader_subgroup_basic : require 11 | #extension GL_KHR_shader_subgroup_ballot : require 12 | #extension GL_KHR_shader_subgroup_vote : require 13 | 14 | #import 15 | 16 | 17 | //This is 1 since each task shader workgroup -> multiple meshlets. its not each globalInvocation (afaik) 18 | layout(local_size_x=1) in; 19 | 20 | bool shouldRenderVisible(uint sectionId) { 21 | return (sectionVisibility[sectionId]&uint8_t(1)) != uint8_t(0); 22 | } 23 | 24 | #import 25 | 26 | void main() { 27 | uint sectionId = gl_WorkGroupID.x; 28 | 29 | if (!shouldRenderVisible(sectionId)) { 30 | //Early exit if the section isnt visible 31 | gl_TaskCountNV = 0; 32 | return; 33 | } 34 | 35 | #ifdef STATISTICS_SECTIONS 36 | atomicAdd(statistics_buffer+1, 1); 37 | #endif 38 | 39 | ivec4 header = sectionData[sectionId].header; 40 | ivec3 chunk = ivec3(header.xyz)>>8; 41 | chunk.y &= 0x1ff; 42 | chunk.y <<= 32-9; 43 | chunk.y >>= 32-9; 44 | chunk -= chunkPosition.xyz; 45 | transformationId = unpackRegionTransformId(regionData[sectionId>>8]); 46 | chunk -= unpackOriginOffsetId(transformationId); 47 | 48 | origin = vec3(chunk<<4); 49 | populateTasks(chunk, uint(header.w), uvec4(sectionData[sectionId].renderRanges)); 50 | 51 | 52 | #ifdef STATISTICS_QUADS 53 | atomicAdd(statistics_buffer+2, quadCount); 54 | #endif 55 | } -------------------------------------------------------------------------------- /src/main/resources/assets/nvidium/shaders/terrain/temporal_task.glsl: -------------------------------------------------------------------------------- 1 | #version 460 2 | //Temporal task shader 3 | #extension GL_ARB_shading_language_include : enable 4 | #pragma optionNV(unroll all) 5 | #define UNROLL_LOOP 6 | #extension GL_NV_mesh_shader : require 7 | #extension GL_NV_gpu_shader5 : require 8 | #extension GL_NV_bindless_texture : require 9 | 10 | #extension GL_KHR_shader_subgroup_basic : require 11 | #extension GL_KHR_shader_subgroup_ballot : require 12 | #extension GL_KHR_shader_subgroup_vote : require 13 | 14 | #import 15 | 16 | 17 | //This is 1 since each task shader workgroup -> multiple meshlets. its not each globalInvocation (afaik) 18 | layout(local_size_x=1) in; 19 | 20 | 21 | 22 | 23 | bool shouldRenderVisible(uint sectionId) { 24 | uint8_t data = sectionVisibility[sectionId]; 25 | return (data&uint8_t(3)) == uint8_t(1);//If the section was not visible last frame but is visible this frame, render it 26 | } 27 | 28 | #import 29 | 30 | void main() { 31 | uint sectionId = gl_WorkGroupID.x; 32 | 33 | if (!shouldRenderVisible(sectionId)) { 34 | //Early exit if the section isnt visible 35 | gl_TaskCountNV = 0; 36 | return; 37 | } 38 | 39 | ivec4 header = sectionData[sectionId].header; 40 | ivec3 chunk = ivec3(header.xyz)>>8; 41 | chunk.y &= 0x1ff; 42 | chunk.y <<= 32-9; 43 | chunk.y >>= 32-9; 44 | chunk -= chunkPosition.xyz; 45 | 46 | transformationId = unpackRegionTransformId(regionData[sectionId>>8]); 47 | chunk -= unpackOriginOffsetId(transformationId); 48 | 49 | origin = vec3(chunk<<4); 50 | 51 | populateTasks(chunk, uint(header.w), uvec4(sectionData[sectionId].renderRanges)); 52 | 53 | #ifdef STATISTICS_QUADS 54 | atomicAdd(statistics_buffer+2, quadCount); 55 | #endif 56 | } -------------------------------------------------------------------------------- /src/main/java/me/cortex/nvidium/gl/TrackedObject.java: -------------------------------------------------------------------------------- 1 | package me.cortex.nvidium.gl; 2 | 3 | import me.cortex.nvidium.Nvidium; 4 | 5 | import java.lang.ref.Cleaner; 6 | import java.lang.ref.Cleaner.Cleanable; 7 | 8 | public abstract class TrackedObject { 9 | private final Ref ref; 10 | public TrackedObject() { 11 | this.ref = register(this); 12 | } 13 | 14 | protected void free0() { 15 | if (this.isFreed()) { 16 | throw new IllegalStateException("Object " + this + " was double freed."); 17 | } 18 | this.ref.freedRef[0] = true; 19 | this.ref.cleanable.clean(); 20 | } 21 | 22 | public abstract void free(); 23 | 24 | public void assertNotFreed() { 25 | if (isFreed()) { 26 | throw new IllegalStateException("Object " + this + " should not be free, but is"); 27 | } 28 | } 29 | 30 | public boolean isFreed() { 31 | return this.ref.freedRef[0]; 32 | } 33 | 34 | public record Ref(Cleaner.Cleanable cleanable, boolean[] freedRef) {} 35 | 36 | private static final Cleaner cleaner = Cleaner.create(); 37 | public static Ref register(Object obj) { 38 | String clazz = obj.getClass().getName(); 39 | Throwable trace; 40 | if (Nvidium.IS_DEBUG) { 41 | trace = new Throwable(); 42 | } else { 43 | trace = null; 44 | } 45 | boolean[] freed = new boolean[1]; 46 | var clean = cleaner.register(obj, ()->{ 47 | if (!freed[0]) { 48 | System.err.println("Object named: "+ clazz+" was not freed, location at:\n"); 49 | if (trace != null) { 50 | trace.printStackTrace(); 51 | } else { 52 | System.err.println("Unknown location, turn on debug mode"); 53 | } 54 | System.err.flush(); 55 | } 56 | }); 57 | return new Ref(clean, freed); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/resources/assets/nvidium/shaders/sorting/sorting_network.glsl: -------------------------------------------------------------------------------- 1 | #ifndef SORTING_INDEX_TYPE 2 | #define SORTING_INDEX_TYPE uint8_t 3 | #endif 4 | 5 | shared float threadBufferFloat[SORTING_NETWORK_SIZE]; 6 | shared SORTING_INDEX_TYPE threadBufferIndex[SORTING_NETWORK_SIZE]; 7 | 8 | void localSortA(const uint scaleBits) { 9 | uint base = (gl_LocalInvocationID.x>>scaleBits)*(1<<(scaleBits+1)); 10 | uint offsetA = (gl_LocalInvocationID.x&((1<>scaleBits)*(1<<(scaleBits+1)); 29 | base += (gl_LocalInvocationID.x&((1<>16; 43 | uvec3 cA = uvec3(cA_.x,cB_.x,cA_.y); 44 | uvec3 cB = uvec3(cB_.y,cA_.z,cB_.z); 45 | 46 | bvec3 a = and(notEqual(cA, uvec3(0)), lessThanEqual(relative, ivec3(0))); 47 | bvec3 b = and(notEqual(cB, uvec3(0)), lessThanEqual(ivec3(0), relative)); 48 | 49 | 50 | uvec4 starts; 51 | uvec4 offsets; 52 | uint sum; 53 | //Note: the + baseOffset here is a cheaky thing which means dont need to add or pass on 54 | uint ci = buildBinData(starts, offsets, sum, (ranges.w>>16)+baseOffset, ranges.w&0xFFFFu, a, b, cA, cB); 55 | 56 | binStarts = starts; 57 | binOffsets = offsets-starts;//Make it a delta from start 58 | 59 | quadCount = sum; 60 | //Emit enough mesh shaders such that max(gl_GlobalInvocationID.x)>=2*quadCount 61 | gl_TaskCountNV = ((sum*2)+MESH_WORKLOAD_PER_INVOCATION-1)/MESH_WORKLOAD_PER_INVOCATION; 62 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # User-specific stuff 2 | .idea/ 3 | 4 | *.iml 5 | *.ipr 6 | *.iws 7 | 8 | # IntelliJ 9 | out/ 10 | # mpeltonen/sbt-idea plugin 11 | .idea_modules/ 12 | 13 | # JIRA plugin 14 | atlassian-ide-plugin.xml 15 | 16 | # Compiled class file 17 | *.class 18 | 19 | # Log file 20 | *.log 21 | 22 | # BlueJ files 23 | *.ctxt 24 | 25 | # Package Files # 26 | *.jar 27 | *.war 28 | *.nar 29 | *.ear 30 | *.zip 31 | *.tar.gz 32 | *.rar 33 | 34 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 35 | hs_err_pid* 36 | 37 | *~ 38 | 39 | # temporary files which can be created if a process still has a handle open of a deleted file 40 | .fuse_hidden* 41 | 42 | # KDE directory preferences 43 | .directory 44 | 45 | # Linux trash folder which might appear on any partition or disk 46 | .Trash-* 47 | 48 | # .nfs files are created when an open file is removed but is still being accessed 49 | .nfs* 50 | 51 | # General 52 | .DS_Store 53 | .AppleDouble 54 | .LSOverride 55 | 56 | # Icon must end with two \r 57 | Icon 58 | 59 | # Thumbnails 60 | ._* 61 | 62 | # Files that might appear in the root of a volume 63 | .DocumentRevisions-V100 64 | .fseventsd 65 | .Spotlight-V100 66 | .TemporaryItems 67 | .Trashes 68 | .VolumeIcon.icns 69 | .com.apple.timemachine.donotpresent 70 | 71 | # Directories potentially created on remote AFP share 72 | .AppleDB 73 | .AppleDesktop 74 | Network Trash Folder 75 | Temporary Items 76 | .apdisk 77 | 78 | # Windows thumbnail cache files 79 | Thumbs.db 80 | Thumbs.db:encryptable 81 | ehthumbs.db 82 | ehthumbs_vista.db 83 | 84 | # Dump file 85 | *.stackdump 86 | 87 | # Folder config file 88 | [Dd]esktop.ini 89 | 90 | # Recycle Bin used on file shares 91 | $RECYCLE.BIN/ 92 | 93 | # Windows Installer files 94 | *.cab 95 | *.msi 96 | *.msix 97 | *.msm 98 | *.msp 99 | 100 | # Windows shortcuts 101 | *.lnk 102 | 103 | .gradle 104 | build/ 105 | 106 | # Ignore Gradle GUI config 107 | gradle-app.setting 108 | 109 | # Cache of project 110 | .gradletasknamecache 111 | 112 | **/build/ 113 | 114 | # Common working directory 115 | run/ 116 | 117 | # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) 118 | !gradle-wrapper.jar 119 | -------------------------------------------------------------------------------- /src/main/java/me/cortex/nvidium/mixin/sodium/MixinRenderRegionManager.java: -------------------------------------------------------------------------------- 1 | package me.cortex.nvidium.mixin.sodium; 2 | 3 | import me.cortex.nvidium.Nvidium; 4 | import me.cortex.nvidium.NvidiumWorldRenderer; 5 | import me.cortex.nvidium.sodiumCompat.INvidiumWorldRendererSetter; 6 | import net.caffeinemc.mods.sodium.client.gl.device.CommandList; 7 | import net.caffeinemc.mods.sodium.client.render.chunk.compile.BuilderTaskOutput; 8 | import net.caffeinemc.mods.sodium.client.render.chunk.region.RenderRegion; 9 | import net.caffeinemc.mods.sodium.client.render.chunk.region.RenderRegionManager; 10 | import org.spongepowered.asm.mixin.Mixin; 11 | import org.spongepowered.asm.mixin.Shadow; 12 | import org.spongepowered.asm.mixin.Unique; 13 | import org.spongepowered.asm.mixin.injection.At; 14 | import org.spongepowered.asm.mixin.injection.Redirect; 15 | 16 | import java.util.Collection; 17 | 18 | import static me.cortex.nvidium.Nvidium.LOGGER; 19 | 20 | @Mixin(value = RenderRegionManager.class, remap = false) 21 | public abstract class MixinRenderRegionManager implements INvidiumWorldRendererSetter { 22 | @Unique private NvidiumWorldRenderer renderer; 23 | 24 | @Shadow 25 | protected abstract void uploadResults(CommandList commandList, RenderRegion region, Collection results); 26 | 27 | @Redirect(method = "uploadResults(Lnet/caffeinemc/mods/sodium/client/gl/device/CommandList;Ljava/util/Collection;)V", 28 | at = @At(value = "INVOKE", 29 | target = "Lnet/caffeinemc/mods/sodium/client/render/chunk/region/RenderRegionManager;uploadResults(Lnet/caffeinemc/mods/sodium/client/gl/device/CommandList;Lnet/caffeinemc/mods/sodium/client/render/chunk/region/RenderRegion;Ljava/util/Collection;)V")) 30 | private void redirectUpload(RenderRegionManager instance, CommandList cmdList, RenderRegion pass, Collection results) { 31 | if (Nvidium.IS_ENABLED) { 32 | for (BuilderTaskOutput result : results) { 33 | renderer.uploadBuildResult(result); 34 | } 35 | } else { 36 | uploadResults(cmdList, pass, results); 37 | } 38 | } 39 | 40 | @Override 41 | public void setWorldRenderer(NvidiumWorldRenderer renderer) { 42 | this.renderer = renderer; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/me/cortex/nvidium/util/DownloadTaskStream.java: -------------------------------------------------------------------------------- 1 | package me.cortex.nvidium.util; 2 | 3 | import it.unimi.dsi.fastutil.longs.LongArrayList; 4 | import it.unimi.dsi.fastutil.longs.LongList; 5 | import it.unimi.dsi.fastutil.objects.ObjectArrayList; 6 | import it.unimi.dsi.fastutil.objects.ObjectList; 7 | import me.cortex.nvidium.gl.RenderDevice; 8 | import me.cortex.nvidium.gl.buffers.Buffer; 9 | import me.cortex.nvidium.gl.buffers.PersistentClientMappedBuffer; 10 | import me.cortex.nvidium.util.SegmentedManager; 11 | 12 | import java.util.ArrayList; 13 | import java.util.List; 14 | import java.util.function.Consumer; 15 | 16 | //Download stream from gpu to cpu 17 | public class DownloadTaskStream { 18 | public interface IDownloadFinishedCallback {void accept(long addr);} 19 | 20 | private record Download(long addr, IDownloadFinishedCallback callback) {} 21 | 22 | private final SegmentedManager allocator = new SegmentedManager(); 23 | private final RenderDevice device; 24 | private PersistentClientMappedBuffer buffer; 25 | 26 | private int cidx; 27 | private final ObjectList[] allocations; 28 | public DownloadTaskStream(RenderDevice device, int frames, long size) { 29 | this.device = device; 30 | allocator.setLimit(size); 31 | buffer = device.createClientMappedBuffer(size); 32 | TickableManager.register(this); 33 | allocations = new ObjectList[frames]; 34 | for (int i = 0; i < frames; i++) { 35 | allocations[i] = new ObjectArrayList<>(); 36 | } 37 | } 38 | 39 | public void download(Buffer source, long offset, int size, IDownloadFinishedCallback callback) { 40 | long addr = allocator.alloc(size); 41 | device.copyBuffer(source, buffer, offset, addr, size); 42 | allocations[cidx].add(new Download(addr, callback)); 43 | } 44 | 45 | void tick() { 46 | cidx = (cidx+1)%allocations.length; 47 | for (var download : allocations[cidx]) { 48 | download.callback.accept(download.addr + buffer.clientAddress()); 49 | allocator.free(download.addr); 50 | } 51 | allocations[cidx].clear(); 52 | } 53 | 54 | public void delete() { 55 | TickableManager.remove(this); 56 | buffer.delete(); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/me/cortex/nvidium/mixin/sodium/MixinSodiumOptionsGUI.java: -------------------------------------------------------------------------------- 1 | package me.cortex.nvidium.mixin.sodium; 2 | 3 | import me.cortex.nvidium.NvidiumWorldRenderer; 4 | import me.cortex.nvidium.config.ConfigGuiBuilder; 5 | import me.cortex.nvidium.sodiumCompat.INvidiumWorldRendererGetter; 6 | import me.cortex.nvidium.sodiumCompat.NvidiumOptionFlags; 7 | import net.caffeinemc.mods.sodium.client.gui.SodiumOptionsGUI; 8 | import net.caffeinemc.mods.sodium.client.gui.options.*; 9 | import net.caffeinemc.mods.sodium.client.gui.options.storage.OptionStorage; 10 | import net.caffeinemc.mods.sodium.client.render.SodiumWorldRenderer; 11 | import net.minecraft.client.Minecraft; 12 | import net.minecraft.client.gui.screens.Screen; 13 | import org.spongepowered.asm.mixin.Final; 14 | import org.spongepowered.asm.mixin.Mixin; 15 | import org.spongepowered.asm.mixin.Shadow; 16 | import org.spongepowered.asm.mixin.injection.At; 17 | import org.spongepowered.asm.mixin.injection.Inject; 18 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 19 | import org.spongepowered.asm.mixin.injection.callback.LocalCapture; 20 | 21 | import java.util.*; 22 | 23 | @Mixin(value = SodiumOptionsGUI.class, remap = false) 24 | public class MixinSodiumOptionsGUI { 25 | @Shadow @Final private List pages; 26 | 27 | @Inject(method = "", at = @At(value = "INVOKE", target = "Ljava/util/List;add(Ljava/lang/Object;)Z", ordinal = 3, shift = At.Shift.AFTER)) 28 | private void addNvidiumOptions(Screen prevScreen, CallbackInfo ci) { 29 | ConfigGuiBuilder.addNvidiumGui(pages); 30 | } 31 | 32 | @Inject(method = "applyChanges", at = @At("RETURN"), locals = LocalCapture.CAPTURE_FAILSOFT) 33 | private void applyShaderReload(CallbackInfo ci, HashSet> dirtyStorages, EnumSet flags, Minecraft client) { 34 | if (client.level != null) { 35 | SodiumWorldRenderer swr = SodiumWorldRenderer.instanceNullable(); 36 | if (swr != null) { 37 | NvidiumWorldRenderer pipeline = ((INvidiumWorldRendererGetter)((SodiumWorldRendererAccessor)swr).getRenderSectionManager()).getRenderer(); 38 | if (pipeline != null && flags.contains(NvidiumOptionFlags.REQUIRES_SHADER_RELOAD)) { 39 | pipeline.reloadShaders(); 40 | } 41 | } 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/me/cortex/nvidium/Nvidium.java: -------------------------------------------------------------------------------- 1 | package me.cortex.nvidium; 2 | 3 | import me.cortex.nvidium.config.NvidiumConfig; 4 | import net.fabricmc.loader.api.FabricLoader; 5 | import net.fabricmc.loader.api.ModContainer; 6 | import net.minecraft.Util; 7 | import org.lwjgl.opengl.GL; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | 11 | public class Nvidium { 12 | public static final String MOD_VERSION; 13 | public static final Logger LOGGER = LoggerFactory.getLogger("Nvidium"); 14 | public static boolean IS_COMPATIBLE = false; 15 | public static boolean IS_ENABLED = false; 16 | public static boolean IS_DEBUG = System.getProperty("nvidium.isDebug", "false").equals("TRUE"); 17 | public static boolean SUPPORTS_PERSISTENT_SPARSE_ADDRESSABLE_BUFFER = true; 18 | public static boolean FORCE_DISABLE = false; 19 | 20 | public static NvidiumConfig config = NvidiumConfig.loadOrCreate(); 21 | 22 | static { 23 | ModContainer mod = (ModContainer) FabricLoader.getInstance().getModContainer("nvidium").orElseThrow(NullPointerException::new); 24 | var version = mod.getMetadata().getVersion().getFriendlyString(); 25 | var commit = mod.getMetadata().getCustomValue("commit").getAsString(); 26 | MOD_VERSION = version+"-"+commit; 27 | } 28 | 29 | public static void checkSystemIsCapable() { 30 | var cap = GL.getCapabilities(); 31 | boolean supported = cap.GL_NV_mesh_shader && 32 | cap.GL_NV_uniform_buffer_unified_memory && 33 | cap.GL_NV_vertex_buffer_unified_memory && 34 | cap.GL_NV_representative_fragment_test && 35 | cap.GL_ARB_sparse_buffer && 36 | cap.GL_NV_bindless_multi_draw_indirect; 37 | IS_COMPATIBLE = supported; 38 | if (IS_COMPATIBLE) { 39 | LOGGER.info("All capabilities met"); 40 | } else { 41 | LOGGER.warn("Not all requirements met, disabling nvidium"); 42 | } 43 | if (IS_COMPATIBLE && Util.getPlatform() == Util.OS.LINUX) { 44 | LOGGER.warn("Linux currently uses fallback terrain buffer due to driver inconsistencies, expect increase vram usage"); 45 | SUPPORTS_PERSISTENT_SPARSE_ADDRESSABLE_BUFFER = false; 46 | } 47 | 48 | if (IS_COMPATIBLE) { 49 | LOGGER.info("Enabling Nvidium"); 50 | } 51 | IS_ENABLED = IS_COMPATIBLE; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/me/cortex/nvidium/config/NvidiumConfig.java: -------------------------------------------------------------------------------- 1 | package me.cortex.nvidium.config; 2 | 3 | import com.google.gson.FieldNamingPolicy; 4 | import com.google.gson.Gson; 5 | import com.google.gson.GsonBuilder; 6 | import me.cortex.nvidium.Nvidium; 7 | import net.fabricmc.loader.api.FabricLoader; 8 | 9 | import java.io.FileReader; 10 | import java.io.IOException; 11 | import java.lang.reflect.Modifier; 12 | import java.nio.file.Files; 13 | import java.nio.file.Path; 14 | 15 | public class NvidiumConfig { 16 | //The options 17 | public boolean enable_temporal_coherence = true; 18 | public int max_geometry_memory = 2048; 19 | public boolean automatic_memory = true; 20 | 21 | public boolean async_bfs = true; 22 | 23 | public int region_keep_distance = 32; 24 | 25 | 26 | public boolean render_fog = true; 27 | public boolean use_sodium_vertex_format = false; 28 | public boolean cull_degenerate_triangles = true; 29 | public boolean use_nv_fragment_shader_barycentric = true; 30 | 31 | public TranslucencySortingLevel translucency_sorting_level = TranslucencySortingLevel.SODIUM; 32 | 33 | public StatisticsLoggingLevel statistics_level = StatisticsLoggingLevel.NONE; 34 | 35 | 36 | private static final Gson GSON = new GsonBuilder() 37 | .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES) 38 | .setPrettyPrinting() 39 | .excludeFieldsWithModifiers(Modifier.PRIVATE) 40 | .create(); 41 | 42 | private NvidiumConfig() {} 43 | public static NvidiumConfig loadOrCreate() { 44 | var path = getConfigPath(); 45 | if (Files.exists(path)) { 46 | try (FileReader reader = new FileReader(path.toFile())) { 47 | return GSON.fromJson(reader, NvidiumConfig.class); 48 | } catch (IOException e) { 49 | Nvidium.LOGGER.error("Could not parse config", e); 50 | } 51 | } 52 | return new NvidiumConfig(); 53 | } 54 | 55 | public void save() { 56 | //Unsafe, todo: fixme! needs to be atomic! 57 | try { 58 | Files.writeString(getConfigPath(), GSON.toJson(this)); 59 | } catch (IOException e) { 60 | Nvidium.LOGGER.error("Failed to write config file", e); 61 | } 62 | } 63 | 64 | private static Path getConfigPath() { 65 | return FabricLoader.getInstance() 66 | .getConfigDir() 67 | .resolve("nvidium-config.json"); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/me/cortex/nvidium/sodiumCompat/ShaderLoader.java: -------------------------------------------------------------------------------- 1 | package me.cortex.nvidium.sodiumCompat; 2 | 3 | import me.cortex.nvidium.Nvidium; 4 | import me.cortex.nvidium.config.StatisticsLoggingLevel; 5 | import me.cortex.nvidium.config.TranslucencySortingLevel; 6 | import net.caffeinemc.mods.sodium.client.gl.shader.ShaderConstants; 7 | import net.caffeinemc.mods.sodium.client.gl.shader.ShaderParser; 8 | import net.minecraft.resources.ResourceLocation; 9 | import java.util.function.Consumer; 10 | 11 | public class ShaderLoader { 12 | public static String parse(ResourceLocation path) { 13 | return parse(path, shaderConstants -> {}); 14 | } 15 | 16 | public static String parse(ResourceLocation path, Consumer constantBuilder) { 17 | var builder = ShaderConstants.builder(); 18 | if (Nvidium.IS_DEBUG) { 19 | builder.add("DEBUG"); 20 | } 21 | 22 | for (int i = 1; i <= Nvidium.config.statistics_level.ordinal(); i++) { 23 | builder.add("STATISTICS_"+StatisticsLoggingLevel.values()[i].name()); 24 | } 25 | 26 | 27 | if (Nvidium.config.translucency_sorting_level.ordinal() >= TranslucencySortingLevel.SECTIONS.ordinal()) { 28 | builder.add("TRANSLUCENCY_SORTING_SECTIONS"); 29 | } 30 | if (Nvidium.config.translucency_sorting_level == TranslucencySortingLevel.QUADS) { 31 | builder.add("TRANSLUCENCY_SORTING_QUADS"); 32 | } 33 | if (Nvidium.config.translucency_sorting_level == TranslucencySortingLevel.SODIUM) { 34 | builder.add("TRANSLUCENCY_SORTING_SODIUM"); 35 | } 36 | 37 | if (Nvidium.config.render_fog) { 38 | builder.add("RENDER_FOG"); 39 | } 40 | 41 | if (Nvidium.config.use_sodium_vertex_format) { 42 | builder.add("USE_SODIUM_VERTEX_FORMAT"); 43 | } 44 | if (Nvidium.config.cull_degenerate_triangles) { 45 | builder.add("CULL_DEGENERATE_TRIANGLES"); 46 | } 47 | if (Nvidium.config.use_nv_fragment_shader_barycentric) { 48 | builder.add("USE_NV_FRAGMENT_SHADER_BARYCENTRIC"); 49 | } 50 | 51 | builder.add("TEXTURE_MAX_SCALE", String.valueOf(NvidiumCompactChunkVertex.TEXTURE_MAX_VALUE)); 52 | constantBuilder.accept(builder); 53 | 54 | var shaderSrc = net.caffeinemc.mods.sodium.client.gl.shader.ShaderLoader.getShaderSource(path); 55 | return ShaderParser.parseShader(shaderSrc, builder.build()).src(); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/resources/assets/nvidium/shaders/terrain/task_common.glsl: -------------------------------------------------------------------------------- 1 | #define MESH_WORKLOAD_PER_INVOCATION 32 2 | 3 | taskNV out Task { 4 | vec3 origin; 5 | uint baseOffset; 6 | uint quadCount; 7 | uint transformationId; 8 | 9 | //Binary search indexs and data 10 | uvec4 binIa; 11 | uvec4 binIb; 12 | uvec4 binVa; 13 | uvec4 binVb; 14 | }; 15 | 16 | void putBinData(inout uint idx, inout uint lastIndex, uint offset, uint nextOffset) { 17 | uint len = nextOffset - offset; 18 | uint id = idx++; 19 | if (id < 4) { 20 | binIa[id] = lastIndex + len; 21 | binVa[id] = offset; 22 | } else { 23 | binIb[id - 4] = lastIndex + len; 24 | binVb[id - 4] = offset; 25 | } 26 | lastIndex += len; 27 | } 28 | 29 | //Populate the tasks with respect to the chunk face visibility 30 | void populateTasks(ivec3 relChunkPos, uvec4 ranges) { 31 | //TODO: make the ranges cumulate up, this means that we can fit much much more data per chunk 32 | // as the range will be spred across all the offsets since they are not the absolute offset 33 | 34 | //Hacky thing to render all block faces if the flag is not set 35 | if (!useBlockFaceCulling()) { 36 | relChunkPos = ivec3(0); 37 | } 38 | 39 | uint idx = 0; 40 | uint lastIndex = 0; 41 | 42 | binIa = uvec4(0); 43 | binIb = uvec4(0); 44 | 45 | uint fr = (ranges.w>>16)&0xFFFF; 46 | 47 | uint delta = (ranges.x&0xFFFF); 48 | if (relChunkPos.x <= 0 && delta > 0) { 49 | putBinData(idx, lastIndex, fr, fr + delta); 50 | } 51 | fr += ranges.x&0xFFFF; 52 | 53 | delta = ((ranges.x>>16)&0xFFFF); 54 | if (relChunkPos.y <= 0 && delta > 0) { 55 | putBinData(idx, lastIndex, fr, fr + delta); 56 | } 57 | fr += (ranges.x>>16)&0xFFFF; 58 | 59 | delta = ranges.y&0xFFFF; 60 | if (relChunkPos.z <= 0 && delta > 0) { 61 | putBinData(idx, lastIndex, fr, fr + delta); 62 | } 63 | fr += ranges.y&0xFFFF; 64 | 65 | delta = (ranges.y>>16)&0xFFFF; 66 | if (relChunkPos.x >= 0 && delta > 0) { 67 | putBinData(idx, lastIndex, fr, fr + delta); 68 | } 69 | fr += (ranges.y>>16)&0xFFFF; 70 | 71 | delta = ranges.z&0xFFFF; 72 | if (relChunkPos.y >= 0 && delta > 0) { 73 | putBinData(idx, lastIndex, fr, fr + delta); 74 | } 75 | fr += ranges.z&0xFFFF; 76 | 77 | delta = (ranges.z>>16)&0xFFFF; 78 | if (relChunkPos.z >= 0 && delta > 0) { 79 | putBinData(idx, lastIndex, fr, fr + delta); 80 | } 81 | fr += (ranges.z>>16)&0xFFFF; 82 | 83 | //TODO: Put unsigned quads at the begining? since it should be cheaper 84 | putBinData(idx, lastIndex, fr, fr + (ranges.w&0xFFFF)); 85 | 86 | 87 | 88 | 89 | quadCount = lastIndex; 90 | 91 | //Emit enough mesh shaders such that max(gl_GlobalInvocationID.x)>=2*quadCount 92 | gl_TaskCountNV = ((lastIndex*2)+MESH_WORKLOAD_PER_INVOCATION-1)/MESH_WORKLOAD_PER_INVOCATION; 93 | } -------------------------------------------------------------------------------- /src/main/resources/assets/nvidium/shaders/occlusion/section_raster/task.glsl: -------------------------------------------------------------------------------- 1 | #version 460 2 | 3 | #extension GL_ARB_shading_language_include : enable 4 | #pragma optionNV(unroll all) 5 | #define UNROLL_LOOP 6 | #extension GL_NV_mesh_shader : require 7 | #extension GL_NV_gpu_shader5 : require 8 | #extension GL_NV_bindless_texture : require 9 | 10 | #extension GL_KHR_shader_subgroup_basic : require 11 | #extension GL_KHR_shader_subgroup_ballot : require 12 | #extension GL_KHR_shader_subgroup_vote : require 13 | 14 | #import 15 | 16 | //This is 1 since each task shader workgroup -> multiple meshlets. its not each globalInvocation (afaik) 17 | layout(local_size_x=1) in; 18 | 19 | taskNV out Task { 20 | uint32_t _visOutBase;// The base offset for the visibility output of the shader 21 | uint32_t _offset;//start offset for regions (can/should probably be a uint16 since this is just the region id << 8) 22 | //uint64_t bitcheck[4];//TODO: MAYBE DO THIS, each bit is whether there a section at that index, doing so is faster than pulling metadata to check if a section is valid or not 23 | mat4 regionTransform; 24 | ivec3 chunkShift; 25 | }; 26 | 27 | void main() { 28 | //TODO: see whats faster, atomicAdd (for mdic) or dispatching alot of empty calls (mdi) 29 | //TODO: experiment with emitting 8 workgroups with the 8th always being 0 30 | // doing so would enable to batch memory write 2 commands 31 | // thus taking 4 mem moves instead of 7 32 | 33 | //Emit 7 workloads per chunk 34 | uint cmdIdx = gl_WorkGroupID.x; 35 | uint transCmdIdx = (uint(regionCount) - gl_WorkGroupID.x) - 1; 36 | 37 | //Early exit if the region wasnt visible 38 | if (regionVisibility[gl_WorkGroupID.x] == uint8_t(0)) { 39 | terrainCommandBuffer[cmdIdx] = uvec2(0); 40 | translucencyCommandBuffer[transCmdIdx] = uvec2(0); 41 | gl_TaskCountNV = 0; 42 | return; 43 | } 44 | 45 | #ifdef STATISTICS_REGIONS 46 | atomicAdd(statistics_buffer, 1); 47 | #endif 48 | 49 | //FIXME: It might actually be more efficent to just upload the region data straight into the ubo 50 | uint32_t offset = regionIndicies[gl_WorkGroupID.x]; 51 | Region data = regionData[offset]; 52 | int count = unpackRegionCount(data)+1; 53 | 54 | //Write in order 55 | _visOutBase = offset<<8;//This makes checking visibility very fast and quick in the compute shader 56 | _offset = offset<<8; 57 | regionTransform = getRegionTransformation(data); 58 | 59 | chunkShift = (-chunkPosition.xyz) - unpackOriginOffsetId(unpackRegionTransformId(data)); 60 | 61 | gl_TaskCountNV = count; 62 | 63 | terrainCommandBuffer[cmdIdx] = uvec2(uint32_t(count), _visOutBase); 64 | //TODO: add a bit to the region header to determine whether or not a region has any translucent 65 | // sections, if it doesnt, write 0 to the command buffer 66 | translucencyCommandBuffer[transCmdIdx] = uvec2(uint32_t(count), _visOutBase); 67 | } 68 | -------------------------------------------------------------------------------- /src/main/resources/assets/nvidium/shaders/occlusion/queries/region/mesh.glsl: -------------------------------------------------------------------------------- 1 | #version 460 2 | #extension GL_ARB_shading_language_include : enable 3 | #pragma optionNV(unroll all) 4 | #define UNROLL_LOOP 5 | #extension GL_NV_mesh_shader : require 6 | #extension GL_NV_gpu_shader5 : require 7 | #extension GL_NV_bindless_texture : require 8 | #extension GL_NV_shader_buffer_load : require 9 | 10 | 11 | #import 12 | 13 | #define ADD_SIZE (0.1f/16) 14 | 15 | //TODO: maybe do multiple cubes per workgroup? this would increase utilization of individual sm's 16 | layout(local_size_x = 8) in; 17 | layout(triangles, max_vertices=8, max_primitives=12) out; 18 | 19 | const uint PILUTA[] = {0, 3, 6, 0, 1, 7, 4, 5}; 20 | const uint PILUTB[] = {1, 2, 6, 4, 0, 7, 6, 4}; 21 | const uint PILUTC[] = {2, 0, 4, 5, 1, 3, 7, 2}; 22 | const uint PILUTD[] = {1, 2, 0, 5, 5, 1, 7, 7}; 23 | 24 | const uint PILUTE[] = {6, 2, 3, 7}; 25 | void emitIndicies(int visIndex) { 26 | gl_PrimitiveIndicesNV[(gl_LocalInvocationID.x<<2)|0] = PILUTA[gl_LocalInvocationID.x]; 27 | gl_PrimitiveIndicesNV[(gl_LocalInvocationID.x<<2)|1] = PILUTB[gl_LocalInvocationID.x]; 28 | gl_PrimitiveIndicesNV[(gl_LocalInvocationID.x<<2)|2] = PILUTC[gl_LocalInvocationID.x]; 29 | gl_PrimitiveIndicesNV[(gl_LocalInvocationID.x<<2)|3] = PILUTD[gl_LocalInvocationID.x]; 30 | gl_MeshPrimitivesNV[gl_LocalInvocationID.x].gl_PrimitiveID = visIndex; 31 | } 32 | 33 | void emitParital(int visIndex) { 34 | gl_PrimitiveIndicesNV[(8*4)+gl_LocalInvocationID.x] = PILUTE[gl_LocalInvocationID.x]; 35 | gl_MeshPrimitivesNV[gl_LocalInvocationID.x+8].gl_PrimitiveID = visIndex; 36 | gl_PrimitiveCountNV = 12; 37 | } 38 | 39 | void main() { 40 | //FIXME: It might actually be more efficent to just upload the region data straight into the ubo 41 | // this remove an entire level of indirection and also puts region data in the very fast path 42 | Region data = regionData[regionIndicies[gl_WorkGroupID.x]];//fetch the region data 43 | 44 | ivec3 pos = unpackRegionPosition(data); 45 | pos -= chunkPosition.xyz; 46 | pos -= unpackOriginOffsetId(unpackRegionTransformId(data)); 47 | 48 | vec3 start = pos - ADD_SIZE; 49 | vec3 end = start + 1 + unpackRegionSize(data) + (ADD_SIZE*2); 50 | 51 | //TODO: Look into only doing 4 locals, for 2 reasons, its more effective for reducing duplicate computation and bandwidth 52 | // it also means that each thread can emit 3 primatives, 9 indicies each 53 | 54 | //can also do 8 threads then each thread emits a primative and 4 indicies each then the lower 4 emit 1 indice extra each 55 | 56 | vec3 corner = vec3(((gl_LocalInvocationID.x&1)==0)?start.x:end.x, ((gl_LocalInvocationID.x&4)==0)?start.y:end.y, ((gl_LocalInvocationID.x&2)==0)?start.z:end.z); 57 | corner *= 16.0f; 58 | gl_MeshVerticesNV[gl_LocalInvocationID.x].gl_Position = MVP*(getRegionTransformation(data)*vec4(corner, 1.0)); 59 | 60 | int visibilityIndex = int(gl_WorkGroupID.x); 61 | 62 | regionVisibility[visibilityIndex] = uint8_t(0); 63 | 64 | emitIndicies(visibilityIndex); 65 | if (gl_LocalInvocationID.x < 4) { 66 | emitParital(visibilityIndex); 67 | } 68 | } -------------------------------------------------------------------------------- /src/main/resources/assets/nvidium/shaders/terrain/translucent/task.glsl: -------------------------------------------------------------------------------- 1 | #version 460 2 | 3 | #extension GL_ARB_shading_language_include : enable 4 | #pragma optionNV(unroll all) 5 | #define UNROLL_LOOP 6 | #extension GL_NV_mesh_shader : require 7 | #extension GL_NV_gpu_shader5 : require 8 | #extension GL_NV_bindless_texture : require 9 | 10 | #extension GL_KHR_shader_subgroup_basic : require 11 | #extension GL_KHR_shader_subgroup_ballot : require 12 | #extension GL_KHR_shader_subgroup_vote : require 13 | 14 | #import 15 | 16 | #define MESH_WORKLOAD_PER_INVOCATION 32 17 | 18 | //This is 1 since each task shader workgroup -> multiple meshlets. its not each globalInvocation (afaik) 19 | layout(local_size_x=1) in; 20 | 21 | //In here add an array that is then "logged" on in the mesh shader to find the draw data 22 | taskNV out Task { 23 | vec4 originAndBaseData; 24 | uint quadCount; 25 | #ifdef TRANSLUCENCY_SORTING_QUADS 26 | uint8_t jiggle; 27 | #endif 28 | int translucencyIndex; 29 | }; 30 | 31 | bool shouldRender(uint sectionId) { 32 | //Check visibility 33 | return (sectionVisibility[sectionId]&uint8_t(1)) != uint8_t(0); 34 | } 35 | 36 | void main() { 37 | uint sectionId = gl_WorkGroupID.x; 38 | #ifdef TRANSLUCENCY_SORTING_SECTIONS 39 | //Compute indirection for translucency sorting 40 | { 41 | ivec4 header = sectionData[sectionId].header; 42 | //If the section is empty, we dont care about it at all, so ignore it and return 43 | if (sectionEmpty(header)) { 44 | return; 45 | } 46 | //Compute the redirected section index 47 | sectionId &= ~0xFF; 48 | sectionId |= uint((header.y>>18)&0xFF); 49 | } 50 | #endif 51 | 52 | if (!shouldRender(sectionId)) { 53 | //Early exit if the section isnt visible 54 | //TODO: also early exit if there are no translucents to render 55 | gl_TaskCountNV = 0; 56 | return; 57 | } 58 | 59 | translucencyIndex = sectionData[sectionId].translucencyDataIdx; 60 | 61 | ivec4 header = sectionData[sectionId].header; 62 | uint baseDataOffset = uint(header.w); 63 | ivec3 chunk = ivec3(header.xyz)>>8; 64 | chunk.y &= 0x1ff; 65 | chunk.y <<= 32-9; 66 | chunk.y >>= 32-9; 67 | originAndBaseData.xyz = vec3((chunk - chunkPosition.xyz)<<4); 68 | 69 | 70 | quadCount = ((sectionData[sectionId].renderRanges.w>>16)&0xFFFF); 71 | #ifdef TRANSLUCENCY_SORTING_QUADS 72 | jiggle = uint8_t(min(quadCount>>1,(uint(frameId)&1)));//Jiggle by 1 quads (either 0 or 1)//*15 73 | //jiggle = uint8_t(0); 74 | quadCount += jiggle; 75 | originAndBaseData.w = uintBitsToFloat(baseDataOffset - uint(jiggle)); 76 | #else 77 | originAndBaseData.w = uintBitsToFloat(baseDataOffset); 78 | #endif 79 | 80 | //Emit enough mesh shaders such that max(gl_GlobalInvocationID.x)>=quadCount 81 | gl_TaskCountNV = (quadCount+MESH_WORKLOAD_PER_INVOCATION-1)/MESH_WORKLOAD_PER_INVOCATION; 82 | 83 | #ifdef STATISTICS_QUADS 84 | atomicAdd(statistics_buffer+2, quadCount); 85 | #endif 86 | 87 | #ifdef STATISTICS_SECTIONS 88 | atomicAdd(statistics_buffer+1, 1); 89 | #endif 90 | } -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | @rem SPDX-License-Identifier: Apache-2.0 17 | @rem 18 | 19 | @if "%DEBUG%"=="" @echo off 20 | @rem ########################################################################## 21 | @rem 22 | @rem Gradle startup script for Windows 23 | @rem 24 | @rem ########################################################################## 25 | 26 | @rem Set local scope for the variables with windows NT shell 27 | if "%OS%"=="Windows_NT" setlocal 28 | 29 | set DIRNAME=%~dp0 30 | if "%DIRNAME%"=="" set DIRNAME=. 31 | @rem This is normally unused 32 | set APP_BASE_NAME=%~n0 33 | set APP_HOME=%DIRNAME% 34 | 35 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 36 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 37 | 38 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 39 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 40 | 41 | @rem Find java.exe 42 | if defined JAVA_HOME goto findJavaFromJavaHome 43 | 44 | set JAVA_EXE=java.exe 45 | %JAVA_EXE% -version >NUL 2>&1 46 | if %ERRORLEVEL% equ 0 goto execute 47 | 48 | echo. 1>&2 49 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 50 | echo. 1>&2 51 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 52 | echo location of your Java installation. 1>&2 53 | 54 | goto fail 55 | 56 | :findJavaFromJavaHome 57 | set JAVA_HOME=%JAVA_HOME:"=% 58 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 59 | 60 | if exist "%JAVA_EXE%" goto execute 61 | 62 | echo. 1>&2 63 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 64 | echo. 1>&2 65 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 66 | echo location of your Java installation. 1>&2 67 | 68 | goto fail 69 | 70 | :execute 71 | @rem Setup the command line 72 | 73 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 74 | 75 | 76 | @rem Execute Gradle 77 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 78 | 79 | :end 80 | @rem End local scope for the variables with windows NT shell 81 | if %ERRORLEVEL% equ 0 goto mainEnd 82 | 83 | :fail 84 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 85 | rem the _cmd.exe /c_ return code! 86 | set EXIT_CODE=%ERRORLEVEL% 87 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 88 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 89 | exit /b %EXIT_CODE% 90 | 91 | :mainEnd 92 | if "%OS%"=="Windows_NT" endlocal 93 | 94 | :omega 95 | -------------------------------------------------------------------------------- /src/main/java/me/cortex/nvidium/managers/RegionVisibilityTracker.java: -------------------------------------------------------------------------------- 1 | package me.cortex.nvidium.managers; 2 | 3 | import me.cortex.nvidium.gl.buffers.Buffer; 4 | import me.cortex.nvidium.gl.shader.Shader; 5 | import me.cortex.nvidium.sodiumCompat.ShaderLoader; 6 | import me.cortex.nvidium.util.DownloadTaskStream; 7 | import net.minecraft.resources.ResourceLocation; 8 | import org.lwjgl.system.MemoryUtil; 9 | 10 | import static me.cortex.nvidium.gl.shader.ShaderType.FRAGMENT; 11 | import static me.cortex.nvidium.gl.shader.ShaderType.MESH; 12 | import static org.lwjgl.opengl.GL42.glMemoryBarrier; 13 | import static org.lwjgl.opengl.GL43C.GL_SHADER_STORAGE_BARRIER_BIT; 14 | import static org.lwjgl.opengl.NVMeshShader.glDrawMeshTasksNV; 15 | 16 | public class RegionVisibilityTracker { 17 | private final Shader shader = Shader.make() 18 | .addSource(MESH, ShaderLoader.parse(ResourceLocation.fromNamespaceAndPath("nvidium", "occlusion/queries/region/mesh.glsl"))) 19 | .addSource(FRAGMENT, ShaderLoader.parse(ResourceLocation.fromNamespaceAndPath("nvidium", "occlusion/queries/region/fragment.frag"))) 20 | .compile(); 21 | 22 | private final DownloadTaskStream downStream; 23 | private final int[] frustum; 24 | private final int[] visible; 25 | public RegionVisibilityTracker(DownloadTaskStream downStream, int maxRegions) { 26 | this.downStream = downStream; 27 | visible = new int[maxRegions]; 28 | frustum = new int[maxRegions]; 29 | for (int i = 0; i < maxRegions; i++) { 30 | frustum[i] = 0; 31 | visible[i] = 0; 32 | } 33 | } 34 | 35 | private int fram = 0; 36 | //This is kind of evil in the fact that it just reuses the visibility buffer 37 | public void computeVisibility(int regionCount, Buffer regionVisibilityBuffer, short[] regionMapping) { 38 | shader.bind(); 39 | fram++; 40 | glDrawMeshTasksNV(0,regionCount); 41 | glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT); 42 | downStream.download(regionVisibilityBuffer, 0, regionCount, ptr -> { 43 | for (int i = 0; i < regionMapping.length; i++) { 44 | if (MemoryUtil.memGetByte(ptr + i) == 1) { 45 | //System.out.println(regionMapping[i] + " was visible"); 46 | frustum[regionMapping[i]]++; 47 | visible[regionMapping[i]] = fram; 48 | } else { 49 | //System.out.println(regionMapping[i] + " was not visible"); 50 | frustum[regionMapping[i]]++; 51 | } 52 | } 53 | }); 54 | } 55 | 56 | 57 | public void delete() { 58 | shader.delete(); 59 | } 60 | 61 | public void resetRegion(int id) { 62 | frustum[id] = 0; 63 | visible[id] = 0; 64 | } 65 | 66 | public int findMostLikelyLeastSeenRegion(int maxIndex) { 67 | int maxRank = Integer.MIN_VALUE; 68 | int id = -1; 69 | for (int i = 0; i < maxIndex; i++) { 70 | if (frustum[i] <= 200) continue; 71 | int rank = - visible[i]; 72 | //int rank = -visible[i]; 73 | if (maxRank < rank) { 74 | maxRank = rank; 75 | id = i; 76 | } 77 | } 78 | return id; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/main/resources/assets/nvidium/lang/en_us.json: -------------------------------------------------------------------------------- 1 | { 2 | "nvidium.options.pages.nvidium" : "Nvidium", 3 | "nvidium.options.region_keep_distance.name" : "Region Keep Distance", 4 | "nvidium.options.region_keep_distance.tooltip" : "Distance to keep loaded regions\n/!\\ Can cause issues, you should probably use a mod like Bobby and let this to Vanilla", 5 | "nvidium.options.automatic_memory_limit.name" : "Automatic memory limit", 6 | "nvidium.options.automatic_memory_limit.tooltip" : "Automatically determines the memory limit to set given the amount of available vram on your system (Close and reopen settings to edit the max memory)", 7 | "nvidium.options.max_gpu_memory.name" : "Max gpu memory", 8 | "nvidium.options.max_gpu_memory.tooltip" : "Max gpu memory allowed, will start to cull chunks if this limit is hit", 9 | "nvidium.options.enable_temporal_coherence.name" : "Enables temporal coherence", 10 | "nvidium.options.enable_temporal_coherence.tooltip" : "Removes artifacting when turning around", 11 | "nvidium.options.mb" : "%s Mbs", 12 | "nvidium.options.translucency_sorting.name" : "Translucency Sorting", 13 | "nvidium.options.translucency_sorting.tooltip" : "Translucency sorting level, each level has different performance impact and visual quality. \nNone:No translucency sorting, no impact, can look quite bad\nSections: Section level translucency sorting, brings translucency to the same level as sodium, minimal impact\nQuads: Incremental sorting, sorts geometry correctly over multiple frames, can cause visual weirdness while sorting\nSodium: rely on sodium translucency sorting", 14 | "nvidium.options.translucency_sorting.none" : "None", 15 | "nvidium.options.translucency_sorting.sections" : "Sections", 16 | "nvidium.options.translucency_sorting.quads" : "Quads", 17 | "nvidium.options.translucency_sorting.sodium" : "Sodium", 18 | "nvidium.options.use_sodium_vertex_format.name": "Use sodium vertex format", 19 | "nvidium.options.use_sodium_vertex_format.tooltip": "Uses sodium vertex format, less performant and uses more VRAM, may fixes issues with some mods", 20 | "nvidium.options.cull_degenerate_triangles.name": "Cull degenerate triangles", 21 | "nvidium.options.cull_degenerate_triangles.tooltip": "Cull degenerate triangles for better performance", 22 | "nvidium.options.use_nv_fragment_shader_barycentric.name": "Use NV fragment shader barycentric", 23 | "nvidium.options.use_nv_fragment_shader_barycentric.tooltip": "Better performance when enabled", 24 | "nvidium.options.statistics_level.name" : "Statistics Level", 25 | "nvidium.options.statistics_level.tooltip" : "Statistics logging level, tracks the visibility count of cull layers", 26 | "nvidium.options.statistics_level.none" : "None", 27 | "nvidium.options.statistics_level.frustum" : "Frustum", 28 | "nvidium.options.statistics_level.regions" : "Regions", 29 | "nvidium.options.statistics_level.sections" : "Sections", 30 | "nvidium.options.statistics_level.quads" : "Quads", 31 | "nvidium.options.statistics_level.cull" : "Culled triangles", 32 | "nvidium.options.async_bfs.name" : "Enable async bfs", 33 | "nvidium.options.async_bfs.tooltip" : "Enables asynchronous bfs chunk section loading, greatly reduces the frame time when moving, more noticeable with higher render distances", 34 | "nvidium.options.render_fog.name": "Render Fog", 35 | "nvidium.options.render_fog.tooltip": "Should fog be rendered" 36 | } -------------------------------------------------------------------------------- /src/main/resources/assets/nvidium/shaders/sorting/region_section_sorter.comp: -------------------------------------------------------------------------------- 1 | #version 460 core 2 | #extension GL_NV_gpu_shader5 : require 3 | #define SORTING_NETWORK_SIZE 256 4 | layout(local_size_x = (SORTING_NETWORK_SIZE>>1), local_size_y = 1 , local_size_z = 1) in; 5 | 6 | #import 7 | #import 8 | 9 | 10 | 11 | void populateSection(uint regionId, uint8_t sectionId) { 12 | ivec4 sectionHeader = sectionData[(regionId<<8)|uint(sectionId)].header; 13 | if (sectionEmpty(sectionHeader)) { 14 | //The section doesnt exist so nuke it from existance 15 | putSortingData(sectionId, -999999999.0f); 16 | } else { 17 | ivec3 chunk = ivec3(sectionHeader.xyz)>>8; 18 | chunk.y &= 0x1ff; 19 | chunk.y <<= 32-9; 20 | chunk.y >>= 32-9; 21 | chunk -= chunkPosition.xyz; 22 | putSortingData(sectionId, abs(chunk.x) + abs(chunk.y) + abs(chunk.z)); 23 | } 24 | } 25 | 26 | uint regionId = 0; 27 | 28 | //Note: dont actually need to access the region header since everything is in the section header 29 | bool populate() { 30 | regionId = sortingRegionList[gl_WorkGroupID.x]; 31 | 32 | //TODO: FIXME: the reason this doesnt work is cause the regionId != the location of visibility 33 | //if (regionVisibility[regionId] == uint8_t(0)) { 34 | // return true; 35 | //} 36 | populateSection(regionId, uint8_t(gl_LocalInvocationID.x<<1)); 37 | populateSection(regionId, uint8_t((gl_LocalInvocationID.x<<1)|1)); 38 | barrier(); 39 | memoryBarrierShared(); 40 | return false; 41 | } 42 | 43 | void updateSection(uint regionId, uint8_t id) { 44 | uint8_t from = id; 45 | uint8_t too = threadBufferIndex[id]; 46 | ivec4 header = sectionData[(regionId<<8)|uint(from)].header; 47 | header.y &= ~(0xFF<<18); 48 | header.y |= int(uint(too))<<18; 49 | sectionData[(regionId<<8)|uint(from)].header = header; 50 | } 51 | 52 | void update() { 53 | updateSection(regionId, uint8_t(gl_LocalInvocationID.x<<1)); 54 | updateSection(regionId, uint8_t((gl_LocalInvocationID.x<<1)|1)); 55 | } 56 | 57 | 58 | 59 | void main() { 60 | if (populate()) { 61 | return; 62 | } 63 | //TODO: add early exits for when the section count in a region is < 1<