native-dawn 0.0.0-stage → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CMakeLists.txt ADDED
@@ -0,0 +1,90 @@
1
+ cmake_minimum_required(VERSION 3.22)
2
+ project(native_dawn LANGUAGES C CXX)
3
+
4
+ set(CMAKE_CXX_STANDARD 20)
5
+ set(CMAKE_C_VISIBILITY_PRESET hidden)
6
+ set(CMAKE_CXX_VISIBILITY_PRESET hidden)
7
+ set(CMAKE_VISIBILITY_INLINES_HIDDEN ON)
8
+ set(CMAKE_INSTALL_LIBDIR lib CACHE PATH "" FORCE)
9
+ include(GNUInstallDirs)
10
+
11
+ # Keep checkout paths out of __FILE__ strings and debug information.
12
+ if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang" AND NOT MSVC)
13
+ add_compile_options("-ffile-prefix-map=${CMAKE_SOURCE_DIR}=.")
14
+ endif()
15
+
16
+ # The Node addon is built for desktop targets only; Android gets the C SDK.
17
+ option(NATIVE_DAWN_NODE "Build the Node-API addon" ON)
18
+
19
+ set(DAWN_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/.cache/dawn" CACHE PATH "Dawn checkout")
20
+
21
+ # One shared libwebgpu_dawn holds all of Dawn. The Node addon links against it
22
+ # instead of carrying a second copy, so C/C++ code and JavaScript in the same
23
+ # process share one implementation and can pass handles to each other.
24
+ set(DAWN_BUILD_MONOLITHIC_LIBRARY SHARED CACHE STRING "" FORCE)
25
+ set(DAWN_ENABLE_INSTALL ON CACHE BOOL "" FORCE)
26
+ set(DAWN_BUILD_NODE_BINDINGS ${NATIVE_DAWN_NODE} CACHE BOOL "" FORCE)
27
+ set(DAWN_ENABLE_PIC ON CACHE BOOL "" FORCE)
28
+ set(DAWN_BUILD_SAMPLES OFF CACHE BOOL "" FORCE)
29
+ set(DAWN_BUILD_TESTS OFF CACHE BOOL "" FORCE)
30
+ set(DAWN_BUILD_BENCHMARKS OFF CACHE BOOL "" FORCE)
31
+ set(DAWN_BUILD_PROTOBUF OFF CACHE BOOL "" FORCE)
32
+ set(TINT_BUILD_TESTS OFF CACHE BOOL "" FORCE)
33
+ set(TINT_BUILD_CMD_TOOLS OFF CACHE BOOL "" FORCE)
34
+ set(DAWN_USE_GLFW OFF CACHE BOOL "" FORCE)
35
+ set(DAWN_SUPPORTS_GLFW_FOR_WINDOWING OFF CACHE BOOL "" FORCE)
36
+ set(DAWN_SUPPORTS_CXX_MODULES OFF CACHE BOOL "" FORCE)
37
+ if(UNIX AND NOT APPLE AND NOT ANDROID)
38
+ set(DAWN_USE_X11 ON CACHE BOOL "" FORCE)
39
+ set(DAWN_USE_WAYLAND ON CACHE BOOL "" FORCE)
40
+ # Vulkan covers desktop Linux GPUs. OpenGL ES stays for GPUs without a
41
+ # Vulkan driver (WebGPU compatibility mode); desktop OpenGL adds nothing.
42
+ set(DAWN_ENABLE_DESKTOP_GL OFF CACHE BOOL "" FORCE)
43
+ endif()
44
+ if(WIN32)
45
+ set(DAWN_USE_BUILT_DXC ON CACHE BOOL "" FORCE)
46
+ set(DAWN_DXC_ENABLE_ASSERTS_IN_NDEBUG OFF CACHE BOOL "" FORCE)
47
+ endif()
48
+
49
+ add_subdirectory("${DAWN_SOURCE_DIR}" dawn)
50
+
51
+ if(APPLE)
52
+ set_target_properties(webgpu_dawn PROPERTIES INSTALL_RPATH "@loader_path" BUILD_WITH_INSTALL_RPATH ON)
53
+ elseif(UNIX)
54
+ set_target_properties(webgpu_dawn PROPERTIES INSTALL_RPATH "$ORIGIN" BUILD_WITH_INSTALL_RPATH ON)
55
+ endif()
56
+ if(ANDROID)
57
+ # 16 KB pages, required for apps targeting Android 15 and newer devices.
58
+ target_link_options(webgpu_dawn PRIVATE "-Wl,-z,max-page-size=16384")
59
+ endif()
60
+
61
+ if(NATIVE_DAWN_NODE)
62
+ # Swap the addon's static Dawn for the shared library. Its calls go straight
63
+ # to webgpu_dawn's exports, so the dawn_proc table is dropped too (see the
64
+ # Module.cpp hook in scripts/build.mjs).
65
+ get_target_property(node_libs dawn_node LINK_LIBRARIES)
66
+ list(REMOVE_ITEM node_libs dawn_native dawn_proc)
67
+ list(APPEND node_libs webgpu_dawn)
68
+ set_property(TARGET dawn_node PROPERTY LINK_LIBRARIES "${node_libs}")
69
+ foreach(target dawn_node dawn_node_binding dawn_node_interop)
70
+ target_compile_definitions(${target} PRIVATE WGPU_SHARED_LIBRARY DAWN_NATIVE_SHARED_LIBRARY)
71
+ endforeach()
72
+
73
+ target_sources(dawn_node PRIVATE src/node/extensions.cpp)
74
+ target_include_directories(dawn_node PRIVATE src/node include)
75
+
76
+ if(APPLE)
77
+ set_target_properties(dawn_node PROPERTIES INSTALL_RPATH "@loader_path" BUILD_WITH_INSTALL_RPATH ON)
78
+ elseif(UNIX)
79
+ set_target_properties(dawn_node PROPERTIES INSTALL_RPATH "$ORIGIN" BUILD_WITH_INSTALL_RPATH ON)
80
+ endif()
81
+
82
+ # The addon sits next to the shared library: lib/ on Linux and macOS, bin/ on
83
+ # Windows, where Node resolves an addon's DLLs from the addon's own directory.
84
+ install(TARGETS dawn_node
85
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
86
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
87
+ endif()
88
+
89
+ install(DIRECTORY include/native_dawn DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
90
+ install(FILES "${DAWN_SOURCE_DIR}/src/dawn/dawn.json" DESTINATION share/native-dawn)
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Luis Montes
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.
package/NOTICE ADDED
@@ -0,0 +1,9 @@
1
+ native-dawn builds Google's Dawn WebGPU implementation and its Node-API
2
+ bindings. Dawn source: https://dawn.googlesource.com/dawn
3
+ The exact revision is recorded in upstream.json. Binary archives include the
4
+ Dawn license and the licenses of its bundled dependencies under
5
+ share/native-dawn/licenses.
6
+
7
+ The handling of @kmamal/sdl window handles follows Konstantin M's
8
+ @kmamal/gpu (MIT): https://github.com/kmamal/gpu, at the revision recorded
9
+ in upstream.json.
package/README.md CHANGED
@@ -1,3 +1,256 @@
1
- # Temporary Holding Version
1
+ # native-dawn
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Prebuilt [Dawn](https://dawn.googlesource.com/dawn), Google's WebGPU implementation, packaged for two kinds of users:
4
+
5
+ - **Native code.** A shared `webgpu_dawn` library, the WebGPU C and C++ headers, and a CMake package, so a C or C++ program can use WebGPU without building Dawn.
6
+ - **Node.js.** Dawn's own Node-API bindings, built against that same library, plus a swap chain for native windows.
7
+
8
+ It is the WebGPU counterpart of [native-gles](https://github.com/monteslu/native-gles): the native layer, kept separate from the browser-style JavaScript API that sits on top of it (webgpu-node, as [webgl-node](https://github.com/monteslu/webgl-node) is for native-gles).
9
+
10
+ Because the Node addon links the shared library instead of carrying its own copy of Dawn, JavaScript and native code in one process use the same Dawn. A `GPUDevice` created in JavaScript can be handed to a C++ addon as a `WGPUDevice`.
11
+
12
+ ## Install
13
+
14
+ ```sh
15
+ npm install native-dawn
16
+ ```
17
+
18
+ Installation downloads the archive for your platform from this repository's GitHub releases, checks its SHA-256 and version, and loads the addon once to make sure it works. If that fails (an unsupported platform, no network, a C library too old for the binary), the install prints a warning and still succeeds, so packages that depend on native-dawn install everywhere; importing native-dawn then throws. A corrupt or mismatched archive is never installed. Set `NATIVE_DAWN_STRICT_INSTALL=1` to make install failures fatal.
19
+
20
+ | Platform | Architectures | Backend |
21
+ | --- | --- | --- |
22
+ | Linux (glibc 2.34+: Ubuntu 22.04, Debian 12 and newer) | x64, arm64 | Vulkan |
23
+ | macOS 11+ | x64, arm64 | Metal |
24
+ | Windows | x64, arm64 | D3D12 |
25
+ | Android 8.0+ (API 26), C SDK only | arm64 | Vulkan, OpenGL ES |
26
+
27
+ Linux builds support X11 and Wayland windows and also include the OpenGL ES backend (see [OpenGL ES](#opengl-es-compatibility-mode)). The Node addon needs Node.js 22 or newer.
28
+
29
+ ## Using it from Node
30
+
31
+ ```js
32
+ import { create, globals } from 'native-dawn'
33
+
34
+ const gpu = create([]) // Dawn instance flags, e.g. ['backend=vulkan']
35
+ const adapter = await gpu.requestAdapter()
36
+ const device = await adapter.requestDevice()
37
+
38
+ const buffer = device.createBuffer({ size: 16, usage: globals.GPUBufferUsage.COPY_DST | globals.GPUBufferUsage.MAP_READ })
39
+ ```
40
+
41
+ `gpu`, `adapter` and `device` are standard WebGPU objects. `globals` holds the WebGPU classes and constants (`GPUBufferUsage`, `GPUDevice`, `GPUValidationError`, ...). Nothing is installed on `globalThis` or `navigator`; webgpu-node does that, along with a canvas and `GPUCanvasContext`.
42
+
43
+ Adapters and devices stay valid even if the `gpu` object is garbage collected. Call `device.destroy()` when you are done to release GPU memory promptly. Only pending asynchronous work (a `mapAsync`, `onSubmittedWorkDone`, pipeline creation and the like) keeps Node running; an idle device does not, and it costs no CPU. Dawn notices a lost device (a driver reset, for example) on the next WebGPU call or asynchronous operation, and `device.lost` settles then.
44
+
45
+ ### Presenting to a window
46
+
47
+ `NativeSurface` is a swap chain for a native window. With [@kmamal/sdl](https://github.com/kmamal/node-sdl):
48
+
49
+ ```js
50
+ import sdl from '@kmamal/sdl'
51
+ import { create, NativeSurface, globals } from 'native-dawn'
52
+
53
+ const window = sdl.video.createWindow({ width: 640, height: 480, webgpu: true })
54
+ const gpu = create([])
55
+ const device = await (await gpu.requestAdapter()).requestDevice()
56
+
57
+ const surface = new NativeSurface(device, window.native.gpu, sdl.info.drivers.video.current)
58
+ surface.configure({
59
+ width: window.pixelWidth,
60
+ height: window.pixelHeight,
61
+ format: gpu.getPreferredCanvasFormat(),
62
+ usage: globals.GPUTextureUsage.RENDER_ATTACHMENT,
63
+ })
64
+
65
+ // Each frame:
66
+ const texture = surface.getCurrentTexture()
67
+ // ... render into texture, submit ...
68
+ surface.present()
69
+ ```
70
+
71
+ The third argument matters on Linux. @kmamal/sdl 0.11 hands over the same two pointers for X11 and Wayland windows without saying which they are, so `NativeSurface` needs the video driver name. Without it you get an error instead of a guess.
72
+
73
+ Windows from other libraries work too. Pass the native handles as BigInts:
74
+
75
+ ```js
76
+ new NativeSurface(device, { kind: 'xlib', display, xid })
77
+ new NativeSurface(device, { kind: 'wayland', display, handle }) // wl_display*, wl_surface*
78
+ new NativeSurface(device, { kind: 'win32', display, handle }) // HINSTANCE, HWND
79
+ new NativeSurface(device, { kind: 'metal-layer', handle }) // CAMetalLayer*
80
+ ```
81
+
82
+ `configure` accepts `alphaMode` (`opaque`, `premultiplied`), `presentMode` (`fifo` by default, or `fifoRelaxed`, `immediate`, `mailbox`) and `viewFormats`, and checks each against what the surface supports. Call `configure` again after the window resizes. The window belongs to you: call `surface.destroy()` before destroying it.
83
+
84
+ ### Sharing a device with native code
85
+
86
+ ```js
87
+ import { deviceHandle } from 'native-dawn'
88
+ const pointer = deviceHandle(device) // BigInt: the WGPUDevice
89
+ ```
90
+
91
+ The pointer is valid while the `GPUDevice` is alive. Native code that holds on to it longer should call `wgpuDeviceAddRef`. This works because the addon and your native code load the same `webgpu_dawn` library; build your addon against native-dawn's headers and library (see below).
92
+
93
+ ## Using it from C or C++
94
+
95
+ The package holds a normal install prefix:
96
+
97
+ ```
98
+ include/webgpu/webgpu.h WebGPU C API
99
+ include/webgpu/webgpu_cpp.h C++ wrapper
100
+ include/dawn/... Dawn extensions
101
+ include/native_dawn/window.h window helpers (see below)
102
+ lib/libwebgpu_dawn.so .dylib on macOS; bin/webgpu_dawn.dll and lib/webgpu_dawn.lib on Windows
103
+ lib/cmake/Dawn/ CMake package: dawn::webgpu_dawn
104
+ share/native-dawn/dawn.json Dawn's description of the whole API
105
+ share/native-dawn/licenses/ licenses for everything in the binaries
106
+ ```
107
+
108
+ Point CMake at it:
109
+
110
+ ```sh
111
+ cmake -B build -DCMAKE_PREFIX_PATH="$(npx native-dawn prefix)"
112
+ ```
113
+
114
+ ```cmake
115
+ find_package(Dawn REQUIRED)
116
+ target_link_libraries(my_app PRIVATE dawn::webgpu_dawn)
117
+ ```
118
+
119
+ You don't need npm for this. Each GitHub release has a `native-dawn-v<version>-<platform>-<arch>.tar.gz` that unpacks to the same prefix. On Linux and macOS the library is found through the executable's rpath when CMake links it. On Windows, put `webgpu_dawn.dll`, `dxcompiler.dll` and `dxil.dll` next to your executable or on `PATH`.
120
+
121
+ [test/c/compute.c](test/c/compute.c) is a complete program: adapter and device setup, a compute shader, an offscreen render pass, and reading both back.
122
+
123
+ ### Window helpers
124
+
125
+ `native_dawn/window.h` is header-only and needs nothing but `webgpu.h`. Describe the window, get a surface:
126
+
127
+ ```c
128
+ #include <native_dawn/window.h>
129
+
130
+ NativeDawnWindow window = { NATIVE_DAWN_WINDOW_XLIB, display, NULL, xid };
131
+ WGPUSurface surface = nativeDawnCreateSurface(instance, &window);
132
+ ```
133
+
134
+ If you use SDL2, `native_dawn/sdl2.h` fills that in from an `SDL_Window*` for X11, Wayland, Windows and macOS (create the window with `SDL_WINDOW_METAL` on macOS):
135
+
136
+ ```c
137
+ #include <native_dawn/sdl2.h>
138
+
139
+ NativeDawnSDL2Window native;
140
+ if (nativeDawnSDL2Window(sdlWindow, &native) != 0) { /* SDL_GetError() */ }
141
+ WGPUSurface surface = nativeDawnCreateSurface(instance, &native.window);
142
+ /* ... */
143
+ wgpuSurfaceRelease(surface);
144
+ nativeDawnSDL2Release(&native);
145
+ ```
146
+
147
+ [test/c/sdl2_window.c](test/c/sdl2_window.c) configures a surface, presents frames and handles a resize.
148
+
149
+ ### OpenGL ES (compatibility mode)
150
+
151
+ GPUs without a Vulkan driver, such as the Mali-G31 in many Allwinner H700 handhelds, can still run WebGPU through Dawn's OpenGL ES backend. It needs OpenGL ES 3.1 or newer; ES 3.0 lacks the compute shaders and storage buffers that WebGPU requires. WGSL vertex, fragment and compute shaders are all translated to GLSL ES.
152
+
153
+ This backend implements WebGPU's compatibility mode, a reduced feature level for OpenGL ES 3.1 and Direct3D 11 class hardware. Ask for it explicitly:
154
+
155
+ ```c
156
+ WGPURequestAdapterOptions options = WGPU_REQUEST_ADAPTER_OPTIONS_INIT;
157
+ options.featureLevel = WGPUFeatureLevel_Compatibility;
158
+ options.backendType = WGPUBackendType_OpenGLES; // optional: prefer it over Vulkan
159
+ ```
160
+
161
+ ```js
162
+ const adapter = await gpu.requestAdapter({ featureLevel: 'compatibility' })
163
+ ```
164
+
165
+ Code written for compatibility mode also runs on full WebGPU. On Linux the backend reaches the driver through EGL; without an X11 or Wayland session, set `EGL_PLATFORM=surfaceless`.
166
+
167
+ Known driver bug: with Mesa 26.0.8 (the only version seen so far, on radeonsi and llvmpipe), creating a render pipeline on this backend crashes inside Mesa whenever the GL program comes from Mesa's on-disk shader cache, so the first run works and later runs segfault. Set `MESA_SHADER_CACHE_DISABLE=true` until Mesa is fixed. The Vulkan backend is not affected. Presenting needs an X11, Wayland or Android window: Dawn has no surface type for direct KMS/DRM output, so on such systems this mode is limited to compute and offscreen rendering.
168
+
169
+ ## Android
170
+
171
+ The Android archive (`android-arm64`) holds the C SDK: `libwebgpu_dawn.so`, headers, the CMake package and `dawn.json`. There is no Node addon for Android, and npm installs don't fetch these; download them from the GitHub release. The library uses the static C++ runtime, needs only system libraries, and is aligned for 16 KB pages.
172
+
173
+ With the NDK and CMake:
174
+
175
+ ```sh
176
+ cmake -B build -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK_HOME/build/cmake/android.toolchain.cmake \
177
+ -DANDROID_ABI=arm64-v8a -DANDROID_PLATFORM=android-26 \
178
+ -DDawn_DIR=/path/to/native-dawn-android-arm64/lib/cmake/Dawn
179
+ ```
180
+
181
+ Package `lib/libwebgpu_dawn.so` with your app's other native libraries (`jniLibs/arm64-v8a/`, or `IMPORTED_LOCATION` in an Android Gradle CMake build). For a window, pass the `ANativeWindow*` from your `Surface`:
182
+
183
+ ```c
184
+ NativeDawnWindow window = { NATIVE_DAWN_WINDOW_ANDROID, NULL, aNativeWindow, 0 };
185
+ WGPUSurface surface = nativeDawnCreateSurface(instance, &window);
186
+ ```
187
+
188
+ or use `native_dawn/sdl2.h` with an SDL2 Android app. Vulkan is used where the device has it; OpenGL ES 3.1+ is available through compatibility mode. Kotlin and Java apps are better served by Google's `androidx.webgpu` library, which is built from Dawn too.
189
+
190
+ ### Bindings for other runtimes
191
+
192
+ `share/native-dawn/dawn.json` is the file Dawn generates its own headers and wrappers from: every object, method, struct, enum and callback in the API. A binding for another JavaScript engine or language can be generated from it rather than written by hand. `npx native-dawn dawn-json` prints its path.
193
+
194
+ ## Building from source
195
+
196
+ You need Git, Python 3, Go 1.26+, CMake 3.22+, a C++20 compiler, and Ninja (Linux and macOS). On Windows use Visual Studio 2022 with the C++ tools and a recent Windows SDK; run the build from a developer command prompt to get Ninja and compiler caching, or from a plain prompt to use the Visual Studio generator.
197
+
198
+ Linux packages (Debian/Ubuntu names):
199
+
200
+ ```sh
201
+ sudo apt-get install ninja-build libx11-dev libx11-xcb-dev libxcb1-dev libxrandr-dev libxinerama-dev \
202
+ libxcursor-dev libxi-dev libwayland-dev libvulkan1 mesa-vulkan-drivers
203
+ ```
204
+
205
+ Then:
206
+
207
+ ```sh
208
+ npm ci --ignore-scripts
209
+ npm run build
210
+ ```
211
+
212
+ The build fetches the Dawn revision pinned in `upstream.json` and the dependencies Dawn pins for it (no depot_tools), builds `webgpu_dawn` and the addon, and installs the result to `dist/<platform>-<arch>`. Sources go in `.cache/dawn`, build files in `build/<platform>-<arch>`. A first build takes a while; Dawn is large.
213
+
214
+ Android builds cross-compile from Linux, macOS or Windows with the NDK (r27 or newer) and need no Go:
215
+
216
+ ```sh
217
+ NATIVE_DAWN_TARGET=android-arm64 npm run build
218
+ ```
219
+
220
+ The NDK is found through `ANDROID_NDK_HOME`, `ANDROID_NDK_ROOT`, or the newest one under `$ANDROID_HOME/ndk`.
221
+
222
+ Environment variables:
223
+
224
+ | Variable | Effect |
225
+ | --- | --- |
226
+ | `NATIVE_DAWN_TARGET` | `android-arm64` to cross-compile; default is this machine |
227
+ | `NATIVE_DAWN_BUILD_JOBS` | Parallel compile jobs (default: up to 8) |
228
+ | `NATIVE_DAWN_COMPILER_LAUNCHER` | Compiler launcher such as `sccache` or `ccache` |
229
+ | `NATIVE_DAWN_BUILD_FROM_SOURCE=1` | Make `npm install` build instead of downloading |
230
+ | `NATIVE_DAWN_BINARY=/path/to/archive.tar.gz` | Install a local archive (with its `.sha256` beside it) |
231
+ | `NATIVE_DAWN_SKIP_INSTALL=1` | Skip the install step entirely |
232
+ | `NATIVE_DAWN_STRICT_INSTALL=1` | Fail `npm install` when no native build can be installed |
233
+
234
+ ## Tests
235
+
236
+ ```sh
237
+ npm test # Node: compute, mapping, validation, rendering, surface errors, installer
238
+ npm run test:types # TypeScript declarations
239
+ npm run test:c # C programs built against dist/ with find_package(Dawn)
240
+ NATIVE_DAWN_TEST_SDL2=1 npm run test:c # plus the SDL2 window test
241
+ npm run test:window # Node + @kmamal/sdl window (needs a display)
242
+ npm run test:package # pack, install into a scratch project, run from Node and C
243
+ NATIVE_DAWN_TARGET=android-arm64 npm run test:android # C test on the device or emulator adb sees
244
+ ```
245
+
246
+ Tests need a working adapter, hardware or software (Mesa's lavapipe on Linux, WARP on Windows), and fail without one rather than skipping. `NATIVE_DAWN_TEST_BACKEND=vulkan|metal|d3d12|opengles` picks a backend, `NATIVE_DAWN_TEST_COMPAT=1` requests compatibility mode (required for `opengles`), and `NATIVE_DAWN_TEST_FALLBACK=1` asks for a fallback adapter. The Android emulator only offers OpenGL ES 3.0, so it tests Vulkan; on Linux, `EGL_PLATFORM=surfaceless NATIVE_DAWN_TEST_BACKEND=opengles NATIVE_DAWN_TEST_COMPAT=1 npm run test:c` tests OpenGL ES. On headless Linux, run the window tests under `xvfb-run -a` with `SDL_VIDEODRIVER=x11`.
247
+
248
+ ## CI and releases
249
+
250
+ [CI](.github/workflows/ci.yml) builds the six desktop targets on native runners and runs every suite above on each one, on Node 22 and 24, against the runner's software adapter. Linux also runs the C tests on OpenGL ES in compatibility mode. The window tests run everywhere except Windows ARM64, which has no @kmamal/sdl build. Android is cross-compiled on Linux and its C tests run on an x86_64 Android emulator, through Android's ARM translation.
251
+
252
+ Pushing a `v<version>` tag that matches `package.json` runs the same jobs, and if all seven pass, creates a GitHub release with the seven archives and their checksums. npm publishing is a separate, manual step after that, since `npm install` downloads from the release.
253
+
254
+ ## License
255
+
256
+ MIT for this package. Dawn and its dependencies keep their own licenses; see [NOTICE](NOTICE) and `share/native-dawn/licenses` in each archive.
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env node
2
+ // Prints install locations for build systems, e.g.
3
+ // cmake -DCMAKE_PREFIX_PATH="$(npx native-dawn prefix)" ...
4
+ import { paths } from '../paths.js'
5
+
6
+ const keys = { prefix: 'prefix', include: 'include', lib: 'lib', bin: 'bin', cmake: 'cmake', library: 'library', addon: 'addon', 'dawn-json': 'dawnJson', licenses: 'licenses' }
7
+ const name = process.argv[2]
8
+ if (!keys[name]) {
9
+ console.error(`usage: native-dawn <${Object.keys(keys).join('|')}>`)
10
+ process.exit(2)
11
+ }
12
+ console.log(paths[keys[name]])
@@ -0,0 +1,88 @@
1
+ /*
2
+ * native-dawn: fill a NativeDawnWindow from an SDL2 window.
3
+ *
4
+ * Header-only and optional; include it only when the program already uses
5
+ * SDL2 (X11, Wayland, Windows, macOS, Android). On macOS, create the window
6
+ * with SDL_WINDOW_METAL. Call nativeDawnSDL2Release after releasing the
7
+ * surface.
8
+ */
9
+ #ifndef NATIVE_DAWN_SDL2_H_
10
+ #define NATIVE_DAWN_SDL2_H_
11
+
12
+ #include <SDL.h>
13
+ #include <SDL_syswm.h>
14
+ #if defined(SDL_VIDEO_DRIVER_COCOA) || defined(SDL_VIDEO_DRIVER_UIKIT)
15
+ #include <SDL_metal.h>
16
+ #endif
17
+ #include "native_dawn/window.h"
18
+
19
+ #ifdef __cplusplus
20
+ extern "C" {
21
+ #endif
22
+
23
+ typedef struct NativeDawnSDL2Window {
24
+ NativeDawnWindow window;
25
+ void* metalView; /* SDL_MetalView created for Cocoa windows */
26
+ } NativeDawnSDL2Window;
27
+
28
+ /* Returns 0 on success, or -1 with SDL_GetError() describing the failure. */
29
+ static inline int nativeDawnSDL2Window(SDL_Window* sdlWindow, NativeDawnSDL2Window* out) {
30
+ SDL_SysWMinfo info;
31
+ SDL_zerop(out);
32
+ SDL_VERSION(&info.version);
33
+ if (!SDL_GetWindowWMInfo(sdlWindow, &info)) return -1;
34
+ switch (info.subsystem) {
35
+ #if defined(SDL_VIDEO_DRIVER_X11)
36
+ case SDL_SYSWM_X11:
37
+ out->window.kind = NATIVE_DAWN_WINDOW_XLIB;
38
+ out->window.display = info.info.x11.display;
39
+ out->window.xid = (uint64_t)info.info.x11.window;
40
+ return 0;
41
+ #endif
42
+ #if defined(SDL_VIDEO_DRIVER_WAYLAND)
43
+ case SDL_SYSWM_WAYLAND:
44
+ out->window.kind = NATIVE_DAWN_WINDOW_WAYLAND;
45
+ out->window.display = info.info.wl.display;
46
+ out->window.handle = info.info.wl.surface;
47
+ return 0;
48
+ #endif
49
+ #if defined(SDL_VIDEO_DRIVER_WINDOWS)
50
+ case SDL_SYSWM_WINDOWS:
51
+ out->window.kind = NATIVE_DAWN_WINDOW_WIN32;
52
+ out->window.display = info.info.win.hinstance;
53
+ out->window.handle = info.info.win.window;
54
+ return 0;
55
+ #endif
56
+ #if defined(SDL_VIDEO_DRIVER_ANDROID)
57
+ case SDL_SYSWM_ANDROID:
58
+ out->window.kind = NATIVE_DAWN_WINDOW_ANDROID;
59
+ out->window.handle = info.info.android.window;
60
+ return 0;
61
+ #endif
62
+ #if defined(SDL_VIDEO_DRIVER_COCOA) || defined(SDL_VIDEO_DRIVER_UIKIT)
63
+ case SDL_SYSWM_COCOA:
64
+ case SDL_SYSWM_UIKIT:
65
+ out->metalView = SDL_Metal_CreateView(sdlWindow);
66
+ if (!out->metalView) return -1;
67
+ out->window.kind = NATIVE_DAWN_WINDOW_METAL_LAYER;
68
+ out->window.handle = SDL_Metal_GetLayer((SDL_MetalView)out->metalView);
69
+ return 0;
70
+ #endif
71
+ default:
72
+ SDL_SetError("native-dawn: unsupported SDL window subsystem %d", (int)info.subsystem);
73
+ return -1;
74
+ }
75
+ }
76
+
77
+ static inline void nativeDawnSDL2Release(NativeDawnSDL2Window* window) {
78
+ #if defined(SDL_VIDEO_DRIVER_COCOA) || defined(SDL_VIDEO_DRIVER_UIKIT)
79
+ if (window->metalView) SDL_Metal_DestroyView((SDL_MetalView)window->metalView);
80
+ #endif
81
+ window->metalView = NULL;
82
+ }
83
+
84
+ #ifdef __cplusplus
85
+ }
86
+ #endif
87
+
88
+ #endif /* NATIVE_DAWN_SDL2_H_ */
@@ -0,0 +1,102 @@
1
+ /*
2
+ * native-dawn: create a WebGPU surface from a native window.
3
+ *
4
+ * Header-only. Include it after linking dawn::webgpu_dawn; it uses nothing
5
+ * but the WebGPU C API. The caller owns the window and must release the
6
+ * surface (wgpuSurfaceRelease) before destroying it.
7
+ */
8
+ #ifndef NATIVE_DAWN_WINDOW_H_
9
+ #define NATIVE_DAWN_WINDOW_H_
10
+
11
+ #include <stddef.h>
12
+ #include <stdint.h>
13
+ #include <webgpu/webgpu.h>
14
+
15
+ #ifdef __cplusplus
16
+ extern "C" {
17
+ #endif
18
+
19
+ typedef enum NativeDawnWindowKind {
20
+ NATIVE_DAWN_WINDOW_XLIB = 1, /* display: Display*, xid: Window */
21
+ NATIVE_DAWN_WINDOW_WAYLAND = 2, /* display: wl_display*, handle: wl_surface* */
22
+ NATIVE_DAWN_WINDOW_WIN32 = 3, /* display: HINSTANCE, handle: HWND */
23
+ NATIVE_DAWN_WINDOW_METAL_LAYER = 4, /* handle: CAMetalLayer* */
24
+ NATIVE_DAWN_WINDOW_ANDROID = 5 /* handle: ANativeWindow* */
25
+ } NativeDawnWindowKind;
26
+
27
+ typedef struct NativeDawnWindow {
28
+ NativeDawnWindowKind kind;
29
+ void* display;
30
+ void* handle;
31
+ uint64_t xid;
32
+ } NativeDawnWindow;
33
+
34
+ /* Returns NULL when the window is complete, otherwise what is missing. */
35
+ static inline const char* nativeDawnWindowError(const NativeDawnWindow* window) {
36
+ if (!window) return "window is NULL";
37
+ switch (window->kind) {
38
+ case NATIVE_DAWN_WINDOW_XLIB:
39
+ return window->display && window->xid ? NULL : "Xlib window needs display and xid";
40
+ case NATIVE_DAWN_WINDOW_WAYLAND:
41
+ return window->display && window->handle ? NULL : "Wayland window needs display and handle (wl_surface)";
42
+ case NATIVE_DAWN_WINDOW_WIN32:
43
+ return window->handle ? NULL : "Win32 window needs handle (HWND)";
44
+ case NATIVE_DAWN_WINDOW_METAL_LAYER:
45
+ return window->handle ? NULL : "Metal window needs handle (CAMetalLayer)";
46
+ case NATIVE_DAWN_WINDOW_ANDROID:
47
+ return window->handle ? NULL : "Android window needs handle (ANativeWindow)";
48
+ }
49
+ return "unknown window kind";
50
+ }
51
+
52
+ /*
53
+ * Creates a surface for the window, or returns NULL if the window description
54
+ * is incomplete. A kind the platform does not support yields an error surface
55
+ * from Dawn, which fails wgpuSurfaceGetCapabilities.
56
+ */
57
+ static inline WGPUSurface nativeDawnCreateSurface(WGPUInstance instance, const NativeDawnWindow* window) {
58
+ WGPUSurfaceDescriptor descriptor = WGPU_SURFACE_DESCRIPTOR_INIT;
59
+ if (!instance || nativeDawnWindowError(window)) return NULL;
60
+ switch (window->kind) {
61
+ case NATIVE_DAWN_WINDOW_XLIB: {
62
+ WGPUSurfaceSourceXlibWindow source = WGPU_SURFACE_SOURCE_XLIB_WINDOW_INIT;
63
+ source.display = window->display;
64
+ source.window = window->xid;
65
+ descriptor.nextInChain = &source.chain;
66
+ return wgpuInstanceCreateSurface(instance, &descriptor);
67
+ }
68
+ case NATIVE_DAWN_WINDOW_WAYLAND: {
69
+ WGPUSurfaceSourceWaylandSurface source = WGPU_SURFACE_SOURCE_WAYLAND_SURFACE_INIT;
70
+ source.display = window->display;
71
+ source.surface = window->handle;
72
+ descriptor.nextInChain = &source.chain;
73
+ return wgpuInstanceCreateSurface(instance, &descriptor);
74
+ }
75
+ case NATIVE_DAWN_WINDOW_WIN32: {
76
+ WGPUSurfaceSourceWindowsHWND source = WGPU_SURFACE_SOURCE_WINDOWS_HWND_INIT;
77
+ source.hinstance = window->display;
78
+ source.hwnd = window->handle;
79
+ descriptor.nextInChain = &source.chain;
80
+ return wgpuInstanceCreateSurface(instance, &descriptor);
81
+ }
82
+ case NATIVE_DAWN_WINDOW_METAL_LAYER: {
83
+ WGPUSurfaceSourceMetalLayer source = WGPU_SURFACE_SOURCE_METAL_LAYER_INIT;
84
+ source.layer = window->handle;
85
+ descriptor.nextInChain = &source.chain;
86
+ return wgpuInstanceCreateSurface(instance, &descriptor);
87
+ }
88
+ case NATIVE_DAWN_WINDOW_ANDROID: {
89
+ WGPUSurfaceSourceAndroidNativeWindow source = WGPU_SURFACE_SOURCE_ANDROID_NATIVE_WINDOW_INIT;
90
+ source.window = window->handle;
91
+ descriptor.nextInChain = &source.chain;
92
+ return wgpuInstanceCreateSurface(instance, &descriptor);
93
+ }
94
+ }
95
+ return NULL;
96
+ }
97
+
98
+ #ifdef __cplusplus
99
+ }
100
+ #endif
101
+
102
+ #endif /* NATIVE_DAWN_WINDOW_H_ */
package/index.d.ts ADDED
@@ -0,0 +1,56 @@
1
+ /// <reference types="@webgpu/types" />
2
+ import type { NativeDawnPaths } from './paths.js'
3
+
4
+ export { paths } from './paths.js'
5
+ export type { NativeDawnPaths }
6
+
7
+ /** An X11, Wayland, Win32 or Metal window, with pointers as BigInt. */
8
+ export interface NativeWindow {
9
+ kind: 'xlib' | 'wayland' | 'win32' | 'metal-layer'
10
+ /** Display* (xlib), wl_display* (wayland) or HINSTANCE (win32). */
11
+ display?: bigint | number
12
+ /** wl_surface* (wayland), HWND (win32) or CAMetalLayer* (metal-layer). */
13
+ handle?: bigint | number
14
+ /** X11 Window id (xlib). */
15
+ xid?: bigint | number
16
+ }
17
+
18
+ export interface NativeSurfaceConfiguration {
19
+ width: number
20
+ height: number
21
+ format: GPUTextureFormat
22
+ usage: number
23
+ alphaMode?: 'opaque' | 'premultiplied'
24
+ presentMode?: 'fifo' | 'fifoRelaxed' | 'immediate' | 'mailbox'
25
+ viewFormats?: GPUTextureFormat[]
26
+ }
27
+
28
+ /** A swap chain for a native window. */
29
+ export declare class NativeSurface {
30
+ /**
31
+ * @param window An @kmamal/sdl `window.native.gpu` buffer, or a NativeWindow.
32
+ * @param videoDriver For SDL buffers on Linux: 'x11' or 'wayland'
33
+ * (sdl.info.drivers.video.current).
34
+ */
35
+ constructor(device: GPUDevice, window: Buffer | NativeWindow, videoDriver?: string)
36
+ configure(configuration: NativeSurfaceConfiguration): void
37
+ getCurrentTexture(): GPUTexture
38
+ present(): void
39
+ unconfigure(): void
40
+ destroy(): void
41
+ }
42
+
43
+ /** Dawn instance flags as `key=value` strings, e.g. `backend=vulkan`. */
44
+ export declare function create(flags: string[]): GPU
45
+ /** The WGPUDevice pointer behind a GPUDevice, for native code in this process. */
46
+ export declare function deviceHandle(device: GPUDevice): bigint
47
+ /** WebGPU interface objects and constants (GPUBufferUsage, GPUDevice, ...). */
48
+ export declare const globals: Record<string, unknown>
49
+
50
+ declare const addon: {
51
+ create: typeof create
52
+ globals: typeof globals
53
+ NativeSurface: typeof NativeSurface
54
+ deviceHandle: typeof deviceHandle
55
+ }
56
+ export default addon
package/index.js ADDED
@@ -0,0 +1,13 @@
1
+ import { createRequire } from 'node:module'
2
+ import { paths } from './paths.js'
3
+
4
+ let addon
5
+ try {
6
+ addon = createRequire(import.meta.url)(paths.addon)
7
+ } catch (cause) {
8
+ throw new Error(`native-dawn: cannot load the ${paths.target} addon from ${paths.addon}. Run npm install, or npm run build in a source checkout. ${cause.message}`, { cause })
9
+ }
10
+
11
+ export { paths }
12
+ export const { create, globals, NativeSurface, deviceHandle } = addon
13
+ export default addon