[tint][glsl] Support binding_array in the writer.

Also updates GenerateBindings to make the end2end tests produce valid
WGSL.

Bug: 411573957
Change-Id: Ifaeed4a508c5b7775f50ccdcdc129e71c63badc5
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/248054
Reviewed-by: James Price <jrprice@google.com>
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
diff --git a/src/tint/lang/glsl/writer/helpers/generate_bindings.cc b/src/tint/lang/glsl/writer/helpers/generate_bindings.cc
index 45db8db..1998384 100644
--- a/src/tint/lang/glsl/writer/helpers/generate_bindings.cc
+++ b/src/tint/lang/glsl/writer/helpers/generate_bindings.cc
@@ -33,6 +33,7 @@
 #include "src/tint/api/common/binding_point.h"
 #include "src/tint/lang/core/ir/module.h"
 #include "src/tint/lang/core/ir/var.h"
+#include "src/tint/lang/core/type/binding_array.h"
 #include "src/tint/lang/core/type/external_texture.h"
 #include "src/tint/lang/core/type/pointer.h"
 #include "src/tint/lang/core/type/storage_texture.h"
@@ -81,9 +82,17 @@
 
             binding::BindingInfo info{get_binding(bp.value())};
             switch (ptr_type->AddressSpace()) {
-                case core::AddressSpace::kHandle:
+                case core::AddressSpace::kHandle: {
+                    // Handle binding_array<handle> before logic dependent on the base handle type.
+                    const core::type::Type* handle_type = ptr_type->StoreType();
+                    uint32_t count = 1;
+                    if (auto* ba = handle_type->As<core::type::BindingArray>()) {
+                        handle_type = ba->ElemType();
+                        count = ba->Count()->As<core::type::ConstantArrayCount>()->value;
+                    }
+
                     Switch(
-                        ptr_type->StoreType(),  //
+                        handle_type,
                         [&](const core::type::Sampler*) { bindings.sampler.emplace(*bp, info); },
                         [&](const core::type::StorageTexture*) {
                             bindings.storage_texture.emplace(*bp, info);
@@ -93,10 +102,13 @@
 
                             // Add all texture variables to the texture-builtin-from-uniform map.
                             bindings.texture_builtins_from_uniform.ubo_contents.push_back(
-                                {.offset = texture_builtin_offset, .count = 1, .binding = info});
-                            texture_builtin_offset++;
+                                {.offset = texture_builtin_offset,
+                                 .count = count,
+                                 .binding = info});
+                            texture_builtin_offset += count;
                         });
                     break;
+                }
                 case core::AddressSpace::kStorage:
                     bindings.storage.emplace(*bp, info);
                     break;
diff --git a/src/tint/lang/glsl/writer/printer/printer.cc b/src/tint/lang/glsl/writer/printer/printer.cc
index 87a387e..5c2c200 100644
--- a/src/tint/lang/glsl/writer/printer/printer.cc
+++ b/src/tint/lang/glsl/writer/printer/printer.cc
@@ -65,6 +65,7 @@
 #include "src/tint/lang/core/ir/validator.h"
 #include "src/tint/lang/core/ir/var.h"
 #include "src/tint/lang/core/type/array.h"
+#include "src/tint/lang/core/type/binding_array.h"
 #include "src/tint/lang/core/type/bool.h"
 #include "src/tint/lang/core/type/depth_multisampled_texture.h"
 #include "src/tint/lang/core/type/depth_texture.h"
@@ -706,6 +707,9 @@
             type,  //
             [&](const core::type::Array* ary) { EmitArrayType(out, ary, name, name_printed); },
             [&](const core::type::Atomic* a) { EmitType(out, a->Type(), name, name_printed); },
+            [&](const core::type::BindingArray* ary) {
+                EmitBindingArrayType(out, ary, name, name_printed);
+            },
             [&](const core::type::Bool*) { out << "bool"; },
             [&](const core::type::I32*) { out << "int"; },
             [&](const core::type::U32*) { out << "uint"; },
@@ -873,6 +877,24 @@
         out << args.str();
     }
 
