1 #ifndef _PCREADER_HPP 2 #define _PCREADER_HPP 3 4 /* Copyright (c) 2021, NVIDIA CORPORATION 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: Daniel Koch <dkoch@nvidia.com> 19 */ 20 21 #include <cstddef> 22 23 #ifndef VKSC_ASSERT 24 #include <cassert> 25 #define VKSC_ASSERT assert 26 #endif // VKSC_ASSERT 27 #ifndef VKSC_MEMCMP 28 #include <cstring> 29 #define VKSC_MEMCMP memcmp 30 #endif // VKSC_MEMCPY 31 32 // Must be version 1.0.6 or newer 33 //#include <vulkan/vulkan_sc_core.hpp> 34 35 // Legacy Header for version 1.0.4 36 #define VK_PIPELINE_CACHE_HEADER_VERSION_SAFETY_CRITICAL_ONE_LEGACY (VkPipelineCacheHeaderVersion)1000298000 37 typedef struct VkPipelineCacheHeaderVersionSafetyCriticalOneLegacy { 38 VkPipelineCacheHeaderVersionOne headerVersionOne; 39 VkPipelineCacheValidationVersion validationVersion; 40 uint32_t pipelineIndexCount; 41 uint32_t pipelineIndexStride; 42 uint64_t pipelineIndexOffset; 43 } VkPipelineCacheHeaderVersionSafetyCriticalOneLegacy; 44 45 // VKSCPipelineCacheHeaderReader 46 // 47 // Utility class to handle extracting information about pipelines from a pipeline cache blob. 48 // 49 // Instantiate the class with a pointer to the pipeline cache blob and the size. 50 // The pipeline cache blob is NOT copied and the application must maintain the lifetime 51 // of the data that was passed in while this object is instantiated. 52 // The cache blob will never be modified by this class. 53 // 54 // getSafetyCriticalOneHeader - return the safety critical header field 55 // 56 // getPipelineIndexEntry(index) - return the pipeline index entry for a specified index in the header 57 // 58 // getPipelineIndexEntry(UUID) - return the pipeline index entry for a specified pipeline identifier 59 // 60 // getJson - get a pointer to the json for a specfied pipeline index entry 61 // 62 // getStageIndexEntry - return the stage index entry for a specified pipeline index entry and stage 63 // 64 // getSPIRV - get a pointer to the SPIRV code for a specified stage index entry 65 // 66 67 68 class VKSCPipelineCacheHeaderReader 69 { 70 public: 71 // initialize the pipeline cache header reader with <cacheSize> bytes of data starting at <cacheData> 72 // the pipeline cache is not copied, but the pointer is saved 73 // cacheData is never modified VKSCPipelineCacheHeaderReader(uint64_t cacheSize,const uint8_t * cacheData)74 VKSCPipelineCacheHeaderReader(uint64_t cacheSize, const uint8_t* cacheData) 75 : m_CacheSize{cacheSize}, m_CacheData{cacheData} 76 { 77 const VkPipelineCacheHeaderVersionSafetyCriticalOne* const sc1 = 78 reinterpret_cast<const VkPipelineCacheHeaderVersionSafetyCriticalOne*>(m_CacheData); 79 80 m_IsLegacy = (sc1->headerVersionOne.headerVersion == VK_PIPELINE_CACHE_HEADER_VERSION_SAFETY_CRITICAL_ONE_LEGACY); 81 } 82 83 // basic sanity check of the referenced pipeline cache data 84 // make sure m_CacheData starts with a well-formed VkPipelineCacheHeaderVersionSafetyCriticalOne structure isValid() const85 bool isValid() const 86 { 87 const VkPipelineCacheHeaderVersionSafetyCriticalOne* const sc1 = 88 reinterpret_cast<const VkPipelineCacheHeaderVersionSafetyCriticalOne*>(m_CacheData); 89 90 if (sc1->headerVersionOne.headerSize != sizeof(VkPipelineCacheHeaderVersionSafetyCriticalOne) || 91 !(sc1->headerVersionOne.headerVersion == VK_PIPELINE_CACHE_HEADER_VERSION_SAFETY_CRITICAL_ONE || 92 isLegacy()) || 93 sc1->validationVersion != VK_PIPELINE_CACHE_VALIDATION_VERSION_SAFETY_CRITICAL_ONE) 94 { 95 return false; 96 } 97 return true; 98 } 99 isLegacy() const100 bool isLegacy() const { return m_IsLegacy; } 101 102 // return pointer to the VkPipelineCacheHeaderVersionOne structure getHeaderVersionOne() const103 const VkPipelineCacheHeaderVersionOne* getHeaderVersionOne() const 104 { 105 const VkPipelineCacheHeaderVersionOne* const hv1 = 106 reinterpret_cast<const VkPipelineCacheHeaderVersionOne*>(m_CacheData); 107 108 return hv1; 109 } 110 111 // return the validation version from the SC1 header getValidationVersion() const112 VkPipelineCacheValidationVersion getValidationVersion() const 113 { 114 if (isLegacy()) 115 { 116 const VkPipelineCacheHeaderVersionSafetyCriticalOneLegacy* const sc1 = getSafetyCriticalOneHeaderLegacy(); 117 return sc1->validationVersion; 118 } 119 else 120 { 121 const VkPipelineCacheHeaderVersionSafetyCriticalOne* const sc1 = getSafetyCriticalOneHeader(); 122 return sc1->validationVersion; 123 } 124 125 } 126 127 // return the implementation data field from the SC1 header getImplementationData() const128 uint32_t getImplementationData() const 129 { 130 if (isLegacy()) 131 { 132 return 0U; 133 } 134 else 135 { 136 const VkPipelineCacheHeaderVersionSafetyCriticalOne* const sc1 = getSafetyCriticalOneHeader(); 137 return sc1->implementationData; 138 } 139 } 140 141 // return the number of pipelines in the index getPipelineIndexCount() const142 uint32_t getPipelineIndexCount() const 143 { 144 if (isLegacy()) 145 { 146 const VkPipelineCacheHeaderVersionSafetyCriticalOneLegacy* const sc1 = getSafetyCriticalOneHeaderLegacy(); 147 return sc1->pipelineIndexCount; 148 } 149 else 150 { 151 const VkPipelineCacheHeaderVersionSafetyCriticalOne* const sc1 = getSafetyCriticalOneHeader(); 152 return sc1->pipelineIndexCount; 153 } 154 } 155 156 // return the stride between pipeline index entries in the index getPipelineIndexStride() const157 uint32_t getPipelineIndexStride() const 158 { 159 if (isLegacy()) 160 { 161 const VkPipelineCacheHeaderVersionSafetyCriticalOneLegacy* const sc1 = getSafetyCriticalOneHeaderLegacy(); 162 return sc1->pipelineIndexStride; 163 } 164 else 165 { 166 const VkPipelineCacheHeaderVersionSafetyCriticalOne* const sc1 = getSafetyCriticalOneHeader(); 167 return sc1->pipelineIndexStride; 168 } 169 } 170 171 // returns the offset to the start of pipeline index entries in the cache getPipelineIndexOffset() const172 uint64_t getPipelineIndexOffset() const 173 { 174 if (isLegacy()) 175 { 176 const VkPipelineCacheHeaderVersionSafetyCriticalOneLegacy* const sc1 = getSafetyCriticalOneHeaderLegacy(); 177 return sc1->pipelineIndexOffset; 178 } 179 else 180 { 181 const VkPipelineCacheHeaderVersionSafetyCriticalOne* const sc1 = getSafetyCriticalOneHeader(); 182 return sc1->pipelineIndexOffset; 183 } 184 } 185 186 // return pointer to pipeline index entry by <index> in pipeline header 187 // typically used for iterating over all pipelines in the cache 188 // nullptr is returned if <index> is out of range getPipelineIndexEntry(uint32_t index) const189 const VkPipelineCacheSafetyCriticalIndexEntry* getPipelineIndexEntry(uint32_t index) const 190 { 191 if (index >= getPipelineIndexCount()) 192 { 193 return nullptr; 194 } 195 196 uint64_t offset = getPipelineIndexOffset() + (index * getPipelineIndexStride()); 197 VKSC_ASSERT(offset + sizeof(VkPipelineCacheSafetyCriticalIndexEntry) <= m_CacheSize); 198 199 const VkPipelineCacheSafetyCriticalIndexEntry* const pipelineIndexEntry = 200 reinterpret_cast<const VkPipelineCacheSafetyCriticalIndexEntry*>(m_CacheData + offset); 201 202 return pipelineIndexEntry; 203 } 204 205 // return pointer to pipeline index entry for requested pipeline identifier 206 // nullptr is returned if not found getPipelineIndexEntry(const uint8_t identifier[VK_UUID_SIZE]) const207 const VkPipelineCacheSafetyCriticalIndexEntry* getPipelineIndexEntry(const uint8_t identifier[VK_UUID_SIZE]) const 208 { 209 const uint32_t pipelineIndexCount = getPipelineIndexCount(); 210 const uint32_t pipelineIndexStride = getPipelineIndexStride(); 211 const uint64_t pipelineIndexOffset = getPipelineIndexOffset(); 212 213 for (uint32_t i = 0U; i < pipelineIndexCount; ++i) 214 { 215 uint64_t offset = pipelineIndexOffset + (i * pipelineIndexStride); 216 VKSC_ASSERT(offset + sizeof(VkPipelineCacheSafetyCriticalIndexEntry) <= m_CacheSize); 217 218 const VkPipelineCacheSafetyCriticalIndexEntry* const pipelineIndexEntry = 219 reinterpret_cast<const VkPipelineCacheSafetyCriticalIndexEntry*>(m_CacheData + offset); 220 221 if (VKSC_MEMCMP(identifier, pipelineIndexEntry->pipelineIdentifier, VK_UUID_SIZE) == 0U) 222 { 223 return pipelineIndexEntry; 224 } 225 } 226 227 return nullptr; 228 } 229 230 // return pointer to json for a given pipeline index entry 231 // nullptr is returned if not present getJson(const VkPipelineCacheSafetyCriticalIndexEntry * const pipelineIndexEntry) const232 const uint8_t* getJson(const VkPipelineCacheSafetyCriticalIndexEntry* const pipelineIndexEntry) const 233 { 234 uint64_t offset = pipelineIndexEntry->jsonOffset; 235 if (0U == offset) return nullptr; 236 237 VKSC_ASSERT(offset + pipelineIndexEntry->jsonSize <= m_CacheSize); 238 239 return (m_CacheData + offset); 240 } 241 242 // return pointer to stage validation index entry given a pipeline index entry <pipelineIndexEntry> and <stage> 243 // nullptr is returned if not present getStageIndexEntry(const VkPipelineCacheSafetyCriticalIndexEntry * const pipelineIndexEntry,uint32_t stage) const244 const VkPipelineCacheStageValidationIndexEntry* getStageIndexEntry(const VkPipelineCacheSafetyCriticalIndexEntry* const pipelineIndexEntry, uint32_t stage) const 245 { 246 if (stage >= pipelineIndexEntry->stageIndexCount) return nullptr; 247 248 uint64_t offset = pipelineIndexEntry->stageIndexOffset + (stage * pipelineIndexEntry->stageIndexStride); 249 VKSC_ASSERT(offset + sizeof(VkPipelineCacheStageValidationIndexEntry) <= m_CacheSize); 250 251 const VkPipelineCacheStageValidationIndexEntry* const stageIndexEntry = 252 reinterpret_cast<const VkPipelineCacheStageValidationIndexEntry*>(m_CacheData + offset); 253 254 return stageIndexEntry; 255 } 256 257 // return pointer to spirv code in the pipeline cache for a given stage index entry 258 // nullptr is returned if not present getSPIRV(const VkPipelineCacheStageValidationIndexEntry * const stageIndexEntry) const259 const uint8_t* getSPIRV(const VkPipelineCacheStageValidationIndexEntry* const stageIndexEntry) const 260 { 261 uint64_t offset = stageIndexEntry->codeOffset; 262 if (0U == offset) return nullptr; 263 264 VKSC_ASSERT(offset + stageIndexEntry->codeSize <= m_CacheSize); 265 266 return (m_CacheData + offset); 267 } 268 269 private: 270 // return pointer to the pipeline cache SafetyCriticalOne structure getSafetyCriticalOneHeader() const271 const VkPipelineCacheHeaderVersionSafetyCriticalOne* getSafetyCriticalOneHeader() const 272 { 273 const VkPipelineCacheHeaderVersionSafetyCriticalOne* const sc1 = 274 reinterpret_cast<const VkPipelineCacheHeaderVersionSafetyCriticalOne*>(m_CacheData); 275 276 return sc1; 277 } 278 279 // return pointer to the pipeline cache SafetyCriticalOneLegacy structure getSafetyCriticalOneHeaderLegacy() const280 const VkPipelineCacheHeaderVersionSafetyCriticalOneLegacy* getSafetyCriticalOneHeaderLegacy() const 281 { 282 const VkPipelineCacheHeaderVersionSafetyCriticalOneLegacy* const sc1 = 283 reinterpret_cast<const VkPipelineCacheHeaderVersionSafetyCriticalOneLegacy*>(m_CacheData); 284 285 return sc1; 286 } 287 288 const uint64_t m_CacheSize; // size of data pointed to by m_CacheData in bytes 289 const uint8_t* const m_CacheData; // pipeline cache data being read by this reader 290 bool m_IsLegacy; // is legacy (pre 1.0.5) pipeline cache format 291 292 }; 293 294 #endif // _PCREADER_HPP 295