// Copyright 2020 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/transform/first_index_offset.h" #include #include #include #include "src/ast/struct_block_decoration.h" #include "src/program_builder.h" #include "src/semantic/function.h" #include "src/semantic/member_accessor_expression.h" #include "src/semantic/struct.h" #include "src/semantic/variable.h" TINT_INSTANTIATE_TYPEINFO(tint::transform::FirstIndexOffset::BindingPoint); TINT_INSTANTIATE_TYPEINFO(tint::transform::FirstIndexOffset::Data); namespace tint { namespace transform { namespace { // Uniform buffer member names constexpr char kFirstVertexName[] = "first_vertex_index"; constexpr char kFirstInstanceName[] = "first_instance_index"; } // namespace FirstIndexOffset::BindingPoint::BindingPoint() = default; FirstIndexOffset::BindingPoint::BindingPoint(uint32_t b, uint32_t g) : binding(b), group(g) {} FirstIndexOffset::BindingPoint::~BindingPoint() = default; FirstIndexOffset::Data::Data(bool has_vtx_index, bool has_inst_index, uint32_t first_vtx_offset, uint32_t first_inst_offset) : has_vertex_index(has_vtx_index), has_instance_index(has_inst_index), first_vertex_offset(first_vtx_offset), first_instance_offset(first_inst_offset) {} FirstIndexOffset::Data::Data(const Data&) = default; FirstIndexOffset::Data::~Data() = default; FirstIndexOffset::FirstIndexOffset() = default; FirstIndexOffset::FirstIndexOffset(uint32_t binding, uint32_t group) : binding_(binding), group_(group) {} FirstIndexOffset::~FirstIndexOffset() = default; Output FirstIndexOffset::Run(const Program* in, const DataMap& data) { // Get the uniform buffer binding point uint32_t ub_binding = binding_; uint32_t ub_group = group_; if (auto* binding_point = data.Get()) { ub_binding = binding_point->binding; ub_group = binding_point->group; } ProgramBuilder out; CloneContext ctx(&out, in); // Map of builtin usages std::unordered_map builtin_vars; std::unordered_map builtin_members; bool has_vertex_index = false; bool has_instance_index = false; // Traverse the AST scanning for builtin accesses via variables (includes // parameters) or structure member accesses. for (auto* node : in->ASTNodes().Objects()) { if (auto* var = node->As()) { for (ast::Decoration* dec : var->decorations()) { if (auto* builtin_dec = dec->As()) { ast::Builtin builtin = builtin_dec->value(); if (builtin == ast::Builtin::kVertexIndex) { auto* sem_var = ctx.src->Sem().Get(var); builtin_vars.emplace(sem_var, kFirstVertexName); has_vertex_index = true; } if (builtin == ast::Builtin::kInstanceIndex) { auto* sem_var = ctx.src->Sem().Get(var); builtin_vars.emplace(sem_var, kFirstInstanceName); has_instance_index = true; } } } } if (auto* member = node->As()) { for (ast::Decoration* dec : member->decorations()) { if (auto* builtin_dec = dec->As()) { ast::Builtin builtin = builtin_dec->value(); if (builtin == ast::Builtin::kVertexIndex) { auto* sem_mem = ctx.src->Sem().Get(member); builtin_members.emplace(sem_mem, kFirstVertexName); has_vertex_index = true; } if (builtin == ast::Builtin::kInstanceIndex) { auto* sem_mem = ctx.src->Sem().Get(member); builtin_members.emplace(sem_mem, kFirstInstanceName); has_instance_index = true; } } } } } // Byte offsets on the uniform buffer uint32_t vertex_index_offset = 0; uint32_t instance_index_offset = 0; if (has_vertex_index || has_instance_index) { // Add uniform buffer members and calculate byte offsets uint32_t offset = 0; ast::StructMemberList members; if (has_vertex_index) { members.push_back(ctx.dst->Member(kFirstVertexName, ctx.dst->ty.u32())); vertex_index_offset = offset; offset += 4; } if (has_instance_index) { members.push_back(ctx.dst->Member(kFirstInstanceName, ctx.dst->ty.u32())); instance_index_offset = offset; offset += 4; } auto* struct_type = ctx.dst->Structure(ctx.dst->Symbols().New(), std::move(members), {ctx.dst->create()}); // Create a global to hold the uniform buffer Symbol buffer_name = ctx.dst->Symbols().New(); ctx.dst->Global(buffer_name, struct_type, ast::StorageClass::kUniform, nullptr, ast::DecorationList{ ctx.dst->create(ub_binding), ctx.dst->create(ub_group), }); // Fix up all references to the builtins with the offsets ctx.ReplaceAll([=, &ctx](ast::Expression* expr) -> ast::Expression* { auto* sem = ctx.src->Sem().Get(expr); if (auto* user = sem->As()) { auto it = builtin_vars.find(user->Variable()); if (it != builtin_vars.end()) { return ctx.dst->Add(ctx.CloneWithoutTransform(expr), ctx.dst->MemberAccessor(buffer_name, it->second)); } } if (auto* access = sem->As()) { auto it = builtin_members.find(access->Member()); if (it != builtin_members.end()) { return ctx.dst->Add(ctx.CloneWithoutTransform(expr), ctx.dst->MemberAccessor(buffer_name, it->second)); } } // Not interested in this experssion. Just clone. return nullptr; }); } ctx.Clone(); return Output( Program(std::move(out)), std::make_unique(has_vertex_index, has_instance_index, vertex_index_offset, instance_index_offset)); } } // namespace transform } // namespace tint