spirv-reader: float array layer conversion uses round-to-even

Vulkan requires round-to-nearest when converting from a float
coordinate to an integer array layer. It prefers round-to-nearest-even.
Use round-to-nearest-even, instad of relying on the rounding behaviour
of the i32(f32) overload.

Fixes: tint:1316
Change-Id: I43624e25e8ea27d3a6e04841a911bbb9418810d0
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/70343
Auto-Submit: David Neto <dneto@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
diff --git a/src/reader/spirv/function.cc b/src/reader/spirv/function.cc
index d987039..f086901 100644
--- a/src/reader/spirv/function.cc
+++ b/src/reader/spirv/function.cc
@@ -5697,9 +5697,16 @@
     result.push_back(prefix_swizzle_expr());
 
     // Now get the array index.
-    const ast::Expression* array_index = create<ast::MemberAccessorExpression>(
-        Source{}, raw_coords.expr, Swizzle(num_axes));
-    // Convert it to a signed integer type, if needed
+    const ast::Expression* array_index =
+        builder_.MemberAccessor(raw_coords.expr, Swizzle(num_axes));
+    if (component_type->IsFloatScalar()) {
+      // When converting from a float array layer to integer, Vulkan requires
+      // round-to-nearest, with preference for round-to-nearest-even.
+      // But i32(f32) in WGSL has unspecified rounding mode, so we have to
+      // explicitly specify the rounding.
+      array_index = builder_.Call("round", array_index);
+    }
+    // Convert it to a signed integer type, if needed.
     result.push_back(ToI32({component_type, array_index}).expr);
   } else {
     if (num_coords_supplied == num_coords_required && !is_proj) {
diff --git a/src/reader/spirv/parser_impl_handle_test.cc b/src/reader/spirv/parser_impl_handle_test.cc
index b121e93..0215bc6 100644
--- a/src/reader/spirv/parser_impl_handle_test.cc
+++ b/src/reader/spirv/parser_impl_handle_test.cc
@@ -1607,7 +1607,7 @@
             R"([[group(0), binding(0)]] var x_10 : sampler;
 
 [[group(2), binding(1)]] var x_20 : texture_2d_array<f32>;)",
-            "textureSample(x_20, x_10, coords123.xy, i32(coords123.z))"},
+            "textureSample(x_20, x_10, coords123.xy, i32(round(coords123.z)))"},
 
         // OpImageSampleImplicitLod with ConstOffset
         ImageAccessCase{
@@ -1627,7 +1627,7 @@
             R"([[group(0), binding(0)]] var x_10 : sampler;
 
 [[group(2), binding(1)]] var x_20 : texture_2d_array<f32>;)",
-            R"(textureSample(x_20, x_10, coords123.xy, i32(coords123.z), vec2<i32>(3, 4)))"},
+            R"(textureSample(x_20, x_10, coords123.xy, i32(round(coords123.z)), vec2<i32>(3, 4)))"},
 
         // OpImageSampleImplicitLod with Bias
         ImageAccessCase{"%float 2D 0 0 0 1 Unknown",
@@ -1646,7 +1646,7 @@
             R"([[group(0), binding(0)]] var x_10 : sampler;
 
 [[group(2), binding(1)]] var x_20 : texture_2d_array<f32>;)",
-            R"(textureSampleBias(x_20, x_10, coords123.xy, i32(coords123.z), 7.0))"},
+            R"(textureSampleBias(x_20, x_10, coords123.xy, i32(round(coords123.z)), 7.0))"},
 
         // OpImageSampleImplicitLod with Bias and signed ConstOffset
         ImageAccessCase{
@@ -1679,7 +1679,7 @@
             R"([[group(0), binding(0)]] var x_10 : sampler;
 
 [[group(2), binding(1)]] var x_20 : texture_2d_array<f32>;)",
-            R"(textureSampleBias(x_20, x_10, coords123.xy, i32(coords123.z), 7.0, vec2<i32>(3, 4))"}));
+            R"(textureSampleBias(x_20, x_10, coords123.xy, i32(round(coords123.z)), 7.0, vec2<i32>(3, 4))"}));
 
 INSTANTIATE_TEST_SUITE_P(
     // This test shows the use of a sampled image used with both regular
@@ -1730,7 +1730,7 @@
             R"([[group(0), binding(0)]] var x_10 : sampler_comparison;
 
 [[group(2), binding(1)]] var x_20 : texture_depth_2d_array;)",
