@simulatte/webgpu-doe 0.1.0 → 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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@simulatte/webgpu-doe",
3
- "version": "0.1.0",
4
- "description": "Native Doe WebGPU runtime for Node.js — Zig-built Metal/Vulkan/D3D12 backends via N-API",
3
+ "version": "0.1.1",
4
+ "description": "Doe WebGPU runtime for Node.js — native Zig GPU backends (Metal, Vulkan) via N-API",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
7
7
  "exports": {
@@ -12,13 +12,18 @@
12
12
  "src/index.js",
13
13
  "native/",
14
14
  "binding.gyp",
15
+ "prebuilds/",
15
16
  "README.md"
16
17
  ],
17
18
  "scripts": {
18
- "install": "node-gyp rebuild",
19
- "build": "node-gyp rebuild"
19
+ "build:addon": "node-gyp rebuild"
20
20
  },
21
- "gypfile": true,
21
+ "os": [
22
+ "darwin"
23
+ ],
24
+ "cpu": [
25
+ "arm64"
26
+ ],
22
27
  "keywords": [
23
28
  "webgpu",
24
29
  "doe",
@@ -26,11 +31,11 @@
26
31
  "compute",
27
32
  "headless",
28
33
  "node",
29
- "bun",
30
34
  "native",
31
35
  "napi",
32
36
  "metal",
33
- "vulkan"
37
+ "vulkan",
38
+ "zig"
34
39
  ],
