Home | History | Annotate | Download | only in vulkan
      1 /*-------------------------------------------------------------------------
      2  * Vulkan Conformance Tests
      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 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 
     39 #include <iostream>
     40 
     41 using std::vector;
     42 using std::string;
     43 using de::UniquePtr;
     44 using de::MovePtr;
     45 using de::SharedPtr;
     46 
     47 namespace vkt
     48 {
     49 
     50 tcu::TestPackageRoot* createRoot (tcu::TestContext& testCtx)
     51 {
     52 	vector<tcu::TestNode*>	children;
     53 	children.push_back(new TestPackage(testCtx));
     54 	return new tcu::TestPackageRoot(testCtx, children);
     55 }
     56 
     57 struct BuildStats
     58 {
     59 	int		numSucceeded;
     60 	int		numFailed;
     61 
     62 	BuildStats (void)
     63 		: numSucceeded	(0)
     64 		, numFailed		(0)
     65 	{
     66 	}
     67 };
     68 
     69 namespace // anonymous
     70 {
     71 
     72 void writeBuildLogs (const glu::ShaderProgramInfo& buildInfo, std::ostream& dst)
     73 {
     74 	for (size_t shaderNdx = 0; shaderNdx < buildInfo.shaders.size(); shaderNdx++)
     75 	{
     76 		const glu::ShaderInfo&	shaderInfo	= buildInfo.shaders[shaderNdx];
     77 		const char* const		shaderName	= getShaderTypeName(shaderInfo.type);
     78 
     79 		dst << shaderName << " source:\n"
     80 			<< "---\n"
     81 			<< shaderInfo.source << "\n"
     82 			<< "---\n"
     83 			<< shaderName << " compile log:\n"
     84 			<< "---\n"
     85 			<< shaderInfo.infoLog << "\n"
     86 			<< "---\n";
     87 	}
     88 
     89 	dst << "link log:\n"
     90 		<< "---\n"
     91 		<< buildInfo.program.infoLog << "\n"
     92 		<< "---\n";
     93 }
     94 
     95 void writeBuildLogs (const vk::SpirVProgramInfo& buildInfo, std::ostream& dst)
     96 {
     97 	dst << "source:\n"
     98 		<< "---\n"
     99 		<< buildInfo.source << "\n"
    100 		<< "---\n";
    101 }
    102 
    103 vk::ProgramBinary* compileProgram (const glu::ProgramSources& source, std::ostream& buildLog)
    104 {
    105 	glu::ShaderProgramInfo	buildInfo;
    106 
    107 	try
    108 	{
    109 		return vk::buildProgram(source, vk::PROGRAM_FORMAT_SPIRV, &buildInfo);
    110 	}
    111 	catch (const tcu::Exception&)
    112 	{
    113 		writeBuildLogs(buildInfo, buildLog);
    114 		throw;
    115 	}
    116 }
    117 
    118 vk::ProgramBinary* compileProgram (const vk::SpirVAsmSource& source, std::ostream& buildLog)
    119 {
    120 	vk::SpirVProgramInfo	buildInfo;
    121 
    122 	try
    123 	{
    124 		return vk::assembleProgram(source, &buildInfo);
    125 	}
    126 	catch (const tcu::Exception&)
    127 	{
    128 		writeBuildLogs(buildInfo, buildLog);
    129 		throw;
    130 	}
    131 }
    132 
    133 struct BuiltProgram
    134 {
    135 	vk::ProgramIdentifier			id;
    136 	bool							buildOk;
    137 	UniquePtr<vk::ProgramBinary>	binary;		// Null if build failed
    138 	std::string						buildLog;
    139 
    140 	BuiltProgram (const vk::ProgramIdentifier&	id_,
    141 				  bool							buildOk_,
    142 				  MovePtr<vk::ProgramBinary>	binary_,
    143 				  const std::string&			buildLog_)
    144 		: id		(id_)
    145 		, buildOk	(buildOk_)
    146 		, binary	(binary_)
    147 		, buildLog	(buildLog_)
    148 	{
    149 	}
    150 };
    151 
    152 typedef SharedPtr<BuiltProgram> BuiltProgramSp;
    153 
    154 template<typename IteratorType>
    155 BuiltProgramSp buildProgram (IteratorType progIter, const std::string& casePath)
    156 {
    157 	std::ostringstream			buildLog;
    158 	MovePtr<vk::ProgramBinary>	programBinary;
    159 	bool						buildOk			= false;
    160 
    161 	try
    162 	{
    163 		programBinary	= MovePtr<vk::ProgramBinary>(compileProgram(progIter.getProgram(), buildLog));
    164 		buildOk			= true;
    165 	}
    166 	catch (const std::exception&)
    167 	{
    168 		// Ignore, buildOk = false
    169 		DE_ASSERT(!programBinary);
    170 	}
    171 
    172 	return BuiltProgramSp(new BuiltProgram(vk::ProgramIdentifier(casePath, progIter.getName()),
    173 										   buildOk,
    174 										   programBinary,
    175 										   buildLog.str()));
    176 }
    177 
    178 } // anonymous
    179 
    180 BuildStats buildPrograms (tcu::TestContext& testCtx, const std::string& dstPath, bool validateBinaries)
    181 {
    182 	const UniquePtr<tcu::TestPackageRoot>	root		(createRoot(testCtx));
    183 	tcu::DefaultHierarchyInflater			inflater	(testCtx);
    184 	tcu::TestHierarchyIterator				iterator	(*root, inflater, testCtx.getCommandLine());
    185 	const tcu::DirArchive					srcArchive	(dstPath.c_str());
    186 	UniquePtr<vk::BinaryRegistryWriter>		writer		(new vk::BinaryRegistryWriter(dstPath));
    187 	BuildStats								stats;
    188 
    189 	while (iterator.getState() != tcu::TestHierarchyIterator::STATE_FINISHED)
    190 	{
    191 		if (iterator.getState() == tcu::TestHierarchyIterator::STATE_ENTER_NODE &&
    192 			tcu::isTestNodeTypeExecutable(iterator.getNode()->getNodeType()))
    193 		{
    194 			const TestCase* const		testCase	= dynamic_cast<TestCase*>(iterator.getNode());
    195 			const string				casePath	= iterator.getNodePath();
    196 			vk::SourceCollections		sourcePrograms;
    197 			vector<BuiltProgramSp>		builtPrograms;
    198 
    199 			tcu::print("%s\n", casePath.c_str());
    200 
    201 			testCase->initPrograms(sourcePrograms);
    202 
    203 			for (vk::GlslSourceCollection::Iterator progIter = sourcePrograms.glslSources.begin();
    204 				 progIter != sourcePrograms.glslSources.end();
    205 				 ++progIter)
    206 			{
    207 				builtPrograms.push_back(buildProgram(progIter, casePath));
    208 			}
    209 
    210 			for (vk::SpirVAsmCollection::Iterator progIter = sourcePrograms.spirvAsmSources.begin();
    211 				 progIter != sourcePrograms.spirvAsmSources.end();
    212 				 ++progIter)
    213 			{
    214 				builtPrograms.push_back(buildProgram(progIter, casePath));
    215 			}
    216 
    217 			// Process programs
    218 			for (vector<BuiltProgramSp>::const_iterator progIter = builtPrograms.begin();
    219 				 progIter != builtPrograms.end();
    220 				 ++progIter)
    221 			{
    222 				const BuiltProgram&	program	= **progIter;
    223 
    224 				if (program.buildOk)
    225 				{
    226 					std::ostringstream	validationLog;
    227 
    228 					writer->storeProgram(program.id, *program.binary);
    229 
    230 					if (validateBinaries &&
    231 						!vk::validateProgram(*program.binary, &validationLog))
    232 					{
    233 						tcu::print("ERROR: validation failed for %s\n", program.id.programName.c_str());
    234 						tcu::print("%s\n", validationLog.str().c_str());
    235 						stats.numFailed += 1;
    236 					}
    237 					else
    238 						stats.numSucceeded += 1;
    239 				}
    240 				else
    241 				{
    242 					tcu::print("ERROR: failed to build %s\n", program.id.programName.c_str());
    243 					tcu::print("%s\n", program.buildLog.c_str());
    244 					stats.numFailed += 1;
    245 				}
    246 			}
    247 		}
    248 
    249 		iterator.next();
    250 	}
    251 
    252 	writer->writeIndex();
    253 
    254 	return stats;
    255 }
    256 
    257 } // vkt
    258 
    259 namespace opt
    260 {
    261 
    262 DE_DECLARE_COMMAND_LINE_OPT(DstPath,	std::string);
    263 DE_DECLARE_COMMAND_LINE_OPT(Cases,		std::string);
    264 DE_DECLARE_COMMAND_LINE_OPT(Validate,	bool);
    265 
    266 } // opt
    267 
    268 void registerOptions (de::cmdline::Parser& parser)
    269 {
    270 	using de::cmdline::Option;
    271 
    272 	parser << Option<opt::DstPath>	("d", "dst-path",		"Destination path",	"out")
    273 		   << Option<opt::Cases>	("n", "deqp-case",		"Case path filter (works as in test binaries)")
    274 		   << Option<opt::Validate>	("v", "validate-spv",	"Validate generated SPIR-V binaries");
    275 }
    276 
    277 int main (int argc, const char* argv[])
    278 {
    279 	de::cmdline::CommandLine	cmdLine;
    280 	tcu::CommandLine			deqpCmdLine;
    281 
    282 	{
    283 		de::cmdline::Parser		parser;
    284 		registerOptions(parser);
    285 		if (!parser.parse(argc, argv, &cmdLine, std::cerr))
    286 		{
    287 			parser.help(std::cout);
    288 			return -1;
    289 		}
    290 	}
    291 
    292 	{
    293 		vector<const char*> deqpArgv;
    294 
    295 		deqpArgv.push_back("unused");
    296 
    297 		if (cmdLine.hasOption<opt::Cases>())
    298 		{
    299 			deqpArgv.push_back("--deqp-case");
    300 			deqpArgv.push_back(cmdLine.getOption<opt::Cases>().c_str());
    301 		}
    302 
    303 		if (!deqpCmdLine.parse((int)deqpArgv.size(), &deqpArgv[0]))
    304 			return -1;
    305 	}
    306 
    307 	try
    308 	{
    309 		tcu::DirArchive			archive			(".");
    310 		tcu::TestLog			log				(deqpCmdLine.getLogFileName(), deqpCmdLine.getLogFlags());
    311 		tcu::Platform			platform;
    312 		tcu::TestContext		testCtx			(platform, archive, log, deqpCmdLine, DE_NULL);
    313 
    314 		const vkt::BuildStats	stats			= vkt::buildPrograms(testCtx,
    315 																	 cmdLine.getOption<opt::DstPath>(),
    316 																	 cmdLine.getOption<opt::Validate>());
    317 
    318 		tcu::print("DONE: %d passed, %d failed\n", stats.numSucceeded, stats.numFailed);
    319 
    320 		return stats.numFailed == 0 ? 0 : -1;
    321 	}
    322 	catch (const std::exception& e)
    323 	{
    324 		tcu::die("%s", e.what());
    325 	}
    326 }
    327