• 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 #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