[inspector] Extract UBO information

Also includes adding in sizing information for various types.

BUG=tint:257

Change-Id: Iaaa8a7c28851d14790285b5bd14636bf3ae2b9b0
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/30704
Commit-Queue: Ryan Harrison <rharrison@chromium.org>
Commit-Queue: dan sinclair <dsinclair@chromium.org>
Reviewed-by: dan sinclair <dsinclair@chromium.org>
This commit is contained in:
Ryan Harrison 2020-10-22 19:31:31 +00:00 committed by Commit Bot service account
parent 8f7c80347d
commit 88d705dc85
23 changed files with 548 additions and 67 deletions

View File

@ -35,6 +35,10 @@ std::string AliasType::type_name() const {
return "__alias_" + name_ + subtype_->type_name(); return "__alias_" + name_ + subtype_->type_name();
} }
uint64_t AliasType::MinBufferBindingSize() const {
return subtype_->MinBufferBindingSize();
}
} // namespace type } // namespace type
} // namespace ast } // namespace ast
} // namespace tint } // namespace tint

View File

@ -23,7 +23,7 @@ namespace tint {
namespace ast { namespace ast {
namespace type { 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 { class AliasType : public Type {
public: public:
/// Constructor /// Constructor
@ -45,6 +45,10 @@ class AliasType : public Type {
/// @returns the name for this type /// @returns the name for this type
std::string type_name() const override; 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: private:
std::string name_; std::string name_;
Type* subtype_ = nullptr; Type* subtype_ = nullptr;

View File

@ -33,6 +33,18 @@ bool ArrayType::IsArray() const {
return true; 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 { uint32_t ArrayType::array_stride() const {
for (const auto& deco : decos_) { for (const auto& deco : decos_) {
if (deco->IsStride()) { if (deco->IsStride()) {

View File

@ -46,6 +46,10 @@ class ArrayType : public Type {
/// i.e. the size is determined at runtime /// i.e. the size is determined at runtime
bool IsRuntimeArray() const { return size_ == 0; } 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 /// Sets the array decorations
/// @param decos the decorations to set /// @param decos the decorations to set
void set_decorations(ast::ArrayDecorationList decos) { void set_decorations(ast::ArrayDecorationList decos) {

View File

@ -30,6 +30,10 @@ std::string F32Type::type_name() const {
return "__f32"; return "__f32";
} }
uint64_t F32Type::MinBufferBindingSize() const {
return 4;
}
} // namespace type } // namespace type
} // namespace ast } // namespace ast
} // namespace tint } // namespace tint

View File

@ -37,6 +37,10 @@ class F32Type : public Type {
/// @returns the name for this type /// @returns the name for this type
std::string type_name() const override; 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 } // namespace type

View File

@ -30,6 +30,10 @@ std::string I32Type::type_name() const {
return "__i32"; return "__i32";
} }
uint64_t I32Type::MinBufferBindingSize() const {
return 4;
}
} // namespace type } // namespace type
} // namespace ast } // namespace ast
} // namespace tint } // namespace tint

View File

@ -37,6 +37,10 @@ class I32Type : public Type {
/// @returns the name for this type /// @returns the name for this type
std::string type_name() const override; 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 } // namespace type

View File

@ -28,6 +28,8 @@ MatrixType::MatrixType(Type* subtype, uint32_t rows, uint32_t columns)
assert(columns < 5); assert(columns < 5);
} }
MatrixType::~MatrixType() = default;
bool MatrixType::IsMatrix() const { bool MatrixType::IsMatrix() const {
return true; return true;
} }
@ -37,7 +39,9 @@ std::string MatrixType::type_name() const {
subtype_->type_name(); subtype_->type_name();
} }
MatrixType::~MatrixType() = default; uint64_t MatrixType::MinBufferBindingSize() const {
return rows_ * columns_ * subtype_->MinBufferBindingSize();
}
} // namespace type } // namespace type
} // namespace ast } // namespace ast

View File

@ -48,6 +48,10 @@ class MatrixType : public Type {
/// @returns the name for this type /// @returns the name for this type
std::string type_name() const override; 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: private:
Type* subtype_ = nullptr; Type* subtype_ = nullptr;
uint32_t rows_ = 2; uint32_t rows_ = 2;

View File

@ -25,6 +25,10 @@ bool PointerType::IsPointer() const {
return true; return true;
} }
uint64_t PointerType::MinBufferBindingSize() const {
return 4;
}
std::string PointerType::type_name() const { std::string PointerType::type_name() const {
std::ostringstream out; std::ostringstream out;
out << "__ptr_" << storage_class_ << subtype_->type_name(); out << "__ptr_" << storage_class_ << subtype_->type_name();

View File

@ -39,6 +39,10 @@ class PointerType : public Type {
/// @returns true if the type is a pointer type /// @returns true if the type is a pointer type
bool IsPointer() const override; 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 /// @returns the pointee type
Type* type() const { return subtype_; } Type* type() const { return subtype_; }
/// @returns the storage class of the pointer /// @returns the storage class of the pointer

View File

@ -25,6 +25,8 @@ StructType::StructType(const std::string& name, std::unique_ptr<Struct> impl)
StructType::StructType(StructType&&) = default; StructType::StructType(StructType&&) = default;
StructType::~StructType() = default;
bool StructType::IsStruct() const { bool StructType::IsStruct() const {
return true; return true;
} }
@ -33,7 +35,21 @@ std::string StructType::type_name() const {
return "__struct_" + name_; 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 type
} // namespace ast } // namespace ast

View File

@ -48,9 +48,13 @@ class StructType : public Type {
/// @returns the struct name /// @returns the struct name
Struct* impl() const { return struct_.get(); } Struct* impl() const { return struct_.get(); }
/// @returns the name for th type /// @returns the name for the type
std::string type_name() const override; 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: private:
std::string name_; std::string name_;
std::unique_ptr<Struct> struct_; std::unique_ptr<Struct> struct_;

View File

@ -109,6 +109,10 @@ bool Type::IsVoid() const {
return false; return false;
} }
uint64_t Type::MinBufferBindingSize() const {
return 0;
}
bool Type::is_scalar() { bool Type::is_scalar() {
return is_float_scalar() || is_integer_scalar() || IsBool(); return is_float_scalar() || is_integer_scalar() || IsBool();
} }

View File

@ -72,6 +72,10 @@ class Type {
/// @returns the name for this type. The |type_name| is unique over all types. /// @returns the name for this type. The |type_name| is unique over all types.
virtual std::string type_name() const = 0; 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 /// @returns the pointee type if this is a pointer, |this| otherwise
Type* UnwrapPtrIfNeeded(); Type* UnwrapPtrIfNeeded();

View File

@ -32,6 +32,10 @@ std::string U32Type::type_name() const {
return "__u32"; return "__u32";
} }
uint64_t U32Type::MinBufferBindingSize() const {
return 4;
}
} // namespace type } // namespace type
} // namespace ast } // namespace ast
} // namespace tint } // namespace tint

View File

@ -37,6 +37,10 @@ class U32Type : public Type {
/// @returns the name for th type /// @returns the name for th type
std::string type_name() const override; 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 } // namespace type

View File

@ -38,6 +38,10 @@ std::string VectorType::type_name() const {
return "__vec_" + std::to_string(size_) + subtype_->type_name(); return "__vec_" + std::to_string(size_) + subtype_->type_name();
} }
uint64_t VectorType::MinBufferBindingSize() const {
return size_ * subtype_->MinBufferBindingSize();
}
} // namespace type } // namespace type
} // namespace ast } // namespace ast
} // namespace tint } // namespace tint

