Home | History | Annotate | Download | only in vulkan
      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 "deString.h"
     30 #include "deInt32.h"
     31 
     32 #include <sstream>
     33 #include <fstream>
     34 #include <stdexcept>
     35 #include <limits>
     36 
     37 namespace vk
     38 {
     39 namespace BinaryRegistryDetail
     40 {
     41 
     42 using std::string;
     43 using std::vector;
     44 
     45 namespace
     46 {
     47 
     48 string getProgramPath (const std::string& dirName, deUint32 index)
     49 {
     50 	return de::FilePath::join(dirName, de::toString(tcu::toHex(index)) + ".spv").getPath();
     51 }
     52 
     53 string getIndexPath (const std::string& dirName)
     54 {
     55 	return de::FilePath::join(dirName, "index.bin").getPath();
     56 }
     57 
     58 void writeBinary (const std::string& dstDir, deUint32 index, const ProgramBinary& binary)
     59 {
     60 	const de::FilePath	fullPath	= getProgramPath(dstDir, index);
     61 
     62 	if (!de::FilePath(fullPath.getDirName()).exists())
     63 		de::createDirectoryAndParents(fullPath.getDirName().c_str());
     64 
     65 	{
     66 		std::ofstream	out		(fullPath.getPath(), std::ios_base::binary);
     67 
     68 		if (!out.is_open() || !out.good())
     69 			throw tcu::Exception("Failed to open " + string(fullPath.getPath()));
     70 
     71 		out.write((const char*)binary.getBinary(), binary.getSize());
     72 		out.close();
     73 	}
     74 }
     75 
     76 deUint32 binaryHash (const ProgramBinary* binary)
     77 {
     78 	return deMemoryHash(binary->getBinary(), binary->getSize());
     79 }
     80 
     81 deBool binaryEqual (const ProgramBinary* a, const ProgramBinary* b)
     82 {
     83 	if (a->getSize() == b->getSize())
     84 		return deMemoryEqual(a->getBinary(), b->getBinary(), a->getSize());
     85 	else
     86 		return DE_FALSE;
     87 }
     88 
     89 std::vector<deUint32> getSearchPath (const ProgramIdentifier& id)
     90 {
     91 	const std::string	combinedStr		= id.testCasePath + '#' + id.programName;
     92 	const size_t		strLen			= combinedStr.size();
     93 	const size_t		numWords		= strLen/4 + 1;		// Must always end up with at least one 0 byte
     94 	vector<deUint32>	words			(numWords, 0u);
     95 
     96 	deMemcpy(&words[0], combinedStr.c_str(), strLen);
     97 
     98 	return words;
     99 }
    100 
    101 const deUint32* findBinaryIndex (BinaryIndexAccess* index, const ProgramIdentifier& id)
    102 {
    103 	const vector<deUint32>	words	= getSearchPath(id);
    104 	size_t					nodeNdx	= 0;
    105 	size_t					wordNdx	= 0;
    106 
    107 	for (;;)
    108 	{
    109 		const BinaryIndexNode&	curNode	= (*index)[nodeNdx];
    110 
    111 		if (curNode.word == words[wordNdx])
    112 		{
    113 			if (wordNdx+1 < words.size())
    114 			{
    115 				TCU_CHECK_INTERNAL((size_t)curNode.index < index->size());
    116 
    117 				nodeNdx  = curNode.index;
    118 				wordNdx	+= 1;
    119 			}
    120 			else if (wordNdx+1 == words.size())
    121 				return &curNode.index;
    122 			else
    123 				return DE_NULL;
    124 		}
    125 		else if (curNode.word != 0)
    126 		{
    127 			nodeNdx += 1;
    128 
    129 			// Index should always be null-terminated
    130 			TCU_CHECK_INTERNAL(nodeNdx < index->size());
    131 		}
    132 		else
    133 			return DE_NULL;
    134 	}
    135 
    136 	return DE_NULL;
    137 }
    138 
    139 //! Sparse index node used for final binary index construction
    140 struct SparseIndexNode
    141 {
    142 	deUint32						word;
    143 	deUint32						index;
    144 	std::vector<SparseIndexNode*>	children;
    145 
    146 	SparseIndexNode (deUint32 word_, deUint32 index_)
    147 		: word	(word_)
    148 		, index	(index_)
    149 	{}
    150 
    151 	SparseIndexNode (void)
    152 		: word	(0)
    153 		, index	(0)
    154 	{}
    155 
    156 	~SparseIndexNode (void)
    157 	{
    158 		for (size_t ndx = 0; ndx < children.size(); ndx++)
    159 			delete children[ndx];
    160 	}
    161 };
    162 
    163 #if defined(DE_DEBUG)
    164 bool isNullByteTerminated (deUint32 word)
    165 {
    166 	deUint8 bytes[4];
    167 	deMemcpy(bytes, &word, sizeof(word));
    168 	return bytes[3] == 0;
    169 }
    170 #endif
    171 
    172 void addToSparseIndex (SparseIndexNode* group, const deUint32* words, size_t numWords, deUint32 index)
    173 {
    174 	const deUint32		curWord	= words[0];
    175 	SparseIndexNode*	child	= DE_NULL;
    176 
    177 	for (size_t childNdx = 0; childNdx < group->children.size(); childNdx++)
    178 	{
    179 		if (group->children[childNdx]->word == curWord)
    180 		{
    181 			child = group->children[childNdx];
    182 			break;
    183 		}
    184 	}
    185 
    186 	DE_ASSERT(numWords > 1 || !child);
    187 
    188 	if (!child)
    189 	{
    190 		group->children.reserve(group->children.size()+1);
    191 		group->children.push_back(new SparseIndexNode(curWord, numWords == 1 ? index : 0));
    192 
    193 		child = group->children.back();
    194 	}
    195 
    196 	if (numWords > 1)
    197 		addToSparseIndex(child, words+1, numWords-1, index);
    198 	else
    199 		DE_ASSERT(isNullByteTerminated(curWord));
    200 }
    201 
    202 // Prepares sparse index for finalization. Ensures that child with word = 0 is moved
    203 // to the end, or one is added if there is no such child already.
    204 void normalizeSparseIndex (SparseIndexNode* group)
    205 {
    206 	int		zeroChildPos	= -1;
    207 
    208 	for (size_t childNdx = 0; childNdx < group->children.size(); childNdx++)
    209 	{
    210 		normalizeSparseIndex(group->children[childNdx]);
    211 
    212 		if (group->children[childNdx]->word == 0)
    213 		{
    214 			DE_ASSERT(zeroChildPos < 0);
    215 			zeroChildPos = (int)childNdx;
    216 		}
    217 	}
    218 
    219 	if (zeroChildPos >= 0)
    220 	{
    221 		// Move child with word = 0 to last
    222 		while (zeroChildPos != (int)group->children.size()-1)
    223 		{
    224 			std::swap(group->children[zeroChildPos], group->children[zeroChildPos+1]);
    225 			zeroChildPos += 1;
    226 		}
    227 	}
    228 	else if (!group->children.empty())
    229 	{
    230 		group->children.reserve(group->children.size()+1);
    231 		group->children.push_back(new SparseIndexNode(0, 0));
    232 	}
    233 }
    234 
    235 deUint32 getIndexSize (const SparseIndexNode* group)
    236 {
    237 	size_t	numNodes	= group->children.size();
    238 
    239 	for (size_t childNdx = 0; childNdx < group->children.size(); childNdx++)
    240 		numNodes += getIndexSize(group->children[childNdx]);
    241 
    242 	DE_ASSERT(numNodes <= std::numeric_limits<deUint32>::max());
    243 
    244 	return (deUint32)numNodes;
    245 }
    246 
    247 deUint32 addAndCountNodes (BinaryIndexNode* index, deUint32 baseOffset, const SparseIndexNode* group)
    248 {
    249 	const deUint32	numLocalNodes	= (deUint32)group->children.size();
    250 	deUint32		curOffset		= numLocalNodes;
    251 
    252 	// Must be normalized prior to construction of final index
    253 	DE_ASSERT(group->children.empty() || group->children.back()->word == 0);
    254 
    255 	for (size_t childNdx = 0; childNdx < numLocalNodes; childNdx++)
    256 	{
    257 		const SparseIndexNode*	child		= group->children[childNdx];
    258 		const deUint32			subtreeSize	= addAndCountNodes(index+curOffset, baseOffset+curOffset, child);
    259 
    260 		index[childNdx].word = child->word;
    261 
    262 		if (subtreeSize == 0)
    263 			index[childNdx].index = child->index;
    264 		else
    265 		{
    266 			DE_ASSERT(child->index == 0);
    267 			index[childNdx].index = baseOffset+curOffset;
    268 		}
    269 
    270 		curOffset += subtreeSize;
    271 	}
    272 
    273 	return curOffset;
    274 }
    275 
    276 void buildFinalIndex (std::vector<BinaryIndexNode>* dst, const SparseIndexNode* root)
    277 {
    278 	const deUint32	indexSize	= getIndexSize(root);
    279 
    280 	if (indexSize > 0)
    281 	{
    282 		dst->resize(indexSize);
    283 		addAndCountNodes(&(*dst)[0], 0, root);
    284 	}
    285 	else
    286 	{
    287 		// Generate empty index
    288 		dst->resize(1);
    289 		(*dst)[0].word	= 0u;
    290 		(*dst)[0].index	= 0u;
    291 	}
    292 }
    293 
    294 } // anonymous
    295 
    296 // BinaryRegistryWriter
    297 
    298 DE_IMPLEMENT_POOL_HASH(BinaryHash, const ProgramBinary*, deUint32, binaryHash, binaryEqual);
    299 
    300 BinaryRegistryWriter::BinaryRegistryWriter (const std::string& dstPath)
    301 	: m_dstPath			(dstPath)
    302 	, m_binaryIndexMap	(DE_NULL)
    303 {
    304 	m_binaryIndexMap = BinaryHash_create(m_memPool.getRawPool());
    305 
    306 	if (!m_binaryIndexMap)
    307 		throw std::bad_alloc();
    308 }
    309 
    310 BinaryRegistryWriter::~BinaryRegistryWriter (void)
    311 {
    312 	for (BinaryVector::const_iterator binaryIter = m_compactedBinaries.begin();
    313 		 binaryIter != m_compactedBinaries.end();
    314 		 ++binaryIter)
    315 		delete *binaryIter;
    316 }
    317 
    318 void BinaryRegistryWriter::storeProgram (const ProgramIdentifier& id, const ProgramBinary& binary)
    319 {
    320 	const deUint32* const	indexPtr	= BinaryHash_find(m_binaryIndexMap, &binary);
    321 	deUint32				index		= indexPtr ? *indexPtr : ~0u;
    322 
    323 	DE_ASSERT(binary.getFormat() == vk::PROGRAM_FORMAT_SPIRV);
    324 
    325 	if (!indexPtr)
    326 	{
    327 		ProgramBinary* const	binaryClone		= new ProgramBinary(binary);
    328 
    329 		try
    330 		{
    331 			index = (deUint32)m_compactedBinaries.size();
    332 			m_compactedBinaries.push_back(binaryClone);
    333 		}
    334 		catch (...)
    335 		{
    336 			delete binaryClone;
    337 			throw;
    338 		}
    339 
    340 		writeBinary(m_dstPath, index, binary);
    341 
    342 		if (!BinaryHash_insert(m_binaryIndexMap, binaryClone, index))
    343 			throw std::bad_alloc();
    344 	}
    345 
    346 	DE_ASSERT((size_t)index < m_compactedBinaries.size());
    347 
    348 	m_binaryIndices.push_back(BinaryIndex(id, index));
    349 }
    350 
    351 void BinaryRegistryWriter::writeIndex (void) const
    352 {
    353 	const de::FilePath				indexPath	= getIndexPath(m_dstPath);
    354 	std::vector<BinaryIndexNode>	index;
    355 
    356 	{
    357 		de::UniquePtr<SparseIndexNode>	sparseIndex	(new SparseIndexNode());
    358 
    359 		for (size_t progNdx = 0; progNdx < m_binaryIndices.size(); progNdx++)
    360 		{
    361 			const std::vector<deUint32>	searchPath	= getSearchPath(m_binaryIndices[progNdx].id);
    362 			addToSparseIndex(sparseIndex.get(), &searchPath[0], searchPath.size(), m_binaryIndices[progNdx].index);
    363 		}
    364 
    365 		normalizeSparseIndex(sparseIndex.get());
    366 		buildFinalIndex(&index, sparseIndex.get());
    367 	}
    368 
    369 	// Even in empty index there is always terminating node for the root group
    370 	DE_ASSERT(!index.empty());
    371 
    372 	if (!de::FilePath(indexPath.getDirName()).exists())
    373 		de::createDirectoryAndParents(indexPath.getDirName().c_str());
    374 
    375 	{
    376 		std::ofstream indexOut(indexPath.getPath(), std::ios_base::binary);
    377 
    378 		if (!indexOut.is_open() || !indexOut.good())
    379 			throw tcu::InternalError(string("Failed to open program binary index file ") + indexPath.getPath());
    380 
    381 		indexOut.write((const char*)&index[0], index.size()*sizeof(BinaryIndexNode));
    382 	}
    383 }
    384 
    385 // BinaryRegistryReader
    386 
    387 BinaryRegistryReader::BinaryRegistryReader (const tcu::Archive& archive, const std::string& srcPath)
    388 	: m_archive	(archive)
    389 	, m_srcPath	(srcPath)
    390 {
    391 }
    392 
    393 BinaryRegistryReader::~BinaryRegistryReader (void)
    394 {
    395 }
    396 
    397 ProgramBinary* BinaryRegistryReader::loadProgram (const ProgramIdentifier& id) const
    398 {
    399 	if (!m_binaryIndex)
    400 	{
    401 		try
    402 		{
    403 			m_binaryIndex = BinaryIndexPtr(new BinaryIndexAccess(de::MovePtr<tcu::Resource>(m_archive.getResource(getIndexPath(m_srcPath).c_str()))));
    404 		}
    405 		catch (const tcu::ResourceError& e)
    406 		{
    407 			throw ProgramNotFoundException(id, string("Failed to open binary index (") + e.what() + ")");
    408 		}
    409 	}
    410 
    411 	{
    412 		const deUint32*	indexPos	= findBinaryIndex(m_binaryIndex.get(), id);
    413 
    414 		if (indexPos)
    415 		{
    416 			const string	fullPath	= getProgramPath(m_srcPath, *indexPos);
    417 
    418 			try
    419 			{
    420 				de::UniquePtr<tcu::Resource>	progRes		(m_archive.getResource(fullPath.c_str()));
    421 				const int						progSize	= progRes->getSize();
    422 				vector<deUint8>					bytes		(progSize);
    423 
    424 				TCU_CHECK_INTERNAL(!bytes.empty());
    425 
    426 				progRes->read(&bytes[0], progSize);
    427 
    428 				return new ProgramBinary(vk::PROGRAM_FORMAT_SPIRV, bytes.size(), &bytes[0]);
    429 			}
    430 			catch (const tcu::ResourceError& e)
    431 			{
    432 				throw ProgramNotFoundException(id, e.what());
    433 			}
    434 		}
    435 		else
    436 			throw ProgramNotFoundException(id, "Program not found in index");
    437 	}
    438 }
    439 
    440 } // BinaryRegistryDetail
    441 } // vk
    442