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 DE_DECLARE_POOL_HASH(BinaryHash, const ProgramBinary*, deUint32);
174
175 class BinaryRegistryReader
176 {
177 public:
178 BinaryRegistryReader (const tcu::Archive& archive, const std::string& srcPath);
179 ~BinaryRegistryReader (void);
180
181 ProgramBinary* loadProgram (const ProgramIdentifier& id) const;
182
183 private:
184 typedef de::MovePtr<BinaryIndexAccess> BinaryIndexPtr;
185
186 const tcu::Archive& m_archive;
187 const std::string m_srcPath;
188
189 mutable BinaryIndexPtr m_binaryIndex;
190 };
191
192 class BinaryRegistryWriter
193 {
194 public:
195 BinaryRegistryWriter (const std::string& dstPath);
196 ~BinaryRegistryWriter (void);
197
198 void storeProgram (const ProgramIdentifier& id, const ProgramBinary& binary);
199 void writeIndex (void) const;
200
201 private:
202 struct BinaryIndex
203 {
204 ProgramIdentifier id;
205 deUint32 index;
206
BinaryIndexvk::BinaryRegistryDetail::BinaryRegistryWriter::BinaryIndex207 BinaryIndex (const ProgramIdentifier& id_,
208 deUint32 index_)
209 : id (id_)
210 , index (index_)
211 {}
212 };
213
214 typedef std::vector<ProgramBinary*> BinaryVector;
215 typedef std::vector<BinaryIndex> BinaryIndexVector;
216
217 const std::string& m_dstPath;
218
219 de::MemPool m_memPool;
220 BinaryHash* m_binaryIndexMap; //!< ProgramBinary -> slot in m_compactedBinaries
221 BinaryVector m_compactedBinaries;
222 BinaryIndexVector m_binaryIndices; //!< ProgramIdentifier -> slot in m_compactedBinaries
223 };
224
225 } // BinaryRegistryDetail
226
227 using BinaryRegistryDetail::BinaryRegistryReader;
228 using BinaryRegistryDetail::BinaryRegistryWriter;
229 using BinaryRegistryDetail::ProgramIdentifier;
230 using BinaryRegistryDetail::ProgramNotFoundException;
231
232 } // vk
233
234 #endif // _VKBINARYREGISTRY_HPP
235