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