| // 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 |