-            R"(textureSampleCompare(x_20, x_10, coords123.xy, i32(coords123.z), 0.200000003))"},
+            R"(textureSampleCompare(x_20, x_10, coords123.xy, i32(round(coords123.z)), 0.200000003))"},
         // ImageSampleDrefImplicitLod with ConstOffset
         ImageAccessCase{
             "%float 2D 0 0 0 1 Unknown",
@@ -1749,7 +1749,7 @@
             R"([[group(0), binding(0)]] var x_10 : sampler_comparison;
 
 [[group(2), binding(1)]] var x_20 : texture_depth_2d_array;)",
-            R"(textureSampleCompare(x_20, x_10, coords123.xy, i32(coords123.z), 0.200000003, vec2<i32>(3, 4)))"}));
+            R"(textureSampleCompare(x_20, x_10, coords123.xy, i32(round(coords123.z)), 0.200000003, vec2<i32>(3, 4)))"}));
 
 INSTANTIATE_TEST_SUITE_P(
     ImageSampleDrefExplicitLod,
@@ -1775,7 +1775,7 @@
             R"([[group(0), binding(0)]] var x_10 : sampler_comparison;
 
 [[group(2), binding(1)]] var x_20 : texture_depth_2d_array;)",
-            R"(textureSampleCompareLevel(x_20, x_10, coords123.xy, i32(coords123.z), 0.200000003))"},
+            R"(textureSampleCompareLevel(x_20, x_10, coords123.xy, i32(round(coords123.z)), 0.200000003))"},
         // 2D, ConstOffset
         ImageAccessCase{
             "%float 2D 1 0 0 1 Unknown",
@@ -1796,7 +1796,7 @@
             R"([[group(0), binding(0)]] var x_10 : sampler_comparison;
 
 [[group(2), binding(1)]] var x_20 : texture_depth_2d_array;)",
-            R"(textureSampleCompareLevel(x_20, x_10, coords123.xy, i32(coords123.z), 0.200000003, vec2<i32>(3, 4)))"},
+            R"(textureSampleCompareLevel(x_20, x_10, coords123.xy, i32(round(coords123.z)), 0.200000003, vec2<i32>(3, 4)))"},
         // Cube
         ImageAccessCase{
             "%float Cube 1 0 0 1 Unknown",
@@ -1814,7 +1814,7 @@
             R"([[group(0), binding(0)]] var x_10 : sampler_comparison;
 
 [[group(2), binding(1)]] var x_20 : texture_depth_cube_array;)",
-            R"(textureSampleCompareLevel(x_20, x_10, coords1234.xyz, i32(coords1234.w), 0.200000003))"}));
+            R"(textureSampleCompareLevel(x_20, x_10, coords1234.xyz, i32(round(coords1234.w)), 0.200000003))"}));
 
 INSTANTIATE_TEST_SUITE_P(
     ImageSampleExplicitLod_UsingLod,
@@ -1838,7 +1838,7 @@
             R"([[group(0), binding(0)]] var x_10 : sampler;
 
 [[group(2), binding(1)]] var x_20 : texture_2d_array<f32>;)",
-            R"(textureSampleLevel(x_20, x_10, coords123.xy, i32(coords123.z), 0.0))"},
+            R"(textureSampleLevel(x_20, x_10, coords123.xy, i32(round(coords123.z)), 0.0))"},
 
         // OpImageSampleExplicitLod - using Lod and ConstOffset
         ImageAccessCase{
@@ -1872,7 +1872,7 @@
             R"([[group(0), binding(0)]] var x_10 : sampler;
 
 [[group(2), binding(1)]] var x_20 : texture_2d_array<f32>;)",
-            R"(textureSampleLevel(x_20, x_10, coords123.xy, i32(coords123.z), 0.0, vec2<i32>(3, 4)))"}));
+            R"(textureSampleLevel(x_20, x_10, coords123.xy, i32(round(coords123.z)), 0.0, vec2<i32>(3, 4)))"}));
 
 INSTANTIATE_TEST_SUITE_P(
     ImageSampleExplicitLod_UsingGrad,
@@ -1897,7 +1897,7 @@
             R"([[group(0), binding(0)]] var x_10 : sampler;
 
 [[group(2), binding(1)]] var x_20 : texture_2d_array<f32>;)",
-            R"(textureSampleGrad(x_20, x_10, coords123.xy, i32(coords123.z), vf12, vf21))"},
+            R"(textureSampleGrad(x_20, x_10, coords123.xy, i32(round(coords123.z)), vf12, vf21))"},
 
         // OpImageSampleExplicitLod - using Grad and ConstOffset
         ImageAccessCase{
@@ -1930,7 +1930,7 @@
             R"([[group(0), binding(0)]] var x_10 : sampler;
 
 [[group(2), binding(1)]] var x_20 : texture_2d_array<f32>;)",
