Skip to content

Commit

Permalink
[v9] fix: prefer named functions, for loops in hot paths (#2540)
Browse files Browse the repository at this point in the history
  • Loading branch information
AlaricBaraou committed Sep 29, 2022
1 parent fee303a commit 3bb9da4
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 37 deletions.
71 changes: 38 additions & 33 deletions packages/fiber/src/core/events.ts
Expand Up @@ -189,36 +189,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 @@ -320,7 +322,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 @@ -363,7 +365,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 @@ -386,10 +388,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 Instance<THREE.Object3D>['object']).__r3f
instance?.handlers.onPointerMissed?.(event)
}
}

function handlePointer(name: string) {
// Deal with cancelation
switch (name) {
case 'onPointerLeave':
Expand All @@ -408,17 +417,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 @@ -439,7 +448,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 Instance<THREE.Object3D>['object']).__r3f

Expand Down Expand Up @@ -490,14 +499,10 @@ export function createEvents(store: UseBoundStore<RootState>) {
}
}
}
})
}
}
}

function pointerMissed(event: MouseEvent, objects: THREE.Object3D[]) {
objects.forEach((object: THREE.Object3D) =>
(object as Instance<THREE.Object3D>['object']).__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 @@ -89,7 +91,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
Expand All @@ -100,7 +102,7 @@ export function createLoop<TCanvas>(roots: Map<TCanvas, Root>) {
) {
repeat += update(timestamp, state)
}
})
}

// Run after-effects
flushGlobalEffects('after', timestamp)
Expand Down Expand Up @@ -130,7 +132,7 @@ export function createLoop<TCanvas>(roots: Map<TCanvas, Root>) {

function advance(timestamp: number, runGlobalEffects: boolean = true, state?: RootState, frame?: XRFrame): void {
if (runGlobalEffects) flushGlobalEffects('before', timestamp)
if (!state) roots.forEach((root) => update(timestamp, root.store.getState()))
if (!state) for (const root of roots.values()) update(timestamp, root.store.getState())
else update(timestamp, state, frame)
if (runGlobalEffects) flushGlobalEffects('after', timestamp)
}
Expand Down

0 comments on commit 3bb9da4

Please sign in to comment.