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 #include <emscripten/bind.h>
8 #include <emscripten/emscripten.h>
9 #include <emscripten/html5.h>
10 // https://github.com/emscripten-core/emscripten/blob/main/system/include/emscripten/html5_webgpu.h
11 // The import/export functions defined here should allow us to fetch a handle to a given JS
12 // Texture/Sampler/Device etc if needed.
13 #include <emscripten/html5_webgpu.h>
14 // https://github.com/emscripten-core/emscripten/blob/main/system/include/webgpu/webgpu.h
15 // This defines WebGPU constants and such. It also includes a lot of typedefs that make something
16 // like WGPUDevice defined as a pointer to something external. These "pointers" are actually just
17 // a small integer that refers to an array index of JS objects being held by a "manager"
18 // https://github.com/emscripten-core/emscripten/blob/f47bef371f3464471c6d30b631cffcdd06ced004/src/library_webgpu.js#L192
19 #include <webgpu/webgpu.h>
20 // https://github.com/emscripten-core/emscripten/blob/main/system/include/webgpu/webgpu_cpp.h
21 // This defines the C++ equivalents to the JS WebGPU API.
22 #include <webgpu/webgpu_cpp.h>
23
24 using namespace emscripten;
25
createShaderModule(wgpu::Device device,const char * source)26 wgpu::ShaderModule createShaderModule(wgpu::Device device, const char* source) {
27 // https://github.com/emscripten-core/emscripten/blob/da842597941f425e92df0b902d3af53f1bcc2713/system/include/webgpu/webgpu_cpp.h#L1415
28 wgpu::ShaderModuleWGSLDescriptor wDesc;
29 wDesc.source = source;
30 wgpu::ShaderModuleDescriptor desc = {.nextInChain = &wDesc};
31 return device.CreateShaderModule(&desc);
32 }
33
createRenderPipeline(wgpu::Device device,wgpu::ShaderModule vertexShader,wgpu::ShaderModule fragmentShader)34 wgpu::RenderPipeline createRenderPipeline(wgpu::Device device, wgpu::ShaderModule vertexShader,
35 wgpu::ShaderModule fragmentShader) {
36 wgpu::ColorTargetState colorTargetState{};
37 colorTargetState.format = wgpu::TextureFormat::BGRA8Unorm;
38
39 wgpu::FragmentState fragmentState{};
40 fragmentState.module = fragmentShader;
41 fragmentState.entryPoint = "main"; // assumes main() is defined in fragment shader code
42 fragmentState.targetCount = 1;
43 fragmentState.targets = &colorTargetState;
44
45 wgpu::PipelineLayoutDescriptor pl{};
46
47 // Inspired by https://github.com/kainino0x/webgpu-cross-platform-demo/blob/4061dd13096580eb5525619714145087b0d5acf6/main.cpp#L129
48 wgpu::RenderPipelineDescriptor pipelineDescriptor{};
49 pipelineDescriptor.layout = device.CreatePipelineLayout(&pl);
50 pipelineDescriptor.vertex.module = vertexShader;
51 pipelineDescriptor.vertex.entryPoint = "main"; // assumes main() is defined in vertex code
52 pipelineDescriptor.fragment = &fragmentState;
53 pipelineDescriptor.primitive.topology = wgpu::PrimitiveTopology::TriangleList;
54 return device.CreateRenderPipeline(&pipelineDescriptor);
55 }
56
getSwapChainForCanvas(wgpu::Device device,std::string canvasSelector,int width,int height)57 wgpu::SwapChain getSwapChainForCanvas(wgpu::Device device, std::string canvasSelector, int width, int height) {
58 wgpu::SurfaceDescriptorFromCanvasHTMLSelector surfaceSelector;
59 surfaceSelector.selector = canvasSelector.c_str();
60
61 wgpu::SurfaceDescriptor surface_desc;
62 surface_desc.nextInChain = &surfaceSelector;
63 wgpu::Instance instance;
64 wgpu::Surface surface = instance.CreateSurface(&surface_desc);
65
66 wgpu::SwapChainDescriptor swap_chain_desc;
67 swap_chain_desc.format = wgpu::TextureFormat::BGRA8Unorm;
68 swap_chain_desc.usage = wgpu::TextureUsage::RenderAttachment;
69 swap_chain_desc.presentMode = wgpu::PresentMode::Fifo;
70 swap_chain_desc.width = width;
71 swap_chain_desc.height = height;
72 return device.CreateSwapChain(surface, &swap_chain_desc);
73 }
74
drawPipeline(wgpu::Device device,wgpu::TextureView view,wgpu::RenderPipeline pipeline,wgpu::Color clearColor)75 void drawPipeline(wgpu::Device device, wgpu::TextureView view, wgpu::RenderPipeline pipeline,
76 wgpu::Color clearColor) {
77 wgpu::RenderPassColorAttachment attachment{};
78 attachment.view = view;
79 attachment.loadOp = wgpu::LoadOp::Clear;
80 attachment.storeOp = wgpu::StoreOp::Store;
81 attachment.clearColor = clearColor;
82
83 wgpu::RenderPassDescriptor renderpass{};
84 renderpass.colorAttachmentCount = 1;
85 renderpass.colorAttachments = &attachment;
86 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
87 wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderpass);
88 pass.SetPipeline(pipeline);
89 pass.Draw(3, // vertexCount
90 1, // instanceCount
91 0, // firstIndex
92 0 // firstInstance
93 );
94 pass.EndPass();
95 wgpu::CommandBuffer commands = encoder.Finish();
96 device.GetQueue().Submit(1, &commands);
97 }
98
99 class WebGPUSurface {
100 public:
WebGPUSurface(std::string canvasSelector,int width,int height)101 WebGPUSurface(std::string canvasSelector, int width, int height) {
102 fDevice = wgpu::Device::Acquire(emscripten_webgpu_get_device());
103 fCanvasSwap = getSwapChainForCanvas(fDevice, canvasSelector, width, height);
104 }
105
makeShader(std::string source)106 wgpu::ShaderModule makeShader(std::string source) {
107 return createShaderModule(fDevice, source.c_str());
108 }
109
makeRenderPipeline(wgpu::ShaderModule vertexShader,wgpu::ShaderModule fragmentShader)110 wgpu::RenderPipeline makeRenderPipeline(wgpu::ShaderModule vertexShader,
111 wgpu::ShaderModule fragmentShader) {
112 return createRenderPipeline(fDevice, vertexShader, fragmentShader);
113 }
114
drawPipeline(wgpu::RenderPipeline pipeline,float r,float g,float b,float a)115 void drawPipeline(wgpu::RenderPipeline pipeline, float r, float g, float b, float a) {
116 // We cannot cache the TextureView because it will be destroyed after use.
117 ::drawPipeline(fDevice, fCanvasSwap.GetCurrentTextureView(), pipeline, {r, g, b, a});
118 }
119
120 private:
121 wgpu::Device fDevice;
122 wgpu::SwapChain fCanvasSwap;
123 };
124
EMSCRIPTEN_BINDINGS(Skia)125 EMSCRIPTEN_BINDINGS(Skia) {
126 class_<WebGPUSurface>("WebGPUSurface")
127 .constructor<std::string, int, int>()
128 .function("MakeShader", &WebGPUSurface::makeShader)
129 .function("MakeRenderPipeline", &WebGPUSurface::makeRenderPipeline)
130 .function("drawPipeline", &WebGPUSurface::drawPipeline);
131
132 class_<wgpu::ShaderModule>("ShaderModule");
133 class_<wgpu::RenderPipeline>("RenderPipeline");
134 }
135