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