diff --git a/src/ast/type/alias_type.cc b/src/ast/type/alias_type.cc index cce05e3b68..d8f67d9340 100644 --- a/src/ast/type/alias_type.cc +++ b/src/ast/type/alias_type.cc @@ -35,6 +35,10 @@ std::string AliasType::type_name() const { return "__alias_" + name_ + subtype_->type_name(); } +uint64_t AliasType::MinBufferBindingSize() const { + return subtype_->MinBufferBindingSize(); +} + } // namespace type } // namespace ast } // namespace tint diff --git a/src/ast/type/alias_type.h b/src/ast/type/alias_type.h index d407306f59..bc7505a745 100644 --- a/src/ast/type/alias_type.h +++ b/src/ast/type/alias_type.h @@ -23,7 +23,7 @@ namespace tint { namespace ast { namespace type { -/// A type alias type. Holds a name a pointer to another type. +/// A type alias type. Holds a name and pointer to another type. class AliasType : public Type { public: /// Constructor @@ -45,6 +45,10 @@ class AliasType : public Type { /// @returns the name for this type std::string type_name() const override; + /// @returns minimum size required for this type, in bytes. + /// 0 for non-host shareable types. + uint64_t MinBufferBindingSize() const override; + private: std::string name_; Type* subtype_ = nullptr; diff --git a/src/ast/type/array_type.cc b/src/ast/type/array_type.cc index 71b386138a..179f6de3dd 100644 --- a/src/ast/type/array_type.cc +++ b/src/ast/type/array_type.cc @@ -33,6 +33,18 @@ bool ArrayType::IsArray() const { return true; } +uint64_t ArrayType::MinBufferBindingSize() const { + if (IsRuntimeArray()) { + return array_stride(); + } + + if (has_array_stride()) { + return size_ * array_stride(); + } + + return size_ * type()->MinBufferBindingSize(); +} + uint32_t ArrayType::array_stride() const { for (const auto& deco : decos_) { if (deco->IsStride()) { diff --git a/src/ast/type/array_type.h b/src/ast/type/array_type.h index 9a84d1e901..fceca8e022 100644 --- a/src/ast/type/array_type.h +++ b/src/ast/type/array_type.h @@ -46,6 +46,10 @@ class ArrayType : public Type { /// i.e. the size is determined at runtime bool IsRuntimeArray() const { return size_ == 0; } + /// @returns minimum size required for this type, in bytes. + /// 0 for non-host shareable types. + uint64_t MinBufferBindingSize() const override; + /// Sets the array decorations /// @param decos the decorations to set void set_decorations(ast::ArrayDecorationList decos) { diff --git a/src/ast/type/f32_type.cc b/src/ast/type/f32_type.cc index bac892b2c6..2306bd03bf 100644 --- a/src/ast/type/f32_type.cc +++ b/src/ast/type/f32_type.cc @@ -30,6 +30,10 @@ std::string F32Type::type_name() const { return "__f32"; } +uint64_t F32Type::MinBufferBindingSize() const { + return 4; +} + } // namespace type } // namespace ast } // namespace tint diff --git a/src/ast/type/f32_type.h b/src/ast/type/f32_type.h index a97583773b..8a0a5b64e7 100644 --- a/src/ast/type/f32_type.h +++ b/src/ast/type/f32_type.h @@ -37,6 +37,10 @@ class F32Type : public Type { /// @returns the name for this type std::string type_name() const override; + + /// @returns minimum size required for this type, in bytes. + /// 0 for non-host shareable types. + uint64_t MinBufferBindingSize() const override; }; } // namespace type diff --git a/src/ast/type/i32_type.cc b/src/ast/type/i32_type.cc index fd5ab0c71b..772f5cf7ef 100644 --- a/src/ast/type/i32_type.cc +++ b/src/ast/type/i32_type.cc @@ -30,6 +30,10 @@ std::string I32Type::type_name() const { return "__i32"; } +uint64_t I32Type::MinBufferBindingSize() const { + return 4; +} + } // namespace type } // namespace ast } // namespace tint diff --git a/src/ast/type/i32_type.h b/src/ast/type/i32_type.h index 3f8a213a1b..6c6b625b72 100644 --- a/src/ast/type/i32_type.h +++ b/src/ast/type/i32_type.h @@ -37,6 +37,10 @@ class I32Type : public Type { /// @returns the name for this type std::string type_name() const override; + + /// @returns minimum size required for this type, in bytes. + /// 0 for non-host shareable types. + uint64_t MinBufferBindingSize() const override; }; } // namespace type diff --git a/src/ast/type/matrix_type.cc b/src/ast/type/matrix_type.cc index d34208cc6c..bd60c6c98c 100644 --- a/src/ast/type/matrix_type.cc +++ b/src/ast/type/matrix_type.cc @@ -28,6 +28,8 @@ MatrixType::MatrixType(Type* subtype, uint32_t rows, uint32_t columns) assert(columns < 5); } +MatrixType::~MatrixType() = default; + bool MatrixType::IsMatrix() const { return true; } @@ -37,7 +39,9 @@ std::string MatrixType::type_name() const { subtype_->type_name(); } -MatrixType::~MatrixType() = default; +uint64_t MatrixType::MinBufferBindingSize() const { + return rows_ * columns_ * subtype_->MinBufferBindingSize(); +} } // namespace type } // namespace ast diff --git a/src/ast/type/matrix_type.h b/src/ast/type/matrix_type.h index 35cd932c27..6a42d3527c 100644 --- a/src/ast/type/matrix_type.h +++ b/src/ast/type/matrix_type.h @@ -48,6 +48,10 @@ class MatrixType : public Type { /// @returns the name for this type std::string type_name() const override; + /// @returns minimum size required for this type, in bytes. + /// 0 for non-host shareable types. + uint64_t MinBufferBindingSize() const override; + private: Type* subtype_ = nullptr; uint32_t rows_ = 2; diff --git a/src/ast/type/pointer_type.cc b/src/ast/type/pointer_type.cc index e6083c811b..507dbedb0f 100644 --- a/src/ast/type/pointer_type.cc +++ b/src/ast/type/pointer_type.cc @@ -25,6 +25,10 @@ bool PointerType::IsPointer() const { return true; } +uint64_t PointerType::MinBufferBindingSize() const { + return 4; +} + std::string PointerType::type_name() const { std::ostringstream out; out << "__ptr_" << storage_class_ << subtype_->type_name(); diff --git a/src/ast/type/pointer_type.h b/src/ast/type/pointer_type.h index fa9797551c..dbf7e5c842 100644 --- a/src/ast/type/pointer_type.h +++ b/src/ast/type/pointer_type.h @@ -39,6 +39,10 @@ class PointerType : public Type { /// @returns true if the type is a pointer type bool IsPointer() const override; + /// @returns minimum size required for this type, in bytes. + /// 0 for non-host shareable types. + uint64_t MinBufferBindingSize() const override; + /// @returns the pointee type Type* type() const { return subtype_; } /// @returns the storage class of the pointer diff --git a/src/ast/type/struct_type.cc b/src/ast/type/struct_type.cc index 8f0a017a8e..c52fdb50a9 100644 --- a/src/ast/type/struct_type.cc +++ b/src/ast/type/struct_type.cc @@ -25,6 +25,8 @@ StructType::StructType(const std::string& name, std::unique_ptr impl) StructType::StructType(StructType&&) = default; +StructType::~StructType() = default; + bool StructType::IsStruct() const { return true; } @@ -33,7 +35,21 @@ std::string StructType::type_name() const { return "__struct_" + name_; } -StructType::~StructType() = default; +uint64_t StructType::MinBufferBindingSize() const { + if (!struct_->members().size()) { + return 0; + } + + const auto& last_member = struct_->members().back(); + + // If there is no offset, then this is not a host-shareable struct, returning + // 0 indicates this to the caller. + if (!last_member->has_offset_decoration()) { + return 0; + } + + return last_member->offset() + last_member->type()->MinBufferBindingSize(); +} } // namespace type } // namespace ast diff --git a/src/ast/type/struct_type.h b/src/ast/type/struct_type.h index 9621adb2ca..ec5a7a11c4 100644 --- a/src/ast/type/struct_type.h +++ b/src/ast/type/struct_type.h @@ -48,9 +48,13 @@ class StructType : public Type { /// @returns the struct name Struct* impl() const { return struct_.get(); } - /// @returns the name for th type + /// @returns the name for the type std::string type_name() const override; + /// @returns minimum size required for this type, in bytes. + /// 0 for non-host shareable types. + uint64_t MinBufferBindingSize() const override; + private: std::string name_; std::unique_ptr struct_; diff --git a/src/ast/type/type.cc b/src/ast/type/type.cc index 7565d737ce..ee8fa423fb 100644 --- a/src/ast/type/type.cc +++ b/src/ast/type/type.cc @@ -109,6 +109,10 @@ bool Type::IsVoid() const { return false; } +uint64_t Type::MinBufferBindingSize() const { + return 0; +} + bool Type::is_scalar() { return is_float_scalar() || is_integer_scalar() || IsBool(); } diff --git a/src/ast/type/type.h b/src/ast/type/type.h index 5d0dbe7076..aabde8d569 100644 --- a/src/ast/type/type.h +++ b/src/ast/type/type.h @@ -72,6 +72,10 @@ class Type { /// @returns the name for this type. The |type_name| is unique over all types. virtual std::string type_name() const = 0; + /// @returns minimum size required for this type, in bytes. + /// 0 for non-host shareable types. + virtual uint64_t MinBufferBindingSize() const; + /// @returns the pointee type if this is a pointer, |this| otherwise Type* UnwrapPtrIfNeeded(); diff --git a/src/ast/type/u32_type.cc b/src/ast/type/u32_type.cc index 2bb7a3f2d8..ebc65f506d 100644 --- a/src/ast/type/u32_type.cc +++ b/src/ast/type/u32_type.cc @@ -32,6 +32,10 @@ std::string U32Type::type_name() const { return "__u32"; } +uint64_t U32Type::MinBufferBindingSize() const { + return 4; +} + } // namespace type } // namespace ast } // namespace tint diff --git a/src/ast/type/u32_type.h b/src/ast/type/u32_type.h index 21dd9ea229..27b40dbf6b 100644 --- a/src/ast/type/u32_type.h +++ b/src/ast/type/u32_type.h @@ -37,6 +37,10 @@ class U32Type : public Type { /// @returns the name for th type std::string type_name() const override; + + /// @returns minimum size required for this type, in bytes. + /// 0 for non-host shareable types. + uint64_t MinBufferBindingSize() const override; }; } // namespace type diff --git a/src/ast/type/vector_type.cc b/src/ast/type/vector_type.cc index 2335b94bdc..645ac2f576 100644 --- a/src/ast/type/vector_type.cc +++ b/src/ast/type/vector_type.cc @@ -38,6 +38,10 @@ std::string VectorType::type_name() const { return "__vec_" + std::to_string(size_) + subtype_->type_name(); } +uint64_t VectorType::MinBufferBindingSize() const { + return size_ * subtype_->MinBufferBindingSize(); +} + } // namespace type } // namespace ast } // namespace tint diff --git a/src/ast/type/vector_type.h b/src/ast/type/vector_type.h index 6ea2c8fac6..3862274f0f 100644 --- a/src/ast/type/vector_type.h +++ b/src/ast/type/vector_type.h @@ -45,6 +45,10 @@ class VectorType : public Type { /// @returns the name for th type std::string type_name() const override; + /// @returns minimum size required for this type, in bytes. + /// 0 for non-host shareable types. + uint64_t MinBufferBindingSize() const override; + private: Type* subtype_ = nullptr; uint32_t size_ = 2; diff --git a/src/inspector/inspector.cc b/src/inspector/inspector.cc index b5d68f70ba..239dcbb613 100644 --- a/src/inspector/inspector.cc +++ b/src/inspector/inspector.cc @@ -25,6 +25,7 @@ #include "src/ast/null_literal.h" #include "src/ast/scalar_constructor_expression.h" #include "src/ast/sint_literal.h" +#include "src/ast/type/struct_type.h" #include "src/ast/uint_literal.h" namespace tint { @@ -134,5 +135,43 @@ std::map Inspector::GetConstantIDs() { return result; } +std::vector Inspector::GetUniformBufferResourceBindings( + const std::string& entry_point) { + auto* func = module_.FindFunctionByName(entry_point); + if (!func) { + error_ += entry_point + " was not found!"; + return {}; + } + + if (!func->IsEntryPoint()) { + error_ += entry_point + " is not an entry point!"; + return {}; + } + + std::vector result; + + for (auto& ruv : func->referenced_uniform_variables()) { + ResourceBinding entry; + ast::Variable* var = nullptr; + ast::Function::BindingInfo binding_info; + std::tie(var, binding_info) = ruv; + if (!var->type()->IsStruct()) { + continue; + } + + if (!var->type()->AsStruct()->IsBlockDecorated()) { + continue; + } + + entry.bind_group = binding_info.set->value(); + entry.binding = binding_info.binding->value(); + entry.min_buffer_binding_size = var->type()->MinBufferBindingSize(); + + result.push_back(std::move(entry)); + } + + return result; +} + } // namespace inspector } // namespace tint diff --git a/src/inspector/inspector.h b/src/inspector/inspector.h index 9559fac125..a0d82f2a91 100644 --- a/src/inspector/inspector.h +++ b/src/inspector/inspector.h @@ -29,6 +29,16 @@ namespace tint { namespace inspector { +/// Container for information about how a resource is bound +struct ResourceBinding { + /// Bind group the binding belongs + uint32_t bind_group; + /// Identifier to identify this binding within the bind group + uint32_t binding; + /// Minimum size required for this binding, in bytes. + uint64_t min_buffer_binding_size; +}; + /// Extracts information from a module class Inspector { public: @@ -48,6 +58,11 @@ class Inspector { /// @returns map of const_id to initial value std::map GetConstantIDs(); + /// @param entry_point name of the entry point to get information about. + /// @returns vector of all of the bindings for Uniform buffers. + std::vector GetUniformBufferResourceBindings( + const std::string& entry_point); + private: const ast::Module& module_; std::string error_; diff --git a/src/inspector/inspector_test.cc b/src/inspector/inspector_test.cc index 9a895cc57e..d136ad8f8f 100644 --- a/src/inspector/inspector_test.cc +++ b/src/inspector/inspector_test.cc @@ -24,17 +24,27 @@ #include "src/ast/float_literal.h" #include "src/ast/function.h" #include "src/ast/identifier_expression.h" +#include "src/ast/member_accessor_expression.h" #include "src/ast/null_literal.h" #include "src/ast/pipeline_stage.h" #include "src/ast/return_statement.h" #include "src/ast/scalar_constructor_expression.h" #include "src/ast/sint_literal.h" #include "src/ast/stage_decoration.h" +#include "src/ast/struct_decoration.h" +#include "src/ast/struct_member.h" +#include "src/ast/struct_member_decoration.h" +#include "src/ast/struct_member_offset_decoration.h" +#include "src/ast/type/array_type.h" #include "src/ast/type/bool_type.h" #include "src/ast/type/f32_type.h" #include "src/ast/type/i32_type.h" +#include "src/ast/type/matrix_type.h" +#include "src/ast/type/pointer_type.h" +#include "src/ast/type/struct_type.h" #include "src/ast/type/type.h" #include "src/ast/type/u32_type.h" +#include "src/ast/type/vector_type.h" #include "src/ast/type/void_type.h" #include "src/ast/uint_literal.h" #include "src/ast/variable_decl_statement.h" @@ -58,7 +68,7 @@ class InspectorHelper { /// Generates an empty function /// @param name name of the function created /// @returns a function object - std::unique_ptr GenerateEmptyBodyFunction(std::string name) { + std::unique_ptr MakeEmptyBodyFunction(std::string name) { auto body = std::make_unique(); body->append(std::make_unique()); std::unique_ptr func = @@ -71,9 +81,8 @@ class InspectorHelper { /// @param caller name of the function created /// @param callee name of the function to be called /// @returns a function object - std::unique_ptr GenerateCallerBodyFunction( - std::string caller, - std::string callee) { + std::unique_ptr MakeCallerBodyFunction(std::string caller, + std::string callee) { auto body = std::make_unique(); auto ident_expr = std::make_unique(callee); auto call_expr = std::make_unique( @@ -89,7 +98,7 @@ class InspectorHelper { /// Add In/Out variables to the global variables /// @param inout_vars tuples of {in, out} that will be added as entries to the /// global variables - void CreateInOutVariables( + void AddInOutVariables( std::vector> inout_vars) { for (auto inout : inout_vars) { std::string in, out; @@ -108,7 +117,7 @@ class InspectorHelper { /// @param inout_vars tuples of {in, out} that will be converted into out = in /// calls in the function body /// @returns a function object - std::unique_ptr GenerateInOutVariableBodyFunction( + std::unique_ptr MakeInOutVariableBodyFunction( std::string name, std::vector> inout_vars) { auto body = std::make_unique(); @@ -133,7 +142,7 @@ class InspectorHelper { /// @param inout_vars tuples of {in, out} that will be converted into out = in /// calls in the function body /// @returns a function object - std::unique_ptr GenerateInOutVariableCallerBodyFunction( + std::unique_ptr MakeInOutVariableCallerBodyFunction( std::string caller, std::string callee, std::vector> inout_vars) { @@ -163,10 +172,10 @@ class InspectorHelper { /// @param val value to initialize the variable with, if NULL no initializer /// will be added. template - void CreateConstantID(std::string name, - uint32_t id, - ast::type::Type* type, - T* val) { + void AddConstantID(std::string name, + uint32_t id, + ast::type::Type* type, + T* val) { auto dvar = std::make_unique( std::make_unique(name, ast::StorageClass::kNone, type)); dvar->set_is_const(true); @@ -180,35 +189,53 @@ class InspectorHelper { mod()->AddGlobalVariable(std::move(dvar)); } + /// Generates an ast::Literal for the given value + /// @tparam T C++ type of the literal, must agree with type + /// @returns a Literal of the expected type and value template std::unique_ptr MakeLiteral(ast::type::Type*, T*) { return nullptr; } + /// @param type AST type of the literal, must resolve to BoolLiteral + /// @param val scalar value for the literal to contain + /// @returns a Literal of the expected type and value template <> std::unique_ptr MakeLiteral(ast::type::Type* type, bool* val) { return std::make_unique(type, *val); } + /// @param type AST type of the literal, must resolve to UIntLiteral + /// @param val scalar value for the literal to contain + /// @returns a Literal of the expected type and value template <> std::unique_ptr MakeLiteral(ast::type::Type* type, uint32_t* val) { return std::make_unique(type, *val); } + /// @param type AST type of the literal, must resolve to IntLiteral + /// @param val scalar value for the literal to contain + /// @returns a Literal of the expected type and value template <> std::unique_ptr MakeLiteral(ast::type::Type* type, int32_t* val) { return std::make_unique(type, *val); } + /// @param type AST type of the literal, must resolve to FloattLiteral + /// @param val scalar value for the literal to contain + /// @returns a Literal of the expected type and value template <> std::unique_ptr MakeLiteral(ast::type::Type* type, float* val) { return std::make_unique(type, *val); } + /// @param vec Vector of strings to be searched + /// @param str String to be searching for + /// @returns true if str is in vec, otherwise false bool ContainsString(const std::vector& vec, const std::string& str) { for (auto& s : vec) { @@ -219,6 +246,105 @@ class InspectorHelper { return false; } + /// Builds a string for accessing a member in a generated struct + /// @param idx index of member + /// @param type type of member + /// @returns a string for the member + std::string StructMemberName(size_t idx, ast::type::Type* type) { + return std::to_string(idx) + type->type_name(); + } + + /// Generates a struct type appropriate for using in a UBO + /// @param name name for the type + /// @param members_info a vector of {type, offset} where each entry is the + /// type and offset of a member of the struct + /// @returns a struct type suitable to use for a UBO + std::unique_ptr MakeUBOStructType( + const std::string& name, + std::vector> members_info) { + ast::StructMemberList members; + for (auto& member_info : members_info) { + ast::type::Type* type; + uint32_t offset; + std::tie(type, offset) = member_info; + + ast::StructMemberDecorationList deco; + deco.push_back( + std::make_unique(offset)); + + members.push_back(std::make_unique( + StructMemberName(members.size(), type), type, std::move(deco))); + } + ast::StructDecorationList decos; + decos.push_back(ast::StructDecoration::kBlock); + + auto str = + std::make_unique(std::move(decos), std::move(members)); + + return std::make_unique(name, std::move(str)); + } + + /// Adds a UBO variable to the module + /// @param name the name of the variable + /// @param struct_type the type to use + /// @param set the binding group/set to use for the UBO + /// @param binding the binding number to use for the UBO + void AddUBO(const std::string& name, + ast::type::StructType* struct_type, + uint32_t set, + uint32_t binding) { + auto var = std::make_unique( + std::make_unique(name, ast::StorageClass::kUniform, + struct_type)); + ast::VariableDecorationList decorations; + + decorations.push_back(std::make_unique(binding)); + decorations.push_back(std::make_unique(set)); + var->set_decorations(std::move(decorations)); + + mod()->AddGlobalVariable(std::move(var)); + } + + /// Generates a function that references a specific UBO + /// @param func_name name of the function created + /// @param ubo_name name of the UBO to be accessed + /// @param member_idx index of the member to access + /// @returns a function that references all of the UBO members specified + std::unique_ptr MakeUBOReferenceBodyFunction( + std::string func_name, + std::string ubo_name, + std::vector> members) { + auto body = std::make_unique(); + + for (auto member : members) { + size_t member_idx; + ast::type::Type* member_type; + std::tie(member_idx, member_type) = member; + std::string member_name = StructMemberName(member_idx, member_type); + body->append(std::make_unique( + std::make_unique( + "local" + member_name, ast::StorageClass::kNone, member_type))); + } + + for (auto member : members) { + size_t member_idx; + ast::type::Type* member_type; + std::tie(member_idx, member_type) = member; + std::string member_name = StructMemberName(member_idx, member_type); + body->append(std::make_unique( + std::make_unique("local" + member_name), + std::make_unique( + std::make_unique(ubo_name), + std::make_unique(member_name)))); + } + + body->append(std::make_unique()); + auto func = std::make_unique(func_name, ast::VariableList(), + void_type()); + func->set_body(std::move(body)); + return func; + } + ast::Module* mod() { return &mod_; } TypeDeterminer* td() { return td_.get(); } Inspector* inspector() { return inspector_.get(); } @@ -227,6 +353,14 @@ class InspectorHelper { ast::type::F32Type* f32_type() { return &f32_type_; } ast::type::I32Type* i32_type() { return &i32_type_; } ast::type::U32Type* u32_type() { return &u32_type_; } + ast::type::ArrayType* u32_array_type(uint32_t count) { + static std::map> memo; + if (memo.find(count) == memo.end()) { + memo[count] = std::make_unique(u32_type(), count); + } + + return memo[count].get(); + } ast::type::VoidType* void_type() { return &void_type_; } private: @@ -245,6 +379,8 @@ class InspectorHelper { class InspectorTest : public InspectorHelper, public testing::Test {}; class InspectorGetEntryPointTest : public InspectorTest {}; +class InspectorGetConstantIDsTest : public InspectorTest {}; +class InspectorGetUniformBufferResourceBindings : public InspectorTest {}; TEST_F(InspectorGetEntryPointTest, NoFunctions) { auto result = inspector()->GetEntryPoints(); @@ -254,7 +390,7 @@ TEST_F(InspectorGetEntryPointTest, NoFunctions) { } TEST_F(InspectorGetEntryPointTest, NoEntryPoints) { - mod()->AddFunction(GenerateEmptyBodyFunction("foo")); + mod()->AddFunction(MakeEmptyBodyFunction("foo")); auto result = inspector()->GetEntryPoints(); ASSERT_FALSE(inspector()->has_error()); @@ -263,7 +399,7 @@ TEST_F(InspectorGetEntryPointTest, NoEntryPoints) { } TEST_F(InspectorGetEntryPointTest, OneEntryPoint) { - auto foo = GenerateEmptyBodyFunction("foo"); + auto foo = MakeEmptyBodyFunction("foo"); foo->add_decoration( std::make_unique(ast::PipelineStage::kVertex)); mod()->AddFunction(std::move(foo)); @@ -277,12 +413,12 @@ TEST_F(InspectorGetEntryPointTest, OneEntryPoint) { } TEST_F(InspectorGetEntryPointTest, MultipleEntryPoints) { - auto foo = GenerateEmptyBodyFunction("foo"); + auto foo = MakeEmptyBodyFunction("foo"); foo->add_decoration( std::make_unique(ast::PipelineStage::kVertex)); mod()->AddFunction(std::move(foo)); - auto bar = GenerateEmptyBodyFunction("bar"); + auto bar = MakeEmptyBodyFunction("bar"); bar->add_decoration( std::make_unique(ast::PipelineStage::kCompute)); mod()->AddFunction(std::move(bar)); @@ -298,15 +434,15 @@ TEST_F(InspectorGetEntryPointTest, MultipleEntryPoints) { } TEST_F(InspectorGetEntryPointTest, MixFunctionsAndEntryPoints) { - auto func = GenerateEmptyBodyFunction("func"); + auto func = MakeEmptyBodyFunction("func"); mod()->AddFunction(std::move(func)); - auto foo = GenerateCallerBodyFunction("foo", "func"); + auto foo = MakeCallerBodyFunction("foo", "func"); foo->add_decoration( std::make_unique(ast::PipelineStage::kVertex)); mod()->AddFunction(std::move(foo)); - auto bar = GenerateCallerBodyFunction("bar", "func"); + auto bar = MakeCallerBodyFunction("bar", "func"); bar->add_decoration( std::make_unique(ast::PipelineStage::kFragment)); mod()->AddFunction(std::move(bar)); @@ -322,7 +458,7 @@ TEST_F(InspectorGetEntryPointTest, MixFunctionsAndEntryPoints) { } TEST_F(InspectorGetEntryPointTest, DefaultWorkgroupSize) { - auto foo = GenerateCallerBodyFunction("foo", "func"); + auto foo = MakeCallerBodyFunction("foo", "func"); foo->add_decoration( std::make_unique(ast::PipelineStage::kVertex)); mod()->AddFunction(std::move(foo)); @@ -339,7 +475,7 @@ TEST_F(InspectorGetEntryPointTest, DefaultWorkgroupSize) { } TEST_F(InspectorGetEntryPointTest, NonDefaultWorkgroupSize) { - auto foo = GenerateEmptyBodyFunction("foo"); + auto foo = MakeEmptyBodyFunction("foo"); foo->add_decoration( std::make_unique(ast::PipelineStage::kCompute)); foo->add_decoration(std::make_unique(8u, 2u, 1u)); @@ -357,10 +493,10 @@ TEST_F(InspectorGetEntryPointTest, NonDefaultWorkgroupSize) { } TEST_F(InspectorGetEntryPointTest, NoInOutVariables) { - auto func = GenerateEmptyBodyFunction("func"); + auto func = MakeEmptyBodyFunction("func"); mod()->AddFunction(std::move(func)); - auto foo = GenerateCallerBodyFunction("foo", "func"); + auto foo = MakeCallerBodyFunction("foo", "func"); foo->add_decoration( std::make_unique(ast::PipelineStage::kVertex)); mod()->AddFunction(std::move(foo)); @@ -374,9 +510,9 @@ TEST_F(InspectorGetEntryPointTest, NoInOutVariables) { } TEST_F(InspectorGetEntryPointTest, EntryPointInOutVariables) { - CreateInOutVariables({{"in_var", "out_var"}}); + AddInOutVariables({{"in_var", "out_var"}}); - auto foo = GenerateInOutVariableBodyFunction("foo", {{"in_var", "out_var"}}); + auto foo = MakeInOutVariableBodyFunction("foo", {{"in_var", "out_var"}}); foo->add_decoration( std::make_unique(ast::PipelineStage::kVertex)); mod()->AddFunction(std::move(foo)); @@ -395,13 +531,12 @@ TEST_F(InspectorGetEntryPointTest, EntryPointInOutVariables) { } TEST_F(InspectorGetEntryPointTest, FunctionInOutVariables) { - CreateInOutVariables({{"in_var", "out_var"}}); + AddInOutVariables({{"in_var", "out_var"}}); - auto func = - GenerateInOutVariableBodyFunction("func", {{"in_var", "out_var"}}); + auto func = MakeInOutVariableBodyFunction("func", {{"in_var", "out_var"}}); mod()->AddFunction(std::move(func)); - auto foo = GenerateCallerBodyFunction("foo", "func"); + auto foo = MakeCallerBodyFunction("foo", "func"); foo->add_decoration( std::make_unique(ast::PipelineStage::kVertex)); mod()->AddFunction(std::move(foo)); @@ -420,14 +555,13 @@ TEST_F(InspectorGetEntryPointTest, FunctionInOutVariables) { } TEST_F(InspectorGetEntryPointTest, RepeatedInOutVariables) { - CreateInOutVariables({{"in_var", "out_var"}}); + AddInOutVariables({{"in_var", "out_var"}}); - auto func = - GenerateInOutVariableBodyFunction("func", {{"in_var", "out_var"}}); + auto func = MakeInOutVariableBodyFunction("func", {{"in_var", "out_var"}}); mod()->AddFunction(std::move(func)); - auto foo = GenerateInOutVariableCallerBodyFunction("foo", "func", - {{"in_var", "out_var"}}); + auto foo = MakeInOutVariableCallerBodyFunction("foo", "func", + {{"in_var", "out_var"}}); foo->add_decoration( std::make_unique(ast::PipelineStage::kVertex)); mod()->AddFunction(std::move(foo)); @@ -446,9 +580,9 @@ TEST_F(InspectorGetEntryPointTest, RepeatedInOutVariables) { } TEST_F(InspectorGetEntryPointTest, EntryPointMultipleInOutVariables) { - CreateInOutVariables({{"in_var", "out_var"}, {"in2_var", "out2_var"}}); + AddInOutVariables({{"in_var", "out_var"}, {"in2_var", "out2_var"}}); - auto foo = GenerateInOutVariableBodyFunction( + auto foo = MakeInOutVariableBodyFunction( "foo", {{"in_var", "out_var"}, {"in2_var", "out2_var"}}); foo->add_decoration( std::make_unique(ast::PipelineStage::kVertex)); @@ -470,13 +604,13 @@ TEST_F(InspectorGetEntryPointTest, EntryPointMultipleInOutVariables) { } TEST_F(InspectorGetEntryPointTest, FunctionMultipleInOutVariables) { - CreateInOutVariables({{"in_var", "out_var"}, {"in2_var", "out2_var"}}); + AddInOutVariables({{"in_var", "out_var"}, {"in2_var", "out2_var"}}); - auto func = GenerateInOutVariableBodyFunction( + auto func = MakeInOutVariableBodyFunction( "func", {{"in_var", "out_var"}, {"in2_var", "out2_var"}}); mod()->AddFunction(std::move(func)); - auto foo = GenerateCallerBodyFunction("foo", "func"); + auto foo = MakeCallerBodyFunction("foo", "func"); foo->add_decoration( std::make_unique(ast::PipelineStage::kVertex)); mod()->AddFunction(std::move(foo)); @@ -497,14 +631,14 @@ TEST_F(InspectorGetEntryPointTest, FunctionMultipleInOutVariables) { } TEST_F(InspectorGetEntryPointTest, MultipleEntryPointsInOutVariables) { - CreateInOutVariables({{"in_var", "out_var"}, {"in2_var", "out2_var"}}); + AddInOutVariables({{"in_var", "out_var"}, {"in2_var", "out2_var"}}); - auto foo = GenerateInOutVariableBodyFunction("foo", {{"in_var", "out2_var"}}); + auto foo = MakeInOutVariableBodyFunction("foo", {{"in_var", "out2_var"}}); foo->add_decoration( std::make_unique(ast::PipelineStage::kVertex)); mod()->AddFunction(std::move(foo)); - auto bar = GenerateInOutVariableBodyFunction("bar", {{"in2_var", "out_var"}}); + auto bar = MakeInOutVariableBodyFunction("bar", {{"in2_var", "out_var"}}); bar->add_decoration( std::make_unique(ast::PipelineStage::kCompute)); mod()->AddFunction(std::move(bar)); @@ -530,19 +664,18 @@ TEST_F(InspectorGetEntryPointTest, MultipleEntryPointsInOutVariables) { } TEST_F(InspectorGetEntryPointTest, MultipleEntryPointsSharedInOutVariables) { - CreateInOutVariables({{"in_var", "out_var"}, {"in2_var", "out2_var"}}); + AddInOutVariables({{"in_var", "out_var"}, {"in2_var", "out2_var"}}); - auto func = - GenerateInOutVariableBodyFunction("func", {{"in2_var", "out2_var"}}); + auto func = MakeInOutVariableBodyFunction("func", {{"in2_var", "out2_var"}}); mod()->AddFunction(std::move(func)); - auto foo = GenerateInOutVariableCallerBodyFunction("foo", "func", - {{"in_var", "out_var"}}); + auto foo = MakeInOutVariableCallerBodyFunction("foo", "func", + {{"in_var", "out_var"}}); foo->add_decoration( std::make_unique(ast::PipelineStage::kVertex)); mod()->AddFunction(std::move(foo)); - auto bar = GenerateCallerBodyFunction("bar", "func"); + auto bar = MakeCallerBodyFunction("bar", "func"); bar->add_decoration( std::make_unique(ast::PipelineStage::kCompute)); mod()->AddFunction(std::move(bar)); @@ -569,12 +702,12 @@ TEST_F(InspectorGetEntryPointTest, MultipleEntryPointsSharedInOutVariables) { EXPECT_EQ("out2_var", result[1].output_variables[0]); } -TEST_F(InspectorGetEntryPointTest, BoolConstantIDs) { +TEST_F(InspectorGetConstantIDsTest, Bool) { bool val_true = true; bool val_false = false; - CreateConstantID("foo", 1, bool_type(), nullptr); - CreateConstantID("bar", 20, bool_type(), &val_true); - CreateConstantID("baz", 300, bool_type(), &val_false); + AddConstantID("foo", 1, bool_type(), nullptr); + AddConstantID("bar", 20, bool_type(), &val_true); + AddConstantID("baz", 300, bool_type(), &val_false); auto result = inspector()->GetConstantIDs(); ASSERT_EQ(3u, result.size()); @@ -591,10 +724,10 @@ TEST_F(InspectorGetEntryPointTest, BoolConstantIDs) { EXPECT_FALSE(result[300].AsBool()); } -TEST_F(InspectorGetEntryPointTest, U32ConstantIDs) { +TEST_F(InspectorGetConstantIDsTest, U32) { uint32_t val = 42; - CreateConstantID("foo", 1, u32_type(), nullptr); - CreateConstantID("bar", 20, u32_type(), &val); + AddConstantID("foo", 1, u32_type(), nullptr); + AddConstantID("bar", 20, u32_type(), &val); auto result = inspector()->GetConstantIDs(); ASSERT_EQ(2u, result.size()); @@ -607,12 +740,12 @@ TEST_F(InspectorGetEntryPointTest, U32ConstantIDs) { EXPECT_EQ(42u, result[20].AsU32()); } -TEST_F(InspectorGetEntryPointTest, I32ConstantIDs) { +TEST_F(InspectorGetConstantIDsTest, I32) { int32_t val_neg = -42; int32_t val_pos = 42; - CreateConstantID("foo", 1, i32_type(), nullptr); - CreateConstantID("bar", 20, i32_type(), &val_neg); - CreateConstantID("baz", 300, i32_type(), &val_pos); + AddConstantID("foo", 1, i32_type(), nullptr); + AddConstantID("bar", 20, i32_type(), &val_neg); + AddConstantID("baz", 300, i32_type(), &val_pos); auto result = inspector()->GetConstantIDs(); ASSERT_EQ(3u, result.size()); @@ -629,14 +762,14 @@ TEST_F(InspectorGetEntryPointTest, I32ConstantIDs) { EXPECT_EQ(42, result[300].AsI32()); } -TEST_F(InspectorGetEntryPointTest, FloatConstantIDs) { +TEST_F(InspectorGetConstantIDsTest, Float) { float val_zero = 0.0f; float val_neg = -10.0f; float val_pos = 15.0f; - CreateConstantID("foo", 1, f32_type(), nullptr); - CreateConstantID("bar", 20, f32_type(), &val_zero); - CreateConstantID("baz", 300, f32_type(), &val_neg); - CreateConstantID("x", 4000, f32_type(), &val_pos); + AddConstantID("foo", 1, f32_type(), nullptr); + AddConstantID("bar", 20, f32_type(), &val_zero); + AddConstantID("baz", 300, f32_type(), &val_neg); + AddConstantID("x", 4000, f32_type(), &val_pos); auto result = inspector()->GetConstantIDs(); ASSERT_EQ(4u, result.size()); @@ -657,6 +790,200 @@ TEST_F(InspectorGetEntryPointTest, FloatConstantIDs) { EXPECT_FLOAT_EQ(15.0, result[4000].AsFloat()); } +TEST_F(InspectorGetUniformBufferResourceBindings, MissingEntryPoint) { + auto result = inspector()->GetUniformBufferResourceBindings("ep_func"); + ASSERT_TRUE(inspector()->has_error()); + std::string error = inspector()->error(); + EXPECT_TRUE(error.find("not found") != std::string::npos); +} + +TEST_F(InspectorGetUniformBufferResourceBindings, NonEntryPointFunc) { + auto foo_type = MakeUBOStructType("foo_type", {{i32_type(), 0}}); + AddUBO("foo_ubo", foo_type.get(), 0, 0); + + auto ubo_func = + MakeUBOReferenceBodyFunction("ubo_func", "foo_ubo", {{0, i32_type()}}); + mod()->AddFunction(std::move(ubo_func)); + + auto ep_func = MakeCallerBodyFunction("ep_func", "ubo_func"); + ep_func->add_decoration( + std::make_unique(ast::PipelineStage::kVertex)); + mod()->AddFunction(std::move(ep_func)); + + ASSERT_TRUE(td()->Determine()) << td()->error(); + + auto result = inspector()->GetUniformBufferResourceBindings("ubo_func"); + std::string error = inspector()->error(); + EXPECT_TRUE(error.find("not an entry point") != std::string::npos); +} + +TEST_F(InspectorGetUniformBufferResourceBindings, MissingBlockDeco) { + ast::StructMemberList members; + ast::StructMemberDecorationList deco; + deco.push_back(std::make_unique(0)); + + members.push_back(std::make_unique( + StructMemberName(members.size(), i32_type()), i32_type(), + std::move(deco))); + + ast::StructDecorationList decos; + + auto str = + std::make_unique(std::move(decos), std::move(members)); + auto foo_type = + std::make_unique("foo_type", std::move(str)); + + AddUBO("foo_ubo", foo_type.get(), 0, 0); + + auto ubo_func = + MakeUBOReferenceBodyFunction("ubo_func", "foo_ubo", {{0, i32_type()}}); + mod()->AddFunction(std::move(ubo_func)); + + auto ep_func = MakeCallerBodyFunction("ep_func", "ubo_func"); + ep_func->add_decoration( + std::make_unique(ast::PipelineStage::kVertex)); + mod()->AddFunction(std::move(ep_func)); + + ASSERT_TRUE(td()->Determine()) << td()->error(); + + auto result = inspector()->GetUniformBufferResourceBindings("ep_func"); + ASSERT_FALSE(inspector()->has_error()); + EXPECT_EQ(0u, result.size()); +} + +TEST_F(InspectorGetUniformBufferResourceBindings, Simple) { + auto foo_type = MakeUBOStructType("foo_type", {{i32_type(), 0}}); + AddUBO("foo_ubo", foo_type.get(), 0, 0); + + auto ubo_func = + MakeUBOReferenceBodyFunction("ubo_func", "foo_ubo", {{0, i32_type()}}); + mod()->AddFunction(std::move(ubo_func)); + + auto ep_func = MakeCallerBodyFunction("ep_func", "ubo_func"); + ep_func->add_decoration( + std::make_unique(ast::PipelineStage::kVertex)); + mod()->AddFunction(std::move(ep_func)); + + ASSERT_TRUE(td()->Determine()) << td()->error(); + + auto result = inspector()->GetUniformBufferResourceBindings("ep_func"); + ASSERT_FALSE(inspector()->has_error()); + ASSERT_EQ(1u, result.size()); + + EXPECT_EQ(0u, result[0].bind_group); + EXPECT_EQ(0u, result[0].binding); + EXPECT_EQ(4u, result[0].min_buffer_binding_size); +} + +TEST_F(InspectorGetUniformBufferResourceBindings, MultipleMembers) { + auto foo_type = MakeUBOStructType( + "foo_type", {{i32_type(), 0}, {u32_type(), 4}, {f32_type(), 8}}); + AddUBO("foo_ubo", foo_type.get(), 0, 0); + + auto ubo_func = MakeUBOReferenceBodyFunction( + "ubo_func", "foo_ubo", + {{0, i32_type()}, {1, u32_type()}, {2, f32_type()}}); + mod()->AddFunction(std::move(ubo_func)); + + auto ep_func = MakeCallerBodyFunction("ep_func", "ubo_func"); + ep_func->add_decoration( + std::make_unique(ast::PipelineStage::kVertex)); + mod()->AddFunction(std::move(ep_func)); + + ASSERT_TRUE(td()->Determine()) << td()->error(); + + auto result = inspector()->GetUniformBufferResourceBindings("ep_func"); + ASSERT_FALSE(inspector()->has_error()); + ASSERT_EQ(1u, result.size()); + + EXPECT_EQ(0u, result[0].bind_group); + EXPECT_EQ(0u, result[0].binding); + EXPECT_EQ(12u, result[0].min_buffer_binding_size); +} + +TEST_F(InspectorGetUniformBufferResourceBindings, MultipleUBOs) { + auto ubo_type = MakeUBOStructType( + "ubo_type", {{i32_type(), 0}, {u32_type(), 4}, {f32_type(), 8}}); + AddUBO("ubo_foo", ubo_type.get(), 0, 0); + AddUBO("ubo_bar", ubo_type.get(), 0, 1); + AddUBO("ubo_baz", ubo_type.get(), 2, 0); + + auto AddReferenceFunc = [this](const std::string& func_name, + const std::string& var_name) { + auto ubo_func = MakeUBOReferenceBodyFunction( + func_name, var_name, + {{0, i32_type()}, {1, u32_type()}, {2, f32_type()}}); + mod()->AddFunction(std::move(ubo_func)); + }; + AddReferenceFunc("ubo_foo_func", "ubo_foo"); + AddReferenceFunc("ubo_bar_func", "ubo_bar"); + AddReferenceFunc("ubo_baz_func", "ubo_baz"); + + auto AddFuncCall = [](ast::BlockStatement* body, const std::string& callee) { + auto ident_expr = std::make_unique(callee); + auto call_expr = std::make_unique( + std::move(ident_expr), ast::ExpressionList()); + body->append(std::make_unique(std::move(call_expr))); + }; + auto body = std::make_unique(); + + AddFuncCall(body.get(), "ubo_foo_func"); + AddFuncCall(body.get(), "ubo_bar_func"); + AddFuncCall(body.get(), "ubo_baz_func"); + + body->append(std::make_unique()); + std::unique_ptr func = std::make_unique( + "ep_func", ast::VariableList(), void_type()); + func->set_body(std::move(body)); + + func->add_decoration( + std::make_unique(ast::PipelineStage::kVertex)); + mod()->AddFunction(std::move(func)); + + ASSERT_TRUE(td()->Determine()) << td()->error(); + + auto result = inspector()->GetUniformBufferResourceBindings("ep_func"); + ASSERT_FALSE(inspector()->has_error()); + ASSERT_EQ(3u, result.size()); + + EXPECT_EQ(0u, result[0].bind_group); + EXPECT_EQ(0u, result[0].binding); + EXPECT_EQ(12u, result[0].min_buffer_binding_size); + + EXPECT_EQ(0u, result[1].bind_group); + EXPECT_EQ(1u, result[1].binding); + EXPECT_EQ(12u, result[1].min_buffer_binding_size); + + EXPECT_EQ(2u, result[2].bind_group); + EXPECT_EQ(0u, result[2].binding); + EXPECT_EQ(12u, result[2].min_buffer_binding_size); +} + +TEST_F(InspectorGetUniformBufferResourceBindings, ContainingArray) { + auto foo_type = + MakeUBOStructType("foo_type", {{i32_type(), 0}, {u32_array_type(4), 4}}); + AddUBO("foo_ubo", foo_type.get(), 0, 0); + + auto ubo_func = + MakeUBOReferenceBodyFunction("ubo_func", "foo_ubo", {{0, i32_type()}}); + mod()->AddFunction(std::move(ubo_func)); + + auto ep_func = MakeCallerBodyFunction("ep_func", "ubo_func"); + ep_func->add_decoration( + std::make_unique(ast::PipelineStage::kVertex)); + mod()->AddFunction(std::move(ep_func)); + + ASSERT_TRUE(td()->Determine()) << td()->error(); + + auto result = inspector()->GetUniformBufferResourceBindings("ep_func"); + ASSERT_FALSE(inspector()->has_error()); + ASSERT_EQ(1u, result.size()); + + EXPECT_EQ(0u, result[0].bind_group); + EXPECT_EQ(0u, result[0].binding); + EXPECT_EQ(20u, result[0].min_buffer_binding_size); +} + } // namespace } // namespace inspector } // namespace tint