View File

@ -45,6 +45,10 @@ class VectorType : public Type {
/// @returns the name for th type /// @returns the name for th type
std::string type_name() const override; 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: private:
Type* subtype_ = nullptr; Type* subtype_ = nullptr;
uint32_t size_ = 2; uint32_t size_ = 2;

View File

@ -25,6 +25,7 @@
#include "src/ast/null_literal.h" #include "src/ast/null_literal.h"
#include "src/ast/scalar_constructor_expression.h" #include "src/ast/scalar_constructor_expression.h"
#include "src/ast/sint_literal.h" #include "src/ast/sint_literal.h"
#include "src/ast/type/struct_type.h"
#include "src/ast/uint_literal.h" #include "src/ast/uint_literal.h"
namespace tint { namespace tint {
@ -134,5 +135,43 @@ std::map<uint32_t, Scalar> Inspector::GetConstantIDs() {
return result; return result;
} }
std::vector<ResourceBinding> 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<ResourceBinding> 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 inspector
} // namespace tint } // namespace tint

View File

@ -29,6 +29,16 @@
namespace tint { namespace tint {
namespace inspector { 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 /// Extracts information from a module
class Inspector { class Inspector {
public: public:
@ -48,6 +58,11 @@ class Inspector {
/// @returns map of const_id to initial value /// @returns map of const_id to initial value
std::map<uint32_t, Scalar> GetConstantIDs(); std::map<uint32_t, Scalar> 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<ResourceBinding> GetUniformBufferResourceBindings(
const std::string& entry_point);
private: private:
const ast::Module& module_; const ast::Module& module_;
std::string error_; std::string error_;

View File

@ -24,17 +24,27 @@
#include "src/ast/float_literal.h" #include "src/ast/float_literal.h"
#include "src/ast/function.h" #include "src/ast/function.h"
#include "src/ast/identifier_expression.h" #include "src/ast/identifier_expression.h"
#include "src/ast/member_accessor_expression.h"
#include "src/ast/null_literal.h" #include "src/ast/null_literal.h"
#include "src/ast/pipeline_stage.h" #include "src/ast/pipeline_stage.h"
#include "src/ast/return_statement.h" #include "src/ast/return_statement.h"
#include "src/ast/scalar_constructor_expression.h" #include "src/ast/scalar_constructor_expression.h"
#include "src/ast/sint_literal.h" #include "src/ast/sint_literal.h"
#include "src/ast/stage_decoration.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/bool_type.h"
#include "src/ast/type/f32_type.h" #include "src/ast/type/f32_type.h"
#include "src/ast/type/i32_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/type.h"
#include "src/ast/type/u32_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/type/void_type.h"
#include "src/ast/uint_literal.h" #include "src/ast/uint_literal.h"
#include "src/ast/variable_decl_statement.h" #include "src/ast/variable_decl_statement.h"
@ -58,7 +68,7 @@ class InspectorHelper {
/// Generates an empty function /// Generates an empty function
/// @param name name of the function created /// @param name name of the function created
/// @returns a function object /// @returns a function object
std::unique_ptr<ast::Function> GenerateEmptyBodyFunction(std::string name) { std::unique_ptr<ast::Function> MakeEmptyBodyFunction(std::string name) {
auto body = std::make_unique<ast::BlockStatement>(); auto body = std::make_unique<ast::BlockStatement>();
body->append(std::make_unique<ast::ReturnStatement>()); body->append(std::make_unique<ast::ReturnStatement>());
std::unique_ptr<ast::Function> func = std::unique_ptr<ast::Function> func =
@ -71,9 +81,8 @@ class InspectorHelper {
/// @param caller name of the function created /// @param caller name of the function created
/// @param callee name of the function to be called /// @param callee name of the function to be called
/// @returns a function object /// @returns a function object
std::unique_ptr<ast::Function> GenerateCallerBodyFunction( std::unique_ptr<ast::Function> MakeCallerBodyFunction(std::string caller,
std::string caller, std::string callee) {
std::string callee) {
auto body = std::make_unique<ast::BlockStatement>(); auto body = std::make_unique<ast::BlockStatement>();
auto ident_expr = std::make_unique<ast::IdentifierExpression>(callee); auto ident_expr = std::make_unique<ast::IdentifierExpression>(callee);
auto call_expr = std::make_unique<ast::CallExpression>( auto call_expr = std::make_unique<ast::CallExpression>(
@ -89,7 +98,7 @@ class InspectorHelper {
/// Add In/Out variables to the global variables /// Add In/Out variables to the global variables
/// @param inout_vars tuples of {in, out} that will be added as entries to the /// @param inout_vars tuples of {in, out} that will be added as entries to the
/// global variables /// global variables
void CreateInOutVariables( void AddInOutVariables(
std::vector<std::tuple<std::string, std::string>> inout_vars) { std::vector<std::tuple<std::string, std::string>> inout_vars) {
for (auto inout : inout_vars) { for (auto inout : inout_vars) {
std::string in, out; std::string in, out;
@ -108,7 +117,7 @@ class InspectorHelper {
/// @param inout_vars tuples of {in, out} that will be converted into out = in /// @param inout_vars tuples of {in, out} that will be converted into out = in
/// calls in the function body /// calls in the function body
/// @returns a function object /// @returns a function object
std::unique_ptr<ast::Function> GenerateInOutVariableBodyFunction( std::unique_ptr<ast::Function> MakeInOutVariableBodyFunction(
std::string name, std::string name,
std::vector<std::tuple<std::string, std::string>> inout_vars) { std::vector<std::tuple<std::string, std::string>> inout_vars) {
auto body = std::make_unique<ast::BlockStatement>(); auto body = std::make_unique<ast::BlockStatement>();
@ -133,7 +142,7 @@ class InspectorHelper {
/// @param inout_vars tuples of {in, out} that will be converted into out = in /// @param inout_vars tuples of {in, out} that will be converted into out = in
/// calls in the function body /// calls in the function body
/// @returns a function object /// @returns a function object
std::unique_ptr<ast::Function> GenerateInOutVariableCallerBodyFunction( std::unique_ptr<ast::Function> MakeInOutVariableCallerBodyFunction(
std::string caller, std::string caller,
std::string callee, std::string callee,
std::vector<std::tuple<std::string, std::string>> inout_vars) { std::vector<std::tuple<std::string, std::string>> inout_vars) {
@ -163,10 +172,10 @@ class InspectorHelper {
/// @param val value to initialize the variable with, if NULL no initializer /// @param val value to initialize the variable with, if NULL no initializer
/// will be added. /// will be added.
template <class T> template <class T>
void CreateConstantID(std::string name, void AddConstantID(std::string name,
uint32_t id, uint32_t id,
ast::type::Type* type, ast::type::Type* type,
T* val) { T* val) {
auto dvar = std::make_unique<ast::DecoratedVariable>( auto dvar = std::make_unique<ast::DecoratedVariable>(
std::make_unique<ast::Variable>(name, ast::StorageClass::kNone, type)); std::make_unique<ast::Variable>(name, ast::StorageClass::kNone, type));
dvar->set_is_const(true); dvar->set_is_const(true);
@ -180,35 +189,53 @@ class InspectorHelper {
mod()->AddGlobalVariable(std::move(dvar)); 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 <class T> template <class T>
std::unique_ptr<ast::Literal> MakeLiteral(ast::type::Type*, T*) { std::unique_ptr<ast::Literal> MakeLiteral(ast::type::Type*, T*) {
return nullptr; 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 <> template <>
std::unique_ptr<ast::Literal> MakeLiteral<bool>(ast::type::Type* type, std::unique_ptr<ast::Literal> MakeLiteral<bool>(ast::type::Type* type,
bool* val) { bool* val) {
return std::make_unique<ast::BoolLiteral>(type, *val); return std::make_unique<ast::BoolLiteral>(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 <> template <>
std::unique_ptr<ast::Literal> MakeLiteral<uint32_t>(ast::type::Type* type, std::unique_ptr<ast::Literal> MakeLiteral<uint32_t>(ast::type::Type* type,
uint32_t* val) { uint32_t* val) {
return std::make_unique<ast::UintLiteral>(type, *val); return std::make_unique<ast::UintLiteral>(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 <> template <>
std::unique_ptr<ast::Literal> MakeLiteral<int32_t>(ast::type::Type* type, std::unique_ptr<ast::Literal> MakeLiteral<int32_t>(ast::type::Type* type,
int32_t* val) { int32_t* val) {
return std::make_unique<ast::SintLiteral>(type, *val); return std::make_unique<ast::SintLiteral>(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 <> template <>
std::unique_ptr<ast::Literal> MakeLiteral<float>(ast::type::Type* type, std::unique_ptr<ast::Literal> MakeLiteral<float>(ast::type::Type* type,
float* val) { float* val) {
return std::make_unique<ast::FloatLiteral>(type, *val); return std::make_unique<ast::FloatLiteral>(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<std::string>& vec, bool ContainsString(const std::vector<std::string>& vec,
const std::string& str) { const std::string& str) {
for (auto& s : vec) { for (auto& s : vec) {
@ -219,6 +246,105 @@ class InspectorHelper {
return false; 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<ast::type::StructType> MakeUBOStructType(
const std::string& name,
std::vector<std::tuple<ast::type::Type*, uint32_t>> 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<ast::StructMemberOffsetDecoration>(offset));
members.push_back(std::make_unique<ast::StructMember>(
StructMemberName(members.size(), type), type, std::move(deco)));
}
ast::StructDecorationList decos;
decos.push_back(ast::StructDecoration::kBlock);
auto str =
std::make_unique<ast::Struct>(std::move(decos), std::move(members));
return std::make_unique<ast::type::StructType>(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<ast::DecoratedVariable>(
std::make_unique<ast::Variable>(name, ast::StorageClass::kUniform,
struct_type));
ast::VariableDecorationList decorations;
decorations.push_back(std::make_unique<ast::BindingDecoration>(binding));
decorations.push_back(std::make_unique<ast::SetDecoration>(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<ast::Function> MakeUBOReferenceBodyFunction(
std::string func_name,
std::string ubo_name,
std::vector<std::tuple<size_t, ast::type::Type*>> members) {
auto body = std::make_unique<ast::BlockStatement>();
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<ast::VariableDeclStatement>(
std::make_unique<ast::Variable>(
"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<ast::AssignmentStatement>(
std::make_unique<ast::IdentifierExpression>("local" + member_name),
std::make_unique<ast::MemberAccessorExpression>(
std::make_unique<ast::IdentifierExpression>(ubo_name),
std::make_unique<ast::IdentifierExpression>(member_name))));
}
body->append(std::make_unique<ast::ReturnStatement>());
auto func = std::make_unique<ast::Function>(func_name, ast::VariableList(),
void_type());
func->set_body(std::move(body));
return func;
}
ast::Module* mod() { return &mod_; } ast::Module* mod() { return &mod_; }
TypeDeterminer* td() { return td_.get(); } TypeDeterminer* td() { return td_.get(); }
Inspector* inspector() { return inspector_.get(); } Inspector* inspector() { return inspector_.get(); }
@ -227,6 +353,14 @@ class InspectorHelper {
ast::type::F32Type* f32_type() { return &f32_type_; } ast::type::F32Type* f32_type() { return &f32_type_; }
ast::type::I32Type* i32_type() { return &i32_type_; } ast::type::I32Type* i32_type() { return &i32_type_; }
ast::type::U32Type* u32_type() { return &u32_type_; } ast::type::U32Type* u32_type() { return &u32_type_; }
ast::type::ArrayType* u32_array_type(uint32_t count) {
static std::map<uint32_t, std::unique_ptr<ast::type::ArrayType>> memo;
if (memo.find(count) == memo.end()) {
memo[count] = std::make_unique<ast::type::ArrayType>(u32_type(), count);
}
return memo[count].get();
}
ast::type::VoidType* void_type() { return &void_type_; } ast::type::VoidType* void_type() { return &void_type_; }
private: private:
@ -245,6 +379,8 @@ class InspectorHelper {
class InspectorTest : public InspectorHelper, public testing::Test {}; class InspectorTest : public InspectorHelper, public testing::Test {};
class InspectorGetEntryPointTest : public InspectorTest {}; class InspectorGetEntryPointTest : public InspectorTest {};
class InspectorGetConstantIDsTest : public InspectorTest {};
class InspectorGetUniformBufferResourceBindings : public InspectorTest {};
TEST_F(InspectorGetEntryPointTest, NoFunctions) { TEST_F(InspectorGetEntryPointTest, NoFunctions) {
auto result = inspector()->GetEntryPoints(); auto result = inspector()->GetEntryPoints();
@ -254,7 +390,7 @@ TEST_F(InspectorGetEntryPointTest, NoFunctions) {
} }
TEST_F(InspectorGetEntryPointTest, NoEntryPoints) { TEST_F(InspectorGetEntryPointTest, NoEntryPoints) {
mod()->AddFunction(GenerateEmptyBodyFunction("foo")); mod()->AddFunction(MakeEmptyBodyFunction("foo"));
auto result = inspector()->GetEntryPoints(); auto result = inspector()->GetEntryPoints();
ASSERT_FALSE(inspector()->has_error()); ASSERT_FALSE(inspector()->has_error());
@ -263,7 +399,7 @@ TEST_F(InspectorGetEntryPointTest, NoEntryPoints) {
} }
TEST_F(InspectorGetEntryPointTest, OneEntryPoint) { TEST_F(InspectorGetEntryPointTest, OneEntryPoint) {
auto foo = GenerateEmptyBodyFunction("foo"); auto foo = MakeEmptyBodyFunction("foo");
foo->add_decoration( foo->add_decoration(
std::make_unique<ast::StageDecoration>(ast::PipelineStage::kVertex)); std::make_unique<ast::StageDecoration>(ast::PipelineStage::kVertex));
mod()->AddFunction(std::move(foo)); mod()->AddFunction(std::move(foo));
@ -277,12 +413,12 @@ TEST_F(InspectorGetEntryPointTest, OneEntryPoint) {
} }
TEST_F(InspectorGetEntryPointTest, MultipleEntryPoints) { TEST_F(InspectorGetEntryPointTest, MultipleEntryPoints) {
auto foo = GenerateEmptyBodyFunction("foo"); auto foo = MakeEmptyBodyFunction("foo");
foo->add_decoration( foo->add_decoration(
std::make_unique<ast::StageDecoration>(ast::PipelineStage::kVertex)); std::make_unique<ast::StageDecoration>(ast::PipelineStage::kVertex));
mod()->AddFunction(std::move(foo)); mod()->AddFunction(std::move(foo));
auto bar = GenerateEmptyBodyFunction("bar"); auto bar = MakeEmptyBodyFunction("bar");
bar->add_decoration( bar->add_decoration(
std::make_unique<ast::StageDecoration>(ast::PipelineStage::kCompute)); std::make_unique<ast::StageDecoration>(ast::PipelineStage::kCompute));
mod()->AddFunction(std::move(bar)); mod()->AddFunction(std::move(bar));
@ -298,15 +434,15 @@ TEST_F(InspectorGetEntryPointTest, MultipleEntryPoints) {
} }
TEST_F(InspectorGetEntryPointTest, MixFunctionsAndEntryPoints) { TEST_F(InspectorGetEntryPointTest, MixFunctionsAndEntryPoints) {
auto func = GenerateEmptyBodyFunction("func"); auto func = MakeEmptyBodyFunction("func");
mod()->AddFunction(std::move(func)); mod()->AddFunction(std::move(func));
auto foo = GenerateCallerBodyFunction("foo", "func"); auto foo = MakeCallerBodyFunction("foo", "func");
foo->add_decoration( foo->add_decoration(
std::make_unique<ast::StageDecoration>(ast::PipelineStage::kVertex)); std::make_unique<ast::StageDecoration>(ast::PipelineStage::kVertex));
mod()->AddFunction(std::move(foo)); mod()->AddFunction(std::move(foo));
auto bar = GenerateCallerBodyFunction("bar", "func"); auto bar = MakeCallerBodyFunction("bar", "func");
bar->add_decoration( bar->add_decoration(
std::make_unique<ast::StageDecoration>(ast::PipelineStage::kFragment)); std::make_unique<ast::StageDecoration>(ast::PipelineStage::kFragment));
mod()->AddFunction(std::move(bar)); mod()->AddFunction(std::move(bar));
@ -322,7 +458,7 @@ TEST_F(InspectorGetEntryPointTest, MixFunctionsAndEntryPoints) {
} }
TEST_F(InspectorGetEntryPointTest, DefaultWorkgroupSize) { TEST_F(InspectorGetEntryPointTest, DefaultWorkgroupSize) {
auto foo = GenerateCallerBodyFunction("foo", "func"); auto foo = MakeCallerBodyFunction("foo", "func");
foo->add_decoration( foo->add_decoration(
std::make_unique<ast::StageDecoration>(ast::PipelineStage::kVertex)); std::make_unique<ast::StageDecoration>(ast::PipelineStage::kVertex));
mod()->AddFunction(std::move(foo)); mod()->AddFunction(std::move(foo));
@ -339,7 +475,7 @@ TEST_F(InspectorGetEntryPointTest, DefaultWorkgroupSize) {
} }
TEST_F(InspectorGetEntryPointTest, NonDefaultWorkgroupSize) { TEST_F(InspectorGetEntryPointTest, NonDefaultWorkgroupSize) {
auto foo = GenerateEmptyBodyFunction("foo"); auto foo = MakeEmptyBodyFunction("foo");
foo->add_decoration( foo->add_decoration(
std::make_unique<ast::StageDecoration>(ast::PipelineStage::kCompute)); std::make_unique<ast::StageDecoration>(ast::PipelineStage::kCompute));
foo->add_decoration(std::make_unique<ast::WorkgroupDecoration>(8u, 2u, 1u)); foo->add_decoration(std::make_unique<ast::WorkgroupDecoration>(8u, 2u, 1u));
@ -357,10 +493,10 @@ TEST_F(InspectorGetEntryPointTest, NonDefaultWorkgroupSize) {
} }
TEST_F(InspectorGetEntryPointTest, NoInOutVariables) { TEST_F(InspectorGetEntryPointTest, NoInOutVariables) {
auto func = GenerateEmptyBodyFunction("func"); auto func = MakeEmptyBodyFunction("func");
mod()->AddFunction(std::move(func)); mod()->AddFunction(std::move(func));
auto foo = GenerateCallerBodyFunction("foo", "func"); auto foo = MakeCallerBodyFunction("foo", "func");
foo->add_decoration( foo->add_decoration(
std::make_unique<ast::StageDecoration>(ast::PipelineStage::kVertex)); std::make_unique<ast::StageDecoration>(ast::PipelineStage::kVertex));
mod()->AddFunction(std::move(foo)); mod()->AddFunction(std::move(foo));
@ -374,9 +510,9 @@ TEST_F(InspectorGetEntryPointTest, NoInOutVariables) {
} }
TEST_F(InspectorGetEntryPointTest, EntryPointInOutVariables) { 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( foo->add_decoration(
std::make_unique<ast::StageDecoration>(ast::PipelineStage::kVertex)); std::make_unique<ast::StageDecoration>(ast::PipelineStage::kVertex));
mod()->AddFunction(std::move(foo)); mod()->AddFunction(std::move(foo));
@ -395,13 +531,12 @@ TEST_F(InspectorGetEntryPointTest, EntryPointInOutVariables) {
} }
TEST_F(InspectorGetEntryPointTest, FunctionInOutVariables) { TEST_F(InspectorGetEntryPointTest, FunctionInOutVariables) {
CreateInOutVariables({{"in_var", "out_var"}}); AddInOutVariables({{"in_var", "out_var"}});
auto func = auto func = MakeInOutVariableBodyFunction("func", {{"in_var", "out_var"}});
GenerateInOutVariableBodyFunction("func", {{"in_var", "out_var"}});
mod()->AddFunction(std::move(func)); mod()->AddFunction(std::move(func));
auto foo = GenerateCallerBodyFunction("foo", "func"); auto foo = MakeCallerBodyFunction("foo", "func");
foo->add_decoration( foo->add_decoration(
std::make_unique<ast::StageDecoration>(ast::PipelineStage::kVertex)); std::make_unique<ast::StageDecoration>(ast::PipelineStage::kVertex));
mod()->AddFunction(std::move(foo)); mod()->AddFunction(std::move(foo));
@ -420,14 +555,13 @@ TEST_F(InspectorGetEntryPointTest, FunctionInOutVariables) {
} }
TEST_F(InspectorGetEntryPointTest, RepeatedInOutVariables) { TEST_F(InspectorGetEntryPointTest, RepeatedInOutVariables) {
CreateInOutVariables({{"in_var", "out_var"}}); AddInOutVariables({{"in_var", "out_var"}});
auto func = auto func = MakeInOutVariableBodyFunction("func", {{"in_var", "out_var"}});
GenerateInOutVariableBodyFunction("func", {{"in_var", "out_var"}});
mod()->AddFunction(std::move(func)); mod()->AddFunction(std::move(func));
auto foo = GenerateInOutVariableCallerBodyFunction("foo", "func", auto foo = MakeInOutVariableCallerBodyFunction("foo", "func",
{{"in_var", "out_var"}}); {{"in_var", "out_var"}});
foo->add_decoration( foo->add_decoration(
std::make_unique<ast::StageDecoration>(ast::PipelineStage::kVertex)); std::make_unique<ast::StageDecoration>(ast::PipelineStage::kVertex));
mod()->AddFunction(std::move(foo)); mod()->AddFunction(std::move(foo));
@ -446,9 +580,9 @@ TEST_F(InspectorGetEntryPointTest, RepeatedInOutVariables) {
} }
TEST_F(InspectorGetEntryPointTest, EntryPointMultipleInOutVariables) { 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", {{"in_var", "out_var"}, {"in2_var", "out2_var"}});
foo->add_decoration( foo->add_decoration(
std::make_unique<ast::StageDecoration>(ast::PipelineStage::kVertex)); std::make_unique<ast::StageDecoration>(ast::PipelineStage::kVertex));
@ -470,13 +604,13 @@ TEST_F(InspectorGetEntryPointTest, EntryPointMultipleInOutVariables) {
} }
TEST_F(InspectorGetEntryPointTest, FunctionMultipleInOutVariables) { 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"}}); "func", {{"in_var", "out_var"}, {"in2_var", "out2_var"}});
mod()->AddFunction(std::move(func)); mod()->AddFunction(std::move(func));
auto foo = GenerateCallerBodyFunction("foo", "func"); auto foo = MakeCallerBodyFunction("foo", "func");
foo->add_decoration( foo->add_decoration(
std::make_unique<ast::StageDecoration>(ast::PipelineStage::kVertex)); std::make_unique<ast::StageDecoration>(ast::PipelineStage::kVertex));
mod()->AddFunction(std::move(foo)); mod()->AddFunction(std::move(foo));
@ -497,14 +631,14 @@ TEST_F(InspectorGetEntryPointTest, FunctionMultipleInOutVariables) {
} }
TEST_F(InspectorGetEntryPointTest, MultipleEntryPointsInOutVariables) { 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( foo->add_decoration(
std::make_unique<ast::StageDecoration>(ast::PipelineStage::kVertex)); std::make_unique<ast::StageDecoration>(ast::PipelineStage::kVertex));
mod()->AddFunction(std::move(foo)); mod()->AddFunction(std::move(foo));
auto bar = GenerateInOutVariableBodyFunction("bar", {{"in2_var", "out_var"}}); auto bar = MakeInOutVariableBodyFunction("bar", {{"in2_var", "out_var"}});
bar->add_decoration( bar->add_decoration(
std::make_unique<ast::StageDecoration>(ast::PipelineStage::kCompute)); std::make_unique<ast::StageDecoration>(ast::PipelineStage::kCompute));
mod()->AddFunction(std::move(bar)); mod()->AddFunction(std::move(bar));
@ -530,19 +664,18 @@ TEST_F(InspectorGetEntryPointTest, MultipleEntryPointsInOutVariables) {
} }
TEST_F(InspectorGetEntryPointTest, MultipleEntryPointsSharedInOutVariables) { TEST_F(InspectorGetEntryPointTest, MultipleEntryPointsSharedInOutVariables) {
CreateInOutVariables({{"in_var", "out_var"}, {"in2_var", "out2_var"}}); AddInOutVariables({{"in_var", "out_var"}, {"in2_var", "out2_var"}});
auto func = auto func = MakeInOutVariableBodyFunction("func", {{"in2_var", "out2_var"}});
GenerateInOutVariableBodyFunction("func", {{"in2_var", "out2_var"}});
mod()->AddFunction(std::move(func)); mod()->AddFunction(std::move(func));
auto foo = GenerateInOutVariableCallerBodyFunction("foo", "func", auto foo = MakeInOutVariableCallerBodyFunction("foo", "func",
{{"in_var", "out_var"}}); {{"in_var", "out_var"}});
foo->add_decoration( foo->add_decoration(
std::make_unique<ast::StageDecoration>(ast::PipelineStage::kVertex)); std::make_unique<ast::StageDecoration>(ast::PipelineStage::kVertex));
mod()->AddFunction(std::move(foo)); mod()->AddFunction(std::move(foo));
auto bar = GenerateCallerBodyFunction("bar", "func"); auto bar = MakeCallerBodyFunction("bar", "func");
bar->add_decoration( bar->add_decoration(
std::make_unique<ast::StageDecoration>(ast::PipelineStage::kCompute)); std::make_unique<ast::StageDecoration>(ast::PipelineStage::kCompute));
mod()->AddFunction(std::move(bar)); mod()->AddFunction(std::move(bar));
@ -569,12 +702,12 @@ TEST_F(InspectorGetEntryPointTest, MultipleEntryPointsSharedInOutVariables) {
EXPECT_EQ("out2_var", result[1].output_variables[0]); EXPECT_EQ("out2_var", result[1].output_variables[0]);
} }
TEST_F(InspectorGetEntryPointTest, BoolConstantIDs) { TEST_F(InspectorGetConstantIDsTest, Bool) {
bool val_true = true; bool val_true = true;
bool val_false = false; bool val_false = false;
CreateConstantID<bool>("foo", 1, bool_type(), nullptr); AddConstantID<bool>("foo", 1, bool_type(), nullptr);
CreateConstantID<bool>("bar", 20, bool_type(), &val_true); AddConstantID<bool>("bar", 20, bool_type(), &val_true);
CreateConstantID<bool>("baz", 300, bool_type(), &val_false); AddConstantID<bool>("baz", 300, bool_type(), &val_false);
auto result = inspector()->GetConstantIDs(); auto result = inspector()->GetConstantIDs();
ASSERT_EQ(3u, result.size()); ASSERT_EQ(3u, result.size());
@ -591,10 +724,10 @@ TEST_F(InspectorGetEntryPointTest, BoolConstantIDs) {
EXPECT_FALSE(result[300].AsBool()); EXPECT_FALSE(result[300].AsBool());
} }
TEST_F(InspectorGetEntryPointTest, U32ConstantIDs) { TEST_F(InspectorGetConstantIDsTest, U32) {
uint32_t val = 42; uint32_t val = 42;
CreateConstantID<uint32_t>("foo", 1, u32_type(), nullptr); AddConstantID<uint32_t>("foo", 1, u32_type(), nullptr);
CreateConstantID<uint32_t>("bar", 20, u32_type(), &val); AddConstantID<uint32_t>("bar", 20, u32_type(), &val);
auto result = inspector()->GetConstantIDs(); auto result = inspector()->GetConstantIDs();
ASSERT_EQ(2u, result.size()); ASSERT_EQ(2u, result.size());
@ -607,12 +740,12 @@ TEST_F(InspectorGetEntryPointTest, U32ConstantIDs) {
EXPECT_EQ(42u, result[20].AsU32()); EXPECT_EQ(42u, result[20].AsU32());
} }
TEST_F(InspectorGetEntryPointTest, I32ConstantIDs) { TEST_F(InspectorGetConstantIDsTest, I32) {
int32_t val_neg = -42; int32_t val_neg = -42;
int32_t val_pos = 42; int32_t val_pos = 42;
CreateConstantID<int32_t>("foo", 1, i32_type(), nullptr); AddConstantID<int32_t>("foo", 1, i32_type(), nullptr);
CreateConstantID<int32_t>("bar", 20, i32_type(), &val_neg); AddConstantID<int32_t>("bar", 20, i32_type(), &val_neg);
CreateConstantID<int32_t>("baz", 300, i32_type(), &val_pos); AddConstantID<int32_t>("baz", 300, i32_type(), &val_pos);
auto result = inspector()->GetConstantIDs(); auto result = inspector()->GetConstantIDs();
ASSERT_EQ(3u, result.size()); ASSERT_EQ(3u, result.size());
@ -629,14 +762,14 @@ TEST_F(InspectorGetEntryPointTest, I32ConstantIDs) {
EXPECT_EQ(42, result[300].AsI32()); EXPECT_EQ(42, result[300].AsI32());
} }
TEST_F(InspectorGetEntryPointTest, FloatConstantIDs) { TEST_F(InspectorGetConstantIDsTest, Float) {
float val_zero = 0.0f; float val_zero = 0.0f;
float val_neg = -10.0f; float val_neg = -10.0f;
float val_pos = 15.0f; float val_pos = 15.0f;
CreateConstantID<float>("foo", 1, f32_type(), nullptr); AddConstantID<float>("foo", 1, f32_type(), nullptr);
CreateConstantID<float>("bar", 20, f32_type(), &val_zero); AddConstantID<float>("bar", 20, f32_type(), &val_zero);
CreateConstantID<float>("baz", 300, f32_type(), &val_neg); AddConstantID<float>("baz", 300, f32_type(), &val_neg);
CreateConstantID<float>("x", 4000, f32_type(), &val_pos); AddConstantID<float>("x", 4000, f32_type(), &val_pos);
auto result = inspector()->GetConstantIDs(); auto result = inspector()->GetConstantIDs();
ASSERT_EQ(4u, result.size()); ASSERT_EQ(4u, result.size());
@ -657,6 +790,200 @@ TEST_F(InspectorGetEntryPointTest, FloatConstantIDs) {
EXPECT_FLOAT_EQ(15.0, result[4000].AsFloat()); 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::StageDecoration>(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<ast::StructMemberOffsetDecoration>(0));
members.push_back(std::make_unique<ast::StructMember>(
StructMemberName(members.size(), i32_type()), i32_type(),
std::move(deco)));
ast::StructDecorationList decos;
auto str =
std::make_unique<ast::Struct>(std::move(decos), std::move(members));
auto foo_type =
std::make_unique<ast::type::StructType>("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::StageDecoration>(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::StageDecoration>(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::StageDecoration>(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<ast::IdentifierExpression>(callee);
auto call_expr = std::make_unique<ast::CallExpression>(
std::move(ident_expr), ast::ExpressionList());
body->append(std::make_unique<ast::CallStatement>(std::move(call_expr)));
};
auto body = std::make_unique<ast::BlockStatement>();
AddFuncCall(body.get(), "ubo_foo_func");
AddFuncCall(body.get(), "ubo_bar_func");
AddFuncCall(body.get(), "ubo_baz_func");
body->append(std::make_unique<ast::ReturnStatement>());
std::unique_ptr<ast::Function> func = std::make_unique<ast::Function>(
"ep_func", ast::VariableList(), void_type());
func->set_body(std::move(body));
func->add_decoration(
std::make_unique<ast::StageDecoration>(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::StageDecoration>(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
} // namespace inspector } // namespace inspector
} // namespace tint } // namespace tint