@gridspace/raster-path 1.0.4 → 1.0.6
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/build/index.html +1 -1
- package/build/raster-path.js +4 -12
- package/build/raster-worker.js +2450 -0
- package/package.json +8 -4
- package/scripts/build-shaders.js +32 -8
- package/src/core/path-planar.js +788 -0
- package/src/core/path-radial.js +651 -0
- package/src/core/raster-config.js +185 -0
- package/src/{index.js → core/raster-path.js} +4 -12
- package/src/core/raster-planar.js +754 -0
- package/src/core/raster-tool.js +104 -0
- package/src/core/raster-worker.js +152 -0
- package/src/core/workload-calibrate.js +416 -0
- package/src/shaders/workload-calibrate.wgsl +106 -0
- package/src/test/calibrate-test.cjs +136 -0
- package/src/test/extreme-work-test.cjs +167 -0
- package/src/test/radial-thread-limit-test.cjs +152 -0
- package/src/web/index.html +1 -1
- package/build/webgpu-worker.js +0 -2800
- package/src/web/webgpu-worker.js +0 -2303
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ═══════════════════════════════════════════════════════════════════════════
|
|
3
|
+
* Raster Config - Shared WebGPU State and Configuration
|
|
4
|
+
* ═══════════════════════════════════════════════════════════════════════════
|
|
5
|
+
*
|
|
6
|
+
* Central module providing shared state, configuration, and utilities for all
|
|
7
|
+
* worker modules. All WebGPU resources and pipelines are initialized here.
|
|
8
|
+
*
|
|
9
|
+
* EXPORTS:
|
|
10
|
+
* ────────
|
|
11
|
+
* State Variables:
|
|
12
|
+
* - config - Worker configuration (memory limits, tiling, etc.)
|
|
13
|
+
* - device - WebGPU device handle
|
|
14
|
+
* - deviceCapabilities - Device limits and capabilities
|
|
15
|
+
* - isInitialized - Initialization status flag
|
|
16
|
+
*
|
|
17
|
+
* Cached Pipelines:
|
|
18
|
+
* - cachedRasterizePipeline - Planar rasterization compute pipeline
|
|
19
|
+
* - cachedToolpathPipeline - Planar toolpath generation pipeline
|
|
20
|
+
* - cachedRadialBatchPipeline - Radial rasterization pipeline
|
|
21
|
+
* - cachedRasterizeShaderModule - Compiled planar rasterize shader
|
|
22
|
+
* - cachedToolpathShaderModule - Compiled planar toolpath shader
|
|
23
|
+
* - cachedRadialBatchShaderModule - Compiled radial shader
|
|
24
|
+
*
|
|
25
|
+
* Constants:
|
|
26
|
+
* - EMPTY_CELL - Sentinel value for empty raster cells (-1e10)
|
|
27
|
+
* - log_pre - Log prefix string "[Worker]"
|
|
28
|
+
* - diagnostic - Debug mode flag
|
|
29
|
+
*
|
|
30
|
+
* Utilities:
|
|
31
|
+
* - debug - Logging object (error, warn, log, ok)
|
|
32
|
+
* - round(v, d) - Round number to d decimal places
|
|
33
|
+
*
|
|
34
|
+
* Functions:
|
|
35
|
+
* - initWebGPU() - Initialize WebGPU device and compile pipelines
|
|
36
|
+
* - setConfig(obj) - Replace entire config object
|
|
37
|
+
* - updateConfig(obj) - Merge updates into existing config
|
|
38
|
+
*
|
|
39
|
+
* ARCHITECTURE:
|
|
40
|
+
* ─────────────
|
|
41
|
+
* This module acts as a singleton state container. All worker modules import
|
|
42
|
+
* from here to access GPU resources. The initWebGPU() function is called once
|
|
43
|
+
* during worker initialization and pre-compiles all compute pipelines to avoid
|
|
44
|
+
* runtime compilation overhead.
|
|
45
|
+
*
|
|
46
|
+
* Shader code placeholders ('SHADER:xxx') are replaced during build by the
|
|
47
|
+
* build-shaders.js script, which bundles modules with esbuild then injects
|
|
48
|
+
* WGSL shader source code.
|
|
49
|
+
*
|
|
50
|
+
* ═══════════════════════════════════════════════════════════════════════════
|
|
51
|
+
*/
|
|
52
|
+
|
|
53
|
+
// WebGPU state
|
|
54
|
+
export let config = {};
|
|
55
|
+
export let device = null;
|
|
56
|
+
export let deviceCapabilities = null;
|
|
57
|
+
export let isInitialized = false;
|
|
58
|
+
|
|
59
|
+
// Cached pipelines (created during initialization)
|
|
60
|
+
export let cachedRasterizePipeline = null;
|
|
61
|
+
export let cachedRasterizeShaderModule = null;
|
|
62
|
+
export let cachedToolpathPipeline = null;
|
|
63
|
+
export let cachedToolpathShaderModule = null;
|
|
64
|
+
export let cachedRadialBatchPipeline = null;
|
|
65
|
+
export let cachedRadialBatchShaderModule = null;
|
|
66
|
+
|
|
67
|
+
// Constants
|
|
68
|
+
export const EMPTY_CELL = -1e10;
|
|
69
|
+
export const log_pre = '[Worker]';
|
|
70
|
+
export const diagnostic = false;
|
|
71
|
+
|
|
72
|
+
// Logging utilities
|
|
73
|
+
let lastlog;
|
|
74
|
+
export const debug = {
|
|
75
|
+
error: function() { console.error(log_pre, ...arguments) },
|
|
76
|
+
warn: function() { console.warn(log_pre, ...arguments) },
|
|
77
|
+
log: function() {
|
|
78
|
+
if (!config.quiet) {
|
|
79
|
+
let now = performance.now();
|
|
80
|
+
let since = ((now - (lastlog ?? now)) | 0).toString().padStart(4,' ');
|
|
81
|
+
console.log(log_pre, `[${since}]`, ...arguments);
|
|
82
|
+
lastlog = now;
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
ok: function() { console.log(log_pre, '✅', ...arguments) },
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
// Utility functions
|
|
89
|
+
export function round(v, d = 1) {
|
|
90
|
+
return parseFloat(v.toFixed(d));
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Shader code placeholders (replaced by build script)
|
|
94
|
+
const rasterizeShaderCode = 'SHADER:planar-rasterize';
|
|
95
|
+
const toolpathShaderCode = 'SHADER:planar-toolpath';
|
|
96
|
+
const radialRasterizeShaderCode = 'SHADER:radial-raster';
|
|
97
|
+
|
|
98
|
+
// Initialize WebGPU device in worker context
|
|
99
|
+
export async function initWebGPU() {
|
|
100
|
+
if (isInitialized) return true;
|
|
101
|
+
|
|
102
|
+
if (!navigator.gpu) {
|
|
103
|
+
debug.warn('WebGPU not supported');
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
try {
|
|
108
|
+
const adapter = await navigator.gpu.requestAdapter();
|
|
109
|
+
if (!adapter) {
|
|
110
|
+
debug.warn('WebGPU adapter not available');
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Request device with higher limits for large meshes
|
|
115
|
+
const adapterLimits = adapter.limits;
|
|
116
|
+
debug.log('Adapter limits:', {
|
|
117
|
+
maxStorageBufferBindingSize: adapterLimits.maxStorageBufferBindingSize,
|
|
118
|
+
maxBufferSize: adapterLimits.maxBufferSize
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
device = await adapter.requestDevice({
|
|
122
|
+
requiredLimits: {
|
|
123
|
+
maxStorageBufferBindingSize: Math.min(
|
|
124
|
+
adapterLimits.maxStorageBufferBindingSize,
|
|
125
|
+
1024 * 1024 * 1024 // Request up to 1GB
|
|
126
|
+
),
|
|
127
|
+
maxBufferSize: Math.min(
|
|
128
|
+
adapterLimits.maxBufferSize,
|
|
129
|
+
1024 * 1024 * 1024 // Request up to 1GB
|
|
130
|
+
)
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
// Pre-compile rasterize shader module (expensive operation)
|
|
135
|
+
cachedRasterizeShaderModule = device.createShaderModule({ code: rasterizeShaderCode });
|
|
136
|
+
|
|
137
|
+
// Pre-create rasterize pipeline (very expensive operation)
|
|
138
|
+
cachedRasterizePipeline = device.createComputePipeline({
|
|
139
|
+
layout: 'auto',
|
|
140
|
+
compute: { module: cachedRasterizeShaderModule, entryPoint: 'main' },
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
// Pre-compile toolpath shader module
|
|
144
|
+
cachedToolpathShaderModule = device.createShaderModule({ code: toolpathShaderCode });
|
|
145
|
+
|
|
146
|
+
// Pre-create toolpath pipeline
|
|
147
|
+
cachedToolpathPipeline = device.createComputePipeline({
|
|
148
|
+
layout: 'auto',
|
|
149
|
+
compute: { module: cachedToolpathShaderModule, entryPoint: 'main' },
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
// Pre-compile radial batch shader module
|
|
153
|
+
cachedRadialBatchShaderModule = device.createShaderModule({ code: radialRasterizeShaderCode });
|
|
154
|
+
|
|
155
|
+
// Pre-create radial batch pipeline
|
|
156
|
+
cachedRadialBatchPipeline = device.createComputePipeline({
|
|
157
|
+
layout: 'auto',
|
|
158
|
+
compute: { module: cachedRadialBatchShaderModule, entryPoint: 'main' },
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
// Store device capabilities
|
|
162
|
+
deviceCapabilities = {
|
|
163
|
+
maxStorageBufferBindingSize: device.limits.maxStorageBufferBindingSize,
|
|
164
|
+
maxBufferSize: device.limits.maxBufferSize,
|
|
165
|
+
maxComputeWorkgroupSizeX: device.limits.maxComputeWorkgroupSizeX,
|
|
166
|
+
maxComputeWorkgroupSizeY: device.limits.maxComputeWorkgroupSizeY,
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
isInitialized = true;
|
|
170
|
+
debug.log('Initialized (pipelines cached)');
|
|
171
|
+
return true;
|
|
172
|
+
} catch (error) {
|
|
173
|
+
debug.error('Failed to initialize:', error);
|
|
174
|
+
return false;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Mutators for config (called from worker main)
|
|
179
|
+
export function setConfig(newConfig) {
|
|
180
|
+
config = newConfig;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export function updateConfig(updates) {
|
|
184
|
+
Object.assign(config, updates);
|
|
185
|
+
}
|
|
@@ -48,13 +48,9 @@
|
|
|
48
48
|
* @property {'planar'|'radial'} mode - Rasterization mode (default: 'planar')
|
|
49
49
|
* @property {boolean} autoTiling - Automatically tile large datasets (default: true)
|
|
50
50
|
* @property {number} gpuMemorySafetyMargin - Safety margin as percentage (default: 0.8 = 80%)
|
|
51
|
-
* @property {number} maxConcurrentTiles - Max concurrent tiles for radial rasterization (default: 50)
|
|
52
51
|
* @property {number} maxGPUMemoryMB - Maximum GPU memory per tile (default: 256MB)
|
|
53
|
-
* @property {number} minTileSize - Minimum tile dimension (default: 50mm)
|
|
54
|
-
* @property {number} radialRotationOffset - Radial mode: rotation offset in degrees (default: 0, use 90 to start at Z-axis)
|
|
55
52
|
* @property {number} resolution - Grid step size in mm (required)
|
|
56
53
|
* @property {number} rotationStep - Radial mode only: degrees between rays (e.g., 1.0 = 360 rays)
|
|
57
|
-
* @property {number} trianglesPerTile - Target triangles per tile for radial rasterization (default: calculated)
|
|
58
54
|
* @property {number} batchDivisor - Testing parameter to artificially divide batch size (default: 1)
|
|
59
55
|
* @property {boolean} debug - Enable debug logging (default: false)
|
|
60
56
|
* @property {boolean} quiet - Suppress log output (default: false)
|
|
@@ -110,14 +106,10 @@ export class RasterPath {
|
|
|
110
106
|
|
|
111
107
|
// Configuration with defaults
|
|
112
108
|
this.config = {
|
|
113
|
-
workerName: config.workerName ?? "
|
|
109
|
+
workerName: config.workerName ?? "raster-worker.js",
|
|
114
110
|
maxGPUMemoryMB: config.maxGPUMemoryMB ?? 256,
|
|
115
111
|
gpuMemorySafetyMargin: config.gpuMemorySafetyMargin ?? 0.8,
|
|
116
112
|
autoTiling: config.autoTiling ?? true,
|
|
117
|
-
minTileSize: config.minTileSize ?? 50,
|
|
118
|
-
maxConcurrentTiles: config.maxConcurrentTiles ?? 10,
|
|
119
|
-
trianglesPerTile: config.trianglesPerTile, // undefined = auto-calculate
|
|
120
|
-
radialRotationOffset: config.radialRotationOffset ?? 0, // degrees
|
|
121
113
|
batchDivisor: config.batchDivisor ?? 1, // For testing batching overhead
|
|
122
114
|
debug: config.debug,
|
|
123
115
|
quiet: config.quiet
|
|
@@ -138,14 +130,14 @@ export class RasterPath {
|
|
|
138
130
|
|
|
139
131
|
return new Promise((resolve, reject) => {
|
|
140
132
|
try {
|
|
141
|
-
// Create worker from the
|
|
133
|
+
// Create worker from the raster-worker.js file
|
|
142
134
|
const workerName = this.config.workerName;
|
|
143
135
|
const isBuildVersion = import.meta.url.includes('/build/') || import.meta.url.includes('raster-path.js');
|
|
144
136
|
const workerPath = workerName
|
|
145
137
|
? new URL(workerName, import.meta.url)
|
|
146
138
|
: isBuildVersion
|
|
147
|
-
? new URL(`./
|
|
148
|
-
: new URL(
|
|
139
|
+
? new URL(`./raster-worker.js`, import.meta.url)
|
|
140
|
+
: new URL(`../core/raster-worker.js`, import.meta.url);
|
|
149
141
|
this.worker = new Worker(workerPath, { type: 'module' });
|
|
150
142
|
|
|
151
143
|
// Set up message handler
|