diff --git a/.gitignore b/.gitignore index 7a1d858..271a357 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,4 @@ .DS_Store node_modules -# --cut-here-- -dist -# --cut-here-- + diff --git a/README.md b/README.md index 19d12bf..a1f49cd 100644 --- a/README.md +++ b/README.md @@ -21,8 +21,28 @@ Object.assign(globalThis, globals); globalThis.navigator = { gpu: create([]) }; ... + +// do some webgpu +const device = await(await navigator.gpu.requestAdapter()).requestDevice(); +... +``` + +see [example](https://github.com/greggman/node-webgpu/tree/main/example) + +You can pass dawn options in `create` + +```js +let navigator = { + gpu: create([ + "enable-dawn-features=allow_unsafe_apis,dump_shaders,disable_symbol_renaming", + ]), +}; ``` +There is both `enable-dawn-features=comma,separated,toggles` and `disable-dawn-features=comma,separated,toggles`. + +The available options are listed [here](https://dawn.googlesource.com/dawn/+/refs/heads/chromium-gpu-experimental/src/dawn_native/Toggles.cpp) + ## Notes This package provides a WebGPU implementation it node. That said, if you are making a webpage @@ -76,6 +96,8 @@ I've tested with Visual Studio Community Edition 2022 Further you must have [cmake installed](https://cmake.org/download/) and either in your path or at it's standard place of `C:\Program Files\CMake` +You must have `go` installed. Get it at https://go.dev/ + And you must have `node.js` installed, at least version 18. I recommend using [nvm-windows](https://github.com/coreybutler/nvm-windows) to install it as it makes it easy to switch version @@ -88,6 +110,8 @@ XCode installed and its command line tools Further you must have [cmake installed](https://cmake.org/download/) and either in your path or at it's standard place of `/Applications/CMake.app` +You must have `go` installed. Get it at https://go.dev/ + And you must have `node.js` installed, at least version 18. I recommend using [nvm](https://github.com/nvm-sh/nvm) to install it as it makes it easy to switch versions. @@ -101,6 +125,8 @@ the following dependencies sudo apt-get install cmake libxrandr-dev libxinerama-dev libxcursor-dev mesa-common-dev libx11-xcb-dev pkg-config nodejs npm ``` +You must have `go` installed. Get it at https://go.dev/ + And you must have `node.js` installed, at least version 18. I recommend using [nvm](https://github.com/nvm-sh/nvm) to install it as it makes it easy to switch versions. diff --git a/build/build.js b/build/build.js index 5e959ed..144dd28 100644 --- a/build/build.js +++ b/build/build.js @@ -32,6 +32,7 @@ async function buildDawnNode() { ...addElemIf(!isWin, '-GNinja'), '-DDAWN_BUILD_NODE_BINDINGS=1', '-DDAWN_USE_X11=OFF', + '-DCMAKE_BUILD_TYPE=Release', ...addElemIf(isWin, '-DCMAKE_SYSTEM_VERSION=10.0.26100.0'), ...addElemIf(isMac, '-DCMAKE_OSX_SYSROOT=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk'), ]); diff --git a/dist/darwin-arm64.dawn.node b/dist/darwin-arm64.dawn.node new file mode 100644 index 0000000..38eaa73 Binary files /dev/null and b/dist/darwin-arm64.dawn.node differ diff --git a/dist/darwin-x64.dawn.node b/dist/darwin-x64.dawn.node new file mode 100644 index 0000000..cef8ad4 Binary files /dev/null and b/dist/darwin-x64.dawn.node differ diff --git a/dist/linux-x64.dawn.node b/dist/linux-x64.dawn.node new file mode 100644 index 0000000..c5cfe3d Binary files /dev/null and b/dist/linux-x64.dawn.node differ diff --git a/dist/win32-x64.dawn.node b/dist/win32-x64.dawn.node new file mode 100644 index 0000000..b0444f5 Binary files /dev/null and b/dist/win32-x64.dawn.node differ diff --git a/example/.gitignore b/example/.gitignore new file mode 100644 index 0000000..38d603c --- /dev/null +++ b/example/.gitignore @@ -0,0 +1,4 @@ +*.pyc +.DS_Store +node_modules +output.png diff --git a/example/README.md b/example/README.md new file mode 100644 index 0000000..cccfee5 --- /dev/null +++ b/example/README.md @@ -0,0 +1,5 @@ +# Example for node-webgpu + +* cd to this +* `npm ci` +* `node example.js` diff --git a/example/example.js b/example/example.js new file mode 100644 index 0000000..5236712 --- /dev/null +++ b/example/example.js @@ -0,0 +1,92 @@ +import fs from 'node:fs'; +import { PNG } from 'pngjs'; +import { create, globals } from 'node-webgpu'; + +Object.assign(globalThis, globals); +globalThis.navigator = { gpu: create([]) }; + +const adapter = await navigator.gpu?.requestAdapter(); +const device = await adapter?.requestDevice(); + +const module = device.createShaderModule({ + code: ` + @vertex fn vs( + @builtin(vertex_index) vertexIndex : u32 + ) -> @builtin(position) vec4f { + let pos = array( + vec2f( 0.0, 0.5), + vec2f(-0.5, -0.5), + vec2f( 0.5, -0.5), + ); + + return vec4f(pos[vertexIndex], 0.0, 1.0); + } + + @fragment fn fs() -> @location(0) vec4f { + return vec4f(1, 0, 0, 1); + } + `, +}); + +const pipeline = device.createRenderPipeline({ + layout: 'auto', + vertex: { module }, + fragment: { module, targets: [{ format: 'rgba8unorm' }] }, +}); + +const texture = device.createTexture({ + format: 'rgba8unorm', + usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_SRC, + size: [256, 256], +}); + +const align = (v, alignment) => Math.ceil(v / alignment) * alignment; + +const bytesPerRow = align(texture.width * 4, 256); +const buffer = device.createBuffer({ + size: bytesPerRow * texture.height, + usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ, +}); + +const encoder = device.createCommandEncoder(); +const pass = encoder.beginRenderPass({ + colorAttachments: [ + { + view: texture.createView(), + clearValue: [0.3, 0.3, 0.3, 1], + loadOp: 'clear', + storeOp: 'store', + }, + ], +}); +pass.setPipeline(pipeline); +pass.draw(3); +pass.end(); +encoder.copyTextureToBuffer( + { texture }, + { buffer, bytesPerRow }, + [ texture.width, texture.height ], +); +const commandBuffer = encoder.finish(); +device.queue.submit([commandBuffer]); + +await buffer.mapAsync(GPUMapMode.READ); +const rawPixelData = buffer.getMappedRange(); + +const png = new PNG({ + width: texture.width, + height: texture.height, + filterType: -1, +}); + +const dstBytesPerRow = texture.width * 4; +for (let y = 0; y < texture.height; y++) { + const dst = (texture.width * y) * 4; + const src = (y * bytesPerRow); + png.data.set(new Uint8Array(rawPixelData, src, dstBytesPerRow), dst); +} + +// Write the PNG to a file +fs.writeFileSync('output.png', PNG.sync.write(png, {colorType: 6})); +console.log('PNG file has been saved as output.png'); +process.exit(0); diff --git a/example/package-lock.json b/example/package-lock.json new file mode 100644 index 0000000..f1a98b9 --- /dev/null +++ b/example/package-lock.json @@ -0,0 +1,30 @@ +{ + "name": "example", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "example", + "version": "1.0.0", + "license": "MIT", + "dependencies": { + "node-webgpu": "^0.0.6", + "pngjs": "^7.0.0" + } + }, + "node_modules/node-webgpu": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/node-webgpu/-/node-webgpu-0.0.6.tgz", + "integrity": "sha512-FfLyotmDI3JjHogEf5SfB2Eem9OFPy7bkQ90NIeFO0hRoMx9NraRPc0O3SJG3/Vk7JBEVlGSUGMEjr8w4K4IVg==" + }, + "node_modules/pngjs": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-7.0.0.tgz", + "integrity": "sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow==", + "engines": { + "node": ">=14.19.0" + } + } + } +} diff --git a/example/package.json b/example/package.json new file mode 100644 index 0000000..0eedcfb --- /dev/null +++ b/example/package.json @@ -0,0 +1,16 @@ +{ + "name": "example", + "version": "1.0.0", + "description": "example using node-webgpu", + "main": "example.js", + "type": "module", + "scripts": { + "start": "node example.js" + }, + "author": "", + "license": "MIT", + "dependencies": { + "node-webgpu": "^0.0.6", + "pngjs": "^7.0.0" + } +} diff --git a/index.js b/index.js index 03817f2..e9a0d53 100644 --- a/index.js +++ b/index.js @@ -4,6 +4,6 @@ import { createRequire } from 'module'; const require = createRequire(import.meta.url); const __dirname = dirname(fileURLToPath(import.meta.url)); -const dawnNodePath = join(__dirname, `${process.platform}-${process.arch}.node`); +const dawnNodePath = join(__dirname, 'dist', `${process.platform}-${process.arch}.dawn.node`); const { create, globals } = require(dawnNodePath); export { create, globals } diff --git a/package-lock.json b/package-lock.json index 97c448d..dd39b8d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "webgpu", - "version": "0.0.4", + "version": "0.0.8", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "webgpu", - "version": "0.0.4", + "version": "0.0.8", "license": "MIT", "devDependencies": { "mocha": "^11.0.1" diff --git a/package.json b/package.json index 48a3253..5042b9b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "node-webgpu", - "version": "0.0.4", + "version": "0.0.8", "description": "WebGPU for node", "main": "index.js", "type": "module", diff --git a/test/test.js b/test/test.js index b2b3aea..bffcaff 100644 --- a/test/test.js +++ b/test/test.js @@ -1,17 +1,6 @@ import Mocha from 'mocha'; -import { createRequire } from 'module'; -const require = createRequire(import.meta.url); - -const isDev = process.argv[2] !== 'dev'; -const isWin = process.platform === 'win32'; -const dawnNodePath = isDev - ? isWin - ? `${process.cwd()}/third_party/dawn/out/cmake-release/Debug/dawn.node` - : `${process.cwd()}/third_party/dawn/out/cmake-release/dawn.node` - : `${process.cwd()}/dist/${process.platform}-${process.arch}.node`; - -const { create, globals } = require(dawnNodePath); +import { create, globals } from '../index.js'; Object.assign(globalThis, globals); globalThis.navigator = { gpu: create([]) };