1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker * Copyright 2021 Google LLC
3*c8dee2aaSAndroid Build Coastguard Worker *
4*c8dee2aaSAndroid Build Coastguard Worker * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker */
7*c8dee2aaSAndroid Build Coastguard Worker
8*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkCanvas.h"
9*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkColor.h"
10*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkColorSpace.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkColorType.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPaint.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkRect.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkSurface.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkTypes.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "include/effects/SkGradientShader.h"
17*c8dee2aaSAndroid Build Coastguard Worker #include "include/effects/SkRuntimeEffect.h"
18*c8dee2aaSAndroid Build Coastguard Worker #include "include/gpu/ganesh/GrBackendSurface.h"
19*c8dee2aaSAndroid Build Coastguard Worker #include "include/gpu/ganesh/GrDirectContext.h"
20*c8dee2aaSAndroid Build Coastguard Worker #include "include/gpu/ganesh/SkSurfaceGanesh.h"
21*c8dee2aaSAndroid Build Coastguard Worker
22*c8dee2aaSAndroid Build Coastguard Worker #include <emscripten/bind.h>
23*c8dee2aaSAndroid Build Coastguard Worker #include <emscripten/emscripten.h>
24*c8dee2aaSAndroid Build Coastguard Worker #include <emscripten/html5.h>
25*c8dee2aaSAndroid Build Coastguard Worker // https://github.com/emscripten-core/emscripten/blob/main/system/include/emscripten/html5_webgpu.h
26*c8dee2aaSAndroid Build Coastguard Worker // The import/export functions defined here should allow us to fetch a handle to a given JS
27*c8dee2aaSAndroid Build Coastguard Worker // Texture/Sampler/Device etc if needed.
28*c8dee2aaSAndroid Build Coastguard Worker #include <emscripten/html5_webgpu.h>
29*c8dee2aaSAndroid Build Coastguard Worker // https://github.com/emscripten-core/emscripten/blob/main/system/include/webgpu/webgpu.h
30*c8dee2aaSAndroid Build Coastguard Worker // This defines WebGPU constants and such. It also includes a lot of typedefs that make something
31*c8dee2aaSAndroid Build Coastguard Worker // like WGPUDevice defined as a pointer to something external. These "pointers" are actually just
32*c8dee2aaSAndroid Build Coastguard Worker // a small integer that refers to an array index of JS objects being held by a "manager"
33*c8dee2aaSAndroid Build Coastguard Worker // https://github.com/emscripten-core/emscripten/blob/f47bef371f3464471c6d30b631cffcdd06ced004/src/library_webgpu.js#L192
34*c8dee2aaSAndroid Build Coastguard Worker #include <webgpu/webgpu.h>
35*c8dee2aaSAndroid Build Coastguard Worker // https://github.com/emscripten-core/emscripten/blob/main/system/include/webgpu/webgpu_cpp.h
36*c8dee2aaSAndroid Build Coastguard Worker // This defines the C++ equivalents to the JS WebGPU API.
37*c8dee2aaSAndroid Build Coastguard Worker #include <webgpu/webgpu_cpp.h>
38*c8dee2aaSAndroid Build Coastguard Worker
getSurfaceForCanvas(wgpu::Device device,std::string canvasSelector,int width,int height)39*c8dee2aaSAndroid Build Coastguard Worker static wgpu::Surface getSurfaceForCanvas(wgpu::Device device,
40*c8dee2aaSAndroid Build Coastguard Worker std::string canvasSelector,
41*c8dee2aaSAndroid Build Coastguard Worker int width,
42*c8dee2aaSAndroid Build Coastguard Worker int height) {
43*c8dee2aaSAndroid Build Coastguard Worker wgpu::SurfaceDescriptorFromCanvasHTMLSelector surfaceSelector;
44*c8dee2aaSAndroid Build Coastguard Worker surfaceSelector.selector = canvasSelector.c_str();
45*c8dee2aaSAndroid Build Coastguard Worker
46*c8dee2aaSAndroid Build Coastguard Worker wgpu::SurfaceDescriptor surface_desc;
47*c8dee2aaSAndroid Build Coastguard Worker surface_desc.nextInChain = &surfaceSelector;
48*c8dee2aaSAndroid Build Coastguard Worker wgpu::Instance instance;
49*c8dee2aaSAndroid Build Coastguard Worker wgpu::Surface surface = instance.CreateSurface(&surface_desc);
50*c8dee2aaSAndroid Build Coastguard Worker
51*c8dee2aaSAndroid Build Coastguard Worker wgpu::SurfaceConfiguration config;
52*c8dee2aaSAndroid Build Coastguard Worker config.device = device;
53*c8dee2aaSAndroid Build Coastguard Worker config.format = wgpu::TextureFormat::BGRA8Unorm;
54*c8dee2aaSAndroid Build Coastguard Worker config.usage = wgpu::TextureUsage::RenderAttachment;
55*c8dee2aaSAndroid Build Coastguard Worker config.presentMode = wgpu::PresentMode::Fifo;
56*c8dee2aaSAndroid Build Coastguard Worker config.width = width;
57*c8dee2aaSAndroid Build Coastguard Worker config.height = height;
58*c8dee2aaSAndroid Build Coastguard Worker surface.Configure(&config);
59*c8dee2aaSAndroid Build Coastguard Worker
60*c8dee2aaSAndroid Build Coastguard Worker return surface;
61*c8dee2aaSAndroid Build Coastguard Worker }
62*c8dee2aaSAndroid Build Coastguard Worker
63*c8dee2aaSAndroid Build Coastguard Worker enum class DemoKind {
64*c8dee2aaSAndroid Build Coastguard Worker SOLID_COLOR,
65*c8dee2aaSAndroid Build Coastguard Worker GRADIENT,
66*c8dee2aaSAndroid Build Coastguard Worker RUNTIME_EFFECT,
67*c8dee2aaSAndroid Build Coastguard Worker };
68*c8dee2aaSAndroid Build Coastguard Worker
69*c8dee2aaSAndroid Build Coastguard Worker struct DemoUniforms {
70*c8dee2aaSAndroid Build Coastguard Worker float width;
71*c8dee2aaSAndroid Build Coastguard Worker float height;
72*c8dee2aaSAndroid Build Coastguard Worker float time;
73*c8dee2aaSAndroid Build Coastguard Worker };
74*c8dee2aaSAndroid Build Coastguard Worker
75*c8dee2aaSAndroid Build Coastguard Worker class Demo final {
76*c8dee2aaSAndroid Build Coastguard Worker public:
init(std::string canvasSelector,int width,int height)77*c8dee2aaSAndroid Build Coastguard Worker bool init(std::string canvasSelector, int width, int height) {
78*c8dee2aaSAndroid Build Coastguard Worker GrContextOptions ctxOpts;
79*c8dee2aaSAndroid Build Coastguard Worker
80*c8dee2aaSAndroid Build Coastguard Worker wgpu::Device device = wgpu::Device::Acquire(emscripten_webgpu_get_device());
81*c8dee2aaSAndroid Build Coastguard Worker sk_sp<GrDirectContext> context = GrDirectContext::MakeDawn(device, ctxOpts);
82*c8dee2aaSAndroid Build Coastguard Worker if (!context) {
83*c8dee2aaSAndroid Build Coastguard Worker SkDebugf("Could not create GrDirectContext\n");
84*c8dee2aaSAndroid Build Coastguard Worker return false;
85*c8dee2aaSAndroid Build Coastguard Worker }
86*c8dee2aaSAndroid Build Coastguard Worker
87*c8dee2aaSAndroid Build Coastguard Worker const char* sksl =
88*c8dee2aaSAndroid Build Coastguard Worker "uniform float2 iResolution;"
89*c8dee2aaSAndroid Build Coastguard Worker "uniform float iTime;"
90*c8dee2aaSAndroid Build Coastguard Worker "vec2 d;"
91*c8dee2aaSAndroid Build Coastguard Worker "float b(float a) {"
92*c8dee2aaSAndroid Build Coastguard Worker " return step(max(d.x, d.y), a);"
93*c8dee2aaSAndroid Build Coastguard Worker "}"
94*c8dee2aaSAndroid Build Coastguard Worker "half4 main(float2 C) {"
95*c8dee2aaSAndroid Build Coastguard Worker " vec4 O = vec4(0);"
96*c8dee2aaSAndroid Build Coastguard Worker " C.y = iResolution.y - C.y;"
97*c8dee2aaSAndroid Build Coastguard Worker " for (float i = 0; i < 3; ++i) {"
98*c8dee2aaSAndroid Build Coastguard Worker " vec2 U = C.yx / iResolution.yx;"
99*c8dee2aaSAndroid Build Coastguard Worker " U.y -= .5;"
100*c8dee2aaSAndroid Build Coastguard Worker " U.x = U.x * .4 + U.y * U.y;"
101*c8dee2aaSAndroid Build Coastguard Worker " U.y += U.x * sin(-iTime * 9. + i * 2. + U.x * 25.) * .2;"
102*c8dee2aaSAndroid Build Coastguard Worker " U.x -= asin(sin(U.y * 34.))/20.;"
103*c8dee2aaSAndroid Build Coastguard Worker " d = abs(U);"
104*c8dee2aaSAndroid Build Coastguard Worker " O += .3 * vec4(.8 * b(.3) + b(.2), b(.2), b(.1), -1.);"
105*c8dee2aaSAndroid Build Coastguard Worker " }"
106*c8dee2aaSAndroid Build Coastguard Worker " return O.xyz1;"
107*c8dee2aaSAndroid Build Coastguard Worker "}";
108*c8dee2aaSAndroid Build Coastguard Worker
109*c8dee2aaSAndroid Build Coastguard Worker auto [effect, err] = SkRuntimeEffect::MakeForShader(SkString(sksl));
110*c8dee2aaSAndroid Build Coastguard Worker if (!effect) {
111*c8dee2aaSAndroid Build Coastguard Worker SkDebugf("Failed to compile SkSL: %s\n", err.c_str());
112*c8dee2aaSAndroid Build Coastguard Worker return false;
113*c8dee2aaSAndroid Build Coastguard Worker }
114*c8dee2aaSAndroid Build Coastguard Worker
115*c8dee2aaSAndroid Build Coastguard Worker fWidth = width;
116*c8dee2aaSAndroid Build Coastguard Worker fHeight = height;
117*c8dee2aaSAndroid Build Coastguard Worker fCanvasSurface = getSurfaceForCanvas(device, canvasSelector, width, height);
118*c8dee2aaSAndroid Build Coastguard Worker fContext = context;
119*c8dee2aaSAndroid Build Coastguard Worker fEffect = effect;
120*c8dee2aaSAndroid Build Coastguard Worker
121*c8dee2aaSAndroid Build Coastguard Worker return true;
122*c8dee2aaSAndroid Build Coastguard Worker }
123*c8dee2aaSAndroid Build Coastguard Worker
setKind(DemoKind kind)124*c8dee2aaSAndroid Build Coastguard Worker void setKind(DemoKind kind) { fDemoKind = kind; }
125*c8dee2aaSAndroid Build Coastguard Worker
draw(int timestamp)126*c8dee2aaSAndroid Build Coastguard Worker void draw(int timestamp) {
127*c8dee2aaSAndroid Build Coastguard Worker wgpu::SurfaceTexture surfaceTexture;
128*c8dee2aaSAndroid Build Coastguard Worker fCanvasSurface.GetCurrentTexture(&surfaceTexture);
129*c8dee2aaSAndroid Build Coastguard Worker
130*c8dee2aaSAndroid Build Coastguard Worker GrDawnRenderTargetInfo rtInfo;
131*c8dee2aaSAndroid Build Coastguard Worker rtInfo.fTextureView = surfaceTexture.texture.CreateView();
132*c8dee2aaSAndroid Build Coastguard Worker rtInfo.fFormat = wgpu::TextureFormat::BGRA8Unorm;
133*c8dee2aaSAndroid Build Coastguard Worker rtInfo.fLevelCount = 1;
134*c8dee2aaSAndroid Build Coastguard Worker GrBackendRenderTarget backendRenderTarget(fWidth, fHeight, 1, 8, rtInfo);
135*c8dee2aaSAndroid Build Coastguard Worker SkSurfaceProps surfaceProps(0, kRGB_H_SkPixelGeometry);
136*c8dee2aaSAndroid Build Coastguard Worker
137*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkSurface> surface = SkSurfaces::WrapBackendRenderTarget(fContext.get(),
138*c8dee2aaSAndroid Build Coastguard Worker backendRenderTarget,
139*c8dee2aaSAndroid Build Coastguard Worker kTopLeft_GrSurfaceOrigin,
140*c8dee2aaSAndroid Build Coastguard Worker kN32_SkColorType,
141*c8dee2aaSAndroid Build Coastguard Worker nullptr,
142*c8dee2aaSAndroid Build Coastguard Worker &surfaceProps);
143*c8dee2aaSAndroid Build Coastguard Worker
144*c8dee2aaSAndroid Build Coastguard Worker SkPaint paint;
145*c8dee2aaSAndroid Build Coastguard Worker if (fDemoKind == DemoKind::SOLID_COLOR) {
146*c8dee2aaSAndroid Build Coastguard Worker drawSolidColor(&paint);
147*c8dee2aaSAndroid Build Coastguard Worker } else if (fDemoKind == DemoKind::GRADIENT) {
148*c8dee2aaSAndroid Build Coastguard Worker drawGradient(&paint);
149*c8dee2aaSAndroid Build Coastguard Worker } else if (fDemoKind == DemoKind::RUNTIME_EFFECT) {
150*c8dee2aaSAndroid Build Coastguard Worker drawRuntimeEffect(&paint, timestamp);
151*c8dee2aaSAndroid Build Coastguard Worker }
152*c8dee2aaSAndroid Build Coastguard Worker
153*c8dee2aaSAndroid Build Coastguard Worker // Schedule the recorded commands and wait until the GPU has executed them.
154*c8dee2aaSAndroid Build Coastguard Worker surface->getCanvas()->drawPaint(paint);
155*c8dee2aaSAndroid Build Coastguard Worker fContext->flushAndSubmit(surface, true);
156*c8dee2aaSAndroid Build Coastguard Worker fFrameCount++;
157*c8dee2aaSAndroid Build Coastguard Worker }
158*c8dee2aaSAndroid Build Coastguard Worker
drawSolidColor(SkPaint * paint)159*c8dee2aaSAndroid Build Coastguard Worker void drawSolidColor(SkPaint* paint) {
160*c8dee2aaSAndroid Build Coastguard Worker bool flipColor = fFrameCount % 2 == 0;
161*c8dee2aaSAndroid Build Coastguard Worker paint->setColor(flipColor ? SK_ColorCYAN : SK_ColorMAGENTA);
162*c8dee2aaSAndroid Build Coastguard Worker }
163*c8dee2aaSAndroid Build Coastguard Worker
drawGradient(SkPaint * paint)164*c8dee2aaSAndroid Build Coastguard Worker void drawGradient(SkPaint* paint) {
165*c8dee2aaSAndroid Build Coastguard Worker bool flipColor = fFrameCount % 2 == 0;
166*c8dee2aaSAndroid Build Coastguard Worker SkColor colors1[2] = {SK_ColorMAGENTA, SK_ColorCYAN};
167*c8dee2aaSAndroid Build Coastguard Worker SkColor colors2[2] = {SK_ColorCYAN, SK_ColorMAGENTA};
168*c8dee2aaSAndroid Build Coastguard Worker
169*c8dee2aaSAndroid Build Coastguard Worker float x = (float)fWidth / 2.f;
170*c8dee2aaSAndroid Build Coastguard Worker float y = (float)fHeight / 2.f;
171*c8dee2aaSAndroid Build Coastguard Worker paint->setShader(SkGradientShader::MakeRadial(SkPoint::Make(x, y),
172*c8dee2aaSAndroid Build Coastguard Worker std::min(x, y),
173*c8dee2aaSAndroid Build Coastguard Worker flipColor ? colors1 : colors2,
174*c8dee2aaSAndroid Build Coastguard Worker nullptr,
175*c8dee2aaSAndroid Build Coastguard Worker 2,
176*c8dee2aaSAndroid Build Coastguard Worker SkTileMode::kClamp));
177*c8dee2aaSAndroid Build Coastguard Worker }
178*c8dee2aaSAndroid Build Coastguard Worker
drawRuntimeEffect(SkPaint * paint,int timestamp)179*c8dee2aaSAndroid Build Coastguard Worker void drawRuntimeEffect(SkPaint* paint, int timestamp) {
180*c8dee2aaSAndroid Build Coastguard Worker DemoUniforms uniforms;
181*c8dee2aaSAndroid Build Coastguard Worker uniforms.width = fWidth;
182*c8dee2aaSAndroid Build Coastguard Worker uniforms.height = fHeight;
183*c8dee2aaSAndroid Build Coastguard Worker uniforms.time = static_cast<float>(timestamp) / 1000.f;
184*c8dee2aaSAndroid Build Coastguard Worker
185*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkData> uniformData = SkData::MakeWithCopy(&uniforms, sizeof(uniforms));
186*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkShader> shader = fEffect->makeShader(std::move(uniformData), /*children=*/{});
187*c8dee2aaSAndroid Build Coastguard Worker paint->setShader(shader);
188*c8dee2aaSAndroid Build Coastguard Worker }
189*c8dee2aaSAndroid Build Coastguard Worker
190*c8dee2aaSAndroid Build Coastguard Worker private:
191*c8dee2aaSAndroid Build Coastguard Worker int fFrameCount = 0;
192*c8dee2aaSAndroid Build Coastguard Worker int fWidth;
193*c8dee2aaSAndroid Build Coastguard Worker int fHeight;
194*c8dee2aaSAndroid Build Coastguard Worker wgpu::Surface fCanvasSurface;
195*c8dee2aaSAndroid Build Coastguard Worker sk_sp<GrDirectContext> fContext;
196*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkRuntimeEffect> fEffect;
197*c8dee2aaSAndroid Build Coastguard Worker DemoKind fDemoKind = DemoKind::SOLID_COLOR;
198*c8dee2aaSAndroid Build Coastguard Worker };
199*c8dee2aaSAndroid Build Coastguard Worker
EMSCRIPTEN_BINDINGS(Skia)200*c8dee2aaSAndroid Build Coastguard Worker EMSCRIPTEN_BINDINGS(Skia) {
201*c8dee2aaSAndroid Build Coastguard Worker emscripten::enum_<DemoKind>("DemoKind")
202*c8dee2aaSAndroid Build Coastguard Worker .value("SOLID_COLOR", DemoKind::SOLID_COLOR)
203*c8dee2aaSAndroid Build Coastguard Worker .value("GRADIENT", DemoKind::GRADIENT)
204*c8dee2aaSAndroid Build Coastguard Worker .value("RUNTIME_EFFECT", DemoKind::RUNTIME_EFFECT);
205*c8dee2aaSAndroid Build Coastguard Worker emscripten::class_<Demo>("Demo")
206*c8dee2aaSAndroid Build Coastguard Worker .constructor()
207*c8dee2aaSAndroid Build Coastguard Worker .function("init", &Demo::init)
208*c8dee2aaSAndroid Build Coastguard Worker .function("setKind", &Demo::setKind)
209*c8dee2aaSAndroid Build Coastguard Worker .function("draw", &Demo::draw);
210*c8dee2aaSAndroid Build Coastguard Worker }
211