Compare commits
88 Commits
mc_1215
...
mc_1217_me
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5c19a7f6d0 | ||
|
|
aff34fb463 | ||
|
|
94f2d03f90 | ||
|
|
45e2ea897f | ||
|
|
2023ac6ad2 | ||
|
|
91b5710e00 | ||
|
|
03b425eeb8 | ||
|
|
653dbc8a3a | ||
|
|
97058f24b4 | ||
|
|
e227d84306 | ||
|
|
f4e0dbb001 | ||
|
|
43d04febd5 | ||
|
|
1078507495 | ||
|
|
294e1f9fb6 | ||
|
|
5fe5ebc0a2 | ||
|
|
aff30961bd | ||
|
|
64d211b333 | ||
|
|
606d3b2282 | ||
|
|
132c6aa2e8 | ||
|
|
4a140c110f | ||
|
|
1c8d052544 | ||
|
|
3199b77ae5 | ||
|
|
f0e1f18379 | ||
|
|
492e2a707a | ||
|
|
7551ca3484 | ||
|
|
8f3fa2e7f2 | ||
|
|
936619ce12 | ||
|
|
d6a42f8ef3 | ||
|
|
bf43e405ff | ||
|
|
0c7c33304d | ||
|
|
f9b1d8a9e1 | ||
|
|
7b4fe4bd5c | ||
|
|
6ba3111ada | ||
|
|
258ccf89e0 | ||
|
|
3e193bb675 | ||
|
|
69b96eee96 | ||
|
|
e1ba2c4ebb | ||
|
|
dfce9dae46 | ||
|
|
f4fca865bb | ||
|
|
08fa0725d3 | ||
|
|
b92b769f7b | ||
|
|
726517a8b6 | ||
|
|
51f54c6edd | ||
|
|
f7f260777a | ||
|
|
dd9ac2819d | ||
|
|
1a7bb8498e | ||
|
|
fb2d26153d | ||
|
|
a640c0e62c | ||
|
|
784322db6f | ||
|
|
355a63c46f | ||
|
|
155eb75b82 | ||
|
|
64d4ef0c03 | ||
|
|
edb15db8fa | ||
|
|
883f140b41 | ||
|
|
90a6765e8a | ||
|
|
b8ede978c2 | ||
|
|
c1091acc6b | ||
|
|
0034940082 | ||
|
|
4d35fad772 | ||
|
|
d86c3b2eb8 | ||
|
|
b3556813a9 | ||
|
|
a94dcf1949 | ||
|
|
7fa07ae5ea | ||
|
|
cf60d31b75 | ||
|
|
e1b4e1ea6a | ||
|
|
4f6b0aa04d | ||
|
|
8b5e2780c7 | ||
|
|
0dd730d8de | ||
|
|
0f865c7afb | ||
|
|
688f24a409 | ||
|
|
dcacd279b3 | ||
|
|
37d0b755af | ||
|
|
26672ce34b | ||
|
|
d1be49f474 | ||
|
|
87072a4edc | ||
|
|
5f8679e5d2 | ||
|
|
1a7cd37741 | ||
|
|
ed181c1dcd | ||
|
|
4d839e3662 | ||
|
|
156b30756d | ||
|
|
6326870525 | ||
|
|
a360c9349a | ||
|
|
9e6276e0fa | ||
|
|
2bbc7a8999 | ||
|
|
fc3e05434f | ||
|
|
388764e9c8 | ||
|
|
3fb8323dd0 | ||
|
|
3aa1c94c6a |
33
.github/workflows/manual-artifact.yml
vendored
Normal file
33
.github/workflows/manual-artifact.yml
vendored
Normal 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
1
.gitignore
vendored
@@ -4,3 +4,4 @@
|
|||||||
/build/
|
/build/
|
||||||
/run/
|
/run/
|
||||||
/out/
|
/out/
|
||||||
|
/logs/
|
||||||
|
|||||||
46
build.gradle
46
build.gradle
@@ -1,5 +1,5 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id 'fabric-loom' version "1.10.1"
|
id 'fabric-loom' version "1.10-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,29 +105,29 @@ 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.7-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")
|
||||||
|
|
||||||
@@ -192,11 +210,13 @@ remapJar {
|
|||||||
delete fileTree(getDestinationDirectory().get())
|
delete fileTree(getDestinationDirectory().get())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!isInGHA) {
|
||||||
def hash = gitCommitHash();
|
def hash = gitCommitHash();
|
||||||
if (!hash.equals("<UnknownCommit>")) {
|
if (!hash.equals("<UnknownCommit>")) {
|
||||||
archiveClassifier.set(hash);
|
archiveClassifier.set(hash);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
project.ext.lwjglVersion = "3.3.3"
|
project.ext.lwjglVersion = "3.3.3"
|
||||||
project.ext.lwjglNatives = "natives-windows"
|
project.ext.lwjglNatives = "natives-windows"
|
||||||
|
|||||||
@@ -6,14 +6,14 @@ 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.7
|
||||||
yarn_mappings=1.21.5+build.1
|
yarn_mappings=1.21.7+build.1
|
||||||
loader_version=0.16.10
|
loader_version=0.16.14
|
||||||
|
|
||||||
# Fabric API
|
# Fabric API
|
||||||
fabric_version=0.119.5+1.21.5
|
fabric_version=0.128.1+1.21.7
|
||||||
|
|
||||||
# Mod Properties
|
# Mod Properties
|
||||||
mod_version = 0.2.0-alpha
|
mod_version = 0.2.3-alpha
|
||||||
maven_group = me.cortex
|
maven_group = me.cortex
|
||||||
archives_base_name = voxy
|
archives_base_name = voxy
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
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;
|
||||||
@@ -18,9 +20,20 @@ public class VoxyClient implements ClientModInitializer {
|
|||||||
@Override
|
@Override
|
||||||
public void onInitializeClient() {
|
public void onInitializeClient() {
|
||||||
ClientLifecycleEvents.CLIENT_STARTED.register(client->{
|
ClientLifecycleEvents.CLIENT_STARTED.register(client->{
|
||||||
|
Capabilities.init();//Ensure clinit is called
|
||||||
|
|
||||||
boolean systemSupported = Capabilities.INSTANCE.compute && Capabilities.INSTANCE.indirectParameters;
|
boolean systemSupported = Capabilities.INSTANCE.compute && Capabilities.INSTANCE.indirectParameters;
|
||||||
if (systemSupported) {
|
if (systemSupported) {
|
||||||
|
|
||||||
|
SharedIndexBuffer.INSTANCE.id();
|
||||||
|
BudgetBufferRenderer.init();
|
||||||
|
|
||||||
VoxyCommon.setInstanceFactory(VoxyClientInstance::new);
|
VoxyCommon.setInstanceFactory(VoxyClientInstance::new);
|
||||||
|
|
||||||
|
if (!Capabilities.INSTANCE.subgroup) {
|
||||||
|
Logger.warn("GPU does not support subgroup operations, expect some performance degradation");
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
Logger.error("Voxy is unsupported on your system.");
|
Logger.error("Voxy is unsupported on your system.");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ 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() {
|
public static VoxyConfig loadOrCreate() {
|
||||||
|
|||||||
@@ -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"))
|
||||||
|
|||||||
@@ -2,19 +2,15 @@ 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;
|
||||||
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.GlTexture;
|
||||||
import me.cortex.voxy.client.core.model.ModelBakerySubsystem;
|
|
||||||
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.RenderService;
|
||||||
import me.cortex.voxy.client.core.rendering.building.RenderDataFactory;
|
|
||||||
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.post.PostProcessing;
|
||||||
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;
|
||||||
@@ -24,31 +20,20 @@ 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 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.Camera;
|
||||||
import net.minecraft.client.render.Frustum;
|
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.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.glFinish;
|
||||||
import static org.lwjgl.opengl.GL14.glBlendFuncSeparate;
|
|
||||||
import static org.lwjgl.opengl.GL30C.GL_DRAW_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.GL33.glBindSampler;
|
import static org.lwjgl.opengl.GL33.glBindSampler;
|
||||||
@@ -69,10 +54,6 @@ public class VoxyRenderSystem {
|
|||||||
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.renderer = new RenderService(world, threadPool);
|
||||||
this.postProcessing = new PostProcessing();
|
this.postProcessing = new PostProcessing();
|
||||||
@@ -143,7 +124,7 @@ public class VoxyRenderSystem {
|
|||||||
).mulLocal(makeProjectionMatrix(16, 16*3000));
|
).mulLocal(makeProjectionMatrix(16, 16*3000));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void renderOpaque(ChunkRenderMatrices matrices, double cameraX, double cameraY, double cameraZ) {
|
public void renderOpaque(ChunkRenderMatrices matrices, FogParameters fogParameters, double cameraX, double cameraY, double cameraZ) {
|
||||||
if (IrisUtil.irisShadowActive()) {
|
if (IrisUtil.irisShadowActive()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -180,11 +161,13 @@ public class VoxyRenderSystem {
|
|||||||
int[] dims = new int[4];
|
int[] dims = new int[4];
|
||||||
glGetIntegerv(GL_VIEWPORT, dims);
|
glGetIntegerv(GL_VIEWPORT, dims);
|
||||||
var viewport = this.renderer.getViewport();
|
var viewport = this.renderer.getViewport();
|
||||||
|
|
||||||
viewport
|
viewport
|
||||||
.setProjection(projection)
|
.setProjection(projection)
|
||||||
.setModelView(new Matrix4f(matrices.modelView()))
|
.setModelView(new Matrix4f(matrices.modelView()))
|
||||||
.setCamera(cameraX, cameraY, cameraZ)
|
.setCamera(cameraX, cameraY, cameraZ)
|
||||||
.setScreenSize(dims[2], dims[3])
|
.setScreenSize(dims[2], dims[3])
|
||||||
|
.setFogParameters(fogParameters)
|
||||||
.update();
|
.update();
|
||||||
viewport.frameId++;
|
viewport.frameId++;
|
||||||
|
|
||||||
@@ -211,7 +194,7 @@ public class VoxyRenderSystem {
|
|||||||
|
|
||||||
|
|
||||||
TimingStatistics.F.start();
|
TimingStatistics.F.start();
|
||||||
this.postProcessing.renderPost(projection, matrices.projection(), boundFB);
|
this.postProcessing.renderPost(viewport, matrices.projection(), boundFB);
|
||||||
TimingStatistics.F.stop();
|
TimingStatistics.F.stop();
|
||||||
|
|
||||||
TimingStatistics.main.stop();
|
TimingStatistics.main.stop();
|
||||||
|
|||||||
@@ -24,10 +24,12 @@ 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 Capabilities() {
|
public Capabilities() {
|
||||||
var cap = GL.getCapabilities();
|
var cap = GL.getCapabilities();
|
||||||
this.compute = cap.glDispatchComputeIndirect != 0;
|
this.compute = cap.glDispatchComputeIndirect != 0;
|
||||||
|
this.subgroup = cap.GL_KHR_shader_subgroup;
|
||||||
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;
|
||||||
this.meshShaders = cap.GL_NV_mesh_shader;
|
this.meshShaders = cap.GL_NV_mesh_shader;
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
|||||||
@@ -101,8 +101,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");
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import static org.lwjgl.opengl.GL30.glBindBufferRange;
|
|||||||
import static org.lwjgl.opengl.GL31.GL_UNIFORM_BUFFER;
|
import static org.lwjgl.opengl.GL31.GL_UNIFORM_BUFFER;
|
||||||
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.GL43.GL_SHADER_STORAGE_BUFFER;
|
||||||
|
import static org.lwjgl.opengl.GL44.*;
|
||||||
|
|
||||||
|
|
||||||
//TODO: rewrite the entire shader builder system
|
//TODO: rewrite the entire shader builder system
|
||||||
@@ -26,6 +27,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 +73,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 +97,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 +114,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();
|
||||||
|
|||||||
@@ -4,8 +4,11 @@ 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.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;
|
||||||
@@ -149,6 +152,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 +174,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);
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,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.BlockRenderLayer;
|
||||||
import net.minecraft.client.render.RenderLayer;
|
import net.minecraft.client.render.RenderLayer;
|
||||||
import net.minecraft.client.render.RenderLayers;
|
import net.minecraft.client.render.RenderLayers;
|
||||||
import net.minecraft.fluid.FluidState;
|
import net.minecraft.fluid.FluidState;
|
||||||
@@ -256,19 +257,19 @@ 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) {
|
if (Capabilities.INSTANCE.isMesa) {
|
||||||
//Mesa does not work with GL_DEPTH_STENCIL_TEXTURE_MODE GL_STENCIL_INDEX
|
//Mesa does not work with GL_DEPTH_STENCIL_TEXTURE_MODE GL_STENCIL_INDEX
|
||||||
@@ -339,7 +340,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 +374,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 +394,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 +412,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,8 +436,8 @@ 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);
|
||||||
@@ -679,7 +680,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
|
||||||
|
|||||||
@@ -24,14 +24,14 @@ 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");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
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, Integer.numberOfTrailingZeros(ModelFactory.MODEL_TEXTURE_SIZE));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -46,6 +46,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 +71,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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -1,12 +1,9 @@
|
|||||||
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;
|
||||||
@@ -21,6 +18,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 +42,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()
|
||||||
@@ -79,7 +81,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
|
||||||
@@ -163,13 +165,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 +191,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 {
|
||||||
@@ -311,7 +313,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 +337,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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,10 +10,8 @@ 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.AsyncNodeManager;
|
||||||
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.hierachical.NodeCleaner;
|
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.*;
|
||||||
import me.cortex.voxy.client.core.rendering.section.geometry.*;
|
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.DownloadStream;
|
||||||
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;
|
||||||
@@ -58,6 +56,10 @@ public class RenderService<T extends AbstractSectionRenderer<J, Q>, J extends Vi
|
|||||||
}
|
}
|
||||||
//geometryCapacity = 1<<28;
|
//geometryCapacity = 1<<28;
|
||||||
//geometryCapacity = 1<<30;//1GB test
|
//geometryCapacity = 1<<30;//1GB test
|
||||||
|
var override = System.getProperty("voxy.geometryBufferSizeOverrideMB", "");
|
||||||
|
if (!override.isEmpty()) {
|
||||||
|
geometryCapacity = Long.parseLong(override)*1024L*1024L;
|
||||||
|
}
|
||||||
return geometryCapacity;
|
return geometryCapacity;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,7 +73,9 @@ public class RenderService<T extends AbstractSectionRenderer<J, Q>, J extends Vi
|
|||||||
this.geometryData = (Q) new BasicSectionGeometryData(1<<20, geometryCapacity);
|
this.geometryData = (Q) new BasicSectionGeometryData(1<<20, geometryCapacity);
|
||||||
|
|
||||||
//Max sections: ~500k
|
//Max sections: ~500k
|
||||||
this.sectionRenderer = (T) new MDICSectionRenderer(this.modelService.getStore(), (BasicSectionGeometryData) this.geometryData);
|
//this.sectionRenderer = (T) new MDICSectionRenderer(this.modelService.getStore(), (BasicSectionGeometryData) this.geometryData);
|
||||||
|
//this.sectionRenderer = (T) new MeshSectionRenderer(this.modelService.getStore(), (BasicSectionGeometryData) this.geometryData);
|
||||||
|
this.sectionRenderer = (T) new MeshEXTSectionRenderer(this.modelService.getStore(), (BasicSectionGeometryData) this.geometryData);
|
||||||
Logger.info("Using renderer: " + this.sectionRenderer.getClass().getSimpleName() + " with geometry buffer of: " + geometryCapacity + " bytes");
|
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
|
//Do something incredibly hacky, we dont need to keep the reference to this around, so just connect and discard
|
||||||
|
|||||||
@@ -2,12 +2,15 @@ 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.HiZBuffer;
|
import me.cortex.voxy.client.core.rendering.util.HiZBuffer;
|
||||||
|
import me.cortex.voxy.client.core.rendering.util.HiZBuffer2;
|
||||||
|
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();
|
||||||
private static final Field planesField;
|
private static final Field planesField;
|
||||||
static {
|
static {
|
||||||
@@ -29,6 +32,7 @@ public abstract class Viewport <A extends Viewport<A>> {
|
|||||||
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();
|
||||||
@@ -75,6 +79,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);
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -1607,6 +1608,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();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -246,7 +246,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;
|
||||||
|
|||||||
@@ -7,11 +7,10 @@ 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.util.MemoryBuffer;
|
import me.cortex.voxy.common.util.MemoryBuffer;
|
||||||
import me.cortex.voxy.common.world.WorldEngine;
|
import me.cortex.voxy.common.world.WorldEngine;
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ 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 java.util.function.Function;
|
||||||
|
|
||||||
import static org.lwjgl.opengl.GL11C.GL_TRIANGLES;
|
import static org.lwjgl.opengl.GL11C.GL_TRIANGLES;
|
||||||
import static org.lwjgl.opengl.GL11C.glDrawArrays;
|
import static org.lwjgl.opengl.GL11C.glDrawArrays;
|
||||||
import static org.lwjgl.opengl.GL30C.glBindVertexArray;
|
import static org.lwjgl.opengl.GL30C.glBindVertexArray;
|
||||||
@@ -13,9 +15,12 @@ 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);
|
||||||
|
}
|
||||||
|
public <T extends Shader> FullscreenBlit(String fragId, Function<Shader.Builder<T>, Shader.Builder<T>> builder) {
|
||||||
|
this.shader = builder.apply((Shader.Builder<T>) Shader.make()
|
||||||
.add(ShaderType.VERTEX, "voxy:post/fullscreen.vert")
|
.add(ShaderType.VERTEX, "voxy:post/fullscreen.vert")
|
||||||
.add(ShaderType.FRAGMENT, fragId)
|
.add(ShaderType.FRAGMENT, fragId))
|
||||||
.compile();
|
.compile();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
package me.cortex.voxy.client.core.rendering.post;
|
package me.cortex.voxy.client.core.rendering.post;
|
||||||
|
|
||||||
|
import me.cortex.voxy.client.config.VoxyConfig;
|
||||||
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 me.cortex.voxy.client.core.rendering.Viewport;
|
||||||
import me.cortex.voxy.client.core.rendering.util.GlStateCapture;
|
import me.cortex.voxy.client.core.rendering.util.GlStateCapture;
|
||||||
import net.minecraft.client.util.math.MatrixStack;
|
|
||||||
import org.joml.Matrix4f;
|
import org.joml.Matrix4f;
|
||||||
import org.joml.Matrix4fc;
|
import org.joml.Matrix4fc;
|
||||||
import org.lwjgl.opengl.GL11C;
|
|
||||||
|
|
||||||
import static org.lwjgl.opengl.ARBComputeShader.glDispatchCompute;
|
import static org.lwjgl.opengl.ARBComputeShader.glDispatchCompute;
|
||||||
import static org.lwjgl.opengl.ARBShaderImageLoadStore.glBindImageTexture;
|
import static org.lwjgl.opengl.ARBShaderImageLoadStore.glBindImageTexture;
|
||||||
@@ -20,6 +20,7 @@ import static org.lwjgl.opengl.GL43.GL_DEPTH_STENCIL_TEXTURE_MODE;
|
|||||||
import static org.lwjgl.opengl.GL45C.*;
|
import static org.lwjgl.opengl.GL45C.*;
|
||||||
|
|
||||||
public class PostProcessing {
|
public class PostProcessing {
|
||||||
|
private final boolean useEnvFog = VoxyConfig.CONFIG.useEnvironmentalFog;
|
||||||
private final GlFramebuffer framebuffer;
|
private final GlFramebuffer framebuffer;
|
||||||
private final GlFramebuffer framebufferSSAO;
|
private final GlFramebuffer framebufferSSAO;
|
||||||
private int width;
|
private int width;
|
||||||
@@ -31,7 +32,8 @@ public class PostProcessing {
|
|||||||
private final FullscreenBlit setDepth0 = new FullscreenBlit("voxy:post/depth0.frag");
|
private final FullscreenBlit setDepth0 = new FullscreenBlit("voxy:post/depth0.frag");
|
||||||
private final FullscreenBlit emptyBlit = new FullscreenBlit("voxy:post/noop.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_cutout.frag");
|
||||||
private final FullscreenBlit blitTexture = new FullscreenBlit("voxy:post/blit_texture_depth_cutout.frag");
|
private final FullscreenBlit blitTexture = new FullscreenBlit("voxy:post/blit_texture_depth_cutout.frag",
|
||||||
|
a->a.defineIf("USE_ENV_FOG", useEnvFog));
|
||||||
private final Shader ssaoComp = Shader.make()
|
private final Shader ssaoComp = Shader.make()
|
||||||
.add(ShaderType.COMPUTE, "voxy:post/ssao.comp")
|
.add(ShaderType.COMPUTE, "voxy:post/ssao.comp")
|
||||||
.compile();
|
.compile();
|
||||||
@@ -158,7 +160,7 @@ public class PostProcessing {
|
|||||||
|
|
||||||
|
|
||||||
//Executes the post processing and emits to whatever framebuffer is currently bound via a blit
|
//Executes the post processing and emits to whatever framebuffer is currently bound via a blit
|
||||||
public void renderPost(Matrix4f fromProjection, Matrix4fc tooProjection, int outputFB) {
|
public void renderPost(Viewport vp, Matrix4fc tooProjection, int outputFB) {
|
||||||
glDisable(GL_STENCIL_TEST);
|
glDisable(GL_STENCIL_TEST);
|
||||||
|
|
||||||
|
|
||||||
@@ -172,12 +174,17 @@ public class PostProcessing {
|
|||||||
this.blitTexture.bind();
|
this.blitTexture.bind();
|
||||||
|
|
||||||
float[] data = new float[4*4];
|
float[] data = new float[4*4];
|
||||||
var mat = new Matrix4f(fromProjection).invert();
|
new Matrix4f(vp.MVP).invert().get(data);
|
||||||
mat.get(data);
|
|
||||||
glUniformMatrix4fv(2, false, data);//inverse fromProjection
|
glUniformMatrix4fv(2, false, data);//inverse fromProjection
|
||||||
tooProjection.get(data);
|
new Matrix4f(tooProjection).mul(vp.modelView).get(data);
|
||||||
glUniformMatrix4fv(3, false, data);//tooProjection
|
glUniformMatrix4fv(3, false, data);//tooProjection
|
||||||
|
if (useEnvFog) {
|
||||||
|
float start = vp.fogParameters.environmentalStart();
|
||||||
|
float end = vp.fogParameters.environmentalEnd();
|
||||||
|
float invEndFogDelta = 1f/(end-start);
|
||||||
|
glUniform3f(4, vp.fogParameters.environmentalEnd()/2f, invEndFogDelta, start*invEndFogDelta);
|
||||||
|
glUniform3f(5, vp.fogParameters.red(), vp.fogParameters.green(), vp.fogParameters.blue());
|
||||||
|
}
|
||||||
|
|
||||||
glBindTextureUnit(0, this.didSSAO?this.colourSSAO.id:this.colour.id);
|
glBindTextureUnit(0, this.didSSAO?this.colourSSAO.id:this.colour.id);
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ 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();
|
||||||
|
|||||||
@@ -0,0 +1,227 @@
|
|||||||
|
package me.cortex.voxy.client.core.rendering.section;
|
||||||
|
|
||||||
|
|
||||||
|
import me.cortex.voxy.client.RenderStatistics;
|
||||||
|
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.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.RenderService;
|
||||||
|
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();
|
||||||
|
|
||||||
|
public MeshEXTSectionRenderer(ModelStore modelStore, BasicSectionGeometryData geometryData) {
|
||||||
|
super(modelStore, geometryData);
|
||||||
|
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, GlTexture depthBoundTexture) {
|
||||||
|
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, depthBoundTexture.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, GlTexture depthBoundTexture) {
|
||||||
|
//RenderLayer.getCutoutMipped().startDrawing();
|
||||||
|
glDisable(GL_CULL_FACE);
|
||||||
|
glEnable(GL_DEPTH_TEST);
|
||||||
|
this.terrainShader.bind();
|
||||||
|
this.bindRenderingBuffers(viewport, depthBoundTexture);
|
||||||
|
|
||||||
|
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, GlTexture dbt) {
|
||||||
|
if (this.geometryManager.getSectionCount() == 0) return;
|
||||||
|
|
||||||
|
this.uploadUniformBuffer(viewport);
|
||||||
|
|
||||||
|
this.renderTerrain(viewport, dbt);
|
||||||
|
|
||||||
|
//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, GlTexture depthBoundTexture) {
|
||||||
|
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(RenderService.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, GlTexture dbt) {
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,228 @@
|
|||||||
|
package me.cortex.voxy.client.core.rendering.section;
|
||||||
|
|
||||||
|
|
||||||
|
import me.cortex.voxy.client.RenderStatistics;
|
||||||
|
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.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.RenderService;
|
||||||
|
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 org.lwjgl.opengl.ARBIndirectParameters.GL_PARAMETER_BUFFER_ARB;
|
||||||
|
import static org.lwjgl.opengl.ARBIndirectParameters.glMultiDrawElementsIndirectCountARB;
|
||||||
|
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.GL45C.glClearNamedBufferData;
|
||||||
|
import static org.lwjgl.opengl.NVMeshShader.glDrawMeshTasksIndirectNV;
|
||||||
|
import static org.lwjgl.opengl.NVRepresentativeFragmentTest.GL_REPRESENTATIVE_FRAGMENT_TEST_NV;
|
||||||
|
|
||||||
|
//Uses MDIC to render the sections
|
||||||
|
public class MeshSectionRenderer 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/mesh/task.glsl")
|
||||||
|
.add(ShaderType.MESH, "voxy:lod/mesh/mesh.glsl")
|
||||||
|
.add(ShaderType.FRAGMENT, "voxy:lod/mesh/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(7*4).zero();//TODO: this needs tobe in the viewport
|
||||||
|
|
||||||
|
//Statistics
|
||||||
|
private final GlBuffer statisticsBuffer = new GlBuffer(1024).zero();
|
||||||
|
|
||||||
|
public MeshSectionRenderer(ModelStore modelStore, BasicSectionGeometryData geometryData) {
|
||||||
|
super(modelStore, geometryData);
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
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, GlTexture depthBoundTexture) {
|
||||||
|
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, depthBoundTexture.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, GlTexture depthBoundTexture) {
|
||||||
|
//RenderLayer.getCutoutMipped().startDrawing();
|
||||||
|
glDisable(GL_CULL_FACE);
|
||||||
|
glEnable(GL_DEPTH_TEST);
|
||||||
|
this.terrainShader.bind();
|
||||||
|
this.bindRenderingBuffers(viewport, depthBoundTexture);
|
||||||
|
|
||||||
|
glMemoryBarrier(GL_COMMAND_BARRIER_BIT|GL_SHADER_STORAGE_BARRIER_BIT);//Barrier everything is needed
|
||||||
|
|
||||||
|
glDrawMeshTasksIndirectNV(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, GlTexture dbt) {
|
||||||
|
if (this.geometryManager.getSectionCount() == 0) return;
|
||||||
|
|
||||||
|
this.uploadUniformBuffer(viewport);
|
||||||
|
|
||||||
|
this.renderTerrain(viewport, dbt);
|
||||||
|
|
||||||
|
//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, GlTexture depthBoundTexture) {
|
||||||
|
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(RenderService.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, GlTexture dbt) {
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,149 @@
|
|||||||
|
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.shader.Shader;
|
||||||
|
import me.cortex.voxy.client.core.gl.shader.ShaderType;
|
||||||
|
import me.cortex.voxy.client.core.rendering.RenderService;
|
||||||
|
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.GL30C.glBindVertexArray;
|
||||||
|
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(RenderService.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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,6 +15,6 @@ 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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,13 +18,16 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
|||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
@Mixin(ClientLoginNetworkHandler.class)
|
@Mixin(ClientPlayNetworkHandler.class)
|
||||||
public class MixinClientLoginNetworkHandler {
|
public class MixinClientLoginNetworkHandler {
|
||||||
@Inject(method = "<init>", at = @At(value = "TAIL"))
|
@Inject(method = "onGameJoin", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/network/ClientPlayerInteractionManager;<init>(Lnet/minecraft/client/MinecraftClient;Lnet/minecraft/client/network/ClientPlayNetworkHandler;)V", shift = At.Shift.BY, by = 2))
|
||||||
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 = true;
|
VoxyClientInstance.isInGame = true;
|
||||||
if (VoxyConfig.CONFIG.enabled) {
|
if (VoxyConfig.CONFIG.enabled) {
|
||||||
|
if (VoxyCommon.getInstance() != null) {
|
||||||
|
VoxyCommon.shutdownInstance();
|
||||||
|
}
|
||||||
VoxyCommon.createInstance();
|
VoxyCommon.createInstance();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package me.cortex.voxy.client.mixin.nvidium;
|
||||||
|
|
||||||
|
import me.cortex.nvidium.RenderPipeline;
|
||||||
|
import me.cortex.voxy.client.core.IGetVoxyRenderSystem;
|
||||||
|
import net.caffeinemc.mods.sodium.client.render.chunk.ChunkRenderMatrices;
|
||||||
|
import net.caffeinemc.mods.sodium.client.render.chunk.terrain.TerrainRenderPass;
|
||||||
|
import net.caffeinemc.mods.sodium.client.render.viewport.Viewport;
|
||||||
|
import net.caffeinemc.mods.sodium.client.util.FogParameters;
|
||||||
|
import net.minecraft.client.MinecraftClient;
|
||||||
|
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 = RenderPipeline.class, remap = false)
|
||||||
|
public class MixinRenderPipeline {
|
||||||
|
@Inject(method = "renderFrame", at = @At("RETURN"))
|
||||||
|
private void voxy$injectRender(TerrainRenderPass pass, Viewport frustum, FogParameters fogParameters, ChunkRenderMatrices crm, double px, double py, double pz, CallbackInfo ci) {
|
||||||
|
var renderer = ((IGetVoxyRenderSystem) MinecraftClient.getInstance().worldRenderer).getVoxyRenderSystem();
|
||||||
|
if (renderer != null) {
|
||||||
|
renderer.renderOpaque(crm, fogParameters, px, py, pz);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,6 +8,7 @@ import net.caffeinemc.mods.sodium.client.render.chunk.lists.ChunkRenderListItera
|
|||||||
import net.caffeinemc.mods.sodium.client.render.chunk.terrain.DefaultTerrainRenderPasses;
|
import net.caffeinemc.mods.sodium.client.render.chunk.terrain.DefaultTerrainRenderPasses;
|
||||||
import net.caffeinemc.mods.sodium.client.render.chunk.terrain.TerrainRenderPass;
|
import net.caffeinemc.mods.sodium.client.render.chunk.terrain.TerrainRenderPass;
|
||||||
import net.caffeinemc.mods.sodium.client.render.viewport.CameraTransform;
|
import net.caffeinemc.mods.sodium.client.render.viewport.CameraTransform;
|
||||||
|
import net.caffeinemc.mods.sodium.client.util.FogParameters;
|
||||||
import net.minecraft.client.MinecraftClient;
|
import net.minecraft.client.MinecraftClient;
|
||||||
import net.minecraft.client.util.math.MatrixStack;
|
import net.minecraft.client.util.math.MatrixStack;
|
||||||
import org.joml.Matrix4f;
|
import org.joml.Matrix4f;
|
||||||
@@ -20,11 +21,11 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
|||||||
public class MixinDefaultChunkRenderer {
|
public class MixinDefaultChunkRenderer {
|
||||||
|
|
||||||
@Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/caffeinemc/mods/sodium/client/render/chunk/ShaderChunkRenderer;end(Lnet/caffeinemc/mods/sodium/client/render/chunk/terrain/TerrainRenderPass;)V", shift = At.Shift.BEFORE))
|
@Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/caffeinemc/mods/sodium/client/render/chunk/ShaderChunkRenderer;end(Lnet/caffeinemc/mods/sodium/client/render/chunk/terrain/TerrainRenderPass;)V", shift = At.Shift.BEFORE))
|
||||||
private void injectRender(ChunkRenderMatrices matrices, CommandList commandList, ChunkRenderListIterable renderLists, TerrainRenderPass renderPass, CameraTransform camera, CallbackInfo ci) {
|
private void injectRender(ChunkRenderMatrices matrices, CommandList commandList, ChunkRenderListIterable renderLists, TerrainRenderPass renderPass, CameraTransform camera, FogParameters fogParameters, CallbackInfo ci) {
|
||||||
if (renderPass == DefaultTerrainRenderPasses.CUTOUT) {
|
if (renderPass == DefaultTerrainRenderPasses.CUTOUT) {
|
||||||
var renderer = ((IGetVoxyRenderSystem) MinecraftClient.getInstance().worldRenderer).getVoxyRenderSystem();
|
var renderer = ((IGetVoxyRenderSystem) MinecraftClient.getInstance().worldRenderer).getVoxyRenderSystem();
|
||||||
if (renderer != null) {
|
if (renderer != null) {
|
||||||
renderer.renderOpaque(matrices, camera.x, camera.y, camera.z);
|
renderer.renderOpaque(matrices, fogParameters, camera.x, camera.y, camera.z);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,25 +1,23 @@
|
|||||||
package me.cortex.voxy.client.mixin.sodium;
|
package me.cortex.voxy.client.mixin.sodium;
|
||||||
|
|
||||||
import com.llamalad7.mixinextras.injector.ModifyReturnValue;
|
import me.cortex.voxy.client.ICheekyClientChunkManager;
|
||||||
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.common.world.WorldEngine;
|
|
||||||
import me.cortex.voxy.common.world.service.VoxelIngestService;
|
import me.cortex.voxy.common.world.service.VoxelIngestService;
|
||||||
import me.cortex.voxy.commonImpl.VoxyCommon;
|
|
||||||
import me.cortex.voxy.commonImpl.WorldIdentifier;
|
|
||||||
import net.caffeinemc.mods.sodium.client.gl.device.CommandList;
|
import net.caffeinemc.mods.sodium.client.gl.device.CommandList;
|
||||||
import net.caffeinemc.mods.sodium.client.render.chunk.RenderSection;
|
import net.caffeinemc.mods.sodium.client.render.chunk.RenderSection;
|
||||||
import net.caffeinemc.mods.sodium.client.render.chunk.RenderSectionFlags;
|
|
||||||
import net.caffeinemc.mods.sodium.client.render.chunk.RenderSectionManager;
|
import net.caffeinemc.mods.sodium.client.render.chunk.RenderSectionManager;
|
||||||
import net.caffeinemc.mods.sodium.client.render.chunk.data.BuiltSectionInfo;
|
import net.caffeinemc.mods.sodium.client.render.chunk.data.BuiltSectionInfo;
|
||||||
|
import net.fabricmc.loader.api.FabricLoader;
|
||||||
import net.minecraft.client.world.ClientWorld;
|
import net.minecraft.client.world.ClientWorld;
|
||||||
import net.minecraft.util.math.ChunkPos;
|
import net.minecraft.util.math.ChunkPos;
|
||||||
import net.minecraft.util.math.ChunkSectionPos;
|
import net.minecraft.util.math.ChunkSectionPos;
|
||||||
|
import net.minecraft.world.chunk.WorldChunk;
|
||||||
import org.spongepowered.asm.mixin.Final;
|
import org.spongepowered.asm.mixin.Final;
|
||||||
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.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.Redirect;
|
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||||
@@ -27,6 +25,9 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
|||||||
|
|
||||||
@Mixin(value = RenderSectionManager.class, remap = false)
|
@Mixin(value = RenderSectionManager.class, remap = false)
|
||||||
public class MixinRenderSectionManager {
|
public class MixinRenderSectionManager {
|
||||||
|
@Unique
|
||||||
|
private static final boolean BOBBY_INSTALLED = FabricLoader.getInstance().isModLoaded("bobby");
|
||||||
|
|
||||||
@Shadow @Final private ClientWorld level;
|
@Shadow @Final private ClientWorld level;
|
||||||
|
|
||||||
@Inject(method = "<init>", at = @At("TAIL"))
|
@Inject(method = "<init>", at = @At("TAIL"))
|
||||||
@@ -39,6 +40,20 @@ public class MixinRenderSectionManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Inject(method = "onChunkRemoved", at = @At("HEAD"))
|
||||||
|
private void injectIngest(int x, int z, CallbackInfo ci) {
|
||||||
|
//TODO: Am not quite sure if this is right
|
||||||
|
if (VoxyConfig.CONFIG.ingestEnabled && !BOBBY_INSTALLED) {
|
||||||
|
var cccm = (ICheekyClientChunkManager)this.level.getChunkManager();
|
||||||
|
if (cccm != null) {
|
||||||
|
var chunk = cccm.voxy$cheekyGetChunk(x, z);
|
||||||
|
if (chunk != null) {
|
||||||
|
VoxelIngestService.tryAutoIngestChunk(chunk);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@Inject(method = "onChunkAdded", at = @At("HEAD"))
|
@Inject(method = "onChunkAdded", at = @At("HEAD"))
|
||||||
private void voxy$trackChunkAdd(int x, int z, CallbackInfo ci) {
|
private void voxy$trackChunkAdd(int x, int z, CallbackInfo ci) {
|
||||||
@@ -61,14 +76,6 @@ public class MixinRenderSectionManager {
|
|||||||
}
|
}
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
@Inject(method = "onChunkRemoved", at = @At("HEAD"))
|
|
||||||
private void injectIngest(int x, int z, CallbackInfo ci) {
|
|
||||||
//TODO: Am not quite sure if this is right
|
|
||||||
if (VoxyConfig.CONFIG.ingestEnabled) {
|
|
||||||
VoxelIngestService.tryAutoIngestChunk(this.level.getChunk(x, z));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Redirect(method = "updateSectionInfo", at = @At(value = "INVOKE", target = "Lnet/caffeinemc/mods/sodium/client/render/chunk/RenderSection;setInfo(Lnet/caffeinemc/mods/sodium/client/render/chunk/data/BuiltSectionInfo;)Z"))
|
@Redirect(method = "updateSectionInfo", at = @At(value = "INVOKE", target = "Lnet/caffeinemc/mods/sodium/client/render/chunk/RenderSection;setInfo(Lnet/caffeinemc/mods/sodium/client/render/chunk/data/BuiltSectionInfo;)Z"))
|
||||||
private boolean voxy$updateOnUpload(RenderSection instance, BuiltSectionInfo info) {
|
private boolean voxy$updateOnUpload(RenderSection instance, BuiltSectionInfo info) {
|
||||||
boolean wasBuilt = instance.isBuilt();
|
boolean wasBuilt = instance.isBuilt();
|
||||||
|
|||||||
@@ -7,10 +7,10 @@ import me.cortex.voxy.common.world.SaveLoadSystem;
|
|||||||
|
|
||||||
import java.lang.ref.Cleaner;
|
import java.lang.ref.Cleaner;
|
||||||
|
|
||||||
|
import static me.cortex.voxy.common.util.GlobalCleaner.CLEANER;
|
||||||
import static org.lwjgl.util.zstd.Zstd.*;
|
import static org.lwjgl.util.zstd.Zstd.*;
|
||||||
|
|
||||||
public class ZSTDCompressor implements StorageCompressor {
|
public class ZSTDCompressor implements StorageCompressor {
|
||||||
private static final Cleaner CLEANER = Cleaner.create();
|
|
||||||
private record Ref(long ptr) {}
|
private record Ref(long ptr) {}
|
||||||
|
|
||||||
private static Ref createCleanableCompressionContext() {
|
private static Ref createCleanableCompressionContext() {
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ public class ServiceSlice extends TrackedObject {
|
|||||||
//Check that we are still alive
|
//Check that we are still alive
|
||||||
if (!this.alive) {
|
if (!this.alive) {
|
||||||
if (this.activeCount.decrementAndGet() < 0) {
|
if (this.activeCount.decrementAndGet() < 0) {
|
||||||
throw new IllegalStateException("Alive count negative!");
|
throw new IllegalStateException("Alive count negative!:" + this.name);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -77,14 +77,14 @@ public class ServiceSlice extends TrackedObject {
|
|||||||
try {
|
try {
|
||||||
ctx.run();
|
ctx.run();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Logger.error("Unexpected error occurred while executing a service job, expect things to break badly", e);
|
Logger.error("Unexpected error occurred while executing a service job, expect things to break badly: " + this.name, e);
|
||||||
MinecraftClient.getInstance().execute(()->MinecraftClient.getInstance().player.sendMessage(Text.literal("A voxy service had an exception while executing please check logs and report error"), true));
|
MinecraftClient.getInstance().execute(()->MinecraftClient.getInstance().player.sendMessage(Text.literal("A voxy service had an exception while executing please check logs and report error"), true));
|
||||||
} finally {
|
} finally {
|
||||||
if (this.activeCount.decrementAndGet() < 0) {
|
if (this.activeCount.decrementAndGet() < 0) {
|
||||||
throw new IllegalStateException("Alive count negative!");
|
throw new IllegalStateException("Alive count negative!: " + this.name);
|
||||||
}
|
}
|
||||||
if (this.jobCount2.decrementAndGet() < 0) {
|
if (this.jobCount2.decrementAndGet() < 0) {
|
||||||
throw new IllegalStateException("Job count negative!");
|
throw new IllegalStateException("Job count negative!" + this.name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@@ -96,9 +96,10 @@ public class ServiceSlice extends TrackedObject {
|
|||||||
Logger.error("Tried to do work on a dead service: " + this.name, new Throwable());
|
Logger.error("Tried to do work on a dead service: " + this.name, new Throwable());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.jobCount.release();
|
this.threadPool.addWeight(this);
|
||||||
this.jobCount2.incrementAndGet();
|
this.jobCount2.incrementAndGet();
|
||||||
this.threadPool.execute(this);
|
this.jobCount.release();
|
||||||
|
this.threadPool.execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void shutdown() {
|
public void shutdown() {
|
||||||
@@ -145,7 +146,7 @@ public class ServiceSlice extends TrackedObject {
|
|||||||
|
|
||||||
public void blockTillEmpty() {
|
public void blockTillEmpty() {
|
||||||
while (this.activeCount.get() != 0 && this.alive) {
|
while (this.activeCount.get() != 0 && this.alive) {
|
||||||
while (this.jobCount2.get() != 0 && this.alive) {
|
while ((this.jobCount2.get() != 0 || this.jobCount.availablePermits()!=0) && this.alive) {
|
||||||
Thread.onSpinWait();
|
Thread.onSpinWait();
|
||||||
try {
|
try {
|
||||||
Thread.sleep(10);
|
Thread.sleep(10);
|
||||||
@@ -163,7 +164,7 @@ public class ServiceSlice extends TrackedObject {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (this.jobCount2.decrementAndGet() < 0) {
|
if (this.jobCount2.decrementAndGet() < 0) {
|
||||||
throw new IllegalStateException("Job count negative!!!");
|
throw new IllegalStateException("Job count negative!!!:" + this.name);
|
||||||
}
|
}
|
||||||
this.threadPool.steal(this, 1);
|
this.threadPool.steal(this, 1);
|
||||||
return true;
|
return true;
|
||||||
@@ -176,7 +177,7 @@ public class ServiceSlice extends TrackedObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this.jobCount2.addAndGet(-count) < 0) {
|
if (this.jobCount2.addAndGet(-count) < 0) {
|
||||||
throw new IllegalStateException("Job count negative!!!");
|
throw new IllegalStateException("Job count negative!!!:" + this.name);
|
||||||
}
|
}
|
||||||
this.threadPool.steal(this, count);
|
this.threadPool.steal(this, count);
|
||||||
return count;
|
return count;
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ public class ServiceThreadPool {
|
|||||||
private final ThreadGroup threadGroup;
|
private final ThreadGroup threadGroup;
|
||||||
|
|
||||||
public ServiceThreadPool(int threadCount) {
|
public ServiceThreadPool(int threadCount) {
|
||||||
this(threadCount, 1);//Maybe change to 3
|
this(threadCount, 3);//Maybe change to 3
|
||||||
}
|
}
|
||||||
|
|
||||||
public ServiceThreadPool(int threadCount, int priority) {
|
public ServiceThreadPool(int threadCount, int priority) {
|
||||||
@@ -47,7 +47,7 @@ public class ServiceThreadPool {
|
|||||||
CpuLayout.setThreadAffinity(CpuLayout.CORES[2 + (threadId % (CpuLayout.CORES.length - 2))]);
|
CpuLayout.setThreadAffinity(CpuLayout.CORES[2 + (threadId % (CpuLayout.CORES.length - 2))]);
|
||||||
}
|
}
|
||||||
if (threadId != 0) {
|
if (threadId != 0) {
|
||||||
ThreadUtils.SetSelfThreadPriorityWin32(ThreadUtils.WIN32_THREAD_PRIORITY_LOWEST);
|
ThreadUtils.SetSelfThreadPriorityWin32(-1);
|
||||||
//ThreadUtils.SetSelfThreadPriorityWin32(ThreadUtils.WIN32_THREAD_MODE_BACKGROUND_BEGIN);
|
//ThreadUtils.SetSelfThreadPriorityWin32(ThreadUtils.WIN32_THREAD_MODE_BACKGROUND_BEGIN);
|
||||||
}
|
}
|
||||||
this.worker(threadId);
|
this.worker(threadId);
|
||||||
@@ -137,8 +137,11 @@ public class ServiceThreadPool {
|
|||||||
this.serviceSlices = newArr;
|
this.serviceSlices = newArr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void execute(ServiceSlice service) {
|
long addWeight(ServiceSlice service) {
|
||||||
this.totalJobWeight.addAndGet(service.weightPerJob);
|
return this.totalJobWeight.addAndGet(service.weightPerJob);
|
||||||
|
}
|
||||||
|
|
||||||
|
void execute() {
|
||||||
this.jobCounter.release(1);
|
this.jobCounter.release(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -268,11 +271,19 @@ public class ServiceThreadPool {
|
|||||||
|
|
||||||
//Consumed a job from the service, decrease weight by the amount
|
//Consumed a job from the service, decrease weight by the amount
|
||||||
if (this.totalJobWeight.addAndGet(-service.weightPerJob)<0) {
|
if (this.totalJobWeight.addAndGet(-service.weightPerJob)<0) {
|
||||||
throw new IllegalStateException("Total job weight is negative");
|
Logger.error("Total job weight is negative");
|
||||||
|
try {
|
||||||
|
Thread.sleep(500);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
if (this.totalJobWeight.get()<0) {
|
||||||
|
throw new IllegalStateException("Total job weight still negative");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Sleep for a bit after running a job, yeild the thread
|
//Sleep for a bit after running a job, yeild the thread
|
||||||
Thread.yield();
|
//Thread.yield();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package me.cortex.voxy.common.util;
|
||||||
|
|
||||||
|
import java.lang.ref.Cleaner;
|
||||||
|
|
||||||
|
public class GlobalCleaner {
|
||||||
|
public static final Cleaner CLEANER = Cleaner.create();
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
package me.cortex.voxy.common.util;
|
package me.cortex.voxy.common.util;
|
||||||
|
|
||||||
|
import me.cortex.voxy.commonImpl.VoxyCommon;
|
||||||
import org.lwjgl.system.MemoryUtil;
|
import org.lwjgl.system.MemoryUtil;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
@@ -7,6 +8,8 @@ import java.util.concurrent.atomic.AtomicInteger;
|
|||||||
import java.util.concurrent.atomic.AtomicLong;
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
|
|
||||||
public class MemoryBuffer extends TrackedObject {
|
public class MemoryBuffer extends TrackedObject {
|
||||||
|
private static final boolean TRACK_MEMORY_BUFFERS = VoxyCommon.isVerificationFlagOn("trackBuffers");
|
||||||
|
|
||||||
public final long address;
|
public final long address;
|
||||||
public final long size;
|
public final long size;
|
||||||
private final boolean freeable;
|
private final boolean freeable;
|
||||||
@@ -21,7 +24,7 @@ public class MemoryBuffer extends TrackedObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private MemoryBuffer(boolean track, long address, long size, boolean freeable) {
|
private MemoryBuffer(boolean track, long address, long size, boolean freeable) {
|
||||||
super(track);
|
super(track && TRACK_MEMORY_BUFFERS);
|
||||||
this.tracked = track;
|
this.tracked = track;
|
||||||
this.size = size;
|
this.size = size;
|
||||||
this.address = address;
|
this.address = address;
|
||||||
|
|||||||
@@ -7,13 +7,9 @@ import java.util.*;
|
|||||||
|
|
||||||
public class MultiGson {
|
public class MultiGson {
|
||||||
private final List<Class<?>> classes;
|
private final List<Class<?>> classes;
|
||||||
private final Gson GSON = new GsonBuilder()
|
private final Gson gson;
|
||||||
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
|
private MultiGson(Gson gson, List<Class<?>> classes) {
|
||||||
.setPrettyPrinting()
|
this.gson = gson;
|
||||||
.excludeFieldsWithModifiers(Modifier.PRIVATE)
|
|
||||||
.create();
|
|
||||||
|
|
||||||
private MultiGson(List<Class<?>> classes) {
|
|
||||||
this.classes = classes;
|
this.classes = classes;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,27 +34,38 @@ public class MultiGson {
|
|||||||
|
|
||||||
var json = new JsonObject();
|
var json = new JsonObject();
|
||||||
for (Object entry : map) {
|
for (Object entry : map) {
|
||||||
GSON.toJsonTree(entry).getAsJsonObject().asMap().forEach((i,j) -> {
|
this.gson.toJsonTree(entry).getAsJsonObject().asMap().forEach((i,j) -> {
|
||||||
if (json.has(i)) {
|
if (json.has(i)) {
|
||||||
throw new IllegalArgumentException("Duplicate name inside unified json: " + i);
|
throw new IllegalArgumentException("Duplicate name inside unified json: " + i);
|
||||||
}
|
}
|
||||||
json.add(i, j);
|
json.add(i, j);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return GSON.toJson(json);
|
return this.gson.toJson(json);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<Class<?>, Object> fromJson(String json) {
|
public Map<Class<?>, Object> fromJson(String json) {
|
||||||
var obj = GSON.fromJson(json, JsonObject.class);
|
var obj = this.gson.fromJson(json, JsonObject.class);
|
||||||
LinkedHashMap<Class<?>, Object> objects = new LinkedHashMap<>();
|
LinkedHashMap<Class<?>, Object> objects = new LinkedHashMap<>();
|
||||||
for (var cls : this.classes) {
|
for (var cls : this.classes) {
|
||||||
objects.put(cls, GSON.fromJson(obj, cls));
|
objects.put(cls, this.gson.fromJson(obj, cls));
|
||||||
}
|
}
|
||||||
return objects;
|
return objects;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Builder {
|
public static class Builder {
|
||||||
private final LinkedHashSet<Class<?>> classes = new LinkedHashSet<>();
|
private final LinkedHashSet<Class<?>> classes = new LinkedHashSet<>();
|
||||||
|
private final GsonBuilder gsonBuilder;
|
||||||
|
public Builder(GsonBuilder gsonBuilder) {
|
||||||
|
this.gsonBuilder = gsonBuilder;
|
||||||
|
}
|
||||||
|
public Builder() {
|
||||||
|
this(new GsonBuilder()
|
||||||
|
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
|
||||||
|
.setPrettyPrinting()
|
||||||
|
.excludeFieldsWithModifiers(Modifier.PRIVATE));
|
||||||
|
}
|
||||||
|
|
||||||
public Builder add(Class<?> clz) {
|
public Builder add(Class<?> clz) {
|
||||||
if (!this.classes.add(clz)) {
|
if (!this.classes.add(clz)) {
|
||||||
throw new IllegalArgumentException("Class has already been added");
|
throw new IllegalArgumentException("Class has already been added");
|
||||||
@@ -67,7 +74,7 @@ public class MultiGson {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public MultiGson build() {
|
public MultiGson build() {
|
||||||
return new MultiGson(new ArrayList<>(this.classes));
|
return new MultiGson(this.gsonBuilder.create(), new ArrayList<>(this.classes));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,9 @@ package me.cortex.voxy.common.util;
|
|||||||
|
|
||||||
import java.lang.ref.Cleaner;
|
import java.lang.ref.Cleaner;
|
||||||
|
|
||||||
|
import static me.cortex.voxy.common.util.GlobalCleaner.CLEANER;
|
||||||
|
|
||||||
public class ThreadLocalMemoryBuffer {
|
public class ThreadLocalMemoryBuffer {
|
||||||
private static final Cleaner CLEANER = Cleaner.create();
|
|
||||||
private static MemoryBuffer createMemoryBuffer(long size) {
|
private static MemoryBuffer createMemoryBuffer(long size) {
|
||||||
var buffer = new MemoryBuffer(size);
|
var buffer = new MemoryBuffer(size);
|
||||||
var ref = MemoryBuffer.createUntrackedUnfreeableRawFrom(buffer.address, buffer.size);
|
var ref = MemoryBuffer.createUntrackedUnfreeableRawFrom(buffer.address, buffer.size);
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ public class ThreadUtils {
|
|||||||
public static final int WIN32_THREAD_PRIORITY_LOWEST = -2;
|
public static final int WIN32_THREAD_PRIORITY_LOWEST = -2;
|
||||||
public static final int WIN32_THREAD_MODE_BACKGROUND_BEGIN = 0x00010000;
|
public static final int WIN32_THREAD_MODE_BACKGROUND_BEGIN = 0x00010000;
|
||||||
public static final int WIN32_THREAD_MODE_BACKGROUND_END = 0x00020000;
|
public static final int WIN32_THREAD_MODE_BACKGROUND_END = 0x00020000;
|
||||||
private static final boolean isWindows = Platform.get() == Platform.WINDOWS;
|
public static final boolean isWindows = Platform.get() == Platform.WINDOWS;
|
||||||
private static final long SetThreadPriority;
|
private static final long SetThreadPriority;
|
||||||
private static final long SetThreadSelectedCpuSetMasks;
|
private static final long SetThreadSelectedCpuSetMasks;
|
||||||
private static final long schedSetaffinity;
|
private static final long schedSetaffinity;
|
||||||
|
|||||||
@@ -5,9 +5,11 @@ import me.cortex.voxy.commonImpl.VoxyCommon;
|
|||||||
|
|
||||||
import java.lang.ref.Cleaner;
|
import java.lang.ref.Cleaner;
|
||||||
|
|
||||||
|
import static me.cortex.voxy.common.util.GlobalCleaner.CLEANER;
|
||||||
|
|
||||||
public abstract class TrackedObject {
|
public abstract class TrackedObject {
|
||||||
//TODO: maybe make this false? for performance overhead?
|
//TODO: maybe make this false? for performance overhead?
|
||||||
public static final boolean TRACK_OBJECT_ALLOCATIONS = VoxyCommon.isVerificationFlagOn("ensureTrackedObjectsAreFreed");
|
public static final boolean TRACK_OBJECT_ALLOCATIONS = VoxyCommon.isVerificationFlagOn("ensureTrackedObjectsAreFreed", true);
|
||||||
public static final boolean TRACK_OBJECT_ALLOCATION_STACKS = VoxyCommon.isVerificationFlagOn("trackObjectAllocationStacks");
|
public static final boolean TRACK_OBJECT_ALLOCATION_STACKS = VoxyCommon.isVerificationFlagOn("trackObjectAllocationStacks");
|
||||||
|
|
||||||
private final Ref ref;
|
private final Ref ref;
|
||||||
@@ -49,14 +51,6 @@ public abstract class TrackedObject {
|
|||||||
|
|
||||||
public record Ref(Cleaner.Cleanable cleanable, boolean[] freedRef) {}
|
public record Ref(Cleaner.Cleanable cleanable, boolean[] freedRef) {}
|
||||||
|
|
||||||
private static final Cleaner cleaner;
|
|
||||||
static {
|
|
||||||
if (TRACK_OBJECT_ALLOCATIONS) {
|
|
||||||
cleaner = Cleaner.create();
|
|
||||||
} else {
|
|
||||||
cleaner = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public static Ref register(boolean track, Object obj) {
|
public static Ref register(boolean track, Object obj) {
|
||||||
boolean[] freed = new boolean[1];
|
boolean[] freed = new boolean[1];
|
||||||
Cleaner.Cleanable cleanable = null;
|
Cleaner.Cleanable cleanable = null;
|
||||||
@@ -69,7 +63,7 @@ public abstract class TrackedObject {
|
|||||||
} else {
|
} else {
|
||||||
trace = null;
|
trace = null;
|
||||||
}
|
}
|
||||||
cleanable = cleaner.register(obj, () -> {
|
cleanable = CLEANER.register(obj, () -> {
|
||||||
if (!freed[0]) {
|
if (!freed[0]) {
|
||||||
Logger.error("Object named: " + clazz + " was not freed, location at:\n", trace==null?"Enable allocation stack tracing":trace);
|
Logger.error("Object named: " + clazz + " was not freed, location at:\n", trace==null?"Enable allocation stack tracing":trace);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
package me.cortex.voxy.common.util;
|
|
||||||
|
|
||||||
public class VolatileHolder <T> {
|
|
||||||
public volatile T obj;
|
|
||||||
}
|
|
||||||
@@ -10,6 +10,7 @@ public class VoxelizedSection {
|
|||||||
public int x;
|
public int x;
|
||||||
public int y;
|
public int y;
|
||||||
public int z;
|
public int z;
|
||||||
|
public int lvl0NonAirCount;
|
||||||
public final long[] section;
|
public final long[] section;
|
||||||
public VoxelizedSection(long[] section) {
|
public VoxelizedSection(long[] section) {
|
||||||
this.section = section;
|
this.section = section;
|
||||||
@@ -51,6 +52,7 @@ public class VoxelizedSection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public VoxelizedSection zero() {
|
public VoxelizedSection zero() {
|
||||||
|
this.lvl0NonAirCount = 0;
|
||||||
Arrays.fill(this.section, 0);
|
Arrays.fill(this.section, 0);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -143,7 +143,7 @@ public class WorldConversionFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int nonZeroCnt = 0;
|
||||||
if (blockContainer.data.storage instanceof PackedIntegerArray bStor) {
|
if (blockContainer.data.storage instanceof PackedIntegerArray bStor) {
|
||||||
var bDat = bStor.getData();
|
var bDat = bStor.getData();
|
||||||
int iterPerLong = (64 / bStor.getElementBits()) - 1;
|
int iterPerLong = (64 / bStor.getElementBits()) - 1;
|
||||||
@@ -168,7 +168,7 @@ public class WorldConversionFactory {
|
|||||||
sample >>>= eBits;
|
sample >>>= eBits;
|
||||||
|
|
||||||
byte light = lightSupplier.supply(i&0xF, (i>>8)&0xF, (i>>4)&0xF);
|
byte light = lightSupplier.supply(i&0xF, (i>>8)&0xF, (i>>4)&0xF);
|
||||||
|
nonZeroCnt += (bId != 0)?1:0;
|
||||||
data[i] = Mapper.composeMappingId(light, bId, biomes[Integer.compress(i,0b1100_1100_1100)]);
|
data[i] = Mapper.composeMappingId(light, bId, biomes[Integer.compress(i,0b1100_1100_1100)]);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -181,12 +181,14 @@ public class WorldConversionFactory {
|
|||||||
data[i] = Mapper.airWithLight(lightSupplier.supply(i&0xF, (i>>8)&0xF, (i>>4)&0xF));
|
data[i] = Mapper.airWithLight(lightSupplier.supply(i&0xF, (i>>8)&0xF, (i>>4)&0xF));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
nonZeroCnt = 4096;
|
||||||
for (int i = 0; i <= 0xFFF; i++) {
|
for (int i = 0; i <= 0xFFF; i++) {
|
||||||
byte light = lightSupplier.supply(i&0xF, (i>>8)&0xF, (i>>4)&0xF);
|
byte light = lightSupplier.supply(i&0xF, (i>>8)&0xF, (i>>4)&0xF);
|
||||||
data[i] = Mapper.composeMappingId(light, bId, biomes[Integer.compress(i,0b1100_1100_1100)]);
|
data[i] = Mapper.composeMappingId(light, bId, biomes[Integer.compress(i,0b1100_1100_1100)]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
section.lvl0NonAirCount = nonZeroCnt;
|
||||||
return section;
|
return section;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,14 +3,13 @@ package me.cortex.voxy.common.world;
|
|||||||
import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap;
|
import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap;
|
||||||
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
||||||
import me.cortex.voxy.common.Logger;
|
import me.cortex.voxy.common.Logger;
|
||||||
import me.cortex.voxy.common.util.VolatileHolder;
|
|
||||||
import me.cortex.voxy.common.world.other.Mapper;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.lang.invoke.MethodHandle;
|
||||||
|
import java.lang.invoke.MethodHandles;
|
||||||
import java.lang.invoke.VarHandle;
|
import java.lang.invoke.VarHandle;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
|
||||||
import java.util.concurrent.locks.StampedLock;
|
import java.util.concurrent.locks.StampedLock;
|
||||||
|
|
||||||
public class ActiveSectionTracker {
|
public class ActiveSectionTracker {
|
||||||
@@ -19,6 +18,21 @@ public class ActiveSectionTracker {
|
|||||||
public interface SectionLoader {int load(WorldSection section);}
|
public interface SectionLoader {int load(WorldSection section);}
|
||||||
|
|
||||||
//Loaded section world cache, TODO: get rid of VolatileHolder and use something more sane
|
//Loaded section world cache, TODO: get rid of VolatileHolder and use something more sane
|
||||||
|
private static final class VolatileHolder <T> {
|
||||||
|
private static final VarHandle PRE_ACQUIRE_COUNT;
|
||||||
|
private static final VarHandle POST_ACQUIRE_COUNT;
|
||||||
|
static {
|
||||||
|
try {
|
||||||
|
PRE_ACQUIRE_COUNT = MethodHandles.lookup().findVarHandle(VolatileHolder.class, "preAcquireCount", int.class);
|
||||||
|
POST_ACQUIRE_COUNT = MethodHandles.lookup().findVarHandle(VolatileHolder.class, "postAcquireCount", int.class);
|
||||||
|
} catch (NoSuchFieldException | IllegalAccessException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public volatile int preAcquireCount;
|
||||||
|
public volatile int postAcquireCount;
|
||||||
|
public volatile T obj;
|
||||||
|
}
|
||||||
|
|
||||||
private final AtomicInteger loadedSections = new AtomicInteger();
|
private final AtomicInteger loadedSections = new AtomicInteger();
|
||||||
private final Long2ObjectOpenHashMap<VolatileHolder<WorldSection>>[] loadedSectionCache;
|
private final Long2ObjectOpenHashMap<VolatileHolder<WorldSection>>[] loadedSectionCache;
|
||||||
@@ -96,9 +110,13 @@ public class ActiveSectionTracker {
|
|||||||
|
|
||||||
if (isLoader) {
|
if (isLoader) {
|
||||||
this.loadedSections.incrementAndGet();
|
this.loadedSections.incrementAndGet();
|
||||||
|
long stamp2 = lock.readLock();
|
||||||
long stamp = this.lruLock.writeLock();
|
long stamp = this.lruLock.writeLock();
|
||||||
section = this.lruSecondaryCache.remove(key);
|
section = this.lruSecondaryCache.remove(key);
|
||||||
this.lruLock.unlockWrite(stamp);
|
this.lruLock.unlockWrite(stamp);
|
||||||
|
lock.unlockRead(stamp2);
|
||||||
|
} else {
|
||||||
|
VolatileHolder.PRE_ACQUIRE_COUNT.getAndAdd(holder, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
//If this thread was the one to create the reference then its the thread to load the section
|
//If this thread was the one to create the reference then its the thread to load the section
|
||||||
@@ -128,8 +146,12 @@ public class ActiveSectionTracker {
|
|||||||
} else {
|
} else {
|
||||||
section.primeForReuse();
|
section.primeForReuse();
|
||||||
}
|
}
|
||||||
|
int preAcquireCount = (int) VolatileHolder.PRE_ACQUIRE_COUNT.getAndSet(holder, 0);
|
||||||
|
section.acquire(preAcquireCount+1);//pre acquire amount
|
||||||
|
VolatileHolder.POST_ACQUIRE_COUNT.set(holder, preAcquireCount);
|
||||||
|
|
||||||
|
//TODO: mark if the section was loaded null
|
||||||
|
|
||||||
section.acquire();
|
|
||||||
VarHandle.storeStoreFence();//Do not reorder setting this object
|
VarHandle.storeStoreFence();//Do not reorder setting this object
|
||||||
holder.obj = section;
|
holder.obj = section;
|
||||||
VarHandle.releaseFence();
|
VarHandle.releaseFence();
|
||||||
@@ -139,6 +161,7 @@ public class ActiveSectionTracker {
|
|||||||
}
|
}
|
||||||
return section;
|
return section;
|
||||||
} else {
|
} else {
|
||||||
|
//TODO: mark the time the loading started in nanos, then here if it has been a while, spin lock, else jump back to the executing service and do work
|
||||||
VarHandle.fullFence();
|
VarHandle.fullFence();
|
||||||
while ((section = holder.obj) == null) {
|
while ((section = holder.obj) == null) {
|
||||||
VarHandle.fullFence();
|
VarHandle.fullFence();
|
||||||
@@ -146,6 +169,11 @@ public class ActiveSectionTracker {
|
|||||||
Thread.yield();
|
Thread.yield();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Try to acquire a pre lock
|
||||||
|
if (0<((int)VolatileHolder.POST_ACQUIRE_COUNT.getAndAdd(holder, -1))) {
|
||||||
|
//We managed to acquire one of the pre locks, so just return the section
|
||||||
|
return section;
|
||||||
|
} else {
|
||||||
//lock.lock();
|
//lock.lock();
|
||||||
{//Dont think need to lock here
|
{//Dont think need to lock here
|
||||||
if (section.tryAcquire()) {
|
if (section.tryAcquire()) {
|
||||||
@@ -158,20 +186,45 @@ public class ActiveSectionTracker {
|
|||||||
return this.acquire(key, nullOnEmpty);
|
return this.acquire(key, nullOnEmpty);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void tryUnload(WorldSection section) {
|
void tryUnload(WorldSection section) {
|
||||||
if (this.engine != null) this.engine.lastActiveTime = System.currentTimeMillis();
|
if (this.engine != null) this.engine.lastActiveTime = System.currentTimeMillis();
|
||||||
|
if (section.isDirty&&this.engine!=null) {
|
||||||
|
if (section.tryAcquire()) {
|
||||||
|
if (section.setNotDirty()) {//If the section is dirty we must enqueue for saving
|
||||||
|
this.engine.saveSection(section);
|
||||||
|
}
|
||||||
|
section.release(false);//Special
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (section.getRefCount() != 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
int index = this.getCacheArrayIndex(section.key);
|
int index = this.getCacheArrayIndex(section.key);
|
||||||
final var cache = this.loadedSectionCache[index];
|
final var cache = this.loadedSectionCache[index];
|
||||||
WorldSection sec = null;
|
WorldSection sec = null;
|
||||||
final var lock = this.locks[index];
|
final var lock = this.locks[index];
|
||||||
long stamp = lock.writeLock();
|
long stamp = lock.writeLock();
|
||||||
{
|
{
|
||||||
if (section.trySetFreed()) {
|
VarHandle.loadLoadFence();
|
||||||
|
if (section.isDirty) {
|
||||||
|
if (section.tryAcquire()) {
|
||||||
|
if (section.setNotDirty()) {//If the section is dirty we must enqueue for saving
|
||||||
|
if (this.engine != null)
|
||||||
|
this.engine.saveSection(section);
|
||||||
|
}
|
||||||
|
section.release(false);//Special
|
||||||
|
} else {
|
||||||
|
throw new IllegalStateException("Section was dirty but is also unloaded, this is very bad");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (section.getRefCount() == 0 && section.trySetFreed()) {
|
||||||
var cached = cache.remove(section.key);
|
var cached = cache.remove(section.key);
|
||||||
var obj = cached.obj;
|
var obj = cached.obj;
|
||||||
if (obj == null) {
|
if (obj == null) {
|
||||||
throw new IllegalStateException("This should be impossible");
|
throw new IllegalStateException("This should be impossible: " + WorldEngine.pprintPos(section.key));
|
||||||
}
|
}
|
||||||
if (obj != section) {
|
if (obj != section) {
|
||||||
throw new IllegalStateException("Removed section not the same as the referenced section in the cache: cached: " + obj + " got: " + section + " A: " + WorldSection.ATOMIC_STATE_HANDLE.get(obj) + " B: " +WorldSection.ATOMIC_STATE_HANDLE.get(section));
|
throw new IllegalStateException("Removed section not the same as the referenced section in the cache: cached: " + obj + " got: " + section + " A: " + WorldSection.ATOMIC_STATE_HANDLE.get(obj) + " B: " +WorldSection.ATOMIC_STATE_HANDLE.get(section));
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ import me.cortex.voxy.commonImpl.VoxyCommon;
|
|||||||
import org.lwjgl.system.MemoryUtil;
|
import org.lwjgl.system.MemoryUtil;
|
||||||
|
|
||||||
public class SaveLoadSystem3 {
|
public class SaveLoadSystem3 {
|
||||||
|
public static final int STORAGE_VERSION = 0;
|
||||||
|
|
||||||
private record SerializationCache(Long2ShortOpenHashMap lutMapCache, MemoryBuffer memoryBuffer) {
|
private record SerializationCache(Long2ShortOpenHashMap lutMapCache, MemoryBuffer memoryBuffer) {
|
||||||
public SerializationCache() {
|
public SerializationCache() {
|
||||||
this(new Long2ShortOpenHashMap(512), ThreadLocalMemoryBuffer.create(WorldSection.SECTION_VOLUME*2+WorldSection.SECTION_VOLUME*8+1024));
|
this(new Long2ShortOpenHashMap(512), ThreadLocalMemoryBuffer.create(WorldSection.SECTION_VOLUME*2+WorldSection.SECTION_VOLUME*8+1024));
|
||||||
@@ -60,6 +62,7 @@ public class SaveLoadSystem3 {
|
|||||||
throw new IllegalStateException();
|
throw new IllegalStateException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//TODO: note! can actually have the first (last?) byte of metadata be the storage version!
|
||||||
long metadata = 0;
|
long metadata = 0;
|
||||||
metadata |= Integer.toUnsignedLong(LUT.size());//Bottom 2 bytes
|
metadata |= Integer.toUnsignedLong(LUT.size());//Bottom 2 bytes
|
||||||
metadata |= Byte.toUnsignedLong(section.getNonEmptyChildren())<<16;//Next byte
|
metadata |= Byte.toUnsignedLong(section.getNonEmptyChildren())<<16;//Next byte
|
||||||
@@ -82,21 +85,26 @@ public class SaveLoadSystem3 {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
long metadata = MemoryUtil.memGetLong(ptr); ptr += 8;
|
final long metadata = MemoryUtil.memGetLong(ptr); ptr += 8;
|
||||||
section.nonEmptyChildren = (byte) ((metadata>>>16)&0xFF);
|
section.nonEmptyChildren = (byte) ((metadata>>>16)&0xFF);
|
||||||
|
final long lutBasePtr = ptr + WorldSection.SECTION_VOLUME * 2;
|
||||||
|
if (section.lvl == 0) {
|
||||||
int nonEmptyBlockCount = 0;
|
int nonEmptyBlockCount = 0;
|
||||||
long lutBasePtr = ptr + WorldSection.SECTION_VOLUME*2;
|
final var blockData = section.data;
|
||||||
var blockData = section.data;
|
|
||||||
for (int i = 0; i < WorldSection.SECTION_VOLUME; i++) {
|
for (int i = 0; i < WorldSection.SECTION_VOLUME; i++) {
|
||||||
short lutId = MemoryUtil.memGetShort(ptr); ptr+=2;
|
final short lutId = MemoryUtil.memGetShort(ptr); ptr += 2;
|
||||||
long blockId = MemoryUtil.memGetLong(lutBasePtr+Short.toUnsignedLong(lutId)*8L);
|
final long blockId = MemoryUtil.memGetLong(lutBasePtr + Short.toUnsignedLong(lutId) * 8L);
|
||||||
nonEmptyBlockCount += Mapper.isAir(blockId) ? 0 : 1;
|
nonEmptyBlockCount += Mapper.isAir(blockId) ? 0 : 1;
|
||||||
blockData[i] = blockId;
|
blockData[i] = blockId;
|
||||||
}
|
}
|
||||||
section.nonEmptyBlockCount = nonEmptyBlockCount;
|
section.nonEmptyBlockCount = nonEmptyBlockCount;
|
||||||
|
} else {
|
||||||
|
final var blockData = section.data;
|
||||||
|
for (int i = 0; i < WorldSection.SECTION_VOLUME; i++) {
|
||||||
|
blockData[i] = MemoryUtil.memGetLong(lutBasePtr + Short.toUnsignedLong(MemoryUtil.memGetShort(ptr)) * 8L);ptr += 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
ptr = lutBasePtr + (metadata & 0xFFFF) * 8L;
|
ptr = lutBasePtr + (metadata & 0xFFFF) * 8L;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,10 +52,15 @@ public class WorldEngine {
|
|||||||
public WorldEngine(SectionStorage storage, @Nullable VoxyInstance instance) {
|
public WorldEngine(SectionStorage storage, @Nullable VoxyInstance instance) {
|
||||||
this.instanceIn = instance;
|
this.instanceIn = instance;
|
||||||
|
|
||||||
|
int cacheSize = 1024;
|
||||||
|
if (Runtime.getRuntime().maxMemory()>=(1L<<32)-200<<20) {
|
||||||
|
cacheSize = 2048;
|
||||||
|
}
|
||||||
|
|
||||||
this.storage = storage;
|
this.storage = storage;
|
||||||
this.mapper = new Mapper(this.storage);
|
this.mapper = new Mapper(this.storage);
|
||||||
//5 cache size bits means that the section tracker has 32 separate maps that it uses
|
//5 cache size bits means that the section tracker has 32 separate maps that it uses
|
||||||
this.sectionTracker = new ActiveSectionTracker(6, storage::loadSection, 2048, this);
|
this.sectionTracker = new ActiveSectionTracker(6, storage::loadSection, cacheSize, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public WorldSection acquireIfExists(int lvl, int x, int y, int z) {
|
public WorldSection acquireIfExists(int lvl, int x, int y, int z) {
|
||||||
@@ -118,8 +123,8 @@ public class WorldEngine {
|
|||||||
if (this.dirtyCallback != null) {
|
if (this.dirtyCallback != null) {
|
||||||
this.dirtyCallback.accept(section, changeState);
|
this.dirtyCallback.accept(section, changeState);
|
||||||
}
|
}
|
||||||
if (this.saveCallback != null) {
|
if (!section.inSaveQueue) {
|
||||||
this.saveCallback.save(this, section);
|
section.markDirty();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -181,4 +186,11 @@ public class WorldEngine {
|
|||||||
//TODO: maybe dont need to tick the last active time?
|
//TODO: maybe dont need to tick the last active time?
|
||||||
this.lastActiveTime = System.currentTimeMillis();
|
this.lastActiveTime = System.currentTimeMillis();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void saveSection(WorldSection section) {
|
||||||
|
section.setNotDirty();
|
||||||
|
if (this.saveCallback != null) {
|
||||||
|
this.saveCallback.save(this, section);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,12 +22,16 @@ public final class WorldSection {
|
|||||||
static final VarHandle ATOMIC_STATE_HANDLE;
|
static final VarHandle ATOMIC_STATE_HANDLE;
|
||||||
private static final VarHandle NON_EMPTY_CHILD_HANDLE;
|
private static final VarHandle NON_EMPTY_CHILD_HANDLE;
|
||||||
private static final VarHandle NON_EMPTY_BLOCK_HANDLE;
|
private static final VarHandle NON_EMPTY_BLOCK_HANDLE;
|
||||||
|
private static final VarHandle IN_SAVE_QUEUE_HANDLE;
|
||||||
|
private static final VarHandle IS_DIRTY_HANDLE;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
try {
|
try {
|
||||||
ATOMIC_STATE_HANDLE = MethodHandles.lookup().findVarHandle(WorldSection.class, "atomicState", int.class);
|
ATOMIC_STATE_HANDLE = MethodHandles.lookup().findVarHandle(WorldSection.class, "atomicState", int.class);
|
||||||
NON_EMPTY_CHILD_HANDLE = MethodHandles.lookup().findVarHandle(WorldSection.class, "nonEmptyChildren", byte.class);
|
NON_EMPTY_CHILD_HANDLE = MethodHandles.lookup().findVarHandle(WorldSection.class, "nonEmptyChildren", byte.class);
|
||||||
NON_EMPTY_BLOCK_HANDLE = MethodHandles.lookup().findVarHandle(WorldSection.class, "nonEmptyBlockCount", int.class);
|
NON_EMPTY_BLOCK_HANDLE = MethodHandles.lookup().findVarHandle(WorldSection.class, "nonEmptyBlockCount", int.class);
|
||||||
|
IN_SAVE_QUEUE_HANDLE = MethodHandles.lookup().findVarHandle(WorldSection.class, "inSaveQueue", boolean.class);
|
||||||
|
IS_DIRTY_HANDLE = MethodHandles.lookup().findVarHandle(WorldSection.class, "isDirty", boolean.class);
|
||||||
} catch (NoSuchFieldException | IllegalAccessException e) {
|
} catch (NoSuchFieldException | IllegalAccessException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
@@ -51,11 +55,12 @@ public final class WorldSection {
|
|||||||
//Serialized states
|
//Serialized states
|
||||||
long metadata;
|
long metadata;
|
||||||
long[] data = null;
|
long[] data = null;
|
||||||
volatile int nonEmptyBlockCount = 0;
|
volatile int nonEmptyBlockCount = 0;//Note: only needed for level 0 sections
|
||||||
volatile byte nonEmptyChildren;
|
volatile byte nonEmptyChildren;
|
||||||
|
|
||||||
final ActiveSectionTracker tracker;
|
final ActiveSectionTracker tracker;
|
||||||
public final AtomicBoolean inSaveQueue = new AtomicBoolean();
|
volatile boolean inSaveQueue;
|
||||||
|
volatile boolean isDirty;
|
||||||
|
|
||||||
//When the first bit is set it means its loaded
|
//When the first bit is set it means its loaded
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
@@ -120,7 +125,7 @@ public final class WorldSection {
|
|||||||
public int acquire(int count) {
|
public int acquire(int count) {
|
||||||
int state = ((int) ATOMIC_STATE_HANDLE.getAndAdd(this, count<<1)) + (count<<1);
|
int state = ((int) ATOMIC_STATE_HANDLE.getAndAdd(this, count<<1)) + (count<<1);
|
||||||
if ((state & 1) == 0) {
|
if ((state & 1) == 0) {
|
||||||
throw new IllegalStateException("Tried to acquire unloaded section");
|
throw new IllegalStateException("Tried to acquire unloaded section: " + WorldEngine.pprintPos(this.key));
|
||||||
}
|
}
|
||||||
return state>>1;
|
return state>>1;
|
||||||
}
|
}
|
||||||
@@ -131,6 +136,10 @@ public final class WorldSection {
|
|||||||
|
|
||||||
//TODO: add the ability to hint to the tracker that yes the section is unloaded, try to cache it in a secondary cache since it will be reused/needed later
|
//TODO: add the ability to hint to the tracker that yes the section is unloaded, try to cache it in a secondary cache since it will be reused/needed later
|
||||||
public int release() {
|
public int release() {
|
||||||
|
return release(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
int release(boolean unload) {
|
||||||
int state = ((int) ATOMIC_STATE_HANDLE.getAndAdd(this, -2)) - 2;
|
int state = ((int) ATOMIC_STATE_HANDLE.getAndAdd(this, -2)) - 2;
|
||||||
if (state < 1) {
|
if (state < 1) {
|
||||||
throw new IllegalStateException("Section got into an invalid state");
|
throw new IllegalStateException("Section got into an invalid state");
|
||||||
@@ -138,7 +147,7 @@ public final class WorldSection {
|
|||||||
if ((state & 1) == 0) {
|
if ((state & 1) == 0) {
|
||||||
throw new IllegalStateException("Tried releasing a freed section");
|
throw new IllegalStateException("Tried releasing a freed section");
|
||||||
}
|
}
|
||||||
if ((state>>1)==0) {
|
if ((state>>1)==0 && unload) {
|
||||||
if (this.tracker != null) {
|
if (this.tracker != null) {
|
||||||
this.tracker.tryUnload(this);
|
this.tracker.tryUnload(this);
|
||||||
} else {
|
} else {
|
||||||
@@ -271,4 +280,20 @@ public final class WorldSection {
|
|||||||
public static WorldSection _createRawUntrackedUnsafeSection(int lvl, int x, int y, int z) {
|
public static WorldSection _createRawUntrackedUnsafeSection(int lvl, int x, int y, int z) {
|
||||||
return new WorldSection(lvl, x, y, z, null);
|
return new WorldSection(lvl, x, y, z, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean exchangeIsInSaveQueue(boolean state) {
|
||||||
|
return ((boolean) IN_SAVE_QUEUE_HANDLE.compareAndExchange(this, !state, state)) == !state;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void markDirty() {
|
||||||
|
IS_DIRTY_HANDLE.getAndSet(this, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean setNotDirty() {
|
||||||
|
return (boolean) IS_DIRTY_HANDLE.getAndSet(this, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isFreed() {
|
||||||
|
return (((int)ATOMIC_STATE_HANDLE.get(this))&1)==0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -6,7 +6,6 @@ import me.cortex.voxy.common.world.other.Mapper;
|
|||||||
import static me.cortex.voxy.common.world.WorldEngine.*;
|
import static me.cortex.voxy.common.world.WorldEngine.*;
|
||||||
|
|
||||||
public class WorldUpdater {
|
public class WorldUpdater {
|
||||||
//TODO: move this to auxilery class so that it can take into account larger than 4 mip levels
|
|
||||||
//Executes an update to the world and automatically updates all the parent mip layers up to level 4 (e.g. where 1 chunk section is 1 block big)
|
//Executes an update to the world and automatically updates all the parent mip layers up to level 4 (e.g. where 1 chunk section is 1 block big)
|
||||||
|
|
||||||
//NOTE: THIS RUNS ON THE THREAD IT WAS EXECUTED ON, when this method exits, the calling method may assume that VoxelizedSection is no longer needed
|
//NOTE: THIS RUNS ON THE THREAD IT WAS EXECUTED ON, when this method exits, the calling method may assume that VoxelizedSection is no longer needed
|
||||||
@@ -23,7 +22,7 @@ public class WorldUpdater {
|
|||||||
WorldSection previousSection = null;
|
WorldSection previousSection = null;
|
||||||
final var vdat = section.section;
|
final var vdat = section.section;
|
||||||
|
|
||||||
for (int lvl = 0; lvl < MAX_LOD_LAYER+1; lvl++) {
|
for (int lvl = 0; lvl <= MAX_LOD_LAYER; lvl++) {
|
||||||
var worldSection = into.acquire(lvl, section.x >> (lvl + 1), section.y >> (lvl + 1), section.z >> (lvl + 1));
|
var worldSection = into.acquire(lvl, section.x >> (lvl + 1), section.y >> (lvl + 1), section.z >> (lvl + 1));
|
||||||
|
|
||||||
int emptinessStateChange = 0;
|
int emptinessStateChange = 0;
|
||||||
@@ -41,15 +40,37 @@ public class WorldUpdater {
|
|||||||
int by = (section.y&msk)<<(4-lvl);
|
int by = (section.y&msk)<<(4-lvl);
|
||||||
int bz = (section.z&msk)<<(4-lvl);
|
int bz = (section.z&msk)<<(4-lvl);
|
||||||
|
|
||||||
int nonAirCountDelta = 0;
|
int airCount = 0;
|
||||||
boolean didStateChange = false;
|
boolean didStateChange = false;
|
||||||
|
|
||||||
|
|
||||||
|
//TODO: remove the nonAirCountDelta stuff if level != 0
|
||||||
|
|
||||||
{//Do a bunch of funny math
|
{//Do a bunch of funny math
|
||||||
var secD = worldSection.data;
|
var secD = worldSection.data;
|
||||||
|
|
||||||
int baseVIdx = VoxelizedSection.getBaseIndexForLevel(lvl);
|
|
||||||
int baseSec = bx | (bz << 5) | (by << 10);
|
int baseSec = bx | (bz << 5) | (by << 10);
|
||||||
|
if (lvl == 0) {
|
||||||
|
final int secMsk = 0b1100|(0xf << 5) | (0xf << 10);
|
||||||
|
final int iSecMsk1 = (~secMsk) + 1;
|
||||||
|
|
||||||
|
int secIdx = 0;
|
||||||
|
//TODO: manually unroll and do e.g. 4 iterations per loop
|
||||||
|
for (int i = 0; i <= 0xFFF; i+=4) {
|
||||||
|
int cSecIdx = secIdx + baseSec;
|
||||||
|
secIdx = (secIdx + iSecMsk1) & secMsk;
|
||||||
|
|
||||||
|
long oldId0 = secD[cSecIdx+0]; secD[cSecIdx+0] = vdat[i+0];
|
||||||
|
long oldId1 = secD[cSecIdx+1]; secD[cSecIdx+1] = vdat[i+1];
|
||||||
|
long oldId2 = secD[cSecIdx+2]; secD[cSecIdx+2] = vdat[i+2];
|
||||||
|
long oldId3 = secD[cSecIdx+3]; secD[cSecIdx+3] = vdat[i+3];
|
||||||
|
|
||||||
|
airCount += Mapper.isAir(oldId0)?1:0; didStateChange |= vdat[i+0] != oldId0;
|
||||||
|
airCount += Mapper.isAir(oldId1)?1:0; didStateChange |= vdat[i+1] != oldId1;
|
||||||
|
airCount += Mapper.isAir(oldId2)?1:0; didStateChange |= vdat[i+2] != oldId2;
|
||||||
|
airCount += Mapper.isAir(oldId3)?1:0; didStateChange |= vdat[i+3] != oldId3;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
int baseVIdx = VoxelizedSection.getBaseIndexForLevel(lvl);
|
||||||
|
|
||||||
int secMsk = 0xF >> lvl;
|
int secMsk = 0xF >> lvl;
|
||||||
secMsk |= (secMsk << 5) | (secMsk << 10);
|
secMsk |= (secMsk << 5) | (secMsk << 10);
|
||||||
@@ -60,17 +81,18 @@ public class WorldUpdater {
|
|||||||
for (int i = baseVIdx; i <= (0xFFF >> (lvl * 3)) + baseVIdx; i++) {
|
for (int i = baseVIdx; i <= (0xFFF >> (lvl * 3)) + baseVIdx; i++) {
|
||||||
int cSecIdx = secIdx + baseSec;
|
int cSecIdx = secIdx + baseSec;
|
||||||
secIdx = (secIdx + iSecMsk1) & secMsk;
|
secIdx = (secIdx + iSecMsk1) & secMsk;
|
||||||
|
|
||||||
long newId = vdat[i];
|
long newId = vdat[i];
|
||||||
long oldId = secD[cSecIdx]; secD[cSecIdx] = newId;
|
long oldId = secD[cSecIdx];
|
||||||
nonAirCountDelta += (Mapper.isAir(newId)?0:1)-(Mapper.isAir(oldId)?0:1);//its 0:1 cause its nonAir
|
|
||||||
didStateChange |= newId != oldId;
|
didStateChange |= newId != oldId;
|
||||||
|
secD[cSecIdx] = newId;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (lvl == 0) {
|
||||||
|
int nonAirCountDelta = section.lvl0NonAirCount-(4096-airCount);
|
||||||
if (nonAirCountDelta != 0) {
|
if (nonAirCountDelta != 0) {
|
||||||
worldSection.addNonEmptyBlockCount(nonAirCountDelta);
|
worldSection.addNonEmptyBlockCount(nonAirCountDelta);
|
||||||
if (lvl == 0) {
|
|
||||||
emptinessStateChange = worldSection.updateLvl0State() ? 2 : 0;
|
emptinessStateChange = worldSection.updateLvl0State() ? 2 : 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import me.cortex.voxy.common.config.section.SectionStorage;
|
|||||||
import net.minecraft.block.Block;
|
import net.minecraft.block.Block;
|
||||||
import net.minecraft.block.BlockState;
|
import net.minecraft.block.BlockState;
|
||||||
import net.minecraft.block.Blocks;
|
import net.minecraft.block.Blocks;
|
||||||
|
import net.minecraft.block.LeavesBlock;
|
||||||
import net.minecraft.nbt.NbtCompound;
|
import net.minecraft.nbt.NbtCompound;
|
||||||
import net.minecraft.nbt.NbtIo;
|
import net.minecraft.nbt.NbtIo;
|
||||||
import net.minecraft.nbt.NbtOps;
|
import net.minecraft.nbt.NbtOps;
|
||||||
@@ -54,9 +55,8 @@ public class Mapper {
|
|||||||
|
|
||||||
|
|
||||||
public static boolean isAir(long id) {
|
public static boolean isAir(long id) {
|
||||||
int bId = getBlockId(id);
|
|
||||||
//Note: air can mean void, cave or normal air, as the block state is remapped during ingesting
|
//Note: air can mean void, cave or normal air, as the block state is remapped during ingesting
|
||||||
return bId == 0;
|
return (id&(((1L<<20)-1)<<27)) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int getBlockId(long id) {
|
public static int getBlockId(long id) {
|
||||||
@@ -299,8 +299,13 @@ public class Mapper {
|
|||||||
public StateEntry(int id, BlockState state) {
|
public StateEntry(int id, BlockState state) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.state = state;
|
this.state = state;
|
||||||
|
//Override opacity of leaves to be solid
|
||||||
|
if (state.getBlock() instanceof LeavesBlock) {
|
||||||
|
this.opacity = 15;
|
||||||
|
} else {
|
||||||
this.opacity = state.getOpacity();
|
this.opacity = state.getOpacity();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public byte[] serialize() {
|
public byte[] serialize() {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -28,8 +28,9 @@ public class SectionSavingService {
|
|||||||
var section = task.section;
|
var section = task.section;
|
||||||
section.assertNotFree();
|
section.assertNotFree();
|
||||||
try {
|
try {
|
||||||
section.inSaveQueue.set(false);
|
if (section.exchangeIsInSaveQueue(false)) {
|
||||||
task.engine.storage.saveSection(section);
|
task.engine.storage.saveSection(section);
|
||||||
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Logger.error("Voxy saver had an exception while executing please check logs and report error", e);
|
Logger.error("Voxy saver had an exception while executing please check logs and report error", e);
|
||||||
}
|
}
|
||||||
@@ -47,7 +48,7 @@ public class SectionSavingService {
|
|||||||
|
|
||||||
public void enqueueSave(WorldEngine in, WorldSection section) {
|
public void enqueueSave(WorldEngine in, WorldSection section) {
|
||||||
//If its not enqueued for saving then enqueue it
|
//If its not enqueued for saving then enqueue it
|
||||||
if (!section.inSaveQueue.getAndSet(true)) {
|
if (section.exchangeIsInSaveQueue(true)) {
|
||||||
//Acquire the section for use
|
//Acquire the section for use
|
||||||
section.acquire();
|
section.acquire();
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import net.minecraft.world.chunk.Chunk;
|
|||||||
import net.minecraft.world.chunk.ChunkNibbleArray;
|
import net.minecraft.world.chunk.ChunkNibbleArray;
|
||||||
import net.minecraft.world.chunk.ChunkSection;
|
import net.minecraft.world.chunk.ChunkSection;
|
||||||
import net.minecraft.world.chunk.WorldChunk;
|
import net.minecraft.world.chunk.WorldChunk;
|
||||||
|
import net.minecraft.world.chunk.light.LightStorage;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.util.concurrent.ConcurrentLinkedDeque;
|
import java.util.concurrent.ConcurrentLinkedDeque;
|
||||||
@@ -93,8 +94,7 @@ public class VoxelIngestService {
|
|||||||
throw new IllegalStateException("Tried inserting chunk into WorldEngine that was not alive");
|
throw new IllegalStateException("Tried inserting chunk into WorldEngine that was not alive");
|
||||||
}
|
}
|
||||||
var lightingProvider = chunk.getWorld().getLightingProvider();
|
var lightingProvider = chunk.getWorld().getLightingProvider();
|
||||||
var blp = lightingProvider.get(LightType.BLOCK);
|
boolean gotLighting = false;
|
||||||
var slp = lightingProvider.get(LightType.SKY);
|
|
||||||
|
|
||||||
int i = chunk.getBottomSectionCoord() - 1;
|
int i = chunk.getBottomSectionCoord() - 1;
|
||||||
for (var section : chunk.getSectionArray()) {
|
for (var section : chunk.getSectionArray()) {
|
||||||
@@ -102,23 +102,41 @@ public class VoxelIngestService {
|
|||||||
if (section == null || !shouldIngestSection(section, chunk.getPos().x, i, chunk.getPos().z)) continue;
|
if (section == null || !shouldIngestSection(section, chunk.getPos().x, i, chunk.getPos().z)) continue;
|
||||||
//if (section.isEmpty()) continue;
|
//if (section.isEmpty()) continue;
|
||||||
var pos = ChunkSectionPos.from(chunk.getPos(), i);
|
var pos = ChunkSectionPos.from(chunk.getPos(), i);
|
||||||
var bl = blp.getLightSection(pos);
|
if (lightingProvider.getStatus(LightType.SKY, pos) != LightStorage.Status.LIGHT_AND_DATA && lightingProvider.getStatus(LightType.BLOCK, pos) != LightStorage.Status.LIGHT_AND_DATA)
|
||||||
if (!(bl == null || bl.isUninitialized())) {
|
continue;
|
||||||
bl = bl.copy();
|
gotLighting = true;
|
||||||
} else {
|
|
||||||
bl = null;
|
|
||||||
}
|
|
||||||
var sl = slp.getLightSection(pos);
|
|
||||||
if (!(sl == null || sl.isUninitialized())) {
|
|
||||||
sl = sl.copy();
|
|
||||||
} else {
|
|
||||||
sl = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((bl == null && sl == null) && section.isEmpty()) {
|
if (!gotLighting) {
|
||||||
continue;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var blp = lightingProvider.get(LightType.BLOCK);
|
||||||
|
var slp = lightingProvider.get(LightType.SKY);
|
||||||
|
|
||||||
|
|
||||||
|
i = chunk.getBottomSectionCoord() - 1;
|
||||||
|
for (var section : chunk.getSectionArray()) {
|
||||||
|
i++;
|
||||||
|
if (section == null || !shouldIngestSection(section, chunk.getPos().x, i, chunk.getPos().z)) continue;
|
||||||
|
//if (section.isEmpty()) continue;
|
||||||
|
var pos = ChunkSectionPos.from(chunk.getPos(), i);
|
||||||
|
|
||||||
|
var bl = blp.getLightSection(pos);
|
||||||
|
if (bl != null) {
|
||||||
|
bl = bl.copy();
|
||||||
|
}
|
||||||
|
|
||||||
|
var sl = slp.getLightSection(pos);
|
||||||
|
if (sl != null) {
|
||||||
|
sl = sl.copy();
|
||||||
|
}
|
||||||
|
|
||||||
|
//If its null for either, assume failure to obtain lighting and ignore section
|
||||||
|
//if (blNone && slNone) {
|
||||||
|
// continue;
|
||||||
|
//}
|
||||||
|
|
||||||
this.ingestQueue.add(new IngestSection(chunk.getPos().x, i, chunk.getPos().z, engine, section, bl, sl));
|
this.ingestQueue.add(new IngestSection(chunk.getPos().x, i, chunk.getPos().z, engine, section, bl, sl));
|
||||||
try {
|
try {
|
||||||
this.threads.execute();
|
this.threads.execute();
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ import net.fabricmc.api.ModInitializer;
|
|||||||
import net.fabricmc.loader.api.FabricLoader;
|
import net.fabricmc.loader.api.FabricLoader;
|
||||||
import net.fabricmc.loader.api.ModContainer;
|
import net.fabricmc.loader.api.ModContainer;
|
||||||
|
|
||||||
|
import java.lang.invoke.VarHandle;
|
||||||
|
|
||||||
public class VoxyCommon implements ModInitializer {
|
public class VoxyCommon implements ModInitializer {
|
||||||
public static final String MOD_VERSION;
|
public static final String MOD_VERSION;
|
||||||
public static final boolean IS_DEDICATED_SERVER;
|
public static final boolean IS_DEDICATED_SERVER;
|
||||||
@@ -30,9 +32,12 @@ public class VoxyCommon implements ModInitializer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//This is hardcoded like this because people do not understand what they are doing
|
//This is hardcoded like this because people do not understand what they are doing
|
||||||
private static final boolean GlobalVerificationDisableOverride = true;//System.getProperty("voxy.verificationDisableOverride", "false").equals("true");
|
|
||||||
public static boolean isVerificationFlagOn(String name) {
|
public static boolean isVerificationFlagOn(String name) {
|
||||||
return (!GlobalVerificationDisableOverride) && System.getProperty("voxy."+name, "true").equals("true");
|
return isVerificationFlagOn(name, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isVerificationFlagOn(String name, boolean defaultOn) {
|
||||||
|
return System.getProperty("voxy."+name, defaultOn?"true":"false").equals("true");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void breakpoint() {
|
public static void breakpoint() {
|
||||||
@@ -61,8 +66,9 @@ public class VoxyCommon implements ModInitializer {
|
|||||||
|
|
||||||
public static void shutdownInstance() {
|
public static void shutdownInstance() {
|
||||||
if (INSTANCE != null) {
|
if (INSTANCE != null) {
|
||||||
INSTANCE.shutdown();
|
var instance = INSTANCE;
|
||||||
INSTANCE = null;
|
INSTANCE = null;//Make it null before shutdown
|
||||||
|
instance.shutdown();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -73,6 +73,7 @@ public abstract class VoxyInstance {
|
|||||||
// note, the reference count should be separate from the number of active chunks to prevent many issues
|
// note, the reference count should be separate from the number of active chunks to prevent many issues
|
||||||
// a world is no longer active once it has no reference counts and no active chunks associated with it
|
// a world is no longer active once it has no reference counts and no active chunks associated with it
|
||||||
public WorldEngine getNullable(WorldIdentifier identifier) {
|
public WorldEngine getNullable(WorldIdentifier identifier) {
|
||||||
|
if (!this.isRunning) return null;
|
||||||
var cache = identifier.cachedEngineObject;
|
var cache = identifier.cachedEngineObject;
|
||||||
WorldEngine world;
|
WorldEngine world;
|
||||||
if (cache == null) {
|
if (cache == null) {
|
||||||
@@ -109,11 +110,21 @@ public abstract class VoxyInstance {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public WorldEngine getOrCreate(WorldIdentifier identifier) {
|
public WorldEngine getOrCreate(WorldIdentifier identifier) {
|
||||||
|
if (!this.isRunning) {
|
||||||
|
Logger.error("Tried getting world object on voxy instance but its not running");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
var world = this.getNullable(identifier);
|
var world = this.getNullable(identifier);
|
||||||
if (world != null) {
|
if (world != null) {
|
||||||
return world;
|
return world;
|
||||||
}
|
}
|
||||||
long stamp = this.activeWorldLock.writeLock();
|
long stamp = this.activeWorldLock.writeLock();
|
||||||
|
|
||||||
|
if (!this.isRunning) {
|
||||||
|
Logger.error("Tried getting world object on voxy instance but its not running");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
world = this.activeWorlds.get(identifier);
|
world = this.activeWorlds.get(identifier);
|
||||||
if (world == null) {
|
if (world == null) {
|
||||||
//Create world here
|
//Create world here
|
||||||
@@ -128,10 +139,13 @@ public abstract class VoxyInstance {
|
|||||||
protected abstract SectionStorage createStorage(WorldIdentifier identifier);
|
protected abstract SectionStorage createStorage(WorldIdentifier identifier);
|
||||||
|
|
||||||
private WorldEngine createWorld(WorldIdentifier identifier) {
|
private WorldEngine createWorld(WorldIdentifier identifier) {
|
||||||
|
if (!this.isRunning) {
|
||||||
|
throw new IllegalStateException("Cannot create world while not running");
|
||||||
|
}
|
||||||
if (this.activeWorlds.containsKey(identifier)) {
|
if (this.activeWorlds.containsKey(identifier)) {
|
||||||
throw new IllegalStateException("Existing world with identifier");
|
throw new IllegalStateException("Existing world with identifier");
|
||||||
}
|
}
|
||||||
Logger.info("Creating new world engine: " + identifier.getLongHash());
|
Logger.info("Creating new world engine: " + identifier.getLongHash() + "@" + System.identityHashCode(this));
|
||||||
var world = new WorldEngine(this.createStorage(identifier), this);
|
var world = new WorldEngine(this.createStorage(identifier), this);
|
||||||
world.setSaveCallback(this.savingService::enqueueSave);
|
world.setSaveCallback(this.savingService::enqueueSave);
|
||||||
this.activeWorlds.put(identifier, world);
|
this.activeWorlds.put(identifier, world);
|
||||||
|
|||||||
@@ -40,13 +40,19 @@ public class WorldIdentifier {
|
|||||||
if (obj instanceof WorldIdentifier other) {
|
if (obj instanceof WorldIdentifier other) {
|
||||||
return other.hashCode == this.hashCode &&
|
return other.hashCode == this.hashCode &&
|
||||||
other.biomeSeed == this.biomeSeed &&
|
other.biomeSeed == this.biomeSeed &&
|
||||||
other.key == this.key &&//other.key.equals(this.key) &&
|
equal(other.key, this.key) &&//other.key.equals(this.key) &&
|
||||||
other.dimension == this.dimension//other.dimension.equals(this.dimension)
|
equal(other.dimension, this.dimension)//other.dimension.equals(this.dimension)
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static <T> boolean equal(RegistryKey<T> a, RegistryKey<T> b) {
|
||||||
|
if (a == b) return true;
|
||||||
|
if (a == null || b == null) return false;
|
||||||
|
return a.getRegistry().equals(b.getRegistry()) && a.getValue().equals(b.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
//Quick access utility method to get or create a world object in the current instance
|
//Quick access utility method to get or create a world object in the current instance
|
||||||
public WorldEngine getOrCreateEngine() {
|
public WorldEngine getOrCreateEngine() {
|
||||||
var instance = VoxyCommon.getInstance();
|
var instance = VoxyCommon.getInstance();
|
||||||
|
|||||||
@@ -0,0 +1,121 @@
|
|||||||
|
package me.cortex.voxy.commonImpl.configuration;
|
||||||
|
|
||||||
|
import com.google.gson.*;
|
||||||
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
import com.google.gson.stream.JsonReader;
|
||||||
|
import com.google.gson.stream.JsonWriter;
|
||||||
|
import me.cortex.voxy.client.config.VoxyConfig;
|
||||||
|
import me.cortex.voxy.common.Logger;
|
||||||
|
import me.cortex.voxy.common.util.MultiGson;
|
||||||
|
import me.cortex.voxy.common.util.cpu.CpuLayout;
|
||||||
|
import me.cortex.voxy.commonImpl.VoxyCommon;
|
||||||
|
import net.fabricmc.loader.api.FabricLoader;
|
||||||
|
|
||||||
|
import java.io.FileReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class VoxyConfigStore {
|
||||||
|
private final MultiGson gson;
|
||||||
|
|
||||||
|
private VoxyConfigStore(Object... defaultValues) {
|
||||||
|
var gb = new GsonBuilder()
|
||||||
|
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
|
||||||
|
.setPrettyPrinting()
|
||||||
|
.excludeFieldsWithModifiers(Modifier.PRIVATE);
|
||||||
|
Map<Class<?>, Object> defaultValueMap = new HashMap<>();
|
||||||
|
var mgb = new MultiGson.Builder(gb);
|
||||||
|
for (var i : defaultValues) {
|
||||||
|
mgb.add(i.getClass());
|
||||||
|
defaultValueMap.put(i.getClass(), i);
|
||||||
|
}
|
||||||
|
gb.registerTypeAdapterFactory(new TypeAdapterFactory() {
|
||||||
|
@Override
|
||||||
|
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
|
||||||
|
if (defaultValueMap.containsKey(typeToken.getRawType())) {
|
||||||
|
var defVal = (T)defaultValueMap.get(typeToken.getRawType());
|
||||||
|
var adapter = gson.getDelegateAdapter(this, typeToken);
|
||||||
|
return new TypeAdapter<T>() {
|
||||||
|
@Override
|
||||||
|
public void write(JsonWriter writer, T obj) throws IOException {
|
||||||
|
var defJson = adapter.toJsonTree(defVal).getAsJsonObject();
|
||||||
|
var val = adapter.toJsonTree(obj).getAsJsonObject();
|
||||||
|
for (var key : defJson.keySet()) {
|
||||||
|
if (val.has(key)) {
|
||||||
|
if (defJson.get(key).equals(val.get(key))) {
|
||||||
|
val.addProperty(key, "DEFAULT_VALUE");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
gson.toJson(val, writer);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T read(JsonReader reader) throws IOException {
|
||||||
|
var defJson = adapter.toJsonTree(defVal).getAsJsonObject();
|
||||||
|
var val = ((JsonElement)gson.fromJson(reader, JsonElement.class)).getAsJsonObject();
|
||||||
|
for (var key : defJson.keySet()) {
|
||||||
|
if (val.has(key)) {
|
||||||
|
if (val.get(key).equals(new JsonPrimitive("DEFAULT_VALUE"))) {
|
||||||
|
val.add(key, defJson.get(key));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return adapter.fromJsonTree(val);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.gson = mgb.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
private static void loadOrCreate() {
|
||||||
|
if (VoxyCommon.isAvailable()) {
|
||||||
|
var path = getConfigPath();
|
||||||
|
if (Files.exists(path)) {
|
||||||
|
try (FileReader reader = new FileReader(path.toFile())) {
|
||||||
|
var conf = GSON.fromJson(reader, VoxyConfig.class);
|
||||||
|
if (conf != null) {
|
||||||
|
conf.save();
|
||||||
|
return conf;
|
||||||
|
} else {
|
||||||
|
Logger.error("Failed to load voxy config, resetting");
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
Logger.error("Could not parse config", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var config = new VoxyConfig();
|
||||||
|
config.save();
|
||||||
|
return config;
|
||||||
|
} else {
|
||||||
|
var config = new VoxyConfig();
|
||||||
|
config.enabled = false;
|
||||||
|
config.enableRendering = false;
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
public void save() {
|
||||||
|
try {
|
||||||
|
Files.writeString(getConfigPath(), this.gson.toJson(this));
|
||||||
|
} catch (IOException e) {
|
||||||
|
Logger.error("Failed to write config file", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Path getConfigPath() {
|
||||||
|
return FabricLoader.getInstance()
|
||||||
|
.getConfigDir()
|
||||||
|
.resolve("voxy-config.json");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -309,7 +309,16 @@ public class DHImporter implements IDataImporter {
|
|||||||
if ((x+1)%16==0) {
|
if ((x+1)%16==0) {
|
||||||
for (int sz = 0; sz < 4; sz++) {
|
for (int sz = 0; sz < 4; sz++) {
|
||||||
for (int sy = 0; sy < this.worldHeightSections; sy++) {
|
for (int sy = 0; sy < this.worldHeightSections; sy++) {
|
||||||
System.arraycopy(storage, (sz|(sy<<2))<<12, section.section, 0, 16 * 16 * 16);
|
{
|
||||||
|
int base = (sz|(sy<<2))<<12;
|
||||||
|
int nonAirCount = 0;
|
||||||
|
final var dat = section.section;
|
||||||
|
for (int i = 0; i < 4096; i++) {
|
||||||
|
nonAirCount += Mapper.isAir(dat[i] = storage[i+base])?0:1;
|
||||||
|
}
|
||||||
|
section.lvl0NonAirCount = nonAirCount;
|
||||||
|
}
|
||||||
|
|
||||||
WorldConversionFactory.mipSection(section, this.engine.getMapper());
|
WorldConversionFactory.mipSection(section, this.engine.getMapper());
|
||||||
|
|
||||||
section.setPosition(X*4+(x>>4), sy+(this.bottomOfWorld>>4), (Z*4)+sz);
|
section.setPosition(X*4+(x>>4), sy+(this.bottomOfWorld>>4), (Z*4)+sz);
|
||||||
|
|||||||
@@ -149,6 +149,10 @@ public class WorldImporter implements IDataImporter {
|
|||||||
this.world.releaseRef();
|
this.world.releaseRef();
|
||||||
this.threadPool.shutdown();
|
this.threadPool.shutdown();
|
||||||
}
|
}
|
||||||
|
//Free all the remaining entries by running the lambda
|
||||||
|
while (!this.jobQueue.isEmpty()) {
|
||||||
|
this.jobQueue.poll().run();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private interface IImporterMethod <T> {
|
private interface IImporterMethod <T> {
|
||||||
@@ -481,8 +485,11 @@ public class WorldImporter implements IDataImporter {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var blockStates = blockStatesRes.getPartialOrThrow();
|
var blockStates = blockStatesRes.getPartialOrThrow();
|
||||||
var biomes = this.biomeCodec.parse(NbtOps.INSTANCE, section.getCompound("biomes").get()).result().orElse(this.defaultBiomeProvider);
|
var biomes = this.defaultBiomeProvider;
|
||||||
|
var optBiomes = section.getCompound("biomes");
|
||||||
|
if (optBiomes.isPresent()) {
|
||||||
|
biomes = this.biomeCodec.parse(NbtOps.INSTANCE, optBiomes.get()).result().orElse(this.defaultBiomeProvider);
|
||||||
|
}
|
||||||
VoxelizedSection csec = WorldConversionFactory.convert(
|
VoxelizedSection csec = WorldConversionFactory.convert(
|
||||||
SECTION_CACHE.get().setPosition(x, y, z),
|
SECTION_CACHE.get().setPosition(x, y, z),
|
||||||
this.world.getMapper(),
|
this.world.getMapper(),
|
||||||
|
|||||||
@@ -19,6 +19,9 @@
|
|||||||
"voxy.config.general.renderDistance": "Render distance",
|
"voxy.config.general.renderDistance": "Render distance",
|
||||||
"voxy.config.general.renderDistance.tooltip": "Render distance of voxy in chunks",
|
"voxy.config.general.renderDistance.tooltip": "Render distance of voxy in chunks",
|
||||||
|
|
||||||
|
"voxy.config.general.environmental_fog": "Enable environmental fog",
|
||||||
|
"voxy.config.general.environmental_fog.tooltip": "Enables or disables voxy rendering environmental fog",
|
||||||
|
|
||||||
"voxy.config.general.vanilla_fog": "Enable vanilla fog",
|
"voxy.config.general.vanilla_fog": "Enable vanilla fog",
|
||||||
"voxy.config.general.vanilla_fog.tooltip": "Enables or disables vanilla fog effect",
|
"voxy.config.general.vanilla_fog.tooltip": "Enables or disables vanilla fog effect",
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,9 @@
|
|||||||
|
|
||||||
layout(location = 0) in vec2 uv;
|
layout(location = 0) in vec2 uv;
|
||||||
layout(binding = 0) uniform sampler2D depthTex;
|
layout(binding = 0) uniform sampler2D depthTex;
|
||||||
|
#ifdef OUTPUT_COLOUR
|
||||||
|
layout(location=0) out vec4 colour;
|
||||||
|
#endif
|
||||||
void main() {
|
void main() {
|
||||||
vec4 depths = textureGather(depthTex, uv, 0); // Get depth values from all surrounding texels.
|
vec4 depths = textureGather(depthTex, uv, 0); // Get depth values from all surrounding texels.
|
||||||
|
|
||||||
@@ -10,6 +13,11 @@ void main() {
|
|||||||
if (any(cv)) {//Patch holes (its very dodgy but should work :tm:, should clamp it to the first 3 levels)
|
if (any(cv)) {//Patch holes (its very dodgy but should work :tm:, should clamp it to the first 3 levels)
|
||||||
depths = mix(vec4(0.0f), depths, cv);
|
depths = mix(vec4(0.0f), depths, cv);
|
||||||
}
|
}
|
||||||
|
float res = max(max(depths.x, depths.y), max(depths.z, depths.w));
|
||||||
|
|
||||||
gl_FragDepth = max(max(depths.x, depths.y), max(depths.z, depths.w)); // Write conservative depth.
|
#ifdef OUTPUT_COLOUR
|
||||||
|
colour = vec4(res);
|
||||||
|
#else
|
||||||
|
gl_FragDepth = res; // Write conservative depth.
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|||||||
93
src/main/resources/assets/voxy/shaders/hiz/hiz.comp
Normal file
93
src/main/resources/assets/voxy/shaders/hiz/hiz.comp
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
#version 460 core
|
||||||
|
|
||||||
|
#extension GL_KHR_shader_subgroup_arithmetic: require
|
||||||
|
#extension GL_KHR_shader_subgroup_basic : require
|
||||||
|
#extension GL_KHR_shader_subgroup_clustered : require
|
||||||
|
|
||||||
|
|
||||||
|
//64x64 reduction
|
||||||
|
layout(local_size_x=256) in;
|
||||||
|
|
||||||
|
const uint spread[64] = {
|
||||||
|
0x11100100, 0x13120302, 0x31302120, 0x33322322, 0x15140504, 0x17160706, 0x35342524, 0x37362726,
|
||||||
|
0x51504140, 0x53524342, 0x71706160, 0x73726362, 0x55544544, 0x57564746, 0x75746564, 0x77766766,
|
||||||
|
0x19180908, 0x1b1a0b0a, 0x39382928, 0x3b3a2b2a, 0x1d1c0d0c, 0x1f1e0f0e, 0x3d3c2d2c, 0x3f3e2f2e,
|
||||||
|
0x59584948, 0x5b5a4b4a, 0x79786968, 0x7b7a6b6a, 0x5d5c4d4c, 0x5f5e4f4e, 0x7d7c6d6c, 0x7f7e6f6e,
|
||||||
|
0x91908180, 0x93928382, 0xb1b0a1a0, 0xb3b2a3a2, 0x95948584, 0x97968786, 0xb5b4a5a4, 0xb7b6a7a6,
|
||||||
|
0xd1d0c1c0, 0xd3d2c3c2, 0xf1f0e1e0, 0xf3f2e3e2, 0xd5d4c5c4, 0xd7d6c7c6, 0xf5f4e5e4, 0xf7f6e7e6,
|
||||||
|
0x99988988, 0x9b9a8b8a, 0xb9b8a9a8, 0xbbbaabaa, 0x9d9c8d8c, 0x9f9e8f8e, 0xbdbcadac, 0xbfbeafae,
|
||||||
|
0xd9d8c9c8, 0xdbdacbca, 0xf9f8e9e8, 0xfbfaebea, 0xdddccdcc, 0xdfdecfce, 0xfdfcedec, 0xfffeefee
|
||||||
|
};
|
||||||
|
|
||||||
|
uint swizzleId(uint id) {
|
||||||
|
//swizzel to z curve
|
||||||
|
return bitfieldExtract(spread[id>>2], (int(id)&3)*8, 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
layout(location = 0) uniform vec2 invImSize;
|
||||||
|
|
||||||
|
layout(binding = 0) uniform sampler2D mip_0;
|
||||||
|
layout(binding = 1, r32f) uniform restrict writeonly image2D mip_1;
|
||||||
|
layout(binding = 2, r32f) uniform restrict writeonly image2D mip_2;
|
||||||
|
layout(binding = 3, r32f) uniform restrict writeonly image2D mip_3;
|
||||||
|
layout(binding = 4, r32f) uniform restrict writeonly image2D mip_4;
|
||||||
|
layout(binding = 5, r32f) uniform restrict writeonly image2D mip_5;
|
||||||
|
layout(binding = 6, r32f) uniform restrict writeonly image2D mip_6;
|
||||||
|
|
||||||
|
float getReduce2x2(ivec2 pos) {//w.r.t mip_1
|
||||||
|
vec4 data = textureGather(mip_0, vec2(pos*2+1)*invImSize);
|
||||||
|
float ret = max(max(data.x,data.y),max(data.z,data.w));
|
||||||
|
imageStore(mip_1, pos, vec4(ret));
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
float getReduce4x4(ivec2 pos) {//w.r.t mip_2
|
||||||
|
ivec2 pos2 = pos*2;
|
||||||
|
float ret = max(max(getReduce2x2(pos2+ivec2(0,0)),getReduce2x2(pos2+ivec2(0,1))),
|
||||||
|
max(getReduce2x2(pos2+ivec2(1,0)),getReduce2x2(pos2+ivec2(1,1))));
|
||||||
|
imageStore(mip_2, pos, vec4(ret));
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
//This is where the funny happens
|
||||||
|
// since we swizzeled the id when getting the value, our ordering within the subgroup should be z ordered
|
||||||
|
// we sadly cannot use the full subgroup reduction as wave size is 32 and we need a square pow2 values, so 16, sad beep
|
||||||
|
float getReduceWave(ivec2 pos, float value) {
|
||||||
|
float reduced;
|
||||||
|
subgroupBarrier();//Wait for active threads in subgroup
|
||||||
|
//Now do clustered reduction, with exploiting dropout
|
||||||
|
reduced = subgroupClusteredMax(value, 4);
|
||||||
|
if ((gl_SubgroupInvocationID&0x3)==0) {//root writes
|
||||||
|
imageStore(mip_3, pos>>1, vec4(reduced));
|
||||||
|
}
|
||||||
|
//could exit 3/4 of the threads here if wanted
|
||||||
|
subgroupBarrier();//Wait for active threads in subgroup
|
||||||
|
reduced = subgroupClusteredMax(value, 16);
|
||||||
|
if ((gl_SubgroupInvocationID&0xF)==0) {//root writes
|
||||||
|
imageStore(mip_4, pos>>2, vec4(reduced));
|
||||||
|
}
|
||||||
|
return reduced;
|
||||||
|
}
|
||||||
|
shared float values[16];
|
||||||
|
void main() {
|
||||||
|
uint id = swizzleId(gl_LocalInvocationID.x);
|
||||||
|
//(ivec2(gl_WorkGroupID.xy)*64+ivec2(id&0xFU, id>>4)*4)/4;
|
||||||
|
ivec2 wavePos = ivec2(gl_WorkGroupID.xy)*16+ivec2(id&0xFU, id>>4);
|
||||||
|
float value = getReduce4x4(wavePos);
|
||||||
|
value = getReduceWave(wavePos, value);//Reduced to 4x4 across all threads and warps
|
||||||
|
if ((gl_LocalInvocationID.x&0xFU)==0) {
|
||||||
|
values[gl_LocalInvocationID.x>>4] = value;
|
||||||
|
}
|
||||||
|
barrier();//Wait for all
|
||||||
|
if ((gl_LocalInvocationID.x>>2)!=0) {
|
||||||
|
return;//Discard all but 4 threads
|
||||||
|
}
|
||||||
|
uint i = gl_LocalInvocationID.x*4;
|
||||||
|
value = max(max(values[i],values[i+1]),max(values[i+2],values[i+3]));//Is funny is already in spread order
|
||||||
|
imageStore(mip_5, ivec2(gl_WorkGroupID.xy)*2+ivec2(gl_LocalInvocationID.x&1u,gl_LocalInvocationID.x>>1), vec4(value));
|
||||||
|
subgroupBarrier();
|
||||||
|
value = subgroupMax(value);
|
||||||
|
if (gl_LocalInvocationID.x==0) {
|
||||||
|
imageStore(mip_6, ivec2(gl_WorkGroupID.xy), vec4(value));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,3 @@
|
|||||||
#line 1
|
|
||||||
|
|
||||||
layout(binding = 0, std140) uniform SceneUniform {
|
layout(binding = 0, std140) uniform SceneUniform {
|
||||||
mat4 MVP;
|
mat4 MVP;
|
||||||
ivec3 baseSectionPos;
|
ivec3 baseSectionPos;
|
||||||
|
|||||||
@@ -47,5 +47,5 @@ void main() {
|
|||||||
dist = (TRANSLUCENT_WRITE_BASE-1)-min(dist, (TRANSLUCENT_WRITE_BASE-1));
|
dist = (TRANSLUCENT_WRITE_BASE-1)-min(dist, (TRANSLUCENT_WRITE_BASE-1));
|
||||||
|
|
||||||
uint drawPtr = atomicAdd(translucentCommandData[dist],1)+TRANSLUCENT_OFFSET;
|
uint drawPtr = atomicAdd(translucentCommandData[dist],1)+TRANSLUCENT_OFFSET;
|
||||||
writeCmd(drawPtr, drawId, extractQuadStart(meta), meta.cntA&0xFFFF);
|
writeCmd(drawPtr, drawId, extractQuadStart(meta), meta.b.x&0xFFFF);
|
||||||
}
|
}
|
||||||
@@ -78,18 +78,18 @@ void main() {
|
|||||||
ivec3 relative = ipos-(baseSectionPos>>detail);
|
ivec3 relative = ipos-(baseSectionPos>>detail);
|
||||||
uint drawId = gl_GlobalInvocationID.x;
|
uint drawId = gl_GlobalInvocationID.x;
|
||||||
|
|
||||||
positionBuffer[drawId] = uvec2(meta.posA, meta.posB);
|
positionBuffer[drawId] = extractRawPos(meta);
|
||||||
|
uvec4 counts = meta.b;
|
||||||
|
|
||||||
uint msk = 0;
|
uint msk = 0;
|
||||||
msk |= uint(((meta.cntB &0xFFFF)!=0) && (relative.y>-1))<<0;
|
msk |= uint(((counts.y &0xFFFFu)!=0) && (relative.y>-1))<<0;
|
||||||
msk |= uint((((meta.cntB>>16)&0xFFFF)!=0) && (relative.y<1 ))<<1;
|
msk |= uint((((counts.y>>16)&0xFFFFu)!=0) && (relative.y<1 ))<<1;
|
||||||
msk |= uint(((meta.cntC &0xFFFF)!=0) && (relative.z>-1))<<2;
|
msk |= uint(((counts.z &0xFFFFu)!=0) && (relative.z>-1))<<2;
|
||||||
msk |= uint((((meta.cntC>>16)&0xFFFF)!=0) && (relative.z<1 ))<<3;
|
msk |= uint((((counts.z>>16)&0xFFFFu)!=0) && (relative.z<1 ))<<3;
|
||||||
msk |= uint(((meta.cntD &0xFFFF)!=0) && (relative.x>-1))<<4;
|
msk |= uint(((counts.w &0xFFFFu)!=0) && (relative.x>-1))<<4;
|
||||||
msk |= uint((((meta.cntD>>16)&0xFFFF)!=0) && (relative.x<1 ))<<5;
|
msk |= uint((((counts.w>>16)&0xFFFFu)!=0) && (relative.x<1 ))<<5;
|
||||||
|
|
||||||
msk |= uint(((meta.cntA>>16)&0xFFFF)!=0)<<6;
|
msk |= uint(((counts.x>>16)&0xFFFFu)!=0)<<6;
|
||||||
|
|
||||||
|
|
||||||
uint cmdCnt = bitCount(msk);
|
uint cmdCnt = bitCount(msk);
|
||||||
@@ -107,7 +107,7 @@ void main() {
|
|||||||
|
|
||||||
uint count = 0;
|
uint count = 0;
|
||||||
//Translucency
|
//Translucency
|
||||||
count = meta.cntA&0xFFFF;
|
count = counts.x&0xFFFFu;
|
||||||
if (count != 0) {
|
if (count != 0) {
|
||||||
uint tp = atomicAdd(translucentDrawCount, 1)+TRANSLUCENT_WRITE_BASE;
|
uint tp = atomicAdd(translucentDrawCount, 1)+TRANSLUCENT_WRITE_BASE;
|
||||||
translucentCommandData[tp] = drawId;
|
translucentCommandData[tp] = drawId;
|
||||||
@@ -123,7 +123,7 @@ void main() {
|
|||||||
ptr += count;
|
ptr += count;
|
||||||
|
|
||||||
//Double sided quads
|
//Double sided quads
|
||||||
count = (meta.cntA>>16)&0xFFFF;
|
count = (counts.x>>16)&0xFFFFu;
|
||||||
if (count != 0) {
|
if (count != 0) {
|
||||||
writeCmd(cmdPtr++, drawId, ptr, count);
|
writeCmd(cmdPtr++, drawId, ptr, count);
|
||||||
if (renderTemporally) {
|
if (renderTemporally) {
|
||||||
@@ -136,7 +136,7 @@ void main() {
|
|||||||
ptr += count;
|
ptr += count;
|
||||||
|
|
||||||
//Down
|
//Down
|
||||||
count = (meta.cntB)&0xFFFF;
|
count = (counts.y)&0xFFFFu;
|
||||||
if (((msk&(1u<<0))!=0)) {
|
if (((msk&(1u<<0))!=0)) {
|
||||||
writeCmd(cmdPtr++, drawId, ptr, count);
|
writeCmd(cmdPtr++, drawId, ptr, count);
|
||||||
if (renderTemporally) {
|
if (renderTemporally) {
|
||||||
@@ -149,7 +149,7 @@ void main() {
|
|||||||
ptr += count;
|
ptr += count;
|
||||||
|
|
||||||
//Up
|
//Up
|
||||||
count = (meta.cntB>>16)&0xFFFF;
|
count = (counts.y>>16)&0xFFFFu;
|
||||||
if ((msk&(1u<<1))!=0) {
|
if ((msk&(1u<<1))!=0) {
|
||||||
writeCmd(cmdPtr++, drawId, ptr, count);
|
writeCmd(cmdPtr++, drawId, ptr, count);
|
||||||
if (renderTemporally) {
|
if (renderTemporally) {
|
||||||
@@ -162,7 +162,7 @@ void main() {
|
|||||||
ptr += count;
|
ptr += count;
|
||||||
|
|
||||||
//North
|
//North
|
||||||
count = (meta.cntC)&0xFFFF;
|
count = (counts.z)&0xFFFFu;
|
||||||
if ((msk&(1u<<2))!=0) {
|
if ((msk&(1u<<2))!=0) {
|
||||||
writeCmd(cmdPtr++, drawId, ptr, count);
|
writeCmd(cmdPtr++, drawId, ptr, count);
|
||||||
if (renderTemporally) {
|
if (renderTemporally) {
|
||||||
@@ -175,7 +175,7 @@ void main() {
|
|||||||
ptr += count;
|
ptr += count;
|
||||||
|
|
||||||
//South
|
//South
|
||||||
count = (meta.cntC>>16)&0xFFFF;
|
count = (counts.z>>16)&0xFFFFu;
|
||||||
if ((msk&(1u<<3))!=0) {
|
if ((msk&(1u<<3))!=0) {
|
||||||
writeCmd(cmdPtr++, drawId, ptr, count);
|
writeCmd(cmdPtr++, drawId, ptr, count);
|
||||||
if (renderTemporally) {
|
if (renderTemporally) {
|
||||||
@@ -188,7 +188,7 @@ void main() {
|
|||||||
ptr += count;
|
ptr += count;
|
||||||
|
|
||||||
//West
|
//West
|
||||||
count = (meta.cntD)&0xFFFF;
|
count = (counts.w)&0xFFFFu;
|
||||||
if ((msk&(1u<<4))!=0) {
|
if ((msk&(1u<<4))!=0) {
|
||||||
writeCmd(cmdPtr++, drawId, ptr, count);
|
writeCmd(cmdPtr++, drawId, ptr, count);
|
||||||
if (renderTemporally) {
|
if (renderTemporally) {
|
||||||
@@ -201,7 +201,7 @@ void main() {
|
|||||||
ptr += count;
|
ptr += count;
|
||||||
|
|
||||||
//East
|
//East
|
||||||
count = (meta.cntD>>16)&0xFFFF;
|
count = (counts.w>>16)&0xFFFFu;
|
||||||
if ((msk&(1u<<5))!=0) {
|
if ((msk&(1u<<5))!=0) {
|
||||||
writeCmd(cmdPtr++, drawId, ptr, count);
|
writeCmd(cmdPtr++, drawId, ptr, count);
|
||||||
if (renderTemporally) {
|
if (renderTemporally) {
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ void main() {
|
|||||||
//Write to the section id, to track temporal over time (litterally just need a single bit, 1 fking bit, but no)
|
//Write to the section id, to track temporal over time (litterally just need a single bit, 1 fking bit, but no)
|
||||||
id = sid;
|
id = sid;
|
||||||
|
|
||||||
|
//Me when data race condition between visibilityData in the vert shader and frag shader
|
||||||
uint previous = visibilityData[sid]&0x7fffffffu;
|
uint previous = visibilityData[sid]&0x7fffffffu;
|
||||||
bool wasVisibleLastFrame = previous==(frameId-1);
|
bool wasVisibleLastFrame = previous==(frameId-1);
|
||||||
value = (frameId&0x7fffffffu)|(uint(wasVisibleLastFrame)<<31);//Encode if it was visible last frame
|
value = (frameId&0x7fffffffu)|(uint(wasVisibleLastFrame)<<31);//Encode if it was visible last frame
|
||||||
|
|||||||
@@ -12,46 +12,68 @@ layout(binding = 2) uniform sampler2D depthTex;
|
|||||||
// however they are not a full block
|
// however they are not a full block
|
||||||
|
|
||||||
layout(location = 0) in vec2 uv;
|
layout(location = 0) in vec2 uv;
|
||||||
layout(location = 1) in flat vec2 baseUV;
|
layout(location = 1) in flat uvec4 interData;
|
||||||
layout(location = 2) in flat vec4 tinting;
|
|
||||||
layout(location = 3) in flat vec4 addin;
|
|
||||||
layout(location = 4) in flat uint flags;
|
|
||||||
layout(location = 5) in flat vec4 conditionalTinting;
|
|
||||||
layout(location = 6) in flat vec2 quadSize;
|
|
||||||
|
|
||||||
#ifdef DEBUG_RENDER
|
#ifdef DEBUG_RENDER
|
||||||
layout(location = 7) in flat uint quadDebug;
|
layout(location = 7) in flat uint quadDebug;
|
||||||
#endif
|
#endif
|
||||||
layout(location = 0) out vec4 outColour;
|
layout(location = 0) out vec4 outColour;
|
||||||
|
|
||||||
vec4 computeColour(vec4 colour) {
|
|
||||||
//Conditional tinting, TODO: FIXME: REPLACE WITH MASK OR SOMETHING, like encode data into the top bit of alpha
|
#import <voxy:lod/gl46/bindings.glsl>
|
||||||
if ((flags&(1u<<2)) != 0 && abs(colour.r-colour.g) < 0.02f && abs(colour.g-colour.b) < 0.02f) {
|
|
||||||
colour *= conditionalTinting;
|
vec4 uint2vec4RGBA(uint colour) {
|
||||||
}
|
return vec4((uvec4(colour)>>uvec4(24,16,8,0))&uvec4(0xFF))/255.0;
|
||||||
return (colour * tinting) + addin;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool useMipmaps() {
|
bool useMipmaps() {
|
||||||
return ((flags>>1)&1u)==0u;
|
return (interData.x&2u)==0u;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool useTinting() {
|
||||||
|
return (interData.x&4u)!=0u;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool useCutout() {
|
||||||
|
return (interData.x&1u)==1u;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 computeColour(vec4 colour) {
|
||||||
|
//Conditional tinting, TODO: FIXME: REPLACE WITH MASK OR SOMETHING, like encode data into the top bit of alpha
|
||||||
|
if (useTinting() && abs(colour.r-colour.g) < 0.02f && abs(colour.g-colour.b) < 0.02f) {
|
||||||
|
colour *= uint2vec4RGBA(interData.z).yzwx;
|
||||||
|
}
|
||||||
|
return (colour * uint2vec4RGBA(interData.y)) + vec4(0,0,0,float(interData.w&0xFFu)/255);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint getFace() {
|
||||||
|
return (interData.w>>8)&7u;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec2 getBaseUV() {
|
||||||
|
uint face = getFace();
|
||||||
|
uint modelId = interData.x>>16;
|
||||||
|
vec2 modelUV = vec2(modelId&0xFFu, (modelId>>8)&0xFFu)*(1.0/(256.0));
|
||||||
|
return modelUV + (vec2(face>>1, face&1u) * (1.0/(vec2(3.0, 2.0)*256.0)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
//Tile is the tile we are in
|
//Tile is the tile we are in
|
||||||
vec2 tile;
|
vec2 tile;
|
||||||
vec2 uv2 = modf(uv, tile)*(1.0/(vec2(3.0,2.0)*256.0));
|
vec2 uv2 = modf(uv, tile)*(1.0/(vec2(3.0,2.0)*256.0));
|
||||||
vec4 colour = vec4(1);
|
vec4 colour;
|
||||||
vec2 texPos = uv2 + baseUV;
|
vec2 texPos = uv2 + getBaseUV();
|
||||||
if (useMipmaps()) {
|
if (useMipmaps()) {
|
||||||
vec2 uvSmol = uv*(1.0/(vec2(3.0,2.0)*256.0));
|
vec2 uvSmol = uv*(1.0/(vec2(3.0,2.0)*256.0));
|
||||||
vec2 dx = dFdx(uvSmol);//vec2(lDx, dDx);
|
vec2 dx = dFdx(uvSmol);//vec2(lDx, dDx);
|
||||||
vec2 dy = dFdy(uvSmol);//vec2(lDy, dDy);
|
vec2 dy = dFdy(uvSmol);//vec2(lDy, dDy);
|
||||||
colour = textureGrad(blockModelAtlas, texPos, dx, dy);
|
colour = textureGrad(blockModelAtlas, texPos, dx, dy);
|
||||||
} else {
|
} else {
|
||||||
colour = texture(blockModelAtlas, texPos, -5.0);
|
colour = textureLod(blockModelAtlas, texPos, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (any(notEqual(clamp(tile, vec2(0), quadSize), tile))) {
|
if (any(notEqual(clamp(tile, vec2(0), vec2((interData.x>>8)&0xFu, (interData.x>>12)&0xFu)), tile))) {
|
||||||
discard;
|
discard;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,7 +84,7 @@ void main() {
|
|||||||
|
|
||||||
|
|
||||||
//Also, small quad is really fking over the mipping level somehow
|
//Also, small quad is really fking over the mipping level somehow
|
||||||
if ((flags&1u) == 1 && (texture(blockModelAtlas, texPos, -16.0).a <= 0.1f)) {
|
if (useCutout() && (textureLod(blockModelAtlas, texPos, 0).a <= 0.1f)) {
|
||||||
//This is stupidly stupidly bad for divergence
|
//This is stupidly stupidly bad for divergence
|
||||||
//TODO: FIXME, basicly what this do is sample the exact pixel (no lod) for discarding, this stops mipmapping fucking it over
|
//TODO: FIXME, basicly what this do is sample the exact pixel (no lod) for discarding, this stops mipmapping fucking it over
|
||||||
#ifndef DEBUG_RENDER
|
#ifndef DEBUG_RENDER
|
||||||
@@ -70,7 +92,9 @@ void main() {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
outColour = computeColour(colour);
|
colour = computeColour(colour);
|
||||||
|
|
||||||
|
outColour = colour;
|
||||||
|
|
||||||
|
|
||||||
#ifdef DEBUG_RENDER
|
#ifdef DEBUG_RENDER
|
||||||
|
|||||||
@@ -15,12 +15,22 @@
|
|||||||
//#define DEBUG_RENDER
|
//#define DEBUG_RENDER
|
||||||
|
|
||||||
layout(location = 0) out vec2 uv;
|
layout(location = 0) out vec2 uv;
|
||||||
layout(location = 1) out flat vec2 baseUV;
|
layout(location = 1) out flat uvec4 interData;
|
||||||
layout(location = 2) out flat vec4 tinting;
|
|
||||||
layout(location = 3) out flat vec4 addin;
|
uint packVec4(vec4 vec) {
|
||||||
layout(location = 4) out flat uint flags;
|
uvec4 vec_=uvec4(vec*255)<<uvec4(24,16,8,0);
|
||||||
layout(location = 5) out flat vec4 conditionalTinting;
|
return vec_.x|vec_.y|vec_.z|vec_.w;
|
||||||
layout(location = 6) out flat vec2 size;
|
}
|
||||||
|
|
||||||
|
void setSizeAndFlags(uint modelId, uint _flags, ivec2 quadSize) {
|
||||||
|
interData.x = (modelId<<16) | _flags | (uint(quadSize.x-1)<<8) | (uint(quadSize.y-1)<<12);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setTintingAndExtra(vec4 _tinting, uint _conditionalTinting, uint addin) {
|
||||||
|
interData.y = packVec4(_tinting);
|
||||||
|
interData.z = _conditionalTinting;
|
||||||
|
interData.w = addin;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef DEBUG_RENDER
|
#ifdef DEBUG_RENDER
|
||||||
layout(location = 7) out flat uint quadDebug;
|
layout(location = 7) out flat uint quadDebug;
|
||||||
@@ -37,38 +47,22 @@ ivec3 extractRelativeLodPos() {
|
|||||||
return (ivec3(gl_BaseInstance)<<ivec3(5,14,23))>>ivec3(23);
|
return (ivec3(gl_BaseInstance)<<ivec3(5,14,23))>>ivec3(23);
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
vec4 uint2vec4RGBA(uint colour) {
|
|
||||||
return vec4((uvec4(colour)>>uvec4(24,16,8,0))&uvec4(0xFF))/255.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
vec4 getFaceSize(uint faceData) {
|
vec4 getFaceSize(uint faceData) {
|
||||||
float EPSILON = 0.00005f;
|
float EPSILON = 0.00005f;
|
||||||
|
|
||||||
vec4 faceOffsetsSizes = extractFaceSizes(faceData);
|
vec4 faceOffsetsSizes = extractFaceSizes(faceData);
|
||||||
|
|
||||||
|
//Expand the quads by a very small amount (because of the subtraction after this also becomes an implicit add)
|
||||||
|
faceOffsetsSizes.xz -= vec2(EPSILON);
|
||||||
|
|
||||||
//Make the end relative to the start
|
//Make the end relative to the start
|
||||||
faceOffsetsSizes.yw -= faceOffsetsSizes.xz;
|
faceOffsetsSizes.yw -= faceOffsetsSizes.xz;
|
||||||
|
|
||||||
//Expand the quads by a very small amount
|
|
||||||
faceOffsetsSizes.xz -= vec2(EPSILON);
|
|
||||||
faceOffsetsSizes.yw += vec2(EPSILON);
|
|
||||||
|
|
||||||
return faceOffsetsSizes;
|
return faceOffsetsSizes;
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: make branchless by using ternaries i think
|
|
||||||
vec3 swizzelDataAxis(uint axis, vec3 data) {
|
vec3 swizzelDataAxis(uint axis, vec3 data) {
|
||||||
if (axis == 0) { //Up/down
|
return mix(mix(data.zxy,data.xzy,bvec3(axis==0)),data,bvec3(axis==1));
|
||||||
data = data.xzy;
|
|
||||||
}
|
|
||||||
//Not needed, here for readability
|
|
||||||
//if (axis == 1) {//north/south
|
|
||||||
// offset = offset.xyz;
|
|
||||||
//}
|
|
||||||
if (axis == 2) { //west/east
|
|
||||||
data = data.zxy;
|
|
||||||
}
|
|
||||||
return data;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint extractDetail(uvec2 encPos) {
|
uint extractDetail(uvec2 encPos) {
|
||||||
@@ -106,15 +100,28 @@ void main() {
|
|||||||
|
|
||||||
ivec2 quadSize = extractSize(quad);
|
ivec2 quadSize = extractSize(quad);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
vec4 faceSize = getFaceSize(faceData);
|
||||||
|
|
||||||
|
vec2 cQuadSize = (faceSize.yw + quadSize - 1) * vec2((cornerIdx>>1)&1, cornerIdx&1);
|
||||||
|
uv = faceSize.xz + cQuadSize;
|
||||||
|
|
||||||
|
vec3 cornerPos = extractPos(quad);
|
||||||
|
float depthOffset = extractFaceIndentation(faceData);
|
||||||
|
cornerPos += swizzelDataAxis(face>>1, vec3(faceSize.xz, mix(depthOffset, 1-depthOffset, float(face&1u))));
|
||||||
|
|
||||||
|
vec3 origin = vec3(((extractLoDPosition(encPos)<<lodLevel) - baseSectionPos)<<5);
|
||||||
|
vec3 pointPos = (cornerPos+swizzelDataAxis(face>>1,vec3(cQuadSize,0)))*(1<<lodLevel)+origin;
|
||||||
|
gl_Position = MVP*vec4(pointPos, 1.0);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (cornerIdx == 1) //Only if we are the provoking vertex
|
if (cornerIdx == 1) //Only if we are the provoking vertex
|
||||||
{
|
{
|
||||||
size = vec2(quadSize-1);
|
|
||||||
|
|
||||||
vec2 modelUV = vec2(modelId&0xFFu, (modelId>>8)&0xFFu)*(1.0/(256.0));
|
|
||||||
baseUV = modelUV + (vec2(face>>1, face&1u) * (1.0/(vec2(3.0, 2.0)*256.0)));
|
|
||||||
|
|
||||||
//Generate tinting and flag data
|
//Generate tinting and flag data
|
||||||
flags = faceHasAlphaCuttout(faceData);
|
uint flags = faceHasAlphaCuttout(faceData);
|
||||||
|
|
||||||
//We need to have a conditional override based on if the model size is < a full face + quadSize > 1
|
//We need to have a conditional override based on if the model size is < a full face + quadSize > 1
|
||||||
flags |= uint(any(greaterThan(quadSize, ivec2(1)))) & faceHasAlphaCuttoutOverride(faceData);
|
flags |= uint(any(greaterThan(quadSize, ivec2(1)))) & faceHasAlphaCuttoutOverride(faceData);
|
||||||
@@ -122,7 +129,7 @@ void main() {
|
|||||||
flags |= uint(!modelHasMipmaps(model))<<1;
|
flags |= uint(!modelHasMipmaps(model))<<1;
|
||||||
|
|
||||||
//Compute lighting
|
//Compute lighting
|
||||||
tinting = getLighting(extractLightId(quad));
|
vec4 tinting = getLighting(extractLightId(quad));
|
||||||
|
|
||||||
//Apply model colour tinting
|
//Apply model colour tinting
|
||||||
uint tintColour = model.colourTint;
|
uint tintColour = model.colourTint;
|
||||||
@@ -130,13 +137,13 @@ void main() {
|
|||||||
tintColour = colourData[tintColour + extractBiomeId(quad)];
|
tintColour = colourData[tintColour + extractBiomeId(quad)];
|
||||||
}
|
}
|
||||||
|
|
||||||
conditionalTinting = vec4(0);
|
uint conditionalTinting = 0;
|
||||||
if (tintColour != uint(-1)) {
|
if (tintColour != uint(-1)) {
|
||||||
flags |= 1u<<2;
|
flags |= 1u<<2;
|
||||||
conditionalTinting = uint2vec4RGBA(tintColour).yzwx;
|
conditionalTinting = tintColour;
|
||||||
}
|
}
|
||||||
|
|
||||||
addin = vec4(0.0);
|
uint addin = 0;
|
||||||
if (!isTranslucent) {
|
if (!isTranslucent) {
|
||||||
tinting.w = 0.0;
|
tinting.w = 0.0;
|
||||||
//Encode the face, the lod level and
|
//Encode the face, the lod level and
|
||||||
@@ -144,7 +151,7 @@ void main() {
|
|||||||
encodedData |= face;
|
encodedData |= face;
|
||||||
encodedData |= (lodLevel<<3);
|
encodedData |= (lodLevel<<3);
|
||||||
encodedData |= uint(hasAO)<<6;
|
encodedData |= uint(hasAO)<<6;
|
||||||
addin.w = float(encodedData)/255.0;
|
addin = encodedData;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Apply face tint
|
//Apply face tint
|
||||||
@@ -159,25 +166,12 @@ void main() {
|
|||||||
tinting.xyz *= 0.5f;
|
tinting.xyz *= 0.5f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setSizeAndFlags(modelId, flags, quadSize);
|
||||||
|
setTintingAndExtra(tinting, conditionalTinting, addin|(face<<8));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
vec4 faceSize = getFaceSize(faceData);
|
|
||||||
|
|
||||||
vec2 cQuadSize = (faceSize.yw + quadSize - 1) * vec2((cornerIdx>>1)&1, cornerIdx&1);
|
|
||||||
uv = faceSize.xz + cQuadSize;
|
|
||||||
|
|
||||||
vec3 cornerPos = extractPos(quad);
|
|
||||||
float depthOffset = extractFaceIndentation(faceData);
|
|
||||||
cornerPos += swizzelDataAxis(face>>1, vec3(faceSize.xz, mix(depthOffset, 1-depthOffset, float(face&1u))));
|
|
||||||
|
|
||||||
|
|
||||||
vec3 origin = vec3(((extractLoDPosition(encPos)<<lodLevel) - baseSectionPos)<<5);
|
|
||||||
gl_Position = MVP*vec4((cornerPos+swizzelDataAxis(face>>1,vec3(cQuadSize,0)))*(1<<lodLevel)+origin, 1.0);
|
|
||||||
|
|
||||||
#ifdef DEBUG_RENDER
|
#ifdef DEBUG_RENDER
|
||||||
quadDebug = lodLevel;
|
quadDebug = lodLevel;
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -133,6 +133,7 @@ bool isCulledByHiz() {
|
|||||||
float miplevel = log2(max(max(size.x, size.y),1));
|
float miplevel = log2(max(max(size.x, size.y),1));
|
||||||
|
|
||||||
miplevel = floor(miplevel)-1;
|
miplevel = floor(miplevel)-1;
|
||||||
|
//miplevel = clamp(miplevel, 0, 6);
|
||||||
miplevel = clamp(miplevel, 0, textureQueryLevels(hizDepthSampler)-1);
|
miplevel = clamp(miplevel, 0, textureQueryLevels(hizDepthSampler)-1);
|
||||||
|
|
||||||
int ml = int(miplevel);
|
int ml = int(miplevel);
|
||||||
|
|||||||
112
src/main/resources/assets/voxy/shaders/lod/mesh/frag.glsl
Normal file
112
src/main/resources/assets/voxy/shaders/lod/mesh/frag.glsl
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
#version 460 core
|
||||||
|
|
||||||
|
#extension GL_NV_fragment_shader_barycentric : require
|
||||||
|
|
||||||
|
layout(binding = 0) uniform sampler2D blockModelAtlas;
|
||||||
|
layout(binding = 2) uniform sampler2D depthTex;
|
||||||
|
|
||||||
|
perprimitiveNV in uvec4 primData;
|
||||||
|
perprimitiveNV in vec4 uvData;
|
||||||
|
|
||||||
|
layout(location = 0) out vec4 outColour;
|
||||||
|
|
||||||
|
vec4 uint2vec4RGBA(uint colour) {
|
||||||
|
return vec4((uvec4(colour)>>uvec4(24,16,8,0))&uvec4(0xFF))/255.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool useMipmaps() {
|
||||||
|
return (primData.x&2u)==0u;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool useTinting() {
|
||||||
|
return (primData.x&4u)!=0u;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool useCutout() {
|
||||||
|
return (primData.x&1u)==1u;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 computeColour(vec4 colour) {
|
||||||
|
//Conditional tinting, TODO: FIXME: REPLACE WITH MASK OR SOMETHING, like encode data into the top bit of alpha
|
||||||
|
if (useTinting() && abs(colour.r-colour.g) < 0.02f && abs(colour.g-colour.b) < 0.02f) {
|
||||||
|
colour *= uint2vec4RGBA(primData.z).yzwx;
|
||||||
|
}
|
||||||
|
return (colour * uint2vec4RGBA(primData.y)) + vec4(0,0,0,float(primData.w&0xFFu)/255);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint getFace() {
|
||||||
|
return (primData.w>>8)&7u;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec2 getBaseUV() {
|
||||||
|
uint face = getFace();
|
||||||
|
uint modelId = primData.x>>16;
|
||||||
|
vec2 modelUV = vec2(modelId&0xFFu, (modelId>>8)&0xFFu)*(1.0/(256.0));
|
||||||
|
return modelUV + (vec2(face>>1, face&1u) * (1.0/(vec2(3.0, 2.0)*256.0)));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isTri0() {
|
||||||
|
return (gl_PrimitiveID&(1<<31))==0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
bool tri0 = isTri0();
|
||||||
|
//1,2,0
|
||||||
|
//1,3,2
|
||||||
|
//vec2((corner>>1)&1u, corner&1u)
|
||||||
|
|
||||||
|
//vec2(0,gl_BaryCoordNV.x)+vec2(gl_BaryCoordNV.y,0)+vec2(0,0);
|
||||||
|
//vec2(0,gl_BaryCoordNV.x)+vec2(gl_BaryCoordNV.y,gl_BaryCoordNV.y)+vec2(gl_BaryCoordNV.z,0);
|
||||||
|
|
||||||
|
|
||||||
|
vec2 uv = fma(mix(gl_BaryCoordNV.zx+gl_BaryCoordNV.y, gl_BaryCoordNV.yx, bvec2(tri0)), uvData.zw, uvData.xy);
|
||||||
|
//Need to interpolate
|
||||||
|
|
||||||
|
//Tile is the tile we are in
|
||||||
|
vec2 tile;
|
||||||
|
vec2 uv2 = modf(uv, tile)*(1.0/(vec2(3.0,2.0)*256.0));
|
||||||
|
vec4 colour;
|
||||||
|
vec2 texPos = uv2 + getBaseUV();
|
||||||
|
if (useMipmaps()) {
|
||||||
|
vec2 uvSmol = uv*(1.0/(vec2(3.0,2.0)*256.0));
|
||||||
|
vec2 dx = dFdx(uvSmol);//vec2(lDx, dDx);
|
||||||
|
vec2 dy = dFdy(uvSmol);//vec2(lDy, dDy);
|
||||||
|
colour = textureGrad(blockModelAtlas, texPos, dx, dy);
|
||||||
|
} else {
|
||||||
|
colour = textureLod(blockModelAtlas, texPos, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (any(notEqual(clamp(tile, vec2(0), vec2((primData.x>>8)&0xFu, (primData.x>>12)&0xFu)), tile))) {
|
||||||
|
discard;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Check the minimum bounding texture and ensure we are greater than it
|
||||||
|
if (gl_FragCoord.z < texelFetch(depthTex, ivec2(gl_FragCoord.xy), 0).r) {
|
||||||
|
discard;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//Also, small quad is really fking over the mipping level somehow
|
||||||
|
if (useCutout() && (textureLod(blockModelAtlas, texPos, 0).a <= 0.1f)) {
|
||||||
|
//This is stupidly stupidly bad for divergence
|
||||||
|
//TODO: FIXME, basicly what this do is sample the exact pixel (no lod) for discarding, this stops mipmapping fucking it over
|
||||||
|
#ifndef DEBUG_RENDER
|
||||||
|
discard;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
colour = computeColour(colour);
|
||||||
|
|
||||||
|
outColour = colour;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
uint hash = gl_PrimitiveID*1231421+123141;
|
||||||
|
hash ^= hash>>16;
|
||||||
|
hash = hash*1231421+123141;
|
||||||
|
hash ^= hash>>16;
|
||||||
|
hash = hash * 1827364925 + 123325621;
|
||||||
|
outColour = vec4(float(hash&15u)/15, float((hash>>4)&15u)/15, float((hash>>8)&15u)/15, 0);
|
||||||
|
*/
|
||||||
|
}
|
||||||
361
src/main/resources/assets/voxy/shaders/lod/mesh/mesh.glsl
Normal file
361
src/main/resources/assets/voxy/shaders/lod/mesh/mesh.glsl
Normal file
@@ -0,0 +1,361 @@
|
|||||||
|
#version 460 core
|
||||||
|
|
||||||
|
#extension GL_NV_mesh_shader : require
|
||||||
|
|
||||||
|
#extension GL_ARB_gpu_shader_int64 : enable
|
||||||
|
|
||||||
|
#extension GL_KHR_shader_subgroup_arithmetic: require
|
||||||
|
#extension GL_KHR_shader_subgroup_basic : require
|
||||||
|
#extension GL_KHR_shader_subgroup_ballot : require
|
||||||
|
#extension GL_KHR_shader_subgroup_vote : require
|
||||||
|
|
||||||
|
|
||||||
|
//TODO: finetune the local size and emission size
|
||||||
|
layout(local_size_x = MESH_SIZE) in;
|
||||||
|
layout(triangles, max_vertices=(MESH_SIZE*4), max_primitives=(MESH_SIZE*2)) out;
|
||||||
|
|
||||||
|
layout(std430) taskNV in Task {
|
||||||
|
//Tightly packed, prefix sum + offset
|
||||||
|
//uvec4 binA;
|
||||||
|
//uvec4 binB;
|
||||||
|
uint bins[8];
|
||||||
|
|
||||||
|
vec3 cameraOffset;
|
||||||
|
uint lodLvl;
|
||||||
|
|
||||||
|
uint baseQuad;
|
||||||
|
uint quadCount;
|
||||||
|
} task;
|
||||||
|
|
||||||
|
perprimitiveNV out uvec4 primData[MESH_SIZE*2];
|
||||||
|
perprimitiveNV out vec4 uvData[MESH_SIZE*2];
|
||||||
|
|
||||||
|
uint getQuadId() {
|
||||||
|
uint mid = gl_GlobalInvocationID.x;
|
||||||
|
uint cv = (mid<<16)|0xFFFFu;
|
||||||
|
/*
|
||||||
|
//Funny method
|
||||||
|
uvec4 a = mix(uvec4(0), uvec4( 1, 2, 4, 8), lessThanEqual(uvec4(task.bins[0],task.bins[1],task.bins[2],task.bins[3]), uvec4(cv))) +
|
||||||
|
mix(uvec4(0), uvec4(16,32,64,128), lessThanEqual(uvec4(task.bins[4],task.bins[5],task.bins[6],task.bins[7]), uvec4(cv)));
|
||||||
|
uint act = a.x+a.y+a.z+a.w;
|
||||||
|
uint id = findLSB(act^(act>>1));
|
||||||
|
|
||||||
|
//uint point = mix(binB, binA, id<4)[id&3u];
|
||||||
|
uint point = task.bins[id];
|
||||||
|
|
||||||
|
return (point&0xFFFFu)+(mid-(point>>16));
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma unroll
|
||||||
|
for (uint i = 0; i<7; i++) {
|
||||||
|
uint point = task.bins[i];
|
||||||
|
if (point<=cv&&cv<task.bins[i+1]) {
|
||||||
|
return (point&0xFFFFu)+(mid-(point>>16));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
for (uint i = 0; i<7; i++) {
|
||||||
|
uint point = task.bins[i];
|
||||||
|
if (point <= ((mid<<16)|0xFFFFu) && ((mid<<16)|0xFFFFu)<task.bins[i+1]) {
|
||||||
|
binId = i;
|
||||||
|
return (point&0xFFFFu)+(mid-(point>>16));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
#import <voxy:lod/quad_format.glsl>
|
||||||
|
#import <voxy:lod/block_model.glsl>
|
||||||
|
|
||||||
|
layout(binding = 0, std140) uniform SceneUniform {
|
||||||
|
mat4 MVP;
|
||||||
|
ivec3 baseSectionPos;
|
||||||
|
uint frameId;
|
||||||
|
vec3 cameraSubPos;
|
||||||
|
uint pad_;
|
||||||
|
vec2 screenSize;
|
||||||
|
};
|
||||||
|
|
||||||
|
layout(binding = 4, std430) readonly restrict buffer QuadBuffer {
|
||||||
|
Quad quadData[];
|
||||||
|
};
|
||||||
|
|
||||||
|
layout(binding = 5, std430) readonly restrict buffer ModelBuffer {
|
||||||
|
BlockModel modelData[];
|
||||||
|
};
|
||||||
|
|
||||||
|
layout(binding = 6, std430) readonly restrict buffer ModelColourBuffer {
|
||||||
|
uint colourData[];
|
||||||
|
};
|
||||||
|
|
||||||
|
layout(binding = 1) uniform sampler2D lightSampler;
|
||||||
|
vec4 getLighting(uint index) {
|
||||||
|
int i2 = int(index);
|
||||||
|
return texture(lightSampler, clamp((vec2((i2>>4)&0xF, i2&0xF))/16, vec2(8.0f/255), vec2(248.0f/255)));
|
||||||
|
}
|
||||||
|
|
||||||
|
//===============
|
||||||
|
|
||||||
|
|
||||||
|
vec3 swizzelDataAxis(uint axis, vec3 data) {
|
||||||
|
return mix(mix(data.zxy,data.xzy,bvec3(axis==0)),data,bvec3(axis==1));
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 getFaceSize(uint faceData) {
|
||||||
|
float EPSILON = 0.00005f;
|
||||||
|
|
||||||
|
vec4 faceOffsetsSizes = extractFaceSizes(faceData);
|
||||||
|
|
||||||
|
//Expand the quads by a very small amount (because of the subtraction after this also becomes an implicit add)
|
||||||
|
faceOffsetsSizes.xz -= vec2(EPSILON);
|
||||||
|
|
||||||
|
//Make the end relative to the start
|
||||||
|
faceOffsetsSizes.yw -= faceOffsetsSizes.xz;
|
||||||
|
|
||||||
|
return faceOffsetsSizes;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 faceNormal(uint face) {
|
||||||
|
//TODO: optimize this garbage
|
||||||
|
return vec3(uint((face>>1)==2), uint((face>>1)==0), uint((face>>1)==1)) * (float(int(face)&1)*2-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint packVec4(vec4 vec) {
|
||||||
|
uvec4 vec_=uvec4(vec*255)<<uvec4(24,16,8,0);
|
||||||
|
return vec_.x|vec_.y|vec_.z|vec_.w;
|
||||||
|
}
|
||||||
|
|
||||||
|
//===============
|
||||||
|
vec3 cornerPos;//Does not include cameraSubPos to get exact need to - cameraSubPos
|
||||||
|
vec2 axisFaceSize;
|
||||||
|
uint face;
|
||||||
|
|
||||||
|
vec4 faceSize;
|
||||||
|
|
||||||
|
uint modelId;
|
||||||
|
BlockModel model;
|
||||||
|
uint faceData;
|
||||||
|
bool isTranslucent;
|
||||||
|
bool hasAO;
|
||||||
|
bool isShaded;
|
||||||
|
|
||||||
|
void setup(Quad quad) {
|
||||||
|
face = extractFace(quad);
|
||||||
|
modelId = extractStateId(quad);
|
||||||
|
model = modelData[modelId];
|
||||||
|
faceData = model.faceData[face];
|
||||||
|
isTranslucent = modelIsTranslucent(model);
|
||||||
|
hasAO = modelHasMipmaps(model);//TODO: replace with per face AO flag
|
||||||
|
isShaded = hasAO;//TODO: make this a per face flag
|
||||||
|
|
||||||
|
ivec2 quadSize = extractSize(quad);
|
||||||
|
|
||||||
|
faceSize = getFaceSize(faceData);
|
||||||
|
|
||||||
|
cornerPos = extractPos(quad);
|
||||||
|
float depthOffset = extractFaceIndentation(faceData);
|
||||||
|
cornerPos += swizzelDataAxis(face>>1, vec3(faceSize.xz, mix(depthOffset, 1-depthOffset, float(face&1u))));
|
||||||
|
cornerPos *= (1<<task.lodLvl);
|
||||||
|
cornerPos += task.cameraOffset;
|
||||||
|
|
||||||
|
axisFaceSize = (faceSize.yw + quadSize - 1);
|
||||||
|
|
||||||
|
//uv =
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
vec2 getUvCorner(uint corner) {
|
||||||
|
return faceSize.xz + axisFaceSize*vec2((corner>>1)&1u, corner&1u);
|
||||||
|
}
|
||||||
|
|
||||||
|
uvec4 createQuadData(Quad quad) {
|
||||||
|
uint flags = faceHasAlphaCuttout(faceData);
|
||||||
|
|
||||||
|
ivec2 quadSize = extractSize(quad);
|
||||||
|
//We need to have a conditional override based on if the model size is < a full face + quadSize > 1
|
||||||
|
flags |= uint(any(greaterThan(quadSize, ivec2(1)))) & faceHasAlphaCuttoutOverride(faceData);
|
||||||
|
|
||||||
|
flags |= uint(!modelHasMipmaps(model))<<1;
|
||||||
|
|
||||||
|
//Compute lighting
|
||||||
|
vec4 tinting = getLighting(extractLightId(quad));
|
||||||
|
|
||||||
|
//Apply model colour tinting
|
||||||
|
uint tintColour = model.colourTint;
|
||||||
|
if (modelHasBiomeLUT(model)) {
|
||||||
|
tintColour = colourData[tintColour + extractBiomeId(quad)];
|
||||||
|
}
|
||||||
|
|
||||||
|
uint conditionalTinting = 0;
|
||||||
|
if (tintColour != uint(-1)) {
|
||||||
|
flags |= 1u<<2;
|
||||||
|
conditionalTinting = tintColour;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint addin = 0;
|
||||||
|
if (!isTranslucent) {
|
||||||
|
tinting.w = 0.0;
|
||||||
|
//Encode the face, the lod level and
|
||||||
|
uint encodedData = 0;
|
||||||
|
encodedData |= face;
|
||||||
|
encodedData |= (task.lodLvl<<3);
|
||||||
|
encodedData |= uint(hasAO)<<6;
|
||||||
|
addin = encodedData;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Apply face tint
|
||||||
|
if (isShaded) {
|
||||||
|
//TODO: make branchless, infact apply ahead of time to the texture itself in ModelManager since that is
|
||||||
|
// per face
|
||||||
|
if ((face>>1) == 1) {//NORTH, SOUTH
|
||||||
|
tinting.xyz *= 0.8f;
|
||||||
|
} else if ((face>>1) == 2) {//EAST, WEST
|
||||||
|
tinting.xyz *= 0.6f;
|
||||||
|
} else if (face == 0) {//DOWN
|
||||||
|
tinting.xyz *= 0.5f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
uvec4 interData;
|
||||||
|
|
||||||
|
interData.x = (modelId<<16) | flags | (uint(quadSize.x-1)<<8) | (uint(quadSize.y-1)<<12);
|
||||||
|
|
||||||
|
interData.y = packVec4(tinting);
|
||||||
|
interData.z = conditionalTinting;
|
||||||
|
interData.w = addin|(face<<8);
|
||||||
|
|
||||||
|
return interData;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 emitVertexPos(int corner) {
|
||||||
|
vec3 pointPos = swizzelDataAxis(face>>1,vec3(axisFaceSize*mix(vec2(0),vec2(1<<task.lodLvl),bvec2((corner>>1)&1, corner&1)),0))+cornerPos;
|
||||||
|
return MVP*vec4(pointPos, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef HAS_STATISTICS
|
||||||
|
layout(binding = STATISTICS_BUFFER_BINDING, std430) restrict buffer statisticsBuffer {
|
||||||
|
uint visibleSectionCounts[5];
|
||||||
|
uint quadCounts[5];
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
if (subgroupElect()) {
|
||||||
|
gl_PrimitiveCountNV = 0;
|
||||||
|
}
|
||||||
|
if (task.quadCount<=gl_GlobalInvocationID.x) {
|
||||||
|
return;//dont emit a quad
|
||||||
|
}
|
||||||
|
uint qid = getQuadId() + task.baseQuad;
|
||||||
|
Quad quad = quadData[qid];
|
||||||
|
setup(quad);
|
||||||
|
|
||||||
|
bool render = dot(faceNormal(face), cornerPos-cameraSubPos) <= 0;
|
||||||
|
subgroupBarrier();
|
||||||
|
uint qId = subgroupExclusiveAdd(render?1:0);
|
||||||
|
if (render) {
|
||||||
|
|
||||||
|
uvec4 data = createQuadData(quad);
|
||||||
|
subgroupBarrier();
|
||||||
|
primData[qId*2] = data;
|
||||||
|
uvData[qId*2] = vec4(faceSize.xz, axisFaceSize);
|
||||||
|
primData[qId*2+1] = data;
|
||||||
|
uvData[qId*2+1] = vec4(faceSize.xz, axisFaceSize);
|
||||||
|
|
||||||
|
#define VID(i) (gl_LocalInvocationIndex*4+i)
|
||||||
|
|
||||||
|
gl_MeshVerticesNV[VID(0)].gl_Position = emitVertexPos(1);
|
||||||
|
gl_MeshVerticesNV[VID(1)].gl_Position = emitVertexPos(2);
|
||||||
|
|
||||||
|
gl_MeshVerticesNV[VID(2)].gl_Position = emitVertexPos(0);
|
||||||
|
gl_MeshVerticesNV[VID(3)].gl_Position = emitVertexPos(3);
|
||||||
|
|
||||||
|
gl_PrimitiveIndicesNV[qId*6+0] = VID(0);
|
||||||
|
gl_PrimitiveIndicesNV[qId*6+1] = VID(1);
|
||||||
|
gl_PrimitiveIndicesNV[qId*6+2] = VID(2);
|
||||||
|
|
||||||
|
gl_PrimitiveIndicesNV[qId*6+3] = VID(0);
|
||||||
|
gl_PrimitiveIndicesNV[qId*6+4] = VID(3);
|
||||||
|
gl_PrimitiveIndicesNV[qId*6+5] = VID(1);
|
||||||
|
|
||||||
|
gl_MeshPrimitivesNV[qId*2].gl_PrimitiveID = int(qid|(0u<<31));
|
||||||
|
gl_MeshPrimitivesNV[qId*2+1].gl_PrimitiveID = int(qid|(1u<<31));
|
||||||
|
|
||||||
|
/*
|
||||||
|
//vec4 p1 = ;
|
||||||
|
//vec4 p2 = ;
|
||||||
|
//vec4 p0 = emitVertexPos(0);
|
||||||
|
//vec4 p3 = emitVertexPos(3);
|
||||||
|
|
||||||
|
//Emit common
|
||||||
|
|
||||||
|
{
|
||||||
|
gl_PrimitiveIndicesNV[idxId++] = vertId_+0;
|
||||||
|
gl_PrimitiveIndicesNV[idxId++] = vertId_+1;
|
||||||
|
gl_PrimitiveIndicesNV[idxId++] = vertId;
|
||||||
|
|
||||||
|
gl_MeshVerticesNV[vertId++].gl_Position = p0;
|
||||||
|
|
||||||
|
primOut[triId].data = data;
|
||||||
|
primOut[triId].uvData = vec4(faceSize.xz, axisFaceSize);
|
||||||
|
gl_MeshPrimitivesNV[triId++].gl_PrimitiveID = int(qid|(0u<<31));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
gl_PrimitiveIndicesNV[idxId++] = vertId_+0;
|
||||||
|
gl_PrimitiveIndicesNV[idxId++] = vertId;
|
||||||
|
gl_PrimitiveIndicesNV[idxId++] = vertId_+1;
|
||||||
|
|
||||||
|
gl_MeshVerticesNV[vertId++].gl_Position = p3;
|
||||||
|
|
||||||
|
primOut[triId].data = data;
|
||||||
|
primOut[triId].uvData = vec4(faceSize.xz, axisFaceSize);
|
||||||
|
gl_MeshPrimitivesNV[triId++].gl_PrimitiveID = int(qid|(1u<<31));
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
subgroupBarrier();
|
||||||
|
uint count = subgroupMax(qId);
|
||||||
|
if (count != 0 && subgroupElect()) {
|
||||||
|
count = count *2+2;
|
||||||
|
gl_PrimitiveCountNV = count;
|
||||||
|
#ifdef HAS_STATISTICS
|
||||||
|
atomicAdd(quadCounts[task.lodLvl], count);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
uint triId = subgroupExclusiveAdd(render?2:0);
|
||||||
|
uint vertId = subgroupExclusiveAdd(render?4:0);
|
||||||
|
if (render) {
|
||||||
|
//common corners
|
||||||
|
gl_MeshVerticesNV[vertId+0].gl_Position = emitVertexPos(1);
|
||||||
|
gl_MeshVerticesNV[vertId+1].gl_Position = emitVertexPos(2);
|
||||||
|
|
||||||
|
//tri corners
|
||||||
|
gl_MeshVerticesNV[vertId+2].gl_Position = emitVertexPos(0);
|
||||||
|
gl_MeshVerticesNV[vertId+3].gl_Position = emitVertexPos(3);
|
||||||
|
|
||||||
|
//Emit tris
|
||||||
|
uint idxId = triId*3;
|
||||||
|
gl_PrimitiveIndicesNV[idxId++] = vertId+0;
|
||||||
|
gl_PrimitiveIndicesNV[idxId++] = vertId+1;
|
||||||
|
gl_PrimitiveIndicesNV[idxId++] = vertId+2;
|
||||||
|
|
||||||
|
gl_PrimitiveIndicesNV[idxId++] = vertId+0;
|
||||||
|
gl_PrimitiveIndicesNV[idxId++] = vertId+3;
|
||||||
|
gl_PrimitiveIndicesNV[idxId++] = vertId+1;
|
||||||
|
|
||||||
|
gl_MeshPrimitivesNV[triId].gl_PrimitiveID = int(qid);
|
||||||
|
gl_MeshPrimitivesNV[triId+1].gl_PrimitiveID = int(qid);
|
||||||
|
}*/
|
||||||
|
}
|
||||||
137
src/main/resources/assets/voxy/shaders/lod/mesh/task.glsl
Normal file
137
src/main/resources/assets/voxy/shaders/lod/mesh/task.glsl
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
#version 460 core
|
||||||
|
|
||||||
|
#extension GL_NV_mesh_shader : require
|
||||||
|
|
||||||
|
//TODO: maybe do 2 sections per workgroup instead of 1, this should double throughput with more complex sections
|
||||||
|
// however will require a rewrite of how the task payload functions, since we want to still keep it under 108 bytes
|
||||||
|
// in theory the maximum we can do is 4 sections in a workgroup
|
||||||
|
layout(local_size_x=1) in;
|
||||||
|
|
||||||
|
#import <voxy:lod/section.glsl>
|
||||||
|
|
||||||
|
layout(binding = 0, std140) uniform SceneUniform {
|
||||||
|
mat4 MVP;
|
||||||
|
ivec3 baseSectionPos;
|
||||||
|
uint frameId;
|
||||||
|
vec3 cameraSubPos;
|
||||||
|
uint pad_;
|
||||||
|
vec2 screenSize;
|
||||||
|
};
|
||||||
|
|
||||||
|
layout(binding = 1, std430) restrict readonly buffer IndirectSectionLookupBuffer {
|
||||||
|
uint sectionCount;
|
||||||
|
uint indirectLookup[];
|
||||||
|
};
|
||||||
|
|
||||||
|
layout(binding = 2, std430) restrict readonly buffer SectionBuffer {
|
||||||
|
SectionMeta sectionData[];
|
||||||
|
};
|
||||||
|
|
||||||
|
layout(binding = 3, std430) restrict readonly buffer VisibilityBuffer {
|
||||||
|
uint visibilityData[];
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef HAS_STATISTICS
|
||||||
|
layout(binding = STATISTICS_BUFFER_BINDING, std430) restrict buffer statisticsBuffer {
|
||||||
|
uint visibleSectionCounts[5];
|
||||||
|
uint quadCounts[5];
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
taskNV out Task {
|
||||||
|
//Tightly packed, prefix sum + offset
|
||||||
|
//uvec4 binA;
|
||||||
|
//uvec4 binB;
|
||||||
|
uint bins[8];
|
||||||
|
|
||||||
|
vec3 cameraOffset;
|
||||||
|
uint lodLvl;
|
||||||
|
|
||||||
|
uint baseQuad;
|
||||||
|
uint quadCount;
|
||||||
|
} task;
|
||||||
|
|
||||||
|
#define BIN(br, cnt) if (br) { task.bins[i++] = (sum<<16)|off; sum += cnt; } off += cnt;
|
||||||
|
//#define BIN(br, cnt) if (br) { batch[i++] = (sum<<16)|off; sum += cnt; } off += cnt;
|
||||||
|
|
||||||
|
bvec3 and(bvec3 a, bvec3 b) {
|
||||||
|
return bvec3(a.x&&b.x, a.y&&b.y, a.z&&b.z);
|
||||||
|
}
|
||||||
|
uint fillBins(uvec4 counts, ivec3 relative) {//Returns quad count
|
||||||
|
#pragma unroll
|
||||||
|
for (uint i = 0; i < 8; i++) task.bins[i] = uint(-1);
|
||||||
|
|
||||||
|
uvec3 cA = counts.yzw&0xFFFFu;
|
||||||
|
uvec3 cB = counts.yzw>>16;
|
||||||
|
|
||||||
|
bvec3 a = and(notEqual(cA, uvec3(0)), lessThanEqual(ivec3(0), relative.yzx));
|
||||||
|
bvec3 b = and(notEqual(cB, uvec3(0)), lessThanEqual(relative.yzx, ivec3(0)));
|
||||||
|
|
||||||
|
uint dsc = counts.x>>16;//double sided quads
|
||||||
|
uint sum = 0;
|
||||||
|
uint off = counts.x&0xFFFFu;//translucent quads
|
||||||
|
uint i = 0;
|
||||||
|
|
||||||
|
//TODO: might need to move this into shared memory or somethign? so that compiler can reason about it (or make the bin an array in here and mesh)
|
||||||
|
//uint batch[8] = {uint(-1), uint(-1), uint(-1), uint(-1), uint(-1),uint(-1),uint(-1),uint(-1)};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//TODO IDEA: add inline merging, meaning if previous bin was true and so are we, just increment sum, dont take up new bucket
|
||||||
|
// this should allow for a new minimum number of bins especially when combined with other sections in the subgroup
|
||||||
|
// with merging, worst case bin count is 4
|
||||||
|
BIN(dsc!=0, dsc);//Double sided quads
|
||||||
|
|
||||||
|
//TODO: compute prefix sums and then jsut batch set into the array (this is an optimization)
|
||||||
|
|
||||||
|
BIN(a.x, cA.x);//Down
|
||||||
|
BIN(b.x, cB.x);//Up
|
||||||
|
BIN(a.y, cA.y);//North
|
||||||
|
BIN(b.y, cB.y);//South
|
||||||
|
BIN(a.z, cA.z);//West
|
||||||
|
BIN(b.z, cB.z);//East
|
||||||
|
|
||||||
|
//task.binA = uvec4(batch[0], batch[1], batch[2], batch[3]);
|
||||||
|
//task.binB = uvec4(batch[4], batch[5], batch[6], batch[7]);
|
||||||
|
return sum;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
uint secId = indirectLookup[gl_WorkGroupID.x];
|
||||||
|
uint vis = visibilityData[secId];
|
||||||
|
|
||||||
|
bool shouldRender = (vis&0x7fffffffu) == frameId-1;//-1 since we are technically in the next frame for the primary rasterization
|
||||||
|
bool renderTemporally = (vis&0x80000000u)==0;// If we are the temporal specialization, only render if marked as render temporally
|
||||||
|
|
||||||
|
task.quadCount = 0;
|
||||||
|
|
||||||
|
if (shouldRender) {
|
||||||
|
SectionMeta section = sectionData[secId];
|
||||||
|
|
||||||
|
uint detail = extractDetail(section);
|
||||||
|
ivec3 ipos = extractPosition(section);
|
||||||
|
|
||||||
|
ivec3 relative = ipos-(baseSectionPos>>detail);
|
||||||
|
|
||||||
|
#ifdef HAS_STATISTICS
|
||||||
|
atomicAdd(visibleSectionCounts[detail], 1);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//TODO: here enqueue the id here for both translucent and temporal (if relevant) (* note technically dont need for temporal as can just check :tm: if we are in temporal render mode)
|
||||||
|
|
||||||
|
//TODO: in the temporal phase, extract the sections that are ment to be rendered and are also translucent
|
||||||
|
// enqueue them into a seperate buffer and increment the bin counters based on distance
|
||||||
|
// this should allow a massive simplificattion of the raster pipeline by eliminating all command gen shaders + prep shaders
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
task.baseQuad = extractQuadStart(section);
|
||||||
|
task.quadCount = fillBins(section.b, relative);
|
||||||
|
|
||||||
|
task.cameraOffset = vec3(((ipos<<detail) - baseSectionPos)<<5);
|
||||||
|
task.lodLvl = detail;
|
||||||
|
}
|
||||||
|
|
||||||
|
gl_TaskCountNV = (task.quadCount+(MESH_SIZE-1))/MESH_SIZE;
|
||||||
|
}
|
||||||
154
src/main/resources/assets/voxy/shaders/lod/mesh/task2.glsl
Normal file
154
src/main/resources/assets/voxy/shaders/lod/mesh/task2.glsl
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
#version 460 core
|
||||||
|
|
||||||
|
#extension GL_NV_mesh_shader : require
|
||||||
|
|
||||||
|
layout(local_size_x=4) in;
|
||||||
|
|
||||||
|
#import <voxy:lod/section.glsl>
|
||||||
|
|
||||||
|
bvec3 and(bvec3 a, bvec3 b) {
|
||||||
|
return bvec3(a.x&&b.x, a.y&&b.y, a.z&&b.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
layout(binding = 0, std140) uniform SceneUniform {
|
||||||
|
mat4 MVP;
|
||||||
|
ivec3 baseSectionPos;
|
||||||
|
uint frameId;
|
||||||
|
vec3 cameraSubPos;
|
||||||
|
uint pad_;
|
||||||
|
vec2 screenSize;
|
||||||
|
};
|
||||||
|
|
||||||
|
layout(binding = 1, std430) restrict readonly buffer IndirectSectionLookupBuffer {
|
||||||
|
uint sectionCount;
|
||||||
|
uint indirectLookup[];
|
||||||
|
};
|
||||||
|
|
||||||
|
layout(binding = 2, std430) restrict readonly buffer SectionBuffer {
|
||||||
|
SectionMeta sectionData[];
|
||||||
|
};
|
||||||
|
|
||||||
|
layout(binding = 3, std430) restrict readonly buffer VisibilityBuffer {
|
||||||
|
uint visibilityData[];
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef HAS_STATISTICS
|
||||||
|
layout(binding = STATISTICS_BUFFER_BINDING, std430) restrict buffer statisticsBuffer {
|
||||||
|
uint visibleSectionCounts[5];
|
||||||
|
uint quadCounts[5];
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
taskNV out Task {
|
||||||
|
uvec4 control;//the control vec it defines what subvector to use, it is effectivly the terminating ranges of each bin
|
||||||
|
uvec4 bins[4];//the bins for each section the last component of each bin is the quad offset
|
||||||
|
|
||||||
|
uint launchSize;
|
||||||
|
} task;
|
||||||
|
|
||||||
|
#define BIN(br, cnt) if (br) { if (!pset) {bin[i++] = (sum<<16)|off;} sum += cnt; } pset = br; off += cnt;
|
||||||
|
|
||||||
|
/*
|
||||||
|
void createBin(out uvec4 bin, out uint sum, out uint offset, uint dsc, bvec3 a, bvec3 b, uvec3 cA, uvec3 cB) {
|
||||||
|
bin = uvec4(-1);
|
||||||
|
|
||||||
|
bool pset = false;
|
||||||
|
uint i = 0;
|
||||||
|
sum = 0;
|
||||||
|
offset = counts.x&0xFFFFu;//translucent quads
|
||||||
|
|
||||||
|
uint dsc = counts.x>>16;//double sided quads
|
||||||
|
|
||||||
|
uint off = counts.x&0xFFFFu;//translucent quads
|
||||||
|
uint i = 0;
|
||||||
|
|
||||||
|
BIN(dsc!=0, dsc);//Double sided quads
|
||||||
|
|
||||||
|
|
||||||
|
BIN(a.x, cA.x);//Down
|
||||||
|
BIN(b.x, cB.x);//Up
|
||||||
|
BIN(a.y, cA.y);//North
|
||||||
|
BIN(b.y, cB.y);//South
|
||||||
|
BIN(a.z, cA.z);//West
|
||||||
|
BIN(b.z, cB.z);//East
|
||||||
|
}*/
|
||||||
|
|
||||||
|
uint fillBins(uvec4 counts, ivec3 relative) {//Returns quad count
|
||||||
|
uvec3 cA = counts.yzw&0xFFFFu;
|
||||||
|
uvec3 cB = counts.yzw>>16;
|
||||||
|
|
||||||
|
bvec3 a = and(notEqual(cA, uvec3(0)), lessThanEqual(ivec3(0), relative.yzx));
|
||||||
|
bvec3 b = and(notEqual(cB, uvec3(0)), lessThanEqual(relative.yzx, ivec3(0)));
|
||||||
|
|
||||||
|
//compute the merged bin values
|
||||||
|
uvec4 bin = uvec4(-1);
|
||||||
|
|
||||||
|
bool pset = false;
|
||||||
|
uint i = 0;
|
||||||
|
uint sum = 0;
|
||||||
|
uint offset = counts.x&0xFFFFu;//translucent quads
|
||||||
|
|
||||||
|
uint dsc = counts.x>>16;//double sided quads
|
||||||
|
|
||||||
|
uint off = counts.x&0xFFFFu;//translucent quads
|
||||||
|
uint i = 0;
|
||||||
|
|
||||||
|
BIN(dsc!=0, dsc);//Double sided quads
|
||||||
|
|
||||||
|
|
||||||
|
BIN(a.x, cA.x);//Down
|
||||||
|
BIN(b.x, cB.x);//Up
|
||||||
|
BIN(a.y, cA.y);//North
|
||||||
|
BIN(b.y, cB.y);//South
|
||||||
|
BIN(a.z, cA.z);//West
|
||||||
|
BIN(b.z, cB.z);//East
|
||||||
|
|
||||||
|
//bin contains filled bin data, non filled slots contain -1
|
||||||
|
|
||||||
|
return sum;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
if (sectionCount<=gl_GlobalInvocationID.x) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (subgroupElect()) {
|
||||||
|
task.quadCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint secId = indirectLookup[gl_GlobalInvocationID.x];
|
||||||
|
uint vis = visibilityData[secId];
|
||||||
|
|
||||||
|
bool shouldRender = (vis&0x7fffffffu) == frameId-1;//-1 since we are technically in the next frame for the primary rasterization
|
||||||
|
bool renderTemporally = (vis&0x80000000u)==0;// If we are the temporal specialization, only render if marked as render temporally
|
||||||
|
|
||||||
|
if (shouldRender) {
|
||||||
|
SectionMeta section = sectionData[secId];
|
||||||
|
|
||||||
|
uint detail = extractDetail(section);
|
||||||
|
ivec3 ipos = extractPosition(section);
|
||||||
|
|
||||||
|
ivec3 relative = ipos-(baseSectionPos>>detail);
|
||||||
|
|
||||||
|
#ifdef HAS_STATISTICS
|
||||||
|
atomicAdd(visibleSectionCounts[detail], 1);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//TODO: here enqueue the id here for both translucent and temporal (if relevant) (* note technically dont need for temporal as can just check :tm: if we are in temporal render mode)
|
||||||
|
|
||||||
|
//TODO: in the temporal phase, extract the sections that are ment to be rendered and are also translucent
|
||||||
|
// enqueue them into a seperate buffer and increment the bin counters based on distance
|
||||||
|
// this should allow a massive simplificattion of the raster pipeline by eliminating all command gen shaders + prep shaders
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
task.baseQuad = extractQuadStart(section);
|
||||||
|
task.quadCount = fillBins(section.b, relative);
|
||||||
|
|
||||||
|
task.cameraOffset = vec3(((ipos<<detail) - baseSectionPos)<<5);
|
||||||
|
task.lodLvl = detail;
|
||||||
|
}
|
||||||
|
|
||||||
|
gl_TaskCountNV = (task.quadCount+(MESH_SIZE-1))/MESH_SIZE;
|
||||||
|
}
|
||||||
90
src/main/resources/assets/voxy/shaders/lod/meshext/frag.glsl
Normal file
90
src/main/resources/assets/voxy/shaders/lod/meshext/frag.glsl
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
#version 460 core
|
||||||
|
|
||||||
|
#extension GL_EXT_mesh_shader : require
|
||||||
|
|
||||||
|
layout(binding = 0) uniform sampler2D blockModelAtlas;
|
||||||
|
layout(binding = 2) uniform sampler2D depthTex;
|
||||||
|
|
||||||
|
layout(location=1) perprimitiveEXT in PerPrimData {
|
||||||
|
uvec4 data;
|
||||||
|
} primIn;
|
||||||
|
|
||||||
|
layout(location = 0) out vec4 outColour;
|
||||||
|
|
||||||
|
vec4 uint2vec4RGBA(uint colour) {
|
||||||
|
return vec4((uvec4(colour)>>uvec4(24,16,8,0))&uvec4(0xFF))/255.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool useMipmaps() {
|
||||||
|
return (primIn.data.x&2u)==0u;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool useTinting() {
|
||||||
|
return (primIn.data.x&4u)!=0u;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool useCutout() {
|
||||||
|
return (primIn.data.x&1u)==1u;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 computeColour(vec4 colour) {
|
||||||
|
//Conditional tinting, TODO: FIXME: REPLACE WITH MASK OR SOMETHING, like encode data into the top bit of alpha
|
||||||
|
if (useTinting() && abs(colour.r-colour.g) < 0.02f && abs(colour.g-colour.b) < 0.02f) {
|
||||||
|
colour *= uint2vec4RGBA(primIn.data.z).yzwx;
|
||||||
|
}
|
||||||
|
return (colour * uint2vec4RGBA(primIn.data.y)) + vec4(0,0,0,float(primIn.data.w&0xFFu)/255);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint getFace() {
|
||||||
|
return (primIn.data.w>>8)&7u;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec2 getBaseUV() {
|
||||||
|
uint face = getFace();
|
||||||
|
uint modelId = primIn.data.x>>16;
|
||||||
|
vec2 modelUV = vec2(modelId&0xFFu, (modelId>>8)&0xFFu)*(1.0/(256.0));
|
||||||
|
return modelUV + (vec2(face>>1, face&1u) * (1.0/(vec2(3.0, 2.0)*256.0)));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec2 uv = vec2(0);
|
||||||
|
|
||||||
|
//Tile is the tile we are in
|
||||||
|
vec2 tile;
|
||||||
|
vec2 uv2 = modf(uv, tile)*(1.0/(vec2(3.0,2.0)*256.0));
|
||||||
|
vec4 colour;
|
||||||
|
vec2 texPos = uv2 + getBaseUV();
|
||||||
|
if (useMipmaps()) {
|
||||||
|
vec2 uvSmol = uv*(1.0/(vec2(3.0,2.0)*256.0));
|
||||||
|
vec2 dx = dFdx(uvSmol);//vec2(lDx, dDx);
|
||||||
|
vec2 dy = dFdy(uvSmol);//vec2(lDy, dDy);
|
||||||
|
colour = textureGrad(blockModelAtlas, texPos, dx, dy);
|
||||||
|
} else {
|
||||||
|
colour = textureLod(blockModelAtlas, texPos, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (any(notEqual(clamp(tile, vec2(0), vec2((primIn.data.x>>8)&0xFu, (primIn.data.x>>12)&0xFu)), tile))) {
|
||||||
|
discard;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Check the minimum bounding texture and ensure we are greater than it
|
||||||
|
if (gl_FragCoord.z < texelFetch(depthTex, ivec2(gl_FragCoord.xy), 0).r) {
|
||||||
|
discard;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//Also, small quad is really fking over the mipping level somehow
|
||||||
|
if (useCutout() && (textureLod(blockModelAtlas, texPos, 0).a <= 0.1f)) {
|
||||||
|
//This is stupidly stupidly bad for divergence
|
||||||
|
//TODO: FIXME, basicly what this do is sample the exact pixel (no lod) for discarding, this stops mipmapping fucking it over
|
||||||
|
#ifndef DEBUG_RENDER
|
||||||
|
discard;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
colour = computeColour(colour);
|
||||||
|
|
||||||
|
outColour = colour;
|
||||||
|
}
|
||||||
327
src/main/resources/assets/voxy/shaders/lod/meshext/mesh.glsl
Normal file
327
src/main/resources/assets/voxy/shaders/lod/meshext/mesh.glsl
Normal file
@@ -0,0 +1,327 @@
|
|||||||
|
#version 460 core
|
||||||
|
|
||||||
|
#extension GL_EXT_mesh_shader : require
|
||||||
|
|
||||||
|
#extension GL_ARB_gpu_shader_int64 : enable
|
||||||
|
|
||||||
|
#extension GL_KHR_shader_subgroup_arithmetic: require
|
||||||
|
#extension GL_KHR_shader_subgroup_basic : require
|
||||||
|
#extension GL_KHR_shader_subgroup_ballot : require
|
||||||
|
#extension GL_KHR_shader_subgroup_vote : require
|
||||||
|
|
||||||
|
|
||||||
|
//TODO: finetune the local size and emission size
|
||||||
|
layout(local_size_x = MESH_SIZE, local_size_y=1, local_size_z=1) in;
|
||||||
|
layout(triangles, max_vertices=(MESH_SIZE*4), max_primitives=(MESH_SIZE*2)) out;
|
||||||
|
|
||||||
|
struct Task {
|
||||||
|
//Tightly packed, prefix sum + offset
|
||||||
|
//uvec4 binA;
|
||||||
|
//uvec4 binB;
|
||||||
|
uint bins[8];
|
||||||
|
|
||||||
|
vec3 cameraOffset;
|
||||||
|
uint lodLvl;
|
||||||
|
|
||||||
|
uint baseQuad;
|
||||||
|
uint quadCount;
|
||||||
|
};
|
||||||
|
|
||||||
|
taskPayloadSharedEXT Task task;
|
||||||
|
|
||||||
|
layout(location=1) perprimitiveEXT out PerPrimData {
|
||||||
|
uvec4 data;
|
||||||
|
} primOut[];
|
||||||
|
|
||||||
|
|
||||||
|
uint getQuadId() {
|
||||||
|
uint mid = gl_GlobalInvocationID.x;
|
||||||
|
uint cv = (mid<<16)|0xFFFFu;
|
||||||
|
/*
|
||||||
|
//Funny method
|
||||||
|
uvec4 a = mix(uvec4(0), uvec4( 1, 2, 4, 8), lessThanEqual(uvec4(task.bins[0],task.bins[1],task.bins[2],task.bins[3]), uvec4(cv))) +
|
||||||
|
mix(uvec4(0), uvec4(16,32,64,128), lessThanEqual(uvec4(task.bins[4],task.bins[5],task.bins[6],task.bins[7]), uvec4(cv)));
|
||||||
|
uint act = a.x+a.y+a.z+a.w;
|
||||||
|
uint id = findLSB(act^(act>>1));
|
||||||
|
|
||||||
|
//uint point = mix(binB, binA, id<4)[id&3u];
|
||||||
|
uint point = task.bins[id];
|
||||||
|
|
||||||
|
return (point&0xFFFFu)+(mid-(point>>16));
|
||||||
|
*/
|
||||||
|
#pragma unroll
|
||||||
|
for (uint i = 0; i<7; i++) {
|
||||||
|
uint point = task.bins[i];
|
||||||
|
if (point<=cv&&cv<task.bins[i+1]) {
|
||||||
|
return (point&0xFFFFu)+(mid-(point>>16));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
for (uint i = 0; i<7; i++) {
|
||||||
|
uint point = task.bins[i];
|
||||||
|
if (point <= ((mid<<16)|0xFFFFu) && ((mid<<16)|0xFFFFu)<task.bins[i+1]) {
|
||||||
|
binId = i;
|
||||||
|
return (point&0xFFFFu)+(mid-(point>>16));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
#import <voxy:lod/quad_format.glsl>
|
||||||
|
#import <voxy:lod/block_model.glsl>
|
||||||
|
|
||||||
|
layout(binding = 0, std140) uniform SceneUniform {
|
||||||
|
mat4 MVP;
|
||||||
|
ivec3 baseSectionPos;
|
||||||
|
uint frameId;
|
||||||
|
vec3 cameraSubPos;
|
||||||
|
uint pad_;
|
||||||
|
vec2 screenSize;
|
||||||
|
};
|
||||||
|
|
||||||
|
layout(binding = 4, std430) readonly restrict buffer QuadBuffer {
|
||||||
|
Quad quadData[];
|
||||||
|
};
|
||||||
|
|
||||||
|
layout(binding = 5, std430) readonly restrict buffer ModelBuffer {
|
||||||
|
BlockModel modelData[];
|
||||||
|
};
|
||||||
|
|
||||||
|
layout(binding = 6, std430) readonly restrict buffer ModelColourBuffer {
|
||||||
|
uint colourData[];
|
||||||
|
};
|
||||||
|
|
||||||
|
layout(binding = 1) uniform sampler2D lightSampler;
|
||||||
|
vec4 getLighting(uint index) {
|
||||||
|
int i2 = int(index);
|
||||||
|
return texture(lightSampler, clamp((vec2((i2>>4)&0xF, i2&0xF))/16, vec2(8.0f/255), vec2(248.0f/255)));
|
||||||
|
}
|
||||||
|
|
||||||
|
//===============
|
||||||
|
|
||||||
|
|
||||||
|
vec3 swizzelDataAxis(uint axis, vec3 data) {
|
||||||
|
return mix(mix(data.zxy,data.xzy,bvec3(axis==0)),data,bvec3(axis==1));
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 getFaceSize(uint faceData) {
|
||||||
|
float EPSILON = 0.00005f;
|
||||||
|
|
||||||
|
vec4 faceOffsetsSizes = extractFaceSizes(faceData);
|
||||||
|
|
||||||
|
//Expand the quads by a very small amount (because of the subtraction after this also becomes an implicit add)
|
||||||
|
faceOffsetsSizes.xz -= vec2(EPSILON);
|
||||||
|
|
||||||
|
//Make the end relative to the start
|
||||||
|
faceOffsetsSizes.yw -= faceOffsetsSizes.xz;
|
||||||
|
|
||||||
|
return faceOffsetsSizes;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 faceNormal(uint face) {
|
||||||
|
//TODO: optimize this garbage
|
||||||
|
return vec3(uint((face>>1)==2), uint((face>>1)==0), uint((face>>1)==1)) * (float(int(face)&1)*2-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint packVec4(vec4 vec) {
|
||||||
|
uvec4 vec_=uvec4(vec*255)<<uvec4(24,16,8,0);
|
||||||
|
return vec_.x|vec_.y|vec_.z|vec_.w;
|
||||||
|
}
|
||||||
|
|
||||||
|
//===============
|
||||||
|
vec3 cornerPos;
|
||||||
|
vec2 axisFaceSize;
|
||||||
|
uint face;
|
||||||
|
|
||||||
|
vec4 faceSize;
|
||||||
|
|
||||||
|
uint modelId;
|
||||||
|
BlockModel model;
|
||||||
|
uint faceData;
|
||||||
|
bool isTranslucent;
|
||||||
|
bool hasAO;
|
||||||
|
bool isShaded;
|
||||||
|
|
||||||
|
void setup(Quad quad) {
|
||||||
|
face = extractFace(quad);
|
||||||
|
modelId = extractStateId(quad);
|
||||||
|
model = modelData[modelId];
|
||||||
|
faceData = model.faceData[face];
|
||||||
|
isTranslucent = modelIsTranslucent(model);
|
||||||
|
hasAO = modelHasMipmaps(model);//TODO: replace with per face AO flag
|
||||||
|
isShaded = hasAO;//TODO: make this a per face flag
|
||||||
|
|
||||||
|
ivec2 quadSize = extractSize(quad);
|
||||||
|
|
||||||
|
faceSize = getFaceSize(faceData);
|
||||||
|
|
||||||
|
cornerPos = extractPos(quad);
|
||||||
|
float depthOffset = extractFaceIndentation(faceData);
|
||||||
|
cornerPos += swizzelDataAxis(face>>1, vec3(faceSize.xz, mix(depthOffset, 1-depthOffset, float(face&1u))));
|
||||||
|
cornerPos *= (1<<task.lodLvl);
|
||||||
|
cornerPos += task.cameraOffset;
|
||||||
|
|
||||||
|
axisFaceSize = (faceSize.yw + quadSize - 1);
|
||||||
|
|
||||||
|
//uv =
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
vec2 getUvCorner(uint corner) {
|
||||||
|
return faceSize.xz + axisFaceSize*vec2((corner>>1)&1u, corner&1u);;
|
||||||
|
}
|
||||||
|
|
||||||
|
uvec4 createQuadData(Quad quad) {
|
||||||
|
uint flags = faceHasAlphaCuttout(faceData);
|
||||||
|
|
||||||
|
ivec2 quadSize = extractSize(quad);
|
||||||
|
//We need to have a conditional override based on if the model size is < a full face + quadSize > 1
|
||||||
|
flags |= uint(any(greaterThan(quadSize, ivec2(1)))) & faceHasAlphaCuttoutOverride(faceData);
|
||||||
|
|
||||||
|
flags |= uint(!modelHasMipmaps(model))<<1;
|
||||||
|
|
||||||
|
//Compute lighting
|
||||||
|
vec4 tinting = getLighting(extractLightId(quad));
|
||||||
|
|
||||||
|
//Apply model colour tinting
|
||||||
|
uint tintColour = model.colourTint;
|
||||||
|
if (modelHasBiomeLUT(model)) {
|
||||||
|
tintColour = colourData[tintColour + extractBiomeId(quad)];
|
||||||
|
}
|
||||||
|
|
||||||
|
uint conditionalTinting = 0;
|
||||||
|
if (tintColour != uint(-1)) {
|
||||||
|
flags |= 1u<<2;
|
||||||
|
conditionalTinting = tintColour;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint addin = 0;
|
||||||
|
if (!isTranslucent) {
|
||||||
|
tinting.w = 0.0;
|
||||||
|
//Encode the face, the lod level and
|
||||||
|
uint encodedData = 0;
|
||||||
|
encodedData |= face;
|
||||||
|
encodedData |= (task.lodLvl<<3);
|
||||||
|
encodedData |= uint(hasAO)<<6;
|
||||||
|
addin = encodedData;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Apply face tint
|
||||||
|
if (isShaded) {
|
||||||
|
//TODO: make branchless, infact apply ahead of time to the texture itself in ModelManager since that is
|
||||||
|
// per face
|
||||||
|
if ((face>>1) == 1) {//NORTH, SOUTH
|
||||||
|
tinting.xyz *= 0.8f;
|
||||||
|
} else if ((face>>1) == 2) {//EAST, WEST
|
||||||
|
tinting.xyz *= 0.6f;
|
||||||
|
} else if (face == 0) {//DOWN
|
||||||
|
tinting.xyz *= 0.5f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
uvec4 interData;
|
||||||
|
|
||||||
|
interData.x = (modelId<<16) | flags | (uint(quadSize.x-1)<<8) | (uint(quadSize.y-1)<<12);
|
||||||
|
|
||||||
|
interData.y = packVec4(tinting);
|
||||||
|
interData.z = conditionalTinting;
|
||||||
|
interData.w = addin|(face<<8);
|
||||||
|
|
||||||
|
return interData;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 emitVertexPos(int corner) {
|
||||||
|
vec3 pointPos = swizzelDataAxis(face>>1,vec3(axisFaceSize*mix(vec2(0),vec2(1<<task.lodLvl),bvec2((corner>>1)&1, corner&1)),0))+cornerPos;
|
||||||
|
return MVP*vec4(pointPos, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
bvec2 whatRender(vec4 p1, vec4 p2, vec4 p0, vec4 p3) {
|
||||||
|
vec2 ssmin = ((p1.xy/p1.w)+1)*screenSize;
|
||||||
|
vec2 ssmax = ssmin;
|
||||||
|
|
||||||
|
vec2 point = ((p2.xy/p2.w)+1)*screenSize;
|
||||||
|
ssmin = min(ssmin, point);
|
||||||
|
ssmax = max(ssmax, point);
|
||||||
|
|
||||||
|
point = ((p0.xy/p0.w)+1)*screenSize;
|
||||||
|
vec2 t0min = min(ssmin, point);
|
||||||
|
vec2 t0max = max(ssmax, point);
|
||||||
|
|
||||||
|
point = ((p3.xy/p3.w)+1)*screenSize;
|
||||||
|
vec2 t1min = min(ssmin, point);
|
||||||
|
vec2 t1max = max(ssmax, point);
|
||||||
|
|
||||||
|
//Possibly cull the triangles if they dont cover the center of a pixel on the screen (degen)
|
||||||
|
float degenBias = 0.01f;
|
||||||
|
bool t0draw = all(notEqual(round(t0min-degenBias),round(t0max+degenBias)));
|
||||||
|
bool t1draw = all(notEqual(round(t1min-degenBias),round(t1max+degenBias)));
|
||||||
|
return bvec2(t0draw, t1draw);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef HAS_STATISTICS
|
||||||
|
layout(binding = STATISTICS_BUFFER_BINDING, std430) restrict buffer statisticsBuffer {
|
||||||
|
uint visibleSectionCounts[5];
|
||||||
|
uint quadCounts[5];
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
uint qid = uint(-1);
|
||||||
|
Quad quad;
|
||||||
|
if (gl_GlobalInvocationID.x<task.quadCount) {
|
||||||
|
uint qid = getQuadId() + task.baseQuad;
|
||||||
|
quad = quadData[qid];
|
||||||
|
setup(quad);
|
||||||
|
bool render = dot(faceNormal(face), cornerPos-cameraSubPos) <= 0;
|
||||||
|
qid = render?qid:uint(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
subgroupBarrier();
|
||||||
|
uint triId_ = subgroupExclusiveAdd(qid!=uint(-1)?2:0);
|
||||||
|
|
||||||
|
uint qc = subgroupMax(triId_+(qid!=uint(-1)?2:0));
|
||||||
|
SetMeshOutputsEXT(qc*2, qc);
|
||||||
|
|
||||||
|
#ifdef HAS_STATISTICS
|
||||||
|
if (subgroupElect()) {
|
||||||
|
atomicAdd(quadCounts[task.lodLvl], qc);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if (qid != uint(-1)) {
|
||||||
|
uint vertId_ = triId_*2;//hack cause we are emitting full quads (2 tris) just to test
|
||||||
|
uint triId = triId_;
|
||||||
|
uint vertId = vertId_;
|
||||||
|
|
||||||
|
vec4 p1 = emitVertexPos(1);
|
||||||
|
vec4 p2 = emitVertexPos(2);
|
||||||
|
vec4 p0 = emitVertexPos(0);
|
||||||
|
vec4 p3 = emitVertexPos(3);
|
||||||
|
|
||||||
|
uvec4 data = createQuadData(quad);
|
||||||
|
|
||||||
|
//Emit common
|
||||||
|
gl_MeshVerticesEXT[vertId++].gl_Position = p1;
|
||||||
|
gl_MeshVerticesEXT[vertId++].gl_Position = p2;
|
||||||
|
|
||||||
|
|
||||||
|
//Prim 1
|
||||||
|
gl_PrimitiveTriangleIndicesEXT[triId] = uvec3(vertId_+0, vertId_+1, vertId);
|
||||||
|
gl_MeshVerticesEXT[vertId++].gl_Position = p0;
|
||||||
|
primOut[triId++].data = data;
|
||||||
|
|
||||||
|
//Prim 2
|
||||||
|
gl_PrimitiveTriangleIndicesEXT[triId] = uvec3(vertId_+0, vertId, vertId_+1);
|
||||||
|
gl_MeshVerticesEXT[vertId++].gl_Position = p3;
|
||||||
|
primOut[triId++].data = data;
|
||||||
|
}
|
||||||
|
}
|
||||||
125
src/main/resources/assets/voxy/shaders/lod/meshext/task.glsl
Normal file
125
src/main/resources/assets/voxy/shaders/lod/meshext/task.glsl
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
#version 460 core
|
||||||
|
|
||||||
|
#extension GL_EXT_mesh_shader : require
|
||||||
|
|
||||||
|
layout(local_size_x=1, local_size_y=1, local_size_z=1) in;
|
||||||
|
|
||||||
|
#import <voxy:lod/section.glsl>
|
||||||
|
|
||||||
|
layout(binding = 0, std140) uniform SceneUniform {
|
||||||
|
mat4 MVP;
|
||||||
|
ivec3 baseSectionPos;
|
||||||
|
uint frameId;
|
||||||
|
vec3 cameraSubPos;
|
||||||
|
uint pad_;
|
||||||
|
vec2 screenSize;
|
||||||
|
};
|
||||||
|
|
||||||
|
layout(binding = 1, std430) restrict readonly buffer IndirectSectionLookupBuffer {
|
||||||
|
uint sectionCount;
|
||||||
|
uint indirectLookup[];
|
||||||
|
};
|
||||||
|
|
||||||
|
layout(binding = 2, std430) restrict readonly buffer SectionBuffer {
|
||||||
|
SectionMeta sectionData[];
|
||||||
|
};
|
||||||
|
|
||||||
|
layout(binding = 3, std430) restrict readonly buffer VisibilityBuffer {
|
||||||
|
uint visibilityData[];
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef HAS_STATISTICS
|
||||||
|
layout(binding = STATISTICS_BUFFER_BINDING, std430) restrict buffer statisticsBuffer {
|
||||||
|
uint visibleSectionCounts[5];
|
||||||
|
uint quadCounts[5];
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct Task {
|
||||||
|
//Tightly packed, prefix sum + offset
|
||||||
|
//uvec4 binA;
|
||||||
|
//uvec4 binB;
|
||||||
|
uint bins[8];
|
||||||
|
|
||||||
|
vec3 cameraOffset;
|
||||||
|
uint lodLvl;
|
||||||
|
|
||||||
|
uint baseQuad;
|
||||||
|
uint quadCount;
|
||||||
|
};
|
||||||
|
taskPayloadSharedEXT Task task;
|
||||||
|
|
||||||
|
#define BIN(br, cnt) if (br) { task.bins[i++] = (sum<<16)|off; sum += cnt; } off += cnt;
|
||||||
|
//#define BIN(br, cnt) if (br) { batch[i++] = (sum<<16)|off; sum += cnt; } off += cnt;
|
||||||
|
|
||||||
|
bvec3 and(bvec3 a, bvec3 b) {
|
||||||
|
return bvec3(a.x&&b.x, a.y&&b.y, a.z&&b.z);
|
||||||
|
}
|
||||||
|
uint fillBins(uvec4 counts, ivec3 relative) {//Returns quad count
|
||||||
|
#pragma unroll
|
||||||
|
for (uint i = 0; i < 8; i++) task.bins[i] = uint(-1);
|
||||||
|
|
||||||
|
uvec3 cA = counts.yzw&0xFFFFu;
|
||||||
|
uvec3 cB = counts.yzw>>16;
|
||||||
|
|
||||||
|
bvec3 a = and(notEqual(cA, uvec3(0)), lessThanEqual(ivec3(0), relative.yzx));
|
||||||
|
bvec3 b = and(notEqual(cB, uvec3(0)), lessThanEqual(relative.yzx, ivec3(0)));
|
||||||
|
|
||||||
|
uint dsc = counts.x>>16;//double sided quads
|
||||||
|
uint sum = 0;
|
||||||
|
uint off = counts.x&0xFFFFu;//translucent quads
|
||||||
|
uint i = 0;
|
||||||
|
|
||||||
|
//TODO: might need to move this into shared memory or somethign? so that compiler can reason about it (or make the bin an array in here and mesh)
|
||||||
|
//uint batch[8] = {uint(-1), uint(-1), uint(-1), uint(-1), uint(-1),uint(-1),uint(-1),uint(-1)};
|
||||||
|
|
||||||
|
BIN(dsc!=0, dsc);//Double sided quads
|
||||||
|
|
||||||
|
//TODO: compute prefix sums and then jsut batch set into the array (this is an optimization)
|
||||||
|
|
||||||
|
BIN(a.x, cA.x);//Down
|
||||||
|
BIN(b.x, cB.x);//Up
|
||||||
|
BIN(a.y, cA.y);//North
|
||||||
|
BIN(b.y, cB.y);//South
|
||||||
|
BIN(a.z, cA.z);//West
|
||||||
|
BIN(b.z, cB.z);//East
|
||||||
|
|
||||||
|
//task.binA = uvec4(batch[0], batch[1], batch[2], batch[3]);
|
||||||
|
//task.binB = uvec4(batch[4], batch[5], batch[6], batch[7]);
|
||||||
|
return sum;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
uint secId = indirectLookup[gl_WorkGroupID.x];
|
||||||
|
uint vis = visibilityData[secId];
|
||||||
|
|
||||||
|
bool shouldRender = (vis&0x7fffffffu) == frameId-1;//-1 since we are technically in the next frame for the primary rasterization
|
||||||
|
bool renderTemporally = (vis&0x80000000u)==0;
|
||||||
|
|
||||||
|
task.quadCount = 0;
|
||||||
|
|
||||||
|
if (shouldRender) {
|
||||||
|
SectionMeta section = sectionData[secId];
|
||||||
|
|
||||||
|
uint detail = extractDetail(section);
|
||||||
|
ivec3 ipos = extractPosition(section);
|
||||||
|
|
||||||
|
ivec3 relative = ipos-(baseSectionPos>>detail);
|
||||||
|
|
||||||
|
#ifdef HAS_STATISTICS
|
||||||
|
atomicAdd(visibleSectionCounts[detail], 1);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//TODO: here enqueue the id here for both translucent and temporal (if relevant) (* note technically dont need for temporal as can just check :tm: if we are in temporal render mode)
|
||||||
|
|
||||||
|
task.baseQuad = extractQuadStart(section);
|
||||||
|
task.quadCount = fillBins(section.b, relative);
|
||||||
|
|
||||||
|
task.cameraOffset = vec3(((ipos<<detail) - baseSectionPos)<<5);
|
||||||
|
task.lodLvl = detail;
|
||||||
|
}
|
||||||
|
|
||||||
|
//It appears to be valid to read from taskPayloadSharedEXT
|
||||||
|
EmitMeshTasksEXT((task.quadCount+(MESH_SIZE-1))/MESH_SIZE, 1, 1);
|
||||||
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
/*
|
||||||
struct SectionMeta {
|
struct SectionMeta {
|
||||||
uint posA;
|
uint posA;
|
||||||
uint posB;
|
uint posB;
|
||||||
@@ -8,29 +9,38 @@ struct SectionMeta {
|
|||||||
uint cntC;
|
uint cntC;
|
||||||
uint cntD;
|
uint cntD;
|
||||||
};
|
};
|
||||||
|
*/
|
||||||
|
struct SectionMeta {
|
||||||
|
uvec4 a;
|
||||||
|
uvec4 b;
|
||||||
|
};
|
||||||
|
|
||||||
|
uvec2 extractRawPos(SectionMeta section) {
|
||||||
|
return section.a.xy;
|
||||||
|
}
|
||||||
|
|
||||||
uint extractDetail(SectionMeta section) {
|
uint extractDetail(SectionMeta section) {
|
||||||
return section.posA>>28;
|
return section.a.x>>28;
|
||||||
}
|
}
|
||||||
|
|
||||||
ivec3 extractPosition(SectionMeta section) {
|
ivec3 extractPosition(SectionMeta section) {
|
||||||
int y = ((int(section.posA)<<4)>>24);
|
int y = ((int(section.a.x)<<4)>>24);
|
||||||
int x = (int(section.posB)<<4)>>8;
|
int x = (int(section.a.y)<<4)>>8;
|
||||||
int z = int((section.posA&((1u<<20)-1))<<4);
|
int z = int((section.a.x&((1u<<20)-1))<<4);
|
||||||
z |= int(section.posB>>28);
|
z |= int(section.a.y>>28);
|
||||||
z <<= 8;
|
z <<= 8;
|
||||||
z >>= 8;
|
z >>= 8;
|
||||||
return ivec3(x,y,z);
|
return ivec3(x,y,z);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint extractQuadStart(SectionMeta meta) {
|
uint extractQuadStart(SectionMeta meta) {
|
||||||
return meta.ptr;
|
return meta.a.w;
|
||||||
}
|
}
|
||||||
|
|
||||||
ivec3 extractAABBOffset(SectionMeta meta) {
|
ivec3 extractAABBOffset(SectionMeta meta) {
|
||||||
return (ivec3(meta.AABB)>>ivec3(0,5,10))&31;
|
return (ivec3(meta.a.z)>>ivec3(0,5,10))&31;
|
||||||
}
|
}
|
||||||
|
|
||||||
ivec3 extractAABBSize(SectionMeta meta) {
|
ivec3 extractAABBSize(SectionMeta meta) {
|
||||||
return ((ivec3(meta.AABB)>>ivec3(15,20,25))&31)+1;//The size is + 1 cause its always at least 1x1x1
|
return ((ivec3(meta.a.z)>>ivec3(15,20,25))&31)+1;//The size is + 1 cause its always at least 1x1x1
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,10 @@ layout(binding = 0) uniform sampler2D colourTex;
|
|||||||
layout(binding = 1) uniform sampler2D depthTex;
|
layout(binding = 1) uniform sampler2D depthTex;
|
||||||
layout(location = 2) uniform mat4 invProjMat;
|
layout(location = 2) uniform mat4 invProjMat;
|
||||||
layout(location = 3) uniform mat4 projMat;
|
layout(location = 3) uniform mat4 projMat;
|
||||||
|
#ifdef USE_ENV_FOG
|
||||||
|
layout(location = 4) uniform vec3 endParams;
|
||||||
|
layout(location = 5) uniform vec3 fogColour;
|
||||||
|
#endif
|
||||||
|
|
||||||
out vec4 colour;
|
out vec4 colour;
|
||||||
in vec2 UV;
|
in vec2 UV;
|
||||||
@@ -28,7 +32,16 @@ void main() {
|
|||||||
discard;
|
discard;
|
||||||
}
|
}
|
||||||
|
|
||||||
depth = projDepth(rev3d(vec3(UV.xy, depth)));
|
vec3 point = rev3d(vec3(UV.xy, depth));
|
||||||
|
|
||||||
|
#ifdef USE_ENV_FOG
|
||||||
|
{
|
||||||
|
float fogLerp = max(fma(min(length(point.xyz), endParams.x),endParams.y,endParams.z),0);//512 is 32*16 which is the render distance in blocks
|
||||||
|
colour.rgb = mix(colour.rgb, fogColour, fogLerp);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
depth = projDepth(point);
|
||||||
depth = min(1.0f-(2.0f/((1<<24)-1)), depth);
|
depth = min(1.0f-(2.0f/((1<<24)-1)), depth);
|
||||||
depth = depth * 0.5f + 0.5f;
|
depth = depth * 0.5f + 0.5f;
|
||||||
depth = gl_DepthRange.diff * depth + gl_DepthRange.near;
|
depth = gl_DepthRange.diff * depth + gl_DepthRange.near;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#version 460
|
#version 460
|
||||||
|
|
||||||
#extension GL_KHR_shader_subgroup_arithmetic: require
|
|
||||||
#extension GL_KHR_shader_subgroup_basic : require
|
#extension GL_KHR_shader_subgroup_basic : require
|
||||||
|
#extension GL_KHR_shader_subgroup_arithmetic: require
|
||||||
|
|
||||||
#define WORK_SIZE 256
|
#define WORK_SIZE 256
|
||||||
|
|
||||||
@@ -12,50 +12,10 @@ layout(binding = IO_BUFFER, std430) buffer InputBuffer {
|
|||||||
uvec4[] ioCount;
|
uvec4[] ioCount;
|
||||||
};
|
};
|
||||||
|
|
||||||
shared uint warpPrefixSum[32];//Warps are 32, tricks require full warp
|
shared uint warpPrefixSum[8];//Warps are 32, tricks require full warp
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
/*
|
if (gl_SubgroupSize == 32) {
|
||||||
uint subgroupId = gl_LocalInvocationID.x>>5;
|
|
||||||
warpPrefixSum[gl_SubgroupInvocationID] = 0;
|
|
||||||
memoryBarrierShared();
|
|
||||||
|
|
||||||
//todo
|
|
||||||
//assert(gl_SubgroupSize == 32);
|
|
||||||
//assert(gl_NumSubgroups == (WORK_SIZE>>5));
|
|
||||||
|
|
||||||
uint gid = gl_GlobalInvocationID.x;
|
|
||||||
uvec4 count = uvec4(0);
|
|
||||||
uint sum = 0;
|
|
||||||
{
|
|
||||||
uvec4 dat = ioCount[gid];
|
|
||||||
count.yzw = dat.xyz;
|
|
||||||
count.z += count.y;
|
|
||||||
count.w += count.z;
|
|
||||||
sum = count.w + dat.w;
|
|
||||||
}
|
|
||||||
|
|
||||||
barrier();
|
|
||||||
count += subgroupExclusiveAdd(sum);
|
|
||||||
|
|
||||||
if (gl_SubgroupInvocationID==31) {
|
|
||||||
warpPrefixSum[subgroupId] = count.x+sum;
|
|
||||||
}
|
|
||||||
memoryBarrierShared();
|
|
||||||
barrier();
|
|
||||||
uint val = warpPrefixSum[gl_SubgroupInvocationID];
|
|
||||||
barrier();
|
|
||||||
if (subgroupId == 0) {
|
|
||||||
//Use warp to do entire add in 1 reduction
|
|
||||||
warpPrefixSum[gl_SubgroupInvocationID] = subgroupExclusiveAdd(val);
|
|
||||||
}
|
|
||||||
memoryBarrierShared();
|
|
||||||
barrier();
|
|
||||||
count += warpPrefixSum[subgroupId];
|
|
||||||
ioCount[gid] = count;
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef IS_INTEL
|
#ifdef IS_INTEL
|
||||||
uint subgroupId = gl_LocalInvocationID.x>>5;
|
uint subgroupId = gl_LocalInvocationID.x>>5;
|
||||||
#else
|
#else
|
||||||
@@ -87,7 +47,7 @@ void main() {
|
|||||||
memoryBarrierShared();
|
memoryBarrierShared();
|
||||||
barrier();
|
barrier();
|
||||||
|
|
||||||
if (subgroupId == 0) {
|
if (gl_LocalInvocationID.x<8) {
|
||||||
uint val = warpPrefixSum[gl_SubgroupInvocationID];
|
uint val = warpPrefixSum[gl_SubgroupInvocationID];
|
||||||
subgroupBarrier();
|
subgroupBarrier();
|
||||||
//Use warp to do entire add in 1 reduction
|
//Use warp to do entire add in 1 reduction
|
||||||
@@ -100,4 +60,65 @@ void main() {
|
|||||||
//Add the computed sum across all threads and warps
|
//Add the computed sum across all threads and warps
|
||||||
count += warpPrefixSum[subgroupId];
|
count += warpPrefixSum[subgroupId];
|
||||||
ioCount[gid] = count;
|
ioCount[gid] = count;
|
||||||
|
} else {
|
||||||
|
#ifdef IS_INTEL
|
||||||
|
uint subgroupId = gl_LocalInvocationID.x>>6;
|
||||||
|
#else
|
||||||
|
uint subgroupId = gl_SubgroupID;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//todo
|
||||||
|
//assert(gl_SubgroupSize == 32);
|
||||||
|
//assert(gl_NumSubgroups == (WORK_SIZE>>5));
|
||||||
|
|
||||||
|
uint gid = gl_GlobalInvocationID.x;
|
||||||
|
uvec4 count = uvec4(0);
|
||||||
|
uint sum = 0;
|
||||||
|
{
|
||||||
|
uvec4 dat = ioCount[gid];
|
||||||
|
count.yzw = dat.xyz;
|
||||||
|
count.z += count.y;
|
||||||
|
count.w += count.z;
|
||||||
|
sum = count.w + dat.w;
|
||||||
|
}
|
||||||
|
subgroupBarrier();//Wait for all threads in the subgroup to get the buffer
|
||||||
|
|
||||||
|
count += subgroupExclusiveAdd(sum);
|
||||||
|
|
||||||
|
if (gl_SubgroupInvocationID==63) {
|
||||||
|
warpPrefixSum[subgroupId] = count.x+sum;
|
||||||
|
}
|
||||||
|
|
||||||
|
memoryBarrierShared();
|
||||||
|
barrier();
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef IS_WINDOWS
|
||||||
|
//hate amd hate amd hate amd hate amd
|
||||||
|
uint val = warpPrefixSum[gl_LocalInvocationID.x&3u];
|
||||||
|
|
||||||
|
subgroupBarrier();
|
||||||
|
//Use warp to do entire add in 1 reduction
|
||||||
|
uint extraJank = subgroupExclusiveAdd(val);
|
||||||
|
|
||||||
|
barrier();
|
||||||
|
if (gl_LocalInvocationID.x<4) {
|
||||||
|
warpPrefixSum[gl_LocalInvocationID.x] = extraJank;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
if (gl_LocalInvocationID.x<4) {
|
||||||
|
uint val = warpPrefixSum[gl_SubgroupInvocationID];
|
||||||
|
subgroupBarrier();
|
||||||
|
//Use warp to do entire add in 1 reduction
|
||||||
|
warpPrefixSum[gl_SubgroupInvocationID] = subgroupExclusiveAdd(val);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
memoryBarrierShared();
|
||||||
|
barrier();
|
||||||
|
|
||||||
|
//Add the computed sum across all threads and warps
|
||||||
|
count += warpPrefixSum[subgroupId];
|
||||||
|
ioCount[gid] = count;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -3,20 +3,24 @@
|
|||||||
"package": "me.cortex.voxy.client.mixin",
|
"package": "me.cortex.voxy.client.mixin",
|
||||||
"compatibilityLevel": "JAVA_17",
|
"compatibilityLevel": "JAVA_17",
|
||||||
"client": [
|
"client": [
|
||||||
"minecraft.MixinBackgroundRenderer",
|
"minecraft.MixinClientChunkManager",
|
||||||
"minecraft.MixinClientCommonNetworkHandler",
|
"minecraft.MixinClientCommonNetworkHandler",
|
||||||
"minecraft.MixinClientLoginNetworkHandler",
|
"minecraft.MixinClientLoginNetworkHandler",
|
||||||
"minecraft.MixinDebugHud",
|
"minecraft.MixinDebugHud",
|
||||||
|
"minecraft.MixinFogRenderer",
|
||||||
|
"minecraft.MixinGlDebug",
|
||||||
"minecraft.MixinMinecraftClient",
|
"minecraft.MixinMinecraftClient",
|
||||||
"minecraft.MixinThreadExecutor",
|
"minecraft.MixinThreadExecutor",
|
||||||
"minecraft.MixinWindow",
|
"minecraft.MixinWindow",
|
||||||
"minecraft.MixinWorldRenderer",
|
"minecraft.MixinWorldRenderer",
|
||||||
"minecraft.MixinGlDebug",
|
"nvidium.MixinRenderPipeline",
|
||||||
"sodium.MixinDefaultChunkRenderer",
|
"sodium.MixinDefaultChunkRenderer",
|
||||||
"sodium.MixinRenderSectionManager",
|
"sodium.MixinRenderSectionManager",
|
||||||
"sodium.MixinSodiumOptionsGUI"
|
"sodium.MixinSodiumOptionsGUI"
|
||||||
],
|
],
|
||||||
"injectors": {
|
"injectors": {
|
||||||
"defaultRequire": 1
|
"defaultRequire": 1
|
||||||
}
|
},
|
||||||
|
"mixins": [
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,7 +32,7 @@
|
|||||||
"common.voxy.mixins.json"
|
"common.voxy.mixins.json"
|
||||||
],
|
],
|
||||||
"depends": {
|
"depends": {
|
||||||
"minecraft": "1.21.5",
|
"minecraft": ["1.21.7","1.21.6"],
|
||||||
"fabricloader": ">=0.14.22",
|
"fabricloader": ">=0.14.22",
|
||||||
"fabric-api": ">=0.91.1",
|
"fabric-api": ">=0.91.1",
|
||||||
"sodium": ">=0.6.13"
|
"sodium": ">=0.6.13"
|
||||||
|
|||||||
@@ -6,9 +6,6 @@ accessible class net/minecraft/client/render/RenderLayer$MultiPhaseParameters
|
|||||||
accessible field net/minecraft/client/texture/SpriteContents image Lnet/minecraft/client/texture/NativeImage;
|
accessible field net/minecraft/client/texture/SpriteContents image Lnet/minecraft/client/texture/NativeImage;
|
||||||
accessible field net/minecraft/client/render/Frustum frustumIntersection Lorg/joml/FrustumIntersection;
|
accessible field net/minecraft/client/render/Frustum frustumIntersection Lorg/joml/FrustumIntersection;
|
||||||
accessible field net/minecraft/client/color/block/BlockColors providers Lnet/minecraft/util/collection/IdList;
|
accessible field net/minecraft/client/color/block/BlockColors providers Lnet/minecraft/util/collection/IdList;
|
||||||
accessible field net/minecraft/client/render/GameRenderer zoomX F
|
|
||||||
accessible field net/minecraft/client/render/GameRenderer zoomY F
|
|
||||||
accessible field net/minecraft/client/render/GameRenderer zoom F
|
|
||||||
accessible field net/minecraft/client/world/ClientWorld worldRenderer Lnet/minecraft/client/render/WorldRenderer;
|
accessible field net/minecraft/client/world/ClientWorld worldRenderer Lnet/minecraft/client/render/WorldRenderer;
|
||||||
accessible field net/minecraft/world/biome/source/BiomeAccess seed J
|
accessible field net/minecraft/world/biome/source/BiomeAccess seed J
|
||||||
|
|
||||||
@@ -27,7 +24,11 @@ accessible field net/minecraft/world/chunk/PalettedContainer$Data palette Lnet/m
|
|||||||
|
|
||||||
accessible field net/minecraft/client/gl/GlGpuBuffer id I
|
accessible field net/minecraft/client/gl/GlGpuBuffer id I
|
||||||
|
|
||||||
accessible field net/minecraft/client/gl/GlResourceManager currentProgram Lnet/minecraft/client/gl/ShaderProgram;
|
accessible field net/minecraft/client/gl/GlCommandEncoder currentProgram Lnet/minecraft/client/gl/ShaderProgram;
|
||||||
accessible field net/minecraft/client/gl/GlResourceManager currentPipeline Lcom/mojang/blaze3d/pipeline/RenderPipeline;
|
accessible field net/minecraft/client/gl/GlCommandEncoder currentPipeline Lcom/mojang/blaze3d/pipeline/RenderPipeline;
|
||||||
|
|
||||||
accessible class net/minecraft/client/gl/GlDebug$DebugMessage
|
accessible class net/minecraft/client/gl/GlDebug$DebugMessage
|
||||||
|
|
||||||
|
accessible class net/minecraft/client/world/ClientChunkManager$ClientChunkMap
|
||||||
|
accessible method net/minecraft/client/world/ClientChunkManager$ClientChunkMap getChunk (I)Lnet/minecraft/world/chunk/WorldChunk;
|
||||||
|
accessible method net/minecraft/client/world/ClientChunkManager$ClientChunkMap getIndex (II)I
|
||||||
Reference in New Issue
Block a user