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 47 ProgramIdentifier (const std::string& testCasePath_, const std::string& programName_) 48 : testCasePath (testCasePath_) 49 , programName (programName_) 50 { 51 } 52 }; 53 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: 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); 108 size_t size (void) const { return m_elements.size(); } 109 110 private: 111 enum 112 { 113 ELEMENTS_PER_PAGE_LOG2 = 10 114 }; 115 116 inline size_t getPageForElement (size_t elemNdx) const { return elemNdx >> ELEMENTS_PER_PAGE_LOG2; } 117 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> 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> 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> 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 207 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