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