1/*-------------------------------------------------------------------------
2 * Vulkan Conformance Tests
3 * ------------------------
4 *
5 * Copyright (c) 2016 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 Utility for pre-compiling source programs to SPIR-V
22 *//*--------------------------------------------------------------------*/
23
24#include "tcuDefs.hpp"
25#include "tcuCommandLine.hpp"
26#include "tcuPlatform.hpp"
27#include "tcuResource.hpp"
28#include "tcuTestLog.hpp"
29#include "tcuTestHierarchyIterator.hpp"
30#include "deUniquePtr.hpp"
31#include "vkPrograms.hpp"
32#include "vkBinaryRegistry.hpp"
33#include "vktTestCase.hpp"
34#include "vktTestPackage.hpp"
35#include "deUniquePtr.hpp"
36#include "deCommandLine.hpp"
37#include "deSharedPtr.hpp"
38#include "deThread.hpp"
39#include "deThreadSafeRingBuffer.hpp"
40#include "dePoolArray.hpp"
41
42#include <iostream>
43
44using std::vector;
45using std::string;
46using de::UniquePtr;
47using de::MovePtr;
48using de::SharedPtr;
49
50namespace vkt
51{
52
53namespace // anonymous
54{
55
56typedef de::SharedPtr<glu::ProgramSources>	ProgramSourcesSp;
57typedef de::SharedPtr<vk::SpirVAsmSource>	SpirVAsmSourceSp;
58typedef de::SharedPtr<vk::ProgramBinary>	ProgramBinarySp;
59
60class Task
61{
62public:
63	virtual void	execute		(void) = 0;
64};
65
66typedef de::ThreadSafeRingBuffer<Task*>	TaskQueue;
67
68class TaskExecutorThread : public de::Thread
69{
70public:
71	TaskExecutorThread (TaskQueue& tasks)
72		: m_tasks(tasks)
73	{
74		start();
75	}
76
77	void run (void)
78	{
79		for (;;)
80		{
81			Task* const	task	= m_tasks.popBack();
82
83			if (task)
84				task->execute();
85			else
86				break; // End of tasks - time to terminate
87		}
88	}
89
90private:
91	TaskQueue&	m_tasks;
92};
93
94class TaskExecutor
95{
96public:
97								TaskExecutor		(deUint32 numThreads);
98								~TaskExecutor		(void);
99
100	void						submit				(Task* task);
101	void						waitForComplete		(void);
102
103private:
104	typedef de::SharedPtr<TaskExecutorThread>	ExecThreadSp;
105
106	std::vector<ExecThreadSp>	m_threads;
107	TaskQueue					m_tasks;
108};
109
110TaskExecutor::TaskExecutor (deUint32 numThreads)
111	: m_threads	(numThreads)
112	, m_tasks	(m_threads.size() * 1024u)
113{
114	for (size_t ndx = 0; ndx < m_threads.size(); ++ndx)
115		m_threads[ndx] = ExecThreadSp(new TaskExecutorThread(m_tasks));
116}
117
118TaskExecutor::~TaskExecutor (void)
119{
120	for (size_t ndx = 0; ndx < m_threads.size(); ++ndx)
121		m_tasks.pushFront(DE_NULL);
122
123	for (size_t ndx = 0; ndx < m_threads.size(); ++ndx)
124		m_threads[ndx]->join();
125}
126
127void TaskExecutor::submit (Task* task)
128{
129	DE_ASSERT(task);
130	m_tasks.pushFront(task);
131}
132
133class SyncTask : public Task
134{
135public:
136	SyncTask (de::Semaphore* enterBarrier, de::Semaphore* inBarrier, de::Semaphore* leaveBarrier)
137		: m_enterBarrier	(enterBarrier)
138		, m_inBarrier		(inBarrier)
139		, m_leaveBarrier	(leaveBarrier)
140	{}
141
142	SyncTask (void)
143		: m_enterBarrier	(DE_NULL)
144		, m_inBarrier		(DE_NULL)
145		, m_leaveBarrier	(DE_NULL)
146	{}
147
148	void execute (void)
149	{
150		m_enterBarrier->increment();
151		m_inBarrier->decrement();
152		m_leaveBarrier->increment();
153	}
154
155private:
156	de::Semaphore*	m_enterBarrier;
157	de::Semaphore*	m_inBarrier;
158	de::Semaphore*	m_leaveBarrier;
159};
160
161void TaskExecutor::waitForComplete (void)
162{
163	de::Semaphore			enterBarrier	(0);
164	de::Semaphore			inBarrier		(0);
165	de::Semaphore			leaveBarrier	(0);
166	std::vector<SyncTask>	syncTasks		(m_threads.size());
167
168	for (size_t ndx = 0; ndx < m_threads.size(); ++ndx)
169	{
170		syncTasks[ndx] = SyncTask(&enterBarrier, &inBarrier, &leaveBarrier);
171		submit(&syncTasks[ndx]);
172	}
173
174	for (size_t ndx = 0; ndx < m_threads.size(); ++ndx)
175		enterBarrier.decrement();
176
177	for (size_t ndx = 0; ndx < m_threads.size(); ++ndx)
178		inBarrier.increment();
179
180	for (size_t ndx = 0; ndx < m_threads.size(); ++ndx)
181		leaveBarrier.decrement();
182}
183
184struct Program
185{
186	enum Status
187	{
188		STATUS_NOT_COMPLETED = 0,
189		STATUS_FAILED,
190		STATUS_PASSED,
191
192		STATUS_LAST
193	};
194
195	vk::ProgramIdentifier	id;
196
197	Status					buildStatus;
198	std::string				buildLog;
199	ProgramBinarySp			binary;
200
201	Status					validationStatus;
202	std::string				validationLog;
203
204	vk::SpirvValidatorOptions	validatorOptions;
205
206	explicit				Program		(const vk::ProgramIdentifier& id_, const vk::SpirvValidatorOptions& valOptions_)
207								: id				(id_)
208								, buildStatus		(STATUS_NOT_COMPLETED)
209								, validationStatus	(STATUS_NOT_COMPLETED)
210								, validatorOptions	(valOptions_)
211							{}
212							Program		(void)
213								: id				("", "")
214								, buildStatus		(STATUS_NOT_COMPLETED)
215								, validationStatus	(STATUS_NOT_COMPLETED)
216								, validatorOptions()
217							{}
218};
219
220void writeBuildLogs (const glu::ShaderProgramInfo& buildInfo, std::ostream& dst)
221{
222	for (size_t shaderNdx = 0; shaderNdx < buildInfo.shaders.size(); shaderNdx++)
223	{
224		const glu::ShaderInfo&	shaderInfo	= buildInfo.shaders[shaderNdx];
225		const char* const		shaderName	= getShaderTypeName(shaderInfo.type);
226
227		dst << shaderName << " source:\n"
228			<< "---\n"
229			<< shaderInfo.source << "\n"
230			<< "---\n"
231			<< shaderName << " compile log:\n"
232			<< "---\n"
233			<< shaderInfo.infoLog << "\n"
234			<< "---\n";
235	}
236
237	dst << "link log:\n"
238		<< "---\n"
239		<< buildInfo.program.infoLog << "\n"
240		<< "---\n";
241}
242
243template <typename Source>
244class BuildHighLevelShaderTask : public Task
245{
246public:
247
248	BuildHighLevelShaderTask (const Source& source, Program* program)
249		: m_source		(source)
250		, m_program		(program)
251		, m_commandLine	(0)
252	{}
253
254	BuildHighLevelShaderTask (void) : m_program(DE_NULL) {}
255
256	void setCommandline (const tcu::CommandLine &commandLine)
257	{
258		m_commandLine = &commandLine;
259	}
260
261	void execute (void)
262	{
263		glu::ShaderProgramInfo buildInfo;
264
265		try
266		{
267			DE_ASSERT(m_source.buildOptions.targetVersion < vk::SPIRV_VERSION_LAST);
268			DE_ASSERT(m_commandLine != DE_NULL);
269			m_program->binary			= ProgramBinarySp(vk::buildProgram(m_source, &buildInfo, *m_commandLine));
270			m_program->buildStatus		= Program::STATUS_PASSED;
271			m_program->validatorOptions	= m_source.buildOptions.getSpirvValidatorOptions();
272		}
273		catch (const tcu::Exception&)
274		{
275			std::ostringstream log;
276
277			writeBuildLogs(buildInfo, log);
278
279			m_program->buildStatus	= Program::STATUS_FAILED;
280			m_program->buildLog		= log.str();
281		}
282	}
283
284private:
285	Source					m_source;
286	Program*				m_program;
287	const tcu::CommandLine*	m_commandLine;
288};
289
290void writeBuildLogs (const vk::SpirVProgramInfo& buildInfo, std::ostream& dst)
291{
292	dst << "source:\n"
293		<< "---\n"
294		<< buildInfo.source << "\n"
295		<< "---\n"
296		<< buildInfo.infoLog << "\n"
297		<< "---\n";
298}
299
300class BuildSpirVAsmTask : public Task
301{
302public:
303	BuildSpirVAsmTask (const vk::SpirVAsmSource& source, Program* program)
304		: m_source		(source)
305		, m_program		(program)
306		, m_commandLine	(0)
307	{}
308
309	BuildSpirVAsmTask (void) : m_program(DE_NULL), m_commandLine(0) {}
310
311	void setCommandline (const tcu::CommandLine &commandLine)
312	{
313		m_commandLine = &commandLine;
314	}
315
316	void execute (void)
317	{
318		vk::SpirVProgramInfo buildInfo;
319
320		try
321		{
322			DE_ASSERT(m_source.buildOptions.targetVersion < vk::SPIRV_VERSION_LAST);
323			DE_ASSERT(m_commandLine != DE_NULL);
324			m_program->binary		= ProgramBinarySp(vk::assembleProgram(m_source, &buildInfo, *m_commandLine));
325			m_program->buildStatus	= Program::STATUS_PASSED;
326		}
327		catch (const tcu::Exception&)
328		{
329			std::ostringstream log;
330
331			writeBuildLogs(buildInfo, log);
332
333			m_program->buildStatus	= Program::STATUS_FAILED;
334			m_program->buildLog		= log.str();
335		}
336	}
337
338private:
339	vk::SpirVAsmSource		m_source;
340	Program*				m_program;
341	const tcu::CommandLine*	m_commandLine;
342};
343
344class ValidateBinaryTask : public Task
345{
346public:
347	ValidateBinaryTask (Program* program)
348		: m_program(program)
349	{}
350
351	void execute (void)
352	{
353		DE_ASSERT(m_program->buildStatus == Program::STATUS_PASSED);
354		DE_ASSERT(m_program->binary->getFormat() == vk::PROGRAM_FORMAT_SPIRV);
355
356		std::ostringstream			validationLogStream;
357
358		if (vk::validateProgram(*m_program->binary, &validationLogStream, m_program->validatorOptions))
359			m_program->validationStatus = Program::STATUS_PASSED;
360		else
361			m_program->validationStatus = Program::STATUS_FAILED;
362		m_program->validationLog = validationLogStream.str();
363	}
364
365private:
366	Program*	m_program;
367};
368
369tcu::TestPackageRoot* createRoot (tcu::TestContext& testCtx)
370{
371	vector<tcu::TestNode*>	children;
372	children.push_back(new TestPackage(testCtx));
373	return new tcu::TestPackageRoot(testCtx, children);
374}
375
376} // anonymous
377
378struct BuildStats
379{
380	int		numSucceeded;
381	int		numFailed;
382	int		notSupported;
383
384	BuildStats (void)
385		: numSucceeded	(0)
386		, numFailed		(0)
387		, notSupported	(0)
388	{
389	}
390};
391
392BuildStats buildPrograms (tcu::TestContext&			testCtx,
393						  const std::string&		dstPath,
394						  const bool				validateBinaries,
395						  const deUint32			usedVulkanVersion,
396						  const vk::SpirvVersion	baselineSpirvVersion,
397						  const vk::SpirvVersion	maxSpirvVersion,
398						  const bool				allowSpirV14)
399{
400	const deUint32						numThreads			= deGetNumAvailableLogicalCores();
401
402	TaskExecutor						executor			(numThreads);
403
404	// de::PoolArray<> is faster to build than std::vector
405	de::MemPool							programPool;
406	de::PoolArray<Program>				programs			(&programPool);
407	int									notSupported		= 0;
408
409	{
410		de::MemPool							tmpPool;
411		de::PoolArray<BuildHighLevelShaderTask<vk::GlslSource> >	buildGlslTasks		(&tmpPool);
412		de::PoolArray<BuildHighLevelShaderTask<vk::HlslSource> >	buildHlslTasks		(&tmpPool);
413		de::PoolArray<BuildSpirVAsmTask>	buildSpirvAsmTasks	(&tmpPool);
414
415		// Collect build tasks
416		{
417			const UniquePtr<tcu::TestPackageRoot>	root			(createRoot(testCtx));
418			tcu::DefaultHierarchyInflater			inflater		(testCtx);
419			de::MovePtr<tcu::CaseListFilter>		caseListFilter	(testCtx.getCommandLine().createCaseListFilter(testCtx.getArchive()));
420			tcu::TestHierarchyIterator				iterator		(*root, inflater, *caseListFilter);
421
422			while (iterator.getState() != tcu::TestHierarchyIterator::STATE_FINISHED)
423			{
424				if (iterator.getState() == tcu::TestHierarchyIterator::STATE_ENTER_NODE &&
425					tcu::isTestNodeTypeExecutable(iterator.getNode()->getNodeType()))
426				{
427					TestCase* const				testCase					= dynamic_cast<TestCase*>(iterator.getNode());
428					const string				casePath					= iterator.getNodePath();
429					vk::ShaderBuildOptions		defaultGlslBuildOptions		(usedVulkanVersion, baselineSpirvVersion, 0u);
430					vk::ShaderBuildOptions		defaultHlslBuildOptions		(usedVulkanVersion, baselineSpirvVersion, 0u);
431					vk::SpirVAsmBuildOptions	defaultSpirvAsmBuildOptions	(usedVulkanVersion, baselineSpirvVersion);
432					vk::SourceCollections		sourcePrograms				(usedVulkanVersion, defaultGlslBuildOptions, defaultHlslBuildOptions, defaultSpirvAsmBuildOptions);
433
434					try
435					{
436						testCase->delayedInit();
437						testCase->initPrograms(sourcePrograms);
438					}
439					catch (const tcu::NotSupportedError& )
440					{
441						notSupported++;
442						iterator.next();
443						continue;
444					}
445
446					for (vk::GlslSourceCollection::Iterator progIter = sourcePrograms.glslSources.begin();
447						 progIter != sourcePrograms.glslSources.end();
448						 ++progIter)
449					{
450						// Source program requires higher SPIR-V version than available: skip it to avoid fail
451						// Unless this is SPIR-V 1.4 and is explicitly allowed.
452						if (progIter.getProgram().buildOptions.targetVersion > maxSpirvVersion && !(allowSpirV14 && progIter.getProgram().buildOptions.supports_VK_KHR_spirv_1_4 && progIter.getProgram().buildOptions.targetVersion == vk::SPIRV_VERSION_1_4))
453							continue;
454
455						programs.pushBack(Program(vk::ProgramIdentifier(casePath, progIter.getName()), progIter.getProgram().buildOptions.getSpirvValidatorOptions()));
456						buildGlslTasks.pushBack(BuildHighLevelShaderTask<vk::GlslSource>(progIter.getProgram(), &programs.back()));
457						buildGlslTasks.back().setCommandline(testCtx.getCommandLine());
458						executor.submit(&buildGlslTasks.back());
459					}
460
461					for (vk::HlslSourceCollection::Iterator progIter = sourcePrograms.hlslSources.begin();
462						 progIter != sourcePrograms.hlslSources.end();
463						 ++progIter)
464					{
465						// Source program requires higher SPIR-V version than available: skip it to avoid fail
466						// Unless this is SPIR-V 1.4 and is explicitly allowed.
467						if (progIter.getProgram().buildOptions.targetVersion > maxSpirvVersion && !(allowSpirV14 && progIter.getProgram().buildOptions.supports_VK_KHR_spirv_1_4 && progIter.getProgram().buildOptions.targetVersion == vk::SPIRV_VERSION_1_4))
468							continue;
469
470						programs.pushBack(Program(vk::ProgramIdentifier(casePath, progIter.getName()), progIter.getProgram().buildOptions.getSpirvValidatorOptions()));
471						buildHlslTasks.pushBack(BuildHighLevelShaderTask<vk::HlslSource>(progIter.getProgram(), &programs.back()));
472						buildHlslTasks.back().setCommandline(testCtx.getCommandLine());
473						executor.submit(&buildHlslTasks.back());
474					}
475
476					for (vk::SpirVAsmCollection::Iterator progIter = sourcePrograms.spirvAsmSources.begin();
477						 progIter != sourcePrograms.spirvAsmSources.end();
478						 ++progIter)
479					{
480						// Source program requires higher SPIR-V version than available: skip it to avoid fail
481						// Unless this is SPIR-V 1.4 and is explicitly allowed.
482						if (progIter.getProgram().buildOptions.targetVersion > maxSpirvVersion && !(allowSpirV14 && progIter.getProgram().buildOptions.supports_VK_KHR_spirv_1_4 && progIter.getProgram().buildOptions.targetVersion == vk::SPIRV_VERSION_1_4))
483							continue;
484
485						programs.pushBack(Program(vk::ProgramIdentifier(casePath, progIter.getName()), progIter.getProgram().buildOptions.getSpirvValidatorOptions()));
486						buildSpirvAsmTasks.pushBack(BuildSpirVAsmTask(progIter.getProgram(), &programs.back()));
487						buildSpirvAsmTasks.back().setCommandline(testCtx.getCommandLine());
488						executor.submit(&buildSpirvAsmTasks.back());
489					}
490				}
491
492				iterator.next();
493			}
494		}
495
496		// Need to wait until tasks completed before freeing task memory
497		executor.waitForComplete();
498	}
499
500	if (validateBinaries)
501	{
502		std::vector<ValidateBinaryTask>	validationTasks;
503
504		validationTasks.reserve(programs.size());
505
506		for (de::PoolArray<Program>::iterator progIter = programs.begin(); progIter != programs.end(); ++progIter)
507		{
508			if (progIter->buildStatus == Program::STATUS_PASSED)
509			{
510				validationTasks.push_back(ValidateBinaryTask(&*progIter));
511				executor.submit(&validationTasks.back());
512			}
513		}
514
515		executor.waitForComplete();
516	}
517
518	{
519		vk::BinaryRegistryWriter	registryWriter		(dstPath);
520
521		for (de::PoolArray<Program>::iterator progIter = programs.begin(); progIter != programs.end(); ++progIter)
522		{
523			if (progIter->buildStatus == Program::STATUS_PASSED)
524				registryWriter.addProgram(progIter->id, *progIter->binary);
525		}
526
527		registryWriter.write();
528	}
529
530	{
531		BuildStats	stats;
532		stats.notSupported = notSupported;
533		for (de::PoolArray<Program>::iterator progIter = programs.begin(); progIter != programs.end(); ++progIter)
534		{
535			const bool	buildOk			= progIter->buildStatus == Program::STATUS_PASSED;
536			const bool	validationOk	= progIter->validationStatus != Program::STATUS_FAILED;
537
538			if (buildOk && validationOk)
539				stats.numSucceeded += 1;
540			else
541			{
542				stats.numFailed += 1;
543				tcu::print("ERROR: %s / %s: %s failed\n",
544						   progIter->id.testCasePath.c_str(),
545						   progIter->id.programName.c_str(),
546						   (buildOk ? "validation" : "build"));
547				tcu::print("%s\n", (buildOk ? progIter->validationLog.c_str() : progIter->buildLog.c_str()));
548			}
549		}
550
551		return stats;
552	}
553}
554
555} // vkt
556
557namespace opt
558{
559
560DE_DECLARE_COMMAND_LINE_OPT(DstPath,				std::string);
561DE_DECLARE_COMMAND_LINE_OPT(Cases,					std::string);
562DE_DECLARE_COMMAND_LINE_OPT(Validate,				bool);
563DE_DECLARE_COMMAND_LINE_OPT(VulkanVersion,			deUint32);
564DE_DECLARE_COMMAND_LINE_OPT(ShaderCache,			bool);
565DE_DECLARE_COMMAND_LINE_OPT(ShaderCacheFilename,	std::string);
566DE_DECLARE_COMMAND_LINE_OPT(ShaderCacheTruncate,	bool);
567DE_DECLARE_COMMAND_LINE_OPT(SpirvOptimize,			bool);
568DE_DECLARE_COMMAND_LINE_OPT(SpirvOptimizationRecipe,std::string);
569DE_DECLARE_COMMAND_LINE_OPT(SpirvAllow14,			bool);
570
571static const de::cmdline::NamedValue<bool> s_enableNames[] =
572{
573	{ "enable",		true },
574	{ "disable",	false }
575};
576
577void registerOptions (de::cmdline::Parser& parser)
578{
579	using de::cmdline::Option;
580	using de::cmdline::NamedValue;
581
582	static const NamedValue<deUint32> s_vulkanVersion[] =
583	{
584		{ "1.0",	VK_MAKE_API_VERSION(0, 1, 0, 0)	},
585		{ "1.1",	VK_MAKE_API_VERSION(0, 1, 1, 0)	},
586		{ "1.2",	VK_MAKE_API_VERSION(0, 1, 2, 0)	},
587		{ "1.3",	VK_MAKE_API_VERSION(0, 1, 3, 0)	},
588	};
589
590	DE_STATIC_ASSERT(vk::SPIRV_VERSION_1_6 + 1 == vk::SPIRV_VERSION_LAST);
591
592	parser << Option<opt::DstPath>("d", "dst-path", "Destination path", "out")
593		<< Option<opt::Cases>("n", "deqp-case", "Case path filter (works as in test binaries)")
594		<< Option<opt::Validate>("v", "validate-spv", "Validate generated SPIR-V binaries")
595		<< Option<opt::VulkanVersion>("t", "target-vulkan-version", "Target Vulkan version", s_vulkanVersion, "1.2")
596		<< Option<opt::ShaderCache>("s", "shadercache", "Enable or disable shader cache", s_enableNames, "enable")
597		<< Option<opt::ShaderCacheFilename>("r", "shadercache-filename", "Write shader cache to given file", "shadercache.bin")
598		<< Option<opt::ShaderCacheTruncate>("x", "shadercache-truncate", "Truncate shader cache before running", s_enableNames, "enable")
599		<< Option<opt::SpirvOptimize>("o", "deqp-optimize-spirv", "Enable optimization for SPIR-V", s_enableNames, "disable")
600		<< Option<opt::SpirvOptimizationRecipe>("p","deqp-optimization-recipe", "Shader optimization recipe")
601		<< Option<opt::SpirvAllow14>("e","allow-spirv-14", "Allow SPIR-V 1.4 with Vulkan 1.1");
602}
603
604} // opt
605
606int main (int argc, const char* argv[])
607{
608	de::cmdline::CommandLine	cmdLine;
609	tcu::CommandLine			deqpCmdLine;
610
611	{
612		de::cmdline::Parser		parser;
613		opt::registerOptions(parser);
614		if (!parser.parse(argc, argv, &cmdLine, std::cerr))
615		{
616			parser.help(std::cout);
617			return -1;
618		}
619	}
620
621	{
622		vector<const char*> deqpArgv;
623
624		deqpArgv.push_back("unused");
625
626		if (cmdLine.hasOption<opt::Cases>())
627		{
628			deqpArgv.push_back("--deqp-case");
629			deqpArgv.push_back(cmdLine.getOption<opt::Cases>().c_str());
630		}
631
632		if (cmdLine.hasOption<opt::ShaderCacheFilename>())
633		{
634			deqpArgv.push_back("--deqp-shadercache-filename");
635			deqpArgv.push_back(cmdLine.getOption<opt::ShaderCacheFilename>().c_str());
636		}
637
638		if (cmdLine.hasOption<opt::ShaderCache>())
639		{
640			deqpArgv.push_back("--deqp-shadercache");
641			if (cmdLine.getOption<opt::ShaderCache>())
642				deqpArgv.push_back("enable");
643			else
644				deqpArgv.push_back("disable");
645		}
646
647		if (cmdLine.hasOption<opt::ShaderCacheTruncate>())
648		{
649			deqpArgv.push_back("--deqp-shadercache-truncate");
650			if (cmdLine.getOption<opt::ShaderCacheTruncate>())
651				deqpArgv.push_back("enable");
652			else
653				deqpArgv.push_back("disable");
654		}
655
656		if (cmdLine.hasOption<opt::SpirvOptimize>())
657		{
658			deqpArgv.push_back("--deqp-optimize-spirv");
659			if (cmdLine.getOption<opt::SpirvOptimize>())
660				deqpArgv.push_back("enable");
661			 else
662				deqpArgv.push_back("disable");
663		}
664
665		if (cmdLine.hasOption<opt::SpirvOptimizationRecipe>())
666		{
667			deqpArgv.push_back("--deqp-optimization-recipe");
668			deqpArgv.push_back(cmdLine.getOption<opt::SpirvOptimizationRecipe>().c_str());
669		}
670
671		if (!deqpCmdLine.parse((int)deqpArgv.size(), &deqpArgv[0]))
672			return -1;
673	}
674
675	try
676	{
677		tcu::DirArchive			archive					(".");
678		tcu::TestLog			log						(deqpCmdLine.getLogFileName(), deqpCmdLine.getLogFlags());
679		tcu::Platform			platform;
680		tcu::TestContext		testCtx					(platform, archive, log, deqpCmdLine, DE_NULL);
681		vk::SpirvVersion		baselineSpirvVersion	= vk::getBaselineSpirvVersion(cmdLine.getOption<opt::VulkanVersion>());
682		vk::SpirvVersion		maxSpirvVersion			= vk::getMaxSpirvVersionForGlsl(cmdLine.getOption<opt::VulkanVersion>());
683
684		testCtx.writeSessionInfo();
685
686		tcu::print("SPIR-V versions: baseline: %s, max supported: %s\n",
687					getSpirvVersionName(baselineSpirvVersion).c_str(),
688					getSpirvVersionName(maxSpirvVersion).c_str());
689
690		const vkt::BuildStats	stats		= vkt::buildPrograms(testCtx,
691																 cmdLine.getOption<opt::DstPath>(),
692																 cmdLine.getOption<opt::Validate>(),
693																 cmdLine.getOption<opt::VulkanVersion>(),
694																 baselineSpirvVersion,
695																 maxSpirvVersion,
696																 cmdLine.getOption<opt::SpirvAllow14>());
697
698		tcu::print("DONE: %d passed, %d failed, %d not supported\n", stats.numSucceeded, stats.numFailed, stats.notSupported);
699
700		return stats.numFailed == 0 ? 0 : -1;
701	}
702	catch (const std::exception& e)
703	{
704		tcu::die("%s", e.what());
705	}
706}
707