@buley/hexgrid-3d 1.1.1 → 3.0.0

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.
@@ -0,0 +1,140 @@
1
+ // fluid_sim.wgsl
2
+ // 3D Fluid Simulation Compute Shaders
3
+
4
+ struct FluidUniforms {
5
+ dt: f32,
6
+ width: f32,
7
+ height: f32,
8
+ depth: f32,
9
+ decay: f32,
10
+ };
11
+
12
+ @group(0) @binding(0) var<uniform> uniforms: FluidUniforms;
13
+
14
+ // Bindings for Double-Buffering (Read -> Write)
15
+ // Group 1: Velocity / Density
16
+ @group(1) @binding(0) var field_in: texture_3d<f32>;
17
+ @group(1) @binding(1) var field_out: texture_storage_3d<rgba16float, write>;
18
+
19
+ // Sampler for linear interpolation
20
+ @group(1) @binding(2) var field_sampler: sampler;
21
+
22
+ // ----------------------------------------------------------------------------
23
+ // ADVECTION
24
+ // Moves quantities along the velocity field
25
+ // ----------------------------------------------------------------------------
26
+ @group(2) @binding(0) var velocity_field: texture_3d<f32>;
27
+
28
+ @compute @workgroup_size(8, 8, 8)
29
+ fn advect(@builtin(global_invocation_id) global_id: vec3<u32>) {
30
+ let dims = vec3<f32>(uniforms.width, uniforms.height, uniforms.depth);
31
+ let coords = vec3<f32>(global_id);
32
+
33
+ if (any(coords >= dims)) { return; }
34
+
35
+ // 1. Sample velocity at current position
36
+ // Note: textureSampleLevel requires normalized coordinates [0, 1]
37
+ let uvw = (coords + 0.5) / dims;
38
+ let vel = textureSampleLevel(velocity_field, field_sampler, uvw, 0.0).xyz;
39
+
40
+ // 2. Trace back in time
41
+ let dt = uniforms.dt;
42
+ // Scale velocity back to grid units?
43
+ // Uniforms velocity tends to be in grid-units per second.
44
+ // Backtrace coordinate:
45
+ let back_pos = coords - vel * dt;
46
+
47
+ // 3. Sample field at previous position
48
+ let back_uvw = (back_pos + 0.5) / dims;
49
+ let new_val = textureSampleLevel(field_in, field_sampler, back_uvw, 0.0);
50
+
51
+ // 4. Apply decay
52
+ let decayed = new_val * uniforms.decay;
53
+
54
+ textureStore(field_out, global_id, decayed);
55
+ }
56
+
57
+ // ----------------------------------------------------------------------------
58
+ // DIFFUSION (Jacobi Iteration)
59
+ // ----------------------------------------------------------------------------
60
+ // x_new = (x_old + alpha * neighbor_sum) * inverse_beta
61
+ struct JacobiUniforms {
62
+ alpha: f32,
63
+ rBeta: f32,
64
+ };
65
+ @group(3) @binding(0) var<uniform> jacobi: JacobiUniforms;
66
+ @group(3) @binding(1) var b_field: texture_3d<f32>; // The 'b' vector in Ax=b (usually previous state or inputs)
67
+ @group(3) @binding(2) var x_field: texture_3d<f32>; // The 'x' vector (current guess)
68
+
69
+ @compute @workgroup_size(8, 8, 8)
70
+ fn diffuse(@builtin(global_invocation_id) global_id: vec3<u32>) {
71
+ let dims = vec3<i32>(uniforms.width, uniforms.height, uniforms.depth);
72
+ let pos = vec3<i32>(global_id);
73
+
74
+ if (any(pos >= dims)) { return; }
75
+
76
+ // Neighbors
77
+ let left = textureLoad(x_field, pos + vec3<i32>(-1, 0, 0), 0);
78
+ let right = textureLoad(x_field, pos + vec3<i32>(1, 0, 0), 0);
79
+ let down = textureLoad(x_field, pos + vec3<i32>(0, -1, 0), 0);
80
+ let up = textureLoad(x_field, pos + vec3<i32>(0, 1, 0), 0);
81
+ let back = textureLoad(x_field, pos + vec3<i32>(0, 0, -1), 0);
82
+ let front = textureLoad(x_field, pos + vec3<i32>(0, 0, 1), 0);
83
+
84
+ let bC = textureLoad(b_field, pos, 0);
85
+
86
+ // Jacobi step
87
+ let result = (left + right + down + up + back + front) * jacobi.alpha + bC;
88
+ let next_val = result * jacobi.rBeta;
89
+
90
+ textureStore(field_out, global_id, next_val);
91
+ }
92
+
93
+ // ----------------------------------------------------------------------------
94
+ // DIVERGENCE
95
+ // ----------------------------------------------------------------------------
96
+ @compute @workgroup_size(8, 8, 8)
97
+ fn divergence(@builtin(global_invocation_id) global_id: vec3<u32>) {
98
+ let dims = vec3<i32>(uniforms.width, uniforms.height, uniforms.depth);
99
+ let pos = vec3<i32>(global_id);
100
+
101
+ if (any(pos >= dims)) { return; }
102
+
103
+ let left = textureLoad(field_in, pos + vec3<i32>(-1, 0, 0), 0).x;
104
+ let right = textureLoad(field_in, pos + vec3<i32>(1, 0, 0), 0).x;
105
+ let down = textureLoad(field_in, pos + vec3<i32>(0, -1, 0), 0).y;
106
+ let up = textureLoad(field_in, pos + vec3<i32>(0, 1, 0), 0).y;
107
+ let back = textureLoad(field_in, pos + vec3<i32>(0, 0, -1), 0).z;
108
+ let front = textureLoad(field_in, pos + vec3<i32>(0, 0, 1), 0).z;
109
+
110
+ let div = 0.5 * ((right - left) + (up - down) + (front - back));
111
+
112
+ textureStore(field_out, global_id, vec4<f32>(div, 0.0, 0.0, 1.0));
113
+ }
114
+
115
+ // ----------------------------------------------------------------------------
116
+ // GRADIENT SUBTRACTION
117
+ // u_new = u_old - gradient(p)
118
+ // ----------------------------------------------------------------------------
119
+ @group(4) @binding(0) var pressure_field: texture_3d<f32>;
120
+
121
+ @compute @workgroup_size(8, 8, 8)
122
+ fn subtract_gradient(@builtin(global_invocation_id) global_id: vec3<u32>) {
123
+ let dims = vec3<i32>(uniforms.width, uniforms.height, uniforms.depth);
124
+ let pos = vec3<i32>(global_id);
125
+
126
+ if (any(pos >= dims)) { return; }
127
+
128
+ let pLeft = textureLoad(pressure_field, pos + vec3<i32>(-1, 0, 0), 0).x;
129
+ let pRight = textureLoad(pressure_field, pos + vec3<i32>(1, 0, 0), 0).x;
130
+ let pDown = textureLoad(pressure_field, pos + vec3<i32>(0, -1, 0), 0).x;
131
+ let pUp = textureLoad(pressure_field, pos + vec3<i32>(0, 1, 0), 0).x;
132
+ let pBack = textureLoad(pressure_field, pos + vec3<i32>(0, 0, -1), 0).x;
133
+ let pFront = textureLoad(pressure_field, pos + vec3<i32>(0, 0, 1), 0).x;
134
+
135
+ let old_vel = textureLoad(field_in, pos, 0).xyz;
136
+ let grad = vec3<f32>(pRight - pLeft, pUp - pDown, pFront - pBack) * 0.5;
137
+ let new_vel = old_vel - grad;
138
+
139
+ textureStore(field_out, global_id, vec4<f32>(new_vel, 1.0));
140
+ }
@@ -0,0 +1,99 @@
1
+ /**
2
+ * WebNN Context Manager
3
+ * Handles the creation and management of the WebNN MLContext.
4
+ * Prioritizes NPU -> GPU -> CPU.
5
+ */
6
+
7
+ export type WebNNDeviceType = 'cpu' | 'gpu' | 'npu';
8
+
9
+ export class WebNNContext {
10
+ private static instance: WebNNContext;
11
+ private context: MLContext | null = null;
12
+ private deviceType: WebNNDeviceType = 'cpu';
13
+ private isSupported: boolean = false;
14
+
15
+ private constructor() {}
16
+
17
+ static getInstance(): WebNNContext {
18
+ if (!WebNNContext.instance) {
19
+ WebNNContext.instance = new WebNNContext();
20
+ }
21
+ return WebNNContext.instance;
22
+ }
23
+
24
+ /**
25
+ * Initialize WebNN context with preferred device type.
26
+ */
27
+ async initialize(preference: WebNNDeviceType = 'npu'): Promise<boolean> {
28
+ if (typeof navigator === 'undefined' || !navigator.ml) {
29
+ console.warn('WebNN is not supported in this environment.');
30
+ this.isSupported = false;
31
+ return false;
32
+ }
33
+
34
+ try {
35
+ // Try preferred device first
36
+ this.context = await navigator.ml.createContext({ deviceType: preference });
37
+ this.deviceType = preference;
38
+ this.isSupported = true;
39
+ console.log(`WebNN initialized successfully on ${preference}`);
40
+ return true;
41
+ } catch (e) {
42
+ console.warn(`Failed to initialize WebNN on ${preference}, trying fallback chain...`, e);
43
+
44
+ // Fallback chain: NPU -> GPU -> CPU
45
+ const chain: WebNNDeviceType[] = ['npu', 'gpu', 'cpu'];
46
+ const startIndex = chain.indexOf(preference) + 1;
47
+
48
+ for (let i = startIndex; i < chain.length; i++) {
49
+ const fallback = chain[i];
50
+ try {
51
+ this.context = await navigator.ml.createContext({ deviceType: fallback });
52
+ this.deviceType = fallback;
53
+ this.isSupported = true;
54
+ console.log(`WebNN initialized successfully on fallback ${fallback}`);
55
+ return true;
56
+ } catch (err) {
57
+ console.warn(`Failed to initialize WebNN on fallback ${fallback}`, err);
58
+ }
59
+ }
60
+ }
61
+
62
+ this.isSupported = false;
63
+ return false;
64
+ }
65
+
66
+ getContext(): MLContext | null {
67
+ return this.context;
68
+ }
69
+
70
+ getDeviceType(): WebNNDeviceType {
71
+ return this.deviceType;
72
+ }
73
+
74
+ isAvailable(): boolean {
75
+ return this.isSupported && this.context !== null;
76
+ }
77
+ }
78
+
79
+ // Type definitions for WebNN (since it might not be in standard lib yet)
80
+ declare global {
81
+ interface Navigator {
82
+ ml: {
83
+ createContext(options?: { deviceType?: string }): Promise<MLContext>;
84
+ };
85
+ }
86
+
87
+ interface MLContext {
88
+ // Placeholder for MLContext methods
89
+ compute(graph: MLGraph, inputs: Record<string, ArrayBufferView>, outputs: Record<string, ArrayBufferView>): Promise<MLComputeResult>;
90
+ }
91
+
92
+ interface MLGraph {
93
+ // Opaque
94
+ }
95
+
96
+ interface MLComputeResult {
97
+ // Opaque
98
+ }
99
+ }
package/tsconfig.json CHANGED
@@ -18,8 +18,9 @@
18
18
  "baseUrl": ".",
19
19
  "paths": {
20
20
  "@/*": ["src/*"]
21
- }
21
+ },
22
+ "types": ["@webgpu/types"]
22
23
  },
23
- "include": ["src"],
24
+ "include": ["src", "src/**/*.wgsl"],
24
25
  "exclude": ["node_modules", "dist", "tests"]
25
26
  }