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);
374 // \todo [2016-09-30 pyry] Use main executor when glslang no longer requires global lock
375 TaskExecutor buildGlslExecutor (1);
377 de::PoolArray<BuildGlslTask> buildGlslTasks (&tmpPool);
378 de::PoolArray<BuildSpirVAsmTask> buildSpirvAsmTasks (&tmpPool);
380 // Collect build tasks
382 const UniquePtr<tcu::TestPackageRoot> root (createRoot(testCtx));
383 tcu::DefaultHierarchyInflater inflater (testCtx);
384 tcu::TestHierarchyIterator iterator (*root, inflater, testCtx.getCommandLine());
386 while (iterator.getState() != tcu::TestHierarchyIterator::STATE_FINISHED)
388 if (iterator.getState() == tcu::TestHierarchyIterator::STATE_ENTER_NODE &&
389 tcu::isTestNodeTypeExecutable(iterator.getNode()->getNodeType()))
391 const TestCase* const testCase = dynamic_cast<TestCase*>(iterator.getNode());
392 const string casePath = iterator.getNodePath();
393 vk::SourceCollections sourcePrograms;
395 testCase->initPrograms(sourcePrograms);
397 for (vk::GlslSourceCollection::Iterator progIter = sourcePrograms.glslSources.begin();
398 progIter != sourcePrograms.glslSources.end();
401 programs.pushBack(Program(vk::ProgramIdentifier(casePath, progIter.getName())));
402 buildGlslTasks.pushBack(BuildGlslTask(progIter.getProgram(), &programs.back()));
403 buildGlslExecutor.submit(&buildGlslTasks.back());
406 for (vk::SpirVAsmCollection::Iterator progIter = sourcePrograms.spirvAsmSources.begin();
407 progIter != sourcePrograms.spirvAsmSources.end();
410 programs.pushBack(Program(vk::ProgramIdentifier(casePath, progIter.getName())));
411 buildSpirvAsmTasks.pushBack(BuildSpirVAsmTask(progIter.getProgram(), &programs.back()));
412 executor.submit(&buildSpirvAsmTasks.back());
420 // Need to wait until tasks completed before freeing task memory
421 buildGlslExecutor.waitForComplete();
422 executor.waitForComplete();
425 if (validateBinaries)
427 std::vector<ValidateBinaryTask> validationTasks;
429 validationTasks.reserve(programs.size());
431 for (de::PoolArray<Program>::iterator progIter = programs.begin(); progIter != programs.end(); ++progIter)
433 if (progIter->buildStatus == Program::STATUS_PASSED)
435 validationTasks.push_back(ValidateBinaryTask(&*progIter));
436 executor.submit(&validationTasks.back());
440 executor.waitForComplete();
444 vk::BinaryRegistryWriter registryWriter (dstPath);
446 for (de::PoolArray<Program>::iterator progIter = programs.begin(); progIter != programs.end(); ++progIter)
448 if (progIter->buildStatus == Program::STATUS_PASSED)
449 registryWriter.storeProgram(progIter->id, *progIter->binary);
452 registryWriter.writeIndex();
458 for (de::PoolArray<Program>::iterator progIter = programs.begin(); progIter != programs.end(); ++progIter)
460 const bool buildOk = progIter->buildStatus == Program::STATUS_PASSED;
461 const bool validationOk = progIter->validationStatus != Program::STATUS_FAILED;
463 if (buildOk && validationOk)
464 stats.numSucceeded += 1;
467 stats.numFailed += 1;
468 tcu::print("ERROR: %s / %s: %s failed\n",
469 progIter->id.testCasePath.c_str(),
470 progIter->id.programName.c_str(),
471 (buildOk ? "validation" : "build"));
472 tcu::print("%s\n", (buildOk ? progIter->validationLog.c_str() : progIter->buildLog.c_str()));
485 DE_DECLARE_COMMAND_LINE_OPT(DstPath, std::string);
486 DE_DECLARE_COMMAND_LINE_OPT(Cases, std::string);
487 DE_DECLARE_COMMAND_LINE_OPT(Validate, bool);
491 void registerOptions (de::cmdline::Parser& parser)
493 using de::cmdline::Option;
495 parser << Option<opt::DstPath> ("d", "dst-path", "Destination path", "out")
496 << Option<opt::Cases> ("n", "deqp-case", "Case path filter (works as in test binaries)")
497 << Option<opt::Validate> ("v", "validate-spv", "Validate generated SPIR-V binaries");
500 int main (int argc, const char* argv[])
502 de::cmdline::CommandLine cmdLine;
503 tcu::CommandLine deqpCmdLine;
506 de::cmdline::Parser parser;
507 registerOptions(parser);
508 if (!parser.parse(argc, argv, &cmdLine, std::cerr))
510 parser.help(std::cout);
516 vector<const char*> deqpArgv;
518 deqpArgv.push_back("unused");
520 if (cmdLine.hasOption<opt::Cases>())
522 deqpArgv.push_back("--deqp-case");
523 deqpArgv.push_back(cmdLine.getOption<opt::Cases>().c_str());
526 if (!deqpCmdLine.parse((int)deqpArgv.size(), &deqpArgv[0]))
532 tcu::DirArchive archive (".");
533 tcu::TestLog log (deqpCmdLine.getLogFileName(), deqpCmdLine.getLogFlags());
534 tcu::Platform platform;
535 tcu::TestContext testCtx (platform, archive, log, deqpCmdLine, DE_NULL);
537 const vkt::BuildStats stats = vkt::buildPrograms(testCtx,
538 cmdLine.getOption<opt::DstPath>(),
539 cmdLine.getOption<opt::Validate>());
541 tcu::print("DONE: %d passed, %d failed\n", stats.numSucceeded, stats.numFailed);
543 return stats.numFailed == 0 ? 0 : -1;
545 catch (const std::exception& e)
547 tcu::die("%s", e.what());