| // Copyright 2021 The Tint Authors. | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use this file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // http://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| #include "src/transform/array_length_from_uniform.h" | |
| #include <utility> | |
| #include "src/transform/simplify_pointers.h" | |
| #include "src/transform/test_helper.h" | |
| #include "src/transform/unshadow.h" | |
| namespace tint { | |
| namespace transform { | |
| namespace { | |
| using ArrayLengthFromUniformTest = TransformTest; | |
| TEST_F(ArrayLengthFromUniformTest, Error_MissingTransformData) { | |
| auto* src = ""; | |
| auto* expect = | |
| "error: missing transform data for " | |
| "tint::transform::ArrayLengthFromUniform"; | |
| auto got = Run<Unshadow, SimplifyPointers, ArrayLengthFromUniform>(src); | |
| EXPECT_EQ(expect, str(got)); | |
| } | |
| TEST_F(ArrayLengthFromUniformTest, Error_MissingSimplifyPointers) { | |
| auto* src = ""; | |
| auto* expect = | |
| "error: tint::transform::ArrayLengthFromUniform depends on " | |
| "tint::transform::SimplifyPointers but the dependency was not run"; | |
| auto got = Run<ArrayLengthFromUniform>(src); | |
| EXPECT_EQ(expect, str(got)); | |
| } | |
| TEST_F(ArrayLengthFromUniformTest, Basic) { | |
| auto* src = R"( | |
| [[block]] | |
| struct SB { | |
| x : i32; | |
| arr : array<i32>; | |
| }; | |
| [[group(0), binding(0)]] var<storage, read> sb : SB; | |
| [[stage(compute), workgroup_size(1)]] | |
| fn main() { | |
| var len : u32 = arrayLength(&sb.arr); | |
| } | |
| )"; | |
| auto* expect = R"( | |
| [[block]] | |
| struct tint_symbol { | |
| buffer_size : array<vec4<u32>, 1u>; | |
| }; | |
| [[group(0), binding(30)]] var<uniform> tint_symbol_1 : tint_symbol; | |
| [[block]] | |
| struct SB { | |
| x : i32; | |
| arr : array<i32>; | |
| }; | |
| [[group(0), binding(0)]] var<storage, read> sb : SB; | |
| [[stage(compute), workgroup_size(1)]] | |
| fn main() { | |
| var len : u32 = ((tint_symbol_1.buffer_size[0u][0u] - 4u) / 4u); | |
| } | |
| )"; | |
| ArrayLengthFromUniform::Config cfg({0, 30u}); | |
| cfg.bindpoint_to_size_index.emplace(sem::BindingPoint{0, 0}, 0); | |
| DataMap data; | |
| data.Add<ArrayLengthFromUniform::Config>(std::move(cfg)); | |
| auto got = Run<Unshadow, SimplifyPointers, ArrayLengthFromUniform>(src, data); | |
| EXPECT_EQ(expect, str(got)); | |
| EXPECT_EQ(std::unordered_set<uint32_t>({0}), | |
| got.data.Get<ArrayLengthFromUniform::Result>()->used_size_indices); | |
| } | |
| TEST_F(ArrayLengthFromUniformTest, WithStride) { | |
| auto* src = R"( | |
| [[block]] | |
| struct SB { | |
| x : i32; | |
| y : f32; | |
| arr : [[stride(64)]] array<i32>; | |
| }; | |
| [[group(0), binding(0)]] var<storage, read> sb : SB; | |
| [[stage(compute), workgroup_size(1)]] | |
| fn main() { | |
| var len : u32 = arrayLength(&sb.arr); | |
| } | |
| )"; | |
| auto* expect = R"( | |
| [[block]] | |
| struct tint_symbol { | |
| buffer_size : array<vec4<u32>, 1u>; | |
| }; | |
| [[group(0), binding(30)]] var<uniform> tint_symbol_1 : tint_symbol; | |
| [[block]] | |
| struct SB { | |
| x : i32; | |
| y : f32; | |
| arr : [[stride(64)]] array<i32>; | |
| }; | |
| [[group(0), binding(0)]] var<storage, read> sb : SB; | |
| [[stage(compute), workgroup_size(1)]] | |
| fn main() { | |
| var len : u32 = ((tint_symbol_1.buffer_size[0u][0u] - 8u) / 64u); | |
| } | |
| )"; | |
| ArrayLengthFromUniform::Config cfg({0, 30u}); | |
| cfg.bindpoint_to_size_index.emplace(sem::BindingPoint{0, 0}, 0); | |
| DataMap data; | |
| data.Add<ArrayLengthFromUniform::Config>(std::move(cfg)); | |
| auto got = Run<Unshadow, SimplifyPointers, ArrayLengthFromUniform>(src, data); | |
| EXPECT_EQ(expect, str(got)); | |
| EXPECT_EQ(std::unordered_set<uint32_t>({0}), | |
| got.data.Get<ArrayLengthFromUniform::Result>()->used_size_indices); | |
| } | |
| TEST_F(ArrayLengthFromUniformTest, MultipleStorageBuffers) { | |
| auto* src = R"( | |
| [[block]] | |
| struct SB1 { | |
| x : i32; | |
| arr1 : array<i32>; | |
| }; | |
| [[block]] | |
| struct SB2 { | |
| x : i32; | |
| arr2 : array<vec4<f32>>; | |
| }; | |
| [[block]] | |
| struct SB3 { | |
| x : i32; | |
| arr3 : array<vec4<f32>>; | |
| }; | |
| [[block]] | |
| struct SB4 { | |
| x : i32; | |
| arr4 : array<vec4<f32>>; | |
| }; | |
| [[block]] | |
| struct SB5 { | |
| x : i32; | |
| arr5 : array<vec4<f32>>; | |
| }; | |
| [[group(0), binding(2)]] var<storage, read> sb1 : SB1; | |
| [[group(1), binding(2)]] var<storage, read> sb2 : SB2; | |
| [[group(2), binding(2)]] var<storage, read> sb3 : SB3; | |
| [[group(3), binding(2)]] var<storage, read> sb4 : SB4; | |
| [[group(4), binding(2)]] var<storage, read> sb5 : SB5; | |
| [[stage(compute), workgroup_size(1)]] | |
| fn main() { | |
| var len1 : u32 = arrayLength(&(sb1.arr1)); | |
| var len2 : u32 = arrayLength(&(sb2.arr2)); | |
| var len3 : u32 = arrayLength(&(sb3.arr3)); | |
| var len4 : u32 = arrayLength(&(sb4.arr4)); | |
| var len5 : u32 = arrayLength(&(sb5.arr5)); | |
| var x : u32 = (len1 + len2 + len3 + len4 + len5); | |
| } | |
| )"; | |
| auto* expect = R"( | |
| [[block]] | |
| struct tint_symbol { | |
| buffer_size : array<vec4<u32>, 2u>; | |
| }; | |
| [[group(0), binding(30)]] var<uniform> tint_symbol_1 : tint_symbol; | |
| [[block]] | |
| struct SB1 { | |
| x : i32; | |
| arr1 : array<i32>; | |
| }; | |
| [[block]] | |
| struct SB2 { | |
| x : i32; | |
| arr2 : array<vec4<f32>>; | |
| }; | |
| [[block]] | |
| struct SB3 { | |
| x : i32; | |
| arr3 : array<vec4<f32>>; | |
| }; | |
| [[block]] | |
| struct SB4 { | |
| x : i32; | |
| arr4 : array<vec4<f32>>; | |
| }; | |
| [[block]] | |
| struct SB5 { | |
| x : i32; | |
| arr5 : array<vec4<f32>>; | |
| }; | |
| [[group(0), binding(2)]] var<storage, read> sb1 : SB1; | |
| [[group(1), binding(2)]] var<storage, read> sb2 : SB2; | |
| [[group(2), binding(2)]] var<storage, read> sb3 : SB3; | |
| [[group(3), binding(2)]] var<storage, read> sb4 : SB4; | |
| [[group(4), binding(2)]] var<storage, read> sb5 : SB5; | |
| [[stage(compute), workgroup_size(1)]] | |
| fn main() { | |
| var len1 : u32 = ((tint_symbol_1.buffer_size[0u][0u] - 4u) / 4u); | |
| var len2 : u32 = ((tint_symbol_1.buffer_size[0u][1u] - 16u) / 16u); | |
| var len3 : u32 = ((tint_symbol_1.buffer_size[0u][2u] - 16u) / 16u); | |
| var len4 : u32 = ((tint_symbol_1.buffer_size[0u][3u] - 16u) / 16u); | |
| var len5 : u32 = ((tint_symbol_1.buffer_size[1u][0u] - 16u) / 16u); | |
| var x : u32 = ((((len1 + len2) + len3) + len4) + len5); | |
| } | |
| )"; | |
| ArrayLengthFromUniform::Config cfg({0, 30u}); | |
| cfg.bindpoint_to_size_index.emplace(sem::BindingPoint{0, 2u}, 0); | |
| cfg.bindpoint_to_size_index.emplace(sem::BindingPoint{1u, 2u}, 1); | |
| cfg.bindpoint_to_size_index.emplace(sem::BindingPoint{2u, 2u}, 2); | |
| cfg.bindpoint_to_size_index.emplace(sem::BindingPoint{3u, 2u}, 3); | |
| cfg.bindpoint_to_size_index.emplace(sem::BindingPoint{4u, 2u}, 4); | |
| DataMap data; | |
| data.Add<ArrayLengthFromUniform::Config>(std::move(cfg)); | |
| auto got = Run<Unshadow, SimplifyPointers, ArrayLengthFromUniform>(src, data); | |
| EXPECT_EQ(expect, str(got)); | |
| EXPECT_EQ(std::unordered_set<uint32_t>({0, 1, 2, 3, 4}), | |
| got.data.Get<ArrayLengthFromUniform::Result>()->used_size_indices); | |
| } | |
| TEST_F(ArrayLengthFromUniformTest, MultipleUnusedStorageBuffers) { | |
| auto* src = R"( | |
| [[block]] | |
| struct SB1 { | |
| x : i32; | |
| arr1 : array<i32>; | |
| }; | |
| [[block]] | |
| struct SB2 { | |
| x : i32; | |
| arr2 : array<vec4<f32>>; | |
| }; | |
| [[block]] | |
| struct SB3 { | |
| x : i32; | |
| arr3 : array<vec4<f32>>; | |
| }; | |
| [[block]] | |
| struct SB4 { | |
| x : i32; | |
| arr4 : array<vec4<f32>>; | |
| }; | |
| [[block]] | |
| struct SB5 { | |
| x : i32; | |
| arr5 : array<vec4<f32>>; | |
| }; | |
| [[group(0), binding(2)]] var<storage, read> sb1 : SB1; | |
| [[group(1), binding(2)]] var<storage, read> sb2 : SB2; | |
| [[group(2), binding(2)]] var<storage, read> sb3 : SB3; | |
| [[group(3), binding(2)]] var<storage, read> sb4 : SB4; | |
| [[group(4), binding(2)]] var<storage, read> sb5 : SB5; | |
| [[stage(compute), workgroup_size(1)]] | |
| fn main() { | |
| var len1 : u32 = arrayLength(&(sb1.arr1)); | |
| var len3 : u32 = arrayLength(&(sb3.arr3)); | |
| var x : u32 = (len1 + len3); | |
| } | |
| )"; | |
| auto* expect = R"( | |
| [[block]] | |
| struct tint_symbol { | |
| buffer_size : array<vec4<u32>, 1u>; | |
| }; | |
| [[group(0), binding(30)]] var<uniform> tint_symbol_1 : tint_symbol; | |
| [[block]] | |
| struct SB1 { | |
| x : i32; | |
| arr1 : array<i32>; | |
| }; | |
| [[block]] | |
| struct SB2 { | |
| x : i32; | |
| arr2 : array<vec4<f32>>; | |
| }; | |
| [[block]] | |
| struct SB3 { | |
| x : i32; | |
| arr3 : array<vec4<f32>>; | |
| }; | |
| [[block]] | |
| struct SB4 { | |
| x : i32; | |
| arr4 : array<vec4<f32>>; | |
| }; | |
| [[block]] | |
| struct SB5 { | |
| x : i32; | |
| arr5 : array<vec4<f32>>; | |
| }; | |
| [[group(0), binding(2)]] var<storage, read> sb1 : SB1; | |
| [[group(1), binding(2)]] var<storage, read> sb2 : SB2; | |
| [[group(2), binding(2)]] var<storage, read> sb3 : SB3; | |
| [[group(3), binding(2)]] var<storage, read> sb4 : SB4; | |
| [[group(4), binding(2)]] var<storage, read> sb5 : SB5; | |
| [[stage(compute), workgroup_size(1)]] | |
| fn main() { | |
| var len1 : u32 = ((tint_symbol_1.buffer_size[0u][0u] - 4u) / 4u); | |
| var len3 : u32 = ((tint_symbol_1.buffer_size[0u][2u] - 16u) / 16u); | |
| var x : u32 = (len1 + len3); | |
| } | |
| )"; | |
| ArrayLengthFromUniform::Config cfg({0, 30u}); | |
| cfg.bindpoint_to_size_index.emplace(sem::BindingPoint{0, 2u}, 0); | |
| cfg.bindpoint_to_size_index.emplace(sem::BindingPoint{1u, 2u}, 1); | |
| cfg.bindpoint_to_size_index.emplace(sem::BindingPoint{2u, 2u}, 2); | |
| cfg.bindpoint_to_size_index.emplace(sem::BindingPoint{3u, 2u}, 3); | |
| cfg.bindpoint_to_size_index.emplace(sem::BindingPoint{4u, 2u}, 4); | |
| DataMap data; | |
| data.Add<ArrayLengthFromUniform::Config>(std::move(cfg)); | |
| auto got = Run<Unshadow, SimplifyPointers, ArrayLengthFromUniform>(src, data); | |
| EXPECT_EQ(expect, str(got)); | |
| EXPECT_EQ(std::unordered_set<uint32_t>({0, 2}), | |
| got.data.Get<ArrayLengthFromUniform::Result>()->used_size_indices); | |
| } | |
| TEST_F(ArrayLengthFromUniformTest, NoArrayLengthCalls) { | |
| auto* src = R"( | |
| [[block]] | |
| struct SB { | |
| x : i32; | |
| arr : array<i32>; | |
| }; | |
| [[group(0), binding(0)]] var<storage, read> sb : SB; | |
| [[stage(compute), workgroup_size(1)]] | |
| fn main() { | |
| ignore(&(sb.arr)); | |
| } | |
| )"; | |
| ArrayLengthFromUniform::Config cfg({0, 30u}); | |
| cfg.bindpoint_to_size_index.emplace(sem::BindingPoint{0, 0}, 0); | |
| DataMap data; | |
| data.Add<ArrayLengthFromUniform::Config>(std::move(cfg)); | |
| auto got = Run<Unshadow, SimplifyPointers, ArrayLengthFromUniform>(src, data); | |
| EXPECT_EQ(src, str(got)); | |
| EXPECT_EQ(std::unordered_set<uint32_t>(), | |
| got.data.Get<ArrayLengthFromUniform::Result>()->used_size_indices); | |
| } | |
| TEST_F(ArrayLengthFromUniformTest, MissingBindingPointToIndexMapping) { | |
| auto* src = R"( | |
| [[block]] | |
| struct SB1 { | |
| x : i32; | |
| arr1 : array<i32>; | |
| }; | |
| [[block]] | |
| struct SB2 { | |
| x : i32; | |
| arr2 : array<vec4<f32>>; | |
| }; | |
| [[group(0), binding(2)]] var<storage, read> sb1 : SB1; | |
| [[group(1), binding(2)]] var<storage, read> sb2 : SB2; | |
| [[stage(compute), workgroup_size(1)]] | |
| fn main() { | |
| var len1 : u32 = arrayLength(&(sb1.arr1)); | |
| var len2 : u32 = arrayLength(&(sb2.arr2)); | |
| var x : u32 = (len1 + len2); | |
| } | |
| )"; | |
| auto* expect = R"( | |
| [[block]] | |
| struct tint_symbol { | |
| buffer_size : array<vec4<u32>, 1u>; | |
| }; | |
| [[group(0), binding(30)]] var<uniform> tint_symbol_1 : tint_symbol; | |
| [[block]] | |
| struct SB1 { | |
| x : i32; | |
| arr1 : array<i32>; | |
| }; | |
| [[block]] | |
| struct SB2 { | |
| x : i32; | |
| arr2 : array<vec4<f32>>; | |
| }; | |
| [[group(0), binding(2)]] var<storage, read> sb1 : SB1; | |
| [[group(1), binding(2)]] var<storage, read> sb2 : SB2; | |
| [[stage(compute), workgroup_size(1)]] | |
| fn main() { | |
| var len1 : u32 = ((tint_symbol_1.buffer_size[0u][0u] - 4u) / 4u); | |
| var len2 : u32 = arrayLength(&(sb2.arr2)); | |
| var x : u32 = (len1 + len2); | |
| } | |
| )"; | |
| ArrayLengthFromUniform::Config cfg({0, 30u}); | |
| cfg.bindpoint_to_size_index.emplace(sem::BindingPoint{0, 2}, 0); | |
| DataMap data; | |
| data.Add<ArrayLengthFromUniform::Config>(std::move(cfg)); | |
| auto got = Run<Unshadow, SimplifyPointers, ArrayLengthFromUniform>(src, data); | |
| EXPECT_EQ(expect, str(got)); | |
| EXPECT_EQ(std::unordered_set<uint32_t>({0}), | |
| got.data.Get<ArrayLengthFromUniform::Result>()->used_size_indices); | |
| } | |
| } // namespace | |
| } // namespace transform | |
| } // namespace tint |