diff --git a/src/reader/spirv/function.cc b/src/reader/spirv/function.cc index 43401f580f..90e63e7355 100644 --- a/src/reader/spirv/function.cc +++ b/src/reader/spirv/function.cc @@ -3996,6 +3996,7 @@ TypedExpression FunctionEmitter::EmitGlslStd450ExtInst( // WGSL does not have scalar form of the normalize builtin. // The answer would be 1 anyway, so return that directly. return {ty_.F32(), builder_.Expr(1.0f)}; + case GLSLstd450FaceForward: { // If dot(Nref, Incident) < 0, the result is Normal, otherwise -Normal. // Also: select(-normal,normal, Incident*Nref < 0) @@ -4021,6 +4022,21 @@ TypedExpression FunctionEmitter::EmitGlslStd450ExtInst( builder_.Expr(0.0f))})}; } + case GLSLstd450Reflect: { + // Compute Incident - 2 * Normal * Normal * Incident + auto incident = MakeOperand(inst, 2); + auto normal = MakeOperand(inst, 3); + TINT_ASSERT(Reader, incident.type->Is()); + TINT_ASSERT(Reader, normal.type->Is()); + return { + ty_.F32(), + builder_.Sub( + incident.expr, + builder_.Mul(2.0f, builder_.Mul(normal.expr, + builder_.Mul(normal.expr, + incident.expr))))}; + } + case GLSLstd450Refract: { // It's a complicated expression. Compute it in two dimensions, but // with a 0-valued y component in both the incident and normal vectors, @@ -4701,6 +4717,10 @@ void FunctionEmitter::FindValuesNeedingNamedOrHoistedDefinition() { // The "normal" operand expression is used twice in code generation. require_named_const_def(inst, 2); break; + case GLSLstd450Reflect: + require_named_const_def(inst, 2); // Incident + require_named_const_def(inst, 3); // Normal + break; default: break; } diff --git a/src/reader/spirv/function_glsl_std_450_test.cc b/src/reader/spirv/function_glsl_std_450_test.cc index 0699005e1b..be6d5a4d97 100644 --- a/src/reader/spirv/function_glsl_std_450_test.cc +++ b/src/reader/spirv/function_glsl_std_450_test.cc @@ -717,7 +717,6 @@ INSTANTIATE_TEST_SUITE_P(Samples, {"FMax", "max"}, // WGSL max promises more for NaN {"FMin", "min"}, // WGSL min promises more for NaN {"Pow", "pow"}, - {"Reflect", "reflect"}, {"Step", "step"}, })); @@ -1913,6 +1912,145 @@ TEST_F(SpvParserTest, GlslStd450_FaceForward_Vector) { EXPECT_THAT(body, HasSubstr(expected)); } +TEST_F(SpvParserTest, GlslStd450_Reflect_Scalar) { + const auto assembly = Preamble() + R"( + %98 = OpFAdd %float %f1 %f1 ; has only one use + %99 = OpFAdd %float %f2 %f2 ; has only one use + %1 = OpExtInst %float %glsl Reflect %98 %99 + OpReturn + OpFunctionEnd + )"; + auto p = parser(test::Assemble(assembly)); + ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()); + auto fe = p->function_emitter(100); + EXPECT_TRUE(fe.EmitBody()) << p->error(); + const auto body = ToString(p->builder(), fe.ast_body()); + // The %99 sum only has one use. Ensure it is evaluated only once by + // making a let-declaration for it, since it is the normal operand to + // the builtin function, and code generation uses it twice. + const auto* expected = R"(VariableDeclStatement{ + VariableConst{ + x_98 + none + undefined + __f32 + { + Binary[not set]{ + Identifier[not set]{f1} + add + Identifier[not set]{f1} + } + } + } +} +VariableDeclStatement{ + VariableConst{ + x_99 + none + undefined + __f32 + { + Binary[not set]{ + Identifier[not set]{f2} + add + Identifier[not set]{f2} + } + } + } +} +VariableDeclStatement{ + VariableConst{ + x_1 + none + undefined + __f32 + { + Binary[not set]{ + Identifier[not set]{x_98} + subtract + Binary[not set]{ + ScalarConstructor[not set]{2.000000} + multiply + Binary[not set]{ + Identifier[not set]{x_99} + multiply + Binary[not set]{ + Identifier[not set]{x_99} + multiply + Identifier[not set]{x_98} + } + } + } + } + } + } +})"; + + EXPECT_THAT(body, HasSubstr(expected)) << body; +} + +TEST_F(SpvParserTest, GlslStd450_Reflect_Vector) { + const auto assembly = Preamble() + R"( + %98 = OpFAdd %v2float %v2f1 %v2f1 + %99 = OpFAdd %v2float %v2f2 %v2f2 + %1 = OpExtInst %v2float %glsl Reflect %98 %99 + OpReturn + OpFunctionEnd + )"; + auto p = parser(test::Assemble(assembly)); + ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()); + auto fe = p->function_emitter(100); + EXPECT_TRUE(fe.EmitBody()) << p->error(); + const auto body = ToString(p->builder(), fe.ast_body()); + const auto* expected = R"(VariableDeclStatement{ + VariableConst{ + x_98 + none + undefined + __vec_2__f32 + { + Binary[not set]{ + Identifier[not set]{v2f1} + add + Identifier[not set]{v2f1} + } + } + } +} +VariableDeclStatement{ + VariableConst{ + x_99 + none + undefined + __vec_2__f32 + { + Binary[not set]{ + Identifier[not set]{v2f2} + add + Identifier[not set]{v2f2} + } + } + } +} +VariableDeclStatement{ + VariableConst{ + x_1 + none + undefined + __vec_2__f32 + { + Call[not set]{ + Identifier[not set]{reflect} + ( + Identifier[not set]{x_98} + Identifier[not set]{x_99} + ) + } + })"; + + EXPECT_THAT(body, HasSubstr(expected)) << body; +} + } // namespace } // namespace spirv } // namespace reader diff --git a/test/vk-gl-cts/graphicsfuzz/cov-undefined-inversesqrt-reflect/0-opt.spvasm.expected.hlsl b/test/vk-gl-cts/graphicsfuzz/cov-undefined-inversesqrt-reflect/0-opt.spvasm.expected.hlsl index a4a3ad2d7e..3fb8095554 100644 --- a/test/vk-gl-cts/graphicsfuzz/cov-undefined-inversesqrt-reflect/0-opt.spvasm.expected.hlsl +++ b/test/vk-gl-cts/graphicsfuzz/cov-undefined-inversesqrt-reflect/0-opt.spvasm.expected.hlsl @@ -21,7 +21,9 @@ void main_1() { m24 = float2x2(float2(x_40, x_42), float2((x_44 * 1.0f), x_47)); a = m24[0u].x; v2 = float2(asfloat(0x7fc00000u), 1.0f); - v3 = reflect(v2, float2(a, 1.0f)); + const float2 x_53 = v2; + const float2 x_55 = float2(a, 1.0f); + v3 = reflect(x_53, x_55); const uint scalar_offset_3 = ((16u * uint(0))) / 4; const float x_58 = asfloat(x_6[scalar_offset_3 / 4][scalar_offset_3 % 4]); const float2 x_59 = v3; diff --git a/test/vk-gl-cts/graphicsfuzz/cov-undefined-inversesqrt-reflect/0-opt.spvasm.expected.msl b/test/vk-gl-cts/graphicsfuzz/cov-undefined-inversesqrt-reflect/0-opt.spvasm.expected.msl index 570e97afda..ec52a786c9 100644 --- a/test/vk-gl-cts/graphicsfuzz/cov-undefined-inversesqrt-reflect/0-opt.spvasm.expected.msl +++ b/test/vk-gl-cts/graphicsfuzz/cov-undefined-inversesqrt-reflect/0-opt.spvasm.expected.msl @@ -36,7 +36,8 @@ void main_1(constant buf0& x_6, constant buf1& x_8, thread float4* const tint_sy v2 = float2(NAN, 1.0f); float2 const x_53 = v2; float const x_54 = a; - v3 = reflect(x_53, float2(x_54, 1.0f)); + float2 const x_55 = float2(x_54, 1.0f); + v3 = reflect(x_53, x_55); float const x_58 = x_6.x_GLF_uniform_float_values.arr[0].el; float2 const x_59 = v3; float const x_61 = x_6.x_GLF_uniform_float_values.arr[0].el; diff --git a/test/vk-gl-cts/graphicsfuzz/cov-undefined-inversesqrt-reflect/0-opt.spvasm.expected.spvasm b/test/vk-gl-cts/graphicsfuzz/cov-undefined-inversesqrt-reflect/0-opt.spvasm.expected.spvasm index a0d50394a2..6061eec59a 100644 --- a/test/vk-gl-cts/graphicsfuzz/cov-undefined-inversesqrt-reflect/0-opt.spvasm.expected.spvasm +++ b/test/vk-gl-cts/graphicsfuzz/cov-undefined-inversesqrt-reflect/0-opt.spvasm.expected.spvasm @@ -4,7 +4,7 @@ ; Bound: 98 ; Schema: 0 OpCapability Shader - %58 = OpExtInstImport "GLSL.std.450" + %59 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %main "main" %tint_symbol_1 OpExecutionMode %main OriginUpperLeft @@ -104,9 +104,9 @@ OpStore %v2 %54 %55 = OpLoad %v2float %v2 %56 = OpLoad %float %a - %59 = OpCompositeConstruct %v2float %56 %float_1 - %57 = OpExtInst %v2float %58 Reflect %55 %59 - OpStore %v3 %57 + %57 = OpCompositeConstruct %v2float %56 %float_1 + %58 = OpExtInst %v2float %59 Reflect %55 %57 + OpStore %v3 %58 %60 = OpAccessChain %_ptr_Uniform_float %x_6 %uint_0 %int_0 %61 = OpLoad %float %60 %62 = OpLoad %v2float %v3 diff --git a/test/vk-gl-cts/graphicsfuzz/cov-undefined-inversesqrt-reflect/0-opt.spvasm.expected.wgsl b/test/vk-gl-cts/graphicsfuzz/cov-undefined-inversesqrt-reflect/0-opt.spvasm.expected.wgsl index 1e71e59410..483443b99a 100644 --- a/test/vk-gl-cts/graphicsfuzz/cov-undefined-inversesqrt-reflect/0-opt.spvasm.expected.wgsl +++ b/test/vk-gl-cts/graphicsfuzz/cov-undefined-inversesqrt-reflect/0-opt.spvasm.expected.wgsl @@ -31,7 +31,8 @@ fn main_1() { v2 = vec2(-0x1.8p+128, 1.0); let x_53 : vec2 = v2; let x_54 : f32 = a; - v3 = reflect(x_53, vec2(x_54, 1.0)); + let x_55 : vec2 = vec2(x_54, 1.0); + v3 = reflect(x_53, x_55); let x_58 : f32 = x_6.x_GLF_uniform_float_values[0]; let x_59 : vec2 = v3; let x_61 : f32 = x_6.x_GLF_uniform_float_values[0];