├── .gitattributes ├── .github └── workflows │ └── build.yml ├── .gitignore ├── .gitmodules ├── .npmignore ├── LICENSE ├── README.md ├── build.js ├── generator ├── generators │ ├── ast.mjs │ ├── descriptorDecoder.mjs │ ├── gyp.mjs │ ├── index.mjs │ └── memoryLayouts.mjs ├── index.mjs ├── specifications │ └── 0.0.1.json ├── templates │ ├── DescriptorDecoder-cpp.njk │ ├── DescriptorDecoder-h.njk │ ├── binding-gyp.njk │ ├── index-cpp.njk │ ├── index-h.njk │ └── memoryLayouts-h.njk ├── types.mjs └── utils.mjs ├── index.js ├── lib ├── darwin │ └── x64 │ │ └── GLFW │ │ └── libglfw3.a ├── include │ └── GLFW │ │ ├── glfw3.h │ │ └── glfw3native.h ├── linux │ └── x64 │ │ └── GLFW │ │ └── libglfw3.a └── win │ └── x64 │ ├── DXC │ ├── dxcompiler.dll │ └── dxil.dll │ └── GLFW │ ├── glfw3.dll │ ├── glfw3.lib │ └── glfw3dll.lib ├── package.json ├── src ├── BackendBinding.cpp ├── BackendBinding.h ├── Base.h ├── D3D12Binding.cpp ├── GPU.cpp ├── GPU.h ├── GPUAdapter.cpp ├── GPUAdapter.h ├── GPUBindGroup.cpp ├── GPUBindGroup.h ├── GPUBindGroupLayout.cpp ├── GPUBindGroupLayout.h ├── GPUBuffer.cpp ├── GPUBuffer.h ├── GPUCanvasContext.cpp ├── GPUCanvasContext.h ├── GPUCommandBuffer.cpp ├── GPUCommandBuffer.h ├── GPUCommandEncoder.cpp ├── GPUCommandEncoder.h ├── GPUComputePassEncoder.cpp ├── GPUComputePassEncoder.h ├── GPUComputePipeline.cpp ├── GPUComputePipeline.h ├── GPUDevice.cpp ├── GPUDevice.h ├── GPUFence.cpp ├── GPUFence.h ├── GPUPipelineLayout.cpp ├── GPUPipelineLayout.h ├── GPUQueue.cpp ├── GPUQueue.h ├── GPURayTracingAccelerationContainer.cpp ├── GPURayTracingAccelerationContainer.h ├── GPURayTracingPassEncoder.cpp ├── GPURayTracingPassEncoder.h ├── GPURayTracingPipeline.cpp ├── GPURayTracingPipeline.h ├── GPURayTracingShaderBindingTable.cpp ├── GPURayTracingShaderBindingTable.h ├── GPURenderBundle.cpp ├── GPURenderBundle.h ├── GPURenderBundleEncoder.cpp ├── GPURenderBundleEncoder.h ├── GPURenderPassEncoder.cpp ├── GPURenderPassEncoder.h ├── GPURenderPipeline.cpp ├── GPURenderPipeline.h ├── GPUSampler.cpp ├── GPUSampler.h ├── GPUShaderModule.cpp ├── GPUShaderModule.h ├── GPUSwapChain.cpp ├── GPUSwapChain.h ├── GPUTexture.cpp ├── GPUTexture.h ├── GPUTextureView.cpp ├── GPUTextureView.h ├── MetalBackend.h ├── MetalBinding.mm ├── NullBinding.cpp ├── SwapChainUtils.h ├── Utils.h ├── VulkanBinding.cpp ├── WebGPUWindow.cpp └── WebGPUWindow.h └── tests └── index.mjs /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | strategy: 8 | matrix: 9 | os: 10 | - windows-latest 11 | - ubuntu-latest 12 | - macos-latest 13 | name: ${{ matrix.os }} 14 | runs-on: ${{ matrix.os }} 15 | 16 | steps: 17 | - name: Checkout webgpu 18 | uses: actions/checkout@v2 19 | - name: Checkout dawn 20 | uses: actions/checkout@v2 21 | with: 22 | repository: maierfelix/dawn-ray-tracing 23 | ref: bae8c688fe96d7fd07136d05b4a6931e9e6ead4d 24 | path: dawn 25 | - uses: actions/setup-node@v1 26 | with: 27 | node-version: '13.x' 28 | 29 | # linux /macos 30 | - name: Linux/MacOS - Add tools for dawn 31 | if: matrix.os != 'windows-latest' 32 | shell: bash 33 | run: | 34 | git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git 35 | export PATH=`pwd`/depot_tools:$PATH 36 | cd dawn 37 | cp scripts/standalone.gclient .gclient 38 | gclient sync 39 | - name: Linux - Install tools and libs from apt 40 | if: matrix.os == 'ubuntu-latest' 41 | run: | 42 | sudo apt-get install -y \ 43 | clang \ 44 | libosmesa6-dev \ 45 | libvulkan-dev \ 46 | libxcursor-dev \ 47 | libxi-dev \ 48 | libxinerama-dev \ 49 | libxrandr-dev \ 50 | libxxf86vm-dev \ 51 | - name: Linux/MacOS - Build dawn 52 | if: matrix.os != 'windows-latest' 53 | shell: bash 54 | run: | 55 | export PATH=`pwd`/depot_tools:$PATH 56 | cd dawn 57 | gn gen out/Shared --target_cpu="x64" --args="is_component_build=true is_debug=false is_clang=true" 58 | ninja -C out/Shared 59 | - name: Linux/MacOS - Build webgpu 60 | if: matrix.os != 'windows-latest' 61 | shell: bash 62 | run: | 63 | printf `pwd`/dawn > PATH_TO_DAWN 64 | npm install 65 | npm run all --dawnversion=0.0.1 66 | ls -l generated/0.0.1/*/build/Release 67 | 68 | # windows 69 | - name: Windows - Add tools for dawn 70 | if: matrix.os == 'windows-latest' 71 | run: | 72 | Write-Host "get depot_tools" 73 | (New-Object System.Net.WebClient).DownloadFile("https://storage.googleapis.com/chrome-infra/depot_tools.zip", "$pwd\depot_tools.zip") 74 | Write-Host "unzip depot_tools" 75 | & 7z x -odepot_tools -bd .\depot_tools.zip 76 | $env:Path="${pwd}\depot_tools;$env:Path" 77 | $env:DEPOT_TOOLS_WIN_TOOLCHAIN=0 78 | $env:GYP_MSVS_VERSION=2019 79 | cd dawn 80 | cp .\scripts\standalone.gclient .gclient 81 | Write-Host "gclient sync" 82 | & gclient sync 83 | & python -m pip install pywin32 84 | - name: Windows - Build dawn 85 | if: matrix.os == 'windows-latest' 86 | run: | 87 | $env:Path="${pwd}\depot_tools;$env:Path" 88 | $env:DEPOT_TOOLS_WIN_TOOLCHAIN=0 89 | $env:GYP_MSVS_VERSION=2019 90 | cd dawn 91 | gn gen out/Shared --ide=vs --target_cpu="x64" --args="is_component_build=true is_debug=false is_clang=false" 92 | ninja -C out/Shared 93 | - name: Windows - Build webgpu 94 | if: matrix.os == 'windows-latest' 95 | run: | 96 | $env:GYP_MSVS_VERSION=2019 97 | [System.IO.File]::WriteAllText("$pwd/PATH_TO_DAWN", ((Resolve-Path .\dawn).Path -Replace "/$" -Replace "\\", "/")) 98 | cat PATH_TO_DAWN 99 | npm install 100 | npm run all --dawnversion=0.0.1 101 | ls generated/0.0.1/*/build/Release 102 | 103 | # upload 104 | - name: Upload build 105 | uses: actions/upload-artifact@v1 106 | with: 107 | name: generated-${{ matrix.os }} 108 | path: generated 109 | 110 | publish: 111 | needs: build 112 | name: publish 113 | runs-on: ubuntu-latest 114 | steps: 115 | - name: Checkout webgpu 116 | uses: actions/checkout@v2 117 | - name: Download linux build 118 | uses: actions/download-artifact@v1 119 | with: 120 | name: generated-ubuntu-latest 121 | path: generated 122 | - name: Download windows build 123 | uses: actions/download-artifact@v1 124 | with: 125 | name: generated-windows-latest 126 | path: generated 127 | - name: Download macos build 128 | uses: actions/download-artifact@v1 129 | with: 130 | name: generated-macos-latest 131 | path: generated 132 | - name: Publish to NPM 133 | uses: primer/publish@v2.0.0 134 | env: 135 | NPM_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 136 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 137 | 138 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # TypeScript v1 declaration files 45 | typings/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Optional REPL history 57 | .node_repl_history 58 | 59 | # Output of 'npm pack' 60 | *.tgz 61 | 62 | # Yarn Integrity file 63 | .yarn-integrity 64 | 65 | # dotenv environment variables file 66 | .env 67 | .env.test 68 | 69 | # parcel-bundler cache (https://parceljs.org/) 70 | .cache 71 | 72 | # next.js build output 73 | .next 74 | 75 | # nuxt.js build output 76 | .nuxt 77 | 78 | # vuepress build output 79 | .vuepress/dist 80 | 81 | # Serverless directories 82 | .serverless/ 83 | 84 | # FuseBox cache 85 | .fusebox/ 86 | 87 | # DynamoDB Local files 88 | .dynamodb/ 89 | 90 | .DS_STORE 91 | 92 | www 93 | 94 | package-lock.json 95 | 96 | /generated 97 | 98 | PATH_TO_DAWN 99 | 100 | package-lock.json 101 | 102 | examples-dev 103 | 104 | generated/*/*/build/.vs 105 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "examples"] 2 | path = examples 3 | url = https://github.com/maierfelix/webgpu-examples 4 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # TypeScript v1 declaration files 45 | typings/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Optional REPL history 57 | .node_repl_history 58 | 59 | # Output of 'npm pack' 60 | *.tgz 61 | 62 | # Yarn Integrity file 63 | .yarn-integrity 64 | 65 | # dotenv environment variables file 66 | .env 67 | .env.test 68 | 69 | # parcel-bundler cache (https://parceljs.org/) 70 | .cache 71 | 72 | # next.js build output 73 | .next 74 | 75 | # nuxt.js build output 76 | .nuxt 77 | 78 | # vuepress build output 79 | .vuepress/dist 80 | 81 | # Serverless directories 82 | .serverless/ 83 | 84 | # FuseBox cache 85 | .fusebox/ 86 | 87 | # DynamoDB Local files 88 | .dynamodb/ 89 | 90 | www 91 | 92 | package-lock.json 93 | 94 | generated/*/*/ast.json 95 | generated/*/*/memoryLayouts.json 96 | generated/*/*/build/.vs 97 | generated/*/*/build/*.lib 98 | generated/*/*/build/*.vcxproj 99 | generated/*/*/build/*.filters 100 | generated/*/*/build/*.users 101 | generated/*/*/build/*.user 102 | generated/*/*/build/*.sln 103 | generated/*/*/build/Release/obj 104 | generated/*/*/build/Release/*.iobj 105 | generated/*/*/build/Release/*.ipdb 106 | generated/*/*/build/Release/*.pdb 107 | generated/*/*/build/Release/*.map 108 | generated/*/*/build/Release/*.ilk 109 | generated/*/*/build/Release/.deps 110 | generated/*/*/build/Release/obj.target 111 | 112 | PATH_TO_DAWN 113 | 114 | package-lock.json 115 | 116 | node_modules 117 | 118 | examples 119 | examples-dev 120 | 121 | lib 122 | src 123 | generator 124 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 maierfelix 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | This is a WebGPU API for native JavaScript, based on a Fork of Chromium's Dawn Project. 3 | 4 |
5 |
6 | 7 | NPM Version 8 | 9 | 10 | NPM Downloads 11 | 12 |

