@gridspace/raster-path 1.0.9 → 1.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.
@@ -464,7 +464,12 @@ export class RasterPath {
464
464
  // Set up progress handler if callback provided
465
465
  if (onProgress) {
466
466
  const progressHandler = (data) => {
467
- onProgress(data.percent, { current: data.current, total: data.total, pathIndex: data.pathIndex });
467
+ onProgress(data.percent, {
468
+ current: data.current,
469
+ total: data.total,
470
+ chunkIndex: data.chunkIndex,
471
+ totalChunks: data.totalChunks
472
+ });
468
473
  };
469
474
  this.messageHandlers.set('tracing-progress', progressHandler);
470
475
  }
@@ -570,7 +575,7 @@ export class RasterPath {
570
575
  const { type, success, data } = e.data;
571
576
 
572
577
  // Handle progress messages (don't delete handler)
573
- if (type === 'rasterize-progress' || type === 'toolpath-progress') {
578
+ if (type === 'rasterize-progress' || type === 'toolpath-progress' || type === 'tracing-progress') {
574
579
  const handler = this.messageHandlers.get(type);
575
580
  if (handler) {
576
581
  handler(data);
@@ -676,18 +681,32 @@ export class RasterPath {
676
681
  }
677
682
 
678
683
  // Flatten triangle indices for GPU
679
- const triangleIndices = [];
684
+ // Pre-calculate total size for optimal performance
685
+ let totalIndices = 0;
686
+ for (let i = 0; i < buckets.length; i++) {
687
+ totalIndices += buckets[i].triangleIndices.length;
688
+ }
689
+
690
+ // Pre-allocate array to avoid resizing overhead
691
+ const triangleIndices = new Array(totalIndices);
680
692
  const bucketInfo = [];
693
+ let offset = 0;
681
694
 
682
695
  for (let i = 0; i < buckets.length; i++) {
683
696
  const bucket = buckets[i];
697
+ const indices = bucket.triangleIndices;
698
+
684
699
  bucketInfo.push({
685
700
  minX: bucket.minX,
686
701
  maxX: bucket.maxX,
687
- startIndex: triangleIndices.length,
688
- count: bucket.triangleIndices.length
702
+ startIndex: offset,
703
+ count: indices.length
689
704
  });
690
- triangleIndices.push(...bucket.triangleIndices);
705
+
706
+ // Copy indices using fast indexed access
707
+ for (let j = 0; j < indices.length; j++) {
708
+ triangleIndices[offset++] = indices[j];
709
+ }
691
710
  }
692
711
 
693
712
  return {
@@ -2387,7 +2387,7 @@ async function generateRadialToolpaths({
2387
2387
  if (!strip.positions || strip.positions.length === 0)
2388
2388
  continue;
2389
2389
  if (diagnostic && (globalStripIdx === 0 || globalStripIdx === 360)) {
2390
- debug.log(`QRSDOFON | Strip ${globalStripIdx} (${strip.angle.toFixed(1)}\xB0) INPUT terrain first 5 Z values: ${strip.positions.slice(0, 5).map((v) => v.toFixed(3)).join(",")}`);
2390
+ debug.log(`ES2VU471 | Strip ${globalStripIdx} (${strip.angle.toFixed(1)}\xB0) INPUT terrain first 5 Z values: ${strip.positions.slice(0, 5).map((v) => v.toFixed(3)).join(",")}`);
2391
2391
  }
2392
2392
  const stripToolpathResult = await runToolpathComputeWithBuffers(
2393
2393
  strip.positions,
@@ -2400,7 +2400,7 @@ async function generateRadialToolpaths({
2400
2400
  pipelineStartTime
2401
2401
  );
2402
2402
  if (diagnostic && (globalStripIdx === 0 || globalStripIdx === 360)) {
2403
- debug.log(`QRSDOFON | Strip ${globalStripIdx} (${strip.angle.toFixed(1)}\xB0) OUTPUT toolpath first 5 Z values: ${stripToolpathResult.pathData.slice(0, 5).map((v) => v.toFixed(3)).join(",")}`);
2403
+ debug.log(`ES2VU471 | Strip ${globalStripIdx} (${strip.angle.toFixed(1)}\xB0) OUTPUT toolpath first 5 Z values: ${stripToolpathResult.pathData.slice(0, 5).map((v) => v.toFixed(3)).join(",")}`);
2404
2404
  }
2405
2405
  allStripToolpaths.push({
2406
2406
  angle: strip.angle,
@@ -2896,7 +2896,7 @@ async function generateTracingToolpaths({
2896
2896
  debug.log(`First point: world(${firstX.toFixed(2)}, ${firstY.toFixed(2)}) -> grid(${gridX.toFixed(2)}, ${gridY.toFixed(2)})`);
2897
2897
  }
2898
2898
  debug.log("PHASE 2: Calculating memory budget and chunking...");
2899
- const bytesPerPoint = 8 + 4;
2899
+ const bytesPerPoint = 8 + 4 + 4;
2900
2900
  const configuredLimit = config.maxGPUMemoryMB * 1024 * 1024;
2901
2901
  const deviceLimit = deviceCapabilities.maxStorageBufferBindingSize;
2902
2902
  const maxSafeSize = Math.min(configuredLimit, deviceLimit) * config.gpuMemorySafetyMargin;
@@ -2911,9 +2911,15 @@ async function generateTracingToolpaths({
2911
2911
  );
2912
2912
  }
2913
2913
  const availableForPaths = maxSafeSize - fixedOverhead;
2914
- const maxPointsPerChunk = Math.floor(availableForPaths / bytesPerPoint);
2914
+ const maxPointsPerChunkMemory = Math.floor(availableForPaths / bytesPerPoint);
2915
+ const maxWorkgroupsPerDimension = deviceCapabilities.maxComputeWorkgroupsPerDimension || 65535;
2916
+ const threadsPerWorkgroup = 64;
2917
+ const maxPointsPerChunkGPU = maxWorkgroupsPerDimension * threadsPerWorkgroup;
2918
+ const maxPointsPerChunk = Math.min(maxPointsPerChunkMemory, maxPointsPerChunkGPU);
2915
2919
  debug.log(`Memory budget: ${(maxSafeSize / 1024 / 1024).toFixed(1)}MB safe, ${(availableForPaths / 1024 / 1024).toFixed(1)}MB available for paths`);
2916
- debug.log(`Max points per chunk: ${maxPointsPerChunk.toLocaleString()}`);
2920
+ debug.log(`Memory-based max: ${maxPointsPerChunkMemory.toLocaleString()} points`);
2921
+ debug.log(`GPU dispatch max: ${maxPointsPerChunkGPU.toLocaleString()} points (${maxWorkgroupsPerDimension.toLocaleString()} workgroups)`);
2922
+ debug.log(`Max points per chunk: ${maxPointsPerChunk.toLocaleString()} (limited by ${maxPointsPerChunk === maxPointsPerChunkGPU ? "GPU" : "memory"})`);
2917
2923
  const chunks = [];
2918
2924
  let currentStart = 0;
2919
2925
  while (currentStart < totalSampledPoints) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gridspace/raster-path",
3
- "version": "1.0.9",
3
+ "version": "1.1.1",
4
4
  "private": false,
5
5
  "description": "Terrain and Tool Raster Path Finder using WebGPU",
6
6
  "type": "module",
@@ -325,7 +325,7 @@ export async function generateTracingToolpaths({
325
325
  // PHASE 2: Calculate memory budget and create chunks
326
326
  // ═══════════════════════════════════════════════════════════════════════
327
327
  debug.log('PHASE 2: Calculating memory budget and chunking...');
328
- const bytesPerPoint = 8 + 4; // XY input (2 floats) + Z output (1 float)
328
+ const bytesPerPoint = 8 + 4 + 4; // XY input (8) + Z output (4) + Z staging (4)
329
329
  const configuredLimit = config.maxGPUMemoryMB * 1024 * 1024;
330
330
  const deviceLimit = deviceCapabilities.maxStorageBufferBindingSize;
331
331
  const maxSafeSize = Math.min(configuredLimit, deviceLimit) * config.gpuMemorySafetyMargin;
@@ -350,10 +350,20 @@ export async function generateTracingToolpaths({
350
350
  }
351
351
 
352
352
  const availableForPaths = maxSafeSize - fixedOverhead;
353
- const maxPointsPerChunk = Math.floor(availableForPaths / bytesPerPoint);
353
+ const maxPointsPerChunkMemory = Math.floor(availableForPaths / bytesPerPoint);
354
+
355
+ // GPU dispatch limit: 65535 workgroups per dimension, 64 threads per workgroup
356
+ const maxWorkgroupsPerDimension = deviceCapabilities.maxComputeWorkgroupsPerDimension || 65535;
357
+ const threadsPerWorkgroup = 64;
358
+ const maxPointsPerChunkGPU = maxWorkgroupsPerDimension * threadsPerWorkgroup;
359
+
360
+ // Use the smaller of memory limit and GPU dispatch limit
361
+ const maxPointsPerChunk = Math.min(maxPointsPerChunkMemory, maxPointsPerChunkGPU);
354
362
 
355
363
  debug.log(`Memory budget: ${(maxSafeSize / 1024 / 1024).toFixed(1)}MB safe, ${(availableForPaths / 1024 / 1024).toFixed(1)}MB available for paths`);
356
- debug.log(`Max points per chunk: ${maxPointsPerChunk.toLocaleString()}`);
364
+ debug.log(`Memory-based max: ${maxPointsPerChunkMemory.toLocaleString()} points`);
365
+ debug.log(`GPU dispatch max: ${maxPointsPerChunkGPU.toLocaleString()} points (${maxWorkgroupsPerDimension.toLocaleString()} workgroups)`);
366
+ debug.log(`Max points per chunk: ${maxPointsPerChunk.toLocaleString()} (limited by ${maxPointsPerChunk === maxPointsPerChunkGPU ? 'GPU' : 'memory'})`);
357
367
 
358
368
  // Create chunks
359
369
  const chunks = [];
@@ -464,7 +464,12 @@ export class RasterPath {
464
464
  // Set up progress handler if callback provided
465
465
  if (onProgress) {
466
466
  const progressHandler = (data) => {
467
- onProgress(data.percent, { current: data.current, total: data.total, pathIndex: data.pathIndex });
467
+ onProgress(data.percent, {
468
+ current: data.current,
469
+ total: data.total,
470
+ chunkIndex: data.chunkIndex,
471
+ totalChunks: data.totalChunks
472
+ });
468
473
  };
469
474
  this.messageHandlers.set('tracing-progress', progressHandler);
470
475
  }
@@ -570,7 +575,7 @@ export class RasterPath {
570
575
  const { type, success, data } = e.data;
571
576
 
572
577
  // Handle progress messages (don't delete handler)
573
- if (type === 'rasterize-progress' || type === 'toolpath-progress') {
578
+ if (type === 'rasterize-progress' || type === 'toolpath-progress' || type === 'tracing-progress') {
574
579
  const handler = this.messageHandlers.get(type);
575
580
  if (handler) {
576
581
  handler(data);
@@ -676,18 +681,32 @@ export class RasterPath {
676
681
  }
677
682
 
678
683
  // Flatten triangle indices for GPU
679
- const triangleIndices = [];
684
+ // Pre-calculate total size for optimal performance
685
+ let totalIndices = 0;
686
+ for (let i = 0; i < buckets.length; i++) {
687
+ totalIndices += buckets[i].triangleIndices.length;
688
+ }
689
+
690
+ // Pre-allocate array to avoid resizing overhead
691
+ const triangleIndices = new Array(totalIndices);
680
692
  const bucketInfo = [];
693
+ let offset = 0;
681
694
 
682
695
  for (let i = 0; i < buckets.length; i++) {
683
696
  const bucket = buckets[i];
697
+ const indices = bucket.triangleIndices;
698
+
684
699
  bucketInfo.push({
685
700
  minX: bucket.minX,
686
701
  maxX: bucket.maxX,
687
- startIndex: triangleIndices.length,
688
- count: bucket.triangleIndices.length
702
+ startIndex: offset,
703
+ count: indices.length
689
704
  });
690
- triangleIndices.push(...bucket.triangleIndices);
705
+
706
+ // Copy indices using fast indexed access
707
+ for (let j = 0; j < indices.length; j++) {
708
+ triangleIndices[offset++] = indices[j];
709
+ }
691
710
  }
692
711
 
693
712
  return {