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 <[email protected]> 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 { 39 VkPipelineCacheHeaderVersionOne headerVersionOne; 40 VkPipelineCacheValidationVersion validationVersion; 41 uint32_t pipelineIndexCount; 42 uint32_t pipelineIndexStride; 43 uint64_t pipelineIndexOffset; 44 } VkPipelineCacheHeaderVersionSafetyCriticalOneLegacy; 45 46 // VKSCPipelineCacheHeaderReader 47 // 48 // Utility class to handle extracting information about pipelines from a pipeline cache blob. 49 // 50 // Instantiate the class with a pointer to the pipeline cache blob and the size. 51 // The pipeline cache blob is NOT copied and the application must maintain the lifetime 52 // of the data that was passed in while this object is instantiated. 53 // The cache blob will never be modified by this class. 54 // 55 // getSafetyCriticalOneHeader - return the safety critical header field 56 // 57 // getPipelineIndexEntry(index) - return the pipeline index entry for a specified index in the header 58 // 59 // getPipelineIndexEntry(UUID) - return the pipeline index entry for a specified pipeline identifier 60 // 61 // getJson - get a pointer to the json for a specfied pipeline index entry 62 // 63 // getStageIndexEntry - return the stage index entry for a specified pipeline index entry and stage 64 // 65 // getSPIRV - get a pointer to the SPIRV code for a specified stage index entry 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} 76 , m_CacheData{cacheData} 77 { 78 const VkPipelineCacheHeaderVersionSafetyCriticalOne *const sc1 = 79 reinterpret_cast<const VkPipelineCacheHeaderVersionSafetyCriticalOne *>(m_CacheData); 80 81 m_IsLegacy = 82 (sc1->headerVersionOne.headerVersion == VK_PIPELINE_CACHE_HEADER_VERSION_SAFETY_CRITICAL_ONE_LEGACY); 83 } 84 85 // basic quick check of the referenced pipeline cache data 86 // make sure m_CacheData starts with a well-formed VkPipelineCacheHeaderVersionSafetyCriticalOne structure isValid() const87 bool isValid() const 88 { 89 const VkPipelineCacheHeaderVersionSafetyCriticalOne *const sc1 = 90 reinterpret_cast<const VkPipelineCacheHeaderVersionSafetyCriticalOne *>(m_CacheData); 91 92 if (sc1->headerVersionOne.headerSize != sizeof(VkPipelineCacheHeaderVersionSafetyCriticalOne) || 93 !(sc1->headerVersionOne.headerVersion == VK_PIPELINE_CACHE_HEADER_VERSION_SAFETY_CRITICAL_ONE || 94 isLegacy()) || 95 sc1->validationVersion != VK_PIPELINE_CACHE_VALIDATION_VERSION_SAFETY_CRITICAL_ONE) 96 { 97 return false; 98 } 99 return true; 100 } 101 isLegacy() const102 bool isLegacy() const 103 { 104 return m_IsLegacy; 105 } 106 107 // return pointer to the VkPipelineCacheHeaderVersionOne structure getHeaderVersionOne() const108 const VkPipelineCacheHeaderVersionOne *getHeaderVersionOne() const 109 { 110 const VkPipelineCacheHeaderVersionOne *const hv1 = 111 reinterpret_cast<const VkPipelineCacheHeaderVersionOne *>(m_CacheData); 112 113 return hv1; 114 } 115 116 // return the validation version from the SC1 header getValidationVersion() const117 VkPipelineCacheValidationVersion getValidationVersion() const 118 { 119 if (isLegacy()) 120 { 121 const VkPipelineCacheHeaderVersionSafetyCriticalOneLegacy *const sc1 = getSafetyCriticalOneHeaderLegacy(); 122 return sc1->validationVersion; 123 } 124 else 125 { 126 const VkPipelineCacheHeaderVersionSafetyCriticalOne *const sc1 = getSafetyCriticalOneHeader(); 127 return sc1->validationVersion; 128 } 129 } 130 131 // return the implementation data field from the SC1 header getImplementationData() const132 uint32_t getImplementationData() const 133 { 134 if (isLegacy()) 135 { 136 return 0U; 137 } 138 else 139 { 140 const VkPipelineCacheHeaderVersionSafetyCriticalOne *const sc1 = getSafetyCriticalOneHeader(); 141 return sc1->implementationData; 142 } 143 } 144 145 // return the number of pipelines in the index getPipelineIndexCount() const146 uint32_t getPipelineIndexCount() const 147 { 148 if (isLegacy()) 149 { 150 const VkPipelineCacheHeaderVersionSafetyCriticalOneLegacy *const sc1 = getSafetyCriticalOneHeaderLegacy(); 151 return sc1->pipelineIndexCount; 152 } 153 else 154 { 155 const VkPipelineCacheHeaderVersionSafetyCriticalOne *const sc1 = getSafetyCriticalOneHeader(); 156 return sc1->pipelineIndexCount; 157 } 158 } 159 160 // return the stride between pipeline index entries in the index getPipelineIndexStride() const161 uint32_t getPipelineIndexStride() const 162 { 163 if (isLegacy()) 164 { 165 const VkPipelineCacheHeaderVersionSafetyCriticalOneLegacy *const sc1 = getSafetyCriticalOneHeaderLegacy(); 166 return sc1->pipelineIndexStride; 167 } 168 else 169 { 170 const VkPipelineCacheHeaderVersionSafetyCriticalOne *const sc1 = getSafetyCriticalOneHeader(); 171 return sc1->pipelineIndexStride; 172 } 173 } 174 175 // returns the offset to the start of pipeline index entries in the cache getPipelineIndexOffset() const176 uint64_t getPipelineIndexOffset() const 177 { 178 if (isLegacy()) 179 { 180 const VkPipelineCacheHeaderVersionSafetyCriticalOneLegacy *const sc1 = getSafetyCriticalOneHeaderLegacy(); 181 return sc1->pipelineIndexOffset; 182 } 183 else 184 { 185 const VkPipelineCacheHeaderVersionSafetyCriticalOne *const sc1 = getSafetyCriticalOneHeader(); 186 return sc1->pipelineIndexOffset; 187 } 188 } 189 190 // return pointer to pipeline index entry by <index> in pipeline header 191 // typically used for iterating over all pipelines in the cache 192 // nullptr is returned if <index> is out of range getPipelineIndexEntry(uint32_t index) const193 const VkPipelineCacheSafetyCriticalIndexEntry *getPipelineIndexEntry(uint32_t index) const 194 { 195 if (index >= getPipelineIndexCount()) 196 { 197 return nullptr; 198 } 199 200 uint64_t offset = getPipelineIndexOffset() + (index * getPipelineIndexStride()); 201 VKSC_ASSERT(offset + sizeof(VkPipelineCacheSafetyCriticalIndexEntry) <= m_CacheSize); 202 203 const VkPipelineCacheSafetyCriticalIndexEntry *const pipelineIndexEntry = 204 reinterpret_cast<const VkPipelineCacheSafetyCriticalIndexEntry *>(m_CacheData + offset); 205 206 return pipelineIndexEntry; 207 } 208 209 // return pointer to pipeline index entry for requested pipeline identifier 210 // nullptr is returned if not found getPipelineIndexEntry(const uint8_t identifier[VK_UUID_SIZE]) const211 const VkPipelineCacheSafetyCriticalIndexEntry *getPipelineIndexEntry(const uint8_t identifier[VK_UUID_SIZE]) const 212 { 213 const uint32_t pipelineIndexCount = getPipelineIndexCount(); 214 const uint32_t pipelineIndexStride = getPipelineIndexStride(); 215 const uint64_t pipelineIndexOffset = getPipelineIndexOffset(); 216 217 for (uint32_t i = 0U; i < pipelineIndexCount; ++i) 218 { 219 uint64_t offset = pipelineIndexOffset + (i * pipelineIndexStride); 220 VKSC_ASSERT(offset + sizeof(VkPipelineCacheSafetyCriticalIndexEntry) <= m_CacheSize); 221 222 const VkPipelineCacheSafetyCriticalIndexEntry *const pipelineIndexEntry = 223 reinterpret_cast<const VkPipelineCacheSafetyCriticalIndexEntry *>(m_CacheData + offset); 224 225 if (VKSC_MEMCMP(identifier, pipelineIndexEntry->pipelineIdentifier, VK_UUID_SIZE) == 0U) 226 { 227 return pipelineIndexEntry; 228 } 229 } 230 231 return nullptr; 232 } 233 234 // return pointer to json for a given pipeline index entry 235 // nullptr is returned if not present getJson(const VkPipelineCacheSafetyCriticalIndexEntry * const pipelineIndexEntry) const236 const uint8_t *getJson(const VkPipelineCacheSafetyCriticalIndexEntry *const pipelineIndexEntry) const 237 { 238 uint64_t offset = pipelineIndexEntry->jsonOffset; 239 if (0U == offset) 240 return nullptr; 241 242 VKSC_ASSERT(offset + pipelineIndexEntry->jsonSize <= m_CacheSize); 243 244 return (m_CacheData + offset); 245 } 246 247 // return pointer to stage validation index entry given a pipeline index entry <pipelineIndexEntry> and <stage> 248 // nullptr is returned if not present getStageIndexEntry(const VkPipelineCacheSafetyCriticalIndexEntry * const pipelineIndexEntry,uint32_t stage) const249 const VkPipelineCacheStageValidationIndexEntry *getStageIndexEntry( 250 const VkPipelineCacheSafetyCriticalIndexEntry *const pipelineIndexEntry, uint32_t stage) const 251 { 252 if (stage >= pipelineIndexEntry->stageIndexCount) 253 return nullptr; 254 255 uint64_t offset = pipelineIndexEntry->stageIndexOffset + (stage * pipelineIndexEntry->stageIndexStride); 256 VKSC_ASSERT(offset + sizeof(VkPipelineCacheStageValidationIndexEntry) <= m_CacheSize); 257 258 const VkPipelineCacheStageValidationIndexEntry *const stageIndexEntry = 259 reinterpret_cast<const VkPipelineCacheStageValidationIndexEntry *>(m_CacheData + offset); 260 261 return stageIndexEntry; 262 } 263 264 // return pointer to spirv code in the pipeline cache for a given stage index entry 265 // nullptr is returned if not present getSPIRV(const VkPipelineCacheStageValidationIndexEntry * const stageIndexEntry) const266 const uint8_t *getSPIRV(const VkPipelineCacheStageValidationIndexEntry *const stageIndexEntry) const 267 { 268 uint64_t offset = stageIndexEntry->codeOffset; 269 if (0U == offset) 270 return nullptr; 271 272 VKSC_ASSERT(offset + stageIndexEntry->codeSize <= m_CacheSize); 273 274 return (m_CacheData + offset); 275 } 276 277 private: 278 // return pointer to the pipeline cache SafetyCriticalOne structure getSafetyCriticalOneHeader() const279 const VkPipelineCacheHeaderVersionSafetyCriticalOne *getSafetyCriticalOneHeader() const 280 { 281 const VkPipelineCacheHeaderVersionSafetyCriticalOne *const sc1 = 282 reinterpret_cast<const VkPipelineCacheHeaderVersionSafetyCriticalOne *>(m_CacheData); 283 284 return sc1; 285 } 286 287 // return pointer to the pipeline cache SafetyCriticalOneLegacy structure getSafetyCriticalOneHeaderLegacy() const288 const VkPipelineCacheHeaderVersionSafetyCriticalOneLegacy *getSafetyCriticalOneHeaderLegacy() const 289 { 290 const VkPipelineCacheHeaderVersionSafetyCriticalOneLegacy *const sc1 = 291 reinterpret_cast<const VkPipelineCacheHeaderVersionSafetyCriticalOneLegacy *>(m_CacheData); 292 293 return sc1; 294 } 295 const uint64_t m_CacheSize; // size of data pointed to by m_CacheData in bytes 296 const uint8_t *const m_CacheData; // pipeline cache data being read by this reader 297 bool m_IsLegacy; // is legacy (pre 1.0.5) pipeline cache format 298 }; 299 300 #endif // _PCREADER_HPP 301