|  | // Copyright 2024 The Dawn & Tint Authors | 
|  | // | 
|  | // Redistribution and use in source and binary forms, with or without | 
|  | // modification, are permitted provided that the following conditions are met: | 
|  | // | 
|  | // 1. Redistributions of source code must retain the above copyright notice, this | 
|  | //    list of conditions and the following disclaimer. | 
|  | // | 
|  | // 2. Redistributions in binary form must reproduce the above copyright notice, | 
|  | //    this list of conditions and the following disclaimer in the documentation | 
|  | //    and/or other materials provided with the distribution. | 
|  | // | 
|  | // 3. Neither the name of the copyright holder nor the names of its | 
|  | //    contributors may be used to endorse or promote products derived from | 
|  | //    this software without specific prior written permission. | 
|  | // | 
|  | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | 
|  | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | 
|  | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | 
|  | // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE | 
|  | // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | 
|  | // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | 
|  | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | 
|  | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | 
|  | // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 
|  | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 
|  |  | 
|  | #include "src/tint/lang/wgsl/ast/transform/push_constant_helper.h" | 
|  |  | 
|  | #include <utility> | 
|  |  | 
|  | #include "src/tint/lang/wgsl/ast/module.h" | 
|  | #include "src/tint/lang/wgsl/ast/var.h" | 
|  | #include "src/tint/lang/wgsl/program/clone_context.h" | 
|  | #include "src/tint/lang/wgsl/program/program.h" | 
|  | #include "src/tint/lang/wgsl/program/program_builder.h" | 
|  | #include "src/tint/lang/wgsl/sem/struct.h" | 
|  | #include "src/tint/lang/wgsl/sem/variable.h" | 
|  |  | 
|  | namespace tint::ast::transform { | 
|  |  | 
|  | PushConstantHelper::PushConstantHelper(program::CloneContext& c) : ctx(c) { | 
|  | // Find first existing push_constant, if any. | 
|  | for (auto* global : ctx.src->AST().GlobalVariables()) { | 
|  | if (auto* var = global->As<ast::Var>()) { | 
|  | auto* v = ctx.src->Sem().Get(var); | 
|  | if (v->AddressSpace() == core::AddressSpace::kPushConstant) { | 
|  | push_constants_var = var; | 
|  | auto* str = v->Type()->UnwrapRef()->As<sem::Struct>(); | 
|  | if (TINT_UNLIKELY(!str)) { | 
|  | TINT_ICE() << "expected var<push_constant> type to be struct. Was " | 
|  | "AddBlockAttribute run?"; | 
|  | } | 
|  | // Clone all members from the existing block and insert them into the map. | 
|  | for (auto* member : str->Members()) { | 
|  | member_map[member->Offset()] = ctx.CloneWithoutTransform(member->Declaration()); | 
|  | } | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void PushConstantHelper::InsertMember(const char* name, ast::Type type, uint32_t offset) { | 
|  | auto& member = member_map[offset]; | 
|  | if (TINT_UNLIKELY(member != nullptr)) { | 
|  | ctx.dst->Diagnostics().AddError(diag::System::Transform, Source{}) | 
|  | << "struct member offset collision"; | 
|  | } | 
|  | member = ctx.dst->Member(name, type, Vector{ctx.dst->MemberOffset(core::AInt(offset))}); | 
|  | } | 
|  |  | 
|  | Symbol PushConstantHelper::Run() { | 
|  | Vector<const tint::ast::StructMember*, 8> members; | 
|  | for (auto i : member_map) { | 
|  | members.Push(i.second); | 
|  | } | 
|  |  | 
|  | new_struct = ctx.dst->Structure(ctx.dst->Symbols().New("PushConstants"), std::move(members)); | 
|  |  | 
|  | Symbol buffer_name; | 
|  |  | 
|  | // If this is the first use of push constants, create a global to hold them. | 
|  | if (!push_constants_var) { | 
|  | ctx.dst->Enable(wgsl::Extension::kChromiumExperimentalPushConstant); | 
|  |  | 
|  | buffer_name = ctx.dst->Symbols().New("push_constants"); | 
|  | ctx.dst->GlobalVar(buffer_name, ctx.dst->ty.Of(new_struct), | 
|  | core::AddressSpace::kPushConstant); | 
|  | } else { | 
|  | buffer_name = ctx.Clone(push_constants_var->name->symbol); | 
|  |  | 
|  | // Replace all variable users of the old struct with the new struct. | 
|  | ctx.ReplaceAll([this](const ast::Variable* var) -> const ast::Variable* { | 
|  | if (ctx.src->Sem().Get(var)->AddressSpace() == core::AddressSpace::kPushConstant) { | 
|  | if (var->As<ast::Parameter>()) { | 
|  | return ctx.dst->Param(ctx.Clone(var->name->symbol), ctx.dst->ty.Of(new_struct), | 
|  | ctx.Clone(var->attributes)); | 
|  | } else { | 
|  | return ctx.dst->Var(ctx.Clone(var->name->symbol), ctx.dst->ty.Of(new_struct), | 
|  | ctx.Clone(var->attributes), | 
|  | core::AddressSpace::kPushConstant); | 
|  | } | 
|  | } | 
|  | return nullptr; | 
|  | }); | 
|  | } | 
|  |  | 
|  | return buffer_name; | 
|  | } | 
|  |  | 
|  | PushConstantHelper::~PushConstantHelper() = default; | 
|  |  | 
|  | }  // namespace tint::ast::transform |