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 
44 using std::vector;
45 using std::string;
46 using de::UniquePtr;
47 using de::MovePtr;
48 using de::SharedPtr;
49 
50 namespace vkt
51 {
52 
53 namespace // anonymous
54 {
55 
56 typedef de::SharedPtr<glu::ProgramSources>	ProgramSourcesSp;
57 typedef de::SharedPtr<vk::SpirVAsmSource>	SpirVAsmSourceSp;
58 typedef de::SharedPtr<vk::ProgramBinary>	ProgramBinarySp;
59 
60 class Task
61 {
62 public:
63 	virtual void	execute		(void) = 0;
64 };
65 
66 typedef de::ThreadSafeRingBuffer<Task*>	TaskQueue;
67 
68 class TaskExecutorThread : public de::Thread
69 {
70 public:
TaskExecutorThread(TaskQueue & tasks)71 	TaskExecutorThread (TaskQueue& tasks)
72 		: m_tasks(tasks)
73 	{
74 		start();
75 	}
76 
run(void)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 
90 private:
91 	TaskQueue&	m_tasks;
92 };
93 
94 class TaskExecutor
95 {
96 public:
97 								TaskExecutor		(deUint32 numThreads);
98 								~TaskExecutor		(void);
99 
100 	void						submit				(Task* task);
101 	void						waitForComplete		(void);
102 
103 private:
104 	typedef de::SharedPtr<TaskExecutorThread>	ExecThreadSp;
105 
106 	std::vector<ExecThreadSp>	m_threads;
107 	TaskQueue					m_tasks;
108 };
109 
TaskExecutor(deUint32 numThreads)110 TaskExecutor::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 
~TaskExecutor(void)118 TaskExecutor::~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 
submit(Task * task)127 void TaskExecutor::submit (Task* task)
128 {
129 	DE_ASSERT(task);
130 	m_tasks.pushFront(task);
131 }
132 
133 class SyncTask : public Task
134 {
135 public:
SyncTask(de::Semaphore * enterBarrier,de::Semaphore * inBarrier,de::Semaphore * leaveBarrier)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 
SyncTask(void)142 	SyncTask (void)
143 		: m_enterBarrier	(DE_NULL)
144 		, m_inBarrier		(DE_NULL)
145 		, m_leaveBarrier	(DE_NULL)
146 	{}
147 
execute(void)148 	void execute (void)
149 	{
150 		m_enterBarrier->increment();
151 		m_inBarrier->decrement();
152 		m_leaveBarrier->increment();
153 	}
154 
155 private:
156 	de::Semaphore*	m_enterBarrier;
157 	de::Semaphore*	m_inBarrier;
158 	de::Semaphore*	m_leaveBarrier;
159 };
160 
waitForComplete(void)161 void 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 
184 struct 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 
Programvkt::__anonb641954f0111::Program206 	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 							{}
Programvkt::__anonb641954f0111::Program212 							Program		(void)
213 								: id				("", "")
214 								, buildStatus		(STATUS_NOT_COMPLETED)
215 								, validationStatus	(STATUS_NOT_COMPLETED)
216 								, validatorOptions()
217 							{}
218 };
219 
writeBuildLogs(const glu::ShaderProgramInfo & buildInfo,std::ostream & dst)220 void 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 
243 template <typename Source>
244 class BuildHighLevelShaderTask : public Task
245 {
246 public:
247 
BuildHighLevelShaderTask(const Source & source,Program * program)248 	BuildHighLevelShaderTask (const Source& source, Program* program)
249 		: m_source		(source)
250 		, m_program		(program)
251 		, m_commandLine	(0)
252 	{}
253 
BuildHighLevelShaderTask(void)254 	BuildHighLevelShaderTask (void) : m_program(DE_NULL) {}
255 
setCommandline(const tcu::CommandLine & commandLine)256 	void setCommandline (const tcu::CommandLine &commandLine)
257 	{
258 		m_commandLine = &commandLine;
259 	}
260 
execute(void)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 
284 private:
285 	Source					m_source;
286 	Program*				m_program;
287 	const tcu::CommandLine*	m_commandLine;
288 };
289 
writeBuildLogs(const vk::SpirVProgramInfo & buildInfo,std::ostream & dst)290 void 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 
300 class BuildSpirVAsmTask : public Task
301 {
302 public:
BuildSpirVAsmTask(const vk::SpirVAsmSource & source,Program * program)303 	BuildSpirVAsmTask (const vk::SpirVAsmSource& source, Program* program)
304 		: m_source		(source)
305 		, m_program		(program)
306 		, m_commandLine	(0)
307 	{}
308 
BuildSpirVAsmTask(void)309 	BuildSpirVAsmTask (void) : m_program(DE_NULL) {}
310 
setCommandline(const tcu::CommandLine & commandLine)311 	void setCommandline (const tcu::CommandLine &commandLine)
312 	{
313 		m_commandLine = &commandLine;
314 	}
315 
execute(void)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 
338 private:
339 	vk::SpirVAsmSource		m_source;
340 	Program*				m_program;
341 	const tcu::CommandLine*	m_commandLine;
342 };
343 
344 class ValidateBinaryTask : public Task
345 {
346 public:
ValidateBinaryTask(Program * program)347 	ValidateBinaryTask (Program* program)
348 		: m_program(program)
349 	{}
350 
execute(void)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 
365 private:
366 	Program*	m_program;
367 };
368 
createRoot(tcu::TestContext & testCtx)369 tcu::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 
378 struct BuildStats
379 {
380 	int		numSucceeded;
381 	int		numFailed;
382 	int		notSupported;
383 
BuildStatsvkt::BuildStats384 	BuildStats (void)
385 		: numSucceeded	(0)
386 		, numFailed		(0)
387 		, notSupported	(0)
388 	{
389 	}
390 };
391 
buildPrograms(tcu::TestContext & testCtx,const std::string & dstPath,const bool validateBinaries,const deUint32 usedVulkanVersion,const vk::SpirvVersion baselineSpirvVersion,const vk::SpirvVersion maxSpirvVersion,const bool allowSpirV14)392 BuildStats 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 
557 namespace opt
558 {
559 
560 DE_DECLARE_COMMAND_LINE_OPT(DstPath,				std::string);
561 DE_DECLARE_COMMAND_LINE_OPT(Cases,					std::string);
562 DE_DECLARE_COMMAND_LINE_OPT(Validate,				bool);
563 DE_DECLARE_COMMAND_LINE_OPT(VulkanVersion,			deUint32);
564 DE_DECLARE_COMMAND_LINE_OPT(ShaderCache,			bool);
565 DE_DECLARE_COMMAND_LINE_OPT(ShaderCacheFilename,	std::string);
566 DE_DECLARE_COMMAND_LINE_OPT(ShaderCacheTruncate,	bool);
567 DE_DECLARE_COMMAND_LINE_OPT(SpirvOptimize,			bool);
568 DE_DECLARE_COMMAND_LINE_OPT(SpirvOptimizationRecipe,std::string);
569 DE_DECLARE_COMMAND_LINE_OPT(SpirvAllow14,			bool);
570 
571 static const de::cmdline::NamedValue<bool> s_enableNames[] =
572 {
573 	{ "enable",		true },
574 	{ "disable",	false }
575 };
576 
registerOptions(de::cmdline::Parser & parser)577 void 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_VERSION(1, 0, 0)	},
585 		{ "1.1",	VK_MAKE_VERSION(1, 1, 0)	},
586 		{ "1.2",	VK_MAKE_VERSION(1, 2, 0)	},
587 	};
588 
589 	DE_STATIC_ASSERT(vk::SPIRV_VERSION_1_5 + 1 == vk::SPIRV_VERSION_LAST);
590 
591 	parser << Option<opt::DstPath>("d", "dst-path", "Destination path", "out")
592 		<< Option<opt::Cases>("n", "deqp-case", "Case path filter (works as in test binaries)")
593 		<< Option<opt::Validate>("v", "validate-spv", "Validate generated SPIR-V binaries")
594 		<< Option<opt::VulkanVersion>("t", "target-vulkan-version", "Target Vulkan version", s_vulkanVersion, "1.2")
595 		<< Option<opt::ShaderCache>("s", "shadercache", "Enable or disable shader cache", s_enableNames, "enable")
596 		<< Option<opt::ShaderCacheFilename>("r", "shadercache-filename", "Write shader cache to given file", "shadercache.bin")
597 		<< Option<opt::ShaderCacheTruncate>("x", "shadercache-truncate", "Truncate shader cache before running", s_enableNames, "enable")
598 		<< Option<opt::SpirvOptimize>("o", "deqp-optimize-spirv", "Enable optimization for SPIR-V", s_enableNames, "disable")
599 		<< Option<opt::SpirvOptimizationRecipe>("p","deqp-optimization-recipe", "Shader optimization recipe")
600 		<< Option<opt::SpirvAllow14>("e","allow-spirv-14", "Allow SPIR-V 1.4 with Vulkan 1.1");
601 }
602 
603 } // opt
604 
main(int argc,const char * argv[])605 int main (int argc, const char* argv[])
606 {
607 	de::cmdline::CommandLine	cmdLine;
608 	tcu::CommandLine			deqpCmdLine;
609 
610 	{
611 		de::cmdline::Parser		parser;
612 		opt::registerOptions(parser);
613 		if (!parser.parse(argc, argv, &cmdLine, std::cerr))
614 		{
615 			parser.help(std::cout);
616 			return -1;
617 		}
618 	}
619 
620 	{
621 		vector<const char*> deqpArgv;
622 
623 		deqpArgv.push_back("unused");
624 
625 		if (cmdLine.hasOption<opt::Cases>())
626 		{
627 			deqpArgv.push_back("--deqp-case");
628 			deqpArgv.push_back(cmdLine.getOption<opt::Cases>().c_str());
629 		}
630 
631 		if (cmdLine.hasOption<opt::ShaderCacheFilename>())
632 		{
633 			deqpArgv.push_back("--deqp-shadercache-filename");
634 			deqpArgv.push_back(cmdLine.getOption<opt::ShaderCacheFilename>().c_str());
635 		}
636 
637 		if (cmdLine.hasOption<opt::ShaderCache>())
638 		{
639 			deqpArgv.push_back("--deqp-shadercache");
640 			if (cmdLine.getOption<opt::ShaderCache>())
641 				deqpArgv.push_back("enable");
642 			else
643 				deqpArgv.push_back("disable");
644 		}
645 
646 		if (cmdLine.hasOption<opt::ShaderCacheTruncate>())
647 		{
648 			deqpArgv.push_back("--deqp-shadercache-truncate");
649 			if (cmdLine.getOption<opt::ShaderCacheTruncate>())
650 				deqpArgv.push_back("enable");
651 			else
652 				deqpArgv.push_back("disable");
653 		}
654 
655 		if (cmdLine.hasOption<opt::SpirvOptimize>())
656 		{
657 			deqpArgv.push_back("--deqp-optimize-spirv");
658 			if (cmdLine.getOption<opt::SpirvOptimize>())
659 				deqpArgv.push_back("enable");
660 			 else
661 				deqpArgv.push_back("disable");
662 		}
663 
664 		if (cmdLine.hasOption<opt::SpirvOptimizationRecipe>())
665 		{
666 			deqpArgv.push_back("--deqp-optimization-recipe");
667 			deqpArgv.push_back(cmdLine.getOption<opt::SpirvOptimizationRecipe>().c_str());
668 		}
669 
670 		if (!deqpCmdLine.parse((int)deqpArgv.size(), &deqpArgv[0]))
671 			return -1;
672 	}
673 
674 	try
675 	{
676 		tcu::DirArchive			archive					(".");
677 		tcu::TestLog			log						(deqpCmdLine.getLogFileName(), deqpCmdLine.getLogFlags());
678 		tcu::Platform			platform;
679 		tcu::TestContext		testCtx					(platform, archive, log, deqpCmdLine, DE_NULL);
680 		vk::SpirvVersion		baselineSpirvVersion	= vk::getBaselineSpirvVersion(cmdLine.getOption<opt::VulkanVersion>());
681 		vk::SpirvVersion		maxSpirvVersion			= vk::getMaxSpirvVersionForGlsl(cmdLine.getOption<opt::VulkanVersion>());
682 
683 		testCtx.writeSessionInfo();
684 
685 		tcu::print("SPIR-V versions: baseline: %s, max supported: %s\n",
686 					getSpirvVersionName(baselineSpirvVersion).c_str(),
687 					getSpirvVersionName(maxSpirvVersion).c_str());
688 
689 		const vkt::BuildStats	stats		= vkt::buildPrograms(testCtx,
690 																 cmdLine.getOption<opt::DstPath>(),
691 																 cmdLine.getOption<opt::Validate>(),
692 																 cmdLine.getOption<opt::VulkanVersion>(),
693 																 baselineSpirvVersion,
694 																 maxSpirvVersion,
695 																 cmdLine.getOption<opt::SpirvAllow14>());
696 
697 		tcu::print("DONE: %d passed, %d failed, %d not supported\n", stats.numSucceeded, stats.numFailed, stats.notSupported);
698 
699 		return stats.numFailed == 0 ? 0 : -1;
700 	}
701 	catch (const std::exception& e)
702 	{
703 		tcu::die("%s", e.what());
704 	}
705 }
706