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