SkQP: refatctor C++ bits.
* C++ code moved into tools/skqp/src/.
* State held with single SkQP class.
* gmkb functions moved to skqp_model.{h,cpp}
* model no longer knows about report format.
* skqp_main and skqp_lib no longer have globals
* jni code has fewer globals.
* skqp_main no longer uses googletest.
* AssetMng returns SkData, not a SkStream.
* Add jitter tool.
* dump GPU information into grdump.txt
* JUnit puts report in directory with timestamp.
* Document SkQP Render Test Algorithm.
* GPU driver correctness workarounds always off
* cut_release tool for assembling models
* make_rendertests_list.py to help cut_release
* make_gmkb.go emits a list of models
CQ_INCLUDE_TRYBOTS=skia.primary:Build-Debian9-Clang-x86-devrel-Android_SKQP
Change-Id: I7d4f0c24592b1f64be0088578a3f1a0bc366dd4d
Reviewed-on: https://skia-review.googlesource.com/c/110420
Reviewed-by: Hal Canary <halcanary@google.com>
Commit-Queue: Hal Canary <halcanary@google.com>
diff --git a/tools/skqp/src/skqp_main.cpp b/tools/skqp/src/skqp_main.cpp
new file mode 100644
index 0000000..2737a9a
--- /dev/null
+++ b/tools/skqp/src/skqp_main.cpp
@@ -0,0 +1,145 @@
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <iostream>
+#include <sys/stat.h>
+
+#include "skqp.h"
+
+#include "Resources.h"
+#include "SkData.h"
+#include "SkOSFile.h"
+
+////////////////////////////////////////////////////////////////////////////////
+
+namespace {
+class StdAssetManager : public SkQPAssetManager {
+public:
+ StdAssetManager(const char* p) : fPrefix(p) {
+ SkASSERT(!fPrefix.empty());
+ //TODO(halcanary): does this need to be changed if I run SkQP in Windows?
+ fPrefix += "/";
+ }
+ sk_sp<SkData> open(const char* path) override {
+ return SkData::MakeFromFileName((fPrefix + path).c_str());
+ }
+private:
+ std::string fPrefix;
+};
+}
+
+static constexpr char kSkipUsage[] =
+ " TEST_MATCH_RULES:"
+ " [~][^]substring[$] [...] of name to run.\n"
+ " Multiple matches may be separated by spaces.\n"
+ " ~ causes a matching name to always be skipped\n"
+ " ^ requires the start of the name to match\n"
+ " $ requires the end of the name to match\n"
+ " ^ and $ requires an exact match\n"
+ " If a name does not match any list entry,\n"
+ " it is skipped unless some list entry starts with ~\n";
+
+static bool should_skip(const char* const* rules, size_t count, const char* name) {
+ size_t testLen = strlen(name);
+ bool anyExclude = count == 0;
+ for (size_t i = 0; i < count; ++i) {
+ const char* matchName = rules[i];
+ size_t matchLen = strlen(matchName);
+ bool matchExclude, matchStart, matchEnd;
+ if ((matchExclude = matchName[0] == '~')) {
+ anyExclude = true;
+ matchName++;
+ matchLen--;
+ }
+ if ((matchStart = matchName[0] == '^')) {
+ matchName++;
+ matchLen--;
+ }
+ if ((matchEnd = matchName[matchLen - 1] == '$')) {
+ matchLen--;
+ }
+ if (matchStart ? (!matchEnd || matchLen == testLen)
+ && strncmp(name, matchName, matchLen) == 0
+ : matchEnd ? matchLen <= testLen
+ && strncmp(name + testLen - matchLen, matchName, matchLen) == 0
+ : strstr(name, matchName) != nullptr) {
+ return matchExclude;
+ }
+ }
+ return !anyExclude;
+}
+
+int main(int argc, char** argv) {
+ if (argc < 3) {
+ std::cerr << "Usage:\n " << argv[0]
+ << " ASSET_DIRECTORY_PATH SKQP_REPORT_PATH [TEST_MATCH_RULES]\n"
+ << kSkipUsage << '\n';
+ return 1;
+ }
+ SetResourcePath((std::string(argv[1]) + "/resources").c_str());
+ if (!sk_mkdir(argv[2])) {
+ std::cerr << "sk_mkdir(" << argv[2] << ") failed.\n";
+ return 2;
+ }
+ StdAssetManager mgr(argv[1]);
+ SkQP skqp;
+ skqp.init(&mgr, argv[2]);
+ int ret = 0;
+
+ const char* const* matchRules = &argv[3];
+ size_t matchRulesCount = (size_t)(argc - 3);
+
+ // Rendering Tests
+ std::ostream& out = std::cout;
+ for (auto backend : skqp.getSupportedBackends()) {
+ auto testPrefix = std::string(SkQP::GetBackendName(backend)) + "_";
+ for (auto gmFactory : skqp.getGMs()) {
+ auto testName = testPrefix + SkQP::GetGMName(gmFactory);
+ if (should_skip(matchRules, matchRulesCount, testName.c_str())) {
+ continue;
+ }
+ out << "Starting: " << testName << std::endl;
+ SkQP::RenderOutcome outcome;
+ std::string except;
+
+ std::tie(outcome, except) = skqp.evaluateGM(backend, gmFactory);
+ if (!except.empty()) {
+ out << "ERROR: " << testName << " (" << except << ")\n";
+ ret = 1;
+ } else if (outcome.fMaxError != 0) {
+ out << "FAILED: " << testName << " (" << outcome.fMaxError << ")\n";
+ ret = 1;
+ } else {
+ out << "Passed: " << testName << "\n";
+ }
+ out.flush();
+ }
+ }
+
+ // Unit Tests
+ for (auto test : skqp.getUnitTests()) {
+ auto testName = std::string("unitTest_") + SkQP::GetUnitTestName(test);
+ if (should_skip(matchRules, matchRulesCount, testName.c_str())) {
+ continue;
+ }
+ out << "Starting test: " << testName << std::endl;
+ std::vector<std::string> errors = skqp.executeTest(test);
+ if (!errors.empty()) {
+ out << "TEST FAILED (" << errors.size() << "): " << testName << "\n";
+ for (const std::string& error : errors) {
+ out << error << "\n";
+ }
+ ret = 1;
+ } else {
+ out << "Test passed: " << testName << "\n";
+ }
+ out.flush();
+ }
+ skqp.makeReport();
+
+ return ret;
+}