35
40
  "repository": {
36
41
  "type": "git",
package/src/index.js CHANGED
@@ -11,13 +11,18 @@ const DOE_LIB_PATH = resolveDoeLibraryPath();
11
11
  let libraryLoaded = false;
12
12
 
13
13
  function loadAddon() {
14
+ const prebuildPath = resolve(__dirname, '..', 'prebuilds', `${process.platform}-${process.arch}`, 'doe_napi.node');
14
15
  try {
15
- return require('../build/Release/doe_napi.node');
16
+ return require(prebuildPath);
16
17
  } catch {
17
18
  try {
18
- return require('../build/Debug/doe_napi.node');
19
+ return require('../build/Release/doe_napi.node');
19
20
  } catch {
20
- return null;
21
+ try {
22
+ return require('../build/Debug/doe_napi.node');
23
+ } catch {
24
+ return null;
25
+ }
21
26
  }
22
27
  }
23
28
  }
@@ -88,14 +93,16 @@ export const globals = {
88
93
  };
89
94
 
90
95
  class DoeGPUBuffer {
91
- constructor(native, instance, size, usage) {
96
+ constructor(native, instance, size, usage, queue) {
92
97
  this._native = native;
93
98
  this._instance = instance;
99
+ this._queue = queue;
94
100
  this.size = size;
95
101
  this.usage = usage;
96
102
  }
97
103
 
98
104
  async mapAsync(mode, offset = 0, size = this.size) {
105
+ if (this._queue) addon.queueFlush(this._queue);
99
106
  addon.bufferMapSync(this._instance, this._native, mode, offset, size);
100
107
  }
101
108
 
@@ -128,6 +135,10 @@ class DoeGPUComputePassEncoder {
128
135
  addon.computePassDispatchWorkgroups(this._native, x, y, z);
129
136
  }
130
137
 
138
+ dispatchWorkgroupsIndirect(indirectBuffer, indirectOffset = 0) {
139
+ addon.computePassDispatchWorkgroupsIndirect(this._native, indirectBuffer._native, indirectOffset);
140
+ }
141
+
131
142
  end() {
132
143
  addon.computePassEnd(this._native);
133
144
  }
@@ -141,6 +152,15 @@ class DoeGPUCommandEncoder {
141
152
  return new DoeGPUComputePassEncoder(pass);
142
153
  }
143
154
 
155
+ beginRenderPass(descriptor) {
156
+ const colorAttachments = (descriptor.colorAttachments || []).map((a) => ({
157
+ view: a.view._native,
158
+ clearValue: a.clearValue || { r: 0, g: 0, b: 0, a: 1 },
159
+ }));
160
+ const pass = addon.beginRenderPass(this._native, colorAttachments);
161
+ return new DoeGPURenderPassEncoder(pass);
162
+ }
163
+
144
164
  copyBufferToBuffer(src, srcOffset, dst, dstOffset, size) {
145
165
  addon.commandEncoderCopyBufferToBuffer(
146
166
  this._native, src._native, srcOffset, dst._native, dstOffset, size);
@@ -171,6 +191,52 @@ class DoeGPUQueue {
171
191
  }
172
192
  addon.queueWriteBuffer(this._native, buffer._native, bufferOffset, view);
173
193
  }
194
+
195
+ async onSubmittedWorkDone() {
196
+ addon.queueFlush(this._native);
197
+ }
198
+ }
199
+
200
+ class DoeGPURenderPassEncoder {
201
+ constructor(native) { this._native = native; }
202
+
203
+ setPipeline(pipeline) {
204
+ addon.renderPassSetPipeline(this._native, pipeline._native);
205
+ }
206
+
207
+ draw(vertexCount, instanceCount = 1, firstVertex = 0, firstInstance = 0) {
208
+ addon.renderPassDraw(this._native, vertexCount, instanceCount, firstVertex, firstInstance);
209
+ }
210
+
211
+ end() {
212
+ addon.renderPassEnd(this._native);
213
+ }
214
+ }
215
+
216
+ class DoeGPUTexture {
217
+ constructor(native) { this._native = native; }
218
+
219
+ createView(descriptor) {
220
+ const view = addon.textureCreateView(this._native);
221
+ return new DoeGPUTextureView(view);
222
+ }
223
+
224
+ destroy() {
225
+ addon.textureRelease(this._native);
226
+ this._native = null;
227
+ }
228
+ }
229
+
230
+ class DoeGPUTextureView {
231
+ constructor(native) { this._native = native; }
232
+ }
233
+
234
+ class DoeGPUSampler {
235
+ constructor(native) { this._native = native; }
236
+ }
237
+
238
+ class DoeGPURenderPipeline {
239
+ constructor(native) { this._native = native; }
174
240
  }
175
241
 
176
242
  class DoeGPUShaderModule {
@@ -179,6 +245,11 @@ class DoeGPUShaderModule {
179
245
 
180
246
  class DoeGPUComputePipeline {
181
247
  constructor(native) { this._native = native; }
248
+
249
+ getBindGroupLayout(index) {
250
+ const layout = addon.computePipelineGetBindGroupLayout(this._native, index);
251
+ return new DoeGPUBindGroupLayout(layout);
252
+ }
182
253
  }
183
254
 
184
255
  class DoeGPUBindGroupLayout {
@@ -193,17 +264,56 @@ class DoeGPUPipelineLayout {
193
264
  constructor(native) { this._native = native; }
194
265
  }
195
266
 
267
+ // Metal defaults for Apple Silicon — matches doe_device_caps.zig METAL_LIMITS.
268
+ const DOE_LIMITS = Object.freeze({
269
+ maxTextureDimension1D: 16384,
270
+ maxTextureDimension2D: 16384,
271
+ maxTextureDimension3D: 2048,
272
+ maxTextureArrayLayers: 2048,
273
+ maxBindGroups: 4,
274
+ maxBindGroupsPlusVertexBuffers: 24,
275
+ maxBindingsPerBindGroup: 1000,
276
+ maxDynamicUniformBuffersPerPipelineLayout: 8,
277
+ maxDynamicStorageBuffersPerPipelineLayout: 4,
278
+ maxSampledTexturesPerShaderStage: 16,
279
+ maxSamplersPerShaderStage: 16,
280
+ maxStorageBuffersPerShaderStage: 8,
281
+ maxStorageTexturesPerShaderStage: 4,
282
+ maxUniformBuffersPerShaderStage: 12,
283
+ maxUniformBufferBindingSize: 65536,
284
+ maxStorageBufferBindingSize: 134217728,
285
+ minUniformBufferOffsetAlignment: 256,
286
+ minStorageBufferOffsetAlignment: 32,
287
+ maxVertexBuffers: 8,
288
+ maxBufferSize: 268435456,
289
+ maxVertexAttributes: 16,
290
+ maxVertexBufferArrayStride: 2048,
291
+ maxInterStageShaderVariables: 16,
292
+ maxColorAttachments: 8,
293
+ maxColorAttachmentBytesPerSample: 32,
294
+ maxComputeWorkgroupStorageSize: 32768,
295
+ maxComputeInvocationsPerWorkgroup: 1024,
296
+ maxComputeWorkgroupSizeX: 1024,
297
+ maxComputeWorkgroupSizeY: 1024,
298
+ maxComputeWorkgroupSizeZ: 64,
299
+ maxComputeWorkgroupsPerDimension: 65535,
300
+ });
301
+
302
+ const DOE_FEATURES = Object.freeze(new Set(['shader-f16']));
303
+
196
304
  class DoeGPUDevice {
197
305
  constructor(native, instance) {
198
306
  this._native = native;
199
307
  this._instance = instance;
200
308
  const q = addon.deviceGetQueue(native);
201
309
  this.queue = new DoeGPUQueue(q);
310
+ this.limits = DOE_LIMITS;
311
+ this.features = DOE_FEATURES;
202
312
  }
203
313
 
204
314
  createBuffer(descriptor) {
205
315
  const buf = addon.createBuffer(this._native, descriptor);
206
- return new DoeGPUBuffer(buf, this._instance, descriptor.size, descriptor.usage);
316
+ return new DoeGPUBuffer(buf, this._instance, descriptor.size, descriptor.usage, this.queue._native);
207
317
  }
208
318
 
209
319
  createShaderModule(descriptor) {
@@ -223,6 +333,10 @@ class DoeGPUDevice {
223
333
  return new DoeGPUComputePipeline(native);
224
334
  }
225
335
 
336
+ async createComputePipelineAsync(descriptor) {
337
+ return this.createComputePipeline(descriptor);
338
+ }
339
+
226
340
  createBindGroupLayout(descriptor) {
227
341
  const entries = (descriptor.entries || []).map((e) => ({
228
342
  binding: e.binding,
@@ -259,6 +373,28 @@ class DoeGPUDevice {
259
373
  return new DoeGPUPipelineLayout(native);
260
374
  }
261
375
 
376
+ createTexture(descriptor) {
377
+ const native = addon.createTexture(this._native, {
378
+ format: descriptor.format || 'rgba8unorm',
379
+ width: descriptor.size?.[0] ?? descriptor.size?.width ?? descriptor.size ?? 1,
380
+ height: descriptor.size?.[1] ?? descriptor.size?.height ?? 1,
381
+ depthOrArrayLayers: descriptor.size?.[2] ?? descriptor.size?.depthOrArrayLayers ?? 1,
382
+ usage: descriptor.usage || 0,
383
+ mipLevelCount: descriptor.mipLevelCount || 1,
384
+ });
385
+ return new DoeGPUTexture(native);
386
+ }
387
+
388
+ createSampler(descriptor = {}) {
389
+ const native = addon.createSampler(this._native, descriptor);
390
+ return new DoeGPUSampler(native);
391
+ }
392
+
393
+ createRenderPipeline(descriptor) {
394
+ const native = addon.createRenderPipeline(this._native);
395
+ return new DoeGPURenderPipeline(native);
396
+ }
397
+
262
398
  createCommandEncoder(descriptor) {
263
399
  const native = addon.createCommandEncoder(this._native);
264
400
  return new DoeGPUCommandEncoder(native);
@@ -274,6 +410,8 @@ class DoeGPUAdapter {
274
410
  constructor(native, instance) {
275
411
  this._native = native;
276
412
  this._instance = instance;
413
+ this.features = DOE_FEATURES;
414
+ this.limits = DOE_LIMITS;
277
415
  }
278
416
 
279
417
  async requestDevice(descriptor) {