Implement compile-time optimization for refract().

$genType refract($genType I, $genType N, float eta);
$genHType refract($genHType I, $genHType N, half eta);

The half form of refract was originally taking a `float eta` in our
headers, which seems wrong (and causes the DSL to break unless you add
casts). I've corrected the headers to use `half eta`.

Change-Id: I74b9ac330e0f7e99622d19cf7365aaa4cc910e57
Bug: skia:12034
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/412664
Commit-Queue: John Stiles <johnstiles@google.com>
Auto-Submit: John Stiles <johnstiles@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
diff --git a/resources/sksl/intrinsics/Refract.sksl b/resources/sksl/intrinsics/Refract.sksl
index ba04c4d..3d29308 100644
--- a/resources/sksl/intrinsics/Refract.sksl
+++ b/resources/sksl/intrinsics/Refract.sksl
@@ -3,4 +3,8 @@
 void main() {
     sk_FragColor.x = refract(a, b, c);
     sk_FragColor = refract(d, e, c);
+
+    sk_FragColor.xy   = refract(half2(1,0),     half2(0,1),     0.5);
+    sk_FragColor.xyz  = refract(half3(1,0,0),   half3(0,0,1),   0.5);
+    sk_FragColor.xyzw = refract(half4(1,0,0,0), half4(0,0,0,1), 0.5);
 }
diff --git a/src/sksl/generated/sksl_gpu.dehydrated.sksl b/src/sksl/generated/sksl_gpu.dehydrated.sksl
index 27a15ee..1d4afc7 100644
--- a/src/sksl/generated/sksl_gpu.dehydrated.sksl
+++ b/src/sksl/generated/sksl_gpu.dehydrated.sksl
@@ -1685,7 +1685,7 @@
 47,49,0,3,
 53,203,1,
 16,186,2,
-47,168,0,3,
+47,176,0,3,
 52,204,1,2,
 47,200,1,
 30,205,1,
diff --git a/src/sksl/generated/sksl_public.dehydrated.sksl b/src/sksl/generated/sksl_public.dehydrated.sksl
index 8abfa6d..1083779 100644
--- a/src/sksl/generated/sksl_public.dehydrated.sksl
+++ b/src/sksl/generated/sksl_public.dehydrated.sksl
@@ -962,7 +962,7 @@
 47,5,0,3,
 53,36,1,
 16,67,1,
-47,115,0,3,
+47,123,0,3,
 52,37,1,2,
 47,33,1,
 30,38,1,
diff --git a/src/sksl/ir/SkSLFunctionCall.cpp b/src/sksl/ir/SkSLFunctionCall.cpp
index b02e11b..5cc4030 100644
--- a/src/sksl/ir/SkSLFunctionCall.cpp
+++ b/src/sksl/ir/SkSLFunctionCall.cpp
@@ -499,6 +499,21 @@
             auto N    = [&] { return DSLExpression{arguments[1]->clone()}; };
             return (I() - 2.0 * Dot(N(), I()) * N()).release();
         }
+        case k_refract_IntrinsicKind: {
+            auto I    = [&] { return DSLExpression{arguments[0]->clone()}; };
+            auto N    = [&] { return DSLExpression{arguments[1]->clone()}; };
+            auto Eta  = [&] { return DSLExpression{arguments[2]->clone()}; };
+
+            std::unique_ptr<Expression> k =
+                    (1 - Pow(Eta(), 2) * (1 - Pow(Dot(N(), I()), 2))).release();
+            if (!k->is<FloatLiteral>()) {
+                return nullptr;
+            }
+            float kValue = k->as<FloatLiteral>().value();
+            return ((kValue < 0) ?
+                        (0 * I()) :
+                        (Eta() * I() - (Eta() * Dot(N(), I()) + sqrt(kValue)) * N())).release();
+        }
         default:
             return nullptr;
     }
diff --git a/src/sksl/sksl_gpu.sksl b/src/sksl/sksl_gpu.sksl
index d159746..07aed7a 100644
--- a/src/sksl/sksl_gpu.sksl
+++ b/src/sksl/sksl_gpu.sksl
@@ -141,7 +141,7 @@
 $genType reflect($genType I, $genType N);
 $genHType reflect($genHType I, $genHType N);
 $genType refract($genType I, $genType N, float eta);
-$genHType refract($genHType I, $genHType N, float eta);
+$genHType refract($genHType I, $genHType N, half eta);
 $mat matrixCompMult($mat x, $mat y);
 $hmat matrixCompMult($hmat x, $hmat y);
 float2x2 outerProduct(float2 c, float2 r);
