[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>
diff --git a/src/ast/type/alias_type.cc b/src/ast/type/alias_type.cc
index cce05e3..d8f67d9 100644
--- a/src/ast/type/alias_type.cc
+++ b/src/ast/type/alias_type.cc
@@ -35,6 +35,10 @@
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 d407306..bc7505a 100644
--- a/src/ast/type/alias_type.h
+++ b/src/ast/type/alias_type.h
@@ -23,7 +23,7 @@
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 @@
/// @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 71b3861..179f6de 100644
--- a/src/ast/type/array_type.cc
+++ b/src/ast/type/array_type.cc
@@ -33,6 +33,18 @@
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 9a84d1e..fceca8e 100644
--- a/src/ast/type/array_type.h
+++ b/src/ast/type/array_type.h
@@ -46,6 +46,10 @@
/// 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 bac892b..2306bd0 100644
--- a/src/ast/type/f32_type.cc
+++ b/src/ast/type/f32_type.cc
@@ -30,6 +30,10 @@
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 a975837..8a0a5b6 100644
--- a/src/ast/type/f32_type.h
+++ b/src/ast/type/f32_type.h
@@ -37,6 +37,10 @@
/// @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 fd5ab0c..772f5cf 100644
--- a/src/ast/type/i32_type.cc
+++ b/src/ast/type/i32_type.cc
@@ -30,6 +30,10 @@
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 3f8a213..6c6b625 100644
--- a/src/ast/type/i32_type.h
+++ b/src/ast/type/i32_type.h
@@ -37,6 +37,10 @@
/// @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 d34208c..bd60c6c 100644
--- a/src/ast/type/matrix_type.cc
+++ b/src/ast/type/matrix_type.cc
@@ -28,6 +28,8 @@
assert(columns < 5);
}
+MatrixType::~MatrixType() = default;
+
bool MatrixType::IsMatrix() const {
return true;
}
@@ -37,7 +39,9 @@
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 35cd932..6a42d35 100644
--- a/src/ast/type/matrix_type.h
+++ b/src/ast/type/matrix_type.h
@@ -48,6 +48,10 @@
/// @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 e6083c8..507dbed 100644
--- a/src/ast/type/pointer_type.cc
+++ b/src/ast/type/pointer_type.cc
@@ -25,6 +25,10 @@
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 fa97975..dbf7e5c 100644
--- a/src/ast/type/pointer_type.h
+++ b/src/ast/type/pointer_type.h
@@ -39,6 +39,10 @@
/// @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 8f0a017..c52fdb5 100644
--- a/src/ast/type/struct_type.cc
+++ b/src/ast/type/struct_type.cc
@@ -25,6 +25,8 @@
StructType::StructType(StructType&&) = default;
+StructType::~StructType() = default;
+
bool StructType::IsStruct() const {
return true;
}
@@ -33,7 +35,21 @@
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 9621adb..ec5a7a1 100644
--- a/src/ast/type/struct_type.h
+++ b/src/ast/type/struct_type.h
@@ -48,9 +48,13 @@
/// @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> struct_;
diff --git a/src/ast/type/type.cc b/src/ast/type/type.cc
index 7565d73..ee8fa42 100644
--- a/src/ast/type/type.cc
+++ b/src/ast/type/type.cc
@@ -109,6 +109,10 @@
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 5d0dbe7..aabde8d 100644
--- a/src/ast/type/type.h
+++ b/src/ast/type/type.h
@@ -72,6 +72,10 @@
/// @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 2bb7a3f..ebc65f5 100644
--- a/src/ast/type/u32_type.cc
+++ b/src/ast/type/u32_type.cc
@@ -32,6 +32,10 @@
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 21dd9ea..27b40db 100644
--- a/src/ast/type/u32_type.h
+++ b/src/ast/type/u32_type.h
@@ -37,6 +37,10 @@
/// @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 2335b94..645ac2f 100644
--- a/src/ast/type/vector_type.cc
+++ b/src/ast/type/vector_type.cc
@@ -38,6 +38,10 @@
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 6ea2c8f..3862274 100644
--- a/src/ast/type/vector_type.h
+++ b/src/ast/type/vector_type.h
@@ -45,6 +45,10 @@
/// @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 b5d68f7..239dcbb 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 @@
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 tint
diff --git a/src/inspector/inspector.h b/src/inspector/inspector.h
index 9559fac..a0d82f2 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 @@
/// @returns map of const_id to initial value
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:
const ast::Module& module_;
std::string error_;
diff --git a/src/inspector/inspector_test.cc b/src/inspector/inspector_test.cc
index 9a895cc..d136ad8 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 @@
/// Generates an empty function
/// @param name name of the function created
/// @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>();
body->append(std::make_unique<ast::ReturnStatement>());
std::unique_ptr<ast::Function> func =
@@ -71,9 +81,8 @@
/// @param caller name of the function created
/// @param callee name of the function to be called
/// @returns a function object
- std::unique_ptr<ast::Function> GenerateCallerBodyFunction(
- std::string caller,
- std::string callee) {
+ std::unique_ptr<ast::Function> MakeCallerBodyFunction(std::string caller,
+ std::string callee) {
auto body = std::make_unique<ast::BlockStatement>();
auto ident_expr = std::make_unique<ast::IdentifierExpression>(callee);
auto call_expr = std::make_unique<ast::CallExpression>(
@@ -89,7 +98,7 @@
/// 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<std::tuple<std::string, std::string>> inout_vars) {
for (auto inout : inout_vars) {
std::string in, out;
@@ -108,7 +117,7 @@
/// @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<ast::Function> GenerateInOutVariableBodyFunction(
+ std::unique_ptr<ast::Function> MakeInOutVariableBodyFunction(
std::string name,
std::vector<std::tuple<std::string, std::string>> inout_vars) {
auto body = std::make_unique<ast::BlockStatement>();
@@ -133,7 +142,7 @@
/// @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<ast::Function> GenerateInOutVariableCallerBodyFunction(
+ std::unique_ptr<ast::Function> MakeInOutVariableCallerBodyFunction(
std::string caller,
std::string callee,
std::vector<std::tuple<std::string, std::string>> inout_vars) {
@@ -163,10 +172,10 @@
/// @param val value to initialize the variable with, if NULL no initializer
/// will be added.
template <class T>
- 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<ast::DecoratedVariable>(
std::make_unique<ast::Variable>(name, ast::StorageClass::kNone, type));
dvar->set_is_const(true);
@@ -180,35 +189,53 @@
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>
std::unique_ptr<ast::Literal> 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<ast::Literal> MakeLiteral<bool>(ast::type::Type* type,
bool* 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 <>
std::unique_ptr<ast::Literal> MakeLiteral<uint32_t>(ast::type::Type* type,
uint32_t* 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 <>
std::unique_ptr<ast::Literal> MakeLiteral<int32_t>(ast::type::Type* type,
int32_t* 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 <>
std::unique_ptr<ast::Literal> MakeLiteral<float>(ast::type::Type* type,
float* 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,
const std::string& str) {
for (auto& s : vec) {
@@ -219,6 +246,105 @@
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_; }
TypeDeterminer* td() { return td_.get(); }
Inspector* inspector() { return inspector_.get(); }
@@ -227,6 +353,14 @@
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<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_; }
private:
@@ -245,6 +379,8 @@
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, 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, OneEntryPoint) {
- auto foo = GenerateEmptyBodyFunction("foo");
+ auto foo = MakeEmptyBodyFunction("foo");
foo->add_decoration(
std::make_unique<ast::StageDecoration>(ast::PipelineStage::kVertex));
mod()->AddFunction(std::move(foo));
@@ -277,12 +413,12 @@
}
TEST_F(InspectorGetEntryPointTest, MultipleEntryPoints) {
- auto foo = GenerateEmptyBodyFunction("foo");
+ auto foo = MakeEmptyBodyFunction("foo");
foo->add_decoration(
std::make_unique<ast::StageDecoration>(ast::PipelineStage::kVertex));
mod()->AddFunction(std::move(foo));
- auto bar = GenerateEmptyBodyFunction("bar");
+ auto bar = MakeEmptyBodyFunction("bar");
bar->add_decoration(
std::make_unique<ast::StageDecoration>(ast::PipelineStage::kCompute));
mod()->AddFunction(std::move(bar));
@@ -298,15 +434,15 @@
}
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::StageDecoration>(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::StageDecoration>(ast::PipelineStage::kFragment));
mod()->AddFunction(std::move(bar));
@@ -322,7 +458,7 @@
}
TEST_F(InspectorGetEntryPointTest, DefaultWorkgroupSize) {
- auto foo = GenerateCallerBodyFunction("foo", "func");
+ auto foo = MakeCallerBodyFunction("foo", "func");
foo->add_decoration(
std::make_unique<ast::StageDecoration>(ast::PipelineStage::kVertex));
mod()->AddFunction(std::move(foo));
@@ -339,7 +475,7 @@
}
TEST_F(InspectorGetEntryPointTest, NonDefaultWorkgroupSize) {
- auto foo = GenerateEmptyBodyFunction("foo");
+ auto foo = MakeEmptyBodyFunction("foo");
foo->add_decoration(
std::make_unique<ast::StageDecoration>(ast::PipelineStage::kCompute));
foo->add_decoration(std::make_unique<ast::WorkgroupDecoration>(8u, 2u, 1u));
@@ -357,10 +493,10 @@
}
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::StageDecoration>(ast::PipelineStage::kVertex));
mod()->AddFunction(std::move(foo));
@@ -374,9 +510,9 @@
}
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::StageDecoration>(ast::PipelineStage::kVertex));
mod()->AddFunction(std::move(foo));
@@ -395,13 +531,12 @@
}
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::StageDecoration>(ast::PipelineStage::kVertex));
mod()->AddFunction(std::move(foo));
@@ -420,14 +555,13 @@
}
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::StageDecoration>(ast::PipelineStage::kVertex));
mod()->AddFunction(std::move(foo));
@@ -446,9 +580,9 @@
}
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::StageDecoration>(ast::PipelineStage::kVertex));
@@ -470,13 +604,13 @@
}
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::StageDecoration>(ast::PipelineStage::kVertex));
mod()->AddFunction(std::move(foo));
@@ -497,14 +631,14 @@
}
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::StageDecoration>(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::StageDecoration>(ast::PipelineStage::kCompute));
mod()->AddFunction(std::move(bar));
@@ -530,19 +664,18 @@
}
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::StageDecoration>(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::StageDecoration>(ast::PipelineStage::kCompute));
mod()->AddFunction(std::move(bar));
@@ -569,12 +702,12 @@
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<bool>("foo", 1, bool_type(), nullptr);
- CreateConstantID<bool>("bar", 20, bool_type(), &val_true);
- CreateConstantID<bool>("baz", 300, bool_type(), &val_false);
+ AddConstantID<bool>("foo", 1, bool_type(), nullptr);
+ AddConstantID<bool>("bar", 20, bool_type(), &val_true);
+ AddConstantID<bool>("baz", 300, bool_type(), &val_false);
auto result = inspector()->GetConstantIDs();
ASSERT_EQ(3u, result.size());
@@ -591,10 +724,10 @@
EXPECT_FALSE(result[300].AsBool());
}
-TEST_F(InspectorGetEntryPointTest, U32ConstantIDs) {
+TEST_F(InspectorGetConstantIDsTest, U32) {
uint32_t val = 42;
- CreateConstantID<uint32_t>("foo", 1, u32_type(), nullptr);
- CreateConstantID<uint32_t>("bar", 20, u32_type(), &val);
+ AddConstantID<uint32_t>("foo", 1, u32_type(), nullptr);
+ AddConstantID<uint32_t>("bar", 20, u32_type(), &val);
auto result = inspector()->GetConstantIDs();
ASSERT_EQ(2u, result.size());
@@ -607,12 +740,12 @@
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<int32_t>("foo", 1, i32_type(), nullptr);
- CreateConstantID<int32_t>("bar", 20, i32_type(), &val_neg);
- CreateConstantID<int32_t>("baz", 300, i32_type(), &val_pos);
+ AddConstantID<int32_t>("foo", 1, i32_type(), nullptr);
+ AddConstantID<int32_t>("bar", 20, i32_type(), &val_neg);
+ AddConstantID<int32_t>("baz", 300, i32_type(), &val_pos);
auto result = inspector()->GetConstantIDs();
ASSERT_EQ(3u, result.size());
@@ -629,14 +762,14 @@
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<float>("foo", 1, f32_type(), nullptr);
- CreateConstantID<float>("bar", 20, f32_type(), &val_zero);
- CreateConstantID<float>("baz", 300, f32_type(), &val_neg);
- CreateConstantID<float>("x", 4000, f32_type(), &val_pos);
+ AddConstantID<float>("foo", 1, f32_type(), nullptr);
+ AddConstantID<float>("bar", 20, f32_type(), &val_zero);
+ AddConstantID<float>("baz", 300, f32_type(), &val_neg);
+ AddConstantID<float>("x", 4000, f32_type(), &val_pos);
auto result = inspector()->GetConstantIDs();
ASSERT_EQ(4u, result.size());
@@ -657,6 +790,200 @@
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 inspector
} // namespace tint