blob: 57e23e6d37775e00a828059e64d367cea7939719 [file] [log] [blame] [edit]
// Copyright 2022 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/tint/transform/std140.h"
#include <string>
#include <utility>
#include <vector>
#include "src/tint/transform/test_helper.h"
#include "src/tint/utils/string.h"
namespace tint::transform {
namespace {
using Std140Test = TransformTest;
TEST_F(Std140Test, ShouldRunEmptyModule) {
auto* src = R"()";
EXPECT_FALSE(ShouldRun<Std140>(src));
}
TEST_F(Std140Test, ShouldRunStructMat2x2Unused) {
auto* src = R"(
struct Unused {
m : mat2x2<f32>,
}
)";
EXPECT_FALSE(ShouldRun<Std140>(src));
}
struct ShouldRunCase {
uint32_t columns;
uint32_t rows;
bool should_run;
std::string Mat() const { return "mat" + std::to_string(columns) + "x" + std::to_string(rows); }
};
inline std::ostream& operator<<(std::ostream& os, const ShouldRunCase& c) {
return os << c.Mat();
}
using Std140TestShouldRun = TransformTestWithParam<ShouldRunCase>;
TEST_P(Std140TestShouldRun, StructStorage) {
std::string src = R"(
struct S {
m : ${mat}<f32>,
}
@group(0) @binding(0) var<storage> s : S;
)";
src = utils::ReplaceAll(src, "${mat}", GetParam().Mat());
EXPECT_FALSE(ShouldRun<Std140>(src));
}
TEST_P(Std140TestShouldRun, StructUniform) {
std::string src = R"(
struct S {
m : ${mat}<f32>,
}
@group(0) @binding(0) var<uniform> s : S;
)";
src = utils::ReplaceAll(src, "${mat}", GetParam().Mat());
EXPECT_EQ(ShouldRun<Std140>(src), GetParam().should_run);
}
TEST_P(Std140TestShouldRun, ArrayStorage) {
std::string src = R"(
@group(0) @binding(0) var<storage> s : array<${mat}<f32>, 2>;
)";
src = utils::ReplaceAll(src, "${mat}", GetParam().Mat());
EXPECT_FALSE(ShouldRun<Std140>(src));
}
TEST_P(Std140TestShouldRun, ArrayUniform) {
if (GetParam().columns == 3u && GetParam().rows == 2u) {
// This permutation is invalid. Skip the test:
// error: uniform storage requires that array elements be aligned to 16 bytes, but array
// element alignment is currently 24. Consider wrapping the element type in a struct and
// using the @size attribute.
return;
}
std::string src = R"(
@group(0) @binding(0) var<uniform> s : array<${mat}<f32>, 2>;
)";
src = utils::ReplaceAll(src, "${mat}", GetParam().Mat());
EXPECT_FALSE(ShouldRun<Std140>(src));
}
INSTANTIATE_TEST_SUITE_P(Std140TestShouldRun,
Std140TestShouldRun,
::testing::ValuesIn(std::vector<ShouldRunCase>{
{2, 2, true},
{2, 3, false},
{2, 4, false},
{3, 2, true},
{3, 3, false},
{3, 4, false},
{4, 2, true},
{4, 3, false},
{4, 4, false},
}));
TEST_F(Std140Test, EmptyModule) {
auto* src = R"()";
auto* expect = R"()";
auto got = Run<Std140>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(Std140Test, SingleStructMat4x4Uniform) {
auto* src = R"(
struct S {
m : mat4x4<f32>,
}
@group(0) @binding(0) var<uniform> s : S;
)";
auto* expect = src; // Nothing violates std140 layout
auto got = Run<Std140>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(Std140Test, SingleStructMat2x2Uniform) {
auto* src = R"(
struct S {
m : mat2x2<f32>,
}
@group(0) @binding(0) var<uniform> s : S;
)";
auto* expect = R"(
struct S {
m : mat2x2<f32>,
}
struct S_std140 {
m_0 : vec2<f32>,
m_1 : vec2<f32>,
}
@group(0) @binding(0) var<uniform> s : S_std140;
)";
auto got = Run<Std140>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(Std140Test, CustomAlignMat3x2) {
auto* src = R"(
struct S {
before : i32,
@align(128) m : mat3x2<f32>,
after : i32,
}
@group(0) @binding(0) var<uniform> s : S;
)";
auto* expect = R"(
struct S {
before : i32,
@align(128)
m : mat3x2<f32>,
after : i32,
}
struct S_std140 {
before : i32,
@align(128u)
m_0 : vec2<f32>,
m_1 : vec2<f32>,
m_2 : vec2<f32>,
after : i32,
}
@group(0) @binding(0) var<uniform> s : S_std140;
)";
auto got = Run<Std140>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(Std140Test, CustomSizeMat3x2) {
auto* src = R"(
struct S {
before : i32,
@size(128) m : mat3x2<f32>,
after : i32,
}
@group(0) @binding(0) var<uniform> s : S;
)";
auto* expect = R"(
struct S {
before : i32,
@size(128)
m : mat3x2<f32>,
after : i32,
}
struct S_std140 {
before : i32,
m_0 : vec2<f32>,
m_1 : vec2<f32>,
@size(112)
m_2 : vec2<f32>,
after : i32,
}
@group(0) @binding(0) var<uniform> s : S_std140;
)";
auto got = Run<Std140>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(Std140Test, CustomAlignAndSizeMat3x2) {
auto* src = R"(
struct S {
before : i32,
@align(128) @size(128) m : mat3x2<f32>,
after : i32,
}
@group(0) @binding(0) var<uniform> s : S;
)";
auto* expect = R"(
struct S {
before : i32,
@align(128) @size(128)
m : mat3x2<f32>,
after : i32,
}
struct S_std140 {
before : i32,
@align(128u)
m_0 : vec2<f32>,
m_1 : vec2<f32>,
@size(112)
m_2 : vec2<f32>,
after : i32,
}
@group(0) @binding(0) var<uniform> s : S_std140;
)";
auto got = Run<Std140>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(Std140Test, StructMatricesUniform) {
auto* src = R"(
struct S2x2 {
m : mat2x2<f32>,
}
struct S3x2 {
m : mat3x2<f32>,
}
struct S4x2 {
m : mat4x2<f32>,
}
struct S2x3 {
m : mat2x3<f32>,
}
struct S3x3 {
m : mat3x3<f32>,
}
struct S4x3 {
m : mat4x3<f32>,
}
struct S2x4 {
m : mat2x4<f32>,
}
struct S3x4 {
m : mat3x4<f32>,
}
struct S4x4 {
m : mat4x4<f32>,
}
@group(2) @binding(2) var<uniform> s2x2 : S2x2;
@group(3) @binding(2) var<uniform> s3x2 : S3x2;
@group(4) @binding(2) var<uniform> s4x2 : S4x2;
@group(2) @binding(3) var<uniform> s2x3 : S2x3;
@group(3) @binding(3) var<uniform> s3x3 : S3x3;
@group(4) @binding(3) var<uniform> s4x3 : S4x3;
@group(2) @binding(4) var<uniform> s2x4 : S2x4;
@group(3) @binding(4) var<uniform> s3x4 : S3x4;
@group(4) @binding(4) var<uniform> s4x4 : S4x4;
)";
auto* expect = R"(
struct S2x2 {
m : mat2x2<f32>,
}
struct S2x2_std140 {
m_0 : vec2<f32>,
m_1 : vec2<f32>,
}
struct S3x2 {
m : mat3x2<f32>,
}
struct S3x2_std140 {
m_0 : vec2<f32>,
m_1 : vec2<f32>,
m_2 : vec2<f32>,
}
struct S4x2 {
m : mat4x2<f32>,
}
struct S4x2_std140 {
m_0 : vec2<f32>,
m_1 : vec2<f32>,
m_2 : vec2<f32>,
m_3 : vec2<f32>,
}
struct S2x3 {
m : mat2x3<f32>,
}
struct S3x3 {
m : mat3x3<f32>,
}
struct S4x3 {
m : mat4x3<f32>,
}
struct S2x4 {
m : mat2x4<f32>,
}
struct S3x4 {
m : mat3x4<f32>,
}
struct S4x4 {
m : mat4x4<f32>,
}
@group(2) @binding(2) var<uniform> s2x2 : S2x2_std140;
@group(3) @binding(2) var<uniform> s3x2 : S3x2_std140;
@group(4) @binding(2) var<uniform> s4x2 : S4x2_std140;
@group(2) @binding(3) var<uniform> s2x3 : S2x3;
@group(3) @binding(3) var<uniform> s3x3 : S3x3;
@group(4) @binding(3) var<uniform> s4x3 : S4x3;
@group(2) @binding(4) var<uniform> s2x4 : S2x4;
@group(3) @binding(4) var<uniform> s3x4 : S3x4;
@group(4) @binding(4) var<uniform> s4x4 : S4x4;
)";
auto got = Run<Std140>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(Std140Test, StructMat2x2Uniform_NameCollision) {
auto* src = R"(
struct S {
m_1 : i32,
m : mat2x2<f32>,
}
@group(0) @binding(0) var<uniform> s : S;
)";
auto* expect = R"(
struct S {
m_1 : i32,
m : mat2x2<f32>,
}
struct S_std140 {
m_1 : i32,
m__0 : vec2<f32>,
m__1 : vec2<f32>,
}
@group(0) @binding(0) var<uniform> s : S_std140;
)";
auto got = Run<Std140>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(Std140Test, StructMat2x2Uniform_LoadStruct) {
auto* src = R"(
struct S {
m : mat2x2<f32>,
}
@group(0) @binding(0) var<uniform> s : S;
fn f() {
let l = s;
}
)";
auto* expect = R"(
struct S {
m : mat2x2<f32>,
}
struct S_std140 {
m_0 : vec2<f32>,
m_1 : vec2<f32>,
}
@group(0) @binding(0) var<uniform> s : S_std140;
fn conv_S(val : S_std140) -> S {
return S(mat2x2<f32>(val.m_0, val.m_1));
}
fn f() {
let l = conv_S(s);
}
)";
auto got = Run<Std140>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(Std140Test, StructMat2x2Uniform_LoadMatrix) {
auto* src = R"(
struct S {
m : mat2x2<f32>,
}
@group(0) @binding(0) var<uniform> s : S;
fn f() {
let l = s.m;
}
)";
auto* expect = R"(
struct S {
m : mat2x2<f32>,
}
struct S_std140 {
m_0 : vec2<f32>,
m_1 : vec2<f32>,
}
@group(0) @binding(0) var<uniform> s : S_std140;
fn load_s_m() -> mat2x2<f32> {
let s = &(s);
return mat2x2<f32>((*(s)).m_0, (*(s)).m_1);
}
fn f() {
let l = load_s_m();
}
)";
auto got = Run<Std140>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(Std140Test, StructMat2x2Uniform_LoadColumn0) {
auto* src = R"(
struct S {
m : mat2x2<f32>,
}
@group(0) @binding(0) var<uniform> s : S;
fn f() {
let l = s.m[0];
}
)";
auto* expect = R"(
struct S {
m : mat2x2<f32>,
}
struct S_std140 {
m_0 : vec2<f32>,
m_1 : vec2<f32>,
}
@group(0) @binding(0) var<uniform> s : S_std140;
fn f() {
let l = s.m_0;
}
)";
auto got = Run<Std140>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(Std140Test, StructMat2x2Uniform_LoadColumn1) {
auto* src = R"(
struct S {
m : mat2x2<f32>,
}
@group(0) @binding(0) var<uniform> s : S;
fn f() {
let l = s.m[1];
}
)";
auto* expect = R"(
struct S {
m : mat2x2<f32>,
}
struct S_std140 {
m_0 : vec2<f32>,
m_1 : vec2<f32>,
}
@group(0) @binding(0) var<uniform> s : S_std140;
fn f() {
let l = s.m_1;
}
)";
auto got = Run<Std140>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(Std140Test, StructMat2x2Uniform_LoadColumnI) {
auto* src = R"(
struct S {
m : mat2x2<f32>,
}
@group(0) @binding(0) var<uniform> s : S;
fn f() {
let I = 0;
let l = s.m[I];
}
)";
auto* expect = R"(
struct S {
m : mat2x2<f32>,
}
struct S_std140 {
m_0 : vec2<f32>,
m_1 : vec2<f32>,
}
@group(0) @binding(0) var<uniform> s : S_std140;
fn load_s_m_p0(p0 : u32) -> vec2<f32> {
switch(p0) {
case 0u: {
return s.m_0;
}
case 1u: {
return s.m_1;
}
default: {
return vec2<f32>();
}
}
}
fn f() {
let I = 0;
let l = load_s_m_p0(u32(I));
}
)";
auto got = Run<Std140>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(Std140Test, StructMat2x2Uniform_LoadScalar00) {
auto* src = R"(
struct S {
m : mat2x2<f32>,
}
@group(0) @binding(0) var<uniform> s : S;
fn f() {
let l = s.m[0][0];
}
)";
auto* expect = R"(
struct S {
m : mat2x2<f32>,
}
struct S_std140 {
m_0 : vec2<f32>,
m_1 : vec2<f32>,
}
@group(0) @binding(0) var<uniform> s : S_std140;
fn f() {
let l = s.m_0[0u];
}
)";
auto got = Run<Std140>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(Std140Test, StructMat2x2Uniform_LoadScalar10) {
auto* src = R"(
struct S {
m : mat2x2<f32>,
}
@group(0) @binding(0) var<uniform> s : S;
fn f() {
let l = s.m[1][0];
}
)";
auto* expect = R"(
struct S {
m : mat2x2<f32>,
}
struct S_std140 {
m_0 : vec2<f32>,
m_1 : vec2<f32>,
}
@group(0) @binding(0) var<uniform> s : S_std140;
fn f() {
let l = s.m_1[0u];
}
)";
auto got = Run<Std140>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(Std140Test, StructMat2x2Uniform_LoadScalarI0) {
auto* src = R"(
struct S {
m : mat2x2<f32>,
}
@group(0) @binding(0) var<uniform> s : S;
fn f() {
let I = 0;
let l = s.m[I][0];
}
)";
auto* expect = R"(
struct S {
m : mat2x2<f32>,
}
struct S_std140 {
m_0 : vec2<f32>,
m_1 : vec2<f32>,
}
@group(0) @binding(0) var<uniform> s : S_std140;
fn load_s_m_p0_0(p0 : u32) -> f32 {
switch(p0) {
case 0u: {
return s.m_0[0u];
}
case 1u: {
return s.m_1[0u];
}
default: {
return f32();
}
}
}
fn f() {
let I = 0;
let l = load_s_m_p0_0(u32(I));
}
)";
auto got = Run<Std140>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(Std140Test, StructMat2x2Uniform_LoadScalar01) {
auto* src = R"(
struct S {
m : mat2x2<f32>,
}
@group(0) @binding(0) var<uniform> s : S;
fn f() {
let l = s.m[0][1];
}
)";
auto* expect = R"(
struct S {
m : mat2x2<f32>,
}
struct S_std140 {
m_0 : vec2<f32>,
m_1 : vec2<f32>,
}
@group(0) @binding(0) var<uniform> s : S_std140;
fn f() {
let l = s.m_0[1u];
}
)";
auto got = Run<Std140>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(Std140Test, StructMat2x2Uniform_LoadScalar11) {
auto* src = R"(
struct S {
m : mat2x2<f32>,
}
@group(0) @binding(0) var<uniform> s : S;
fn f() {
let l = s.m[1][1];
}
)";
auto* expect = R"(
struct S {
m : mat2x2<f32>,
}
struct S_std140 {
m_0 : vec2<f32>,
m_1 : vec2<f32>,
}
@group(0) @binding(0) var<uniform> s : S_std140;
fn f() {
let l = s.m_1[1u];
}
)";
auto got = Run<Std140>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(Std140Test, StructMat2x2Uniform_LoadScalarI1) {
auto* src = R"(
struct S {
m : mat2x2<f32>,
}
@group(0) @binding(0) var<uniform> s : S;
fn f() {
let I = 0;
let l = s.m[I][1];
}
)";
auto* expect = R"(
struct S {
m : mat2x2<f32>,
}
struct S_std140 {
m_0 : vec2<f32>,
m_1 : vec2<f32>,
}
@group(0) @binding(0) var<uniform> s : S_std140;
fn load_s_m_p0_1(p0 : u32) -> f32 {
switch(p0) {
case 0u: {
return s.m_0[1u];
}
case 1u: {
return s.m_1[1u];
}
default: {
return f32();
}
}
}
fn f() {
let I = 0;
let l = load_s_m_p0_1(u32(I));
}
)";
auto got = Run<Std140>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(Std140Test, StructMat2x2Uniform_LoadScalar0I) {
auto* src = R"(
struct S {
m : mat2x2<f32>,
}
@group(0) @binding(0) var<uniform> s : S;
fn f() {
let I = 0;
let l = s.m[0][I];
}
)";
auto* expect = R"(
struct S {
m : mat2x2<f32>,
}
struct S_std140 {
m_0 : vec2<f32>,
m_1 : vec2<f32>,
}
@group(0) @binding(0) var<uniform> s : S_std140;
fn f() {
let I = 0;
let l = s.m_0[I];
}
)";
auto got = Run<Std140>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(Std140Test, StructMat2x2Uniform_LoadScalar1I) {
auto* src = R"(
struct S {
m : mat2x2<f32>,
}
@group(0) @binding(0) var<uniform> s : S;
fn f() {
let I = 0;
let l = s.m[1][I];
}
)";
auto* expect = R"(
struct S {
m : mat2x2<f32>,
}
struct S_std140 {
m_0 : vec2<f32>,
m_1 : vec2<f32>,
}
@group(0) @binding(0) var<uniform> s : S_std140;
fn f() {
let I = 0;
let l = s.m_1[I];
}
)";
auto got = Run<Std140>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(Std140Test, StructMat2x2Uniform_LoadScalarII) {
auto* src = R"(
struct S {
m : mat2x2<f32>,
}
@group(0) @binding(0) var<uniform> s : S;
fn f() {
let I = 0;
let l = s.m[I][I];
}
)";
auto* expect = R"(
struct S {
m : mat2x2<f32>,
}
struct S_std140 {
m_0 : vec2<f32>,
m_1 : vec2<f32>,
}
@group(0) @binding(0) var<uniform> s : S_std140;
fn load_s_m_p0_p1(p0 : u32, p1 : u32) -> f32 {
switch(p0) {
case 0u: {
return s.m_0[p1];
}
case 1u: {
return s.m_1[p1];
}
default: {
return f32();
}
}
}
fn f() {
let I = 0;
let l = load_s_m_p0_p1(u32(I), u32(I));
}
)";
auto got = Run<Std140>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(Std140Test, ArrayStructMat3x2Uniform_LoadArray) {
auto* src = R"(
struct S {
@size(64) m : mat3x2<f32>,
}
@group(0) @binding(0) var<uniform> a : array<S, 3>;
fn f() {
let l = a;
}
)";
auto* expect = R"(
struct S {
@size(64)
m : mat3x2<f32>,
}
struct S_std140 {
m_0 : vec2<f32>,
m_1 : vec2<f32>,
@size(48)
m_2 : vec2<f32>,
}
@group(0) @binding(0) var<uniform> a : array<S_std140, 3u>;
fn conv_S(val : S_std140) -> S {
return S(mat3x2<f32>(val.m_0, val.m_1, val.m_2));
}
fn conv_arr_3_S(val : array<S_std140, 3u>) -> array<S, 3u> {
var arr : array<S, 3u>;
for(var i : u32; (i < 3u); i = (i + 1)) {
arr[i] = conv_S(val[i]);
}
return arr;
}
fn f() {
let l = conv_arr_3_S(a);
}
)";
auto got = Run<Std140>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(Std140Test, ArrayStructMat3x2Uniform_LoadStruct0) {
auto* src = R"(
struct S {
@size(64) m : mat3x2<f32>,
}
@group(0) @binding(0) var<uniform> a : array<S, 3>;
fn f() {
let l = a[0];
}
)";
auto* expect = R"(
struct S {
@size(64)
m : mat3x2<f32>,
}
struct S_std140 {
m_0 : vec2<f32>,
m_1 : vec2<f32>,
@size(48)
m_2 : vec2<f32>,
}
@group(0) @binding(0) var<uniform> a : array<S_std140, 3u>;
fn conv_S(val : S_std140) -> S {
return S(mat3x2<f32>(val.m_0, val.m_1, val.m_2));
}
fn f() {
let l = conv_S(a[0u]);
}
)";
auto got = Run<Std140>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(Std140Test, ArrayStructMat3x2Uniform_LoadStruct1) {
auto* src = R"(
struct S {
@size(64) m : mat3x2<f32>,
}
@group(0) @binding(0) var<uniform> a : array<S, 3>;
fn f() {
let l = a[1];
}
)";
auto* expect = R"(
struct S {
@size(64)
m : mat3x2<f32>,
}
struct S_std140 {
m_0 : vec2<f32>,
m_1 : vec2<f32>,
@size(48)
m_2 : vec2<f32>,
}
@group(0) @binding(0) var<uniform> a : array<S_std140, 3u>;
fn conv_S(val : S_std140) -> S {
return S(mat3x2<f32>(val.m_0, val.m_1, val.m_2));
}
fn f() {
let l = conv_S(a[1u]);
}
)";
auto got = Run<Std140>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(Std140Test, ArrayStructMat3x2Uniform_LoadStructI) {
auto* src = R"(
struct S {
@size(64) m : mat3x2<f32>,
}
@group(0) @binding(0) var<uniform> a : array<S, 3>;
fn f() {
let I = 1;
let l = a[I];
}
)";
auto* expect = R"(
struct S {
@size(64)
m : mat3x2<f32>,
}
struct S_std140 {
m_0 : vec2<f32>,
m_1 : vec2<f32>,
@size(48)
m_2 : vec2<f32>,
}
@group(0) @binding(0) var<uniform> a : array<S_std140, 3u>;
fn conv_S(val : S_std140) -> S {
return S(mat3x2<f32>(val.m_0, val.m_1, val.m_2));
}
fn f() {
let I = 1;
let l = conv_S(a[I]);
}
)";
auto got = Run<Std140>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(Std140Test, ArrayStructMat3x2Uniform_LoadMatrix0) {
auto* src = R"(
struct S {
@size(64) m : mat3x2<f32>,
}
@group(0) @binding(0) var<uniform> a : array<S, 3>;
fn f() {
let l = a[0].m;
}
)";
auto* expect = R"(
struct S {
@size(64)
m : mat3x2<f32>,
}
struct S_std140 {
m_0 : vec2<f32>,
m_1 : vec2<f32>,
@size(48)
m_2 : vec2<f32>,
}
@group(0) @binding(0) var<uniform> a : array<S_std140, 3u>;
fn load_a_0_m() -> mat3x2<f32> {
let s = &(a[0u]);
return mat3x2<f32>((*(s)).m_0, (*(s)).m_1, (*(s)).m_2);
}
fn f() {
let l = load_a_0_m();
}
)";
auto got = Run<Std140>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(Std140Test, ArrayStructMat3x2Uniform_LoadMatrix1) {
auto* src = R"(
struct S {
@size(64) m : mat3x2<f32>,
}
@group(0) @binding(0) var<uniform> a : array<S, 3>;
fn f() {
let l = a[1].m;
}
)";
auto* expect = R"(
struct S {
@size(64)
m : mat3x2<f32>,
}
struct S_std140 {
m_0 : vec2<f32>,
m_1 : vec2<f32>,
@size(48)
m_2 : vec2<f32>,
}
@group(0) @binding(0) var<uniform> a : array<S_std140, 3u>;
fn load_a_1_m() -> mat3x2<f32> {
let s = &(a[1u]);
return mat3x2<f32>((*(s)).m_0, (*(s)).m_1, (*(s)).m_2);
}
fn f() {
let l = load_a_1_m();
}
)";
auto got = Run<Std140>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(Std140Test, ArrayStructMat3x2Uniform_LoadMatrixI) {
auto* src = R"(
struct S {
@size(64) m : mat3x2<f32>,
}
@group(0) @binding(0) var<uniform> a : array<S, 3>;
fn f() {
let I = 1;
let l = a[I].m;
}
)";
auto* expect = R"(
struct S {
@size(64)
m : mat3x2<f32>,
}
struct S_std140 {
m_0 : vec2<f32>,
m_1 : vec2<f32>,
@size(48)
m_2 : vec2<f32>,
}
@group(0) @binding(0) var<uniform> a : array<S_std140, 3u>;
fn load_a_p0_m(p0 : u32) -> mat3x2<f32> {
let s = &(a[p0]);
return mat3x2<f32>((*(s)).m_0, (*(s)).m_1, (*(s)).m_2);
}
fn f() {
let I = 1;
let l = load_a_p0_m(u32(I));
}
)";
auto got = Run<Std140>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(Std140Test, ArrayStructMat3x2Uniform_LoadMatrix0Column0) {
auto* src = R"(
struct S {
@size(64) m : mat3x2<f32>,
}
@group(0) @binding(0) var<uniform> a : array<S, 3>;
fn f() {
let l = a[0].m[0];
}
)";
auto* expect = R"(
struct S {
@size(64)
m : mat3x2<f32>,
}
struct S_std140 {
m_0 : vec2<f32>,
m_1 : vec2<f32>,
@size(48)
m_2 : vec2<f32>,
}
@group(0) @binding(0) var<uniform> a : array<S_std140, 3u>;
fn f() {
let l = a[0u].m_0;
}
)";
auto got = Run<Std140>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(Std140Test, ArrayStructMat3x2Uniform_LoadMatrix1Column0) {
auto* src = R"(
struct S {
@size(64) m : mat3x2<f32>,
}
@group(0) @binding(0) var<uniform> a : array<S, 3>;
fn f() {
let l = a[1].m[0];
}
)";
auto* expect = R"(
struct S {
@size(64)
m : mat3x2<f32>,
}
struct S_std140 {
m_0 : vec2<f32>,
m_1 : vec2<f32>,
@size(48)
m_2 : vec2<f32>,
}
@group(0) @binding(0) var<uniform> a : array<S_std140, 3u>;
fn f() {
let l = a[1u].m_0;
}
)";
auto got = Run<Std140>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(Std140Test, ArrayStructMat3x2Uniform_LoadMatrixIColumn0) {
auto* src = R"(
struct S {
@size(64) m : mat3x2<f32>,
}
@group(0) @binding(0) var<uniform> a : array<S, 3>;
fn f() {
let I = 1;
let l = a[I].m[0];
}
)";
auto* expect = R"(
struct S {
@size(64)
m : mat3x2<f32>,
}
struct S_std140 {
m_0 : vec2<f32>,
m_1 : vec2<f32>,
@size(48)
m_2 : vec2<f32>,
}
@group(0) @binding(0) var<uniform> a : array<S_std140, 3u>;
fn f() {
let I = 1;
let l = a[I].m_0;
}
)";
auto got = Run<Std140>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(Std140Test, ArrayStructMat3x2Uniform_LoadMatrix0Column1) {
auto* src = R"(
struct S {
@size(64) m : mat3x2<f32>,
}
@group(0) @binding(0) var<uniform> a : array<S, 3>;
fn f() {
let l = a[0].m[1];
}
)";
auto* expect = R"(
struct S {
@size(64)
m : mat3x2<f32>,
}
struct S_std140 {
m_0 : vec2<f32>,
m_1 : vec2<f32>,
@size(48)
m_2 : vec2<f32>,
}
@group(0) @binding(0) var<uniform> a : array<S_std140, 3u>;
fn f() {
let l = a[0u].m_1;
}
)";
auto got = Run<Std140>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(Std140Test, ArrayStructMat3x2Uniform_LoadMatrix1Column1) {
auto* src = R"(
struct S {
@size(64) m : mat3x2<f32>,
}
@group(0) @binding(0) var<uniform> a : array<S, 3>;
fn f() {
let l = a[1].m[1];
}
)";
auto* expect = R"(
struct S {
@size(64)
m : mat3x2<f32>,
}
struct S_std140 {
m_0 : vec2<f32>,
m_1 : vec2<f32>,
@size(48)
m_2 : vec2<f32>,
}
@group(0) @binding(0) var<uniform> a : array<S_std140, 3u>;
fn f() {
let l = a[1u].m_1;
}
)";
auto got = Run<Std140>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(Std140Test, ArrayStructMat3x2Uniform_LoadMatrixIColumnI) {
auto* src = R"(
struct S {
@size(64) m : mat3x2<f32>,
}
@group(0) @binding(0) var<uniform> a : array<S, 3>;
fn f() {
let I = 1;
let l = a[I].m[I];
}
)";
auto* expect = R"(
struct S {
@size(64)
m : mat3x2<f32>,
}
struct S_std140 {
m_0 : vec2<f32>,
m_1 : vec2<f32>,
@size(48)
m_2 : vec2<f32>,
}
@group(0) @binding(0) var<uniform> a : array<S_std140, 3u>;
fn load_a_p0_m_p1(p0 : u32, p1 : u32) -> vec2<f32> {
switch(p1) {
case 0u: {
return a[p0].m_0;
}
case 1u: {
return a[p0].m_1;
}
case 2u: {
return a[p0].m_2;
}
default: {
return vec2<f32>();
}
}
}
fn f() {
let I = 1;
let l = load_a_p0_m_p1(u32(I), u32(I));
}
)";
auto got = Run<Std140>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(Std140Test, ArrayStructArrayStructMat4x2Uniform_Loads) {
auto* src = R"(
struct Inner {
m : mat4x2<f32>,
}
struct Outer {
a : array<Inner, 4>,
}
@group(0) @binding(0) var<uniform> a : array<Outer, 4>;
fn f() {
let I = 1;
let l_a : array<Outer, 4> = a;
let l_a_1 : Outer = a[1];
let l_a_2_a : array<Inner, 4> = a[2].a;
let l_a_3_a_1 : Inner = a[3].a[1];
let l_a_0_a_2_m : mat4x2<f32> = a[0].a[2].m;
let l_a_1_a_3_m_0 : vec2<f32> = a[1].a[3].m[0];
let l_a_2_a_0_m_1_0 : f32 = a[2].a[0].m[1][0];
}
)";
auto* expect = R"(
struct Inner {
m : mat4x2<f32>,
}
struct Inner_std140 {
m_0 : vec2<f32>,
m_1 : vec2<f32>,
m_2 : vec2<f32>,
m_3 : vec2<f32>,
}
struct Outer {
a : array<Inner, 4>,
}
struct Outer_std140 {
a : array<Inner_std140, 4u>,
}
@group(0) @binding(0) var<uniform> a : array<Outer_std140, 4u>;
fn conv_Inner(val : Inner_std140) -> Inner {
return Inner(mat4x2<f32>(val.m_0, val.m_1, val.m_2, val.m_3));
}
fn conv_arr_4_Inner(val : array<Inner_std140, 4u>) -> array<Inner, 4u> {
var arr : array<Inner, 4u>;
for(var i : u32; (i < 4u); i = (i + 1)) {
arr[i] = conv_Inner(val[i]);
}
return arr;
}
fn conv_Outer(val : Outer_std140) -> Outer {
return Outer(conv_arr_4_Inner(val.a));
}
fn conv_arr_4_Outer(val : array<Outer_std140, 4u>) -> array<Outer, 4u> {
var arr : array<Outer, 4u>;
for(var i : u32; (i < 4u); i = (i + 1)) {
arr[i] = conv_Outer(val[i]);
}
return arr;
}
fn load_a_0_a_2_m() -> mat4x2<f32> {
let s = &(a[0u].a[2u]);
return mat4x2<f32>((*(s)).m_0, (*(s)).m_1, (*(s)).m_2, (*(s)).m_3);
}
fn f() {
let I = 1;
let l_a : array<Outer, 4> = conv_arr_4_Outer(a);
let l_a_1 : Outer = conv_Outer(a[1u]);
let l_a_2_a : array<Inner, 4> = conv_arr_4_Inner(a[2u].a);
let l_a_3_a_1 : Inner = conv_Inner(a[3u].a[1u]);
let l_a_0_a_2_m : mat4x2<f32> = load_a_0_a_2_m();
let l_a_1_a_3_m_0 : vec2<f32> = a[1u].a[3u].m_0;
let l_a_2_a_0_m_1_0 : f32 = a[2u].a[0u].m_1[0u];
}
)";
auto got = Run<Std140>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(Std140Test, ArrayStructArrayStructMat4x2Uniform_LoadsViaPtrs) {
// Note: Std140Test requires the PromoteSideEffectsToDecl transform to have been run first, so
// side-effects in the let-chain will not be a problem.
auto* src = R"(
struct Inner {
m : mat4x2<f32>,
}
struct Outer {
a : array<Inner, 4>,
}
@group(0) @binding(0) var<uniform> a : array<Outer, 4>;
fn f() {
let I = 1;
let p_a = &a;
let p_a_3 = &((*p_a)[3]);
let p_a_3_a = &((*p_a_3).a);
let p_a_3_a_2 = &((*p_a_3_a)[2]);
let p_a_3_a_2_m = &((*p_a_3_a_2).m);
let p_a_3_a_2_m_1 = &((*p_a_3_a_2_m)[1]);
let l_a : array<Outer, 4> = *p_a;
let l_a_3 : Outer = *p_a_3;
let l_a_3_a : array<Inner, 4> = *p_a_3_a;
let l_a_3_a_2 : Inner = *p_a_3_a_2;
let l_a_3_a_2_m : mat4x2<f32> = *p_a_3_a_2_m;
let l_a_3_a_2_m_1 : vec2<f32> = *p_a_3_a_2_m_1;
let l_a_2_a_0_m_1_0 : f32 = (*p_a_3_a_2_m_1)[0];
}
)";
auto* expect = R"(
struct Inner {
m : mat4x2<f32>,
}
struct Inner_std140 {
m_0 : vec2<f32>,
m_1 : vec2<f32>,
m_2 : vec2<f32>,
m_3 : vec2<f32>,
}
struct Outer {
a : array<Inner, 4>,
}
struct Outer_std140 {
a : array<Inner_std140, 4u>,
}
@group(0) @binding(0) var<uniform> a : array<Outer_std140, 4u>;
fn conv_Inner(val : Inner_std140) -> Inner {
return Inner(mat4x2<f32>(val.m_0, val.m_1, val.m_2, val.m_3));
}
fn conv_arr_4_Inner(val : array<Inner_std140, 4u>) -> array<Inner, 4u> {
var arr : array<Inner, 4u>;
for(var i : u32; (i < 4u); i = (i + 1)) {
arr[i] = conv_Inner(val[i]);
}
return arr;
}
fn conv_Outer(val : Outer_std140) -> Outer {
return Outer(conv_arr_4_Inner(val.a));
}
fn conv_arr_4_Outer(val : array<Outer_std140, 4u>) -> array<Outer, 4u> {
var arr : array<Outer, 4u>;
for(var i : u32; (i < 4u); i = (i + 1)) {
arr[i] = conv_Outer(val[i]);
}
return arr;
}
fn load_a_3_a_2_m() -> mat4x2<f32> {
let s = &(a[3u].a[2u]);
return mat4x2<f32>((*(s)).m_0, (*(s)).m_1, (*(s)).m_2, (*(s)).m_3);
}
fn f() {
let I = 1;
let p_a = conv_arr_4_Outer(a);
let p_a_3 = conv_Outer(a[3u]);
let p_a_3_a = conv_arr_4_Inner(a[3u].a);
let p_a_3_a_2 = conv_Inner(a[3u].a[2u]);
let p_a_3_a_2_m = load_a_3_a_2_m();
let p_a_3_a_2_m_1 = a[3u].a[2u].m_1;
let l_a : array<Outer, 4> = conv_arr_4_Outer(a);
let l_a_3 : Outer = conv_Outer(a[3u]);
let l_a_3_a : array<Inner, 4> = conv_arr_4_Inner(a[3u].a);
let l_a_3_a_2 : Inner = conv_Inner(a[3u].a[2u]);
let l_a_3_a_2_m : mat4x2<f32> = load_a_3_a_2_m();
let l_a_3_a_2_m_1 : vec2<f32> = a[3u].a[2u].m_1;
let l_a_2_a_0_m_1_0 : f32 = a[3u].a[2u].m_1[0u];
}
)";
auto got = Run<Std140>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(Std140Test, ArrayStructMat3x2Uniform_CopyArray_UniformToStorage) {
auto* src = R"(
struct S {
@size(64) m : mat3x2<f32>,
}
@group(0) @binding(0) var<uniform> u : array<S, 4>;
@group(0) @binding(1) var<storage, read_write> s : array<S, 4>;
fn f() {
s = u;
}
)";
auto* expect =
R"(
struct S {
@size(64)
m : mat3x2<f32>,
}
struct S_std140 {
m_0 : vec2<f32>,
m_1 : vec2<f32>,
@size(48)
m_2 : vec2<f32>,
}
@group(0) @binding(0) var<uniform> u : array<S_std140, 4u>;
@group(0) @binding(1) var<storage, read_write> s : array<S, 4>;
fn conv_S(val : S_std140) -> S {
return S(mat3x2<f32>(val.m_0, val.m_1, val.m_2));
}
fn conv_arr_4_S(val : array<S_std140, 4u>) -> array<S, 4u> {
var arr : array<S, 4u>;
for(var i : u32; (i < 4u); i = (i + 1)) {
arr[i] = conv_S(val[i]);
}
return arr;
}
fn f() {
s = conv_arr_4_S(u);
}
)";
auto got = Run<Std140>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(Std140Test, ArrayStructMat3x2Uniform_CopyStruct_UniformToWorkgroup) {
auto* src = R"(
struct S {
v : vec4<i32>,
@size(64) m : mat3x2<f32>,
}
@group(0) @binding(0) var<uniform> u : array<S, 4>;
@group(0) @binding(1) var<workgroup> w : array<S, 4>;
fn f() {
w[0] = u[1];
}
)";
auto* expect =
R"(test:8:38 error: non-resource variables must not have @group or @binding attributes
@group(0) @binding(1) var<workgroup> w : array<S, 4>;
^
)";
auto got = Run<Std140>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(Std140Test, ArrayStructMat3x2Uniform_CopyMatrix_UniformToPrivate) {
auto* src = R"(
struct S {
v : vec4<i32>,
@size(64) m : mat3x2<f32>,
}
@group(0) @binding(0) var<uniform> u : array<S, 4>;
var<private> p : array<S, 4>;
fn f() {
p[2].m = u[1].m;
}
)";
auto* expect = R"(
struct S {
v : vec4<i32>,
@size(64)
m : mat3x2<f32>,
}
struct S_std140 {
v : vec4<i32>,
m_0 : vec2<f32>,
m_1 : vec2<f32>,
@size(48)
m_2 : vec2<f32>,
}
@group(0) @binding(0) var<uniform> u : array<S_std140, 4u>;
var<private> p : array<S, 4>;
fn load_u_1_m() -> mat3x2<f32> {
let s = &(u[1u]);
return mat3x2<f32>((*(s)).m_0, (*(s)).m_1, (*(s)).m_2);
}
fn f() {
p[2].m = load_u_1_m();
}
)";
auto got = Run<Std140>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(Std140Test, ArrayStructMat3x2Uniform_CopyColumn_UniformToStorage) {
auto* src = R"(
struct S {
@size(64) m : mat3x2<f32>,
}
@group(0) @binding(0) var<uniform> u : array<S, 4>;
@group(0) @binding(1) var<storage, read_write> s : array<S, 4>;
fn f() {
s[3].m[1] = u[2].m[0];
}
)";
auto* expect =
R"(
struct S {
@size(64)
m : mat3x2<f32>,
}
struct S_std140 {
m_0 : vec2<f32>,
m_1 : vec2<f32>,
@size(48)
m_2 : vec2<f32>,
}
@group(0) @binding(0) var<uniform> u : array<S_std140, 4u>;
@group(0) @binding(1) var<storage, read_write> s : array<S, 4>;
fn f() {
s[3].m[1] = u[2u].m_0;
}
)";
auto got = Run<Std140>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(Std140Test, ArrayStructMat3x2Uniform_CopySwizzle_UniformToWorkgroup) {
auto* src = R"(
struct S {
@size(64) m : mat3x2<f32>,
}
@group(0) @binding(0) var<uniform> u : array<S, 4>;
var<workgroup> w : array<S, 4>;
fn f() {
w[3].m[1] = u[2].m[0].yx.xy;
}
)";
auto* expect =
R"(
struct S {
@size(64)
m : mat3x2<f32>,
}
struct S_std140 {
m_0 : vec2<f32>,
m_1 : vec2<f32>,
@size(48)
m_2 : vec2<f32>,
}
@group(0) @binding(0) var<uniform> u : array<S_std140, 4u>;
var<workgroup> w : array<S, 4>;
fn f() {
w[3].m[1] = u[2u].m_0.yx.xy;
}
)";
auto got = Run<Std140>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(Std140Test, ArrayStructMat3x2Uniform_CopyScalar_UniformToPrivate) {
auto* src = R"(
struct S {
@size(64) m : mat3x2<f32>,
}
@group(0) @binding(0) var<uniform> u : array<S, 4>;
var<private> w : array<S, 4>;
fn f() {
w[3].m[1].x = u[2].m[0].y;
}
)";
auto* expect =
R"(
struct S {
@size(64)
m : mat3x2<f32>,
}
struct S_std140 {
m_0 : vec2<f32>,
m_1 : vec2<f32>,
@size(48)
m_2 : vec2<f32>,
}
@group(0) @binding(0) var<uniform> u : array<S_std140, 4u>;
var<private> w : array<S, 4>;
fn f() {
w[3].m[1].x = u[2u].m_0[1u];
}
)";
auto got = Run<Std140>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(Std140Test, MatrixUsageInForLoop) {
auto* src = R"(
struct S {
@size(64) m : mat3x2<f32>,
}
@group(0) @binding(0) var<uniform> u : S;
fn f() {
for (var i = u32(u.m[0][0]); i < u32(u.m[i][1]); i += u32(u.m[1][i])) {
}
}
)";
auto* expect =
R"(
struct S {
@size(64)
m : mat3x2<f32>,
}
struct S_std140 {
m_0 : vec2<f32>,
m_1 : vec2<f32>,
@size(48)
m_2 : vec2<f32>,
}
@group(0) @binding(0) var<uniform> u : S_std140;
fn load_u_m_p0_1(p0 : u32) -> f32 {
switch(p0) {
case 0u: {
return u.m_0[1u];
}
case 1u: {
return u.m_1[1u];
}
case 2u: {
return u.m_2[1u];
}
default: {
return f32();
}
}
}
fn f() {
for(var i = u32(u.m_0[0u]); (i < u32(load_u_m_p0_1(u32(i)))); i += u32(u.m_1[i])) {
}
}
)";
auto got = Run<Std140>(src);
EXPECT_EQ(expect, str(got));
}
} // namespace
} // namespace tint::transform