148 Commits

Author SHA1 Message Date
mcrcortex
5458d9f9b8 improve 2025-09-15 11:19:34 +10:00
mcrcortex
0b5183e196 update 2025-09-14 22:07:38 +10:00
mcrcortex
8c49b42aa6 e 2025-09-14 18:50:16 +10:00
mcrcortex
5657018a98 a 2025-09-11 22:05:46 +10:00
mcrcortex
711d3c44d3 Fixed rare race condition in ActiveSectionTracker, occured more frequently with high core systems. This is hopefully the last concurrency bug in the tracker 2025-09-11 17:11:31 +10:00
mcrcortex
864f798f19 util 2025-09-09 12:44:57 +10:00
mcrcortex
92bb0fbb5c change mesh builder 2025-09-09 12:44:46 +10:00
mcrcortex
6cf1f7f8ae micro opt + gpu timer 2025-09-09 12:44:12 +10:00
mcrcortex
d8c5f08f06 MeshEXT modern, slightly fixed 2025-09-08 23:58:22 +10:00
mcrcortex
a012cead98 silly 2025-09-08 11:00:07 +10:00
mcrcortex
3513192907 ret null 2025-09-08 07:30:46 +10:00
mcrcortex
99ee9a5ad2 stupid idiot 2025-09-08 07:28:43 +10:00
mcrcortex
cb9b41baf6 move patch data to seperate files 2025-09-08 00:25:47 +10:00
mcrcortex
effabf95fd flashback compat 2025-09-07 19:12:25 +10:00
mcrcortex
8615f29132 add useViewportDims 2025-09-07 12:01:58 +10:00
mcrcortex
dc94b60121 a 2025-09-06 08:51:27 +10:00
mcrcortex
8c5672d5fb License 2025-09-05 16:26:48 +10:00
mcrcortex
75a9df969b Support render scaling factor in shaders 2025-09-03 14:51:42 +10:00
mcrcortex
c268306bd6 tweeks for scaling 2025-09-03 14:35:10 +10:00
mcrcortex
e2342a75dd Inital support for lower resolution rendering 2025-09-03 14:00:26 +10:00
mcrcortex
37323f9170 very very _very_ iritated with iris atm 2025-09-03 12:59:34 +10:00
mcrcortex
aa48dbbf86 idiotic moment 2025-09-02 23:04:17 +10:00
mcrcortex
ae54c6ebea attempted taa support 2025-09-02 22:51:10 +10:00
mcrcortex
8384b9f88b Report invalid block bake errors and dont crash (will probably cause an insane amount of spam) 2025-09-02 11:19:11 +10:00
mcrcortex
ea884f1583 changes to samplers 2025-09-02 11:05:06 +10:00
mcrcortex
c506865d7b dont zero the geometry buffer 2025-09-02 10:50:05 +10:00
mcrcortex
920d7db5f6 optionally disable emission to vanilla depth 2025-09-02 03:00:06 +10:00
mcrcortex
35f93122e5 extreme override 2025-09-02 02:17:54 +10:00
mcrcortex
ed04e21861 Version bump, fix dh impersonation uniform, add 4 more colourtexs to iris 2025-09-02 02:12:41 +10:00
mcrcortex
c25643525a todos 2025-08-31 18:33:07 +10:00
mcrcortex
f031d761b3 Use work around sparse buffer allocation on nvidia 2025-08-30 12:03:06 +10:00
mcrcortex
f3fa371ef9 Fixed full screen blitting, blend with sky 2025-08-29 15:50:01 +10:00
mcrcortex
0d09f4c11f small improvement 2025-08-29 10:19:00 +10:00
mcrcortex
dd4f1de695 Massivly improved fog rendering 2025-08-29 10:03:12 +10:00
mcrcortex
269c7da0ac woops 2025-08-28 08:58:03 +10:00
mcrcortex
0ed453fe1d Fix issues with mesa stencil, added self culling option, 2025-08-28 08:47:35 +10:00
mcrcortex
f2bcfca8e8 changes and attempted fixes 2025-08-27 21:21:29 +10:00
mcrcortex
d8324dacd4 combine log msg 2025-08-27 18:58:44 +10:00
mcrcortex
544c1df366 Added waiting for gpu memory collection 2025-08-27 18:56:06 +10:00
mcrcortex
63a969e5df Dont crash if key is null 2025-08-27 17:30:32 +10:00
mcrcortex
552a6e33a6 Slight attempt improve detection, and dont fking require the extensions in the no extension workaround shader ;-; 2025-08-26 01:33:43 +10:00
mcrcortex
3533d2356c tweeked how chunkbound renderer detects changes, auto ingest section when section status changes while surounded by loaded chunks 2025-08-25 23:42:44 +10:00
mcrcortex
120f1e2018 Preset matrices, add modelview uniforms, add extra logging when creating geometry buffer 2025-08-25 21:58:34 +10:00
mcrcortex
c92c1d5b4a Improve detection of subgroup Capabilities in compute shader 2025-08-25 12:47:27 +10:00
mcrcortex
2fd686a5e6 todo and fixes 2025-08-23 23:45:55 +10:00
mcrcortex
aa185f11d7 Add extra protections against shutdown and startup events firing incorrectly, version bump 2025-08-22 16:50:31 +10:00
mcrcortex
7fab36ff3e shader faker thing 2025-08-20 23:01:20 +10:00
mcrcortex
cc609bbb07 Image and ssbo bindings 2025-08-20 13:44:31 +10:00
mcrcortex
9c74f92147 blend state setup 2025-08-18 12:37:23 +10:00
mcrcortex
3b113e4914 delete postprocessing 2025-08-18 00:48:49 +10:00
mcrcortex
33d50aed67 rem unused import 2025-08-18 00:42:34 +10:00
mcrcortex
a9c2d57bc5 Add non subgroup based prefix sum 2025-08-18 00:41:59 +10:00
mcrcortex
3d2693796d beans 2025-08-17 13:23:58 +10:00
mcrcortex
70a937bcaa Iris add 2025-08-16 08:09:52 +10:00
mcrcortex
3e50c95c91 remove 2025-08-14 22:41:49 +10:00
mcrcortex
a0f9b78162 java imports cleanup 2025-08-14 22:20:45 +10:00
mcrcortex
d507429b9b more changes 2025-08-14 22:19:01 +10:00
mcrcortex
634d187c11 painn 2025-08-11 23:15:08 +10:00
mcrcortex
11f7041df0 misc 2025-08-10 09:44:20 +10:00
mcrcortex
2956872970 bean 2025-08-10 09:42:42 +10:00
mcrcortex
c8b0df6ff9 Part A 2025-08-07 10:44:08 +10:00
mcrcortex
16952e13e4 cleanup java imports 2 2025-07-31 16:10:14 +10:00
mcrcortex
e9fba367c0 cleanup java imports 2025-07-31 16:06:16 +10:00
mcrcortex
db06516f97 Remove some unused imports 2025-07-30 14:16:38 +10:00
mcrcortex
ff8e96e293 Possibly fix some weirdness on screen resize and with MiB 2025-07-28 13:08:22 +10:00
mcrcortex
454a5a0e11 bean 2025-07-28 12:46:36 +10:00
mcrcortex
7b456e3d98 dont overcomplicate things 2025-07-20 18:52:56 +10:00
mcrcortex
0514528a4c attempted micro optimization of memcpy writer 2025-07-20 18:42:11 +10:00
mcrcortex
bdcdae791e slight logic error in flushing 2025-07-20 18:23:04 +10:00
mcrcortex
4d59d05ad6 tweek flush and replace exception with error log 2025-07-20 13:01:21 +10:00
mcrcortex
00928fdb88 Fix render generation service task map entries not being removed 2025-07-19 23:21:04 +10:00
mcrcortex
4a34b29b46 improve ingest and saving perf 2025-07-19 12:52:48 +10:00
mcrcortex
f590ad704e fix build 2025-07-19 12:52:48 +10:00
mcrcortex
99a1bb1dc9 1.21.8 2025-07-19 12:52:48 +10:00
mcrcortex
5fe5ebc0a2 Log 2025-07-13 21:37:25 +10:00
mcrcortex
aff30961bd tweek section meta 2025-07-13 18:13:15 +10:00
mcrcortex
64d211b333 use normal hiz 2025-07-13 17:40:23 +10:00
mcrcortex
606d3b2282 hiz2 2025-07-13 17:39:48 +10:00
mcrcortex
132c6aa2e8 thing 2025-07-12 18:48:25 +10:00
mcrcortex
4a140c110f Add more types 2025-07-12 14:14:53 +10:00
mcrcortex
1c8d052544 add todo and optimized imports 2025-07-12 14:13:16 +10:00
mcrcortex
3199b77ae5 Reorder operations in attempt to fix race conditions 2025-07-12 14:12:06 +10:00
mcrcortex
f0e1f18379 mark as compatible with both 1.21.7 and 1.21.6 2025-07-07 22:26:36 +10:00
mcrcortex
492e2a707a Fix compatibility when joml.fastmath is enabled, fixes physics mod causing everything to detonate
Version bump
2025-07-07 22:20:46 +10:00
mcrcortex
7551ca3484 readd override thing 2025-07-07 12:23:09 +10:00
mcrcortex
8f3fa2e7f2 start on no subgroup impl 2025-07-06 20:01:44 +10:00
mcrcortex
936619ce12 no abstract 2025-07-06 17:28:51 +10:00
mcrcortex
d6a42f8ef3 c 2025-07-03 11:06:15 +10:00
mcrcortex
bf43e405ff b 2025-07-03 00:49:42 +10:00
mcrcortex
0c7c33304d Attempt to fix login unable to get object 2025-07-03 00:31:55 +10:00
mcrcortex
f9b1d8a9e1 Check render type before baking model 2025-07-02 18:53:45 +10:00
mcrcortex
7b4fe4bd5c thing 2025-07-02 11:49:13 +10:00
mcrcortex
6ba3111ada fix default biomes on no biome section data biome 2025-07-02 00:03:14 +10:00
mcrcortex
258ccf89e0 move init 2025-07-01 10:33:47 +10:00
mcrcortex
3e193bb675 update 1.21.7 2025-07-01 09:04:31 +10:00
mcrcortex
69b96eee96 L 2025-06-30 19:23:07 +10:00
mcrcortex
e1ba2c4ebb make null before shutdown 2025-06-30 14:20:42 +10:00
mcrcortex
dfce9dae46 Logging and checking 2025-06-30 13:53:03 +10:00
mcrcortex
f4fca865bb add git ignore 2025-06-30 11:23:31 +10:00
mcrcortex
08fa0725d3 Beans 2025-06-30 11:21:43 +10:00
mcrcortex
b92b769f7b Attempt to fix weirdness on thread change while importing 2025-06-30 11:21:01 +10:00
mcrcortex
726517a8b6 Move to global cleaner + enable tracking for all but memory buffers 2025-06-27 22:35:52 +10:00
mcrcortex
51f54c6edd version bump 2025-06-27 20:14:44 +10:00
mcrcortex
f7f260777a changes to ingest 2025-06-26 11:58:40 +10:00
mcrcortex
dd9ac2819d version bump 2025-06-25 00:28:16 +10:00
mcrcortex
1a7bb8498e fast path on not windows 2025-06-25 00:17:26 +10:00
mcrcortex
fb2d26153d attempt ultimit jank to fix shader 2025-06-24 23:44:15 +10:00
mcrcortex
a640c0e62c Add geometry override 2025-06-24 20:14:22 +10:00
mcrcortex
784322db6f Added amd shader compiler driver segfault workaround 2025-06-24 20:01:45 +10:00
mcrcortex
355a63c46f Final attempt at fixing ingest lighting 2025-06-24 00:12:38 +10:00
mcrcortex
155eb75b82 Attempt fix tracking 2025-06-24 00:03:11 +10:00
mcrcortex
64d4ef0c03 attempt improve lighting thing 2025-06-23 23:22:48 +10:00
mcrcortex
edb15db8fa add classifier if not in gha 2025-06-23 22:29:06 +10:00
mcrcortex
883f140b41 Allow enabling debug flags 2025-06-23 22:26:53 +10:00
mcrcortex
90a6765e8a Move chunk ingest into client chunk manager 2025-06-23 22:22:40 +10:00
mcrcortex
b8ede978c2 fix very theoretical incorrect ordering issue 2025-06-23 21:30:43 +10:00
mcrcortex
c1091acc6b service name logging on error 2025-06-23 20:53:30 +10:00
mcrcortex
0034940082 manual artifact workflow 2025-06-23 20:30:24 +10:00
mcrcortex
4d35fad772 Try to get the chunk at all costs 2025-06-23 20:21:18 +10:00
mcrcortex
d86c3b2eb8 Decrease cache size if max memory is small 2025-06-23 19:39:18 +10:00
mcrcortex
b3556813a9 bvec3 2025-06-23 11:21:57 +10:00
mcrcortex
a94dcf1949 Fix mesa 2025-06-23 11:13:10 +10:00
mcrcortex
7fa07ae5ea Add support for vanilla enviromental fog 2025-06-23 00:51:54 +10:00
mcrcortex
cf60d31b75 update chunky 2025-06-22 23:03:54 +10:00
mcrcortex
e1b4e1ea6a micro optimizations 2025-06-22 21:51:07 +10:00
mcrcortex
4f6b0aa04d use textureLod 2025-06-22 17:42:32 +10:00
mcrcortex
8b5e2780c7 fix 64 sized warps 2025-06-22 16:52:08 +10:00
mcrcortex
0dd730d8de nope cant do that am stupid 2025-06-22 12:14:14 +10:00
mcrcortex
0f865c7afb dont remap pipeline 2025-06-22 11:58:00 +10:00
mcrcortex
688f24a409 bean 2025-06-22 11:52:30 +10:00
mcrcortex
dcacd279b3 readd nvidium support 2025-06-22 11:31:38 +10:00
mcrcortex
37d0b755af Fix issues 2025-06-22 11:06:15 +10:00
mcrcortex
26672ce34b Move more computation into frag shader 2025-06-22 10:48:51 +10:00
mcrcortex
d1be49f474 massivly shrinked interstage attributes 2025-06-21 20:55:53 +10:00
mcrcortex
87072a4edc attempt to improve mipping 2025-06-21 15:03:28 +10:00
mcrcortex
5f8679e5d2 Fix memory leak on reload while importing 2025-06-21 12:32:24 +10:00
mcrcortex
1a7cd37741 attempted to improve ingest perfomance by only saving on section unload 2025-06-21 12:25:46 +10:00
mcrcortex
ed181c1dcd changed priority 2025-06-19 22:43:27 +10:00
mcrcortex
4d839e3662 Attempt to reduce reaquires on miss 2025-06-19 22:15:29 +10:00
mcrcortex
156b30756d Attempted optimizations for world processing 2025-06-19 16:03:31 +10:00
mcrcortex
6326870525 Attempt to fix race condition.... _again_ 2025-06-19 15:33:38 +10:00
mcrcortex
a360c9349a woops 2025-06-19 13:08:45 +10:00
mcrcortex
9e6276e0fa Attempt fix capture index buffer before it gets large 2025-06-19 13:03:39 +10:00
mcrcortex
2bbc7a8999 change fence query 2025-06-19 12:45:21 +10:00
mcrcortex
fc3e05434f add fog override (hackily) back 2025-06-18 10:05:29 +10:00
mcrcortex
388764e9c8 mostly finished 1.21.6, except fog 2025-06-18 09:11:25 +10:00
mcrcortex
3fb8323dd0 Merge branch 'mc_1215' into mc_1216 2025-06-18 08:55:06 +10:00
mcrcortex
3aa1c94c6a inital 1.21.6 2025-06-13 14:49:03 +10:00
182 changed files with 6303 additions and 1474 deletions

33
.github/workflows/manual-artifact.yml vendored Normal file
View File

@@ -0,0 +1,33 @@
name: manual-artifact
on: [ workflow_dispatch ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout sources
uses: actions/checkout@v4
- name: Verify wrapper
uses: gradle/actions/wrapper-validation@v3
- name: Setup Java
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: 21
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4
with:
cache-read-only: false
- name: Gradle build
run: ./gradlew build
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: voxy-artifacts
path: build/libs/*.jar

1
.gitignore vendored
View File

@@ -4,3 +4,4 @@
/build/ /build/
/run/ /run/
/out/ /out/
/logs/

5
LICENSE.md Normal file
View File

@@ -0,0 +1,5 @@
# Copyright 2025 MCRcortex
All rights reserved.
Do not redistribute.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -1,5 +1,5 @@
plugins { plugins {
id 'fabric-loom' version "1.10.1" id 'fabric-loom' version "1.11-SNAPSHOT"
id 'maven-publish' id 'maven-publish'
} }
@@ -26,6 +26,24 @@ repositories {
} }
maven { url = "https://maven.shedaniel.me/" } maven { url = "https://maven.shedaniel.me/" }
maven { url = "https://maven.terraformersmc.com/releases/" } maven { url = "https://maven.terraformersmc.com/releases/" }
exclusiveContent {
forRepository {
ivy {
name = "github"
url = "https://github.com/"
patternLayout {
artifact '/[organisation]/[module]/releases/download/[revision]/[module]-[revision]-[classifier].[ext]'
}
metadataSources {
artifact()
}
}
}
filter {
includeModuleByRegex("[^\\.]+", "nvidium")
}
}
} }
@@ -87,35 +105,37 @@ dependencies {
modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}" modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}"
//TODO: this is to eventually not need sodium installed as atm its just used for parsing shaders //TODO: this is to eventually not need sodium installed as atm its just used for parsing shaders
modRuntimeOnlyMsk "maven.modrinth:sodium:mc1.21.5-0.6.13-fabric" modRuntimeOnlyMsk "maven.modrinth:sodium:mc1.21.6-0.6.13-fabric"
modCompileOnly "maven.modrinth:sodium:mc1.21.5-0.6.13-fabric" modCompileOnly "maven.modrinth:sodium:mc1.21.6-0.6.13-fabric"
modImplementation("maven.modrinth:lithium:mc1.21.5-0.16.0-fabric") modImplementation("maven.modrinth:lithium:mc1.21.8-0.18.0-fabric")
//modRuntimeOnly "maven.modrinth:nvidium:0.2.6-beta" //modRuntimeOnlyMsk "drouarb:nvidium:0.4.1-beta4:1.21.6@jar"
//modCompileOnly "maven.modrinth:nvidium:0.2.8-beta" modCompileOnly "drouarb:nvidium:0.4.1-beta4:1.21.6@jar"
modCompileOnly("maven.modrinth:modmenu:14.0.0-rc.2") modCompileOnly("maven.modrinth:modmenu:15.0.0-beta.3")
modRuntimeOnlyMsk("maven.modrinth:modmenu:14.0.0-rc.2") modRuntimeOnlyMsk("maven.modrinth:modmenu:15.0.0-beta.3")
modCompileOnly("maven.modrinth:iris:1.8.11+1.21.5-fabric") modCompileOnly("maven.modrinth:iris:1.9.1+1.21.7-fabric")
modRuntimeOnlyMsk("maven.modrinth:iris:1.8.11+1.21.5-fabric") modRuntimeOnlyMsk("maven.modrinth:iris:1.9.1+1.21.7-fabric")
//modCompileOnly("maven.modrinth:starlight:1.1.3+1.20.4") //modCompileOnly("maven.modrinth:starlight:1.1.3+1.20.4")
//modCompileOnly("maven.modrinth:immersiveportals:v5.1.7-mc1.20.4") //modCompileOnly("maven.modrinth:immersiveportals:v5.1.7-mc1.20.4")
modCompileOnly("maven.modrinth:chunky:1.4.36-fabric") modCompileOnly("maven.modrinth:chunky:1.4.40-fabric")
modRuntimeOnlyMsk("maven.modrinth:chunky:1.4.36-fabric") modRuntimeOnlyMsk("maven.modrinth:chunky:1.4.40-fabric")
modRuntimeOnlyMsk("maven.modrinth:spark:1.10.121-fabric") modRuntimeOnlyMsk("maven.modrinth:spark:1.10.139-fabric")
modRuntimeOnlyMsk("maven.modrinth:fabric-permissions-api:0.3.3") modRuntimeOnlyMsk("maven.modrinth:fabric-permissions-api:0.3.3")
//modRuntimeOnly("maven.modrinth:nsight-loader:1.2.0") //modRuntimeOnly("maven.modrinth:nsight-loader:1.2.0")
//modImplementation('io.github.douira:glsl-transformer:2.0.1') //modImplementation('io.github.douira:glsl-transformer:2.0.1')
modCompileOnly("maven.modrinth:vivecraft:1.21.1-1.1.14-b2-fabric") modCompileOnly("maven.modrinth:vivecraft:1.21.1-1.1.14-b2-fabric")
modCompileOnly("maven.modrinth:flashback:rNCr1Rbs")
} }
@@ -192,9 +212,11 @@ remapJar {
delete fileTree(getDestinationDirectory().get()) delete fileTree(getDestinationDirectory().get())
} }
def hash = gitCommitHash(); if (!isInGHA) {
if (!hash.equals("<UnknownCommit>")) { def hash = gitCommitHash();
archiveClassifier.set(hash); if (!hash.equals("<UnknownCommit>")) {
archiveClassifier.set(hash);
}
} }
} }

View File

@@ -6,14 +6,15 @@ org.gradle.parallel=true
# Fabric Properties # Fabric Properties
# check these on https://modmuss50.me/fabric.html # check these on https://modmuss50.me/fabric.html
minecraft_version=1.21.5 minecraft_version=1.21.8
yarn_mappings=1.21.5+build.1 yarn_mappings=1.21.8+build.1
loader_version=0.16.10 loader_version=0.16.14
loom_version=1.11-SNAPSHOT
# Fabric API # Fabric API
fabric_version=0.119.5+1.21.5 fabric_version=0.129.0+1.21.8
# Mod Properties # Mod Properties
mod_version = 0.2.0-alpha mod_version = 0.2.5-alpha
maven_group = me.cortex maven_group = me.cortex
archives_base_name = voxy archives_base_name = voxy

View File

@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-bin.zip
networkTimeout=10000 networkTimeout=10000
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists

View File

@@ -1,8 +1,6 @@
package me.cortex.voxy.client; package me.cortex.voxy.client;
import me.cortex.voxy.client.taskbar.Taskbar;
import me.cortex.voxy.common.Logger; import me.cortex.voxy.common.Logger;
import me.cortex.voxy.common.thread.ServiceThreadPool;
import me.cortex.voxy.commonImpl.ImportManager; import me.cortex.voxy.commonImpl.ImportManager;
import me.cortex.voxy.commonImpl.importers.IDataImporter; import me.cortex.voxy.commonImpl.importers.IDataImporter;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
@@ -12,7 +10,6 @@ import net.minecraft.text.Text;
import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.MathHelper;
import java.util.UUID; import java.util.UUID;
import java.util.function.BooleanSupplier;
public class ClientImportManager extends ImportManager { public class ClientImportManager extends ImportManager {
protected class ClientImportTask extends ImportTask { protected class ClientImportTask extends ImportTask {

View File

@@ -0,0 +1,7 @@
package me.cortex.voxy.client;
import net.minecraft.world.chunk.WorldChunk;
public interface ICheekyClientChunkManager {
WorldChunk voxy$cheekyGetChunk(int x, int z);
}

View File

@@ -2,6 +2,10 @@ package me.cortex.voxy.client;
import me.cortex.voxy.common.world.WorldEngine; import me.cortex.voxy.common.world.WorldEngine;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class RenderStatistics { public class RenderStatistics {
public static boolean enabled = false; public static boolean enabled = false;
@@ -9,4 +13,24 @@ public class RenderStatistics {
public static final int[] hierarchicalRenderSections = new int[WorldEngine.MAX_LOD_LAYER+1]; public static final int[] hierarchicalRenderSections = new int[WorldEngine.MAX_LOD_LAYER+1];
public static final int[] visibleSections = new int[WorldEngine.MAX_LOD_LAYER+1]; public static final int[] visibleSections = new int[WorldEngine.MAX_LOD_LAYER+1];
public static final int[] quadCount = new int[WorldEngine.MAX_LOD_LAYER+1]; public static final int[] quadCount = new int[WorldEngine.MAX_LOD_LAYER+1];
public static void addDebug(List<String> debug) {
if (!enabled) {
return;
}
debug.add("HTC: [" + Arrays.stream(flipCopy(RenderStatistics.hierarchicalTraversalCounts)).mapToObj(Integer::toString).collect(Collectors.joining(", "))+"]");
debug.add("HRS: [" + Arrays.stream(flipCopy(RenderStatistics.hierarchicalRenderSections)).mapToObj(Integer::toString).collect(Collectors.joining(", "))+"]");
debug.add("VS: [" + Arrays.stream(flipCopy(RenderStatistics.visibleSections)).mapToObj(Integer::toString).collect(Collectors.joining(", "))+"]");
debug.add("QC: [" + Arrays.stream(flipCopy(RenderStatistics.quadCount)).mapToObj(Integer::toString).collect(Collectors.joining(", "))+"]");
}
private static int[] flipCopy(int[] array) {
int[] ret = new int[array.length];
int i = ret.length;
for (int j : array) {
ret[--i] = j;
}
return ret;
}
} }

View File

@@ -1,6 +1,5 @@
package me.cortex.voxy.client; package me.cortex.voxy.client;
import java.lang.invoke.VarHandle;
import java.util.ArrayList; import java.util.ArrayList;
public class TimingStatistics { public class TimingStatistics {

View File

@@ -1,11 +1,12 @@
package me.cortex.voxy.client; package me.cortex.voxy.client;
import me.cortex.voxy.client.core.gl.Capabilities; import me.cortex.voxy.client.core.gl.Capabilities;
import me.cortex.voxy.client.core.model.bakery.BudgetBufferRenderer;
import me.cortex.voxy.client.core.rendering.util.SharedIndexBuffer;
import me.cortex.voxy.common.Logger; import me.cortex.voxy.common.Logger;
import me.cortex.voxy.commonImpl.VoxyCommon; import me.cortex.voxy.commonImpl.VoxyCommon;
import net.fabricmc.api.ClientModInitializer; import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback; import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents;
import net.fabricmc.loader.api.FabricLoader; import net.fabricmc.loader.api.FabricLoader;
import java.util.HashSet; import java.util.HashSet;
@@ -15,18 +16,29 @@ import java.util.function.Function;
public class VoxyClient implements ClientModInitializer { public class VoxyClient implements ClientModInitializer {
private static final HashSet<String> FREX = new HashSet<>(); private static final HashSet<String> FREX = new HashSet<>();
public static void initVoxyClient() {
Capabilities.init();//Ensure clinit is called
boolean systemSupported = Capabilities.INSTANCE.compute && Capabilities.INSTANCE.indirectParameters;
if (systemSupported) {
SharedIndexBuffer.INSTANCE.id();
BudgetBufferRenderer.init();
VoxyCommon.setInstanceFactory(VoxyClientInstance::new);
if (!Capabilities.INSTANCE.subgroup) {
Logger.warn("GPU does not support subgroup operations, expect some performance degradation");
}
} else {
Logger.error("Voxy is unsupported on your system.");
}
}
@Override @Override
public void onInitializeClient() { public void onInitializeClient() {
ClientLifecycleEvents.CLIENT_STARTED.register(client->{
boolean systemSupported = Capabilities.INSTANCE.compute && Capabilities.INSTANCE.indirectParameters;
if (systemSupported) {
VoxyCommon.setInstanceFactory(VoxyClientInstance::new);
} else {
Logger.error("Voxy is unsupported on your system.");
}
});
ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> { ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> {
if (VoxyCommon.isAvailable()) { if (VoxyCommon.isAvailable()) {
dispatcher.register(VoxyCommands.register()); dispatcher.register(VoxyCommands.register());

View File

@@ -1,5 +1,6 @@
package me.cortex.voxy.client; package me.cortex.voxy.client;
import me.cortex.voxy.client.compat.FlashbackCompat;
import me.cortex.voxy.client.config.VoxyConfig; import me.cortex.voxy.client.config.VoxyConfig;
import me.cortex.voxy.common.Logger; import me.cortex.voxy.common.Logger;
import me.cortex.voxy.common.config.ConfigBuildCtx; import me.cortex.voxy.common.config.ConfigBuildCtx;
@@ -16,25 +17,24 @@ import me.cortex.voxy.commonImpl.WorldIdentifier;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
import net.minecraft.util.WorldSavePath; import net.minecraft.util.WorldSavePath;
import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class VoxyClientInstance extends VoxyInstance { public class VoxyClientInstance extends VoxyInstance {
public static boolean isInGame = false; public static boolean isInGame = false;
private final SectionStorageConfig storageConfig; private final SectionStorageConfig storageConfig;
private final Path basePath = getBasePath(); private final Path basePath;
private final boolean noIngestOverride;
public VoxyClientInstance() { public VoxyClientInstance() {
super(VoxyConfig.CONFIG.serviceThreads); super(VoxyConfig.CONFIG.serviceThreads);
try { var path = FlashbackCompat.getReplayStoragePath();
Files.createDirectories(this.basePath); this.noIngestOverride = path != null;
} catch (Exception e) { if (path == null) {
throw new RuntimeException(e); path = getBasePath();
} }
this.storageConfig = getCreateStorageConfig(this.basePath); this.basePath = path;
this.storageConfig = getCreateStorageConfig(path);
} }
@Override @Override
@@ -46,34 +46,17 @@ public class VoxyClientInstance extends VoxyInstance {
protected SectionStorage createStorage(WorldIdentifier identifier) { protected SectionStorage createStorage(WorldIdentifier identifier) {
var ctx = new ConfigBuildCtx(); var ctx = new ConfigBuildCtx();
ctx.setProperty(ConfigBuildCtx.BASE_SAVE_PATH, this.basePath.toString()); ctx.setProperty(ConfigBuildCtx.BASE_SAVE_PATH, this.basePath.toString());
ctx.setProperty(ConfigBuildCtx.WORLD_IDENTIFIER, getWorldId(identifier)); ctx.setProperty(ConfigBuildCtx.WORLD_IDENTIFIER, identifier.getWorldId());
ctx.pushPath(ConfigBuildCtx.DEFAULT_STORAGE_PATH); ctx.pushPath(ConfigBuildCtx.DEFAULT_STORAGE_PATH);
return this.storageConfig.build(ctx); return this.storageConfig.build(ctx);
} }
private static String bytesToHex(byte[] hash) { public static SectionStorageConfig getCreateStorageConfig(Path path) {
StringBuilder hexString = new StringBuilder(2 * hash.length);
for (byte b : hash) {
String hex = Integer.toHexString(0xff & b);
if (hex.length() == 1) {
hexString.append('0');
}
hexString.append(hex);
}
return hexString.toString();
}
private static String getWorldId(WorldIdentifier identifier) {
String data = identifier.biomeSeed + identifier.key.toString();
try { try {
return bytesToHex(MessageDigest.getInstance("SHA-256").digest(data.getBytes())).substring(0, 32); Files.createDirectories(path);
} catch ( } catch (Exception e) {
NoSuchAlgorithmException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
}
private static SectionStorageConfig getCreateStorageConfig(Path path) {
var json = path.resolve("config.json"); var json = path.resolve("config.json");
Config config = null; Config config = null;
if (Files.exists(json)) { if (Files.exists(json)) {
@@ -106,6 +89,15 @@ public class VoxyClientInstance extends VoxyInstance {
return config.sectionStorageConfig; return config.sectionStorageConfig;
} }
public Path getStorageBasePath() {
return this.basePath;
}
@Override
public boolean isIngestEnabled(WorldIdentifier worldId) {
return !this.noIngestOverride;
}
private static class Config { private static class Config {
public int version = 1; public int version = 1;
public SectionStorageConfig sectionStorageConfig; public SectionStorageConfig sectionStorageConfig;

View File

@@ -6,7 +6,6 @@ import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.suggestion.Suggestions; import com.mojang.brigadier.suggestion.Suggestions;
import com.mojang.brigadier.suggestion.SuggestionsBuilder; import com.mojang.brigadier.suggestion.SuggestionsBuilder;
import me.cortex.voxy.client.core.IGetVoxyRenderSystem; import me.cortex.voxy.client.core.IGetVoxyRenderSystem;
import me.cortex.voxy.common.world.WorldEngine;
import me.cortex.voxy.commonImpl.VoxyCommon; import me.cortex.voxy.commonImpl.VoxyCommon;
import me.cortex.voxy.commonImpl.WorldIdentifier; import me.cortex.voxy.commonImpl.WorldIdentifier;
import me.cortex.voxy.commonImpl.importers.DHImporter; import me.cortex.voxy.commonImpl.importers.DHImporter;

View File

@@ -0,0 +1,42 @@
package me.cortex.voxy.client.compat;
import com.moulberry.flashback.Flashback;
import com.moulberry.flashback.playback.ReplayServer;
import com.moulberry.flashback.record.FlashbackMeta;
import me.cortex.voxy.common.Logger;
import me.cortex.voxy.common.config.section.SectionStorageConfig;
import net.fabricmc.loader.api.FabricLoader;
import org.apache.commons.logging.Log;
import java.nio.file.Path;
public class FlashbackCompat {
public static final boolean FLASHBACK_INSTALLED = FabricLoader.getInstance().isModLoaded("flashback");
public static Path getReplayStoragePath() {
if (!FLASHBACK_INSTALLED) {
return null;
}
return getReplayStoragePath0();
}
private static Path getReplayStoragePath0() {
ReplayServer replayServer = Flashback.getReplayServer();
if (replayServer != null) {
FlashbackMeta meta = replayServer.getMetadata();
if (meta != null) {
var path = ((IFlashbackMeta)meta).getVoxyPath();
if (path != null) {
Logger.info("Flashback replay server exists and meta exists");
if (path.exists()) {
Logger.info("Flashback voxy path exists in filesystem, using this as lod data source");
return path.toPath();
} else {
Logger.warn("Flashback meta had voxy path saved but path doesnt exist");
}
}
}
}
return null;
}
}

View File

@@ -0,0 +1,8 @@
package me.cortex.voxy.client.compat;
import java.io.File;
public interface IFlashbackMeta {
void setVoxyPath(File path);
File getVoxyPath();
}

View File

@@ -31,9 +31,10 @@ public class VoxyConfig implements OptionStorage<VoxyConfig> {
public int serviceThreads = (int) Math.max(CpuLayout.CORES.length/1.5, 1); public int serviceThreads = (int) Math.max(CpuLayout.CORES.length/1.5, 1);
public float subDivisionSize = 64; public float subDivisionSize = 64;
public boolean renderVanillaFog = false; public boolean renderVanillaFog = false;
public boolean useEnvironmentalFog = false;
public boolean renderStatistics = false; public boolean renderStatistics = false;
public static VoxyConfig loadOrCreate() { private static VoxyConfig loadOrCreate() {
if (VoxyCommon.isAvailable()) { if (VoxyCommon.isAvailable()) {
var path = getConfigPath(); var path = getConfigPath();
if (Files.exists(path)) { if (Files.exists(path)) {
@@ -78,4 +79,8 @@ public class VoxyConfig implements OptionStorage<VoxyConfig> {
public VoxyConfig getData() { public VoxyConfig getData() {
return this; return this;
} }
public boolean isRenderingEnabled() {
return VoxyCommon.isAvailable() && this.enabled && this.enableRendering;
}
} }

View File

@@ -70,13 +70,10 @@ public abstract class VoxyConfigScreenPages {
if (wasEnabled) { if (wasEnabled) {
VoxyCommon.createInstance(); VoxyCommon.createInstance();
if (vrsh != null && s.enableRendering) {
vrsh.createRenderer();
}
} }
}, s -> s.serviceThreads) }, s -> s.serviceThreads)
.setImpact(OptionImpact.HIGH) .setImpact(OptionImpact.HIGH)
.setFlags(OptionFlag.REQUIRES_RENDERER_RELOAD)
.build() .build()
).add(OptionImpl.createBuilder(boolean.class, storage) ).add(OptionImpl.createBuilder(boolean.class, storage)
.setName(Text.translatable("voxy.config.general.ingest")) .setName(Text.translatable("voxy.config.general.ingest"))
@@ -129,6 +126,14 @@ public abstract class VoxyConfigScreenPages {
}, s -> s.sectionRenderDistance) }, s -> s.sectionRenderDistance)
.setImpact(OptionImpact.LOW) .setImpact(OptionImpact.LOW)
.build() .build()
).add(OptionImpl.createBuilder(boolean.class, storage)
.setName(Text.translatable("voxy.config.general.environmental_fog"))
.setTooltip(Text.translatable("voxy.config.general.environmental_fog.tooltip"))
.setControl(TickBoxControl::new)
.setImpact(OptionImpact.VARIES)
.setBinding((s, v)-> s.useEnvironmentalFog = v, s -> s.useEnvironmentalFog)
.setFlags(OptionFlag.REQUIRES_RENDERER_RELOAD)
.build()
).add(OptionImpl.createBuilder(boolean.class, storage) ).add(OptionImpl.createBuilder(boolean.class, storage)
.setName(Text.translatable("voxy.config.general.vanilla_fog")) .setName(Text.translatable("voxy.config.general.vanilla_fog"))
.setTooltip(Text.translatable("voxy.config.general.vanilla_fog.tooltip")) .setTooltip(Text.translatable("voxy.config.general.vanilla_fog.tooltip"))

View File

@@ -0,0 +1,237 @@
package me.cortex.voxy.client.core;
import me.cortex.voxy.client.RenderStatistics;
import me.cortex.voxy.client.TimingStatistics;
import me.cortex.voxy.client.core.gl.Capabilities;
import me.cortex.voxy.client.core.model.ModelBakerySubsystem;
import me.cortex.voxy.client.core.rendering.Viewport;
import me.cortex.voxy.client.core.rendering.hierachical.AsyncNodeManager;
import me.cortex.voxy.client.core.rendering.hierachical.HierarchicalOcclusionTraverser;
import me.cortex.voxy.client.core.rendering.hierachical.NodeCleaner;
import me.cortex.voxy.client.core.rendering.post.FullscreenBlit;
import me.cortex.voxy.client.core.rendering.section.AbstractSectionRenderer;
import me.cortex.voxy.client.core.rendering.util.DownloadStream;
import me.cortex.voxy.common.util.TrackedObject;
import org.joml.Matrix4f;
import org.lwjgl.opengl.GL30;
import org.lwjgl.system.MemoryUtil;
import java.util.List;
import java.util.function.BooleanSupplier;
import static org.lwjgl.opengl.GL11C.GL_ALWAYS;
import static org.lwjgl.opengl.GL11C.GL_DEPTH_TEST;
import static org.lwjgl.opengl.GL11C.GL_EQUAL;
import static org.lwjgl.opengl.GL11C.GL_KEEP;
import static org.lwjgl.opengl.GL11C.GL_NEAREST;
import static org.lwjgl.opengl.GL11C.GL_REPLACE;
import static org.lwjgl.opengl.GL11C.GL_STENCIL_TEST;
import static org.lwjgl.opengl.GL11C.glColorMask;
import static org.lwjgl.opengl.GL11C.glDisable;
import static org.lwjgl.opengl.GL11C.glEnable;
import static org.lwjgl.opengl.GL11C.glStencilFunc;
import static org.lwjgl.opengl.GL11C.glStencilMask;
import static org.lwjgl.opengl.GL11C.glStencilOp;
import static org.lwjgl.opengl.GL30C.GL_FRAMEBUFFER;
import static org.lwjgl.opengl.GL30C.glBindFramebuffer;
import static org.lwjgl.opengl.GL42.GL_DEPTH_BUFFER_BIT;
import static org.lwjgl.opengl.GL42.GL_LEQUAL;
import static org.lwjgl.opengl.GL42.GL_NOTEQUAL;
import static org.lwjgl.opengl.GL42.glDepthFunc;
import static org.lwjgl.opengl.GL42.*;
import static org.lwjgl.opengl.GL45.glClearNamedFramebufferfi;
import static org.lwjgl.opengl.GL45C.glBindTextureUnit;
import static org.lwjgl.opengl.GL45C.glBlitNamedFramebuffer;
public abstract class AbstractRenderPipeline extends TrackedObject {
private final BooleanSupplier frexStillHasWork;
private final AsyncNodeManager nodeManager;
private final NodeCleaner nodeCleaner;
private final HierarchicalOcclusionTraverser traversal;
protected AbstractSectionRenderer<?,?> sectionRenderer;
private final FullscreenBlit depthMaskBlit = new FullscreenBlit("voxy:post/fullscreen2.vert", "voxy:post/noop.frag");
private final FullscreenBlit depthSetBlit = new FullscreenBlit("voxy:post/fullscreen2.vert", "voxy:post/depth0.frag");
protected AbstractRenderPipeline(AsyncNodeManager nodeManager, NodeCleaner nodeCleaner, HierarchicalOcclusionTraverser traversal, BooleanSupplier frexSupplier) {
this.frexStillHasWork = frexSupplier;
this.nodeManager = nodeManager;
this.nodeCleaner = nodeCleaner;
this.traversal = traversal;
}
//Allows pipelines to configure model baking system
public void setupExtraModelBakeryData(ModelBakerySubsystem modelService) {}
public final void setSectionRenderer(AbstractSectionRenderer<?,?> sectionRenderer) {//Stupid java ordering not allowing something pre super
if (this.sectionRenderer != null) throw new IllegalStateException();
this.sectionRenderer = sectionRenderer;
}
//Called before the pipeline starts running, used to update uniforms etc
public void preSetup(Viewport<?> viewport) {
}
protected abstract int setup(Viewport<?> viewport, int sourceFramebuffer, int srcWidth, int srcHeight);
protected abstract void postOpaquePreTranslucent(Viewport<?> viewport);
protected void finish(Viewport<?> viewport, int sourceFrameBuffer, int srcWidth, int srcHeight) {
glDisable(GL_STENCIL_TEST);
glBindFramebuffer(GL_FRAMEBUFFER, sourceFrameBuffer);
}
public void runPipeline(Viewport<?> viewport, int sourceFrameBuffer, int srcWidth, int srcHeight) {
int depthTexture = this.setup(viewport, sourceFrameBuffer, srcWidth, srcHeight);
var rs = ((AbstractSectionRenderer)this.sectionRenderer);
rs.renderOpaque(viewport);
this.innerPrimaryWork(viewport, depthTexture);
rs.buildDrawCalls(viewport);
rs.renderTemporal(viewport);
this.postOpaquePreTranslucent(viewport);
rs.renderTranslucent(viewport);
this.finish(viewport, sourceFrameBuffer, srcWidth, srcHeight);
glBindFramebuffer(GL_FRAMEBUFFER, sourceFrameBuffer);
}
protected void initDepthStencil(int sourceFrameBuffer, int targetFb, int srcWidth, int srcHeight, int width, int height) {
glClearNamedFramebufferfi(targetFb, GL_DEPTH_STENCIL, 0, 1.0f, 1);
glBlitNamedFramebuffer(sourceFrameBuffer, targetFb, 0,0, srcWidth, srcHeight, 0,0, width, height, GL_DEPTH_BUFFER_BIT, GL_NEAREST);
glBindFramebuffer(GL30.GL_FRAMEBUFFER, targetFb);
/*
if (Capabilities.INSTANCE.isMesa){
glClearStencil(1);
glClear(GL_STENCIL_BUFFER_BIT);
}*/
//This whole thing is hell, we basicly want to create a mask stenicel/depth mask specificiclly
// in theory we could do this in a single pass by passing in the depth buffer from the sourceFrambuffer
// but the current implmentation does a 2 pass system
glEnable(GL_STENCIL_TEST);
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
glStencilFunc(GL_ALWAYS, 0, 0xFF);
glStencilMask(0xFF);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_NOTEQUAL);//If != 1 pass
glColorMask(false,false,false,false);
//We do here
this.depthMaskBlit.blit();
glDisable(GL_DEPTH_TEST);
//Blit depth 0 where stencil is 0
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
glStencilFunc(GL_EQUAL, 0, 0xFF);
this.depthSetBlit.blit();
glDepthFunc(GL_LEQUAL);
glColorMask(true,true,true,true);
//Make voxy terrain render only where there isnt mc terrain
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
glStencilFunc(GL_EQUAL, 1, 0xFF);
}
private static final long SCRATCH = MemoryUtil.nmemAlloc(4*4*4);
protected static void transformBlitDepth(FullscreenBlit blitShader, int srcDepthTex, int dstFB, Viewport<?> viewport, Matrix4f targetTransform) {
glBindFramebuffer(GL30.GL_FRAMEBUFFER, dstFB);
blitShader.bind();
glBindTextureUnit(0, srcDepthTex);
new Matrix4f(viewport.MVP).invert().getToAddress(SCRATCH);
nglUniformMatrix4fv(1, 1, false, SCRATCH);//inverse fromProjection
targetTransform.getToAddress(SCRATCH);//new Matrix4f(tooProjection).mul(vp.modelView).get(data);
nglUniformMatrix4fv(2, 1, false, SCRATCH);//tooProjection
glEnable(GL_DEPTH_TEST);
//We keep the stencil test on with the emitting, only to where non terrain is rendered
blitShader.blit();
glDisable(GL_STENCIL_TEST);
glDisable(GL_DEPTH_TEST);
}
protected void innerPrimaryWork(Viewport<?> viewport, int depthBuffer) {
//Compute the mip chain
viewport.hiZBuffer.buildMipChain(depthBuffer, viewport.width, viewport.height);
do {
TimingStatistics.main.stop();
TimingStatistics.dynamic.start();
TimingStatistics.D.start();
//Tick download stream
DownloadStream.INSTANCE.tick();
TimingStatistics.D.stop();
this.nodeManager.tick(this.traversal.getNodeBuffer(), this.nodeCleaner);
//glFlush();
this.nodeCleaner.tick(this.traversal.getNodeBuffer());//Probably do this here??
TimingStatistics.dynamic.stop();
TimingStatistics.main.start();
glMemoryBarrier(GL_FRAMEBUFFER_BARRIER_BIT | GL_PIXEL_BUFFER_BARRIER_BIT);
TimingStatistics.I.start();
this.traversal.doTraversal(viewport);
TimingStatistics.I.stop();
} while (this.frexStillHasWork.getAsBoolean());
}
@Override
protected void free0() {
this.sectionRenderer.free();
this.depthMaskBlit.delete();
this.depthSetBlit.delete();
super.free0();
}
public void addDebug(List<String> debug) {
this.sectionRenderer.addDebug(debug);
RenderStatistics.addDebug(debug);
}
//Binds the framebuffer and any other bindings needed for rendering
public abstract void setupAndBindOpaque(Viewport<?> viewport);
public abstract void setupAndBindTranslucent(Viewport<?> viewport);
public void bindUniforms() {
this.bindUniforms(-1);
}
public void bindUniforms(int index) {
}
//null means no function, otherwise return the taa injection function
public String taaFunction(String functionName) {
return this.taaFunction(-1, functionName);
}
public String taaFunction(int uboBindingPoint, String functionName) {
return null;
}
//null means dont transform the shader
public String patchOpaqueShader(AbstractSectionRenderer<?,?> renderer, String input) {
return null;
}
//Returning null means apply the same patch as the opaque
public String patchTranslucentShader(AbstractSectionRenderer<?,?> renderer, String input) {
return null;
}
//Null means no scaling factor
public float[] getRenderScalingFactor() {return null;}
}

View File

@@ -0,0 +1,262 @@
package me.cortex.voxy.client.core;
import me.cortex.voxy.client.core.gl.GlBuffer;
import me.cortex.voxy.client.core.model.ModelBakerySubsystem;
import me.cortex.voxy.client.core.rendering.Viewport;
import me.cortex.voxy.client.core.rendering.hierachical.AsyncNodeManager;
import me.cortex.voxy.client.core.rendering.hierachical.HierarchicalOcclusionTraverser;
import me.cortex.voxy.client.core.rendering.hierachical.NodeCleaner;
import me.cortex.voxy.client.core.rendering.post.FullscreenBlit;
import me.cortex.voxy.client.core.rendering.section.AbstractSectionRenderer;
import me.cortex.voxy.client.core.rendering.util.DepthFramebuffer;
import me.cortex.voxy.client.core.rendering.util.UploadStream;
import me.cortex.voxy.client.iris.IrisVoxyRenderPipelineData;
import net.irisshaders.iris.shaderpack.materialmap.WorldRenderingSettings;
import org.joml.Matrix4f;
import org.lwjgl.opengl.GL30;
import java.util.List;
import java.util.function.BooleanSupplier;
import static org.lwjgl.opengl.GL11.GL_DEPTH_BUFFER_BIT;
import static org.lwjgl.opengl.GL30C.*;
import static org.lwjgl.opengl.GL31.GL_UNIFORM_BUFFER;
import static org.lwjgl.opengl.GL45C.*;
public class IrisVoxyRenderPipeline extends AbstractRenderPipeline {
private final IrisVoxyRenderPipelineData data;
private final FullscreenBlit depthBlit = new FullscreenBlit("voxy:post/blit_texture_depth_cutout.frag");
public final DepthFramebuffer fb = new DepthFramebuffer(GL_DEPTH24_STENCIL8);
public final DepthFramebuffer fbTranslucent = new DepthFramebuffer(GL_DEPTH24_STENCIL8);
private final GlBuffer shaderUniforms;
public IrisVoxyRenderPipeline(IrisVoxyRenderPipelineData data, AsyncNodeManager nodeManager, NodeCleaner nodeCleaner, HierarchicalOcclusionTraverser traversal, BooleanSupplier frexSupplier) {
super(nodeManager, nodeCleaner, traversal, frexSupplier);
this.data = data;
if (this.data.thePipeline != null) {
throw new IllegalStateException("Pipeline data already bound");
}
this.data.thePipeline = this;
//Bind the drawbuffers
var oDT = this.data.opaqueDrawTargets;
int[] binding = new int[oDT.length];
for (int i = 0; i < oDT.length; i++) {
binding[i] = GL30.GL_COLOR_ATTACHMENT0+i;
glNamedFramebufferTexture(this.fb.framebuffer.id, GL30.GL_COLOR_ATTACHMENT0+i, oDT[i], 0);
}
glNamedFramebufferDrawBuffers(this.fb.framebuffer.id, binding);
var tDT = this.data.translucentDrawTargets;
binding = new int[tDT.length];
for (int i = 0; i < tDT.length; i++) {
binding[i] = GL30.GL_COLOR_ATTACHMENT0+i;
glNamedFramebufferTexture(this.fbTranslucent.framebuffer.id, GL30.GL_COLOR_ATTACHMENT0+i, tDT[i], 0);
}
glNamedFramebufferDrawBuffers(this.fbTranslucent.framebuffer.id, binding);
this.fb.framebuffer.verify();
this.fbTranslucent.framebuffer.verify();
if (data.getUniforms() != null) {
this.shaderUniforms = new GlBuffer(data.getUniforms().size());
} else {
this.shaderUniforms = null;
}
}
@Override
public void setupExtraModelBakeryData(ModelBakerySubsystem modelService) {
modelService.factory.setCustomBlockStateMapping(WorldRenderingSettings.INSTANCE.getBlockStateIds());
}
@Override
public void free() {
if (this.data.thePipeline != this) {
throw new IllegalStateException();
}
this.data.thePipeline = null;
this.depthBlit.delete();
this.fb.free();
this.fbTranslucent.free();
if (this.shaderUniforms != null) {
this.shaderUniforms.free();
}
super.free0();
}
@Override
public void preSetup(Viewport<?> viewport) {
super.preSetup(viewport);
if (this.shaderUniforms != null) {
//Update the uniforms
long ptr = UploadStream.INSTANCE.uploadTo(this.shaderUniforms);
this.data.getUniforms().updater().accept(ptr);
UploadStream.INSTANCE.commit();
}
}
@Override
protected int setup(Viewport<?> viewport, int sourceFramebuffer, int srcWidth, int srcHeight) {
this.fb.resize(viewport.width, viewport.height);
this.fbTranslucent.resize(viewport.width, viewport.height);
if (false) {//TODO: only do this if shader specifies
//Clear the colour component
glBindFramebuffer(GL_FRAMEBUFFER, this.fb.framebuffer.id);
glClearColor(0, 0, 0, 0);
glClear(GL_COLOR_BUFFER_BIT);
}
if (this.data.useViewportDims) {
srcWidth = viewport.width;
srcHeight = viewport.height;
}
this.initDepthStencil(sourceFramebuffer, this.fb.framebuffer.id, srcWidth, srcHeight, viewport.width, viewport.height);
return this.fb.getDepthTex().id;
}
@Override
protected void postOpaquePreTranslucent(Viewport<?> viewport) {
int msk = GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT;
if (true) {//TODO: make shader specified
if (false) {//TODO: only do this if shader specifies
glBindFramebuffer(GL_FRAMEBUFFER, this.fbTranslucent.framebuffer.id);
glClearColor(0, 0, 0, 0);
glClear(GL_COLOR_BUFFER_BIT);
}
} else {
msk |= GL_COLOR_BUFFER_BIT;
}
glBlitNamedFramebuffer(this.fb.framebuffer.id, this.fbTranslucent.framebuffer.id, 0,0, viewport.width, viewport.height, 0,0, viewport.width, viewport.height, msk, GL_NEAREST);
}
@Override
protected void finish(Viewport<?> viewport, int sourceFrameBuffer, int srcWidth, int srcHeight) {
if (this.data.renderToVanillaDepth && srcWidth == viewport.width && srcHeight == viewport.height) {//We can only depthblit out if destination size is the same
glColorMask(false, false, false, false);
AbstractRenderPipeline.transformBlitDepth(this.depthBlit,
this.fbTranslucent.getDepthTex().id, sourceFrameBuffer,
viewport, new Matrix4f(viewport.vanillaProjection).mul(viewport.modelView));
glColorMask(true, true, true, true);
}
}
@Override
public void bindUniforms() {
this.bindUniforms(UNIFORM_BINDING_POINT);
}
@Override
public void bindUniforms(int bindingPoint) {
if (this.shaderUniforms != null) {
GL30.glBindBufferBase(GL_UNIFORM_BUFFER, bindingPoint, this.shaderUniforms.id);// todo: dont randomly select this to 5
}
}
private void doBindings() {
this.bindUniforms();
if (this.data.getSsboSet() != null) {
this.data.getSsboSet().bindingFunction().accept(10);
}
if (this.data.getImageSet() != null) {
this.data.getImageSet().bindingFunction().accept(6);
}
}
@Override
public void setupAndBindOpaque(Viewport<?> viewport) {
this.fb.bind();
this.doBindings();
}
@Override
public void setupAndBindTranslucent(Viewport<?> viewport) {
this.fbTranslucent.bind();
this.doBindings();
if (this.data.getBlender() != null) {
this.data.getBlender().run();
}
}
@Override
public void addDebug(List<String> debug) {
debug.add("Using: " + this.getClass().getSimpleName());
super.addDebug(debug);
}
private static final int UNIFORM_BINDING_POINT = 5;//TODO make ths binding point... not randomly 5
private StringBuilder buildGenericShaderHeader(AbstractSectionRenderer<?, ?> renderer, String input) {
StringBuilder builder = new StringBuilder(input).append("\n\n\n");
if (this.data.getUniforms() != null) {
builder.append("layout(binding = "+UNIFORM_BINDING_POINT+", std140) uniform ShaderUniformBindings ")
.append(this.data.getUniforms().layout())
.append(";\n\n");
}
if (this.data.getSsboSet() != null) {
builder.append("#define BUFFER_BINDING_INDEX_BASE 10\n");//TODO: DONT RANDOMLY MAKE THIS 10
builder.append(this.data.getSsboSet().layout()).append("\n\n");
}
if (this.data.getImageSet() != null) {
builder.append("#define BASE_SAMPLER_BINDING_INDEX 6\n");//TODO: DONT RANDOMLY MAKE THIS 6
builder.append(this.data.getImageSet().layout()).append("\n\n");
}
return builder.append("\n\n");
}
@Override
public String patchOpaqueShader(AbstractSectionRenderer<?, ?> renderer, String input) {
var builder = this.buildGenericShaderHeader(renderer, input);
builder.append(this.data.opaqueFragPatch());
return builder.toString();
}
@Override
public String patchTranslucentShader(AbstractSectionRenderer<?, ?> renderer, String input) {
if (this.data.translucentFragPatch() == null) return null;
var builder = this.buildGenericShaderHeader(renderer, input);
builder.append(this.data.translucentFragPatch());
return builder.toString();
}
@Override
public String taaFunction(String functionName) {
return this.taaFunction(UNIFORM_BINDING_POINT, functionName);
}
@Override
public String taaFunction(int uboBindingPoint, String functionName) {
var builder = new StringBuilder();
if (this.data.getUniforms() != null) {
builder.append("layout(binding = "+uboBindingPoint+", std140) uniform ShaderUniformBindings ")
.append(this.data.getUniforms().layout())
.append(";\n\n");
}
builder.append("vec2 ").append(functionName).append("()\n");
builder.append(this.data.TAA);
builder.append("\n");
return builder.toString();
}
@Override
public float[] getRenderScalingFactor() {
return this.data.resolutionScale;
}
}

View File

@@ -0,0 +1,150 @@
package me.cortex.voxy.client.core;
import me.cortex.voxy.client.config.VoxyConfig;
import me.cortex.voxy.client.core.gl.GlFramebuffer;
import me.cortex.voxy.client.core.gl.GlTexture;
import me.cortex.voxy.client.core.gl.shader.Shader;
import me.cortex.voxy.client.core.gl.shader.ShaderType;
import me.cortex.voxy.client.core.rendering.Viewport;
import me.cortex.voxy.client.core.rendering.hierachical.AsyncNodeManager;
import me.cortex.voxy.client.core.rendering.hierachical.HierarchicalOcclusionTraverser;
import me.cortex.voxy.client.core.rendering.hierachical.NodeCleaner;
import me.cortex.voxy.client.core.rendering.post.FullscreenBlit;
import me.cortex.voxy.client.core.rendering.util.DepthFramebuffer;
import net.minecraft.client.MinecraftClient;
import org.joml.Matrix4f;
import org.lwjgl.system.MemoryStack;
import java.util.function.BooleanSupplier;
import static org.lwjgl.opengl.ARBComputeShader.glDispatchCompute;
import static org.lwjgl.opengl.ARBShaderImageLoadStore.glBindImageTexture;
import static org.lwjgl.opengl.GL11.GL_BLEND;
import static org.lwjgl.opengl.GL11.GL_ONE;
import static org.lwjgl.opengl.GL11.GL_ONE_MINUS_SRC_ALPHA;
import static org.lwjgl.opengl.GL11.GL_SRC_ALPHA;
import static org.lwjgl.opengl.GL11.glEnable;
import static org.lwjgl.opengl.GL11C.GL_NEAREST;
import static org.lwjgl.opengl.GL11C.GL_RGBA8;
import static org.lwjgl.opengl.GL14.glBlendFuncSeparate;
import static org.lwjgl.opengl.GL15.GL_READ_WRITE;
import static org.lwjgl.opengl.GL30C.*;
import static org.lwjgl.opengl.GL43.GL_DEPTH_STENCIL_TEXTURE_MODE;
import static org.lwjgl.opengl.GL45C.glBindTextureUnit;
import static org.lwjgl.opengl.GL45C.glTextureParameterf;
public class NormalRenderPipeline extends AbstractRenderPipeline {
private GlTexture colourTex;
private GlTexture colourSSAOTex;
private final GlFramebuffer fbSSAO = new GlFramebuffer();
private final DepthFramebuffer fb = new DepthFramebuffer(GL_DEPTH24_STENCIL8);
private final boolean useEnvFog;
private final FullscreenBlit finalBlit;
private final Shader ssaoCompute = Shader.make()
.add(ShaderType.COMPUTE, "voxy:post/ssao.comp")
.compile();
protected NormalRenderPipeline(AsyncNodeManager nodeManager, NodeCleaner nodeCleaner, HierarchicalOcclusionTraverser traversal, BooleanSupplier frexSupplier) {
super(nodeManager, nodeCleaner, traversal, frexSupplier);
this.useEnvFog = VoxyConfig.CONFIG.useEnvironmentalFog;
this.finalBlit = new FullscreenBlit("voxy:post/blit_texture_depth_cutout.frag",
a->a.defineIf("USE_ENV_FOG", this.useEnvFog).define("EMIT_COLOUR"));
}
@Override
protected int setup(Viewport<?> viewport, int sourceFB, int srcWidth, int srcHeight) {
if (this.colourTex == null || this.colourTex.getHeight() != viewport.height || this.colourTex.getWidth() != viewport.width) {
if (this.colourTex != null) {
this.colourTex.free();
this.colourSSAOTex.free();
}
this.fb.resize(viewport.width, viewport.height);
this.colourTex = new GlTexture().store(GL_RGBA8, 1, viewport.width, viewport.height);
this.colourSSAOTex = new GlTexture().store(GL_RGBA8, 1, viewport.width, viewport.height);
this.fb.framebuffer.bind(GL_COLOR_ATTACHMENT0, this.colourTex).verify();
this.fbSSAO.bind(GL_DEPTH_STENCIL_ATTACHMENT, this.fb.getDepthTex()).bind(GL_COLOR_ATTACHMENT0, this.colourSSAOTex).verify();
glTextureParameterf(this.colourTex.id, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTextureParameterf(this.colourTex.id, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTextureParameterf(this.colourSSAOTex.id, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTextureParameterf(this.colourSSAOTex.id, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTextureParameterf(this.fb.getDepthTex().id, GL_DEPTH_STENCIL_TEXTURE_MODE, GL_DEPTH_COMPONENT);
}
this.initDepthStencil(sourceFB, this.fb.framebuffer.id, viewport.width, viewport.height, viewport.width, viewport.height);
return this.fb.getDepthTex().id;
}
@Override
protected void postOpaquePreTranslucent(Viewport<?> viewport) {
this.ssaoCompute.bind();
try (var stack = MemoryStack.stackPush()) {
long ptr = stack.nmalloc(4*4*4);
viewport.MVP.getToAddress(ptr);
nglUniformMatrix4fv(3, 1, false, ptr);//MVP
viewport.MVP.invert(new Matrix4f()).getToAddress(ptr);
nglUniformMatrix4fv(4, 1, false, ptr);//invMVP
}
glBindImageTexture(0, this.colourSSAOTex.id, 0, false,0, GL_READ_WRITE, GL_RGBA8);
glBindTextureUnit(1, this.fb.getDepthTex().id);
glBindTextureUnit(2, this.colourTex.id);
glDispatchCompute((viewport.width+31)/32, (viewport.height+31)/32, 1);
glBindFramebuffer(GL_FRAMEBUFFER, this.fbSSAO.id);
}
@Override
protected void finish(Viewport<?> viewport, int sourceFrameBuffer, int srcWidth, int srcHeight) {
this.finalBlit.bind();
if (this.useEnvFog) {
float start = viewport.fogParameters.environmentalStart();
float end = viewport.fogParameters.environmentalEnd();
float invEndFogDelta = 1f/(end-start);
float endDistance = MinecraftClient.getInstance().gameRenderer.getViewDistanceBlocks()*1.5f;
glUniform3f(4, endDistance, invEndFogDelta, Math.abs(start)*invEndFogDelta);
glUniform3f(5, viewport.fogParameters.red(), viewport.fogParameters.green(), viewport.fogParameters.blue());
}
glBindTextureUnit(3, this.colourSSAOTex.id);
//Do alpha blending
glEnable(GL_BLEND);
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
AbstractRenderPipeline.transformBlitDepth(this.finalBlit, this.fb.getDepthTex().id, sourceFrameBuffer, viewport, new Matrix4f(viewport.vanillaProjection).mul(viewport.modelView));
glDisable(GL_BLEND);
//glBlitNamedFramebuffer(this.fbSSAO.id, sourceFrameBuffer, 0,0, viewport.width, viewport.height, 0,0, viewport.width, viewport.height, GL_COLOR_BUFFER_BIT, GL_NEAREST);
}
@Override
public void setupAndBindOpaque(Viewport<?> viewport) {
this.fb.bind();
}
@Override
public void setupAndBindTranslucent(Viewport<?> viewport) {
glBindFramebuffer(GL_FRAMEBUFFER, this.fbSSAO.id);
}
@Override
public void free() {
this.finalBlit.delete();
this.ssaoCompute.free();
this.fb.free();
this.fbSSAO.free();
if (this.colourTex != null) {
this.colourTex.free();
this.colourSSAOTex.free();
}
super.free0();
}
}

View File

@@ -0,0 +1,48 @@
package me.cortex.voxy.client.core;
import me.cortex.voxy.client.core.rendering.hierachical.AsyncNodeManager;
import me.cortex.voxy.client.core.rendering.hierachical.HierarchicalOcclusionTraverser;
import me.cortex.voxy.client.core.rendering.hierachical.NodeCleaner;
import me.cortex.voxy.client.core.util.IrisUtil;
import me.cortex.voxy.client.iris.IGetIrisVoxyPipelineData;
import me.cortex.voxy.common.Logger;
import net.irisshaders.iris.Iris;
import net.irisshaders.iris.api.v0.IrisApi;
import java.util.function.BooleanSupplier;
public class RenderPipelineFactory {
public static AbstractRenderPipeline createPipeline(AsyncNodeManager nodeManager, NodeCleaner nodeCleaner, HierarchicalOcclusionTraverser traversal, BooleanSupplier frexSupplier) {
//Note this is where will choose/create e.g. IrisRenderPipeline or normal pipeline
AbstractRenderPipeline pipeline = null;
if (IrisUtil.IRIS_INSTALLED && IrisUtil.SHADER_SUPPORT) {
pipeline = createIrisPipeline(nodeManager, nodeCleaner, traversal, frexSupplier);
}
if (pipeline == null) {
pipeline = new NormalRenderPipeline(nodeManager, nodeCleaner, traversal, frexSupplier);
}
return pipeline;
}
private static AbstractRenderPipeline createIrisPipeline(AsyncNodeManager nodeManager, NodeCleaner nodeCleaner, HierarchicalOcclusionTraverser traversal, BooleanSupplier frexSupplier) {
var irisPipe = Iris.getPipelineManager().getPipelineNullable();
if (irisPipe == null) {
return null;
}
if (irisPipe instanceof IGetIrisVoxyPipelineData getVoxyPipeData) {
var pipeData = getVoxyPipeData.voxy$getPipelineData();
if (pipeData == null) {
return null;
}
Logger.info("Creating voxy iris render pipeline");
try {
return new IrisVoxyRenderPipeline(pipeData, nodeManager, nodeCleaner, traversal, frexSupplier);
} catch (Exception e) {
Logger.error("Failed to create iris render pipeline", e);
IrisUtil.disableIrisShaders();
return null;
}
}
return null;
}
}

View File

@@ -2,7 +2,6 @@ package me.cortex.voxy.client.core;
import com.mojang.blaze3d.opengl.GlConst; import com.mojang.blaze3d.opengl.GlConst;
import com.mojang.blaze3d.opengl.GlStateManager; import com.mojang.blaze3d.opengl.GlStateManager;
import com.mojang.blaze3d.systems.RenderSystem;
import me.cortex.voxy.client.TimingStatistics; import me.cortex.voxy.client.TimingStatistics;
import me.cortex.voxy.client.VoxyClient; import me.cortex.voxy.client.VoxyClient;
import me.cortex.voxy.client.config.VoxyConfig; import me.cortex.voxy.client.config.VoxyConfig;
@@ -10,113 +9,336 @@ import me.cortex.voxy.client.core.gl.Capabilities;
import me.cortex.voxy.client.core.gl.GlBuffer; import me.cortex.voxy.client.core.gl.GlBuffer;
import me.cortex.voxy.client.core.gl.GlTexture; import me.cortex.voxy.client.core.gl.GlTexture;
import me.cortex.voxy.client.core.model.ModelBakerySubsystem; import me.cortex.voxy.client.core.model.ModelBakerySubsystem;
import me.cortex.voxy.client.core.model.ModelStore;
import me.cortex.voxy.client.core.rendering.ChunkBoundRenderer; import me.cortex.voxy.client.core.rendering.ChunkBoundRenderer;
import me.cortex.voxy.client.core.rendering.RenderDistanceTracker; import me.cortex.voxy.client.core.rendering.RenderDistanceTracker;
import me.cortex.voxy.client.core.rendering.RenderService; import me.cortex.voxy.client.core.rendering.Viewport;
import me.cortex.voxy.client.core.rendering.building.RenderDataFactory; import me.cortex.voxy.client.core.rendering.ViewportSelector;
import me.cortex.voxy.client.core.rendering.building.RenderGenerationService; import me.cortex.voxy.client.core.rendering.building.RenderGenerationService;
import me.cortex.voxy.client.core.rendering.post.PostProcessing; import me.cortex.voxy.client.core.rendering.hierachical.AsyncNodeManager;
import me.cortex.voxy.client.core.rendering.hierachical.HierarchicalOcclusionTraverser;
import me.cortex.voxy.client.core.rendering.hierachical.NodeCleaner;
import me.cortex.voxy.client.core.rendering.section.AbstractSectionRenderer;
import me.cortex.voxy.client.core.rendering.section.MDICSectionRenderer;
import me.cortex.voxy.client.core.rendering.section.MeshEXTSectionRenderer;
import me.cortex.voxy.client.core.rendering.section.geometry.BasicSectionGeometryData;
import me.cortex.voxy.client.core.rendering.section.geometry.IGeometryData;
import me.cortex.voxy.client.core.rendering.util.DownloadStream; import me.cortex.voxy.client.core.rendering.util.DownloadStream;
import me.cortex.voxy.client.core.rendering.util.PrintfDebugUtil; import me.cortex.voxy.client.core.rendering.util.PrintfDebugUtil;
import me.cortex.voxy.client.core.rendering.util.SharedIndexBuffer;
import me.cortex.voxy.client.core.rendering.util.UploadStream; import me.cortex.voxy.client.core.rendering.util.UploadStream;
import me.cortex.voxy.client.core.util.GPUTiming;
import me.cortex.voxy.client.core.util.IrisUtil; import me.cortex.voxy.client.core.util.IrisUtil;
import me.cortex.voxy.common.Logger; import me.cortex.voxy.common.Logger;
import me.cortex.voxy.common.thread.ServiceThreadPool; import me.cortex.voxy.common.thread.ServiceThreadPool;
import me.cortex.voxy.common.world.WorldEngine; import me.cortex.voxy.common.world.WorldEngine;
import me.cortex.voxy.common.world.WorldSection;
import me.cortex.voxy.common.world.other.Mapper;
import me.cortex.voxy.commonImpl.VoxyCommon; import me.cortex.voxy.commonImpl.VoxyCommon;
import net.caffeinemc.mods.sodium.client.render.chunk.ChunkRenderMatrices; import net.caffeinemc.mods.sodium.client.render.chunk.ChunkRenderMatrices;
import net.caffeinemc.mods.sodium.client.render.chunk.terrain.DefaultTerrainRenderPasses; import net.caffeinemc.mods.sodium.client.util.FogParameters;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gl.GlBackend;
import net.minecraft.client.render.Camera;
import net.minecraft.client.render.Frustum;
import org.joml.Matrix4f; import org.joml.Matrix4f;
import org.joml.Matrix4fc; import org.joml.Matrix4fc;
import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL11;
import java.util.ArrayList; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Random;
import java.util.concurrent.atomic.AtomicInteger;
import static org.lwjgl.opengl.GL11.GL_ONE;
import static org.lwjgl.opengl.GL11.GL_ONE_MINUS_SRC_ALPHA;
import static org.lwjgl.opengl.GL11.GL_SRC_ALPHA;
import static org.lwjgl.opengl.GL11.GL_VIEWPORT; import static org.lwjgl.opengl.GL11.GL_VIEWPORT;
import static org.lwjgl.opengl.GL11.glGetIntegerv; import static org.lwjgl.opengl.GL11.glGetIntegerv;
import static org.lwjgl.opengl.GL11C.*; import static org.lwjgl.opengl.GL11C.*;
import static org.lwjgl.opengl.GL14.glBlendFuncSeparate; import static org.lwjgl.opengl.GL30C.*;
import static org.lwjgl.opengl.GL30C.GL_DRAW_FRAMEBUFFER_BINDING;
import static org.lwjgl.opengl.GL30C.glBindFramebuffer;
import static org.lwjgl.opengl.GL33.glBindSampler; import static org.lwjgl.opengl.GL33.glBindSampler;
import static org.lwjgl.opengl.GL43.GL_SHADER_STORAGE_BUFFER;
import static org.lwjgl.opengl.GL43C.GL_SHADER_STORAGE_BUFFER_BINDING;
public class VoxyRenderSystem { public class VoxyRenderSystem {
private final RenderService renderer;
private final PostProcessing postProcessing;
private final WorldEngine worldIn; private final WorldEngine worldIn;
private final ModelBakerySubsystem modelService;
private final RenderGenerationService renderGen;
private final IGeometryData geometryData;
private final AsyncNodeManager nodeManager;
private final NodeCleaner nodeCleaner;
private final HierarchicalOcclusionTraverser traversal;
private final RenderDistanceTracker renderDistanceTracker; private final RenderDistanceTracker renderDistanceTracker;
public final ChunkBoundRenderer chunkBoundRenderer; public final ChunkBoundRenderer chunkBoundRenderer;
private final ViewportSelector<?> viewportSelector;
private final AbstractRenderPipeline pipeline;
private static AbstractSectionRenderer<?,?> createSectionRenderer(AbstractRenderPipeline pipeline, ModelStore modelStore, IGeometryData geometryData) {
//TODO: need todo a thing where selects optimal section render based on if supports the pipeline and geometry data type
//return new MDICSectionRenderer(pipeline, modelStore, (BasicSectionGeometryData) geometryData);//We only have MDIC backend... for now
return new MeshEXTSectionRenderer(pipeline, modelStore, (BasicSectionGeometryData) geometryData);
}
public VoxyRenderSystem(WorldEngine world, ServiceThreadPool threadPool) { public VoxyRenderSystem(WorldEngine world, ServiceThreadPool threadPool) {
//Keep the world loaded, NOTE: this is done FIRST, to keep and ensure that even if the rest of loading takes more //Keep the world loaded, NOTE: this is done FIRST, to keep and ensure that even if the rest of loading takes more
// than timeout, we keep the world acquired // than timeout, we keep the world acquired
world.acquireRef(); world.acquireRef();
//Fking HATE EVERYTHING AAAAAAAAAAAAAAAA
int[] oldBufferBindings = new int[10];
for (int i = 0; i < oldBufferBindings.length; i++) {
oldBufferBindings[i] = glGetIntegeri(GL_SHADER_STORAGE_BUFFER_BINDING, i);
}
try { try {
//wait for opengl to be finished, this should hopefully ensure all memory allocations are free //wait for opengl to be finished, this should hopefully ensure all memory allocations are free
glFinish(); glFinish();
glFinish(); glFinish();
//Trigger the shared index buffer loading
SharedIndexBuffer.INSTANCE.id();
Capabilities.init();//Ensure clinit is called
this.worldIn = world; this.worldIn = world;
this.renderer = new RenderService(world, threadPool);
this.postProcessing = new PostProcessing();
int minSec = MinecraftClient.getInstance().world.getBottomSectionCoord() >> 5;
int maxSec = (MinecraftClient.getInstance().world.getTopSectionCoord() - 1) >> 5;
//Do some very cheeky stuff for MiB long geometryCapacity = getGeometryBufferSize();
if (false) { {
minSec = -8;
maxSec = 7;
this.modelService = new ModelBakerySubsystem(world.getMapper());
this.renderGen = new RenderGenerationService(world, this.modelService, threadPool, false, () -> true);
this.geometryData = new BasicSectionGeometryData(1 << 20, geometryCapacity);
this.nodeManager = new AsyncNodeManager(1 << 21, this.geometryData, this.renderGen);
this.nodeCleaner = new NodeCleaner(this.nodeManager);
this.traversal = new HierarchicalOcclusionTraverser(this.nodeManager, this.nodeCleaner, this.renderGen);
world.setDirtyCallback(this.nodeManager::worldEvent);
Arrays.stream(world.getMapper().getBiomeEntries()).forEach(this.modelService::addBiome);
world.getMapper().setBiomeCallback(this.modelService::addBiome);
this.nodeManager.start();
} }
this.renderDistanceTracker = new RenderDistanceTracker(20, this.pipeline = RenderPipelineFactory.createPipeline(this.nodeManager, this.nodeCleaner, this.traversal, this::frexStillHasWork);
minSec, this.pipeline.setupExtraModelBakeryData(this.modelService);//Configure the model service
maxSec, var sectionRenderer = createSectionRenderer(this.pipeline, this.modelService.getStore(), this.geometryData);
this.renderer::addTopLevelNode, this.pipeline.setSectionRenderer(sectionRenderer);
this.renderer::removeTopLevelNode); this.viewportSelector = new ViewportSelector<>(sectionRenderer::createViewport);
this.renderDistanceTracker.setRenderDistance(VoxyConfig.CONFIG.sectionRenderDistance); {
int minSec = MinecraftClient.getInstance().world.getBottomSectionCoord() >> 5;
int maxSec = (MinecraftClient.getInstance().world.getTopSectionCoord() - 1) >> 5;
this.chunkBoundRenderer = new ChunkBoundRenderer(); //Do some very cheeky stuff for MiB
if (VoxyCommon.IS_MINE_IN_ABYSS) {//TODO: make this somehow configurable
minSec = -8;
maxSec = 7;
}
this.renderDistanceTracker = new RenderDistanceTracker(20,
minSec,
maxSec,
this.nodeManager::addTopLevel,
this.nodeManager::removeTopLevel);
this.renderDistanceTracker.setRenderDistance(VoxyConfig.CONFIG.sectionRenderDistance);
}
this.chunkBoundRenderer = new ChunkBoundRenderer(this.pipeline);
Logger.info("Voxy render system created with " + geometryCapacity + " geometry capacity, using pipeline '" + this.pipeline.getClass().getSimpleName() + "' with renderer '" + sectionRenderer.getClass().getSimpleName() + "'");
} catch (RuntimeException e) { } catch (RuntimeException e) {
world.releaseRef();//If something goes wrong, we must release the world first world.releaseRef();//If something goes wrong, we must release the world first
throw e; throw e;
} }
for (int i = 0; i < oldBufferBindings.length; i++) {
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, i, oldBufferBindings[i]);
}
} }
public void setRenderDistance(int renderDistance) {
this.renderDistanceTracker.setRenderDistance(renderDistance); public Viewport<?> setupViewport(ChunkRenderMatrices matrices, FogParameters fogParameters, double cameraX, double cameraY, double cameraZ) {
var viewport = this.getViewport();
if (viewport == null) {
return null;
}
//Do some very cheeky stuff for MiB
if (VoxyCommon.IS_MINE_IN_ABYSS) {
int sector = (((int)Math.floor(cameraX)>>4)+512)>>10;
cameraX -= sector<<14;//10+4
cameraY += (16+(256-32-sector*30))*16;
}
//cameraY += 100;
var projection = computeProjectionMat(matrices.projection());//RenderSystem.getProjectionMatrix();
//var projection = ShadowMatrices.createOrthoMatrix(160, -16*300, 16*300);
//var projection = new Matrix4f(matrices.projection());
int[] dims = new int[4];
glGetIntegerv(GL_VIEWPORT, dims);
int width = dims[2];
int height = dims[3];
{//Apply render scaling factor
var factor = this.pipeline.getRenderScalingFactor();
if (factor != null) {
width = (int) (width*factor[0]);
height = (int) (height*factor[1]);
}
}
viewport
.setVanillaProjection(matrices.projection())
.setProjection(projection)
.setModelView(new Matrix4f(matrices.modelView()))
.setCamera(cameraX, cameraY, cameraZ)
.setScreenSize(width, height)
.setFogParameters(fogParameters)
.update();
viewport.frameId++;
return viewport;
} }
public void renderOpaque(Viewport<?> viewport) {
if (viewport == null) {
return;
}
TimingStatistics.resetSamplers();
long startTime = System.nanoTime();
TimingStatistics.all.start();
GPUTiming.INSTANCE.marker();//Start marker
TimingStatistics.main.start();
//TODO: optimize
int[] oldBufferBindings = new int[10];
for (int i = 0; i < oldBufferBindings.length; i++) {
oldBufferBindings[i] = glGetIntegeri(GL_SHADER_STORAGE_BUFFER_BINDING, i);
}
int oldFB = GL11.glGetInteger(GL_DRAW_FRAMEBUFFER_BINDING);
int boundFB = oldFB;
int[] dims = new int[4];
glGetIntegerv(GL_VIEWPORT, dims);
glViewport(0,0, viewport.width, viewport.height);
//var target = DefaultTerrainRenderPasses.CUTOUT.getTarget();
//boundFB = ((net.minecraft.client.texture.GlTexture) target.getColorAttachment()).getOrCreateFramebuffer(((GlBackend) RenderSystem.getDevice()).getFramebufferManager(), target.getDepthAttachment());
if (boundFB == 0) {
throw new IllegalStateException("Cannot use the default framebuffer as cannot source from it");
}
//this.autoBalanceSubDivSize();
this.pipeline.preSetup(viewport);
TimingStatistics.E.start();
if (!IrisUtil.irisShadowActive()) {
this.chunkBoundRenderer.render(viewport);
} else {
viewport.depthBoundingBuffer.clear(0);
}
TimingStatistics.E.stop();
//The entire rendering pipeline (excluding the chunkbound thing)
this.pipeline.runPipeline(viewport, boundFB, dims[2], dims[3]);
TimingStatistics.main.stop();
TimingStatistics.postDynamic.start();
PrintfDebugUtil.tick();
//As much dynamic runtime stuff here
{
//Tick upload stream (this is ok to do here as upload ticking is just memory management)
UploadStream.INSTANCE.tick();
while (this.renderDistanceTracker.setCenterAndProcess(viewport.cameraX, viewport.cameraZ) && VoxyClient.isFrexActive());//While FF is active, run until everything is processed
//Done here as is allows less gl state resetup
this.modelService.tick(Math.max(3_000_000-(System.nanoTime()-startTime), 500_000));
}
GPUTiming.INSTANCE.marker();
TimingStatistics.postDynamic.stop();
GPUTiming.INSTANCE.tick();
glBindFramebuffer(GlConst.GL_FRAMEBUFFER, oldFB);
glViewport(dims[0], dims[1], dims[2], dims[3]);
{//Reset state manager stuffs
glUseProgram(0);
glEnable(GL_DEPTH_TEST);
GlStateManager._glBindVertexArray(0);//Clear binding
GlStateManager._activeTexture(GlConst.GL_TEXTURE1);
for (int i = 0; i < 12; i++) {
GlStateManager._activeTexture(GlConst.GL_TEXTURE0+i);
GlStateManager._bindTexture(0);
glBindSampler(i, 0);
}
IrisUtil.clearIrisSamplers();//Thanks iris (sigh)
//TODO: should/needto actually restore all of these, not just clear them
//Clear all the bindings
for (int i = 0; i < oldBufferBindings.length; i++) {
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, i, oldBufferBindings[i]);
}
//((SodiumShader) Iris.getPipelineManager().getPipelineNullable().getSodiumPrograms().getProgram(DefaultTerrainRenderPasses.CUTOUT).getInterface()).setupState(DefaultTerrainRenderPasses.CUTOUT, fogParameters);
}
TimingStatistics.all.stop();
/*
TimingStatistics.F.start();
this.postProcessing.setup(viewport.width, viewport.height, boundFB);
TimingStatistics.F.stop();
this.renderer.renderFarAwayOpaque(viewport, this.chunkBoundRenderer.getDepthBoundTexture());
TimingStatistics.F.start();
//Compute the SSAO of the rendered terrain, TODO: fix it breaking depth or breaking _something_ am not sure what
this.postProcessing.computeSSAO(viewport.MVP);
TimingStatistics.F.stop();
TimingStatistics.G.start();
//We can render the translucent directly after as it is the furthest translucent objects
this.renderer.renderFarAwayTranslucent(viewport, this.chunkBoundRenderer.getDepthBoundTexture());
TimingStatistics.G.stop();
TimingStatistics.F.start();
this.postProcessing.renderPost(viewport, matrices.projection(), boundFB);
TimingStatistics.F.stop();
*/
}
private void autoBalanceSubDivSize() { private void autoBalanceSubDivSize() {
//only increase quality while there are very few mesh queues, this stops, //only increase quality while there are very few mesh queues, this stops,
// e.g. while flying and is rendering alot of low quality chunks // e.g. while flying and is rendering alot of low quality chunks
boolean canDecreaseSize = this.renderer.getMeshQueueCount() < 5000; boolean canDecreaseSize = this.renderGen.getTaskCount() < 300;
float CHANGE_PER_SECOND = 30; int MIN_FPS = 55;
int MAX_FPS = 65;
float INCREASE_PER_SECOND = 60;
float DECREASE_PER_SECOND = 30;
//Auto fps targeting //Auto fps targeting
if (MinecraftClient.getInstance().getCurrentFps() < 45) { if (MinecraftClient.getInstance().getCurrentFps() < MIN_FPS) {
VoxyConfig.CONFIG.subDivisionSize = Math.min(VoxyConfig.CONFIG.subDivisionSize + CHANGE_PER_SECOND / Math.max(1f, MinecraftClient.getInstance().getCurrentFps()), 256); VoxyConfig.CONFIG.subDivisionSize = Math.min(VoxyConfig.CONFIG.subDivisionSize + INCREASE_PER_SECOND / Math.max(1f, MinecraftClient.getInstance().getCurrentFps()), 256);
} }
if (55 < MinecraftClient.getInstance().getCurrentFps() && canDecreaseSize) { if (MAX_FPS < MinecraftClient.getInstance().getCurrentFps() && canDecreaseSize) {
VoxyConfig.CONFIG.subDivisionSize = Math.max(VoxyConfig.CONFIG.subDivisionSize - CHANGE_PER_SECOND / Math.max(1f, MinecraftClient.getInstance().getCurrentFps()), 30); VoxyConfig.CONFIG.subDivisionSize = Math.max(VoxyConfig.CONFIG.subDivisionSize - DECREASE_PER_SECOND / Math.max(1f, MinecraftClient.getInstance().getCurrentFps()), 28);
} }
} }
@@ -143,117 +365,41 @@ public class VoxyRenderSystem {
).mulLocal(makeProjectionMatrix(16, 16*3000)); ).mulLocal(makeProjectionMatrix(16, 16*3000));
} }
public void renderOpaque(ChunkRenderMatrices matrices, double cameraX, double cameraY, double cameraZ) { private boolean frexStillHasWork() {
if (IrisUtil.irisShadowActive()) { if (!VoxyClient.isFrexActive()) {
return; return false;
} }
TimingStatistics.resetSamplers(); //If frex is running we must tick everything to ensure correctness
UploadStream.INSTANCE.tick();
//Done here as is allows less gl state resetup
//Do some very cheeky stuff for MiB this.modelService.tick(100_000_000);
if (false) { GL11.glFinish();
int sector = (((int)Math.floor(cameraX)>>4)+512)>>10; return this.nodeManager.hasWork() || this.renderGen.getTaskCount()!=0 || !this.modelService.areQueuesEmpty();
cameraX -= sector<<14;//10+4
cameraY += (16+(256-32-sector*30))*16;
}
long startTime = System.nanoTime();
TimingStatistics.all.start();
TimingStatistics.main.start();
int oldFB = GL11.glGetInteger(GL_DRAW_FRAMEBUFFER_BINDING);
int boundFB = oldFB;
//var target = DefaultTerrainRenderPasses.CUTOUT.getTarget();
//boundFB = ((net.minecraft.client.texture.GlTexture) target.getColorAttachment()).getOrCreateFramebuffer(((GlBackend) RenderSystem.getDevice()).getFramebufferManager(), target.getDepthAttachment());
if (boundFB == 0) {
throw new IllegalStateException("Cannot use the default framebuffer as cannot source from it");
}
//this.autoBalanceSubDivSize();
var projection = computeProjectionMat(matrices.projection());//RenderSystem.getProjectionMatrix();
//var projection = new Matrix4f(matrices.projection());
int[] dims = new int[4];
glGetIntegerv(GL_VIEWPORT, dims);
var viewport = this.renderer.getViewport();
viewport
.setProjection(projection)
.setModelView(new Matrix4f(matrices.modelView()))
.setCamera(cameraX, cameraY, cameraZ)
.setScreenSize(dims[2], dims[3])
.update();
viewport.frameId++;
TimingStatistics.E.start();
this.chunkBoundRenderer.render(viewport);
TimingStatistics.E.stop();
TimingStatistics.F.start();
this.postProcessing.setup(viewport.width, viewport.height, boundFB);
TimingStatistics.F.stop();
this.renderer.renderFarAwayOpaque(viewport, this.chunkBoundRenderer.getDepthBoundTexture());
TimingStatistics.F.start();
//Compute the SSAO of the rendered terrain, TODO: fix it breaking depth or breaking _something_ am not sure what
this.postProcessing.computeSSAO(viewport.MVP);
TimingStatistics.F.stop();
TimingStatistics.G.start();
//We can render the translucent directly after as it is the furthest translucent objects
this.renderer.renderFarAwayTranslucent(viewport, this.chunkBoundRenderer.getDepthBoundTexture());
TimingStatistics.G.stop();
TimingStatistics.F.start();
this.postProcessing.renderPost(projection, matrices.projection(), boundFB);
TimingStatistics.F.stop();
TimingStatistics.main.stop();
TimingStatistics.postDynamic.start();
PrintfDebugUtil.tick();
//As much dynamic runtime stuff here
{
//Tick upload stream (this is ok to do here as upload ticking is just memory management)
UploadStream.INSTANCE.tick();
while (this.renderDistanceTracker.setCenterAndProcess(cameraX, cameraZ) && VoxyClient.isFrexActive());//While FF is active, run until everything is processed
//Done here as is allows less gl state resetup
this.renderer.tickModelService(Math.max(3_000_000-(System.nanoTime()-startTime), 500_000));
}
TimingStatistics.postDynamic.stop();
glBindFramebuffer(GlConst.GL_FRAMEBUFFER, oldFB);
{//Reset state manager stuffs
GlStateManager._glBindVertexArray(0);//Clear binding
GlStateManager._activeTexture(GlConst.GL_TEXTURE0);
GlStateManager._bindTexture(0);
glBindSampler(0, 0);
GlStateManager._activeTexture(GlConst.GL_TEXTURE1);
GlStateManager._bindTexture(0);
glBindSampler(1, 0);
GlStateManager._activeTexture(GlConst.GL_TEXTURE2);
GlStateManager._bindTexture(0);
glBindSampler(2, 0);
}
TimingStatistics.all.stop();
} }
public void setRenderDistance(int renderDistance) {
this.renderDistanceTracker.setRenderDistance(renderDistance);
}
public Viewport<?> getViewport() {
if (IrisUtil.irisShadowActive()) {
return null;
}
return this.viewportSelector.getViewport();
}
public void addDebugInfo(List<String> debug) { public void addDebugInfo(List<String> debug) {
debug.add("Buf/Tex [#/Mb]: [" + GlBuffer.getCount() + "/" + (GlBuffer.getTotalSize()/1_000_000) + "],[" + GlTexture.getCount() + "/" + (GlTexture.getEstimatedTotalSize()/1_000_000)+"]"); debug.add("Buf/Tex [#/Mb]: [" + GlBuffer.getCount() + "/" + (GlBuffer.getTotalSize()/1_000_000) + "],[" + GlTexture.getCount() + "/" + (GlTexture.getEstimatedTotalSize()/1_000_000)+"]");
this.renderer.addDebugData(debug); {
this.modelService.addDebugData(debug);
this.renderGen.addDebugData(debug);
this.nodeManager.addDebug(debug);
this.pipeline.addDebug(debug);
}
{ {
TimingStatistics.update(); TimingStatistics.update();
debug.add("Voxy frame runtime (millis): " + TimingStatistics.dynamic.pVal() + ", " + TimingStatistics.main.pVal()+ ", " + TimingStatistics.postDynamic.pVal()+ ", " + TimingStatistics.all.pVal()); debug.add("Voxy frame runtime (millis): " + TimingStatistics.dynamic.pVal() + ", " + TimingStatistics.main.pVal()+ ", " + TimingStatistics.postDynamic.pVal()+ ", " + TimingStatistics.all.pVal());
@@ -267,11 +413,62 @@ public class VoxyRenderSystem {
Logger.info("Flushing download stream"); Logger.info("Flushing download stream");
DownloadStream.INSTANCE.flushWaitClear(); DownloadStream.INSTANCE.flushWaitClear();
Logger.info("Shutting down rendering"); Logger.info("Shutting down rendering");
try {this.renderer.shutdown();this.chunkBoundRenderer.free();} catch (Exception e) {Logger.error("Error shutting down renderer", e);} try {
Logger.info("Shutting down post processor"); //Cleanup callbacks
if (this.postProcessing!=null){try {this.postProcessing.shutdown();} catch (Exception e) {Logger.error("Error shutting down post processor", e);}} this.worldIn.setDirtyCallback(null);
this.worldIn.getMapper().setBiomeCallback(null);
this.worldIn.getMapper().setStateCallback(null);
this.nodeManager.stop();
this.modelService.shutdown();
this.renderGen.shutdown();
this.traversal.free();
this.nodeCleaner.free();
this.geometryData.free();
this.chunkBoundRenderer.free();
this.viewportSelector.free();
} catch (Exception e) {Logger.error("Error shutting down renderer components", e);}
Logger.info("Shutting down render pipeline");
try {this.pipeline.free();} catch (Exception e){Logger.error("Error releasing render pipeline", e);}
Logger.info("Flushing download stream");
DownloadStream.INSTANCE.flushWaitClear();
//Release hold on the world //Release hold on the world
this.worldIn.releaseRef(); this.worldIn.releaseRef();
Logger.info("Render shutdown completed");
}
private static long getGeometryBufferSize() {
long geometryCapacity = Math.min((1L<<(64-Long.numberOfLeadingZeros(Capabilities.INSTANCE.ssboMaxSize-1)))<<1, 1L<<32)-1024/*(1L<<32)-1024*/;
if (Capabilities.INSTANCE.isIntel) {
geometryCapacity = Math.max(geometryCapacity, 1L<<30);//intel moment, force min 1gb
}
//Limit to available dedicated memory if possible
if (Capabilities.INSTANCE.canQueryGpuMemory) {
//512mb less than avalible,
long limit = Capabilities.INSTANCE.getFreeDedicatedGpuMemory() - (long)(1.5*1024*1024*1024);//1.5gb vram buffer
// Give a minimum of 512 mb requirement
limit = Math.max(512*1024*1024, limit);
geometryCapacity = Math.min(geometryCapacity, limit);
}
//geometryCapacity = 1<<28;
//geometryCapacity = 1<<30;//1GB test
var override = System.getProperty("voxy.geometryBufferSizeOverrideMB", "");
if (!override.isEmpty()) {
geometryCapacity = Long.parseLong(override)*1024L*1024L;
}
return geometryCapacity;
}
public WorldEngine getEngine() {
return this.worldIn;
} }
} }

View File

@@ -24,9 +24,13 @@ public class Capabilities {
public final boolean compute; public final boolean compute;
public final boolean indirectParameters; public final boolean indirectParameters;
public final boolean isIntel; public final boolean isIntel;
public final boolean subgroup;
public final boolean sparseBuffer;
public final boolean isNvidia;
public Capabilities() { public Capabilities() {
var cap = GL.getCapabilities(); var cap = GL.getCapabilities();
this.sparseBuffer = cap.GL_ARB_sparse_buffer;
this.compute = cap.glDispatchComputeIndirect != 0; this.compute = cap.glDispatchComputeIndirect != 0;
this.indirectParameters = cap.glMultiDrawElementsIndirectCountARB != 0; this.indirectParameters = cap.glMultiDrawElementsIndirectCountARB != 0;
this.repFragTest = cap.GL_NV_representative_fragment_test; this.repFragTest = cap.GL_NV_representative_fragment_test;
@@ -42,11 +46,25 @@ public class Capabilities {
uint64_t a = 1234; uint64_t a = 1234;
} }
"""); """);
if (cap.GL_KHR_shader_subgroup) {
this.subgroup = testShaderCompilesOk(ShaderType.COMPUTE, """
#version 430
#extension GL_KHR_shader_subgroup_basic : require
#extension GL_KHR_shader_subgroup_arithmetic : require
layout(local_size_x=32) in;
void main() {
uint a = subgroupExclusiveAdd(gl_LocalInvocationIndex);
}
""");
} else {
this.subgroup = false;
}
this.ssboMaxSize = glGetInteger64(GL_MAX_SHADER_STORAGE_BLOCK_SIZE); this.ssboMaxSize = glGetInteger64(GL_MAX_SHADER_STORAGE_BLOCK_SIZE);
this.isMesa = glGetString(GL_VERSION).toLowerCase().contains("mesa"); this.isMesa = glGetString(GL_VERSION).toLowerCase().contains("mesa");
this.isIntel = glGetString(GL_VENDOR).toLowerCase().contains("intel"); this.isIntel = glGetString(GL_VENDOR).toLowerCase().contains("intel");
this.isNvidia = glGetString(GL_VENDOR).toLowerCase().contains("nvidia");
if (this.canQueryGpuMemory) { if (this.canQueryGpuMemory) {
this.totalDedicatedMemory = glGetInteger64(GL_GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX)*1024;//Since its in Kb this.totalDedicatedMemory = glGetInteger64(GL_GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX)*1024;//Since its in Kb

View File

@@ -0,0 +1,25 @@
package me.cortex.voxy.client.core.gl;
import org.lwjgl.opengl.GL;
import org.lwjgl.system.JNI;
public class EXTMeshShader {
public static final int
GL_MESH_SHADER_EXT = 0x9559,
GL_TASK_SHADER_EXT = 0x955A;
private static final long glDrawMeshTasksIndirectEXT_ptr;
static {
if (GL.getFunctionProvider() == null) {
throw new IllegalStateException("Class must be initalized after gl context has been created");
}
glDrawMeshTasksIndirectEXT_ptr = GL.getFunctionProvider().getFunctionAddress("glDrawMeshTasksIndirectEXT");
}
public static void glDrawMeshTasksIndirectEXT(long indirect) {
if (glDrawMeshTasksIndirectEXT_ptr == 0) {
throw new IllegalStateException("glDrawMeshTasksIndirectEXT not supported");
}
JNI.callPV(indirect, glDrawMeshTasksIndirectEXT_ptr);
}
}

View File

@@ -2,10 +2,11 @@ package me.cortex.voxy.client.core.gl;
import me.cortex.voxy.common.util.TrackedObject; import me.cortex.voxy.common.util.TrackedObject;
import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL11;
import org.lwjgl.system.MemoryUtil;
import static org.lwjgl.opengl.ARBSparseBuffer.GL_SPARSE_STORAGE_BIT_ARB;
import static org.lwjgl.opengl.GL11.GL_UNSIGNED_BYTE; import static org.lwjgl.opengl.GL11.GL_UNSIGNED_BYTE;
import static org.lwjgl.opengl.GL15.glDeleteBuffers; import static org.lwjgl.opengl.GL15.glDeleteBuffers;
import static org.lwjgl.opengl.GL44C.glBufferStorage;
import static org.lwjgl.opengl.GL45C.*; import static org.lwjgl.opengl.GL45C.*;
public class GlBuffer extends TrackedObject { public class GlBuffer extends TrackedObject {
@@ -18,12 +19,21 @@ public class GlBuffer extends TrackedObject {
public GlBuffer(long size) { public GlBuffer(long size) {
this(size, 0); this(size, 0);
} }
public GlBuffer(long size, boolean zero) {
this(size, 0, zero);
}
public GlBuffer(long size, int flags) { public GlBuffer(long size, int flags) {
this(size, flags, true);
}
public GlBuffer(long size, int flags, boolean zero) {
this.id = glCreateBuffers(); this.id = glCreateBuffers();
this.size = size; this.size = size;
glNamedBufferStorage(this.id, size, flags); glNamedBufferStorage(this.id, size, flags);
this.zero(); if ((flags&GL_SPARSE_STORAGE_BIT_ARB)==0 && zero) {
this.zero();
}
COUNT++; COUNT++;
TOTAL_SIZE += size; TOTAL_SIZE += size;
@@ -58,7 +68,8 @@ public class GlBuffer extends TrackedObject {
glPixelStorei(GL11.GL_UNPACK_SKIP_ROWS, 0); glPixelStorei(GL11.GL_UNPACK_SKIP_ROWS, 0);
glPixelStorei(GL11.GL_UNPACK_SKIP_PIXELS, 0); glPixelStorei(GL11.GL_UNPACK_SKIP_PIXELS, 0);
glClearNamedBufferData(this.id, GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT, new int[]{data}); MemoryUtil.memPutInt(SCRATCH, data);
nglClearNamedBufferData(this.id, GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT, SCRATCH);
return this; return this;
} }
@@ -73,4 +84,6 @@ public class GlBuffer extends TrackedObject {
public GlBuffer name(String name) { public GlBuffer name(String name) {
return GlDebug.name(name, this); return GlDebug.name(name, this);
} }
private static final long SCRATCH = MemoryUtil.nmemAlloc(4);
} }

View File

@@ -1,6 +1,7 @@
package me.cortex.voxy.client.core.gl; package me.cortex.voxy.client.core.gl;
import me.cortex.voxy.common.util.TrackedObject; import me.cortex.voxy.common.util.TrackedObject;
import org.lwjgl.system.MemoryUtil;
import static org.lwjgl.opengl.GL32.*; import static org.lwjgl.opengl.GL32.*;
@@ -12,13 +13,24 @@ public class GlFence extends TrackedObject {
this.fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); this.fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
} }
private static final long SCRATCH = MemoryUtil.nmemCalloc(1,4);
public boolean signaled() { public boolean signaled() {
if (!this.signaled) { if (!this.signaled) {
/*
int ret = glClientWaitSync(this.fence, 0, 0); int ret = glClientWaitSync(this.fence, 0, 0);
if (ret == GL_ALREADY_SIGNALED || ret == GL_CONDITION_SATISFIED) { if (ret == GL_ALREADY_SIGNALED || ret == GL_CONDITION_SATISFIED) {
this.signaled = true; this.signaled = true;
} else if (ret != GL_TIMEOUT_EXPIRED) { } else if (ret != GL_TIMEOUT_EXPIRED) {
throw new IllegalStateException("Poll for fence failed, glError: " + glGetError()); throw new IllegalStateException("Poll for fence failed, glError: " + glGetError());
}*/
MemoryUtil.memPutInt(SCRATCH, -1);
nglGetSynciv(this.fence, GL_SYNC_STATUS, 1, 0, SCRATCH);
int val = MemoryUtil.memGetInt(SCRATCH);
if (val == GL_SIGNALED) {
this.signaled = true;
} else if (val != GL_UNSIGNALED) {
throw new IllegalStateException("Unknown data from glGetSync: "+val);
} }
} }
return this.signaled; return this.signaled;

View File

@@ -2,7 +2,6 @@ package me.cortex.voxy.client.core.gl;
import me.cortex.voxy.common.util.TrackedObject; import me.cortex.voxy.common.util.TrackedObject;
import static org.lwjgl.opengl.GL11C.*;
import static org.lwjgl.opengl.GL45C.*; import static org.lwjgl.opengl.GL45C.*;
public class GlRenderBuffer extends TrackedObject { public class GlRenderBuffer extends TrackedObject {

View File

@@ -2,8 +2,6 @@ package me.cortex.voxy.client.core.gl;
import me.cortex.voxy.common.util.TrackedObject; import me.cortex.voxy.common.util.TrackedObject;
import static org.lwjgl.opengl.ARBFramebufferObject.glDeleteFramebuffers;
import static org.lwjgl.opengl.ARBFramebufferObject.glGenFramebuffers;
import static org.lwjgl.opengl.GL11.GL_RGBA8; import static org.lwjgl.opengl.GL11.GL_RGBA8;
import static org.lwjgl.opengl.GL11C.*; import static org.lwjgl.opengl.GL11C.*;
import static org.lwjgl.opengl.GL30.GL_DEPTH24_STENCIL8; import static org.lwjgl.opengl.GL30.GL_DEPTH24_STENCIL8;
@@ -101,8 +99,10 @@ public class GlTexture extends TrackedObject {
private long getEstimatedSize() { private long getEstimatedSize() {
this.assertAllocated(); this.assertAllocated();
long elemSize = switch (this.format) { long elemSize = switch (this.format) {
case GL_RGBA8, GL_DEPTH24_STENCIL8 -> 4; case GL_RGBA8, GL_DEPTH24_STENCIL8, GL_R32F -> 4;
case GL_DEPTH_COMPONENT24 -> 4;//TODO: check this is right???? case GL_DEPTH_COMPONENT24 -> 4;//TODO: check this is right????
case GL_DEPTH_COMPONENT32F -> 4;
case GL_DEPTH_COMPONENT32 -> 4;
default -> throw new IllegalStateException("Unknown element size"); default -> throw new IllegalStateException("Unknown element size");
}; };

View File

@@ -4,9 +4,12 @@ import me.cortex.voxy.common.util.TrackedObject;
import java.util.Arrays; import java.util.Arrays;
import static org.lwjgl.opengl.GL30.glGenVertexArrays;
import static org.lwjgl.opengl.GL45C.*; import static org.lwjgl.opengl.GL45C.*;
public class GlVertexArray extends TrackedObject { public class GlVertexArray extends TrackedObject {
public static final int STATIC_VAO = glGenVertexArrays();
public final int id; public final int id;
private int[] indices = new int[0]; private int[] indices = new int[0];
private int stride; private int stride;

View File

@@ -26,6 +26,8 @@ public class AutoBindingShader extends Shader {
private final List<BufferBinding> bindings = new ArrayList<>(); private final List<BufferBinding> bindings = new ArrayList<>();
private final List<TextureBinding> textureBindings = new ArrayList<>(); private final List<TextureBinding> textureBindings = new ArrayList<>();
private boolean rebuild = true;
AutoBindingShader(Shader.Builder<AutoBindingShader> builder, int program) { AutoBindingShader(Shader.Builder<AutoBindingShader> builder, int program) {
super(program); super(program);
this.defines = builder.defines; this.defines = builder.defines;
@@ -70,6 +72,8 @@ public class AutoBindingShader extends Shader {
} }
private void insertOrReplaceBinding(BufferBinding binding) { private void insertOrReplaceBinding(BufferBinding binding) {
this.rebuild = true;
//Check if there is already a binding at the index with the target, if so, replace it //Check if there is already a binding at the index with the target, if so, replace it
for (int i = 0; i < this.bindings.size(); i++) { for (int i = 0; i < this.bindings.size(); i++) {
var entry = this.bindings.get(i); var entry = this.bindings.get(i);
@@ -92,6 +96,16 @@ public class AutoBindingShader extends Shader {
} }
public AutoBindingShader texture(int unit, int sampler, GlTexture texture) { public AutoBindingShader texture(int unit, int sampler, GlTexture texture) {
this.rebuild = true;
for (int i = 0; i < this.textureBindings.size(); i++) {
var entry = this.textureBindings.get(i);
if (entry.unit == unit) {
this.textureBindings.set(i, new TextureBinding(unit, sampler, texture));
return this;
}
}
this.textureBindings.add(new TextureBinding(unit, sampler, texture)); this.textureBindings.add(new TextureBinding(unit, sampler, texture));
return this; return this;
} }
@@ -99,6 +113,13 @@ public class AutoBindingShader extends Shader {
@Override @Override
public void bind() { public void bind() {
super.bind(); super.bind();
//TODO: replace with multibind and use the invalidate flag
/*
glBindSamplers();
glBindTextures();
glBindBuffersBase();
glBindBuffersRange();
*/
if (!this.bindings.isEmpty()) { if (!this.bindings.isEmpty()) {
for (var binding : this.bindings) { for (var binding : this.bindings) {
binding.buffer.assertNotFreed(); binding.buffer.assertNotFreed();

View File

@@ -1,12 +0,0 @@
package me.cortex.voxy.client.core.gl.shader;
import java.util.regex.Pattern;
public class GenericsProcessor implements IShaderProcessor {
private static final Pattern GENERIC_DEFINE = Pattern.compile("#defineGen (?<name>[A-Za-z0-9]+)<(?<generic>[A-Za-z0-9]*)>");
private static final Pattern GENERIC_USE = Pattern.compile("(?<type>[A-Za-z0-9]+)<(?<generic>[A-Za-z0-9]*)>");
@Override
public String process(ShaderType type, String source) {
return null;
}
}

View File

@@ -1,11 +1,13 @@
package me.cortex.voxy.client.core.gl.shader; package me.cortex.voxy.client.core.gl.shader;
import me.cortex.voxy.client.core.gl.Capabilities; import me.cortex.voxy.client.core.gl.Capabilities;
import me.cortex.voxy.client.core.gl.GlBuffer;
import me.cortex.voxy.client.core.gl.GlDebug; import me.cortex.voxy.client.core.gl.GlDebug;
import me.cortex.voxy.common.Logger; import me.cortex.voxy.common.Logger;
import me.cortex.voxy.common.util.ThreadUtils;
import me.cortex.voxy.common.util.TrackedObject; import me.cortex.voxy.common.util.TrackedObject;
import org.lwjgl.opengl.GL20C; import org.lwjgl.opengl.GL20C;
import org.lwjgl.system.MemoryStack;
import org.lwjgl.system.MemoryUtil;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
@@ -75,6 +77,13 @@ public class Shader extends TrackedObject {
this.processor = processor; this.processor = processor;
} }
public Builder<T> clone() {
var clone = new Builder<>(this.constructor, this.processor);
clone.defines.putAll(this.defines);
clone.sources.putAll(this.sources);
return clone;
}
public Builder<T> define(String name) { public Builder<T> define(String name) {
this.defines.put(name, ""); this.defines.put(name, "");
return this; return this;
@@ -149,6 +158,7 @@ public class Shader extends TrackedObject {
public T compile() { public T compile() {
this.defineIf("IS_INTEL", Capabilities.INSTANCE.isIntel); this.defineIf("IS_INTEL", Capabilities.INSTANCE.isIntel);
this.defineIf("IS_WINDOWS", ThreadUtils.isWindows);
return this.constructor.make(this, this.compileToProgram()); return this.constructor.make(this, this.compileToProgram());
} }
@@ -170,7 +180,13 @@ public class Shader extends TrackedObject {
private static int createShader(ShaderType type, String src) { private static int createShader(ShaderType type, String src) {
int shader = GL20C.glCreateShader(type.gl); int shader = GL20C.glCreateShader(type.gl);
GL20C.glShaderSource(shader, src); {//https://github.com/CaffeineMC/sodium/blob/fc42a7b19836c98a35df46e63303608de0587ab6/src/main/java/me/jellysquid/mods/sodium/client/gl/shader/ShaderWorkarounds.java
long ptr = MemoryUtil.memAddress(MemoryUtil.memUTF8(src, true));
try (var stack = MemoryStack.stackPush()) {
GL20C.nglShaderSource(shader, 1, stack.pointers(ptr).address0(), 0);
}
MemoryUtil.nmemFree(ptr);
}
GL20C.glCompileShader(shader); GL20C.glCompileShader(shader);
String log = GL20C.glGetShaderInfoLog(shader); String log = GL20C.glGetShaderInfoLog(shader);

View File

@@ -1,33 +1,22 @@
package me.cortex.voxy.client.core.model; package me.cortex.voxy.client.core.model;
import it.unimi.dsi.fastutil.ints.IntLinkedOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet; import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import me.cortex.voxy.client.TimingStatistics; import me.cortex.voxy.common.Logger;
import me.cortex.voxy.client.core.gl.GlFramebuffer;
import me.cortex.voxy.client.core.rendering.building.BuiltSection;
import me.cortex.voxy.client.core.rendering.util.RawDownloadStream;
import me.cortex.voxy.common.world.other.Mapper; import me.cortex.voxy.common.world.other.Mapper;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
import net.minecraft.registry.RegistryKeys; import net.minecraft.registry.RegistryKeys;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
import org.lwjgl.opengl.GL11;
import java.lang.invoke.VarHandle;
import java.util.List; import java.util.List;
import java.util.concurrent.ConcurrentLinkedDeque; import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.StampedLock; import java.util.concurrent.locks.ReentrantLock;
import static org.lwjgl.opengl.ARBFramebufferObject.GL_COLOR_ATTACHMENT0;
import static org.lwjgl.opengl.GL11.GL_COLOR_BUFFER_BIT;
import static org.lwjgl.opengl.GL11.glGetInteger; import static org.lwjgl.opengl.GL11.glGetInteger;
import static org.lwjgl.opengl.GL11C.GL_NEAREST;
import static org.lwjgl.opengl.GL30.GL_FRAMEBUFFER; import static org.lwjgl.opengl.GL30.GL_FRAMEBUFFER;
import static org.lwjgl.opengl.GL30.GL_FRAMEBUFFER_BINDING; import static org.lwjgl.opengl.GL30.GL_FRAMEBUFFER_BINDING;
import static org.lwjgl.opengl.GL30C.GL_DRAW_FRAMEBUFFER_BINDING;
import static org.lwjgl.opengl.GL30C.glBindFramebuffer; import static org.lwjgl.opengl.GL30C.glBindFramebuffer;
import static org.lwjgl.opengl.GL45.glBlitNamedFramebuffer;
public class ModelBakerySubsystem { public class ModelBakerySubsystem {
//Redo to just make it request the block faces with the async texture download stream which //Redo to just make it request the block faces with the async texture download stream which
@@ -35,11 +24,13 @@ public class ModelBakerySubsystem {
private final ModelStore storage = new ModelStore(); private final ModelStore storage = new ModelStore();
public final ModelFactory factory; public final ModelFactory factory;
private final Mapper mapper;
private final AtomicInteger blockIdCount = new AtomicInteger(); private final AtomicInteger blockIdCount = new AtomicInteger();
private final ConcurrentLinkedDeque<Integer> blockIdQueue = new ConcurrentLinkedDeque<>();//TODO: replace with custom DS private final ConcurrentLinkedDeque<Integer> blockIdQueue = new ConcurrentLinkedDeque<>();//TODO: replace with custom DS
private final ConcurrentLinkedDeque<Mapper.BiomeEntry> biomeQueue = new ConcurrentLinkedDeque<>(); private final ConcurrentLinkedDeque<Mapper.BiomeEntry> biomeQueue = new ConcurrentLinkedDeque<>();
public ModelBakerySubsystem(Mapper mapper) { public ModelBakerySubsystem(Mapper mapper) {
this.mapper = mapper;
this.factory = new ModelFactory(mapper, this.storage); this.factory = new ModelFactory(mapper, this.storage);
} }
@@ -113,15 +104,19 @@ public class ModelBakerySubsystem {
} }
//This is on this side only and done like this as only worker threads call this code //This is on this side only and done like this as only worker threads call this code
private final StampedLock seenIdsLock = new StampedLock(); private final ReentrantLock seenIdsLock = new ReentrantLock();
private final IntOpenHashSet seenIds = new IntOpenHashSet(6000); private final IntOpenHashSet seenIds = new IntOpenHashSet(6000);//TODO: move to a lock free concurrent hashmap
public void requestBlockBake(int blockId) { public void requestBlockBake(int blockId) {
long stamp = this.seenIdsLock.writeLock(); if (this.mapper.getBlockStateCount() < blockId) {
if (!this.seenIds.add(blockId)) { Logger.error("Error, got bakeing request for out of range state id. StateId: " + blockId + " max id: " + this.mapper.getBlockStateCount(), new Exception());
this.seenIdsLock.unlockWrite(stamp);
return; return;
} }
this.seenIdsLock.unlockWrite(stamp); this.seenIdsLock.lock();
if (!this.seenIds.add(blockId)) {
this.seenIdsLock.unlock();
return;
}
this.seenIdsLock.unlock();
this.blockIdQueue.add(blockId); this.blockIdQueue.add(blockId);
this.blockIdCount.incrementAndGet(); this.blockIdCount.incrementAndGet();
} }

View File

@@ -1,6 +1,7 @@
package me.cortex.voxy.client.core.model; package me.cortex.voxy.client.core.model;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet; import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
import it.unimi.dsi.fastutil.objects.ObjectSet; import it.unimi.dsi.fastutil.objects.ObjectSet;
@@ -18,7 +19,7 @@ import net.minecraft.block.LeavesBlock;
import net.minecraft.block.entity.BlockEntity; import net.minecraft.block.entity.BlockEntity;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
import net.minecraft.client.color.block.BlockColorProvider; import net.minecraft.client.color.block.BlockColorProvider;
import net.minecraft.client.render.RenderLayer; import net.minecraft.client.render.BlockRenderLayer;
import net.minecraft.client.render.RenderLayers; import net.minecraft.client.render.RenderLayers;
import net.minecraft.fluid.FluidState; import net.minecraft.fluid.FluidState;
import net.minecraft.registry.Registries; import net.minecraft.registry.Registries;
@@ -40,9 +41,6 @@ import java.util.*;
import static me.cortex.voxy.client.core.model.ModelStore.MODEL_SIZE; import static me.cortex.voxy.client.core.model.ModelStore.MODEL_SIZE;
import static org.lwjgl.opengl.ARBDirectStateAccess.nglTextureSubImage2D; import static org.lwjgl.opengl.ARBDirectStateAccess.nglTextureSubImage2D;
import static org.lwjgl.opengl.GL11.*; import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.opengl.GL33.glDeleteSamplers;
import static org.lwjgl.opengl.GL33.glGenSamplers;
import static org.lwjgl.opengl.GL45C.glTextureSubImage2D;
//Manages the storage and updating of model states, textures and colours //Manages the storage and updating of model states, textures and colours
@@ -121,6 +119,7 @@ public class ModelFactory {
public final Deque<Runnable> resultJobs = new ArrayDeque<>(); public final Deque<Runnable> resultJobs = new ArrayDeque<>();
private Object2IntMap<BlockState> customBlockStateIdMapping;
//TODO: NOTE!!! is it worth even uploading as a 16x16 texture, since automatic lod selection... doing 8x8 textures might be perfectly ok!!! //TODO: NOTE!!! is it worth even uploading as a 16x16 texture, since automatic lod selection... doing 8x8 textures might be perfectly ok!!!
// this _quarters_ the memory requirements for the texture atlas!!! WHICH IS HUGE saving // this _quarters_ the memory requirements for the texture atlas!!! WHICH IS HUGE saving
@@ -144,6 +143,10 @@ public class ModelFactory {
this.downstream.tick(); this.downstream.tick();
} }
public void setCustomBlockStateMapping(Object2IntMap<BlockState> mapping) {
this.customBlockStateIdMapping = mapping;
}
public boolean addEntry(int blockId) { public boolean addEntry(int blockId) {
if (this.idMappings[blockId] != -1) { if (this.idMappings[blockId] != -1) {
return false; return false;
@@ -256,26 +259,20 @@ public class ModelFactory {
this.fluidStateLUT[modelId] = clientFluidStateId; this.fluidStateLUT[modelId] = clientFluidStateId;
} }
RenderLayer blockRenderLayer = null; BlockRenderLayer blockRenderLayer = null;
if (blockState.getBlock() instanceof FluidBlock) { if (blockState.getBlock() instanceof FluidBlock) {
blockRenderLayer = RenderLayers.getFluidLayer(blockState.getFluidState()); blockRenderLayer = RenderLayers.getFluidLayer(blockState.getFluidState());
} else { } else {
if (blockState.getBlock() instanceof LeavesBlock) { if (blockState.getBlock() instanceof LeavesBlock) {
blockRenderLayer = RenderLayer.getSolid(); blockRenderLayer = BlockRenderLayer.SOLID;
} else { } else {
blockRenderLayer = RenderLayers.getBlockLayer(blockState); blockRenderLayer = RenderLayers.getBlockLayer(blockState);
} }
} }
int checkMode = blockRenderLayer==RenderLayer.getSolid()?TextureUtils.WRITE_CHECK_STENCIL:TextureUtils.WRITE_CHECK_ALPHA; int checkMode = blockRenderLayer==BlockRenderLayer.SOLID?TextureUtils.WRITE_CHECK_STENCIL:TextureUtils.WRITE_CHECK_ALPHA;
if (Capabilities.INSTANCE.isMesa) {
//Mesa does not work with GL_DEPTH_STENCIL_TEXTURE_MODE GL_STENCIL_INDEX
// the sampler in the compute shader always reads zero even when stencil is guarenteed not to be zero
// (e.g. clearing with stencil 10)
checkMode = TextureUtils.WRITE_CHECK_ALPHA;
}
var colourProvider = getColourProvider(blockState.getBlock()); var colourProvider = getColourProvider(blockState.getBlock());
@@ -339,7 +336,7 @@ public class ModelFactory {
//Each face gets 1 byte, with the top 2 bytes being for whatever //Each face gets 1 byte, with the top 2 bytes being for whatever
long metadata = 0; long metadata = 0;
metadata |= isBiomeColourDependent?1:0; metadata |= isBiomeColourDependent?1:0;
metadata |= blockRenderLayer == RenderLayer.getTranslucent()?2:0; metadata |= blockRenderLayer == BlockRenderLayer.TRANSLUCENT?2:0;
metadata |= needsDoubleSidedQuads?4:0; metadata |= needsDoubleSidedQuads?4:0;
metadata |= ((!isFluid) && !blockState.getFluidState().isEmpty())?8:0;//Has a fluid state accosiacted with it and is not itself a fluid metadata |= ((!isFluid) && !blockState.getFluidState().isEmpty())?8:0;//Has a fluid state accosiacted with it and is not itself a fluid
metadata |= isFluid?16:0;//Is a fluid metadata |= isFluid?16:0;//Is a fluid
@@ -373,7 +370,7 @@ public class ModelFactory {
//TODO: add alot of config options for the following //TODO: add alot of config options for the following
boolean occludesFace = true; boolean occludesFace = true;
occludesFace &= blockRenderLayer != RenderLayer.getTranslucent();//If its translucent, it doesnt occlude occludesFace &= blockRenderLayer != BlockRenderLayer.TRANSLUCENT;//If its translucent, it doesnt occlude
//TODO: make this an option, basicly if the face is really close, it occludes otherwise it doesnt //TODO: make this an option, basicly if the face is really close, it occludes otherwise it doesnt
occludesFace &= offset < 0.1;//If the face is rendered far away from the other face, then it doesnt occlude occludesFace &= offset < 0.1;//If the face is rendered far away from the other face, then it doesnt occlude
@@ -393,7 +390,7 @@ public class ModelFactory {
metadata |= canBeOccluded?4:0; metadata |= canBeOccluded?4:0;
//Face uses its own lighting if its not flat against the adjacent block & isnt traslucent //Face uses its own lighting if its not flat against the adjacent block & isnt traslucent
metadata |= (offset > 0.01 || blockRenderLayer == RenderLayer.getTranslucent())?0b1000:0; metadata |= (offset > 0.01 || blockRenderLayer == BlockRenderLayer.TRANSLUCENT)?0b1000:0;
@@ -411,11 +408,11 @@ public class ModelFactory {
int area = (faceSize[1]-faceSize[0]+1) * (faceSize[3]-faceSize[2]+1); int area = (faceSize[1]-faceSize[0]+1) * (faceSize[3]-faceSize[2]+1);
boolean needsAlphaDiscard = ((float)writeCount)/area<0.9;//If the amount of area covered by written pixels is less than a threashold, disable discard as its not needed boolean needsAlphaDiscard = ((float)writeCount)/area<0.9;//If the amount of area covered by written pixels is less than a threashold, disable discard as its not needed
needsAlphaDiscard |= blockRenderLayer != RenderLayer.getSolid(); needsAlphaDiscard |= blockRenderLayer != BlockRenderLayer.SOLID;
needsAlphaDiscard &= blockRenderLayer != RenderLayer.getTranslucent();//Translucent doesnt have alpha discard needsAlphaDiscard &= blockRenderLayer != BlockRenderLayer.TRANSLUCENT;//Translucent doesnt have alpha discard
faceModelData |= needsAlphaDiscard?1<<22:0; faceModelData |= needsAlphaDiscard?1<<22:0;
faceModelData |= ((!faceCoversFullBlock)&&blockRenderLayer != RenderLayer.getTranslucent())?1<<23:0;//Alpha discard override, translucency doesnt have alpha discard faceModelData |= ((!faceCoversFullBlock)&&blockRenderLayer != BlockRenderLayer.TRANSLUCENT)?1<<23:0;//Alpha discard override, translucency doesnt have alpha discard
@@ -435,21 +432,22 @@ public class ModelFactory {
int modelFlags = 0; int modelFlags = 0;
modelFlags |= colourProvider != null?1:0; modelFlags |= colourProvider != null?1:0;
modelFlags |= isBiomeColourDependent?2:0;//Basicly whether to use the next int as a colour or as a base index/id into a colour buffer for biome dependent colours modelFlags |= isBiomeColourDependent?2:0;//Basicly whether to use the next int as a colour or as a base index/id into a colour buffer for biome dependent colours
modelFlags |= blockRenderLayer == RenderLayer.getTranslucent()?4:0;//Is translucent modelFlags |= blockRenderLayer == BlockRenderLayer.TRANSLUCENT?4:0;//Is translucent
modelFlags |= blockRenderLayer == RenderLayer.getCutout()?0:8;//Dont use mipmaps (AND ALSO FKING SPECIFIES IF IT HAS AO, WHY??? GREAT QUESTION, TODO FIXE THIS) modelFlags |= blockRenderLayer == BlockRenderLayer.CUTOUT?0:8;//Dont use mipmaps (AND ALSO FKING SPECIFIES IF IT HAS AO, WHY??? GREAT QUESTION, TODO FIXE THIS)
//modelFlags |= blockRenderLayer == RenderLayer.getSolid()?0:1;// should discard alpha //modelFlags |= blockRenderLayer == RenderLayer.getSolid()?0:1;// should discard alpha
MemoryUtil.memPutInt(uploadPtr, modelFlags); MemoryUtil.memPutInt(uploadPtr, modelFlags); uploadPtr += 4;
//Temporary override to always be non biome specific //Temporary override to always be non biome specific
if (colourProvider == null) { if (colourProvider == null) {
MemoryUtil.memPutInt(uploadPtr + 4, -1);//Set the default to nothing so that its faster on the gpu MemoryUtil.memPutInt(uploadPtr, -1);//Set the default to nothing so that its faster on the gpu
} else if (!isBiomeColourDependent) { } else if (!isBiomeColourDependent) {
MemoryUtil.memPutInt(uploadPtr + 4, captureColourConstant(colourProvider, blockState, DEFAULT_BIOME)|0xFF000000); MemoryUtil.memPutInt(uploadPtr, captureColourConstant(colourProvider, blockState, DEFAULT_BIOME)|0xFF000000);
} else if (!this.biomes.isEmpty()) { } else if (!this.biomes.isEmpty()) {
//Populate the list of biomes for the model state //Populate the list of biomes for the model state
int biomeIndex = this.modelsRequiringBiomeColours.size() * this.biomes.size(); int biomeIndex = this.modelsRequiringBiomeColours.size() * this.biomes.size();
MemoryUtil.memPutInt(uploadPtr + 4, biomeIndex); MemoryUtil.memPutInt(uploadPtr, biomeIndex);
this.modelsRequiringBiomeColours.add(new Pair<>(modelId, blockState)); this.modelsRequiringBiomeColours.add(new Pair<>(modelId, blockState));
//NOTE: UploadStream.INSTANCE is called _after_ uploadPtr is finished being used, this is cause the upload pointer //NOTE: UploadStream.INSTANCE is called _after_ uploadPtr is finished being used, this is cause the upload pointer
// may be invalidated as soon as another upload stream is invoked // may be invalidated as soon as another upload stream is invoked
@@ -458,11 +456,22 @@ public class ModelFactory {
MemoryUtil.memPutInt(clrUploadPtr, captureColourConstant(colourProvider, blockState, biome)|0xFF000000); clrUploadPtr += 4; MemoryUtil.memPutInt(clrUploadPtr, captureColourConstant(colourProvider, blockState, biome)|0xFF000000); clrUploadPtr += 4;
} }
} }
uploadPtr += 4;
//have 32 bytes of free space after here
//install the custom mapping id if it exists
if (this.customBlockStateIdMapping != null && this.customBlockStateIdMapping.containsKey(blockState)) {
MemoryUtil.memPutInt(uploadPtr, this.customBlockStateIdMapping.getInt(blockState));
} else {
MemoryUtil.memPutInt(uploadPtr, 0);
} uploadPtr += 4;
//Note: if the layer isSolid then need to fill all the points in the texture where alpha == 0 with the average colour //Note: if the layer isSolid then need to fill all the points in the texture where alpha == 0 with the average colour
// of the surrounding blocks but only within the computed face size bounds // of the surrounding blocks but only within the computed face size bounds
//TODO
//TODO callback to inject extra data into the model data
this.putTextures(modelId, textureData); this.putTextures(modelId, textureData);
@@ -496,7 +505,7 @@ public class ModelFactory {
int i = 0; int i = 0;
for (var entry : this.modelsRequiringBiomeColours) { for (var entry : this.modelsRequiringBiomeColours) {
var colourProvider = MinecraftClient.getInstance().getBlockColors().providers.get(Registries.BLOCK.getRawId(entry.getRight().getBlock())); var colourProvider = getColourProvider(entry.getRight().getBlock());
if (colourProvider == null) { if (colourProvider == null) {
throw new IllegalStateException(); throw new IllegalStateException();
} }
@@ -679,7 +688,7 @@ public class ModelFactory {
private static final int LAYERS = Integer.numberOfTrailingZeros(MODEL_TEXTURE_SIZE); private static final int LAYERS = Integer.numberOfTrailingZeros(MODEL_TEXTURE_SIZE);
//TODO: redo to batch blit, instead of 6 seperate blits, and also fix mipping //TODO: redo to batch blit, instead of 6 seperate blits, and also fix mipping
private void putTextures(int id, ColourDepthTextureData[] textures) { private void putTextures(int id, ColourDepthTextureData[] textures) {
if (MODEL_TEXTURE_SIZE != 16) {throw new IllegalStateException("THIS METHOD MUST BE REDONE IF THIS CONST CHANGES");} //if (MODEL_TEXTURE_SIZE != 16) {throw new IllegalStateException("THIS METHOD MUST BE REDONE IF THIS CONST CHANGES");}
//TODO: need to use a write mask to see what pixels must be used to contribute to mipping //TODO: need to use a write mask to see what pixels must be used to contribute to mipping
// as in, using the depth/stencil info, check if pixel was written to, if so, use that pixel when blending, else dont // as in, using the depth/stencil info, check if pixel was written to, if so, use that pixel when blending, else dont

View File

@@ -2,6 +2,9 @@ package me.cortex.voxy.client.core.model;
import me.cortex.voxy.client.core.gl.GlBuffer; import me.cortex.voxy.client.core.gl.GlBuffer;
import me.cortex.voxy.client.core.gl.GlTexture; import me.cortex.voxy.client.core.gl.GlTexture;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.texture.SpriteAtlasTexture;
import net.minecraft.util.Identifier;
import static org.lwjgl.opengl.GL11.*; import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.opengl.GL11C.GL_NEAREST; import static org.lwjgl.opengl.GL11C.GL_NEAREST;
@@ -24,14 +27,18 @@ public class ModelStore {
public ModelStore() { public ModelStore() {
this.modelBuffer = new GlBuffer(MODEL_SIZE * (1<<16)).name("ModelData"); this.modelBuffer = new GlBuffer(MODEL_SIZE * (1<<16)).name("ModelData");
this.modelColourBuffer = new GlBuffer(4 * (1<<16)).name("ModelColour"); this.modelColourBuffer = new GlBuffer(4 * (1<<16)).name("ModelColour");
this.textures = new GlTexture().store(GL_RGBA8, 4, ModelFactory.MODEL_TEXTURE_SIZE*3*256,ModelFactory.MODEL_TEXTURE_SIZE*2*256).name("ModelTextures"); this.textures = new GlTexture().store(GL_RGBA8, Integer.numberOfTrailingZeros(ModelFactory.MODEL_TEXTURE_SIZE), ModelFactory.MODEL_TEXTURE_SIZE*3*256,ModelFactory.MODEL_TEXTURE_SIZE*2*256).name("ModelTextures");
//Limit the mips of the texture to match that of the terrain atlas
int mipLvl = ((SpriteAtlasTexture) MinecraftClient.getInstance().getTextureManager()
.getTexture(Identifier.of("minecraft", "textures/atlas/blocks.png")))
.mipLevel;
glSamplerParameteri(this.blockSampler, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR); glSamplerParameteri(this.blockSampler, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR);
glSamplerParameteri(this.blockSampler, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glSamplerParameteri(this.blockSampler, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glSamplerParameteri(this.blockSampler, GL_TEXTURE_MIN_LOD, 0); glSamplerParameteri(this.blockSampler, GL_TEXTURE_MIN_LOD, 0);
glSamplerParameteri(this.blockSampler, GL_TEXTURE_MAX_LOD, 4); glSamplerParameteri(this.blockSampler, GL_TEXTURE_MAX_LOD, mipLvl);//Integer.numberOfTrailingZeros(ModelFactory.MODEL_TEXTURE_SIZE)
} }

View File

@@ -1,7 +1,7 @@
package me.cortex.voxy.client.core.model; package me.cortex.voxy.client.core.model;
import net.caffeinemc.mods.sodium.client.util.color.ColorSRGB; import net.caffeinemc.mods.sodium.client.util.color.ColorSRGB;
import net.minecraft.util.math.ColorHelper; import net.minecraft.client.texture.MipmapHelper;
//Texturing utils to manipulate data from the model bakery //Texturing utils to manipulate data from the model bakery
public class TextureUtils { public class TextureUtils {
@@ -178,7 +178,11 @@ public class TextureUtils {
public static int mipColours(int one, int two, int three, int four) { public static int mipColours(int one, int two, int three, int four) {
return weightedAverageColor(weightedAverageColor(one, two), weightedAverageColor(three, four)); if (true) {
return MipmapHelper.blend(one, two, three, four, false);
} else {
return weightedAverageColor(weightedAverageColor(one, two), weightedAverageColor(three, four));
}
} }
//TODO: FIXME!!! ITS READING IT AS ABGR??? isnt the format RGBA?? //TODO: FIXME!!! ITS READING IT AS ABGR??? isnt the format RGBA??

View File

@@ -1,12 +1,10 @@
package me.cortex.voxy.client.core.model.bakery; package me.cortex.voxy.client.core.model.bakery;
import com.mojang.blaze3d.textures.GpuTexture;
import me.cortex.voxy.common.Logger; import me.cortex.voxy.common.Logger;
import net.minecraft.block.BlockEntityProvider; import net.minecraft.block.BlockEntityProvider;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gl.GlGpuBuffer; import net.minecraft.client.render.RenderLayer;
import net.minecraft.client.render.*;
import net.minecraft.client.util.math.MatrixStack; import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
@@ -46,6 +44,21 @@ public class BakedBlockEntityModel {
this.layers.forEach(layer->layer.consumer.free()); this.layers.forEach(layer->layer.consumer.free());
} }
private static int getMetaFromLayer(RenderLayer layer) {
boolean hasDiscard = layer == RenderLayer.getCutout() ||
layer == RenderLayer.getCutoutMipped() ||
layer == RenderLayer.getTripwire();
boolean isMipped = layer == RenderLayer.getCutoutMipped() ||
layer == RenderLayer.getSolid() ||
layer.isTranslucent() ||
layer == RenderLayer.getTripwire();
int meta = hasDiscard?1:0;
meta |= isMipped?2:0;
return meta;
}
public static BakedBlockEntityModel bake(BlockState state) { public static BakedBlockEntityModel bake(BlockState state) {
Map<RenderLayer, LayerConsumer> map = new HashMap<>(); Map<RenderLayer, LayerConsumer> map = new HashMap<>();
var entity = ((BlockEntityProvider)state.getBlock()).createBlockEntity(BlockPos.ORIGIN, state); var entity = ((BlockEntityProvider)state.getBlock()).createBlockEntity(BlockPos.ORIGIN, state);
@@ -56,7 +69,7 @@ public class BakedBlockEntityModel {
entity.setWorld(MinecraftClient.getInstance().world); entity.setWorld(MinecraftClient.getInstance().world);
if (renderer != null) { if (renderer != null) {
try { try {
renderer.render(entity, 0.0f, new MatrixStack(), layer->map.computeIfAbsent(layer, rl -> new LayerConsumer(rl, new ReuseVertexConsumer().setDefaultMeta(ModelTextureBakery.getMetaFromLayer(rl)))).consumer, 0, 0, new Vec3d(0,0,0)); renderer.render(entity, 0.0f, new MatrixStack(), layer->map.computeIfAbsent(layer, rl -> new LayerConsumer(rl, new ReuseVertexConsumer().setDefaultMeta(getMetaFromLayer(rl)))).consumer, 0, 0, new Vec3d(0,0,0));
} catch (Exception e) { } catch (Exception e) {
Logger.error("Unable to bake block entity: " + entity, e); Logger.error("Unable to bake block entity: " + entity, e);
} }

View File

@@ -26,6 +26,7 @@ public class BudgetBufferRenderer {
.compile(); .compile();
public static void init(){}
private static final GlBuffer indexBuffer; private static final GlBuffer indexBuffer;
static { static {
var i = RenderSystem.getSequentialBuffer(VertexFormat.DrawMode.QUADS); var i = RenderSystem.getSequentialBuffer(VertexFormat.DrawMode.QUADS);

View File

@@ -1,20 +1,20 @@
package me.cortex.voxy.client.core.model.bakery; package me.cortex.voxy.client.core.model.bakery;
import me.cortex.voxy.client.core.gl.GlBuffer;
import me.cortex.voxy.client.core.gl.GlFramebuffer; import me.cortex.voxy.client.core.gl.GlFramebuffer;
import me.cortex.voxy.client.core.gl.GlTexture; import me.cortex.voxy.client.core.gl.GlTexture;
import me.cortex.voxy.client.core.gl.shader.Shader; import me.cortex.voxy.client.core.gl.shader.Shader;
import me.cortex.voxy.client.core.gl.shader.ShaderType; import me.cortex.voxy.client.core.gl.shader.ShaderType;
import org.lwjgl.system.MemoryStack;
import org.lwjgl.system.MemoryUtil;
import static org.lwjgl.opengl.ARBDirectStateAccess.glClearNamedFramebufferfv;
import static org.lwjgl.opengl.ARBDirectStateAccess.glTextureParameteri; import static org.lwjgl.opengl.ARBDirectStateAccess.glTextureParameteri;
import static org.lwjgl.opengl.ARBDirectStateAccess.nglClearNamedFramebufferfv;
import static org.lwjgl.opengl.ARBShaderImageLoadStore.GL_FRAMEBUFFER_BARRIER_BIT; import static org.lwjgl.opengl.ARBShaderImageLoadStore.GL_FRAMEBUFFER_BARRIER_BIT;
import static org.lwjgl.opengl.ARBShaderImageLoadStore.GL_PIXEL_BUFFER_BARRIER_BIT; import static org.lwjgl.opengl.ARBShaderImageLoadStore.GL_PIXEL_BUFFER_BARRIER_BIT;
import static org.lwjgl.opengl.ARBShaderImageLoadStore.GL_SHADER_IMAGE_ACCESS_BARRIER_BIT; import static org.lwjgl.opengl.ARBShaderImageLoadStore.GL_SHADER_IMAGE_ACCESS_BARRIER_BIT;
import static org.lwjgl.opengl.ARBShaderImageLoadStore.GL_TEXTURE_UPDATE_BARRIER_BIT; import static org.lwjgl.opengl.ARBShaderImageLoadStore.GL_TEXTURE_UPDATE_BARRIER_BIT;
import static org.lwjgl.opengl.ARBShaderImageLoadStore.glMemoryBarrier; import static org.lwjgl.opengl.ARBShaderImageLoadStore.glMemoryBarrier;
import static org.lwjgl.opengl.GL11.*; import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.opengl.GL11.GL_STENCIL_INDEX;
import static org.lwjgl.opengl.GL30.*; import static org.lwjgl.opengl.GL30.*;
import static org.lwjgl.opengl.GL43.*; import static org.lwjgl.opengl.GL43.*;
import static org.lwjgl.opengl.GL45.glClearNamedFramebufferfi; import static org.lwjgl.opengl.GL45.glClearNamedFramebufferfi;
@@ -42,6 +42,8 @@ public class GlViewCapture {
this.framebuffer = new GlFramebuffer().bind(GL_COLOR_ATTACHMENT0, this.colourTex).bind(GL_DEPTH_STENCIL_ATTACHMENT, this.depthTex).verify().name("ModelFramebuffer"); this.framebuffer = new GlFramebuffer().bind(GL_COLOR_ATTACHMENT0, this.colourTex).bind(GL_DEPTH_STENCIL_ATTACHMENT, this.depthTex).verify().name("ModelFramebuffer");
glTextureParameteri(this.stencilTex.id, GL_DEPTH_STENCIL_TEXTURE_MODE, GL_STENCIL_INDEX); glTextureParameteri(this.stencilTex.id, GL_DEPTH_STENCIL_TEXTURE_MODE, GL_STENCIL_INDEX);
glTextureParameteri(this.stencilTex.id, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTextureParameteri(this.stencilTex.id, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
this.copyOutShader = Shader.makeAuto() this.copyOutShader = Shader.makeAuto()
.define("WIDTH", width) .define("WIDTH", width)
@@ -63,10 +65,16 @@ public class GlViewCapture {
glBindBufferRange(GL_SHADER_STORAGE_BUFFER, 3, buffer, offset, (this.width*3L)*(this.height*2L)*4L*2);//its 2*4 because colour + depth stencil glBindBufferRange(GL_SHADER_STORAGE_BUFFER, 3, buffer, offset, (this.width*3L)*(this.height*2L)*4L*2);//its 2*4 because colour + depth stencil
glMemoryBarrier(GL_FRAMEBUFFER_BARRIER_BIT|GL_TEXTURE_UPDATE_BARRIER_BIT|GL_PIXEL_BUFFER_BARRIER_BIT|GL_SHADER_IMAGE_ACCESS_BARRIER_BIT);//Am not sure if barriers are right glMemoryBarrier(GL_FRAMEBUFFER_BARRIER_BIT|GL_TEXTURE_UPDATE_BARRIER_BIT|GL_PIXEL_BUFFER_BARRIER_BIT|GL_SHADER_IMAGE_ACCESS_BARRIER_BIT);//Am not sure if barriers are right
glDispatchCompute(3, 2, 1); glDispatchCompute(3, 2, 1);
glBindBufferRange(GL_SHADER_STORAGE_BUFFER, 3, 0, 0, 4);//WHY DOES THIS FIX FUCKING BINDING ISSUES HERE WHEN DOING THIS IN THE RENDER SYSTEM DOESNT
} }
public void clear() { public void clear() {
glClearNamedFramebufferfv(this.framebuffer.id, GL_COLOR, 0, new float[]{0,0,0,0}); try (var stack = MemoryStack.stackPush()) {
long ptr = stack.nmalloc(4*4);
MemoryUtil.memPutLong(ptr, 0);
MemoryUtil.memPutLong(ptr+8, 0);
nglClearNamedFramebufferfv(this.framebuffer.id, GL_COLOR, 0, ptr);
}
glClearNamedFramebufferfi(this.framebuffer.id, GL_DEPTH_STENCIL, 0, 1.0f, 0); glClearNamedFramebufferfi(this.framebuffer.id, GL_DEPTH_STENCIL, 0, 1.0f, 0);
} }

View File

@@ -1,19 +1,15 @@
package me.cortex.voxy.client.core.model.bakery; package me.cortex.voxy.client.core.model.bakery;
import net.minecraft.block.BlockState; import net.minecraft.block.*;
import net.minecraft.block.Blocks;
import net.minecraft.block.FluidBlock;
import net.minecraft.block.LeavesBlock;
import net.minecraft.block.entity.BlockEntity; import net.minecraft.block.entity.BlockEntity;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
import net.minecraft.client.render.RenderLayer; import net.minecraft.client.render.BlockRenderLayer;
import net.minecraft.client.render.RenderLayers; import net.minecraft.client.render.RenderLayers;
import net.minecraft.client.util.math.MatrixStack; import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.fluid.FluidState; import net.minecraft.fluid.FluidState;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction; import net.minecraft.util.math.Direction;
import net.minecraft.util.math.RotationAxis;
import net.minecraft.util.math.random.LocalRandom; import net.minecraft.util.math.random.LocalRandom;
import net.minecraft.world.BlockRenderView; import net.minecraft.world.BlockRenderView;
import net.minecraft.world.LightType; import net.minecraft.world.LightType;
@@ -21,6 +17,8 @@ import net.minecraft.world.biome.ColorResolver;
import net.minecraft.world.chunk.light.LightingProvider; import net.minecraft.world.chunk.light.LightingProvider;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.joml.Matrix4f; import org.joml.Matrix4f;
import org.joml.Quaternionf;
import org.joml.Vector3f;
import org.lwjgl.opengl.GL14; import org.lwjgl.opengl.GL14;
import static org.lwjgl.opengl.GL11.*; import static org.lwjgl.opengl.GL11.*;
@@ -43,22 +41,25 @@ public class ModelTextureBakery {
this.height = height; this.height = height;
} }
public static int getMetaFromLayer(RenderLayer layer) { public static int getMetaFromLayer(BlockRenderLayer layer) {
boolean hasDiscard = layer == RenderLayer.getCutout() || boolean hasDiscard = layer == BlockRenderLayer.CUTOUT ||
layer == RenderLayer.getCutoutMipped() || layer == BlockRenderLayer.CUTOUT_MIPPED ||
layer == RenderLayer.getTripwire(); layer == BlockRenderLayer.TRIPWIRE;
boolean isMipped = layer == RenderLayer.getCutoutMipped() || boolean isMipped = layer == BlockRenderLayer.CUTOUT_MIPPED ||
layer == RenderLayer.getSolid() || layer == BlockRenderLayer.SOLID ||
layer == RenderLayer.getTranslucent() || layer == BlockRenderLayer.TRANSLUCENT ||
layer == RenderLayer.getTripwire(); layer == BlockRenderLayer.TRIPWIRE;
int meta = hasDiscard?1:0; int meta = hasDiscard?1:0;
meta |= isMipped?2:0; meta |= isMipped?2:0;
return meta; return meta;
} }
private void bakeBlockModel(BlockState state, RenderLayer layer) { private void bakeBlockModel(BlockState state, BlockRenderLayer layer) {
if (state.getRenderType() == BlockRenderType.INVISIBLE) {
return;//Dont bake if invisible
}
var model = MinecraftClient.getInstance() var model = MinecraftClient.getInstance()
.getBakedModelManager() .getBakedModelManager()
.getBlockModels() .getBlockModels()
@@ -71,7 +72,7 @@ public class ModelTextureBakery {
var quads = part.getQuads(direction); var quads = part.getQuads(direction);
for (var quad : quads) { for (var quad : quads) {
//TODO: add meta specifiying quad has a tint //TODO: add meta specifiying quad has a tint
//quad.hasTint()
this.vc.quad(quad, meta); this.vc.quad(quad, meta);
} }
} }
@@ -79,7 +80,7 @@ public class ModelTextureBakery {
} }
private void bakeFluidState(BlockState state, RenderLayer layer, int face) { private void bakeFluidState(BlockState state, BlockRenderLayer layer, int face) {
this.vc.setDefaultMeta(getMetaFromLayer(layer));//Set the meta while baking this.vc.setDefaultMeta(getMetaFromLayer(layer));//Set the meta while baking
MinecraftClient.getInstance().getBlockRenderManager().renderFluid(BlockPos.ORIGIN, new BlockRenderView() { MinecraftClient.getInstance().getBlockRenderManager().renderFluid(BlockPos.ORIGIN, new BlockRenderView() {
@Override @Override
@@ -144,7 +145,7 @@ public class ModelTextureBakery {
public int getBottomY() { public int getBottomY() {
return 0; return 0;
} }
}, this.vc, state, state.getFluidState()); }, this. vc, state, state.getFluidState());
this.vc.setDefaultMeta(0);//Reset default meta this.vc.setDefaultMeta(0);//Reset default meta
} }
@@ -163,13 +164,13 @@ public class ModelTextureBakery {
public void renderToStream(BlockState state, int streamBuffer, int streamOffset) { public void renderToStream(BlockState state, int streamBuffer, int streamOffset) {
this.capture.clear(); this.capture.clear();
boolean isBlock = true; boolean isBlock = true;
RenderLayer layer; BlockRenderLayer layer;
if (state.getBlock() instanceof FluidBlock) { if (state.getBlock() instanceof FluidBlock) {
layer = RenderLayers.getFluidLayer(state.getFluidState()); layer = RenderLayers.getFluidLayer(state.getFluidState());
isBlock = false; isBlock = false;
} else { } else {
if (state.getBlock() instanceof LeavesBlock) { if (state.getBlock() instanceof LeavesBlock) {
layer = RenderLayer.getSolid(); layer = BlockRenderLayer.SOLID;
} else { } else {
layer = RenderLayers.getBlockLayer(state); layer = RenderLayers.getBlockLayer(state);
} }
@@ -189,7 +190,7 @@ public class ModelTextureBakery {
glEnable(GL_STENCIL_TEST); glEnable(GL_STENCIL_TEST);
glEnable(GL_DEPTH_TEST); glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE); glEnable(GL_CULL_FACE);
if (layer == RenderLayer.getTranslucent()) { if (layer == BlockRenderLayer.TRANSLUCENT) {
glEnable(GL_BLEND); glEnable(GL_BLEND);
glBlendFuncSeparate(GL_ONE_MINUS_DST_ALPHA, GL_DST_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); glBlendFuncSeparate(GL_ONE_MINUS_DST_ALPHA, GL_DST_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
} else { } else {
@@ -241,6 +242,7 @@ public class ModelTextureBakery {
} }
glBindVertexArray(0); glBindVertexArray(0);
} else {//Is fluid, slow path :( } else {//Is fluid, slow path :(
if (!(state.getBlock() instanceof FluidBlock)) throw new IllegalStateException(); if (!(state.getBlock() instanceof FluidBlock)) throw new IllegalStateException();
var mat = new Matrix4f(); var mat = new Matrix4f();
@@ -299,6 +301,7 @@ public class ModelTextureBakery {
} }
//"Restore" gl state //"Restore" gl state
glViewport(viewdat[0], viewdat[1], viewdat[2], viewdat[3]); glViewport(viewdat[0], viewdat[1], viewdat[2], viewdat[3]);
glDisable(GL_STENCIL_TEST); glDisable(GL_STENCIL_TEST);
@@ -311,7 +314,7 @@ public class ModelTextureBakery {
glBindFramebuffer(GL_FRAMEBUFFER, this.capture.framebuffer.id); glBindFramebuffer(GL_FRAMEBUFFER, this.capture.framebuffer.id);
glClearDepth(1); glClearDepth(1);
glClear(GL_DEPTH_BUFFER_BIT); glClear(GL_DEPTH_BUFFER_BIT);
if (layer == RenderLayer.getTranslucent()) { if (layer == BlockRenderLayer.TRANSLUCENT) {
//reset the blend func //reset the blend func
GL14.glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); GL14.glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
} }
@@ -335,11 +338,22 @@ public class ModelTextureBakery {
private static void addView(int i, float pitch, float yaw, float rotation, int flip) { private static void addView(int i, float pitch, float yaw, float rotation, int flip) {
var stack = new MatrixStack(); var stack = new MatrixStack();
stack.translate(0.5f,0.5f,0.5f); stack.translate(0.5f,0.5f,0.5f);
stack.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(rotation)); stack.multiply(makeQuatFromAxisExact(new Vector3f(0,0,1), rotation));
stack.multiply(RotationAxis.POSITIVE_X.rotationDegrees(pitch)); stack.multiply(makeQuatFromAxisExact(new Vector3f(1,0,0), pitch));
stack.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(yaw)); stack.multiply(makeQuatFromAxisExact(new Vector3f(0,1,0), yaw));
stack.multiplyPositionMatrix(new Matrix4f().scale(1-2*(flip&1), 1-(flip&2), 1-((flip>>1)&2))); stack.multiplyPositionMatrix(new Matrix4f().scale(1-2*(flip&1), 1-(flip&2), 1-((flip>>1)&2)));
stack.translate(-0.5f,-0.5f,-0.5f); stack.translate(-0.5f,-0.5f,-0.5f);
VIEWS[i] = new Matrix4f(stack.peek().getPositionMatrix()); VIEWS[i] = new Matrix4f(stack.peek().getPositionMatrix());
} }
private static Quaternionf makeQuatFromAxisExact(Vector3f vec, float angle) {
angle = (float) Math.toRadians(angle);
float hangle = angle / 2.0f;
float sinAngle = (float) Math.sin(hangle);
float invVLength = (float) (1/Math.sqrt(vec.lengthSquared()));
return new Quaternionf(vec.x * invVLength * sinAngle,
vec.y * invVLength * sinAngle,
vec.z * invVLength * sinAngle,
Math.cos(hangle));
}
} }

View File

@@ -1,13 +1,13 @@
package me.cortex.voxy.client.core.rendering; package me.cortex.voxy.client.core.rendering;
import it.unimi.dsi.fastutil.longs.Long2ByteOpenHashMap;
import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap; import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap;
import it.unimi.dsi.fastutil.longs.LongOpenHashSet; import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
import me.cortex.voxy.client.core.AbstractRenderPipeline;
import me.cortex.voxy.client.core.gl.GlBuffer; import me.cortex.voxy.client.core.gl.GlBuffer;
import me.cortex.voxy.client.core.gl.GlFramebuffer; import me.cortex.voxy.client.core.gl.GlVertexArray;
import me.cortex.voxy.client.core.gl.GlTexture;
import me.cortex.voxy.client.core.gl.shader.AutoBindingShader; import me.cortex.voxy.client.core.gl.shader.AutoBindingShader;
import me.cortex.voxy.client.core.gl.shader.Shader; import me.cortex.voxy.client.core.gl.shader.Shader;
import me.cortex.voxy.client.core.gl.shader.ShaderLoader;
import me.cortex.voxy.client.core.gl.shader.ShaderType; import me.cortex.voxy.client.core.gl.shader.ShaderType;
import me.cortex.voxy.client.core.rendering.util.SharedIndexBuffer; import me.cortex.voxy.client.core.rendering.util.SharedIndexBuffer;
import me.cortex.voxy.client.core.rendering.util.UploadStream; import me.cortex.voxy.client.core.rendering.util.UploadStream;
@@ -26,10 +26,8 @@ import static org.lwjgl.opengl.GL15.GL_ELEMENT_ARRAY_BUFFER;
import static org.lwjgl.opengl.GL15.glBindBuffer; import static org.lwjgl.opengl.GL15.glBindBuffer;
import static org.lwjgl.opengl.GL30.glBindVertexArray; import static org.lwjgl.opengl.GL30.glBindVertexArray;
import static org.lwjgl.opengl.GL30C.*; import static org.lwjgl.opengl.GL30C.*;
import static org.lwjgl.opengl.GL31.GL_UNIFORM_BUFFER;
import static org.lwjgl.opengl.GL31.glDrawElementsInstanced; import static org.lwjgl.opengl.GL31.glDrawElementsInstanced;
import static org.lwjgl.opengl.GL42.glDrawElementsInstancedBaseInstance; import static org.lwjgl.opengl.GL42.glDrawElementsInstancedBaseInstance;
import static org.lwjgl.opengl.GL45.glClearNamedFramebufferfv;
//This is a render subsystem, its very simple in what it does //This is a render subsystem, its very simple in what it does
// it renders an AABB around loaded chunks, thats it // it renders an AABB around loaded chunks, thats it
@@ -39,20 +37,28 @@ public class ChunkBoundRenderer {
private final GlBuffer uniformBuffer = new GlBuffer(128); private final GlBuffer uniformBuffer = new GlBuffer(128);
private final Long2IntOpenHashMap chunk2idx = new Long2IntOpenHashMap(INIT_MAX_CHUNK_COUNT); private final Long2IntOpenHashMap chunk2idx = new Long2IntOpenHashMap(INIT_MAX_CHUNK_COUNT);
private long[] idx2chunk = new long[INIT_MAX_CHUNK_COUNT]; private long[] idx2chunk = new long[INIT_MAX_CHUNK_COUNT];
private final Shader rasterShader = Shader.makeAuto() private final Shader rasterShader;
.add(ShaderType.VERTEX, "voxy:chunkoutline/outline.vsh")
.add(ShaderType.FRAGMENT, "voxy:chunkoutline/outline.fsh")
.compile()
.ubo(0, this.uniformBuffer)
.ssbo(1, this.chunkPosBuffer);
private GlTexture depthBuffer = new GlTexture().store(GL_DEPTH_COMPONENT24, 1, 128, 128);
private final GlFramebuffer frameBuffer = new GlFramebuffer().bind(GL_DEPTH_ATTACHMENT, this.depthBuffer).verify();
private final LongOpenHashSet addQueue = new LongOpenHashSet(); private final LongOpenHashSet addQueue = new LongOpenHashSet();
private final LongOpenHashSet remQueue = new LongOpenHashSet(); private final LongOpenHashSet remQueue = new LongOpenHashSet();
public ChunkBoundRenderer() { private final AbstractRenderPipeline pipeline;
public ChunkBoundRenderer(AbstractRenderPipeline pipeline) {
this.chunk2idx.defaultReturnValue(-1); this.chunk2idx.defaultReturnValue(-1);
this.pipeline = pipeline;
String vert = ShaderLoader.parse("voxy:chunkoutline/outline.vsh");
String taa = pipeline.taaFunction("getTAA");
if (taa != null) {
vert = vert+"\n\n\n"+taa;
}
this.rasterShader = Shader.makeAuto()
.addSource(ShaderType.VERTEX, vert)
.defineIf("TAA", taa != null)
.add(ShaderType.FRAGMENT, "voxy:chunkoutline/outline.fsh")
.compile()
.ubo(0, this.uniformBuffer)
.ssbo(1, this.chunkPosBuffer);
} }
public void addSection(long pos) { public void addSection(long pos) {
@@ -74,18 +80,14 @@ public class ChunkBoundRenderer {
this.remQueue.forEach(this::_remPos);//TODO: REPLACE WITH SCATTER COMPUTE this.remQueue.forEach(this::_remPos);//TODO: REPLACE WITH SCATTER COMPUTE
this.remQueue.clear(); this.remQueue.clear();
if (this.chunk2idx.isEmpty()&&!wasEmpty) {//When going from stuff to nothing need to clear the depth buffer if (this.chunk2idx.isEmpty()&&!wasEmpty) {//When going from stuff to nothing need to clear the depth buffer
glClearNamedFramebufferfv(this.frameBuffer.id, GL_DEPTH, 0, new float[]{0}); viewport.depthBoundingBuffer.clear(0);
} }
} }
if (this.depthBuffer.getWidth() != viewport.width || this.depthBuffer.getHeight() != viewport.height) {
this.depthBuffer.free();
this.depthBuffer = new GlTexture().store(GL_DEPTH_COMPONENT24, 1, viewport.width, viewport.height);
this.frameBuffer.bind(GL_DEPTH_ATTACHMENT, this.depthBuffer).verify();
}
if (this.chunk2idx.isEmpty() && this.addQueue.isEmpty()) return; if (this.chunk2idx.isEmpty() && this.addQueue.isEmpty()) return;
viewport.depthBoundingBuffer.clear(0);
long ptr = UploadStream.INSTANCE.upload(this.uniformBuffer, 0, 128); long ptr = UploadStream.INSTANCE.upload(this.uniformBuffer, 0, 128);
long matPtr = ptr; ptr += 4*4*4; long matPtr = ptr; ptr += 4*4*4;
@@ -116,16 +118,16 @@ public class ChunkBoundRenderer {
glFrontFace(GL_CW);//Reverse winding order glFrontFace(GL_CW);//Reverse winding order
//"reverse depth buffer" it goes from 0->1 where 1 is far away //"reverse depth buffer" it goes from 0->1 where 1 is far away
glClearNamedFramebufferfv(this.frameBuffer.id, GL_DEPTH, 0, new float[]{0});
glEnable(GL_CULL_FACE); glEnable(GL_CULL_FACE);
glEnable(GL_DEPTH_TEST); glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_GREATER); glDepthFunc(GL_GREATER);
} }
glBindVertexArray(RenderService.STATIC_VAO); glBindVertexArray(GlVertexArray.STATIC_VAO);
glBindFramebuffer(GL_FRAMEBUFFER, this.frameBuffer.id); viewport.depthBoundingBuffer.bind();
this.rasterShader.bind(); this.rasterShader.bind();
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, SharedIndexBuffer.INSTANCE_BB_BYTE.id()); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, SharedIndexBuffer.INSTANCE_BB_BYTE.id());
this.pipeline.bindUniforms();
//Batch the draws into groups of size 32 //Batch the draws into groups of size 32
int count = this.chunk2idx.size(); int count = this.chunk2idx.size();
@@ -224,15 +226,8 @@ public class ChunkBoundRenderer {
} }
public void free() { public void free() {
this.depthBuffer.free();
this.frameBuffer.free();
this.rasterShader.free(); this.rasterShader.free();
this.uniformBuffer.free(); this.uniformBuffer.free();
this.chunkPosBuffer.free(); this.chunkPosBuffer.free();
} }
public GlTexture getDepthBoundTexture() {
return this.depthBuffer;
}
} }

View File

@@ -4,7 +4,6 @@ import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap;
import me.cortex.voxy.client.core.rendering.building.BuiltSection; import me.cortex.voxy.client.core.rendering.building.BuiltSection;
import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.StampedLock;
//CPU side cache for section geometry, not thread safe //CPU side cache for section geometry, not thread safe
public class GeometryCache { public class GeometryCache {

View File

@@ -1,247 +0,0 @@
package me.cortex.voxy.client.core.rendering;
import me.cortex.voxy.client.RenderStatistics;
import me.cortex.voxy.client.TimingStatistics;
import me.cortex.voxy.client.VoxyClient;
import me.cortex.voxy.client.core.gl.Capabilities;
import me.cortex.voxy.client.core.gl.GlTexture;
import me.cortex.voxy.client.core.model.ModelBakerySubsystem;
import me.cortex.voxy.client.core.rendering.building.RenderGenerationService;
import me.cortex.voxy.client.core.rendering.hierachical.AsyncNodeManager;
import me.cortex.voxy.client.core.rendering.hierachical.HierarchicalOcclusionTraverser;
import me.cortex.voxy.client.core.rendering.hierachical.NodeCleaner;
import me.cortex.voxy.client.core.rendering.section.AbstractSectionRenderer;
import me.cortex.voxy.client.core.rendering.section.geometry.*;
import me.cortex.voxy.client.core.rendering.section.IUsesMeshlets;
import me.cortex.voxy.client.core.rendering.section.MDICSectionRenderer;
import me.cortex.voxy.client.core.rendering.util.DownloadStream;
import me.cortex.voxy.client.core.rendering.util.UploadStream;
import me.cortex.voxy.common.Logger;
import me.cortex.voxy.common.world.WorldEngine;
import me.cortex.voxy.common.thread.ServiceThreadPool;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import static org.lwjgl.opengl.GL42.*;
public class RenderService<T extends AbstractSectionRenderer<J, Q>, J extends Viewport<J>, Q extends IGeometryData> {
public static final int STATIC_VAO = glGenVertexArrays();
private final ViewportSelector<?> viewportSelector;
private final Q geometryData;
private final AbstractSectionRenderer<J, Q> sectionRenderer;
private final AsyncNodeManager nodeManager;
private final NodeCleaner nodeCleaner;
private final HierarchicalOcclusionTraverser traversal;
private final ModelBakerySubsystem modelService;
private final RenderGenerationService renderGen;
private final WorldEngine world;
private static long getGeometryBufferSize() {
long geometryCapacity = Math.min((1L<<(64-Long.numberOfLeadingZeros(Capabilities.INSTANCE.ssboMaxSize-1)))<<1, 1L<<32)-1024/*(1L<<32)-1024*/;
if (Capabilities.INSTANCE.isIntel) {
geometryCapacity = Math.max(geometryCapacity, 1L<<30);//intel moment, force min 1gb
}
//Limit to available dedicated memory if possible
if (Capabilities.INSTANCE.canQueryGpuMemory) {
//512mb less than avalible,
long limit = Capabilities.INSTANCE.getFreeDedicatedGpuMemory() - 1024*1024*1024;
// Give a minimum of 512 mb requirement
limit = Math.max(512*1024*1024, limit);
geometryCapacity = Math.min(geometryCapacity, limit);
}
//geometryCapacity = 1<<28;
//geometryCapacity = 1<<30;//1GB test
return geometryCapacity;
}
@SuppressWarnings("unchecked")
public RenderService(WorldEngine world, ServiceThreadPool serviceThreadPool) {
this.world = world;
this.modelService = new ModelBakerySubsystem(world.getMapper());
long geometryCapacity = getGeometryBufferSize();
this.geometryData = (Q) new BasicSectionGeometryData(1<<20, geometryCapacity);
//Max sections: ~500k
this.sectionRenderer = (T) new MDICSectionRenderer(this.modelService.getStore(), (BasicSectionGeometryData) this.geometryData);
Logger.info("Using renderer: " + this.sectionRenderer.getClass().getSimpleName() + " with geometry buffer of: " + geometryCapacity + " bytes");
//Do something incredibly hacky, we dont need to keep the reference to this around, so just connect and discard
this.viewportSelector = new ViewportSelector<>(this.sectionRenderer::createViewport);
this.renderGen = new RenderGenerationService(world, this.modelService, serviceThreadPool,
this.sectionRenderer.getGeometryManager() instanceof IUsesMeshlets,
()->true);
this.nodeManager = new AsyncNodeManager(1<<21, this.geometryData, this.renderGen);
this.nodeCleaner = new NodeCleaner(this.nodeManager);
this.traversal = new HierarchicalOcclusionTraverser(this.nodeManager, this.nodeCleaner, this.renderGen);
world.setDirtyCallback(this.nodeManager::worldEvent);
Arrays.stream(world.getMapper().getBiomeEntries()).forEach(this.modelService::addBiome);
world.getMapper().setBiomeCallback(this.modelService::addBiome);
this.nodeManager.start();
}
public void addTopLevelNode(long pos) {
this.nodeManager.addTopLevel(pos);
}
public void removeTopLevelNode(long pos) {
this.nodeManager.removeTopLevel(pos);
}
public void tickModelService(long budget) {
this.modelService.tick(budget);
}
private boolean frexStillHasWork() {
if (!VoxyClient.isFrexActive()) {
return false;
}
//If frex is running we must tick everything to ensure correctness
UploadStream.INSTANCE.tick();
//Done here as is allows less gl state resetup
this.modelService.tick(100_000_000);
glFinish();
return this.nodeManager.hasWork() || this.renderGen.getTaskCount()!=0 || !this.modelService.areQueuesEmpty();
}
public void renderFarAwayOpaque(J viewport, GlTexture depthBoundTexture) {
//LightMapHelper.tickLightmap();
//Render previous geometry with the abstract renderer
//Execute the hieracial selector
// render delta sections
//Hieracial is not an abstract thing but
// the section renderer is as it might have different backends, but they all accept a buffer containing the section list
TimingStatistics.G.start();
this.sectionRenderer.renderOpaque(viewport, depthBoundTexture);
TimingStatistics.G.stop();
{
int depthBuffer = glGetFramebufferAttachmentParameteri(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME);
//Compute the mip chain
viewport.hiZBuffer.buildMipChain(depthBuffer, viewport.width, viewport.height);
}
do {
//NOTE: need to do the upload and download tick here, after the section renderer renders the world, to ensure "stable"
// sections
{
TimingStatistics.main.stop();
TimingStatistics.dynamic.start();
/*
this.sectionUpdateQueue.consume(128);
//if (this.modelService.getProcessingCount() < 750)
{//Very bad hack to try control things
this.geometryUpdateQueue.consumeNano(Math.max(3_000_000 - (System.nanoTime() - frameStart), 50_000));
}
if (this.nodeManager.writeChanges(this.traversal.getNodeBuffer())) {//TODO: maybe move the node buffer out of the traversal class
UploadStream.INSTANCE.commit();
}*/
TimingStatistics.D.start();
//Tick download stream
DownloadStream.INSTANCE.tick();
TimingStatistics.D.stop();
this.nodeManager.tick(this.traversal.getNodeBuffer(), this.nodeCleaner);
//glFlush();
this.nodeCleaner.tick(this.traversal.getNodeBuffer());//Probably do this here??
TimingStatistics.dynamic.stop();
TimingStatistics.main.start();
}
glMemoryBarrier(GL_FRAMEBUFFER_BARRIER_BIT | GL_PIXEL_BUFFER_BARRIER_BIT);
TimingStatistics.I.start();
this.traversal.doTraversal(viewport);
TimingStatistics.I.stop();
} while (this.frexStillHasWork());
TimingStatistics.H.start();
this.sectionRenderer.buildDrawCalls(viewport);
TimingStatistics.H.stop();
TimingStatistics.G.start();
this.sectionRenderer.renderTemporal(viewport, depthBoundTexture);
TimingStatistics.G.stop();
}
public void renderFarAwayTranslucent(J viewport, GlTexture depthBoundTexture) {
this.sectionRenderer.renderTranslucent(viewport, depthBoundTexture);
}
public void addDebugData(List<String> debug) {
this.modelService.addDebugData(debug);
this.renderGen.addDebugData(debug);
this.sectionRenderer.addDebug(debug);
this.nodeManager.addDebug(debug);
if (RenderStatistics.enabled) {
debug.add("HTC: [" + Arrays.stream(flipCopy(RenderStatistics.hierarchicalTraversalCounts)).mapToObj(Integer::toString).collect(Collectors.joining(", "))+"]");
debug.add("HRS: [" + Arrays.stream(flipCopy(RenderStatistics.hierarchicalRenderSections)).mapToObj(Integer::toString).collect(Collectors.joining(", "))+"]");
debug.add("VS: [" + Arrays.stream(flipCopy(RenderStatistics.visibleSections)).mapToObj(Integer::toString).collect(Collectors.joining(", "))+"]");
debug.add("QC: [" + Arrays.stream(flipCopy(RenderStatistics.quadCount)).mapToObj(Integer::toString).collect(Collectors.joining(", "))+"]");
}
}
private static int[] flipCopy(int[] array) {
int[] ret = new int[array.length];
int i = ret.length;
for (int j : array) {
ret[--i] = j;
}
return ret;
}
public void shutdown() {
//Cleanup callbacks
this.world.setDirtyCallback(null);
this.world.getMapper().setBiomeCallback(null);
this.world.getMapper().setStateCallback(null);
this.nodeManager.stop();
this.modelService.shutdown();
this.renderGen.shutdown();
this.viewportSelector.free();
this.sectionRenderer.free();
this.traversal.free();
this.nodeCleaner.free();
this.geometryData.free();
}
public Viewport<?> getViewport() {
return this.viewportSelector.getViewport();
}
public int getMeshQueueCount() {
return this.renderGen.getTaskCount();
}
}

View File

@@ -1,14 +1,19 @@
package me.cortex.voxy.client.core.rendering; package me.cortex.voxy.client.core.rendering;
import me.cortex.voxy.client.core.gl.GlBuffer; import me.cortex.voxy.client.core.gl.GlBuffer;
import me.cortex.voxy.client.core.rendering.util.DepthFramebuffer;
import me.cortex.voxy.client.core.rendering.util.HiZBuffer; import me.cortex.voxy.client.core.rendering.util.HiZBuffer;
import net.caffeinemc.mods.sodium.client.util.FogParameters;
import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.MathHelper;
import org.joml.*; import org.joml.*;
import java.lang.reflect.Field; import java.lang.reflect.Field;
public abstract class Viewport <A extends Viewport<A>> { public abstract class Viewport <A extends Viewport<A>> {
//public final HiZBuffer2 hiZBuffer = new HiZBuffer2();
public final HiZBuffer hiZBuffer = new HiZBuffer(); public final HiZBuffer hiZBuffer = new HiZBuffer();
public final DepthFramebuffer depthBoundingBuffer = new DepthFramebuffer();
private static final Field planesField; private static final Field planesField;
static { static {
try { try {
@@ -22,13 +27,15 @@ public abstract class Viewport <A extends Viewport<A>> {
public int width; public int width;
public int height; public int height;
public int frameId; public int frameId;
public Matrix4f projection; public Matrix4f vanillaProjection = new Matrix4f();
public Matrix4f modelView; public Matrix4f projection = new Matrix4f();
public Matrix4f modelView = new Matrix4f();
public final FrustumIntersection frustum = new FrustumIntersection(); public final FrustumIntersection frustum = new FrustumIntersection();
public final Vector4f[] frustumPlanes; public final Vector4f[] frustumPlanes;
public double cameraX; public double cameraX;
public double cameraY; public double cameraY;
public double cameraZ; public double cameraZ;
public FogParameters fogParameters;
public final Matrix4f MVP = new Matrix4f(); public final Matrix4f MVP = new Matrix4f();
public final Vector3i section = new Vector3i(); public final Vector3i section = new Vector3i();
@@ -50,6 +57,12 @@ public abstract class Viewport <A extends Viewport<A>> {
protected void delete0() { protected void delete0() {
this.hiZBuffer.free(); this.hiZBuffer.free();
this.depthBoundingBuffer.free();
}
public A setVanillaProjection(Matrix4fc projection) {
this.vanillaProjection.set(projection);
return (A) this;
} }
public A setProjection(Matrix4f projection) { public A setProjection(Matrix4f projection) {
@@ -75,6 +88,11 @@ public abstract class Viewport <A extends Viewport<A>> {
return (A) this; return (A) this;
} }
public A setFogParameters(FogParameters fogParameters) {
this.fogParameters = fogParameters;
return (A) this;
}
public A update() { public A update() {
//MVP //MVP
this.projection.mul(this.modelView, this.MVP); this.projection.mul(this.modelView, this.MVP);
@@ -93,6 +111,8 @@ public abstract class Viewport <A extends Viewport<A>> {
(float) (this.cameraY-(sy<<5)), (float) (this.cameraY-(sy<<5)),
(float) (this.cameraZ-(sz<<5))); (float) (this.cameraZ-(sz<<5)));
this.depthBoundingBuffer.resize(this.width, this.height);
return (A) this; return (A) this;
} }

View File

@@ -1,6 +1,6 @@
package me.cortex.voxy.client.core.rendering; package me.cortex.voxy.client.core.rendering;
import me.cortex.voxy.client.core.rendering.Viewport; import me.cortex.voxy.client.core.util.IrisUtil;
import net.fabricmc.loader.api.FabricLoader; import net.fabricmc.loader.api.FabricLoader;
import org.vivecraft.client_vr.ClientDataHolderVR; import org.vivecraft.client_vr.ClientDataHolderVR;
@@ -20,19 +20,28 @@ public class ViewportSelector <T extends Viewport<?>> {
this.defaultViewport = viewportCreator.get(); this.defaultViewport = viewportCreator.get();
} }
private T getOrCreate(Object holder) {
return this.extraViewports.computeIfAbsent(holder, a->this.creator.get());
}
private T getVivecraftViewport() { private T getVivecraftViewport() {
var cdh = ClientDataHolderVR.getInstance(); var cdh = ClientDataHolderVR.getInstance();
var pass = cdh.currentPass; var pass = cdh.currentPass;
if (pass == null) { if (pass == null) {
return this.defaultViewport; return this.defaultViewport;
} }
return this.extraViewports.computeIfAbsent(pass, a->this.creator.get()); return this.getOrCreate(pass);
} }
private static final Object IRIS_SHADOW_OBJECT = new Object();
public T getViewport() { public T getViewport() {
if (VIVECRAFT_INSTALLED) { if (VIVECRAFT_INSTALLED) {
return getVivecraftViewport(); return getVivecraftViewport();
} }
if (IrisUtil.irisShadowActive()) {
return this.getOrCreate(IRIS_SHADOW_OBJECT);
}
return this.defaultViewport; return this.defaultViewport;
} }

View File

@@ -4,6 +4,7 @@ import me.cortex.voxy.client.core.model.IdNotYetComputedException;
import me.cortex.voxy.client.core.model.ModelFactory; import me.cortex.voxy.client.core.model.ModelFactory;
import me.cortex.voxy.client.core.model.ModelQueries; import me.cortex.voxy.client.core.model.ModelQueries;
import me.cortex.voxy.client.core.util.ScanMesher2D; import me.cortex.voxy.client.core.util.ScanMesher2D;
import me.cortex.voxy.common.Logger;
import me.cortex.voxy.common.util.MemoryBuffer; import me.cortex.voxy.common.util.MemoryBuffer;
import me.cortex.voxy.common.util.UnsafeUtil; import me.cortex.voxy.common.util.UnsafeUtil;
import me.cortex.voxy.common.world.WorldEngine; import me.cortex.voxy.common.world.WorldEngine;
@@ -17,6 +18,7 @@ import java.util.Arrays;
public class RenderDataFactory { public class RenderDataFactory {
private static final boolean CHECK_NEIGHBOR_FACE_OCCLUSION = true; private static final boolean CHECK_NEIGHBOR_FACE_OCCLUSION = true;
private static final boolean DISABLE_CULL_SAME_OCCLUDES = false;
private static final boolean VERIFY_MESHING = VoxyCommon.isVerificationFlagOn("verifyMeshing"); private static final boolean VERIFY_MESHING = VoxyCommon.isVerificationFlagOn("verifyMeshing");
@@ -339,7 +341,7 @@ public class RenderDataFactory {
private static final long LM = (0xFFL<<55); private static final long LM = (0xFFL<<55);
private static boolean shouldMeshNonOpaqueBlockFace(int face, long quad, long meta, long neighborQuad, long neighborMeta) { private static boolean shouldMeshNonOpaqueBlockFace(int face, long quad, long meta, long neighborQuad, long neighborMeta) {
if (((quad^neighborQuad)&(0xFFFFL<<26))==0) return false;//This is a hack, if the neigbor and this are the same, dont mesh the face if (((quad^neighborQuad)&(0xFFFFL<<26))==0 && (DISABLE_CULL_SAME_OCCLUDES || ModelQueries.faceOccludes(meta, face))) return false;//This is a hack, if the neigbor and this are the same, dont mesh the face// TODO: FIXME
if (!ModelQueries.faceExists(meta, face)) return false;//Dont mesh if no face if (!ModelQueries.faceExists(meta, face)) return false;//Dont mesh if no face
//if (ModelQueries.faceCanBeOccluded(meta, face)) //TODO: maybe enable this //if (ModelQueries.faceCanBeOccluded(meta, face)) //TODO: maybe enable this
if (ModelQueries.faceOccludes(neighborMeta, face^1)) return false; if (ModelQueries.faceOccludes(neighborMeta, face^1)) return false;
@@ -751,7 +753,7 @@ public class RenderDataFactory {
//Check and test if can cull W.R.T neighbor //Check and test if can cull W.R.T neighbor
if (Mapper.getBlockId(neighborId) != 0) {//Not air if (Mapper.getBlockId(neighborId) != 0) {//Not air
int modelId = this.modelMan.getModelId(Mapper.getBlockId(neighborId)); int modelId = this.modelMan.getModelId(Mapper.getBlockId(neighborId));
if (modelId == ((A>>26)&0xFFFF)) { if (modelId == ((A>>26)&0xFFFF)) {//TODO: FIXME, this technically isnt correct as need to check self occulsion, thinks?
fail = true; fail = true;
} else { } else {
long meta = this.modelMan.getModelMetadataFromClientId(modelId); long meta = this.modelMan.getModelMetadataFromClientId(modelId);
@@ -765,7 +767,7 @@ public class RenderDataFactory {
long nA = this.sectionData[(idx+skipAmount) * 2]; long nA = this.sectionData[(idx+skipAmount) * 2];
long nB = this.sectionData[(idx+skipAmount) * 2 + 1]; long nB = this.sectionData[(idx+skipAmount) * 2 + 1];
boolean failB = false; boolean failB = false;
if ((nA&(0xFFFFL<<26)) == (A&(0xFFFFL<<26))) { if ((nA&(0xFFFFL<<26)) == (A&(0xFFFFL<<26))) {//TODO: FIXME, this technically isnt correct as need to check self occulsion, thinks?
failB = true; failB = true;
} else { } else {
if (ModelQueries.faceOccludes(nB, (axis << 1) | (side))) { if (ModelQueries.faceOccludes(nB, (axis << 1) | (side))) {
@@ -1607,6 +1609,10 @@ public class RenderDataFactory {
return BuiltSection.emptyWithChildren(section.key, section.getNonEmptyChildren()); return BuiltSection.emptyWithChildren(section.key, section.getNonEmptyChildren());
} }
if (this.quadCount >= 1<<16) {
Logger.warn("Large quad count for section " + WorldEngine.pprintPos(section.key) + " is " + this.quadCount);
}
if (this.minX<0 || this.minY<0 || this.minZ<0 || 32<this.maxX || 32<this.maxY || 32<this.maxZ) { if (this.minX<0 || this.minY<0 || this.minZ<0 || 32<this.maxX || 32<this.maxY || 32<this.maxZ) {
throw new IllegalStateException(); throw new IllegalStateException();
} }

View File

@@ -4,12 +4,12 @@ import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
import me.cortex.voxy.client.core.model.IdNotYetComputedException; import me.cortex.voxy.client.core.model.IdNotYetComputedException;
import me.cortex.voxy.client.core.model.ModelBakerySubsystem; import me.cortex.voxy.client.core.model.ModelBakerySubsystem;
import me.cortex.voxy.common.thread.ServiceSlice;
import me.cortex.voxy.common.thread.ServiceThreadPool;
import me.cortex.voxy.common.util.Pair; import me.cortex.voxy.common.util.Pair;
import me.cortex.voxy.common.world.WorldEngine; import me.cortex.voxy.common.world.WorldEngine;
import me.cortex.voxy.common.world.WorldSection; import me.cortex.voxy.common.world.WorldSection;
import me.cortex.voxy.common.world.other.Mapper; import me.cortex.voxy.common.world.other.Mapper;
import me.cortex.voxy.common.thread.ServiceSlice;
import me.cortex.voxy.common.thread.ServiceThreadPool;
import java.util.List; import java.util.List;
import java.util.concurrent.PriorityBlockingQueue; import java.util.concurrent.PriorityBlockingQueue;
@@ -51,9 +51,9 @@ public class RenderGenerationService {
private final AtomicInteger holdingSectionCount = new AtomicInteger();//Used to limit section holding private final AtomicInteger holdingSectionCount = new AtomicInteger();//Used to limit section holding
private final AtomicInteger taskQueueCount = new AtomicInteger(); private final AtomicInteger taskQueueCount = new AtomicInteger();
private final PriorityBlockingQueue<BuildTask> taskQueue = new PriorityBlockingQueue<>(320000, (a,b)-> Long.compareUnsigned(a.priority, b.priority)); private final PriorityBlockingQueue<BuildTask> taskQueue = new PriorityBlockingQueue<>(5000, (a,b)-> Long.compareUnsigned(a.priority, b.priority));
private final StampedLock taskMapLock = new StampedLock(); private final StampedLock taskMapLock = new StampedLock();
private final Long2ObjectOpenHashMap<BuildTask> taskMap = new Long2ObjectOpenHashMap<>(320000); private final Long2ObjectOpenHashMap<BuildTask> taskMap = new Long2ObjectOpenHashMap<>(5000);
private final WorldEngine world; private final WorldEngine world;
private final ModelBakerySubsystem modelBakery; private final ModelBakerySubsystem modelBakery;
@@ -141,6 +141,17 @@ public class RenderGenerationService {
section = task.section; section = task.section;
} }
{//Remove the task from the map, this is done before we check for null sections as well the task map needs to be correct
long stamp = this.taskMapLock.writeLock();
var rtask = this.taskMap.remove(task.position);
if (rtask != task) {
this.taskMapLock.unlockWrite(stamp);
throw new IllegalStateException();
}
this.taskMapLock.unlockWrite(stamp);
}
if (section == null) { if (section == null) {
if (this.resultConsumer != null) { if (this.resultConsumer != null) {
this.resultConsumer.accept(BuiltSection.empty(task.position)); this.resultConsumer.accept(BuiltSection.empty(task.position));
@@ -150,15 +161,6 @@ public class RenderGenerationService {
section.assertNotFree(); section.assertNotFree();
BuiltSection mesh = null; BuiltSection mesh = null;
{
long stamp = this.taskMapLock.writeLock();
var rtask = this.taskMap.remove(task.position);
if (rtask != task) {
this.taskMapLock.unlockWrite(stamp);
throw new IllegalStateException();
}
this.taskMapLock.unlockWrite(stamp);
}
try { try {
mesh = factory.generateMesh(section); mesh = factory.generateMesh(section);

View File

@@ -1,6 +1,8 @@
package me.cortex.voxy.client.core.rendering.hierachical; package me.cortex.voxy.client.core.rendering.hierachical;
import it.unimi.dsi.fastutil.ints.*; import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
import it.unimi.dsi.fastutil.ints.IntConsumer;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.longs.LongOpenHashSet; import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
import me.cortex.voxy.client.TimingStatistics; import me.cortex.voxy.client.TimingStatistics;
import me.cortex.voxy.client.core.gl.GlBuffer; import me.cortex.voxy.client.core.gl.GlBuffer;
@@ -17,7 +19,7 @@ import me.cortex.voxy.client.core.rendering.util.UploadStream;
import me.cortex.voxy.common.Logger; import me.cortex.voxy.common.Logger;
import me.cortex.voxy.common.util.AllocationArena; import me.cortex.voxy.common.util.AllocationArena;
import me.cortex.voxy.common.util.MemoryBuffer; import me.cortex.voxy.common.util.MemoryBuffer;
import me.cortex.voxy.common.world.WorldEngine; import me.cortex.voxy.common.util.UnsafeUtil;
import me.cortex.voxy.common.world.WorldSection; import me.cortex.voxy.common.world.WorldSection;
import org.lwjgl.system.MemoryUtil; import org.lwjgl.system.MemoryUtil;
@@ -246,7 +248,7 @@ public class AsyncNodeManager {
//Limit uploading as well as by geometry capacity being available //Limit uploading as well as by geometry capacity being available
// must have 50 mb of free geometry space to upload // must have 50 mb of free geometry space to upload
for (int limit = 0; limit < 200 && ((this.geometryCapacity-this.geometryManager.getGeometryUsedBytes())>50_000_000); limit++) { for (int limit = 0; limit < 200 && ((this.geometryCapacity-this.geometryManager.getGeometryUsedBytes())>50_000_000L); limit++) {
var job = this.geometryUpdateQueue.poll(); var job = this.geometryUpdateQueue.poll();
if (job == null) if (job == null)
break; break;
@@ -516,8 +518,8 @@ public class AsyncNodeManager {
int copies = upload.dataUploadPoints.size(); int copies = upload.dataUploadPoints.size();
int scratchSize = (int) upload.arena.getSize() * 8; int scratchSize = (int) upload.arena.getSize() * 8;
long ptr = UploadStream.INSTANCE.rawUploadAddress(scratchSize + copies * 16); long ptr = UploadStream.INSTANCE.rawUploadAddress(scratchSize + copies * 16);
MemoryUtil.memCopy(upload.scratchHeaderBuffer.address, UploadStream.INSTANCE.getBaseAddress() + ptr, copies * 16L); UnsafeUtil.memcpy(upload.scratchHeaderBuffer.address, UploadStream.INSTANCE.getBaseAddress() + ptr, copies * 16L);
MemoryUtil.memCopy(upload.scratchDataBuffer.address, UploadStream.INSTANCE.getBaseAddress() + ptr + copies * 16L, scratchSize); UnsafeUtil.memcpy(upload.scratchDataBuffer.address, UploadStream.INSTANCE.getBaseAddress() + ptr + copies * 16L, scratchSize);
UploadStream.INSTANCE.commit();//Commit the buffer UploadStream.INSTANCE.commit();//Commit the buffer
this.multiMemcpy.bind(); this.multiMemcpy.bind();

View File

@@ -1,11 +1,11 @@
package me.cortex.voxy.client.core.rendering.hierachical; package me.cortex.voxy.client.core.rendering.hierachical;
import me.cortex.voxy.client.core.gl.GlBuffer; import me.cortex.voxy.client.core.gl.GlBuffer;
import me.cortex.voxy.client.core.gl.GlVertexArray;
import me.cortex.voxy.client.core.gl.shader.Shader; import me.cortex.voxy.client.core.gl.shader.Shader;
import me.cortex.voxy.client.core.gl.shader.ShaderType; import me.cortex.voxy.client.core.gl.shader.ShaderType;
import me.cortex.voxy.client.core.rendering.RenderService;
import me.cortex.voxy.client.core.rendering.util.SharedIndexBuffer;
import me.cortex.voxy.client.core.rendering.Viewport; import me.cortex.voxy.client.core.rendering.Viewport;
import me.cortex.voxy.client.core.rendering.util.SharedIndexBuffer;
import me.cortex.voxy.client.core.rendering.util.UploadStream; import me.cortex.voxy.client.core.rendering.util.UploadStream;
import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.MathHelper;
import org.joml.Matrix4f; import org.joml.Matrix4f;
@@ -67,7 +67,7 @@ public class DebugRenderer {
glEnable(GL_DEPTH_TEST); glEnable(GL_DEPTH_TEST);
this.debugShader.bind(); this.debugShader.bind();
glBindVertexArray(RenderService.STATIC_VAO); glBindVertexArray(GlVertexArray.STATIC_VAO);
glBindBuffer(GL_DRAW_INDIRECT_BUFFER, this.drawBuffer.id); glBindBuffer(GL_DRAW_INDIRECT_BUFFER, this.drawBuffer.id);
GL15.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, SharedIndexBuffer.INSTANCE_BYTE.id()); GL15.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, SharedIndexBuffer.INSTANCE_BYTE.id());
glBindBufferBase(GL_UNIFORM_BUFFER, 0, this.uniformBuffer.id); glBindBufferBase(GL_UNIFORM_BUFFER, 0, this.uniformBuffer.id);

View File

@@ -7,12 +7,12 @@ import me.cortex.voxy.client.core.gl.GlBuffer;
import me.cortex.voxy.client.core.gl.shader.AutoBindingShader; import me.cortex.voxy.client.core.gl.shader.AutoBindingShader;
import me.cortex.voxy.client.core.gl.shader.Shader; import me.cortex.voxy.client.core.gl.shader.Shader;
import me.cortex.voxy.client.core.gl.shader.ShaderType; import me.cortex.voxy.client.core.gl.shader.ShaderType;
import me.cortex.voxy.client.core.rendering.building.RenderGenerationService;
import me.cortex.voxy.client.core.rendering.util.PrintfDebugUtil;
import me.cortex.voxy.client.core.rendering.util.HiZBuffer;
import me.cortex.voxy.client.core.rendering.Viewport; import me.cortex.voxy.client.core.rendering.Viewport;
import me.cortex.voxy.client.core.rendering.building.RenderGenerationService;
import me.cortex.voxy.client.core.rendering.util.DownloadStream; import me.cortex.voxy.client.core.rendering.util.DownloadStream;
import me.cortex.voxy.client.core.rendering.util.PrintfDebugUtil;
import me.cortex.voxy.client.core.rendering.util.UploadStream; import me.cortex.voxy.client.core.rendering.util.UploadStream;
import me.cortex.voxy.common.Logger;
import me.cortex.voxy.common.util.MemoryBuffer; import me.cortex.voxy.common.util.MemoryBuffer;
import me.cortex.voxy.common.world.WorldEngine; import me.cortex.voxy.common.world.WorldEngine;
import org.lwjgl.system.MemoryUtil; import org.lwjgl.system.MemoryUtil;
@@ -131,7 +131,9 @@ public class HierarchicalOcclusionTraverser {
//Use clear buffer, yes know is a bad idea, TODO: replace //Use clear buffer, yes know is a bad idea, TODO: replace
//Add the new top level node to the queue //Add the new top level node to the queue
glClearNamedBufferSubData(this.topNodeIds.id, GL_R32UI, aid*4L, 4, GL_RED_INTEGER, GL_UNSIGNED_INT, new int[]{id}); MemoryUtil.memPutInt(SCRATCH, id);
nglClearNamedBufferSubData(this.topNodeIds.id, GL_R32UI, aid * 4L, 4, GL_RED_INTEGER, GL_UNSIGNED_INT, SCRATCH);
if (this.topNode2idxMapping.put(id, aid) != -1) { if (this.topNode2idxMapping.put(id, aid) != -1) {
throw new IllegalStateException(); throw new IllegalStateException();
} }
@@ -158,8 +160,10 @@ public class HierarchicalOcclusionTraverser {
this.idx2topNodeMapping[idx] = endTLNId;//Set the old to the new this.idx2topNodeMapping[idx] = endTLNId;//Set the old to the new
if (this.topNode2idxMapping.put(endTLNId, idx) == -1) if (this.topNode2idxMapping.put(endTLNId, idx) == -1)
throw new IllegalStateException(); throw new IllegalStateException();
//Move it server side, from end to new idx //Move it server side, from end to new idx
glClearNamedBufferSubData(this.topNodeIds.id, GL_R32UI, idx*4L, 4, GL_RED_INTEGER, GL_UNSIGNED_INT, new int[]{endTLNId}); MemoryUtil.memPutInt(SCRATCH, endTLNId);
nglClearNamedBufferSubData(this.topNodeIds.id, GL_R32UI, idx*4L, 4, GL_RED_INTEGER, GL_UNSIGNED_INT, SCRATCH);
} }
private static void setFrustum(Viewport<?> viewport, long ptr) { private static void setFrustum(Viewport<?> viewport, long ptr) {
@@ -321,7 +325,8 @@ public class HierarchicalOcclusionTraverser {
private void forwardDownloadResult(long ptr, long size) { private void forwardDownloadResult(long ptr, long size) {
int count = MemoryUtil.memGetInt(ptr);ptr += 8;//its 8 since we need to skip the second value (which is empty) int count = MemoryUtil.memGetInt(ptr);ptr += 8;//its 8 since we need to skip the second value (which is empty)
if (count < 0 || count > 50000) { if (count < 0 || count > 50000) {
throw new IllegalStateException("Count unexpected extreme value: " + count); Logger.error(new IllegalStateException("Count unexpected extreme value: " + count + " things may get weird"));
return;
} }
if (count > (this.requestBuffer.size()>>3)-1) { if (count > (this.requestBuffer.size()>>3)-1) {
//This should not break the synchonization between gpu and cpu as in the traversal shader is //This should not break the synchonization between gpu and cpu as in the traversal shader is
@@ -358,4 +363,6 @@ public class HierarchicalOcclusionTraverser {
this.scratchQueueB.free(); this.scratchQueueB.free();
glDeleteSamplers(this.hizSampler); glDeleteSamplers(this.hizSampler);
} }
private static final long SCRATCH = MemoryUtil.nmemAlloc(32);//32 bytes of scratch memory
} }

View File

@@ -5,13 +5,12 @@ import me.cortex.voxy.client.core.gl.GlBuffer;
import me.cortex.voxy.client.core.gl.shader.AutoBindingShader; import me.cortex.voxy.client.core.gl.shader.AutoBindingShader;
import me.cortex.voxy.client.core.gl.shader.Shader; import me.cortex.voxy.client.core.gl.shader.Shader;
import me.cortex.voxy.client.core.gl.shader.ShaderType; import me.cortex.voxy.client.core.gl.shader.ShaderType;
import me.cortex.voxy.client.core.rendering.util.PrintfDebugUtil;
import me.cortex.voxy.client.core.rendering.util.DownloadStream; import me.cortex.voxy.client.core.rendering.util.DownloadStream;
import me.cortex.voxy.client.core.rendering.util.PrintfDebugUtil;
import me.cortex.voxy.client.core.rendering.util.UploadStream; import me.cortex.voxy.client.core.rendering.util.UploadStream;
import org.lwjgl.opengl.ARBDirectStateAccess; import org.lwjgl.opengl.ARBDirectStateAccess;
import org.lwjgl.system.MemoryUtil; import org.lwjgl.system.MemoryUtil;
import static org.lwjgl.opengl.ARBDirectStateAccess.glCopyNamedBufferSubData;
import static org.lwjgl.opengl.GL30C.glBindBufferRange; import static org.lwjgl.opengl.GL30C.glBindBufferRange;
import static org.lwjgl.opengl.GL42C.glMemoryBarrier; import static org.lwjgl.opengl.GL42C.glMemoryBarrier;
import static org.lwjgl.opengl.GL43C.*; import static org.lwjgl.opengl.GL43C.*;

View File

@@ -18,7 +18,6 @@ import me.cortex.voxy.common.world.WorldEngine;
import org.lwjgl.system.MemoryUtil; import org.lwjgl.system.MemoryUtil;
import java.util.List; import java.util.List;
import java.util.function.Consumer;
import static me.cortex.voxy.common.world.WorldEngine.MAX_LOD_LAYER; import static me.cortex.voxy.common.world.WorldEngine.MAX_LOD_LAYER;
import static me.cortex.voxy.common.world.WorldEngine.UPDATE_TYPE_BLOCK_BIT; import static me.cortex.voxy.common.world.WorldEngine.UPDATE_TYPE_BLOCK_BIT;

View File

@@ -2,9 +2,13 @@ package me.cortex.voxy.client.core.rendering.post;
import me.cortex.voxy.client.core.gl.shader.Shader; import me.cortex.voxy.client.core.gl.shader.Shader;
import me.cortex.voxy.client.core.gl.shader.ShaderType; import me.cortex.voxy.client.core.gl.shader.ShaderType;
import me.cortex.voxy.client.core.rendering.util.SharedIndexBuffer;
import static org.lwjgl.opengl.GL11C.GL_TRIANGLES; import java.util.function.Function;
import static org.lwjgl.opengl.GL11C.glDrawArrays;
import static org.lwjgl.opengl.GL11C.*;
import static org.lwjgl.opengl.GL15C.GL_ELEMENT_ARRAY_BUFFER;
import static org.lwjgl.opengl.GL15C.glBindBuffer;
import static org.lwjgl.opengl.GL30C.glBindVertexArray; import static org.lwjgl.opengl.GL30C.glBindVertexArray;
import static org.lwjgl.opengl.GL45C.glCreateVertexArrays; import static org.lwjgl.opengl.GL45C.glCreateVertexArrays;
@@ -13,9 +17,21 @@ public class FullscreenBlit {
private final Shader shader; private final Shader shader;
public FullscreenBlit(String fragId) { public FullscreenBlit(String fragId) {
this.shader = Shader.make() this(fragId, (a)->a);
.add(ShaderType.VERTEX, "voxy:post/fullscreen.vert") }
.add(ShaderType.FRAGMENT, fragId)
public FullscreenBlit(String vertId, String fragId) {
this(vertId, fragId, (a)->a);
}
public <T extends Shader> FullscreenBlit(String fragId, Function<Shader.Builder<T>, Shader.Builder<T>> builder) {
this("voxy:post/fullscreen.vert", fragId, builder);
}
public <T extends Shader> FullscreenBlit(String vertId, String fragId, Function<Shader.Builder<T>, Shader.Builder<T>> builder) {
this.shader = builder.apply((Shader.Builder<T>) Shader.make()
.add(ShaderType.VERTEX, vertId)
.add(ShaderType.FRAGMENT, fragId))
.compile(); .compile();
} }
@@ -26,7 +42,8 @@ public class FullscreenBlit {
public void blit() { public void blit() {
glBindVertexArray(EMPTY_VAO); glBindVertexArray(EMPTY_VAO);
this.shader.bind(); this.shader.bind();
glDrawArrays(GL_TRIANGLES, 0, 6); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, SharedIndexBuffer.INSTANCE_BYTE.id());
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, 0);
glBindVertexArray(0); glBindVertexArray(0);
} }

View File

@@ -1,195 +0,0 @@
package me.cortex.voxy.client.core.rendering.post;
import me.cortex.voxy.client.core.gl.GlFramebuffer;
import me.cortex.voxy.client.core.gl.GlTexture;
import me.cortex.voxy.client.core.gl.shader.Shader;
import me.cortex.voxy.client.core.gl.shader.ShaderType;
import me.cortex.voxy.client.core.rendering.util.GlStateCapture;
import net.minecraft.client.util.math.MatrixStack;
import org.joml.Matrix4f;
import org.joml.Matrix4fc;
import org.lwjgl.opengl.GL11C;
import static org.lwjgl.opengl.ARBComputeShader.glDispatchCompute;
import static org.lwjgl.opengl.ARBShaderImageLoadStore.glBindImageTexture;
import static org.lwjgl.opengl.GL15.GL_READ_WRITE;
import static org.lwjgl.opengl.GL20.glUniformMatrix4fv;
import static org.lwjgl.opengl.GL30C.*;
import static org.lwjgl.opengl.GL30C.GL_DEPTH24_STENCIL8;
import static org.lwjgl.opengl.GL43.GL_DEPTH_STENCIL_TEXTURE_MODE;
import static org.lwjgl.opengl.GL45C.*;
public class PostProcessing {
private final GlFramebuffer framebuffer;
private final GlFramebuffer framebufferSSAO;
private int width;
private int height;
private GlTexture colour;
private GlTexture colourSSAO;
private GlTexture depthStencil;
private boolean didSSAO;
private final FullscreenBlit setDepth0 = new FullscreenBlit("voxy:post/depth0.frag");
private final FullscreenBlit emptyBlit = new FullscreenBlit("voxy:post/noop.frag");
//private final FullscreenBlit blitTexture = new FullscreenBlit("voxy:post/blit_texture_cutout.frag");
private final FullscreenBlit blitTexture = new FullscreenBlit("voxy:post/blit_texture_depth_cutout.frag");
private final Shader ssaoComp = Shader.make()
.add(ShaderType.COMPUTE, "voxy:post/ssao.comp")
.compile();
private final GlStateCapture glStateCapture = GlStateCapture.make()
.addCapability(GL_STENCIL_TEST)
.addCapability(GL_DEPTH_TEST)
.addTexture(GL_TEXTURE0)
.addTexture(GL_TEXTURE1)
.addTexture(GL_TEXTURE2)
.build();
public PostProcessing() {
this.framebuffer = new GlFramebuffer();
this.framebufferSSAO = new GlFramebuffer();
}
public void setSize(int width, int height) {
if (this.width != width || this.height != height) {
this.width = width;
this.height = height;
if (this.colour != null) {
if (this.colourSSAO != null) {
this.colourSSAO.free();
}
this.colour.free();
this.depthStencil.free();
}
this.colour = new GlTexture().store(GL_RGBA8, 1, width, height);
this.colourSSAO = new GlTexture().store(GL_RGBA8, 1, width, height);
this.depthStencil = new GlTexture().store(GL_DEPTH24_STENCIL8, 1, width, height);
glTextureParameterf(this.colour.id, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTextureParameterf(this.colour.id, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTextureParameterf(this.colourSSAO.id, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTextureParameterf(this.colourSSAO.id, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTextureParameterf(this.depthStencil.id, GL_DEPTH_STENCIL_TEXTURE_MODE, GL_DEPTH_COMPONENT);
this.framebuffer.bind(GL_COLOR_ATTACHMENT0, this.colour)
.bind(GL_DEPTH_STENCIL_ATTACHMENT, this.depthStencil)
.verify();
this.framebufferSSAO.bind(GL_COLOR_ATTACHMENT0, this.colourSSAO)
.bind(GL_DEPTH_STENCIL_ATTACHMENT, this.depthStencil)
.verify();
}
}
public void shutdown() {
this.framebuffer.free();
this.framebufferSSAO.free();
if (this.colourSSAO != null) this.colourSSAO.free();
if (this.colour != null) this.colour.free();
if (this.depthStencil != null) this.depthStencil.free();
this.emptyBlit.delete();
this.setDepth0.delete();
this.blitTexture.delete();
this.ssaoComp.free();
}
public void setup(int width, int height, int sourceFB) {
//TODO: use the raw depth texture instead
//TODO: when blitting, also set the depth value of where the mask is created to 0 (I.E. closest to the camera)
// cause of hiz computing, it makes alot of sections visible
this.didSSAO = false;
this.glStateCapture.capture();
this.setSize(width, height);
glBindFramebuffer(GL_FRAMEBUFFER, this.framebuffer.id);
//glClearColor(0,0,0,0);//TODO: RESTORE THIS TO THE ORIGINAL VALUE
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
glBlitNamedFramebuffer(sourceFB, this.framebuffer.id, 0,0, width, height, 0,0, width, height, GL_DEPTH_BUFFER_BIT, GL_NEAREST);
//Create a stencil mask of terrain generated by minecraft
glEnable(GL_STENCIL_TEST);
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
glStencilFunc(GL_ALWAYS, 1, 0xFF);
glStencilMask(0xFF);
glEnable(GL_DEPTH_TEST);
glDepthMask(false);
glColorMask(false,false,false,false);
this.emptyBlit.blit();
glDepthMask(true);
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
//Set depth to 0 w.r.t mask
glStencilFunc(GL_EQUAL, 0, 0xFF);
this.setDepth0.blit();
glDisable(GL_DEPTH_TEST);
glColorMask(true,true,true,true);
//Make voxy terrain render only where there isnt mc terrain
glStencilFunc(GL_EQUAL, 1, 0xFF);
//TODO: need to figure out how to do translucency cause doing it normally will cause water to have double translucency (i think)
}
//Computes ssao on the current framebuffer data and updates it
// this means that translucency wont be effected etc
public void computeSSAO(Matrix4f mvp) {
this.didSSAO = true;
this.ssaoComp.bind();
float[] data = new float[4*4];
mvp.get(data);
glUniformMatrix4fv(3, false, data);//MVP
mvp.invert(new Matrix4f()).get(data);
glUniformMatrix4fv(4, false, data);//invMVP
glBindImageTexture(0, this.colourSSAO.id, 0, false,0, GL_READ_WRITE, GL_RGBA8);
glBindTextureUnit(1, this.depthStencil.id);
glBindTextureUnit(2, this.colour.id);
glDispatchCompute((this.width+31)/32, (this.height+31)/32, 1);
glBindFramebuffer(GL_FRAMEBUFFER, this.framebufferSSAO.id);
}
//Executes the post processing and emits to whatever framebuffer is currently bound via a blit
public void renderPost(Matrix4f fromProjection, Matrix4fc tooProjection, int outputFB) {
glDisable(GL_STENCIL_TEST);
glBindFramebuffer(GL_FRAMEBUFFER, outputFB);
//This will need to be replaced with a blit shader to raster with respect to the stencil
//glBlitNamedFramebuffer(this.framebuffer.id, outputFB, 0,0, this.width, this.height, 0,0, this.width, this.height, GL_COLOR_BUFFER_BIT, GL_NEAREST);
this.blitTexture.bind();
float[] data = new float[4*4];
var mat = new Matrix4f(fromProjection).invert();
mat.get(data);
glUniformMatrix4fv(2, false, data);//inverse fromProjection
tooProjection.get(data);
glUniformMatrix4fv(3, false, data);//tooProjection
glBindTextureUnit(0, this.didSSAO?this.colourSSAO.id:this.colour.id);
glBindTextureUnit(1, this.depthStencil.id);
//glTextureParameteri(this.depthStencil.id, GL_DEPTH_STENCIL_TEXTURE_MODE, GL_DEPTH_COMPONENT);
glEnable(GL_DEPTH_TEST);
glDepthMask(true);
this.blitTexture.blit();
glDisable(GL_DEPTH_TEST);
glDepthMask(true);
this.glStateCapture.restore();
}
}

View File

@@ -1,7 +1,6 @@
package me.cortex.voxy.client.core.rendering.section; package me.cortex.voxy.client.core.rendering.section;
import me.cortex.voxy.client.core.gl.GlTexture;
import me.cortex.voxy.client.core.model.ModelStore; import me.cortex.voxy.client.core.model.ModelStore;
import me.cortex.voxy.client.core.rendering.Viewport; import me.cortex.voxy.client.core.rendering.Viewport;
import me.cortex.voxy.client.core.rendering.section.geometry.IGeometryData; import me.cortex.voxy.client.core.rendering.section.geometry.IGeometryData;
@@ -17,10 +16,10 @@ public abstract class AbstractSectionRenderer <T extends Viewport<T>, J extends
this.modelStore = modelStore; this.modelStore = modelStore;
} }
public abstract void renderOpaque(T viewport, GlTexture depthBoundTexture); public abstract void renderOpaque(T viewport);
public abstract void buildDrawCalls(T viewport); public abstract void buildDrawCalls(T viewport);
public abstract void renderTemporal(T viewport, GlTexture depthBoundTexture); public abstract void renderTemporal(T viewport);
public abstract void renderTranslucent(T viewport, GlTexture depthBoundTexture); public abstract void renderTranslucent(T viewport);
public abstract T createViewport(); public abstract T createViewport();
public abstract void free(); public abstract void free();

View File

@@ -2,17 +2,18 @@ package me.cortex.voxy.client.core.rendering.section;
import me.cortex.voxy.client.RenderStatistics; import me.cortex.voxy.client.RenderStatistics;
import me.cortex.voxy.client.core.AbstractRenderPipeline;
import me.cortex.voxy.client.core.gl.Capabilities; import me.cortex.voxy.client.core.gl.Capabilities;
import me.cortex.voxy.client.core.gl.GlBuffer; import me.cortex.voxy.client.core.gl.GlBuffer;
import me.cortex.voxy.client.core.gl.GlTexture; import me.cortex.voxy.client.core.gl.GlVertexArray;
import me.cortex.voxy.client.core.gl.shader.Shader; import me.cortex.voxy.client.core.gl.shader.Shader;
import me.cortex.voxy.client.core.gl.shader.ShaderLoader;
import me.cortex.voxy.client.core.gl.shader.ShaderType; import me.cortex.voxy.client.core.gl.shader.ShaderType;
import me.cortex.voxy.client.core.model.ModelStore; import me.cortex.voxy.client.core.model.ModelStore;
import me.cortex.voxy.client.core.rendering.section.geometry.BasicSectionGeometryData; import me.cortex.voxy.client.core.rendering.section.geometry.BasicSectionGeometryData;
import me.cortex.voxy.client.core.rendering.util.LightMapHelper;
import me.cortex.voxy.client.core.rendering.RenderService;
import me.cortex.voxy.client.core.rendering.util.SharedIndexBuffer;
import me.cortex.voxy.client.core.rendering.util.DownloadStream; import me.cortex.voxy.client.core.rendering.util.DownloadStream;
import me.cortex.voxy.client.core.rendering.util.LightMapHelper;
import me.cortex.voxy.client.core.rendering.util.SharedIndexBuffer;
import me.cortex.voxy.client.core.rendering.util.UploadStream; import me.cortex.voxy.client.core.rendering.util.UploadStream;
import me.cortex.voxy.common.Logger; import me.cortex.voxy.common.Logger;
import me.cortex.voxy.common.world.WorldEngine; import me.cortex.voxy.common.world.WorldEngine;
@@ -34,7 +35,6 @@ import static org.lwjgl.opengl.GL40C.GL_DRAW_INDIRECT_BUFFER;
import static org.lwjgl.opengl.GL42.glMemoryBarrier; import static org.lwjgl.opengl.GL42.glMemoryBarrier;
import static org.lwjgl.opengl.GL43.*; import static org.lwjgl.opengl.GL43.*;
import static org.lwjgl.opengl.GL45.glBindTextureUnit; import static org.lwjgl.opengl.GL45.glBindTextureUnit;
import static org.lwjgl.opengl.GL45.glClearNamedBufferData;
import static org.lwjgl.opengl.NVRepresentativeFragmentTest.GL_REPRESENTATIVE_FRAGMENT_TEST_NV; import static org.lwjgl.opengl.NVRepresentativeFragmentTest.GL_REPRESENTATIVE_FRAGMENT_TEST_NV;
//Uses MDIC to render the sections //Uses MDIC to render the sections
@@ -42,11 +42,8 @@ public class MDICSectionRenderer extends AbstractSectionRenderer<MDICViewport, B
private static final int TRANSLUCENT_OFFSET = 400_000;//in draw calls private static final int TRANSLUCENT_OFFSET = 400_000;//in draw calls
private static final int TEMPORAL_OFFSET = 500_000;//in draw calls private static final int TEMPORAL_OFFSET = 500_000;//in draw calls
private static final int STATISTICS_BUFFER_BINDING = 8; private static final int STATISTICS_BUFFER_BINDING = 8;
private final Shader terrainShader = Shader.make() private final Shader terrainShader;
.defineIf("DEBUG_RENDER", false) private final Shader translucentTerrainShader;
.add(ShaderType.VERTEX, "voxy:lod/gl46/quads2.vert")
.add(ShaderType.FRAGMENT, "voxy:lod/gl46/quads.frag")
.compile();
private final Shader commandGenShader = Shader.make() private final Shader commandGenShader = Shader.make()
.define("TRANSLUCENT_WRITE_BASE", 1024) .define("TRANSLUCENT_WRITE_BASE", 1024)
@@ -70,7 +67,8 @@ public class MDICSectionRenderer extends AbstractSectionRenderer<MDICViewport, B
.compile(); .compile();
private final Shader prefixSumShader = Shader.make() private final Shader prefixSumShader = Shader.make()
.add(ShaderType.COMPUTE, "voxy:util/prefixsum/inital3.comp") //Use subgroup prefix sum if possible otherwise use dodgy... slow prefix sum
.add(ShaderType.COMPUTE, Capabilities.INSTANCE.subgroup?"voxy:util/prefixsum/inital3.comp":"voxy:util/prefixsum/simple.comp")
.define("IO_BUFFER", 0) .define("IO_BUFFER", 0)
.compile(); .compile();
@@ -90,8 +88,53 @@ public class MDICSectionRenderer extends AbstractSectionRenderer<MDICViewport, B
//Statistics //Statistics
private final GlBuffer statisticsBuffer = new GlBuffer(1024).zero(); private final GlBuffer statisticsBuffer = new GlBuffer(1024).zero();
public MDICSectionRenderer(ModelStore modelStore, BasicSectionGeometryData geometryData) { private final AbstractRenderPipeline pipeline;
public MDICSectionRenderer(AbstractRenderPipeline pipeline, ModelStore modelStore, BasicSectionGeometryData geometryData) {
super(modelStore, geometryData); super(modelStore, geometryData);
this.pipeline = pipeline;
//The pipeline can be used to transform the renderer in abstract ways
String vertex = ShaderLoader.parse("voxy:lod/gl46/quads2.vert");
String taa = pipeline.taaFunction("taaShift");
if (taa != null) {
vertex += "\n"+taa;//inject it at the end
}
var builder = Shader.make()
.defineIf("TAA_PATCH", taa != null)
.defineIf("DEBUG_RENDER", false)
.addSource(ShaderType.VERTEX, vertex);
String frag = ShaderLoader.parse("voxy:lod/gl46/quads.frag");
String opaqueFrag = pipeline.patchOpaqueShader(this, frag);
opaqueFrag = opaqueFrag==null?frag:opaqueFrag;
//TODO: find a more robust/nicer way todo this
this.terrainShader = tryCompilePatchedOrNormal(builder, opaqueFrag, frag);
String translucentFrag = pipeline.patchTranslucentShader(this, frag);
if (translucentFrag != null) {
this.translucentTerrainShader = tryCompilePatchedOrNormal(builder, translucentFrag, frag);
} else {
this.translucentTerrainShader = this.terrainShader;
}
}
private static Shader tryCompilePatchedOrNormal(Shader.Builder<?> builder, String shader, String original) {
boolean patched = shader != original;//This is the correct comparison type (reference)
try {
return builder.clone()
.defineIf("PATCHED_SHADER", patched)
.addSource(ShaderType.FRAGMENT, shader)
.compile();
} catch (RuntimeException e) {
if (patched) {
Logger.error("Failed to compile shader patch, using normal pipeline to prevent errors", e);
return tryCompilePatchedOrNormal(builder, original, original);
} else {
throw e;
}
}
} }
private void uploadUniformBuffer(MDICViewport viewport) { private void uploadUniformBuffer(MDICViewport viewport) {
@@ -114,28 +157,30 @@ public class MDICSectionRenderer extends AbstractSectionRenderer<MDICViewport, B
} }
private void bindRenderingBuffers(MDICViewport viewport, GlTexture depthBoundTexture) { private void bindRenderingBuffers(MDICViewport viewport) {
glBindBufferBase(GL_UNIFORM_BUFFER, 0, this.uniform.id); glBindBufferBase(GL_UNIFORM_BUFFER, 0, this.uniform.id);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, this.geometryManager.getGeometryBuffer().id); glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, this.geometryManager.getGeometryBuffer().id);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, this.geometryManager.getMetadataBuffer().id); glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, this.geometryManager.getMetadataBuffer().id);
this.modelStore.bind(3, 4, 0); this.modelStore.bind(3, 4, 0);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 5, viewport.positionScratchBuffer.id); glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 5, viewport.positionScratchBuffer.id);
LightMapHelper.bind(1); LightMapHelper.bind(1);
glBindTextureUnit(2, depthBoundTexture.id); glBindTextureUnit(2, viewport.depthBoundingBuffer.getDepthTex().id);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, SharedIndexBuffer.INSTANCE.id()); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, SharedIndexBuffer.INSTANCE.id());
glBindBuffer(GL_DRAW_INDIRECT_BUFFER, viewport.drawCallBuffer.id); glBindBuffer(GL_DRAW_INDIRECT_BUFFER, viewport.drawCallBuffer.id);
glBindBuffer(GL_PARAMETER_BUFFER_ARB, viewport.drawCountCallBuffer.id); glBindBuffer(GL_PARAMETER_BUFFER_ARB, viewport.drawCountCallBuffer.id);
} }
private void renderTerrain(MDICViewport viewport, GlTexture depthBoundTexture, long indirectOffset, long drawCountOffset, int maxDrawCount) { private void renderTerrain(MDICViewport viewport, long indirectOffset, long drawCountOffset, int maxDrawCount) {
//RenderLayer.getCutoutMipped().startDrawing(); //RenderLayer.getCutoutMipped().startDrawing();
glDisable(GL_CULL_FACE); glDisable(GL_CULL_FACE);
glEnable(GL_DEPTH_TEST); glEnable(GL_DEPTH_TEST);
this.terrainShader.bind(); this.terrainShader.bind();
glBindVertexArray(RenderService.STATIC_VAO);//Needs to be before binding glBindVertexArray(GlVertexArray.STATIC_VAO);//Needs to be before binding
this.bindRenderingBuffers(viewport, depthBoundTexture); this.pipeline.setupAndBindOpaque(viewport);
this.bindRenderingBuffers(viewport);
glMemoryBarrier(GL_COMMAND_BARRIER_BIT|GL_SHADER_STORAGE_BARRIER_BIT);//Barrier everything is needed glMemoryBarrier(GL_COMMAND_BARRIER_BIT|GL_SHADER_STORAGE_BARRIER_BIT);//Barrier everything is needed
glProvokingVertex(GL_FIRST_VERTEX_CONVENTION); glProvokingVertex(GL_FIRST_VERTEX_CONVENTION);
@@ -152,25 +197,27 @@ public class MDICSectionRenderer extends AbstractSectionRenderer<MDICViewport, B
} }
@Override @Override
public void renderOpaque(MDICViewport viewport, GlTexture dbt) { public void renderOpaque(MDICViewport viewport) {
if (this.geometryManager.getSectionCount() == 0) return; if (this.geometryManager.getSectionCount() == 0) return;
this.uploadUniformBuffer(viewport); this.uploadUniformBuffer(viewport);
this.renderTerrain(viewport, dbt, 0, 4*3, Math.min((int)(this.geometryManager.getSectionCount()*4.4+128), 400_000)); this.renderTerrain(viewport, 0, 4*3, Math.min((int)(this.geometryManager.getSectionCount()*4.4+128), 400_000));
} }
@Override @Override
public void renderTranslucent(MDICViewport viewport, GlTexture depthBoundTexture) { public void renderTranslucent(MDICViewport viewport) {
if (this.geometryManager.getSectionCount() == 0) return; if (this.geometryManager.getSectionCount() == 0) return;
glEnable(GL_BLEND); glEnable(GL_BLEND);
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_CULL_FACE); glDisable(GL_CULL_FACE);
glEnable(GL_DEPTH_TEST); glEnable(GL_DEPTH_TEST);
this.terrainShader.bind(); this.translucentTerrainShader.bind();
glBindVertexArray(RenderService.STATIC_VAO);//Needs to be before binding glBindVertexArray(GlVertexArray.STATIC_VAO);//Needs to be before binding
this.bindRenderingBuffers(viewport, depthBoundTexture); this.pipeline.setupAndBindTranslucent(viewport);
this.bindRenderingBuffers(viewport);
glMemoryBarrier(GL_COMMAND_BARRIER_BIT|GL_SHADER_STORAGE_BARRIER_BIT);//Barrier everything is needed glMemoryBarrier(GL_COMMAND_BARRIER_BIT|GL_SHADER_STORAGE_BARRIER_BIT);//Barrier everything is needed
glProvokingVertex(GL_FIRST_VERTEX_CONVENTION); glProvokingVertex(GL_FIRST_VERTEX_CONVENTION);
@@ -209,7 +256,7 @@ public class MDICSectionRenderer extends AbstractSectionRenderer<MDICViewport, B
if (Capabilities.INSTANCE.repFragTest) { if (Capabilities.INSTANCE.repFragTest) {
glEnable(GL_REPRESENTATIVE_FRAGMENT_TEST_NV); glEnable(GL_REPRESENTATIVE_FRAGMENT_TEST_NV);
} }
glBindVertexArray(RenderService.STATIC_VAO); glBindVertexArray(GlVertexArray.STATIC_VAO);
glBindBufferBase(GL_UNIFORM_BUFFER, 0, this.uniform.id); glBindBufferBase(GL_UNIFORM_BUFFER, 0, this.uniform.id);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, this.geometryManager.getMetadataBuffer().id); glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, this.geometryManager.getMetadataBuffer().id);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, viewport.visibilityBuffer.id); glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, viewport.visibilityBuffer.id);
@@ -290,10 +337,10 @@ public class MDICSectionRenderer extends AbstractSectionRenderer<MDICViewport, B
} }
@Override @Override
public void renderTemporal(MDICViewport viewport, GlTexture dbt) { public void renderTemporal(MDICViewport viewport) {
if (this.geometryManager.getSectionCount() == 0) return; if (this.geometryManager.getSectionCount() == 0) return;
//Render temporal //Render temporal
this.renderTerrain(viewport, dbt, TEMPORAL_OFFSET*5*4, 4*5, Math.min(this.geometryManager.getSectionCount(), 100_000)); this.renderTerrain(viewport, TEMPORAL_OFFSET*5*4, 4*5, Math.min(this.geometryManager.getSectionCount(), 100_000));
} }
@Override @Override
@@ -309,6 +356,9 @@ public class MDICSectionRenderer extends AbstractSectionRenderer<MDICViewport, B
@Override @Override
public void free() { public void free() {
if (this.terrainShader != this.translucentTerrainShader) {
this.translucentTerrainShader.free();
}
this.uniform.free(); this.uniform.free();
this.distanceCountBuffer.free(); this.distanceCountBuffer.free();
this.terrainShader.free(); this.terrainShader.free();

View File

@@ -3,13 +3,12 @@ package me.cortex.voxy.client.core.rendering.section;
import me.cortex.voxy.client.core.gl.GlBuffer; import me.cortex.voxy.client.core.gl.GlBuffer;
import me.cortex.voxy.client.core.rendering.Viewport; import me.cortex.voxy.client.core.rendering.Viewport;
import me.cortex.voxy.client.core.rendering.hierachical.HierarchicalOcclusionTraverser; import me.cortex.voxy.client.core.rendering.hierachical.HierarchicalOcclusionTraverser;
import me.cortex.voxy.client.core.rendering.util.HiZBuffer;
public class MDICViewport extends Viewport<MDICViewport> { public class MDICViewport extends Viewport<MDICViewport> {
public final GlBuffer drawCountCallBuffer = new GlBuffer(1024).zero(); public final GlBuffer drawCountCallBuffer = new GlBuffer(1024).zero();
public final GlBuffer drawCallBuffer = new GlBuffer(5*4*(400_000+100_000+100_000)).zero();//400k draw calls public final GlBuffer drawCallBuffer = new GlBuffer(5*4*(400_000+100_000+100_000)).zero();//400k draw calls
public final GlBuffer positionScratchBuffer = new GlBuffer(8*400000).zero();//400k positions public final GlBuffer positionScratchBuffer = new GlBuffer(8*400000).zero();//400k positions
public final GlBuffer indirectLookupBuffer = new GlBuffer(HierarchicalOcclusionTraverser.MAX_QUEUE_SIZE *4+4); public final GlBuffer indirectLookupBuffer = new GlBuffer(HierarchicalOcclusionTraverser.MAX_QUEUE_SIZE *4+4);//In theory, this could be global/not unique to the viewport
public final GlBuffer visibilityBuffer; public final GlBuffer visibilityBuffer;
public MDICViewport(int maxSectionCount) { public MDICViewport(int maxSectionCount) {

View File

@@ -0,0 +1,231 @@
package me.cortex.voxy.client.core.rendering.section;
import me.cortex.voxy.client.RenderStatistics;
import me.cortex.voxy.client.core.AbstractRenderPipeline;
import me.cortex.voxy.client.core.gl.Capabilities;
import me.cortex.voxy.client.core.gl.GlBuffer;
import me.cortex.voxy.client.core.gl.GlTexture;
import me.cortex.voxy.client.core.gl.GlVertexArray;
import me.cortex.voxy.client.core.gl.shader.Shader;
import me.cortex.voxy.client.core.gl.shader.ShaderType;
import me.cortex.voxy.client.core.model.ModelStore;
import me.cortex.voxy.client.core.rendering.section.geometry.BasicSectionGeometryData;
import me.cortex.voxy.client.core.rendering.util.DownloadStream;
import me.cortex.voxy.client.core.rendering.util.LightMapHelper;
import me.cortex.voxy.client.core.rendering.util.SharedIndexBuffer;
import me.cortex.voxy.client.core.rendering.util.UploadStream;
import me.cortex.voxy.common.Logger;
import me.cortex.voxy.common.world.WorldEngine;
import org.joml.Matrix4f;
import org.lwjgl.system.MemoryUtil;
import java.util.List;
import static me.cortex.voxy.client.core.gl.EXTMeshShader.glDrawMeshTasksIndirectEXT;
import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.opengl.GL11C.GL_UNSIGNED_INT;
import static org.lwjgl.opengl.GL15.GL_ELEMENT_ARRAY_BUFFER;
import static org.lwjgl.opengl.GL15.glBindBuffer;
import static org.lwjgl.opengl.GL30.glBindBufferBase;
import static org.lwjgl.opengl.GL30.glBindVertexArray;
import static org.lwjgl.opengl.GL30C.GL_R32UI;
import static org.lwjgl.opengl.GL30C.GL_RED_INTEGER;
import static org.lwjgl.opengl.GL31.GL_UNIFORM_BUFFER;
import static org.lwjgl.opengl.GL33.glBindSampler;
import static org.lwjgl.opengl.GL40C.GL_DRAW_INDIRECT_BUFFER;
import static org.lwjgl.opengl.GL42.glMemoryBarrier;
import static org.lwjgl.opengl.GL43.*;
import static org.lwjgl.opengl.GL45.*;
import static org.lwjgl.opengl.NVRepresentativeFragmentTest.GL_REPRESENTATIVE_FRAGMENT_TEST_NV;
//Uses MDIC to render the sections
public class MeshEXTSectionRenderer extends AbstractSectionRenderer<MeshViewport, BasicSectionGeometryData> {
private static final int STATISTICS_BUFFER_BINDING = 8;
private final Shader terrainShader = Shader.make()
.define("MESH_SIZE", 32)//16
.defineIf("HAS_STATISTICS", RenderStatistics.enabled)
.defineIf("STATISTICS_BUFFER_BINDING", RenderStatistics.enabled, STATISTICS_BUFFER_BINDING)
.add(ShaderType.TASK, "voxy:lod/meshext/task.glsl")
.add(ShaderType.MESH, "voxy:lod/meshext/mesh.glsl")
.add(ShaderType.FRAGMENT, "voxy:lod/meshext/frag.glsl")
.compile();
private final Shader cullShader = Shader.make()
.add(ShaderType.VERTEX, "voxy:lod/gl46/cull/raster.vert")
.add(ShaderType.FRAGMENT, "voxy:lod/gl46/cull/raster.frag")
.compile();
private final GlBuffer uniform = new GlBuffer(1024).zero();
private final GlBuffer cullAndMeshDrawCommand = new GlBuffer(8*4).zero();//TODO: this needs tobe in the viewport
//Statistics
private final GlBuffer statisticsBuffer = new GlBuffer(1024).zero();
private final AbstractRenderPipeline pipeline;
public MeshEXTSectionRenderer(AbstractRenderPipeline pipeline, ModelStore modelStore, BasicSectionGeometryData geometryData) {
super(modelStore, geometryData);
this.pipeline = pipeline;
glClearNamedBufferSubData(this.cullAndMeshDrawCommand.id, GL_R32UI,0, 4, GL_RED_INTEGER, GL_UNSIGNED_INT, new int[]{6*2*3});//count
glClearNamedBufferSubData(this.cullAndMeshDrawCommand.id, GL_R32UI,8, 4, GL_RED_INTEGER, GL_UNSIGNED_INT, new int[]{(1<<16)*6*2});//firstIndex
glClearNamedBufferSubData(this.cullAndMeshDrawCommand.id, GL_R32UI,5*4+4, 4, GL_RED_INTEGER, GL_UNSIGNED_INT, new int[]{1});//y
glClearNamedBufferSubData(this.cullAndMeshDrawCommand.id, GL_R32UI,5*4+8, 4, GL_RED_INTEGER, GL_UNSIGNED_INT, new int[]{1});//z
}
private void uploadUniformBuffer(MeshViewport viewport) {
long ptr = UploadStream.INSTANCE.upload(this.uniform, 0, 1024);
var mat = new Matrix4f(viewport.MVP);
mat.translate(-viewport.innerTranslation.x, -viewport.innerTranslation.y, -viewport.innerTranslation.z);
mat.getToAddress(ptr); ptr += 4*4*4;
viewport.section.getToAddress(ptr); ptr += 4*3;
if (viewport.frameId<0) {
Logger.error("Frame ID negative, this will cause things to break, wrapping around");
viewport.frameId &= 0x7fffffff;
}
MemoryUtil.memPutInt(ptr, viewport.frameId&0x7fffffff); ptr += 4;
viewport.innerTranslation.getToAddress(ptr); ptr += 4*3;
ptr += 4;// padd
MemoryUtil.memPutFloat(ptr, viewport.width); ptr += 4;
MemoryUtil.memPutFloat(ptr, viewport.height); ptr += 4;
UploadStream.INSTANCE.commit();
}
private void bindRenderingBuffers(MeshViewport viewport) {
glBindBufferBase(GL_UNIFORM_BUFFER, 0, this.uniform.id);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, viewport.getRenderList().id);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, this.geometryManager.getMetadataBuffer().id);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 3, viewport.visibilityBuffer.id);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 4, this.geometryManager.getGeometryBuffer().id);
this.modelStore.bind(5, 6, 0);
LightMapHelper.bind(1);
glBindTextureUnit(2, viewport.depthBoundingBuffer.getDepthTex().id);
glBindBuffer(GL_DRAW_INDIRECT_BUFFER, this.cullAndMeshDrawCommand.id);
if (RenderStatistics.enabled) {
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, STATISTICS_BUFFER_BINDING, this.statisticsBuffer.id);
}
}
private void renderTerrain(MeshViewport viewport) {
//RenderLayer.getCutoutMipped().startDrawing();
glDisable(GL_CULL_FACE);
glEnable(GL_DEPTH_TEST);
this.terrainShader.bind();
this.pipeline.setupAndBindOpaque(viewport);
this.bindRenderingBuffers(viewport);
glMemoryBarrier(GL_COMMAND_BARRIER_BIT|GL_SHADER_STORAGE_BARRIER_BIT);//Barrier everything is needed
glDrawMeshTasksIndirectEXT(20);
glEnable(GL_CULL_FACE);
glBindSampler(0, 0);
glBindTextureUnit(0, 0);
glBindSampler(1, 0);
glBindTextureUnit(1, 0);
//RenderLayer.getCutoutMipped().endDrawing();
}
@Override
public void renderOpaque(MeshViewport viewport) {
if (this.geometryManager.getSectionCount() == 0) return;
this.uploadUniformBuffer(viewport);
this.renderTerrain(viewport);
//We need todo the statistics here as rastering is part of them, download then clear
if (RenderStatistics.enabled) {
DownloadStream.INSTANCE.download(this.statisticsBuffer, down->{
final int LAYERS = WorldEngine.MAX_LOD_LAYER+1;
for (int i = 0; i < LAYERS; i++) {
RenderStatistics.visibleSections[i] = MemoryUtil.memGetInt(down.address+i*4L);
}
for (int i = 0; i < LAYERS; i++) {
RenderStatistics.quadCount[i] = MemoryUtil.memGetInt(down.address+LAYERS*4L+i*4L);
}
});
this.statisticsBuffer.zero();
}
}
@Override
public void renderTranslucent(MeshViewport viewport) {
return;
}
@Override
public void buildDrawCalls(MeshViewport viewport) {
if (this.geometryManager.getSectionCount() == 0) return;
this.uploadUniformBuffer(viewport);
//Can do a sneeky trick, since the sectionRenderList is a list to things to render, it invokes the culler
// which only marks visible sections
{//Test occlusion
glCopyNamedBufferSubData(viewport.getRenderList().id, this.cullAndMeshDrawCommand.id, 0, 4, 4);//Copy counts to the draw buffer
glCopyNamedBufferSubData(viewport.getRenderList().id, this.cullAndMeshDrawCommand.id, 0, 20, 4);//Copy counts to the draw buffer
this.cullShader.bind();
if (Capabilities.INSTANCE.repFragTest) {
glEnable(GL_REPRESENTATIVE_FRAGMENT_TEST_NV);
}
glBindVertexArray(GlVertexArray.STATIC_VAO);
glBindBufferBase(GL_UNIFORM_BUFFER, 0, this.uniform.id);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, this.geometryManager.getMetadataBuffer().id);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, viewport.visibilityBuffer.id);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 3, viewport.getRenderList().id);
glBindBuffer(GL_DRAW_INDIRECT_BUFFER, this.cullAndMeshDrawCommand.id);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, SharedIndexBuffer.INSTANCE.id());
glEnable(GL_DEPTH_TEST);
glColorMask(false, false, false, false);
glDepthMask(false);
glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT|GL_COMMAND_BARRIER_BIT);
glDrawElementsIndirect(GL_TRIANGLES, GL_UNSIGNED_BYTE, 0);
glDepthMask(true);
glColorMask(true, true, true, true);
glDisable(GL_DEPTH_TEST);
if (Capabilities.INSTANCE.repFragTest) {
glDisable(GL_REPRESENTATIVE_FRAGMENT_TEST_NV);
}
}
}
@Override
public void renderTemporal(MeshViewport viewport) {
return;
}
@Override
public void addDebug(List<String> lines) {
super.addDebug(lines);
//lines.add("SC/GS: " + this.geometryManager.getSectionCount() + "/" + (this.geometryManager.getGeometryUsed()/(1024*1024)));//section count/geometry size (MB)
}
@Override
public MeshViewport createViewport() {
return new MeshViewport(this.geometryManager.getMaxSectionCount());
}
@Override
public void free() {
this.cullAndMeshDrawCommand.free();
this.uniform.free();
this.terrainShader.free();
this.cullShader.free();
this.statisticsBuffer.free();
}
}

View File

@@ -0,0 +1,26 @@
package me.cortex.voxy.client.core.rendering.section;
import me.cortex.voxy.client.core.gl.GlBuffer;
import me.cortex.voxy.client.core.rendering.Viewport;
import me.cortex.voxy.client.core.rendering.hierachical.HierarchicalOcclusionTraverser;
public class MeshViewport extends Viewport<MeshViewport> {
public final GlBuffer indirectLookupBuffer = new GlBuffer(HierarchicalOcclusionTraverser.MAX_QUEUE_SIZE *4+4);
public final GlBuffer visibilityBuffer;
public MeshViewport(int maxSectionCount) {
this.visibilityBuffer = new GlBuffer(maxSectionCount*4L);
}
@Override
protected void delete0() {
super.delete0();
this.visibilityBuffer.free();
this.indirectLookupBuffer.free();
}
@Override
public GlBuffer getRenderList() {
return this.indirectLookupBuffer;
}
}

View File

@@ -107,12 +107,14 @@ public class BasicAsyncGeometryManager implements IGeometryManager {
private SectionMeta createMeta(BuiltSection section) { private SectionMeta createMeta(BuiltSection section) {
if ((section.geometryBuffer.size%GEOMETRY_ELEMENT_SIZE)!=0) throw new IllegalStateException(); if ((section.geometryBuffer.size%GEOMETRY_ELEMENT_SIZE)!=0) throw new IllegalStateException();
int size = (int) (section.geometryBuffer.size/GEOMETRY_ELEMENT_SIZE); int size = (int) (section.geometryBuffer.size/GEOMETRY_ELEMENT_SIZE);
//clamp size upwards
int upsized = (size+1023)&~1023;
//Address //Address
int addr = (int)this.allocationHeap.alloc(size); int addr = (int)this.allocationHeap.alloc(upsized);
if (addr == -1) { if (addr == -1) {
throw new IllegalStateException("Geometry OOM"); throw new IllegalStateException("Geometry OOM. requested allocation size (in elements): " + size + ", Heap size at top remaining: " + (this.allocationHeap.getLimit()-this.allocationHeap.getSize()) + ", used elements: " + this.usedCapacity);
} }
this.usedCapacity += size; this.usedCapacity += upsized;
//Create upload //Create upload
if (this.heapUploads.put(addr, section.geometryBuffer) != null) { if (this.heapUploads.put(addr, section.geometryBuffer) != null) {
throw new IllegalStateException("Addr: " + addr); throw new IllegalStateException("Addr: " + addr);

View File

@@ -1,6 +1,14 @@
package me.cortex.voxy.client.core.rendering.section.geometry; package me.cortex.voxy.client.core.rendering.section.geometry;
import me.cortex.voxy.client.core.gl.Capabilities;
import me.cortex.voxy.client.core.gl.GlBuffer; import me.cortex.voxy.client.core.gl.GlBuffer;
import me.cortex.voxy.common.Logger;
import me.cortex.voxy.common.util.ThreadUtils;
import static org.lwjgl.opengl.ARBSparseBuffer.*;
import static org.lwjgl.opengl.GL11C.*;
import static org.lwjgl.opengl.GL15C.GL_ARRAY_BUFFER;
import static org.lwjgl.opengl.GL15C.glBindBuffer;
public class BasicSectionGeometryData implements IGeometryData { public class BasicSectionGeometryData implements IGeometryData {
public static final int SECTION_METADATA_SIZE = 32; public static final int SECTION_METADATA_SIZE = 32;
@@ -17,7 +25,46 @@ public class BasicSectionGeometryData implements IGeometryData {
if ((geometryCapacity%8)!=0) { if ((geometryCapacity%8)!=0) {
throw new IllegalStateException(); throw new IllegalStateException();
} }
this.geometryBuffer = new GlBuffer(geometryCapacity); long start = System.currentTimeMillis();
String msg = "Creating and zeroing " + (geometryCapacity/(1024*1024)) + "MB geometry buffer";
if (Capabilities.INSTANCE.canQueryGpuMemory) {
msg += " driver states " + (Capabilities.INSTANCE.getFreeDedicatedGpuMemory()/(1024*1024)) + "MB of free memory";
}
Logger.info(msg);
Logger.info("if your game crashes/exits here without any other log message, try manually decreasing the geometry capacity");
glGetError();//Clear any errors
GlBuffer buffer = null;
if (!(Capabilities.INSTANCE.isNvidia && ThreadUtils.isWindows)) {
buffer = new GlBuffer(geometryCapacity, false);//Only do this if we are not on nvidia
//TODO: FIXME: TEST, see if the issue is that we are trying to zero the entire buffer, try only zeroing increments
// or dont zero it at all
} else {
Logger.info("Running on windows nvidia, using workaround sparse buffer allocation");
}
int error = glGetError();
if (error != GL_NO_ERROR || buffer == null) {
if ((buffer == null || error == GL_OUT_OF_MEMORY) && Capabilities.INSTANCE.sparseBuffer) {
if (buffer != null) {
Logger.error("Failed to allocate geometry buffer, attempting workaround with sparse buffers");
buffer.free();
}
buffer = new GlBuffer(geometryCapacity, GL_SPARSE_STORAGE_BIT_ARB);
glBindBuffer(GL_ARRAY_BUFFER, buffer.id);
glBufferPageCommitmentARB(GL_ARRAY_BUFFER, 0, geometryCapacity, true);
glBindBuffer(GL_ARRAY_BUFFER, 0);
//buffer.zero();
error = glGetError();
if (error != GL_NO_ERROR) {
buffer.free();
throw new IllegalStateException("Unable to allocate geometry buffer using workaround, got gl error " + error);
}
} else {
throw new IllegalStateException("Unable to allocate geometry buffer, got gl error " + error);
}
}
this.geometryBuffer = buffer;
long delta = System.currentTimeMillis() - start;
Logger.info("Successfully allocated the geometry buffer in " + delta + "ms");
} }
public GlBuffer getGeometryBuffer() { public GlBuffer getGeometryBuffer() {
@@ -47,6 +94,31 @@ public class BasicSectionGeometryData implements IGeometryData {
@Override @Override
public void free() { public void free() {
this.sectionMetadataBuffer.free(); this.sectionMetadataBuffer.free();
long gpuMemory = 0;
if (Capabilities.INSTANCE.canQueryGpuMemory) {
glFinish();
gpuMemory = Capabilities.INSTANCE.getFreeDedicatedGpuMemory();
}
glFinish();
this.geometryBuffer.free(); this.geometryBuffer.free();
glFinish();
if (Capabilities.INSTANCE.canQueryGpuMemory) {
long releaseSize = (long) (this.geometryBuffer.size()*0.75);//if gpu memory usage drops by 75% of the expected value assume we freed it
if (Capabilities.INSTANCE.getFreeDedicatedGpuMemory()-gpuMemory<=releaseSize) {
Logger.info("Attempting to wait for gpu memory to release");
long start = System.currentTimeMillis();
long TIMEOUT = 2500;
while (System.currentTimeMillis() - start > TIMEOUT) {//Wait up to 2.5 seconds for memory to release
glFinish();
if (Capabilities.INSTANCE.getFreeDedicatedGpuMemory() - gpuMemory > releaseSize) break;
}
if (Capabilities.INSTANCE.getFreeDedicatedGpuMemory() - gpuMemory <= releaseSize) {
Logger.warn("Failed to wait for gpu memory to be freed, this could indicate an issue with the driver");
}
}
}
} }
} }

View File

@@ -7,12 +7,8 @@ import org.lwjgl.system.MemoryUtil;
import java.util.function.Supplier; import java.util.function.Supplier;
import static org.lwjgl.opengl.GL15.glBindBuffer;
import static org.lwjgl.opengl.GL20.glUniform1i; import static org.lwjgl.opengl.GL20.glUniform1i;
import static org.lwjgl.opengl.GL20.glUseProgram;
import static org.lwjgl.opengl.GL30.glBindBufferBase; import static org.lwjgl.opengl.GL30.glBindBufferBase;
import static org.lwjgl.opengl.GL30.glUniform1ui;
import static org.lwjgl.opengl.GL31.GL_UNIFORM_BUFFER;
import static org.lwjgl.opengl.GL43.GL_SHADER_STORAGE_BUFFER; import static org.lwjgl.opengl.GL43.GL_SHADER_STORAGE_BUFFER;
import static org.lwjgl.opengl.GL43C.glDispatchCompute; import static org.lwjgl.opengl.GL43C.glDispatchCompute;

View File

@@ -0,0 +1,59 @@
package me.cortex.voxy.client.core.rendering.util;
import me.cortex.voxy.client.core.gl.GlFramebuffer;
import me.cortex.voxy.client.core.gl.GlTexture;
import org.lwjgl.system.MemoryStack;
import static org.lwjgl.opengl.ARBDirectStateAccess.nglClearNamedFramebufferfv;
import static org.lwjgl.opengl.GL11C.GL_DEPTH;
import static org.lwjgl.opengl.GL14.GL_DEPTH_COMPONENT24;
import static org.lwjgl.opengl.GL30C.*;
public class DepthFramebuffer {
private final int depthType;
private GlTexture depthBuffer;
public final GlFramebuffer framebuffer = new GlFramebuffer();
public DepthFramebuffer() {
this(GL_DEPTH_COMPONENT24);
}
public DepthFramebuffer(int depthType) {
this.depthType = depthType;
}
public void resize(int width, int height) {
if (this.depthBuffer == null || this.depthBuffer.getWidth() != width || this.depthBuffer.getHeight() != height) {
if (this.depthBuffer != null) {
this.depthBuffer.free();
}
this.depthBuffer = new GlTexture().store(this.depthType, 1, width, height);
this.framebuffer.bind(this.depthType == GL_DEPTH24_STENCIL8?GL_DEPTH_STENCIL_ATTACHMENT: GL_DEPTH_ATTACHMENT, this.depthBuffer).verify();
}
}
public void clear() {
this.clear(1.0f);
}
public void clear(float depth) {
try (var stack = MemoryStack.stackPush()) {
nglClearNamedFramebufferfv(this.framebuffer.id, GL_DEPTH, 0, stack.nfloat(depth));
}
}
public GlTexture getDepthTex() {
return this.depthBuffer;
}
public void free() {
this.framebuffer.free();
if (this.depthBuffer != null) {
this.depthBuffer.free();
}
}
public void bind() {
glBindFramebuffer(GL_FRAMEBUFFER, this.framebuffer.id);
}
}

View File

@@ -18,7 +18,7 @@ import static org.lwjgl.opengl.GL11.glFinish;
import static org.lwjgl.opengl.GL30C.GL_MAP_READ_BIT; import static org.lwjgl.opengl.GL30C.GL_MAP_READ_BIT;
import static org.lwjgl.opengl.GL42.GL_BUFFER_UPDATE_BARRIER_BIT; import static org.lwjgl.opengl.GL42.GL_BUFFER_UPDATE_BARRIER_BIT;
import static org.lwjgl.opengl.GL42.glMemoryBarrier; import static org.lwjgl.opengl.GL42.glMemoryBarrier;
import static org.lwjgl.opengl.GL44.*; import static org.lwjgl.opengl.GL44.GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT;
import static org.lwjgl.opengl.GL45.glCopyNamedBufferSubData; import static org.lwjgl.opengl.GL45.glCopyNamedBufferSubData;
public class DownloadStream { public class DownloadStream {
@@ -149,6 +149,21 @@ public class DownloadStream {
} }
//Synchonize force flushes everything //Synchonize force flushes everything
public void waitDiscard() {
glFinish();
var fence = new GlFence();
glFinish();
while (!fence.signaled())
Thread.onSpinWait();
fence.free();
while (!this.frames.isEmpty()) {
var frame = this.frames.pop();
while (!frame.fence.signaled()) Thread.onSpinWait();
frame.allocations.forEach(this.allocationArena::free);
frame.fence.free();
}
}
public void flushWaitClear() { public void flushWaitClear() {
glFinish(); glFinish();
this.tick(); this.tick();

View File

@@ -5,8 +5,6 @@ import it.unimi.dsi.fastutil.ints.IntArrayList;
import static org.lwjgl.opengl.GL11.*; import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.opengl.GL13.GL_ACTIVE_TEXTURE; import static org.lwjgl.opengl.GL13.GL_ACTIVE_TEXTURE;
import static org.lwjgl.opengl.GL13.glActiveTexture; import static org.lwjgl.opengl.GL13.glActiveTexture;
import static org.lwjgl.opengl.GL13C.GL_TEXTURE0;
import static org.lwjgl.opengl.GL33.glBindSampler;
public class GlStateCapture { public class GlStateCapture {
private final int[] capabilityIds; private final int[] capabilityIds;

View File

@@ -2,9 +2,9 @@ package me.cortex.voxy.client.core.rendering.util;
import me.cortex.voxy.client.core.gl.GlFramebuffer; import me.cortex.voxy.client.core.gl.GlFramebuffer;
import me.cortex.voxy.client.core.gl.GlTexture; import me.cortex.voxy.client.core.gl.GlTexture;
import me.cortex.voxy.client.core.gl.GlVertexArray;
import me.cortex.voxy.client.core.gl.shader.Shader; import me.cortex.voxy.client.core.gl.shader.Shader;
import me.cortex.voxy.client.core.gl.shader.ShaderType; import me.cortex.voxy.client.core.gl.shader.ShaderType;
import me.cortex.voxy.client.core.rendering.RenderService;
import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL11;
import static org.lwjgl.opengl.ARBDirectStateAccess.*; import static org.lwjgl.opengl.ARBDirectStateAccess.*;
@@ -17,7 +17,6 @@ import static org.lwjgl.opengl.GL33C.glDeleteSamplers;
import static org.lwjgl.opengl.GL33C.glSamplerParameteri; import static org.lwjgl.opengl.GL33C.glSamplerParameteri;
import static org.lwjgl.opengl.GL42C.GL_FRAMEBUFFER_BARRIER_BIT; import static org.lwjgl.opengl.GL42C.GL_FRAMEBUFFER_BARRIER_BIT;
import static org.lwjgl.opengl.GL42C.glMemoryBarrier; import static org.lwjgl.opengl.GL42C.glMemoryBarrier;
import static org.lwjgl.opengl.GL43C.glCopyImageSubData;
import static org.lwjgl.opengl.GL45C.glTextureBarrier; import static org.lwjgl.opengl.GL45C.glTextureBarrier;
public class HiZBuffer { public class HiZBuffer {
@@ -76,7 +75,7 @@ public class HiZBuffer {
} }
this.alloc(Integer.highestOneBit(width), Integer.highestOneBit(height)); this.alloc(Integer.highestOneBit(width), Integer.highestOneBit(height));
} }
glBindVertexArray(RenderService.STATIC_VAO); glBindVertexArray(GlVertexArray.STATIC_VAO);
int boundFB = GL11.glGetInteger(GL_DRAW_FRAMEBUFFER_BINDING); int boundFB = GL11.glGetInteger(GL_DRAW_FRAMEBUFFER_BINDING);
this.hiz.bind(); this.hiz.bind();
glBindFramebuffer(GL_FRAMEBUFFER, this.fb.id); glBindFramebuffer(GL_FRAMEBUFFER, this.fb.id);
@@ -128,6 +127,6 @@ public class HiZBuffer {
} }
public int getPackedLevels() { public int getPackedLevels() {
return ((Integer.numberOfTrailingZeros(this.width))<<16)|(Integer.numberOfTrailingZeros(this.height));//+1 return (this.width<<16)|this.height;//+1
} }
} }

View File

@@ -0,0 +1,148 @@
package me.cortex.voxy.client.core.rendering.util;
import me.cortex.voxy.client.core.gl.GlFramebuffer;
import me.cortex.voxy.client.core.gl.GlTexture;
import me.cortex.voxy.client.core.gl.GlVertexArray;
import me.cortex.voxy.client.core.gl.shader.Shader;
import me.cortex.voxy.client.core.gl.shader.ShaderType;
import org.lwjgl.opengl.GL11;
import static org.lwjgl.opengl.ARBDirectStateAccess.*;
import static org.lwjgl.opengl.ARBShaderImageLoadStore.GL_TEXTURE_FETCH_BARRIER_BIT;
import static org.lwjgl.opengl.GL11C.*;
import static org.lwjgl.opengl.GL30C.*;
import static org.lwjgl.opengl.GL33.glBindSampler;
import static org.lwjgl.opengl.GL33.glGenSamplers;
import static org.lwjgl.opengl.GL33C.glDeleteSamplers;
import static org.lwjgl.opengl.GL33C.glSamplerParameteri;
import static org.lwjgl.opengl.GL42C.*;
import static org.lwjgl.opengl.GL43C.glDispatchCompute;
import static org.lwjgl.opengl.GL45C.glTextureBarrier;
public class HiZBuffer2 {
private final Shader hizMip = Shader.make()
.add(ShaderType.COMPUTE, "voxy:hiz/hiz.comp")
.compile();
private final Shader hizInitial = Shader.make()
.add(ShaderType.VERTEX, "voxy:hiz/blit.vsh")
.add(ShaderType.FRAGMENT, "voxy:hiz/blit.fsh")
.define("OUTPUT_COLOUR")
.compile();
private final GlFramebuffer fb = new GlFramebuffer().name("HiZ");
private final int sampler = glGenSamplers();
private final int type;
private GlTexture texture;
private int levels;
private int width;
private int height;
public HiZBuffer2() {
this(GL_R32F);
}
public HiZBuffer2(int type) {
glNamedFramebufferDrawBuffer(this.fb.id, GL_COLOR_ATTACHMENT0);
this.type = type;
}
private void alloc(int width, int height) {
this.levels = (int)Math.ceil(Math.log(Math.max(width, height))/Math.log(2));
//We dont care about e.g. 1x1 size texture since you dont get meshlets that big to cover such a large area
//this.levels -= 1;//Arbitrary size, shinks the max level by alot and saves a significant amount of processing time
// (could probably increase it to be defined by a max meshlet coverage computation thing)
//GL_DEPTH_COMPONENT32F //Cant use this as it does not match the depth format of the provided depth buffer
this.texture = new GlTexture().store(this.type, this.levels, width, height).name("HiZ");
glTextureParameteri(this.texture.id, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
glTextureParameteri(this.texture.id, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTextureParameteri(this.texture.id, GL_TEXTURE_COMPARE_MODE, GL_NONE);
glTextureParameteri(this.texture.id, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTextureParameteri(this.texture.id, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glSamplerParameteri(this.sampler, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
glSamplerParameteri(this.sampler, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glSamplerParameteri(this.sampler, GL_TEXTURE_COMPARE_MODE, GL_NONE);
glSamplerParameteri(this.sampler, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glSamplerParameteri(this.sampler, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
this.width = width;
this.height = height;
this.fb.bind(GL_COLOR_ATTACHMENT0, this.texture, 0).verify();
}
public void buildMipChain(int srcDepthTex, int width, int height) {
if (this.width != Integer.highestOneBit(width) || this.height != Integer.highestOneBit(height)) {
if (this.texture != null) {
this.texture.free();
this.texture = null;
}
this.alloc(Integer.highestOneBit(width), Integer.highestOneBit(height));
}
{//Mip down to initial chain
int boundFB = GL11.glGetInteger(GL_DRAW_FRAMEBUFFER_BINDING);
glBindVertexArray(GlVertexArray.STATIC_VAO);
this.hizInitial.bind();
glBindFramebuffer(GL_FRAMEBUFFER, this.fb.id);
glDisable(GL_DEPTH_TEST);
glBindTextureUnit(0, srcDepthTex);
glBindSampler(0, this.sampler);
glUniform1i(0, 0);
glViewport(0, 0, this.width, this.height);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
glTextureBarrier();
glMemoryBarrier(GL_FRAMEBUFFER_BARRIER_BIT|GL_TEXTURE_FETCH_BARRIER_BIT);
glBindFramebuffer(GL_FRAMEBUFFER, boundFB);
glViewport(0, 0, width, height);
glBindVertexArray(0);
}
{//Compute based Mipping
this.hizMip.bind();
glUniform2f(0, 1f/this.width, 1f/this.height);
glBindTextureUnit(0, this.texture.id);
glBindSampler(0, this.sampler);
for (int i = 1; i < 7; i++) {
glBindImageTexture(i, this.texture.id, i, false, 0, GL_WRITE_ONLY, GL_R32F);
}
glDispatchCompute(this.width/64, this.height/64, 1);
glBindSampler(0, 0);
for (int i =0;i<7;i++)
glBindTextureUnit(i, 0);
}
}
public void free() {
this.fb.free();
if (this.texture != null) {
this.texture.free();
this.texture = null;
}
glDeleteSamplers(this.sampler);
this.hizInitial.free();
this.hizMip.free();
}
public int getHizTextureId() {
return this.texture.id;
}
public int getPackedLevels() {
return ((Integer.numberOfTrailingZeros(this.width))<<16)|(Integer.numberOfTrailingZeros(this.height));//+1
}
}

View File

@@ -1,20 +1,13 @@
package me.cortex.voxy.client.core.rendering.util; package me.cortex.voxy.client.core.rendering.util;
import me.cortex.voxy.client.core.gl.GlBuffer;
import me.cortex.voxy.client.core.gl.GlTexture;
import me.cortex.voxy.client.core.rendering.util.UploadStream;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
import org.lwjgl.system.MemoryUtil;
import static org.lwjgl.opengl.ARBUniformBufferObject.glBindBufferBase; import static org.lwjgl.opengl.GL33.glBindSampler;
import static org.lwjgl.opengl.GL11.glBindTexture;
import static org.lwjgl.opengl.GL33.*;
import static org.lwjgl.opengl.GL43.GL_SHADER_STORAGE_BUFFER;
import static org.lwjgl.opengl.GL45.glBindTextureUnit; import static org.lwjgl.opengl.GL45.glBindTextureUnit;
public class LightMapHelper { public class LightMapHelper {
public static void bind(int lightingIndex) { public static void bind(int lightingIndex) {
glBindSampler(lightingIndex, 0); glBindSampler(lightingIndex, 0);
glBindTextureUnit(lightingIndex, ((net.minecraft.client.texture.GlTexture)(MinecraftClient.getInstance().gameRenderer.getLightmapTextureManager().getGlTexture())).getGlId()); glBindTextureUnit(lightingIndex, ((net.minecraft.client.texture.GlTexture)(MinecraftClient.getInstance().gameRenderer.getLightmapTextureManager().getGlTextureView().texture())).getGlId());
} }
} }

View File

@@ -15,11 +15,8 @@ import static me.cortex.voxy.common.util.AllocationArena.SIZE_LIMIT;
import static org.lwjgl.opengl.ARBDirectStateAccess.glCopyNamedBufferSubData; import static org.lwjgl.opengl.ARBDirectStateAccess.glCopyNamedBufferSubData;
import static org.lwjgl.opengl.ARBMapBufferRange.*; import static org.lwjgl.opengl.ARBMapBufferRange.*;
import static org.lwjgl.opengl.GL11.glFinish; import static org.lwjgl.opengl.GL11.glFinish;
import static org.lwjgl.opengl.GL42.GL_UNIFORM_BARRIER_BIT;
import static org.lwjgl.opengl.GL42.glMemoryBarrier; import static org.lwjgl.opengl.GL42.glMemoryBarrier;
import static org.lwjgl.opengl.GL42C.GL_BUFFER_UPDATE_BARRIER_BIT; import static org.lwjgl.opengl.GL42C.GL_BUFFER_UPDATE_BARRIER_BIT;
import static org.lwjgl.opengl.GL43.GL_SHADER_STORAGE_BARRIER_BIT;
import static org.lwjgl.opengl.GL44.GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT;
import static org.lwjgl.opengl.GL44.GL_MAP_COHERENT_BIT; import static org.lwjgl.opengl.GL44.GL_MAP_COHERENT_BIT;
import static org.lwjgl.opengl.GL45C.glFlushMappedNamedBufferRange; import static org.lwjgl.opengl.GL45C.glFlushMappedNamedBufferRange;
@@ -44,6 +41,10 @@ public class UploadStream {
data.cpyTo(this.upload(buffer, destOffset, data.size)); data.cpyTo(this.upload(buffer, destOffset, data.size));
} }
public long uploadTo(GlBuffer buffer) {
return this.upload(buffer, 0, buffer.size());
}
public long upload(GlBuffer buffer, long destOffset, long size) { public long upload(GlBuffer buffer, long destOffset, long size) {
long addr = this.rawUploadAddress((int) size); long addr = this.rawUploadAddress((int) size);
@@ -105,14 +106,15 @@ public class UploadStream {
} }
public void commit() { public void commit() {
if (this.uploadList.isEmpty()) {
return;
}
if ((!USE_COHERENT)&&this.caddr != -1) { if ((!USE_COHERENT)&&this.caddr != -1) {
//Flush this allocation //Flush this allocation
glFlushMappedNamedBufferRange(this.uploadBuffer.id, this.caddr, this.offset); glFlushMappedNamedBufferRange(this.uploadBuffer.id, this.caddr, this.offset);
} }
if (this.uploadList.isEmpty()) {
return;
}
glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT); glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT);
//Execute all the copies //Execute all the copies
for (var entry : this.uploadList) { for (var entry : this.uploadList) {

View File

@@ -0,0 +1,165 @@
package me.cortex.voxy.client.core.util;
import it.unimi.dsi.fastutil.ints.IntArrayFIFOQueue;
import it.unimi.dsi.fastutil.objects.ObjectArrayFIFOQueue;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import me.cortex.voxy.client.core.gl.GlBuffer;
import me.cortex.voxy.client.core.rendering.util.DownloadStream;
import me.cortex.voxy.common.util.MemoryBuffer;
import me.cortex.voxy.common.util.Pair;
import me.cortex.voxy.common.util.TrackedObject;
import org.lwjgl.system.MemoryUtil;
import java.util.Arrays;
import java.util.function.Consumer;
import static org.lwjgl.opengl.ARBTimerQuery.GL_TIMESTAMP;
import static org.lwjgl.opengl.ARBTimerQuery.glQueryCounter;
import static org.lwjgl.opengl.GL11.glFinish;
import static org.lwjgl.opengl.GL11.glFlush;
import static org.lwjgl.opengl.GL15.glDeleteQueries;
import static org.lwjgl.opengl.GL15.glGenQueries;
import static org.lwjgl.opengl.GL15C.*;
import static org.lwjgl.opengl.GL33.glGetQueryObjecti64;
import static org.lwjgl.opengl.GL42.glMemoryBarrier;
import static org.lwjgl.opengl.GL44.GL_QUERY_RESULT_NO_WAIT;
import static org.lwjgl.opengl.GL45.glGetQueryBufferObjectui64v;
public class GPUTiming {
public static GPUTiming INSTANCE = new GPUTiming();
private final GlTimestampQuerySet timingSet = new GlTimestampQuerySet();
public void marker() {
this.timingSet.capture(0);
}
public void tick() {
this.timingSet.download((meta,data)->{
long current = data[0];
for (int i = 1; i < meta.length; i++) {
long next = data[i];
long delta = next - current;
//System.out.println(delta);
current = next;
}
});
this.timingSet.tick();
}
public void free() {
this.timingSet.free();
}
public interface TimingDataConsumer {
void accept(int[] metadata, long[] timings);
}
private static final class GlTimestampQuerySet extends TrackedObject {
private record InflightRequest(int[] queries, int[] meta, TimingDataConsumer callback) {
private boolean callbackIfReady(IntArrayFIFOQueue queryPool) {
boolean ready = glGetQueryObjecti(this.queries[this.queries.length-1], GL_QUERY_RESULT_AVAILABLE) == GL_TRUE;
if (!ready) {
return false;
}
long[] results = new long[this.queries.length];
for (int i = 0; i < this.queries.length; i++) {
results[i] = glGetQueryObjecti64(this.queries[i], GL_QUERY_RESULT);
queryPool.enqueue(this.queries[i]);
}
this.callback.accept(this.meta, results);
return true;
}
}
private final IntArrayFIFOQueue POOL = new IntArrayFIFOQueue();
private final ObjectArrayFIFOQueue<InflightRequest> INFLIGHT = new ObjectArrayFIFOQueue();
private final int[] queries = new int[64];
private final int[] metadata = new int[64];
private int index;
public void capture(int metadata) {
if (this.index > this.metadata.length) {
throw new IllegalStateException();
}
int slot = this.index++;
this.metadata[slot] = metadata;
int query = this.getQuery();
glQueryCounter(query, GL_TIMESTAMP);
this.queries[slot] = query;
}
public void download(TimingDataConsumer consumer) {
var queries = Arrays.copyOf(this.queries, this.index);
var metadata = Arrays.copyOf(this.metadata, this.index);
this.index = 0;
this.INFLIGHT.enqueue(new InflightRequest(queries, metadata, consumer));
}
public void tick() {
while (!INFLIGHT.isEmpty()) {
if (INFLIGHT.first().callbackIfReady(POOL)) {
INFLIGHT.dequeue();
} else {
break;
}
}
}
private int getQuery() {
if (POOL.isEmpty()) {
return glGenQueries();
} else {
return POOL.dequeueInt();
}
}
@Override
public void free() {
super.free0();
while (!POOL.isEmpty()) {
glDeleteQueries(POOL.dequeueInt());
}
while (!INFLIGHT.isEmpty()) {
glDeleteQueries(INFLIGHT.dequeue().queries);
}
}
}
/*
private static final class GlTimestampQuerySet extends TrackedObject {
private final int query = glGenQueries();
public final GlBuffer store;
public final int[] metadata;
public int index;
public GlTimestampQuerySet(int maxCount) {
this.store = new GlBuffer(maxCount*8L);
this.metadata = new int[maxCount];
}
public void capture(int metadata) {
if (this.index>this.metadata.length) {
throw new IllegalStateException();
}
int slot = this.index++;
this.metadata[slot] = metadata;
glQueryCounter(this.query, GL_TIMESTAMP);//This should be gpu side, so should be fast
glFinish();
glGetQueryBufferObjectui64v(this.query, this.store.id, GL_QUERY_RESULT_NO_WAIT, slot*8L);
glMemoryBarrier(-1);
}
public void download(TimingDataConsumer consumer) {
var meta = Arrays.copyOf(this.metadata, this.index);
this.index = 0;
//DownloadStream.INSTANCE.download(this.store, buffer->consumer.accept(meta, buffer));
}
@Override
public void free() {
super.free0();
glDeleteQueries(this.query);
this.store.free();
}
}*/
}

View File

@@ -1,11 +1,26 @@
package me.cortex.voxy.client.core.util; package me.cortex.voxy.client.core.util;
import me.cortex.voxy.client.core.VoxyRenderSystem;
import me.cortex.voxy.client.core.rendering.Viewport;
import net.caffeinemc.mods.sodium.client.render.chunk.ChunkRenderMatrices;
import net.caffeinemc.mods.sodium.client.util.FogParameters;
import net.fabricmc.loader.api.FabricLoader; import net.fabricmc.loader.api.FabricLoader;
import net.irisshaders.iris.Iris;
import net.irisshaders.iris.api.v0.IrisApi;
import net.irisshaders.iris.gl.IrisRenderSystem;
import net.irisshaders.iris.shadows.ShadowRenderer; import net.irisshaders.iris.shadows.ShadowRenderer;
import org.spongepowered.asm.mixin.Unique;
public class IrisUtil { public class IrisUtil {
private static final boolean IRIS_INSTALLED = FabricLoader.getInstance().isModLoaded("iris"); public record CapturedViewportParameters(ChunkRenderMatrices matrices, FogParameters parameters, double x, double y, double z) {
public Viewport<?> apply(VoxyRenderSystem vrs) {
return vrs.setupViewport(this.matrices, this.parameters, this.x, this.y, this.z);
}
}
public static CapturedViewportParameters CAPTURED_VIEWPORT_PARAMETERS;
public static final boolean IRIS_INSTALLED = FabricLoader.getInstance().isModLoaded("iris");
public static final boolean SHADER_SUPPORT = true;//System.getProperty("voxy.enableExperimentalIrisPipeline", "false").equalsIgnoreCase("true");
private static boolean irisShadowActive0() { private static boolean irisShadowActive0() {
@@ -15,4 +30,28 @@ public class IrisUtil {
public static boolean irisShadowActive() { public static boolean irisShadowActive() {
return IRIS_INSTALLED && irisShadowActive0(); return IRIS_INSTALLED && irisShadowActive0();
} }
public static void clearIrisSamplers() {
if (IRIS_INSTALLED) clearIrisSamplers0();
}
private static void clearIrisSamplers0() {
for (int i = 0; i < 16; i++) {
IrisRenderSystem.bindSamplerToUnit(i, 0);
}
}
private static boolean irisShaderPackEnabled0() {
return Iris.isPackInUseQuick();
}
public static boolean irisShaderPackEnabled() {
return IRIS_INSTALLED && irisShaderPackEnabled0();
}
public static void disableIrisShaders() {
if(IRIS_INSTALLED) disableIrisShaders0();
}
private static void disableIrisShaders0() {
IrisApi.getInstance().getConfig().setShadersEnabledAndApply(false);//Disable shaders
}
} }

View File

@@ -0,0 +1,5 @@
package me.cortex.voxy.client.iris;
public interface IGetIrisVoxyPipelineData {
IrisVoxyRenderPipelineData voxy$getPipelineData();
}

View File

@@ -0,0 +1,5 @@
package me.cortex.voxy.client.iris;
public interface IGetVoxyPatchData {
IrisShaderPatch voxy$getPatchData();
}

View File

@@ -0,0 +1,330 @@
package me.cortex.voxy.client.iris;
import com.google.gson.*;
import com.google.gson.annotations.JsonAdapter;
import it.unimi.dsi.fastutil.ints.Int2ObjectLinkedOpenHashMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import me.cortex.voxy.client.core.util.IrisUtil;
import me.cortex.voxy.common.Logger;
import net.irisshaders.iris.api.v0.IrisApi;
import net.irisshaders.iris.shaderpack.ShaderPack;
import net.irisshaders.iris.shaderpack.include.AbsolutePackPath;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.util.function.Function;
import java.util.function.IntSupplier;
import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.opengl.GL33.*;
import static org.lwjgl.opengl.GL40.glBlendFuncSeparatei;
public class IrisShaderPatch {
public static final int VERSION = ((IntSupplier)()->1).getAsInt();
public static final boolean IMPERSONATE_DISTANT_HORIZONS = System.getProperty("voxy.impersonateDHShader", "false").equalsIgnoreCase("true");
private static final class SSBODeserializer implements JsonDeserializer<Int2ObjectOpenHashMap<String>> {
@Override
public Int2ObjectOpenHashMap<String> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
Int2ObjectOpenHashMap<String> ret = new Int2ObjectOpenHashMap<>();
if (json==null) return null;
try {
for (var entry : json.getAsJsonObject().entrySet()) {
ret.put(Integer.parseInt(entry.getKey()), entry.getValue().getAsString());
}
} catch (Exception e) {
Logger.error(e);
}
return ret;
}
}
private static final class SamplerDeserializer implements JsonDeserializer<Object2ObjectLinkedOpenHashMap<String, String>> {
@Override
public Object2ObjectLinkedOpenHashMap<String, String> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
Object2ObjectLinkedOpenHashMap<String, String> ret = new Object2ObjectLinkedOpenHashMap<>();
if (json==null) return null;
try {
if (json.isJsonArray()) {
for (var entry : json.getAsJsonArray()) {
var name = entry.getAsString();
var type = "sampler2D";
if (name.matches("shadowtex")) {
type = "sampler2DShadow";
}
ret.put(name, type);
}
} else {
for (var entry : json.getAsJsonObject().entrySet()) {
String type = "sampler2D";
if (entry.getValue().isJsonNull()) {
if (entry.getKey().matches("shadowtex")) {
type = "sampler2DShadow";
}
} else {
type = entry.getValue().getAsString();
}
ret.put(entry.getKey(), type);
}
}
} catch (Exception e) {
Logger.error(e);
}
return ret;
}
}
public record BlendState(int buffer, boolean off, int sRBG, int dRGb, int sA, int dA) {
public static BlendState ALL_OFF = new BlendState(-1, true, 0,0,0,0);
}
private static final class BlendStateDeserializer implements JsonDeserializer<Int2ObjectMap<BlendState>> {
private static int parseType(String type) {
type = type.toUpperCase();
return switch (type) {
case "GL_ZERO" -> GL_ZERO;
case "GL_ONE" -> GL_ONE;
case "GL_SRC_COLOR" -> GL_SRC_COLOR;
case "GL_ONE_MINUS_SRC_COLOR" -> GL_ONE_MINUS_SRC_COLOR;
case "GL_SRC_ALPHA" -> GL_SRC_ALPHA;
case "GL_ONE_MINUS_SRC_ALPHA" -> GL_ONE_MINUS_SRC_ALPHA;
case "GL_DST_ALPHA" -> GL_DST_ALPHA;
case "GL_ONE_MINUS_DST_ALPHA" -> GL_ONE_MINUS_DST_ALPHA;
case "GL_DST_COLOR" -> GL_DST_COLOR;
case "GL_ONE_MINUS_DST_COLOR" -> GL_ONE_MINUS_DST_COLOR;
case "GL_SRC_ALPHA_SATURATE" -> GL_SRC_ALPHA_SATURATE;
case "GL_SRC1_COLOR" -> GL_SRC1_COLOR;
case "GL_ONE_MINUS_SRC1_COLOR" -> GL_ONE_MINUS_SRC1_COLOR;
case "GL_ONE_MINUS_SRC1_ALPHA" -> GL_ONE_MINUS_SRC1_ALPHA;
default -> -1;
};
}
@Override
public Int2ObjectMap<BlendState> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
if (json==null) return null;
Int2ObjectMap<BlendState> ret = new Int2ObjectOpenHashMap<>();
try {
if (json.isJsonPrimitive()) {
if (json.getAsString().equalsIgnoreCase("off")) {
ret.put(-1, BlendState.ALL_OFF);
return ret;
}
} else if (json.isJsonObject()) {
for (var entry : json.getAsJsonObject().entrySet()) {
int buffer = Integer.parseInt(entry.getKey());
BlendState state;
var val = entry.getValue();
if (val.isJsonArray()) {
int[] v = val.getAsJsonArray().asList().stream().mapToInt(a->parseType(a.getAsString())).toArray();
state = new BlendState(buffer, false, v[0], v[1], v[2], v[3]);
} else if (val.isJsonPrimitive()) {
if (val.getAsString().equalsIgnoreCase("off")) {
state = new BlendState(buffer, true, 0,0,0,0);
} else {
state = new BlendState(buffer, true, -1,-1,-1,-1);
}
} else {
state = null;
}
ret.put(buffer, state);
}
return ret;
}
} catch (Exception e) {
Logger.error(e);
}
Logger.error("Failed to parse blend state: " + json);
return ret;
}
}
private static class PatchGson {
public int version;//TODO maybe replace with semver?
public int[] opaqueDrawBuffers;
public int[] translucentDrawBuffers;
public String[] uniforms;
@JsonAdapter(SamplerDeserializer.class)
public Object2ObjectLinkedOpenHashMap<String, String> samplers;
public String opaquePatchData;
public String translucentPatchData;
@JsonAdapter(SSBODeserializer.class)
public Int2ObjectOpenHashMap<String> ssbos;
@JsonAdapter(BlendStateDeserializer.class)
public Int2ObjectOpenHashMap<BlendState> blending;
public String taaOffset;
public boolean excludeLodsFromVanillaDepth;
public float[] renderScale;
public boolean useViewportDims;
public boolean checkValid() {
return this.opaqueDrawBuffers != null && this.translucentDrawBuffers != null && this.uniforms != null && this.opaquePatchData != null;
}
}
private final PatchGson patchData;
private final ShaderPack pack;
private final Int2ObjectMap<String> ssbos;
private IrisShaderPatch(PatchGson patchData, ShaderPack pack) {
this.patchData = patchData;
this.pack = pack;
if (patchData.ssbos == null) {
this.ssbos = new Int2ObjectOpenHashMap<>();
} else {
this.ssbos = patchData.ssbos;
}
}
public boolean useViewportDims() {
return this.patchData.useViewportDims;
}
public Int2ObjectMap<String> getSSBOs() {
return new Int2ObjectLinkedOpenHashMap<>(this.ssbos);
}
public String getPatchOpaqueSource() {
return this.patchData.opaquePatchData;
}
public String getPatchTranslucentSource() {
return this.patchData.translucentPatchData;
}
public String getTAAShift() {
return this.patchData.taaOffset == null?"{return vec2(0.0);}":this.patchData.taaOffset;
}
public String[] getUniformList() {
return this.patchData.uniforms;
}
public Object2ObjectLinkedOpenHashMap<String, String> getSamplerSet() {
return this.patchData.samplers;
}
public int[] getOpqaueTargets() {
return this.patchData.opaqueDrawBuffers;
}
public int[] getTranslucentTargets() {
return this.patchData.translucentDrawBuffers;
}
public boolean emitToVanillaDepth() {
return !this.patchData.excludeLodsFromVanillaDepth;
}
public float[] getRenderScale() {
if (this.patchData.renderScale == null || this.patchData.renderScale.length==0) {
return new float[]{1,1};
}
if (this.patchData.renderScale.length == 1) {
return new float[]{this.patchData.renderScale[0],this.patchData.renderScale[0]};
}
return new float[]{Math.max(0.01f,this.patchData.renderScale[0]),Math.max(0.01f,this.patchData.renderScale[1])};
}
public Runnable createBlendSetup() {
if (this.patchData.blending == null || this.patchData.blending.isEmpty()) {
return ()->{};//No blending change
}
return ()->{
final var BS = this.patchData.blending;
//Set inital state
var init = BS.getOrDefault(-1, null);
if (init != null) {
if (init.off) {
glDisable(GL_BLEND);
} else {
glEnable(GL_BLEND);
glBlendFuncSeparate(init.sRBG, init.dRGb, init.sA, init.dA);
}
}
for (var entry:BS.int2ObjectEntrySet()) {
if (entry.getIntKey() == -1) continue;
final var s = entry.getValue();
if (s.off) {
glDisablei(GL_BLEND, s.buffer);
} else {
glEnablei(GL_BLEND, s.buffer);
glBlendFuncSeparatei(s.buffer, s.sRBG, s.dRGb, s.sA, s.dA);
}
}
};
}
private static final Gson GSON = new GsonBuilder()
.excludeFieldsWithModifiers(Modifier.PRIVATE)
.setStrictness(Strictness.LENIENT)
.create();
public static IrisShaderPatch makePatch(ShaderPack ipack, AbsolutePackPath directory, Function<AbsolutePackPath, String> sourceProvider) {
String voxyPatchData = sourceProvider.apply(directory.resolve("voxy.json"));
if (voxyPatchData == null) {//No voxy patch data in shaderpack
return null;
}
//A more graceful exit on blank string
if (voxyPatchData.isBlank()) {
return null;
}
//Escape things
voxyPatchData = voxyPatchData.replace("\\", "\\\\");
PatchGson patchData = null;
try {
//TODO: basicly find any "commented out" quotation marks and escape them (if the line, when stripped starts with a // or /* then escape all quotation marks in that line)
{
StringBuilder builder = new StringBuilder(voxyPatchData.length());
//Rebuild the patch, replacing commented out " with \"
for (var line : voxyPatchData.split("\n")) {
int idx = line.indexOf("//");
if (idx != -1) {
builder.append(line, 0, idx);
builder.append(line.substring(idx).replace("\"","\\\""));
} else {
builder.append(line);
}
builder.append("\n");
}
voxyPatchData = builder.toString();
}
patchData = GSON.fromJson(voxyPatchData, PatchGson.class);
if (patchData == null) {
return null;
}
{//Inject data from the auxilery files if they are present
var opaque = sourceProvider.apply(directory.resolve("voxy_opaque.glsl"));
if (opaque != null) {
patchData.opaquePatchData = opaque;
}
var translucent = sourceProvider.apply(directory.resolve("voxy_translucent.glsl"));
if (translucent != null) {
patchData.translucentPatchData = translucent;
}
}
if (!patchData.checkValid()) {
throw new IllegalStateException("voxy json patch not valid: " + voxyPatchData);
}
} catch (Exception e) {
patchData = null;
Logger.error("Failed to parse patch data gson",e);
throw new ShaderLoadError("Failed to parse patch data gson",e);
}
if (patchData == null) {
return null;
}
if (patchData.version != VERSION) {
Logger.error("Shader has voxy patch data, but patch version is incorrect. expected " + VERSION + " got "+patchData.version);
throw new IllegalStateException("Shader version mismatch expected " + VERSION + " got "+patchData.version);
}
return new IrisShaderPatch(patchData, ipack);
}
}

View File

@@ -0,0 +1,445 @@
package me.cortex.voxy.client.iris;
import com.google.common.collect.ImmutableSet;
import it.unimi.dsi.fastutil.ints.Int2ObjectLinkedOpenHashMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import kroppeb.stareval.function.FunctionReturn;
import kroppeb.stareval.function.Type;
import me.cortex.voxy.client.core.IrisVoxyRenderPipeline;
import me.cortex.voxy.client.mixin.iris.CustomUniformsAccessor;
import me.cortex.voxy.client.mixin.iris.IrisRenderingPipelineAccessor;
import me.cortex.voxy.common.Logger;
import net.irisshaders.iris.gl.buffer.ShaderStorageBufferHolder;
import net.irisshaders.iris.gl.image.ImageHolder;
import net.irisshaders.iris.gl.sampler.GlSampler;
import net.irisshaders.iris.gl.sampler.SamplerHolder;
import net.irisshaders.iris.gl.state.ValueUpdateNotifier;
import net.irisshaders.iris.gl.texture.InternalTextureFormat;
import net.irisshaders.iris.gl.texture.TextureType;
import net.irisshaders.iris.gl.uniform.*;
import net.irisshaders.iris.pipeline.IrisRenderingPipeline;
import net.irisshaders.iris.targets.RenderTarget;
import net.irisshaders.iris.targets.RenderTargets;
import net.irisshaders.iris.uniforms.custom.CustomUniforms;
import net.irisshaders.iris.uniforms.custom.cached.*;
import org.joml.*;
import org.lwjgl.system.MemoryUtil;
import java.util.*;
import java.util.function.IntConsumer;
import java.util.function.IntSupplier;
import java.util.function.LongConsumer;
import java.util.stream.Collectors;
import static org.lwjgl.opengl.ARBDirectStateAccess.glBindTextureUnit;
import static org.lwjgl.opengl.ARBUniformBufferObject.glBindBufferBase;
import static org.lwjgl.opengl.GL33C.glBindSampler;
import static org.lwjgl.opengl.GL43C.GL_SHADER_STORAGE_BUFFER;
public class IrisVoxyRenderPipelineData {
public IrisVoxyRenderPipeline thePipeline;
public final int[] opaqueDrawTargets;
public final int[] translucentDrawTargets;
private final String opaquePatch;
private final String translucentPatch;
private final StructLayout uniforms;
private final Runnable blendingSetup;
private final ImageSet imageSet;
private final SSBOSet ssboSet;
public final boolean renderToVanillaDepth;
public final float[] resolutionScale;
public final String TAA;
public final boolean useViewportDims;
private IrisVoxyRenderPipelineData(IrisShaderPatch patch, int[] opaqueDrawTargets, int[] translucentDrawTargets, StructLayout uniformSet, Runnable blendingSetup, ImageSet imageSet, SSBOSet ssboSet) {
this.opaqueDrawTargets = opaqueDrawTargets;
this.translucentDrawTargets = translucentDrawTargets;
this.opaquePatch = patch.getPatchOpaqueSource();
this.translucentPatch = patch.getPatchTranslucentSource();
this.uniforms = uniformSet;
this.blendingSetup = blendingSetup;
this.imageSet = imageSet;
this.ssboSet = ssboSet;
this.renderToVanillaDepth = patch.emitToVanillaDepth();
this.TAA = patch.getTAAShift();
this.resolutionScale = patch.getRenderScale();
this.useViewportDims = patch.useViewportDims();
}
public SSBOSet getSsboSet() {
return this.ssboSet;
}
public ImageSet getImageSet() {
return this.imageSet;
}
public StructLayout getUniforms() {
return this.uniforms;
}
public Runnable getBlender() {
return this.blendingSetup;
}
public String opaqueFragPatch() {
return this.opaquePatch;
}
public String translucentFragPatch() {
return this.translucentPatch;
}
public static IrisVoxyRenderPipelineData buildPipeline(IrisRenderingPipeline ipipe, IrisShaderPatch patch, CustomUniforms cu, ShaderStorageBufferHolder ssboHolder) {
var uniforms = createUniformLayoutStructAndUpdater(createUniformSet(cu, patch));
var imageSet = createImageSet(ipipe, patch);
var ssboSet = createSSBOLayouts(patch.getSSBOs(), ssboHolder);
var opaqueDrawTargets = getDrawBuffers(patch.getOpqaueTargets(), ipipe.getFlippedAfterPrepare(), ((IrisRenderingPipelineAccessor)ipipe).getRenderTargets());
var translucentDrawTargets = getDrawBuffers(patch.getTranslucentTargets(), ipipe.getFlippedAfterPrepare(), ((IrisRenderingPipelineAccessor)ipipe).getRenderTargets());
//TODO: need to transform the string patch with the uniform decleration aswell as sampler declerations
return new IrisVoxyRenderPipelineData(patch, opaqueDrawTargets, translucentDrawTargets, uniforms, patch.createBlendSetup(), imageSet, ssboSet);
}
private static int[] getDrawBuffers(int[] targets, ImmutableSet<Integer> stageWritesToAlt, RenderTargets rt) {
int[] targetTextures = new int[targets.length];
for(int i = 0; i < targets.length; i++) {
RenderTarget target = rt.getOrCreate(targets[i]);
int textureId = stageWritesToAlt.contains(targets[i]) ? target.getAltTexture() : target.getMainTexture();
targetTextures[i] = textureId;
}
return targetTextures;
}
public record StructLayout(int size, String layout, LongConsumer updater) {}
private static StructLayout createUniformLayoutStructAndUpdater(CachedUniform[] uniforms) {
if (uniforms.length == 0) {
return null;
}
List<CachedUniform>[] ordering = new List[]{new ArrayList<>(), new ArrayList<>(), new ArrayList<>(), new ArrayList<>()};
//Creates an optimial struct layout for the uniforms
for (var uniform : uniforms) {
int order = getUniformOrdering(Type.convert(uniform.getType()));
ordering[order].add(uniform);
}
//Emit the ordering, note this is not optimial, but good enough, e.g. if have even number of align 2, emit that after align 4
int pos = 0;
Int2ObjectLinkedOpenHashMap<CachedUniform> layout = new Int2ObjectLinkedOpenHashMap<>();
for (var uniform : ordering[0]) {//Emit exact align 4
layout.put(pos, uniform); pos += getSizeAndAlignment(Type.convert(uniform.getType()))>>5;
}
if (!ordering[1].isEmpty() && (ordering[1].size()&1)==0) {
//Emit all the align 2 as there is an even number of them
for (var uniform : ordering[1]) {
layout.put(pos, uniform); pos += getSizeAndAlignment(Type.convert(uniform.getType()))>>5;
}
ordering[1].clear();
}
//Emit align 3
for (var uniform : ordering[2]) {//Emit size odd, alignment must be 4
layout.put(pos, uniform); pos += getSizeAndAlignment(Type.convert(uniform.getType()))>>5;
//We must get a size 1 to pad to align 4
if (!ordering[3].isEmpty()) {//Size 1
uniform = ordering[3].removeFirst();
layout.put(pos, uniform); pos += getSizeAndAlignment(Type.convert(uniform.getType()))>>5;
} else {//Padding must be injected
pos += 1;
}
}
//Emit align 2
for (var uniform : ordering[1]) {
layout.put(pos, uniform); pos += getSizeAndAlignment(Type.convert(uniform.getType()))>>5;
}
//Emit align 1
for (var uniform : ordering[3]) {
layout.put(pos, uniform); pos += getSizeAndAlignment(Type.convert(uniform.getType()))>>5;
}
if (layout.size()!=uniforms.length) {
throw new IllegalStateException();
}
//We have our ordering and aligned offsets, generate an updater aswell as the layout
String structLayout;
{
StringBuilder struct = new StringBuilder("{\n");
for (var pair : layout.int2ObjectEntrySet()) {
struct.append("\t").append(pair.getValue().getType().toString()).append(" ").append(pair.getValue().getName()).append(";\n");
}
struct.append("}");
structLayout = struct.toString();
}
LongConsumer updater;
{
FunctionReturn cacheRetObj = new FunctionReturn();
LongConsumer[] updaters = new LongConsumer[uniforms.length];
int i = 0;
for (var pair : layout.int2ObjectEntrySet()) {
updaters[i++] = createWriter(pair.getIntKey()*4L, cacheRetObj, pair.getValue());
}
updater = ptr -> {
for (var u : updaters) {
u.accept(ptr);
}
};//Writes all the uniforms to the locations
}
return new StructLayout(pos*4, structLayout, updater);//*4 since each slot is 4 bytes
}
private static LongConsumer createWriter(long offset, FunctionReturn ret, CachedUniform uniform) {
if (uniform instanceof BooleanCachedUniform bcu) {
return ptr->{ptr += offset;
bcu.writeTo(ret);
MemoryUtil.memPutInt(ptr, ret.booleanReturn?1:0);
};
} else if (uniform instanceof FloatCachedUniform fcu) {
return ptr->{ptr += offset;
fcu.writeTo(ret);
MemoryUtil.memPutFloat(ptr, ret.floatReturn);
};
} else if (uniform instanceof IntCachedUniform icu) {
return ptr->{ptr += offset;
icu.writeTo(ret);
MemoryUtil.memPutInt(ptr, ret.intReturn);
};
} else if (uniform instanceof Float2VectorCachedUniform v2fcu) {
return ptr->{ptr += offset;
v2fcu.writeTo(ret);
((Vector2f)ret.objectReturn).getToAddress(ptr);
};
} else if (uniform instanceof Float3VectorCachedUniform v3fcu) {
return ptr->{ptr += offset;
v3fcu.writeTo(ret);
((Vector3f)ret.objectReturn).getToAddress(ptr);
};
} else if (uniform instanceof Float4VectorCachedUniform v4fcu) {
return ptr->{ptr += offset;
v4fcu.writeTo(ret);
((Vector4f)ret.objectReturn).getToAddress(ptr);
};
} else if (uniform instanceof Int2VectorCachedUniform v2icu) {
return ptr->{ptr += offset;
v2icu.writeTo(ret);
((Vector2i)ret.objectReturn).getToAddress(ptr);
};
} else if (uniform instanceof Int3VectorCachedUniform v3icu) {
return ptr->{ptr += offset;
v3icu.writeTo(ret);
((Vector3i)ret.objectReturn).getToAddress(ptr);
};
} else if (uniform instanceof Float4MatrixCachedUniform f4mcu) {
return ptr->{ptr += offset;
f4mcu.writeTo(ret);
((Matrix4f)ret.objectReturn).getToAddress(ptr);
};
} else {
throw new IllegalStateException("Unknown uniform type " + uniform.getClass().getName());
}
}
private static int P(int size, int align) {
return size<<5|align;
}
private static int getSizeAndAlignment(UniformType type) {
return switch (type) {
case INT, FLOAT -> P(1,1);//Size, Alignment
case MAT3 -> P(4+4+3,4);//is funky as each row is a vec3 padded to a vec4
case MAT4 -> P(4*4,4);
case VEC2, VEC2I -> P(2,2);
case VEC3, VEC3I -> P(3,4);
case VEC4, VEC4I -> P(4,4);
};
}
private static int getUniformOrdering(UniformType type) {
return switch (type) {
case MAT4, VEC4, VEC4I -> 0;
case VEC2, VEC2I -> 1;
case VEC3, VEC3I, MAT3 -> 2;
case INT, FLOAT -> 3;
};
}
private static CachedUniform[] createUniformSet(CustomUniforms cu, IrisShaderPatch patch) {
//This is a fking awful hack... but it works thinks
LocationalUniformHolder uniformBuilder = new LocationalUniformHolder() {
@Override
public LocationalUniformHolder addUniform(UniformUpdateFrequency uniformUpdateFrequency, Uniform uniform) {
return this;
}
@Override
public OptionalInt location(String uniformName, UniformType uniformType) {
//Yes am aware how performant inefficent this is... just dont care tbh since is on setup and is small
var names = patch.getUniformList();
for (int i = 0; i < names.length; i++) {
if (names[i].equals(uniformName)) {
return OptionalInt.of(i);//Have a base uniform offset of 10
}
}
return OptionalInt.empty();
}
@Override
public UniformHolder externallyManagedUniform(String s, UniformType uniformType) {
return null;
}
};
cu.assignTo(uniformBuilder);
cu.mapholderToPass(uniformBuilder, patch);
CachedUniform[] uniforms = new CachedUniform[patch.getUniformList().length];
((CustomUniformsAccessor)cu).getLocationMap().get(patch).object2IntEntrySet().forEach(entry->uniforms[entry.getIntValue()] = entry.getKey());
int i = 0;
int j = 0;
for (var uniform : uniforms) {
if (uniform == null) {
Logger.error("Unknown uniform at location "+j + " skipping");
} else {
uniforms[i++] = uniform;//This shuffles the uniforms down till its compacted
}
j++;
}
//In _theory_ this should work?
return Arrays.copyOf(uniforms, i);
}
private record TextureWSampler(String name, IntSupplier texture, int sampler) { }
public record ImageSet(String layout, IntConsumer bindingFunction) {
}
private static ImageSet createImageSet(IrisRenderingPipeline ipipe, IrisShaderPatch patch) {
var samplerDataSet = patch.getSamplerSet();
Set<String> samplerNameSet = new LinkedHashSet<>(samplerDataSet.keySet());
if (samplerNameSet.isEmpty()) return null;
Set<TextureWSampler> samplerSet = new LinkedHashSet<>();
SamplerHolder samplerBuilder = new SamplerHolder() {
@Override
public boolean hasSampler(String s) {
return samplerNameSet.contains(s);
}
public boolean hasSampler(String... names) {
for (var name : names) {
if (samplerNameSet.contains(name)) return true;
}
return false;
}
private String name(String... names) {
for (var name : names) {
if (samplerNameSet.contains(name)) return name;
}
return null;
}
@Override
public boolean addDefaultSampler(TextureType type, IntSupplier texture, ValueUpdateNotifier notifier, GlSampler sampler, String... names) {
Logger.error("Unsupported default sampler");
return false;
}
@Override
public boolean addDynamicSampler(TextureType type, IntSupplier texture, GlSampler sampler, String... names) {
return this.addDynamicSampler(type, texture, null, sampler, names);
}
@Override
public boolean addDynamicSampler(TextureType type, IntSupplier texture, ValueUpdateNotifier notifier, GlSampler sampler, String... names) {
if (!this.hasSampler(names)) return false;
samplerSet.add(new TextureWSampler(this.name(names), texture, sampler!=null?sampler.getId():-1));
return true;
}
@Override
public void addExternalSampler(int texture, String... names) {
if (!this.hasSampler(names)) return;
samplerSet.add(new TextureWSampler(this.name(names), ()->texture, -1));
}
};
//Unsupported
ImageHolder imageBuilder = new ImageHolder() {
@Override
public boolean hasImage(String s) {
return false;
}
@Override
public void addTextureImage(IntSupplier intSupplier, InternalTextureFormat internalTextureFormat, String s) {
}
};
ipipe.addGbufferOrShadowSamplers(samplerBuilder, imageBuilder, ipipe::getFlippedAfterPrepare, false, true, true, false);
//samplerSet contains our samplers
if (samplerSet.size() != samplerNameSet.size()) {
Logger.error("Did not find all requested samplers. Found [" + samplerSet.stream().map(a->a.name).collect(Collectors.joining()) + "] expected " + samplerNameSet);
}
//TODO: generate a layout (defines) for all the samplers with the correct types
StringBuilder builder = new StringBuilder();
TextureWSampler[] samplers = new TextureWSampler[samplerSet.size()];
int i = 0;
for (var entry : samplerSet) {
samplers[i]=entry;
String samplerType = samplerDataSet.get(entry.name);
builder.append("layout(binding=(BASE_SAMPLER_BINDING_INDEX+").append(i).append(")) uniform ").append(samplerType).append(" ").append(entry.name).append(";\n");
i++;
}
IntConsumer bindingFunction = base->{
for (int j = 0; j < samplers.length; j++) {
int unit = j+base;
var ts = samplers[j];
glBindTextureUnit(unit, ts.texture.getAsInt());
if (ts.sampler != -1) {
glBindSampler(unit, ts.sampler);
}//TODO: might need to bind sampler 0
}
};
return new ImageSet(builder.toString(), bindingFunction);
}
public record SSBOSet(String layout, IntConsumer bindingFunction){}
private record SSBOBinding(int irisIndex, int bindingOffset) {}
private static SSBOSet createSSBOLayouts(Int2ObjectMap<String> ssbos, ShaderStorageBufferHolder ssboStore) {
if (ssboStore == null) return null;//If there is no store, there cannot be any ssbos
if (ssbos.isEmpty()) return null;
String header = "";
if (ssbos.containsKey(-1)) header = ssbos.remove(-1);
StringBuilder builder = new StringBuilder(header);
builder.append("\n");
SSBOBinding[] bindings = new SSBOBinding[ssbos.size()];
int i = 0;
for (var entry : ssbos.int2ObjectEntrySet()) {
var val = entry.getValue();
bindings[i] = new SSBOBinding(entry.getIntKey(), i);
builder.append("layout(binding = (BUFFER_BINDING_INDEX_BASE+").append(i).append(")) restrict buffer IrisBufferBinding").append(i);
builder.append(" ").append(val).append(";\n");
i++;
}
//ssboStore.getBufferIndex()
IntConsumer bindingFunction = base->{
for (var binding : bindings) {
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, base+binding.bindingOffset, ssboStore.getBufferIndex(binding.irisIndex));
}
};
return new SSBOSet(builder.toString(), bindingFunction);
}
}

View File

@@ -0,0 +1,11 @@
package me.cortex.voxy.client.iris;
public class ShaderLoadError extends RuntimeException {
public ShaderLoadError(String reason) {
super(reason);
}
public ShaderLoadError(String reason, Exception cause) {
super(reason, cause);
}
}

View File

@@ -0,0 +1,54 @@
package me.cortex.voxy.client.iris;
import net.irisshaders.iris.gl.sampler.SamplerHolder;
import net.irisshaders.iris.gl.texture.TextureType;
import net.irisshaders.iris.pipeline.IrisRenderingPipeline;
public class VoxySamplers {
public static void addSamplers(IrisRenderingPipeline pipeline, SamplerHolder samplers) {
var patchData = ((IGetVoxyPatchData)pipeline).voxy$getPatchData();
if (patchData != null) {
String[] opaqueNames = new String[]{"vxDepthTexOpaque"};
String[] translucentNames = new String[]{"vxDepthTexTrans"};
if (IrisShaderPatch.IMPERSONATE_DISTANT_HORIZONS) {
opaqueNames = new String[]{"vxDepthTexOpaque", "dhDepthTex1"};
translucentNames = new String[]{"vxDepthTexTrans", "dhDepthTex", "dhDepthTex0"};
}
//TODO replace ()->0 with the actual depth texture id
samplers.addDynamicSampler(TextureType.TEXTURE_2D, () -> {
var pipeData = ((IGetIrisVoxyPipelineData)pipeline).voxy$getPipelineData();
if (pipeData == null) {
return 0;
}
if (pipeData.thePipeline == null) {
return 0;
}
//In theory the first frame could be null
var dt = pipeData.thePipeline.fb.getDepthTex();
if (dt == null) {
return 0;
}
return dt.id;
}, null, opaqueNames);
samplers.addDynamicSampler(TextureType.TEXTURE_2D, () -> {
var pipeData = ((IGetIrisVoxyPipelineData)pipeline).voxy$getPipelineData();
if (pipeData == null) {
return 0;
}
if (pipeData.thePipeline == null) {
return 0;
}
//In theory the first frame could be null
var dt = pipeData.thePipeline.fbTranslucent.getDepthTex();
if (dt == null) {
return 0;
}
return dt.id;
}, null, translucentNames);
}
}
}

View File

@@ -0,0 +1,106 @@
package me.cortex.voxy.client.iris;
import me.cortex.voxy.client.config.VoxyConfig;
import me.cortex.voxy.client.core.IGetVoxyRenderSystem;
import net.irisshaders.iris.gl.uniform.UniformHolder;
import net.minecraft.client.MinecraftClient;
import org.joml.Matrix4f;
import org.joml.Matrix4fc;
import java.util.function.Supplier;
import static net.irisshaders.iris.gl.uniform.UniformUpdateFrequency.PER_FRAME;
public class VoxyUniforms {
public static Matrix4f getViewProjection() {//This is 1 frame late ;-; cries, since the update occurs _before_ the voxy render pipeline
var getVrs = (IGetVoxyRenderSystem) MinecraftClient.getInstance().worldRenderer;
if (getVrs == null || getVrs.getVoxyRenderSystem() == null) {
return new Matrix4f();
}
var vrs = getVrs.getVoxyRenderSystem();
return new Matrix4f(vrs.getViewport().MVP);
}
public static Matrix4f getModelView() {//This is 1 frame late ;-; cries, since the update occurs _before_ the voxy render pipeline
var getVrs = (IGetVoxyRenderSystem) MinecraftClient.getInstance().worldRenderer;
if (getVrs == null || getVrs.getVoxyRenderSystem() == null) {
return new Matrix4f();
}
var vrs = getVrs.getVoxyRenderSystem();
return new Matrix4f(vrs.getViewport().modelView);
}
public static Matrix4f getProjection() {//This is 1 frame late ;-; cries, since the update occurs _before_ the voxy render pipeline
var getVrs = (IGetVoxyRenderSystem) MinecraftClient.getInstance().worldRenderer;
if (getVrs == null || getVrs.getVoxyRenderSystem() == null) {
return new Matrix4f();
}
var vrs = getVrs.getVoxyRenderSystem();
var mat = vrs.getViewport().projection;
if (mat == null) {
return new Matrix4f();
}
return new Matrix4f(mat);
}
public static void addUniforms(UniformHolder uniforms) {
uniforms
.uniform1i(PER_FRAME, "vxRenderDistance", ()-> VoxyConfig.CONFIG.sectionRenderDistance*32)//In chunks
.uniformMatrix(PER_FRAME, "vxViewProj", VoxyUniforms::getViewProjection)
.uniformMatrix(PER_FRAME, "vxViewProjInv", new Inverted(VoxyUniforms::getViewProjection))
.uniformMatrix(PER_FRAME, "vxViewProjPrev", new PreviousMat(VoxyUniforms::getViewProjection))
.uniformMatrix(PER_FRAME, "vxModelView", VoxyUniforms::getModelView)
.uniformMatrix(PER_FRAME, "vxModelViewInv", new Inverted(VoxyUniforms::getModelView))
.uniformMatrix(PER_FRAME, "vxModelViewPrev", new PreviousMat(VoxyUniforms::getModelView))
.uniformMatrix(PER_FRAME, "vxProj", VoxyUniforms::getProjection)
.uniformMatrix(PER_FRAME, "vxProjInv", new Inverted(VoxyUniforms::getProjection))
.uniformMatrix(PER_FRAME, "vxProjPrev", new PreviousMat(VoxyUniforms::getProjection));
if (IrisShaderPatch.IMPERSONATE_DISTANT_HORIZONS) {
uniforms
.uniform1f(PER_FRAME, "dhNearPlane", ()->16)//Presently hardcoded in voxy
.uniform1f(PER_FRAME, "dhFarPlane", ()->16*3000)//Presently hardcoded in voxy
.uniform1i(PER_FRAME, "dhRenderDistance", ()-> VoxyConfig.CONFIG.sectionRenderDistance*32*16)//In blocks
.uniformMatrix(PER_FRAME, "dhProjection", VoxyUniforms::getProjection)
.uniformMatrix(PER_FRAME, "dhProjectionInverse", new Inverted(VoxyUniforms::getProjection))
.uniformMatrix(PER_FRAME, "dhPreviousProjection", new PreviousMat(VoxyUniforms::getProjection));
}
}
private record Inverted(Supplier<Matrix4fc> parent) implements Supplier<Matrix4fc> {
private Inverted(Supplier<Matrix4fc> parent) {
this.parent = parent;
}
public Matrix4fc get() {
Matrix4f copy = new Matrix4f(this.parent.get());
copy.invert();
return copy;
}
public Supplier<Matrix4fc> parent() {
return this.parent;
}
}
private static class PreviousMat implements Supplier<Matrix4fc> {
private final Supplier<Matrix4fc> parent;
private Matrix4f previous;
PreviousMat(Supplier<Matrix4fc> parent) {
this.parent = parent;
this.previous = new Matrix4f();
}
public Matrix4fc get() {
Matrix4f previous = this.previous;
this.previous = new Matrix4f(this.parent.get());
return previous;
}
}
}

View File

@@ -0,0 +1,45 @@
package me.cortex.voxy.client.mixin.flashback;
import com.google.gson.JsonObject;
import com.moulberry.flashback.record.FlashbackMeta;
import me.cortex.voxy.client.compat.IFlashbackMeta;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import java.io.File;
@Mixin(value = FlashbackMeta.class, remap = false)
public class MixinFlashbackMeta implements IFlashbackMeta {
@Unique private File voxyPath;
@Override
public void setVoxyPath(File path) {
this.voxyPath = path;
}
@Override
public File getVoxyPath() {
return this.voxyPath;
}
@Inject(method = "toJson", at = @At("RETURN"))
private void voxy$injectSaveVoxyPath(CallbackInfoReturnable<JsonObject> cir) {
var val = cir.getReturnValue();
if (val != null && this.voxyPath != null) {
val.addProperty("voxy_storage_path", this.voxyPath.getAbsoluteFile().getPath());
}
}
@Inject(method = "fromJson", at = @At("RETURN"))
private static void voxy$injectGetVoxyPath(JsonObject meta, CallbackInfoReturnable<FlashbackMeta> cir) {
var val = cir.getReturnValue();
if (val != null && meta != null) {
if (meta.has("voxy_storage_path")) {
((IFlashbackMeta)val).setVoxyPath(new File(meta.get("voxy_storage_path").getAsString()));
}
}
}
}

View File

@@ -0,0 +1,29 @@
package me.cortex.voxy.client.mixin.flashback;
import com.moulberry.flashback.record.FlashbackMeta;
import com.moulberry.flashback.record.Recorder;
import me.cortex.voxy.client.VoxyClientInstance;
import me.cortex.voxy.client.compat.IFlashbackMeta;
import me.cortex.voxy.commonImpl.VoxyCommon;
import net.minecraft.registry.DynamicRegistryManager;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(value = Recorder.class, remap = false)
public class MixinFlashbackRecorder {
@Shadow @Final private FlashbackMeta metadata;
@Inject(method = "<init>", at = @At("TAIL"))
private void voxy$getStoragePath(DynamicRegistryManager registryAccess, CallbackInfo retInf) {
if (VoxyCommon.isAvailable()) {
var instance = VoxyCommon.getInstance();
if (instance instanceof VoxyClientInstance ci) {
((IFlashbackMeta)this.metadata).setVoxyPath(ci.getStorageBasePath().toFile());
}
}
}
}

View File

@@ -0,0 +1,14 @@
package me.cortex.voxy.client.mixin.iris;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import net.irisshaders.iris.uniforms.custom.CustomUniforms;
import net.irisshaders.iris.uniforms.custom.cached.CachedUniform;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
import java.util.Map;
@Mixin(value = CustomUniforms.class, remap = false)
public interface CustomUniformsAccessor {
@Accessor Map<Object, Object2IntMap<CachedUniform>> getLocationMap();
}

View File

@@ -0,0 +1,12 @@
package me.cortex.voxy.client.mixin.iris;
import net.irisshaders.iris.pipeline.IrisRenderingPipeline;
import net.irisshaders.iris.targets.RenderTargets;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
@Mixin(value = IrisRenderingPipeline.class, remap = false)
public interface IrisRenderingPipelineAccessor {
@Accessor
RenderTargets getRenderTargets();
}

View File

@@ -0,0 +1,25 @@
package me.cortex.voxy.client.mixin.iris;
import me.cortex.voxy.client.iris.ShaderLoadError;
import me.cortex.voxy.common.Logger;
import net.irisshaders.iris.Iris;
import net.irisshaders.iris.shaderpack.ShaderPack;
import net.irisshaders.iris.shaderpack.materialmap.NamespacedId;
import net.irisshaders.iris.shaderpack.programs.ProgramSet;
import net.minecraft.util.Identifier;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
@Mixin(value = Iris.class, remap = false)
public class MixinIris {
@Redirect(method = "createPipeline", at = @At(value = "INVOKE", target = "Lnet/irisshaders/iris/shaderpack/ShaderPack;getProgramSet(Lnet/irisshaders/iris/shaderpack/materialmap/NamespacedId;)Lnet/irisshaders/iris/shaderpack/programs/ProgramSet;"))
private static ProgramSet voxy$redirectProgramSet(ShaderPack shaderPack, NamespacedId dim) {
try {
return shaderPack.getProgramSet(dim);
} catch (ShaderLoadError e) {
Logger.error(e);
return null;
}
}
}

View File

@@ -0,0 +1,63 @@
package me.cortex.voxy.client.mixin.iris;
import me.cortex.voxy.client.core.IGetVoxyRenderSystem;
import me.cortex.voxy.client.core.util.IrisUtil;
import me.cortex.voxy.client.iris.IGetIrisVoxyPipelineData;
import me.cortex.voxy.client.iris.IGetVoxyPatchData;
import me.cortex.voxy.client.iris.IrisShaderPatch;
import me.cortex.voxy.client.iris.IrisVoxyRenderPipelineData;
import net.irisshaders.iris.gl.buffer.ShaderStorageBufferHolder;
import net.irisshaders.iris.pipeline.IrisRenderingPipeline;
import net.irisshaders.iris.shaderpack.programs.ProgramSet;
import net.irisshaders.iris.uniforms.custom.CustomUniforms;
import net.minecraft.client.MinecraftClient;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(value = IrisRenderingPipeline.class, remap = false)
public class MixinIrisRenderingPipeline implements IGetVoxyPatchData, IGetIrisVoxyPipelineData {
@Shadow @Final private CustomUniforms customUniforms;
@Shadow private ShaderStorageBufferHolder shaderStorageBufferHolder;
@Unique IrisShaderPatch patchData;
@Unique
IrisVoxyRenderPipelineData pipeline;
@Inject(method = "<init>", at = @At(value = "INVOKE", target = "Lnet/irisshaders/iris/pipeline/transform/ShaderPrinter;resetPrintState()V", shift = At.Shift.AFTER))
private void voxy$injectPatchDataStore(ProgramSet programSet, CallbackInfo ci) {
if (IrisUtil.SHADER_SUPPORT) {
this.patchData = ((IGetVoxyPatchData) programSet).voxy$getPatchData();
}
}
@Inject(method = "<init>", at = @At(value = "INVOKE", target = "Lnet/irisshaders/iris/pipeline/IrisRenderingPipeline;createSetupComputes([Lnet/irisshaders/iris/shaderpack/programs/ComputeSource;Lnet/irisshaders/iris/shaderpack/programs/ProgramSet;Lnet/irisshaders/iris/shaderpack/texture/TextureStage;)[Lnet/irisshaders/iris/gl/program/ComputeProgram;"))
private void voxy$injectPipeline(ProgramSet programSet, CallbackInfo ci) {
if (this.patchData != null) {
this.pipeline = IrisVoxyRenderPipelineData.buildPipeline((IrisRenderingPipeline)(Object)this, this.patchData, this.customUniforms, this.shaderStorageBufferHolder);
}
}
@Inject(method = "beginLevelRendering", at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/opengl/GlStateManager;_activeTexture(I)V", shift = At.Shift.BEFORE), remap = true)
private void voxy$injectViewportSetup(CallbackInfo ci) {
if (IrisUtil.CAPTURED_VIEWPORT_PARAMETERS != null) {
var renderer = ((IGetVoxyRenderSystem) MinecraftClient.getInstance().worldRenderer).getVoxyRenderSystem();
if (renderer != null) {
IrisUtil.CAPTURED_VIEWPORT_PARAMETERS.apply(renderer);
}
}
}
@Override
public IrisShaderPatch voxy$getPatchData() {
return this.patchData;
}
@Override
public IrisVoxyRenderPipelineData voxy$getPipelineData() {
return this.pipeline;
}
}

View File

@@ -0,0 +1,25 @@
package me.cortex.voxy.client.mixin.iris;
import com.google.common.collect.ImmutableSet;
import me.cortex.voxy.client.iris.VoxySamplers;
import net.irisshaders.iris.gl.sampler.SamplerHolder;
import net.irisshaders.iris.pipeline.IrisRenderingPipeline;
import net.irisshaders.iris.pipeline.WorldRenderingPipeline;
import net.irisshaders.iris.samplers.IrisSamplers;
import net.irisshaders.iris.targets.RenderTargets;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.function.Supplier;
@Mixin(value = IrisSamplers.class, remap = false)
public class MixinIrisSamplers {
@Inject(method = "addRenderTargetSamplers", at = @At("TAIL"))
private static void voxy$injectSamplers(SamplerHolder samplers, Supplier<ImmutableSet<Integer>> flipped, RenderTargets renderTargets, boolean isFullscreenPass, WorldRenderingPipeline pipeline, CallbackInfo ci) {
if (pipeline instanceof IrisRenderingPipeline ipipe) {
VoxySamplers.addSamplers(ipipe, samplers);
}
}
}

View File

@@ -0,0 +1,24 @@
package me.cortex.voxy.client.mixin.iris;
import me.cortex.voxy.client.config.VoxyConfig;
import me.cortex.voxy.client.core.util.IrisUtil;
import me.cortex.voxy.client.iris.VoxyUniforms;
import net.irisshaders.iris.gl.uniform.UniformHolder;
import net.irisshaders.iris.shaderpack.IdMap;
import net.irisshaders.iris.shaderpack.properties.PackDirectives;
import net.irisshaders.iris.uniforms.CommonUniforms;
import net.irisshaders.iris.uniforms.FrameUpdateNotifier;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(value = CommonUniforms.class, remap = false)
public class MixinMatrixUniforms {
@Inject(method = "addNonDynamicUniforms", at = @At("HEAD"))//Am so angry ims this is what IS REQUIRED TODO, because we need to override the uniforms of dh
private static void voxy$InjectMatrixUniforms(UniformHolder uniforms, IdMap idMap, PackDirectives directives, FrameUpdateNotifier updateNotifier, CallbackInfo ci) {
if (VoxyConfig.CONFIG.isRenderingEnabled() && IrisUtil.SHADER_SUPPORT) {
VoxyUniforms.addUniforms(uniforms);
}
}
}

View File

@@ -0,0 +1,23 @@
package me.cortex.voxy.client.mixin.iris;
import com.google.common.collect.ImmutableSet;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import net.irisshaders.iris.shaderpack.properties.PackRenderTargetDirectives;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.Redirect;
import java.util.Set;
@Mixin(value = PackRenderTargetDirectives.class, remap = false)
public class MixinPackRenderTargetDirectives {
@Redirect(method = "<clinit>", at = @At(value = "INVOKE", target = "Lcom/google/common/collect/ImmutableSet$Builder;build()Lcom/google/common/collect/ImmutableSet;"))
private static ImmutableSet<Integer> voxy$injectExtraColourTex(ImmutableSet.Builder<Integer> builder) {
int limit = System.getProperty("voxy.IrisExtremeColourTexOverride", "false").equalsIgnoreCase("true")?200:20;
for (int i = 16; i < limit; i++) {
builder.add(i);
}
return builder.build();
}
}

View File

@@ -0,0 +1,51 @@
package me.cortex.voxy.client.mixin.iris;
import me.cortex.voxy.client.config.VoxyConfig;
import me.cortex.voxy.client.core.util.IrisUtil;
import me.cortex.voxy.client.iris.IGetVoxyPatchData;
import me.cortex.voxy.client.iris.IrisShaderPatch;
import net.irisshaders.iris.shaderpack.ShaderPack;
import net.irisshaders.iris.shaderpack.include.AbsolutePackPath;
import net.irisshaders.iris.shaderpack.programs.ProgramSet;
import net.irisshaders.iris.shaderpack.properties.PackDirectives;
import net.irisshaders.iris.shaderpack.properties.ShaderProperties;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.function.Function;
@Mixin(value = ProgramSet.class, remap = false)
public class MixinProgramSet implements IGetVoxyPatchData {
@Shadow @Final private PackDirectives packDirectives;
@Unique IrisShaderPatch patchData;
@Inject(method = "<init>", at = @At(value = "INVOKE", target = "Lnet/irisshaders/iris/shaderpack/programs/ProgramSet;locateDirectives()V", shift = At.Shift.BEFORE))
private void voxy$injectPatchMaker(AbsolutePackPath directory, Function<AbsolutePackPath, String> sourceProvider, ShaderProperties shaderProperties, ShaderPack pack, CallbackInfo ci) {
if (VoxyConfig.CONFIG.isRenderingEnabled() && IrisUtil.SHADER_SUPPORT) {
this.patchData = IrisShaderPatch.makePatch(pack, directory, sourceProvider);
}
/*
if (this.patchData != null) {
//Inject directives from voxy
DispatchingDirectiveHolder ddh = new DispatchingDirectiveHolder();
this.packDirectives.acceptDirectivesFrom(ddh);
CommentDirectiveParser.findDirective(this.patchData.getPatchSource(), CommentDirective.Type.RENDERTARGETS)
.map(dir->Arrays.stream(dir.getDirective().split(","))
.mapToInt(Integer::parseInt).toArray())
.ifPresent(ddh::processDirective);
}
*/
}
@Override
public IrisShaderPatch voxy$getPatchData() {
return this.patchData;
}
}

View File

@@ -0,0 +1,20 @@
package me.cortex.voxy.client.mixin.iris;
import com.google.common.collect.ImmutableList;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import net.irisshaders.iris.shaderpack.include.ShaderPackSourceNames;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
@Mixin(value = ShaderPackSourceNames.class, remap = false)
public class MixinShaderPackSourceNames {
@WrapOperation(method = "findPotentialStarts", at = @At(value = "INVOKE", target = "Lcom/google/common/collect/ImmutableList;builder()Lcom/google/common/collect/ImmutableList$Builder;"))
private static ImmutableList.Builder<String> voxy$injectVoxyShaderPatch(Operation<ImmutableList.Builder<String>> original){
var builder = original.call();
builder.add("voxy.json");
builder.add("voxy_opaque.glsl");
builder.add("voxy_translucent.glsl");
return builder;
}
}

View File

@@ -0,0 +1,33 @@
package me.cortex.voxy.client.mixin.iris;
import com.google.common.collect.ImmutableList;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import me.cortex.voxy.client.config.VoxyConfig;
import me.cortex.voxy.client.core.util.IrisUtil;
import me.cortex.voxy.client.iris.IrisShaderPatch;
import net.irisshaders.iris.gl.shader.StandardMacros;
import net.irisshaders.iris.helpers.StringPair;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import java.util.Collection;
import java.util.List;
@Mixin(value = StandardMacros.class, remap = false)
public abstract class MixinStandardMacros {
@Shadow
private static void define(List<StringPair> defines, String key){}
@WrapOperation(method = "createStandardEnvironmentDefines", at = @At(value = "INVOKE", target = "Lcom/google/common/collect/ImmutableList;copyOf(Ljava/util/Collection;)Lcom/google/common/collect/ImmutableList;"))
private static ImmutableList<StringPair> voxy$injectVoxyDefine(Collection<StringPair> list, Operation<ImmutableList<StringPair>> original) {
if (VoxyConfig.CONFIG.isRenderingEnabled() && IrisUtil.SHADER_SUPPORT) {
define((List<StringPair>) list, "VOXY");
if (IrisShaderPatch.IMPERSONATE_DISTANT_HORIZONS) {
define((List<StringPair>) list, "DISTANT_HORIZONS");
}
}
return ImmutableList.copyOf(list);
}
}

View File

@@ -0,0 +1,52 @@
package me.cortex.voxy.client.mixin.iris;
import com.mojang.blaze3d.buffers.GpuBufferSlice;
import me.cortex.voxy.client.core.IGetVoxyRenderSystem;
import me.cortex.voxy.client.core.VoxyRenderSystem;
import me.cortex.voxy.client.core.util.IrisUtil;
import net.caffeinemc.mods.sodium.client.render.chunk.ChunkRenderMatrices;
import net.caffeinemc.mods.sodium.client.util.FogStorage;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.render.Camera;
import net.minecraft.client.render.RenderTickCounter;
import net.minecraft.client.render.WorldRenderer;
import net.minecraft.client.util.ObjectAllocator;
import org.joml.Matrix4f;
import org.joml.Vector4f;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import static org.lwjgl.opengl.GL11C.glViewport;
@Mixin(WorldRenderer.class)
public class MixinWorldRenderer {
@Shadow @Final private MinecraftClient client;
@Inject(method = "render", at = @At("HEAD"), order = 100)
private void voxy$injectIrisCompat(
ObjectAllocator allocator,
RenderTickCounter tickCounter,
boolean renderBlockOutline,
Camera camera,
Matrix4f positionMatrix,
Matrix4f projectionMatrix,
GpuBufferSlice fog,
Vector4f fogColor,
boolean shouldRenderSky,
CallbackInfo ci) {
if (IrisUtil.irisShaderPackEnabled()) {
var renderer = ((IGetVoxyRenderSystem) this).getVoxyRenderSystem();
if (renderer != null) {
//Fixthe fucking viewport dims, fuck iris
glViewport(0,0,MinecraftClient.getInstance().getFramebuffer().textureWidth, MinecraftClient.getInstance().getFramebuffer().textureHeight);
var pos = camera.getCameraPos();
IrisUtil.CAPTURED_VIEWPORT_PARAMETERS = new IrisUtil.CapturedViewportParameters(new ChunkRenderMatrices(projectionMatrix, positionMatrix), ((FogStorage) this.client.gameRenderer).sodium$getFogParameters(), pos.x, pos.y, pos.z);
}
}
}
}

View File

@@ -1,27 +0,0 @@
package me.cortex.voxy.client.mixin.minecraft;
import com.llamalad7.mixinextras.injector.wrapmethod.WrapMethod;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import me.cortex.voxy.client.config.VoxyConfig;
import me.cortex.voxy.client.core.IGetVoxyRenderSystem;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.render.BackgroundRenderer;
import net.minecraft.client.render.Camera;
import net.minecraft.client.render.Fog;
import org.joml.Vector4f;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.Inject;
@Mixin(BackgroundRenderer.class)
public class MixinBackgroundRenderer {
@WrapMethod(method = "applyFog")
private static Fog voxy$overrideFog(Camera camera, BackgroundRenderer.FogType fogType, Vector4f color, float viewDistance, boolean thickenFog, float tickProgress, Operation<Fog> original) {
var vrs = (IGetVoxyRenderSystem)MinecraftClient.getInstance().worldRenderer;
if (VoxyConfig.CONFIG.renderVanillaFog || vrs == null || vrs.getVoxyRenderSystem() == null) {
return original.call(camera, fogType, color, viewDistance, thickenFog, tickProgress);
} else {
return Fog.DUMMY;
}
}
}

View File

@@ -0,0 +1,39 @@
package me.cortex.voxy.client.mixin.minecraft;
import me.cortex.voxy.client.ICheekyClientChunkManager;
import me.cortex.voxy.client.config.VoxyConfig;
import me.cortex.voxy.common.world.service.VoxelIngestService;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.client.world.ClientChunkManager;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.world.chunk.WorldChunk;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(ClientChunkManager.class)
public class MixinClientChunkManager implements ICheekyClientChunkManager {
@Unique
private static final boolean BOBBY_INSTALLED = FabricLoader.getInstance().isModLoaded("bobby");
@Shadow volatile ClientChunkManager.ClientChunkMap chunks;
@Override
public WorldChunk voxy$cheekyGetChunk(int x, int z) {
//This doesnt do the in range check stuff, it just gets the chunk at all costs
return this.chunks.getChunk(this.chunks.getIndex(x, z));
}
@Inject(method = "unload", at = @At("HEAD"))
public void voxy$captureChunkBeforeUnload(ChunkPos pos, CallbackInfo ci) {
if (VoxyConfig.CONFIG.ingestEnabled && BOBBY_INSTALLED) {
var chunk = this.voxy$cheekyGetChunk(pos.x, pos.z);
if (chunk != null) {
VoxelIngestService.tryAutoIngestChunk(chunk);
}
}
}
}

View File

@@ -1,30 +1,26 @@
package me.cortex.voxy.client.mixin.minecraft; package me.cortex.voxy.client.mixin.minecraft;
import me.cortex.voxy.client.LoadException; import me.cortex.voxy.client.VoxyClient;
import me.cortex.voxy.client.VoxyClientInstance; import me.cortex.voxy.client.VoxyClientInstance;
import me.cortex.voxy.client.config.VoxyConfig; import me.cortex.voxy.client.config.VoxyConfig;
import me.cortex.voxy.commonImpl.VoxyCommon; import me.cortex.voxy.commonImpl.VoxyCommon;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.network.ClientPlayNetworkHandler;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.network.*;
import net.minecraft.network.ClientConnection;
import net.minecraft.network.packet.Packet;
import net.minecraft.network.packet.s2c.play.GameJoinS2CPacket; import net.minecraft.network.packet.s2c.play.GameJoinS2CPacket;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.time.Duration; @Mixin(ClientPlayNetworkHandler.class)
import java.util.function.Consumer;
@Mixin(ClientLoginNetworkHandler.class)
public class MixinClientLoginNetworkHandler { public class MixinClientLoginNetworkHandler {
@Inject(method = "<init>", at = @At(value = "TAIL")) @Inject(method = "onGameJoin", at = @At(value = "INVOKE", target = "Lnet/minecraft/network/packet/s2c/play/GameJoinS2CPacket;commonPlayerSpawnInfo()Lnet/minecraft/network/packet/s2c/play/CommonPlayerSpawnInfo;"))
private void voxy$init(ClientConnection connection, MinecraftClient client, ServerInfo serverInfo, Screen parentScreen, boolean newWorld, Duration worldLoadTime, Consumer statusConsumer, CookieStorage cookieStorage, CallbackInfo ci) { private void voxy$init(GameJoinS2CPacket packet, CallbackInfo ci) {
if (VoxyCommon.isAvailable()) { if (VoxyCommon.isAvailable() && !VoxyClientInstance.isInGame) {
VoxyClientInstance.isInGame = true; VoxyClientInstance.isInGame = true;
if (VoxyConfig.CONFIG.enabled) { if (VoxyConfig.CONFIG.enabled) {
if (VoxyCommon.getInstance() != null) {
VoxyCommon.shutdownInstance();
}
VoxyCommon.createInstance(); VoxyCommon.createInstance();
} }
} }

View File

@@ -13,7 +13,7 @@ import java.util.List;
@Mixin(DebugHud.class) @Mixin(DebugHud.class)
public class MixinDebugHud { public class MixinDebugHud {
@Inject(method = "getRightText", at = @At("TAIL")) @Inject(method = "getRightText", at = @At("RETURN"))
private void injectDebug(CallbackInfoReturnable<List<String>> cir) { private void injectDebug(CallbackInfoReturnable<List<String>> cir) {
var ret = cir.getReturnValue(); var ret = cir.getReturnValue();
var instance = VoxyCommon.getInstance(); var instance = VoxyCommon.getInstance();

View File

@@ -0,0 +1,30 @@
package me.cortex.voxy.client.mixin.minecraft;
import me.cortex.voxy.client.config.VoxyConfig;
import me.cortex.voxy.client.core.IGetVoxyRenderSystem;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.render.fog.FogData;
import net.minecraft.client.render.fog.FogRenderer;
import org.objectweb.asm.Opcodes;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
@Mixin(FogRenderer.class)
public class MixinFogRenderer {
@Redirect(method = "applyFog(Lnet/minecraft/client/render/Camera;IZLnet/minecraft/client/render/RenderTickCounter;FLnet/minecraft/client/world/ClientWorld;)Lorg/joml/Vector4f;", at = @At(value = "FIELD", target = "Lnet/minecraft/client/render/fog/FogData;renderDistanceEnd:F", opcode = Opcodes.PUTFIELD), require = 0)
private void voxy$modifyFog(FogData instance, float distance) {
var vrs = (IGetVoxyRenderSystem) MinecraftClient.getInstance().worldRenderer;
if (VoxyConfig.CONFIG.renderVanillaFog || vrs == null || vrs.getVoxyRenderSystem() == null) {
instance.renderDistanceEnd = distance;
} else {
instance.renderDistanceStart = 999999999;
instance.renderDistanceEnd = 999999999;
if (!VoxyConfig.CONFIG.useEnvironmentalFog) {
instance.environmentalStart = 99999999;
instance.environmentalEnd = 99999999;
}
}
}
}

View File

@@ -1,12 +1,8 @@
package me.cortex.voxy.client.mixin.minecraft; package me.cortex.voxy.client.mixin.minecraft;
import me.cortex.voxy.client.VoxyClientInstance; import me.cortex.voxy.client.VoxyClientInstance;
import me.cortex.voxy.client.config.VoxyConfig;
import me.cortex.voxy.commonImpl.VoxyCommon; import me.cortex.voxy.commonImpl.VoxyCommon;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
import net.minecraft.client.RunArgs;
import net.minecraft.client.gui.screen.DownloadingTerrainScreen;
import net.minecraft.client.world.ClientWorld;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
@@ -16,7 +12,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
public class MixinMinecraftClient { public class MixinMinecraftClient {
@Inject(method = "disconnect(Lnet/minecraft/client/gui/screen/Screen;Z)V", at = @At("TAIL")) @Inject(method = "disconnect(Lnet/minecraft/client/gui/screen/Screen;Z)V", at = @At("TAIL"))
private void voxy$injectWorldClose(CallbackInfo ci) { private void voxy$injectWorldClose(CallbackInfo ci) {
if (VoxyCommon.isAvailable()) { if (VoxyCommon.isAvailable() && VoxyClientInstance.isInGame) {
VoxyCommon.shutdownInstance(); VoxyCommon.shutdownInstance();
VoxyClientInstance.isInGame = false; VoxyClientInstance.isInGame = false;
} }

View File

@@ -0,0 +1,23 @@
package me.cortex.voxy.client.mixin.minecraft;
import com.mojang.blaze3d.shaders.ShaderType;
import com.mojang.blaze3d.systems.RenderSystem;
import me.cortex.voxy.client.VoxyClient;
import net.minecraft.util.Identifier;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.function.BiFunction;
//Thanks iris for making me need todo this ;-; _irritater_
@Mixin(RenderSystem.class)
public class MixinRenderSystem {
//We need to inject before iris to initalize our systems
@Inject(method = "initRenderer", order = 900, remap = false, at = @At("RETURN"))
private static void voxy$injectInit(long windowHandle, int debugVerbosity, boolean sync, BiFunction<Identifier, ShaderType, String> shaderSourceGetter, boolean renderDebugLabels, CallbackInfo ci) {
VoxyClient.initVoxyClient();
}
}

View File

@@ -15,6 +15,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
public class MixinWindow { public class MixinWindow {
@Inject(method = "<init>", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/util/Window;throwOnGlError()V")) @Inject(method = "<init>", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/util/Window;throwOnGlError()V"))
private void injectInitWindow(WindowEventHandler eventHandler, MonitorTracker monitorTracker, WindowSettings settings, String fullscreenVideoMode, String title, CallbackInfo ci) { private void injectInitWindow(WindowEventHandler eventHandler, MonitorTracker monitorTracker, WindowSettings settings, String fullscreenVideoMode, String title, CallbackInfo ci) {
//System.load("C:\\Users\\Cortex\\Desktop\\minecraft\\mesabuild\\mesa\\builddir\\src\\gallium\\targets\\wgl\\libgallium_wgl.dll");
//System.load("C:\\Program Files\\RenderDoc\\renderdoc.dll"); //System.load("C:\\Program Files\\RenderDoc\\renderdoc.dll");
var prop = System.getProperty("voxy.forceGpuSelectionIndex", "NO"); var prop = System.getProperty("voxy.forceGpuSelectionIndex", "NO");
if (!prop.equals("NO")) { if (!prop.equals("NO")) {

View File

@@ -4,15 +4,15 @@ import me.cortex.voxy.client.VoxyClientInstance;
import me.cortex.voxy.client.config.VoxyConfig; import me.cortex.voxy.client.config.VoxyConfig;
import me.cortex.voxy.client.core.IGetVoxyRenderSystem; import me.cortex.voxy.client.core.IGetVoxyRenderSystem;
import me.cortex.voxy.client.core.VoxyRenderSystem; import me.cortex.voxy.client.core.VoxyRenderSystem;
import me.cortex.voxy.client.core.util.IrisUtil;
import me.cortex.voxy.common.Logger; import me.cortex.voxy.common.Logger;
import me.cortex.voxy.common.world.WorldEngine; import me.cortex.voxy.common.world.WorldEngine;
import me.cortex.voxy.commonImpl.VoxyCommon; import me.cortex.voxy.commonImpl.VoxyCommon;
import me.cortex.voxy.commonImpl.WorldIdentifier; import me.cortex.voxy.commonImpl.WorldIdentifier;
import net.minecraft.client.render.*; import net.minecraft.client.render.Frustum;
import net.minecraft.client.util.ObjectAllocator; import net.minecraft.client.render.WorldRenderer;
import net.minecraft.client.world.ClientWorld; import net.minecraft.client.world.ClientWorld;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.joml.Matrix4f;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.Unique;
@@ -62,7 +62,7 @@ public abstract class MixinWorldRenderer implements IGetVoxyRenderSystem {
@Override @Override
public void createRenderer() { public void createRenderer() {
if (this.renderer != null) throw new IllegalStateException("Cannot have multiple renderers"); if (this.renderer != null) throw new IllegalStateException("Cannot have multiple renderers");
if ((!VoxyConfig.CONFIG.enableRendering)||(!VoxyConfig.CONFIG.enabled)) { if (!VoxyConfig.CONFIG.isRenderingEnabled()) {
Logger.info("Not creating renderer due to disabled"); Logger.info("Not creating renderer due to disabled");
return; return;
} }
@@ -80,6 +80,14 @@ public abstract class MixinWorldRenderer implements IGetVoxyRenderSystem {
Logger.error("Null world selected"); Logger.error("Null world selected");
return; return;
} }
this.renderer = new VoxyRenderSystem(world, instance.getThreadPool()); try {
this.renderer = new VoxyRenderSystem(world, instance.getThreadPool());
} catch (RuntimeException e) {
if (IrisUtil.irisShaderPackEnabled()) {
IrisUtil.disableIrisShaders();
} else {
throw e;
}
}
} }
} }

Some files were not shown because too many files have changed in this diff Show More