mirror of
https://github.com/encounter/dawn-cmake.git
synced 2025-12-13 15:16:16 +00:00
writer/hlsl: Simplify emission logic, clean up output
And fix issues where global variables would not be emitted unless they were transitively referenced by an entry point. This change requires crbug.com/tint/697 to be fixed before landing. Change-Id: I712bd9d369e08c9a3cdfb0f114c3609584f91f28 Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/54241 Kokoro: Kokoro <noreply+kokoro@google.com> Reviewed-by: James Price <jrprice@google.com>
This commit is contained in:
@@ -911,7 +911,6 @@ if(${TINT_BUILD_TESTS})
|
||||
writer/hlsl/generator_impl_constructor_test.cc
|
||||
writer/hlsl/generator_impl_continue_test.cc
|
||||
writer/hlsl/generator_impl_discard_test.cc
|
||||
writer/hlsl/generator_impl_function_entry_point_data_test.cc
|
||||
writer/hlsl/generator_impl_function_test.cc
|
||||
writer/hlsl/generator_impl_identifier_test.cc
|
||||
writer/hlsl/generator_impl_if_test.cc
|
||||
|
||||
@@ -26,6 +26,7 @@ class Expression;
|
||||
class Function;
|
||||
class MemberAccessorExpression;
|
||||
class Statement;
|
||||
class Struct;
|
||||
class StructMember;
|
||||
class Type;
|
||||
class TypeDecl;
|
||||
@@ -59,6 +60,7 @@ struct TypeMappings {
|
||||
StructMember* operator()(ast::StructMember*);
|
||||
Type* operator()(ast::Type*);
|
||||
Type* operator()(ast::TypeDecl*);
|
||||
Struct* operator()(ast::Struct*);
|
||||
Variable* operator()(ast::Variable*);
|
||||
//! @endcond
|
||||
};
|
||||
|
||||
@@ -43,10 +43,6 @@ namespace writer {
|
||||
namespace hlsl {
|
||||
namespace {
|
||||
|
||||
const char kInStructNameSuffix[] = "in";
|
||||
const char kOutStructNameSuffix[] = "out";
|
||||
const char kTintStructInVarPrefix[] = "tint_in";
|
||||
const char kTintStructOutVarPrefix[] = "tint_out";
|
||||
const char kTempNamePrefix[] = "tint_tmp";
|
||||
const char kSpecConstantPrefix[] = "WGSL_SPEC_CONSTANT_";
|
||||
|
||||
@@ -116,110 +112,59 @@ GeneratorImpl::GeneratorImpl(const Program* program)
|
||||
GeneratorImpl::~GeneratorImpl() = default;
|
||||
|
||||
bool GeneratorImpl::Generate(std::ostream& out) {
|
||||
for (auto* global : builder_.AST().GlobalVariables()) {
|
||||
register_global(global);
|
||||
}
|
||||
std::stringstream pending;
|
||||
const TypeInfo* last_kind = nullptr;
|
||||
|
||||
for (auto* const ty : builder_.AST().TypeDecls()) {
|
||||
if (ty->Is<ast::Alias>()) {
|
||||
continue;
|
||||
for (auto* decl : builder_.AST().GlobalDeclarations()) {
|
||||
if (decl->Is<ast::Alias>()) {
|
||||
continue; // Ignore aliases.
|
||||
}
|
||||
if (!EmitTypeDecl(out, TypeOf(ty))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!builder_.AST().TypeDecls().empty()) {
|
||||
out << std::endl;
|
||||
}
|
||||
|
||||
for (auto* var : builder_.AST().GlobalVariables()) {
|
||||
if (!var->is_const()) {
|
||||
continue;
|
||||
// Emit a new line between declarations if the type of declaration has
|
||||
// changed, or we're about to emit a function
|
||||
auto* kind = &decl->TypeInfo();
|
||||
if (pending.str().length() &&
|
||||
(last_kind != kind || decl->Is<ast::Function>())) {
|
||||
out << pending.str() << std::endl;
|
||||
pending.str(std::string());
|
||||
make_indent(out);
|
||||
}
|
||||
if (!EmitProgramConstVariable(out, var)) {
|
||||
last_kind = kind;
|
||||
|
||||
if (auto* global = decl->As<ast::Variable>()) {
|
||||
if (!EmitGlobalVariable(pending, global)) {
|
||||
return false;
|
||||
}
|
||||
} else if (auto* str = decl->As<ast::Struct>()) {
|
||||
if (!EmitStructType(pending, builder_.Sem().Get(str))) {
|
||||
return false;
|
||||
}
|
||||
} else if (auto* func = decl->As<ast::Function>()) {
|
||||
if (func->IsEntryPoint()) {
|
||||
if (!EmitEntryPointFunction(pending, func)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (!EmitFunction(pending, func)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
TINT_ICE(diagnostics_)
|
||||
<< "unhandled module-scope declaration: " << decl->TypeInfo().name;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// emitted_globals is a set used to ensure that globals are emitted once even
|
||||
// if they are used by multiple entry points.
|
||||
std::unordered_set<Symbol> emitted_globals;
|
||||
out << pending.str();
|
||||
|
||||
// Make sure all entry point data is emitted before the entry point functions
|
||||
for (auto* func : builder_.AST().Functions()) {
|
||||
if (!func->IsEntryPoint()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!EmitEntryPointData(out, func, emitted_globals)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto* func : builder_.AST().Functions()) {
|
||||
if (!EmitFunction(out, func)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto* func : builder_.AST().Functions()) {
|
||||
if (!func->IsEntryPoint()) {
|
||||
continue;
|
||||
}
|
||||
if (!EmitEntryPointFunction(out, func)) {
|
||||
return false;
|
||||
}
|
||||
out << std::endl;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void GeneratorImpl::register_global(ast::Variable* global) {
|
||||
auto* sem = builder_.Sem().Get(global);
|
||||
global_variables_.set(global->symbol(), sem);
|
||||
}
|
||||
|
||||
std::string GeneratorImpl::generate_name(const std::string& prefix) {
|
||||
return builder_.Symbols().NameFor(builder_.Symbols().New(prefix));
|
||||
}
|
||||
|
||||
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 outit = ep_sym_to_out_data_.find(current_ep_sym_);
|
||||
if (outit != ep_sym_to_out_data_.end()) {
|
||||
name = outit->second.var_name;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
bool GeneratorImpl::EmitTypeDecl(std::ostream& out, const sem::Type* ty) {
|
||||
make_indent(out);
|
||||
|
||||
if (auto* str = ty->As<sem::Struct>()) {
|
||||
if (!EmitStructType(
|
||||
out, str, builder_.Symbols().NameFor(str->Declaration()->name()))) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
TINT_UNREACHABLE(diagnostics_) << "declared type: " << ty->TypeInfo().name;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GeneratorImpl::EmitArrayAccessor(std::ostream& pre,
|
||||
std::ostream& out,
|
||||
ast::ArrayAccessorExpression* expr) {
|
||||
@@ -626,11 +571,6 @@ bool GeneratorImpl::EmitCall(std::ostream& pre,
|
||||
|
||||
auto name = builder_.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 = builder_.AST().Functions().Find(ident->symbol());
|
||||
if (func == nullptr) {
|
||||
@@ -641,27 +581,7 @@ bool GeneratorImpl::EmitCall(std::ostream& pre,
|
||||
|
||||
out << name << "(";
|
||||
|
||||
auto* func_sem = builder_.Sem().Get(func);
|
||||
|
||||
bool first = true;
|
||||
if (has_referenced_in_var_needing_struct(func_sem)) {
|
||||
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_sem)) {
|
||||
auto var_name = current_ep_var_name(VarType::kOut);
|
||||
if (!var_name.empty()) {
|
||||
if (!first) {
|
||||
out << ", ";
|
||||
}
|
||||
first = false;
|
||||
out << var_name;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto* param : params) {
|
||||
if (!first) {
|
||||
out << ", ";
|
||||
@@ -1469,37 +1389,10 @@ bool GeneratorImpl::EmitExpression(std::ostream& pre,
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GeneratorImpl::global_is_in_struct(const sem::Variable* var) const {
|
||||
auto& decorations = var->Declaration()->decorations();
|
||||
if (ast::HasDecoration<ast::LocationDecoration>(decorations) ||
|
||||
ast::HasDecoration<ast::BuiltinDecoration>(decorations)) {
|
||||
return var->StorageClass() == ast::StorageClass::kInput ||
|
||||
var->StorageClass() == ast::StorageClass::kOutput;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GeneratorImpl::EmitIdentifier(std::ostream&,
|
||||
std::ostream& out,
|
||||
ast::IdentifierExpression* expr) {
|
||||
auto* ident = expr->As<ast::IdentifierExpression>();
|
||||
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 << builder_.Symbols().NameFor(ident->symbol());
|
||||
|
||||
out << builder_.Symbols().NameFor(expr->symbol());
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1558,154 +1451,28 @@ bool GeneratorImpl::EmitIf(std::ostream& out, ast::IfStatement* stmt) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GeneratorImpl::has_referenced_in_var_needing_struct(
|
||||
const sem::Function* func) {
|
||||
for (auto data : func->ReferencedLocationVariables()) {
|
||||
auto* var = data.first;
|
||||
if (var->StorageClass() == ast::StorageClass::kInput) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto data : func->ReferencedBuiltinVariables()) {
|
||||
auto* var = data.first;
|
||||
if (var->StorageClass() == ast::StorageClass::kInput) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GeneratorImpl::has_referenced_out_var_needing_struct(
|
||||
const sem::Function* func) {
|
||||
for (auto data : func->ReferencedLocationVariables()) {
|
||||
auto* var = data.first;
|
||||
if (var->StorageClass() == ast::StorageClass::kOutput) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto data : func->ReferencedBuiltinVariables()) {
|
||||
auto* var = data.first;
|
||||
if (var->StorageClass() == ast::StorageClass::kOutput) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GeneratorImpl::has_referenced_var_needing_struct(
|
||||
const sem::Function* func) {
|
||||
for (auto data : func->ReferencedLocationVariables()) {
|
||||
auto* var = data.first;
|
||||
if (var->StorageClass() == ast::StorageClass::kOutput ||
|
||||
var->StorageClass() == ast::StorageClass::kInput) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto data : func->ReferencedBuiltinVariables()) {
|
||||
auto* var = data.first;
|
||||
if (var->StorageClass() == ast::StorageClass::kOutput ||
|
||||
var->StorageClass() == ast::StorageClass::kInput) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GeneratorImpl::EmitFunction(std::ostream& out, ast::Function* func) {
|
||||
make_indent(out);
|
||||
|
||||
// Entry points will be emitted later, skip for now.
|
||||
if (func->IsEntryPoint()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
auto* func_sem = builder_.Sem().Get(func);
|
||||
auto* sem = builder_.Sem().Get(func);
|
||||
|
||||
if (ast::HasDecoration<ast::InternalDecoration>(func->decorations())) {
|
||||
// An internal function. Do not emit.
|
||||
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_sem);
|
||||
|
||||
if (emit_duplicate_functions) {
|
||||
for (const auto& ep_sym : func_sem->AncestorEntryPoints()) {
|
||||
if (!EmitFunctionInternal(out, func, emit_duplicate_functions, ep_sym)) {
|
||||
return false;
|
||||
}
|
||||
out << std::endl;
|
||||
}
|
||||
} else {
|
||||
// Emit as non-duplicated
|
||||
if (!EmitFunctionInternal(out, func, false, Symbol())) {
|
||||
return false;
|
||||
}
|
||||
out << std::endl;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GeneratorImpl::EmitFunctionInternal(std::ostream& out,
|
||||
ast::Function* func_ast,
|
||||
bool emit_duplicate_functions,
|
||||
Symbol ep_sym) {
|
||||
auto* func = builder_.Sem().Get(func_ast);
|
||||
|
||||
if (!EmitType(out, func->ReturnType(), ast::StorageClass::kNone,
|
||||
if (!EmitType(out, sem->ReturnType(), ast::StorageClass::kNone,
|
||||
ast::Access::kReadWrite, "")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
out << " ";
|
||||
|
||||
std::string name;
|
||||
if (emit_duplicate_functions) {
|
||||
auto ep_name = ep_sym.to_str();
|
||||
// TODO(dsinclair): The SymbolToName should go away and just use
|
||||
// to_str() here when the conversion is complete.
|
||||
name = generate_name(builder_.Symbols().NameFor(func_ast->symbol()) + "_" +
|
||||
builder_.Symbols().NameFor(ep_sym));
|
||||
ep_func_name_remapped_[ep_name + "_" + func_ast->symbol().to_str()] = name;
|
||||
} else {
|
||||
name = builder_.Symbols().NameFor(func_ast->symbol());
|
||||
}
|
||||
|
||||
out << name << "(";
|
||||
out << builder_.Symbols().NameFor(func->symbol()) << "(";
|
||||
|
||||
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 << "in " << in_it->second.struct_name << " "
|
||||
<< in_it->second.var_name;
|
||||
first = false;
|
||||
}
|
||||
|
||||
auto outit = ep_sym_to_out_data_.find(ep_sym);
|
||||
if (outit != ep_sym_to_out_data_.end()) {
|
||||
if (!first) {
|
||||
out << ", ";
|
||||
}
|
||||
out << "out " << outit->second.struct_name << " "
|
||||
<< outit->second.var_name;
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto* v : func->Parameters()) {
|
||||
for (auto* v : sem->Parameters()) {
|
||||
if (!first) {
|
||||
out << ", ";
|
||||
}
|
||||
@@ -1739,301 +1506,199 @@ bool GeneratorImpl::EmitFunctionInternal(std::ostream& out,
|
||||
|
||||
out << ") ";
|
||||
|
||||
current_ep_sym_ = ep_sym;
|
||||
|
||||
if (!EmitBlockAndNewline(out, func_ast->body())) {
|
||||
if (!EmitBlockAndNewline(out, func->body())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
current_ep_sym_ = Symbol();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GeneratorImpl::EmitEntryPointData(
|
||||
std::ostream& out,
|
||||
ast::Function* func,
|
||||
std::unordered_set<Symbol>& emitted_globals) {
|
||||
std::vector<std::pair<const ast::Variable*, ast::Decoration*>> in_variables;
|
||||
std::vector<std::pair<const ast::Variable*, ast::Decoration*>> outvariables;
|
||||
auto* func_sem = builder_.Sem().Get(func);
|
||||
auto func_sym = func->symbol();
|
||||
|
||||
// TODO(crbug.com/tint/697): Remove this.
|
||||
for (auto data : func_sem->ReferencedLocationVariables()) {
|
||||
auto* var = data.first;
|
||||
auto* decl = var->Declaration();
|
||||
auto* deco = data.second;
|
||||
|
||||
if (var->StorageClass() == ast::StorageClass::kInput) {
|
||||
in_variables.push_back({decl, deco});
|
||||
} else if (var->StorageClass() == ast::StorageClass::kOutput) {
|
||||
outvariables.push_back({decl, deco});
|
||||
}
|
||||
bool GeneratorImpl::EmitGlobalVariable(std::ostream& out,
|
||||
ast::Variable* global) {
|
||||
if (global->is_const()) {
|
||||
return EmitProgramConstVariable(out, global);
|
||||
}
|
||||
|
||||
// TODO(crbug.com/tint/697): Remove this.
|
||||
for (auto data : func_sem->ReferencedBuiltinVariables()) {
|
||||
auto* var = data.first;
|
||||
auto* decl = var->Declaration();
|
||||
auto* deco = data.second;
|
||||
|
||||
if (var->StorageClass() == ast::StorageClass::kInput) {
|
||||
in_variables.push_back({decl, deco});
|
||||
} else if (var->StorageClass() == ast::StorageClass::kOutput) {
|
||||
outvariables.push_back({decl, deco});
|
||||
}
|
||||
auto* sem = builder_.Sem().Get(global);
|
||||
switch (sem->StorageClass()) {
|
||||
case ast::StorageClass::kUniform:
|
||||
return EmitUniformVariable(out, sem);
|
||||
case ast::StorageClass::kStorage:
|
||||
return EmitStorageVariable(out, sem);
|
||||
case ast::StorageClass::kUniformConstant:
|
||||
return EmitHandleVariable(out, sem);
|
||||
case ast::StorageClass::kPrivate:
|
||||
return EmitPrivateVariable(out, sem);
|
||||
case ast::StorageClass::kWorkgroup:
|
||||
return EmitWorkgroupVariable(out, sem);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
bool emitted_uniform = false;
|
||||
for (auto data : func_sem->ReferencedUniformVariables()) {
|
||||
auto* var = data.first;
|
||||
auto& binding_point = data.second;
|
||||
auto* decl = var->Declaration();
|
||||
TINT_ICE(diagnostics_) << "unhandled storage class " << sem->StorageClass();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!emitted_globals.emplace(decl->symbol()).second) {
|
||||
continue; // Global already emitted
|
||||
}
|
||||
bool GeneratorImpl::EmitUniformVariable(std::ostream& out,
|
||||
const sem::Variable* var) {
|
||||
make_indent(out);
|
||||
|
||||
auto* type = var->Type()->UnwrapRef();
|
||||
if (auto* strct = type->As<sem::Struct>()) {
|
||||
out << "ConstantBuffer<"
|
||||
<< builder_.Symbols().NameFor(strct->Declaration()->name()) << "> "
|
||||
<< builder_.Symbols().NameFor(decl->symbol())
|
||||
<< RegisterAndSpace('b', binding_point) << ";" << std::endl;
|
||||
} else {
|
||||
// TODO(dsinclair): There is outstanding spec work to require all uniform
|
||||
// buffers to be [[block]] decorated, which means structs. This is
|
||||
// currently not the case, so this code handles the cases where the data
|
||||
// is not a block.
|
||||
// Relevant: https://github.com/gpuweb/gpuweb/issues/1004
|
||||
// https://github.com/gpuweb/gpuweb/issues/1008
|
||||
auto name = "cbuffer_" + builder_.Symbols().NameFor(decl->symbol());
|
||||
out << "cbuffer " << name << RegisterAndSpace('b', binding_point) << " {"
|
||||
<< std::endl;
|
||||
auto* decl = var->Declaration();
|
||||
auto binding_point = decl->binding_point();
|
||||
auto* type = var->Type()->UnwrapRef();
|
||||
|
||||
increment_indent();
|
||||
make_indent(out);
|
||||
if (!EmitType(out, type, var->StorageClass(), var->Access(), "")) {
|
||||
return false;
|
||||
}
|
||||
out << " " << builder_.Symbols().NameFor(decl->symbol()) << ";"
|
||||
<< std::endl;
|
||||
decrement_indent();
|
||||
out << "};" << std::endl;
|
||||
}
|
||||
if (auto* strct = type->As<sem::Struct>()) {
|
||||
out << "ConstantBuffer<"
|
||||
<< builder_.Symbols().NameFor(strct->Declaration()->name()) << "> "
|
||||
<< builder_.Symbols().NameFor(decl->symbol())
|
||||
<< RegisterAndSpace('b', binding_point) << ";" << std::endl;
|
||||
} else {
|
||||
auto name = "cbuffer_" + builder_.Symbols().NameFor(decl->symbol());
|
||||
out << "cbuffer " << name << RegisterAndSpace('b', binding_point) << " {"
|
||||
<< std::endl;
|
||||
|
||||
emitted_uniform = true;
|
||||
}
|
||||
if (emitted_uniform) {
|
||||
out << std::endl;
|
||||
}
|
||||
|
||||
bool emitted_storagebuffer = false;
|
||||
for (auto data : func_sem->ReferencedStorageBufferVariables()) {
|
||||
auto* var = data.first;
|
||||
auto& binding_point = data.second;
|
||||
auto* decl = var->Declaration();
|
||||
|
||||
if (!emitted_globals.emplace(decl->symbol()).second) {
|
||||
continue; // Global already emitted
|
||||
}
|
||||
|
||||
auto* type = var->Type()->UnwrapRef();
|
||||
if (!EmitType(out, type, ast::StorageClass::kStorage, var->Access(), "")) {
|
||||
increment_indent();
|
||||
make_indent(out);
|
||||
if (!EmitType(out, type, var->StorageClass(), var->Access(), "")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
out << " " << builder_.Symbols().NameFor(decl->symbol())
|
||||
<< RegisterAndSpace(var->Access() == ast::Access::kRead ? 't' : 'u',
|
||||
binding_point)
|
||||
<< ";" << std::endl;
|
||||
emitted_storagebuffer = true;
|
||||
}
|
||||
if (emitted_storagebuffer) {
|
||||
out << std::endl;
|
||||
}
|
||||
|
||||
// TODO(crbug.com/tint/697): Remove this.
|
||||
if (!in_variables.empty()) {
|
||||
auto in_struct_name = generate_name(builder_.Symbols().NameFor(func_sym) +
|
||||
"_" + kInStructNameSuffix);
|
||||
auto in_var_name = generate_name(kTintStructInVarPrefix);
|
||||
ep_sym_to_in_data_[func_sym] = {in_struct_name, in_var_name};
|
||||
|
||||
make_indent(out);
|
||||
out << "struct " << in_struct_name << " {" << std::endl;
|
||||
|
||||
increment_indent();
|
||||
|
||||
for (auto& data : in_variables) {
|
||||
auto* var = data.first;
|
||||
auto* deco = data.second;
|
||||
auto* sem = builder_.Sem().Get(var);
|
||||
auto* type = sem->Type()->UnwrapRef();
|
||||
|
||||
make_indent(out);
|
||||
if (!EmitType(out, type, sem->StorageClass(), sem->Access(),
|
||||
builder_.Symbols().NameFor(var->symbol()))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
out << " " << builder_.Symbols().NameFor(var->symbol()) << " : ";
|
||||
if (auto* location = deco->As<ast::LocationDecoration>()) {
|
||||
if (func->pipeline_stage() == ast::PipelineStage::kCompute) {
|
||||
diagnostics_.add_error(
|
||||
"invalid location variable for pipeline stage");
|
||||
return false;
|
||||
}
|
||||
out << "TEXCOORD" << location->value();
|
||||
} 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;
|
||||
}
|
||||
out << " " << builder_.Symbols().NameFor(decl->symbol()) << ";"
|
||||
<< std::endl;
|
||||
decrement_indent();
|
||||
make_indent(out);
|
||||
out << "};" << std::endl;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
out << "};" << std::endl << std::endl;
|
||||
bool GeneratorImpl::EmitStorageVariable(std::ostream& out,
|
||||
const sem::Variable* var) {
|
||||
make_indent(out);
|
||||
|
||||
auto* decl = var->Declaration();
|
||||
auto* type = var->Type()->UnwrapRef();
|
||||
if (!EmitType(out, type, ast::StorageClass::kStorage, var->Access(), "")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO(crbug.com/tint/697): Remove this.
|
||||
if (!outvariables.empty()) {
|
||||
auto outstruct_name = generate_name(builder_.Symbols().NameFor(func_sym) +
|
||||
"_" + kOutStructNameSuffix);
|
||||
auto outvar_name = generate_name(kTintStructOutVarPrefix);
|
||||
ep_sym_to_out_data_[func_sym] = {outstruct_name, outvar_name};
|
||||
out << " " << builder_.Symbols().NameFor(decl->symbol())
|
||||
<< RegisterAndSpace(var->Access() == ast::Access::kRead ? 't' : 'u',
|
||||
decl->binding_point())
|
||||
<< ";" << std::endl;
|
||||
|
||||
make_indent(out);
|
||||
out << "struct " << outstruct_name << " {" << std::endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
increment_indent();
|
||||
for (auto& data : outvariables) {
|
||||
auto* var = data.first;
|
||||
auto* deco = data.second;
|
||||
auto* sem = builder_.Sem().Get(var);
|
||||
auto* type = sem->Type()->UnwrapRef();
|
||||
bool GeneratorImpl::EmitHandleVariable(std::ostream& out,
|
||||
const sem::Variable* var) {
|
||||
make_indent(out);
|
||||
|
||||
make_indent(out);
|
||||
if (!EmitType(out, type, sem->StorageClass(), sem->Access(),
|
||||
builder_.Symbols().NameFor(var->symbol()))) {
|
||||
return false;
|
||||
}
|
||||
auto* decl = var->Declaration();
|
||||
auto* unwrapped_type = var->Type()->UnwrapRef();
|
||||
|
||||
out << " " << builder_.Symbols().NameFor(var->symbol()) << " : ";
|
||||
|
||||
if (auto* location = deco->As<ast::LocationDecoration>()) {
|
||||
auto loc = location->value();
|
||||
if (func->pipeline_stage() == ast::PipelineStage::kVertex) {
|
||||
out << "TEXCOORD" << loc;
|
||||
} else if (func->pipeline_stage() == ast::PipelineStage::kFragment) {
|
||||
out << "SV_Target" << 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);
|
||||
out << "};" << std::endl << std::endl;
|
||||
}
|
||||
|
||||
{
|
||||
bool add_newline = false;
|
||||
for (auto* var : func_sem->ReferencedModuleVariables()) {
|
||||
auto* decl = var->Declaration();
|
||||
|
||||
auto* unwrapped_type = var->Type()->UnwrapRef();
|
||||
if (!emitted_globals.emplace(decl->symbol()).second) {
|
||||
continue; // Global already emitted
|
||||
}
|
||||
|
||||
make_indent(out);
|
||||
|
||||
std::ostringstream constructor_out;
|
||||
if (auto* constructor = decl->constructor()) {
|
||||
if (!EmitExpression(out, constructor_out, constructor)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
switch (var->StorageClass()) {
|
||||
case ast::StorageClass::kUniformConstant:
|
||||
break;
|
||||
case ast::StorageClass::kPrivate:
|
||||
out << "static ";
|
||||
break;
|
||||
case ast::StorageClass::kWorkgroup:
|
||||
out << "groupshared ";
|
||||
break;
|
||||
default:
|
||||
continue; // Not interested in this storage class
|
||||
}
|
||||
|
||||
auto name = builder_.Symbols().NameFor(decl->symbol());
|
||||
auto* type = var->Type()->UnwrapRef();
|
||||
if (!EmitType(out, type, var->StorageClass(), var->Access(), name)) {
|
||||
return false;
|
||||
}
|
||||
if (!type->Is<sem::Array>()) {
|
||||
out << " " << name;
|
||||
}
|
||||
|
||||
const char* register_space = nullptr;
|
||||
|
||||
if (unwrapped_type->Is<sem::Texture>()) {
|
||||
register_space = "t";
|
||||
if (auto* storage_tex = unwrapped_type->As<sem::StorageTexture>()) {
|
||||
if (storage_tex->access() != ast::Access::kRead) {
|
||||
register_space = "u";
|
||||
}
|
||||
}
|
||||
} else if (unwrapped_type->Is<sem::Sampler>()) {
|
||||
register_space = "s";
|
||||
}
|
||||
|
||||
if (register_space) {
|
||||
auto bp = decl->binding_point();
|
||||
out << " : register(" << register_space << bp.binding->value()
|
||||
<< ", space" << bp.group->value() << ")";
|
||||
}
|
||||
|
||||
if (constructor_out.str().length()) {
|
||||
out << " = " << constructor_out.str();
|
||||
}
|
||||
|
||||
out << ";" << std::endl;
|
||||
|
||||
add_newline = true;
|
||||
}
|
||||
if (add_newline) {
|
||||
out << std::endl;
|
||||
std::ostringstream constructor_out;
|
||||
if (auto* constructor = decl->constructor()) {
|
||||
if (!EmitExpression(out, constructor_out, constructor)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
auto name = builder_.Symbols().NameFor(decl->symbol());
|
||||
auto* type = var->Type()->UnwrapRef();
|
||||
if (!EmitType(out, type, var->StorageClass(), var->Access(), name)) {
|
||||
return false;
|
||||
}
|
||||
if (!type->Is<sem::Array>()) {
|
||||
out << " " << name;
|
||||
}
|
||||
|
||||
const char* register_space = nullptr;
|
||||
|
||||
if (unwrapped_type->Is<sem::Texture>()) {
|
||||
register_space = "t";
|
||||
if (auto* storage_tex = unwrapped_type->As<sem::StorageTexture>()) {
|
||||
if (storage_tex->access() != ast::Access::kRead) {
|
||||
register_space = "u";
|
||||
}
|
||||
}
|
||||
} else if (unwrapped_type->Is<sem::Sampler>()) {
|
||||
register_space = "s";
|
||||
}
|
||||
|
||||
if (register_space) {
|
||||
auto bp = decl->binding_point();
|
||||
out << " : register(" << register_space << bp.binding->value() << ", space"
|
||||
<< bp.group->value() << ")";
|
||||
}
|
||||
|
||||
if (constructor_out.str().length()) {
|
||||
out << " = " << constructor_out.str();
|
||||
}
|
||||
|
||||
out << ";" << std::endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GeneratorImpl::EmitPrivateVariable(std::ostream& out,
|
||||
const sem::Variable* var) {
|
||||
make_indent(out);
|
||||
|
||||
auto* decl = var->Declaration();
|
||||
|
||||
std::ostringstream constructor_out;
|
||||
if (auto* constructor = decl->constructor()) {
|
||||
if (!EmitExpression(out, constructor_out, constructor)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
out << "static ";
|
||||
|
||||
auto name = builder_.Symbols().NameFor(decl->symbol());
|
||||
auto* type = var->Type()->UnwrapRef();
|
||||
if (!EmitType(out, type, var->StorageClass(), var->Access(), name)) {
|
||||
return false;
|
||||
}
|
||||
if (!type->Is<sem::Array>()) {
|
||||
out << " " << name;
|
||||
}
|
||||
|
||||
if (constructor_out.str().length()) {
|
||||
out << " = " << constructor_out.str();
|
||||
}
|
||||
|
||||
out << ";" << std::endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GeneratorImpl::EmitWorkgroupVariable(std::ostream& out,
|
||||
const sem::Variable* var) {
|
||||
make_indent(out);
|
||||
|
||||
auto* decl = var->Declaration();
|
||||
|
||||
std::ostringstream constructor_out;
|
||||
if (auto* constructor = decl->constructor()) {
|
||||
if (!EmitExpression(out, constructor_out, constructor)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
out << "groupshared ";
|
||||
|
||||
auto name = builder_.Symbols().NameFor(decl->symbol());
|
||||
auto* type = var->Type()->UnwrapRef();
|
||||
if (!EmitType(out, type, var->StorageClass(), var->Access(), name)) {
|
||||
return false;
|
||||
}
|
||||
if (!type->Is<sem::Array>()) {
|
||||
out << " " << name;
|
||||
}
|
||||
|
||||
if (constructor_out.str().length()) {
|
||||
out << " = " << constructor_out.str();
|
||||
}
|
||||
|
||||
out << ";" << std::endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -2071,7 +1736,6 @@ bool GeneratorImpl::EmitEntryPointFunction(std::ostream& out,
|
||||
ast::Function* func) {
|
||||
make_indent(out);
|
||||
|
||||
current_ep_sym_ = func->symbol();
|
||||
auto* func_sem = builder_.Sem().Get(func);
|
||||
|
||||
if (func->pipeline_stage() == ast::PipelineStage::kCompute) {
|
||||
@@ -2098,28 +1762,11 @@ bool GeneratorImpl::EmitEntryPointFunction(std::ostream& out,
|
||||
make_indent(out);
|
||||
}
|
||||
|
||||
auto outdata = ep_sym_to_out_data_.find(current_ep_sym_);
|
||||
bool has_outdata = outdata != ep_sym_to_out_data_.end();
|
||||
if (has_outdata) {
|
||||
// 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 << outdata->second.struct_name;
|
||||
} else {
|
||||
out << func->return_type()->FriendlyName(builder_.Symbols());
|
||||
}
|
||||
// TODO(dsinclair): This should output the remapped name
|
||||
out << " " << builder_.Symbols().NameFor(current_ep_sym_) << "(";
|
||||
out << func->return_type()->FriendlyName(builder_.Symbols());
|
||||
|
||||
out << " " << builder_.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;
|
||||
first = false;
|
||||
}
|
||||
|
||||
// Emit entry point parameters.
|
||||
for (auto* var : func->params()) {
|
||||
@@ -2145,13 +1792,6 @@ bool GeneratorImpl::EmitEntryPointFunction(std::ostream& out,
|
||||
|
||||
increment_indent();
|
||||
|
||||
if (has_outdata) {
|
||||
make_indent(out);
|
||||
out << outdata->second.struct_name << " " << outdata->second.var_name
|
||||
<< " = (" << outdata->second.struct_name << ")0;" << std::endl;
|
||||
}
|
||||
|
||||
generating_entry_point_ = true;
|
||||
for (auto* s : *func->body()) {
|
||||
if (!EmitStatement(out, s)) {
|
||||
return false;
|
||||
@@ -2165,14 +1805,11 @@ bool GeneratorImpl::EmitEntryPointFunction(std::ostream& out,
|
||||
return false;
|
||||
}
|
||||
}
|
||||
generating_entry_point_ = false;
|
||||
|
||||
decrement_indent();
|
||||
make_indent(out);
|
||||
out << "}" << std::endl;
|
||||
|
||||
current_ep_sym_ = Symbol();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -2318,13 +1955,6 @@ bool GeneratorImpl::EmitReturn(std::ostream& out, ast::ReturnStatement* stmt) {
|
||||
}
|
||||
out << pre.str();
|
||||
out << "return " << ret_out.str();
|
||||
} else if (generating_entry_point_) {
|
||||
// TODO(crbug.com/tint/697): Remove this (and generating_entry_point_)
|
||||
out << "return";
|
||||
auto outdata = ep_sym_to_out_data_.find(current_ep_sym_);
|
||||
if (outdata != ep_sym_to_out_data_.end()) {
|
||||
out << " " << outdata->second.var_name;
|
||||
}
|
||||
} else {
|
||||
out << "return";
|
||||
}
|
||||
@@ -2567,9 +2197,7 @@ bool GeneratorImpl::EmitType(std::ostream& out,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GeneratorImpl::EmitStructType(std::ostream& out,
|
||||
const sem::Struct* str,
|
||||
const std::string& name) {
|
||||
bool GeneratorImpl::EmitStructType(std::ostream& out, const sem::Struct* str) {
|
||||
auto storage_class_uses = str->StorageClassUsage();
|
||||
if (storage_class_uses.size() ==
|
||||
storage_class_uses.count(ast::StorageClass::kStorage)) {
|
||||
@@ -2579,6 +2207,7 @@ bool GeneratorImpl::EmitStructType(std::ostream& out,
|
||||
return true;
|
||||
}
|
||||
|
||||
auto name = builder_.Symbols().NameFor(str->Declaration()->name());
|
||||
out << "struct " << name << " {" << std::endl;
|
||||
|
||||
increment_indent();
|
||||
|
||||
@@ -57,11 +57,6 @@ class GeneratorImpl : public TextGenerator {
|
||||
/// @returns true on successful generation; false otherwise
|
||||
bool Generate(std::ostream& out);
|
||||
|
||||
/// Handles generating a declared type
|
||||
/// @param out the output stream
|
||||
/// @param ty the declared type to generate
|
||||
/// @returns true if the declared type was emitted
|
||||
bool EmitTypeDecl(std::ostream& out, const sem::Type* ty);
|
||||
/// Handles an array accessor expression
|
||||
/// @param pre the preamble for the expression stream
|
||||
/// @param out the output of the expression stream
|
||||
@@ -229,25 +224,43 @@ class GeneratorImpl : public TextGenerator {
|
||||
/// @param func the function to generate
|
||||
/// @returns true if the function was emitted
|
||||
bool EmitFunction(std::ostream& out, ast::Function* func);
|
||||
/// Internal helper for emitting functions
|
||||
|
||||
/// Handles emitting a global variable
|
||||
/// @param out the output stream
|
||||
/// @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 none set
|
||||
/// @returns true if the function was emitted.
|
||||
bool EmitFunctionInternal(std::ostream& out,
|
||||
ast::Function* func,
|
||||
bool emit_duplicate_functions,
|
||||
Symbol ep_sym);
|
||||
/// Handles emitting information for an entry point
|
||||
/// @param global the global variable
|
||||
/// @returns true on success
|
||||
bool EmitGlobalVariable(std::ostream& out, ast::Variable* global);
|
||||
|
||||
/// Handles emitting a global variable with the uniform storage class
|
||||
/// @param out the output stream
|
||||
/// @param func the entry point
|
||||
/// @param emitted_globals the set of globals emitted over all entry points
|
||||
/// @returns true if the entry point data was emitted
|
||||
bool EmitEntryPointData(std::ostream& out,
|
||||
ast::Function* func,
|
||||
std::unordered_set<Symbol>& emitted_globals);
|
||||
/// @param var the global variable
|
||||
/// @returns true on success
|
||||
bool EmitUniformVariable(std::ostream& out, const sem::Variable* var);
|
||||
|
||||
/// Handles emitting a global variable with the storage storage class
|
||||
/// @param out the output stream
|
||||
/// @param var the global variable
|
||||
/// @returns true on success
|
||||
bool EmitStorageVariable(std::ostream& out, const sem::Variable* var);
|
||||
|
||||
/// Handles emitting a global variable with the handle storage class
|
||||
/// @param out the output stream
|
||||
/// @param var the global variable
|
||||
/// @returns true on success
|
||||
bool EmitHandleVariable(std::ostream& out, const sem::Variable* var);
|
||||
|
||||
/// Handles emitting a global variable with the private storage class
|
||||
/// @param out the output stream
|
||||
/// @param var the global variable
|
||||
/// @returns true on success
|
||||
bool EmitPrivateVariable(std::ostream& out, const sem::Variable* var);
|
||||
|
||||
/// Handles emitting a global variable with the workgroup storage class
|
||||
/// @param out the output stream
|
||||
/// @param var the global variable
|
||||
/// @returns true on success
|
||||
bool EmitWorkgroupVariable(std::ostream& out, const sem::Variable* var);
|
||||
|
||||
/// Handles emitting the entry point function
|
||||
/// @param out the output stream
|
||||
/// @param func the entry point
|
||||
@@ -314,11 +327,8 @@ class GeneratorImpl : public TextGenerator {
|
||||
/// Handles generating a structure declaration
|
||||
/// @param out the output stream
|
||||
/// @param ty the struct to generate
|
||||
/// @param name the struct name
|
||||
/// @returns true if the struct is emitted
|
||||
bool EmitStructType(std::ostream& out,
|
||||
const sem::Struct* ty,
|
||||
const std::string& name);
|
||||
bool EmitStructType(std::ostream& out, const sem::Struct* ty);
|
||||
/// Handles a unary op expression
|
||||
/// @param pre the preamble for the expression stream
|
||||
/// @param out the output of the expression stream
|
||||
@@ -343,13 +353,6 @@ class GeneratorImpl : public TextGenerator {
|
||||
/// @returns true if the variable was emitted
|
||||
bool EmitProgramConstVariable(std::ostream& out, const ast::Variable* var);
|
||||
|
||||
/// Registers the given global with the generator
|
||||
/// @param global the global to register
|
||||
void register_global(ast::Variable* global);
|
||||
/// 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;
|
||||
/// Handles generating a builtin method name
|
||||
/// @param intrinsic the semantic info for the intrinsic
|
||||
/// @returns the name or "" if not valid
|
||||
@@ -358,19 +361,6 @@ class GeneratorImpl : public TextGenerator {
|
||||
/// @param builtin the builtin to convert
|
||||
/// @returns the string name of the builtin or blank on error
|
||||
std::string builtin_to_attribute(ast::Builtin builtin) const;
|
||||
/// 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(const sem::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(const sem::Function* func);
|
||||
/// Determines if any used program 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(const sem::Function* func);
|
||||
|
||||
/// Generate a unique name
|
||||
/// @param prefix the name prefix
|
||||
@@ -385,7 +375,6 @@ class GeneratorImpl : public TextGenerator {
|
||||
std::string var_name;
|
||||
};
|
||||
|
||||
std::string current_ep_var_name(VarType type);
|
||||
std::string get_buffer_name(ast::Expression* expr);
|
||||
|
||||
/// @returns the resolved type of the ast::Expression `expr`
|
||||
@@ -428,17 +417,7 @@ class GeneratorImpl : public TextGenerator {
|
||||
}
|
||||
|
||||
ProgramBuilder builder_;
|
||||
Symbol current_ep_sym_;
|
||||
bool generating_entry_point_ = false;
|
||||
std::function<bool(std::ostream& out)> emit_continuing_;
|
||||
ScopeStack<const sem::Variable*> global_variables_;
|
||||
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 hlsl
|
||||
|
||||
@@ -1,320 +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/hlsl/test_helper.h"
|
||||
|
||||
namespace tint {
|
||||
namespace writer {
|
||||
namespace hlsl {
|
||||
namespace {
|
||||
|
||||
using HlslGeneratorImplTest_EntryPoint = TestHelper;
|
||||
|
||||
TEST_F(HlslGeneratorImplTest_EntryPoint,
|
||||
Emit_Function_EntryPointData_Vertex_Input) {
|
||||
// [[location 0]] var<in> foo : f32;
|
||||
// [[location 1]] var<in> bar : i32;
|
||||
//
|
||||
// struct vtx_main_in {
|
||||
// float foo : TEXCOORD0;
|
||||
// int bar : TEXCOORD1;
|
||||
// };
|
||||
|
||||
Global("foo", ty.f32(), ast::StorageClass::kInput, nullptr,
|
||||
ast::DecorationList{
|
||||
Location(0),
|
||||
});
|
||||
|
||||
Global("bar", ty.i32(), ast::StorageClass::kInput, nullptr,
|
||||
ast::DecorationList{
|
||||
Location(1),
|
||||
});
|
||||
|
||||
Func("vtx_main", ast::VariableList{}, ty.vec4<f32>(),
|
||||
ast::StatementList{
|
||||
Assign("foo", "foo"),
|
||||
Assign("bar", "bar"),
|
||||
Return(Construct(ty.vec4<f32>())),
|
||||
},
|
||||
{Stage(ast::PipelineStage::kVertex)},
|
||||
{Builtin(ast::Builtin::kPosition)});
|
||||
|
||||
std::unordered_set<Symbol> globals;
|
||||
|
||||
GeneratorImpl& gen = Build();
|
||||
|
||||
auto* func = program->AST().Functions()[0];
|
||||
ASSERT_TRUE(gen.EmitEntryPointData(out, func, globals)) << gen.error();
|
||||
EXPECT_EQ(result(), R"(struct vtx_main_in {
|
||||
float foo : TEXCOORD0;
|
||||
int bar : TEXCOORD1;
|
||||
};
|
||||
|
||||
)");
|
||||
}
|
||||
|
||||
TEST_F(HlslGeneratorImplTest_EntryPoint,
|
||||
Emit_Function_EntryPointData_Vertex_Output) {
|
||||
// [[location 0]] var<out> foo : f32;
|
||||
// [[location 1]] var<out> bar : i32;
|
||||
//
|
||||
// struct vtx_main_out {
|
||||
// float foo : TEXCOORD0;
|
||||
// int bar : TEXCOORD1;
|
||||
// };
|
||||
|
||||
Global("foo", ty.f32(), ast::StorageClass::kOutput, nullptr,
|
||||
ast::DecorationList{
|
||||
Location(0),
|
||||
});
|
||||
|
||||
Global("bar", ty.i32(), ast::StorageClass::kOutput, nullptr,
|
||||
ast::DecorationList{
|
||||
Location(1),
|
||||
});
|
||||
|
||||
Func("vtx_main", ast::VariableList{}, ty.vec4<f32>(),
|
||||
ast::StatementList{
|
||||
Assign("foo", "foo"),
|
||||
Assign("bar", "bar"),
|
||||
Return(Construct(ty.vec4<f32>())),
|
||||
},
|
||||
{Stage(ast::PipelineStage::kVertex)},
|
||||
{Builtin(ast::Builtin::kPosition)});
|
||||
|
||||
std::unordered_set<Symbol> globals;
|
||||
|
||||
GeneratorImpl& gen = Build();
|
||||
|
||||
auto* func = program->AST().Functions()[0];
|
||||
ASSERT_TRUE(gen.EmitEntryPointData(out, func, globals)) << gen.error();
|
||||
EXPECT_EQ(result(), R"(struct vtx_main_out {
|
||||
float foo : TEXCOORD0;
|
||||
int bar : TEXCOORD1;
|
||||
};
|
||||
|
||||
)");
|
||||
}
|
||||
|
||||
TEST_F(HlslGeneratorImplTest_EntryPoint,
|
||||
Emit_Function_EntryPointData_Fragment_Input) {
|
||||
// [[location 0]] var<in> foo : f32;
|
||||
// [[location 1]] var<in> bar : i32;
|
||||
//
|
||||
// struct frag_main_in {
|
||||
// float foo : TEXCOORD0;
|
||||
// int bar : TEXCOORD1;
|
||||
// };
|
||||
|
||||
Global("foo", ty.f32(), ast::StorageClass::kInput, nullptr,
|
||||
ast::DecorationList{
|
||||
Location(0),
|
||||
});
|
||||
|
||||
Global("bar", ty.i32(), ast::StorageClass::kInput, nullptr,
|
||||
ast::DecorationList{
|
||||
Location(1),
|
||||
});
|
||||
|
||||
Func("main", ast::VariableList{}, ty.void_(),
|
||||
ast::StatementList{
|
||||
Assign("foo", "foo"),
|
||||
Assign("bar", "bar"),
|
||||
},
|
||||
ast::DecorationList{
|
||||
Stage(ast::PipelineStage::kFragment),
|
||||
});
|
||||
|
||||
std::unordered_set<Symbol> globals;
|
||||
|
||||
GeneratorImpl& gen = Build();
|
||||
|
||||
auto* func = program->AST().Functions()[0];
|
||||
ASSERT_TRUE(gen.EmitEntryPointData(out, func, globals)) << gen.error();
|
||||
EXPECT_EQ(result(), R"(struct main_in {
|
||||
float foo : TEXCOORD0;
|
||||
int bar : TEXCOORD1;
|
||||
};
|
||||
|
||||
)");
|
||||
}
|
||||
|
||||
TEST_F(HlslGeneratorImplTest_EntryPoint,
|
||||
Emit_Function_EntryPointData_Fragment_Output) {
|
||||
// [[location 0]] var<out> foo : f32;
|
||||
// [[location 1]] var<out> bar : i32;
|
||||
//
|
||||
// struct frag_main_out {
|
||||
// float foo : SV_Target0;
|
||||
// int bar : SV_Target1;
|
||||
// };
|
||||
|
||||
Global("foo", ty.f32(), ast::StorageClass::kOutput, nullptr,
|
||||
ast::DecorationList{
|
||||
Location(0),
|
||||
});
|
||||
|
||||
Global("bar", ty.i32(), ast::StorageClass::kOutput, nullptr,
|
||||
ast::DecorationList{
|
||||
Location(1),
|
||||
});
|
||||
|
||||
Func("main", ast::VariableList{}, ty.void_(),
|
||||
ast::StatementList{
|
||||
Assign("foo", "foo"),
|
||||
Assign("bar", "bar"),
|
||||
},
|
||||
ast::DecorationList{
|
||||
Stage(ast::PipelineStage::kFragment),
|
||||
});
|
||||
|
||||
std::unordered_set<Symbol> globals;
|
||||
|
||||
GeneratorImpl& gen = Build();
|
||||
|
||||
auto* func = program->AST().Functions()[0];
|
||||
ASSERT_TRUE(gen.EmitEntryPointData(out, func, globals)) << gen.error();
|
||||
EXPECT_EQ(result(), R"(struct main_out {
|
||||
float foo : SV_Target0;
|
||||
int bar : SV_Target1;
|
||||
};
|
||||
|
||||
)");
|
||||
}
|
||||
|
||||
TEST_F(HlslGeneratorImplTest_EntryPoint,
|
||||
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),
|
||||
});
|
||||
|
||||
Func("main", ast::VariableList{}, ty.void_(),
|
||||
ast::StatementList{
|
||||
Assign("foo", "foo"),
|
||||
Assign("bar", "bar"),
|
||||
},
|
||||
ast::DecorationList{
|
||||
Stage(ast::PipelineStage::kCompute),
|
||||
});
|
||||
|
||||
std::unordered_set<Symbol> globals;
|
||||
|
||||
GeneratorImpl& gen = Build();
|
||||
|
||||
auto* func = program->AST().Functions()[0];
|
||||
ASSERT_FALSE(gen.EmitEntryPointData(out, func, globals)) << gen.error();
|
||||
EXPECT_EQ(gen.error(),
|
||||
R"(error: invalid location variable for pipeline stage)");
|
||||
}
|
||||
|
||||
TEST_F(HlslGeneratorImplTest_EntryPoint,
|
||||
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),
|
||||
});
|
||||
|
||||
Func("main", ast::VariableList{}, ty.void_(),
|
||||
ast::StatementList{
|
||||
Assign("foo", "foo"),
|
||||
Assign("bar", "bar"),
|
||||
},
|
||||
ast::DecorationList{
|
||||
Stage(ast::PipelineStage::kCompute),
|
||||
});
|
||||
|
||||
std::unordered_set<Symbol> globals;
|
||||
|
||||
GeneratorImpl& gen = Build();
|
||||
|
||||
auto* func = program->AST().Functions()[0];
|
||||
ASSERT_FALSE(gen.EmitEntryPointData(out, func, globals)) << gen.error();
|
||||
EXPECT_EQ(gen.error(),
|
||||
R"(error: invalid location variable for pipeline stage)");
|
||||
}
|
||||
|
||||
TEST_F(HlslGeneratorImplTest_EntryPoint,
|
||||
Emit_Function_EntryPointData_Builtins) {
|
||||
// [[builtin position]] var<in> coord : vec4<f32>;
|
||||
// [[builtin frag_depth]] var<out> depth : f32;
|
||||
//
|
||||
// struct main_in {
|
||||
// float4 coord : SV_Position;
|
||||
// };
|
||||
//
|
||||
// struct main_out {
|
||||
// float depth : SV_Depth;
|
||||
// };
|
||||
|
||||
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),
|
||||
});
|
||||
|
||||
Func("main", ast::VariableList{}, ty.void_(),
|
||||
ast::StatementList{
|
||||
Assign("depth", MemberAccessor("coord", "x")),
|
||||
},
|
||||
ast::DecorationList{
|
||||
Stage(ast::PipelineStage::kFragment),
|
||||
});
|
||||
|
||||
std::unordered_set<Symbol> globals;
|
||||
|
||||
GeneratorImpl& gen = Build();
|
||||
|
||||
auto* func = program->AST().Functions()[0];
|
||||
ASSERT_TRUE(gen.EmitEntryPointData(out, func, globals)) << gen.error();
|
||||
EXPECT_EQ(result(), R"(struct main_in {
|
||||
float4 coord : SV_Position;
|
||||
};
|
||||
|
||||
struct main_out {
|
||||
float depth : SV_Depth;
|
||||
};
|
||||
|
||||
)");
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace hlsl
|
||||
} // namespace writer
|
||||
} // namespace tint
|
||||
@@ -42,7 +42,6 @@ TEST_F(HlslGeneratorImplTest_Function, Emit_Function) {
|
||||
EXPECT_EQ(result(), R"( void my_func() {
|
||||
return;
|
||||
}
|
||||
|
||||
)");
|
||||
}
|
||||
|
||||
@@ -77,7 +76,6 @@ TEST_F(HlslGeneratorImplTest_Function, Emit_Function_WithParams) {
|
||||
EXPECT_EQ(result(), R"( void my_func(float a, int b) {
|
||||
return;
|
||||
}
|
||||
|
||||
)");
|
||||
}
|
||||
|
||||
@@ -94,7 +92,6 @@ TEST_F(HlslGeneratorImplTest_Function,
|
||||
EXPECT_EQ(result(), R"(void main() {
|
||||
return;
|
||||
}
|
||||
|
||||
)");
|
||||
}
|
||||
|
||||
@@ -111,7 +108,6 @@ TEST_F(HlslGeneratorImplTest_Function, PtrParameter) {
|
||||
EXPECT_THAT(result(), HasSubstr(R"(float f(inout float foo) {
|
||||
return foo;
|
||||
}
|
||||
|
||||
)"));
|
||||
}
|
||||
|
||||
@@ -139,7 +135,6 @@ tint_symbol_2 frag_main(tint_symbol_1 tint_symbol) {
|
||||
const tint_symbol_2 tint_symbol_3 = {foo};
|
||||
return tint_symbol_3;
|
||||
}
|
||||
|
||||
)");
|
||||
}
|
||||
|
||||
@@ -170,7 +165,6 @@ tint_symbol_2 frag_main(tint_symbol_1 tint_symbol) {
|
||||
const tint_symbol_2 tint_symbol_3 = {coord.x};
|
||||
return tint_symbol_3;
|
||||
}
|
||||
|
||||
)");
|
||||
}
|
||||
|
||||
@@ -223,11 +217,6 @@ struct tint_symbol {
|
||||
float col2 : TEXCOORD2;
|
||||
float4 pos : SV_Position;
|
||||
};
|
||||
struct tint_symbol_3 {
|
||||
float col1 : TEXCOORD1;
|
||||
float col2 : TEXCOORD2;
|
||||
float4 pos : SV_Position;
|
||||
};
|
||||
|
||||
tint_symbol vert_main() {
|
||||
const Interface tint_symbol_1 = {float4(0.0f, 0.0f, 0.0f, 0.0f), 0.5f, 0.25f};
|
||||
@@ -235,6 +224,12 @@ tint_symbol vert_main() {
|
||||
return tint_symbol_4;
|
||||
}
|
||||
|
||||
struct tint_symbol_3 {
|
||||
float col1 : TEXCOORD1;
|
||||
float col2 : TEXCOORD2;
|
||||
float4 pos : SV_Position;
|
||||
};
|
||||
|
||||
void frag_main(tint_symbol_3 tint_symbol_2) {
|
||||
const Interface inputs = {tint_symbol_2.pos, tint_symbol_2.col1, tint_symbol_2.col2};
|
||||
const float r = inputs.col1;
|
||||
@@ -242,7 +237,6 @@ void frag_main(tint_symbol_3 tint_symbol_2) {
|
||||
const float4 p = inputs.pos;
|
||||
return;
|
||||
}
|
||||
|
||||
)");
|
||||
}
|
||||
|
||||
@@ -285,30 +279,31 @@ TEST_F(HlslGeneratorImplTest_Function,
|
||||
EXPECT_EQ(result(), R"(struct VertexOutput {
|
||||
float4 pos;
|
||||
};
|
||||
struct tint_symbol {
|
||||
float4 pos : SV_Position;
|
||||
};
|
||||
struct tint_symbol_2 {
|
||||
float4 pos : SV_Position;
|
||||
};
|
||||
|
||||
VertexOutput foo(float x) {
|
||||
const VertexOutput tint_symbol_4 = {float4(x, x, x, 1.0f)};
|
||||
return tint_symbol_4;
|
||||
}
|
||||
|
||||
struct tint_symbol {
|
||||
float4 pos : SV_Position;
|
||||
};
|
||||
|
||||
tint_symbol vert_main1() {
|
||||
const VertexOutput tint_symbol_1 = {foo(0.5f)};
|
||||
const tint_symbol tint_symbol_5 = {tint_symbol_1.pos};
|
||||
return tint_symbol_5;
|
||||
}
|
||||
|
||||
struct tint_symbol_2 {
|
||||
float4 pos : SV_Position;
|
||||
};
|
||||
|
||||
tint_symbol_2 vert_main2() {
|
||||
const VertexOutput tint_symbol_3 = {foo(0.25f)};
|
||||
const tint_symbol_2 tint_symbol_6 = {tint_symbol_3.pos};
|
||||
return tint_symbol_6;
|
||||
}
|
||||
|
||||
)");
|
||||
}
|
||||
|
||||
@@ -360,7 +355,6 @@ void frag_main() {
|
||||
float v = sub_func(1.0f);
|
||||
return;
|
||||
}
|
||||
|
||||
)");
|
||||
}
|
||||
|
||||
@@ -400,7 +394,6 @@ void frag_main() {
|
||||
float v = uniforms.coord.x;
|
||||
return;
|
||||
}
|
||||
|
||||
)");
|
||||
}
|
||||
|
||||
@@ -436,14 +429,12 @@ TEST_F(HlslGeneratorImplTest_Function,
|
||||
|
||||
ASSERT_TRUE(gen.Generate(out)) << gen.error();
|
||||
EXPECT_EQ(result(),
|
||||
R"(
|
||||
RWByteAddressBuffer coord : register(u0, space1);
|
||||
R"(RWByteAddressBuffer coord : register(u0, space1);
|
||||
|
||||
void frag_main() {
|
||||
float v = asfloat(coord.Load(4u));
|
||||
return;
|
||||
}
|
||||
|
||||
)");
|
||||
}
|
||||
|
||||
@@ -478,14 +469,12 @@ TEST_F(HlslGeneratorImplTest_Function,
|
||||
|
||||
ASSERT_TRUE(gen.Generate(out)) << gen.error();
|
||||
EXPECT_EQ(result(),
|
||||
R"(
|
||||
ByteAddressBuffer coord : register(t0, space1);
|
||||
R"(ByteAddressBuffer coord : register(t0, space1);
|
||||
|
||||
void frag_main() {
|
||||
float v = asfloat(coord.Load(4u));
|
||||
return;
|
||||
}
|
||||
|
||||
)");
|
||||
}
|
||||
|
||||
@@ -517,14 +506,12 @@ TEST_F(HlslGeneratorImplTest_Function,
|
||||
|
||||
ASSERT_TRUE(gen.Generate(out)) << gen.error();
|
||||
EXPECT_EQ(result(),
|
||||
R"(
|
||||
RWByteAddressBuffer coord : register(u0, space1);
|
||||
R"(RWByteAddressBuffer coord : register(u0, space1);
|
||||
|
||||
void frag_main() {
|
||||
coord.Store(4u, asuint(2.0f));
|
||||
return;
|
||||
}
|
||||
|
||||
)");
|
||||
}
|
||||
|
||||
@@ -557,171 +544,12 @@ TEST_F(HlslGeneratorImplTest_Function,
|
||||
|
||||
ASSERT_TRUE(gen.Generate(out)) << gen.error();
|
||||
EXPECT_EQ(result(),
|
||||
R"(
|
||||
RWByteAddressBuffer coord : register(u0, space1);
|
||||
R"(RWByteAddressBuffer coord : register(u0, space1);
|
||||
|
||||
void frag_main() {
|
||||
coord.Store(4u, asuint(2.0f));
|
||||
return;
|
||||
}
|
||||
|
||||
)");
|
||||
}
|
||||
|
||||
// TODO(crbug.com/tint/697): Remove this test
|
||||
TEST_F(
|
||||
HlslGeneratorImplTest_Function,
|
||||
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),
|
||||
});
|
||||
|
||||
Func("sub_func", ast::VariableList{Param("param", ty.f32())}, ty.f32(),
|
||||
{
|
||||
Assign("bar", "foo"),
|
||||
Assign("val", "param"),
|
||||
Return("foo"),
|
||||
});
|
||||
|
||||
Func("ep_1", ast::VariableList{}, ty.void_(),
|
||||
{
|
||||
Assign("bar", Call("sub_func", 1.0f)),
|
||||
Return(),
|
||||
},
|
||||
{
|
||||
Stage(ast::PipelineStage::kFragment),
|
||||
});
|
||||
|
||||
GeneratorImpl& gen = Build();
|
||||
|
||||
ASSERT_TRUE(gen.Generate(out)) << gen.error();
|
||||
EXPECT_EQ(result(), R"(struct ep_1_in {
|
||||
float foo : TEXCOORD0;
|
||||
};
|
||||
|
||||
struct ep_1_out {
|
||||
float bar : SV_Target1;
|
||||
float val : SV_Target0;
|
||||
};
|
||||
|
||||
float sub_func_ep_1(in ep_1_in tint_in, out ep_1_out tint_out, float param) {
|
||||
tint_out.bar = tint_in.foo;
|
||||
tint_out.val = param;
|
||||
return tint_in.foo;
|
||||
}
|
||||
|
||||
ep_1_out ep_1(ep_1_in tint_in) {
|
||||
ep_1_out tint_out = (ep_1_out)0;
|
||||
tint_out.bar = sub_func_ep_1(tint_in, tint_out, 1.0f);
|
||||
return tint_out;
|
||||
}
|
||||
|
||||
)");
|
||||
}
|
||||
|
||||
TEST_F(HlslGeneratorImplTest_Function,
|
||||
Emit_Decoration_Called_By_EntryPoints_NoUsedGlobals) {
|
||||
Global("depth", ty.f32(), ast::StorageClass::kOutput,
|
||||
ast::DecorationList{
|
||||
Builtin(ast::Builtin::kFragDepth),
|
||||
});
|
||||
|
||||
Func("sub_func", ast::VariableList{Param("param", ty.f32())}, ty.f32(),
|
||||
{
|
||||
Return("param"),
|
||||
});
|
||||
|
||||
Func("ep_1", ast::VariableList{}, ty.void_(),
|
||||
{
|
||||
Assign("depth", Call("sub_func", 1.0f)),
|
||||
Return(),
|
||||
},
|
||||
{
|
||||
Stage(ast::PipelineStage::kFragment),
|
||||
});
|
||||
|
||||
GeneratorImpl& gen = Build();
|
||||
|
||||
ASSERT_TRUE(gen.Generate(out)) << gen.error();
|
||||
EXPECT_EQ(result(), R"(struct ep_1_out {
|
||||
float depth : SV_Depth;
|
||||
};
|
||||
|
||||
float sub_func(float param) {
|
||||
return param;
|
||||
}
|
||||
|
||||
ep_1_out ep_1() {
|
||||
ep_1_out tint_out = (ep_1_out)0;
|
||||
tint_out.depth = sub_func(1.0f);
|
||||
return tint_out;
|
||||
}
|
||||
|
||||
)");
|
||||
}
|
||||
|
||||
// TODO(crbug.com/tint/697): Remove this test
|
||||
TEST_F(
|
||||
HlslGeneratorImplTest_Function,
|
||||
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),
|
||||
});
|
||||
|
||||
Func("sub_func", ast::VariableList{Param("param", ty.f32())}, ty.f32(),
|
||||
{
|
||||
Assign("depth", MemberAccessor("coord", "x")),
|
||||
Return("param"),
|
||||
});
|
||||
|
||||
Func("ep_1", ast::VariableList{}, ty.void_(),
|
||||
{
|
||||
Assign("depth", Call("sub_func", 1.0f)),
|
||||
Return(),
|
||||
},
|
||||
{
|
||||
Stage(ast::PipelineStage::kFragment),
|
||||
});
|
||||
|
||||
GeneratorImpl& gen = SanitizeAndBuild();
|
||||
|
||||
ASSERT_TRUE(gen.Generate(out)) << gen.error();
|
||||
EXPECT_EQ(result(), R"(struct ep_1_in {
|
||||
float4 coord : SV_Position;
|
||||
};
|
||||
|
||||
struct ep_1_out {
|
||||
float depth : SV_Depth;
|
||||
};
|
||||
|
||||
float sub_func_ep_1(in ep_1_in tint_in, out ep_1_out tint_out, float param) {
|
||||
tint_out.depth = tint_in.coord.x;
|
||||
return param;
|
||||
}
|
||||
|
||||
ep_1_out ep_1(ep_1_in tint_in) {
|
||||
ep_1_out tint_out = (ep_1_out)0;
|
||||
tint_out.depth = sub_func_ep_1(tint_in, tint_out, 1.0f);
|
||||
return tint_out;
|
||||
}
|
||||
|
||||
)");
|
||||
}
|
||||
|
||||
@@ -769,7 +597,6 @@ void frag_main() {
|
||||
float v = sub_func(1.0f);
|
||||
return;
|
||||
}
|
||||
|
||||
)");
|
||||
}
|
||||
|
||||
@@ -805,8 +632,7 @@ TEST_F(HlslGeneratorImplTest_Function,
|
||||
|
||||
ASSERT_TRUE(gen.Generate(out)) << gen.error();
|
||||
EXPECT_EQ(result(),
|
||||
R"(
|
||||
RWByteAddressBuffer coord : register(u0, space1);
|
||||
R"(RWByteAddressBuffer coord : register(u0, space1);
|
||||
|
||||
float sub_func(float param) {
|
||||
return asfloat(coord.Load(0u));
|
||||
@@ -816,46 +642,6 @@ void frag_main() {
|
||||
float v = sub_func(1.0f);
|
||||
return;
|
||||
}
|
||||
|
||||
)");
|
||||
}
|
||||
|
||||
TEST_F(HlslGeneratorImplTest_Function,
|
||||
Emit_Decoration_EntryPoints_WithGlobal_Nested_Return) {
|
||||
Global("bar", ty.f32(), ast::StorageClass::kOutput,
|
||||
ast::DecorationList{
|
||||
Location(1),
|
||||
});
|
||||
|
||||
Func(
|
||||
"ep_1", ast::VariableList{}, ty.void_(),
|
||||
{
|
||||
Assign("bar", Expr(1.0f)),
|
||||
create<ast::IfStatement>(create<ast::BinaryExpression>(
|
||||
ast::BinaryOp::kEqual, Expr(1), Expr(1)),
|
||||
Block(Return()), ast::ElseStatementList{}),
|
||||
Return(),
|
||||
},
|
||||
{
|
||||
Stage(ast::PipelineStage::kFragment),
|
||||
});
|
||||
|
||||
GeneratorImpl& gen = Build();
|
||||
|
||||
ASSERT_TRUE(gen.Generate(out)) << gen.error();
|
||||
EXPECT_EQ(result(), R"(struct ep_1_out {
|
||||
float bar : SV_Target1;
|
||||
};
|
||||
|
||||
ep_1_out ep_1() {
|
||||
ep_1_out tint_out = (ep_1_out)0;
|
||||
tint_out.bar = 1.0f;
|
||||
if ((1 == 1)) {
|
||||
return tint_out;
|
||||
}
|
||||
return tint_out;
|
||||
}
|
||||
|
||||
)");
|
||||
}
|
||||
|
||||
@@ -872,7 +658,6 @@ TEST_F(HlslGeneratorImplTest_Function,
|
||||
EXPECT_EQ(result(), R"(void tint_symbol() {
|
||||
return;
|
||||
}
|
||||
|
||||
)");
|
||||
}
|
||||
|
||||
@@ -892,7 +677,6 @@ TEST_F(HlslGeneratorImplTest_Function, Emit_Decoration_EntryPoint_Compute) {
|
||||
void main() {
|
||||
return;
|
||||
}
|
||||
|
||||
)");
|
||||
}
|
||||
|
||||
@@ -911,7 +695,6 @@ TEST_F(HlslGeneratorImplTest_Function,
|
||||
void main() {
|
||||
return;
|
||||
}
|
||||
|
||||
)");
|
||||
}
|
||||
|
||||
@@ -932,11 +715,11 @@ TEST_F(HlslGeneratorImplTest_Function,
|
||||
EXPECT_EQ(result(), R"(static const int width = int(2);
|
||||
static const int height = int(3);
|
||||
static const int depth = int(4);
|
||||
|
||||
[numthreads(2, 3, 4)]
|
||||
void main() {
|
||||
return;
|
||||
}
|
||||
|
||||
)");
|
||||
}
|
||||
|
||||
@@ -966,11 +749,11 @@ static const int height = WGSL_SPEC_CONSTANT_8;
|
||||
#define WGSL_SPEC_CONSTANT_9 int(4)
|
||||
#endif
|
||||
static const int depth = WGSL_SPEC_CONSTANT_9;
|
||||
|
||||
[numthreads(WGSL_SPEC_CONSTANT_7, WGSL_SPEC_CONSTANT_8, WGSL_SPEC_CONSTANT_9)]
|
||||
void main() {
|
||||
return;
|
||||
}
|
||||
|
||||
)");
|
||||
}
|
||||
|
||||
@@ -988,7 +771,6 @@ TEST_F(HlslGeneratorImplTest_Function, Emit_Function_WithArrayParams) {
|
||||
EXPECT_EQ(result(), R"( void my_func(float a[5]) {
|
||||
return;
|
||||
}
|
||||
|
||||
)");
|
||||
}
|
||||
|
||||
@@ -1052,8 +834,7 @@ TEST_F(HlslGeneratorImplTest_Function,
|
||||
GeneratorImpl& gen = SanitizeAndBuild();
|
||||
|
||||
ASSERT_TRUE(gen.Generate(out)) << gen.error();
|
||||
EXPECT_EQ(result(), R"(
|
||||
RWByteAddressBuffer data : register(u0, space0);
|
||||
EXPECT_EQ(result(), R"(RWByteAddressBuffer data : register(u0, space0);
|
||||
|
||||
[numthreads(1, 1, 1)]
|
||||
void a() {
|
||||
@@ -1066,7 +847,6 @@ void b() {
|
||||
float v = asfloat(data.Load(0u));
|
||||
return;
|
||||
}
|
||||
|
||||
)");
|
||||
}
|
||||
|
||||
|
||||
@@ -502,7 +502,6 @@ void main() {
|
||||
DeviceMemoryBarrierWithGroupSync();
|
||||
return;
|
||||
}
|
||||
|
||||
)");
|
||||
}
|
||||
|
||||
@@ -519,7 +518,6 @@ void main() {
|
||||
GroupMemoryBarrierWithGroupSync();
|
||||
return;
|
||||
}
|
||||
|
||||
)");
|
||||
}
|
||||
|
||||
@@ -543,7 +541,6 @@ void main() {
|
||||
f(1, 2, 3);
|
||||
return;
|
||||
}
|
||||
|
||||
)");
|
||||
}
|
||||
|
||||
|
||||
@@ -143,7 +143,6 @@ void test_function() {
|
||||
float expr = str.mem;
|
||||
return;
|
||||
}
|
||||
|
||||
)");
|
||||
}
|
||||
|
||||
@@ -342,19 +341,17 @@ TEST_F(HlslGeneratorImplTest_MemberAccessor, StorageBuffer_Store_Matrix_Empty) {
|
||||
|
||||
ASSERT_TRUE(gen.Generate(out)) << gen.error();
|
||||
auto* expected =
|
||||
R"(
|
||||
RWByteAddressBuffer data : register(u0, space1);
|
||||
|
||||
void tint_symbol_1(RWByteAddressBuffer buffer, uint offset, float2x3 value) {
|
||||
R"(void tint_symbol_1(RWByteAddressBuffer buffer, uint offset, float2x3 value) {
|
||||
buffer.Store3((offset + 0u), asuint(value[0u]));
|
||||
buffer.Store3((offset + 16u), asuint(value[1u]));
|
||||
}
|
||||
|
||||
RWByteAddressBuffer data : register(u0, space1);
|
||||
|
||||
void main() {
|
||||
tint_symbol_1(data, 16u, float2x3(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
|
||||
return;
|
||||
}
|
||||
|
||||
)";
|
||||
EXPECT_EQ(result(), expected);
|
||||
}
|
||||
@@ -383,14 +380,12 @@ TEST_F(HlslGeneratorImplTest_MemberAccessor,
|
||||
|
||||
ASSERT_TRUE(gen.Generate(out)) << gen.error();
|
||||
auto* expected =
|
||||
R"(
|
||||
RWByteAddressBuffer data : register(u0, space1);
|
||||
R"(RWByteAddressBuffer data : register(u0, space1);
|
||||
|
||||
void main() {
|
||||
float x = asfloat(data.Load(52u));
|
||||
return;
|
||||
}
|
||||
|
||||
)";
|
||||
EXPECT_EQ(result(), expected);
|
||||
}
|
||||
@@ -417,14 +412,12 @@ TEST_F(HlslGeneratorImplTest_MemberAccessor,
|
||||
|
||||
ASSERT_TRUE(gen.Generate(out)) << gen.error();
|
||||
auto* expected =
|
||||
R"(
|
||||
RWByteAddressBuffer data : register(u0, space1);
|
||||
R"(RWByteAddressBuffer data : register(u0, space1);
|
||||
|
||||
void main() {
|
||||
int x = asint(data.Load(12u));
|
||||
return;
|
||||
}
|
||||
|
||||
)";
|
||||
EXPECT_EQ(result(), expected);
|
||||
}
|
||||
@@ -452,14 +445,12 @@ TEST_F(HlslGeneratorImplTest_MemberAccessor,
|
||||
|
||||
ASSERT_TRUE(gen.Generate(out)) << gen.error();
|
||||
auto* expected =
|
||||
R"(
|
||||
RWByteAddressBuffer data : register(u0, space1);
|
||||
R"(RWByteAddressBuffer data : register(u0, space1);
|
||||
|
||||
void main() {
|
||||
int x = asint(data.Load((4u + (4u * uint(((2 + 4) - 3))))));
|
||||
return;
|
||||
}
|
||||
|
||||
)";
|
||||
EXPECT_EQ(result(), expected);
|
||||
}
|
||||
@@ -484,14 +475,12 @@ TEST_F(HlslGeneratorImplTest_MemberAccessor, StorageBuffer_Store_ToArray) {
|
||||
|
||||
ASSERT_TRUE(gen.Generate(out)) << gen.error();
|
||||
auto* expected =
|
||||
R"(
|
||||
RWByteAddressBuffer data : register(u0, space1);
|
||||
R"(RWByteAddressBuffer data : register(u0, space1);
|
||||
|
||||
void main() {
|
||||
data.Store(12u, asuint(2));
|
||||
return;
|
||||
}
|
||||
|
||||
)";
|
||||
EXPECT_EQ(result(), expected);
|
||||
}
|
||||
@@ -527,14 +516,12 @@ TEST_F(HlslGeneratorImplTest_MemberAccessor, StorageBuffer_Load_MultiLevel) {
|
||||
|
||||
ASSERT_TRUE(gen.Generate(out)) << gen.error();
|
||||
auto* expected =
|
||||
R"(
|
||||
RWByteAddressBuffer data : register(u0, space1);
|
||||
R"(RWByteAddressBuffer data : register(u0, space1);
|
||||
|
||||
void main() {
|
||||
float3 x = asfloat(data.Load3(80u));
|
||||
return;
|
||||
}
|
||||
|
||||
)";
|
||||
EXPECT_EQ(result(), expected);
|
||||
}
|
||||
@@ -573,14 +560,12 @@ TEST_F(HlslGeneratorImplTest_MemberAccessor,
|
||||
|
||||
ASSERT_TRUE(gen.Generate(out)) << gen.error();
|
||||
auto* expected =
|
||||
R"(
|
||||
RWByteAddressBuffer data : register(u0, space1);
|
||||
R"(RWByteAddressBuffer data : register(u0, space1);
|
||||
|
||||
void main() {
|
||||
float2 x = asfloat(data.Load3(80u)).xy;
|
||||
return;
|
||||
}
|
||||
|
||||
)";
|
||||
EXPECT_EQ(result(), expected);
|
||||
}
|
||||
@@ -619,14 +604,12 @@ TEST_F(HlslGeneratorImplTest_MemberAccessor,
|
||||
|
||||
ASSERT_TRUE(gen.Generate(out)) << gen.error();
|
||||
auto* expected =
|
||||
R"(
|
||||
RWByteAddressBuffer data : register(u0, space1);
|
||||
R"(RWByteAddressBuffer data : register(u0, space1);
|
||||
|
||||
void main() {
|
||||
float x = asfloat(data.Load(84u));
|
||||
return;
|
||||
}
|
||||
|
||||
)";
|
||||
EXPECT_EQ(result(), expected);
|
||||
}
|
||||
@@ -665,14 +648,12 @@ TEST_F(HlslGeneratorImplTest_MemberAccessor,
|
||||
|
||||
ASSERT_TRUE(gen.Generate(out)) << gen.error();
|
||||
auto* expected =
|
||||
R"(
|
||||
RWByteAddressBuffer data : register(u0, space1);
|
||||
R"(RWByteAddressBuffer data : register(u0, space1);
|
||||
|
||||
void main() {
|
||||
float x = asfloat(data.Load(84u));
|
||||
return;
|
||||
}
|
||||
|
||||
)";
|
||||
EXPECT_EQ(result(), expected);
|
||||
}
|
||||
@@ -707,14 +688,12 @@ TEST_F(HlslGeneratorImplTest_MemberAccessor, StorageBuffer_Store_MultiLevel) {
|
||||
|
||||
ASSERT_TRUE(gen.Generate(out)) << gen.error();
|
||||
auto* expected =
|
||||
R"(
|
||||
RWByteAddressBuffer data : register(u0, space1);
|
||||
R"(RWByteAddressBuffer data : register(u0, space1);
|
||||
|
||||
void main() {
|
||||
data.Store3(80u, asuint(float3(1.0f, 2.0f, 3.0f)));
|
||||
return;
|
||||
}
|
||||
|
||||
)";
|
||||
EXPECT_EQ(result(), expected);
|
||||
}
|
||||
@@ -753,14 +732,12 @@ TEST_F(HlslGeneratorImplTest_MemberAccessor,
|
||||
|
||||
ASSERT_TRUE(gen.Generate(out)) << gen.error();
|
||||
auto* expected =
|
||||
R"(
|
||||
RWByteAddressBuffer data : register(u0, space1);
|
||||
R"(RWByteAddressBuffer data : register(u0, space1);
|
||||
|
||||
void main() {
|
||||
data.Store(84u, asuint(1.0f));
|
||||
return;
|
||||
}
|
||||
|
||||
)";
|
||||
EXPECT_EQ(result(), expected);
|
||||
}
|
||||
|
||||
@@ -56,8 +56,7 @@ TEST_F(HlslSanitizerTest, ArrayLength_DEPRECATED) {
|
||||
ASSERT_TRUE(gen.Generate(out)) << gen.error();
|
||||
|
||||
auto got = result();
|
||||
auto* expect = R"(
|
||||
ByteAddressBuffer sb : register(t0, space1);
|
||||
auto* expect = R"(ByteAddressBuffer sb : register(t0, space1);
|
||||
|
||||
void main() {
|
||||
uint tint_symbol_1 = 0u;
|
||||
@@ -66,7 +65,6 @@ void main() {
|
||||
uint len = tint_symbol_2;
|
||||
return;
|
||||
}
|
||||
|
||||
)";
|
||||
EXPECT_EQ(expect, got);
|
||||
}
|
||||
@@ -94,8 +92,7 @@ TEST_F(HlslSanitizerTest, Call_ArrayLength) {
|
||||
ASSERT_TRUE(gen.Generate(out)) << gen.error();
|
||||
|
||||
auto got = result();
|
||||
auto* expect = R"(
|
||||
ByteAddressBuffer b : register(t1, space2);
|
||||
auto* expect = R"(ByteAddressBuffer b : register(t1, space2);
|
||||
|
||||
void a_func() {
|
||||
uint tint_symbol_1 = 0u;
|
||||
@@ -104,7 +101,6 @@ void a_func() {
|
||||
uint len = tint_symbol_2;
|
||||
return;
|
||||
}
|
||||
|
||||
)";
|
||||
EXPECT_EQ(expect, got);
|
||||
}
|
||||
@@ -136,8 +132,7 @@ TEST_F(HlslSanitizerTest, Call_ArrayLength_OtherMembersInStruct) {
|
||||
ASSERT_TRUE(gen.Generate(out)) << gen.error();
|
||||
|
||||
auto got = result();
|
||||
auto* expect = R"(
|
||||
ByteAddressBuffer b : register(t1, space2);
|
||||
auto* expect = R"(ByteAddressBuffer b : register(t1, space2);
|
||||
|
||||
void a_func() {
|
||||
uint tint_symbol_1 = 0u;
|
||||
@@ -146,7 +141,6 @@ void a_func() {
|
||||
uint len = tint_symbol_2;
|
||||
return;
|
||||
}
|
||||
|
||||
)";
|
||||
|
||||
EXPECT_EQ(expect, got);
|
||||
@@ -180,8 +174,7 @@ TEST_F(HlslSanitizerTest, Call_ArrayLength_ViaLets) {
|
||||
ASSERT_TRUE(gen.Generate(out)) << gen.error();
|
||||
|
||||
auto got = result();
|
||||
auto* expect = R"(
|
||||
ByteAddressBuffer b : register(t1, space2);
|
||||
auto* expect = R"(ByteAddressBuffer b : register(t1, space2);
|
||||
|
||||
void a_func() {
|
||||
uint tint_symbol_1 = 0u;
|
||||
@@ -190,7 +183,6 @@ void a_func() {
|
||||
uint len = tint_symbol_2;
|
||||
return;
|
||||
}
|
||||
|
||||
)";
|
||||
|
||||
EXPECT_EQ(expect, got);
|
||||
@@ -219,7 +211,6 @@ TEST_F(HlslSanitizerTest, PromoteArrayInitializerToConstVar) {
|
||||
int pos = tint_symbol[3];
|
||||
return;
|
||||
}
|
||||
|
||||
)";
|
||||
EXPECT_EQ(expect, got);
|
||||
}
|
||||
@@ -259,7 +250,6 @@ void main() {
|
||||
float3 pos = tint_symbol.b;
|
||||
return;
|
||||
}
|
||||
|
||||
)";
|
||||
EXPECT_EQ(expect, got);
|
||||
}
|
||||
@@ -293,7 +283,6 @@ TEST_F(HlslSanitizerTest, InlinePtrLetsBasic) {
|
||||
int x = v;
|
||||
return;
|
||||
}
|
||||
|
||||
)";
|
||||
EXPECT_EQ(expect, got);
|
||||
}
|
||||
@@ -337,7 +326,6 @@ TEST_F(HlslSanitizerTest, InlinePtrLetsComplexChain) {
|
||||
float f = m[2][1];
|
||||
return;
|
||||
}
|
||||
|
||||
)";
|
||||
EXPECT_EQ(expect, got);
|
||||
}
|
||||
@@ -386,7 +374,6 @@ void main() {
|
||||
int r = x(v);
|
||||
return;
|
||||
}
|
||||
|
||||
)";
|
||||
EXPECT_EQ(expect, got);
|
||||
}
|
||||
|
||||
@@ -30,7 +30,6 @@ TEST_F(HlslGeneratorImplTest, Generate) {
|
||||
ASSERT_TRUE(gen.Generate(out)) << gen.error();
|
||||
EXPECT_EQ(result(), R"(void my_func() {
|
||||
}
|
||||
|
||||
)");
|
||||
}
|
||||
|
||||
|
||||
@@ -175,7 +175,7 @@ TEST_F(HlslGeneratorImplTest_Type, EmitType_StructDecl) {
|
||||
GeneratorImpl& gen = Build();
|
||||
|
||||
auto* sem_s = program->TypeOf(s)->As<sem::Struct>();
|
||||
ASSERT_TRUE(gen.EmitStructType(out, sem_s, "S")) << gen.error();
|
||||
ASSERT_TRUE(gen.EmitStructType(out, sem_s)) << gen.error();
|
||||
EXPECT_EQ(result(), R"(struct S {
|
||||
int a;
|
||||
float b;
|
||||
@@ -199,7 +199,7 @@ TEST_F(HlslGeneratorImplTest_Type, EmitType_StructDecl_OmittedIfStorageBuffer) {
|
||||
GeneratorImpl& gen = Build();
|
||||
|
||||
auto* sem_s = program->TypeOf(s)->As<sem::Struct>();
|
||||
ASSERT_TRUE(gen.EmitStructType(out, sem_s, "S")) << gen.error();
|
||||
ASSERT_TRUE(gen.EmitStructType(out, sem_s)) << gen.error();
|
||||
EXPECT_EQ(result(), "");
|
||||
}
|
||||
|
||||
@@ -264,26 +264,6 @@ TEST_F(HlslGeneratorImplTest_Type, EmitType_Struct_NameCollision) {
|
||||
)"));
|
||||
}
|
||||
|
||||
// TODO(dsinclair): How to translate [[block]]
|
||||
TEST_F(HlslGeneratorImplTest_Type, DISABLED_EmitType_Struct_WithDecoration) {
|
||||
auto* s = Structure("S",
|
||||
{
|
||||
Member("a", ty.i32()),
|
||||
Member("b", ty.f32()),
|
||||
},
|
||||
{create<ast::StructBlockDecoration>()});
|
||||
Global("g", ty.Of(s), ast::StorageClass::kPrivate);
|
||||
|
||||
GeneratorImpl& gen = Build();
|
||||
|
||||
auto* sem_s = program->TypeOf(s)->As<sem::Struct>();
|
||||
ASSERT_TRUE(gen.EmitStructType(out, sem_s, "B")) << gen.error();
|
||||
EXPECT_EQ(result(), R"(struct B {
|
||||
int a;
|
||||
float b;
|
||||
})");
|
||||
}
|
||||
|
||||
TEST_F(HlslGeneratorImplTest_Type, EmitType_U32) {
|
||||
auto* u32 = create<sem::U32>();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user