fix: improve computeAssetNodes rendering caching and performance (#6414)

* better caching for computeAssetNodes

* only pass required fields

* unecessary returns

* type not necessary
This commit is contained in:
Diego Imbert
2025-08-19 23:12:34 +02:00
committed by GitHub
parent 43286ee131
commit 1eeecb88dc
2 changed files with 45 additions and 22 deletions
@@ -56,6 +56,7 @@
import { ChangeTracker } from '$lib/svelte5Utils.svelte'
import type { ModulesTestStates } from '../modulesTest.svelte'
import { deepEqual } from 'fast-equals'
import type { AssetWithAltAccessType } from '../assets/lib'
let useDataflow: Writable<boolean | undefined> = writable<boolean | undefined>(false)
@@ -388,13 +389,23 @@
offset: n.data.offset ?? 0
}))
)
let newNodes: (Node & NodeLayout)[] = layoutedNodes.map((n) => {
return {
...n,
...graph.nodes[n.id]
}
})
;[nodes, edges] = computeAssetNodes(newNodes, graph.edges)
let newNodes: (Node & NodeLayout)[] = layoutedNodes.map((n) => ({ ...n, ...graph.nodes[n.id] }))
let assetNodesResult = computeAssetNodes(
newNodes.map((n) => ({
data: { assets: n.data?.assets as AssetWithAltAccessType[] },
id: n.id,
position: n.position
}))
)
newNodes = [
...newNodes.map((n) => ({ ...n, position: assetNodesResult.newNodePositions[n.id] })),
...assetNodesResult.newAssetNodes
]
nodes = newNodes
edges = [...assetNodesResult.newAssetEdges, ...graph.edges]
await tick()
height = Math.max(...nodes.map((n) => n.position.y + NODE.height + 100), minHeight)
}
@@ -8,16 +8,23 @@
export const assetDisplaysAsOutputInFlowGraph = (a: AssetWithAltAccessType) =>
getAccessType(a) === 'w' || getAccessType(a) === 'rw'
let computeAssetNodesCache:
| [(Node & NodeLayout)[], ReturnType<typeof computeAssetNodes>]
| undefined
let computeAssetNodesCache: [NodeDep[], ReturnType<typeof computeAssetNodes>] | undefined
export function computeAssetNodes(
nodes: (Node & NodeLayout)[],
edges: Edge[]
): [(Node & NodeLayout)[], Edge[]] {
if (nodes === computeAssetNodesCache?.[0]) return computeAssetNodesCache[1]
type NodeDep = {
data: object & { assets?: AssetWithAltAccessType[] | undefined }
id: string
position: { x: number; y: number }
}
export function computeAssetNodes(nodes: NodeDep[]): {
newAssetNodes: (Node & NodeLayout)[]
newAssetEdges: Edge[]
// Nodes need to be offset on the y axis to make space for the asset nodes
newNodePositions: Record<string, { x: number; y: number }>
} {
if (computeAssetNodesCache && deepEqual(nodes, computeAssetNodesCache[0])) {
return computeAssetNodesCache[1]
}
const MAX_ASSET_ROW_WIDTH = 300
const ASSETS_OVERFLOWED_NODE_WIDTH = 25
const allAssetNodes: (Node & NodeLayout)[] = []
@@ -26,8 +33,8 @@
const yPosMap: Record<number, { r?: true; w?: true }> = {}
for (const node of nodes) {
if (node.type !== 'module' && node.type !== 'input2') continue
const assets = node.data.assets ?? []
if (!assets.length) continue
// Each asset can be displayed at the top and bottom
// i.e once (R or W) or twice (RW)
@@ -179,7 +186,10 @@
}
// Shift all nodes to make space for the new asset nodes
const sortedNewNodes = clone(nodes.sort((a, b) => a.position.y - b.position.y))
const sortedNewNodes = nodes
.map((n) => ({ position: { ...n.position }, id: n.id }))
.sort((a, b) => a.position.y - b.position.y)
let currentYOffset = 0
let prevYPos = NaN
for (const node of sortedNewNodes) {
@@ -191,10 +201,11 @@
node.position.y += currentYOffset
}
let ret: ReturnType<typeof computeAssetNodes> = [
[...sortedNewNodes, ...allAssetNodes],
[...edges, ...allAssetEdges]
]
let ret: ReturnType<typeof computeAssetNodes> = {
newAssetNodes: allAssetNodes,
newAssetEdges: allAssetEdges,
newNodePositions: Object.fromEntries(sortedNewNodes.map((n) => [n.id, n.position]))
}
computeAssetNodesCache = [nodes, ret]
return ret
}
@@ -215,12 +226,13 @@
import { getContext } from 'svelte'
import ExploreAssetButton, { assetCanBeExplored } from '../../../ExploreAssetButton.svelte'
import { Tooltip } from '$lib/components/meltComponents'
import { clone, pluralize } from '$lib/utils'
import { pluralize } from '$lib/utils'
import AssetGenericIcon from '$lib/components/icons/AssetGenericIcon.svelte'
import type { Edge, Node } from '@xyflow/svelte'
import { NODE } from '../../util'
import { userStore } from '$lib/stores'
import { deepEqual } from 'fast-equals'
interface Props {
data: AssetN['data']