1 /* Copyright (c) 2015-2017 The Khronos Group Inc. 2 * Copyright (c) 2015-2017 Valve Corporation 3 * Copyright (c) 2015-2017 LunarG, Inc. 4 * Copyright (C) 2015-2017 Google Inc. 5 * 6 * Licensed under the Apache License, Version 2.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 * 18 * Author: Chris Forbes <chrisf@ijw.co.nz> 19 */ 20 #ifndef VULKAN_SHADER_VALIDATION_H 21 #define VULKAN_SHADER_VALIDATION_H 22 23 #include <spirv_tools_commit_id.h> 24 25 // A forward iterator over spirv instructions. Provides easy access to len, opcode, and content words 26 // without the caller needing to care too much about the physical SPIRV module layout. 27 struct spirv_inst_iter { 28 std::vector<uint32_t>::const_iterator zero; 29 std::vector<uint32_t>::const_iterator it; 30 lenspirv_inst_iter31 uint32_t len() { 32 auto result = *it >> 16; 33 assert(result > 0); 34 return result; 35 } 36 opcodespirv_inst_iter37 uint32_t opcode() { return *it & 0x0ffffu; } 38 wordspirv_inst_iter39 uint32_t const &word(unsigned n) { 40 assert(n < len()); 41 return it[n]; 42 } 43 offsetspirv_inst_iter44 uint32_t offset() { return (uint32_t)(it - zero); } 45 spirv_inst_iterspirv_inst_iter46 spirv_inst_iter() {} 47 spirv_inst_iterspirv_inst_iter48 spirv_inst_iter(std::vector<uint32_t>::const_iterator zero, std::vector<uint32_t>::const_iterator it) : zero(zero), it(it) {} 49 50 bool operator==(spirv_inst_iter const &other) { return it == other.it; } 51 52 bool operator!=(spirv_inst_iter const &other) { return it != other.it; } 53 54 spirv_inst_iter operator++(int) { // x++ 55 spirv_inst_iter ii = *this; 56 it += len(); 57 return ii; 58 } 59 60 spirv_inst_iter operator++() { // ++x; 61 it += len(); 62 return *this; 63 } 64 65 // The iterator and the value are the same thing. 66 spirv_inst_iter &operator*() { return *this; } 67 spirv_inst_iter const &operator*() const { return *this; } 68 }; 69 70 struct shader_module { 71 // The spirv image itself 72 std::vector<uint32_t> words; 73 // A mapping of <id> to the first word of its def. this is useful because walking type 74 // trees, constant expressions, etc requires jumping all over the instruction stream. 75 std::unordered_map<unsigned, unsigned> def_index; 76 bool has_valid_spirv; 77 shader_moduleshader_module78 shader_module(VkShaderModuleCreateInfo const *pCreateInfo) 79 : words((uint32_t *)pCreateInfo->pCode, (uint32_t *)pCreateInfo->pCode + pCreateInfo->codeSize / sizeof(uint32_t)), 80 def_index(), 81 has_valid_spirv(true) { 82 build_def_index(); 83 } 84 shader_moduleshader_module85 shader_module() : has_valid_spirv(false) {} 86 87 // Expose begin() / end() to enable range-based for beginshader_module88 spirv_inst_iter begin() const { return spirv_inst_iter(words.begin(), words.begin() + 5); } // First insn endshader_module89 spirv_inst_iter end() const { return spirv_inst_iter(words.begin(), words.end()); } // Just past last insn 90 // Given an offset into the module, produce an iterator there. atshader_module91 spirv_inst_iter at(unsigned offset) const { return spirv_inst_iter(words.begin(), words.begin() + offset); } 92 93 // Gets an iterator to the definition of an id get_defshader_module94 spirv_inst_iter get_def(unsigned id) const { 95 auto it = def_index.find(id); 96 if (it == def_index.end()) { 97 return end(); 98 } 99 return at(it->second); 100 } 101 102 void build_def_index(); 103 }; 104 105 class ValidationCache { 106 // hashes of shaders that have passed validation before, and can be skipped. 107 // we don't store negative results, as we would have to also store what was 108 // wrong with them; also, we expect they will get fixed, so we're less 109 // likely to see them again. 110 std::unordered_set<uint32_t> good_shader_hashes; ValidationCache()111 ValidationCache() {} 112 113 public: Create(VkValidationCacheCreateInfoEXT const * pCreateInfo)114 static VkValidationCacheEXT Create(VkValidationCacheCreateInfoEXT const *pCreateInfo) { 115 auto cache = new ValidationCache(); 116 cache->Load(pCreateInfo); 117 return VkValidationCacheEXT(cache); 118 } 119 Load(VkValidationCacheCreateInfoEXT const * pCreateInfo)120 void Load(VkValidationCacheCreateInfoEXT const *pCreateInfo) { 121 const auto headerSize = 2 * sizeof(uint32_t) + VK_UUID_SIZE; 122 auto size = headerSize; 123 if (!pCreateInfo->pInitialData || pCreateInfo->initialDataSize < size) return; 124 125 uint32_t const *data = (uint32_t const *)pCreateInfo->pInitialData; 126 if (data[0] != size) return; 127 if (data[1] != VK_VALIDATION_CACHE_HEADER_VERSION_ONE_EXT) return; 128 uint8_t expected_uuid[VK_UUID_SIZE]; 129 Sha1ToVkUuid(SPIRV_TOOLS_COMMIT_ID, expected_uuid); 130 if (memcmp(&data[2], expected_uuid, VK_UUID_SIZE) != 0) return; // different version 131 132 data = (uint32_t const *)(reinterpret_cast<uint8_t const *>(data) + headerSize); 133 134 for (; size < pCreateInfo->initialDataSize; data++, size += sizeof(uint32_t)) { 135 good_shader_hashes.insert(*data); 136 } 137 } 138 Write(size_t * pDataSize,void * pData)139 void Write(size_t *pDataSize, void *pData) { 140 const auto headerSize = 2 * sizeof(uint32_t) + VK_UUID_SIZE; // 4 bytes for header size + 4 bytes for version number + UUID 141 if (!pData) { 142 *pDataSize = headerSize + good_shader_hashes.size() * sizeof(uint32_t); 143 return; 144 } 145 146 if (*pDataSize < headerSize) { 147 *pDataSize = 0; 148 return; // Too small for even the header! 149 } 150 151 uint32_t *out = (uint32_t *)pData; 152 size_t actualSize = headerSize; 153 154 // Write the header 155 *out++ = headerSize; 156 *out++ = VK_VALIDATION_CACHE_HEADER_VERSION_ONE_EXT; 157 Sha1ToVkUuid(SPIRV_TOOLS_COMMIT_ID, reinterpret_cast<uint8_t *>(out)); 158 out = (uint32_t *)(reinterpret_cast<uint8_t *>(out) + VK_UUID_SIZE); 159 160 for (auto it = good_shader_hashes.begin(); it != good_shader_hashes.end() && actualSize < *pDataSize; 161 it++, out++, actualSize += sizeof(uint32_t)) { 162 *out = *it; 163 } 164 165 *pDataSize = actualSize; 166 } 167 Merge(ValidationCache const * other)168 void Merge(ValidationCache const *other) { 169 good_shader_hashes.reserve(good_shader_hashes.size() + other->good_shader_hashes.size()); 170 for (auto h : other->good_shader_hashes) good_shader_hashes.insert(h); 171 } 172 173 static uint32_t MakeShaderHash(VkShaderModuleCreateInfo const *smci); 174 Contains(uint32_t hash)175 bool Contains(uint32_t hash) { return good_shader_hashes.count(hash) != 0; } 176 Insert(uint32_t hash)177 void Insert(uint32_t hash) { good_shader_hashes.insert(hash); } 178 179 private: Sha1ToVkUuid(const char * sha1_str,uint8_t uuid[VK_UUID_SIZE])180 void Sha1ToVkUuid(const char *sha1_str, uint8_t uuid[VK_UUID_SIZE]) { 181 // Convert sha1_str from a hex string to binary. We only need VK_UUID_BYTES of 182 // output, so pad with zeroes if the input string is shorter than that, and truncate 183 // if it's longer. 184 char padded_sha1_str[2 * VK_UUID_SIZE + 1] = {}; 185 strncpy(padded_sha1_str, sha1_str, 2 * VK_UUID_SIZE + 1); 186 char byte_str[3] = {}; 187 for (uint32_t i = 0; i < VK_UUID_SIZE; ++i) { 188 byte_str[0] = padded_sha1_str[2 * i + 0]; 189 byte_str[1] = padded_sha1_str[2 * i + 1]; 190 uuid[i] = static_cast<uint8_t>(strtol(byte_str, NULL, 16)); 191 } 192 } 193 }; 194 195 bool validate_and_capture_pipeline_shader_state(layer_data *dev_data, PIPELINE_STATE *pPipeline); 196 bool validate_compute_pipeline(layer_data *dev_data, PIPELINE_STATE *pPipeline); 197 typedef std::pair<unsigned, unsigned> descriptor_slot_t; 198 bool PreCallValidateCreateShaderModule(layer_data *dev_data, VkShaderModuleCreateInfo const *pCreateInfo, bool *spirv_valid); 199 200 #endif // VULKAN_SHADER_VALIDATION_H 201