Custom images, textures, SSBO, and ShaderPack/ShaderProperties backports

This commit is contained in:
Jason Mitchell authored and Jason Mitchell committed 2026-01-22 16:32:15 -08:00
1 parent 6d60477b93
commit bb52476d27
54 files changed
+2210 -494

No files matched your search

+1
View File
@@ -36,3 +36,4 @@ addon.local.gradle
addon.local.gradle.kts
addon.late.local.gradle
addon.late.local.gradle.kts
local/
@@ -421,6 +421,7 @@ public class GLStateManager {
public static void init() {
RenderSystem.initRenderer();
IrisSamplers.initRenderer();
if(BYPASS_CACHE) {
LOGGER.info("GLStateManager cache bypassed");
@@ -10,14 +10,23 @@ import org.jetbrains.annotations.Nullable;
import org.joml.Matrix4f;
import org.joml.Vector3i;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.ARBBufferStorage;
import org.lwjgl.opengl.ARBClearBufferObject;
import org.lwjgl.opengl.ARBClearTexture;
import org.lwjgl.opengl.ARBShaderImageLoadStore;
import org.lwjgl.opengl.ARBShaderStorageBufferObject;
import org.lwjgl.opengl.EXTShaderImageLoadStore;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL12;
import org.lwjgl.opengl.GL15;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GL30;
import org.lwjgl.opengl.GL33;
import org.lwjgl.opengl.GL40;
import org.lwjgl.opengl.GL42;
import org.lwjgl.opengl.GL43;
import org.lwjgl.opengl.GL44;
import org.lwjgl.opengl.NVXGpuMemoryInfo;
import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
@@ -31,6 +40,16 @@ public class RenderSystem {
private static final Logger LOGGER = LogManager.getLogger("RenderSystem");
private static DSAAccess dsaState;
private static boolean supportsCompute;
private static boolean supportsImageLoadStore;
private static boolean supportsSSBO;
private static boolean supportsBufferStorage;
private static boolean supportsClearTexture;
private static boolean supportsSamplerObjects;
private static int maxImageUnits;
private static int maxSSBOBindings;
// Sampler object state tracking (null if unsupported)
private static int[] samplers;
private RenderSystem() {}
@@ -54,6 +73,46 @@ public class RenderSystem {
}
supportsCompute = supportsCompute();
supportsImageLoadStore = GLStateManager.capabilities.OpenGL42 || GLStateManager.capabilities.GL_ARB_shader_image_load_store || GLStateManager.capabilities.GL_EXT_shader_image_load_store;
supportsSSBO = GLStateManager.capabilities.OpenGL43 || GLStateManager.capabilities.GL_ARB_shader_storage_buffer_object;
supportsBufferStorage = GLStateManager.capabilities.OpenGL44 || GLStateManager.capabilities.GL_ARB_buffer_storage;
supportsClearTexture = GLStateManager.capabilities.OpenGL44 || GLStateManager.capabilities.GL_ARB_clear_texture;
// Cache maximum image units
if (supportsImageLoadStore) {
if (GLStateManager.capabilities.OpenGL42) {
maxImageUnits = GL11.glGetInteger(GL42.GL_MAX_IMAGE_UNITS);
} else if (GLStateManager.capabilities.GL_ARB_shader_image_load_store) {
maxImageUnits = GL11.glGetInteger(ARBShaderImageLoadStore.GL_MAX_IMAGE_UNITS);
} else {
maxImageUnits = GL11.glGetInteger(EXTShaderImageLoadStore.GL_MAX_IMAGE_UNITS_EXT);
}
} else {
maxImageUnits = 0;
}
// Cache maximum SSBO bindings
if (supportsSSBO) {
if (GLStateManager.capabilities.OpenGL43) {
maxSSBOBindings = GL11.glGetInteger(GL43.GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS);
} else {
maxSSBOBindings = GL11.glGetInteger(ARBShaderStorageBufferObject.GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS);
}
} else {
maxSSBOBindings = 0;
}
// Check for sampler objects support (GL 3.3+ or ARB extension)
supportsSamplerObjects = GLStateManager.capabilities.OpenGL33
|| GLStateManager.capabilities.GL_ARB_sampler_objects;
if (supportsSamplerObjects) {
samplers = new int[GL11.glGetInteger(GL20.GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS)];
}
LOGGER.info("Image Load/Store: {}, Max Image Units: {}", supportsImageLoadStore, maxImageUnits);
LOGGER.info("SSBO: {}, Max SSBO Bindings: {}", supportsSSBO, maxSSBOBindings);
LOGGER.info("Buffer Storage: {}, Clear Texture: {}, Sampler Objects: {}", supportsBufferStorage, supportsClearTexture, supportsSamplerObjects);
}
public static void generateMipmaps(int texture, int mipmapTarget) {
@@ -65,8 +124,8 @@ public class RenderSystem {
}
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) {
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, texture);
GLStateManager.glTexImage2D(target, level, internalformat, width, height, border, format, type, pixels);
GL11.glBindTexture(target, texture);
GL11.glTexImage2D(target, level, internalformat, width, height, border, format, type, pixels);
}
public static void uniformMatrix4fv(int location, boolean transpose, FloatBuffer matrix) {
@@ -147,6 +206,17 @@ public class RenderSystem {
dsaState.textureSubImage2D(texture, target, level, xoffset, yoffset, width, height, format, type, pixels);
}
public static void texImage1D(int texture, int target, int level, int internalformat, int width, int border, int format, int type, ByteBuffer pixels) {
GL11.glBindTexture(target, texture);
GL11.glTexImage1D(target, level, internalformat, width, border, format, type, pixels);
}
public static void texImage3D(int texture, int target, int level, int internalformat, int width, int height, int depth, int border, int format, int type, ByteBuffer pixels) {
GL11.glBindTexture(target, texture);
GL12.glTexImage3D(target, level, internalformat, width, height, depth, border, format, type, pixels);
}
public static String getProgramInfoLog(int program) {
return GL20.glGetProgramInfoLog(program, GL20.glGetProgrami(program, GL20.GL_INFO_LOG_LENGTH));
}
@@ -208,13 +278,27 @@ public class RenderSystem {
}
public static int getMaxImageUnits() {
if (GLStateManager.capabilities.OpenGL42) {
return GL11.glGetInteger(GL42.GL_MAX_IMAGE_UNITS);
} else if (GLStateManager.capabilities.GL_EXT_shader_image_load_store) {
return GL11.glGetInteger(EXTShaderImageLoadStore.GL_MAX_IMAGE_UNITS_EXT);
} else {
return 0;
}
return maxImageUnits;
}
public static boolean supportsImageLoadStore() {
return supportsImageLoadStore;
}
public static boolean supportsSSBO() {
return supportsSSBO;
}
public static boolean supportsBufferStorage() {
return supportsBufferStorage;
}
public static boolean supportsClearTexture() {
return supportsClearTexture;
}
public static int getMaxSSBOBindings() {
return maxSSBOBindings;
}
public static void getProgramiv(int program, int value, IntBuffer storage) {
@@ -229,6 +313,14 @@ public class RenderSystem {
GL43.glDispatchCompute(workGroups.x, workGroups.y, workGroups.z);
}
public static void dispatchComputeIndirect(long offset) {
GL43.glDispatchComputeIndirect(offset);
}
public static void bindBuffer(int target, int buffer) {
GL15.glBindBuffer(target, buffer);
}
public static void memoryBarrier(int barriers) {
if (supportsCompute) {
GL42.glMemoryBarrier(barriers);
@@ -255,6 +347,10 @@ public class RenderSystem {
dsaState.bindTextureToUnit(unit, texture);
}
public static void bindTextureToUnit(int target, int unit, int texture) {
dsaState.bindTextureToUnit(target, unit, texture);
}
public static final FloatBuffer PROJECTION_MATRIX_BUFFER = BufferUtils.createFloatBuffer(16);
public static void setupProjectionMatrix(Matrix4f matrix) {
GLStateManager.glMatrixMode(GL11.GL_PROJECTION);
@@ -290,4 +386,100 @@ public class RenderSystem {
return false;
}
}
public static int createBuffers() {
return GL15.glGenBuffers();
}
public static void bindBufferBase(int target, int index, int buffer) {
GL30.glBindBufferBase(target, index, buffer);
}
public static void bufferStorage(int target, long size, int flags) {
if (GLStateManager.capabilities.OpenGL44) {
GL44.glBufferStorage(target, size, flags);
} else if (GLStateManager.capabilities.GL_ARB_buffer_storage) {
ARBBufferStorage.glBufferStorage(target, size, flags);
} else {
// Fallback: use mutable storage
GL15.glBufferData(target, size, GL15.GL_DYNAMIC_DRAW);
}
}
public static void clearBufferSubData(int target, int internalFormat, long offset, long size, int format, int type, int[] data) {
if (GLStateManager.capabilities.OpenGL43) {
GL43.glClearBufferSubData(target, internalFormat, offset, size, format, type, (ByteBuffer) null);
} else if (GLStateManager.capabilities.GL_ARB_clear_buffer_object) {
ARBClearBufferObject.glClearBufferSubData(target, internalFormat, offset, size, format, type, (ByteBuffer) null);
}
}
public static void deleteBuffers(int buffer) {
GL15.glDeleteBuffers(buffer);
}
public static long getVRAM() {
if (GLStateManager.capabilities.GL_NVX_gpu_memory_info) {
return GL11.glGetInteger(NVXGpuMemoryInfo.GL_GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX) * 1024L;
} else {
// Default to 4GB if we can't query VRAM
return 4294967296L;
}
}
public static void clearTexImage(int texture, int target, int level, int format, int type) {
if (GLStateManager.capabilities.OpenGL44) {
GL44.glClearTexImage(texture, level, format, type, (ByteBuffer) null);
} else {
ARBClearTexture.glClearTexImage(texture, level, format, type, (ByteBuffer) null);
}
}
public static void textureStorage1D(int texture, int target, int levels, int internalFormat, int width) {
dsaState.textureStorage1D(texture, target, levels, internalFormat, width);
}
public static void textureStorage2D(int texture, int target, int levels, int internalFormat, int width, int height) {
dsaState.textureStorage2D(texture, target, levels, internalFormat, width, height);
}
public static void textureStorage3D(int texture, int target, int levels, int internalFormat, int width, int height, int depth) {
dsaState.textureStorage3D(texture, target, levels, internalFormat, width, height, depth);
}
public static boolean supportsSamplerObjects() {
return supportsSamplerObjects;
}
public static int genSampler() {
if (!supportsSamplerObjects) return 0;
return GL33.glGenSamplers();
}
public static void destroySampler(int sampler) {
if (!supportsSamplerObjects || sampler == 0) return;
GL33.glDeleteSamplers(sampler);
}
public static void samplerParameteri(int sampler, int pname, int param) {
if (!supportsSamplerObjects || sampler == 0) return;
GL33.glSamplerParameteri(sampler, pname, param);
}
public static void bindSamplerToUnit(int unit, int sampler) {
if (!supportsSamplerObjects) return;
if (samplers[unit] == sampler) return;
GL33.glBindSampler(unit, sampler);
samplers[unit] = sampler;
}
public static void unbindAllSamplers() {
if (!supportsSamplerObjects) return;
for (int i = 0; i < samplers.length; i++) {
if (samplers[i] != 0) {
GL33.glBindSampler(i, 0);
samplers[i] = 0;
}
}
}
}
@@ -94,6 +94,11 @@ public class DSAARB extends DSAUnsupported {
}
}
@Override
public void bindTextureToUnit(int target, int unit, int texture) {
bindTextureToUnit(unit, texture);
}
@Override
public int bufferStorage(int target, FloatBuffer data, int usage) {
final int buffer = GL45.glCreateBuffers();
@@ -121,4 +126,19 @@ public class DSAARB extends DSAUnsupported {
public int createTexture(int target) {
return ARBDirectStateAccess.glCreateTextures(target);
}
@Override
public void textureStorage1D(int texture, int target, int levels, int internalFormat, int width) {
ARBDirectStateAccess.glTextureStorage1D(texture, levels, internalFormat, width);
}
@Override
public void textureStorage2D(int texture, int target, int levels, int internalFormat, int width, int height) {
ARBDirectStateAccess.glTextureStorage2D(texture, levels, internalFormat, width, height);
}
@Override
public void textureStorage3D(int texture, int target, int levels, int internalFormat, int width, int height, int depth) {
ARBDirectStateAccess.glTextureStorage3D(texture, levels, internalFormat, width, height, depth);
}
}
@@ -28,6 +28,7 @@ public interface DSAAccess {
void copyTexSubImage2D(int destTexture, int target, int i, int i1, int i2, int i3, int i4, int width, int height);
void bindTextureToUnit(int unit, int texture);
void bindTextureToUnit(int target, int unit, int texture);
int bufferStorage(int target, FloatBuffer data, int usage);
@@ -37,4 +38,9 @@ public interface DSAAccess {
int createFramebuffer();
int createTexture(int target);
// Texture storage methods for custom images
void textureStorage1D(int texture, int target, int levels, int internalFormat, int width);
void textureStorage2D(int texture, int target, int levels, int internalFormat, int width, int height);
void textureStorage3D(int texture, int target, int levels, int internalFormat, int width, int height, int depth);
}
@@ -4,10 +4,13 @@ import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.RenderSystem;
import net.minecraft.client.renderer.OpenGlHelper;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL12;
import org.lwjgl.opengl.GL13;
import org.lwjgl.opengl.GL15;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GL30;
import org.lwjgl.opengl.GL31;
import org.lwjgl.opengl.GL42;
import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
@@ -113,6 +116,12 @@ public class DSAUnsupported implements DSAAccess {
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, texture);
}
@Override
public void bindTextureToUnit(int target, int unit, int texture) {
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0 + unit);
GLStateManager.glBindTexture(target, texture);
}
@Override
public int bufferStorage(int target, FloatBuffer data, int usage) {
final int buffer = GL15.glGenBuffers();
@@ -150,4 +159,28 @@ public class DSAUnsupported implements DSAAccess {
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, texture);
return texture;
}
@Override
public void textureStorage1D(int texture, int target, int levels, int internalFormat, int width) {
final int previous = GL11.glGetInteger(GL11.GL_TEXTURE_BINDING_1D);
GL11.glBindTexture(target, texture);
GL42.glTexStorage1D(target, levels, internalFormat, width);
GL11.glBindTexture(target, previous);
}
@Override
public void textureStorage2D(int texture, int target, int levels, int internalFormat, int width, int height) {
final int previous = (target == GL11.GL_TEXTURE_2D) ? GLStateManager.getBoundTextureForServerState() : GL11.glGetInteger(GL31.GL_TEXTURE_BINDING_RECTANGLE);
GL11.glBindTexture(target, texture);
GL42.glTexStorage2D(target, levels, internalFormat, width, height);
GL11.glBindTexture(target, previous);
}
@Override
public void textureStorage3D(int texture, int target, int levels, int internalFormat, int width, int height, int depth) {
final int previous = GL11.glGetInteger(GL12.GL_TEXTURE_BINDING_3D);
GL11.glBindTexture(target, texture);
GL42.glTexStorage3D(target, levels, internalFormat, width, height, depth);
GL11.glBindTexture(target, previous);
}
}
@@ -52,6 +52,10 @@ public class IrisLogging {
this.logger.debug(debug);
}
public void debug(String debug, Object... o) {
this.logger.debug(debug, o);
}
public void debug(String debug, Throwable t) {
this.logger.debug(debug, t);
}
@@ -0,0 +1,14 @@
package net.coderbot.iris.compat.dh;
/**
* Stub implementation of Distant Horizons compatibility.
*/
public class DHCompat {
public int getDepthTex() {
return 0;
}
public int getDepthTexNoTranslucent() {
return 0;
}
}
@@ -13,6 +13,8 @@ public enum FeatureFlags {
COMPUTE_SHADERS(() -> true, RenderSystem::supportsCompute),
ENTITY_TRANSLUCENT(() -> true, () -> true),
BLOCK_EMISSION_ATTRIBUTE(() -> true, () -> true),
CUSTOM_IMAGES(() -> true, RenderSystem::supportsImageLoadStore),
SSBO(() -> true, RenderSystem::supportsSSBO),
UNKNOWN(() -> false, () -> false);
private final BooleanSupplier irisRequirement;
@@ -0,0 +1,59 @@
package net.coderbot.iris.gl.buffer;
import com.gtnewhorizons.angelica.glsm.RenderSystem;
import org.embeddedt.embeddium.impl.gl.debug.GLDebug;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL15;
import org.lwjgl.opengl.GL30;
import org.lwjgl.opengl.GL43;
public class ShaderStorageBuffer {
protected final int index;
protected final ShaderStorageInfo info;
protected int id;
public ShaderStorageBuffer(int index, ShaderStorageInfo info) {
this.id = RenderSystem.createBuffers();
GLDebug.nameObject(GL43.GL_BUFFER, id, "SSBO " + index);
this.index = index;
this.info = info;
}
public final int getIndex() {
return index;
}
public final long getSize() {
return info.size();
}
protected void destroy() {
RenderSystem.bindBufferBase(GL43.GL_SHADER_STORAGE_BUFFER, index, 0);
RenderSystem.deleteBuffers(id);
}
public void bind() {
RenderSystem.bindBufferBase(GL43.GL_SHADER_STORAGE_BUFFER, index, id);
}
public void resizeIfRelative(int width, int height) {
if (!info.relative()) return;
RenderSystem.deleteBuffers(id);
int newId = GL15.glGenBuffers();
GL15.glBindBuffer(GL43.GL_SHADER_STORAGE_BUFFER, newId);
// Calculation time
int newWidth = (int) (width * info.scaleX());
int newHeight = (int) (height * info.scaleY());
int finalSize = (newHeight * newWidth) * info.size();
RenderSystem.bufferStorage(GL43.GL_SHADER_STORAGE_BUFFER, finalSize, 0);
RenderSystem.clearBufferSubData(GL43.GL_SHADER_STORAGE_BUFFER, GL30.GL_R8, 0, finalSize, GL11.GL_RED, GL11.GL_BYTE, new int[]{0});
RenderSystem.bindBufferBase(GL43.GL_SHADER_STORAGE_BUFFER, index, newId);
id = newId;
}
public int getId() {
return id;
}
}
@@ -0,0 +1,98 @@
package net.coderbot.iris.gl.buffer;
import com.gtnewhorizons.angelica.glsm.RenderSystem;
import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap;
import net.coderbot.iris.gl.sampler.SamplerLimits;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL15;
import org.lwjgl.opengl.GL30;
import org.lwjgl.opengl.GL43;
import java.util.Collections;
public class ShaderStorageBufferHolder {
private int cachedWidth;
private int cachedHeight;
private ShaderStorageBuffer[] buffers;
private boolean destroyed;
public ShaderStorageBufferHolder(Int2ObjectArrayMap<ShaderStorageInfo> overrides, int width, int height) {
destroyed = false;
cachedWidth = width;
cachedHeight = height;
buffers = new ShaderStorageBuffer[Collections.max(overrides.keySet()) + 1];
overrides.forEach((index, bufferInfo) -> {
if (bufferInfo.size() > RenderSystem.getVRAM()) {
throw new OutOfVideoMemoryError("We only have " + toMib(RenderSystem.getVRAM()) + "MiB of RAM to work with, but the pack is requesting " + bufferInfo.size() + "! Can't continue.");
}
if (index > SamplerLimits.get().getMaxShaderStorageUnits()) {
throw new IllegalStateException("We don't have enough SSBO units??? (index: " + index + ", max: " + SamplerLimits.get().getMaxShaderStorageUnits());
}
buffers[index] = new ShaderStorageBuffer(index, bufferInfo);
int buffer = buffers[index].getId();
if (bufferInfo.relative()) {
buffers[index].resizeIfRelative(width, height);
} else {
GL15.glBindBuffer(GL43.GL_SHADER_STORAGE_BUFFER, buffer);
RenderSystem.bufferStorage(GL43.GL_SHADER_STORAGE_BUFFER, bufferInfo.size(), 0);
RenderSystem.clearBufferSubData(GL43.GL_SHADER_STORAGE_BUFFER, GL30.GL_R8, 0, bufferInfo.size(), GL11.GL_RED, GL11.GL_BYTE, new int[]{0});
RenderSystem.bindBufferBase(GL43.GL_SHADER_STORAGE_BUFFER, index, buffer);
}
});
GL15.glBindBuffer(GL43.GL_SHADER_STORAGE_BUFFER, 0);
}
private static long toMib(long x) {
return x / 1024L / 1024L;
}
public void hasResizedScreen(int width, int height) {
if (width != cachedWidth || height != cachedHeight) {
cachedWidth = width;
cachedHeight = height;
for (ShaderStorageBuffer buffer : buffers) {
if (buffer != null) {
buffer.resizeIfRelative(width, height);
}
}
}
}
public void setupBuffers() {
if (destroyed) {
throw new IllegalStateException("Tried to use destroyed buffer objects");
}
for (ShaderStorageBuffer buffer : buffers) {
if (buffer != null) {
buffer.bind();
}
}
}
public int getBufferIndex(int index) {
if (buffers.length < index || buffers[index] == null)
throw new RuntimeException("Tried to query a buffer for indirect dispatch that doesn't exist!");
return buffers[index].getId();
}
public void destroyBuffers() {
for (ShaderStorageBuffer buffer : buffers) {
if (buffer != null) {
buffer.destroy();
}
}
buffers = null;
destroyed = true;
}
private static class OutOfVideoMemoryError extends RuntimeException {
public OutOfVideoMemoryError(String s) {
super(s);
}
}
}
@@ -0,0 +1,4 @@
package net.coderbot.iris.gl.buffer;
public record ShaderStorageInfo(int size, boolean relative, float scaleX, float scaleY) {
}
@@ -0,0 +1,9 @@
package net.coderbot.iris.gl.framebuffer;
public record ViewportData(float scale, float viewportX, float viewportY) {
private static final ViewportData DEFAULT = new ViewportData(1.0f, 0.0f, 0.0f);
public static ViewportData defaultValue() {
return DEFAULT;
}
}
@@ -0,0 +1,152 @@
package net.coderbot.iris.gl.image;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.RenderSystem;
import net.coderbot.iris.gl.texture.InternalTextureFormat;
import net.coderbot.iris.gl.texture.PixelFormat;
import net.coderbot.iris.gl.texture.PixelType;
import net.coderbot.iris.gl.texture.TextureType;
import org.embeddedt.embeddium.impl.gl.GlObject;
import org.embeddedt.embeddium.impl.gl.debug.GLDebug;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL12;
import org.lwjgl.opengl.GL14;
public class GlImage extends GlObject {
protected final String name;
protected final String samplerName;
protected final TextureType target;
protected final PixelFormat format;
protected final InternalTextureFormat internalTextureFormat;
protected final PixelType pixelType;
private final boolean clear;
public GlImage(String name, String samplerName, TextureType target, PixelFormat format, InternalTextureFormat internalFormat, PixelType pixelType, boolean clear, int width, int height, int depth) {
this.setHandle(RenderSystem.createTexture(target.getGlType()));
this.name = name;
this.samplerName = samplerName;
this.target = target;
this.format = format;
this.internalTextureFormat = internalFormat;
this.pixelType = pixelType;
this.clear = clear;
GLDebug.nameObject(GL11.GL_TEXTURE, handle(), name);
GL11.glBindTexture(target.getGlType(), handle());
target.apply(handle(), width, height, depth, internalFormat.getGlFormat(), format.getGlFormat(), pixelType.getGlFormat(), null);
int texture = handle();
setup(texture, width, height, depth);
GL11.glBindTexture(target.getGlType(), 0);
}
protected void setup(int texture, int width, int height, int depth) {
boolean isInteger = internalTextureFormat.getPixelFormat().isInteger();
RenderSystem.texParameteri(texture, target.getGlType(), GL11.GL_TEXTURE_MIN_FILTER, isInteger ? GL11.GL_NEAREST : GL11.GL_LINEAR);
RenderSystem.texParameteri(texture, target.getGlType(), GL11.GL_TEXTURE_MAG_FILTER, isInteger ? GL11.GL_NEAREST : GL11.GL_LINEAR);
RenderSystem.texParameteri(texture, target.getGlType(), GL11.GL_TEXTURE_WRAP_S, GL12.GL_CLAMP_TO_EDGE);
if (height > 0) {
RenderSystem.texParameteri(texture, target.getGlType(), GL11.GL_TEXTURE_WRAP_T, GL12.GL_CLAMP_TO_EDGE);
}
if (depth > 0) {
RenderSystem.texParameteri(texture, target.getGlType(), GL12.GL_TEXTURE_WRAP_R, GL12.GL_CLAMP_TO_EDGE);
}
RenderSystem.texParameteri(texture, target.getGlType(), GL12.GL_TEXTURE_MAX_LEVEL, 0);
RenderSystem.texParameteri(texture, target.getGlType(), GL12.GL_TEXTURE_MIN_LOD, 0);
RenderSystem.texParameteri(texture, target.getGlType(), GL12.GL_TEXTURE_MAX_LOD, 0);
RenderSystem.texParameterf(texture, target.getGlType(), GL14.GL_TEXTURE_LOD_BIAS, 0.0F);
RenderSystem.clearTexImage(texture, target.getGlType(), 0, format.getGlFormat(), pixelType.getGlFormat());
}
public String getName() {
return name;
}
public String getSamplerName() {
return samplerName;
}
public TextureType getTarget() {
return target;
}
public boolean shouldClear() {
return clear;
}
public int getId() {
return handle();
}
/**
* Clears the texture to zero. Called each frame for images that need clearing.
*/
public void clear() {
RenderSystem.clearTexImage(handle(), target.getGlType(), 0, format.getGlFormat(), pixelType.getGlFormat());
}
/**
* This makes the image aware of a new render target. Depending on the image's properties, it may not follow these targets.
*
* @param width The width of the main render target.
* @param height The height of the main render target.
*/
public void updateNewSize(int width, int height) {
}
@Override
protected void destroyInternal() {
GLStateManager.glDeleteTextures(handle());
}
public InternalTextureFormat getInternalFormat() {
return internalTextureFormat;
}
@Override
public String toString() {
return "GlImage name " + name + " format " + format + "internalformat " + internalTextureFormat + " pixeltype " + pixelType;
}
public PixelFormat getFormat() {
return format;
}
public PixelType getPixelType() {
return pixelType;
}
public static class Relative extends GlImage {
private final float relativeHeight;
private final float relativeWidth;
public Relative(String name, String samplerName, PixelFormat format, InternalTextureFormat internalFormat, PixelType pixelType, boolean clear, float relativeWidth, float relativeHeight, int currentWidth, int currentHeight) {
super(name, samplerName, TextureType.TEXTURE_2D, format, internalFormat, pixelType, clear, (int) (currentWidth * relativeWidth), (int) (currentHeight * relativeHeight), 0);
this.relativeWidth = relativeWidth;
this.relativeHeight = relativeHeight;
}
@Override
public void updateNewSize(int width, int height) {
GL11.glBindTexture(target.getGlType(), handle());
target.apply(handle(), (int) (width * relativeWidth), (int) (height * relativeHeight), 0, internalTextureFormat.getGlFormat(), format.getGlFormat(), pixelType.getGlFormat(), null);
int texture = handle();
setup(texture, width, height, 0);
GL11.glBindTexture(target.getGlType(), 0);
}
}
}
@@ -0,0 +1,12 @@
package net.coderbot.iris.gl.image;
import net.coderbot.iris.gl.texture.InternalTextureFormat;
import net.coderbot.iris.gl.texture.PixelFormat;
import net.coderbot.iris.gl.texture.PixelType;
import net.coderbot.iris.gl.texture.TextureType;
public record ImageInformation(String name, String samplerName, TextureType target, PixelFormat format,
InternalTextureFormat internalTextureFormat,
PixelType type, int width, int height, int depth, boolean clear, boolean isRelative,
float relativeWidth, float relativeHeight) {
}
@@ -4,10 +4,12 @@ import com.gtnewhorizons.angelica.glsm.RenderSystem;
import net.coderbot.iris.Iris;
import net.coderbot.iris.gl.GlResource;
import net.coderbot.iris.pipeline.WorldRenderingPipeline;
import net.coderbot.iris.shaderpack.FilledIndirectPointer;
import org.joml.Vector2f;
import org.joml.Vector3i;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GL42;
import org.lwjgl.opengl.GL43;
import java.nio.IntBuffer;
@@ -18,8 +20,8 @@ public final class ComputeProgram extends GlResource {
private final ProgramImages images;
private Vector3i absoluteWorkGroups;
private Vector2f relativeWorkGroups;
// private int[] localSize;
private final IntBuffer localSizeBuffer;
private FilledIndirectPointer indirectPointer;
private final IntBuffer localSizeBuffer;
private float cachedWidth;
private float cachedHeight;
private Vector3i cachedWorkGroups;
@@ -34,19 +36,21 @@ public final class ComputeProgram extends GlResource {
this.images = images;
}
public void setWorkGroupInfo(Vector2f relativeWorkGroups, Vector3i absoluteWorkGroups) {
public void setWorkGroupInfo(Vector2f relativeWorkGroups, Vector3i absoluteWorkGroups, FilledIndirectPointer indirectPointer) {
this.relativeWorkGroups = relativeWorkGroups;
this.absoluteWorkGroups = absoluteWorkGroups;
this.indirectPointer = indirectPointer;
}
public Vector3i getWorkGroups(float width, float height) {
if (indirectPointer != null) return null;
if (cachedWidth != width || cachedHeight != height || cachedWorkGroups == null) {
this.cachedWidth = width;
this.cachedHeight = height;
if (this.absoluteWorkGroups != null) {
this.cachedWorkGroups = this.absoluteWorkGroups;
} else if (relativeWorkGroups != null) {
// TODO: This is my best guess at what Optifine does. Can this be confirmed?
// Do not use actual localSize here, apparently that's not what we want.
this.cachedWorkGroups = new Vector3i((int) Math.ceil(Math.ceil((width * relativeWorkGroups.x)) / localSizeBuffer.get(0)), (int) Math.ceil(Math.ceil((height * relativeWorkGroups.y)) / localSizeBuffer.get(1)), 1);
} else {
@@ -67,10 +71,15 @@ public final class ComputeProgram extends GlResource {
public void dispatch(float width, float height) {
if (!Iris.getPipelineManager().getPipeline().map(WorldRenderingPipeline::allowConcurrentCompute).orElse(false)) {
RenderSystem.memoryBarrier(40);
RenderSystem.memoryBarrier(GL42.GL_SHADER_IMAGE_ACCESS_BARRIER_BIT | GL42.GL_TEXTURE_FETCH_BARRIER_BIT | GL43.GL_SHADER_STORAGE_BARRIER_BIT);
}
RenderSystem.dispatchCompute(getWorkGroups(width, height));
if (indirectPointer != null) {
RenderSystem.bindBuffer(GL43.GL_DISPATCH_INDIRECT_BUFFER, indirectPointer.buffer());
RenderSystem.dispatchComputeIndirect(indirectPointer.offset());
} else {
RenderSystem.dispatchCompute(getWorkGroups(width, height));
}
}
public static void unbind() {
@@ -3,12 +3,14 @@ package net.coderbot.iris.gl.program;
import com.google.common.collect.ImmutableSet;
import com.gtnewhorizons.angelica.glsm.RenderSystem;
import net.coderbot.iris.gl.image.ImageHolder;
import net.coderbot.iris.gl.sampler.GlSampler;
import net.coderbot.iris.gl.sampler.SamplerHolder;
import net.coderbot.iris.gl.shader.GlShader;
import net.coderbot.iris.gl.shader.ProgramCreator;
import net.coderbot.iris.gl.shader.ShaderType;
import net.coderbot.iris.gl.state.ValueUpdateNotifier;
import net.coderbot.iris.gl.texture.InternalTextureFormat;
import net.coderbot.iris.gl.texture.TextureType;
import org.jetbrains.annotations.Nullable;
import java.util.function.IntSupplier;
@@ -106,17 +108,18 @@ public class ProgramBuilder extends ProgramUniforms.Builder implements SamplerHo
}
@Override
public boolean addDefaultSampler(IntSupplier sampler, String... names) {
return samplers.addDefaultSampler(sampler, names);
public boolean addDefaultSampler(TextureType type, IntSupplier texture, ValueUpdateNotifier notifier, GlSampler sampler, String... names) {
return samplers.addDefaultSampler(type, texture, notifier, sampler, names);
}
@Override
public boolean addDynamicSampler(IntSupplier sampler, String... names) {
return samplers.addDynamicSampler(sampler, names);
public boolean addDynamicSampler(TextureType type, IntSupplier texture, GlSampler sampler, String... names) {
return samplers.addDynamicSampler(type, texture, sampler, names);
}
public boolean addDynamicSampler(IntSupplier sampler, ValueUpdateNotifier notifier, String... names) {
return samplers.addDynamicSampler(sampler, notifier, names);
@Override
public boolean addDynamicSampler(TextureType type, IntSupplier texture, ValueUpdateNotifier notifier, GlSampler sampler, String... names) {
return samplers.addDynamicSampler(type, texture, notifier, sampler, names);
}
@Override
@@ -5,10 +5,13 @@ import com.google.common.collect.ImmutableSet;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.RenderSystem;
import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
import net.coderbot.iris.gl.sampler.GlSampler;
import net.coderbot.iris.gl.sampler.SamplerBinding;
import net.coderbot.iris.gl.sampler.SamplerHolder;
import net.coderbot.iris.gl.sampler.SamplerLimits;
import net.coderbot.iris.gl.state.ValueUpdateNotifier;
import net.coderbot.iris.gl.texture.TextureAccess;
import net.coderbot.iris.gl.texture.TextureType;
import net.coderbot.iris.shaderpack.PackRenderTargetDirectives;
import org.lwjgl.opengl.GL13;
@@ -73,11 +76,11 @@ public class ProgramSamplers {
return new Builder(program, reservedTextureUnits);
}
public static CustomTextureSamplerInterceptor customTextureSamplerInterceptor(SamplerHolder samplerHolder, Object2ObjectMap<String, IntSupplier> customTextureIds) {
public static CustomTextureSamplerInterceptor customTextureSamplerInterceptor(SamplerHolder samplerHolder, Object2ObjectMap<String, TextureAccess> customTextureIds) {
return customTextureSamplerInterceptor(samplerHolder, customTextureIds, ImmutableSet.of());
}
public static CustomTextureSamplerInterceptor customTextureSamplerInterceptor(SamplerHolder samplerHolder, Object2ObjectMap<String, IntSupplier> customTextureIds, ImmutableSet<Integer> flippedAtLeastOnceSnapshot) {
public static CustomTextureSamplerInterceptor customTextureSamplerInterceptor(SamplerHolder samplerHolder, Object2ObjectMap<String, TextureAccess> customTextureIds, ImmutableSet<Integer> flippedAtLeastOnceSnapshot) {
return new CustomTextureSamplerInterceptor(samplerHolder, customTextureIds, flippedAtLeastOnceSnapshot);
}
@@ -145,35 +148,31 @@ public class ProgramSamplers {
}
@Override
public boolean addDefaultSampler(IntSupplier sampler, String... names) {
public boolean addDefaultSampler(TextureType type, IntSupplier texture, ValueUpdateNotifier notifier, GlSampler sampler, String... names) {
if (nextUnit != 0) {
// TODO: Relax this restriction!
throw new IllegalStateException("Texture unit 0 is already used.");
}
return addDynamicSampler(sampler, true, null, names);
if (notifier != null) {
notifiersToReset.add(notifier);
}
return addDynamicSampler(type, texture, sampler, true, notifier, names);
}
/**
* Adds a sampler
* @return false if this sampler is not active, true if at least one of the names referred to an active sampler
*/
@Override
public boolean addDynamicSampler(IntSupplier sampler, String... names) {
return addDynamicSampler(sampler, false, null, names);
public boolean addDynamicSampler(TextureType type, IntSupplier texture, GlSampler sampler, String... names) {
return addDynamicSampler(type, texture, sampler, false, null, names);
}
/**
* Adds a sampler
* @return false if this sampler is not active, true if at least one of the names referred to an active sampler
*/
@Override
public boolean addDynamicSampler(IntSupplier sampler, ValueUpdateNotifier notifier, String... names) {
public boolean addDynamicSampler(TextureType type, IntSupplier texture, ValueUpdateNotifier notifier, GlSampler sampler, String... names) {
notifiersToReset.add(notifier);
return addDynamicSampler(sampler, false, notifier, names);
return addDynamicSampler(type, texture, sampler, false, notifier, names);
}
private boolean addDynamicSampler(IntSupplier sampler, boolean used, ValueUpdateNotifier notifier, String... names) {
private boolean addDynamicSampler(TextureType type, IntSupplier texture, GlSampler sampler, boolean used, ValueUpdateNotifier notifier, String... names) {
for (String name : names) {
int location = RenderSystem.getUniformLocation(program, name);
@@ -187,8 +186,6 @@ public class ProgramSamplers {
throw new IllegalStateException("No more available texture units while activating sampler " + name);
}
//System.out.println("Binding dynamic sampler " + name + " to texture unit " + nextUnit);
// Set up this sampler uniform to use this particular texture unit.
calls.add(new GlUniform1iCall(location, nextUnit));
@@ -200,7 +197,7 @@ public class ProgramSamplers {
return false;
}
samplers.add(new SamplerBinding(nextUnit, sampler, notifier));
samplers.add(new SamplerBinding(type, nextUnit, texture, sampler, notifier));
remainingUnits -= 1;
nextUnit += 1;
@@ -209,8 +206,6 @@ public class ProgramSamplers {
nextUnit += 1;
}
//System.out.println("The next unit is " + nextUnit + ", there are " + remainingUnits + " units remaining.");
return true;
}
@@ -221,10 +216,10 @@ public class ProgramSamplers {
public static final class CustomTextureSamplerInterceptor implements SamplerHolder {
private final SamplerHolder samplerHolder;
private final Object2ObjectMap<String, IntSupplier> customTextureIds;
private final Object2ObjectMap<String, TextureAccess> customTextureIds;
private final ImmutableSet<String> deactivatedOverrides;
private CustomTextureSamplerInterceptor(SamplerHolder samplerHolder, Object2ObjectMap<String, IntSupplier> customTextureIds, ImmutableSet<Integer> flippedAtLeastOnceSnapshot) {
private CustomTextureSamplerInterceptor(SamplerHolder samplerHolder, Object2ObjectMap<String, TextureAccess> customTextureIds, ImmutableSet<Integer> flippedAtLeastOnceSnapshot) {
this.samplerHolder = samplerHolder;
this.customTextureIds = customTextureIds;
@@ -244,7 +239,7 @@ public class ProgramSamplers {
private IntSupplier getOverride(IntSupplier existing, String... names) {
for (String name : names) {
if (customTextureIds.containsKey(name) && !deactivatedOverrides.contains(name)) {
return customTextureIds.get(name);
return customTextureIds.get(name).getTextureId();
}
}
@@ -272,24 +267,21 @@ public class ProgramSamplers {
}
@Override
public boolean addDefaultSampler(IntSupplier sampler, String... names) {
sampler = getOverride(sampler, names);
return samplerHolder.addDefaultSampler(sampler, names);
public boolean addDefaultSampler(TextureType type, IntSupplier texture, ValueUpdateNotifier notifier, GlSampler sampler, String... names) {
texture = getOverride(texture, names);
return samplerHolder.addDefaultSampler(type, texture, notifier, sampler, names);
}
@Override
public boolean addDynamicSampler(IntSupplier sampler, String... names) {
sampler = getOverride(sampler, names);
return samplerHolder.addDynamicSampler(sampler, names);
public boolean addDynamicSampler(TextureType type, IntSupplier texture, GlSampler sampler, String... names) {
texture = getOverride(texture, names);
return samplerHolder.addDynamicSampler(type, texture, sampler, names);
}
@Override
public boolean addDynamicSampler(IntSupplier sampler, ValueUpdateNotifier notifier, String... names) {
sampler = getOverride(sampler, names);
return samplerHolder.addDynamicSampler(sampler, notifier, names);
public boolean addDynamicSampler(TextureType type, IntSupplier texture, ValueUpdateNotifier notifier, GlSampler sampler, String... names) {
texture = getOverride(texture, names);
return samplerHolder.addDynamicSampler(type, texture, notifier, sampler, names);
}
}
}
@@ -0,0 +1,41 @@
package net.coderbot.iris.gl.sampler;
import com.gtnewhorizons.angelica.glsm.RenderSystem;
import org.embeddedt.embeddium.impl.gl.GlObject;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL12;
import org.lwjgl.opengl.GL14;
import org.lwjgl.opengl.GL30;
public class GlSampler extends GlObject {
public GlSampler(boolean linear, boolean mipmapped, boolean shadow, boolean hardwareShadow) {
if (!RenderSystem.supportsSamplerObjects()) {
this.setHandle(0);
return;
}
this.setHandle(RenderSystem.genSampler());
RenderSystem.samplerParameteri(getId(), GL11.GL_TEXTURE_MIN_FILTER, linear ? GL11.GL_LINEAR : GL11.GL_NEAREST);
RenderSystem.samplerParameteri(getId(), GL11.GL_TEXTURE_MAG_FILTER, linear ? GL11.GL_LINEAR : GL11.GL_NEAREST);
RenderSystem.samplerParameteri(getId(), GL11.GL_TEXTURE_WRAP_S, GL12.GL_CLAMP_TO_EDGE);
RenderSystem.samplerParameteri(getId(), GL11.GL_TEXTURE_WRAP_T, GL12.GL_CLAMP_TO_EDGE);
if (mipmapped) {
RenderSystem.samplerParameteri(getId(), GL11.GL_TEXTURE_MIN_FILTER, linear ? GL11.GL_LINEAR_MIPMAP_LINEAR : GL11.GL_NEAREST_MIPMAP_NEAREST);
}
if (hardwareShadow) {
RenderSystem.samplerParameteri(getId(), GL14.GL_TEXTURE_COMPARE_MODE, GL30.GL_COMPARE_REF_TO_TEXTURE);
}
}
@Override
protected void destroyInternal() {
RenderSystem.destroySampler(handle());
}
public int getId() {
return handle();
}
}
@@ -2,17 +2,22 @@ package net.coderbot.iris.gl.sampler;
import com.gtnewhorizons.angelica.glsm.RenderSystem;
import net.coderbot.iris.gl.state.ValueUpdateNotifier;
import net.coderbot.iris.gl.texture.TextureType;
import java.util.function.IntSupplier;
public class SamplerBinding {
private final TextureType textureType;
private final int textureUnit;
private final IntSupplier texture;
private final int sampler;
private final ValueUpdateNotifier notifier;
public SamplerBinding(int textureUnit, IntSupplier texture, ValueUpdateNotifier notifier) {
public SamplerBinding(TextureType type, int textureUnit, IntSupplier texture, GlSampler sampler, ValueUpdateNotifier notifier) {
this.textureType = type;
this.textureUnit = textureUnit;
this.texture = texture;
this.sampler = sampler == null ? 0 : sampler.getId();
this.notifier = notifier;
}
@@ -25,6 +30,7 @@ public class SamplerBinding {
}
private void updateSampler() {
RenderSystem.bindTextureToUnit(textureUnit, texture.getAsInt());
RenderSystem.bindSamplerToUnit(textureUnit, sampler);
RenderSystem.bindTextureToUnit(textureType.getGlType(), textureUnit, texture.getAsInt());
}
}
@@ -1,6 +1,7 @@
package net.coderbot.iris.gl.sampler;
import net.coderbot.iris.gl.state.ValueUpdateNotifier;
import net.coderbot.iris.gl.texture.TextureType;
import java.util.function.IntSupplier;
@@ -15,7 +16,25 @@ public interface SamplerHolder {
* Throws an exception if texture unit 0 is already allocated or reserved in some way. Do not call this
* function after calls to addDynamicSampler, it must be called before any calls to addDynamicSampler.
*/
boolean addDefaultSampler(IntSupplier sampler, String... names);
boolean addDynamicSampler(IntSupplier sampler, String... names);
boolean addDynamicSampler(IntSupplier sampler, ValueUpdateNotifier notifier, String... names);
default boolean addDefaultSampler(IntSupplier texture, String... names) {
return addDefaultSampler(TextureType.TEXTURE_2D, texture, null, null, names);
}
boolean addDefaultSampler(TextureType type, IntSupplier texture, ValueUpdateNotifier notifier, GlSampler sampler, String... names);
default boolean addDynamicSampler(IntSupplier texture, String... names) {
return addDynamicSampler(TextureType.TEXTURE_2D, texture, null, names);
}
default boolean addDynamicSampler(TextureType type, IntSupplier texture, String... names) {
return addDynamicSampler(type, texture, null, names);
}
boolean addDynamicSampler(TextureType type, IntSupplier texture, GlSampler sampler, String... names);
default boolean addDynamicSampler(IntSupplier texture, ValueUpdateNotifier notifier, String... names) {
return addDynamicSampler(TextureType.TEXTURE_2D, texture, notifier, null, names);
}
boolean addDynamicSampler(TextureType type, IntSupplier texture, ValueUpdateNotifier notifier, GlSampler sampler, String... names);
}
@@ -1,16 +1,28 @@
package net.coderbot.iris.gl.sampler;
import com.gtnewhorizons.angelica.glsm.RenderSystem;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GL43;
public class SamplerLimits {
private static SamplerLimits instance;
private final int maxTextureUnits;
private final int maxDrawBuffers;
private static SamplerLimits instance;
private final int maxShaderStorageUnits;
private SamplerLimits() {
this.maxTextureUnits = GL11.glGetInteger(GL20.GL_MAX_TEXTURE_IMAGE_UNITS);
this.maxDrawBuffers = GL11.glGetInteger(GL20.GL_MAX_DRAW_BUFFERS);
this.maxShaderStorageUnits = RenderSystem.supportsSSBO() ? GL11.glGetInteger(GL43.GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS) : 0;
}
public static SamplerLimits get() {
if (instance == null) {
instance = new SamplerLimits();
}
return instance;
}
public int getMaxTextureUnits() {
@@ -21,11 +33,7 @@ public class SamplerLimits {
return maxDrawBuffers;
}
public static SamplerLimits get() {
if (instance == null) {
instance = new SamplerLimits();
}
return instance;
public int getMaxShaderStorageUnits() {
return maxShaderStorageUnits;
}
}
@@ -0,0 +1,82 @@
package net.coderbot.iris.gl.texture;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.RenderSystem;
import net.coderbot.iris.shaderpack.texture.TextureFilteringData;
import org.embeddedt.embeddium.impl.gl.GlObject;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL12;
import org.lwjgl.opengl.GL14;
import java.nio.ByteBuffer;
import java.util.function.IntSupplier;
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memAlloc;
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memFree;
public class GlTexture extends GlObject implements TextureAccess {
private final TextureType target;
public GlTexture(TextureType target, int sizeX, int sizeY, int sizeZ, int internalFormat, int format, int pixelType, byte[] pixels, TextureFilteringData filteringData) {
this.setHandle(GL11.glGenTextures());
bindTextureForSetup(target.getGlType(), handle());
TextureUploadHelper.resetTextureUploadState();
final ByteBuffer buffer = memAlloc(pixels.length);
buffer.put(pixels);
buffer.flip();
target.apply(this.handle(), sizeX, sizeY, sizeZ, internalFormat, format, pixelType, buffer);
memFree(buffer);
final int texture = this.handle();
RenderSystem.texParameteri(texture, target.getGlType(), GL11.GL_TEXTURE_MIN_FILTER, filteringData.shouldBlur() ? GL11.GL_LINEAR : GL11.GL_NEAREST);
RenderSystem.texParameteri(texture, target.getGlType(), GL11.GL_TEXTURE_MAG_FILTER, filteringData.shouldBlur() ? GL11.GL_LINEAR : GL11.GL_NEAREST);
RenderSystem.texParameteri(texture, target.getGlType(), GL11.GL_TEXTURE_WRAP_S, filteringData.shouldClamp() ? GL12.GL_CLAMP_TO_EDGE : GL11.GL_REPEAT);
if (sizeY > 0) {
RenderSystem.texParameteri(texture, target.getGlType(), GL11.GL_TEXTURE_WRAP_T, filteringData.shouldClamp() ? GL12.GL_CLAMP_TO_EDGE : GL11.GL_REPEAT);
}
if (sizeZ > 0) {
RenderSystem.texParameteri(texture, target.getGlType(), GL12.GL_TEXTURE_WRAP_R, filteringData.shouldClamp() ? GL12.GL_CLAMP_TO_EDGE : GL11.GL_REPEAT);
}
RenderSystem.texParameteri(texture, target.getGlType(), GL12.GL_TEXTURE_MAX_LEVEL, 0);
RenderSystem.texParameteri(texture, target.getGlType(), GL12.GL_TEXTURE_MIN_LOD, 0);
RenderSystem.texParameteri(texture, target.getGlType(), GL12.GL_TEXTURE_MAX_LOD, 0);
RenderSystem.texParameterf(texture, target.getGlType(), GL14.GL_TEXTURE_LOD_BIAS, 0.0F);
bindTextureForSetup(target.getGlType(), 0);
this.target = target;
}
public static void bindTextureForSetup(int glType, int glId) {
GL11.glBindTexture(glType, glId);
}
public TextureType getTarget() {
return target;
}
public void bind(int unit) {
RenderSystem.bindTextureToUnit(target.getGlType(), unit, handle());
}
@Override
public TextureType getType() {
return target;
}
@Override
public IntSupplier getTextureId() {
return this::handle;
}
@Override
protected void destroyInternal() {
GLStateManager.glDeleteTextures(handle());
}
}
@@ -0,0 +1,9 @@
package net.coderbot.iris.gl.texture;
import java.util.function.IntSupplier;
public interface TextureAccess {
TextureType getType();
IntSupplier getTextureId();
}
@@ -0,0 +1,37 @@
package net.coderbot.iris.gl.texture;
import lombok.Getter;
@Getter
public abstract class TextureDefinition {
protected String name;
public static class PNGDefinition extends TextureDefinition {
public PNGDefinition(String name) {
this.name = name;
}
}
@Getter
public static class RawDefinition extends TextureDefinition {
private final TextureType target;
private final int sizeX;
private final int sizeY;
private final int sizeZ;
private final InternalTextureFormat internalFormat;
private final PixelFormat format;
private final PixelType pixelType;
public RawDefinition(String path, TextureType target, InternalTextureFormat internalFormat, int sizeX, int sizeY, int sizeZ, PixelFormat format, PixelType pixelType) {
this.name = path;
this.target = target;
this.sizeX = sizeX;
this.sizeY = sizeY;
this.sizeZ = sizeZ;
this.internalFormat = internalFormat;
this.format = format;
this.pixelType = pixelType;
}
}
}
@@ -0,0 +1,42 @@
package net.coderbot.iris.gl.texture;
import com.gtnewhorizons.angelica.glsm.RenderSystem;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL12;
import org.lwjgl.opengl.GL31;
import java.nio.ByteBuffer;
import java.util.Optional;
public enum TextureType {
TEXTURE_1D(GL11.GL_TEXTURE_1D),
TEXTURE_2D(GL11.GL_TEXTURE_2D),
TEXTURE_3D(GL12.GL_TEXTURE_3D),
TEXTURE_RECTANGLE(GL31.GL_TEXTURE_RECTANGLE);
private final int glType;
TextureType(int glType) {
this.glType = glType;
}
public static Optional<TextureType> fromString(String name) {
try {
return Optional.of(TextureType.valueOf(name));
} catch (IllegalArgumentException e) {
return Optional.empty();
}
}
public int getGlType() {
return glType;
}
public void apply(int texture, int sizeX, int sizeY, int sizeZ, int internalFormat, int format, int pixelType, ByteBuffer pixels) {
switch (this) {
case TEXTURE_1D -> RenderSystem.texImage1D(texture, glType, 0, internalFormat, sizeX, 0, format, pixelType, pixels);
case TEXTURE_2D, TEXTURE_RECTANGLE -> RenderSystem.texImage2D(texture, glType, 0, internalFormat, sizeX, sizeY, 0, format, pixelType, pixels);
case TEXTURE_3D -> RenderSystem.texImage3D(texture, glType, 0, internalFormat, sizeX, sizeY, sizeZ, 0, format, pixelType, pixels);
}
}
}
@@ -0,0 +1,23 @@
package net.coderbot.iris.gl.texture;
import java.util.function.IntSupplier;
public class TextureWrapper implements TextureAccess {
private final IntSupplier texture;
private final TextureType type;
public TextureWrapper(IntSupplier texture, TextureType type) {
this.texture = texture;
this.type = type;
}
@Override
public TextureType getType() {
return this.type;
}
@Override
public IntSupplier getTextureId() {
return this.texture;
}
}
@@ -0,0 +1,18 @@
package net.coderbot.iris.helpers;
import java.util.Objects;
public record Tri<X, Y, Z>(X first, Y second, Z third) {
@Override
public boolean equals(Object obj) {
if (obj == this) return true;
if (!(obj instanceof Tri tri)) return false;
return Objects.equals(tri.first, this.first) && Objects.equals(tri.second, this.second) && Objects.equals(tri.third, this.third);
}
@Override
public String toString() {
return "First: " + first.toString() + " Second: " + second.toString() + " Third: " + third.toString();
}
}
@@ -22,6 +22,7 @@ public class GbufferPrograms {
public static void beginEntities() {
checkReentrancy();
setPhase(WorldRenderingPhase.ENTITIES);
setBlockEntityDefaults();
entities = true;
}
@@ -52,6 +53,7 @@ public class GbufferPrograms {
public static void beginBlockEntities() {
checkReentrancy();
setPhase(WorldRenderingPhase.BLOCK_ENTITIES);
setBlockEntityDefaults();
blockEntities = true;
}
@@ -68,6 +70,7 @@ public class GbufferPrograms {
GL20.glVertexAttrib2s(ProgramCreator.MC_ENTITY, (short)-1, (short)-1);
GL20.glVertexAttrib2f(ProgramCreator.MC_MID_TEX_COORD, 0.5f, 0.5f);
GL20.glVertexAttrib4f(ProgramCreator.AT_TANGENT, 1.0f, 0.0f, 0.0f, 1.0f);
GL20.glVertexAttrib4f(ProgramCreator.AT_MIDBLOCK, 0.0f, 0.0f, 0.0f, 0.0f);
}
public static WorldRenderingPhase getCurrentPhase() {
@@ -1,10 +1,16 @@
package net.coderbot.iris.pipeline;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.mixins.interfaces.EntityRendererAccessor;
import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectMaps;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import lombok.Getter;
import net.coderbot.iris.Iris;
import net.coderbot.iris.gl.texture.GlTexture;
import net.coderbot.iris.gl.texture.TextureAccess;
import net.coderbot.iris.gl.texture.TextureType;
import net.coderbot.iris.gl.texture.TextureWrapper;
import net.coderbot.iris.rendertarget.NativeImageBackedCustomTexture;
import net.coderbot.iris.rendertarget.NativeImageBackedNoiseTexture;
import net.coderbot.iris.shaderpack.PackDirectives;
@@ -29,24 +35,25 @@ import java.util.ArrayList;
import java.util.EnumMap;
import java.util.List;
import java.util.Optional;
import java.util.function.IntSupplier;
public class CustomTextureManager {
private final EnumMap<TextureStage, Object2ObjectMap<String, IntSupplier>> customTextureIdMap = new EnumMap<>(TextureStage.class);
private final IntSupplier noise;
@Getter private final EnumMap<TextureStage, Object2ObjectMap<String, TextureAccess>> customTextureIdMap = new EnumMap<>(TextureStage.class);
@Getter private final Object2ObjectMap<String, TextureAccess> irisCustomTextures = new Object2ObjectOpenHashMap<>();
private final TextureAccess noise;
/**
* List of all OpenGL texture objects owned by this CustomTextureManager that need to be deleted in order to avoid
* leaks.
* List of all OpenGL texture objects owned by this CustomTextureManager that need to be deleted in order to avoid leaks.
* Make sure any textures added to this list call releaseId from the close method.
*/
private final List<AbstractTexture> ownedTextures = new ArrayList<>();
private final List<GlTexture> ownedRawTextures = new ArrayList<>();
public CustomTextureManager(PackDirectives packDirectives,
EnumMap<TextureStage, Object2ObjectMap<String, CustomTextureData>> customTextureDataMap,
Object2ObjectMap<String, CustomTextureData> irisCustomTextureDataMap,
Optional<CustomTextureData> customNoiseTextureData) {
customTextureDataMap.forEach((textureStage, customTextureStageDataMap) -> {
Object2ObjectMap<String, IntSupplier> customTextureIds = new Object2ObjectOpenHashMap<>();
final Object2ObjectMap<String, TextureAccess> customTextureIds = new Object2ObjectOpenHashMap<>();
customTextureStageDataMap.forEach((samplerName, textureData) -> {
try {
@@ -60,6 +67,14 @@ public class CustomTextureManager {
customTextureIdMap.put(textureStage, customTextureIds);
});
irisCustomTextureDataMap.forEach((name, texture) -> {
try {
irisCustomTextures.put(name, createCustomTexture(texture));
} catch (IOException e) {
Iris.logger.error("Unable to parse the image data for the custom texture on sampler " + name, e);
}
});
noise = customNoiseTextureData.flatMap(textureData -> {
try {
return Optional.of(createCustomTexture(textureData));
@@ -71,75 +86,85 @@ public class CustomTextureManager {
}).orElseGet(() -> {
final int noiseTextureResolution = packDirectives.getNoiseTextureResolution();
AbstractTexture texture = new NativeImageBackedNoiseTexture(noiseTextureResolution);
final AbstractTexture texture = new NativeImageBackedNoiseTexture(noiseTextureResolution);
ownedTextures.add(texture);
return texture::getGlTextureId;
return new TextureWrapper(texture::getGlTextureId, TextureType.TEXTURE_2D);
});
}
private IntSupplier createCustomTexture(CustomTextureData textureData) throws IOException {
private TextureAccess createCustomTexture(CustomTextureData textureData) throws IOException {
if (textureData instanceof CustomTextureData.PngData) {
AbstractTexture texture = new NativeImageBackedCustomTexture((CustomTextureData.PngData) textureData);
final AbstractTexture texture = new NativeImageBackedCustomTexture((CustomTextureData.PngData) textureData);
ownedTextures.add(texture);
return texture::getGlTextureId;
}
// TODO: Iris
// else if (textureData instanceof CustomTextureData.LightmapMarker) {
// // Special code path for the light texture. While shader packs hardcode the primary light texture, it's
// // possible that a mod will create a different light texture, so this code path is robust to that.
// return () -> ((LightTextureAccessor) Minecraft.getMinecraft().gameRenderer.lightTexture()).getLightTexture().getId();
// }
else if (textureData instanceof CustomTextureData.ResourceData) {
CustomTextureData.ResourceData resourceData = (CustomTextureData.ResourceData) textureData;
String namespace = resourceData.getNamespace();
return new TextureWrapper(texture::getGlTextureId, TextureType.TEXTURE_2D);
} else if (textureData instanceof CustomTextureData.LightmapMarker) {
// Special code path for the light texture. While shader packs hardcode the primary light texture, it's possible that a mod will
// create a different light texture, so this code path is robust to that.
return new TextureWrapper(() -> ((EntityRendererAccessor) Minecraft.getMinecraft().entityRenderer).getLightmapTexture().getGlTextureId(), TextureType.TEXTURE_2D);
} else if (textureData instanceof CustomTextureData.RawData1D rawData) {
final GlTexture texture = new GlTexture(TextureType.TEXTURE_1D, rawData.getSizeX(), 0, 0, rawData.getInternalFormat().getGlFormat(), rawData.getPixelFormat().getGlFormat(), rawData.getPixelType().getGlFormat(), rawData.getContent(), rawData.getFilteringData());
ownedRawTextures.add(texture);
return texture;
} else if (textureData instanceof CustomTextureData.RawDataRect rawData) {
final GlTexture texture = new GlTexture(TextureType.TEXTURE_RECTANGLE, rawData.getSizeX(), rawData.getSizeY(), 0, rawData.getInternalFormat().getGlFormat(), rawData.getPixelFormat().getGlFormat(), rawData.getPixelType().getGlFormat(), rawData.getContent(), rawData.getFilteringData());
ownedRawTextures.add(texture);
return texture;
} else if (textureData instanceof CustomTextureData.RawData2D rawData) {
final GlTexture texture = new GlTexture(TextureType.TEXTURE_2D, rawData.getSizeX(), rawData.getSizeY(), 0, rawData.getInternalFormat().getGlFormat(), rawData.getPixelFormat().getGlFormat(), rawData.getPixelType().getGlFormat(), rawData.getContent(), rawData.getFilteringData());
ownedRawTextures.add(texture);
return texture;
} else if (textureData instanceof CustomTextureData.RawData3D rawData) {
final GlTexture texture = new GlTexture(TextureType.TEXTURE_3D, rawData.getSizeX(), rawData.getSizeY(), rawData.getSizeZ(), rawData.getInternalFormat().getGlFormat(), rawData.getPixelFormat().getGlFormat(), rawData.getPixelType().getGlFormat(), rawData.getContent(), rawData.getFilteringData());
ownedRawTextures.add(texture);
return texture;
} else if (textureData instanceof CustomTextureData.ResourceData resourceData) {
final String namespace = resourceData.getNamespace();
String location = resourceData.getLocation();
String withoutExtension;
int extensionIndex = FilenameUtils.indexOfExtension(location);
final String withoutExtension;
final int extensionIndex = FilenameUtils.indexOfExtension(location);
if (extensionIndex != -1) {
withoutExtension = location.substring(0, extensionIndex);
} else {
withoutExtension = location;
}
PBRType pbrType = PBRType.fromFileLocation(withoutExtension);
final PBRType pbrType = PBRType.fromFileLocation(withoutExtension);
TextureManager textureManager = Minecraft.getMinecraft().getTextureManager();
final TextureManager textureManager = Minecraft.getMinecraft().getTextureManager();
if (pbrType == null) {
ResourceLocation textureLocation = new ResourceLocation(namespace, location);
final ResourceLocation textureLocation = new ResourceLocation(namespace, location);
// NB: We have to re-query the TextureManager for the texture object every time. This is because the
// AbstractTexture object could be removed / deleted from the TextureManager on resource reloads,
// and we could end up holding on to a deleted texture unless we added special code to handle resource
// reloads. Re-fetching the texture from the TextureManager every time is the most robust approach for
// now.
return () -> {
ITextureObject texture = textureManager.getTexture(textureLocation);
// TODO: Should we give something else if the texture isn't there? This will need some thought
return new TextureWrapper(() -> {
final ITextureObject texture = textureManager.getTexture(textureLocation);
return texture != null ? texture.getGlTextureId() : TextureUtil.missingTexture.getGlTextureId();
};
}, TextureType.TEXTURE_2D);
} else {
location = location.substring(0, extensionIndex - pbrType.getSuffix().length()) + location.substring(extensionIndex);
ResourceLocation textureLocation = new ResourceLocation(namespace, location);
final ResourceLocation textureLocation = new ResourceLocation(namespace, location);
return () -> {
ITextureObject texture = textureManager.getTexture(textureLocation);
return new TextureWrapper(() -> {
final ITextureObject texture = textureManager.getTexture(textureLocation);
if (texture != null) {
int id = texture.getGlTextureId();
PBRTextureHolder pbrHolder = PBRTextureManager.INSTANCE.getOrLoadHolder(id);
AbstractTexture pbrTexture = switch (pbrType) {
final int id = texture.getGlTextureId();
final PBRTextureHolder pbrHolder = PBRTextureManager.INSTANCE.getOrLoadHolder(id);
final AbstractTexture pbrTexture = switch (pbrType) {
case NORMAL -> pbrHolder.getNormalTexture();
case SPECULAR -> pbrHolder.getSpecularTexture();
default -> throw new Error("Unknown PBRType '" + pbrType + "'");
default -> throw new IllegalArgumentException("Unknown PBRType '" + pbrType + "'");
};
TextureFormat textureFormat = TextureFormatLoader.getFormat();
final TextureFormat textureFormat = TextureFormatLoader.getFormat();
if (textureFormat != null) {
int previousBinding = GL11.glGetInteger(GL11.GL_TEXTURE_BINDING_2D);
final int previousBinding = GL11.glGetInteger(GL11.GL_TEXTURE_BINDING_2D);
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, pbrTexture.getGlTextureId());
textureFormat.setupTextureParameters(pbrType, pbrTexture);
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, previousBinding);
@@ -148,29 +173,26 @@ public class CustomTextureManager {
return pbrTexture.getGlTextureId();
}
// return MissingTextureAtlasSprite.getTexture().getId();
return TextureUtil.missingTexture.getGlTextureId();
};
}, TextureType.TEXTURE_2D);
}
} else {
throw new IllegalArgumentException("Unable to handle custom texture data " + textureData);
}
throw new IllegalArgumentException("Unable to handle custom texture data " + textureData);
}
public EnumMap<TextureStage, Object2ObjectMap<String, IntSupplier>> getCustomTextureIdMap() {
return customTextureIdMap;
}
public Object2ObjectMap<String, IntSupplier> getCustomTextureIdMap(TextureStage stage) {
public Object2ObjectMap<String, TextureAccess> getCustomTextureIdMap(TextureStage stage) {
return customTextureIdMap.getOrDefault(stage, Object2ObjectMaps.emptyMap());
}
public IntSupplier getNoiseTexture() {
public TextureAccess getNoiseTexture() {
return noise;
}
public void destroy() {
// TODO :Iris
// ownedTextures.forEach(AbstractTexture::close);
public void destroy() {
ownedTextures.forEach(AbstractTexture::deleteGlTexture);
ownedTextures.clear();
ownedRawTextures.forEach(GlTexture::destroy);
ownedRawTextures.clear();
}
}
@@ -5,11 +5,18 @@ import com.google.common.collect.ImmutableSet;
import com.google.common.primitives.Ints;
import com.gtnewhorizons.angelica.compat.mojang.Camera;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.RenderSystem;
import com.gtnewhorizons.angelica.glsm.texture.TextureInfoCache;
import com.gtnewhorizons.angelica.rendering.RenderingState;
import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap;
import net.coderbot.iris.gl.buffer.ShaderStorageBufferHolder;
import net.coderbot.iris.gl.buffer.ShaderStorageInfo;
import net.coderbot.iris.gl.image.GlImage;
import net.coderbot.iris.gl.image.ImageInformation;
import net.coderbot.iris.Iris;
import net.coderbot.iris.block_rendering.BlockMaterialMapping;
import net.coderbot.iris.celeritas.CeleritasTerrainPipeline;
import net.coderbot.iris.compat.dh.DHCompat;
import net.coderbot.iris.block_rendering.BlockRenderingSettings;
import net.coderbot.iris.gbuffer_overrides.matching.InputAvailability;
import net.coderbot.iris.gbuffer_overrides.matching.ProgramTable;
@@ -131,6 +138,13 @@ public class DeferredWorldRenderingPipeline implements WorldRenderingPipeline, R
private final ImmutableSet<Integer> flippedAfterTranslucent;
private final CeleritasTerrainPipeline celeritasTerrainPipeline;
private final DHCompat dhCompat;
// Custom images and SSBOs
private final Set<GlImage> customImages;
private final GlImage[] imagesToClear;
@Nullable
private final ShaderStorageBufferHolder ssboHolder;
private final Map<Pair<String, InputAvailability>, Map<PatchShaderType, String>> attributeTransforms;
@@ -194,7 +208,9 @@ public class DeferredWorldRenderingPipeline implements WorldRenderingPipeline, R
this.shouldRenderSun = programs.getPackDirectives().shouldRenderSun();
this.shouldRenderMoon = programs.getPackDirectives().shouldRenderMoon();
this.shouldWriteRainAndSnowToDepthBuffer = programs.getPackDirectives().rainDepth();
this.shouldRenderParticlesBeforeDeferred = programs.getPackDirectives().areParticlesBeforeDeferred();
this.shouldRenderParticlesBeforeDeferred = programs.getPackDirectives().getParticleRenderingSettings()
.map(s -> s == net.coderbot.iris.shaderpack.ParticleRenderingSettings.BEFORE || s == net.coderbot.iris.shaderpack.ParticleRenderingSettings.MIXED)
.orElse(false);
this.allowConcurrentCompute = programs.getPackDirectives().getConcurrentCompute();
this.shouldRenderPrepareBeforeShadow = programs.getPackDirectives().isPrepareBeforeShadow();
this.oldLighting = programs.getPackDirectives().isOldLighting();
@@ -246,10 +262,46 @@ public class DeferredWorldRenderingPipeline implements WorldRenderingPipeline, R
// Don't clobber anything in texture unit 0. It probably won't cause issues, but we're just being cautious here.
GLStateManager.glActiveTexture(GL13.GL_TEXTURE2);
customTextureManager = new CustomTextureManager(programs.getPackDirectives(), programs.getPack().getCustomTextureDataMap(), programs.getPack().getCustomNoiseTexture());
customTextureManager = new CustomTextureManager(programs.getPackDirectives(), programs.getPack().getCustomTextureDataMap(), programs.getPack().getIrisCustomTextureDataMap(), programs.getPack().getCustomNoiseTexture());
whitePixel = new NativeImageBackedSingleColorTexture(255, 255, 255, 255);
// Initialize custom images
this.customImages = new HashSet<>();
final var customImageInfos = programs.getPack().getCustomImages();
Iris.logger.debug("[CustomImages] Found {} custom image definitions from shader pack", customImageInfos.size());
final List<GlImage> clearList = new ArrayList<>();
for (var entry : customImageInfos.object2ObjectEntrySet()) {
ImageInformation info = entry.getValue();
Iris.logger.debug("[CustomImages] Creating GlImage: {} with info: {}", entry.getKey(), info);
GlImage image;
if (info.isRelative()) {
image = new GlImage.Relative(info.name(), info.samplerName(), info.format(), info.internalTextureFormat(),
info.type(), info.clear(), info.relativeWidth(), info.relativeHeight(),
main.framebufferWidth, main.framebufferHeight);
} else {
image = new GlImage(info.name(), info.samplerName(), info.target(), info.format(), info.internalTextureFormat(),
info.type(), info.clear(), info.width(), info.height(), info.depth());
}
Iris.logger.debug("[CustomImages] Created GlImage: {} with texture ID: {}", entry.getKey(), image.getId());
customImages.add(image);
if (image.shouldClear()) {
clearList.add(image);
}
}
this.imagesToClear = clearList.toArray(new GlImage[0]);
Iris.logger.debug("[CustomImages] Total GlImages created: {}, images to clear: {}", customImages.size(), imagesToClear.length);
// Initialize SSBOs
final var bufferObjectInfos = programs.getPack().getBufferObjects();
if (!bufferObjectInfos.isEmpty() && RenderSystem.supportsSSBO()) {
Int2ObjectArrayMap<ShaderStorageInfo> ssboOverrides = new Int2ObjectArrayMap<>();
bufferObjectInfos.forEach(ssboOverrides::put);
this.ssboHolder = new ShaderStorageBufferHolder(ssboOverrides, main.framebufferWidth, main.framebufferHeight);
} else {
this.ssboHolder = null;
}
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
this.flippedBeforeShadow = ImmutableSet.of();
@@ -270,9 +322,9 @@ public class DeferredWorldRenderingPipeline implements WorldRenderingPipeline, R
PatchedShaderPrinter.resetPrintState();
final ProgramBuildContext prepareBuildContext = new ProgramBuildContext(renderTargets, customTextureManager.getNoiseTexture(), updateNotifier, centerDepthSampler, shadowTargetsSupplier, customTextureManager.getCustomTextureIdMap(TextureStage.PREPARE), customUniforms);
final ProgramBuildContext deferredBuildContext = new ProgramBuildContext(renderTargets, customTextureManager.getNoiseTexture(), updateNotifier, centerDepthSampler, shadowTargetsSupplier, customTextureManager.getCustomTextureIdMap(TextureStage.DEFERRED), customUniforms);
final ProgramBuildContext compositeBuildContext = new ProgramBuildContext(renderTargets, customTextureManager.getNoiseTexture(), updateNotifier, centerDepthSampler, shadowTargetsSupplier, customTextureManager.getCustomTextureIdMap(TextureStage.COMPOSITE_AND_FINAL), customUniforms);
final ProgramBuildContext prepareBuildContext = new ProgramBuildContext(renderTargets, customTextureManager.getNoiseTexture(), updateNotifier, centerDepthSampler, shadowTargetsSupplier, customTextureManager.getCustomTextureIdMap(TextureStage.PREPARE), customUniforms, customImages, customTextureManager.getIrisCustomTextures(), this);
final ProgramBuildContext deferredBuildContext = new ProgramBuildContext(renderTargets, customTextureManager.getNoiseTexture(), updateNotifier, centerDepthSampler, shadowTargetsSupplier, customTextureManager.getCustomTextureIdMap(TextureStage.DEFERRED), customUniforms, customImages, customTextureManager.getIrisCustomTextures(), this);
final ProgramBuildContext compositeBuildContext = new ProgramBuildContext(renderTargets, customTextureManager.getNoiseTexture(), updateNotifier, centerDepthSampler, shadowTargetsSupplier, customTextureManager.getCustomTextureIdMap(TextureStage.COMPOSITE_AND_FINAL), customUniforms, customImages, customTextureManager.getIrisCustomTextures(), this);
this.prepareRenderer = new CompositeRenderer(programs.getPrepare(), programs.getPrepareCompute(), flipper, prepareBuildContext, programs.getPackDirectives().getExplicitFlips("prepare_pre"), prepareTransformFutures, "prepare");
flippedAfterPrepare = flipper.snapshot();
@@ -405,13 +457,18 @@ public class DeferredWorldRenderingPipeline implements WorldRenderingPipeline, R
ProgramSamplers.Builder builder = ProgramSamplers.builder(programId, IrisSamplers.WORLD_RESERVED_TEXTURE_UNITS);
ProgramSamplers.CustomTextureSamplerInterceptor customTextureSamplerInterceptor = ProgramSamplers.customTextureSamplerInterceptor(builder, customTextureManager.getCustomTextureIdMap(TextureStage.GBUFFERS_AND_SHADOW));
IrisSamplers.addRenderTargetSamplers(customTextureSamplerInterceptor, flipped, renderTargets, false);
IrisSamplers.addRenderTargetSamplers(customTextureSamplerInterceptor, flipped, renderTargets, false, this);
IrisSamplers.addLevelSamplers(customTextureSamplerInterceptor, this, whitePixel, new InputAvailability(true, true));
IrisSamplers.addWorldDepthSamplers(customTextureSamplerInterceptor, renderTargets);
IrisSamplers.addNoiseSampler(customTextureSamplerInterceptor, customTextureManager.getNoiseTexture());
// Bind custom images as samplers (for texture() access to voxel_sampler, floodfill_sampler, etc.)
IrisSamplers.addCustomImages(customTextureSamplerInterceptor, customImages);
// Bind custom textures (PNG files from shader pack)
IrisSamplers.addCustomTextures(customTextureSamplerInterceptor, customTextureManager.getIrisCustomTextures());
if (IrisSamplers.hasShadowSamplers(customTextureSamplerInterceptor)) {
IrisSamplers.addShadowSamplers(customTextureSamplerInterceptor, Objects.requireNonNull(shadowRenderTargets));
IrisSamplers.addShadowSamplers(customTextureSamplerInterceptor, Objects.requireNonNull(shadowRenderTargets), null, true);
}
return builder.build();
@@ -421,9 +478,11 @@ public class DeferredWorldRenderingPipeline implements WorldRenderingPipeline, R
ProgramImages.Builder builder = ProgramImages.builder(programId);
IrisImages.addRenderTargetImages(builder, flipped, renderTargets);
// Bind custom images as image units (for imageLoad/imageStore)
IrisImages.addCustomImages(builder, customImages);
if (IrisImages.hasShadowImages(builder)) {
IrisImages.addShadowColorImages(builder, Objects.requireNonNull(shadowRenderTargets));
IrisImages.addShadowColorImages(builder, Objects.requireNonNull(shadowRenderTargets), null);
}
return builder.build();
@@ -433,13 +492,18 @@ public class DeferredWorldRenderingPipeline implements WorldRenderingPipeline, R
ProgramSamplers.Builder builder = ProgramSamplers.builder(programId, IrisSamplers.WORLD_RESERVED_TEXTURE_UNITS);
ProgramSamplers.CustomTextureSamplerInterceptor customTextureSamplerInterceptor = ProgramSamplers.customTextureSamplerInterceptor(builder, customTextureManager.getCustomTextureIdMap(TextureStage.GBUFFERS_AND_SHADOW));
IrisSamplers.addRenderTargetSamplers(customTextureSamplerInterceptor, () -> flippedAfterPrepare, renderTargets, false);
IrisSamplers.addRenderTargetSamplers(customTextureSamplerInterceptor, () -> flippedAfterPrepare, renderTargets, false, this);
IrisSamplers.addLevelSamplers(customTextureSamplerInterceptor, this, whitePixel, new InputAvailability(true, true));
IrisSamplers.addNoiseSampler(customTextureSamplerInterceptor, customTextureManager.getNoiseTexture());
// Bind custom images as samplers (for texture() access to voxel_sampler, floodfill_sampler, etc.)
IrisSamplers.addCustomImages(customTextureSamplerInterceptor, customImages);
// Bind custom textures (PNG files from shader pack)
IrisSamplers.addCustomTextures(customTextureSamplerInterceptor, customTextureManager.getIrisCustomTextures());
// Only initialize these samplers if the shadow map renderer exists. Otherwise, this program shouldn't be used at all?
if (IrisSamplers.hasShadowSamplers(customTextureSamplerInterceptor)) {
IrisSamplers.addShadowSamplers(customTextureSamplerInterceptor, Objects.requireNonNull(shadowRenderTargets));
IrisSamplers.addShadowSamplers(customTextureSamplerInterceptor, Objects.requireNonNull(shadowRenderTargets), null, true);
}
return builder.build();
@@ -448,9 +512,11 @@ public class DeferredWorldRenderingPipeline implements WorldRenderingPipeline, R
ProgramImages.Builder builder = ProgramImages.builder(programId);
IrisImages.addRenderTargetImages(builder, () -> flippedAfterPrepare, renderTargets);
// Bind custom images as image units (for imageLoad/imageStore - voxelization uses this in shadow pass)
IrisImages.addCustomImages(builder, customImages);
if (IrisImages.hasShadowImages(builder)) {
IrisImages.addShadowColorImages(builder, Objects.requireNonNull(shadowRenderTargets));
IrisImages.addShadowColorImages(builder, Objects.requireNonNull(shadowRenderTargets), null);
}
return builder.build();
@@ -471,6 +537,8 @@ public class DeferredWorldRenderingPipeline implements WorldRenderingPipeline, R
celeritasTerrainFuture, celeritasTranslucentFuture, celeritasShadowFuture,
renderTargets, flippedAfterPrepare, flippedAfterTranslucent,
celeritasShadowFb, celeritasTerrainDrawBuffers);
this.dhCompat = new DHCompat();
}
private RenderTargets getRenderTargets() {
@@ -674,7 +742,7 @@ public class DeferredWorldRenderingPipeline implements WorldRenderingPipeline, R
ProgramSamplers.customTextureSamplerInterceptor(builder,
customTextureManager.getCustomTextureIdMap(textureStage));
IrisSamplers.addRenderTargetSamplers(customTextureSamplerInterceptor, flipped, renderTargets, false);
IrisSamplers.addRenderTargetSamplers(customTextureSamplerInterceptor, flipped, renderTargets, false, this);
IrisImages.addRenderTargetImages(builder, flipped, renderTargets);
if (!shouldBindPBR) {
@@ -695,8 +763,8 @@ public class DeferredWorldRenderingPipeline implements WorldRenderingPipeline, R
}
if (shadowRenderTargets != null) {
IrisSamplers.addShadowSamplers(customTextureSamplerInterceptor, shadowRenderTargets);
IrisImages.addShadowColorImages(builder, shadowRenderTargets);
IrisSamplers.addShadowSamplers(customTextureSamplerInterceptor, shadowRenderTargets, null, true);
IrisImages.addShadowColorImages(builder, shadowRenderTargets, null);
}
}
@@ -890,6 +958,17 @@ public class DeferredWorldRenderingPipeline implements WorldRenderingPipeline, R
// Destroy custom textures and the static samplers (normals, specular, and noise)
customTextureManager.destroy();
// whitePixel.releaseId();
// Destroy custom images
for (GlImage image : customImages) {
image.destroy();
}
customImages.clear();
// Destroy SSBOs
if (ssboHolder != null) {
ssboHolder.destroyBuffers();
}
}
private static void destroyPasses(ProgramTable<Pass> table) {
@@ -974,6 +1053,16 @@ public class DeferredWorldRenderingPipeline implements WorldRenderingPipeline, R
this.clearPassesFull = ClearPassCreator.createClearPasses(renderTargets, true, packDirectives.getRenderTargetDirectives());
this.clearPasses = ClearPassCreator.createClearPasses(renderTargets, false, packDirectives.getRenderTargetDirectives());
// Resize custom images if needed
for (GlImage image : customImages) {
image.updateNewSize(main.framebufferWidth, main.framebufferHeight);
}
// Resize SSBOs if needed
if (ssboHolder != null) {
ssboHolder.hasResizedScreen(main.framebufferWidth, main.framebufferHeight);
}
}
final ImmutableList<ClearPass> passes;
@@ -1027,7 +1116,7 @@ public class DeferredWorldRenderingPipeline implements WorldRenderingPipeline, R
ProgramSamplers.CustomTextureSamplerInterceptor customTextureSamplerInterceptor =
ProgramSamplers.customTextureSamplerInterceptor(builder, customTextureManager.getCustomTextureIdMap(textureStage));
IrisSamplers.addRenderTargetSamplers(customTextureSamplerInterceptor, flipped, renderTargets, false);
IrisSamplers.addRenderTargetSamplers(customTextureSamplerInterceptor, flipped, renderTargets, false, this);
IrisImages.addRenderTargetImages(builder, flipped, renderTargets);
IrisSamplers.addLevelSamplers(customTextureSamplerInterceptor, this, whitePixel, new InputAvailability(true, true));
@@ -1036,8 +1125,8 @@ public class DeferredWorldRenderingPipeline implements WorldRenderingPipeline, R
if (IrisSamplers.hasShadowSamplers(customTextureSamplerInterceptor)) {
if (shadowRenderTargets != null) {
IrisSamplers.addShadowSamplers(customTextureSamplerInterceptor, shadowRenderTargets);
IrisImages.addShadowColorImages(builder, shadowRenderTargets);
IrisSamplers.addShadowSamplers(customTextureSamplerInterceptor, shadowRenderTargets, null, true);
IrisImages.addShadowColorImages(builder, shadowRenderTargets, null);
}
}
@@ -1045,7 +1134,7 @@ public class DeferredWorldRenderingPipeline implements WorldRenderingPipeline, R
this.customUniforms.mapholderToPass(builder, programs[i]);
programs[i].setWorkGroupInfo(source.getWorkGroupRelative(), source.getWorkGroups());
programs[i].setWorkGroupInfo(source.getWorkGroupRelative(), source.getWorkGroups(), null);
}
}
@@ -1170,6 +1259,11 @@ public class DeferredWorldRenderingPipeline implements WorldRenderingPipeline, R
// Get ready for world rendering
prepareRenderTargets();
// Clear custom images that need clearing each frame
for (GlImage image : imagesToClear) {
image.clear();
}
setPhase(WorldRenderingPhase.SKY);
// Render our horizon box before actual sky rendering to avoid being broken by mods that do weird things
@@ -1227,6 +1321,11 @@ public class DeferredWorldRenderingPipeline implements WorldRenderingPipeline, R
return updateNotifier;
}
@Override
public DHCompat getDHCompat() {
return dhCompat;
}
@Override
public WorldRenderingPhase getPhase() {
if (overridePhase != null) {
@@ -3,6 +3,7 @@ package net.coderbot.iris.pipeline;
import com.gtnewhorizons.angelica.compat.mojang.Camera;
import net.coderbot.iris.block_rendering.BlockRenderingSettings;
import net.coderbot.iris.celeritas.CeleritasTerrainPipeline;
import net.coderbot.iris.compat.dh.DHCompat;
import net.coderbot.iris.gbuffer_overrides.matching.InputAvailability;
import net.coderbot.iris.gbuffer_overrides.matching.SpecialCondition;
import net.coderbot.iris.gbuffer_overrides.state.RenderTargetStateListener;
@@ -123,6 +124,13 @@ public class FixedFunctionWorldRenderingPipeline implements WorldRenderingPipeli
return new FrameUpdateNotifier();
}
private static final DHCompat DH_COMPAT = new DHCompat();
@Override
public DHCompat getDHCompat() {
return DH_COMPAT;
}
@Override
public boolean shouldDisableVanillaEntityShadows() {
return false;
@@ -7,6 +7,7 @@ import com.gtnewhorizons.angelica.compat.toremove.RenderLayer;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.rendering.RenderingState;
import net.coderbot.iris.block_rendering.BlockRenderingSettings;
import net.coderbot.iris.layer.GbufferPrograms;
import net.irisshaders.iris.api.v0.IrisApi;
import net.minecraft.block.Block;
import net.minecraft.client.Minecraft;
@@ -114,6 +115,8 @@ public class HandRenderer {
setupGlState(gameRenderer, camera, tickDelta);
GbufferPrograms.setBlockEntityDefaults();
renderingSolid = true;
mc.entityRenderer.enableLightmap(tickDelta);
mc.entityRenderer.itemRenderer.renderItemInFirstPerson(tickDelta);
@@ -151,6 +154,8 @@ public class HandRenderer {
setupGlState(gameRenderer, camera, tickDelta);
GbufferPrograms.setBlockEntityDefaults();
mc.entityRenderer.enableLightmap(tickDelta);
mc.entityRenderer.itemRenderer.renderItemInFirstPerson(tickDelta);
mc.entityRenderer.disableLightmap(tickDelta);
@@ -17,8 +17,8 @@ import java.util.Objects;
import net.coderbot.iris.Iris;
import net.coderbot.iris.layer.GbufferPrograms;
import net.coderbot.iris.gui.option.IrisVideoSettings;
import net.coderbot.iris.shaderpack.OptionalBoolean;
import net.coderbot.iris.shaderpack.PackDirectives;
import net.coderbot.iris.shaderpack.ShadowCullState;
import net.coderbot.iris.shaderpack.PackShadowDirectives;
import net.coderbot.iris.shaderpack.ProgramSource;
import net.coderbot.iris.shadow.ShadowMatrices;
@@ -79,7 +79,7 @@ public class ShadowRenderer {
private final float intervalSize;
private final Float fov;
private final ShadowRenderTargets targets;
private final OptionalBoolean packCullingState;
private final ShadowCullState packCullingState;
private boolean packHasVoxelization;
private final boolean shouldRenderTerrain;
private final boolean shouldRenderTranslucent;
@@ -141,7 +141,7 @@ public class ShadowRenderer {
this.packCullingState = shadowDirectives.getCullingState();
} else {
this.packHasVoxelization = false;
this.packCullingState = OptionalBoolean.DEFAULT;
this.packCullingState = ShadowCullState.DEFAULT;
}
this.sunPathRotation = directives.getSunPathRotation();
@@ -286,12 +286,12 @@ public class ShadowRenderer {
// TODO: Cull entities / block entities with Advanced Frustum Culling even if voxelization is detected.
String distanceInfo;
String cullingInfo;
if ((packCullingState == OptionalBoolean.FALSE || packHasVoxelization) && packCullingState != OptionalBoolean.TRUE) {
if ((packCullingState == ShadowCullState.DISTANCE || packHasVoxelization) && packCullingState != ShadowCullState.ADVANCED) {
double distance = halfPlaneLength * renderMultiplier;
String reason;
if (packCullingState == OptionalBoolean.FALSE) {
if (packCullingState == ShadowCullState.DISTANCE) {
reason = "(set by shader pack)";
} else /*if (packHasVoxelization)*/ {
reason = "(voxelization detected)";
@@ -1,6 +1,7 @@
package net.coderbot.iris.pipeline;
import com.gtnewhorizons.angelica.compat.mojang.Camera;
import net.coderbot.iris.compat.dh.DHCompat;
import net.coderbot.iris.gbuffer_overrides.matching.InputAvailability;
import net.coderbot.iris.gbuffer_overrides.matching.SpecialCondition;
import net.coderbot.iris.gbuffer_overrides.state.RenderTargetStateListener;
@@ -39,6 +40,7 @@ public interface WorldRenderingPipeline {
CeleritasTerrainPipeline getCeleritasTerrainPipeline();
FrameUpdateNotifier getFrameUpdateNotifier();
DHCompat getDHCompat();
boolean shouldDisableVanillaEntityShadows();
boolean shouldDisableDirectionalShading();
@@ -204,8 +204,12 @@ public class ShaderTransformer {
hacky120Patches.add(transformer);
}
// Extract extensions from the pre-parsed content (version + extensions before main code)
// This preserves #extension directives that the shader pack declares
var extensions = versionPattern.matcher(getFormattedShader(parsedShader.pre(), "")).replaceFirst("").trim();
types.put(type, transformer);
prepatched.put(type, profileString);
prepatched.put(type, profileString + (extensions.isEmpty() ? "" : "\n" + extensions));
}
CompatibilityTransformer.transformGrouped(types, parameters);
for (var entry : types.entrySet()) {
@@ -394,8 +398,10 @@ public class ShaderTransformer {
stringBuilder.deleteCharAt(stringBuilder.length() - 2);
}
tabHolder[0] = "";
stringBuilder.append(" \n");
} else {
stringBuilder.append(text.equals(";") ? " \n" + tabHolder[0] : " ");
}
stringBuilder.append(text.equals(";") ? " \n" + tabHolder[0] : " ");
} else {
for (int i = 0; i < tree.getChildCount(); ++i) {
getFormattedShader(tree.getChild(i), stringBuilder, tabHolder);
@@ -8,13 +8,17 @@ import com.gtnewhorizons.angelica.glsm.RenderSystem;
import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
import lombok.Getter;
import net.coderbot.iris.gl.framebuffer.GlFramebuffer;
import net.coderbot.iris.gl.framebuffer.ViewportData;
import net.coderbot.iris.gl.image.GlImage;
import net.coderbot.iris.gl.program.ComputeProgram;
import net.coderbot.iris.gl.program.Program;
import net.coderbot.iris.gl.program.ProgramBuilder;
import net.coderbot.iris.gl.program.ProgramSamplers;
import net.coderbot.iris.gl.program.ProgramUniforms;
import net.coderbot.iris.gl.sampler.SamplerLimits;
import net.coderbot.iris.gl.texture.TextureAccess;
import net.coderbot.iris.pipeline.PatchedShaderPrinter;
import net.coderbot.iris.pipeline.WorldRenderingPipeline;
import net.coderbot.iris.pipeline.transform.PatchShaderType;
import net.coderbot.iris.pipeline.transform.TransformPatcher;
import net.coderbot.iris.rendertarget.RenderTarget;
@@ -32,6 +36,7 @@ import net.coderbot.iris.uniforms.custom.CustomUniforms;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.OpenGlHelper;
import net.minecraft.client.shader.Framebuffer;
import org.jetbrains.annotations.Nullable;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL13;
import org.lwjgl.opengl.GL20;
@@ -39,31 +44,34 @@ import org.lwjgl.opengl.GL30;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.function.IntSupplier;
import java.util.function.Supplier;
public class CompositeRenderer {
private final RenderTargets renderTargets;
private final ImmutableList<Pass> passes;
private final IntSupplier noiseTexture;
private final TextureAccess noiseTexture;
private final FrameUpdateNotifier updateNotifier;
private final CenterDepthSampler centerDepthSampler;
private final CustomUniforms customUniforms;
private final Object2ObjectMap<String, IntSupplier> customTextureIds;
private final Object2ObjectMap<String, TextureAccess> customTextureIds;
@Nullable private final Set<GlImage> customImages;
@Nullable private final Object2ObjectMap<String, TextureAccess> irisCustomTextures;
@Nullable private final WorldRenderingPipeline pipeline;
@Getter
private final ImmutableSet<Integer> flippedAtLeastOnceFinal;
public CompositeRenderer(PackDirectives packDirectives, ProgramSource[] sources, ComputeSource[][] computes, RenderTargets renderTargets,
IntSupplier noiseTexture, FrameUpdateNotifier updateNotifier,
TextureAccess noiseTexture, FrameUpdateNotifier updateNotifier,
CenterDepthSampler centerDepthSampler, BufferFlipper bufferFlipper,
Supplier<ShadowRenderTargets> shadowTargetsSupplier,
Object2ObjectMap<String, IntSupplier> customTextureIds, ImmutableMap<Integer, Boolean> explicitPreFlips,
Object2ObjectMap<String, TextureAccess> customTextureIds, ImmutableMap<Integer, Boolean> explicitPreFlips,
CustomUniforms customUniforms,
Map<Integer, CompletableFuture<Map<PatchShaderType, String>>> precomputedTransformFutures) {
this(sources, computes, bufferFlipper, new ProgramBuildContext(renderTargets, noiseTexture, updateNotifier, centerDepthSampler, shadowTargetsSupplier, customTextureIds, customUniforms), explicitPreFlips, precomputedTransformFutures, "unknown");
this(sources, computes, bufferFlipper, new ProgramBuildContext(renderTargets, noiseTexture, updateNotifier, centerDepthSampler, shadowTargetsSupplier, customTextureIds, customUniforms, null, null, null), explicitPreFlips, precomputedTransformFutures, "unknown");
}
public CompositeRenderer(ProgramSource[] sources, ComputeSource[][] computes, BufferFlipper bufferFlipper, ProgramBuildContext context, ImmutableMap<Integer, Boolean> explicitPreFlips, Map<Integer, CompletableFuture<Map<PatchShaderType, String>>> precomputedTransformFutures, String stageName) {
@@ -73,6 +81,9 @@ public class CompositeRenderer {
this.renderTargets = context.renderTargets();
this.customTextureIds = context.customTextureIds();
this.customUniforms = context.customUniforms();
this.customImages = context.customImages();
this.irisCustomTextures = context.irisCustomTextures();
this.pipeline = context.pipeline();
final ImmutableList.Builder<Pass> passes = ImmutableList.builder();
final ImmutableSet.Builder<Integer> flippedAtLeastOnce = new ImmutableSet.Builder<>();
@@ -205,7 +216,7 @@ public class CompositeRenderer {
GlFramebuffer framebuffer;
ImmutableSet<Integer> stageReadsFromAlt;
ImmutableSet<Integer> mipmappedBuffers;
float viewportScale;
ViewportData viewportScale;
protected void destroy() {
this.program.destroy();
@@ -264,9 +275,11 @@ public class CompositeRenderer {
}
}
final float scaledWidth = renderPass.viewWidth * renderPass.viewportScale;
final float scaledHeight = renderPass.viewHeight * renderPass.viewportScale;
GL11.glViewport(0, 0, (int) scaledWidth, (int) scaledHeight);
final float scaledWidth = renderPass.viewWidth * renderPass.viewportScale.scale();
final float scaledHeight = renderPass.viewHeight * renderPass.viewportScale.scale();
final float viewportX = renderPass.viewWidth * renderPass.viewportScale.viewportX();
final float viewportY = renderPass.viewHeight * renderPass.viewportScale.viewportY();
GL11.glViewport((int) viewportX, (int) viewportY, (int) scaledWidth, (int) scaledHeight);
renderPass.framebuffer.bind();
renderPass.program.use();
@@ -345,15 +358,19 @@ public class CompositeRenderer {
ProgramSamplers.CustomTextureSamplerInterceptor customTextureSamplerInterceptor = ProgramSamplers.customTextureSamplerInterceptor(builder, customTextureIds, flippedAtLeastOnceSnapshot);
IrisSamplers.addRenderTargetSamplers(customTextureSamplerInterceptor, () -> flipped, renderTargets, true);
IrisSamplers.addRenderTargetSamplers(customTextureSamplerInterceptor, () -> flipped, renderTargets, true, pipeline);
IrisSamplers.addCustomImages(customTextureSamplerInterceptor, customImages);
IrisSamplers.addCustomTextures(customTextureSamplerInterceptor, irisCustomTextures);
IrisImages.addRenderTargetImages(builder, () -> flipped, renderTargets);
IrisImages.addCustomImages(builder, customImages);
IrisSamplers.addNoiseSampler(customTextureSamplerInterceptor, noiseTexture);
IrisSamplers.addCompositeSamplers(customTextureSamplerInterceptor, renderTargets);
if (IrisSamplers.hasShadowSamplers(customTextureSamplerInterceptor)) {
IrisSamplers.addShadowSamplers(customTextureSamplerInterceptor, shadowTargetsSupplier.get());
IrisImages.addShadowColorImages(builder, shadowTargetsSupplier.get());
IrisSamplers.addShadowSamplers(customTextureSamplerInterceptor, shadowTargetsSupplier.get(), null, false);
IrisImages.addShadowColorImages(builder, shadowTargetsSupplier.get(), null);
}
// TODO: Don't duplicate this with FinalPassRenderer
@@ -390,15 +407,19 @@ public class CompositeRenderer {
this.customUniforms.assignTo(builder);
IrisSamplers.addRenderTargetSamplers(customTextureSamplerInterceptor, () -> flipped, renderTargets, true);
IrisSamplers.addRenderTargetSamplers(customTextureSamplerInterceptor, () -> flipped, renderTargets, true, pipeline);
IrisSamplers.addCustomImages(customTextureSamplerInterceptor, customImages);
IrisSamplers.addCustomTextures(customTextureSamplerInterceptor, irisCustomTextures);
IrisImages.addRenderTargetImages(builder, () -> flipped, renderTargets);
IrisImages.addCustomImages(builder, customImages);
IrisSamplers.addNoiseSampler(customTextureSamplerInterceptor, noiseTexture);
IrisSamplers.addCompositeSamplers(customTextureSamplerInterceptor, renderTargets);
if (IrisSamplers.hasShadowSamplers(customTextureSamplerInterceptor)) {
IrisSamplers.addShadowSamplers(customTextureSamplerInterceptor, shadowTargetsSupplier.get());
IrisImages.addShadowColorImages(builder, shadowTargetsSupplier.get());
IrisSamplers.addShadowSamplers(customTextureSamplerInterceptor, shadowTargetsSupplier.get(), null, false);
IrisImages.addShadowColorImages(builder, shadowTargetsSupplier.get(), null);
}
// TODO: Don't duplicate this with FinalPassRenderer
@@ -408,7 +429,7 @@ public class CompositeRenderer {
customUniforms.mapholderToPass(builder, programs[i]);
programs[i].setWorkGroupInfo(source.getWorkGroupRelative(), source.getWorkGroups());
programs[i].setWorkGroupInfo(source.getWorkGroupRelative(), source.getWorkGroups(), null);
}
}
@@ -7,13 +7,16 @@ import com.gtnewhorizons.angelica.glsm.RenderSystem;
import it.unimi.dsi.fastutil.ints.IntList;
import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
import net.coderbot.iris.gl.framebuffer.GlFramebuffer;
import net.coderbot.iris.gl.image.GlImage;
import net.coderbot.iris.gl.program.ComputeProgram;
import net.coderbot.iris.gl.program.Program;
import net.coderbot.iris.gl.program.ProgramBuilder;
import net.coderbot.iris.gl.program.ProgramSamplers;
import net.coderbot.iris.gl.program.ProgramUniforms;
import net.coderbot.iris.gl.sampler.SamplerLimits;
import net.coderbot.iris.gl.texture.TextureAccess;
import net.coderbot.iris.pipeline.PatchedShaderPrinter;
import net.coderbot.iris.pipeline.WorldRenderingPipeline;
import net.coderbot.iris.pipeline.transform.PatchShaderType;
import net.coderbot.iris.pipeline.transform.TransformPatcher;
import net.coderbot.iris.rendertarget.IRenderTargetExt;
@@ -40,9 +43,9 @@ import org.lwjgl.opengl.GL30;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.function.IntSupplier;
import java.util.function.Supplier;
public class FinalPassRenderer {
@@ -55,20 +58,23 @@ public class FinalPassRenderer {
private final GlFramebuffer colorHolder;
private int lastColorTextureId;
private int lastColorTextureVersion;
private final IntSupplier noiseTexture;
private final TextureAccess noiseTexture;
private final FrameUpdateNotifier updateNotifier;
private final CenterDepthSampler centerDepthSampler;
private final Object2ObjectMap<String, IntSupplier> customTextureIds;
private final Object2ObjectMap<String, TextureAccess> customTextureIds;
private final CustomUniforms customUniforms;
@Nullable private final Set<GlImage> customImages;
@Nullable private final Object2ObjectMap<String, TextureAccess> irisCustomTextures;
@Nullable private final WorldRenderingPipeline pipeline;
public FinalPassRenderer(ProgramSet pack, RenderTargets renderTargets, IntSupplier noiseTexture,
public FinalPassRenderer(ProgramSet pack, RenderTargets renderTargets, TextureAccess noiseTexture,
FrameUpdateNotifier updateNotifier, ImmutableSet<Integer> flippedBuffers,
CenterDepthSampler centerDepthSampler,
Supplier<ShadowRenderTargets> shadowTargetsSupplier,
Object2ObjectMap<String, IntSupplier> customTextureIds,
Object2ObjectMap<String, TextureAccess> customTextureIds,
ImmutableSet<Integer> flippedAtLeastOnce, CustomUniforms customUniforms,
CompletableFuture<Map<PatchShaderType, String>> precomputedTransformFuture) {
this(pack, new ProgramBuildContext(renderTargets, noiseTexture, updateNotifier, centerDepthSampler, shadowTargetsSupplier, customTextureIds, customUniforms), flippedBuffers, flippedAtLeastOnce, precomputedTransformFuture, "final");
this(pack, new ProgramBuildContext(renderTargets, noiseTexture, updateNotifier, centerDepthSampler, shadowTargetsSupplier, customTextureIds, customUniforms, null, null, null), flippedBuffers, flippedAtLeastOnce, precomputedTransformFuture, "final");
}
public FinalPassRenderer(ProgramSet pack, ProgramBuildContext context, ImmutableSet<Integer> flippedBuffers, ImmutableSet<Integer> flippedAtLeastOnce, CompletableFuture<Map<PatchShaderType, String>> precomputedTransformFuture, String stageName) {
@@ -79,6 +85,9 @@ public class FinalPassRenderer {
this.noiseTexture = context.noiseTexture();
this.renderTargets = context.renderTargets();
this.customUniforms = context.customUniforms();
this.customImages = context.customImages();
this.irisCustomTextures = context.irisCustomTextures();
this.pipeline = context.pipeline();
this.finalPass = pack.getCompositeFinal().filter(ProgramSource::isValid).map(source -> {
final Pass pass = new Pass();
final ProgramDirectives directives = source.getDirectives();
@@ -359,14 +368,22 @@ public class FinalPassRenderer {
final ProgramSamplers.CustomTextureSamplerInterceptor customTextureSamplerInterceptor = ProgramSamplers.customTextureSamplerInterceptor(builder, customTextureIds, flippedAtLeastOnceSnapshot);
CommonUniforms.addDynamicUniforms(builder);
IrisSamplers.addRenderTargetSamplers(customTextureSamplerInterceptor, () -> flipped, renderTargets, true);
IrisSamplers.addRenderTargetSamplers(customTextureSamplerInterceptor, () -> flipped, renderTargets, true, pipeline);
// Bind custom images as samplers BEFORE render target images
IrisSamplers.addCustomImages(customTextureSamplerInterceptor, customImages);
// Bind custom textures (PNG files from shader pack)
IrisSamplers.addCustomTextures(customTextureSamplerInterceptor, irisCustomTextures);
IrisImages.addRenderTargetImages(builder, () -> flipped, renderTargets);
// Bind custom images as image units AFTER render target images
IrisImages.addCustomImages(builder, customImages);
IrisSamplers.addNoiseSampler(customTextureSamplerInterceptor, noiseTexture);
IrisSamplers.addCompositeSamplers(customTextureSamplerInterceptor, renderTargets);
if (IrisSamplers.hasShadowSamplers(customTextureSamplerInterceptor)) {
IrisSamplers.addShadowSamplers(customTextureSamplerInterceptor, shadowTargetsSupplier.get());
IrisImages.addShadowColorImages(builder, shadowTargetsSupplier.get());
IrisSamplers.addShadowSamplers(customTextureSamplerInterceptor, shadowTargetsSupplier.get(), null, false);
IrisImages.addShadowColorImages(builder, shadowTargetsSupplier.get(), null);
}
// TODO: Don't duplicate this with CompositeRenderer
@@ -402,15 +419,19 @@ public class FinalPassRenderer {
customUniforms.assignTo(builder);
CommonUniforms.addDynamicUniforms(builder);
IrisSamplers.addRenderTargetSamplers(customTextureSamplerInterceptor, () -> flipped, renderTargets, true);
IrisSamplers.addRenderTargetSamplers(customTextureSamplerInterceptor, () -> flipped, renderTargets, true, pipeline);
IrisSamplers.addCustomImages(customTextureSamplerInterceptor, customImages);
IrisSamplers.addCustomTextures(customTextureSamplerInterceptor, irisCustomTextures);
IrisImages.addRenderTargetImages(builder, () -> flipped, renderTargets);
IrisImages.addCustomImages(builder, customImages);
IrisSamplers.addNoiseSampler(customTextureSamplerInterceptor, noiseTexture);
IrisSamplers.addCompositeSamplers(customTextureSamplerInterceptor, renderTargets);
if (IrisSamplers.hasShadowSamplers(customTextureSamplerInterceptor)) {
IrisSamplers.addShadowSamplers(customTextureSamplerInterceptor, shadowTargetsSupplier.get());
IrisImages.addShadowColorImages(builder, shadowTargetsSupplier.get());
IrisSamplers.addShadowSamplers(customTextureSamplerInterceptor, shadowTargetsSupplier.get(), null, false);
IrisImages.addShadowColorImages(builder, shadowTargetsSupplier.get(), null);
}
// TODO: Don't duplicate this with FinalPassRenderer
@@ -420,7 +441,7 @@ public class FinalPassRenderer {
this.customUniforms.mapholderToPass(builder, programs[i]);
programs[i].setWorkGroupInfo(source.getWorkGroupRelative(), source.getWorkGroups());
programs[i].setWorkGroupInfo(source.getWorkGroupRelative(), source.getWorkGroups(), null);
}
}
@@ -2,22 +2,29 @@ package net.coderbot.iris.postprocess;
import com.github.bsideup.jabel.Desugar;
import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
import net.coderbot.iris.gl.image.GlImage;
import net.coderbot.iris.gl.texture.TextureAccess;
import net.coderbot.iris.pipeline.WorldRenderingPipeline;
import net.coderbot.iris.rendertarget.RenderTargets;
import net.coderbot.iris.shadows.ShadowRenderTargets;
import net.coderbot.iris.uniforms.FrameUpdateNotifier;
import net.coderbot.iris.uniforms.custom.CustomUniforms;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.function.IntSupplier;
import java.util.Set;
import java.util.function.Supplier;
@Desugar
public record ProgramBuildContext(
@NotNull RenderTargets renderTargets,
@NotNull IntSupplier noiseTexture,
@NotNull TextureAccess noiseTexture,
@NotNull FrameUpdateNotifier updateNotifier,
@NotNull CenterDepthSampler centerDepthSampler,
@NotNull Supplier<ShadowRenderTargets> shadowTargetsSupplier,
@NotNull Object2ObjectMap<String, IntSupplier> customTextureIds,
@NotNull CustomUniforms customUniforms
@NotNull Object2ObjectMap<String, TextureAccess> customTextureIds,
@NotNull CustomUniforms customUniforms,
@Nullable Set<GlImage> customImages,
@Nullable Object2ObjectMap<String, TextureAccess> irisCustomTextures,
@Nullable WorldRenderingPipeline pipeline
) {}
@@ -1,12 +1,14 @@
package net.coderbot.iris.samplers;
import com.google.common.collect.ImmutableSet;
import net.coderbot.iris.gl.image.GlImage;
import net.coderbot.iris.gl.image.ImageHolder;
import net.coderbot.iris.gl.texture.InternalTextureFormat;
import net.coderbot.iris.rendertarget.RenderTarget;
import net.coderbot.iris.rendertarget.RenderTargets;
import net.coderbot.iris.shadows.ShadowRenderTargets;
import java.util.Set;
import java.util.function.IntSupplier;
import java.util.function.Supplier;
@@ -16,10 +18,14 @@ public class IrisImages {
for (int i = 0; i < renderTargets.getRenderTargetCount(); i++) {
final int index = i;
final String name = "colorimg" + i;
if (!images.hasImage(name)) continue;
// Note: image bindings *are* impacted by buffer flips.
IntSupplier textureID = () -> {
ImmutableSet<Integer> flippedBuffers = flipped.get();
RenderTarget target = renderTargets.get(index);
final IntSupplier textureID = () -> {
final ImmutableSet<Integer> flippedBuffers = flipped.get();
final RenderTarget target = renderTargets.get(index);
if (flippedBuffers.contains(index)) {
return target.getAltTexture();
@@ -29,7 +35,6 @@ public class IrisImages {
};
final InternalTextureFormat internalFormat = renderTargets.get(i).getInternalFormat();
final String name = "colorimg" + i;
images.addTextureImage(textureID, internalFormat, name);
}
@@ -37,17 +42,43 @@ public class IrisImages {
public static boolean hasShadowImages(ImageHolder images) {
// TODO: Generalize
if (images == null) {
return false;
}
return images.hasImage("shadowcolorimg0") || images.hasImage("shadowcolorimg1");
}
public static void addShadowColorImages(ImageHolder images, ShadowRenderTargets shadowRenderTargets) {
public static boolean hasRenderTargetImages(ImageHolder images, RenderTargets targets) {
for (int i = 0; i < targets.getRenderTargetCount(); i++) {
if (images != null && images.hasImage("colorimg" + i)) {
return true;
}
}
return false;
}
public static void addShadowColorImages(ImageHolder images, ShadowRenderTargets shadowRenderTargets, ImmutableSet<Integer> flipped) {
if (images == null) {
return;
}
for (int i = 0; i < shadowRenderTargets.getNumColorTextures(); i++) {
final int index = i;
IntSupplier textureID = () -> shadowRenderTargets.getColorTextureId(index);
IntSupplier textureID;
if (flipped == null) {
textureID = () -> shadowRenderTargets.getColorTextureId(index);
} else {
textureID = () -> flipped.contains(index) ? shadowRenderTargets.get(index).getAltTexture() : shadowRenderTargets.get(index).getMainTexture();
}
InternalTextureFormat format = shadowRenderTargets.getColorTextureFormat(index);
images.addTextureImage(textureID, format, "shadowcolorimg" + i);
}
}
public static void addCustomImages(ImageHolder images, Set<GlImage> customImages) {
customImages.forEach(image -> {
images.addTextureImage(image::getId, image.getInternalFormat(), image.getName());
});
}
}
@@ -2,16 +2,24 @@ package net.coderbot.iris.samplers;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.gtnewhorizons.angelica.glsm.RenderSystem;
import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
import net.coderbot.iris.gbuffer_overrides.matching.InputAvailability;
import net.coderbot.iris.gl.image.GlImage;
import net.coderbot.iris.gl.sampler.GlSampler;
import net.coderbot.iris.gl.sampler.SamplerHolder;
import net.coderbot.iris.gl.state.StateUpdateNotifiers;
import net.coderbot.iris.gl.texture.TextureAccess;
import net.coderbot.iris.gl.texture.TextureType;
import net.coderbot.iris.pipeline.WorldRenderingPipeline;
import net.coderbot.iris.rendertarget.RenderTarget;
import net.coderbot.iris.rendertarget.RenderTargets;
import net.coderbot.iris.shaderpack.PackRenderTargetDirectives;
import net.coderbot.iris.shaderpack.PackShadowDirectives;
import net.coderbot.iris.shadows.ShadowRenderTargets;
import net.minecraft.client.renderer.texture.AbstractTexture;
import java.util.Set;
import java.util.function.IntSupplier;
import java.util.function.Supplier;
@@ -25,22 +33,35 @@ public class IrisSamplers {
// We need a way to restore these texture bindings.
public static final ImmutableSet<Integer> COMPOSITE_RESERVED_TEXTURE_UNITS = ImmutableSet.of(1);
private static GlSampler SHADOW_SAMPLER_NEAREST;
private static GlSampler SHADOW_SAMPLER_LINEAR;
private IrisSamplers() {
// no construction allowed
}
public static void initRenderer() {
if (!RenderSystem.supportsSamplerObjects()) {
// Leave as null - callers handle null gracefully
return;
}
SHADOW_SAMPLER_NEAREST = new GlSampler(false, false, true, true);
SHADOW_SAMPLER_LINEAR = new GlSampler(true, false, true, true);
}
public static void addRenderTargetSamplers(SamplerHolder samplers, Supplier<ImmutableSet<Integer>> flipped,
RenderTargets renderTargets, boolean isFullscreenPass) {
RenderTargets renderTargets, boolean isFullscreenPass,
WorldRenderingPipeline pipeline) {
// colortex0,1,2,3 are only able to be sampled from fullscreen passes.
// Iris could lift this restriction, though I'm not sure if it could cause issues.
int startIndex = isFullscreenPass ? 0 : 4;
final int startIndex = isFullscreenPass ? 0 : 4;
for (int i = startIndex; i < renderTargets.getRenderTargetCount(); i++) {
final int index = i;
IntSupplier sampler = () -> {
ImmutableSet<Integer> flippedBuffers = flipped.get();
RenderTarget target = renderTargets.get(index);
final IntSupplier texture = () -> {
final ImmutableSet<Integer> flippedBuffers = flipped.get();
final RenderTarget target = renderTargets.get(index);
if (flippedBuffers.contains(index)) {
return target.getAltTexture();
@@ -54,32 +75,43 @@ public class IrisSamplers {
// TODO: How do custom textures interact with aliases?
if (i < PackRenderTargetDirectives.LEGACY_RENDER_TARGETS.size()) {
String legacyName = PackRenderTargetDirectives.LEGACY_RENDER_TARGETS.get(i);
final String legacyName = PackRenderTargetDirectives.LEGACY_RENDER_TARGETS.get(i);
// colortex0 is the default sampler in fullscreen passes
if (i == 0 && isFullscreenPass) {
samplers.addDefaultSampler(sampler, name, legacyName);
samplers.addDefaultSampler(texture, name, legacyName);
} else {
samplers.addDynamicSampler(sampler, name, legacyName);
samplers.addDynamicSampler(texture, name, legacyName);
}
} else {
samplers.addDynamicSampler(sampler, name);
samplers.addDynamicSampler(texture, name);
}
}
// Add the DH depth textures (stub implementation returns 0 until DH is integrated)
if (pipeline != null) {
samplers.addDynamicSampler(TextureType.TEXTURE_2D, () -> pipeline.getDHCompat().getDepthTex(), (GlSampler) null, "dhDepthTex", "dhDepthTex0");
samplers.addDynamicSampler(TextureType.TEXTURE_2D, () -> pipeline.getDHCompat().getDepthTexNoTranslucent(), (GlSampler) null, "dhDepthTex1");
}
}
public static void addNoiseSampler(SamplerHolder samplers, IntSupplier sampler) {
samplers.addDynamicSampler(sampler, "noisetex");
public static void addNoiseSampler(SamplerHolder samplers, TextureAccess sampler) {
samplers.addDynamicSampler(sampler.getTextureId(), "noisetex");
}
public static boolean hasShadowSamplers(SamplerHolder samplers) {
// TODO: Keep this up to date with the actual definitions.
// TODO: Don't query image presence using the sampler interface even though the current underlying implementation
// is the same.
ImmutableList<String> shadowSamplers = ImmutableList.of("shadowtex0", "shadowtex0HW", "shadowtex1", "shadowtex1HW", "shadow", "watershadow",
"shadowcolor", "shadowcolor0", "shadowcolor1", "shadowcolorimg0", "shadowcolorimg1");
final ImmutableList.Builder<String> shadowSamplers = ImmutableList.<String>builder()
.add("shadowtex0", "shadowtex0HW", "shadowtex0DH", "shadowtex1", "shadowtex1HW", "shadowtex1DH", "shadow", "watershadow", "shadowcolor");
for (String samplerName : shadowSamplers) {
for (int i = 0; i < PackShadowDirectives.MAX_SHADOW_COLOR_BUFFERS; i++) {
shadowSamplers.add("shadowcolor" + i);
shadowSamplers.add("shadowcolorimg" + i);
}
for (String samplerName : shadowSamplers.build()) {
if (samplers.hasSampler(samplerName)) {
return true;
}
@@ -88,31 +120,61 @@ public class IrisSamplers {
return false;
}
public static boolean addShadowSamplers(SamplerHolder samplers, ShadowRenderTargets shadowRenderTargets) {
public static boolean addShadowSamplers(SamplerHolder samplers, ShadowRenderTargets shadowRenderTargets, ImmutableSet<Integer> flipped, boolean separateHardwareSamplers) {
boolean usesShadows;
// TODO: figure this out from parsing the shader source code to be 100% compatible with the legacy
// shader packs that rely on this behavior.
boolean waterShadowEnabled = samplers.hasSampler("watershadow");
final boolean waterShadowEnabled = samplers.hasSampler("watershadow");
// Get the appropriate sampler for hardware filtering
// If separateHardwareSamplers is true, we don't apply HW filtering to the main samplers
final GlSampler sampler0 = separateHardwareSamplers ? null : (shadowRenderTargets.isHardwareFiltered(0) ? (shadowRenderTargets.isLinearFiltered(0) ? SHADOW_SAMPLER_LINEAR : SHADOW_SAMPLER_NEAREST) : null);
final GlSampler sampler1 = separateHardwareSamplers ? null : (shadowRenderTargets.isHardwareFiltered(1) ? (shadowRenderTargets.isLinearFiltered(1) ? SHADOW_SAMPLER_LINEAR : SHADOW_SAMPLER_NEAREST) : null);
if (waterShadowEnabled) {
usesShadows = true;
samplers.addDynamicSampler(shadowRenderTargets.getDepthTexture()::getTextureId, "shadowtex0", "watershadow");
samplers.addDynamicSampler(shadowRenderTargets.getDepthTextureNoTranslucents()::getTextureId, "shadowtex1", "shadow");
samplers.addDynamicSampler(TextureType.TEXTURE_2D, shadowRenderTargets.getDepthTexture()::getTextureId, sampler0, "shadowtex0", "watershadow");
samplers.addDynamicSampler(TextureType.TEXTURE_2D, shadowRenderTargets.getDepthTextureNoTranslucents()::getTextureId, sampler1, "shadowtex1", "shadow");
} else {
usesShadows = samplers.addDynamicSampler(shadowRenderTargets.getDepthTexture()::getTextureId, "shadowtex0", "shadow");
usesShadows |= samplers.addDynamicSampler(shadowRenderTargets.getDepthTextureNoTranslucents()::getTextureId, "shadowtex1");
usesShadows = samplers.addDynamicSampler(TextureType.TEXTURE_2D, shadowRenderTargets.getDepthTexture()::getTextureId, sampler0, "shadowtex0", "shadow");
usesShadows |= samplers.addDynamicSampler(TextureType.TEXTURE_2D, shadowRenderTargets.getDepthTextureNoTranslucents()::getTextureId, sampler1, "shadowtex1");
}
samplers.addDynamicSampler(() -> shadowRenderTargets.getColorTextureId(0), "shadowcolor", "shadowcolor0");
samplers.addDynamicSampler(() -> shadowRenderTargets.getColorTextureId(1), "shadowcolor1");
if (shadowRenderTargets.isHardwareFiltered(0)) {
samplers.addDynamicSampler(shadowRenderTargets.getDepthTexture()::getTextureId, "shadowtex0HW");
// Shadow color textures with flip support
if (flipped == null) {
// No flip tracking - use getColorTextureId which handles internal flip state
if (samplers.addDynamicSampler(() -> shadowRenderTargets.getColorTextureId(0), "shadowcolor")) {
shadowRenderTargets.createIfEmpty(0);
}
for (int i = 0; i < shadowRenderTargets.getRenderTargetCount(); i++) {
final int finalI = i;
if (samplers.addDynamicSampler(() -> shadowRenderTargets.getColorTextureId(finalI), "shadowcolor" + i)) {
shadowRenderTargets.createIfEmpty(finalI);
}
}
} else {
// With flip tracking - check provided flipped set
if (samplers.addDynamicSampler(() -> flipped.contains(0) ? shadowRenderTargets.get(0).getAltTexture() : shadowRenderTargets.get(0).getMainTexture(), "shadowcolor")) {
shadowRenderTargets.createIfEmpty(0);
}
for (int i = 0; i < shadowRenderTargets.getRenderTargetCount(); i++) {
final int finalI = i;
if (samplers.addDynamicSampler(() -> flipped.contains(finalI) ? shadowRenderTargets.get(finalI).getAltTexture() : shadowRenderTargets.get(finalI).getMainTexture(), "shadowcolor" + finalI)) {
shadowRenderTargets.createIfEmpty(finalI);
}
}
}
if (shadowRenderTargets.isHardwareFiltered(1)) {
samplers.addDynamicSampler(shadowRenderTargets.getDepthTextureNoTranslucents()::getTextureId, "shadowtex1HW");
// Hardware-filtered shadow samplers for separate HW sampler access
if (shadowRenderTargets.isHardwareFiltered(0) && separateHardwareSamplers) {
final GlSampler hwSampler0 = shadowRenderTargets.isLinearFiltered(0) ? SHADOW_SAMPLER_LINEAR : SHADOW_SAMPLER_NEAREST;
samplers.addDynamicSampler(TextureType.TEXTURE_2D, shadowRenderTargets.getDepthTexture()::getTextureId, hwSampler0, "shadowtex0HW");
}
if (shadowRenderTargets.isHardwareFiltered(1) && separateHardwareSamplers) {
final GlSampler hwSampler1 = shadowRenderTargets.isLinearFiltered(1) ? SHADOW_SAMPLER_LINEAR : SHADOW_SAMPLER_NEAREST;
samplers.addDynamicSampler(TextureType.TEXTURE_2D, shadowRenderTargets.getDepthTextureNoTranslucents()::getTextureId, hwSampler1, "shadowtex1HW");
}
return usesShadows;
@@ -151,4 +213,18 @@ public class IrisSamplers {
samplers.addDynamicSampler(renderTargets.getDepthTextureNoTranslucents()::getTextureId, "depthtex1");
samplers.addDynamicSampler(renderTargets.getDepthTextureNoHand()::getTextureId, "depthtex2");
}
public static void addCustomTextures(SamplerHolder samplers, Object2ObjectMap<String, TextureAccess> irisCustomTextures) {
irisCustomTextures.forEach((name, texture) -> {
samplers.addDynamicSampler(texture.getType(), texture.getTextureId(), name);
});
}
public static void addCustomImages(SamplerHolder images, Set<GlImage> customImages) {
customImages.forEach(image -> {
if (image.getSamplerName() != null) {
images.addDynamicSampler(image.getTarget(), image::getId, image.getSamplerName());
}
});
}
}
@@ -1,56 +1,40 @@
package net.coderbot.iris.shaderpack;
import lombok.Getter;
import lombok.Setter;
import org.joml.Vector2f;
import org.joml.Vector3i;
import java.util.Optional;
public class ComputeSource {
private final String name;
@Getter private final String name;
private final String source;
private final ProgramSet parent;
private Vector3i workGroups;
private Vector2f workGroupRelative;
@Getter private final ProgramSet parent;
@Getter private final IndirectPointer indirectPointer;
@Getter @Setter private Vector3i workGroups;
@Getter @Setter private Vector2f workGroupRelative;
public ComputeSource(String name, String source, ProgramSet parent) {
this(name, source, parent, null);
}
public ComputeSource(String name, String source, ProgramSet parent, ShaderProperties properties) {
this.name = name;
this.source = source;
this.parent = parent;
this.indirectPointer = properties != null ? properties.getIndirectPointers().get(name) : null;
}
public String getName() {
return name;
}
public Optional<String> getSource() {
public Optional<String> getSource() {
return Optional.ofNullable(source);
}
public ProgramSet getParent() {
return parent;
}
public boolean isValid() {
public boolean isValid() {
return source != null;
}
public void setWorkGroups(Vector3i workGroups) {
this.workGroups = workGroups;
}
public void setWorkGroupRelative(Vector2f workGroupRelative) {
this.workGroupRelative = workGroupRelative;
}
public Vector2f getWorkGroupRelative() {
return workGroupRelative;
}
public Vector3i getWorkGroups() {
return workGroups;
}
public Optional<ComputeSource> requireValid() {
public Optional<ComputeSource> requireValid() {
if (this.isValid()) {
return Optional.of(this);
} else {
@@ -0,0 +1,11 @@
package net.coderbot.iris.shaderpack;
import net.coderbot.iris.gl.buffer.ShaderStorageBufferHolder;
public record FilledIndirectPointer(int buffer, long offset) {
public static FilledIndirectPointer basedOff(ShaderStorageBufferHolder holder, IndirectPointer pointer) {
if (pointer == null || holder == null) return null;
return new FilledIndirectPointer(holder.getBufferIndex(pointer.buffer()), pointer.offset());
}
}
@@ -0,0 +1,4 @@
package net.coderbot.iris.shaderpack;
public record IndirectPointer(int buffer, long offset) {
}
@@ -1,64 +1,69 @@
package net.coderbot.iris.shaderpack;
import com.google.common.collect.ImmutableMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.Object2BooleanMap;
import it.unimi.dsi.fastutil.objects.Object2BooleanMaps;
import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import lombok.Getter;
import net.coderbot.iris.Iris;
import net.coderbot.iris.gl.buffer.ShaderStorageInfo;
import net.coderbot.iris.gl.texture.TextureScaleOverride;
import net.coderbot.iris.gl.texture.TextureType;
import net.coderbot.iris.helpers.Tri;
import net.coderbot.iris.shaderpack.texture.TextureStage;
import org.joml.Vector2i;
import java.util.Optional;
import java.util.Set;
public class PackDirectives {
@Getter
private int noiseTextureResolution;
@Getter
private float sunPathRotation;
@Getter
private float ambientOcclusionLevel;
@Getter
private float wetnessHalfLife;
@Getter
private float drynessHalfLife;
@Getter
private float eyeBrightnessHalfLife;
@Getter
private float centerDepthHalfLife;
@Getter
private CloudSetting cloudSetting;
@Getter private int noiseTextureResolution;
@Getter private float sunPathRotation;
@Getter private float ambientOcclusionLevel;
@Getter private float wetnessHalfLife;
@Getter private float drynessHalfLife;
@Getter private float eyeBrightnessHalfLife;
@Getter private float centerDepthHalfLife;
@Getter private CloudSetting cloudSetting;
@Getter private CloudSetting dhCloudSetting;
private boolean underwaterOverlay;
private boolean vignette;
private boolean sun;
private boolean moon;
private boolean rainDepth;
private boolean separateAo;
@Getter
private boolean oldLighting;
private boolean voxelizeLightBlocks;
private boolean separateEntityDraws;
private boolean frustumCulling;
private boolean occlusionCulling;
@Getter private boolean oldLighting;
private boolean concurrentCompute;
@Getter
private boolean oldHandLight;
private boolean particlesBeforeDeferred;
@Getter
private boolean prepareBeforeShadow;
@Getter private boolean oldHandLight;
@Getter private boolean prepareBeforeShadow;
@Getter private boolean supportsColorCorrection;
@Getter private int fallbackTex;
private Object2ObjectMap<String, Object2BooleanMap<String>> explicitFlips = new Object2ObjectOpenHashMap<>();
private Object2ObjectMap<String, TextureScaleOverride> scaleOverrides = new Object2ObjectOpenHashMap<>();
@Getter private Object2ObjectMap<Tri<String, TextureType, TextureStage>, String> textureMap;
@Getter private Int2ObjectMap<ShaderStorageInfo> bufferObjects;
@Getter private Optional<ParticleRenderingSettings> particleRenderingSettings;
@Getter
private final PackRenderTargetDirectives renderTargetDirectives;
@Getter
private final PackShadowDirectives shadowDirectives;
@Getter private final PackRenderTargetDirectives renderTargetDirectives;
@Getter private final PackShadowDirectives shadowDirectives;
private PackDirectives(Set<Integer> supportedRenderTargets, PackShadowDirectives packShadowDirectives) {
noiseTextureResolution = 256;
sunPathRotation = 0.0F;
supportsColorCorrection = false;
ambientOcclusionLevel = 1.0F;
wetnessHalfLife = 600.0f;
drynessHalfLife = 200.0f;
eyeBrightnessHalfLife = 10.0f;
centerDepthHalfLife = 1.0F;
bufferObjects = new Int2ObjectOpenHashMap<>();
renderTargetDirectives = new PackRenderTargetDirectives(supportedRenderTargets);
shadowDirectives = packShadowDirectives;
}
@@ -66,31 +71,46 @@ public class PackDirectives {
PackDirectives(Set<Integer> supportedRenderTargets, ShaderProperties properties) {
this(supportedRenderTargets, new PackShadowDirectives(properties));
cloudSetting = properties.getCloudSetting();
dhCloudSetting = properties.getDhCloudSetting();
underwaterOverlay = properties.getUnderwaterOverlay().orElse(false);
vignette = properties.getVignette().orElse(false);
sun = properties.getSun().orElse(true);
moon = properties.getMoon().orElse(true);
rainDepth = properties.getRainDepth().orElse(false);
separateAo = false; //properties.getSeparateAo().orElse(false);
voxelizeLightBlocks = properties.getVoxelizeLightBlocks().orElse(false);
separateEntityDraws = properties.getSeparateEntityDraws().orElse(false);
frustumCulling = properties.getFrustumCulling().orElse(true);
occlusionCulling = properties.getOcclusionCulling().orElse(true);
oldLighting = properties.getOldLighting().orElse(false);
fallbackTex = properties.getFallbackTex();
supportsColorCorrection = properties.getSupportsColorCorrection().orElse(false);
concurrentCompute = properties.getConcurrentCompute().orElse(false);
oldHandLight = properties.getOldHandLight().orElse(true);
explicitFlips = properties.getExplicitFlips();
scaleOverrides = properties.getTextureScaleOverrides();
particlesBeforeDeferred = properties.getParticlesBeforeDeferred().orElse(false);
prepareBeforeShadow = properties.getPrepareBeforeShadow().orElse(false);
particleRenderingSettings = properties.getParticleRenderingSettings();
textureMap = properties.getCustomTexturePatching();
bufferObjects = properties.getBufferObjects();
}
PackDirectives(Set<Integer> supportedRenderTargets, PackDirectives directives) {
this(supportedRenderTargets, new PackShadowDirectives(directives.getShadowDirectives()));
cloudSetting = directives.cloudSetting;
separateAo = directives.separateAo;
voxelizeLightBlocks = directives.voxelizeLightBlocks;
separateEntityDraws = directives.separateEntityDraws;
frustumCulling = directives.frustumCulling;
occlusionCulling = directives.occlusionCulling;
oldLighting = directives.oldLighting;
concurrentCompute = directives.concurrentCompute;
explicitFlips = directives.explicitFlips;
scaleOverrides = directives.scaleOverrides;
particlesBeforeDeferred = directives.particlesBeforeDeferred;
prepareBeforeShadow = directives.prepareBeforeShadow;
particleRenderingSettings = directives.particleRenderingSettings;
textureMap = directives.textureMap;
bufferObjects = directives.bufferObjects;
}
public boolean underwaterOverlay() {
@@ -117,8 +137,20 @@ public class PackDirectives {
return separateAo;
}
public boolean areParticlesBeforeDeferred() {
return particlesBeforeDeferred;
public boolean shouldVoxelizeLightBlocks() {
return voxelizeLightBlocks;
}
public boolean shouldUseSeparateEntityDraws() {
return separateEntityDraws;
}
public boolean shouldUseFrustumCulling() {
return frustumCulling;
}
public boolean shouldUseOcclusionCulling() {
return occlusionCulling;
}
public boolean getConcurrentCompute() {
@@ -133,30 +165,17 @@ public class PackDirectives {
renderTargetDirectives.acceptDirectives(directives);
shadowDirectives.acceptDirectives(directives);
directives.acceptConstIntDirective("noiseTextureResolution",
noiseTextureResolution -> this.noiseTextureResolution = noiseTextureResolution);
directives.acceptConstFloatDirective("sunPathRotation",
sunPathRotation -> this.sunPathRotation = sunPathRotation);
directives.acceptConstFloatDirective("ambientOcclusionLevel",
ambientOcclusionLevel -> this.ambientOcclusionLevel = clamp(ambientOcclusionLevel, 0.0f, 1.0f));
directives.acceptConstFloatDirective("wetnessHalflife",
wetnessHalfLife -> this.wetnessHalfLife = wetnessHalfLife);
directives.acceptConstFloatDirective("drynessHalflife",
wetnessHalfLife -> this.wetnessHalfLife = wetnessHalfLife);
directives.acceptConstFloatDirective("eyeBrightnessHalflife",
eyeBrightnessHalfLife -> this.eyeBrightnessHalfLife = eyeBrightnessHalfLife);
directives.acceptConstFloatDirective("centerDepthHalflife",
centerDepthHalfLife -> this.centerDepthHalfLife = centerDepthHalfLife);
directives.acceptConstIntDirective("noiseTextureResolution", noiseTextureResolution -> this.noiseTextureResolution = noiseTextureResolution);
directives.acceptConstFloatDirective("sunPathRotation", sunPathRotation -> this.sunPathRotation = sunPathRotation);
directives.acceptConstFloatDirective("ambientOcclusionLevel", ambientOcclusionLevel -> this.ambientOcclusionLevel = clamp(ambientOcclusionLevel, 0.0f, 1.0f));
directives.acceptConstFloatDirective("wetnessHalflife", wetnessHalfLife -> this.wetnessHalfLife = wetnessHalfLife);
directives.acceptConstFloatDirective("drynessHalflife", wetnessHalfLife -> this.wetnessHalfLife = wetnessHalfLife);
directives.acceptConstFloatDirective("eyeBrightnessHalflife", eyeBrightnessHalfLife -> this.eyeBrightnessHalfLife = eyeBrightnessHalfLife);
directives.acceptConstFloatDirective("centerDepthHalflife", centerDepthHalfLife -> this.centerDepthHalfLife = centerDepthHalfLife);
}
public ImmutableMap<Integer, Boolean> getExplicitFlips(String pass) {
ImmutableMap.Builder<Integer, Boolean> explicitFlips = ImmutableMap.builder();
final ImmutableMap.Builder<Integer, Boolean> explicitFlips = ImmutableMap.builder();
Object2BooleanMap<String> explicitFlipsStr = this.explicitFlips.get(pass);
@@ -180,8 +199,7 @@ public class PackDirectives {
if (index != -1) {
explicitFlips.put(index, shouldFlip);
} else {
Iris.logger.warn("Unknown buffer with ID " + buffer + " specified in flip directive for pass "
+ pass);
Iris.logger.warn("Unknown buffer with ID " + buffer + " specified in flip directive for pass " + pass);
}
});
@@ -1,6 +1,7 @@
package net.coderbot.iris.shaderpack;
import com.google.common.collect.ImmutableList;
import lombok.Getter;
import net.coderbot.iris.Iris;
import net.coderbot.iris.gl.texture.InternalTextureFormat;
import org.joml.Vector4f;
@@ -29,7 +30,7 @@ public class PackShadowDirectives {
private final boolean shouldRenderEntities;
private final boolean shouldRenderPlayer;
private final boolean shouldRenderBlockEntities;
private final OptionalBoolean cullingState;
@Getter private final ShadowCullState cullingState;
private final ImmutableList<DepthSamplingSettings> depthSamplingSettings;
private final ImmutableList<SamplingSettings> colorSamplingSettings;
@@ -159,11 +160,7 @@ public class PackShadowDirectives {
return shouldRenderBlockEntities;
}
public OptionalBoolean getCullingState() {
return cullingState;
}
public OptionalBoolean isShadowEnabled() {
public OptionalBoolean isShadowEnabled() {
return shadowEnabled;
}
@@ -0,0 +1,20 @@
package net.coderbot.iris.shaderpack;
import net.coderbot.iris.Iris;
import java.util.Optional;
public enum ParticleRenderingSettings {
BEFORE,
MIXED,
AFTER;
public static Optional<ParticleRenderingSettings> fromString(String name) {
try {
return Optional.of(ParticleRenderingSettings.valueOf(name));
} catch (IllegalArgumentException e) {
Iris.logger.warn("Invalid particle rendering settings! " + name);
return Optional.empty();
}
}
}
@@ -7,6 +7,7 @@ import it.unimi.dsi.fastutil.booleans.BooleanConsumer;
import net.coderbot.iris.gl.blending.AlphaTestOverride;
import net.coderbot.iris.gl.blending.BlendModeOverride;
import net.coderbot.iris.gl.blending.BufferBlendInformation;
import net.coderbot.iris.gl.framebuffer.ViewportData;
import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
@@ -20,7 +21,7 @@ public class ProgramDirectives {
private static final ImmutableList<String> LEGACY_RENDER_TARGETS = PackRenderTargetDirectives.LEGACY_RENDER_TARGETS;
private final int[] drawBuffers;
private final float viewportScale;
private final ViewportData viewportScale;
@Nullable
private final AlphaTestOverride alphaTestOverride;
@@ -29,7 +30,7 @@ public class ProgramDirectives {
private final ImmutableSet<Integer> mipmappedBuffers;
private final ImmutableMap<Integer, Boolean> explicitFlips;
private ProgramDirectives(int[] drawBuffers, float viewportScale, @Nullable AlphaTestOverride alphaTestOverride,
private ProgramDirectives(int[] drawBuffers, ViewportData viewportScale, @Nullable AlphaTestOverride alphaTestOverride,
Optional<BlendModeOverride> blendModeOverride, List<BufferBlendInformation> bufferBlendInformations, ImmutableSet<Integer> mipmappedBuffers,
ImmutableMap<Integer, Boolean> explicitFlips) {
this.drawBuffers = drawBuffers;
@@ -41,8 +42,7 @@ public class ProgramDirectives {
this.explicitFlips = explicitFlips;
}
ProgramDirectives(ProgramSource source, ShaderProperties properties, Set<Integer> supportedRenderTargets,
@Nullable BlendModeOverride defaultBlendOverride) {
ProgramDirectives(ProgramSource source, ShaderProperties properties, Set<Integer> supportedRenderTargets, @Nullable BlendModeOverride defaultBlendOverride) {
// DRAWBUFFERS is only detected in the fragment shader source code (.fsh).
// If there's no explicit declaration, then by default /* DRAWBUFFERS:0 */ is inferred.
// For SEUS v08 and SEUS v10 to work, this will need to be set to 01234567. However, doing this causes
@@ -50,10 +50,10 @@ public class ProgramDirectives {
// undefined data to be written to colortex7.
//
// TODO: Figure out how to infer the DRAWBUFFERS directive when it is missing.
Optional<CommentDirective> optionalDrawbuffersDirective = findDrawbuffersDirective(source.getFragmentSource());
Optional<CommentDirective> optionalRendertargetsDirective = findRendertargetsDirective(source.getFragmentSource());
final Optional<CommentDirective> optionalDrawbuffersDirective = findDrawbuffersDirective(source.getFragmentSource());
final Optional<CommentDirective> optionalRendertargetsDirective = findRendertargetsDirective(source.getFragmentSource());
Optional<CommentDirective> optionalCommentDirective = getAppliedDirective(optionalDrawbuffersDirective, optionalRendertargetsDirective);
final Optional<CommentDirective> optionalCommentDirective = getAppliedDirective(optionalDrawbuffersDirective, optionalRendertargetsDirective);
drawBuffers = optionalCommentDirective.map(commentDirective -> {
if (commentDirective.getType() == CommentDirective.Type.DRAWBUFFERS) {
return parseDigits(commentDirective.getDirective().toCharArray());
@@ -65,28 +65,28 @@ public class ProgramDirectives {
}).orElse(new int[] { 0 });
if (properties != null) {
viewportScale = properties.getViewportScaleOverrides().getOrDefault(source.getName(), 1.0f);
viewportScale = properties.getViewportScaleOverrides().getOrDefault(source.getName(), ViewportData.defaultValue());
alphaTestOverride = properties.getAlphaTestOverrides().get(source.getName());
BlendModeOverride blendModeOverride = properties.getBlendModeOverrides().get(source.getName());
List<BufferBlendInformation> bufferBlendInformations = properties.getBufferBlendOverrides().get(source.getName());
final BlendModeOverride blendModeOverride = properties.getBlendModeOverrides().get(source.getName());
final List<BufferBlendInformation> bufferBlendInformations = properties.getBufferBlendOverrides().get(source.getName());
this.blendModeOverride = Optional.ofNullable(blendModeOverride != null ? blendModeOverride : defaultBlendOverride);
this.bufferBlendInformations = bufferBlendInformations != null ? bufferBlendInformations : Collections.emptyList();
explicitFlips = source.getParent().getPackDirectives().getExplicitFlips(source.getName());
} else {
viewportScale = 1.0f;
viewportScale = ViewportData.defaultValue();
alphaTestOverride = null;
blendModeOverride = Optional.ofNullable(defaultBlendOverride);
bufferBlendInformations = Collections.emptyList();
explicitFlips = ImmutableMap.of();
}
HashSet<Integer> mipmappedBuffers = new HashSet<>();
DispatchingDirectiveHolder directiveHolder = new DispatchingDirectiveHolder();
final HashSet<Integer> mipmappedBuffers = new HashSet<>();
final DispatchingDirectiveHolder directiveHolder = new DispatchingDirectiveHolder();
supportedRenderTargets.forEach(index -> {
BooleanConsumer mipmapHandler = shouldMipmap -> {
final BooleanConsumer mipmapHandler = shouldMipmap -> {
if (shouldMipmap) {
mipmappedBuffers.add(index);
} else {
@@ -160,7 +160,7 @@ public class ProgramDirectives {
return drawBuffers;
}
public float getViewportScale() {
public ViewportData getViewportScale() {
return viewportScale;
}
@@ -5,11 +5,15 @@ import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.stream.JsonReader;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import lombok.Getter;
import net.coderbot.iris.Iris;
import net.coderbot.iris.features.FeatureFlags;
import net.coderbot.iris.gl.buffer.ShaderStorageInfo;
import net.coderbot.iris.gl.image.ImageInformation;
import net.coderbot.iris.gl.texture.TextureDefinition;
import net.coderbot.iris.shaderpack.include.AbsolutePackPath;
import net.coderbot.iris.shaderpack.include.IncludeGraph;
import net.coderbot.iris.shaderpack.include.IncludeProcessor;
@@ -51,22 +55,19 @@ public class ShaderPack {
public final CustomUniforms.Builder customUniforms;
private final ProgramSet base;
@Nullable
private final ProgramSet overworld;
@Nullable private final ProgramSet overworld;
private final ProgramSet nether;
private final ProgramSet end;
@Getter
private final IdMap idMap;
@Getter
private final LanguageMap languageMap;
@Getter
private final EnumMap<TextureStage, Object2ObjectMap<String, CustomTextureData>> customTextureDataMap = new EnumMap<>(TextureStage.class);
@Getter private final IdMap idMap;
@Getter private final LanguageMap languageMap;
@Getter private final EnumMap<TextureStage, Object2ObjectMap<String, CustomTextureData>> customTextureDataMap = new EnumMap<>(TextureStage.class);
@Getter private final Object2ObjectMap<String, CustomTextureData> irisCustomTextureDataMap = new Object2ObjectOpenHashMap<>();
private final CustomTextureData customNoiseTexture;
@Getter
private final ShaderPackOptions shaderPackOptions;
@Getter
private final OptionMenuContainer menuContainer;
@Getter private final Object2ObjectMap<String, ImageInformation> customImages;
@Getter private final Int2ObjectMap<ShaderStorageInfo> bufferObjects;
@Getter private final ShaderPackOptions shaderPackOptions;
@Getter private final OptionMenuContainer menuContainer;
private final ProfileSet.ProfileResult profile;
private final String profileInfo;
@@ -88,20 +89,14 @@ public class ShaderPack {
Objects.requireNonNull(root);
ImmutableList.Builder<AbsolutePackPath> starts = ImmutableList.builder();
ImmutableList<String> potentialFileNames = ShaderPackSourceNames.POTENTIAL_STARTS;
final ImmutableList.Builder<AbsolutePackPath> starts = ImmutableList.builder();
final ImmutableList<String> potentialFileNames = ShaderPackSourceNames.POTENTIAL_STARTS;
ShaderPackSourceNames.findPresentSources(starts, root, AbsolutePackPath.fromAbsolutePath("/"),
potentialFileNames);
ShaderPackSourceNames.findPresentSources(starts, root, AbsolutePackPath.fromAbsolutePath("/"), potentialFileNames);
boolean hasWorld0 = ShaderPackSourceNames.findPresentSources(starts, root,
AbsolutePackPath.fromAbsolutePath("/world0"), potentialFileNames);
boolean hasNether = ShaderPackSourceNames.findPresentSources(starts, root,
AbsolutePackPath.fromAbsolutePath("/world-1"), potentialFileNames);
boolean hasEnd = ShaderPackSourceNames.findPresentSources(starts, root,
AbsolutePackPath.fromAbsolutePath("/world1"), potentialFileNames);
final boolean hasWorld0 = ShaderPackSourceNames.findPresentSources(starts, root, AbsolutePackPath.fromAbsolutePath("/world0"), potentialFileNames);
final boolean hasNether = ShaderPackSourceNames.findPresentSources(starts, root, AbsolutePackPath.fromAbsolutePath("/world-1"), potentialFileNames);
final boolean hasEnd = ShaderPackSourceNames.findPresentSources(starts, root, AbsolutePackPath.fromAbsolutePath("/world1"), potentialFileNames);
// Read all files and included files recursively
IncludeGraph graph = new IncludeGraph(root, starts.build());
@@ -236,18 +231,30 @@ public class ShaderPack {
shaderProperties.getCustomTextures().forEach((textureStage, customTexturePropertiesMap) -> {
Object2ObjectMap<String, CustomTextureData> innerCustomTextureDataMap = new Object2ObjectOpenHashMap<>();
customTexturePropertiesMap.forEach((samplerName, path) -> {
customTexturePropertiesMap.forEach((samplerName, definition) -> {
try {
innerCustomTextureDataMap.put(samplerName, readTexture(root, path));
innerCustomTextureDataMap.put(samplerName, readTexture(root, definition));
} catch (IOException e) {
Iris.logger.error("Unable to read the custom texture at " + path, e);
Iris.logger.error("Unable to read the custom texture at " + definition.getName(), e);
}
});
customTextureDataMap.put(textureStage, innerCustomTextureDataMap);
});
// Process irisCustomTextures (direct custom textures via customTexture.<name>=<path>)
shaderProperties.getIrisCustomTextures().forEach((samplerName, definition) -> {
try {
irisCustomTextureDataMap.put(samplerName, readTexture(root, definition));
Iris.logger.debug("[CustomTextures] Loaded customTexture.{} from {}", samplerName, definition.getName());
} catch (IOException e) {
Iris.logger.error("Unable to read custom texture for sampler " + samplerName + " at " + definition.getName(), e);
}
});
this.customUniforms = shaderProperties.getCustomUniforms();
this.customImages = shaderProperties.getCustomImages();
this.bufferObjects = shaderProperties.getBufferObjects();
}
private String getCurrentProfileName() {
@@ -278,7 +285,73 @@ public class ShaderPack {
return Optional.of(fileContents);
}
// TODO: Implement raw texture data types
/**
* Reads a texture from a TextureDefinition. Handles both PNG and raw texture types.
*/
public CustomTextureData readTexture(Path root, TextureDefinition definition) throws IOException {
if (definition instanceof TextureDefinition.RawDefinition rawDef) {
return readRawTexture(root, rawDef);
} else {
// PNG definition - use the path-based method
return readTexture(root, definition.getName());
}
}
/**
* Reads a raw texture from a RawDefinition.
*/
private CustomTextureData readRawTexture(Path root, TextureDefinition.RawDefinition rawDef) throws IOException {
String path = rawDef.getName();
if (path.startsWith("/")) {
path = path.substring(1);
}
// Read mcmeta for filtering data
boolean blur = false;
boolean clamp = false;
String mcMetaPath = path + ".mcmeta";
Path mcMetaResolvedPath = root.resolve(mcMetaPath);
if (Files.exists(mcMetaResolvedPath)) {
try {
JsonObject meta = loadMcMeta(mcMetaResolvedPath);
if (meta.get("texture") != null) {
if (meta.get("texture").getAsJsonObject().get("blur") != null) {
blur = meta.get("texture").getAsJsonObject().get("blur").getAsBoolean();
}
if (meta.get("texture").getAsJsonObject().get("clamp") != null) {
clamp = meta.get("texture").getAsJsonObject().get("clamp").getAsBoolean();
}
}
} catch (IOException e) {
Iris.logger.error("Unable to read the custom texture mcmeta at " + mcMetaPath + ", ignoring: " + e);
}
}
byte[] content = Files.readAllBytes(root.resolve(path));
TextureFilteringData filteringData = new TextureFilteringData(blur, clamp);
return switch (rawDef.getTarget()) {
case TEXTURE_1D -> new CustomTextureData.RawData1D(content, filteringData,
rawDef.getInternalFormat(), rawDef.getFormat(), rawDef.getPixelType(),
rawDef.getSizeX());
case TEXTURE_2D -> new CustomTextureData.RawData2D(content, filteringData,
rawDef.getInternalFormat(), rawDef.getFormat(), rawDef.getPixelType(),
rawDef.getSizeX(), rawDef.getSizeY());
case TEXTURE_3D -> new CustomTextureData.RawData3D(content, filteringData,
rawDef.getInternalFormat(), rawDef.getFormat(), rawDef.getPixelType(),
rawDef.getSizeX(), rawDef.getSizeY(), rawDef.getSizeZ());
case TEXTURE_RECTANGLE -> new CustomTextureData.RawDataRect(content, filteringData,
rawDef.getInternalFormat(), rawDef.getFormat(), rawDef.getPixelType(),
rawDef.getSizeX(), rawDef.getSizeY());
};
}
/**
* Reads a texture from a path string (for PNG textures and resource locations).
*/
public CustomTextureData readTexture(Path root, String path) throws IOException {
CustomTextureData customTextureData;
if (path.contains(":")) {
@@ -294,7 +367,6 @@ public class ShaderPack {
customTextureData = new CustomTextureData.ResourceData(parts[0], parts[1]);
}
} else {
// TODO: Make sure the resulting path is within the shaderpack?
if (path.startsWith("/")) {
// NB: This does not guarantee the resulting path is in the shaderpack as a double slash could be used,
// this just fixes shaderpacks like Continuum 2.0.4 that use a leading slash in texture paths
@@ -3,12 +3,20 @@ package net.coderbot.iris.shaderpack;
import com.gtnewhorizons.angelica.glsm.RenderSystem;
import com.gtnewhorizons.angelica.glsm.states.BlendState;
import it.unimi.dsi.fastutil.booleans.BooleanConsumer;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.Object2BooleanMap;
import it.unimi.dsi.fastutil.objects.Object2BooleanOpenHashMap;
import it.unimi.dsi.fastutil.objects.Object2FloatMap;
import it.unimi.dsi.fastutil.objects.Object2FloatOpenHashMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import net.coderbot.iris.gl.buffer.ShaderStorageInfo;
import net.coderbot.iris.gl.framebuffer.ViewportData;
import net.coderbot.iris.gl.image.ImageInformation;
import net.coderbot.iris.gl.texture.InternalTextureFormat;
import net.coderbot.iris.gl.texture.PixelFormat;
import net.coderbot.iris.gl.texture.PixelType;
import net.coderbot.iris.gl.texture.TextureDefinition;
import net.coderbot.iris.gl.texture.TextureType;
import lombok.Getter;
import net.coderbot.iris.Iris;
import net.coderbot.iris.gl.blending.AlphaTest;
@@ -18,6 +26,7 @@ import net.coderbot.iris.gl.blending.BlendModeFunction;
import net.coderbot.iris.gl.blending.BlendModeOverride;
import net.coderbot.iris.gl.blending.BufferBlendInformation;
import net.coderbot.iris.gl.texture.TextureScaleOverride;
import net.coderbot.iris.helpers.Tri;
import net.coderbot.iris.pipeline.PatchedShaderPrinter;
import net.coderbot.iris.shaderpack.option.ShaderPackOptions;
import net.coderbot.iris.shaderpack.preprocessor.PropertiesPreprocessor;
@@ -35,6 +44,7 @@ import java.util.EnumMap;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.Properties;
@@ -47,16 +57,19 @@ import java.util.function.Consumer;
* values in here & the values parsed from shader source code.
*/
public class ShaderProperties {
@Getter private final CustomUniforms.Builder customUniforms = new CustomUniforms.Builder();
@Getter private final CustomUniforms.Builder customUniforms = new CustomUniforms.Builder();
@Getter private CloudSetting cloudSetting = CloudSetting.DEFAULT;
@Getter private CloudSetting dhCloudSetting = CloudSetting.DEFAULT;
@Getter private OptionalBoolean oldHandLight = OptionalBoolean.DEFAULT;
@Getter private OptionalBoolean dynamicHandLight = OptionalBoolean.DEFAULT;
@Getter private OptionalBoolean supportsColorCorrection = OptionalBoolean.DEFAULT;
@Getter private OptionalBoolean oldLighting = OptionalBoolean.DEFAULT;
@Getter private OptionalBoolean shadowTerrain = OptionalBoolean.DEFAULT;
@Getter private OptionalBoolean shadowTranslucent = OptionalBoolean.DEFAULT;
@Getter private OptionalBoolean shadowEntities = OptionalBoolean.DEFAULT;
@Getter private OptionalBoolean shadowPlayer = OptionalBoolean.DEFAULT;
@Getter private OptionalBoolean shadowBlockEntities = OptionalBoolean.DEFAULT;
@Getter private OptionalBoolean shadowLightBlockEntities = OptionalBoolean.DEFAULT;
@Getter private OptionalBoolean underwaterOverlay = OptionalBoolean.DEFAULT;
@Getter private OptionalBoolean sun = OptionalBoolean.DEFAULT;
@Getter private OptionalBoolean moon = OptionalBoolean.DEFAULT;
@@ -69,10 +82,14 @@ public class ShaderProperties {
@Getter private OptionalBoolean concurrentCompute = OptionalBoolean.DEFAULT;
@Getter private OptionalBoolean beaconBeamDepth = OptionalBoolean.DEFAULT;
@Getter private OptionalBoolean separateAo = OptionalBoolean.DEFAULT;
@Getter private OptionalBoolean voxelizeLightBlocks = OptionalBoolean.DEFAULT;
@Getter private OptionalBoolean separateEntityDraws = OptionalBoolean.DEFAULT;
@Getter private OptionalBoolean frustumCulling = OptionalBoolean.DEFAULT;
@Getter private OptionalBoolean shadowCulling = OptionalBoolean.DEFAULT;
@Getter private OptionalBoolean occlusionCulling = OptionalBoolean.DEFAULT;
@Getter private ShadowCullState shadowCulling = ShadowCullState.DEFAULT;
@Getter private OptionalBoolean shadowEnabled = OptionalBoolean.DEFAULT;
@Getter private OptionalBoolean particlesBeforeDeferred = OptionalBoolean.DEFAULT;
@Getter private OptionalBoolean dhShadowEnabled = OptionalBoolean.DEFAULT;
private Optional<ParticleRenderingSettings> particleRenderingSettings = Optional.empty();
@Getter private OptionalBoolean prepareBeforeShadow = OptionalBoolean.DEFAULT;
@Getter private List<String> sliderOptions = new ArrayList<>();
@Getter private final Map<String, List<String>> profiles = new LinkedHashMap<>();
@@ -80,19 +97,26 @@ public class ShaderProperties {
@Getter private final Map<String, List<String>> subScreenOptions = new HashMap<>();
private Integer mainScreenColumnCount = null;
@Getter private final Map<String, Integer> subScreenColumnCount = new HashMap<>();
@Getter private int fallbackTex = 0;
// TODO: private Map<String, String> optifineVersionRequirements;
// TODO: Parse custom uniforms / variables
@Getter private final Object2ObjectMap<String, AlphaTestOverride> alphaTestOverrides = new Object2ObjectOpenHashMap<>();
@Getter private final Object2FloatMap<String> viewportScaleOverrides = new Object2FloatOpenHashMap<>();
@Getter private final Object2ObjectMap<String, ViewportData> viewportScaleOverrides = new Object2ObjectOpenHashMap<>();
@Getter private final Object2ObjectMap<String, TextureScaleOverride> textureScaleOverrides = new Object2ObjectOpenHashMap<>();
@Getter private final Object2ObjectMap<String, BlendModeOverride> blendModeOverrides = new Object2ObjectOpenHashMap<>();
@Getter private final Object2ObjectMap<String, IndirectPointer> indirectPointers = new Object2ObjectOpenHashMap<>();
@Getter private final Object2ObjectMap<String, ArrayList<BufferBlendInformation>> bufferBlendOverrides = new Object2ObjectOpenHashMap<>();
@Getter private final EnumMap<TextureStage, Object2ObjectMap<String, String>> customTextures = new EnumMap<>(TextureStage.class);
@Getter private final EnumMap<TextureStage, Object2ObjectMap<String, TextureDefinition>> customTextures = new EnumMap<>(TextureStage.class);
@Getter private final Object2ObjectMap<Tri<String, TextureType, TextureStage>, String> customTexturePatching = new Object2ObjectOpenHashMap<>();
@Getter private final Object2ObjectMap<String, Object2BooleanMap<String>> explicitFlips = new Object2ObjectOpenHashMap<>();
private String noiseTexturePath = null;
@Getter private Object2ObjectMap<String, String> conditionallyEnabledPrograms = new Object2ObjectOpenHashMap<>();
@Getter private List<String> requiredFeatureFlags = new ArrayList<>();
@Getter private List<String> optionalFeatureFlags = new ArrayList<>();
@Getter private final Object2ObjectMap<String, ImageInformation> customImages = new Object2ObjectOpenHashMap<>();
@Getter private final Int2ObjectMap<ShaderStorageInfo> bufferObjects = new Int2ObjectOpenHashMap<>();
@Getter private final Object2ObjectMap<String, TextureDefinition> irisCustomTextures = new Object2ObjectOpenHashMap<>();
private int customTexAmount;
private ShaderProperties() {
// empty
@@ -139,6 +163,32 @@ public class ShaderProperties {
} else {
Iris.logger.error("Unrecognized clouds setting: " + value);
}
if (dhCloudSetting == CloudSetting.DEFAULT) {
dhCloudSetting = cloudSetting;
}
}
if ("dhClouds".equals(key)) {
if ("off".equals(value)) {
dhCloudSetting = CloudSetting.OFF;
} else if ("on".equals(value) || "fancy".equals(value)) {
dhCloudSetting = CloudSetting.FANCY;
} else {
Iris.logger.error("Unrecognized DH clouds setting (need off, on): " + value);
}
}
if ("shadow.culling".equals(key)) {
if ("false".equals(value)) {
shadowCulling = ShadowCullState.DISTANCE;
} else if ("true".equals(value)) {
shadowCulling = ShadowCullState.ADVANCED;
} else if ("reversed".equals(value)) {
shadowCulling = ShadowCullState.REVERSED;
} else {
Iris.logger.error("Unrecognized shadow culling setting: " + value);
}
}
handleBooleanDirective(key, value, "oldHandLight", bool -> oldHandLight = bool);
@@ -149,6 +199,7 @@ public class ShaderProperties {
handleBooleanDirective(key, value, "shadowEntities", bool -> shadowEntities = bool);
handleBooleanDirective(key, value, "shadowPlayer", bool -> shadowPlayer = bool);
handleBooleanDirective(key, value, "shadowBlockEntities", bool -> shadowBlockEntities = bool);
handleBooleanDirective(key, value, "shadowLightBlockEntities", bool -> shadowLightBlockEntities = bool);
handleBooleanDirective(key, value, "underwaterOverlay", bool -> underwaterOverlay = bool);
handleBooleanDirective(key, value, "sun", bool -> sun = bool);
handleBooleanDirective(key, value, "moon", bool -> moon = bool);
@@ -161,25 +212,50 @@ public class ShaderProperties {
handleBooleanDirective(key, value, "allowConcurrentCompute", bool -> concurrentCompute = bool);
handleBooleanDirective(key, value, "beacon.beam.depth", bool -> beaconBeamDepth = bool);
handleBooleanDirective(key, value, "separateAo", bool -> separateAo = bool);
handleBooleanDirective(key, value, "voxelizeLightBlocks", bool -> voxelizeLightBlocks = bool);
handleBooleanDirective(key, value, "separateEntityDraws", bool -> separateEntityDraws = bool);
handleBooleanDirective(key, value, "frustum.culling", bool -> frustumCulling = bool);
handleBooleanDirective(key, value, "shadow.culling", bool -> shadowCulling = bool);
handleBooleanDirective(key, value, "particles.before.deferred", bool -> particlesBeforeDeferred = bool);
handleBooleanDirective(key, value, "occlusion.culling", bool -> occlusionCulling = bool);
handleBooleanDirective(key, value, "shadow.enabled", bool -> shadowEnabled = bool);
handleBooleanDirective(key, value, "dhShadow.enabled", bool -> dhShadowEnabled = bool);
handleBooleanDirective(key, value, "particles.before.deferred", bool -> {
if (bool.orElse(false) && particleRenderingSettings.isEmpty()) {
particleRenderingSettings = Optional.of(ParticleRenderingSettings.BEFORE);
}
});
handleBooleanDirective(key, value, "prepareBeforeShadow", bool -> prepareBeforeShadow = bool);
handleBooleanDirective(key, value, "supportsColorCorrection", bool -> supportsColorCorrection = bool);
handleIntDirective(key, value, "fallbackTex", val -> fallbackTex = val);
if (key.startsWith("particles.ordering")) {
Optional<ParticleRenderingSettings> settings = ParticleRenderingSettings.fromString(value.trim().toUpperCase(Locale.ROOT));
if (settings.isPresent()) {
particleRenderingSettings = settings;
} else {
throw new RuntimeException("Failed to parse particle rendering order! " + value);
}
}
// TODO: Min optifine versions, shader options layout / appearance / profiles
// TODO: Custom uniforms
handlePassDirective("scale.", key, value, pass -> {
final float scale;
float scale, offsetX = 0.0f, offsetY = 0.0f;
String[] parts = value.split(" ");
try {
scale = Float.parseFloat(value);
} catch (NumberFormatException e) {
scale = Float.parseFloat(parts[0]);
if (parts.length > 1) {
offsetX = Float.parseFloat(parts[1]);
offsetY = Float.parseFloat(parts[2]);
}
} catch (NumberFormatException | ArrayIndexOutOfBoundsException e) {
Iris.logger.error("Unable to parse scale directive for " + pass + ": " + value, e);
return;
}
viewportScaleOverrides.put(pass, scale);
viewportScaleOverrides.put(pass, new ViewportData(scale, offsetX, offsetY));
});
handlePassDirective("size.buffer.", key, value, pass -> {
@@ -291,26 +367,76 @@ public class ShaderProperties {
conditionallyEnabledPrograms.put(program, value);
});
handleTwoArgDirective("texture.", key, value, (stageName, samplerName) -> {
final String[] parts = value.split(" ");
// TODO: Support raw textures
if (parts.length > 1) {
Iris.logger.warn("Custom texture directive for stage " + stageName + ", sampler " + samplerName + " contains more parts than we expected: " + value);
return;
handlePassDirective("indirect.", key, value, pass -> {
try {
String[] locations = value.split(" ");
indirectPointers.put(pass, new IndirectPointer(Integer.parseInt(locations[0]), Long.parseLong(locations[1])));
} catch (NumberFormatException | ArrayIndexOutOfBoundsException e) {
Iris.logger.fatal("Failed to parse indirect command for " + pass + "! " + value);
}
});
final Optional<TextureStage> optionalTextureStage = TextureStage.parse(stageName);
handleTwoArgDirective("texture.", key, value, (stageName, samplerName) -> {
String[] parts = value.split(" ");
// Handle sampler names with dots by taking only the first part
samplerName = samplerName.split("\\.")[0];
final Optional<TextureStage> optionalTextureStage = TextureStage.parse(stageName);
if (!optionalTextureStage.isPresent()) {
Iris.logger.warn("Unknown texture stage " + "\"" + stageName + "\"," + " ignoring custom texture directive for " + key);
return;
}
final TextureStage stage = optionalTextureStage.get();
final TextureStage stage = optionalTextureStage.get();
if (parts.length > 1) {
// Raw texture - create internal sampler name and store mapping
String newSamplerName = "customtex" + customTexAmount;
customTexAmount++;
TextureType type = null;
// Raw texture handling based on number of parts
if (parts.length == 6) {
// 1D texture handling
type = TextureType.TEXTURE_1D;
irisCustomTextures.put(newSamplerName, new TextureDefinition.RawDefinition(parts[0],
TextureType.valueOf(parts[1].toUpperCase(Locale.ROOT)),
InternalTextureFormat.fromString(parts[2]).orElseThrow(IllegalArgumentException::new),
Integer.parseInt(parts[3]), 0, 0,
PixelFormat.fromString(parts[4]).orElseThrow(IllegalArgumentException::new),
PixelType.fromString(parts[5]).orElseThrow(IllegalArgumentException::new)));
} else if (parts.length == 7) {
// 2D texture handling
type = TextureType.valueOf(parts[1].toUpperCase(Locale.ROOT));
irisCustomTextures.put(newSamplerName, new TextureDefinition.RawDefinition(parts[0],
TextureType.valueOf(parts[1].toUpperCase(Locale.ROOT)),
InternalTextureFormat.fromString(parts[2]).orElseThrow(IllegalArgumentException::new),
Integer.parseInt(parts[3]), Integer.parseInt(parts[4]), 0,
PixelFormat.fromString(parts[5]).orElseThrow(IllegalArgumentException::new),
PixelType.fromString(parts[6]).orElseThrow(IllegalArgumentException::new)));
} else if (parts.length == 8) {
// 3D texture handling
type = TextureType.TEXTURE_3D;
irisCustomTextures.put(newSamplerName, new TextureDefinition.RawDefinition(parts[0],
TextureType.valueOf(parts[1].toUpperCase(Locale.ROOT)),
InternalTextureFormat.fromString(parts[2]).orElseThrow(IllegalArgumentException::new),
Integer.parseInt(parts[3]), Integer.parseInt(parts[4]), Integer.parseInt(parts[5]),
PixelFormat.fromString(parts[6]).orElseThrow(IllegalArgumentException::new),
PixelType.fromString(parts[7]).orElseThrow(IllegalArgumentException::new)));
} else {
Iris.logger.warn("Unknown texture directive for " + key + ": " + value);
}
if (type != null) {
customTexturePatching.put(new Tri<>(samplerName, type, stage), newSamplerName);
}
return;
}
// PNG texture
customTextures.computeIfAbsent(stage, _stage -> new Object2ObjectOpenHashMap<>())
.put(samplerName, value);
.put(samplerName, new TextureDefinition.PNGDefinition(value));
});
handleTwoArgDirective("flip.", key, value, (pass, buffer) -> {
@@ -324,6 +450,25 @@ public class ShaderProperties {
handleWhitespacedListDirective(key, value, "iris.features.required", options -> requiredFeatureFlags = options);
handleWhitespacedListDirective(key, value, "iris.features.optional", options -> optionalFeatureFlags = options);
// Parse custom images: image.<name> = <sampler> <pixelFormat> <internalFormat> <pixelType> <clear> <relative> [dimensions...]
handlePassDirective("image.", key, value, imageName -> {
parseCustomImage(imageName, value);
});
// Parse buffer objects: bufferObject.<index> = <size> [relative <scaleX> <scaleY>]
handlePassDirective("bufferObject.", key, value, indexStr -> {
parseBufferObject(indexStr, value);
});
// Parse custom textures: customTexture.<name> = <path> [type format sizeX [sizeY [sizeZ]] pixelFormat pixelType]
handlePassDirective("customTexture.", key, value, samplerName -> {
final TextureDefinition definition = parseTextureDefinition("customTexture." + samplerName, value);
if (definition != null) {
irisCustomTextures.put(samplerName, definition);
Iris.logger.debug("[CustomTextures] Parsed customTexture.{} = {}", samplerName, value);
}
});
handlePassDirective("variable.", key, value, pass -> {
String[] parts = pass.split("\\.");
if (parts.length != 2) {
@@ -497,4 +642,258 @@ public class ShaderProperties {
return Optional.ofNullable(mainScreenColumnCount);
}
public Optional<ParticleRenderingSettings> getParticleRenderingSettings() {
// MIXED is implied if separateEntityDraws is true.
if (separateEntityDraws == OptionalBoolean.TRUE) {
return Optional.of(ParticleRenderingSettings.MIXED);
}
return particleRenderingSettings;
}
/**
* Parses a custom image directive.
* Format: image.<name> = <sampler_name> <pixel_format> <internal_format> <pixel_type> <clear> <relative> [width height [depth]] | [relativeX relativeY]
*/
private void parseCustomImage(String imageName, String value) {
if (customImages.size() >= 16) {
Iris.logger.error("Maximum of 16 custom images exceeded, cannot add: " + imageName);
return;
}
final String[] parts = value.split("\\s+");
if (parts.length < 6) {
Iris.logger.error("Invalid custom image directive for " + imageName + ": expected at least 6 parts, got " + parts.length);
return;
}
try {
String samplerName = parts[0];
if (samplerName.equals("none") || samplerName.equals("null") || samplerName.isEmpty()) {
samplerName = null;
}
final Optional<PixelFormat> pixelFormatOpt = PixelFormat.fromString(parts[1].toUpperCase());
if (!pixelFormatOpt.isPresent()) {
Iris.logger.error("Invalid pixel format for custom image {}: {}", imageName, parts[1]);
return;
}
final PixelFormat pixelFormat = pixelFormatOpt.get();
final Optional<InternalTextureFormat> internalFormatOpt = InternalTextureFormat.fromString(parts[2].toUpperCase());
if (!internalFormatOpt.isPresent()) {
Iris.logger.error("Invalid internal format for custom image {}: {}", imageName, parts[2]);
return;
}
final InternalTextureFormat internalFormat = internalFormatOpt.get();
final Optional<PixelType> pixelTypeOpt = PixelType.fromString(parts[3].toUpperCase());
if (!pixelTypeOpt.isPresent()) {
Iris.logger.error("Invalid pixel type for custom image {}: {}", imageName, parts[3]);
return;
}
final PixelType pixelType = pixelTypeOpt.get();
final boolean clear = Boolean.parseBoolean(parts[4]);
final boolean relative = Boolean.parseBoolean(parts[5]);
ImageInformation imageInfo;
if (parts.length == 7) {
// 1D texture: width only
int width = Integer.parseInt(parts[6]);
imageInfo = new ImageInformation(imageName, samplerName, TextureType.TEXTURE_1D, pixelFormat, internalFormat, pixelType, width, 1, 1, clear, false, 1.0f, 1.0f);
} else if (parts.length == 8) {
if (relative) {
// Relative 2D: relativeWidth relativeHeight
float relativeWidth = Float.parseFloat(parts[6]);
float relativeHeight = Float.parseFloat(parts[7]);
imageInfo = new ImageInformation(imageName, samplerName, TextureType.TEXTURE_2D, pixelFormat, internalFormat, pixelType, 0, 0, 1, clear, true, relativeWidth, relativeHeight);
} else {
// Absolute 2D: width height
int width = Integer.parseInt(parts[6]);
int height = Integer.parseInt(parts[7]);
imageInfo = new ImageInformation(imageName, samplerName, TextureType.TEXTURE_2D, pixelFormat, internalFormat, pixelType, width, height, 1, clear, false, 1.0f, 1.0f);
}
} else if (parts.length >= 9) {
// 3D texture: width height depth
int width = Integer.parseInt(parts[6]);
int height = Integer.parseInt(parts[7]);
int depth = Integer.parseInt(parts[8]);
imageInfo = new ImageInformation(imageName, samplerName, TextureType.TEXTURE_3D, pixelFormat, internalFormat, pixelType, width, height, depth, clear, false, 1.0f, 1.0f);
} else {
Iris.logger.error("Invalid custom image directive for {}: expected dimensions", imageName);
return;
}
customImages.put(imageName, imageInfo);
Iris.logger.debug("[CustomImages] Parsed custom image: {} -> {}", imageName, imageInfo);
} catch (NumberFormatException e) {
Iris.logger.error("Failed to parse dimensions for custom image {}: {}", imageName, e.getMessage());
}
}
/**
* Parses a buffer object directive.
* Format: bufferObject.<index> = <size> [relative <scaleX> <scaleY>]
*/
private void parseBufferObject(String indexStr, String value) {
try {
final int index = Integer.parseInt(indexStr);
if (index > 8) {
Iris.logger.error("SSBO index " + index + " exceeds maximum of 8, buffers 9+ are reserved");
return;
}
final String[] parts = value.split("\\s+");
if (parts.length < 1) {
Iris.logger.error("Invalid buffer object directive for index " + index + ": expected size");
return;
}
final int size = Integer.parseInt(parts[0]);
// Size < 1 means the shader dev intended to disable the buffer
if (size < 1) {
return;
}
ShaderStorageInfo info;
if (parts.length >= 4 && "true".equalsIgnoreCase(parts[1])) {
// Relative: size relative scaleX scaleY
float scaleX = Float.parseFloat(parts[2]);
float scaleY = Float.parseFloat(parts[3]);
info = new ShaderStorageInfo(size, true, scaleX, scaleY);
} else {
// Absolute: size only
info = new ShaderStorageInfo(size, false, 1.0f, 1.0f);
}
bufferObjects.put(index, info);
Iris.logger.debug("Parsed buffer object: " + index + " -> " + info);
} catch (NumberFormatException e) {
Iris.logger.error("Failed to parse buffer object " + indexStr + ": " + e.getMessage());
}
}
/**
* Parses a texture definition from a shaders.properties value.
* Format can be:
* - PNG: path
* - Raw 1D: path type format sizeX pixelFormat pixelType
* - Raw 2D: path type format sizeX sizeY pixelFormat pixelType
* - Raw 3D: path type format sizeX sizeY sizeZ pixelFormat pixelType
*/
private TextureDefinition parseTextureDefinition(String key, String value) {
final String[] parts = value.split(" ");
if (parts.length == 1) {
// PNG texture - just a path
return new TextureDefinition.PNGDefinition(value);
}
try {
if (parts.length == 6) {
// 1D raw texture: path type format sizeX pixelFormat pixelType
final Optional<TextureType> targetOpt = TextureType.fromString(parts[1].toUpperCase(Locale.ROOT));
if (!targetOpt.isPresent()) {
Iris.logger.error("Invalid texture type for {}: {}", key, parts[1]);
return null;
}
final Optional<InternalTextureFormat> internalFormatOpt = InternalTextureFormat.fromString(parts[2].toUpperCase(Locale.ROOT));
if (!internalFormatOpt.isPresent()) {
Iris.logger.error("Invalid internal format for {}: {}", key, parts[2]);
return null;
}
final Optional<PixelFormat> formatOpt = PixelFormat.fromString(parts[4].toUpperCase(Locale.ROOT));
if (!formatOpt.isPresent()) {
Iris.logger.error("Invalid pixel format for {}: {}", key, parts[4]);
return null;
}
final Optional<PixelType> pixelTypeOpt = PixelType.fromString(parts[5].toUpperCase(Locale.ROOT));
if (!pixelTypeOpt.isPresent()) {
Iris.logger.error("Invalid pixel type for {}: {}", key, parts[5]);
return null;
}
return new TextureDefinition.RawDefinition(parts[0], targetOpt.get(), internalFormatOpt.get(),
Integer.parseInt(parts[3]), 0, 0, formatOpt.get(), pixelTypeOpt.get());
} else if (parts.length == 7) {
// 2D raw texture: path type format sizeX sizeY pixelFormat pixelType
final Optional<TextureType> targetOpt = TextureType.fromString(parts[1].toUpperCase(Locale.ROOT));
if (!targetOpt.isPresent()) {
Iris.logger.error("Invalid texture type for {}: {}", key, parts[1]);
return null;
}
final Optional<InternalTextureFormat> internalFormatOpt = InternalTextureFormat.fromString(parts[2].toUpperCase(Locale.ROOT));
if (!internalFormatOpt.isPresent()) {
Iris.logger.error("Invalid internal format for {}: {}", key, parts[2]);
return null;
}
final Optional<PixelFormat> formatOpt = PixelFormat.fromString(parts[5].toUpperCase(Locale.ROOT));
if (!formatOpt.isPresent()) {
Iris.logger.error("Invalid pixel format for {}: {}", key, parts[5]);
return null;
}
final Optional<PixelType> pixelTypeOpt = PixelType.fromString(parts[6].toUpperCase(Locale.ROOT));
if (!pixelTypeOpt.isPresent()) {
Iris.logger.error("Invalid pixel type for {}: {}", key, parts[6]);
return null;
}
return new TextureDefinition.RawDefinition(parts[0], targetOpt.get(), internalFormatOpt.get(),
Integer.parseInt(parts[3]), Integer.parseInt(parts[4]), 0, formatOpt.get(), pixelTypeOpt.get());
} else if (parts.length == 8) {
// 3D raw texture: path type format sizeX sizeY sizeZ pixelFormat pixelType
final Optional<TextureType> targetOpt = TextureType.fromString(parts[1].toUpperCase(Locale.ROOT));
if (!targetOpt.isPresent()) {
Iris.logger.error("Invalid texture type for {}: {}", key, parts[1]);
return null;
}
final Optional<InternalTextureFormat> internalFormatOpt = InternalTextureFormat.fromString(parts[2].toUpperCase(Locale.ROOT));
if (!internalFormatOpt.isPresent()) {
Iris.logger.error("Invalid internal format for {}: {}", key, parts[2]);
return null;
}
final Optional<PixelFormat> formatOpt = PixelFormat.fromString(parts[6].toUpperCase(Locale.ROOT));
if (!formatOpt.isPresent()) {
Iris.logger.error("Invalid pixel format for {}: {}", key, parts[6]);
return null;
}
final Optional<PixelType> pixelTypeOpt = PixelType.fromString(parts[7].toUpperCase(Locale.ROOT));
if (!pixelTypeOpt.isPresent()) {
Iris.logger.error("Invalid pixel type for {}: {}", key, parts[7]);
return null;
}
return new TextureDefinition.RawDefinition(parts[0], targetOpt.get(), internalFormatOpt.get(),
Integer.parseInt(parts[3]), Integer.parseInt(parts[4]), Integer.parseInt(parts[5]),
formatOpt.get(), pixelTypeOpt.get());
} else {
Iris.logger.warn("Unknown texture directive format for {}: {} (expected 1, 6, 7, or 8 parts, got {})",
key, value, parts.length);
return null;
}
} catch (NumberFormatException e) {
Iris.logger.error("Failed to parse texture dimensions for {}: {}", key, e.getMessage());
return null;
}
}
}
@@ -0,0 +1,8 @@
package net.coderbot.iris.shaderpack;
public enum ShadowCullState {
DEFAULT,
ADVANCED,
REVERSED,
DISTANCE
}
@@ -1,15 +1,16 @@
package net.coderbot.iris.shaderpack.texture;
import lombok.Getter;
import net.coderbot.iris.gl.texture.InternalTextureFormat;
import net.coderbot.iris.gl.texture.PixelFormat;
import net.coderbot.iris.gl.texture.PixelType;
public abstract class CustomTextureData {
private CustomTextureData() {
CustomTextureData() {
}
public static final class PngData extends CustomTextureData {
@Getter
public static final class PngData extends CustomTextureData {
private final TextureFilteringData filteringData;
private final byte[] content;
@@ -18,14 +19,7 @@ public abstract class CustomTextureData {
this.content = content;
}
public TextureFilteringData getFilteringData() {
return filteringData;
}
public byte[] getContent() {
return content;
}
}
}
public static final class LightmapMarker extends CustomTextureData {
@Override
@@ -39,123 +33,80 @@ public abstract class CustomTextureData {
}
}
public static final class ResourceData extends CustomTextureData {
private final String namespace;
private final String location;
@Getter
public static final class ResourceData extends CustomTextureData {
private final String namespace;
private final String location;
public ResourceData(String namespace, String location) {
this.namespace = namespace;
this.location = location;
}
/**
* @return The namespace of the texture. The caller is responsible for checking whether this is actually
* a valid namespace.
*/
public String getNamespace() {
return namespace;
}
/**
* @return The path / location of the texture. The caller is responsible for checking whether this is actually
* a valid path.
*/
public String getLocation() {
return location;
}
}
}
@Getter
public abstract static class RawData extends CustomTextureData {
private final byte[] content;
private final TextureFilteringData filteringData;
private final InternalTextureFormat internalFormat;
private final PixelFormat pixelFormat;
private final PixelType pixelType;
private RawData(byte[] content, InternalTextureFormat internalFormat,
PixelFormat pixelFormat, PixelType pixelType) {
protected RawData(byte[] content, TextureFilteringData filteringData, InternalTextureFormat internalFormat, PixelFormat pixelFormat, PixelType pixelType) {
this.content = content;
this.filteringData = filteringData;
this.internalFormat = internalFormat;
this.pixelFormat = pixelFormat;
this.pixelType = pixelType;
}
public final byte[] getContent() {
return content;
}
public final InternalTextureFormat getInternalFormat() {
return internalFormat;
}
public final PixelFormat getPixelFormat() {
return pixelFormat;
}
public final PixelType getPixelType() {
return pixelType;
}
}
public static final class RawData1D extends RawData {
@Getter
public static final class RawData1D extends RawData {
private final int sizeX;
private RawData1D(byte[] content, InternalTextureFormat internalFormat,
PixelFormat pixelFormat, PixelType pixelType, int sizeX) {
super(content, internalFormat, pixelFormat, pixelType);
public RawData1D(byte[] content, TextureFilteringData filteringData, InternalTextureFormat internalFormat, PixelFormat pixelFormat, PixelType pixelType, int sizeX) {
super(content, filteringData, internalFormat, pixelFormat, pixelType);
this.sizeX = sizeX;
}
public int getSizeX() {
return sizeX;
}
}
}
public static final class RawData2D extends RawData {
int sizeX;
int sizeY;
@Getter
public static class RawData2D extends RawData {
private final int sizeX;
private final int sizeY;
private RawData2D(byte[] content, InternalTextureFormat internalFormat,
PixelFormat pixelFormat, PixelType pixelType, int sizeX, int sizeY) {
super(content, internalFormat, pixelFormat, pixelType);
public RawData2D(byte[] content, TextureFilteringData filteringData, InternalTextureFormat internalFormat, PixelFormat pixelFormat, PixelType pixelType, int sizeX, int sizeY) {
super(content, filteringData, internalFormat, pixelFormat, pixelType);
this.sizeX = sizeX;
this.sizeY = sizeY;
}
public int getSizeX() {
return sizeX;
}
}
public int getSizeY() {
return sizeY;
}
}
@Getter
public static final class RawData3D extends RawData {
private final int sizeX;
private final int sizeY;
private final int sizeZ;
public static final class RawData3D extends RawData {
int sizeX;
int sizeY;
int sizeZ;
private RawData3D(byte[] content, InternalTextureFormat internalFormat,
PixelFormat pixelFormat, PixelType pixelType, int sizeX, int sizeY, int sizeZ) {
super(content, internalFormat, pixelFormat, pixelType);
public RawData3D(byte[] content, TextureFilteringData filteringData, InternalTextureFormat internalFormat, PixelFormat pixelFormat, PixelType pixelType, int sizeX, int sizeY, int sizeZ) {
super(content, filteringData, internalFormat, pixelFormat, pixelType);
this.sizeX = sizeX;
this.sizeY = sizeY;
this.sizeZ = sizeZ;
}
public int getSizeX() {
return sizeX;
}
}
public int getSizeY() {
return sizeY;
}
public int getSizeZ() {
return sizeZ;
public static final class RawDataRect extends RawData2D {
public RawDataRect(byte[] content, TextureFilteringData filteringData, InternalTextureFormat internalFormat, PixelFormat pixelFormat, PixelType pixelType, int sizeX, int sizeY) {
super(content, filteringData, internalFormat, pixelFormat, pixelType, sizeX, sizeY);
}
}
}
@@ -3,6 +3,18 @@ package net.coderbot.iris.shaderpack.texture;
import java.util.Optional;
public enum TextureStage {
/**
* The setup passes.
*
* Exclusive to Iris 1.6.
*/
SETUP,
/**
* The begin pass.
*
* Exclusive to Iris 1.6.
*/
BEGIN,
/**
* The shadowcomp passes.
*
@@ -29,13 +41,15 @@ public enum TextureStage {
COMPOSITE_AND_FINAL;
public static Optional<TextureStage> parse(String name) {
return switch (name) {
case "shadowcomp" -> Optional.of(SHADOWCOMP);
case "prepare" -> Optional.of(PREPARE);
case "gbuffers" -> Optional.of(GBUFFERS_AND_SHADOW);
case "deferred" -> Optional.of(DEFERRED);
case "composite" -> Optional.of(COMPOSITE_AND_FINAL);
default -> Optional.empty();
};
return switch (name) {
case "setup" -> Optional.of(SETUP);
case "begin" -> Optional.of(BEGIN);
case "shadowcomp" -> Optional.of(SHADOWCOMP);
case "prepare" -> Optional.of(PREPARE);
case "gbuffers" -> Optional.of(GBUFFERS_AND_SHADOW);
case "deferred" -> Optional.of(DEFERRED);
case "composite" -> Optional.of(COMPOSITE_AND_FINAL);
default -> Optional.empty();
};
}
}
@@ -18,6 +18,7 @@ import java.util.List;
public class ShadowRenderTargets {
private final RenderTarget[] targets;
private final PackShadowDirectives shadowDirectives;
private final DepthTexture mainDepth;
private final DepthTexture noTranslucents;
private final GlFramebuffer depthSourceFb;
@@ -29,42 +30,41 @@ public class ShadowRenderTargets {
private boolean fullClearRequired;
private boolean translucentDepthDirty;
private boolean[] hardwareFiltered;
private InternalTextureFormat[] formats;
private IntList buffersToBeCleared;
private final boolean[] hardwareFiltered;
private final boolean[] linearFiltered;
private final InternalTextureFormat[] formats;
private final IntList buffersToBeCleared;
public ShadowRenderTargets(int resolution, PackShadowDirectives shadowDirectives) {
targets = new RenderTarget[shadowDirectives.getColorSamplingSettings().size()];
formats = new InternalTextureFormat[shadowDirectives.getColorSamplingSettings().size()];
flipped = new boolean[shadowDirectives.getColorSamplingSettings().size()];
hardwareFiltered = new boolean[shadowDirectives.getColorSamplingSettings().size()];
this.shadowDirectives = shadowDirectives;
this.resolution = resolution;
final int size = shadowDirectives.getColorSamplingSettings().size();
targets = new RenderTarget[size];
formats = new InternalTextureFormat[size];
flipped = new boolean[size];
hardwareFiltered = new boolean[shadowDirectives.getDepthSamplingSettings().size()];
linearFiltered = new boolean[shadowDirectives.getDepthSamplingSettings().size()];
buffersToBeCleared = new IntArrayList();
this.mainDepth = new DepthTexture(resolution, resolution, DepthBufferFormat.DEPTH);
this.noTranslucents = new DepthTexture(resolution, resolution, DepthBufferFormat.DEPTH);
for (int i = 0; i < shadowDirectives.getColorSamplingSettings().size(); i++) {
PackShadowDirectives.SamplingSettings settings = shadowDirectives.getColorSamplingSettings().get(i);
targets[i] = RenderTarget.builder().setDimensions(resolution, resolution)
.setInternalFormat(settings.getFormat())
.setPixelFormat(settings.getFormat().getPixelFormat()).build();
formats[i] = settings.getFormat();
if (settings.getClear()) {
buffersToBeCleared.add(i);
}
this.hardwareFiltered[i] = shadowDirectives.getDepthSamplingSettings().get(i).getHardwareFiltering();
}
this.resolution = resolution;
this.ownedFramebuffers = new ArrayList<>();
// Populate hardware/linear filtering settings from depth sampling settings
for (int i = 0; i < shadowDirectives.getDepthSamplingSettings().size(); i++) {
this.hardwareFiltered[i] = shadowDirectives.getDepthSamplingSettings().get(i).getHardwareFiltering();
this.linearFiltered[i] = !shadowDirectives.getDepthSamplingSettings().get(i).getNearest();
}
// NB: Make sure all buffers are cleared so that they don't contain undefined
// data. Otherwise very weird things can happen.
fullClearRequired = true;
// Create target 0 eagerly - it's needed for framebuffer creation and almost always used
createIfEmpty(0);
this.depthSourceFb = createFramebufferWritingToMain(new int[] {0});
this.noTranslucentsDestFb = createFramebufferWritingToMain(new int[] {0});
@@ -73,6 +73,36 @@ public class ShadowRenderTargets {
this.translucentDepthDirty = true;
}
private void create(int index) {
if (index >= targets.length) {
throw new IllegalStateException("Tried to create shadow color buffer " + index + " but only " + targets.length + " are supported.");
}
PackShadowDirectives.SamplingSettings settings = shadowDirectives.getColorSamplingSettings().get(index);
targets[index] = RenderTarget.builder()
.setDimensions(resolution, resolution)
.setInternalFormat(settings.getFormat())
.setPixelFormat(settings.getFormat().getPixelFormat()).build();
formats[index] = settings.getFormat();
if (settings.getClear()) {
buffersToBeCleared.add(index);
}
fullClearRequired = true;
}
public void createIfEmpty(int index) {
if (targets[index] == null) {
create(index);
}
}
public RenderTarget getOrCreate(int index) {
createIfEmpty(index);
return targets[index];
}
// TODO: Actually flip. This is required for shadow composites!
public void flip(int target) {
flipped[target] = !flipped[target];
@@ -88,9 +118,12 @@ public class ShadowRenderTargets {
}
for (RenderTarget target : targets) {
target.destroy();
if (target != null) {
target.destroy();
}
}
mainDepth.destroy();
noTranslucents.destroy();
}
@@ -165,7 +198,7 @@ public class ShadowRenderTargets {
// NB: Before OpenGL 3.0, all framebuffers are required to have a color
// attachment no matter what.
framebuffer.addColorAttachment(0, get(0).getMainTexture());
framebuffer.addColorAttachment(0, getOrCreate(0).getMainTexture());
framebuffer.noDrawBuffers();
return framebuffer;
@@ -224,7 +257,7 @@ public class ShadowRenderTargets {
+ getRenderTargetCount() + " render targets are supported.");
}
RenderTarget target = this.get(drawBuffers[i]);
RenderTarget target = this.getOrCreate(drawBuffers[i]);
int textureId = stageWritesToMain.contains(drawBuffers[i]) ? target.getMainTexture() : target.getAltTexture();
@@ -242,13 +275,21 @@ public class ShadowRenderTargets {
}
public int getColorTextureId(int i) {
return isFlipped(i) ? get(i).getAltTexture() : get(i).getMainTexture();
RenderTarget target = get(i);
if (target == null) {
return 0;
}
return isFlipped(i) ? target.getAltTexture() : target.getMainTexture();
}
public boolean isHardwareFiltered(int i) {
return hardwareFiltered[i];
}
public boolean isLinearFiltered(int i) {
return linearFiltered[i];
}
public int getNumColorTextures() {
return targets.length;
}