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