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 uint32_t word; //!< 4 bytes of search string.
98 uint32_t 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
109 {
110 return m_elements.size();
111 }
112
113 private:
114 enum
115 {
116 ELEMENTS_PER_PAGE_LOG2 = 10
117 };
118
getPageForElement(size_t elemNdx) const119 inline size_t getPageForElement(size_t elemNdx) const
120 {
121 return elemNdx >> ELEMENTS_PER_PAGE_LOG2;
122 }
isPageResident(size_t pageNdx) const123 inline bool isPageResident(size_t pageNdx) const
124 {
125 return m_isPageResident[pageNdx];
126 }
127
128 void makePageResident(size_t pageNdx);
129
130 de::UniquePtr<tcu::Resource> m_resource;
131
132 std::vector<Element> m_elements;
133 std::vector<bool> m_isPageResident;
134 };
135
136 template <typename Element>
LazyResource(de::MovePtr<tcu::Resource> resource)137 LazyResource<Element>::LazyResource(de::MovePtr<tcu::Resource> resource) : m_resource(resource)
138 {
139 const size_t resSize = m_resource->getSize();
140 const size_t numElements = resSize / sizeof(Element);
141 const size_t numPages =
142 (numElements >> ELEMENTS_PER_PAGE_LOG2) + ((numElements & ((1u << ELEMENTS_PER_PAGE_LOG2) - 1u)) == 0 ? 0 : 1);
143
144 TCU_CHECK_INTERNAL(numElements * sizeof(Element) == resSize);
145
146 m_elements.resize(numElements);
147 m_isPageResident.resize(numPages, false);
148 }
149
150 template <typename Element>
operator [](size_t ndx)151 const Element &LazyResource<Element>::operator[](size_t ndx)
152 {
153 const size_t pageNdx = getPageForElement(ndx);
154
155 if (ndx >= m_elements.size())
156 throw std::out_of_range("");
157
158 if (!isPageResident(pageNdx))
159 makePageResident(pageNdx);
160
161 return m_elements[ndx];
162 }
163
164 template <typename Element>
makePageResident(size_t pageNdx)165 void LazyResource<Element>::makePageResident(size_t pageNdx)
166 {
167 const size_t pageSize = (size_t)(1 << ELEMENTS_PER_PAGE_LOG2) * sizeof(Element);
168 const size_t pageOffset = pageNdx * pageSize;
169 const size_t numBytesToRead = de::min(m_elements.size() * sizeof(Element) - pageOffset, pageSize);
170
171 DE_ASSERT(!isPageResident(pageNdx));
172
173 if ((size_t)m_resource->getPosition() != pageOffset)
174 m_resource->setPosition((int)pageOffset);
175
176 m_resource->read((uint8_t *)&m_elements[pageNdx << ELEMENTS_PER_PAGE_LOG2], (int)numBytesToRead);
177 m_isPageResident[pageNdx] = true;
178 }
179
180 typedef LazyResource<BinaryIndexNode> BinaryIndexAccess;
181
182 class BinaryRegistryReader
183 {
184 public:
185 BinaryRegistryReader(const tcu::Archive &archive, const std::string &srcPath);
186 ~BinaryRegistryReader(void);
187
188 ProgramBinary *loadProgram(const ProgramIdentifier &id) const;
189
190 private:
191 typedef de::MovePtr<BinaryIndexAccess> BinaryIndexPtr;
192
193 const tcu::Archive &m_archive;
194 const std::string m_srcPath;
195
196 mutable BinaryIndexPtr m_binaryIndex;
197 };
198
199 struct ProgramIdentifierIndex
200 {
201 ProgramIdentifier id;
202 uint32_t index;
203
ProgramIdentifierIndexvk::BinaryRegistryDetail::ProgramIdentifierIndex204 ProgramIdentifierIndex(const ProgramIdentifier &id_, uint32_t index_) : id(id_), index(index_)
205 {
206 }
207 };
208
209 DE_DECLARE_POOL_HASH(BinaryIndexHashImpl, const ProgramBinary *, uint32_t);
210
211 class BinaryIndexHash
212 {
213 public:
214 BinaryIndexHash(void);
215 ~BinaryIndexHash(void);
216
217 uint32_t *find(const ProgramBinary *binary) const;
218 void insert(const ProgramBinary *binary, uint32_t index);
219
220 private:
221 BinaryIndexHash(const BinaryIndexHash &);
222 BinaryIndexHash &operator=(const BinaryIndexHash &);
223
224 de::MemPool m_memPool;
225 BinaryIndexHashImpl *const m_hash;
226 };
227
228 class BinaryRegistryWriter
229 {
230 public:
231 BinaryRegistryWriter(const std::string &dstPath);
232 ~BinaryRegistryWriter(void);
233
234 void addProgram(const ProgramIdentifier &id, const ProgramBinary &binary);
235 void write(void) const;
236
237 private:
238 void initFromPath(const std::string &srcPath);
239 void writeToPath(const std::string &dstPath) const;
240
241 uint32_t *findBinary(const ProgramBinary &binary) const;
242 uint32_t getNextSlot(void) const;
243 void addBinary(uint32_t index, const ProgramBinary &binary);
244
245 struct BinarySlot
246 {
247 ProgramBinary *binary;
248 size_t referenceCount;
249
BinarySlotvk::BinaryRegistryDetail::BinaryRegistryWriter::BinarySlot250 BinarySlot(ProgramBinary *binary_, size_t referenceCount_) : binary(binary_), referenceCount(referenceCount_)
251 {
252 }
253
BinarySlotvk::BinaryRegistryDetail::BinaryRegistryWriter::BinarySlot254 BinarySlot(void) : binary(DE_NULL), referenceCount(0)
255 {
256 }
257 };
258
259 typedef std::vector<BinarySlot> BinaryVector;
260 typedef std::vector<ProgramIdentifierIndex> ProgIdIndexVector;
261
262 const std::string &m_dstPath;
263
264 ProgIdIndexVector m_binaryIndices; //!< ProgramIdentifier -> slot in m_binaries
265 BinaryIndexHash m_binaryHash; //!< ProgramBinary -> slot in m_binaries
266 BinaryVector m_binaries;
267 };
268
269 } // namespace BinaryRegistryDetail
270
271 using BinaryRegistryDetail::BinaryRegistryReader;
272 using BinaryRegistryDetail::BinaryRegistryWriter;
273 using BinaryRegistryDetail::ProgramIdentifier;
274 using BinaryRegistryDetail::ProgramNotFoundException;
275
276 } // namespace vk
277
278 #endif // _VKBINARYREGISTRY_HPP
279