+    void EmitBindingArrayType(StringStream& out,
+                              const core::type::BindingArray* ary,
+                              const std::string& name,
+                              bool* name_printed) {
+        EmitType(out, ary->ElemType());
+
+        if (!name.empty()) {
+            out << " " << name;
+            if (name_printed) {
+                *name_printed = true;
+            }
+        }
+
+        auto* constant_count = ary->Count()->As<core::type::ConstantArrayCount>();
+        TINT_ASSERT(constant_count != nullptr);
+        out << "[" << constant_count->value << "]";
+    }
+
     void EmitTextureType(StringStream& out, const core::type::Texture* t) {
         TINT_ASSERT(!t->Is<core::type::ExternalTexture>());
 
diff --git a/src/tint/lang/glsl/writer/writer.cc b/src/tint/lang/glsl/writer/writer.cc
index b5435d4..7d29961 100644
--- a/src/tint/lang/glsl/writer/writer.cc
+++ b/src/tint/lang/glsl/writer/writer.cc
@@ -45,9 +45,6 @@
         if (ty->Is<core::type::SubgroupMatrix>()) {
             return Failure("subgroup matrices are not supported by the GLSL backend");
         }
-        if (ty->Is<core::type::BindingArray>()) {
-            return Failure("binding_array are not supported by the GLSL backend");
-        }
     }
 
     // Make sure that every texture variable is in the texture_builtins_from_uniform binding list,
@@ -69,33 +66,47 @@
             return Failure("pixel_local address space is not supported by the GLSL backend");
         }
 
