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