mirror of
https://github.com/encounter/dawn-cmake.git
synced 2025-06-05 06:03:34 +00:00
tint: Add StructMember attributes to sem.
Removes the need to examine AST attributes. Change-Id: Iaaa6b10fd56baf732057c4c3960c1bfc5bbdeaa6 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/129621 Reviewed-by: Dan Sinclair <dsinclair@chromium.org> Commit-Queue: Dan Sinclair <dsinclair@chromium.org> Kokoro: Kokoro <noreply+kokoro@google.com>
This commit is contained in:
parent
333cea405c
commit
576ba1c493
@ -747,6 +747,7 @@ libtint_source_set("libtint_builtins_src") {
|
||||
"builtin/extension.h",
|
||||
"builtin/function.cc",
|
||||
"builtin/function.h",
|
||||
"builtin/interpolation.h",
|
||||
"builtin/interpolation_sampling.cc",
|
||||
"builtin/interpolation_sampling.h",
|
||||
"builtin/interpolation_type.cc",
|
||||
|
33
src/tint/builtin/interpolation.h
Normal file
33
src/tint/builtin/interpolation.h
Normal file
@ -0,0 +1,33 @@
|
||||
// Copyright 2023 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_BUILTIN_INTERPOLATION_H_
|
||||
#define SRC_TINT_BUILTIN_INTERPOLATION_H_
|
||||
|
||||
#include "src/tint/builtin/interpolation_sampling.h"
|
||||
#include "src/tint/builtin/interpolation_type.h"
|
||||
|
||||
namespace tint::builtin {
|
||||
|
||||
/// The values of an `@interpolate` attribute
|
||||
struct Interpolation {
|
||||
/// The first argument of a `@interpolate` attribute
|
||||
builtin::InterpolationType type = builtin::InterpolationType::kUndefined;
|
||||
/// The second argument of a `@interpolate` attribute
|
||||
builtin::InterpolationSampling sampling = builtin::InterpolationSampling::kUndefined;
|
||||
};
|
||||
|
||||
} // namespace tint::builtin
|
||||
|
||||
#endif // SRC_TINT_BUILTIN_INTERPOLATION_H_
|
@ -621,8 +621,8 @@ void Inspector::AddEntryPointInOutVariables(std::string name,
|
||||
// Recurse into members.
|
||||
for (auto* member : struct_ty->Members()) {
|
||||
AddEntryPointInOutVariables(name + "." + member->Name().Name(), member->Type(),
|
||||
member->Declaration()->attributes, member->Location(),
|
||||
variables);
|
||||
member->Declaration()->attributes,
|
||||
member->Attributes().location, variables);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -53,7 +53,7 @@ sem::Struct* BuildStruct(ProgramBuilder& b,
|
||||
/* offset */ offset,
|
||||
/* align */ align,
|
||||
/* size */ size,
|
||||
/* location */ std::nullopt));
|
||||
/* attributes */ type::StructMemberAttributes{}));
|
||||
offset += size;
|
||||
}
|
||||
uint32_t size_without_padding = offset;
|
||||
|
@ -150,11 +150,12 @@ TEST_F(ResolverInferredTypeTest, InferStruct_Pass) {
|
||||
auto* member = Member("x", ty.i32());
|
||||
auto* str = Structure("S", utils::Vector{member});
|
||||
|
||||
auto* expected_type = create<sem::Struct>(
|
||||
str, str->source, str->name->symbol,
|
||||
utils::Vector{create<sem::StructMember>(member, member->source, member->name->symbol,
|
||||
create<type::I32>(), 0u, 0u, 0u, 4u, std::nullopt)},
|
||||
0u, 4u, 4u);
|
||||
auto* expected_type =
|
||||
create<sem::Struct>(str, str->source, str->name->symbol,
|
||||
utils::Vector{create<sem::StructMember>(
|
||||
member, member->source, member->name->symbol, create<type::I32>(),
|
||||
0u, 0u, 0u, 4u, type::StructMemberAttributes{})},
|
||||
0u, 4u, 4u);
|
||||
|
||||
auto* ctor_expr = Call(ty.Of(str));
|
||||
|
||||
|
@ -3721,14 +3721,22 @@ bool Resolver::StrideAttribute(const ast::StrideAttribute*) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Resolver::InterpolateAttribute(const ast::InterpolateAttribute* attr) {
|
||||
if (!InterpolationType(attr->type)) {
|
||||
return false;
|
||||
utils::Result<builtin::Interpolation> Resolver::InterpolateAttribute(
|
||||
const ast::InterpolateAttribute* attr) {
|
||||
builtin::Interpolation out;
|
||||
auto* type = InterpolationType(attr->type);
|
||||
if (!type) {
|
||||
return utils::Failure;
|
||||
}
|
||||
if (attr->sampling && !InterpolationSampling(attr->sampling)) {
|
||||
return false;
|
||||
out.type = type->Value();
|
||||
if (attr->sampling) {
|
||||
auto* sampling = InterpolationSampling(attr->sampling);
|
||||
if (!sampling) {
|
||||
return utils::Failure;
|
||||
}
|
||||
out.sampling = sampling->Value();
|
||||
}
|
||||
return true;
|
||||
return out;
|
||||
}
|
||||
|
||||
bool Resolver::InternalAttribute(const ast::InternalAttribute* attr) {
|
||||
@ -4021,7 +4029,7 @@ sem::Struct* Resolver::Structure(const ast::Struct* str) {
|
||||
bool has_offset_attr = false;
|
||||
bool has_align_attr = false;
|
||||
bool has_size_attr = false;
|
||||
std::optional<uint32_t> location;
|
||||
type::StructMemberAttributes attributes;
|
||||
for (auto* attribute : member->attributes) {
|
||||
Mark(attribute);
|
||||
bool ok = Switch(
|
||||
@ -4122,12 +4130,32 @@ sem::Struct* Resolver::Structure(const ast::Struct* str) {
|
||||
if (!value) {
|
||||
return false;
|
||||
}
|
||||
location = value.Get();
|
||||
attributes.location = value.Get();
|
||||
return true;
|
||||
},
|
||||
[&](const ast::BuiltinAttribute* attr) {
|
||||
auto value = BuiltinAttribute(attr);
|
||||
if (!value) {
|
||||
return false;
|
||||
}
|
||||
attributes.builtin = value.Get();
|
||||
return true;
|
||||
},
|
||||
[&](const ast::InterpolateAttribute* attr) {
|
||||
auto value = InterpolateAttribute(attr);
|
||||
if (!value) {
|
||||
return false;
|
||||
}
|
||||
attributes.interpolation = value.Get();
|
||||
return true;
|
||||
},
|
||||
[&](const ast::InvariantAttribute* attr) {
|
||||
if (!InvariantAttribute(attr)) {
|
||||
return false;
|
||||
}
|
||||
attributes.invariant = true;
|
||||
return true;
|
||||
},
|
||||
[&](const ast::BuiltinAttribute* attr) -> bool { return BuiltinAttribute(attr); },
|
||||
[&](const ast::InterpolateAttribute* attr) { return InterpolateAttribute(attr); },
|
||||
[&](const ast::InvariantAttribute* attr) { return InvariantAttribute(attr); },
|
||||
[&](const ast::StrideAttribute* attr) {
|
||||
if (validator_.IsValidationEnabled(
|
||||
member->attributes, ast::DisabledValidation::kIgnoreStrideAttribute)) {
|
||||
@ -4163,7 +4191,7 @@ sem::Struct* Resolver::Structure(const ast::Struct* str) {
|
||||
auto* sem_member = builder_->create<sem::StructMember>(
|
||||
member, member->source, member->name->symbol, type,
|
||||
static_cast<uint32_t>(sem_members.Length()), static_cast<uint32_t>(offset),
|
||||
static_cast<uint32_t>(align), static_cast<uint32_t>(size), location);
|
||||
static_cast<uint32_t>(align), static_cast<uint32_t>(size), attributes);
|
||||
builder_->Sem().Add(member, sem_member);
|
||||
sem_members.Push(sem_member);
|
||||
|
||||
|
@ -354,7 +354,8 @@ class Resolver {
|
||||
|
||||
/// Resolves the `@interpolate` attribute @p attr
|
||||
/// @returns true on success, false on failure
|
||||
bool InterpolateAttribute(const ast::InterpolateAttribute* attr);
|
||||
utils::Result<builtin::Interpolation> InterpolateAttribute(
|
||||
const ast::InterpolateAttribute* attr);
|
||||
|
||||
/// Resolves the internal attribute @p attr
|
||||
/// @returns true on success, false on failure
|
||||
|
@ -187,7 +187,7 @@ TEST_F(ResolverPipelineStageUseTest, StructUsedAsShaderParamLocationSet) {
|
||||
auto* sem = TypeOf(s)->As<sem::Struct>();
|
||||
ASSERT_NE(sem, nullptr);
|
||||
ASSERT_EQ(1u, sem->Members().Length());
|
||||
EXPECT_EQ(3u, sem->Members()[0]->Location());
|
||||
EXPECT_EQ(3u, sem->Members()[0]->Attributes().location);
|
||||
}
|
||||
|
||||
TEST_F(ResolverPipelineStageUseTest, StructUsedAsShaderReturnTypeViaAlias) {
|
||||
@ -217,7 +217,7 @@ TEST_F(ResolverPipelineStageUseTest, StructUsedAsShaderReturnTypeLocationSet) {
|
||||
auto* sem = TypeOf(s)->As<sem::Struct>();
|
||||
ASSERT_NE(sem, nullptr);
|
||||
ASSERT_EQ(1u, sem->Members().Length());
|
||||
EXPECT_EQ(3u, sem->Members()[0]->Location());
|
||||
EXPECT_EQ(3u, sem->Members()[0]->Attributes().location);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
@ -1215,7 +1215,7 @@ bool Validator::EntryPoint(const sem::Function* func, ast::PipelineStage stage)
|
||||
if (!validate_entry_point_attributes_inner(
|
||||
member->Declaration()->attributes, member->Type(), member->Source(),
|
||||
param_or_ret,
|
||||
/*is_struct_member*/ true, member->Location())) {
|
||||
/*is_struct_member*/ true, member->Attributes().location)) {
|
||||
AddNote("while analyzing entry point '" + decl->name->symbol.Name() + "'",
|
||||
decl->source);
|
||||
return false;
|
||||
@ -2105,9 +2105,9 @@ bool Validator::Structure(const sem::Struct* str, ast::PipelineStage stage) cons
|
||||
},
|
||||
[&](const ast::LocationAttribute* location) {
|
||||
has_location = true;
|
||||
TINT_ASSERT(Resolver, member->Location().has_value());
|
||||
if (!LocationAttribute(location, member->Location().value(), member->Type(),
|
||||
locations, stage, member->Source())) {
|
||||
TINT_ASSERT(Resolver, member->Attributes().location.has_value());
|
||||
if (!LocationAttribute(location, member->Attributes().location.value(),
|
||||
member->Type(), locations, stage, member->Source())) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
@ -40,8 +40,8 @@ StructMember::StructMember(const ast::StructMember* declaration,
|
||||
uint32_t offset,
|
||||
uint32_t align,
|
||||
uint32_t size,
|
||||
std::optional<uint32_t> location)
|
||||
: Base(source, name, type, index, offset, align, size, location), declaration_(declaration) {}
|
||||
const type::StructMemberAttributes& attributes)
|
||||
: Base(source, name, type, index, offset, align, size, attributes), declaration_(declaration) {}
|
||||
|
||||
StructMember::~StructMember() = default;
|
||||
|
||||
|
@ -84,7 +84,7 @@ class StructMember final : public utils::Castable<StructMember, type::StructMemb
|
||||
/// @param offset the byte offset from the base of the structure
|
||||
/// @param align the byte alignment of the member
|
||||
/// @param size the byte size of the member
|
||||
/// @param location the location attribute, if present
|
||||
/// @param attributes the optional attributes
|
||||
StructMember(const ast::StructMember* declaration,
|
||||
tint::Source source,
|
||||
Symbol name,
|
||||
@ -93,7 +93,7 @@ class StructMember final : public utils::Castable<StructMember, type::StructMemb
|
||||
uint32_t offset,
|
||||
uint32_t align,
|
||||
uint32_t size,
|
||||
std::optional<uint32_t> location);
|
||||
const type::StructMemberAttributes& attributes);
|
||||
|
||||
/// Destructor
|
||||
~StructMember() override;
|
||||
|
@ -380,8 +380,8 @@ struct CanonicalizeEntryPointIO::State {
|
||||
|
||||
auto attributes =
|
||||
CloneShaderIOAttributes(member->Declaration()->attributes, do_interpolate);
|
||||
auto* input_expr =
|
||||
AddInput(name, member->Type(), member->Location(), std::move(attributes));
|
||||
auto* input_expr = AddInput(name, member->Type(), member->Attributes().location,
|
||||
std::move(attributes));
|
||||
inner_struct_values.Push(input_expr);
|
||||
}
|
||||
|
||||
@ -410,8 +410,8 @@ struct CanonicalizeEntryPointIO::State {
|
||||
CloneShaderIOAttributes(member->Declaration()->attributes, do_interpolate);
|
||||
|
||||
// Extract the original structure member.
|
||||
AddOutput(name, member->Type(), member->Location(), std::move(attributes),
|
||||
ctx.dst->MemberAccessor(original_result, name));
|
||||
AddOutput(name, member->Type(), member->Attributes().location,
|
||||
std::move(attributes), ctx.dst->MemberAccessor(original_result, name));
|
||||
}
|
||||
} else if (!inner_ret_type->Is<type::Void>()) {
|
||||
auto attributes =
|
||||
|
@ -83,6 +83,8 @@ Transform::ApplyResult TruncateInterstageVariables::Apply(const Program* src,
|
||||
auto* func_sem = sem.Get(func_ast);
|
||||
auto* str = func_sem->ReturnType()->As<sem::Struct>();
|
||||
|
||||
// This transform is run after CanonicalizeEntryPointIO transform,
|
||||
// So it is guaranteed that entry point inputs are already grouped in a struct.
|
||||
if (TINT_UNLIKELY(!str)) {
|
||||
TINT_ICE(Transform, ctx.dst->Diagnostics())
|
||||
<< "Entrypoint function return type is non-struct.\n"
|
||||
@ -91,20 +93,14 @@ Transform::ApplyResult TruncateInterstageVariables::Apply(const Program* src,
|
||||
continue;
|
||||
}
|
||||
|
||||
// This transform is run after CanonicalizeEntryPointIO transform,
|
||||
// So it is guaranteed that entry point inputs are already grouped in a struct.
|
||||
const ast::Struct* struct_ty = str->Declaration();
|
||||
|
||||
// A prepass to check if any interstage variable locations in the entry point needs
|
||||
// truncating. If not we don't really need to handle this entry point.
|
||||
utils::Hashset<const sem::StructMember*, 16u> omit_members;
|
||||
|
||||
for (auto* member : struct_ty->members) {
|
||||
if (ast::GetAttribute<ast::LocationAttribute>(member->attributes)) {
|
||||
auto* m = sem.Get(member);
|
||||
uint32_t location = m->Location().value();
|
||||
if (!data->interstage_locations.test(location)) {
|
||||
omit_members.Add(m);
|
||||
for (auto* member : str->Members()) {
|
||||
if (auto location = member->Attributes().location) {
|
||||
if (!data->interstage_locations.test(location.value())) {
|
||||
omit_members.Add(member);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -826,8 +826,8 @@ struct VertexPulling::State {
|
||||
auto* sem = src->Sem().Get(member);
|
||||
info.type = sem->Type();
|
||||
|
||||
TINT_ASSERT(Transform, sem->Location().has_value());
|
||||
location_info[sem->Location().value()] = info;
|
||||
TINT_ASSERT(Transform, sem->Attributes().location.has_value());
|
||||
location_info[sem->Attributes().location.value()] = info;
|
||||
has_locations = true;
|
||||
} else {
|
||||
auto* builtin_attr = ast::GetAttribute<ast::BuiltinAttribute>(member->attributes);
|
||||
|
@ -179,7 +179,7 @@ StructMember::StructMember(tint::Source source,
|
||||
uint32_t offset,
|
||||
uint32_t align,
|
||||
uint32_t size,
|
||||
std::optional<uint32_t> location)
|
||||
const StructMemberAttributes& attributes)
|
||||
: source_(source),
|
||||
name_(name),
|
||||
type_(type),
|
||||
@ -187,7 +187,7 @@ StructMember::StructMember(tint::Source source,
|
||||
offset_(offset),
|
||||
align_(align),
|
||||
size_(size),
|
||||
location_(location) {}
|
||||
attributes_(attributes) {}
|
||||
|
||||
StructMember::~StructMember() = default;
|
||||
|
||||
@ -195,7 +195,7 @@ StructMember* StructMember::Clone(CloneContext& ctx) const {
|
||||
auto sym = ctx.dst.st->Register(name_.Name());
|
||||
auto* ty = type_->Clone(ctx);
|
||||
return ctx.dst.mgr->Get<StructMember>(source_, sym, ty, index_, offset_, align_, size_,
|
||||
location_);
|
||||
attributes_);
|
||||
}
|
||||
|
||||
} // namespace tint::type
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include <unordered_set>
|
||||
|
||||
#include "src/tint/builtin/address_space.h"
|
||||
#include "src/tint/builtin/interpolation.h"
|
||||
#include "src/tint/symbol.h"
|
||||
#include "src/tint/type/node.h"
|
||||
#include "src/tint/type/type.h"
|
||||
@ -163,6 +164,18 @@ class Struct : public utils::Castable<Struct, Type> {
|
||||
utils::Vector<const Struct*, 2> concrete_types_;
|
||||
};
|
||||
|
||||
/// Attributes that can be applied to the StructMember
|
||||
struct StructMemberAttributes {
|
||||
/// The value of a `@location` attribute
|
||||
std::optional<uint32_t> location;
|
||||
/// The value of a `@builtin` attribute
|
||||
std::optional<builtin::BuiltinValue> builtin;
|
||||
/// The values of a `@interpolate` attribute
|
||||
std::optional<builtin::Interpolation> interpolation;
|
||||
/// True if the member was annotated with `@invariant`
|
||||
bool invariant = false;
|
||||
};
|
||||
|
||||
/// StructMember holds the type information for structure members.
|
||||
class StructMember : public utils::Castable<StructMember, Node> {
|
||||
public:
|
||||
@ -174,7 +187,7 @@ class StructMember : public utils::Castable<StructMember, Node> {
|
||||
/// @param offset the byte offset from the base of the structure
|
||||
/// @param align the byte alignment of the member
|
||||
/// @param size the byte size of the member
|
||||
/// @param location the location attribute, if present
|
||||
/// @param attributes the optional attributes
|
||||
StructMember(tint::Source source,
|
||||
Symbol name,
|
||||
const type::Type* type,
|
||||
@ -182,7 +195,7 @@ class StructMember : public utils::Castable<StructMember, Node> {
|
||||
uint32_t offset,
|
||||
uint32_t align,
|
||||
uint32_t size,
|
||||
std::optional<uint32_t> location);
|
||||
const StructMemberAttributes& attributes);
|
||||
|
||||
/// Destructor
|
||||
~StructMember() override;
|
||||
@ -215,8 +228,8 @@ class StructMember : public utils::Castable<StructMember, Node> {
|
||||
/// @returns byte size
|
||||
uint32_t Size() const { return size_; }
|
||||
|
||||
/// @returns the location, if set
|
||||
std::optional<uint32_t> Location() const { return location_; }
|
||||
/// @returns the optional attributes
|
||||
const StructMemberAttributes& Attributes() const { return attributes_; }
|
||||
|
||||
/// @param ctx the clone context
|
||||
/// @returns a clone of this struct member
|
||||
@ -231,7 +244,7 @@ class StructMember : public utils::Castable<StructMember, Node> {
|
||||
const uint32_t offset_;
|
||||
const uint32_t align_;
|
||||
const uint32_t size_;
|
||||
const std::optional<uint32_t> location_;
|
||||
const StructMemberAttributes attributes_;
|
||||
};
|
||||
|
||||
} // namespace tint::type
|
||||
|
@ -101,10 +101,8 @@ TEST_F(TypeStructTest, Location) {
|
||||
auto* sem = p.Sem().Get(st);
|
||||
ASSERT_EQ(2u, sem->Members().Length());
|
||||
|
||||
EXPECT_TRUE(sem->Members()[0]->Location().has_value());
|
||||
EXPECT_EQ(sem->Members()[0]->Location().value(), 1u);
|
||||
|
||||
EXPECT_FALSE(sem->Members()[1]->Location().has_value());
|
||||
EXPECT_EQ(sem->Members()[0]->Attributes().location, 1u);
|
||||
EXPECT_FALSE(sem->Members()[1]->Attributes().location.has_value());
|
||||
}
|
||||
|
||||
TEST_F(TypeStructTest, IsConstructable) {
|
||||
@ -207,12 +205,15 @@ TEST_F(TypeStructTest, HasFixedFootprint) {
|
||||
}
|
||||
|
||||
TEST_F(TypeStructTest, Clone) {
|
||||
type::StructMemberAttributes attrs_location_2;
|
||||
attrs_location_2.location = 2;
|
||||
|
||||
auto* s = create<Struct>(
|
||||
Source{}, Sym("my_struct"),
|
||||
utils::Vector{create<StructMember>(Source{}, Sym("b"), create<Vector>(create<F32>(), 3u),
|
||||
0u, 0u, 16u, 12u, std::optional<uint32_t>{2}),
|
||||
0u, 0u, 16u, 12u, attrs_location_2),
|
||||
create<StructMember>(Source{}, Sym("a"), create<I32>(), 1u, 16u, 4u, 4u,
|
||||
std::optional<uint32_t>())},
|
||||
type::StructMemberAttributes{})},
|
||||
4u /* align */, 8u /* size */, 16u /* size_no_padding */);
|
||||
|
||||
ProgramID id;
|
||||
|
@ -56,7 +56,7 @@ struct TypeTest : public TestHelper {
|
||||
/* offset */ 0u,
|
||||
/* align */ 4u,
|
||||
/* size */ 4u,
|
||||
/* location */ std::nullopt),
|
||||
/* attributes */ type::StructMemberAttributes{}),
|
||||
},
|
||||
/* align*/ 4u,
|
||||
/* size*/ 4u,
|
||||
@ -72,7 +72,7 @@ struct TypeTest : public TestHelper {
|
||||
/* offset */ 0u,
|
||||
/* align */ 4u,
|
||||
/* size */ 4u,
|
||||
/* location */ std::nullopt),
|
||||
/* attributes */ type::StructMemberAttributes{}),
|
||||
},
|
||||
/* align*/ 4u,
|
||||
/* size*/ 4u,
|
||||
@ -88,7 +88,7 @@ struct TypeTest : public TestHelper {
|
||||
/* offset */ 0u,
|
||||
/* align */ 4u,
|
||||
/* size */ 4u,
|
||||
/* location */ std::nullopt),
|
||||
/* attributes */ type::StructMemberAttributes{}),
|
||||
},
|
||||
/* align*/ 4u,
|
||||
/* size*/ 4u,
|
||||
|
@ -399,7 +399,7 @@ class GeneratorImpl : public TextGenerator {
|
||||
/// Handles generating a 'var' declaration
|
||||
/// @param var the variable to generate
|
||||
void EmitVar(const ast::Var* var);
|
||||
/// Handles generating a function-scope 'let' declaration
|
||||
/// Handles generating a 'let' declaration
|
||||
/// @param let the variable to generate
|
||||
void EmitLet(const ast::Let* let);
|
||||
/// Handles generating a module-scope 'let' declaration
|
||||
|
@ -4216,73 +4216,48 @@ bool GeneratorImpl::EmitStructType(TextBuffer* b, const sem::Struct* str) {
|
||||
auto* ty = mem->Type();
|
||||
auto out = line(b);
|
||||
std::string pre, post;
|
||||
if (auto* decl = mem->Declaration()) {
|
||||
for (auto* attr : decl->attributes) {
|
||||
if (attr->Is<ast::LocationAttribute>()) {
|
||||
auto& pipeline_stage_uses = str->PipelineStageUses();
|
||||
if (TINT_UNLIKELY(pipeline_stage_uses.size() != 1)) {
|
||||
TINT_ICE(Writer, diagnostics_) << "invalid entry point IO struct uses";
|
||||
}
|
||||
|
||||
auto loc = mem->Location().value();
|
||||
if (pipeline_stage_uses.count(type::PipelineStageUsage::kVertexInput)) {
|
||||
post += " : TEXCOORD" + std::to_string(loc);
|
||||
} else if (pipeline_stage_uses.count(
|
||||
type::PipelineStageUsage::kVertexOutput)) {
|
||||
post += " : TEXCOORD" + std::to_string(loc);
|
||||
} else if (pipeline_stage_uses.count(
|
||||
type::PipelineStageUsage::kFragmentInput)) {
|
||||
post += " : TEXCOORD" + std::to_string(loc);
|
||||
} else if (TINT_LIKELY(pipeline_stage_uses.count(
|
||||
type::PipelineStageUsage::kFragmentOutput))) {
|
||||
post += " : SV_Target" + std::to_string(loc);
|
||||
} else {
|
||||
TINT_ICE(Writer, diagnostics_) << "invalid use of location attribute";
|
||||
}
|
||||
} else if (auto* builtin_attr = attr->As<ast::BuiltinAttribute>()) {
|
||||
auto builtin = program_->Sem().Get(builtin_attr)->Value();
|
||||
auto name = builtin_to_attribute(builtin);
|
||||
if (name.empty()) {
|
||||
diagnostics_.add_error(diag::System::Writer, "unsupported builtin");
|
||||
return false;
|
||||
}
|
||||
post += " : " + name;
|
||||
} else if (auto* interpolate = attr->As<ast::InterpolateAttribute>()) {
|
||||
auto& sem = program_->Sem();
|
||||
auto i_type =
|
||||
sem.Get<sem::BuiltinEnumExpression<builtin::InterpolationType>>(
|
||||
interpolate->type)
|
||||
->Value();
|
||||
auto& attributes = mem->Attributes();
|
||||
|
||||
auto i_smpl = builtin::InterpolationSampling::kUndefined;
|
||||
if (interpolate->sampling) {
|
||||
i_smpl =
|
||||
sem.Get<sem::BuiltinEnumExpression<builtin::InterpolationSampling>>(
|
||||
interpolate->sampling)
|
||||
->Value();
|
||||
}
|
||||
|
||||
auto mod = interpolation_to_modifiers(i_type, i_smpl);
|
||||
if (mod.empty()) {
|
||||
diagnostics_.add_error(diag::System::Writer,
|
||||
"unsupported interpolation");
|
||||
return false;
|
||||
}
|
||||
pre += mod;
|
||||
|
||||
} else if (attr->Is<ast::InvariantAttribute>()) {
|
||||
// Note: `precise` is not exactly the same as `invariant`, but is
|
||||
// stricter and therefore provides the necessary guarantees.
|
||||
// See discussion here: https://github.com/gpuweb/gpuweb/issues/893
|
||||
pre += "precise ";
|
||||
} else if (TINT_UNLIKELY((!attr->IsAnyOf<ast::StructMemberAlignAttribute,
|
||||
ast::StructMemberOffsetAttribute,
|
||||
ast::StructMemberSizeAttribute>()))) {
|
||||
TINT_ICE(Writer, diagnostics_)
|
||||
<< "unhandled struct member attribute: " << attr->Name();
|
||||
return false;
|
||||
}
|
||||
if (auto location = attributes.location) {
|
||||
auto& pipeline_stage_uses = str->PipelineStageUses();
|
||||
if (TINT_UNLIKELY(pipeline_stage_uses.size() != 1)) {
|
||||
TINT_ICE(Writer, diagnostics_) << "invalid entry point IO struct uses";
|
||||
}
|
||||
if (pipeline_stage_uses.count(type::PipelineStageUsage::kVertexInput)) {
|
||||
post += " : TEXCOORD" + std::to_string(location.value());
|
||||
} else if (pipeline_stage_uses.count(type::PipelineStageUsage::kVertexOutput)) {
|
||||
post += " : TEXCOORD" + std::to_string(location.value());
|
||||
} else if (pipeline_stage_uses.count(type::PipelineStageUsage::kFragmentInput)) {
|
||||
post += " : TEXCOORD" + std::to_string(location.value());
|
||||
} else if (TINT_LIKELY(pipeline_stage_uses.count(
|
||||
type::PipelineStageUsage::kFragmentOutput))) {
|
||||
post += " : SV_Target" + std::to_string(location.value());
|
||||
} else {
|
||||
TINT_ICE(Writer, diagnostics_) << "invalid use of location attribute";
|
||||
}
|
||||
}
|
||||
if (auto builtin = attributes.builtin) {
|
||||
auto name = builtin_to_attribute(builtin.value());
|
||||
if (name.empty()) {
|
||||
diagnostics_.add_error(diag::System::Writer, "unsupported builtin");
|
||||
return false;
|
||||
}
|
||||
post += " : " + name;
|
||||
}
|
||||
if (auto interpolation = attributes.interpolation) {
|
||||
auto mod = interpolation_to_modifiers(interpolation->type, interpolation->sampling);
|
||||
if (mod.empty()) {
|
||||
diagnostics_.add_error(diag::System::Writer, "unsupported interpolation");
|
||||
return false;
|
||||
}
|
||||
pre += mod;
|
||||
}
|
||||
if (attributes.invariant) {
|
||||
// Note: `precise` is not exactly the same as `invariant`, but is
|
||||
// stricter and therefore provides the necessary guarantees.
|
||||
// See discussion here: https://github.com/gpuweb/gpuweb/issues/893
|
||||
pre += "precise ";
|
||||
}
|
||||
|
||||
out << pre;
|
||||
|
@ -470,7 +470,7 @@ class GeneratorImpl : public TextGenerator {
|
||||
/// @param var the variable to generate
|
||||
/// @returns true if the variable was emitted
|
||||
bool EmitVar(const ast::Var* var);
|
||||
/// Handles generating a function-scope 'let' declaration
|
||||
/// Handles generating a 'let' declaration
|
||||
/// @param let the variable to generate
|
||||
/// @returns true if the variable was emitted
|
||||
bool EmitLet(const ast::Let* let);
|
||||
|
@ -2852,88 +2852,51 @@ bool GeneratorImpl::EmitStructType(TextBuffer* b, const sem::Struct* str) {
|
||||
|
||||
out << " " << mem_name;
|
||||
// Emit attributes
|
||||
if (auto* decl = mem->Declaration()) {
|
||||
for (auto* attr : decl->attributes) {
|
||||
bool ok = Switch(
|
||||
attr,
|
||||
[&](const ast::BuiltinAttribute* builtin_attr) {
|
||||
auto builtin = program_->Sem().Get(builtin_attr)->Value();
|
||||
auto name = builtin_to_attribute(builtin);
|
||||
if (name.empty()) {
|
||||
diagnostics_.add_error(diag::System::Writer, "unknown builtin");
|
||||
return false;
|
||||
}
|
||||
out << " [[" << name << "]]";
|
||||
return true;
|
||||
},
|
||||
[&](const ast::LocationAttribute*) {
|
||||
auto& pipeline_stage_uses = str->PipelineStageUses();
|
||||
if (TINT_UNLIKELY(pipeline_stage_uses.size() != 1)) {
|
||||
TINT_ICE(Writer, diagnostics_) << "invalid entry point IO struct uses";
|
||||
return false;
|
||||
}
|
||||
auto& attributes = mem->Attributes();
|
||||
|
||||
uint32_t loc = mem->Location().value();
|
||||
if (pipeline_stage_uses.count(type::PipelineStageUsage::kVertexInput)) {
|
||||
out << " [[attribute(" + std::to_string(loc) + ")]]";
|
||||
} else if (pipeline_stage_uses.count(
|
||||
type::PipelineStageUsage::kVertexOutput)) {
|
||||
out << " [[user(locn" + std::to_string(loc) + ")]]";
|
||||
} else if (pipeline_stage_uses.count(
|
||||
type::PipelineStageUsage::kFragmentInput)) {
|
||||
out << " [[user(locn" + std::to_string(loc) + ")]]";
|
||||
} else if (TINT_LIKELY(pipeline_stage_uses.count(
|
||||
type::PipelineStageUsage::kFragmentOutput))) {
|
||||
out << " [[color(" + std::to_string(loc) + ")]]";
|
||||
} else {
|
||||
TINT_ICE(Writer, diagnostics_) << "invalid use of location decoration";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
[&](const ast::InterpolateAttribute* interpolate) {
|
||||
auto& sem = program_->Sem();
|
||||
auto i_type =
|
||||
sem.Get<sem::BuiltinEnumExpression<builtin::InterpolationType>>(
|
||||
interpolate->type)
|
||||
->Value();
|
||||
|
||||
auto i_smpl = builtin::InterpolationSampling::kUndefined;
|
||||
if (interpolate->sampling) {
|
||||
i_smpl =
|
||||
sem.Get<sem::BuiltinEnumExpression<builtin::InterpolationSampling>>(
|
||||
interpolate->sampling)
|
||||
->Value();
|
||||
}
|
||||
|
||||
auto name = interpolation_to_attribute(i_type, i_smpl);
|
||||
if (name.empty()) {
|
||||
diagnostics_.add_error(diag::System::Writer,
|
||||
"unknown interpolation attribute");
|
||||
return false;
|
||||
}
|
||||
out << " [[" << name << "]]";
|
||||
return true;
|
||||
},
|
||||
[&](const ast::InvariantAttribute*) {
|
||||
if (invariant_define_name_.empty()) {
|
||||
invariant_define_name_ = UniqueIdentifier("TINT_INVARIANT");
|
||||
}
|
||||
out << " " << invariant_define_name_;
|
||||
return true;
|
||||
},
|
||||
[&](const ast::StructMemberOffsetAttribute*) { return true; },
|
||||
[&](const ast::StructMemberAlignAttribute*) { return true; },
|
||||
[&](const ast::StructMemberSizeAttribute*) { return true; },
|
||||
[&](Default) {
|
||||
TINT_ICE(Writer, diagnostics_)
|
||||
<< "unhandled struct member attribute: " << attr->Name();
|
||||
return false;
|
||||
});
|
||||
if (!ok) {
|
||||
return false;
|
||||
}
|
||||
if (auto builtin = attributes.builtin) {
|
||||
auto name = builtin_to_attribute(builtin.value());
|
||||
if (name.empty()) {
|
||||
diagnostics_.add_error(diag::System::Writer, "unknown builtin");
|
||||
return false;
|
||||
}
|
||||
out << " [[" << name << "]]";
|
||||
}
|
||||
|
||||
if (auto location = attributes.location) {
|
||||
auto& pipeline_stage_uses = str->PipelineStageUses();
|
||||
if (TINT_UNLIKELY(pipeline_stage_uses.size() != 1)) {
|
||||
TINT_ICE(Writer, diagnostics_) << "invalid entry point IO struct uses";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pipeline_stage_uses.count(type::PipelineStageUsage::kVertexInput)) {
|
||||
out << " [[attribute(" + std::to_string(location.value()) + ")]]";
|
||||
} else if (pipeline_stage_uses.count(type::PipelineStageUsage::kVertexOutput)) {
|
||||
out << " [[user(locn" + std::to_string(location.value()) + ")]]";
|
||||
} else if (pipeline_stage_uses.count(type::PipelineStageUsage::kFragmentInput)) {
|
||||
out << " [[user(locn" + std::to_string(location.value()) + ")]]";
|
||||
} else if (TINT_LIKELY(
|
||||
pipeline_stage_uses.count(type::PipelineStageUsage::kFragmentOutput))) {
|
||||
out << " [[color(" + std::to_string(location.value()) + ")]]";
|
||||
} else {
|
||||
TINT_ICE(Writer, diagnostics_) << "invalid use of location decoration";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (auto interpolation = attributes.interpolation) {
|
||||
auto name = interpolation_to_attribute(interpolation->type, interpolation->sampling);
|
||||
if (name.empty()) {
|
||||
diagnostics_.add_error(diag::System::Writer, "unknown interpolation attribute");
|
||||
return false;
|
||||
}
|
||||
out << " [[" << name << "]]";
|
||||
}
|
||||
|
||||
if (attributes.invariant) {
|
||||
invariant_define_name_ = UniqueIdentifier("TINT_INVARIANT");
|
||||
out << " " << invariant_define_name_;
|
||||
}
|
||||
|
||||
out << ";";
|
||||
|
@ -350,7 +350,7 @@ class GeneratorImpl : public TextGenerator {
|
||||
/// @param var the variable to generate
|
||||
/// @returns true if the variable was emitted
|
||||
bool EmitVar(const ast::Var* var);
|
||||
/// Handles generating a function-scope 'let' declaration
|
||||
/// Handles generating a 'let' declaration
|
||||
/// @param let the variable to generate
|
||||
/// @returns true if the variable was emitted
|
||||
bool EmitLet(const ast::Let* let);
|
||||
|
Loading…
x
Reference in New Issue
Block a user