-        // Check texture types that need metadata for texture_builtins_from_uniform.
-        if (ptr->StoreType()->Is<core::type::Texture>() &&
-            !ptr->StoreType()->IsAnyOf<core::type::StorageTexture, core::type::ExternalTexture>()) {
-            bool found = false;
-            auto binding = options.bindings.texture.at(var->BindingPoint().value());
-            for (auto& bp : options.bindings.texture_builtins_from_uniform.ubo_contents) {
-                if (bp.binding == binding) {
-                    found = true;
-                    break;
+        if (ptr->AddressSpace() == core::AddressSpace::kHandle) {
+            const core::type::Type* handle_type = ptr->StoreType();
+            uint32_t count = 1;
+            if (auto* ba = handle_type->As<core::type::BindingArray>()) {
+                handle_type = ba->ElemType();
+                count = ba->Count()->As<core::type::ConstantArrayCount>()->value;
+            }
+
+            // Check texture types that need metadata for texture_builtins_from_uniform.
+            if (handle_type->Is<core::type::Texture>() &&
+                !handle_type->IsAnyOf<core::type::StorageTexture, core::type::ExternalTexture>()) {
+                bool found = false;
+                auto binding = options.bindings.texture.at(var->BindingPoint().value());
+                for (auto& bp : options.bindings.texture_builtins_from_uniform.ubo_contents) {
+                    if (bp.binding == binding) {
+                        if (bp.count < count) {
+                            return Failure(
+                                "binding_array of textures doesn't have enough data in "
+                                "texture_builtins_from_uniform list");
+                        }
+                        found = true;
+                        break;
+                    }
+                }
+                if (!found) {
+                    return Failure("texture missing from texture_builtins_from_uniform list");
                 }
             }
-            if (!found) {
-                return Failure("texture missing from texture_builtins_from_uniform list");
-            }
-        }
 
-        // Check texel formats for read-write storage textures when targeting ES.
-        if (options.version.IsES()) {
-            if (auto* st = ptr->StoreType()->As<core::type::StorageTexture>()) {
-                if (st->Access() == core::Access::kReadWrite) {
-                    switch (st->TexelFormat()) {
-                        case core::TexelFormat::kR32Float:
-                        case core::TexelFormat::kR32Sint:
-                        case core::TexelFormat::kR32Uint:
-                            break;
-                        default:
-                            return Failure("unsupported read-write storage texture format");
+            // Check texel formats for read-write storage textures when targeting ES.
+            if (options.version.IsES()) {
+                if (auto* st = handle_type->As<core::type::StorageTexture>()) {
+                    if (st->Access() == core::Access::kReadWrite) {
+                        switch (st->TexelFormat()) {
+                            case core::TexelFormat::kR32Float:
+                            case core::TexelFormat::kR32Sint:
+                            case core::TexelFormat::kR32Uint:
+                                break;
+                            default:
+                                return Failure("unsupported read-write storage texture format");
+                        }
                     }
                 }
             }
diff --git a/test/tint/binding_array/access_as_function_argument.wgsl.expected.glsl b/test/tint/binding_array/access_as_function_argument.wgsl.expected.glsl
index 82220c4..949427e 100644
--- a/test/tint/binding_array/access_as_function_argument.wgsl.expected.glsl
+++ b/test/tint/binding_array/access_as_function_argument.wgsl.expected.glsl
@@ -1,12 +1,31 @@
 SKIP: FAILED
 
-../../src/tint/lang/glsl/writer/raise/texture_builtins_from_uniform.cc:137 internal compiler error: Switch() matched no cases. Type: tint::core::ir::Access
+Error parsing GLSL shader:
+ERROR: 0:14: 'variable indexing sampler array' : not supported for this version or the enabled extensions 
+ERROR: 0:14: '' : compilation terminated 
+ERROR: 2 compilation errors.  No code generated.
 
-********************************************************************
-*  The tint shader compiler has encountered an unexpected error.   *
-*                                                                  *
-*  Please help us fix this issue by submitting a bug report at     *
-*  crbug.com/tint with the source program that triggered the bug.  *
-********************************************************************
 
-tint executable returned error: signal: trace/BPT trap
+
+#version 310 es
+precision highp float;
+precision highp int;
+
+layout(binding = 0, std140)
+uniform f_TintTextureUniformData_ubo {
+  uvec4 metadata[1];
+} v;
+uniform highp sampler2D f_sampled_textures[4];
+void do_texture_load(uint t_indices[1]) {
+  uint v_1 = (0u + uint(t_indices[0u]));
+  uint v_2 = (v.metadata[(v_1 / 4u)][(v_1 % 4u)] - 1u);
+  uint v_3 = min(uint(0), v_2);
+  uvec2 v_4 = (uvec2(textureSize(f_sampled_textures[t_indices[0u]], int(v_3))) - uvec2(1u));
+  ivec2 v_5 = ivec2(min(uvec2(ivec2(0)), v_4));
+  vec4 texture_load = texelFetch(f_sampled_textures[t_indices[0u]], v_5, int(v_3));
+}
+void main() {
+  do_texture_load(uint[1](uint(0)));
+}
+
+tint executable returned error: exit status 1
diff --git a/test/tint/binding_array/access_constant.wgsl.expected.glsl b/test/tint/binding_array/access_constant.wgsl.expected.glsl
index 82220c4..ca8abe2 100644
--- a/test/tint/binding_array/access_constant.wgsl.expected.glsl
+++ b/test/tint/binding_array/access_constant.wgsl.expected.glsl
@@ -1,12 +1,17 @@
-SKIP: FAILED
+#version 310 es
+precision highp float;
+precision highp int;
 
-../../src/tint/lang/glsl/writer/raise/texture_builtins_from_uniform.cc:137 internal compiler error: Switch() matched no cases. Type: tint::core::ir::Access
-
-********************************************************************
-*  The tint shader compiler has encountered an unexpected error.   *
-*                                                                  *
-*  Please help us fix this issue by submitting a bug report at     *
-*  crbug.com/tint with the source program that triggered the bug.  *
-********************************************************************
-
-tint executable returned error: signal: trace/BPT trap
+layout(binding = 0, std140)
+uniform f_TintTextureUniformData_ubo {
+  uvec4 metadata[1];
+} v;
+uniform highp sampler2D f_sampled_textures[4];
+void main() {
+  uint v_1 = (0u + uint(0));
+  uint v_2 = (v.metadata[(v_1 / 4u)][(v_1 % 4u)] - 1u);
+  uint v_3 = min(uint(0), v_2);
+  uvec2 v_4 = (uvec2(textureSize(f_sampled_textures[0], int(v_3))) - uvec2(1u));
+  ivec2 v_5 = ivec2(min(uvec2(ivec2(0)), v_4));
+  vec4 texture_load = texelFetch(f_sampled_textures[0], v_5, int(v_3));
+}
diff --git a/test/tint/binding_array/access_uniform.wgsl.expected.glsl b/test/tint/binding_array/access_uniform.wgsl.expected.glsl
index 82220c4..15a2eb7 100644
--- a/test/tint/binding_array/access_uniform.wgsl.expected.glsl
+++ b/test/tint/binding_array/access_uniform.wgsl.expected.glsl
@@ -1,12 +1,33 @@
 SKIP: FAILED
 
-../../src/tint/lang/glsl/writer/raise/texture_builtins_from_uniform.cc:137 internal compiler error: Switch() matched no cases. Type: tint::core::ir::Access
+Error parsing GLSL shader:
+ERROR: 0:19: 'variable indexing sampler array' : not supported for this version or the enabled extensions 
+ERROR: 0:19: '' : compilation terminated 
+ERROR: 2 compilation errors.  No code generated.
 
-********************************************************************
-*  The tint shader compiler has encountered an unexpected error.   *
-*                                                                  *
-*  Please help us fix this issue by submitting a bug report at     *
-*  crbug.com/tint with the source program that triggered the bug.  *
-********************************************************************
 
-tint executable returned error: signal: trace/BPT trap
+
+#version 310 es
+precision highp float;
+precision highp int;
+
+layout(binding = 0, std140)
+uniform f_index_block_ubo {
+  uint inner;
+} v;
+layout(binding = 0, std140)
+uniform f_TintTextureUniformData_ubo {
+  uvec4 metadata[1];
+} v_1;
+uniform highp sampler2D f_sampled_textures[4];
+void main() {
+  uint v_2 = v.inner;
+  uint v_3 = (0u + uint(v_2));
+  uint v_4 = (v_1.metadata[(v_3 / 4u)][(v_3 % 4u)] - 1u);
+  uint v_5 = min(uint(0), v_4);
+  uvec2 v_6 = (uvec2(textureSize(f_sampled_textures[v_2], int(v_5))) - uvec2(1u));
+  ivec2 v_7 = ivec2(min(uvec2(ivec2(0)), v_6));
+  vec4 texture_load = texelFetch(f_sampled_textures[v_2], v_7, int(v_5));
+}
+
+tint executable returned error: exit status 1
diff --git a/test/tint/binding_array/as_function_parameter.wgsl.expected.glsl b/test/tint/binding_array/as_function_parameter.wgsl.expected.glsl
index 82220c4..c8e288b 100644
--- a/test/tint/binding_array/as_function_parameter.wgsl.expected.glsl
+++ b/test/tint/binding_array/as_function_parameter.wgsl.expected.glsl
@@ -1,12 +1,20 @@
-SKIP: FAILED
+#version 310 es
+precision highp float;
+precision highp int;
 
-../../src/tint/lang/glsl/writer/raise/texture_builtins_from_uniform.cc:137 internal compiler error: Switch() matched no cases. Type: tint::core::ir::Access
-
-********************************************************************
-*  The tint shader compiler has encountered an unexpected error.   *
-*                                                                  *
-*  Please help us fix this issue by submitting a bug report at     *
-*  crbug.com/tint with the source program that triggered the bug.  *
-********************************************************************
-
-tint executable returned error: signal: trace/BPT trap
+layout(binding = 0, std140)
+uniform f_TintTextureUniformData_ubo {
+  uvec4 metadata[1];
+} v;
+uniform highp sampler2D f_sampled_textures[4];
+void do_texture_load() {
+  uint v_1 = (0u + uint(0));
+  uint v_2 = (v.metadata[(v_1 / 4u)][(v_1 % 4u)] - 1u);
+  uint v_3 = min(uint(0), v_2);
+  uvec2 v_4 = (uvec2(textureSize(f_sampled_textures[0], int(v_3))) - uvec2(1u));
+  ivec2 v_5 = ivec2(min(uvec2(ivec2(0)), v_4));
+  vec4 texture_load = texelFetch(f_sampled_textures[0], v_5, int(v_3));
+}
+void main() {
+  do_texture_load();
+}