diff --git a/src/sksl/sksl_public.sksl b/src/sksl/sksl_public.sksl
index 5a14767..bd6a514 100644
--- a/src/sksl/sksl_public.sksl
+++ b/src/sksl/sksl_public.sksl
@@ -100,7 +100,7 @@
 $genType  reflect($genType  I, $genType  N);
 $genHType reflect($genHType I, $genHType N);
 $genType  refract($genType  I, $genType  N, float eta);
-$genHType refract($genHType I, $genHType N, float eta);
+$genHType refract($genHType I, $genHType N, half eta);
 
 // 8.5 : Matrix Functions
 $squareMat  matrixCompMult($squareMat  x, $squareMat  y);
diff --git a/tests/sksl/intrinsics/Refract.asm.frag b/tests/sksl/intrinsics/Refract.asm.frag
index a3da953..75e8b6c 100644
--- a/tests/sksl/intrinsics/Refract.asm.frag
+++ b/tests/sksl/intrinsics/Refract.asm.frag
@@ -37,6 +37,13 @@
 OpDecorate %34 RelaxedPrecision
 OpDecorate %37 RelaxedPrecision
 OpDecorate %39 RelaxedPrecision
+OpDecorate %43 RelaxedPrecision
+OpDecorate %44 RelaxedPrecision
+OpDecorate %45 RelaxedPrecision
+OpDecorate %48 RelaxedPrecision
+OpDecorate %49 RelaxedPrecision
+OpDecorate %50 RelaxedPrecision
+OpDecorate %51 RelaxedPrecision
 %float = OpTypeFloat 32
 %v4float = OpTypeVector %float 4
 %_ptr_Output_v4float = OpTypePointer Output %v4float
@@ -58,6 +65,14 @@
 %_ptr_Uniform_v4float = OpTypePointer Uniform %v4float
 %int_3 = OpConstant %int 3
 %int_4 = OpConstant %int 4
+%v2float = OpTypeVector %float 2
+%float_0_5 = OpConstant %float 0.5
+%float_n0_866025388 = OpConstant %float -0.866025388
+%43 = OpConstantComposite %v2float %float_0_5 %float_n0_866025388
+%v3float = OpTypeVector %float 3
+%float_0 = OpConstant %float 0
+%48 = OpConstantComposite %v3float %float_0_5 %float_0 %float_n0_866025388
+%51 = OpConstantComposite %v4float %float_0_5 %float_0 %float_0 %float_n0_866025388
 %main = OpFunction %void None %14
 %15 = OpLabel
 %17 = OpAccessChain %_ptr_Uniform_float %10 %int_0
@@ -77,5 +92,12 @@
 %39 = OpLoad %float %38
 %30 = OpExtInst %v4float %1 Refract %34 %37 %39
 OpStore %sk_FragColor %30
+%44 = OpLoad %v4float %sk_FragColor
+%45 = OpVectorShuffle %v4float %44 %43 4 5 2 3
+OpStore %sk_FragColor %45
+%49 = OpLoad %v4float %sk_FragColor
+%50 = OpVectorShuffle %v4float %49 %48 4 5 6 3
+OpStore %sk_FragColor %50
+OpStore %sk_FragColor %51
 OpReturn
 OpFunctionEnd
diff --git a/tests/sksl/intrinsics/Refract.glsl b/tests/sksl/intrinsics/Refract.glsl
index bab3625..cb844e2 100644
--- a/tests/sksl/intrinsics/Refract.glsl
+++ b/tests/sksl/intrinsics/Refract.glsl
@@ -8,4 +8,7 @@
 void main() {
     sk_FragColor.x = refract(a, b, c);
     sk_FragColor = refract(d, e, c);
+    sk_FragColor.xy = vec2(0.5, -0.86602538824081421);
+    sk_FragColor.xyz = vec3(0.5, 0.0, -0.86602538824081421);
+    sk_FragColor = vec4(0.5, 0.0, 0.0, -0.86602538824081421);
 }
diff --git a/tests/sksl/intrinsics/Refract.metal b/tests/sksl/intrinsics/Refract.metal
index 67bbdba..e6fbfc8 100644
--- a/tests/sksl/intrinsics/Refract.metal
+++ b/tests/sksl/intrinsics/Refract.metal
@@ -18,5 +18,8 @@
     (void)_out;
     _out.sk_FragColor.x = (refract(float2(_uniforms.a, 0), float2(_uniforms.b, 0), _uniforms.c).x);
     _out.sk_FragColor = refract(_uniforms.d, _uniforms.e, _uniforms.c);
+    _out.sk_FragColor.xy = float2(0.5, -0.86602538824081421);
+    _out.sk_FragColor.xyz = float3(0.5, 0.0, -0.86602538824081421);
+    _out.sk_FragColor = float4(0.5, 0.0, 0.0, -0.86602538824081421);
     return _out;
 }