diff --git a/common/src/main/java/net/irisshaders/iris/compat/dh/DHCompatInternal.java b/common/src/main/java/net/irisshaders/iris/compat/dh/DHCompatInternal.java index 0481e99e44..e2bde332c8 100644 --- a/common/src/main/java/net/irisshaders/iris/compat/dh/DHCompatInternal.java +++ b/common/src/main/java/net/irisshaders/iris/compat/dh/DHCompatInternal.java @@ -15,6 +15,7 @@ import net.irisshaders.iris.pipeline.IrisRenderingPipeline; import net.irisshaders.iris.shaderpack.programs.ProgramSource; import net.irisshaders.iris.shaderpack.properties.CloudSetting; +import net.irisshaders.iris.shadows.ShadowRenderTargets; import net.irisshaders.iris.targets.Blaze3dRenderTargetExt; import net.irisshaders.iris.targets.DepthTexture; import net.irisshaders.iris.uniforms.CapturedRenderingState; @@ -81,7 +82,7 @@ public DHCompatInternal(IrisRenderingPipeline pipeline, boolean dhShadowEnabled) ProgramSource shadow = pipeline.getDHShadowShader().get(); shadowProgram = IrisLodRenderProgram.createProgram(shadow.getName(), true, false, shadow, pipeline.getCustomUniforms(), pipeline); if (pipeline.hasShadowRenderTargets()) { - dhShadowFramebuffer = pipeline.createDHFramebufferShadow(shadow); + dhShadowFramebuffer = pipeline.createDHFramebufferShadow(shadow, ShadowRenderTargets.TEMP_LAYER); dhShadowFramebufferWrapper = new DhFrameBufferWrapper(dhShadowFramebuffer); } shouldOverrideShadow = true; diff --git a/common/src/main/java/net/irisshaders/iris/gl/IrisRenderSystem.java b/common/src/main/java/net/irisshaders/iris/gl/IrisRenderSystem.java index 65c48fbb7b..b60147f8c9 100644 --- a/common/src/main/java/net/irisshaders/iris/gl/IrisRenderSystem.java +++ b/common/src/main/java/net/irisshaders/iris/gl/IrisRenderSystem.java @@ -6,6 +6,7 @@ import net.irisshaders.iris.Iris; import net.irisshaders.iris.gl.sampler.SamplerLimits; import net.irisshaders.iris.mixin.GlStateManagerAccessor; +import net.irisshaders.iris.pbr.TextureInfoCache; import org.jetbrains.annotations.Nullable; import org.joml.Matrix4f; import org.joml.Vector3i; @@ -89,6 +90,8 @@ public static void texImage1D(int texture, int target, int level, int internalfo public static void texImage2D(int texture, int target, int level, int internalformat, int width, int height, int border, int format, int type, @Nullable ByteBuffer pixels) { RenderSystem.assertOnRenderThreadOrInit(); + TextureInfoCache.INSTANCE.onTexImage(target, texture, level, internalformat, width, height); + IrisRenderSystem.bindTextureForSetup(target, texture); GL32C.glTexImage2D(target, level, internalformat, width, height, border, format, type, pixels); } @@ -96,6 +99,7 @@ public static void texImage2D(int texture, int target, int level, int internalfo public static void texImage3D(int texture, int target, int level, int internalformat, int width, int height, int depth, int border, int format, int type, @Nullable ByteBuffer pixels) { RenderSystem.assertOnRenderThreadOrInit(); IrisRenderSystem.bindTextureForSetup(target, texture); + TextureInfoCache.INSTANCE.onTexImage(target, texture, level, internalformat, width, height); GL30C.glTexImage3D(target, level, internalformat, width, height, depth, border, format, type, pixels); } @@ -236,6 +240,10 @@ public static void framebufferTexture2D(int fb, int fbtarget, int attachment, in dsaState.framebufferTexture2D(fb, fbtarget, attachment, target, texture, levels); } + public static void framebufferTextureLayer(int fb, int fbtarget, int attachment, int target, int texture, int levels, int layer) { + dsaState.framebufferTextureLayer(fb, fbtarget, attachment, target, texture, levels, layer); + } + public static int getTexParameteri(int texture, int target, int pname) { RenderSystem.assertOnRenderThreadOrInit(); return dsaState.getTexParameteri(texture, target, pname); @@ -454,6 +462,10 @@ public static int createBuffers() { return dsaState.createBuffers(); } + public static void createTextures(int target, int[] textures) { + dsaState.createTextures(target, textures); + } + public interface DSAAccess { void generateMipmaps(int texture, int target); @@ -479,10 +491,14 @@ public interface DSAAccess { void framebufferTexture2D(int fb, int fbtarget, int attachment, int target, int texture, int levels); + void framebufferTextureLayer(int fb, int fbtarget, int attachment, int target, int texture, int levels, int layer); + int createFramebuffer(); int createTexture(int target); + void createTextures(int target, int[] textures); + int createBuffers(); } @@ -566,6 +582,11 @@ public void framebufferTexture2D(int fb, int fbtarget, int attachment, int targe ARBDirectStateAccess.glNamedFramebufferTexture(fb, attachment, texture, levels); } + @Override + public void framebufferTextureLayer(int fb, int fbtarget, int attachment, int target, int texture, int levels, int layer) { + ARBDirectStateAccess.glNamedFramebufferTextureLayer(fb, attachment, texture, levels, layer); + } + @Override public int createFramebuffer() { return ARBDirectStateAccess.glCreateFramebuffers(); @@ -575,6 +596,11 @@ public int createFramebuffer() { public int createTexture(int target) { return ARBDirectStateAccess.glCreateTextures(target); } + + @Override + public void createTextures(int target, int[] textures) { + ARBDirectStateAccess.glCreateTextures(target, textures); + } } public static class DSAUnsupported implements DSAAccess { @@ -659,6 +685,12 @@ public void framebufferTexture2D(int fb, int fbtarget, int attachment, int targe GL32C.glFramebufferTexture2D(fbtarget, attachment, target, texture, levels); } + @Override + public void framebufferTextureLayer(int fb, int fbtarget, int attachment, int target, int texture, int levels, int layer) { + GlStateManager._glBindFramebuffer(fbtarget, fb); + GL32C.glFramebufferTextureLayer(fbtarget, attachment, texture, levels, layer); + } + @Override public int createFramebuffer() { int framebuffer = GlStateManager.glGenFramebuffers(); @@ -673,6 +705,14 @@ public int createTexture(int target) { return texture; } + @Override + public void createTextures(int target, int[] textures) { + GL46C.glGenTextures(textures); + for (int texture : textures) { + GL46C.glBindTexture(target, texture); + } + } + @Override public int createBuffers() { return GlStateManager._glGenBuffers(); diff --git a/common/src/main/java/net/irisshaders/iris/gl/framebuffer/GlFramebuffer.java b/common/src/main/java/net/irisshaders/iris/gl/framebuffer/GlFramebuffer.java index 5a142f42b3..eda6ca02ac 100644 --- a/common/src/main/java/net/irisshaders/iris/gl/framebuffer/GlFramebuffer.java +++ b/common/src/main/java/net/irisshaders/iris/gl/framebuffer/GlFramebuffer.java @@ -39,6 +39,21 @@ public void addDepthAttachment(int texture) { this.hasDepthAttachment = true; } + public void addDepthAttachmentLayer(int texture, int layer) { + int internalFormat = TextureInfoCache.INSTANCE.getInfo(texture).getInternalFormat(); + DepthBufferFormat depthBufferFormat = DepthBufferFormat.fromGlEnumOrDefault(internalFormat); + + int fb = getGlId(); + + if (depthBufferFormat.isCombinedStencil()) { + IrisRenderSystem.framebufferTextureLayer(fb, GL30C.GL_FRAMEBUFFER, GL30C.GL_DEPTH_STENCIL_ATTACHMENT, GL30C.GL_TEXTURE_2D_ARRAY, texture, 0, layer); + } else { + IrisRenderSystem.framebufferTextureLayer(fb, GL30C.GL_FRAMEBUFFER, GL30C.GL_DEPTH_ATTACHMENT, GL30C.GL_TEXTURE_2D_ARRAY, texture, 0, layer); + } + + this.hasDepthAttachment = true; + } + public void addColorAttachment(int index, int texture) { int fb = getGlId(); @@ -46,6 +61,13 @@ public void addColorAttachment(int index, int texture) { attachments.put(index, texture); } + public void addColorAttachmentLayer(int index, int texture, int layer) { + int fb = getGlId(); + + IrisRenderSystem.framebufferTextureLayer(fb, GL30C.GL_FRAMEBUFFER, GL30C.GL_COLOR_ATTACHMENT0 + index, GL30C.GL_TEXTURE_2D_ARRAY, texture, 0, layer); + attachments.put(index, texture); + } + public void noDrawBuffers() { IrisRenderSystem.drawBuffers(getGlId(), new int[]{GL30C.GL_NONE}); } diff --git a/common/src/main/java/net/irisshaders/iris/gl/uniform/FloatArrayUniform.java b/common/src/main/java/net/irisshaders/iris/gl/uniform/FloatArrayUniform.java new file mode 100644 index 0000000000..7b5ff19cd9 --- /dev/null +++ b/common/src/main/java/net/irisshaders/iris/gl/uniform/FloatArrayUniform.java @@ -0,0 +1,42 @@ +package net.irisshaders.iris.gl.uniform; + +import net.irisshaders.iris.gl.IrisRenderSystem; +import net.irisshaders.iris.gl.state.ValueUpdateNotifier; +import org.lwjgl.opengl.GL46C; + +import java.util.Arrays; +import java.util.function.Supplier; + +public class FloatArrayUniform extends Uniform { + private final Supplier value; + private float[] cachedValue; + + FloatArrayUniform(int location, int count, Supplier value) { + this(location, value, count, null); + } + + FloatArrayUniform(int location, Supplier value, int count, ValueUpdateNotifier notifier) { + super(location, notifier); + + this.cachedValue = new float[count]; + this.value = value; + } + + @Override + public void update() { + updateValue(); + + if (notifier != null) { + notifier.setListener(this::updateValue); + } + } + + private void updateValue() { + float[] newValue = value.get(); + + if (!Arrays.equals(newValue, cachedValue)) { + System.arraycopy(newValue, 0, cachedValue, 0, newValue.length); + GL46C.glUniform1fv(location, newValue); + } + } +} diff --git a/common/src/main/java/net/irisshaders/iris/gl/uniform/LocationalUniformHolder.java b/common/src/main/java/net/irisshaders/iris/gl/uniform/LocationalUniformHolder.java index 894b329c1b..5ef360f147 100644 --- a/common/src/main/java/net/irisshaders/iris/gl/uniform/LocationalUniformHolder.java +++ b/common/src/main/java/net/irisshaders/iris/gl/uniform/LocationalUniformHolder.java @@ -1,5 +1,6 @@ package net.irisshaders.iris.gl.uniform; +import net.irisshaders.iris.uniforms.custom.CustomUniformFixedInputUniformsHolder; import org.joml.Matrix4fc; import org.joml.Vector2f; import org.joml.Vector2i; @@ -26,6 +27,13 @@ default LocationalUniformHolder uniform1f(UniformUpdateFrequency updateFrequency return this; } + @Override + default LocationalUniformHolder uniform1fArray(UniformUpdateFrequency updateFrequency, String name, int count, Supplier value) { + location(name, UniformType.FLOAT).ifPresent(id -> addUniform(updateFrequency, new FloatArrayUniform(id, count, value))); + + return this; + } + @Override default LocationalUniformHolder uniform1f(UniformUpdateFrequency updateFrequency, String name, IntSupplier value) { location(name, UniformType.FLOAT).ifPresent(id -> addUniform(updateFrequency, new FloatUniform(id, () -> (float) value.getAsInt()))); @@ -61,6 +69,13 @@ default LocationalUniformHolder uniform2f(UniformUpdateFrequency updateFrequency return this; } + @Override + default LocationalUniformHolder uniform2fArray(UniformUpdateFrequency updateFrequency, String name, int count, Supplier value) { + location(name, UniformType.VEC2).ifPresent(id -> addUniform(updateFrequency, new Vector2ArrayUniform(id, count, value))); + + return this; + } + @Override default LocationalUniformHolder uniform2i(UniformUpdateFrequency updateFrequency, String name, Supplier value) { location(name, UniformType.VEC2I).ifPresent(id -> addUniform(updateFrequency, new Vector2IntegerJomlUniform(id, value))); @@ -118,8 +133,8 @@ default LocationalUniformHolder uniformMatrix(UniformUpdateFrequency updateFrequ } @Override - default LocationalUniformHolder uniformMatrixFromArray(UniformUpdateFrequency updateFrequency, String name, Supplier value) { - location(name, UniformType.MAT4).ifPresent(id -> addUniform(updateFrequency, new MatrixFromFloatArrayUniform(id, value))); + default LocationalUniformHolder uniformMatrixArray(UniformUpdateFrequency updateFrequency, String name, int count, Supplier value) { + location(name, UniformType.MAT4).ifPresent(id -> addUniform(updateFrequency, new MatrixArrayUniform(id, count, value))); return this; } diff --git a/common/src/main/java/net/irisshaders/iris/gl/uniform/MatrixArrayUniform.java b/common/src/main/java/net/irisshaders/iris/gl/uniform/MatrixArrayUniform.java new file mode 100644 index 0000000000..f64ee8b4ff --- /dev/null +++ b/common/src/main/java/net/irisshaders/iris/gl/uniform/MatrixArrayUniform.java @@ -0,0 +1,65 @@ +package net.irisshaders.iris.gl.uniform; + +import com.mojang.blaze3d.systems.RenderSystem; +import net.irisshaders.iris.gl.state.ValueUpdateNotifier; +import org.joml.Matrix4f; +import org.joml.Matrix4fc; +import org.lwjgl.BufferUtils; +import org.lwjgl.opengl.GL46C; +import org.lwjgl.system.MemoryStack; + +import java.nio.FloatBuffer; +import java.util.Arrays; +import java.util.function.Supplier; + +public class MatrixArrayUniform extends Uniform { + private final Supplier value; + private final int count; + private Matrix4fc[] cachedValue; + + MatrixArrayUniform(int location, int count, Supplier value) { + super(location); + + this.count = count; + this.value = value; + } + + MatrixArrayUniform(int location, int count, Supplier value, ValueUpdateNotifier notifier) { + super(location, notifier); + + this.count = count; + this.value = value; + } + + @Override + public void update() { + updateValue(); + + if (notifier != null) { + notifier.setListener(this::updateValue); + } + } + + public void updateValue() { + Matrix4fc[] values = value.get(); + + if (Arrays.equals(cachedValue, values)) { + return; + } else { + System.arraycopy(values, 0, cachedValue, 0, values.length); + } + + try (MemoryStack stack = MemoryStack.stackPush()) { + long buffer = stack.nmalloc(64 * count); + + int offset = 0; + + for (Matrix4fc matrix : values) { + matrix.getToAddress(buffer + offset); + offset += 64; + } + + GL46C.nglUniformMatrix4fv(location, count, false, buffer); + } + } +} diff --git a/common/src/main/java/net/irisshaders/iris/gl/uniform/UniformHolder.java b/common/src/main/java/net/irisshaders/iris/gl/uniform/UniformHolder.java index 0e8dc325dc..2458ee3aec 100644 --- a/common/src/main/java/net/irisshaders/iris/gl/uniform/UniformHolder.java +++ b/common/src/main/java/net/irisshaders/iris/gl/uniform/UniformHolder.java @@ -16,6 +16,8 @@ public interface UniformHolder { UniformHolder uniform1f(UniformUpdateFrequency updateFrequency, String name, FloatSupplier value); + UniformHolder uniform1fArray(UniformUpdateFrequency updateFrequency, String name, int count, Supplier value); + UniformHolder uniform1f(UniformUpdateFrequency updateFrequency, String name, IntSupplier value); UniformHolder uniform1f(UniformUpdateFrequency updateFrequency, String name, DoubleSupplier value); @@ -26,6 +28,8 @@ public interface UniformHolder { UniformHolder uniform2f(UniformUpdateFrequency updateFrequency, String name, Supplier value); + UniformHolder uniform2fArray(UniformUpdateFrequency updateFrequency, String name, int count, Supplier value); + UniformHolder uniform2i(UniformUpdateFrequency updateFrequency, String name, Supplier value); UniformHolder uniform3f(UniformUpdateFrequency updateFrequency, String name, Supplier value); @@ -42,7 +46,7 @@ public interface UniformHolder { UniformHolder uniformMatrix(UniformUpdateFrequency updateFrequency, String name, Supplier value); - UniformHolder uniformMatrixFromArray(UniformUpdateFrequency updateFrequency, String name, Supplier value); + UniformHolder uniformMatrixArray(UniformUpdateFrequency updateFrequency, String name, int count, Supplier value); UniformHolder externallyManagedUniform(String name, UniformType type); } diff --git a/common/src/main/java/net/irisshaders/iris/gl/uniform/Vector2ArrayUniform.java b/common/src/main/java/net/irisshaders/iris/gl/uniform/Vector2ArrayUniform.java new file mode 100644 index 0000000000..0609953dcb --- /dev/null +++ b/common/src/main/java/net/irisshaders/iris/gl/uniform/Vector2ArrayUniform.java @@ -0,0 +1,67 @@ +package net.irisshaders.iris.gl.uniform; + +import net.irisshaders.iris.gl.IrisRenderSystem; +import net.irisshaders.iris.gl.state.ValueUpdateNotifier; +import org.joml.Vector2f; +import org.lwjgl.opengl.GL46C; +import org.lwjgl.system.MemoryStack; + +import java.nio.FloatBuffer; +import java.util.Arrays; +import java.util.function.Supplier; + +public class Vector2ArrayUniform extends Uniform { + private final Supplier value; + private Vector2f[] cachedValue; + + Vector2ArrayUniform(int location, int count, Supplier value) { + super(location); + + this.cachedValue = new Vector2f[count]; + + for (int i = 0; i < count; i++) { + this.cachedValue[i] = new Vector2f(); + } + this.value = value; + } + + Vector2ArrayUniform(int location, Supplier value, int count, ValueUpdateNotifier notifier) { + super(location, notifier); + + this.cachedValue = new Vector2f[count]; + + for (int i = 0; i < count; i++) { + this.cachedValue[i] = new Vector2f(); + } + this.value = value; + + } + + @Override + public void update() { + updateValue(); + + if (notifier != null) { + notifier.setListener(this::updateValue); + } + } + + private void updateValue() { + Vector2f[] newValue = value.get(); + + if (!Arrays.equals(newValue, cachedValue)) { + try (MemoryStack stack = MemoryStack.stackPush()) { + FloatBuffer buffer = stack.mallocFloat(newValue.length * 2); + + int index = 0; + for (int i = 0; i < cachedValue.length; i++) { + cachedValue[i].set(newValue[i]); + newValue[i].get(index, buffer); + index += 2; + } + + GL46C.glUniform2fv(this.location, buffer); + } + } + } +} diff --git a/common/src/main/java/net/irisshaders/iris/mixin/MixinWindow.java b/common/src/main/java/net/irisshaders/iris/mixin/MixinWindow.java index cb2f559d14..d1bdd1bd11 100644 --- a/common/src/main/java/net/irisshaders/iris/mixin/MixinWindow.java +++ b/common/src/main/java/net/irisshaders/iris/mixin/MixinWindow.java @@ -8,12 +8,13 @@ import org.lwjgl.glfw.GLFW; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @Mixin(value = Window.class, priority = 1010) public class MixinWindow { - //@Inject(method = "", at = @At(value = "INVOKE", target = "Lorg/lwjgl/glfw/GLFW;glfwCreateWindow(IILjava/lang/CharSequence;JJ)J")) - @Unique + @Inject(method = "", at = @At(value = "INVOKE", target = "Lorg/lwjgl/glfw/GLFW;glfwCreateWindow(IILjava/lang/CharSequence;JJ)J")) private void iris$enableDebugContext(WindowEventHandler arg, ScreenManager arg2, DisplayData arg3, String string, String string2, CallbackInfo ci) { if (Iris.getIrisConfig().areDebugOptionsEnabled()) { GLFW.glfwWindowHint(GLFW.GLFW_OPENGL_DEBUG_CONTEXT, GLFW.GLFW_TRUE); diff --git a/common/src/main/java/net/irisshaders/iris/pbr/TextureInfoCache.java b/common/src/main/java/net/irisshaders/iris/pbr/TextureInfoCache.java index f9232adcfe..07cb303512 100644 --- a/common/src/main/java/net/irisshaders/iris/pbr/TextureInfoCache.java +++ b/common/src/main/java/net/irisshaders/iris/pbr/TextureInfoCache.java @@ -6,6 +6,7 @@ import net.irisshaders.iris.mixin.GlStateManagerAccessor; import org.jetbrains.annotations.Nullable; import org.lwjgl.opengl.GL20C; +import org.lwjgl.opengl.GL43C; import java.nio.IntBuffer; @@ -37,6 +38,16 @@ public void onTexImage2D(int target, int level, int internalformat, int width, i } } + public void onTexImage(int target, int id, int level, int internalformat, int width, int height) { + if (level == 0) { + TextureInfo info = getInfo(id); + info.internalFormat = internalformat; + info.target = target; + info.width = width; + info.height = height; + } + } + public void onDeleteTexture(int id) { cache.remove(id); } @@ -46,6 +57,7 @@ public static class TextureInfo { private int internalFormat = -1; private int width = -1; private int height = -1; + private int target = GL43C.GL_TEXTURE_2D; private TextureInfo(int id) { this.id = id; @@ -82,7 +94,7 @@ private int fetchLevelParameter(int pname) { // Bind this texture and grab the parameter from it. GlStateManager._bindTexture(id); - int parameter = GlStateManager._getTexLevelParameter(GL20C.GL_TEXTURE_2D, 0, pname); + int parameter = GlStateManager._getTexLevelParameter(target, 0, pname); // Make sure to re-bind the previous texture to avoid issues. GlStateManager._bindTexture(previousTextureBinding); diff --git a/common/src/main/java/net/irisshaders/iris/pipeline/IrisRenderingPipeline.java b/common/src/main/java/net/irisshaders/iris/pipeline/IrisRenderingPipeline.java index 79f3f235a3..7a948e6fa8 100644 --- a/common/src/main/java/net/irisshaders/iris/pipeline/IrisRenderingPipeline.java +++ b/common/src/main/java/net/irisshaders/iris/pipeline/IrisRenderingPipeline.java @@ -683,7 +683,7 @@ private ShaderInstance createShadowShader(String name, Optional s } private ShaderInstance createFallbackShadowShader(String name, ShaderKey key) throws IOException { - GlFramebuffer framebuffer = shadowRenderTargets.createShadowFramebuffer(ImmutableSet.of(), new int[]{0}); + GlFramebuffer framebuffer = shadowRenderTargets.createShadowFramebuffer(ImmutableSet.of(), new int[]{0}, ShadowRenderTargets.TEMP_LAYER); FallbackShader shader = ShaderCreator.createFallback(name, framebuffer, framebuffer, key.getAlphaTest(), key.getVertexFormat(), BlendModeOverride.OFF, this, key.getFogMode(), @@ -696,7 +696,7 @@ private ShaderInstance createFallbackShadowShader(String name, ShaderKey key) th private ShaderInstance createShadowShader(String name, ProgramSource source, ProgramId programId, AlphaTest fallbackAlpha, VertexFormat vertexFormat, boolean isIntensity, boolean isFullbright, boolean isText, boolean isIE) throws IOException { - GlFramebuffer framebuffer = shadowRenderTargets.createShadowFramebuffer(ImmutableSet.of(), source.getDirectives().hasUnknownDrawBuffers() ? new int[]{0, 1} : source.getDirectives().getDrawBuffers()); + GlFramebuffer framebuffer = shadowRenderTargets.createShadowFramebuffer(ImmutableSet.of(), source.getDirectives().hasUnknownDrawBuffers() ? new int[]{0, 1} : source.getDirectives().getDrawBuffers(), ShadowRenderTargets.TEMP_LAYER); boolean isLines = programId == ProgramId.Line && resolver.has(ProgramId.Line); ShaderAttributeInputs inputs = new ShaderAttributeInputs(vertexFormat, isFullbright, isLines, false, isText, isIE); @@ -826,8 +826,10 @@ public void beginLevelRendering() { } } else { // Clear depth first, regardless of any color clearing. - shadowRenderTargets.getDepthSourceFb().bind(); - RenderSystem.clear(GL21C.GL_DEPTH_BUFFER_BIT, Minecraft.ON_OSX); + for (int i = 0; i < ShadowRenderTargets.NUM_CASCADES; i++) { + shadowRenderTargets.getDepthSourceFb()[i].bind(); + RenderSystem.clear(GL21C.GL_DEPTH_BUFFER_BIT, Minecraft.ON_OSX); + } ImmutableList passes; @@ -1251,9 +1253,9 @@ public ImmutableSet getFlippedAfterTranslucent() { return flippedAfterTranslucent; } - public GlFramebuffer createDHFramebufferShadow(ProgramSource sources) { + public GlFramebuffer createDHFramebufferShadow(ProgramSource sources, int layer) { - return shadowRenderTargets.createDHFramebuffer(ImmutableSet.of(), new int[]{0, 1}); + return shadowRenderTargets.createDHFramebuffer(ImmutableSet.of(), new int[]{0, 1}, layer); } public boolean hasShadowRenderTargets() { diff --git a/common/src/main/java/net/irisshaders/iris/pipeline/programs/SodiumPrograms.java b/common/src/main/java/net/irisshaders/iris/pipeline/programs/SodiumPrograms.java index 8f945ec5af..3a5af5a5ea 100644 --- a/common/src/main/java/net/irisshaders/iris/pipeline/programs/SodiumPrograms.java +++ b/common/src/main/java/net/irisshaders/iris/pipeline/programs/SodiumPrograms.java @@ -2,6 +2,8 @@ import com.google.common.collect.ImmutableSet; import com.google.common.primitives.Ints; +import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import net.caffeinemc.mods.sodium.client.gl.GlObject; import net.caffeinemc.mods.sodium.client.gl.shader.GlProgram; import net.caffeinemc.mods.sodium.client.gl.shader.GlShader; @@ -25,6 +27,7 @@ import net.irisshaders.iris.shaderpack.programs.ProgramSet; import net.irisshaders.iris.shaderpack.programs.ProgramSource; import net.irisshaders.iris.shadows.ShadowRenderTargets; +import net.irisshaders.iris.shadows.ShadowRenderer; import net.irisshaders.iris.shadows.ShadowRenderingState; import net.irisshaders.iris.targets.RenderTargets; import net.irisshaders.iris.uniforms.custom.CustomUniforms; @@ -43,6 +46,7 @@ import java.util.function.Supplier; public class SodiumPrograms { + private final GlFramebuffer[] shadowMap = new GlFramebuffer[ShadowRenderTargets.NUM_CASCADES]; private final EnumMap framebuffers = new EnumMap<>(Pass.class); private final EnumMap> shaders = new EnumMap<>(Pass.class); @@ -58,8 +62,16 @@ public SodiumPrograms(IrisRenderingPipeline pipeline, ProgramSet programSet, Pro for (Pass pass : Pass.values()) { ProgramSource source = resolver.resolveNullable(pass.getOriginalId()); Supplier> flipState = getFlipState(pipeline, pass, pass == Pass.SHADOW || pass == Pass.SHADOW_CUTOUT); - GlFramebuffer framebuffer = createFramebuffer(pass, source, shadowRenderTargets, renderTargets, flipState); - framebuffers.put(pass, framebuffer); + if (pass == Pass.SHADOW) { + for (int layer = 0; layer < ShadowRenderTargets.NUM_CASCADES; layer++) { + GlFramebuffer framebuffer = shadowRenderTargets.get().createShadowFramebuffer(ImmutableSet.of(), + source == null ? new int[]{0, 1} : (source.getDirectives().hasUnknownDrawBuffers() ? new int[]{0, 1} : source.getDirectives().getDrawBuffers()), layer); + shadowMap[layer] = framebuffer; + } + } else { + GlFramebuffer framebuffer = createFramebuffer(pass, source, shadowRenderTargets, renderTargets, flipState); + framebuffers.put(pass, framebuffer); + } if (source == null) { continue; @@ -137,7 +149,7 @@ private GlFramebuffer createFramebuffer(Pass pass, ProgramSource source, Supplier> flipState) { if (pass == Pass.SHADOW || pass == Pass.SHADOW_CUTOUT) { return shadowRenderTargets.get().createShadowFramebuffer(ImmutableSet.of(), - source == null ? new int[]{0, 1} : (source.getDirectives().hasUnknownDrawBuffers() ? new int[]{0, 1} : source.getDirectives().getDrawBuffers())); + source == null ? new int[]{0, 1} : (source.getDirectives().hasUnknownDrawBuffers() ? new int[]{0, 1} : source.getDirectives().getDrawBuffers()), ShadowRenderTargets.TEMP_LAYER); } else { return renderTargets.createGbufferFramebuffer(flipState.get(), source == null ? new int[]{0, 1} : (source.getDirectives().hasUnknownDrawBuffers() ? new int[]{0} : source.getDirectives().getDrawBuffers())); } @@ -192,7 +204,7 @@ public GlProgram getProgram(TerrainRenderPass pass) { public GlFramebuffer getFramebuffer(TerrainRenderPass pass) { Pass pass2 = mapTerrainRenderPass(pass); - return this.framebuffers.get(pass2); + return ShadowRenderingState.areShadowsCurrentlyBeingRendered() ? this.shadowMap[ShadowRenderer.CASCADE] : this.framebuffers.get(pass2); } private Pass mapTerrainRenderPass(TerrainRenderPass pass) { diff --git a/common/src/main/java/net/irisshaders/iris/pipeline/programs/SodiumShader.java b/common/src/main/java/net/irisshaders/iris/pipeline/programs/SodiumShader.java index 78328f3627..e1c907d773 100644 --- a/common/src/main/java/net/irisshaders/iris/pipeline/programs/SodiumShader.java +++ b/common/src/main/java/net/irisshaders/iris/pipeline/programs/SodiumShader.java @@ -4,6 +4,7 @@ import com.mojang.blaze3d.platform.GlStateManager; import com.mojang.blaze3d.systems.RenderSystem; import net.caffeinemc.mods.sodium.client.gl.shader.uniform.GlUniformFloat3v; +import net.caffeinemc.mods.sodium.client.gl.shader.uniform.GlUniformInt; import net.caffeinemc.mods.sodium.client.gl.shader.uniform.GlUniformMatrix4f; import net.caffeinemc.mods.sodium.client.render.chunk.shader.ChunkShaderInterface; import net.caffeinemc.mods.sodium.client.render.chunk.shader.ShaderBindingContext; @@ -16,12 +17,14 @@ import net.irisshaders.iris.gl.state.FogMode; import net.irisshaders.iris.pipeline.IrisRenderingPipeline; import net.irisshaders.iris.samplers.IrisSamplers; +import net.irisshaders.iris.shadows.ShadowRenderer; import net.irisshaders.iris.uniforms.CapturedRenderingState; import net.irisshaders.iris.uniforms.CommonUniforms; import net.irisshaders.iris.uniforms.builtin.BuiltinReplacementUniforms; import net.irisshaders.iris.uniforms.custom.CustomUniforms; import net.irisshaders.iris.vertices.ImmediateState; import net.minecraft.client.Minecraft; +import org.jetbrains.annotations.Nullable; import org.joml.Matrix3f; import org.joml.Matrix4f; import org.joml.Matrix4fc; @@ -47,6 +50,7 @@ public class SodiumShader implements ChunkShaderInterface { private final List bufferBlendOverrides; private final float alphaTest; private final boolean containsTessellation; + private final @Nullable GlUniformInt uniformCascade; public SodiumShader(IrisRenderingPipeline pipeline, SodiumPrograms.Pass pass, ShaderBindingContext context, int handle, Optional blendModeOverride, @@ -59,6 +63,7 @@ public SodiumShader(IrisRenderingPipeline pipeline, SodiumPrograms.Pass pass, Sh this.uniformProjectionMatrix = context.bindUniformOptional("iris_ProjectionMatrix", GlUniformMatrix4f::new); this.uniformProjectionMatrixInv = context.bindUniformOptional("iris_ProjectionMatrixInv", GlUniformMatrix4f::new); this.uniformRegionOffset = context.bindUniformOptional("u_RegionOffset", GlUniformFloat3v::new); + this.uniformCascade = context.bindUniformOptional("u_CascadeNum", GlUniformInt::new); this.alphaTest = alphaTest; this.containsTessellation = containsTessellation; @@ -163,6 +168,10 @@ private void applyBlendModes() { } private void updateUniforms() { + if (uniformCascade != null) { + // TODO + uniformCascade.set(ShadowRenderer.CASCADE); + } CapturedRenderingState.INSTANCE.setCurrentAlphaTest(alphaTest); samplers.update(); uniforms.update(); diff --git a/common/src/main/java/net/irisshaders/iris/shadows/NullCascade.java b/common/src/main/java/net/irisshaders/iris/shadows/NullCascade.java new file mode 100644 index 0000000000..c9ab020855 --- /dev/null +++ b/common/src/main/java/net/irisshaders/iris/shadows/NullCascade.java @@ -0,0 +1,185 @@ +package net.irisshaders.iris.shadows; + +import net.irisshaders.iris.uniforms.CapturedRenderingState; +import org.joml.Matrix4f; +import org.joml.Vector2f; +import org.joml.Vector3f; +import org.joml.Vector4f; + +import static net.irisshaders.iris.shadows.ShadowRenderTargets.NUM_CASCADES; + +public class NullCascade { + private static final float[] tile_dist = new float[]{5, 12, 30, 80}; + + private static final float SHADOW_CSM_FIT_FARSCALE = 1.1f; + private static final float SHADOW_CSM_FITSCALE = 0.1f; + private static final Vector3f[] frustum = new Vector3f[]{ + new Vector3f(-1.0f, -1.0f, -1.0f), + new Vector3f(1.0f, -1.0f, -1.0f), + new Vector3f(-1.0f, 1.0f, -1.0f), + new Vector3f(1.0f, 1.0f, -1.0f), + new Vector3f(-1.0f, -1.0f, 1.0f), + new Vector3f(1.0f, -1.0f, 1.0f), + new Vector3f(-1.0f, 1.0f, 1.0f), + new Vector3f(1.0f, 1.0f, 1.0f)}; + + static float GetCascadeDistance(float shadowDistance, float far, final int tile) { + float maxDist = Math.min(shadowDistance, far * SHADOW_CSM_FIT_FARSCALE); + + if (tile == 2) { + return tile_dist[2] + Math.max(maxDist - tile_dist[2], 0.0f) * SHADOW_CSM_FITSCALE; + } else if (tile == 3) { + return maxDist; + } + + return tile_dist[tile]; + } + + private static void SetProjectionRange(Matrix4f projection, float near, float far) { + float scale = far / (far - near); + projection.m22(scale); + projection.m32(-scale * near); + } + + public static void GetFrustumMinMax(Matrix4f matProjection, Vector3f clipMin, Vector3f clipMax) { + + Vector4f temp = new Vector4f(); + Vector3f shadowClipPos = new Vector3f(); + + for (int i = 0; i < 8; i++) { + matProjection.transform(new Vector4f(frustum[i], 1.0f), temp); + unproject(temp, shadowClipPos); + + if (i == 0) { + clipMin.set(shadowClipPos); + clipMax.set(shadowClipPos); + } else { + clipMin.min(shadowClipPos); + clipMax.max(shadowClipPos); + } + } + } + + private static void unproject(Vector4f clipPos, Vector3f result) { + result.x = clipPos.x / clipPos.w; + result.y = clipPos.y / clipPos.w; + result.z = clipPos.z / clipPos.w; + } + + public static Matrix4f GetShadowTileProjectionMatrix(float shadowDistance, float far, float near, Matrix4f shadowModelView, float[] cascadeSizes, int tile, Vector2f shadowViewMin, Vector2f shadowViewMax) { + float tileSize = cascadeSizes[tile]; + float projectionSize = tileSize * 2.0f + 3.0f; + float zNear = -far; + float zFar = far * 2.0f; + + Matrix4f matShadowProjection = ShadowMatrices.createOrthoMatrix(projectionSize, zNear, zFar); + + Matrix4f matSceneProjectionRanged = new Matrix4f(CapturedRenderingState.INSTANCE.getGbufferProjection()); + Matrix4f matSceneModelView = new Matrix4f(CapturedRenderingState.INSTANCE.getGbufferModelView()); + + float rangeNear = tile > 0 ? GetCascadeDistance(shadowDistance, far,tile - 1) : near; + rangeNear = Math.max(rangeNear - 3.0f, near); + float rangeFar = tileSize + 3.0f; + + SetProjectionRange(matSceneProjectionRanged, rangeNear, rangeFar); + + Matrix4f matModelViewProjectionInv = new Matrix4f(); + matSceneProjectionRanged.mul(matSceneModelView, matModelViewProjectionInv).invert(); + + Matrix4f matSceneToShadow = new Matrix4f(); + shadowModelView.mul(matModelViewProjectionInv, matSceneToShadow); + matShadowProjection.mul(matSceneToShadow, matSceneToShadow); + + Vector3f clipMin = new Vector3f(); + Vector3f clipMax = new Vector3f(); + GetFrustumMinMax(matSceneToShadow, clipMin, clipMax); + + clipMin.max(new Vector3f(-1.0f)); + clipMax.min(new Vector3f(1.0f)); + + float viewScale = 2.0f / projectionSize; + shadowViewMin.set(clipMin.x / viewScale, clipMin.y / viewScale); + shadowViewMax.set(clipMax.x / viewScale, clipMax.y / viewScale); + + Vector2f blockPadding = new Vector2f( + 3.0f * matShadowProjection.m00(), + 3.0f * matShadowProjection.m11() + ); + + clipMin.sub(blockPadding.x, blockPadding.y, 0.0f); + clipMax.add(blockPadding.x, blockPadding.y, 0.0f); + + clipMin.max(new Vector3f(-1.0f)); + clipMax.min(new Vector3f(1.0f)); + + Vector2f center = new Vector2f(clipMin.x + clipMax.x, clipMin.y + clipMax.y); + Vector2f scale = new Vector2f(2.0f).div(clipMax.x - clipMin.x, clipMax.y - clipMin.y); + + Matrix4f matProjScale = new Matrix4f().scaling(scale.x, scale.y, 1.0f); + Matrix4f matProjTranslate = new Matrix4f().translation(-center.x, -center.y, 0.0f); + + return matProjScale.mul(matProjTranslate.mul(matShadowProjection, new Matrix4f())); + } + + // tile: 0-3 + private static Vector2f GetShadowTilePos(int tile) { + if (tile < 0) return new Vector2f(10.0f); + + Vector2f pos = new Vector2f(); + pos.x = fract(tile / 2.0f); + pos.y = (float) (Math.floor((float) tile * 0.5f) * 0.5f); + return pos; + } + + private static float fract(float v) { + return (float) (v - Math.floor(v)); + } + + public static CascadeOutput getCascades(Matrix4f modelView, float near, float far, float distance) { + CascadeOutput out = new CascadeOutput(); + + float[] cascadeSizes = new float[4]; + cascadeSizes[0] = GetCascadeDistance(distance, far, 0); + cascadeSizes[1] = GetCascadeDistance(distance, far, 1); + cascadeSizes[2] = GetCascadeDistance(distance, far, 2); + cascadeSizes[3] = GetCascadeDistance(distance, far, 3); + + for (int i = 0; i < NUM_CASCADES; i++) { + out.cascadeSize[i] = cascadeSizes[i]; + out.shadowProjectionPos[i] = GetShadowTilePos(i); + out.cascadeProjection[i] = GetShadowTileProjectionMatrix(distance, far, near, modelView, cascadeSizes, i, out.cascadeViewMin[i], out.cascadeViewMax[i]); + + out.shadowProjectionSize[i] = new Vector2f(2.0f).div(new Vector2f( + out.cascadeProjection[i].m00(), + out.cascadeProjection[i].m11())); + } + + return out; + } + + Vector3f GetCascadePaddedFrustumClipBounds(final Matrix4f matShadowProjection, final float padding) { + return new Vector3f(1.0f + padding).mul(new Vector3f( + matShadowProjection.m00(), + matShadowProjection.m11(), + -matShadowProjection.m22())); + } + + public static class CascadeOutput { + public Matrix4f[] cascadeProjection = new Matrix4f[NUM_CASCADES]; + public float[] cascadeSize = new float[NUM_CASCADES]; + public Vector2f[] shadowProjectionSize = new Vector2f[NUM_CASCADES]; + public Vector2f[] shadowProjectionPos = new Vector2f[NUM_CASCADES]; + public Vector2f[] cascadeViewMin = new Vector2f[NUM_CASCADES]; + public Vector2f[] cascadeViewMax = new Vector2f[NUM_CASCADES]; + + public CascadeOutput() { + for (int i = 0; i < NUM_CASCADES; i++) { + cascadeProjection[i] = new Matrix4f(); + cascadeViewMin[i] = new Vector2f(); + cascadeViewMax[i] = new Vector2f(); + shadowProjectionSize[i] = new Vector2f(); + shadowProjectionPos[i] = new Vector2f(); + } + } + } +} diff --git a/common/src/main/java/net/irisshaders/iris/shadows/ShadowCascade.java b/common/src/main/java/net/irisshaders/iris/shadows/ShadowCascade.java new file mode 100644 index 0000000000..2f19d20240 --- /dev/null +++ b/common/src/main/java/net/irisshaders/iris/shadows/ShadowCascade.java @@ -0,0 +1,320 @@ +package net.irisshaders.iris.shadows; + +import net.irisshaders.iris.uniforms.CapturedRenderingState; +import org.joml.Matrix4f; +import org.joml.Matrix4fc; +import org.joml.Vector3f; +import org.joml.Vector4f; + +public class ShadowCascade { + // Function are derived from Vulkan examples from Sascha Willems, and licensed under the MIT License: + // https://github.com/SaschaWillems/Vulkan/tree/master/examples/shadowmappingcascade, which are based on + // https://johanmedestrom.wordpress.com/2016/03/18/opengl-cascaded-shadow-maps/ + public static Matrix4f[] updateCascadeShadows(Matrix4fc modelView, Matrix4fc projection) { + Matrix4f[] cascades = new Matrix4f[ShadowRenderTargets.NUM_CASCADES]; + float cascadeSplitLambda = 0.95f; + + float[] cascadeSplits = new float[ShadowRenderTargets.NUM_CASCADES]; + + float nearClip = 0.05f; + float farClip = 256.0f; + float clipRange = farClip - nearClip; + + float maxZ = nearClip + clipRange; + + float range = maxZ - nearClip; + float ratio = maxZ / nearClip; + + // Calculate split depths based on view camera frustum + // Based on method presented in https://developer.nvidia.com/gpugems/GPUGems3/gpugems3_ch10.html + for (int i = 0; i < ShadowRenderTargets.NUM_CASCADES; i++) { + float p = (i + 1) / (float) (ShadowRenderTargets.NUM_CASCADES); + float log = (float) (nearClip * java.lang.Math.pow(ratio, p)); + float uniform = nearClip + range * p; + float d = cascadeSplitLambda * (log - uniform) + uniform; + cascadeSplits[i] = (d - nearClip) / clipRange; + } + + // Calculate orthographic projection matrix for each cascade + float lastSplitDist = 0.0f; + for (int i = 0; i < ShadowRenderTargets.NUM_CASCADES; i++) { + float splitDist = cascadeSplits[i]; + + Vector3f[] frustumCorners = new Vector3f[]{ + new Vector3f(-1.0f, 1.0f, -1.0f), + new Vector3f(1.0f, 1.0f, -1.0f), + new Vector3f(1.0f, -1.0f, -1.0f), + new Vector3f(-1.0f, -1.0f, -1.0f), + new Vector3f(-1.0f, 1.0f, 1.0f), + new Vector3f(1.0f, 1.0f, 1.0f), + new Vector3f(1.0f, -1.0f, 1.0f), + new Vector3f(-1.0f, -1.0f, 1.0f), + }; + + // Project frustum corners into world space + Matrix4f matShadowProjection = ShadowMatrices.createOrthoMatrix(ShadowRenderer.halfPlaneLength, 0.05f, 256.0f); + + Matrix4f matModelViewProjectionInv = CapturedRenderingState.INSTANCE.getGbufferProjection().mul(CapturedRenderingState.INSTANCE.getGbufferModelView(), new Matrix4f()).invert(); + Matrix4f invCam = matShadowProjection.mul(modelView.mul(matModelViewProjectionInv, new Matrix4f()), new Matrix4f()); + for (int j = 0; j < 8; j++) { + Vector4f invCorner = new Vector4f(frustumCorners[j], 1.0f).mul(invCam); + frustumCorners[j] = new Vector3f(invCorner.x / invCorner.w, invCorner.y / invCorner.w, invCorner.z / invCorner.w); + } + + for (int j = 0; j < 4; j++) { + Vector3f dist = new Vector3f(frustumCorners[j + 4]).sub(frustumCorners[j]); + frustumCorners[j + 4] = new Vector3f(frustumCorners[j]).add(new Vector3f(dist).mul(splitDist)); + frustumCorners[j] = new Vector3f(frustumCorners[j]).add(new Vector3f(dist).mul(lastSplitDist)); + } + + // Get frustum center + Vector3f frustumCenter = new Vector3f(0.0f); + for (int j = 0; j < 8; j++) { + frustumCenter.add(frustumCorners[j]); + } + frustumCenter.div(8.0f); + + float radius = 0.0f; + for (int j = 0; j < 8; j++) { + float distance = (new Vector3f(frustumCorners[j]).sub(frustumCenter)).length(); + radius = java.lang.Math.max(radius, distance); + } + radius = (float) java.lang.Math.ceil(radius * 16.0f) / 16.0f; + + Vector3f maxExtents = new Vector3f(radius); + Vector3f minExtents = new Vector3f(maxExtents).mul(-1); + + cascades[i] = new Matrix4f().ortho + (minExtents.x, maxExtents.x, minExtents.y, maxExtents.y, 0.05f, maxExtents.z - minExtents.z, true); + + // Store split distance and matrix in cascade + float splitDistance = (nearClip + splitDist * clipRange) * -1.0f; + + lastSplitDist = cascadeSplits[i]; + } + + return cascades; + } + + + + + + /* + // Renders the shadow map for all cascades, and performs VSM conversion if necessary. +// This uses CPU-driven shadow map setup and scene submission + void MeshRenderer::RenderShadowMap(ID3D11DeviceContext* context, final Camera& camera, + final Matrix4f world, final Matrix4f characterWorld) + { + PIXEvent event(L"Mesh Shadow Map Rendering"); + ProfileBlock block(L"Shadow Map Rendering"); + + final int ShadowMapSize = AppSettings::ShadowMapResolution(); + final float sMapSize = static_cast(ShadowMapSize); + + final float MinDistance = AppSettings::AutoComputeDepthBounds ? reductionDepth.x + : AppSettings::MinCascadeDistance; + final float MaxDistance = AppSettings::AutoComputeDepthBounds ? reductionDepth.y + : AppSettings::MaxCascadeDistance; + + // Compute the split distances based on the partitioning mode + float CascadeSplits[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; + + if(AppSettings::PartitionMode == PartitionMode::Manual) + { + CascadeSplits[0] = MinDistance + AppSettings::SplitDistance0 * MaxDistance; + CascadeSplits[1] = MinDistance + AppSettings::SplitDistance1 * MaxDistance; + CascadeSplits[2] = MinDistance + AppSettings::SplitDistance2 * MaxDistance; + CascadeSplits[3] = MinDistance + AppSettings::SplitDistance3 * MaxDistance; + } + else if(AppSettings::PartitionMode == PartitionMode::Logarithmic + || AppSettings::PartitionMode == PartitionMode::PSSM) + { + float lambda = 1.0f; + if(AppSettings::PartitionMode == PartitionMode::PSSM) + lambda = AppSettings::PSSMLambda; + + float nearClip = camera.NearClip(); + float farClip = camera.FarClip(); + float clipRange = farClip - nearClip; + + float minZ = nearClip + MinDistance * clipRange; + float maxZ = nearClip + MaxDistance * clipRange; + + float range = maxZ - minZ; + float ratio = maxZ / minZ; + + for(int i = 0; i < ShadowRenderTargets.NUM_CASCADES; ++i) + { + float p = (i + 1) / (float) ShadowRenderTargets.NUM_CASCADES; + float log = (float) (minZ * Math.pow(ratio, p)); + float uniform = minZ + range * p; + float d = lambda * (log - uniform) + uniform; + CascadeSplits[i] = (d - nearClip) / clipRange; + } + } + + Matrix4f globalShadowMatrix = MakeGlobalShadowMatrix(camera); + meshPSConstants.Data.ShadowMatrix = Matrix4f::Transpose(globalShadowMatrix); + + // Render the meshes to each cascade + for(int cascadeIdx = 0; cascadeIdx < ShadowRenderTargets.NUM_CASCADES; ++cascadeIdx) + { + + // Get the 8 points of the view frustum in world space + Vector3f[] frustumCornersWS = new Vector3f[] + { + new Vector3f(-1.0f, 1.0f, 0.0f), + new Vector3f( 1.0f, 1.0f, 0.0f), + new Vector3f( 1.0f, -1.0f, 0.0f), + new Vector3f(-1.0f, -1.0f, 0.0f), + new Vector3f(-1.0f, 1.0f, 1.0f), + new Vector3f( 1.0f, 1.0f, 1.0f), + new Vector3f( 1.0f, -1.0f, 1.0f), + new Vector3f(-1.0f, -1.0f, 1.0f), + }; + + float prevSplitDist = cascadeIdx == 0 ? MinDistance : CascadeSplits[cascadeIdx - 1]; + float splitDist = CascadeSplits[cascadeIdx]; + + Matrix4f invViewProj = CalculateInverseViewProj(camera); + + for(int i = 0; i < 8; ++i) + frustumCornersWS[i] = Vector3f::Transform(frustumCornersWS[i], invViewProj); + + // Get the corners of the current cascade slice of the view frustum + for(int i = 0; i < 4; ++i) + { + Vector3f cornerRay = frustumCornersWS[i + 4] - frustumCornersWS[i]; + Vector3f nearCornerRay = cornerRay * prevSplitDist; + Vector3f farCornerRay = cornerRay * splitDist; + frustumCornersWS[i + 4] = frustumCornersWS[i] + farCornerRay; + frustumCornersWS[i] = frustumCornersWS[i] + nearCornerRay; + } + + // Calculate the centroid of the view frustum slice + Vector3f frustumCenter = 0.0f; + for(int i = 0; i < 8; ++i) + frustumCenter = frustumCenter + frustumCornersWS[i]; + frustumCenter *= 1.0f / 8.0f; + + // Pick the up vector to use for the light camera + Vector3f upDir = camera.Right(); + + Vector3f minExtents; + Vector3f maxExtents; + if(AppSettings::StabilizeCascades) + { + // This needs to be constant for it to be stable + upDir = Vector3f(0.0f, 1.0f, 0.0f); + + // Calculate the radius of a bounding sphere surrounding the frustum corners + float sphereRadius = 0.0f; + for(int i = 0; i < 8; ++i) + { + float dist = Vector3f::Length(frustumCornersWS[i] - frustumCenter); + sphereRadius = std::max(sphereRadius, dist); + } + + sphereRadius = std::ceil(sphereRadius * 16.0f) / 16.0f; + + maxExtents = Vector3f(sphereRadius, sphereRadius, sphereRadius); + minExtents = -maxExtents; + } + else + { + // Create a temporary view matrix for the light + Vector3f lightCameraPos = frustumCenter; + Vector3f lookAt = frustumCenter - AppSettings::LightDirection; + Matrix4f lightView = new Matrix4f().setLookAtLH(lightCameraPos, lookAt, upDir); + //XMMatrixLookAtLH(lightCameraPos.ToSIMD(), lookAt.ToSIMD(), upDir.ToSIMD()); + + // Calculate an AABB around the frustum corners + Vector3f mins = new Vector3f(Float.MAX_VALUE); + Vector3f maxes = new Vector3f(-Float.MAX_VALUE); + for(int i = 0; i < 8; ++i) + { + Vector3f corner = lightView.transformPosition(frustumCornersWS[i], new Vector3f()); + mins = mins.min(corner); + maxes = maxes.max(corner); + } + + minExtents = mins; + maxExtents = maxes; + + // Adjust the min/max to accommodate the filtering size + float scale = (ShadowMapSize + AppSettings::FixedFilterKernelSize()) / static_cast(ShadowMapSize); + minExtents.x *= scale; + minExtents.y *= scale; + maxExtents.x *= scale; + maxExtents.y *= scale; + } + + Vector3f cascadeExtents = maxExtents - minExtents; + + // Get position of the shadow camera + Vector3f shadowCameraPos = frustumCenter + AppSettings::LightDirection.Value() * -minExtents.z; + + // Come up with a new orthographic camera for the shadow caster + OrthographicCamera shadowCamera(minExtents.x, minExtents.y, maxExtents.x, + maxExtents.y, 0.0f, cascadeExtents.z); + shadowCamera.SetLookAt(shadowCameraPos, frustumCenter, upDir); + + if(AppSettings::StabilizeCascades) + { + // Create the rounding matrix, by projecting the world-space origin and determining + // the fractional offset in texel space + XMMATRIX shadowMatrix = shadowCamera.ViewProjectionMatrix().ToSIMD(); + XMVECTOR shadowOrigin = XMVectorSet(0.0f, 0.0f, 0.0f, 1.0f); + shadowOrigin = XMVector4Transform(shadowOrigin, shadowMatrix); + shadowOrigin = XMVectorScale(shadowOrigin, sMapSize / 2.0f); + + XMVECTOR roundedOrigin = XMVectorRound(shadowOrigin); + XMVECTOR roundOffset = XMVectorSubtract(roundedOrigin, shadowOrigin); + roundOffset = XMVectorScale(roundOffset, 2.0f / sMapSize); + roundOffset = XMVectorSetZ(roundOffset, 0.0f); + roundOffset = XMVectorSetW(roundOffset, 0.0f); + + XMMATRIX shadowProj = shadowCamera.ProjectionMatrix().ToSIMD(); + shadowProj.r[3] = XMVectorAdd(shadowProj.r[3], roundOffset); + shadowCamera.SetProjection(shadowProj); + } + + // Draw the mesh with depth only, using the new shadow camera + RenderDepthCPU(context, shadowCamera, world, characterWorld, true); + + // Apply the scale/offset matrix, which transforms from [-1,1] + // post-projection space to [0,1] UV space + Matrix4f texScaleBias = new Matrix4f(); + texScaleBias.setRow(0, new Vector4f(0.5f, 0.0f, 0.0f, 0.0f)); + texScaleBias.setRow(1, new Vector4f(0.0f, -0.5f, 0.0f, 0.0f)); + texScaleBias.setRow(2, new Vector4f(0.0f, 0.0f, 1.0f, 0.0f)); + texScaleBias.setRow(3, new Vector4f(0.5f, 0.5f, 0.0f, 1.0f)); + XMMATRIX shadowMatrix = shadowCamera.ViewProjectionMatrix().ToSIMD(); + shadowMatrix = XMMatrixMultiply(shadowMatrix, texScaleBias); + + // Store the split distance in terms of view space depth + final float clipDist = camera.FarClip() - camera.NearClip(); + meshPSConstants.Data.CascadeSplits[cascadeIdx] = camera.NearClip() + splitDist * clipDist; + + // Calculate the position of the lower corner of the cascade partition, in the UV space + // of the first cascade partition + Matrix4f invCascadeMat = Matrix4f::Invert(shadowMatrix); + Vector3f cascadeCorner = invCascadeMat.transformPosition(new Vector3f(0.0f, 0.0f, 0.0f)); + cascadeCorner = Vector3f::Transform(cascadeCorner, globalShadowMatrix); + + // Do the same for the upper corner + Vector3f otherCorner = invCascadeMat.transformPosition(new Vector3f(1.0f, 1.0f, 1.0f)); + otherCorner = globalShadowMatrix.transform(otherCorner); + + // Calculate the scale and offset + Vector3f cascadeScale = Vector3f(1.0f, 1.0f, 1.0f) / (otherCorner - cascadeCorner); + meshPSConstants.Data.CascadeOffsets[cascadeIdx] = new Vector4f(-cascadeCorner, 0.0f); + meshPSConstants.Data.CascadeScales[cascadeIdx] = new Vector4f(cascadeScale, 1.0f); + + if(AppSettings::UseFilterableShadows()) + ConvertToVSM(context, cascadeIdx, meshPSConstants.Data.CascadeScales[cascadeIdx].To3D(), + meshPSConstants.Data.CascadeScales[0].To3D()); + } + }*/ +} diff --git a/common/src/main/java/net/irisshaders/iris/shadows/ShadowCompositeRenderer.java b/common/src/main/java/net/irisshaders/iris/shadows/ShadowCompositeRenderer.java index 88ede9881d..56acfc059f 100644 --- a/common/src/main/java/net/irisshaders/iris/shadows/ShadowCompositeRenderer.java +++ b/common/src/main/java/net/irisshaders/iris/shadows/ShadowCompositeRenderer.java @@ -33,6 +33,7 @@ import net.irisshaders.iris.shaderpack.properties.PackRenderTargetDirectives; import net.irisshaders.iris.shaderpack.properties.ProgramDirectives; import net.irisshaders.iris.shaderpack.texture.TextureStage; +import net.irisshaders.iris.targets.ArrayRenderTarget; import net.irisshaders.iris.targets.RenderTarget; import net.irisshaders.iris.uniforms.CommonUniforms; import net.irisshaders.iris.uniforms.FrameUpdateNotifier; @@ -106,7 +107,7 @@ public ShadowCompositeRenderer(WorldRenderingPipeline pipeline, PackDirectives p pass.computes = createComputes(computes[i], flipped, flippedAtLeastOnceSnapshot, renderTargets, holder); int[] drawBuffers = source.getDirectives().hasUnknownDrawBuffers() ? new int[]{0, 1} : source.getDirectives().getDrawBuffers(); - GlFramebuffer framebuffer = renderTargets.createColorFramebuffer(flipped, drawBuffers); + GlFramebuffer framebuffer = renderTargets.createColorFramebuffer(flipped, drawBuffers, ShadowRenderTargets.TEMP_LAYER); pass.stageReadsFromAlt = flipped; pass.framebuffer = framebuffer; @@ -143,7 +144,7 @@ public ShadowCompositeRenderer(WorldRenderingPipeline pipeline, PackDirectives p GlStateManager._glBindFramebuffer(GL30C.GL_READ_FRAMEBUFFER, 0); } - private static void setupMipmapping(net.irisshaders.iris.targets.RenderTarget target, boolean readFromAlt) { + private static void setupMipmapping(net.irisshaders.iris.targets.ArrayRenderTarget target, boolean readFromAlt) { int texture = readFromAlt ? target.getAltTexture() : target.getMainTexture(); // TODO: Only generate the mipmap if a valid mipmap hasn't been generated or if we've written to the buffer @@ -157,11 +158,11 @@ private static void setupMipmapping(net.irisshaders.iris.targets.RenderTarget ta // // Also note that this only applies to one of the two buffers in a render target buffer pair - making it // unlikely that this issue occurs in practice with most shader packs. - IrisRenderSystem.generateMipmaps(texture, GL20C.GL_TEXTURE_2D); - IrisRenderSystem.texParameteri(texture, GL20C.GL_TEXTURE_2D, GL20C.GL_TEXTURE_MIN_FILTER, target.getInternalFormat().getPixelFormat().isInteger() ? GL20C.GL_NEAREST_MIPMAP_NEAREST : GL20C.GL_LINEAR_MIPMAP_LINEAR); + IrisRenderSystem.generateMipmaps(texture, GL30C.GL_TEXTURE_2D_ARRAY); + IrisRenderSystem.texParameteri(texture, GL30C.GL_TEXTURE_2D_ARRAY, GL20C.GL_TEXTURE_MIN_FILTER, target.getInternalFormat().getPixelFormat().isInteger() ? GL20C.GL_NEAREST_MIPMAP_NEAREST : GL20C.GL_LINEAR_MIPMAP_LINEAR); } - private static void resetRenderTarget(RenderTarget target) { + private static void resetRenderTarget(ArrayRenderTarget target) { // Resets the sampling mode of the given render target and then unbinds it to prevent accidental sampling of it // elsewhere. @@ -170,8 +171,8 @@ private static void resetRenderTarget(RenderTarget target) { filter = GL20C.GL_NEAREST; } - IrisRenderSystem.texParameteri(target.getMainTexture(), GL20C.GL_TEXTURE_2D, GL20C.GL_TEXTURE_MIN_FILTER, filter); - IrisRenderSystem.texParameteri(target.getAltTexture(), GL20C.GL_TEXTURE_2D, GL20C.GL_TEXTURE_MIN_FILTER, filter); + IrisRenderSystem.texParameteri(target.getMainTexture(), GL30C.GL_TEXTURE_2D_ARRAY, GL20C.GL_TEXTURE_MIN_FILTER, filter); + IrisRenderSystem.texParameteri(target.getAltTexture(), GL30C.GL_TEXTURE_2D_ARRAY, GL20C.GL_TEXTURE_MIN_FILTER, filter); } public ImmutableSet getFlippedAtLeastOnceFinal() { diff --git a/common/src/main/java/net/irisshaders/iris/shadows/ShadowRenderTargets.java b/common/src/main/java/net/irisshaders/iris/shadows/ShadowRenderTargets.java index f8f931751c..ed9b650a70 100644 --- a/common/src/main/java/net/irisshaders/iris/shadows/ShadowRenderTargets.java +++ b/common/src/main/java/net/irisshaders/iris/shadows/ShadowRenderTargets.java @@ -11,20 +11,26 @@ import net.irisshaders.iris.gl.texture.InternalTextureFormat; import net.irisshaders.iris.pipeline.WorldRenderingPipeline; import net.irisshaders.iris.shaderpack.properties.PackShadowDirectives; +import net.irisshaders.iris.targets.ArrayDepthTexture; +import net.irisshaders.iris.targets.ArrayRenderTarget; import net.irisshaders.iris.targets.DepthTexture; import net.irisshaders.iris.targets.RenderTarget; import org.lwjgl.opengl.GL30C; +import org.lwjgl.opengl.GL43C; import java.util.ArrayList; import java.util.List; public class ShadowRenderTargets { - private final RenderTarget[] targets; + public static final int NUM_CASCADES = 4; + public static final int TEMP_LAYER = 0; + + private final ArrayRenderTarget[] targets; private final PackShadowDirectives shadowDirectives; - private final DepthTexture mainDepth; - private final DepthTexture noTranslucents; - private final GlFramebuffer depthSourceFb; - private final GlFramebuffer noTranslucentsDestFb; + private final ArrayDepthTexture mainDepth; + private final ArrayDepthTexture noTranslucents; + private final GlFramebuffer[] depthSourceFb; + private final GlFramebuffer[] noTranslucentsDestFb; private final boolean[] flipped; private final List ownedFramebuffers; @@ -40,15 +46,15 @@ public class ShadowRenderTargets { public ShadowRenderTargets(WorldRenderingPipeline pipeline, int resolution, PackShadowDirectives shadowDirectives) { this.shadowDirectives = shadowDirectives; this.size = pipeline.hasFeature(FeatureFlags.HIGHER_SHADOWCOLOR) ? PackShadowDirectives.MAX_SHADOW_COLOR_BUFFERS_IRIS : PackShadowDirectives.MAX_SHADOW_COLOR_BUFFERS_OF; - targets = new RenderTarget[size]; + targets = new ArrayRenderTarget[size]; formats = new InternalTextureFormat[size]; flipped = new boolean[size]; hardwareFiltered = new boolean[size]; linearFiltered = new boolean[size]; buffersToBeCleared = new IntArrayList(); - this.mainDepth = new DepthTexture("shadowtex0", resolution, resolution, DepthBufferFormat.DEPTH); - this.noTranslucents = new DepthTexture("shadowtex1", resolution, resolution, DepthBufferFormat.DEPTH); + this.mainDepth = new ArrayDepthTexture("shadowtex0", resolution, resolution, NUM_CASCADES, DepthBufferFormat.DEPTH); + this.noTranslucents = new ArrayDepthTexture("shadowtex1", resolution, resolution, NUM_CASCADES, DepthBufferFormat.DEPTH); this.ownedFramebuffers = new ArrayList<>(); this.resolution = resolution; @@ -62,10 +68,16 @@ public ShadowRenderTargets(WorldRenderingPipeline pipeline, int resolution, Pack // data. Otherwise very weird things can happen. fullClearRequired = true; - this.depthSourceFb = createFramebufferWritingToMain(new int[]{0}); + this.depthSourceFb = new GlFramebuffer[NUM_CASCADES]; + this.noTranslucentsDestFb = new GlFramebuffer[NUM_CASCADES]; + + for (int i = 0; i < NUM_CASCADES; i++) { + this.depthSourceFb[i] = createFramebufferWritingToMain(new int[]{0}, i); - this.noTranslucentsDestFb = createFramebufferWritingToMain(new int[]{0}); - this.noTranslucentsDestFb.addDepthAttachment(this.noTranslucents.getTextureId()); + this.noTranslucentsDestFb[i] = createFramebufferWritingToMain(new int[]{0}, i); + + this.noTranslucentsDestFb[i].addDepthAttachmentLayer(this.noTranslucents.getTextureId(), i); + } this.translucentDepthDirty = true; boolean shouldRefresh = false; @@ -85,7 +97,7 @@ public void destroy() { owned.destroy(); } - for (RenderTarget target : targets) { + for (ArrayRenderTarget target : targets) { if (target != null) { target.destroy(); } @@ -99,7 +111,7 @@ public int getRenderTargetCount() { return targets.length; } - public RenderTarget get(int index) { + public ArrayRenderTarget get(int index) { return targets[index]; } @@ -110,7 +122,7 @@ public RenderTarget get(int index) { * @param index The index of the render target to get * @return The existing or a new render target, if no existing one exists */ - public RenderTarget getOrCreate(int index) { + public ArrayRenderTarget getOrCreate(int index) { if (targets[index] != null) { return targets[index]; } @@ -126,7 +138,7 @@ private void create(int index) { PackShadowDirectives.SamplingSettings settings = shadowDirectives.getColorSamplingSettings().computeIfAbsent(index, i -> new PackShadowDirectives.SamplingSettings()); - targets[index] = RenderTarget.builder().setDimensions(resolution, resolution) + targets[index] = ArrayRenderTarget.builder().setDimensions(resolution, resolution, NUM_CASCADES) .setInternalFormat(settings.getFormat()) .setName("shadowcolor" + index) .setPixelFormat(settings.getFormat().getPixelFormat()).build(); @@ -152,28 +164,43 @@ public int getResolution() { return resolution; } - public DepthTexture getDepthTexture() { + public ArrayDepthTexture getDepthTexture() { return mainDepth; } - public DepthTexture getDepthTextureNoTranslucents() { + public ArrayDepthTexture getDepthTextureNoTranslucents() { return noTranslucents; } - public GlFramebuffer getDepthSourceFb() { + public GlFramebuffer[] getDepthSourceFb() { return depthSourceFb; } public void copyPreTranslucentDepth() { if (translucentDepthDirty) { translucentDepthDirty = false; - IrisRenderSystem.blitFramebuffer(depthSourceFb.getId(), noTranslucentsDestFb.getId(), 0, 0, resolution, resolution, + IrisRenderSystem.blitFramebuffer(depthSourceFb[ShadowRenderTargets.TEMP_LAYER].getId(), noTranslucentsDestFb[ShadowRenderTargets.TEMP_LAYER].getId(), 0, 0, resolution, resolution, 0, 0, resolution, resolution, GL30C.GL_DEPTH_BUFFER_BIT, GL30C.GL_NEAREST); } else { - DepthCopyStrategy.fastest(false).copy(depthSourceFb, mainDepth.getTextureId(), noTranslucentsDestFb, noTranslucents.getTextureId(), - resolution, resolution); + GL43C.glCopyImageSubData( + mainDepth.getTextureId(), + GL43C.GL_TEXTURE_2D_ARRAY, + 0, + 0, + 0, + 0, + noTranslucents.getTextureId(), + GL43C.GL_TEXTURE_2D_ARRAY, + 0, + 0, + 0, + 0, + resolution, + resolution, + NUM_CASCADES + ); } } @@ -185,12 +212,12 @@ public void onFullClear() { fullClearRequired = false; } - public GlFramebuffer createFramebufferWritingToMain(int[] drawBuffers) { - return createFullFramebuffer(false, drawBuffers); + public GlFramebuffer createFramebufferWritingToMain(int[] drawBuffers, int layer) { + return createFullFramebuffer(false, drawBuffers, layer); } - public GlFramebuffer createFramebufferWritingToAlt(int[] drawBuffers) { - return createFullFramebuffer(true, drawBuffers); + public GlFramebuffer createFramebufferWritingToAlt(int[] drawBuffers, int layer) { + return createFullFramebuffer(true, drawBuffers, layer); } private ImmutableSet invert(ImmutableSet base, int[] relevant) { @@ -205,11 +232,11 @@ private ImmutableSet invert(ImmutableSet base, int[] relevant) return inverted.build(); } - private GlFramebuffer createEmptyFramebuffer() { + private GlFramebuffer createEmptyFramebuffer(int layer) { GlFramebuffer framebuffer = new GlFramebuffer(); ownedFramebuffers.add(framebuffer); - framebuffer.addDepthAttachment(mainDepth.getTextureId()); + framebuffer.addDepthAttachmentLayer(mainDepth.getTextureId(), layer); // NB: Before OpenGL 3.0, all framebuffers are required to have a color // attachment no matter what. @@ -219,37 +246,37 @@ private GlFramebuffer createEmptyFramebuffer() { return framebuffer; } - public GlFramebuffer createDHFramebuffer(ImmutableSet stageWritesToAlt, int[] drawBuffers) { + public GlFramebuffer createDHFramebuffer(ImmutableSet stageWritesToAlt, int[] drawBuffers, int layer) { if (drawBuffers.length == 0) { - return createEmptyFramebuffer(); + return createEmptyFramebuffer(layer); } ImmutableSet stageWritesToMain = invert(stageWritesToAlt, drawBuffers); - GlFramebuffer framebuffer = createColorFramebuffer(stageWritesToMain, drawBuffers); + GlFramebuffer framebuffer = createColorFramebuffer(stageWritesToMain, drawBuffers, layer); - framebuffer.addDepthAttachment(mainDepth.getTextureId()); + framebuffer.addDepthAttachmentLayer(mainDepth.getTextureId(), layer); return framebuffer; } - public GlFramebuffer createShadowFramebuffer(ImmutableSet stageWritesToAlt, int[] drawBuffers) { + public GlFramebuffer createShadowFramebuffer(ImmutableSet stageWritesToAlt, int[] drawBuffers, int layer) { if (drawBuffers.length == 0) { - return createEmptyFramebuffer(); + return createEmptyFramebuffer(layer); } ImmutableSet stageWritesToMain = invert(stageWritesToAlt, drawBuffers); - GlFramebuffer framebuffer = createColorFramebuffer(stageWritesToMain, drawBuffers); + GlFramebuffer framebuffer = createColorFramebuffer(stageWritesToMain, drawBuffers, layer); - framebuffer.addDepthAttachment(mainDepth.getTextureId()); + framebuffer.addDepthAttachmentLayer(mainDepth.getTextureId(), layer); return framebuffer; } - private GlFramebuffer createFullFramebuffer(boolean clearsAlt, int[] drawBuffers) { + private GlFramebuffer createFullFramebuffer(boolean clearsAlt, int[] drawBuffers, int layer) { if (drawBuffers.length == 0) { - return createEmptyFramebuffer(); + return createEmptyFramebuffer(layer); } ImmutableSet stageWritesToMain = ImmutableSet.of(); @@ -258,18 +285,18 @@ private GlFramebuffer createFullFramebuffer(boolean clearsAlt, int[] drawBuffers stageWritesToMain = invert(ImmutableSet.of(), drawBuffers); } - return createColorFramebufferWithDepth(stageWritesToMain, drawBuffers); + return createColorFramebufferWithDepth(stageWritesToMain, drawBuffers, layer); } - public GlFramebuffer createColorFramebufferWithDepth(ImmutableSet stageWritesToMain, int[] drawBuffers) { - GlFramebuffer framebuffer = createColorFramebuffer(stageWritesToMain, drawBuffers); + public GlFramebuffer createColorFramebufferWithDepth(ImmutableSet stageWritesToMain, int[] drawBuffers, int layer) { + GlFramebuffer framebuffer = createColorFramebuffer(stageWritesToMain, drawBuffers, layer); - framebuffer.addDepthAttachment(mainDepth.getTextureId()); + framebuffer.addDepthAttachmentLayer(mainDepth.getTextureId(), layer); return framebuffer; } - public GlFramebuffer createColorFramebuffer(ImmutableSet stageWritesToMain, int[] drawBuffers) { + public GlFramebuffer createColorFramebuffer(ImmutableSet stageWritesToMain, int[] drawBuffers, int layer) { if (drawBuffers.length == 0) { throw new IllegalArgumentException("Framebuffer must have at least one color buffer"); } @@ -288,14 +315,14 @@ public GlFramebuffer createColorFramebuffer(ImmutableSet stageWritesToM // Iris.logger.warn("Invalid framebuffer was attempted to be created! Forcing a framebuffer with DRAWBUFFERS 01 for shadow."); ownedFramebuffers.remove(framebuffer); framebuffer.destroy(); - return createColorFramebuffer(stageWritesToMain, new int[]{0, 1}); + return createColorFramebuffer(stageWritesToMain, new int[]{0, 1}, layer); } - RenderTarget target = this.getOrCreate(drawBuffers[i]); + ArrayRenderTarget target = this.getOrCreate(drawBuffers[i]); int textureId = stageWritesToMain.contains(drawBuffers[i]) ? target.getMainTexture() : target.getAltTexture(); - framebuffer.addColorAttachment(i, textureId); + framebuffer.addColorAttachmentLayer(i, textureId, layer); } framebuffer.drawBuffers(actualDrawBuffers); @@ -303,7 +330,7 @@ public GlFramebuffer createColorFramebuffer(ImmutableSet stageWritesToM int status = framebuffer.getStatus(); if (status != GL30C.GL_FRAMEBUFFER_COMPLETE) { - throw new IllegalStateException("Unexpected error while creating framebuffer"); + throw new IllegalStateException("Unexpected error while creating framebuffer (Status: " + status + ")"); } return framebuffer; diff --git a/common/src/main/java/net/irisshaders/iris/shadows/ShadowRenderer.java b/common/src/main/java/net/irisshaders/iris/shadows/ShadowRenderer.java index 04449f4a7b..d74dae1112 100644 --- a/common/src/main/java/net/irisshaders/iris/shadows/ShadowRenderer.java +++ b/common/src/main/java/net/irisshaders/iris/shadows/ShadowRenderer.java @@ -55,14 +55,15 @@ import java.util.Objects; public class ShadowRenderer { + public static int CASCADE; public static boolean ACTIVE = false; public static List visibleBlockEntities; public static int renderDistance; - public static Matrix4f MODELVIEW; + public static Matrix4f MODELVIEW = new Matrix4f(); public static Matrix4f PROJECTION; public static Frustum FRUSTUM; - private final float halfPlaneLength; - private final float nearPlane, farPlane; + public static float halfPlaneLength; + public static float nearPlane, farPlane; private final float voxelDistance; private final float renderDistanceMultiplier; private final float entityShadowDistanceMultiplier; @@ -434,14 +435,11 @@ public void renderShadows(LevelRendererAccessor levelRenderer, Camera playerCame levelRenderer.getLevel().getProfiler().popPush("terrain"); - // Set up our orthographic projection matrix and load it into RenderSystem - Matrix4f shadowProjection; - if (this.fov != null) { - // If FOV is not null, the pack wants a perspective based projection matrix. (This is to support legacy packs) - shadowProjection = ShadowMatrices.createPerspectiveMatrix(this.fov); - } else { - shadowProjection = ShadowMatrices.createOrthoMatrix(halfPlaneLength, nearPlane < 0 ? -DHCompat.getRenderDistance() : nearPlane, farPlane < 0 ? DHCompat.getRenderDistance() : farPlane); - } + NullCascade.CascadeOutput o = NullCascade.getCascades(MODELVIEW, nearPlane, farPlane, halfPlaneLength); + + Matrix4f[] proj = o.cascadeProjection; + + Matrix4f shadowProjection = proj[0]; IrisRenderSystem.setShadowProjection(shadowProjection); @@ -458,10 +456,14 @@ public void renderShadows(LevelRendererAccessor levelRenderer, Camera playerCame // Render all opaque terrain unless pack requests not to if (shouldRenderTerrain) { - levelRenderer.invokeRenderSectionLayer(RenderType.solid(), cameraX, cameraY, cameraZ, MODELVIEW, shadowProjection); - levelRenderer.invokeRenderSectionLayer(RenderType.cutout(), cameraX, cameraY, cameraZ, MODELVIEW, shadowProjection); - levelRenderer.invokeRenderSectionLayer(RenderType.cutoutMipped(), cameraX, cameraY, cameraZ, MODELVIEW, shadowProjection); + for (int e = 0; e < ShadowRenderTargets.NUM_CASCADES; e++) { + ShadowRenderer.CASCADE = e; + levelRenderer.invokeRenderSectionLayer(RenderType.solid(), cameraX, cameraY, cameraZ, MODELVIEW, proj[e]); + levelRenderer.invokeRenderSectionLayer(RenderType.cutout(), cameraX, cameraY, cameraZ, MODELVIEW, proj[e]); + levelRenderer.invokeRenderSectionLayer(RenderType.cutoutMipped(), cameraX, cameraY, cameraZ, MODELVIEW, proj[e]); + } } + ShadowRenderer.CASCADE = 0; // Reset our viewport in case Sodium overrode it RenderSystem.viewport(0, 0, resolution, resolution); @@ -533,8 +535,12 @@ public void renderShadows(LevelRendererAccessor levelRenderer, Camera playerCame // It doesn't matter a ton, since this just means that they won't be sorted in the normal rendering pass. // Just something to watch out for, however... if (shouldRenderTranslucent) { - levelRenderer.invokeRenderSectionLayer(RenderType.translucent(), cameraX, cameraY, cameraZ, MODELVIEW, shadowProjection); + for (int e = 0; e < ShadowRenderTargets.NUM_CASCADES; e++) { + ShadowRenderer.CASCADE = e; + levelRenderer.invokeRenderSectionLayer(RenderType.translucent(), cameraX, cameraY, cameraZ, MODELVIEW, proj[e]); + } } + ShadowRenderer.CASCADE = 0; // Note: Apparently tripwire isn't rendered in the shadow pass. // levelRenderer.invokeRenderChunkLayer(RenderType.tripwire(), modelView, cameraX, cameraY, cameraZ, shadowProjection); diff --git a/common/src/main/java/net/irisshaders/iris/targets/ArrayDepthTexture.java b/common/src/main/java/net/irisshaders/iris/targets/ArrayDepthTexture.java new file mode 100644 index 0000000000..64898eb24f --- /dev/null +++ b/common/src/main/java/net/irisshaders/iris/targets/ArrayDepthTexture.java @@ -0,0 +1,42 @@ +package net.irisshaders.iris.targets; + +import com.mojang.blaze3d.platform.GlStateManager; +import net.irisshaders.iris.gl.GLDebug; +import net.irisshaders.iris.gl.GlResource; +import net.irisshaders.iris.gl.IrisRenderSystem; +import net.irisshaders.iris.gl.texture.DepthBufferFormat; +import org.lwjgl.opengl.GL11C; +import org.lwjgl.opengl.GL13C; +import org.lwjgl.opengl.GL32C; +import org.lwjgl.opengl.GL43C; + +public class ArrayDepthTexture extends GlResource { + public ArrayDepthTexture(String name, int width, int height, int layers, DepthBufferFormat format) { + super(IrisRenderSystem.createTexture(GL32C.GL_TEXTURE_2D_ARRAY)); + int texture = getGlId(); + + resize(width, height, layers, format); + GLDebug.nameObject(GL43C.GL_TEXTURE, texture, name); + + IrisRenderSystem.texParameteri(texture, GL32C.GL_TEXTURE_2D_ARRAY, GL11C.GL_TEXTURE_MIN_FILTER, GL11C.GL_NEAREST); + IrisRenderSystem.texParameteri(texture, GL32C.GL_TEXTURE_2D_ARRAY, GL11C.GL_TEXTURE_MAG_FILTER, GL11C.GL_NEAREST); + IrisRenderSystem.texParameteri(texture, GL32C.GL_TEXTURE_2D_ARRAY, GL11C.GL_TEXTURE_WRAP_S, GL13C.GL_CLAMP_TO_EDGE); + IrisRenderSystem.texParameteri(texture, GL32C.GL_TEXTURE_2D_ARRAY, GL11C.GL_TEXTURE_WRAP_T, GL13C.GL_CLAMP_TO_EDGE); + + GlStateManager._bindTexture(0); + } + + void resize(int width, int height, int layers, DepthBufferFormat format) { + IrisRenderSystem.texImage3D(getTextureId(), GL32C.GL_TEXTURE_2D_ARRAY, 0, format.getGlInternalFormat(), width, height, layers, 0, + format.getGlType(), format.getGlFormat(), null); + } + + public int getTextureId() { + return getGlId(); + } + + @Override + protected void destroyInternal() { + GlStateManager._deleteTexture(getGlId()); + } +} diff --git a/common/src/main/java/net/irisshaders/iris/targets/ArrayRenderTarget.java b/common/src/main/java/net/irisshaders/iris/targets/ArrayRenderTarget.java new file mode 100644 index 0000000000..8bfba2e931 --- /dev/null +++ b/common/src/main/java/net/irisshaders/iris/targets/ArrayRenderTarget.java @@ -0,0 +1,192 @@ +package net.irisshaders.iris.targets; + +import com.mojang.blaze3d.platform.GlStateManager; +import net.irisshaders.iris.gl.GLDebug; +import net.irisshaders.iris.gl.IrisRenderSystem; +import net.irisshaders.iris.gl.texture.InternalTextureFormat; +import net.irisshaders.iris.gl.texture.PixelFormat; +import net.irisshaders.iris.gl.texture.PixelType; +import org.joml.Vector2i; +import org.lwjgl.opengl.GL11C; +import org.lwjgl.opengl.GL13C; +import org.lwjgl.opengl.GL32C; +import org.lwjgl.opengl.GL43C; + +import java.nio.ByteBuffer; +import java.util.Arrays; + +public class ArrayRenderTarget { + private static final ByteBuffer NULL_BUFFER = null; + private final InternalTextureFormat internalFormat; + private final PixelFormat format; + private final PixelType type; + private final int mainTexture; + private final int altTexture; + private int width; + private int height; + private int layers; + private boolean isValid; + + public ArrayRenderTarget(Builder builder) { + this.isValid = true; + + this.internalFormat = builder.internalFormat; + this.format = builder.format; + this.type = builder.type; + + this.width = builder.width; + this.height = builder.height; + this.layers = builder.layers; + + int[] textures = new int[2]; + IrisRenderSystem.createTextures(GL32C.GL_TEXTURE_2D_ARRAY, textures); + + this.mainTexture = textures[0]; + this.altTexture = textures[1]; + + boolean isPixelFormatInteger = builder.internalFormat.getPixelFormat().isInteger(); + setupTexture(mainTexture, builder.width, builder.height, builder.layers, !isPixelFormatInteger); + setupTexture(altTexture, builder.width, builder.height, builder.layers, !isPixelFormatInteger); + + if (builder.name != null) { + GLDebug.nameObject(GL43C.GL_TEXTURE, mainTexture, builder.name + " main"); + GLDebug.nameObject(GL43C.GL_TEXTURE, mainTexture, builder.name + " alt"); + } + + // Clean up after ourselves + // This is strictly defensive to ensure that other buggy code doesn't tamper with our textures + GlStateManager._bindTexture(0); + } + + public static Builder builder() { + return new Builder(); + } + + private void setupTexture(int texture, int width, int height, int layers, boolean allowsLinear) { + resizeTexture(texture, width, height, layers); + + IrisRenderSystem.texParameteri(texture, GL32C.GL_TEXTURE_2D_ARRAY, GL11C.GL_TEXTURE_MIN_FILTER, allowsLinear ? GL11C.GL_LINEAR : GL11C.GL_NEAREST); + IrisRenderSystem.texParameteri(texture, GL32C.GL_TEXTURE_2D_ARRAY, GL11C.GL_TEXTURE_MAG_FILTER, allowsLinear ? GL11C.GL_LINEAR : GL11C.GL_NEAREST); + IrisRenderSystem.texParameteri(texture, GL32C.GL_TEXTURE_2D_ARRAY, GL11C.GL_TEXTURE_WRAP_S, GL13C.GL_CLAMP_TO_EDGE); + IrisRenderSystem.texParameteri(texture, GL32C.GL_TEXTURE_2D_ARRAY, GL11C.GL_TEXTURE_WRAP_T, GL13C.GL_CLAMP_TO_EDGE); + } + + private void resizeTexture(int texture, int width, int height, int layers) { + IrisRenderSystem.texImage3D(texture, GL32C.GL_TEXTURE_2D_ARRAY, 0, internalFormat.getGlFormat(), width, height, layers, 0, format.getGlFormat(), type.getGlFormat(), NULL_BUFFER); + } + + void resize(Vector2i textureScaleOverride) { + this.resize(textureScaleOverride.x, textureScaleOverride.y, layers); + } + + // Package private, call CompositeRenderTargets#resizeIfNeeded instead. + void resize(int width, int height, int layers) { + requireValid(); + + this.width = width; + this.height = height; + + resizeTexture(mainTexture, width, height, layers); + + resizeTexture(altTexture, width, height, layers); + } + + public InternalTextureFormat getInternalFormat() { + return internalFormat; + } + + public int getMainTexture() { + requireValid(); + + return mainTexture; + } + + public int getAltTexture() { + requireValid(); + + return altTexture; + } + + public int getWidth() { + return width; + } + + public int getHeight() { + return height; + } + + public void destroy() { + requireValid(); + isValid = false; + + GlStateManager._deleteTextures(new int[]{mainTexture, altTexture}); + } + + private void requireValid() { + if (!isValid) { + throw new IllegalStateException("Attempted to use a deleted composite render target"); + } + } + + public static class Builder { + private InternalTextureFormat internalFormat = InternalTextureFormat.RGBA8; + private int width = 0; + private int height = 0; + private int layers = 0; + private PixelFormat format = PixelFormat.RGBA; + private PixelType type = PixelType.UNSIGNED_BYTE; + private String name = null; + + private Builder() { + // No-op + } + + public Builder setName(String name) { + this.name = name; + + return this; + } + + public Builder setInternalFormat(InternalTextureFormat format) { + this.internalFormat = format; + + return this; + } + + public Builder setDimensions(int width, int height, int layers) { + if (width <= 0) { + throw new IllegalArgumentException("Width must be greater than zero"); + } + + if (height <= 0) { + throw new IllegalArgumentException("Height must be greater than zero"); + } + + if (layers <= 0) { + throw new IllegalArgumentException("Layer count must be greater than zero"); + } + + this.width = width; + this.height = height; + this.layers = layers; + + return this; + } + + public Builder setPixelFormat(PixelFormat pixelFormat) { + this.format = pixelFormat; + + return this; + } + + public Builder setPixelType(PixelType pixelType) { + this.type = pixelType; + + return this; + } + + public ArrayRenderTarget build() { + return new ArrayRenderTarget(this); + } + } +} diff --git a/common/src/main/java/net/irisshaders/iris/targets/ClearPassCreator.java b/common/src/main/java/net/irisshaders/iris/targets/ClearPassCreator.java index a612200345..0f0c669b91 100644 --- a/common/src/main/java/net/irisshaders/iris/targets/ClearPassCreator.java +++ b/common/src/main/java/net/irisshaders/iris/targets/ClearPassCreator.java @@ -117,12 +117,14 @@ public static ImmutableList createShadowClearPasses(ShadowRenderTarge startIndex++; } - // No need to clear the depth buffer, since we're using Minecraft's depth buffer. - clearPasses.add(new ClearPass(clearColor, renderTargets::getResolution, renderTargets::getResolution, - renderTargets.createFramebufferWritingToAlt(clearBuffers), GL21C.GL_COLOR_BUFFER_BIT)); + for (int layer = 0; layer < ShadowRenderTargets.NUM_CASCADES; layer++) { + // No need to clear the depth buffer, since we're using Minecraft's depth buffer. + clearPasses.add(new ClearPass(clearColor, renderTargets::getResolution, renderTargets::getResolution, + renderTargets.createFramebufferWritingToAlt(clearBuffers, layer), GL21C.GL_COLOR_BUFFER_BIT)); - clearPasses.add(new ClearPass(clearColor, renderTargets::getResolution, renderTargets::getResolution, - renderTargets.createFramebufferWritingToMain(clearBuffers), GL21C.GL_COLOR_BUFFER_BIT)); + clearPasses.add(new ClearPass(clearColor, renderTargets::getResolution, renderTargets::getResolution, + renderTargets.createFramebufferWritingToMain(clearBuffers, layer), GL21C.GL_COLOR_BUFFER_BIT)); + } } }); diff --git a/common/src/main/java/net/irisshaders/iris/uniforms/CameraUniforms.java b/common/src/main/java/net/irisshaders/iris/uniforms/CameraUniforms.java index 02b3d8823e..022481a344 100644 --- a/common/src/main/java/net/irisshaders/iris/uniforms/CameraUniforms.java +++ b/common/src/main/java/net/irisshaders/iris/uniforms/CameraUniforms.java @@ -34,7 +34,7 @@ public static void addCameraUniforms(UniformHolder uniforms, FrameUpdateNotifier .uniform3f(PER_FRAME, "previousCameraPositionFract", () -> getCameraPositionFract(tracker.getPreviousCameraPositionUnshifted())); } - private static int getRenderDistanceInBlocks() { + public static int getRenderDistanceInBlocks() { // TODO: Should we ask the game renderer for this? return client.options.getEffectiveRenderDistance() * 16; } diff --git a/common/src/main/java/net/irisshaders/iris/uniforms/CommonUniforms.java b/common/src/main/java/net/irisshaders/iris/uniforms/CommonUniforms.java index 8d682497b8..d82d49ee72 100644 --- a/common/src/main/java/net/irisshaders/iris/uniforms/CommonUniforms.java +++ b/common/src/main/java/net/irisshaders/iris/uniforms/CommonUniforms.java @@ -122,7 +122,7 @@ public static void addNonDynamicUniforms(UniformHolder uniforms, IdMap idMap, Pa new CelestialUniforms(directives.getSunPathRotation()).addCelestialUniforms(uniforms); IrisExclusiveUniforms.addIrisExclusiveUniforms(uniforms); IrisTimeUniforms.addTimeUniforms(uniforms); - MatrixUniforms.addMatrixUniforms(uniforms, directives); + MatrixUniforms.addMatrixUniforms(uniforms, directives, updateNotifier); IdMapUniforms.addIdMapUniforms(updateNotifier, uniforms, idMap, directives.isOldHandLight()); CommonUniforms.generalCommonUniforms(uniforms, updateNotifier, directives); } diff --git a/common/src/main/java/net/irisshaders/iris/uniforms/MatrixUniforms.java b/common/src/main/java/net/irisshaders/iris/uniforms/MatrixUniforms.java index 1b7cea1f8b..ecf5da5af2 100644 --- a/common/src/main/java/net/irisshaders/iris/uniforms/MatrixUniforms.java +++ b/common/src/main/java/net/irisshaders/iris/uniforms/MatrixUniforms.java @@ -3,7 +3,9 @@ import net.irisshaders.iris.compat.dh.DHCompat; import net.irisshaders.iris.gl.uniform.UniformHolder; import net.irisshaders.iris.shaderpack.properties.PackDirectives; +import net.irisshaders.iris.shadows.NullCascade; import net.irisshaders.iris.shadows.ShadowMatrices; +import net.irisshaders.iris.shadows.ShadowRenderTargets; import net.irisshaders.iris.shadows.ShadowRenderer; import org.joml.Matrix4f; import org.joml.Matrix4fc; @@ -16,13 +18,13 @@ public final class MatrixUniforms { private MatrixUniforms() { } - public static void addMatrixUniforms(UniformHolder uniforms, PackDirectives directives) { + public static void addMatrixUniforms(UniformHolder uniforms, PackDirectives directives, FrameUpdateNotifier updateNotifier) { addMatrix(uniforms, "ModelView", CapturedRenderingState.INSTANCE::getGbufferModelView); addMatrix(uniforms, "Projection", CapturedRenderingState.INSTANCE::getGbufferProjection); addDHMatrix(uniforms, "Projection", DHCompat::getProjection); addShadowMatrix(uniforms, "ModelView", () -> new Matrix4f(ShadowRenderer.createShadowModelView(directives.getSunPathRotation(), directives.getShadowDirectives().getIntervalSize()).last().pose())); - addShadowMatrix(uniforms, "Projection", () -> ShadowMatrices.createOrthoMatrix(directives.getShadowDirectives().getDistance(), + addShadowMatrixProj(uniforms, updateNotifier, "Projection", () -> ShadowMatrices.createOrthoMatrix(directives.getShadowDirectives().getDistance(), directives.getShadowDirectives().getNearPlane() < 0 ? -DHCompat.getRenderDistance() : directives.getShadowDirectives().getNearPlane(), directives.getShadowDirectives().getFarPlane() < 0 ? DHCompat.getRenderDistance() : directives.getShadowDirectives().getFarPlane())); } @@ -47,6 +49,35 @@ private static void addShadowMatrix(UniformHolder uniforms, String name, Supplie .uniformMatrix(PER_FRAME, "shadow" + name + "Inverse", new Inverted(supplier)); } + private static NullCascade.CascadeOutput out; + + private static void addShadowMatrixProj(UniformHolder uniforms, FrameUpdateNotifier updateNotifier, String name, Supplier supplier) { + updateNotifier.addListener(() -> { + out = NullCascade.getCascades(ShadowRenderer.MODELVIEW, ShadowRenderer.nearPlane, ShadowRenderer.farPlane, ShadowRenderer.halfPlaneLength); + }); + + uniforms + .uniformMatrixArray(PER_FRAME, "shadow" + name, ShadowRenderTargets.NUM_CASCADES, () -> { + return out.cascadeProjection; + }) + .uniform1fArray(PER_FRAME, "cascadeSize", ShadowRenderTargets.NUM_CASCADES, () -> { + return out.cascadeSize; + }) + .uniform2fArray(PER_FRAME, "shadowProjectionSize", ShadowRenderTargets.NUM_CASCADES, () -> { + return out.shadowProjectionSize; + }) + .uniform2fArray(PER_FRAME, "shadowProjectionPos", ShadowRenderTargets.NUM_CASCADES, () -> { + return out.shadowProjectionPos; + }) + .uniform2fArray(PER_FRAME, "cascadeViewMin", ShadowRenderTargets.NUM_CASCADES, () -> { + return out.cascadeViewMin; + }) + .uniform2fArray(PER_FRAME, "cascadeViewMax", ShadowRenderTargets.NUM_CASCADES, () -> { + return out.cascadeViewMax; + }) + .uniformMatrix(PER_FRAME, "shadow" + name + "Inverse", new Inverted(supplier)); + } + private record Inverted(Supplier parent) implements Supplier { @Override public Matrix4fc get() { diff --git a/common/src/main/java/net/irisshaders/iris/uniforms/custom/CustomUniformFixedInputUniformsHolder.java b/common/src/main/java/net/irisshaders/iris/uniforms/custom/CustomUniformFixedInputUniformsHolder.java index 0f9186df08..415ab7f34a 100644 --- a/common/src/main/java/net/irisshaders/iris/uniforms/custom/CustomUniformFixedInputUniformsHolder.java +++ b/common/src/main/java/net/irisshaders/iris/uniforms/custom/CustomUniformFixedInputUniformsHolder.java @@ -10,14 +10,18 @@ import net.irisshaders.iris.gl.uniform.UniformUpdateFrequency; import net.irisshaders.iris.uniforms.custom.cached.BooleanCachedUniform; import net.irisshaders.iris.uniforms.custom.cached.CachedUniform; +import net.irisshaders.iris.uniforms.custom.cached.Float2VectorArrayCachedUniform; import net.irisshaders.iris.uniforms.custom.cached.Float2VectorCachedUniform; import net.irisshaders.iris.uniforms.custom.cached.Float3VectorCachedUniform; import net.irisshaders.iris.uniforms.custom.cached.Float4MatrixCachedUniform; import net.irisshaders.iris.uniforms.custom.cached.Float4VectorCachedUniform; +import net.irisshaders.iris.uniforms.custom.cached.FloatArrayCachedUniform; import net.irisshaders.iris.uniforms.custom.cached.FloatCachedUniform; import net.irisshaders.iris.uniforms.custom.cached.Int2VectorCachedUniform; import net.irisshaders.iris.uniforms.custom.cached.Int3VectorCachedUniform; import net.irisshaders.iris.uniforms.custom.cached.IntCachedUniform; +import net.irisshaders.iris.uniforms.custom.cached.MatrixArrayUniform; +import org.apache.commons.lang3.ArrayUtils; import org.joml.Matrix4f; import org.joml.Matrix4fc; import org.joml.Vector2f; @@ -84,6 +88,11 @@ public Builder uniform1f(UniformUpdateFrequency updateFrequency, String name, Fl return this.put(name, new FloatCachedUniform(name, updateFrequency, value)); } + @Override + public Builder uniform1fArray(UniformUpdateFrequency updateFrequency, String name, int count, Supplier value) { + return this.put(name, new FloatArrayCachedUniform(name, updateFrequency, count, value)); + } + @Override public Builder uniform1f(UniformUpdateFrequency updateFrequency, String name, IntSupplier value) { return this.put(name, new FloatCachedUniform(name, updateFrequency, value::getAsInt)); @@ -109,6 +118,11 @@ public Builder uniform2f(UniformUpdateFrequency updateFrequency, String name, Su return this.put(name, new Float2VectorCachedUniform(name, updateFrequency, value)); } + @Override + public UniformHolder uniform2fArray(UniformUpdateFrequency updateFrequency, String name, int count, Supplier value) { + return this.put(name, new Float2VectorArrayCachedUniform(name, updateFrequency, count, value)); + } + @Override public Builder uniform2i(UniformUpdateFrequency updateFrequency, String name, Supplier value) { return this.put(name, new Int2VectorCachedUniform(name, updateFrequency, value)); @@ -171,14 +185,8 @@ public UniformHolder uniformMatrix( } @Override - public UniformHolder uniformMatrixFromArray( - UniformUpdateFrequency updateFrequency, String name, Supplier value) { - Matrix4f held = new Matrix4f(); - - return this.put(name, new Float4MatrixCachedUniform(name, updateFrequency, () -> { - held.set(value.get()); - return held; - })); + public UniformHolder uniformMatrixArray(UniformUpdateFrequency updateFrequency, String name, int count, Supplier value) { + return this.put(name, new MatrixArrayUniform(name, count, updateFrequency, value)); } @Override diff --git a/common/src/main/java/net/irisshaders/iris/uniforms/custom/cached/Float2VectorArrayCachedUniform.java b/common/src/main/java/net/irisshaders/iris/uniforms/custom/cached/Float2VectorArrayCachedUniform.java new file mode 100644 index 0000000000..632bf7bddc --- /dev/null +++ b/common/src/main/java/net/irisshaders/iris/uniforms/custom/cached/Float2VectorArrayCachedUniform.java @@ -0,0 +1,53 @@ +package net.irisshaders.iris.uniforms.custom.cached; + +import net.irisshaders.iris.gl.uniform.UniformUpdateFrequency; +import net.irisshaders.iris.parsing.VectorType; +import org.joml.Vector2f; +import org.lwjgl.opengl.GL21; +import org.lwjgl.opengl.GL46C; +import org.lwjgl.system.MemoryStack; + +import java.nio.FloatBuffer; +import java.util.function.Supplier; + +public class Float2VectorArrayCachedUniform extends VectorCachedUniform { + + public Float2VectorArrayCachedUniform(String name, UniformUpdateFrequency updateFrequency, int count, Supplier supplier) { + super(name, updateFrequency, createArray(count), supplier); + } + + private static Vector2f[] createArray(int count) { + Vector2f[] array = new Vector2f[count]; + + for (int i = 0; i < count; i++) { + array[i] = new Vector2f(); + } + + return array; + } + + @Override + protected void setFrom(Vector2f[] other) { + System.arraycopy(other, 0, this.cached, 0, this.cached.length); + } + + @Override + public void push(int location) { + try (MemoryStack stack = MemoryStack.stackPush()) { + FloatBuffer buffer = stack.mallocFloat(cached.length * 2); + + int index = 0; + for (int i = 0; i < cached.length; i++) { + cached[i].get(index, buffer); + index += 2; + } + + GL46C.glUniform2fv(location, buffer); + } + } + + @Override + public VectorType getType() { + return VectorType.VEC2; + } +} diff --git a/common/src/main/java/net/irisshaders/iris/uniforms/custom/cached/FloatArrayCachedUniform.java b/common/src/main/java/net/irisshaders/iris/uniforms/custom/cached/FloatArrayCachedUniform.java new file mode 100644 index 0000000000..791141f127 --- /dev/null +++ b/common/src/main/java/net/irisshaders/iris/uniforms/custom/cached/FloatArrayCachedUniform.java @@ -0,0 +1,43 @@ +package net.irisshaders.iris.uniforms.custom.cached; + +import kroppeb.stareval.function.FunctionReturn; +import kroppeb.stareval.function.Type; +import net.irisshaders.iris.gl.uniform.FloatSupplier; +import net.irisshaders.iris.gl.uniform.UniformUpdateFrequency; +import org.lwjgl.opengl.GL21; + +import java.util.function.Supplier; + +public class FloatArrayCachedUniform extends CachedUniform { + + final private Supplier supplier; + private float[] cached; + + public FloatArrayCachedUniform(String name, UniformUpdateFrequency updateFrequency, int count, Supplier supplier) { + super(name, updateFrequency); + this.supplier = supplier; + this.cached = new float[count]; + } + + @Override + protected boolean doUpdate() { + float[] prev = this.cached; + System.arraycopy(this.supplier.get(), 0, this.cached, 0, this.cached.length); + return prev != cached; + } + + @Override + public void push(int location) { + GL21.glUniform1fv(location, this.cached); + } + + @Override + public void writeTo(FunctionReturn functionReturn) { + functionReturn.objectReturn = this.cached; + } + + @Override + public Type getType() { + return Type.Float; + } +} diff --git a/common/src/main/java/net/irisshaders/iris/uniforms/custom/cached/MatrixArrayUniform.java b/common/src/main/java/net/irisshaders/iris/uniforms/custom/cached/MatrixArrayUniform.java new file mode 100644 index 0000000000..63635a3ae5 --- /dev/null +++ b/common/src/main/java/net/irisshaders/iris/uniforms/custom/cached/MatrixArrayUniform.java @@ -0,0 +1,57 @@ +package net.irisshaders.iris.uniforms.custom.cached; + +import net.irisshaders.iris.gl.uniform.UniformUpdateFrequency; +import net.irisshaders.iris.parsing.MatrixType; +import net.irisshaders.iris.shadows.ShadowRenderTargets; +import org.joml.Matrix4f; +import org.joml.Matrix4fc; +import org.lwjgl.opengl.GL21; +import org.lwjgl.opengl.GL46C; +import org.lwjgl.system.MemoryStack; + +import java.util.function.Supplier; + +public class MatrixArrayUniform extends VectorCachedUniform { + public MatrixArrayUniform(String name, int count, UniformUpdateFrequency updateFrequency, Supplier supplier) { + super(name, updateFrequency, createMatrixArray(count), supplier); + } + + private static Matrix4fc[] createMatrixArray(int count) { + Matrix4fc[] matrices = new Matrix4fc[count]; + + for (int i = 0; i < count; i++) { + matrices[i] = new Matrix4f(); + } + + return matrices; + } + + @Override + protected void setFrom(Matrix4fc[] other) { + for (int i = 0; i < other.length; i++) { + ((Matrix4f) this.cached[i]).set(other[i]); + } + } + + @Override + public void push(int location) { + // `gets` the values from the matrix and put's them into a buffer + try (MemoryStack stack = MemoryStack.stackPush()) { + long buffer = stack.nmalloc(64 * cached.length); + + int offset = 0; + + for (Matrix4fc matrix : cached) { + matrix.getToAddress(buffer + offset); + offset += 64; + } + + GL46C.nglUniformMatrix4fv(location, cached.length, false, buffer); + } + } + + @Override + public MatrixType getType() { + return MatrixType.MAT4; + } +} diff --git a/fabric/build.gradle.kts b/fabric/build.gradle.kts index 905cffab98..b534a9db68 100644 --- a/fabric/build.gradle.kts +++ b/fabric/build.gradle.kts @@ -83,6 +83,7 @@ loom { ideConfigGenerated(true) runDir("run") environmentVariable("LD_PRELOAD", "/usr/lib/librenderdoc.so") + //vmArgs("-Dorg.lwjgl.util.Debug=true", "-Dorg.lwjgl.util.DebugAllocator=true") } named("server") { server()