Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: prefer named functions, for loops in hot paths #2541

Merged
merged 3 commits into from Sep 29, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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