44 Commits

Author SHA1 Message Date
mcrcortex
a06fc754fc bring 1.21.8 up to date 2025-10-04 12:38:49 +10:00
mcrcortex
8546a754c7 things 2025-10-04 12:35:18 +10:00
mcrcortex
1438c16558 Sodium update 2025-10-01 16:06:41 +10:00
mcrcortex
0968b25968 things 2025-09-30 18:59:15 +10:00
mcrcortex
b364713268 if 2025-09-28 14:11:03 +10:00
mcrcortex
ff7aecadb2 compute neighbor mask 2025-09-26 10:24:35 +10:00
mcrcortex
02d0d024d6 Changes to commands 2025-09-26 10:02:11 +10:00
mcrcortex
471d00b534 TODOS and tweeks 2025-09-26 10:01:58 +10:00
mcrcortex
0cf46b3d5d dont crash on blockstate deserialization errors 2025-09-25 19:59:11 +10:00
mcrcortex
bc4bf03a64 force fluid tinting 2025-09-24 23:08:32 +10:00
mcrcortex
77d51dd27d use tinting metadata in vert/frag 2025-09-24 22:52:46 +10:00
mcrcortex
474a8a7e3c model metadata 2025-09-24 22:32:27 +10:00
mcrcortex
ae7004f5d8 reduce batching delay to 10 2025-09-24 13:27:12 +10:00
mcrcortex
944e1c3c7f tweek 2025-09-24 13:09:21 +10:00
mcrcortex
83975c8a98 fix using wrong mesher during building 2025-09-24 11:10:44 +10:00
mcrcortex
6f748cbe12 taa file override 2025-09-23 21:11:33 +10:00
mcrcortex
7ca910e35a fixes 2025-09-23 13:05:41 +10:00
mcrcortex
3f94bba49e not air return 2025-09-22 13:47:13 +10:00
mcrcortex
301d587535 ingest chunk section on block change on board 2025-09-22 11:29:36 +10:00
mcrcortex
7b15dbbea3 slight prep for sodium 0.7 2025-09-22 10:54:16 +10:00
mcrcortex
e0d125906b flag 2025-09-20 22:28:55 +10:00
mcrcortex
d5ab22c7f3 thing 2025-09-18 12:03:05 +10:00
mcrcortex
000fa24b2f so many bloody todos ;-; aaaaa 2025-09-18 12:02:56 +10:00
mcrcortex
b1582e5a1f added check against ModelQueries.cullsSame 2025-09-18 11:39:50 +10:00
mcrcortex
4ba2b1ca1f more returns 2025-09-18 10:52:59 +10:00
mcrcortex
7ec41006f7 extra return 2025-09-17 16:56:05 +10:00
mcrcortex
25ac827865 incremental sparse allocation, clear viewport on resize 2025-09-16 13:52:50 +10:00
mcrcortex
a6710c3e2e incremental sparcial buffer allocation, should prevent the huge lag spike on loading 2025-09-15 20:50:56 +10:00
mcrcortex
6539d67087 log 2025-09-15 18:26:55 +10:00
mcrcortex
5781f17858 improve parsing of blending data 2025-09-15 17:05:08 +10:00
mcrcortex
3ceba131f1 fk 2025-09-14 20:16:12 +10:00
mcrcortex
f0efd36674 try not to crash on shader load failure 2025-09-14 20:14:06 +10:00
mcrcortex
d78653a76f move to irisutil 2025-09-14 19:51:41 +10:00
mcrcortex
b86546a178 Auto disable shaders on load failure 2025-09-14 19:42:22 +10:00
mcrcortex
465a55a77e increase upsize to 1024 2025-09-14 18:51:05 +10:00
mcrcortex
1b023f859b more debug 2025-09-13 21:25:15 +10:00
mcrcortex
9edb680114 aaa 2025-09-12 14:27:21 +10:00
mcrcortex
a47876aca8 enable iris support 2025-09-12 10:12:51 +10:00
mcrcortex
ad182d4170 add taa injection into ChunkBoundRenderer, shift up uniform update 2025-09-12 10:04:56 +10:00
mcrcortex
2a4d6d085c a 2025-09-12 10:04:09 +10:00
mcrcortex
7605ebf48d pre-setup 2025-09-12 09:45:46 +10:00
mcrcortex
9f4282e37b Fixed rare race condition in ActiveSectionTracker, occured more frequently with high core systems. This is hopefully the last concurrency bug in the tracker 2025-09-11 01:32:24 +10:00
mcrcortex
81741974bb change mesh builder 2025-09-09 12:45:46 +10:00
mcrcortex
a2722b98de micro opt + gpu timer 2025-09-09 12:45:45 +10:00
60 changed files with 931 additions and 171 deletions

View File

@@ -9,9 +9,6 @@ jobs:
- name: Checkout sources - name: Checkout sources
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Verify wrapper
uses: gradle/actions/wrapper-validation@v3
- name: Setup Java - name: Setup Java
uses: actions/setup-java@v4 uses: actions/setup-java@v4
with: with:

View File

@@ -9,19 +9,24 @@ jobs:
- name: Checkout sources - name: Checkout sources
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Verify wrapper
uses: gradle/actions/wrapper-validation@v3
- name: Setup Java - name: Setup Java
uses: actions/setup-java@v4 uses: actions/setup-java@v4
with: with:
distribution: temurin distribution: temurin
java-version: 21 java-version: 21
- name: Loom Cache
uses: actions/cache@v4
with:
path: "**/.gradle/loom-cache"
key: "${{ runner.os }}-gradle-${{ hashFiles('**/libs.versions.*', '**/*.gradle*', '**/gradle-wrapper.properties') }}"
restore-keys: "${{ runner.os }}-gradle-"
- name: Setup Gradle - name: Setup Gradle
uses: gradle/actions/setup-gradle@v4 uses: gradle/actions/setup-gradle@v4
with: with:
cache-read-only: false cache-read-only: false
cache-cleanup: never
- name: Gradle build - name: Gradle build
run: ./gradlew build run: ./gradlew -I init.gradle build

View File

@@ -9,9 +9,6 @@ jobs:
- name: Checkout sources - name: Checkout sources
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Verify wrapper
uses: gradle/actions/wrapper-validation@v3
- name: Setup Java - name: Setup Java
uses: actions/setup-java@v4 uses: actions/setup-java@v4
with: with:
@@ -22,6 +19,7 @@ jobs:
uses: gradle/actions/setup-gradle@v4 uses: gradle/actions/setup-gradle@v4
with: with:
cache-read-only: false cache-read-only: false
cache-cleanup: always
- name: Gradle build - name: Gradle build
run: ./gradlew build run: ./gradlew build

View File

@@ -105,8 +105,8 @@ 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.6-0.6.13-fabric" modRuntimeOnlyMsk "maven.modrinth:sodium:mc1.21.8-0.7.0-fabric"
modCompileOnly "maven.modrinth:sodium:mc1.21.6-0.6.13-fabric" modCompileOnly "maven.modrinth:sodium:mc1.21.8-0.7.0-fabric"
modImplementation("maven.modrinth:lithium:mc1.21.8-0.18.0-fabric") modImplementation("maven.modrinth:lithium:mc1.21.8-0.18.0-fabric")
@@ -117,7 +117,7 @@ dependencies {
modRuntimeOnlyMsk("maven.modrinth:modmenu:15.0.0-beta.3") modRuntimeOnlyMsk("maven.modrinth:modmenu:15.0.0-beta.3")
modCompileOnly("maven.modrinth:iris:1.9.1+1.21.7-fabric") modCompileOnly("maven.modrinth:iris:1.9.1+1.21.7-fabric")
modRuntimeOnlyMsk("maven.modrinth:iris:1.9.1+1.21.7-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")

View File

@@ -3,6 +3,7 @@ org.gradle.jvmargs=-Xmx2G
org.gradle.caching=true org.gradle.caching=true
org.gradle.parallel=true org.gradle.parallel=true
org.gradle.daemon = false
# Fabric Properties # Fabric Properties
# check these on https://modmuss50.me/fabric.html # check these on https://modmuss50.me/fabric.html

16
init.gradle Normal file
View File

@@ -0,0 +1,16 @@
if (System.getenv("GITHUB_ACTIONS") == "true") {
beforeSettings { settings ->
def cleanupTime = Long.parseLong(System.getProperty("CLEANUP_TIME", Long.toString(System.currentTimeMillis()-(60_000*10))));//Remove unused entries from more then 10 min old
settings.caches {
//Note: this could be Cleanup.DEFAULT
cleanup = Cleanup.ALWAYS
releasedWrappers.setRemoveUnusedEntriesOlderThan(cleanupTime)
snapshotWrappers.setRemoveUnusedEntriesOlderThan(cleanupTime)
downloadedResources.setRemoveUnusedEntriesOlderThan(cleanupTime)
createdResources.setRemoveUnusedEntriesOlderThan(cleanupTime)
buildCache.setRemoveUnusedEntriesOlderThan(cleanupTime)
}
}
}

View File

@@ -57,4 +57,4 @@ public class VoxyClient implements ClientModInitializer {
public static boolean isFrexActive() { public static boolean isFrexActive() {
return !FREX.isEmpty(); return !FREX.isEmpty();
} }
} }

View File

