| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192 |
- import { statsBuffer } from '../stats/buffer.mjs';
- function createRenderStep(runNextFrame, stepName) {
- /**
- * We create and reuse two queues, one to queue jobs for the current frame
- * and one for the next. We reuse to avoid triggering GC after x frames.
- */
- let thisFrame = new Set();
- let nextFrame = new Set();
- /**
- * Track whether we're currently processing jobs in this step. This way
- * we can decide whether to schedule new jobs for this frame or next.
- */
- let isProcessing = false;
- let flushNextFrame = false;
- /**
- * A set of processes which were marked keepAlive when scheduled.
- */
- const toKeepAlive = new WeakSet();
- let latestFrameData = {
- delta: 0.0,
- timestamp: 0.0,
- isProcessing: false,
- };
- let numCalls = 0;
- function triggerCallback(callback) {
- if (toKeepAlive.has(callback)) {
- step.schedule(callback);
- runNextFrame();
- }
- numCalls++;
- callback(latestFrameData);
- }
- const step = {
- /**
- * Schedule a process to run on the next frame.
- */
- schedule: (callback, keepAlive = false, immediate = false) => {
- const addToCurrentFrame = immediate && isProcessing;
- const queue = addToCurrentFrame ? thisFrame : nextFrame;
- if (keepAlive)
- toKeepAlive.add(callback);
- if (!queue.has(callback))
- queue.add(callback);
- return callback;
- },
- /**
- * Cancel the provided callback from running on the next frame.
- */
- cancel: (callback) => {
- nextFrame.delete(callback);
- toKeepAlive.delete(callback);
- },
- /**
- * Execute all schedule callbacks.
- */
- process: (frameData) => {
- latestFrameData = frameData;
- /**
- * If we're already processing we've probably been triggered by a flushSync
- * inside an existing process. Instead of executing, mark flushNextFrame
- * as true and ensure we flush the following frame at the end of this one.
- */
- if (isProcessing) {
- flushNextFrame = true;
- return;
- }
- isProcessing = true;
- [thisFrame, nextFrame] = [nextFrame, thisFrame];
- // Execute this frame
- thisFrame.forEach(triggerCallback);
- /**
- * If we're recording stats then
- */
- if (stepName && statsBuffer.value) {
- statsBuffer.value.frameloop[stepName].push(numCalls);
- }
- numCalls = 0;
- // Clear the frame so no callbacks remain. This is to avoid
- // memory leaks should this render step not run for a while.
- thisFrame.clear();
- isProcessing = false;
- if (flushNextFrame) {
- flushNextFrame = false;
- step.process(frameData);
- }
- },
- };
- return step;
- }
- export { createRenderStep };
|