Implement Default Struct Layout
Implements https://github.com/gpuweb/gpuweb/pull/1447 SPIR-V Reader is still TODO, but continues to function as the offset decoration is still supported. Bug: tint:626 Bug: tint:629 Change-Id: Id574eb3a5c6729559382812de37b23f0c68fd406 Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/43640 Commit-Queue: Ben Clayton <bclayton@chromium.org> Reviewed-by: David Neto <dneto@google.com>
This commit is contained in:
parent
717fbbf183
commit
d614dd5d12
|
@ -750,8 +750,6 @@ int main(int argc, const char** argv) {
|
|||
<< "]:" << std::endl;
|
||||
std::cout << "\t\t resource_type = "
|
||||
<< ResourceTypeToString(binding.resource_type) << std::endl;
|
||||
std::cout << "\t\t min_buffer_binding_size = "
|
||||
<< binding.min_buffer_binding_size << std::endl;
|
||||
std::cout << "\t\t dim = " << TextureDimensionToString(binding.dim)
|
||||
<< std::endl;
|
||||
std::cout << "\t\t sampled_kind = "
|
||||
|
|
|
@ -317,8 +317,12 @@ source_set("libtint_core_src") {
|
|||
"ast/struct_block_decoration.h",
|
||||
"ast/struct_member.cc",
|
||||
"ast/struct_member.h",
|
||||
"ast/struct_member_align_decoration.cc",
|
||||
"ast/struct_member_align_decoration.h",
|
||||
"ast/struct_member_offset_decoration.cc",
|
||||
"ast/struct_member_offset_decoration.h",
|
||||
"ast/struct_member_size_decoration.cc",
|
||||
"ast/struct_member_size_decoration.h",
|
||||
"ast/switch_statement.cc",
|
||||
"ast/switch_statement.h",
|
||||
"ast/type_constructor_expression.cc",
|
||||
|
@ -367,11 +371,14 @@ source_set("libtint_core_src") {
|
|||
"resolver/resolver.cc",
|
||||
"resolver/resolver.h",
|
||||
"scope_stack.h",
|
||||
"semantic/array.h",
|
||||
"semantic/call.h",
|
||||
"semantic/call_target.h",
|
||||
"semantic/expression.h",
|
||||
"semantic/info.h",
|
||||
"semantic/intrinsic.h",
|
||||
"semantic/node.h",
|
||||
"semantic/sem_array.cc",
|
||||
"semantic/sem_call.cc",
|
||||
"semantic/sem_call_target.cc",
|
||||
"semantic/sem_expression.cc",
|
||||
|
@ -381,6 +388,7 @@ source_set("libtint_core_src") {
|
|||
"semantic/sem_member_accessor_expression.cc",
|
||||
"semantic/sem_node.cc",
|
||||
"semantic/sem_statement.cc",
|
||||
"semantic/sem_struct.cc",
|
||||
"semantic/sem_variable.cc",
|
||||
"semantic/type_mappings.h",
|
||||
"source.cc",
|
||||
|
|
|
@ -132,8 +132,12 @@ set(TINT_LIB_SRCS
|
|||
ast/struct_block_decoration.h
|
||||
ast/struct_member.cc
|
||||
ast/struct_member.h
|
||||
ast/struct_member_align_decoration.cc
|
||||
ast/struct_member_align_decoration.h
|
||||
ast/struct_member_offset_decoration.cc
|
||||
ast/struct_member_offset_decoration.h
|
||||
ast/struct_member_size_decoration.cc
|
||||
ast/struct_member_size_decoration.h
|
||||
ast/switch_statement.cc
|
||||
ast/switch_statement.h
|
||||
ast/type_constructor_expression.cc
|
||||
|
@ -182,11 +186,14 @@ set(TINT_LIB_SRCS
|
|||
resolver/resolver.cc
|
||||
resolver/resolver.h
|
||||
scope_stack.h
|
||||
semantic/array.h
|
||||
semantic/call.h
|
||||
semantic/call_target.h
|
||||
semantic/expression.h
|
||||
semantic/info.h
|
||||
semantic/intrinsic.h
|
||||
semantic/node.h
|
||||
semantic/sem_array.cc
|
||||
semantic/sem_call.cc
|
||||
semantic/sem_call_target.cc
|
||||
semantic/sem_expression.cc
|
||||
|
@ -196,6 +203,7 @@ set(TINT_LIB_SRCS
|
|||
semantic/sem_intrinsic.cc
|
||||
semantic/sem_node.cc
|
||||
semantic/sem_statement.cc
|
||||
semantic/sem_struct.cc
|
||||
semantic/sem_variable.cc
|
||||
semantic/type_mappings.h
|
||||
source.cc
|
||||
|
@ -436,7 +444,9 @@ if(${TINT_BUILD_TESTS})
|
|||
ast/sint_literal_test.cc
|
||||
ast/stage_decoration_test.cc
|
||||
ast/stride_decoration_test.cc
|
||||
ast/struct_member_align_decoration_test.cc
|
||||
ast/struct_member_offset_decoration_test.cc
|
||||
ast/struct_member_size_decoration_test.cc
|
||||
ast/struct_member_test.cc
|
||||
ast/struct_test.cc
|
||||
ast/switch_statement_test.cc
|
||||
|
@ -458,9 +468,11 @@ if(${TINT_BUILD_TESTS})
|
|||
intrinsic_table_test.cc
|
||||
program_test.cc
|
||||
resolver/intrinsic_test.cc
|
||||
resolver/is_storeable_test.cc
|
||||
resolver/resolver_test_helper.cc
|
||||
resolver/resolver_test_helper.h
|
||||
resolver/resolver_test.cc
|
||||
resolver/struct_layout_test.cc
|
||||
resolver/validation_test.cc
|
||||
scope_stack_test.cc
|
||||
semantic/sem_intrinsic_test.cc
|
||||
|
|
|
@ -28,9 +28,8 @@ TEST(ModuleCloneTest, Clone) {
|
|||
// See also fuzzers/tint_ast_clone_fuzzer.cc for further coverage of cloning.
|
||||
Source::File file("test.wgsl", R"([[block]]
|
||||
struct S {
|
||||
[[offset(0)]]
|
||||
[[size(4)]]
|
||||
m0 : u32;
|
||||
[[offset(4)]]
|
||||
m1 : array<u32>;
|
||||
};
|
||||
|
||||
|
@ -38,7 +37,7 @@ const c0 : i32 = 10;
|
|||
const c1 : bool = true;
|
||||
|
||||
type t0 = [[stride(16)]] array<vec4<f32>>;
|
||||
type t1 = [[stride(32)]] array<vec4<f32>>;
|
||||
type t1 = array<vec4<f32>>;
|
||||
|
||||
var<uniform> g0 : u32 = 20u;
|
||||
var<out> g1 : f32 = 123.0;
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
// Copyright 2021 The Tint Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "src/ast/struct_member_align_decoration.h"
|
||||
|
||||
#include "src/clone_context.h"
|
||||
#include "src/program_builder.h"
|
||||
|
||||
TINT_INSTANTIATE_TYPEINFO(tint::ast::StructMemberAlignDecoration);
|
||||
|
||||
namespace tint {
|
||||
namespace ast {
|
||||
|
||||
StructMemberAlignDecoration::StructMemberAlignDecoration(const Source& source,
|
||||
uint32_t align)
|
||||
: Base(source), align_(align) {}
|
||||
|
||||
StructMemberAlignDecoration::~StructMemberAlignDecoration() = default;
|
||||
|
||||
void StructMemberAlignDecoration::to_str(const semantic::Info&,
|
||||
std::ostream& out,
|
||||
size_t indent) const {
|
||||
make_indent(out, indent);
|
||||
out << "align " << std::to_string(align_);
|
||||
}
|
||||
|
||||
StructMemberAlignDecoration* StructMemberAlignDecoration::Clone(
|
||||
CloneContext* ctx) const {
|
||||
// Clone arguments outside of create() call to have deterministic ordering
|
||||
auto src = ctx->Clone(source());
|
||||
return ctx->dst->create<StructMemberAlignDecoration>(src, align_);
|
||||
}
|
||||
|
||||
} // namespace ast
|
||||
} // namespace tint
|
|
@ -0,0 +1,59 @@
|
|||
// Copyright 2021 The Tint Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef SRC_AST_STRUCT_MEMBER_ALIGN_DECORATION_H_
|
||||
#define SRC_AST_STRUCT_MEMBER_ALIGN_DECORATION_H_
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "src/ast/decoration.h"
|
||||
|
||||
namespace tint {
|
||||
namespace ast {
|
||||
|
||||
/// A struct member align decoration
|
||||
class StructMemberAlignDecoration
|
||||
: public Castable<StructMemberAlignDecoration, Decoration> {
|
||||
public:
|
||||
/// constructor
|
||||
/// @param source the source of this decoration
|
||||
/// @param align the align value
|
||||
StructMemberAlignDecoration(const Source& source, uint32_t align);
|
||||
~StructMemberAlignDecoration() override;
|
||||
|
||||
/// @returns the align value
|
||||
uint32_t align() const { return align_; }
|
||||
|
||||
/// Outputs the decoration to the given stream
|
||||
/// @param sem the semantic info for the program
|
||||
/// @param out the stream to write to
|
||||
/// @param indent number of spaces to indent the node when writing
|
||||
void to_str(const semantic::Info& sem,
|
||||
std::ostream& out,
|
||||
size_t indent) const override;
|
||||
|
||||
/// Clones this node and all transitive child nodes using the `CloneContext`
|
||||
/// `ctx`.
|
||||
/// @param ctx the clone context
|
||||
/// @return the newly cloned node
|
||||
StructMemberAlignDecoration* Clone(CloneContext* ctx) const override;
|
||||
|
||||
private:
|
||||
uint32_t const align_;
|
||||
};
|
||||
|
||||
} // namespace ast
|
||||
} // namespace tint
|
||||
|
||||
#endif // SRC_AST_STRUCT_MEMBER_ALIGN_DECORATION_H_
|
|
@ -0,0 +1,37 @@
|
|||
// Copyright 2021 The Tint Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "src/ast/struct_member_align_decoration.h"
|
||||
|
||||
#include "src/ast/test_helper.h"
|
||||
|
||||
namespace tint {
|
||||
namespace ast {
|
||||
namespace {
|
||||
|
||||
using StructMemberAlignDecorationTest = TestHelper;
|
||||
|
||||
TEST_F(StructMemberAlignDecorationTest, Creation) {
|
||||
auto* d = create<StructMemberAlignDecoration>(2);
|
||||
EXPECT_EQ(2u, d->align());
|
||||
}
|
||||
|
||||
TEST_F(StructMemberAlignDecorationTest, Is) {
|
||||
auto* d = create<StructMemberAlignDecoration>(2);
|
||||
EXPECT_TRUE(d->Is<StructMemberAlignDecoration>());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace ast
|
||||
} // namespace tint
|
|
@ -21,6 +21,8 @@ namespace tint {
|
|||
namespace ast {
|
||||
|
||||
/// A struct member offset decoration
|
||||
// [DEPRECATED] - Replaced with StructMemberAlignDecoration and
|
||||
// StructMemberSizeDecoration
|
||||
class StructMemberOffsetDecoration
|
||||
: public Castable<StructMemberOffsetDecoration, Decoration> {
|
||||
public:
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
// Copyright 2021 The Tint Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "src/ast/struct_member_size_decoration.h"
|
||||
|
||||
#include "src/clone_context.h"
|
||||
#include "src/program_builder.h"
|
||||
|
||||
TINT_INSTANTIATE_TYPEINFO(tint::ast::StructMemberSizeDecoration);
|
||||
|
||||
namespace tint {
|
||||
namespace ast {
|
||||
|
||||
StructMemberSizeDecoration::StructMemberSizeDecoration(const Source& source,
|
||||
uint32_t size)
|
||||
: Base(source), size_(size) {}
|
||||
|
||||
StructMemberSizeDecoration::~StructMemberSizeDecoration() = default;
|
||||
|
||||
void StructMemberSizeDecoration::to_str(const semantic::Info&,
|
||||
std::ostream& out,
|
||||
size_t indent) const {
|
||||
make_indent(out, indent);
|
||||
out << "size " << std::to_string(size_);
|
||||
}
|
||||
|
||||
StructMemberSizeDecoration* StructMemberSizeDecoration::Clone(
|
||||
CloneContext* ctx) const {
|
||||
// Clone arguments outside of create() call to have deterministic ordering
|
||||
auto src = ctx->Clone(source());
|
||||
return ctx->dst->create<StructMemberSizeDecoration>(src, size_);
|
||||
}
|
||||
|
||||
} // namespace ast
|
||||
} // namespace tint
|
|
@ -0,0 +1,59 @@
|
|||
// Copyright 2021 The Tint Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef SRC_AST_STRUCT_MEMBER_SIZE_DECORATION_H_
|
||||
#define SRC_AST_STRUCT_MEMBER_SIZE_DECORATION_H_
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "src/ast/decoration.h"
|
||||
|
||||
namespace tint {
|
||||
namespace ast {
|
||||
|
||||
/// A struct member size decoration
|
||||
class StructMemberSizeDecoration
|
||||
: public Castable<StructMemberSizeDecoration, Decoration> {
|
||||
public:
|
||||
/// constructor
|
||||
/// @param source the source of this decoration
|
||||
/// @param size the size value
|
||||
StructMemberSizeDecoration(const Source& source, uint32_t size);
|
||||
~StructMemberSizeDecoration() override;
|
||||
|
||||
/// @returns the size value
|
||||
uint32_t size() const { return size_; }
|
||||
|
||||
/// Outputs the decoration to the given stream
|
||||
/// @param sem the semantic info for the program
|
||||
/// @param out the stream to write to
|
||||
/// @param indent number of spaces to indent the node when writing
|
||||
void to_str(const semantic::Info& sem,
|
||||
std::ostream& out,
|
||||
size_t indent) const override;
|
||||
|
||||
/// Clones this node and all transitive child nodes using the `CloneContext`
|
||||
/// `ctx`.
|
||||
/// @param ctx the clone context
|
||||
/// @return the newly cloned node
|
||||
StructMemberSizeDecoration* Clone(CloneContext* ctx) const override;
|
||||
|
||||
private:
|
||||
uint32_t const size_;
|
||||
};
|
||||
|
||||
} // namespace ast
|
||||
} // namespace tint
|
||||
|
||||
#endif // SRC_AST_STRUCT_MEMBER_SIZE_DECORATION_H_
|
|
@ -0,0 +1,37 @@
|
|||
// Copyright 2021 The Tint Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "src/ast/struct_member_size_decoration.h"
|
||||
|
||||
#include "src/ast/test_helper.h"
|
||||
|
||||
namespace tint {
|
||||
namespace ast {
|
||||
namespace {
|
||||
|
||||
using StructMemberOffsetDecorationTest = TestHelper;
|
||||
|
||||
TEST_F(StructMemberOffsetDecorationTest, Creation) {
|
||||
auto* d = create<StructMemberSizeDecoration>(2);
|
||||
EXPECT_EQ(2u, d->size());
|
||||
}
|
||||
|
||||
TEST_F(StructMemberOffsetDecorationTest, Is) {
|
||||
auto* d = create<StructMemberSizeDecoration>(2);
|
||||
EXPECT_TRUE(d->Is<StructMemberSizeDecoration>());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace ast
|
||||
} // namespace tint
|
|
@ -22,11 +22,11 @@ namespace {
|
|||
using StructMemberTest = TestHelper;
|
||||
|
||||
TEST_F(StructMemberTest, Creation) {
|
||||
auto* st = Member("a", ty.i32(), {MemberOffset(4)});
|
||||
auto* st = Member("a", ty.i32(), {MemberSize(4)});
|
||||
EXPECT_EQ(st->symbol(), Symbol(1));
|
||||
EXPECT_EQ(st->type(), ty.i32());
|
||||
EXPECT_EQ(st->decorations().size(), 1u);
|
||||
EXPECT_TRUE(st->decorations()[0]->Is<StructMemberOffsetDecoration>());
|
||||
EXPECT_TRUE(st->decorations()[0]->Is<StructMemberSizeDecoration>());
|
||||
EXPECT_EQ(st->source().range.begin.line, 0u);
|
||||
EXPECT_EQ(st->source().range.begin.column, 0u);
|
||||
EXPECT_EQ(st->source().range.end.line, 0u);
|
||||
|
@ -68,14 +68,14 @@ TEST_F(StructMemberTest, Assert_NullDecoration) {
|
|||
EXPECT_FATAL_FAILURE(
|
||||
{
|
||||
ProgramBuilder b;
|
||||
b.Member("a", b.ty.i32(), {b.MemberOffset(4), nullptr});
|
||||
b.Member("a", b.ty.i32(), {b.MemberSize(4), nullptr});
|
||||
},
|
||||
"internal compiler error");
|
||||
}
|
||||
|
||||
TEST_F(StructMemberTest, ToStr) {
|
||||
auto* st = Member("a", ty.i32(), {MemberOffset(4)});
|
||||
EXPECT_EQ(str(st), "StructMember{[[ offset 4 ]] a: __i32}\n");
|
||||
auto* st = Member("a", ty.i32(), {MemberSize(4)});
|
||||
EXPECT_EQ(str(st), "StructMember{[[ size 4 ]] a: __i32}\n");
|
||||
}
|
||||
|
||||
TEST_F(StructMemberTest, ToStrNoDecorations) {
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include "src/ast/sint_literal.h"
|
||||
#include "src/ast/uint_literal.h"
|
||||
#include "src/semantic/function.h"
|
||||
#include "src/semantic/struct.h"
|
||||
#include "src/semantic/variable.h"
|
||||
#include "src/type/access_control_type.h"
|
||||
#include "src/type/array_type.h"
|
||||
|
@ -379,12 +380,18 @@ std::vector<ResourceBinding> Inspector::GetUniformBufferResourceBindings(
|
|||
continue;
|
||||
}
|
||||
|
||||
auto* sem = program_->Sem().Get(str);
|
||||
if (!sem) {
|
||||
error_ = "Missing semantic information for structure " +
|
||||
program_->Symbols().NameFor(str->symbol());
|
||||
continue;
|
||||
}
|
||||
|
||||
ResourceBinding entry;
|
||||
entry.resource_type = ResourceBinding::ResourceType::kUniformBuffer;
|
||||
entry.bind_group = binding_info.group->value();
|
||||
entry.binding = binding_info.binding->value();
|
||||
entry.min_buffer_binding_size =
|
||||
decl->type()->MinBufferBindingSize(type::MemoryLayout::kUniformBuffer);
|
||||
entry.size = sem->Size();
|
||||
|
||||
result.push_back(entry);
|
||||
}
|
||||
|
@ -541,7 +548,15 @@ std::vector<ResourceBinding> Inspector::GetStorageBufferResourceBindingsImpl(
|
|||
continue;
|
||||
}
|
||||
|
||||
if (!decl->type()->UnwrapIfNeeded()->Is<type::Struct>()) {
|
||||
auto* str = decl->type()->UnwrapIfNeeded()->As<type::Struct>();
|
||||
if (!str) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto* sem = program_->Sem().Get(str);
|
||||
if (!sem) {
|
||||
error_ = "Missing semantic information for structure " +
|
||||
program_->Symbols().NameFor(str->symbol());
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -551,8 +566,7 @@ std::vector<ResourceBinding> Inspector::GetStorageBufferResourceBindingsImpl(
|
|||
: ResourceBinding::ResourceType::kStorageBuffer;
|
||||
entry.bind_group = binding_info.group->value();
|
||||
entry.binding = binding_info.binding->value();
|
||||
entry.min_buffer_binding_size =
|
||||
decl->type()->MinBufferBindingSize(type::MemoryLayout::kStorageBuffer);
|
||||
entry.size = sem->Size();
|
||||
|
||||
result.push_back(entry);
|
||||
}
|
||||
|
|
|
@ -112,8 +112,8 @@ struct ResourceBinding {
|
|||
uint32_t bind_group;
|
||||
/// Identifier to identify this binding within the bind group
|
||||
uint32_t binding;
|
||||
/// Minimum size required for this binding, in bytes, if defined.
|
||||
uint64_t min_buffer_binding_size;
|
||||
/// Size for this binding, in bytes, if defined.
|
||||
uint64_t size;
|
||||
/// Dimensionality of this binding, if defined.
|
||||
TextureDimension dim;
|
||||
/// Kind of data being sampled, if defined.
|
||||
|
|
|
@ -190,22 +190,15 @@ class InspectorHelper : public ProgramBuilder {
|
|||
|
||||
/// Generates a struct type
|
||||
/// @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
|
||||
/// @param member_types a vector of member types
|
||||
/// @param is_block whether or not to decorate as a Block
|
||||
/// @returns a struct type
|
||||
type::Struct* MakeStructType(
|
||||
const std::string& name,
|
||||
std::vector<std::tuple<type::Type*, uint32_t>> members_info,
|
||||
bool is_block) {
|
||||
type::Struct* MakeStructType(const std::string& name,
|
||||
std::vector<type::Type*> member_types,
|
||||
bool is_block) {
|
||||
ast::StructMemberList members;
|
||||
for (auto& member_info : members_info) {
|
||||
type::Type* type;
|
||||
uint32_t offset;
|
||||
std::tie(type, offset) = member_info;
|
||||
|
||||
members.push_back(Member(StructMemberName(members.size(), type), type,
|
||||
{MemberOffset(offset)}));
|
||||
for (auto* type : member_types) {
|
||||
members.push_back(Member(StructMemberName(members.size(), type), type));
|
||||
}
|
||||
|
||||
ast::DecorationList decos;
|
||||
|
@ -214,20 +207,21 @@ class InspectorHelper : public ProgramBuilder {
|
|||
}
|
||||
|
||||
auto* str = create<ast::Struct>(members, decos);
|
||||
return ty.struct_(name, str);
|
||||
auto* str_ty = ty.struct_(name, str);
|
||||
AST().AddConstructedType(str_ty);
|
||||
return str_ty;
|
||||
}
|
||||
|
||||
/// Generates types appropriate for using in an uniform buffer
|
||||
/// @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
|
||||
/// @param member_types a vector of member types
|
||||
/// @returns a tuple {struct type, access control type}, where the struct has
|
||||
/// the layout for an uniform buffer, and the control type wraps the
|
||||
/// struct.
|
||||
std::tuple<type::Struct*, type::AccessControl*> MakeUniformBufferTypes(
|
||||
const std::string& name,
|
||||
std::vector<std::tuple<type::Type*, uint32_t>> members_info) {
|
||||
auto* struct_type = MakeStructType(name, members_info, true);
|
||||
std::vector<type::Type*> member_types) {
|
||||
auto* struct_type = MakeStructType(name, member_types, true);
|
||||
auto* access_type =
|
||||
create<type::AccessControl>(ast::AccessControl::kReadOnly, struct_type);
|
||||
return {struct_type, std::move(access_type)};
|
||||
|
@ -235,15 +229,14 @@ class InspectorHelper : public ProgramBuilder {
|
|||
|
||||
/// Generates types appropriate for using in a storage buffer
|
||||
/// @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
|
||||
/// @param member_types a vector of member types
|
||||
/// @returns a tuple {struct type, access control type}, where the struct has
|
||||
/// the layout for a storage buffer, and the control type wraps the
|
||||
/// struct.
|
||||
std::tuple<type::Struct*, type::AccessControl*> MakeStorageBufferTypes(
|
||||
const std::string& name,
|
||||
std::vector<std::tuple<type::Type*, uint32_t>> members_info) {
|
||||
auto* struct_type = MakeStructType(name, members_info, false);
|
||||
std::vector<type::Type*> member_types) {
|
||||
auto* struct_type = MakeStructType(name, member_types, false);
|
||||
auto* access_type = create<type::AccessControl>(
|
||||
ast::AccessControl::kReadWrite, struct_type);
|
||||
return {struct_type, std::move(access_type)};
|
||||
|
@ -251,16 +244,14 @@ class InspectorHelper : public ProgramBuilder {
|
|||
|
||||
/// Generates types appropriate for using in a read-only storage buffer
|
||||
/// @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
|
||||
/// @param member_types a vector of member types
|
||||
/// @returns a tuple {struct type, access control type}, where the struct has
|
||||
/// the layout for a read-only storage buffer, and the control type
|
||||
/// wraps the struct.
|
||||
std::tuple<type::Struct*, type::AccessControl*>
|
||||
MakeReadOnlyStorageBufferTypes(
|
||||
const std::string& name,
|
||||
std::vector<std::tuple<type::Type*, uint32_t>> members_info) {
|
||||
auto* struct_type = MakeStructType(name, members_info, false);
|
||||
MakeReadOnlyStorageBufferTypes(const std::string& name,
|
||||
std::vector<type::Type*> member_types) {
|
||||
auto* struct_type = MakeStructType(name, member_types, false);
|
||||
auto* access_type =
|
||||
create<type::AccessControl>(ast::AccessControl::kReadOnly, struct_type);
|
||||
return {struct_type, std::move(access_type)};
|
||||
|
@ -1404,21 +1395,21 @@ TEST_F(InspectorGetResourceBindingsTest, Simple) {
|
|||
type::Struct* ub_struct_type;
|
||||
type::AccessControl* ub_control_type;
|
||||
std::tie(ub_struct_type, ub_control_type) =
|
||||
MakeUniformBufferTypes("ub_type", {{ty.i32(), 0}});
|
||||
MakeUniformBufferTypes("ub_type", {ty.i32()});
|
||||
AddUniformBuffer("ub_var", ub_control_type, 0, 0);
|
||||
MakeStructVariableReferenceBodyFunction("ub_func", "ub_var", {{0, ty.i32()}});
|
||||
|
||||
type::Struct* sb_struct_type;
|
||||
type::AccessControl* sb_control_type;
|
||||
std::tie(sb_struct_type, sb_control_type) =
|
||||
MakeStorageBufferTypes("sb_type", {{ty.i32(), 0}});
|
||||
MakeStorageBufferTypes("sb_type", {ty.i32()});
|
||||
AddStorageBuffer("sb_var", sb_control_type, 1, 0);
|
||||
MakeStructVariableReferenceBodyFunction("sb_func", "sb_var", {{0, ty.i32()}});
|
||||
|
||||
type::Struct* ro_struct_type;
|
||||
type::AccessControl* ro_control_type;
|
||||
std::tie(ro_struct_type, ro_control_type) =
|
||||
MakeReadOnlyStorageBufferTypes("ro_type", {{ty.i32(), 0}});
|
||||
MakeReadOnlyStorageBufferTypes("ro_type", {ty.i32()});
|
||||
AddStorageBuffer("ro_var", ro_control_type, 1, 1);
|
||||
MakeStructVariableReferenceBodyFunction("ro_func", "ro_var", {{0, ty.i32()}});
|
||||
|
||||
|
@ -1499,7 +1490,7 @@ TEST_F(InspectorGetUniformBufferResourceBindingsTest, NonEntryPointFunc) {
|
|||
type::Struct* foo_struct_type;
|
||||
type::AccessControl* foo_control_type;
|
||||
std::tie(foo_struct_type, foo_control_type) =
|
||||
MakeUniformBufferTypes("foo_type", {{ty.i32(), 0}});
|
||||
MakeUniformBufferTypes("foo_type", {ty.i32()});
|
||||
AddUniformBuffer("foo_ub", foo_control_type, 0, 0);
|
||||
|
||||
MakeStructVariableReferenceBodyFunction("ub_func", "foo_ub", {{0, ty.i32()}});
|
||||
|
@ -1520,8 +1511,7 @@ TEST_F(InspectorGetUniformBufferResourceBindingsTest, NonEntryPointFunc) {
|
|||
TEST_F(InspectorGetUniformBufferResourceBindingsTest, MissingBlockDeco) {
|
||||
ast::DecorationList decos;
|
||||
auto* str = create<ast::Struct>(
|
||||
ast::StructMemberList{
|
||||
Member(StructMemberName(0, ty.i32()), ty.i32(), {MemberOffset(0)})},
|
||||
ast::StructMemberList{Member(StructMemberName(0, ty.i32()), ty.i32())},
|
||||
decos);
|
||||
|
||||
auto* foo_type = ty.struct_("foo_type", str);
|
||||
|
@ -1546,7 +1536,7 @@ TEST_F(InspectorGetUniformBufferResourceBindingsTest, Simple) {
|
|||
type::Struct* foo_struct_type;
|
||||
type::AccessControl* foo_control_type;
|
||||
std::tie(foo_struct_type, foo_control_type) =
|
||||
MakeUniformBufferTypes("foo_type", {{ty.i32(), 0}});
|
||||
MakeUniformBufferTypes("foo_type", {ty.i32()});
|
||||
AddUniformBuffer("foo_ub", foo_control_type, 0, 0);
|
||||
|
||||
MakeStructVariableReferenceBodyFunction("ub_func", "foo_ub", {{0, ty.i32()}});
|
||||
|
@ -1567,14 +1557,14 @@ TEST_F(InspectorGetUniformBufferResourceBindingsTest, Simple) {
|
|||
result[0].resource_type);
|
||||
EXPECT_EQ(0u, result[0].bind_group);
|
||||
EXPECT_EQ(0u, result[0].binding);
|
||||
EXPECT_EQ(16u, result[0].min_buffer_binding_size);
|
||||
EXPECT_EQ(4u, result[0].size);
|
||||
}
|
||||
|
||||
TEST_F(InspectorGetUniformBufferResourceBindingsTest, MultipleMembers) {
|
||||
type::Struct* foo_struct_type;
|
||||
type::AccessControl* foo_control_type;
|
||||
std::tie(foo_struct_type, foo_control_type) = MakeUniformBufferTypes(
|
||||
"foo_type", {{ty.i32(), 0}, {ty.u32(), 4}, {ty.f32(), 8}});
|
||||
std::tie(foo_struct_type, foo_control_type) =
|
||||
MakeUniformBufferTypes("foo_type", {ty.i32(), ty.u32(), ty.f32()});
|
||||
AddUniformBuffer("foo_ub", foo_control_type, 0, 0);
|
||||
|
||||
MakeStructVariableReferenceBodyFunction(
|
||||
|
@ -1596,14 +1586,14 @@ TEST_F(InspectorGetUniformBufferResourceBindingsTest, MultipleMembers) {
|
|||
result[0].resource_type);
|
||||
EXPECT_EQ(0u, result[0].bind_group);
|
||||
EXPECT_EQ(0u, result[0].binding);
|
||||
EXPECT_EQ(16u, result[0].min_buffer_binding_size);
|
||||
EXPECT_EQ(12u, result[0].size);
|
||||
}
|
||||
|
||||
TEST_F(InspectorGetUniformBufferResourceBindingsTest, MultipleUniformBuffers) {
|
||||
type::Struct* ub_struct_type;
|
||||
type::AccessControl* ub_control_type;
|
||||
std::tie(ub_struct_type, ub_control_type) = MakeUniformBufferTypes(
|
||||
"ub_type", {{ty.i32(), 0}, {ty.u32(), 4}, {ty.f32(), 8}});
|
||||
std::tie(ub_struct_type, ub_control_type) =
|
||||
MakeUniformBufferTypes("ub_type", {ty.i32(), ty.u32(), ty.f32()});
|
||||
AddUniformBuffer("ub_foo", ub_control_type, 0, 0);
|
||||
AddUniformBuffer("ub_bar", ub_control_type, 0, 1);
|
||||
AddUniformBuffer("ub_baz", ub_control_type, 2, 0);
|
||||
|
@ -1639,26 +1629,29 @@ TEST_F(InspectorGetUniformBufferResourceBindingsTest, MultipleUniformBuffers) {
|
|||
result[0].resource_type);
|
||||
EXPECT_EQ(0u, result[0].bind_group);
|
||||
EXPECT_EQ(0u, result[0].binding);
|
||||
EXPECT_EQ(16u, result[0].min_buffer_binding_size);
|
||||
EXPECT_EQ(12u, result[0].size);
|
||||
|
||||
EXPECT_EQ(ResourceBinding::ResourceType::kUniformBuffer,
|
||||
result[1].resource_type);
|
||||
EXPECT_EQ(0u, result[1].bind_group);
|
||||
EXPECT_EQ(1u, result[1].binding);
|
||||
EXPECT_EQ(16u, result[1].min_buffer_binding_size);
|
||||
EXPECT_EQ(12u, result[1].size);
|
||||
|
||||
EXPECT_EQ(ResourceBinding::ResourceType::kUniformBuffer,
|
||||
result[2].resource_type);
|
||||
EXPECT_EQ(2u, result[2].bind_group);
|
||||
EXPECT_EQ(0u, result[2].binding);
|
||||
EXPECT_EQ(16u, result[2].min_buffer_binding_size);
|
||||
EXPECT_EQ(12u, result[2].size);
|
||||
}
|
||||
|
||||
TEST_F(InspectorGetUniformBufferResourceBindingsTest, ContainingArray) {
|
||||
type::Struct* foo_struct_type;
|
||||
type::AccessControl* foo_control_type;
|
||||
std::tie(foo_struct_type, foo_control_type) = MakeUniformBufferTypes(
|
||||
"foo_type", {{ty.i32(), 0}, {u32_array_type(4), 4}});
|
||||
// TODO(bclayton) - This is not a legal structure layout for uniform buffer
|
||||
// usage. Once crbug.com/tint/628 is implemented, this will fail validation
|
||||
// and will need to be fixed.
|
||||
std::tie(foo_struct_type, foo_control_type) =
|
||||
MakeUniformBufferTypes("foo_type", {ty.i32(), u32_array_type(4)});
|
||||
AddUniformBuffer("foo_ub", foo_control_type, 0, 0);
|
||||
|
||||
MakeStructVariableReferenceBodyFunction("ub_func", "foo_ub", {{0, ty.i32()}});
|
||||
|
@ -1679,14 +1672,14 @@ TEST_F(InspectorGetUniformBufferResourceBindingsTest, ContainingArray) {
|
|||
result[0].resource_type);
|
||||
EXPECT_EQ(0u, result[0].bind_group);
|
||||
EXPECT_EQ(0u, result[0].binding);
|
||||
EXPECT_EQ(32u, result[0].min_buffer_binding_size);
|
||||
EXPECT_EQ(20u, result[0].size);
|
||||
}
|
||||
|
||||
TEST_F(InspectorGetStorageBufferResourceBindingsTest, Simple) {
|
||||
type::Struct* foo_struct_type;
|
||||
type::AccessControl* foo_control_type;
|
||||
std::tie(foo_struct_type, foo_control_type) =
|
||||
MakeStorageBufferTypes("foo_type", {{ty.i32(), 0}});
|
||||
MakeStorageBufferTypes("foo_type", {ty.i32()});
|
||||
AddStorageBuffer("foo_sb", foo_control_type, 0, 0);
|
||||
|
||||
MakeStructVariableReferenceBodyFunction("sb_func", "foo_sb", {{0, ty.i32()}});
|
||||
|
@ -1707,14 +1700,14 @@ TEST_F(InspectorGetStorageBufferResourceBindingsTest, Simple) {
|
|||
result[0].resource_type);
|
||||
EXPECT_EQ(0u, result[0].bind_group);
|
||||
EXPECT_EQ(0u, result[0].binding);
|
||||
EXPECT_EQ(4u, result[0].min_buffer_binding_size);
|
||||
EXPECT_EQ(4u, result[0].size);
|
||||
}
|
||||
|
||||
TEST_F(InspectorGetStorageBufferResourceBindingsTest, MultipleMembers) {
|
||||
type::Struct* foo_struct_type;
|
||||
type::AccessControl* foo_control_type;
|
||||
std::tie(foo_struct_type, foo_control_type) = MakeStorageBufferTypes(
|
||||
"foo_type", {{ty.i32(), 0}, {ty.u32(), 4}, {ty.f32(), 8}});
|
||||
std::tie(foo_struct_type, foo_control_type) =
|
||||
MakeStorageBufferTypes("foo_type", {ty.i32(), ty.u32(), ty.f32()});
|
||||
AddStorageBuffer("foo_sb", foo_control_type, 0, 0);
|
||||
|
||||
MakeStructVariableReferenceBodyFunction(
|
||||
|
@ -1736,14 +1729,14 @@ TEST_F(InspectorGetStorageBufferResourceBindingsTest, MultipleMembers) {
|
|||
result[0].resource_type);
|
||||
EXPECT_EQ(0u, result[0].bind_group);
|
||||
EXPECT_EQ(0u, result[0].binding);
|
||||
EXPECT_EQ(12u, result[0].min_buffer_binding_size);
|
||||
EXPECT_EQ(12u, result[0].size);
|
||||
}
|
||||
|
||||
TEST_F(InspectorGetStorageBufferResourceBindingsTest, MultipleStorageBuffers) {
|
||||
type::Struct* sb_struct_type;
|
||||
type::AccessControl* sb_control_type;
|
||||
std::tie(sb_struct_type, sb_control_type) = MakeStorageBufferTypes(
|
||||
"sb_type", {{ty.i32(), 0}, {ty.u32(), 4}, {ty.f32(), 8}});
|
||||
std::tie(sb_struct_type, sb_control_type) =
|
||||
MakeStorageBufferTypes("sb_type", {ty.i32(), ty.u32(), ty.f32()});
|
||||
AddStorageBuffer("sb_foo", sb_control_type, 0, 0);
|
||||
AddStorageBuffer("sb_bar", sb_control_type, 0, 1);
|
||||
AddStorageBuffer("sb_baz", sb_control_type, 2, 0);
|
||||
|
@ -1782,26 +1775,26 @@ TEST_F(InspectorGetStorageBufferResourceBindingsTest, MultipleStorageBuffers) {
|
|||
result[0].resource_type);
|
||||
EXPECT_EQ(0u, result[0].bind_group);
|
||||
EXPECT_EQ(0u, result[0].binding);
|
||||
EXPECT_EQ(12u, result[0].min_buffer_binding_size);
|
||||
EXPECT_EQ(12u, result[0].size);
|
||||
|
||||
EXPECT_EQ(ResourceBinding::ResourceType::kStorageBuffer,
|
||||
result[1].resource_type);
|
||||
EXPECT_EQ(0u, result[1].bind_group);
|
||||
EXPECT_EQ(1u, result[1].binding);
|
||||
EXPECT_EQ(12u, result[1].min_buffer_binding_size);
|
||||
EXPECT_EQ(12u, result[1].size);
|
||||
|
||||
EXPECT_EQ(ResourceBinding::ResourceType::kStorageBuffer,
|
||||
result[2].resource_type);
|
||||
EXPECT_EQ(2u, result[2].bind_group);
|
||||
EXPECT_EQ(0u, result[2].binding);
|
||||
EXPECT_EQ(12u, result[2].min_buffer_binding_size);
|
||||
EXPECT_EQ(12u, result[2].size);
|
||||
}
|
||||
|
||||
TEST_F(InspectorGetStorageBufferResourceBindingsTest, ContainingArray) {
|
||||
type::Struct* foo_struct_type;
|
||||
type::AccessControl* foo_control_type;
|
||||
std::tie(foo_struct_type, foo_control_type) = MakeStorageBufferTypes(
|
||||
"foo_type", {{ty.i32(), 0}, {u32_array_type(4), 4}});
|
||||
std::tie(foo_struct_type, foo_control_type) =
|
||||
MakeStorageBufferTypes("foo_type", {ty.i32(), u32_array_type(4)});
|
||||
AddStorageBuffer("foo_sb", foo_control_type, 0, 0);
|
||||
|
||||
MakeStructVariableReferenceBodyFunction("sb_func", "foo_sb", {{0, ty.i32()}});
|
||||
|
@ -1822,14 +1815,14 @@ TEST_F(InspectorGetStorageBufferResourceBindingsTest, ContainingArray) {
|
|||
result[0].resource_type);
|
||||
EXPECT_EQ(0u, result[0].bind_group);
|
||||
EXPECT_EQ(0u, result[0].binding);
|
||||
EXPECT_EQ(20u, result[0].min_buffer_binding_size);
|
||||
EXPECT_EQ(20u, result[0].size);
|
||||
}
|
||||
|
||||
TEST_F(InspectorGetStorageBufferResourceBindingsTest, ContainingRuntimeArray) {
|
||||
type::Struct* foo_struct_type;
|
||||
type::AccessControl* foo_control_type;
|
||||
std::tie(foo_struct_type, foo_control_type) = MakeStorageBufferTypes(
|
||||
"foo_type", {{ty.i32(), 0}, {u32_array_type(0), 4}});
|
||||
std::tie(foo_struct_type, foo_control_type) =
|
||||
MakeStorageBufferTypes("foo_type", {ty.i32(), u32_array_type(0)});
|
||||
AddStorageBuffer("foo_sb", foo_control_type, 0, 0);
|
||||
|
||||
MakeStructVariableReferenceBodyFunction("sb_func", "foo_sb", {{0, ty.i32()}});
|
||||
|
@ -1850,14 +1843,14 @@ TEST_F(InspectorGetStorageBufferResourceBindingsTest, ContainingRuntimeArray) {
|
|||
result[0].resource_type);
|
||||
EXPECT_EQ(0u, result[0].bind_group);
|
||||
EXPECT_EQ(0u, result[0].binding);
|
||||
EXPECT_EQ(8u, result[0].min_buffer_binding_size);
|
||||
EXPECT_EQ(8u, result[0].size);
|
||||
}
|
||||
|
||||
TEST_F(InspectorGetStorageBufferResourceBindingsTest, SkipReadOnly) {
|
||||
type::Struct* foo_struct_type;
|
||||
type::AccessControl* foo_control_type;
|
||||
std::tie(foo_struct_type, foo_control_type) =
|
||||
MakeReadOnlyStorageBufferTypes("foo_type", {{ty.i32(), 0}});
|
||||
MakeReadOnlyStorageBufferTypes("foo_type", {ty.i32()});
|
||||
AddStorageBuffer("foo_sb", foo_control_type, 0, 0);
|
||||
|
||||
MakeStructVariableReferenceBodyFunction("sb_func", "foo_sb", {{0, ty.i32()}});
|
||||
|
@ -1879,7 +1872,7 @@ TEST_F(InspectorGetReadOnlyStorageBufferResourceBindingsTest, Simple) {
|
|||
type::Struct* foo_struct_type;
|
||||
type::AccessControl* foo_control_type;
|
||||
std::tie(foo_struct_type, foo_control_type) =
|
||||
MakeReadOnlyStorageBufferTypes("foo_type", {{ty.i32(), 0}});
|
||||
MakeReadOnlyStorageBufferTypes("foo_type", {ty.i32()});
|
||||
AddStorageBuffer("foo_sb", foo_control_type, 0, 0);
|
||||
|
||||
MakeStructVariableReferenceBodyFunction("sb_func", "foo_sb", {{0, ty.i32()}});
|
||||
|
@ -1900,15 +1893,15 @@ TEST_F(InspectorGetReadOnlyStorageBufferResourceBindingsTest, Simple) {
|
|||
result[0].resource_type);
|
||||
EXPECT_EQ(0u, result[0].bind_group);
|
||||
EXPECT_EQ(0u, result[0].binding);
|
||||
EXPECT_EQ(4u, result[0].min_buffer_binding_size);
|
||||
EXPECT_EQ(4u, result[0].size);
|
||||
}
|
||||
|
||||
TEST_F(InspectorGetReadOnlyStorageBufferResourceBindingsTest,
|
||||
MultipleStorageBuffers) {
|
||||
type::Struct* sb_struct_type;
|
||||
type::AccessControl* sb_control_type;
|
||||
std::tie(sb_struct_type, sb_control_type) = MakeReadOnlyStorageBufferTypes(
|
||||
"sb_type", {{ty.i32(), 0}, {ty.u32(), 4}, {ty.f32(), 8}});
|
||||
std::tie(sb_struct_type, sb_control_type) =
|
||||
MakeReadOnlyStorageBufferTypes("sb_type", {ty.i32(), ty.u32(), ty.f32()});
|
||||
AddStorageBuffer("sb_foo", sb_control_type, 0, 0);
|
||||
AddStorageBuffer("sb_bar", sb_control_type, 0, 1);
|
||||
AddStorageBuffer("sb_baz", sb_control_type, 2, 0);
|
||||
|
@ -1947,26 +1940,26 @@ TEST_F(InspectorGetReadOnlyStorageBufferResourceBindingsTest,
|
|||
result[0].resource_type);
|
||||
EXPECT_EQ(0u, result[0].bind_group);
|
||||
EXPECT_EQ(0u, result[0].binding);
|
||||
EXPECT_EQ(12u, result[0].min_buffer_binding_size);
|
||||
EXPECT_EQ(12u, result[0].size);
|
||||
|
||||
EXPECT_EQ(ResourceBinding::ResourceType::kReadOnlyStorageBuffer,
|
||||
result[1].resource_type);
|
||||
EXPECT_EQ(0u, result[1].bind_group);
|
||||
EXPECT_EQ(1u, result[1].binding);
|
||||
EXPECT_EQ(12u, result[1].min_buffer_binding_size);
|
||||
EXPECT_EQ(12u, result[1].size);
|
||||
|
||||
EXPECT_EQ(ResourceBinding::ResourceType::kReadOnlyStorageBuffer,
|
||||
result[2].resource_type);
|
||||
EXPECT_EQ(2u, result[2].bind_group);
|
||||
EXPECT_EQ(0u, result[2].binding);
|
||||
EXPECT_EQ(12u, result[2].min_buffer_binding_size);
|
||||
EXPECT_EQ(12u, result[2].size);
|
||||
}
|
||||
|
||||
TEST_F(InspectorGetReadOnlyStorageBufferResourceBindingsTest, ContainingArray) {
|
||||
type::Struct* foo_struct_type;
|
||||
type::AccessControl* foo_control_type;
|
||||
std::tie(foo_struct_type, foo_control_type) = MakeReadOnlyStorageBufferTypes(
|
||||
"foo_type", {{ty.i32(), 0}, {u32_array_type(4), 4}});
|
||||
std::tie(foo_struct_type, foo_control_type) =
|
||||
MakeReadOnlyStorageBufferTypes("foo_type", {ty.i32(), u32_array_type(4)});
|
||||
AddStorageBuffer("foo_sb", foo_control_type, 0, 0);
|
||||
|
||||
MakeStructVariableReferenceBodyFunction("sb_func", "foo_sb", {{0, ty.i32()}});
|
||||
|
@ -1987,15 +1980,15 @@ TEST_F(InspectorGetReadOnlyStorageBufferResourceBindingsTest, ContainingArray) {
|
|||
result[0].resource_type);
|
||||
EXPECT_EQ(0u, result[0].bind_group);
|
||||
EXPECT_EQ(0u, result[0].binding);
|
||||
EXPECT_EQ(20u, result[0].min_buffer_binding_size);
|
||||
EXPECT_EQ(20u, result[0].size);
|
||||
}
|
||||
|
||||
TEST_F(InspectorGetReadOnlyStorageBufferResourceBindingsTest,
|
||||
ContainingRuntimeArray) {
|
||||
type::Struct* foo_struct_type;
|
||||
type::AccessControl* foo_control_type;
|
||||
std::tie(foo_struct_type, foo_control_type) = MakeReadOnlyStorageBufferTypes(
|
||||
"foo_type", {{ty.i32(), 0}, {u32_array_type(0), 4}});
|
||||
std::tie(foo_struct_type, foo_control_type) =
|
||||
MakeReadOnlyStorageBufferTypes("foo_type", {ty.i32(), u32_array_type(0)});
|
||||
AddStorageBuffer("foo_sb", foo_control_type, 0, 0);
|
||||
|
||||
MakeStructVariableReferenceBodyFunction("sb_func", "foo_sb", {{0, ty.i32()}});
|
||||
|
@ -2016,14 +2009,14 @@ TEST_F(InspectorGetReadOnlyStorageBufferResourceBindingsTest,
|
|||
result[0].resource_type);
|
||||
EXPECT_EQ(0u, result[0].bind_group);
|
||||
EXPECT_EQ(0u, result[0].binding);
|
||||
EXPECT_EQ(8u, result[0].min_buffer_binding_size);
|
||||
EXPECT_EQ(8u, result[0].size);
|
||||
}
|
||||
|
||||
TEST_F(InspectorGetReadOnlyStorageBufferResourceBindingsTest, SkipNonReadOnly) {
|
||||
type::Struct* foo_struct_type;
|
||||
type::AccessControl* foo_control_type;
|
||||
std::tie(foo_struct_type, foo_control_type) =
|
||||
MakeStorageBufferTypes("foo_type", {{ty.i32(), 0}});
|
||||
MakeStorageBufferTypes("foo_type", {ty.i32()});
|
||||
AddStorageBuffer("foo_sb", foo_control_type, 0, 0);
|
||||
|
||||
MakeStructVariableReferenceBodyFunction("sb_func", "foo_sb", {{0, ty.i32()}});
|
||||
|
|
|
@ -31,7 +31,9 @@
|
|||
#include "src/ast/scalar_constructor_expression.h"
|
||||
#include "src/ast/sint_literal.h"
|
||||
#include "src/ast/stride_decoration.h"
|
||||
#include "src/ast/struct_member_align_decoration.h"
|
||||
#include "src/ast/struct_member_offset_decoration.h"
|
||||
#include "src/ast/struct_member_size_decoration.h"
|
||||
#include "src/ast/type_constructor_expression.h"
|
||||
#include "src/ast/uint_literal.h"
|
||||
#include "src/ast/variable_decl_statement.h"
|
||||
|
@ -955,6 +957,36 @@ class ProgramBuilder {
|
|||
return create<ast::StructMemberOffsetDecoration>(source_, val);
|
||||
}
|
||||
|
||||
/// Creates a ast::StructMemberSizeDecoration
|
||||
/// @param source the source information
|
||||
/// @param val the size value
|
||||
/// @returns the size decoration pointer
|
||||
ast::StructMemberSizeDecoration* MemberSize(Source source, uint32_t val) {
|
||||
return create<ast::StructMemberSizeDecoration>(source, val);
|
||||
}
|
||||
|
||||
/// Creates a ast::StructMemberSizeDecoration
|
||||
/// @param val the size value
|
||||
/// @returns the size decoration pointer
|
||||
ast::StructMemberSizeDecoration* MemberSize(uint32_t val) {
|
||||
return create<ast::StructMemberSizeDecoration>(source_, val);
|
||||
}
|
||||
|
||||
/// Creates a ast::StructMemberAlignDecoration
|
||||
/// @param source the source information
|
||||
/// @param val the align value
|
||||
/// @returns the align decoration pointer
|
||||
ast::StructMemberAlignDecoration* MemberAlign(Source source, uint32_t val) {
|
||||
return create<ast::StructMemberAlignDecoration>(source, val);
|
||||
}
|
||||
|
||||
/// Creates a ast::StructMemberAlignDecoration
|
||||
/// @param val the align value
|
||||
/// @returns the align decoration pointer
|
||||
ast::StructMemberAlignDecoration* MemberAlign(uint32_t val) {
|
||||
return create<ast::StructMemberAlignDecoration>(source_, val);
|
||||
}
|
||||
|
||||
/// Creates an ast::Function and registers it with the ast::Module.
|
||||
/// @param source the source information
|
||||
/// @param name the function name
|
||||
|
@ -995,37 +1027,64 @@ class ProgramBuilder {
|
|||
return func;
|
||||
}
|
||||
|
||||
/// Creates a ast::Struct and type::Struct, registering the type::Struct with
|
||||
/// the AST().ConstructedTypes().
|
||||
/// @param source the source information
|
||||
/// @param name the struct name
|
||||
/// @param members the struct members
|
||||
/// @param decorations the optional struct decorations
|
||||
/// @returns the struct type
|
||||
type::Struct* Structure(const Source& source,
|
||||
const std::string& name,
|
||||
ast::StructMemberList members,
|
||||
ast::DecorationList decorations = {}) {
|
||||
auto* impl =
|
||||
create<ast::Struct>(source, std::move(members), std::move(decorations));
|
||||
auto* type = ty.struct_(name, impl);
|
||||
AST().AddConstructedType(type);
|
||||
return type;
|
||||
}
|
||||
|
||||
/// Creates a ast::Struct and type::Struct, registering the type::Struct with
|
||||
/// the AST().ConstructedTypes().
|
||||
/// @param name the struct name
|
||||
/// @param members the struct members
|
||||
/// @param decorations the optional struct decorations
|
||||
/// @returns the struct type
|
||||
type::Struct* Structure(const std::string& name,
|
||||
ast::StructMemberList members,
|
||||
ast::DecorationList decorations = {}) {
|
||||
auto* impl =
|
||||
create<ast::Struct>(std::move(members), std::move(decorations));
|
||||
auto* type = ty.struct_(name, impl);
|
||||
AST().AddConstructedType(type);
|
||||
return type;
|
||||
}
|
||||
|
||||
/// Creates a ast::StructMember
|
||||
/// @param source the source information
|
||||
/// @param name the struct member name
|
||||
/// @param type the struct member type
|
||||
/// @param decorations the optional struct member decorations
|
||||
/// @returns the struct member pointer
|
||||
ast::StructMember* Member(const Source& source,
|
||||
const std::string& name,
|
||||
type::Type* type) {
|
||||
type::Type* type,
|
||||
ast::DecorationList decorations = {}) {
|
||||
return create<ast::StructMember>(source, Symbols().Register(name), type,
|
||||
ast::DecorationList{});
|
||||
std::move(decorations));
|
||||
}
|
||||
|
||||
/// Creates a ast::StructMember
|
||||
/// @param name the struct member name
|
||||
/// @param type the struct member type
|
||||
/// @returns the struct member pointer
|
||||
ast::StructMember* Member(const std::string& name, type::Type* type) {
|
||||
return create<ast::StructMember>(source_, Symbols().Register(name), type,
|
||||
ast::DecorationList{});
|
||||
}
|
||||
|
||||
/// Creates a ast::StructMember
|
||||
/// @param name the struct member name
|
||||
/// @param type the struct member type
|
||||
/// @param decorations the struct member decorations
|
||||
/// @param decorations the optional struct member decorations
|
||||
/// @returns the struct member pointer
|
||||
ast::StructMember* Member(const std::string& name,
|
||||
type::Type* type,
|
||||
ast::DecorationList decorations) {
|
||||
ast::DecorationList decorations = {}) {
|
||||
return create<ast::StructMember>(source_, Symbols().Register(name), type,
|
||||
decorations);
|
||||
std::move(decorations));
|
||||
}
|
||||
|
||||
/// Creates a ast::StructMember with the given byte offset
|
||||
|
|
|
@ -331,8 +331,7 @@ TEST_F(SpvParserTest, ConvertType_RuntimeArray) {
|
|||
EXPECT_TRUE(arr_type->IsRuntimeArray());
|
||||
ASSERT_NE(arr_type, nullptr);
|
||||
EXPECT_EQ(arr_type->size(), 0u);
|
||||
EXPECT_EQ(arr_type->array_stride(), 0u);
|
||||
EXPECT_FALSE(arr_type->has_array_stride());
|
||||
EXPECT_EQ(arr_type->decorations().size(), 0u);
|
||||
auto* elem_type = arr_type->type();
|
||||
ASSERT_NE(elem_type, nullptr);
|
||||
EXPECT_TRUE(elem_type->Is<type::U32>());
|
||||
|
@ -365,8 +364,10 @@ TEST_F(SpvParserTest, ConvertType_RuntimeArray_ArrayStride_Valid) {
|
|||
auto* arr_type = type->As<type::Array>();
|
||||
EXPECT_TRUE(arr_type->IsRuntimeArray());
|
||||
ASSERT_NE(arr_type, nullptr);
|
||||
EXPECT_EQ(arr_type->array_stride(), 64u);
|
||||
EXPECT_TRUE(arr_type->has_array_stride());
|
||||
ASSERT_EQ(arr_type->decorations().size(), 1u);
|
||||
auto* stride = arr_type->decorations()[0];
|
||||
ASSERT_TRUE(stride->Is<ast::StrideDecoration>());
|
||||
ASSERT_EQ(stride->As<ast::StrideDecoration>()->stride(), 64u);
|
||||
EXPECT_TRUE(p->error().empty());
|
||||
}
|
||||
|
||||
|
@ -413,8 +414,7 @@ TEST_F(SpvParserTest, ConvertType_Array) {
|
|||
EXPECT_FALSE(arr_type->IsRuntimeArray());
|
||||
ASSERT_NE(arr_type, nullptr);
|
||||
EXPECT_EQ(arr_type->size(), 42u);
|
||||
EXPECT_EQ(arr_type->array_stride(), 0u);
|
||||
EXPECT_FALSE(arr_type->has_array_stride());
|
||||
EXPECT_EQ(arr_type->decorations().size(), 0u);
|
||||
auto* elem_type = arr_type->type();
|
||||
ASSERT_NE(elem_type, nullptr);
|
||||
EXPECT_TRUE(elem_type->Is<type::U32>());
|
||||
|
@ -499,8 +499,12 @@ TEST_F(SpvParserTest, ConvertType_ArrayStride_Valid) {
|
|||
EXPECT_TRUE(type->Is<type::Array>());
|
||||
auto* arr_type = type->As<type::Array>();
|
||||
ASSERT_NE(arr_type, nullptr);
|
||||
ASSERT_EQ(arr_type->array_stride(), 8u);
|
||||
EXPECT_TRUE(arr_type->has_array_stride());
|
||||
|
||||
ASSERT_EQ(arr_type->decorations().size(), 1u);
|
||||
auto* stride = arr_type->decorations()[0];
|
||||
ASSERT_TRUE(stride->Is<ast::StrideDecoration>());
|
||||
ASSERT_EQ(stride->As<ast::StrideDecoration>()->stride(), 8u);
|
||||
|
||||
EXPECT_TRUE(p->error().empty());
|
||||
}
|
||||
|
||||
|
|
|
@ -104,7 +104,9 @@ const char kBuiltinDecoration[] = "builtin";
|
|||
const char kConstantIdDecoration[] = "constant_id";
|
||||
const char kGroupDecoration[] = "group";
|
||||
const char kLocationDecoration[] = "location";
|
||||
const char kOffsetDecoration[] = "offset";
|
||||
const char kOffsetDecoration[] = "offset"; // DEPRECATED
|
||||
const char kSizeDecoration[] = "size";
|
||||
const char kAlignDecoration[] = "align";
|
||||
const char kSetDecoration[] = "set";
|
||||
const char kStageDecoration[] = "stage";
|
||||
const char kStrideDecoration[] = "stride";
|
||||
|
@ -115,11 +117,12 @@ bool is_decoration(Token t) {
|
|||
return false;
|
||||
|
||||
auto s = t.to_str();
|
||||
return s == kAccessDecoration || s == kBindingDecoration ||
|
||||
s == kBlockDecoration || s == kBuiltinDecoration ||
|
||||
s == kConstantIdDecoration || s == kLocationDecoration ||
|
||||
return s == kAccessDecoration || s == kAlignDecoration ||
|
||||
s == kBindingDecoration || s == kBlockDecoration ||
|
||||
s == kBuiltinDecoration || s == kConstantIdDecoration ||
|
||||
s == kGroupDecoration || s == kLocationDecoration ||
|
||||
s == kOffsetDecoration || s == kSetDecoration ||
|
||||
s == kGroupDecoration || s == kStageDecoration ||
|
||||
s == kSizeDecoration || s == kStageDecoration ||
|
||||
s == kStrideDecoration || s == kWorkgroupSizeDecoration;
|
||||
}
|
||||
|
||||
|
@ -2919,6 +2922,28 @@ Maybe<ast::Decoration*> ParserImpl::decoration() {
|
|||
});
|
||||
}
|
||||
|
||||
if (s == kSizeDecoration) {
|
||||
const char* use = "size decoration";
|
||||
return expect_paren_block(use, [&]() -> Result {
|
||||
auto val = expect_positive_sint(use);
|
||||
if (val.errored)
|
||||
return Failure::kErrored;
|
||||
|
||||
return create<ast::StructMemberSizeDecoration>(t.source(), val.value);
|
||||
});
|
||||
}
|
||||
|
||||
if (s == kAlignDecoration) {
|
||||
const char* use = "align decoration";
|
||||
return expect_paren_block(use, [&]() -> Result {
|
||||
auto val = expect_positive_sint(use);
|
||||
if (val.errored)
|
||||
return Failure::kErrored;
|
||||
|
||||
return create<ast::StructMemberAlignDecoration>(t.source(), val.value);
|
||||
});
|
||||
}
|
||||
|
||||
if (s == kConstantIdDecoration) {
|
||||
const char* use = "constant_id decoration";
|
||||
return expect_paren_block(use, [&]() -> Result {
|
||||
|
|
|
@ -704,35 +704,64 @@ TEST_F(ParserImplErrorTest, GlobalDeclStructMemberMissingSemicolon) {
|
|||
" ^\n");
|
||||
}
|
||||
|
||||
TEST_F(ParserImplErrorTest, GlobalDeclStructMemberOffsetMissingLParen) {
|
||||
EXPECT("struct S { [[offset 1)]] i : i32; };",
|
||||
"test.wgsl:1:21 error: expected '(' for offset decoration\n"
|
||||
"struct S { [[offset 1)]] i : i32; };\n"
|
||||
" ^\n");
|
||||
TEST_F(ParserImplErrorTest, GlobalDeclStructMemberAlignMissingLParen) {
|
||||
EXPECT("struct S { [[align 1)]] i : i32; };",
|
||||
"test.wgsl:1:20 error: expected '(' for align decoration\n"
|
||||
"struct S { [[align 1)]] i : i32; };\n"
|
||||
" ^\n");
|
||||
}
|
||||
|
||||
TEST_F(ParserImplErrorTest, GlobalDeclStructMemberOffsetMissingRParen) {
|
||||
EXPECT("struct S { [[offset(1]] i : i32; };",
|
||||
"test.wgsl:1:22 error: expected ')' for offset decoration\n"
|
||||
"struct S { [[offset(1]] i : i32; };\n"
|
||||
" ^^\n");
|
||||
}
|
||||
|
||||
TEST_F(ParserImplErrorTest, GlobalDeclStructMemberOffsetInvaldValue) {
|
||||
EXPECT("struct S { [[offset(x)]] i : i32; };",
|
||||
"test.wgsl:1:21 error: expected signed integer literal for offset "
|
||||
"decoration\n"
|
||||
"struct S { [[offset(x)]] i : i32; };\n"
|
||||
" ^\n");
|
||||
}
|
||||
|
||||
TEST_F(ParserImplErrorTest, GlobalDeclStructMemberOffsetNegativeValue) {
|
||||
EXPECT("struct S { [[offset(-2)]] i : i32; };",
|
||||
"test.wgsl:1:21 error: offset decoration must be positive\n"
|
||||
"struct S { [[offset(-2)]] i : i32; };\n"
|
||||
TEST_F(ParserImplErrorTest, GlobalDeclStructMemberAlignMissingRParen) {
|
||||
EXPECT("struct S { [[align(1]] i : i32; };",
|
||||
"test.wgsl:1:21 error: expected ')' for align decoration\n"
|
||||
"struct S { [[align(1]] i : i32; };\n"
|
||||
" ^^\n");
|
||||
}
|
||||
|
||||
TEST_F(ParserImplErrorTest, GlobalDeclStructMemberAlignInvaldValue) {
|
||||
EXPECT("struct S { [[align(x)]] i : i32; };",
|
||||
"test.wgsl:1:20 error: expected signed integer literal for align "
|
||||
"decoration\n"
|
||||
"struct S { [[align(x)]] i : i32; };\n"
|
||||
" ^\n");
|
||||
}
|
||||
|
||||
TEST_F(ParserImplErrorTest, GlobalDeclStructMemberAlignNegativeValue) {
|
||||
EXPECT("struct S { [[align(-2)]] i : i32; };",
|
||||
"test.wgsl:1:20 error: align decoration must be positive\n"
|
||||
"struct S { [[align(-2)]] i : i32; };\n"
|
||||
" ^^\n");
|
||||
}
|
||||
|
||||
TEST_F(ParserImplErrorTest, GlobalDeclStructMemberSizeMissingLParen) {
|
||||
EXPECT("struct S { [[size 1)]] i : i32; };",
|
||||
"test.wgsl:1:19 error: expected '(' for size decoration\n"
|
||||
"struct S { [[size 1)]] i : i32; };\n"
|
||||
" ^\n");
|
||||
}
|
||||
|
||||
TEST_F(ParserImplErrorTest, GlobalDeclStructMemberSizeMissingRParen) {
|
||||
EXPECT("struct S { [[size(1]] i : i32; };",
|
||||
"test.wgsl:1:20 error: expected ')' for size decoration\n"
|
||||
"struct S { [[size(1]] i : i32; };\n"
|
||||
" ^^\n");
|
||||
}
|
||||
|
||||
TEST_F(ParserImplErrorTest, GlobalDeclStructMemberSizeInvaldValue) {
|
||||
EXPECT("struct S { [[size(x)]] i : i32; };",
|
||||
"test.wgsl:1:19 error: expected signed integer literal for size "
|
||||
"decoration\n"
|
||||
"struct S { [[size(x)]] i : i32; };\n"
|
||||
" ^\n");
|
||||
}
|
||||
|
||||
TEST_F(ParserImplErrorTest, GlobalDeclStructMemberSizeNegativeValue) {
|
||||
EXPECT("struct S { [[size(-2)]] i : i32; };",
|
||||
"test.wgsl:1:19 error: size decoration must be positive\n"
|
||||
"struct S { [[size(-2)]] i : i32; };\n"
|
||||
" ^^\n");
|
||||
}
|
||||
|
||||
TEST_F(ParserImplErrorTest, GlobalDeclTypeAliasMissingIdentifier) {
|
||||
EXPECT("type 1 = f32;",
|
||||
"test.wgsl:1:6 error: expected identifier for type alias\n"
|
||||
|
|
|
@ -173,8 +173,7 @@ TEST_F(ParserImplTest, GlobalDecl_ParsesStruct) {
|
|||
}
|
||||
|
||||
TEST_F(ParserImplTest, GlobalDecl_Struct_WithStride) {
|
||||
auto p =
|
||||
parser("struct A { [[offset(0)]] data: [[stride(4)]] array<f32>; };");
|
||||
auto p = parser("struct A { data: [[stride(4)]] array<f32>; };");
|
||||
|
||||
p->expect_global_decl();
|
||||
ASSERT_FALSE(p->has_error()) << p->error();
|
||||
|
@ -194,12 +193,15 @@ TEST_F(ParserImplTest, GlobalDecl_Struct_WithStride) {
|
|||
const auto* ty = str->impl()->members()[0]->type();
|
||||
ASSERT_TRUE(ty->Is<type::Array>());
|
||||
const auto* arr = ty->As<type::Array>();
|
||||
EXPECT_TRUE(arr->has_array_stride());
|
||||
EXPECT_EQ(arr->array_stride(), 4u);
|
||||
|
||||
ASSERT_EQ(arr->decorations().size(), 1u);
|
||||
auto* stride = arr->decorations()[0];
|
||||
ASSERT_TRUE(stride->Is<ast::StrideDecoration>());
|
||||
ASSERT_EQ(stride->As<ast::StrideDecoration>()->stride(), 4u);
|
||||
}
|
||||
|
||||
TEST_F(ParserImplTest, GlobalDecl_Struct_WithDecoration) {
|
||||
auto p = parser("[[block]] struct A { [[offset(0)]] data: f32; };");
|
||||
auto p = parser("[[block]] struct A { data: f32; };");
|
||||
p->expect_global_decl();
|
||||
ASSERT_FALSE(p->has_error()) << p->error();
|
||||
|
||||
|
|
|
@ -44,16 +44,28 @@ TEST_F(ParserImplTest, StructBodyDecl_ParsesEmpty) {
|
|||
ASSERT_EQ(m.value.size(), 0u);
|
||||
}
|
||||
|
||||
TEST_F(ParserImplTest, StructBodyDecl_InvalidMember) {
|
||||
TEST_F(ParserImplTest, StructBodyDecl_InvalidAlign) {
|
||||
auto p = parser(R"(
|
||||
{
|
||||
[[offset(nan)]] a : i32;
|
||||
[[align(nan)]] a : i32;
|
||||
})");
|
||||
auto m = p->expect_struct_body_decl();
|
||||
ASSERT_TRUE(p->has_error());
|
||||
ASSERT_TRUE(m.errored);
|
||||
EXPECT_EQ(p->error(),
|
||||
"3:12: expected signed integer literal for offset decoration");
|
||||
"3:11: expected signed integer literal for align decoration");
|
||||
}
|
||||
|
||||
TEST_F(ParserImplTest, StructBodyDecl_InvalidSize) {
|
||||
auto p = parser(R"(
|
||||
{
|
||||
[[size(nan)]] a : i32;
|
||||
})");
|
||||
auto m = p->expect_struct_body_decl();
|
||||
ASSERT_TRUE(p->has_error());
|
||||
ASSERT_TRUE(m.errored);
|
||||
EXPECT_EQ(p->error(),
|
||||
"3:10: expected signed integer literal for size decoration");
|
||||
}
|
||||
|
||||
TEST_F(ParserImplTest, StructBodyDecl_MissingClosingBracket) {
|
||||
|
|
|
@ -24,7 +24,7 @@ TEST_F(ParserImplTest, StructDecl_Parses) {
|
|||
auto p = parser(R"(
|
||||
struct S {
|
||||
a : i32;
|
||||
[[offset(4)]] b : f32;
|
||||
b : f32;
|
||||
})");
|
||||
auto decos = p->decoration_list();
|
||||
EXPECT_FALSE(decos.errored);
|
||||
|
|
|
@ -39,7 +39,7 @@ TEST_F(ParserImplTest, DecorationDecl_EmptyBlock) {
|
|||
}
|
||||
|
||||
TEST_F(ParserImplTest, DecorationDecl_Single) {
|
||||
auto p = parser("[[offset(4)]]");
|
||||
auto p = parser("[[size(4)]]");
|
||||
auto decos = p->decoration_list();
|
||||
EXPECT_FALSE(p->has_error());
|
||||
EXPECT_FALSE(decos.errored);
|
||||
|
@ -47,26 +47,35 @@ TEST_F(ParserImplTest, DecorationDecl_Single) {
|
|||
ASSERT_EQ(decos.value.size(), 1u);
|
||||
auto* deco = decos.value[0]->As<ast::Decoration>();
|
||||
ASSERT_NE(deco, nullptr);
|
||||
EXPECT_TRUE(deco->Is<ast::StructMemberOffsetDecoration>());
|
||||
EXPECT_TRUE(deco->Is<ast::StructMemberSizeDecoration>());
|
||||
}
|
||||
|
||||
TEST_F(ParserImplTest, DecorationDecl_InvalidDecoration) {
|
||||
auto p = parser("[[offset(nan)]]");
|
||||
auto p = parser("[[size(nan)]]");
|
||||
auto decos = p->decoration_list();
|
||||
EXPECT_TRUE(p->has_error()) << p->error();
|
||||
EXPECT_TRUE(decos.errored);
|
||||
EXPECT_FALSE(decos.matched);
|
||||
EXPECT_EQ(p->error(),
|
||||
"1:10: expected signed integer literal for offset decoration");
|
||||
"1:8: expected signed integer literal for size decoration");
|
||||
}
|
||||
|
||||
TEST_F(ParserImplTest, DecorationDecl_MissingClose) {
|
||||
auto p = parser("[[offset(4)");
|
||||
auto p = parser("[[size(4)");
|
||||
auto decos = p->decoration_list();
|
||||
EXPECT_TRUE(p->has_error()) << p->error();
|
||||
EXPECT_TRUE(decos.errored);
|
||||
EXPECT_FALSE(decos.matched);
|
||||
EXPECT_EQ(p->error(), "1:12: expected ']]' for decoration list");
|
||||
EXPECT_EQ(p->error(), "1:10: expected ']]' for decoration list");
|
||||
}
|
||||
|
||||
TEST_F(ParserImplTest, StructMemberDecorationDecl_SizeMissingClose) {
|
||||
auto p = parser("[[size(4)");
|
||||
auto decos = p->decoration_list();
|
||||
EXPECT_TRUE(p->has_error()) << p->error();
|
||||
EXPECT_TRUE(decos.errored);
|
||||
EXPECT_FALSE(decos.matched);
|
||||
EXPECT_EQ(p->error(), "1:10: expected ']]' for decoration list");
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
|
|
@ -23,7 +23,7 @@ TEST_F(ParserImplTest, StructMember_Parses) {
|
|||
auto p = parser("a : i32;");
|
||||
|
||||
auto& builder = p->builder();
|
||||
auto* i32 = builder.create<type::I32>();
|
||||
auto* i32 = builder.ty.i32();
|
||||
|
||||
auto decos = p->decoration_list();
|
||||
EXPECT_FALSE(decos.errored);
|
||||
|
@ -45,11 +45,11 @@ TEST_F(ParserImplTest, StructMember_Parses) {
|
|||
ASSERT_EQ(m->source().range.end.column, 2u);
|
||||
}
|
||||
|
||||
TEST_F(ParserImplTest, StructMember_ParsesWithDecoration) {
|
||||
TEST_F(ParserImplTest, StructMember_ParsesWithOffsetDecoration_DEPRECATED) {
|
||||
auto p = parser("[[offset(2)]] a : i32;");
|
||||
|
||||
auto& builder = p->builder();
|
||||
auto* i32 = builder.create<type::I32>();
|
||||
auto* i32 = builder.ty.i32();
|
||||
|
||||
auto decos = p->decoration_list();
|
||||
EXPECT_FALSE(decos.errored);
|
||||
|
@ -75,12 +75,99 @@ TEST_F(ParserImplTest, StructMember_ParsesWithDecoration) {
|
|||
ASSERT_EQ(m->source().range.end.column, 16u);
|
||||
}
|
||||
|
||||
TEST_F(ParserImplTest, StructMember_ParsesWithMultipleDecorations) {
|
||||
auto p = parser(R"([[offset(2)]]
|
||||
[[offset(4)]] a : i32;)");
|
||||
TEST_F(ParserImplTest, StructMember_ParsesWithAlignDecoration) {
|
||||
auto p = parser("[[align(2)]] a : i32;");
|
||||
|
||||
auto& builder = p->builder();
|
||||
auto* i32 = builder.create<type::I32>();
|
||||
auto* i32 = builder.ty.i32();
|
||||
|
||||
auto decos = p->decoration_list();
|
||||
EXPECT_FALSE(decos.errored);
|
||||
EXPECT_TRUE(decos.matched);
|
||||
EXPECT_EQ(decos.value.size(), 1u);
|
||||
|
||||
auto m = p->expect_struct_member(decos.value);
|
||||
ASSERT_FALSE(p->has_error());
|
||||
ASSERT_FALSE(m.errored);
|
||||
ASSERT_NE(m.value, nullptr);
|
||||
|
||||
EXPECT_EQ(m->symbol(), builder.Symbols().Get("a"));
|
||||
EXPECT_EQ(m->type(), i32);
|
||||
EXPECT_EQ(m->decorations().size(), 1u);
|
||||
EXPECT_TRUE(m->decorations()[0]->Is<ast::StructMemberAlignDecoration>());
|
||||
EXPECT_EQ(
|
||||
m->decorations()[0]->As<ast::StructMemberAlignDecoration>()->align(), 2u);
|
||||
|
||||
ASSERT_EQ(m->source().range.begin.line, 1u);
|
||||
ASSERT_EQ(m->source().range.begin.column, 14u);
|
||||
ASSERT_EQ(m->source().range.end.line, 1u);
|
||||
ASSERT_EQ(m->source().range.end.column, 15u);
|
||||
}
|
||||
|
||||
TEST_F(ParserImplTest, StructMember_ParsesWithSizeDecoration) {
|
||||
auto p = parser("[[size(2)]] a : i32;");
|
||||
|
||||
auto& builder = p->builder();
|
||||
auto* i32 = builder.ty.i32();
|
||||
|
||||
auto decos = p->decoration_list();
|
||||
EXPECT_FALSE(decos.errored);
|
||||
EXPECT_TRUE(decos.matched);
|
||||
EXPECT_EQ(decos.value.size(), 1u);
|
||||
|
||||
auto m = p->expect_struct_member(decos.value);
|
||||
ASSERT_FALSE(p->has_error());
|
||||
ASSERT_FALSE(m.errored);
|
||||
ASSERT_NE(m.value, nullptr);
|
||||
|
||||
EXPECT_EQ(m->symbol(), builder.Symbols().Get("a"));
|
||||
EXPECT_EQ(m->type(), i32);
|
||||
EXPECT_EQ(m->decorations().size(), 1u);
|
||||
EXPECT_TRUE(m->decorations()[0]->Is<ast::StructMemberSizeDecoration>());
|
||||
EXPECT_EQ(m->decorations()[0]->As<ast::StructMemberSizeDecoration>()->size(),
|
||||
2u);
|
||||
|
||||
ASSERT_EQ(m->source().range.begin.line, 1u);
|
||||
ASSERT_EQ(m->source().range.begin.column, 13u);
|
||||
ASSERT_EQ(m->source().range.end.line, 1u);
|
||||
ASSERT_EQ(m->source().range.end.column, 14u);
|
||||
}
|
||||
|
||||
TEST_F(ParserImplTest, StructMember_ParsesWithDecoration) {
|
||||
auto p = parser("[[size(2)]] a : i32;");
|
||||
|
||||
auto& builder = p->builder();
|
||||
auto* i32 = builder.ty.i32();
|
||||
|
||||
auto decos = p->decoration_list();
|
||||
EXPECT_FALSE(decos.errored);
|
||||
EXPECT_TRUE(decos.matched);
|
||||
EXPECT_EQ(decos.value.size(), 1u);
|
||||
|
||||
auto m = p->expect_struct_member(decos.value);
|
||||
ASSERT_FALSE(p->has_error());
|
||||
ASSERT_FALSE(m.errored);
|
||||
ASSERT_NE(m.value, nullptr);
|
||||
|
||||
EXPECT_EQ(m->symbol(), builder.Symbols().Get("a"));
|
||||
EXPECT_EQ(m->type(), i32);
|
||||
EXPECT_EQ(m->decorations().size(), 1u);
|
||||
EXPECT_TRUE(m->decorations()[0]->Is<ast::StructMemberSizeDecoration>());
|
||||
EXPECT_EQ(m->decorations()[0]->As<ast::StructMemberSizeDecoration>()->size(),
|
||||
2u);
|
||||
|
||||
ASSERT_EQ(m->source().range.begin.line, 1u);
|
||||
ASSERT_EQ(m->source().range.begin.column, 13u);
|
||||
ASSERT_EQ(m->source().range.end.line, 1u);
|
||||
ASSERT_EQ(m->source().range.end.column, 14u);
|
||||
}
|
||||
|
||||
TEST_F(ParserImplTest, StructMember_ParsesWithMultipleDecorations) {
|
||||
auto p = parser(R"([[size(2)]]
|
||||
[[align(4)]] a : i32;)");
|
||||
|
||||
auto& builder = p->builder();
|
||||
auto* i32 = builder.ty.i32();
|
||||
|
||||
auto decos = p->decoration_list();
|
||||
EXPECT_FALSE(decos.errored);
|
||||
|
@ -95,23 +182,21 @@ TEST_F(ParserImplTest, StructMember_ParsesWithMultipleDecorations) {
|
|||
EXPECT_EQ(m->symbol(), builder.Symbols().Get("a"));
|
||||
EXPECT_EQ(m->type(), i32);
|
||||
EXPECT_EQ(m->decorations().size(), 2u);
|
||||
EXPECT_TRUE(m->decorations()[0]->Is<ast::StructMemberOffsetDecoration>());
|
||||
EXPECT_TRUE(m->decorations()[0]->Is<ast::StructMemberSizeDecoration>());
|
||||
EXPECT_EQ(m->decorations()[0]->As<ast::StructMemberSizeDecoration>()->size(),
|
||||
2u);
|
||||
EXPECT_TRUE(m->decorations()[1]->Is<ast::StructMemberAlignDecoration>());
|
||||
EXPECT_EQ(
|
||||
m->decorations()[0]->As<ast::StructMemberOffsetDecoration>()->offset(),
|
||||
2u);
|
||||
EXPECT_TRUE(m->decorations()[1]->Is<ast::StructMemberOffsetDecoration>());
|
||||
EXPECT_EQ(
|
||||
m->decorations()[1]->As<ast::StructMemberOffsetDecoration>()->offset(),
|
||||
4u);
|
||||
m->decorations()[1]->As<ast::StructMemberAlignDecoration>()->align(), 4u);
|
||||
|
||||
ASSERT_EQ(m->source().range.begin.line, 2u);
|
||||
ASSERT_EQ(m->source().range.begin.column, 15u);
|
||||
ASSERT_EQ(m->source().range.begin.column, 14u);
|
||||
ASSERT_EQ(m->source().range.end.line, 2u);
|
||||
ASSERT_EQ(m->source().range.end.column, 16u);
|
||||
ASSERT_EQ(m->source().range.end.column, 15u);
|
||||
}
|
||||
|
||||
TEST_F(ParserImplTest, StructMember_InvalidDecoration) {
|
||||
auto p = parser("[[offset(nan)]] a : i32;");
|
||||
auto p = parser("[[size(nan)]] a : i32;");
|
||||
auto decos = p->decoration_list();
|
||||
EXPECT_TRUE(decos.errored);
|
||||
EXPECT_FALSE(decos.matched);
|
||||
|
@ -122,11 +207,11 @@ TEST_F(ParserImplTest, StructMember_InvalidDecoration) {
|
|||
|
||||
ASSERT_TRUE(p->has_error());
|
||||
EXPECT_EQ(p->error(),
|
||||
"1:10: expected signed integer literal for offset decoration");
|
||||
"1:8: expected signed integer literal for size decoration");
|
||||
}
|
||||
|
||||
TEST_F(ParserImplTest, StructMember_InvalidVariable) {
|
||||
auto p = parser("[[offset(4)]] a : B;");
|
||||
auto p = parser("[[size(4)]] a : B;");
|
||||
auto decos = p->decoration_list();
|
||||
EXPECT_FALSE(decos.errored);
|
||||
EXPECT_TRUE(decos.matched);
|
||||
|
@ -135,7 +220,7 @@ TEST_F(ParserImplTest, StructMember_InvalidVariable) {
|
|||
ASSERT_TRUE(p->has_error());
|
||||
ASSERT_TRUE(m.errored);
|
||||
ASSERT_EQ(m.value, nullptr);
|
||||
EXPECT_EQ(p->error(), "1:19: unknown constructed type 'B'");
|
||||
EXPECT_EQ(p->error(), "1:17: unknown constructed type 'B'");
|
||||
}
|
||||
|
||||
TEST_F(ParserImplTest, StructMember_MissingSemicolon) {
|
||||
|
|
|
@ -344,7 +344,7 @@ TEST_F(ParserImplTest, TypeDecl_Array) {
|
|||
ASSERT_FALSE(a->IsRuntimeArray());
|
||||
ASSERT_EQ(a->size(), 5u);
|
||||
ASSERT_TRUE(a->type()->Is<type::F32>());
|
||||
ASSERT_FALSE(a->has_array_stride());
|
||||
EXPECT_EQ(a->decorations().size(), 0u);
|
||||
}
|
||||
|
||||
TEST_F(ParserImplTest, TypeDecl_Array_Stride) {
|
||||
|
@ -360,8 +360,11 @@ TEST_F(ParserImplTest, TypeDecl_Array_Stride) {
|
|||
ASSERT_FALSE(a->IsRuntimeArray());
|
||||
ASSERT_EQ(a->size(), 5u);
|
||||
ASSERT_TRUE(a->type()->Is<type::F32>());
|
||||
ASSERT_TRUE(a->has_array_stride());
|
||||
EXPECT_EQ(a->array_stride(), 16u);
|
||||
|
||||
ASSERT_EQ(a->decorations().size(), 1u);
|
||||
auto* stride = a->decorations()[0];
|
||||
ASSERT_TRUE(stride->Is<ast::StrideDecoration>());
|
||||
ASSERT_EQ(stride->As<ast::StrideDecoration>()->stride(), 16u);
|
||||
}
|
||||
|
||||
TEST_F(ParserImplTest, TypeDecl_Array_Runtime_Stride) {
|
||||
|
@ -376,8 +379,11 @@ TEST_F(ParserImplTest, TypeDecl_Array_Runtime_Stride) {
|
|||
auto* a = t->As<type::Array>();
|
||||
ASSERT_TRUE(a->IsRuntimeArray());
|
||||
ASSERT_TRUE(a->type()->Is<type::F32>());
|
||||
ASSERT_TRUE(a->has_array_stride());
|
||||
EXPECT_EQ(a->array_stride(), 16u);
|
||||
|
||||
ASSERT_EQ(a->decorations().size(), 1u);
|
||||
auto* stride = a->decorations()[0];
|
||||
ASSERT_TRUE(stride->Is<ast::StrideDecoration>());
|
||||
ASSERT_EQ(stride->As<ast::StrideDecoration>()->stride(), 16u);
|
||||
}
|
||||
|
||||
TEST_F(ParserImplTest, TypeDecl_Array_MultipleDecorations_OneBlock) {
|
||||
|
|
|
@ -0,0 +1,137 @@
|
|||
// Copyright 2021 The Tint Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "src/resolver/resolver.h"
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "src/resolver/resolver_test_helper.h"
|
||||
|
||||
namespace tint {
|
||||
namespace resolver {
|
||||
namespace {
|
||||
|
||||
using ResolverIsStorableTest = ResolverTest;
|
||||
|
||||
TEST_F(ResolverIsStorableTest, Void) {
|
||||
auto* void_ty = ty.void_();
|
||||
|
||||
EXPECT_FALSE(r()->IsStorable(void_ty));
|
||||
}
|
||||
|
||||
TEST_F(ResolverIsStorableTest, Scalar) {
|
||||
auto* bool_ = ty.bool_();
|
||||
auto* i32 = ty.i32();
|
||||
auto* u32 = ty.u32();
|
||||
auto* f32 = ty.f32();
|
||||
|
||||
EXPECT_TRUE(r()->IsStorable(bool_));
|
||||
EXPECT_TRUE(r()->IsStorable(i32));
|
||||
EXPECT_TRUE(r()->IsStorable(u32));
|
||||
EXPECT_TRUE(r()->IsStorable(f32));
|
||||
}
|
||||
|
||||
TEST_F(ResolverIsStorableTest, Vector) {
|
||||
auto* vec2_i32 = ty.vec2<int>();
|
||||
auto* vec3_i32 = ty.vec3<int>();
|
||||
auto* vec4_i32 = ty.vec4<int>();
|
||||
auto* vec2_u32 = ty.vec2<unsigned>();
|
||||
auto* vec3_u32 = ty.vec3<unsigned>();
|
||||
auto* vec4_u32 = ty.vec4<unsigned>();
|
||||
auto* vec2_f32 = ty.vec2<float>();
|
||||
auto* vec3_f32 = ty.vec3<float>();
|
||||
auto* vec4_f32 = ty.vec4<float>();
|
||||
|
||||
EXPECT_TRUE(r()->IsStorable(vec2_i32));
|
||||
EXPECT_TRUE(r()->IsStorable(vec3_i32));
|
||||
EXPECT_TRUE(r()->IsStorable(vec4_i32));
|
||||
EXPECT_TRUE(r()->IsStorable(vec2_u32));
|
||||
EXPECT_TRUE(r()->IsStorable(vec3_u32));
|
||||
EXPECT_TRUE(r()->IsStorable(vec4_u32));
|
||||
EXPECT_TRUE(r()->IsStorable(vec2_f32));
|
||||
EXPECT_TRUE(r()->IsStorable(vec3_f32));
|
||||
EXPECT_TRUE(r()->IsStorable(vec4_f32));
|
||||
}
|
||||
|
||||
TEST_F(ResolverIsStorableTest, Matrix) {
|
||||
auto* mat2x2 = ty.mat2x2<float>();
|
||||
auto* mat2x3 = ty.mat2x3<float>();
|
||||
auto* mat2x4 = ty.mat2x4<float>();
|
||||
auto* mat3x2 = ty.mat3x2<float>();
|
||||
auto* mat3x3 = ty.mat3x3<float>();
|
||||
auto* mat3x4 = ty.mat3x4<float>();
|
||||
auto* mat4x2 = ty.mat4x2<float>();
|
||||
auto* mat4x3 = ty.mat4x3<float>();
|
||||
auto* mat4x4 = ty.mat4x4<float>();
|
||||
|
||||
EXPECT_TRUE(r()->IsStorable(mat2x2));
|
||||
EXPECT_TRUE(r()->IsStorable(mat2x3));
|
||||
EXPECT_TRUE(r()->IsStorable(mat2x4));
|
||||
EXPECT_TRUE(r()->IsStorable(mat3x2));
|
||||
EXPECT_TRUE(r()->IsStorable(mat3x3));
|
||||
EXPECT_TRUE(r()->IsStorable(mat3x4));
|
||||
EXPECT_TRUE(r()->IsStorable(mat4x2));
|
||||
EXPECT_TRUE(r()->IsStorable(mat4x3));
|
||||
EXPECT_TRUE(r()->IsStorable(mat4x4));
|
||||
}
|
||||
|
||||
TEST_F(ResolverIsStorableTest, Pointer) {
|
||||
auto* ptr_ty = ty.pointer<int>(ast::StorageClass::kPrivate);
|
||||
|
||||
EXPECT_FALSE(r()->IsStorable(ptr_ty));
|
||||
}
|
||||
|
||||
TEST_F(ResolverIsStorableTest, AliasVoid) {
|
||||
auto* alias = ty.alias("myalias", ty.void_());
|
||||
|
||||
EXPECT_FALSE(r()->IsStorable(alias));
|
||||
}
|
||||
|
||||
TEST_F(ResolverIsStorableTest, AliasI32) {
|
||||
auto* alias = ty.alias("myalias", ty.i32());
|
||||
|
||||
EXPECT_TRUE(r()->IsStorable(alias));
|
||||
}
|
||||
|
||||
TEST_F(ResolverIsStorableTest, ArraySizedOfStorable) {
|
||||
auto* arr = ty.array(ty.i32(), 5);
|
||||
|
||||
EXPECT_TRUE(r()->IsStorable(arr));
|
||||
}
|
||||
|
||||
TEST_F(ResolverIsStorableTest, ArrayUnsizedOfStorable) {
|
||||
auto* arr = ty.array<int>();
|
||||
|
||||
EXPECT_TRUE(r()->IsStorable(arr));
|
||||
}
|
||||
|
||||
TEST_F(ResolverIsStorableTest, Struct_AllMembersStorable) {
|
||||
ast::StructMemberList members{Member("a", ty.i32()), Member("b", ty.f32())};
|
||||
auto* s = create<ast::Struct>(Source{}, members, ast::DecorationList{});
|
||||
auto* s_ty = ty.struct_("mystruct", s);
|
||||
|
||||
EXPECT_TRUE(r()->IsStorable(s_ty));
|
||||
}
|
||||
|
||||
TEST_F(ResolverIsStorableTest, Struct_SomeMembersNonStorable) {
|
||||
auto* ptr_ty = ty.pointer<int>(ast::StorageClass::kPrivate);
|
||||
ast::StructMemberList members{Member("a", ty.i32()), Member("b", ptr_ty)};
|
||||
auto* s = create<ast::Struct>(Source{}, members, ast::DecorationList{});
|
||||
auto* s_ty = ty.struct_("mystruct", s);
|
||||
|
||||
EXPECT_FALSE(r()->IsStorable(s_ty));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace resolver
|
||||
} // namespace tint
|
|
@ -30,11 +30,14 @@
|
|||
#include "src/ast/switch_statement.h"
|
||||
#include "src/ast/unary_op_expression.h"
|
||||
#include "src/ast/variable_decl_statement.h"
|
||||
#include "src/semantic/array.h"
|
||||
#include "src/semantic/call.h"
|
||||
#include "src/semantic/function.h"
|
||||
#include "src/semantic/member_accessor_expression.h"
|
||||
#include "src/semantic/statement.h"
|
||||
#include "src/semantic/struct.h"
|
||||
#include "src/semantic/variable.h"
|
||||
#include "src/type/access_control_type.h"
|
||||
|
||||
namespace tint {
|
||||
namespace resolver {
|
||||
|
@ -59,6 +62,20 @@ class ScopedAssignment {
|
|||
T old_value_;
|
||||
};
|
||||
|
||||
/// Rounds `value` to the next multiple of `alignment`
|
||||
/// Assumes `alignment` is positive.
|
||||
template <typename T>
|
||||
T RoundUp(T alignment, T value) {
|
||||
return ((value + alignment - 1) / alignment) * alignment;
|
||||
}
|
||||
|
||||
/// Returns true if `value` is a power-of-two.
|
||||
/// Assumes `alignment` is positive.
|
||||
template <typename T>
|
||||
bool IsPowerOfTwo(T value) {
|
||||
return (value & (value - 1)) == 0;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Resolver::Resolver(ProgramBuilder* builder)
|
||||
|
@ -98,7 +115,47 @@ bool Resolver::Resolve() {
|
|||
return result;
|
||||
}
|
||||
|
||||
bool Resolver::IsStorable(type::Type* type) {
|
||||
if (type == nullptr) {
|
||||
return false;
|
||||
}
|
||||
if (type->is_scalar() || type->Is<type::Vector>() ||
|
||||
type->Is<type::Matrix>()) {
|
||||
return true;
|
||||
}
|
||||
if (type::Array* array_type = type->As<type::Array>()) {
|
||||
return IsStorable(array_type->type());
|
||||
}
|
||||
if (type::Struct* struct_type = type->As<type::Struct>()) {
|
||||
for (const auto* member : struct_type->impl()->members()) {
|
||||
if (!IsStorable(member->type())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (type::Alias* alias_type = type->As<type::Alias>()) {
|
||||
return IsStorable(alias_type->type());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Resolver::ResolveInternal() {
|
||||
for (auto* ty : builder_->Types()) {
|
||||
if (auto* str = ty->As<type::Struct>()) {
|
||||
if (!Structure(str)) {
|
||||
return false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (auto* arr = ty->As<type::Array>()) {
|
||||
if (!Array(arr)) {
|
||||
return false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto* var : builder_->AST().GlobalVariables()) {
|
||||
variable_stack_.set_global(var->symbol(), CreateVariableInfo(var));
|
||||
|
||||
|
@ -962,6 +1019,204 @@ void Resolver::CreateSemanticNodes() const {
|
|||
}
|
||||
}
|
||||
|
||||
bool Resolver::DefaultAlignAndSize(type::Type* ty,
|
||||
uint32_t& align,
|
||||
uint32_t& size) {
|
||||
static constexpr uint32_t vector_size[] = {
|
||||
/* padding */ 0,
|
||||
/* padding */ 0,
|
||||
/*vec2*/ 8,
|
||||
/*vec3*/ 12,
|
||||
/*vec4*/ 16,
|
||||
};
|
||||
static constexpr uint32_t vector_align[] = {
|
||||
/* padding */ 0,
|
||||
/* padding */ 0,
|
||||
/*vec2*/ 8,
|
||||
/*vec3*/ 16,
|
||||
/*vec4*/ 16,
|
||||
};
|
||||
|
||||
ty = ty->UnwrapAliasIfNeeded();
|
||||
if (ty->is_scalar()) {
|
||||
// Note: Also captures booleans, but these are not host-sharable.
|
||||
align = 4;
|
||||
size = 4;
|
||||
return true;
|
||||
} else if (auto* vec = ty->As<type::Vector>()) {
|
||||
if (vec->size() < 2 || vec->size() > 4) {
|
||||
TINT_UNREACHABLE(diagnostics_)
|
||||
<< "Invalid vector size: vec" << vec->size();
|
||||
return false;
|
||||
}
|
||||
align = vector_align[vec->size()];
|
||||
size = vector_size[vec->size()];
|
||||
return true;
|
||||
} else if (auto* mat = ty->As<type::Matrix>()) {
|
||||
if (mat->columns() < 2 || mat->columns() > 4 || mat->rows() < 2 ||
|
||||
mat->rows() > 4) {
|
||||
TINT_UNREACHABLE(diagnostics_)
|
||||
<< "Invalid matrix size: mat" << mat->columns() << "x" << mat->rows();
|
||||
return false;
|
||||
}
|
||||
align = vector_align[mat->rows()];
|
||||
size = vector_align[mat->rows()] * mat->columns();
|
||||
return true;
|
||||
} else if (auto* s = ty->As<type::Struct>()) {
|
||||
if (auto* sem = Structure(s)) {
|
||||
align = sem->Align();
|
||||
size = sem->Size();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} else if (auto* arr = ty->As<type::Array>()) {
|
||||
if (auto* sem = Array(arr)) {
|
||||
align = sem->Align();
|
||||
size = sem->Size();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
TINT_UNREACHABLE(diagnostics_) << "Invalid type " << ty->TypeInfo().name;
|
||||
return false;
|
||||
}
|
||||
|
||||
const semantic::Array* Resolver::Array(type::Array* arr) {
|
||||
if (auto* sem = builder_->Sem().Get(arr)) {
|
||||
// Semantic info already constructed for this array type
|
||||
return sem;
|
||||
}
|
||||
|
||||
// First check the element type is legal
|
||||
auto* el_ty = arr->type();
|
||||
if (!IsStorable(el_ty)) {
|
||||
builder_->Diagnostics().add_error(
|
||||
std::string(el_ty->FriendlyName(builder_->Symbols())) +
|
||||
" cannot be used as an element type of an array");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto create_semantic = [&](uint32_t stride) -> semantic::Array* {
|
||||
uint32_t el_align = 0;
|
||||
uint32_t el_size = 0;
|
||||
if (!DefaultAlignAndSize(arr->type(), el_align, el_size)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto align = el_align;
|
||||
// WebGPU requires runtime arrays have at least one element, but the AST
|
||||
// records an element count of 0 for it.
|
||||
auto size = std::max<uint32_t>(arr->size(), 1) * stride;
|
||||
auto* sem = builder_->create<semantic::Array>(arr, align, size, stride);
|
||||
builder_->Sem().Add(arr, sem);
|
||||
return sem;
|
||||
};
|
||||
|
||||
// Look for explicit stride via [[stride(n)]] decoration
|
||||
for (auto* deco : arr->decorations()) {
|
||||
if (auto* stride = deco->As<ast::StrideDecoration>()) {
|
||||
return create_semantic(stride->stride());
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate implicit stride
|
||||
uint32_t el_align = 0;
|
||||
uint32_t el_size = 0;
|
||||
if (!DefaultAlignAndSize(el_ty, el_align, el_size)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return create_semantic(RoundUp(el_align, el_size));
|
||||
}
|
||||
|
||||
const semantic::Struct* Resolver::Structure(type::Struct* str) {
|
||||
if (auto* sem = builder_->Sem().Get(str)) {
|
||||
// Semantic info already constructed for this structure type
|
||||
return sem;
|
||||
}
|
||||
|
||||
semantic::StructMemberList sem_members;
|
||||
sem_members.reserve(str->impl()->members().size());
|
||||
|
||||
// Calculate the effective size and alignment of each field, and the overall
|
||||
// size of the structure.
|
||||
// For size, use the size attribute if provided, otherwise use the default
|
||||
// size for the type.
|
||||
// For alignment, use the alignment attribute if provided, otherwise use the
|
||||
// default alignment for the member type.
|
||||
// Diagnostic errors are raised if a basic rule is violated.
|
||||
// Validation of storage-class rules requires analysing the actual variable
|
||||
// usage of the structure, and so is performed as part of the variable
|
||||
// validation.
|
||||
// TODO(crbug.com/tint/628): Actually implement storage-class validation.
|
||||
uint32_t struct_size = 0;
|
||||
uint32_t struct_align = 1;
|
||||
|
||||
for (auto* member : str->impl()->members()) {
|
||||
// First check the member type is legal
|
||||
if (!IsStorable(member->type())) {
|
||||
builder_->Diagnostics().add_error(
|
||||
std::string(member->type()->FriendlyName(builder_->Symbols())) +
|
||||
" cannot be used as the type of a structure member");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uint32_t offset = struct_size;
|
||||
uint32_t align = 0;
|
||||
uint32_t size = 0;
|
||||
if (!DefaultAlignAndSize(member->type(), align, size)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
for (auto* deco : member->decorations()) {
|
||||
if (auto* o = deco->As<ast::StructMemberOffsetDecoration>()) {
|
||||
// [DEPRECATED]
|
||||
if (o->offset() < struct_size) {
|
||||
diagnostics_.add_error("offsets must be in ascending order",
|
||||
o->source());
|
||||
return nullptr;
|
||||
}
|
||||
offset = o->offset();
|
||||
align = 1;
|
||||
} else if (auto* a = deco->As<ast::StructMemberAlignDecoration>()) {
|
||||
if (a->align() <= 0 || !IsPowerOfTwo(a->align())) {
|
||||
diagnostics_.add_error(
|
||||
"align value must be a positive, power-of-two integer",
|
||||
a->source());
|
||||
return nullptr;
|
||||
}
|
||||
align = a->align();
|
||||
} else if (auto* s = deco->As<ast::StructMemberSizeDecoration>()) {
|
||||
if (s->size() < size) {
|
||||
diagnostics_.add_error(
|
||||
"size must be at least as big as the type's size (" +
|
||||
std::to_string(size) + ")",
|
||||
s->source());
|
||||
return nullptr;
|
||||
}
|
||||
size = s->size();
|
||||
}
|
||||
}
|
||||
|
||||
offset = RoundUp(align, offset);
|
||||
|
||||
auto* sem_member =
|
||||
builder_->create<semantic::StructMember>(member, offset, size);
|
||||
builder_->Sem().Add(member, sem_member);
|
||||
sem_members.emplace_back(sem_member);
|
||||
|
||||
struct_size = offset + size;
|
||||
struct_align = std::max(struct_align, align);
|
||||
}
|
||||
|
||||
struct_size = RoundUp(struct_align, struct_size);
|
||||
|
||||
auto* sem = builder_->create<semantic::Struct>(str, std::move(sem_members),
|
||||
struct_align, struct_size);
|
||||
builder_->Sem().Add(str, sem);
|
||||
return sem;
|
||||
}
|
||||
|
||||
template <typename F>
|
||||
bool Resolver::BlockScope(BlockInfo::Type type, F&& callback) {
|
||||
BlockInfo block_info(type, current_block_);
|
||||
|
|
|
@ -42,8 +42,12 @@ class UnaryOpExpression;
|
|||
class Variable;
|
||||
} // namespace ast
|
||||
namespace semantic {
|
||||
class Array;
|
||||
class Statement;
|
||||
} // namespace semantic
|
||||
namespace type {
|
||||
class Struct;
|
||||
} // namespace type
|
||||
|
||||
namespace resolver {
|
||||
|
||||
|
@ -63,6 +67,10 @@ class Resolver {
|
|||
/// @returns true if the resolver was successful
|
||||
bool Resolve();
|
||||
|
||||
/// @param type the given type
|
||||
/// @returns true if the given type is storable.
|
||||
static bool IsStorable(type::Type* type);
|
||||
|
||||
private:
|
||||
/// Structure holding semantic information about a variable.
|
||||
/// Used to build the semantic::Variable nodes at the end of resolving.
|
||||
|
@ -141,41 +149,6 @@ class Resolver {
|
|||
/// @returns true on success, false on error
|
||||
bool ResolveInternal();
|
||||
|
||||
/// Resolves functions
|
||||
/// @param funcs the functions to check
|
||||
/// @returns true on success, false on error
|
||||
bool Functions(const ast::FunctionList& funcs);
|
||||
/// Resolves a function. Requires all dependency
|
||||
/// (callee) functions to have DetermineFunction() called on them first.
|
||||
/// @param func the function to check
|
||||
/// @returns true on success, false on error
|
||||
bool Function(ast::Function* func);
|
||||
/// Resolves a block statement
|
||||
/// @param stmt the block statement
|
||||
/// @returns true if determination was successful
|
||||
bool BlockStatement(const ast::BlockStatement* stmt);
|
||||
/// Resolves the list of statements
|
||||
/// @param stmts the statements to resolve
|
||||
/// @returns true on success, false on error
|
||||
bool Statements(const ast::StatementList& stmts);
|
||||
/// Resolves a statement
|
||||
/// @param stmt the statement to check
|
||||
/// @returns true on success, false on error
|
||||
bool Statement(ast::Statement* stmt);
|
||||
/// Resolves an expression list
|
||||
/// @param list the expression list to check
|
||||
/// @returns true on success, false on error
|
||||
bool Expressions(const ast::ExpressionList& list);
|
||||
/// Resolves an expression
|
||||
/// @param expr the expression to check
|
||||
/// @returns true on success, false on error
|
||||
bool Expression(ast::Expression* expr);
|
||||
/// Resolves the storage class for variables. This assumes that it is only
|
||||
/// called for things in function scope, not module scope.
|
||||
/// @param stmt the statement to check
|
||||
/// @returns false on error
|
||||
bool VariableStorageClass(ast::Statement* stmt);
|
||||
|
||||
/// Creates the nodes and adds them to the semantic::Info mappings of the
|
||||
/// ProgramBuilder.
|
||||
void CreateSemanticNodes() const;
|
||||
|
@ -195,20 +168,43 @@ class Resolver {
|
|||
|
||||
void set_referenced_from_function_if_needed(VariableInfo* var, bool local);
|
||||
|
||||
bool ArrayAccessor(ast::ArrayAccessorExpression* expr);
|
||||
bool Binary(ast::BinaryExpression* expr);
|
||||
bool Bitcast(ast::BitcastExpression* expr);
|
||||
bool Call(ast::CallExpression* expr);
|
||||
bool CaseStatement(ast::CaseStatement* stmt);
|
||||
bool Constructor(ast::ConstructorExpression* expr);
|
||||
bool Identifier(ast::IdentifierExpression* expr);
|
||||
bool IfStatement(ast::IfStatement* stmt);
|
||||
bool IntrinsicCall(ast::CallExpression* call,
|
||||
semantic::IntrinsicType intrinsic_type);
|
||||
bool MemberAccessor(ast::MemberAccessorExpression* expr);
|
||||
bool UnaryOp(ast::UnaryOpExpression* expr);
|
||||
// AST and Type traversal methods
|
||||
// Each return true on success, false on failure.
|
||||
bool ArrayAccessor(ast::ArrayAccessorExpression*);
|
||||
bool Binary(ast::BinaryExpression*);
|
||||
bool Bitcast(ast::BitcastExpression*);
|
||||
bool BlockStatement(const ast::BlockStatement*);
|
||||
bool Call(ast::CallExpression*);
|
||||
bool CaseStatement(ast::CaseStatement*);
|
||||
bool Constructor(ast::ConstructorExpression*);
|
||||
bool Expression(ast::Expression*);
|
||||
bool Expressions(const ast::ExpressionList&);
|
||||
bool Function(ast::Function*);
|
||||
bool Functions(const ast::FunctionList&);
|
||||
bool Identifier(ast::IdentifierExpression*);
|
||||
bool IfStatement(ast::IfStatement*);
|
||||
bool IntrinsicCall(ast::CallExpression*, semantic::IntrinsicType);
|
||||
bool MemberAccessor(ast::MemberAccessorExpression*);
|
||||
bool Statement(ast::Statement*);
|
||||
bool Statements(const ast::StatementList&);
|
||||
bool UnaryOp(ast::UnaryOpExpression*);
|
||||
bool VariableDeclStatement(const ast::VariableDeclStatement*);
|
||||
bool VariableStorageClass(ast::Statement*);
|
||||
|
||||
bool VariableDeclStatement(const ast::VariableDeclStatement* stmt);
|
||||
/// @returns the semantic information for the array `arr`, building it if it
|
||||
/// hasn't been constructed already. If an error is raised, nullptr is
|
||||
/// returned.
|
||||
const semantic::Array* Array(type::Array*);
|
||||
|
||||
/// @returns the semantic information for the structure `str`, building it if
|
||||
/// it hasn't been constructed already. If an error is raised, nullptr is
|
||||
/// returned.
|
||||
const semantic::Struct* Structure(type::Struct* str);
|
||||
|
||||
/// @param align the output default alignment in bytes for the type `ty`
|
||||
/// @param size the output default size in bytes for the type `ty`
|
||||
/// @returns true on success, false on error
|
||||
bool DefaultAlignAndSize(type::Type* ty, uint32_t& align, uint32_t& size);
|
||||
|
||||
VariableInfo* CreateVariableInfo(ast::Variable*);
|
||||
|
||||
|
|
|
@ -0,0 +1,333 @@
|
|||
// Copyright 2021 The Tint Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "src/resolver/resolver.h"
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "src/resolver/resolver_test_helper.h"
|
||||
#include "src/semantic/struct.h"
|
||||
|
||||
namespace tint {
|
||||
namespace resolver {
|
||||
namespace {
|
||||
|
||||
using ResolverStructLayoutTest = ResolverTest;
|
||||
|
||||
TEST_F(ResolverStructLayoutTest, Scalars) {
|
||||
auto* s = Structure("S", {
|
||||
Member("a", ty.f32()),
|
||||
Member("b", ty.u32()),
|
||||
Member("c", ty.i32()),
|
||||
});
|
||||
|
||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||
|
||||
auto* sem = Sem().Get(s);
|
||||
ASSERT_NE(sem, nullptr);
|
||||
EXPECT_EQ(sem->Size(), 12u);
|
||||
EXPECT_EQ(sem->Align(), 4u);
|
||||
ASSERT_EQ(sem->Members().size(), 3u);
|
||||
EXPECT_EQ(sem->Members()[0]->Offset(), 0u);
|
||||
EXPECT_EQ(sem->Members()[0]->Size(), 4u);
|
||||
EXPECT_EQ(sem->Members()[1]->Offset(), 4u);
|
||||
EXPECT_EQ(sem->Members()[1]->Size(), 4u);
|
||||
EXPECT_EQ(sem->Members()[2]->Offset(), 8u);
|
||||
EXPECT_EQ(sem->Members()[2]->Size(), 4u);
|
||||
}
|
||||
|
||||
TEST_F(ResolverStructLayoutTest, Alias) {
|
||||
auto* s = Structure("S", {
|
||||
Member("a", ty.alias("a", ty.f32())),
|
||||
Member("b", ty.alias("b", ty.f32())),
|
||||
});
|
||||
|
||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||
|
||||
auto* sem = Sem().Get(s);
|
||||
ASSERT_NE(sem, nullptr);
|
||||
EXPECT_EQ(sem->Size(), 8u);
|
||||
EXPECT_EQ(sem->Align(), 4u);
|
||||
ASSERT_EQ(sem->Members().size(), 2u);
|
||||
EXPECT_EQ(sem->Members()[0]->Offset(), 0u);
|
||||
EXPECT_EQ(sem->Members()[0]->Size(), 4u);
|
||||
EXPECT_EQ(sem->Members()[1]->Offset(), 4u);
|
||||
EXPECT_EQ(sem->Members()[1]->Size(), 4u);
|
||||
}
|
||||
|
||||
TEST_F(ResolverStructLayoutTest, ImplicitStrideArrayStaticSize) {
|
||||
auto* s = Structure("S", {
|
||||
Member("a", ty.array<i32, 3>()),
|
||||
Member("b", ty.array<f32, 5>()),
|
||||
Member("c", ty.array<f32, 1>()),
|
||||
});
|
||||
|
||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||
|
||||
auto* sem = Sem().Get(s);
|
||||
ASSERT_NE(sem, nullptr);
|
||||
EXPECT_EQ(sem->Size(), 36u);
|
||||
EXPECT_EQ(sem->Align(), 4u);
|
||||
ASSERT_EQ(sem->Members().size(), 3u);
|
||||
EXPECT_EQ(sem->Members()[0]->Offset(), 0u);
|
||||
EXPECT_EQ(sem->Members()[0]->Size(), 12u);
|
||||
EXPECT_EQ(sem->Members()[1]->Offset(), 12u);
|
||||
EXPECT_EQ(sem->Members()[1]->Size(), 20u);
|
||||
EXPECT_EQ(sem->Members()[2]->Offset(), 32u);
|
||||
EXPECT_EQ(sem->Members()[2]->Size(), 4u);
|
||||
}
|
||||
|
||||
TEST_F(ResolverStructLayoutTest, ExplicitStrideArrayStaticSize) {
|
||||
auto* s = Structure("S", {
|
||||
Member("a", ty.array<i32, 3>(/*stride*/ 8)),
|
||||
Member("b", ty.array<f32, 5>(/*stride*/ 16)),
|
||||
Member("c", ty.array<f32, 1>(/*stride*/ 32)),
|
||||
});
|
||||
|
||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||
|
||||
auto* sem = Sem().Get(s);
|
||||
ASSERT_NE(sem, nullptr);
|
||||
EXPECT_EQ(sem->Size(), 136u);
|
||||
EXPECT_EQ(sem->Align(), 4u);
|
||||
ASSERT_EQ(sem->Members().size(), 3u);
|
||||
EXPECT_EQ(sem->Members()[0]->Offset(), 0u);
|
||||
EXPECT_EQ(sem->Members()[0]->Size(), 24u);
|
||||
EXPECT_EQ(sem->Members()[1]->Offset(), 24u);
|
||||
EXPECT_EQ(sem->Members()[1]->Size(), 80u);
|
||||
EXPECT_EQ(sem->Members()[2]->Offset(), 104u);
|
||||
EXPECT_EQ(sem->Members()[2]->Size(), 32u);
|
||||
}
|
||||
|
||||
TEST_F(ResolverStructLayoutTest, ImplicitStrideArrayRuntimeSized) {
|
||||
auto* s = Structure("S", {
|
||||
Member("c", ty.array<f32>()),
|
||||
});
|
||||
|
||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||
|
||||
auto* sem = Sem().Get(s);
|
||||
ASSERT_NE(sem, nullptr);
|
||||
EXPECT_EQ(sem->Size(), 4u);
|
||||
EXPECT_EQ(sem->Align(), 4u);
|
||||
ASSERT_EQ(sem->Members().size(), 1u);
|
||||
EXPECT_EQ(sem->Members()[0]->Offset(), 0u);
|
||||
EXPECT_EQ(sem->Members()[0]->Size(), 4u);
|
||||
}
|
||||
|
||||
TEST_F(ResolverStructLayoutTest, ExplicitStrideArrayRuntimeSized) {
|
||||
auto* s = Structure("S", {
|
||||
Member("c", ty.array<f32>(/*stride*/ 32)),
|
||||
});
|
||||
|
||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||
|
||||
auto* sem = Sem().Get(s);
|
||||
ASSERT_NE(sem, nullptr);
|
||||
EXPECT_EQ(sem->Size(), 32u);
|
||||
EXPECT_EQ(sem->Align(), 4u);
|
||||
ASSERT_EQ(sem->Members().size(), 1u);
|
||||
EXPECT_EQ(sem->Members()[0]->Offset(), 0u);
|
||||
EXPECT_EQ(sem->Members()[0]->Size(), 32u);
|
||||
}
|
||||
|
||||
TEST_F(ResolverStructLayoutTest, ImplicitStrideArrayOfExplicitStrideArray) {
|
||||
auto* inner = ty.array<i32, 2>(/*stride*/ 16); // size: 32
|
||||
auto* outer = ty.array(inner, 12); // size: 12 * 32
|
||||
auto* s = Structure("S", {
|
||||
Member("c", outer),
|
||||
});
|
||||
|
||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||
|
||||
auto* sem = Sem().Get(s);
|
||||
ASSERT_NE(sem, nullptr);
|
||||
EXPECT_EQ(sem->Size(), 384u);
|
||||
EXPECT_EQ(sem->Align(), 4u);
|
||||
ASSERT_EQ(sem->Members().size(), 1u);
|
||||
EXPECT_EQ(sem->Members()[0]->Offset(), 0u);
|
||||
EXPECT_EQ(sem->Members()[0]->Size(), 384u);
|
||||
}
|
||||
|
||||
TEST_F(ResolverStructLayoutTest, ImplicitStrideArrayOfStructure) {
|
||||
auto* inner = Structure("Inner", {
|
||||
Member("a", ty.vec2<i32>()),
|
||||
Member("b", ty.vec3<i32>()),
|
||||
Member("c", ty.vec4<i32>()),
|
||||
}); // size: 48
|
||||
auto* outer = ty.array(inner, 12); // size: 12 * 48
|
||||
auto* s = Structure("S", {
|
||||
Member("c", outer),
|
||||
});
|
||||
|
||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||
|
||||
auto* sem = Sem().Get(s);
|
||||
ASSERT_NE(sem, nullptr);
|
||||
EXPECT_EQ(sem->Size(), 576u);
|
||||
EXPECT_EQ(sem->Align(), 16u);
|
||||
ASSERT_EQ(sem->Members().size(), 1u);
|
||||
EXPECT_EQ(sem->Members()[0]->Offset(), 0u);
|
||||
EXPECT_EQ(sem->Members()[0]->Size(), 576u);
|
||||
}
|
||||
|
||||
TEST_F(ResolverStructLayoutTest, Vector) {
|
||||
auto* s = Structure("S", {
|
||||
Member("a", ty.vec2<i32>()),
|
||||
Member("b", ty.vec3<i32>()),
|
||||
Member("c", ty.vec4<i32>()),
|
||||
});
|
||||
|
||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||
|
||||
auto* sem = Sem().Get(s);
|
||||
ASSERT_NE(sem, nullptr);
|
||||
EXPECT_EQ(sem->Size(), 48u);
|
||||
EXPECT_EQ(sem->Align(), 16u);
|
||||
ASSERT_EQ(sem->Members().size(), 3u);
|
||||
EXPECT_EQ(sem->Members()[0]->Offset(), 0u); // vec2
|
||||
EXPECT_EQ(sem->Members()[0]->Size(), 8u);
|
||||
EXPECT_EQ(sem->Members()[1]->Offset(), 16u); // vec3
|
||||
EXPECT_EQ(sem->Members()[1]->Size(), 12u);
|
||||
EXPECT_EQ(sem->Members()[2]->Offset(), 32u); // vec4
|
||||
EXPECT_EQ(sem->Members()[2]->Size(), 16u);
|
||||
}
|
||||
|
||||
TEST_F(ResolverStructLayoutTest, Matrix) {
|
||||
auto* s = Structure("S", {
|
||||
Member("a", ty.mat2x2<i32>()),
|
||||
Member("b", ty.mat2x3<i32>()),
|
||||
Member("c", ty.mat2x4<i32>()),
|
||||
Member("d", ty.mat3x2<i32>()),
|
||||
Member("e", ty.mat3x3<i32>()),
|
||||
Member("f", ty.mat3x4<i32>()),
|
||||
Member("g", ty.mat4x2<i32>()),
|
||||
Member("h", ty.mat4x3<i32>()),
|
||||
Member("i", ty.mat4x4<i32>()),
|
||||
});
|
||||
|
||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||
|
||||
auto* sem = Sem().Get(s);
|
||||
ASSERT_NE(sem, nullptr);
|
||||
EXPECT_EQ(sem->Size(), 368u);
|
||||
EXPECT_EQ(sem->Align(), 16u);
|
||||
ASSERT_EQ(sem->Members().size(), 9u);
|
||||
EXPECT_EQ(sem->Members()[0]->Offset(), 0u); // mat2x2
|
||||
EXPECT_EQ(sem->Members()[0]->Size(), 16u);
|
||||
EXPECT_EQ(sem->Members()[1]->Offset(), 16u); // mat2x3
|
||||
EXPECT_EQ(sem->Members()[1]->Size(), 32u);
|
||||
EXPECT_EQ(sem->Members()[2]->Offset(), 48u); // mat2x4
|
||||
EXPECT_EQ(sem->Members()[2]->Size(), 32u);
|
||||
EXPECT_EQ(sem->Members()[3]->Offset(), 80u); // mat3x2
|
||||
EXPECT_EQ(sem->Members()[3]->Size(), 24u);
|
||||
EXPECT_EQ(sem->Members()[4]->Offset(), 112u); // mat3x3
|
||||
EXPECT_EQ(sem->Members()[4]->Size(), 48u);
|
||||
EXPECT_EQ(sem->Members()[5]->Offset(), 160u); // mat3x4
|
||||
EXPECT_EQ(sem->Members()[5]->Size(), 48u);
|
||||
EXPECT_EQ(sem->Members()[6]->Offset(), 208u); // mat4x2
|
||||
EXPECT_EQ(sem->Members()[6]->Size(), 32u);
|
||||
EXPECT_EQ(sem->Members()[7]->Offset(), 240u); // mat4x3
|
||||
EXPECT_EQ(sem->Members()[7]->Size(), 64u);
|
||||
EXPECT_EQ(sem->Members()[8]->Offset(), 304u); // mat4x4
|
||||
EXPECT_EQ(sem->Members()[8]->Size(), 64u);
|
||||
}
|
||||
|
||||
TEST_F(ResolverStructLayoutTest, NestedStruct) {
|
||||
auto* inner = Structure("Inner", {
|
||||
Member("a", ty.mat3x3<i32>()),
|
||||
});
|
||||
auto* s = Structure("S", {
|
||||
Member("a", ty.i32()),
|
||||
Member("b", inner),
|
||||
Member("c", ty.i32()),
|
||||
});
|
||||
|
||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||
|
||||
auto* sem = Sem().Get(s);
|
||||
ASSERT_NE(sem, nullptr);
|
||||
EXPECT_EQ(sem->Size(), 80u);
|
||||
EXPECT_EQ(sem->Align(), 16u);
|
||||
ASSERT_EQ(sem->Members().size(), 3u);
|
||||
EXPECT_EQ(sem->Members()[0]->Offset(), 0u);
|
||||
EXPECT_EQ(sem->Members()[0]->Size(), 4u);
|
||||
EXPECT_EQ(sem->Members()[1]->Offset(), 16u);
|
||||
EXPECT_EQ(sem->Members()[1]->Size(), 48u);
|
||||
EXPECT_EQ(sem->Members()[2]->Offset(), 64u);
|
||||
EXPECT_EQ(sem->Members()[2]->Size(), 4u);
|
||||
}
|
||||
|
||||
TEST_F(ResolverStructLayoutTest, SizeDecorations) {
|
||||
auto* inner = Structure("Inner", {
|
||||
Member("a", ty.f32(), {MemberSize(8)}),
|
||||
Member("b", ty.f32(), {MemberSize(16)}),
|
||||
Member("c", ty.f32(), {MemberSize(8)}),
|
||||
});
|
||||
auto* s = Structure("S", {
|
||||
Member("a", ty.f32(), {MemberSize(4)}),
|
||||
Member("b", ty.u32(), {MemberSize(8)}),
|
||||
Member("c", inner),
|
||||
Member("d", ty.i32(), {MemberSize(32)}),
|
||||
});
|
||||
|
||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||
|
||||
auto* sem = Sem().Get(s);
|
||||
ASSERT_NE(sem, nullptr);
|
||||
EXPECT_EQ(sem->Size(), 76u);
|
||||
EXPECT_EQ(sem->Align(), 4u);
|
||||
ASSERT_EQ(sem->Members().size(), 4u);
|
||||
EXPECT_EQ(sem->Members()[0]->Offset(), 0u);
|
||||
EXPECT_EQ(sem->Members()[0]->Size(), 4u);
|
||||
EXPECT_EQ(sem->Members()[1]->Offset(), 4u);
|
||||
EXPECT_EQ(sem->Members()[1]->Size(), 8u);
|
||||
EXPECT_EQ(sem->Members()[2]->Offset(), 12u);
|
||||
EXPECT_EQ(sem->Members()[2]->Size(), 32u);
|
||||
EXPECT_EQ(sem->Members()[3]->Offset(), 44u);
|
||||
EXPECT_EQ(sem->Members()[3]->Size(), 32u);
|
||||
}
|
||||
|
||||
TEST_F(ResolverStructLayoutTest, AlignDecorations) {
|
||||
auto* inner = Structure("Inner", {
|
||||
Member("a", ty.f32(), {MemberAlign(8)}),
|
||||
Member("b", ty.f32(), {MemberAlign(16)}),
|
||||
Member("c", ty.f32(), {MemberAlign(4)}),
|
||||
});
|
||||
auto* s = Structure("S", {
|
||||
Member("a", ty.f32(), {MemberAlign(4)}),
|
||||
Member("b", ty.u32(), {MemberAlign(8)}),
|
||||
Member("c", inner),
|
||||
Member("d", ty.i32(), {MemberAlign(32)}),
|
||||
});
|
||||
|
||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||
|
||||
auto* sem = Sem().Get(s);
|
||||
ASSERT_NE(sem, nullptr);
|
||||
EXPECT_EQ(sem->Size(), 96u);
|
||||
EXPECT_EQ(sem->Align(), 32u);
|
||||
ASSERT_EQ(sem->Members().size(), 4u);
|
||||
EXPECT_EQ(sem->Members()[0]->Offset(), 0u);
|
||||
EXPECT_EQ(sem->Members()[0]->Size(), 4u);
|
||||
EXPECT_EQ(sem->Members()[1]->Offset(), 8u);
|
||||
EXPECT_EQ(sem->Members()[1]->Size(), 4u);
|
||||
EXPECT_EQ(sem->Members()[2]->Offset(), 16u);
|
||||
EXPECT_EQ(sem->Members()[2]->Size(), 32u);
|
||||
EXPECT_EQ(sem->Members()[3]->Offset(), 64u);
|
||||
EXPECT_EQ(sem->Members()[3]->Size(), 4u);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace resolver
|
||||
} // namespace tint
|
|
@ -556,6 +556,38 @@ TEST_F(ResolverValidationTest, Stmt_BreakNotInLoopOrSwitch) {
|
|||
"12:34 error: break statement must be in a loop or switch case");
|
||||
}
|
||||
|
||||
TEST_F(ResolverValidationTest, NonPOTStructMemberAlignDecoration) {
|
||||
Structure("S", {
|
||||
Member("a", ty.f32(), {MemberAlign(Source{{12, 34}}, 3)}),
|
||||
});
|
||||
|
||||
EXPECT_FALSE(r()->Resolve());
|
||||
EXPECT_EQ(
|
||||
r()->error(),
|
||||
"12:34 error: align value must be a positive, power-of-two integer");
|
||||
}
|
||||
|
||||
TEST_F(ResolverValidationTest, ZeroStructMemberAlignDecoration) {
|
||||
Structure("S", {
|
||||
Member("a", ty.f32(), {MemberAlign(Source{{12, 34}}, 0)}),
|
||||
});
|
||||
|
||||
EXPECT_FALSE(r()->Resolve());
|
||||
EXPECT_EQ(
|
||||
r()->error(),
|
||||
"12:34 error: align value must be a positive, power-of-two integer");
|
||||
}
|
||||
|
||||
TEST_F(ResolverValidationTest, ZeroStructMemberSizeDecoration) {
|
||||
Structure("S", {
|
||||
Member("a", ty.f32(), {MemberSize(Source{{12, 34}}, 0)}),
|
||||
});
|
||||
|
||||
EXPECT_FALSE(r()->Resolve());
|
||||
EXPECT_EQ(r()->error(),
|
||||
"12:34 error: size must be at least as big as the type's size (4)");
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace resolver
|
||||
} // namespace tint
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
// Copyright 2021 The Tint Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef SRC_SEMANTIC_ARRAY_H_
|
||||
#define SRC_SEMANTIC_ARRAY_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "src/semantic/node.h"
|
||||
|
||||
namespace tint {
|
||||
|
||||
// Forward declarations
|
||||
namespace type {
|
||||
class Array;
|
||||
} // namespace type
|
||||
|
||||
namespace semantic {
|
||||
|
||||
/// Array holds the semantic information for Array nodes.
|
||||
class Array : public Castable<Array, Node> {
|
||||
public:
|
||||
/// Constructor
|
||||
/// @param type the Array type
|
||||
/// @param align the byte alignment of the structure
|
||||
/// @param size the byte size of the structure
|
||||
/// @param stride the number of bytes from the start of one element of the
|
||||
/// array to the start of the next element
|
||||
Array(type::Array* type, uint32_t align, uint32_t size, uint32_t stride);
|
||||
|
||||
/// @return the resolved type of the Array
|
||||
type::Array* Type() const { return type_; }
|
||||
|
||||
/// @returns the byte alignment of the array
|
||||
/// @note this may differ from the alignment of a structure member of this
|
||||
/// array type, if the member is annotated with the `[[align(n)]]` decoration.
|
||||
uint32_t Align() const { return align_; }
|
||||
|
||||
/// @returns the byte size of the array
|
||||
/// @note this may differ from the size of a structure member of this array
|
||||
/// type, if the member is annotated with the `[[size(n)]]` decoration.
|
||||
uint32_t Size() const { return size_; }
|
||||
|
||||
/// @returns the number of bytes from the start of one element of the
|
||||
/// array to the start of the next element
|
||||
uint32_t Stride() const { return stride_; }
|
||||
|
||||
private:
|
||||
type::Array* const type_;
|
||||
uint32_t const align_;
|
||||
uint32_t const size_;
|
||||
uint32_t const stride_;
|
||||
};
|
||||
|
||||
} // namespace semantic
|
||||
} // namespace tint
|
||||
|
||||
#endif // SRC_SEMANTIC_ARRAY_H_
|
|
@ -29,9 +29,9 @@ class Call : public Castable<Call, Expression> {
|
|||
/// @param declaration the AST node
|
||||
/// @param target the call target
|
||||
/// @param statement the statement that owns this expression
|
||||
explicit Call(ast::Expression* declaration,
|
||||
const CallTarget* target,
|
||||
Statement* statement);
|
||||
Call(ast::Expression* declaration,
|
||||
const CallTarget* target,
|
||||
Statement* statement);
|
||||
|
||||
/// Destructor
|
||||
~Call() override;
|
||||
|
|
|
@ -23,12 +23,6 @@
|
|||
#include "src/semantic/type_mappings.h"
|
||||
|
||||
namespace tint {
|
||||
|
||||
// Forward declarations
|
||||
namespace ast {
|
||||
class Node;
|
||||
} // namespace ast
|
||||
|
||||
namespace semantic {
|
||||
|
||||
/// Info holds all the resolved semantic information for a Program.
|
||||
|
@ -48,26 +42,29 @@ class Info {
|
|||
/// @return this Program
|
||||
Info& operator=(Info&& rhs);
|
||||
|
||||
/// Get looks up the semantic information for the AST node `ast_node`.
|
||||
/// @param ast_node the AST node
|
||||
/// Get looks up the semantic information for the AST or type node `node`.
|
||||
/// @param node the AST or type node
|
||||
/// @returns a pointer to the semantic node if found, otherwise nullptr
|
||||
template <typename AST, typename SEM = SemanticNodeTypeFor<AST>>
|
||||
const SEM* Get(const AST* ast_node) const {
|
||||
auto it = ast_to_sem_.find(ast_node);
|
||||
if (it == ast_to_sem_.end()) {
|
||||
template <typename AST_OR_TYPE,
|
||||
typename SEM = SemanticNodeTypeFor<AST_OR_TYPE>>
|
||||
const SEM* Get(const AST_OR_TYPE* node) const {
|
||||
auto it = map.find(node);
|
||||
if (it == map.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
return it->second->template As<SEM>();
|
||||
}
|
||||
|
||||
/// Add registers the semantic node `sem_node` for the AST node `ast_node`.
|
||||
/// @param ast_node the AST node
|
||||
/// Add registers the semantic node `sem_node` for the AST or type node
|
||||
/// `node`.
|
||||
/// @param node the AST or type node
|
||||
/// @param sem_node the semantic node
|
||||
template <typename AST>
|
||||
void Add(const AST* ast_node, const SemanticNodeTypeFor<AST>* sem_node) {
|
||||
template <typename AST_OR_TYPE>
|
||||
void Add(const AST_OR_TYPE* node,
|
||||
const SemanticNodeTypeFor<AST_OR_TYPE>* sem_node) {
|
||||
// Check there's no semantic info already existing for the node
|
||||
assert(Get(ast_node) == nullptr);
|
||||
ast_to_sem_.emplace(ast_node, sem_node);
|
||||
assert(Get(node) == nullptr);
|
||||
map.emplace(node, sem_node);
|
||||
}
|
||||
|
||||
/// Wrap returns a new Info created with the contents of `inner`.
|
||||
|
@ -79,12 +76,12 @@ class Info {
|
|||
/// @return the Info that wraps `inner`
|
||||
static Info Wrap(const Info& inner) {
|
||||
Info out;
|
||||
out.ast_to_sem_ = inner.ast_to_sem_;
|
||||
out.map = inner.map;
|
||||
return out;
|
||||
}
|
||||
|
||||
private:
|
||||
std::unordered_map<const ast::Node*, const semantic::Node*> ast_to_sem_;
|
||||
std::unordered_map<const CastableBase*, const semantic::Node*> map;
|
||||
};
|
||||
|
||||
} // namespace semantic
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
// Copyright 2021 The Tint Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "src/semantic/array.h"
|
||||
|
||||
TINT_INSTANTIATE_TYPEINFO(tint::semantic::Array);
|
||||
|
||||
namespace tint {
|
||||
namespace semantic {
|
||||
|
||||
Array::Array(type::Array* type, uint32_t align, uint32_t size, uint32_t stride)
|
||||
: type_(type), align_(align), size_(size), stride_(stride) {}
|
||||
|
||||
} // namespace semantic
|
||||
} // namespace tint
|
|
@ -0,0 +1,39 @@
|
|||
// Copyright 2021 The Tint Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "src/semantic/struct.h"
|
||||
|
||||
TINT_INSTANTIATE_TYPEINFO(tint::semantic::Struct);
|
||||
TINT_INSTANTIATE_TYPEINFO(tint::semantic::StructMember);
|
||||
|
||||
namespace tint {
|
||||
namespace semantic {
|
||||
|
||||
Struct::Struct(type::Struct* type,
|
||||
StructMemberList members,
|
||||
uint32_t align,
|
||||
uint32_t size)
|
||||
: type_(type), members_(std::move(members)), align_(align), size_(size) {}
|
||||
|
||||
Struct::~Struct() = default;
|
||||
|
||||
StructMember::StructMember(ast::StructMember* declaration,
|
||||
uint32_t offset,
|
||||
uint32_t size)
|
||||
: declaration_(declaration), offset_(offset), size_(size) {}
|
||||
|
||||
StructMember::~StructMember() = default;
|
||||
|
||||
} // namespace semantic
|
||||
} // namespace tint
|
|
@ -0,0 +1,112 @@
|
|||
// Copyright 2021 The Tint Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0(the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef SRC_SEMANTIC_STRUCT_H_
|
||||
#define SRC_SEMANTIC_STRUCT_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "src/semantic/node.h"
|
||||
|
||||
namespace tint {
|
||||
|
||||
// Forward declarations
|
||||
namespace ast {
|
||||
class StructMember;
|
||||
} // namespace ast
|
||||
namespace type {
|
||||
class Struct;
|
||||
} // namespace type
|
||||
|
||||
namespace semantic {
|
||||
|
||||
class StructMember;
|
||||
|
||||
/// A vector of StructMember pointers.
|
||||
using StructMemberList = std::vector<StructMember*>;
|
||||
|
||||
/// Struct holds the semantic information for structures.
|
||||
class Struct : public Castable<Struct, Node> {
|
||||
public:
|
||||
/// Constructor
|
||||
/// @param type the structure type
|
||||
/// @param members the structure members
|
||||
/// @param align the byte alignment of the structure
|
||||
/// @param size the byte size of the structure
|
||||
Struct(type::Struct* type,
|
||||
StructMemberList members,
|
||||
uint32_t align,
|
||||
uint32_t size);
|
||||
|
||||
/// Destructor
|
||||
~Struct() override;
|
||||
|
||||
/// @returns the structure type
|
||||
type::Struct* Type() const { return type_; }
|
||||
|
||||
/// @returns the members of the structure
|
||||
const StructMemberList& Members() const { return members_; }
|
||||
|
||||
/// @returns the byte alignment of the structure
|
||||
/// @note this may differ from the alignment of a structure member of this
|
||||
/// structure type, if the member is annotated with the `[[align(n)]]`
|
||||
/// decoration.
|
||||
uint32_t Align() const { return align_; }
|
||||
|
||||
/// @returns the byte size of the structure
|
||||
/// @note this may differ from the size of a structure member of this
|
||||
/// structure type, if the member is annotated with the `[[size(n)]]`
|
||||
/// decoration.
|
||||
uint32_t Size() const { return size_; }
|
||||
|
||||
private:
|
||||
type::Struct* const type_;
|
||||
StructMemberList const members_;
|
||||
uint32_t const align_;
|
||||
uint32_t const size_;
|
||||
};
|
||||
|
||||
/// StructMember holds the semantic information for structure members.
|
||||
class StructMember : public Castable<StructMember, Node> {
|
||||
public:
|
||||
/// Constructor
|
||||
/// @param declaration the AST declaration node
|
||||
/// @param offset the byte offset from the base of the structure
|
||||
/// @param size the byte size
|
||||
StructMember(ast::StructMember* declaration, uint32_t offset, uint32_t size);
|
||||
|
||||
/// Destructor
|
||||
~StructMember() override;
|
||||
|
||||
/// @returns the AST declaration node
|
||||
ast::StructMember* Declaration() const { return declaration_; }
|
||||
|
||||
/// @returns byte offset from base of structure
|
||||
uint32_t Offset() const { return offset_; }
|
||||
|
||||
/// @returns byte size
|
||||
uint32_t Size() const { return size_; }
|
||||
|
||||
private:
|
||||
ast::StructMember* const declaration_;
|
||||
uint32_t const offset_; // Byte offset from base of structure
|
||||
uint32_t const size_; // Byte size
|
||||
};
|
||||
|
||||
} // namespace semantic
|
||||
} // namespace tint
|
||||
|
||||
#endif // SRC_SEMANTIC_STRUCT_H_
|
|
@ -19,44 +19,52 @@ namespace tint {
|
|||
|
||||
// Forward declarations
|
||||
namespace ast {
|
||||
|
||||
class CallExpression;
|
||||
class Expression;
|
||||
class Function;
|
||||
class MemberAccessorExpression;
|
||||
class StructMember;
|
||||
class Variable;
|
||||
|
||||
} // namespace ast
|
||||
namespace type {
|
||||
class Array;
|
||||
class Struct;
|
||||
} // namespace type
|
||||
|
||||
namespace semantic {
|
||||
|
||||
// Forward declarations
|
||||
class Array;
|
||||
class Call;
|
||||
class Expression;
|
||||
class Function;
|
||||
class MemberAccessorExpression;
|
||||
class Struct;
|
||||
class StructMember;
|
||||
class Variable;
|
||||
|
||||
/// TypeMappings is a struct that holds dummy `operator()` methods that's used
|
||||
/// by SemanticNodeTypeFor to map AST node types to their corresponding semantic
|
||||
/// node types.
|
||||
/// The standard operator overload resolving rules will be used to infer the
|
||||
/// return type based on the argument type.
|
||||
/// by SemanticNodeTypeFor to map AST / type node types to their corresponding
|
||||
/// semantic node types. The standard operator overload resolving rules will be
|
||||
/// used to infer the return type based on the argument type.
|
||||
struct TypeMappings {
|
||||
//! @cond Doxygen_Suppress
|
||||
semantic::Expression* operator()(ast::Expression*);
|
||||
semantic::Function* operator()(ast::Function*);
|
||||
semantic::Variable* operator()(ast::Variable*);
|
||||
semantic::Call* operator()(ast::CallExpression*);
|
||||
semantic::MemberAccessorExpression* operator()(
|
||||
ast::MemberAccessorExpression*);
|
||||
Array* operator()(type::Array*);
|
||||
Call* operator()(ast::CallExpression*);
|
||||
Expression* operator()(ast::Expression*);
|
||||
Function* operator()(ast::Function*);
|
||||
MemberAccessorExpression* operator()(ast::MemberAccessorExpression*);
|
||||
Struct* operator()(type::Struct*);
|
||||
StructMember* operator()(ast::StructMember*);
|
||||
Variable* operator()(ast::Variable*);
|
||||
//! @endcond
|
||||
};
|
||||
|
||||
/// SemanticNodeTypeFor resolves to the appropriate semantic::Node type for the
|
||||
/// AST node type `AST`.
|
||||
template <typename AST>
|
||||
/// AST or type node `AST_OR_TYPE`.
|
||||
template <typename AST_OR_TYPE>
|
||||
using SemanticNodeTypeFor = typename std::remove_pointer<decltype(
|
||||
TypeMappings()(std::declval<AST*>()))>::type;
|
||||
TypeMappings()(std::declval<AST_OR_TYPE*>()))>::type;
|
||||
|
||||
} // namespace semantic
|
||||
} // namespace tint
|
||||
|
|
|
@ -154,26 +154,20 @@ Transform::Output FirstIndexOffset::Run(const Program* in) {
|
|||
|
||||
ast::Variable* FirstIndexOffset::State::AddUniformBuffer() {
|
||||
auto* u32_type = dst->create<type::U32>();
|
||||
ast::StructMemberList members;
|
||||
uint32_t offset = 0;
|
||||
ast::StructMemberList members;
|
||||
if (has_vertex_index) {
|
||||
ast::DecorationList member_dec;
|
||||
member_dec.push_back(
|
||||
dst->create<ast::StructMemberOffsetDecoration>(Source{}, offset));
|
||||
members.push_back(dst->create<ast::StructMember>(
|
||||
Source{}, dst->Symbols().Register(kFirstVertexName), u32_type,
|
||||
std::move(member_dec)));
|
||||
ast::DecorationList{}));
|
||||
vertex_index_offset = offset;
|
||||
offset += 4;
|
||||
}
|
||||
|
||||
if (has_instance_index) {
|
||||
ast::DecorationList member_dec;
|
||||
member_dec.push_back(
|
||||
dst->create<ast::StructMemberOffsetDecoration>(Source{}, offset));
|
||||
members.push_back(dst->create<ast::StructMember>(
|
||||
Source{}, dst->Symbols().Register(kFirstInstanceName), u32_type,
|
||||
std::move(member_dec)));
|
||||
ast::DecorationList{}));
|
||||
instance_index_offset = offset;
|
||||
offset += 4;
|
||||
}
|
||||
|
|
|
@ -46,8 +46,8 @@ namespace transform {
|
|||
/// After:
|
||||
/// [[block]]
|
||||
/// struct TintFirstIndexOffsetData {
|
||||
/// [[offset(0)]] tint_first_vertex_index : u32;
|
||||
/// [[offset(4)]] tint_first_instance_index : u32;
|
||||
/// tint_first_vertex_index : u32;
|
||||
/// tint_first_instance_index : u32;
|
||||
/// };
|
||||
/// [[builtin(vertex_index)]] var<in> tint_first_index_offset_vert_idx : u32;
|
||||
/// [[binding(N), group(M)]] var<uniform> tint_first_index_data :
|
||||
|
|
|
@ -98,7 +98,6 @@ fn entry() -> void {
|
|||
|
||||
[[block]]
|
||||
struct TintFirstIndexOffsetData {
|
||||
[[offset(0)]]
|
||||
tint_first_vertex_index : u32;
|
||||
};
|
||||
|
||||
|
@ -147,7 +146,6 @@ fn entry() -> void {
|
|||
|
||||
[[block]]
|
||||
struct TintFirstIndexOffsetData {
|
||||
[[offset(0)]]
|
||||
tint_first_instance_index : u32;
|
||||
};
|
||||
|
||||
|
@ -199,9 +197,7 @@ fn entry() -> void {
|
|||
|
||||
[[block]]
|
||||
struct TintFirstIndexOffsetData {
|
||||
[[offset(0)]]
|
||||
tint_first_vertex_index : u32;
|
||||
[[offset(4)]]
|
||||
tint_first_instance_index : u32;
|
||||
};
|
||||
|
||||
|
@ -255,7 +251,6 @@ fn entry() -> void {
|
|||
|
||||
[[block]]
|
||||
struct TintFirstIndexOffsetData {
|
||||
[[offset(0)]]
|
||||
tint_first_vertex_index : u32;
|
||||
};
|
||||
|
||||
|
|
|
@ -96,7 +96,6 @@ TEST_F(HlslTest, PromoteArrayInitializerToConstVar_Bug406) {
|
|||
auto* src = R"(
|
||||
[[block]]
|
||||
struct Uniforms {
|
||||
[[offset(0)]]
|
||||
transform : mat2x2<f32>;
|
||||
};
|
||||
|
||||
|
@ -121,7 +120,6 @@ fn main() -> void {
|
|||
auto* expect = R"(
|
||||
[[block]]
|
||||
struct Uniforms {
|
||||
[[offset(0)]]
|
||||
transform : mat2x2<f32>;
|
||||
};
|
||||
|
||||
|
|
|
@ -34,6 +34,7 @@ class Msl : public Transform {
|
|||
/// @returns the transformation result
|
||||
Output Run(const Program* program) override;
|
||||
|
||||
private:
|
||||
/// Hoist location-decorated entry point parameters out to struct members.
|
||||
void HandleEntryPointIOTypes(CloneContext& ctx) const;
|
||||
};
|
||||
|
|
|
@ -230,13 +230,9 @@ void VertexPulling::State::AddVertexStorageBuffers() {
|
|||
|
||||
// Creating the struct type
|
||||
ast::StructMemberList members;
|
||||
ast::DecorationList member_dec;
|
||||
member_dec.push_back(
|
||||
ctx.dst->create<ast::StructMemberOffsetDecoration>(Source{}, 0u));
|
||||
|
||||
members.push_back(ctx.dst->create<ast::StructMember>(
|
||||
Source{}, ctx.dst->Symbols().Register(kStructBufferName),
|
||||
internal_array_type, std::move(member_dec)));
|
||||
internal_array_type, ast::DecorationList{}));
|
||||
|
||||
ast::DecorationList decos;
|
||||
decos.push_back(ctx.dst->create<ast::StructBlockDecoration>(Source{}));
|
||||
|
|
|
@ -83,7 +83,6 @@ fn main() -> void {}
|
|||
auto* expect = R"(
|
||||
[[block]]
|
||||
struct TintVertexData {
|
||||
[[offset(0)]]
|
||||
_tint_vertex_data : [[stride(4)]] array<u32>;
|
||||
};
|
||||
|
||||
|
@ -120,7 +119,6 @@ fn main() -> void {}
|
|||
|
||||
[[block]]
|
||||
struct TintVertexData {
|
||||
[[offset(0)]]
|
||||
_tint_vertex_data : [[stride(4)]] array<u32>;
|
||||
};
|
||||
|
||||
|
@ -163,7 +161,6 @@ fn main() -> void {}
|
|||
|
||||
[[block]]
|
||||
struct TintVertexData {
|
||||
[[offset(0)]]
|
||||
_tint_vertex_data : [[stride(4)]] array<u32>;
|
||||
};
|
||||
|
||||
|
@ -206,7 +203,6 @@ fn main() -> void {}
|
|||
|
||||
[[block]]
|
||||
struct TintVertexData {
|
||||
[[offset(0)]]
|
||||
_tint_vertex_data : [[stride(4)]] array<u32>;
|
||||
};
|
||||
|
||||
|
@ -254,7 +250,6 @@ fn main() -> void {}
|
|||
|
||||
[[block]]
|
||||
struct TintVertexData {
|
||||
[[offset(0)]]
|
||||
_tint_vertex_data : [[stride(4)]] array<u32>;
|
||||
};
|
||||
|
||||
|
@ -316,7 +311,6 @@ fn main() -> void {}
|
|||
|
||||
[[block]]
|
||||
struct TintVertexData {
|
||||
[[offset(0)]]
|
||||
_tint_vertex_data : [[stride(4)]] array<u32>;
|
||||
};
|
||||
|
||||
|
@ -371,7 +365,6 @@ fn main() -> void {}
|
|||
|
||||
[[block]]
|
||||
struct TintVertexData {
|
||||
[[offset(0)]]
|
||||
_tint_vertex_data : [[stride(4)]] array<u32>;
|
||||
};
|
||||
|
||||
|
|
|
@ -65,14 +65,6 @@ std::string AccessControl::FriendlyName(const SymbolTable& symbols) const {
|
|||
return out.str();
|
||||
}
|
||||
|
||||
uint64_t AccessControl::MinBufferBindingSize(MemoryLayout mem_layout) const {
|
||||
return subtype_->MinBufferBindingSize(mem_layout);
|
||||
}
|
||||
|
||||
uint64_t AccessControl::BaseAlignment(MemoryLayout mem_layout) const {
|
||||
return subtype_->BaseAlignment(mem_layout);
|
||||
}
|
||||
|
||||
AccessControl* AccessControl::Clone(CloneContext* ctx) const {
|
||||
// Clone arguments outside of create() call to have deterministic ordering
|
||||
auto* ty = ctx->Clone(type());
|
||||
|
|
|
@ -54,16 +54,6 @@ class AccessControl : public Castable<AccessControl, Type> {
|
|||
/// declared in WGSL.
|
||||
std::string FriendlyName(const SymbolTable& symbols) const override;
|
||||
|
||||
/// @param mem_layout type of memory layout to use in calculation.
|
||||
/// @returns minimum size required for this type, in bytes.
|
||||
/// 0 for non-host shareable types.
|
||||
uint64_t MinBufferBindingSize(MemoryLayout mem_layout) const override;
|
||||
|
||||
/// @param mem_layout type of memory layout to use in calculation.
|
||||
/// @returns base alignment for the type, in bytes.
|
||||
/// 0 for non-host shareable types.
|
||||
uint64_t BaseAlignment(MemoryLayout mem_layout) const override;
|
||||
|
||||
/// Clones this type and all transitive types using the `CloneContext` `ctx`.
|
||||
/// @param ctx the clone context
|
||||
/// @return the newly cloned type
|
||||
|
|
|
@ -95,70 +95,6 @@ TEST_F(AccessControlTest, FriendlyNameReadWrite) {
|
|||
EXPECT_EQ(at.FriendlyName(Symbols()), "[[access(read_write)]] i32");
|
||||
}
|
||||
|
||||
TEST_F(AccessControlTest, MinBufferBindingSizeU32) {
|
||||
U32 u32;
|
||||
AccessControl at{ast::AccessControl::kReadOnly, &u32};
|
||||
EXPECT_EQ(4u, at.MinBufferBindingSize(MemoryLayout::kUniformBuffer));
|
||||
}
|
||||
|
||||
TEST_F(AccessControlTest, MinBufferBindingSizeArray) {
|
||||
U32 u32;
|
||||
Array array(&u32, 4, ast::DecorationList{create<ast::StrideDecoration>(4)});
|
||||
AccessControl at{ast::AccessControl::kReadOnly, &array};
|
||||
EXPECT_EQ(16u, at.MinBufferBindingSize(MemoryLayout::kUniformBuffer));
|
||||
}
|
||||
|
||||
TEST_F(AccessControlTest, MinBufferBindingSizeRuntimeArray) {
|
||||
U32 u32;
|
||||
Array array(&u32, 0, ast::DecorationList{create<ast::StrideDecoration>(4)});
|
||||
AccessControl at{ast::AccessControl::kReadOnly, &array};
|
||||
EXPECT_EQ(4u, at.MinBufferBindingSize(MemoryLayout::kUniformBuffer));
|
||||
}
|
||||
|
||||
TEST_F(AccessControlTest, MinBufferBindingSizeStruct) {
|
||||
auto* str = create<ast::Struct>(
|
||||
ast::StructMemberList{Member("foo", ty.u32(), {MemberOffset(0)}),
|
||||
Member("bar", ty.u32(), {MemberOffset(4)})},
|
||||
ast::DecorationList{});
|
||||
|
||||
auto* struct_type = ty.struct_("struct_type", str);
|
||||
AccessControl at{ast::AccessControl::kReadOnly, struct_type};
|
||||
EXPECT_EQ(16u, at.MinBufferBindingSize(MemoryLayout::kUniformBuffer));
|
||||
EXPECT_EQ(8u, at.MinBufferBindingSize(MemoryLayout::kStorageBuffer));
|
||||
}
|
||||
|
||||
TEST_F(AccessControlTest, BaseAlignmentU32) {
|
||||
U32 u32;
|
||||
AccessControl at{ast::AccessControl::kReadOnly, &u32};
|
||||
EXPECT_EQ(4u, at.BaseAlignment(MemoryLayout::kUniformBuffer));
|
||||
}
|
||||
|
||||
TEST_F(AccessControlTest, BaseAlignmentArray) {
|
||||
U32 u32;
|
||||
Array array(&u32, 4, ast::DecorationList{create<ast::StrideDecoration>(4)});
|
||||
AccessControl at{ast::AccessControl::kReadOnly, &array};
|
||||
EXPECT_EQ(16u, at.BaseAlignment(MemoryLayout::kUniformBuffer));
|
||||
}
|
||||
|
||||
TEST_F(AccessControlTest, BaseAlignmentRuntimeArray) {
|
||||
U32 u32;
|
||||
Array array(&u32, 0, ast::DecorationList{create<ast::StrideDecoration>(4)});
|
||||
AccessControl at{ast::AccessControl::kReadOnly, &array};
|
||||
EXPECT_EQ(16u, at.BaseAlignment(MemoryLayout::kUniformBuffer));
|
||||
}
|
||||
|
||||
TEST_F(AccessControlTest, BaseAlignmentStruct) {
|
||||
auto* str = create<ast::Struct>(
|
||||
ast::StructMemberList{Member("foo", ty.u32(), {MemberOffset(0)}),
|
||||
Member("bar", ty.u32(), {MemberOffset(4)})},
|
||||
ast::DecorationList{});
|
||||
auto* struct_type = ty.struct_("struct_type", str);
|
||||
|
||||
AccessControl at{ast::AccessControl::kReadOnly, struct_type};
|
||||
EXPECT_EQ(16u, at.BaseAlignment(MemoryLayout::kUniformBuffer));
|
||||
EXPECT_EQ(4u, at.BaseAlignment(MemoryLayout::kStorageBuffer));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace type
|
||||
} // namespace tint
|
||||
|
|
|
@ -38,14 +38,6 @@ std::string Alias::FriendlyName(const SymbolTable& symbols) const {
|
|||
return symbols.NameFor(symbol_);
|
||||
}
|
||||
|
||||
uint64_t Alias::MinBufferBindingSize(MemoryLayout mem_layout) const {
|
||||
return subtype_->MinBufferBindingSize(mem_layout);
|
||||
}
|
||||
|
||||
uint64_t Alias::BaseAlignment(MemoryLayout mem_layout) const {
|
||||
return subtype_->BaseAlignment(mem_layout);
|
||||
}
|
||||
|
||||
Alias* Alias::Clone(CloneContext* ctx) const {
|
||||
// Clone arguments outside of create() call to have deterministic ordering
|
||||
auto sym = ctx->Clone(symbol());
|
||||
|
|
|
@ -47,16 +47,6 @@ class Alias : public Castable<Alias, Type> {
|
|||
/// declared in WGSL.
|
||||
std::string FriendlyName(const SymbolTable& symbols) const override;
|
||||
|
||||
/// @param mem_layout type of memory layout to use in calculation.
|
||||
/// @returns minimum size required for this type, in bytes.
|
||||
/// 0 for non-host shareable types.
|
||||
uint64_t MinBufferBindingSize(MemoryLayout mem_layout) const override;
|
||||
|
||||
/// @param mem_layout type of memory layout to use in calculation.
|
||||
/// @returns base alignment for the type, in bytes.
|
||||
/// 0 for non-host shareable types.
|
||||
uint64_t BaseAlignment(MemoryLayout mem_layout) const override;
|
||||
|
||||
/// Clones this type and all transitive types using the `CloneContext` `ctx`.
|
||||
/// @param ctx the clone context
|
||||
/// @return the newly cloned type
|
||||
|
|
|
@ -137,76 +137,6 @@ TEST_F(AliasTest, UnwrapAll_PointerAccessControl) {
|
|||
EXPECT_EQ(a.UnwrapAll(), ty.u32());
|
||||
}
|
||||
|
||||
TEST_F(AliasTest, MinBufferBindingSizeU32) {
|
||||
auto* alias = ty.alias("alias", ty.u32());
|
||||
EXPECT_EQ(4u, alias->MinBufferBindingSize(MemoryLayout::kUniformBuffer));
|
||||
}
|
||||
|
||||
TEST_F(AliasTest, MinBufferBindingSizeArray) {
|
||||
Array array(ty.u32(), 4,
|
||||
ast::DecorationList{
|
||||
create<ast::StrideDecoration>(4),
|
||||
});
|
||||
auto* alias = ty.alias("alias", &array);
|
||||
EXPECT_EQ(16u, alias->MinBufferBindingSize(MemoryLayout::kUniformBuffer));
|
||||
}
|
||||
|
||||
TEST_F(AliasTest, MinBufferBindingSizeRuntimeArray) {
|
||||
Array array(ty.u32(), 0,
|
||||
ast::DecorationList{
|
||||
create<ast::StrideDecoration>(4),
|
||||
});
|
||||
auto* alias = ty.alias("alias", &array);
|
||||
EXPECT_EQ(4u, alias->MinBufferBindingSize(MemoryLayout::kUniformBuffer));
|
||||
}
|
||||
|
||||
TEST_F(AliasTest, MinBufferBindingSizeStruct) {
|
||||
auto* str = create<ast::Struct>(
|
||||
ast::StructMemberList{Member("foo", ty.u32(), {MemberOffset(0)}),
|
||||
Member("bar", ty.u32(), {MemberOffset(4)})},
|
||||
ast::DecorationList{});
|
||||
auto* struct_type = ty.struct_("struct_type", str);
|
||||
auto* alias = ty.alias("alias", struct_type);
|
||||
|
||||
EXPECT_EQ(16u, alias->MinBufferBindingSize(MemoryLayout::kUniformBuffer));
|
||||
EXPECT_EQ(8u, alias->MinBufferBindingSize(MemoryLayout::kStorageBuffer));
|
||||
}
|
||||
|
||||
TEST_F(AliasTest, BaseAlignmentU32) {
|
||||
auto* alias = ty.alias("alias", ty.u32());
|
||||
EXPECT_EQ(4u, alias->BaseAlignment(MemoryLayout::kUniformBuffer));
|
||||
}
|
||||
|
||||
TEST_F(AliasTest, BaseAlignmentArray) {
|
||||
Array array(ty.u32(), 4,
|
||||
ast::DecorationList{
|
||||
create<ast::StrideDecoration>(4),
|
||||
});
|
||||
auto* alias = ty.alias("alias", &array);
|
||||
EXPECT_EQ(16u, alias->BaseAlignment(MemoryLayout::kUniformBuffer));
|
||||
}
|
||||
|
||||
TEST_F(AliasTest, BaseAlignmentRuntimeArray) {
|
||||
Array array(ty.u32(), 0,
|
||||
ast::DecorationList{
|
||||
create<ast::StrideDecoration>(4),
|
||||
});
|
||||
auto* alias = ty.alias("alias", &array);
|
||||
EXPECT_EQ(16u, alias->BaseAlignment(MemoryLayout::kUniformBuffer));
|
||||
}
|
||||
|
||||
TEST_F(AliasTest, BaseAlignmentStruct) {
|
||||
auto* str = create<ast::Struct>(
|
||||
ast::StructMemberList{Member("foo", ty.u32(), {MemberOffset(0)}),
|
||||
Member("bar", ty.u32(), {MemberOffset(4)})},
|
||||
ast::DecorationList{});
|
||||
auto* struct_type = ty.struct_("struct_type", str);
|
||||
auto* alias = ty.alias("alias", struct_type);
|
||||
|
||||
EXPECT_EQ(16u, alias->BaseAlignment(MemoryLayout::kUniformBuffer));
|
||||
EXPECT_EQ(4u, alias->BaseAlignment(MemoryLayout::kStorageBuffer));
|
||||
}
|
||||
|
||||
TEST_F(AliasTest, UnwrapAliasIfNeeded) {
|
||||
auto* alias1 = ty.alias("alias1", ty.f32());
|
||||
auto* alias2 = ty.alias("alias2", alias1);
|
||||
|
|
|
@ -30,71 +30,28 @@ Array::Array(Array&&) = default;
|
|||
|
||||
Array::~Array() = default;
|
||||
|
||||
uint64_t Array::MinBufferBindingSize(MemoryLayout mem_layout) const {
|
||||
if (!has_array_stride()) {
|
||||
// Arrays in buffers are required to have a stride.
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (IsRuntimeArray()) {
|
||||
// WebGPU spec 10.1.2:
|
||||
// If the last field of the corresponding structure defined in the shader
|
||||
// has an unbounded array type, then the value of minBufferBindingSize must
|
||||
// be greater than or equal to the byte offset of that field plus the stride
|
||||
// of the unbounded array
|
||||
return array_stride();
|
||||
} else {
|
||||
// Not including the padding for the last element
|
||||
return (size_ - 1) * array_stride() +
|
||||
subtype_->MinBufferBindingSize(mem_layout);
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t Array::BaseAlignment(MemoryLayout mem_layout) const {
|
||||
if (mem_layout == MemoryLayout::kUniformBuffer) {
|
||||
float aligment = 16; // for a vec4
|
||||
float unaligned = static_cast<float>(subtype_->BaseAlignment(mem_layout));
|
||||
return static_cast<uint64_t>(aligment * std::ceil(unaligned / aligment));
|
||||
} else if (mem_layout == MemoryLayout::kStorageBuffer) {
|
||||
return subtype_->BaseAlignment(mem_layout);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t Array::array_stride() const {
|
||||
for (auto* deco : decos_) {
|
||||
if (auto* stride = deco->As<ast::StrideDecoration>()) {
|
||||
return stride->stride();
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool Array::has_array_stride() const {
|
||||
for (auto* deco : decos_) {
|
||||
if (deco->Is<ast::StrideDecoration>()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string Array::type_name() const {
|
||||
assert(subtype_);
|
||||
|
||||
std::string type_name = "__array" + subtype_->type_name();
|
||||
if (!IsRuntimeArray())
|
||||
if (!IsRuntimeArray()) {
|
||||
type_name += "_" + std::to_string(size_);
|
||||
if (has_array_stride())
|
||||
type_name += "_stride_" + std::to_string(array_stride());
|
||||
}
|
||||
for (auto* deco : decos_) {
|
||||
if (auto* stride = deco->As<ast::StrideDecoration>()) {
|
||||
type_name += "_stride_" + std::to_string(stride->stride());
|
||||
}
|
||||
}
|
||||
|
||||
return type_name;
|
||||
}
|
||||
|
||||
std::string Array::FriendlyName(const SymbolTable& symbols) const {
|
||||
std::ostringstream out;
|
||||
if (has_array_stride()) {
|
||||
out << "[[stride(" << array_stride() << ")]] ";
|
||||
for (auto* deco : decos_) {
|
||||
if (auto* stride = deco->As<ast::StrideDecoration>()) {
|
||||
out << "[[stride(" << stride->stride() << ")]] ";
|
||||
}
|
||||
}
|
||||
out << "array<" << subtype_->FriendlyName(symbols);
|
||||
if (!IsRuntimeArray()) {
|
||||
|
|
|
@ -40,24 +40,9 @@ class Array : public Castable<Array, Type> {
|
|||
/// i.e. the size is determined at runtime
|
||||
bool IsRuntimeArray() const { return size_ == 0; }
|
||||
|
||||
/// @param mem_layout type of memory layout to use in calculation.
|
||||
/// @returns minimum size required for this type, in bytes.
|
||||
/// 0 for non-host shareable types.
|
||||
uint64_t MinBufferBindingSize(MemoryLayout mem_layout) const override;
|
||||
|
||||
/// @param mem_layout type of memory layout to use in calculation.
|
||||
/// @returns base alignment for the type, in bytes.
|
||||
/// 0 for non-host shareable types.
|
||||
uint64_t BaseAlignment(MemoryLayout mem_layout) const override;
|
||||
|
||||
/// @returns the array decorations
|
||||
const ast::DecorationList& decorations() const { return decos_; }
|
||||
|
||||
/// @returns the array stride or 0 if none set.
|
||||
uint32_t array_stride() const;
|
||||
/// @returns true if the array has a stride set
|
||||
bool has_array_stride() const;
|
||||
|
||||
/// @returns the array type
|
||||
Type* type() const { return subtype_; }
|
||||
/// @returns the array size. Size is 0 for a runtime array
|
||||
|
|
|
@ -94,38 +94,6 @@ TEST_F(ArrayTest, TypeName_WithStride) {
|
|||
EXPECT_EQ(arr.type_name(), "__array__i32_3_stride_16");
|
||||
}
|
||||
|
||||
TEST_F(ArrayTest, MinBufferBindingSizeNoStride) {
|
||||
U32 u32;
|
||||
Array arr(&u32, 4, ast::DecorationList{});
|
||||
EXPECT_EQ(0u, arr.MinBufferBindingSize(MemoryLayout::kUniformBuffer));
|
||||
}
|
||||
|
||||
TEST_F(ArrayTest, MinBufferBindingSizeArray) {
|
||||
U32 u32;
|
||||
Array arr(&u32, 4, ast::DecorationList{create<ast::StrideDecoration>(4)});
|
||||
EXPECT_EQ(16u, arr.MinBufferBindingSize(MemoryLayout::kUniformBuffer));
|
||||
}
|
||||
|
||||
TEST_F(ArrayTest, MinBufferBindingSizeRuntimeArray) {
|
||||
U32 u32;
|
||||
Array arr(&u32, 0, ast::DecorationList{create<ast::StrideDecoration>(4)});
|
||||
EXPECT_EQ(4u, arr.MinBufferBindingSize(MemoryLayout::kUniformBuffer));
|
||||
}
|
||||
|
||||
TEST_F(ArrayTest, BaseAlignmentArray) {
|
||||
U32 u32;
|
||||
Array arr(&u32, 4, ast::DecorationList{create<ast::StrideDecoration>(4)});
|
||||
EXPECT_EQ(16u, arr.BaseAlignment(MemoryLayout::kUniformBuffer));
|
||||
EXPECT_EQ(4u, arr.BaseAlignment(MemoryLayout::kStorageBuffer));
|
||||
}
|
||||
|
||||
TEST_F(ArrayTest, BaseAlignmentRuntimeArray) {
|
||||
U32 u32;
|
||||
Array arr(&u32, 0, ast::DecorationList{create<ast::StrideDecoration>(4)});
|
||||
EXPECT_EQ(16u, arr.BaseAlignment(MemoryLayout::kUniformBuffer));
|
||||
EXPECT_EQ(4u, arr.BaseAlignment(MemoryLayout::kStorageBuffer));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace type
|
||||
} // namespace tint
|
||||
|
|
|
@ -50,11 +50,6 @@ TEST_F(BoolTest, FriendlyName) {
|
|||
EXPECT_EQ(b.FriendlyName(Symbols()), "bool");
|
||||
}
|
||||
|
||||
TEST_F(BoolTest, MinBufferBindingSize) {
|
||||
Bool b;
|
||||
EXPECT_EQ(0u, b.MinBufferBindingSize(MemoryLayout::kUniformBuffer));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace type
|
||||
} // namespace tint
|
||||
|
|
|
@ -67,11 +67,6 @@ TEST_F(DepthTextureTest, FriendlyName) {
|
|||
EXPECT_EQ(d.FriendlyName(Symbols()), "texture_depth_cube");
|
||||
}
|
||||
|
||||
TEST_F(DepthTextureTest, MinBufferBindingSize) {
|
||||
DepthTexture d(TextureDimension::kCube);
|
||||
EXPECT_EQ(0u, d.MinBufferBindingSize(MemoryLayout::kUniformBuffer));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace type
|
||||
} // namespace tint
|
||||
|
|
|
@ -35,14 +35,6 @@ std::string F32::FriendlyName(const SymbolTable&) const {
|
|||
return "f32";
|
||||
}
|
||||
|
||||
uint64_t F32::MinBufferBindingSize(MemoryLayout) const {
|
||||
return 4;
|
||||
}
|
||||
|
||||
uint64_t F32::BaseAlignment(MemoryLayout) const {
|
||||
return 4;
|
||||
}
|
||||
|
||||
F32* F32::Clone(CloneContext* ctx) const {
|
||||
return ctx->dst->create<F32>();
|
||||
}
|
||||
|
|
|
@ -39,16 +39,6 @@ class F32 : public Castable<F32, Type> {
|
|||
/// declared in WGSL.
|
||||
std::string FriendlyName(const SymbolTable& symbols) const override;
|
||||
|
||||
/// @param mem_layout type of memory layout to use in calculation.
|
||||
/// @returns minimum size required for this type, in bytes.
|
||||
/// 0 for non-host shareable types.
|
||||
uint64_t MinBufferBindingSize(MemoryLayout mem_layout) const override;
|
||||
|
||||
/// @param mem_layout type of memory layout to use in calculation.
|
||||
/// @returns base alignment for the type, in bytes.
|
||||
/// 0 for non-host shareable types.
|
||||
uint64_t BaseAlignment(MemoryLayout mem_layout) const override;
|
||||
|
||||
/// Clones this type and all transitive types using the `CloneContext` `ctx`.
|
||||
/// @param ctx the clone context
|
||||
/// @return the newly cloned type
|
||||
|
|
|
@ -50,16 +50,6 @@ TEST_F(F32Test, FriendlyName) {
|
|||
EXPECT_EQ(f.FriendlyName(Symbols()), "f32");
|
||||
}
|
||||
|
||||
TEST_F(F32Test, MinBufferBindingSize) {
|
||||
F32 f;
|
||||
EXPECT_EQ(4u, f.MinBufferBindingSize(MemoryLayout::kUniformBuffer));
|
||||
}
|
||||
|
||||
TEST_F(F32Test, BaseAlignment) {
|
||||
F32 f;
|
||||
EXPECT_EQ(4u, f.BaseAlignment(MemoryLayout::kUniformBuffer));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace type
|
||||
} // namespace tint
|
||||
|
|
|
@ -35,14 +35,6 @@ std::string I32::FriendlyName(const SymbolTable&) const {
|
|||
return "i32";
|
||||
}
|
||||
|
||||
uint64_t I32::MinBufferBindingSize(MemoryLayout) const {
|
||||
return 4;
|
||||
}
|
||||
|
||||
uint64_t I32::BaseAlignment(MemoryLayout) const {
|
||||
return 4;
|
||||
}
|
||||
|
||||
I32* I32::Clone(CloneContext* ctx) const {
|
||||
return ctx->dst->create<I32>();
|
||||
}
|
||||
|
|
|
@ -39,16 +39,6 @@ class I32 : public Castable<I32, Type> {
|
|||
/// declared in WGSL.
|
||||
std::string FriendlyName(const SymbolTable& symbols) const override;
|
||||
|
||||
/// @param mem_layout type of memory layout to use in calculation.
|
||||
/// @returns minimum size required for this type, in bytes.
|
||||
/// 0 for non-host shareable types.
|
||||
uint64_t MinBufferBindingSize(MemoryLayout mem_layout) const override;
|
||||
|
||||
/// @param mem_layout type of memory layout to use in calculation.
|
||||
/// @returns base alignment for the type, in bytes.
|
||||
/// 0 for non-host shareable types.
|
||||
uint64_t BaseAlignment(MemoryLayout mem_layout) const override;
|
||||
|
||||
/// Clones this type and all transitive types using the `CloneContext` `ctx`.
|
||||
/// @param ctx the clone context
|
||||
/// @return the newly cloned type
|
||||
|
|
|
@ -50,16 +50,6 @@ TEST_F(I32Test, FriendlyName) {
|
|||
EXPECT_EQ(i.FriendlyName(Symbols()), "i32");
|
||||
}
|
||||
|
||||
TEST_F(I32Test, MinBufferBindingSize) {
|
||||
I32 i;
|
||||
EXPECT_EQ(4u, i.MinBufferBindingSize(MemoryLayout::kUniformBuffer));
|
||||
}
|
||||
|
||||
TEST_F(I32Test, BaseAlignment) {
|
||||
I32 i;
|
||||
EXPECT_EQ(4u, i.BaseAlignment(MemoryLayout::kUniformBuffer));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace type
|
||||
} // namespace tint
|
||||
|
|
|
@ -45,18 +45,6 @@ std::string Matrix::FriendlyName(const SymbolTable& symbols) const {
|
|||
return out.str();
|
||||
}
|
||||
|
||||
uint64_t Matrix::MinBufferBindingSize(MemoryLayout mem_layout) const {
|
||||
Vector vec(subtype_, rows_);
|
||||
return (columns_ - 1) * vec.BaseAlignment(mem_layout) +
|
||||
vec.MinBufferBindingSize(mem_layout);
|
||||
}
|
||||
|
||||
uint64_t Matrix::BaseAlignment(MemoryLayout mem_layout) const {
|
||||
Vector vec(subtype_, rows_);
|
||||
Array arr(&vec, columns_, ast::DecorationList{});
|
||||
return arr.BaseAlignment(mem_layout);
|
||||
}
|
||||
|
||||
Matrix* Matrix::Clone(CloneContext* ctx) const {
|
||||
// Clone arguments outside of create() call to have deterministic ordering
|
||||
auto* ty = ctx->Clone(type());
|
||||
|
|
|
@ -49,16 +49,6 @@ class Matrix : public Castable<Matrix, Type> {
|
|||
/// declared in WGSL.
|
||||
std::string FriendlyName(const SymbolTable& symbols) const override;
|
||||
|
||||
/// @param mem_layout type of memory layout to use in calculation.
|
||||
/// @returns minimum size required for this type, in bytes.
|
||||
/// 0 for non-host shareable types.
|
||||
uint64_t MinBufferBindingSize(MemoryLayout mem_layout) const override;
|
||||
|
||||
/// @param mem_layout type of memory layout to use in calculation.
|
||||
/// @returns base alignment for the type, in bytes.
|
||||
/// 0 for non-host shareable types.
|
||||
uint64_t BaseAlignment(MemoryLayout mem_layout) const override;
|
||||
|
||||
/// Clones this type and all transitive types using the `CloneContext` `ctx`.
|
||||
/// @param ctx the clone context
|
||||
/// @return the newly cloned type
|
||||
|
|
|
@ -60,62 +60,6 @@ TEST_F(MatrixTest, FriendlyName) {
|
|||
EXPECT_EQ(m.FriendlyName(Symbols()), "mat2x3<i32>");
|
||||
}
|
||||
|
||||
TEST_F(MatrixTest, MinBufferBindingSize4x2) {
|
||||
I32 i32;
|
||||
Matrix m{&i32, 4, 2};
|
||||
EXPECT_EQ(32u, m.MinBufferBindingSize(MemoryLayout::kUniformBuffer));
|
||||
EXPECT_EQ(32u, m.MinBufferBindingSize(MemoryLayout::kStorageBuffer));
|
||||
}
|
||||
|
||||
TEST_F(MatrixTest, MinBufferBindingSize3x2) {
|
||||
I32 i32;
|
||||
Matrix m{&i32, 3, 2};
|
||||
EXPECT_EQ(28u, m.MinBufferBindingSize(MemoryLayout::kUniformBuffer));
|
||||
EXPECT_EQ(28u, m.MinBufferBindingSize(MemoryLayout::kStorageBuffer));
|
||||
}
|
||||
|
||||
TEST_F(MatrixTest, MinBufferBindingSize2x3) {
|
||||
I32 i32;
|
||||
Matrix m{&i32, 2, 3};
|
||||
EXPECT_EQ(24u, m.MinBufferBindingSize(MemoryLayout::kUniformBuffer));
|
||||
EXPECT_EQ(24u, m.MinBufferBindingSize(MemoryLayout::kStorageBuffer));
|
||||
}
|
||||
|
||||
TEST_F(MatrixTest, MinBufferBindingSize2x2) {
|
||||
I32 i32;
|
||||
Matrix m{&i32, 2, 2};
|
||||
EXPECT_EQ(16u, m.MinBufferBindingSize(MemoryLayout::kUniformBuffer));
|
||||
EXPECT_EQ(16u, m.MinBufferBindingSize(MemoryLayout::kStorageBuffer));
|
||||
}
|
||||
|
||||
TEST_F(MatrixTest, BaseAlignment4x2) {
|
||||
I32 i32;
|
||||
Matrix m{&i32, 4, 2};
|
||||
EXPECT_EQ(16u, m.BaseAlignment(MemoryLayout::kUniformBuffer));
|
||||
EXPECT_EQ(16u, m.BaseAlignment(MemoryLayout::kStorageBuffer));
|
||||
}
|
||||
|
||||
TEST_F(MatrixTest, BaseAlignment3x2) {
|
||||
I32 i32;
|
||||
Matrix m{&i32, 3, 2};
|
||||
EXPECT_EQ(16u, m.BaseAlignment(MemoryLayout::kUniformBuffer));
|
||||
EXPECT_EQ(16u, m.BaseAlignment(MemoryLayout::kStorageBuffer));
|
||||
}
|
||||
|
||||
TEST_F(MatrixTest, BaseAlignment2x3) {
|
||||
I32 i32;
|
||||
Matrix m{&i32, 2, 3};
|
||||
EXPECT_EQ(16u, m.BaseAlignment(MemoryLayout::kUniformBuffer));
|
||||
EXPECT_EQ(8u, m.BaseAlignment(MemoryLayout::kStorageBuffer));
|
||||
}
|
||||
|
||||
TEST_F(MatrixTest, BaseAlignment2x2) {
|
||||
I32 i32;
|
||||
Matrix m{&i32, 2, 2};
|
||||
EXPECT_EQ(16u, m.BaseAlignment(MemoryLayout::kUniformBuffer));
|
||||
EXPECT_EQ(8u, m.BaseAlignment(MemoryLayout::kStorageBuffer));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace type
|
||||
} // namespace tint
|
||||
|
|
|
@ -78,12 +78,6 @@ TEST_F(MultisampledTextureTest, FriendlyName) {
|
|||
EXPECT_EQ(s.FriendlyName(Symbols()), "texture_multisampled_3d<f32>");
|
||||
}
|
||||
|
||||
TEST_F(MultisampledTextureTest, MinBufferBindingSize) {
|
||||
F32 f32;
|
||||
MultisampledTexture s(TextureDimension::k3d, &f32);
|
||||
EXPECT_EQ(0u, s.MinBufferBindingSize(MemoryLayout::kUniformBuffer));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace type
|
||||
} // namespace tint
|
||||
|
|
|
@ -76,12 +76,6 @@ TEST_F(SampledTextureTest, FriendlyName) {
|
|||
EXPECT_EQ(s.FriendlyName(Symbols()), "texture_3d<f32>");
|
||||
}
|
||||
|
||||
TEST_F(SampledTextureTest, MinBufferBindingSize) {
|
||||
F32 f32;
|
||||
SampledTexture s(TextureDimension::kCube, &f32);
|
||||
EXPECT_EQ(0u, s.MinBufferBindingSize(MemoryLayout::kUniformBuffer));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace type
|
||||
} // namespace tint
|
||||
|
|
|
@ -71,11 +71,6 @@ TEST_F(SamplerTest, FriendlyNameComparisonSampler) {
|
|||
EXPECT_EQ(s.FriendlyName(Symbols()), "sampler_comparison");
|
||||
}
|
||||
|
||||
TEST_F(SamplerTest, MinBufferBindingSize) {
|
||||
Sampler s{SamplerKind::kSampler};
|
||||
EXPECT_EQ(0u, s.MinBufferBindingSize(MemoryLayout::kUniformBuffer));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace type
|
||||
} // namespace tint
|
||||
|
|
|
@ -132,13 +132,6 @@ TEST_F(StorageTextureTest, I32) {
|
|||
EXPECT_TRUE(s->As<StorageTexture>()->type()->Is<I32>());
|
||||
}
|
||||
|
||||
TEST_F(StorageTextureTest, MinBufferBindingSize) {
|
||||
auto* subtype = StorageTexture::SubtypeFor(ImageFormat::kRgba32Sint, Types());
|
||||
auto* s = create<StorageTexture>(TextureDimension::k2dArray,
|
||||
ImageFormat::kRgba32Sint, subtype);
|
||||
EXPECT_EQ(0u, s->MinBufferBindingSize(MemoryLayout::kUniformBuffer));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace type
|
||||
} // namespace tint
|
||||
|
|
|
@ -38,48 +38,6 @@ std::string Struct::FriendlyName(const SymbolTable& symbols) const {
|
|||
return symbols.NameFor(symbol_);
|
||||
}
|
||||
|
||||
uint64_t Struct::MinBufferBindingSize(MemoryLayout mem_layout) const {
|
||||
if (!struct_->members().size()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
uint64_t size = last_member->type()->MinBufferBindingSize(mem_layout);
|
||||
if (!size) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
float unaligned = static_cast<float>(last_member->offset() + size);
|
||||
float alignment = static_cast<float>(BaseAlignment(mem_layout));
|
||||
|
||||
return static_cast<uint64_t>(alignment * std::ceil(unaligned / alignment));
|
||||
}
|
||||
|
||||
uint64_t Struct::BaseAlignment(MemoryLayout mem_layout) const {
|
||||
uint64_t max = 0;
|
||||
for (auto* member : struct_->members()) {
|
||||
if (member->type()->BaseAlignment(mem_layout) > max) {
|
||||
max = member->type()->BaseAlignment(mem_layout);
|
||||
}
|
||||
}
|
||||
|
||||
if (mem_layout == MemoryLayout::kUniformBuffer) {
|
||||
// Round up to a vec4.
|
||||
return static_cast<uint64_t>(16 *
|
||||
std::ceil(static_cast<float>(max) / 16.0f));
|
||||
} else if (mem_layout == MemoryLayout::kStorageBuffer) {
|
||||
return max;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
Struct* Struct::Clone(CloneContext* ctx) const {
|
||||
// Clone arguments outside of create() call to have deterministic ordering
|
||||
auto sym = ctx->Clone(symbol());
|
||||
|
|
|
@ -51,16 +51,6 @@ class Struct : public Castable<Struct, Type> {
|
|||
/// declared in WGSL.
|
||||
std::string FriendlyName(const SymbolTable& symbols) const override;
|
||||
|
||||
/// @param mem_layout type of memory layout to use in calculation.
|
||||
/// @returns minimum size required for this type, in bytes.
|
||||
/// 0 for non-host shareable types.
|
||||
uint64_t MinBufferBindingSize(MemoryLayout mem_layout) const override;
|
||||
|
||||
/// @param mem_layout type of memory layout to use in calculation.
|
||||
/// @returns base alignment for the type, in bytes.
|
||||
/// 0 for non-host shareable types.
|
||||
uint64_t BaseAlignment(MemoryLayout mem_layout) const override;
|
||||
|
||||
/// Clones this type and all transitive types using the `CloneContext` `ctx`.
|
||||
/// @param ctx the clone context
|
||||
/// @return the newly cloned type
|
||||
|
|
|
@ -64,140 +64,6 @@ TEST_F(StructTypeTest, FriendlyName) {
|
|||
EXPECT_EQ(s->FriendlyName(Symbols()), "my_struct");
|
||||
}
|
||||
|
||||
TEST_F(StructTypeTest, MinBufferBindingSize) {
|
||||
auto* str = create<ast::Struct>(
|
||||
ast::StructMemberList{Member("foo", ty.u32(), {MemberOffset(0)}),
|
||||
Member("bar", ty.u32(), {MemberOffset(4)})},
|
||||
ast::DecorationList{});
|
||||
auto* s_ty = ty.struct_("s_ty", str);
|
||||
|
||||
EXPECT_EQ(16u, s_ty->MinBufferBindingSize(MemoryLayout::kUniformBuffer));
|
||||
EXPECT_EQ(8u, s_ty->MinBufferBindingSize(MemoryLayout::kStorageBuffer));
|
||||
}
|
||||
|
||||
TEST_F(StructTypeTest, MinBufferBindingSizeArray) {
|
||||
Array arr(ty.u32(), 4, ast::DecorationList{create<ast::StrideDecoration>(4)});
|
||||
|
||||
auto* str = create<ast::Struct>(
|
||||
ast::StructMemberList{Member("foo", ty.u32(), {MemberOffset(0)}),
|
||||
Member("bar", ty.u32(), {MemberOffset(4)}),
|
||||
Member("bar", &arr, {MemberOffset(8)})},
|
||||
ast::DecorationList{});
|
||||
auto* s_ty = ty.struct_("s_ty", str);
|
||||
|
||||
EXPECT_EQ(32u, s_ty->MinBufferBindingSize(MemoryLayout::kUniformBuffer));
|
||||
EXPECT_EQ(24u, s_ty->MinBufferBindingSize(MemoryLayout::kStorageBuffer));
|
||||
}
|
||||
|
||||
TEST_F(StructTypeTest, MinBufferBindingSizeRuntimeArray) {
|
||||
Array arr(ty.u32(), 0, ast::DecorationList{create<ast::StrideDecoration>(4)});
|
||||
|
||||
auto* str = create<ast::Struct>(
|
||||
ast::StructMemberList{Member("foo", ty.u32(), {MemberOffset(0)}),
|
||||
Member("bar", ty.u32(), {MemberOffset(4)}),
|
||||
Member("bar", ty.u32(), {MemberOffset(8)})},
|
||||
ast::DecorationList{});
|
||||
auto* s_ty = ty.struct_("s_ty", str);
|
||||
|
||||
EXPECT_EQ(12u, s_ty->MinBufferBindingSize(MemoryLayout::kStorageBuffer));
|
||||
}
|
||||
|
||||
TEST_F(StructTypeTest, MinBufferBindingSizeVec2) {
|
||||
auto* str = create<ast::Struct>(
|
||||
ast::StructMemberList{Member("foo", ty.vec2<u32>(), {MemberOffset(0)})},
|
||||
ast::DecorationList{});
|
||||
auto* s_ty = ty.struct_("s_ty", str);
|
||||
|
||||
EXPECT_EQ(16u, s_ty->MinBufferBindingSize(MemoryLayout::kUniformBuffer));
|
||||
EXPECT_EQ(8u, s_ty->MinBufferBindingSize(MemoryLayout::kStorageBuffer));
|
||||
}
|
||||
|
||||
TEST_F(StructTypeTest, MinBufferBindingSizeVec3) {
|
||||
auto* str = create<ast::Struct>(
|
||||
ast::StructMemberList{Member("foo", ty.vec3<u32>(), {MemberOffset(0)})},
|
||||
ast::DecorationList{});
|
||||
auto* s_ty = ty.struct_("s_ty", str);
|
||||
|
||||
EXPECT_EQ(16u, s_ty->MinBufferBindingSize(MemoryLayout::kUniformBuffer));
|
||||
EXPECT_EQ(16u, s_ty->MinBufferBindingSize(MemoryLayout::kStorageBuffer));
|
||||
}
|
||||
|
||||
TEST_F(StructTypeTest, MinBufferBindingSizeVec4) {
|
||||
auto* str = create<ast::Struct>(
|
||||
ast::StructMemberList{Member("foo", ty.vec4<u32>(), {MemberOffset(0)})},
|
||||
ast::DecorationList{});
|
||||
auto* s_ty = ty.struct_("s_ty", str);
|
||||
|
||||
EXPECT_EQ(16u, s_ty->MinBufferBindingSize(MemoryLayout::kUniformBuffer));
|
||||
EXPECT_EQ(16u, s_ty->MinBufferBindingSize(MemoryLayout::kStorageBuffer));
|
||||
}
|
||||
|
||||
TEST_F(StructTypeTest, BaseAlignment) {
|
||||
auto* str = create<ast::Struct>(
|
||||
ast::StructMemberList{Member("foo", ty.u32(), {MemberOffset(0)}),
|
||||
Member("bar", ty.u32(), {MemberOffset(8)})},
|
||||
ast::DecorationList{});
|
||||
auto* s_ty = ty.struct_("s_ty", str);
|
||||
|
||||
EXPECT_EQ(16u, s_ty->BaseAlignment(MemoryLayout::kUniformBuffer));
|
||||
EXPECT_EQ(4u, s_ty->BaseAlignment(MemoryLayout::kStorageBuffer));
|
||||
}
|
||||
|
||||
TEST_F(StructTypeTest, BaseAlignmentArray) {
|
||||
Array arr(ty.u32(), 4, ast::DecorationList{create<ast::StrideDecoration>(4)});
|
||||
auto* str = create<ast::Struct>(
|
||||
ast::StructMemberList{Member("foo", ty.u32(), {MemberOffset(0)}),
|
||||
Member("bar", ty.u32(), {MemberOffset(4)}),
|
||||
Member("bar", &arr, {MemberOffset(8)})},
|
||||
ast::DecorationList{});
|
||||
auto* s_ty = ty.struct_("s_ty", str);
|
||||
|
||||
EXPECT_EQ(16u, s_ty->BaseAlignment(MemoryLayout::kUniformBuffer));
|
||||
EXPECT_EQ(4u, s_ty->BaseAlignment(MemoryLayout::kStorageBuffer));
|
||||
}
|
||||
|
||||
TEST_F(StructTypeTest, BaseAlignmentRuntimeArray) {
|
||||
Array arr(ty.u32(), 0, ast::DecorationList{create<ast::StrideDecoration>(4)});
|
||||
auto* str = create<ast::Struct>(
|
||||
ast::StructMemberList{Member("foo", ty.u32(), {MemberOffset(0)}),
|
||||
Member("bar", ty.u32(), {MemberOffset(4)}),
|
||||
Member("bar", ty.u32(), {MemberOffset(8)})},
|
||||
ast::DecorationList{});
|
||||
auto* s_ty = ty.struct_("s_ty", str);
|
||||
|
||||
EXPECT_EQ(4u, s_ty->BaseAlignment(MemoryLayout::kStorageBuffer));
|
||||
}
|
||||
|
||||
TEST_F(StructTypeTest, BaseAlignmentVec2) {
|
||||
auto* str = create<ast::Struct>(
|
||||
ast::StructMemberList{Member("foo", ty.vec2<u32>(), {MemberOffset(0)})},
|
||||
ast::DecorationList{});
|
||||
auto* s_ty = ty.struct_("s_ty", str);
|
||||
|
||||
EXPECT_EQ(16u, s_ty->BaseAlignment(MemoryLayout::kUniformBuffer));
|
||||
EXPECT_EQ(8u, s_ty->BaseAlignment(MemoryLayout::kStorageBuffer));
|
||||
}
|
||||
|
||||
TEST_F(StructTypeTest, BaseAlignmentVec3) {
|
||||
auto* str = create<ast::Struct>(
|
||||
ast::StructMemberList{Member("foo", ty.vec3<u32>(), {MemberOffset(0)})},
|
||||
ast::DecorationList{});
|
||||
auto* s_ty = ty.struct_("s_ty", str);
|
||||
|
||||
EXPECT_EQ(16u, s_ty->BaseAlignment(MemoryLayout::kUniformBuffer));
|
||||
EXPECT_EQ(16u, s_ty->BaseAlignment(MemoryLayout::kStorageBuffer));
|
||||
}
|
||||
|
||||
TEST_F(StructTypeTest, BaseAlignmentVec4) {
|
||||
auto* str = create<ast::Struct>(
|
||||
ast::StructMemberList{Member("foo", ty.vec4<u32>(), {MemberOffset(0)})},
|
||||
ast::DecorationList{});
|
||||
auto* s_ty = ty.struct_("s_ty", str);
|
||||
|
||||
EXPECT_EQ(16u, s_ty->BaseAlignment(MemoryLayout::kUniformBuffer));
|
||||
EXPECT_EQ(16u, s_ty->BaseAlignment(MemoryLayout::kStorageBuffer));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace type
|
||||
} // namespace tint
|
||||
|
|
|
@ -70,14 +70,6 @@ Type* Type::UnwrapAll() {
|
|||
return UnwrapIfNeeded()->UnwrapPtrIfNeeded()->UnwrapIfNeeded();
|
||||
}
|
||||
|
||||
uint64_t Type::MinBufferBindingSize(MemoryLayout) const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t Type::BaseAlignment(MemoryLayout) const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool Type::is_scalar() const {
|
||||
return is_float_scalar() || is_integer_scalar() || Is<Bool>();
|
||||
}
|
||||
|
|
|
@ -45,16 +45,6 @@ class Type : public Castable<Type, Cloneable> {
|
|||
/// declared in WGSL.
|
||||
virtual std::string FriendlyName(const SymbolTable& symbols) const = 0;
|
||||
|
||||
/// @param mem_layout type of memory layout to use in calculation.
|
||||
/// @returns minimum size required for this type, in bytes.
|
||||
/// 0 for non-host shareable types.
|
||||
virtual uint64_t MinBufferBindingSize(MemoryLayout mem_layout) const;
|
||||
|
||||
/// @param mem_layout type of memory layout to use in calculation.
|
||||
/// @returns base alignment for the type, in bytes.
|
||||
/// 0 for non-host shareable types.
|
||||
virtual uint64_t BaseAlignment(MemoryLayout mem_layout) const;
|
||||
|
||||
/// @returns the pointee type if this is a pointer, `this` otherwise
|
||||
Type* UnwrapPtrIfNeeded();
|
||||
|
||||
|
|
|
@ -35,14 +35,6 @@ std::string U32::FriendlyName(const SymbolTable&) const {
|
|||
return "u32";
|
||||
}
|
||||
|
||||
uint64_t U32::MinBufferBindingSize(MemoryLayout) const {
|
||||
return 4;
|
||||
}
|
||||
|
||||
uint64_t U32::BaseAlignment(MemoryLayout) const {
|
||||
return 4;
|
||||
}
|
||||
|
||||
U32* U32::Clone(CloneContext* ctx) const {
|
||||
return ctx->dst->create<U32>();
|
||||
}
|
||||
|
|
|
@ -39,16 +39,6 @@ class U32 : public Castable<U32, Type> {
|
|||
/// declared in WGSL.
|
||||
std::string FriendlyName(const SymbolTable& symbols) const override;
|
||||
|
||||
/// @param mem_layout type of memory layout to use in calculation.
|
||||
/// @returns minimum size required for this type, in bytes.
|
||||
/// 0 for non-host shareable types.
|
||||
uint64_t MinBufferBindingSize(MemoryLayout mem_layout) const override;
|
||||
|
||||
/// @param mem_layout type of memory layout to use in calculation.
|
||||
/// @returns base alignment for the type, in bytes.
|
||||
/// 0 for non-host shareable types.
|
||||
uint64_t BaseAlignment(MemoryLayout mem_layout) const override;
|
||||
|
||||
/// Clones this type and all transitive types using the `CloneContext` `ctx`.
|
||||
/// @param ctx the clone context
|
||||
/// @return the newly cloned type
|
||||
|
|
|
@ -50,16 +50,6 @@ TEST_F(U32Test, FriendlyName) {
|
|||
EXPECT_EQ(u.FriendlyName(Symbols()), "u32");
|
||||
}
|
||||
|
||||
TEST_F(U32Test, MinBufferBindingSize) {
|
||||
U32 u;
|
||||
EXPECT_EQ(4u, u.MinBufferBindingSize(MemoryLayout::kUniformBuffer));
|
||||
}
|
||||
|
||||
TEST_F(U32Test, BaseAlignment) {
|
||||
U32 u;
|
||||
EXPECT_EQ(4u, u.BaseAlignment(MemoryLayout::kUniformBuffer));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace type
|
||||
} // namespace tint
|
||||
|
|
|
@ -40,20 +40,6 @@ std::string Vector::FriendlyName(const SymbolTable& symbols) const {
|
|||
return out.str();
|
||||
}
|
||||
|
||||
uint64_t Vector::MinBufferBindingSize(MemoryLayout mem_layout) const {
|
||||
return size_ * subtype_->MinBufferBindingSize(mem_layout);
|
||||
}
|
||||
|
||||
uint64_t Vector::BaseAlignment(MemoryLayout mem_layout) const {
|
||||
if (size_ == 2) {
|
||||
return 2 * subtype_->BaseAlignment(mem_layout);
|
||||
} else if (size_ == 3 || size_ == 4) {
|
||||
return 4 * subtype_->BaseAlignment(mem_layout);
|
||||
}
|
||||
|
||||
return 0; // vectors are only supposed to have 2, 3, or 4 elements.
|
||||
}
|
||||
|
||||
Vector* Vector::Clone(CloneContext* ctx) const {
|
||||
// Clone arguments outside of create() call to have deterministic ordering
|
||||
auto* ty = ctx->Clone(type());
|
||||
|
|
|
@ -46,16 +46,6 @@ class Vector : public Castable<Vector, Type> {
|
|||
/// declared in WGSL.
|
||||
std::string FriendlyName(const SymbolTable& symbols) const override;
|
||||
|
||||
/// @param mem_layout type of memory layout to use in calculation.
|
||||
/// @returns minimum size required for this type, in bytes.
|
||||
/// 0 for non-host shareable types.
|
||||
uint64_t MinBufferBindingSize(MemoryLayout mem_layout) const override;
|
||||
|
||||
/// @param mem_layout type of memory layout to use in calculation.
|
||||
/// @returns base alignment for the type, in bytes.
|
||||
/// 0 for non-host shareable types.
|
||||
uint64_t BaseAlignment(MemoryLayout mem_layout) const override;
|
||||
|
||||
/// Clones this type and all transitive types using the `CloneContext` `ctx`.
|
||||
/// @param ctx the clone context
|
||||
/// @return the newly cloned type
|
||||
|
|
|
@ -59,42 +59,6 @@ TEST_F(VectorTest, FriendlyName) {
|
|||
EXPECT_EQ(v->FriendlyName(Symbols()), "vec3<f32>");
|
||||
}
|
||||
|
||||
TEST_F(VectorTest, MinBufferBindingSizeVec2) {
|
||||
I32 i32;
|
||||
Vector v{&i32, 2};
|
||||
EXPECT_EQ(8u, v.MinBufferBindingSize(MemoryLayout::kUniformBuffer));
|
||||
}
|
||||
|
||||
TEST_F(VectorTest, MinBufferBindingSizeVec3) {
|
||||
I32 i32;
|
||||
Vector v{&i32, 3};
|
||||
EXPECT_EQ(12u, v.MinBufferBindingSize(MemoryLayout::kUniformBuffer));
|
||||
}
|
||||
|
||||
TEST_F(VectorTest, MinBufferBindingSizeVec4) {
|
||||
I32 i32;
|
||||
Vector v{&i32, 4};
|
||||
EXPECT_EQ(16u, v.MinBufferBindingSize(MemoryLayout::kUniformBuffer));
|
||||
}
|
||||
|
||||
TEST_F(VectorTest, BaseAlignmentVec2) {
|
||||
I32 i32;
|
||||
Vector v{&i32, 2};
|
||||
EXPECT_EQ(8u, v.BaseAlignment(MemoryLayout::kUniformBuffer));
|
||||
}
|
||||
|
||||
TEST_F(VectorTest, BaseAlignmentVec3) {
|
||||
I32 i32;
|
||||
Vector v{&i32, 3};
|
||||
EXPECT_EQ(16u, v.BaseAlignment(MemoryLayout::kUniformBuffer));
|
||||
}
|
||||
|
||||
TEST_F(VectorTest, BaseAlignmentVec4) {
|
||||
I32 i32;
|
||||
Vector v{&i32, 4};
|
||||
EXPECT_EQ(16u, v.BaseAlignment(MemoryLayout::kUniformBuffer));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace type
|
||||
} // namespace tint
|
||||
|
|
|
@ -872,14 +872,6 @@ TEST_F(ValidatorTest, IsStorable_ArraySizedOfStorable) {
|
|||
EXPECT_TRUE(v.IsStorable(arr));
|
||||
}
|
||||
|
||||
TEST_F(ValidatorTest, IsStorable_ArraySizedOfNonStorable) {
|
||||
auto* arr = ty.array(ty.void_(), 5);
|
||||
|
||||
ValidatorImpl& v = Build();
|
||||
|
||||
EXPECT_FALSE(v.IsStorable(arr));
|
||||
}
|
||||
|
||||
TEST_F(ValidatorTest, IsStorable_ArrayUnsizedOfStorable) {
|
||||
auto* arr = ty.array<int>();
|
||||
|
||||
|
@ -888,14 +880,6 @@ TEST_F(ValidatorTest, IsStorable_ArrayUnsizedOfStorable) {
|
|||
EXPECT_TRUE(v.IsStorable(arr));
|
||||
}
|
||||
|
||||
TEST_F(ValidatorTest, IsStorable_ArrayUnsizedOfNonStorable) {
|
||||
auto* arr = ty.array<void>();
|
||||
|
||||
ValidatorImpl& v = Build();
|
||||
|
||||
EXPECT_FALSE(v.IsStorable(arr));
|
||||
}
|
||||
|
||||
TEST_F(ValidatorTest, IsStorable_Struct_AllMembersStorable) {
|
||||
ast::StructMemberList members{Member("a", ty.i32()), Member("b", ty.f32())};
|
||||
auto* s = create<ast::Struct>(Source{}, members, ast::DecorationList{});
|
||||
|
@ -906,16 +890,5 @@ TEST_F(ValidatorTest, IsStorable_Struct_AllMembersStorable) {
|
|||
EXPECT_TRUE(v.IsStorable(s_ty));
|
||||
}
|
||||
|
||||
TEST_F(ValidatorTest, IsStorable_Struct_SomeMembersNonStorable) {
|
||||
auto* ptr_ty = ty.pointer<int>(ast::StorageClass::kPrivate);
|
||||
ast::StructMemberList members{Member("a", ty.i32()), Member("b", ptr_ty)};
|
||||
auto* s = create<ast::Struct>(Source{}, members, ast::DecorationList{});
|
||||
auto* s_ty = ty.struct_("mystruct", s);
|
||||
|
||||
ValidatorImpl& v = Build();
|
||||
|
||||
EXPECT_FALSE(v.IsStorable(s_ty));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace tint
|
||||
|
|
|
@ -21,9 +21,11 @@
|
|||
#include "src/ast/constant_id_decoration.h"
|
||||
#include "src/ast/fallthrough_statement.h"
|
||||
#include "src/ast/variable_decl_statement.h"
|
||||
#include "src/semantic/array.h"
|
||||
#include "src/semantic/call.h"
|
||||
#include "src/semantic/function.h"
|
||||
#include "src/semantic/member_accessor_expression.h"
|
||||
#include "src/semantic/struct.h"
|
||||
#include "src/semantic/variable.h"
|
||||
#include "src/type/access_control_type.h"
|
||||
#include "src/type/multisampled_texture_type.h"
|
||||
|
@ -2021,11 +2023,13 @@ std::string GeneratorImpl::generate_storage_buffer_index_expression(
|
|||
auto* str_type = str->impl();
|
||||
auto* str_member = str_type->get_member(mem->member()->symbol());
|
||||
|
||||
if (!str_member->has_offset_decoration()) {
|
||||
diagnostics_.add_error("missing offset decoration for struct member");
|
||||
auto* sem_mem = builder_.Sem().Get(str_member);
|
||||
if (!sem_mem) {
|
||||
TINT_ICE(diagnostics_) << "struct member missing semantic info";
|
||||
return "";
|
||||
}
|
||||
out << str_member->offset();
|
||||
|
||||
out << sem_mem->Offset();
|
||||
|
||||
} else if (res_type->Is<type::Vector>()) {
|
||||
auto swizzle = builder_.Sem().Get(mem)->Swizzle();
|
||||
|
@ -2035,9 +2039,9 @@ std::string GeneratorImpl::generate_storage_buffer_index_expression(
|
|||
// This must be a single element swizzle if we've got a vector at this
|
||||
// point.
|
||||
if (swizzle.size() != 1) {
|
||||
diagnostics_.add_error(
|
||||
"Encountered multi-element swizzle when should have only one "
|
||||
"level");
|
||||
TINT_ICE(diagnostics_)
|
||||
<< "Encountered multi-element swizzle when should have only one "
|
||||
"level";
|
||||
return "";
|
||||
}
|
||||
|
||||
|
@ -2046,8 +2050,8 @@ std::string GeneratorImpl::generate_storage_buffer_index_expression(
|
|||
// f64 types.
|
||||
out << "(4 * " << swizzle[0] << ")";
|
||||
} else {
|
||||
diagnostics_.add_error("Invalid result type for member accessor: " +
|
||||
res_type->type_name());
|
||||
TINT_ICE(diagnostics_) << "Invalid result type for member accessor: "
|
||||
<< res_type->type_name();
|
||||
return "";
|
||||
}
|
||||
|
||||
|
@ -2057,7 +2061,12 @@ std::string GeneratorImpl::generate_storage_buffer_index_expression(
|
|||
|
||||
out << "(";
|
||||
if (auto* arr = ary_type->As<type::Array>()) {
|
||||
out << arr->array_stride();
|
||||
auto* sem_arr = builder_.Sem().Get(arr);
|
||||
if (!sem_arr) {
|
||||
TINT_ICE(diagnostics_) << "array type missing semantic info";
|
||||
return "";
|
||||
}
|
||||
out << sem_arr->Stride();
|
||||
} else if (ary_type->Is<type::Vector>()) {
|
||||
// TODO(dsinclair): This is a hack. Our vectors can only be f32, i32
|
||||
// or u32 which are all 4 bytes. When we get f16 or other types we'll
|
||||
|
|
|
@ -43,8 +43,10 @@ TEST_F(HlslGeneratorImplTest_Alias, EmitAlias_NameCollision) {
|
|||
|
||||
TEST_F(HlslGeneratorImplTest_Alias, EmitAlias_Struct) {
|
||||
auto* str = create<ast::Struct>(
|
||||
ast::StructMemberList{Member("a", ty.f32()),
|
||||
Member("b", ty.i32(), {MemberOffset(4)})},
|
||||
ast::StructMemberList{
|
||||
Member("a", ty.f32()),
|
||||
Member("b", ty.i32()),
|
||||
},
|
||||
ast::DecorationList{});
|
||||
|
||||
auto* s = ty.struct_("A", str);
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "src/ast/stage_decoration.h"
|
||||
#include "src/ast/struct_block_decoration.h"
|
||||
#include "src/ast/variable_decl_statement.h"
|
||||
|
@ -19,6 +20,8 @@
|
|||
#include "src/type/access_control_type.h"
|
||||
#include "src/writer/hlsl/test_helper.h"
|
||||
|
||||
using ::testing::HasSubstr;
|
||||
|
||||
namespace tint {
|
||||
namespace writer {
|
||||
namespace hlsl {
|
||||
|
@ -264,11 +267,7 @@ void frag_main() {
|
|||
|
||||
TEST_F(HlslGeneratorImplTest_Function,
|
||||
Emit_Decoration_EntryPoint_With_UniformStruct) {
|
||||
auto* str = create<ast::Struct>(
|
||||
ast::StructMemberList{Member("coord", ty.vec4<f32>())},
|
||||
ast::DecorationList{});
|
||||
|
||||
auto* s = ty.struct_("Uniforms", str);
|
||||
auto* s = Structure("Uniforms", {Member("coord", ty.vec4<f32>())});
|
||||
|
||||
Global("uniforms", s, ast::StorageClass::kUniform, nullptr,
|
||||
ast::DecorationList{
|
||||
|
@ -276,8 +275,6 @@ TEST_F(HlslGeneratorImplTest_Function,
|
|||
create<ast::GroupDecoration>(1),
|
||||
});
|
||||
|
||||
AST().AddConstructedType(s);
|
||||
|
||||
auto* var = Var("v", ty.f32(), ast::StorageClass::kFunction,
|
||||
create<ast::MemberAccessorExpression>(
|
||||
MemberAccessor("uniforms", "coord"), Expr("x")));
|
||||
|
@ -310,12 +307,11 @@ void frag_main() {
|
|||
|
||||
TEST_F(HlslGeneratorImplTest_Function,
|
||||
Emit_Decoration_EntryPoint_With_RW_StorageBuffer_Read) {
|
||||
auto* str = create<ast::Struct>(
|
||||
ast::StructMemberList{Member("a", ty.i32(), {MemberOffset(0)}),
|
||||
Member("b", ty.f32(), {MemberOffset(4)})},
|
||||
ast::DecorationList{});
|
||||
auto* s = Structure("Data", {
|
||||
Member("a", ty.i32()),
|
||||
Member("b", ty.f32()),
|
||||
});
|
||||
|
||||
auto* s = ty.struct_("Data", str);
|
||||
type::AccessControl ac(ast::AccessControl::kReadWrite, s);
|
||||
|
||||
Global("coord", &ac, ast::StorageClass::kStorage, nullptr,
|
||||
|
@ -339,24 +335,21 @@ TEST_F(HlslGeneratorImplTest_Function,
|
|||
GeneratorImpl& gen = Build();
|
||||
|
||||
ASSERT_TRUE(gen.Generate(out)) << gen.error();
|
||||
EXPECT_EQ(result(), R"(RWByteAddressBuffer coord : register(u0);
|
||||
EXPECT_THAT(result(), HasSubstr(R"(RWByteAddressBuffer coord : register(u0);
|
||||
|
||||
void frag_main() {
|
||||
float v = asfloat(coord.Load(4));
|
||||
return;
|
||||
}
|
||||
|
||||
)");
|
||||
})"));
|
||||
}
|
||||
|
||||
TEST_F(HlslGeneratorImplTest_Function,
|
||||
Emit_Decoration_EntryPoint_With_RO_StorageBuffer_Read) {
|
||||
auto* str = create<ast::Struct>(
|
||||
ast::StructMemberList{Member("a", ty.i32(), {MemberOffset(0)}),
|
||||
Member("b", ty.f32(), {MemberOffset(4)})},
|
||||
ast::DecorationList{});
|
||||
auto* s = Structure("Data", {
|
||||
Member("a", ty.i32()),
|
||||
Member("b", ty.f32()),
|
||||
});
|
||||
|
||||
auto* s = ty.struct_("Data", str);
|
||||
type::AccessControl ac(ast::AccessControl::kReadOnly, s);
|
||||
|
||||
Global("coord", &ac, ast::StorageClass::kStorage, nullptr,
|
||||
|
@ -380,24 +373,21 @@ TEST_F(HlslGeneratorImplTest_Function,
|
|||
GeneratorImpl& gen = Build();
|
||||
|
||||
ASSERT_TRUE(gen.Generate(out)) << gen.error();
|
||||
EXPECT_EQ(result(), R"(ByteAddressBuffer coord : register(t0);
|
||||
EXPECT_THAT(result(), HasSubstr(R"(ByteAddressBuffer coord : register(t0);
|
||||
|
||||
void frag_main() {
|
||||
float v = asfloat(coord.Load(4));
|
||||
return;
|
||||
}
|
||||
|
||||
)");
|
||||
})"));
|
||||
}
|
||||
|
||||
TEST_F(HlslGeneratorImplTest_Function,
|
||||
Emit_Decoration_EntryPoint_With_WO_StorageBuffer_Store) {
|
||||
auto* str = create<ast::Struct>(
|
||||
ast::StructMemberList{Member("a", ty.i32(), {MemberOffset(0)}),
|
||||
Member("b", ty.f32(), {MemberOffset(4)})},
|
||||
ast::DecorationList{});
|
||||
auto* s = Structure("Data", {
|
||||
Member("a", ty.i32()),
|
||||
Member("b", ty.f32()),
|
||||
});
|
||||
|
||||
auto* s = ty.struct_("Data", str);
|
||||
type::AccessControl ac(ast::AccessControl::kWriteOnly, s);
|
||||
|
||||
Global("coord", &ac, ast::StorageClass::kStorage, nullptr,
|
||||
|
@ -419,24 +409,21 @@ TEST_F(HlslGeneratorImplTest_Function,
|
|||
GeneratorImpl& gen = Build();
|
||||
|
||||
ASSERT_TRUE(gen.Generate(out)) << gen.error();
|
||||
EXPECT_EQ(result(), R"(RWByteAddressBuffer coord : register(u0);
|
||||
EXPECT_THAT(result(), HasSubstr(R"(RWByteAddressBuffer coord : register(u0);
|
||||
|
||||
void frag_main() {
|
||||
coord.Store(4, asuint(2.0f));
|
||||
return;
|
||||
}
|
||||
|
||||
)");
|
||||
})"));
|
||||
}
|
||||
|
||||
TEST_F(HlslGeneratorImplTest_Function,
|
||||
Emit_Decoration_EntryPoint_With_StorageBuffer_Store) {
|
||||
auto* str = create<ast::Struct>(
|
||||
ast::StructMemberList{Member("a", ty.i32(), {MemberOffset(0)}),
|
||||
Member("b", ty.f32(), {MemberOffset(4)})},
|
||||
ast::DecorationList{});
|
||||
auto* s = Structure("Data", {
|
||||
Member("a", ty.i32()),
|
||||
Member("b", ty.f32()),
|
||||
});
|
||||
|
||||
auto* s = ty.struct_("Data", str);
|
||||
type::AccessControl ac(ast::AccessControl::kReadWrite, s);
|
||||
|
||||
Global("coord", &ac, ast::StorageClass::kStorage, nullptr,
|
||||
|
@ -458,14 +445,12 @@ TEST_F(HlslGeneratorImplTest_Function,
|
|||
GeneratorImpl& gen = Build();
|
||||
|
||||
ASSERT_TRUE(gen.Generate(out)) << gen.error();
|
||||
EXPECT_EQ(result(), R"(RWByteAddressBuffer coord : register(u0);
|
||||
EXPECT_THAT(result(), HasSubstr(R"(RWByteAddressBuffer coord : register(u0);
|
||||
|
||||
void frag_main() {
|
||||
coord.Store(4, asuint(2.0f));
|
||||
return;
|
||||
}
|
||||
|
||||
)");
|
||||
})"));
|
||||
}
|
||||
|
||||
TEST_F(
|
||||
|
@ -715,7 +700,7 @@ TEST_F(HlslGeneratorImplTest_Function,
|
|||
GeneratorImpl& gen = Build();
|
||||
|
||||
ASSERT_TRUE(gen.Generate(out)) << gen.error();
|
||||
EXPECT_EQ(result(), R"(RWByteAddressBuffer coord : register(u0);
|
||||
EXPECT_THAT(result(), HasSubstr(R"(RWByteAddressBuffer coord : register(u0);
|
||||
|
||||
float sub_func(float param) {
|
||||
return asfloat(coord.Load((4 * 0)));
|
||||
|
@ -724,9 +709,7 @@ float sub_func(float param) {
|
|||
void frag_main() {
|
||||
float v = sub_func(1.0f);
|
||||
return;
|
||||
}
|
||||
|
||||
)");
|
||||
})"));
|
||||
}
|
||||
|
||||
TEST_F(HlslGeneratorImplTest_Function,
|
||||
|
@ -857,7 +840,7 @@ TEST_F(HlslGeneratorImplTest_Function, Emit_Function_WithArrayParams) {
|
|||
TEST_F(HlslGeneratorImplTest_Function,
|
||||
Emit_Multiple_EntryPoint_With_Same_ModuleVar) {
|
||||
// [[block]] struct Data {
|
||||
// [[offset(0)]] d : f32;
|
||||
// d : f32;
|
||||
// };
|
||||
// [[binding(0), group(0)]] var<storage> data : Data;
|
||||
//
|
||||
|
@ -871,12 +854,9 @@ TEST_F(HlslGeneratorImplTest_Function,
|
|||
// return;
|
||||
// }
|
||||
|
||||
auto* str = create<ast::Struct>(
|
||||
ast::StructMemberList{Member("d", ty.f32(), {MemberOffset(0)})},
|
||||
ast::DecorationList{create<ast::StructBlockDecoration>()});
|
||||
|
||||
auto* s = ty.struct_("Data", str);
|
||||
AST().AddConstructedType(s);
|
||||
auto* s =
|
||||
Structure("Data", {Member("d", ty.f32())},
|
||||
ast::DecorationList{create<ast::StructBlockDecoration>()});
|
||||
|
||||
type::AccessControl ac(ast::AccessControl::kReadWrite, s);
|
||||
|
||||
|
|
|
@ -22,11 +22,7 @@ namespace {
|
|||
using HlslGeneratorImplTest_MemberAccessor = TestHelper;
|
||||
|
||||
TEST_F(HlslGeneratorImplTest_MemberAccessor, EmitExpression_MemberAccessor) {
|
||||
auto* strct = create<ast::Struct>(
|
||||
ast::StructMemberList{Member("mem", ty.f32(), {MemberOffset(0)})},
|
||||
ast::DecorationList{});
|
||||
|
||||
auto* s = ty.struct_("Str", strct);
|
||||
auto* s = Structure("Data", {Member("mem", ty.f32())});
|
||||
auto* str_var = Global("str", s, ast::StorageClass::kPrivate);
|
||||
|
||||
auto* expr = MemberAccessor("str", "mem");
|
||||
|
@ -43,20 +39,19 @@ TEST_F(HlslGeneratorImplTest_MemberAccessor, EmitExpression_MemberAccessor) {
|
|||
TEST_F(HlslGeneratorImplTest_MemberAccessor,
|
||||
EmitExpression_MemberAccessor_StorageBuffer_Load) {
|
||||
// struct Data {
|
||||
// [[offset(0)]] a : i32;
|
||||
// [[offset(4)]] b : f32;
|
||||
// a : i32;
|
||||
// b : f32;
|
||||
// };
|
||||
// var<storage> data : Data;
|
||||
// data.b;
|
||||
//
|
||||
// -> asfloat(data.Load(4));
|
||||
|
||||
auto* str = create<ast::Struct>(
|
||||
ast::StructMemberList{Member("a", ty.i32(), {MemberOffset(0)}),
|
||||
Member("b", ty.f32(), {MemberOffset(4)})},
|
||||
ast::DecorationList{});
|
||||
auto* s = Structure("data", {
|
||||
Member("a", ty.i32()),
|
||||
Member("b", ty.f32()),
|
||||
});
|
||||
|
||||
auto* s = ty.struct_("Data", str);
|
||||
auto* coord_var = Global("data", s, ast::StorageClass::kStorage);
|
||||
|
||||
auto* expr = MemberAccessor("data", "b");
|
||||
|
@ -73,19 +68,19 @@ TEST_F(HlslGeneratorImplTest_MemberAccessor,
|
|||
TEST_F(HlslGeneratorImplTest_MemberAccessor,
|
||||
EmitExpression_MemberAccessor_StorageBuffer_Load_Int) {
|
||||
// struct Data {
|
||||
// [[offset(0)]] a : i32;
|
||||
// [[offset(4)]] b : f32;
|
||||
// a : i32;
|
||||
// b : f32;
|
||||
// };
|
||||
// var<storage> data : Data;
|
||||
// data.a;
|
||||
//
|
||||
// -> asint(data.Load(0));
|
||||
|
||||
auto* str = create<ast::Struct>(
|
||||
ast::StructMemberList{Member("a", ty.i32(), {MemberOffset(0)}),
|
||||
Member("b", ty.f32(), {MemberOffset(4)})},
|
||||
ast::DecorationList{});
|
||||
auto* s = ty.struct_("Data", str);
|
||||
auto* s = Structure("data", {
|
||||
Member("a", ty.i32()),
|
||||
Member("b", ty.f32()),
|
||||
});
|
||||
|
||||
auto* coord_var = Global("data", s, ast::StorageClass::kStorage);
|
||||
|
||||
auto* expr = MemberAccessor("data", "a");
|
||||
|
@ -102,8 +97,8 @@ TEST_F(HlslGeneratorImplTest_MemberAccessor,
|
|||
TEST_F(HlslGeneratorImplTest_MemberAccessor,
|
||||
EmitExpression_MemberAccessor_StorageBuffer_Store_Matrix) {
|
||||
// struct Data {
|
||||
// [[offset(0)]] z : f32;
|
||||
// [[offset(4)]] a : mat2x3<f32>;
|
||||
// z : f32;
|
||||
// a : mat2x3<f32>;
|
||||
// };
|
||||
// var<storage> data : Data;
|
||||
// mat2x3<f32> b;
|
||||
|
@ -113,12 +108,9 @@ TEST_F(HlslGeneratorImplTest_MemberAccessor,
|
|||
// data.Store3(4 + 0, asuint(_tint_tmp[0]));
|
||||
// data.Store3(4 + 16, asuint(_tint_tmp[1]));
|
||||
|
||||
auto* str = create<ast::Struct>(
|
||||
ast::StructMemberList{Member("z", ty.i32(), {MemberOffset(0)}),
|
||||
Member("a", ty.mat2x3<f32>(), {MemberOffset(4)})},
|
||||
ast::DecorationList{});
|
||||
auto* s =
|
||||
Structure("Data", {Member("z", ty.i32()), Member("a", ty.mat2x3<f32>())});
|
||||
|
||||
auto* s = ty.struct_("Data", str);
|
||||
auto* b_var = Global("b", ty.mat2x3<f32>(), ast::StorageClass::kPrivate);
|
||||
auto* coord_var = Global("data", s, ast::StorageClass::kStorage);
|
||||
|
||||
|
@ -135,31 +127,28 @@ TEST_F(HlslGeneratorImplTest_MemberAccessor,
|
|||
|
||||
ASSERT_TRUE(gen.EmitStatement(out, assign)) << gen.error();
|
||||
EXPECT_EQ(result(), R"(float3x2 _tint_tmp = b;
|
||||
data.Store3(4 + 0, asuint(_tint_tmp[0]));
|
||||
data.Store3(4 + 16, asuint(_tint_tmp[1]));
|
||||
data.Store3(16 + 0, asuint(_tint_tmp[0]));
|
||||
data.Store3(16 + 16, asuint(_tint_tmp[1]));
|
||||
)");
|
||||
}
|
||||
|
||||
TEST_F(HlslGeneratorImplTest_MemberAccessor,
|
||||
EmitExpression_MemberAccessor_StorageBuffer_Store_Matrix_Empty) {
|
||||
// struct Data {
|
||||
// [[offset(0)]] z : f32;
|
||||
// [[offset(4)]] a : mat2x3<f32>;
|
||||
// z : f32;
|
||||
// a : mat2x3<f32>;
|
||||
// };
|
||||
// var<storage> data : Data;
|
||||
// data.a = mat2x3<f32>();
|
||||
//
|
||||
// -> float3x2 _tint_tmp = float3x2(0.0f, 0.0f, 0.0f,
|
||||
// 0.0f, 0.0f, 0.0f);
|
||||
// data.Store3(4 + 0, asuint(_tint_tmp[0]);
|
||||
// data.Store3(4 + 16, asuint(_tint_tmp[1]));
|
||||
// data.Store3(16 + 0, asuint(_tint_tmp[0]);
|
||||
// data.Store3(16 + 16, asuint(_tint_tmp[1]));
|
||||
|
||||
auto* str = create<ast::Struct>(
|
||||
ast::StructMemberList{Member("z", ty.i32(), {MemberOffset(0)}),
|
||||
Member("a", ty.mat2x3<f32>(), {MemberOffset(4)})},
|
||||
ast::DecorationList{});
|
||||
auto* s =
|
||||
Structure("Data", {Member("z", ty.i32()), Member("a", ty.mat2x3<f32>())});
|
||||
|
||||
auto* s = ty.struct_("Data", str);
|
||||
auto* coord_var = Global("data", s, ast::StorageClass::kStorage);
|
||||
|
||||
auto* lhs = MemberAccessor("data", "a");
|
||||
|
@ -176,16 +165,16 @@ TEST_F(HlslGeneratorImplTest_MemberAccessor,
|
|||
EXPECT_EQ(
|
||||
result(),
|
||||
R"(float3x2 _tint_tmp = float3x2(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f);
|
||||
data.Store3(4 + 0, asuint(_tint_tmp[0]));
|
||||
data.Store3(4 + 16, asuint(_tint_tmp[1]));
|
||||
data.Store3(16 + 0, asuint(_tint_tmp[0]));
|
||||
data.Store3(16 + 16, asuint(_tint_tmp[1]));
|
||||
)");
|
||||
}
|
||||
|
||||
TEST_F(HlslGeneratorImplTest_MemberAccessor,
|
||||
EmitExpression_MemberAccessor_StorageBuffer_Load_Matrix) {
|
||||
// struct Data {
|
||||
// [[offset(0)]] z : f32;
|
||||
// [[offset(4)]] a : mat3x2<f32>;
|
||||
// z : f32;
|
||||
// a : mat3x2<f32>;
|
||||
// };
|
||||
// var<storage> data : Data;
|
||||
// data.a;
|
||||
|
@ -193,12 +182,9 @@ TEST_F(HlslGeneratorImplTest_MemberAccessor,
|
|||
// -> asfloat(uint2x3(data.Load2(4 + 0), data.Load2(4 + 8),
|
||||
// data.Load2(4 + 16)));
|
||||
|
||||
auto* str = create<ast::Struct>(
|
||||
ast::StructMemberList{Member("z", ty.i32(), {MemberOffset(0)}),
|
||||
Member("a", ty.mat3x2<f32>(), {MemberOffset(4)})},
|
||||
ast::DecorationList{});
|
||||
auto* s =
|
||||
Structure("Data", {Member("z", ty.i32()), Member("a", ty.mat3x2<f32>())});
|
||||
|
||||
auto* s = ty.struct_("Data", str);
|
||||
auto* coord_var = Global("data", s, ast::StorageClass::kStorage);
|
||||
|
||||
auto* expr = MemberAccessor("data", "a");
|
||||
|
@ -210,33 +196,26 @@ TEST_F(HlslGeneratorImplTest_MemberAccessor,
|
|||
|
||||
ASSERT_TRUE(gen.EmitExpression(pre, out, expr)) << gen.error();
|
||||
EXPECT_EQ(result(),
|
||||
"asfloat(uint2x3(data.Load2(4 + 0), data.Load2(4 + 8), "
|
||||
"data.Load2(4 + 16)))");
|
||||
"asfloat(uint2x3(data.Load2(8 + 0), data.Load2(8 + 8), "
|
||||
"data.Load2(8 + 16)))");
|
||||
}
|
||||
|
||||
TEST_F(HlslGeneratorImplTest_MemberAccessor,
|
||||
EmitExpression_MemberAccessor_StorageBuffer_Load_Matrix_Nested) {
|
||||
// struct Data {
|
||||
// [[offset(0)]] z : f32;
|
||||
// [[offset(4)]] a : mat2x3<f32;
|
||||
// };
|
||||
// struct Outer {
|
||||
// [[offset(0)]] c : f32;
|
||||
// [[offset(4)]] b : Data;
|
||||
// z : f32;
|
||||
// a : mat2x3<f32>
|
||||
// };
|
||||
// var<storage> data : Outer;
|
||||
// data.b.a;
|
||||
//
|
||||
// -> asfloat(uint3x2(data.Load3(4 + 0), data.Load3(4 + 16)));
|
||||
// -> asfloat(uint3x2(data.Load3(4 + 0), data.Load3(16 + 16)));
|
||||
|
||||
auto* str = create<ast::Struct>(
|
||||
ast::StructMemberList{
|
||||
Member("z", ty.i32(), {MemberOffset(0)}),
|
||||
Member("a", ty.mat2x3<f32>(), {MemberOffset(4)}),
|
||||
},
|
||||
ast::DecorationList{});
|
||||
auto* s = Structure("Data", {
|
||||
Member("z", ty.i32()),
|
||||
Member("a", ty.mat2x3<f32>()),
|
||||
});
|
||||
|
||||
auto* s = ty.struct_("Data", str);
|
||||
auto* coord_var = Global("data", s, ast::StorageClass::kStorage);
|
||||
|
||||
auto* expr = MemberAccessor("data", "a");
|
||||
|
@ -248,14 +227,14 @@ TEST_F(HlslGeneratorImplTest_MemberAccessor,
|
|||
|
||||
ASSERT_TRUE(gen.EmitExpression(pre, out, expr)) << gen.error();
|
||||
EXPECT_EQ(result(),
|
||||
"asfloat(uint3x2(data.Load3(4 + 0), data.Load3(4 + 16)))");
|
||||
"asfloat(uint3x2(data.Load3(16 + 0), data.Load3(16 + 16)))");
|
||||
}
|
||||
|
||||
TEST_F(
|
||||
HlslGeneratorImplTest_MemberAccessor,
|
||||
EmitExpression_MemberAccessor_StorageBuffer_Load_Matrix_By3_Is_16_Bytes) {
|
||||
// struct Data {
|
||||
// [[offset(4)]] a : mat3x3<f32;
|
||||
// a : mat3x3<f32>
|
||||
// };
|
||||
// var<storage> data : Data;
|
||||
// data.a;
|
||||
|
@ -263,11 +242,10 @@ TEST_F(
|
|||
// -> asfloat(uint3x3(data.Load3(0), data.Load3(16),
|
||||
// data.Load3(32)));
|
||||
|
||||
auto* str = create<ast::Struct>(
|
||||
ast::StructMemberList{Member("a", ty.mat3x3<f32>(), {MemberOffset(0)})},
|
||||
ast::DecorationList{});
|
||||
auto* s = Structure("Data", {
|
||||
Member("a", ty.mat3x3<f32>()),
|
||||
});
|
||||
|
||||
auto* s = ty.struct_("Data", str);
|
||||
auto* coord_var = Global("data", s, ast::StorageClass::kStorage);
|
||||
|
||||
auto* expr = MemberAccessor("data", "a");
|
||||
|
@ -286,20 +264,19 @@ TEST_F(
|
|||
TEST_F(HlslGeneratorImplTest_MemberAccessor,
|
||||
EmitExpression_MemberAccessor_StorageBuffer_Load_Matrix_Single_Element) {
|
||||
// struct Data {
|
||||
// [[offset(0)]] z : f32;
|
||||
// [[offset(16)]] a : mat4x3<f32>;
|
||||
// z : f32;
|
||||
// a : mat4x3<f32>;
|
||||
// };
|
||||
// var<storage> data : Data;
|
||||
// data.a[2][1];
|
||||
//
|
||||
// -> asfloat(data.Load((2 * 16) + (1 * 4) + 16)))
|
||||
|
||||
auto* str = create<ast::Struct>(
|
||||
ast::StructMemberList{Member("z", ty.i32(), {MemberOffset(0)}),
|
||||
Member("a", ty.mat4x3<f32>(), {MemberOffset(16)})},
|
||||
ast::DecorationList{});
|
||||
auto* s = Structure("Data", {
|
||||
Member("z", ty.i32()),
|
||||
Member("a", ty.mat4x3<f32>()),
|
||||
});
|
||||
|
||||
auto* s = ty.struct_("Data", str);
|
||||
auto* coord_var = Global("data", s, ast::StorageClass::kStorage);
|
||||
|
||||
auto* expr = IndexAccessor(
|
||||
|
@ -317,7 +294,7 @@ TEST_F(HlslGeneratorImplTest_MemberAccessor,
|
|||
TEST_F(HlslGeneratorImplTest_MemberAccessor,
|
||||
EmitExpression_ArrayAccessor_StorageBuffer_Load_Int_FromArray) {
|
||||
// struct Data {
|
||||
// [[offset(0)]] a : [[stride(4)]] array<i32, 5>;
|
||||
// a : [[stride(4)]] array<i32, 5>;
|
||||
// };
|
||||
// var<storage> data : Data;
|
||||
// data.a[2];
|
||||
|
@ -328,10 +305,8 @@ TEST_F(HlslGeneratorImplTest_MemberAccessor,
|
|||
create<ast::StrideDecoration>(4),
|
||||
});
|
||||
|
||||
auto* str = create<ast::Struct>(
|
||||
ast::StructMemberList{Member("a", &ary, {MemberOffset(0)})},
|
||||
ast::DecorationList{});
|
||||
auto* s = ty.struct_("Data", str);
|
||||
auto* s = Structure("Data", {Member("a", &ary)});
|
||||
|
||||
auto* coord_var = Global("data", s, ast::StorageClass::kStorage);
|
||||
|
||||
auto* expr = IndexAccessor(MemberAccessor("data", "a"), Expr(2));
|
||||
|
@ -348,7 +323,7 @@ TEST_F(HlslGeneratorImplTest_MemberAccessor,
|
|||
TEST_F(HlslGeneratorImplTest_MemberAccessor,
|
||||
EmitExpression_ArrayAccessor_StorageBuffer_Load_Int_FromArray_ExprIdx) {
|
||||
// struct Data {
|
||||
// [[offset(0)]] a : [[stride(4)]] array<i32, 5>;
|
||||
// a : [[stride(4)]] array<i32, 5>;
|
||||
// };
|
||||
// var<storage> data : Data;
|
||||
// data.a[(2 + 4) - 3];
|
||||
|
@ -359,10 +334,8 @@ TEST_F(HlslGeneratorImplTest_MemberAccessor,
|
|||
create<ast::StrideDecoration>(4),
|
||||
});
|
||||
|
||||
auto* str = create<ast::Struct>(
|
||||
ast::StructMemberList{Member("a", &ary, {MemberOffset(0)})},
|
||||
ast::DecorationList{});
|
||||
auto* s = ty.struct_("Data", str);
|
||||
auto* s = Structure("Data", {Member("a", &ary)});
|
||||
|
||||
auto* coord_var = Global("data", s, ast::StorageClass::kStorage);
|
||||
|
||||
auto* expr = IndexAccessor(MemberAccessor("data", "a"),
|
||||
|
@ -380,20 +353,19 @@ TEST_F(HlslGeneratorImplTest_MemberAccessor,
|
|||
TEST_F(HlslGeneratorImplTest_MemberAccessor,
|
||||
EmitExpression_MemberAccessor_StorageBuffer_Store) {
|
||||
// struct Data {
|
||||
// [[offset(0)]] a : i32;
|
||||
// [[offset(4)]] b : f32;
|
||||
// a : i32;
|
||||
// b : f32;
|
||||
// };
|
||||
// var<storage> data : Data;
|
||||
// data.b = 2.3f;
|
||||
//
|
||||
// -> data.Store(0, asuint(2.0f));
|
||||
|
||||
auto* str = create<ast::Struct>(
|
||||
ast::StructMemberList{Member("a", ty.i32(), {MemberOffset(0)}),
|
||||
Member("b", ty.f32(), {MemberOffset(4)})},
|
||||
ast::DecorationList{});
|
||||
auto* s = Structure("data", {
|
||||
Member("a", ty.i32()),
|
||||
Member("b", ty.f32()),
|
||||
});
|
||||
|
||||
auto* s = ty.struct_("Data", str);
|
||||
auto* coord_var = Global("data", s, ast::StorageClass::kStorage);
|
||||
|
||||
auto* lhs = MemberAccessor("data", "b");
|
||||
|
@ -413,7 +385,7 @@ TEST_F(HlslGeneratorImplTest_MemberAccessor,
|
|||
TEST_F(HlslGeneratorImplTest_MemberAccessor,
|
||||
EmitExpression_MemberAccessor_StorageBuffer_Store_ToArray) {
|
||||
// struct Data {
|
||||
// [[offset(0)]] a : [[stride(4)]] array<i32, 5>;
|
||||
// a : [[stride(4)]] array<i32, 5>;
|
||||
// };
|
||||
// var<storage> data : Data;
|
||||
// data.a[2] = 2;
|
||||
|
@ -425,11 +397,8 @@ TEST_F(HlslGeneratorImplTest_MemberAccessor,
|
|||
create<ast::StrideDecoration>(4),
|
||||
});
|
||||
|
||||
auto* str = create<ast::Struct>(
|
||||
ast::StructMemberList{Member("a", &ary, {MemberOffset(0)})},
|
||||
ast::DecorationList{});
|
||||
auto* s = Structure("Data", {Member("a", &ary)});
|
||||
|
||||
auto* s = ty.struct_("Data", str);
|
||||
auto* coord_var = Global("data", s, ast::StorageClass::kStorage);
|
||||
|
||||
auto* lhs = IndexAccessor(MemberAccessor("data", "a"), Expr(2));
|
||||
|
@ -449,20 +418,19 @@ TEST_F(HlslGeneratorImplTest_MemberAccessor,
|
|||
TEST_F(HlslGeneratorImplTest_MemberAccessor,
|
||||
EmitExpression_MemberAccessor_StorageBuffer_Store_Int) {
|
||||
// struct Data {
|
||||
// [[offset(0)]] a : i32;
|
||||
// [[offset(4)]] b : f32;
|
||||
// a : i32;
|
||||
// b : f32;
|
||||
// };
|
||||
// var<storage> data : Data;
|
||||
// data.a = 2;
|
||||
//
|
||||
// -> data.Store(0, asuint(2));
|
||||
|
||||
auto* str = create<ast::Struct>(
|
||||
ast::StructMemberList{Member("a", ty.i32(), {MemberOffset(0)}),
|
||||
Member("b", ty.f32(), {MemberOffset(4)})},
|
||||
ast::DecorationList{});
|
||||
auto* s = Structure("data", {
|
||||
Member("a", ty.i32()),
|
||||
Member("b", ty.f32()),
|
||||
});
|
||||
|
||||
auto* s = ty.struct_("Data", str);
|
||||
auto* coord_var = Global("data", s, ast::StorageClass::kStorage);
|
||||
|
||||
auto* lhs = MemberAccessor("data", "a");
|
||||
|
@ -482,20 +450,19 @@ TEST_F(HlslGeneratorImplTest_MemberAccessor,
|
|||
TEST_F(HlslGeneratorImplTest_MemberAccessor,
|
||||
EmitExpression_MemberAccessor_StorageBuffer_Load_Vec3) {
|
||||
// struct Data {
|
||||
// [[offset(0)]] a : vec3<i32>;
|
||||
// [[offset(16)]] b : vec3<f32>;
|
||||
// a : vec3<i32>;
|
||||
// b : vec3<f32>;
|
||||
// };
|
||||
// var<storage> data : Data;
|
||||
// data.b;
|
||||
//
|
||||
// -> asfloat(data.Load(16));
|
||||
|
||||
auto* str = create<ast::Struct>(
|
||||
ast::StructMemberList{Member("a", ty.vec3<i32>(), {MemberOffset(0)}),
|
||||
Member("b", ty.vec3<f32>(), {MemberOffset(16)})},
|
||||
ast::DecorationList{});
|
||||
auto* s = Structure("Data", {
|
||||
Member("a", ty.vec3<i32>()),
|
||||
Member("b", ty.vec3<f32>()),
|
||||
});
|
||||
|
||||
auto* s = ty.struct_("Data", str);
|
||||
auto* coord_var = Global("data", s, ast::StorageClass::kStorage);
|
||||
|
||||
auto* expr = MemberAccessor("data", "b");
|
||||
|
@ -512,20 +479,19 @@ TEST_F(HlslGeneratorImplTest_MemberAccessor,
|
|||
TEST_F(HlslGeneratorImplTest_MemberAccessor,
|
||||
EmitExpression_MemberAccessor_StorageBuffer_Store_Vec3) {
|
||||
// struct Data {
|
||||
// [[offset(0)]] a : vec3<i32>;
|
||||
// [[offset(16)]] b : vec3<f32>;
|
||||
// a : vec3<i32>;
|
||||
// b : vec3<f32>;
|
||||
// };
|
||||
// var<storage> data : Data;
|
||||
// data.b = vec3<f32>(2.3f, 1.2f, 0.2f);
|
||||
//
|
||||
// -> data.Store(16, asuint(float3(2.3f, 1.2f, 0.2f)));
|
||||
|
||||
auto* str = create<ast::Struct>(
|
||||
ast::StructMemberList{Member("a", ty.vec3<i32>(), {MemberOffset(0)}),
|
||||
Member("b", ty.vec3<f32>(), {MemberOffset(16)})},
|
||||
ast::DecorationList{});
|
||||
auto* s = Structure("Data", {
|
||||
Member("a", ty.vec3<i32>()),
|
||||
Member("b", ty.vec3<f32>()),
|
||||
});
|
||||
|
||||
auto* s = ty.struct_("Data", str);
|
||||
auto* coord_var = Global("data", s, ast::StorageClass::kStorage);
|
||||
|
||||
auto* lhs = MemberAccessor("data", "b");
|
||||
|
@ -548,8 +514,8 @@ TEST_F(HlslGeneratorImplTest_MemberAccessor,
|
|||
TEST_F(HlslGeneratorImplTest_MemberAccessor,
|
||||
EmitExpression_MemberAccessor_StorageBuffer_Load_MultiLevel) {
|
||||
// struct Data {
|
||||
// [[offset(0)]] a : vec3<i32>;
|
||||
// [[offset(16)]] b : vec3<f32>;
|
||||
// a : vec3<i32>;
|
||||
// b : vec3<f32>;
|
||||
// };
|
||||
// struct Pre {
|
||||
// var c : [[stride(32)]] array<Data, 4>;
|
||||
|
@ -560,24 +526,18 @@ TEST_F(HlslGeneratorImplTest_MemberAccessor,
|
|||
//
|
||||
// -> asfloat(data.Load3(16 + (2 * 32)))
|
||||
|
||||
auto* data_str = create<ast::Struct>(
|
||||
ast::StructMemberList{
|
||||
Member("a", ty.vec3<i32>(), {MemberOffset(0)}),
|
||||
Member("b", ty.vec3<f32>(), {MemberOffset(16)}),
|
||||
},
|
||||
ast::DecorationList{});
|
||||
auto* data = Structure("Data", {
|
||||
Member("a", ty.vec3<i32>()),
|
||||
Member("b", ty.vec3<f32>()),
|
||||
});
|
||||
|
||||
auto* data = ty.struct_("Data", data_str);
|
||||
type::Array ary(data, 4,
|
||||
ast::DecorationList{
|
||||
create<ast::StrideDecoration>(32),
|
||||
});
|
||||
|
||||
auto* pre_str = create<ast::Struct>(
|
||||
ast::StructMemberList{Member("c", &ary, {MemberOffset(0)})},
|
||||
ast::DecorationList{});
|
||||
auto* pre_struct = Structure("Pre", {Member("c", &ary)});
|
||||
|
||||
auto* pre_struct = ty.struct_("Pre", pre_str);
|
||||
auto* coord_var = Global("data", pre_struct, ast::StorageClass::kStorage);
|
||||
|
||||
auto* expr =
|
||||
|
@ -595,8 +555,8 @@ TEST_F(HlslGeneratorImplTest_MemberAccessor,
|
|||
TEST_F(HlslGeneratorImplTest_MemberAccessor,
|
||||
EmitExpression_MemberAccessor_StorageBuffer_Load_MultiLevel_Swizzle) {
|
||||
// struct Data {
|
||||
// [[offset(0)]] a : vec3<i32>;
|
||||
// [[offset(16)]] b : vec3<f32>;
|
||||
// a : vec3<i32>;
|
||||
// b : vec3<f32>;
|
||||
// };
|
||||
// struct Pre {
|
||||
// var c : [[stride(32)]] array<Data, 4>;
|
||||
|
@ -607,22 +567,16 @@ TEST_F(HlslGeneratorImplTest_MemberAccessor,
|
|||
//
|
||||
// -> asfloat(data.Load3(16 + (2 * 32))).xy
|
||||
|
||||
auto* data_str = create<ast::Struct>(
|
||||
ast::StructMemberList{
|
||||
Member("a", ty.vec3<i32>(), {MemberOffset(0)}),
|
||||
Member("b", ty.vec3<f32>(), {MemberOffset(16)}),
|
||||
},
|
||||
ast::DecorationList{});
|
||||
auto* data = Structure("Data", {
|
||||
Member("a", ty.vec3<i32>()),
|
||||
Member("b", ty.vec3<f32>()),
|
||||
});
|
||||
|
||||
auto* data = ty.struct_("Data", data_str);
|
||||
type::Array ary(data, 4,
|
||||
ast::DecorationList{create<ast::StrideDecoration>(32)});
|
||||
|
||||
auto* pre_str = create<ast::Struct>(
|
||||
ast::StructMemberList{Member("c", &ary, {MemberOffset(0)})},
|
||||
ast::DecorationList{});
|
||||
auto* pre_struct = Structure("Pre", {Member("c", &ary)});
|
||||
|
||||
auto* pre_struct = ty.struct_("Pre", pre_str);
|
||||
auto* coord_var = Global("data", pre_struct, ast::StorageClass::kStorage);
|
||||
|
||||
auto* expr = MemberAccessor(
|
||||
|
@ -642,8 +596,8 @@ TEST_F(
|
|||
HlslGeneratorImplTest_MemberAccessor,
|
||||
EmitExpression_MemberAccessor_StorageBuffer_Load_MultiLevel_Swizzle_SingleLetter) { // NOLINT
|
||||
// struct Data {
|
||||
// [[offset(0)]] a : vec3<i32>;
|
||||
// [[offset(16)]] b : vec3<f32>;
|
||||
// a : vec3<i32>;
|
||||
// b : vec3<f32>;
|
||||
// };
|
||||
// struct Pre {
|
||||
// var c : [[stride(32)]] array<Data, 4>;
|
||||
|
@ -654,24 +608,18 @@ TEST_F(
|
|||
//
|
||||
// -> asfloat(data.Load((4 * 1) + 16 + (2 * 32) + 0))
|
||||
|
||||
auto* data_str = create<ast::Struct>(
|
||||
ast::StructMemberList{
|
||||
Member("a", ty.vec3<i32>(), {MemberOffset(0)}),
|
||||
Member("b", ty.vec3<f32>(), {MemberOffset(16)}),
|
||||
},
|
||||
ast::DecorationList{});
|
||||
auto* data = Structure("Data", {
|
||||
Member("a", ty.vec3<i32>()),
|
||||
Member("b", ty.vec3<f32>()),
|
||||
});
|
||||
|
||||
auto* data = ty.struct_("Data", data_str);
|
||||
type::Array ary(data, 4,
|
||||
ast::DecorationList{
|
||||
create<ast::StrideDecoration>(32),
|
||||
});
|
||||
|
||||
auto* pre_str = create<ast::Struct>(
|
||||
ast::StructMemberList{Member("c", &ary, {MemberOffset(0)})},
|
||||
ast::DecorationList{});
|
||||
auto* pre_struct = Structure("Pre", {Member("c", &ary)});
|
||||
|
||||
auto* pre_struct = ty.struct_("Pre", pre_str);
|
||||
auto* coord_var = Global("data", pre_struct, ast::StorageClass::kStorage);
|
||||
|
||||
auto* expr = MemberAccessor(
|
||||
|
@ -690,8 +638,8 @@ TEST_F(
|
|||
TEST_F(HlslGeneratorImplTest_MemberAccessor,
|
||||
EmitExpression_MemberAccessor_StorageBuffer_Load_MultiLevel_Index) {
|
||||
// struct Data {
|
||||
// [[offset(0)]] a : vec3<i32>;
|
||||
// [[offset(16)]] b : vec3<f32>;
|
||||
// a : vec3<i32>;
|
||||
// b : vec3<f32>;
|
||||
// };
|
||||
// struct Pre {
|
||||
// var c : [[stride(32)]] array<Data, 4>;
|
||||
|
@ -702,24 +650,18 @@ TEST_F(HlslGeneratorImplTest_MemberAccessor,
|
|||
//
|
||||
// -> asfloat(data.Load(4 + 16 + (2 * 32)))
|
||||
|
||||
auto* data_str = create<ast::Struct>(
|
||||
ast::StructMemberList{
|
||||
Member("a", ty.vec3<i32>(), {MemberOffset(0)}),
|
||||
Member("b", ty.vec3<f32>(), {MemberOffset(16)}),
|
||||
},
|
||||
ast::DecorationList{});
|
||||
auto* data = Structure("Data", {
|
||||
Member("a", ty.vec3<i32>()),
|
||||
Member("b", ty.vec3<f32>()),
|
||||
});
|
||||
|
||||
auto* data = ty.struct_("Data", data_str);
|
||||
type::Array ary(data, 4,
|
||||
ast::DecorationList{
|
||||
create<ast::StrideDecoration>(32),
|
||||
});
|
||||
|
||||
auto* pre_str = create<ast::Struct>(
|
||||
ast::StructMemberList{Member("c", &ary, {MemberOffset(0)})},
|
||||
ast::DecorationList{});
|
||||
auto* pre_struct = Structure("Pre", {Member("c", &ary)});
|
||||
|
||||
auto* pre_struct = ty.struct_("Pre", pre_str);
|
||||
auto* coord_var = Global("data", pre_struct, ast::StorageClass::kStorage);
|
||||
|
||||
auto* expr = IndexAccessor(
|
||||
|
@ -738,8 +680,8 @@ TEST_F(HlslGeneratorImplTest_MemberAccessor,
|
|||
TEST_F(HlslGeneratorImplTest_MemberAccessor,
|
||||
EmitExpression_MemberAccessor_StorageBuffer_Store_MultiLevel) {
|
||||
// struct Data {
|
||||
// [[offset(0)]] a : vec3<i32>;
|
||||
// [[offset(16)]] b : vec3<f32>;
|
||||
// a : vec3<i32>;
|
||||
// b : vec3<f32>;
|
||||
// };
|
||||
// struct Pre {
|
||||
// var c : [[stride(32)]] array<Data, 4>;
|
||||
|
@ -750,24 +692,18 @@ TEST_F(HlslGeneratorImplTest_MemberAccessor,
|
|||
//
|
||||
// -> data.Store3(16 + (2 * 32), asuint(float3(1.0f, 2.0f, 3.0f)));
|
||||
|
||||
auto* data_str = create<ast::Struct>(
|
||||
ast::StructMemberList{
|
||||
Member("a", ty.vec3<i32>(), {MemberOffset(0)}),
|
||||
Member("b", ty.vec3<f32>(), {MemberOffset(16)}),
|
||||
},
|
||||
ast::DecorationList{});
|
||||
auto* data = Structure("Data", {
|
||||
Member("a", ty.vec3<i32>()),
|
||||
Member("b", ty.vec3<f32>()),
|
||||
});
|
||||
|
||||
auto* data = ty.struct_("Data", data_str);
|
||||
type::Array ary(data, 4,
|
||||
ast::DecorationList{
|
||||
create<ast::StrideDecoration>(32),
|
||||
});
|
||||
|
||||
auto* pre_str = create<ast::Struct>(
|
||||
ast::StructMemberList{Member("c", &ary, {MemberOffset(0)})},
|
||||
ast::DecorationList{});
|
||||
auto* pre_struct = Structure("Pre", {Member("c", &ary)});
|
||||
|
||||
auto* pre_struct = ty.struct_("Pre", pre_str);
|
||||
auto* coord_var = Global("data", pre_struct, ast::StorageClass::kStorage);
|
||||
|
||||
auto* lhs =
|
||||
|
@ -791,8 +727,8 @@ TEST_F(HlslGeneratorImplTest_MemberAccessor,
|
|||
TEST_F(HlslGeneratorImplTest_MemberAccessor,
|
||||
EmitExpression_MemberAccessor_StorageBuffer_Store_Swizzle_SingleLetter) {
|
||||
// struct Data {
|
||||
// [[offset(0)]] a : vec3<i32>;
|
||||
// [[offset(16)]] b : vec3<f32>;
|
||||
// a : vec3<i32>;
|
||||
// b : vec3<f32>;
|
||||
// };
|
||||
// struct Pre {
|
||||
// var c : [[stride(32)]] array<Data, 4>;
|
||||
|
@ -803,24 +739,18 @@ TEST_F(HlslGeneratorImplTest_MemberAccessor,
|
|||
//
|
||||
// -> data.Store((4 * 1) + 16 + (2 * 32) + 0, asuint(1.0f));
|
||||
|
||||
auto* data_str = create<ast::Struct>(
|
||||
ast::StructMemberList{
|
||||
Member("a", ty.vec3<i32>(), {MemberOffset(0)}),
|
||||
Member("b", ty.vec3<f32>(), {MemberOffset(16)}),
|
||||
},
|
||||
ast::DecorationList{});
|
||||
auto* data = Structure("Data", {
|
||||
Member("a", ty.vec3<i32>()),
|
||||
Member("b", ty.vec3<f32>()),
|
||||
});
|
||||
|
||||
auto* data = ty.struct_("Data", data_str);
|
||||
type::Array ary(data, 4,
|
||||
ast::DecorationList{
|
||||
create<ast::StrideDecoration>(32),
|
||||
});
|
||||
|
||||
auto* pre_str = create<ast::Struct>(
|
||||
ast::StructMemberList{Member("c", &ary, {MemberOffset(0)})},
|
||||
ast::DecorationList{});
|
||||
auto* pre_struct = Structure("Pre", {Member("c", &ary)});
|
||||
|
||||
auto* pre_struct = ty.struct_("Pre", pre_str);
|
||||
auto* coord_var = Global("data", pre_struct, ast::StorageClass::kStorage);
|
||||
|
||||
auto* lhs = MemberAccessor(
|
||||
|
|
|
@ -167,12 +167,10 @@ TEST_F(HlslGeneratorImplTest_Type, DISABLED_EmitType_Pointer) {
|
|||
}
|
||||
|
||||
TEST_F(HlslGeneratorImplTest_Type, EmitType_StructDecl) {
|
||||
auto* str = create<ast::Struct>(
|
||||
ast::StructMemberList{Member("a", ty.i32()),
|
||||
Member("b", ty.f32(), {MemberOffset(4)})},
|
||||
ast::DecorationList{});
|
||||
|
||||
auto* s = ty.struct_("S", str);
|
||||
auto* s = Structure("S", {
|
||||
Member("a", ty.i32()),
|
||||
Member("b", ty.f32()),
|
||||
});
|
||||
|
||||
GeneratorImpl& gen = Build();
|
||||
|
||||
|
@ -185,12 +183,10 @@ TEST_F(HlslGeneratorImplTest_Type, EmitType_StructDecl) {
|
|||
}
|
||||
|
||||
TEST_F(HlslGeneratorImplTest_Type, EmitType_Struct) {
|
||||
auto* str = create<ast::Struct>(
|
||||
ast::StructMemberList{Member("a", ty.i32()),
|
||||
Member("b", ty.f32(), {MemberOffset(4)})},
|
||||
ast::DecorationList{});
|
||||
|
||||
auto* s = ty.struct_("S", str);
|
||||
auto* s = Structure("S", {
|
||||
Member("a", ty.i32()),
|
||||
Member("b", ty.f32()),
|
||||
});
|
||||
|
||||
GeneratorImpl& gen = Build();
|
||||
|
||||
|
@ -198,35 +194,35 @@ TEST_F(HlslGeneratorImplTest_Type, EmitType_Struct) {
|
|||
EXPECT_EQ(result(), "S");
|
||||
}
|
||||
|
||||
/// TODO(bclayton): Enable this, fix it, add tests for vector, matrix, array and
|
||||
/// nested structures.
|
||||
TEST_F(HlslGeneratorImplTest_Type, DISABLED_EmitType_Struct_InjectPadding) {
|
||||
auto* str = create<ast::Struct>(
|
||||
ast::StructMemberList{Member("a", ty.i32(), {MemberOffset(4)}),
|
||||
Member("b", ty.f32(), {MemberOffset(32)}),
|
||||
Member("c", ty.f32(), {MemberOffset(128)})},
|
||||
ast::DecorationList{});
|
||||
|
||||
auto* s = ty.struct_("S", str);
|
||||
auto* s = Structure(
|
||||
"S", {
|
||||
Member("a", ty.i32(), {MemberSize(32)}),
|
||||
Member("b", ty.f32()),
|
||||
Member("c", ty.f32(), {MemberAlign(128), MemberSize(128)}),
|
||||
});
|
||||
|
||||
GeneratorImpl& gen = Build();
|
||||
|
||||
ASSERT_TRUE(gen.EmitType(out, s, "")) << gen.error();
|
||||
EXPECT_EQ(result(), R"(struct {
|
||||
int8_t pad_0[4];
|
||||
EXPECT_EQ(gen.result(), R"(struct S {
|
||||
int a;
|
||||
int8_t pad_1[24];
|
||||
int8_t pad_0[28];
|
||||
float b;
|
||||
int8_t pad_2[92];
|
||||
int8_t pad_1[92];
|
||||
float c;
|
||||
})");
|
||||
int8_t pad_2[124];
|
||||
};
|
||||
)");
|
||||
}
|
||||
|
||||
TEST_F(HlslGeneratorImplTest_Type, EmitType_Struct_NameCollision) {
|
||||
auto* str =
|
||||
create<ast::Struct>(ast::StructMemberList{Member("double", ty.i32()),
|
||||
Member("float", ty.f32())},
|
||||
ast::DecorationList{});
|
||||
|
||||
auto* s = ty.struct_("S", str);
|
||||
auto* s = Structure("S", {
|
||||
Member("double", ty.i32()),
|
||||
Member("float", ty.f32()),
|
||||
});
|
||||
|
||||
GeneratorImpl& gen = Build();
|
||||
|
||||
|
@ -240,15 +236,12 @@ TEST_F(HlslGeneratorImplTest_Type, EmitType_Struct_NameCollision) {
|
|||
|
||||
// TODO(dsinclair): How to translate [[block]]
|
||||
TEST_F(HlslGeneratorImplTest_Type, DISABLED_EmitType_Struct_WithDecoration) {
|
||||
ast::DecorationList decos;
|
||||
decos.push_back(create<ast::StructBlockDecoration>());
|
||||
|
||||
auto* str = create<ast::Struct>(
|
||||
ast::StructMemberList{Member("a", ty.i32()),
|
||||
Member("b", ty.f32(), {MemberOffset(4)})},
|
||||
decos);
|
||||
|
||||
auto* s = ty.struct_("S", str);
|
||||
auto* s = Structure("S",
|
||||
{
|
||||
Member("a", ty.i32()),
|
||||
Member("b", ty.f32()),
|
||||
},
|
||||
{create<ast::StructBlockDecoration>()});
|
||||
|
||||
GeneratorImpl& gen = Build();
|
||||
|
||||
|
|
|
@ -25,12 +25,12 @@
|
|||
#include "src/ast/float_literal.h"
|
||||
#include "src/ast/module.h"
|
||||
#include "src/ast/sint_literal.h"
|
||||
#include "src/ast/struct_member_offset_decoration.h"
|
||||
#include "src/ast/uint_literal.h"
|
||||
#include "src/ast/variable_decl_statement.h"
|
||||
#include "src/semantic/call.h"
|
||||
#include "src/semantic/function.h"
|
||||
#include "src/semantic/member_accessor_expression.h"
|
||||
#include "src/semantic/struct.h"
|
||||
#include "src/semantic/variable.h"
|
||||
#include "src/type/access_control_type.h"
|
||||
#include "src/type/alias_type.h"
|
||||
|
@ -68,14 +68,6 @@ bool last_is_break_or_fallthrough(const ast::BlockStatement* stmts) {
|
|||
stmts->last()->Is<ast::FallthroughStatement>();
|
||||
}
|
||||
|
||||
uint32_t adjust_for_alignment(uint32_t count, uint32_t alignment) {
|
||||
const auto spill = count % alignment;
|
||||
if (spill == 0) {
|
||||
return count;
|
||||
}
|
||||
return count + alignment - spill;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
GeneratorImpl::GeneratorImpl(const Program* program)
|
||||
|
@ -140,89 +132,6 @@ bool GeneratorImpl::Generate() {
|
|||
return true;
|
||||
}
|
||||
|
||||
uint32_t GeneratorImpl::calculate_largest_alignment(type::Struct* type) {
|
||||
auto* stct = type->As<type::Struct>()->impl();
|
||||
uint32_t largest_alignment = 0;
|
||||
for (auto* mem : stct->members()) {
|
||||
auto align = calculate_alignment_size(mem->type());
|
||||
if (align == 0) {
|
||||
return 0;
|
||||
}
|
||||
if (!mem->type()->Is<type::Struct>()) {
|
||||
largest_alignment = std::max(largest_alignment, align);
|
||||
} else {
|
||||
largest_alignment = std::max(
|
||||
largest_alignment,
|
||||
calculate_largest_alignment(mem->type()->As<type::Struct>()));
|
||||
}
|
||||
}
|
||||
return largest_alignment;
|
||||
}
|
||||
|
||||
uint32_t GeneratorImpl::calculate_alignment_size(type::Type* type) {
|
||||
if (auto* alias = type->As<type::Alias>()) {
|
||||
return calculate_alignment_size(alias->type());
|
||||
}
|
||||
if (auto* ary = type->As<type::Array>()) {
|
||||
// TODO(dsinclair): Handle array stride and adjust for alignment.
|
||||
uint32_t type_size = calculate_alignment_size(ary->type());
|
||||
return ary->size() * type_size;
|
||||
}
|
||||
if (type->Is<type::Bool>()) {
|
||||
return 1;
|
||||
}
|
||||
if (type->Is<type::Pointer>()) {
|
||||
return 0;
|
||||
}
|
||||
if (type->Is<type::F32>() || type->Is<type::I32>() || type->Is<type::U32>()) {
|
||||
return 4;
|
||||
}
|
||||
if (auto* mat = type->As<type::Matrix>()) {
|
||||
// TODO(dsinclair): Handle MatrixStride
|
||||
// https://github.com/gpuweb/gpuweb/issues/773
|
||||
uint32_t type_size = calculate_alignment_size(mat->type());
|
||||
return mat->rows() * mat->columns() * type_size;
|
||||
}
|
||||
if (auto* stct_ty = type->As<type::Struct>()) {
|
||||
auto* stct = stct_ty->impl();
|
||||
uint32_t count = 0;
|
||||
uint32_t largest_alignment = 0;
|
||||
// Offset decorations in WGSL must be in increasing order.
|
||||
for (auto* mem : stct->members()) {
|
||||
for (auto* deco : mem->decorations()) {
|
||||
if (auto* offset = deco->As<ast::StructMemberOffsetDecoration>()) {
|
||||
count = offset->offset();
|
||||
}
|
||||
}
|
||||
auto align = calculate_alignment_size(mem->type());
|
||||
if (align == 0) {
|
||||
return 0;
|
||||
}
|
||||
if (auto* str = mem->type()->As<type::Struct>()) {
|
||||
largest_alignment =
|
||||
std::max(largest_alignment, calculate_largest_alignment(str));
|
||||
} else {
|
||||
largest_alignment = std::max(largest_alignment, align);
|
||||
}
|
||||
|
||||
// Round up to the alignment size
|
||||
count = adjust_for_alignment(count, align);
|
||||
count += align;
|
||||
}
|
||||
// Round struct up to largest align size
|
||||
count = adjust_for_alignment(count, largest_alignment);
|
||||
return count;
|
||||
}
|
||||
if (auto* vec = type->As<type::Vector>()) {
|
||||
uint32_t type_size = calculate_alignment_size(vec->type());
|
||||
if (vec->size() == 2) {
|
||||
return 2 * type_size;
|
||||
}
|
||||
return 4 * type_size;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool GeneratorImpl::EmitConstructedType(const type::Type* ty) {
|
||||
make_indent();
|
||||
|
||||
|
@ -2075,52 +1984,76 @@ bool GeneratorImpl::EmitStructType(const type::Struct* str) {
|
|||
out_ << "struct " << program_->Symbols().NameFor(str->symbol()) << " {"
|
||||
<< std::endl;
|
||||
|
||||
uint32_t pad_count = 0;
|
||||
auto add_padding = [&](uint32_t size) {
|
||||
out_ << "int8_t pad_" << pad_count << "[" << size << "];" << std::endl;
|
||||
pad_count++;
|
||||
};
|
||||
|
||||
increment_indent();
|
||||
uint32_t current_offset = 0;
|
||||
uint32_t pad_count = 0;
|
||||
for (auto* mem : str->impl()->members()) {
|
||||
std::string attributes;
|
||||
|
||||
make_indent();
|
||||
|
||||
auto* sem_mem = program_->Sem().Get(mem);
|
||||
if (!sem_mem) {
|
||||
TINT_ICE(diagnostics_) << "struct member missing semantic info";
|
||||
return false;
|
||||
}
|
||||
|
||||
auto const offset = sem_mem->Offset();
|
||||
if (offset != current_offset) {
|
||||
add_padding(offset - current_offset);
|
||||
make_indent();
|
||||
}
|
||||
|
||||
for (auto* deco : mem->decorations()) {
|
||||
if (auto* o = deco->As<ast::StructMemberOffsetDecoration>()) {
|
||||
uint32_t offset = o->offset();
|
||||
if (offset != current_offset) {
|
||||
out_ << "int8_t pad_" << pad_count << "[" << (offset - current_offset)
|
||||
<< "];" << std::endl;
|
||||
pad_count++;
|
||||
make_indent();
|
||||
}
|
||||
current_offset = offset;
|
||||
} else if (auto* loc = deco->As<ast::LocationDecoration>()) {
|
||||
if (auto* loc = deco->As<ast::LocationDecoration>()) {
|
||||
attributes = " [[user(locn" + std::to_string(loc->value()) + ")]]";
|
||||
} else {
|
||||
diagnostics_.add_error("unsupported member decoration: " +
|
||||
program_->str(deco));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!EmitType(mem->type(), program_->Symbols().NameFor(mem->symbol()))) {
|
||||
return false;
|
||||
}
|
||||
auto size = calculate_alignment_size(mem->type());
|
||||
if (size == 0) {
|
||||
diagnostics_.add_error("unable to calculate byte size for: " +
|
||||
mem->type()->type_name());
|
||||
return false;
|
||||
}
|
||||
current_offset += size;
|
||||
|
||||
auto* ty = mem->type()->UnwrapAliasIfNeeded();
|
||||
|
||||
// Array member name will be output with the type
|
||||
if (!mem->type()->Is<type::Array>()) {
|
||||
if (!ty->Is<type::Array>()) {
|
||||
out_ << " " << program_->Symbols().NameFor(mem->symbol());
|
||||
}
|
||||
|
||||
out_ << attributes;
|
||||
|
||||
out_ << ";" << std::endl;
|
||||
|
||||
if (ty->is_scalar()) {
|
||||
current_offset = offset + 4;
|
||||
} else if (ty->Is<type::Struct>()) {
|
||||
/// Structure will already contain padding matching the WGSL size
|
||||
current_offset = offset + sem_mem->Size();
|
||||
} else {
|
||||
/// TODO(bclayton): Implement for vector, matrix, array and nested
|
||||
/// structures.
|
||||
TINT_UNREACHABLE(diagnostics_)
|
||||
<< "Unhandled type " << ty->TypeInfo().name;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
auto* sem_str = program_->Sem().Get(str);
|
||||
if (!sem_str) {
|
||||
TINT_ICE(diagnostics_) << "struct missing semantic info";
|
||||
return false;
|
||||
}
|
||||
if (sem_str->Size() != current_offset) {
|
||||
make_indent();
|
||||
add_padding(sem_str->Size() - current_offset);
|
||||
}
|
||||
|
||||
decrement_indent();
|
||||
make_indent();
|
||||
|
||||
|
|
|
@ -60,16 +60,6 @@ class GeneratorImpl : public TextGenerator {
|
|||
/// @returns true on successful generation; false otherwise
|
||||
bool Generate();
|
||||
|
||||
/// Calculates the alignment size of the given `type`. This returns 0
|
||||
/// for pointers as the size is unknown.
|
||||
/// @param type the type to calculate the alignment size for
|
||||
/// @returns the number of bytes used to align `type` or 0 on error
|
||||
uint32_t calculate_alignment_size(type::Type* type);
|
||||
/// Calculates the largest alignment seen within a struct
|
||||
/// @param type the struct to calculate
|
||||
/// @returns the largest alignment value
|
||||
uint32_t calculate_largest_alignment(type::Struct* type);
|
||||
|
||||
/// Handles generating a constructed
|
||||
/// @param ty the constructed type to generate
|
||||
/// @returns true if the constructed type was emitted
|
||||
|
|
|
@ -32,12 +32,10 @@ TEST_F(MslGeneratorImplTest, EmitConstructedType_F32) {
|
|||
}
|
||||
|
||||
TEST_F(MslGeneratorImplTest, EmitConstructedType_Struct) {
|
||||
auto* str = create<ast::Struct>(
|
||||
ast::StructMemberList{Member("a", ty.f32()),
|
||||
Member("b", ty.i32(), {MemberOffset(4)})},
|
||||
ast::DecorationList{});
|
||||
|
||||
auto* s = ty.struct_("a", str);
|
||||
auto* s = Structure("a", {
|
||||
Member("a", ty.f32()),
|
||||
Member("b", ty.i32()),
|
||||
});
|
||||
|
||||
GeneratorImpl& gen = Build();
|
||||
|
||||
|
@ -50,12 +48,11 @@ TEST_F(MslGeneratorImplTest, EmitConstructedType_Struct) {
|
|||
}
|
||||
|
||||
TEST_F(MslGeneratorImplTest, EmitConstructedType_AliasStructIdent) {
|
||||
auto* str = create<ast::Struct>(
|
||||
ast::StructMemberList{Member("a", ty.f32()),
|
||||
Member("b", ty.i32(), {MemberOffset(4)})},
|
||||
ast::DecorationList{});
|
||||
auto* s = Structure("b", {
|
||||
Member("a", ty.f32()),
|
||||
Member("b", ty.i32()),
|
||||
});
|
||||
|
||||
auto* s = ty.struct_("b", str);
|
||||
auto* alias = ty.alias("a", s);
|
||||
|
||||
GeneratorImpl& gen = Build();
|
||||
|
|
|
@ -243,17 +243,15 @@ fragment void frag_main(constant float4& coord [[buffer(0)]]) {
|
|||
)");
|
||||
}
|
||||
|
||||
TEST_F(MslGeneratorImplTest, Emit_Decoration_EntryPoint_With_RW_StorageBuffer) {
|
||||
auto* str = create<ast::Struct>(
|
||||
ast::StructMemberList{Member("a", ty.i32(), {MemberOffset(0)}),
|
||||
Member("b", ty.f32(), {MemberOffset(4)})},
|
||||
ast::DecorationList{});
|
||||
TEST_F(MslGeneratorImplTest,
|
||||
Emit_FunctionDecoration_EntryPoint_With_RW_StorageBuffer) {
|
||||
auto* s = Structure("Data", {
|
||||
Member("a", ty.i32()),
|
||||
Member("b", ty.f32()),
|
||||
});
|
||||
|
||||
auto* s = ty.struct_("Data", str);
|
||||
type::AccessControl ac(ast::AccessControl::kReadWrite, s);
|
||||
|
||||
AST().AddConstructedType(s);
|
||||
|
||||
Global("coord", &ac, ast::StorageClass::kStorage, nullptr,
|
||||
ast::DecorationList{create<ast::BindingDecoration>(0),
|
||||
create<ast::GroupDecoration>(1)});
|
||||
|
@ -289,15 +287,14 @@ fragment void frag_main(device Data& coord [[buffer(0)]]) {
|
|||
)");
|
||||
}
|
||||
|
||||
TEST_F(MslGeneratorImplTest, Emit_Decoration_EntryPoint_With_RO_StorageBuffer) {
|
||||
auto* str = create<ast::Struct>(
|
||||
ast::StructMemberList{Member("a", ty.i32(), {MemberOffset(0)}),
|
||||
Member("b", ty.f32(), {MemberOffset(4)})},
|
||||
ast::DecorationList{});
|
||||
TEST_F(MslGeneratorImplTest,
|
||||
Emit_FunctionDecoration_EntryPoint_With_RO_StorageBuffer) {
|
||||
auto* s = Structure("Data", {
|
||||
Member("a", ty.i32()),
|
||||
Member("b", ty.f32()),
|
||||
});
|
||||
|
||||
auto* s = ty.struct_("Data", str);
|
||||
type::AccessControl ac(ast::AccessControl::kReadOnly, s);
|
||||
AST().AddConstructedType(s);
|
||||
|
||||
Global("coord", &ac, ast::StorageClass::kStorage, nullptr,
|
||||
ast::DecorationList{create<ast::BindingDecoration>(0),
|
||||
|
@ -555,15 +552,13 @@ fragment void frag_main(constant float4& coord [[buffer(0)]]) {
|
|||
}
|
||||
|
||||
TEST_F(MslGeneratorImplTest,
|
||||
Emit_Decoration_Called_By_EntryPoint_With_RW_StorageBuffer) {
|
||||
auto* str = create<ast::Struct>(
|
||||
ast::StructMemberList{Member("a", ty.i32(), {MemberOffset(0)}),
|
||||
Member("b", ty.f32(), {MemberOffset(4)})},
|
||||
ast::DecorationList{});
|
||||
Emit_FunctionDecoration_Called_By_EntryPoint_With_RW_StorageBuffer) {
|
||||
auto* s = Structure("Data", {
|
||||
Member("a", ty.i32()),
|
||||
Member("b", ty.f32()),
|
||||
});
|
||||
|
||||
auto* s = ty.struct_("Data", str);
|
||||
type::AccessControl ac(ast::AccessControl::kReadWrite, s);
|
||||
AST().AddConstructedType(s);
|
||||
|
||||
Global("coord", &ac, ast::StorageClass::kStorage, nullptr,
|
||||
ast::DecorationList{create<ast::BindingDecoration>(0),
|
||||
|
@ -613,15 +608,13 @@ fragment void frag_main(device Data& coord [[buffer(0)]]) {
|
|||
}
|
||||
|
||||
TEST_F(MslGeneratorImplTest,
|
||||
Emit_Decoration_Called_By_EntryPoint_With_RO_StorageBuffer) {
|
||||
auto* str = create<ast::Struct>(
|
||||
ast::StructMemberList{Member("a", ty.i32(), {MemberOffset(0)}),
|
||||
Member("b", ty.f32(), {MemberOffset(4)})},
|
||||
ast::DecorationList{});
|
||||
Emit_FunctionDecoration_Called_By_EntryPoint_With_RO_StorageBuffer) {
|
||||
auto* s = Structure("Data", {
|
||||
Member("a", ty.i32()),
|
||||
Member("b", ty.f32()),
|
||||
});
|
||||
|
||||
auto* s = ty.struct_("Data", str);
|
||||
type::AccessControl ac(ast::AccessControl::kReadOnly, s);
|
||||
AST().AddConstructedType(s);
|
||||
|
||||
Global("coord", &ac, ast::StorageClass::kStorage, nullptr,
|
||||
ast::DecorationList{create<ast::BindingDecoration>(0),
|
||||
|
@ -746,7 +739,7 @@ using namespace metal;
|
|||
TEST_F(MslGeneratorImplTest,
|
||||
Emit_Function_Multiple_EntryPoint_With_Same_ModuleVar) {
|
||||
// [[block]] struct Data {
|
||||
// [[offset(0)]] d : f32;
|
||||
// d : f32;
|
||||
// };
|
||||
// [[binding(0), group(0)]] var<storage> data : Data;
|
||||
//
|
||||
|
@ -760,21 +753,15 @@ TEST_F(MslGeneratorImplTest,
|
|||
// return;
|
||||
// }
|
||||
|
||||
ast::DecorationList s_decos;
|
||||
s_decos.push_back(create<ast::StructBlockDecoration>());
|
||||
auto* s = Structure("Data", {Member("d", ty.f32())},
|
||||
{create<ast::StructBlockDecoration>()});
|
||||
|
||||
auto* str = create<ast::Struct>(
|
||||
ast::StructMemberList{Member("d", ty.f32(), {MemberOffset(0)})}, s_decos);
|
||||
|
||||
auto* s = ty.struct_("Data", str);
|
||||
type::AccessControl ac(ast::AccessControl::kReadWrite, s);
|
||||
|
||||
Global("data", &ac, ast::StorageClass::kStorage, nullptr,
|
||||
ast::DecorationList{create<ast::BindingDecoration>(0),
|
||||
create<ast::GroupDecoration>(0)});
|
||||
|
||||
AST().AddConstructedType(s);
|
||||
|
||||
{
|
||||
auto* var = Var("v", ty.f32(), ast::StorageClass::kFunction,
|
||||
MemberAccessor("data", "d"));
|
||||
|
|
|
@ -78,178 +78,6 @@ INSTANTIATE_TEST_SUITE_P(
|
|||
MslBuiltinData{ast::Builtin::kSampleMaskOut,
|
||||
"sample_mask"}));
|
||||
|
||||
TEST_F(MslGeneratorImplTest, calculate_alignment_size_alias) {
|
||||
auto* alias = ty.alias("a", ty.f32());
|
||||
|
||||
GeneratorImpl& gen = Build();
|
||||
|
||||
EXPECT_EQ(4u, gen.calculate_alignment_size(alias));
|
||||
}
|
||||
|
||||
TEST_F(MslGeneratorImplTest, calculate_alignment_size_array) {
|
||||
auto* array = ty.array<f32, 4>();
|
||||
|
||||
GeneratorImpl& gen = Build();
|
||||
|
||||
EXPECT_EQ(4u * 4u, gen.calculate_alignment_size(array));
|
||||
}
|
||||
|
||||
TEST_F(MslGeneratorImplTest, calculate_alignment_size_bool) {
|
||||
auto* bool_ = ty.bool_();
|
||||
|
||||
GeneratorImpl& gen = Build();
|
||||
|
||||
EXPECT_EQ(1u, gen.calculate_alignment_size(bool_));
|
||||
}
|
||||
|
||||
TEST_F(MslGeneratorImplTest, calculate_alignment_size_f32) {
|
||||
auto* f32 = ty.f32();
|
||||
|
||||
GeneratorImpl& gen = Build();
|
||||
|
||||
EXPECT_EQ(4u, gen.calculate_alignment_size(f32));
|
||||
}
|
||||
|
||||
TEST_F(MslGeneratorImplTest, calculate_alignment_size_i32) {
|
||||
auto* i32 = ty.i32();
|
||||
|
||||
GeneratorImpl& gen = Build();
|
||||
|
||||
EXPECT_EQ(4u, gen.calculate_alignment_size(i32));
|
||||
}
|
||||
|
||||
TEST_F(MslGeneratorImplTest, calculate_alignment_size_matrix) {
|
||||
auto* mat3x2 = ty.mat3x2<f32>();
|
||||
|
||||
GeneratorImpl& gen = Build();
|
||||
|
||||
EXPECT_EQ(4u * 3u * 2u, gen.calculate_alignment_size(mat3x2));
|
||||
}
|
||||
|
||||
TEST_F(MslGeneratorImplTest, calculate_alignment_size_pointer) {
|
||||
type::Pointer ptr(ty.bool_(), ast::StorageClass::kPrivate);
|
||||
|
||||
GeneratorImpl& gen = Build();
|
||||
|
||||
EXPECT_EQ(0u, gen.calculate_alignment_size(&ptr));
|
||||
}
|
||||
|
||||
TEST_F(MslGeneratorImplTest, calculate_alignment_size_struct) {
|
||||
auto* str = create<ast::Struct>(
|
||||
ast::StructMemberList{Member("a", ty.i32(), {MemberOffset(4)}),
|
||||
Member("b", ty.f32(), {MemberOffset(32)}),
|
||||
Member("c", ty.f32(), {MemberOffset(128)})},
|
||||
ast::DecorationList{});
|
||||
|
||||
auto* s = ty.struct_("S", str);
|
||||
|
||||
GeneratorImpl& gen = Build();
|
||||
|
||||
EXPECT_EQ(132u, gen.calculate_alignment_size(s));
|
||||
}
|
||||
|
||||
TEST_F(MslGeneratorImplTest, calculate_alignment_size_struct_of_struct) {
|
||||
auto* inner_str = create<ast::Struct>(
|
||||
ast::StructMemberList{Member("a", ty.i32(), {MemberOffset(0)}),
|
||||
Member("b", ty.vec3<f32>(), {MemberOffset(16)}),
|
||||
Member("c", ty.f32(), {MemberOffset(32)})},
|
||||
ast::DecorationList{});
|
||||
|
||||
auto* inner_s = ty.struct_("Inner", inner_str);
|
||||
|
||||
auto* outer_str = create<ast::Struct>(
|
||||
ast::StructMemberList{Member("d", ty.f32(), {MemberOffset(0)}),
|
||||
Member("e", inner_s, {MemberOffset(32)}),
|
||||
Member("f", ty.f32(), {MemberOffset(64)})},
|
||||
ast::DecorationList{});
|
||||
|
||||
auto* outer_s = ty.struct_("Outer", outer_str);
|
||||
|
||||
GeneratorImpl& gen = Build();
|
||||
|
||||
EXPECT_EQ(80u, gen.calculate_alignment_size(outer_s));
|
||||
}
|
||||
|
||||
TEST_F(MslGeneratorImplTest, calculate_alignment_size_u32) {
|
||||
auto* u32 = ty.u32();
|
||||
|
||||
GeneratorImpl& gen = Build();
|
||||
|
||||
EXPECT_EQ(4u, gen.calculate_alignment_size(u32));
|
||||
}
|
||||
|
||||
struct MslVectorSizeData {
|
||||
uint32_t elements;
|
||||
uint32_t byte_size;
|
||||
};
|
||||
inline std::ostream& operator<<(std::ostream& out, MslVectorSizeData data) {
|
||||
out << data.elements;
|
||||
return out;
|
||||
}
|
||||
using MslVectorSizeBoolTest = TestParamHelper<MslVectorSizeData>;
|
||||
TEST_P(MslVectorSizeBoolTest, calculate) {
|
||||
auto param = GetParam();
|
||||
|
||||
type::Vector vec(ty.bool_(), param.elements);
|
||||
|
||||
GeneratorImpl& gen = Build();
|
||||
|
||||
EXPECT_EQ(param.byte_size, gen.calculate_alignment_size(&vec));
|
||||
}
|
||||
INSTANTIATE_TEST_SUITE_P(MslGeneratorImplTest,
|
||||
MslVectorSizeBoolTest,
|
||||
testing::Values(MslVectorSizeData{2u, 2u},
|
||||
MslVectorSizeData{3u, 4u},
|
||||
MslVectorSizeData{4u, 4u}));
|
||||
|
||||
using MslVectorSizeI32Test = TestParamHelper<MslVectorSizeData>;
|
||||
TEST_P(MslVectorSizeI32Test, calculate) {
|
||||
auto param = GetParam();
|
||||
|
||||
type::Vector vec(ty.i32(), param.elements);
|
||||
|
||||
GeneratorImpl& gen = Build();
|
||||
|
||||
EXPECT_EQ(param.byte_size, gen.calculate_alignment_size(&vec));
|
||||
}
|
||||
INSTANTIATE_TEST_SUITE_P(MslGeneratorImplTest,
|
||||
MslVectorSizeI32Test,
|
||||
testing::Values(MslVectorSizeData{2u, 8u},
|
||||
MslVectorSizeData{3u, 16u},
|
||||
MslVectorSizeData{4u, 16u}));
|
||||
|
||||
using MslVectorSizeU32Test = TestParamHelper<MslVectorSizeData>;
|
||||
TEST_P(MslVectorSizeU32Test, calculate) {
|
||||
auto param = GetParam();
|
||||
|
||||
type::Vector vec(ty.u32(), param.elements);
|
||||
|
||||
GeneratorImpl& gen = Build();
|
||||
|
||||
EXPECT_EQ(param.byte_size, gen.calculate_alignment_size(&vec));
|
||||
}
|
||||
INSTANTIATE_TEST_SUITE_P(MslGeneratorImplTest,
|
||||
MslVectorSizeU32Test,
|
||||
testing::Values(MslVectorSizeData{2u, 8u},
|
||||
MslVectorSizeData{3u, 16u},
|
||||
MslVectorSizeData{4u, 16u}));
|
||||
|
||||
using MslVectorSizeF32Test = TestParamHelper<MslVectorSizeData>;
|
||||
TEST_P(MslVectorSizeF32Test, calculate) {
|
||||
auto param = GetParam();
|
||||
|
||||
type::Vector vec(ty.f32(), param.elements);
|
||||
|
||||
GeneratorImpl& gen = Build();
|
||||
|
||||
EXPECT_EQ(param.byte_size, gen.calculate_alignment_size(&vec));
|
||||
}
|
||||
INSTANTIATE_TEST_SUITE_P(MslGeneratorImplTest,
|
||||
MslVectorSizeF32Test,
|
||||
testing::Values(MslVectorSizeData{2u, 8u},
|
||||
MslVectorSizeData{3u, 16u},
|
||||
MslVectorSizeData{4u, 16u}));
|
||||
|
||||
} // namespace
|
||||
} // namespace msl
|
||||
} // namespace writer
|
||||
|
|
|
@ -143,12 +143,10 @@ TEST_F(MslGeneratorImplTest, DISABLED_EmitType_Pointer) {
|
|||
}
|
||||
|
||||
TEST_F(MslGeneratorImplTest, EmitType_Struct) {
|
||||
auto* str = create<ast::Struct>(
|
||||
ast::StructMemberList{Member("a", ty.i32()),
|
||||
Member("b", ty.f32(), {MemberOffset(4)})},
|
||||
ast::DecorationList{});
|
||||
|
||||
auto* s = ty.struct_("S", str);
|
||||
auto* s = Structure("S", {
|
||||
Member("a", ty.i32()),
|
||||
Member("b", ty.f32()),
|
||||
});
|
||||
|
||||
GeneratorImpl& gen = Build();
|
||||
|
||||
|
@ -157,12 +155,10 @@ TEST_F(MslGeneratorImplTest, EmitType_Struct) {
|
|||
}
|
||||
|
||||
TEST_F(MslGeneratorImplTest, EmitType_StructDecl) {
|
||||
auto* str = create<ast::Struct>(
|
||||
ast::StructMemberList{Member("a", ty.i32()),
|
||||
Member("b", ty.f32(), {MemberOffset(4)})},
|
||||
ast::DecorationList{});
|
||||
|
||||
auto* s = ty.struct_("S", str);
|
||||
auto* s = Structure("S", {
|
||||
Member("a", ty.i32()),
|
||||
Member("b", ty.f32()),
|
||||
});
|
||||
|
||||
GeneratorImpl& gen = Build();
|
||||
|
||||
|
@ -174,41 +170,37 @@ TEST_F(MslGeneratorImplTest, EmitType_StructDecl) {
|
|||
)");
|
||||
}
|
||||
|
||||
/// TODO(bclayton): Add tests for vector, matrix, array and nested structures.
|
||||
TEST_F(MslGeneratorImplTest, EmitType_Struct_InjectPadding) {
|
||||
auto* str = create<ast::Struct>(
|
||||
ast::StructMemberList{
|
||||
Member("a", ty.i32(), {MemberOffset(4)}),
|
||||
Member("b", ty.f32(), {MemberOffset(32)}),
|
||||
Member("c", ty.f32(), {MemberOffset(128)}),
|
||||
},
|
||||
ast::DecorationList{});
|
||||
|
||||
auto* s = ty.struct_("S", str);
|
||||
auto* s = Structure(
|
||||
"S", {
|
||||
Member("a", ty.i32(), {MemberSize(32)}),
|
||||
Member("b", ty.f32()),
|
||||
Member("c", ty.f32(), {MemberAlign(128), MemberSize(128)}),
|
||||
});
|
||||
|
||||
GeneratorImpl& gen = Build();
|
||||
|
||||
ASSERT_TRUE(gen.EmitStructType(s)) << gen.error();
|
||||
EXPECT_EQ(gen.result(), R"(struct S {
|
||||
int8_t pad_0[4];
|
||||
int a;
|
||||
int8_t pad_1[24];
|
||||
int8_t pad_0[28];
|
||||
float b;
|
||||
int8_t pad_2[92];
|
||||
int8_t pad_1[92];
|
||||
float c;
|
||||
int8_t pad_2[124];
|
||||
};
|
||||
)");
|
||||
}
|
||||
|
||||
// TODO(dsinclair): How to translate [[block]]
|
||||
TEST_F(MslGeneratorImplTest, DISABLED_EmitType_Struct_WithDecoration) {
|
||||
ast::DecorationList decos;
|
||||
decos.push_back(create<ast::StructBlockDecoration>());
|
||||
auto* str = create<ast::Struct>(
|
||||
ast::StructMemberList{Member("a", ty.i32()),
|
||||
Member("b", ty.f32(), {MemberOffset(4)})},
|
||||
decos);
|
||||
|
||||
auto* s = ty.struct_("S", str);
|
||||
auto* s = Structure("S",
|
||||
{
|
||||
Member("a", ty.i32()),
|
||||
Member("b", ty.f32()),
|
||||
},
|
||||
{create<ast::StructBlockDecoration>()});
|
||||
|
||||
GeneratorImpl& gen = Build();
|
||||
|
||||
|
|
|
@ -64,12 +64,11 @@ TEST_F(MslGeneratorImplTest, Emit_VariableDeclStatement_Array) {
|
|||
}
|
||||
|
||||
TEST_F(MslGeneratorImplTest, Emit_VariableDeclStatement_Struct) {
|
||||
auto* str = create<ast::Struct>(
|
||||
ast::StructMemberList{Member("a", ty.f32()),
|
||||
Member("b", ty.f32(), {MemberOffset(4)})},
|
||||
ast::DecorationList{});
|
||||
auto* s = Structure("S", {
|
||||
Member("a", ty.f32()),
|
||||
Member("b", ty.f32()),
|
||||
});
|
||||
|
||||
auto* s = ty.struct_("S", str);
|
||||
auto* var = Var("a", s, ast::StorageClass::kNone);
|
||||
auto* stmt = create<ast::VariableDeclStatement>(var);
|
||||
WrapInFunction(stmt);
|
||||
|
|
|
@ -22,8 +22,11 @@
|
|||
#include "src/ast/constant_id_decoration.h"
|
||||
#include "src/ast/fallthrough_statement.h"
|
||||
#include "src/ast/null_literal.h"
|
||||
#include "src/semantic/array.h"
|
||||
#include "src/semantic/call.h"
|
||||
#include "src/semantic/function.h"
|
||||
#include "src/semantic/intrinsic.h"
|
||||
#include "src/semantic/struct.h"
|
||||
#include "src/semantic/variable.h"
|
||||
#include "src/type/depth_texture_type.h"
|
||||
#include "src/type/multisampled_texture_type.h"
|
||||
|
@ -2961,11 +2964,14 @@ bool Builder::GenerateArrayType(type::Array* ary, const Operand& result) {
|
|||
{result, Operand::Int(elem_type), Operand::Int(len_id)});
|
||||
}
|
||||
|
||||
if (ary->has_array_stride()) {
|
||||
push_annot(spv::Op::OpDecorate,
|
||||
{Operand::Int(result_id), Operand::Int(SpvDecorationArrayStride),
|
||||
Operand::Int(ary->array_stride())});
|
||||
auto* sem_arr = builder_.Sem().Get(ary);
|
||||
if (!sem_arr) {
|
||||
error_ = "array type missing semantic info";
|
||||
return false;
|
||||
}
|
||||
push_annot(spv::Op::OpDecorate,
|
||||
{Operand::Int(result_id), Operand::Int(SpvDecorationArrayStride),
|
||||
Operand::Int(sem_arr->Stride())});
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -3054,38 +3060,39 @@ uint32_t Builder::GenerateStructMember(uint32_t struct_id,
|
|||
{Operand::Int(struct_id), Operand::Int(idx),
|
||||
Operand::String(builder_.Symbols().NameFor(member->symbol()))});
|
||||
|
||||
bool has_layout = false;
|
||||
for (auto* deco : member->decorations()) {
|
||||
if (auto* offset = deco->As<ast::StructMemberOffsetDecoration>()) {
|
||||
push_annot(
|
||||
spv::Op::OpMemberDecorate,
|
||||
{Operand::Int(struct_id), Operand::Int(idx),
|
||||
Operand::Int(SpvDecorationOffset), Operand::Int(offset->offset())});
|
||||
has_layout = true;
|
||||
} else {
|
||||
error_ = "unknown struct member decoration";
|
||||
return 0;
|
||||
}
|
||||
// Note: This will generate layout annotations for *all* structs, whether or
|
||||
// not they are used in host-shareable variables. This is officially ok in
|
||||
// SPIR-V 1.0 through 1.3. If / when we migrate to using SPIR-V 1.4 we'll have
|
||||
// to only generate the layout info for structs used for certain storage
|
||||
// classes.
|
||||
|
||||
auto* sem_member = builder_.Sem().Get(member);
|
||||
if (!sem_member) {
|
||||
error_ = "Struct member has no semantic information";
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (has_layout) {
|
||||
// Infer and emit matrix layout.
|
||||
auto* matrix_type = GetNestedMatrixType(member->type());
|
||||
if (matrix_type) {
|
||||
push_annot(spv::Op::OpMemberDecorate,
|
||||
{Operand::Int(struct_id), Operand::Int(idx),
|
||||
Operand::Int(SpvDecorationColMajor)});
|
||||
if (!matrix_type->type()->Is<type::F32>()) {
|
||||
error_ = "matrix scalar element type must be f32";
|
||||
return 0;
|
||||
}
|
||||
const auto scalar_elem_size = 4;
|
||||
const auto effective_row_count = (matrix_type->rows() == 2) ? 2 : 4;
|
||||
push_annot(spv::Op::OpMemberDecorate,
|
||||
{Operand::Int(struct_id), Operand::Int(idx),
|
||||
Operand::Int(SpvDecorationMatrixStride),
|
||||
Operand::Int(effective_row_count * scalar_elem_size)});
|
||||
push_annot(
|
||||
spv::Op::OpMemberDecorate,
|
||||
{Operand::Int(struct_id), Operand::Int(idx),
|
||||
Operand::Int(SpvDecorationOffset), Operand::Int(sem_member->Offset())});
|
||||
|
||||
// Infer and emit matrix layout.
|
||||
auto* matrix_type = GetNestedMatrixType(member->type());
|
||||
if (matrix_type) {
|
||||
push_annot(spv::Op::OpMemberDecorate,
|
||||
{Operand::Int(struct_id), Operand::Int(idx),
|
||||
Operand::Int(SpvDecorationColMajor)});
|
||||
if (!matrix_type->type()->Is<type::F32>()) {
|
||||
error_ = "matrix scalar element type must be f32";
|
||||
return 0;
|
||||
}
|
||||
const auto scalar_elem_size = 4;
|
||||
const auto effective_row_count = (matrix_type->rows() == 2) ? 2 : 4;
|
||||
push_annot(spv::Op::OpMemberDecorate,
|
||||
{Operand::Int(struct_id), Operand::Int(idx),
|
||||
Operand::Int(SpvDecorationMatrixStride),
|
||||
Operand::Int(effective_row_count * scalar_elem_size)});
|
||||
}
|
||||
|
||||
return GenerateTypeIfNeeded(member->type());
|
||||
|
|
|
@ -135,12 +135,12 @@ TEST_F(BuilderTest, ArrayAccessor_Dynamic) {
|
|||
}
|
||||
|
||||
TEST_F(BuilderTest, ArrayAccessor_MultiLevel) {
|
||||
type::Array ary4(ty.vec3<f32>(), 4, ast::DecorationList{});
|
||||
auto* ary4 = ty.array(ty.vec3<f32>(), 4);
|
||||
|
||||
// ary = array<vec3<f32>, 4>
|
||||
// ary[3][2];
|
||||
|
||||
auto* var = Global("ary", &ary4, ast::StorageClass::kFunction);
|
||||
auto* var = Global("ary", ary4, ast::StorageClass::kFunction);
|
||||
|
||||
auto* expr = IndexAccessor(IndexAccessor("ary", 3), 2);
|
||||
WrapInFunction(expr);
|
||||
|
@ -173,12 +173,12 @@ TEST_F(BuilderTest, ArrayAccessor_MultiLevel) {
|
|||
}
|
||||
|
||||
TEST_F(BuilderTest, Accessor_ArrayWithSwizzle) {
|
||||
type::Array ary4(ty.vec3<f32>(), 4, ast::DecorationList{});
|
||||
auto* ary4 = ty.array(ty.vec3<f32>(), 4);
|
||||
|
||||
// var a : array<vec3<f32>, 4>;
|
||||
// a[2].xy;
|
||||
|
||||
auto* var = Global("ary", &ary4, ast::StorageClass::kFunction);
|
||||
auto* var = Global("ary", ary4, ast::StorageClass::kFunction);
|
||||
|
||||
auto* expr = MemberAccessor(IndexAccessor("ary", 2), "xy");
|
||||
WrapInFunction(expr);
|
||||
|
@ -219,12 +219,12 @@ TEST_F(BuilderTest, MemberAccessor) {
|
|||
// var ident : my_struct
|
||||
// ident.b
|
||||
|
||||
auto* s = create<ast::Struct>(
|
||||
ast::StructMemberList{Member("a", ty.f32()), Member("b", ty.f32())},
|
||||
ast::DecorationList{});
|
||||
auto* s = Structure("my_struct", {
|
||||
Member("a", ty.f32()),
|
||||
Member("b", ty.f32()),
|
||||
});
|
||||
|
||||
auto* s_type = ty.struct_("my_struct", s);
|
||||
auto* var = Global("ident", s_type, ast::StorageClass::kFunction);
|
||||
auto* var = Global("ident", s, ast::StorageClass::kFunction);
|
||||
|
||||
auto* expr = MemberAccessor("ident", "b");
|
||||
WrapInFunction(expr);
|
||||
|
@ -262,15 +262,12 @@ TEST_F(BuilderTest, MemberAccessor_Nested) {
|
|||
//
|
||||
// var ident : my_struct
|
||||
// ident.inner.a
|
||||
auto* inner_struct = ty.struct_(
|
||||
"Inner", create<ast::Struct>(ast::StructMemberList{Member("a", ty.f32()),
|
||||
Member("b", ty.f32())},
|
||||
ast::DecorationList{}));
|
||||
auto* inner_struct = Structure("Inner", {
|
||||
Member("a", ty.f32()),
|
||||
Member("b", ty.f32()),
|
||||
});
|
||||
|
||||
auto* s_type = ty.struct_(
|
||||
"my_struct",
|
||||
create<ast::Struct>(ast::StructMemberList{Member("inner", inner_struct)},
|
||||
ast::DecorationList{}));
|
||||
auto* s_type = Structure("my_struct", {Member("inner", inner_struct)});
|
||||
|
||||
auto* var = Global("ident", s_type, ast::StorageClass::kFunction);
|
||||
auto* expr = MemberAccessor(MemberAccessor("ident", "inner"), "a");
|
||||
|
@ -311,16 +308,13 @@ TEST_F(BuilderTest, MemberAccessor_Nested_WithAlias) {
|
|||
//
|
||||
// var ident : my_struct
|
||||
// ident.inner.a
|
||||
auto* inner_struct = ty.struct_(
|
||||
"Inner", create<ast::Struct>(ast::StructMemberList{Member("a", ty.f32()),
|
||||
Member("b", ty.f32())},
|
||||
ast::DecorationList{}));
|
||||
auto* inner_struct = Structure("Inner", {
|
||||
Member("a", ty.f32()),
|
||||
Member("b", ty.f32()),
|
||||
});
|
||||
|
||||
auto* alias = ty.alias("Inner", inner_struct);
|
||||
auto* s_type = ty.struct_(
|
||||
"Outer",
|
||||
create<ast::Struct>(ast::StructMemberList{Member("inner", alias)},
|
||||
ast::DecorationList{}));
|
||||
auto* s_type = Structure("Outer", {Member("inner", alias)});
|
||||
|
||||
auto* var = Global("ident", s_type, ast::StorageClass::kFunction);
|
||||
auto* expr = MemberAccessor(MemberAccessor("ident", "inner"), "a");
|
||||
|
@ -360,15 +354,12 @@ TEST_F(BuilderTest, MemberAccessor_Nested_Assignment_LHS) {
|
|||
//
|
||||
// var ident : my_struct
|
||||
// ident.inner.a = 2.0f;
|
||||
auto* inner_struct = ty.struct_(
|
||||
"Inner", create<ast::Struct>(ast::StructMemberList{Member("a", ty.f32()),
|
||||
Member("b", ty.f32())},
|
||||
ast::DecorationList{}));
|
||||
auto* inner_struct = Structure("Inner", {
|
||||
Member("a", ty.f32()),
|
||||
Member("b", ty.f32()),
|
||||
});
|
||||
|
||||
auto* s_type = ty.struct_(
|
||||
"my_struct",
|
||||
create<ast::Struct>(ast::StructMemberList{Member("inner", inner_struct)},
|
||||
ast::DecorationList{}));
|
||||
auto* s_type = Structure("my_struct", {Member("inner", inner_struct)});
|
||||
|
||||
auto* var = Global("ident", s_type, ast::StorageClass::kFunction);
|
||||
auto* expr = create<ast::AssignmentStatement>(
|
||||
|
@ -412,15 +403,12 @@ TEST_F(BuilderTest, MemberAccessor_Nested_Assignment_RHS) {
|
|||
// var ident : my_struct
|
||||
// var store : f32 = ident.inner.a
|
||||
|
||||
auto* inner_struct = ty.struct_(
|
||||
"Inner", create<ast::Struct>(ast::StructMemberList{Member("a", ty.f32()),
|
||||
Member("b", ty.f32())},
|
||||
ast::DecorationList{}));
|
||||
auto* inner_struct = Structure("Inner", {
|
||||
Member("a", ty.f32()),
|
||||
Member("b", ty.f32()),
|
||||
});
|
||||
|
||||
auto* s_type = ty.struct_(
|
||||
"my_struct",
|
||||
create<ast::Struct>(ast::StructMemberList{Member("inner", inner_struct)},
|
||||
ast::DecorationList{}));
|
||||
auto* s_type = Structure("my_struct", {Member("inner", inner_struct)});
|
||||
|
||||
auto* var = Global("ident", s_type, ast::StorageClass::kFunction);
|
||||
auto* store = Global("store", ty.f32(), ast::StorageClass::kFunction);
|
||||
|
@ -625,21 +613,14 @@ TEST_F(BuilderTest, Accessor_Mixed_ArrayAndMember) {
|
|||
// var index : array<A, 2>
|
||||
// index[0].foo[2].bar.baz.yx
|
||||
|
||||
auto* s =
|
||||
create<ast::Struct>(ast::StructMemberList{Member("baz", ty.vec3<f32>())},
|
||||
ast::DecorationList{});
|
||||
auto* c_type = ty.struct_("C", s);
|
||||
auto* c_type = Structure("C", {Member("baz", ty.vec3<f32>())});
|
||||
|
||||
s = create<ast::Struct>(ast::StructMemberList{Member("bar", c_type)},
|
||||
ast::DecorationList{});
|
||||
auto* b_type = ty.struct_("B", s);
|
||||
type::Array b_ary_type(b_type, 3, ast::DecorationList{});
|
||||
s = create<ast::Struct>(ast::StructMemberList{Member("foo", &b_ary_type)},
|
||||
ast::DecorationList{});
|
||||
auto* a_type = ty.struct_("A", s);
|
||||
auto* b_type = Structure("B", {Member("bar", c_type)});
|
||||
auto* b_ary_type = ty.array(b_type, 3);
|
||||
auto* a_type = Structure("A", {Member("foo", b_ary_type)});
|
||||
|
||||
type::Array a_ary_type(a_type, 2, ast::DecorationList{});
|
||||
auto* var = Global("index", &a_ary_type, ast::StorageClass::kFunction);
|
||||
auto* a_ary_type = ty.array(a_type, 2);
|
||||
auto* var = Global("index", a_ary_type, ast::StorageClass::kFunction);
|
||||
auto* expr = MemberAccessor(
|
||||
MemberAccessor(
|
||||
MemberAccessor(
|
||||
|
@ -693,12 +674,12 @@ TEST_F(BuilderTest, Accessor_Array_Of_Vec) {
|
|||
// vec2<f32>(0.5, -0.5));
|
||||
// pos[1]
|
||||
|
||||
type::Array arr(ty.vec2<f32>(), 3, ast::DecorationList{});
|
||||
auto* arr = ty.array(ty.vec2<f32>(), 3);
|
||||
|
||||
auto* var =
|
||||
GlobalConst("pos", &arr,
|
||||
Construct(&arr, vec2<f32>(0.0f, 0.5f),
|
||||
vec2<f32>(-0.5f, -0.5f), vec2<f32>(0.5f, -0.5f)));
|
||||
GlobalConst("pos", arr,
|
||||
Construct(arr, vec2<f32>(0.0f, 0.5f), vec2<f32>(-0.5f, -0.5f),
|
||||
vec2<f32>(0.5f, -0.5f)));
|
||||
|
||||
auto* expr = IndexAccessor("pos", 1u);
|
||||
WrapInFunction(expr);
|
||||
|
|
|
@ -176,12 +176,12 @@ TEST_F(BuilderTest, Assign_StructMember) {
|
|||
// var ident : my_struct
|
||||
// ident.b = 4.0;
|
||||
|
||||
auto* s = create<ast::Struct>(
|
||||
ast::StructMemberList{Member("a", ty.f32()), Member("b", ty.f32())},
|
||||
ast::DecorationList{});
|
||||
auto* s = Structure("my_struct", {
|
||||
Member("a", ty.f32()),
|
||||
Member("b", ty.f32()),
|
||||
});
|
||||
|
||||
auto* s_type = ty.struct_("my_struct", s);
|
||||
auto* v = Global("ident", s_type, ast::StorageClass::kFunction);
|
||||
auto* v = Global("ident", s, ast::StorageClass::kFunction);
|
||||
|
||||
auto* assign =
|
||||
create<ast::AssignmentStatement>(MemberAccessor("ident", "b"), Expr(4.f));
|
||||
|
|
|
@ -974,15 +974,12 @@ TEST_F(SpvBuilderConstructorTest, Type_Array_2_Vec3) {
|
|||
}
|
||||
|
||||
TEST_F(SpvBuilderConstructorTest, Type_Struct) {
|
||||
auto* s = create<ast::Struct>(
|
||||
ast::StructMemberList{
|
||||
Member("a", ty.f32()),
|
||||
Member("b", ty.vec3<f32>()),
|
||||
},
|
||||
ast::DecorationList{});
|
||||
auto* s_type = ty.struct_("my_struct", s);
|
||||
auto* s = Structure("my_struct", {
|
||||
Member("a", ty.f32()),
|
||||
Member("b", ty.vec3<f32>()),
|
||||
});
|
||||
|
||||
auto* t = Construct(s_type, 2.0f, vec3<f32>(2.0f, 2.0f, 2.0f));
|
||||
auto* t = Construct(s, 2.0f, vec3<f32>(2.0f, 2.0f, 2.0f));
|
||||
WrapInFunction(t);
|
||||
|
||||
spirv::Builder& b = Build();
|
||||
|
@ -1127,13 +1124,8 @@ TEST_F(SpvBuilderConstructorTest, Type_ZeroInit_Array) {
|
|||
}
|
||||
|
||||
TEST_F(SpvBuilderConstructorTest, Type_ZeroInit_Struct) {
|
||||
auto* s = create<ast::Struct>(
|
||||
ast::StructMemberList{
|
||||
Member("a", ty.f32()),
|
||||
},
|
||||
ast::DecorationList{});
|
||||
auto* s_type = ty.struct_("my_struct", s);
|
||||
auto* t = Construct(s_type);
|
||||
auto* s = Structure("my_struct", {Member("a", ty.f32())});
|
||||
auto* t = Construct(s);
|
||||
WrapInFunction(t);
|
||||
|
||||
spirv::Builder& b = Build();
|
||||
|
@ -1564,14 +1556,12 @@ TEST_F(SpvBuilderConstructorTest, IsConstructorConst_BitCastScalars) {
|
|||
}
|
||||
|
||||
TEST_F(SpvBuilderConstructorTest, IsConstructorConst_Struct) {
|
||||
auto* s = create<ast::Struct>(
|
||||
ast::StructMemberList{
|
||||
Member("a", ty.f32()),
|
||||
Member("b", ty.vec3<f32>()),
|
||||
},
|
||||
ast::DecorationList{});
|
||||
auto* s_type = ty.struct_("my_struct", s);
|
||||
auto* t = Construct(s_type, 2.f, vec3<f32>(2.f, 2.f, 2.f));
|
||||
auto* s = Structure("my_struct", {
|
||||
Member("a", ty.f32()),
|
||||
Member("b", ty.vec3<f32>()),
|
||||
});
|
||||
|
||||
auto* t = Construct(s, 2.f, vec3<f32>(2.f, 2.f, 2.f));
|
||||
WrapInFunction(t);
|
||||
|
||||
spirv::Builder& b = Build();
|
||||
|
@ -1582,15 +1572,12 @@ TEST_F(SpvBuilderConstructorTest, IsConstructorConst_Struct) {
|
|||
|
||||
TEST_F(SpvBuilderConstructorTest,
|
||||
IsConstructorConst_Struct_WithIdentSubExpression) {
|
||||
auto* s = create<ast::Struct>(
|
||||
ast::StructMemberList{
|
||||
Member("a", ty.f32()),
|
||||
Member("b", ty.vec3<f32>()),
|
||||
},
|
||||
ast::DecorationList{});
|
||||
auto* s = Structure("my_struct", {
|
||||
Member("a", ty.f32()),
|
||||
Member("b", ty.vec3<f32>()),
|
||||
});
|
||||
|
||||
auto* s_type = ty.struct_("my_struct", s);
|
||||
auto* t = Construct(s_type, 2.f, "a", 2.f);
|
||||
auto* t = Construct(s, 2.f, "a", 2.f);
|
||||
WrapInFunction(t);
|
||||
|
||||
Global("a", ty.f32(), ast::StorageClass::kPrivate);
|
||||
|
|
|
@ -192,7 +192,7 @@ TEST_F(BuilderTest, FunctionType_DeDuplicate) {
|
|||
// https://crbug.com/tint/297
|
||||
TEST_F(BuilderTest, Emit_Multiple_EntryPoint_With_Same_ModuleVar) {
|
||||
// [[block]] struct Data {
|
||||
// [[offset(0)]] d : f32;
|
||||
// d : f32;
|
||||
// };
|
||||
// [[binding(0), group(0)]] var<storage> data : Data;
|
||||
//
|
||||
|
@ -206,13 +206,9 @@ TEST_F(BuilderTest, Emit_Multiple_EntryPoint_With_Same_ModuleVar) {
|
|||
// return;
|
||||
// }
|
||||
|
||||
ast::DecorationList s_decos;
|
||||
s_decos.push_back(create<ast::StructBlockDecoration>());
|
||||
auto* s = Structure("Data", {Member("d", ty.f32())},
|
||||
{create<ast::StructBlockDecoration>()});
|
||||
|
||||
auto* str = create<ast::Struct>(
|
||||
ast::StructMemberList{Member("d", ty.f32(), {MemberOffset(0)})}, s_decos);
|
||||
|
||||
auto* s = ty.struct_("Data", str);
|
||||
type::AccessControl ac(ast::AccessControl::kReadWrite, s);
|
||||
|
||||
Global("data", &ac, ast::StorageClass::kStorage, nullptr,
|
||||
|
@ -221,8 +217,6 @@ TEST_F(BuilderTest, Emit_Multiple_EntryPoint_With_Same_ModuleVar) {
|
|||
create<ast::GroupDecoration>(0),
|
||||
});
|
||||
|
||||
AST().AddConstructedType(s);
|
||||
|
||||
{
|
||||
auto* var = Var("v", ty.f32(), ast::StorageClass::kFunction,
|
||||
MemberAccessor("data", "d"));
|
||||
|
|
|
@ -383,10 +383,10 @@ TEST_F(BuilderTest, GlobalVar_DeclReadOnly) {
|
|||
// };
|
||||
// var b : [[access(read)]] A
|
||||
|
||||
auto* A = ty.struct_(
|
||||
"A", create<ast::Struct>(ast::StructMemberList{Member("a", ty.i32()),
|
||||
Member("b", ty.i32())},
|
||||
ast::DecorationList{}));
|
||||
auto* A = Structure("A", {
|
||||
Member("a", ty.i32()),
|
||||
Member("b", ty.i32()),
|
||||
});
|
||||
auto* ac = create<type::AccessControl>(ast::AccessControl::kReadOnly, A);
|
||||
|
||||
auto* var = Global("b", ac, ast::StorageClass::kStorage);
|
||||
|
@ -395,7 +395,9 @@ TEST_F(BuilderTest, GlobalVar_DeclReadOnly) {
|
|||
|
||||
EXPECT_TRUE(b.GenerateGlobalVariable(var)) << b.error();
|
||||
|
||||
EXPECT_EQ(DumpInstructions(b.annots()), R"(OpMemberDecorate %3 0 NonWritable
|
||||
EXPECT_EQ(DumpInstructions(b.annots()), R"(OpMemberDecorate %3 0 Offset 0
|
||||
OpMemberDecorate %3 0 NonWritable
|
||||
OpMemberDecorate %3 1 Offset 4
|
||||
OpMemberDecorate %3 1 NonWritable
|
||||
)");
|
||||
EXPECT_EQ(DumpInstructions(b.debug()), R"(OpName %3 "A"
|
||||
|
@ -417,9 +419,7 @@ TEST_F(BuilderTest, GlobalVar_TypeAliasDeclReadOnly) {
|
|||
// type B = A;
|
||||
// var b : [[access(read)]] B
|
||||
|
||||
auto* A = ty.struct_(
|
||||
"A", create<ast::Struct>(ast::StructMemberList{Member("a", ty.i32())},
|
||||
ast::DecorationList{}));
|
||||
auto* A = Structure("A", {Member("a", ty.i32())});
|
||||
auto* B = ty.alias("B", A);
|
||||
auto* ac = create<type::AccessControl>(ast::AccessControl::kReadOnly, B);
|
||||
auto* var = Global("b", ac, ast::StorageClass::kStorage);
|
||||
|
@ -428,7 +428,8 @@ TEST_F(BuilderTest, GlobalVar_TypeAliasDeclReadOnly) {
|
|||
|
||||
EXPECT_TRUE(b.GenerateGlobalVariable(var)) << b.error();
|
||||
|
||||
EXPECT_EQ(DumpInstructions(b.annots()), R"(OpMemberDecorate %3 0 NonWritable
|
||||
EXPECT_EQ(DumpInstructions(b.annots()), R"(OpMemberDecorate %3 0 Offset 0
|
||||
OpMemberDecorate %3 0 NonWritable
|
||||
)");
|
||||
EXPECT_EQ(DumpInstructions(b.debug()), R"(OpName %3 "A"
|
||||
OpMemberName %3 0 "a"
|
||||
|
@ -448,9 +449,7 @@ TEST_F(BuilderTest, GlobalVar_TypeAliasAssignReadOnly) {
|
|||
// type B = [[access(read)]] A;
|
||||
// var b : B
|
||||
|
||||
auto* A = ty.struct_(
|
||||
"A", create<ast::Struct>(ast::StructMemberList{Member("a", ty.i32())},
|
||||
ast::DecorationList{}));
|
||||
auto* A = Structure("A", {Member("a", ty.i32())});
|
||||
auto* ac = create<type::AccessControl>(ast::AccessControl::kReadOnly, A);
|
||||
auto* B = ty.alias("B", ac);
|
||||
auto* var = Global("b", B, ast::StorageClass::kStorage);
|
||||
|
@ -459,7 +458,8 @@ TEST_F(BuilderTest, GlobalVar_TypeAliasAssignReadOnly) {
|
|||
|
||||
EXPECT_TRUE(b.GenerateGlobalVariable(var)) << b.error();
|
||||
|
||||
EXPECT_EQ(DumpInstructions(b.annots()), R"(OpMemberDecorate %3 0 NonWritable
|
||||
EXPECT_EQ(DumpInstructions(b.annots()), R"(OpMemberDecorate %3 0 Offset 0
|
||||
OpMemberDecorate %3 0 NonWritable
|
||||
)");
|
||||
EXPECT_EQ(DumpInstructions(b.debug()), R"(OpName %3 "A"
|
||||
OpMemberName %3 0 "a"
|
||||
|
@ -479,9 +479,7 @@ TEST_F(BuilderTest, GlobalVar_TwoVarDeclReadOnly) {
|
|||
// var b : [[access(read)]] A
|
||||
// var c : [[access(read_write)]] A
|
||||
|
||||
auto* A = ty.struct_(
|
||||
"A", create<ast::Struct>(ast::StructMemberList{Member("a", ty.i32())},
|
||||
ast::DecorationList{}));
|
||||
auto* A = Structure("A", {Member("a", ty.i32())});
|
||||
type::AccessControl read{ast::AccessControl::kReadOnly, A};
|
||||
type::AccessControl rw{ast::AccessControl::kReadWrite, A};
|
||||
|
||||
|
@ -494,7 +492,9 @@ TEST_F(BuilderTest, GlobalVar_TwoVarDeclReadOnly) {
|
|||
EXPECT_TRUE(b.GenerateGlobalVariable(var_c)) << b.error();
|
||||
|
||||
EXPECT_EQ(DumpInstructions(b.annots()),
|
||||
R"(OpMemberDecorate %3 0 NonWritable
|
||||
R"(OpMemberDecorate %3 0 Offset 0
|
||||
OpMemberDecorate %3 0 NonWritable
|
||||
OpMemberDecorate %7 0 Offset 0
|
||||
)");
|
||||
EXPECT_EQ(DumpInstructions(b.debug()), R"(OpName %3 "A"
|
||||
OpMemberName %3 0 "a"
|
||||
|
@ -665,6 +665,7 @@ OpExecutionMode %11 LocalSize 1 1 1
|
|||
OpName %1 "mask_in"
|
||||
OpName %6 "mask_out"
|
||||
OpName %11 "main"
|
||||
OpDecorate %3 ArrayStride 4
|
||||
OpDecorate %1 BuiltIn SampleMask
|
||||
OpDecorate %6 BuiltIn SampleMask
|
||||
%4 = OpTypeInt 32 0
|
||||
|
|
|
@ -1377,13 +1377,9 @@ OpFunctionEnd
|
|||
}
|
||||
|
||||
TEST_F(IntrinsicBuilderTest, Call_ArrayLength) {
|
||||
auto* s = create<ast::Struct>(
|
||||
ast::StructMemberList{Member(0, "a", ty.array<f32>(4))},
|
||||
ast::DecorationList{
|
||||
create<ast::StructBlockDecoration>(),
|
||||
});
|
||||
auto* s_type = ty.struct_("my_struct", s);
|
||||
Global("b", s_type, ast::StorageClass::kStorage, nullptr,
|
||||
auto* s = Structure("my_struct", {Member(0, "a", ty.array<f32>(4))},
|
||||
{create<ast::StructBlockDecoration>()});
|
||||
Global("b", s, ast::StorageClass::kStorage, nullptr,
|
||||
ast::DecorationList{
|
||||
create<ast::BindingDecoration>(1),
|
||||
create<ast::GroupDecoration>(2),
|
||||
|
@ -1426,15 +1422,14 @@ TEST_F(IntrinsicBuilderTest, Call_ArrayLength) {
|
|||
}
|
||||
|
||||
TEST_F(IntrinsicBuilderTest, Call_ArrayLength_OtherMembersInStruct) {
|
||||
auto* s = create<ast::Struct>(
|
||||
ast::StructMemberList{Member(0, "z", ty.f32()),
|
||||
Member(4, "a", ty.array<f32>(4))},
|
||||
ast::DecorationList{
|
||||
create<ast::StructBlockDecoration>(),
|
||||
});
|
||||
auto* s = Structure("my_struct",
|
||||
{
|
||||
Member(0, "z", ty.f32()),
|
||||
Member(4, "a", ty.array<f32>(4)),
|
||||
},
|
||||
{create<ast::StructBlockDecoration>()});
|
||||
|
||||
auto* s_type = ty.struct_("my_struct", s);
|
||||
Global("b", s_type, ast::StorageClass::kStorage, nullptr,
|
||||
Global("b", s, ast::StorageClass::kStorage, nullptr,
|
||||
ast::DecorationList{
|
||||
create<ast::BindingDecoration>(1),
|
||||
create<ast::GroupDecoration>(2),
|
||||
|
|
|
@ -58,11 +58,11 @@ TEST_F(BuilderTest_Type, ReturnsGeneratedAlias) {
|
|||
}
|
||||
|
||||
TEST_F(BuilderTest_Type, GenerateRuntimeArray) {
|
||||
type::Array ary(ty.i32(), 0, ast::DecorationList{});
|
||||
auto* ary = ty.array(ty.i32(), 0);
|
||||
|
||||
spirv::Builder& b = Build();
|
||||
|
||||
auto id = b.GenerateTypeIfNeeded(&ary);
|
||||
auto id = b.GenerateTypeIfNeeded(ary);
|
||||
ASSERT_FALSE(b.has_error()) << b.error();
|
||||
EXPECT_EQ(1u, id);
|
||||
|
||||
|
@ -72,12 +72,12 @@ TEST_F(BuilderTest_Type, GenerateRuntimeArray) {
|
|||
}
|
||||
|
||||
TEST_F(BuilderTest_Type, ReturnsGeneratedRuntimeArray) {
|
||||
type::Array ary(ty.i32(), 0, ast::DecorationList{});
|
||||
auto* ary = ty.array(ty.i32(), 0);
|
||||
|
||||
spirv::Builder& b = Build();
|
||||
|
||||
EXPECT_EQ(b.GenerateTypeIfNeeded(&ary), 1u);
|
||||
EXPECT_EQ(b.GenerateTypeIfNeeded(&ary), 1u);
|
||||
EXPECT_EQ(b.GenerateTypeIfNeeded(ary), 1u);
|
||||
EXPECT_EQ(b.GenerateTypeIfNeeded(ary), 1u);
|
||||
ASSERT_FALSE(b.has_error()) << b.error();
|
||||
|
||||
EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeInt 32 1
|
||||
|
@ -86,11 +86,11 @@ TEST_F(BuilderTest_Type, ReturnsGeneratedRuntimeArray) {
|
|||
}
|
||||
|
||||
TEST_F(BuilderTest_Type, GenerateArray) {
|
||||
type::Array ary(ty.i32(), 4, ast::DecorationList{});
|
||||
auto* ary = ty.array(ty.i32(), 4);
|
||||
|
||||
spirv::Builder& b = Build();
|
||||
|
||||
auto id = b.GenerateTypeIfNeeded(&ary);
|
||||
auto id = b.GenerateTypeIfNeeded(ary);
|
||||
ASSERT_FALSE(b.has_error()) << b.error();
|
||||
EXPECT_EQ(1u, id);
|
||||
|
||||
|
@ -102,14 +102,11 @@ TEST_F(BuilderTest_Type, GenerateArray) {
|
|||
}
|
||||
|
||||
TEST_F(BuilderTest_Type, GenerateArray_WithStride) {
|
||||
type::Array ary(ty.i32(), 4,
|
||||
ast::DecorationList{
|
||||
create<ast::StrideDecoration>(16u),
|
||||
});
|
||||
auto* ary = ty.array(ty.i32(), 4, 16u);
|
||||
|
||||
spirv::Builder& b = Build();
|
||||
|
||||
auto id = b.GenerateTypeIfNeeded(&ary);
|
||||
auto id = b.GenerateTypeIfNeeded(ary);
|
||||
ASSERT_FALSE(b.has_error()) << b.error();
|
||||
EXPECT_EQ(1u, id);
|
||||
|
||||
|
@ -124,12 +121,12 @@ TEST_F(BuilderTest_Type, GenerateArray_WithStride) {
|
|||
}
|
||||
|
||||
TEST_F(BuilderTest_Type, ReturnsGeneratedArray) {
|
||||
type::Array ary(ty.i32(), 4, ast::DecorationList{});
|
||||
auto* ary = ty.array(ty.i32(), 4);
|
||||
|
||||
spirv::Builder& b = Build();
|
||||
|
||||
EXPECT_EQ(b.GenerateTypeIfNeeded(&ary), 1u);
|
||||
EXPECT_EQ(b.GenerateTypeIfNeeded(&ary), 1u);
|
||||
EXPECT_EQ(b.GenerateTypeIfNeeded(ary), 1u);
|
||||
EXPECT_EQ(b.GenerateTypeIfNeeded(ary), 1u);
|
||||
ASSERT_FALSE(b.has_error()) << b.error();
|
||||
|
||||
EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeInt 32 1
|
||||
|
@ -277,12 +274,11 @@ TEST_F(BuilderTest_Type, ReturnsGeneratedPtr) {
|
|||
}
|
||||
|
||||
TEST_F(BuilderTest_Type, GenerateStruct_Empty) {
|
||||
auto* s = create<ast::Struct>(ast::StructMemberList{}, ast::DecorationList{});
|
||||
auto* s_type = ty.struct_("S", s);
|
||||
auto* s = Structure("S", {});
|
||||
|
||||
spirv::Builder& b = Build();
|
||||
|
||||
auto id = b.GenerateTypeIfNeeded(s_type);
|
||||
auto id = b.GenerateTypeIfNeeded(s);
|
||||
ASSERT_FALSE(b.has_error()) << b.error();
|
||||
EXPECT_EQ(id, 1u);
|
||||
|
||||
|
@ -294,13 +290,11 @@ TEST_F(BuilderTest_Type, GenerateStruct_Empty) {
|
|||
}
|
||||
|
||||
TEST_F(BuilderTest_Type, GenerateStruct) {
|
||||
auto* s = create<ast::Struct>(ast::StructMemberList{Member("a", ty.f32())},
|
||||
ast::DecorationList{});
|
||||
auto* s_type = ty.struct_("my_struct", s);
|
||||
auto* s = Structure("my_struct", {Member("a", ty.f32())});
|
||||
|
||||
spirv::Builder& b = Build();
|
||||
|
||||
auto id = b.GenerateTypeIfNeeded(s_type);
|
||||
auto id = b.GenerateTypeIfNeeded(s);
|
||||
ASSERT_FALSE(b.has_error()) << b.error();
|
||||
EXPECT_EQ(id, 1u);
|
||||
|
||||
|
@ -313,16 +307,12 @@ OpMemberName %1 0 "a"
|
|||
}
|
||||
|
||||
TEST_F(BuilderTest_Type, GenerateStruct_Decorated) {
|
||||
ast::DecorationList struct_decos;
|
||||
struct_decos.push_back(create<ast::StructBlockDecoration>());
|
||||
|
||||
auto* s = create<ast::Struct>(ast::StructMemberList{Member("a", ty.f32())},
|
||||
struct_decos);
|
||||
auto* s_type = ty.struct_("my_struct", s);
|
||||
auto* s = Structure("my_struct", {Member("a", ty.f32())},
|
||||
{create<ast::StructBlockDecoration>()});
|
||||
|
||||
spirv::Builder& b = Build();
|
||||
|
||||
auto id = b.GenerateTypeIfNeeded(s_type);
|
||||
auto id = b.GenerateTypeIfNeeded(s);
|
||||
ASSERT_FALSE(b.has_error()) << b.error();
|
||||
EXPECT_EQ(id, 1u);
|
||||
|
||||
|
@ -333,19 +323,19 @@ TEST_F(BuilderTest_Type, GenerateStruct_Decorated) {
|
|||
OpMemberName %1 0 "a"
|
||||
)");
|
||||
EXPECT_EQ(DumpInstructions(b.annots()), R"(OpDecorate %1 Block
|
||||
OpMemberDecorate %1 0 Offset 0
|
||||
)");
|
||||
}
|
||||
|
||||
TEST_F(BuilderTest_Type, GenerateStruct_DecoratedMembers) {
|
||||
auto* s = create<ast::Struct>(
|
||||
ast::StructMemberList{Member("a", ty.f32(), {MemberOffset(0)}),
|
||||
Member("b", ty.f32(), {MemberOffset(8)})},
|
||||
ast::DecorationList{});
|
||||
auto* s_type = ty.struct_("S", s);
|
||||
auto* s = Structure("S", {
|
||||
Member("a", ty.f32()),
|
||||
Member("b", ty.f32(), {MemberAlign(8)}),
|
||||
});
|
||||
|
||||
spirv::Builder& b = Build();
|
||||
|
||||
auto id = b.GenerateTypeIfNeeded(s_type);
|
||||
auto id = b.GenerateTypeIfNeeded(s);
|
||||
ASSERT_FALSE(b.has_error()) << b.error();
|
||||
EXPECT_EQ(id, 1u);
|
||||
|
||||
|
@ -362,16 +352,15 @@ OpMemberDecorate %1 1 Offset 8
|
|||
}
|
||||
|
||||
TEST_F(BuilderTest_Type, GenerateStruct_NonLayout_Matrix) {
|
||||
auto* s =
|
||||
create<ast::Struct>(ast::StructMemberList{Member("a", ty.mat2x2<f32>()),
|
||||
Member("b", ty.mat2x3<f32>()),
|
||||
Member("c", ty.mat4x4<f32>())},
|
||||
ast::DecorationList{});
|
||||
auto* s_type = ty.struct_("S", s);
|
||||
auto* s = Structure("S", {
|
||||
Member("a", ty.mat2x2<f32>()),
|
||||
Member("b", ty.mat2x3<f32>()),
|
||||
Member("c", ty.mat4x4<f32>()),
|
||||
});
|
||||
|
||||
spirv::Builder& b = Build();
|
||||
|
||||
auto id = b.GenerateTypeIfNeeded(s_type);
|
||||
auto id = b.GenerateTypeIfNeeded(s);
|
||||
ASSERT_FALSE(b.has_error()) << b.error();
|
||||
EXPECT_EQ(id, 1u);
|
||||
|
||||
|
@ -389,21 +378,29 @@ OpMemberName %1 0 "a"
|
|||
OpMemberName %1 1 "b"
|
||||
OpMemberName %1 2 "c"
|
||||
)");
|
||||
EXPECT_EQ(DumpInstructions(b.annots()), "");
|
||||
EXPECT_EQ(DumpInstructions(b.annots()), R"(OpMemberDecorate %1 0 Offset 0
|
||||
OpMemberDecorate %1 0 ColMajor
|
||||
OpMemberDecorate %1 0 MatrixStride 8
|
||||
OpMemberDecorate %1 1 Offset 16
|
||||
OpMemberDecorate %1 1 ColMajor
|
||||
OpMemberDecorate %1 1 MatrixStride 16
|
||||
OpMemberDecorate %1 2 Offset 48
|
||||
OpMemberDecorate %1 2 ColMajor
|
||||
OpMemberDecorate %1 2 MatrixStride 16
|
||||
)");
|
||||
}
|
||||
|
||||
TEST_F(BuilderTest_Type, GenerateStruct_DecoratedMembers_LayoutMatrix) {
|
||||
// We have to infer layout for matrix when it also has an offset.
|
||||
auto* s = create<ast::Struct>(
|
||||
ast::StructMemberList{Member("a", ty.mat2x2<f32>(), {MemberOffset(0)}),
|
||||
Member("b", ty.mat2x3<f32>(), {MemberOffset(16)}),
|
||||
Member("c", ty.mat4x4<f32>(), {MemberOffset(48)})},
|
||||
ast::DecorationList{});
|
||||
auto* s_type = ty.struct_("S", s);
|
||||
auto* s = Structure("S", {
|
||||
Member("a", ty.mat2x2<f32>()),
|
||||
Member("b", ty.mat2x3<f32>()),
|
||||
Member("c", ty.mat4x4<f32>()),
|
||||
});
|
||||
|
||||
spirv::Builder& b = Build();
|
||||
|
||||
auto id = b.GenerateTypeIfNeeded(s_type);
|
||||
auto id = b.GenerateTypeIfNeeded(s);
|
||||
ASSERT_FALSE(b.has_error()) << b.error();
|
||||
EXPECT_EQ(id, 1u);
|
||||
|
||||
|
@ -437,26 +434,19 @@ TEST_F(BuilderTest_Type, GenerateStruct_DecoratedMembers_LayoutArraysOfMatrix) {
|
|||
// We have to infer layout for matrix when it also has an offset.
|
||||
// The decoration goes on the struct member, even if the matrix is buried
|
||||
// in levels of arrays.
|
||||
type::Array arr_mat2x2(ty.mat2x2<f32>(), 1,
|
||||
ast::DecorationList{}); // Singly nested array
|
||||
auto* arr_mat2x2 = ty.array(ty.mat2x2<f32>(), 1); // Singly nested array
|
||||
auto* arr_arr_mat2x3 = ty.array(ty.mat2x3<f32>(), 1); // Doubly nested array
|
||||
auto* rtarr_mat4x4 = ty.array(ty.mat4x4<f32>(), 0); // Runtime array
|
||||
|
||||
type::Array arr_mat2x3(ty.mat2x3<f32>(), 1, ast::DecorationList{});
|
||||
type::Array arr_arr_mat2x3(ty.mat2x3<f32>(), 1,
|
||||
ast::DecorationList{}); // Doubly nested array
|
||||
|
||||
type::Array rtarr_mat4x4(ty.mat4x4<f32>(), 0,
|
||||
ast::DecorationList{}); // Runtime array
|
||||
|
||||
auto* s = create<ast::Struct>(
|
||||
ast::StructMemberList{Member("a", &arr_mat2x2, {MemberOffset(0)}),
|
||||
Member("b", &arr_arr_mat2x3, {MemberOffset(16)}),
|
||||
Member("c", &rtarr_mat4x4, {MemberOffset(48)})},
|
||||
ast::DecorationList{});
|
||||
auto* s_type = ty.struct_("S", s);
|
||||
auto* s = Structure("S", {
|
||||
Member("a", arr_mat2x2),
|
||||
Member("b", arr_arr_mat2x3),
|
||||
Member("c", rtarr_mat4x4),
|
||||
});
|
||||
|
||||
spirv::Builder& b = Build();
|
||||
|
||||
auto id = b.GenerateTypeIfNeeded(s_type);
|
||||
auto id = b.GenerateTypeIfNeeded(s);
|
||||
ASSERT_FALSE(b.has_error()) << b.error();
|
||||
EXPECT_EQ(id, 1u);
|
||||
|
||||
|
@ -482,12 +472,15 @@ OpMemberName %1 2 "c"
|
|||
EXPECT_EQ(DumpInstructions(b.annots()), R"(OpMemberDecorate %1 0 Offset 0
|
||||
OpMemberDecorate %1 0 ColMajor
|
||||
OpMemberDecorate %1 0 MatrixStride 8
|
||||
OpDecorate %2 ArrayStride 16
|
||||
OpMemberDecorate %1 1 Offset 16
|
||||
OpMemberDecorate %1 1 ColMajor
|
||||
OpMemberDecorate %1 1 MatrixStride 16
|
||||
OpDecorate %8 ArrayStride 32
|
||||
OpMemberDecorate %1 2 Offset 48
|
||||
OpMemberDecorate %1 2 ColMajor
|
||||
OpMemberDecorate %1 2 MatrixStride 16
|
||||
OpDecorate %11 ArrayStride 64
|
||||
)");
|
||||
}
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue