diff --git a/src/main/java/me/cortex/voxy/client/core/RenderPipelineFactory.java b/src/main/java/me/cortex/voxy/client/core/RenderPipelineFactory.java index ba3ac1f5..3585bd9c 100644 --- a/src/main/java/me/cortex/voxy/client/core/RenderPipelineFactory.java +++ b/src/main/java/me/cortex/voxy/client/core/RenderPipelineFactory.java @@ -14,7 +14,7 @@ public class RenderPipelineFactory { public static AbstractRenderPipeline createPipeline(AsyncNodeManager nodeManager, NodeCleaner nodeCleaner, HierarchicalOcclusionTraverser traversal, BooleanSupplier frexSupplier) { //Note this is where will choose/create e.g. IrisRenderPipeline or normal pipeline AbstractRenderPipeline pipeline = null; - if (IrisUtil.IRIS_INSTALLED && System.getProperty("voxy.enableExperimentalIrisPipeline", "false").equalsIgnoreCase("true")) { + if (IrisUtil.IRIS_INSTALLED && IrisUtil.SHADER_SUPPORT) { pipeline = createIrisPipeline(nodeManager, nodeCleaner, traversal, frexSupplier); } if (pipeline == null) { diff --git a/src/main/java/me/cortex/voxy/client/core/VoxyRenderSystem.java b/src/main/java/me/cortex/voxy/client/core/VoxyRenderSystem.java index 25b23d9b..5edf7133 100644 --- a/src/main/java/me/cortex/voxy/client/core/VoxyRenderSystem.java +++ b/src/main/java/me/cortex/voxy/client/core/VoxyRenderSystem.java @@ -253,7 +253,7 @@ public class VoxyRenderSystem { GlStateManager._glBindVertexArray(0);//Clear binding GlStateManager._activeTexture(GlConst.GL_TEXTURE1); - for (int i = 0; i < 16; i++) { + for (int i = 0; i < 12; i++) { GlStateManager._activeTexture(GlConst.GL_TEXTURE0+i); GlStateManager._bindTexture(0); glBindSampler(i, 0); diff --git a/src/main/java/me/cortex/voxy/client/core/rendering/ViewportSelector.java b/src/main/java/me/cortex/voxy/client/core/rendering/ViewportSelector.java index 0793c99e..4020324e 100644 --- a/src/main/java/me/cortex/voxy/client/core/rendering/ViewportSelector.java +++ b/src/main/java/me/cortex/voxy/client/core/rendering/ViewportSelector.java @@ -1,5 +1,6 @@ package me.cortex.voxy.client.core.rendering; +import me.cortex.voxy.client.core.util.IrisUtil; import net.fabricmc.loader.api.FabricLoader; import org.vivecraft.client_vr.ClientDataHolderVR; @@ -19,19 +20,28 @@ public class ViewportSelector > { this.defaultViewport = viewportCreator.get(); } + private T getOrCreate(Object holder) { + return this.extraViewports.computeIfAbsent(holder, a->this.creator.get()); + } + private T getVivecraftViewport() { var cdh = ClientDataHolderVR.getInstance(); var pass = cdh.currentPass; if (pass == null) { return this.defaultViewport; } - return this.extraViewports.computeIfAbsent(pass, a->this.creator.get()); + return this.getOrCreate(pass); } + private static final Object IRIS_SHADOW_OBJECT = new Object(); public T getViewport() { if (VIVECRAFT_INSTALLED) { return getVivecraftViewport(); } + + if (IrisUtil.irisShadowActive()) { + return this.getOrCreate(IRIS_SHADOW_OBJECT); + } return this.defaultViewport; } diff --git a/src/main/java/me/cortex/voxy/client/core/util/IrisUtil.java b/src/main/java/me/cortex/voxy/client/core/util/IrisUtil.java index 3a62ec9c..3ac71ebb 100644 --- a/src/main/java/me/cortex/voxy/client/core/util/IrisUtil.java +++ b/src/main/java/me/cortex/voxy/client/core/util/IrisUtil.java @@ -6,6 +6,7 @@ import net.irisshaders.iris.shadows.ShadowRenderer; public class IrisUtil { public static final boolean IRIS_INSTALLED = FabricLoader.getInstance().isModLoaded("iris"); + public static final boolean SHADER_SUPPORT = System.getProperty("voxy.enableExperimentalIrisPipeline", "false").equalsIgnoreCase("true"); private static boolean irisShadowActive0() { diff --git a/src/main/java/me/cortex/voxy/client/iris/IrisShaderPatch.java b/src/main/java/me/cortex/voxy/client/iris/IrisShaderPatch.java index 6641a3bb..0389f8eb 100644 --- a/src/main/java/me/cortex/voxy/client/iris/IrisShaderPatch.java +++ b/src/main/java/me/cortex/voxy/client/iris/IrisShaderPatch.java @@ -1,20 +1,107 @@ package me.cortex.voxy.client.iris; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.Strictness; +import com.google.gson.*; +import com.google.gson.annotations.JsonAdapter; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import me.cortex.voxy.common.Logger; import net.irisshaders.iris.shaderpack.ShaderPack; import net.irisshaders.iris.shaderpack.include.AbsolutePackPath; import java.lang.reflect.Modifier; +import java.lang.reflect.Type; +import java.util.Map; import java.util.function.Function; import java.util.function.IntSupplier; +import static org.lwjgl.opengl.GL11.*; +import static org.lwjgl.opengl.GL33.*; + public class IrisShaderPatch { public static final int VERSION = ((IntSupplier)()->1).getAsInt(); + private static final class SSBODeserializer implements JsonDeserializer> { + @Override + public Int2ObjectOpenHashMap deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { + Int2ObjectOpenHashMap ret = new Int2ObjectOpenHashMap<>(); + if (json==null) return null; + try { + for (var entry : json.getAsJsonObject().entrySet()) { + ret.put(Integer.parseInt(entry.getKey()), entry.getValue().getAsString()); + } + } catch (Exception e) { + Logger.error(e); + } + return ret; + } + } + + public record BlendState(int buffer, boolean off, int sRBG, int dRGb, int sA, int dA) { + public static BlendState ALL_OFF = new BlendState(-1, true, 0,0,0,0); + } + + + private static final class BlendStateDeserializer implements JsonDeserializer> { + private static int parseType(String type) { + type = type.toUpperCase(); + return switch (type) { + case "GL_ZERO" -> GL_ZERO; + case "GL_ONE" -> GL_ONE; + case "GL_SRC_COLOR" -> GL_SRC_COLOR; + case "GL_ONE_MINUS_SRC_COLOR" -> GL_ONE_MINUS_SRC_COLOR; + case "GL_SRC_ALPHA" -> GL_SRC_ALPHA; + case "GL_ONE_MINUS_SRC_ALPHA" -> GL_ONE_MINUS_SRC_ALPHA; + case "GL_DST_ALPHA" -> GL_DST_ALPHA; + case "GL_ONE_MINUS_DST_ALPHA" -> GL_ONE_MINUS_DST_ALPHA; + case "GL_DST_COLOR" -> GL_DST_COLOR; + case "GL_ONE_MINUS_DST_COLOR" -> GL_ONE_MINUS_DST_COLOR; + case "GL_SRC_ALPHA_SATURATE" -> GL_SRC_ALPHA_SATURATE; + case "GL_SRC1_COLOR" -> GL_SRC1_COLOR; + case "GL_ONE_MINUS_SRC1_COLOR" -> GL_ONE_MINUS_SRC1_COLOR; + case "GL_ONE_MINUS_SRC1_ALPHA" -> GL_ONE_MINUS_SRC1_ALPHA; + default -> -1; + }; + } + @Override + public Int2ObjectMap deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { + if (json==null) return null; + Int2ObjectMap ret = new Int2ObjectOpenHashMap<>(); + try { + if (json.isJsonPrimitive()) { + if (json.getAsString().equalsIgnoreCase("off")) { + ret.put(-1, BlendState.ALL_OFF); + return ret; + } + } else if (json.isJsonObject()) { + for (var entry : json.getAsJsonObject().entrySet()) { + int buffer = Integer.parseInt(entry.getKey()); + BlendState state; + var val = entry.getValue(); + if (val.isJsonArray()) { + int[] v = val.getAsJsonArray().asList().stream().mapToInt(a->parseType(a.getAsString())).toArray(); + state = new BlendState(buffer, false, v[0], v[1], v[2], v[3]); + } else if (val.isJsonPrimitive()) { + if (val.getAsString().equalsIgnoreCase("off")) { + state = new BlendState(buffer, true, 0,0,0,0); + } else { + state = new BlendState(buffer, true, -1,-1,-1,-1); + } + } else { + state = null; + } + ret.put(buffer, state); + } + return ret; + } + } catch (Exception e) { + Logger.error(e); + } + Logger.error("Failed to parse blend state: " + json); + return ret; + } + } + private static class PatchGson { public int version;//TODO maybe replace with semver? public int[] opaqueDrawBuffers; @@ -23,19 +110,35 @@ public class IrisShaderPatch { public String[] samplers; public String[] opaquePatchData; public String[] translucentPatchData; + @JsonAdapter(SSBODeserializer.class) + public Int2ObjectOpenHashMap ssbos; + @JsonAdapter(BlendStateDeserializer.class) + public Int2ObjectOpenHashMap blending; public boolean checkValid() { return this.opaqueDrawBuffers != null && this.translucentDrawBuffers != null && this.uniforms != null && this.opaquePatchData != null; } } + + private final PatchGson patchData; private final ShaderPack pack; + private final Int2ObjectMap ssbos; private IrisShaderPatch(PatchGson patchData, ShaderPack pack) { this.patchData = patchData; this.pack = pack; + + if (patchData.ssbos == null) { + this.ssbos = new Int2ObjectOpenHashMap<>(); + } else { + this.ssbos = patchData.ssbos; + } } + public Int2ObjectMap getSSBOs() { + return this.ssbos; + } public String getPatchOpaqueSource() { return String.join("\n", this.patchData.opaquePatchData); } diff --git a/src/main/java/me/cortex/voxy/client/iris/IrisVoxyRenderPipelineData.java b/src/main/java/me/cortex/voxy/client/iris/IrisVoxyRenderPipelineData.java index 7eac80b3..6facf8b4 100644 --- a/src/main/java/me/cortex/voxy/client/iris/IrisVoxyRenderPipelineData.java +++ b/src/main/java/me/cortex/voxy/client/iris/IrisVoxyRenderPipelineData.java @@ -2,12 +2,14 @@ package me.cortex.voxy.client.iris; import com.google.common.collect.ImmutableSet; import it.unimi.dsi.fastutil.ints.Int2ObjectLinkedOpenHashMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import kroppeb.stareval.function.FunctionReturn; import kroppeb.stareval.function.Type; import me.cortex.voxy.client.core.IrisVoxyRenderPipeline; import me.cortex.voxy.client.mixin.iris.CustomUniformsAccessor; import me.cortex.voxy.client.mixin.iris.IrisRenderingPipelineAccessor; import me.cortex.voxy.common.Logger; +import net.irisshaders.iris.gl.buffer.ShaderStorageBufferHolder; import net.irisshaders.iris.gl.image.ImageHolder; import net.irisshaders.iris.gl.sampler.GlSampler; import net.irisshaders.iris.gl.sampler.SamplerHolder; @@ -26,6 +28,7 @@ import org.lwjgl.system.MemoryUtil; import java.util.*; import java.util.function.IntSupplier; import java.util.function.LongConsumer; +import java.util.stream.Collectors; public class IrisVoxyRenderPipelineData { public IrisVoxyRenderPipeline thePipeline; @@ -57,11 +60,13 @@ public class IrisVoxyRenderPipelineData { return this.translucentPatch; } - public static IrisVoxyRenderPipelineData buildPipeline(IrisRenderingPipeline ipipe, IrisShaderPatch patch, CustomUniforms cu) { + public static IrisVoxyRenderPipelineData buildPipeline(IrisRenderingPipeline ipipe, IrisShaderPatch patch, CustomUniforms cu, ShaderStorageBufferHolder ssboHolder) { var uniforms = createUniformLayoutStructAndUpdater(createUniformSet(cu, patch)); createImageSet(ipipe, patch); + createSSBOLayouts(patch.getSSBOs(), ssboHolder); + var opaqueDrawTargets = getDrawBuffers(patch.getOpqaueTargets(), ipipe.getFlippedAfterPrepare(), ((IrisRenderingPipelineAccessor)ipipe).getRenderTargets()); var translucentDrawTargets = getDrawBuffers(patch.getTranslucentTargets(), ipipe.getFlippedAfterPrepare(), ((IrisRenderingPipelineAccessor)ipipe).getRenderTargets()); @@ -272,8 +277,12 @@ public class IrisVoxyRenderPipelineData { return uniforms; } + private record TextureWSampler(String name, IntSupplier texture, int sampler) { + + } private static void createImageSet(IrisRenderingPipeline ipipe, IrisShaderPatch patch) { - Set samplerNameSet = new HashSet<>(List.of(patch.getSamplerList())); + Set samplerNameSet = new LinkedHashSet<>(List.of(patch.getSamplerList())); + Set samplerSet = new LinkedHashSet<>(); SamplerHolder samplerBuilder = new SamplerHolder() { @Override public boolean hasSampler(String s) { @@ -287,6 +296,13 @@ public class IrisVoxyRenderPipelineData { return false; } + private String name(String... names) { + for (var name : names) { + if (samplerNameSet.contains(name)) return name; + } + return null; + } + @Override public boolean addDefaultSampler(TextureType type, IntSupplier texture, ValueUpdateNotifier notifier, GlSampler sampler, String... names) { Logger.error("Unsupported default sampler"); @@ -301,14 +317,14 @@ public class IrisVoxyRenderPipelineData { @Override public boolean addDynamicSampler(TextureType type, IntSupplier texture, ValueUpdateNotifier notifier, GlSampler sampler, String... names) { if (!this.hasSampler(names)) return false; - Logger.info(Arrays.toString(names)); - return false; + samplerSet.add(new TextureWSampler(this.name(names), texture, sampler!=null?sampler.getId():-1)); + return true; } @Override public void addExternalSampler(int texture, String... names) { if (!this.hasSampler(names)) return; - Logger.info(Arrays.toString(names)); + samplerSet.add(new TextureWSampler(this.name(names), ()->texture, -1)); } }; @@ -326,6 +342,18 @@ public class IrisVoxyRenderPipelineData { }; ipipe.addGbufferOrShadowSamplers(samplerBuilder, imageBuilder, ipipe::getFlippedAfterPrepare, false, true, true, false); + + //samplerSet contains our samplers + if (samplerSet.size() != samplerNameSet.size()) { + Logger.error("Did not find all requested samplers. Found [" + samplerSet.stream().map(a->a.name).collect(Collectors.joining()) + "] expected " + samplerNameSet); + } + + //TODO: generate a layout (defines) for all the samplers with the correct types + } + + private static void createSSBOLayouts(Int2ObjectMap ssbos, ShaderStorageBufferHolder ssboStore) { + //ssboStore.getBufferIndex() + } } diff --git a/src/main/java/me/cortex/voxy/client/mixin/iris/MixinIrisRenderingPipeline.java b/src/main/java/me/cortex/voxy/client/mixin/iris/MixinIrisRenderingPipeline.java index 88810012..0ba62874 100644 --- a/src/main/java/me/cortex/voxy/client/mixin/iris/MixinIrisRenderingPipeline.java +++ b/src/main/java/me/cortex/voxy/client/mixin/iris/MixinIrisRenderingPipeline.java @@ -1,9 +1,11 @@ package me.cortex.voxy.client.mixin.iris; +import me.cortex.voxy.client.core.util.IrisUtil; import me.cortex.voxy.client.iris.IGetIrisVoxyPipelineData; import me.cortex.voxy.client.iris.IGetVoxyPatchData; import me.cortex.voxy.client.iris.IrisShaderPatch; import me.cortex.voxy.client.iris.IrisVoxyRenderPipelineData; +import net.irisshaders.iris.gl.buffer.ShaderStorageBufferHolder; import net.irisshaders.iris.pipeline.IrisRenderingPipeline; import net.irisshaders.iris.shaderpack.programs.ProgramSet; import net.irisshaders.iris.uniforms.custom.CustomUniforms; @@ -18,19 +20,22 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @Mixin(value = IrisRenderingPipeline.class, remap = false) public class MixinIrisRenderingPipeline implements IGetVoxyPatchData, IGetIrisVoxyPipelineData { @Shadow @Final private CustomUniforms customUniforms; + @Shadow private ShaderStorageBufferHolder shaderStorageBufferHolder; @Unique IrisShaderPatch patchData; @Unique IrisVoxyRenderPipelineData pipeline; @Inject(method = "", at = @At(value = "INVOKE", target = "Lnet/irisshaders/iris/pipeline/transform/ShaderPrinter;resetPrintState()V", shift = At.Shift.AFTER)) private void voxy$injectPatchDataStore(ProgramSet programSet, CallbackInfo ci) { - this.patchData = ((IGetVoxyPatchData)programSet).voxy$getPatchData(); + if (IrisUtil.SHADER_SUPPORT) { + this.patchData = ((IGetVoxyPatchData) programSet).voxy$getPatchData(); + } } @Inject(method = "", at = @At(value = "INVOKE", target = "Lnet/irisshaders/iris/pipeline/IrisRenderingPipeline;createSetupComputes([Lnet/irisshaders/iris/shaderpack/programs/ComputeSource;Lnet/irisshaders/iris/shaderpack/programs/ProgramSet;Lnet/irisshaders/iris/shaderpack/texture/TextureStage;)[Lnet/irisshaders/iris/gl/program/ComputeProgram;")) private void voxy$injectPipeline(ProgramSet programSet, CallbackInfo ci) { if (this.patchData != null) { - this.pipeline = IrisVoxyRenderPipelineData.buildPipeline((IrisRenderingPipeline)(Object)this, this.patchData, this.customUniforms); + this.pipeline = IrisVoxyRenderPipelineData.buildPipeline((IrisRenderingPipeline)(Object)this, this.patchData, this.customUniforms, this.shaderStorageBufferHolder); } } diff --git a/src/main/java/me/cortex/voxy/client/mixin/iris/MixinMatrixUniforms.java b/src/main/java/me/cortex/voxy/client/mixin/iris/MixinMatrixUniforms.java index c466153c..7b52423c 100644 --- a/src/main/java/me/cortex/voxy/client/mixin/iris/MixinMatrixUniforms.java +++ b/src/main/java/me/cortex/voxy/client/mixin/iris/MixinMatrixUniforms.java @@ -1,5 +1,7 @@ package me.cortex.voxy.client.mixin.iris; +import me.cortex.voxy.client.config.VoxyConfig; +import me.cortex.voxy.client.core.util.IrisUtil; import me.cortex.voxy.client.iris.VoxyUniforms; import net.irisshaders.iris.gl.uniform.UniformHolder; import net.irisshaders.iris.shaderpack.IdMap; @@ -15,6 +17,8 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; public class MixinMatrixUniforms { @Inject(method = "addNonDynamicUniforms", at = @At("TAIL")) private static void voxy$InjectMatrixUniforms(UniformHolder uniforms, IdMap idMap, PackDirectives directives, FrameUpdateNotifier updateNotifier, CallbackInfo ci) { - VoxyUniforms.addUniforms(uniforms); + if (VoxyConfig.CONFIG.isRenderingEnabled() && IrisUtil.SHADER_SUPPORT) { + VoxyUniforms.addUniforms(uniforms); + } } } diff --git a/src/main/java/me/cortex/voxy/client/mixin/iris/MixinProgramSet.java b/src/main/java/me/cortex/voxy/client/mixin/iris/MixinProgramSet.java index 9f034110..4ebfcdda 100644 --- a/src/main/java/me/cortex/voxy/client/mixin/iris/MixinProgramSet.java +++ b/src/main/java/me/cortex/voxy/client/mixin/iris/MixinProgramSet.java @@ -1,6 +1,7 @@ package me.cortex.voxy.client.mixin.iris; import me.cortex.voxy.client.config.VoxyConfig; +import me.cortex.voxy.client.core.util.IrisUtil; import me.cortex.voxy.client.iris.IGetVoxyPatchData; import me.cortex.voxy.client.iris.IrisShaderPatch; import net.irisshaders.iris.shaderpack.ShaderPack; @@ -25,7 +26,7 @@ public class MixinProgramSet implements IGetVoxyPatchData { @Inject(method = "", at = @At(value = "INVOKE", target = "Lnet/irisshaders/iris/shaderpack/programs/ProgramSet;locateDirectives()V", shift = At.Shift.BEFORE)) private void voxy$injectPatchMaker(AbsolutePackPath directory, Function sourceProvider, ShaderProperties shaderProperties, ShaderPack pack, CallbackInfo ci) { - if (VoxyConfig.CONFIG.isRenderingEnabled()) { + if (VoxyConfig.CONFIG.isRenderingEnabled() && IrisUtil.SHADER_SUPPORT) { this.patchData = IrisShaderPatch.makePatch(pack, directory, sourceProvider); } /* diff --git a/src/main/java/me/cortex/voxy/client/mixin/iris/MixinStandardMacros.java b/src/main/java/me/cortex/voxy/client/mixin/iris/MixinStandardMacros.java index 994b6ac5..d78c10b3 100644 --- a/src/main/java/me/cortex/voxy/client/mixin/iris/MixinStandardMacros.java +++ b/src/main/java/me/cortex/voxy/client/mixin/iris/MixinStandardMacros.java @@ -4,6 +4,7 @@ import com.google.common.collect.ImmutableList; import com.llamalad7.mixinextras.injector.wrapoperation.Operation; import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import me.cortex.voxy.client.config.VoxyConfig; +import me.cortex.voxy.client.core.util.IrisUtil; import net.irisshaders.iris.gl.shader.StandardMacros; import net.irisshaders.iris.helpers.StringPair; import org.spongepowered.asm.mixin.Mixin; @@ -20,7 +21,7 @@ public abstract class MixinStandardMacros { @WrapOperation(method = "createStandardEnvironmentDefines", at = @At(value = "INVOKE", target = "Lcom/google/common/collect/ImmutableList;copyOf(Ljava/util/Collection;)Lcom/google/common/collect/ImmutableList;")) private static ImmutableList voxy$injectVoxyDefine(Collection list, Operation> original) { - if (VoxyConfig.CONFIG.isRenderingEnabled()) { + if (VoxyConfig.CONFIG.isRenderingEnabled() && IrisUtil.SHADER_SUPPORT) { define((List) list, "VOXY"); } return ImmutableList.copyOf(list); diff --git a/src/main/resources/assets/voxy/shaders/lod/gl46/quads.frag b/src/main/resources/assets/voxy/shaders/lod/gl46/quads.frag index e6e33ff8..c4ce9a18 100644 --- a/src/main/resources/assets/voxy/shaders/lod/gl46/quads.frag +++ b/src/main/resources/assets/voxy/shaders/lod/gl46/quads.frag @@ -119,6 +119,17 @@ void main() { colour = textureLod(blockModelAtlas, texPos, 0); } + //If we are in shaders and are a helper invocation, just exit, as it enables extra performance gains for small sized + // fragments, we do this here after derivative computation + //Trying it with all shaders + //#ifdef PATCHED_SHADER + #ifndef PATCHED_SHADER_ALLOW_DERIVATIVES + if (gl_HelperInvocation) { + return; + } + #endif + //#endif + if (any(notEqual(clamp(tile, vec2(0), vec2((interData.x>>8)&0xFu, (interData.x>>12)&0xFu)), tile))) { discard; }