mirror of
https://github.com/encounter/dawn-cmake.git
synced 2025-06-29 18:03:44 +00:00
This extension adds support for the push_constant storage class such that it can be tested with WGSL test files. The real goal is to allow future transforms that will add push constants that the SPIRV writer will output. The extension: - Adds the `chromium_experimental_push_constant` enable. - Allows the push_constant storage class for global variables. - Adds validation that the types are host-shareable for push_constant variables, and that they don't contain f16 (must be 32bit types only). - Validates that at most one push_constant variable is statically used per entry-point. - Skips validation that the extension has been enabled if kIgnoreStorageClass is used. Tests are added: - For parsing of var<push_constant> - Caught a missing conversion. - For each of the validation rules. - For the wrapping of push constants in structs if needed by AddSpirvBlockAttribute. - For the layout and type rules of the storage class. - For a shader with multiple entry-points using various push constants. - Caught a missing reset of the previous push constant variable in the validation check that at most one is used. - Caught the missing wrapping in structs that had to be added to AddSpirvBlockAttribute. - Caught incorrect logic when adding diagnostics about the call graph leading to the reference to push constants. Bug: tint:1620 Change-Id: I04a5d8e5188c0dcef077f2233ba1359d1575bf51 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/96682 Kokoro: Kokoro <noreply+kokoro@google.com> Commit-Queue: Corentin Wallez <cwallez@chromium.org> Reviewed-by: Ben Clayton <bclayton@google.com>
117 lines
4.8 KiB
C++
117 lines
4.8 KiB
C++
// 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/tint/transform/add_spirv_block_attribute.h"
|
|
|
|
#include <unordered_map>
|
|
#include <unordered_set>
|
|
#include <utility>
|
|
|
|
#include "src/tint/program_builder.h"
|
|
#include "src/tint/sem/variable.h"
|
|
#include "src/tint/utils/map.h"
|
|
|
|
TINT_INSTANTIATE_TYPEINFO(tint::transform::AddSpirvBlockAttribute);
|
|
TINT_INSTANTIATE_TYPEINFO(tint::transform::AddSpirvBlockAttribute::SpirvBlockAttribute);
|
|
|
|
namespace tint::transform {
|
|
|
|
AddSpirvBlockAttribute::AddSpirvBlockAttribute() = default;
|
|
|
|
AddSpirvBlockAttribute::~AddSpirvBlockAttribute() = default;
|
|
|
|
void AddSpirvBlockAttribute::Run(CloneContext& ctx, const DataMap&, DataMap&) const {
|
|
auto& sem = ctx.src->Sem();
|
|
|
|
// Collect the set of structs that are nested in other types.
|
|
std::unordered_set<const sem::Struct*> nested_structs;
|
|
for (auto* node : ctx.src->ASTNodes().Objects()) {
|
|
if (auto* arr = sem.Get<sem::Array>(node->As<ast::Array>())) {
|
|
if (auto* nested_str = arr->ElemType()->As<sem::Struct>()) {
|
|
nested_structs.insert(nested_str);
|
|
}
|
|
} else if (auto* str = sem.Get<sem::Struct>(node->As<ast::Struct>())) {
|
|
for (auto* member : str->Members()) {
|
|
if (auto* nested_str = member->Type()->As<sem::Struct>()) {
|
|
nested_structs.insert(nested_str);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// A map from a type in the source program to a block-decorated wrapper that
|
|
// contains it in the destination program.
|
|
std::unordered_map<const sem::Type*, const ast::Struct*> wrapper_structs;
|
|
|
|
// Process global 'var' declarations that are buffers.
|
|
for (auto* var : ctx.src->AST().Globals<ast::Var>()) {
|
|
auto* sem_var = sem.Get<sem::GlobalVariable>(var);
|
|
if (var->declared_storage_class != ast::StorageClass::kStorage &&
|
|
var->declared_storage_class != ast::StorageClass::kUniform &&
|
|
var->declared_storage_class != ast::StorageClass::kPushConstant) {
|
|
continue;
|
|
}
|
|
|
|
auto* ty = sem.Get(var->type);
|
|
auto* str = ty->As<sem::Struct>();
|
|
if (!str || nested_structs.count(str)) {
|
|
const char* kMemberName = "inner";
|
|
|
|
// This is a non-struct or a struct that is nested somewhere else, so we
|
|
// need to wrap it first.
|
|
auto* wrapper = utils::GetOrCreate(wrapper_structs, ty, [&]() {
|
|
auto* block = ctx.dst->ASTNodes().Create<SpirvBlockAttribute>(
|
|
ctx.dst->ID(), ctx.dst->AllocateNodeID());
|
|
auto wrapper_name = ctx.src->Symbols().NameFor(var->symbol) + "_block";
|
|
auto* ret = ctx.dst->create<ast::Struct>(
|
|
ctx.dst->Symbols().New(wrapper_name),
|
|
ast::StructMemberList{ctx.dst->Member(kMemberName, CreateASTTypeFor(ctx, ty))},
|
|
ast::AttributeList{block});
|
|
ctx.InsertBefore(ctx.src->AST().GlobalDeclarations(), var, ret);
|
|
return ret;
|
|
});
|
|
ctx.Replace(var->type, ctx.dst->ty.Of(wrapper));
|
|
|
|
// Insert a member accessor to get the original type from the wrapper at
|
|
// any usage of the original variable.
|
|
for (auto* user : sem_var->Users()) {
|
|
ctx.Replace(user->Declaration(),
|
|
ctx.dst->MemberAccessor(ctx.Clone(var->symbol), kMemberName));
|
|
}
|
|
} else {
|
|
// Add a block attribute to this struct directly.
|
|
auto* block = ctx.dst->ASTNodes().Create<SpirvBlockAttribute>(
|
|
ctx.dst->ID(), ctx.dst->AllocateNodeID());
|
|
ctx.InsertFront(str->Declaration()->attributes, block);
|
|
}
|
|
}
|
|
|
|
ctx.Clone();
|
|
}
|
|
|
|
AddSpirvBlockAttribute::SpirvBlockAttribute::SpirvBlockAttribute(ProgramID pid, ast::NodeID nid)
|
|
: Base(pid, nid) {}
|
|
AddSpirvBlockAttribute::SpirvBlockAttribute::~SpirvBlockAttribute() = default;
|
|
std::string AddSpirvBlockAttribute::SpirvBlockAttribute::InternalName() const {
|
|
return "spirv_block";
|
|
}
|
|
|
|
const AddSpirvBlockAttribute::SpirvBlockAttribute*
|
|
AddSpirvBlockAttribute::SpirvBlockAttribute::Clone(CloneContext* ctx) const {
|
|
return ctx->dst->ASTNodes().Create<AddSpirvBlockAttribute::SpirvBlockAttribute>(
|
|
ctx->dst->ID(), ctx->dst->AllocateNodeID());
|
|
}
|
|
|
|
} // namespace tint::transform
|