1 /* Copyright (c) 2015-2019 The Khronos Group Inc. 2 * Copyright (c) 2015-2019 Valve Corporation 3 * Copyright (c) 2015-2019 LunarG, Inc. 4 * Copyright (C) 2015-2019 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 #include "spirv-tools/optimizer.hpp" 25 26 // A forward iterator over spirv instructions. Provides easy access to len, opcode, and content words 27 // without the caller needing to care too much about the physical SPIRV module layout. 28 struct spirv_inst_iter { 29 std::vector<uint32_t>::const_iterator zero; 30 std::vector<uint32_t>::const_iterator it; 31 lenspirv_inst_iter32 uint32_t len() { 33 auto result = *it >> 16; 34 assert(result > 0); 35 return result; 36 } 37 opcodespirv_inst_iter38 uint32_t opcode() { return *it & 0x0ffffu; } 39 wordspirv_inst_iter40 uint32_t const &word(unsigned n) { 41 assert(n < len()); 42 return it[n]; 43 } 44 offsetspirv_inst_iter45 uint32_t offset() { return (uint32_t)(it - zero); } 46 spirv_inst_iterspirv_inst_iter47 spirv_inst_iter() {} 48 spirv_inst_iterspirv_inst_iter49 spirv_inst_iter(std::vector<uint32_t>::const_iterator zero, std::vector<uint32_t>::const_iterator it) : zero(zero), it(it) {} 50 51 bool operator==(spirv_inst_iter const &other) { return it == other.it; } 52 53 bool operator!=(spirv_inst_iter const &other) { return it != other.it; } 54 55 spirv_inst_iter operator++(int) { // x++ 56 spirv_inst_iter ii = *this; 57 it += len(); 58 return ii; 59 } 60 61 spirv_inst_iter operator++() { // ++x; 62 it += len(); 63 return *this; 64 } 65 66 // The iterator and the value are the same thing. 67 spirv_inst_iter &operator*() { return *this; } 68 spirv_inst_iter const &operator*() const { return *this; } 69 }; 70 71 struct shader_module { 72 // The spirv image itself 73 std::vector<uint32_t> words; 74 // A mapping of <id> to the first word of its def. this is useful because walking type 75 // trees, constant expressions, etc requires jumping all over the instruction stream. 76 std::unordered_map<unsigned, unsigned> def_index; 77 bool has_valid_spirv; 78 VkShaderModule vk_shader_module; 79 uint32_t gpu_validation_shader_id; 80 PreprocessShaderBinaryshader_module81 std::vector<uint32_t> PreprocessShaderBinary(uint32_t *src_binary, size_t binary_size, spv_target_env env) { 82 spvtools::Optimizer optimizer(env); 83 optimizer.RegisterPass(spvtools::CreateFlattenDecorationPass()); 84 std::vector<uint32_t> optimized_binary; 85 auto result = optimizer.Run(src_binary, binary_size / sizeof(uint32_t), &optimized_binary); 86 return (result ? optimized_binary : std::vector<uint32_t>(src_binary, src_binary + binary_size / sizeof(uint32_t))); 87 } 88 shader_moduleshader_module89 shader_module(VkShaderModuleCreateInfo const *pCreateInfo, VkShaderModule shaderModule, spv_target_env env, 90 uint32_t unique_shader_id) 91 : words(PreprocessShaderBinary((uint32_t *)pCreateInfo->pCode, pCreateInfo->codeSize, env)), 92 def_index(), 93 has_valid_spirv(true), 94 vk_shader_module(shaderModule), 95 gpu_validation_shader_id(unique_shader_id) { 96 BuildDefIndex(); 97 } 98 shader_moduleshader_module99 shader_module() : has_valid_spirv(false), vk_shader_module(VK_NULL_HANDLE) {} 100 101 // Expose begin() / end() to enable range-based for beginshader_module102 spirv_inst_iter begin() const { return spirv_inst_iter(words.begin(), words.begin() + 5); } // First insn endshader_module103 spirv_inst_iter end() const { return spirv_inst_iter(words.begin(), words.end()); } // Just past last insn 104 // Given an offset into the module, produce an iterator there. atshader_module105 spirv_inst_iter at(unsigned offset) const { return spirv_inst_iter(words.begin(), words.begin() + offset); } 106 107 // Gets an iterator to the definition of an id get_defshader_module108 spirv_inst_iter get_def(unsigned id) const { 109 auto it = def_index.find(id); 110 if (it == def_index.end()) { 111 return end(); 112 } 113 return at(it->second); 114 } 115 116 void BuildDefIndex(); 117 }; 118 119 class ValidationCache { 120 // hashes of shaders that have passed validation before, and can be skipped. 121 // we don't store negative results, as we would have to also store what was 122 // wrong with them; also, we expect they will get fixed, so we're less 123 // likely to see them again. 124 std::unordered_set<uint32_t> good_shader_hashes; ValidationCache()125 ValidationCache() {} 126 127 public: Create(VkValidationCacheCreateInfoEXT const * pCreateInfo)128 static VkValidationCacheEXT Create(VkValidationCacheCreateInfoEXT const *pCreateInfo) { 129 auto cache = new ValidationCache(); 130 cache->Load(pCreateInfo); 131 return VkValidationCacheEXT(cache); 132 } 133 Load(VkValidationCacheCreateInfoEXT const * pCreateInfo)134 void Load(VkValidationCacheCreateInfoEXT const *pCreateInfo) { 135 const auto headerSize = 2 * sizeof(uint32_t) + VK_UUID_SIZE; 136 auto size = headerSize; 137 if (!pCreateInfo->pInitialData || pCreateInfo->initialDataSize < size) return; 138 139 uint32_t const *data = (uint32_t const *)pCreateInfo->pInitialData; 140 if (data[0] != size) return; 141 if (data[1] != VK_VALIDATION_CACHE_HEADER_VERSION_ONE_EXT) return; 142 uint8_t expected_uuid[VK_UUID_SIZE]; 143 Sha1ToVkUuid(SPIRV_TOOLS_COMMIT_ID, expected_uuid); 144 if (memcmp(&data[2], expected_uuid, VK_UUID_SIZE) != 0) return; // different version 145 146 data = (uint32_t const *)(reinterpret_cast<uint8_t const *>(data) + headerSize); 147 148 for (; size < pCreateInfo->initialDataSize; data++, size += sizeof(uint32_t)) { 149 good_shader_hashes.insert(*data); 150 } 151 } 152 Write(size_t * pDataSize,void * pData)153 void Write(size_t *pDataSize, void *pData) { 154 const auto headerSize = 2 * sizeof(uint32_t) + VK_UUID_SIZE; // 4 bytes for header size + 4 bytes for version number + UUID 155 if (!pData) { 156 *pDataSize = headerSize + good_shader_hashes.size() * sizeof(uint32_t); 157 return; 158 } 159 160 if (*pDataSize < headerSize) { 161 *pDataSize = 0; 162 return; // Too small for even the header! 163 } 164 165 uint32_t *out = (uint32_t *)pData; 166 size_t actualSize = headerSize; 167 168 // Write the header 169 *out++ = headerSize; 170 *out++ = VK_VALIDATION_CACHE_HEADER_VERSION_ONE_EXT; 171 Sha1ToVkUuid(SPIRV_TOOLS_COMMIT_ID, reinterpret_cast<uint8_t *>(out)); 172 out = (uint32_t *)(reinterpret_cast<uint8_t *>(out) + VK_UUID_SIZE); 173 174 for (auto it = good_shader_hashes.begin(); it != good_shader_hashes.end() && actualSize < *pDataSize; 175 it++, out++, actualSize += sizeof(uint32_t)) { 176 *out = *it; 177 } 178 179 *pDataSize = actualSize; 180 } 181 Merge(ValidationCache const * other)182 void Merge(ValidationCache const *other) { 183 good_shader_hashes.reserve(good_shader_hashes.size() + other->good_shader_hashes.size()); 184 for (auto h : other->good_shader_hashes) good_shader_hashes.insert(h); 185 } 186 187 static uint32_t MakeShaderHash(VkShaderModuleCreateInfo const *smci); 188 Contains(uint32_t hash)189 bool Contains(uint32_t hash) { return good_shader_hashes.count(hash) != 0; } 190 Insert(uint32_t hash)191 void Insert(uint32_t hash) { good_shader_hashes.insert(hash); } 192 193 private: Sha1ToVkUuid(const char * sha1_str,uint8_t uuid[VK_UUID_SIZE])194 void Sha1ToVkUuid(const char *sha1_str, uint8_t uuid[VK_UUID_SIZE]) { 195 // Convert sha1_str from a hex string to binary. We only need VK_UUID_BYTES of 196 // output, so pad with zeroes if the input string is shorter than that, and truncate 197 // if it's longer. 198 char padded_sha1_str[2 * VK_UUID_SIZE + 1] = {}; 199 strncpy(padded_sha1_str, sha1_str, 2 * VK_UUID_SIZE + 1); 200 char byte_str[3] = {}; 201 for (uint32_t i = 0; i < VK_UUID_SIZE; ++i) { 202 byte_str[0] = padded_sha1_str[2 * i + 0]; 203 byte_str[1] = padded_sha1_str[2 * i + 1]; 204 uuid[i] = static_cast<uint8_t>(strtol(byte_str, NULL, 16)); 205 } 206 } 207 }; 208 209 typedef std::pair<unsigned, unsigned> descriptor_slot_t; 210 211 #endif // VULKAN_SHADER_VALIDATION_H 212