1cb93a386Sopenharmony_ci/* 2cb93a386Sopenharmony_ci * Copyright 2020 Google LLC 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 <set> 9cb93a386Sopenharmony_ci#include <string> 10cb93a386Sopenharmony_ci#include <emscripten.h> 11cb93a386Sopenharmony_ci#include <emscripten/bind.h> 12cb93a386Sopenharmony_ci#include <emscripten/html5.h> 13cb93a386Sopenharmony_ci 14cb93a386Sopenharmony_ci#include "gm/gm.h" 15cb93a386Sopenharmony_ci#include "include/core/SkBitmap.h" 16cb93a386Sopenharmony_ci#include "include/core/SkCanvas.h" 17cb93a386Sopenharmony_ci#include "include/core/SkData.h" 18cb93a386Sopenharmony_ci#include "include/core/SkImageInfo.h" 19cb93a386Sopenharmony_ci#include "include/core/SkStream.h" 20cb93a386Sopenharmony_ci#include "include/core/SkSurface.h" 21cb93a386Sopenharmony_ci#include "include/gpu/GrContextOptions.h" 22cb93a386Sopenharmony_ci#include "include/gpu/GrDirectContext.h" 23cb93a386Sopenharmony_ci#include "include/gpu/gl/GrGLInterface.h" 24cb93a386Sopenharmony_ci#include "include/gpu/gl/GrGLTypes.h" 25cb93a386Sopenharmony_ci#include "modules/canvaskit/WasmCommon.h" 26cb93a386Sopenharmony_ci#include "src/core/SkFontMgrPriv.h" 27cb93a386Sopenharmony_ci#include "src/core/SkMD5.h" 28cb93a386Sopenharmony_ci#include "tests/Test.h" 29cb93a386Sopenharmony_ci#include "tools/HashAndEncode.h" 30cb93a386Sopenharmony_ci#include "tools/ResourceFactory.h" 31cb93a386Sopenharmony_ci#include "tools/flags/CommandLineFlags.h" 32cb93a386Sopenharmony_ci#include "tools/fonts/TestFontMgr.h" 33cb93a386Sopenharmony_ci 34cb93a386Sopenharmony_ciusing namespace emscripten; 35cb93a386Sopenharmony_ci 36cb93a386Sopenharmony_ci/** 37cb93a386Sopenharmony_ci * Returns a JS array of strings containing the names of the registered GMs. GMs are only registered 38cb93a386Sopenharmony_ci * when their source is included in the "link" step, not if they are in something like libgm.a. 39cb93a386Sopenharmony_ci * The names are also logged to the console. 40cb93a386Sopenharmony_ci */ 41cb93a386Sopenharmony_cistatic JSArray ListGMs() { 42cb93a386Sopenharmony_ci SkDebugf("Listing GMs\n"); 43cb93a386Sopenharmony_ci JSArray gms = emscripten::val::array(); 44cb93a386Sopenharmony_ci for (skiagm::GMFactory fact : skiagm::GMRegistry::Range()) { 45cb93a386Sopenharmony_ci std::unique_ptr<skiagm::GM> gm(fact()); 46cb93a386Sopenharmony_ci SkDebugf("gm %s\n", gm->getName()); 47cb93a386Sopenharmony_ci gms.call<void>("push", std::string(gm->getName())); 48cb93a386Sopenharmony_ci } 49cb93a386Sopenharmony_ci return gms; 50cb93a386Sopenharmony_ci} 51cb93a386Sopenharmony_ci 52cb93a386Sopenharmony_cistatic std::unique_ptr<skiagm::GM> getGMWithName(std::string name) { 53cb93a386Sopenharmony_ci for (skiagm::GMFactory fact : skiagm::GMRegistry::Range()) { 54cb93a386Sopenharmony_ci std::unique_ptr<skiagm::GM> gm(fact()); 55cb93a386Sopenharmony_ci if (gm->getName() == name) { 56cb93a386Sopenharmony_ci return gm; 57cb93a386Sopenharmony_ci } 58cb93a386Sopenharmony_ci } 59cb93a386Sopenharmony_ci return nullptr; 60cb93a386Sopenharmony_ci} 61cb93a386Sopenharmony_ci 62cb93a386Sopenharmony_ci/** 63cb93a386Sopenharmony_ci * Sets the given WebGL context to be "current" and then creates a GrDirectContext from that 64cb93a386Sopenharmony_ci * context. 65cb93a386Sopenharmony_ci */ 66cb93a386Sopenharmony_cistatic sk_sp<GrDirectContext> MakeGrContext(EMSCRIPTEN_WEBGL_CONTEXT_HANDLE context) 67cb93a386Sopenharmony_ci{ 68cb93a386Sopenharmony_ci EMSCRIPTEN_RESULT r = emscripten_webgl_make_context_current(context); 69cb93a386Sopenharmony_ci if (r < 0) { 70cb93a386Sopenharmony_ci printf("failed to make webgl context current %d\n", r); 71cb93a386Sopenharmony_ci return nullptr; 72cb93a386Sopenharmony_ci } 73cb93a386Sopenharmony_ci // setup GrDirectContext 74cb93a386Sopenharmony_ci auto interface = GrGLMakeNativeInterface(); 75cb93a386Sopenharmony_ci // setup contexts 76cb93a386Sopenharmony_ci sk_sp<GrDirectContext> dContext((GrDirectContext::MakeGL(interface))); 77cb93a386Sopenharmony_ci return dContext; 78cb93a386Sopenharmony_ci} 79cb93a386Sopenharmony_ci 80cb93a386Sopenharmony_cistatic std::set<std::string> gKnownDigests; 81cb93a386Sopenharmony_ci 82cb93a386Sopenharmony_cistatic void LoadKnownDigest(std::string md5) { 83cb93a386Sopenharmony_ci gKnownDigests.insert(md5); 84cb93a386Sopenharmony_ci} 85cb93a386Sopenharmony_ci 86cb93a386Sopenharmony_cistatic std::map<std::string, sk_sp<SkData>> gResources; 87cb93a386Sopenharmony_ci 88cb93a386Sopenharmony_cistatic sk_sp<SkData> getResource(const char* name) { 89cb93a386Sopenharmony_ci auto it = gResources.find(name); 90cb93a386Sopenharmony_ci if (it == gResources.end()) { 91cb93a386Sopenharmony_ci SkDebugf("Resource %s not found\n", name); 92cb93a386Sopenharmony_ci return nullptr; 93cb93a386Sopenharmony_ci } 94cb93a386Sopenharmony_ci return it->second; 95cb93a386Sopenharmony_ci} 96cb93a386Sopenharmony_ci 97cb93a386Sopenharmony_cistatic void LoadResource(std::string name, WASMPointerU8 bPtr, size_t len) { 98cb93a386Sopenharmony_ci const uint8_t* bytes = reinterpret_cast<const uint8_t*>(bPtr); 99cb93a386Sopenharmony_ci auto data = SkData::MakeFromMalloc(bytes, len); 100cb93a386Sopenharmony_ci gResources[name] = std::move(data); 101cb93a386Sopenharmony_ci 102cb93a386Sopenharmony_ci if (!gResourceFactory) { 103cb93a386Sopenharmony_ci gResourceFactory = getResource; 104cb93a386Sopenharmony_ci } 105cb93a386Sopenharmony_ci} 106cb93a386Sopenharmony_ci 107cb93a386Sopenharmony_ci/** 108cb93a386Sopenharmony_ci * Runs the given GM and returns a JS object. If the GM was successful, the object will have the 109cb93a386Sopenharmony_ci * following properties: 110cb93a386Sopenharmony_ci * "png" - a Uint8Array of the PNG data extracted from the surface. 111cb93a386Sopenharmony_ci * "hash" - a string which is the md5 hash of the pixel contents and the metadata. 112cb93a386Sopenharmony_ci */ 113cb93a386Sopenharmony_cistatic JSObject RunGM(sk_sp<GrDirectContext> ctx, std::string name) { 114cb93a386Sopenharmony_ci JSObject result = emscripten::val::object(); 115cb93a386Sopenharmony_ci auto gm = getGMWithName(name); 116cb93a386Sopenharmony_ci if (!gm) { 117cb93a386Sopenharmony_ci SkDebugf("Could not find gm with name %s\n", name.c_str()); 118cb93a386Sopenharmony_ci return result; 119cb93a386Sopenharmony_ci } 120cb93a386Sopenharmony_ci // TODO(kjlubick) make these configurable somehow. This probably makes sense to do as function 121cb93a386Sopenharmony_ci // parameters. 122cb93a386Sopenharmony_ci auto alphaType = SkAlphaType::kPremul_SkAlphaType; 123cb93a386Sopenharmony_ci auto colorType = SkColorType::kN32_SkColorType; 124cb93a386Sopenharmony_ci SkISize size = gm->getISize(); 125cb93a386Sopenharmony_ci SkImageInfo info = SkImageInfo::Make(size, colorType, alphaType); 126cb93a386Sopenharmony_ci sk_sp<SkSurface> surface(SkSurface::MakeRenderTarget(ctx.get(), 127cb93a386Sopenharmony_ci SkBudgeted::kYes, 128cb93a386Sopenharmony_ci info, 0, 129cb93a386Sopenharmony_ci kBottomLeft_GrSurfaceOrigin, 130cb93a386Sopenharmony_ci nullptr, true)); 131cb93a386Sopenharmony_ci if (!surface) { 132cb93a386Sopenharmony_ci SkDebugf("Could not make surface\n"); 133cb93a386Sopenharmony_ci return result; 134cb93a386Sopenharmony_ci } 135cb93a386Sopenharmony_ci auto canvas = surface->getCanvas(); 136cb93a386Sopenharmony_ci 137cb93a386Sopenharmony_ci gm->onceBeforeDraw(); 138cb93a386Sopenharmony_ci SkString msg; 139cb93a386Sopenharmony_ci // Based on GMSrc::draw from DM. 140cb93a386Sopenharmony_ci auto gpuSetupResult = gm->gpuSetup(ctx.get(), canvas, &msg); 141cb93a386Sopenharmony_ci if (gpuSetupResult == skiagm::DrawResult::kFail) { 142cb93a386Sopenharmony_ci SkDebugf("Error with gpu setup for gm %s: %s\n", name.c_str(), msg.c_str()); 143cb93a386Sopenharmony_ci return result; 144cb93a386Sopenharmony_ci } else if (gpuSetupResult == skiagm::DrawResult::kSkip) { 145cb93a386Sopenharmony_ci return result; 146cb93a386Sopenharmony_ci } 147cb93a386Sopenharmony_ci 148cb93a386Sopenharmony_ci auto drawResult = gm->draw(canvas, &msg); 149cb93a386Sopenharmony_ci if (drawResult == skiagm::DrawResult::kFail) { 150cb93a386Sopenharmony_ci SkDebugf("Error with gm %s: %s\n", name.c_str(), msg.c_str()); 151cb93a386Sopenharmony_ci return result; 152cb93a386Sopenharmony_ci } else if (drawResult == skiagm::DrawResult::kSkip) { 153cb93a386Sopenharmony_ci return result; 154cb93a386Sopenharmony_ci } 155cb93a386Sopenharmony_ci surface->flushAndSubmit(true); 156cb93a386Sopenharmony_ci 157cb93a386Sopenharmony_ci // Based on GPUSink::readBack 158cb93a386Sopenharmony_ci SkBitmap bitmap; 159cb93a386Sopenharmony_ci bitmap.allocPixels(info); 160cb93a386Sopenharmony_ci if (!canvas->readPixels(bitmap, 0, 0)) { 161cb93a386Sopenharmony_ci SkDebugf("Could not read pixels back\n"); 162cb93a386Sopenharmony_ci return result; 163cb93a386Sopenharmony_ci } 164cb93a386Sopenharmony_ci 165cb93a386Sopenharmony_ci // Now we need to encode to PNG and get the md5 hash of the pixels (and colorspace and stuff). 166cb93a386Sopenharmony_ci // This is based on Task::Run from DM.cpp 167cb93a386Sopenharmony_ci std::unique_ptr<HashAndEncode> hashAndEncode = std::make_unique<HashAndEncode>(bitmap); 168cb93a386Sopenharmony_ci SkString md5; 169cb93a386Sopenharmony_ci SkMD5 hash; 170cb93a386Sopenharmony_ci hashAndEncode->feedHash(&hash); 171cb93a386Sopenharmony_ci SkMD5::Digest digest = hash.finish(); 172cb93a386Sopenharmony_ci for (int i = 0; i < 16; i++) { 173cb93a386Sopenharmony_ci md5.appendf("%02x", digest.data[i]); 174cb93a386Sopenharmony_ci } 175cb93a386Sopenharmony_ci 176cb93a386Sopenharmony_ci auto ok = gKnownDigests.find(md5.c_str()); 177cb93a386Sopenharmony_ci if (ok == gKnownDigests.end()) { 178cb93a386Sopenharmony_ci // We only need to decode the image if it is "interesting", that is, we have not written it 179cb93a386Sopenharmony_ci // before to disk and uploaded it to gold. 180cb93a386Sopenharmony_ci SkDynamicMemoryWStream stream; 181cb93a386Sopenharmony_ci // We do not need to include the keys because they are optional - they are not read by Gold. 182cb93a386Sopenharmony_ci CommandLineFlags::StringArray empty; 183cb93a386Sopenharmony_ci hashAndEncode->encodePNG(&stream, md5.c_str(), empty, empty); 184cb93a386Sopenharmony_ci 185cb93a386Sopenharmony_ci auto data = stream.detachAsData(); 186cb93a386Sopenharmony_ci 187cb93a386Sopenharmony_ci // This is the cleanest way to create a new Uint8Array with a copy of the data that is not 188cb93a386Sopenharmony_ci // in the WASM heap. kjlubick tried returning a pointer inside an SkData, but that lead to 189cb93a386Sopenharmony_ci // some use after free issues. By making the copy using the JS transliteration, we don't 190cb93a386Sopenharmony_ci // risk the SkData object being cleaned up before we make the copy. 191cb93a386Sopenharmony_ci Uint8Array pngData = emscripten::val( 192cb93a386Sopenharmony_ci // https://emscripten.org/docs/porting/connecting_cpp_and_javascript/embind.html#memory-views 193cb93a386Sopenharmony_ci typed_memory_view(data->size(), data->bytes()) 194cb93a386Sopenharmony_ci ).call<Uint8Array>("slice"); // slice with no args makes a copy of the memory view. 195cb93a386Sopenharmony_ci 196cb93a386Sopenharmony_ci result.set("png", pngData); 197cb93a386Sopenharmony_ci gKnownDigests.emplace(md5.c_str()); 198cb93a386Sopenharmony_ci } 199cb93a386Sopenharmony_ci result.set("hash", md5.c_str()); 200cb93a386Sopenharmony_ci return result; 201cb93a386Sopenharmony_ci} 202cb93a386Sopenharmony_ci 203cb93a386Sopenharmony_cistatic JSArray ListTests() { 204cb93a386Sopenharmony_ci SkDebugf("Listing Tests\n"); 205cb93a386Sopenharmony_ci JSArray tests = emscripten::val::array(); 206cb93a386Sopenharmony_ci for (auto test : skiatest::TestRegistry::Range()) { 207cb93a386Sopenharmony_ci SkDebugf("test %s\n", test.fName); 208cb93a386Sopenharmony_ci tests.call<void>("push", std::string(test.fName)); 209cb93a386Sopenharmony_ci } 210cb93a386Sopenharmony_ci return tests; 211cb93a386Sopenharmony_ci} 212cb93a386Sopenharmony_ci 213cb93a386Sopenharmony_cistatic skiatest::Test getTestWithName(std::string name, bool* ok) { 214cb93a386Sopenharmony_ci for (auto test : skiatest::TestRegistry::Range()) { 215cb93a386Sopenharmony_ci if (name == test.fName) { 216cb93a386Sopenharmony_ci *ok = true; 217cb93a386Sopenharmony_ci return test; 218cb93a386Sopenharmony_ci } 219cb93a386Sopenharmony_ci } 220cb93a386Sopenharmony_ci *ok = false; 221cb93a386Sopenharmony_ci return skiatest::Test(nullptr, /*gpu*/ false, /*graphite*/ false, nullptr); 222cb93a386Sopenharmony_ci} 223cb93a386Sopenharmony_ci 224cb93a386Sopenharmony_ci// Based on DM.cpp:run_test 225cb93a386Sopenharmony_cistruct WasmReporter : public skiatest::Reporter { 226cb93a386Sopenharmony_ci WasmReporter(std::string name, JSObject result): fName(name), fResult(result){} 227cb93a386Sopenharmony_ci 228cb93a386Sopenharmony_ci void reportFailed(const skiatest::Failure& failure) override { 229cb93a386Sopenharmony_ci SkDebugf("Test %s failed: %s\n", fName.c_str(), failure.toString().c_str()); 230cb93a386Sopenharmony_ci fResult.set("result", "failed"); 231cb93a386Sopenharmony_ci fResult.set("msg", failure.toString().c_str()); 232cb93a386Sopenharmony_ci } 233cb93a386Sopenharmony_ci std::string fName; 234cb93a386Sopenharmony_ci JSObject fResult; 235cb93a386Sopenharmony_ci}; 236cb93a386Sopenharmony_ci 237cb93a386Sopenharmony_ci/** 238cb93a386Sopenharmony_ci * Runs the given Test and returns a JS object. If the Test was located, the object will have the 239cb93a386Sopenharmony_ci * following properties: 240cb93a386Sopenharmony_ci * "result" : One of "passed", "failed", "skipped". 241cb93a386Sopenharmony_ci * "msg": May be non-empty on failure 242cb93a386Sopenharmony_ci */ 243cb93a386Sopenharmony_cistatic JSObject RunTest(std::string name) { 244cb93a386Sopenharmony_ci JSObject result = emscripten::val::object(); 245cb93a386Sopenharmony_ci bool ok = false; 246cb93a386Sopenharmony_ci auto test = getTestWithName(name, &ok); 247cb93a386Sopenharmony_ci if (!ok) { 248cb93a386Sopenharmony_ci SkDebugf("Could not find test with name %s\n", name.c_str()); 249cb93a386Sopenharmony_ci return result; 250cb93a386Sopenharmony_ci } 251cb93a386Sopenharmony_ci GrContextOptions grOpts; 252cb93a386Sopenharmony_ci if (test.fNeedsGpu) { 253cb93a386Sopenharmony_ci result.set("result", "passed"); // default to passing - the reporter will mark failed. 254cb93a386Sopenharmony_ci WasmReporter reporter(name, result); 255cb93a386Sopenharmony_ci test.modifyGrContextOptions(&grOpts); 256cb93a386Sopenharmony_ci test.run(&reporter, grOpts); 257cb93a386Sopenharmony_ci return result; 258cb93a386Sopenharmony_ci } 259cb93a386Sopenharmony_ci 260cb93a386Sopenharmony_ci result.set("result", "passed"); // default to passing - the reporter will mark failed. 261cb93a386Sopenharmony_ci WasmReporter reporter(name, result); 262cb93a386Sopenharmony_ci test.run(&reporter, grOpts); 263cb93a386Sopenharmony_ci return result; 264cb93a386Sopenharmony_ci} 265cb93a386Sopenharmony_ci 266cb93a386Sopenharmony_cinamespace skiatest { 267cb93a386Sopenharmony_ci 268cb93a386Sopenharmony_ciusing ContextType = sk_gpu_test::GrContextFactory::ContextType; 269cb93a386Sopenharmony_ci 270cb93a386Sopenharmony_ci// These are the supported GrContextTypeFilterFn 271cb93a386Sopenharmony_cibool IsGLContextType(ContextType ct) { 272cb93a386Sopenharmony_ci return GrBackendApi::kOpenGL == sk_gpu_test::GrContextFactory::ContextTypeBackend(ct); 273cb93a386Sopenharmony_ci} 274cb93a386Sopenharmony_cibool IsRenderingGLContextType(ContextType ct) { 275cb93a386Sopenharmony_ci return IsGLContextType(ct) && sk_gpu_test::GrContextFactory::IsRenderingContext(ct); 276cb93a386Sopenharmony_ci} 277cb93a386Sopenharmony_cibool IsMockContextType(ContextType ct) { 278cb93a386Sopenharmony_ci return ct == ContextType::kMock_ContextType; 279cb93a386Sopenharmony_ci} 280cb93a386Sopenharmony_ci// These are not supported 281cb93a386Sopenharmony_cibool IsVulkanContextType(ContextType) {return false;} 282cb93a386Sopenharmony_cibool IsMetalContextType(ContextType) {return false;} 283cb93a386Sopenharmony_cibool IsDirect3DContextType(ContextType) {return false;} 284cb93a386Sopenharmony_cibool IsDawnContextType(ContextType) {return false;} 285cb93a386Sopenharmony_ci 286cb93a386Sopenharmony_civoid RunWithGPUTestContexts(GrContextTestFn* test, GrContextTypeFilterFn* contextTypeFilter, 287cb93a386Sopenharmony_ci Reporter* reporter, const GrContextOptions& options) { 288cb93a386Sopenharmony_ci for (auto contextType : {ContextType::kGLES_ContextType, ContextType::kMock_ContextType}) { 289cb93a386Sopenharmony_ci if (contextTypeFilter && !(*contextTypeFilter)(contextType)) { 290cb93a386Sopenharmony_ci continue; 291cb93a386Sopenharmony_ci } 292cb93a386Sopenharmony_ci 293cb93a386Sopenharmony_ci sk_gpu_test::GrContextFactory factory(options); 294cb93a386Sopenharmony_ci sk_gpu_test::ContextInfo ctxInfo = factory.getContextInfo(contextType); 295cb93a386Sopenharmony_ci 296cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, ctxInfo.directContext() != nullptr); 297cb93a386Sopenharmony_ci if (!ctxInfo.directContext()) { 298cb93a386Sopenharmony_ci return; 299cb93a386Sopenharmony_ci } 300cb93a386Sopenharmony_ci ctxInfo.testContext()->makeCurrent(); 301cb93a386Sopenharmony_ci // From DMGpuTestProcs.cpp 302cb93a386Sopenharmony_ci (*test)(reporter, ctxInfo); 303cb93a386Sopenharmony_ci // Sync so any release/finished procs get called. 304cb93a386Sopenharmony_ci ctxInfo.directContext()->flushAndSubmit(/*sync*/true); 305cb93a386Sopenharmony_ci } 306cb93a386Sopenharmony_ci} 307cb93a386Sopenharmony_ci} // namespace skiatest 308cb93a386Sopenharmony_ci 309cb93a386Sopenharmony_cinamespace { 310cb93a386Sopenharmony_ci 311cb93a386Sopenharmony_ci// A GLtestContext that we can return from CreatePlatformGLTestContext below. 312cb93a386Sopenharmony_ci// It doesn't have to do anything WebGL-specific that I know of but we can't return 313cb93a386Sopenharmony_ci// a GLTestContext because it has pure virtual methods that need to be implemented. 314cb93a386Sopenharmony_ciclass WasmWebGlTestContext : public sk_gpu_test::GLTestContext { 315cb93a386Sopenharmony_cipublic: 316cb93a386Sopenharmony_ci WasmWebGlTestContext() {} 317cb93a386Sopenharmony_ci ~WasmWebGlTestContext() override { 318cb93a386Sopenharmony_ci this->teardown(); 319cb93a386Sopenharmony_ci } 320cb93a386Sopenharmony_ci // We assume WebGL only has one context and that it is always current. 321cb93a386Sopenharmony_ci // Therefore these context related functions return null intentionally. 322cb93a386Sopenharmony_ci // It's possible that more tests will pass if these were correctly implemented. 323cb93a386Sopenharmony_ci std::unique_ptr<GLTestContext> makeNew() const override { 324cb93a386Sopenharmony_ci // This is supposed to create a new GL context in a new GLTestContext. 325cb93a386Sopenharmony_ci // Specifically for tests that do not want to re-use the existing one. 326cb93a386Sopenharmony_ci return nullptr; 327cb93a386Sopenharmony_ci } 328cb93a386Sopenharmony_ci void onPlatformMakeNotCurrent() const override { } 329cb93a386Sopenharmony_ci void onPlatformMakeCurrent() const override { } 330cb93a386Sopenharmony_ci std::function<void()> onPlatformGetAutoContextRestore() const override { 331cb93a386Sopenharmony_ci return nullptr; 332cb93a386Sopenharmony_ci } 333cb93a386Sopenharmony_ci GrGLFuncPtr onPlatformGetProcAddress(const char* procName) const override { 334cb93a386Sopenharmony_ci return nullptr; 335cb93a386Sopenharmony_ci } 336cb93a386Sopenharmony_ci}; 337cb93a386Sopenharmony_ci} // namespace 338cb93a386Sopenharmony_ci 339cb93a386Sopenharmony_cinamespace sk_gpu_test { 340cb93a386Sopenharmony_ciGLTestContext *CreatePlatformGLTestContext(GrGLStandard forcedGpuAPI, 341cb93a386Sopenharmony_ci GLTestContext *shareContext) { 342cb93a386Sopenharmony_ci return new WasmWebGlTestContext(); 343cb93a386Sopenharmony_ci} 344cb93a386Sopenharmony_ci} // namespace sk_gpu_test 345cb93a386Sopenharmony_ci 346cb93a386Sopenharmony_civoid Init() { 347cb93a386Sopenharmony_ci // Use the portable fonts. 348cb93a386Sopenharmony_ci gSkFontMgr_DefaultFactory = &ToolUtils::MakePortableFontMgr; 349cb93a386Sopenharmony_ci} 350cb93a386Sopenharmony_ci 351cb93a386Sopenharmony_ciEMSCRIPTEN_BINDINGS(GMs) { 352cb93a386Sopenharmony_ci function("Init", &Init); 353cb93a386Sopenharmony_ci function("ListGMs", &ListGMs); 354cb93a386Sopenharmony_ci function("ListTests", &ListTests); 355cb93a386Sopenharmony_ci function("LoadKnownDigest", &LoadKnownDigest); 356cb93a386Sopenharmony_ci function("_LoadResource", &LoadResource); 357cb93a386Sopenharmony_ci function("MakeGrContext", &MakeGrContext); 358cb93a386Sopenharmony_ci function("RunGM", &RunGM); 359cb93a386Sopenharmony_ci function("RunTest", &RunTest); 360cb93a386Sopenharmony_ci 361cb93a386Sopenharmony_ci class_<GrDirectContext>("GrDirectContext") 362cb93a386Sopenharmony_ci .smart_ptr<sk_sp<GrDirectContext>>("sk_sp<GrDirectContext>"); 363cb93a386Sopenharmony_ci} 364