Shader packs: a built-in declared with another GLSL type reads 0

Iris 1.11.7 disables a built-in uniform whose declared type is not the one it gives
("Wrong uniform type ... Disabling that uniform"), and the program reads 0. Our probe
packs saw this for floats declared int or vec2, ints declared float, ivec2s declared
vec2, int or ivec3, vec3s declared vec2 or vec4, a mat4 declared mat3, and uint; ints
and bools declared bool or int were kept. Horizon converted the value instead.

Fixes #9758
Part of #9307

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
Arslan ArmanulyandClaude Opus 5.5 committed 2026-11-05 14:07:25 +05:00
1 parent cc94ed7afe
commit beb30045fd
3 files changed
+123 -1

No files matched your search

@@ -2934,7 +2934,15 @@ impl PackPost {
let n = head.len().min(pass.block.len());
pass.block[..n].copy_from_slice(&head[..n]);
for (index, ty, offset) in &pass.uniforms {
if let Some(v) = index.and_then(|i| self.uniforms.values.get(i)) {
let Some((i, v)) = index.and_then(|i| Some((i, self.uniforms.values.get(i)?))) else {
continue;
};
// A built-in of another type keeps its initial value (0); custom uniforms were
// checked when the pass was built.
if pack_uniforms::NAMES
.get(i)
.is_none_or(|name| v.read_as(name, ty))
{
v.write(ty, &mut pass.block, *offset as usize);
}
}
@@ -5490,3 +5498,6 @@ mod const_offset_tests;
#[cfg(test)]
mod integer_colortex0_tests;
#[cfg(test)]
mod wrong_type_uniform_tests;
@@ -0,0 +1,56 @@
//! A built-in uniform declared with another GLSL type than the one Iris 1.11.7 gives is disabled
//! there ("Wrong uniform type ... Disabling that uniform") and reads 0; our probe packs (#9307)
//! read 0 for each such declaration. Declared with its own type, it keeps its value.
use super::destination_tests::Fixture;
use super::*;
use std::io::{Cursor, Write};
const VERTEX: &str = "#version 120\nvarying vec2 uv;\nvoid main() { uv = gl_MultiTexCoord0.xy; gl_Position = ftransform(); }\n";
/// composite declares two floats as `int` and `vec2` and an int as `float`, and writes what it
/// reads into colortex1; final declares the same names with their own types and adds them.
fn pack() -> Post {
let mut zip = zip::ZipWriter::new(Cursor::new(Vec::new()));
let mut add = |name: &str, source: &str| {
zip.start_file(
format!("shaders/{name}"),
zip::write::SimpleFileOptions::default(),
)
.unwrap();
zip.write_all(source.as_bytes()).unwrap();
};
add("composite.vsh", VERTEX);
add("final.vsh", VERTEX);
add(
"composite.fsh",
"#version 130\nuniform int viewWidth;\nuniform vec2 viewHeight;\nuniform float fogShape;\n\
void main() {\n/* DRAWBUFFERS:1 */\n\
\x20 gl_FragData[0] = vec4(float(viewWidth), viewHeight.x, fogShape + 1.0, 255.0) / 255.0;\n}\n",
);
add(
"final.fsh",
"#version 130\nuniform sampler2D colortex1;\nuniform float viewWidth;\nuniform int fogShape;\n\
varying vec2 uv;\nvoid main() {\n\
\x20 vec4 c = texture(colortex1, uv);\n\
\x20 gl_FragColor = vec4(c.r, c.g + viewWidth * 4.0 / 255.0, c.b + float(fogShape + 2) * 10.0 / 255.0, 1.0);\n}\n",
);
let pack = horizon_shaders::Pack::from_zip_bytes(
"original-wrong-uniform-types",
zip.finish().unwrap().into_inner(),
)
.unwrap();
let mut macros = horizon_shaders::Macros::standard();
let (properties, _) = horizon_shaders::read_properties(&pack, &mut macros);
horizon_shaders::load_post(&pack, "", &properties, &macros, &Default::default())
}
#[test]
fn built_ins_declared_with_another_type_read_zero() {
let Some(gpu) = Fixture::new() else { return };
let mut post = gpu.build(pack());
// 8 pixels wide; post programs read `fogShape` -1. composite reads 0, 0 and 0 (+1 = 1);
// final reads 8 (green 32) and -1 (+2 = 1, blue 10).
for p in gpu.render(&mut post, [8, 8]) {
assert_eq!(p, [0, 32, 11, 255]);
}
}
@@ -154,6 +154,31 @@ impl Value {
Value::I(a)
}
/// Whether a program that declares the built-in `name` as `ty` reads this value. Iris 1.11.7
/// disables a built-in declared with another GLSL type than the one it gives ("Wrong uniform
/// type ... Disabling that uniform"), and the program reads 0. Our probe packs saw it for a
/// float declared `int` or `vec2`, an int `float`, an `ivec2` `vec2`, `int` or `ivec3`, a
/// `vec3` `vec2` or `vec4`, a `mat4` `mat3`, and any `uint`; an int or a bool built-in
/// declared `bool` or `int` was kept. A float declared `bool` is left as it was: Iris warned,
/// but our probe read true.
pub fn read_as(&self, name: &str, ty: &str) -> bool {
let int = matches!(self, Value::I(_));
let (declared_int, size) = match ty {
"float" => (false, 1),
"bool" if !int => return true,
"int" | "bool" => (true, 1),
"mat2" => (false, 4),
"mat3" => (false, 9),
"mat4" => (false, 16),
t if t.starts_with('u') => return false,
t if let Some(n) = t.strip_prefix("ivec") => (true, n.parse().unwrap_or(0)),
t if let Some(n) = t.strip_prefix("vec") => (false, n.parse().unwrap_or(0)),
_ => return true,
};
let name = name.strip_prefix("gbuffers.").unwrap_or(name);
declared_int == int && size == components(name)
}
/// Writes the value as the type the pack declared, at `offset` of a std140 block.
pub fn write(&self, ty: &str, out: &mut [u8], offset: usize) {
let ints = matches!(ty, "int" | "bool" | "uint") || ty.starts_with(['i', 'u']);
@@ -935,6 +960,36 @@ mod tests {
assert_eq!(out[60..64], [0; 4], "a value past the block is dropped");
}
/// The declarations our probe packs (#9307) ran in Iris 1.11.7: kept, or read as 0.
#[test]
fn built_ins_are_read_only_as_iris_gives_them() {
let int = Value::i(&[1]);
let float = Value::f(&[0.25]);
let matrix = Value::f(&[1.0; 16]);
for (value, name, ty, kept) in [
(&int, "isSpectator", "bool", true),
(&int, "isSpectator", "int", true),
(&int, "worldTime", "bool", true),
(&int, "worldTime", "uint", false),
(&int, "worldTime", "float", false),
(&Value::i(&[2048, 2048]), "atlasSize", "ivec2", true),
(&Value::i(&[2048, 2048]), "atlasSize", "vec2", false),
(&Value::i(&[2048, 2048]), "atlasSize", "int", false),
(&Value::i(&[2048, 2048]), "atlasSize", "ivec3", false),
(&float, "sunAngle", "float", true),
(&float, "sunAngle", "vec2", false),
(&float, "viewWidth", "int", false),
(&Value::f(&[1.0, 2.0, 3.0]), "cameraPosition", "vec3", true),
(&Value::f(&[1.0, 2.0, 3.0]), "cameraPosition", "vec4", false),
(&Value::f(&[1.0, 2.0, 3.0]), "cameraPosition", "vec2", false),
(&matrix, "gbufferModelView", "mat4", true),
(&matrix, "gbufferModelView", "mat3", false),
(&Value::i(&[1]), GBUFFERS_FOG_MODE, "int", true),
] {
assert_eq!(value.read_as(name, ty), kept, "{ty} {name}");
}
}
/// The load-time run of custom uniforms sees each built-in at its size: Iris 1.11.7 reads 2
/// for the first line of our probes K and X (#9086) and 0 for the others.
#[test]