1 /*-------------------------------------------------------------------------
2 * Vulkan Conformance Tests
3 * ------------------------
5 * Copyright (c) 2016 Google Inc.
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
11 * http://www.apache.org/licenses/LICENSE-2.0
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.
21 * \brief Utility for pre-compiling source programs to SPIR-V
22 *//*--------------------------------------------------------------------*/
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"
53 namespace // anonymous
56 typedef de::SharedPtr<glu::ProgramSources> ProgramSourcesSp;
57 typedef de::SharedPtr<vk::SpirVAsmSource> SpirVAsmSourceSp;
58 typedef de::SharedPtr<vk::ProgramBinary> ProgramBinarySp;
63 virtual void execute (void) = 0;
66 typedef de::ThreadSafeRingBuffer<Task*> TaskQueue;
68 class TaskExecutorThread : public de::Thread
71 TaskExecutorThread (TaskQueue& tasks)
81 Task* const task = m_tasks.popBack();
86 break; // End of tasks - time to terminate
97 TaskExecutor (deUint32 numThreads);
100 void submit (Task* task);
101 void waitForComplete (void);
104 typedef de::SharedPtr<TaskExecutorThread> ExecThreadSp;
106 std::vector<ExecThreadSp> m_threads;
110 TaskExecutor::TaskExecutor (deUint32 numThreads)
111 : m_threads (numThreads)
112 , m_tasks (m_threads.size() * 1024u)
114 for (size_t ndx = 0; ndx < m_threads.size(); ++ndx)
115 m_threads[ndx] = ExecThreadSp(new TaskExecutorThread(m_tasks));
118 TaskExecutor::~TaskExecutor (void)
120 for (size_t ndx = 0; ndx < m_threads.size(); ++ndx)
121 m_tasks.pushFront(DE_NULL);
123 for (size_t ndx = 0; ndx < m_threads.size(); ++ndx)
124 m_threads[ndx]->join();
127 void TaskExecutor::submit (Task* task)
130 m_tasks.pushFront(task);
133 class SyncTask : public Task
136 SyncTask (de::Semaphore* enterBarrier, de::Semaphore* inBarrier, de::Semaphore* leaveBarrier)
137 : m_enterBarrier (enterBarrier)
138 , m_inBarrier (inBarrier)
139 , m_leaveBarrier (leaveBarrier)
143 : m_enterBarrier (DE_NULL)
144 , m_inBarrier (DE_NULL)
145 , m_leaveBarrier (DE_NULL)
150 m_enterBarrier->increment();
151 m_inBarrier->decrement();
152 m_leaveBarrier->increment();
156 de::Semaphore* m_enterBarrier;
157 de::Semaphore* m_inBarrier;
158 de::Semaphore* m_leaveBarrier;
161 void TaskExecutor::waitForComplete (void)
163 de::Semaphore enterBarrier (0);
164 de::Semaphore inBarrier (0);
165 de::Semaphore leaveBarrier (0);
166 std::vector<SyncTask> syncTasks (m_threads.size());
168 for (size_t ndx = 0; ndx < m_threads.size(); ++ndx)
170 syncTasks[ndx] = SyncTask(&enterBarrier, &inBarrier, &leaveBarrier);
171 submit(&syncTasks[ndx]);
174 for (size_t ndx = 0; ndx < m_threads.size(); ++ndx)
175 enterBarrier.decrement();
177 for (size_t ndx = 0; ndx < m_threads.size(); ++ndx)
178 inBarrier.increment();
180 for (size_t ndx = 0; ndx < m_threads.size(); ++ndx)
181 leaveBarrier.decrement();
188 STATUS_NOT_COMPLETED = 0,
195 vk::ProgramIdentifier id;
198 std::string buildLog;
199 ProgramBinarySp binary;
201 Status validationStatus;
202 std::string validationLog;
204 explicit Program (const vk::ProgramIdentifier& id_)
206 , buildStatus (STATUS_NOT_COMPLETED)
207 , validationStatus (STATUS_NOT_COMPLETED)
211 , buildStatus (STATUS_NOT_COMPLETED)
212 , validationStatus (STATUS_NOT_COMPLETED)
216 void writeBuildLogs (const glu::ShaderProgramInfo& buildInfo, std::ostream& dst)
218 for (size_t shaderNdx = 0; shaderNdx < buildInfo.shaders.size(); shaderNdx++)
220 const glu::ShaderInfo& shaderInfo = buildInfo.shaders[shaderNdx];
221 const char* const shaderName = getShaderTypeName(shaderInfo.type);
223 dst << shaderName << " source:\n"
225 << shaderInfo.source << "\n"
227 << shaderName << " compile log:\n"
229 << shaderInfo.infoLog << "\n"
235 << buildInfo.program.infoLog << "\n"
239 class BuildGlslTask : public Task
243 BuildGlslTask (const glu::ProgramSources& source, Program* program)
245 , m_program (program)
248 BuildGlslTask (void) : m_program(DE_NULL) {}
252 glu::ShaderProgramInfo buildInfo;
256 m_program->binary = ProgramBinarySp(vk::buildProgram(m_source, vk::PROGRAM_FORMAT_SPIRV, &buildInfo));
257 m_program->buildStatus = Program::STATUS_PASSED;
259 catch (const tcu::Exception&)
261 std::ostringstream log;
263 writeBuildLogs(buildInfo, log);
265 m_program->buildStatus = Program::STATUS_FAILED;
266 m_program->buildLog = log.str();
272 glu::ProgramSources m_source;
276 void writeBuildLogs (const vk::SpirVProgramInfo& buildInfo, std::ostream& dst)
280 << buildInfo.source << "\n"
284 class BuildSpirVAsmTask : public Task
287 BuildSpirVAsmTask (const vk::SpirVAsmSource& source, Program* program)
289 , m_program (program)
292 BuildSpirVAsmTask (void) : m_program(DE_NULL) {}
296 vk::SpirVProgramInfo buildInfo;
300 m_program->binary = ProgramBinarySp(vk::assembleProgram(m_source, &buildInfo));
301 m_program->buildStatus = Program::STATUS_PASSED;
303 catch (const tcu::Exception&)
305 std::ostringstream log;
307 writeBuildLogs(buildInfo, log);
309 m_program->buildStatus = Program::STATUS_FAILED;
310 m_program->buildLog = log.str();
315 vk::SpirVAsmSource m_source;
319 class ValidateBinaryTask : public Task
322 ValidateBinaryTask (Program* program)
328 DE_ASSERT(m_program->buildStatus == Program::STATUS_PASSED);
330 std::ostringstream validationLog;
332 if (vk::validateProgram(*m_program->binary, &validationLog))
333 m_program->validationStatus = Program::STATUS_PASSED;
335 m_program->validationStatus = Program::STATUS_FAILED;
342 tcu::TestPackageRoot* createRoot (tcu::TestContext& testCtx)
344 vector<tcu::TestNode*> children;
345 children.push_back(new TestPackage(testCtx));
346 return new tcu::TestPackageRoot(testCtx, children);
363 BuildStats buildPrograms (tcu::TestContext& testCtx, const std::string& dstPath, bool validateBinaries)
365 const deUint32 numThreads = deGetNumAvailableLogicalCores();
367 TaskExecutor executor (numThreads);
369 // de::PoolArray<> is faster to build than std::vector
370 de::MemPool programPool;
371 de::PoolArray<Program> programs (&programPool);
375 de::PoolArray<BuildGlslTask> buildGlslTasks (&tmpPool);
376 de::PoolArray<BuildSpirVAsmTask> buildSpirvAsmTasks (&tmpPool);
378 // Collect build tasks
380 const UniquePtr<tcu::TestPackageRoot> root (createRoot(testCtx));
381 tcu::DefaultHierarchyInflater inflater (testCtx);
382 de::MovePtr<tcu::CaseListFilter> caseListFilter (testCtx.getCommandLine().createCaseListFilter(testCtx.getArchive()));
383 tcu::TestHierarchyIterator iterator (*root, inflater, *caseListFilter);
385 while (iterator.getState() != tcu::TestHierarchyIterator::STATE_FINISHED)
387 if (iterator.getState() == tcu::TestHierarchyIterator::STATE_ENTER_NODE &&
388 tcu::isTestNodeTypeExecutable(iterator.getNode()->getNodeType()))
390 const TestCase* const testCase = dynamic_cast<TestCase*>(iterator.getNode());
391 const string casePath = iterator.getNodePath();
392 vk::SourceCollections sourcePrograms;
394 testCase->initPrograms(sourcePrograms);
396 for (vk::GlslSourceCollection::Iterator progIter = sourcePrograms.glslSources.begin();
397 progIter != sourcePrograms.glslSources.end();
400 programs.pushBack(Program(vk::ProgramIdentifier(casePath, progIter.getName())));
401 buildGlslTasks.pushBack(BuildGlslTask(progIter.getProgram(), &programs.back()));
402 executor.submit(&buildGlslTasks.back());
405 for (vk::SpirVAsmCollection::Iterator progIter = sourcePrograms.spirvAsmSources.begin();
406 progIter != sourcePrograms.spirvAsmSources.end();
409 programs.pushBack(Program(vk::ProgramIdentifier(casePath, progIter.getName())));
410 buildSpirvAsmTasks.pushBack(BuildSpirVAsmTask(progIter.getProgram(), &programs.back()));
411 executor.submit(&buildSpirvAsmTasks.back());
419 // Need to wait until tasks completed before freeing task memory
420 executor.waitForComplete();
423 if (validateBinaries)
425 std::vector<ValidateBinaryTask> validationTasks;
427 validationTasks.reserve(programs.size());
429 for (de::PoolArray<Program>::iterator progIter = programs.begin(); progIter != programs.end(); ++progIter)
431 if (progIter->buildStatus == Program::STATUS_PASSED)
433 validationTasks.push_back(ValidateBinaryTask(&*progIter));
434 executor.submit(&validationTasks.back());
438 executor.waitForComplete();
442 vk::BinaryRegistryWriter registryWriter (dstPath);
444 for (de::PoolArray<Program>::iterator progIter = programs.begin(); progIter != programs.end(); ++progIter)
446 if (progIter->buildStatus == Program::STATUS_PASSED)
447 registryWriter.addProgram(progIter->id, *progIter->binary);
450 registryWriter.write();
456 for (de::PoolArray<Program>::iterator progIter = programs.begin(); progIter != programs.end(); ++progIter)
458 const bool buildOk = progIter->buildStatus == Program::STATUS_PASSED;
459 const bool validationOk = progIter->validationStatus != Program::STATUS_FAILED;
461 if (buildOk && validationOk)
462 stats.numSucceeded += 1;
465 stats.numFailed += 1;
466 tcu::print("ERROR: %s / %s: %s failed\n",
467 progIter->id.testCasePath.c_str(),
468 progIter->id.programName.c_str(),
469 (buildOk ? "validation" : "build"));
470 tcu::print("%s\n", (buildOk ? progIter->validationLog.c_str() : progIter->buildLog.c_str()));
483 DE_DECLARE_COMMAND_LINE_OPT(DstPath, std::string);
484 DE_DECLARE_COMMAND_LINE_OPT(Cases, std::string);
485 DE_DECLARE_COMMAND_LINE_OPT(Validate, bool);
489 void registerOptions (de::cmdline::Parser& parser)
491 using de::cmdline::Option;
493 parser << Option<opt::DstPath> ("d", "dst-path", "Destination path", "out")
494 << Option<opt::Cases> ("n", "deqp-case", "Case path filter (works as in test binaries)")
495 << Option<opt::Validate> ("v", "validate-spv", "Validate generated SPIR-V binaries");
498 int main (int argc, const char* argv[])
500 de::cmdline::CommandLine cmdLine;
501 tcu::CommandLine deqpCmdLine;
504 de::cmdline::Parser parser;
505 registerOptions(parser);
506 if (!parser.parse(argc, argv, &cmdLine, std::cerr))
508 parser.help(std::cout);
514 vector<const char*> deqpArgv;
516 deqpArgv.push_back("unused");
518 if (cmdLine.hasOption<opt::Cases>())
520 deqpArgv.push_back("--deqp-case");
521 deqpArgv.push_back(cmdLine.getOption<opt::Cases>().c_str());
524 if (!deqpCmdLine.parse((int)deqpArgv.size(), &deqpArgv[0]))
530 tcu::DirArchive archive (".");
531 tcu::TestLog log (deqpCmdLine.getLogFileName(), deqpCmdLine.getLogFlags());
532 tcu::Platform platform;
533 tcu::TestContext testCtx (platform, archive, log, deqpCmdLine, DE_NULL);
535 const vkt::BuildStats stats = vkt::buildPrograms(testCtx,
536 cmdLine.getOption<opt::DstPath>(),
537 cmdLine.getOption<opt::Validate>());
539 tcu::print("DONE: %d passed, %d failed\n", stats.numSucceeded, stats.numFailed);
541 return stats.numFailed == 0 ? 0 : -1;
543 catch (const std::exception& e)
545 tcu::die("%s", e.what());