• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2020 The Tint Authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "src/inspector/inspector.h"
16 
17 #include <limits>
18 #include <utility>
19 
20 #include "src/ast/bool_literal_expression.h"
21 #include "src/ast/call_expression.h"
22 #include "src/ast/float_literal_expression.h"
23 #include "src/ast/interpolate_decoration.h"
24 #include "src/ast/location_decoration.h"
25 #include "src/ast/module.h"
26 #include "src/ast/override_decoration.h"
27 #include "src/ast/sint_literal_expression.h"
28 #include "src/ast/uint_literal_expression.h"
29 #include "src/sem/array.h"
30 #include "src/sem/call.h"
31 #include "src/sem/depth_multisampled_texture_type.h"
32 #include "src/sem/f32_type.h"
33 #include "src/sem/function.h"
34 #include "src/sem/i32_type.h"
35 #include "src/sem/matrix_type.h"
36 #include "src/sem/multisampled_texture_type.h"
37 #include "src/sem/sampled_texture_type.h"
38 #include "src/sem/statement.h"
39 #include "src/sem/storage_texture_type.h"
40 #include "src/sem/struct.h"
41 #include "src/sem/u32_type.h"
42 #include "src/sem/variable.h"
43 #include "src/sem/vector_type.h"
44 #include "src/sem/void_type.h"
45 #include "src/utils/math.h"
46 #include "src/utils/unique_vector.h"
47 
48 namespace tint {
49 namespace inspector {
50 
51 namespace {
52 
AppendResourceBindings(std::vector<ResourceBinding> * dest,const std::vector<ResourceBinding> & orig)53 void AppendResourceBindings(std::vector<ResourceBinding>* dest,
54                             const std::vector<ResourceBinding>& orig) {
55   TINT_ASSERT(Inspector, dest);
56   if (!dest) {
57     return;
58   }
59 
60   dest->reserve(dest->size() + orig.size());
61   dest->insert(dest->end(), orig.begin(), orig.end());
62 }
63 
CalculateComponentAndComposition(const sem::Type * type)64 std::tuple<ComponentType, CompositionType> CalculateComponentAndComposition(
65     const sem::Type* type) {
66   if (type->is_float_scalar()) {
67     return {ComponentType::kFloat, CompositionType::kScalar};
68   } else if (type->is_float_vector()) {
69     auto* vec = type->As<sem::Vector>();
70     if (vec->Width() == 2) {
71       return {ComponentType::kFloat, CompositionType::kVec2};
72     } else if (vec->Width() == 3) {
73       return {ComponentType::kFloat, CompositionType::kVec3};
74     } else if (vec->Width() == 4) {
75       return {ComponentType::kFloat, CompositionType::kVec4};
76     }
77   } else if (type->is_unsigned_integer_scalar()) {
78     return {ComponentType::kUInt, CompositionType::kScalar};
79   } else if (type->is_unsigned_integer_vector()) {
80     auto* vec = type->As<sem::Vector>();
81     if (vec->Width() == 2) {
82       return {ComponentType::kUInt, CompositionType::kVec2};
83     } else if (vec->Width() == 3) {
84       return {ComponentType::kUInt, CompositionType::kVec3};
85     } else if (vec->Width() == 4) {
86       return {ComponentType::kUInt, CompositionType::kVec4};
87     }
88   } else if (type->is_signed_integer_scalar()) {
89     return {ComponentType::kSInt, CompositionType::kScalar};
90   } else if (type->is_signed_integer_vector()) {
91     auto* vec = type->As<sem::Vector>();
92     if (vec->Width() == 2) {
93       return {ComponentType::kSInt, CompositionType::kVec2};
94     } else if (vec->Width() == 3) {
95       return {ComponentType::kSInt, CompositionType::kVec3};
96     } else if (vec->Width() == 4) {
97       return {ComponentType::kSInt, CompositionType::kVec4};
98     }
99   }
100   return {ComponentType::kUnknown, CompositionType::kUnknown};
101 }
102 
CalculateInterpolationData(const sem::Type * type,const ast::DecorationList & decorations)103 std::tuple<InterpolationType, InterpolationSampling> CalculateInterpolationData(
104     const sem::Type* type,
105     const ast::DecorationList& decorations) {
106   auto* interpolation_decoration =
107       ast::GetDecoration<ast::InterpolateDecoration>(decorations);
108   if (type->is_integer_scalar_or_vector()) {
109     return {InterpolationType::kFlat, InterpolationSampling::kNone};
110   }
111 
112   if (!interpolation_decoration) {
113     return {InterpolationType::kPerspective, InterpolationSampling::kCenter};
114   }
115 
116   auto interpolation_type = interpolation_decoration->type;
117   auto sampling = interpolation_decoration->sampling;
118   if (interpolation_type != ast::InterpolationType::kFlat &&
119       sampling == ast::InterpolationSampling::kNone) {
120     sampling = ast::InterpolationSampling::kCenter;
121   }
122   return {ASTToInspectorInterpolationType(interpolation_type),
123           ASTToInspectorInterpolationSampling(sampling)};
124 }
125 
126 }  // namespace
127 
Inspector(const Program * program)128 Inspector::Inspector(const Program* program) : program_(program) {}
129 
130 Inspector::~Inspector() = default;
131 
GetEntryPoints()132 std::vector<EntryPoint> Inspector::GetEntryPoints() {
133   std::vector<EntryPoint> result;
134 
135   for (auto* func : program_->AST().Functions()) {
136     if (!func->IsEntryPoint()) {
137       continue;
138     }
139 
140     auto* sem = program_->Sem().Get(func);
141 
142     EntryPoint entry_point;
143     entry_point.name = program_->Symbols().NameFor(func->symbol);
144     entry_point.remapped_name = program_->Symbols().NameFor(func->symbol);
145     entry_point.stage = func->PipelineStage();
146 
147     auto wgsize = sem->WorkgroupSize();
148     entry_point.workgroup_size_x = wgsize[0].value;
149     entry_point.workgroup_size_y = wgsize[1].value;
150     entry_point.workgroup_size_z = wgsize[2].value;
151     if (wgsize[0].overridable_const || wgsize[1].overridable_const ||
152         wgsize[2].overridable_const) {
153       // TODO(crbug.com/tint/713): Handle overridable constants.
154       TINT_ASSERT(Inspector, false);
155     }
156 
157     for (auto* param : sem->Parameters()) {
158       AddEntryPointInOutVariables(
159           program_->Symbols().NameFor(param->Declaration()->symbol),
160           param->Type(), param->Declaration()->decorations,
161           entry_point.input_variables);
162 
163       entry_point.input_position_used |=
164           ContainsBuiltin(ast::Builtin::kPosition, param->Type(),
165                           param->Declaration()->decorations);
166       entry_point.front_facing_used |=
167           ContainsBuiltin(ast::Builtin::kFrontFacing, param->Type(),
168                           param->Declaration()->decorations);
169       entry_point.sample_index_used |=
170           ContainsBuiltin(ast::Builtin::kSampleIndex, param->Type(),
171                           param->Declaration()->decorations);
172       entry_point.input_sample_mask_used |=
173           ContainsBuiltin(ast::Builtin::kSampleMask, param->Type(),
174                           param->Declaration()->decorations);
175       entry_point.num_workgroups_used |=
176           ContainsBuiltin(ast::Builtin::kNumWorkgroups, param->Type(),
177                           param->Declaration()->decorations);
178     }
179 
180     if (!sem->ReturnType()->Is<sem::Void>()) {
181       AddEntryPointInOutVariables("<retval>", sem->ReturnType(),
182                                   func->return_type_decorations,
183                                   entry_point.output_variables);
184 
185       entry_point.output_sample_mask_used =
186           ContainsBuiltin(ast::Builtin::kSampleMask, sem->ReturnType(),
187                           func->return_type_decorations);
188     }
189 
190     for (auto* var : sem->TransitivelyReferencedGlobals()) {
191       auto* decl = var->Declaration();
192 
193       auto name = program_->Symbols().NameFor(decl->symbol);
194 
195       auto* global = var->As<sem::GlobalVariable>();
196       if (global && global->IsOverridable()) {
197         OverridableConstant overridable_constant;
198         overridable_constant.name = name;
199         overridable_constant.numeric_id = global->ConstantId();
200         auto* type = var->Type();
201         TINT_ASSERT(Inspector, type->is_scalar());
202         if (type->is_bool_scalar_or_vector()) {
203           overridable_constant.type = OverridableConstant::Type::kBool;
204         } else if (type->is_float_scalar()) {
205           overridable_constant.type = OverridableConstant::Type::kFloat32;
206         } else if (type->is_signed_integer_scalar()) {
207           overridable_constant.type = OverridableConstant::Type::kInt32;
208         } else if (type->is_unsigned_integer_scalar()) {
209           overridable_constant.type = OverridableConstant::Type::kUint32;
210         } else {
211           TINT_UNREACHABLE(Inspector, diagnostics_);
212         }
213 
214         overridable_constant.is_initialized =
215             global->Declaration()->constructor;
216         auto* override_deco = ast::GetDecoration<ast::OverrideDecoration>(
217             global->Declaration()->decorations);
218         overridable_constant.is_numeric_id_specified =
219             override_deco ? override_deco->has_value : false;
220 
221         entry_point.overridable_constants.push_back(overridable_constant);
222       }
223     }
224 
225     result.push_back(std::move(entry_point));
226   }
227 
228   return result;
229 }
230 
GetRemappedNameForEntryPoint(const std::string & entry_point)231 std::string Inspector::GetRemappedNameForEntryPoint(
232     const std::string& entry_point) {
233   // TODO(rharrison): Reenable once all of the backends are using the renamed
234   //                  entry points.
235 
236   //  auto* func = FindEntryPointByName(entry_point);
237   //  if (!func) {
238   //    return {};
239   //  }
240   //  return func->name();
241   return entry_point;
242 }
243 
GetConstantIDs()244 std::map<uint32_t, Scalar> Inspector::GetConstantIDs() {
245   std::map<uint32_t, Scalar> result;
246   for (auto* var : program_->AST().GlobalVariables()) {
247     auto* global = program_->Sem().Get<sem::GlobalVariable>(var);
248     if (!global || !global->IsOverridable()) {
249       continue;
250     }
251 
252     // If there are conflicting defintions for a constant id, that is invalid
253     // WGSL, so the resolver should catch it. Thus here the inspector just
254     // assumes all definitions of the constant id are the same, so only needs
255     // to find the first reference to constant id.
256     uint32_t constant_id = global->ConstantId();
257     if (result.find(constant_id) != result.end()) {
258       continue;
259     }
260 
261     if (!var->constructor) {
262       result[constant_id] = Scalar();
263       continue;
264     }
265 
266     auto* literal = var->constructor->As<ast::LiteralExpression>();
267     if (!literal) {
268       // This is invalid WGSL, but handling gracefully.
269       result[constant_id] = Scalar();
270       continue;
271     }
272 
273     if (auto* l = literal->As<ast::BoolLiteralExpression>()) {
274       result[constant_id] = Scalar(l->value);
275       continue;
276     }
277 
278     if (auto* l = literal->As<ast::UintLiteralExpression>()) {
279       result[constant_id] = Scalar(l->value);
280       continue;
281     }
282 
283     if (auto* l = literal->As<ast::SintLiteralExpression>()) {
284       result[constant_id] = Scalar(l->value);
285       continue;
286     }
287 
288     if (auto* l = literal->As<ast::FloatLiteralExpression>()) {
289       result[constant_id] = Scalar(l->value);
290       continue;
291     }
292 
293     result[constant_id] = Scalar();
294   }
295 
296   return result;
297 }
298 
GetConstantNameToIdMap()299 std::map<std::string, uint32_t> Inspector::GetConstantNameToIdMap() {
300   std::map<std::string, uint32_t> result;
301   for (auto* var : program_->AST().GlobalVariables()) {
302     auto* global = program_->Sem().Get<sem::GlobalVariable>(var);
303     if (global && global->IsOverridable()) {
304       auto name = program_->Symbols().NameFor(var->symbol);
305       result[name] = global->ConstantId();
306     }
307   }
308   return result;
309 }
310 
GetStorageSize(const std::string & entry_point)311 uint32_t Inspector::GetStorageSize(const std::string& entry_point) {
312   auto* func = FindEntryPointByName(entry_point);
313   if (!func) {
314     return 0;
315   }
316 
317   size_t size = 0;
318   auto* func_sem = program_->Sem().Get(func);
319   for (auto& ruv : func_sem->TransitivelyReferencedUniformVariables()) {
320     const sem::Struct* s = ruv.first->Type()->UnwrapRef()->As<sem::Struct>();
321     if (s && s->IsBlockDecorated()) {
322       size += s->Size();
323     }
324   }
325   for (auto& rsv : func_sem->TransitivelyReferencedStorageBufferVariables()) {
326     const sem::Struct* s = rsv.first->Type()->UnwrapRef()->As<sem::Struct>();
327     if (s) {
328       size += s->Size();
329     }
330   }
331 
332   if (static_cast<uint64_t>(size) >
333       static_cast<uint64_t>(std::numeric_limits<uint32_t>::max())) {
334     return std::numeric_limits<uint32_t>::max();
335   }
336   return static_cast<uint32_t>(size);
337 }
338 
GetResourceBindings(const std::string & entry_point)339 std::vector<ResourceBinding> Inspector::GetResourceBindings(
340     const std::string& entry_point) {
341   auto* func = FindEntryPointByName(entry_point);
342   if (!func) {
343     return {};
344   }
345 
346   std::vector<ResourceBinding> result;
347   for (auto fn : {
348            &Inspector::GetUniformBufferResourceBindings,
349            &Inspector::GetStorageBufferResourceBindings,
350            &Inspector::GetReadOnlyStorageBufferResourceBindings,
351            &Inspector::GetSamplerResourceBindings,
352            &Inspector::GetComparisonSamplerResourceBindings,
353            &Inspector::GetSampledTextureResourceBindings,
354            &Inspector::GetMultisampledTextureResourceBindings,
355            &Inspector::GetWriteOnlyStorageTextureResourceBindings,
356            &Inspector::GetDepthTextureResourceBindings,
357            &Inspector::GetDepthMultisampledTextureResourceBindings,
358            &Inspector::GetExternalTextureResourceBindings,
359        }) {
360     AppendResourceBindings(&result, (this->*fn)(entry_point));
361   }
362   return result;
363 }
364 
GetUniformBufferResourceBindings(const std::string & entry_point)365 std::vector<ResourceBinding> Inspector::GetUniformBufferResourceBindings(
366     const std::string& entry_point) {
367   auto* func = FindEntryPointByName(entry_point);
368   if (!func) {
369     return {};
370   }
371 
372   std::vector<ResourceBinding> result;
373 
374   auto* func_sem = program_->Sem().Get(func);
375   for (auto& ruv : func_sem->TransitivelyReferencedUniformVariables()) {
376     auto* var = ruv.first;
377     auto binding_info = ruv.second;
378 
379     auto* unwrapped_type = var->Type()->UnwrapRef();
380     auto* str = unwrapped_type->As<sem::Struct>();
381     if (str == nullptr) {
382       continue;
383     }
384 
385     if (!str->IsBlockDecorated()) {
386       continue;
387     }
388 
389     ResourceBinding entry;
390     entry.resource_type = ResourceBinding::ResourceType::kUniformBuffer;
391     entry.bind_group = binding_info.group->value;
392     entry.binding = binding_info.binding->value;
393     entry.size = str->Size();
394     entry.size_no_padding = str->SizeNoPadding();
395 
396     result.push_back(entry);
397   }
398 
399   return result;
400 }
401 
GetStorageBufferResourceBindings(const std::string & entry_point)402 std::vector<ResourceBinding> Inspector::GetStorageBufferResourceBindings(
403     const std::string& entry_point) {
404   return GetStorageBufferResourceBindingsImpl(entry_point, false);
405 }
406 
407 std::vector<ResourceBinding>
GetReadOnlyStorageBufferResourceBindings(const std::string & entry_point)408 Inspector::GetReadOnlyStorageBufferResourceBindings(
409     const std::string& entry_point) {
410   return GetStorageBufferResourceBindingsImpl(entry_point, true);
411 }
412 
GetSamplerResourceBindings(const std::string & entry_point)413 std::vector<ResourceBinding> Inspector::GetSamplerResourceBindings(
414     const std::string& entry_point) {
415   auto* func = FindEntryPointByName(entry_point);
416   if (!func) {
417     return {};
418   }
419 
420   std::vector<ResourceBinding> result;
421 
422   auto* func_sem = program_->Sem().Get(func);
423   for (auto& rs : func_sem->TransitivelyReferencedSamplerVariables()) {
424     auto binding_info = rs.second;
425 
426     ResourceBinding entry;
427     entry.resource_type = ResourceBinding::ResourceType::kSampler;
428     entry.bind_group = binding_info.group->value;
429     entry.binding = binding_info.binding->value;
430 
431     result.push_back(entry);
432   }
433 
434   return result;
435 }
436 
GetComparisonSamplerResourceBindings(const std::string & entry_point)437 std::vector<ResourceBinding> Inspector::GetComparisonSamplerResourceBindings(
438     const std::string& entry_point) {
439   auto* func = FindEntryPointByName(entry_point);
440   if (!func) {
441     return {};
442   }
443 
444   std::vector<ResourceBinding> result;
445 
446   auto* func_sem = program_->Sem().Get(func);
447   for (auto& rcs :
448        func_sem->TransitivelyReferencedComparisonSamplerVariables()) {
449     auto binding_info = rcs.second;
450 
451     ResourceBinding entry;
452     entry.resource_type = ResourceBinding::ResourceType::kComparisonSampler;
453     entry.bind_group = binding_info.group->value;
454     entry.binding = binding_info.binding->value;
455 
456     result.push_back(entry);
457   }
458 
459   return result;
460 }
461 
GetSampledTextureResourceBindings(const std::string & entry_point)462 std::vector<ResourceBinding> Inspector::GetSampledTextureResourceBindings(
463     const std::string& entry_point) {
464   return GetSampledTextureResourceBindingsImpl(entry_point, false);
465 }
466 
GetMultisampledTextureResourceBindings(const std::string & entry_point)467 std::vector<ResourceBinding> Inspector::GetMultisampledTextureResourceBindings(
468     const std::string& entry_point) {
469   return GetSampledTextureResourceBindingsImpl(entry_point, true);
470 }
471 
472 std::vector<ResourceBinding>
GetWriteOnlyStorageTextureResourceBindings(const std::string & entry_point)473 Inspector::GetWriteOnlyStorageTextureResourceBindings(
474     const std::string& entry_point) {
475   return GetStorageTextureResourceBindingsImpl(entry_point);
476 }
477 
GetTextureResourceBindings(const std::string & entry_point,const tint::TypeInfo & texture_type,ResourceBinding::ResourceType resource_type)478 std::vector<ResourceBinding> Inspector::GetTextureResourceBindings(
479     const std::string& entry_point,
480     const tint::TypeInfo& texture_type,
481     ResourceBinding::ResourceType resource_type) {
482   auto* func = FindEntryPointByName(entry_point);
483   if (!func) {
484     return {};
485   }
486 
487   std::vector<ResourceBinding> result;
488   auto* func_sem = program_->Sem().Get(func);
489   for (auto& ref :
490        func_sem->TransitivelyReferencedVariablesOfType(texture_type)) {
491     auto* var = ref.first;
492     auto binding_info = ref.second;
493 
494     ResourceBinding entry;
495     entry.resource_type = resource_type;
496     entry.bind_group = binding_info.group->value;
497     entry.binding = binding_info.binding->value;
498 
499     auto* tex = var->Type()->UnwrapRef()->As<sem::Texture>();
500     entry.dim =
501         TypeTextureDimensionToResourceBindingTextureDimension(tex->dim());
502 
503     result.push_back(entry);
504   }
505 
506   return result;
507 }
508 
GetDepthTextureResourceBindings(const std::string & entry_point)509 std::vector<ResourceBinding> Inspector::GetDepthTextureResourceBindings(
510     const std::string& entry_point) {
511   return GetTextureResourceBindings(
512       entry_point, TypeInfo::Of<sem::DepthTexture>(),
513       ResourceBinding::ResourceType::kDepthTexture);
514 }
515 
516 std::vector<ResourceBinding>
GetDepthMultisampledTextureResourceBindings(const std::string & entry_point)517 Inspector::GetDepthMultisampledTextureResourceBindings(
518     const std::string& entry_point) {
519   return GetTextureResourceBindings(
520       entry_point, TypeInfo::Of<sem::DepthMultisampledTexture>(),
521       ResourceBinding::ResourceType::kDepthMultisampledTexture);
522 }
523 
GetExternalTextureResourceBindings(const std::string & entry_point)524 std::vector<ResourceBinding> Inspector::GetExternalTextureResourceBindings(
525     const std::string& entry_point) {
526   return GetTextureResourceBindings(
527       entry_point, TypeInfo::Of<sem::ExternalTexture>(),
528       ResourceBinding::ResourceType::kExternalTexture);
529 }
530 
GetSamplerTextureUses(const std::string & entry_point)531 std::vector<SamplerTexturePair> Inspector::GetSamplerTextureUses(
532     const std::string& entry_point) {
533   auto* func = FindEntryPointByName(entry_point);
534   if (!func) {
535     return {};
536   }
537 
538   GenerateSamplerTargets();
539 
540   auto it = sampler_targets_->find(entry_point);
541   if (it == sampler_targets_->end()) {
542     return {};
543   }
544   return it->second;
545 }
546 
GetWorkgroupStorageSize(const std::string & entry_point)547 uint32_t Inspector::GetWorkgroupStorageSize(const std::string& entry_point) {
548   auto* func = FindEntryPointByName(entry_point);
549   if (!func) {
550     return 0;
551   }
552 
553   uint32_t total_size = 0;
554   auto* func_sem = program_->Sem().Get(func);
555   for (const sem::Variable* var : func_sem->TransitivelyReferencedGlobals()) {
556     if (var->StorageClass() == ast::StorageClass::kWorkgroup) {
557       auto* ty = var->Type()->UnwrapRef();
558       uint32_t align = ty->Align();
559       uint32_t size = ty->Size();
560 
561       // This essentially matches std430 layout rules from GLSL, which are in
562       // turn specified as an upper bound for Vulkan layout sizing. Since D3D
563       // and Metal are even less specific, we assume Vulkan behavior as a
564       // good-enough approximation everywhere.
565       total_size += utils::RoundUp(align, size);
566     }
567   }
568 
569   return total_size;
570 }
571 
FindEntryPointByName(const std::string & name)572 const ast::Function* Inspector::FindEntryPointByName(const std::string& name) {
573   auto* func = program_->AST().Functions().Find(program_->Symbols().Get(name));
574   if (!func) {
575     diagnostics_.add_error(diag::System::Inspector, name + " was not found!");
576     return nullptr;
577   }
578 
579   if (!func->IsEntryPoint()) {
580     diagnostics_.add_error(diag::System::Inspector,
581                            name + " is not an entry point!");
582     return nullptr;
583   }
584 
585   return func;
586 }
587 
AddEntryPointInOutVariables(std::string name,const sem::Type * type,const ast::DecorationList & decorations,std::vector<StageVariable> & variables) const588 void Inspector::AddEntryPointInOutVariables(
589     std::string name,
590     const sem::Type* type,
591     const ast::DecorationList& decorations,
592     std::vector<StageVariable>& variables) const {
593   // Skip builtins.
594   if (ast::HasDecoration<ast::BuiltinDecoration>(decorations)) {
595     return;
596   }
597 
598   auto* unwrapped_type = type->UnwrapRef();
599 
600   if (auto* struct_ty = unwrapped_type->As<sem::Struct>()) {
601     // Recurse into members.
602     for (auto* member : struct_ty->Members()) {
603       AddEntryPointInOutVariables(
604           name + "." +
605               program_->Symbols().NameFor(member->Declaration()->symbol),
606           member->Type(), member->Declaration()->decorations, variables);
607     }
608     return;
609   }
610 
611   // Base case: add the variable.
612 
613   StageVariable stage_variable;
614   stage_variable.name = name;
615   std::tie(stage_variable.component_type, stage_variable.composition_type) =
616       CalculateComponentAndComposition(type);
617 
618   auto* location = ast::GetDecoration<ast::LocationDecoration>(decorations);
619   TINT_ASSERT(Inspector, location != nullptr);
620   stage_variable.has_location_decoration = true;
621   stage_variable.location_decoration = location->value;
622 
623   std::tie(stage_variable.interpolation_type,
624            stage_variable.interpolation_sampling) =
625       CalculateInterpolationData(type, decorations);
626 
627   variables.push_back(stage_variable);
628 }
629 
ContainsBuiltin(ast::Builtin builtin,const sem::Type * type,const ast::DecorationList & decorations) const630 bool Inspector::ContainsBuiltin(ast::Builtin builtin,
631                                 const sem::Type* type,
632                                 const ast::DecorationList& decorations) const {
633   auto* unwrapped_type = type->UnwrapRef();
634 
635   if (auto* struct_ty = unwrapped_type->As<sem::Struct>()) {
636     // Recurse into members.
637     for (auto* member : struct_ty->Members()) {
638       if (ContainsBuiltin(builtin, member->Type(),
639                           member->Declaration()->decorations)) {
640         return true;
641       }
642     }
643     return false;
644   }
645 
646   // Base case: check for builtin
647   auto* builtin_declaration =
648       ast::GetDecoration<ast::BuiltinDecoration>(decorations);
649   if (!builtin_declaration || builtin_declaration->builtin != builtin) {
650     return false;
651   }
652 
653   return true;
654 }
655 
GetStorageBufferResourceBindingsImpl(const std::string & entry_point,bool read_only)656 std::vector<ResourceBinding> Inspector::GetStorageBufferResourceBindingsImpl(
657     const std::string& entry_point,
658     bool read_only) {
659   auto* func = FindEntryPointByName(entry_point);
660   if (!func) {
661     return {};
662   }
663 
664   auto* func_sem = program_->Sem().Get(func);
665   std::vector<ResourceBinding> result;
666   for (auto& rsv : func_sem->TransitivelyReferencedStorageBufferVariables()) {
667     auto* var = rsv.first;
668     auto binding_info = rsv.second;
669 
670     if (read_only != (var->Access() == ast::Access::kRead)) {
671       continue;
672     }
673 
674     auto* str = var->Type()->UnwrapRef()->As<sem::Struct>();
675     if (!str) {
676       continue;
677     }
678 
679     ResourceBinding entry;
680     entry.resource_type =
681         read_only ? ResourceBinding::ResourceType::kReadOnlyStorageBuffer
682                   : ResourceBinding::ResourceType::kStorageBuffer;
683     entry.bind_group = binding_info.group->value;
684     entry.binding = binding_info.binding->value;
685     entry.size = str->Size();
686     entry.size_no_padding = str->SizeNoPadding();
687 
688     result.push_back(entry);
689   }
690 
691   return result;
692 }
693 
GetSampledTextureResourceBindingsImpl(const std::string & entry_point,bool multisampled_only)694 std::vector<ResourceBinding> Inspector::GetSampledTextureResourceBindingsImpl(
695     const std::string& entry_point,
696     bool multisampled_only) {
697   auto* func = FindEntryPointByName(entry_point);
698   if (!func) {
699     return {};
700   }
701 
702   std::vector<ResourceBinding> result;
703   auto* func_sem = program_->Sem().Get(func);
704   auto referenced_variables =
705       multisampled_only
706           ? func_sem->TransitivelyReferencedMultisampledTextureVariables()
707           : func_sem->TransitivelyReferencedSampledTextureVariables();
708   for (auto& ref : referenced_variables) {
709     auto* var = ref.first;
710     auto binding_info = ref.second;
711 
712     ResourceBinding entry;
713     entry.resource_type =
714         multisampled_only ? ResourceBinding::ResourceType::kMultisampledTexture
715                           : ResourceBinding::ResourceType::kSampledTexture;
716     entry.bind_group = binding_info.group->value;
717     entry.binding = binding_info.binding->value;
718 
719     auto* texture_type = var->Type()->UnwrapRef()->As<sem::Texture>();
720     entry.dim = TypeTextureDimensionToResourceBindingTextureDimension(
721         texture_type->dim());
722 
723     const sem::Type* base_type = nullptr;
724     if (multisampled_only) {
725       base_type = texture_type->As<sem::MultisampledTexture>()->type();
726     } else {
727       base_type = texture_type->As<sem::SampledTexture>()->type();
728     }
729     entry.sampled_kind = BaseTypeToSampledKind(base_type);
730 
731     result.push_back(entry);
732   }
733 
734   return result;
735 }
736 
GetStorageTextureResourceBindingsImpl(const std::string & entry_point)737 std::vector<ResourceBinding> Inspector::GetStorageTextureResourceBindingsImpl(
738     const std::string& entry_point) {
739   auto* func = FindEntryPointByName(entry_point);
740   if (!func) {
741     return {};
742   }
743 
744   auto* func_sem = program_->Sem().Get(func);
745   std::vector<ResourceBinding> result;
746   for (auto& ref :
747        func_sem->TransitivelyReferencedVariablesOfType<sem::StorageTexture>()) {
748     auto* var = ref.first;
749     auto binding_info = ref.second;
750 
751     auto* texture_type = var->Type()->UnwrapRef()->As<sem::StorageTexture>();
752 
753     ResourceBinding entry;
754     entry.resource_type =
755         ResourceBinding::ResourceType::kWriteOnlyStorageTexture;
756     entry.bind_group = binding_info.group->value;
757     entry.binding = binding_info.binding->value;
758 
759     entry.dim = TypeTextureDimensionToResourceBindingTextureDimension(
760         texture_type->dim());
761 
762     auto* base_type = texture_type->type();
763     entry.sampled_kind = BaseTypeToSampledKind(base_type);
764     entry.image_format = TypeImageFormatToResourceBindingImageFormat(
765         texture_type->image_format());
766 
767     result.push_back(entry);
768   }
769 
770   return result;
771 }
772 
GenerateSamplerTargets()773 void Inspector::GenerateSamplerTargets() {
774   // Do not re-generate, since |program_| should not change during the lifetime
775   // of the inspector.
776   if (sampler_targets_ != nullptr) {
777     return;
778   }
779 
780   sampler_targets_ = std::make_unique<std::unordered_map<
781       std::string, utils::UniqueVector<SamplerTexturePair>>>();
782 
783   auto& sem = program_->Sem();
784 
785   for (auto* node : program_->ASTNodes().Objects()) {
786     auto* c = node->As<ast::CallExpression>();
787     if (!c) {
788       continue;
789     }
790 
791     auto* call = sem.Get(c);
792     if (!call) {
793       continue;
794     }
795 
796     auto* i = call->Target()->As<sem::Intrinsic>();
797     if (!i) {
798       continue;
799     }
800 
801     const auto& signature = i->Signature();
802     int sampler_index = signature.IndexOf(sem::ParameterUsage::kSampler);
803     if (sampler_index == -1) {
804       continue;
805     }
806 
807     int texture_index = signature.IndexOf(sem::ParameterUsage::kTexture);
808     if (texture_index == -1) {
809       continue;
810     }
811 
812     auto* call_func = call->Stmt()->Function();
813     std::vector<const sem::Function*> entry_points;
814     if (call_func->Declaration()->IsEntryPoint()) {
815       entry_points = {call_func};
816     } else {
817       entry_points = call_func->AncestorEntryPoints();
818     }
819 
820     if (entry_points.empty()) {
821       continue;
822     }
823 
824     auto* t = c->args[texture_index];
825     auto* s = c->args[sampler_index];
826 
827     GetOriginatingResources(
828         std::array<const ast::Expression*, 2>{t, s},
829         [&](std::array<const sem::GlobalVariable*, 2> globals) {
830           auto* texture = globals[0];
831           sem::BindingPoint texture_binding_point = {
832               texture->Declaration()->BindingPoint().group->value,
833               texture->Declaration()->BindingPoint().binding->value};
834 
835           auto* sampler = globals[1];
836           sem::BindingPoint sampler_binding_point = {
837               sampler->Declaration()->BindingPoint().group->value,
838               sampler->Declaration()->BindingPoint().binding->value};
839 
840           for (auto* entry_point : entry_points) {
841             const auto& ep_name =
842                 program_->Symbols().NameFor(entry_point->Declaration()->symbol);
843             (*sampler_targets_)[ep_name].add(
844                 {sampler_binding_point, texture_binding_point});
845           }
846         });
847   }
848 }
849 
850 template <size_t N, typename F>
GetOriginatingResources(std::array<const ast::Expression *,N> exprs,F && callback)851 void Inspector::GetOriginatingResources(
852     std::array<const ast::Expression*, N> exprs,
853     F&& callback) {
854   if (!program_->IsValid()) {
855     TINT_ICE(Inspector, diagnostics_)
856         << "attempting to get originating resources in invalid program";
857     return;
858   }
859 
860   auto& sem = program_->Sem();
861 
862   std::array<const sem::GlobalVariable*, N> globals{};
863   std::array<const sem::Parameter*, N> parameters{};
864   utils::UniqueVector<const ast::CallExpression*> callsites;
865 
866   for (size_t i = 0; i < N; i++) {
867     auto*& expr = exprs[i];
868     // Resolve each of the expressions
869     while (true) {
870       if (auto* user = sem.Get<sem::VariableUser>(expr)) {
871         auto* var = user->Variable();
872 
873         if (auto* global = tint::As<sem::GlobalVariable>(var)) {
874           // Found the global resource declaration.
875           globals[i] = global;
876           break;  // Done with this expression.
877         }
878 
879         if (auto* local = tint::As<sem::LocalVariable>(var)) {
880           // Chase the variable
881           expr = local->Declaration()->constructor;
882           if (!expr) {
883             TINT_ICE(Inspector, diagnostics_)
884                 << "resource variable had no initializer";
885             return;
886           }
887           continue;  // Continue chasing the expression in this function
888         }
889 
890         if (auto* param = tint::As<sem::Parameter>(var)) {
891           // Gather each of the callers of this function
892           auto* func = tint::As<sem::Function>(param->Owner());
893           if (func->CallSites().empty()) {
894             // One or more of the expressions is a parameter, but this function
895             // is not called. Ignore.
896             return;
897           }
898           for (auto* call : func->CallSites()) {
899             callsites.add(call->Declaration());
900           }
901           // Need to evaluate each function call with the group of
902           // expressions, so move on to the next expression.
903           parameters[i] = param;
904           break;
905         }
906 
907         TINT_ICE(Inspector, diagnostics_)
908             << "unexpected variable type " << var->TypeInfo().name;
909       }
910 
911       if (auto* unary = tint::As<ast::UnaryOpExpression>(expr)) {
912         switch (unary->op) {
913           case ast::UnaryOp::kAddressOf:
914           case ast::UnaryOp::kIndirection:
915             // `*` and `&` are the only valid unary ops for a resource type,
916             // and must be balanced in order for the program to have passed
917             // validation. Just skip past these.
918             expr = unary->expr;
919             continue;
920           default: {
921             TINT_ICE(Inspector, diagnostics_)
922                 << "unexpected unary op on resource: " << unary->op;
923             return;
924           }
925         }
926       }
927 
928       TINT_ICE(Inspector, diagnostics_)
929           << "cannot resolve originating resource with expression type "
930           << expr->TypeInfo().name;
931       return;
932     }
933   }
934 
935   if (callsites.size()) {
936     for (auto* call_expr : callsites) {
937       // Make a copy of the expressions for this callsite
938       std::array<const ast::Expression*, N> call_exprs = exprs;
939       // Patch all the parameter expressions with their argument
940       for (size_t i = 0; i < N; i++) {
941         if (auto* param = parameters[i]) {
942           call_exprs[i] = call_expr->args[param->Index()];
943         }
944       }
945       // Now call GetOriginatingResources() with from the callsite
946       GetOriginatingResources(call_exprs, callback);
947     }
948   } else {
949     // All the expressions resolved to globals
950     callback(globals);
951   }
952 }
953 
954 }  // namespace inspector
955 }  // namespace tint
956