Skip to content

Commit

Permalink
Replacing frameloop Queue with Set (#2727)
Browse files Browse the repository at this point in the history
  • Loading branch information
mattgperry authored Jul 16, 2024
1 parent 5728ef5 commit d7b8caf
Showing 1 changed file with 23 additions and 50 deletions.
73 changes: 23 additions & 50 deletions packages/framer-motion/src/frameloop/render-step.ts
Original file line number Diff line number Diff line change
@@ -1,40 +1,12 @@
import { Step, Process } from "./types"

class Queue {
order: Process[] = []
scheduled: Set<Process> = new Set()

add(process: Process) {
if (!this.scheduled.has(process)) {
this.scheduled.add(process)
this.order.push(process)
return true
}
}

remove(process: Process) {
const index = this.order.indexOf(process)
if (index !== -1) {
this.order.splice(index, 1)
this.scheduled.delete(process)
}
}

clear() {
this.order.length = 0
this.scheduled.clear()
}
}
import { Step, Process, FrameData } from "./types"

export function createRenderStep(runNextFrame: () => void): Step {
/**
* 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 Queue()
let nextFrame = new Queue()

let numToRun = 0
let thisFrame = new Set<Process>()
let nextFrame = new Set<Process>()

/**
* Track whether we're currently processing jobs in this step. This way
Expand All @@ -49,6 +21,21 @@ export function createRenderStep(runNextFrame: () => void): Step {
*/
const toKeepAlive = new WeakSet<Process>()

let latestFrameData: FrameData = {
delta: 0,
timestamp: 0,
isProcessing: false,
}

function triggerCallback(callback: Process) {
if (toKeepAlive.has(callback)) {
step.schedule(callback)
runNextFrame()
}

callback(latestFrameData)
}

const step: Step = {
/**
* Schedule a process to run on the next frame.
Expand All @@ -59,10 +46,7 @@ export function createRenderStep(runNextFrame: () => void): Step {

if (keepAlive) toKeepAlive.add(callback)

if (queue.add(callback) && addToCurrentFrame && isProcessing) {
// If we're adding it to the currently running queue, update its measured size
numToRun = thisFrame.order.length
}
if (!queue.has(callback)) queue.add(callback)

return callback
},
Expand All @@ -71,14 +55,16 @@ export function createRenderStep(runNextFrame: () => void): Step {
* Cancel the provided callback from running on the next frame.
*/
cancel: (callback) => {
nextFrame.remove(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
Expand All @@ -98,20 +84,7 @@ export function createRenderStep(runNextFrame: () => void): Step {
nextFrame.clear()

// Execute this frame
numToRun = thisFrame.order.length

if (numToRun) {
for (let i = 0; i < numToRun; i++) {
const callback = thisFrame.order[i]

if (toKeepAlive.has(callback)) {
step.schedule(callback)
runNextFrame()
}

callback(frameData)
}
}
thisFrame.forEach(triggerCallback)

isProcessing = false

Expand Down

0 comments on commit d7b8caf

Please sign in to comment.