| // Copyright (c) 2025 Google LLC |
| // |
| // 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. |
| |
| #ifndef LIBSPIRV_OPT_SPLIT_COMBINED_IMAGE_SAMPLER_PASS_H_ |
| #define LIBSPIRV_OPT_SPLIT_COMBINED_IMAGE_SAMPLER_PASS_H_ |
| |
| #include <unordered_map> |
| #include <utility> |
| #include <vector> |
| |
| #include "source/diagnostic.h" |
| #include "source/opt/decoration_manager.h" |
| #include "source/opt/def_use_manager.h" |
| #include "source/opt/pass.h" |
| #include "source/opt/type_manager.h" |
| |
| namespace spvtools { |
| namespace opt { |
| |
| // Replaces each combined-image sampler variable with an image variable |
| // and a sampler variable. Similar for function parameters. |
| // |
| // Copy the descriptor set and binding number. Vulkan allows this, surprisingly. |
| class SplitCombinedImageSamplerPass : public Pass { |
| public: |
| virtual ~SplitCombinedImageSamplerPass() override = default; |
| const char* name() const override { return "split-combined-image-sampler"; } |
| IRContext::Analysis GetPreservedAnalyses() override; |
| Status Process() override; |
| |
| private: |
| // Records failure for the current module, and returns a stream |
| // that can be used to provide user error information to the message |
| // consumer. |
| spvtools::DiagnosticStream Fail(); |
| |
| // Find variables that contain combined texture-samplers, or arrays of them. |
| // Also populate known_globals_. |
| void FindCombinedTextureSamplers(); |
| |
| // Returns the sampler type. If it does not yet exist, then it is created |
| // and placed before the first sampled image type. |
| Instruction* GetSamplerType(); |
| |
| // Remaps function types and function declarations. Each |
| // pointer-to-sampled-image-type operand is replaced with a pair of |
| // pointer-to-image-type and pointer-to-sampler-type pair. |
| // Updates the def-use manager and type manager. |
| spv_result_t RemapFunctions(); |
| // Remap resource variables. |
| // Updates the def-use manager. |
| spv_result_t RemapVars(); |
| // Remap a single resource variable for combined var. |
| // Updates the def-use manager and the decorations manager. |
| spv_result_t RemapVar(Instruction* combined_var); |
| // Transitively remaps uses of the combined object with uses of the |
| // decomposed image and sampler parts. The combined object can be sampled |
| // image value, a pointer to one, an array of one, or a pointer to an array |
| // of one. The image and sampler parts have corresponding shapes. |
| // Updates the def-use manager and the decorations manager. |
| spv_result_t RemapUses(Instruction* combined, Instruction* image_part, |
| Instruction* sampler_part); |
| // Removes types that are no longer referenced. |
| spv_result_t RemoveDeadTypes(); |
| |
| // Returns the type instruction for a UniformConstant pointer to the given |
| // pointee type. If it does not yet exist, the new type instruction is created |
| // and placed immediately after the pointee type instruction. Updates def-use |
| // and type managers, and the set of known globals. |
| Instruction* MakeUniformConstantPointer(Instruction* pointee); |
| |
| // Returns the ID of the pointee type for a pointer value instruction. |
| uint32_t PointeeTypeId(Instruction* ptr_value) { |
| auto* ptr_ty = def_use_mgr_->GetDef(ptr_value->type_id()); |
| assert(ptr_ty->opcode() == spv::Op::OpTypePointer); |
| return ptr_ty->GetSingleWordInOperand(1); |
| } |
| |
| // Creates a new OpName instruction mapping the given name to the given |
| // string, and adds it to the module at the end of the OpName and OpMemberName |
| // section. |
| void AddOpName(uint32_t id, const std::string& name); |
| |
| // Cached from the IRContext. Valid while Process() is running. |
| analysis::DefUseManager* def_use_mgr_ = nullptr; |
| // Cached from the IRContext. Valid while Process() is running. |
| analysis::TypeManager* type_mgr_ = nullptr; |
| |
| // Did processing modify the module? |
| bool modified_ = false; |
| Pass::Status Ok() { |
| return modified_ ? Pass::Status::SuccessWithChange |
| : Pass::Status::SuccessWithoutChange; |
| } |
| |
| // The first OpTypeSampledImage instruction in the module, if one exists. |
| Instruction* first_sampled_image_type_ = nullptr; |
| // An OpTypeSampler instruction, if one existed already, or if we created one. |
| Instruction* sampler_type_ = nullptr; |
| |
| // The known types and module-scope values. |
| // We use this to know when a new such value was created. |
| std::unordered_set<uint32_t> known_globals_; |
| bool IsKnownGlobal(uint32_t id) const { |
| return known_globals_.find(id) != known_globals_.end(); |
| } |
| void RegisterGlobal(uint32_t id) { known_globals_.insert(id); } |
| void RegisterNewGlobal(uint32_t id) { |
| modified_ = true; |
| RegisterGlobal(id); |
| } |
| |
| // Deletes an instruction and associated debug and decoration instructions. |
| // Updates the def-use manager. |
| void KillInst(Instruction* inst); |
| |
| // Combined types. The known combined sampled-image type, |
| // and recursively pointers or arrays of them. |
| std::unordered_set<uint32_t> combined_types_; |
| // The pre-existing types this pass should remove: pointer to |
| // combined type, array of combined type, pointer to array of combined type. |
| std::vector<uint32_t> combined_types_to_remove_; |
| // Is an OpTypeSampledImage used as a function parameter? Those should be |
| // transformed. |
| bool sampled_image_used_as_param_ = false; |
| |
| // Remaps a combined-kind type to corresponding sampler-kind and image-kind |
| // of type. |
| struct TypeRemapInfo { |
| // The instruction for the combined type, pointer to combined type, |
| // or point to array of combined type. |
| Instruction* combined_kind_type; |
| // The corresponding image type, with the same shape of indirection as the |
| // combined_kind_type. |
| Instruction* image_kind_type; |
| // The corresponding sampler type, with the same shape of indirection as the |
| // combined_kind_type. |
| Instruction* sampler_kind_type; |
| }; |
| // Maps the ID of a combined-image-sampler type kind to its corresponding |
| // split parts. |
| std::unordered_map<uint32_t, TypeRemapInfo> type_remap_; |
| |
| // Returns the image-like and sampler-like types of the same indirection shape |
| // as the given combined-like type. If combined_kind_type is not a combined |
| // type or a pointer to one, or an array of one or a pointer to an array of |
| // one, then returns a pair of null pointer. Either both components are |
| // non-null, or both components are null. Updates the def-use manager and the |
| // type manager if new instructions are created. |
| std::pair<Instruction*, Instruction*> SplitType( |
| Instruction& combined_kind_type); |
| |
| // The combined-image-sampler variables to be replaced. |
| std::vector<Instruction*> ordered_vars_; |
| }; |
| } // namespace opt |
| } // namespace spvtools |
| #endif // LIBSPIRV_OPT_SPLIT_COMBINED_IMAGE_SAMPLER_PASS_H_ |