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