mirror of
https://github.com/encounter/dawn-cmake.git
synced 2025-06-25 07:53:32 +00:00
writer/msl: Remove legacy shader IO support
Mostly just deleting unneeded code, and a few additional cleanups as a result. Bug: tint:697 Change-Id: I31ceea93feb34994f51a1b6d294a35cf0c127447 Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/55282 Kokoro: Kokoro <noreply+kokoro@google.com> Commit-Queue: James Price <jrprice@google.com> Reviewed-by: Ben Clayton <bclayton@google.com>
This commit is contained in:
parent
a865b375aa
commit
5eadb83a3f
@ -896,7 +896,6 @@ if(${TINT_BUILD_TESTS})
|
|||||||
writer/msl/generator_impl_constructor_test.cc
|
writer/msl/generator_impl_constructor_test.cc
|
||||||
writer/msl/generator_impl_continue_test.cc
|
writer/msl/generator_impl_continue_test.cc
|
||||||
writer/msl/generator_impl_discard_test.cc
|
writer/msl/generator_impl_discard_test.cc
|
||||||
writer/msl/generator_impl_function_entry_point_data_test.cc
|
|
||||||
writer/msl/generator_impl_function_test.cc
|
writer/msl/generator_impl_function_test.cc
|
||||||
writer/msl/generator_impl_identifier_test.cc
|
writer/msl/generator_impl_identifier_test.cc
|
||||||
writer/msl/generator_impl_if_test.cc
|
writer/msl/generator_impl_if_test.cc
|
||||||
|
@ -57,11 +57,6 @@ namespace writer {
|
|||||||
namespace msl {
|
namespace msl {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
const char kInStructNameSuffix[] = "in";
|
|
||||||
const char kOutStructNameSuffix[] = "out";
|
|
||||||
const char kTintStructInVarPrefix[] = "_tint_in";
|
|
||||||
const char kTintStructOutVarPrefix[] = "_tint_out";
|
|
||||||
|
|
||||||
bool last_is_break_or_fallthrough(const ast::BlockStatement* stmts) {
|
bool last_is_break_or_fallthrough(const ast::BlockStatement* stmts) {
|
||||||
if (stmts->empty()) {
|
if (stmts->empty()) {
|
||||||
return false;
|
return false;
|
||||||
@ -82,11 +77,6 @@ bool GeneratorImpl::Generate() {
|
|||||||
out_ << "#include <metal_stdlib>" << std::endl << std::endl;
|
out_ << "#include <metal_stdlib>" << std::endl << std::endl;
|
||||||
out_ << "using namespace metal;" << std::endl;
|
out_ << "using namespace metal;" << std::endl;
|
||||||
|
|
||||||
for (auto* global : program_->AST().GlobalVariables()) {
|
|
||||||
auto* sem = program_->Sem().Get(global);
|
|
||||||
global_variables_.set(global->symbol(), sem);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto* const type_decl : program_->AST().TypeDecls()) {
|
for (auto* const type_decl : program_->AST().TypeDecls()) {
|
||||||
if (!type_decl->Is<ast::Alias>()) {
|
if (!type_decl->Is<ast::Alias>()) {
|
||||||
if (!EmitTypeDecl(TypeOf(type_decl))) {
|
if (!EmitTypeDecl(TypeOf(type_decl))) {
|
||||||
@ -120,31 +110,16 @@ bool GeneratorImpl::Generate() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure all entry point data is emitted before the entry point functions
|
|
||||||
for (auto* func : program_->AST().Functions()) {
|
for (auto* func : program_->AST().Functions()) {
|
||||||
if (!func->IsEntryPoint()) {
|
if (!func->IsEntryPoint()) {
|
||||||
continue;
|
if (!EmitFunction(func)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!EmitEntryPointFunction(func)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!EmitEntryPointData(func)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto* func : program_->AST().Functions()) {
|
|
||||||
if (!EmitFunction(func)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto* func : program_->AST().Functions()) {
|
|
||||||
if (!func->IsEntryPoint()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!EmitEntryPointFunction(func)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
out_ << std::endl;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -312,27 +287,6 @@ bool GeneratorImpl::EmitBreak(ast::BreakStatement*) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string GeneratorImpl::current_ep_var_name(VarType type) {
|
|
||||||
std::string name = "";
|
|
||||||
switch (type) {
|
|
||||||
case VarType::kIn: {
|
|
||||||
auto in_it = ep_sym_to_in_data_.find(current_ep_sym_);
|
|
||||||
if (in_it != ep_sym_to_in_data_.end()) {
|
|
||||||
name = in_it->second.var_name;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case VarType::kOut: {
|
|
||||||
auto out_it = ep_sym_to_out_data_.find(current_ep_sym_);
|
|
||||||
if (out_it != ep_sym_to_out_data_.end()) {
|
|
||||||
name = out_it->second.var_name;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GeneratorImpl::EmitCall(ast::CallExpression* expr) {
|
bool GeneratorImpl::EmitCall(ast::CallExpression* expr) {
|
||||||
auto* ident = expr->func();
|
auto* ident = expr->func();
|
||||||
auto* call = program_->Sem().Get(expr);
|
auto* call = program_->Sem().Get(expr);
|
||||||
@ -340,14 +294,6 @@ bool GeneratorImpl::EmitCall(ast::CallExpression* expr) {
|
|||||||
return EmitIntrinsicCall(expr, intrinsic);
|
return EmitIntrinsicCall(expr, intrinsic);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto name = program_->Symbols().NameFor(ident->symbol());
|
|
||||||
auto caller_sym = ident->symbol();
|
|
||||||
auto it = ep_func_name_remapped_.find(current_ep_sym_.to_str() + "_" +
|
|
||||||
caller_sym.to_str());
|
|
||||||
if (it != ep_func_name_remapped_.end()) {
|
|
||||||
name = it->second;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto* func = program_->AST().Functions().Find(ident->symbol());
|
auto* func = program_->AST().Functions().Find(ident->symbol());
|
||||||
if (func == nullptr) {
|
if (func == nullptr) {
|
||||||
diagnostics_.add_error("Unable to find function: " +
|
diagnostics_.add_error("Unable to find function: " +
|
||||||
@ -355,40 +301,10 @@ bool GeneratorImpl::EmitCall(ast::CallExpression* expr) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
out_ << name << "(";
|
out_ << program_->Symbols().NameFor(ident->symbol()) << "(";
|
||||||
|
|
||||||
bool first = true;
|
bool first = true;
|
||||||
if (has_referenced_in_var_needing_struct(func)) {
|
|
||||||
auto var_name = current_ep_var_name(VarType::kIn);
|
|
||||||
if (!var_name.empty()) {
|
|
||||||
out_ << var_name;
|
|
||||||
first = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (has_referenced_out_var_needing_struct(func)) {
|
|
||||||
auto var_name = current_ep_var_name(VarType::kOut);
|
|
||||||
if (!var_name.empty()) {
|
|
||||||
if (!first) {
|
|
||||||
out_ << ", ";
|
|
||||||
}
|
|
||||||
first = false;
|
|
||||||
out_ << var_name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto* func_sem = program_->Sem().Get(func);
|
auto* func_sem = program_->Sem().Get(func);
|
||||||
for (const auto& data : func_sem->ReferencedBuiltinVariables()) {
|
|
||||||
auto* var = data.first;
|
|
||||||
if (var->StorageClass() != ast::StorageClass::kInput) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!first) {
|
|
||||||
out_ << ", ";
|
|
||||||
}
|
|
||||||
first = false;
|
|
||||||
out_ << program_->Symbols().NameFor(var->Declaration()->symbol());
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto& data : func_sem->ReferencedUniformVariables()) {
|
for (const auto& data : func_sem->ReferencedUniformVariables()) {
|
||||||
auto* var = data.first;
|
auto* var = data.first;
|
||||||
if (!first) {
|
if (!first) {
|
||||||
@ -1061,127 +977,6 @@ bool GeneratorImpl::EmitLiteral(ast::Literal* lit) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(crbug.com/tint/697): Remove this when we remove support for entry point
|
|
||||||
// params as module-scope globals.
|
|
||||||
bool GeneratorImpl::EmitEntryPointData(ast::Function* func) {
|
|
||||||
auto* func_sem = program_->Sem().Get(func);
|
|
||||||
|
|
||||||
std::vector<std::pair<const ast::Variable*, uint32_t>> in_locations;
|
|
||||||
std::vector<std::pair<const ast::Variable*, ast::Decoration*>> out_variables;
|
|
||||||
|
|
||||||
for (auto data : func_sem->ReferencedLocationVariables()) {
|
|
||||||
auto* var = data.first;
|
|
||||||
auto* deco = data.second;
|
|
||||||
|
|
||||||
if (var->StorageClass() == ast::StorageClass::kInput) {
|
|
||||||
in_locations.push_back({var->Declaration(), deco->value()});
|
|
||||||
} else if (var->StorageClass() == ast::StorageClass::kOutput) {
|
|
||||||
out_variables.push_back({var->Declaration(), deco});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto data : func_sem->ReferencedBuiltinVariables()) {
|
|
||||||
auto* var = data.first;
|
|
||||||
auto* deco = data.second;
|
|
||||||
|
|
||||||
if (var->StorageClass() == ast::StorageClass::kOutput) {
|
|
||||||
out_variables.push_back({var->Declaration(), deco});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!in_locations.empty()) {
|
|
||||||
auto in_struct_name =
|
|
||||||
program_->Symbols().NameFor(func->symbol()) + "_" + kInStructNameSuffix;
|
|
||||||
auto* in_var_name = kTintStructInVarPrefix;
|
|
||||||
ep_sym_to_in_data_[func->symbol()] = {in_struct_name, in_var_name};
|
|
||||||
|
|
||||||
make_indent();
|
|
||||||
out_ << "struct " << in_struct_name << " {" << std::endl;
|
|
||||||
|
|
||||||
increment_indent();
|
|
||||||
|
|
||||||
for (auto& data : in_locations) {
|
|
||||||
auto* var = data.first;
|
|
||||||
uint32_t loc = data.second;
|
|
||||||
|
|
||||||
make_indent();
|
|
||||||
if (!EmitType(program_->Sem().Get(var)->Type()->UnwrapRef(),
|
|
||||||
program_->Symbols().NameFor(var->symbol()))) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
out_ << " " << program_->Symbols().NameFor(var->symbol()) << " [[";
|
|
||||||
if (func->pipeline_stage() == ast::PipelineStage::kVertex) {
|
|
||||||
out_ << "attribute(" << loc << ")";
|
|
||||||
} else if (func->pipeline_stage() == ast::PipelineStage::kFragment) {
|
|
||||||
out_ << "user(locn" << loc << ")";
|
|
||||||
} else {
|
|
||||||
diagnostics_.add_error("invalid location variable for pipeline stage");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
out_ << "]];" << std::endl;
|
|
||||||
}
|
|
||||||
decrement_indent();
|
|
||||||
make_indent();
|
|
||||||
|
|
||||||
out_ << "};" << std::endl << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!out_variables.empty()) {
|
|
||||||
auto out_struct_name = program_->Symbols().NameFor(func->symbol()) + "_" +
|
|
||||||
kOutStructNameSuffix;
|
|
||||||
auto* out_var_name = kTintStructOutVarPrefix;
|
|
||||||
ep_sym_to_out_data_[func->symbol()] = {out_struct_name, out_var_name};
|
|
||||||
|
|
||||||
make_indent();
|
|
||||||
out_ << "struct " << out_struct_name << " {" << std::endl;
|
|
||||||
|
|
||||||
increment_indent();
|
|
||||||
for (auto& data : out_variables) {
|
|
||||||
auto* var = data.first;
|
|
||||||
auto* deco = data.second;
|
|
||||||
|
|
||||||
make_indent();
|
|
||||||
if (!EmitType(program_->Sem().Get(var)->Type()->UnwrapRef(),
|
|
||||||
program_->Symbols().NameFor(var->symbol()))) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
out_ << " " << program_->Symbols().NameFor(var->symbol()) << " [[";
|
|
||||||
|
|
||||||
if (auto* location = deco->As<ast::LocationDecoration>()) {
|
|
||||||
auto loc = location->value();
|
|
||||||
if (func->pipeline_stage() == ast::PipelineStage::kVertex) {
|
|
||||||
out_ << "user(locn" << loc << ")";
|
|
||||||
} else if (func->pipeline_stage() == ast::PipelineStage::kFragment) {
|
|
||||||
out_ << "color(" << loc << ")";
|
|
||||||
} else {
|
|
||||||
diagnostics_.add_error(
|
|
||||||
"invalid location variable for pipeline stage");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else if (auto* builtin = deco->As<ast::BuiltinDecoration>()) {
|
|
||||||
auto attr = builtin_to_attribute(builtin->value());
|
|
||||||
if (attr.empty()) {
|
|
||||||
diagnostics_.add_error("unsupported builtin");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
out_ << attr;
|
|
||||||
} else {
|
|
||||||
diagnostics_.add_error(
|
|
||||||
"unsupported variable decoration for entry point output");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
out_ << "]];" << std::endl;
|
|
||||||
}
|
|
||||||
decrement_indent();
|
|
||||||
make_indent();
|
|
||||||
out_ << "};" << std::endl << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GeneratorImpl::EmitExpression(ast::Expression* expr) {
|
bool GeneratorImpl::EmitExpression(ast::Expression* expr) {
|
||||||
if (auto* a = expr->As<ast::ArrayAccessorExpression>()) {
|
if (auto* a = expr->As<ast::ArrayAccessorExpression>()) {
|
||||||
return EmitArrayAccessor(a);
|
return EmitArrayAccessor(a);
|
||||||
@ -1229,139 +1024,17 @@ void GeneratorImpl::EmitStage(ast::PipelineStage stage) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GeneratorImpl::has_referenced_in_var_needing_struct(ast::Function* func) {
|
|
||||||
auto* func_sem = program_->Sem().Get(func);
|
|
||||||
for (auto data : func_sem->ReferencedLocationVariables()) {
|
|
||||||
auto* var = data.first;
|
|
||||||
if (var->StorageClass() == ast::StorageClass::kInput) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GeneratorImpl::has_referenced_out_var_needing_struct(ast::Function* func) {
|
|
||||||
auto* func_sem = program_->Sem().Get(func);
|
|
||||||
|
|
||||||
for (auto data : func_sem->ReferencedLocationVariables()) {
|
|
||||||
auto* var = data.first;
|
|
||||||
if (var->StorageClass() == ast::StorageClass::kOutput) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto data : func_sem->ReferencedBuiltinVariables()) {
|
|
||||||
auto* var = data.first;
|
|
||||||
if (var->StorageClass() == ast::StorageClass::kOutput) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GeneratorImpl::has_referenced_var_needing_struct(ast::Function* func) {
|
|
||||||
return has_referenced_in_var_needing_struct(func) ||
|
|
||||||
has_referenced_out_var_needing_struct(func);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GeneratorImpl::EmitFunction(ast::Function* func) {
|
bool GeneratorImpl::EmitFunction(ast::Function* func) {
|
||||||
auto* func_sem = program_->Sem().Get(func);
|
|
||||||
|
|
||||||
make_indent();
|
make_indent();
|
||||||
|
|
||||||
// Entry points will be emitted later, skip for now.
|
|
||||||
if (func->IsEntryPoint()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(dsinclair): This could be smarter. If the input/outputs for multiple
|
|
||||||
// entry points are the same we could generate a single struct and then have
|
|
||||||
// this determine it's the same struct and just emit once.
|
|
||||||
bool emit_duplicate_functions = func_sem->AncestorEntryPoints().size() > 0 &&
|
|
||||||
has_referenced_var_needing_struct(func);
|
|
||||||
|
|
||||||
if (emit_duplicate_functions) {
|
|
||||||
for (const auto& ep_sym : func_sem->AncestorEntryPoints()) {
|
|
||||||
if (!EmitFunctionInternal(func, emit_duplicate_functions, ep_sym)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
out_ << std::endl;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Emit as non-duplicated
|
|
||||||
if (!EmitFunctionInternal(func, false, Symbol())) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
out_ << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GeneratorImpl::EmitFunctionInternal(ast::Function* func,
|
|
||||||
bool emit_duplicate_functions,
|
|
||||||
Symbol ep_sym) {
|
|
||||||
auto* func_sem = program_->Sem().Get(func);
|
auto* func_sem = program_->Sem().Get(func);
|
||||||
|
|
||||||
auto name = func->symbol().to_str();
|
|
||||||
if (!EmitType(func_sem->ReturnType(), "")) {
|
if (!EmitType(func_sem->ReturnType(), "")) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
out_ << " " << program_->Symbols().NameFor(func->symbol()) << "(";
|
||||||
out_ << " ";
|
|
||||||
if (emit_duplicate_functions) {
|
|
||||||
auto func_name = name;
|
|
||||||
auto ep_name = ep_sym.to_str();
|
|
||||||
name = program_->Symbols().NameFor(func->symbol()) + "_" +
|
|
||||||
program_->Symbols().NameFor(ep_sym);
|
|
||||||
ep_func_name_remapped_[ep_name + "_" + func_name] = name;
|
|
||||||
} else {
|
|
||||||
name = program_->Symbols().NameFor(func->symbol());
|
|
||||||
}
|
|
||||||
out_ << name << "(";
|
|
||||||
|
|
||||||
bool first = true;
|
bool first = true;
|
||||||
|
|
||||||
// If we're emitting duplicate functions that means the function takes
|
|
||||||
// the stage_in or stage_out value from the entry point, emit them.
|
|
||||||
//
|
|
||||||
// We emit both of them if they're there regardless of if they're both used.
|
|
||||||
if (emit_duplicate_functions) {
|
|
||||||
auto in_it = ep_sym_to_in_data_.find(ep_sym);
|
|
||||||
if (in_it != ep_sym_to_in_data_.end()) {
|
|
||||||
out_ << "thread " << in_it->second.struct_name << "& "
|
|
||||||
<< in_it->second.var_name;
|
|
||||||
first = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto out_it = ep_sym_to_out_data_.find(ep_sym);
|
|
||||||
if (out_it != ep_sym_to_out_data_.end()) {
|
|
||||||
if (!first) {
|
|
||||||
out_ << ", ";
|
|
||||||
}
|
|
||||||
out_ << "thread " << out_it->second.struct_name << "& "
|
|
||||||
<< out_it->second.var_name;
|
|
||||||
first = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto& data : func_sem->ReferencedBuiltinVariables()) {
|
|
||||||
auto* var = data.first;
|
|
||||||
if (var->StorageClass() != ast::StorageClass::kInput) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!first) {
|
|
||||||
out_ << ", ";
|
|
||||||
}
|
|
||||||
first = false;
|
|
||||||
|
|
||||||
out_ << "thread ";
|
|
||||||
if (!EmitType(var->Type()->UnwrapRef(), "")) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
out_ << "& " << program_->Symbols().NameFor(var->Declaration()->symbol());
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto& data : func_sem->ReferencedUniformVariables()) {
|
for (const auto& data : func_sem->ReferencedUniformVariables()) {
|
||||||
auto* var = data.first;
|
auto* var = data.first;
|
||||||
if (!first) {
|
if (!first) {
|
||||||
@ -1416,13 +1089,11 @@ bool GeneratorImpl::EmitFunctionInternal(ast::Function* func,
|
|||||||
|
|
||||||
out_ << ") ";
|
out_ << ") ";
|
||||||
|
|
||||||
current_ep_sym_ = ep_sym;
|
|
||||||
|
|
||||||
if (!EmitBlockAndNewline(func->body())) {
|
if (!EmitBlockAndNewline(func->body())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
current_ep_sym_ = Symbol();
|
out_ << std::endl;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -1462,37 +1133,12 @@ bool GeneratorImpl::EmitEntryPointFunction(ast::Function* func) {
|
|||||||
|
|
||||||
make_indent();
|
make_indent();
|
||||||
|
|
||||||
current_ep_sym_ = func->symbol();
|
|
||||||
|
|
||||||
EmitStage(func->pipeline_stage());
|
EmitStage(func->pipeline_stage());
|
||||||
out_ << " ";
|
out_ << " " << func->return_type()->FriendlyName(program_->Symbols());
|
||||||
|
|
||||||
// This is an entry point, the return type is the entry point output structure
|
|
||||||
// if one exists, or void otherwise.
|
|
||||||
auto out_data = ep_sym_to_out_data_.find(current_ep_sym_);
|
|
||||||
bool has_out_data = out_data != ep_sym_to_out_data_.end();
|
|
||||||
if (has_out_data) {
|
|
||||||
// TODO(crbug.com/tint/697): Remove this.
|
|
||||||
if (!func->return_type()->Is<ast::Void>()) {
|
|
||||||
TINT_ICE(diagnostics_) << "Mixing module-scope variables and return "
|
|
||||||
"types for shader outputs";
|
|
||||||
}
|
|
||||||
out_ << out_data->second.struct_name;
|
|
||||||
} else {
|
|
||||||
out_ << func->return_type()->FriendlyName(program_->Symbols());
|
|
||||||
}
|
|
||||||
out_ << " " << program_->Symbols().NameFor(func->symbol()) << "(";
|
out_ << " " << program_->Symbols().NameFor(func->symbol()) << "(";
|
||||||
|
|
||||||
bool first = true;
|
|
||||||
// TODO(crbug.com/tint/697): Remove this.
|
|
||||||
auto in_data = ep_sym_to_in_data_.find(current_ep_sym_);
|
|
||||||
if (in_data != ep_sym_to_in_data_.end()) {
|
|
||||||
out_ << in_data->second.struct_name << " " << in_data->second.var_name
|
|
||||||
<< " [[stage_in]]";
|
|
||||||
first = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Emit entry point parameters.
|
// Emit entry point parameters.
|
||||||
|
bool first = true;
|
||||||
for (auto* var : func->params()) {
|
for (auto* var : func->params()) {
|
||||||
if (!first) {
|
if (!first) {
|
||||||
out_ << ", ";
|
out_ << ", ";
|
||||||
@ -1549,33 +1195,6 @@ bool GeneratorImpl::EmitEntryPointFunction(ast::Function* func) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(crbug.com/tint/697): Remove this.
|
|
||||||
for (auto data : func_sem->ReferencedBuiltinVariables()) {
|
|
||||||
auto* var = data.first;
|
|
||||||
if (var->StorageClass() != ast::StorageClass::kInput) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!first) {
|
|
||||||
out_ << ", ";
|
|
||||||
}
|
|
||||||
first = false;
|
|
||||||
|
|
||||||
auto* builtin = data.second;
|
|
||||||
|
|
||||||
if (!EmitType(var->Type()->UnwrapRef(), "")) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto attr = builtin_to_attribute(builtin->value());
|
|
||||||
if (attr.empty()) {
|
|
||||||
diagnostics_.add_error("unknown builtin");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
out_ << " " << program_->Symbols().NameFor(var->Declaration()->symbol())
|
|
||||||
<< " [[" << attr << "]]";
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto data : func_sem->ReferencedUniformVariables()) {
|
for (auto data : func_sem->ReferencedUniformVariables()) {
|
||||||
if (!first) {
|
if (!first) {
|
||||||
out_ << ", ";
|
out_ << ", ";
|
||||||
@ -1634,13 +1253,6 @@ bool GeneratorImpl::EmitEntryPointFunction(ast::Function* func) {
|
|||||||
|
|
||||||
increment_indent();
|
increment_indent();
|
||||||
|
|
||||||
if (has_out_data) {
|
|
||||||
make_indent();
|
|
||||||
out_ << out_data->second.struct_name << " " << out_data->second.var_name
|
|
||||||
<< " = {};" << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
generating_entry_point_ = true;
|
|
||||||
for (auto* s : *func->body()) {
|
for (auto* s : *func->body()) {
|
||||||
if (!EmitStatement(s)) {
|
if (!EmitStatement(s)) {
|
||||||
return false;
|
return false;
|
||||||
@ -1654,49 +1266,15 @@ bool GeneratorImpl::EmitEntryPointFunction(ast::Function* func) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
generating_entry_point_ = false;
|
|
||||||
|
|
||||||
decrement_indent();
|
decrement_indent();
|
||||||
make_indent();
|
make_indent();
|
||||||
out_ << "}" << std::endl;
|
out_ << "}" << std::endl << std::endl;
|
||||||
|
|
||||||
current_ep_sym_ = Symbol();
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GeneratorImpl::global_is_in_struct(const sem::Variable* var) const {
|
|
||||||
auto& decorations = var->Declaration()->decorations();
|
|
||||||
bool in_or_out_struct_has_location =
|
|
||||||
var != nullptr &&
|
|
||||||
ast::HasDecoration<ast::LocationDecoration>(decorations) &&
|
|
||||||
(var->StorageClass() == ast::StorageClass::kInput ||
|
|
||||||
var->StorageClass() == ast::StorageClass::kOutput);
|
|
||||||
bool in_struct_has_builtin =
|
|
||||||
var != nullptr &&
|
|
||||||
ast::HasDecoration<ast::BuiltinDecoration>(decorations) &&
|
|
||||||
var->StorageClass() == ast::StorageClass::kOutput;
|
|
||||||
return in_or_out_struct_has_location || in_struct_has_builtin;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GeneratorImpl::EmitIdentifier(ast::IdentifierExpression* expr) {
|
bool GeneratorImpl::EmitIdentifier(ast::IdentifierExpression* expr) {
|
||||||
auto* ident = expr->As<ast::IdentifierExpression>();
|
out_ << program_->Symbols().NameFor(expr->symbol());
|
||||||
const sem::Variable* var = nullptr;
|
|
||||||
if (global_variables_.get(ident->symbol(), &var)) {
|
|
||||||
if (global_is_in_struct(var)) {
|
|
||||||
auto var_type = var->StorageClass() == ast::StorageClass::kInput
|
|
||||||
? VarType::kIn
|
|
||||||
: VarType::kOut;
|
|
||||||
auto name = current_ep_var_name(var_type);
|
|
||||||
if (name.empty()) {
|
|
||||||
diagnostics_.add_error("unable to find entry point data for variable");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
out_ << name << ".";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
out_ << program_->Symbols().NameFor(ident->symbol());
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1865,14 +1443,6 @@ bool GeneratorImpl::EmitReturn(ast::ReturnStatement* stmt) {
|
|||||||
make_indent();
|
make_indent();
|
||||||
|
|
||||||
out_ << "return";
|
out_ << "return";
|
||||||
|
|
||||||
// TODO(crbug.com/tint/697): Remove this conditional.
|
|
||||||
if (generating_entry_point_) {
|
|
||||||
auto out_data = ep_sym_to_out_data_.find(current_ep_sym_);
|
|
||||||
if (out_data != ep_sym_to_out_data_.end()) {
|
|
||||||
out_ << " " << out_data->second.var_name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (stmt->has_value()) {
|
if (stmt->has_value()) {
|
||||||
out_ << " ";
|
out_ << " ";
|
||||||
if (!EmitExpression(stmt->value())) {
|
if (!EmitExpression(stmt->value())) {
|
||||||
@ -2363,8 +1933,7 @@ bool GeneratorImpl::EmitVariable(const sem::Variable* var,
|
|||||||
}
|
}
|
||||||
} else if (var->StorageClass() == ast::StorageClass::kPrivate ||
|
} else if (var->StorageClass() == ast::StorageClass::kPrivate ||
|
||||||
var->StorageClass() == ast::StorageClass::kFunction ||
|
var->StorageClass() == ast::StorageClass::kFunction ||
|
||||||
var->StorageClass() == ast::StorageClass::kNone ||
|
var->StorageClass() == ast::StorageClass::kNone) {
|
||||||
var->StorageClass() == ast::StorageClass::kOutput) {
|
|
||||||
out_ << " = ";
|
out_ << " = ";
|
||||||
if (!EmitZeroValue(type)) {
|
if (!EmitZeroValue(type)) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -16,7 +16,6 @@
|
|||||||
#define SRC_WRITER_MSL_GENERATOR_IMPL_H_
|
#define SRC_WRITER_MSL_GENERATOR_IMPL_H_
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_map>
|
|
||||||
|
|
||||||
#include "src/ast/array_accessor_expression.h"
|
#include "src/ast/array_accessor_expression.h"
|
||||||
#include "src/ast/assignment_statement.h"
|
#include "src/ast/assignment_statement.h"
|
||||||
@ -133,10 +132,6 @@ class GeneratorImpl : public TextGenerator {
|
|||||||
/// @param stmt the statement to emit
|
/// @param stmt the statement to emit
|
||||||
/// @returns true if the statement was emitted
|
/// @returns true if the statement was emitted
|
||||||
bool EmitElse(ast::ElseStatement* stmt);
|
bool EmitElse(ast::ElseStatement* stmt);
|
||||||
/// Handles emitting information for an entry point
|
|
||||||
/// @param func the entry point function
|
|
||||||
/// @returns true if the entry point data was emitted
|
|
||||||
bool EmitEntryPointData(ast::Function* func);
|
|
||||||
/// Handles emitting the entry point function
|
/// Handles emitting the entry point function
|
||||||
/// @param func the entry point function
|
/// @param func the entry point function
|
||||||
/// @returns true if the entry point function was emitted
|
/// @returns true if the entry point function was emitted
|
||||||
@ -149,15 +144,6 @@ class GeneratorImpl : public TextGenerator {
|
|||||||
/// @param func the function to generate
|
/// @param func the function to generate
|
||||||
/// @returns true if the function was emitted
|
/// @returns true if the function was emitted
|
||||||
bool EmitFunction(ast::Function* func);
|
bool EmitFunction(ast::Function* func);
|
||||||
/// Internal helper for emitting functions
|
|
||||||
/// @param func the function to emit
|
|
||||||
/// @param emit_duplicate_functions set true if we need to duplicate per entry
|
|
||||||
/// point
|
|
||||||
/// @param ep_sym the current entry point or symbol::kInvalid if not set
|
|
||||||
/// @returns true if the function was emitted.
|
|
||||||
bool EmitFunctionInternal(ast::Function* func,
|
|
||||||
bool emit_duplicate_functions,
|
|
||||||
Symbol ep_sym);
|
|
||||||
/// Handles generating an identifier expression
|
/// Handles generating an identifier expression
|
||||||
/// @param expr the identifier expression
|
/// @param expr the identifier expression
|
||||||
/// @returns true if the identifier was emitted
|
/// @returns true if the identifier was emitted
|
||||||
@ -235,44 +221,17 @@ class GeneratorImpl : public TextGenerator {
|
|||||||
/// @returns true if the zero value was successfully emitted.
|
/// @returns true if the zero value was successfully emitted.
|
||||||
bool EmitZeroValue(const sem::Type* type);
|
bool EmitZeroValue(const sem::Type* type);
|
||||||
|
|
||||||
/// Determines if the function needs the input struct passed to it.
|
|
||||||
/// @param func the function to check
|
|
||||||
/// @returns true if there are input struct variables used in the function
|
|
||||||
bool has_referenced_in_var_needing_struct(ast::Function* func);
|
|
||||||
/// Determines if the function needs the output struct passed to it.
|
|
||||||
/// @param func the function to check
|
|
||||||
/// @returns true if there are output struct variables used in the function
|
|
||||||
bool has_referenced_out_var_needing_struct(ast::Function* func);
|
|
||||||
/// Determines if any used module variable requires an input or output struct.
|
|
||||||
/// @param func the function to check
|
|
||||||
/// @returns true if an input or output struct is required.
|
|
||||||
bool has_referenced_var_needing_struct(ast::Function* func);
|
|
||||||
|
|
||||||
/// Handles generating a builtin name
|
/// Handles generating a builtin name
|
||||||
/// @param intrinsic the semantic info for the intrinsic
|
/// @param intrinsic the semantic info for the intrinsic
|
||||||
/// @returns the name or "" if not valid
|
/// @returns the name or "" if not valid
|
||||||
std::string generate_builtin_name(const sem::Intrinsic* intrinsic);
|
std::string generate_builtin_name(const sem::Intrinsic* intrinsic);
|
||||||
|
|
||||||
/// Checks if the global variable is in an input or output struct
|
|
||||||
/// @param var the variable to check
|
|
||||||
/// @returns true if the global is in an input or output struct
|
|
||||||
bool global_is_in_struct(const sem::Variable* var) const;
|
|
||||||
|
|
||||||
/// Converts a builtin to an attribute name
|
/// Converts a builtin to an attribute name
|
||||||
/// @param builtin the builtin to convert
|
/// @param builtin the builtin to convert
|
||||||
/// @returns the string name of the builtin or blank on error
|
/// @returns the string name of the builtin or blank on error
|
||||||
std::string builtin_to_attribute(ast::Builtin builtin) const;
|
std::string builtin_to_attribute(ast::Builtin builtin) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
enum class VarType { kIn, kOut };
|
|
||||||
|
|
||||||
struct EntryPointData {
|
|
||||||
std::string struct_name;
|
|
||||||
std::string var_name;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::string current_ep_var_name(VarType type);
|
|
||||||
|
|
||||||
/// @returns the resolved type of the ast::Expression `expr`
|
/// @returns the resolved type of the ast::Expression `expr`
|
||||||
/// @param expr the expression
|
/// @param expr the expression
|
||||||
sem::Type* TypeOf(ast::Expression* expr) const {
|
sem::Type* TypeOf(ast::Expression* expr) const {
|
||||||
@ -301,19 +260,8 @@ class GeneratorImpl : public TextGenerator {
|
|||||||
/// type.
|
/// type.
|
||||||
SizeAndAlign MslPackedTypeSizeAndAlign(const sem::Type* ty);
|
SizeAndAlign MslPackedTypeSizeAndAlign(const sem::Type* ty);
|
||||||
|
|
||||||
ScopeStack<const sem::Variable*> global_variables_;
|
|
||||||
Symbol current_ep_sym_;
|
|
||||||
bool generating_entry_point_ = false;
|
|
||||||
const Program* program_ = nullptr;
|
const Program* program_ = nullptr;
|
||||||
uint32_t loop_emission_counter_ = 0;
|
uint32_t loop_emission_counter_ = 0;
|
||||||
|
|
||||||
std::unordered_map<Symbol, EntryPointData> ep_sym_to_in_data_;
|
|
||||||
std::unordered_map<Symbol, EntryPointData> ep_sym_to_out_data_;
|
|
||||||
|
|
||||||
// This maps an input of "<entry_point_name>_<function_name>" to a remapped
|
|
||||||
// function name. If there is no entry for a given key then function did
|
|
||||||
// not need to be remapped for the entry point and can be emitted directly.
|
|
||||||
std::unordered_map<std::string, std::string> ep_func_name_remapped_;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace msl
|
} // namespace msl
|
||||||
|
@ -42,8 +42,8 @@ TEST_F(MslGeneratorImplTest, EmitExpression_Call_WithParams) {
|
|||||||
Param(Sym(), ty.f32()),
|
Param(Sym(), ty.f32()),
|
||||||
},
|
},
|
||||||
ty.void_(), ast::StatementList{}, ast::DecorationList{});
|
ty.void_(), ast::StatementList{}, ast::DecorationList{});
|
||||||
Global("param1", ty.f32(), ast::StorageClass::kInput);
|
Global("param1", ty.f32(), ast::StorageClass::kPrivate);
|
||||||
Global("param2", ty.f32(), ast::StorageClass::kInput);
|
Global("param2", ty.f32(), ast::StorageClass::kPrivate);
|
||||||
|
|
||||||
auto* call = Call("my_func", "param1", "param2");
|
auto* call = Call("my_func", "param1", "param2");
|
||||||
WrapInFunction(call);
|
WrapInFunction(call);
|
||||||
@ -61,8 +61,8 @@ TEST_F(MslGeneratorImplTest, EmitStatement_Call) {
|
|||||||
Param(Sym(), ty.f32()),
|
Param(Sym(), ty.f32()),
|
||||||
},
|
},
|
||||||
ty.void_(), ast::StatementList{}, ast::DecorationList{});
|
ty.void_(), ast::StatementList{}, ast::DecorationList{});
|
||||||
Global("param1", ty.f32(), ast::StorageClass::kInput);
|
Global("param1", ty.f32(), ast::StorageClass::kPrivate);
|
||||||
Global("param2", ty.f32(), ast::StorageClass::kInput);
|
Global("param2", ty.f32(), ast::StorageClass::kPrivate);
|
||||||
|
|
||||||
auto* call = Call("my_func", "param1", "param2");
|
auto* call = Call("my_func", "param1", "param2");
|
||||||
auto* stmt = create<ast::CallStatement>(call);
|
auto* stmt = create<ast::CallStatement>(call);
|
||||||
|
@ -1,271 +0,0 @@
|
|||||||
// 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/ast/stage_decoration.h"
|
|
||||||
#include "src/writer/msl/test_helper.h"
|
|
||||||
|
|
||||||
namespace tint {
|
|
||||||
namespace writer {
|
|
||||||
namespace msl {
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
using MslGeneratorImplTest = TestHelper;
|
|
||||||
|
|
||||||
TEST_F(MslGeneratorImplTest, Emit_Function_EntryPointData_Vertex_Input) {
|
|
||||||
// [[location 0]] var<in> foo : f32;
|
|
||||||
// [[location 1]] var<in> bar : i32;
|
|
||||||
//
|
|
||||||
// struct vtx_main_in {
|
|
||||||
// float foo [[attribute(0)]];
|
|
||||||
// int bar [[attribute(1)]];
|
|
||||||
// };
|
|
||||||
|
|
||||||
Global("foo", ty.f32(), ast::StorageClass::kInput, nullptr,
|
|
||||||
ast::DecorationList{Location(0)});
|
|
||||||
|
|
||||||
Global("bar", ty.i32(), ast::StorageClass::kInput, nullptr,
|
|
||||||
ast::DecorationList{Location(1)});
|
|
||||||
|
|
||||||
auto body = ast::StatementList{
|
|
||||||
Assign("foo", "foo"),
|
|
||||||
Assign("bar", "bar"),
|
|
||||||
Return(Construct(ty.vec4<f32>())),
|
|
||||||
};
|
|
||||||
|
|
||||||
Func("vtx_main", ast::VariableList{}, ty.vec4<f32>(), body,
|
|
||||||
{Stage(ast::PipelineStage::kVertex)},
|
|
||||||
{Builtin(ast::Builtin::kPosition)});
|
|
||||||
|
|
||||||
GeneratorImpl& gen = Build();
|
|
||||||
|
|
||||||
auto* func = program->AST().Functions()[0];
|
|
||||||
ASSERT_TRUE(gen.EmitEntryPointData(func)) << gen.error();
|
|
||||||
EXPECT_EQ(gen.result(), R"(struct vtx_main_in {
|
|
||||||
float foo [[attribute(0)]];
|
|
||||||
int bar [[attribute(1)]];
|
|
||||||
};
|
|
||||||
|
|
||||||
)");
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(MslGeneratorImplTest, Emit_Function_EntryPointData_Vertex_Output) {
|
|
||||||
// [[location 0]] var<out> foo : f32;
|
|
||||||
// [[location 1]] var<out> bar : i32;
|
|
||||||
//
|
|
||||||
// struct vtx_main_out {
|
|
||||||
// float foo [[user(locn0)]];
|
|
||||||
// int bar [[user(locn1)]];
|
|
||||||
// };
|
|
||||||
|
|
||||||
Global("foo", ty.f32(), ast::StorageClass::kOutput, nullptr,
|
|
||||||
ast::DecorationList{Location(0)});
|
|
||||||
|
|
||||||
Global("bar", ty.i32(), ast::StorageClass::kOutput, nullptr,
|
|
||||||
ast::DecorationList{Location(1)});
|
|
||||||
|
|
||||||
auto body = ast::StatementList{
|
|
||||||
Assign("foo", "foo"),
|
|
||||||
Assign("bar", "bar"),
|
|
||||||
Return(Construct(ty.vec4<f32>())),
|
|
||||||
};
|
|
||||||
|
|
||||||
Func("vtx_main", ast::VariableList{}, ty.vec4<f32>(), body,
|
|
||||||
{Stage(ast::PipelineStage::kVertex)},
|
|
||||||
{Builtin(ast::Builtin::kPosition)});
|
|
||||||
|
|
||||||
GeneratorImpl& gen = Build();
|
|
||||||
|
|
||||||
auto* func = program->AST().Functions()[0];
|
|
||||||
ASSERT_TRUE(gen.EmitEntryPointData(func)) << gen.error();
|
|
||||||
EXPECT_EQ(gen.result(), R"(struct vtx_main_out {
|
|
||||||
float foo [[user(locn0)]];
|
|
||||||
int bar [[user(locn1)]];
|
|
||||||
};
|
|
||||||
|
|
||||||
)");
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(MslGeneratorImplTest, Emit_Function_EntryPointData_Fragment_Input) {
|
|
||||||
// [[location 0]] var<in> foo : f32;
|
|
||||||
// [[location 1]] var<in> bar : i32;
|
|
||||||
//
|
|
||||||
// struct frag_main_in {
|
|
||||||
// float foo [[user(locn0)]];
|
|
||||||
// int bar [[user(locn1)]];
|
|
||||||
// };
|
|
||||||
|
|
||||||
Global("foo", ty.f32(), ast::StorageClass::kInput, nullptr,
|
|
||||||
ast::DecorationList{Location(0)});
|
|
||||||
|
|
||||||
Global("bar", ty.i32(), ast::StorageClass::kInput, nullptr,
|
|
||||||
ast::DecorationList{Location(1)});
|
|
||||||
|
|
||||||
auto body = ast::StatementList{
|
|
||||||
Assign("foo", "foo"),
|
|
||||||
Assign("bar", "bar"),
|
|
||||||
};
|
|
||||||
|
|
||||||
Func("main", ast::VariableList{}, ty.void_(), body,
|
|
||||||
ast::DecorationList{
|
|
||||||
Stage(ast::PipelineStage::kFragment),
|
|
||||||
});
|
|
||||||
|
|
||||||
GeneratorImpl& gen = Build();
|
|
||||||
|
|
||||||
auto* func = program->AST().Functions()[0];
|
|
||||||
ASSERT_TRUE(gen.EmitEntryPointData(func)) << gen.error();
|
|
||||||
EXPECT_EQ(gen.result(), R"(struct main_in {
|
|
||||||
float foo [[user(locn0)]];
|
|
||||||
int bar [[user(locn1)]];
|
|
||||||
};
|
|
||||||
|
|
||||||
)");
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(MslGeneratorImplTest, Emit_Function_EntryPointData_Fragment_Output) {
|
|
||||||
// [[location 0]] var<out> foo : f32;
|
|
||||||
// [[location 1]] var<out> bar : i32;
|
|
||||||
//
|
|
||||||
// struct frag_main_out {
|
|
||||||
// float foo [[color(0)]];
|
|
||||||
// int bar [[color(1)]];
|
|
||||||
// };
|
|
||||||
|
|
||||||
Global("foo", ty.f32(), ast::StorageClass::kOutput, nullptr,
|
|
||||||
ast::DecorationList{Location(0)});
|
|
||||||
|
|
||||||
Global("bar", ty.i32(), ast::StorageClass::kOutput, nullptr,
|
|
||||||
ast::DecorationList{Location(1)});
|
|
||||||
|
|
||||||
auto body = ast::StatementList{
|
|
||||||
Assign("foo", "foo"),
|
|
||||||
Assign("bar", "bar"),
|
|
||||||
};
|
|
||||||
|
|
||||||
Func("main", ast::VariableList{}, ty.void_(), body,
|
|
||||||
ast::DecorationList{
|
|
||||||
Stage(ast::PipelineStage::kFragment),
|
|
||||||
});
|
|
||||||
|
|
||||||
GeneratorImpl& gen = Build();
|
|
||||||
|
|
||||||
auto* func = program->AST().Functions()[0];
|
|
||||||
ASSERT_TRUE(gen.EmitEntryPointData(func)) << gen.error();
|
|
||||||
EXPECT_EQ(gen.result(), R"(struct main_out {
|
|
||||||
float foo [[color(0)]];
|
|
||||||
int bar [[color(1)]];
|
|
||||||
};
|
|
||||||
|
|
||||||
)");
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(MslGeneratorImplTest, Emit_Function_EntryPointData_Compute_Input) {
|
|
||||||
// [[location 0]] var<in> foo : f32;
|
|
||||||
// [[location 1]] var<in> bar : i32;
|
|
||||||
//
|
|
||||||
// -> Error, not allowed
|
|
||||||
|
|
||||||
Global("foo", ty.f32(), ast::StorageClass::kInput, nullptr,
|
|
||||||
ast::DecorationList{Location(0)});
|
|
||||||
|
|
||||||
Global("bar", ty.i32(), ast::StorageClass::kInput, nullptr,
|
|
||||||
ast::DecorationList{Location(1)});
|
|
||||||
|
|
||||||
auto body = ast::StatementList{
|
|
||||||
Assign("foo", "foo"),
|
|
||||||
Assign("bar", "bar"),
|
|
||||||
};
|
|
||||||
|
|
||||||
Func("main", ast::VariableList{}, ty.void_(), body,
|
|
||||||
ast::DecorationList{
|
|
||||||
Stage(ast::PipelineStage::kCompute),
|
|
||||||
});
|
|
||||||
|
|
||||||
GeneratorImpl& gen = Build();
|
|
||||||
|
|
||||||
auto* func = program->AST().Functions()[0];
|
|
||||||
ASSERT_FALSE(gen.EmitEntryPointData(func)) << gen.error();
|
|
||||||
EXPECT_EQ(gen.error(),
|
|
||||||
R"(error: invalid location variable for pipeline stage)");
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(MslGeneratorImplTest, Emit_Function_EntryPointData_Compute_Output) {
|
|
||||||
// [[location 0]] var<out> foo : f32;
|
|
||||||
// [[location 1]] var<out> bar : i32;
|
|
||||||
//
|
|
||||||
// -> Error not allowed
|
|
||||||
|
|
||||||
Global("foo", ty.f32(), ast::StorageClass::kOutput, nullptr,
|
|
||||||
ast::DecorationList{Location(0)});
|
|
||||||
|
|
||||||
Global("bar", ty.i32(), ast::StorageClass::kOutput, nullptr,
|
|
||||||
ast::DecorationList{Location(1)});
|
|
||||||
|
|
||||||
auto body = ast::StatementList{
|
|
||||||
Assign("foo", "foo"),
|
|
||||||
Assign("bar", "bar"),
|
|
||||||
};
|
|
||||||
|
|
||||||
Func("main", ast::VariableList{}, ty.void_(), body,
|
|
||||||
ast::DecorationList{
|
|
||||||
Stage(ast::PipelineStage::kCompute),
|
|
||||||
});
|
|
||||||
|
|
||||||
GeneratorImpl& gen = Build();
|
|
||||||
|
|
||||||
auto* func = program->AST().Functions()[0];
|
|
||||||
ASSERT_FALSE(gen.EmitEntryPointData(func)) << gen.error();
|
|
||||||
EXPECT_EQ(gen.error(),
|
|
||||||
R"(error: invalid location variable for pipeline stage)");
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(MslGeneratorImplTest, Emit_Function_EntryPointData_Builtins) {
|
|
||||||
// Output builtins go in the output struct, input builtins will be passed
|
|
||||||
// as input parameters to the entry point function.
|
|
||||||
|
|
||||||
// [[builtin position]] var<in> coord : vec4<f32>;
|
|
||||||
// [[builtin frag_depth]] var<out> depth : f32;
|
|
||||||
//
|
|
||||||
// struct main_out {
|
|
||||||
// float depth [[depth(any)]];
|
|
||||||
// };
|
|
||||||
|
|
||||||
Global("coord", ty.vec4<f32>(), ast::StorageClass::kInput, nullptr,
|
|
||||||
ast::DecorationList{Builtin(ast::Builtin::kPosition)});
|
|
||||||
|
|
||||||
Global("depth", ty.f32(), ast::StorageClass::kOutput, nullptr,
|
|
||||||
ast::DecorationList{Builtin(ast::Builtin::kFragDepth)});
|
|
||||||
|
|
||||||
auto body = ast::StatementList{Assign("depth", MemberAccessor("coord", "x"))};
|
|
||||||
|
|
||||||
Func("main", ast::VariableList{}, ty.void_(), body,
|
|
||||||
ast::DecorationList{
|
|
||||||
Stage(ast::PipelineStage::kFragment),
|
|
||||||
});
|
|
||||||
|
|
||||||
GeneratorImpl& gen = Build();
|
|
||||||
|
|
||||||
auto* func = program->AST().Functions()[0];
|
|
||||||
ASSERT_TRUE(gen.EmitEntryPointData(func)) << gen.error();
|
|
||||||
EXPECT_EQ(gen.result(), R"(struct main_out {
|
|
||||||
float depth [[depth(any)]];
|
|
||||||
};
|
|
||||||
|
|
||||||
)");
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
} // namespace msl
|
|
||||||
} // namespace writer
|
|
||||||
} // namespace tint
|
|
@ -382,171 +382,6 @@ fragment void frag_main(const device Data& coord [[buffer(0)]]) {
|
|||||||
)");
|
)");
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(crbug.com/tint/697): Remove this test
|
|
||||||
TEST_F(
|
|
||||||
MslGeneratorImplTest,
|
|
||||||
Emit_Decoration_Called_By_EntryPoints_WithLocationGlobals_And_Params) { // NOLINT
|
|
||||||
Global("foo", ty.f32(), ast::StorageClass::kInput,
|
|
||||||
ast::DecorationList{Location(0)});
|
|
||||||
|
|
||||||
Global("bar", ty.f32(), ast::StorageClass::kOutput,
|
|
||||||
ast::DecorationList{
|
|
||||||
Location(1),
|
|
||||||
});
|
|
||||||
|
|
||||||
Global("val", ty.f32(), ast::StorageClass::kOutput,
|
|
||||||
ast::DecorationList{Location(0)});
|
|
||||||
|
|
||||||
ast::VariableList params;
|
|
||||||
params.push_back(Param("param", ty.f32()));
|
|
||||||
|
|
||||||
auto body = ast::StatementList{Assign("bar", "foo"), Assign("val", "param"),
|
|
||||||
Return("foo")};
|
|
||||||
|
|
||||||
Func("sub_func", params, ty.f32(), body, {});
|
|
||||||
|
|
||||||
body = ast::StatementList{
|
|
||||||
Assign("bar", Call("sub_func", 1.0f)),
|
|
||||||
Return(),
|
|
||||||
};
|
|
||||||
|
|
||||||
Func("ep_1", ast::VariableList{}, ty.void_(), body,
|
|
||||||
{
|
|
||||||
Stage(ast::PipelineStage::kFragment),
|
|
||||||
});
|
|
||||||
|
|
||||||
GeneratorImpl& gen = Build();
|
|
||||||
|
|
||||||
ASSERT_TRUE(gen.Generate()) << gen.error();
|
|
||||||
EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
|
|
||||||
|
|
||||||
using namespace metal;
|
|
||||||
struct ep_1_in {
|
|
||||||
float foo [[user(locn0)]];
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ep_1_out {
|
|
||||||
float bar [[color(1)]];
|
|
||||||
float val [[color(0)]];
|
|
||||||
};
|
|
||||||
|
|
||||||
float sub_func_ep_1(thread ep_1_in& _tint_in, thread ep_1_out& _tint_out, float param) {
|
|
||||||
_tint_out.bar = _tint_in.foo;
|
|
||||||
_tint_out.val = param;
|
|
||||||
return _tint_in.foo;
|
|
||||||
}
|
|
||||||
|
|
||||||
fragment ep_1_out ep_1(ep_1_in _tint_in [[stage_in]]) {
|
|
||||||
ep_1_out _tint_out = {};
|
|
||||||
_tint_out.bar = sub_func_ep_1(_tint_in, _tint_out, 1.0f);
|
|
||||||
return _tint_out;
|
|
||||||
}
|
|
||||||
|
|
||||||
)");
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(crbug.com/tint/697): Remove this test
|
|
||||||
TEST_F(MslGeneratorImplTest,
|
|
||||||
Emit_Decoration_Called_By_EntryPoints_NoUsedGlobals) {
|
|
||||||
Global("depth", ty.f32(), ast::StorageClass::kOutput,
|
|
||||||
ast::DecorationList{Builtin(ast::Builtin::kFragDepth)});
|
|
||||||
|
|
||||||
ast::VariableList params;
|
|
||||||
params.push_back(Param("param", ty.f32()));
|
|
||||||
|
|
||||||
Func("sub_func", params, ty.f32(),
|
|
||||||
ast::StatementList{
|
|
||||||
Return("param"),
|
|
||||||
},
|
|
||||||
{});
|
|
||||||
|
|
||||||
auto body = ast::StatementList{
|
|
||||||
Assign("depth", Call("sub_func", 1.0f)),
|
|
||||||
Return(),
|
|
||||||
};
|
|
||||||
|
|
||||||
Func("ep_1", ast::VariableList{}, ty.void_(), body,
|
|
||||||
{
|
|
||||||
Stage(ast::PipelineStage::kFragment),
|
|
||||||
});
|
|
||||||
|
|
||||||
GeneratorImpl& gen = Build();
|
|
||||||
|
|
||||||
ASSERT_TRUE(gen.Generate()) << gen.error();
|
|
||||||
EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
|
|
||||||
|
|
||||||
using namespace metal;
|
|
||||||
struct ep_1_out {
|
|
||||||
float depth [[depth(any)]];
|
|
||||||
};
|
|
||||||
|
|
||||||
float sub_func(float param) {
|
|
||||||
return param;
|
|
||||||
}
|
|
||||||
|
|
||||||
fragment ep_1_out ep_1() {
|
|
||||||
ep_1_out _tint_out = {};
|
|
||||||
_tint_out.depth = sub_func(1.0f);
|
|
||||||
return _tint_out;
|
|
||||||
}
|
|
||||||
|
|
||||||
)");
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(crbug.com/tint/697): Remove this test
|
|
||||||
TEST_F(
|
|
||||||
MslGeneratorImplTest,
|
|
||||||
Emit_Decoration_Called_By_EntryPoints_WithBuiltinGlobals_And_Params) { // NOLINT
|
|
||||||
Global("coord", ty.vec4<f32>(), ast::StorageClass::kInput,
|
|
||||||
ast::DecorationList{Builtin(ast::Builtin::kPosition)});
|
|
||||||
|
|
||||||
Global("depth", ty.f32(), ast::StorageClass::kOutput,
|
|
||||||
ast::DecorationList{Builtin(ast::Builtin::kFragDepth)});
|
|
||||||
|
|
||||||
ast::VariableList params;
|
|
||||||
params.push_back(Param("param", ty.f32()));
|
|
||||||
|
|
||||||
auto body = ast::StatementList{
|
|
||||||
Assign("depth", MemberAccessor("coord", "x")),
|
|
||||||
Return("param"),
|
|
||||||
};
|
|
||||||
|
|
||||||
Func("sub_func", params, ty.f32(), body, {});
|
|
||||||
|
|
||||||
body = ast::StatementList{
|
|
||||||
Assign("depth", Call("sub_func", 1.0f)),
|
|
||||||
Return(),
|
|
||||||
};
|
|
||||||
|
|
||||||
Func("ep_1", ast::VariableList{}, ty.void_(), body,
|
|
||||||
{
|
|
||||||
Stage(ast::PipelineStage::kFragment),
|
|
||||||
});
|
|
||||||
|
|
||||||
GeneratorImpl& gen = Build();
|
|
||||||
|
|
||||||
ASSERT_TRUE(gen.Generate()) << gen.error();
|
|
||||||
EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
|
|
||||||
|
|
||||||
using namespace metal;
|
|
||||||
struct ep_1_out {
|
|
||||||
float depth [[depth(any)]];
|
|
||||||
};
|
|
||||||
|
|
||||||
float sub_func_ep_1(thread ep_1_out& _tint_out, thread float4& coord, float param) {
|
|
||||||
_tint_out.depth = coord.x;
|
|
||||||
return param;
|
|
||||||
}
|
|
||||||
|
|
||||||
fragment ep_1_out ep_1(float4 coord [[position]]) {
|
|
||||||
ep_1_out _tint_out = {};
|
|
||||||
_tint_out.depth = sub_func_ep_1(_tint_out, coord, 1.0f);
|
|
||||||
return _tint_out;
|
|
||||||
}
|
|
||||||
|
|
||||||
)");
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(MslGeneratorImplTest,
|
TEST_F(MslGeneratorImplTest,
|
||||||
Emit_Decoration_Called_By_EntryPoint_With_Uniform) {
|
Emit_Decoration_Called_By_EntryPoint_With_Uniform) {
|
||||||
auto* ubo_ty = Structure("UBO", {Member("coord", ty.vec4<f32>())},
|
auto* ubo_ty = Structure("UBO", {Member("coord", ty.vec4<f32>())},
|
||||||
@ -715,51 +550,6 @@ fragment void frag_main(const device Data& coord [[buffer(0)]]) {
|
|||||||
)");
|
)");
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(crbug.com/tint/697): Remove this test
|
|
||||||
TEST_F(MslGeneratorImplTest,
|
|
||||||
Emit_Decoration_EntryPoints_WithGlobal_Nested_Return) {
|
|
||||||
Global("bar", ty.f32(), ast::StorageClass::kOutput,
|
|
||||||
ast::DecorationList{
|
|
||||||
Location(1),
|
|
||||||
});
|
|
||||||
|
|
||||||
auto* list = Block(Return());
|
|
||||||
|
|
||||||
auto body = ast::StatementList{
|
|
||||||
Assign("bar", Expr(1.f)),
|
|
||||||
create<ast::IfStatement>(create<ast::BinaryExpression>(
|
|
||||||
ast::BinaryOp::kEqual, Expr(1), Expr(1)),
|
|
||||||
list, ast::ElseStatementList{}),
|
|
||||||
Return(),
|
|
||||||
};
|
|
||||||
|
|
||||||
Func("ep_1", ast::VariableList{}, ty.void_(), body,
|
|
||||||
{
|
|
||||||
Stage(ast::PipelineStage::kFragment),
|
|
||||||
});
|
|
||||||
|
|
||||||
GeneratorImpl& gen = Build();
|
|
||||||
|
|
||||||
ASSERT_TRUE(gen.Generate()) << gen.error();
|
|
||||||
EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
|
|
||||||
|
|
||||||
using namespace metal;
|
|
||||||
struct ep_1_out {
|
|
||||||
float bar [[color(1)]];
|
|
||||||
};
|
|
||||||
|
|
||||||
fragment ep_1_out ep_1() {
|
|
||||||
ep_1_out _tint_out = {};
|
|
||||||
_tint_out.bar = 1.0f;
|
|
||||||
if ((1 == 1)) {
|
|
||||||
return _tint_out;
|
|
||||||
}
|
|
||||||
return _tint_out;
|
|
||||||
}
|
|
||||||
|
|
||||||
)");
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(MslGeneratorImplTest, Emit_Function_WithArrayParams) {
|
TEST_F(MslGeneratorImplTest, Emit_Function_WithArrayParams) {
|
||||||
ast::VariableList params;
|
ast::VariableList params;
|
||||||
params.push_back(Param("a", ty.array<f32, 5>()));
|
params.push_back(Param("a", ty.array<f32, 5>()));
|
||||||
|
@ -65,8 +65,8 @@ TEST_F(MslGeneratorImplTest, Emit_LoopWithContinuing) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(MslGeneratorImplTest, Emit_LoopNestedWithContinuing) {
|
TEST_F(MslGeneratorImplTest, Emit_LoopNestedWithContinuing) {
|
||||||
Global("lhs", ty.f32(), ast::StorageClass::kInput);
|
Global("lhs", ty.f32(), ast::StorageClass::kPrivate);
|
||||||
Global("rhs", ty.f32(), ast::StorageClass::kInput);
|
Global("rhs", ty.f32(), ast::StorageClass::kPrivate);
|
||||||
|
|
||||||
auto* body = Block(create<ast::DiscardStatement>());
|
auto* body = Block(create<ast::DiscardStatement>());
|
||||||
auto* continuing = Block(Return());
|
auto* continuing = Block(Return());
|
||||||
@ -130,7 +130,7 @@ TEST_F(MslGeneratorImplTest, Emit_LoopWithVarUsedInContinuing) {
|
|||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
Global("rhs", ty.f32(), ast::StorageClass::kInput);
|
Global("rhs", ty.f32(), ast::StorageClass::kPrivate);
|
||||||
|
|
||||||
auto* var = Var("lhs", ty.f32(), ast::StorageClass::kNone, Expr(2.4f));
|
auto* var = Var("lhs", ty.f32(), ast::StorageClass::kNone, Expr(2.4f));
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user