1 #ifndef _VKBINARYREGISTRY_HPP
2 #define _VKBINARYREGISTRY_HPP
3 /*-------------------------------------------------------------------------
4 * Vulkan CTS Framework
5 * --------------------
6 *
7 * Copyright (c) 2015 Google Inc.
8 *
9 * Licensed under the Apache License, Version 2.0 (the "License");
10 * you may not use this file except in compliance with the License.
11 * You may obtain a copy of the License at
12 *
13 * http://www.apache.org/licenses/LICENSE-2.0
14 *
15 * Unless required by applicable law or agreed to in writing, software
16 * distributed under the License is distributed on an "AS IS" BASIS,
17 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 * See the License for the specific language governing permissions and
19 * limitations under the License.
20 *
21 *//*!
22 * \file
23 * \brief Program binary registry.
24 *//*--------------------------------------------------------------------*/
25
26 #include "vkDefs.hpp"
27 #include "vkPrograms.hpp"
28 #include "tcuResource.hpp"
29 #include "deMemPool.hpp"
30 #include "dePoolHash.h"
31 #include "deUniquePtr.hpp"
32
33 #include <map>
34 #include <vector>
35 #include <stdexcept>
36
37 namespace vk
38 {
39 namespace BinaryRegistryDetail
40 {
41
42 struct ProgramIdentifier
43 {
44 std::string testCasePath;
45 std::string programName;
46
ProgramIdentifiervk::BinaryRegistryDetail::ProgramIdentifier47 ProgramIdentifier (const std::string& testCasePath_, const std::string& programName_)
48 : testCasePath (testCasePath_)
49 , programName (programName_)
50 {
51 }
52 };
53
operator <(const ProgramIdentifier & a,const ProgramIdentifier & b)54 inline bool operator< (const ProgramIdentifier& a, const ProgramIdentifier& b)
55 {
56 return (a.testCasePath < b.testCasePath) || ((a.testCasePath == b.testCasePath) && (a.programName < b.programName));
57 }
58
59 class ProgramNotFoundException : public tcu::ResourceError
60 {
61 public:
ProgramNotFoundException(const ProgramIdentifier & id,const std::string & reason)62 ProgramNotFoundException (const ProgramIdentifier& id, const std::string& reason)
63 : tcu::ResourceError("Program " + id.testCasePath + " / '" + id.programName + "' not found: " + reason)
64 {
65 }
66 };
67
68 // Program Binary Index
69 // --------------------
70 //
71 // When SPIR-V binaries are stored on disk, duplicate binaries are eliminated
72 // to save a significant amount of space. Many tests use identical binaries and
73 // just storing each compiled binary without de-duplication would be incredibly
74 // wasteful.
75 //
76 // To locate binary that corresponds given ProgramIdentifier, a program binary
77 // index is needed. Since that index is accessed every time a test requests shader
78 // binary, it must be fast to load (to reduce statup cost), and fast to access.
79 //
80 // Simple trie is used to store binary indices. It is laid out as an array of
81 // BinaryIndexNodes. Nodes store 4-byte pieces (words) of search string, rather
82 // than just a single character. This gives more regular memory layout in exchange
83 // of a little wasted storage.
84 //
85 // Search strings are created by splitting original string into 4-byte words and
86 // appending one or more terminating 0 bytes.
87 //
88 // For each node where word doesn't have trailing 0 bytes (not terminated), the
89 // index points into a offset of its child list. Children for each node are stored
90 // consecutively, and the list is terminated by child with word = 0.
91 //
92 // If word contains one or more trailing 0 bytes, index denotes the binary index
93 // instead of index of the child list.
94
95 struct BinaryIndexNode
96 {
97 deUint32 word; //!< 4 bytes of search string.
98 deUint32 index; //!< Binary index if word ends with 0 bytes, or index of first child node otherwise.
99 };
100
101 template<typename Element>
102 class LazyResource
103 {
104 public:
105 LazyResource (de::MovePtr<tcu::Resource> resource);
106
107 const Element& operator[] (size_t ndx);
size(void) const108 size_t size (void) const { return m_elements.size(); }
109
110 private:
111 enum
112 {
113 ELEMENTS_PER_PAGE_LOG2 = 10
114 };
115
getPageForElement(size_t elemNdx) const116 inline size_t getPageForElement (size_t elemNdx) const { return elemNdx >> ELEMENTS_PER_PAGE_LOG2; }
isPageResident(size_t pageNdx) const117 inline bool isPageResident (size_t pageNdx) const { return m_isPageResident[pageNdx]; }
118
119 void makePageResident (size_t pageNdx);
120
121 de::UniquePtr<tcu::Resource> m_resource;
122
123 std::vector<Element> m_elements;
124 std::vector<bool> m_isPageResident;
125 };
126
127 template<typename Element>
LazyResource(de::MovePtr<tcu::Resource> resource)128 LazyResource<Element>::LazyResource (de::MovePtr<tcu::Resource> resource)
129 : m_resource(resource)
130 {
131 const size_t resSize = m_resource->getSize();
132 const size_t numElements = resSize/sizeof(Element);
133 const size_t numPages = (numElements >> ELEMENTS_PER_PAGE_LOG2) + ((numElements & ((1u<<ELEMENTS_PER_PAGE_LOG2)-1u)) == 0 ? 0 : 1);
134
135 TCU_CHECK_INTERNAL(numElements*sizeof(Element) == resSize);
136
137 m_elements.resize(numElements);
138 m_isPageResident.resize(numPages, false);
139 }
140
141 template<typename Element>
operator [](size_t ndx)142 const Element& LazyResource<Element>::operator[] (size_t ndx)
143 {
144 const size_t pageNdx = getPageForElement(ndx);
145
146 if (ndx >= m_elements.size())
147 throw std::out_of_range("");
148
149 if (!isPageResident(pageNdx))
150 makePageResident(pageNdx);
151
152 return m_elements[ndx];
153 }
154
155 template<typename Element>
makePageResident(size_t pageNdx)156 void LazyResource<Element>::makePageResident (size_t pageNdx)
157 {
158 const size_t pageSize = (size_t)(1<<ELEMENTS_PER_PAGE_LOG2)*sizeof(Element);
159 const size_t pageOffset = pageNdx*pageSize;
160 const size_t numBytesToRead = de::min(m_elements.size()*sizeof(Element) - pageOffset, pageSize);
161
162 DE_ASSERT(!isPageResident(pageNdx));
163
164 if ((size_t)m_resource->getPosition() != pageOffset)
165 m_resource->setPosition((int)pageOffset);
166
167 m_resource->read((deUint8*)&m_elements[pageNdx << ELEMENTS_PER_PAGE_LOG2], (int)numBytesToRead);
168 m_isPageResident[pageNdx] = true;
169 }
170
171 typedef LazyResource<BinaryIndexNode> BinaryIndexAccess;
172
173 class BinaryRegistryReader
174 {
175 public:
176 BinaryRegistryReader (const tcu::Archive& archive, const std::string& srcPath);
177 ~BinaryRegistryReader (void);
178
179 ProgramBinary* loadProgram (const ProgramIdentifier& id) const;
180
181 private:
182 typedef de::MovePtr<BinaryIndexAccess> BinaryIndexPtr;
183
184 const tcu::Archive& m_archive;
185 const std::string m_srcPath;
186
187 mutable BinaryIndexPtr m_binaryIndex;
188 };
189
190 struct ProgramIdentifierIndex
191 {
192 ProgramIdentifier id;
193 deUint32 index;
194
ProgramIdentifierIndexvk::BinaryRegistryDetail::ProgramIdentifierIndex195 ProgramIdentifierIndex (const ProgramIdentifier& id_,
196 deUint32 index_)
197 : id (id_)
198 , index (index_)
199 {}
200 };
201
202 DE_DECLARE_POOL_HASH(BinaryIndexHashImpl, const ProgramBinary*, deUint32);
203
204 class BinaryIndexHash
205 {
206 public:
207 BinaryIndexHash (void);
208 ~BinaryIndexHash (void);
209
210 deUint32* find (const ProgramBinary* binary) const;
211 void insert (const ProgramBinary* binary, deUint32 index);
212
213 private:
214 BinaryIndexHash (const BinaryIndexHash&);
215 BinaryIndexHash& operator= (const BinaryIndexHash&);
216
217 de::MemPool m_memPool;
218 BinaryIndexHashImpl* const m_hash;
219 };
220
221 class BinaryRegistryWriter
222 {
223 public:
224 BinaryRegistryWriter (const std::string& dstPath);
225 ~BinaryRegistryWriter (void);
226
227 void addProgram (const ProgramIdentifier& id, const ProgramBinary& binary);
228 void write (void) const;
229
230 private:
231 void initFromPath (const std::string& srcPath);
232 void writeToPath (const std::string& dstPath) const;
233
234 deUint32* findBinary (const ProgramBinary& binary) const;
235 deUint32 getNextSlot (void) const;
236 void addBinary (deUint32 index, const ProgramBinary& binary);
237
238 struct BinarySlot
239 {
240 ProgramBinary* binary;
241 size_t referenceCount;
242
BinarySlotvk::BinaryRegistryDetail::BinaryRegistryWriter::BinarySlot243 BinarySlot (ProgramBinary* binary_, size_t referenceCount_)
244 : binary (binary_)
245 , referenceCount(referenceCount_)
246 {}
247
BinarySlotvk::BinaryRegistryDetail::BinaryRegistryWriter::BinarySlot248 BinarySlot (void)
249 : binary (DE_NULL)
250 , referenceCount(0)
251 {}
252 };
253
254 typedef std::vector<BinarySlot> BinaryVector;
255 typedef std::vector<ProgramIdentifierIndex> ProgIdIndexVector;
256
257 const std::string& m_dstPath;
258
259 ProgIdIndexVector m_binaryIndices; //!< ProgramIdentifier -> slot in m_binaries
260 BinaryIndexHash m_binaryHash; //!< ProgramBinary -> slot in m_binaries
261 BinaryVector m_binaries;
262 };
263
264 } // BinaryRegistryDetail
265
266 using BinaryRegistryDetail::BinaryRegistryReader;
267 using BinaryRegistryDetail::BinaryRegistryWriter;
268 using BinaryRegistryDetail::ProgramIdentifier;
269 using BinaryRegistryDetail::ProgramNotFoundException;
270
271 } // vk
272
273 #endif // _VKBINARYREGISTRY_HPP
274