1 /*
2  * Copyright 2017 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "gm_runner.h"
9 
10 #include <algorithm>
11 
12 #include "../dm/DMFontMgr.h"
13 #include "GrContext.h"
14 #include "GrContextOptions.h"
15 #include "SkFontMgrPriv.h"
16 #include "SkFontStyle.h"
17 #include "SkGraphics.h"
18 #include "SkSurface.h"
19 #include "Test.h"
20 #include "gl/GLTestContext.h"
21 #include "gm.h"
22 #include "gm_knowledge.h"
23 #include "vk/VkTestContext.h"
24 
25 static SkTHashSet<SkString> gDoNotScoreInCompatibilityTestMode;
26 static SkTHashSet<SkString> gDoNotExecuteInExperimentalMode;
27 static SkTHashSet<SkString> gKnownGpuUnitTests;
28 static SkTHashSet<SkString> gKnownGMs;
29 static gm_runner::Mode gMode = gm_runner::Mode::kCompatibilityTestMode;
30 
is_empty(const SkTHashSet<SkString> & set)31 static bool is_empty(const SkTHashSet<SkString>& set) {
32     return 0 == set.count();
33 }
in_set(const char * s,const SkTHashSet<SkString> & set)34 static bool in_set(const char* s, const SkTHashSet<SkString>& set) {
35     return !is_empty(set) && nullptr != set.find(SkString(s));
36 }
37 
readlist(skqp::AssetManager * mgr,const char * path,SkTHashSet<SkString> * dst)38 static void readlist(skqp::AssetManager* mgr, const char* path, SkTHashSet<SkString>* dst) {
39     auto asset = mgr->open(path);
40     if (!asset || asset->getLength() == 0) {
41         return;  // missing file same as empty file.
42     }
43     std::vector<char> buffer(asset->getLength() + 1);
44     asset->read(buffer.data(), buffer.size());
45     buffer.back() = '\0';
46     const char* ptr = buffer.data();
47     const char* end = &buffer.back();
48     SkASSERT(ptr < end);
49     while (true) {
50         while (*ptr == '\n' && ptr < end) {
51             ++ptr;
52         }
53         if (ptr == end) {
54             return;
55         }
56         const char* find = strchr(ptr, '\n');
57         if (!find) {
58             find = end;
59         }
60         SkASSERT(find > ptr);
61         dst->add(SkString(ptr, find - ptr));
62         ptr = find;
63     }
64 }
65 
66 namespace gm_runner {
67 
GetErrorString(Error e)68 const char* GetErrorString(Error e) {
69     switch (e) {
70         case Error::None:          return "";
71         case Error::BadSkiaOutput: return "Bad Skia Output";
72         case Error::BadGMKBData:   return "Bad GMKB Data";
73         case Error::SkiaFailure:   return "Skia Failure";
74         default:                   SkASSERT(false);
75                                    return "unknown";
76     }
77 }
78 
ExecuteTest(UnitTest test)79 std::vector<std::string> ExecuteTest(UnitTest test) {
80     struct : public skiatest::Reporter {
81         std::vector<std::string> fErrors;
82         void reportFailed(const skiatest::Failure& failure) override {
83             SkString desc = failure.toString();
84             fErrors.push_back(std::string(desc.c_str(), desc.size()));
85         }
86     } r;
87     GrContextOptions options;
88     #ifndef SK_SKQP_ENABLE_DRIVER_CORRECTNESS_WORKAROUNDS
89     options.fDisableDriverCorrectnessWorkarounds = true;
90     #endif
91     if (test->fContextOptionsProc) {
92         test->fContextOptionsProc(&options);
93     }
94     test->proc(&r, options);
95     return std::move(r.fErrors);
96 }
97 
GetUnitTestName(UnitTest test)98 const char* GetUnitTestName(UnitTest test) { return test->name; }
99 
GetUnitTests()100 std::vector<UnitTest> GetUnitTests() {
101     std::vector<UnitTest> tests;
102     for (const skiatest::TestRegistry* r = skiatest::TestRegistry::Head(); r; r = r->next()) {
103         const skiatest::Test& test = r->factory();
104         if ((is_empty(gKnownGpuUnitTests) || in_set(test.name, gKnownGpuUnitTests))
105             && test.needsGpu) {
106             tests.push_back(&test);
107         }
108     }
109     struct {
110         bool operator()(UnitTest u, UnitTest v) const { return strcmp(u->name, v->name) < 0; }
111     } less;
112     std::sort(tests.begin(), tests.end(), less);
113     return tests;
114 }
115 
GetBackendName(SkiaBackend backend)116 const char* GetBackendName(SkiaBackend backend) {
117     switch (backend) {
118         case SkiaBackend::kGL:     return "gl";
119         case SkiaBackend::kGLES:   return "gles";
120         case SkiaBackend::kVulkan: return "vk";
121         default:                   SkASSERT(false);
122                                    return "error";
123     }
124 }
125 
make_test_context(SkiaBackend backend)126 static std::unique_ptr<sk_gpu_test::TestContext> make_test_context(SkiaBackend backend) {
127     using U = std::unique_ptr<sk_gpu_test::TestContext>;
128     switch (backend) {
129         case SkiaBackend::kGL:
130             return U(sk_gpu_test::CreatePlatformGLTestContext(kGL_GrGLStandard, nullptr));
131         case SkiaBackend::kGLES:
132             return U(sk_gpu_test::CreatePlatformGLTestContext(kGLES_GrGLStandard, nullptr));
133 #ifdef SK_VULKAN
134         case SkiaBackend::kVulkan:
135             return U(sk_gpu_test::CreatePlatformVkTestContext(nullptr));
136 #endif
137         default:
138             return nullptr;
139     }
140 }
141 
context_options(skiagm::GM * gm=nullptr)142 static GrContextOptions context_options(skiagm::GM* gm = nullptr) {
143     GrContextOptions grContextOptions;
144     grContextOptions.fAllowPathMaskCaching = true;
145     grContextOptions.fSuppressPathRendering = true;
146     #ifndef SK_SKQP_ENABLE_DRIVER_CORRECTNESS_WORKAROUNDS
147     grContextOptions.fDisableDriverCorrectnessWorkarounds = true;
148     #endif
149     if (gm) {
150         gm->modifyGrContextOptions(&grContextOptions);
151     }
152     return grContextOptions;
153 }
154 
GetSupportedBackends()155 std::vector<SkiaBackend> GetSupportedBackends() {
156     std::vector<SkiaBackend> result;
157     SkiaBackend backends[] = {
158         #ifndef SK_BUILD_FOR_ANDROID
159         SkiaBackend::kGL,  // Used for testing on desktop machines.
160         #endif
161         SkiaBackend::kGLES,
162         SkiaBackend::kVulkan,
163     };
164     for (SkiaBackend backend : backends) {
165         std::unique_ptr<sk_gpu_test::TestContext> testCtx = make_test_context(backend);
166         if (testCtx) {
167             testCtx->makeCurrent();
168             if (nullptr != testCtx->makeGrContext(context_options())) {
169                 result.push_back(backend);
170             }
171         }
172     }
173     SkASSERT_RELEASE(result.size() > 0);
174     return result;
175 }
176 
evaluate_gm(SkiaBackend backend,skiagm::GM * gm,int * width,int * height,std::vector<uint32_t> * storage)177 static bool evaluate_gm(SkiaBackend backend,
178                         skiagm::GM* gm,
179                         int* width,
180                         int* height,
181                         std::vector<uint32_t>* storage) {
182     constexpr SkColorType ct = kRGBA_8888_SkColorType;
183     SkASSERT(storage);
184     SkASSERT(gm);
185     SkASSERT(width);
186     SkASSERT(height);
187     SkISize size = gm->getISize();
188     int w = size.width(),
189         h = size.height();
190     *width = w;
191     *height = h;
192     SkImageInfo info = SkImageInfo::Make(w, h, ct, kPremul_SkAlphaType, nullptr);
193     SkSurfaceProps props(0, SkSurfaceProps::kLegacyFontHost_InitType);
194 
195     std::unique_ptr<sk_gpu_test::TestContext> testCtx = make_test_context(backend);
196     if (!testCtx) {
197         return false;
198     }
199     testCtx->makeCurrent();
200     sk_sp<SkSurface> surf = SkSurface::MakeRenderTarget(
201             testCtx->makeGrContext(context_options(gm)).get(), SkBudgeted::kNo, info, 0, &props);
202     if (!surf) {
203         return false;
204     }
205     gm->draw(surf->getCanvas());
206 
207     storage->resize(w * h);
208     uint32_t* pix = storage->data();
209     size_t rb = w * sizeof(uint32_t);
210     SkASSERT(SkColorTypeBytesPerPixel(ct) == sizeof(uint32_t));
211     if (!surf->readPixels(SkImageInfo::Make(w, h, ct, kUnpremul_SkAlphaType), pix, rb, 0, 0)) {
212         storage->resize(0);
213         return false;
214     }
215     return true;
216 }
217 
EvaluateGM(SkiaBackend backend,GMFactory gmFact,skqp::AssetManager * assetManager,const char * reportDirectoryPath)218 std::tuple<float, Error> EvaluateGM(SkiaBackend backend,
219                                     GMFactory gmFact,
220                                     skqp::AssetManager* assetManager,
221                                     const char* reportDirectoryPath) {
222     std::vector<uint32_t> pixels;
223     SkASSERT(gmFact);
224     std::unique_ptr<skiagm::GM> gm(gmFact(nullptr));
225     SkASSERT(gm);
226     const char* name = gm->getName();
227     int width = 0, height = 0;
228     if (!evaluate_gm(backend, gm.get(), &width, &height, &pixels)) {
229         return std::make_tuple(FLT_MAX, Error::SkiaFailure);
230     }
231     if (Mode::kCompatibilityTestMode == gMode && in_set(name, gDoNotScoreInCompatibilityTestMode)) {
232         return std::make_tuple(0, Error::None);
233     }
234 
235     gmkb::Error e;
236     float value = gmkb::Check(pixels.data(), width, height,
237                               name, GetBackendName(backend), assetManager,
238                               reportDirectoryPath, &e);
239     Error error = gmkb::Error::kBadInput == e ? Error::BadSkiaOutput
240                 : gmkb::Error::kBadData  == e ? Error::BadGMKBData
241                                               : Error::None;
242     return std::make_tuple(value, error);
243 }
244 
InitSkia(Mode mode,skqp::AssetManager * mgr)245 void InitSkia(Mode mode, skqp::AssetManager* mgr) {
246     SkGraphics::Init();
247     gSkFontMgr_DefaultFactory = &DM::MakeFontMgr;
248 
249     gMode = mode;
250     readlist(mgr, "skqp/DoNotScoreInCompatibilityTestMode.txt",
251              &gDoNotScoreInCompatibilityTestMode);
252     readlist(mgr, "skqp/DoNotExecuteInExperimentalMode.txt", &gDoNotExecuteInExperimentalMode);
253     readlist(mgr, "skqp/KnownGpuUnitTests.txt", &gKnownGpuUnitTests);
254     readlist(mgr, "skqp/KnownGMs.txt", &gKnownGMs);
255 }
256 
GetGMFactories(skqp::AssetManager * assetManager)257 std::vector<GMFactory> GetGMFactories(skqp::AssetManager* assetManager) {
258     std::vector<GMFactory> result;
259     for (const skiagm::GMRegistry* r = skiagm::GMRegistry::Head(); r; r = r->next()) {
260         GMFactory f = r->factory();
261         SkASSERT(f);
262         auto name = GetGMName(f);
263         if ((is_empty(gKnownGMs) || in_set(name.c_str(), gKnownGMs)) &&
264             !(Mode::kExperimentalMode == gMode &&
265               in_set(name.c_str(), gDoNotExecuteInExperimentalMode)))
266         {
267             result.push_back(f);
268         }
269     }
270     struct {
271         bool operator()(GMFactory u, GMFactory v) const { return GetGMName(u) < GetGMName(v); }
272     } less;
273     std::sort(result.begin(), result.end(), less);
274     return result;
275 }
276 
GetGMName(GMFactory gmFactory)277 std::string GetGMName(GMFactory gmFactory) {
278     SkASSERT(gmFactory);
279     std::unique_ptr<skiagm::GM> gm(gmFactory(nullptr));
280     SkASSERT(gm);
281     return std::string(gm->getName());
282 }
283 }  // namespace gm_runner
284