blob: 9d2c50df1458b6493a0b0d580c354aa86125448f [file] [log] [blame]
// 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 CanonicalizeEntryPointIO";
auto got = Run<CanonicalizeEntryPointIO>(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<u32>,
[[builtin(position)]] coord : vec4<f32>) {
var col : f32 = (coord.x * loc1);
}
)";
auto* expect = R"(
struct tint_symbol_1 {
[[location(1)]]
loc1 : f32;
[[location(2)]]
loc2 : vec4<u32>;
};
[[stage(fragment)]]
fn frag_main([[builtin(position)]] coord : vec4<f32>, tint_symbol : tint_symbol_1) {
let loc1 : f32 = tint_symbol.loc1;
let loc2 : vec4<u32> = tint_symbol.loc2;
var col : f32 = (coord.x * loc1);
}
)";
DataMap data;
data.Add<CanonicalizeEntryPointIO::Config>(
CanonicalizeEntryPointIO::BuiltinStyle::kParameter);
auto got = Run<CanonicalizeEntryPointIO>(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<u32>,
[[builtin(position)]] coord : vec4<f32>) {
var col : f32 = (coord.x * loc1);
}
)";
auto* expect = R"(
struct tint_symbol_1 {
[[location(1)]]
loc1 : f32;
[[location(2)]]
loc2 : vec4<u32>;
[[builtin(position)]]
coord : vec4<f32>;
};
[[stage(fragment)]]
fn frag_main(tint_symbol : tint_symbol_1) {
let loc1 : f32 = tint_symbol.loc1;
let loc2 : vec4<u32> = tint_symbol.loc2;
let coord : vec4<f32> = tint_symbol.coord;
var col : f32 = (coord.x * loc1);
}
)";
DataMap data;
data.Add<CanonicalizeEntryPointIO::Config>(
CanonicalizeEntryPointIO::BuiltinStyle::kStructMember);
auto got = Run<CanonicalizeEntryPointIO>(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::Config>(
CanonicalizeEntryPointIO::BuiltinStyle::kParameter);
auto got = Run<CanonicalizeEntryPointIO>(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<u32>,
[[builtin(position)]] coord : vec4<f32>) {
}
)";
auto* expect = R"(
struct tint_symbol_1 {
[[location(1)]]
loc1 : f32;
[[location(2)]]
loc2 : vec4<u32>;
};
[[stage(fragment)]]
fn frag_main([[builtin(position)]] coord : vec4<f32>, tint_symbol : tint_symbol_1) {
let loc1 : f32 = tint_symbol.loc1;
let loc2 : vec4<u32> = tint_symbol.loc2;
}
)";
DataMap data;
data.Add<CanonicalizeEntryPointIO::Config>(
CanonicalizeEntryPointIO::BuiltinStyle::kParameter);
auto got = Run<CanonicalizeEntryPointIO>(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<u32>,
[[builtin(position)]] coord : vec4<f32>) {
}
)";
auto* expect = R"(
struct tint_symbol_1 {
[[location(1)]]
loc1 : f32;
[[location(2)]]
loc2 : vec4<u32>;
[[builtin(position)]]
coord : vec4<f32>;
};
[[stage(fragment)]]
fn frag_main(tint_symbol : tint_symbol_1) {
let loc1 : f32 = tint_symbol.loc1;
let loc2 : vec4<u32> = tint_symbol.loc2;
let coord : vec4<f32> = tint_symbol.coord;
}
)";
DataMap data;
data.Add<CanonicalizeEntryPointIO::Config>(
CanonicalizeEntryPointIO::BuiltinStyle::kStructMember);
auto got = Run<CanonicalizeEntryPointIO>(src, data);
EXPECT_EQ(expect, str(got));
}
TEST_F(CanonicalizeEntryPointIOTest, StructParameters_BuiltinsAsParameters) {
auto* src = R"(
struct FragBuiltins {
[[builtin(position)]] coord : vec4<f32>;
};
struct FragLocations {
[[location(1)]] loc1 : f32;
[[location(2)]] loc2 : vec4<u32>;
};
[[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<f32>;
};
struct FragLocations {
loc1 : f32;
loc2 : vec4<u32>;
};
struct tint_symbol_2 {
[[location(0)]]
loc0 : f32;
[[location(1)]]
loc1 : f32;
[[location(2)]]
loc2 : vec4<u32>;
};
[[stage(fragment)]]
fn frag_main([[builtin(position)]] tint_symbol_1 : vec4<f32>, 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::Config>(
CanonicalizeEntryPointIO::BuiltinStyle::kParameter);
auto got = Run<CanonicalizeEntryPointIO>(src, data);
EXPECT_EQ(expect, str(got));
}
TEST_F(CanonicalizeEntryPointIOTest, StructParameters_BuiltinsAsStructMembers) {
auto* src = R"(
struct FragBuiltins {
[[builtin(position)]] coord : vec4<f32>;
};
struct FragLocations {
[[location(1)]] loc1 : f32;
[[location(2)]] loc2 : vec4<u32>;
};
[[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<f32>;
};
struct FragLocations {
loc1 : f32;
loc2 : vec4<u32>;
};
struct tint_symbol_1 {
[[location(0)]]
loc0 : f32;
[[location(1)]]
loc1 : f32;
[[location(2)]]
loc2 : vec4<u32>;
[[builtin(position)]]
coord : vec4<f32>;
};
[[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::Config>(
CanonicalizeEntryPointIO::BuiltinStyle::kStructMember);
auto got = Run<CanonicalizeEntryPointIO>(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::Config>(
CanonicalizeEntryPointIO::BuiltinStyle::kParameter);
auto got = Run<CanonicalizeEntryPointIO>(src, data);
EXPECT_EQ(expect, str(got));
}
TEST_F(CanonicalizeEntryPointIOTest, Return_Struct) {
auto* src = R"(
struct FragOutput {
[[location(0)]] color : vec4<f32>;
[[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<f32>(0.5, 0.5, 0.5, 1.0);
return output;
}
)";
auto* expect = R"(
struct FragOutput {
color : vec4<f32>;
depth : f32;
mask : u32;
};
struct tint_symbol {
[[location(0)]]
color : vec4<f32>;
[[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<f32>(0.5, 0.5, 0.5, 1.0);
return tint_symbol(output.color, output.depth, output.mask);
}
)";
DataMap data;
data.Add<CanonicalizeEntryPointIO::Config>(
CanonicalizeEntryPointIO::BuiltinStyle::kParameter);
auto got = Run<CanonicalizeEntryPointIO>(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::Config>(
CanonicalizeEntryPointIO::BuiltinStyle::kParameter);
auto got = Run<CanonicalizeEntryPointIO>(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<private> 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<private> 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::Config>(
CanonicalizeEntryPointIO::BuiltinStyle::kParameter);
auto got = Run<CanonicalizeEntryPointIO>(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::Config>(
CanonicalizeEntryPointIO::BuiltinStyle::kParameter);
auto got = Run<CanonicalizeEntryPointIO>(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<f32>;
};
struct FragmentOutput {
[[size(16), location(1)]] value : f32;
};
[[stage(fragment)]]
fn frag_main(inputs : FragmentInput) -> FragmentOutput {
return FragmentOutput(inputs.coord.x * inputs.value);
}
)";
auto* expect = R"(
[[block]]
struct FragmentInput {
[[size(16)]]
value : f32;
[[align(32)]]
coord : vec4<f32>;
};
struct FragmentOutput {
[[size(16)]]
value : f32;
};
struct tint_symbol_1 {
[[location(1)]]
value : f32;
[[builtin(position)]]
coord : vec4<f32>;
};
struct tint_symbol_2 {
[[location(1)]]
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);
let tint_symbol_3 : FragmentOutput = FragmentOutput((inputs.coord.x * inputs.value));
return tint_symbol_2(tint_symbol_3.value);
}
)";
DataMap data;
data.Add<CanonicalizeEntryPointIO::Config>(
CanonicalizeEntryPointIO::BuiltinStyle::kStructMember);
auto got = Run<CanonicalizeEntryPointIO>(src, data);
EXPECT_EQ(expect, str(got));
}
TEST_F(CanonicalizeEntryPointIOTest, SortedMembers) {
auto* src = R"(
struct VertexOutput {
[[location(1)]] b : u32;
[[builtin(position)]] pos : vec4<f32>;
[[location(3)]] d : u32;
[[location(0)]] a : f32;
[[location(2)]] c : i32;
};
struct FragmentInputExtra {
[[location(3)]] d : u32;
[[builtin(position)]] pos : vec4<f32>;
[[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<f32>;
d : u32;
a : f32;
c : i32;
};
struct FragmentInputExtra {
d : u32;
pos : vec4<f32>;
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<f32>;
};
[[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<f32>;
[[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::Config>(
CanonicalizeEntryPointIO::BuiltinStyle::kStructMember);
auto got = Run<CanonicalizeEntryPointIO>(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::Config>(
CanonicalizeEntryPointIO::BuiltinStyle::kParameter);
auto got = Run<CanonicalizeEntryPointIO>(src, data);
EXPECT_EQ(expect, str(got));
}
} // namespace
} // namespace transform
} // namespace tint