13 | 14 | ### Platforms 15 | 16 | This project comes with pre-built N-API binaries for the following platforms: 17 | 18 | | OS | Status | 19 | | ------------- | ------------- | 20 | | Windows | ‌‌ ‌‌ ‌‌ ‌‌ ‌‌ ‌‌ ✔ ‌‌ ‌‌ ‌‌ ‌‌ ‌‌ ‌‌| 21 | | Linux | ‌‌ ‌‌ ‌‌ ‌‌ ‌‌ ‌‌ ‌‌✔ ‌‌ ‌‌ ‌‌ ‌‌ ‌‌ ‌‌| 22 | | MacOS | ‌‌ ‌‌ ‌‌ ‌‌ ‌‌ ‌‌ ‌‌✔ ‌‌ ‌‌ ‌‌ ‌‌ ‌‌ ‌‌| 23 | 24 | ### Installation 25 | 26 | ```` 27 | npm install webgpu 28 | ```` 29 | 30 | ## Building 31 | 32 | You have to build [dawn](https://dawn.googlesource.com/dawn) as a shared library. 33 | After building dawn, create a file named `PATH_TO_DAWN` in this project's root directory, containing the **absolute** path to dawn. 34 | 35 | In case you have multiple python installations, you might want to use the `--script-executable` gn flag to instruct *gn* to use the python 2.x installation. 36 | 37 | After you have generated and built dawn, you can now build this project by running: 38 | ```` 39 | npm run all --dawnversion=0.0.1 40 | ```` 41 | 42 | ### Windows 43 | 44 | Follow dawn's initial setup instructions, but instead of the standard build, do the following: 45 | 46 | To generate the project as a shared library using MSVS: 47 | ```` 48 | gn gen out/Shared --ide=vs --target_cpu="x64" --args="is_component_build=true is_debug=false is_clang=false" 49 | ```` 50 | It's important that you build using MSVS and **not** clang, as otherwise you will potentially get linking errors. 51 | 52 | To build the project run: 53 | ```` 54 | ninja -C out/Shared 55 | ```` 56 | 57 | In case python wasn't found: 58 | - Use `where python` to get the location of your python installation 59 | - Repoint it by running e.g. `npm config set python C:\depot_tools\python.bat` 60 | 61 | ### Linux 62 | 63 | Follow dawn's initial setup instructions, but instead of the standard build, do the following: 64 | 65 | To generate the project as a shared library: 66 | ```` 67 | gn gen out/Shared --target_cpu="x64" --args="is_component_build=true is_debug=false is_clang=true" 68 | ```` 69 | 70 | To build the project run: 71 | ```` 72 | ninja -C out/Shared 73 | ```` 74 | 75 | ### MacOS 76 | 77 | Follow dawn's initial setup instructions, but instead of the standard build, do the following: 78 | 79 | To generate the project as a shared library: 80 | ```` 81 | gn gen out/Shared --target_cpu="x64" --args="is_component_build=true is_debug=false is_clang=true" 82 | ```` 83 | 84 | To build the project run: 85 | ```` 86 | ninja -C out/Shared 87 | ```` 88 | 89 | ## Examples 90 | ```` 91 | cd examples & cd .. 92 | node --experimental-modules examples/interactive-triangle.mjs 93 | ```` 94 | 95 | ## TODOs 96 | - Add CTS 97 | - Remove libshaderc from build? 98 | -------------------------------------------------------------------------------- /build.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const ncp = require("ncp"); 3 | const { spawn } = require("child_process"); 4 | 5 | const pkg = require("./package.json"); 6 | 7 | const platform = process.platform; 8 | const v8Version = process.versions.v8; 9 | const nodeVersion = process.versions.node; 10 | const architecture = process.arch; 11 | 12 | ncp.limit = 16; 13 | 14 | const dawnVersion = process.env.npm_config_dawnversion; 15 | if (!dawnVersion) throw `No Dawn version --dawnversion specified!`; 16 | 17 | const bypassBuild = !!process.env.npm_config_bypass_build; 18 | 19 | const msvsVersion = process.env.npm_config_msvsversion || ""; 20 | 21 | const generatePath = `${pkg.config.GEN_OUT_DIR}/${dawnVersion}/${platform}`; 22 | 23 | const unitPlatform = ( 24 | platform === "win32" ? "win" : 25 | platform === "linux" ? "linux" : 26 | platform === "darwin" ? "darwin" : 27 | "unknown" 28 | ); 29 | 30 | if (unitPlatform === "unknown") { 31 | process.stderr.write(`Unsupported platform!\n`); 32 | process.stderr.write(`Exiting..\n`); 33 | return; 34 | } 35 | 36 | // generated/version/ 37 | if (!fs.existsSync(generatePath)) { 38 | process.stderr.write(`Cannot find bindings for ${dawnVersion} in ${generatePath}\n`); 39 | process.stderr.write(`Exiting..\n`); 40 | return; 41 | } 42 | 43 | if (bypassBuild) { 44 | process.stderr.write(`Skipping build..\n`); 45 | } 46 | 47 | // build 48 | // build/release 49 | let buildDir = `${generatePath}/build/`; 50 | let buildOutputDir = buildDir + "Release/"; 51 | if (!fs.existsSync(buildDir)) fs.mkdirSync(buildDir); 52 | if (!fs.existsSync(buildOutputDir)) fs.mkdirSync(buildOutputDir); 53 | 54 | process.stdout.write(` 55 | Compiling bindings for version ${dawnVersion}... 56 | Platform: ${platform} | ${architecture} 57 | Node: ${nodeVersion} 58 | V8: ${v8Version} 59 | `); 60 | 61 | function copyFiles() { 62 | process.stdout.write(`\nCopying files..\n`); 63 | return new Promise(resolve => { 64 | // copy files into release folder 65 | let dawnDir = fs.readFileSync(pkg.config.DAWN_PATH, "utf-8"); 66 | let dawnOutputDir = dawnDir + "/out/Shared"; 67 | let baseDir = `./lib/${unitPlatform}/${architecture}`; 68 | let targetDir = buildOutputDir; 69 | let files = [ 70 | // src folder 71 | [`./src/`, targetDir + "/../../src/"] 72 | ]; 73 | // add win32 runtime files 74 | if (platform === "win32") { 75 | files.push([`${baseDir}/GLFW/glfw3.dll`, targetDir]); 76 | files.push([`${baseDir}/DXC/dxcompiler.dll`, targetDir]); 77 | files.push([`${baseDir}/DXC/dxil.dll`, targetDir]); 78 | // dawn dlls 79 | { 80 | //files.push([`${dawnOutputDir}/libc++.dll`, targetDir]); 81 | //files.push([`${dawnOutputDir}/libdawn.dll`, targetDir]); 82 | files.push([`${dawnOutputDir}/dawn_native.dll`, targetDir]); 83 | files.push([`${dawnOutputDir}/dawn_proc.dll`, targetDir]); 84 | files.push([`${dawnOutputDir}/dawn_wire.dll`, targetDir]); 85 | files.push([`${dawnOutputDir}/libshaderc.dll`, targetDir]); 86 | files.push([`${dawnOutputDir}/libshaderc_spvc.dll`, targetDir]); 87 | } 88 | // dawn libs 89 | { 90 | //files.push([`${dawnOutputDir}/libc++.dll.lib`, targetDir + "/../"]); 91 | //files.push([`${dawnOutputDir}/libdawn.dll.lib`, targetDir + "/../"]); 92 | files.push([`${dawnOutputDir}/dawn_native.dll.lib`, targetDir + "/../"]); 93 | files.push([`${dawnOutputDir}/dawn_proc.dll.lib`, targetDir + "/../"]); 94 | files.push([`${dawnOutputDir}/dawn_wire.dll.lib`, targetDir + "/../"]); 95 | files.push([`${dawnOutputDir}/libshaderc.dll.lib`, targetDir + "/../"]); 96 | files.push([`${dawnOutputDir}/libshaderc_spvc.dll.lib`, targetDir + "/../"]); 97 | } 98 | } 99 | // add darwin runtime files 100 | else if (platform === "darwin") { 101 | files.push([`${dawnOutputDir}/libdawn_native.dylib`, targetDir]); 102 | files.push([`${dawnOutputDir}/libdawn_proc.dylib`, targetDir]); 103 | files.push([`${dawnOutputDir}/libdawn_wire.dylib`, targetDir]); 104 | files.push([`${dawnOutputDir}/libshaderc_spvc.dylib`, targetDir]); 105 | files.push([`${dawnOutputDir}/libshaderc.dylib`, targetDir]); 106 | files.push([`${dawnOutputDir}/libc++.dylib`, targetDir]); 107 | } 108 | else if (platform === "linux") { 109 | files.push([`${dawnOutputDir}/libdawn_native.so`, targetDir]); 110 | files.push([`${dawnOutputDir}/libdawn_proc.so`, targetDir]); 111 | files.push([`${dawnOutputDir}/libdawn_wire.so`, targetDir]); 112 | files.push([`${dawnOutputDir}/libshaderc_spvc.so`, targetDir]); 113 | files.push([`${dawnOutputDir}/libshaderc.so`, targetDir]); 114 | files.push([`${dawnOutputDir}/libc++.so`, targetDir]); 115 | } 116 | let counter = 0; 117 | if (!files.length) return resolve(true); 118 | files.map(entry => { 119 | let source = entry[0]; 120 | let target = entry[1]; 121 | // copy single files 122 | let fileName = source.replace(/^.*[\\\/]/, ""); 123 | let isFile = fileName.length > 0; 124 | if (isFile) target += "/" + fileName; 125 | // copy 126 | ncp(source, target, error => { 127 | process.stdout.write(`Copying ${source} -> ${target}\n`); 128 | if (error) { 129 | process.stderr.write(`Failed to copy ${source} -> ${target}\n`); 130 | throw error; 131 | } 132 | }); 133 | if (counter++ >= files.length - 1) { 134 | process.stdout.write("Done!\n"); 135 | resolve(true); 136 | } 137 | }); 138 | }); 139 | }; 140 | 141 | function buildFiles() { 142 | process.stdout.write(`\nCompiling bindings..\n`); 143 | return new Promise(resolve => { 144 | let msargs = ""; 145 | // add win32 vs version 146 | if (platform === "win32") { 147 | msargs += `--msvs_version ${msvsVersion}`; 148 | } 149 | let cmd = `cd ${generatePath} && node-gyp configure && node-gyp build`; 150 | let shell = spawn(cmd, { shell: true, stdio: "inherit" }, { stdio: "pipe" }); 151 | shell.on("exit", error => { 152 | if (!error) { 153 | actionsAfter(); 154 | process.stdout.write("Done!\n"); 155 | } 156 | resolve(!error); 157 | }); 158 | }); 159 | }; 160 | 161 | function inlineMemoryLayouts() { 162 | const addon = require(`${buildOutputDir}/addon-${platform}.node`); 163 | if (!addon.$getMemoryLayouts) return; 164 | process.stdout.write(`Inlining memory layouts..\n`); 165 | let memoryLayouts = addon.$getMemoryLayouts(); 166 | if (!fs.existsSync(`${generatePath}/memoryLayouts.json`)) { 167 | process.stdout.write(`Pending bootstrapping, module should be re-generated!\n`); 168 | } 169 | fs.writeFileSync(`${generatePath}/memoryLayouts.json`, JSON.stringify(memoryLayouts, null, 2)); 170 | }; 171 | 172 | function actionsAfter() { 173 | //inlineMemoryLayouts(); 174 | }; 175 | 176 | (async function run() { 177 | await copyFiles(); 178 | let buildSuccess = false; 179 | if (!bypassBuild) { 180 | buildSuccess = await buildFiles(); 181 | } else { 182 | buildSuccess = true; 183 | } 184 | if (buildSuccess) { 185 | process.stdout.write(`\nSuccessfully compiled bindings for ${dawnVersion}!\n`); 186 | } else { 187 | process.stderr.write(`\nFailed to compile bindings for ${dawnVersion}!`); 188 | } 189 | })(); 190 | -------------------------------------------------------------------------------- /generator/generators/ast.mjs: -------------------------------------------------------------------------------- 1 | import { 2 | warn, 3 | getCamelizedName, 4 | getSnakeCaseName, 5 | firstLetterToUpperCase, 6 | getSnakeCaseFromCamelCaseName 7 | } from "../utils.mjs"; 8 | 9 | import { 10 | getASTType, 11 | getASTNodeByName, 12 | getASTCategoryByName, 13 | getDawnDeclarationName, 14 | getExplortDeclarationName 15 | } from "../types.mjs"; 16 | 17 | export default function generateAST(ast) { 18 | let out = {}; 19 | // normalize 20 | { 21 | let normalized = []; 22 | for (let key in ast) { 23 | if (!ast.hasOwnProperty(key)) continue; 24 | if (key === "_comment") continue; 25 | normalized.push({ 26 | textName: key, 27 | ...ast[key] 28 | }); 29 | }; 30 | // overwrite input with normalized input 31 | ast = normalized; 32 | } 33 | // generate enum nodes 34 | { 35 | let enums = ast.filter(node => { 36 | return node.category === "enum"; 37 | }); 38 | enums = enums.map(enu => { 39 | let node = {}; 40 | let {textName} = enu; 41 | node.name = getDawnDeclarationName(textName); 42 | node.externalName = getExplortDeclarationName(node.name); 43 | node.type = getASTCategoryByName(textName, ast); 44 | node.textName = textName; 45 | node.children = []; 46 | enu.values.map(member => { 47 | let {value} = member; 48 | let name = member.name; 49 | let type = { 50 | isString: true, 51 | nativeType: "char", 52 | jsType: { isString: true, type: "String" } 53 | }; 54 | let child = { 55 | name, 56 | type, 57 | value 58 | }; 59 | node.children.push(child); 60 | }); 61 | return node; 62 | }); 63 | out.enums = enums; 64 | } 65 | // generate bitmask nodes 66 | { 67 | let bitmasks = ast.filter(node => { 68 | return node.category === "bitmask"; 69 | }); 70 | bitmasks = bitmasks.map(bitmask => { 71 | let node = {}; 72 | let {textName} = bitmask; 73 | node.name = getDawnDeclarationName(textName); 74 | node.externalName = getExplortDeclarationName(node.name); 75 | node.type = getASTCategoryByName(textName, ast); 76 | node.textName = textName; 77 | node.children = []; 78 | bitmask.values.map(member => { 79 | let {value} = member; 80 | let name = getSnakeCaseName(member.name); 81 | let type = { 82 | isNumber: true, 83 | nativeType: "uint32_t", 84 | jsType: { isNumber: true, type: "Number" } 85 | }; 86 | let child = { 87 | name, 88 | type, 89 | value 90 | }; 91 | node.children.push(child); 92 | }); 93 | return node; 94 | }); 95 | out.bitmasks = bitmasks; 96 | } 97 | // generate object nodes 98 | { 99 | let objects = ast.filter(node => { 100 | return node.category === "object"; 101 | }); 102 | objects = objects.map(object => { 103 | let node = {}; 104 | let {textName} = object; 105 | node.name = getDawnDeclarationName(textName); 106 | node.externalName = getExplortDeclarationName(node.name); 107 | node.type = getASTCategoryByName(textName, ast); 108 | node.textName = textName; 109 | node.children = []; 110 | // process the object's methods 111 | (object.methods || []).map(method => { 112 | let name = getCamelizedName(method.name); 113 | let child = { 114 | name, 115 | children: [] 116 | }; 117 | if (method.returns) { 118 | child.type = getASTType({ type: method.returns }, ast); 119 | } else { 120 | child.type = getASTType({ type: "void" }, ast); 121 | } 122 | // process the method's arguments 123 | (method.args || []).map(arg => { 124 | let name = getCamelizedName(arg.name); 125 | let type = getASTType(arg, ast); 126 | let argChild = { 127 | name, 128 | type 129 | }; 130 | if (arg.optional) argChild.isOptional = true; 131 | child.children.push(argChild); 132 | }); 133 | node.children.push(child); 134 | }); 135 | return node; 136 | }); 137 | out.objects = objects; 138 | } 139 | // generate structure nodes 140 | { 141 | let structures = ast.filter(node => { 142 | return node.category === "structure"; 143 | }); 144 | structures = structures.map(structure => { 145 | let node = {}; 146 | let {textName, extensible} = structure; 147 | node.name = getDawnDeclarationName(textName); 148 | node.externalName = getExplortDeclarationName(node.name); 149 | node.type = getASTCategoryByName(textName, ast); 150 | if (extensible) node.isExtensible = true; 151 | node.textName = textName; 152 | node.children = []; 153 | structure.members.map(member => { 154 | let name = getCamelizedName(member.name); 155 | let type = getASTType(member, ast); 156 | let child = { 157 | name, 158 | type 159 | }; 160 | if (member.optional) child.isOptional = true; 161 | if (member.hasOwnProperty("default")) { 162 | let defaultValue = member.default; 163 | if (type.isEnum) { 164 | child.defaultValue = `"${member.default}"`; 165 | // find the native default value for this member 166 | let nativeDefaultValue = getASTNodeByName(member.type, ast).values.filter(({ name }) => { 167 | return name === member.default; 168 | }); 169 | if (!nativeDefaultValue.length) { 170 | return warn(`Cannot resolve default value for '${node.externalName}'.'${child.name}'`); 171 | } 172 | child.defaultValueNative = nativeDefaultValue[0].value; 173 | } 174 | else if (defaultValue === "true" || defaultValue === "false") { 175 | child.defaultValue = defaultValue === "true"; 176 | child.defaultValueNative = defaultValue; 177 | } 178 | else if (Number.isInteger(parseInt(defaultValue))) { 179 | child.defaultValue = String(defaultValue); 180 | child.defaultValueNative = defaultValue; 181 | } 182 | else if (typeof defaultValue === "string") { 183 | child.defaultValue = `"${member.default}"`; 184 | child.defaultValueNative = getASTNodeByName(member.type, ast).values.filter(({ name }) => { 185 | return name === member.default; 186 | })[0].value; 187 | } 188 | else { 189 | warn(`Unexpected default value for '${node.externalName}'.'${child.name}'`); 190 | } 191 | } 192 | node.children.push(child); 193 | }); 194 | return node; 195 | }); 196 | // find length members and mark them as "isInternalProperty" 197 | structures.map(structure => { 198 | let {children} = structure; 199 | children.map(child => { 200 | let {type} = child; 201 | if (type.hasOwnProperty("length") && !type.isDynamicLength) { 202 | let lengthMember = children.filter(child => { 203 | return child.name === type.length; 204 | })[0] || null; 205 | if (!lengthMember) { 206 | return warn(`Cannot resolve relative length member for '${structure.externalName}'.'${child.name}'`); 207 | } 208 | lengthMember.isInternalProperty = true; 209 | // if array length member is optional 210 | // then that means the relative array member is optional too 211 | if (lengthMember.type.isOptional) { 212 | type.isRequired = false; 213 | type.isOptional = true; 214 | } 215 | } 216 | }); 217 | }); 218 | out.structures = structures; 219 | } 220 | return out; 221 | }; 222 | -------------------------------------------------------------------------------- /generator/generators/gyp.mjs: -------------------------------------------------------------------------------- 1 | import fs from "fs"; 2 | import nunjucks from "nunjucks"; 3 | 4 | import pkg from "../../package.json"; 5 | 6 | import { 7 | warn 8 | } from "../utils.mjs"; 9 | 10 | let ast = null; 11 | 12 | const GYP_TEMPLATE = fs.readFileSync(`${pkg.config.TEMPLATE_DIR}/binding-gyp.njk`, "utf-8"); 13 | 14 | const DAWN_PATH = fs.readFileSync(pkg.config.DAWN_PATH, "utf-8"); 15 | 16 | nunjucks.configure({ autoescape: true }); 17 | 18 | export default function(astReference) { 19 | ast = astReference; 20 | let out = {}; 21 | let vars = { 22 | DAWN_PATH, 23 | SOURCE_INCLUDES: [ 24 | "src/*.cpp" 25 | ].map(v => `"${v}"`) 26 | }; 27 | // binding.gyp 28 | { 29 | let template = GYP_TEMPLATE; 30 | let output = nunjucks.renderString(template, vars); 31 | out.gyp = output; 32 | } 33 | return out; 34 | }; 35 | -------------------------------------------------------------------------------- /generator/generators/index.mjs: -------------------------------------------------------------------------------- 1 | import fs from "fs"; 2 | import nunjucks from "nunjucks"; 3 | 4 | import pkg from "../../package.json"; 5 | 6 | import { 7 | warn 8 | } from "../utils.mjs"; 9 | 10 | let ast = null; 11 | 12 | const H_TEMPLATE = fs.readFileSync(`${pkg.config.TEMPLATE_DIR}/index-h.njk`, "utf-8"); 13 | const CPP_TEMPLATE = fs.readFileSync(`${pkg.config.TEMPLATE_DIR}/index-cpp.njk`, "utf-8"); 14 | 15 | nunjucks.configure({ autoescape: true }); 16 | 17 | export default function(astReference, includeMemoryLayouts = false) { 18 | ast = astReference; 19 | let {bitmasks} = ast; 20 | let out = {}; 21 | let vars = { 22 | bitmasks, 23 | includeMemoryLayouts 24 | }; 25 | // h 26 | { 27 | let template = H_TEMPLATE; 28 | let output = nunjucks.renderString(template, vars); 29 | out.header = output; 30 | } 31 | // cpp 32 | { 33 | let template = CPP_TEMPLATE; 34 | let output = nunjucks.renderString(template, vars); 35 | out.source = output; 36 | } 37 | return out; 38 | }; 39 | -------------------------------------------------------------------------------- /generator/generators/memoryLayouts.mjs: -------------------------------------------------------------------------------- 1 | import fs from "fs"; 2 | import nunjucks from "nunjucks"; 3 | 4 | import pkg from "../../package.json"; 5 | 6 | import { 7 | warn 8 | } from "../utils.mjs"; 9 | 10 | let ast = null; 11 | 12 | const H_TEMPLATE = fs.readFileSync(`${pkg.config.TEMPLATE_DIR}/memoryLayouts-h.njk`, "utf-8"); 13 | 14 | nunjucks.configure({ autoescape: true }); 15 | 16 | export default function(astReference) { 17 | ast = astReference; 18 | let out = {}; 19 | let vars = { 20 | structures: ast.structures 21 | }; 22 | // h 23 | { 24 | let template = H_TEMPLATE; 25 | let output = nunjucks.renderString(template, vars); 26 | out.header = output; 27 | } 28 | return out; 29 | }; 30 | -------------------------------------------------------------------------------- /generator/index.mjs: -------------------------------------------------------------------------------- 1 | import fs from "fs"; 2 | import https from "https"; 3 | import nunjucks from "nunjucks"; 4 | 5 | import pkg from "../package.json"; 6 | 7 | import { 8 | warn, 9 | getPlatform, 10 | normalizeDawnPath 11 | } from "./utils.mjs"; 12 | 13 | import generateAST from "./generators/ast.mjs"; 14 | import generateGyp from "./generators/gyp.mjs"; 15 | import generateIndex from "./generators/index.mjs"; 16 | import generateMemoryLayouts from "./generators/memoryLayouts.mjs"; 17 | import generateDescriptorDecoder from "./generators/descriptorDecoder.mjs"; 18 | 19 | const DAWN_PATH = normalizeDawnPath(fs.readFileSync(pkg.config.DAWN_PATH, "utf-8")); 20 | 21 | const GEN_FILE_NOTICE = `/* 22 | * MACHINE GENERATED, DO NOT EDIT 23 | * GENERATED BY ${pkg.name} v${pkg.version} 24 | */ 25 | `; 26 | 27 | const dawnVersion = process.env.npm_config_dawnversion; 28 | if (!dawnVersion) throw `No Dawn version --dawnversion specified!`; 29 | 30 | // dst write paths 31 | const baseGeneratePath = pkg.config.GEN_OUT_DIR; 32 | const generateVersionPath = `${baseGeneratePath}/${dawnVersion}`; 33 | const generatePath = `${generateVersionPath}/${getPlatform()}`; 34 | const generateSrcPath = `${generatePath}/src`; 35 | const bypassBuild = !!process.env.npm_config_bypass_build; 36 | 37 | // enables js interface minifcation 38 | const enableMinification = false; 39 | 40 | // indicating if it's necessary to include memorylayouts in the build 41 | const includeMemoryLayouts = false /*!fs.existsSync(`${generatePath}/memoryLayouts.json`)*/; 42 | 43 | function writeGeneratedFile(path, text, includeNotice = true) { 44 | if (typeof text !== "string") throw new TypeError(`Expected 'string' type for parameter 'text'`); 45 | let source = null; 46 | try { 47 | source = fs.readFileSync(path, "utf-8"); 48 | } catch(e) { }; 49 | // append notice 50 | if (includeNotice) text = GEN_FILE_NOTICE + text; 51 | if (source !== text) { 52 | fs.writeFileSync(path, text, "utf-8"); 53 | } 54 | }; 55 | 56 | async function generateBindings(version, enableMinification, includeMemoryLayouts) { 57 | // copy dawn.json specification from dawn folder into into specification folder 58 | fs.copyFileSync( 59 | DAWN_PATH + "/dawn.json", 60 | pkg.config.SPEC_DIR + `/${version}.json` 61 | ); 62 | let JSONspecification = fs.readFileSync(pkg.config.SPEC_DIR + `/${version}.json`, "utf-8"); 63 | let fakePlatform = process.env.npm_config_fake_platform; 64 | // let the user know when he uses a fake platform 65 | if (fakePlatform) { 66 | console.log(`Fake platform enabled!`); 67 | console.log(`Fake platform: '${fakePlatform}' - Real platform: '${process.platform}'`); 68 | } 69 | if (!enableMinification) console.log(`Code minification is disabled!`); 70 | if (includeMemoryLayouts) console.log(`Memory layouts are not inlined yet.`); 71 | // reserve dst write paths 72 | { 73 | // generated/ 74 | if (!fs.existsSync(baseGeneratePath)) fs.mkdirSync(baseGeneratePath); 75 | // generated/version/ 76 | if (!fs.existsSync(generateVersionPath)) fs.mkdirSync(generateVersionPath); 77 | // generated/version/platform/ 78 | if (!fs.existsSync(generatePath)) fs.mkdirSync(generatePath); 79 | // generated/version/platform/src/ 80 | if (!fs.existsSync(generateSrcPath)) fs.mkdirSync(generateSrcPath); 81 | } 82 | console.log(`Generating bindings for ${version}...`); 83 | let ast = generateAST(JSON.parse(JSONspecification)); 84 | // generate AST 85 | { 86 | let out = JSON.stringify(ast, null, 2); 87 | // .json 88 | writeGeneratedFile(`${generatePath}/ast.json`, out, false); 89 | } 90 | // generate gyp 91 | { 92 | let out = generateGyp(ast); 93 | // .gyp 94 | writeGeneratedFile(`${generatePath}/binding.gyp`, out.gyp, false); 95 | } 96 | // generate index 97 | { 98 | let out = generateIndex(ast, includeMemoryLayouts); 99 | // .h 100 | writeGeneratedFile(`${generatePath}/src/index.h`, out.header); 101 | // .cpp 102 | writeGeneratedFile(`${generatePath}/src/index.cpp`, out.source); 103 | } 104 | // generate memorylayouts 105 | { 106 | let out = generateMemoryLayouts(ast); 107 | // .h 108 | writeGeneratedFile(`${generatePath}/src/memoryLayouts.h`, out.header); 109 | } 110 | // generate descriptor decoder 111 | { 112 | let out = generateDescriptorDecoder(ast); 113 | // .h 114 | writeGeneratedFile(`${generatePath}/src/DescriptorDecoder.h`, out.header); 115 | // .cpp 116 | writeGeneratedFile(`${generatePath}/src/DescriptorDecoder.cpp`, out.source); 117 | } 118 | console.log(`Successfully generated bindings!`); 119 | }; 120 | 121 | if (bypassBuild) { 122 | process.stderr.write(`Skipping generation..\n`); 123 | } else { 124 | generateBindings( 125 | dawnVersion, 126 | enableMinification, 127 | includeMemoryLayouts 128 | ); 129 | } 130 | -------------------------------------------------------------------------------- /generator/templates/DescriptorDecoder-cpp.njk: -------------------------------------------------------------------------------- 1 | #include "DescriptorDecoder.h" 2 | 3 | {% for enum in enums %} 4 | static std::unordered_map {{ enum.externalName }}Map = { 5 | {%- for member in enum.children %} 6 | { "{{ getEnumNameFromDawnEnumName(member.name) | safe }}", {{ member.value }} }, 7 | {%- endfor %} 8 | }; 9 | {% endfor %} 10 | 11 | namespace DescriptorDecoder { 12 | 13 | {% for enum in enums %} 14 | uint32_t {{ enum.externalName }}(std::string name) { 15 | return {{ enum.externalName }}Map[name]; 16 | }; 17 | std::string {{ enum.externalName }}(uint32_t value) { 18 | auto it = std::find_if( 19 | std::begin({{ enum.externalName }}Map), 20 | std::end({{ enum.externalName }}Map), 21 | [value](auto&& p) { 22 | return p.second == value; 23 | } 24 | ); 25 | 26 | if (it == std::end({{ enum.externalName }}Map)) return ""; 27 | 28 | return it->first; 29 | }; 30 | {% endfor %} 31 | 32 | {% for struct in structures %} 33 | void Destroy{{ struct.externalName }}({{ struct.name }} descriptor) { 34 | {%- for member in struct.children %} 35 | {{- getDestroyStructureMember(struct, member) | safe -}} 36 | {% endfor %} 37 | }; 38 | {% endfor %} 39 | 40 | {% for struct in structures %} 41 | {{ struct.name }} Decode{{ struct.externalName }}({{ getDecodeStructureParameters(struct, false) | safe }}) { 42 | {{ struct.name }} descriptor; 43 | // reset descriptor 44 | {{- getDescriptorInstanceReset(struct) | safe }} 45 | // fill descriptor 46 | Napi::Object obj = value.As(); 47 | {%- for member in struct.children %} 48 | {{- getDecodeStructureMember(struct, member, undefined, true) | safe -}} 49 | {% endfor %} 50 | return descriptor; 51 | }; 52 | {% endfor %} 53 | 54 | {% for struct in structures %} 55 | {{ struct.externalName }}::{{ struct.externalName }}({{ getDecodeStructureParameters(struct, false) | safe }}) { 56 | // reset descriptor 57 | {{- getDescriptorInstanceReset(struct) | safe }} 58 | // fill descriptor 59 | Napi::Object obj = value.As(); 60 | {%- for member in struct.children %} 61 | {{- getDecodeStructureMember(struct, member, undefined, false) | safe -}} 62 | {% endfor %} 63 | }; 64 | {{ struct.externalName }}::~{{ struct.externalName }}() { 65 | Destroy{{ struct.externalName }}(descriptor); 66 | }; 67 | {% endfor %} 68 | 69 | } 70 | -------------------------------------------------------------------------------- /generator/templates/DescriptorDecoder-h.njk: -------------------------------------------------------------------------------- 1 | #ifndef __DESCRIPTOR_DECODER_H__ 2 | #define __DESCRIPTOR_DECODER_H__ 3 | 4 | #include "GPUDevice.h" 5 | #include "GPUAdapter.h" 6 | #include "GPUQueue.h" 7 | #include "GPUFence.h" 8 | #include "GPUBuffer.h" 9 | #include "GPUTexture.h" 10 | #include "GPUTextureView.h" 11 | #include "GPUSampler.h" 12 | #include "GPUBindGroupLayout.h" 13 | #include "GPUPipelineLayout.h" 14 | #include "GPUBindGroup.h" 15 | #include "GPUShaderModule.h" 16 | #include "GPURenderPipeline.h" 17 | #include "GPURayTracingAccelerationContainer.h" 18 | #include "GPURayTracingShaderBindingTable.h" 19 | #include "GPURayTracingPipeline.h" 20 | 21 | #include 22 | 23 | namespace DescriptorDecoder { 24 | {% for enum in enums %} 25 | uint32_t {{ enum.externalName }}(std::string name); 26 | std::string {{ enum.externalName }}(uint32_t value); 27 | {% endfor %} 28 | 29 | {% for struct in structures %} 30 | {{ struct.name }} Decode{{ struct.externalName }}({{ getDecodeStructureParameters(struct, true) | safe }}); 31 | {% endfor %} 32 | 33 | {% for struct in structures %} 34 | class {{ struct.externalName }} { 35 | public: 36 | {{ struct.externalName }}({{ getDecodeStructureParameters(struct, true) | safe }}); 37 | ~{{ struct.externalName }}(); 38 | {{ struct.name }}* operator &() { return &descriptor; }; 39 | private: 40 | {{ struct.name }} descriptor; 41 | }; 42 | {% endfor %} 43 | 44 | {% for struct in structures %} 45 | void Destroy{{ struct.externalName }}({{ struct.name }} descriptor); 46 | {% endfor %} 47 | } 48 | 49 | #endif 50 | -------------------------------------------------------------------------------- /generator/templates/binding-gyp.njk: -------------------------------------------------------------------------------- 1 | { 2 | "variables": { 3 | "root": "../../..", 4 | "platform": "<(OS)", 5 | "build": "<@(module_root_dir)/build", 6 | "release": "<(build)/Release", 7 | "dawn": "{{ DAWN_PATH | safe }}", 8 | }, 9 | "conditions": [ 10 | [ "platform == 'win'", { "variables": { "platform": "win" } } ], 11 | [ "platform == 'linux'", { "variables": { 12 | "platform": "linux", 13 | "rel_release": " 35 | 36 | static const wchar_t kTagForGetModuleHandleEx[] = L"kernel32.dll"; 37 | std::string GetModulePath() { 38 | HMODULE moduleHandle; 39 | uint32_t flags = ( 40 | GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | 41 | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT 42 | ); 43 | if (GetModuleHandleEx(flags, kTagForGetModuleHandleEx, &moduleHandle) == 0) { 44 | return ""; 45 | } 46 | char lpFilename[MAX_PATH]; 47 | if (GetModuleFileNameA(moduleHandle, lpFilename, sizeof(lpFilename)) == 0) { 48 | return ""; 49 | } 50 | std::string moduleDirPath = std::string(lpFilename); 51 | size_t lastPathSepLoc = moduleDirPath.find_last_of("\\"); 52 | return lastPathSepLoc != std::string::npos ? moduleDirPath.substr(0, lastPathSepLoc + 1) : ""; 53 | } 54 | #endif 55 | 56 | Napi::Object Init(Napi::Env env, Napi::Object exports) { 57 | 58 | GPU::Initialize(env, exports); 59 | GPUAdapter::Initialize(env, exports); 60 | GPUDevice::Initialize(env, exports); 61 | GPUQueue::Initialize(env, exports); 62 | GPUFence::Initialize(env, exports); 63 | GPUBuffer::Initialize(env, exports); 64 | GPUTexture::Initialize(env, exports); 65 | GPUTextureView::Initialize(env, exports); 66 | GPUSampler::Initialize(env, exports); 67 | GPUBindGroupLayout::Initialize(env, exports); 68 | GPUPipelineLayout::Initialize(env, exports); 69 | GPUBindGroup::Initialize(env, exports); 70 | GPUShaderModule::Initialize(env, exports); 71 | GPURenderPipeline::Initialize(env, exports); 72 | GPUComputePipeline::Initialize(env, exports); 73 | GPUCanvasContext::Initialize(env, exports); 74 | GPUSwapChain::Initialize(env, exports); 75 | GPUCommandBuffer::Initialize(env, exports); 76 | GPUCommandEncoder::Initialize(env, exports); 77 | GPURenderPassEncoder::Initialize(env, exports); 78 | GPUComputePassEncoder::Initialize(env, exports); 79 | GPURenderBundle::Initialize(env, exports); 80 | GPURenderBundleEncoder::Initialize(env, exports); 81 | GPURayTracingAccelerationContainer::Initialize(env, exports); 82 | GPURayTracingShaderBindingTable::Initialize(env, exports); 83 | GPURayTracingPipeline::Initialize(env, exports); 84 | GPURayTracingPassEncoder::Initialize(env, exports); 85 | 86 | WebGPUWindow::Initialize(env, exports); 87 | 88 | {% for bitmask in bitmasks %} 89 | Napi::Object {{ bitmask.externalName }} = Napi::Object::New(env); 90 | {%- for member in bitmask.children %} 91 | {{ bitmask.externalName }}.Set( 92 | Napi::String::New(env, "{{ member.name }}"), 93 | Napi::Number::New(env, {{ member.value }}) 94 | ); 95 | {%- endfor %} 96 | exports["{{ bitmask.externalName }}"] = {{ bitmask.externalName }}; 97 | {% endfor %} 98 | 99 | #ifdef _WIN32 100 | std::string modulePath = GetModulePath(); 101 | std::string dxilPath = modulePath + "dxil.dll"; 102 | if (LoadLibraryA(dxilPath.c_str()) == nullptr) { 103 | printf("Failed to load DXIL\n"); 104 | } 105 | std::string dxcPath = modulePath + "dxcompiler.dll"; 106 | if (LoadLibraryA(dxcPath.c_str()) == nullptr) { 107 | printf("Failed to load DXC\n"); 108 | } 109 | #endif 110 | 111 | return exports; 112 | } 113 | 114 | NODE_API_MODULE(addon, Init) 115 | -------------------------------------------------------------------------------- /generator/templates/index-h.njk: -------------------------------------------------------------------------------- 1 | #ifndef __BASE__ 2 | #define __BASE__ 3 | 4 | #define NAPI_EXPERIMENTAL 5 | #include 6 | 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /generator/templates/memoryLayouts-h.njk: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | Napi::Value MemoryLayouts(const Napi::CallbackInfo& info) { 7 | Napi::Env env = info.Env(); 8 | Napi::Object out = Napi::Object::New(env); 9 | 10 | Napi::String strByteOffset = Napi::String::New(env, "byteOffset"); 11 | Napi::String strByteLength = Napi::String::New(env, "byteLength"); 12 | 13 | {% for struct in structures -%} 14 | Napi::Object s{{ struct.name }} = Napi::Object::New(env); 15 | {% for child in struct.children -%} 16 | { 17 | Napi::Object obj = Napi::Object::New(env); 18 | obj.Set(strByteOffset, Napi::Number::New(env, offsetof({{ struct.name }}, {{ child.name }}))); 19 | obj.Set(strByteLength, Napi::Number::New(env, sizeof({{ struct.name }}::{{ child.name }}))); 20 | s{{ struct.name }}.Set(Napi::String::New(env, "{{ child.name }}"), obj); 21 | } 22 | {% endfor -%} 23 | s{{ struct.name }}.Set(strByteLength, Napi::Number::New(env, sizeof({{ struct.name }}))); 24 | out.Set(Napi::String::New(env, "{{ struct.name }}"), s{{ struct.name }}); 25 | {% endfor %} 26 | 27 | return out; 28 | } 29 | -------------------------------------------------------------------------------- /generator/types.mjs: -------------------------------------------------------------------------------- 1 | import { 2 | warn, 3 | error, 4 | getCamelizedName, 5 | getSnakeCaseName 6 | } from "./utils.mjs"; 7 | 8 | export function getDawnDeclarationName(name) { 9 | let camelized = getCamelizedName(name); 10 | let structName = "WGPU" + (camelized[0].toUpperCase()) + camelized.substr(1); 11 | return structName; 12 | }; 13 | 14 | export function getExplortDeclarationName(name) { 15 | if (name.substr(0, 4) !== "WGPU") warn(`Expected name [0-4] to be 'WGPU'`); 16 | return "GPU" + name.substr(4); 17 | }; 18 | 19 | export function getASTNodeByName(name, ast) { 20 | for (let ii = 0; ii < ast.length; ++ii) { 21 | let node = ast[ii]; 22 | if (node.textName === name) return node; 23 | }; 24 | warn(`Cannot resolve AST node for '${name}'`); 25 | return null; 26 | }; 27 | 28 | export function getASTCategoryByName(name, ast) { 29 | let out = getASTNodeByName(name, ast); 30 | if (!out) warn(`Cannot resolve AST category for '${name}'`); 31 | return out.category; 32 | }; 33 | 34 | export function getASTJavaScriptType(type, member, ast) { 35 | let out = {}; 36 | switch (member.type) { 37 | // numbers 38 | case "float": 39 | case "int32_t": 40 | case "uint32_t": { 41 | if (type.isArray) { 42 | let arrayName = member.type.replace("_t", ""); 43 | arrayName = arrayName[0].toUpperCase() + arrayName.substr(1); 44 | out.type = arrayName + "Array"; 45 | out.isTypedArray = true; 46 | } else { 47 | out.type = "Number"; 48 | out.isNumber = true; 49 | } 50 | } break; 51 | case "int64_t": 52 | case "uint64_t": { 53 | if (type.isArray) { 54 | let arrayName = member.type.replace("_t", ""); 55 | arrayName = arrayName[0].toUpperCase() + arrayName.substr(1); 56 | out.type = "Big" + arrayName + "Array"; 57 | out.isTypedArray = true; 58 | } else { 59 | out.type = "Number"; 60 | out.isNumber = true; 61 | } 62 | } break; 63 | case "bool": { 64 | out.type = "Boolean"; 65 | out.isBoolean = true; 66 | } break; 67 | case "char": { 68 | out.type = "String"; 69 | out.isString = true; 70 | } break; 71 | case "void": { 72 | if (type.isReference) { 73 | out.type = "ArrayBuffer"; 74 | out.isArrayBuffer = true; 75 | } else { 76 | out.type = "Undefined"; 77 | out.isUndefined = true; 78 | } 79 | } break; 80 | default: 81 | if (type.isEnum) { 82 | out.type = "String"; 83 | out.isString = true; 84 | } 85 | else if (type.isBitmask) { 86 | out.type = "Number"; 87 | out.isNumber = true; 88 | } 89 | else if (type.isObject) { 90 | out.type = type.nativeType; 91 | out.isObject = true; 92 | } 93 | else if (type.isStructure) { 94 | out.type = "Object"; 95 | out.isObject = true; 96 | } 97 | else if (type.isFunction) { 98 | out.type = "Function"; 99 | out.isFunction = true; 100 | } 101 | else { 102 | warn(`Unexpected member type '${member.type}'`); 103 | } 104 | break; 105 | }; 106 | return out; 107 | }; 108 | 109 | export function getASTType(member, ast) { 110 | let out = {}; 111 | let type = member.type; 112 | switch (member.type) { 113 | // numbers 114 | case "float": 115 | case "int32_t": 116 | case "uint32_t": 117 | case "uint64_t": { 118 | let initialValue = member.default + ""; 119 | if (!member.hasOwnProperty("default")) { 120 | initialValue = "0"; 121 | } 122 | out.isNumber = true; 123 | out.initialValue = initialValue; 124 | } break; 125 | case "bool": { 126 | let initialValue = member.default + ""; 127 | if (!member.hasOwnProperty("default")) { 128 | initialValue = "false"; 129 | } 130 | out.isBoolean = true; 131 | out.initialValue = initialValue; 132 | } break; 133 | case "char": { 134 | if (member.annotation !== "const*") { 135 | warn(`Expected 'annotation' property to be 'const*' for 'char' type`); 136 | } 137 | if (!member.hasOwnProperty("length")) { 138 | warn(`Expected 'length' property to be set for 'char' type`); 139 | } 140 | out.isString = true; 141 | if (member.length === "strlen") out.isDynamicLength = true; 142 | } break; 143 | case "void": { 144 | // TODO 145 | } break; 146 | case "error callback": 147 | case "buffer map read callback": 148 | case "buffer map write callback": 149 | case "buffer create mapped callback": 150 | case "fence on completion callback": { 151 | out.isFunction = true; 152 | } break; 153 | // arbitrary 154 | default: 155 | let node = getASTNodeByName(member.type, ast); 156 | let memberType = getDawnDeclarationName(node.textName); 157 | // validate category 158 | { 159 | switch (node.category) { 160 | case "enum": 161 | case "bitmask": 162 | case "object": 163 | case "structure": 164 | case "natively defined": break; 165 | default: { 166 | warn(`Unexpected node category '${node.category}'`); 167 | } break; 168 | }; 169 | } 170 | // maps to isEnum, isBitmask etc. 171 | let key = "is" + node.category[0].toUpperCase() + node.category.substr(1); 172 | // overwrite default type 173 | type = memberType; 174 | out[key] = true; 175 | out.nativeType = memberType; 176 | break; 177 | }; 178 | // validate annotation 179 | if (member.annotation) { 180 | switch (member.annotation) { 181 | case "*": 182 | case "const*": 183 | case "const*const*": break; 184 | default: { 185 | warn(`Unexpected annotation member '${member.annotation}'`); 186 | } break; 187 | }; 188 | } 189 | if (member.length) { 190 | if (!Number.isInteger(parseInt(member.length))) { 191 | out.length = getCamelizedName(member.length); 192 | } 193 | else { 194 | warn(`Expected member-based 'length' but got '${member.length}'`); 195 | } 196 | } 197 | // if the type is generally a reference 198 | { 199 | if (member.annotation === "*" || member.annotation === "const*") { 200 | out.isReference = true; 201 | if (member.hasOwnProperty("length")) { 202 | delete out.isNumber; 203 | delete out.initialValue; 204 | out.isArray = true; 205 | } 206 | } 207 | else if (member.annotation === "const*const*") { 208 | out.isArray = true; 209 | out.isArrayOfPointers = true; 210 | out.isReference = true; 211 | } 212 | } 213 | if (out.isReference) { 214 | switch (member.annotation) { 215 | case "*": out.rawType = `${type}*`; break; 216 | case "const*": out.rawType = `const ${type}*`; break; 217 | case "const*const*": out.rawType = `const ${type}* const *`; break; 218 | }; 219 | } else { 220 | out.rawType = type; 221 | } 222 | if (member.hasOwnProperty("default") || member.optional) out.isOptional = true; 223 | else out.isRequired = true; 224 | // append javascript relative type 225 | out.jsType = getASTJavaScriptType(out, member, ast); 226 | return out; 227 | }; 228 | -------------------------------------------------------------------------------- /generator/utils.mjs: -------------------------------------------------------------------------------- 1 | import fs from "fs"; 2 | import path from "path"; 3 | 4 | export function warn() { 5 | let args = []; 6 | for (let ii = 0; ii < arguments.length; ++ii) args.push(arguments[ii]); 7 | let str = args.join(", "); 8 | console.log(`\x1b[33m%s\x1b[0m`, `Warning: ${str}`); 9 | }; 10 | 11 | export function error() { 12 | let args = []; 13 | for (let ii = 0; ii < arguments.length; ++ii) args.push(arguments[ii]); 14 | let str = args.join(", "); 15 | process.stderr.write(`\x1b[31mError: ${str}\n\x1b[0m`); 16 | }; 17 | 18 | export function getPlatform() { 19 | let fakePlatform = process.env.npm_config_fake_platform; 20 | if (fakePlatform) { 21 | switch (fakePlatform) { 22 | case "win32": 23 | case "linux": 24 | case "darwin": 25 | break; 26 | default: 27 | throw new Error(`Invalid fake platform! Aborting..`); 28 | }; 29 | return fakePlatform; 30 | } 31 | return process.platform; 32 | }; 33 | 34 | export function getCamelizedName(name) { 35 | return ( 36 | name 37 | .replace(/\s(.)/g, _ => _.toUpperCase()) 38 | .replace(/\s/g, '') 39 | .replace(/^(.)/, _ => _.toLowerCase()) 40 | ); 41 | }; 42 | 43 | export function getSnakeCaseName(name) { 44 | return ( 45 | name 46 | .replace(/\s/g, "_") 47 | .toUpperCase() 48 | ); 49 | }; 50 | 51 | // "R16 float" -> "r16float" 52 | // "depth24 plus stencil8" -> "depth24plus-stencil8" 53 | export function getEnumNameFromDawnEnumName(name) { 54 | let chunks = name.split(/\s/g); 55 | chunks = chunks.map((v, i) => { 56 | if (i < chunks.length - 1) { 57 | if (v === "1D" || v === "2D" || v === "3D") return v + "-"; 58 | else if (v.match(/^(?=.*[a-zA-Z])(?=.*[0-9])[a-zA-Z0-9]+$/gm)) return v; 59 | else return v + "-"; 60 | } 61 | return v; 62 | }); 63 | chunks = chunks.map(v => v.toLowerCase()); 64 | let out = chunks.join(""); 65 | return out; 66 | }; 67 | 68 | export function getSnakeCaseFromCamelCaseName(name) { 69 | return name.split(/(?=[A-Z])/).join('_').toUpperCase(); 70 | }; 71 | 72 | export function firstLetterToUpperCase(str) { 73 | return str[0].toUpperCase() + str.substr(1); 74 | }; 75 | 76 | export function isQuotedString(str) { 77 | return !!((String(str)).match(/"[^"]*"/g)); 78 | }; 79 | 80 | export function normalizeDawnPath(p) { 81 | p = p.replace(/\s+/g, ""); 82 | if (!path.isAbsolute(p)) { 83 | throw new Error(`PATH_TO_DAWN must be an absolute path`); 84 | } 85 | return p; 86 | }; 87 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const path = require("path"); 3 | 4 | const pkg = require("./package.json"); 5 | 6 | let {platform} = process; 7 | 8 | const dawnVersion = "0.0.1"; 9 | 10 | const bindingsPath = path.join(__dirname, `${pkg.config.GEN_OUT_DIR}/`); 11 | const generatedPath = bindingsPath + `${dawnVersion}/${platform}`; 12 | 13 | module.exports = require(`${generatedPath}/build/Release/addon-${platform}.node`); 14 | 15 | // let the module know which platform we're running on 16 | module.exports.GPU.$setPlatform(process.platform); 17 | 18 | // the creates an auto tick loop for each device 19 | { 20 | let devices = []; 21 | process.nextTick(() => { 22 | for (let ii = 0; ii < devices.length; ++ii) { 23 | /*if (!device.isDestroyed) */ 24 | devices[ii].tick(); 25 | }; 26 | }); 27 | const {GPUAdapter} = module.exports; 28 | GPUAdapter.prototype.requestDevice = function() { 29 | let args = arguments; 30 | return new Promise((resolve, reject) => { 31 | this._requestDevice(...args).then(device => { 32 | device._onErrorCallback = function(type, msg) { 33 | setImmediate(() => { 34 | switch (type) { 35 | case "Error": throw new Error(msg); break; 36 | case "Type": throw new TypeError(msg); break; 37 | case "Range": throw new RangeError(msg); break; 38 | case "Reference": throw new ReferenceError(msg); break; 39 | case "Internal": throw new InternalError(msg); break; 40 | case "Syntax": throw new SyntaxError(msg); break; 41 | default: throw new Error(msg); break; 42 | }; 43 | }); 44 | }; 45 | devices.push(device); 46 | resolve(device); 47 | }); 48 | }); 49 | }; 50 | } 51 | 52 | // temporary hack to return a promise instead of a callback 53 | { 54 | const {GPUFence} = module.exports; 55 | GPUFence.prototype.onCompletion = function(completionValue) { 56 | return new Promise(resolve => { 57 | setImmediate(() => { 58 | this._onCompletion(completionValue, resolve); 59 | }); 60 | }); 61 | }; 62 | } 63 | { 64 | const {GPUBuffer} = module.exports; 65 | GPUBuffer.prototype.mapReadAsync = function() { 66 | return new Promise(resolve => { 67 | setImmediate(() => { 68 | this._mapReadAsync(resolve); 69 | }); 70 | }); 71 | }; 72 | } 73 | { 74 | const {GPUBuffer} = module.exports; 75 | GPUBuffer.prototype.mapWriteAsync = function() { 76 | return new Promise(resolve => { 77 | setImmediate(() => { 78 | this._mapWriteAsync(resolve); 79 | }); 80 | }); 81 | }; 82 | } 83 | { 84 | const {GPUDevice} = module.exports; 85 | GPUDevice.prototype.createBufferMappedAsync = function(descriptor) { 86 | return new Promise(resolve => { 87 | setImmediate(() => { 88 | this._createBufferMappedAsync(descriptor, resolve); 89 | }); 90 | }); 91 | }; 92 | } 93 | -------------------------------------------------------------------------------- /lib/darwin/x64/GLFW/libglfw3.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maierfelix/webgpu/94beacb7163b83641310f872bef9499505bb35e4/lib/darwin/x64/GLFW/libglfw3.a -------------------------------------------------------------------------------- /lib/linux/x64/GLFW/libglfw3.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maierfelix/webgpu/94beacb7163b83641310f872bef9499505bb35e4/lib/linux/x64/GLFW/libglfw3.a -------------------------------------------------------------------------------- /lib/win/x64/DXC/dxcompiler.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maierfelix/webgpu/94beacb7163b83641310f872bef9499505bb35e4/lib/win/x64/DXC/dxcompiler.dll -------------------------------------------------------------------------------- /lib/win/x64/DXC/dxil.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maierfelix/webgpu/94beacb7163b83641310f872bef9499505bb35e4/lib/win/x64/DXC/dxil.dll -------------------------------------------------------------------------------- /lib/win/x64/GLFW/glfw3.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maierfelix/webgpu/94beacb7163b83641310f872bef9499505bb35e4/lib/win/x64/GLFW/glfw3.dll -------------------------------------------------------------------------------- /lib/win/x64/GLFW/glfw3.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maierfelix/webgpu/94beacb7163b83641310f872bef9499505bb35e4/lib/win/x64/GLFW/glfw3.lib -------------------------------------------------------------------------------- /lib/win/x64/GLFW/glfw3dll.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maierfelix/webgpu/94beacb7163b83641310f872bef9499505bb35e4/lib/win/x64/GLFW/glfw3dll.lib -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "webgpu", 3 | "main": "index.js", 4 | "version": "0.1.16", 5 | "engines": { 6 | "node": ">= 13.0.0" 7 | }, 8 | "config": { 9 | "DAWN_PATH": "PATH_TO_DAWN", 10 | "GEN_OUT_DIR": "./generated", 11 | "SPEC_DIR": "./generator/specifications", 12 | "TEMPLATE_DIR": "./generator/templates" 13 | }, 14 | "scripts": { 15 | "build": "node ./build.js", 16 | "generate": "node --experimental-modules --experimental-json-modules ./generator/index.mjs", 17 | "all": "npm run generate && npm run build", 18 | "tests": "node --experimental-modules tests/index.mjs" 19 | }, 20 | "devDependencies": { 21 | "ncp": "^2.0.0", 22 | "node-addon-api": "^1.7.1", 23 | "nunjucks": "^3.2.0" 24 | }, 25 | "dependencies": {}, 26 | "description": "WebGPU for Node [WIP]", 27 | "repository": { 28 | "type": "git", 29 | "url": "git+https://github.com/maierfelix/webgpu.git" 30 | }, 31 | "keywords": [ 32 | "webgpu", 33 | "vulkan", 34 | "dawn", 35 | "gpu", 36 | "gpgpu" 37 | ], 38 | "author": "Felix Maier", 39 | "license": "MIT", 40 | "bugs": { 41 | "url": "https://github.com/maierfelix/webgpu/issues" 42 | }, 43 | "homepage": "https://github.com/maierfelix/webgpu#readme", 44 | "directories": { 45 | "example": "examples", 46 | "lib": "lib", 47 | "test": "tests" 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/BackendBinding.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Dawn Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "BackendBinding.h" 16 | 17 | #include "GLFW/glfw3.h" 18 | 19 | #if defined(DAWN_ENABLE_BACKEND_OPENGL) 20 | # include "dawn_native/OpenGLBackend.h" 21 | #endif // defined(DAWN_ENABLE_BACKEND_OPENGL) 22 | 23 | #if defined(DAWN_ENABLE_BACKEND_D3D12) 24 | BackendBinding* CreateD3D12Binding(GLFWwindow* window, WGPUDevice device); 25 | #endif 26 | #if defined(DAWN_ENABLE_BACKEND_METAL) 27 | BackendBinding* CreateMetalBinding(GLFWwindow* window, WGPUDevice device); 28 | #endif 29 | #if defined(DAWN_ENABLE_BACKEND_NULL) 30 | BackendBinding* CreateNullBinding(GLFWwindow* window, WGPUDevice device); 31 | #endif 32 | #if defined(DAWN_ENABLE_BACKEND_OPENGL) 33 | BackendBinding* CreateOpenGLBinding(GLFWwindow* window, WGPUDevice device); 34 | #endif 35 | #if defined(DAWN_ENABLE_BACKEND_VULKAN) 36 | BackendBinding* CreateVulkanBinding(GLFWwindow* window, WGPUDevice device); 37 | #endif 38 | 39 | BackendBinding::BackendBinding(GLFWwindow* window, WGPUDevice device) 40 | : mWindow(window), mDevice(device) { 41 | } 42 | 43 | void SetupGLFWWindowHintsForBackend(dawn_native::BackendType type) { 44 | if (type == dawn_native::BackendType::OpenGL) { 45 | glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); 46 | glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 4); 47 | glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE); 48 | glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); 49 | } else { 50 | glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); 51 | } 52 | } 53 | 54 | void DiscoverAdapter(dawn_native::Instance* instance, 55 | GLFWwindow* window, 56 | dawn_native::BackendType type) { 57 | //DAWN_UNUSED(type); 58 | //DAWN_UNUSED(window); 59 | 60 | if (type == dawn_native::BackendType::OpenGL) { 61 | #if defined(DAWN_ENABLE_BACKEND_OPENGL) 62 | glfwMakeContextCurrent(window); 63 | dawn_native::opengl::AdapterDiscoveryOptions adapterOptions; 64 | adapterOptions.getProc = reinterpret_cast(glfwGetProcAddress); 65 | instance->DiscoverAdapters(&adapterOptions); 66 | #endif // defined(DAWN_ENABLE_BACKEND_OPENGL) 67 | } else { 68 | instance->DiscoverDefaultAdapters(); 69 | } 70 | } 71 | 72 | BackendBinding* CreateBinding(dawn_native::BackendType type, 73 | GLFWwindow* window, 74 | WGPUDevice device) { 75 | switch (type) { 76 | #if defined(DAWN_ENABLE_BACKEND_D3D12) 77 | case dawn_native::BackendType::D3D12: 78 | return CreateD3D12Binding(window, device); 79 | #endif 80 | 81 | #if defined(DAWN_ENABLE_BACKEND_METAL) 82 | case dawn_native::BackendType::Metal: 83 | return CreateMetalBinding(window, device); 84 | #endif 85 | 86 | #if defined(DAWN_ENABLE_BACKEND_NULL) 87 | case dawn_native::BackendType::Null: 88 | return CreateNullBinding(window, device); 89 | #endif 90 | 91 | #if defined(DAWN_ENABLE_BACKEND_OPENGL) 92 | case dawn_native::BackendType::OpenGL: 93 | return CreateOpenGLBinding(window, device); 94 | #endif 95 | 96 | #if defined(DAWN_ENABLE_BACKEND_VULKAN) 97 | case dawn_native::BackendType::Vulkan: 98 | return CreateVulkanBinding(window, device); 99 | #endif 100 | 101 | default: 102 | return nullptr; 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/BackendBinding.h: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Dawn Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef UTILS_BACKENDBINDING_H_ 16 | #define UTILS_BACKENDBINDING_H_ 17 | 18 | #include "dawn/webgpu.h" 19 | #include "dawn_native/DawnNative.h" 20 | 21 | struct GLFWwindow; 22 | 23 | class BackendBinding { 24 | public: 25 | virtual ~BackendBinding() = default; 26 | 27 | virtual uint64_t GetSwapChainImplementation() = 0; 28 | virtual WGPUTextureFormat GetPreferredSwapChainTextureFormat() = 0; 29 | 30 | protected: 31 | BackendBinding(GLFWwindow* window, WGPUDevice device); 32 | 33 | GLFWwindow* mWindow = nullptr; 34 | WGPUDevice mDevice = nullptr; 35 | }; 36 | 37 | void SetupGLFWWindowHintsForBackend(dawn_native::BackendType type); 38 | void DiscoverAdapter(dawn_native::Instance* instance, 39 | GLFWwindow* window, 40 | dawn_native::BackendType type); 41 | BackendBinding* CreateBinding(dawn_native::BackendType type, 42 | GLFWwindow* window, 43 | WGPUDevice device); 44 | 45 | 46 | #endif // UTILS_BACKENDBINDING_H_ 47 | -------------------------------------------------------------------------------- /src/Base.h: -------------------------------------------------------------------------------- 1 | #define NAPI_EXPERIMENTAL 2 | #include 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | 14 | #include "Utils.h" 15 | -------------------------------------------------------------------------------- /src/D3D12Binding.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Dawn Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "BackendBinding.h" 16 | 17 | #include "dawn_native/D3D12Backend.h" 18 | 19 | #include "GLFW/glfw3.h" 20 | #define GLFW_EXPOSE_NATIVE_WIN32 21 | #include "GLFW/glfw3native.h" 22 | 23 | #include 24 | 25 | class D3D12Binding : public BackendBinding { 26 | public: 27 | D3D12Binding(GLFWwindow* window, WGPUDevice device) : BackendBinding(window, device) { 28 | } 29 | 30 | uint64_t GetSwapChainImplementation() override { 31 | if (mSwapchainImpl.userData == nullptr) { 32 | HWND win32Window = glfwGetWin32Window(mWindow); 33 | mSwapchainImpl = 34 | dawn_native::d3d12::CreateNativeSwapChainImpl(mDevice, win32Window); 35 | } 36 | return reinterpret_cast(&mSwapchainImpl); 37 | } 38 | 39 | WGPUTextureFormat GetPreferredSwapChainTextureFormat() override { 40 | //ASSERT(mSwapchainImpl.userData != nullptr); 41 | return dawn_native::d3d12::GetNativeSwapChainPreferredFormat(&mSwapchainImpl); 42 | } 43 | 44 | private: 45 | DawnSwapChainImplementation mSwapchainImpl = {}; 46 | }; 47 | 48 | BackendBinding* CreateD3D12Binding(GLFWwindow* window, WGPUDevice device) { 49 | return new D3D12Binding(window, device); 50 | } 51 | -------------------------------------------------------------------------------- /src/GPU.cpp: -------------------------------------------------------------------------------- 1 | #include "GPU.h" 2 | #include "GPUAdapter.h" 3 | 4 | std::string platform = ""; 5 | 6 | Napi::FunctionReference GPU::constructor; 7 | 8 | GPU::GPU(const Napi::CallbackInfo& info) : Napi::ObjectWrap(info) { } 9 | GPU::~GPU() { } 10 | 11 | Napi::Value GPU::requestAdapter(const Napi::CallbackInfo &info) { 12 | Napi::Env env = info.Env(); 13 | auto deferred = Napi::Promise::Deferred::New(env); 14 | 15 | std::vector args = {}; 16 | if (info[0].IsObject()) args.push_back(info[0].As()); 17 | else args.push_back(env.Undefined()); 18 | args.push_back(Napi::String::New(env, platform)); 19 | 20 | deferred.Resolve(GPUAdapter::constructor.New(args)); 21 | 22 | return deferred.Promise(); 23 | } 24 | 25 | Napi::Value SetPlatform(const Napi::CallbackInfo &info) { 26 | Napi::Env env = info.Env(); 27 | platform = info[0].ToString().Utf8Value(); 28 | return env.Undefined(); 29 | } 30 | 31 | Napi::Object GPU::Initialize(Napi::Env env, Napi::Object exports) { 32 | Napi::HandleScope scope(env); 33 | Napi::Function func = DefineClass(env, "GPU", { 34 | StaticMethod( 35 | "requestAdapter", 36 | &GPU::requestAdapter, 37 | napi_enumerable 38 | ), 39 | StaticMethod( 40 | "$setPlatform", 41 | &SetPlatform 42 | ) 43 | }); 44 | constructor = Napi::Persistent(func); 45 | constructor.SuppressDestruct(); 46 | exports.Set("GPU", func); 47 | return exports; 48 | } 49 | -------------------------------------------------------------------------------- /src/GPU.h: -------------------------------------------------------------------------------- 1 | #ifndef __GPU_H__ 2 | #define __GPU_H__ 3 | 4 | #include "Base.h" 5 | 6 | class GPU : public Napi::ObjectWrap { 7 | 8 | public: 9 | 10 | static Napi::Object Initialize(Napi::Env env, Napi::Object exports); 11 | static Napi::FunctionReference constructor; 12 | 13 | static Napi::Value requestAdapter(const Napi::CallbackInfo &info); 14 | 15 | GPU(const Napi::CallbackInfo &info); 16 | ~GPU(); 17 | 18 | }; 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /src/GPUAdapter.cpp: -------------------------------------------------------------------------------- 1 | #include "GPUAdapter.h" 2 | #include "WebGPUWindow.h" 3 | 4 | Napi::FunctionReference GPUAdapter::constructor; 5 | 6 | GPUAdapter::GPUAdapter(const Napi::CallbackInfo& info) : Napi::ObjectWrap(info) { 7 | Napi::Env env = info.Env(); 8 | 9 | if (info[0].IsObject()) { 10 | // ignore powerPreference 11 | Napi::Object obj = info[0].As(); 12 | if (!obj.Has("window")) { 13 | Napi::Error::New(env, "Expected 'WebGPUWindow' in 'GPURequestAdapterOptions.window'").ThrowAsJavaScriptException(); 14 | return; 15 | } 16 | this->window.Reset(obj.Get("window").As(), 1); 17 | } else { 18 | Napi::Error::New(env, "Expected 'Object' for argument 1 in 'requestAdapter'").ThrowAsJavaScriptException(); 19 | return; 20 | } 21 | 22 | // we expect a string containing the process platform here 23 | if (!info[1].IsString()) { 24 | Napi::Error::New(env, "Invalid Function Signature").ThrowAsJavaScriptException(); 25 | return; 26 | } 27 | 28 | this->nativeInstance = std::make_unique(); 29 | 30 | //this->nativeInstance->EnableBackendValidation(true); 31 | //this->nativeInstance->EnableBeginCaptureOnStartup(true); 32 | 33 | this->nativeInstance->DiscoverDefaultAdapters(); 34 | 35 | this->instance = this->createAdapter(info); 36 | } 37 | 38 | GPUAdapter::~GPUAdapter() { 39 | this->window.Reset(); 40 | this->nativeInstance.reset(); 41 | this->instance = nullptr; 42 | this->nativeInstance = nullptr; 43 | } 44 | 45 | Napi::Value GPUAdapter::requestDevice(const Napi::CallbackInfo &info) { 46 | Napi::Env env = info.Env(); 47 | 48 | Napi::Promise::Deferred deferred = Napi::Promise::Deferred::New(env); 49 | 50 | std::vector args = { 51 | info.This().As() 52 | }; 53 | if (info[0].IsObject()) args.push_back(info[0].As()); 54 | 55 | Napi::Object device = GPUDevice::constructor.New(args); 56 | deferred.Resolve(device); 57 | 58 | return deferred.Promise(); 59 | } 60 | 61 | dawn_native::Adapter GPUAdapter::createAdapter(const Napi::CallbackInfo& info) { 62 | Napi::Env env = info.Env(); 63 | std::vector adapters = this->nativeInstance->GetAdapters(); 64 | std::string platform = info[1].ToString().Utf8Value(); 65 | Napi::Object obj = info[0].As(); 66 | // try to use the preferred backend 67 | if (obj.Has("preferredBackend")) { 68 | // validate 69 | if (!obj.Get("preferredBackend").IsString()) { 70 | Napi::Error::New(env, "Expected 'String' for 'preferredBackend'").ThrowAsJavaScriptException(); 71 | return nullptr; 72 | } 73 | std::string preferredBackend = obj.Get("preferredBackend").ToString().Utf8Value(); 74 | auto adapterIt = std::find_if( 75 | adapters.begin(), 76 | adapters.end(), 77 | [&platform, &preferredBackend](const dawn_native::Adapter adapter) -> bool { 78 | if (preferredBackend == "OpenGL" && (platform == "win32" || platform == "linux")) { 79 | return adapter.GetBackendType() == dawn_native::BackendType::OpenGL; 80 | } 81 | if (preferredBackend == "D3D12" && (platform == "win32")) { 82 | return adapter.GetBackendType() == dawn_native::BackendType::D3D12; 83 | } 84 | if (preferredBackend == "Metal" && (platform == "darwin")) { 85 | return adapter.GetBackendType() == dawn_native::BackendType::Metal; 86 | } 87 | if (preferredBackend == "Vulkan" && (platform == "win32" || platform == "linux")) { 88 | return adapter.GetBackendType() == dawn_native::BackendType::Vulkan; 89 | } 90 | return false; 91 | } 92 | ); 93 | // we found a preferred adapter 94 | if (adapterIt != adapters.end()) return *adapterIt; 95 | // otherwise we try to auto-choose a backend 96 | } 97 | // auto-choose backend 98 | auto adapterIt = std::find_if( 99 | adapters.begin(), 100 | adapters.end(), 101 | [&platform](const dawn_native::Adapter adapter) -> bool { 102 | // on windows, prefer d3d12 103 | if (platform == "win32") { 104 | return adapter.GetBackendType() == dawn_native::BackendType::D3D12; 105 | } 106 | // on linux, prefer vulkan 107 | if (platform == "linux") { 108 | return adapter.GetBackendType() == dawn_native::BackendType::Vulkan; 109 | } 110 | // on mac, prefer metal 111 | if (platform == "darwin") { 112 | return adapter.GetBackendType() == dawn_native::BackendType::Metal; 113 | } 114 | return false; 115 | } 116 | ); 117 | if (adapterIt == adapters.end()) { 118 | Napi::Error::New(env, "No compatible adapter found").ThrowAsJavaScriptException(); 119 | return nullptr; 120 | } 121 | return *adapterIt; 122 | } 123 | 124 | Napi::Value GPUAdapter::GetName(const Napi::CallbackInfo& info) { 125 | Napi::Env env = info.Env(); 126 | return Napi::String::New(env, this->instance.GetPCIInfo().name); 127 | } 128 | 129 | Napi::Value GPUAdapter::GetExtensions(const Napi::CallbackInfo& info) { 130 | Napi::Env env = info.Env(); 131 | 132 | std::vector extensions = this->instance.GetSupportedExtensions(); 133 | 134 | Napi::Array out = Napi::Array::New(env); 135 | for (unsigned int ii = 0; ii < extensions.size(); ++ii) { 136 | out[ii] = Napi::String::New(env, extensions.at(ii)); 137 | }; 138 | 139 | return out; 140 | } 141 | 142 | Napi::Object GPUAdapter::Initialize(Napi::Env env, Napi::Object exports) { 143 | Napi::HandleScope scope(env); 144 | Napi::Function func = DefineClass(env, "GPUAdapter", { 145 | InstanceAccessor( 146 | "name", 147 | &GPUAdapter::GetName, 148 | nullptr, 149 | napi_enumerable 150 | ), 151 | InstanceAccessor( 152 | "extensions", 153 | &GPUAdapter::GetExtensions, 154 | nullptr, 155 | napi_enumerable 156 | ), 157 | InstanceMethod( 158 | "_requestDevice", 159 | &GPUAdapter::requestDevice, 160 | napi_enumerable 161 | ) 162 | }); 163 | constructor = Napi::Persistent(func); 164 | constructor.SuppressDestruct(); 165 | exports.Set("GPUAdapter", func); 166 | return exports; 167 | } 168 | -------------------------------------------------------------------------------- /src/GPUAdapter.h: -------------------------------------------------------------------------------- 1 | #ifndef __GPU_ADAPTER_H__ 2 | #define __GPU_ADAPTER_H__ 3 | 4 | #include "Base.h" 5 | 6 | #include "GPUDevice.h" 7 | 8 | class GPUAdapter : public Napi::ObjectWrap { 9 | 10 | public: 11 | 12 | static Napi::Object Initialize(Napi::Env env, Napi::Object exports); 13 | static Napi::FunctionReference constructor; 14 | 15 | GPUAdapter(const Napi::CallbackInfo &info); 16 | ~GPUAdapter(); 17 | 18 | // #accessors 19 | Napi::Value GetName(const Napi::CallbackInfo &info); 20 | Napi::Value GetExtensions(const Napi::CallbackInfo &info); 21 | 22 | Napi::Value requestDevice(const Napi::CallbackInfo &info); 23 | 24 | Napi::ObjectReference window; 25 | 26 | std::string platform; 27 | std::unique_ptr nativeInstance; 28 | dawn_native::Adapter instance; 29 | 30 | private: 31 | dawn_native::Adapter createAdapter(const Napi::CallbackInfo& info); 32 | 33 | }; 34 | 35 | #endif 36 | -------------------------------------------------------------------------------- /src/GPUBindGroup.cpp: -------------------------------------------------------------------------------- 1 | #include "GPUBindGroup.h" 2 | #include "GPUDevice.h" 3 | #include "GPUBindGroupLayout.h" 4 | #include "GPUBuffer.h" 5 | #include "GPUSampler.h" 6 | #include "GPUTextureView.h" 7 | 8 | #include "DescriptorDecoder.h" 9 | 10 | #include 11 | 12 | Napi::FunctionReference GPUBindGroup::constructor; 13 | 14 | GPUBindGroup::GPUBindGroup(const Napi::CallbackInfo& info) : Napi::ObjectWrap(info) { 15 | Napi::Env env = info.Env(); 16 | 17 | this->device.Reset(info[0].As(), 1); 18 | GPUDevice* device = Napi::ObjectWrap::Unwrap(this->device.Value()); 19 | 20 | auto descriptor = DescriptorDecoder::GPUBindGroupDescriptor(device, info[1].As()); 21 | 22 | this->instance = wgpuDeviceCreateBindGroup(device->instance, &descriptor); 23 | } 24 | 25 | GPUBindGroup::~GPUBindGroup() { 26 | this->device.Reset(); 27 | wgpuBindGroupRelease(this->instance); 28 | } 29 | 30 | Napi::Object GPUBindGroup::Initialize(Napi::Env env, Napi::Object exports) { 31 | Napi::HandleScope scope(env); 32 | Napi::Function func = DefineClass(env, "GPUBindGroup", { 33 | 34 | }); 35 | constructor = Napi::Persistent(func); 36 | constructor.SuppressDestruct(); 37 | exports.Set("GPUBindGroup", func); 38 | return exports; 39 | } 40 | -------------------------------------------------------------------------------- /src/GPUBindGroup.h: -------------------------------------------------------------------------------- 1 | #ifndef __GPU_BIND_GROUP_H__ 2 | #define __GPU_BIND_GROUP_H__ 3 | 4 | #include "Base.h" 5 | 6 | class GPUBindGroup : public Napi::ObjectWrap { 7 | 8 | public: 9 | 10 | static Napi::Object Initialize(Napi::Env env, Napi::Object exports); 11 | static Napi::FunctionReference constructor; 12 | 13 | GPUBindGroup(const Napi::CallbackInfo &info); 14 | ~GPUBindGroup(); 15 | 16 | Napi::ObjectReference device; 17 | 18 | WGPUBindGroup instance; 19 | private: 20 | 21 | }; 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /src/GPUBindGroupLayout.cpp: -------------------------------------------------------------------------------- 1 | #include "GPUBindGroupLayout.h" 2 | #include "GPUDevice.h" 3 | 4 | #include "DescriptorDecoder.h" 5 | 6 | Napi::FunctionReference GPUBindGroupLayout::constructor; 7 | 8 | GPUBindGroupLayout::GPUBindGroupLayout(const Napi::CallbackInfo& info) : Napi::ObjectWrap(info) { 9 | Napi::Env env = info.Env(); 10 | 11 | this->device.Reset(info[0].As(), 1); 12 | GPUDevice* device = Napi::ObjectWrap::Unwrap(this->device.Value()); 13 | 14 | auto descriptor = DescriptorDecoder::GPUBindGroupLayoutDescriptor(device, info[1].As()); 15 | 16 | this->instance = wgpuDeviceCreateBindGroupLayout(device->instance, &descriptor); 17 | } 18 | 19 | GPUBindGroupLayout::~GPUBindGroupLayout() { 20 | this->device.Reset(); 21 | wgpuBindGroupLayoutRelease(this->instance); 22 | } 23 | 24 | Napi::Object GPUBindGroupLayout::Initialize(Napi::Env env, Napi::Object exports) { 25 | Napi::HandleScope scope(env); 26 | Napi::Function func = DefineClass(env, "GPUBindGroupLayout", { 27 | 28 | }); 29 | constructor = Napi::Persistent(func); 30 | constructor.SuppressDestruct(); 31 | exports.Set("GPUBindGroupLayout", func); 32 | return exports; 33 | } 34 | -------------------------------------------------------------------------------- /src/GPUBindGroupLayout.h: -------------------------------------------------------------------------------- 1 | #ifndef __GPU_BIND_GROUP_LAYOUT_H__ 2 | #define __GPU_BIND_GROUP_LAYOUT_H__ 3 | 4 | #include "Base.h" 5 | 6 | class GPUBindGroupLayout : public Napi::ObjectWrap { 7 | 8 | public: 9 | 10 | static Napi::Object Initialize(Napi::Env env, Napi::Object exports); 11 | static Napi::FunctionReference constructor; 12 | 13 | GPUBindGroupLayout(const Napi::CallbackInfo &info); 14 | ~GPUBindGroupLayout(); 15 | 16 | Napi::ObjectReference device; 17 | 18 | WGPUBindGroupLayout instance; 19 | private: 20 | 21 | }; 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /src/GPUBuffer.cpp: -------------------------------------------------------------------------------- 1 | #include "GPUBuffer.h" 2 | #include "GPUDevice.h" 3 | 4 | #include "DescriptorDecoder.h" 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | Napi::FunctionReference GPUBuffer::constructor; 11 | 12 | struct BufferCallbackResult { 13 | void* addr = nullptr; 14 | uint64_t length = 0; 15 | }; 16 | 17 | GPUBuffer::GPUBuffer(const Napi::CallbackInfo& info) : Napi::ObjectWrap(info) { 18 | Napi::Env env = info.Env(); 19 | 20 | Napi::Array mappingArray = Napi::Array::New(env); 21 | this->mappingArrayBuffers.Reset(mappingArray.As(), 1); 22 | 23 | this->device.Reset(info[0].As(), 1); 24 | GPUDevice* device = Napi::ObjectWrap::Unwrap(this->device.Value()); 25 | 26 | auto descriptor = DescriptorDecoder::GPUBufferDescriptor(device, info[1].As()); 27 | 28 | this->instance = wgpuDeviceCreateBuffer(device->instance, &descriptor); 29 | } 30 | 31 | GPUBuffer::~GPUBuffer() { 32 | this->device.Reset(); 33 | this->mappingArrayBuffers.Reset(); 34 | wgpuBufferRelease(this->instance); 35 | } 36 | 37 | void GPUBuffer::DestroyMappingArrayBuffers() { 38 | Napi::Array mappingArray = this->mappingArrayBuffers.Value().As(); 39 | Napi::Env env = mappingArray.Env(); 40 | for (unsigned int ii = 0; ii < mappingArray.Length(); ++ii) { 41 | Napi::ArrayBuffer ab = mappingArray.Get(ii).As(); 42 | napi_detach_arraybuffer(env, ab); 43 | }; 44 | // reset to empty array 45 | { 46 | Napi::Array mappingArray = Napi::Array::New(env); 47 | this->mappingArrayBuffers.Reset(mappingArray.As(), 1); 48 | } 49 | } 50 | 51 | Napi::Value GPUBuffer::setSubData(const Napi::CallbackInfo &info) { 52 | Napi::Env env = info.Env(); 53 | 54 | uint64_t start = static_cast(info[0].As().Uint32Value()); 55 | size_t count = 0; 56 | 57 | uint8_t* data = getTypedArrayData(info[1].As(), &count); 58 | 59 | wgpuBufferSetSubData(this->instance, start, count, data); 60 | 61 | return env.Undefined(); 62 | } 63 | 64 | Napi::Value GPUBuffer::mapReadAsync(const Napi::CallbackInfo &info) { 65 | Napi::Env env = info.Env(); 66 | 67 | bool hasCallback = info[0].IsFunction(); 68 | 69 | Napi::Function callback; 70 | if (hasCallback) callback = info[0].As(); 71 | 72 | BufferCallbackResult callbackResult; 73 | 74 | wgpuBufferMapReadAsync( 75 | this->instance, 76 | [](WGPUBufferMapAsyncStatus status, const void* data, uint64_t dataLength, void* userdata) { 77 | BufferCallbackResult* result = reinterpret_cast(userdata); 78 | result->addr = const_cast(data); 79 | result->length = dataLength; 80 | }, 81 | &callbackResult 82 | ); 83 | 84 | GPUDevice* device = Napi::ObjectWrap::Unwrap(this->device.Value()); 85 | WGPUDevice backendDevice = device->instance; 86 | 87 | wgpuDeviceTick(backendDevice); 88 | if (!callbackResult.addr) { 89 | while (!callbackResult.addr) { 90 | std::this_thread::sleep_for(std::chrono::milliseconds(5)); 91 | wgpuDeviceTick(backendDevice); 92 | }; 93 | } 94 | 95 | Napi::ArrayBuffer buffer = Napi::ArrayBuffer::New( 96 | env, 97 | callbackResult.addr, 98 | callbackResult.length, 99 | [](Napi::Env env, void* data) { } 100 | ); 101 | 102 | Napi::Array mappingArray = this->mappingArrayBuffers.Value().As(); 103 | mappingArray[mappingArray.Length()] = buffer; 104 | 105 | if (hasCallback) callback.Call({ buffer }); 106 | 107 | return hasCallback ? env.Undefined() : buffer; 108 | } 109 | 110 | Napi::Value GPUBuffer::mapWriteAsync(const Napi::CallbackInfo &info) { 111 | Napi::Env env = info.Env(); 112 | 113 | Napi::Function callback = info[0].As(); 114 | 115 | BufferCallbackResult callbackResult; 116 | wgpuBufferMapWriteAsync( 117 | this->instance, 118 | [](WGPUBufferMapAsyncStatus status, void* ptr, uint64_t dataLength, void* userdata) { 119 | BufferCallbackResult* result = reinterpret_cast(userdata); 120 | result->addr = ptr; 121 | result->length = dataLength; 122 | }, 123 | &callbackResult 124 | ); 125 | 126 | GPUDevice* device = Napi::ObjectWrap::Unwrap(this->device.Value()); 127 | WGPUDevice backendDevice = device->instance; 128 | 129 | wgpuDeviceTick(backendDevice); 130 | if (!callbackResult.addr) { 131 | while (!callbackResult.addr) { 132 | std::this_thread::sleep_for(std::chrono::milliseconds(5)); 133 | wgpuDeviceTick(backendDevice); 134 | }; 135 | } 136 | 137 | Napi::ArrayBuffer buffer = Napi::ArrayBuffer::New( 138 | env, 139 | callbackResult.addr, 140 | callbackResult.length, 141 | [](Napi::Env env, void* data) { } 142 | ); 143 | 144 | Napi::Array mappingArray = this->mappingArrayBuffers.Value().As(); 145 | mappingArray[mappingArray.Length()] = buffer; 146 | 147 | callback.Call({ buffer }); 148 | 149 | return env.Undefined(); 150 | } 151 | 152 | Napi::Value GPUBuffer::unmap(const Napi::CallbackInfo &info) { 153 | Napi::Env env = info.Env(); 154 | this->DestroyMappingArrayBuffers(); 155 | wgpuBufferUnmap(this->instance); 156 | return env.Undefined(); 157 | } 158 | 159 | Napi::Value GPUBuffer::destroy(const Napi::CallbackInfo &info) { 160 | Napi::Env env = info.Env(); 161 | this->DestroyMappingArrayBuffers(); 162 | wgpuBufferDestroy(this->instance); 163 | return env.Undefined(); 164 | } 165 | 166 | Napi::Object GPUBuffer::Initialize(Napi::Env env, Napi::Object exports) { 167 | Napi::HandleScope scope(env); 168 | Napi::Function func = DefineClass(env, "GPUBuffer", { 169 | InstanceMethod( 170 | "setSubData", 171 | &GPUBuffer::setSubData, 172 | napi_enumerable 173 | ), 174 | InstanceMethod( 175 | "_mapReadAsync", 176 | &GPUBuffer::mapReadAsync, 177 | napi_enumerable 178 | ), 179 | InstanceMethod( 180 | "_mapWriteAsync", 181 | &GPUBuffer::mapWriteAsync, 182 | napi_enumerable 183 | ), 184 | InstanceMethod( 185 | "unmap", 186 | &GPUBuffer::unmap, 187 | napi_enumerable 188 | ), 189 | InstanceMethod( 190 | "destroy", 191 | &GPUBuffer::destroy, 192 | napi_enumerable 193 | ) 194 | }); 195 | constructor = Napi::Persistent(func); 196 | constructor.SuppressDestruct(); 197 | exports.Set("GPUBuffer", func); 198 | return exports; 199 | } 200 | -------------------------------------------------------------------------------- /src/GPUBuffer.h: -------------------------------------------------------------------------------- 1 | #ifndef __GPU_BUFFER_H__ 2 | #define __GPU_BUFFER_H__ 3 | 4 | #include "Base.h" 5 | 6 | class GPUBuffer : public Napi::ObjectWrap { 7 | 8 | public: 9 | 10 | static Napi::Object Initialize(Napi::Env env, Napi::Object exports); 11 | static Napi::FunctionReference constructor; 12 | 13 | GPUBuffer(const Napi::CallbackInfo &info); 14 | ~GPUBuffer(); 15 | 16 | Napi::Value setSubData(const Napi::CallbackInfo &info); 17 | 18 | Napi::Value mapReadAsync(const Napi::CallbackInfo &info); 19 | Napi::Value mapWriteAsync(const Napi::CallbackInfo &info); 20 | 21 | Napi::Value unmap(const Napi::CallbackInfo &info); 22 | Napi::Value destroy(const Napi::CallbackInfo &info); 23 | 24 | Napi::ObjectReference device; 25 | 26 | WGPUBuffer instance; 27 | 28 | private: 29 | // ArrayBuffers created and returned in the mapping process get linked 30 | // to this GPUBuffer - we keep track of them, since we have to detach them 31 | // after this GPUBuffer got unmapped or destroyed 32 | Napi::ObjectReference mappingArrayBuffers; 33 | 34 | void DestroyMappingArrayBuffers(); 35 | }; 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /src/GPUCanvasContext.cpp: -------------------------------------------------------------------------------- 1 | #include "GPUCanvasContext.h" 2 | #include "GPUDevice.h" 3 | #include "GPUAdapter.h" 4 | #include "GPUSwapChain.h" 5 | #include "BackendBinding.h" 6 | #include "WebGPUWindow.h" 7 | 8 | #include "DescriptorDecoder.h" 9 | 10 | Napi::FunctionReference GPUCanvasContext::constructor; 11 | 12 | GPUCanvasContext::GPUCanvasContext(const Napi::CallbackInfo& info) : Napi::ObjectWrap(info) { 13 | this->window.Reset(info[0].As(), 1); 14 | } 15 | 16 | GPUCanvasContext::~GPUCanvasContext() { 17 | this->window.Reset(); 18 | } 19 | 20 | Napi::Value GPUCanvasContext::configureSwapChain(const Napi::CallbackInfo &info) { 21 | Napi::Env env = info.Env(); 22 | Napi::Object swapchain = GPUSwapChain::constructor.New({ 23 | info.This().As(), 24 | info[0].As() 25 | }); 26 | return swapchain; 27 | } 28 | 29 | Napi::Value GPUCanvasContext::getSwapChainPreferredFormat(const Napi::CallbackInfo &info) { 30 | Napi::Env env = info.Env(); 31 | 32 | Napi::Promise::Deferred deferred = Napi::Promise::Deferred::New(env); 33 | 34 | GPUDevice* device = Napi::ObjectWrap::Unwrap(info[0].As()); 35 | GPUAdapter* adapter = Napi::ObjectWrap::Unwrap(device->adapter.Value()); 36 | WebGPUWindow* window = Napi::ObjectWrap::Unwrap(adapter->window.Value()); 37 | 38 | if (window->preferredSwapChainFormat == WGPUTextureFormat_Undefined) { 39 | WGPUSwapChainDescriptor descriptor; 40 | descriptor.nextInChain = nullptr; 41 | // returns always the same address, so we dont have to release this temp swapchain? 42 | descriptor.implementation = device->binding->GetSwapChainImplementation(); 43 | WGPUSwapChain instance = wgpuDeviceCreateSwapChain(device->instance, nullptr, &descriptor); 44 | glfwPollEvents(); 45 | window->preferredSwapChainFormat = device->binding->GetPreferredSwapChainTextureFormat(); 46 | } 47 | 48 | std::string textureFormat = DescriptorDecoder::GPUTextureFormat( 49 | static_cast(window->preferredSwapChainFormat) 50 | ); 51 | deferred.Resolve(Napi::String::New(env, textureFormat)); 52 | 53 | return deferred.Promise(); 54 | } 55 | 56 | Napi::Object GPUCanvasContext::Initialize(Napi::Env env, Napi::Object exports) { 57 | Napi::HandleScope scope(env); 58 | Napi::Function func = DefineClass(env, "GPUCanvasContext", { 59 | InstanceMethod( 60 | "configureSwapChain", 61 | &GPUCanvasContext::configureSwapChain, 62 | napi_enumerable 63 | ), 64 | InstanceMethod( 65 | "getSwapChainPreferredFormat", 66 | &GPUCanvasContext::getSwapChainPreferredFormat, 67 | napi_enumerable 68 | ) 69 | }); 70 | constructor = Napi::Persistent(func); 71 | constructor.SuppressDestruct(); 72 | exports.Set("GPUCanvasContext", func); 73 | return exports; 74 | } 75 | -------------------------------------------------------------------------------- /src/GPUCanvasContext.h: -------------------------------------------------------------------------------- 1 | #ifndef __GPU_CANVAS_CONTEXT_H__ 2 | #define __GPU_CANVAS_CONTEXT_H__ 3 | 4 | #include "Base.h" 5 | 6 | class GPUCanvasContext : public Napi::ObjectWrap { 7 | 8 | public: 9 | 10 | static Napi::Object Initialize(Napi::Env env, Napi::Object exports); 11 | static Napi::FunctionReference constructor; 12 | 13 | GPUCanvasContext(const Napi::CallbackInfo &info); 14 | ~GPUCanvasContext(); 15 | 16 | Napi::Value configureSwapChain(const Napi::CallbackInfo &info); 17 | Napi::Value getSwapChainPreferredFormat(const Napi::CallbackInfo &info); 18 | 19 | Napi::ObjectReference window; 20 | }; 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /src/GPUCommandBuffer.cpp: -------------------------------------------------------------------------------- 1 | #include "GPUCommandBuffer.h" 2 | 3 | Napi::FunctionReference GPUCommandBuffer::constructor; 4 | 5 | GPUCommandBuffer::GPUCommandBuffer(const Napi::CallbackInfo& info) : Napi::ObjectWrap(info) { 6 | 7 | } 8 | 9 | GPUCommandBuffer::~GPUCommandBuffer() { 10 | wgpuCommandBufferRelease(this->instance); 11 | } 12 | 13 | Napi::Object GPUCommandBuffer::Initialize(Napi::Env env, Napi::Object exports) { 14 | Napi::HandleScope scope(env); 15 | Napi::Function func = DefineClass(env, "GPUCommandBuffer", { 16 | 17 | }); 18 | constructor = Napi::Persistent(func); 19 | constructor.SuppressDestruct(); 20 | exports.Set("GPUCommandBuffer", func); 21 | return exports; 22 | } 23 | -------------------------------------------------------------------------------- /src/GPUCommandBuffer.h: -------------------------------------------------------------------------------- 1 | #ifndef __GPU_COMMAND_BUFFER_H__ 2 | #define __GPU_COMMAND_BUFFER_H__ 3 | 4 | #include "Base.h" 5 | 6 | class GPUCommandBuffer : public Napi::ObjectWrap { 7 | 8 | public: 9 | 10 | static Napi::Object Initialize(Napi::Env env, Napi::Object exports); 11 | static Napi::FunctionReference constructor; 12 | 13 | GPUCommandBuffer(const Napi::CallbackInfo &info); 14 | ~GPUCommandBuffer(); 15 | 16 | WGPUCommandBuffer instance; 17 | private: 18 | 19 | }; 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /src/GPUCommandEncoder.cpp: -------------------------------------------------------------------------------- 1 | #include "GPUCommandEncoder.h" 2 | #include "GPUDevice.h" 3 | #include "GPUBuffer.h" 4 | #include "GPUCommandBuffer.h" 5 | #include "GPURenderPassEncoder.h" 6 | #include "GPUComputePassEncoder.h" 7 | #include "GPURayTracingPassEncoder.h" 8 | #include "GPURayTracingAccelerationContainer.h" 9 | 10 | #include "DescriptorDecoder.h" 11 | 12 | Napi::FunctionReference GPUCommandEncoder::constructor; 13 | 14 | GPUCommandEncoder::GPUCommandEncoder(const Napi::CallbackInfo& info) : Napi::ObjectWrap(info) { 15 | Napi::Env env = info.Env(); 16 | 17 | this->device.Reset(info[0].As(), 1); 18 | GPUDevice* device = Napi::ObjectWrap::Unwrap(this->device.Value()); 19 | 20 | auto descriptor = DescriptorDecoder::GPUCommandEncoderDescriptor(device, info[1].As()); 21 | 22 | this->instance = wgpuDeviceCreateCommandEncoder(device->instance, &descriptor); 23 | } 24 | 25 | GPUCommandEncoder::~GPUCommandEncoder() { 26 | this->device.Reset(); 27 | wgpuCommandEncoderRelease(this->instance); 28 | } 29 | 30 | Napi::Value GPUCommandEncoder::beginRenderPass(const Napi::CallbackInfo &info) { 31 | Napi::Env env = info.Env(); 32 | Napi::Object renderPass = GPURenderPassEncoder::constructor.New({ 33 | info.This().As(), 34 | info[0].As() 35 | }); 36 | return renderPass; 37 | } 38 | 39 | Napi::Value GPUCommandEncoder::beginComputePass(const Napi::CallbackInfo &info) { 40 | Napi::Env env = info.Env(); 41 | Napi::Object computePass = GPUComputePassEncoder::constructor.New({ 42 | info.This().As(), 43 | info[0].As() 44 | }); 45 | return computePass; 46 | } 47 | 48 | Napi::Value GPUCommandEncoder::beginRayTracingPass(const Napi::CallbackInfo &info) { 49 | Napi::Env env = info.Env(); 50 | Napi::Object rayTracingPass = GPURayTracingPassEncoder::constructor.New({ 51 | info.This().As(), 52 | info[0].As() 53 | }); 54 | return rayTracingPass; 55 | } 56 | 57 | Napi::Value GPUCommandEncoder::buildRayTracingAccelerationContainer(const Napi::CallbackInfo &info) { 58 | Napi::Env env = info.Env(); 59 | 60 | WGPUCommandEncoder commandEncoder = this->instance; 61 | 62 | WGPURayTracingAccelerationContainer container = Napi::ObjectWrap::Unwrap( 63 | info[0].As() 64 | )->instance; 65 | 66 | wgpuCommandEncoderBuildRayTracingAccelerationContainer(commandEncoder, container); 67 | 68 | return env.Undefined(); 69 | } 70 | 71 | Napi::Value GPUCommandEncoder::copyRayTracingAccelerationContainer(const Napi::CallbackInfo &info) { 72 | Napi::Env env = info.Env(); 73 | 74 | WGPUCommandEncoder commandEncoder = this->instance; 75 | 76 | WGPURayTracingAccelerationContainer srcContainer = Napi::ObjectWrap::Unwrap( 77 | info[0].As() 78 | )->instance; 79 | WGPURayTracingAccelerationContainer dstContainer = Napi::ObjectWrap::Unwrap( 80 | info[1].As() 81 | )->instance; 82 | 83 | wgpuCommandEncoderCopyRayTracingAccelerationContainer(commandEncoder, srcContainer, dstContainer); 84 | 85 | return env.Undefined(); 86 | } 87 | 88 | Napi::Value GPUCommandEncoder::updateRayTracingAccelerationContainer(const Napi::CallbackInfo &info) { 89 | Napi::Env env = info.Env(); 90 | 91 | WGPUCommandEncoder commandEncoder = this->instance; 92 | 93 | WGPURayTracingAccelerationContainer container = Napi::ObjectWrap::Unwrap( 94 | info[0].As() 95 | )->instance; 96 | 97 | wgpuCommandEncoderUpdateRayTracingAccelerationContainer(commandEncoder, container); 98 | 99 | return env.Undefined(); 100 | } 101 | 102 | Napi::Value GPUCommandEncoder::copyBufferToBuffer(const Napi::CallbackInfo &info) { 103 | Napi::Env env = info.Env(); 104 | 105 | WGPUCommandEncoder commandEncoder = this->instance; 106 | WGPUBuffer source = Napi::ObjectWrap::Unwrap(info[0].As())->instance; 107 | 108 | uint64_t sourceOffset = static_cast(info[1].As().Uint32Value()); 109 | 110 | WGPUBuffer destination = Napi::ObjectWrap::Unwrap(info[2].As())->instance; 111 | uint64_t destinationOffset = static_cast(info[3].As().Uint32Value()); 112 | uint64_t size = static_cast(info[4].As().Uint32Value()); 113 | 114 | wgpuCommandEncoderCopyBufferToBuffer(commandEncoder, source, sourceOffset, destination, destinationOffset, size); 115 | 116 | return env.Undefined(); 117 | } 118 | 119 | Napi::Value GPUCommandEncoder::copyBufferToTexture(const Napi::CallbackInfo &info) { 120 | Napi::Env env = info.Env(); 121 | 122 | GPUDevice* device = Napi::ObjectWrap::Unwrap(this->device.Value()); 123 | 124 | auto source = DescriptorDecoder::GPUBufferCopyView(device, info[0].As()); 125 | auto destination = DescriptorDecoder::GPUTextureCopyView(device, info[1].As()); 126 | auto copySize = DescriptorDecoder::GPUExtent3D(device, info[2].As()); 127 | 128 | wgpuCommandEncoderCopyBufferToTexture(this->instance, &source, &destination, ©Size); 129 | 130 | return env.Undefined(); 131 | } 132 | 133 | Napi::Value GPUCommandEncoder::copyTextureToBuffer(const Napi::CallbackInfo &info) { 134 | Napi::Env env = info.Env(); 135 | 136 | GPUDevice* device = Napi::ObjectWrap::Unwrap(this->device.Value()); 137 | 138 | auto source = DescriptorDecoder::GPUTextureCopyView(device, info[0].As()); 139 | auto destination = DescriptorDecoder::GPUBufferCopyView(device, info[1].As()); 140 | auto copySize = DescriptorDecoder::GPUExtent3D(device, info[2].As()); 141 | 142 | wgpuCommandEncoderCopyTextureToBuffer(this->instance, &source, &destination, ©Size); 143 | 144 | return env.Undefined(); 145 | } 146 | 147 | Napi::Value GPUCommandEncoder::copyTextureToTexture(const Napi::CallbackInfo &info) { 148 | Napi::Env env = info.Env(); 149 | 150 | GPUDevice* device = Napi::ObjectWrap::Unwrap(this->device.Value()); 151 | 152 | auto source = DescriptorDecoder::GPUTextureCopyView(device, info[0].As()); 153 | auto destination = DescriptorDecoder::GPUTextureCopyView(device, info[1].As()); 154 | auto copySize = DescriptorDecoder::GPUExtent3D(device, info[2].As()); 155 | 156 | wgpuCommandEncoderCopyTextureToTexture(this->instance, &source, &destination, ©Size); 157 | 158 | return env.Undefined(); 159 | } 160 | 161 | Napi::Value GPUCommandEncoder::copyImageBitmapToTexture(const Napi::CallbackInfo &info) { 162 | Napi::Env env = info.Env(); 163 | 164 | GPUDevice* device = Napi::ObjectWrap::Unwrap(this->device.Value()); 165 | Napi::String type = Napi::String::New(env, "Type"); 166 | Napi::String message = Napi::String::New(env, "Unimplemented method 'GPUCommandEncoder::copyImageBitmapToTexture'"); 167 | device->throwCallbackError(type, message); 168 | 169 | return env.Undefined(); 170 | } 171 | 172 | Napi::Value GPUCommandEncoder::pushDebugGroup(const Napi::CallbackInfo &info) { 173 | Napi::Env env = info.Env(); 174 | 175 | const char* groupLabel = info[0].As().Utf8Value().c_str(); 176 | wgpuCommandEncoderPushDebugGroup(this->instance, groupLabel); 177 | 178 | return env.Undefined(); 179 | } 180 | 181 | Napi::Value GPUCommandEncoder::popDebugGroup(const Napi::CallbackInfo &info) { 182 | Napi::Env env = info.Env(); 183 | 184 | wgpuCommandEncoderPopDebugGroup(this->instance); 185 | 186 | return env.Undefined(); 187 | } 188 | 189 | Napi::Value GPUCommandEncoder::insertDebugMarker(const Napi::CallbackInfo &info) { 190 | Napi::Env env = info.Env(); 191 | 192 | const char* groupLabel = info[0].As().Utf8Value().c_str(); 193 | wgpuCommandEncoderInsertDebugMarker(this->instance, groupLabel); 194 | 195 | return env.Undefined(); 196 | } 197 | 198 | Napi::Value GPUCommandEncoder::finish(const Napi::CallbackInfo &info) { 199 | Napi::Env env = info.Env(); 200 | 201 | WGPUCommandBuffer buffer = wgpuCommandEncoderFinish(this->instance, nullptr); 202 | 203 | Napi::Object commandBuffer = GPUCommandBuffer::constructor.New({}); 204 | GPUCommandBuffer* uwCommandBuffer = Napi::ObjectWrap::Unwrap(commandBuffer); 205 | uwCommandBuffer->instance = buffer; 206 | 207 | return commandBuffer; 208 | } 209 | 210 | Napi::Object GPUCommandEncoder::Initialize(Napi::Env env, Napi::Object exports) { 211 | Napi::HandleScope scope(env); 212 | Napi::Function func = DefineClass(env, "GPUCommandEncoder", { 213 | InstanceMethod( 214 | "beginRenderPass", 215 | &GPUCommandEncoder::beginRenderPass, 216 | napi_enumerable 217 | ), 218 | InstanceMethod( 219 | "beginComputePass", 220 | &GPUCommandEncoder::beginComputePass, 221 | napi_enumerable 222 | ), 223 | InstanceMethod( 224 | "beginRayTracingPass", 225 | &GPUCommandEncoder::beginRayTracingPass, 226 | napi_enumerable 227 | ), 228 | InstanceMethod( 229 | "buildRayTracingAccelerationContainer", 230 | &GPUCommandEncoder::buildRayTracingAccelerationContainer, 231 | napi_enumerable 232 | ), 233 | InstanceMethod( 234 | "copyRayTracingAccelerationContainer", 235 | &GPUCommandEncoder::copyRayTracingAccelerationContainer, 236 | napi_enumerable 237 | ), 238 | InstanceMethod( 239 | "updateRayTracingAccelerationContainer", 240 | &GPUCommandEncoder::updateRayTracingAccelerationContainer, 241 | napi_enumerable 242 | ), 243 | InstanceMethod( 244 | "copyBufferToBuffer", 245 | &GPUCommandEncoder::copyBufferToBuffer, 246 | napi_enumerable 247 | ), 248 | InstanceMethod( 249 | "copyBufferToTexture", 250 | &GPUCommandEncoder::copyBufferToTexture, 251 | napi_enumerable 252 | ), 253 | InstanceMethod( 254 | "copyTextureToBuffer", 255 | &GPUCommandEncoder::copyTextureToBuffer, 256 | napi_enumerable 257 | ), 258 | InstanceMethod( 259 | "copyTextureToTexture", 260 | &GPUCommandEncoder::copyTextureToTexture, 261 | napi_enumerable 262 | ), 263 | InstanceMethod( 264 | "copyImageBitmapToTexture", 265 | &GPUCommandEncoder::copyImageBitmapToTexture, 266 | napi_enumerable 267 | ), 268 | InstanceMethod( 269 | "pushDebugGroup", 270 | &GPUCommandEncoder::pushDebugGroup, 271 | napi_enumerable 272 | ), 273 | InstanceMethod( 274 | "popDebugGroup", 275 | &GPUCommandEncoder::popDebugGroup, 276 | napi_enumerable 277 | ), 278 | InstanceMethod( 279 | "insertDebugMarker", 280 | &GPUCommandEncoder::insertDebugMarker, 281 | napi_enumerable 282 | ), 283 | InstanceMethod( 284 | "finish", 285 | &GPUCommandEncoder::finish, 286 | napi_enumerable 287 | ) 288 | }); 289 | constructor = Napi::Persistent(func); 290 | constructor.SuppressDestruct(); 291 | exports.Set("GPUCommandEncoder", func); 292 | return exports; 293 | } 294 | -------------------------------------------------------------------------------- /src/GPUCommandEncoder.h: -------------------------------------------------------------------------------- 1 | #ifndef __GPU_COMMAND_ENCODER_H__ 2 | #define __GPU_COMMAND_ENCODER_H__ 3 | 4 | #include "Base.h" 5 | 6 | class GPUCommandEncoder : public Napi::ObjectWrap { 7 | 8 | public: 9 | 10 | static Napi::Object Initialize(Napi::Env env, Napi::Object exports); 11 | static Napi::FunctionReference constructor; 12 | 13 | GPUCommandEncoder(const Napi::CallbackInfo &info); 14 | ~GPUCommandEncoder(); 15 | 16 | Napi::Value beginRenderPass(const Napi::CallbackInfo &info); 17 | Napi::Value beginComputePass(const Napi::CallbackInfo &info); 18 | Napi::Value beginRayTracingPass(const Napi::CallbackInfo &info); 19 | 20 | Napi::Value buildRayTracingAccelerationContainer(const Napi::CallbackInfo &info); 21 | Napi::Value copyRayTracingAccelerationContainer(const Napi::CallbackInfo &info); 22 | Napi::Value updateRayTracingAccelerationContainer(const Napi::CallbackInfo &info); 23 | 24 | Napi::Value copyBufferToBuffer(const Napi::CallbackInfo &info); 25 | Napi::Value copyBufferToTexture(const Napi::CallbackInfo &info); 26 | Napi::Value copyTextureToBuffer(const Napi::CallbackInfo &info); 27 | Napi::Value copyTextureToTexture(const Napi::CallbackInfo &info); 28 | Napi::Value copyImageBitmapToTexture(const Napi::CallbackInfo &info); 29 | 30 | Napi::Value pushDebugGroup(const Napi::CallbackInfo &info); 31 | Napi::Value popDebugGroup(const Napi::CallbackInfo &info); 32 | Napi::Value insertDebugMarker(const Napi::CallbackInfo &info); 33 | 34 | Napi::Value finish(const Napi::CallbackInfo &info); 35 | 36 | Napi::ObjectReference device; 37 | 38 | WGPUCommandEncoder instance; 39 | private: 40 | 41 | }; 42 | 43 | #endif 44 | -------------------------------------------------------------------------------- /src/GPUComputePassEncoder.cpp: -------------------------------------------------------------------------------- 1 | #include "GPUComputePassEncoder.h" 2 | #include "GPUDevice.h" 3 | #include "GPUCommandEncoder.h" 4 | #include "GPUComputePipeline.h" 5 | #include "GPUBuffer.h" 6 | #include "GPUBindGroup.h" 7 | 8 | #include "DescriptorDecoder.h" 9 | 10 | Napi::FunctionReference GPUComputePassEncoder::constructor; 11 | 12 | GPUComputePassEncoder::GPUComputePassEncoder(const Napi::CallbackInfo& info) : Napi::ObjectWrap(info) { 13 | Napi::Env env = info.Env(); 14 | 15 | this->commandEncoder.Reset(info[0].As(), 1); 16 | GPUCommandEncoder* commandEncoder = Napi::ObjectWrap::Unwrap(this->commandEncoder.Value()); 17 | GPUDevice* device = Napi::ObjectWrap::Unwrap(commandEncoder->device.Value()); 18 | 19 | auto descriptor = DescriptorDecoder::GPUComputePassDescriptor(device, info[1].As()); 20 | 21 | this->instance = wgpuCommandEncoderBeginComputePass(commandEncoder->instance, &descriptor); 22 | } 23 | 24 | GPUComputePassEncoder::~GPUComputePassEncoder() { 25 | this->device.Reset(); 26 | this->commandEncoder.Reset(); 27 | wgpuComputePassEncoderRelease(this->instance); 28 | } 29 | 30 | Napi::Value GPUComputePassEncoder::setPipeline(const Napi::CallbackInfo &info) { 31 | Napi::Env env = info.Env(); 32 | 33 | GPUComputePipeline* computePipeline = Napi::ObjectWrap::Unwrap(info[0].As()); 34 | 35 | wgpuComputePassEncoderSetPipeline(this->instance, computePipeline->instance); 36 | 37 | return env.Undefined(); 38 | } 39 | 40 | Napi::Value GPUComputePassEncoder::dispatch(const Napi::CallbackInfo &info) { 41 | Napi::Env env = info.Env(); 42 | 43 | uint32_t x = info[0].IsNumber() ? info[0].As().Uint32Value() : 1; 44 | uint32_t y = info[1].IsNumber() ? info[1].As().Uint32Value() : 1; 45 | uint32_t z = info[2].IsNumber() ? info[2].As().Uint32Value() : 1; 46 | 47 | wgpuComputePassEncoderDispatch(this->instance, x, y, z); 48 | 49 | return env.Undefined(); 50 | } 51 | 52 | Napi::Value GPUComputePassEncoder::dispatchIndirect(const Napi::CallbackInfo &info) { 53 | Napi::Env env = info.Env(); 54 | 55 | GPUBuffer* indirectBuffer = Napi::ObjectWrap::Unwrap(info[0].As()); 56 | uint64_t indirectOffset = static_cast(info[1].As().Uint32Value()); 57 | 58 | wgpuComputePassEncoderDispatchIndirect(this->instance, indirectBuffer->instance, indirectOffset); 59 | 60 | return env.Undefined(); 61 | } 62 | 63 | Napi::Value GPUComputePassEncoder::endPass(const Napi::CallbackInfo &info) { 64 | Napi::Env env = info.Env(); 65 | 66 | wgpuComputePassEncoderEndPass(this->instance); 67 | 68 | return env.Undefined(); 69 | } 70 | 71 | Napi::Value GPUComputePassEncoder::setBindGroup(const Napi::CallbackInfo &info) { 72 | Napi::Env env = info.Env(); 73 | 74 | uint32_t groupIndex = info[0].As().Uint32Value(); 75 | 76 | WGPUBindGroup group = Napi::ObjectWrap::Unwrap(info[1].As())->instance; 77 | 78 | uint32_t dynamicOffsetCount = 0; 79 | std::vector dynamicOffsets; 80 | if (info[2].IsArray()) { 81 | Napi::Array array = info[2].As(); 82 | for (unsigned int ii = 0; ii < array.Length(); ++ii) { 83 | uint32_t offset = array.Get(ii).As().Uint32Value(); 84 | dynamicOffsets.push_back(offset); 85 | }; 86 | dynamicOffsetCount = array.Length(); 87 | } 88 | 89 | wgpuComputePassEncoderSetBindGroup(this->instance, groupIndex, group, dynamicOffsetCount, dynamicOffsets.data()); 90 | 91 | return env.Undefined(); 92 | } 93 | 94 | Napi::Value GPUComputePassEncoder::pushDebugGroup(const Napi::CallbackInfo &info) { 95 | Napi::Env env = info.Env(); 96 | 97 | const char* groupLabel = info[0].As().Utf8Value().c_str(); 98 | wgpuComputePassEncoderPushDebugGroup(this->instance, groupLabel); 99 | 100 | return env.Undefined(); 101 | } 102 | 103 | Napi::Value GPUComputePassEncoder::popDebugGroup(const Napi::CallbackInfo &info) { 104 | Napi::Env env = info.Env(); 105 | 106 | wgpuComputePassEncoderPopDebugGroup(this->instance); 107 | 108 | return env.Undefined(); 109 | } 110 | 111 | Napi::Value GPUComputePassEncoder::insertDebugMarker(const Napi::CallbackInfo &info) { 112 | Napi::Env env = info.Env(); 113 | 114 | const char* groupLabel = info[0].As().Utf8Value().c_str(); 115 | wgpuComputePassEncoderInsertDebugMarker(this->instance, groupLabel); 116 | 117 | return env.Undefined(); 118 | } 119 | 120 | Napi::Object GPUComputePassEncoder::Initialize(Napi::Env env, Napi::Object exports) { 121 | Napi::HandleScope scope(env); 122 | Napi::Function func = DefineClass(env, "GPUComputePassEncoder", { 123 | InstanceMethod( 124 | "setPipeline", 125 | &GPUComputePassEncoder::setPipeline, 126 | napi_enumerable 127 | ), 128 | InstanceMethod( 129 | "dispatch", 130 | &GPUComputePassEncoder::dispatch, 131 | napi_enumerable 132 | ), 133 | InstanceMethod( 134 | "dispatchIndirect", 135 | &GPUComputePassEncoder::dispatchIndirect, 136 | napi_enumerable 137 | ), 138 | InstanceMethod( 139 | "endPass", 140 | &GPUComputePassEncoder::endPass, 141 | napi_enumerable 142 | ), 143 | InstanceMethod( 144 | "setBindGroup", 145 | &GPUComputePassEncoder::setBindGroup, 146 | napi_enumerable 147 | ), 148 | InstanceMethod( 149 | "pushDebugGroup", 150 | &GPUComputePassEncoder::pushDebugGroup, 151 | napi_enumerable 152 | ), 153 | InstanceMethod( 154 | "popDebugGroup", 155 | &GPUComputePassEncoder::popDebugGroup, 156 | napi_enumerable 157 | ), 158 | InstanceMethod( 159 | "insertDebugMarker", 160 | &GPUComputePassEncoder::insertDebugMarker, 161 | napi_enumerable 162 | ), 163 | }); 164 | constructor = Napi::Persistent(func); 165 | constructor.SuppressDestruct(); 166 | exports.Set("GPUComputePassEncoder", func); 167 | return exports; 168 | } 169 | -------------------------------------------------------------------------------- /src/GPUComputePassEncoder.h: -------------------------------------------------------------------------------- 1 | #ifndef __GPU_COMPUTE_PASS_ENCODER_H__ 2 | #define __GPU_COMPUTE_PASS_ENCODER_H__ 3 | 4 | #include "Base.h" 5 | 6 | class GPUComputePassEncoder : public Napi::ObjectWrap { 7 | 8 | public: 9 | 10 | static Napi::Object Initialize(Napi::Env env, Napi::Object exports); 11 | static Napi::FunctionReference constructor; 12 | 13 | GPUComputePassEncoder(const Napi::CallbackInfo &info); 14 | ~GPUComputePassEncoder(); 15 | 16 | // GPURenderEncoderBase BEGIN 17 | Napi::Value setPipeline(const Napi::CallbackInfo &info); 18 | Napi::Value dispatch(const Napi::CallbackInfo &info); 19 | Napi::Value dispatchIndirect(const Napi::CallbackInfo &info); 20 | Napi::Value endPass(const Napi::CallbackInfo &info); 21 | // GPURenderEncoderBase END 22 | 23 | // GPUProgrammablePassEncoder BEGIN 24 | Napi::Value setBindGroup(const Napi::CallbackInfo &info); 25 | Napi::Value pushDebugGroup(const Napi::CallbackInfo &info); 26 | Napi::Value popDebugGroup(const Napi::CallbackInfo &info); 27 | Napi::Value insertDebugMarker(const Napi::CallbackInfo &info); 28 | // GPUProgrammablePassEncoder END 29 | 30 | Napi::ObjectReference device; 31 | Napi::ObjectReference commandEncoder; 32 | 33 | WGPUComputePassEncoder instance; 34 | private: 35 | 36 | }; 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /src/GPUComputePipeline.cpp: -------------------------------------------------------------------------------- 1 | #include "GPUComputePipeline.h" 2 | #include "GPUDevice.h" 3 | #include "GPUShaderModule.h" 4 | 5 | #include "DescriptorDecoder.h" 6 | 7 | Napi::FunctionReference GPUComputePipeline::constructor; 8 | 9 | GPUComputePipeline::GPUComputePipeline(const Napi::CallbackInfo& info) : Napi::ObjectWrap(info) { 10 | Napi::Env env = info.Env(); 11 | 12 | this->device.Reset(info[0].As(), 1); 13 | GPUDevice* device = Napi::ObjectWrap::Unwrap(this->device.Value()); 14 | 15 | auto descriptor = DescriptorDecoder::GPUComputePipelineDescriptor(device, info[1].As()); 16 | 17 | this->instance = wgpuDeviceCreateComputePipeline(device->instance, &descriptor); 18 | } 19 | 20 | GPUComputePipeline::~GPUComputePipeline() { 21 | this->device.Reset(); 22 | wgpuComputePipelineRelease(this->instance); 23 | } 24 | 25 | Napi::Object GPUComputePipeline::Initialize(Napi::Env env, Napi::Object exports) { 26 | Napi::HandleScope scope(env); 27 | Napi::Function func = DefineClass(env, "GPUComputePipeline", { 28 | 29 | }); 30 | constructor = Napi::Persistent(func); 31 | constructor.SuppressDestruct(); 32 | exports.Set("GPUComputePipeline", func); 33 | return exports; 34 | } 35 | -------------------------------------------------------------------------------- /src/GPUComputePipeline.h: -------------------------------------------------------------------------------- 1 | #ifndef __GPU_COMPUTE_PIPELINE_H__ 2 | #define __GPU_COMPUTE_PIPELINE_H__ 3 | 4 | #include "Base.h" 5 | 6 | class GPUComputePipeline : public Napi::ObjectWrap { 7 | 8 | public: 9 | 10 | static Napi::Object Initialize(Napi::Env env, Napi::Object exports); 11 | static Napi::FunctionReference constructor; 12 | 13 | GPUComputePipeline(const Napi::CallbackInfo &info); 14 | ~GPUComputePipeline(); 15 | 16 | Napi::ObjectReference device; 17 | 18 | WGPUComputePipeline instance; 19 | private: 20 | 21 | }; 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /src/GPUDevice.h: -------------------------------------------------------------------------------- 1 | #ifndef __GPU_DEVICE_H__ 2 | #define __GPU_DEVICE_H__ 3 | 4 | #include "Base.h" 5 | 6 | #include "BackendBinding.h" 7 | 8 | class GPUDevice : public Napi::ObjectWrap { 9 | 10 | public: 11 | 12 | static Napi::Object Initialize(Napi::Env env, Napi::Object exports); 13 | static Napi::FunctionReference constructor; 14 | 15 | GPUDevice(const Napi::CallbackInfo &info); 16 | ~GPUDevice(); 17 | 18 | // #accessors 19 | Napi::Value GetExtensions(const Napi::CallbackInfo &info); 20 | Napi::Value GetLimits(const Napi::CallbackInfo &info); 21 | Napi::Value GetAdapter(const Napi::CallbackInfo &info); 22 | void SetOnErrorCallback(const Napi::CallbackInfo& info, const Napi::Value& value); 23 | 24 | Napi::Value tick(const Napi::CallbackInfo &info); 25 | Napi::Value getQueue(const Napi::CallbackInfo &info); 26 | Napi::Value createBuffer(const Napi::CallbackInfo &info); 27 | Napi::Value createBufferMapped(const Napi::CallbackInfo &info); 28 | Napi::Value createBufferMappedAsync(const Napi::CallbackInfo &info); 29 | Napi::Value createTexture(const Napi::CallbackInfo &info); 30 | Napi::Value createSampler(const Napi::CallbackInfo &info); 31 | Napi::Value createBindGroupLayout(const Napi::CallbackInfo &info); 32 | Napi::Value createPipelineLayout(const Napi::CallbackInfo &info); 33 | Napi::Value createBindGroup(const Napi::CallbackInfo &info); 34 | Napi::Value createShaderModule(const Napi::CallbackInfo &info); 35 | Napi::Value createComputePipeline(const Napi::CallbackInfo &info); 36 | Napi::Value createRenderPipeline(const Napi::CallbackInfo &info); 37 | Napi::Value createCommandEncoder(const Napi::CallbackInfo &info); 38 | Napi::Value createRenderBundleEncoder(const Napi::CallbackInfo &info); 39 | Napi::Value createRayTracingAccelerationContainer(const Napi::CallbackInfo &info); 40 | Napi::Value createRayTracingShaderBindingTable(const Napi::CallbackInfo &info); 41 | Napi::Value createRayTracingPipeline(const Napi::CallbackInfo &info); 42 | 43 | void throwCallbackError(const Napi::Value& type, const Napi::Value& msg); 44 | 45 | Napi::ObjectReference extensions; 46 | Napi::ObjectReference limits; 47 | Napi::ObjectReference adapter; 48 | 49 | Napi::ObjectReference mainQueue; 50 | 51 | Napi::FunctionReference onErrorCallback; 52 | 53 | dawn_native::Adapter _adapter; 54 | BackendBinding* binding; 55 | 56 | WGPUDevice instance; 57 | private: 58 | Napi::Object createQueue(const Napi::CallbackInfo& info); 59 | BackendBinding* createBinding(const Napi::CallbackInfo& info, WGPUDevice device); 60 | 61 | }; 62 | 63 | #endif 64 | -------------------------------------------------------------------------------- /src/GPUFence.cpp: -------------------------------------------------------------------------------- 1 | #include "GPUFence.h" 2 | #include "GPUQueue.h" 3 | #include "GPUDevice.h" 4 | 5 | #include "DescriptorDecoder.h" 6 | 7 | #include 8 | #include 9 | 10 | Napi::FunctionReference GPUFence::constructor; 11 | 12 | GPUFence::GPUFence(const Napi::CallbackInfo& info) : Napi::ObjectWrap(info) { 13 | Napi::Env env = info.Env(); 14 | 15 | this->queue.Reset(info[0].As(), 1); 16 | GPUQueue* queue = Napi::ObjectWrap::Unwrap(this->queue.Value()); 17 | GPUDevice* device = Napi::ObjectWrap::Unwrap(queue->device.Value()); 18 | 19 | auto descriptor = DescriptorDecoder::GPUFenceDescriptor(device, info[1].As()); 20 | 21 | this->instance = wgpuQueueCreateFence(queue->instance, &descriptor); 22 | } 23 | 24 | GPUFence::~GPUFence() { 25 | this->device.Reset(); 26 | this->queue.Reset(); 27 | wgpuFenceRelease(this->instance); 28 | } 29 | 30 | Napi::Value GPUFence::getCompletedValue(const Napi::CallbackInfo &info) { 31 | Napi::Env env = info.Env(); 32 | uint64_t completedValue = wgpuFenceGetCompletedValue(this->instance); 33 | return Napi::Number::New(env, static_cast(completedValue)); 34 | } 35 | 36 | Napi::Value GPUFence::onCompletion(const Napi::CallbackInfo &info) { 37 | Napi::Env env = info.Env(); 38 | 39 | uint64_t completionValue = static_cast(info[0].As().Uint32Value()); 40 | 41 | Napi::Function callback = info[1].As(); 42 | 43 | wgpuFenceOnCompletion( 44 | this->instance, 45 | static_cast(completionValue), 46 | [](WGPUFenceCompletionStatus status, void* userdata) { 47 | 48 | }, 49 | nullptr 50 | ); 51 | 52 | GPUQueue* queue = Napi::ObjectWrap::Unwrap(this->queue.Value()); 53 | GPUDevice* device = Napi::ObjectWrap::Unwrap(queue->device.Value()); 54 | WGPUDevice backendDevice = device->instance; 55 | 56 | wgpuDeviceTick(backendDevice); 57 | if (wgpuFenceGetCompletedValue(this->instance) != completionValue) { 58 | while (wgpuFenceGetCompletedValue(this->instance) != completionValue) { 59 | std::this_thread::sleep_for(std::chrono::milliseconds(5)); 60 | wgpuDeviceTick(backendDevice); 61 | }; 62 | } 63 | 64 | callback.Call({ }); 65 | 66 | return env.Undefined(); 67 | } 68 | 69 | Napi::Object GPUFence::Initialize(Napi::Env env, Napi::Object exports) { 70 | Napi::HandleScope scope(env); 71 | Napi::Function func = DefineClass(env, "GPUFence", { 72 | InstanceMethod( 73 | "getCompletedValue", 74 | &GPUFence::getCompletedValue, 75 | napi_enumerable 76 | ), 77 | InstanceMethod( 78 | "_onCompletion", 79 | &GPUFence::onCompletion, 80 | napi_enumerable 81 | ) 82 | }); 83 | constructor = Napi::Persistent(func); 84 | constructor.SuppressDestruct(); 85 | exports.Set("GPUFence", func); 86 | return exports; 87 | } 88 | -------------------------------------------------------------------------------- /src/GPUFence.h: -------------------------------------------------------------------------------- 1 | #ifndef __GPU_FENCE_H__ 2 | #define __GPU_FENCE_H__ 3 | 4 | #include "Base.h" 5 | 6 | class GPUFence : public Napi::ObjectWrap { 7 | 8 | public: 9 | 10 | static Napi::Object Initialize(Napi::Env env, Napi::Object exports); 11 | static Napi::FunctionReference constructor; 12 | 13 | GPUFence(const Napi::CallbackInfo &info); 14 | ~GPUFence(); 15 | 16 | Napi::Value getCompletedValue(const Napi::CallbackInfo &info); 17 | Napi::Value onCompletion(const Napi::CallbackInfo &info); 18 | 19 | Napi::ObjectReference device; 20 | 21 | Napi::ObjectReference queue; 22 | 23 | WGPUFence instance; 24 | private: 25 | 26 | }; 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /src/GPUPipelineLayout.cpp: -------------------------------------------------------------------------------- 1 | #include "GPUPipelineLayout.h" 2 | #include "GPUDevice.h" 3 | #include "GPUBindGroupLayout.h" 4 | 5 | #include "DescriptorDecoder.h" 6 | 7 | #include 8 | 9 | Napi::FunctionReference GPUPipelineLayout::constructor; 10 | 11 | GPUPipelineLayout::GPUPipelineLayout(const Napi::CallbackInfo& info) : Napi::ObjectWrap(info) { 12 | Napi::Env env = info.Env(); 13 | 14 | this->device.Reset(info[0].As(), 1); 15 | GPUDevice* device = Napi::ObjectWrap::Unwrap(this->device.Value()); 16 | 17 | auto descriptor = DescriptorDecoder::GPUPipelineLayoutDescriptor(device, info[1].As()); 18 | 19 | this->instance = wgpuDeviceCreatePipelineLayout(device->instance, &descriptor); 20 | } 21 | 22 | GPUPipelineLayout::~GPUPipelineLayout() { 23 | this->device.Reset(); 24 | wgpuPipelineLayoutRelease(this->instance); 25 | } 26 | 27 | Napi::Object GPUPipelineLayout::Initialize(Napi::Env env, Napi::Object exports) { 28 | Napi::HandleScope scope(env); 29 | Napi::Function func = DefineClass(env, "GPUPipelineLayout", { 30 | 31 | }); 32 | constructor = Napi::Persistent(func); 33 | constructor.SuppressDestruct(); 34 | exports.Set("GPUPipelineLayout", func); 35 | return exports; 36 | } 37 | -------------------------------------------------------------------------------- /src/GPUPipelineLayout.h: -------------------------------------------------------------------------------- 1 | #ifndef __GPU_PIPELINE_LAYOUT_H__ 2 | #define __GPU_PIPELINE_LAYOUT_H__ 3 | 4 | #include "Base.h" 5 | 6 | class GPUPipelineLayout : public Napi::ObjectWrap { 7 | 8 | public: 9 | 10 | static Napi::Object Initialize(Napi::Env env, Napi::Object exports); 11 | static Napi::FunctionReference constructor; 12 | 13 | GPUPipelineLayout(const Napi::CallbackInfo &info); 14 | ~GPUPipelineLayout(); 15 | 16 | Napi::ObjectReference device; 17 | 18 | WGPUPipelineLayout instance; 19 | private: 20 | 21 | }; 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /src/GPUQueue.cpp: -------------------------------------------------------------------------------- 1 | #include "GPUQueue.h" 2 | #include "GPUDevice.h" 3 | #include "GPUFence.h" 4 | #include "GPUCommandBuffer.h" 5 | 6 | #include 7 | 8 | Napi::FunctionReference GPUQueue::constructor; 9 | 10 | GPUQueue::GPUQueue(const Napi::CallbackInfo& info) : Napi::ObjectWrap(info) { 11 | Napi::Env env = info.Env(); 12 | 13 | this->device.Reset(info[0].As(), 1); 14 | GPUDevice* device = Napi::ObjectWrap::Unwrap(this->device.Value()); 15 | 16 | this->instance = wgpuDeviceGetDefaultQueue(device->instance); 17 | } 18 | 19 | GPUQueue::~GPUQueue() { 20 | this->device.Reset(); 21 | wgpuQueueRelease(this->instance); 22 | } 23 | 24 | Napi::Value GPUQueue::submit(const Napi::CallbackInfo &info) { 25 | Napi::Env env = info.Env(); 26 | 27 | Napi::Array array = info[0].As(); 28 | 29 | uint32_t length = array.Length(); 30 | std::vector commands; 31 | for (unsigned int ii = 0; ii < length; ++ii) { 32 | Napi::Object item = array.Get(ii).As(); 33 | WGPUCommandBuffer value = Napi::ObjectWrap::Unwrap(item)->instance; 34 | commands.push_back(value); 35 | }; 36 | 37 | wgpuQueueSubmit(this->instance, length, commands.data()); 38 | 39 | return env.Undefined(); 40 | } 41 | 42 | Napi::Value GPUQueue::createFence(const Napi::CallbackInfo &info) { 43 | Napi::Env env = info.Env(); 44 | std::vector args = { 45 | info.This().As() 46 | }; 47 | if (info[0].IsObject()) { 48 | args.push_back(info[0].As()); 49 | } 50 | Napi::Object fence = GPUFence::constructor.New(args); 51 | return fence; 52 | } 53 | 54 | Napi::Value GPUQueue::signal(const Napi::CallbackInfo &info) { 55 | Napi::Env env = info.Env(); 56 | 57 | WGPUFence fence = Napi::ObjectWrap::Unwrap(info[0].ToObject())->instance; 58 | 59 | uint64_t signalValue = static_cast(info[0].As().Uint32Value()); 60 | wgpuQueueSignal(this->instance, fence, signalValue); 61 | 62 | return env.Undefined(); 63 | } 64 | 65 | Napi::Object GPUQueue::Initialize(Napi::Env env, Napi::Object exports) { 66 | Napi::HandleScope scope(env); 67 | Napi::Function func = DefineClass(env, "GPUQueue", { 68 | InstanceMethod( 69 | "submit", 70 | &GPUQueue::submit, 71 | napi_enumerable 72 | ), 73 | InstanceMethod( 74 | "createFence", 75 | &GPUQueue::createFence, 76 | napi_enumerable 77 | ), 78 | InstanceMethod( 79 | "signal", 80 | &GPUQueue::signal, 81 | napi_enumerable 82 | ) 83 | }); 84 | constructor = Napi::Persistent(func); 85 | constructor.SuppressDestruct(); 86 | exports.Set("GPUQueue", func); 87 | return exports; 88 | } 89 | -------------------------------------------------------------------------------- /src/GPUQueue.h: -------------------------------------------------------------------------------- 1 | #ifndef __GPU_QUEUE_H__ 2 | #define __GPU_QUEUE_H__ 3 | 4 | #include "Base.h" 5 | 6 | class GPUQueue : public Napi::ObjectWrap { 7 | 8 | public: 9 | 10 | static Napi::Object Initialize(Napi::Env env, Napi::Object exports); 11 | static Napi::FunctionReference constructor; 12 | 13 | GPUQueue(const Napi::CallbackInfo &info); 14 | ~GPUQueue(); 15 | 16 | Napi::Value submit(const Napi::CallbackInfo &info); 17 | Napi::Value createFence(const Napi::CallbackInfo &info); 18 | Napi::Value signal(const Napi::CallbackInfo &info); 19 | 20 | Napi::ObjectReference device; 21 | 22 | WGPUQueue instance; 23 | private: 24 | 25 | }; 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /src/GPURayTracingAccelerationContainer.cpp: -------------------------------------------------------------------------------- 1 | #include "GPURayTracingAccelerationContainer.h" 2 | #include "GPUDevice.h" 3 | 4 | #include "DescriptorDecoder.h" 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | Napi::FunctionReference GPURayTracingAccelerationContainer::constructor; 11 | 12 | GPURayTracingAccelerationContainer::GPURayTracingAccelerationContainer(const Napi::CallbackInfo& info) : Napi::ObjectWrap(info) { 13 | Napi::Env env = info.Env(); 14 | 15 | this->device.Reset(info[0].As(), 1); 16 | GPUDevice* device = Napi::ObjectWrap::Unwrap(this->device.Value()); 17 | 18 | auto descriptor = DescriptorDecoder::GPURayTracingAccelerationContainerDescriptor(device, info[1].As()); 19 | this->instance = wgpuDeviceCreateRayTracingAccelerationContainer(device->instance, &descriptor); 20 | } 21 | 22 | GPURayTracingAccelerationContainer::~GPURayTracingAccelerationContainer() { 23 | this->device.Reset(); 24 | wgpuRayTracingAccelerationContainerRelease(this->instance); 25 | } 26 | 27 | Napi::Value GPURayTracingAccelerationContainer::destroy(const Napi::CallbackInfo &info) { 28 | Napi::Env env = info.Env(); 29 | wgpuRayTracingAccelerationContainerDestroy(this->instance); 30 | return env.Undefined(); 31 | } 32 | 33 | Napi::Value GPURayTracingAccelerationContainer::updateInstance(const Napi::CallbackInfo &info) { 34 | Napi::Env env = info.Env(); 35 | GPUDevice* device = Napi::ObjectWrap::Unwrap(this->device.Value()); 36 | 37 | uint32_t instanceIndex = info[0].As().Uint32Value(); 38 | auto descriptor = DescriptorDecoder::GPURayTracingAccelerationInstanceDescriptor(device, info[1].As()); 39 | 40 | wgpuRayTracingAccelerationContainerUpdateInstance(this->instance, instanceIndex, &descriptor); 41 | return env.Undefined(); 42 | } 43 | 44 | Napi::Object GPURayTracingAccelerationContainer::Initialize(Napi::Env env, Napi::Object exports) { 45 | Napi::HandleScope scope(env); 46 | Napi::Function func = DefineClass(env, "GPURayTracingAccelerationContainer", { 47 | InstanceMethod( 48 | "destroy", 49 | &GPURayTracingAccelerationContainer::destroy, 50 | napi_enumerable 51 | ), 52 | InstanceMethod( 53 | "updateInstance", 54 | &GPURayTracingAccelerationContainer::updateInstance, 55 | napi_enumerable 56 | ) 57 | }); 58 | constructor = Napi::Persistent(func); 59 | constructor.SuppressDestruct(); 60 | exports.Set("GPURayTracingAccelerationContainer", func); 61 | return exports; 62 | } 63 | -------------------------------------------------------------------------------- /src/GPURayTracingAccelerationContainer.h: -------------------------------------------------------------------------------- 1 | #ifndef __GPU_RAY_TRACING_ACCELERATION_CONTAINER_H__ 2 | #define __GPU_RAY_TRACING_ACCELERATION_CONTAINER_H__ 3 | 4 | #include "Base.h" 5 | 6 | class GPURayTracingAccelerationContainer : public Napi::ObjectWrap { 7 | 8 | public: 9 | 10 | static Napi::Object Initialize(Napi::Env env, Napi::Object exports); 11 | static Napi::FunctionReference constructor; 12 | 13 | GPURayTracingAccelerationContainer(const Napi::CallbackInfo &info); 14 | ~GPURayTracingAccelerationContainer(); 15 | 16 | Napi::Value destroy(const Napi::CallbackInfo &info); 17 | Napi::Value updateInstance(const Napi::CallbackInfo &info); 18 | 19 | Napi::ObjectReference device; 20 | 21 | WGPURayTracingAccelerationContainer instance; 22 | 23 | private: 24 | 25 | }; 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /src/GPURayTracingPassEncoder.cpp: -------------------------------------------------------------------------------- 1 | #include "GPURayTracingPassEncoder.h" 2 | #include "GPUDevice.h" 3 | #include "GPUCommandEncoder.h" 4 | #include "GPURayTracingPipeline.h" 5 | #include "GPUBindGroup.h" 6 | 7 | #include "DescriptorDecoder.h" 8 | 9 | Napi::FunctionReference GPURayTracingPassEncoder::constructor; 10 | 11 | GPURayTracingPassEncoder::GPURayTracingPassEncoder(const Napi::CallbackInfo& info) : Napi::ObjectWrap(info) { 12 | Napi::Env env = info.Env(); 13 | 14 | this->commandEncoder.Reset(info[0].As(), 1); 15 | GPUCommandEncoder* commandEncoder = Napi::ObjectWrap::Unwrap(this->commandEncoder.Value()); 16 | GPUDevice* device = Napi::ObjectWrap::Unwrap(commandEncoder->device.Value()); 17 | 18 | auto descriptor = DescriptorDecoder::GPURayTracingPassDescriptor(device, info[1].As()); 19 | 20 | this->instance = wgpuCommandEncoderBeginRayTracingPass(commandEncoder->instance, &descriptor); 21 | } 22 | 23 | GPURayTracingPassEncoder::~GPURayTracingPassEncoder() { 24 | this->device.Reset(); 25 | this->commandEncoder.Reset(); 26 | wgpuRayTracingPassEncoderRelease(this->instance); 27 | } 28 | 29 | Napi::Value GPURayTracingPassEncoder::setPipeline(const Napi::CallbackInfo &info) { 30 | Napi::Env env = info.Env(); 31 | 32 | GPURayTracingPipeline* rayTracingPipeline = Napi::ObjectWrap::Unwrap(info[0].As()); 33 | 34 | wgpuRayTracingPassEncoderSetPipeline(this->instance, rayTracingPipeline->instance); 35 | 36 | return env.Undefined(); 37 | } 38 | 39 | Napi::Value GPURayTracingPassEncoder::traceRays(const Napi::CallbackInfo &info) { 40 | Napi::Env env = info.Env(); 41 | 42 | uint32_t rayGenOffset = info[0].As().Uint32Value(); 43 | uint32_t rayHitOffset = info[1].As().Uint32Value(); 44 | uint32_t rayMissOffset = info[2].As().Uint32Value(); 45 | 46 | uint32_t width = info[3].As().Uint32Value(); 47 | uint32_t height = info[4].As().Uint32Value(); 48 | uint32_t depth = info[5].As().Uint32Value(); 49 | 50 | wgpuRayTracingPassEncoderTraceRays( 51 | this->instance, 52 | rayGenOffset, 53 | rayHitOffset, 54 | rayMissOffset, 55 | width, 56 | height, 57 | depth 58 | ); 59 | 60 | return env.Undefined(); 61 | } 62 | 63 | Napi::Value GPURayTracingPassEncoder::endPass(const Napi::CallbackInfo &info) { 64 | Napi::Env env = info.Env(); 65 | 66 | wgpuRayTracingPassEncoderEndPass(this->instance); 67 | 68 | return env.Undefined(); 69 | } 70 | 71 | Napi::Value GPURayTracingPassEncoder::setBindGroup(const Napi::CallbackInfo &info) { 72 | Napi::Env env = info.Env(); 73 | 74 | uint32_t groupIndex = info[0].As().Uint32Value(); 75 | 76 | WGPUBindGroup group = Napi::ObjectWrap::Unwrap(info[1].As())->instance; 77 | 78 | uint32_t dynamicOffsetCount = 0; 79 | std::vector dynamicOffsets; 80 | if (info[2].IsArray()) { 81 | Napi::Array array = info[2].As(); 82 | for (unsigned int ii = 0; ii < array.Length(); ++ii) { 83 | uint32_t offset = array.Get(ii).As().Uint32Value(); 84 | dynamicOffsets.push_back(offset); 85 | }; 86 | dynamicOffsetCount = array.Length(); 87 | } 88 | 89 | wgpuRayTracingPassEncoderSetBindGroup(this->instance, groupIndex, group, dynamicOffsetCount, dynamicOffsets.data()); 90 | 91 | return env.Undefined(); 92 | } 93 | 94 | Napi::Value GPURayTracingPassEncoder::pushDebugGroup(const Napi::CallbackInfo &info) { 95 | Napi::Env env = info.Env(); 96 | 97 | const char* groupLabel = info[0].As().Utf8Value().c_str(); 98 | wgpuRayTracingPassEncoderPushDebugGroup(this->instance, groupLabel); 99 | 100 | return env.Undefined(); 101 | } 102 | 103 | Napi::Value GPURayTracingPassEncoder::popDebugGroup(const Napi::CallbackInfo &info) { 104 | Napi::Env env = info.Env(); 105 | 106 | wgpuRayTracingPassEncoderPopDebugGroup(this->instance); 107 | 108 | return env.Undefined(); 109 | } 110 | 111 | Napi::Value GPURayTracingPassEncoder::insertDebugMarker(const Napi::CallbackInfo &info) { 112 | Napi::Env env = info.Env(); 113 | 114 | const char* groupLabel = info[0].As().Utf8Value().c_str(); 115 | wgpuRayTracingPassEncoderInsertDebugMarker(this->instance, groupLabel); 116 | 117 | return env.Undefined(); 118 | } 119 | 120 | Napi::Object GPURayTracingPassEncoder::Initialize(Napi::Env env, Napi::Object exports) { 121 | Napi::HandleScope scope(env); 122 | Napi::Function func = DefineClass(env, "GPURayTracingPassEncoder", { 123 | InstanceMethod( 124 | "setPipeline", 125 | &GPURayTracingPassEncoder::setPipeline, 126 | napi_enumerable 127 | ), 128 | InstanceMethod( 129 | "traceRays", 130 | &GPURayTracingPassEncoder::traceRays, 131 | napi_enumerable 132 | ), 133 | InstanceMethod( 134 | "endPass", 135 | &GPURayTracingPassEncoder::endPass, 136 | napi_enumerable 137 | ), 138 | InstanceMethod( 139 | "setBindGroup", 140 | &GPURayTracingPassEncoder::setBindGroup, 141 | napi_enumerable 142 | ), 143 | InstanceMethod( 144 | "pushDebugGroup", 145 | &GPURayTracingPassEncoder::pushDebugGroup, 146 | napi_enumerable 147 | ), 148 | InstanceMethod( 149 | "popDebugGroup", 150 | &GPURayTracingPassEncoder::popDebugGroup, 151 | napi_enumerable 152 | ), 153 | InstanceMethod( 154 | "insertDebugMarker", 155 | &GPURayTracingPassEncoder::insertDebugMarker, 156 | napi_enumerable 157 | ), 158 | }); 159 | constructor = Napi::Persistent(func); 160 | constructor.SuppressDestruct(); 161 | exports.Set("GPURayTracingPassEncoder", func); 162 | return exports; 163 | } 164 | -------------------------------------------------------------------------------- /src/GPURayTracingPassEncoder.h: -------------------------------------------------------------------------------- 1 | #ifndef __GPU_RAY_TRACING_PASS_ENCODER_H__ 2 | #define __GPU_RAY_TRACING_PASS_ENCODER_H__ 3 | 4 | #include "Base.h" 5 | 6 | class GPURayTracingPassEncoder : public Napi::ObjectWrap { 7 | 8 | public: 9 | 10 | static Napi::Object Initialize(Napi::Env env, Napi::Object exports); 11 | static Napi::FunctionReference constructor; 12 | 13 | GPURayTracingPassEncoder(const Napi::CallbackInfo &info); 14 | ~GPURayTracingPassEncoder(); 15 | 16 | // GPURenderEncoderBase BEGIN 17 | Napi::Value setPipeline(const Napi::CallbackInfo &info); 18 | Napi::Value traceRays(const Napi::CallbackInfo &info); 19 | Napi::Value endPass(const Napi::CallbackInfo &info); 20 | // GPURenderEncoderBase END 21 | 22 | // GPUProgrammablePassEncoder BEGIN 23 | Napi::Value setBindGroup(const Napi::CallbackInfo &info); 24 | Napi::Value pushDebugGroup(const Napi::CallbackInfo &info); 25 | Napi::Value popDebugGroup(const Napi::CallbackInfo &info); 26 | Napi::Value insertDebugMarker(const Napi::CallbackInfo &info); 27 | // GPUProgrammablePassEncoder END 28 | 29 | Napi::ObjectReference device; 30 | Napi::ObjectReference commandEncoder; 31 | 32 | WGPURayTracingPassEncoder instance; 33 | private: 34 | 35 | }; 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /src/GPURayTracingPipeline.cpp: -------------------------------------------------------------------------------- 1 | #include "GPURayTracingPipeline.h" 2 | #include "GPUDevice.h" 3 | #include "GPUShaderModule.h" 4 | 5 | #include "DescriptorDecoder.h" 6 | 7 | Napi::FunctionReference GPURayTracingPipeline::constructor; 8 | 9 | GPURayTracingPipeline::GPURayTracingPipeline(const Napi::CallbackInfo& info) : Napi::ObjectWrap(info) { 10 | Napi::Env env = info.Env(); 11 | 12 | this->device.Reset(info[0].As(), 1); 13 | GPUDevice* device = Napi::ObjectWrap::Unwrap(this->device.Value()); 14 | 15 | auto descriptor = DescriptorDecoder::GPURayTracingPipelineDescriptor(device, info[1].As()); 16 | 17 | this->instance = wgpuDeviceCreateRayTracingPipeline(device->instance, &descriptor); 18 | } 19 | 20 | GPURayTracingPipeline::~GPURayTracingPipeline() { 21 | this->device.Reset(); 22 | wgpuRayTracingPipelineRelease(this->instance); 23 | } 24 | 25 | Napi::Object GPURayTracingPipeline::Initialize(Napi::Env env, Napi::Object exports) { 26 | Napi::HandleScope scope(env); 27 | Napi::Function func = DefineClass(env, "GPURayTracingPipeline", { 28 | 29 | }); 30 | constructor = Napi::Persistent(func); 31 | constructor.SuppressDestruct(); 32 | exports.Set("GPURayTracingPipeline", func); 33 | return exports; 34 | } 35 | -------------------------------------------------------------------------------- /src/GPURayTracingPipeline.h: -------------------------------------------------------------------------------- 1 | #ifndef __GPU_RAY_TRACING_PIPELINE_H__ 2 | #define __GPU_RAY_TRACING_PIPELINE_H__ 3 | 4 | #include "Base.h" 5 | 6 | class GPURayTracingPipeline : public Napi::ObjectWrap { 7 | 8 | public: 9 | 10 | static Napi::Object Initialize(Napi::Env env, Napi::Object exports); 11 | static Napi::FunctionReference constructor; 12 | 13 | GPURayTracingPipeline(const Napi::CallbackInfo &info); 14 | ~GPURayTracingPipeline(); 15 | 16 | Napi::ObjectReference device; 17 | 18 | WGPURayTracingPipeline instance; 19 | private: 20 | 21 | }; 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /src/GPURayTracingShaderBindingTable.cpp: -------------------------------------------------------------------------------- 1 | #include "GPURayTracingShaderBindingTable.h" 2 | #include "GPUDevice.h" 3 | 4 | #include "DescriptorDecoder.h" 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | Napi::FunctionReference GPURayTracingShaderBindingTable::constructor; 11 | 12 | GPURayTracingShaderBindingTable::GPURayTracingShaderBindingTable(const Napi::CallbackInfo& info) : Napi::ObjectWrap(info) { 13 | Napi::Env env = info.Env(); 14 | 15 | this->device.Reset(info[0].As(), 1); 16 | GPUDevice* device = Napi::ObjectWrap::Unwrap(this->device.Value()); 17 | 18 | auto descriptor = DescriptorDecoder::GPURayTracingShaderBindingTableDescriptor(device, info[1].As()); 19 | this->instance = wgpuDeviceCreateRayTracingShaderBindingTable(device->instance, &descriptor); 20 | } 21 | 22 | GPURayTracingShaderBindingTable::~GPURayTracingShaderBindingTable() { 23 | this->device.Reset(); 24 | wgpuRayTracingShaderBindingTableRelease(this->instance); 25 | } 26 | 27 | Napi::Value GPURayTracingShaderBindingTable::destroy(const Napi::CallbackInfo &info) { 28 | Napi::Env env = info.Env(); 29 | wgpuRayTracingShaderBindingTableDestroy(this->instance); 30 | return env.Undefined(); 31 | } 32 | 33 | Napi::Object GPURayTracingShaderBindingTable::Initialize(Napi::Env env, Napi::Object exports) { 34 | Napi::HandleScope scope(env); 35 | Napi::Function func = DefineClass(env, "GPURayTracingShaderBindingTable", { 36 | InstanceMethod( 37 | "destroy", 38 | &GPURayTracingShaderBindingTable::destroy, 39 | napi_enumerable 40 | ), 41 | }); 42 | constructor = Napi::Persistent(func); 43 | constructor.SuppressDestruct(); 44 | exports.Set("GPURayTracingShaderBindingTable", func); 45 | return exports; 46 | } 47 | -------------------------------------------------------------------------------- /src/GPURayTracingShaderBindingTable.h: -------------------------------------------------------------------------------- 1 | #ifndef __GPU_RAY_TRACING_SHADER_BINDING_TABLE_H__ 2 | #define __GPU_RAY_TRACING_SHADER_BINDING_TABLE_H__ 3 | 4 | #include "Base.h" 5 | 6 | class GPURayTracingShaderBindingTable : public Napi::ObjectWrap { 7 | 8 | public: 9 | 10 | static Napi::Object Initialize(Napi::Env env, Napi::Object exports); 11 | static Napi::FunctionReference constructor; 12 | 13 | GPURayTracingShaderBindingTable(const Napi::CallbackInfo &info); 14 | ~GPURayTracingShaderBindingTable(); 15 | 16 | Napi::Value destroy(const Napi::CallbackInfo &info); 17 | 18 | Napi::ObjectReference device; 19 | 20 | WGPURayTracingShaderBindingTable instance; 21 | 22 | private: 23 | 24 | }; 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /src/GPURenderBundle.cpp: -------------------------------------------------------------------------------- 1 | #include "GPURenderBundle.h" 2 | 3 | Napi::FunctionReference GPURenderBundle::constructor; 4 | 5 | GPURenderBundle::GPURenderBundle(const Napi::CallbackInfo& info) : Napi::ObjectWrap(info) { 6 | 7 | } 8 | 9 | GPURenderBundle::~GPURenderBundle() { 10 | wgpuRenderBundleRelease(this->instance); 11 | } 12 | 13 | Napi::Object GPURenderBundle::Initialize(Napi::Env env, Napi::Object exports) { 14 | Napi::HandleScope scope(env); 15 | Napi::Function func = DefineClass(env, "GPURenderBundle", { 16 | 17 | }); 18 | constructor = Napi::Persistent(func); 19 | constructor.SuppressDestruct(); 20 | exports.Set("GPURenderBundle", func); 21 | return exports; 22 | } 23 | -------------------------------------------------------------------------------- /src/GPURenderBundle.h: -------------------------------------------------------------------------------- 1 | #ifndef __GPU_RENDER_BUNDLE_H__ 2 | #define __GPU_RENDER_BUNDLE_H__ 3 | 4 | #include "Base.h" 5 | 6 | class GPURenderBundle : public Napi::ObjectWrap { 7 | 8 | public: 9 | 10 | static Napi::Object Initialize(Napi::Env env, Napi::Object exports); 11 | static Napi::FunctionReference constructor; 12 | 13 | GPURenderBundle(const Napi::CallbackInfo &info); 14 | ~GPURenderBundle(); 15 | 16 | WGPURenderBundle instance; 17 | private: 18 | 19 | }; 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /src/GPURenderBundleEncoder.cpp: -------------------------------------------------------------------------------- 1 | #include "GPURenderBundleEncoder.h" 2 | #include "GPUDevice.h" 3 | #include "GPUCommandEncoder.h" 4 | #include "GPURenderBundle.h" 5 | #include "GPUBuffer.h" 6 | #include "GPUBindGroup.h" 7 | 8 | #include "DescriptorDecoder.h" 9 | 10 | Napi::FunctionReference GPURenderBundleEncoder::constructor; 11 | 12 | GPURenderBundleEncoder::GPURenderBundleEncoder(const Napi::CallbackInfo& info) : Napi::ObjectWrap(info) { 13 | Napi::Env env = info.Env(); 14 | 15 | this->commandEncoder.Reset(info[0].As(), 1); 16 | GPUCommandEncoder* commandEncoder = Napi::ObjectWrap::Unwrap(this->commandEncoder.Value()); 17 | GPUDevice* device = Napi::ObjectWrap::Unwrap(commandEncoder->device.Value()); 18 | 19 | auto descriptor = DescriptorDecoder::GPURenderBundleEncoderDescriptor(device, info[1].As()); 20 | 21 | this->instance = wgpuDeviceCreateRenderBundleEncoder(device->instance, &descriptor); 22 | } 23 | 24 | GPURenderBundleEncoder::~GPURenderBundleEncoder() { 25 | this->device.Reset(); 26 | this->commandEncoder.Reset(); 27 | wgpuRenderBundleEncoderRelease(this->instance); 28 | } 29 | 30 | Napi::Value GPURenderBundleEncoder::setPipeline(const Napi::CallbackInfo &info) { 31 | Napi::Env env = info.Env(); 32 | 33 | GPURenderPipeline* renderPipeline = Napi::ObjectWrap::Unwrap(info[0].As()); 34 | 35 | wgpuRenderBundleEncoderSetPipeline(this->instance, renderPipeline->instance); 36 | 37 | return env.Undefined(); 38 | } 39 | 40 | Napi::Value GPURenderBundleEncoder::setIndexBuffer(const Napi::CallbackInfo &info) { 41 | Napi::Env env = info.Env(); 42 | 43 | GPUBuffer* buffer = Napi::ObjectWrap::Unwrap(info[0].As()); 44 | uint64_t offset = 0; 45 | if (info[1].IsNumber()) { 46 | offset = info[1].As().Uint32Value(); 47 | } 48 | uint64_t size = 0; 49 | if (info[2].IsNumber()) { 50 | size = info[2].As().Uint32Value(); 51 | } 52 | 53 | wgpuRenderBundleEncoderSetIndexBuffer(this->instance, buffer->instance, offset, size); 54 | 55 | return env.Undefined(); 56 | } 57 | 58 | Napi::Value GPURenderBundleEncoder::setVertexBuffer(const Napi::CallbackInfo &info) { 59 | Napi::Env env = info.Env(); 60 | 61 | uint32_t startSlot = info[0].As().Uint32Value(); 62 | WGPUBuffer buffer = Napi::ObjectWrap::Unwrap(info[1].As())->instance; 63 | uint32_t offset = 0; 64 | if (info[2].IsNumber()) { 65 | offset = info[2].As().Uint32Value(); 66 | } 67 | uint64_t size = 0; 68 | if (info[3].IsNumber()) { 69 | size = info[3].As().Uint32Value(); 70 | } 71 | 72 | wgpuRenderBundleEncoderSetVertexBuffer(this->instance, startSlot, buffer, offset, size); 73 | 74 | return env.Undefined(); 75 | } 76 | 77 | Napi::Value GPURenderBundleEncoder::draw(const Napi::CallbackInfo &info) { 78 | Napi::Env env = info.Env(); 79 | 80 | uint32_t vertexCount = info[0].As().Uint32Value(); 81 | uint32_t instanceCount = info[1].As().Uint32Value(); 82 | uint32_t firstVertex = info[2].As().Uint32Value(); 83 | uint32_t firstInstance = info[3].As().Uint32Value(); 84 | 85 | wgpuRenderBundleEncoderDraw(this->instance, vertexCount, instanceCount, firstVertex, firstInstance); 86 | 87 | return env.Undefined(); 88 | } 89 | 90 | Napi::Value GPURenderBundleEncoder::drawIndexed(const Napi::CallbackInfo &info) { 91 | Napi::Env env = info.Env(); 92 | 93 | uint32_t indexCount = info[0].As().Uint32Value(); 94 | uint32_t instanceCount = info[1].As().Uint32Value(); 95 | uint32_t firstIndex = info[2].As().Uint32Value(); 96 | int32_t baseVertex = info[3].As().Int32Value(); 97 | uint32_t firstInstance = info[4].As().Uint32Value(); 98 | 99 | wgpuRenderBundleEncoderDrawIndexed(this->instance, indexCount, instanceCount, firstIndex, baseVertex, firstInstance); 100 | 101 | return env.Undefined(); 102 | } 103 | 104 | Napi::Value GPURenderBundleEncoder::drawIndirect(const Napi::CallbackInfo &info) { 105 | Napi::Env env = info.Env(); 106 | 107 | GPUBuffer* indirectBuffer = Napi::ObjectWrap::Unwrap(info[0].As()); 108 | uint64_t indirectOffset = static_cast(info[1].As().Uint32Value()); 109 | 110 | wgpuRenderBundleEncoderDrawIndirect(this->instance, indirectBuffer->instance, indirectOffset); 111 | 112 | return env.Undefined(); 113 | } 114 | 115 | Napi::Value GPURenderBundleEncoder::drawIndexedIndirect(const Napi::CallbackInfo &info) { 116 | Napi::Env env = info.Env(); 117 | 118 | GPUBuffer* indirectBuffer = Napi::ObjectWrap::Unwrap(info[0].As()); 119 | uint64_t indirectOffset = static_cast(info[1].As().Uint32Value()); 120 | 121 | wgpuRenderBundleEncoderDrawIndexedIndirect(this->instance, indirectBuffer->instance, indirectOffset); 122 | 123 | return env.Undefined(); 124 | } 125 | 126 | Napi::Value GPURenderBundleEncoder::finish(const Napi::CallbackInfo &info) { 127 | Napi::Env env = info.Env(); 128 | 129 | GPUCommandEncoder* commandEncoder = Napi::ObjectWrap::Unwrap(this->commandEncoder.Value()); 130 | GPUDevice* device = Napi::ObjectWrap::Unwrap(commandEncoder->device.Value()); 131 | 132 | auto descriptor = DescriptorDecoder::GPURenderBundleDescriptor(device, info[1].As()); 133 | 134 | wgpuRenderBundleEncoderFinish(this->instance, &descriptor); 135 | 136 | return env.Undefined(); 137 | } 138 | 139 | Napi::Value GPURenderBundleEncoder::setBindGroup(const Napi::CallbackInfo &info) { 140 | Napi::Env env = info.Env(); 141 | 142 | uint32_t groupIndex = info[0].As().Uint32Value(); 143 | 144 | WGPUBindGroup group = Napi::ObjectWrap::Unwrap(info[1].As())->instance; 145 | 146 | uint32_t dynamicOffsetCount = 0; 147 | std::vector dynamicOffsets; 148 | if (info[2].IsArray()) { 149 | Napi::Array array = info[2].As(); 150 | for (unsigned int ii = 0; ii < array.Length(); ++ii) { 151 | uint32_t offset = array.Get(ii).As().Uint32Value(); 152 | dynamicOffsets.push_back(offset); 153 | }; 154 | dynamicOffsetCount = array.Length(); 155 | } 156 | 157 | wgpuRenderBundleEncoderSetBindGroup(this->instance, groupIndex, group, dynamicOffsetCount, dynamicOffsets.data()); 158 | 159 | return env.Undefined(); 160 | } 161 | 162 | Napi::Value GPURenderBundleEncoder::pushDebugGroup(const Napi::CallbackInfo &info) { 163 | Napi::Env env = info.Env(); 164 | 165 | const char* groupLabel = info[0].As().Utf8Value().c_str(); 166 | wgpuRenderBundleEncoderPushDebugGroup(this->instance, groupLabel); 167 | 168 | return env.Undefined(); 169 | } 170 | 171 | Napi::Value GPURenderBundleEncoder::popDebugGroup(const Napi::CallbackInfo &info) { 172 | Napi::Env env = info.Env(); 173 | 174 | wgpuRenderBundleEncoderPopDebugGroup(this->instance); 175 | 176 | return env.Undefined(); 177 | } 178 | 179 | Napi::Value GPURenderBundleEncoder::insertDebugMarker(const Napi::CallbackInfo &info) { 180 | Napi::Env env = info.Env(); 181 | 182 | const char* groupLabel = info[0].As().Utf8Value().c_str(); 183 | wgpuRenderBundleEncoderInsertDebugMarker(this->instance, groupLabel); 184 | 185 | return env.Undefined(); 186 | } 187 | 188 | Napi::Object GPURenderBundleEncoder::Initialize(Napi::Env env, Napi::Object exports) { 189 | Napi::HandleScope scope(env); 190 | Napi::Function func = DefineClass(env, "GPURenderBundleEncoder", { 191 | InstanceMethod( 192 | "setPipeline", 193 | &GPURenderBundleEncoder::setPipeline, 194 | napi_enumerable 195 | ), 196 | InstanceMethod( 197 | "setIndexBuffer", 198 | &GPURenderBundleEncoder::setIndexBuffer, 199 | napi_enumerable 200 | ), 201 | InstanceMethod( 202 | "setVertexBuffer", 203 | &GPURenderBundleEncoder::setVertexBuffer, 204 | napi_enumerable 205 | ), 206 | InstanceMethod( 207 | "draw", 208 | &GPURenderBundleEncoder::draw, 209 | napi_enumerable 210 | ), 211 | InstanceMethod( 212 | "drawIndexed", 213 | &GPURenderBundleEncoder::drawIndexed, 214 | napi_enumerable 215 | ), 216 | InstanceMethod( 217 | "drawIndirect", 218 | &GPURenderBundleEncoder::drawIndirect, 219 | napi_enumerable 220 | ), 221 | InstanceMethod( 222 | "drawIndexedIndirect", 223 | &GPURenderBundleEncoder::drawIndexedIndirect, 224 | napi_enumerable 225 | ), 226 | InstanceMethod( 227 | "finish", 228 | &GPURenderBundleEncoder::finish, 229 | napi_enumerable 230 | ), 231 | InstanceMethod( 232 | "setBindGroup", 233 | &GPURenderBundleEncoder::setBindGroup, 234 | napi_enumerable 235 | ), 236 | InstanceMethod( 237 | "pushDebugGroup", 238 | &GPURenderBundleEncoder::pushDebugGroup, 239 | napi_enumerable 240 | ), 241 | InstanceMethod( 242 | "popDebugGroup", 243 | &GPURenderBundleEncoder::popDebugGroup, 244 | napi_enumerable 245 | ), 246 | InstanceMethod( 247 | "insertDebugMarker", 248 | &GPURenderBundleEncoder::insertDebugMarker, 249 | napi_enumerable 250 | ), 251 | }); 252 | constructor = Napi::Persistent(func); 253 | constructor.SuppressDestruct(); 254 | exports.Set("GPURenderBundleEncoder", func); 255 | return exports; 256 | } 257 | -------------------------------------------------------------------------------- /src/GPURenderBundleEncoder.h: -------------------------------------------------------------------------------- 1 | #ifndef __GPU_RENDER_BUNDLE_ENCODER_H__ 2 | #define __GPU_RENDER_BUNDLE_ENCODER_H__ 3 | 4 | #include "Base.h" 5 | 6 | class GPURenderBundleEncoder : public Napi::ObjectWrap { 7 | 8 | public: 9 | 10 | static Napi::Object Initialize(Napi::Env env, Napi::Object exports); 11 | static Napi::FunctionReference constructor; 12 | 13 | GPURenderBundleEncoder(const Napi::CallbackInfo &info); 14 | ~GPURenderBundleEncoder(); 15 | 16 | // GPURenderEncoderBase BEGIN 17 | Napi::Value setPipeline(const Napi::CallbackInfo &info); 18 | Napi::Value setIndexBuffer(const Napi::CallbackInfo &info); 19 | Napi::Value setVertexBuffer(const Napi::CallbackInfo &info); 20 | Napi::Value draw(const Napi::CallbackInfo &info); 21 | Napi::Value drawIndexed(const Napi::CallbackInfo &info); 22 | Napi::Value drawIndirect(const Napi::CallbackInfo &info); 23 | Napi::Value drawIndexedIndirect(const Napi::CallbackInfo &info); 24 | // GPURenderEncoderBase END 25 | 26 | // GPURenderBundleEncoder BEGIN 27 | Napi::Value finish(const Napi::CallbackInfo &info); 28 | // GPURenderBundleEncoder END 29 | 30 | // GPUProgrammablePassEncoder BEGIN 31 | Napi::Value setBindGroup(const Napi::CallbackInfo &info); 32 | Napi::Value pushDebugGroup(const Napi::CallbackInfo &info); 33 | Napi::Value popDebugGroup(const Napi::CallbackInfo &info); 34 | Napi::Value insertDebugMarker(const Napi::CallbackInfo &info); 35 | // GPUProgrammablePassEncoder END 36 | 37 | Napi::ObjectReference device; 38 | Napi::ObjectReference commandEncoder; 39 | 40 | WGPURenderBundleEncoder instance; 41 | private: 42 | 43 | }; 44 | 45 | #endif 46 | -------------------------------------------------------------------------------- /src/GPURenderPassEncoder.cpp: -------------------------------------------------------------------------------- 1 | #include "GPURenderPassEncoder.h" 2 | #include "GPUDevice.h" 3 | #include "GPUCommandEncoder.h" 4 | #include "GPURenderPipeline.h" 5 | #include "GPUBuffer.h" 6 | #include "GPUBindGroup.h" 7 | 8 | #include "DescriptorDecoder.h" 9 | 10 | Napi::FunctionReference GPURenderPassEncoder::constructor; 11 | 12 | GPURenderPassEncoder::GPURenderPassEncoder(const Napi::CallbackInfo& info) : Napi::ObjectWrap(info) { 13 | Napi::Env env = info.Env(); 14 | 15 | this->commandEncoder.Reset(info[0].As(), 1); 16 | GPUCommandEncoder* commandEncoder = Napi::ObjectWrap::Unwrap(this->commandEncoder.Value()); 17 | GPUDevice* device = Napi::ObjectWrap::Unwrap(commandEncoder->device.Value()); 18 | 19 | auto descriptor = DescriptorDecoder::GPURenderPassDescriptor(device, info[1].As()); 20 | 21 | this->instance = wgpuCommandEncoderBeginRenderPass(commandEncoder->instance, &descriptor); 22 | } 23 | 24 | GPURenderPassEncoder::~GPURenderPassEncoder() { 25 | this->device.Reset(); 26 | this->commandEncoder.Reset(); 27 | wgpuRenderPassEncoderRelease(this->instance); 28 | } 29 | 30 | Napi::Value GPURenderPassEncoder::setPipeline(const Napi::CallbackInfo &info) { 31 | Napi::Env env = info.Env(); 32 | 33 | GPURenderPipeline* renderPipeline = Napi::ObjectWrap::Unwrap(info[0].As()); 34 | 35 | wgpuRenderPassEncoderSetPipeline(this->instance, renderPipeline->instance); 36 | 37 | return env.Undefined(); 38 | } 39 | 40 | Napi::Value GPURenderPassEncoder::setIndexBuffer(const Napi::CallbackInfo &info) { 41 | Napi::Env env = info.Env(); 42 | 43 | GPUBuffer* buffer = Napi::ObjectWrap::Unwrap(info[0].As()); 44 | uint64_t offset = 0; 45 | if (info[1].IsNumber()) { 46 | offset = static_cast(info[1].As().Uint32Value()); 47 | } 48 | uint64_t size = 0; 49 | if (info[2].IsNumber()) { 50 | size = static_cast(info[2].As().Uint32Value()); 51 | } 52 | 53 | wgpuRenderPassEncoderSetIndexBuffer(this->instance, buffer->instance, offset, size); 54 | 55 | return env.Undefined(); 56 | } 57 | 58 | Napi::Value GPURenderPassEncoder::setVertexBuffer(const Napi::CallbackInfo &info) { 59 | Napi::Env env = info.Env(); 60 | 61 | uint32_t startSlot = info[0].As().Uint32Value(); 62 | WGPUBuffer buffer = Napi::ObjectWrap::Unwrap(info[1].As())->instance; 63 | uint32_t offset = 0; 64 | if (info[2].IsNumber()) { 65 | offset = info[2].As().Uint32Value(); 66 | } 67 | uint64_t size = 0; 68 | if (info[3].IsNumber()) { 69 | size = static_cast(info[3].As().Uint32Value()); 70 | } 71 | 72 | wgpuRenderPassEncoderSetVertexBuffer(this->instance, startSlot, buffer, offset, size); 73 | 74 | return env.Undefined(); 75 | } 76 | 77 | Napi::Value GPURenderPassEncoder::draw(const Napi::CallbackInfo &info) { 78 | Napi::Env env = info.Env(); 79 | 80 | uint32_t vertexCount = info[0].As().Uint32Value(); 81 | uint32_t instanceCount = info[1].As().Uint32Value(); 82 | uint32_t firstVertex = info[2].As().Uint32Value(); 83 | uint32_t firstInstance = info[3].As().Uint32Value(); 84 | 85 | wgpuRenderPassEncoderDraw(this->instance, vertexCount, instanceCount, firstVertex, firstInstance); 86 | 87 | return env.Undefined(); 88 | } 89 | 90 | Napi::Value GPURenderPassEncoder::drawIndexed(const Napi::CallbackInfo &info) { 91 | Napi::Env env = info.Env(); 92 | 93 | uint32_t indexCount = info[0].As().Uint32Value(); 94 | uint32_t instanceCount = info[1].As().Uint32Value(); 95 | uint32_t firstIndex = info[2].As().Uint32Value(); 96 | int32_t baseVertex = info[3].As().Int32Value(); 97 | uint32_t firstInstance = info[4].As().Uint32Value(); 98 | 99 | wgpuRenderPassEncoderDrawIndexed(this->instance, indexCount, instanceCount, firstIndex, baseVertex, firstInstance); 100 | 101 | return env.Undefined(); 102 | } 103 | 104 | Napi::Value GPURenderPassEncoder::drawIndirect(const Napi::CallbackInfo &info) { 105 | Napi::Env env = info.Env(); 106 | 107 | GPUBuffer* indirectBuffer = Napi::ObjectWrap::Unwrap(info[0].As()); 108 | uint64_t indirectOffset = static_cast(info[1].As().Uint32Value()); 109 | 110 | wgpuRenderPassEncoderDrawIndirect(this->instance, indirectBuffer->instance, indirectOffset); 111 | 112 | return env.Undefined(); 113 | } 114 | 115 | Napi::Value GPURenderPassEncoder::drawIndexedIndirect(const Napi::CallbackInfo &info) { 116 | Napi::Env env = info.Env(); 117 | 118 | GPUBuffer* indirectBuffer = Napi::ObjectWrap::Unwrap(info[0].As()); 119 | uint64_t indirectOffset = static_cast(info[1].As().Uint32Value()); 120 | 121 | wgpuRenderPassEncoderDrawIndexedIndirect(this->instance, indirectBuffer->instance, indirectOffset); 122 | 123 | return env.Undefined(); 124 | } 125 | 126 | Napi::Value GPURenderPassEncoder::setViewport(const Napi::CallbackInfo &info) { 127 | Napi::Env env = info.Env(); 128 | 129 | float x = info[0].As().FloatValue(); 130 | float y = info[1].As().FloatValue(); 131 | float width = info[2].As().FloatValue(); 132 | float height = info[3].As().FloatValue(); 133 | float minDepth = info[4].As().FloatValue(); 134 | float maxDepth = info[5].As().FloatValue(); 135 | 136 | wgpuRenderPassEncoderSetViewport(this->instance, x, y, width, height, minDepth, maxDepth); 137 | 138 | return env.Undefined(); 139 | } 140 | 141 | Napi::Value GPURenderPassEncoder::setScissorRect(const Napi::CallbackInfo &info) { 142 | Napi::Env env = info.Env(); 143 | 144 | uint32_t x = info[0].As().Uint32Value(); 145 | uint32_t y = info[1].As().Uint32Value(); 146 | uint32_t width = info[2].As().Uint32Value(); 147 | uint32_t height = info[3].As().Uint32Value(); 148 | 149 | wgpuRenderPassEncoderSetScissorRect(this->instance, x, y, width, height); 150 | 151 | return env.Undefined(); 152 | } 153 | 154 | Napi::Value GPURenderPassEncoder::setBlendColor(const Napi::CallbackInfo &info) { 155 | Napi::Env env = info.Env(); 156 | 157 | GPUCommandEncoder* commandEncoder = Napi::ObjectWrap::Unwrap(this->commandEncoder.Value()); 158 | GPUDevice* device = Napi::ObjectWrap::Unwrap(commandEncoder->device.Value()); 159 | 160 | auto color = DescriptorDecoder::GPUColor(device, info[0].As()); 161 | 162 | wgpuRenderPassEncoderSetBlendColor(this->instance, &color); 163 | 164 | return env.Undefined(); 165 | } 166 | 167 | Napi::Value GPURenderPassEncoder::setStencilReference(const Napi::CallbackInfo &info) { 168 | Napi::Env env = info.Env(); 169 | 170 | uint32_t reference = info[0].As().Uint32Value(); 171 | 172 | wgpuRenderPassEncoderSetStencilReference(this->instance, reference); 173 | 174 | return env.Undefined(); 175 | } 176 | 177 | Napi::Value GPURenderPassEncoder::executeBundles(const Napi::CallbackInfo &info) { 178 | Napi::Env env = info.Env(); 179 | // TODO 180 | return env.Undefined(); 181 | } 182 | 183 | Napi::Value GPURenderPassEncoder::endPass(const Napi::CallbackInfo &info) { 184 | Napi::Env env = info.Env(); 185 | 186 | wgpuRenderPassEncoderEndPass(this->instance); 187 | 188 | return env.Undefined(); 189 | } 190 | 191 | Napi::Value GPURenderPassEncoder::setBindGroup(const Napi::CallbackInfo &info) { 192 | Napi::Env env = info.Env(); 193 | 194 | uint32_t groupIndex = info[0].As().Uint32Value(); 195 | 196 | WGPUBindGroup group = Napi::ObjectWrap::Unwrap(info[1].As())->instance; 197 | 198 | uint32_t dynamicOffsetCount = 0; 199 | std::vector dynamicOffsets; 200 | if (info[2].IsArray()) { 201 | Napi::Array array = info[2].As(); 202 | for (unsigned int ii = 0; ii < array.Length(); ++ii) { 203 | uint32_t offset = array.Get(ii).As().Uint32Value(); 204 | dynamicOffsets.push_back(offset); 205 | }; 206 | dynamicOffsetCount = array.Length(); 207 | } 208 | 209 | wgpuRenderPassEncoderSetBindGroup(this->instance, groupIndex, group, dynamicOffsetCount, dynamicOffsets.data()); 210 | 211 | return env.Undefined(); 212 | } 213 | 214 | Napi::Value GPURenderPassEncoder::pushDebugGroup(const Napi::CallbackInfo &info) { 215 | Napi::Env env = info.Env(); 216 | 217 | const char* groupLabel = info[0].As().Utf8Value().c_str(); 218 | wgpuRenderPassEncoderPushDebugGroup(this->instance, groupLabel); 219 | 220 | return env.Undefined(); 221 | } 222 | 223 | Napi::Value GPURenderPassEncoder::popDebugGroup(const Napi::CallbackInfo &info) { 224 | Napi::Env env = info.Env(); 225 | 226 | wgpuRenderPassEncoderPopDebugGroup(this->instance); 227 | 228 | return env.Undefined(); 229 | } 230 | 231 | Napi::Value GPURenderPassEncoder::insertDebugMarker(const Napi::CallbackInfo &info) { 232 | Napi::Env env = info.Env(); 233 | 234 | const char* groupLabel = info[0].As().Utf8Value().c_str(); 235 | wgpuRenderPassEncoderInsertDebugMarker(this->instance, groupLabel); 236 | 237 | return env.Undefined(); 238 | } 239 | 240 | Napi::Object GPURenderPassEncoder::Initialize(Napi::Env env, Napi::Object exports) { 241 | Napi::HandleScope scope(env); 242 | Napi::Function func = DefineClass(env, "GPURenderPassEncoder", { 243 | InstanceMethod( 244 | "setPipeline", 245 | &GPURenderPassEncoder::setPipeline, 246 | napi_enumerable 247 | ), 248 | InstanceMethod( 249 | "setIndexBuffer", 250 | &GPURenderPassEncoder::setIndexBuffer, 251 | napi_enumerable 252 | ), 253 | InstanceMethod( 254 | "setVertexBuffer", 255 | &GPURenderPassEncoder::setVertexBuffer, 256 | napi_enumerable 257 | ), 258 | InstanceMethod( 259 | "draw", 260 | &GPURenderPassEncoder::draw, 261 | napi_enumerable 262 | ), 263 | InstanceMethod( 264 | "drawIndexed", 265 | &GPURenderPassEncoder::drawIndexed, 266 | napi_enumerable 267 | ), 268 | InstanceMethod( 269 | "drawIndirect", 270 | &GPURenderPassEncoder::drawIndirect, 271 | napi_enumerable 272 | ), 273 | InstanceMethod( 274 | "drawIndexedIndirect", 275 | &GPURenderPassEncoder::drawIndexedIndirect, 276 | napi_enumerable 277 | ), 278 | InstanceMethod( 279 | "setViewport", 280 | &GPURenderPassEncoder::setViewport, 281 | napi_enumerable 282 | ), 283 | InstanceMethod( 284 | "setScissorRect", 285 | &GPURenderPassEncoder::setScissorRect, 286 | napi_enumerable 287 | ), 288 | InstanceMethod( 289 | "setBlendColor", 290 | &GPURenderPassEncoder::setBlendColor, 291 | napi_enumerable 292 | ), 293 | InstanceMethod( 294 | "setStencilReference", 295 | &GPURenderPassEncoder::setStencilReference, 296 | napi_enumerable 297 | ), 298 | InstanceMethod( 299 | "executeBundles", 300 | &GPURenderPassEncoder::executeBundles, 301 | napi_enumerable 302 | ), 303 | InstanceMethod( 304 | "endPass", 305 | &GPURenderPassEncoder::endPass, 306 | napi_enumerable 307 | ), 308 | InstanceMethod( 309 | "setBindGroup", 310 | &GPURenderPassEncoder::setBindGroup, 311 | napi_enumerable 312 | ), 313 | InstanceMethod( 314 | "pushDebugGroup", 315 | &GPURenderPassEncoder::pushDebugGroup, 316 | napi_enumerable 317 | ), 318 | InstanceMethod( 319 | "popDebugGroup", 320 | &GPURenderPassEncoder::popDebugGroup, 321 | napi_enumerable 322 | ), 323 | InstanceMethod( 324 | "insertDebugMarker", 325 | &GPURenderPassEncoder::insertDebugMarker, 326 | napi_enumerable 327 | ), 328 | }); 329 | constructor = Napi::Persistent(func); 330 | constructor.SuppressDestruct(); 331 | exports.Set("GPURenderPassEncoder", func); 332 | return exports; 333 | } 334 | -------------------------------------------------------------------------------- /src/GPURenderPassEncoder.h: -------------------------------------------------------------------------------- 1 | #ifndef __GPU_RENDER_PASS_ENCODER_H__ 2 | #define __GPU_RENDER_PASS_ENCODER_H__ 3 | 4 | #include "Base.h" 5 | 6 | // no inheritance in NAPI 7 | // GPURenderEncoderBase : GPUProgrammablePassEncoder 8 | // GPURenderPassEncoder : GPURenderEncoderBase 9 | 10 | class GPURenderPassEncoder : public Napi::ObjectWrap { 11 | 12 | public: 13 | 14 | static Napi::Object Initialize(Napi::Env env, Napi::Object exports); 15 | static Napi::FunctionReference constructor; 16 | 17 | GPURenderPassEncoder(const Napi::CallbackInfo &info); 18 | ~GPURenderPassEncoder(); 19 | 20 | // GPURenderEncoderBase BEGIN 21 | Napi::Value setPipeline(const Napi::CallbackInfo &info); 22 | Napi::Value setIndexBuffer(const Napi::CallbackInfo &info); 23 | Napi::Value setVertexBuffer(const Napi::CallbackInfo &info); 24 | Napi::Value draw(const Napi::CallbackInfo &info); 25 | Napi::Value drawIndexed(const Napi::CallbackInfo &info); 26 | Napi::Value drawIndirect(const Napi::CallbackInfo &info); 27 | Napi::Value drawIndexedIndirect(const Napi::CallbackInfo &info); 28 | // GPURenderEncoderBase END 29 | 30 | // GPURenderPassEncoder BEGIN 31 | Napi::Value setViewport(const Napi::CallbackInfo &info); 32 | Napi::Value setScissorRect(const Napi::CallbackInfo &info); 33 | Napi::Value setBlendColor(const Napi::CallbackInfo &info); 34 | Napi::Value setStencilReference(const Napi::CallbackInfo &info); 35 | 36 | Napi::Value executeBundles(const Napi::CallbackInfo &info); 37 | 38 | Napi::Value endPass(const Napi::CallbackInfo &info); 39 | // GPURenderPassEncoder END 40 | 41 | // GPUProgrammablePassEncoder BEGIN 42 | Napi::Value setBindGroup(const Napi::CallbackInfo &info); 43 | Napi::Value pushDebugGroup(const Napi::CallbackInfo &info); 44 | Napi::Value popDebugGroup(const Napi::CallbackInfo &info); 45 | Napi::Value insertDebugMarker(const Napi::CallbackInfo &info); 46 | // GPUProgrammablePassEncoder END 47 | 48 | Napi::ObjectReference device; 49 | Napi::ObjectReference commandEncoder; 50 | 51 | WGPURenderPassEncoder instance; 52 | private: 53 | 54 | }; 55 | 56 | #endif 57 | -------------------------------------------------------------------------------- /src/GPURenderPipeline.cpp: -------------------------------------------------------------------------------- 1 | #include "GPURenderPipeline.h" 2 | #include "GPUDevice.h" 3 | #include "GPUShaderModule.h" 4 | 5 | #include "DescriptorDecoder.h" 6 | 7 | Napi::FunctionReference GPURenderPipeline::constructor; 8 | 9 | GPURenderPipeline::GPURenderPipeline(const Napi::CallbackInfo& info) : Napi::ObjectWrap(info) { 10 | Napi::Env env = info.Env(); 11 | 12 | this->device.Reset(info[0].As(), 1); 13 | GPUDevice* device = Napi::ObjectWrap::Unwrap(this->device.Value()); 14 | 15 | auto descriptor = DescriptorDecoder::GPURenderPipelineDescriptor(device, info[1].As()); 16 | 17 | this->instance = wgpuDeviceCreateRenderPipeline(device->instance, &descriptor); 18 | } 19 | 20 | GPURenderPipeline::~GPURenderPipeline() { 21 | this->device.Reset(); 22 | wgpuRenderPipelineRelease(this->instance); 23 | } 24 | 25 | Napi::Object GPURenderPipeline::Initialize(Napi::Env env, Napi::Object exports) { 26 | Napi::HandleScope scope(env); 27 | Napi::Function func = DefineClass(env, "GPURenderPipeline", { 28 | 29 | }); 30 | constructor = Napi::Persistent(func); 31 | constructor.SuppressDestruct(); 32 | exports.Set("GPURenderPipeline", func); 33 | return exports; 34 | } 35 | -------------------------------------------------------------------------------- /src/GPURenderPipeline.h: -------------------------------------------------------------------------------- 1 | #ifndef __GPU_RENDER_PIPELINE_H__ 2 | #define __GPU_RENDER_PIPELINE_H__ 3 | 4 | #include "Base.h" 5 | 6 | class GPURenderPipeline : public Napi::ObjectWrap { 7 | 8 | public: 9 | 10 | static Napi::Object Initialize(Napi::Env env, Napi::Object exports); 11 | static Napi::FunctionReference constructor; 12 | 13 | GPURenderPipeline(const Napi::CallbackInfo &info); 14 | ~GPURenderPipeline(); 15 | 16 | Napi::ObjectReference device; 17 | 18 | WGPURenderPipeline instance; 19 | private: 20 | 21 | }; 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /src/GPUSampler.cpp: -------------------------------------------------------------------------------- 1 | #include "GPUSampler.h" 2 | #include "GPUDevice.h" 3 | 4 | #include "DescriptorDecoder.h" 5 | 6 | Napi::FunctionReference GPUSampler::constructor; 7 | 8 | GPUSampler::GPUSampler(const Napi::CallbackInfo& info) : Napi::ObjectWrap(info) { 9 | Napi::Env env = info.Env(); 10 | 11 | this->device.Reset(info[0].As(), 1); 12 | GPUDevice* device = Napi::ObjectWrap::Unwrap(this->device.Value()); 13 | 14 | auto descriptor = DescriptorDecoder::GPUSamplerDescriptor(device, info[1].As()); 15 | 16 | this->instance = wgpuDeviceCreateSampler(device->instance, &descriptor); 17 | } 18 | 19 | GPUSampler::~GPUSampler() { 20 | this->device.Reset(); 21 | wgpuSamplerRelease(this->instance); 22 | } 23 | 24 | Napi::Object GPUSampler::Initialize(Napi::Env env, Napi::Object exports) { 25 | Napi::HandleScope scope(env); 26 | Napi::Function func = DefineClass(env, "GPUSampler", { 27 | 28 | }); 29 | constructor = Napi::Persistent(func); 30 | constructor.SuppressDestruct(); 31 | exports.Set("GPUSampler", func); 32 | return exports; 33 | } 34 | -------------------------------------------------------------------------------- /src/GPUSampler.h: -------------------------------------------------------------------------------- 1 | #ifndef __GPU_SAMPLER_H__ 2 | #define __GPU_SAMPLER_H__ 3 | 4 | #include "Base.h" 5 | 6 | class GPUSampler : public Napi::ObjectWrap { 7 | 8 | public: 9 | 10 | static Napi::Object Initialize(Napi::Env env, Napi::Object exports); 11 | static Napi::FunctionReference constructor; 12 | 13 | GPUSampler(const Napi::CallbackInfo &info); 14 | ~GPUSampler(); 15 | 16 | Napi::ObjectReference device; 17 | 18 | WGPUSampler instance; 19 | private: 20 | 21 | }; 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /src/GPUShaderModule.cpp: -------------------------------------------------------------------------------- 1 | #include "GPUShaderModule.h" 2 | #include "GPUDevice.h" 3 | 4 | #include 5 | 6 | #include 7 | 8 | Napi::FunctionReference GPUShaderModule::constructor; 9 | 10 | GPUShaderModule::GPUShaderModule(const Napi::CallbackInfo& info) : Napi::ObjectWrap(info) { 11 | Napi::Env env = info.Env(); 12 | 13 | this->device.Reset(info[0].As(), 1); 14 | GPUDevice* uwDevice = Napi::ObjectWrap::Unwrap(this->device.Value()); 15 | WGPUDevice backendDevice = uwDevice->instance; 16 | 17 | WGPUShaderModuleSPIRVDescriptor spirvDescriptor; 18 | spirvDescriptor.chain.sType = WGPUSType_ShaderModuleSPIRVDescriptor; 19 | spirvDescriptor.chain.next = nullptr; 20 | 21 | WGPUShaderModuleDescriptor descriptor; 22 | descriptor.nextInChain = reinterpret_cast(&spirvDescriptor); 23 | descriptor.label = nullptr; 24 | 25 | { 26 | Napi::Object obj = info[1].As(); 27 | Napi::Value code = obj.Get("code"); 28 | // code is 'String' 29 | if (code.IsString()) { 30 | // shaderc inputs 31 | const char* source = getNAPIStringCopy(code); 32 | size_t size = strlen(source); 33 | shaderc_shader_kind kind = shaderc_glsl_infer_from_source; 34 | 35 | shaderc::Compiler compiler; 36 | 37 | auto result = compiler.CompileGlslToSpv(source, size, kind, "shader"); 38 | 39 | if (result.GetCompilationStatus() != shaderc_compilation_status_success) { 40 | uwDevice->throwCallbackError( 41 | Napi::String::New(env, "Error"), 42 | Napi::String::New(env, result.GetErrorMessage()) 43 | ); 44 | return; 45 | } 46 | 47 | const uint32_t* resultBegin = result.cbegin(); 48 | const uint32_t* resultEnd = result.cend(); 49 | 50 | ptrdiff_t resultSize = resultEnd - resultBegin; 51 | 52 | spirvDescriptor.code = result.cbegin(); 53 | spirvDescriptor.codeSize = static_cast(resultSize); 54 | this->instance = wgpuDeviceCreateShaderModule(backendDevice, &descriptor); 55 | delete source; 56 | } 57 | // code is 'Uint32Array' 58 | else if (code.IsTypedArray()) { 59 | if (code.As().TypedArrayType() == napi_uint32_array) { 60 | size_t size; 61 | spirvDescriptor.code = getTypedArrayData(code, &size); 62 | spirvDescriptor.codeSize = static_cast(size); 63 | this->instance = wgpuDeviceCreateShaderModule(backendDevice, &descriptor); 64 | } else { 65 | uwDevice->throwCallbackError( 66 | Napi::String::New(env, "TypeError"), 67 | Napi::String::New(env, "Expected 'Uint32Array' for property 'code'") 68 | ); 69 | } 70 | } 71 | // code is invalid type 72 | else { 73 | uwDevice->throwCallbackError( 74 | Napi::String::New(env, "TypeError"), 75 | Napi::String::New(env, "Expected 'String' or 'Uint32Array' property 'code'") 76 | ); 77 | } 78 | } 79 | 80 | } 81 | 82 | GPUShaderModule::~GPUShaderModule() { 83 | this->device.Reset(); 84 | wgpuShaderModuleRelease(this->instance); 85 | } 86 | 87 | Napi::Object GPUShaderModule::Initialize(Napi::Env env, Napi::Object exports) { 88 | Napi::HandleScope scope(env); 89 | Napi::Function func = DefineClass(env, "GPUShaderModule", { 90 | 91 | }); 92 | constructor = Napi::Persistent(func); 93 | constructor.SuppressDestruct(); 94 | exports.Set("GPUShaderModule", func); 95 | return exports; 96 | } 97 | -------------------------------------------------------------------------------- /src/GPUShaderModule.h: -------------------------------------------------------------------------------- 1 | #ifndef __GPU_SHADER_MODULE_H__ 2 | #define __GPU_SHADER_MODULE_H__ 3 | 4 | #include "Base.h" 5 | 6 | class GPUShaderModule : public Napi::ObjectWrap { 7 | 8 | public: 9 | 10 | static Napi::Object Initialize(Napi::Env env, Napi::Object exports); 11 | static Napi::FunctionReference constructor; 12 | 13 | GPUShaderModule(const Napi::CallbackInfo &info); 14 | ~GPUShaderModule(); 15 | 16 | Napi::ObjectReference device; 17 | 18 | WGPUShaderModule instance; 19 | private: 20 | 21 | }; 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /src/GPUSwapChain.cpp: -------------------------------------------------------------------------------- 1 | #include "GPUSwapChain.h" 2 | #include "GPUDevice.h" 3 | #include "GPUTexture.h" 4 | #include "BackendBinding.h" 5 | #include "GPUCanvasContext.h" 6 | #include "WebGPUWindow.h" 7 | 8 | #include "DescriptorDecoder.h" 9 | 10 | Napi::FunctionReference GPUSwapChain::constructor; 11 | 12 | GPUSwapChain::GPUSwapChain(const Napi::CallbackInfo& info) : Napi::ObjectWrap(info) { 13 | Napi::Env env = info.Env(); 14 | 15 | this->context.Reset(info[0].As(), 1); 16 | GPUCanvasContext* context = Napi::ObjectWrap::Unwrap(this->context.Value()); 17 | WebGPUWindow* window = Napi::ObjectWrap::Unwrap(context->window.Value()); 18 | 19 | Napi::Object args = info[1].As(); 20 | 21 | this->device.Reset(args.Get("device").As(), 1); 22 | GPUDevice* device = Napi::ObjectWrap::Unwrap(this->device.Value()); 23 | 24 | // create 25 | WGPUSwapChainDescriptor descriptor; 26 | descriptor.nextInChain = nullptr; 27 | descriptor.implementation = device->binding->GetSwapChainImplementation(); 28 | 29 | this->instance = wgpuDeviceCreateSwapChain(device->instance, nullptr, &descriptor); 30 | 31 | // configurate 32 | WGPUTextureFormat format = static_cast( 33 | DescriptorDecoder::GPUTextureFormat(args.Get("format").As().Utf8Value()) 34 | ); 35 | 36 | WGPUTextureUsage usage = WGPUTextureUsage_OutputAttachment; 37 | if (args.Has("usage")) { 38 | usage = static_cast(args.Get("usage").As().Uint32Value()); 39 | } 40 | 41 | wgpuSwapChainConfigure(this->instance, format, usage, window->width, window->height); 42 | 43 | this->format = format; 44 | this->usage = usage; 45 | 46 | window->swapChain = this; 47 | } 48 | 49 | GPUSwapChain::~GPUSwapChain() { 50 | this->device.Reset(); 51 | this->context.Reset(); 52 | wgpuSwapChainRelease(this->instance); 53 | } 54 | 55 | Napi::Value GPUSwapChain::getCurrentTextureView(const Napi::CallbackInfo &info) { 56 | Napi::Env env = info.Env(); 57 | 58 | WGPUTextureView nextTextureView = wgpuSwapChainGetCurrentTextureView(this->instance); 59 | 60 | std::vector args = { 61 | info.This().As(), 62 | info[0].As(), 63 | Napi::Boolean::New(env, true) 64 | }; 65 | Napi::Object textureView = GPUTextureView::constructor.New(args); 66 | 67 | GPUTextureView* uwTexture = Napi::ObjectWrap::Unwrap(textureView); 68 | uwTexture->instance = nextTextureView; 69 | 70 | return textureView; 71 | } 72 | 73 | Napi::Value GPUSwapChain::present(const Napi::CallbackInfo &info) { 74 | Napi::Env env = info.Env(); 75 | 76 | wgpuSwapChainPresent(this->instance); 77 | 78 | return env.Undefined(); 79 | } 80 | 81 | Napi::Object GPUSwapChain::Initialize(Napi::Env env, Napi::Object exports) { 82 | Napi::HandleScope scope(env); 83 | Napi::Function func = DefineClass(env, "GPUSwapChain", { 84 | InstanceMethod( 85 | "getCurrentTextureView", 86 | &GPUSwapChain::getCurrentTextureView, 87 | napi_enumerable 88 | ), 89 | InstanceMethod( 90 | "present", 91 | &GPUSwapChain::present, 92 | napi_enumerable 93 | ) 94 | }); 95 | constructor = Napi::Persistent(func); 96 | constructor.SuppressDestruct(); 97 | exports.Set("GPUSwapChain", func); 98 | return exports; 99 | } 100 | -------------------------------------------------------------------------------- /src/GPUSwapChain.h: -------------------------------------------------------------------------------- 1 | #ifndef __GPU_SWAPCHAIN_H__ 2 | #define __GPU_SWAPCHAIN_H__ 3 | 4 | #include "Base.h" 5 | 6 | class GPUSwapChain : public Napi::ObjectWrap { 7 | 8 | public: 9 | 10 | static Napi::Object Initialize(Napi::Env env, Napi::Object exports); 11 | static Napi::FunctionReference constructor; 12 | 13 | GPUSwapChain(const Napi::CallbackInfo &info); 14 | ~GPUSwapChain(); 15 | 16 | Napi::Value getCurrentTextureView(const Napi::CallbackInfo &info); 17 | Napi::Value present(const Napi::CallbackInfo &info); 18 | 19 | Napi::ObjectReference device; 20 | Napi::ObjectReference context; 21 | 22 | WGPUSwapChain instance; 23 | 24 | WGPUTextureFormat format; 25 | WGPUTextureUsage usage; 26 | }; 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /src/GPUTexture.cpp: -------------------------------------------------------------------------------- 1 | #include "GPUTexture.h" 2 | #include "GPUDevice.h" 3 | #include "GPUTextureView.h" 4 | 5 | #include "DescriptorDecoder.h" 6 | 7 | Napi::FunctionReference GPUTexture::constructor; 8 | 9 | GPUTexture::GPUTexture(const Napi::CallbackInfo& info) : Napi::ObjectWrap(info) { 10 | Napi::Env env = info.Env(); 11 | 12 | this->device.Reset(info[0].As(), 1); 13 | 14 | // constructor called internally: 15 | // prevents this constructor to create a new texture, 16 | // since the texture is expected to be created externally 17 | if (info[2].IsBoolean() && info[2].As().Value() == true) { 18 | return; 19 | } 20 | GPUDevice* device = Napi::ObjectWrap::Unwrap(this->device.Value()); 21 | 22 | auto descriptor = DescriptorDecoder::GPUTextureDescriptor(device, info[1].As()); 23 | 24 | this->instance = wgpuDeviceCreateTexture(device->instance, &descriptor); 25 | 26 | this->dimension = (&descriptor)->dimension; 27 | this->arrayLayerCount = (&descriptor)->arrayLayerCount; 28 | } 29 | 30 | GPUTexture::~GPUTexture() { 31 | this->device.Reset(); 32 | wgpuTextureRelease(this->instance); 33 | } 34 | 35 | Napi::Value GPUTexture::createView(const Napi::CallbackInfo &info) { 36 | Napi::Env env = info.Env(); 37 | std::vector args = { 38 | info.This().As() 39 | }; 40 | if (info[0].IsObject()) args.push_back(info[0].As()); 41 | Napi::Object textureView = GPUTextureView::constructor.New(args); 42 | return textureView; 43 | } 44 | 45 | Napi::Value GPUTexture::destroy(const Napi::CallbackInfo &info) { 46 | Napi::Env env = info.Env(); 47 | 48 | return env.Undefined(); 49 | } 50 | 51 | Napi::Object GPUTexture::Initialize(Napi::Env env, Napi::Object exports) { 52 | Napi::HandleScope scope(env); 53 | Napi::Function func = DefineClass(env, "GPUTexture", { 54 | InstanceMethod( 55 | "createView", 56 | &GPUTexture::createView, 57 | napi_enumerable 58 | ), 59 | InstanceMethod( 60 | "destroy", 61 | &GPUTexture::destroy, 62 | napi_enumerable 63 | ) 64 | }); 65 | constructor = Napi::Persistent(func); 66 | constructor.SuppressDestruct(); 67 | exports.Set("GPUTexture", func); 68 | return exports; 69 | } 70 | -------------------------------------------------------------------------------- /src/GPUTexture.h: -------------------------------------------------------------------------------- 1 | #ifndef __GPU_TEXTURE_H__ 2 | #define __GPU_TEXTURE_H__ 3 | 4 | #include "Base.h" 5 | 6 | class GPUTexture : public Napi::ObjectWrap { 7 | 8 | public: 9 | 10 | static Napi::Object Initialize(Napi::Env env, Napi::Object exports); 11 | static Napi::FunctionReference constructor; 12 | 13 | GPUTexture(const Napi::CallbackInfo &info); 14 | ~GPUTexture(); 15 | 16 | Napi::Value createView(const Napi::CallbackInfo &info); 17 | Napi::Value destroy(const Napi::CallbackInfo &info); 18 | 19 | Napi::ObjectReference device; 20 | 21 | WGPUTextureDimension dimension; 22 | uint64_t arrayLayerCount; 23 | 24 | WGPUTexture instance; 25 | private: 26 | 27 | }; 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /src/GPUTextureView.cpp: -------------------------------------------------------------------------------- 1 | #include "GPUTextureView.h" 2 | #include "GPUTexture.h" 3 | 4 | #include "DescriptorDecoder.h" 5 | 6 | Napi::FunctionReference GPUTextureView::constructor; 7 | 8 | GPUTextureView::GPUTextureView(const Napi::CallbackInfo& info) : Napi::ObjectWrap(info) { 9 | Napi::Env env = info.Env(); 10 | 11 | // constructor called internally: 12 | // prevents this constructor to create a new texture, 13 | // since the texture is expected to be created externally 14 | if (info[2].IsBoolean() && info[2].As().Value() == true) { 15 | return; 16 | } 17 | 18 | this->texture.Reset(info[0].As(), 1); 19 | GPUTexture* texture = Napi::ObjectWrap::Unwrap(this->texture.Value()); 20 | GPUDevice* device = Napi::ObjectWrap::Unwrap(texture->device.Value()); 21 | 22 | auto descriptor = DescriptorDecoder::GPUTextureViewDescriptor(device, info[1].As()); 23 | 24 | this->instance = wgpuTextureCreateView(texture->instance, &descriptor); 25 | } 26 | 27 | GPUTextureView::~GPUTextureView() { 28 | this->texture.Reset(); 29 | wgpuTextureViewRelease(this->instance); 30 | } 31 | 32 | Napi::Object GPUTextureView::Initialize(Napi::Env env, Napi::Object exports) { 33 | Napi::HandleScope scope(env); 34 | Napi::Function func = DefineClass(env, "GPUTextureView", { 35 | 36 | }); 37 | constructor = Napi::Persistent(func); 38 | constructor.SuppressDestruct(); 39 | exports.Set("GPUTextureView", func); 40 | return exports; 41 | } 42 | -------------------------------------------------------------------------------- /src/GPUTextureView.h: -------------------------------------------------------------------------------- 1 | #ifndef __GPU_TEXTURE_VIEW_H__ 2 | #define __GPU_TEXTURE_VIEW_H__ 3 | 4 | #include "Base.h" 5 | 6 | class GPUTextureView : public Napi::ObjectWrap { 7 | 8 | public: 9 | 10 | static Napi::Object Initialize(Napi::Env env, Napi::Object exports); 11 | static Napi::FunctionReference constructor; 12 | 13 | GPUTextureView(const Napi::CallbackInfo &info); 14 | ~GPUTextureView(); 15 | 16 | Napi::ObjectReference texture; 17 | 18 | WGPUTextureView instance; 19 | private: 20 | 21 | }; 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /src/MetalBackend.h: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Dawn Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef DAWNNATIVE_METALBACKEND_H_ 16 | #define DAWNNATIVE_METALBACKEND_H_ 17 | 18 | #include 19 | #include 20 | 21 | // The specifics of the Metal backend expose types in function signatures that might not be 22 | // available in dependent's minimum supported SDK version. Suppress all availability errors using 23 | // clang's pragmas. Dependents using the types without guarded availability will still get errors 24 | // when using the types. 25 | #pragma clang diagnostic push 26 | #pragma clang diagnostic ignored "-Wunguarded-availability" 27 | 28 | struct __IOSurface; 29 | typedef __IOSurface* IOSurfaceRef; 30 | 31 | #ifdef __OBJC__ 32 | # import 33 | #endif //__OBJC__ 34 | 35 | namespace dawn_native { namespace metal { 36 | DAWN_NATIVE_EXPORT WGPUTexture WrapIOSurface(WGPUDevice device, 37 | const WGPUTextureDescriptor* descriptor, 38 | IOSurfaceRef ioSurface, 39 | uint32_t plane); 40 | 41 | // When making Metal interop with other APIs, we need to be careful that QueueSubmit doesn't 42 | // mean that the operations will be visible to other APIs/Metal devices right away. macOS 43 | // does have a global queue of graphics operations, but the command buffers are inserted there 44 | // when they are "scheduled". Submitting other operations before the command buffer is 45 | // scheduled could lead to races in who gets scheduled first and incorrect rendering. 46 | DAWN_NATIVE_EXPORT void WaitForCommandsToBeScheduled(WGPUDevice device); 47 | }} // namespace dawn_native::metal 48 | 49 | #ifdef __OBJC__ 50 | namespace dawn_native { namespace metal { 51 | DAWN_NATIVE_EXPORT id GetMetalDevice(WGPUDevice device); 52 | }} // namespace dawn_native::metal 53 | #endif // __OBJC__ 54 | 55 | #pragma clang diagnostic pop 56 | 57 | #endif // DAWNNATIVE_METALBACKEND_H_ 58 | -------------------------------------------------------------------------------- /src/MetalBinding.mm: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Dawn Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "BackendBinding.h" 16 | 17 | #include "SwapChainUtils.h" 18 | #include "dawn_native/MetalBackend.h" 19 | 20 | #define GLFW_EXPOSE_NATIVE_COCOA 21 | #include "GLFW/glfw3.h" 22 | #include "GLFW/glfw3native.h" 23 | 24 | #import 25 | 26 | class SwapChainImplMTL { 27 | public: 28 | using WSIContext = DawnWSIContextMetal; 29 | 30 | SwapChainImplMTL(id nsWindow) : mNsWindow(nsWindow) { 31 | } 32 | 33 | ~SwapChainImplMTL() { 34 | [mCurrentTexture release]; 35 | [mCurrentDrawable release]; 36 | } 37 | 38 | void Init(DawnWSIContextMetal* ctx) { 39 | mMtlDevice = ctx->device; 40 | mCommandQueue = ctx->queue; 41 | } 42 | 43 | DawnSwapChainError Configure(WGPUTextureFormat format, 44 | WGPUTextureUsage usage, 45 | uint32_t width, 46 | uint32_t height) { 47 | if (format != WGPUTextureFormat_BGRA8Unorm) { 48 | return "unsupported format"; 49 | } 50 | //ASSERT(width > 0); 51 | //ASSERT(height > 0); 52 | 53 | NSView* contentView = [mNsWindow contentView]; 54 | [contentView setWantsLayer:YES]; 55 | 56 | CGSize size = {}; 57 | size.width = width; 58 | size.height = height; 59 | 60 | mLayer = [CAMetalLayer layer]; 61 | [mLayer setDevice:mMtlDevice]; 62 | [mLayer setPixelFormat:MTLPixelFormatBGRA8Unorm]; 63 | [mLayer setDrawableSize:size]; 64 | 65 | constexpr uint32_t kFramebufferOnlyTextureUsages = 66 | WGPUTextureUsage_OutputAttachment | WGPUTextureUsage_Present; 67 | bool hasOnlyFramebufferUsages = !(usage & (~kFramebufferOnlyTextureUsages)); 68 | if (hasOnlyFramebufferUsages) { 69 | [mLayer setFramebufferOnly:YES]; 70 | } 71 | 72 | [contentView setLayer:mLayer]; 73 | 74 | return DAWN_SWAP_CHAIN_NO_ERROR; 75 | } 76 | 77 | DawnSwapChainError GetNextTexture(DawnSwapChainNextTexture* nextTexture) { 78 | [mCurrentDrawable release]; 79 | mCurrentDrawable = [mLayer nextDrawable]; 80 | [mCurrentDrawable retain]; 81 | 82 | [mCurrentTexture release]; 83 | mCurrentTexture = mCurrentDrawable.texture; 84 | [mCurrentTexture retain]; 85 | 86 | nextTexture->texture.ptr = reinterpret_cast(mCurrentTexture); 87 | 88 | return DAWN_SWAP_CHAIN_NO_ERROR; 89 | } 90 | 91 | DawnSwapChainError Present() { 92 | id commandBuffer = [mCommandQueue commandBuffer]; 93 | [commandBuffer presentDrawable:mCurrentDrawable]; 94 | [commandBuffer commit]; 95 | 96 | return DAWN_SWAP_CHAIN_NO_ERROR; 97 | } 98 | 99 | private: 100 | id mNsWindow = nil; 101 | id mMtlDevice = nil; 102 | id mCommandQueue = nil; 103 | 104 | CAMetalLayer* mLayer = nullptr; 105 | id mCurrentDrawable = nil; 106 | id mCurrentTexture = nil; 107 | }; 108 | 109 | class MetalBinding : public BackendBinding { 110 | public: 111 | MetalBinding(GLFWwindow* window, WGPUDevice device) : BackendBinding(window, device) { 112 | } 113 | 114 | uint64_t GetSwapChainImplementation() override { 115 | if (mSwapchainImpl.userData == nullptr) { 116 | mSwapchainImpl = CreateSwapChainImplementation( 117 | new SwapChainImplMTL(glfwGetCocoaWindow(mWindow))); 118 | } 119 | return reinterpret_cast(&mSwapchainImpl); 120 | } 121 | 122 | WGPUTextureFormat GetPreferredSwapChainTextureFormat() override { 123 | return WGPUTextureFormat_BGRA8Unorm; 124 | } 125 | 126 | private: 127 | DawnSwapChainImplementation mSwapchainImpl = {}; 128 | }; 129 | 130 | BackendBinding* CreateMetalBinding(GLFWwindow* window, WGPUDevice device) { 131 | return new MetalBinding(window, device); 132 | } 133 | -------------------------------------------------------------------------------- /src/NullBinding.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Dawn Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "BackendBinding.h" 16 | 17 | #include "dawn_native/NullBackend.h" 18 | 19 | #include 20 | 21 | class NullBinding : public BackendBinding { 22 | public: 23 | NullBinding(GLFWwindow* window, WGPUDevice device) : BackendBinding(window, device) { 24 | } 25 | 26 | uint64_t GetSwapChainImplementation() override { 27 | if (mSwapchainImpl.userData == nullptr) { 28 | mSwapchainImpl = dawn_native::null::CreateNativeSwapChainImpl(); 29 | } 30 | return reinterpret_cast(&mSwapchainImpl); 31 | } 32 | WGPUTextureFormat GetPreferredSwapChainTextureFormat() override { 33 | return WGPUTextureFormat_RGBA8Unorm; 34 | } 35 | 36 | private: 37 | DawnSwapChainImplementation mSwapchainImpl = {}; 38 | }; 39 | 40 | BackendBinding* CreateNullBinding(GLFWwindow* window, WGPUDevice device) { 41 | return new NullBinding(window, device); 42 | } 43 | -------------------------------------------------------------------------------- /src/SwapChainUtils.h: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Dawn Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef COMMON_SWAPCHAINUTILS_H_ 16 | #define COMMON_SWAPCHAINUTILS_H_ 17 | 18 | #include "dawn/dawn_wsi.h" 19 | 20 | template 21 | DawnSwapChainImplementation CreateSwapChainImplementation(T* swapChain) { 22 | DawnSwapChainImplementation impl = {}; 23 | impl.userData = swapChain; 24 | impl.Init = [](void* userData, void* wsiContext) { 25 | auto* ctx = static_cast(wsiContext); 26 | reinterpret_cast(userData)->Init(ctx); 27 | }; 28 | impl.Destroy = [](void* userData) { delete reinterpret_cast(userData); }; 29 | impl.Configure = [](void* userData, WGPUTextureFormat format, WGPUTextureUsage allowedUsage, 30 | uint32_t width, uint32_t height) { 31 | return static_cast(userData)->Configure(format, allowedUsage, width, height); 32 | }; 33 | impl.GetNextTexture = [](void* userData, DawnSwapChainNextTexture* nextTexture) { 34 | return static_cast(userData)->GetNextTexture(nextTexture); 35 | }; 36 | impl.Present = [](void* userData) { return static_cast(userData)->Present(); }; 37 | return impl; 38 | } 39 | 40 | #endif // COMMON_SWAPCHAINUTILS_H_ 41 | -------------------------------------------------------------------------------- /src/Utils.h: -------------------------------------------------------------------------------- 1 | #ifndef __GPU_UTILS_H__ 2 | #define __GPU_UTILS_H__ 3 | 4 | #define NAPI_EXPERIMENTAL 5 | #include 6 | 7 | inline char* getNAPIStringCopy(const Napi::Value& value) { 8 | std::string utf8 = value.ToString().Utf8Value(); 9 | int len = utf8.length() + 1; // +1 NULL 10 | char *str = new char[len]; 11 | strncpy(str, utf8.c_str(), len); 12 | return str; 13 | }; 14 | 15 | inline void nextJSProcessTick(Napi::Env& env) { 16 | Napi::Object process = env.Global().Get("process").As(); 17 | Napi::Function nextTick = process.Get("nextTick").As(); 18 | nextTick.Call(env.Global(), {}); 19 | }; 20 | 21 | template inline T* getTypedArrayData(const Napi::Value& value, size_t* len = nullptr) { 22 | T* data = nullptr; 23 | if (len) *len = 0; 24 | if (!value.IsTypedArray()) { 25 | Napi::Env env = value.Env(); 26 | Napi::Error::New(env, "Argument must be a 'ArrayBufferView'").ThrowAsJavaScriptException(); 27 | return data; 28 | } 29 | Napi::TypedArray arr = value.As(); 30 | Napi::ArrayBuffer buffer = arr.ArrayBuffer(); 31 | if (len) *len = arr.ByteLength() / sizeof(T); 32 | data = reinterpret_cast(reinterpret_cast(buffer.Data()) + arr.ByteOffset()); 33 | return data; 34 | }; 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /src/VulkanBinding.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Dawn Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "BackendBinding.h" 16 | 17 | #include "dawn_native/VulkanBackend.h" 18 | 19 | // Include GLFW after VulkanBackend so that it declares the Vulkan-specific functions 20 | #include "GLFW/glfw3.h" 21 | 22 | #include 23 | 24 | class VulkanBinding : public BackendBinding { 25 | public: 26 | VulkanBinding(GLFWwindow* window, WGPUDevice device) : BackendBinding(window, device) { 27 | } 28 | 29 | uint64_t GetSwapChainImplementation() override { 30 | if (mSwapchainImpl.userData == nullptr) { 31 | VkSurfaceKHR surface = VK_NULL_HANDLE; 32 | if (glfwCreateWindowSurface(dawn_native::vulkan::GetInstance(mDevice), mWindow, 33 | nullptr, &surface) != VK_SUCCESS) { 34 | //ASSERT(false); 35 | } 36 | 37 | mSwapchainImpl = dawn_native::vulkan::CreateNativeSwapChainImpl(mDevice, surface); 38 | } 39 | return reinterpret_cast(&mSwapchainImpl); 40 | } 41 | WGPUTextureFormat GetPreferredSwapChainTextureFormat() override { 42 | //ASSERT(mSwapchainImpl.userData != nullptr); 43 | return dawn_native::vulkan::GetNativeSwapChainPreferredFormat(&mSwapchainImpl); 44 | } 45 | 46 | private: 47 | DawnSwapChainImplementation mSwapchainImpl = {}; 48 | }; 49 | 50 | BackendBinding* CreateVulkanBinding(GLFWwindow* window, WGPUDevice device) { 51 | return new VulkanBinding(window, device); 52 | } 53 | -------------------------------------------------------------------------------- /src/WebGPUWindow.h: -------------------------------------------------------------------------------- 1 | #ifndef __WEB_GPU_WINDOW_H__ 2 | #define __WEB_GPU_WINDOW_H__ 3 | 4 | #include "Base.h" 5 | #include "GPUSwapChain.h" 6 | 7 | class WebGPUWindow : public Napi::ObjectWrap { 8 | 9 | public: 10 | 11 | static Napi::Object Initialize(Napi::Env env, Napi::Object exports); 12 | static Napi::FunctionReference constructor; 13 | 14 | WebGPUWindow(const Napi::CallbackInfo &info); 15 | ~WebGPUWindow(); 16 | 17 | int width = 480; 18 | int height = 320; 19 | std::string title = "undefined"; 20 | 21 | double mouseLastX = 0; 22 | double mouseLastY = 0; 23 | 24 | bool isClosed = false; 25 | 26 | // event callbacks 27 | Napi::FunctionReference onresize; 28 | Napi::FunctionReference onfocus; 29 | Napi::FunctionReference onclose; 30 | Napi::FunctionReference onkeydown; 31 | Napi::FunctionReference onkeyup; 32 | Napi::FunctionReference onmousemove; 33 | Napi::FunctionReference onmousewheel; 34 | Napi::FunctionReference onmousedown; 35 | Napi::FunctionReference onmouseup; 36 | Napi::FunctionReference ondrop; 37 | 38 | Napi::Env env_; 39 | 40 | GLFWwindow* instance; 41 | 42 | GPUSwapChain* swapChain; 43 | WGPUTextureFormat preferredSwapChainFormat = WGPUTextureFormat_Undefined; 44 | 45 | Napi::Value getContext(const Napi::CallbackInfo &info); 46 | Napi::Value pollEvents(const Napi::CallbackInfo &info); 47 | 48 | Napi::Value focus(const Napi::CallbackInfo &info); 49 | Napi::Value close(const Napi::CallbackInfo &info); 50 | Napi::Value shouldClose(const Napi::CallbackInfo &info); 51 | 52 | Napi::Value Gettitle(const Napi::CallbackInfo &info); 53 | void Settitle(const Napi::CallbackInfo &info, const Napi::Value& value); 54 | 55 | Napi::Value Getwidth(const Napi::CallbackInfo &info); 56 | void Setwidth(const Napi::CallbackInfo &info, const Napi::Value& value); 57 | 58 | Napi::Value Getheight(const Napi::CallbackInfo &info); 59 | void Setheight(const Napi::CallbackInfo &info, const Napi::Value& value); 60 | 61 | Napi::Value GetframeBufferWidth(const Napi::CallbackInfo &info); 62 | Napi::Value GetframeBufferHeight(const Napi::CallbackInfo &info); 63 | 64 | Napi::Value GetdevicePixelRatio(const Napi::CallbackInfo &info); 65 | 66 | Napi::Value Getonresize(const Napi::CallbackInfo &info); 67 | void Setonresize(const Napi::CallbackInfo &info, const Napi::Value& value); 68 | 69 | Napi::Value Getonfocus(const Napi::CallbackInfo &info); 70 | void Setonfocus(const Napi::CallbackInfo &info, const Napi::Value& value); 71 | 72 | Napi::Value Getonclose(const Napi::CallbackInfo &info); 73 | void Setonclose(const Napi::CallbackInfo &info, const Napi::Value& value); 74 | 75 | Napi::Value Getonkeydown(const Napi::CallbackInfo &info); 76 | void Setonkeydown(const Napi::CallbackInfo &info, const Napi::Value& value); 77 | 78 | Napi::Value Getonkeyup(const Napi::CallbackInfo &info); 79 | void Setonkeyup(const Napi::CallbackInfo &info, const Napi::Value& value); 80 | 81 | Napi::Value Getonmousemove(const Napi::CallbackInfo &info); 82 | void Setonmousemove(const Napi::CallbackInfo &info, const Napi::Value& value); 83 | 84 | Napi::Value Getonmousewheel(const Napi::CallbackInfo &info); 85 | void Setonmousewheel(const Napi::CallbackInfo &info, const Napi::Value& value); 86 | 87 | Napi::Value Getonmousedown(const Napi::CallbackInfo &info); 88 | void Setonmousedown(const Napi::CallbackInfo &info, const Napi::Value& value); 89 | 90 | Napi::Value Getonmouseup(const Napi::CallbackInfo &info); 91 | void Setonmouseup(const Napi::CallbackInfo &info, const Napi::Value& value); 92 | 93 | Napi::Value Getondrop(const Napi::CallbackInfo &info); 94 | void Setondrop(const Napi::CallbackInfo &info, const Napi::Value& value); 95 | 96 | static void onWindowResize(GLFWwindow*, int, int); 97 | static void onWindowFocus(GLFWwindow*, int); 98 | static void onWindowClose(GLFWwindow*); 99 | static void onWindowKeyPress(GLFWwindow*, int, int, int, int); 100 | static void onWindowMouseMove(GLFWwindow*, double, double); 101 | static void onWindowMouseWheel(GLFWwindow*, double, double); 102 | static void onWindowMouseButton(GLFWwindow*, int, int, int); 103 | static void onWindowDrop(GLFWwindow*, int, const char**); 104 | }; 105 | 106 | #endif 107 | -------------------------------------------------------------------------------- /tests/index.mjs: -------------------------------------------------------------------------------- 1 | import WebGPU from "../index.js"; 2 | 3 | Object.assign(global, WebGPU); 4 | 5 | const vsSrc = ` 6 | #version 450 7 | #pragma shader_stage(vertex) 8 | const vec2 pos[3] = vec2[3]( 9 | vec2(0.0f, 0.5f), 10 | vec2(-0.5f, -0.5f), 11 | vec2(0.5f, -0.5f) 12 | ); 13 | void main() { 14 | gl_Position = vec4(pos[gl_VertexIndex], 0.0, 1.0); 15 | } 16 | `; 17 | 18 | const fsSrc = ` 19 | #version 450 20 | #pragma shader_stage(fragment) 21 | layout(location = 0) out vec4 outColor; 22 | void main() { 23 | outColor = vec4(1.0, 0.0, 0.0, 1.0); 24 | } 25 | `; 26 | 27 | (async function main() { 28 | 29 | const swapChainFormat = "rgba8unorm"; 30 | 31 | const window = new WebGPUWindow({ 32 | width: 640, 33 | height: 480, 34 | title: "WebGPU" 35 | }); 36 | 37 | const adapter = await GPU.requestAdapter({ window }); 38 | 39 | const device = await adapter.requestDevice(); 40 | 41 | const queue = device.getQueue(); 42 | 43 | const context = window.getContext("webgpu"); 44 | 45 | const swapChain = context.configureSwapChain({ 46 | device: device, 47 | format: swapChainFormat 48 | }); 49 | 50 | const layout = device.createPipelineLayout({ 51 | bindGroupLayouts: [] 52 | }); 53 | 54 | const vertexShaderModule = device.createShaderModule({ code: vsSrc }); 55 | const fragmentShaderModule = device.createShaderModule({ code: fsSrc }); 56 | 57 | const pipeline = device.createRenderPipeline({ 58 | layout, 59 | sampleCount: 1, 60 | vertexStage: { 61 | module: vertexShaderModule, 62 | entryPoint: "main" 63 | }, 64 | fragmentStage: { 65 | module: fragmentShaderModule, 66 | entryPoint: "main" 67 | }, 68 | primitiveTopology: "triangle-list", 69 | vertexInput: { 70 | indexFormat: "uint32", 71 | buffers: [] 72 | }, 73 | rasterizationState: { 74 | frontFace: "CCW", 75 | cullMode: "none" 76 | }, 77 | colorStates: [{ 78 | format: swapChainFormat, 79 | alphaBlend: {}, 80 | colorBlend: {} 81 | }] 82 | }); 83 | 84 | function onFrame() { 85 | if (!window.shouldClose()) setTimeout(onFrame, 1e3 / 60); 86 | 87 | const backBuffer = swapChain.getCurrentTexture(); 88 | const backBufferView = backBuffer.createView({ 89 | format: swapChainFormat 90 | }); 91 | const commandEncoder = device.createCommandEncoder({}); 92 | const renderPass = commandEncoder.beginRenderPass({ 93 | colorAttachments: [{ 94 | clearColor: { r: 0.0, g: 0.0, b: 0.0, a: 1.0 }, 95 | loadOp: "clear", 96 | storeOp: "store", 97 | attachment: backBufferView 98 | }] 99 | }); 100 | renderPass.setPipeline(pipeline); 101 | renderPass.draw(3, 1, 0, 0); 102 | renderPass.endPass(); 103 | 104 | const commandBuffer = commandEncoder.finish(); 105 | queue.submit([ commandBuffer ]); 106 | swapChain.present(backBuffer); 107 | window.pollEvents(); 108 | }; 109 | setTimeout(onFrame, 1e3 / 60); 110 | 111 | })(); 112 | --------------------------------------------------------------------------------