// 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/vertex_pulling.h" #include #include "src/transform/test_helper.h" namespace tint { namespace transform { namespace { using VertexPullingTest = TransformTest; TEST_F(VertexPullingTest, Error_NoEntryPoint) { auto* src = ""; auto* expect = "error: Vertex stage entry point not found"; DataMap data; data.Add(); auto got = Run(src, data); EXPECT_EQ(expect, str(got)); } TEST_F(VertexPullingTest, Error_InvalidEntryPoint) { auto* src = R"( [[stage(vertex)]] fn main() -> [[builtin(position)]] vec4 { return vec4(); } )"; auto* expect = "error: Vertex stage entry point not found"; VertexPulling::Config cfg; cfg.entry_point_name = "_"; DataMap data; data.Add(cfg); auto got = Run(src, data); EXPECT_EQ(expect, str(got)); } TEST_F(VertexPullingTest, Error_EntryPointWrongStage) { auto* src = R"( [[stage(fragment)]] fn main() {} )"; auto* expect = "error: Vertex stage entry point not found"; VertexPulling::Config cfg; cfg.entry_point_name = "main"; DataMap data; data.Add(cfg); auto got = Run(src, data); EXPECT_EQ(expect, str(got)); } TEST_F(VertexPullingTest, BasicModule) { auto* src = R"( [[stage(vertex)]] fn main() -> [[builtin(position)]] vec4 { return vec4(); } )"; auto* expect = R"( [[block]] struct TintVertexData { tint_vertex_data : [[stride(4)]] array; }; [[stage(vertex)]] fn main() -> [[builtin(position)]] vec4 { { var tint_pulling_pos : u32; } return vec4(); } )"; VertexPulling::Config cfg; cfg.entry_point_name = "main"; DataMap data; data.Add(cfg); auto got = Run(src, data); EXPECT_EQ(expect, str(got)); } TEST_F(VertexPullingTest, OneAttribute) { auto* src = R"( [[stage(vertex)]] fn main([[location(0)]] var_a : f32) -> [[builtin(position)]] vec4 { return vec4(var_a, 0.0, 0.0, 1.0); } )"; auto* expect = R"( [[block]] struct TintVertexData { tint_vertex_data : [[stride(4)]] array; }; [[binding(0), group(4)]] var tint_pulling_vertex_buffer_0 : [[access(read)]] TintVertexData; [[stage(vertex)]] fn main([[builtin(vertex_index)]] tint_pulling_vertex_index : u32) -> [[builtin(position)]] vec4 { var var_a : f32; { var tint_pulling_pos : u32; tint_pulling_pos = ((tint_pulling_vertex_index * 4u) + 0u); var_a = bitcast(tint_pulling_vertex_buffer_0.tint_vertex_data[(tint_pulling_pos / 4u)]); } return vec4(var_a, 0.0, 0.0, 1.0); } )"; VertexPulling::Config cfg; cfg.vertex_state = { {{4, InputStepMode::kVertex, {{VertexFormat::kF32, 0, 0}}}}}; cfg.entry_point_name = "main"; DataMap data; data.Add(cfg); auto got = Run(src, data); EXPECT_EQ(expect, str(got)); } TEST_F(VertexPullingTest, OneInstancedAttribute) { auto* src = R"( [[stage(vertex)]] fn main([[location(0)]] var_a : f32) -> [[builtin(position)]] vec4 { return vec4(var_a, 0.0, 0.0, 1.0); } )"; auto* expect = R"( [[block]] struct TintVertexData { tint_vertex_data : [[stride(4)]] array; }; [[binding(0), group(4)]] var tint_pulling_vertex_buffer_0 : [[access(read)]] TintVertexData; [[stage(vertex)]] fn main([[builtin(instance_index)]] tint_pulling_instance_index : u32) -> [[builtin(position)]] vec4 { var var_a : f32; { var tint_pulling_pos : u32; tint_pulling_pos = ((tint_pulling_instance_index * 4u) + 0u); var_a = bitcast(tint_pulling_vertex_buffer_0.tint_vertex_data[(tint_pulling_pos / 4u)]); } return vec4(var_a, 0.0, 0.0, 1.0); } )"; VertexPulling::Config cfg; cfg.vertex_state = { {{4, InputStepMode::kInstance, {{VertexFormat::kF32, 0, 0}}}}}; cfg.entry_point_name = "main"; DataMap data; data.Add(cfg); auto got = Run(src, data); EXPECT_EQ(expect, str(got)); } TEST_F(VertexPullingTest, OneAttributeDifferentOutputSet) { auto* src = R"( [[stage(vertex)]] fn main([[location(0)]] var_a : f32) -> [[builtin(position)]] vec4 { return vec4(var_a, 0.0, 0.0, 1.0); } )"; auto* expect = R"( [[block]] struct TintVertexData { tint_vertex_data : [[stride(4)]] array; }; [[binding(0), group(5)]] var tint_pulling_vertex_buffer_0 : [[access(read)]] TintVertexData; [[stage(vertex)]] fn main([[builtin(vertex_index)]] tint_pulling_vertex_index : u32) -> [[builtin(position)]] vec4 { var var_a : f32; { var tint_pulling_pos : u32; tint_pulling_pos = ((tint_pulling_vertex_index * 4u) + 0u); var_a = bitcast(tint_pulling_vertex_buffer_0.tint_vertex_data[(tint_pulling_pos / 4u)]); } return vec4(var_a, 0.0, 0.0, 1.0); } )"; VertexPulling::Config cfg; cfg.vertex_state = { {{4, InputStepMode::kVertex, {{VertexFormat::kF32, 0, 0}}}}}; cfg.pulling_group = 5; cfg.entry_point_name = "main"; DataMap data; data.Add(cfg); auto got = Run(src, data); EXPECT_EQ(expect, str(got)); } TEST_F(VertexPullingTest, OneAttribute_Struct) { auto* src = R"( struct Inputs { [[location(0)]] var_a : f32; }; [[stage(vertex)]] fn main(inputs : Inputs) -> [[builtin(position)]] vec4 { return vec4(inputs.var_a, 0.0, 0.0, 1.0); } )"; auto* expect = R"( [[block]] struct TintVertexData { tint_vertex_data : [[stride(4)]] array; }; [[binding(0), group(4)]] var tint_pulling_vertex_buffer_0 : [[access(read)]] TintVertexData; struct Inputs { [[location(0)]] var_a : f32; }; [[stage(vertex)]] fn main([[builtin(vertex_index)]] tint_pulling_vertex_index : u32) -> [[builtin(position)]] vec4 { var inputs : Inputs; { var tint_pulling_pos : u32; tint_pulling_pos = ((tint_pulling_vertex_index * 4u) + 0u); inputs.var_a = bitcast(tint_pulling_vertex_buffer_0.tint_vertex_data[(tint_pulling_pos / 4u)]); } return vec4(inputs.var_a, 0.0, 0.0, 1.0); } )"; VertexPulling::Config cfg; cfg.vertex_state = { {{4, InputStepMode::kVertex, {{VertexFormat::kF32, 0, 0}}}}}; cfg.entry_point_name = "main"; DataMap data; data.Add(cfg); auto got = Run(src, data); EXPECT_EQ(expect, str(got)); } // We expect the transform to use an existing builtin variables if it finds them TEST_F(VertexPullingTest, ExistingVertexIndexAndInstanceIndex) { auto* src = R"( [[stage(vertex)]] fn main([[location(0)]] var_a : f32, [[location(1)]] var_b : f32, [[builtin(vertex_index)]] custom_vertex_index : u32, [[builtin(instance_index)]] custom_instance_index : u32 ) -> [[builtin(position)]] vec4 { return vec4(var_a, var_b, 0.0, 1.0); } )"; auto* expect = R"( [[block]] struct TintVertexData { tint_vertex_data : [[stride(4)]] array; }; [[binding(0), group(4)]] var tint_pulling_vertex_buffer_0 : [[access(read)]] TintVertexData; [[binding(1), group(4)]] var tint_pulling_vertex_buffer_1 : [[access(read)]] TintVertexData; [[stage(vertex)]] fn main([[builtin(vertex_index)]] custom_vertex_index : u32, [[builtin(instance_index)]] custom_instance_index : u32) -> [[builtin(position)]] vec4 { var var_a : f32; var var_b : f32; { var tint_pulling_pos : u32; tint_pulling_pos = ((custom_vertex_index * 4u) + 0u); var_a = bitcast(tint_pulling_vertex_buffer_0.tint_vertex_data[(tint_pulling_pos / 4u)]); tint_pulling_pos = ((custom_instance_index * 4u) + 0u); var_b = bitcast(tint_pulling_vertex_buffer_1.tint_vertex_data[(tint_pulling_pos / 4u)]); } return vec4(var_a, var_b, 0.0, 1.0); } )"; VertexPulling::Config cfg; cfg.vertex_state = {{ { 4, InputStepMode::kVertex, {{VertexFormat::kF32, 0, 0}}, }, { 4, InputStepMode::kInstance, {{VertexFormat::kF32, 0, 1}}, }, }}; cfg.entry_point_name = "main"; DataMap data; data.Add(cfg); auto got = Run(src, data); EXPECT_EQ(expect, str(got)); } TEST_F(VertexPullingTest, ExistingVertexIndexAndInstanceIndex_Struct) { auto* src = R"( struct Inputs { [[location(0)]] var_a : f32; [[location(1)]] var_b : f32; [[builtin(vertex_index)]] custom_vertex_index : u32; [[builtin(instance_index)]] custom_instance_index : u32; }; [[stage(vertex)]] fn main(inputs : Inputs) -> [[builtin(position)]] vec4 { return vec4(inputs.var_a, inputs.var_b, 0.0, 1.0); } )"; auto* expect = R"( [[block]] struct TintVertexData { tint_vertex_data : [[stride(4)]] array; }; [[binding(0), group(4)]] var tint_pulling_vertex_buffer_0 : [[access(read)]] TintVertexData; [[binding(1), group(4)]] var tint_pulling_vertex_buffer_1 : [[access(read)]] TintVertexData; struct tint_symbol { [[builtin(vertex_index)]] custom_vertex_index : u32; [[builtin(instance_index)]] custom_instance_index : u32; }; struct Inputs { [[location(0)]] var_a : f32; [[location(1)]] var_b : f32; [[builtin(vertex_index)]] custom_vertex_index : u32; [[builtin(instance_index)]] custom_instance_index : u32; }; [[stage(vertex)]] fn main(tint_symbol_1 : tint_symbol) -> [[builtin(position)]] vec4 { var inputs : Inputs; inputs.custom_vertex_index = tint_symbol_1.custom_vertex_index; inputs.custom_instance_index = tint_symbol_1.custom_instance_index; { var tint_pulling_pos : u32; tint_pulling_pos = ((inputs.custom_vertex_index * 4u) + 0u); inputs.var_a = bitcast(tint_pulling_vertex_buffer_0.tint_vertex_data[(tint_pulling_pos / 4u)]); tint_pulling_pos = ((inputs.custom_instance_index * 4u) + 0u); inputs.var_b = bitcast(tint_pulling_vertex_buffer_1.tint_vertex_data[(tint_pulling_pos / 4u)]); } return vec4(inputs.var_a, inputs.var_b, 0.0, 1.0); } )"; VertexPulling::Config cfg; cfg.vertex_state = {{ { 4, InputStepMode::kVertex, {{VertexFormat::kF32, 0, 0}}, }, { 4, InputStepMode::kInstance, {{VertexFormat::kF32, 0, 1}}, }, }}; cfg.entry_point_name = "main"; DataMap data; data.Add(cfg); auto got = Run(src, data); EXPECT_EQ(expect, str(got)); } TEST_F(VertexPullingTest, ExistingVertexIndexAndInstanceIndex_SeparateStruct) { auto* src = R"( struct Inputs { [[location(0)]] var_a : f32; [[location(1)]] var_b : f32; }; struct Indices { [[builtin(vertex_index)]] custom_vertex_index : u32; [[builtin(instance_index)]] custom_instance_index : u32; }; [[stage(vertex)]] fn main(inputs : Inputs, indices : Indices) -> [[builtin(position)]] vec4 { return vec4(inputs.var_a, inputs.var_b, 0.0, 1.0); } )"; auto* expect = R"( [[block]] struct TintVertexData { tint_vertex_data : [[stride(4)]] array; }; [[binding(0), group(4)]] var tint_pulling_vertex_buffer_0 : [[access(read)]] TintVertexData; [[binding(1), group(4)]] var tint_pulling_vertex_buffer_1 : [[access(read)]] TintVertexData; struct Inputs { [[location(0)]] var_a : f32; [[location(1)]] var_b : f32; }; struct Indices { [[builtin(vertex_index)]] custom_vertex_index : u32; [[builtin(instance_index)]] custom_instance_index : u32; }; [[stage(vertex)]] fn main(indices : Indices) -> [[builtin(position)]] vec4 { var inputs : Inputs; { var tint_pulling_pos : u32; tint_pulling_pos = ((indices.custom_vertex_index * 4u) + 0u); inputs.var_a = bitcast(tint_pulling_vertex_buffer_0.tint_vertex_data[(tint_pulling_pos / 4u)]); tint_pulling_pos = ((indices.custom_instance_index * 4u) + 0u); inputs.var_b = bitcast(tint_pulling_vertex_buffer_1.tint_vertex_data[(tint_pulling_pos / 4u)]); } return vec4(inputs.var_a, inputs.var_b, 0.0, 1.0); } )"; VertexPulling::Config cfg; cfg.vertex_state = {{ { 4, InputStepMode::kVertex, {{VertexFormat::kF32, 0, 0}}, }, { 4, InputStepMode::kInstance, {{VertexFormat::kF32, 0, 1}}, }, }}; cfg.entry_point_name = "main"; DataMap data; data.Add(cfg); auto got = Run(src, data); EXPECT_EQ(expect, str(got)); } TEST_F(VertexPullingTest, TwoAttributesSameBuffer) { auto* src = R"( [[stage(vertex)]] fn main([[location(0)]] var_a : f32, [[location(1)]] var_b : vec4) -> [[builtin(position)]] vec4 { return vec4(); } )"; auto* expect = R"( [[block]] struct TintVertexData { tint_vertex_data : [[stride(4)]] array; }; [[binding(0), group(4)]] var tint_pulling_vertex_buffer_0 : [[access(read)]] TintVertexData; [[stage(vertex)]] fn main([[builtin(vertex_index)]] tint_pulling_vertex_index : u32) -> [[builtin(position)]] vec4 { var var_a : f32; var var_b : vec4; { var tint_pulling_pos : u32; tint_pulling_pos = ((tint_pulling_vertex_index * 16u) + 0u); var_a = bitcast(tint_pulling_vertex_buffer_0.tint_vertex_data[(tint_pulling_pos / 4u)]); tint_pulling_pos = ((tint_pulling_vertex_index * 16u) + 0u); var_b = vec4(bitcast(tint_pulling_vertex_buffer_0.tint_vertex_data[((tint_pulling_pos + 0u) / 4u)]), bitcast(tint_pulling_vertex_buffer_0.tint_vertex_data[((tint_pulling_pos + 4u) / 4u)]), bitcast(tint_pulling_vertex_buffer_0.tint_vertex_data[((tint_pulling_pos + 8u) / 4u)]), bitcast(tint_pulling_vertex_buffer_0.tint_vertex_data[((tint_pulling_pos + 12u) / 4u)])); } return vec4(); } )"; VertexPulling::Config cfg; cfg.vertex_state = { {{16, InputStepMode::kVertex, {{VertexFormat::kF32, 0, 0}, {VertexFormat::kVec4F32, 0, 1}}}}}; cfg.entry_point_name = "main"; DataMap data; data.Add(cfg); auto got = Run(src, data); EXPECT_EQ(expect, str(got)); } TEST_F(VertexPullingTest, FloatVectorAttributes) { auto* src = R"( [[stage(vertex)]] fn main([[location(0)]] var_a : vec2, [[location(1)]] var_b : vec3, [[location(2)]] var_c : vec4 ) -> [[builtin(position)]] vec4 { return vec4(); } )"; auto* expect = R"( [[block]] struct TintVertexData { tint_vertex_data : [[stride(4)]] array; }; [[binding(0), group(4)]] var tint_pulling_vertex_buffer_0 : [[access(read)]] TintVertexData; [[binding(1), group(4)]] var tint_pulling_vertex_buffer_1 : [[access(read)]] TintVertexData; [[binding(2), group(4)]] var tint_pulling_vertex_buffer_2 : [[access(read)]] TintVertexData; [[stage(vertex)]] fn main([[builtin(vertex_index)]] tint_pulling_vertex_index : u32) -> [[builtin(position)]] vec4 { var var_a : vec2; var var_b : vec3; var var_c : vec4; { var tint_pulling_pos : u32; tint_pulling_pos = ((tint_pulling_vertex_index * 8u) + 0u); var_a = vec2(bitcast(tint_pulling_vertex_buffer_0.tint_vertex_data[((tint_pulling_pos + 0u) / 4u)]), bitcast(tint_pulling_vertex_buffer_0.tint_vertex_data[((tint_pulling_pos + 4u) / 4u)])); tint_pulling_pos = ((tint_pulling_vertex_index * 12u) + 0u); var_b = vec3(bitcast(tint_pulling_vertex_buffer_1.tint_vertex_data[((tint_pulling_pos + 0u) / 4u)]), bitcast(tint_pulling_vertex_buffer_1.tint_vertex_data[((tint_pulling_pos + 4u) / 4u)]), bitcast(tint_pulling_vertex_buffer_1.tint_vertex_data[((tint_pulling_pos + 8u) / 4u)])); tint_pulling_pos = ((tint_pulling_vertex_index * 16u) + 0u); var_c = vec4(bitcast(tint_pulling_vertex_buffer_2.tint_vertex_data[((tint_pulling_pos + 0u) / 4u)]), bitcast(tint_pulling_vertex_buffer_2.tint_vertex_data[((tint_pulling_pos + 4u) / 4u)]), bitcast(tint_pulling_vertex_buffer_2.tint_vertex_data[((tint_pulling_pos + 8u) / 4u)]), bitcast(tint_pulling_vertex_buffer_2.tint_vertex_data[((tint_pulling_pos + 12u) / 4u)])); } return vec4(); } )"; VertexPulling::Config cfg; cfg.vertex_state = {{ {8, InputStepMode::kVertex, {{VertexFormat::kVec2F32, 0, 0}}}, {12, InputStepMode::kVertex, {{VertexFormat::kVec3F32, 0, 1}}}, {16, InputStepMode::kVertex, {{VertexFormat::kVec4F32, 0, 2}}}, }}; cfg.entry_point_name = "main"; DataMap data; data.Add(cfg); auto got = Run(src, data); EXPECT_EQ(expect, str(got)); } TEST_F(VertexPullingTest, AttemptSymbolCollision) { auto* src = R"( [[stage(vertex)]] fn main([[location(0)]] var_a : f32, [[location(1)]] var_b : vec4) -> [[builtin(position)]] vec4 { var tint_pulling_vertex_index : i32; var tint_pulling_vertex_buffer_0 : i32; var tint_vertex_data : i32; var tint_pulling_pos : i32; return vec4(); } )"; auto* expect = R"( [[block]] struct TintVertexData { tint_vertex_data_1 : [[stride(4)]] array; }; [[binding(0), group(4)]] var tint_pulling_vertex_buffer_0_1 : [[access(read)]] TintVertexData; [[stage(vertex)]] fn main([[builtin(vertex_index)]] tint_pulling_vertex_index_1 : u32) -> [[builtin(position)]] vec4 { var var_a : f32; var var_b : vec4; { var tint_pulling_pos_1 : u32; tint_pulling_pos_1 = ((tint_pulling_vertex_index_1 * 16u) + 0u); var_a = bitcast(tint_pulling_vertex_buffer_0_1.tint_vertex_data_1[(tint_pulling_pos_1 / 4u)]); tint_pulling_pos_1 = ((tint_pulling_vertex_index_1 * 16u) + 0u); var_b = vec4(bitcast(tint_pulling_vertex_buffer_0_1.tint_vertex_data_1[((tint_pulling_pos_1 + 0u) / 4u)]), bitcast(tint_pulling_vertex_buffer_0_1.tint_vertex_data_1[((tint_pulling_pos_1 + 4u) / 4u)]), bitcast(tint_pulling_vertex_buffer_0_1.tint_vertex_data_1[((tint_pulling_pos_1 + 8u) / 4u)]), bitcast(tint_pulling_vertex_buffer_0_1.tint_vertex_data_1[((tint_pulling_pos_1 + 12u) / 4u)])); } var tint_pulling_vertex_index : i32; var tint_pulling_vertex_buffer_0 : i32; var tint_vertex_data : i32; var tint_pulling_pos : i32; return vec4(); } )"; VertexPulling::Config cfg; cfg.vertex_state = { {{16, InputStepMode::kVertex, {{VertexFormat::kF32, 0, 0}, {VertexFormat::kVec4F32, 0, 1}}}}}; cfg.entry_point_name = "main"; DataMap data; data.Add(cfg); auto got = Run(src, std::move(data)); EXPECT_EQ(expect, str(got)); } // TODO(crbug.com/tint/697): Remove this. TEST_F(VertexPullingTest, OneAttributeDifferentOutputSet_Legacy) { auto* src = R"( [[location(0)]] var var_a : f32; [[stage(vertex)]] fn main() -> [[builtin(position)]] vec4 { return vec4(); } )"; auto* expect = R"( [[builtin(vertex_index)]] var tint_pulling_vertex_index : u32; [[block]] struct TintVertexData { tint_vertex_data : [[stride(4)]] array; }; [[binding(0), group(5)]] var tint_pulling_vertex_buffer_0 : [[access(read)]] TintVertexData; var var_a : f32; [[stage(vertex)]] fn main() -> [[builtin(position)]] vec4 { { var tint_pulling_pos : u32; tint_pulling_pos = ((tint_pulling_vertex_index * 4u) + 0u); var_a = bitcast(tint_pulling_vertex_buffer_0.tint_vertex_data[(tint_pulling_pos / 4u)]); } return vec4(); } )"; VertexPulling::Config cfg; cfg.vertex_state = { {{4, InputStepMode::kVertex, {{VertexFormat::kF32, 0, 0}}}}}; cfg.pulling_group = 5; cfg.entry_point_name = "main"; DataMap data; data.Add(cfg); auto got = Run(src, data); EXPECT_EQ(expect, str(got)); } // TODO(crbug.com/tint/697): Remove this. // We expect the transform to use an existing builtin variables if it finds them TEST_F(VertexPullingTest, ExistingVertexIndexAndInstanceIndex_Legacy) { auto* src = R"( [[location(0)]] var var_a : f32; [[location(1)]] var var_b : f32; [[builtin(vertex_index)]] var custom_vertex_index : u32; [[builtin(instance_index)]] var custom_instance_index : u32; [[stage(vertex)]] fn main() -> [[builtin(position)]] vec4 { return vec4(); } )"; auto* expect = R"( [[block]] struct TintVertexData { tint_vertex_data : [[stride(4)]] array; }; [[binding(0), group(4)]] var tint_pulling_vertex_buffer_0 : [[access(read)]] TintVertexData; [[binding(1), group(4)]] var tint_pulling_vertex_buffer_1 : [[access(read)]] TintVertexData; var var_a : f32; var var_b : f32; [[builtin(vertex_index)]] var custom_vertex_index : u32; [[builtin(instance_index)]] var custom_instance_index : u32; [[stage(vertex)]] fn main() -> [[builtin(position)]] vec4 { { var tint_pulling_pos : u32; tint_pulling_pos = ((custom_vertex_index * 4u) + 0u); var_a = bitcast(tint_pulling_vertex_buffer_0.tint_vertex_data[(tint_pulling_pos / 4u)]); tint_pulling_pos = ((custom_instance_index * 4u) + 0u); var_b = bitcast(tint_pulling_vertex_buffer_1.tint_vertex_data[(tint_pulling_pos / 4u)]); } return vec4(); } )"; VertexPulling::Config cfg; cfg.vertex_state = {{ { 4, InputStepMode::kVertex, {{VertexFormat::kF32, 0, 0}}, }, { 4, InputStepMode::kInstance, {{VertexFormat::kF32, 0, 1}}, }, }}; cfg.entry_point_name = "main"; DataMap data; data.Add(cfg); auto got = Run(src, data); EXPECT_EQ(expect, str(got)); } // TODO(crbug.com/tint/697): Remove this. TEST_F(VertexPullingTest, TwoAttributesSameBuffer_Legacy) { auto* src = R"( [[location(0)]] var var_a : f32; [[location(1)]] var var_b : vec4; [[stage(vertex)]] fn main() -> [[builtin(position)]] vec4 { return vec4(); } )"; auto* expect = R"( [[builtin(vertex_index)]] var tint_pulling_vertex_index : u32; [[block]] struct TintVertexData { tint_vertex_data : [[stride(4)]] array; }; [[binding(0), group(4)]] var tint_pulling_vertex_buffer_0 : [[access(read)]] TintVertexData; var var_a : f32; var var_b : vec4; [[stage(vertex)]] fn main() -> [[builtin(position)]] vec4 { { var tint_pulling_pos : u32; tint_pulling_pos = ((tint_pulling_vertex_index * 16u) + 0u); var_a = bitcast(tint_pulling_vertex_buffer_0.tint_vertex_data[(tint_pulling_pos / 4u)]); tint_pulling_pos = ((tint_pulling_vertex_index * 16u) + 0u); var_b = vec4(bitcast(tint_pulling_vertex_buffer_0.tint_vertex_data[((tint_pulling_pos + 0u) / 4u)]), bitcast(tint_pulling_vertex_buffer_0.tint_vertex_data[((tint_pulling_pos + 4u) / 4u)]), bitcast(tint_pulling_vertex_buffer_0.tint_vertex_data[((tint_pulling_pos + 8u) / 4u)]), bitcast(tint_pulling_vertex_buffer_0.tint_vertex_data[((tint_pulling_pos + 12u) / 4u)])); } return vec4(); } )"; VertexPulling::Config cfg; cfg.vertex_state = { {{16, InputStepMode::kVertex, {{VertexFormat::kF32, 0, 0}, {VertexFormat::kVec4F32, 0, 1}}}}}; cfg.entry_point_name = "main"; DataMap data; data.Add(cfg); auto got = Run(src, data); EXPECT_EQ(expect, str(got)); } } // namespace } // namespace transform } // namespace tint