├── .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 |
8 |
9 |
10 |
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