@cazala/party 0.1.0 → 0.1.2

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/dist/index.js CHANGED
@@ -2366,7 +2366,35 @@ fn grid_cell_index(pos: vec2<f32>) -> u32 { let col = i32(floor((pos.x - GRID_MI
2366
2366
  fn grid_cell_index_from_rc(r: i32, c: i32) -> u32 { let rr = max(0, min(r, i32(GRID_ROWS()) - 1)); let cc = max(0, min(c, i32(GRID_COLS()) - 1)); return u32(rr) * GRID_COLS() + u32(cc); }
2367
2367
  struct NeighborIter { cx: i32, cy: i32, r: i32, c: i32, k: u32, reach: i32, maxK: u32, base: u32, emitted: u32, maxEmit: u32 }
2368
2368
  fn neighbor_iter_init(pos: vec2<f32>, radius: f32) -> NeighborIter { let cx = i32(floor((pos.x - GRID_MINX()) / GRID_CELL_SIZE())); let cy = i32(floor((pos.y - GRID_MINY()) / GRID_CELL_SIZE())); let reach = max(1, i32(ceil(radius / GRID_CELL_SIZE()))); var it: NeighborIter; it.cx = cx; it.cy = cy; it.reach = reach; it.r = cy - reach; it.c = cx - reach; let firstCell = grid_cell_index_from_rc(it.r, it.c); let cnt = atomicLoad(&GRID_COUNTS[firstCell]); it.maxK = min(cnt, GRID_MAX_PER_CELL()); it.base = firstCell * GRID_MAX_PER_CELL(); it.k = 0u; it.emitted = 0u; it.maxEmit = max(1u, SIM_MAX_NEIGHBORS()); return it; }
2369
- fn neighbor_iter_next(it: ptr<function, NeighborIter>, selfIndex: u32) -> u32 { loop { if ((*it).emitted >= (*it).maxEmit) { return NEIGHBOR_NONE; } if ((*it).r > (*it).cy + (*it).reach) { return NEIGHBOR_NONE; } if ((*it).k < (*it).maxK) { let id = GRID_INDICES[(*it).base + (*it).k]; (*it).k = (*it).k + 1u; if (id != selfIndex) { (*it).emitted = (*it).emitted + 1u; return id; } else { continue; } } (*it).c = (*it).c + 1; if ((*it).c > (*it).cx + (*it).reach) { (*it).c = (*it).cx - (*it).reach; (*it).r = (*it).r + 1; } if ((*it).r > (*it).cy + (*it).reach) { return NEIGHBOR_NONE; } let cell = grid_cell_index_from_rc((*it).r, (*it).c); let cnt = atomicLoad(&GRID_COUNTS[cell]); (*it).maxK = min(cnt, GRID_MAX_PER_CELL()); (*it).base = cell * GRID_MAX_PER_CELL(); (*it).k = 0u; } }`);
2369
+ fn neighbor_iter_next(it: ptr<function, NeighborIter>, selfIndex: u32) -> u32 {
2370
+ // Naga (Firefox) sometimes fails to prove that a loop always returns, so
2371
+ // we structure this as break + return result to keep validation happy.
2372
+ var result: u32 = NEIGHBOR_NONE;
2373
+ loop {
2374
+ if ((*it).emitted >= (*it).maxEmit) { break; }
2375
+ if ((*it).r > (*it).cy + (*it).reach) { break; }
2376
+ if ((*it).k < (*it).maxK) {
2377
+ let id = GRID_INDICES[(*it).base + (*it).k];
2378
+ (*it).k = (*it).k + 1u;
2379
+ if (id != selfIndex) {
2380
+ (*it).emitted = (*it).emitted + 1u;
2381
+ result = id;
2382
+ break;
2383
+ } else {
2384
+ continue;
2385
+ }
2386
+ }
2387
+ (*it).c = (*it).c + 1;
2388
+ if ((*it).c > (*it).cx + (*it).reach) { (*it).c = (*it).cx - (*it).reach; (*it).r = (*it).r + 1; }
2389
+ if ((*it).r > (*it).cy + (*it).reach) { break; }
2390
+ let cell = grid_cell_index_from_rc((*it).r, (*it).c);
2391
+ let cnt = atomicLoad(&GRID_COUNTS[cell]);
2392
+ (*it).maxK = min(cnt, GRID_MAX_PER_CELL());
2393
+ (*it).base = cell * GRID_MAX_PER_CELL();
2394
+ (*it).k = 0u;
2395
+ }
2396
+ return result;
2397
+ }`);
2370
2398
  // Optional: allow force modules to inject globals
2371
2399
  const addGlobal = (module, descriptor) => {
2372
2400
  if (module.role !== ModuleRole.Force)
@@ -3481,6 +3509,12 @@ class WebGPUEngine extends AbstractEngine {
3481
3509
  writable: true,
3482
3510
  value: null
3483
3511
  });
3512
+ Object.defineProperty(this, "didSwapchainWarmup", {
3513
+ enumerable: true,
3514
+ configurable: true,
3515
+ writable: true,
3516
+ value: false
3517
+ });
3484
3518
  Object.defineProperty(this, "animate", {
3485
3519
  enumerable: true,
3486
3520
  configurable: true,
@@ -3569,6 +3603,12 @@ class WebGPUEngine extends AbstractEngine {
3569
3603
  this.resources.canvas.width = size.width;
3570
3604
  this.resources.canvas.height = size.height;
3571
3605
  this.render.ensureTargets(this.resources, size.width, size.height);
3606
+ // Safari/WebKit workaround:
3607
+ // On WebKit's WebGPU implementation, the first present can stay blank until the canvas
3608
+ // experiences a "real" resize (even 1px). Apps have been working around this by
3609
+ // jiggling size once after starting. We do it here, once, after the final canvas size
3610
+ // is set and before the first present.
3611
+ await this.warmupSwapchainIfNeeded(size.width, size.height);
3572
3612
  // Configure grid storage + uniforms
3573
3613
  this.grid.configure(this.view.getSnapshot(), this.resources, program);
3574
3614
  // Seed module uniforms on GPU
@@ -3660,6 +3700,50 @@ class WebGPUEngine extends AbstractEngine {
3660
3700
  });
3661
3701
  });
3662
3702
  }
3703
+ isWebKitWebGPU() {
3704
+ // We specifically want WebKit's WebGPU, not Chromium/Blink.
3705
+ // - On iOS, *all* browsers use WebKit, so we treat them as WebKit.
3706
+ // - On macOS, Safari is WebKit; Chrome/Edge/Opera are not (even though their UA includes "AppleWebKit").
3707
+ try {
3708
+ if (typeof navigator === "undefined")
3709
+ return false;
3710
+ const ua = navigator.userAgent || "";
3711
+ const isIOS = /iPad|iPhone|iPod/.test(ua);
3712
+ if (isIOS)
3713
+ return true;
3714
+ const isAppleWebKit = /AppleWebKit\//.test(ua);
3715
+ const isSafari = /Safari\//.test(ua);
3716
+ const isChromiumLike = /(Chrome|Chromium|Edg|OPR)\//.test(ua);
3717
+ return isAppleWebKit && isSafari && !isChromiumLike;
3718
+ }
3719
+ catch {
3720
+ return false;
3721
+ }
3722
+ }
3723
+ async warmupSwapchainIfNeeded(width, height) {
3724
+ if (this.didSwapchainWarmup)
3725
+ return;
3726
+ if (!this.isWebKitWebGPU())
3727
+ return;
3728
+ if (height <= 1)
3729
+ return;
3730
+ this.didSwapchainWarmup = true;
3731
+ // 1px "resize" to force WebKit to fully bind the swapchain/currentTexture.
3732
+ // We wait a couple of animation frames to match real-world workarounds that proved reliable.
3733
+ try {
3734
+ this.resources.canvas.width = width;
3735
+ this.resources.canvas.height = height - 1;
3736
+ await this.waitForNextTick();
3737
+ this.resources.canvas.width = width;
3738
+ this.resources.canvas.height = height;
3739
+ await this.waitForNextTick();
3740
+ // Re-ensure targets (idempotent) in case any implementation ties resources to canvas size.
3741
+ this.render.ensureTargets(this.resources, width, height);
3742
+ }
3743
+ catch {
3744
+ // If anything goes wrong, fail open: rendering may still work on other browsers.
3745
+ }
3746
+ }
3663
3747
  // Override export to use module registry
3664
3748
  export() {
3665
3749
  const settings = {};