Merge vk-gl-cts/vulkan-cts-1.0.1 into vk-gl-cts/vulkan-cts-1.0.2
[platform/upstream/VK-GL-CTS.git] / external / vulkancts / framework / vulkan / vkBinaryRegistry.cpp
1 /*-------------------------------------------------------------------------
2  * Vulkan CTS Framework
3  * --------------------
4  *
5  * Copyright (c) 2015 Google Inc.
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  *
19  *//*!
20  * \file
21  * \brief Program binary registry.
22  *//*--------------------------------------------------------------------*/
23
24 #include "vkBinaryRegistry.hpp"
25 #include "tcuResource.hpp"
26 #include "tcuFormatUtil.hpp"
27 #include "deFilePath.hpp"
28 #include "deStringUtil.hpp"
29 #include "deDirectoryIterator.hpp"
30 #include "deString.h"
31 #include "deInt32.h"
32 #include "deFile.h"
33
34 #include <sstream>
35 #include <fstream>
36 #include <stdexcept>
37 #include <limits>
38
39 namespace vk
40 {
41 namespace BinaryRegistryDetail
42 {
43
44 using std::string;
45 using std::vector;
46
47 namespace
48 {
49
50 string getProgramFileName (deUint32 index)
51 {
52         return de::toString(tcu::toHex(index)) + ".spv";
53 }
54
55 string getProgramPath (const std::string& dirName, deUint32 index)
56 {
57         return de::FilePath::join(dirName, getProgramFileName(index)).getPath();
58 }
59
60 bool isHexChr (char c)
61 {
62         return de::inRange(c, '0', '9') || de::inRange(c, 'a', 'f') || de::inRange(c, 'A', 'F');
63 }
64
65 bool isProgramFileName (const std::string& name)
66 {
67         // 0x + 00000000 + .spv
68         if (name.length() != (2 + 8 + 4))
69                 return false;
70
71         if (name[0] != '0' ||
72                 name[1] != 'x' ||
73                 name[10] != '.' ||
74                 name[11] != 's' ||
75                 name[12] != 'p' ||
76                 name[13] != 'v')
77                 return false;
78
79         for (size_t ndx = 2; ndx < 10; ++ndx)
80         {
81                 if (!isHexChr(name[ndx]))
82                         return false;
83         }
84
85         return true;
86 }
87
88 deUint32 getProgramIndexFromName (const std::string& name)
89 {
90         DE_ASSERT(isProgramFileName(name));
91
92         deUint32                        index   = ~0u;
93         std::stringstream       str;
94
95         str << std::hex << name.substr(2,10);
96         str >> index;
97
98         DE_ASSERT(getProgramFileName(index) == name);
99
100         return index;
101 }
102
103 string getIndexPath (const std::string& dirName)
104 {
105         return de::FilePath::join(dirName, "index.bin").getPath();
106 }
107
108 void writeBinary (const ProgramBinary& binary, const std::string& dstPath)
109 {
110         const de::FilePath      filePath(dstPath);
111
112         if (!de::FilePath(filePath.getDirName()).exists())
113                 de::createDirectoryAndParents(filePath.getDirName().c_str());
114
115         {
116                 std::ofstream   out             (dstPath.c_str(), std::ios_base::binary);
117
118                 if (!out.is_open() || !out.good())
119                         throw tcu::Exception("Failed to open " + dstPath);
120
121                 out.write((const char*)binary.getBinary(), binary.getSize());
122                 out.close();
123         }
124 }
125
126 void writeBinary (const std::string& dstDir, deUint32 index, const ProgramBinary& binary)
127 {
128         writeBinary(binary, getProgramPath(dstDir, index));
129 }
130
131 ProgramBinary* readBinary (const std::string& srcPath)
132 {
133         std::ifstream   in              (srcPath.c_str(), std::ios::binary | std::ios::ate);
134         const size_t    size    = (size_t)in.tellg();
135
136         if (!in.is_open() || !in.good())
137                 throw tcu::Exception("Failed to open " + srcPath);
138
139         if (size == 0)
140                 throw tcu::Exception("Malformed binary, size = 0");
141
142         in.seekg(0, std::ios::beg);
143
144         {
145                 std::vector<deUint8>    bytes   (size);
146
147                 in.read((char*)&bytes[0], size);
148                 DE_ASSERT(bytes[0] != 0);
149
150                 return new ProgramBinary(vk::PROGRAM_FORMAT_SPIRV, bytes.size(), &bytes[0]);
151         }
152 }
153
154 deUint32 binaryHash (const ProgramBinary* binary)
155 {
156         return deMemoryHash(binary->getBinary(), binary->getSize());
157 }
158
159 deBool binaryEqual (const ProgramBinary* a, const ProgramBinary* b)
160 {
161         if (a->getSize() == b->getSize())
162                 return deMemoryEqual(a->getBinary(), b->getBinary(), a->getSize());
163         else
164                 return DE_FALSE;
165 }
166
167 std::vector<deUint32> getSearchPath (const ProgramIdentifier& id)
168 {
169         const std::string       combinedStr             = id.testCasePath + '#' + id.programName;
170         const size_t            strLen                  = combinedStr.size();
171         const size_t            numWords                = strLen/4 + 1;         // Must always end up with at least one 0 byte
172         vector<deUint32>        words                   (numWords, 0u);
173
174         deMemcpy(&words[0], combinedStr.c_str(), strLen);
175
176         return words;
177 }
178
179 const deUint32* findBinaryIndex (BinaryIndexAccess* index, const ProgramIdentifier& id)
180 {
181         const vector<deUint32>  words   = getSearchPath(id);
182         size_t                                  nodeNdx = 0;
183         size_t                                  wordNdx = 0;
184
185         for (;;)
186         {
187                 const BinaryIndexNode&  curNode = (*index)[nodeNdx];
188
189                 if (curNode.word == words[wordNdx])
190                 {
191                         if (wordNdx+1 < words.size())
192                         {
193                                 TCU_CHECK_INTERNAL((size_t)curNode.index < index->size());
194
195                                 nodeNdx  = curNode.index;
196                                 wordNdx += 1;
197                         }
198                         else if (wordNdx+1 == words.size())
199                                 return &curNode.index;
200                         else
201                                 return DE_NULL;
202                 }
203                 else if (curNode.word != 0)
204                 {
205                         nodeNdx += 1;
206
207                         // Index should always be null-terminated
208                         TCU_CHECK_INTERNAL(nodeNdx < index->size());
209                 }
210                 else
211                         return DE_NULL;
212         }
213
214         return DE_NULL;
215 }
216
217 //! Sparse index node used for final binary index construction
218 struct SparseIndexNode
219 {
220         deUint32                                                word;
221         deUint32                                                index;
222         std::vector<SparseIndexNode*>   children;
223
224         SparseIndexNode (deUint32 word_, deUint32 index_)
225                 : word  (word_)
226                 , index (index_)
227         {}
228
229         SparseIndexNode (void)
230                 : word  (0)
231                 , index (0)
232         {}
233
234         ~SparseIndexNode (void)
235         {
236                 for (size_t ndx = 0; ndx < children.size(); ndx++)
237                         delete children[ndx];
238         }
239 };
240
241 #if defined(DE_DEBUG)
242 bool isNullByteTerminated (deUint32 word)
243 {
244         deUint8 bytes[4];
245         deMemcpy(bytes, &word, sizeof(word));
246         return bytes[3] == 0;
247 }
248 #endif
249
250 void addToSparseIndex (SparseIndexNode* group, const deUint32* words, size_t numWords, deUint32 index)
251 {
252         const deUint32          curWord = words[0];
253         SparseIndexNode*        child   = DE_NULL;
254
255         for (size_t childNdx = 0; childNdx < group->children.size(); childNdx++)
256         {
257                 if (group->children[childNdx]->word == curWord)
258                 {
259                         child = group->children[childNdx];
260                         break;
261                 }
262         }
263
264         DE_ASSERT(numWords > 1 || !child);
265
266         if (!child)
267         {
268                 group->children.reserve(group->children.size()+1);
269                 group->children.push_back(new SparseIndexNode(curWord, numWords == 1 ? index : 0));
270
271                 child = group->children.back();
272         }
273
274         if (numWords > 1)
275                 addToSparseIndex(child, words+1, numWords-1, index);
276         else
277                 DE_ASSERT(isNullByteTerminated(curWord));
278 }
279
280 // Prepares sparse index for finalization. Ensures that child with word = 0 is moved
281 // to the end, or one is added if there is no such child already.
282 void normalizeSparseIndex (SparseIndexNode* group)
283 {
284         int             zeroChildPos    = -1;
285
286         for (size_t childNdx = 0; childNdx < group->children.size(); childNdx++)
287         {
288                 normalizeSparseIndex(group->children[childNdx]);
289
290                 if (group->children[childNdx]->word == 0)
291                 {
292                         DE_ASSERT(zeroChildPos < 0);
293                         zeroChildPos = (int)childNdx;
294                 }
295         }
296
297         if (zeroChildPos >= 0)
298         {
299                 // Move child with word = 0 to last
300                 while (zeroChildPos != (int)group->children.size()-1)
301                 {
302                         std::swap(group->children[zeroChildPos], group->children[zeroChildPos+1]);
303                         zeroChildPos += 1;
304                 }
305         }
306         else if (!group->children.empty())
307         {
308                 group->children.reserve(group->children.size()+1);
309                 group->children.push_back(new SparseIndexNode(0, 0));
310         }
311 }
312
313 deUint32 getIndexSize (const SparseIndexNode* group)
314 {
315         size_t  numNodes        = group->children.size();
316
317         for (size_t childNdx = 0; childNdx < group->children.size(); childNdx++)
318                 numNodes += getIndexSize(group->children[childNdx]);
319
320         DE_ASSERT(numNodes <= std::numeric_limits<deUint32>::max());
321
322         return (deUint32)numNodes;
323 }
324
325 deUint32 addAndCountNodes (BinaryIndexNode* index, deUint32 baseOffset, const SparseIndexNode* group)
326 {
327         const deUint32  numLocalNodes   = (deUint32)group->children.size();
328         deUint32                curOffset               = numLocalNodes;
329
330         // Must be normalized prior to construction of final index
331         DE_ASSERT(group->children.empty() || group->children.back()->word == 0);
332
333         for (size_t childNdx = 0; childNdx < numLocalNodes; childNdx++)
334         {
335                 const SparseIndexNode*  child           = group->children[childNdx];
336                 const deUint32                  subtreeSize     = addAndCountNodes(index+curOffset, baseOffset+curOffset, child);
337
338                 index[childNdx].word = child->word;
339
340                 if (subtreeSize == 0)
341                         index[childNdx].index = child->index;
342                 else
343                 {
344                         DE_ASSERT(child->index == 0);
345                         index[childNdx].index = baseOffset+curOffset;
346                 }
347
348                 curOffset += subtreeSize;
349         }
350
351         return curOffset;
352 }
353
354 void buildFinalIndex (std::vector<BinaryIndexNode>* dst, const SparseIndexNode* root)
355 {
356         const deUint32  indexSize       = getIndexSize(root);
357
358         if (indexSize > 0)
359         {
360                 dst->resize(indexSize);
361                 addAndCountNodes(&(*dst)[0], 0, root);
362         }
363         else
364         {
365                 // Generate empty index
366                 dst->resize(1);
367                 (*dst)[0].word  = 0u;
368                 (*dst)[0].index = 0u;
369         }
370 }
371
372 void buildBinaryIndex (std::vector<BinaryIndexNode>* dst, size_t numEntries, const ProgramIdentifierIndex* entries)
373 {
374         de::UniquePtr<SparseIndexNode>  sparseIndex     (new SparseIndexNode());
375
376         for (size_t ndx = 0; ndx < numEntries; ndx++)
377         {
378                 const std::vector<deUint32>     searchPath      = getSearchPath(entries[ndx].id);
379                 addToSparseIndex(sparseIndex.get(), &searchPath[0], searchPath.size(), entries[ndx].index);
380         }
381
382         normalizeSparseIndex(sparseIndex.get());
383         buildFinalIndex(dst, sparseIndex.get());
384 }
385
386 } // anonymous
387
388 // BinaryIndexHash
389
390 DE_IMPLEMENT_POOL_HASH(BinaryIndexHashImpl, const ProgramBinary*, deUint32, binaryHash, binaryEqual);
391
392 BinaryIndexHash::BinaryIndexHash (void)
393         : m_hash(BinaryIndexHashImpl_create(m_memPool.getRawPool()))
394 {
395         if (!m_hash)
396                 throw std::bad_alloc();
397 }
398
399 BinaryIndexHash::~BinaryIndexHash (void)
400 {
401 }
402
403 deUint32* BinaryIndexHash::find (const ProgramBinary* binary) const
404 {
405         return BinaryIndexHashImpl_find(m_hash, binary);
406 }
407
408 void BinaryIndexHash::insert (const ProgramBinary* binary, deUint32 index)
409 {
410         if (!BinaryIndexHashImpl_insert(m_hash, binary, index))
411                 throw std::bad_alloc();
412 }
413
414 // BinaryRegistryWriter
415
416 BinaryRegistryWriter::BinaryRegistryWriter (const std::string& dstPath)
417         : m_dstPath(dstPath)
418 {
419         if (de::FilePath(dstPath).exists())
420                 initFromPath(dstPath);
421 }
422
423 BinaryRegistryWriter::~BinaryRegistryWriter (void)
424 {
425         for (BinaryVector::const_iterator binaryIter = m_binaries.begin();
426                  binaryIter != m_binaries.end();
427                  ++binaryIter)
428                 delete binaryIter->binary;
429 }
430
431 void BinaryRegistryWriter::initFromPath (const std::string& srcPath)
432 {
433         DE_ASSERT(m_binaries.empty());
434
435         for (de::DirectoryIterator iter(srcPath); iter.hasItem(); iter.next())
436         {
437                 const de::FilePath      path            = iter.getItem();
438                 const std::string       baseName        = path.getBaseName();
439
440                 if (isProgramFileName(baseName))
441                 {
442                         const deUint32                                          index   = getProgramIndexFromName(baseName);
443                         const de::UniquePtr<ProgramBinary>      binary  (readBinary(path.getPath()));
444
445                         addBinary(index, *binary);
446                         // \note referenceCount is left to 0 and will only be incremented
447                         //               if binary is reused (added via addProgram()).
448                 }
449         }
450 }
451
452 void BinaryRegistryWriter::addProgram (const ProgramIdentifier& id, const ProgramBinary& binary)
453 {
454         const deUint32* const   indexPtr        = findBinary(binary);
455         deUint32                                index           = indexPtr ? *indexPtr : ~0u;
456
457         if (!indexPtr)
458         {
459                 index = getNextSlot();
460                 addBinary(index, binary);
461         }
462
463         m_binaries[index].referenceCount += 1;
464         m_binaryIndices.push_back(ProgramIdentifierIndex(id, index));
465 }
466
467 deUint32* BinaryRegistryWriter::findBinary (const ProgramBinary& binary) const
468 {
469         return m_binaryHash.find(&binary);
470 }
471
472 deUint32 BinaryRegistryWriter::getNextSlot (void) const
473 {
474         const deUint32  index   = (deUint32)m_binaries.size();
475
476         if ((size_t)index != m_binaries.size())
477                 throw std::bad_alloc(); // Overflow
478
479         return index;
480 }
481
482 void BinaryRegistryWriter::addBinary (deUint32 index, const ProgramBinary& binary)
483 {
484         DE_ASSERT(binary.getFormat() == vk::PROGRAM_FORMAT_SPIRV);
485         DE_ASSERT(findBinary(binary) == DE_NULL);
486
487         ProgramBinary* const    binaryClone             = new ProgramBinary(binary);
488
489         try
490         {
491                 if (m_binaries.size() < (size_t)index+1)
492                         m_binaries.resize(index+1);
493
494                 DE_ASSERT(!m_binaries[index].binary);
495                 DE_ASSERT(m_binaries[index].referenceCount == 0);
496
497                 m_binaries[index].binary = binaryClone;
498                 // \note referenceCount is not incremented here
499         }
500         catch (...)
501         {
502                 delete binaryClone;
503                 throw;
504         }
505
506         m_binaryHash.insert(binaryClone, index);
507 }
508
509 void BinaryRegistryWriter::write (void) const
510 {
511         writeToPath(m_dstPath);
512 }
513
514 void BinaryRegistryWriter::writeToPath (const std::string& dstPath) const
515 {
516         if (!de::FilePath(dstPath).exists())
517                 de::createDirectoryAndParents(dstPath.c_str());
518
519         DE_ASSERT(m_binaries.size() <= 0xffffffffu);
520         for (size_t binaryNdx = 0; binaryNdx < m_binaries.size(); ++binaryNdx)
521         {
522                 const BinarySlot&       slot    = m_binaries[binaryNdx];
523
524                 if (slot.referenceCount > 0)
525                 {
526                         DE_ASSERT(slot.binary);
527                         writeBinary(dstPath, (deUint32)binaryNdx, *slot.binary);
528                 }
529                 else
530                 {
531                         // Delete stale binary if such exists
532                         const std::string       progPath        = getProgramPath(dstPath, (deUint32)binaryNdx);
533
534                         if (de::FilePath(progPath).exists())
535                                 deDeleteFile(progPath.c_str());
536                 }
537
538         }
539
540         // Write index
541         {
542                 const de::FilePath                              indexPath       = getIndexPath(dstPath);
543                 std::vector<BinaryIndexNode>    index;
544
545                 buildBinaryIndex(&index, m_binaryIndices.size(), !m_binaryIndices.empty() ? &m_binaryIndices[0] : DE_NULL);
546
547                 // Even in empty index there is always terminating node for the root group
548                 DE_ASSERT(!index.empty());
549
550                 if (!de::FilePath(indexPath.getDirName()).exists())
551                         de::createDirectoryAndParents(indexPath.getDirName().c_str());
552
553                 {
554                         std::ofstream indexOut(indexPath.getPath(), std::ios_base::binary);
555
556                         if (!indexOut.is_open() || !indexOut.good())
557                                 throw tcu::InternalError(string("Failed to open program binary index file ") + indexPath.getPath());
558
559                         indexOut.write((const char*)&index[0], index.size()*sizeof(BinaryIndexNode));
560                 }
561         }
562 }
563
564 // BinaryRegistryReader
565
566 BinaryRegistryReader::BinaryRegistryReader (const tcu::Archive& archive, const std::string& srcPath)
567         : m_archive     (archive)
568         , m_srcPath     (srcPath)
569 {
570 }
571
572 BinaryRegistryReader::~BinaryRegistryReader (void)
573 {
574 }
575
576 ProgramBinary* BinaryRegistryReader::loadProgram (const ProgramIdentifier& id) const
577 {
578         if (!m_binaryIndex)
579         {
580                 try
581                 {
582                         m_binaryIndex = BinaryIndexPtr(new BinaryIndexAccess(de::MovePtr<tcu::Resource>(m_archive.getResource(getIndexPath(m_srcPath).c_str()))));
583                 }
584                 catch (const tcu::ResourceError& e)
585                 {
586                         throw ProgramNotFoundException(id, string("Failed to open binary index (") + e.what() + ")");
587                 }
588         }
589
590         {
591                 const deUint32* indexPos        = findBinaryIndex(m_binaryIndex.get(), id);
592
593                 if (indexPos)
594                 {
595                         const string    fullPath        = getProgramPath(m_srcPath, *indexPos);
596
597                         try
598                         {
599                                 de::UniquePtr<tcu::Resource>    progRes         (m_archive.getResource(fullPath.c_str()));
600                                 const int                                               progSize        = progRes->getSize();
601                                 vector<deUint8>                                 bytes           (progSize);
602
603                                 TCU_CHECK_INTERNAL(!bytes.empty());
604
605                                 progRes->read(&bytes[0], progSize);
606
607                                 return new ProgramBinary(vk::PROGRAM_FORMAT_SPIRV, bytes.size(), &bytes[0]);
608                         }
609                         catch (const tcu::ResourceError& e)
610                         {
611                                 throw ProgramNotFoundException(id, e.what());
612                         }
613                 }
614                 else
615                         throw ProgramNotFoundException(id, "Program not found in index");
616         }
617 }
618
619 } // BinaryRegistryDetail
620 } // vk