• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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