mirror of
https://github.com/encounter/dawn-cmake.git
synced 2025-12-21 18:59:21 +00:00
tint/writer/msl: Move packed_vector hacks to transform
Attempting to paper over all the MSL standard library holes for packed_vector in the MSL writer added complexity to the writer, produced messy output, and didn't actually catch all the cases where casts were needed. Add a new PackedVec3 transform that applies the packed_vector -> vec casts in a smarter, more precise way. Fixed: tint:1534 Change-Id: I73ce7e5a62fbc9cb04e1093133070f5fb8965dce Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/107340 Kokoro: Kokoro <noreply+kokoro@google.com> Reviewed-by: Dan Sinclair <dsinclair@chromium.org> Commit-Queue: Ben Clayton <bclayton@chromium.org>
This commit is contained in:
committed by
Dawn LUCI CQ
parent
59c0982426
commit
a92f4259d5
194
src/tint/transform/packed_vec3.cc
Normal file
194
src/tint/transform/packed_vec3.cc
Normal file
@@ -0,0 +1,194 @@
|
||||
// Copyright 2022 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/tint/transform/packed_vec3.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "src/tint/program_builder.h"
|
||||
#include "src/tint/sem/index_accessor_expression.h"
|
||||
#include "src/tint/sem/member_accessor_expression.h"
|
||||
#include "src/tint/sem/statement.h"
|
||||
#include "src/tint/sem/variable.h"
|
||||
#include "src/tint/utils/hashmap.h"
|
||||
#include "src/tint/utils/hashset.h"
|
||||
|
||||
TINT_INSTANTIATE_TYPEINFO(tint::transform::PackedVec3);
|
||||
TINT_INSTANTIATE_TYPEINFO(tint::transform::PackedVec3::Attribute);
|
||||
|
||||
using namespace tint::number_suffixes; // NOLINT
|
||||
|
||||
namespace tint::transform {
|
||||
|
||||
/// The PIMPL state for the PackedVec3 transform
|
||||
struct PackedVec3::State {
|
||||
/// Constructor
|
||||
/// @param c the CloneContext
|
||||
explicit State(CloneContext& c) : ctx(c) {}
|
||||
|
||||
/// Runs the transform
|
||||
void Run() {
|
||||
// Packed vec3<T> struct members
|
||||
utils::Hashset<const sem::StructMember*, 8> members;
|
||||
|
||||
// Find all the packed vector struct members, and apply the @internal(packed_vector)
|
||||
// attribute.
|
||||
for (auto* decl : ctx.src->AST().GlobalDeclarations()) {
|
||||
if (auto* str = sem.Get<sem::Struct>(decl)) {
|
||||
if (str->IsHostShareable()) {
|
||||
for (auto* member : str->Members()) {
|
||||
if (auto* vec = member->Type()->As<sem::Vector>()) {
|
||||
if (vec->Width() == 3) {
|
||||
members.Add(member);
|
||||
|
||||
// Apply the PackedVec3::Attribute to the member
|
||||
auto* member_decl = member->Declaration();
|
||||
auto name = ctx.Clone(member_decl->symbol);
|
||||
auto* type = ctx.Clone(member_decl->type);
|
||||
utils::Vector<const ast::Attribute*, 4> attrs{
|
||||
b.ASTNodes().Create<Attribute>(b.ID(), b.AllocateNodeID()),
|
||||
};
|
||||
for (auto* attr : member_decl->attributes) {
|
||||
attrs.Push(ctx.Clone(attr));
|
||||
}
|
||||
ctx.Replace(member_decl, b.Member(name, type, std::move(attrs)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Walk the nodes, starting with the most deeply nested, finding all the AST expressions
|
||||
// that load a whole packed vector (not a scalar / swizzle of the vector).
|
||||
utils::Hashset<const sem::Expression*, 16> refs;
|
||||
for (auto* node : ctx.src->ASTNodes().Objects()) {
|
||||
Switch(
|
||||
sem.Get(node), //
|
||||
[&](const sem::StructMemberAccess* access) {
|
||||
if (members.Contains(access->Member())) {
|
||||
// Access to a packed vector member. Seed the expression tracking.
|
||||
refs.Add(access);
|
||||
}
|
||||
},
|
||||
[&](const sem::IndexAccessorExpression* access) {
|
||||
// Not loading a whole packed vector. Ignore.
|
||||
refs.Remove(access->Object());
|
||||
},
|
||||
[&](const sem::Swizzle* access) {
|
||||
// Not loading a whole packed vector. Ignore.
|
||||
refs.Remove(access->Object());
|
||||
},
|
||||
[&](const sem::VariableUser* user) {
|
||||
auto* v = user->Variable();
|
||||
if (v->Declaration()->Is<ast::Let>() && // if variable is let...
|
||||
v->Type()->Is<sem::Pointer>() && // and let is a pointer...
|
||||
refs.Contains(v->Initializer())) { // and pointer is to a packed vector...
|
||||
refs.Add(user); // then propagate tracking to pointer usage
|
||||
}
|
||||
},
|
||||
[&](const sem::Expression* expr) {
|
||||
if (auto* unary = expr->Declaration()->As<ast::UnaryOpExpression>()) {
|
||||
if (unary->op == ast::UnaryOp::kAddressOf ||
|
||||
unary->op == ast::UnaryOp::kIndirection) {
|
||||
// Memory access on the packed vector. Track these.
|
||||
auto* inner = sem.Get(unary->expr);
|
||||
if (refs.Remove(inner)) {
|
||||
refs.Add(expr);
|
||||
}
|
||||
}
|
||||
// Note: non-memory ops (e.g. '-') are ignored, leaving any tracked
|
||||
// reference at the inner expression, so we'd cast, then apply the unary op.
|
||||
}
|
||||
},
|
||||
[&](const sem::Statement* e) {
|
||||
if (auto* assign = e->Declaration()->As<ast::AssignmentStatement>()) {
|
||||
// We don't want to cast packed_vectors if they're being assigned to.
|
||||
refs.Remove(sem.Get(assign->lhs));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Wrap the load expressions with a cast to the unpacked type.
|
||||
utils::Hashmap<const sem::Vector*, Symbol, 3> unpack_fns;
|
||||
for (auto* ref : refs) {
|
||||
// ref is either a packed vec3 that needs casting, or a pointer to a vec3 which we just
|
||||
// leave alone.
|
||||
if (auto* vec_ty = ref->Type()->UnwrapRef()->As<sem::Vector>()) {
|
||||
auto* expr = ref->Declaration();
|
||||
ctx.Replace(expr, [this, vec_ty, expr] { //
|
||||
auto* packed = ctx.CloneWithoutTransform(expr);
|
||||
return b.Construct(CreateASTTypeFor(ctx, vec_ty), packed);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
ctx.Clone();
|
||||
}
|
||||
|
||||
/// @returns true if this transform should be run for the given program
|
||||
/// @param program the program to inspect
|
||||
static bool ShouldRun(const Program* program) {
|
||||
for (auto* decl : program->AST().GlobalDeclarations()) {
|
||||
if (auto* str = program->Sem().Get<sem::Struct>(decl)) {
|
||||
if (str->IsHostShareable()) {
|
||||
for (auto* member : str->Members()) {
|
||||
if (auto* vec = member->Type()->As<sem::Vector>()) {
|
||||
if (vec->Width() == 3) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
/// The clone context
|
||||
CloneContext& ctx;
|
||||
/// Alias to the semantic info in ctx.src
|
||||
const sem::Info& sem = ctx.src->Sem();
|
||||
/// Alias to the symbols in ctx.src
|
||||
const SymbolTable& sym = ctx.src->Symbols();
|
||||
/// Alias to the ctx.dst program builder
|
||||
ProgramBuilder& b = *ctx.dst;
|
||||
};
|
||||
|
||||
PackedVec3::Attribute::Attribute(ProgramID pid, ast::NodeID nid) : Base(pid, nid) {}
|
||||
PackedVec3::Attribute::~Attribute() = default;
|
||||
|
||||
const PackedVec3::Attribute* PackedVec3::Attribute::Clone(CloneContext* ctx) const {
|
||||
return ctx->dst->ASTNodes().Create<Attribute>(ctx->dst->ID(), ctx->dst->AllocateNodeID());
|
||||
}
|
||||
|
||||
std::string PackedVec3::Attribute::InternalName() const {
|
||||
return "packed_vector";
|
||||
}
|
||||
|
||||
PackedVec3::PackedVec3() = default;
|
||||
PackedVec3::~PackedVec3() = default;
|
||||
|
||||
bool PackedVec3::ShouldRun(const Program* program, const DataMap&) const {
|
||||
return State::ShouldRun(program);
|
||||
}
|
||||
|
||||
void PackedVec3::Run(CloneContext& ctx, const DataMap&, DataMap&) const {
|
||||
State(ctx).Run();
|
||||
}
|
||||
|
||||
} // namespace tint::transform
|
||||
78
src/tint/transform/packed_vec3.h
Normal file
78
src/tint/transform/packed_vec3.h
Normal file
@@ -0,0 +1,78 @@
|
||||
// Copyright 2022 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_TINT_TRANSFORM_PACKED_VEC3_H_
|
||||
#define SRC_TINT_TRANSFORM_PACKED_VEC3_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "src/tint/ast/internal_attribute.h"
|
||||
#include "src/tint/transform/transform.h"
|
||||
|
||||
namespace tint::transform {
|
||||
|
||||
/// A transform to be used by the MSL backend which will:
|
||||
/// * Apply the `@internal('packed_vector')` attribute (PackedVec3::Attribute) to all host-sharable
|
||||
/// structure members that have a vec3<T> type.
|
||||
/// * Cast all direct (not sub-accessed) loads of these packed vectors to the 'unpacked' vec3<T>
|
||||
/// type before usage.
|
||||
///
|
||||
/// This transform papers over overload holes in the MSL standard library where an MSL
|
||||
/// `packed_vector` type cannot be interchangable used as a regular `vec` type.
|
||||
class PackedVec3 final : public Castable<PackedVec3, Transform> {
|
||||
public:
|
||||
/// Attribute is the attribute applied to padded vector structure members.
|
||||
class Attribute final : public Castable<Attribute, ast::InternalAttribute> {
|
||||
public:
|
||||
/// Constructor
|
||||
/// @param pid the identifier of the program that owns this node
|
||||
/// @param nid the unique node identifier
|
||||
Attribute(ProgramID pid, ast::NodeID nid);
|
||||
/// Destructor
|
||||
~Attribute() override;
|
||||
|
||||
/// @returns "packed_vector".
|
||||
std::string InternalName() const override;
|
||||
|
||||
/// Performs a deep clone of this object using the CloneContext `ctx`.
|
||||
/// @param ctx the clone context
|
||||
/// @return the newly cloned object
|
||||
const Attribute* Clone(CloneContext* ctx) const override;
|
||||
};
|
||||
|
||||
/// Constructor
|
||||
PackedVec3();
|
||||
/// Destructor
|
||||
~PackedVec3() override;
|
||||
|
||||
/// @param program the program to inspect
|
||||
/// @param data optional extra transform-specific input data
|
||||
/// @returns true if this transform should be run for the given program
|
||||
bool ShouldRun(const Program* program, const DataMap& data = {}) const override;
|
||||
|
||||
private:
|
||||
struct State;
|
||||
|
||||
/// Runs the transform using the CloneContext built for transforming a
|
||||
/// program. Run() is responsible for calling Clone() on the CloneContext.
|
||||
/// @param ctx the CloneContext primed with the input program and
|
||||
/// ProgramBuilder
|
||||
/// @param inputs optional extra transform-specific input data
|
||||
/// @param outputs optional extra transform-specific output data
|
||||
void Run(CloneContext& ctx, const DataMap& inputs, DataMap& outputs) const override;
|
||||
};
|
||||
|
||||
} // namespace tint::transform
|
||||
|
||||
#endif // SRC_TINT_TRANSFORM_PACKED_VEC3_H_
|
||||
662
src/tint/transform/packed_vec3_test.cc
Normal file
662
src/tint/transform/packed_vec3_test.cc
Normal file
@@ -0,0 +1,662 @@
|
||||
// Copyright 2022 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/tint/transform/packed_vec3.h"
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "src/tint/transform/test_helper.h"
|
||||
#include "src/tint/utils/string.h"
|
||||
|
||||
namespace tint::transform {
|
||||
namespace {
|
||||
|
||||
using PackedVec3Test = TransformTest;
|
||||
|
||||
TEST_F(PackedVec3Test, ShouldRun_EmptyModule) {
|
||||
auto* src = R"()";
|
||||
|
||||
EXPECT_FALSE(ShouldRun<PackedVec3>(src));
|
||||
}
|
||||
|
||||
TEST_F(PackedVec3Test, ShouldRun_NonHostSharableStruct) {
|
||||
auto* src = R"(
|
||||
struct S {
|
||||
v : vec3<f32>,
|
||||
}
|
||||
|
||||
fn f() {
|
||||
var v : S; // function address-space - not host sharable
|
||||
}
|
||||
)";
|
||||
|
||||
EXPECT_FALSE(ShouldRun<PackedVec3>(src));
|
||||
}
|
||||
|
||||
TEST_F(PackedVec3Test, ShouldRun_Vec4Vec2) {
|
||||
auto* src = R"(
|
||||
struct S {
|
||||
v4 : vec4<f32>,
|
||||
v2 : vec2<f32>,
|
||||
}
|
||||
|
||||
@group(0) @binding(0) var<uniform> P : S; // Host sharable
|
||||
)";
|
||||
|
||||
EXPECT_FALSE(ShouldRun<PackedVec3>(src));
|
||||
}
|
||||
|
||||
TEST_F(PackedVec3Test, ShouldRun_HostSharableStruct) {
|
||||
auto* src = R"(
|
||||
struct S {
|
||||
v : vec3<f32>,
|
||||
}
|
||||
|
||||
@group(0) @binding(0) var<uniform> P : S; // Host sharable
|
||||
)";
|
||||
|
||||
EXPECT_TRUE(ShouldRun<PackedVec3>(src));
|
||||
}
|
||||
|
||||
TEST_F(PackedVec3Test, UniformAddressSpace) {
|
||||
auto* src = R"(
|
||||
struct S {
|
||||
v : vec3<f32>,
|
||||
}
|
||||
|
||||
@group(0) @binding(0) var<uniform> P : S;
|
||||
|
||||
fn f() {
|
||||
let x = P.v;
|
||||
}
|
||||
)";
|
||||
|
||||
auto* expect = R"(
|
||||
struct S {
|
||||
@internal(packed_vector)
|
||||
v : vec3<f32>,
|
||||
}
|
||||
|
||||
@group(0) @binding(0) var<uniform> P : S;
|
||||
|
||||
fn f() {
|
||||
let x = vec3<f32>(P.v);
|
||||
}
|
||||
)";
|
||||
|
||||
DataMap data;
|
||||
auto got = Run<PackedVec3>(src, data);
|
||||
|
||||
EXPECT_EQ(expect, str(got));
|
||||
}
|
||||
|
||||
TEST_F(PackedVec3Test, StorageAddressSpace) {
|
||||
auto* src = R"(
|
||||
struct S {
|
||||
v : vec3<f32>,
|
||||
}
|
||||
|
||||
@group(0) @binding(0) var<storage> P : S;
|
||||
|
||||
fn f() {
|
||||
let x = P.v;
|
||||
}
|
||||
)";
|
||||
|
||||
auto* expect = R"(
|
||||
struct S {
|
||||
@internal(packed_vector)
|
||||
v : vec3<f32>,
|
||||
}
|
||||
|
||||
@group(0) @binding(0) var<storage> P : S;
|
||||
|
||||
fn f() {
|
||||
let x = vec3<f32>(P.v);
|
||||
}
|
||||
)";
|
||||
|
||||
DataMap data;
|
||||
auto got = Run<PackedVec3>(src, data);
|
||||
|
||||
EXPECT_EQ(expect, str(got));
|
||||
}
|
||||
|
||||
TEST_F(PackedVec3Test, ExistingMemberAttributes) {
|
||||
auto* src = R"(
|
||||
struct S {
|
||||
@align(32) @size(64) v : vec3<f32>,
|
||||
}
|
||||
|
||||
@group(0) @binding(0) var<storage> P : S;
|
||||
|
||||
fn f() {
|
||||
let x = P.v;
|
||||
}
|
||||
)";
|
||||
|
||||
auto* expect = R"(
|
||||
struct S {
|
||||
@internal(packed_vector) @align(32) @size(64)
|
||||
v : vec3<f32>,
|
||||
}
|
||||
|
||||
@group(0) @binding(0) var<storage> P : S;
|
||||
|
||||
fn f() {
|
||||
let x = vec3<f32>(P.v);
|
||||
}
|
||||
)";
|
||||
|
||||
DataMap data;
|
||||
auto got = Run<PackedVec3>(src, data);
|
||||
|
||||
EXPECT_EQ(expect, str(got));
|
||||
}
|
||||
|
||||
TEST_F(PackedVec3Test, MultipleVectors) {
|
||||
auto* src = R"(
|
||||
struct S {
|
||||
v2_a : vec2<f32>,
|
||||
v3_a : vec3<f32>,
|
||||
v4_a : vec4<f32>,
|
||||
v2_b : vec2<f32>,
|
||||
v3_b : vec3<f32>,
|
||||
v4_b : vec4<f32>,
|
||||
}
|
||||
|
||||
@group(0) @binding(0) var<storage> P : S;
|
||||
|
||||
fn f() {
|
||||
let v2_a = P.v2_a;
|
||||
let v3_a = P.v3_a;
|
||||
let v4_a = P.v4_a;
|
||||
let v2_b = P.v2_b;
|
||||
let v3_b = P.v3_b;
|
||||
let v4_b = P.v4_b;
|
||||
}
|
||||
)";
|
||||
|
||||
auto* expect = R"(
|
||||
struct S {
|
||||
v2_a : vec2<f32>,
|
||||
@internal(packed_vector)
|
||||
v3_a : vec3<f32>,
|
||||
v4_a : vec4<f32>,
|
||||
v2_b : vec2<f32>,
|
||||
@internal(packed_vector)
|
||||
v3_b : vec3<f32>,
|
||||
v4_b : vec4<f32>,
|
||||
}
|
||||
|
||||
@group(0) @binding(0) var<storage> P : S;
|
||||
|
||||
fn f() {
|
||||
let v2_a = P.v2_a;
|
||||
let v3_a = vec3<f32>(P.v3_a);
|
||||
let v4_a = P.v4_a;
|
||||
let v2_b = P.v2_b;
|
||||
let v3_b = vec3<f32>(P.v3_b);
|
||||
let v4_b = P.v4_b;
|
||||
}
|
||||
)";
|
||||
|
||||
DataMap data;
|
||||
auto got = Run<PackedVec3>(src, data);
|
||||
|
||||
EXPECT_EQ(expect, str(got));
|
||||
}
|
||||
|
||||
TEST_F(PackedVec3Test, MixedAddressSpace) {
|
||||
auto* src = R"(
|
||||
struct S {
|
||||
v : vec3<f32>,
|
||||
}
|
||||
|
||||
@group(0) @binding(0) var<storage> P : S;
|
||||
|
||||
fn f() {
|
||||
var f : S;
|
||||
let x = f.v;
|
||||
}
|
||||
)";
|
||||
|
||||
auto* expect = R"(
|
||||
struct S {
|
||||
@internal(packed_vector)
|
||||
v : vec3<f32>,
|
||||
}
|
||||
|
||||
@group(0) @binding(0) var<storage> P : S;
|
||||
|
||||
fn f() {
|
||||
var f : S;
|
||||
let x = vec3<f32>(f.v);
|
||||
}
|
||||
)";
|
||||
|
||||
DataMap data;
|
||||
auto got = Run<PackedVec3>(src, data);
|
||||
|
||||
EXPECT_EQ(expect, str(got));
|
||||
}
|
||||
|
||||
TEST_F(PackedVec3Test, ReadMemberAccessChain) {
|
||||
auto* src = R"(
|
||||
struct S {
|
||||
v : vec3<f32>,
|
||||
}
|
||||
|
||||
@group(0) @binding(0) var<storage> P : S;
|
||||
|
||||
fn f() {
|
||||
let x = P.v.yz.x;
|
||||
}
|
||||
)";
|
||||
|
||||
auto* expect = R"(
|
||||
struct S {
|
||||
@internal(packed_vector)
|
||||
v : vec3<f32>,
|
||||
}
|
||||
|
||||
@group(0) @binding(0) var<storage> P : S;
|
||||
|
||||
fn f() {
|
||||
let x = P.v.yz.x;
|
||||
}
|
||||
)";
|
||||
|
||||
DataMap data;
|
||||
auto got = Run<PackedVec3>(src, data);
|
||||
|
||||
EXPECT_EQ(expect, str(got));
|
||||
}
|
||||
|
||||
TEST_F(PackedVec3Test, ReadVector) {
|
||||
auto* src = R"(
|
||||
struct S {
|
||||
v : vec3<f32>,
|
||||
}
|
||||
|
||||
@group(0) @binding(0) var<storage> P : S;
|
||||
|
||||
fn f() {
|
||||
let x = P.v;
|
||||
}
|
||||
)";
|
||||
|
||||
auto* expect = R"(
|
||||
struct S {
|
||||
@internal(packed_vector)
|
||||
v : vec3<f32>,
|
||||
}
|
||||
|
||||
@group(0) @binding(0) var<storage> P : S;
|
||||
|
||||
fn f() {
|
||||
let x = vec3<f32>(P.v);
|
||||
}
|
||||
)";
|
||||
|
||||
DataMap data;
|
||||
auto got = Run<PackedVec3>(src, data);
|
||||
|
||||
EXPECT_EQ(expect, str(got));
|
||||
}
|
||||
|
||||
TEST_F(PackedVec3Test, ReadIndexAccessor) {
|
||||
auto* src = R"(
|
||||
struct S {
|
||||
v : vec3<f32>,
|
||||
}
|
||||
|
||||
@group(0) @binding(0) var<storage> P : S;
|
||||
|
||||
fn f() {
|
||||
let x = P.v[1];
|
||||
}
|
||||
)";
|
||||
|
||||
auto* expect = R"(
|
||||
struct S {
|
||||
@internal(packed_vector)
|
||||
v : vec3<f32>,
|
||||
}
|
||||
|
||||
@group(0) @binding(0) var<storage> P : S;
|
||||
|
||||
fn f() {
|
||||
let x = P.v[1];
|
||||
}
|
||||
)";
|
||||
|
||||
DataMap data;
|
||||
auto got = Run<PackedVec3>(src, data);
|
||||
|
||||
EXPECT_EQ(expect, str(got));
|
||||
}
|
||||
|
||||
TEST_F(PackedVec3Test, ReadViaStructPtrDirect) {
|
||||
auto* src = R"(
|
||||
struct S {
|
||||
v : vec3<f32>,
|
||||
}
|
||||
|
||||
@group(0) @binding(0) var<storage> P : S;
|
||||
|
||||
fn f() {
|
||||
let x = (*(&(*(&P)))).v;
|
||||
}
|
||||
)";
|
||||
|
||||
auto* expect = R"(
|
||||
struct S {
|
||||
@internal(packed_vector)
|
||||
v : vec3<f32>,
|
||||
}
|
||||
|
||||
@group(0) @binding(0) var<storage> P : S;
|
||||
|
||||
fn f() {
|
||||
let x = vec3<f32>((*(&(*(&(P))))).v);
|
||||
}
|
||||
)";
|
||||
|
||||
DataMap data;
|
||||
auto got = Run<PackedVec3>(src, data);
|
||||
|
||||
EXPECT_EQ(expect, str(got));
|
||||
}
|
||||
|
||||
TEST_F(PackedVec3Test, ReadViaVectorPtrDirect) {
|
||||
auto* src = R"(
|
||||
struct S {
|
||||
v : vec3<f32>,
|
||||
}
|
||||
|
||||
@group(0) @binding(0) var<storage> P : S;
|
||||
|
||||
fn f() {
|
||||
let x = *(&(*(&(P.v))));
|
||||
}
|
||||
)";
|
||||
|
||||
auto* expect = R"(
|
||||
struct S {
|
||||
@internal(packed_vector)
|
||||
v : vec3<f32>,
|
||||
}
|
||||
|
||||
@group(0) @binding(0) var<storage> P : S;
|
||||
|
||||
fn f() {
|
||||
let x = vec3<f32>(*(&(*(&(P.v)))));
|
||||
}
|
||||
)";
|
||||
|
||||
DataMap data;
|
||||
auto got = Run<PackedVec3>(src, data);
|
||||
|
||||
EXPECT_EQ(expect, str(got));
|
||||
}
|
||||
|
||||
TEST_F(PackedVec3Test, ReadViaStructPtrViaLet) {
|
||||
auto* src = R"(
|
||||
struct S {
|
||||
v : vec3<f32>,
|
||||
}
|
||||
|
||||
@group(0) @binding(0) var<storage> P : S;
|
||||
|
||||
fn f() {
|
||||
let p0 = &P;
|
||||
let p1 = &(*(p0));
|
||||
let a = (*p1).v;
|
||||
let p2 = &(*(p1));
|
||||
let b = (*p2).v;
|
||||
let c = (*p2).v;
|
||||
}
|
||||
)";
|
||||
|
||||
auto* expect = R"(
|
||||
struct S {
|
||||
@internal(packed_vector)
|
||||
v : vec3<f32>,
|
||||
}
|
||||
|
||||
@group(0) @binding(0) var<storage> P : S;
|
||||
|
||||
fn f() {
|
||||
let p0 = &(P);
|
||||
let p1 = &(*(p0));
|
||||
let a = vec3<f32>((*(p1)).v);
|
||||
let p2 = &(*(p1));
|
||||
let b = vec3<f32>((*(p2)).v);
|
||||
let c = vec3<f32>((*(p2)).v);
|
||||
}
|
||||
)";
|
||||
|
||||
DataMap data;
|
||||
auto got = Run<PackedVec3>(src, data);
|
||||
|
||||
EXPECT_EQ(expect, str(got));
|
||||
}
|
||||
|
||||
TEST_F(PackedVec3Test, ReadViaVectorPtrViaLet) {
|
||||
auto* src = R"(
|
||||
struct S {
|
||||
v : vec3<f32>,
|
||||
}
|
||||
|
||||
@group(0) @binding(0) var<storage> P : S;
|
||||
|
||||
fn f() {
|
||||
let p0 = &(P.v);
|
||||
let p1 = &(*(p0));
|
||||
let a = *p1;
|
||||
let p2 = &(*(p1));
|
||||
let b = *p2;
|
||||
let c = *p2;
|
||||
}
|
||||
)";
|
||||
|
||||
auto* expect = R"(
|
||||
struct S {
|
||||
@internal(packed_vector)
|
||||
v : vec3<f32>,
|
||||
}
|
||||
|
||||
@group(0) @binding(0) var<storage> P : S;
|
||||
|
||||
fn f() {
|
||||
let p0 = &(P.v);
|
||||
let p1 = &(*(p0));
|
||||
let a = vec3<f32>(*(p1));
|
||||
let p2 = &(*(p1));
|
||||
let b = vec3<f32>(*(p2));
|
||||
let c = vec3<f32>(*(p2));
|
||||
}
|
||||
)";
|
||||
|
||||
DataMap data;
|
||||
auto got = Run<PackedVec3>(src, data);
|
||||
|
||||
EXPECT_EQ(expect, str(got));
|
||||
}
|
||||
|
||||
TEST_F(PackedVec3Test, ReadUnaryOp) {
|
||||
auto* src = R"(
|
||||
struct S {
|
||||
v : vec3<f32>,
|
||||
}
|
||||
|
||||
@group(0) @binding(0) var<storage> P : S;
|
||||
|
||||
fn f() {
|
||||
let x = -P.v;
|
||||
}
|
||||
)";
|
||||
|
||||
auto* expect = R"(
|
||||
struct S {
|
||||
@internal(packed_vector)
|
||||
v : vec3<f32>,
|
||||
}
|
||||
|
||||
@group(0) @binding(0) var<storage> P : S;
|
||||
|
||||
fn f() {
|
||||
let x = -(vec3<f32>(P.v));
|
||||
}
|
||||
)";
|
||||
|
||||
DataMap data;
|
||||
auto got = Run<PackedVec3>(src, data);
|
||||
|
||||
EXPECT_EQ(expect, str(got));
|
||||
}
|
||||
|
||||
TEST_F(PackedVec3Test, ReadBinaryOp) {
|
||||
auto* src = R"(
|
||||
struct S {
|
||||
v : vec3<f32>,
|
||||
}
|
||||
|
||||
@group(0) @binding(0) var<storage> P : S;
|
||||
|
||||
fn f() {
|
||||
let x = P.v + P.v;
|
||||
}
|
||||
)";
|
||||
|
||||
auto* expect = R"(
|
||||
struct S {
|
||||
@internal(packed_vector)
|
||||
v : vec3<f32>,
|
||||
}
|
||||
|
||||
@group(0) @binding(0) var<storage> P : S;
|
||||
|
||||
fn f() {
|
||||
let x = (vec3<f32>(P.v) + vec3<f32>(P.v));
|
||||
}
|
||||
)";
|
||||
|
||||
DataMap data;
|
||||
auto got = Run<PackedVec3>(src, data);
|
||||
|
||||
EXPECT_EQ(expect, str(got));
|
||||
}
|
||||
|
||||
TEST_F(PackedVec3Test, WriteVector) {
|
||||
auto* src = R"(
|
||||
struct S {
|
||||
v : vec3<f32>,
|
||||
}
|
||||
|
||||
@group(0) @binding(0) var<storage, read_write> P : S;
|
||||
|
||||
fn f() {
|
||||
P.v = vec3(1.23);
|
||||
}
|
||||
)";
|
||||
|
||||
auto* expect = R"(
|
||||
struct S {
|
||||
@internal(packed_vector)
|
||||
v : vec3<f32>,
|
||||
}
|
||||
|
||||
@group(0) @binding(0) var<storage, read_write> P : S;
|
||||
|
||||
fn f() {
|
||||
P.v = vec3(1.23);
|
||||
}
|
||||
)";
|
||||
|
||||
DataMap data;
|
||||
auto got = Run<PackedVec3>(src, data);
|
||||
|
||||
EXPECT_EQ(expect, str(got));
|
||||
}
|
||||
|
||||
TEST_F(PackedVec3Test, WriteMemberAccess) {
|
||||
auto* src = R"(
|
||||
struct S {
|
||||
v : vec3<f32>,
|
||||
}
|
||||
|
||||
@group(0) @binding(0) var<storage, read_write> P : S;
|
||||
|
||||
fn f() {
|
||||
P.v.y = 1.23;
|
||||
}
|
||||
)";
|
||||
|
||||
auto* expect = R"(
|
||||
struct S {
|
||||
@internal(packed_vector)
|
||||
v : vec3<f32>,
|
||||
}
|
||||
|
||||
@group(0) @binding(0) var<storage, read_write> P : S;
|
||||
|
||||
fn f() {
|
||||
P.v.y = 1.23;
|
||||
}
|
||||
)";
|
||||
|
||||
DataMap data;
|
||||
auto got = Run<PackedVec3>(src, data);
|
||||
|
||||
EXPECT_EQ(expect, str(got));
|
||||
}
|
||||
|
||||
TEST_F(PackedVec3Test, WriteIndexAccessor) {
|
||||
auto* src = R"(
|
||||
struct S {
|
||||
v : vec3<f32>,
|
||||
}
|
||||
|
||||
@group(0) @binding(0) var<storage, read_write> P : S;
|
||||
|
||||
fn f() {
|
||||
P.v[1] = 1.23;
|
||||
}
|
||||
)";
|
||||
|
||||
auto* expect = R"(
|
||||
struct S {
|
||||
@internal(packed_vector)
|
||||
v : vec3<f32>,
|
||||
}
|
||||
|
||||
@group(0) @binding(0) var<storage, read_write> P : S;
|
||||
|
||||
fn f() {
|
||||
P.v[1] = 1.23;
|
||||
}
|
||||
)";
|
||||
|
||||
DataMap data;
|
||||
auto got = Run<PackedVec3>(src, data);
|
||||
|
||||
EXPECT_EQ(expect, str(got));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace tint::transform
|
||||
Reference in New Issue
Block a user