// 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/canonicalize_entry_point_io.h" #include "src/transform/test_helper.h" namespace tint { namespace transform { namespace { using CanonicalizeEntryPointIOTest = TransformTest; TEST_F(CanonicalizeEntryPointIOTest, Error_MissingTransformData) { auto* src = ""; auto* expect = "error: missing transform data for " "tint::transform::CanonicalizeEntryPointIO"; auto got = Run(src); EXPECT_EQ(expect, str(got)); } TEST_F(CanonicalizeEntryPointIOTest, Parameters_BuiltinsAsParameters) { auto* src = R"( [[stage(fragment)]] fn frag_main([[location(1)]] loc1 : f32, [[location(2)]] loc2 : vec4, [[builtin(position)]] coord : vec4) { var col : f32 = (coord.x * loc1); } )"; auto* expect = R"( struct tint_symbol_1 { [[location(1)]] loc1 : f32; [[location(2)]] loc2 : vec4; }; [[stage(fragment)]] fn frag_main([[builtin(position)]] coord : vec4, tint_symbol : tint_symbol_1) { let loc1 : f32 = tint_symbol.loc1; let loc2 : vec4 = tint_symbol.loc2; var col : f32 = (coord.x * loc1); } )"; DataMap data; data.Add( CanonicalizeEntryPointIO::BuiltinStyle::kParameter); auto got = Run(src, data); EXPECT_EQ(expect, str(got)); } TEST_F(CanonicalizeEntryPointIOTest, Parameters_BuiltinsAsStructMembers) { auto* src = R"( [[stage(fragment)]] fn frag_main([[location(1)]] loc1 : f32, [[location(2)]] loc2 : vec4, [[builtin(position)]] coord : vec4) { var col : f32 = (coord.x * loc1); } )"; auto* expect = R"( struct tint_symbol_1 { [[location(1)]] loc1 : f32; [[location(2)]] loc2 : vec4; [[builtin(position)]] coord : vec4; }; [[stage(fragment)]] fn frag_main(tint_symbol : tint_symbol_1) { let loc1 : f32 = tint_symbol.loc1; let loc2 : vec4 = tint_symbol.loc2; let coord : vec4 = tint_symbol.coord; var col : f32 = (coord.x * loc1); } )"; DataMap data; data.Add( CanonicalizeEntryPointIO::BuiltinStyle::kStructMember); auto got = Run(src, data); EXPECT_EQ(expect, str(got)); } TEST_F(CanonicalizeEntryPointIOTest, Parameter_TypeAlias) { auto* src = R"( type myf32 = f32; [[stage(fragment)]] fn frag_main([[location(1)]] loc1 : myf32) { var x : myf32 = loc1; } )"; auto* expect = R"( type myf32 = f32; struct tint_symbol_1 { [[location(1)]] loc1 : myf32; }; [[stage(fragment)]] fn frag_main(tint_symbol : tint_symbol_1) { let loc1 : myf32 = tint_symbol.loc1; var x : myf32 = loc1; } )"; DataMap data; data.Add( CanonicalizeEntryPointIO::BuiltinStyle::kParameter); auto got = Run(src, data); EXPECT_EQ(expect, str(got)); } TEST_F(CanonicalizeEntryPointIOTest, Parameters_EmptyBody_BuiltinsAsParameters) { auto* src = R"( [[stage(fragment)]] fn frag_main([[location(1)]] loc1 : f32, [[location(2)]] loc2 : vec4, [[builtin(position)]] coord : vec4) { } )"; auto* expect = R"( struct tint_symbol_1 { [[location(1)]] loc1 : f32; [[location(2)]] loc2 : vec4; }; [[stage(fragment)]] fn frag_main([[builtin(position)]] coord : vec4, tint_symbol : tint_symbol_1) { let loc1 : f32 = tint_symbol.loc1; let loc2 : vec4 = tint_symbol.loc2; } )"; DataMap data; data.Add( CanonicalizeEntryPointIO::BuiltinStyle::kParameter); auto got = Run(src, data); EXPECT_EQ(expect, str(got)); } TEST_F(CanonicalizeEntryPointIOTest, Parameters_EmptyBody_BuiltinsAsStructMembers) { auto* src = R"( [[stage(fragment)]] fn frag_main([[location(1)]] loc1 : f32, [[location(2)]] loc2 : vec4, [[builtin(position)]] coord : vec4) { } )"; auto* expect = R"( struct tint_symbol_1 { [[location(1)]] loc1 : f32; [[location(2)]] loc2 : vec4; [[builtin(position)]] coord : vec4; }; [[stage(fragment)]] fn frag_main(tint_symbol : tint_symbol_1) { let loc1 : f32 = tint_symbol.loc1; let loc2 : vec4 = tint_symbol.loc2; let coord : vec4 = tint_symbol.coord; } )"; DataMap data; data.Add( CanonicalizeEntryPointIO::BuiltinStyle::kStructMember); auto got = Run(src, data); EXPECT_EQ(expect, str(got)); } TEST_F(CanonicalizeEntryPointIOTest, StructParameters_BuiltinsAsParameters) { auto* src = R"( struct FragBuiltins { [[builtin(position)]] coord : vec4; }; struct FragLocations { [[location(1)]] loc1 : f32; [[location(2)]] loc2 : vec4; }; [[stage(fragment)]] fn frag_main([[location(0)]] loc0 : f32, locations : FragLocations, builtins : FragBuiltins) { var col : f32 = ((builtins.coord.x * locations.loc1) + loc0); } )"; auto* expect = R"( struct FragBuiltins { coord : vec4; }; struct FragLocations { loc1 : f32; loc2 : vec4; }; struct tint_symbol_2 { [[location(0)]] loc0 : f32; [[location(1)]] loc1 : f32; [[location(2)]] loc2 : vec4; }; [[stage(fragment)]] fn frag_main([[builtin(position)]] tint_symbol_1 : vec4, tint_symbol : tint_symbol_2) { let loc0 : f32 = tint_symbol.loc0; let locations : FragLocations = FragLocations(tint_symbol.loc1, tint_symbol.loc2); let builtins : FragBuiltins = FragBuiltins(tint_symbol_1); var col : f32 = ((builtins.coord.x * locations.loc1) + loc0); } )"; DataMap data; data.Add( CanonicalizeEntryPointIO::BuiltinStyle::kParameter); auto got = Run(src, data); EXPECT_EQ(expect, str(got)); } TEST_F(CanonicalizeEntryPointIOTest, StructParameters_BuiltinsAsStructMembers) { auto* src = R"( struct FragBuiltins { [[builtin(position)]] coord : vec4; }; struct FragLocations { [[location(1)]] loc1 : f32; [[location(2)]] loc2 : vec4; }; [[stage(fragment)]] fn frag_main([[location(0)]] loc0 : f32, locations : FragLocations, builtins : FragBuiltins) { var col : f32 = ((builtins.coord.x * locations.loc1) + loc0); } )"; auto* expect = R"( struct FragBuiltins { coord : vec4; }; struct FragLocations { loc1 : f32; loc2 : vec4; }; struct tint_symbol_1 { [[location(0)]] loc0 : f32; [[location(1)]] loc1 : f32; [[location(2)]] loc2 : vec4; [[builtin(position)]] coord : vec4; }; [[stage(fragment)]] fn frag_main(tint_symbol : tint_symbol_1) { let loc0 : f32 = tint_symbol.loc0; let locations : FragLocations = FragLocations(tint_symbol.loc1, tint_symbol.loc2); let builtins : FragBuiltins = FragBuiltins(tint_symbol.coord); var col : f32 = ((builtins.coord.x * locations.loc1) + loc0); } )"; DataMap data; data.Add( CanonicalizeEntryPointIO::BuiltinStyle::kStructMember); auto got = Run(src, data); EXPECT_EQ(expect, str(got)); } TEST_F(CanonicalizeEntryPointIOTest, Return_Scalar) { auto* src = R"( [[stage(fragment)]] fn frag_main() -> [[builtin(frag_depth)]] f32 { return 1.0; } )"; auto* expect = R"( struct tint_symbol { [[builtin(frag_depth)]] value : f32; }; [[stage(fragment)]] fn frag_main() -> tint_symbol { return tint_symbol(1.0); } )"; DataMap data; data.Add( CanonicalizeEntryPointIO::BuiltinStyle::kParameter); auto got = Run(src, data); EXPECT_EQ(expect, str(got)); } TEST_F(CanonicalizeEntryPointIOTest, Return_Struct) { auto* src = R"( struct FragOutput { [[location(0)]] color : vec4; [[builtin(frag_depth)]] depth : f32; [[builtin(sample_mask)]] mask : u32; }; [[stage(fragment)]] fn frag_main() -> FragOutput { var output : FragOutput; output.depth = 1.0; output.mask = 7u; output.color = vec4(0.5, 0.5, 0.5, 1.0); return output; } )"; auto* expect = R"( struct FragOutput { color : vec4; depth : f32; mask : u32; }; struct tint_symbol { [[location(0)]] color : vec4; [[builtin(frag_depth)]] depth : f32; [[builtin(sample_mask)]] mask : u32; }; [[stage(fragment)]] fn frag_main() -> tint_symbol { var output : FragOutput; output.depth = 1.0; output.mask = 7u; output.color = vec4(0.5, 0.5, 0.5, 1.0); return tint_symbol(output.color, output.depth, output.mask); } )"; DataMap data; data.Add( CanonicalizeEntryPointIO::BuiltinStyle::kParameter); auto got = Run(src, data); EXPECT_EQ(expect, str(got)); } TEST_F(CanonicalizeEntryPointIOTest, StructParameters_SharedDeviceFunction) { auto* src = R"( struct FragmentInput { [[location(0)]] value : f32; [[location(1)]] mul : f32; }; fn foo(x : FragmentInput) -> f32 { return x.value * x.mul; } [[stage(fragment)]] fn frag_main1(inputs : FragmentInput) { var x : f32 = foo(inputs); } [[stage(fragment)]] fn frag_main2(inputs : FragmentInput) { var x : f32 = foo(inputs); } )"; auto* expect = R"( struct FragmentInput { value : f32; mul : f32; }; fn foo(x : FragmentInput) -> f32 { return (x.value * x.mul); } struct tint_symbol_1 { [[location(0)]] value : f32; [[location(1)]] mul : f32; }; [[stage(fragment)]] fn frag_main1(tint_symbol : tint_symbol_1) { let inputs : FragmentInput = FragmentInput(tint_symbol.value, tint_symbol.mul); var x : f32 = foo(inputs); } struct tint_symbol_3 { [[location(0)]] value : f32; [[location(1)]] mul : f32; }; [[stage(fragment)]] fn frag_main2(tint_symbol_2 : tint_symbol_3) { let inputs : FragmentInput = FragmentInput(tint_symbol_2.value, tint_symbol_2.mul); var x : f32 = foo(inputs); } )"; DataMap data; data.Add( CanonicalizeEntryPointIO::BuiltinStyle::kParameter); auto got = Run(src, data); EXPECT_EQ(expect, str(got)); } TEST_F(CanonicalizeEntryPointIOTest, Struct_ModuleScopeVariable) { auto* src = R"( struct FragmentInput { [[location(0)]] col1 : f32; [[location(1)]] col2 : f32; }; var global_inputs : FragmentInput; fn foo() -> f32 { return global_inputs.col1 * 0.5; } fn bar() -> f32 { return global_inputs.col2 * 2.0; } [[stage(fragment)]] fn frag_main1(inputs : FragmentInput) { global_inputs = inputs; var r : f32 = foo(); var g : f32 = bar(); } )"; auto* expect = R"( struct FragmentInput { col1 : f32; col2 : f32; }; var global_inputs : FragmentInput; fn foo() -> f32 { return (global_inputs.col1 * 0.5); } fn bar() -> f32 { return (global_inputs.col2 * 2.0); } struct tint_symbol_1 { [[location(0)]] col1 : f32; [[location(1)]] col2 : f32; }; [[stage(fragment)]] fn frag_main1(tint_symbol : tint_symbol_1) { let inputs : FragmentInput = FragmentInput(tint_symbol.col1, tint_symbol.col2); global_inputs = inputs; var r : f32 = foo(); var g : f32 = bar(); } )"; DataMap data; data.Add( CanonicalizeEntryPointIO::BuiltinStyle::kParameter); auto got = Run(src, data); EXPECT_EQ(expect, str(got)); } TEST_F(CanonicalizeEntryPointIOTest, Struct_TypeAliases) { auto* src = R"( type myf32 = f32; struct FragmentInput { [[location(0)]] col1 : myf32; [[location(1)]] col2 : myf32; }; struct FragmentOutput { [[location(0)]] col1 : myf32; [[location(1)]] col2 : myf32; }; type MyFragmentInput = FragmentInput; type MyFragmentOutput = FragmentOutput; fn foo(x : MyFragmentInput) -> myf32 { return x.col1; } [[stage(fragment)]] fn frag_main(inputs : MyFragmentInput) -> MyFragmentOutput { var x : myf32 = foo(inputs); return MyFragmentOutput(x, inputs.col2); } )"; auto* expect = R"( type myf32 = f32; struct FragmentInput { col1 : myf32; col2 : myf32; }; struct FragmentOutput { col1 : myf32; col2 : myf32; }; type MyFragmentInput = FragmentInput; type MyFragmentOutput = FragmentOutput; fn foo(x : MyFragmentInput) -> myf32 { return x.col1; } struct tint_symbol_1 { [[location(0)]] col1 : myf32; [[location(1)]] col2 : myf32; }; struct tint_symbol_2 { [[location(0)]] col1 : myf32; [[location(1)]] col2 : myf32; }; [[stage(fragment)]] fn frag_main(tint_symbol : tint_symbol_1) -> tint_symbol_2 { let inputs : MyFragmentInput = MyFragmentInput(tint_symbol.col1, tint_symbol.col2); var x : myf32 = foo(inputs); let tint_symbol_3 : FragmentOutput = MyFragmentOutput(x, inputs.col2); return tint_symbol_2(tint_symbol_3.col1, tint_symbol_3.col2); } )"; DataMap data; data.Add( CanonicalizeEntryPointIO::BuiltinStyle::kParameter); auto got = Run(src, data); EXPECT_EQ(expect, str(got)); } TEST_F(CanonicalizeEntryPointIOTest, InterpolateAttributes) { auto* src = R"( struct VertexOut { [[builtin(position)]] pos : vec4; [[location(1), interpolate(flat)]] loc1: f32; [[location(2), interpolate(linear, sample)]] loc2 : f32; [[location(3), interpolate(perspective, centroid)]] loc3 : f32; }; struct FragmentIn { [[location(1), interpolate(flat)]] loc1: f32; [[location(2), interpolate(linear, sample)]] loc2 : f32; }; [[stage(vertex)]] fn vert_main() -> VertexOut { return VertexOut(); } [[stage(fragment)]] fn frag_main(inputs : FragmentIn, [[location(3), interpolate(perspective, centroid)]] loc3 : f32) { let x = inputs.loc1 + inputs.loc2 + loc3; } )"; auto* expect = R"( struct VertexOut { pos : vec4; loc1 : f32; loc2 : f32; loc3 : f32; }; struct FragmentIn { loc1 : f32; loc2 : f32; }; struct tint_symbol { [[location(1), interpolate(flat)]] loc1 : f32; [[location(2), interpolate(linear, sample)]] loc2 : f32; [[location(3), interpolate(perspective, centroid)]] loc3 : f32; [[builtin(position)]] pos : vec4; }; [[stage(vertex)]] fn vert_main() -> tint_symbol { let tint_symbol_1 : VertexOut = VertexOut(); return tint_symbol(tint_symbol_1.loc1, tint_symbol_1.loc2, tint_symbol_1.loc3, tint_symbol_1.pos); } struct tint_symbol_3 { [[location(1), interpolate(flat)]] loc1 : f32; [[location(2), interpolate(linear, sample)]] loc2 : f32; [[location(3), interpolate(perspective, centroid)]] loc3 : f32; }; [[stage(fragment)]] fn frag_main(tint_symbol_2 : tint_symbol_3) { let inputs : FragmentIn = FragmentIn(tint_symbol_2.loc1, tint_symbol_2.loc2); let loc3 : f32 = tint_symbol_2.loc3; let x = ((inputs.loc1 + inputs.loc2) + loc3); } )"; DataMap data; data.Add( CanonicalizeEntryPointIO::BuiltinStyle::kStructMember); auto got = Run(src, data); EXPECT_EQ(expect, str(got)); } TEST_F(CanonicalizeEntryPointIOTest, InvariantAttributes) { auto* src = R"( struct VertexOut { [[builtin(position), invariant]] pos : vec4; }; [[stage(vertex)]] fn main1() -> VertexOut { return VertexOut(); } [[stage(vertex)]] fn main2() -> [[builtin(position), invariant]] vec4 { return vec4(); } )"; auto* expect = R"( struct VertexOut { pos : vec4; }; struct tint_symbol { [[builtin(position), invariant]] pos : vec4; }; [[stage(vertex)]] fn main1() -> tint_symbol { let tint_symbol_1 : VertexOut = VertexOut(); return tint_symbol(tint_symbol_1.pos); } struct tint_symbol_2 { [[builtin(position), invariant]] value : vec4; }; [[stage(vertex)]] fn main2() -> tint_symbol_2 { return tint_symbol_2(vec4()); } )"; DataMap data; data.Add( CanonicalizeEntryPointIO::BuiltinStyle::kStructMember); auto got = Run(src, data); EXPECT_EQ(expect, str(got)); } TEST_F(CanonicalizeEntryPointIOTest, Struct_LayoutDecorations) { auto* src = R"( [[block]] struct FragmentInput { [[size(16), location(1)]] value : f32; [[builtin(position)]] [[align(32)]] coord : vec4; [[location(0), interpolate(linear, sample)]] [[align(128)]] loc0 : f32; }; struct FragmentOutput { [[size(16), location(1), interpolate(flat)]] value : f32; }; [[stage(fragment)]] fn frag_main(inputs : FragmentInput) -> FragmentOutput { return FragmentOutput(inputs.coord.x * inputs.value + inputs.loc0); } )"; auto* expect = R"( [[block]] struct FragmentInput { [[size(16)]] value : f32; [[align(32)]] coord : vec4; [[align(128)]] loc0 : f32; }; struct FragmentOutput { [[size(16)]] value : f32; }; struct tint_symbol_1 { [[location(0), interpolate(linear, sample)]] loc0 : f32; [[location(1)]] value : f32; [[builtin(position)]] coord : vec4; }; struct tint_symbol_2 { [[location(1), interpolate(flat)]] value : f32; }; [[stage(fragment)]] fn frag_main(tint_symbol : tint_symbol_1) -> tint_symbol_2 { let inputs : FragmentInput = FragmentInput(tint_symbol.value, tint_symbol.coord, tint_symbol.loc0); let tint_symbol_3 : FragmentOutput = FragmentOutput(((inputs.coord.x * inputs.value) + inputs.loc0)); return tint_symbol_2(tint_symbol_3.value); } )"; DataMap data; data.Add( CanonicalizeEntryPointIO::BuiltinStyle::kStructMember); auto got = Run(src, data); EXPECT_EQ(expect, str(got)); } TEST_F(CanonicalizeEntryPointIOTest, SortedMembers) { auto* src = R"( struct VertexOutput { [[location(1)]] b : u32; [[builtin(position)]] pos : vec4; [[location(3)]] d : u32; [[location(0)]] a : f32; [[location(2)]] c : i32; }; struct FragmentInputExtra { [[location(3)]] d : u32; [[builtin(position)]] pos : vec4; [[location(0)]] a : f32; }; [[stage(vertex)]] fn vert_main() -> VertexOutput { return VertexOutput(); } [[stage(fragment)]] fn frag_main([[builtin(front_facing)]] ff : bool, [[location(2)]] c : i32, inputs : FragmentInputExtra, [[location(1)]] b : u32) { } )"; auto* expect = R"( struct VertexOutput { b : u32; pos : vec4; d : u32; a : f32; c : i32; }; struct FragmentInputExtra { d : u32; pos : vec4; a : f32; }; struct tint_symbol { [[location(0)]] a : f32; [[location(1)]] b : u32; [[location(2)]] c : i32; [[location(3)]] d : u32; [[builtin(position)]] pos : vec4; }; [[stage(vertex)]] fn vert_main() -> tint_symbol { let tint_symbol_1 : VertexOutput = VertexOutput(); return tint_symbol(tint_symbol_1.a, tint_symbol_1.b, tint_symbol_1.c, tint_symbol_1.d, tint_symbol_1.pos); } struct tint_symbol_3 { [[location(0)]] a : f32; [[location(1)]] b : u32; [[location(2)]] c : i32; [[location(3)]] d : u32; [[builtin(position)]] pos : vec4; [[builtin(front_facing)]] ff : bool; }; [[stage(fragment)]] fn frag_main(tint_symbol_2 : tint_symbol_3) { let ff : bool = tint_symbol_2.ff; let c : i32 = tint_symbol_2.c; let inputs : FragmentInputExtra = FragmentInputExtra(tint_symbol_2.d, tint_symbol_2.pos, tint_symbol_2.a); let b : u32 = tint_symbol_2.b; } )"; DataMap data; data.Add( CanonicalizeEntryPointIO::BuiltinStyle::kStructMember); auto got = Run(src, data); EXPECT_EQ(expect, str(got)); } TEST_F(CanonicalizeEntryPointIOTest, DontRenameSymbols) { auto* src = R"( [[stage(fragment)]] fn tint_symbol_1([[location(0)]] col : f32) { } )"; auto* expect = R"( struct tint_symbol_2 { [[location(0)]] col : f32; }; [[stage(fragment)]] fn tint_symbol_1(tint_symbol : tint_symbol_2) { let col : f32 = tint_symbol.col; } )"; DataMap data; data.Add( CanonicalizeEntryPointIO::BuiltinStyle::kParameter); auto got = Run(src, data); EXPECT_EQ(expect, str(got)); } TEST_F(CanonicalizeEntryPointIOTest, FixedSampleMask_VoidNoReturn) { auto* src = R"( [[stage(fragment)]] fn frag_main() { } )"; auto* expect = R"( struct tint_symbol_1 { [[builtin(sample_mask)]] tint_symbol : u32; }; [[stage(fragment)]] fn frag_main() -> tint_symbol_1 { return tint_symbol_1(3u); } )"; DataMap data; data.Add( CanonicalizeEntryPointIO::BuiltinStyle::kParameter, 0x03u); auto got = Run(src, data); EXPECT_EQ(expect, str(got)); } TEST_F(CanonicalizeEntryPointIOTest, FixedSampleMask_VoidWithReturn) { auto* src = R"( [[stage(fragment)]] fn frag_main() { return; } )"; auto* expect = R"( struct tint_symbol_1 { [[builtin(sample_mask)]] tint_symbol : u32; }; [[stage(fragment)]] fn frag_main() -> tint_symbol_1 { return tint_symbol_1(3u); } )"; DataMap data; data.Add( CanonicalizeEntryPointIO::BuiltinStyle::kParameter, 0x03u); auto got = Run(src, data); EXPECT_EQ(expect, str(got)); } TEST_F(CanonicalizeEntryPointIOTest, FixedSampleMask_WithAuthoredMask) { auto* src = R"( [[stage(fragment)]] fn frag_main() -> [[builtin(sample_mask)]] u32 { return 7u; } )"; auto* expect = R"( struct tint_symbol { [[builtin(sample_mask)]] value : u32; }; [[stage(fragment)]] fn frag_main() -> tint_symbol { return tint_symbol((7u & 3u)); } )"; DataMap data; data.Add( CanonicalizeEntryPointIO::BuiltinStyle::kParameter, 0x03u); auto got = Run(src, data); EXPECT_EQ(expect, str(got)); } TEST_F(CanonicalizeEntryPointIOTest, FixedSampleMask_WithoutAuthoredMask) { auto* src = R"( [[stage(fragment)]] fn frag_main() -> [[location(0)]] f32 { return 1.0; } )"; auto* expect = R"( struct tint_symbol_1 { [[location(0)]] value : f32; [[builtin(sample_mask)]] tint_symbol : u32; }; [[stage(fragment)]] fn frag_main() -> tint_symbol_1 { return tint_symbol_1(1.0, 3u); } )"; DataMap data; data.Add( CanonicalizeEntryPointIO::BuiltinStyle::kParameter, 0x03u); auto got = Run(src, data); EXPECT_EQ(expect, str(got)); } TEST_F(CanonicalizeEntryPointIOTest, FixedSampleMask_StructWithAuthoredMask) { auto* src = R"( struct Output { [[builtin(frag_depth)]] depth : f32; [[builtin(sample_mask)]] mask : u32; [[location(0)]] value : f32; }; [[stage(fragment)]] fn frag_main() -> Output { return Output(0.5, 7u, 1.0); } )"; auto* expect = R"( struct Output { depth : f32; mask : u32; value : f32; }; struct tint_symbol { [[location(0)]] value : f32; [[builtin(frag_depth)]] depth : f32; [[builtin(sample_mask)]] mask : u32; }; [[stage(fragment)]] fn frag_main() -> tint_symbol { let tint_symbol_1 : Output = Output(0.5, 7u, 1.0); return tint_symbol(tint_symbol_1.value, tint_symbol_1.depth, (tint_symbol_1.mask & 3u)); } )"; DataMap data; data.Add( CanonicalizeEntryPointIO::BuiltinStyle::kParameter, 0x03u); auto got = Run(src, data); EXPECT_EQ(expect, str(got)); } TEST_F(CanonicalizeEntryPointIOTest, FixedSampleMask_StructWithoutAuthoredMask) { auto* src = R"( struct Output { [[builtin(frag_depth)]] depth : f32; [[location(0)]] value : f32; }; [[stage(fragment)]] fn frag_main() -> Output { return Output(0.5, 1.0); } )"; auto* expect = R"( struct Output { depth : f32; value : f32; }; struct tint_symbol_1 { [[location(0)]] value : f32; [[builtin(frag_depth)]] depth : f32; [[builtin(sample_mask)]] tint_symbol : u32; }; [[stage(fragment)]] fn frag_main() -> tint_symbol_1 { let tint_symbol_2 : Output = Output(0.5, 1.0); return tint_symbol_1(tint_symbol_2.value, tint_symbol_2.depth, 3u); } )"; DataMap data; data.Add( CanonicalizeEntryPointIO::BuiltinStyle::kParameter, 0x03u); auto got = Run(src, data); EXPECT_EQ(expect, str(got)); } TEST_F(CanonicalizeEntryPointIOTest, FixedSampleMask_MultipleShaders) { auto* src = R"( [[stage(fragment)]] fn frag_main1() -> [[builtin(sample_mask)]] u32 { return 7u; } [[stage(fragment)]] fn frag_main2() -> [[location(0)]] f32 { return 1.0; } [[stage(vertex)]] fn vert_main1() -> [[builtin(position)]] vec4 { return vec4(); } [[stage(compute), workgroup_size(1)]] fn comp_main1() { } )"; auto* expect = R"( struct tint_symbol { [[builtin(sample_mask)]] value : u32; }; [[stage(fragment)]] fn frag_main1() -> tint_symbol { return tint_symbol((7u & 3u)); } struct tint_symbol_2 { [[location(0)]] value : f32; [[builtin(sample_mask)]] tint_symbol_1 : u32; }; [[stage(fragment)]] fn frag_main2() -> tint_symbol_2 { return tint_symbol_2(1.0, 3u); } struct tint_symbol_3 { [[builtin(position)]] value : vec4; }; [[stage(vertex)]] fn vert_main1() -> tint_symbol_3 { return tint_symbol_3(vec4()); } [[stage(compute), workgroup_size(1)]] fn comp_main1() { } )"; DataMap data; data.Add( CanonicalizeEntryPointIO::BuiltinStyle::kParameter, 0x03u); auto got = Run(src, data); EXPECT_EQ(expect, str(got)); } } // namespace } // namespace transform } // namespace tint