• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*-------------------------------------------------------------------------
2  * Vulkan CTS Framework
3  * --------------------
4  *
5  * Copyright (c) 2019 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 utilities.
22  *//*--------------------------------------------------------------------*/
23 
24 #include "spirv-tools/optimizer.hpp"
25 
26 #include "qpInfo.h"
27 
28 #include "vkPrograms.hpp"
29 #include "vkShaderToSpirV.hpp"
30 #include "vkSpirVAsm.hpp"
31 #include "vkRefUtil.hpp"
32 
33 #include "deMutex.hpp"
34 #include "deFilePath.hpp"
35 #include "deArrayUtil.hpp"
36 #include "deMemory.h"
37 #include "deInt32.h"
38 
39 #include "tcuCommandLine.hpp"
40 
41 #include <map>
42 #include <mutex>
43 
44 #if DE_OS == DE_OS_ANDROID
45 #define DISABLE_SHADERCACHE_IPC
46 #endif
47 
48 namespace vk
49 {
50 
51 using std::string;
52 using std::vector;
53 using std::map;
54 
55 #if defined(DE_DEBUG)
56 #	define VALIDATE_BINARIES	true
57 #else
58 #	define VALIDATE_BINARIES	false
59 #endif
60 
61 #define SPIRV_BINARY_ENDIANNESS DE_LITTLE_ENDIAN
62 
63 // ProgramBinary
64 
ProgramBinary(ProgramFormat format,size_t binarySize,const deUint8 * binary)65 ProgramBinary::ProgramBinary (ProgramFormat format, size_t binarySize, const deUint8* binary)
66 	: m_format	(format)
67 	, m_binary	(binary, binary+binarySize)
68 	, m_used	(false)
69 {
70 }
71 
72 // Utils
73 
74 namespace
75 {
76 
isNativeSpirVBinaryEndianness(void)77 bool isNativeSpirVBinaryEndianness (void)
78 {
79 #if (DE_ENDIANNESS == SPIRV_BINARY_ENDIANNESS)
80 	return true;
81 #else
82 	return false;
83 #endif
84 }
85 
isSaneSpirVBinary(const ProgramBinary & binary)86 bool isSaneSpirVBinary (const ProgramBinary& binary)
87 {
88 	const deUint32	spirvMagicWord	= 0x07230203;
89 	const deUint32	spirvMagicBytes	= isNativeSpirVBinaryEndianness()
90 									? spirvMagicWord
91 									: deReverseBytes32(spirvMagicWord);
92 
93 	DE_ASSERT(binary.getFormat() == PROGRAM_FORMAT_SPIRV);
94 
95 	if (binary.getSize() % sizeof(deUint32) != 0)
96 		return false;
97 
98 	if (binary.getSize() < sizeof(deUint32))
99 		return false;
100 
101 	if (*(const deUint32*)binary.getBinary() != spirvMagicBytes)
102 		return false;
103 
104 	return true;
105 }
106 
optimizeCompiledBinary(vector<deUint32> & binary,int optimizationRecipe,const SpirvVersion spirvVersion)107 void optimizeCompiledBinary (vector<deUint32>& binary, int optimizationRecipe, const SpirvVersion spirvVersion)
108 {
109 	spv_target_env targetEnv = SPV_ENV_VULKAN_1_0;
110 
111 	// Map SpirvVersion with spv_target_env:
112 	switch (spirvVersion)
113 	{
114 		case SPIRV_VERSION_1_0: targetEnv = SPV_ENV_VULKAN_1_0;	break;
115 		case SPIRV_VERSION_1_1:
116 		case SPIRV_VERSION_1_2:
117 		case SPIRV_VERSION_1_3: targetEnv = SPV_ENV_VULKAN_1_1;	break;
118 		case SPIRV_VERSION_1_4: targetEnv = SPV_ENV_VULKAN_1_1_SPIRV_1_4;	break;
119 		case SPIRV_VERSION_1_5: targetEnv = SPV_ENV_VULKAN_1_2;	break;
120 		case SPIRV_VERSION_1_6: targetEnv = SPV_ENV_VULKAN_1_3;	break;
121 		default:
122 			TCU_THROW(InternalError, "Unexpected SPIR-V version requested");
123 	}
124 
125 	spvtools::Optimizer optimizer(targetEnv);
126 
127 	switch (optimizationRecipe)
128 	{
129 		case 1:
130 			optimizer.RegisterPerformancePasses();
131 			break;
132 		case 2:
133 			optimizer.RegisterSizePasses();
134 			break;
135 		default:
136 			TCU_THROW(InternalError, "Unknown optimization recipe requested");
137 	}
138 
139 	spvtools::OptimizerOptions optimizer_options;
140 	optimizer_options.set_run_validator(false);
141 	const bool ok = optimizer.Run(binary.data(), binary.size(), &binary, optimizer_options);
142 
143 	if (!ok)
144 		TCU_THROW(InternalError, "Optimizer call failed");
145 }
146 
createProgramBinaryFromSpirV(const vector<deUint32> & binary)147 ProgramBinary* createProgramBinaryFromSpirV (const vector<deUint32>& binary)
148 {
149 	DE_ASSERT(!binary.empty());
150 
151 	if (isNativeSpirVBinaryEndianness())
152 		return new ProgramBinary(PROGRAM_FORMAT_SPIRV, binary.size()*sizeof(deUint32), (const deUint8*)&binary[0]);
153 	else
154 		TCU_THROW(InternalError, "SPIR-V endianness translation not supported");
155 }
156 
157 } // anonymous
158 
validateCompiledBinary(const vector<deUint32> & binary,glu::ShaderProgramInfo * buildInfo,const SpirvValidatorOptions & options)159 void validateCompiledBinary(const vector<deUint32>& binary, glu::ShaderProgramInfo* buildInfo, const SpirvValidatorOptions& options)
160 {
161 	std::ostringstream validationLog;
162 
163 	if (!validateSpirV(binary.size(), &binary[0], &validationLog, options))
164 	{
165 		buildInfo->program.linkOk	 = false;
166 		buildInfo->program.infoLog	+= "\n" + validationLog.str();
167 
168 		TCU_THROW(InternalError, "Validation failed for compiled SPIR-V binary");
169 	}
170 }
171 
validateCompiledBinary(const vector<deUint32> & binary,SpirVProgramInfo * buildInfo,const SpirvValidatorOptions & options)172 void validateCompiledBinary(const vector<deUint32>& binary, SpirVProgramInfo* buildInfo, const SpirvValidatorOptions& options)
173 {
174 	std::ostringstream validationLog;
175 
176 	if (!validateSpirV(binary.size(), &binary[0], &validationLog, options))
177 	{
178 		buildInfo->compileOk = false;
179 		buildInfo->infoLog += "\n" + validationLog.str();
180 
181 		TCU_THROW(InternalError, "Validation failed for compiled SPIR-V binary");
182 	}
183 }
184 
185 // IPC functions
186 #ifndef DISABLE_SHADERCACHE_IPC
187 #include "vkIPC.inl"
188 #endif
189 
190 // Overridable wrapper for de::Mutex
191 class cacheMutex
192 {
193 public:
cacheMutex()194 	cacheMutex () {}
~cacheMutex()195 	virtual ~cacheMutex () {}
lock()196 	virtual void lock () { localMutex.lock(); }
unlock()197 	virtual void unlock () { localMutex.unlock(); }
198 private:
199 	de::Mutex localMutex;
200 };
201 
202 #ifndef DISABLE_SHADERCACHE_IPC
203 // Overriden cacheMutex that uses IPC instead
204 class cacheMutexIPC : public cacheMutex
205 {
206 public:
cacheMutexIPC()207 	cacheMutexIPC ()
208 	{
209 		ipc_sem_init(&guard, "cts_shadercache_ipc_guard");
210 		ipc_sem_create(&guard, 1);
211 	}
~cacheMutexIPC()212 	virtual ~cacheMutexIPC ()
213 	{
214 		ipc_sem_close(&guard);
215 	}
lock()216 	virtual void lock () { ipc_sem_decrement(&guard); }
unlock()217 	virtual void unlock () { ipc_sem_increment(&guard); }
218 private:
219 	ipc_sharedsemaphore guard;
220 };
221 #endif
222 
223 // Each cache node takes 4 * 4 = 16 bytes; 1M items takes 16M memory.
224 const deUint32						cacheMaxItems		= 1024 * 1024;
225 cacheMutex*							cacheFileMutex		= nullptr;
226 deUint32*							cacheMempool		= nullptr;
227 #ifndef DISABLE_SHADERCACHE_IPC
228 ipc_sharedmemory					cacheIPCMemory;
229 #endif
230 
231 struct cacheNode
232 {
233 	deUint32 key;
234 	deUint32 data;
235 	deUint32 right_child;
236 	deUint32 left_child;
237 };
238 
cacheSearch(deUint32 key)239 cacheNode* cacheSearch (deUint32 key)
240 {
241 	cacheNode*		r		= (cacheNode*)(cacheMempool + 1);
242 	int*			tail	= (int*)cacheMempool;
243 	unsigned int	p		= 0;
244 
245 	if (!*tail) {
246 		// Cache is empty.
247 		return 0;
248 	}
249 
250 	while (1)
251 	{
252 		if (r[p].key == key)
253 			return &r[p];
254 
255 		if (key > r[p].key)
256 			p = r[p].right_child;
257 		else
258 			p = r[p].left_child;
259 
260 		if (p == 0)
261 			return 0;
262 	}
263 }
264 
cacheInsert(deUint32 key,deUint32 data)265 void cacheInsert (deUint32 key, deUint32 data)
266 {
267 	cacheNode*	r		= (cacheNode*)(cacheMempool + 1);
268 	int*		tail	= (int*)cacheMempool;
269 	int			newnode	= *tail;
270 
271 	DE_ASSERT(newnode < cacheMaxItems);
272 
273 	// If we run out of cache space, reset the cache index.
274 	if (newnode >= cacheMaxItems)
275 	{
276 		*tail = 0;
277 		newnode = 0;
278 	}
279 
280 	r[*tail].data = data;
281 	r[*tail].key = key;
282 	r[*tail].left_child = 0;
283 	r[*tail].right_child = 0;
284 
285 	(*tail)++;
286 
287 	if (newnode == 0)
288 	{
289 		// first
290 		return;
291 	}
292 
293 	int p = 0;
294 	while (1)
295 	{
296 		if (r[p].key == key)
297 		{
298 			// collision; use the latest data
299 			r[p].data = data;
300 			(*tail)--;
301 			return;
302 		}
303 
304 		if (key > r[p].key)
305 		{
306 			if (r[p].right_child != 0)
307 			{
308 				p = r[p].right_child;
309 			}
310 			else
311 			{
312 				r[p].right_child = newnode;
313 				return;
314 			}
315 		}
316 		else
317 		{
318 			if (r[p].left_child != 0)
319 			{
320 				p = r[p].left_child;
321 			}
322 			else
323 			{
324 				r[p].left_child = newnode;
325 				return;
326 			}
327 		}
328 	}
329 }
330 
331 // Called via atexit()
shaderCacheClean()332 void shaderCacheClean ()
333 {
334 	delete cacheFileMutex;
335 	delete[] cacheMempool;
336 }
337 
338 #ifndef DISABLE_SHADERCACHE_IPC
339 // Called via atexit()
shaderCacheCleanIPC()340 void shaderCacheCleanIPC ()
341 {
342 	delete cacheFileMutex;
343 	ipc_mem_close(&cacheIPCMemory);
344 }
345 #endif
346 
shaderCacheFirstRunCheck(const tcu::CommandLine & commandLine)347 void shaderCacheFirstRunCheck (const tcu::CommandLine& commandLine)
348 {
349 	bool first = true;
350 
351 	// We need to solve two problems here:
352 	// 1) The cache and cache mutex only have to be initialized once by the first thread that arrives here.
353 	// 2) We must prevent other threads from exiting early from this function thinking they don't have to initialize the cache and
354 	//    cache mutex, only to try to lock the cache mutex while the first thread is still initializing it. To prevent this, we must
355 	//    hold an initialization mutex (declared below) while initializing the cache and cache mutex, making other threads wait.
356 
357 	// Used to check and set cacheFileFirstRun. We make it static, and C++11 guarantees it will only be initialized once.
358 	static std::mutex cacheFileFirstRunMutex;
359 	static bool cacheFileFirstRun = true;
360 
361 	// Is cacheFileFirstRun true for this thread?
362 	bool needInit = false;
363 
364 	// Check cacheFileFirstRun only while holding the mutex, and hold it while initializing the cache.
365 	const std::lock_guard<std::mutex> lock(cacheFileFirstRunMutex);
366 	if (cacheFileFirstRun)
367 	{
368 		needInit = true;
369 		cacheFileFirstRun = false;
370 	}
371 
372 	if (needInit)
373 	{
374 #ifndef DISABLE_SHADERCACHE_IPC
375 		if (commandLine.isShaderCacheIPCEnabled())
376 		{
377 			// IPC path, allocate shared mutex and shared memory
378 			cacheFileMutex = new cacheMutexIPC;
379 			cacheFileMutex->lock();
380 			ipc_mem_init(&cacheIPCMemory, "cts_shadercache_memory", sizeof(deUint32) * (cacheMaxItems * 4 + 1));
381 			if (ipc_mem_open_existing(&cacheIPCMemory) != 0)
382 			{
383 				ipc_mem_create(&cacheIPCMemory);
384 				cacheMempool = (deUint32*)ipc_mem_access(&cacheIPCMemory);
385 				cacheMempool[0] = 0;
386 			}
387 			else
388 			{
389 				cacheMempool = (deUint32*)ipc_mem_access(&cacheIPCMemory);
390 				first = false;
391 			}
392 			atexit(shaderCacheCleanIPC);
393 		}
394 		else
395 #endif
396 		{
397 			// Non-IPC path, allocate local mutex and memory
398 			cacheFileMutex = new cacheMutex;
399 			cacheFileMutex->lock();
400 			cacheMempool = new deUint32[cacheMaxItems * 4 + 1];
401 			cacheMempool[0] = 0;
402 
403 			atexit(shaderCacheClean);
404 		}
405 
406 		if (first)
407 		{
408 			if (commandLine.isShaderCacheTruncateEnabled())
409 			{
410 				// Open file with "w" access to truncate it
411 				FILE* f = fopen(commandLine.getShaderCacheFilename(), "wb");
412 				if (f)
413 					fclose(f);
414 			}
415 			else
416 			{
417 				// Parse chunked shader cache file for hashes and offsets
418 				FILE* file	= fopen(commandLine.getShaderCacheFilename(), "rb");
419 				int count	= 0;
420 				if (file)
421 				{
422 					deUint32 chunksize	= 0;
423 					deUint32 hash		= 0;
424 					deUint32 offset		= 0;
425 					bool ok				= true;
426 					while (ok)
427 					{
428 						offset = (deUint32)ftell(file);
429 						if (ok) ok = fread(&chunksize, 1, 4, file)				== 4;
430 						if (ok) ok = fread(&hash, 1, 4, file)					== 4;
431 						if (ok) cacheInsert(hash, offset);
432 						if (ok) ok = fseek(file, offset + chunksize, SEEK_SET)	== 0;
433 						count++;
434 					}
435 					fclose(file);
436 				}
437 			}
438 		}
439 		cacheFileMutex->unlock();
440 	}
441 }
442 
intToString(deUint32 integer)443 std::string intToString (deUint32 integer)
444 {
445 	std::stringstream temp_sstream;
446 
447 	temp_sstream << integer;
448 
449 	return temp_sstream.str();
450 }
451 
452 // 32-bit FNV-1 hash
shadercacheHash(const char * str)453 deUint32 shadercacheHash (const char* str)
454 {
455 	deUint32 hash = 0x811c9dc5;
456 	deUint32 c;
457 	while ((c = (deUint32)*str++) != 0)
458 	{
459 		hash *= 16777619;
460 		hash ^= c;
461 	}
462 	return hash;
463 }
464 
shadercacheLoad(const std::string & shaderstring,const char * shaderCacheFilename,deUint32 hash)465 vk::ProgramBinary* shadercacheLoad (const std::string& shaderstring, const char* shaderCacheFilename, deUint32 hash)
466 {
467 	deInt32			format;
468 	deInt32			length;
469 	deInt32			sourcelength;
470 	deUint32		temp;
471 	deUint8*		bin			= 0;
472 	char*			source		= 0;
473 	deBool			ok			= true;
474 	deBool			diff		= true;
475 	cacheNode*		node		= 0;
476 	cacheFileMutex->lock();
477 
478 	node = cacheSearch(hash);
479 	if (node == 0)
480 	{
481 		cacheFileMutex->unlock();
482 		return 0;
483 	}
484 	FILE*			file		= fopen(shaderCacheFilename, "rb");
485 	ok				= file										!= 0;
486 
487 	if (ok) ok = fseek(file, node->data, SEEK_SET)				== 0;
488 	if (ok) ok = fread(&temp, 1, 4, file)						== 4; // Chunk size (skip)
489 	if (ok) ok = fread(&temp, 1, 4, file)						== 4; // Stored hash
490 	if (ok) ok = temp											== hash; // Double check
491 	if (ok) ok = fread(&format, 1, 4, file)						== 4;
492 	if (ok) ok = fread(&length, 1, 4, file)						== 4;
493 	if (ok) ok = length											> 0; // sanity check
494 	if (ok) bin = new deUint8[length];
495 	if (ok) ok = fread(bin, 1, length, file)					== (size_t)length;
496 	if (ok) ok = fread(&sourcelength, 1, 4, file)				== 4;
497 	if (ok && sourcelength > 0)
498 	{
499 		source = new char[sourcelength + 1];
500 		ok = fread(source, 1, sourcelength, file)				== (size_t)sourcelength;
501 		source[sourcelength] = 0;
502 		diff = shaderstring != std::string(source);
503 	}
504 	if (!ok || diff)
505 	{
506 		// Mismatch
507 		delete[] source;
508 		delete[] bin;
509 	}
510 	else
511 	{
512 		delete[] source;
513 		if (file) fclose(file);
514 		cacheFileMutex->unlock();
515 		vk::ProgramBinary* res = new vk::ProgramBinary((vk::ProgramFormat)format, length, bin);
516 		delete[] bin;
517 		return res;
518 	}
519 	if (file) fclose(file);
520 	cacheFileMutex->unlock();
521 	return 0;
522 }
523 
shadercacheSave(const vk::ProgramBinary * binary,const std::string & shaderstring,const char * shaderCacheFilename,deUint32 hash)524 void shadercacheSave (const vk::ProgramBinary* binary, const std::string& shaderstring, const char* shaderCacheFilename, deUint32 hash)
525 {
526 	if (binary == 0)
527 		return;
528 	deInt32				format		= binary->getFormat();
529 	deUint32			length		= (deUint32)binary->getSize();
530 	deUint32			chunksize;
531 	deUint32			offset;
532 	const deUint8*		bin			= binary->getBinary();
533 	const de::FilePath	filePath	(shaderCacheFilename);
534 	cacheNode*			node		= 0;
535 
536 	cacheFileMutex->lock();
537 
538 	node = cacheSearch(hash);
539 
540 	if (node)
541 	{
542 		FILE*			file		= fopen(shaderCacheFilename, "rb");
543 		deBool			ok			= (file != 0);
544 		deBool			diff		= DE_TRUE;
545 		deInt32			sourcelength;
546 		deUint32		temp;
547 
548 		deUint32	cachedLength	= 0;
549 
550 		if (ok) ok = fseek(file, node->data, SEEK_SET)				== 0;
551 		if (ok) ok = fread(&temp, 1, 4, file)						== 4; // Chunk size (skip)
552 		if (ok) ok = fread(&temp, 1, 4, file)						== 4; // Stored hash
553 		if (ok) ok = temp											== hash; // Double check
554 		if (ok) ok = fread(&temp, 1, 4, file)						== 4;
555 		if (ok) ok = fread(&cachedLength, 1, 4, file)				== 4;
556 		if (ok) ok = cachedLength									> 0; // sanity check
557 		if (ok) fseek(file, cachedLength, SEEK_CUR); // skip binary
558 		if (ok) ok = fread(&sourcelength, 1, 4, file)				== 4;
559 
560 		if (ok && sourcelength > 0)
561 		{
562 			char* source;
563 			source	= new char[sourcelength + 1];
564 			ok		= fread(source, 1, sourcelength, file)			== (size_t)sourcelength;
565 			source[sourcelength] = 0;
566 			diff	= shaderstring != std::string(source);
567 			delete[] source;
568 		}
569 
570 		if (ok && !diff)
571 		{
572 			// Already in cache (written by another thread, probably)
573 			fclose(file);
574 			cacheFileMutex->unlock();
575 			return;
576 		}
577 		fclose(file);
578 	}
579 
580 	if (!de::FilePath(filePath.getDirName()).exists())
581 		de::createDirectoryAndParents(filePath.getDirName().c_str());
582 
583 	FILE*				file		= fopen(shaderCacheFilename, "ab");
584 	if (!file)
585 	{
586 		cacheFileMutex->unlock();
587 		return;
588 	}
589 	// Append mode starts writing from the end of the file,
590 	// but unless we do a seek, ftell returns 0.
591 	fseek(file, 0, SEEK_END);
592 	offset		= (deUint32)ftell(file);
593 	chunksize	= 4 + 4 + 4 + 4 + length + 4 + (deUint32)shaderstring.length();
594 	fwrite(&chunksize, 1, 4, file);
595 	fwrite(&hash, 1, 4, file);
596 	fwrite(&format, 1, 4, file);
597 	fwrite(&length, 1, 4, file);
598 	fwrite(bin, 1, length, file);
599 	length = (deUint32)shaderstring.length();
600 	fwrite(&length, 1, 4, file);
601 	fwrite(shaderstring.c_str(), 1, length, file);
602 	fclose(file);
603 	cacheInsert(hash, offset);
604 
605 	cacheFileMutex->unlock();
606 }
607 
608 // Insert any information that may affect compilation into the shader string.
getCompileEnvironment(std::string & shaderstring)609 void getCompileEnvironment (std::string& shaderstring)
610 {
611 	shaderstring += "GLSL:";
612 	shaderstring += qpGetReleaseGlslName();
613 	shaderstring += "\nSpir-v Tools:";
614 	shaderstring += qpGetReleaseSpirvToolsName();
615 	shaderstring += "\nSpir-v Headers:";
616 	shaderstring += qpGetReleaseSpirvHeadersName();
617 	shaderstring += "\n";
618 }
619 
620 // Insert compilation options into the shader string.
getBuildOptions(std::string & shaderstring,const ShaderBuildOptions & buildOptions,int optimizationRecipe)621 void getBuildOptions (std::string& shaderstring, const ShaderBuildOptions& buildOptions, int optimizationRecipe)
622 {
623 	shaderstring += "Target Spir-V ";
624 	shaderstring += getSpirvVersionName(buildOptions.targetVersion);
625 	shaderstring += "\n";
626 	if (buildOptions.flags & ShaderBuildOptions::FLAG_ALLOW_RELAXED_OFFSETS)
627 		shaderstring += "Flag:Allow relaxed offsets\n";
628 	if (buildOptions.flags & ShaderBuildOptions::FLAG_USE_STORAGE_BUFFER_STORAGE_CLASS)
629 		shaderstring += "Flag:Use storage buffer storage class\n";
630 	if (optimizationRecipe != 0)
631 	{
632 		shaderstring += "Optimization recipe ";
633 		shaderstring += de::toString(optimizationRecipe);
634 		shaderstring += "\n";
635 	}
636 }
637 
buildProgram(const GlslSource & program,glu::ShaderProgramInfo * buildInfo,const tcu::CommandLine & commandLine)638 ProgramBinary* buildProgram (const GlslSource& program, glu::ShaderProgramInfo* buildInfo, const tcu::CommandLine& commandLine)
639 {
640 	const SpirvVersion	spirvVersion		= program.buildOptions.targetVersion;
641 	const bool			validateBinary		= VALIDATE_BINARIES;
642 	vector<deUint32>	binary;
643 	std::string			cachekey;
644 	std::string			shaderstring;
645 	vk::ProgramBinary*	res					= 0;
646 	const int			optimizationRecipe	= commandLine.getOptimizationRecipe();
647 	deUint32			hash				= 0;
648 
649 	if (commandLine.isShadercacheEnabled())
650 	{
651 		shaderCacheFirstRunCheck(commandLine);
652 		getCompileEnvironment(cachekey);
653 		getBuildOptions(cachekey, program.buildOptions, optimizationRecipe);
654 
655 		for (int i = 0; i < glu::SHADERTYPE_LAST; i++)
656 		{
657 			if (!program.sources[i].empty())
658 			{
659 				cachekey += glu::getShaderTypeName((glu::ShaderType)i);
660 
661 				for (std::vector<std::string>::const_iterator it = program.sources[i].begin(); it != program.sources[i].end(); ++it)
662 					shaderstring += *it;
663 			}
664 		}
665 
666 		cachekey = cachekey + shaderstring;
667 
668 		hash = shadercacheHash(cachekey.c_str());
669 
670 		res = shadercacheLoad(cachekey, commandLine.getShaderCacheFilename(), hash);
671 
672 		if (res)
673 		{
674 			buildInfo->program.infoLog		= "Loaded from cache";
675 			buildInfo->program.linkOk		= true;
676 			buildInfo->program.linkTimeUs	= 0;
677 
678 			for (int shaderType = 0; shaderType < glu::SHADERTYPE_LAST; shaderType++)
679 			{
680 				if (!program.sources[shaderType].empty())
681 				{
682 					glu::ShaderInfo	shaderBuildInfo;
683 
684 					shaderBuildInfo.type			= (glu::ShaderType)shaderType;
685 					shaderBuildInfo.source			= shaderstring;
686 					shaderBuildInfo.compileTimeUs	= 0;
687 					shaderBuildInfo.compileOk		= true;
688 
689 					buildInfo->shaders.push_back(shaderBuildInfo);
690 				}
691 			}
692 		}
693 	}
694 
695 	if (!res)
696 	{
697 		{
698 			vector<deUint32> nonStrippedBinary;
699 
700 			if (!compileGlslToSpirV(program, &nonStrippedBinary, buildInfo))
701 				TCU_THROW(InternalError, "Compiling GLSL to SPIR-V failed");
702 
703 			TCU_CHECK_INTERNAL(!nonStrippedBinary.empty());
704 			stripSpirVDebugInfo(nonStrippedBinary.size(), &nonStrippedBinary[0], &binary);
705 			TCU_CHECK_INTERNAL(!binary.empty());
706 		}
707 
708 		if (optimizationRecipe != 0)
709 		{
710 			validateCompiledBinary(binary, buildInfo, program.buildOptions.getSpirvValidatorOptions());
711 			optimizeCompiledBinary(binary, optimizationRecipe, spirvVersion);
712 		}
713 
714 		if (validateBinary)
715 		{
716 			validateCompiledBinary(binary, buildInfo, program.buildOptions.getSpirvValidatorOptions());
717 		}
718 
719 		res = createProgramBinaryFromSpirV(binary);
720 		if (commandLine.isShadercacheEnabled())
721 			shadercacheSave(res, cachekey, commandLine.getShaderCacheFilename(), hash);
722 	}
723 	return res;
724 }
725 
buildProgram(const HlslSource & program,glu::ShaderProgramInfo * buildInfo,const tcu::CommandLine & commandLine)726 ProgramBinary* buildProgram (const HlslSource& program, glu::ShaderProgramInfo* buildInfo, const tcu::CommandLine& commandLine)
727 {
728 	const SpirvVersion	spirvVersion		= program.buildOptions.targetVersion;
729 	const bool			validateBinary		= VALIDATE_BINARIES;
730 	vector<deUint32>	binary;
731 	std::string			cachekey;
732 	std::string			shaderstring;
733 	vk::ProgramBinary*	res					= 0;
734 	const int			optimizationRecipe	= commandLine.getOptimizationRecipe();
735 	deInt32				hash				= 0;
736 
737 	if (commandLine.isShadercacheEnabled())
738 	{
739 		shaderCacheFirstRunCheck(commandLine);
740 		getCompileEnvironment(cachekey);
741 		getBuildOptions(cachekey, program.buildOptions, optimizationRecipe);
742 
743 		for (int i = 0; i < glu::SHADERTYPE_LAST; i++)
744 		{
745 			if (!program.sources[i].empty())
746 			{
747 				cachekey += glu::getShaderTypeName((glu::ShaderType)i);
748 
749 				for (std::vector<std::string>::const_iterator it = program.sources[i].begin(); it != program.sources[i].end(); ++it)
750 					shaderstring += *it;
751 			}
752 		}
753 
754 		cachekey = cachekey + shaderstring;
755 
756 		hash = shadercacheHash(cachekey.c_str());
757 
758 		res = shadercacheLoad(cachekey, commandLine.getShaderCacheFilename(), hash);
759 
760 		if (res)
761 		{
762 			buildInfo->program.infoLog		= "Loaded from cache";
763 			buildInfo->program.linkOk		= true;
764 			buildInfo->program.linkTimeUs	= 0;
765 
766 			for (int shaderType = 0; shaderType < glu::SHADERTYPE_LAST; shaderType++)
767 			{
768 				if (!program.sources[shaderType].empty())
769 				{
770 					glu::ShaderInfo	shaderBuildInfo;
771 
772 					shaderBuildInfo.type			= (glu::ShaderType)shaderType;
773 					shaderBuildInfo.source			= shaderstring;
774 					shaderBuildInfo.compileTimeUs	= 0;
775 					shaderBuildInfo.compileOk		= true;
776 
777 					buildInfo->shaders.push_back(shaderBuildInfo);
778 				}
779 			}
780 		}
781 	}
782 
783 	if (!res)
784 	{
785 		{
786 			vector<deUint32> nonStrippedBinary;
787 
788 			if (!compileHlslToSpirV(program, &nonStrippedBinary, buildInfo))
789 				TCU_THROW(InternalError, "Compiling HLSL to SPIR-V failed");
790 
791 			TCU_CHECK_INTERNAL(!nonStrippedBinary.empty());
792 			stripSpirVDebugInfo(nonStrippedBinary.size(), &nonStrippedBinary[0], &binary);
793 			TCU_CHECK_INTERNAL(!binary.empty());
794 		}
795 
796 		if (optimizationRecipe != 0)
797 		{
798 			validateCompiledBinary(binary, buildInfo, program.buildOptions.getSpirvValidatorOptions());
799 			optimizeCompiledBinary(binary, optimizationRecipe, spirvVersion);
800 		}
801 
802 		if (validateBinary)
803 		{
804 			validateCompiledBinary(binary, buildInfo, program.buildOptions.getSpirvValidatorOptions());
805 		}
806 
807 		res = createProgramBinaryFromSpirV(binary);
808 		if (commandLine.isShadercacheEnabled())
809 		{
810 			shadercacheSave(res, cachekey, commandLine.getShaderCacheFilename(), hash);
811 		}
812 	}
813 	return res;
814 }
815 
assembleProgram(const SpirVAsmSource & program,SpirVProgramInfo * buildInfo,const tcu::CommandLine & commandLine)816 ProgramBinary* assembleProgram (const SpirVAsmSource& program, SpirVProgramInfo* buildInfo, const tcu::CommandLine& commandLine)
817 {
818 	const SpirvVersion	spirvVersion		= program.buildOptions.targetVersion;
819 	const bool			validateBinary		= VALIDATE_BINARIES;
820 	vector<deUint32>	binary;
821 	vk::ProgramBinary*	res					= 0;
822 	std::string			cachekey;
823 	const int			optimizationRecipe	= commandLine.isSpirvOptimizationEnabled() ? commandLine.getOptimizationRecipe() : 0;
824 	deUint32			hash				= 0;
825 
826 	if (commandLine.isShadercacheEnabled())
827 	{
828 		shaderCacheFirstRunCheck(commandLine);
829 		getCompileEnvironment(cachekey);
830 		cachekey += "Target Spir-V ";
831 		cachekey += getSpirvVersionName(spirvVersion);
832 		cachekey += "\n";
833 		if (optimizationRecipe != 0)
834 		{
835 			cachekey += "Optimization recipe ";
836 			cachekey += de::toString(optimizationRecipe);
837 			cachekey += "\n";
838 		}
839 
840 		cachekey += program.source;
841 
842 		hash = shadercacheHash(cachekey.c_str());
843 
844 		res = shadercacheLoad(cachekey, commandLine.getShaderCacheFilename(), hash);
845 
846 		if (res)
847 		{
848 			buildInfo->source			= program.source;
849 			buildInfo->compileOk		= true;
850 			buildInfo->compileTimeUs	= 0;
851 			buildInfo->infoLog			= "Loaded from cache";
852 		}
853 	}
854 
855 	if (!res)
856 	{
857 
858 		if (!assembleSpirV(&program, &binary, buildInfo, spirvVersion))
859 			TCU_THROW(InternalError, "Failed to assemble SPIR-V");
860 
861 		if (optimizationRecipe != 0)
862 		{
863 			validateCompiledBinary(binary, buildInfo, program.buildOptions.getSpirvValidatorOptions());
864 			optimizeCompiledBinary(binary, optimizationRecipe, spirvVersion);
865 		}
866 
867 		if (validateBinary)
868 		{
869 			validateCompiledBinary(binary, buildInfo, program.buildOptions.getSpirvValidatorOptions());
870 		}
871 
872 		res = createProgramBinaryFromSpirV(binary);
873 		if (commandLine.isShadercacheEnabled())
874 		{
875 			shadercacheSave(res, cachekey, commandLine.getShaderCacheFilename(), hash);
876 		}
877 	}
878 	return res;
879 }
880 
disassembleProgram(const ProgramBinary & program,std::ostream * dst)881 void disassembleProgram (const ProgramBinary& program, std::ostream* dst)
882 {
883 	if (program.getFormat() == PROGRAM_FORMAT_SPIRV)
884 	{
885 		TCU_CHECK_INTERNAL(isSaneSpirVBinary(program));
886 
887 		if (isNativeSpirVBinaryEndianness())
888 			disassembleSpirV(program.getSize()/sizeof(deUint32), (const deUint32*)program.getBinary(), dst,
889 							 extractSpirvVersion(program));
890 		else
891 			TCU_THROW(InternalError, "SPIR-V endianness translation not supported");
892 	}
893 	else
894 		TCU_THROW(NotSupportedError, "Unsupported program format");
895 }
896 
validateProgram(const ProgramBinary & program,std::ostream * dst,const SpirvValidatorOptions & options)897 bool validateProgram (const ProgramBinary& program, std::ostream* dst, const SpirvValidatorOptions& options)
898 {
899 	if (program.getFormat() == PROGRAM_FORMAT_SPIRV)
900 	{
901 		if (!isSaneSpirVBinary(program))
902 		{
903 			*dst << "Binary doesn't look like SPIR-V at all";
904 			return false;
905 		}
906 
907 		if (isNativeSpirVBinaryEndianness())
908 			return validateSpirV(program.getSize()/sizeof(deUint32), (const deUint32*)program.getBinary(), dst, options);
909 		else
910 			TCU_THROW(InternalError, "SPIR-V endianness translation not supported");
911 	}
912 	else
913 		TCU_THROW(NotSupportedError, "Unsupported program format");
914 }
915 
createShaderModule(const DeviceInterface & deviceInterface,VkDevice device,const ProgramBinary & binary,VkShaderModuleCreateFlags flags)916 Move<VkShaderModule> createShaderModule (const DeviceInterface& deviceInterface, VkDevice device, const ProgramBinary& binary, VkShaderModuleCreateFlags flags)
917 {
918 	if (binary.getFormat() == PROGRAM_FORMAT_SPIRV)
919 	{
920 		const struct VkShaderModuleCreateInfo		shaderModuleInfo	=
921 		{
922 			VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
923 			DE_NULL,
924 			flags,
925 			(deUintptr)binary.getSize(),
926 			(const deUint32*)binary.getBinary(),
927 		};
928 
929 		binary.setUsed();
930 
931 		return createShaderModule(deviceInterface, device, &shaderModuleInfo);
932 	}
933 	else
934 		TCU_THROW(NotSupportedError, "Unsupported program format");
935 }
936 
getGluShaderType(VkShaderStageFlagBits shaderStage)937 glu::ShaderType getGluShaderType (VkShaderStageFlagBits shaderStage)
938 {
939 	switch (shaderStage)
940 	{
941 		case VK_SHADER_STAGE_VERTEX_BIT:					return glu::SHADERTYPE_VERTEX;
942 		case VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT:		return glu::SHADERTYPE_TESSELLATION_CONTROL;
943 		case VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT:	return glu::SHADERTYPE_TESSELLATION_EVALUATION;
944 		case VK_SHADER_STAGE_GEOMETRY_BIT:					return glu::SHADERTYPE_GEOMETRY;
945 		case VK_SHADER_STAGE_FRAGMENT_BIT:					return glu::SHADERTYPE_FRAGMENT;
946 		case VK_SHADER_STAGE_COMPUTE_BIT:					return glu::SHADERTYPE_COMPUTE;
947 		default:
948 			DE_FATAL("Unknown shader stage");
949 			return glu::SHADERTYPE_LAST;
950 	}
951 }
952 
getVkShaderStage(glu::ShaderType shaderType)953 VkShaderStageFlagBits getVkShaderStage (glu::ShaderType shaderType)
954 {
955 	static const VkShaderStageFlagBits s_shaderStages[] =
956 	{
957 		VK_SHADER_STAGE_VERTEX_BIT,
958 		VK_SHADER_STAGE_FRAGMENT_BIT,
959 		VK_SHADER_STAGE_GEOMETRY_BIT,
960 		VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT,
961 		VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT,
962 		VK_SHADER_STAGE_COMPUTE_BIT,
963 #ifndef CTS_USES_VULKANSC
964 		VK_SHADER_STAGE_RAYGEN_BIT_NV,
965 		VK_SHADER_STAGE_ANY_HIT_BIT_NV,
966 		VK_SHADER_STAGE_CLOSEST_HIT_BIT_NV,
967 		VK_SHADER_STAGE_MISS_BIT_NV,
968 		VK_SHADER_STAGE_INTERSECTION_BIT_NV,
969 		VK_SHADER_STAGE_CALLABLE_BIT_NV,
970 		VK_SHADER_STAGE_TASK_BIT_NV,
971 		VK_SHADER_STAGE_MESH_BIT_NV,
972 #else // CTS_USES_VULKANSC
973 		(VkShaderStageFlagBits)64u,
974 		(VkShaderStageFlagBits)128u,
975 		(VkShaderStageFlagBits)256u,
976 		(VkShaderStageFlagBits)512u,
977 		(VkShaderStageFlagBits)1024u,
978 		(VkShaderStageFlagBits)2048u,
979 		(VkShaderStageFlagBits)4096u,
980 		(VkShaderStageFlagBits)8192u
981 #endif // CTS_USES_VULKANSC
982 	};
983 
984 	return de::getSizedArrayElement<glu::SHADERTYPE_LAST>(s_shaderStages, shaderType);
985 }
986 
987 // Baseline version, to be used for shaders which don't specify a version
getBaselineSpirvVersion(const deUint32)988 vk::SpirvVersion getBaselineSpirvVersion (const deUint32 /* vulkanVersion */)
989 {
990 	return vk::SPIRV_VERSION_1_0;
991 }
992 
993 // Max supported versions for each Vulkan version, without requiring a Vulkan extension.
getMaxSpirvVersionForVulkan(const deUint32 vulkanVersion)994 vk::SpirvVersion getMaxSpirvVersionForVulkan (const deUint32 vulkanVersion)
995 {
996 	vk::SpirvVersion	result			= vk::SPIRV_VERSION_LAST;
997 
998 	deUint32 vulkanVersionVariantMajorMinor = VK_MAKE_API_VERSION(VK_API_VERSION_VARIANT(vulkanVersion), VK_API_VERSION_MAJOR(vulkanVersion), VK_API_VERSION_MINOR(vulkanVersion), 0);
999 	if (vulkanVersionVariantMajorMinor == VK_API_VERSION_1_0)
1000 		result = vk::SPIRV_VERSION_1_0;
1001 	else if (vulkanVersionVariantMajorMinor == VK_API_VERSION_1_1)
1002 		result = vk::SPIRV_VERSION_1_3;
1003 #ifndef CTS_USES_VULKANSC
1004 	else if (vulkanVersionVariantMajorMinor == VK_API_VERSION_1_2)
1005 		result = vk::SPIRV_VERSION_1_5;
1006 	else if (vulkanVersionVariantMajorMinor >= VK_API_VERSION_1_3)
1007 		result = vk::SPIRV_VERSION_1_6;
1008 #else
1009 	else if (vulkanVersionVariantMajorMinor >= VK_API_VERSION_1_2)
1010 		result = vk::SPIRV_VERSION_1_5;
1011 #endif // CTS_USES_VULKANSC
1012 
1013 	DE_ASSERT(result < vk::SPIRV_VERSION_LAST);
1014 
1015 	return result;
1016 }
1017 
getMaxSpirvVersionForAsm(const deUint32 vulkanVersion)1018 vk::SpirvVersion getMaxSpirvVersionForAsm (const deUint32 vulkanVersion)
1019 {
1020 	return getMaxSpirvVersionForVulkan(vulkanVersion);
1021 }
1022 
getMaxSpirvVersionForGlsl(const deUint32 vulkanVersion)1023 vk::SpirvVersion getMaxSpirvVersionForGlsl (const deUint32 vulkanVersion)
1024 {
1025 	return getMaxSpirvVersionForVulkan(vulkanVersion);
1026 }
1027 
extractSpirvVersion(const ProgramBinary & binary)1028 SpirvVersion extractSpirvVersion (const ProgramBinary& binary)
1029 {
1030 	DE_STATIC_ASSERT(SPIRV_VERSION_1_6 + 1 == SPIRV_VERSION_LAST);
1031 
1032 	if (binary.getFormat() != PROGRAM_FORMAT_SPIRV)
1033 		TCU_THROW(InternalError, "Binary is not in SPIR-V format");
1034 
1035 	if (!isSaneSpirVBinary(binary) || binary.getSize() < sizeof(SpirvBinaryHeader))
1036 		TCU_THROW(InternalError, "Invalid SPIR-V header format");
1037 
1038 	const deUint32				spirvBinaryVersion10	= 0x00010000;
1039 	const deUint32				spirvBinaryVersion11	= 0x00010100;
1040 	const deUint32				spirvBinaryVersion12	= 0x00010200;
1041 	const deUint32				spirvBinaryVersion13	= 0x00010300;
1042 	const deUint32				spirvBinaryVersion14	= 0x00010400;
1043 	const deUint32				spirvBinaryVersion15	= 0x00010500;
1044 	const deUint32				spirvBinaryVersion16	= 0x00010600;
1045 	const SpirvBinaryHeader*	header					= reinterpret_cast<const SpirvBinaryHeader*>(binary.getBinary());
1046 	const deUint32				spirvVersion			= isNativeSpirVBinaryEndianness()
1047 														? header->version
1048 														: deReverseBytes32(header->version);
1049 	SpirvVersion				result					= SPIRV_VERSION_LAST;
1050 
1051 	switch (spirvVersion)
1052 	{
1053 		case spirvBinaryVersion10:	result = SPIRV_VERSION_1_0; break; //!< SPIR-V 1.0
1054 		case spirvBinaryVersion11:	result = SPIRV_VERSION_1_1; break; //!< SPIR-V 1.1
1055 		case spirvBinaryVersion12:	result = SPIRV_VERSION_1_2; break; //!< SPIR-V 1.2
1056 		case spirvBinaryVersion13:	result = SPIRV_VERSION_1_3; break; //!< SPIR-V 1.3
1057 		case spirvBinaryVersion14:	result = SPIRV_VERSION_1_4; break; //!< SPIR-V 1.4
1058 		case spirvBinaryVersion15:	result = SPIRV_VERSION_1_5; break; //!< SPIR-V 1.5
1059 		case spirvBinaryVersion16:	result = SPIRV_VERSION_1_6; break; //!< SPIR-V 1.6
1060 		default:					TCU_THROW(InternalError, "Unknown SPIR-V version detected in binary");
1061 	}
1062 
1063 	return result;
1064 }
1065 
getSpirvVersionName(const SpirvVersion spirvVersion)1066 std::string getSpirvVersionName (const SpirvVersion spirvVersion)
1067 {
1068 	DE_STATIC_ASSERT(SPIRV_VERSION_1_6 + 1 == SPIRV_VERSION_LAST);
1069 	DE_ASSERT(spirvVersion < SPIRV_VERSION_LAST);
1070 
1071 	std::string result;
1072 
1073 	switch (spirvVersion)
1074 	{
1075 		case SPIRV_VERSION_1_0: result = "1.0"; break; //!< SPIR-V 1.0
1076 		case SPIRV_VERSION_1_1: result = "1.1"; break; //!< SPIR-V 1.1
1077 		case SPIRV_VERSION_1_2: result = "1.2"; break; //!< SPIR-V 1.2
1078 		case SPIRV_VERSION_1_3: result = "1.3"; break; //!< SPIR-V 1.3
1079 		case SPIRV_VERSION_1_4: result = "1.4"; break; //!< SPIR-V 1.4
1080 		case SPIRV_VERSION_1_5: result = "1.5"; break; //!< SPIR-V 1.5
1081 		case SPIRV_VERSION_1_6: result = "1.6"; break; //!< SPIR-V 1.6
1082 		default:				result = "Unknown";
1083 	}
1084 
1085 	return result;
1086 }
1087 
operator ++(SpirvVersion & spirvVersion)1088 SpirvVersion& operator++(SpirvVersion& spirvVersion)
1089 {
1090 	if (spirvVersion == SPIRV_VERSION_LAST)
1091 		spirvVersion = SPIRV_VERSION_1_0;
1092 	else
1093 		spirvVersion = static_cast<SpirvVersion>(static_cast<deUint32>(spirvVersion) + 1);
1094 
1095 	return spirvVersion;
1096 }
1097 
1098 } // vk
1099