@onerjs/core 8.52.9 → 8.53.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.
@@ -187,6 +187,59 @@ var PLYValue;
187
187
  PLYValue[PLYValue["SH_71"] = 111] = "SH_71";
188
188
  PLYValue[PLYValue["UNDEFINED"] = 112] = "UNDEFINED";
189
189
  })(PLYValue || (PLYValue = {}));
190
+ function _AcquireGsInterFrameYield(engine) {
191
+ // Browser-only optimization: wraps the global requestAnimationFrame, which
192
+ // doesn't exist on Babylon Native. Skip it there so the engine uses its
193
+ // default frame scheduling.
194
+ if (IsNative) {
195
+ return;
196
+ }
197
+ const existing = engine.customAnimationFrameRequester;
198
+ if (existing?._gsInterFrameYield) {
199
+ existing._refCount++;
200
+ return;
201
+ }
202
+ if (existing) {
203
+ // Slot is owned by another requester. Don't interfere.
204
+ return;
205
+ }
206
+ let _timeoutId = 0;
207
+ let _innerRafId = 0;
208
+ const wrapper = {
209
+ _gsInterFrameYield: true,
210
+ _refCount: 1,
211
+ requestAnimationFrame: (callback) => {
212
+ // Insert a setTimeout(0) to yield to the regular task queue (worker
213
+ // postMessage results, input events) before scheduling the next animation
214
+ // frame. Without this, a continuous rAF loop at high refresh rates leaves
215
+ // almost no time for regular tasks, starving worker messages for hundreds of ms.
216
+ _timeoutId = setTimeout(() => {
217
+ _innerRafId = requestAnimationFrame(callback);
218
+ }, 0);
219
+ return _timeoutId;
220
+ },
221
+ cancelAnimationFrame: (_id) => {
222
+ clearTimeout(_timeoutId);
223
+ if (_innerRafId > 0) {
224
+ cancelAnimationFrame(_innerRafId);
225
+ }
226
+ _timeoutId = 0;
227
+ _innerRafId = 0;
228
+ },
229
+ };
230
+ engine.customAnimationFrameRequester = wrapper;
231
+ }
232
+ // Counterpart to _AcquireGsInterFrameYield. Call once per mesh on dispose.
233
+ function _ReleaseGsInterFrameYield(engine) {
234
+ const existing = engine.customAnimationFrameRequester;
235
+ if (!existing?._gsInterFrameYield) {
236
+ return;
237
+ }
238
+ existing._refCount--;
239
+ if (existing._refCount === 0) {
240
+ engine.customAnimationFrameRequester = null;
241
+ }
242
+ }
190
243
  /**
191
244
  * Base class for Gaussian Splatting meshes. Contains all single-cloud rendering logic.
192
245
  * @internal Use GaussianSplattingMesh instead; this class is an internal implementation detail.
@@ -587,6 +640,7 @@ export class GaussianSplattingMeshBase extends Mesh {
587
640
  this.setEnabled(false);
588
641
  // webGL2 and webGPU support for RG texture with float16 is fine. not webGL1
589
642
  this._useRGBACovariants = !this.getEngine().isWebGPU && this.getEngine().version === 1.0;
643
+ _AcquireGsInterFrameYield(this.getEngine());
590
644
  this._keepInRam = keepInRam;
591
645
  if (url) {
592
646
  this._loadingPromise = this.loadFileAsync(url);
@@ -1693,6 +1747,7 @@ export class GaussianSplattingMeshBase extends Mesh {
1693
1747
  // Note: _splatsData and _shData are intentionally kept alive after dispose.
1694
1748
  // They can still be used as runtime source buffers by a compound mesh that retained
1695
1749
  // this mesh's data before disposal.
1750
+ _ReleaseGsInterFrameYield(this.getEngine());
1696
1751
  this._worker?.terminate();
1697
1752
  this._worker = null;
1698
1753
  this.onPartCountChangedObservable.clear();