1 /*-------------------------------------------------------------------------
2 * OpenGL Conformance Test Suite
3 * -----------------------------
5 * Copyright (c) 2016 Google Inc.
6 * Copyright (c) 2016 The Khronos Group Inc.
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
12 * http://www.apache.org/licenses/LICENSE-2.0
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
23 */ /*-------------------------------------------------------------------*/
25 #include "glcTestRunner.hpp"
26 #include "deFilePath.hpp"
27 #include "deStringUtil.hpp"
28 #include "deUniquePtr.hpp"
29 #include "glcConfigList.hpp"
30 #include "qpXmlWriter.h"
32 #include "tcuCommandLine.hpp"
33 #include "tcuTestLog.hpp"
34 #include "tcuTestSessionExecutor.hpp"
49 RunSession(tcu::Platform& platform, tcu::Archive& archive, const int numArgs, const char* const* args)
50 : m_cmdLine(numArgs, args)
51 , m_log(m_cmdLine.getLogFileName(), m_cmdLine.getLogFlags())
52 , m_app(platform, archive, m_log, m_cmdLine)
56 inline bool iterate(void)
58 return m_app.iterate();
61 inline const tcu::TestRunStatus& getResult(void) const
63 return m_app.getResult();
67 tcu::CommandLine m_cmdLine;
72 static void appendConfigArgs(const Config& config, std::vector<std::string>& args, const char* fboConfig)
74 if (fboConfig != NULL)
76 args.push_back(string("--deqp-gl-config-name=") + fboConfig);
77 args.push_back("--deqp-surface-type=fbo");
80 if (config.type != CONFIGTYPE_DEFAULT)
82 // \todo [2013-05-06 pyry] Test all surface types for some configs?
83 if (fboConfig == NULL)
85 if (config.surfaceTypes & SURFACETYPE_WINDOW)
86 args.push_back("--deqp-surface-type=window");
87 else if (config.surfaceTypes & SURFACETYPE_PBUFFER)
88 args.push_back("--deqp-surface-type=pbuffer");
89 else if (config.surfaceTypes & SURFACETYPE_PIXMAP)
90 args.push_back("--deqp-surface-type=pixmap");
93 args.push_back(string("--deqp-gl-config-id=") + de::toString(config.id));
95 if (config.type == CONFIGTYPE_EGL)
96 args.push_back("--deqp-gl-context-type=egl");
97 else if (config.type == CONFIGTYPE_WGL)
98 args.push_back("--deqp-gl-context-type=wgl");
102 typedef struct configInfo
113 static configInfo parseConfigBitsFromName(const char* configName)
124 { "rgba8888", 8, 8, 8, 8 }, { "rgb565", 5, 6, 5, 0 },
126 for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(colorCfgs); ndx++)
128 if (!strncmp(configName, colorCfgs[ndx].name, strlen(colorCfgs[ndx].name)))
130 cfgInfo.redBits = colorCfgs[ndx].redBits;
131 cfgInfo.greenBits = colorCfgs[ndx].greenBits;
132 cfgInfo.blueBits = colorCfgs[ndx].blueBits;
133 cfgInfo.alphaBits = colorCfgs[ndx].alphaBits;
135 configName += strlen(colorCfgs[ndx].name);
145 { "d0", 0 }, { "d24", 24 },
147 for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(depthCfgs); ndx++)
149 if (!strncmp(configName, depthCfgs[ndx].name, strlen(depthCfgs[ndx].name)))
151 cfgInfo.depthBits = depthCfgs[ndx].depthBits;
153 configName += strlen(depthCfgs[ndx].name);
163 { "s0", 0 }, { "s8", 8 },
165 for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(stencilCfgs); ndx++)
167 if (!strncmp(configName, stencilCfgs[ndx].name, strlen(stencilCfgs[ndx].name)))
169 cfgInfo.stencilBits = stencilCfgs[ndx].stencilBits;
171 configName += strlen(stencilCfgs[ndx].name);
180 } multiSampleCfgs[] = {
181 { "ms0", 0 }, { "ms4", 4 },
183 for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(multiSampleCfgs); ndx++)
185 if (!strncmp(configName, multiSampleCfgs[ndx].name, strlen(multiSampleCfgs[ndx].name)))
187 cfgInfo.samples = multiSampleCfgs[ndx].samples;
189 configName += strlen(multiSampleCfgs[ndx].name);
197 static string getApiName(glu::ApiType apiType)
199 if (apiType == glu::ApiType::es(2, 0))
201 else if (apiType == glu::ApiType::es(3, 0))
203 else if (apiType == glu::ApiType::es(3, 1))
205 else if (apiType == glu::ApiType::es(3, 2))
207 else if (apiType == glu::ApiType::core(3, 0))
209 else if (apiType == glu::ApiType::core(3, 1))
211 else if (apiType == glu::ApiType::core(3, 2))
213 else if (apiType == glu::ApiType::core(3, 3))
215 else if (apiType == glu::ApiType::core(4, 0))
217 else if (apiType == glu::ApiType::core(4, 1))
219 else if (apiType == glu::ApiType::core(4, 2))
221 else if (apiType == glu::ApiType::core(4, 3))
223 else if (apiType == glu::ApiType::core(4, 4))
225 else if (apiType == glu::ApiType::core(4, 5))
228 throw std::runtime_error("Unknown context type");
231 static const string getCaseListFileOption(const string mustpassDir, const string apiName, const string mustpassName)
233 #if DE_OS == DE_OS_ANDROID
234 const string case_list_option = "--deqp-caselist-resource=";
236 const string case_list_option = "--deqp-caselist-file=";
238 return case_list_option + mustpassDir + apiName + "-" + mustpassName + ".txt";
241 static const string getLogFileName(const string apiName, const string configName, const int iterId, const int runId,
242 const int width, const int height, const int seed)
244 string res = string("config-") + apiName + "-" + configName + "-cfg-" + de::toString(iterId) + "-run-" +
245 de::toString(runId) + "-width-" + de::toString(width) + "-height-" + de::toString(height);
248 res += "-seed-" + de::toString(seed);
255 static void getBaseOptions(std::vector<std::string>& args, const string mustpassDir, const string apiName,
256 const string configName, const string screenRotation, int width, int height)
258 args.push_back(getCaseListFileOption(mustpassDir, apiName, configName));
259 args.push_back(string("--deqp-screen-rotation=") + screenRotation);
260 args.push_back(string("--deqp-surface-width=") + de::toString(width));
261 args.push_back(string("--deqp-surface-height=") + de::toString(height));
262 args.push_back("--deqp-watchdog=disable");
265 static bool isGLConfigCompatible(configInfo cfgInfo, const AOSPConfig& config)
267 return cfgInfo.redBits == config.redBits && cfgInfo.greenBits == config.greenBits &&
268 cfgInfo.blueBits == config.blueBits && cfgInfo.alphaBits == config.alphaBits &&
269 cfgInfo.depthBits == config.depthBits && cfgInfo.stencilBits == config.stencilBits &&
270 cfgInfo.samples == config.samples;
273 static void getTestRunsForAOSPEGL(vector<TestRunParams>& runs, const ConfigList& configs)
275 #include "glcAospMustpassEgl.hpp"
277 for (int i = 0; i < DE_LENGTH_OF_ARRAY(aosp_mustpass_egl_first_cfg); ++i)
279 configInfo cfgInfo = parseConfigBitsFromName(aosp_mustpass_egl_first_cfg[i].glConfigName);
281 vector<AOSPConfig>::const_iterator cfgIter;
282 for (cfgIter = configs.aospConfigs.begin(); cfgIter != configs.aospConfigs.end(); ++cfgIter)
284 // find first compatible config
285 if ((*cfgIter).type == CONFIGTYPE_EGL && isGLConfigCompatible(cfgInfo, *cfgIter))
291 if (cfgIter == configs.aospConfigs.end())
293 // No suitable configuration found. Skipping EGL tests
297 string apiName = "egl";
298 const int width = aosp_mustpass_egl_first_cfg[i].surfaceWidth;
299 const int height = aosp_mustpass_egl_first_cfg[i].surfaceHeight;
301 TestRunParams params;
303 getLogFileName(apiName, aosp_mustpass_egl_first_cfg[i].configName, 1, i, width, height, -1);
304 getBaseOptions(params.args, mustpassDir, apiName, aosp_mustpass_egl_first_cfg[i].configName,
305 aosp_mustpass_egl_first_cfg[i].screenRotation, width, height);
307 params.args.push_back(string("--deqp-gl-config-name=") + string(aosp_mustpass_egl_first_cfg[i].glConfigName));
309 runs.push_back(params);
313 static void getTestRunsForAOSPES(vector<TestRunParams>& runs, const ConfigList& configs, const glu::ApiType apiType)
315 #include "glcAospMustpassEs.hpp"
317 for (int i = 0; i < DE_LENGTH_OF_ARRAY(aosp_mustpass_es_first_cfg); ++i)
319 if (!glu::contextSupports(glu::ContextType(apiType), aosp_mustpass_es_first_cfg[i].apiType))
322 configInfo cfgInfo = parseConfigBitsFromName(aosp_mustpass_es_first_cfg[i].glConfigName);
324 vector<AOSPConfig>::const_iterator cfgIter;
325 for (cfgIter = configs.aospConfigs.begin(); cfgIter != configs.aospConfigs.end(); ++cfgIter)
327 // find first compatible config
328 if (isGLConfigCompatible(cfgInfo, *cfgIter))
334 if (cfgIter == configs.aospConfigs.end())
336 TCU_FAIL(("No suitable configuration found for GL config " +
337 de::toString(aosp_mustpass_es_first_cfg[i].glConfigName))
342 string apiName = getApiName(aosp_mustpass_es_first_cfg[i].apiType);
343 const int width = aosp_mustpass_es_first_cfg[i].surfaceWidth;
344 const int height = aosp_mustpass_es_first_cfg[i].surfaceHeight;
346 TestRunParams params;
347 params.logFilename = getLogFileName(apiName, aosp_mustpass_es_first_cfg[i].configName, 1, i, width, height, -1);
348 getBaseOptions(params.args, mustpassDir, apiName, aosp_mustpass_es_first_cfg[i].configName,
349 aosp_mustpass_es_first_cfg[i].screenRotation, width, height);
351 params.args.push_back(string("--deqp-gl-config-name=") + string(aosp_mustpass_es_first_cfg[i].glConfigName));
354 if ((*cfgIter).surfaceTypes & SURFACETYPE_WINDOW)
355 params.args.push_back("--deqp-surface-type=window");
356 else if ((*cfgIter).surfaceTypes & SURFACETYPE_PBUFFER)
357 params.args.push_back("--deqp-surface-type=pbuffer");
358 else if ((*cfgIter).surfaceTypes & SURFACETYPE_PIXMAP)
359 params.args.push_back("--deqp-surface-type=pixmap");
360 runs.push_back(params);
364 static void getTestRunsForES(glu::ApiType type, const ConfigList& configs, vector<TestRunParams>& runs)
366 getTestRunsForAOSPEGL(runs, configs);
367 getTestRunsForAOSPES(runs, configs, type);
369 #include "glcKhronosMustpassEs.hpp"
371 for (vector<Config>::const_iterator cfgIter = configs.configs.begin(); cfgIter != configs.configs.end(); ++cfgIter)
373 const bool isFirst = cfgIter == configs.configs.begin();
374 const int numRunParams = isFirst ? DE_LENGTH_OF_ARRAY(khronos_mustpass_es_first_cfg) :
375 DE_LENGTH_OF_ARRAY(khronos_mustpass_es_other_cfg);
376 const RunParams* runParams = isFirst ? khronos_mustpass_es_first_cfg : khronos_mustpass_es_other_cfg;
378 for (int runNdx = 0; runNdx < numRunParams; runNdx++)
380 if (!glu::contextSupports(glu::ContextType(type), runParams[runNdx].apiType))
383 string apiName = getApiName(runParams[runNdx].apiType);
384 const int width = runParams[runNdx].surfaceWidth;
385 const int height = runParams[runNdx].surfaceHeight;
386 const int seed = runParams[runNdx].baseSeed;
388 TestRunParams params;
391 getLogFileName(apiName, runParams[runNdx].configName, cfgIter->id, runNdx, width, height, seed);
393 getBaseOptions(params.args, mustpassDir, apiName, runParams[runNdx].configName,
394 runParams[runNdx].screenRotation, width, height);
395 params.args.push_back(string("--deqp-base-seed=") + de::toString(seed));
397 appendConfigArgs(*cfgIter, params.args, runParams[runNdx].fboConfig);
399 runs.push_back(params);
404 static void getTestRunsForGL(glu::ApiType type, const ConfigList& configs, vector<TestRunParams>& runs)
406 #include "glcKhronosMustpassGl.hpp"
408 for (vector<Config>::const_iterator cfgIter = configs.configs.begin(); cfgIter != configs.configs.end(); ++cfgIter)
410 const bool isFirst = cfgIter == configs.configs.begin();
411 const int numRunParams = isFirst ? DE_LENGTH_OF_ARRAY(khronos_mustpass_gl_first_cfg) :
412 DE_LENGTH_OF_ARRAY(khronos_mustpass_gl_other_cfg);
413 const RunParams* runParams = isFirst ? khronos_mustpass_gl_first_cfg : khronos_mustpass_gl_other_cfg;
415 for (int runNdx = 0; runNdx < numRunParams; runNdx++)
417 if (type != runParams[runNdx].apiType)
420 string apiName = getApiName(runParams[runNdx].apiType);
421 const int width = runParams[runNdx].surfaceWidth;
422 const int height = runParams[runNdx].surfaceHeight;
423 const int seed = runParams[runNdx].baseSeed;
425 TestRunParams params;
428 getLogFileName(apiName, runParams[runNdx].configName, cfgIter->id, runNdx, width, height, seed);
430 getBaseOptions(params.args, mustpassDir, apiName, runParams[runNdx].configName,
431 runParams[runNdx].screenRotation, width, height);
432 params.args.push_back(string("--deqp-base-seed=") + de::toString(seed));
434 appendConfigArgs(*cfgIter, params.args, runParams[runNdx].fboConfig);
436 runs.push_back(params);
441 static void getTestRunParams(glu::ApiType type, const ConfigList& configs, vector<TestRunParams>& runs)
443 switch (type.getProfile())
445 case glu::PROFILE_CORE:
446 getTestRunsForGL(type, configs, runs);
448 case glu::PROFILE_ES:
449 getTestRunsForES(type, configs, runs);
452 throw std::runtime_error("Unknown context type");
458 void operator()(FILE* file) const
465 struct XmlWriterDeleter
467 void operator()(qpXmlWriter* writer) const
470 qpXmlWriter_destroy(writer);
474 static const char* getRunTypeName(glu::ApiType type)
476 if (type == glu::ApiType::es(2, 0))
478 else if (type == glu::ApiType::es(3, 0))
480 else if (type == glu::ApiType::es(3, 1))
482 else if (type == glu::ApiType::es(3, 2))
484 else if (type == glu::ApiType::core(3, 0))
486 else if (type == glu::ApiType::core(3, 1))
488 else if (type == glu::ApiType::core(3, 2))
490 else if (type == glu::ApiType::core(3, 3))
492 else if (type == glu::ApiType::core(4, 0))
494 else if (type == glu::ApiType::core(4, 1))
496 else if (type == glu::ApiType::core(4, 2))
498 else if (type == glu::ApiType::core(4, 3))
500 else if (type == glu::ApiType::core(4, 4))
502 else if (type == glu::ApiType::core(4, 5))
508 #define XML_CHECK(X) \
510 throw tcu::Exception("Writing XML failed")
512 static void writeRunSummary(const TestRunSummary& summary, const char* filename)
514 de::UniquePtr<FILE, FileDeleter> out(fopen(filename, "wb"));
516 throw tcu::Exception(string("Failed to open ") + filename);
518 de::UniquePtr<qpXmlWriter, XmlWriterDeleter> writer(qpXmlWriter_createFileWriter(out.get(), DE_FALSE, DE_FALSE));
520 throw std::bad_alloc();
522 XML_CHECK(qpXmlWriter_startDocument(writer.get()));
525 qpXmlAttribute attribs[2];
527 attribs[0] = qpSetStringAttrib("Type", getRunTypeName(summary.runType));
528 attribs[1] = qpSetBoolAttrib("Conformant", summary.isConformant ? DE_TRUE : DE_FALSE);
530 XML_CHECK(qpXmlWriter_startElement(writer.get(), "Summary", DE_LENGTH_OF_ARRAY(attribs), attribs));
535 qpXmlAttribute attribs[1];
536 attribs[0] = qpSetStringAttrib("FileName", summary.configLogFilename.c_str());
537 XML_CHECK(qpXmlWriter_startElement(writer.get(), "Configs", DE_LENGTH_OF_ARRAY(attribs), attribs) &&
538 qpXmlWriter_endElement(writer.get(), "Configs"));
541 // Record test run parameters (log filename & command line).
542 for (vector<TestRunParams>::const_iterator runIter = summary.runParams.begin(); runIter != summary.runParams.end();
546 qpXmlAttribute attribs[2];
548 for (vector<string>::const_iterator argIter = runIter->args.begin(); argIter != runIter->args.end(); ++argIter)
550 if (argIter != runIter->args.begin())
555 attribs[0] = qpSetStringAttrib("FileName", runIter->logFilename.c_str());
556 attribs[1] = qpSetStringAttrib("CmdLine", cmdLine.c_str());
558 XML_CHECK(qpXmlWriter_startElement(writer.get(), "TestRun", DE_LENGTH_OF_ARRAY(attribs), attribs) &&
559 qpXmlWriter_endElement(writer.get(), "TestRun"));
562 XML_CHECK(qpXmlWriter_endElement(writer.get(), "Summary"));
563 XML_CHECK(qpXmlWriter_endDocument(writer.get()));
568 TestRunner::TestRunner(tcu::Platform& platform, tcu::Archive& archive, const char* logDirPath, glu::ApiType type,
570 : m_platform(platform)
572 , m_logDirPath(logDirPath)
575 , m_iterState(ITERATE_INIT)
576 , m_curSession(DE_NULL)
577 , m_sessionsExecuted(0)
578 , m_sessionsPassed(0)
579 , m_sessionsFailed(0)
583 TestRunner::~TestRunner(void)
588 bool TestRunner::iterate(void)
594 m_iterState = (m_sessionIter != m_runSessions.end()) ? ITERATE_INIT_SESSION : ITERATE_DEINIT;
599 m_iterState = ITERATE_INIT;
602 case ITERATE_INIT_SESSION:
603 DE_ASSERT(m_sessionIter != m_runSessions.end());
604 initSession(*m_sessionIter);
605 if (m_flags & PRINT_SUMMARY)
606 m_iterState = ITERATE_DEINIT_SESSION;
608 m_iterState = ITERATE_ITERATE_SESSION;
611 case ITERATE_DEINIT_SESSION:
614 m_iterState = (m_sessionIter != m_runSessions.end()) ? ITERATE_INIT_SESSION : ITERATE_DEINIT;
617 case ITERATE_ITERATE_SESSION:
618 if (!iterateSession())
619 m_iterState = ITERATE_DEINIT_SESSION;
628 void TestRunner::init(void)
630 DE_ASSERT(m_runSessions.empty() && m_summary.runParams.empty());
632 tcu::print("Running %s conformance\n", glu::getApiTypeDescription(m_type));
634 m_summary.runType = m_type;
636 // Get list of configs to test.
637 ConfigList configList;
638 getDefaultConfigList(m_platform, m_type, configList);
640 tcu::print(" found %d compatible and %d excluded configs\n", (int)configList.configs.size(),
641 (int)configList.excludedConfigs.size());
645 const char* configLogFilename = "configs.qpa";
646 TestRunParams configRun;
648 configRun.logFilename = configLogFilename;
649 configRun.args.push_back("--deqp-case=CTS-Configs.*");
650 m_runSessions.push_back(configRun);
652 m_summary.configLogFilename = configLogFilename;
655 // Conformance test type specific runs
656 getTestRunParams(m_type, configList, m_runSessions);
658 // Record run params for summary.
659 for (std::vector<TestRunParams>::const_iterator runIter = m_runSessions.begin() + 1; runIter != m_runSessions.end();
661 m_summary.runParams.push_back(*runIter);
664 m_sessionIter = m_runSessions.begin();
667 void TestRunner::deinit(void)
670 bool isConformant = m_sessionsExecuted == m_sessionsPassed;
671 DE_ASSERT(m_sessionsExecuted == m_sessionsPassed + m_sessionsFailed);
672 tcu::print("\n%d/%d sessions passed, conformance test %s\n", m_sessionsPassed, m_sessionsExecuted,
673 isConformant ? "PASSED" : "FAILED");
675 m_summary.isConformant = isConformant;
677 // Write out summary.
678 writeRunSummary(m_summary, de::FilePath::join(m_logDirPath, "cts-run-summary.xml").getPath());
680 m_runSessions.clear();
684 void TestRunner::initSession(const TestRunParams& runParams)
686 DE_ASSERT(!m_curSession);
688 tcu::print("\n Test run %d / %d\n", (int)(m_sessionIter - m_runSessions.begin() + 1), (int)m_runSessions.size());
690 // Compute final args for run.
691 vector<string> args(runParams.args);
692 args.push_back(string("--deqp-log-filename=") + de::FilePath::join(m_logDirPath, runParams.logFilename).getPath());
694 if (!(m_flags & VERBOSE_IMAGES))
695 args.push_back("--deqp-log-images=disable");
697 if (!(m_flags & VERBOSE_SHADERS))
698 args.push_back("--deqp-log-shader-sources=disable");
700 std::ostringstream ostr;
701 std::ostream_iterator<string> out_it(ostr, ", ");
702 std::copy(args.begin(), args.end(), out_it);
703 tcu::print("\n Config: %s \n\n", ostr.str().c_str());
705 // Translate to argc, argv
706 vector<const char*> argv;
707 argv.push_back("cts-runner"); // Dummy binary name
708 for (vector<string>::const_iterator i = args.begin(); i != args.end(); i++)
709 argv.push_back(i->c_str());
712 m_curSession = new RunSession(m_platform, m_archive, (int)argv.size(), &argv[0]);
715 void TestRunner::deinitSession(void)
717 DE_ASSERT(m_curSession);
720 // \note NotSupported is treated as pass.
721 const tcu::TestRunStatus& result = m_curSession->getResult();
723 result.numExecuted == (result.numPassed + result.numNotSupported + result.numWarnings) && result.isComplete;
725 DE_ASSERT(result.numExecuted == result.numPassed + result.numFailed + result.numNotSupported + result.numWarnings);
727 m_sessionsExecuted += 1;
728 (isOk ? m_sessionsPassed : m_sessionsFailed) += 1;
731 m_curSession = DE_NULL;
734 inline bool TestRunner::iterateSession(void)
736 return m_curSession->iterate();