@@ -14,6 +14,8 @@ import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager;
import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
import net.minecraft.command.CommandSource; import net.minecraft.command.CommandSource;
import net.minecraft.text.PlainTextContent;
import net.minecraft.text.Text;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
@@ -52,7 +54,7 @@ public class VoxyCommands {
.executes(VoxyCommands::importDistantHorizons))); .executes(VoxyCommands::importDistantHorizons)));
} }
return ClientCommandManager.literal("voxy").requires((ctx)-> VoxyCommon.getInstance() != null) return ClientCommandManager.literal("voxy")//.requires((ctx)-> VoxyCommon.getInstance() != null)
.then(ClientCommandManager.literal("reload") .then(ClientCommandManager.literal("reload")
.executes(VoxyCommands::reloadInstance)) .executes(VoxyCommands::reloadInstance))
.then(imports); .then(imports);
@@ -61,6 +63,7 @@ public class VoxyCommands {
private static int reloadInstance(CommandContext<FabricClientCommandSource> ctx) { private static int reloadInstance(CommandContext<FabricClientCommandSource> ctx) {
var instance = (VoxyClientInstance)VoxyCommon.getInstance(); var instance = (VoxyClientInstance)VoxyCommon.getInstance();
if (instance == null) { if (instance == null) {
ctx.getSource().sendError(Text.translatable("Voxy must be enabled in settings to use this"));
return 1; return 1;
} }
var wr = MinecraftClient.getInstance().worldRenderer; var wr = MinecraftClient.getInstance().worldRenderer;
@@ -82,6 +85,7 @@ public class VoxyCommands {
private static int importDistantHorizons(CommandContext<FabricClientCommandSource> ctx) { private static int importDistantHorizons(CommandContext<FabricClientCommandSource> ctx) {
var instance = (VoxyClientInstance)VoxyCommon.getInstance(); var instance = (VoxyClientInstance)VoxyCommon.getInstance();
if (instance == null) { if (instance == null) {
ctx.getSource().sendError(Text.translatable("Voxy must be enabled in settings to use this"));
return 1; return 1;
} }
var dbFile = new File(ctx.getArgument("sqlDbPath", String.class)); var dbFile = new File(ctx.getArgument("sqlDbPath", String.class));
@@ -96,10 +100,10 @@ public class VoxyCommands {
} }
File dbFile_ = dbFile; File dbFile_ = dbFile;
var engine = WorldIdentifier.ofEngine(MinecraftClient.getInstance().player.clientWorld); var engine = WorldIdentifier.ofEngine(MinecraftClient.getInstance().world);
if (engine==null)return 1; if (engine==null)return 1;
return instance.getImportManager().makeAndRunIfNone(engine, ()-> return instance.getImportManager().makeAndRunIfNone(engine, ()->
new DHImporter(dbFile_, engine, MinecraftClient.getInstance().player.clientWorld, instance.getThreadPool(), instance.savingServiceRateLimiter))?0:1; new DHImporter(dbFile_, engine, MinecraftClient.getInstance().world, instance.getThreadPool(), instance.savingServiceRateLimiter))?0:1;
} }
private static boolean fileBasedImporter(File directory) { private static boolean fileBasedImporter(File directory) {
@@ -108,20 +112,30 @@ public class VoxyCommands {
return false; return false;
} }
var engine = WorldIdentifier.ofEngine(MinecraftClient.getInstance().player.clientWorld); var engine = WorldIdentifier.ofEngine(MinecraftClient.getInstance().world);
if (engine==null) return false; if (engine==null) return false;
return instance.getImportManager().makeAndRunIfNone(engine, ()->{ return instance.getImportManager().makeAndRunIfNone(engine, ()->{
var importer = new WorldImporter(engine, MinecraftClient.getInstance().player.clientWorld, instance.getThreadPool(), instance.savingServiceRateLimiter); var importer = new WorldImporter(engine, MinecraftClient.getInstance().world, instance.getThreadPool(), instance.savingServiceRateLimiter);
importer.importRegionDirectoryAsync(directory); importer.importRegionDirectoryAsync(directory);
return importer; return importer;
}); });
} }
private static int importRaw(CommandContext<FabricClientCommandSource> ctx) { private static int importRaw(CommandContext<FabricClientCommandSource> ctx) {
if (VoxyCommon.getInstance() == null) {
ctx.getSource().sendError(Text.translatable("Voxy must be enabled in settings to use this"));
return 1;
}
return fileBasedImporter(new File(ctx.getArgument("path", String.class)))?0:1; return fileBasedImporter(new File(ctx.getArgument("path", String.class)))?0:1;
} }
private static int importBobby(CommandContext<FabricClientCommandSource> ctx) { private static int importBobby(CommandContext<FabricClientCommandSource> ctx) {
if (VoxyCommon.getInstance() == null) {
ctx.getSource().sendError(Text.translatable("Voxy must be enabled in settings to use this"));
return 1;
}
var file = new File(".bobby").toPath().resolve(ctx.getArgument("world_name", String.class)).toFile(); var file = new File(".bobby").toPath().resolve(ctx.getArgument("world_name", String.class)).toFile();
return fileBasedImporter(file)?0:1; return fileBasedImporter(file)?0:1;
} }
@@ -176,6 +190,11 @@ public class VoxyCommands {
} }
private static int importWorld(CommandContext<FabricClientCommandSource> ctx) { private static int importWorld(CommandContext<FabricClientCommandSource> ctx) {
if (VoxyCommon.getInstance() == null) {
ctx.getSource().sendError(Text.translatable("Voxy must be enabled in settings to use this"));
return 1;
}
var name = ctx.getArgument("world_name", String.class); var name = ctx.getArgument("world_name", String.class);
var file = new File("saves").toPath().resolve(name); var file = new File("saves").toPath().resolve(name);
name = name.toLowerCase(); name = name.toLowerCase();
@@ -197,14 +216,15 @@ public class VoxyCommands {
var instance = (VoxyClientInstance)VoxyCommon.getInstance(); var instance = (VoxyClientInstance)VoxyCommon.getInstance();
if (instance == null) { if (instance == null) {
ctx.getSource().sendError(Text.translatable("Voxy must be enabled in settings to use this"));
return 1; return 1;
} }
String finalInnerDir = innerDir; String finalInnerDir = innerDir;
var engine = WorldIdentifier.ofEngine(MinecraftClient.getInstance().player.clientWorld); var engine = WorldIdentifier.ofEngine(MinecraftClient.getInstance().world);
if (engine != null) { if (engine != null) {
return instance.getImportManager().makeAndRunIfNone(engine, () -> { return instance.getImportManager().makeAndRunIfNone(engine, () -> {
var importer = new WorldImporter(engine, MinecraftClient.getInstance().player.clientWorld, instance.getThreadPool(), instance.savingServiceRateLimiter); var importer = new WorldImporter(engine, MinecraftClient.getInstance().world, instance.getThreadPool(), instance.savingServiceRateLimiter);
importer.importZippedRegionDirectoryAsync(zip, finalInnerDir); importer.importZippedRegionDirectoryAsync(zip, finalInnerDir);
return importer; return importer;
}) ? 0 : 1; }) ? 0 : 1;
@@ -212,12 +232,13 @@ public class VoxyCommands {
return 1; return 1;
} }
private static int cancelImport(CommandContext<FabricClientCommandSource> fabricClientCommandSourceCommandContext) { private static int cancelImport(CommandContext<FabricClientCommandSource> ctx) {
var instance = (VoxyClientInstance)VoxyCommon.getInstance(); var instance = (VoxyClientInstance)VoxyCommon.getInstance();
if (instance == null) { if (instance == null) {
ctx.getSource().sendError(Text.translatable("Voxy must be enabled in settings to use this"));
return 1; return 1;
} }
var world = WorldIdentifier.ofEngineNullable(MinecraftClient.getInstance().player.clientWorld); var world = WorldIdentifier.ofEngineNullable(MinecraftClient.getInstance().world);
if (world != null) { if (world != null) {
return instance.getImportManager().cancelImport(world)?0:1; return instance.getImportManager().cancelImport(world)?0:1;
} }

View File

@@ -69,6 +69,11 @@ public abstract class AbstractRenderPipeline extends TrackedObject {
this.sectionRenderer = sectionRenderer; this.sectionRenderer = sectionRenderer;
} }
//Called before the pipeline starts running, used to update uniforms etc
public void preSetup(Viewport<?> viewport) {
}
protected abstract int setup(Viewport<?> viewport, int sourceFramebuffer, int srcWidth, int srcHeight); protected abstract int setup(Viewport<?> viewport, int sourceFramebuffer, int srcWidth, int srcHeight);
protected abstract void postOpaquePreTranslucent(Viewport<?> viewport); protected abstract void postOpaquePreTranslucent(Viewport<?> viewport);
protected void finish(Viewport<?> viewport, int sourceFrameBuffer, int srcWidth, int srcHeight) { protected void finish(Viewport<?> viewport, int sourceFrameBuffer, int srcWidth, int srcHeight) {
@@ -200,8 +205,19 @@ public abstract class AbstractRenderPipeline extends TrackedObject {
public abstract void setupAndBindTranslucent(Viewport<?> viewport); public abstract void setupAndBindTranslucent(Viewport<?> viewport);
public void bindUniforms() {
this.bindUniforms(-1);
}
public void bindUniforms(int index) {
}
//null means no function, otherwise return the taa injection function //null means no function, otherwise return the taa injection function
public String taaFunction(AbstractSectionRenderer<?,?> renderer, String functionName) { public String taaFunction(String functionName) {
return this.taaFunction(-1, functionName);
}
public String taaFunction(int uboBindingPoint, String functionName) {
return null; return null;
} }

View File

@@ -90,14 +90,18 @@ public class IrisVoxyRenderPipeline extends AbstractRenderPipeline {
} }
@Override @Override
protected int setup(Viewport<?> viewport, int sourceFramebuffer, int srcWidth, int srcHeight) { public void preSetup(Viewport<?> viewport) {
super.preSetup(viewport);
if (this.shaderUniforms != null) { if (this.shaderUniforms != null) {
//Update the uniforms //Update the uniforms
long ptr = UploadStream.INSTANCE.uploadTo(this.shaderUniforms); long ptr = UploadStream.INSTANCE.uploadTo(this.shaderUniforms);
this.data.getUniforms().updater().accept(ptr); this.data.getUniforms().updater().accept(ptr);
UploadStream.INSTANCE.commit(); UploadStream.INSTANCE.commit();
} }
}
@Override
protected int setup(Viewport<?> viewport, int sourceFramebuffer, int srcWidth, int srcHeight) {
this.fb.resize(viewport.width, viewport.height); this.fb.resize(viewport.width, viewport.height);
this.fbTranslucent.resize(viewport.width, viewport.height); this.fbTranslucent.resize(viewport.width, viewport.height);
@@ -144,10 +148,20 @@ public class IrisVoxyRenderPipeline extends AbstractRenderPipeline {
} }
private void doBindings() { @Override
public void bindUniforms() {
this.bindUniforms(UNIFORM_BINDING_POINT);
}
@Override
public void bindUniforms(int bindingPoint) {
if (this.shaderUniforms != null) { if (this.shaderUniforms != null) {
GL30.glBindBufferBase(GL_UNIFORM_BUFFER, 5, this.shaderUniforms.id);// todo: dont randomly select this to 5 GL30.glBindBufferBase(GL_UNIFORM_BUFFER, bindingPoint, this.shaderUniforms.id);// todo: dont randomly select this to 5
} }
}
private void doBindings() {
this.bindUniforms();
if (this.data.getSsboSet() != null) { if (this.data.getSsboSet() != null) {
this.data.getSsboSet().bindingFunction().accept(10); this.data.getSsboSet().bindingFunction().accept(10);
} }
@@ -221,11 +235,16 @@ public class IrisVoxyRenderPipeline extends AbstractRenderPipeline {
} }
@Override @Override
public String taaFunction(AbstractSectionRenderer<?, ?> renderer, String functionName) { public String taaFunction(String functionName) {
return this.taaFunction(UNIFORM_BINDING_POINT, functionName);
}
@Override
public String taaFunction(int uboBindingPoint, String functionName) {
var builder = new StringBuilder(); var builder = new StringBuilder();
if (this.data.getUniforms() != null) { if (this.data.getUniforms() != null) {
builder.append("layout(binding = "+UNIFORM_BINDING_POINT+", std140) uniform ShaderUniformBindings ") builder.append("layout(binding = "+uboBindingPoint+", std140) uniform ShaderUniformBindings ")
.append(this.data.getUniforms().layout()) .append(this.data.getUniforms().layout())
.append(";\n\n"); .append(";\n\n");
} }

View File

@@ -7,6 +7,7 @@ import me.cortex.voxy.client.core.util.IrisUtil;
import me.cortex.voxy.client.iris.IGetIrisVoxyPipelineData; import me.cortex.voxy.client.iris.IGetIrisVoxyPipelineData;
import me.cortex.voxy.common.Logger; import me.cortex.voxy.common.Logger;
import net.irisshaders.iris.Iris; import net.irisshaders.iris.Iris;
import net.irisshaders.iris.api.v0.IrisApi;
import java.util.function.BooleanSupplier; import java.util.function.BooleanSupplier;
@@ -34,7 +35,13 @@ public class RenderPipelineFactory {
return null; return null;
} }
Logger.info("Creating voxy iris render pipeline"); Logger.info("Creating voxy iris render pipeline");
return new IrisVoxyRenderPipeline(pipeData, nodeManager, nodeCleaner, traversal, frexSupplier); try {
return new IrisVoxyRenderPipeline(pipeData, nodeManager, nodeCleaner, traversal, frexSupplier);
} catch (Exception e) {
Logger.error("Failed to create iris render pipeline", e);
IrisUtil.disableIrisShaders();
return null;
}
} }
return null; return null;
} }

View File

@@ -25,6 +25,7 @@ import me.cortex.voxy.client.core.rendering.section.geometry.IGeometryData;
import me.cortex.voxy.client.core.rendering.util.DownloadStream; import me.cortex.voxy.client.core.rendering.util.DownloadStream;
import me.cortex.voxy.client.core.rendering.util.PrintfDebugUtil; import me.cortex.voxy.client.core.rendering.util.PrintfDebugUtil;
import me.cortex.voxy.client.core.rendering.util.UploadStream; import me.cortex.voxy.client.core.rendering.util.UploadStream;
import me.cortex.voxy.client.core.util.GPUTiming;
import me.cortex.voxy.client.core.util.IrisUtil; import me.cortex.voxy.client.core.util.IrisUtil;
import me.cortex.voxy.common.Logger; import me.cortex.voxy.common.Logger;
import me.cortex.voxy.common.thread.ServiceThreadPool; import me.cortex.voxy.common.thread.ServiceThreadPool;
@@ -136,7 +137,7 @@ public class VoxyRenderSystem {
this.renderDistanceTracker.setRenderDistance(VoxyConfig.CONFIG.sectionRenderDistance); this.renderDistanceTracker.setRenderDistance(VoxyConfig.CONFIG.sectionRenderDistance);
} }
this.chunkBoundRenderer = new ChunkBoundRenderer(); this.chunkBoundRenderer = new ChunkBoundRenderer(this.pipeline);
Logger.info("Voxy render system created with " + geometryCapacity + " geometry capacity, using pipeline '" + this.pipeline.getClass().getSimpleName() + "' with renderer '" + sectionRenderer.getClass().getSimpleName() + "'"); Logger.info("Voxy render system created with " + geometryCapacity + " geometry capacity, using pipeline '" + this.pipeline.getClass().getSimpleName() + "' with renderer '" + sectionRenderer.getClass().getSimpleName() + "'");
} catch (RuntimeException e) { } catch (RuntimeException e) {
@@ -204,6 +205,7 @@ public class VoxyRenderSystem {
long startTime = System.nanoTime(); long startTime = System.nanoTime();
TimingStatistics.all.start(); TimingStatistics.all.start();
GPUTiming.INSTANCE.marker();//Start marker
TimingStatistics.main.start(); TimingStatistics.main.start();
//TODO: optimize //TODO: optimize
@@ -229,6 +231,7 @@ public class VoxyRenderSystem {
//this.autoBalanceSubDivSize(); //this.autoBalanceSubDivSize();
this.pipeline.preSetup(viewport);
TimingStatistics.E.start(); TimingStatistics.E.start();
if (!IrisUtil.irisShadowActive()) { if (!IrisUtil.irisShadowActive()) {
@@ -258,8 +261,11 @@ public class VoxyRenderSystem {
//Done here as is allows less gl state resetup //Done here as is allows less gl state resetup
this.modelService.tick(Math.max(3_000_000-(System.nanoTime()-startTime), 500_000)); this.modelService.tick(Math.max(3_000_000-(System.nanoTime()-startTime), 500_000));
} }
GPUTiming.INSTANCE.marker();
TimingStatistics.postDynamic.stop(); TimingStatistics.postDynamic.stop();
GPUTiming.INSTANCE.tick();
glBindFramebuffer(GlConst.GL_FRAMEBUFFER, oldFB); glBindFramebuffer(GlConst.GL_FRAMEBUFFER, oldFB);
glViewport(dims[0], dims[1], dims[2], dims[3]); glViewport(dims[0], dims[1], dims[2], dims[3]);

View File

@@ -12,6 +12,7 @@ import static org.lwjgl.opengl.GL45C.*;
public class GlBuffer extends TrackedObject { public class GlBuffer extends TrackedObject {
public final int id; public final int id;
private final long size; private final long size;
private final int flags;
private static int COUNT; private static int COUNT;
private static long TOTAL_SIZE; private static long TOTAL_SIZE;
@@ -28,6 +29,7 @@ public class GlBuffer extends TrackedObject {
} }
public GlBuffer(long size, int flags, boolean zero) { public GlBuffer(long size, int flags, boolean zero) {
this.flags = flags;
this.id = glCreateBuffers(); this.id = glCreateBuffers();
this.size = size; this.size = size;
glNamedBufferStorage(this.id, size, flags); glNamedBufferStorage(this.id, size, flags);
@@ -48,6 +50,10 @@ public class GlBuffer extends TrackedObject {
TOTAL_SIZE -= this.size; TOTAL_SIZE -= this.size;
} }
public boolean isSparse() {
return (this.flags&GL_SPARSE_STORAGE_BIT_ARB)!=0;
}
public long size() { public long size() {
return this.size; return this.size;
} }

View File

@@ -3,6 +3,7 @@ package me.cortex.voxy.client.core.gl;
import me.cortex.voxy.common.util.TrackedObject; import me.cortex.voxy.common.util.TrackedObject;
import static org.lwjgl.opengl.GL45C.*; import static org.lwjgl.opengl.GL45C.*;
import static org.lwjgl.opengl.GL45C.glNamedFramebufferDrawBuffers;
public class GlFramebuffer extends TrackedObject { public class GlFramebuffer extends TrackedObject {
public final int id; public final int id;
@@ -24,6 +25,11 @@ public class GlFramebuffer extends TrackedObject {
return this; return this;
} }
public GlFramebuffer setDrawBuffers(int... buffers) {
glNamedFramebufferDrawBuffers(this.id, buffers);
return this;
}
@Override @Override
public void free() { public void free() {
super.free0(); super.free0();

View File

@@ -99,7 +99,7 @@ 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, GL_R32F -> 4; case GL_R32UI, 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_COMPONENT32F -> 4;
case GL_DEPTH_COMPONENT32 -> 4; case GL_DEPTH_COMPONENT32 -> 4;

View File

@@ -6,7 +6,7 @@ import net.caffeinemc.mods.sodium.client.gl.shader.ShaderParser;
public class ShaderLoader { public class ShaderLoader {
public static String parse(String id) { public static String parse(String id) {
return ShaderParser.parseShader("#import <" + id + ">", ShaderConstants.builder().build()); return "#version 460 core\n"+ShaderParser.parseShader("\n#import <" + id + ">\n//beans", ShaderConstants.builder().build()).src().replaceAll("\r\n", "\n").replaceFirst("\n#version .+\n", "\n");
//return me.jellysquid.mods.sodium.client.gl.shader.ShaderLoader.getShaderSource(new Identifier(id)); //return me.jellysquid.mods.sodium.client.gl.shader.ShaderLoader.getShaderSource(new Identifier(id));
} }
} }

View File

@@ -345,6 +345,8 @@ public class ModelFactory {
boolean fullyOpaque = true; boolean fullyOpaque = true;
//TODO: FIXME faces that have the same "alignment depth" e.g. (sizes[0]+sizes[1])~=1 can be merged into a double faced single quad
//TODO: add a bunch of control config options for overriding/setting options of metadata for each face of each type //TODO: add a bunch of control config options for overriding/setting options of metadata for each face of each type
for (int face = 5; face != -1; face--) {//In reverse order to make indexing into the metadata long easier for (int face = 5; face != -1; face--) {//In reverse order to make indexing into the metadata long easier
long faceUploadPtr = uploadPtr + 4L * face;//Each face gets 4 bytes worth of data long faceUploadPtr = uploadPtr + 4L * face;//Each face gets 4 bytes worth of data
@@ -394,14 +396,19 @@ public class ModelFactory {
//Scale face size from 0->this.modelTextureSize-1 to 0->15 if (MODEL_TEXTURE_SIZE-1 != 15) {
for (int i = 0; i < 4; i++) { //Scale face size from 0->this.modelTextureSize-1 to 0->15
faceSize[i] = Math.round((((float)faceSize[i])/(MODEL_TEXTURE_SIZE-1))*15); for (int i = 0; i < 4; i++) {
faceSize[i] = Math.round((((float) faceSize[i]) / (MODEL_TEXTURE_SIZE - 1)) * 15);
}
} }
int faceModelData = 0; int faceModelData = 0;
faceModelData |= faceSize[0] | (faceSize[1]<<4) | (faceSize[2]<<8) | (faceSize[3]<<12); faceModelData |= faceSize[0] | (faceSize[1]<<4) | (faceSize[2]<<8) | (faceSize[3]<<12);
faceModelData |= Math.round(offset*63)<<16;//Change the scale from 0->1 (ends inclusive) float to 0->63 (6 bits) NOTE! that 63 == 1.0f meaning its shifted all the way to the other side of the model //Change the scale from 0->1 (ends inclusive)
// this is cursed also warning stuff at 63 (i.e half a pixel from the end will be clamped to the end)
int enc = Math.round(offset*64);
faceModelData |= Math.min(enc,63)<<16;
//Still have 11 bits free //Still have 11 bits free
//Stuff like fences are solid, however they have extra side piece that mean it needs to have discard on //Stuff like fences are solid, however they have extra side piece that mean it needs to have discard on
@@ -414,7 +421,15 @@ public class ModelFactory {
faceModelData |= ((!faceCoversFullBlock)&&blockRenderLayer != BlockRenderLayer.TRANSLUCENT)?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
//Bits 24,25 are tint metadata
if (colourProvider!=null) {//We have a tint
int tintState = TextureUtils.computeFaceTint(textureData[face], checkMode);
if (tintState == 2) {//Partial tint
faceModelData |= 1<<24;
} else if (tintState == 3) {//Full tint
faceModelData |= 2<<24;
}
}
MemoryUtil.memPutInt(faceUploadPtr, faceModelData); MemoryUtil.memPutInt(faceUploadPtr, faceModelData);
} }

View File

@@ -38,6 +38,37 @@ public class TextureUtils {
throw new IllegalArgumentException(); throw new IllegalArgumentException();
} }
//0: nothing written
//1: none tinted
//2: some tinted
//3: all tinted
public static int computeFaceTint(ColourDepthTextureData texture, int checkMode) {
boolean allTinted = true;
boolean someTinted = false;
boolean wasWriten = false;
final var colourData = texture.colour();
final var depthData = texture.depth();
for (int i = 0; i < colourData.length; i++) {
if (!wasPixelWritten(texture, checkMode, i)) {
continue;
}
if ((colourData[i]&0xFFFFFF) == 0 || (colourData[i]>>>24)==0) {//If the pixel is fully black (or translucent)
continue;
}
boolean pixelTinited = (depthData[i]&(1<<7))!=0;
wasWriten |= true;
allTinted &= pixelTinited;
someTinted |= pixelTinited;
}
if (!wasWriten) {
return 0;
}
return someTinted?(allTinted?3:2):1;
}
public static final int DEPTH_MODE_AVG = 1; public static final int DEPTH_MODE_AVG = 1;
public static final int DEPTH_MODE_MAX = 2; public static final int DEPTH_MODE_MAX = 2;
public static final int DEPTH_MODE_MIN = 3; public static final int DEPTH_MODE_MIN = 3;

View File

@@ -7,8 +7,7 @@ import me.cortex.voxy.client.core.gl.shader.ShaderType;
import org.lwjgl.system.MemoryStack; import org.lwjgl.system.MemoryStack;
import org.lwjgl.system.MemoryUtil; import org.lwjgl.system.MemoryUtil;
import static org.lwjgl.opengl.ARBDirectStateAccess.glTextureParameteri; import static org.lwjgl.opengl.ARBDirectStateAccess.*;
import static org.lwjgl.opengl.ARBDirectStateAccess.nglClearNamedFramebufferfv;
import static org.lwjgl.opengl.ARBShaderImageLoadStore.GL_FRAMEBUFFER_BARRIER_BIT; import static org.lwjgl.opengl.ARBShaderImageLoadStore.GL_FRAMEBUFFER_BARRIER_BIT;
import static org.lwjgl.opengl.ARBShaderImageLoadStore.GL_PIXEL_BUFFER_BARRIER_BIT; import static org.lwjgl.opengl.ARBShaderImageLoadStore.GL_PIXEL_BUFFER_BARRIER_BIT;
import static org.lwjgl.opengl.ARBShaderImageLoadStore.GL_SHADER_IMAGE_ACCESS_BARRIER_BIT; import static org.lwjgl.opengl.ARBShaderImageLoadStore.GL_SHADER_IMAGE_ACCESS_BARRIER_BIT;
@@ -25,12 +24,14 @@ public class GlViewCapture {
private final GlTexture colourTex; private final GlTexture colourTex;
private final GlTexture depthTex; private final GlTexture depthTex;
private final GlTexture stencilTex; private final GlTexture stencilTex;
private final GlTexture metaTex;
final GlFramebuffer framebuffer; final GlFramebuffer framebuffer;
private final Shader copyOutShader; private final Shader copyOutShader;
public GlViewCapture(int width, int height) { public GlViewCapture(int width, int height) {
this.width = width; this.width = width;
this.height = height; this.height = height;
this.metaTex = new GlTexture().store(GL_R32UI, 1, width*3, height*2).name("ModelBakeryMetadata");
this.colourTex = new GlTexture().store(GL_RGBA8, 1, width*3, height*2).name("ModelBakeryColour"); this.colourTex = new GlTexture().store(GL_RGBA8, 1, width*3, height*2).name("ModelBakeryColour");
this.depthTex = new GlTexture().store(GL_DEPTH24_STENCIL8, 1, width*3, height*2).name("ModelBakeryDepth"); this.depthTex = new GlTexture().store(GL_DEPTH24_STENCIL8, 1, width*3, height*2).name("ModelBakeryDepth");
//TODO: FIXME: Mesa is broken when trying to read from a sampler of GL_STENCIL_INDEX //TODO: FIXME: Mesa is broken when trying to read from a sampler of GL_STENCIL_INDEX
@@ -39,22 +40,27 @@ public class GlViewCapture {
this.stencilTex = this.depthTex.createView(); this.stencilTex = this.depthTex.createView();
glTextureParameteri(this.depthTex.id, GL_DEPTH_STENCIL_TEXTURE_MODE, GL_DEPTH_COMPONENT); glTextureParameteri(this.depthTex.id, GL_DEPTH_STENCIL_TEXTURE_MODE, GL_DEPTH_COMPONENT);
this.framebuffer = new GlFramebuffer().bind(GL_COLOR_ATTACHMENT0, this.colourTex).bind(GL_DEPTH_STENCIL_ATTACHMENT, this.depthTex).verify().name("ModelFramebuffer"); this.framebuffer = new GlFramebuffer().bind(GL_COLOR_ATTACHMENT0, this.colourTex).bind(GL_COLOR_ATTACHMENT1, this.metaTex).setDrawBuffers(GL_COLOR_ATTACHMENT0,GL_COLOR_ATTACHMENT1).bind(GL_DEPTH_STENCIL_ATTACHMENT, this.depthTex).verify().name("ModelFramebuffer");
glTextureParameteri(this.stencilTex.id, GL_DEPTH_STENCIL_TEXTURE_MODE, GL_STENCIL_INDEX); glTextureParameteri(this.stencilTex.id, GL_DEPTH_STENCIL_TEXTURE_MODE, GL_STENCIL_INDEX);
glTextureParameteri(this.stencilTex.id, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTextureParameteri(this.stencilTex.id, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTextureParameteri(this.stencilTex.id, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTextureParameteri(this.stencilTex.id, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTextureParameteri(this.metaTex.id, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTextureParameteri(this.metaTex.id, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
this.copyOutShader = Shader.makeAuto() this.copyOutShader = Shader.makeAuto()
.define("WIDTH", width) .define("WIDTH", width)
.define("HEIGHT", height) .define("HEIGHT", height)
.define("COLOUR_IN_BINDING", 0) .define("COLOUR_IN_BINDING", 0)
.define("DEPTH_IN_BINDING", 1) .define("DEPTH_IN_BINDING", 1)
.define("STENCIL_IN_BINDING", 2) .define("STENCIL_IN_BINDING", 2)
.define("BUFFER_OUT_BINDING", 3) .define("META_IN_BINDING", 3)
.define("BUFFER_OUT_BINDING", 4)
.add(ShaderType.COMPUTE, "voxy:bakery/bufferreorder.comp") .add(ShaderType.COMPUTE, "voxy:bakery/bufferreorder.comp")
.compile() .compile()
.name("ModelBakeryOut") .name("ModelBakeryOut")
.texture("META_IN_BINDING", 0, this.metaTex)
.texture("COLOUR_IN_BINDING", 0, this.colourTex) .texture("COLOUR_IN_BINDING", 0, this.colourTex)
.texture("DEPTH_IN_BINDING", 0, this.depthTex) .texture("DEPTH_IN_BINDING", 0, this.depthTex)
.texture("STENCIL_IN_BINDING", 0, this.stencilTex); .texture("STENCIL_IN_BINDING", 0, this.stencilTex);
@@ -62,10 +68,10 @@ public class GlViewCapture {
public void emitToStream(int buffer, int offset) { public void emitToStream(int buffer, int offset) {
this.copyOutShader.bind(); this.copyOutShader.bind();
glBindBufferRange(GL_SHADER_STORAGE_BUFFER, 3, buffer, offset, (this.width*3L)*(this.height*2L)*4L*2);//its 2*4 because colour + depth stencil glBindBufferRange(GL_SHADER_STORAGE_BUFFER, 4, buffer, offset, (this.width*3L)*(this.height*2L)*4L*2);//its 2*4 because colour + depth stencil
glMemoryBarrier(GL_FRAMEBUFFER_BARRIER_BIT|GL_TEXTURE_UPDATE_BARRIER_BIT|GL_PIXEL_BUFFER_BARRIER_BIT|GL_SHADER_IMAGE_ACCESS_BARRIER_BIT);//Am not sure if barriers are right glMemoryBarrier(GL_FRAMEBUFFER_BARRIER_BIT|GL_TEXTURE_UPDATE_BARRIER_BIT|GL_PIXEL_BUFFER_BARRIER_BIT|GL_SHADER_IMAGE_ACCESS_BARRIER_BIT);//Am not sure if barriers are right
glDispatchCompute(3, 2, 1); glDispatchCompute(3, 2, 1);
glBindBufferRange(GL_SHADER_STORAGE_BUFFER, 3, 0, 0, 4);//WHY DOES THIS FIX FUCKING BINDING ISSUES HERE WHEN DOING THIS IN THE RENDER SYSTEM DOESNT glBindBufferRange(GL_SHADER_STORAGE_BUFFER, 4, 0, 0, 4);//WHY DOES THIS FIX FUCKING BINDING ISSUES HERE WHEN DOING THIS IN THE RENDER SYSTEM DOESNT
} }
public void clear() { public void clear() {
@@ -74,6 +80,9 @@ public class GlViewCapture {
MemoryUtil.memPutLong(ptr, 0); MemoryUtil.memPutLong(ptr, 0);
MemoryUtil.memPutLong(ptr+8, 0); MemoryUtil.memPutLong(ptr+8, 0);
nglClearNamedFramebufferfv(this.framebuffer.id, GL_COLOR, 0, ptr); nglClearNamedFramebufferfv(this.framebuffer.id, GL_COLOR, 0, ptr);
nglClearNamedFramebufferuiv(this.framebuffer.id, GL_COLOR, 1, ptr);
//TODO: fix the draw buffer thing maybe? it might need todo multiple clears
//nglClearNamedFramebufferfv(this.framebuffer.id, GL_COLOR, 0, ptr);
} }
glClearNamedFramebufferfi(this.framebuffer.id, GL_DEPTH_STENCIL, 0, 1.0f, 0); glClearNamedFramebufferfi(this.framebuffer.id, GL_DEPTH_STENCIL, 0, 1.0f, 0);
} }
@@ -83,6 +92,7 @@ public class GlViewCapture {
this.colourTex.free(); this.colourTex.free();
this.stencilTex.free(); this.stencilTex.free();
this.depthTex.free(); this.depthTex.free();
this.metaTex.free();
this.copyOutShader.free(); this.copyOutShader.free();
} }
} }

View File

@@ -67,13 +67,11 @@ public class ModelTextureBakery {
int meta = getMetaFromLayer(layer); int meta = getMetaFromLayer(layer);
for (Direction direction : new Direction[]{Direction.DOWN, Direction.UP, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST, null}) { for (var part : model.getParts(new LocalRandom(42L))) {
for (var part : model.getParts(new LocalRandom(42L))) { for (Direction direction : new Direction[]{Direction.DOWN, Direction.UP, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST, null}) {
var quads = part.getQuads(direction); var quads = part.getQuads(direction);
for (var quad : quads) { for (var quad : quads) {
//TODO: add meta specifiying quad has a tint this.vc.quad(quad, meta|(quad.hasTint()?4:0));
//quad.hasTint()
this.vc.quad(quad, meta);
} }
} }
} }
@@ -81,7 +79,14 @@ public class ModelTextureBakery {
private void bakeFluidState(BlockState state, BlockRenderLayer layer, int face) { private void bakeFluidState(BlockState state, BlockRenderLayer layer, int face) {
this.vc.setDefaultMeta(getMetaFromLayer(layer));//Set the meta while baking {
//TODO: somehow set the tint flag per quad or something?
int metadata = getMetaFromLayer(layer);
//Just assume all fluids are tinted, if they arnt it should be implicitly culled in the model baking phase
// since it wont have the colour provider
metadata |= 4;//Has tint
this.vc.setDefaultMeta(metadata);//Set the meta while baking
}
MinecraftClient.getInstance().getBlockRenderManager().renderFluid(BlockPos.ORIGIN, new BlockRenderView() { MinecraftClient.getInstance().getBlockRenderManager().renderFluid(BlockPos.ORIGIN, new BlockRenderView() {
@Override @Override
public float getBrightness(Direction direction, boolean shaded) { public float getBrightness(Direction direction, boolean shaded) {

View File

@@ -2,10 +2,12 @@ package me.cortex.voxy.client.core.rendering;
import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap; import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap;
import it.unimi.dsi.fastutil.longs.LongOpenHashSet; import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
import me.cortex.voxy.client.core.AbstractRenderPipeline;
import me.cortex.voxy.client.core.gl.GlBuffer; import me.cortex.voxy.client.core.gl.GlBuffer;
import me.cortex.voxy.client.core.gl.GlVertexArray; import me.cortex.voxy.client.core.gl.GlVertexArray;
import me.cortex.voxy.client.core.gl.shader.AutoBindingShader; import me.cortex.voxy.client.core.gl.shader.AutoBindingShader;
import me.cortex.voxy.client.core.gl.shader.Shader; import me.cortex.voxy.client.core.gl.shader.Shader;
import me.cortex.voxy.client.core.gl.shader.ShaderLoader;
import me.cortex.voxy.client.core.gl.shader.ShaderType; import me.cortex.voxy.client.core.gl.shader.ShaderType;
import me.cortex.voxy.client.core.rendering.util.SharedIndexBuffer; import me.cortex.voxy.client.core.rendering.util.SharedIndexBuffer;
import me.cortex.voxy.client.core.rendering.util.UploadStream; import me.cortex.voxy.client.core.rendering.util.UploadStream;
@@ -35,18 +37,28 @@ public class ChunkBoundRenderer {
private final GlBuffer uniformBuffer = new GlBuffer(128); private final GlBuffer uniformBuffer = new GlBuffer(128);
private final Long2IntOpenHashMap chunk2idx = new Long2IntOpenHashMap(INIT_MAX_CHUNK_COUNT); private final Long2IntOpenHashMap chunk2idx = new Long2IntOpenHashMap(INIT_MAX_CHUNK_COUNT);
private long[] idx2chunk = new long[INIT_MAX_CHUNK_COUNT]; private long[] idx2chunk = new long[INIT_MAX_CHUNK_COUNT];
private final Shader rasterShader = Shader.makeAuto() private final Shader rasterShader;
.add(ShaderType.VERTEX, "voxy:chunkoutline/outline.vsh")
.add(ShaderType.FRAGMENT, "voxy:chunkoutline/outline.fsh")
.compile()
.ubo(0, this.uniformBuffer)
.ssbo(1, this.chunkPosBuffer);
private final LongOpenHashSet addQueue = new LongOpenHashSet(); private final LongOpenHashSet addQueue = new LongOpenHashSet();
private final LongOpenHashSet remQueue = new LongOpenHashSet(); private final LongOpenHashSet remQueue = new LongOpenHashSet();
public ChunkBoundRenderer() { private final AbstractRenderPipeline pipeline;
public ChunkBoundRenderer(AbstractRenderPipeline pipeline) {
this.chunk2idx.defaultReturnValue(-1); this.chunk2idx.defaultReturnValue(-1);
this.pipeline = pipeline;
String vert = ShaderLoader.parse("voxy:chunkoutline/outline.vsh");
String taa = pipeline.taaFunction("getTAA");
if (taa != null) {
vert = vert+"\n\n\n"+taa;
}
this.rasterShader = Shader.makeAuto()
.addSource(ShaderType.VERTEX, vert)
.defineIf("TAA", taa != null)
.add(ShaderType.FRAGMENT, "voxy:chunkoutline/outline.fsh")
.compile()
.ubo(0, this.uniformBuffer)
.ssbo(1, this.chunkPosBuffer);
} }
public void addSection(long pos) { public void addSection(long pos) {
@@ -115,6 +127,7 @@ public class ChunkBoundRenderer {
viewport.depthBoundingBuffer.bind(); viewport.depthBoundingBuffer.bind();
this.rasterShader.bind(); this.rasterShader.bind();
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, SharedIndexBuffer.INSTANCE_BB_BYTE.id()); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, SharedIndexBuffer.INSTANCE_BB_BYTE.id());
this.pipeline.bindUniforms();
//Batch the draws into groups of size 32 //Batch the draws into groups of size 32
int count = this.chunk2idx.size(); int count = this.chunk2idx.size();

View File

@@ -69,7 +69,7 @@ public class SectionUpdateRouter implements ISectionWatcher {
} }
lock.unlock(stamp); lock.unlock(stamp);
} }
if ((delta&UPDATE_TYPE_BLOCK_BIT)!=0) { if (((delta&types)&UPDATE_TYPE_BLOCK_BIT)!=0) {
//If we added it, immediately invoke for an update //If we added it, immediately invoke for an update
this.initialRenderMeshGen.accept(position); this.initialRenderMeshGen.accept(position);
} }
@@ -138,19 +138,32 @@ public class SectionUpdateRouter implements ISectionWatcher {
var lock = this.locks[idx]; var lock = this.locks[idx];
long stamp = lock.readLock(); long stamp = lock.readLock();
byte types = set.getOrDefault(position, (byte) 0); byte types = (byte) (set.getOrDefault(position, (byte) 0)&type);
lock.unlockRead(stamp); lock.unlockRead(stamp);
if (types!=0) { if (types!=0) {
if ((type&WorldEngine.UPDATE_TYPE_CHILD_EXISTENCE_BIT)!=0) { if ((types&WorldEngine.UPDATE_TYPE_CHILD_EXISTENCE_BIT)!=0) {
this.childUpdateCallback.accept(section); this.childUpdateCallback.accept(section);
} }
if ((type& UPDATE_TYPE_BLOCK_BIT)!=0) { if ((types&UPDATE_TYPE_BLOCK_BIT)!=0) {
this.renderMeshGen.accept(section.key); this.renderMeshGen.accept(section.key);
} }
} }
} }
public void triggerRemesh(long position) {
int idx = getSliceIndex(position);
var set = this.slices[idx];
var lock = this.locks[idx];
long stamp = lock.readLock();
byte types = set.getOrDefault(position, (byte) 0);
lock.unlockRead(stamp);
if ((types&UPDATE_TYPE_BLOCK_BIT)!=0) {
this.renderMeshGen.accept(position);
}
}
private static int getSliceIndex(long value) { private static int getSliceIndex(long value) {
value = (value ^ value >>> 30) * -4658895280553007687L; value = (value ^ value >>> 30) * -4658895280553007687L;
value = (value ^ value >>> 27) * -7723592293110705685L; value = (value ^ value >>> 27) * -7723592293110705685L;

View File

@@ -111,7 +111,9 @@ public abstract class Viewport <A extends Viewport<A>> {
(float) (this.cameraY-(sy<<5)), (float) (this.cameraY-(sy<<5)),
(float) (this.cameraZ-(sz<<5))); (float) (this.cameraZ-(sz<<5)));
this.depthBoundingBuffer.resize(this.width, this.height); if (this.depthBoundingBuffer.resize(this.width, this.height)) {
this.depthBoundingBuffer.clear(0.0f);
}
return (A) this; return (A) this;
} }

View File

@@ -18,7 +18,7 @@ import java.util.Arrays;
public class RenderDataFactory { public class RenderDataFactory {
private static final boolean CHECK_NEIGHBOR_FACE_OCCLUSION = true; private static final boolean CHECK_NEIGHBOR_FACE_OCCLUSION = true;
private static final boolean DISABLE_CULL_SAME_OCCLUDES = true; private static final boolean DISABLE_CULL_SAME_OCCLUDES = false;//TODO: FIX TRANSLUCENTS (e.g. stained glass) breaking on chunk boarders with this set to false (it might be something else????)
private static final boolean VERIFY_MESHING = VoxyCommon.isVerificationFlagOn("verifyMeshing"); private static final boolean VERIFY_MESHING = VoxyCommon.isVerificationFlagOn("verifyMeshing");
@@ -341,7 +341,7 @@ public class RenderDataFactory {
private static final long LM = (0xFFL<<55); private static final long LM = (0xFFL<<55);
private static boolean shouldMeshNonOpaqueBlockFace(int face, long quad, long meta, long neighborQuad, long neighborMeta) { private static boolean shouldMeshNonOpaqueBlockFace(int face, long quad, long meta, long neighborQuad, long neighborMeta) {
if (((quad^neighborQuad)&(0xFFFFL<<26))==0 && (DISABLE_CULL_SAME_OCCLUDES || ModelQueries.faceOccludes(meta, face))) return false;//This is a hack, if the neigbor and this are the same, dont mesh the face// TODO: FIXME if (((quad^neighborQuad)&(0xFFFFL<<26))==0 && (DISABLE_CULL_SAME_OCCLUDES || (ModelQueries.cullsSame(meta)||ModelQueries.faceOccludes(meta, face)))) return false;//This is a hack, if the neigbor and this are the same, dont mesh the face// TODO: FIXME
if (!ModelQueries.faceExists(meta, face)) return false;//Dont mesh if no face if (!ModelQueries.faceExists(meta, face)) return false;//Dont mesh if no face
//if (ModelQueries.faceCanBeOccluded(meta, face)) //TODO: maybe enable this //if (ModelQueries.faceCanBeOccluded(meta, face)) //TODO: maybe enable this
if (ModelQueries.faceOccludes(neighborMeta, face^1)) return false; if (ModelQueries.faceOccludes(neighborMeta, face^1)) return false;
@@ -397,16 +397,21 @@ public class RenderDataFactory {
int iA = idx * 2 + (facingForward == 1 ? 0 : shift); int iA = idx * 2 + (facingForward == 1 ? 0 : shift);
int iB = idx * 2 + (facingForward == 1 ? shift : 0); int iB = idx * 2 + (facingForward == 1 ? shift : 0);
long selfModel = this.sectionData[iA];
long nextModel = this.sectionData[iB];
//Check if next culls this face //Check if next culls this face
if (CHECK_NEIGHBOR_FACE_OCCLUSION) { if (CHECK_NEIGHBOR_FACE_OCCLUSION) {
if (ModelQueries.faceOccludes(this.sectionData[iB + 1], (axis << 1) | (1 - facingForward))) { long neighbor = this.sectionData[iB + 1];
boolean culls = false;
culls |= ((selfModel^nextModel)&(0xFFFFL<<26))==0&&ModelQueries.cullsSame(neighbor);
culls |= ModelQueries.faceOccludes(neighbor, (axis << 1) | (1 - facingForward));
if (culls) {
this.blockMesher.skip(1); this.blockMesher.skip(1);
continue; continue;
} }
} }
long selfModel = this.sectionData[iA];
long nextModel = this.sectionData[iB];
this.blockMesher.putNext(((long) facingForward) |//Facing this.blockMesher.putNext(((long) facingForward) |//Facing
(selfModel&~LM) | (selfModel&~LM) |
(nextModel&LM)//Apply lighting (nextModel&LM)//Apply lighting
@@ -452,8 +457,10 @@ public class RenderDataFactory {
int neighborIdx = ((axis+1)*32*32 * 2)+(side)*32*32; int neighborIdx = ((axis+1)*32*32 * 2)+(side)*32*32;
long neighborId = this.neighboringFaces[neighborIdx + (other*32) + index]; long neighborId = this.neighboringFaces[neighborIdx + (other*32) + index];
long A = this.sectionData[idx * 2];
if (Mapper.getBlockId(neighborId) != 0) {//Not air int nib = Mapper.getBlockId(neighborId);
if (nib != 0) {//Not air
long meta = this.modelMan.getModelMetadataFromClientId(this.modelMan.getModelId(Mapper.getBlockId(neighborId))); long meta = this.modelMan.getModelMetadataFromClientId(this.modelMan.getModelId(Mapper.getBlockId(neighborId)));
if (ModelQueries.isFullyOpaque(meta)) {//Dont mesh this face if (ModelQueries.isFullyOpaque(meta)) {//Dont mesh this face
this.blockMesher.skip(1); this.blockMesher.skip(1);
@@ -463,7 +470,10 @@ public class RenderDataFactory {
//This very funnily causes issues when not combined with meshing non full opaque geometry //This very funnily causes issues when not combined with meshing non full opaque geometry
//TODO:FIXME, when non opaque geometry is added //TODO:FIXME, when non opaque geometry is added
if (CHECK_NEIGHBOR_FACE_OCCLUSION) { if (CHECK_NEIGHBOR_FACE_OCCLUSION) {
if (ModelQueries.faceOccludes(meta, (axis << 1) | (1 - side))) { boolean culls = false;
culls |= nib==((A>>26)&0xFFFF)&&ModelQueries.cullsSame(meta);
culls |= ModelQueries.faceOccludes(meta, (axis << 1) | (1 - side));
if (culls) {
this.blockMesher.skip(1); this.blockMesher.skip(1);
continue; continue;
} }
@@ -471,7 +481,6 @@ public class RenderDataFactory {
} }
long A = this.sectionData[idx * 2];
this.blockMesher.putNext(((side == 0) ? 0L : 1L) | this.blockMesher.putNext(((side == 0) ? 0L : 1L) |
(A&~LM) | (A&~LM) |
@@ -531,7 +540,7 @@ public class RenderDataFactory {
int bi = facingForward == 1 ? b : a; int bi = facingForward == 1 ? b : a;
//TODO: check if must cull against next entries face //TODO: check if must cull against next entries face
if (CHECK_NEIGHBOR_FACE_OCCLUSION) { if (CHECK_NEIGHBOR_FACE_OCCLUSION) {//TODO:SELF OCCLUSION
if (ModelQueries.faceOccludes(this.sectionData[bi + 1], (axis << 1) | (1 - facingForward))) { if (ModelQueries.faceOccludes(this.sectionData[bi + 1], (axis << 1) | (1 - facingForward))) {
this.blockMesher.skip(1); this.blockMesher.skip(1);
continue; continue;
@@ -754,6 +763,7 @@ public class RenderDataFactory {
if (Mapper.getBlockId(neighborId) != 0) {//Not air if (Mapper.getBlockId(neighborId) != 0) {//Not air
int modelId = this.modelMan.getModelId(Mapper.getBlockId(neighborId)); int modelId = this.modelMan.getModelId(Mapper.getBlockId(neighborId));
if (modelId == ((A>>26)&0xFFFF)) {//TODO: FIXME, this technically isnt correct as need to check self occulsion, thinks? if (modelId == ((A>>26)&0xFFFF)) {//TODO: FIXME, this technically isnt correct as need to check self occulsion, thinks?
//TODO: check self occlsuion in the if statment
fail = true; fail = true;
} else { } else {
long meta = this.modelMan.getModelMetadataFromClientId(modelId); long meta = this.modelMan.getModelMetadataFromClientId(modelId);
@@ -768,6 +778,7 @@ public class RenderDataFactory {
long nB = this.sectionData[(idx+skipAmount) * 2 + 1]; long nB = this.sectionData[(idx+skipAmount) * 2 + 1];
boolean failB = false; boolean failB = false;
if ((nA&(0xFFFFL<<26)) == (A&(0xFFFFL<<26))) {//TODO: FIXME, this technically isnt correct as need to check self occulsion, thinks? if ((nA&(0xFFFFL<<26)) == (A&(0xFFFFL<<26))) {//TODO: FIXME, this technically isnt correct as need to check self occulsion, thinks?
//TODO: check self occlsuion in the if statment
failB = true; failB = true;
} else { } else {
if (ModelQueries.faceOccludes(nB, (axis << 1) | (side))) { if (ModelQueries.faceOccludes(nB, (axis << 1) | (side))) {
@@ -930,6 +941,7 @@ public class RenderDataFactory {
//Check if next culls this face //Check if next culls this face
if (CHECK_NEIGHBOR_FACE_OCCLUSION) { if (CHECK_NEIGHBOR_FACE_OCCLUSION) {
//TODO: check self occlsuion
if (ModelQueries.faceOccludes(this.sectionData[iB + 1], (2 << 1) | (1 - facingForward))) { if (ModelQueries.faceOccludes(this.sectionData[iB + 1], (2 << 1) | (1 - facingForward))) {
mesher.skip(1); mesher.skip(1);
continue; continue;
@@ -996,6 +1008,7 @@ public class RenderDataFactory {
if (ModelQueries.isFullyOpaque(meta)) { if (ModelQueries.isFullyOpaque(meta)) {
oki = false; oki = false;
} else if (CHECK_NEIGHBOR_FACE_OCCLUSION && ModelQueries.faceOccludes(meta, (2 << 1) | (1 - 1))) { } else if (CHECK_NEIGHBOR_FACE_OCCLUSION && ModelQueries.faceOccludes(meta, (2 << 1) | (1 - 1))) {
//TODO check self occlsion
oki = false; oki = false;
} }
} }
@@ -1017,6 +1030,7 @@ public class RenderDataFactory {
if (ModelQueries.isFullyOpaque(meta)) { if (ModelQueries.isFullyOpaque(meta)) {
oki = false; oki = false;
} else if (CHECK_NEIGHBOR_FACE_OCCLUSION && ModelQueries.faceOccludes(meta, (2 << 1) | (1 - 0))) { } else if (CHECK_NEIGHBOR_FACE_OCCLUSION && ModelQueries.faceOccludes(meta, (2 << 1) | (1 - 0))) {
//TODO check self occlsion
oki = false; oki = false;
} }
} }
@@ -1122,7 +1136,8 @@ public class RenderDataFactory {
if (CHECK_NEIGHBOR_FACE_OCCLUSION) { if (CHECK_NEIGHBOR_FACE_OCCLUSION) {
if (ModelQueries.faceOccludes(this.sectionData[bi + 1], (2 << 1) | (1 - facingForward))) { if (ModelQueries.faceOccludes(this.sectionData[bi + 1], (2 << 1) | (1 - facingForward))) {
this.blockMesher.skip(1); //TODO check self occlsion
mesher.skip(1);
continue; continue;
} }
} }
@@ -1591,7 +1606,9 @@ public class RenderDataFactory {
if (neighborMsk>>31!=0) {//We failed to get everything so throw exception if (neighborMsk>>31!=0) {//We failed to get everything so throw exception
throw new IdNotYetComputedException(neighborMsk&(~(1<<31)), true); throw new IdNotYetComputedException(neighborMsk&(~(1<<31)), true);
} }
this.acquireNeighborData(section, neighborMsk); if (CHECK_NEIGHBOR_FACE_OCCLUSION) {
this.acquireNeighborData(section, neighborMsk);
}
try { try {
this.generateYZFaces(); this.generateYZFaces();

View File

@@ -51,9 +51,9 @@ public class RenderGenerationService {
private final AtomicInteger holdingSectionCount = new AtomicInteger();//Used to limit section holding private final AtomicInteger holdingSectionCount = new AtomicInteger();//Used to limit section holding
private final AtomicInteger taskQueueCount = new AtomicInteger(); private final AtomicInteger taskQueueCount = new AtomicInteger();
private final PriorityBlockingQueue<BuildTask> taskQueue = new PriorityBlockingQueue<>(320000, (a,b)-> Long.compareUnsigned(a.priority, b.priority)); private final PriorityBlockingQueue<BuildTask> taskQueue = new PriorityBlockingQueue<>(5000, (a,b)-> Long.compareUnsigned(a.priority, b.priority));
private final StampedLock taskMapLock = new StampedLock(); private final StampedLock taskMapLock = new StampedLock();
private final Long2ObjectOpenHashMap<BuildTask> taskMap = new Long2ObjectOpenHashMap<>(320000); private final Long2ObjectOpenHashMap<BuildTask> taskMap = new Long2ObjectOpenHashMap<>(5000);
private final WorldEngine world; private final WorldEngine world;
private final ModelBakerySubsystem modelBakery; private final ModelBakerySubsystem modelBakery;

View File

@@ -20,6 +20,7 @@ import me.cortex.voxy.common.Logger;
import me.cortex.voxy.common.util.AllocationArena; import me.cortex.voxy.common.util.AllocationArena;
import me.cortex.voxy.common.util.MemoryBuffer; import me.cortex.voxy.common.util.MemoryBuffer;
import me.cortex.voxy.common.util.UnsafeUtil; import me.cortex.voxy.common.util.UnsafeUtil;
import me.cortex.voxy.common.world.WorldEngine;
import me.cortex.voxy.common.world.WorldSection; import me.cortex.voxy.common.world.WorldSection;
import org.lwjgl.system.MemoryUtil; import org.lwjgl.system.MemoryUtil;
@@ -188,7 +189,7 @@ public class AsyncNodeManager {
} }
//This is a funny thing, wait a bit, this allows for better batching, but this thread is independent of everything else so waiting a bit should be mostly ok //This is a funny thing, wait a bit, this allows for better batching, but this thread is independent of everything else so waiting a bit should be mostly ok
try { try {
Thread.sleep(25); Thread.sleep(10);
} catch (InterruptedException e) { } catch (InterruptedException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
@@ -513,6 +514,7 @@ public class AsyncNodeManager {
var upload = results.geometryUpload; var upload = results.geometryUpload;
if (!upload.dataUploadPoints.isEmpty()) { if (!upload.dataUploadPoints.isEmpty()) {
((BasicSectionGeometryData)this.geometryData).ensureAccessable(upload.maxElementAccess);
TimingStatistics.A.start(); TimingStatistics.A.start();
int copies = upload.dataUploadPoints.size(); int copies = upload.dataUploadPoints.size();
@@ -759,11 +761,20 @@ public class AsyncNodeManager {
return this.workCounter.get()!=0 || RESULT_HANDLE.get(this) != null; return this.workCounter.get()!=0 || RESULT_HANDLE.get(this) != null;
} }
public void worldEvent(WorldSection section, int flags) { public void worldEvent(WorldSection section, int flags, int neighborMask) {
//If there is any change, we need to clear the geometry cache before emitting update //If there is any change, we need to clear the geometry cache before emitting update
this.geometryCache.clear(section.key); this.geometryCache.clear(section.key);
this.router.forwardEvent(section, flags); this.router.forwardEvent(section, flags);
if (neighborMask != 0) {//trigger rebuilds for neighbors
if ((neighborMask&0b000001)!=0) this.router.triggerRemesh(WorldEngine.getWorldSectionId(section.lvl, section.x, section.y-1, section.z));//-y
if ((neighborMask&0b000010)!=0) this.router.triggerRemesh(WorldEngine.getWorldSectionId(section.lvl, section.x, section.y+1, section.z));//+y
if ((neighborMask&0b000100)!=0) this.router.triggerRemesh(WorldEngine.getWorldSectionId(section.lvl, section.x-1, section.y, section.z));//-x
if ((neighborMask&0b001000)!=0) this.router.triggerRemesh(WorldEngine.getWorldSectionId(section.lvl, section.x+1, section.y, section.z));//+x
if ((neighborMask&0b010000)!=0) this.router.triggerRemesh(WorldEngine.getWorldSectionId(section.lvl, section.x, section.y, section.z-1));//-z
if ((neighborMask&0b100000)!=0) this.router.triggerRemesh(WorldEngine.getWorldSectionId(section.lvl, section.x, section.y, section.z+1));//+z
}
} }
//Results object, which is to be synced between the render thread and worker thread //Results object, which is to be synced between the render thread and worker thread
@@ -848,6 +859,7 @@ public class AsyncNodeManager {
private static class ComputeMemoryCopy { private static class ComputeMemoryCopy {
public int currentElemCopyAmount; public int currentElemCopyAmount;
public int maxElementAccess;
private MemoryBuffer scratchHeaderBuffer = new MemoryBuffer(1<<16); private MemoryBuffer scratchHeaderBuffer = new MemoryBuffer(1<<16);
private MemoryBuffer scratchDataBuffer = new MemoryBuffer(1<<20); private MemoryBuffer scratchDataBuffer = new MemoryBuffer(1<<20);
@@ -899,6 +911,7 @@ public class AsyncNodeManager {
public void upload(int point, MemoryBuffer data) { public void upload(int point, MemoryBuffer data) {
if ((data.size%8)!=0) throw new IllegalStateException("Data must be of size multiple 8"); if ((data.size%8)!=0) throw new IllegalStateException("Data must be of size multiple 8");
int elemSize = (int) (data.size / 8); int elemSize = (int) (data.size / 8);
this.maxElementAccess = Math.max(this.maxElementAccess, point + elemSize);
int header = this.dataUploadPoints.get(point); int header = this.dataUploadPoints.get(point);
if (header != -1) { if (header != -1) {
//If we already have a header location, we just need to reallocate the data //If we already have a header location, we just need to reallocate the data
@@ -973,6 +986,7 @@ public class AsyncNodeManager {
} }
public void reset() { public void reset() {
this.maxElementAccess = 0;
this.currentElemCopyAmount = 0; this.currentElemCopyAmount = 0;
this.dataUploadPoints.clear(); this.dataUploadPoints.clear();
this.arena.reset(); this.arena.reset();

View File

@@ -154,7 +154,7 @@ public class NodeManager {
var request = new SingleNodeRequest(pos); var request = new SingleNodeRequest(pos);
int id = this.singleRequests.put(request); int id = this.singleRequests.put(request);
this.watcher.watch(pos, WorldEngine.UPDATE_FLAGS); this.watcher.watch(pos, WorldEngine.DEFAULT_UPDATE_FLAGS);
this.activeSectionMap.put(pos, id|NODE_TYPE_REQUEST|REQUEST_TYPE_SINGLE); this.activeSectionMap.put(pos, id|NODE_TYPE_REQUEST|REQUEST_TYPE_SINGLE);
this.topLevelNodes.add(pos); this.topLevelNodes.add(pos);
} }
@@ -353,7 +353,7 @@ public class NodeManager {
throw new IllegalStateException("Child pos was in a request but not in active section map"); throw new IllegalStateException("Child pos was in a request but not in active section map");
} }
if (!this.watcher.unwatch(cPos, WorldEngine.UPDATE_FLAGS)) { if (!this.watcher.unwatch(cPos, WorldEngine.DEFAULT_UPDATE_FLAGS)) {
throw new IllegalStateException("Child pos was not being watched"); throw new IllegalStateException("Child pos was not being watched");
} }
} }
@@ -371,7 +371,7 @@ public class NodeManager {
if (this.activeSectionMap.put(cPos, requestId|NODE_TYPE_REQUEST|REQUEST_TYPE_CHILD) != -1) { if (this.activeSectionMap.put(cPos, requestId|NODE_TYPE_REQUEST|REQUEST_TYPE_CHILD) != -1) {
throw new IllegalStateException("Child pos was already in active section tracker but was part of a request"); throw new IllegalStateException("Child pos was already in active section tracker but was part of a request");
} }
if (!this.watcher.watch(cPos, WorldEngine.UPDATE_FLAGS)) { if (!this.watcher.watch(cPos, WorldEngine.DEFAULT_UPDATE_FLAGS)) {
throw new IllegalStateException("Child pos update router issue"); throw new IllegalStateException("Child pos update router issue");
} }
} }
@@ -428,7 +428,7 @@ public class NodeManager {
if (this.activeSectionMap.put(cPos, requestId|NODE_TYPE_REQUEST|REQUEST_TYPE_CHILD) != -1) { if (this.activeSectionMap.put(cPos, requestId|NODE_TYPE_REQUEST|REQUEST_TYPE_CHILD) != -1) {
throw new IllegalStateException("Child pos was already in active section tracker but was part of a request"); throw new IllegalStateException("Child pos was already in active section tracker but was part of a request");
} }
if (!this.watcher.watch(cPos, WorldEngine.UPDATE_FLAGS)) { if (!this.watcher.watch(cPos, WorldEngine.DEFAULT_UPDATE_FLAGS)) {
throw new IllegalStateException("Child pos update router issue"); throw new IllegalStateException("Child pos update router issue");
} }
} }
@@ -467,7 +467,7 @@ public class NodeManager {
if (cnid == -1 || (cnid&NODE_TYPE_MSK) != NODE_TYPE_REQUEST) {//TODO: verify the removed section is a request type of child and the request id matches this if (cnid == -1 || (cnid&NODE_TYPE_MSK) != NODE_TYPE_REQUEST) {//TODO: verify the removed section is a request type of child and the request id matches this
throw new IllegalStateException("Child pos was in a request but not in active section map"); throw new IllegalStateException("Child pos was in a request but not in active section map");
} }
if (!this.watcher.unwatch(cPos, WorldEngine.UPDATE_FLAGS)) { if (!this.watcher.unwatch(cPos, WorldEngine.DEFAULT_UPDATE_FLAGS)) {
throw new IllegalStateException("Child pos was not being watched"); throw new IllegalStateException("Child pos was not being watched");
} }
} }
@@ -663,7 +663,7 @@ public class NodeManager {
if ((cId&NODE_TYPE_MSK) != NODE_TYPE_REQUEST || (cId&REQUEST_TYPE_MSK) != REQUEST_TYPE_CHILD || (cId&NODE_ID_MSK) != reqId) { if ((cId&NODE_TYPE_MSK) != NODE_TYPE_REQUEST || (cId&REQUEST_TYPE_MSK) != REQUEST_TYPE_CHILD || (cId&NODE_ID_MSK) != reqId) {
throw new IllegalStateException("Invalid child active state map: " + cId); throw new IllegalStateException("Invalid child active state map: " + cId);
} }
if (!this.watcher.unwatch(childPos, WorldEngine.UPDATE_FLAGS)) { if (!this.watcher.unwatch(childPos, WorldEngine.DEFAULT_UPDATE_FLAGS)) {
throw new IllegalStateException("Pos was not being watched"); throw new IllegalStateException("Pos was not being watched");
} }
} }
@@ -785,7 +785,7 @@ public class NodeManager {
this.invalidateNode(nodeId); this.invalidateNode(nodeId);
//Unwatch position //Unwatch position
if (!this.watcher.unwatch(pos, WorldEngine.UPDATE_FLAGS)) { if (!this.watcher.unwatch(pos, WorldEngine.DEFAULT_UPDATE_FLAGS)) {
throw new IllegalStateException("Pos was not being watched"); throw new IllegalStateException("Pos was not being watched");
} }
} else { } else {
@@ -795,7 +795,7 @@ public class NodeManager {
this.invalidateNode(nodeId); this.invalidateNode(nodeId);
} }
} else if (type == NODE_TYPE_REQUEST) { } else if (type == NODE_TYPE_REQUEST) {
if (!this.watcher.unwatch(pos, WorldEngine.UPDATE_FLAGS)) { if (!this.watcher.unwatch(pos, WorldEngine.DEFAULT_UPDATE_FLAGS)) {
throw new IllegalStateException("Pos was not being watched"); throw new IllegalStateException("Pos was not being watched");
} }
if ((nodeId&REQUEST_TYPE_MSK) == REQUEST_TYPE_SINGLE) { if ((nodeId&REQUEST_TYPE_MSK) == REQUEST_TYPE_SINGLE) {
@@ -1072,7 +1072,8 @@ public class NodeManager {
public void processRequest(long pos) { public void processRequest(long pos) {
int nodeId = this.activeSectionMap.get(pos); int nodeId = this.activeSectionMap.get(pos);
if (nodeId == -1) { if (nodeId == -1) {
Logger.warn("Got request for pos " + WorldEngine.pprintPos(pos) + " but it was not in active map, ignoring!"); //TODO: make into timing thing
//Logger.warn("Got request for pos " + WorldEngine.pprintPos(pos) + " but it was not in active map, ignoring!");
return; return;
} }
int nodeType = nodeId&NODE_TYPE_MSK; int nodeType = nodeId&NODE_TYPE_MSK;
@@ -1183,7 +1184,7 @@ public class NodeManager {
} }
//Watch and request the child node at the given position //Watch and request the child node at the given position
if (!this.watcher.watch(childPos, WorldEngine.UPDATE_FLAGS)) { if (!this.watcher.watch(childPos, WorldEngine.DEFAULT_UPDATE_FLAGS)) {
throw new IllegalStateException("Failed to watch childPos"); throw new IllegalStateException("Failed to watch childPos");
} }
} }
@@ -1237,7 +1238,8 @@ public class NodeManager {
int nodeType = nodeId&NODE_TYPE_MSK; int nodeType = nodeId&NODE_TYPE_MSK;
nodeId &= NODE_ID_MSK; nodeId &= NODE_ID_MSK;
if (nodeType == NODE_TYPE_REQUEST) { if (nodeType == NODE_TYPE_REQUEST) {
Logger.warn("Tried removing geometry for pos: " + WorldEngine.pprintPos(pos) + " but its type was a request, ignoring!"); //TODO: only log a specific number of times
//Logger.warn("Tried removing geometry for pos: " + WorldEngine.pprintPos(pos) + " but its type was a request, ignoring!");
return; return;
} }
//this.clearId(nodeId); //this.clearId(nodeId);

View File

@@ -139,7 +139,7 @@ public class TestNodeManager {
} }
private static String[] getPrettyTypes(int msk) { private static String[] getPrettyTypes(int msk) {
if ((msk&~UPDATE_FLAGS)!=0) { if ((msk&~(DEFAULT_UPDATE_FLAGS|UPDATE_TYPE_DONT_SAVE))!=0) {
throw new IllegalStateException(); throw new IllegalStateException();
} }
String[] types = new String[Integer.bitCount(msk)]; String[] types = new String[Integer.bitCount(msk)];
@@ -150,6 +150,9 @@ public class TestNodeManager {
if ((msk&UPDATE_TYPE_CHILD_EXISTENCE_BIT)!=0) { if ((msk&UPDATE_TYPE_CHILD_EXISTENCE_BIT)!=0) {
types[i++] = "CHILD"; types[i++] = "CHILD";
} }
if ((msk&UPDATE_TYPE_DONT_SAVE)!=0) {
types[i++] = "DONT_SAVE";
}
return types; return types;
} }
} }

View File

@@ -17,6 +17,7 @@ import me.cortex.voxy.client.core.rendering.util.SharedIndexBuffer;
import me.cortex.voxy.client.core.rendering.util.UploadStream; import me.cortex.voxy.client.core.rendering.util.UploadStream;
import me.cortex.voxy.common.Logger; import me.cortex.voxy.common.Logger;
import me.cortex.voxy.common.world.WorldEngine; import me.cortex.voxy.common.world.WorldEngine;
import net.minecraft.client.MinecraftClient;
import org.joml.Matrix4f; import org.joml.Matrix4f;
import org.lwjgl.system.MemoryUtil; import org.lwjgl.system.MemoryUtil;
@@ -95,13 +96,16 @@ public class MDICSectionRenderer extends AbstractSectionRenderer<MDICViewport, B
//The pipeline can be used to transform the renderer in abstract ways //The pipeline can be used to transform the renderer in abstract ways
String vertex = ShaderLoader.parse("voxy:lod/gl46/quads2.vert"); String vertex = ShaderLoader.parse("voxy:lod/gl46/quads2.vert");
String taa = pipeline.taaFunction(this, "taaShift"); String taa = pipeline.taaFunction("taaShift");
if (taa != null) { if (taa != null) {
vertex += "\n"+taa;//inject it at the end vertex += "\n"+taa;//inject it at the end
} }
var builder = Shader.make() var builder = Shader.make()
.defineIf("TAA_PATCH", taa != null) .defineIf("TAA_PATCH", taa != null)
.defineIf("DEBUG_RENDER", false) .defineIf("DEBUG_RENDER", false)
.defineIf("DARKENED_TINTING", MinecraftClient.getInstance().world.getDimensionEffects().isDarkened())//TODO: FIXME: this is really jank atm
.addSource(ShaderType.VERTEX, vertex); .addSource(ShaderType.VERTEX, vertex);
String frag = ShaderLoader.parse("voxy:lod/gl46/quads.frag"); String frag = ShaderLoader.parse("voxy:lod/gl46/quads.frag");

View File

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

View File

@@ -34,12 +34,12 @@ public class BasicSectionGeometryData implements IGeometryData {
Logger.info("if your game crashes/exits here without any other log message, try manually decreasing the geometry capacity"); Logger.info("if your game crashes/exits here without any other log message, try manually decreasing the geometry capacity");
glGetError();//Clear any errors glGetError();//Clear any errors
GlBuffer buffer = null; GlBuffer buffer = null;
if (!(Capabilities.INSTANCE.isNvidia && ThreadUtils.isWindows)) { if (!(Capabilities.INSTANCE.isNvidia)) {// && ThreadUtils.isWindows
buffer = new GlBuffer(geometryCapacity, false);//Only do this if we are not on nvidia buffer = new GlBuffer(geometryCapacity, false);//Only do this if we are not on nvidia
//TODO: FIXME: TEST, see if the issue is that we are trying to zero the entire buffer, try only zeroing increments //TODO: FIXME: TEST, see if the issue is that we are trying to zero the entire buffer, try only zeroing increments
// or dont zero it at all // or dont zero it at all
} else { } else {
Logger.info("Running on windows nvidia, using workaround sparse buffer allocation"); Logger.info("Running on nvidia, using workaround sparse buffer allocation");
} }
int error = glGetError(); int error = glGetError();
if (error != GL_NO_ERROR || buffer == null) { if (error != GL_NO_ERROR || buffer == null) {
@@ -49,9 +49,6 @@ public class BasicSectionGeometryData implements IGeometryData {
buffer.free(); buffer.free();
} }
buffer = new GlBuffer(geometryCapacity, GL_SPARSE_STORAGE_BIT_ARB); buffer = new GlBuffer(geometryCapacity, GL_SPARSE_STORAGE_BIT_ARB);
glBindBuffer(GL_ARRAY_BUFFER, buffer.id);
glBufferPageCommitmentARB(GL_ARRAY_BUFFER, 0, geometryCapacity, true);
glBindBuffer(GL_ARRAY_BUFFER, 0);
//buffer.zero(); //buffer.zero();
error = glGetError(); error = glGetError();
if (error != GL_NO_ERROR) { if (error != GL_NO_ERROR) {
@@ -67,6 +64,20 @@ public class BasicSectionGeometryData implements IGeometryData {
Logger.info("Successfully allocated the geometry buffer in " + delta + "ms"); Logger.info("Successfully allocated the geometry buffer in " + delta + "ms");
} }
private long sparseCommitment = 0;//Tracks the current range of the allocated sparse buffer
public void ensureAccessable(int maxElementAccess) {
long size = (Integer.toUnsignedLong(maxElementAccess)*8L+65535L)&~65535L;
//If we are a sparse buffer, ensure the memory upto the requested size is allocated
if (this.geometryBuffer.isSparse()) {
if (this.sparseCommitment < size) {//if we try to access memory outside the allocation range, allocate it
glBindBuffer(GL_ARRAY_BUFFER, this.geometryBuffer.id);
glBufferPageCommitmentARB(GL_ARRAY_BUFFER, this.sparseCommitment, size-this.sparseCommitment, true);
glBindBuffer(GL_ARRAY_BUFFER, 0);
this.sparseCommitment = size;
}
}
}
public GlBuffer getGeometryBuffer() { public GlBuffer getGeometryBuffer() {
return this.geometryBuffer; return this.geometryBuffer;
} }
@@ -100,11 +111,20 @@ public class BasicSectionGeometryData implements IGeometryData {
glFinish(); glFinish();
gpuMemory = Capabilities.INSTANCE.getFreeDedicatedGpuMemory(); gpuMemory = Capabilities.INSTANCE.getFreeDedicatedGpuMemory();
} }
if (this.geometryBuffer.isSparse()) {
glBindBuffer(GL_ARRAY_BUFFER, this.geometryBuffer.id);
glBufferPageCommitmentARB(GL_ARRAY_BUFFER, 0, this.sparseCommitment, false);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
glFinish(); glFinish();
this.geometryBuffer.free(); this.geometryBuffer.free();
glFinish(); glFinish();
if (Capabilities.INSTANCE.canQueryGpuMemory) { if (Capabilities.INSTANCE.canQueryGpuMemory) {
long releaseSize = (long) (this.geometryBuffer.size()*0.75);//if gpu memory usage drops by 75% of the expected value assume we freed it long releaseSize = (long) (this.geometryBuffer.size()*0.75);//if gpu memory usage drops by 75% of the expected value assume we freed it
if (this.geometryBuffer.isSparse()) {//If we are using sparse buffers, use the commited size instead
releaseSize = (long)(this.sparseCommitment*0.75);
}
if (Capabilities.INSTANCE.getFreeDedicatedGpuMemory()-gpuMemory<=releaseSize) { if (Capabilities.INSTANCE.getFreeDedicatedGpuMemory()-gpuMemory<=releaseSize) {
Logger.info("Attempting to wait for gpu memory to release"); Logger.info("Attempting to wait for gpu memory to release");
long start = System.currentTimeMillis(); long start = System.currentTimeMillis();

View File

@@ -22,14 +22,16 @@ public class DepthFramebuffer {
this.depthType = depthType; this.depthType = depthType;
} }
public void resize(int width, int height) { public boolean resize(int width, int height) {
if (this.depthBuffer == null || this.depthBuffer.getWidth() != width || this.depthBuffer.getHeight() != height) { if (this.depthBuffer == null || this.depthBuffer.getWidth() != width || this.depthBuffer.getHeight() != height) {
if (this.depthBuffer != null) { if (this.depthBuffer != null) {
this.depthBuffer.free(); this.depthBuffer.free();
} }
this.depthBuffer = new GlTexture().store(this.depthType, 1, width, height); this.depthBuffer = new GlTexture().store(this.depthType, 1, width, height);
this.framebuffer.bind(this.depthType == GL_DEPTH24_STENCIL8?GL_DEPTH_STENCIL_ATTACHMENT: GL_DEPTH_ATTACHMENT, this.depthBuffer).verify(); this.framebuffer.bind(this.depthType == GL_DEPTH24_STENCIL8?GL_DEPTH_STENCIL_ATTACHMENT: GL_DEPTH_ATTACHMENT, this.depthBuffer).verify();
return true;
} }
return false;
} }
public void clear() { public void clear() {

View File

@@ -45,7 +45,7 @@ public class HiZBuffer2 {
} }
private void alloc(int width, int height) { private void alloc(int width, int height) {
this.levels = (int)Math.ceil(Math.log(Math.max(width, height))/Math.log(2)); this.levels = Math.min(7,(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 //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 //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) // (could probably increase it to be defined by a max meshlet coverage computation thing)

View File

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

View File

@@ -6,6 +6,7 @@ import net.caffeinemc.mods.sodium.client.render.chunk.ChunkRenderMatrices;
import net.caffeinemc.mods.sodium.client.util.FogParameters; import net.caffeinemc.mods.sodium.client.util.FogParameters;
import net.fabricmc.loader.api.FabricLoader; import net.fabricmc.loader.api.FabricLoader;
import net.irisshaders.iris.Iris; import net.irisshaders.iris.Iris;
import net.irisshaders.iris.api.v0.IrisApi;
import net.irisshaders.iris.gl.IrisRenderSystem; import net.irisshaders.iris.gl.IrisRenderSystem;
import net.irisshaders.iris.shadows.ShadowRenderer; import net.irisshaders.iris.shadows.ShadowRenderer;
@@ -19,7 +20,7 @@ public class IrisUtil {
public static CapturedViewportParameters CAPTURED_VIEWPORT_PARAMETERS; public static CapturedViewportParameters CAPTURED_VIEWPORT_PARAMETERS;
public static final boolean IRIS_INSTALLED = FabricLoader.getInstance().isModLoaded("iris"); public static final boolean IRIS_INSTALLED = FabricLoader.getInstance().isModLoaded("iris");
public static final boolean SHADER_SUPPORT = System.getProperty("voxy.enableExperimentalIrisPipeline", "false").equalsIgnoreCase("true"); public static final boolean SHADER_SUPPORT = true;//System.getProperty("voxy.enableExperimentalIrisPipeline", "false").equalsIgnoreCase("true");
private static boolean irisShadowActive0() { private static boolean irisShadowActive0() {
@@ -47,4 +48,10 @@ public class IrisUtil {
public static boolean irisShaderPackEnabled() { public static boolean irisShaderPackEnabled() {
return IRIS_INSTALLED && irisShaderPackEnabled0(); return IRIS_INSTALLED && irisShaderPackEnabled0();
} }
public static void disableIrisShaders() {
if(IRIS_INSTALLED) disableIrisShaders0();
}
private static void disableIrisShaders0() {
IrisApi.getInstance().getConfig().setShadersEnabledAndApply(false);//Disable shaders
}
} }

View File

@@ -6,19 +6,19 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectLinkedOpenHashMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap; import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import me.cortex.voxy.common.Logger; import me.cortex.voxy.common.Logger;
import net.irisshaders.iris.shaderpack.ShaderPack; import net.irisshaders.iris.shaderpack.ShaderPack;
import net.irisshaders.iris.shaderpack.include.AbsolutePackPath; import net.irisshaders.iris.shaderpack.include.AbsolutePackPath;
import org.lwjgl.opengl.ARBDrawBuffersBlend;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.util.List;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.IntSupplier; import java.util.function.IntSupplier;
import static org.lwjgl.opengl.GL11.*; import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.opengl.GL33.*; import static org.lwjgl.opengl.GL33.*;
import static org.lwjgl.opengl.GL40.glBlendFuncSeparatei;
public class IrisShaderPatch { public class IrisShaderPatch {
public static final int VERSION = ((IntSupplier)()->1).getAsInt(); public static final int VERSION = ((IntSupplier)()->1).getAsInt();
@@ -77,7 +77,7 @@ public class IrisShaderPatch {
} }
} }
public record BlendState(int buffer, boolean off, int sRBG, int dRGb, int sA, int dA) { public record BlendState(int buffer, boolean off, int sRGB, int dRGB, int sA, int dA) {
public static BlendState ALL_OFF = new BlendState(-1, true, 0,0,0,0); public static BlendState ALL_OFF = new BlendState(-1, true, 0,0,0,0);
} }
@@ -85,6 +85,9 @@ public class IrisShaderPatch {
private static final class BlendStateDeserializer implements JsonDeserializer<Int2ObjectMap<BlendState>> { private static final class BlendStateDeserializer implements JsonDeserializer<Int2ObjectMap<BlendState>> {
private static int parseType(String type) { private static int parseType(String type) {
type = type.toUpperCase(); type = type.toUpperCase();
if (!type.startsWith("GL_")) {
type = "GL_"+type;
}
return switch (type) { return switch (type) {
case "GL_ZERO" -> GL_ZERO; case "GL_ZERO" -> GL_ZERO;
case "GL_ONE" -> GL_ONE; case "GL_ONE" -> GL_ONE;
@@ -100,7 +103,10 @@ public class IrisShaderPatch {
case "GL_SRC1_COLOR" -> GL_SRC1_COLOR; case "GL_SRC1_COLOR" -> GL_SRC1_COLOR;
case "GL_ONE_MINUS_SRC1_COLOR" -> GL_ONE_MINUS_SRC1_COLOR; case "GL_ONE_MINUS_SRC1_COLOR" -> GL_ONE_MINUS_SRC1_COLOR;
case "GL_ONE_MINUS_SRC1_ALPHA" -> GL_ONE_MINUS_SRC1_ALPHA; case "GL_ONE_MINUS_SRC1_ALPHA" -> GL_ONE_MINUS_SRC1_ALPHA;
default -> -1; default -> {
Logger.error("Unknown blend option " + type);
yield -1;
}
}; };
} }
@Override @Override
@@ -116,20 +122,30 @@ public class IrisShaderPatch {
} else if (json.isJsonObject()) { } else if (json.isJsonObject()) {
for (var entry : json.getAsJsonObject().entrySet()) { for (var entry : json.getAsJsonObject().entrySet()) {
int buffer = Integer.parseInt(entry.getKey()); int buffer = Integer.parseInt(entry.getKey());
BlendState state; BlendState state = null;
var val = entry.getValue(); var val = entry.getValue();
List<String> bs = null;
if (val.isJsonArray()) { if (val.isJsonArray()) {
int[] v = val.getAsJsonArray().asList().stream().mapToInt(a->parseType(a.getAsString())).toArray(); bs = val.getAsJsonArray().asList().stream().map(JsonElement::getAsString).toList();
state = new BlendState(buffer, false, v[0], v[1], v[2], v[3]);
} else if (val.isJsonPrimitive()) { } else if (val.isJsonPrimitive()) {
if (val.getAsString().equalsIgnoreCase("off")) { var str = val.getAsString();
if (str.equalsIgnoreCase("off")) {
state = new BlendState(buffer, true, 0,0,0,0); state = new BlendState(buffer, true, 0,0,0,0);
} else { } else {
state = new BlendState(buffer, true, -1,-1,-1,-1); var parts = str.split(" ");
if (parts.length < 4) {
state = new BlendState(buffer, true, -1, -1, -1, -1);
} else {
bs = List.of(parts);
}
} }
} else { } else {
state = null; state = null;
} }
if (bs != null) {
int[] v = bs.stream().mapToInt(BlendStateDeserializer::parseType).toArray();
state = new BlendState(buffer, false, v[0], v[1], v[2], v[3]);
}
ret.put(buffer, state); ret.put(buffer, state);
} }
return ret; return ret;
@@ -160,6 +176,14 @@ public class IrisShaderPatch {
public float[] renderScale; public float[] renderScale;
public boolean useViewportDims; public boolean useViewportDims;
public boolean checkValid() { public boolean checkValid() {
if (this.blending != null) {
for (BlendState state : this.blending.values()) {
if (state.buffer != -1 && (state.buffer<0||this.translucentDrawBuffers.length<=state.buffer)) {
return false;
}
}
}
return this.opaqueDrawBuffers != null && this.translucentDrawBuffers != null && this.uniforms != null && this.opaquePatchData != null; return this.opaqueDrawBuffers != null && this.translucentDrawBuffers != null && this.uniforms != null && this.opaquePatchData != null;
} }
} }
@@ -239,7 +263,7 @@ public class IrisShaderPatch {
glDisable(GL_BLEND); glDisable(GL_BLEND);
} else { } else {
glEnable(GL_BLEND); glEnable(GL_BLEND);
glBlendFuncSeparate(init.sRBG, init.dRGb, init.sA, init.dA); glBlendFuncSeparate(init.sRGB, init.dRGB, init.sA, init.dA);
} }
} }
for (var entry:BS.int2ObjectEntrySet()) { for (var entry:BS.int2ObjectEntrySet()) {
@@ -249,7 +273,8 @@ public class IrisShaderPatch {
glDisablei(GL_BLEND, s.buffer); glDisablei(GL_BLEND, s.buffer);
} else { } else {
glEnablei(GL_BLEND, s.buffer); glEnablei(GL_BLEND, s.buffer);
glBlendFuncSeparatei(s.buffer, s.sRBG, s.dRGb, s.sA, s.dA); //_sigh_ thanks nvidia
ARBDrawBuffersBlend.glBlendFuncSeparateiARB(s.buffer, s.sRGB, s.dRGB, s.sA, s.dA);
} }
} }
}; };
@@ -294,7 +319,7 @@ public class IrisShaderPatch {
} }
patchData = GSON.fromJson(voxyPatchData, PatchGson.class); patchData = GSON.fromJson(voxyPatchData, PatchGson.class);
if (patchData == null) { if (patchData == null) {
return null; throw new IllegalStateException("Voxy patch json returned null");
} }
{//Inject data from the auxilery files if they are present {//Inject data from the auxilery files if they are present
@@ -306,6 +331,11 @@ public class IrisShaderPatch {
if (translucent != null) { if (translucent != null) {
patchData.translucentPatchData = translucent; patchData.translucentPatchData = translucent;
} }
//This might be ok? not.. sure if is nice or not
var taa = sourceProvider.apply(directory.resolve("voxy_taa.glsl"));
if (taa != null) {
patchData.taaOffset = taa;
}
} }
if (!patchData.checkValid()) { if (!patchData.checkValid()) {
@@ -314,13 +344,14 @@ public class IrisShaderPatch {
} catch (Exception e) { } catch (Exception e) {
patchData = null; patchData = null;
Logger.error("Failed to parse patch data gson",e); Logger.error("Failed to parse patch data gson",e);
throw new ShaderLoadError("Failed to parse patch data gson",e);
} }
if (patchData == null) { if (patchData == null) {
return null; return null;
} }
if (patchData.version != VERSION) { if (patchData.version != VERSION) {
Logger.error("Shader has voxy patch data, but patch version is incorrect. expected " + VERSION + " got "+patchData.version); Logger.error("Shader has voxy patch data, but patch version is incorrect. expected " + VERSION + " got "+patchData.version);
return null; throw new IllegalStateException("Shader version mismatch expected " + VERSION + " got "+patchData.version);
} }
return new IrisShaderPatch(patchData, ipack); return new IrisShaderPatch(patchData, ipack);
} }

View File

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

View File

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

View File

@@ -0,0 +1,85 @@
package me.cortex.voxy.client.mixin.minecraft;
import me.cortex.voxy.client.config.VoxyConfig;
import me.cortex.voxy.client.core.IGetVoxyRenderSystem;
import me.cortex.voxy.common.world.service.VoxelIngestService;
import me.cortex.voxy.commonImpl.VoxyCommon;
import me.cortex.voxy.commonImpl.WorldIdentifier;
import net.minecraft.block.BlockState;
import net.minecraft.client.network.ClientPlayNetworkHandler;
import net.minecraft.client.render.WorldRenderer;
import net.minecraft.client.world.ClientChunkManager;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.registry.RegistryKey;
import net.minecraft.registry.entry.RegistryEntry;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.ChunkSectionPos;
import net.minecraft.world.LightType;
import net.minecraft.world.World;
import net.minecraft.world.dimension.DimensionType;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(ClientWorld.class)
public abstract class MixinClientWorld {
@Unique
private int bottomSectionY;
@Shadow @Final public WorldRenderer worldRenderer;
@Shadow public abstract ClientChunkManager getChunkManager();
@Inject(method = "<init>", at = @At("TAIL"))
private void voxy$getBottom(
ClientPlayNetworkHandler networkHandler,
ClientWorld.Properties properties,
RegistryKey<World> registryRef,
RegistryEntry<DimensionType> dimensionType,
int loadDistance,
int simulationDistance,
WorldRenderer worldRenderer,
boolean debugWorld,
long seed,
int seaLevel,
CallbackInfo cir) {
this.bottomSectionY = ((World)(Object)this).getBottomY()>>4;
}
@Inject(method = "scheduleBlockRerenderIfNeeded", at = @At("TAIL"))
private void voxy$injectIngestOnStateChange(BlockPos pos, BlockState old, BlockState updated, CallbackInfo cir) {
if (old == updated) return;
//TODO: is this _really_ needed, we should have enough processing power to not need todo it if its only a
// block removal
if (!updated.isAir()) return;
if (!VoxyConfig.CONFIG.ingestEnabled) return;//Only ingest if setting enabled
var self = (World)(Object)this;
var wi = WorldIdentifier.of(self);
if (wi == null) {
return;
}
int x = pos.getX()&15;
int y = pos.getY()&15;
int z = pos.getZ()&15;
if (x == 0 || x==15 || y==0 || y==15 || z==0||z==15) {//Update if there is a statechange on the boarder
var csp = ChunkSectionPos.from(pos);
var section = self.getChunk(pos).getSection(csp.getSectionY()-this.bottomSectionY);
var lp = self.getLightingProvider();
var blp = lp.get(LightType.BLOCK).getLightSection(csp);
var slp = lp.get(LightType.SKY).getLightSection(csp);
VoxelIngestService.rawIngest(wi, section, csp.getSectionX(), csp.getSectionY(), csp.getSectionZ(), blp==null?null:blp.copy(), slp==null?null:slp.copy());
}
}
}

View File

@@ -8,6 +8,9 @@ import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import java.io.PrintWriter;
import java.io.StringWriter;
@Mixin(GlDebug.class) @Mixin(GlDebug.class)
public class MixinGlDebug { public class MixinGlDebug {
@WrapOperation(method = "onDebugMessage", at = @At(value = "INVOKE", target = "Lorg/slf4j/Logger;info(Ljava/lang/String;Ljava/lang/Object;)V", remap = false)) @WrapOperation(method = "onDebugMessage", at = @At(value = "INVOKE", target = "Lorg/slf4j/Logger;info(Ljava/lang/String;Ljava/lang/Object;)V", remap = false))
@@ -15,7 +18,7 @@ public class MixinGlDebug {
if (msgObj instanceof GlDebug.DebugMessage msg) { if (msgObj instanceof GlDebug.DebugMessage msg) {
var throwable = new Throwable(msg.toString()); var throwable = new Throwable(msg.toString());
if (isCausedByVoxy(throwable.getStackTrace())) { if (isCausedByVoxy(throwable.getStackTrace())) {
original.call(instance, base, throwable); original.call(instance, base+"\n"+getStackTraceAsString(throwable), throwable);
} else { } else {
original.call(instance, base, msg); original.call(instance, base, msg);
} }
@@ -24,6 +27,14 @@ public class MixinGlDebug {
} }
} }
@Unique
private static String getStackTraceAsString(Throwable throwable) {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
throwable.printStackTrace(pw);
return sw.toString();
}
@Unique @Unique
private boolean isCausedByVoxy(StackTraceElement[] trace) { private boolean isCausedByVoxy(StackTraceElement[] trace) {
for (var elem : trace) { for (var elem : trace) {

View File

@@ -4,6 +4,7 @@ import me.cortex.voxy.client.VoxyClientInstance;
import me.cortex.voxy.client.config.VoxyConfig; import me.cortex.voxy.client.config.VoxyConfig;
import me.cortex.voxy.client.core.IGetVoxyRenderSystem; import me.cortex.voxy.client.core.IGetVoxyRenderSystem;
import me.cortex.voxy.client.core.VoxyRenderSystem; import me.cortex.voxy.client.core.VoxyRenderSystem;
import me.cortex.voxy.client.core.util.IrisUtil;
import me.cortex.voxy.common.Logger; import me.cortex.voxy.common.Logger;
import me.cortex.voxy.common.world.WorldEngine; import me.cortex.voxy.common.world.WorldEngine;
import me.cortex.voxy.commonImpl.VoxyCommon; import me.cortex.voxy.commonImpl.VoxyCommon;
@@ -21,7 +22,6 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(WorldRenderer.class) @Mixin(WorldRenderer.class)
public abstract class MixinWorldRenderer implements IGetVoxyRenderSystem { public abstract class MixinWorldRenderer implements IGetVoxyRenderSystem {
@Shadow private Frustum frustum;
@Shadow private @Nullable ClientWorld world; @Shadow private @Nullable ClientWorld world;
@Unique private VoxyRenderSystem renderer; @Unique private VoxyRenderSystem renderer;
@@ -79,6 +79,14 @@ public abstract class MixinWorldRenderer implements IGetVoxyRenderSystem {
Logger.error("Null world selected"); Logger.error("Null world selected");
return; return;
} }
this.renderer = new VoxyRenderSystem(world, instance.getThreadPool()); try {
this.renderer = new VoxyRenderSystem(world, instance.getThreadPool());
} catch (RuntimeException e) {
if (IrisUtil.irisShaderPackEnabled()) {
IrisUtil.disableIrisShaders();
} else {
throw e;
}
}
} }
} }

View File

@@ -21,7 +21,7 @@ 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, FogParameters fogParameters, CallbackInfo ci) { private void injectRender(ChunkRenderMatrices matrices, CommandList commandList, ChunkRenderListIterable renderLists, TerrainRenderPass renderPass, CameraTransform camera, FogParameters fogParameters, boolean indexedRenderingEnabled, 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) {

View File

@@ -12,6 +12,7 @@ import net.caffeinemc.mods.sodium.client.render.chunk.RenderSection;
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.caffeinemc.mods.sodium.client.render.chunk.map.ChunkTrackerHolder; import net.caffeinemc.mods.sodium.client.render.chunk.map.ChunkTrackerHolder;
import net.caffeinemc.mods.sodium.client.render.chunk.translucent_sorting.SortBehavior;
import net.fabricmc.loader.api.FabricLoader; 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;
@@ -34,7 +35,7 @@ public class MixinRenderSectionManager {
@Shadow @Final private ClientWorld level; @Shadow @Final private ClientWorld level;
@Inject(method = "<init>", at = @At("TAIL")) @Inject(method = "<init>", at = @At("TAIL"))
private void voxy$resetChunkTracker(ClientWorld level, int renderDistance, CommandList commandList, CallbackInfo ci) { private void voxy$resetChunkTracker(ClientWorld level, int renderDistance, SortBehavior sortBehavior, CommandList commandList, CallbackInfo ci) {
if (level.worldRenderer != null) { if (level.worldRenderer != null) {
var system = ((IGetVoxyRenderSystem)(level.worldRenderer)).getVoxyRenderSystem(); var system = ((IGetVoxyRenderSystem)(level.worldRenderer)).getVoxyRenderSystem();
if (system != null) { if (system != null) {

View File

@@ -15,8 +15,8 @@ public class AllocationArena {
private static final int SIZE_BITS = 64 - ADDR_BITS; private static final int SIZE_BITS = 64 - ADDR_BITS;
private static final long SIZE_MSK = (1L<<SIZE_BITS)-1; private static final long SIZE_MSK = (1L<<SIZE_BITS)-1;
private static final long ADDR_MSK = (1L<<ADDR_BITS)-1; private static final long ADDR_MSK = (1L<<ADDR_BITS)-1;
private final LongRBTreeSet FREE = new LongRBTreeSet();//Size Address private final LongRBTreeSet FREE = new LongRBTreeSet(Long::compareUnsigned);//Size Address
private final LongRBTreeSet TAKEN = new LongRBTreeSet();//Address Size private final LongRBTreeSet TAKEN = new LongRBTreeSet(Long::compareUnsigned);//Address Size
private long sizeLimit = Long.MAX_VALUE; private long sizeLimit = Long.MAX_VALUE;
private long totalSize; private long totalSize;
@@ -41,6 +41,19 @@ public class AllocationArena {
public long getSize() { public long getSize() {
return this.totalSize; return this.totalSize;
} }
public int numFreeBlocks() {
return this.FREE.size();
}
public int getLargestFreeBlockSize(int index) {
var iter = this.FREE.tailSet(-1).iterator();
for (;index>0&&iter.hasPrevious();index--){iter.previousLong();}
long slot = iter.previousLong();
return (int) (slot>>ADDR_BITS);
}
/* /*
public long allocFromLargest(int size) {//Allocates from the largest avalible block, this is useful for expanding later on public long allocFromLargest(int size) {//Allocates from the largest avalible block, this is useful for expanding later on
@@ -190,4 +203,8 @@ public class AllocationArena {
throw new IllegalStateException("Size set smaller than current size"); throw new IllegalStateException("Size set smaller than current size");
} }
} }
public long getLimit() {
return this.sizeLimit;
}
} }

View File

@@ -69,6 +69,7 @@ public class ActiveSectionTracker {
} }
public WorldSection acquire(long key, boolean nullOnEmpty) { public WorldSection acquire(long key, boolean nullOnEmpty) {
//TODO: add optional verification check to ensure this (or other critical systems) arnt being called on the render or server thread
if (this.engine != null) this.engine.lastActiveTime = System.currentTimeMillis(); if (this.engine != null) this.engine.lastActiveTime = System.currentTimeMillis();
int index = this.getCacheArrayIndex(key); int index = this.getCacheArrayIndex(key);
var cache = this.loadedSectionCache[index]; var cache = this.loadedSectionCache[index];
@@ -113,6 +114,10 @@ public class ActiveSectionTracker {
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);
if (section != null) {
section.primeForReuse();
section.acquire(1);
}
lock.unlockRead(stamp2); lock.unlockRead(stamp2);
} else { } else {
VolatileHolder.PRE_ACQUIRE_COUNT.getAndAdd(holder, 1); VolatileHolder.PRE_ACQUIRE_COUNT.getAndAdd(holder, 1);
@@ -142,11 +147,10 @@ public class ActiveSectionTracker {
//We need to set the data to air as it is undefined state //We need to set the data to air as it is undefined state
Arrays.fill(section.data, 0); Arrays.fill(section.data, 0);
} }
} else { section.acquire(1);
section.primeForReuse();
} }
int preAcquireCount = (int) VolatileHolder.PRE_ACQUIRE_COUNT.getAndSet(holder, 0); int preAcquireCount = (int) VolatileHolder.PRE_ACQUIRE_COUNT.getAndSet(holder, 0);
section.acquire(preAcquireCount+1);//pre acquire amount section.acquire(preAcquireCount);//pre acquire amount
VolatileHolder.POST_ACQUIRE_COUNT.set(holder, preAcquireCount); VolatileHolder.POST_ACQUIRE_COUNT.set(holder, preAcquireCount);
//TODO: mark if the section was loaded null //TODO: mark if the section was loaded null
@@ -223,7 +227,7 @@ public class ActiveSectionTracker {
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: " + WorldEngine.pprintPos(section.key)); throw new IllegalStateException("This should be impossible: " + WorldEngine.pprintPos(section.key) + " secObj: " + System.identityHashCode(section));
} }
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));
@@ -235,6 +239,7 @@ public class ActiveSectionTracker {
WorldSection aa = null; WorldSection aa = null;
if (sec != null) { if (sec != null) {
long stamp2 = this.lruLock.writeLock(); long stamp2 = this.lruLock.writeLock();
lock.unlockWrite(stamp);
WorldSection a = this.lruSecondaryCache.put(section.key, section); WorldSection a = this.lruSecondaryCache.put(section.key, section);
if (a != null) { if (a != null) {
throw new IllegalStateException("duplicate sections in cache is impossible"); throw new IllegalStateException("duplicate sections in cache is impossible");
@@ -245,9 +250,10 @@ public class ActiveSectionTracker {
} }
this.lruLock.unlockWrite(stamp2); this.lruLock.unlockWrite(stamp2);
} else {
lock.unlockWrite(stamp);
} }
lock.unlockWrite(stamp);
if (aa != null) { if (aa != null) {
aa._releaseArray(); aa._releaseArray();
@@ -276,16 +282,45 @@ public class ActiveSectionTracker {
return this.lruSecondaryCache.size(); return this.lruSecondaryCache.size();
} }
public static void main(String[] args) { public static void main(String[] args) throws InterruptedException {
var tracker = new ActiveSectionTracker(1, a->0, 1<<10); var tracker = new ActiveSectionTracker(6, a->0, 2<<10);
var bean = tracker.acquire(0, 0, 0, 9, false);
var section = tracker.acquire(0,0,0,0, false); var bean2 = tracker.acquire(1, 0, 0, 0, false);
section.acquire(); System.out.println("Target obj:" + System.identityHashCode(bean2));
var section2 = tracker.acquire(0,0,0,0, false); bean2.release();
section.release(); Thread[] ts = new Thread[10];
section.release(); for (int i = 0; i < ts.length;i++) {
section = tracker.acquire(0,0,0,0, false); int tid = i;
section.release(); ts[i] = new Thread(()->{
try {
for (int j = 0; j < 5000; j++) {
if (true) {
var section = tracker.acquire(0, 0, 0, 0, false);
section.acquire();
var section2 = tracker.acquire(1, 0, 0, 0, false);
section.release();
section.release();
section2.release();
}
if (true) {
var section = tracker.acquire(0, 0, 0, 0, false);
var section2 = tracker.acquire(1, 0, 0, 0, false);
section2.release();
section.release();
}
if (true) {
tracker.acquire(1, 0, 0, 0, false).release();
}
}
} catch (Exception e) {
throw new RuntimeException("Thread " + tid, e);
}
});
ts[i].start();
}
for (var t : ts) {
t.join();
}
} }
} }

View File

@@ -16,9 +16,10 @@ public class WorldEngine {
public static final int UPDATE_TYPE_BLOCK_BIT = 1; public static final int UPDATE_TYPE_BLOCK_BIT = 1;
public static final int UPDATE_TYPE_CHILD_EXISTENCE_BIT = 2; public static final int UPDATE_TYPE_CHILD_EXISTENCE_BIT = 2;
public static final int UPDATE_FLAGS = UPDATE_TYPE_BLOCK_BIT | UPDATE_TYPE_CHILD_EXISTENCE_BIT; public static final int UPDATE_TYPE_DONT_SAVE = 4;
public static final int DEFAULT_UPDATE_FLAGS = UPDATE_TYPE_BLOCK_BIT | UPDATE_TYPE_CHILD_EXISTENCE_BIT;
public interface ISectionChangeCallback {void accept(WorldSection section, int updateFlags);} public interface ISectionChangeCallback {void accept(WorldSection section, int updateFlags, int neighborMsk);}
public interface ISectionSaveCallback {void save(WorldEngine engine, WorldSection section);} public interface ISectionSaveCallback {void save(WorldEngine engine, WorldSection section);}
private final TrackedObject thisTracker = TrackedObject.createTrackedObject(this); private final TrackedObject thisTracker = TrackedObject.createTrackedObject(this);
@@ -112,18 +113,18 @@ public class WorldEngine {
//Marks a section as dirty, enqueuing it for saving and or render data rebuilding //Marks a section as dirty, enqueuing it for saving and or render data rebuilding
public void markDirty(WorldSection section) { public void markDirty(WorldSection section) {
this.markDirty(section, UPDATE_FLAGS); this.markDirty(section, DEFAULT_UPDATE_FLAGS, 0);
} }
public void markDirty(WorldSection section, int changeState) { public void markDirty(WorldSection section, int changeState, int neighborMsk) {
if (!this.isLive) throw new IllegalStateException("World is not live"); if (!this.isLive) throw new IllegalStateException("World is not live");
if (section.tracker != this.sectionTracker) { if (section.tracker != this.sectionTracker) {
throw new IllegalStateException("Section is not from here"); throw new IllegalStateException("Section is not from here");
} }
if (this.dirtyCallback != null) { if (this.dirtyCallback != null) {
this.dirtyCallback.accept(section, changeState); this.dirtyCallback.accept(section, changeState, neighborMsk);
} }
if (!section.inSaveQueue) { if ((!section.inSaveQueue)&&(changeState&UPDATE_TYPE_DONT_SAVE)==0) {
section.markDirty(); section.markDirty();
} }
} }

View File

@@ -122,7 +122,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: " + WorldEngine.pprintPos(this.key)); throw new IllegalStateException("Tried to acquire unloaded section: " + WorldEngine.pprintPos(this.key) + " obj: " + System.identityHashCode(this));
} }
return state>>1; return state>>1;
} }

View File

@@ -99,7 +99,21 @@ public class WorldUpdater {
} }
if (didStateChange||(emptinessStateChange!=0)) { if (didStateChange||(emptinessStateChange!=0)) {
into.markDirty(worldSection, (didStateChange?UPDATE_TYPE_BLOCK_BIT:0)|(emptinessStateChange!=0?UPDATE_TYPE_CHILD_EXISTENCE_BIT:0)); //TODO: somehow foward the neighbors that are facing the updated area, this allows forwarding to the dirty consumer
// which can decide wether to dispatch mesh rebuilds to the surounding sections
//Bitmask of neighboring sections
//Note, this may be zero (this is more likely to occure at higher lod levels) if it doesnt face any neighbors
int neighbors = 0;
if (didStateChange) {
neighbors |= ((section.y^(section.y-1))>>(lvl+1))==0?0:1<<0;//Down
neighbors |= ((section.y^(section.y+1))>>(lvl+1))==0?0:1<<1;//Up
neighbors |= ((section.x^(section.x-1))>>(lvl+1))==0?0:1<<2;//-x
neighbors |= ((section.x^(section.x+1))>>(lvl+1))==0?0:1<<3;//+x
neighbors |= ((section.z^(section.z-1))>>(lvl+1))==0?0:1<<4;//-z
neighbors |= ((section.z^(section.z+1))>>(lvl+1))==0?0:1<<5;//+z
}
into.markDirty(worldSection, (didStateChange?UPDATE_TYPE_BLOCK_BIT:0)|(emptinessStateChange!=0?UPDATE_TYPE_CHILD_EXISTENCE_BIT:0), neighbors);
} }
//Need to release the section after using it //Need to release the section after using it

View File

@@ -1,12 +1,16 @@
package me.cortex.voxy.common.world.other; package me.cortex.voxy.common.world.other;
import com.mojang.serialization.Dynamic;
import it.unimi.dsi.fastutil.objects.ObjectArrayList; import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import me.cortex.voxy.common.Logger; import me.cortex.voxy.common.Logger;
import me.cortex.voxy.common.config.IMappingStorage; import me.cortex.voxy.common.config.IMappingStorage;
import net.minecraft.SharedConstants;
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.block.LeavesBlock;
import net.minecraft.datafixer.Schemas;
import net.minecraft.datafixer.TypeReferences;
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;
@@ -95,26 +99,31 @@ public class Mapper {
} }
private void loadFromStorage() { private void loadFromStorage() {
//TODO: FIXME: have/store the minecraft version the mappings are from (the data version)
// SharedConstants.getGameVersion().dataVersion().id()
// then use this to create an update path instead
var mappings = this.storage.getIdMappingsData(); var mappings = this.storage.getIdMappingsData();
List<StateEntry> sentries = new ArrayList<>(); List<StateEntry> sentries = new ArrayList<>();
List<BiomeEntry> bentries = new ArrayList<>(); List<BiomeEntry> bentries = new ArrayList<>();
List<Pair<byte[], Integer>> sentryErrors = new ArrayList<>(); List<Pair<byte[], Integer>> sentryErrors = new ArrayList<>();
boolean[] forceResave = new boolean[1];
for (var entry : mappings.int2ObjectEntrySet()) { for (var entry : mappings.int2ObjectEntrySet()) {
int entryType = entry.getIntKey()>>>30; int entryType = entry.getIntKey()>>>30;
int id = entry.getIntKey() & ((1<<30)-1); int id = entry.getIntKey() & ((1<<30)-1);
if (entryType == BLOCK_STATE_TYPE) { if (entryType == BLOCK_STATE_TYPE) {
var sentry = StateEntry.deserialize(id, entry.getValue()); var sentry = StateEntry.deserialize(id, entry.getValue(), forceResave);
if (sentry.state.isAir()) { if (sentry.state.isAir()) {
Logger.error("Deserialization was air, removed block"); Logger.error("Deserialization was air, removed block");
sentryErrors.add(new Pair<>(entry.getValue(), id)); sentryErrors.add(new Pair<>(entry.getValue(), id));
continue; continue;
} }
sentries.add(sentry); sentries.add(sentry);
var oldEntry = this.block2stateEntry.put(sentry.state, sentry); var oldEntry = this.block2stateEntry.putIfAbsent(sentry.state, sentry);
if (oldEntry != null) { if (oldEntry != null) {
throw new IllegalStateException("Multiple mappings for blockstate"); //forceResave[0] |= true;
Logger.warn("Multiple mappings for blockstate, using old state, expect things to possibly go really badly. " + oldEntry.id + ":" + sentry.id + ":" + sentry.state );
} }
} else if (entryType == BIOME_TYPE) { } else if (entryType == BIOME_TYPE) {
var bentry = BiomeEntry.deserialize(id, entry.getValue()); var bentry = BiomeEntry.deserialize(id, entry.getValue());
@@ -127,7 +136,8 @@ public class Mapper {
} }
} }
{ if (!sentryErrors.isEmpty()) {
forceResave[0] |= true;
//Insert garbage types into the mapping for those blocks, TODO:FIXME: Need to upgrade the type or have a solution to error blocks //Insert garbage types into the mapping for those blocks, TODO:FIXME: Need to upgrade the type or have a solution to error blocks
var rand = new Random(); var rand = new Random();
for (var error : sentryErrors) { for (var error : sentryErrors) {
@@ -156,6 +166,10 @@ public class Mapper {
this.biomeId2biomeEntry.add(entry); this.biomeId2biomeEntry.add(entry);
}); });
if (forceResave[0]) {
Logger.warn("Forced state resave triggered");
this.forceResaveStates();
}
} }
public final int getBlockStateCount() { public final int getBlockStateCount() {
@@ -357,14 +371,29 @@ public class Mapper {
} }
} }
public static StateEntry deserialize(int id, byte[] data) { public static StateEntry deserialize(int id, byte[] data, boolean[] forceResave) {
try { try {
var compound = NbtIo.readCompressed(new ByteArrayInputStream(data), NbtSizeTracker.ofUnlimitedBytes()); var compound = NbtIo.readCompressed(new ByteArrayInputStream(data), NbtSizeTracker.ofUnlimitedBytes());
if (compound.getInt("id", -1) != id) { if (compound.getInt("id", -1) != id) {
throw new IllegalStateException("Encoded id != expected id"); throw new IllegalStateException("Encoded id != expected id");
} }
BlockState state = BlockState.CODEC.parse(NbtOps.INSTANCE, compound.getCompound("block_state").orElseThrow()).getOrThrow(); var bsc = compound.getCompound("block_state").orElseThrow();
return new StateEntry(id, state); var state = BlockState.CODEC.parse(NbtOps.INSTANCE, bsc);
if (state.isError()) {
Logger.info("Could not decode blockstate, attempting fixes, error: "+ state.error().get().message());
bsc = (NbtCompound) Schemas.getFixer().update(TypeReferences.BLOCK_STATE, new Dynamic<>(NbtOps.INSTANCE,bsc),0, SharedConstants.getGameVersion().dataVersion().id()).getValue();
state = BlockState.CODEC.parse(NbtOps.INSTANCE, bsc);
if (state.isError()) {
Logger.error("Could not decode blockstate setting to air. id:" + id + " error: " + state.error().get().message());
return new StateEntry(id, Blocks.AIR.getDefaultState());
} else {
Logger.info("Fixed blockstate to: " + state.getOrThrow());
forceResave[0] |= true;
return new StateEntry(id, state.getOrThrow());
}
} else {
return new StateEntry(id, state.getOrThrow());
}
} catch (IOException e) { } catch (IOException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }

View File

@@ -200,6 +200,13 @@ public class VoxelIngestService {
} }
} }
public static boolean rawIngest(WorldIdentifier id, ChunkSection section, int x, int y, int z, ChunkNibbleArray bl, ChunkNibbleArray sl) {
if (id == null) return false;
var engine = id.getOrCreateEngine();
if (engine == null) return false;
return rawIngest(engine, section, x, y, z, bl, sl);
}
public static boolean rawIngest(WorldEngine engine, ChunkSection section, int x, int y, int z, ChunkNibbleArray bl, ChunkNibbleArray sl) { public static boolean rawIngest(WorldEngine engine, ChunkSection section, int x, int y, int z, ChunkNibbleArray bl, ChunkNibbleArray sl) {
if (!shouldIngestSection(section, x, y, z)) return false; if (!shouldIngestSection(section, x, y, z)) return false;
if (engine.instanceIn == null) return false; if (engine.instanceIn == null) return false;

View File

@@ -86,6 +86,5 @@ public class VoxyCommon implements ModInitializer {
return FACTORY != null; return FACTORY != null;
} }
public static final boolean IS_MINE_IN_ABYSS = false; public static final boolean IS_MINE_IN_ABYSS = false;
} }

View File

@@ -2,6 +2,7 @@
layout(local_size_x = WIDTH, local_size_y = HEIGHT) in; layout(local_size_x = WIDTH, local_size_y = HEIGHT) in;
layout(binding = META_IN_BINDING) uniform usampler2D metaTexIn;
layout(binding = COLOUR_IN_BINDING) uniform sampler2D colourTexIn; layout(binding = COLOUR_IN_BINDING) uniform sampler2D colourTexIn;
layout(binding = DEPTH_IN_BINDING) uniform sampler2D depthTexIn; layout(binding = DEPTH_IN_BINDING) uniform sampler2D depthTexIn;
layout(binding = STENCIL_IN_BINDING) uniform usampler2D stencilTexIn; layout(binding = STENCIL_IN_BINDING) uniform usampler2D stencilTexIn;
@@ -24,8 +25,11 @@ void main() {
float depth = clamp(texelFetch(depthTexIn, samplePoint, 0).r, 0, 1);//Opengl grumble grumble float depth = clamp(texelFetch(depthTexIn, samplePoint, 0).r, 0, 1);//Opengl grumble grumble
uint stencil = texelFetch(stencilTexIn, samplePoint, 0).r; uint stencil = texelFetch(stencilTexIn, samplePoint, 0).r;
uint metadata = texelFetch(metaTexIn, samplePoint, 0).r;
uint value = uint(depth*((1<<24)-1))<<8; uint value = uint(depth*((1<<24)-1))<<8;
value |= stencil; value |= stencil&0xFFu;
//Use the very top bit of the stencil to mask if its tinted
value |= (metadata&1u)<<7;
outPoint.y = value; outPoint.y = value;
outBuffer[globalOutIndex] = outPoint; outBuffer[globalOutIndex] = outPoint;

View File

@@ -3,11 +3,13 @@
layout(location=0) uniform sampler2D tex; layout(location=0) uniform sampler2D tex;
in vec2 texCoord; in vec2 texCoord;
in flat uint metadata; in flat uint metadata;
out vec4 colour; layout(location=0) out vec4 colour;
layout(location=1) out uvec4 metaOut;
void main() { void main() {
colour = texture(tex, texCoord, ((~metadata>>1)&1u)*-16.0f); colour = texture(tex, texCoord, ((~metadata>>1)&1u)*-16.0f);
if (colour.a < 0.001f && ((metadata&1u)!=0)) { if (colour.a < 0.001f && ((metadata&1u)!=0)) {
discard; discard;
} }
metaOut = uvec4((metadata>>2)&1u);//Write if it is or isnt tinted
} }

View File

@@ -21,6 +21,10 @@ bool shouldRender(ivec3 icorner) {
return (corner.x*corner.x + corner.z*corner.z < negInnerSec.w*negInnerSec.w) && abs(corner.y) < negInnerSec.w; return (corner.x*corner.x + corner.z*corner.z < negInnerSec.w*negInnerSec.w) && abs(corner.y) < negInnerSec.w;
} }
#ifdef TAA
vec2 getTAA();
#endif
void main() { void main() {
uint id = (gl_InstanceID<<5)+gl_BaseInstance+(gl_VertexID>>3); uint id = (gl_InstanceID<<5)+gl_BaseInstance+(gl_VertexID>>3);
@@ -38,4 +42,8 @@ void main() {
//cubeCornerI.y = cubeCornerI.y*1024-512; //cubeCornerI.y = cubeCornerI.y*1024-512;
gl_Position = MVP * vec4(vec3(cubeCornerI+origin)*16, 1); gl_Position = MVP * vec4(vec3(cubeCornerI+origin)*16, 1);
gl_Position.z -= 0.0005f; gl_Position.z -= 0.0005f;
#ifdef TAA
gl_Position.xy += getTAA()*gl_Position.w;//Apply TAA if we have it
#endif
} }

View File

@@ -9,7 +9,9 @@ struct BlockModel {
//TODO: FIXME: this isnt actually correct cause depending on the face (i think) it could be 1/64 th of a position off //TODO: FIXME: this isnt actually correct cause depending on the face (i think) it could be 1/64 th of a position off
// but im going to assume that since we are dealing with huge render distances, this shouldent matter that much // but im going to assume that since we are dealing with huge render distances, this shouldent matter that much
float extractFaceIndentation(uint faceData) { float extractFaceIndentation(uint faceData) {
return float((faceData>>16)&63u)/63.0; uint enc = (faceData>>16)&63u;
enc += uint(enc==63u);//convert 63 to 64 cause of pain reasons
return float(enc)/64.0;
} }
vec4 extractFaceSizes(uint faceData) { vec4 extractFaceSizes(uint faceData) {
@@ -25,6 +27,10 @@ uint faceHasAlphaCuttoutOverride(uint faceData) {
return (faceData>>23)&1u; return (faceData>>23)&1u;
} }
uint faceTintState(uint faceData) {
return (faceData>>24)&3u;
}
bool modelHasBiomeLUT(BlockModel model) { bool modelHasBiomeLUT(BlockModel model) {
return ((model.flagsA)&2u) != 0; return ((model.flagsA)&2u) != 0;
} }

View File

@@ -1,8 +1,9 @@
#version 460 core #version 460 core
#extension GL_ARB_gpu_shader_int64 : enable
#define VISIBILITY_ACCESS
#define VISIBILITY_BUFFER_BINDING 2 #define VISIBILITY_BUFFER_BINDING 2
#import <voxy:lod/gl46/bindings.glsl> layout(binding = VISIBILITY_BUFFER_BINDING, std430) restrict buffer VisibilityBuffer {
uint visibilityData[];
};
layout(early_fragment_tests) in; layout(early_fragment_tests) in;
flat in uint id; flat in uint id;
@@ -11,5 +12,5 @@ flat in uint value;
void main() { void main() {
visibilityData[id] = value; visibilityData[id] = value;
//colour = vec4(float(id&7u)/7, float((id>>3)&7u)/7, float((id>>6)&7u)/7, 1); //colour = vec4(float(id&7u)/7, float((id>>3)&7u)/7, float((id>>6)&7u)/7, 0);
} }

View File

@@ -39,8 +39,8 @@ bool useMipmaps() {
return (interData.x&2u)==0u; return (interData.x&2u)==0u;
} }
bool useTinting() { uint tintingState() {
return (interData.x&4u)!=0u; return (interData.x>>2)&3u;
} }
bool useCutout() { bool useCutout() {
@@ -91,13 +91,19 @@ void voxy_emitFragment(VoxyFragmentParameters parameters);
#else #else
vec4 computeColour(vec2 texturePos, vec4 colour) { vec4 computeColour(vec2 texturePos, vec4 colour) {
//Conditional tinting, TODO: FIXME: REPLACE WITH MASK OR SOMETHING, like encode data into the top bit of alpha //Conditional tinting, TODO: FIXME: this is better but still not great, try encode data into the top bit of alpha so its per pixel
if (useTinting()) {
uint tintingFunction = tintingState();
bool doTint = tintingFunction==2;//Always tint if function == 2
if (tintingFunction == 1) {//partial tint
vec4 tintTest = textureLod(blockModelAtlas, texturePos, 0); vec4 tintTest = textureLod(blockModelAtlas, texturePos, 0);
if (abs(tintTest.r-tintTest.g) < 0.02f && abs(tintTest.g-tintTest.b) < 0.02f) { if (abs(tintTest.r-tintTest.g) < 0.02f && abs(tintTest.g-tintTest.b) < 0.02f) {
colour *= uint2vec4RGBA(interData.z).yzwx; doTint = true;
} }
} }
if (doTint) {
colour *= uint2vec4RGBA(interData.z).yzwx;
}
return (colour * uint2vec4RGBA(interData.y)) + vec4(0,0,0,float(interData.w&0xFFu)/255); return (colour * uint2vec4RGBA(interData.y)) + vec4(0,0,0,float(interData.w&0xFFu)/255);
} }
@@ -105,6 +111,7 @@ vec4 computeColour(vec2 texturePos, vec4 colour) {
void main() { void main() {
//vec2 uv = vec2(0);
//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));
@@ -132,11 +139,13 @@ void main() {
if (any(notEqual(clamp(tile, vec2(0), vec2((interData.x>>8)&0xFu, (interData.x>>12)&0xFu)), tile))) { if (any(notEqual(clamp(tile, vec2(0), vec2((interData.x>>8)&0xFu, (interData.x>>12)&0xFu)), tile))) {
discard; discard;
return;
} }
//Check the minimum bounding texture and ensure we are greater than it //Check the minimum bounding texture and ensure we are greater than it
if (gl_FragCoord.z < texelFetch(depthTex, ivec2(gl_FragCoord.xy), 0).r) { if (gl_FragCoord.z < texelFetch(depthTex, ivec2(gl_FragCoord.xy), 0).r) {
discard; discard;
return;
} }
@@ -146,9 +155,16 @@ void main() {
//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
discard; discard;
return;
#endif #endif
} }
#ifndef PATCHED_SHADER_ALLOW_DERIVATIVES
if (gl_HelperInvocation) {
return;
}
#endif
#ifndef PATCHED_SHADER #ifndef PATCHED_SHADER
colour = computeColour(texPos, colour); colour = computeColour(texPos, colour);
outColour = colour; outColour = colour;
@@ -165,13 +181,18 @@ void main() {
#else #else
uint modelId = getModelId(); uint modelId = getModelId();
BlockModel model = modelData[modelId]; BlockModel model = modelData[modelId];
vec4 tint = vec4(1); uint tintingFunction = tintingState();
if (useTinting()) { bool doTint = tintingFunction==2;//Always tint if function == 2
if (tintingFunction==1) {//Partial tint
vec4 tintTest = texture(blockModelAtlas, texPos, -2); vec4 tintTest = texture(blockModelAtlas, texPos, -2);
if (abs(tintTest.r-tintTest.g) < 0.02f && abs(tintTest.g-tintTest.b) < 0.02f) { if (abs(tintTest.r-tintTest.g) < 0.02f && abs(tintTest.g-tintTest.b) < 0.02f) {
tint = uint2vec4RGBA(interData.z).yzwx; doTint = true;
} }
} }
vec4 tint = vec4(1);
if (doTint) {
tint = uint2vec4RGBA(interData.z).yzwx;
}
voxy_emitFragment(VoxyFragmentParameters(colour, tile, texPos, getFace(), modelId, getLightmap().yx, tint, model.customId)); voxy_emitFragment(VoxyFragmentParameters(colour, tile, texPos, getFace(), modelId, getLightmap().yx, tint, model.customId));

View File

@@ -139,13 +139,16 @@ void main() {
//Apply model colour tinting //Apply model colour tinting
uint tintColour = model.colourTint; uint tintColour = model.colourTint;
if (modelHasBiomeLUT(model)) { if (modelHasBiomeLUT(model)) {
tintColour = colourData[tintColour + extractBiomeId(quad)]; tintColour = colourData[tintColour + extractBiomeId(quad)];
} }
uint tintState = faceTintState(faceData);
uint conditionalTinting = 0; uint conditionalTinting = 0;
if (tintColour != uint(-1)) { if (tintColour != uint(-1)) {
flags |= 1u<<2; flags |= tintState<<2;
conditionalTinting = tintColour; conditionalTinting = tintColour;
} }
@@ -164,6 +167,21 @@ void main() {
} }
//Apply face tint //Apply face tint
#ifdef DARKENED_TINTING
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 {//UP DOWN
tinting.xyz *= 0.9f;
}
} else {
tinting.xyz *= 0.9f;
}
#else
if (isShaded) { if (isShaded) {
//TODO: make branchless, infact apply ahead of time to the texture itself in ModelManager since that is //TODO: make branchless, infact apply ahead of time to the texture itself in ModelManager since that is
// per face // per face
@@ -175,6 +193,7 @@ void main() {
tinting.xyz *= 0.5f; tinting.xyz *= 0.5f;
} }
} }
#endif
setTintingAndExtra(tinting, conditionalTinting, addin|(face<<8)); setTintingAndExtra(tinting, conditionalTinting, addin|(face<<8));
#else #else

View File

@@ -9,12 +9,14 @@
"iris.MixinPackRenderTargetDirectives", "iris.MixinPackRenderTargetDirectives",
"iris.CustomUniformsAccessor", "iris.CustomUniformsAccessor",
"iris.IrisRenderingPipelineAccessor", "iris.IrisRenderingPipelineAccessor",
"iris.MixinIris",
"iris.MixinIrisRenderingPipeline", "iris.MixinIrisRenderingPipeline",
"iris.MixinIrisSamplers", "iris.MixinIrisSamplers",
"iris.MixinMatrixUniforms", "iris.MixinMatrixUniforms",
"iris.MixinProgramSet", "iris.MixinProgramSet",
"iris.MixinShaderPackSourceNames", "iris.MixinShaderPackSourceNames",
"iris.MixinStandardMacros", "iris.MixinStandardMacros",
"minecraft.MixinClientWorld",
"minecraft.MixinClientChunkManager", "minecraft.MixinClientChunkManager",
"minecraft.MixinClientCommonNetworkHandler", "minecraft.MixinClientCommonNetworkHandler",
"minecraft.MixinClientLoginNetworkHandler", "minecraft.MixinClientLoginNetworkHandler",

View File

@@ -35,7 +35,7 @@
"minecraft": ["1.21.8","1.21.7","1.21.6"], "minecraft": ["1.21.8","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.7.*"
}, },
"accessWidener": "voxy.accesswidener" "accessWidener": "voxy.accesswidener"
} }