blob: 01d2daebf13b8b40a7892b7897e156e81a355b4d [file] [log] [blame]
// 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 {
enum class MatrixType { f32, f16 };
struct MatrixCase {
uint32_t columns;
uint32_t rows;
MatrixType type;
size_t ElementSize() const { return type == MatrixType::f16 ? 2 : 4; }
size_t ColumnVectorAlign() const { return (rows == 3 ? 4 : rows) * ElementSize(); }
bool NotStd140Compatible() const { return ColumnVectorAlign() != 16; }
// Return if this matrix type can be used as element type of an uniform buffer, i.e. the
// array stride is multiple of 16.
bool CanBeUsedAsUniformArrayElememts() const {
const size_t array_stride = columns * ColumnVectorAlign();
return (array_stride % 16 == 0);
}
std::string Shape() const { return std::to_string(columns) + "x" + std::to_string(rows); }
std::string ElementType() const { return type == MatrixType::f16 ? "f16" : "f32"; }
std::string Mat() const { return "mat" + Shape() + "<" + ElementType() + ">"; }
std::string ColumnVector() const {
return "vec" + std::to_string(rows) + "<" + (type == MatrixType::f32 ? "f32" : "f16") + ">";
}
std::string ColumnVectorSwizzle() const {
switch (rows) {
case 2:
return "yx";
case 3:
return "yzx";
case 4:
return "wzxy";
}
return "";
}
// For each column, replaces "${col_id_for_tmpl}" by column index in `tmpl` to get a string, and
// join all these strings with `seperator`. If `tmpl_for_last_column` is not empty, use it
// instead of `tmpl` for the last column.
std::string JoinTemplatedStringForEachMatrixColumn(
std::string tmpl,
std::string seperator,
std::string tmpl_for_last_column = "") const {
std::string result;
if (tmpl_for_last_column.size() == 0) {
tmpl_for_last_column = tmpl;
}
for (size_t c = 0; c < columns - 1; c++) {
if (c > 0) {
result += seperator;
}
std::string string_for_current_column =
utils::ReplaceAll(tmpl, "${col_id_for_tmpl}", std::to_string(c));
result += string_for_current_column;
}
result += seperator;
std::string string_for_last_column = utils::ReplaceAll(
tmpl_for_last_column, "${col_id_for_tmpl}", std::to_string(columns - 1));
result += string_for_last_column;
return result;
}
std::string ExpendedColumnVectors(uint32_t leading_space, std::string name) const {
std::string space(leading_space, ' ');
return JoinTemplatedStringForEachMatrixColumn(
space + name + "${col_id_for_tmpl} : " + ColumnVector() + ",", "\n");
}
std::string ExpendedColumnVectorsInline(std::string name, std::string seperator) const {
return JoinTemplatedStringForEachMatrixColumn(name + "${col_id_for_tmpl}", seperator);
}
std::string ExpendedColumnVectorsWithLastSize(uint32_t leading_space,
std::string name,
uint32_t last_size) const {
std::string space(leading_space, ' ');
return JoinTemplatedStringForEachMatrixColumn(
space + name + "${col_id_for_tmpl} : " + ColumnVector() + ",", "\n",
space + "@size(" + std::to_string(last_size) + ")\n" + space + name +
"${col_id_for_tmpl} : " + ColumnVector() + ",");
}
// Replace user-given fields and predefined fields in a given string `str`.
// First, for each pair of string in `replacement_pairs`, replace all occurrences of the first
// string of pair with second string. Then, replace several predefined fields with the matrix
// information. E.g. for a matrix mat4x3<f32>, would replace "${mat}" with "mat4x3<f32>",
// replace "${shape}" with "4x3", "${elem_type}" with "f32", "${col_vector_type}" with
// "vec3<f32>", and "${swizzle}" with "yzx".
std::string ReplaceFieldsInString(
std::string str,
std::initializer_list<std::pair<std::string, std::string>> replacement_pairs = {}) const {
for (auto& replace : replacement_pairs) {
str = utils::ReplaceAll(str, replace.first, replace.second);
}
str = utils::ReplaceAll(str, "${mat}", Mat());
str = utils::ReplaceAll(str, "${shape}", Shape());
str = utils::ReplaceAll(str, "${elem_type}", ElementType());
str = utils::ReplaceAll(str, "${col_vector_type}", ColumnVector());
str = utils::ReplaceAll(str, "${swizzle}", ColumnVectorSwizzle());
return str;
}
};
inline std::ostream& operator<<(std::ostream& os, const MatrixCase& c) {
return os << c.Mat();
}
using Std140Test_Matrix = TransformTestWithParam<MatrixCase>;
TEST_P(Std140Test_Matrix, SingleStructMatUniform) {
auto matrix = GetParam();
std::string src = R"(
enable f16;
struct S {
m : ${mat},
}
@group(0) @binding(0) var<uniform> s : S;
)";
src = matrix.ReplaceFieldsInString(src);
std::string expect;
if (matrix.NotStd140Compatible()) {
expect = R"(
enable f16;
struct S {
m : ${mat},
}
struct S_std140 {
${col_vectors}
}
@group(0) @binding(0) var<uniform> s : S_std140;
)";
expect = matrix.ReplaceFieldsInString(
expect, {{"${col_vectors}", matrix.ExpendedColumnVectors(2, "m_")}});
} else {
expect = src;
}
auto got = Run<Std140>(src);
EXPECT_EQ(expect, str(got));
}
TEST_P(Std140Test_Matrix, CustomAlign) {
auto matrix = GetParam();
std::string src = R"(
enable f16;
struct S {
before : i32,
@align(128)
m : ${mat},
after : i32,
}
@group(0) @binding(0) var<uniform> s : S;
)";
src = matrix.ReplaceFieldsInString(src);
std::string expect;
if (matrix.NotStd140Compatible()) {
expect = R"(
enable f16;
struct S {
before : i32,
@align(128)
m : ${mat},
after : i32,
}
struct S_std140 {
before : i32,
@align(128i)
${col_vectors}
after : i32,
}
@group(0) @binding(0) var<uniform> s : S_std140;
)";
expect = matrix.ReplaceFieldsInString(
expect, {{"${col_vectors}", matrix.ExpendedColumnVectors(2, "m_")}});
} else {
expect = src;
}
auto got = Run<Std140>(src);
EXPECT_EQ(expect, str(got));
}
TEST_P(Std140Test_Matrix, CustomSizeMat) {
auto matrix = GetParam();
std::string src = R"(
enable f16;
struct S {
before : i32,
@size(128)
m : ${mat},
after : i32,
}
@group(0) @binding(0) var<uniform> s : S;
)";
src = matrix.ReplaceFieldsInString(src);
std::string expect;
if (matrix.NotStd140Compatible()) {
uint32_t last_size =
128 - static_cast<uint32_t>(matrix.ColumnVectorAlign() * (matrix.columns - 1));
expect = R"(
enable f16;
struct S {
before : i32,
@size(128)
m : ${mat},
after : i32,
}
struct S_std140 {
before : i32,
${col_vectors}
after : i32,
}
@group(0) @binding(0) var<uniform> s : S_std140;
)";
expect = matrix.ReplaceFieldsInString(
expect,
{{"${col_vectors}", matrix.ExpendedColumnVectorsWithLastSize(2, "m_", last_size)}});
} else {
expect = src;
}
auto got = Run<Std140>(src);
EXPECT_EQ(expect, str(got));
}
TEST_P(Std140Test_Matrix, CustomAlignAndSize) {
auto matrix = GetParam();
std::string src = R"(
enable f16;
struct S {
before : i32,
@align(128) @size(128)
m : ${mat},
after : i32,
}
@group(0) @binding(0) var<uniform> s : S;
)";
src = matrix.ReplaceFieldsInString(src);
std::string expect;
if (matrix.NotStd140Compatible()) {
uint32_t last_size =
128 - static_cast<uint32_t>(matrix.ColumnVectorAlign() * (matrix.columns - 1));
expect = R"(
enable f16;
struct S {
before : i32,
@align(128) @size(128)
m : ${mat},
after : i32,
}
struct S_std140 {
before : i32,
@align(128i)
${col_vectors}
after : i32,
}
@group(0) @binding(0) var<uniform> s : S_std140;
)";
expect = matrix.ReplaceFieldsInString(
expect,
{{"${col_vectors}", matrix.ExpendedColumnVectorsWithLastSize(2, "m_", last_size)}});
} else {
expect = src;
}
auto got = Run<Std140>(src);
EXPECT_EQ(expect, str(got));
}
TEST_P(Std140Test_Matrix, MatrixUsageInForLoop) {
auto matrix = GetParam();
std::string src = R"(
enable f16;
struct S {
m : ${mat},
}
@group(0) @binding(0) var<uniform> s : S;
fn f() {
for(var i = u32(s.m[0][0]); (i < u32(s.m[i][1])); i += u32(s.m[1][i])) {
}
}
)";
src = matrix.ReplaceFieldsInString(src);
std::string expect;
if (matrix.NotStd140Compatible()) {
expect = R"(
enable f16;
struct S {
m : ${mat},
}
struct S_std140 {
${col_vectors}
}
@group(0) @binding(0) var<uniform> s : S_std140;
fn load_s_m_p0_1(p0 : u32) -> ${elem_type} {
switch(p0) {
${col_table}
default: {
return ${elem_type}();
}
}
}
fn f() {
for(var i = u32(s.m_0[0u]); (i < u32(load_s_m_p0_1(u32(i)))); i += u32(s.m_1[i])) {
}
}
)";
// col_table is the switch cases for all column index.
// Example for a matrix having 2 columns:
// case 0u: {
// return s.m_0[1u];
// }
// case 1u: {
// return s.m_1[1u];
// }
std::string col_table = matrix.JoinTemplatedStringForEachMatrixColumn( //
R"( case ${col_id_for_tmpl}u: {
return s.m_${col_id_for_tmpl}[1u];
})",
"\n");
expect = matrix.ReplaceFieldsInString(
expect, {{"${col_vectors}", matrix.ExpendedColumnVectors(2, "m_")},
{"${col_table}", col_table}});
} else {
expect = src;
}
auto got = Run<Std140>(src);
EXPECT_EQ(expect, str(got));
}
TEST_P(Std140Test_Matrix, MatUniform_LoadMatrix) {
auto matrix = GetParam();
std::string src = R"(
enable f16;
@group(0) @binding(0) var<uniform> m : ${mat};
fn f() {
let l = m;
}
)";
src = matrix.ReplaceFieldsInString(src);
std::string expect;
if (matrix.NotStd140Compatible()) {
expect = R"(
enable f16;
struct mat${shape}_${elem_type} {
${col_vectors}
}
@group(0) @binding(0) var<uniform> m : mat${shape}_${elem_type};
fn conv_mat${shape}_${elem_type}(val : mat${shape}_${elem_type}) -> ${mat} {
return ${mat}(${col_vectors_inline});
}
fn f() {
let l = conv_mat${shape}_${elem_type}(m);
}
)";
expect = matrix.ReplaceFieldsInString(
expect,
{{"${col_vectors}", matrix.ExpendedColumnVectors(2, "col")},
{"${col_vectors_inline}", matrix.ExpendedColumnVectorsInline("val.col", ", ")}});
} else {
expect = src;
}
auto got = Run<Std140>(src);
EXPECT_EQ(expect, str(got));
}
TEST_P(Std140Test_Matrix, MatUniform_LoadColumn_ConstIndex) {
auto matrix = GetParam();
std::string tmpl_src = R"(
enable f16;
@group(0) @binding(0) var<uniform> a : ${mat};
fn f() {
let l = a[${cloumn_index}];
}
)";
tmpl_src = matrix.ReplaceFieldsInString(tmpl_src);
std::string tmpl_expect;
if (matrix.NotStd140Compatible()) {
tmpl_expect = R"(
enable f16;
struct mat${shape}_${elem_type} {
${col_vectors}
}
@group(0) @binding(0) var<uniform> a : mat${shape}_${elem_type};
fn f() {
let l = a.col${cloumn_index};
}
)";
tmpl_expect = matrix.ReplaceFieldsInString(
tmpl_expect, {{"${col_vectors}", matrix.ExpendedColumnVectors(2, "col")}});
} else {
tmpl_expect = tmpl_src;
}
for (uint32_t col = 0; col < matrix.columns; col++) {
std::string src = utils::ReplaceAll(tmpl_src, "${cloumn_index}", std::to_string(col));
std::string expect = utils::ReplaceAll(tmpl_expect, "${cloumn_index}", std::to_string(col));
auto got = Run<Std140>(src);
EXPECT_EQ(expect, str(got)) << "accessing col " << col;
}
}
TEST_P(Std140Test_Matrix, MatUniform_LoadColumn_VariableIndex) {
auto matrix = GetParam();
std::string src = R"(
enable f16;
@group(0) @binding(0) var<uniform> a : ${mat};
fn f() {
let I = 1;
let l = a[I];
}
)";
src = matrix.ReplaceFieldsInString(src);
std::string expect;
if (matrix.NotStd140Compatible()) {
expect = R"(
enable f16;
struct mat${shape}_${elem_type} {
${col_vectors}
}
@group(0) @binding(0) var<uniform> a : mat${shape}_${elem_type};
fn load_a_p0(p0 : u32) -> ${col_vector_type} {
switch(p0) {
${col_table}
default: {
return ${col_vector_type}();
}
}
}
fn f() {
let I = 1;
let l = load_a_p0(u32(I));
}
)";
// col_table is the switch cases for all column index.
// Example for a matrix having 2 columns:
// case 0u: {
// return a.col0;
// }
// case 1u: {
// return a.col1;
// }
std::string col_table = matrix.JoinTemplatedStringForEachMatrixColumn( //
R"( case ${col_id_for_tmpl}u: {
return a.col${col_id_for_tmpl};
})",
"\n");
expect = matrix.ReplaceFieldsInString(
expect, {{"${col_vectors}", matrix.ExpendedColumnVectors(2, "col")},
{"${col_table}", col_table}});
} else {
expect = src;
}
auto got = Run<Std140>(src);
EXPECT_EQ(expect, str(got));
}
TEST_P(Std140Test_Matrix, MatUniform_LoadColumnSwizzle_ConstIndex) {
auto matrix = GetParam();
std::string tmpl_src = R"(
enable f16;
@group(0) @binding(0) var<uniform> a : ${mat};
fn f() {
let l = a[${cloumn_index}].${swizzle};
}
)";
tmpl_src = matrix.ReplaceFieldsInString(tmpl_src);
std::string tmpl_expect;
if (matrix.NotStd140Compatible()) {
tmpl_expect = R"(
enable f16;
struct mat${shape}_${elem_type} {
${col_vectors}
}
@group(0) @binding(0) var<uniform> a : mat${shape}_${elem_type};
fn f() {
let l = a.col${cloumn_index}.${swizzle};
}
)";
tmpl_expect = matrix.ReplaceFieldsInString(
tmpl_expect, {{"${col_vectors}", matrix.ExpendedColumnVectors(2, "col")}});
} else {
tmpl_expect = tmpl_src;
}
for (uint32_t col = 0; col < matrix.columns; col++) {
std::string src = utils::ReplaceAll(tmpl_src, "${cloumn_index}", std::to_string(col));
std::string expect = utils::ReplaceAll(tmpl_expect, "${cloumn_index}", std::to_string(col));
auto got = Run<Std140>(src);
EXPECT_EQ(expect, str(got)) << "accessing col " << col;
}
}
TEST_P(Std140Test_Matrix, MatUniform_LoadColumnSwizzle_VariableIndex) {
auto matrix = GetParam();
std::string src = R"(
enable f16;
@group(0) @binding(0) var<uniform> a : ${mat};
fn f() {
let I = 1;
let l = a[I].${swizzle};
}
)";
src = matrix.ReplaceFieldsInString(src);
std::string expect;
if (matrix.NotStd140Compatible()) {
expect = R"(
enable f16;
struct mat${shape}_${elem_type} {
${col_vectors}
}
@group(0) @binding(0) var<uniform> a : mat${shape}_${elem_type};
fn load_a_p0_${swizzle}(p0 : u32) -> ${col_vector_type} {
switch(p0) {
${col_table}
default: {
return ${col_vector_type}();
}
}
}
fn f() {
let I = 1;
let l = load_a_p0_${swizzle}(u32(I));
}
)";
// col_table is the switch cases for all column index.
// Example for a matrix having 2 columns:
// case 0u: {
// return a.col0.${swizzle};
// }
// case 1u: {
// return a.col1.${swizzle};
// }
std::string col_table = matrix.JoinTemplatedStringForEachMatrixColumn( //
R"( case ${col_id_for_tmpl}u: {
return a.col${col_id_for_tmpl}.${swizzle};
})",
"\n");
expect = matrix.ReplaceFieldsInString(
expect, {{"${col_vectors}", matrix.ExpendedColumnVectors(2, "col")},
{"${col_table}", col_table}});
} else {
expect = src;
}
auto got = Run<Std140>(src);
EXPECT_EQ(expect, str(got));
}
TEST_P(Std140Test_Matrix, MatUniform_LoadScalar_ConstColumnIndex_ConstRowIndex) {
auto matrix = GetParam();
std::string tmpl_src = R"(
enable f16;
@group(0) @binding(0) var<uniform> a : ${mat};
fn f() {
let l = a[${col_index}][${row_index}];
}
)";
tmpl_src = matrix.ReplaceFieldsInString(tmpl_src);
std::string tmpl_expect;
if (matrix.NotStd140Compatible()) {
tmpl_expect = R"(
enable f16;
struct mat${shape}_${elem_type} {
${col_vectors}
}
@group(0) @binding(0) var<uniform> a : mat${shape}_${elem_type};
fn f() {
let l = a.col${col_index}[${row_index}u];
}
)";
tmpl_expect = matrix.ReplaceFieldsInString(
tmpl_expect, {{"${col_vectors}", matrix.ExpendedColumnVectors(2, "col")}});
} else {
tmpl_expect = tmpl_src;
}
for (uint32_t col = 0; col < matrix.columns; col++) {
for (uint32_t row = 0; row < matrix.rows; row++) {
std::string src = utils::ReplaceAll(tmpl_src, "${col_index}", std::to_string(col));
src = utils::ReplaceAll(src, "${row_index}", std::to_string(row));
std::string expect =
utils::ReplaceAll(tmpl_expect, "${col_index}", std::to_string(col));
expect = utils::ReplaceAll(expect, "${row_index}", std::to_string(row));
auto got = Run<Std140>(src);
EXPECT_EQ(expect, str(got)) << "accessing col " << col << " row " << row;
}
}
}
TEST_P(Std140Test_Matrix, MatUniform_LoadScalar_VariableColumnIndex_ConstRowIndex) {
auto matrix = GetParam();
std::string tmpl_src = R"(
enable f16;
@group(0) @binding(0) var<uniform> a : ${mat};
fn f() {
let I = 0;
let l = a[I][${row_index}];
}
)";
tmpl_src = matrix.ReplaceFieldsInString(tmpl_src);
std::string tmpl_expect;
if (matrix.NotStd140Compatible()) {
tmpl_expect = R"(
enable f16;
struct mat${shape}_${elem_type} {
${col_vectors}
}
@group(0) @binding(0) var<uniform> a : mat${shape}_${elem_type};
fn load_a_p0_${row_index}(p0 : u32) -> ${elem_type} {
switch(p0) {
${col_table}
default: {
return ${elem_type}();
}
}
}
fn f() {
let I = 0;
let l = load_a_p0_${row_index}(u32(I));
}
)";
// col_table is the switch cases for all column index.
// Example for a matrix having 2 columns:
// case 0u: {
// return a.col0[${row_index}u];
// }
// case 1u: {
// return a.col1[${row_index}u];
// }
std::string col_table = matrix.JoinTemplatedStringForEachMatrixColumn( //
R"( case ${col_id_for_tmpl}u: {
return a.col${col_id_for_tmpl}[${row_index}u];
})",
"\n");
tmpl_expect = matrix.ReplaceFieldsInString(
tmpl_expect, {{"${col_vectors}", matrix.ExpendedColumnVectors(2, "col")},
{"${col_table}", col_table}});
} else {
tmpl_expect = tmpl_src;
}
for (uint32_t row = 0; row < matrix.rows; row++) {
std::string src = utils::ReplaceAll(tmpl_src, "${row_index}", std::to_string(row));
std::string expect = utils::ReplaceAll(tmpl_expect, "${row_index}", std::to_string(row));
auto got = Run<Std140>(src);
EXPECT_EQ(expect, str(got)) << "accessing row " << row;
}
}
TEST_P(Std140Test_Matrix, MatUniform_LoadScalar_ConstColumnIndex_VariableRowIndex) {
auto matrix = GetParam();
std::string tmpl_src = R"(
enable f16;
@group(0) @binding(0) var<uniform> a : ${mat};
fn f() {
let I = 0;
let l = a[${col_index}][I];
}
)";
tmpl_src = matrix.ReplaceFieldsInString(tmpl_src);
std::string tmpl_expect;
if (matrix.NotStd140Compatible()) {
tmpl_expect = R"(
enable f16;
struct mat${shape}_${elem_type} {
${col_vectors}
}
@group(0) @binding(0) var<uniform> a : mat${shape}_${elem_type};
fn f() {
let I = 0;
let l = a.col${col_index}[I];
}
)";
tmpl_expect = matrix.ReplaceFieldsInString(
tmpl_expect, {{"${col_vectors}", matrix.ExpendedColumnVectors(2, "col")}});
} else {
tmpl_expect = tmpl_src;
}
for (uint32_t col = 0; col < matrix.columns; col++) {
std::string src = utils::ReplaceAll(tmpl_src, "${col_index}", std::to_string(col));
std::string expect = utils::ReplaceAll(tmpl_expect, "${col_index}", std::to_string(col));
auto got = Run<Std140>(src);
EXPECT_EQ(expect, str(got)) << "accessing col " << col;
}
}
TEST_P(Std140Test_Matrix, MatUniform_LoadScalar_VariableColumnIndex_VariableRowIndex) {
auto matrix = GetParam();
std::string src = R"(
enable f16;
@group(0) @binding(0) var<uniform> a : ${mat};
fn f() {
let I = 0;
let l = a[I][I];
}
)";
src = matrix.ReplaceFieldsInString(src);
std::string expect;
if (matrix.NotStd140Compatible()) {
expect = R"(
enable f16;
struct mat${shape}_${elem_type} {
${col_vectors}
}
@group(0) @binding(0) var<uniform> a : mat${shape}_${elem_type};
fn load_a_p0_p1(p0 : u32, p1 : u32) -> ${elem_type} {
switch(p0) {
${col_table}
default: {
return ${elem_type}();
}
}
}
fn f() {
let I = 0;
let l = load_a_p0_p1(u32(I), u32(I));
}
)";
// col_table is the switch cases for all column index.
// Example for a matrix having 2 columns:
// case 0u: {
// return a.col0[p1];
// }
// case 1u: {
// return a.col1[p1];
// }
std::string col_table = matrix.JoinTemplatedStringForEachMatrixColumn( //
R"( case ${col_id_for_tmpl}u: {
return a.col${col_id_for_tmpl}[p1];
})",
"\n");
expect = matrix.ReplaceFieldsInString(
expect, {{"${col_vectors}", matrix.ExpendedColumnVectors(2, "col")},
{"${col_table}", col_table}});
} else {
expect = src;
}
auto got = Run<Std140>(src);
EXPECT_EQ(expect, str(got));
}
TEST_P(Std140Test_Matrix, StructMatUniform_NameCollision) {
auto matrix = GetParam();
std::string src = R"(
enable f16;
struct S {
m_1 : i32,
m : ${mat},
}
@group(0) @binding(0) var<uniform> s : S;
)";
src = matrix.ReplaceFieldsInString(src);
std::string expect;
if (matrix.NotStd140Compatible()) {
expect = R"(
enable f16;
struct S {
m_1 : i32,
m : ${mat},
}
struct S_std140 {
m_1 : i32,
${col_vectors}
}
@group(0) @binding(0) var<uniform> s : S_std140;
)";
expect = matrix.ReplaceFieldsInString(
expect, {{"${col_vectors}", matrix.ExpendedColumnVectors(2, "m__")}});
} else {
expect = src;
}
auto got = Run<Std140>(src);
EXPECT_EQ(expect, str(got));
}
TEST_P(Std140Test_Matrix, StructMatUniform_LoadStruct) {
auto matrix = GetParam();
std::string src = R"(
enable f16;
struct S {
m : ${mat},
}
@group(0) @binding(0) var<uniform> s : S;
fn f() {
let l = s;
}
)";
src = matrix.ReplaceFieldsInString(src);
std::string expect;
if (matrix.NotStd140Compatible()) {
expect = R"(
enable f16;
struct S {
m : ${mat},
}
struct S_std140 {
${col_vectors}
}
@group(0) @binding(0) var<uniform> s : S_std140;
fn conv_S(val : S_std140) -> S {
return S(${mat}(${col_vectors_inline}));
}
fn f() {
let l = conv_S(s);
}
)";
expect = matrix.ReplaceFieldsInString(
expect,
{{"${col_vectors}", matrix.ExpendedColumnVectors(2, "m_")},
{"${col_vectors_inline}", matrix.ExpendedColumnVectorsInline("val.m_", ", ")}});
} else {
expect = src;
}
auto got = Run<Std140>(src);
EXPECT_EQ(expect, str(got));
}
TEST_P(Std140Test_Matrix, StructMatUniform_LoadMatrix) {
auto matrix = GetParam();
std::string src = R"(
enable f16;
struct S {
m : ${mat},
}
@group(0) @binding(0) var<uniform> s : S;
fn f() {
let l = s.m;
}
)";
src = matrix.ReplaceFieldsInString(src);
std::string expect;
if (matrix.NotStd140Compatible()) {
expect = R"(
enable f16;
struct S {
m : ${mat},
}
struct S_std140 {
${col_vectors}
}
@group(0) @binding(0) var<uniform> s : S_std140;
fn load_s_m() -> ${mat} {
let s = &(s);
return ${mat}(${col_vectors_inline});
}
fn f() {
let l = load_s_m();
}
)";
expect = matrix.ReplaceFieldsInString(
expect,
{{"${col_vectors}", matrix.ExpendedColumnVectors(2, "m_")},
{"${col_vectors_inline}", matrix.ExpendedColumnVectorsInline("(*(s)).m_", ", ")}});
} else {
expect = src;
}
auto got = Run<Std140>(src);
EXPECT_EQ(expect, str(got));
}
TEST_P(Std140Test_Matrix, StructMatUniform_LoadColumn_ConstIndex) {
auto matrix = GetParam();
std::string tmpl_src = R"(
enable f16;
struct S {
m : ${mat},
}
@group(0) @binding(0) var<uniform> s : S;
fn f() {
let l = s.m[${col_index}];
}
)";
tmpl_src = matrix.ReplaceFieldsInString(tmpl_src);
std::string tmpl_expect;
if (matrix.NotStd140Compatible()) {
tmpl_expect = R"(
enable f16;
struct S {
m : ${mat},
}
struct S_std140 {
${col_vectors}
}
@group(0) @binding(0) var<uniform> s : S_std140;
fn f() {
let l = s.m_${col_index};
}
)";
tmpl_expect = matrix.ReplaceFieldsInString(
tmpl_expect, {{"${col_vectors}", matrix.ExpendedColumnVectors(2, "m_")}});
} else {
tmpl_expect = tmpl_src;
}
for (uint32_t col = 0; col < matrix.columns; col++) {
std::string src = utils::ReplaceAll(tmpl_src, "${col_index}", std::to_string(col));
std::string expect = utils::ReplaceAll(tmpl_expect, "${col_index}", std::to_string(col));
auto got = Run<Std140>(src);
EXPECT_EQ(expect, str(got)) << "accessing col " << col;
}
}
TEST_P(Std140Test_Matrix, StructMatUniform_LoadColumn_VariableIndex) {
auto matrix = GetParam();
std::string src = R"(
enable f16;
struct S {
m : ${mat},
}
@group(0) @binding(0) var<uniform> s : S;
fn f() {
let I = 0;
let l = s.m[I];
}
)";
src = matrix.ReplaceFieldsInString(src);
std::string expect;
if (matrix.NotStd140Compatible()) {
expect = R"(
enable f16;
struct S {
m : ${mat},
}
struct S_std140 {
${col_vectors}
}
@group(0) @binding(0) var<uniform> s : S_std140;
fn load_s_m_p0(p0 : u32) -> ${col_vector_type} {
switch(p0) {
${col_table}
default: {
return ${col_vector_type}();
}
}
}
fn f() {
let I = 0;
let l = load_s_m_p0(u32(I));
}
)";
// col_table is the switch cases for all column index.
// Example for a matrix having 2 columns:
// case 0u: {
// return s.m_0;
// }
// case 1u: {
// return s.m_1;
// }
std::string col_table = matrix.JoinTemplatedStringForEachMatrixColumn( //
R"( case ${col_id_for_tmpl}u: {
return s.m_${col_id_for_tmpl};
})",
"\n");
expect = matrix.ReplaceFieldsInString(
expect, {{"${col_vector_type}", matrix.ColumnVector()},
{"${col_vectors}", matrix.ExpendedColumnVectors(2, "m_")},
{"${col_table}", col_table}});
} else {
expect = src;
}
auto got = Run<Std140>(src);
EXPECT_EQ(expect, str(got));
}
TEST_P(Std140Test_Matrix, StructMatUniform_LoadScalar_ConstColumnIndex_ConstRowIndex) {
auto matrix = GetParam();
std::string tmpl_src = R"(
enable f16;
struct S {
m : ${mat},
}
@group(0) @binding(0) var<uniform> s : S;
fn f() {
let l = s.m[${col_index}][${row_index}];
}
)";
tmpl_src = matrix.ReplaceFieldsInString(tmpl_src);
std::string tmpl_expect;
if (matrix.NotStd140Compatible()) {
tmpl_expect = R"(
enable f16;
struct S {
m : ${mat},
}
struct S_std140 {
${col_vectors}
}
@group(0) @binding(0) var<uniform> s : S_std140;
fn f() {
let l = s.m_${col_index}[${row_index}u];
}
)";
tmpl_expect = matrix.ReplaceFieldsInString(
tmpl_expect, {{"${col_vectors}", matrix.ExpendedColumnVectors(2, "m_")}});
} else {
tmpl_expect = tmpl_src;
}
for (uint32_t col = 0; col < matrix.columns; col++) {
for (uint32_t row = 0; row < matrix.rows; row++) {
std::string src = utils::ReplaceAll(tmpl_src, "${col_index}", std::to_string(col));
src = utils::ReplaceAll(src, "${row_index}", std::to_string(row));
std::string expect =
utils::ReplaceAll(tmpl_expect, "${col_index}", std::to_string(col));
expect = utils::ReplaceAll(expect, "${row_index}", std::to_string(row));
auto got = Run<Std140>(src);
EXPECT_EQ(expect, str(got)) << "accessing col " << col << " row " << row;
}
}
}
TEST_P(Std140Test_Matrix, StructMatUniform_LoadScalar_VariableColumnIndex_ConstRowIndex) {
auto matrix = GetParam();
std::string tmpl_src = R"(
enable f16;
struct S {
m : ${mat},
}
@group(0) @binding(0) var<uniform> s : S;
fn f() {
let I = 0;
let l = s.m[I][${row_index}];
}
)";
tmpl_src = matrix.ReplaceFieldsInString(tmpl_src);
std::string tmpl_expect;
if (matrix.NotStd140Compatible()) {
tmpl_expect = R"(
enable f16;
struct S {
m : ${mat},
}
struct S_std140 {
${col_vectors}
}
@group(0) @binding(0) var<uniform> s : S_std140;
fn load_s_m_p0_${row_index}(p0 : u32) -> ${elem_type} {
switch(p0) {
${col_table}
default: {
return ${elem_type}();
}
}
}
fn f() {
let I = 0;
let l = load_s_m_p0_${row_index}(u32(I));
}
)";
// col_table is the switch cases for all column index.
// Example for a matrix having 2 columns:
// case 0u: {
// return s.m_0[${row_index}u];
// }
// case 1u: {
// return s.m_1[${row_index}u];
// }
std::string col_table = matrix.JoinTemplatedStringForEachMatrixColumn( //
R"( case ${col_id_for_tmpl}u: {
return s.m_${col_id_for_tmpl}[${row_index}u];
})",
"\n");
tmpl_expect = matrix.ReplaceFieldsInString(
tmpl_expect, {{"${col_vectors}", matrix.ExpendedColumnVectors(2, "m_")},
{"${col_table}", col_table}});
} else {
tmpl_expect = tmpl_src;
}
for (uint32_t row = 0; row < matrix.rows; row++) {
std::string src = utils::ReplaceAll(tmpl_src, "${row_index}", std::to_string(row));
std::string expect = utils::ReplaceAll(tmpl_expect, "${row_index}", std::to_string(row));
auto got = Run<Std140>(src);
EXPECT_EQ(expect, str(got)) << "accessing row " << row;
}
}
TEST_P(Std140Test_Matrix, StructMatUniform_LoadScalar_ConstColumnIndex_VariableRowIndex) {
auto matrix = GetParam();
std::string tmpl_src = R"(
enable f16;
struct S {
m : ${mat},
}
@group(0) @binding(0) var<uniform> s : S;
fn f() {
let I = 0;
let l = s.m[${col_index}][I];
}
)";
tmpl_src = matrix.ReplaceFieldsInString(tmpl_src);
std::string tmpl_expect;
if (matrix.NotStd140Compatible()) {
tmpl_expect = R"(
enable f16;
struct S {
m : ${mat},
}
struct S_std140 {
${col_vectors}
}
@group(0) @binding(0) var<uniform> s : S_std140;
fn f() {
let I = 0;
let l = s.m_${col_index}[I];
}
)";
tmpl_expect = matrix.ReplaceFieldsInString(
tmpl_expect, {{"${col_vectors}", matrix.ExpendedColumnVectors(2, "m_")}});
} else {
tmpl_expect = tmpl_src;
}
for (uint32_t col = 0; col < matrix.columns; col++) {
std::string src = utils::ReplaceAll(tmpl_src, "${col_index}", std::to_string(col));
std::string expect = utils::ReplaceAll(tmpl_expect, "${col_index}", std::to_string(col));
auto got = Run<Std140>(src);
EXPECT_EQ(expect, str(got)) << "accessing col " << col;
}
}
TEST_P(Std140Test_Matrix, StructMatUniform_LoadScalar_VariableColumnIndex_VariableRowIndex) {
auto matrix = GetParam();
std::string src = R"(
enable f16;
struct S {
m : ${mat},
}
@group(0) @binding(0) var<uniform> s : S;
fn f() {
let I = 0;
let l = s.m[I][I];
}
)";
src = matrix.ReplaceFieldsInString(src);
std::string expect;
if (matrix.NotStd140Compatible()) {
expect = R"(
enable f16;
struct S {
m : ${mat},
}
struct S_std140 {
${col_vectors}
}
@group(0) @binding(0) var<uniform> s : S_std140;
fn load_s_m_p0_p1(p0 : u32, p1 : u32) -> ${elem_type} {
switch(p0) {
${col_table}
default: {
return ${elem_type}();
}
}
}
fn f() {
let I = 0;
let l = load_s_m_p0_p1(u32(I), u32(I));
}
)";
// col_table is the switch cases for all column index.
// Example for a matrix having 2 columns:
// case 0u: {
// return s.m_0[p1];
// }
// case 1u: {
// return s.m_1[p1];
// }
std::string col_table = matrix.JoinTemplatedStringForEachMatrixColumn( //
R"( case ${col_id_for_tmpl}u: {
return s.m_${col_id_for_tmpl}[p1];
})",
"\n");
expect = matrix.ReplaceFieldsInString(
expect, {{"${col_vectors}", matrix.ExpendedColumnVectors(2, "m_")},
{"${col_table}", col_table}});
} else {
expect = src;
}
auto got = Run<Std140>(src);
EXPECT_EQ(expect, str(got));
}
TEST_P(Std140Test_Matrix, ArrayStructMatUniform_LoadArray) {
auto matrix = GetParam();
std::string src = R"(
enable f16;
struct S {
@size(64)
m : ${mat},
}
@group(0) @binding(0) var<uniform> a : array<S, 3>;
fn f() {
let l = a;
}
)";
src = matrix.ReplaceFieldsInString(src);
std::string expect;
if (matrix.NotStd140Compatible()) {
expect = R"(
enable f16;
struct S {
@size(64)
m : ${mat},
}
struct S_std140 {
${col_vectors}
}
@group(0) @binding(0) var<uniform> a : array<S_std140, 3u>;
fn conv_S(val : S_std140) -> S {
return S(${mat}(${col_vectors_inline}));
}
fn conv_arr3_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_arr3_S(a);
}
)";
uint32_t last_size =
64 - static_cast<uint32_t>(matrix.ColumnVectorAlign() * (matrix.columns - 1));
expect = matrix.ReplaceFieldsInString(
expect,
{{"${col_vectors}", matrix.ExpendedColumnVectorsWithLastSize(2, "m_", last_size)},
{"${col_vectors_inline}", matrix.ExpendedColumnVectorsInline("val.m_", ", ")}});
} else {
expect = src;
}
auto got = Run<Std140>(src);
EXPECT_EQ(expect, str(got));
}
TEST_P(Std140Test_Matrix, ArrayStructMatUniform_LoadStruct_ConstIndex) {
auto matrix = GetParam();
std::string tmpl_src = R"(
enable f16;
struct S {
@size(64)
m : ${mat},
}
@group(0) @binding(0) var<uniform> a : array<S, 3>;
fn f() {
let l = a[${array_index}];
}
)";
tmpl_src = matrix.ReplaceFieldsInString(tmpl_src);
std::string tmpl_expect;
if (matrix.NotStd140Compatible()) {
tmpl_expect = R"(
enable f16;
struct S {
@size(64)
m : ${mat},
}
struct S_std140 {
${col_vectors}
}
@group(0) @binding(0) var<uniform> a : array<S_std140, 3u>;
fn conv_S(val : S_std140) -> S {
return S(${mat}(${col_vectors_inline}));
}
fn f() {
let l = conv_S(a[${array_index}u]);
}
)";
uint32_t last_size =
64 - static_cast<uint32_t>(matrix.ColumnVectorAlign() * (matrix.columns - 1));
tmpl_expect = matrix.ReplaceFieldsInString(
tmpl_expect,
{{"${col_vectors}", matrix.ExpendedColumnVectorsWithLastSize(2, "m_", last_size)},
{"${col_vectors_inline}", matrix.ExpendedColumnVectorsInline("val.m_", ", ")}});
} else {
tmpl_expect = tmpl_src;
}
for (uint32_t array_index = 0; array_index < 3; array_index++) {
std::string src =
utils::ReplaceAll(tmpl_src, "${array_index}", std::to_string(array_index));
std::string expect =
utils::ReplaceAll(tmpl_expect, "${array_index}", std::to_string(array_index));
auto got = Run<Std140>(src);
EXPECT_EQ(expect, str(got)) << "accessing array element " << array_index;
}
}
TEST_P(Std140Test_Matrix, ArrayStructMatUniform_LoadStruct_VariableIndex) {
auto matrix = GetParam();
std::string src = R"(
enable f16;
struct S {
@size(64)
m : ${mat},
}
@group(0) @binding(0) var<uniform> a : array<S, 3>;
fn f() {
let I = 1;
let l = a[I];
}
)";
src = matrix.ReplaceFieldsInString(src);
std::string expect;
if (matrix.NotStd140Compatible()) {
expect = R"(
enable f16;
struct S {
@size(64)
m : ${mat},
}
struct S_std140 {
${col_vectors}
}
@group(0) @binding(0) var<uniform> a : array<S_std140, 3u>;
fn conv_S(val : S_std140) -> S {
return S(${mat}(${col_vectors_inline}));
}
fn f() {
let I = 1;
let l = conv_S(a[I]);
}
)";
uint32_t last_size =
64 - static_cast<uint32_t>(matrix.ColumnVectorAlign() * (matrix.columns - 1));
expect = matrix.ReplaceFieldsInString(
expect,
{{"${col_vectors}", matrix.ExpendedColumnVectorsWithLastSize(2, "m_", last_size)},
{"${col_vectors_inline}", matrix.ExpendedColumnVectorsInline("val.m_", ", ")}});
} else {
expect = src;
}
auto got = Run<Std140>(src);
EXPECT_EQ(expect, str(got));
}
TEST_P(Std140Test_Matrix, ArrayStructMatUniform_LoadMatrix_ConstArrayIndex) {
auto matrix = GetParam();
std::string tmpl_src = R"(
enable f16;
struct S {
@size(64)
m : ${mat},
}
@group(0) @binding(0) var<uniform> a : array<S, 3>;
fn f() {
let l = a[${array_index}].m;
}
)";
tmpl_src = matrix.ReplaceFieldsInString(tmpl_src);
std::string tmpl_expect;
if (matrix.NotStd140Compatible()) {
tmpl_expect = R"(
enable f16;
struct S {
@size(64)
m : ${mat},
}
struct S_std140 {
${col_vectors}
}
@group(0) @binding(0) var<uniform> a : array<S_std140, 3u>;
fn load_a_${array_index}_m() -> ${mat} {
let s = &(a[${array_index}u]);
return ${mat}(${col_vectors_inline});
}
fn f() {
let l = load_a_${array_index}_m();
}
)";
uint32_t last_size =
64 - static_cast<uint32_t>(matrix.ColumnVectorAlign() * (matrix.columns - 1));
tmpl_expect = matrix.ReplaceFieldsInString(
tmpl_expect,
{{"${col_vectors}", matrix.ExpendedColumnVectorsWithLastSize(2, "m_", last_size)},
{"${col_vectors_inline}", matrix.ExpendedColumnVectorsInline("(*(s)).m_", ", ")}});
} else {
tmpl_expect = tmpl_src;
}
for (uint32_t array_index = 0; array_index < 3; array_index++) {
std::string src =
utils::ReplaceAll(tmpl_src, "${array_index}", std::to_string(array_index));
std::string expect =
utils::ReplaceAll(tmpl_expect, "${array_index}", std::to_string(array_index));
auto got = Run<Std140>(src);
EXPECT_EQ(expect, str(got)) << "accessing array element " << array_index;
}
}
TEST_P(Std140Test_Matrix, ArrayStructMatUniform_LoadMatrix_VariableArrayIndex) {
auto matrix = GetParam();
std::string src = R"(
enable f16;
struct S {
@size(64)
m : ${mat},
}
@group(0) @binding(0) var<uniform> a : array<S, 3>;
fn f() {
let I = 1;
let l = a[I].m;
}
)";
src = matrix.ReplaceFieldsInString(src);
std::string expect;
if (matrix.NotStd140Compatible()) {
expect = R"(
enable f16;
struct S {
@size(64)
m : ${mat},
}
struct S_std140 {
${col_vectors}
}
@group(0) @binding(0) var<uniform> a : array<S_std140, 3u>;
fn load_a_p0_m(p0 : u32) -> ${mat} {
let s = &(a[p0]);
return ${mat}(${col_vectors_inline});
}
fn f() {
let I = 1;
let l = load_a_p0_m(u32(I));
}
)";
uint32_t last_size =
64 - static_cast<uint32_t>(matrix.ColumnVectorAlign() * (matrix.columns - 1));
expect = matrix.ReplaceFieldsInString(
expect,
{{"${col_vectors}", matrix.ExpendedColumnVectorsWithLastSize(2, "m_", last_size)},
{"${col_vectors_inline}", matrix.ExpendedColumnVectorsInline("(*(s)).m_", ", ")}});
} else {
expect = src;
}
auto got = Run<Std140>(src);
EXPECT_EQ(expect, str(got));
}
TEST_P(Std140Test_Matrix, ArrayStructMatUniform_LoadColumn_ConstArrayIndex_ConstColumnIndex) {
auto matrix = GetParam();
std::string tmpl_src = R"(
enable f16;
struct S {
@size(64)
m : ${mat},
}
@group(0) @binding(0) var<uniform> a : array<S, 3>;
fn f() {
let l = a[${array_index}].m[${cloumn_index}];
}
)";
tmpl_src = matrix.ReplaceFieldsInString(tmpl_src);
std::string tmpl_expect;
if (matrix.NotStd140Compatible()) {
tmpl_expect = R"(
enable f16;
struct S {
@size(64)
m : ${mat},
}
struct S_std140 {
${col_vectors}
}
@group(0) @binding(0) var<uniform> a : array<S_std140, 3u>;
fn f() {
let l = a[${array_index}u].m_${cloumn_index};
}
)";
uint32_t last_size =
64 - static_cast<uint32_t>(matrix.ColumnVectorAlign() * (matrix.columns - 1));
tmpl_expect = matrix.ReplaceFieldsInString(
tmpl_expect,
{{"${col_vectors}", matrix.ExpendedColumnVectorsWithLastSize(2, "m_", last_size)}});
} else {
tmpl_expect = tmpl_src;
}
for (uint32_t array_index = 0; array_index < 3; array_index++) {
for (uint32_t col = 0; col < matrix.columns; col++) {
std::string src =
utils::ReplaceAll(tmpl_src, "${array_index}", std::to_string(array_index));
src = utils::ReplaceAll(src, "${cloumn_index}", std::to_string(col));
std::string expect =
utils::ReplaceAll(tmpl_expect, "${array_index}", std::to_string(array_index));
expect = utils::ReplaceAll(expect, "${cloumn_index}", std::to_string(col));
auto got = Run<Std140>(src);
EXPECT_EQ(expect, str(got))
<< "accessing array element " << array_index << " col " << col;
}
}
}
TEST_P(Std140Test_Matrix, ArrayStructMatUniform_LoadColumn_VariableArrayIndex_ConstColumnIndex) {
auto matrix = GetParam();
std::string tmpl_src = R"(
enable f16;
struct S {
@size(64)
m : ${mat},
}
@group(0) @binding(0) var<uniform> a : array<S, 3>;
fn f() {
let I = 1;
let l = a[I].m[${cloumn_index}];
}
)";
tmpl_src = matrix.ReplaceFieldsInString(tmpl_src);
std::string tmpl_expect;
if (matrix.NotStd140Compatible()) {
tmpl_expect = R"(
enable f16;
struct S {
@size(64)
m : ${mat},
}
struct S_std140 {
${col_vectors}
}
@group(0) @binding(0) var<uniform> a : array<S_std140, 3u>;
fn f() {
let I = 1;
let l = a[I].m_${cloumn_index};
}
)";
uint32_t last_size =
64 - static_cast<uint32_t>(matrix.ColumnVectorAlign() * (matrix.columns - 1));
tmpl_expect = matrix.ReplaceFieldsInString(
tmpl_expect,
{{"${col_vectors}", matrix.ExpendedColumnVectorsWithLastSize(2, "m_", last_size)}});
} else {
tmpl_expect = tmpl_src;
}
for (uint32_t col = 0; col < matrix.columns; col++) {
std::string src = utils::ReplaceAll(tmpl_src, "${cloumn_index}", std::to_string(col));
std::string expect = utils::ReplaceAll(tmpl_expect, "${cloumn_index}", std::to_string(col));
auto got = Run<Std140>(src);
EXPECT_EQ(expect, str(got)) << "accessing col " << col;
}
}
TEST_P(Std140Test_Matrix, ArrayStructMatUniform_LoadColumn_ConstArrayIndex_VariableColumnIndex) {
auto matrix = GetParam();
std::string tmpl_src = R"(
enable f16;
struct S {
@size(64)
m : ${mat},
}
@group(0) @binding(0) var<uniform> a : array<S, 3>;
fn f() {
let I = 1;
let l = a[${array_index}].m[I];
}
)";
tmpl_src = matrix.ReplaceFieldsInString(tmpl_src);
std::string tmpl_expect;
if (matrix.NotStd140Compatible()) {
tmpl_expect = R"(
enable f16;
struct S {
@size(64)
m : ${mat},
}
struct S_std140 {
${col_vectors}
}
@group(0) @binding(0) var<uniform> a : array<S_std140, 3u>;
fn load_a_${array_index}_m_p0(p0 : u32) -> ${col_vector_type} {
switch(p0) {
${col_table}
default: {
return ${col_vector_type}();
}
}
}
fn f() {
let I = 1;
let l = load_a_${array_index}_m_p0(u32(I));
}
)";
// col_table is the switch cases for all column index.
// Example for a matrix having 2 columns:
// case 0u: {
// return a[${array_index}u].m_0;
// }
// case 1u: {
// return a[${array_index}u].m_1;
// }
std::string col_table = matrix.JoinTemplatedStringForEachMatrixColumn( //
R"( case ${col_id_for_tmpl}u: {
return a[${array_index}u].m_${col_id_for_tmpl};
})",
"\n");
uint32_t last_size =
64 - static_cast<uint32_t>(matrix.ColumnVectorAlign() * (matrix.columns - 1));
tmpl_expect = matrix.ReplaceFieldsInString(
tmpl_expect,
{{"${col_vectors}", matrix.ExpendedColumnVectorsWithLastSize(2, "m_", last_size)},
{"${col_table}", col_table}});
} else {
tmpl_expect = tmpl_src;
}
for (uint32_t array_index = 0; array_index < 3; array_index++) {
std::string src =
utils::ReplaceAll(tmpl_src, "${array_index}", std::to_string(array_index));
std::string expect =
utils::ReplaceAll(tmpl_expect, "${array_index}", std::to_string(array_index));
auto got = Run<Std140>(src);
EXPECT_EQ(expect, str(got)) << "accessing array element " << array_index;
}
}
TEST_P(Std140Test_Matrix, ArrayStructMatUniform_LoadColumn_VariableArrayIndex_VariableColumnIndex) {
auto matrix = GetParam();
std::string src = R"(
enable f16;
struct S {
@size(64)
m : ${mat},
}
@group(0) @binding(0) var<uniform> a : array<S, 3>;
fn f() {
let I = 1;
let l = a[I].m[I];
}
)";
src = matrix.ReplaceFieldsInString(src);
std::string expect;
if (matrix.NotStd140Compatible()) {
expect = R"(
enable f16;
struct S {
@size(64)
m : ${mat},
}
struct S_std140 {
${col_vectors}
}
@group(0) @binding(0) var<uniform> a : array<S_std140, 3u>;
fn load_a_p0_m_p1(p0 : u32, p1 : u32) -> ${col_vector_type} {
switch(p1) {
${col_table}
default: {
return ${col_vector_type}();
}
}
}
fn f() {
let I = 1;
let l = load_a_p0_m_p1(u32(I), u32(I));
}
)";
// col_table is the switch cases for all column index.
// Example for a matrix having 2 columns:
// case 0u: {
// return a[p0].m_0;
// }
// case 1u: {
// return a[p0].m_1;
// }
std::string col_table = matrix.JoinTemplatedStringForEachMatrixColumn( //
R"( case ${col_id_for_tmpl}u: {
return a[p0].m_${col_id_for_tmpl};
})",
"\n");
uint32_t last_size =
64 - static_cast<uint32_t>(matrix.ColumnVectorAlign() * (matrix.columns - 1));
expect = matrix.ReplaceFieldsInString(
expect,
{{"${col_vectors}", matrix.ExpendedColumnVectorsWithLastSize(2, "m_", last_size)},
{"${col_table}", col_table}});
} else {
expect = src;
}
auto got = Run<Std140>(src);
EXPECT_EQ(expect, str(got));
}
TEST_P(Std140Test_Matrix, ArrayStructArrayStructMatUniform_Loads) {
auto matrix = GetParam();
std::string src = R"(
enable f16;
struct Inner {
@size(64)
m : ${mat},
}
struct Outer {
a : array<Inner, 4>,
}
@group(0) @binding(0) var<uniform> a : array<Outer, 4>;
fn f() {
let I = 1;
let J = 2;
let K = 0;
let l_a : array<Outer, 4> = a;
let l_a_1 : Outer = a[1];
let l_a_I : Outer = a[I];
let l_a_2_a : array<Inner, 4> = a[2].a;
let l_a_I_a : array<Inner, 4> = a[I].a;
let l_a_3_a_1 : Inner = a[3].a[1];
let l_a_3_a_I : Inner = a[3].a[I];
let l_a_I_a_1 : Inner = a[I].a[1];
let l_a_I_a_J : Inner = a[I].a[J];
let l_a_0_a_2_m : ${mat} = a[0].a[2].m;
let l_a_0_a_I_m : ${mat} = a[0].a[I].m;
let l_a_I_a_2_m : ${mat} = a[I].a[2].m;
let l_a_I_a_J_m : ${mat} = a[I].a[J].m;
let l_a_1_a_3_m_0 : ${col_vector_type} = a[1].a[3].m[0];
let l_a_I_a_J_m_K : ${col_vector_type} = a[I].a[J].m[K];
let l_a_2_a_0_m_1_0 : ${elem_type} = a[2].a[0].m[1][0];
let l_a_I_a_J_m_K_I : ${elem_type} = a[I].a[J].m[K][I];
}
)";
src = matrix.ReplaceFieldsInString(src);
std::string expect;
if (matrix.NotStd140Compatible()) {
expect = R"(
enable f16;
struct Inner {
@size(64)
m : ${mat},
}
struct Inner_std140 {
${col_vectors}
}
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(${mat}(${col_vectors_inline_conv_Inner}));
}
fn conv_arr4_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_arr4_Inner(val.a));
}
fn conv_arr4_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() -> ${mat} {
let s = &(a[0u].a[2u]);
return ${mat}(${col_vectors_inline_load_matrix});
}
fn load_a_0_a_p0_m(p0 : u32) -> ${mat} {
let s = &(a[0u].a[p0]);
return ${mat}(${col_vectors_inline_load_matrix});
}
fn load_a_p0_a_2_m(p0 : u32) -> ${mat} {
let s = &(a[p0].a[2u]);
return ${mat}(${col_vectors_inline_load_matrix});
}
fn load_a_p0_a_p1_m(p0 : u32, p1 : u32) -> ${mat} {
let s = &(a[p0].a[p1]);
return ${mat}(${col_vectors_inline_load_matrix});
}
fn load_a_p0_a_p1_m_p2(p0 : u32, p1 : u32, p2 : u32) -> ${col_vector_type} {
switch(p2) {
${col_table_load_column}
default: {
return ${col_vector_type}();
}
}
}
fn load_a_p0_a_p1_m_p2_p3(p0 : u32, p1 : u32, p2 : u32, p3 : u32) -> ${elem_type} {
switch(p2) {
${col_table_load_element}
default: {
return ${elem_type}();
}
}
}
fn f() {
let I = 1;
let J = 2;
let K = 0;
let l_a : array<Outer, 4> = conv_arr4_Outer(a);
let l_a_1 : Outer = conv_Outer(a[1u]);
let l_a_I : Outer = conv_Outer(a[I]);
let l_a_2_a : array<Inner, 4> = conv_arr4_Inner(a[2u].a);
let l_a_I_a : array<Inner, 4> = conv_arr4_Inner(a[I].a);
let l_a_3_a_1 : Inner = conv_Inner(a[3u].a[1u]);
let l_a_3_a_I : Inner = conv_Inner(a[3u].a[I]);
let l_a_I_a_1 : Inner = conv_Inner(a[I].a[1u]);
let l_a_I_a_J : Inner = conv_Inner(a[I].a[J]);
let l_a_0_a_2_m : ${mat} = load_a_0_a_2_m();
let l_a_0_a_I_m : ${mat} = load_a_0_a_p0_m(u32(I));
let l_a_I_a_2_m : ${mat} = load_a_p0_a_2_m(u32(I));
let l_a_I_a_J_m : ${mat} = load_a_p0_a_p1_m(u32(I), u32(J));
let l_a_1_a_3_m_0 : ${col_vector_type} = a[1u].a[3u].m_0;
let l_a_I_a_J_m_K : ${col_vector_type} = load_a_p0_a_p1_m_p2(u32(I), u32(J), u32(K));
let l_a_2_a_0_m_1_0 : ${elem_type} = a[2u].a[0u].m_1[0u];
let l_a_I_a_J_m_K_I : ${elem_type} = load_a_p0_a_p1_m_p2_p3(u32(I), u32(J), u32(K), u32(I));
}
)";
std::string col_tableLoadColumn = matrix.JoinTemplatedStringForEachMatrixColumn( //
R"( case ${col_id_for_tmpl}u: {
return a[p0].a[p1].m_${col_id_for_tmpl};
})",
"\n");
std::string col_tableLoadElement = matrix.JoinTemplatedStringForEachMatrixColumn( //
R"( case ${col_id_for_tmpl}u: {
return a[p0].a[p1].m_${col_id_for_tmpl}[p3];
})",
"\n");
uint32_t last_size =
64 - static_cast<uint32_t>(matrix.ColumnVectorAlign() * (matrix.columns - 1));
expect = matrix.ReplaceFieldsInString(
expect,
{{"${col_vectors}", matrix.ExpendedColumnVectorsWithLastSize(2, "m_", last_size)},
{"${col_vectors_inline_conv_Inner}",
matrix.ExpendedColumnVectorsInline("val.m_", ", ")},
{"${col_vectors_inline_load_matrix}",
matrix.ExpendedColumnVectorsInline("(*(s)).m_", ", ")},
{"${col_table_load_column}", col_tableLoadColumn},
{"${col_table_load_element}", col_tableLoadElement}});
} else {
expect = src;
}
auto got = Run<Std140>(src);
EXPECT_EQ(expect, str(got));
}
TEST_P(Std140Test_Matrix, ArrayStructArrayStructMatUniform_LoadsViaPtrs) {
auto matrix = GetParam();
std::string src = R"(
enable f16;
struct Inner {
@size(64)
m : ${mat},
}
struct Outer {
a : array<Inner, 4>,
}
@group(0) @binding(0) var<uniform> a : array<Outer, 4>;
fn f() {
let I = 1;
let J = 2;
let K = 0;
let p_a = &(a);
let p_a_3 = &((*(p_a))[3]);
let p_a_I = &((*(p_a))[I]);
let p_a_3_a = &((*(p_a_3)).a);
let p_a_I_a = &((*(p_a_I)).a);
let p_a_3_a_2 = &((*(p_a_3_a))[2]);
let p_a_3_a_I = &((*(p_a_3_a))[I]);
let p_a_I_a_2 = &((*(p_a_I_a))[2]);
let p_a_I_a_J = &((*(p_a_I_a))[J]);
let p_a_3_a_2_m = &((*(p_a_3_a_2)).m);
let p_a_3_a_I_m = &((*(p_a_3_a_I)).m);
let p_a_I_a_2_m = &((*(p_a_I_a_2)).m);
let p_a_I_a_J_m = &((*(p_a_I_a_J)).m);
let p_a_3_a_2_m_1 = &((*(p_a_3_a_2_m))[1]);
let p_a_I_a_J_m_K = &((*(p_a_I_a_J_m))[K]);
let l_a : array<Outer, 4> = *(p_a);
let l_a_3 : Outer = *(p_a_3);
let l_a_I : Outer = *(p_a_I);
let l_a_3_a : array<Inner, 4> = *(p_a_3_a);
let l_a_I_a : array<Inner, 4> = *(p_a_I_a);
let l_a_3_a_2 : Inner = *(p_a_3_a_2);
let l_a_3_a_I : Inner = *(p_a_3_a_I);
let l_a_I_a_2 : Inner = *(p_a_I_a_2);
let l_a_I_a_J : Inner = *(p_a_I_a_J);
let l_a_3_a_2_m : ${mat} = *(p_a_3_a_2_m);
let l_a_3_a_I_m : ${mat} = *(p_a_3_a_I_m);
let l_a_I_a_2_m : ${mat} = *(p_a_I_a_2_m);
let l_a_I_a_J_m : ${mat} = *(p_a_I_a_J_m);
let l_a_3_a_2_m_1 : ${col_vector_type} = *(p_a_3_a_2_m_1);
let l_a_I_a_J_m_K : ${col_vector_type} = *(p_a_I_a_J_m_K);
let l_a_2_a_0_m_1_0 : ${elem_type} = (*(p_a_3_a_2_m_1))[0];
let l_a_I_a_J_m_K_I : ${elem_type} = (*(p_a_I_a_J_m_K))[I];
}
)";
src = matrix.ReplaceFieldsInString(src);
std::string expect;
if (matrix.NotStd140Compatible()) {
expect = R"(
enable f16;
struct Inner {
@size(64)
m : ${mat},
}
struct Inner_std140 {
${col_vectors}
}
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(${mat}(${col_vectors_inline_conv_Inner}));
}
fn conv_arr4_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_arr4_Inner(val.a));
}
fn conv_arr4_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() -> ${mat} {
let s = &(a[3u].a[2u]);
return ${mat}(${col_vectors_inline_load_matrix});
}
fn load_a_3_a_p0_m(p0 : u32) -> ${mat} {
let s = &(a[3u].a[p0]);
return ${mat}(${col_vectors_inline_load_matrix});
}
fn load_a_p0_a_2_m(p0 : u32) -> ${mat} {
let s = &(a[p0].a[2u]);
return ${mat}(${col_vectors_inline_load_matrix});
}
fn load_a_p0_a_p1_m(p0 : u32, p1 : u32) -> ${mat} {
let s = &(a[p0].a[p1]);
return ${mat}(${col_vectors_inline_load_matrix});
}
fn load_a_p0_a_p1_m_p2(p0 : u32, p1 : u32, p2 : u32) -> ${col_vector_type} {
switch(p2) {
${col_table_load_column}
default: {
return ${col_vector_type}();
}
}
}
fn load_a_p0_a_p1_m_p2_p3(p0 : u32, p1 : u32, p2 : u32, p3 : u32) -> ${elem_type} {
switch(p2) {
${col_table_load_element}
default: {
return ${elem_type}();
}
}
}
fn f() {
let I = 1;
let J = 2;
let K = 0;
let p_a = conv_arr4_Outer(a);
let p_a_3 = conv_Outer(a[3u]);
let p_a_I = conv_Outer(a[I]);
let p_a_3_a = conv_arr4_Inner(a[3u].a);
let p_a_I_a = conv_arr4_Inner(a[I].a);
let p_a_3_a_2 = conv_Inner(a[3u].a[2u]);
let p_a_3_a_I = conv_Inner(a[3u].a[I]);
let p_a_I_a_2 = conv_Inner(a[I].a[2u]);
let p_a_I_a_J = conv_Inner(a[I].a[J]);
let p_a_3_a_2_m = load_a_3_a_2_m();
let p_a_3_a_I_m = load_a_3_a_p0_m(u32(I));
let p_a_I_a_2_m = load_a_p0_a_2_m(u32(I));
let p_a_I_a_J_m = load_a_p0_a_p1_m(u32(I), u32(J));
let p_a_3_a_2_m_1 = a[3u].a[2u].m_1;
let p_a_I_a_J_m_K = load_a_p0_a_p1_m_p2(u32(I), u32(J), u32(K));
let l_a : array<Outer, 4> = conv_arr4_Outer(a);
let l_a_3 : Outer = conv_Outer(a[3u]);
let l_a_I : Outer = conv_Outer(a[I]);
let l_a_3_a : array<Inner, 4> = conv_arr4_Inner(a[3u].a);
let l_a_I_a : array<Inner, 4> = conv_arr4_Inner(a[I].a);
let l_a_3_a_2 : Inner = conv_Inner(a[3u].a[2u]);
let l_a_3_a_I : Inner = conv_Inner(a[3u].a[I]);
let l_a_I_a_2 : Inner = conv_Inner(a[I].a[2u]);
let l_a_I_a_J : Inner = conv_Inner(a[I].a[J]);
let l_a_3_a_2_m : ${mat} = load_a_3_a_2_m();
let l_a_3_a_I_m : ${mat} = load_a_3_a_p0_m(u32(I));
let l_a_I_a_2_m : ${mat} = load_a_p0_a_2_m(u32(I));
let l_a_I_a_J_m : ${mat} = load_a_p0_a_p1_m(u32(I), u32(J));
let l_a_3_a_2_m_1 : ${col_vector_type} = a[3u].a[2u].m_1;
let l_a_I_a_J_m_K : ${col_vector_type} = load_a_p0_a_p1_m_p2(u32(I), u32(J), u32(K));
let l_a_2_a_0_m_1_0 : ${elem_type} = a[3u].a[2u].m_1[0u];
let l_a_I_a_J_m_K_I : ${elem_type} = load_a_p0_a_p1_m_p2_p3(u32(I), u32(J), u32(K), u32(I));
}
)";
std::string col_tableLoadColumn = matrix.JoinTemplatedStringForEachMatrixColumn( //
R"( case ${col_id_for_tmpl}u: {
return a[p0].a[p1].m_${col_id_for_tmpl};
})",
"\n");
std::string col_tableLoadElement = matrix.JoinTemplatedStringForEachMatrixColumn( //
R"( case ${col_id_for_tmpl}u: {
return a[p0].a[p1].m_${col_id_for_tmpl}[p3];
})",
"\n");
uint32_t last_size =
64 - static_cast<uint32_t>(matrix.ColumnVectorAlign() * (matrix.columns - 1));
expect = matrix.ReplaceFieldsInString(
expect,
{{"${col_vectors}", matrix.ExpendedColumnVectorsWithLastSize(2, "m_", last_size)},
{"${col_vectors_inline_conv_Inner}",
matrix.ExpendedColumnVectorsInline("val.m_", ", ")},
{"${col_vectors_inline_load_matrix}",
matrix.ExpendedColumnVectorsInline("(*(s)).m_", ", ")},
{"${col_table_load_column}", col_tableLoadColumn},
{"${col_table_load_element}", col_tableLoadElement}});
} else {
expect = src;
}
auto got = Run<Std140>(src);
EXPECT_EQ(expect, str(got));
}
TEST_P(Std140Test_Matrix, ArrayStructMatUniform_CopyArray_UniformToStorage) {
auto matrix = GetParam();
std::string src = R"(
enable f16;
struct S {
@size(64)
m : ${mat},
}
@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;
}
)";
src = matrix.ReplaceFieldsInString(src);
std::string expect;
if (matrix.NotStd140Compatible()) {
expect = R"(
enable f16;
struct S {
@size(64)
m : ${mat},
}
struct S_std140 {
${col_vectors}
}
@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(${mat}(${col_vectors_inline}));
}
fn conv_arr4_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_arr4_S(u);
}
)";
uint32_t last_size =
64 - static_cast<uint32_t>(matrix.ColumnVectorAlign() * (matrix.columns - 1));
expect = matrix.ReplaceFieldsInString(
expect,
{{"${col_vectors}", matrix.ExpendedColumnVectorsWithLastSize(2, "m_", last_size)},
{"${col_vectors_inline}", matrix.ExpendedColumnVectorsInline("val.m_", ", ")}});
} else {
expect = src;
}
auto got = Run<Std140>(src);
EXPECT_EQ(expect, str(got));
}
TEST_P(Std140Test_Matrix, ArrayStructMatUniform_CopyStruct_UniformToWorkgroup) {
auto matrix = GetParam();
std::string src = R"(
enable f16;
struct S {
v : vec4<i32>,
@size(64)
m : ${mat},
}
@group(0) @binding(0) var<uniform> u : array<S, 4>;
var<workgroup> w : array<S, 4>;
fn f() {
w[0] = u[1];
}
)";
src = matrix.ReplaceFieldsInString(src);
std::string expect;
if (matrix.NotStd140Compatible()) {
expect = R"(
enable f16;
struct S {
v : vec4<i32>,
@size(64)
m : ${mat},
}
struct S_std140 {
v : vec4<i32>,
${col_vectors}
}
@group(0) @binding(0) var<uniform> u : array<S_std140, 4u>;
var<workgroup> w : array<S, 4>;
fn conv_S(val : S_std140) -> S {
return S(val.v, ${mat}(${col_vectors_inline}));
}
fn f() {
w[0] = conv_S(u[1u]);
}
)";
uint32_t last_size =
64 - static_cast<uint32_t>(matrix.ColumnVectorAlign() * (matrix.columns - 1));
expect = matrix.ReplaceFieldsInString(
expect,
{{"${col_vectors}", matrix.ExpendedColumnVectorsWithLastSize(2, "m_", last_size)},
{"${col_vectors_inline}", matrix.ExpendedColumnVectorsInline("val.m_", ", ")}});
} else {
expect = src;
}
auto got = Run<Std140>(src);
EXPECT_EQ(expect, str(got));
}
TEST_P(Std140Test_Matrix, ArrayStructMatUniform_CopyMatrix_UniformToPrivate) {
auto matrix = GetParam();
std::string src = R"(
enable f16;
struct S {
v : vec4<i32>,
@size(64)
m : ${mat},
}
@group(0) @binding(0) var<uniform> u : array<S, 3>;
var<private> p : array<S, 4>;
fn f() {
p[2].m = u[1].m;
}
)";
src = matrix.ReplaceFieldsInString(src);
std::string expect;
if (matrix.NotStd140Compatible()) {
expect = R"(
enable f16;
struct S {
v : vec4<i32>,
@size(64)
m : ${mat},
}
struct S_std140 {
v : vec4<i32>,
${col_vectors}
}
@group(0) @binding(0) var<uniform> u : array<S_std140, 3u>;
var<private> p : array<S, 4>;
fn load_u_1_m() -> ${mat} {
let s = &(u[1u]);
return ${mat}(${col_vectors_inline});
}
fn f() {
p[2].m = load_u_1_m();
}
)";
uint32_t last_size =
64 - static_cast<uint32_t>(matrix.ColumnVectorAlign() * (matrix.columns - 1));
expect = matrix.ReplaceFieldsInString(
expect,
{{"${col_vectors}", matrix.ExpendedColumnVectorsWithLastSize(2, "m_", last_size)},
{"${col_vectors_inline}", matrix.ExpendedColumnVectorsInline("(*(s)).m_", ", ")}});
} else {
expect = src;
}
auto got = Run<Std140>(src);
EXPECT_EQ(expect, str(got));
}
TEST_P(Std140Test_Matrix, ArrayStructMatUniform_CopyColumn_UniformToStorage) {
auto matrix = GetParam();
std::string src = R"(
enable f16;
struct S {
@size(64)
m : ${mat},
}
@group(0) @binding(0) var<uniform> u : array<S, 3>;
@group(0) @binding(1) var<storage, read_write> s : array<S, 4>;
fn f() {
s[3].m[1] = u[2].m[0];
}
)";
src = matrix.ReplaceFieldsInString(src);
std::string expect;
if (matrix.NotStd140Compatible()) {
expect = R"(
enable f16;
struct S {
@size(64)
m : ${mat},
}
struct S_std140 {
${col_vectors}
}
@group(0) @binding(0) var<uniform> u : array<S_std140, 3u>;
@group(0) @binding(1) var<storage, read_write> s : array<S, 4>;
fn f() {
s[3].m[1] = u[2u].m_0;
}
)";
uint32_t last_size =
64 - static_cast<uint32_t>(matrix.ColumnVectorAlign() * (matrix.columns - 1));
expect = matrix.ReplaceFieldsInString(
expect,
{{"${col_vectors}", matrix.ExpendedColumnVectorsWithLastSize(2, "m_", last_size)}});
} else {
expect = src;
}
auto got = Run<Std140>(src);
EXPECT_EQ(expect, str(got));
}
TEST_P(Std140Test_Matrix, ArrayStructMatUniform_CopyColumnSwizzle_UniformToWorkgroup) {
auto matrix = GetParam();
std::string src = R"(
enable f16;
struct S {
@size(64)
m : ${mat},
}
@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].${swizzle}.${swizzle};
}
)";
src = matrix.ReplaceFieldsInString(src);
std::string expect;
if (matrix.NotStd140Compatible()) {
expect = R"(
enable f16;
struct S {
@size(64)
m : ${mat},
}
struct S_std140 {
${col_vectors}
}
@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.${swizzle}.${swizzle};
}
)";
uint32_t last_size =
64 - static_cast<uint32_t>(matrix.ColumnVectorAlign() * (matrix.columns - 1));
expect = matrix.ReplaceFieldsInString(
expect,
{{"${col_vectors}", matrix.ExpendedColumnVectorsWithLastSize(2, "m_", last_size)}});
} else {
expect = src;
}
auto got = Run<Std140>(src);
EXPECT_EQ(expect, str(got));
}
TEST_P(Std140Test_Matrix, ArrayStructMatUniform_CopyScalar_UniformToPrivate) {
auto matrix = GetParam();
std::string src = R"(
enable f16;
struct S {
v : vec4<i32>,
@size(64)
m : ${mat},
}
@group(0) @binding(0) var<uniform> u : array<S, 3>;
var<private> p : array<S, 4>;
fn f() {
p[3].m[1].x = u[2].m[0].y;
}
)";
src = matrix.ReplaceFieldsInString(src);
std::string expect;
if (matrix.NotStd140Compatible()) {
expect = R"(
enable f16;
struct S {
v : vec4<i32>,
@size(64)
m : ${mat},
}
struct S_std140 {
v : vec4<i32>,
${col_vectors}
}
@group(0) @binding(0) var<uniform> u : array<S_std140, 3u>;
var<private> p : array<S, 4>;
fn f() {
p[3].m[1].x = u[2u].m_0[1u];
}
)";
uint32_t last_size =
64 - static_cast<uint32_t>(matrix.ColumnVectorAlign() * (matrix.columns - 1));
expect = matrix.ReplaceFieldsInString(
expect,
{{"${col_vectors}", matrix.ExpendedColumnVectorsWithLastSize(2, "m_", last_size)},
{"${col_vectors_inline}", matrix.ExpendedColumnVectorsInline("(*(s)).m_", ", ")}});
} else {
expect = src;
}
auto got = Run<Std140>(src);
EXPECT_EQ(expect, str(got));
}
INSTANTIATE_TEST_SUITE_P(,
Std140Test_Matrix,
::testing::ValuesIn(std::vector<MatrixCase>{
{2, 2, MatrixType::f32},
{2, 3, MatrixType::f32},
{2, 4, MatrixType::f32},
{3, 2, MatrixType::f32},
{3, 3, MatrixType::f32},
{3, 4, MatrixType::f32},
{4, 2, MatrixType::f32},
{4, 3, MatrixType::f32},
{4, 4, MatrixType::f32},
}));
using Std140Test_MatrixArray = TransformTestWithParam<MatrixCase>;
TEST_P(Std140Test_MatrixArray, ArrayMatUniform_LoadArray) {
auto matrix = GetParam();
if (!matrix.CanBeUsedAsUniformArrayElememts()) {
// This permutation is invalid, skip the test.
return;
}
std::string src = R"(
enable f16;
@group(0) @binding(0) var<uniform> a : array<${mat}, 3>;
fn f() {
let l = a;
}
)";
src = matrix.ReplaceFieldsInString(src);
std::string expect;
if (matrix.NotStd140Compatible()) {
expect = R"(
enable f16;
struct mat${shape}_${elem_type} {
${col_vectors}
}
@group(0) @binding(0) var<uniform> a : array<mat${shape}_${elem_type}, 3u>;
fn conv_mat${shape}_${elem_type}(val : mat${shape}_${elem_type}) -> ${mat} {
return ${mat}(${col_vectors_inline});
}
fn conv_arr3_mat${shape}_${elem_type}(val : array<mat${shape}_${elem_type}, 3u>) -> array<${mat}, 3u> {
var arr : array<${mat}, 3u>;
for(var i : u32; (i < 3u); i = (i + 1)) {
arr[i] = conv_mat${shape}_${elem_type}(val[i]);
}
return arr;
}
fn f() {
let l = conv_arr3_mat${shape}_${elem_type}(a);
}
)";
expect = matrix.ReplaceFieldsInString(
expect,
{{"${col_vectors}", matrix.ExpendedColumnVectors(2, "col")},
{"${col_vectors_inline}", matrix.ExpendedColumnVectorsInline("val.col", ", ")}});
} else {
expect = src;
}
auto got = Run<Std140>(src);
EXPECT_EQ(expect, str(got));
}
TEST_P(Std140Test_MatrixArray, ArrayMatUniform_LoadMatrix_ConstArrayIndex) {
auto matrix = GetParam();
if (!matrix.CanBeUsedAsUniformArrayElememts()) {
// This permutation is invalid, skip the test.
return;
}
std::string tmpl_src = R"(
enable f16;
@group(0) @binding(0) var<uniform> a : array<${mat}, 3>;
fn f() {
let l = a[${array_index}];
}
)";
tmpl_src = matrix.ReplaceFieldsInString(tmpl_src);
std::string tmpl_expect;
if (matrix.NotStd140Compatible()) {
tmpl_expect = R"(
enable f16;
struct mat${shape}_${elem_type} {
${col_vectors}
}
@group(0) @binding(0) var<uniform> a : array<mat${shape}_${elem_type}, 3u>;
fn conv_mat${shape}_${elem_type}(val : mat${shape}_${elem_type}) -> ${mat} {
return ${mat}(${col_vectors_inline});
}
fn f() {
let l = conv_mat${shape}_${elem_type}(a[${array_index}u]);
}
)";
tmpl_expect = matrix.ReplaceFieldsInString(
tmpl_expect,
{{"${col_vectors}", matrix.ExpendedColumnVectors(2, "col")},
{"${col_vectors_inline}", matrix.ExpendedColumnVectorsInline("val.col", ", ")}});
} else {
tmpl_expect = tmpl_src;
}
for (uint32_t array_index = 0; array_index < 3; array_index++) {
std::string src =
utils::ReplaceAll(tmpl_src, "${array_index}", std::to_string(array_index));
std::string expect =
utils::ReplaceAll(tmpl_expect, "${array_index}", std::to_string(array_index));
auto got = Run<Std140>(src);
EXPECT_EQ(expect, str(got)) << "accessing array element " << array_index;
}
}
TEST_P(Std140Test_MatrixArray, ArrayMatUniform_LoadMatrix_VariableArrayIndex) {
auto matrix = GetParam();
if (!matrix.CanBeUsedAsUniformArrayElememts()) {
// This permutation is invalid, skip the test.
return;
}
std::string src = R"(
enable f16;
@group(0) @binding(0) var<uniform> a : array<${mat}, 3>;
fn f() {
let I = 1;
let l = a[I];
}
)";
src = matrix.ReplaceFieldsInString(src);
std::string expect;
if (matrix.NotStd140Compatible()) {