Skip to content

Commit

Permalink
fix: prefer named functions, for loops in hot paths (#2541)
Browse files Browse the repository at this point in the history
  • Loading branch information
AlaricBaraou committed Sep 29, 2022
1 parent 970f885 commit 0823585
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 39 deletions.
71 changes: 38 additions & 33 deletions packages/fiber/src/core/events.ts
Expand Up @@ -188,36 +188,38 @@ export function createEvents(store: UseBoundStore<RootState>) {
// Allow callers to eliminate event objects
const eventsObjects = filter ? filter(state.internal.interaction) : state.internal.interaction
// Reset all raycaster cameras to undefined
eventsObjects.forEach((obj) => {
const state = getRootState(obj)
for (let i = 0; i < eventsObjects.length; i++) {
const state = getRootState(eventsObjects[i])
if (state) {
state.raycaster.camera = undefined!
}
})
}

if (!state.previousRoot) {
// Make sure root-level pointer and ray are set up
state.events.compute?.(event, state)
}

function handleRaycast(obj: THREE.Object3D<THREE.Event>) {
const state = getRootState(obj)
// Skip event handling when noEvents is set, or when the raycasters camera is null
if (!state || !state.events.enabled || state.raycaster.camera === null) return []

// When the camera is undefined we have to call the event layers update function
if (state.raycaster.camera === undefined) {
state.events.compute?.(event, state, state.previousRoot?.getState())
// If the camera is still undefined we have to skip this layer entirely
if (state.raycaster.camera === undefined) state.raycaster.camera = null!
}

// Intersect object by object
return state.raycaster.camera ? state.raycaster.intersectObject(obj, true) : []
}

// Collect events
let hits: THREE.Intersection<THREE.Object3D<THREE.Event>>[] = eventsObjects
// Intersect objects
.flatMap((obj) => {
const state = getRootState(obj)
// Skip event handling when noEvents is set, or when the raycasters camera is null
if (!state || !state.events.enabled || state.raycaster.camera === null) return []

// When the camera is undefined we have to call the event layers update function
if (state.raycaster.camera === undefined) {
state.events.compute?.(event, state, state.previousRoot?.getState())
// If the camera is still undefined we have to skip this layer entirely
if (state.raycaster.camera === undefined) state.raycaster.camera = null!
}

// Intersect object by object
return state.raycaster.camera ? state.raycaster.intersectObject(obj, true) : []
})
.flatMap(handleRaycast)
// Sort by event priority and distance
.sort((a, b) => {
const aState = getRootState(a.object)
Expand Down Expand Up @@ -317,7 +319,7 @@ export function createEvents(store: UseBoundStore<RootState>) {
ray: raycaster.ray,
camera: camera,
// Hijack stopPropagation, which just sets a flag
stopPropagation: () => {
stopPropagation() {
// https://github.com/pmndrs/react-three-fiber/issues/596
// Events are not allowed to stop propagation if the pointer has been captured
const capturesForPointer = 'pointerId' in event && internal.capturedMap.get(event.pointerId)
Expand Down Expand Up @@ -359,7 +361,7 @@ export function createEvents(store: UseBoundStore<RootState>) {

function cancelPointer(intersections: Intersection[]) {
const { internal } = store.getState()
Array.from(internal.hovered.values()).forEach((hoveredObj) => {
for (const hoveredObj of internal.hovered.values()) {
// When no objects were hit or the the hovered object wasn't found underneath the cursor
// we call onPointerOut and delete the object from the hovered-elements map
if (
Expand All @@ -382,10 +384,17 @@ export function createEvents(store: UseBoundStore<RootState>) {
handlers.onPointerLeave?.(data as ThreeEvent<PointerEvent>)
}
}
})
}
}

const handlePointer = (name: string) => {
function pointerMissed(event: MouseEvent, objects: THREE.Object3D[]) {
for (let i = 0; i < objects.length; i++) {
const instance = (objects[i] as unknown as Instance).__r3f
instance?.handlers.onPointerMissed?.(event)
}
}

function handlePointer(name: string) {
// Deal with cancelation
switch (name) {
case 'onPointerLeave':
Expand All @@ -404,17 +413,17 @@ export function createEvents(store: UseBoundStore<RootState>) {
}

// Any other pointer goes here ...
return (event: DomEvent) => {
return function handleEvent(event: DomEvent) {
const { onPointerMissed, internal } = store.getState()

//prepareRay(event)
// prepareRay(event)
internal.lastEvent.current = event

// Get fresh intersects
const isPointerMove = name === 'onPointerMove'
const isClickEvent = name === 'onClick' || name === 'onContextMenu' || name === 'onDoubleClick'
const filter = isPointerMove ? filterPointerEvents : undefined
//const hits = patchIntersects(intersect(filter), event)
// const hits = patchIntersects(intersect(filter), event)
const hits = intersect(event, filter)
const delta = isClickEvent ? calculateDistance(event) : 0

Expand All @@ -435,7 +444,7 @@ export function createEvents(store: UseBoundStore<RootState>) {
// Take care of unhover
if (isPointerMove) cancelPointer(hits)

handleIntersects(hits, event, delta, (data: ThreeEvent<DomEvent>) => {
function onIntersect(data: ThreeEvent<DomEvent>) {
const eventObject = data.eventObject
const instance = (eventObject as unknown as Instance).__r3f
const handlers = instance?.handlers
Expand Down Expand Up @@ -485,14 +494,10 @@ export function createEvents(store: UseBoundStore<RootState>) {
}
}
}
})
}
}
}

function pointerMissed(event: MouseEvent, objects: THREE.Object3D[]) {
objects.forEach((object: THREE.Object3D) =>
(object as unknown as Instance).__r3f?.handlers.onPointerMissed?.(event),
)
handleIntersects(hits, event, delta, onIntersect)
}
}

return { handlePointer }
Expand Down
10 changes: 6 additions & 4 deletions packages/fiber/src/core/loop.ts
Expand Up @@ -36,7 +36,9 @@ export const addTail = (callback: GlobalRenderCallback) => createSubs(callback,

function run(effects: Set<SubItem>, timestamp: number) {
if (!effects.size) return
effects.forEach(({ callback }) => callback(timestamp))
for (const { callback } of effects.values()) {
callback(timestamp)
}
}

export type GlobalEffectType = 'before' | 'after' | 'tail'
Expand Down Expand Up @@ -91,7 +93,7 @@ export function createLoop<TCanvas>(roots: Map<TCanvas, Root>) {
flushGlobalEffects('before', timestamp)

// Render all roots
roots.forEach((root) => {
for (const root of roots.values()) {
state = root.store.getState()
// If the frameloop is invalidated, do not run another frame
if (
Expand All @@ -101,7 +103,7 @@ export function createLoop<TCanvas>(roots: Map<TCanvas, Root>) {
) {
repeat += render(timestamp, state)
}
})
}

// Run after-effects
flushGlobalEffects('after', timestamp)
Expand Down Expand Up @@ -136,7 +138,7 @@ export function createLoop<TCanvas>(roots: Map<TCanvas, Root>) {
frame?: THREE.XRFrame,
): void {
if (runGlobalEffects) flushGlobalEffects('before', timestamp)
if (!state) roots.forEach((root) => render(timestamp, root.store.getState()))
if (!state) for (const root of roots.values()) render(timestamp, root.store.getState())
else render(timestamp, state, frame)
if (runGlobalEffects) flushGlobalEffects('after', timestamp)
}
Expand Down
5 changes: 3 additions & 2 deletions packages/fiber/src/core/utils.ts
Expand Up @@ -254,7 +254,8 @@ export function applyProps(instance: Instance, data: InstanceProps | DiffSet) {
// Prepare memoized props
if (instance.__r3f) instance.__r3f.memoizedProps = memoized

changes.forEach(([key, value, isEvent, keys]) => {
for (let i = 0; i < changes.length; i++) {
let [key, value, isEvent, keys] = changes[i]
let currentInstance = instance
let targetProp = currentInstance[key]

Expand Down Expand Up @@ -340,7 +341,7 @@ export function applyProps(instance: Instance, data: InstanceProps | DiffSet) {
}

invalidateInstance(instance)
})
}

if (localState.parent && rootState.internal && instance.raycast && prevHandlers !== localState.eventCount) {
// Pre-emptively remove the instance from the interaction manager
Expand Down

0 comments on commit 0823585

Please sign in to comment.