├── .gitignore
├── LICENSE.txt
├── README.md
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── settings.gradle
└── src
└── main
├── java
└── me
│ └── cortex
│ └── nvidium
│ ├── Nvidium.java
│ ├── NvidiumWorldRenderer.java
│ ├── RenderPipeline.java
│ ├── api0
│ └── NvidiumAPI.java
│ ├── config
│ ├── ConfigGuiBuilder.java
│ ├── NvidiumConfig.java
│ ├── NvidiumConfigStore.java
│ ├── StatisticsLoggingLevel.java
│ └── TranslucencySortingLevel.java
│ ├── gl
│ ├── GlFence.java
│ ├── GlObject.java
│ ├── IResource.java
│ ├── RenderDevice.java
│ ├── TrackedObject.java
│ ├── buffers
│ │ ├── Buffer.java
│ │ ├── DeviceOnlyMappedBuffer.java
│ │ ├── IClientMappedBuffer.java
│ │ ├── IDeviceMappedBuffer.java
│ │ ├── PersistentClientMappedBuffer.java
│ │ └── PersistentSparseAddressableBuffer.java
│ ├── images
│ │ └── DepthOnlyFrameBuffer.java
│ └── shader
│ │ ├── IShaderProcessor.java
│ │ ├── Shader.java
│ │ └── ShaderType.java
│ ├── managers
│ ├── AsyncOcclusionTracker.java
│ ├── RegionManager.java
│ ├── RegionVisibilityTracker.java
│ └── SectionManager.java
│ ├── mixin
│ ├── minecraft
│ │ ├── LightMapAccessor.java
│ │ ├── MixinBackgroundRenderer.java
│ │ ├── MixinGameRenderer.java
│ │ ├── MixinWindow.java
│ │ └── MixinWorldRenderer.java
│ └── sodium
│ │ ├── MixinChunkBuildOutput.java
│ │ ├── MixinChunkBuilder.java
│ │ ├── MixinChunkBuilderMeshingTask.java
│ │ ├── MixinChunkJobQueue.java
│ │ ├── MixinOptionFlag.java
│ │ ├── MixinRenderRegionManager.java
│ │ ├── MixinRenderSection.java
│ │ ├── MixinRenderSectionManager.java
│ │ ├── MixinSodiumOptionsGUI.java
│ │ ├── MixinSodiumWorldRenderer.java
│ │ └── SodiumWorldRendererAccessor.java
│ ├── renderers
│ ├── Phase.java
│ ├── PrimaryTerrainRasterizer.java
│ ├── RegionRasterizer.java
│ ├── SectionRasterizer.java
│ ├── SortRegionSectionPhase.java
│ ├── TemporalTerrainRasterizer.java
│ └── TranslucentTerrainRasterizer.java
│ ├── sodiumCompat
│ ├── INvidiumWorldRendererGetter.java
│ ├── INvidiumWorldRendererSetter.java
│ ├── IRenderSectionExtension.java
│ ├── IRepackagedResult.java
│ ├── IrisCheck.java
│ ├── NvidiumCompactChunkVertex.java
│ ├── NvidiumOptionFlags.java
│ ├── RepackagedSectionOutput.java
│ ├── ShaderLoader.java
│ └── SodiumResultCompatibility.java
│ └── util
│ ├── BufferArena.java
│ ├── DownloadTaskStream.java
│ ├── IdProvider.java
│ ├── SegmentedManager.java
│ ├── TickableManager.java
│ └── UploadingBufferStream.java
└── resources
├── assets
└── nvidium
│ ├── lang
│ └── en_us.json
│ ├── nvidium.png
│ └── shaders
│ ├── occlusion
│ ├── queries
│ │ └── region
│ │ │ ├── fragment.frag
│ │ │ └── mesh.glsl
│ ├── region_raster
│ │ ├── fragment.frag
│ │ └── mesh.glsl
│ ├── scene.glsl
│ └── section_raster
│ │ ├── fragment.glsl
│ │ ├── mesh.glsl
│ │ └── task.glsl
│ ├── sorting
│ ├── region_section_sorter.comp
│ └── sorting_network.glsl
│ └── terrain
│ ├── fog.glsl
│ ├── frag.frag
│ ├── mesh.glsl
│ ├── task.glsl
│ ├── task_common.glsl
│ ├── temporal_task.glsl
│ ├── translucent
│ ├── mesh.glsl
│ └── task.glsl
│ └── vertex_format.glsl
├── fabric.mod.json
└── nvidium.mixins.json
/.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 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | GNU LESSER GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 |
9 | This version of the GNU Lesser General Public License incorporates
10 | the terms and conditions of version 3 of the GNU General Public
11 | License, supplemented by the additional permissions listed below.
12 |
13 | 0. Additional Definitions.
14 |
15 | As used herein, "this License" refers to version 3 of the GNU Lesser
16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU
17 | General Public License.
18 |
19 | "The Library" refers to a covered work governed by this License,
20 | other than an Application or a Combined Work as defined below.
21 |
22 | An "Application" is any work that makes use of an interface provided
23 | by the Library, but which is not otherwise based on the Library.
24 | Defining a subclass of a class defined by the Library is deemed a mode
25 | of using an interface provided by the Library.
26 |
27 | A "Combined Work" is a work produced by combining or linking an
28 | Application with the Library. The particular version of the Library
29 | with which the Combined Work was made is also called the "Linked
30 | Version".
31 |
32 | The "Minimal Corresponding Source" for a Combined Work means the
33 | Corresponding Source for the Combined Work, excluding any source code
34 | for portions of the Combined Work that, considered in isolation, are
35 | based on the Application, and not on the Linked Version.
36 |
37 | The "Corresponding Application Code" for a Combined Work means the
38 | object code and/or source code for the Application, including any data
39 | and utility programs needed for reproducing the Combined Work from the
40 | Application, but excluding the System Libraries of the Combined Work.
41 |
42 | 1. Exception to Section 3 of the GNU GPL.
43 |
44 | You may convey a covered work under sections 3 and 4 of this License
45 | without being bound by section 3 of the GNU GPL.
46 |
47 | 2. Conveying Modified Versions.
48 |
49 | If you modify a copy of the Library, and, in your modifications, a
50 | facility refers to a function or data to be supplied by an Application
51 | that uses the facility (other than as an argument passed when the
52 | facility is invoked), then you may convey a copy of the modified
53 | version:
54 |
55 | a) under this License, provided that you make a good faith effort to
56 | ensure that, in the event an Application does not supply the
57 | function or data, the facility still operates, and performs
58 | whatever part of its purpose remains meaningful, or
59 |
60 | b) under the GNU GPL, with none of the additional permissions of
61 | this License applicable to that copy.
62 |
63 | 3. Object Code Incorporating Material from Library Header Files.
64 |
65 | The object code form of an Application may incorporate material from
66 | a header file that is part of the Library. You may convey such object
67 | code under terms of your choice, provided that, if the incorporated
68 | material is not limited to numerical parameters, data structure
69 | layouts and accessors, or small macros, inline functions and templates
70 | (ten or fewer lines in length), you do both of the following:
71 |
72 | a) Give prominent notice with each copy of the object code that the
73 | Library is used in it and that the Library and its use are
74 | covered by this License.
75 |
76 | b) Accompany the object code with a copy of the GNU GPL and this license
77 | document.
78 |
79 | 4. Combined Works.
80 |
81 | You may convey a Combined Work under terms of your choice that,
82 | taken together, effectively do not restrict modification of the
83 | portions of the Library contained in the Combined Work and reverse
84 | engineering for debugging such modifications, if you also do each of
85 | the following:
86 |
87 | a) Give prominent notice with each copy of the Combined Work that
88 | the Library is used in it and that the Library and its use are
89 | covered by this License.
90 |
91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license
92 | document.
93 |
94 | c) For a Combined Work that displays copyright notices during
95 | execution, include the copyright notice for the Library among
96 | these notices, as well as a reference directing the user to the
97 | copies of the GNU GPL and this license document.
98 |
99 | d) Do one of the following:
100 |
101 | 0) Convey the Minimal Corresponding Source under the terms of this
102 | License, and the Corresponding Application Code in a form
103 | suitable for, and under terms that permit, the user to
104 | recombine or relink the Application with a modified version of
105 | the Linked Version to produce a modified Combined Work, in the
106 | manner specified by section 6 of the GNU GPL for conveying
107 | Corresponding Source.
108 |
109 | 1) Use a suitable shared library mechanism for linking with the
110 | Library. A suitable mechanism is one that (a) uses at run time
111 | a copy of the Library already present on the user's computer
112 | system, and (b) will operate properly with a modified version
113 | of the Library that is interface-compatible with the Linked
114 | Version.
115 |
116 | e) Provide Installation Information, but only if you would otherwise
117 | be required to provide such information under section 6 of the
118 | GNU GPL, and only to the extent that such information is
119 | necessary to install and execute a modified version of the
120 | Combined Work produced by recombining or relinking the
121 | Application with a modified version of the Linked Version. (If
122 | you use option 4d0, the Installation Information must accompany
123 | the Minimal Corresponding Source and Corresponding Application
124 | Code. If you use option 4d1, you must provide the Installation
125 | Information in the manner specified by section 6 of the GNU GPL
126 | for conveying Corresponding Source.)
127 |
128 | 5. Combined Libraries.
129 |
130 | You may place library facilities that are a work based on the
131 | Library side by side in a single library together with other library
132 | facilities that are not Applications and are not covered by this
133 | License, and convey such a combined library under terms of your
134 | choice, if you do both of the following:
135 |
136 | a) Accompany the combined library with a copy of the same work based
137 | on the Library, uncombined with any other library facilities,
138 | conveyed under the terms of this License.
139 |
140 | b) Give prominent notice with the combined library that part of it
141 | is a work based on the Library, and explaining where to find the
142 | accompanying uncombined form of the same work.
143 |
144 | 6. Revised Versions of the GNU Lesser General Public License.
145 |
146 | The Free Software Foundation may publish revised and/or new versions
147 | of the GNU Lesser General Public License from time to time. Such new
148 | versions will be similar in spirit to the present version, but may
149 | differ in detail to address new problems or concerns.
150 |
151 | Each version is given a distinguishing version number. If the
152 | Library as you received it specifies that a certain numbered version
153 | of the GNU Lesser General Public License "or any later version"
154 | applies to it, you have the option of following the terms and
155 | conditions either of that published version or of any later version
156 | published by the Free Software Foundation. If the Library as you
157 | received it does not specify a version number of the GNU Lesser
158 | General Public License, you may choose any version of the GNU Lesser
159 | General Public License ever published by the Free Software Foundation.
160 |
161 | If the Library as you received it specifies that a proxy can decide
162 | whether future versions of the GNU Lesser General Public License shall
163 | apply, that proxy's public statement of acceptance of any version is
164 | permanent authorization for you to choose that version for the
165 | Library.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Nvidium
2 |
3 | [](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)
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'fabric-loom' version '1.7-SNAPSHOT'
3 | id 'maven-publish'
4 | }
5 |
6 | def gitCommitHash = { ->
7 | def stdout = new ByteArrayOutputStream()
8 | exec {
9 | commandLine 'git', 'rev-parse', '--short', 'HEAD'
10 | standardOutput = stdout
11 | }
12 | return stdout.toString().trim()
13 | }
14 | def buildtime = System.currentTimeSeconds()
15 |
16 | version = project.mod_version
17 | group = project.maven_group
18 |
19 | sourceCompatibility = JavaVersion.VERSION_21
20 | targetCompatibility = JavaVersion.VERSION_21
21 |
22 | apply plugin: "fabric-loom"
23 |
24 | loom {
25 | mixin.defaultRefmapName = "nvidium.refmap.json"
26 | runs {
27 | it.configureEach {
28 | vmArgs("-Xmx8G", "-XX:+UseZGC")
29 | }
30 | }
31 | }
32 |
33 |
34 | processResources {
35 | inputs.properties("version": project.version, "commit": gitCommitHash, "buildtime":buildtime)
36 |
37 | archivesBaseName = "nvidium"
38 | filesMatching("fabric.mod.json") {
39 | expand "commit": gitCommitHash, "version": project.version, "buildtime": buildtime
40 | }
41 | }
42 |
43 | repositories {
44 | exclusiveContent {
45 | forRepository {
46 | maven {
47 | name = "Modrinth"
48 | url = "https://api.modrinth.com/maven"
49 | }
50 | }
51 | filter {
52 | includeGroup "maven.modrinth"
53 | }
54 | }
55 | }
56 |
57 | dependencies {
58 | // To change the versions see the gradle.properties file
59 | minecraft "com.mojang:minecraft:${project.minecraft_version}"
60 | mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2"
61 | modImplementation "net.fabricmc:fabric-loader:${project.loader_version}"
62 |
63 | // Fabric API
64 | modImplementation(fabricApi.module("fabric-api-base", project.fabric_version))
65 | modImplementation(fabricApi.module("fabric-block-view-api-v2", project.fabric_version))
66 | modImplementation(fabricApi.module("fabric-rendering-fluids-v1", project.fabric_version))
67 |
68 | modImplementation("net.fabricmc.fabric-api:fabric-rendering-data-attachment-v1:0.3.40+73761d2e3b")
69 | modImplementation(fabricApi.module("fabric-resource-loader-v0", project.fabric_version))
70 |
71 | modImplementation "maven.modrinth:sodium:mc1.21-0.5.11"
72 |
73 | //modImplementation "maven.modrinth:c2me-fabric:0.2.0+alpha.10.49+1.19.4"
74 | //modImplementation "maven.modrinth:chunks-fade-in:v1.0.3-1.19.4"
75 | //modImplementation "maven.modrinth:immersiveportals:v2.7.3-mc1.19.4"
76 | modCompileOnly "maven.modrinth:iris:1.7.0+1.20.6"
77 | }
78 |
79 |
--------------------------------------------------------------------------------
/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
7 | yarn_mappings=1.21+build.2
8 | loader_version=0.15.11
9 |
10 | #Fabric api
11 | fabric_version=0.100.1+1.21
12 |
13 | # Mod Properties
14 | mod_version=0.3.0
15 | maven_group=me.cortex
16 | archives_base_name=nvidium
17 | # Dependencies
18 | # check this on https://modmuss50.me/fabric.html
19 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MCRcortex/nvidium/f2028b2ba9a5dc95d69e73a9eaeadcb447367d7d/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | #
4 | # Copyright © 2015-2021 the original authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | #
21 | # Gradle start up script for POSIX generated by Gradle.
22 | #
23 | # Important for running:
24 | #
25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
26 | # noncompliant, but you have some other compliant shell such as ksh or
27 | # bash, then to run this script, type that shell name before the whole
28 | # command line, like:
29 | #
30 | # ksh Gradle
31 | #
32 | # Busybox and similar reduced shells will NOT work, because this script
33 | # requires all of these POSIX shell features:
34 | # * functions;
35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»;
37 | # * compound commands having a testable exit status, especially «case»;
38 | # * various built-in commands including «command», «set», and «ulimit».
39 | #
40 | # Important for patching:
41 | #
42 | # (2) This script targets any POSIX shell, so it avoids extensions provided
43 | # by Bash, Ksh, etc; in particular arrays are avoided.
44 | #
45 | # The "traditional" practice of packing multiple parameters into a
46 | # space-separated string is a well documented source of bugs and security
47 | # problems, so this is (mostly) avoided, by progressively accumulating
48 | # options in "$@", and eventually passing that to Java.
49 | #
50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
52 | # see the in-line comments for details.
53 | #
54 | # There are tweaks for specific operating systems such as AIX, CygWin,
55 | # Darwin, MinGW, and NonStop.
56 | #
57 | # (3) This script is generated from the Groovy template
58 | # https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
59 | # within the Gradle project.
60 | #
61 | # You can find Gradle at https://github.com/gradle/gradle/.
62 | #
63 | ##############################################################################
64 |
65 | # Attempt to set APP_HOME
66 |
67 | # Resolve links: $0 may be a link
68 | app_path=$0
69 |
70 | # Need this for daisy-chained symlinks.
71 | while
72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
73 | [ -h "$app_path" ]
74 | do
75 | ls=$( ls -ld "$app_path" )
76 | link=${ls#*' -> '}
77 | case $link in #(
78 | /*) app_path=$link ;; #(
79 | *) app_path=$APP_HOME$link ;;
80 | esac
81 | done
82 |
83 | APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
84 |
85 | APP_NAME="Gradle"
86 | APP_BASE_NAME=${0##*/}
87 |
88 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
89 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
90 |
91 | # Use the maximum available, or set MAX_FD != -1 to use that value.
92 | MAX_FD=maximum
93 |
94 | warn () {
95 | echo "$*"
96 | } >&2
97 |
98 | die () {
99 | echo
100 | echo "$*"
101 | echo
102 | exit 1
103 | } >&2
104 |
105 | # OS specific support (must be 'true' or 'false').
106 | cygwin=false
107 | msys=false
108 | darwin=false
109 | nonstop=false
110 | case "$( uname )" in #(
111 | CYGWIN* ) cygwin=true ;; #(
112 | Darwin* ) darwin=true ;; #(
113 | MSYS* | MINGW* ) msys=true ;; #(
114 | NONSTOP* ) nonstop=true ;;
115 | esac
116 |
117 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
118 |
119 |
120 | # Determine the Java command to use to start the JVM.
121 | if [ -n "$JAVA_HOME" ] ; then
122 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
123 | # IBM's JDK on AIX uses strange locations for the executables
124 | JAVACMD=$JAVA_HOME/jre/sh/java
125 | else
126 | JAVACMD=$JAVA_HOME/bin/java
127 | fi
128 | if [ ! -x "$JAVACMD" ] ; then
129 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
130 |
131 | Please set the JAVA_HOME variable in your environment to match the
132 | location of your Java installation."
133 | fi
134 | else
135 | JAVACMD=java
136 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
137 |
138 | Please set the JAVA_HOME variable in your environment to match the
139 | location of your Java installation."
140 | fi
141 |
142 | # Increase the maximum file descriptors if we can.
143 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
144 | case $MAX_FD in #(
145 | max*)
146 | MAX_FD=$( ulimit -H -n ) ||
147 | warn "Could not query maximum file descriptor limit"
148 | esac
149 | case $MAX_FD in #(
150 | '' | soft) :;; #(
151 | *)
152 | ulimit -n "$MAX_FD" ||
153 | warn "Could not set maximum file descriptor limit to $MAX_FD"
154 | esac
155 | fi
156 |
157 | # Collect all arguments for the java command, stacking in reverse order:
158 | # * args from the command line
159 | # * the main class name
160 | # * -classpath
161 | # * -D...appname settings
162 | # * --module-path (only if needed)
163 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
164 |
165 | # For Cygwin or MSYS, switch paths to Windows format before running java
166 | if "$cygwin" || "$msys" ; then
167 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
168 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
169 |
170 | JAVACMD=$( cygpath --unix "$JAVACMD" )
171 |
172 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
173 | for arg do
174 | if
175 | case $arg in #(
176 | -*) false ;; # don't mess with options #(
177 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
178 | [ -e "$t" ] ;; #(
179 | *) false ;;
180 | esac
181 | then
182 | arg=$( cygpath --path --ignore --mixed "$arg" )
183 | fi
184 | # Roll the args list around exactly as many times as the number of
185 | # args, so each arg winds up back in the position where it started, but
186 | # possibly modified.
187 | #
188 | # NB: a `for` loop captures its iteration list before it begins, so
189 | # changing the positional parameters here affects neither the number of
190 | # iterations, nor the values presented in `arg`.
191 | shift # remove old arg
192 | set -- "$@" "$arg" # push replacement arg
193 | done
194 | fi
195 |
196 | # Collect all arguments for the java command;
197 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
198 | # shell script including quotes and variable substitutions, so put them in
199 | # double quotes to make sure that they get re-expanded; and
200 | # * put everything else in single quotes, so that it's not re-expanded.
201 |
202 | set -- \
203 | "-Dorg.gradle.appname=$APP_BASE_NAME" \
204 | -classpath "$CLASSPATH" \
205 | org.gradle.wrapper.GradleWrapperMain \
206 | "$@"
207 |
208 | # Use "xargs" to parse quoted args.
209 | #
210 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed.
211 | #
212 | # In Bash we could simply go:
213 | #
214 | # readarray ARGS < <( xargs -n1 <<<"$var" ) &&
215 | # set -- "${ARGS[@]}" "$@"
216 | #
217 | # but POSIX shell has neither arrays nor command substitution, so instead we
218 | # post-process each arg (as a line of input to sed) to backslash-escape any
219 | # character that might be a shell metacharacter, then use eval to reverse
220 | # that process (while maintaining the separation between arguments), and wrap
221 | # the whole thing up as a single "set" statement.
222 | #
223 | # This will of course break if any of these variables contains a newline or
224 | # an unmatched quote.
225 | #
226 |
227 | eval "set -- $(
228 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
229 | xargs -n1 |
230 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
231 | tr '\n' ' '
232 | )" '"$@"'
233 |
234 | exec "$JAVACMD" "$@"
235 |
--------------------------------------------------------------------------------
/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 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
34 |
35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
37 |
38 | @rem Find java.exe
39 | if defined JAVA_HOME goto findJavaFromJavaHome
40 |
41 | set JAVA_EXE=java.exe
42 | %JAVA_EXE% -version >NUL 2>&1
43 | if "%ERRORLEVEL%" == "0" goto execute
44 |
45 | echo.
46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
47 | echo.
48 | echo Please set the JAVA_HOME variable in your environment to match the
49 | echo location of your Java installation.
50 |
51 | goto fail
52 |
53 | :findJavaFromJavaHome
54 | set JAVA_HOME=%JAVA_HOME:"=%
55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
56 |
57 | if exist "%JAVA_EXE%" goto execute
58 |
59 | echo.
60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
61 | echo.
62 | echo Please set the JAVA_HOME variable in your environment to match the
63 | echo location of your Java installation.
64 |
65 | goto fail
66 |
67 | :execute
68 | @rem Setup the command line
69 |
70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
71 |
72 |
73 | @rem Execute Gradle
74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
75 |
76 | :end
77 | @rem End local scope for the variables with windows NT shell
78 | if "%ERRORLEVEL%"=="0" goto mainEnd
79 |
80 | :fail
81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
82 | rem the _cmd.exe /c_ return code!
83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
84 | exit /b 1
85 |
86 | :mainEnd
87 | if "%OS%"=="Windows_NT" endlocal
88 |
89 | :omega
90 |
--------------------------------------------------------------------------------
/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/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.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.getOperatingSystem() == Util.OperatingSystem.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/NvidiumWorldRenderer.java:
--------------------------------------------------------------------------------
1 | package me.cortex.nvidium;
2 |
3 | import me.cortex.nvidium.gl.RenderDevice;
4 | import me.cortex.nvidium.managers.AsyncOcclusionTracker;
5 | import me.cortex.nvidium.managers.SectionManager;
6 | import me.cortex.nvidium.sodiumCompat.NvidiumCompactChunkVertex;
7 | import me.cortex.nvidium.util.DownloadTaskStream;
8 | import me.cortex.nvidium.util.UploadingBufferStream;
9 | import me.jellysquid.mods.sodium.client.SodiumClientMod;
10 | import me.jellysquid.mods.sodium.client.render.chunk.ChunkRenderMatrices;
11 | import me.jellysquid.mods.sodium.client.render.chunk.RenderSection;
12 | import me.jellysquid.mods.sodium.client.render.chunk.compile.ChunkBuildOutput;
13 | import me.jellysquid.mods.sodium.client.render.chunk.vertex.format.impl.CompactChunkVertex;
14 | import me.jellysquid.mods.sodium.client.render.viewport.Viewport;
15 | import net.minecraft.client.render.Camera;
16 | import net.minecraft.client.texture.Sprite;
17 | import org.jetbrains.annotations.Nullable;
18 | import org.joml.Matrix4fc;
19 | import org.joml.Matrix4x3fc;
20 |
21 | import java.util.ArrayList;
22 | import java.util.Arrays;
23 | import java.util.List;
24 |
25 | import static org.lwjgl.opengl.GL11.glGetInteger;
26 | import static org.lwjgl.opengl.GL11.glNewList;
27 | import static org.lwjgl.opengl.NVXGPUMemoryInfo.GL_GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX;
28 |
29 | public class NvidiumWorldRenderer {
30 | private static final RenderDevice device = new RenderDevice();
31 |
32 | private final UploadingBufferStream uploadStream;
33 | private final DownloadTaskStream downloadStream;
34 |
35 | private final SectionManager sectionManager;
36 | private final RenderPipeline renderPipeline;
37 |
38 | private final AsyncOcclusionTracker asyncChunkTracker;
39 |
40 | //Max memory that the gpu can use to store geometry in mb
41 | private long max_geometry_memory;
42 | private long last_sample_time;
43 |
44 | //Note: the reason that asyncChunkTracker is passed in as an already constructed object is cause of the amount of argmuents it takes to construct it
45 | public NvidiumWorldRenderer(AsyncOcclusionTracker asyncChunkTracker) {
46 | int frames = SodiumClientMod.options().advanced.cpuRenderAheadLimit+1;
47 | //32 mb upload buffer
48 | this.uploadStream = new UploadingBufferStream(device, 32000000);
49 | //8 mb download buffer
50 | this.downloadStream = new DownloadTaskStream(device, frames, 8000000);
51 |
52 | update_allowed_memory();
53 | //this.sectionManager = new SectionManager(device, max_geometry_memory*1024*1024, uploadStream, 150, 24, CompactChunkVertex.STRIDE);
54 | this.sectionManager = new SectionManager(device, max_geometry_memory*1024*1024, uploadStream, NvidiumCompactChunkVertex.STRIDE, this);
55 | this.renderPipeline = new RenderPipeline(device, uploadStream, downloadStream, sectionManager);
56 |
57 |
58 | this.asyncChunkTracker = asyncChunkTracker;
59 | }
60 |
61 | public void enqueueRegionSort(int regionId) {
62 | this.renderPipeline.enqueueRegionSort(regionId);
63 | }
64 |
65 | public void delete() {
66 | uploadStream.delete();
67 | downloadStream.delete();
68 | renderPipeline.delete();
69 | if (asyncChunkTracker != null) {
70 | asyncChunkTracker.delete();
71 | }
72 |
73 | sectionManager.destroy();
74 | }
75 |
76 | public void reloadShaders() {
77 | renderPipeline.reloadShaders();
78 | }
79 |
80 | public void renderFrame(Viewport viewport, ChunkRenderMatrices matrices, double x, double y, double z) {
81 | renderPipeline.renderFrame(viewport, matrices, x, y, z);
82 |
83 | while (sectionManager.terrainAreana.getUsedMB() > (max_geometry_memory - 100)) {
84 | renderPipeline.removeARegion();
85 | }
86 |
87 | if (Nvidium.SUPPORTS_PERSISTENT_SPARSE_ADDRESSABLE_BUFFER && (System.currentTimeMillis() - last_sample_time) > 60000) {
88 | last_sample_time = System.currentTimeMillis();
89 | update_allowed_memory();
90 | }
91 | }
92 |
93 | public void renderTranslucent() {
94 | this.renderPipeline.renderTranslucent();
95 | }
96 |
97 | public void deleteSection(RenderSection section) {
98 | this.sectionManager.deleteSection(section);
99 | }
100 |
101 | public void uploadBuildResult(ChunkBuildOutput buildOutput) {
102 | this.sectionManager.uploadChunkBuildResult(buildOutput);
103 | }
104 |
105 | public void addDebugInfo(ArrayList debugInfo) {
106 | debugInfo.add("Using nvidium renderer: "+ Nvidium.MOD_VERSION);
107 | /*
108 | debugInfo.add("Memory limit: " + max_geometry_memory + " mb");
109 | debugInfo.add("Terrain Memory MB: " +);
110 | debugInfo.add(String.format("Fragmentation: %.2f", sectionManager.terrainAreana.getFragmentation()*100));
111 | debugInfo.add("Regions: " + sectionManager.getRegionManager().regionCount() + "/" + sectionManager.getRegionManager().maxRegions());
112 | */
113 | debugInfo.add("Mem" + (Nvidium.SUPPORTS_PERSISTENT_SPARSE_ADDRESSABLE_BUFFER?"":" (fallback)") + ": " + (Nvidium.SUPPORTS_PERSISTENT_SPARSE_ADDRESSABLE_BUFFER?this.sectionManager.terrainAreana.getAllocatedMB():this.sectionManager.terrainAreana.getUsedMB()) + "/"+ this.max_geometry_memory + String.format(", F: %.2f", sectionManager.terrainAreana.getFragmentation()*100));
114 | debugInfo.add("Regions: " + sectionManager.getRegionManager().regionCount() + "/" + sectionManager.getRegionManager().maxRegions());
115 | if (this.asyncChunkTracker != null) {
116 | debugInfo.add("A-BFS: " + asyncChunkTracker.getIterationTime() + " Q: " + Arrays.toString(this.asyncChunkTracker.getBuildQueueSizes()));//Async BFS iteration time:, Build queue sizes:
117 | }
118 | this.renderPipeline.addDebugInfo(debugInfo);
119 | }
120 |
121 |
122 | private void update_allowed_memory() {
123 | if (Nvidium.config.automatic_memory) {
124 | max_geometry_memory = (glGetInteger(GL_GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX) / 1024) + (sectionManager==null?0:sectionManager.terrainAreana.getMemoryUsed()/(1024*1024));
125 | max_geometry_memory -= 1024;//Minus 1gb of vram
126 | max_geometry_memory = Math.max(2048, max_geometry_memory);//Minimum 2 gb of vram
127 | } else {
128 | max_geometry_memory = Nvidium.config.max_geometry_memory;
129 | }
130 | }
131 |
132 | public void update(Camera camera, Viewport viewport, int frame, boolean spectator) {
133 | if (asyncChunkTracker != null) {
134 | asyncChunkTracker.update(viewport, camera, spectator);
135 | }
136 | }
137 |
138 | public int getAsyncFrameId() {
139 | if (asyncChunkTracker != null) {
140 | return asyncChunkTracker.getFrame();
141 | } else {
142 | return -1;
143 | }
144 | }
145 |
146 | public List getSectionsWithEntities() {
147 | if (asyncChunkTracker != null) {
148 | return asyncChunkTracker.getLatestSectionsWithEntities();
149 | } else {
150 | return List.of();
151 | }
152 | }
153 |
154 | public SectionManager getSectionManager() {
155 | return sectionManager;
156 | }
157 |
158 | @Nullable
159 | public Sprite[] getAnimatedSpriteSet() {
160 | if (asyncChunkTracker != null) {
161 | return asyncChunkTracker.getVisibleAnimatedSprites();
162 | } else {
163 | return new Sprite[0];
164 | }
165 | }
166 |
167 | public void setTransformation(int id, Matrix4fc transform) {
168 | this.renderPipeline.setTransformation(id, transform);
169 | }
170 |
171 | public void setOrigin(int id, int x, int y, int z) {
172 | this.renderPipeline.setOrigin(id, x, y, z);
173 | }
174 |
175 | public int getAsyncBfsVisibilityCount() {
176 | if (this.asyncChunkTracker != null) {
177 | return this.asyncChunkTracker.getLastVisibilityCount();
178 | } else {
179 | return -1;
180 | }
181 | }
182 | public int getMaxGeometryMemory() {
183 | return (int) max_geometry_memory;
184 | }
185 | }
186 |
--------------------------------------------------------------------------------
/src/main/java/me/cortex/nvidium/api0/NvidiumAPI.java:
--------------------------------------------------------------------------------
1 | package me.cortex.nvidium.api0;
2 |
3 | import me.cortex.nvidium.Nvidium;
4 | import me.cortex.nvidium.sodiumCompat.INvidiumWorldRendererGetter;
5 | import me.jellysquid.mods.sodium.client.render.SodiumWorldRenderer;
6 | import org.joml.Matrix4fc;
7 | import org.joml.Matrix4x3fc;
8 |
9 | public class NvidiumAPI {
10 | private final String modName;
11 | public NvidiumAPI(String modName) {
12 | this.modName = modName;
13 | }
14 |
15 | /***
16 | * Forces a render section to not render, guarantees the section will stay hidden until it is marked as visible
17 | * @param x sectionX pos
18 | * @param y sectionY pos
19 | * @param z sectionZ pos
20 | */
21 | public void hideSection(int x, int y, int z) {
22 | if (Nvidium.IS_ENABLED) {
23 | var renderer = ((INvidiumWorldRendererGetter) SodiumWorldRenderer.instance()).getRenderer();
24 | if (renderer != null) {
25 | renderer.getSectionManager().setHideBit(x, y, z, true);
26 | }
27 | }
28 | }
29 |
30 | /***
31 | * Unhides a render section if it was previously hidden
32 | * @param x sectionX pos
33 | * @param y sectionY pos
34 | * @param z sectionZ pos
35 | */
36 | public void showSection(int x, int y, int z) {
37 | if (Nvidium.IS_ENABLED) {
38 | var renderer = ((INvidiumWorldRendererGetter) SodiumWorldRenderer.instance()).getRenderer();
39 | if (renderer != null) {
40 | renderer.getSectionManager().setHideBit(x, y, z, false);
41 | }
42 | }
43 | }
44 |
45 | /***
46 | * Assigns a specified region to the supplied transformation id
47 | * @param id id to set the region too (all regions have the default id of 0)
48 | * @param x region X pos
49 | * @param y region Y pos
50 | * @param z region Z pos
51 | */
52 | public void setRegionTransformId(int id, int x, int y, int z) {
53 | if (Nvidium.IS_ENABLED) {
54 | var renderer = ((INvidiumWorldRendererGetter) SodiumWorldRenderer.instance()).getRenderer();
55 | if (renderer != null) {
56 | renderer.getSectionManager().getRegionManager().setRegionTransformId(x, y, z, id);
57 | }
58 | }
59 | }
60 |
61 | /***
62 | * Sets the transform for the supplied id
63 | * @param id The id to set the transform of
64 | * @param transform The transform to set it too
65 | */
66 | public void setTransformation(int id, Matrix4fc transform) {
67 | if (Nvidium.IS_ENABLED) {
68 | var renderer = ((INvidiumWorldRendererGetter) SodiumWorldRenderer.instance()).getRenderer();
69 | if (renderer != null) {
70 | renderer.setTransformation(id, transform);
71 | }
72 | }
73 | }
74 |
75 | /***
76 | * Sets the origin point of the transformation id, this is in chunk coordinates.
77 | * @param id The id to set the origin of
78 | * @param x Chunk coord x
79 | * @param y Chunk coord y
80 | * @param z Chunk coord z
81 | */
82 | public void setOrigin(int id, int x, int y, int z) {
83 | if (Nvidium.IS_ENABLED) {
84 | var renderer = ((INvidiumWorldRendererGetter) SodiumWorldRenderer.instance()).getRenderer();
85 | if (renderer != null) {
86 | renderer.setOrigin(id, x, y, z);
87 | }
88 | }
89 | }
90 |
91 | }
92 |
--------------------------------------------------------------------------------
/src/main/java/me/cortex/nvidium/config/ConfigGuiBuilder.java:
--------------------------------------------------------------------------------
1 | package me.cortex.nvidium.config;
2 |
3 | import com.google.common.collect.ImmutableList;
4 | import me.cortex.nvidium.Nvidium;
5 | import me.cortex.nvidium.sodiumCompat.NvidiumOptionFlags;
6 | import me.jellysquid.mods.sodium.client.gui.options.*;
7 | import me.jellysquid.mods.sodium.client.gui.options.control.ControlValueFormatter;
8 | import me.jellysquid.mods.sodium.client.gui.options.control.CyclingControl;
9 | import me.jellysquid.mods.sodium.client.gui.options.control.SliderControl;
10 | import me.jellysquid.mods.sodium.client.gui.options.control.TickBoxControl;
11 | import net.minecraft.text.Text;
12 |
13 | import java.util.ArrayList;
14 | import java.util.List;
15 |
16 | public class ConfigGuiBuilder {
17 | private static final NvidiumConfigStore store = new NvidiumConfigStore();
18 | public static void addNvidiumGui(List pages) {
19 | List groups = new ArrayList<>();
20 |
21 | groups.add(OptionGroup.createBuilder()
22 | .add(OptionImpl.createBuilder(boolean.class, store)
23 | .setName(Text.literal("Disable nvidium"))
24 | .setTooltip(Text.literal("Used to disable nvidium (DOES NOT SAVE, WILL RE-ENABLE AFTER A RE-LAUNCH)"))
25 | .setControl(TickBoxControl::new)
26 | .setImpact(OptionImpact.HIGH)
27 | .setBinding((opts, value) -> Nvidium.FORCE_DISABLE = value, opts -> Nvidium.FORCE_DISABLE)
28 | .setFlags(OptionFlag.REQUIRES_RENDERER_RELOAD)
29 | .build()
30 | ).build());
31 |
32 | if (Nvidium.IS_COMPATIBLE && !Nvidium.IS_ENABLED && !Nvidium.FORCE_DISABLE) {
33 | groups.add(OptionGroup.createBuilder()
34 | .add(OptionImpl.createBuilder(boolean.class, store)
35 | .setName(Text.literal("Nvidium disabled due to shaders being loaded"))
36 | .setTooltip(Text.literal("Nvidium disabled due to shaders being loaded"))
37 | .setControl(TickBoxControl::new)
38 | .setImpact(OptionImpact.VARIES)
39 | .setBinding((opts, value) -> {}, opts -> false)
40 | .setFlags()
41 | .build()
42 | ).build());
43 | }
44 | groups.add(OptionGroup.createBuilder()
45 | .add(OptionImpl.createBuilder(int.class, store)
46 | .setName(Text.translatable("nvidium.options.region_keep_distance.name"))
47 | .setTooltip(Text.translatable("nvidium.options.region_keep_distance.tooltip"))
48 | .setControl(option -> new SliderControl(option, 32, 256, 1, x->Text.literal(x==32?"Vanilla":(x==256?"Keep All":x+" chunks"))))
49 | .setImpact(OptionImpact.VARIES)
50 | .setEnabled(Nvidium.IS_ENABLED)
51 | .setBinding((opts, value) -> opts.region_keep_distance = value, opts -> opts.region_keep_distance)
52 | .setFlags()
53 | .build()
54 | ).add(OptionImpl.createBuilder(boolean.class, store)
55 | .setName(Text.translatable("nvidium.options.enable_temporal_coherence.name"))
56 | .setTooltip(Text.translatable("nvidium.options.enable_temporal_coherence.tooltip"))
57 | .setControl(TickBoxControl::new)
58 | .setImpact(OptionImpact.MEDIUM)
59 | .setEnabled(Nvidium.IS_ENABLED)
60 | .setBinding((opts, value) -> opts.enable_temporal_coherence = value, opts -> opts.enable_temporal_coherence)
61 | .setFlags()
62 | .build()
63 | ).add(OptionImpl.createBuilder(boolean.class, store)
64 | .setName(Text.translatable("nvidium.options.async_bfs.name"))
65 | .setTooltip(Text.translatable("nvidium.options.async_bfs.tooltip"))
66 | .setControl(TickBoxControl::new)
67 | .setImpact(OptionImpact.HIGH)
68 | .setEnabled(Nvidium.IS_ENABLED)
69 | .setBinding((opts, value) -> opts.async_bfs = value, opts -> opts.async_bfs)
70 | .setFlags(OptionFlag.REQUIRES_RENDERER_RELOAD)
71 | .build()
72 | ).add(OptionImpl.createBuilder(boolean.class, store)
73 | .setName(Text.translatable("nvidium.options.automatic_memory_limit.name"))
74 | .setTooltip(Text.translatable("nvidium.options.automatic_memory_limit.tooltip"))
75 | .setControl(TickBoxControl::new)
76 | .setImpact(OptionImpact.VARIES)
77 | .setEnabled(Nvidium.IS_ENABLED)
78 | .setBinding((opts, value) -> opts.automatic_memory = value, opts -> opts.automatic_memory)
79 | .setFlags()
80 | .build())
81 | .add(OptionImpl.createBuilder(int.class, store)
82 | .setName(Text.translatable("nvidium.options.max_gpu_memory.name"))
83 | .setTooltip(Text.translatable("nvidium.options.max_gpu_memory.tooltip"))
84 | .setControl(option -> new SliderControl(option, 2048, 32768, 512, ControlValueFormatter.translateVariable("nvidium.options.mb")))
85 | .setImpact(OptionImpact.VARIES)
86 | .setEnabled(Nvidium.IS_ENABLED && !Nvidium.config.automatic_memory)
87 | .setBinding((opts, value) -> opts.max_geometry_memory = value, opts -> opts.max_geometry_memory)
88 | .setFlags(Nvidium.SUPPORTS_PERSISTENT_SPARSE_ADDRESSABLE_BUFFER?new OptionFlag[0]:new OptionFlag[]{OptionFlag.REQUIRES_RENDERER_RELOAD})
89 | .build()
90 | ).add(OptionImpl.createBuilder(boolean.class, store)
91 | .setName(Text.translatable("nvidium.options.render_fog.name"))
92 | .setTooltip(Text.translatable("nvidium.options.render_fog.tooltip"))
93 | .setControl(TickBoxControl::new)
94 | .setBinding((opts, value) -> opts.render_fog = value, opts -> opts.render_fog)
95 | .setEnabled(Nvidium.IS_ENABLED)
96 | .setImpact(OptionImpact.MEDIUM)
97 | .setFlags(OptionFlag.REQUIRES_RENDERER_RELOAD)
98 | .build()
99 | ).add(OptionImpl.createBuilder(TranslucencySortingLevel.class, store)
100 | .setName(Text.translatable("nvidium.options.translucency_sorting.name"))
101 | .setTooltip(Text.translatable("nvidium.options.translucency_sorting.tooltip"))
102 | .setControl(
103 | opts -> new CyclingControl<>(
104 | opts,
105 | TranslucencySortingLevel.class,
106 | new Text[]{
107 | Text.translatable("nvidium.options.translucency_sorting.none"),
108 | Text.translatable("nvidium.options.translucency_sorting.sections"),
109 | Text.translatable("nvidium.options.translucency_sorting.quads")
110 | }
111 | )
112 | )
113 | .setBinding((opts, value) -> opts.translucency_sorting_level = value, opts -> opts.translucency_sorting_level)
114 | .setEnabled(Nvidium.IS_ENABLED)
115 | .setImpact(OptionImpact.MEDIUM)
116 | //Technically, only need to reload when going from NONE->SECTIONS
117 | .setFlags(OptionFlag.REQUIRES_RENDERER_RELOAD)
118 | .build()
119 | ).add(OptionImpl.createBuilder(StatisticsLoggingLevel.class, store)
120 | .setName(Text.translatable("nvidium.options.statistics_level.name"))
121 | .setTooltip(Text.translatable("nvidium.options.statistics_level.tooltip"))
122 | .setControl(
123 | opts -> new CyclingControl<>(
124 | opts,
125 | StatisticsLoggingLevel.class,
126 | new Text[]{
127 | Text.translatable("nvidium.options.statistics_level.none"),
128 | Text.translatable("nvidium.options.statistics_level.frustum"),
129 | Text.translatable("nvidium.options.statistics_level.regions"),
130 | Text.translatable("nvidium.options.statistics_level.sections"),
131 | Text.translatable("nvidium.options.statistics_level.quads")
132 | }
133 | )
134 | )
135 | .setBinding((opts, value) -> opts.statistics_level = value, opts -> opts.statistics_level)
136 | .setEnabled(Nvidium.IS_ENABLED)
137 | .setImpact(OptionImpact.LOW)
138 | .setFlags(NvidiumOptionFlags.REQUIRES_SHADER_RELOAD)
139 | .build()
140 | )
141 | .build());
142 | if (Nvidium.IS_COMPATIBLE) {
143 | pages.add(new OptionPage(Text.translatable("nvidium.options.pages.nvidium"), ImmutableList.copyOf(groups)));
144 | }
145 | }
146 | }
147 |
--------------------------------------------------------------------------------
/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 int extra_rd = 100;
18 | public boolean enable_temporal_coherence = true;
19 | public int max_geometry_memory = 2048;
20 | public boolean automatic_memory = true;
21 |
22 | public boolean async_bfs = true;
23 |
24 | public int region_keep_distance = 32;
25 |
26 | public boolean render_fog = true;
27 | public TranslucencySortingLevel translucency_sorting_level = TranslucencySortingLevel.QUADS;
28 |
29 | public StatisticsLoggingLevel statistics_level = StatisticsLoggingLevel.NONE;
30 |
31 |
32 | private static final Gson GSON = new GsonBuilder()
33 | .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
34 | .setPrettyPrinting()
35 | .excludeFieldsWithModifiers(Modifier.PRIVATE)
36 | .create();
37 |
38 | private NvidiumConfig() {}
39 | public static NvidiumConfig loadOrCreate() {
40 | var path = getConfigPath();
41 | if (Files.exists(path)) {
42 | try (FileReader reader = new FileReader(path.toFile())) {
43 | return GSON.fromJson(reader, NvidiumConfig.class);
44 | } catch (IOException e) {
45 | Nvidium.LOGGER.error("Could not parse config", e);
46 | }
47 | }
48 | return new NvidiumConfig();
49 | }
50 |
51 | public void save() {
52 | //Unsafe, todo: fixme! needs to be atomic!
53 | try {
54 | Files.writeString(getConfigPath(), GSON.toJson(this));
55 | } catch (IOException e) {
56 | Nvidium.LOGGER.error("Failed to write config file", e);
57 | }
58 | }
59 |
60 | private static Path getConfigPath() {
61 | return FabricLoader.getInstance()
62 | .getConfigDir()
63 | .resolve("nvidium-config.json");
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/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 me.jellysquid.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/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 | }
10 |
--------------------------------------------------------------------------------
/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 | }
8 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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/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/TrackedObject.java:
--------------------------------------------------------------------------------
1 | package me.cortex.nvidium.gl;
2 |
3 | import me.cortex.nvidium.Nvidium;
4 |
5 | import java.lang.ref.Cleaner;
6 |
7 | public abstract class TrackedObject {
8 | private final Ref ref;
9 | public TrackedObject() {
10 | this.ref = register(this);
11 | }
12 |
13 | protected void free0() {
14 | if (this.isFreed()) {
15 | throw new IllegalStateException("Object " + this + " was double freed.");
16 | }
17 | this.ref.freedRef[0] = true;
18 | this.ref.cleanable.clean();
19 | }
20 |
21 | public abstract void free();
22 |
23 | public void assertNotFreed() {
24 | if (isFreed()) {
25 | throw new IllegalStateException("Object " + this + " should not be free, but is");
26 | }
27 | }
28 |
29 | public boolean isFreed() {
30 | return this.ref.freedRef[0];
31 | }
32 |
33 | public record Ref(Cleaner.Cleanable cleanable, boolean[] freedRef) {}
34 |
35 | private static final Cleaner cleaner = Cleaner.create();
36 | public static Ref register(Object obj) {
37 | String clazz = obj.getClass().getName();
38 | Throwable trace;
39 | if (Nvidium.IS_DEBUG) {
40 | trace = new Throwable();
41 | } else {
42 | trace = null;
43 | }
44 | boolean[] freed = new boolean[1];
45 | var clean = cleaner.register(obj, ()->{
46 | if (!freed[0]) {
47 | System.err.println("Object named: "+ clazz+" was not freed, location at:\n");
48 | if (trace != null) {
49 | trace.printStackTrace();
50 | } else {
51 | System.err.println("Unknown location, turn on debug mode");
52 | }
53 | System.err.flush();
54 | }
55 | });
56 | return new Ref(clean, freed);
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/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/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/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/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/gl/buffers/PersistentSparseAddressableBuffer.java:
--------------------------------------------------------------------------------
1 | package me.cortex.nvidium.gl.buffers;
2 |
3 | import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
4 | import me.cortex.nvidium.Nvidium;
5 | import me.cortex.nvidium.gl.GlObject;
6 | import org.lwjgl.opengl.ARBSparseBuffer;
7 | import org.lwjgl.opengl.GL15;
8 | import org.lwjgl.opengl.GL21;
9 |
10 | import static org.lwjgl.opengl.ARBDirectStateAccess.glCreateBuffers;
11 | import static org.lwjgl.opengl.ARBDirectStateAccess.glNamedBufferStorage;
12 | import static org.lwjgl.opengl.ARBSparseBuffer.GL_SPARSE_STORAGE_BIT_ARB;
13 | import static org.lwjgl.opengl.GL15C.GL_READ_WRITE;
14 | import static org.lwjgl.opengl.GL15C.glDeleteBuffers;
15 | import static org.lwjgl.opengl.NVShaderBufferLoad.*;
16 |
17 | public class PersistentSparseAddressableBuffer extends GlObject implements IDeviceMappedBuffer {
18 | public static long alignUp(long number, long alignment) {
19 | long delta = number % alignment;
20 | return delta == 0?number: number + (alignment - delta);
21 | }
22 |
23 | public final long addr;
24 | public final long size;
25 |
26 | //The reason the page size is now 1mb is cause the nv driver doesnt defrag the sparse allocations easily
27 | // meaning smaller pages result in more fragmented memory and not happy for the driver
28 | // 1mb seems to work well
29 | public static final long PAGE_SIZE = 1<<20;//16
30 |
31 | public PersistentSparseAddressableBuffer(long size) {
32 | super(glCreateBuffers());
33 | if (!Nvidium.SUPPORTS_PERSISTENT_SPARSE_ADDRESSABLE_BUFFER) {
34 | throw new IllegalStateException();
35 | }
36 | this.size = alignUp(size, PAGE_SIZE);
37 | glNamedBufferStorage(id, size, GL_SPARSE_STORAGE_BIT_ARB);
38 | long[] holder = new long[1];
39 | glMakeNamedBufferResidentNV(id, GL_READ_WRITE);
40 | glGetNamedBufferParameterui64vNV(id, GL_BUFFER_GPU_ADDRESS_NV, holder);
41 | addr = holder[0];
42 | if (addr == 0) {
43 | throw new IllegalStateException();
44 | }
45 | }
46 |
47 | private static void doCommit(int buffer, long offset, long size, boolean commit) {
48 | GL21.glBindBuffer(GL15.GL_ARRAY_BUFFER, buffer);
49 | ARBSparseBuffer.glBufferPageCommitmentARB(GL15.GL_ARRAY_BUFFER, offset, size, commit);
50 | }
51 |
52 | private final Int2IntOpenHashMap allocationCount = new Int2IntOpenHashMap();
53 |
54 | private void allocatePages(int page, int pageCount) {
55 | doCommit(id, PAGE_SIZE * page, PAGE_SIZE * pageCount, true);
56 | for (int i = 0; i < pageCount; i++) {
57 | allocationCount.addTo(i+page,1);
58 | }
59 | }
60 |
61 | private void deallocatePages(int page, int pageCount) {
62 | for (int i = 0; i < pageCount; i++) {
63 | int newCount = allocationCount.get(i+page) - 1;
64 | if (newCount != 0) {
65 | allocationCount.put(i+page, newCount);
66 | } else {
67 | allocationCount.remove(i+page);
68 | doCommit(id, PAGE_SIZE * (page+i), PAGE_SIZE,false);
69 | }
70 | }
71 | }
72 |
73 | public int getPagesCommitted() {
74 | return allocationCount.size();
75 | }
76 |
77 | public void ensureAllocated(long addr, long size) {
78 | int pstart = (int) (addr/PAGE_SIZE);
79 | int pend = (int) ((addr+size+PAGE_SIZE-1)/PAGE_SIZE);
80 | allocatePages(pstart, pend-pstart);
81 | }
82 |
83 | public void deallocate(long addr, long size) {
84 | int pstart = (int) (addr/PAGE_SIZE);
85 | int pend = (int) ((addr+size+PAGE_SIZE-1)/PAGE_SIZE);
86 | deallocatePages(pstart, pend-pstart);
87 | }
88 |
89 | @Override
90 | public long getDeviceAddress() {
91 | return addr;
92 | }
93 |
94 | public void delete() {
95 | super.free0();
96 | glMakeNamedBufferNonResidentNV(id);
97 | glDeleteBuffers(id);
98 | }
99 |
100 | @Override
101 | public void free() {
102 | this.delete();
103 | }
104 |
105 | @Override
106 | public long getSize() {
107 | return size;
108 | }
109 | }
--------------------------------------------------------------------------------
/src/main/java/me/cortex/nvidium/gl/images/DepthOnlyFrameBuffer.java:
--------------------------------------------------------------------------------
1 | package me.cortex.nvidium.gl.images;
2 |
3 | import com.mojang.blaze3d.platform.GlConst;
4 | import com.mojang.blaze3d.platform.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/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/gl/shader/Shader.java:
--------------------------------------------------------------------------------
1 | package me.cortex.nvidium.gl.shader;
2 |
3 | import me.cortex.nvidium.gl.GlObject;
4 | import org.lwjgl.opengl.GL20C;
5 |
6 | import java.util.HashMap;
7 | import java.util.Map;
8 |
9 | import static org.lwjgl.opengl.GL20.glDeleteProgram;
10 | import static org.lwjgl.opengl.GL20.glUseProgram;
11 |
12 | public class Shader extends GlObject {
13 | private Shader(int program) {
14 | super(program);
15 | }
16 |
17 | public static Builder make(IShaderProcessor processor) {
18 | return new Builder(processor);
19 | }
20 |
21 | public static Builder make() {
22 | return new Builder((aa,source)->source);
23 | }
24 |
25 | public void bind() {
26 | glUseProgram(id);
27 | }
28 |
29 | public void delete() {
30 | super.free0();
31 | glDeleteProgram(id);
32 | }
33 |
34 | @Override
35 | public void free() {
36 | this.delete();
37 | }
38 |
39 | public static class Builder {
40 | private final Map sources = new HashMap<>();
41 | private final IShaderProcessor processor;
42 | private Builder(IShaderProcessor processor) {
43 | this.processor = processor;
44 | }
45 | public Builder addSource(ShaderType type, String source) {
46 | sources.put(type, processor.process(type, source));
47 | return this;
48 | }
49 |
50 | public Shader compile() {
51 | int program = GL20C.glCreateProgram();
52 | int[] shaders = sources.entrySet().stream().mapToInt(a->createShader(a.getKey(), a.getValue())).toArray();
53 |
54 | for (int i : shaders) {
55 | GL20C.glAttachShader(program, i);
56 | }
57 | GL20C.glLinkProgram(program);
58 | for (int i : shaders) {
59 | GL20C.glDetachShader(program, i);
60 | GL20C.glDeleteShader(i);
61 | }
62 | printProgramLinkLog(program);
63 | verifyProgramLinked(program);
64 | return new Shader(program);
65 | }
66 |
67 |
68 | private static void printProgramLinkLog(int program) {
69 | String log = GL20C.glGetProgramInfoLog(program);
70 |
71 | if (!log.isEmpty()) {
72 | System.err.println(log);
73 | }
74 | }
75 |
76 | private static void verifyProgramLinked(int program) {
77 | int result = GL20C.glGetProgrami(program, GL20C.GL_LINK_STATUS);
78 |
79 | if (result != GL20C.GL_TRUE) {
80 | throw new RuntimeException("Shader program linking failed, see log for details");
81 | }
82 | }
83 |
84 | private static int createShader(ShaderType type, String src) {
85 | int shader = GL20C.glCreateShader(type.gl);
86 | GL20C.glShaderSource(shader, src);
87 | GL20C.glCompileShader(shader);
88 | String log = GL20C.glGetShaderInfoLog(shader);
89 |
90 | if (!log.isEmpty()) {
91 | System.err.println(log);
92 | }
93 |
94 | int result = GL20C.glGetShaderi(shader, GL20C.GL_COMPILE_STATUS);
95 |
96 | if (result != GL20C.GL_TRUE) {
97 | GL20C.glDeleteShader(shader);
98 |
99 | throw new RuntimeException("Shader compilation failed, see log for details");
100 | }
101 |
102 | return shader;
103 | }
104 | }
105 |
106 | }
107 |
--------------------------------------------------------------------------------
/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/java/me/cortex/nvidium/managers/RegionVisibilityTracker.java:
--------------------------------------------------------------------------------
1 | package me.cortex.nvidium.managers;
2 |
3 | import me.cortex.nvidium.gl.RenderDevice;
4 | import me.cortex.nvidium.gl.buffers.Buffer;
5 | import me.cortex.nvidium.gl.shader.Shader;
6 | import me.cortex.nvidium.sodiumCompat.ShaderLoader;
7 | import me.cortex.nvidium.util.DownloadTaskStream;
8 | import net.minecraft.util.Identifier;
9 | import org.lwjgl.system.MemoryUtil;
10 |
11 | import static me.cortex.nvidium.gl.shader.ShaderType.FRAGMENT;
12 | import static me.cortex.nvidium.gl.shader.ShaderType.MESH;
13 | import static org.lwjgl.opengl.GL42.GL_COMMAND_BARRIER_BIT;
14 | import static org.lwjgl.opengl.GL42.glMemoryBarrier;
15 | import static org.lwjgl.opengl.GL43C.GL_SHADER_STORAGE_BARRIER_BIT;
16 | import static org.lwjgl.opengl.NVMeshShader.glDrawMeshTasksNV;
17 |
18 | public class RegionVisibilityTracker {
19 | private final Shader shader = Shader.make()
20 | .addSource(MESH, ShaderLoader.parse(Identifier.of("nvidium", "occlusion/queries/region/mesh.glsl")))
21 | .addSource(FRAGMENT, ShaderLoader.parse(Identifier.of("nvidium", "occlusion/queries/region/fragment.frag")))
22 | .compile();
23 |
24 | private final DownloadTaskStream downStream;
25 | private final int[] frustum;
26 | private final int[] visible;
27 | public RegionVisibilityTracker(DownloadTaskStream downStream, int maxRegions) {
28 | this.downStream = downStream;
29 | visible = new int[maxRegions];
30 | frustum = new int[maxRegions];
31 | for (int i = 0; i < maxRegions; i++) {
32 | frustum[i] = 0;
33 | visible[i] = 0;
34 | }
35 | }
36 |
37 | private int fram = 0;
38 | //This is kind of evil in the fact that it just reuses the visibility buffer
39 | public void computeVisibility(int regionCount, Buffer regionVisibilityBuffer, short[] regionMapping) {
40 | shader.bind();
41 | fram++;
42 | glDrawMeshTasksNV(0,regionCount);
43 | glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT);
44 | downStream.download(regionVisibilityBuffer, 0, regionCount, ptr -> {
45 | for (int i = 0; i < regionMapping.length; i++) {
46 | if (MemoryUtil.memGetByte(ptr + i) == 1) {
47 | //System.out.println(regionMapping[i] + " was visible");
48 | frustum[regionMapping[i]]++;
49 | visible[regionMapping[i]] = fram;
50 | } else {
51 | //System.out.println(regionMapping[i] + " was not visible");
52 | frustum[regionMapping[i]]++;
53 | }
54 | }
55 | });
56 | }
57 |
58 |
59 | public void delete() {
60 | shader.delete();
61 | }
62 |
63 | public void resetRegion(int id) {
64 | frustum[id] = 0;
65 | visible[id] = 0;
66 | }
67 |
68 | public int findMostLikelyLeastSeenRegion(int maxIndex) {
69 | int maxRank = Integer.MIN_VALUE;
70 | int id = -1;
71 | for (int i = 0; i < maxIndex; i++) {
72 | if (frustum[i] <= 200) continue;
73 | int rank = - visible[i];
74 | //int rank = -visible[i];
75 | if (maxRank < rank) {
76 | maxRank = rank;
77 | id = i;
78 | }
79 | }
80 | return id;
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/src/main/java/me/cortex/nvidium/managers/SectionManager.java:
--------------------------------------------------------------------------------
1 | package me.cortex.nvidium.managers;
2 |
3 | import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap;
4 | import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
5 | import it.unimi.dsi.fastutil.longs.LongSet;
6 | import me.cortex.nvidium.Nvidium;
7 | import me.cortex.nvidium.NvidiumWorldRenderer;
8 | import me.cortex.nvidium.gl.RenderDevice;
9 | import me.cortex.nvidium.sodiumCompat.INvidiumWorldRendererGetter;
10 | import me.cortex.nvidium.sodiumCompat.IRepackagedResult;
11 | import me.cortex.nvidium.util.BufferArena;
12 | import me.cortex.nvidium.util.SegmentedManager;
13 | import me.cortex.nvidium.util.UploadingBufferStream;
14 | import me.jellysquid.mods.sodium.client.render.SodiumWorldRenderer;
15 | import me.jellysquid.mods.sodium.client.render.chunk.RenderSection;
16 | import me.jellysquid.mods.sodium.client.render.chunk.compile.ChunkBuildOutput;
17 | import net.minecraft.client.MinecraftClient;
18 | import net.minecraft.util.math.ChunkSectionPos;
19 | import org.joml.Vector3i;
20 | import org.joml.Vector4i;
21 | import org.lwjgl.system.MemoryUtil;
22 |
23 | public class SectionManager {
24 | public static final int SECTION_SIZE = 32;
25 |
26 | //Sections should be grouped and batched into sizes of the count of sections in a region
27 | private final RegionManager regionManager;
28 |
29 | private final Long2IntOpenHashMap section2id = new Long2IntOpenHashMap();
30 | private final Long2IntOpenHashMap section2terrain = new Long2IntOpenHashMap();
31 |
32 | public final UploadingBufferStream uploadStream;
33 | public final BufferArena terrainAreana;
34 |
35 | private final RenderDevice device;
36 |
37 | private final LongSet hiddenSectionKeys = new LongOpenHashSet();
38 |
39 | public SectionManager(RenderDevice device, long fallbackMemorySize, UploadingBufferStream uploadStream, int quadVertexSize, NvidiumWorldRenderer worldRenderer) {
40 | int maxRegions = 50_000;
41 |
42 | this.device = device;
43 | this.uploadStream = uploadStream;
44 |
45 | this.terrainAreana = new BufferArena(device, fallbackMemorySize, quadVertexSize);
46 | this.regionManager = new RegionManager(device, maxRegions, maxRegions * 200, uploadStream, worldRenderer::enqueueRegionSort);
47 |
48 | this.section2id.defaultReturnValue(-1);
49 | this.section2terrain.defaultReturnValue(-1);
50 | }
51 |
52 | public void uploadChunkBuildResult(ChunkBuildOutput result) {
53 | var output = ((IRepackagedResult)result).getOutput();
54 |
55 | RenderSection section = result.render;
56 | long sectionKey = ChunkSectionPos.asLong(section.getChunkX(), section.getChunkY(), section.getChunkZ());
57 |
58 | if (output == null || output.quads() == 0) {
59 | deleteSection(sectionKey);
60 | return;
61 | }
62 |
63 | int terrainAddress;
64 | {
65 | //Attempt to reuse the same memory
66 | terrainAddress = this.section2terrain.get(sectionKey);
67 | if (terrainAddress != -1 && !this.terrainAreana.canReuse(terrainAddress, output.quads())) {
68 | this.section2terrain.remove(sectionKey);
69 | this.terrainAreana.free(terrainAddress);
70 | terrainAddress = -1;
71 | }
72 |
73 | if (terrainAddress == -1) {
74 | terrainAddress = this.terrainAreana.allocQuads(output.quads());
75 | }
76 |
77 | if (terrainAddress == SegmentedManager.SIZE_LIMIT) {
78 | Nvidium.LOGGER.error("Terrain arena critically out of memory, expect issues with chunks!! " +
79 | " quad_used: " + this.terrainAreana.getUsedMB() +
80 | " physically used: " + this.terrainAreana.getMemoryUsed() +
81 | " limit: " + ((INvidiumWorldRendererGetter)(SodiumWorldRenderer.instance())).getRenderer().getMaxGeometryMemory());
82 |
83 | deleteSection(sectionKey);
84 | return;
85 | }
86 |
87 | this.section2terrain.put(sectionKey, terrainAddress);
88 |
89 | long geometryUpload = terrainAreana.upload(uploadStream, terrainAddress);
90 | MemoryUtil.memCopy(MemoryUtil.memAddress(output.geometry().getDirectBuffer()), geometryUpload, output.geometry().getLength());
91 | }
92 |
93 |
94 |
95 | //Get the section id or allocate a new instance for it
96 | int sectionIdx = this.section2id.computeIfAbsent(
97 | sectionKey,
98 | key -> this.regionManager.allocateSection(ChunkSectionPos.unpackX(key), ChunkSectionPos.unpackY(key), ChunkSectionPos.unpackZ(key))
99 | );
100 |
101 |
102 | long metadata = regionManager.setSectionData(sectionIdx);
103 | boolean hideSectionBitSet = this.hiddenSectionKeys.contains(sectionKey);
104 | Vector3i min = output.min();
105 | Vector3i size = output.size();
106 |
107 |
108 | //bits 18->26 taken by section id (used for translucency sorting/rendering)
109 | // 26->32 is free
110 | int px = section.getChunkX()<<8 | size.x<<4 | min.x;
111 | int py = (section.getChunkY()&0x1FF)<<8 | size.y<<4 | min.y | (hideSectionBitSet?1<<17:0) | ((regionManager.getSectionRefId(sectionIdx))<<18);
112 | int pz = section.getChunkZ()<<8 | size.z<<4 | min.z;
113 | int pw = terrainAddress;
114 | new Vector4i(px, py, pz, pw).getToAddress(metadata);
115 | metadata += 4*4;
116 |
117 | //Write the geometry offsets, packed into ints
118 | for (int i = 0; i < 4; i++) {
119 | int geo = Short.toUnsignedInt(output.offsets()[i*2])|(Short.toUnsignedInt(output.offsets()[i*2+1])<<16);
120 | MemoryUtil.memPutInt(metadata, geo);
121 | metadata += 4;
122 | }
123 | }
124 |
125 | public void setHideBit(int x, int y, int z, boolean hide) {
126 | long sectionKey = ChunkSectionPos.asLong(x, y, z);
127 |
128 | if (hide) {
129 | //Do a fast return if it was already hidden
130 | if (!this.hiddenSectionKeys.add(sectionKey)) {
131 | return;
132 | }
133 | } else {
134 | //Do a fast return if the section was not hidden
135 | if (!this.hiddenSectionKeys.remove(sectionKey)) {
136 | return;
137 | }
138 | }
139 |
140 | int sectionId = this.section2id.get(sectionKey);
141 | //Only update the section if it is loaded
142 | if (sectionId != -1) {
143 | long metadata = this.regionManager.setSectionData(sectionId);
144 | MemoryUtil.memPutInt(metadata + 4, (MemoryUtil.memGetInt(metadata + 4)&~(1<<17))| (hide?1:0)<<17);
145 | }
146 | }
147 |
148 | public void deleteSection(RenderSection section) {
149 | deleteSection(ChunkSectionPos.asLong(section.getChunkX(), section.getChunkY(), section.getChunkZ()));
150 | }
151 |
152 | private void deleteSection(long sectionKey) {
153 | int sectionIdx = this.section2id.remove(sectionKey);
154 | if (sectionIdx != -1) {
155 | int terrainIndex = this.section2terrain.remove(sectionKey);
156 | if (terrainIndex != -1) {
157 | this.terrainAreana.free(terrainIndex);
158 | }
159 | //Clear the segment
160 | this.regionManager.removeSection(sectionIdx);
161 | }
162 | }
163 |
164 | public void destroy() {
165 | this.regionManager.destroy();
166 | this.terrainAreana.delete();
167 | }
168 |
169 | public void commitChanges() {
170 | this.regionManager.commitChanges();
171 | }
172 |
173 | public RegionManager getRegionManager() {
174 | return regionManager;
175 | }
176 |
177 | public void removeRegionById(int regionId) {
178 | if (!this.regionManager.regionExists(regionId)) return;
179 | long rk = this.regionManager.regionIdToKey(regionId);
180 | int X = ChunkSectionPos.unpackX(rk)<<3;
181 | int Y = ChunkSectionPos.unpackY(rk)<<2;
182 | int Z = ChunkSectionPos.unpackZ(rk)<<3;
183 | for (int x = X; x < X+8; x++) {
184 | for (int y = Y; y < Y+4; y++) {
185 | for (int z = Z; z < Z+8; z++) {
186 | this.deleteSection(ChunkSectionPos.asLong(x, y, z));
187 | }
188 | }
189 | }
190 | }
191 | }
192 |
193 |
194 |
195 |
196 |
--------------------------------------------------------------------------------
/src/main/java/me/cortex/nvidium/mixin/minecraft/LightMapAccessor.java:
--------------------------------------------------------------------------------
1 | package me.cortex.nvidium.mixin.minecraft;
2 |
3 | import net.minecraft.client.render.LightmapTextureManager;
4 | import net.minecraft.client.texture.NativeImageBackedTexture;
5 | import org.spongepowered.asm.mixin.Mixin;
6 | import org.spongepowered.asm.mixin.gen.Accessor;
7 |
8 | @Mixin(LightmapTextureManager.class)
9 | public interface LightMapAccessor {
10 | @Accessor()
11 | NativeImageBackedTexture getTexture();
12 | }
13 |
--------------------------------------------------------------------------------
/src/main/java/me/cortex/nvidium/mixin/minecraft/MixinBackgroundRenderer.java:
--------------------------------------------------------------------------------
1 | package me.cortex.nvidium.mixin.minecraft;
2 |
3 | import me.cortex.nvidium.Nvidium;
4 | import net.minecraft.client.render.BackgroundRenderer;
5 | import org.spongepowered.asm.mixin.Mixin;
6 | import org.spongepowered.asm.mixin.injection.Constant;
7 | import org.spongepowered.asm.mixin.injection.ModifyConstant;
8 |
9 | @Mixin(BackgroundRenderer.class)
10 |
11 | public class MixinBackgroundRenderer {
12 | @ModifyConstant(method = "applyFog", constant = @Constant(floatValue = 192.0F))
13 | private static float changeFog(float fog) {
14 | if (Nvidium.IS_ENABLED) {
15 | return 9999999f;
16 | } else {
17 | return fog;
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/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.render.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 = "getFarPlaneDistance", 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/java/me/cortex/nvidium/mixin/minecraft/MixinWindow.java:
--------------------------------------------------------------------------------
1 | package me.cortex.nvidium.mixin.minecraft;
2 |
3 | import me.cortex.nvidium.Nvidium;
4 | import net.minecraft.client.WindowEventHandler;
5 | import net.minecraft.client.WindowSettings;
6 | import net.minecraft.client.util.MonitorTracker;
7 | import net.minecraft.client.util.Window;
8 | import org.spongepowered.asm.mixin.Mixin;
9 | import org.spongepowered.asm.mixin.injection.At;
10 | import org.spongepowered.asm.mixin.injection.Inject;
11 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
12 |
13 | @Mixin(Window.class)
14 | public class MixinWindow {
15 | @Inject(method = "", at = @At(value = "INVOKE", target = "Lorg/lwjgl/opengl/GL;createCapabilities()Lorg/lwjgl/opengl/GLCapabilities;", shift = At.Shift.AFTER))
16 | private void init(WindowEventHandler eventHandler, MonitorTracker monitorTracker, WindowSettings settings, String videoMode, String title, CallbackInfo ci) {
17 | Nvidium.checkSystemIsCapable();
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/main/java/me/cortex/nvidium/mixin/minecraft/MixinWorldRenderer.java:
--------------------------------------------------------------------------------
1 | package me.cortex.nvidium.mixin.minecraft;
2 |
3 | import me.cortex.nvidium.Nvidium;
4 | import net.minecraft.client.render.GameRenderer;
5 | import net.minecraft.client.render.WorldRenderer;
6 | import org.spongepowered.asm.mixin.Mixin;
7 | import org.spongepowered.asm.mixin.injection.At;
8 | import org.spongepowered.asm.mixin.injection.Redirect;
9 |
10 | @Mixin(WorldRenderer.class)
11 | public class MixinWorldRenderer {
12 | @Redirect(method = "render", at = @At(value = "INVOKE", target = "Ljava/lang/Math;max(FF)F"), require = 0)
13 | private float redirectMax(float a, float b) {
14 | if (Nvidium.IS_ENABLED) {
15 | return a;
16 | }
17 | return Math.max(a, b);
18 | }
19 |
20 | @Redirect(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/GameRenderer;getViewDistance()F"))
21 | private float changeRD(GameRenderer instance) {
22 | float viewDistance = instance.getViewDistance();
23 | if (Nvidium.IS_ENABLED) {
24 | var dist = Nvidium.config.region_keep_distance * 16;
25 | return dist == 32 * 16 ? viewDistance : (dist == 256 * 16 ? 9999999 : dist);
26 | }
27 | return viewDistance;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/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 me.jellysquid.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 = "delete", 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/mixin/sodium/MixinChunkBuilder.java:
--------------------------------------------------------------------------------
1 | package me.cortex.nvidium.mixin.sodium;
2 |
3 | import me.cortex.nvidium.Nvidium;
4 | import me.jellysquid.mods.sodium.client.render.chunk.compile.executor.ChunkBuilder;
5 | import org.spongepowered.asm.mixin.Mixin;
6 | import org.spongepowered.asm.mixin.injection.At;
7 | import org.spongepowered.asm.mixin.injection.Redirect;
8 |
9 | import java.util.List;
10 |
11 | @Mixin(value = ChunkBuilder.class, remap = false)
12 | public class MixinChunkBuilder {
13 | @Redirect(method = "getSchedulingBudget", at = @At(value = "INVOKE", target = "Ljava/util/List;size()I"))
14 | private int moreSchedulingBudget(List threads) {
15 | int budget = threads.size();
16 | if (Nvidium.IS_ENABLED && Nvidium.config.async_bfs) {
17 | budget *= 3;
18 | }
19 | return budget;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/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 me.jellysquid.mods.sodium.client.render.chunk.compile.ChunkBuildContext;
7 | import me.jellysquid.mods.sodium.client.render.chunk.compile.ChunkBuildOutput;
8 | import me.jellysquid.mods.sodium.client.render.chunk.compile.tasks.ChunkBuilderMeshingTask;
9 | import me.jellysquid.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(Lme/jellysquid/mods/sodium/client/render/chunk/compile/ChunkBuildContext;Lme/jellysquid/mods/sodium/client/util/task/CancellationToken;)Lme/jellysquid/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/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 = {"me.jellysquid.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/mixin/sodium/MixinOptionFlag.java:
--------------------------------------------------------------------------------
1 | package me.cortex.nvidium.mixin.sodium;
2 |
3 | import me.cortex.nvidium.sodiumCompat.NvidiumOptionFlags;
4 | import me.jellysquid.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/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 me.jellysquid.mods.sodium.client.gl.device.CommandList;
7 | import me.jellysquid.mods.sodium.client.render.chunk.compile.ChunkBuildOutput;
8 | import me.jellysquid.mods.sodium.client.render.chunk.region.RenderRegion;
9 | import me.jellysquid.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 | @Mixin(value = RenderRegionManager.class, remap = false)
19 | public abstract class MixinRenderRegionManager implements INvidiumWorldRendererSetter {
20 |
21 |
22 | @Shadow protected abstract void uploadMeshes(CommandList commandList, RenderRegion region, Collection results);
23 |
24 | @Unique private NvidiumWorldRenderer renderer;
25 |
26 |
27 | @Redirect(method = "uploadMeshes(Lme/jellysquid/mods/sodium/client/gl/device/CommandList;Ljava/util/Collection;)V", at = @At(value = "INVOKE", target = "Lme/jellysquid/mods/sodium/client/render/chunk/region/RenderRegionManager;uploadMeshes(Lme/jellysquid/mods/sodium/client/gl/device/CommandList;Lme/jellysquid/mods/sodium/client/render/chunk/region/RenderRegion;Ljava/util/Collection;)V"))
28 | private void redirectUpload(RenderRegionManager instance, CommandList cmdList, RenderRegion pass, Collection uploadQueue) {
29 | if (Nvidium.IS_ENABLED) {
30 | uploadQueue.forEach(renderer::uploadBuildResult);
31 | } else {
32 | uploadMeshes(cmdList, pass, uploadQueue);
33 | }
34 | }
35 |
36 | @Override
37 | public void setWorldRenderer(NvidiumWorldRenderer renderer) {
38 | this.renderer = renderer;
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/main/java/me/cortex/nvidium/mixin/sodium/MixinRenderSection.java:
--------------------------------------------------------------------------------
1 | package me.cortex.nvidium.mixin.sodium;
2 |
3 | import it.unimi.dsi.fastutil.longs.Long2ReferenceMap;
4 | import me.cortex.nvidium.Nvidium;
5 | import me.cortex.nvidium.NvidiumWorldRenderer;
6 | import me.cortex.nvidium.managers.AsyncOcclusionTracker;
7 | import me.cortex.nvidium.sodiumCompat.INvidiumWorldRendererGetter;
8 | import me.cortex.nvidium.sodiumCompat.INvidiumWorldRendererSetter;
9 | import me.cortex.nvidium.sodiumCompat.IRenderSectionExtension;
10 | import me.cortex.nvidium.sodiumCompat.IrisCheck;
11 | import me.jellysquid.mods.sodium.client.gl.device.CommandList;
12 | import me.jellysquid.mods.sodium.client.render.chunk.ChunkRenderMatrices;
13 | import me.jellysquid.mods.sodium.client.render.chunk.ChunkUpdateType;
14 | import me.jellysquid.mods.sodium.client.render.chunk.RenderSection;
15 | import me.jellysquid.mods.sodium.client.render.chunk.RenderSectionManager;
16 | import me.jellysquid.mods.sodium.client.render.chunk.region.RenderRegionManager;
17 | import me.jellysquid.mods.sodium.client.render.chunk.terrain.DefaultTerrainRenderPasses;
18 | import me.jellysquid.mods.sodium.client.render.chunk.terrain.TerrainRenderPass;
19 | import me.jellysquid.mods.sodium.client.render.viewport.Viewport;
20 | import net.minecraft.client.render.Camera;
21 | import net.minecraft.client.world.ClientWorld;
22 | import org.jetbrains.annotations.NotNull;
23 | import org.spongepowered.asm.mixin.Final;
24 | import org.spongepowered.asm.mixin.Mixin;
25 | import org.spongepowered.asm.mixin.Shadow;
26 | import org.spongepowered.asm.mixin.Unique;
27 | import org.spongepowered.asm.mixin.injection.At;
28 | import org.spongepowered.asm.mixin.injection.Inject;
29 | import org.spongepowered.asm.mixin.injection.Redirect;
30 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
31 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
32 |
33 | import java.util.ArrayDeque;
34 | import java.util.ArrayList;
35 | import java.util.Collection;
36 | import java.util.Map;
37 |
38 | @Mixin(value = RenderSection.class, remap = false)
39 | public class MixinRenderSection implements IRenderSectionExtension {
40 | @Unique private volatile boolean isEnqueued;
41 | @Unique private volatile boolean isSeen;
42 |
43 | @Override
44 | public boolean isSubmittedRebuild() {
45 | return isEnqueued;
46 | }
47 |
48 | @Override
49 | public void isSubmittedRebuild(boolean state) {
50 | isEnqueued = state;
51 | }
52 |
53 | @Override
54 | public boolean isSeen() {
55 | return isSeen;
56 | }
57 |
58 | @Override
59 | public void isSeen(boolean state) {
60 | isSeen = state;
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/main/java/me/cortex/nvidium/mixin/sodium/MixinRenderSectionManager.java:
--------------------------------------------------------------------------------
1 | package me.cortex.nvidium.mixin.sodium;
2 |
3 | import it.unimi.dsi.fastutil.longs.Long2ReferenceMap;
4 | import me.cortex.nvidium.Nvidium;
5 | import me.cortex.nvidium.NvidiumWorldRenderer;
6 | import me.cortex.nvidium.managers.AsyncOcclusionTracker;
7 | import me.cortex.nvidium.sodiumCompat.*;
8 | import me.jellysquid.mods.sodium.client.SodiumClientMod;
9 | import me.jellysquid.mods.sodium.client.gl.device.CommandList;
10 | import me.jellysquid.mods.sodium.client.render.chunk.ChunkRenderMatrices;
11 | import me.jellysquid.mods.sodium.client.render.chunk.ChunkUpdateType;
12 | import me.jellysquid.mods.sodium.client.render.chunk.RenderSection;
13 | import me.jellysquid.mods.sodium.client.render.chunk.RenderSectionManager;
14 | import me.jellysquid.mods.sodium.client.render.chunk.region.RenderRegionManager;
15 | import me.jellysquid.mods.sodium.client.render.chunk.terrain.DefaultTerrainRenderPasses;
16 | import me.jellysquid.mods.sodium.client.render.chunk.terrain.TerrainRenderPass;
17 | import me.jellysquid.mods.sodium.client.render.chunk.vertex.format.ChunkVertexType;
18 | import me.jellysquid.mods.sodium.client.render.texture.SpriteUtil;
19 | import me.jellysquid.mods.sodium.client.render.viewport.Viewport;
20 | import net.minecraft.client.render.Camera;
21 | import net.minecraft.client.world.ClientWorld;
22 | import org.jetbrains.annotations.NotNull;
23 | import org.spongepowered.asm.mixin.Final;
24 | import org.spongepowered.asm.mixin.Mixin;
25 | import org.spongepowered.asm.mixin.Shadow;
26 | import org.spongepowered.asm.mixin.Unique;
27 | import org.spongepowered.asm.mixin.injection.*;
28 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
29 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
30 | import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
31 |
32 | import java.util.ArrayDeque;
33 | import java.util.ArrayList;
34 | import java.util.Collection;
35 | import java.util.Map;
36 |
37 | @Mixin(value = RenderSectionManager.class, remap = false)
38 | public class MixinRenderSectionManager implements INvidiumWorldRendererGetter {
39 | @Shadow @Final private RenderRegionManager regions;
40 | @Shadow @Final private Long2ReferenceMap sectionByPosition;
41 | @Shadow private @NotNull Map> rebuildLists;
42 | @Shadow @Final private int renderDistance;
43 | @Unique private NvidiumWorldRenderer renderer;
44 | @Unique private Viewport viewport;
45 |
46 | @Unique
47 | private static void updateNvidiumIsEnabled() {
48 | Nvidium.IS_ENABLED = (!Nvidium.FORCE_DISABLE) && Nvidium.IS_COMPATIBLE && IrisCheck.checkIrisShouldDisable();
49 | }
50 |
51 | @Inject(method = "", at = @At("TAIL"))
52 | private void init(ClientWorld world, int renderDistance, CommandList commandList, CallbackInfo ci) {
53 | updateNvidiumIsEnabled();
54 | if (Nvidium.IS_ENABLED) {
55 | if (renderer != null)
56 | throw new IllegalStateException("Cannot have multiple world renderers");
57 | renderer = new NvidiumWorldRenderer(Nvidium.config.async_bfs?new AsyncOcclusionTracker(renderDistance, sectionByPosition, world, rebuildLists):null);
58 | ((INvidiumWorldRendererSetter)regions).setWorldRenderer(renderer);
59 | }
60 | }
61 |
62 | @ModifyArg(method = "", at = @At(value = "INVOKE", target = "Lme/jellysquid/mods/sodium/client/render/chunk/compile/executor/ChunkBuilder;(Lnet/minecraft/client/world/ClientWorld;Lme/jellysquid/mods/sodium/client/render/chunk/vertex/format/ChunkVertexType;)V", remap = true), index = 1)
63 | private ChunkVertexType modifyVertexType(ChunkVertexType vertexType) {
64 | updateNvidiumIsEnabled();
65 | if (Nvidium.IS_ENABLED) {
66 | return NvidiumCompactChunkVertex.INSTANCE;
67 | }
68 | return vertexType;
69 | }
70 |
71 |
72 | @Inject(method = "destroy", at = @At("TAIL"))
73 | private void destroy(CallbackInfo ci) {
74 | if (Nvidium.IS_ENABLED) {
75 | if (renderer == null)
76 | throw new IllegalStateException("Pipeline already destroyed");
77 | ((INvidiumWorldRendererSetter)regions).setWorldRenderer(null);
78 | renderer.delete();
79 | renderer = null;
80 | }
81 | }
82 |
83 | @Redirect(method = "onSectionRemoved", at = @At(value = "INVOKE", target = "Lme/jellysquid/mods/sodium/client/render/chunk/RenderSection;delete()V"))
84 | private void deleteSection(RenderSection section) {
85 | if (Nvidium.IS_ENABLED) {
86 | if (Nvidium.config.region_keep_distance == 32) {
87 | renderer.deleteSection(section);
88 | }
89 | }
90 | section.delete();
91 | }
92 |
93 | @Inject(method = "update", at = @At("HEAD"))
94 | private void trackViewport(Camera camera, Viewport viewport, int frame, boolean spectator, CallbackInfo ci) {
95 | this.viewport = viewport;
96 | }
97 |
98 | @Inject(method = "renderLayer", at = @At("HEAD"), cancellable = true)
99 | public void renderLayer(ChunkRenderMatrices matrices, TerrainRenderPass pass, double x, double y, double z, CallbackInfo ci) {
100 | if (Nvidium.IS_ENABLED) {
101 | ci.cancel();
102 | pass.startDrawing();
103 | if (pass == DefaultTerrainRenderPasses.SOLID) {
104 | renderer.renderFrame(viewport, matrices, x, y, z);
105 | } else if (pass == DefaultTerrainRenderPasses.TRANSLUCENT) {
106 | renderer.renderTranslucent();
107 | }
108 | pass.endDrawing();
109 | }
110 | }
111 |
112 | @Inject(method = "getDebugStrings", at = @At("HEAD"), cancellable = true)
113 | private void redirectDebug(CallbackInfoReturnable> cir) {
114 | if (Nvidium.IS_ENABLED) {
115 | var debugStrings = new ArrayList();
116 | renderer.addDebugInfo(debugStrings);
117 | cir.setReturnValue(debugStrings);
118 | cir.cancel();
119 | }
120 | }
121 |
122 | @Override
123 | public NvidiumWorldRenderer getRenderer() {
124 | return renderer;
125 | }
126 |
127 | @Inject(method = "createTerrainRenderList", at = @At("HEAD"), cancellable = true)
128 | private void redirectTerrainRenderList(Camera camera, Viewport viewport, int frame, boolean spectator, CallbackInfo ci) {
129 | if (Nvidium.IS_ENABLED && Nvidium.config.async_bfs) {
130 | ci.cancel();
131 | }
132 | }
133 |
134 | @Redirect(method = "submitRebuildTasks", at = @At(value = "INVOKE", target = "Lme/jellysquid/mods/sodium/client/render/chunk/RenderSection;setPendingUpdate(Lme/jellysquid/mods/sodium/client/render/chunk/ChunkUpdateType;)V"))
135 | private void injectEnqueueFalse(RenderSection instance, ChunkUpdateType type) {
136 | instance.setPendingUpdate(type);
137 | if (Nvidium.IS_ENABLED && Nvidium.config.async_bfs) {
138 | //We need to reset the fact that its been submitted to the rebuild queue from the build queue
139 | ((IRenderSectionExtension) instance).isSubmittedRebuild(false);
140 | }
141 | }
142 |
143 | @Unique
144 | private boolean isSectionVisibleBfs(RenderSection section) {
145 | //The reason why this is done is that since the bfs search is async it could be updating the frame counter with the next frame
146 | // while some sections that arnt updated/ticked yet still have the old frame id
147 | int delta = Math.abs(section.getLastVisibleFrame() - renderer.getAsyncFrameId());
148 | return delta <= 1;
149 | }
150 |
151 | @Inject(method = "isSectionVisible", at = @At(value = "INVOKE", target = "Lme/jellysquid/mods/sodium/client/render/chunk/RenderSection;getLastVisibleFrame()I", shift = At.Shift.BEFORE), cancellable = true, locals = LocalCapture.CAPTURE_FAILHARD)
152 | private void redirectIsSectionVisible(int x, int y, int z, CallbackInfoReturnable cir, RenderSection render) {
153 | if (Nvidium.IS_ENABLED && Nvidium.config.async_bfs) {
154 | cir.setReturnValue(isSectionVisibleBfs(render));
155 | }
156 | }
157 |
158 | @Inject(method = "tickVisibleRenders", at = @At("HEAD"), cancellable = true)
159 | private void redirectAnimatedSpriteUpdates(CallbackInfo ci) {
160 | if (Nvidium.IS_ENABLED && Nvidium.config.async_bfs && SodiumClientMod.options().performance.animateOnlyVisibleTextures) {
161 | ci.cancel();
162 | var sprites = renderer.getAnimatedSpriteSet();
163 | if (sprites == null) {
164 | return;
165 | }
166 | for (var sprite : sprites) {
167 | SpriteUtil.markSpriteActive(sprite);
168 | }
169 | }
170 | }
171 |
172 | @Inject(method = "scheduleRebuild", at = @At(value = "INVOKE", target = "Lme/jellysquid/mods/sodium/client/render/chunk/RenderSection;setPendingUpdate(Lme/jellysquid/mods/sodium/client/render/chunk/ChunkUpdateType;)V", shift = At.Shift.AFTER), locals = LocalCapture.CAPTURE_FAILHARD)
173 | private void instantReschedule(int x, int y, int z, boolean important, CallbackInfo ci, RenderSection section, ChunkUpdateType pendingUpdate) {
174 | if (Nvidium.IS_ENABLED && Nvidium.config.async_bfs) {
175 | var queue = rebuildLists.get(pendingUpdate);
176 | if (isSectionVisibleBfs(section) && queue.size() < pendingUpdate.getMaximumQueueSize()) {
177 | ((IRenderSectionExtension)section).isSubmittedRebuild(true);
178 | rebuildLists.get(pendingUpdate).add(section);
179 | }
180 | }
181 | }
182 |
183 | @Inject(method = "getVisibleChunkCount", at = @At("HEAD"), cancellable = true)
184 | private void injectVisibilityCount(CallbackInfoReturnable cir) {
185 | if (Nvidium.IS_ENABLED && Nvidium.config.async_bfs) {
186 | cir.setReturnValue(this.renderer.getAsyncBfsVisibilityCount());
187 | }
188 | }
189 | }
190 |
--------------------------------------------------------------------------------
/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 me.jellysquid.mods.sodium.client.gui.SodiumOptionsGUI;
8 | import me.jellysquid.mods.sodium.client.gui.options.*;
9 | import me.jellysquid.mods.sodium.client.gui.options.storage.OptionStorage;
10 | import me.jellysquid.mods.sodium.client.render.SodiumWorldRenderer;
11 | import net.minecraft.client.MinecraftClient;
12 | import net.minecraft.client.gui.screen.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, MinecraftClient client) {
34 | if (client.world != 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/mixin/sodium/MixinSodiumWorldRenderer.java:
--------------------------------------------------------------------------------
1 | package me.cortex.nvidium.mixin.sodium;
2 |
3 | import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
4 | import me.cortex.nvidium.Nvidium;
5 | import me.cortex.nvidium.NvidiumWorldRenderer;
6 | import me.cortex.nvidium.sodiumCompat.INvidiumWorldRendererGetter;
7 | import me.jellysquid.mods.sodium.client.render.SodiumWorldRenderer;
8 | import me.jellysquid.mods.sodium.client.render.chunk.RenderSectionManager;
9 | import me.jellysquid.mods.sodium.client.render.viewport.Viewport;
10 | import net.minecraft.block.entity.BlockEntity;
11 | import net.minecraft.client.render.BufferBuilderStorage;
12 | import net.minecraft.client.render.Camera;
13 | import net.minecraft.client.render.VertexConsumerProvider;
14 | import net.minecraft.client.render.block.entity.BlockEntityRenderDispatcher;
15 | import net.minecraft.client.util.math.MatrixStack;
16 | import net.minecraft.entity.player.BlockBreakingInfo;
17 | import org.spongepowered.asm.mixin.Mixin;
18 | import org.spongepowered.asm.mixin.Shadow;
19 | import org.spongepowered.asm.mixin.injection.At;
20 | import org.spongepowered.asm.mixin.injection.Inject;
21 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
22 |
23 | import java.util.SortedSet;
24 |
25 | @Mixin(value = SodiumWorldRenderer.class, remap = false)
26 | public abstract class MixinSodiumWorldRenderer implements INvidiumWorldRendererGetter {
27 | @Shadow private RenderSectionManager renderSectionManager;
28 |
29 | @Shadow
30 | protected static void renderBlockEntity(MatrixStack matrices, BufferBuilderStorage bufferBuilders, Long2ObjectMap> blockBreakingProgressions, float tickDelta, VertexConsumerProvider.Immediate immediate, double x, double y, double z, BlockEntityRenderDispatcher dispatcher, BlockEntity entity) {
31 | }
32 |
33 | @Inject(method = "setupTerrain", at = @At(value = "INVOKE", target = "Lme/jellysquid/mods/sodium/client/render/chunk/RenderSectionManager;needsUpdate()Z", shift = At.Shift.BEFORE))
34 | private void injectTerrainSetup(Camera camera, Viewport viewport, int frame, boolean spectator, boolean updateChunksImmediately, CallbackInfo ci) {
35 | if (Nvidium.IS_ENABLED && Nvidium.config.async_bfs) {
36 | ((INvidiumWorldRendererGetter)renderSectionManager).getRenderer().update(camera, viewport, frame, spectator);
37 | }
38 | }
39 |
40 | @Inject(method = "renderBlockEntities(Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/BufferBuilderStorage;Lit/unimi/dsi/fastutil/longs/Long2ObjectMap;FLnet/minecraft/client/render/VertexConsumerProvider$Immediate;DDDLnet/minecraft/client/render/block/entity/BlockEntityRenderDispatcher;)V", at = @At("HEAD"), cancellable = true, remap = true)
41 | private void overrideEntityRenderer(MatrixStack matrices, BufferBuilderStorage bufferBuilders, Long2ObjectMap> blockBreakingProgressions, float tickDelta, VertexConsumerProvider.Immediate immediate, double x, double y, double z, BlockEntityRenderDispatcher blockEntityRenderer, CallbackInfo ci) {
42 | if (Nvidium.IS_ENABLED && Nvidium.config.async_bfs) {
43 | ci.cancel();
44 | var sectionsWithEntities = ((INvidiumWorldRendererGetter)renderSectionManager).getRenderer().getSectionsWithEntities();
45 | for (var section : sectionsWithEntities) {
46 | if (section.isDisposed() || section.getCulledBlockEntities() == null)
47 | continue;
48 | for (var entity : section.getCulledBlockEntities()) {
49 | renderBlockEntity(matrices, bufferBuilders, blockBreakingProgressions, tickDelta, immediate, x, y, z, blockEntityRenderer, entity);
50 | }
51 | }
52 | }
53 | }
54 |
55 | @Override
56 | public NvidiumWorldRenderer getRenderer() {
57 | if (Nvidium.IS_ENABLED) {
58 | return ((INvidiumWorldRendererGetter)renderSectionManager).getRenderer();
59 | } else {
60 | return null;
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/main/java/me/cortex/nvidium/mixin/sodium/SodiumWorldRendererAccessor.java:
--------------------------------------------------------------------------------
1 | package me.cortex.nvidium.mixin.sodium;
2 |
3 | import me.jellysquid.mods.sodium.client.render.SodiumWorldRenderer;
4 | import me.jellysquid.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/renderers/Phase.java:
--------------------------------------------------------------------------------
1 | package me.cortex.nvidium.renderers;
2 |
3 | public abstract class Phase {
4 | }
5 |
--------------------------------------------------------------------------------
/src/main/java/me/cortex/nvidium/renderers/PrimaryTerrainRasterizer.java:
--------------------------------------------------------------------------------
1 | package me.cortex.nvidium.renderers;
2 |
3 | import com.mojang.blaze3d.platform.GlStateManager;
4 | import me.cortex.nvidium.gl.shader.Shader;
5 | import me.cortex.nvidium.sodiumCompat.ShaderLoader;
6 | import me.cortex.nvidium.mixin.minecraft.LightMapAccessor;
7 | import net.minecraft.client.MinecraftClient;
8 | import net.minecraft.util.Identifier;
9 | import org.lwjgl.opengl.GL12C;
10 | import org.lwjgl.opengl.GL45;
11 | import org.lwjgl.opengl.GL45C;
12 |
13 | import static me.cortex.nvidium.RenderPipeline.GL_DRAW_INDIRECT_ADDRESS_NV;
14 | import static me.cortex.nvidium.gl.shader.ShaderType.*;
15 | import static org.lwjgl.opengl.GL11C.*;
16 | import static org.lwjgl.opengl.GL33.glGenSamplers;
17 | import static org.lwjgl.opengl.NVMeshShader.glMultiDrawMeshTasksIndirectNV;
18 | import static org.lwjgl.opengl.NVVertexBufferUnifiedMemory.glBufferAddressRangeNV;
19 |
20 | public class PrimaryTerrainRasterizer extends Phase {
21 | private final int blockSampler = glGenSamplers();
22 | private final int lightSampler = glGenSamplers();
23 | private final Shader shader = Shader.make()
24 | .addSource(TASK, ShaderLoader.parse(Identifier.of("nvidium", "terrain/task.glsl")))
25 | .addSource(MESH, ShaderLoader.parse(Identifier.of("nvidium", "terrain/mesh.glsl")))
26 | .addSource(FRAGMENT, ShaderLoader.parse(Identifier.of("nvidium", "terrain/frag.frag"))).compile();
27 |
28 | public PrimaryTerrainRasterizer() {
29 | GL45C.glSamplerParameteri(blockSampler, GL45C.GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR);
30 | GL45C.glSamplerParameteri(blockSampler, GL45C.GL_TEXTURE_MAG_FILTER, GL_NEAREST);
31 | GL45C.glSamplerParameteri(blockSampler, GL45C.GL_TEXTURE_MIN_LOD, 0);
32 | GL45C.glSamplerParameteri(blockSampler, GL45C.GL_TEXTURE_MAX_LOD, 4);
33 | GL45C.glSamplerParameteri(lightSampler, GL_TEXTURE_WRAP_T, GL12C.GL_CLAMP_TO_EDGE);
34 | GL45C.glSamplerParameteri(lightSampler, GL_TEXTURE_WRAP_S, GL12C.GL_CLAMP_TO_EDGE);
35 | GL45C.glSamplerParameteri(lightSampler, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
36 | GL45C.glSamplerParameteri(lightSampler, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
37 | }
38 |
39 | private static void setTexture(int textureId, int bindingPoint) {
40 | GlStateManager._activeTexture(33984 + bindingPoint);
41 | GlStateManager._bindTexture(textureId);
42 | }
43 |
44 | public void raster(int regionCount, long commandAddr) {
45 | shader.bind();
46 |
47 | int blockId = MinecraftClient.getInstance().getTextureManager().getTexture(Identifier.of("minecraft", "textures/atlas/blocks.png")).getGlId();
48 | int lightId = ((LightMapAccessor)MinecraftClient.getInstance().gameRenderer.getLightmapTextureManager()).getTexture().getGlId();
49 |
50 | GL45C.glBindSampler(0, blockSampler);
51 | GL45C.glBindSampler(1, lightSampler);
52 | setTexture(blockId, 0);
53 | setTexture(lightId, 1);
54 |
55 | glBufferAddressRangeNV(GL_DRAW_INDIRECT_ADDRESS_NV, 0, commandAddr, regionCount*8L);//Bind the command buffer
56 | glMultiDrawMeshTasksIndirectNV( 0, regionCount, 0);
57 | GL45C.glBindSampler(0, 0);
58 | GL45C.glBindSampler(1, 0);
59 | }
60 |
61 | public void delete() {
62 | GL45.glDeleteSamplers(blockSampler);
63 | GL45.glDeleteSamplers(lightSampler);
64 | shader.delete();
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/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 me.jellysquid.mods.sodium.client.gl.shader.ShaderParser;
6 | import net.minecraft.util.Identifier;
7 |
8 | import static me.cortex.nvidium.gl.shader.ShaderType.FRAGMENT;
9 | import static me.cortex.nvidium.gl.shader.ShaderType.MESH;
10 | import static org.lwjgl.opengl.NVMeshShader.glDrawMeshTasksNV;
11 |
12 | public class RegionRasterizer extends Phase {
13 | private final Shader shader = Shader.make()
14 | .addSource(MESH, ShaderLoader.parse(Identifier.of("nvidium", "occlusion/region_raster/mesh.glsl")))
15 | .addSource(FRAGMENT, ShaderLoader.parse(Identifier.of("nvidium", "occlusion/region_raster/fragment.frag")))
16 | .compile();
17 |
18 | public void raster(int regionCount) {
19 | shader.bind();
20 | glDrawMeshTasksNV(0,regionCount);
21 | }
22 |
23 | public void delete() {
24 | shader.delete();
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/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 me.jellysquid.mods.sodium.client.gl.shader.ShaderParser;
6 | import net.minecraft.util.Identifier;
7 |
8 | import static me.cortex.nvidium.gl.shader.ShaderType.*;
9 | import static org.lwjgl.opengl.NVMeshShader.glDrawMeshTasksNV;
10 |
11 | public class SectionRasterizer extends Phase {
12 |
13 | private final Shader shader = Shader.make()
14 | .addSource(TASK, ShaderLoader.parse(Identifier.of("nvidium", "occlusion/section_raster/task.glsl")))
15 | .addSource(MESH, ShaderLoader.parse(Identifier.of("nvidium", "occlusion/section_raster/mesh.glsl")))
16 | .addSource(FRAGMENT, ShaderLoader.parse(Identifier.of("nvidium", "occlusion/section_raster/fragment.glsl"))).compile();
17 |
18 | public void raster(int regionCount) {
19 | shader.bind();
20 | glDrawMeshTasksNV(0,regionCount);
21 | }
22 |
23 | public void delete() {
24 | shader.delete();
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/main/java/me/cortex/nvidium/renderers/SortRegionSectionPhase.java:
--------------------------------------------------------------------------------
1 | package me.cortex.nvidium.renderers;
2 |
3 | import com.mojang.blaze3d.platform.GlStateManager;
4 | import me.cortex.nvidium.gl.shader.Shader;
5 | import me.cortex.nvidium.mixin.minecraft.LightMapAccessor;
6 | import me.cortex.nvidium.sodiumCompat.ShaderLoader;
7 | import net.minecraft.client.MinecraftClient;
8 | import net.minecraft.util.Identifier;
9 | import org.lwjgl.opengl.GL45;
10 | import org.lwjgl.opengl.GL45C;
11 |
12 | import static me.cortex.nvidium.RenderPipeline.GL_DRAW_INDIRECT_ADDRESS_NV;
13 | import static me.cortex.nvidium.gl.shader.ShaderType.*;
14 | import static org.lwjgl.opengl.GL11C.GL_NEAREST;
15 | import static org.lwjgl.opengl.GL11C.GL_NEAREST_MIPMAP_LINEAR;
16 | import static org.lwjgl.opengl.GL33.glGenSamplers;
17 | import static org.lwjgl.opengl.GL43C.glDispatchCompute;
18 | import static org.lwjgl.opengl.NVMeshShader.glMultiDrawMeshTasksIndirectNV;
19 | import static org.lwjgl.opengl.NVVertexBufferUnifiedMemory.glBufferAddressRangeNV;
20 |
21 | public class SortRegionSectionPhase extends Phase {
22 | private final Shader shader = Shader.make()
23 | .addSource(COMPUTE, ShaderLoader.parse(Identifier.of("nvidium", "sorting/region_section_sorter.comp")))
24 | .compile();
25 |
26 | public SortRegionSectionPhase() {
27 | }
28 |
29 | public void dispatch(int sortingRegionCount) {
30 | shader.bind();
31 | glDispatchCompute(sortingRegionCount, 1, 1);
32 | }
33 |
34 | public void delete() {
35 | shader.delete();
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/main/java/me/cortex/nvidium/renderers/TemporalTerrainRasterizer.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 me.cortex.nvidium.mixin.minecraft.LightMapAccessor;
6 | import net.minecraft.client.MinecraftClient;
7 | import net.minecraft.util.Identifier;
8 | import org.lwjgl.opengl.GL12C;
9 | import org.lwjgl.opengl.GL45;
10 | import org.lwjgl.opengl.GL45C;
11 |
12 | import static me.cortex.nvidium.RenderPipeline.GL_DRAW_INDIRECT_ADDRESS_NV;
13 | import static me.cortex.nvidium.gl.shader.ShaderType.*;
14 | import static org.lwjgl.opengl.GL11C.*;
15 | import static org.lwjgl.opengl.GL11C.GL_TEXTURE_WRAP_S;
16 | import static org.lwjgl.opengl.GL33.glGenSamplers;
17 | import static org.lwjgl.opengl.NVMeshShader.glMultiDrawMeshTasksIndirectNV;
18 | import static org.lwjgl.opengl.NVVertexBufferUnifiedMemory.glBufferAddressRangeNV;
19 |
20 | public class TemporalTerrainRasterizer extends Phase {
21 | private final int blockSampler = glGenSamplers();
22 | private final int lightSampler = glGenSamplers();
23 | private final Shader shader = Shader.make()
24 | .addSource(TASK, ShaderLoader.parse(Identifier.of("nvidium", "terrain/temporal_task.glsl")))
25 | .addSource(MESH, ShaderLoader.parse(Identifier.of("nvidium", "terrain/mesh.glsl")))
26 | .addSource(FRAGMENT, ShaderLoader.parse(Identifier.of("nvidium", "terrain/frag.frag"))).compile();
27 |
28 | public TemporalTerrainRasterizer() {
29 | GL45C.glSamplerParameteri(blockSampler, GL45C.GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR);
30 | GL45C.glSamplerParameteri(blockSampler, GL45C.GL_TEXTURE_MAG_FILTER, GL_NEAREST);
31 | GL45C.glSamplerParameteri(blockSampler, GL45C.GL_TEXTURE_MIN_LOD, 0);
32 | GL45C.glSamplerParameteri(blockSampler, GL45C.GL_TEXTURE_MAX_LOD, 4);
33 | GL45C.glSamplerParameteri(lightSampler, GL_TEXTURE_WRAP_T, GL12C.GL_CLAMP_TO_EDGE);
34 | GL45C.glSamplerParameteri(lightSampler, GL_TEXTURE_WRAP_S, GL12C.GL_CLAMP_TO_EDGE);
35 | GL45C.glSamplerParameteri(lightSampler, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
36 | GL45C.glSamplerParameteri(lightSampler, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
37 | }
38 |
39 | public void raster(int regionCount, long commandAddr) {
40 | shader.bind();
41 |
42 | int blockId = MinecraftClient.getInstance().getTextureManager().getTexture(Identifier.of("minecraft", "textures/atlas/blocks.png")).getGlId();
43 | int lightId = ((LightMapAccessor)MinecraftClient.getInstance().gameRenderer.getLightmapTextureManager()).getTexture().getGlId();
44 |
45 | GL45C.glBindTextureUnit(0, blockId);
46 | GL45C.glBindSampler(0, blockSampler);
47 |
48 | GL45C.glBindTextureUnit(1, lightId);
49 | GL45C.glBindSampler(1, lightSampler);
50 |
51 |
52 |
53 | glBufferAddressRangeNV(GL_DRAW_INDIRECT_ADDRESS_NV, 0, commandAddr, regionCount*8L);//Bind the command buffer
54 | glMultiDrawMeshTasksIndirectNV( 0, regionCount, 0);
55 |
56 |
57 | GL45C.glBindSampler(0, 0);
58 | GL45C.glBindSampler(1, 0);
59 | }
60 |
61 | public void delete() {
62 | GL45.glDeleteSamplers(blockSampler);
63 | GL45.glDeleteSamplers(lightSampler);
64 | shader.delete();
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/main/java/me/cortex/nvidium/renderers/TranslucentTerrainRasterizer.java:
--------------------------------------------------------------------------------
1 | package me.cortex.nvidium.renderers;
2 |
3 | import com.mojang.blaze3d.platform.GlStateManager;
4 | import me.cortex.nvidium.gl.shader.Shader;
5 | import me.cortex.nvidium.sodiumCompat.ShaderLoader;
6 | import me.cortex.nvidium.mixin.minecraft.LightMapAccessor;
7 | import net.minecraft.client.MinecraftClient;
8 | import net.minecraft.util.Identifier;
9 | import org.lwjgl.opengl.GL12C;
10 | import org.lwjgl.opengl.GL30C;
11 | import org.lwjgl.opengl.GL45;
12 | import org.lwjgl.opengl.GL45C;
13 |
14 | import static me.cortex.nvidium.RenderPipeline.GL_DRAW_INDIRECT_ADDRESS_NV;
15 | import static me.cortex.nvidium.gl.shader.ShaderType.*;
16 | import static org.lwjgl.opengl.GL11C.*;
17 | import static org.lwjgl.opengl.GL11C.GL_TEXTURE_WRAP_S;
18 | import static org.lwjgl.opengl.GL33.glGenSamplers;
19 | import static org.lwjgl.opengl.NVMeshShader.glMultiDrawMeshTasksIndirectNV;
20 | import static org.lwjgl.opengl.NVVertexBufferUnifiedMemory.glBufferAddressRangeNV;
21 |
22 | public class TranslucentTerrainRasterizer extends Phase {
23 | private final int blockSampler = glGenSamplers();
24 | private final int lightSampler = glGenSamplers();
25 |
26 | private final Shader shader = Shader.make()
27 | .addSource(TASK, ShaderLoader.parse(Identifier.of("nvidium", "terrain/translucent/task.glsl")))
28 | .addSource(MESH, ShaderLoader.parse(Identifier.of("nvidium", "terrain/translucent/mesh.glsl")))
29 | .addSource(FRAGMENT, ShaderLoader.parse(Identifier.of("nvidium", "terrain/frag.frag"), builder->{builder.add("TRANSLUCENT_PASS");}))
30 | .compile();
31 |
32 | public TranslucentTerrainRasterizer() {
33 | GL45C.glSamplerParameteri(blockSampler, GL45C.GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR);
34 | GL45C.glSamplerParameteri(blockSampler, GL45C.GL_TEXTURE_MAG_FILTER, GL_NEAREST);
35 | GL45C.glSamplerParameteri(blockSampler, GL45C.GL_TEXTURE_MIN_LOD, 0);
36 | GL45C.glSamplerParameteri(blockSampler, GL45C.GL_TEXTURE_MAX_LOD, 4);
37 | GL45C.glSamplerParameteri(lightSampler, GL_TEXTURE_WRAP_T, GL12C.GL_CLAMP_TO_EDGE);
38 | GL45C.glSamplerParameteri(lightSampler, GL_TEXTURE_WRAP_S, GL12C.GL_CLAMP_TO_EDGE);
39 | GL45C.glSamplerParameteri(lightSampler, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
40 | GL45C.glSamplerParameteri(lightSampler, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
41 | }
42 |
43 |
44 | private static void setTexture(int textureId, int bindingPoint) {
45 | GlStateManager._activeTexture(33984 + bindingPoint);
46 | GlStateManager._bindTexture(textureId);
47 | }
48 |
49 | //Translucency is rendered in a very cursed and incorrect way
50 | // it hijacks the unassigned indirect command dispatch and uses that to dispatch the translucent chunks as well
51 | public void raster(int regionCount, long commandAddr) {
52 | shader.bind();
53 |
54 | int blockId = MinecraftClient.getInstance().getTextureManager().getTexture(Identifier.of("minecraft", "textures/atlas/blocks.png")).getGlId();
55 | int lightId = ((LightMapAccessor)MinecraftClient.getInstance().gameRenderer.getLightmapTextureManager()).getTexture().getGlId();
56 |
57 | GL45C.glBindSampler(0, blockSampler);
58 | GL45C.glBindSampler(1, lightSampler);
59 | setTexture(blockId, 0);
60 | setTexture(lightId, 1);
61 |
62 | //the +8*6 is to offset to the unassigned dispatch
63 | glBufferAddressRangeNV(GL_DRAW_INDIRECT_ADDRESS_NV, 0, commandAddr, regionCount*8L);//Bind the command buffer
64 | glMultiDrawMeshTasksIndirectNV( 0, regionCount, 0);
65 | GL45C.glBindSampler(0, 0);
66 | GL45C.glBindSampler(1, 0);
67 | }
68 |
69 | public void delete() {
70 | GL45.glDeleteSamplers(blockSampler);
71 | GL45.glDeleteSamplers(lightSampler);
72 | shader.delete();
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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/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/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/NvidiumCompactChunkVertex.java:
--------------------------------------------------------------------------------
1 | package me.cortex.nvidium.sodiumCompat;
2 |
3 |
4 | import me.jellysquid.mods.sodium.client.gl.attribute.GlVertexAttributeFormat;
5 | import me.jellysquid.mods.sodium.client.gl.attribute.GlVertexFormat;
6 | import me.jellysquid.mods.sodium.client.render.chunk.terrain.material.Material;
7 | import me.jellysquid.mods.sodium.client.render.chunk.vertex.format.ChunkMeshAttribute;
8 | import me.jellysquid.mods.sodium.client.render.chunk.vertex.format.ChunkVertexEncoder;
9 | import me.jellysquid.mods.sodium.client.render.chunk.vertex.format.ChunkVertexType;
10 | import net.caffeinemc.mods.sodium.api.util.ColorABGR;
11 | import net.caffeinemc.mods.sodium.api.util.ColorU8;
12 | import net.minecraft.util.math.MathHelper;
13 | import org.lwjgl.system.MemoryUtil;
14 |
15 | public class NvidiumCompactChunkVertex implements ChunkVertexType {
16 | public static final GlVertexFormat VERTEX_FORMAT = new GlVertexFormat<>(ChunkMeshAttribute.class, null, 16);
17 |
18 | public static final int STRIDE = 16;
19 | public static final NvidiumCompactChunkVertex INSTANCE = new NvidiumCompactChunkVertex();
20 |
21 | private static final int POSITION_MAX_VALUE = 65536;
22 | public static final int TEXTURE_MAX_VALUE = 32768;
23 |
24 | private static final float MODEL_ORIGIN = 8.0f;
25 | private static final float MODEL_RANGE = 32.0f;
26 | private static final float MODEL_SCALE = MODEL_RANGE / POSITION_MAX_VALUE;
27 | private static final float MODEL_SCALE_INV = POSITION_MAX_VALUE / MODEL_RANGE;
28 | private static final float TEXTURE_SCALE = (1.0f / TEXTURE_MAX_VALUE);
29 |
30 |
31 | @Override
32 | public float getTextureScale() {
33 | return TEXTURE_SCALE;
34 | }
35 |
36 | @Override
37 | public float getPositionScale() {
38 | return MODEL_SCALE;
39 | }
40 |
41 | @Override
42 | public float getPositionOffset() {
43 | return -MODEL_ORIGIN;
44 | }
45 |
46 | @Override
47 | public GlVertexFormat getVertexFormat() {
48 | return VERTEX_FORMAT;
49 | }
50 |
51 | @Override
52 | public ChunkVertexEncoder getEncoder() {
53 | return (ptr, material, vertex, sectionIndex) -> {
54 | int light = compactLight(vertex.light);
55 |
56 | MemoryUtil.memPutInt(ptr + 0, (encodePosition(vertex.x) << 0) | (encodePosition(vertex.y) << 16));
57 | MemoryUtil.memPutInt(ptr + 4, (encodePosition(vertex.z) << 0) | (encodeDrawParameters(material) << 16) | ((light&0xFF)<<24));
58 | MemoryUtil.memPutInt(ptr + 8, (encodeColor(vertex.color) << 0) | (((light>>8)&0xFF) << 24));
59 | MemoryUtil.memPutInt(ptr + 12, encodeTexture(vertex.u, vertex.v));
60 |
61 | return ptr + STRIDE;
62 | };
63 | }
64 |
65 |
66 | private static int compactLight(int light) {
67 | int sky = MathHelper.clamp((light >>> 16) & 0xFF, 8, 248);
68 | int block = MathHelper.clamp((light >>> 0) & 0xFF, 8, 248);
69 |
70 | return (block << 0) | (sky << 8);
71 | }
72 |
73 | private static int encodePosition(float v) {
74 | return (int) ((MODEL_ORIGIN + v) * MODEL_SCALE_INV);
75 | }
76 |
77 | private static int encodeDrawParameters(Material material) {
78 | return ((material.bits() & 0xFF) << 0);
79 | }
80 |
81 |
82 | private static int encodeColor(int color) {
83 | var brightness = ColorU8.byteToNormalizedFloat(ColorABGR.unpackAlpha(color));
84 |
85 | int r = ColorU8.normalizedFloatToByte(ColorU8.byteToNormalizedFloat(ColorABGR.unpackRed(color)) * brightness);
86 | int g = ColorU8.normalizedFloatToByte(ColorU8.byteToNormalizedFloat(ColorABGR.unpackGreen(color)) * brightness);
87 | int b = ColorU8.normalizedFloatToByte(ColorU8.byteToNormalizedFloat(ColorABGR.unpackBlue(color)) * brightness);
88 |
89 | return ColorABGR.pack(r, g, b, 0x00);
90 | }
91 |
92 |
93 | private static int encodeTexture(float u, float v) {
94 | return ((Math.round(u * TEXTURE_MAX_VALUE) & 0xFFFF) << 0) |
95 | ((Math.round(v * TEXTURE_MAX_VALUE) & 0xFFFF) << 16);
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/src/main/java/me/cortex/nvidium/sodiumCompat/NvidiumOptionFlags.java:
--------------------------------------------------------------------------------
1 | package me.cortex.nvidium.sodiumCompat;
2 |
3 | import me.jellysquid.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/RepackagedSectionOutput.java:
--------------------------------------------------------------------------------
1 | package me.cortex.nvidium.sodiumCompat;
2 |
3 | import me.jellysquid.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/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 me.jellysquid.mods.sodium.client.gl.shader.ShaderConstants;
7 | import me.jellysquid.mods.sodium.client.gl.shader.ShaderParser;
8 | import net.minecraft.util.Identifier;
9 |
10 | import java.util.function.Consumer;
11 |
12 | public class ShaderLoader {
13 | public static String parse(Identifier path) {
14 | return parse(path, shaderConstants -> {});
15 | }
16 |
17 | public static String parse(Identifier path, Consumer constantBuilder) {
18 | var builder = ShaderConstants.builder();
19 | if (Nvidium.IS_DEBUG) {
20 | builder.add("DEBUG");
21 | }
22 |
23 | for (int i = 1; i <= Nvidium.config.statistics_level.ordinal(); i++) {
24 | builder.add("STATISTICS_"+StatisticsLoggingLevel.values()[i].name());
25 | }
26 |
27 |
28 | for (int i = 1; i <= Nvidium.config.translucency_sorting_level.ordinal(); i++) {
29 | builder.add("TRANSLUCENCY_SORTING_"+TranslucencySortingLevel.values()[i].name());
30 | }
31 |
32 | if (Nvidium.config.render_fog) {
33 | builder.add("RENDER_FOG");
34 | }
35 |
36 | builder.add("TEXTURE_MAX_SCALE", String.valueOf(NvidiumCompactChunkVertex.TEXTURE_MAX_VALUE));
37 | constantBuilder.accept(builder);
38 |
39 | return ShaderParser.parseShader("#import <"+path.getNamespace()+":"+path.getPath()+">", builder.build());
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/main/java/me/cortex/nvidium/util/BufferArena.java:
--------------------------------------------------------------------------------
1 | package me.cortex.nvidium.util;
2 |
3 | import me.cortex.nvidium.Nvidium;
4 | import me.cortex.nvidium.gl.RenderDevice;
5 | import me.cortex.nvidium.gl.buffers.IDeviceMappedBuffer;
6 | import me.cortex.nvidium.gl.buffers.PersistentSparseAddressableBuffer;
7 |
8 | //TODO: make it not remove and immediately deallocate the sparse pages, wait until the end of a frame to deallocate
9 | // since committing pages is not cheap
10 | public class BufferArena {
11 | SegmentedManager segments = new SegmentedManager();
12 | private final RenderDevice device;
13 | public final IDeviceMappedBuffer buffer;
14 | private long totalQuads;
15 | private final int vertexFormatSize;
16 |
17 | private final long memory_size;
18 |
19 |
20 | public BufferArena(RenderDevice device, long memory, int vertexFormatSize) {
21 | this.device = device;
22 | this.vertexFormatSize = vertexFormatSize;
23 | this.memory_size = memory;
24 | if (Nvidium.SUPPORTS_PERSISTENT_SPARSE_ADDRESSABLE_BUFFER) {
25 | buffer = device.createSparseBuffer(80000000000L);//Create a 80gb buffer
26 | } else {
27 | buffer = device.createDeviceOnlyMappedBuffer(memory);
28 | this.segments.setLimit(memory/(4L*this.vertexFormatSize));
29 | }
30 | //Reserve index 0
31 | this.allocQuads(1);
32 | }
33 |
34 | public int allocQuads(int quadCount) {
35 | totalQuads += quadCount;
36 | int addr = (int) segments.alloc(quadCount);
37 | if (addr == SegmentedManager.SIZE_LIMIT) {
38 | return addr;
39 | }
40 | if (buffer instanceof PersistentSparseAddressableBuffer psab) {
41 | psab.ensureAllocated(Integer.toUnsignedLong(addr) * 4L * vertexFormatSize, quadCount * 4L * vertexFormatSize);
42 | }
43 | return addr;
44 | }
45 |
46 | public void free(int addr) {
47 | int count = segments.free(addr);
48 | totalQuads -= count;
49 | if (buffer instanceof PersistentSparseAddressableBuffer psab) {
50 | psab.deallocate(Integer.toUnsignedLong(addr) * 4L * vertexFormatSize, count * 4L * vertexFormatSize);
51 | }
52 | }
53 |
54 | public long upload(UploadingBufferStream stream, int addr) {
55 | return stream.upload(buffer, Integer.toUnsignedLong(addr)*4L*vertexFormatSize, (int) segments.getSize(addr)*4*vertexFormatSize);
56 | }
57 |
58 | public void delete() {
59 | buffer.delete();
60 | }
61 |
62 | public int getAllocatedMB() {
63 | if (buffer instanceof PersistentSparseAddressableBuffer psab) {
64 | return (int) ((psab.getPagesCommitted() * PersistentSparseAddressableBuffer.PAGE_SIZE) / (1024 * 1024));
65 | } else {
66 | return (int) (memory_size/(1024*1024));
67 | }
68 | }
69 |
70 | public int getUsedMB() {
71 | return (int) ((totalQuads * vertexFormatSize * 4)/(1024*1024));
72 | }
73 |
74 | public long getMemoryUsed() {
75 | if (buffer instanceof PersistentSparseAddressableBuffer psab) {
76 | return (psab.getPagesCommitted() * PersistentSparseAddressableBuffer.PAGE_SIZE);
77 | } else {
78 | return memory_size;
79 | }
80 | }
81 |
82 | public float getFragmentation() {
83 | long expected = totalQuads * vertexFormatSize * 4;
84 | return (float) ((double)expected/getMemoryUsed());
85 | }
86 |
87 | public boolean canReuse(int addr, int quads) {
88 | return this.segments.getSize(addr) == quads;
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/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/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/java/me/cortex/nvidium/util/SegmentedManager.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.longs.LongRBTreeSet;
6 |
7 | import java.util.Random;
8 |
9 | //TODO: replace the LongAVLTreeSet with a custom implementation that doesnt cause allocations when searching
10 | // and see if something like a RBTree is any better
11 | public class SegmentedManager {
12 | public static final long SIZE_LIMIT = -1;
13 |
14 | private final int ADDR_BITS = 34;//This gives max size per allocation of 2^30 and max address of 2^39
15 | private final int SIZE_BITS = 64 - ADDR_BITS;
16 | private final long SIZE_MSK = (1L<sizeLimit) {
43 | return SIZE_LIMIT;
44 | }
45 | totalSize += size;
46 | TAKEN.add((addr<>> ADDR_BITS) == size) {//If the allocation and slot is the same size, just add it to the taken
52 | TAKEN.add((slot<>> ADDR_BITS));
53 | } else {
54 | TAKEN.add(((slot&ADDR_MSK)<>> ADDR_BITS)-size)<>SIZE_BITS != addr) {
67 | throw new IllegalStateException();
68 | }
69 | long size = slot&SIZE_MSK;
70 | iter.remove();
71 |
72 | //Note: if there is a previous entry, it means that it is guaranteed for the ending address to either
73 | // be the addr, or indicate a free slot that needs to be merged
74 | if (iter.hasPrevious()) {
75 | long prevSlot = iter.previousLong();
76 | long endAddr = (prevSlot>>>SIZE_BITS) + (prevSlot&SIZE_MSK);
77 | if (endAddr != addr) {//It means there is a free slot that needs to get merged into
78 | long delta = (addr - endAddr);
79 | FREE.remove((delta<>>SIZE_BITS) + (slot&SIZE_MSK);
96 | if (endAddr != nextSlot>>>SIZE_BITS) {//It means there is a memory block to be merged in FREE
97 | long delta = ((nextSlot>>>SIZE_BITS) - endAddr);
98 | FREE.remove((delta<>>SIZE_BITS) | (slot<>SIZE_BITS != addr) {
126 | throw new IllegalStateException();
127 | }
128 | long updatedSlot = (slot & (ADDR_MSK << SIZE_BITS)) | ((slot & SIZE_MSK) + extra);
129 | resized = false;
130 | if (iter.hasNext()) {
131 | long next = iter.nextLong();
132 | long endAddr = (slot>>>SIZE_BITS)+(slot&SIZE_MSK);
133 | long delta = (next>>>SIZE_BITS) - endAddr;
134 | if (extra <= delta) {
135 | FREE.remove((delta<sizeLimit)//If expanding and we would exceed the size limit, dont resize
150 | return false;
151 | iter.remove();
152 | TAKEN.add(updatedSlot);
153 | totalSize += extra;
154 | resized = true;
155 | return true;
156 | }
157 | }
158 |
159 | public long getSize(long addr) {
160 | addr &= ADDR_MSK;
161 | var iter = TAKEN.iterator(addr << SIZE_BITS);
162 | if (!iter.hasNext())
163 | throw new IllegalArgumentException();
164 | long slot = iter.nextLong();
165 | if (slot>>SIZE_BITS != addr) {
166 | throw new IllegalStateException();
167 | }
168 | return slot&SIZE_MSK;
169 | }
170 |
171 |
172 | public static void main(String[] args) {
173 | /*
174 | {
175 | SegmentedManager m = new SegmentedManager();
176 | long a = m.alloc(10);
177 | long b = m.alloc(11);
178 | long c = m.alloc(1);
179 | System.out.println(m.expand(a, 1));
180 | m.free(b);
181 | System.out.println(m.expand(a, 1));
182 | System.out.println(m.expand(a, 10));
183 | System.out.println(m.expand(a, 1));
184 | m.free(a);
185 | m.free(c);
186 | System.out.println(m.getSize());
187 | }*/
188 | /*
189 | Random r = new Random(32);
190 | SegmentedManager m = new SegmentedManager();
191 | LongList l = new LongArrayList();
192 | for (int i = 0; i < 5; i++) {
193 | if (r.nextBoolean() || l.isEmpty()) {
194 | long a = m.alloc(r.nextInt(1000) + 1);
195 | l.add(a);
196 | } else {
197 | m.free(l.removeLong(r.nextInt(l.size())));
198 | }
199 | }
200 |
201 | for (long a : l) {
202 | m.free(a);
203 | }
204 | System.out.println(m.getSize());
205 | */
206 |
207 |
208 | for (int j = 0; j < 10000; j++) {
209 | Random r = new Random(j);
210 | SegmentedManager m = new SegmentedManager();
211 | LongList l = new LongArrayList();
212 | for (int i = 0; i < 5000; i++) {
213 | int ac = r.nextInt(3);
214 | if ( ac ==0 || l.isEmpty()) {
215 | long a = m.alloc(r.nextInt(1000) + 1);
216 | l.add(a);
217 | } else if (ac == 1) {
218 | m.free(l.removeLong(r.nextInt(l.size())));
219 | } else {
220 | m.expand(l.getLong(r.nextInt(l.size())), r.nextInt(10) + 1);
221 | }
222 | }
223 |
224 | for (long a : l) {
225 | m.free(a);
226 | }
227 | if (m.getSize() != 0) {
228 | System.out.println(j);
229 | return;
230 | }
231 | }
232 | }
233 |
234 | public void setLimit(long size) {
235 | this.sizeLimit = size;
236 | }
237 | }
238 |
--------------------------------------------------------------------------------
/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.LinkedHashSet;
5 | import java.util.LinkedList;
6 | import java.util.List;
7 | import java.util.Set;
8 |
9 | public class TickableManager {
10 | private static final Set UPLOADERS = new LinkedHashSet<>();
11 | private static final Set DOWNLOADERS = new LinkedHashSet<>();
12 | public static void register(UploadingBufferStream stream) {
13 | UPLOADERS.add(stream);
14 | }
15 | public static void register(DownloadTaskStream stream) {
16 | DOWNLOADERS.add(stream);
17 | }
18 | public static void remove(UploadingBufferStream stream) {
19 | UPLOADERS.remove(stream);
20 | }
21 | public static void remove(DownloadTaskStream stream) {
22 | DOWNLOADERS.remove(stream);
23 | }
24 |
25 | public static void TickAll() {//Should be called at the very end of the frame
26 | var iter = UPLOADERS.iterator();
27 | while (iter.hasNext()) {
28 | iter.next().tick();
29 | }
30 |
31 | var iter2 = DOWNLOADERS.iterator();
32 | while (iter2.hasNext()) {
33 | iter2.next().tick();
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/main/java/me/cortex/nvidium/util/UploadingBufferStream.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.ReferenceArrayList;
6 | import me.cortex.nvidium.gl.GlFence;
7 | import me.cortex.nvidium.gl.RenderDevice;
8 | import me.cortex.nvidium.gl.buffers.Buffer;
9 | import me.cortex.nvidium.gl.buffers.PersistentClientMappedBuffer;
10 |
11 | import java.lang.ref.WeakReference;
12 | import java.util.ArrayDeque;
13 | import java.util.Deque;
14 | import java.util.LinkedList;
15 | import java.util.List;
16 |
17 | import static me.cortex.nvidium.util.SegmentedManager.SIZE_LIMIT;
18 | import static org.lwjgl.opengl.ARBDirectStateAccess.glCopyNamedBufferSubData;
19 | import static org.lwjgl.opengl.ARBDirectStateAccess.glFlushMappedNamedBufferRange;
20 | import static org.lwjgl.opengl.ARBMapBufferRange.*;
21 | import static org.lwjgl.opengl.GL11.glFinish;
22 | import static org.lwjgl.opengl.GL11.glGetError;
23 | import static org.lwjgl.opengl.GL42.glMemoryBarrier;
24 | import static org.lwjgl.opengl.GL42C.GL_BUFFER_UPDATE_BARRIER_BIT;
25 | import static org.lwjgl.opengl.GL44.GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT;
26 |
27 | public class UploadingBufferStream {
28 | private final SegmentedManager allocationArena = new SegmentedManager();
29 | private final PersistentClientMappedBuffer uploadBuffer;
30 |
31 | private final Deque frames = new ArrayDeque<>();
32 | private final LongArrayList thisFrameAllocations = new LongArrayList();
33 | private final Deque uploadList = new ArrayDeque<>();
34 | private final LongArrayList flushList = new LongArrayList();
35 |
36 | public UploadingBufferStream(RenderDevice device, long size) {
37 | this.allocationArena.setLimit(size);
38 | this.uploadBuffer = device.createClientMappedBuffer(size);
39 | TickableManager.register(this);
40 | }
41 |
42 | private long caddr = -1;
43 | private long offset = 0;
44 | public long upload(Buffer buffer, long destOffset, long size) {
45 | if (size > Integer.MAX_VALUE || size == 0 || size < 0) {
46 | throw new IllegalArgumentException();
47 | }
48 | if (destOffset < 0) {
49 | throw new IllegalStateException();
50 | }
51 | if (destOffset+size > buffer.getSize()) {
52 | throw new IllegalStateException();
53 | }
54 |
55 | long addr;
56 | if (this.caddr == -1 || !this.allocationArena.expand(this.caddr, (int) size)) {
57 | this.caddr = this.allocationArena.alloc((int) size);
58 | //If the upload stream is full, flush it and empty it
59 | if (this.caddr == SIZE_LIMIT) {
60 | this.commit();
61 | int attempts = 10;
62 | while (--attempts != 0 && this.caddr == SIZE_LIMIT) {
63 | glFinish();
64 | this.tick();
65 | this.caddr = this.allocationArena.alloc((int) size);
66 | }
67 | if (this.caddr == SIZE_LIMIT) {
68 | throw new IllegalStateException("Could not allocate memory segment big enough for upload even after force flush");
69 | }
70 | }
71 | this.flushList.add(this.caddr);
72 | this.offset = size;
73 | addr = this.caddr;
74 | } else {//Could expand the allocation so just update it
75 | addr = this.caddr + this.offset;
76 | this.offset += size;
77 | }
78 |
79 | if (this.caddr + size > this.uploadBuffer.size) {
80 | throw new IllegalStateException();
81 | }
82 |
83 | this.uploadList.add(new UploadData(buffer, addr, destOffset, size));
84 |
85 | return this.uploadBuffer.addr + addr;
86 | }
87 |
88 |
89 | public void commit() {
90 | //First flush all the allocations and enqueue them to be freed
91 | {
92 | for (long alloc : flushList) {
93 | glFlushMappedNamedBufferRange(this.uploadBuffer.getId(), alloc, this.allocationArena.getSize(alloc));
94 | this.thisFrameAllocations.add(alloc);
95 | }
96 | this.flushList.clear();
97 | }
98 | glMemoryBarrier(GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT);
99 | //Execute all the copies
100 | for (var entry : this.uploadList) {
101 | glCopyNamedBufferSubData(this.uploadBuffer.getId(), entry.target.getId(), entry.uploadOffset, entry.targetOffset, entry.size);
102 | }
103 | this.uploadList.clear();
104 |
105 | glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT);
106 |
107 | this.caddr = -1;
108 | this.offset = 0;
109 | }
110 |
111 | public void tick() {
112 | this.commit();
113 | if (!this.thisFrameAllocations.isEmpty()) {
114 | this.frames.add(new UploadFrame(new GlFence(), new LongArrayList(this.thisFrameAllocations)));
115 | this.thisFrameAllocations.clear();
116 | }
117 |
118 | while (!this.frames.isEmpty()) {
119 | //Since the ordering of frames is the ordering of the gl commands if we encounter an unsignaled fence
120 | // all the other fences should also be unsignaled
121 | if (!this.frames.peek().fence.signaled()) {
122 | break;
123 | }
124 | //Release all the allocations from the frame
125 | var frame = this.frames.pop();
126 | frame.allocations.forEach(allocationArena::free);
127 | frame.fence.free();
128 | }
129 | }
130 |
131 | public void delete() {
132 | TickableManager.remove(this);
133 | this.uploadBuffer.delete();
134 | this.frames.forEach(frame->frame.fence.free());
135 | }
136 |
137 | private record UploadFrame(GlFence fence, LongArrayList allocations) {}
138 | private record UploadData(Buffer target, long uploadOffset, long targetOffset, long size) {}
139 |
140 | }
141 |
--------------------------------------------------------------------------------
/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",
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",
14 | "nvidium.options.translucency_sorting.none" : "None",
15 | "nvidium.options.translucency_sorting.sections" : "Sections",
16 | "nvidium.options.translucency_sorting.quads" : "Quads",
17 | "nvidium.options.statistics_level.name" : "Statistics Level",
18 | "nvidium.options.statistics_level.tooltip" : "Statistics logging level, tracks the visibility count of cull layers",
19 | "nvidium.options.statistics_level.none" : "None",
20 | "nvidium.options.statistics_level.frustum" : "Frustum",
21 | "nvidium.options.statistics_level.regions" : "Regions",
22 | "nvidium.options.statistics_level.sections" : "Sections",
23 | "nvidium.options.statistics_level.quads" : "Quads",
24 | "nvidium.options.async_bfs.name" : "Enable async bfs",
25 | "nvidium.options.async_bfs.tooltip" : "Enables asynchronous bfs chunk section loading, greatly reduces the frame time when moving, more noticeable with higher render distances",
26 | "nvidium.options.render_fog.name": "Render Fog",
27 | "nvidium.options.render_fog.tooltip": "Should fog be rendered"
28 | }
--------------------------------------------------------------------------------
/src/main/resources/assets/nvidium/nvidium.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MCRcortex/nvidium/f2028b2ba9a5dc95d69e73a9eaeadcb447367d7d/src/main/resources/assets/nvidium/nvidium.png
--------------------------------------------------------------------------------
/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/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/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/resources/assets/nvidium/shaders/occlusion/region_raster/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 | int visibilityIndex = (int)gl_WorkGroupID.x;
45 | //If the region metadata was empty, return
46 | if (data.a == uint64_t(-1)) {
47 | regionVisibility[visibilityIndex] = uint8_t(0);
48 | gl_PrimitiveCountNV = 0;
49 | return;
50 | }
51 |
52 | ivec3 pos = unpackRegionPosition(data);
53 | pos -= chunkPosition.xyz;
54 | pos -= unpackOriginOffsetId(unpackRegionTransformId(data));
55 | ivec3 size = unpackRegionSize(data);
56 |
57 | vec3 start = pos - ADD_SIZE;
58 | vec3 end = start + 1 + size + (ADD_SIZE*2);
59 |
60 | //TODO: Look into only doing 4 locals, for 2 reasons, its more effective for reducing duplicate computation and bandwidth
61 | // it also means that each thread can emit 3 primatives, 9 indicies each
62 |
63 | //can also do 8 threads then each thread emits a primative and 4 indicies each then the lower 4 emit 1 indice extra each
64 |
65 | 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);
66 | corner *= 16.0f;
67 | gl_MeshVerticesNV[gl_LocalInvocationID.x].gl_Position = MVP*(getRegionTransformation(data)*vec4(corner, 1.0));
68 |
69 |
70 | emitIndicies(visibilityIndex);
71 | if (gl_LocalInvocationID.x < 4) {
72 | emitParital(visibilityIndex);
73 |
74 | if (gl_LocalInvocationID.x == 0) {
75 | bool cameraInRegion = all(lessThan(start*16+subchunkOffset.xyz, vec3(ADD_SIZE*16))) && all(lessThan(vec3(-ADD_SIZE*16), end*16+subchunkOffset.xyz));
76 | regionVisibility[visibilityIndex] = cameraInRegion?uint8_t(1):uint8_t(0);
77 | }
78 | }
79 | }
--------------------------------------------------------------------------------
/src/main/resources/assets/nvidium/shaders/occlusion/scene.glsl:
--------------------------------------------------------------------------------
1 | #define Vertex uvec4
2 |
3 | // this is cause in the section rasterizer you get less cache misses thus higher throughput
4 | struct Section {
5 | ivec4 header;
6 | //Header.x -> 0-3=offsetx 4-7=sizex 8-31=chunk x
7 | //Header.y -> 0-3=offsetz 4-7=sizez 8-31=chunk z
8 | //Header.z -> 0-3=offsety 4-7=sizey 8-15=chunk y
9 | //Header.w -> quad offset
10 |
11 | ivec4 renderRanges;
12 | };
13 |
14 | struct Region {
15 | uint64_t a;
16 | uint64_t b;
17 | };
18 |
19 | ivec3 unpackRegionSize(Region region) {
20 | return ivec3((region.a>>59)&7, region.a>>62, (region.a>>56)&7);
21 | }
22 |
23 | uint unpackRegionTransformId(Region region) {
24 | return uint((region.b>>(64-24-10))&((1<<10)-1));
25 | }
26 |
27 | ivec3 unpackRegionPosition(Region region) {
28 | //TODO: optimize
29 | int x = int(int64_t(region.a<<(64-24-24))>>(64-24));
30 | int y = (int(region.a)<<8)>>8;
31 | int z = int(int64_t(region.b)>>(64-24));
32 | return ivec3(x,y,z);
33 | }
34 |
35 | int unpackRegionCount(Region region) {
36 | return int((region.a>>48)&255);
37 | }
38 |
39 | bool sectionEmpty(ivec4 header) {
40 | header.y &= ~0x1FF<<17;
41 | return header == ivec4(0);
42 | }
43 |
44 |
45 | layout(std140, binding=0) uniform SceneData {
46 | //Need to basicly go in order of alignment
47 | //align(16)
48 | mat4 MVP;
49 | #ifdef RENDER_FOG
50 | mat4 MVPInv;
51 | #endif
52 | ivec4 chunkPosition;
53 | vec4 subchunkOffset;
54 | vec4 fogColour;
55 |
56 | //vec4 subChunkPosition;//The subChunkTranslation is already done inside the MVP
57 | //align(8)
58 | readonly restrict uint16_t *regionIndicies;//Pointer to block of memory at the end of the SceneData struct, also mapped to be a uniform
59 | readonly restrict Region *regionData;
60 | restrict Section *sectionData;
61 | //NOTE: for the following, can make it so that region visibility actually uses section visibility array
62 | restrict uint8_t *regionVisibility;
63 | restrict uint8_t *sectionVisibility;
64 | //Terrain command buffer, the first 4 bytes are actually the count
65 | writeonly restrict uvec2 *terrainCommandBuffer;
66 | writeonly restrict uvec2 *translucencyCommandBuffer;
67 |
68 | readonly restrict uint16_t *sortingRegionList;
69 |
70 | //TODO:FIXME: only apply non readonly to translucency mesh
71 | restrict Vertex *terrainData;//readonly
72 |
73 | //TODO: possibly make this a uniform instead of a buffer, but it might get quite large is the issue
74 | readonly restrict mat4 *transformationArray;
75 | readonly restrict uint64_t *originArray;
76 |
77 | //readonly restrict u64vec4 *terrainData;
78 | //uvec4 *terrainData;
79 |
80 | uint32_t *statistics_buffer;
81 |
82 | vec2 screenSize;
83 |
84 | float fogStart;
85 | float fogEnd;
86 | bool isCylindricalFog;
87 |
88 | //align(2)
89 | uint16_t regionCount;//Number of regions in regionIndicies
90 | //align(1)
91 | uint8_t frameId;
92 | };
93 |
94 | mat4 getRegionTransformation(Region region) {
95 | return transformationArray[unpackRegionTransformId(region)];
96 | }
97 |
98 | ivec3 unpackOriginOffsetId(uint id) {
99 | uint64_t val = originArray[id];
100 | int x = (int(uint(val&0x1ffffff))<<7)>>7;
101 | int y = (int(uint((val>>50)&0x3fff))<<18)>>18;
102 | int z = (int(uint((val>>25)&0x1ffffff))<<7)>>7;
103 | return ivec3(x,y,z);
104 | }
--------------------------------------------------------------------------------
/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/resources/assets/nvidium/shaders/occlusion/section_raster/mesh.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 ADD_SIZE (0.1f)
17 | layout(local_size_x = 8) in;
18 | layout(triangles, max_vertices=8, max_primitives=12) out;
19 |
20 | taskNV in Task {
21 | uint32_t _visOutBase;//Base output visibility index
22 | uint32_t _offset;
23 | mat4 regionTransform;
24 | ivec3 chunkShift;
25 | };
26 |
27 | const uint PILUTA[] = {0, 3, 6, 0, 1, 7, 4, 5};
28 | const uint PILUTB[] = {1, 2, 6, 4, 0, 7, 6, 4};
29 | const uint PILUTC[] = {2, 0, 4, 5, 1, 3, 7, 2};
30 | const uint PILUTD[] = {1, 2, 0, 5, 5, 1, 7, 7};
31 |
32 | const uint PILUTE[] = {6, 2, 3, 7};
33 |
34 | void emitIndicies(int visIndex) {
35 | gl_PrimitiveIndicesNV[(gl_LocalInvocationID.x<<2)|0] = PILUTA[gl_LocalInvocationID.x];
36 | gl_PrimitiveIndicesNV[(gl_LocalInvocationID.x<<2)|1] = PILUTB[gl_LocalInvocationID.x];
37 | gl_PrimitiveIndicesNV[(gl_LocalInvocationID.x<<2)|2] = PILUTC[gl_LocalInvocationID.x];
38 | gl_PrimitiveIndicesNV[(gl_LocalInvocationID.x<<2)|3] = PILUTD[gl_LocalInvocationID.x];
39 | gl_MeshPrimitivesNV[gl_LocalInvocationID.x].gl_PrimitiveID = visIndex;
40 | }
41 | void emitParital(int visIndex) {
42 | gl_PrimitiveIndicesNV[(8*4)+gl_LocalInvocationID.x] = PILUTE[gl_LocalInvocationID.x];
43 | gl_MeshPrimitivesNV[gl_LocalInvocationID.x+8].gl_PrimitiveID = visIndex;
44 | }
45 |
46 | //TODO: Check if the section can be culled via fog
47 | void main() {
48 | int visibilityIndex = (int)(_visOutBase|gl_WorkGroupID.x);
49 |
50 | uint8_t lastData = sectionVisibility[visibilityIndex];
51 | // this is almost 100% guarenteed not needed afaik
52 | //barrier();
53 |
54 | ivec4 header = sectionData[_offset|gl_WorkGroupID.x].header;
55 | //If the section header was empty or the hide section bit is set, return
56 |
57 | //NOTE: technically this has the infinitly small probability of not rendering a block if the block is located at
58 | // 0,0,0 the only block in the chunk and the first thing in the buffer
59 | // to fix, also check that the ranges are null
60 | if (sectionEmpty(header) || (header.y&(1<<17)) != 0) {
61 | if (gl_LocalInvocationID.x == 0) {
62 | sectionVisibility[visibilityIndex] = uint8_t(0);
63 | gl_PrimitiveCountNV = 0;
64 | }
65 | return;
66 | }
67 |
68 | vec3 mins = (header.xyz&0xF)-ADD_SIZE;
69 | vec3 maxs = mins+((header.xyz>>4)&0xF)+1+(ADD_SIZE*2);
70 | ivec3 chunk = ivec3(header.xyz)>>8;
71 | chunk.y &= 0x1ff;
72 | chunk.y <<= 32-9;
73 | chunk.y >>= 32-9;
74 |
75 | ivec3 relativeChunkPos = (chunk + chunkShift);
76 | vec3 corner = vec3(relativeChunkPos<<4);
77 | vec3 cornerCopy = corner;
78 |
79 | //TODO: try mix instead or something other than just ternaries, i think they get compiled to a cmov type instruction but not sure
80 | corner += vec3(((gl_LocalInvocationID.x&1)==0)?mins.x:maxs.x, ((gl_LocalInvocationID.x&4)==0)?mins.y:maxs.y, ((gl_LocalInvocationID.x&2)==0)?mins.z:maxs.z);
81 | gl_MeshVerticesNV[gl_LocalInvocationID.x].gl_Position = (MVP*(regionTransform*vec4(corner, 1.0)));
82 |
83 | int prim_payload = (visibilityIndex<<8)|int(((uint(lastData))<<1)&0xff)|1;
84 |
85 | emitIndicies(prim_payload);
86 | if (gl_LocalInvocationID.x < 4) {
87 | emitParital(prim_payload);
88 | }
89 | if (gl_LocalInvocationID.x == 0) {
90 | cornerCopy += subchunkOffset.xyz;
91 | vec3 minPos = mins + cornerCopy;
92 | vec3 maxPos = maxs + cornerCopy;
93 | bool isInSection = all(lessThan(minPos, vec3(ADD_SIZE))) && all(lessThan(vec3(-ADD_SIZE), maxPos));
94 |
95 | //Shift and set, this gives us a bonus of having the last 8 frames as visibility history
96 | sectionVisibility[visibilityIndex] = uint8_t(lastData<<1) | uint8_t(isInSection?1:0);//Inject visibility aswell
97 | //sectionVisibility[visibilityIndex] = uint8_t(lastData<<1) | uint8_t(0);
98 |
99 | gl_PrimitiveCountNV = 12;
100 | }
101 | }
--------------------------------------------------------------------------------
/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/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, -999999999f);
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<>scaleBits)*(1<<(scaleBits+1));
10 | uint offsetA = (gl_LocalInvocationID.x&((1<>scaleBits)*(1<<(scaleBits+1));
29 | base += (gl_LocalInvocationID.x&((1<
15 | #import
16 |
17 |
18 |
19 |
20 | layout(location = 0) out vec4 colour;
21 | #if defined(RENDER_FOG) || defined(TRANSLUCENT_PASS)
22 | layout(location = 1) in Interpolants {
23 | #ifdef RENDER_FOG
24 | float fogLerp;
25 | #endif
26 | #ifdef TRANSLUCENT_PASS
27 | vec2 uv;
28 | vec3 v_colour;
29 | #endif
30 | };
31 | #endif
32 |
33 |
34 | layout(binding = 1) uniform sampler2D tex_light;
35 |
36 | vec4 sampleLight(vec2 uv) {
37 | //Its divided by 16 to match sodium/vanilla (it can never be 1 which is funny)
38 | return vec4(texture(tex_light, uv).rgb, 1);
39 | }
40 |
41 | vec3 computeMultiplier(Vertex V) {
42 | vec4 tint = decodeVertexColour(V);
43 | tint *= sampleLight(decodeLightUV(V));
44 | tint *= tint.w;
45 | return tint.xyz;
46 | }
47 |
48 |
49 | Vertex V0;
50 | Vertex Vp;
51 | Vertex V2;
52 | void computeOutputColour(inout vec3 colour) {
53 | vec3 multiplier = gl_BaryCoordNV.x*computeMultiplier(V0) + gl_BaryCoordNV.y*computeMultiplier(Vp) + gl_BaryCoordNV.z*computeMultiplier(V2);
54 | colour *= multiplier;
55 | }
56 |
57 | #ifdef RENDER_FOG
58 | //2 ways to do it, either use an interpolation, or screenspace reversal, screenspace reversal is better when many many vertices
59 | // however interpolation increases ISBE
60 | void applyFog(inout vec3 colour) {
61 | /*
62 | //Reverse the transformation and compute the original position
63 | vec4 clip = (MVPInv * vec4((gl_FragCoord.xy/screenSize)-1, gl_FragCoord.z*2-1, 1));
64 | vec3 pos = clip.xyz/clip.w;
65 | float fogLerp = clamp(computeFogLerp(pos, isCylindricalFog, fogStart, fogEnd) * fogColour.a, 0,1);
66 | colour = mix(colour, fogColour.rgb, fogLerp);
67 | */
68 | colour = mix(colour, fogColour.rgb, fogLerp);
69 | }
70 | #endif
71 |
72 |
73 | layout(binding = 0) uniform sampler2D tex_diffuse;
74 | void main() {
75 | uint quadId = uint(gl_PrimitiveID)>>4;
76 | bool triangle0 = uint((gl_PrimitiveID>>3)&1)==0;
77 | uvec3 TRI_INDICIES = triangle0?uvec3(0,1,2):uvec3(2,3,0);
78 | V0 = terrainData[(quadId<<2)+TRI_INDICIES.x];
79 | Vp = terrainData[(quadId<<2)+TRI_INDICIES.y];
80 | V2 = terrainData[(quadId<<2)+TRI_INDICIES.z];
81 |
82 |
83 | #ifdef TRANSLUCENT_PASS
84 | colour = texture(tex_diffuse, uv, 0);
85 | colour.rgb *= v_colour;
86 | #else
87 | vec2 uv = gl_BaryCoordNV.x*decodeVertexUV(V0) + gl_BaryCoordNV.y*decodeVertexUV(Vp) + gl_BaryCoordNV.z*decodeVertexUV(V2);
88 | colour = texture(tex_diffuse, uv, ((gl_PrimitiveID>>2)&1)*-8.0f);
89 | if (colour.a < getVertexAlphaCutoff(uint(gl_PrimitiveID&3))) discard;
90 | colour.a = 1;
91 | computeOutputColour(colour.rgb);
92 | #endif
93 |
94 | #ifdef RENDER_FOG
95 | applyFog(colour.rgb);
96 | #endif
97 | }
--------------------------------------------------------------------------------
/src/main/resources/assets/nvidium/shaders/terrain/mesh.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_arithmetic: require
11 | #extension GL_KHR_shader_subgroup_basic : require
12 | #extension GL_KHR_shader_subgroup_ballot : require
13 | #extension GL_KHR_shader_subgroup_vote : require
14 |
15 | #import
16 | #import
17 | #import
18 |
19 |
20 | //It seems like for terrain at least, the sweat spot is ~16 quads per mesh invocation (even if the local size is not 32 )
21 | layout(local_size_x = 16) in;
22 | layout(triangles, max_vertices=64, max_primitives=32) out;
23 |
24 | #ifdef RENDER_FOG
25 | layout(location=1) out Interpolants {
26 | float fogLerp;
27 | } OUT[];
28 | #endif
29 |
30 | taskNV in Task {
31 | vec3 origin;
32 | uint baseOffset;
33 | uint quadCount;
34 | uint transformationId;
35 |
36 | //Binary search indexs and data
37 | uvec4 binIa;
38 | uvec4 binIb;
39 | uvec4 binVa;
40 | uvec4 binVb;
41 | };
42 |
43 |
44 | //Do a binary search via global invocation index to determine the base offset
45 | // Note, all threads in the work group are probably going to take the same path
46 | uint getOffset() {
47 | uint gii = gl_GlobalInvocationID.x;
48 |
49 | //TODO: replace this with binary search
50 | if (gii < binIa.x) {
51 | return binVa.x + gii + baseOffset;
52 | } else if (gii < binIa.y) {
53 | return binVa.y + (gii - binIa.x) + baseOffset;
54 | } else if (gii < binIa.z) {
55 | return binVa.z + (gii - binIa.y) + baseOffset;
56 | } else if (gii < binIa.w) {
57 | return binVa.w + (gii - binIa.z) + baseOffset;
58 | } else if (gii < binIb.x) {
59 | return binVb.x + (gii - binIa.w) + baseOffset;
60 | } else if (gii < binIb.y) {
61 | return binVb.y + (gii - binIb.x) + baseOffset;
62 | } else if (gii < binIb.z) {
63 | return binVb.z + (gii - binIb.y) + baseOffset;
64 | } else if (gii < binIb.w) {
65 | return binVb.w + (gii - binIb.z) + baseOffset;
66 | } else {
67 | return uint(-1);
68 | }
69 | }
70 |
71 | mat4 transformMat;
72 |
73 | vec4 transformVertex(Vertex V) {
74 | vec3 pos = decodeVertexPosition(V)+origin;
75 | return MVP*(transformMat * vec4(pos,1.0));
76 | }
77 |
78 | Vertex V0;
79 | vec4 pV0;
80 | Vertex V1;
81 | vec4 pV1;
82 | Vertex V2;
83 | vec4 pV2;
84 | Vertex V3;
85 | vec4 pV3;
86 |
87 |
88 |
89 | void putVertex(uint id, Vertex V) {
90 | #ifdef RENDER_FOG
91 | vec3 pos = decodeVertexPosition(V)+origin;
92 | vec3 exactPos = pos+subchunkOffset.xyz;
93 | OUT[id].fogLerp = clamp(computeFogLerp(exactPos, isCylindricalFog, fogStart, fogEnd) * fogColour.a, 0, 1);
94 | #endif
95 | }
96 |
97 |
98 | //TODO: make it so that its 32 threads but still 16 quads, each thread processes 2 verticies
99 | // it computes the min of 0,2 with subgroups, then locally it decieds if its triangle needs to be discarded
100 | // should significantly increase the warp efficency
101 | void main() {
102 | if (gl_LocalInvocationIndex == 0) {
103 | gl_PrimitiveCountNV = 0;//Set the prim count to 0
104 | }
105 |
106 | uint id = getOffset();
107 |
108 | //If its over, dont render
109 | if (id == uint(-1)) {
110 | return;
111 | }
112 | transformMat = transformationArray[transformationId];
113 |
114 | //Load the data
115 | V0 = terrainData[(id<<2)+0];
116 | V1 = terrainData[(id<<2)+1];
117 | V2 = terrainData[(id<<2)+2];
118 | V3 = terrainData[(id<<2)+3];
119 |
120 | //Transform the vertices
121 | pV0 = transformVertex(V0);
122 | pV1 = transformVertex(V1);
123 | pV2 = transformVertex(V2);
124 | pV3 = transformVertex(V3);
125 |
126 | bool t0draw;
127 | bool t1draw;
128 |
129 | //Compute the bounding pixels of the 2 triangles in the quad. note, vertex 0 and 2 are the common verticies
130 | {
131 | vec2 ssmin = ((pV0.xy/pV0.w)+1)*screenSize;
132 | vec2 ssmax = ssmin;
133 |
134 | vec2 point = ((pV2.xy/pV2.w)+1)*screenSize;
135 | ssmin = min(ssmin, point);
136 | ssmax = max(ssmax, point);
137 |
138 | point = ((pV1.xy/pV1.w)+1)*screenSize;
139 | vec2 t0min = min(ssmin, point);
140 | vec2 t0max = max(ssmax, point);
141 |
142 | point = ((pV3.xy/pV3.w)+1)*screenSize;
143 | vec2 t1min = min(ssmin, point);
144 | vec2 t1max = max(ssmax, point);
145 |
146 | //Possibly cull the triangles if they dont cover the center of a pixel on the screen (degen)
147 | t0draw = all(notEqual(round(t0min),round(t0max)));
148 | t1draw = all(notEqual(round(t1min),round(t1max)));
149 | }
150 |
151 | //Abort if there are no triangles to dispatch
152 | if (!(t0draw || t1draw)) {
153 | return;
154 | }
155 |
156 | //barrier();
157 | uint triCnt = uint(t0draw)+uint(t1draw);
158 | //Do a subgroup prefix sum to compute emission indies and verticies, aswell as a max to compute the total count
159 | uint triIndex = subgroupExclusiveAdd(triCnt);
160 | uint vertBase = subgroupExclusiveAdd((t0draw==t1draw)?4:3);//if both tris are needed, its 4 verticies else its only 3
161 | uint totalTris = subgroupMax(triIndex+triCnt);
162 |
163 |
164 | uint indexIndex = triIndex*3;//3 indicies to a tri
165 | uint vertIndex = vertBase;
166 |
167 | //We have triangles to emit!
168 | // emit the constant vertices (0,2) that are needed for both triangles
169 | putVertex(vertIndex, V0); gl_MeshVerticesNV[vertIndex++].gl_Position = pV0;
170 | putVertex(vertIndex, V2); gl_MeshVerticesNV[vertIndex++].gl_Position = pV2;
171 |
172 |
173 | uint lodBias = hasMipping(V0)?0:1;
174 | uint alphaCutoff = rawVertexAlphaCutoff(V0);
175 | int primData = int((lodBias<<2)|alphaCutoff|(id<<4));
176 |
177 | if (t0draw) {
178 | putVertex(vertIndex, V1); gl_MeshVerticesNV[vertIndex].gl_Position = pV1;
179 | // 0 1 2
180 | gl_PrimitiveIndicesNV[indexIndex++] = vertBase+0;
181 | gl_PrimitiveIndicesNV[indexIndex++] = vertIndex;
182 | gl_PrimitiveIndicesNV[indexIndex++] = vertBase+1;
183 | vertIndex++;
184 |
185 | //gl_MeshPrimitivesNV[triIndex++].gl_PrimitiveID = int(id<<1);
186 | gl_MeshPrimitivesNV[triIndex++].gl_PrimitiveID = primData|(0<<3);
187 | }
188 |
189 | if (t1draw) {
190 | putVertex(vertIndex, V3); gl_MeshVerticesNV[vertIndex].gl_Position = pV3;
191 | // 2 3 0
192 | gl_PrimitiveIndicesNV[indexIndex++] = vertBase+1;
193 | gl_PrimitiveIndicesNV[indexIndex++] = vertIndex;
194 | gl_PrimitiveIndicesNV[indexIndex++] = vertBase+0;
195 | vertIndex++;
196 |
197 | //gl_MeshPrimitivesNV[triIndex++].gl_PrimitiveID = int((id<<1)+1);
198 | gl_MeshPrimitivesNV[triIndex++].gl_PrimitiveID = primData|(1<<3);
199 | }
200 |
201 |
202 | if (subgroupElect()) {
203 | gl_PrimitiveCountNV = totalTris;
204 | }
205 | }
--------------------------------------------------------------------------------
/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 | baseOffset = (uint)header.w;
50 |
51 | populateTasks(chunk, uvec4(sectionData[sectionId].renderRanges));
52 |
53 |
54 | #ifdef STATISTICS_QUADS
55 | atomicAdd(statistics_buffer+2, quadCount);
56 | #endif
57 | }
--------------------------------------------------------------------------------
/src/main/resources/assets/nvidium/shaders/terrain/task_common.glsl:
--------------------------------------------------------------------------------
1 | #define MESH_WORKLOAD_PER_INVOCATION 16
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 | uint idx = 0;
34 | uint lastIndex = 0;
35 |
36 | binIa = uvec4(0);
37 | binIb = uvec4(0);
38 |
39 | uint fr = (ranges.w>>16)&0xFFFF;
40 |
41 | uint delta = (ranges.x&0xFFFF);
42 | if (relChunkPos.x <= 0 && delta > 0) {
43 | putBinData(idx, lastIndex, fr, fr + delta);
44 | }
45 | fr += ranges.x&0xFFFF;
46 |
47 | delta = ((ranges.x>>16)&0xFFFF);
48 | if (relChunkPos.y <= 0 && delta > 0) {
49 | putBinData(idx, lastIndex, fr, fr + delta);
50 | }
51 | fr += (ranges.x>>16)&0xFFFF;
52 |
53 | delta = ranges.y&0xFFFF;
54 | if (relChunkPos.z <= 0 && delta > 0) {
55 | putBinData(idx, lastIndex, fr, fr + delta);
56 | }
57 | fr += ranges.y&0xFFFF;
58 |
59 | delta = (ranges.y>>16)&0xFFFF;
60 | if (relChunkPos.x >= 0 && delta > 0) {
61 | putBinData(idx, lastIndex, fr, fr + delta);
62 | }
63 | fr += (ranges.y>>16)&0xFFFF;
64 |
65 | delta = ranges.z&0xFFFF;
66 | if (relChunkPos.y >= 0 && delta > 0) {
67 | putBinData(idx, lastIndex, fr, fr + delta);
68 | }
69 | fr += ranges.z&0xFFFF;
70 |
71 | delta = (ranges.z>>16)&0xFFFF;
72 | if (relChunkPos.z >= 0 && delta > 0) {
73 | putBinData(idx, lastIndex, fr, fr + delta);
74 | }
75 | fr += (ranges.z>>16)&0xFFFF;
76 |
77 | //TODO: Put unsigned quads at the begining? since it should be cheaper
78 | putBinData(idx, lastIndex, fr, fr + (ranges.w&0xFFFF));
79 |
80 |
81 |
82 |
83 | quadCount = lastIndex;
84 |
85 | //Emit enough mesh shaders such that max(gl_GlobalInvocationID.x)>=quadCount
86 | gl_TaskCountNV = (lastIndex+MESH_WORKLOAD_PER_INVOCATION-1)/MESH_WORKLOAD_PER_INVOCATION;
87 | }
--------------------------------------------------------------------------------
/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 | baseOffset = (uint)header.w;
51 |
52 | populateTasks(chunk, uvec4(sectionData[sectionId].renderRanges));
53 |
54 | #ifdef STATISTICS_QUADS
55 | atomicAdd(statistics_buffer+2, quadCount);
56 | #endif
57 | }
--------------------------------------------------------------------------------
/src/main/resources/assets/nvidium/shaders/terrain/translucent/mesh.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 | #import
16 | #import
17 |
18 |
19 | #ifdef TRANSLUCENCY_SORTING_QUADS
20 | vec3 depthPos = vec3(0);
21 | shared float depthBuffers[32];
22 | #endif
23 |
24 | layout(local_size_x = 32) in;
25 | layout(triangles, max_vertices=128, max_primitives=64) out;
26 |
27 | //originAndBaseData.w is in quad count space, so is endIdx
28 | taskNV in Task {
29 | vec4 originAndBaseData;
30 | uint quadCount;
31 | #ifdef TRANSLUCENCY_SORTING_QUADS
32 | uint8_t jiggle;
33 | #endif
34 | };
35 |
36 | layout(location=1) out Interpolants {
37 | #ifdef RENDER_FOG
38 | float16_t fogLerp;
39 | #endif
40 | vec2 uv;
41 | vec3 v_colour;
42 | } OUT[];
43 |
44 | layout(binding = 1) uniform sampler2D tex_light;
45 |
46 | vec4 sampleLight(vec2 uv) {
47 | //Its divided by 16 to match sodium/vanilla (it can never be 1 which is funny)
48 | return vec4(texture(tex_light, uv).rgb, 1);
49 | }
50 |
51 | void emitQuadIndicies() {
52 | uint primBase = gl_LocalInvocationID.x * 6;
53 | uint vertexBase = gl_LocalInvocationID.x<<2;
54 | gl_PrimitiveIndicesNV[primBase+0] = vertexBase+0;
55 | gl_PrimitiveIndicesNV[primBase+1] = vertexBase+1;
56 | gl_PrimitiveIndicesNV[primBase+2] = vertexBase+2;
57 | gl_PrimitiveIndicesNV[primBase+3] = vertexBase+2;
58 | gl_PrimitiveIndicesNV[primBase+4] = vertexBase+3;
59 | gl_PrimitiveIndicesNV[primBase+5] = vertexBase+0;
60 | }
61 |
62 | void emitVertex(uint vertexBaseId, uint innerId) {
63 | Vertex V = terrainData[vertexBaseId + innerId];
64 | uint outId = (gl_LocalInvocationID.x<<2)+innerId;
65 | vec3 pos = decodeVertexPosition(V)+originAndBaseData.xyz;
66 | gl_MeshVerticesNV[outId].gl_Position = MVP*vec4(pos,1.0);
67 |
68 |
69 | vec3 exactPos = pos+subchunkOffset.xyz;
70 |
71 | #ifdef RENDER_FOG
72 | float fogLerp = clamp(computeFogLerp(exactPos, isCylindricalFog, fogStart, fogEnd) * fogColour.a, 0, 1);
73 | OUT[outId].fogLerp = float16_t(fogLerp);
74 | #endif
75 | OUT[outId].uv = decodeVertexUV(V);
76 |
77 | vec4 tint = decodeVertexColour(V);
78 | tint *= sampleLight(decodeLightUV(V));
79 | tint *= tint.w;
80 | OUT[outId].v_colour = tint.rgb;
81 |
82 | #ifdef TRANSLUCENCY_SORTING_QUADS
83 | depthPos += exactPos;
84 | #endif
85 |
86 | }
87 |
88 | #ifdef TRANSLUCENCY_SORTING_QUADS
89 | void swapQuads(uint idxA, uint idxB) {
90 | if (idxA == idxB) {
91 | return;
92 | }
93 |
94 | Vertex A0 = terrainData[(idxA<<2)+0];
95 | Vertex A1 = terrainData[(idxA<<2)+1];
96 | Vertex A2 = terrainData[(idxA<<2)+2];
97 | Vertex A3 = terrainData[(idxA<<2)+3];
98 | Vertex B0 = terrainData[(idxB<<2)+0];
99 | Vertex B1 = terrainData[(idxB<<2)+1];
100 | Vertex B2 = terrainData[(idxB<<2)+2];
101 | Vertex B3 = terrainData[(idxB<<2)+3];
102 | //groupMemoryBarrier();
103 | //memoryBarrier();
104 | //barrier();
105 | terrainData[(idxA<<2)+0] = B0;
106 | terrainData[(idxA<<2)+1] = B1;
107 | terrainData[(idxA<<2)+2] = B2;
108 | terrainData[(idxA<<2)+3] = B3;
109 | terrainData[(idxB<<2)+0] = A0;
110 | terrainData[(idxB<<2)+1] = A1;
111 | terrainData[(idxB<<2)+2] = A2;
112 | terrainData[(idxB<<2)+3] = A3;
113 | //groupMemoryBarrier();
114 | //memoryBarrier();
115 | //barrier();
116 | }
117 |
118 | void performTranslucencySort() {
119 | uint baseQuadPtr = floatBitsToUint(originAndBaseData.w) + (gl_WorkGroupID.x<<5);
120 |
121 | float depth = dot(depthPos, depthPos) * ((1/4f)*(1/4f));
122 | depthBuffers[gl_LocalInvocationID.x] = depth;
123 |
124 | if (gl_GlobalInvocationID.x < jiggle) {
125 | //If we are in the jiggle index dont attempt to swap else we start rendering garbage data
126 | depthBuffers[gl_LocalInvocationID.x] = -9999f;
127 | }
128 |
129 | groupMemoryBarrier();
130 | memoryBarrier();
131 | barrier();
132 | //TODO: use subgroup ballot to check if all the quads are already sorted, if they are dont perform sort op
133 |
134 | //Only use 16 threads to sort all 32 data
135 | if (gl_LocalInvocationID.x < 16) {
136 | uint idA = (gl_LocalInvocationID.x<<1);
137 | uint idB = (gl_LocalInvocationID.x<<1)+1;
138 | float a = depthBuffers[idA];
139 | float b = depthBuffers[idB];
140 |
141 | if (a > 0.0001f && b > 0.0001f && a < b) {
142 | swapQuads(idA + baseQuadPtr, idB + baseQuadPtr);
143 | }
144 | }
145 | }
146 | #endif
147 |
148 | //TODO: extra per quad culling
149 | void main() {
150 | #ifdef TRANSLUCENCY_SORTING_QUADS
151 | depthBuffers[gl_LocalInvocationID.x] = -99999999f;
152 | #endif
153 | if ((gl_GlobalInvocationID.x)>=quadCount) { //If its over the quad count, dont render
154 | return;
155 | }
156 |
157 | emitQuadIndicies();
158 |
159 | //Each pair of meshlet invokations emits 4 vertices each and 2 primative each
160 | uint id = (floatBitsToUint(originAndBaseData.w) + gl_GlobalInvocationID.x)<<2;
161 |
162 | #ifdef TRANSLUCENCY_SORTING_QUADS
163 | //If we are at the start, dont want to render as it contains garbled data (out of bounds)
164 | if (gl_GlobalInvocationID.x < jiggle) {
165 | gl_MeshVerticesNV[(gl_LocalInvocationID.x<<2)+0].gl_Position = vec4(1,1,1,-1);
166 | gl_MeshVerticesNV[(gl_LocalInvocationID.x<<2)+1].gl_Position = vec4(1,1,1,-1);
167 | gl_MeshVerticesNV[(gl_LocalInvocationID.x<<2)+2].gl_Position = vec4(1,1,1,-1);
168 | gl_MeshVerticesNV[(gl_LocalInvocationID.x<<2)+3].gl_Position = vec4(1,1,1,-1);
169 |
170 | } else {
171 | emitVertex(id, 0);
172 | emitVertex(id, 1);
173 | emitVertex(id, 2);
174 | emitVertex(id, 3);
175 | }
176 | barrier();
177 | memoryBarrierShared();
178 |
179 | performTranslucencySort();
180 | #else
181 | emitVertex(id, 0);
182 | emitVertex(id, 1);
183 | emitVertex(id, 2);
184 | emitVertex(id, 3);
185 | #endif
186 |
187 | gl_MeshPrimitivesNV[(gl_LocalInvocationID.x<<1)].gl_PrimitiveID = int((id>>2)<<4)|(0<<3);
188 | gl_MeshPrimitivesNV[(gl_LocalInvocationID.x<<1)|1].gl_PrimitiveID = int((id>>2)<<4)|(1<<3);
189 |
190 | if (gl_LocalInvocationID.x == 0) {
191 | //Remaining quads in workgroup
192 | gl_PrimitiveCountNV = min(uint(int(quadCount)-int(gl_WorkGroupID.x<<5))<<1, 64);//2 primatives per quad
193 | }
194 |
195 | }
--------------------------------------------------------------------------------
/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 | };
29 |
30 | bool shouldRender(uint sectionId) {
31 | //Check visibility
32 | return (sectionVisibility[sectionId]&uint8_t(1)) != uint8_t(0);
33 | }
34 |
35 | void main() {
36 | uint sectionId = gl_WorkGroupID.x;
37 | #ifdef TRANSLUCENCY_SORTING_SECTIONS
38 | //Compute indirection for translucency sorting
39 | {
40 | ivec4 header = sectionData[sectionId].header;
41 | //If the section is empty, we dont care about it at all, so ignore it and return
42 | if (sectionEmpty(header)) {
43 | return;
44 | }
45 | //Compute the redirected section index
46 | sectionId &= ~0xFF;
47 | sectionId |= uint((header.y>>18)&0xFF);
48 | }
49 | #endif
50 |
51 | if (!shouldRender(sectionId)) {
52 | //Early exit if the section isnt visible
53 | //TODO: also early exit if there are no translucents to render
54 | gl_TaskCountNV = 0;
55 | return;
56 | }
57 |
58 | ivec4 header = sectionData[sectionId].header;
59 | uint baseDataOffset = (uint)header.w;
60 | ivec3 chunk = ivec3(header.xyz)>>8;
61 | chunk.y &= 0x1ff;
62 | chunk.y <<= 32-9;
63 | chunk.y >>= 32-9;
64 | originAndBaseData.xyz = vec3((chunk - chunkPosition.xyz)<<4);
65 |
66 |
67 | quadCount = ((sectionData[sectionId].renderRanges.w>>16)&0xFFFF);
68 | #ifdef TRANSLUCENCY_SORTING_QUADS
69 | jiggle = uint8_t(min(quadCount>>1,(uint(frameId)&1)));//Jiggle by 1 quads (either 0 or 1)//*15
70 | //jiggle = uint8_t(0);
71 | quadCount += jiggle;
72 | originAndBaseData.w = uintBitsToFloat(baseDataOffset - uint(jiggle));
73 | #else
74 | originAndBaseData.w = uintBitsToFloat(baseDataOffset);
75 | #endif
76 |
77 | //Emit enough mesh shaders such that max(gl_GlobalInvocationID.x)>=quadCount
78 | gl_TaskCountNV = (quadCount+MESH_WORKLOAD_PER_INVOCATION-1)/MESH_WORKLOAD_PER_INVOCATION;
79 |
80 | #ifdef STATISTICS_QUADS
81 | atomicAdd(statistics_buffer+2, quadCount);
82 | #endif
83 |
84 | #ifdef STATISTICS_SECTIONS
85 | atomicAdd(statistics_buffer+1, 1);
86 | #endif
87 | }
--------------------------------------------------------------------------------
/src/main/resources/assets/nvidium/shaders/terrain/vertex_format.glsl:
--------------------------------------------------------------------------------
1 | #define MODEL_SCALE 32.0 / 65536.0
2 | #define MODEL_ORIGIN 8.0
3 |
4 | #define COLOR_SCALE 1.0 / 255.0
5 |
6 | vec3 decodeVertexPosition(Vertex v) {
7 | uvec3 packed_position = uvec3(
8 | (v.x >> 0) & 0xFFFFu,
9 | (v.x >> 16) & 0xFFFFu,
10 | (v.y >> 0) & 0xFFFFu
11 | );
12 |
13 | return (vec3(packed_position) * MODEL_SCALE) - MODEL_ORIGIN;
14 | }
15 |
16 | vec4 decodeVertexColour(Vertex v) {
17 | uvec3 packed_color = (uvec3(v.z) >> uvec3(0, 8, 16)) & uvec3(0xFFu);
18 | return vec4(vec3(packed_color) * COLOR_SCALE, 1);
19 | }
20 |
21 | vec2 decodeVertexUV(Vertex v) {
22 | return vec2(v.w&0xffff,v.w>>16)*(1f/(TEXTURE_MAX_SCALE));
23 | }
24 |
25 | bool hasMipping(Vertex v) {
26 | return ((v.y>>16)&4)!=0;
27 | }
28 |
29 | float decodeVertexAlphaCutoff(Vertex v) {
30 | return (float[](0.0f, 0.1f,0.5f))[((v.y>>16)&int16_t(3))];
31 | }
32 |
33 | uint rawVertexAlphaCutoff(Vertex v) {
34 | return uint((v.y>>16)&int16_t(3));
35 | }
36 |
37 | float getVertexAlphaCutoff(uint v) {
38 | return (float[](0.0f, 0.1f,0.5f))[v];
39 | }
40 |
41 | vec2 decodeLightUV(Vertex v) {
42 | uvec2 light = uvec2(v.y>>24, v.z>>24) & uvec2(0xFFu);
43 | return vec2(light)/256.0;
44 | }
45 |
46 |
--------------------------------------------------------------------------------
/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 | "contact": {},
15 | "license": "LGPL-3.0",
16 | "icon": "assets/nvidium/nvidium.png",
17 | "environment": "client",
18 | "entrypoints": {},
19 | "mixins": [
20 | "nvidium.mixins.json"
21 | ],
22 | "depends": {
23 | "fabricloader": ">=0.15",
24 | "minecraft": ">=1.21",
25 | "sodium": ["=0.5.9", "=0.5.11"]
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/main/resources/nvidium.mixins.json:
--------------------------------------------------------------------------------
1 | {
2 | "required": true,
3 | "package": "me.cortex.nvidium.mixin",
4 | "compatibilityLevel": "JAVA_17",
5 | "client": [
6 | "minecraft.LightMapAccessor",
7 | "minecraft.MixinBackgroundRenderer",
8 | "minecraft.MixinGameRenderer",
9 | "minecraft.MixinWindow",
10 | "minecraft.MixinWorldRenderer",
11 | "sodium.MixinChunkBuilder",
12 | "sodium.MixinChunkBuilderMeshingTask",
13 | "sodium.MixinChunkBuildOutput",
14 | "sodium.MixinChunkJobQueue",
15 | "sodium.MixinOptionFlag",
16 | "sodium.MixinRenderRegionManager",
17 | "sodium.MixinRenderSection",
18 | "sodium.MixinRenderSectionManager",
19 | "sodium.MixinSodiumOptionsGUI",
20 | "sodium.MixinSodiumWorldRenderer",
21 | "sodium.SodiumWorldRendererAccessor"
22 | ],
23 | "injectors": {
24 | "defaultRequire": 1
25 | }
26 | }
27 |
--------------------------------------------------------------------------------