|  | // Copyright 2020 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/first_index_offset.h" | 
|  |  | 
|  | #include <memory> | 
|  | #include <utility> | 
|  | #include <vector> | 
|  |  | 
|  | #include "src/transform/test_helper.h" | 
|  |  | 
|  | namespace tint { | 
|  | namespace transform { | 
|  | namespace { | 
|  |  | 
|  | using FirstIndexOffsetTest = TransformTest; | 
|  |  | 
|  | TEST_F(FirstIndexOffsetTest, EmptyModule) { | 
|  | auto* src = ""; | 
|  | auto* expect = ""; | 
|  |  | 
|  | DataMap config; | 
|  | config.Add<FirstIndexOffset::BindingPoint>(0, 0); | 
|  | auto got = Run<FirstIndexOffset>(src, std::move(config)); | 
|  |  | 
|  | EXPECT_EQ(expect, str(got)); | 
|  |  | 
|  | auto* data = got.data.Get<FirstIndexOffset::Data>(); | 
|  |  | 
|  | ASSERT_NE(data, nullptr); | 
|  | EXPECT_EQ(data->has_vertex_index, false); | 
|  | EXPECT_EQ(data->has_instance_index, false); | 
|  | EXPECT_EQ(data->first_vertex_offset, 0u); | 
|  | EXPECT_EQ(data->first_instance_offset, 0u); | 
|  | } | 
|  |  | 
|  | TEST_F(FirstIndexOffsetTest, BasicModuleVertexIndex) { | 
|  | auto* src = R"( | 
|  | fn test(vert_idx : u32) -> u32 { | 
|  | return vert_idx; | 
|  | } | 
|  |  | 
|  | [[stage(vertex)]] | 
|  | fn entry([[builtin(vertex_index)]] vert_idx : u32) -> [[builtin(position)]] vec4<f32> { | 
|  | test(vert_idx); | 
|  | return vec4<f32>(); | 
|  | } | 
|  | )"; | 
|  |  | 
|  | auto* expect = R"( | 
|  | [[block]] | 
|  | struct tint_symbol { | 
|  | first_vertex_index : u32; | 
|  | }; | 
|  |  | 
|  | [[binding(1), group(2)]] var<uniform> tint_symbol_1 : tint_symbol; | 
|  |  | 
|  | fn test(vert_idx : u32) -> u32 { | 
|  | return vert_idx; | 
|  | } | 
|  |  | 
|  | [[stage(vertex)]] | 
|  | fn entry([[builtin(vertex_index)]] vert_idx : u32) -> [[builtin(position)]] vec4<f32> { | 
|  | test((vert_idx + tint_symbol_1.first_vertex_index)); | 
|  | return vec4<f32>(); | 
|  | } | 
|  | )"; | 
|  |  | 
|  | DataMap config; | 
|  | config.Add<FirstIndexOffset::BindingPoint>(1, 2); | 
|  | auto got = Run<FirstIndexOffset>(src, std::move(config)); | 
|  |  | 
|  | EXPECT_EQ(expect, str(got)); | 
|  |  | 
|  | auto* data = got.data.Get<FirstIndexOffset::Data>(); | 
|  |  | 
|  | ASSERT_NE(data, nullptr); | 
|  | EXPECT_EQ(data->has_vertex_index, true); | 
|  | EXPECT_EQ(data->has_instance_index, false); | 
|  | EXPECT_EQ(data->first_vertex_offset, 0u); | 
|  | EXPECT_EQ(data->first_instance_offset, 0u); | 
|  | } | 
|  |  | 
|  | TEST_F(FirstIndexOffsetTest, BasicModuleInstanceIndex) { | 
|  | auto* src = R"( | 
|  | fn test(inst_idx : u32) -> u32 { | 
|  | return inst_idx; | 
|  | } | 
|  |  | 
|  | [[stage(vertex)]] | 
|  | fn entry([[builtin(instance_index)]] inst_idx : u32) -> [[builtin(position)]] vec4<f32> { | 
|  | test(inst_idx); | 
|  | return vec4<f32>(); | 
|  | } | 
|  | )"; | 
|  |  | 
|  | auto* expect = R"( | 
|  | [[block]] | 
|  | struct tint_symbol { | 
|  | first_instance_index : u32; | 
|  | }; | 
|  |  | 
|  | [[binding(1), group(7)]] var<uniform> tint_symbol_1 : tint_symbol; | 
|  |  | 
|  | fn test(inst_idx : u32) -> u32 { | 
|  | return inst_idx; | 
|  | } | 
|  |  | 
|  | [[stage(vertex)]] | 
|  | fn entry([[builtin(instance_index)]] inst_idx : u32) -> [[builtin(position)]] vec4<f32> { | 
|  | test((inst_idx + tint_symbol_1.first_instance_index)); | 
|  | return vec4<f32>(); | 
|  | } | 
|  | )"; | 
|  |  | 
|  | DataMap config; | 
|  | config.Add<FirstIndexOffset::BindingPoint>(1, 7); | 
|  | auto got = Run<FirstIndexOffset>(src, std::move(config)); | 
|  |  | 
|  | EXPECT_EQ(expect, str(got)); | 
|  |  | 
|  | auto* data = got.data.Get<FirstIndexOffset::Data>(); | 
|  |  | 
|  | ASSERT_NE(data, nullptr); | 
|  | EXPECT_EQ(data->has_vertex_index, false); | 
|  | EXPECT_EQ(data->has_instance_index, true); | 
|  | EXPECT_EQ(data->first_vertex_offset, 0u); | 
|  | EXPECT_EQ(data->first_instance_offset, 0u); | 
|  | } | 
|  |  | 
|  | TEST_F(FirstIndexOffsetTest, BasicModuleBothIndex) { | 
|  | auto* src = R"( | 
|  | fn test(instance_idx : u32, vert_idx : u32) -> u32 { | 
|  | return instance_idx + vert_idx; | 
|  | } | 
|  |  | 
|  | struct Inputs { | 
|  | [[builtin(instance_index)]] instance_idx : u32; | 
|  | [[builtin(vertex_index)]] vert_idx : u32; | 
|  | }; | 
|  |  | 
|  | [[stage(vertex)]] | 
|  | fn entry(inputs : Inputs) -> [[builtin(position)]] vec4<f32> { | 
|  | test(inputs.instance_idx, inputs.vert_idx); | 
|  | return vec4<f32>(); | 
|  | } | 
|  | )"; | 
|  |  | 
|  | auto* expect = R"( | 
|  | [[block]] | 
|  | struct tint_symbol { | 
|  | first_vertex_index : u32; | 
|  | first_instance_index : u32; | 
|  | }; | 
|  |  | 
|  | [[binding(1), group(2)]] var<uniform> tint_symbol_1 : tint_symbol; | 
|  |  | 
|  | fn test(instance_idx : u32, vert_idx : u32) -> u32 { | 
|  | return (instance_idx + vert_idx); | 
|  | } | 
|  |  | 
|  | struct Inputs { | 
|  | [[builtin(instance_index)]] | 
|  | instance_idx : u32; | 
|  | [[builtin(vertex_index)]] | 
|  | vert_idx : u32; | 
|  | }; | 
|  |  | 
|  | [[stage(vertex)]] | 
|  | fn entry(inputs : Inputs) -> [[builtin(position)]] vec4<f32> { | 
|  | test((inputs.instance_idx + tint_symbol_1.first_instance_index), (inputs.vert_idx + tint_symbol_1.first_vertex_index)); | 
|  | return vec4<f32>(); | 
|  | } | 
|  | )"; | 
|  |  | 
|  | DataMap config; | 
|  | config.Add<FirstIndexOffset::BindingPoint>(1, 2); | 
|  | auto got = Run<FirstIndexOffset>(src, std::move(config)); | 
|  |  | 
|  | EXPECT_EQ(expect, str(got)); | 
|  |  | 
|  | auto* data = got.data.Get<FirstIndexOffset::Data>(); | 
|  |  | 
|  | ASSERT_NE(data, nullptr); | 
|  | EXPECT_EQ(data->has_vertex_index, true); | 
|  | EXPECT_EQ(data->has_instance_index, true); | 
|  | EXPECT_EQ(data->first_vertex_offset, 0u); | 
|  | EXPECT_EQ(data->first_instance_offset, 4u); | 
|  | } | 
|  |  | 
|  | TEST_F(FirstIndexOffsetTest, NestedCalls) { | 
|  | auto* src = R"( | 
|  | fn func1(vert_idx : u32) -> u32 { | 
|  | return vert_idx; | 
|  | } | 
|  |  | 
|  | fn func2(vert_idx : u32) -> u32 { | 
|  | return func1(vert_idx); | 
|  | } | 
|  |  | 
|  | [[stage(vertex)]] | 
|  | fn entry([[builtin(vertex_index)]] vert_idx : u32) -> [[builtin(position)]] vec4<f32> { | 
|  | func2(vert_idx); | 
|  | return vec4<f32>(); | 
|  | } | 
|  | )"; | 
|  |  | 
|  | auto* expect = R"( | 
|  | [[block]] | 
|  | struct tint_symbol { | 
|  | first_vertex_index : u32; | 
|  | }; | 
|  |  | 
|  | [[binding(1), group(2)]] var<uniform> tint_symbol_1 : tint_symbol; | 
|  |  | 
|  | fn func1(vert_idx : u32) -> u32 { | 
|  | return vert_idx; | 
|  | } | 
|  |  | 
|  | fn func2(vert_idx : u32) -> u32 { | 
|  | return func1(vert_idx); | 
|  | } | 
|  |  | 
|  | [[stage(vertex)]] | 
|  | fn entry([[builtin(vertex_index)]] vert_idx : u32) -> [[builtin(position)]] vec4<f32> { | 
|  | func2((vert_idx + tint_symbol_1.first_vertex_index)); | 
|  | return vec4<f32>(); | 
|  | } | 
|  | )"; | 
|  |  | 
|  | DataMap config; | 
|  | config.Add<FirstIndexOffset::BindingPoint>(1, 2); | 
|  | auto got = Run<FirstIndexOffset>(src, std::move(config)); | 
|  |  | 
|  | EXPECT_EQ(expect, str(got)); | 
|  |  | 
|  | auto* data = got.data.Get<FirstIndexOffset::Data>(); | 
|  |  | 
|  | ASSERT_NE(data, nullptr); | 
|  | EXPECT_EQ(data->has_vertex_index, true); | 
|  | EXPECT_EQ(data->has_instance_index, false); | 
|  | EXPECT_EQ(data->first_vertex_offset, 0u); | 
|  | EXPECT_EQ(data->first_instance_offset, 0u); | 
|  | } | 
|  |  | 
|  | TEST_F(FirstIndexOffsetTest, MultipleEntryPoints) { | 
|  | auto* src = R"( | 
|  | fn func(i : u32) -> u32 { | 
|  | return i; | 
|  | } | 
|  |  | 
|  | [[stage(vertex)]] | 
|  | fn entry_a([[builtin(vertex_index)]] vert_idx : u32) -> [[builtin(position)]] vec4<f32> { | 
|  | func(vert_idx); | 
|  | return vec4<f32>(); | 
|  | } | 
|  |  | 
|  | [[stage(vertex)]] | 
|  | fn entry_b([[builtin(vertex_index)]] vert_idx : u32, [[builtin(instance_index)]] inst_idx : u32) -> [[builtin(position)]] vec4<f32> { | 
|  | func(vert_idx + inst_idx); | 
|  | return vec4<f32>(); | 
|  | } | 
|  |  | 
|  | [[stage(vertex)]] | 
|  | fn entry_c([[builtin(instance_index)]] inst_idx : u32) -> [[builtin(position)]] vec4<f32> { | 
|  | func(inst_idx); | 
|  | return vec4<f32>(); | 
|  | } | 
|  | )"; | 
|  |  | 
|  | auto* expect = R"( | 
|  | [[block]] | 
|  | struct tint_symbol { | 
|  | first_vertex_index : u32; | 
|  | first_instance_index : u32; | 
|  | }; | 
|  |  | 
|  | [[binding(1), group(2)]] var<uniform> tint_symbol_1 : tint_symbol; | 
|  |  | 
|  | fn func(i : u32) -> u32 { | 
|  | return i; | 
|  | } | 
|  |  | 
|  | [[stage(vertex)]] | 
|  | fn entry_a([[builtin(vertex_index)]] vert_idx : u32) -> [[builtin(position)]] vec4<f32> { | 
|  | func((vert_idx + tint_symbol_1.first_vertex_index)); | 
|  | return vec4<f32>(); | 
|  | } | 
|  |  | 
|  | [[stage(vertex)]] | 
|  | fn entry_b([[builtin(vertex_index)]] vert_idx : u32, [[builtin(instance_index)]] inst_idx : u32) -> [[builtin(position)]] vec4<f32> { | 
|  | func(((vert_idx + tint_symbol_1.first_vertex_index) + (inst_idx + tint_symbol_1.first_instance_index))); | 
|  | return vec4<f32>(); | 
|  | } | 
|  |  | 
|  | [[stage(vertex)]] | 
|  | fn entry_c([[builtin(instance_index)]] inst_idx : u32) -> [[builtin(position)]] vec4<f32> { | 
|  | func((inst_idx + tint_symbol_1.first_instance_index)); | 
|  | return vec4<f32>(); | 
|  | } | 
|  | )"; | 
|  |  | 
|  | DataMap config; | 
|  | config.Add<FirstIndexOffset::BindingPoint>(1, 2); | 
|  | auto got = Run<FirstIndexOffset>(src, std::move(config)); | 
|  |  | 
|  | EXPECT_EQ(expect, str(got)); | 
|  |  | 
|  | auto* data = got.data.Get<FirstIndexOffset::Data>(); | 
|  |  | 
|  | ASSERT_NE(data, nullptr); | 
|  | EXPECT_EQ(data->has_vertex_index, true); | 
|  | EXPECT_EQ(data->has_instance_index, true); | 
|  | EXPECT_EQ(data->first_vertex_offset, 0u); | 
|  | EXPECT_EQ(data->first_instance_offset, 4u); | 
|  | } | 
|  |  | 
|  | TEST_F(FirstIndexOffsetTest, OLD_BasicModuleVertexIndex) { | 
|  | auto* src = R"( | 
|  | [[builtin(vertex_index)]] var<in> vert_idx : u32; | 
|  |  | 
|  | [[builtin(position)]] var<out> pos : vec4<f32>; | 
|  |  | 
|  | fn test() -> u32 { | 
|  | return vert_idx; | 
|  | } | 
|  |  | 
|  | [[stage(vertex)]] | 
|  | fn entry() { | 
|  | test(); | 
|  | pos = vec4<f32>(); | 
|  | } | 
|  | )"; | 
|  |  | 
|  | auto* expect = R"( | 
|  | [[block]] | 
|  | struct tint_symbol { | 
|  | first_vertex_index : u32; | 
|  | }; | 
|  |  | 
|  | [[binding(1), group(2)]] var<uniform> tint_symbol_1 : tint_symbol; | 
|  |  | 
|  | [[builtin(vertex_index)]] var<in> vert_idx : u32; | 
|  |  | 
|  | [[builtin(position)]] var<out> pos : vec4<f32>; | 
|  |  | 
|  | fn test() -> u32 { | 
|  | return (vert_idx + tint_symbol_1.first_vertex_index); | 
|  | } | 
|  |  | 
|  | [[stage(vertex)]] | 
|  | fn entry() { | 
|  | test(); | 
|  | pos = vec4<f32>(); | 
|  | } | 
|  | )"; | 
|  |  | 
|  | DataMap config; | 
|  | config.Add<FirstIndexOffset::BindingPoint>(1, 2); | 
|  | auto got = Run<FirstIndexOffset>(src, std::move(config)); | 
|  |  | 
|  | EXPECT_EQ(expect, str(got)); | 
|  |  | 
|  | auto* data = got.data.Get<FirstIndexOffset::Data>(); | 
|  |  | 
|  | ASSERT_NE(data, nullptr); | 
|  | EXPECT_EQ(data->has_vertex_index, true); | 
|  | EXPECT_EQ(data->has_instance_index, false); | 
|  | EXPECT_EQ(data->first_vertex_offset, 0u); | 
|  | EXPECT_EQ(data->first_instance_offset, 0u); | 
|  | } | 
|  |  | 
|  | TEST_F(FirstIndexOffsetTest, OLD_BasicModuleInstanceIndex) { | 
|  | auto* src = R"( | 
|  | [[builtin(instance_index)]] var<in> inst_idx : u32; | 
|  |  | 
|  | [[builtin(position)]] var<out> pos : vec4<f32>; | 
|  |  | 
|  | fn test() -> u32 { | 
|  | return inst_idx; | 
|  | } | 
|  |  | 
|  | [[stage(vertex)]] | 
|  | fn entry() { | 
|  | test(); | 
|  | pos = vec4<f32>(); | 
|  | } | 
|  | )"; | 
|  |  | 
|  | auto* expect = R"( | 
|  | [[block]] | 
|  | struct tint_symbol { | 
|  | first_instance_index : u32; | 
|  | }; | 
|  |  | 
|  | [[binding(1), group(7)]] var<uniform> tint_symbol_1 : tint_symbol; | 
|  |  | 
|  | [[builtin(instance_index)]] var<in> inst_idx : u32; | 
|  |  | 
|  | [[builtin(position)]] var<out> pos : vec4<f32>; | 
|  |  | 
|  | fn test() -> u32 { | 
|  | return (inst_idx + tint_symbol_1.first_instance_index); | 
|  | } | 
|  |  | 
|  | [[stage(vertex)]] | 
|  | fn entry() { | 
|  | test(); | 
|  | pos = vec4<f32>(); | 
|  | } | 
|  | )"; | 
|  |  | 
|  | DataMap config; | 
|  | config.Add<FirstIndexOffset::BindingPoint>(1, 7); | 
|  | auto got = Run<FirstIndexOffset>(src, std::move(config)); | 
|  |  | 
|  | EXPECT_EQ(expect, str(got)); | 
|  |  | 
|  | auto* data = got.data.Get<FirstIndexOffset::Data>(); | 
|  |  | 
|  | ASSERT_NE(data, nullptr); | 
|  | EXPECT_EQ(data->has_vertex_index, false); | 
|  | EXPECT_EQ(data->has_instance_index, true); | 
|  | EXPECT_EQ(data->first_vertex_offset, 0u); | 
|  | EXPECT_EQ(data->first_instance_offset, 0u); | 
|  | } | 
|  |  | 
|  | TEST_F(FirstIndexOffsetTest, OLD_BasicModuleBothIndex) { | 
|  | auto* src = R"( | 
|  | [[builtin(instance_index)]] var<in> instance_idx : u32; | 
|  | [[builtin(vertex_index)]] var<in> vert_idx : u32; | 
|  | [[builtin(position)]] var<out> pos : vec4<f32>; | 
|  |  | 
|  | fn test() -> u32 { | 
|  | return instance_idx + vert_idx; | 
|  | } | 
|  |  | 
|  | [[stage(vertex)]] | 
|  | fn entry() { | 
|  | test(); | 
|  | pos = vec4<f32>(); | 
|  | } | 
|  | )"; | 
|  |  | 
|  | auto* expect = R"( | 
|  | [[block]] | 
|  | struct tint_symbol { | 
|  | first_vertex_index : u32; | 
|  | first_instance_index : u32; | 
|  | }; | 
|  |  | 
|  | [[binding(1), group(2)]] var<uniform> tint_symbol_1 : tint_symbol; | 
|  |  | 
|  | [[builtin(instance_index)]] var<in> instance_idx : u32; | 
|  |  | 
|  | [[builtin(vertex_index)]] var<in> vert_idx : u32; | 
|  |  | 
|  | [[builtin(position)]] var<out> pos : vec4<f32>; | 
|  |  | 
|  | fn test() -> u32 { | 
|  | return ((instance_idx + tint_symbol_1.first_instance_index) + (vert_idx + tint_symbol_1.first_vertex_index)); | 
|  | } | 
|  |  | 
|  | [[stage(vertex)]] | 
|  | fn entry() { | 
|  | test(); | 
|  | pos = vec4<f32>(); | 
|  | } | 
|  | )"; | 
|  |  | 
|  | DataMap config; | 
|  | config.Add<FirstIndexOffset::BindingPoint>(1, 2); | 
|  | auto got = Run<FirstIndexOffset>(src, std::move(config)); | 
|  |  | 
|  | EXPECT_EQ(expect, str(got)); | 
|  |  | 
|  | auto* data = got.data.Get<FirstIndexOffset::Data>(); | 
|  |  | 
|  | ASSERT_NE(data, nullptr); | 
|  | EXPECT_EQ(data->has_vertex_index, true); | 
|  | EXPECT_EQ(data->has_instance_index, true); | 
|  | EXPECT_EQ(data->first_vertex_offset, 0u); | 
|  | EXPECT_EQ(data->first_instance_offset, 4u); | 
|  | } | 
|  |  | 
|  | TEST_F(FirstIndexOffsetTest, OLD_NestedCalls) { | 
|  | auto* src = R"( | 
|  | [[builtin(vertex_index)]] var<in> vert_idx : u32; | 
|  | [[builtin(position)]] var<out> pos : vec4<f32>; | 
|  |  | 
|  | fn func1() -> u32 { | 
|  | return vert_idx; | 
|  | } | 
|  |  | 
|  | fn func2() -> u32 { | 
|  | return func1(); | 
|  | } | 
|  |  | 
|  | [[stage(vertex)]] | 
|  | fn entry() { | 
|  | func2(); | 
|  | pos = vec4<f32>(); | 
|  | } | 
|  | )"; | 
|  |  | 
|  | auto* expect = R"( | 
|  | [[block]] | 
|  | struct tint_symbol { | 
|  | first_vertex_index : u32; | 
|  | }; | 
|  |  | 
|  | [[binding(1), group(2)]] var<uniform> tint_symbol_1 : tint_symbol; | 
|  |  | 
|  | [[builtin(vertex_index)]] var<in> vert_idx : u32; | 
|  |  | 
|  | [[builtin(position)]] var<out> pos : vec4<f32>; | 
|  |  | 
|  | fn func1() -> u32 { | 
|  | return (vert_idx + tint_symbol_1.first_vertex_index); | 
|  | } | 
|  |  | 
|  | fn func2() -> u32 { | 
|  | return func1(); | 
|  | } | 
|  |  | 
|  | [[stage(vertex)]] | 
|  | fn entry() { | 
|  | func2(); | 
|  | pos = vec4<f32>(); | 
|  | } | 
|  | )"; | 
|  |  | 
|  | DataMap config; | 
|  | config.Add<FirstIndexOffset::BindingPoint>(1, 2); | 
|  | auto got = Run<FirstIndexOffset>(src, std::move(config)); | 
|  |  | 
|  | EXPECT_EQ(expect, str(got)); | 
|  |  | 
|  | auto* data = got.data.Get<FirstIndexOffset::Data>(); | 
|  |  | 
|  | ASSERT_NE(data, nullptr); | 
|  | EXPECT_EQ(data->has_vertex_index, true); | 
|  | EXPECT_EQ(data->has_instance_index, false); | 
|  | EXPECT_EQ(data->first_vertex_offset, 0u); | 
|  | EXPECT_EQ(data->first_instance_offset, 0u); | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  | }  // namespace transform | 
|  | }  // namespace tint |