• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2025 Google LLC
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 #ifndef LIBSPIRV_OPT_SPLIT_COMBINED_IMAGE_SAMPLER_PASS_H_
16 #define LIBSPIRV_OPT_SPLIT_COMBINED_IMAGE_SAMPLER_PASS_H_
17 
18 #include <unordered_map>
19 #include <utility>
20 #include <vector>
21 
22 #include "source/diagnostic.h"
23 #include "source/opt/decoration_manager.h"
24 #include "source/opt/def_use_manager.h"
25 #include "source/opt/pass.h"
26 #include "source/opt/type_manager.h"
27 
28 namespace spvtools {
29 namespace opt {
30 
31 // Replaces each combined-image sampler variable with an image variable
32 // and a sampler variable. Similar for function parameters.
33 //
34 // Copy the descriptor set and binding number. Vulkan allows this, surprisingly.
35 class SplitCombinedImageSamplerPass : public Pass {
36  public:
37   virtual ~SplitCombinedImageSamplerPass() override = default;
name()38   const char* name() const override { return "split-combined-image-sampler"; }
39   IRContext::Analysis GetPreservedAnalyses() override;
40   Status Process() override;
41 
42  private:
43   // Records failure for the current module, and returns a stream
44   // that can be used to provide user error information to the message
45   // consumer.
46   spvtools::DiagnosticStream Fail();
47 
48   // Find variables that contain combined texture-samplers, or arrays of them.
49   // Also populate known_globals_.
50   void FindCombinedTextureSamplers();
51 
52   // Returns the sampler type. If it does not yet exist, then it is created
53   // and placed before the first sampled image type.
54   Instruction* GetSamplerType();
55 
56   // Remaps function types and function declarations.  Each
57   // pointer-to-sampled-image-type operand is replaced with a pair of
58   // pointer-to-image-type and pointer-to-sampler-type pair.
59   // Updates the def-use manager and type manager.
60   spv_result_t RemapFunctions();
61   // Remap resource variables.
62   // Updates the def-use manager.
63   spv_result_t RemapVars();
64   // Remap a single resource variable for combined var.
65   // Updates the def-use manager and the decorations manager.
66   spv_result_t RemapVar(Instruction* combined_var);
67   // Transitively remaps uses of the combined object with uses of the
68   // decomposed image and sampler parts.  The combined object can be sampled
69   // image value, a pointer to one, an array of one, or a pointer to an array
70   // of one. The image and sampler parts have corresponding shapes.
71   // Updates the def-use manager and the decorations manager.
72   spv_result_t RemapUses(Instruction* combined, Instruction* image_part,
73                          Instruction* sampler_part);
74   // Removes types that are no longer referenced.
75   spv_result_t RemoveDeadTypes();
76 
77   // Returns the type instruction for a UniformConstant pointer to the given
78   // pointee type. If it does not yet exist, the new type instruction is created
79   // and placed immediately after the pointee type instruction. Updates def-use
80   // and type managers, and the set of known globals.
81   Instruction* MakeUniformConstantPointer(Instruction* pointee);
82 
83   // Returns the ID of the pointee type for a pointer value instruction.
PointeeTypeId(Instruction * ptr_value)84   uint32_t PointeeTypeId(Instruction* ptr_value) {
85     auto* ptr_ty = def_use_mgr_->GetDef(ptr_value->type_id());
86     assert(ptr_ty->opcode() == spv::Op::OpTypePointer);
87     return ptr_ty->GetSingleWordInOperand(1);
88   }
89 
90   // Creates a new OpName instruction mapping the given name to the given
91   // string, and adds it to the module at the end of the OpName and OpMemberName
92   // section.
93   void AddOpName(uint32_t id, const std::string& name);
94 
95   // Cached from the IRContext. Valid while Process() is running.
96   analysis::DefUseManager* def_use_mgr_ = nullptr;
97   // Cached from the IRContext. Valid while Process() is running.
98   analysis::TypeManager* type_mgr_ = nullptr;
99 
100   // Did processing modify the module?
101   bool modified_ = false;
Ok()102   Pass::Status Ok() {
103     return modified_ ? Pass::Status::SuccessWithChange
104                      : Pass::Status::SuccessWithoutChange;
105   }
106 
107   // The first OpTypeSampledImage instruction in the module, if one exists.
108   Instruction* first_sampled_image_type_ = nullptr;
109   // An OpTypeSampler instruction, if one existed already, or if we created one.
110   Instruction* sampler_type_ = nullptr;
111 
112   // The known types and module-scope values.
113   // We use this to know when a new such value was created.
114   std::unordered_set<uint32_t> known_globals_;
IsKnownGlobal(uint32_t id)115   bool IsKnownGlobal(uint32_t id) const {
116     return known_globals_.find(id) != known_globals_.end();
117   }
RegisterGlobal(uint32_t id)118   void RegisterGlobal(uint32_t id) { known_globals_.insert(id); }
RegisterNewGlobal(uint32_t id)119   void RegisterNewGlobal(uint32_t id) {
120     modified_ = true;
121     RegisterGlobal(id);
122   }
123 
124   // Deletes an instruction and associated debug and decoration instructions.
125   // Updates the def-use manager.
126   void KillInst(Instruction* inst);
127 
128   // Combined types.  The known combined sampled-image type,
129   // and recursively pointers or arrays of them.
130   std::unordered_set<uint32_t> combined_types_;
131   // The pre-existing types this pass should remove: pointer to
132   // combined type, array of combined type, pointer to array of combined type.
133   std::vector<uint32_t> combined_types_to_remove_;
134   // Is an OpTypeSampledImage used as a function parameter? Those should be
135   // transformed.
136   bool sampled_image_used_as_param_ = false;
137 
138   // Remaps a combined-kind type to corresponding sampler-kind and image-kind
139   // of type.
140   struct TypeRemapInfo {
141     // The instruction for the combined type, pointer to combined type,
142     // or point to array of combined type.
143     Instruction* combined_kind_type;
144     // The corresponding image type, with the same shape of indirection as the
145     // combined_kind_type.
146     Instruction* image_kind_type;
147     // The corresponding sampler type, with the same shape of indirection as the
148     // combined_kind_type.
149     Instruction* sampler_kind_type;
150   };
151   // Maps the ID of a combined-image-sampler type kind to its corresponding
152   // split parts.
153   std::unordered_map<uint32_t, TypeRemapInfo> type_remap_;
154 
155   // Returns the image-like and sampler-like types of the same indirection shape
156   // as the given combined-like type.  If combined_kind_type is not a combined
157   // type or a pointer to one, or an array of one or a pointer to an array of
158   // one, then returns a pair of null pointer. Either both components are
159   // non-null, or both components are null. Updates the def-use manager and the
160   // type manager if new instructions are created.
161   std::pair<Instruction*, Instruction*> SplitType(
162       Instruction& combined_kind_type);
163 
164   // The combined-image-sampler variables to be replaced.
165   std::vector<Instruction*> ordered_vars_;
166 };
167 }  // namespace opt
168 }  // namespace spvtools
169 #endif  // LIBSPIRV_OPT_SPLIT_COMBINED_IMAGE_SAMPLER_PASS_H_
170