393 lines
10 KiB
C++
393 lines
10 KiB
C++
|
// Copyright 2020 The Tint Authors.
|
||
|
//
|
||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
// you may not use this file except in compliance with the License.
|
||
|
// You may obtain a copy of the License at
|
||
|
//
|
||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||
|
//
|
||
|
// Unless required by applicable law or agreed to in writing, software
|
||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
// See the License for the specific language governing permissions and
|
||
|
// limitations under the License.
|
||
|
|
||
|
#include "src/transform/first_index_offset.h"
|
||
|
|
||
|
#include <memory>
|
||
|
#include <string>
|
||
|
#include <utility>
|
||
|
|
||
|
#include "gtest/gtest.h"
|
||
|
#include "src/ast/block_statement.h"
|
||
|
#include "src/ast/builder.h"
|
||
|
#include "src/ast/builtin.h"
|
||
|
#include "src/ast/builtin_decoration.h"
|
||
|
#include "src/ast/call_expression.h"
|
||
|
#include "src/ast/call_statement.h"
|
||
|
#include "src/ast/decorated_variable.h"
|
||
|
#include "src/ast/function.h"
|
||
|
#include "src/ast/identifier_expression.h"
|
||
|
#include "src/ast/module.h"
|
||
|
#include "src/ast/return_statement.h"
|
||
|
#include "src/ast/storage_class.h"
|
||
|
#include "src/ast/type/u32_type.h"
|
||
|
#include "src/ast/variable.h"
|
||
|
#include "src/ast/variable_decoration.h"
|
||
|
#include "src/diagnostic/formatter.h"
|
||
|
#include "src/source.h"
|
||
|
#include "src/transform/manager.h"
|
||
|
|
||
|
namespace tint {
|
||
|
namespace transform {
|
||
|
namespace {
|
||
|
|
||
|
class FirstIndexOffsetTest : public testing::Test {};
|
||
|
|
||
|
struct ModuleBuilder : public ast::BuilderWithModule {
|
||
|
ast::Module Module() {
|
||
|
Build();
|
||
|
return std::move(*mod);
|
||
|
}
|
||
|
|
||
|
protected:
|
||
|
void AddBuiltinInput(const std::string& name, ast::Builtin builtin) {
|
||
|
auto* var = Var(name, ast::StorageClass::kInput, ty.u32);
|
||
|
auto* dec_var = create<ast::DecoratedVariable>(var);
|
||
|
ast::VariableDecorationList decs;
|
||
|
decs.push_back(create<ast::BuiltinDecoration>(builtin, Source{}));
|
||
|
dec_var->set_decorations(std::move(decs));
|
||
|
mod->AddGlobalVariable(dec_var);
|
||
|
}
|
||
|
|
||
|
ast::Function* AddFunction(const std::string& name,
|
||
|
ast::VariableList params = {}) {
|
||
|
auto* func = create<ast::Function>(Source{}, name, std::move(params),
|
||
|
ty.u32, create<ast::BlockStatement>(),
|
||
|
ast::FunctionDecorationList());
|
||
|
mod->AddFunction(func);
|
||
|
return func;
|
||
|
}
|
||
|
|
||
|
virtual void Build() = 0;
|
||
|
};
|
||
|
|
||
|
TEST_F(FirstIndexOffsetTest, Error_AlreadyTransformed) {
|
||
|
struct Builder : public ModuleBuilder {
|
||
|
void Build() override {
|
||
|
AddBuiltinInput("vert_idx", ast::Builtin::kVertexIdx);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
Manager manager;
|
||
|
manager.append(std::make_unique<FirstIndexOffset>(0, 0));
|
||
|
manager.append(std::make_unique<FirstIndexOffset>(1, 1));
|
||
|
|
||
|
auto module = Builder{}.Module();
|
||
|
auto result = manager.Run(&module);
|
||
|
|
||
|
// Release the source module to ensure there's no uncloned data in result
|
||
|
{ auto tmp = std::move(module); }
|
||
|
|
||
|
ASSERT_EQ(diag::Formatter().format(result.diagnostics),
|
||
|
"error: First index offset transform has already been applied.");
|
||
|
}
|
||
|
|
||
|
TEST_F(FirstIndexOffsetTest, EmptyModule) {
|
||
|
Manager manager;
|
||
|
manager.append(std::make_unique<FirstIndexOffset>(0, 0));
|
||
|
|
||
|
ast::Module module;
|
||
|
auto result = manager.Run(&module);
|
||
|
|
||
|
// Release the source module to ensure there's no uncloned data in result
|
||
|
{ auto tmp = std::move(module); }
|
||
|
|
||
|
ASSERT_FALSE(result.diagnostics.contains_errors())
|
||
|
<< diag::Formatter().format(result.diagnostics);
|
||
|
|
||
|
EXPECT_EQ("Module{\n}\n", result.module.to_str());
|
||
|
}
|
||
|
|
||
|
TEST_F(FirstIndexOffsetTest, BasicModuleVertexIndex) {
|
||
|
struct Builder : public ModuleBuilder {
|
||
|
void Build() override {
|
||
|
AddBuiltinInput("vert_idx", ast::Builtin::kVertexIdx);
|
||
|
ast::Function* func = AddFunction("test");
|
||
|
func->body()->append(create<ast::ReturnStatement>(
|
||
|
Source{}, create<ast::IdentifierExpression>("vert_idx")));
|
||
|
}
|
||
|
};
|
||
|
|
||
|
Manager manager;
|
||
|
manager.append(std::make_unique<FirstIndexOffset>(1, 2));
|
||
|
|
||
|
auto module = Builder{}.Module();
|
||
|
auto result = manager.Run(&module);
|
||
|
|
||
|
// Release the source module to ensure there's no uncloned data in result
|
||
|
{ auto tmp = std::move(module); }
|
||
|
|
||
|
ASSERT_FALSE(result.diagnostics.contains_errors())
|
||
|
<< diag::Formatter().format(result.diagnostics);
|
||
|
|
||
|
EXPECT_EQ(R"(Module{
|
||
|
TintFirstIndexOffsetData Struct{
|
||
|
[[block]]
|
||
|
StructMember{[[ offset 0 ]] tint_first_vertex_index: __u32}
|
||
|
}
|
||
|
DecoratedVariable{
|
||
|
Decorations{
|
||
|
BuiltinDecoration{vertex_idx}
|
||
|
}
|
||
|
tint_first_index_offset_vert_idx
|
||
|
in
|
||
|
__u32
|
||
|
}
|
||
|
DecoratedVariable{
|
||
|
Decorations{
|
||
|
BindingDecoration{1}
|
||
|
SetDecoration{2}
|
||
|
}
|
||
|
tint_first_index_data
|
||
|
uniform
|
||
|
__struct_TintFirstIndexOffsetData
|
||
|
}
|
||
|
Function test -> __u32
|
||
|
()
|
||
|
{
|
||
|
VariableDeclStatement{
|
||
|
VariableConst{
|
||
|
vert_idx
|
||
|
none
|
||
|
__u32
|
||
|
{
|
||
|
Binary[__u32]{
|
||
|
Identifier[__ptr_in__u32]{tint_first_index_offset_vert_idx}
|
||
|
add
|
||
|
MemberAccessor[__ptr_uniform__u32]{
|
||
|
Identifier[__ptr_uniform__struct_TintFirstIndexOffsetData]{tint_first_index_data}
|
||
|
Identifier[not set]{tint_first_vertex_index}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
Return{
|
||
|
{
|
||
|
Identifier[__u32]{vert_idx}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
)",
|
||
|
result.module.to_str());
|
||
|
}
|
||
|
|
||
|
TEST_F(FirstIndexOffsetTest, BasicModuleInstanceIndex) {
|
||
|
struct Builder : public ModuleBuilder {
|
||
|
void Build() override {
|
||
|
AddBuiltinInput("inst_idx", ast::Builtin::kInstanceIdx);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
Manager manager;
|
||
|
manager.append(std::make_unique<FirstIndexOffset>(1, 7));
|
||
|
|
||
|
auto module = Builder{}.Module();
|
||
|
auto result = manager.Run(&module);
|
||
|
|
||
|
// Release the source module to ensure there's no uncloned data in result
|
||
|
{ auto tmp = std::move(module); }
|
||
|
|
||
|
ASSERT_FALSE(result.diagnostics.contains_errors())
|
||
|
<< diag::Formatter().format(result.diagnostics);
|
||
|
EXPECT_EQ(R"(Module{
|
||
|
TintFirstIndexOffsetData Struct{
|
||
|
[[block]]
|
||
|
StructMember{[[ offset 0 ]] tint_first_instance_index: __u32}
|
||
|
}
|
||
|
DecoratedVariable{
|
||
|
Decorations{
|
||
|
BuiltinDecoration{instance_idx}
|
||
|
}
|
||
|
tint_first_index_offset_inst_idx
|
||
|
in
|
||
|
__u32
|
||
|
}
|
||
|
DecoratedVariable{
|
||
|
Decorations{
|
||
|
BindingDecoration{1}
|
||
|
SetDecoration{7}
|
||
|
}
|
||
|
tint_first_index_data
|
||
|
uniform
|
||
|
__struct_TintFirstIndexOffsetData
|
||
|
}
|
||
|
}
|
||
|
)",
|
||
|
result.module.to_str());
|
||
|
}
|
||
|
|
||
|
TEST_F(FirstIndexOffsetTest, BasicModuleBothIndex) {
|
||
|
struct Builder : public ModuleBuilder {
|
||
|
void Build() override {
|
||
|
AddBuiltinInput("inst_idx", ast::Builtin::kInstanceIdx);
|
||
|
AddBuiltinInput("vert_idx", ast::Builtin::kVertexIdx);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
auto transform = std::make_unique<FirstIndexOffset>(1, 7);
|
||
|
auto* transform_ptr = transform.get();
|
||
|
|
||
|
Manager manager;
|
||
|
manager.append(std::move(transform));
|
||
|
|
||
|
auto module = Builder{}.Module();
|
||
|
auto result = manager.Run(&module);
|
||
|
|
||
|
// Release the source module to ensure there's no uncloned data in result
|
||
|
{ auto tmp = std::move(module); }
|
||
|
|
||
|
ASSERT_FALSE(result.diagnostics.contains_errors())
|
||
|
<< diag::Formatter().format(result.diagnostics);
|
||
|
EXPECT_EQ(R"(Module{
|
||
|
TintFirstIndexOffsetData Struct{
|
||
|
[[block]]
|
||
|
StructMember{[[ offset 0 ]] tint_first_vertex_index: __u32}
|
||
|
StructMember{[[ offset 4 ]] tint_first_instance_index: __u32}
|
||
|
}
|
||
|
DecoratedVariable{
|
||
|
Decorations{
|
||
|
BuiltinDecoration{instance_idx}
|
||
|
}
|
||
|
tint_first_index_offset_inst_idx
|
||
|
in
|
||
|
__u32
|
||
|
}
|
||
|
DecoratedVariable{
|
||
|
Decorations{
|
||
|
BuiltinDecoration{vertex_idx}
|
||
|
}
|
||
|
tint_first_index_offset_vert_idx
|
||
|
in
|
||
|
__u32
|
||
|
}
|
||
|
DecoratedVariable{
|
||
|
Decorations{
|
||
|
BindingDecoration{1}
|
||
|
SetDecoration{7}
|
||
|
}
|
||
|
tint_first_index_data
|
||
|
uniform
|
||
|
__struct_TintFirstIndexOffsetData
|
||
|
}
|
||
|
}
|
||
|
)",
|
||
|
result.module.to_str());
|
||
|
|
||
|
EXPECT_TRUE(transform_ptr->HasVertexIndex());
|
||
|
EXPECT_EQ(transform_ptr->GetFirstVertexOffset(), 0u);
|
||
|
|
||
|
EXPECT_TRUE(transform_ptr->HasInstanceIndex());
|
||
|
EXPECT_EQ(transform_ptr->GetFirstInstanceOffset(), 4u);
|
||
|
}
|
||
|
|
||
|
TEST_F(FirstIndexOffsetTest, NestedCalls) {
|
||
|
struct Builder : public ModuleBuilder {
|
||
|
void Build() override {
|
||
|
AddBuiltinInput("vert_idx", ast::Builtin::kVertexIdx);
|
||
|
ast::Function* func1 = AddFunction("func1");
|
||
|
func1->body()->append(create<ast::ReturnStatement>(
|
||
|
Source{}, create<ast::IdentifierExpression>("vert_idx")));
|
||
|
ast::Function* func2 = AddFunction("func2");
|
||
|
func2->body()->append(create<ast::ReturnStatement>(
|
||
|
Source{}, create<ast::CallExpression>(
|
||
|
create<ast::IdentifierExpression>("func1"),
|
||
|
ast::ExpressionList{})));
|
||
|
}
|
||
|
};
|
||
|
|
||
|
auto transform = std::make_unique<FirstIndexOffset>(2, 2);
|
||
|
|
||
|
Manager manager;
|
||
|
manager.append(std::move(transform));
|
||
|
|
||
|
auto module = Builder{}.Module();
|
||
|
auto result = manager.Run(&module);
|
||
|
|
||
|
// Release the source module to ensure there's no uncloned data in result
|
||
|
{ auto tmp = std::move(module); }
|
||
|
|
||
|
ASSERT_FALSE(result.diagnostics.contains_errors())
|
||
|
<< diag::Formatter().format(result.diagnostics);
|
||
|
EXPECT_EQ(R"(Module{
|
||
|
TintFirstIndexOffsetData Struct{
|
||
|
[[block]]
|
||
|
StructMember{[[ offset 0 ]] tint_first_vertex_index: __u32}
|
||
|
}
|
||
|
DecoratedVariable{
|
||
|
Decorations{
|
||
|
BuiltinDecoration{vertex_idx}
|
||
|
}
|
||
|
tint_first_index_offset_vert_idx
|
||
|
in
|
||
|
__u32
|
||
|
}
|
||
|
DecoratedVariable{
|
||
|
Decorations{
|
||
|
BindingDecoration{2}
|
||
|
SetDecoration{2}
|
||
|
}
|
||
|
tint_first_index_data
|
||
|
uniform
|
||
|
__struct_TintFirstIndexOffsetData
|
||
|
}
|
||
|
Function func1 -> __u32
|
||
|
()
|
||
|
{
|
||
|
VariableDeclStatement{
|
||
|
VariableConst{
|
||
|
vert_idx
|
||
|
none
|
||
|
__u32
|
||
|
{
|
||
|
Binary[__u32]{
|
||
|
Identifier[__ptr_in__u32]{tint_first_index_offset_vert_idx}
|
||
|
add
|
||
|
MemberAccessor[__ptr_uniform__u32]{
|
||
|
Identifier[__ptr_uniform__struct_TintFirstIndexOffsetData]{tint_first_index_data}
|
||
|
Identifier[not set]{tint_first_vertex_index}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
Return{
|
||
|
{
|
||
|
Identifier[__u32]{vert_idx}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
Function func2 -> __u32
|
||
|
()
|
||
|
{
|
||
|
Return{
|
||
|
{
|
||
|
Call[__u32]{
|
||
|
Identifier[__u32]{func1}
|
||
|
(
|
||
|
)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
)",
|
||
|
result.module.to_str());
|
||
|
}
|
||
|
|
||
|
} // namespace
|
||
|
} // namespace transform
|
||
|
} // namespace tint
|