1 // Copyright (c) 2021 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 SOURCE_OPT_CONVERT_TO_SAMPLED_IMAGE_PASS_H_ 16 #define SOURCE_OPT_CONVERT_TO_SAMPLED_IMAGE_PASS_H_ 17 18 #include <memory> 19 #include <unordered_set> 20 #include <utility> 21 22 #include "source/opt/pass.h" 23 #include "source/opt/types.h" 24 25 namespace spvtools { 26 namespace opt { 27 28 // A struct for a pair of descriptor set and binding. 29 struct DescriptorSetAndBinding { 30 uint32_t descriptor_set; 31 uint32_t binding; 32 33 bool operator==(const DescriptorSetAndBinding& descriptor_set_binding) const { 34 return descriptor_set_binding.descriptor_set == descriptor_set && 35 descriptor_set_binding.binding == binding; 36 } 37 }; 38 39 // See optimizer.hpp for documentation. 40 class ConvertToSampledImagePass : public Pass { 41 public: 42 // Hashing functor for the pair of descriptor set and binding. 43 struct DescriptorSetAndBindingHash { operatorDescriptorSetAndBindingHash44 size_t operator()( 45 const DescriptorSetAndBinding& descriptor_set_binding) const { 46 return std::hash<uint32_t>()(descriptor_set_binding.descriptor_set) ^ 47 std::hash<uint32_t>()(descriptor_set_binding.binding); 48 } 49 }; 50 51 using SetOfDescriptorSetAndBindingPairs = 52 std::unordered_set<DescriptorSetAndBinding, DescriptorSetAndBindingHash>; 53 using DescriptorSetBindingToInstruction = 54 std::unordered_map<DescriptorSetAndBinding, Instruction*, 55 DescriptorSetAndBindingHash>; 56 ConvertToSampledImagePass(const std::vector<DescriptorSetAndBinding> & descriptor_set_binding_pairs)57 explicit ConvertToSampledImagePass( 58 const std::vector<DescriptorSetAndBinding>& descriptor_set_binding_pairs) 59 : descriptor_set_binding_pairs_(descriptor_set_binding_pairs.begin(), 60 descriptor_set_binding_pairs.end()) {} 61 name()62 const char* name() const override { return "convert-to-sampled-image"; } 63 Status Process() override; 64 65 // Parses the given null-terminated C string to get a vector of descriptor set 66 // and binding pairs. Returns a unique pointer to the vector of descriptor set 67 // and binding pairs built from the given |str| on success. Returns a nullptr 68 // if the given string is not valid for building the vector of pairs. 69 // A valid string for building the vector of pairs should follow the rule 70 // below: 71 // 72 // "<descriptor set>:<binding> <descriptor set>:<binding> ..." 73 // Example: 74 // "3:5 2:1 0:4" 75 // 76 // Entries are separated with blank spaces (i.e.:' ', '\n', '\r', '\t', 77 // '\f', '\v'). Each entry corresponds to a descriptor set and binding pair. 78 // Multiple spaces between, before or after entries are allowed. However, 79 // spaces are not allowed within a descriptor set or binding. 80 // 81 // In each entry, the descriptor set and binding are separated by ':'. 82 // Missing ':' in any entry is invalid. And it is invalid to have blank 83 // spaces in between the descriptor set and ':' or ':' and the binding. 84 // 85 // <descriptor set>: the descriptor set. 86 // The text must represent a valid uint32_t number. 87 // 88 // <binding>: the binding. 89 // The text must represent a valid uint32_t number. 90 static std::unique_ptr<std::vector<DescriptorSetAndBinding>> 91 ParseDescriptorSetBindingPairsString(const char* str); 92 93 private: 94 // Collects resources to convert to sampled image and saves them in 95 // |descriptor_set_binding_pair_to_sampler| if the resource is a sampler and 96 // saves them in |descriptor_set_binding_pair_to_image| if the resource is an 97 // image. Returns false if two samplers or two images have the same descriptor 98 // set and binding. Otherwise, returns true. 99 bool CollectResourcesToConvert( 100 DescriptorSetBindingToInstruction* descriptor_set_binding_pair_to_sampler, 101 DescriptorSetBindingToInstruction* descriptor_set_binding_pair_to_image) 102 const; 103 104 // Finds an OpDecorate with DescriptorSet decorating |inst| and another 105 // OpDecorate with Binding decorating |inst|. Stores the descriptor set and 106 // binding in |descriptor_set_binding|. Returns whether it successfully finds 107 // the descriptor set and binding or not. 108 bool GetDescriptorSetBinding( 109 const Instruction& inst, 110 DescriptorSetAndBinding* descriptor_set_binding) const; 111 112 // Returns whether |descriptor_set_binding| is a pair of a descriptor set 113 // and a binding that we have to convert resources with it to a sampled image 114 // or not. 115 bool ShouldResourceBeConverted( 116 const DescriptorSetAndBinding& descriptor_set_binding) const; 117 118 // Returns the pointee type of the type of variable |variable|. If |variable| 119 // is not an OpVariable instruction, just returns nullptr. 120 const analysis::Type* GetVariableType(const Instruction& variable) const; 121 122 // Returns the storage class of |variable|. 123 SpvStorageClass GetStorageClass(const Instruction& variable) const; 124 125 // Finds |inst|'s users whose opcode is |user_opcode| or users of OpCopyObject 126 // instructions of |inst| whose opcode is |user_opcode| and puts them in 127 // |uses|. 128 void FindUses(const Instruction* inst, std::vector<Instruction*>* uses, 129 uint32_t user_opcode) const; 130 131 // Finds OpImage* instructions using |image| or OpCopyObject instructions that 132 // copy |image| and puts them in |uses|. 133 void FindUsesOfImage(const Instruction* image, 134 std::vector<Instruction*>* uses) const; 135 136 // Creates an OpImage instruction that extracts the image from the sampled 137 // image |sampled_image|. 138 Instruction* CreateImageExtraction(Instruction* sampled_image); 139 140 // Converts |image_variable| whose type is an image pointer to sampled image 141 // type. Updates users of |image_variable| accordingly. If some instructions 142 // e.g., OpImageRead use |image_variable| as an Image operand, creates an 143 // image extracted from the sampled image using OpImage and replace the Image 144 // operands of the users with the extracted image. If some OpSampledImage 145 // instructions use |image_variable| and sampler whose descriptor set and 146 // binding are the same with |image_variable|, just combines |image_variable| 147 // and the sampler to a sampled image. 148 Pass::Status UpdateImageVariableToSampledImage( 149 Instruction* image_variable, 150 const DescriptorSetAndBinding& descriptor_set_binding); 151 152 // Returns the id of type sampled image type whose image type is the one of 153 // |image_variable|. 154 uint32_t GetSampledImageTypeForImage(Instruction* image_variable); 155 156 // Moves |inst| next to the OpType* instruction with |type_id|. 157 void MoveInstructionNextToType(Instruction* inst, uint32_t type_id); 158 159 // Converts |image_variable| whose type is an image pointer to sampled image 160 // with the type id |sampled_image_type_id|. Returns whether it successfully 161 // converts the type of |image_variable| or not. 162 bool ConvertImageVariableToSampledImage(Instruction* image_variable, 163 uint32_t sampled_image_type_id); 164 165 // Replaces |sampled_image_load| instruction used by OpImage* with the image 166 // extracted from |sampled_image_load|. Returns the extracted image or nullptr 167 // if it does not have uses. 168 Instruction* UpdateImageUses(Instruction* sampled_image_load); 169 170 // Returns true if the sampler of |sampled_image_inst| is decorated by a 171 // descriptor set and a binding |descriptor_set_binding|. 172 bool IsSamplerOfSampledImageDecoratedByDescriptorSetBinding( 173 Instruction* sampled_image_inst, 174 const DescriptorSetAndBinding& descriptor_set_binding); 175 176 // Replaces OpSampledImage instructions using |image_load| with |image_load| 177 // if the sampler of the OpSampledImage instruction has descriptor set and 178 // binding |image_descriptor_set_binding|. Otherwise, replaces |image_load| 179 // with |image_extraction|. 180 void UpdateSampledImageUses( 181 Instruction* image_load, Instruction* image_extraction, 182 const DescriptorSetAndBinding& image_descriptor_set_binding); 183 184 // Checks the uses of |sampler_variable|. When a sampler is used by 185 // OpSampledImage instruction, the corresponding image must be 186 // |image_to_be_combined_with| that should be already converted to a sampled 187 // image by UpdateImageVariableToSampledImage() method. 188 Pass::Status CheckUsesOfSamplerVariable( 189 const Instruction* sampler_variable, 190 Instruction* image_to_be_combined_with); 191 192 // Returns true if Image operand of |sampled_image_inst| is the image of 193 // |image_variable|. 194 bool DoesSampledImageReferenceImage(Instruction* sampled_image_inst, 195 Instruction* image_variable); 196 197 // A set of pairs of descriptor set and binding. If an image and/or a sampler 198 // have a pair of descriptor set and binding that is an element of 199 // |descriptor_set_binding_pairs_|, they/it will be converted to a sampled 200 // image by this pass. 201 const SetOfDescriptorSetAndBindingPairs descriptor_set_binding_pairs_; 202 }; 203 204 } // namespace opt 205 } // namespace spvtools 206 207 #endif // SOURCE_OPT_CONVERT_TO_SAMPLED_IMAGE_PASS_H_ 208