Skip to content

Commit

Permalink
fix: when using the cache: invalidate objects on layers affected by c…
Browse files Browse the repository at this point in the history
…hanges
  • Loading branch information
nytamin committed Sep 3, 2024
1 parent 1a43707 commit 26f4e82
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 11 deletions.
61 changes: 52 additions & 9 deletions src/resolver/CacheHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Reference, ResolvedTimelineObject, ResolvedTimelineObjects, ResolverCac
import { ResolvedTimelineHandler } from './ResolvedTimelineHandler'
import { mapToObject } from './lib/lib'
import { tic } from './lib/performance'
import { getRefObjectId, isObjectReference, joinReferences } from './lib/reference'
import { getRefLayer, getRefObjectId, isLayerReference, isObjectReference, joinReferences } from './lib/reference'
import { objHasLayer } from './lib/timeline'

export class CacheHandler {
Expand Down Expand Up @@ -43,6 +43,9 @@ export class CacheHandler {
for (const ref of references) {
changedReferences[ref] = true
}
if (objHasLayer(obj)) {
changedReferences[`$${obj.layer}`] = true
}
}

for (const obj of this.resolvedTimeline.objectsMap.values()) {
Expand Down Expand Up @@ -94,15 +97,21 @@ export class CacheHandler {
addChangedObject(obj)
}
}
// Invalidate objects, by gradually removing the invalidated ones from validObjects
// At this point, all directly changed objects have been marked as changed.

// Next step is to invalidate any indirectly affected objects, by gradually removing the invalidated ones from validObjects

// Prepare validObjects:
const validObjects: ResolvedTimelineObjects = {}
for (const obj of this.resolvedTimeline.objectsMap.values()) {
validObjects[obj.id] = obj
}
/** All references that depend on another reference (ie objects, classs or layers): */
/** All references that depend on another reference (ie objects, class or layers): */
const affectReferenceMap: { [ref: Reference]: Reference[] } = {}

/** Map of which objects can be affected by any other object, per layer */
const objectLayerMap: { [layer: string]: string[] } = {}

for (const obj of this.resolvedTimeline.objectsMap.values()) {
// Add everything that this object affects:
const cachedObj = this.cache.objects[obj.id]
Expand All @@ -124,16 +133,26 @@ export class CacheHandler {

// Add everything that this object is affected by:
if (changedReferences[`#${obj.id}`]) {
// The object is directly said to be invalid, no need to add it to referencingObjects,
// since it'll be easily invalidated anyway later
// The object is directly said to have changed.
// (No need to add it to affectReferenceMap, since it'll be easily invalidated anyway later)
} else {
// The object is not directly said to have changed.
// But if might have been affected by other objects that have changed.

// Note: we only have to check for the OLD object, since if the old and the new object differs,
// that would mean it'll be directly invalidated anyway.
if (cachedObj) {
// Fetch all references for the object from the last time it was resolved.
// Note: This can be done, since _if_ the object was changed in any way since last resolve
// it'll be invalidated anyway
const dependOnReferences = cachedObj.resolved.directReferences

// Build up objectLayerMap:
if (objHasLayer(cachedObj)) {
if (!objectLayerMap[cachedObj.layer]) objectLayerMap[cachedObj.layer] = []
objectLayerMap[cachedObj.layer].push(obj.id)
}

for (let i = 0; i < dependOnReferences.length; i++) {
const ref = dependOnReferences[i]
if (!affectReferenceMap[ref]) affectReferenceMap[ref] = []
Expand All @@ -142,13 +161,20 @@ export class CacheHandler {
}
}
}

// Invalidate all changed objects, and recursively invalidate all objects that reference those objects:
const handledReferences: { [ref: Reference]: true } = {}
for (const reference of Object.keys(changedReferences) as Reference[]) {
this.invalidateObjectsWithReference(handledReferences, reference, affectReferenceMap, validObjects)
this.invalidateObjectsWithReference(
handledReferences,
reference,
affectReferenceMap,
validObjects,
objectLayerMap
)
}

// The objects that are left in validObjects at this point are still valid.
// At this point, the objects that are left in validObjects are still valid (ie has not changed or is affected by any others).
// We can reuse the old resolving for those:
for (const obj of Object.values<ResolvedTimelineObject>(validObjects)) {
if (!this.cache.objects[obj.id])
Expand Down Expand Up @@ -201,7 +227,9 @@ export class CacheHandler {
handledReferences: { [ref: Reference]: true },
reference: Reference,
affectReferenceMap: { [ref: Reference]: Reference[] },
validObjects: ResolvedTimelineObjects
validObjects: ResolvedTimelineObjects,
/** Map of which objects can be affected by any other object, per layer */
objectLayerMap: { [layer: string]: string[] }
) {
if (handledReferences[reference]) return // to avoid infinite loops
handledReferences[reference] = true
Expand All @@ -212,6 +240,20 @@ export class CacheHandler {
delete validObjects[objId]
}
}
if (isLayerReference(reference)) {
const layer = getRefLayer(reference)
if (objectLayerMap[layer]) {
for (const affectedObjId of objectLayerMap[layer]) {
this.invalidateObjectsWithReference(
handledReferences,
`#${affectedObjId}`,
affectReferenceMap,
validObjects,
objectLayerMap
)
}
}
}

// Invalidate all objects that depend on any of the references that this reference affects:
const affectedReferences = affectReferenceMap[reference]
Expand All @@ -222,7 +264,8 @@ export class CacheHandler {
handledReferences,
referencingReference,
affectReferenceMap,
validObjects
validObjects,
objectLayerMap
)
}
}
Expand Down
3 changes: 1 addition & 2 deletions src/resolver/ResolvedTimelineHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -233,8 +233,7 @@ export class ResolvedTimelineHandler<TContent extends Content = Content> {
if (obj.disabled) {
resultingInstances = []
} else {
// Loop up references to the parent:

// Look up references to the parent:
let parentInstances: TimelineObjectInstance[] | null = null
let hasParent = false
let parentRef: ObjectReference | undefined = undefined
Expand Down

0 comments on commit 26f4e82

Please sign in to comment.