diff --git a/crates/horizon-shaders/tests/pack_compat.rs b/crates/horizon-shaders/tests/pack_compat.rs index a6fe286537..3b80656186 100644 --- a/crates/horizon-shaders/tests/pack_compat.rs +++ b/crates/horizon-shaders/tests/pack_compat.rs @@ -162,6 +162,36 @@ fn a_matrix_built_from_one_vector_takes_its_components_column_by_column() { ); } +#[test] +fn a_texel_offset_a_function_gets_as_a_parameter_moves_the_coordinate() { + // Arc's FXAA: `textureLodOffset(tex, uv, 0.0, offset)` with `const in ivec2 offset`. + let module = assert_terrain_translates( + "#version 330\nuniform sampler2D colortex4;\n\ + float lumaAt(const in vec2 uv, const in ivec2 offset) {\n\ + return textureLodOffset(colortex4, uv, 0.0, offset).r + textureOffset(colortex4, uv, offset).g;\n}\n\ + layout(location = 0) out vec4 color;\n\ + void main() { color = vec4(lumaAt(vec2(0.5), ivec2(0, -1)), textureOffset(colortex4, vec2(0.5), ivec2(1, 0)).r, 0.0, 1.0); }\n", + ); + // The constant offset in `main` stays an offset. + let offsets = module + .functions + .iter() + .map(|(_, f)| &f.expressions) + .chain(module.entry_points.iter().map(|e| &e.function.expressions)) + .flat_map(|e| e.iter()) + .filter(|(_, e)| { + matches!( + e, + naga::Expression::ImageSample { + offset: Some(_), + .. + } + ) + }) + .count(); + assert_eq!(offsets, 1); +} + #[test] fn integer_samplers_read_through_the_float_sampling_functions() { // Mello, Voyager, Exposa, Reverie: `uint mask = texture(colortex3, uv).r` on a usampler2D. diff --git a/third_party/naga/src/front/glsl/builtins.rs b/third_party/naga/src/front/glsl/builtins.rs index 3b6a7e1018..524356c1b2 100644 --- a/third_party/naga/src/front/glsl/builtins.rs +++ b/third_party/naga/src/front/glsl/builtins.rs @@ -2582,12 +2582,60 @@ fn texture_call( ctx.conversion(array_index_expr, meta, Scalar::I32)?; } + // A texel offset must be a constant expression, but GL drivers also take one a + // function gets as a parameter (`const in ivec2 offset`, Arc's FXAA). Such an offset + // moves the coordinate by that many texels of the base level instead. + let mut coordinate = comps.coordinate; + let mut offset = offset; + if let Some(by) = offset.filter(|&o| !ctx.local_expression_kind_tracker.is_const(o)) { + let size = ctx.add_expression( + Expression::ImageQuery { + image, + query: ImageQuery::Size { level: None }, + }, + meta, + )?; + let size = ctx.add_expression( + Expression::As { + expr: size, + kind: Sk::Float, + convert: Some(4), + }, + meta, + )?; + let by = ctx.add_expression( + Expression::As { + expr: by, + kind: Sk::Float, + convert: Some(4), + }, + meta, + )?; + let shift = ctx.add_expression( + Expression::Binary { + op: BinaryOperator::Divide, + left: by, + right: size, + }, + meta, + )?; + coordinate = ctx.add_expression( + Expression::Binary { + op: BinaryOperator::Add, + left: coordinate, + right: shift, + }, + meta, + )?; + offset = None; + } + Ok(ctx.add_expression( Expression::ImageSample { image, sampler, gather: None, //TODO - coordinate: comps.coordinate, + coordinate, array_index, offset, level,