-            R"(textureSampleGrad(x_20, x_10, coords123.xy, i32(coords123.z), vf12, vf21, vec2<i32>(3, 4)))"},
+            R"(textureSampleGrad(x_20, x_10, coords123.xy, i32(round(coords123.z)), vf12, vf21, vec2<i32>(3, 4)))"},
 
         // OpImageSampleExplicitLod arrayed - using Grad and unsigned
         // ConstOffset
@@ -1942,7 +1942,7 @@
             R"([[group(0), binding(0)]] var x_10 : sampler;
 
 [[group(2), binding(1)]] var x_20 : texture_2d_array<f32>;)",
-            R"(textureSampleGrad(x_20, x_10, coords123.xy, i32(coords123.z), vf12, vf21, vec2<i32>(vec2<u32>(3u, 4u))))"}));
+            R"(textureSampleGrad(x_20, x_10, coords123.xy, i32(round(coords123.z)), vf12, vf21, vec2<i32>(vec2<u32>(3u, 4u))))"}));
 
 // Test crbug.com/378:
 // In WGSL, sampling from depth texture with explicit level of detail
@@ -3088,17 +3088,17 @@
                               "%result = OpImageSampleImplicitLod %v4float "
                               "%sampled_image %vf12",
                               "",
-                              {"vf12.x", "i32(vf12.y)"}},
+                              {"vf12.x", "i32(round(vf12.y))"}},
                              {"%float 1D 0 1 0 1 Unknown",
                               "%result = OpImageSampleImplicitLod %v4float "
                               "%sampled_image %vf123",  // one excess arg
                               "",
-                              {"vf123.x", "i32(vf123.y)"}},
+                              {"vf123.x", "i32(round(vf123.y))"}},
                              {"%float 1D 0 1 0 1 Unknown",
                               "%result = OpImageSampleImplicitLod %v4float "
                               "%sampled_image %vf1234",  // two excess args
                               "",
-                              {"vf1234.x", "i32(vf1234.y)"}}}));
+                              {"vf1234.x", "i32(round(vf1234.y))"}}}));
 
 INSTANTIATE_TEST_SUITE_P(Good_2D,
                          SpvParserHandleTest_ImageCoordsTest,
@@ -3126,12 +3126,12 @@
                               "%result = OpImageSampleImplicitLod %v4float "
                               "%sampled_image %vf123",
                               "",
-                              {"vf123.xy", "i32(vf123.z)"}},
+                              {"vf123.xy", "i32(round(vf123.z))"}},
                              {"%float 2D 0 1 0 1 Unknown",
                               "%result = OpImageSampleImplicitLod %v4float "
                               "%sampled_image %vf1234",  // one excess arg
                               "",
-                              {"vf1234.xy", "i32(vf1234.z)"}}}));
+                              {"vf1234.xy", "i32(round(vf1234.z))"}}}));
 
 INSTANTIATE_TEST_SUITE_P(Good_3D,
                          SpvParserHandleTest_ImageCoordsTest,
@@ -3175,7 +3175,7 @@
                               "%v4float "
                               "%sampled_image %vf1234",
                               "",
-                              {"vf1234.xyz", "i32(vf1234.w)"}}}));
+                              {"vf1234.xyz", "i32(round(vf1234.w))"}}}));
 
 INSTANTIATE_TEST_SUITE_P(
     PreserveFloatCoords_NonArrayed,
@@ -3228,23 +3228,23 @@
         {"%float 2D 0 1 0 1 Unknown",
          "%result = OpImageSampleImplicitLod %v4float %sampled_image %vf123",
          "",
-         {"vf123.xy", "i32(vf123.z)"}},
+         {"vf123.xy", "i32(round(vf123.z))"}},
 
         {"%float 2D 0 1 0 1 Unknown",
          "%result = OpImageSampleExplicitLod %v4float %sampled_image %vf123 "
          "Lod %f1",
          "",
-         {"vf123.xy", "i32(vf123.z)"}},
+         {"vf123.xy", "i32(round(vf123.z))"}},
         {"%float 2D 1 1 0 1 Unknown",
          "%result = OpImageSampleDrefImplicitLod %float %sampled_image "
          "%vf123 %depth",
          "",
-         {"vf123.xy", "i32(vf123.z)"}},
+         {"vf123.xy", "i32(round(vf123.z))"}},
         {"%float 2D 1 1 0 1 Unknown",
          "%result = OpImageSampleDrefExplicitLod %float %sampled_image "
          "%vf123 %depth Lod %float_0",
          "",
-         {"vf123.xy", "i32(vf123.z)"}}}));
+         {"vf123.xy", "i32(round(vf123.z))"}}}));
 
 INSTANTIATE_TEST_SUITE_P(
     PreserveIntCoords_NonArrayed,