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 41using std::vector; 42using std::string; 43using de::UniquePtr; 44using de::MovePtr; 45using de::SharedPtr; 46 47namespace vkt 48{ 49 50tcu::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 57struct BuildStats 58{ 59 int numSucceeded; 60 int numFailed; 61 62 BuildStats (void) 63 : numSucceeded (0) 64 , numFailed (0) 65 { 66 } 67}; 68 69namespace // anonymous 70{ 71 72void 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 95void writeBuildLogs (const vk::SpirVProgramInfo& buildInfo, std::ostream& dst) 96{ 97 dst << "source:\n" 98 << "---\n" 99 << buildInfo.source << "\n" 100 << "---\n"; 101} 102 103vk::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 118vk::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 133struct 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 152typedef SharedPtr<BuiltProgram> BuiltProgramSp; 153 154template<typename IteratorType> 155BuiltProgramSp 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 180BuildStats 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 259namespace opt 260{ 261 262DE_DECLARE_COMMAND_LINE_OPT(DstPath, std::string); 263DE_DECLARE_COMMAND_LINE_OPT(Cases, std::string); 264DE_DECLARE_COMMAND_LINE_OPT(Validate, bool); 265 266} // opt 267 268void 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 277int 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