1cb93a386Sopenharmony_ci/* 2cb93a386Sopenharmony_ci * Copyright 2018 Google Inc. 3cb93a386Sopenharmony_ci * 4cb93a386Sopenharmony_ci * Use of this source code is governed by a BSD-style license that can be 5cb93a386Sopenharmony_ci * found in the LICENSE file. 6cb93a386Sopenharmony_ci */ 7cb93a386Sopenharmony_ci 8cb93a386Sopenharmony_ci#include "tools/skqp/src/skqp.h" 9cb93a386Sopenharmony_ci 10cb93a386Sopenharmony_ci#include "gm/gm.h" 11cb93a386Sopenharmony_ci#include "include/core/SkFontStyle.h" 12cb93a386Sopenharmony_ci#include "include/core/SkGraphics.h" 13cb93a386Sopenharmony_ci#include "include/core/SkStream.h" 14cb93a386Sopenharmony_ci#include "include/core/SkSurface.h" 15cb93a386Sopenharmony_ci#include "include/encode/SkPngEncoder.h" 16cb93a386Sopenharmony_ci#include "include/gpu/GrContextOptions.h" 17cb93a386Sopenharmony_ci#include "include/gpu/GrDirectContext.h" 18cb93a386Sopenharmony_ci#include "include/private/SkImageInfoPriv.h" 19cb93a386Sopenharmony_ci#include "src/core/SkFontMgrPriv.h" 20cb93a386Sopenharmony_ci#include "src/core/SkOSFile.h" 21cb93a386Sopenharmony_ci#include "src/core/SkStreamPriv.h" 22cb93a386Sopenharmony_ci#include "src/utils/SkOSPath.h" 23cb93a386Sopenharmony_ci#include "tests/Test.h" 24cb93a386Sopenharmony_ci#include "tools/fonts/TestFontMgr.h" 25cb93a386Sopenharmony_ci#ifdef SK_GL 26cb93a386Sopenharmony_ci#include "tools/gpu/gl/GLTestContext.h" 27cb93a386Sopenharmony_ci#endif 28cb93a386Sopenharmony_ci#ifdef SK_VULKAN 29cb93a386Sopenharmony_ci#include "tools/gpu/vk/VkTestContext.h" 30cb93a386Sopenharmony_ci#endif 31cb93a386Sopenharmony_ci 32cb93a386Sopenharmony_ci#include <limits.h> 33cb93a386Sopenharmony_ci#include <algorithm> 34cb93a386Sopenharmony_ci#include <cinttypes> 35cb93a386Sopenharmony_ci#include <sstream> 36cb93a386Sopenharmony_ci 37cb93a386Sopenharmony_ci#include "tools/skqp/src/skqp_model.h" 38cb93a386Sopenharmony_ci 39cb93a386Sopenharmony_ci#define IMAGES_DIRECTORY_PATH "images" 40cb93a386Sopenharmony_ci#define PATH_MAX_PNG "max.png" 41cb93a386Sopenharmony_ci#define PATH_MIN_PNG "min.png" 42cb93a386Sopenharmony_ci#define PATH_IMG_PNG "image.png" 43cb93a386Sopenharmony_ci#define PATH_ERR_PNG "errors.png" 44cb93a386Sopenharmony_ci#define PATH_MODEL "model" 45cb93a386Sopenharmony_ci 46cb93a386Sopenharmony_cistatic constexpr char kRenderTestCSVReport[] = "out.csv"; 47cb93a386Sopenharmony_cistatic constexpr char kRenderTestReportPath[] = "report.html"; 48cb93a386Sopenharmony_cistatic constexpr char kDefaultRenderTestsPath[] = "skqp/rendertests.txt"; 49cb93a386Sopenharmony_cistatic constexpr char kUnitTestReportPath[] = "unit_tests.txt"; 50cb93a386Sopenharmony_cistatic constexpr char kUnitTestsPath[] = "skqp/unittests.txt"; 51cb93a386Sopenharmony_ci 52cb93a386Sopenharmony_ci// Kind of like Python's readlines(), but without any allocation. 53cb93a386Sopenharmony_ci// Calls f() on each line. 54cb93a386Sopenharmony_ci// F is [](const char*, size_t) -> void 55cb93a386Sopenharmony_citemplate <typename F> 56cb93a386Sopenharmony_cistatic void readlines(const void* data, size_t size, F f) { 57cb93a386Sopenharmony_ci const char* start = (const char*)data; 58cb93a386Sopenharmony_ci const char* end = start + size; 59cb93a386Sopenharmony_ci const char* ptr = start; 60cb93a386Sopenharmony_ci while (ptr < end) { 61cb93a386Sopenharmony_ci while (*ptr++ != '\n' && ptr < end) {} 62cb93a386Sopenharmony_ci size_t len = ptr - start; 63cb93a386Sopenharmony_ci f(start, len); 64cb93a386Sopenharmony_ci start = ptr; 65cb93a386Sopenharmony_ci } 66cb93a386Sopenharmony_ci} 67cb93a386Sopenharmony_ci 68cb93a386Sopenharmony_cistatic void get_unit_tests(SkQPAssetManager* mgr, std::vector<SkQP::UnitTest>* unitTests) { 69cb93a386Sopenharmony_ci std::unordered_set<std::string> testset; 70cb93a386Sopenharmony_ci auto insert = [&testset](const char* s, size_t l) { 71cb93a386Sopenharmony_ci SkASSERT(l > 1) ; 72cb93a386Sopenharmony_ci if (l > 0 && s[l - 1] == '\n') { // strip line endings. 73cb93a386Sopenharmony_ci --l; 74cb93a386Sopenharmony_ci } 75cb93a386Sopenharmony_ci if (l > 0) { // only add non-empty strings. 76cb93a386Sopenharmony_ci testset.insert(std::string(s, l)); 77cb93a386Sopenharmony_ci } 78cb93a386Sopenharmony_ci }; 79cb93a386Sopenharmony_ci if (sk_sp<SkData> dat = mgr->open(kUnitTestsPath)) { 80cb93a386Sopenharmony_ci readlines(dat->data(), dat->size(), insert); 81cb93a386Sopenharmony_ci } 82cb93a386Sopenharmony_ci for (const skiatest::Test& test : skiatest::TestRegistry::Range()) { 83cb93a386Sopenharmony_ci if ((testset.empty() || testset.count(std::string(test.fName)) > 0) && test.fNeedsGpu) { 84cb93a386Sopenharmony_ci unitTests->push_back(&test); 85cb93a386Sopenharmony_ci } 86cb93a386Sopenharmony_ci } 87cb93a386Sopenharmony_ci auto lt = [](SkQP::UnitTest u, SkQP::UnitTest v) { return strcmp(u->fName, v->fName) < 0; }; 88cb93a386Sopenharmony_ci std::sort(unitTests->begin(), unitTests->end(), lt); 89cb93a386Sopenharmony_ci} 90cb93a386Sopenharmony_ci 91cb93a386Sopenharmony_cistatic void get_render_tests(SkQPAssetManager* mgr, 92cb93a386Sopenharmony_ci const char *renderTestsIn, 93cb93a386Sopenharmony_ci std::vector<SkQP::GMFactory>* gmlist, 94cb93a386Sopenharmony_ci std::unordered_map<std::string, int64_t>* gmThresholds) { 95cb93a386Sopenharmony_ci // Runs all render tests if the |renderTests| file can't be found or is empty. 96cb93a386Sopenharmony_ci const char *renderTests = (renderTestsIn && renderTestsIn[0]) ? 97cb93a386Sopenharmony_ci renderTestsIn : kDefaultRenderTestsPath; 98cb93a386Sopenharmony_ci auto insert = [gmThresholds](const char* s, size_t l) { 99cb93a386Sopenharmony_ci SkASSERT(l > 1) ; 100cb93a386Sopenharmony_ci if (l > 0 && s[l - 1] == '\n') { // strip line endings. 101cb93a386Sopenharmony_ci --l; 102cb93a386Sopenharmony_ci } 103cb93a386Sopenharmony_ci if (l == 0) { 104cb93a386Sopenharmony_ci return; 105cb93a386Sopenharmony_ci } 106cb93a386Sopenharmony_ci const char* end = s + l; 107cb93a386Sopenharmony_ci const char* ptr = s; 108cb93a386Sopenharmony_ci constexpr char kDelimeter = ','; 109cb93a386Sopenharmony_ci while (ptr < end && *ptr != kDelimeter) { ++ptr; } 110cb93a386Sopenharmony_ci if (ptr + 1 >= end) { 111cb93a386Sopenharmony_ci SkASSERT(false); // missing delimeter 112cb93a386Sopenharmony_ci return; 113cb93a386Sopenharmony_ci } 114cb93a386Sopenharmony_ci std::string key(s, ptr - s); 115cb93a386Sopenharmony_ci ++ptr; // skip delimeter 116cb93a386Sopenharmony_ci std::string number(ptr, end - ptr); // null-terminated copy. 117cb93a386Sopenharmony_ci int64_t value = 0; 118cb93a386Sopenharmony_ci if (1 != sscanf(number.c_str(), "%" SCNd64 , &value)) { 119cb93a386Sopenharmony_ci SkASSERT(false); // Not a number 120cb93a386Sopenharmony_ci return; 121cb93a386Sopenharmony_ci } 122cb93a386Sopenharmony_ci gmThresholds->insert({std::move(key), value}); // (*gmThresholds)[s] = value; 123cb93a386Sopenharmony_ci }; 124cb93a386Sopenharmony_ci if (sk_sp<SkData> dat = mgr->open(renderTests)) { 125cb93a386Sopenharmony_ci readlines(dat->data(), dat->size(), insert); 126cb93a386Sopenharmony_ci } 127cb93a386Sopenharmony_ci using GmAndName = std::pair<SkQP::GMFactory, std::string>; 128cb93a386Sopenharmony_ci std::vector<GmAndName> gmsWithNames; 129cb93a386Sopenharmony_ci for (skiagm::GMFactory f : skiagm::GMRegistry::Range()) { 130cb93a386Sopenharmony_ci std::string name = SkQP::GetGMName(f); 131cb93a386Sopenharmony_ci if ((gmThresholds->empty() || gmThresholds->count(name) > 0)) { 132cb93a386Sopenharmony_ci gmsWithNames.push_back(std::make_pair(f, std::move(name))); 133cb93a386Sopenharmony_ci } 134cb93a386Sopenharmony_ci } 135cb93a386Sopenharmony_ci std::sort(gmsWithNames.begin(), gmsWithNames.end(), 136cb93a386Sopenharmony_ci [](GmAndName u, GmAndName v) { return u.second < v.second; }); 137cb93a386Sopenharmony_ci gmlist->reserve(gmsWithNames.size()); 138cb93a386Sopenharmony_ci for (const GmAndName& gmn : gmsWithNames) { 139cb93a386Sopenharmony_ci gmlist->push_back(gmn.first); 140cb93a386Sopenharmony_ci } 141cb93a386Sopenharmony_ci} 142cb93a386Sopenharmony_ci 143cb93a386Sopenharmony_cistatic std::unique_ptr<sk_gpu_test::TestContext> make_test_context(SkQP::SkiaBackend backend) { 144cb93a386Sopenharmony_ci using U = std::unique_ptr<sk_gpu_test::TestContext>; 145cb93a386Sopenharmony_ci switch (backend) { 146cb93a386Sopenharmony_ci// TODO(halcanary): Fuchsia will have SK_SUPPORT_GPU and SK_VULKAN, but *not* SK_GL. 147cb93a386Sopenharmony_ci#ifdef SK_GL 148cb93a386Sopenharmony_ci case SkQP::SkiaBackend::kGL: 149cb93a386Sopenharmony_ci return U(sk_gpu_test::CreatePlatformGLTestContext(kGL_GrGLStandard, nullptr)); 150cb93a386Sopenharmony_ci case SkQP::SkiaBackend::kGLES: 151cb93a386Sopenharmony_ci return U(sk_gpu_test::CreatePlatformGLTestContext(kGLES_GrGLStandard, nullptr)); 152cb93a386Sopenharmony_ci#endif 153cb93a386Sopenharmony_ci#ifdef SK_VULKAN 154cb93a386Sopenharmony_ci case SkQP::SkiaBackend::kVulkan: 155cb93a386Sopenharmony_ci return U(sk_gpu_test::CreatePlatformVkTestContext(nullptr)); 156cb93a386Sopenharmony_ci#endif 157cb93a386Sopenharmony_ci default: 158cb93a386Sopenharmony_ci return nullptr; 159cb93a386Sopenharmony_ci } 160cb93a386Sopenharmony_ci} 161cb93a386Sopenharmony_ci 162cb93a386Sopenharmony_cistatic GrContextOptions context_options(skiagm::GM* gm = nullptr) { 163cb93a386Sopenharmony_ci GrContextOptions grContextOptions; 164cb93a386Sopenharmony_ci grContextOptions.fAllowPathMaskCaching = true; 165cb93a386Sopenharmony_ci grContextOptions.fDisableDriverCorrectnessWorkarounds = true; 166cb93a386Sopenharmony_ci if (gm) { 167cb93a386Sopenharmony_ci gm->modifyGrContextOptions(&grContextOptions); 168cb93a386Sopenharmony_ci } 169cb93a386Sopenharmony_ci return grContextOptions; 170cb93a386Sopenharmony_ci} 171cb93a386Sopenharmony_ci 172cb93a386Sopenharmony_cistatic std::vector<SkQP::SkiaBackend> get_backends() { 173cb93a386Sopenharmony_ci std::vector<SkQP::SkiaBackend> result; 174cb93a386Sopenharmony_ci SkQP::SkiaBackend backends[] = { 175cb93a386Sopenharmony_ci #ifdef SK_GL 176cb93a386Sopenharmony_ci #ifndef SK_BUILD_FOR_ANDROID 177cb93a386Sopenharmony_ci SkQP::SkiaBackend::kGL, // Used for testing on desktop machines. 178cb93a386Sopenharmony_ci #endif 179cb93a386Sopenharmony_ci SkQP::SkiaBackend::kGLES, 180cb93a386Sopenharmony_ci #endif // SK_GL 181cb93a386Sopenharmony_ci #ifdef SK_VULKAN 182cb93a386Sopenharmony_ci SkQP::SkiaBackend::kVulkan, 183cb93a386Sopenharmony_ci #endif 184cb93a386Sopenharmony_ci }; 185cb93a386Sopenharmony_ci for (SkQP::SkiaBackend backend : backends) { 186cb93a386Sopenharmony_ci std::unique_ptr<sk_gpu_test::TestContext> testCtx = make_test_context(backend); 187cb93a386Sopenharmony_ci if (testCtx) { 188cb93a386Sopenharmony_ci testCtx->makeCurrent(); 189cb93a386Sopenharmony_ci if (nullptr != testCtx->makeContext(context_options())) { 190cb93a386Sopenharmony_ci result.push_back(backend); 191cb93a386Sopenharmony_ci } 192cb93a386Sopenharmony_ci } 193cb93a386Sopenharmony_ci } 194cb93a386Sopenharmony_ci SkASSERT_RELEASE(result.size() > 0); 195cb93a386Sopenharmony_ci return result; 196cb93a386Sopenharmony_ci} 197cb93a386Sopenharmony_ci 198cb93a386Sopenharmony_cistatic void print_backend_info(const char* dstPath, 199cb93a386Sopenharmony_ci const std::vector<SkQP::SkiaBackend>& backends) { 200cb93a386Sopenharmony_ci#ifdef SK_ENABLE_DUMP_GPU 201cb93a386Sopenharmony_ci SkFILEWStream out(dstPath); 202cb93a386Sopenharmony_ci out.writeText("[\n"); 203cb93a386Sopenharmony_ci for (SkQP::SkiaBackend backend : backends) { 204cb93a386Sopenharmony_ci if (std::unique_ptr<sk_gpu_test::TestContext> testCtx = make_test_context(backend)) { 205cb93a386Sopenharmony_ci testCtx->makeCurrent(); 206cb93a386Sopenharmony_ci if (sk_sp<GrDirectContext> ctx = testCtx->makeContext(context_options())) { 207cb93a386Sopenharmony_ci SkString info = ctx->dump(); 208cb93a386Sopenharmony_ci // remove null 209cb93a386Sopenharmony_ci out.write(info.c_str(), info.size()); 210cb93a386Sopenharmony_ci out.writeText(",\n"); 211cb93a386Sopenharmony_ci } 212cb93a386Sopenharmony_ci } 213cb93a386Sopenharmony_ci } 214cb93a386Sopenharmony_ci out.writeText("]\n"); 215cb93a386Sopenharmony_ci#endif 216cb93a386Sopenharmony_ci} 217cb93a386Sopenharmony_ci 218cb93a386Sopenharmony_cistatic void encode_png(const SkBitmap& src, const std::string& dst) { 219cb93a386Sopenharmony_ci SkFILEWStream wStream(dst.c_str()); 220cb93a386Sopenharmony_ci SkPngEncoder::Options options; 221cb93a386Sopenharmony_ci bool success = wStream.isValid() && SkPngEncoder::Encode(&wStream, src.pixmap(), options); 222cb93a386Sopenharmony_ci SkASSERT_RELEASE(success); 223cb93a386Sopenharmony_ci} 224cb93a386Sopenharmony_ci 225cb93a386Sopenharmony_cistatic void write_to_file(const sk_sp<SkData>& src, const std::string& dst) { 226cb93a386Sopenharmony_ci SkFILEWStream wStream(dst.c_str()); 227cb93a386Sopenharmony_ci bool success = wStream.isValid() && wStream.write(src->data(), src->size()); 228cb93a386Sopenharmony_ci SkASSERT_RELEASE(success); 229cb93a386Sopenharmony_ci} 230cb93a386Sopenharmony_ci 231cb93a386Sopenharmony_ci//////////////////////////////////////////////////////////////////////////////// 232cb93a386Sopenharmony_ci 233cb93a386Sopenharmony_ciconst char* SkQP::GetBackendName(SkQP::SkiaBackend b) { 234cb93a386Sopenharmony_ci switch (b) { 235cb93a386Sopenharmony_ci case SkQP::SkiaBackend::kGL: return "gl"; 236cb93a386Sopenharmony_ci case SkQP::SkiaBackend::kGLES: return "gles"; 237cb93a386Sopenharmony_ci case SkQP::SkiaBackend::kVulkan: return "vk"; 238cb93a386Sopenharmony_ci } 239cb93a386Sopenharmony_ci return ""; 240cb93a386Sopenharmony_ci} 241cb93a386Sopenharmony_ci 242cb93a386Sopenharmony_cistd::string SkQP::GetGMName(SkQP::GMFactory f) { 243cb93a386Sopenharmony_ci std::unique_ptr<skiagm::GM> gm(f ? f() : nullptr); 244cb93a386Sopenharmony_ci return std::string(gm ? gm->getName() : ""); 245cb93a386Sopenharmony_ci} 246cb93a386Sopenharmony_ci 247cb93a386Sopenharmony_ciconst char* SkQP::GetUnitTestName(SkQP::UnitTest t) { return t->fName; } 248cb93a386Sopenharmony_ci 249cb93a386Sopenharmony_ciSkQP::SkQP() {} 250cb93a386Sopenharmony_ci 251cb93a386Sopenharmony_ciSkQP::~SkQP() {} 252cb93a386Sopenharmony_ci 253cb93a386Sopenharmony_civoid SkQP::init(SkQPAssetManager* am, const char* renderTests, const char* reportDirectory) { 254cb93a386Sopenharmony_ci SkASSERT_RELEASE(!fAssetManager); 255cb93a386Sopenharmony_ci SkASSERT_RELEASE(am); 256cb93a386Sopenharmony_ci fAssetManager = am; 257cb93a386Sopenharmony_ci fReportDirectory = reportDirectory; 258cb93a386Sopenharmony_ci 259cb93a386Sopenharmony_ci SkGraphics::Init(); 260cb93a386Sopenharmony_ci gSkFontMgr_DefaultFactory = &ToolUtils::MakePortableFontMgr; 261cb93a386Sopenharmony_ci 262cb93a386Sopenharmony_ci get_render_tests(fAssetManager, renderTests, &fGMs, &fGMThresholds); 263cb93a386Sopenharmony_ci /* If the file "skqp/unittests.txt" does not exist or is empty, run all gpu 264cb93a386Sopenharmony_ci unit tests. Otherwise only run tests mentioned in that file. */ 265cb93a386Sopenharmony_ci get_unit_tests(fAssetManager, &fUnitTests); 266cb93a386Sopenharmony_ci fSupportedBackends = get_backends(); 267cb93a386Sopenharmony_ci 268cb93a386Sopenharmony_ci print_backend_info((fReportDirectory + "/grdump.txt").c_str(), fSupportedBackends); 269cb93a386Sopenharmony_ci} 270cb93a386Sopenharmony_ci 271cb93a386Sopenharmony_cistd::tuple<SkQP::RenderOutcome, std::string> SkQP::evaluateGM(SkQP::SkiaBackend backend, 272cb93a386Sopenharmony_ci SkQP::GMFactory gmFact) { 273cb93a386Sopenharmony_ci SkASSERT_RELEASE(fAssetManager); 274cb93a386Sopenharmony_ci static constexpr SkQP::RenderOutcome kError = {INT_MAX, INT_MAX, INT64_MAX}; 275cb93a386Sopenharmony_ci static constexpr SkQP::RenderOutcome kPass = {0, 0, 0}; 276cb93a386Sopenharmony_ci 277cb93a386Sopenharmony_ci std::unique_ptr<sk_gpu_test::TestContext> testCtx = make_test_context(backend); 278cb93a386Sopenharmony_ci if (!testCtx) { 279cb93a386Sopenharmony_ci return std::make_tuple(kError, "Skia Failure: test context"); 280cb93a386Sopenharmony_ci } 281cb93a386Sopenharmony_ci testCtx->makeCurrent(); 282cb93a386Sopenharmony_ci 283cb93a386Sopenharmony_ci SkASSERT(gmFact); 284cb93a386Sopenharmony_ci std::unique_ptr<skiagm::GM> gm(gmFact()); 285cb93a386Sopenharmony_ci SkASSERT(gm); 286cb93a386Sopenharmony_ci const char* const name = gm->getName(); 287cb93a386Sopenharmony_ci const SkISize size = gm->getISize(); 288cb93a386Sopenharmony_ci const int w = size.width(); 289cb93a386Sopenharmony_ci const int h = size.height(); 290cb93a386Sopenharmony_ci const SkImageInfo info = 291cb93a386Sopenharmony_ci SkImageInfo::Make(w, h, skqp::kColorType, kPremul_SkAlphaType, nullptr); 292cb93a386Sopenharmony_ci const SkSurfaceProps props(0, kRGB_H_SkPixelGeometry); 293cb93a386Sopenharmony_ci 294cb93a386Sopenharmony_ci sk_sp<SkSurface> surf = SkSurface::MakeRenderTarget( 295cb93a386Sopenharmony_ci testCtx->makeContext(context_options(gm.get())).get(), 296cb93a386Sopenharmony_ci SkBudgeted::kNo, info, 0, &props); 297cb93a386Sopenharmony_ci if (!surf) { 298cb93a386Sopenharmony_ci return std::make_tuple(kError, "Skia Failure: gr-context"); 299cb93a386Sopenharmony_ci } 300cb93a386Sopenharmony_ci gm->draw(surf->getCanvas()); 301cb93a386Sopenharmony_ci 302cb93a386Sopenharmony_ci SkBitmap image; 303cb93a386Sopenharmony_ci image.allocPixels(SkImageInfo::Make(w, h, skqp::kColorType, skqp::kAlphaType)); 304cb93a386Sopenharmony_ci 305cb93a386Sopenharmony_ci // SkColorTypeBytesPerPixel should be constexpr, but is not. 306cb93a386Sopenharmony_ci SkASSERT(SkColorTypeBytesPerPixel(skqp::kColorType) == sizeof(uint32_t)); 307cb93a386Sopenharmony_ci // Call readPixels because we need to compare pixels. 308cb93a386Sopenharmony_ci if (!surf->readPixels(image.pixmap(), 0, 0)) { 309cb93a386Sopenharmony_ci return std::make_tuple(kError, "Skia Failure: read pixels"); 310cb93a386Sopenharmony_ci } 311cb93a386Sopenharmony_ci int64_t passingThreshold = fGMThresholds.empty() ? -1 : fGMThresholds[std::string(name)]; 312cb93a386Sopenharmony_ci 313cb93a386Sopenharmony_ci if (-1 == passingThreshold) { 314cb93a386Sopenharmony_ci return std::make_tuple(kPass, ""); 315cb93a386Sopenharmony_ci } 316cb93a386Sopenharmony_ci skqp::ModelResult modelResult = 317cb93a386Sopenharmony_ci skqp::CheckAgainstModel(name, image.pixmap(), fAssetManager); 318cb93a386Sopenharmony_ci 319cb93a386Sopenharmony_ci if (!modelResult.fErrorString.empty()) { 320cb93a386Sopenharmony_ci return std::make_tuple(kError, std::move(modelResult.fErrorString)); 321cb93a386Sopenharmony_ci } 322cb93a386Sopenharmony_ci fRenderResults.push_back(SkQP::RenderResult{backend, gmFact, modelResult.fOutcome}); 323cb93a386Sopenharmony_ci if (modelResult.fOutcome.fMaxError <= passingThreshold) { 324cb93a386Sopenharmony_ci return std::make_tuple(kPass, ""); 325cb93a386Sopenharmony_ci } 326cb93a386Sopenharmony_ci std::string imagesDirectory = fReportDirectory + "/" IMAGES_DIRECTORY_PATH; 327cb93a386Sopenharmony_ci if (!sk_mkdir(imagesDirectory.c_str())) { 328cb93a386Sopenharmony_ci SkDebugf("ERROR: sk_mkdir('%s');\n", imagesDirectory.c_str()); 329cb93a386Sopenharmony_ci return std::make_tuple(modelResult.fOutcome, ""); 330cb93a386Sopenharmony_ci } 331cb93a386Sopenharmony_ci std::ostringstream tmp; 332cb93a386Sopenharmony_ci tmp << imagesDirectory << '/' << SkQP::GetBackendName(backend) << '_' << name << '_'; 333cb93a386Sopenharmony_ci std::string imagesPathPrefix1 = tmp.str(); 334cb93a386Sopenharmony_ci tmp = std::ostringstream(); 335cb93a386Sopenharmony_ci tmp << imagesDirectory << '/' << PATH_MODEL << '_' << name << '_'; 336cb93a386Sopenharmony_ci std::string imagesPathPrefix2 = tmp.str(); 337cb93a386Sopenharmony_ci encode_png(image, imagesPathPrefix1 + PATH_IMG_PNG); 338cb93a386Sopenharmony_ci encode_png(modelResult.fErrors, imagesPathPrefix1 + PATH_ERR_PNG); 339cb93a386Sopenharmony_ci write_to_file(modelResult.fMaxPng, imagesPathPrefix2 + PATH_MAX_PNG); 340cb93a386Sopenharmony_ci write_to_file(modelResult.fMinPng, imagesPathPrefix2 + PATH_MIN_PNG); 341cb93a386Sopenharmony_ci return std::make_tuple(modelResult.fOutcome, ""); 342cb93a386Sopenharmony_ci} 343cb93a386Sopenharmony_ci 344cb93a386Sopenharmony_cistd::vector<std::string> SkQP::executeTest(SkQP::UnitTest test) { 345cb93a386Sopenharmony_ci SkASSERT_RELEASE(fAssetManager); 346cb93a386Sopenharmony_ci struct : public skiatest::Reporter { 347cb93a386Sopenharmony_ci std::vector<std::string> fErrors; 348cb93a386Sopenharmony_ci void reportFailed(const skiatest::Failure& failure) override { 349cb93a386Sopenharmony_ci SkString desc = failure.toString(); 350cb93a386Sopenharmony_ci fErrors.push_back(std::string(desc.c_str(), desc.size())); 351cb93a386Sopenharmony_ci } 352cb93a386Sopenharmony_ci } r; 353cb93a386Sopenharmony_ci GrContextOptions options; 354cb93a386Sopenharmony_ci options.fDisableDriverCorrectnessWorkarounds = true; 355cb93a386Sopenharmony_ci if (test->fContextOptionsProc) { 356cb93a386Sopenharmony_ci test->fContextOptionsProc(&options); 357cb93a386Sopenharmony_ci } 358cb93a386Sopenharmony_ci test->fProc(&r, options); 359cb93a386Sopenharmony_ci fUnitTestResults.push_back(UnitTestResult{test, r.fErrors}); 360cb93a386Sopenharmony_ci return r.fErrors; 361cb93a386Sopenharmony_ci} 362cb93a386Sopenharmony_ci 363cb93a386Sopenharmony_ci//////////////////////////////////////////////////////////////////////////////// 364cb93a386Sopenharmony_ci 365cb93a386Sopenharmony_cistatic constexpr char kDocHead[] = 366cb93a386Sopenharmony_ci "<!doctype html>\n" 367cb93a386Sopenharmony_ci "<html lang=\"en\">\n" 368cb93a386Sopenharmony_ci "<head>\n" 369cb93a386Sopenharmony_ci "<meta charset=\"UTF-8\">\n" 370cb93a386Sopenharmony_ci "<title>SkQP Report</title>\n" 371cb93a386Sopenharmony_ci "<style>\n" 372cb93a386Sopenharmony_ci "img { max-width:48%; border:1px green solid;\n" 373cb93a386Sopenharmony_ci " image-rendering: pixelated;\n" 374cb93a386Sopenharmony_ci " background-image:url('data:image/png;base64,iVBORw0KGgoA" 375cb93a386Sopenharmony_ci "AAANSUhEUgAAABAAAAAQCAAAAAA6mKC9AAAAAXNSR0IArs4c6QAAAAJiS0dEAP+H" 376cb93a386Sopenharmony_ci "j8y/AAAACXBIWXMAAA7DAAAOwwHHb6hkAAAAB3RJTUUH3gUBEi4DGRAQYgAAAB1J" 377cb93a386Sopenharmony_ci "REFUGNNjfMoAAVJQmokBDdBHgPE/lPFsYN0BABdaAwN6tehMAAAAAElFTkSuQmCC" 378cb93a386Sopenharmony_ci "'); }\n" 379cb93a386Sopenharmony_ci "</style>\n" 380cb93a386Sopenharmony_ci "<script>\n" 381cb93a386Sopenharmony_ci "function ce(t) { return document.createElement(t); }\n" 382cb93a386Sopenharmony_ci "function ct(n) { return document.createTextNode(n); }\n" 383cb93a386Sopenharmony_ci "function ac(u,v) { return u.appendChild(v); }\n" 384cb93a386Sopenharmony_ci "function br(u) { ac(u, ce(\"br\")); }\n" 385cb93a386Sopenharmony_ci "function ma(s, c) { var a = ce(\"a\"); a.href = s; ac(a, c); return a; }\n" 386cb93a386Sopenharmony_ci "function f(backend, gm, e1, e2, e3) {\n" 387cb93a386Sopenharmony_ci " var b = ce(\"div\");\n" 388cb93a386Sopenharmony_ci " var x = ce(\"h2\");\n" 389cb93a386Sopenharmony_ci " var t = backend + \"_\" + gm;\n" 390cb93a386Sopenharmony_ci " ac(x, ct(t));\n" 391cb93a386Sopenharmony_ci " ac(b, x);\n" 392cb93a386Sopenharmony_ci " ac(b, ct(\"backend: \" + backend));\n" 393cb93a386Sopenharmony_ci " br(b);\n" 394cb93a386Sopenharmony_ci " ac(b, ct(\"gm name: \" + gm));\n" 395cb93a386Sopenharmony_ci " br(b);\n" 396cb93a386Sopenharmony_ci " ac(b, ct(\"maximum error: \" + e1));\n" 397cb93a386Sopenharmony_ci " br(b);\n" 398cb93a386Sopenharmony_ci " ac(b, ct(\"bad pixel counts: \" + e2));\n" 399cb93a386Sopenharmony_ci " br(b);\n" 400cb93a386Sopenharmony_ci " ac(b, ct(\"total error: \" + e3));\n" 401cb93a386Sopenharmony_ci " br(b);\n" 402cb93a386Sopenharmony_ci " var q = \"" IMAGES_DIRECTORY_PATH "/\" + backend + \"_\" + gm + \"_\";\n" 403cb93a386Sopenharmony_ci " var p = \"" IMAGES_DIRECTORY_PATH "/" PATH_MODEL "_\" + gm + \"_\";\n" 404cb93a386Sopenharmony_ci " var i = ce(\"img\");\n" 405cb93a386Sopenharmony_ci " i.src = q + \"" PATH_IMG_PNG "\";\n" 406cb93a386Sopenharmony_ci " i.alt = \"img\";\n" 407cb93a386Sopenharmony_ci " ac(b, ma(i.src, i));\n" 408cb93a386Sopenharmony_ci " i = ce(\"img\");\n" 409cb93a386Sopenharmony_ci " i.src = q + \"" PATH_ERR_PNG "\";\n" 410cb93a386Sopenharmony_ci " i.alt = \"err\";\n" 411cb93a386Sopenharmony_ci " ac(b, ma(i.src, i));\n" 412cb93a386Sopenharmony_ci " br(b);\n" 413cb93a386Sopenharmony_ci " ac(b, ct(\"Expectation: \"));\n" 414cb93a386Sopenharmony_ci " ac(b, ma(p + \"" PATH_MAX_PNG "\", ct(\"max\")));\n" 415cb93a386Sopenharmony_ci " ac(b, ct(\" | \"));\n" 416cb93a386Sopenharmony_ci " ac(b, ma(p + \"" PATH_MIN_PNG "\", ct(\"min\")));\n" 417cb93a386Sopenharmony_ci " ac(b, ce(\"hr\"));\n" 418cb93a386Sopenharmony_ci " b.id = backend + \":\" + gm;\n" 419cb93a386Sopenharmony_ci " ac(document.body, b);\n" 420cb93a386Sopenharmony_ci " l = ce(\"li\");\n" 421cb93a386Sopenharmony_ci " ac(l, ct(\"[\" + e3 + \"] \"));\n" 422cb93a386Sopenharmony_ci " ac(l, ma(\"#\" + backend +\":\"+ gm , ct(t)));\n" 423cb93a386Sopenharmony_ci " ac(document.getElementById(\"toc\"), l);\n" 424cb93a386Sopenharmony_ci "}\n" 425cb93a386Sopenharmony_ci "function main() {\n"; 426cb93a386Sopenharmony_ci 427cb93a386Sopenharmony_cistatic constexpr char kDocMiddle[] = 428cb93a386Sopenharmony_ci "}\n" 429cb93a386Sopenharmony_ci "</script>\n" 430cb93a386Sopenharmony_ci "</head>\n" 431cb93a386Sopenharmony_ci "<body onload=\"main()\">\n" 432cb93a386Sopenharmony_ci "<h1>SkQP Report</h1>\n"; 433cb93a386Sopenharmony_ci 434cb93a386Sopenharmony_cistatic constexpr char kDocTail[] = 435cb93a386Sopenharmony_ci "<ul id=\"toc\"></ul>\n" 436cb93a386Sopenharmony_ci "<hr>\n" 437cb93a386Sopenharmony_ci "<p>Left image: test result<br>\n" 438cb93a386Sopenharmony_ci "Right image: errors (white = no error, black = smallest error, red = biggest error; " 439cb93a386Sopenharmony_ci "other errors are a color between black and red.)</p>\n" 440cb93a386Sopenharmony_ci "<hr>\n" 441cb93a386Sopenharmony_ci "</body>\n" 442cb93a386Sopenharmony_ci "</html>\n"; 443cb93a386Sopenharmony_ci 444cb93a386Sopenharmony_citemplate <typename T> 445cb93a386Sopenharmony_ciinline void write(SkWStream* wStream, const T& text) { 446cb93a386Sopenharmony_ci wStream->write(text.c_str(), text.size()); 447cb93a386Sopenharmony_ci} 448cb93a386Sopenharmony_ci 449cb93a386Sopenharmony_civoid SkQP::makeReport() { 450cb93a386Sopenharmony_ci SkASSERT_RELEASE(fAssetManager); 451cb93a386Sopenharmony_ci int glesErrorCount = 0, vkErrorCount = 0, gles = 0, vk = 0; 452cb93a386Sopenharmony_ci 453cb93a386Sopenharmony_ci if (!sk_isdir(fReportDirectory.c_str())) { 454cb93a386Sopenharmony_ci SkDebugf("Report destination does not exist: '%s'\n", fReportDirectory.c_str()); 455cb93a386Sopenharmony_ci return; 456cb93a386Sopenharmony_ci } 457cb93a386Sopenharmony_ci SkFILEWStream csvOut(SkOSPath::Join(fReportDirectory.c_str(), kRenderTestCSVReport).c_str()); 458cb93a386Sopenharmony_ci SkFILEWStream htmOut(SkOSPath::Join(fReportDirectory.c_str(), kRenderTestReportPath).c_str()); 459cb93a386Sopenharmony_ci SkASSERT_RELEASE(csvOut.isValid() && htmOut.isValid()); 460cb93a386Sopenharmony_ci htmOut.writeText(kDocHead); 461cb93a386Sopenharmony_ci for (const SkQP::RenderResult& run : fRenderResults) { 462cb93a386Sopenharmony_ci switch (run.fBackend) { 463cb93a386Sopenharmony_ci case SkQP::SkiaBackend::kGLES: ++gles; break; 464cb93a386Sopenharmony_ci case SkQP::SkiaBackend::kVulkan: ++vk; break; 465cb93a386Sopenharmony_ci default: break; 466cb93a386Sopenharmony_ci } 467cb93a386Sopenharmony_ci const char* backendName = SkQP::GetBackendName(run.fBackend); 468cb93a386Sopenharmony_ci std::string gmName = SkQP::GetGMName(run.fGM); 469cb93a386Sopenharmony_ci const SkQP::RenderOutcome& outcome = run.fOutcome; 470cb93a386Sopenharmony_ci auto str = SkStringPrintf("\"%s\",\"%s\",%d,%d,%" PRId64, backendName, gmName.c_str(), 471cb93a386Sopenharmony_ci outcome.fMaxError, outcome.fBadPixelCount, outcome.fTotalError); 472cb93a386Sopenharmony_ci write(&csvOut, SkStringPrintf("%s\n", str.c_str())); 473cb93a386Sopenharmony_ci 474cb93a386Sopenharmony_ci int64_t passingThreshold = fGMThresholds.empty() ? 0 : fGMThresholds[gmName]; 475cb93a386Sopenharmony_ci if (passingThreshold == -1 || outcome.fMaxError <= passingThreshold) { 476cb93a386Sopenharmony_ci continue; 477cb93a386Sopenharmony_ci } 478cb93a386Sopenharmony_ci write(&htmOut, SkStringPrintf(" f(%s);\n", str.c_str())); 479cb93a386Sopenharmony_ci switch (run.fBackend) { 480cb93a386Sopenharmony_ci case SkQP::SkiaBackend::kGLES: ++glesErrorCount; break; 481cb93a386Sopenharmony_ci case SkQP::SkiaBackend::kVulkan: ++vkErrorCount; break; 482cb93a386Sopenharmony_ci default: break; 483cb93a386Sopenharmony_ci } 484cb93a386Sopenharmony_ci } 485cb93a386Sopenharmony_ci htmOut.writeText(kDocMiddle); 486cb93a386Sopenharmony_ci write(&htmOut, SkStringPrintf("<p>gles errors: %d (of %d)</br>\n" 487cb93a386Sopenharmony_ci "vk errors: %d (of %d)</p>\n", 488cb93a386Sopenharmony_ci glesErrorCount, gles, vkErrorCount, vk)); 489cb93a386Sopenharmony_ci htmOut.writeText(kDocTail); 490cb93a386Sopenharmony_ci SkFILEWStream unitOut(SkOSPath::Join(fReportDirectory.c_str(), kUnitTestReportPath).c_str()); 491cb93a386Sopenharmony_ci SkASSERT_RELEASE(unitOut.isValid()); 492cb93a386Sopenharmony_ci for (const SkQP::UnitTestResult& result : fUnitTestResults) { 493cb93a386Sopenharmony_ci unitOut.writeText(GetUnitTestName(result.fUnitTest)); 494cb93a386Sopenharmony_ci if (result.fErrors.empty()) { 495cb93a386Sopenharmony_ci unitOut.writeText(" PASSED\n* * *\n"); 496cb93a386Sopenharmony_ci } else { 497cb93a386Sopenharmony_ci write(&unitOut, SkStringPrintf(" FAILED (%zu errors)\n", result.fErrors.size())); 498cb93a386Sopenharmony_ci for (const std::string& err : result.fErrors) { 499cb93a386Sopenharmony_ci write(&unitOut, err); 500cb93a386Sopenharmony_ci unitOut.newline(); 501cb93a386Sopenharmony_ci } 502cb93a386Sopenharmony_ci unitOut.writeText("* * *\n"); 503cb93a386Sopenharmony_ci } 504cb93a386Sopenharmony_ci } 505cb93a386Sopenharmony_ci} 506