From ee95dac74ee4470d3b505f691a13db37a71610b5 Mon Sep 17 00:00:00 2001 From: 0HyperCube <78500760+0HyperCube@users.noreply.github.com> Date: Sat, 25 Nov 2023 23:25:58 +0000 Subject: [PATCH] Speed up graph view frontend by removing a querySelectorAll hot path (#1475) * Reduce query selector all that was slowing perf * Speed up double iteration of filter map with flatMap --------- Co-authored-by: Keavon Chambers --- frontend/src/components/views/Graph.svelte | 35 ++++++++++++++-------- 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/frontend/src/components/views/Graph.svelte b/frontend/src/components/views/Graph.svelte index b23701af..8268514a 100644 --- a/frontend/src/components/views/Graph.svelte +++ b/frontend/src/components/views/Graph.svelte @@ -40,6 +40,9 @@ let searchTerm = ""; let nodeListLocation: { x: number; y: number } | undefined = undefined; + let inputs: SVGSVGElement[][] = []; + let outputs: SVGSVGElement[][] = []; + $: watchNodes($nodeGraph.nodes); $: gridSpacing = calculateGridSpacing(transform.scale); @@ -126,17 +129,21 @@ } async function watchNodes(nodes: FrontendNode[]) { + nodes.forEach((_, index) => { + if (!inputs[index]) inputs[index] = []; + if (!outputs[index]) outputs[index] = []; + }); + selected = selected.filter((id) => nodes.find((node) => node.id === id)); await refreshLinks(); } - function resolveLink(link: FrontendNodeLink, containerBounds: HTMLDivElement): { nodeOutput: SVGSVGElement | undefined; nodeInput: SVGSVGElement | undefined } { + function resolveLink(link: FrontendNodeLink): { nodeOutput: SVGSVGElement | undefined; nodeInput: SVGSVGElement | undefined } { const outputIndex = Number(link.linkStartOutputIndex); const inputIndex = Number(link.linkEndInputIndex); - const nodeOutputConnectors = containerBounds.querySelectorAll(`[data-node="${String(link.linkStart)}"] [data-port="output"]`) || undefined; - - const nodeInputConnectors = containerBounds.querySelectorAll(`[data-node="${String(link.linkEnd)}"] [data-port="input"]`) || undefined; + const nodeOutputConnectors = outputs[$nodeGraph.nodes.findIndex((node) => node.id === link.linkStart)]; + const nodeInputConnectors = inputs[$nodeGraph.nodes.findIndex((node) => node.id === link.linkEnd)] || undefined; const nodeOutput = nodeOutputConnectors?.[outputIndex] as SVGSVGElement | undefined; const nodeInput = nodeInputConnectors?.[inputIndex] as SVGSVGElement | undefined; @@ -146,12 +153,9 @@ async function refreshLinks() { await tick(); - if (!nodesContainer) return; - const theNodesContainer = nodesContainer; - const links = $nodeGraph.links; nodeLinkPaths = links.flatMap((link, index) => { - const { nodeInput, nodeOutput } = resolveLink(link, theNodesContainer); + const { nodeInput, nodeOutput } = resolveLink(link); if (!nodeInput || !nodeOutput) return []; if (disconnecting?.linkIndex === index) return []; const linkStart = $nodeGraph.nodes.find((node) => node.id === link.linkStart)?.displayName === "Layer"; @@ -463,7 +467,7 @@ // Find the link that the node has been dragged on top of const link = $nodeGraph.links.find((link): boolean => { - const { nodeInput, nodeOutput } = resolveLink(link, theNodesContainer); + const { nodeInput, nodeOutput } = resolveLink(link); if (!nodeInput || !nodeOutput) return false; const wireCurveLocations = buildWirePathLocations(nodeOutput.getBoundingClientRect(), nodeInput.getBoundingClientRect(), false, false); @@ -661,7 +665,7 @@
- {#each $nodeGraph.nodes.filter((node) => node.displayName === "Layer") as node (String(node.id))} + {#each $nodeGraph.nodes.flatMap((node, nodeIndex) => (node.displayName === "Layer" ? [{ node, nodeIndex }] : [])) as { node, nodeIndex } (String(node.id))} {@const clipPathId = `${Math.random()}`.substring(2)} {@const stackDatainput = node.exposedInputs[0]}
{node.primaryInput} data @@ -705,6 +710,7 @@ data-datatype={node.primaryOutput.dataType} style:--data-color={`var(--color-data-${node.primaryOutput.dataType})`} style:--data-color-dim={`var(--color-data-${node.primaryOutput.dataType}-dim)`} + bind:this={outputs[nodeIndex][0]} > {node.primaryOutput.dataType} data @@ -718,6 +724,7 @@ data-datatype={stackDatainput.dataType} style:--data-color={`var(--color-data-${stackDatainput.dataType})`} style:--data-color-dim={`var(--color-data-${stackDatainput.dataType}-dim)`} + bind:this={inputs[nodeIndex][1]} > {stackDatainput.dataType} data @@ -737,7 +744,7 @@
{/each} - {#each $nodeGraph.nodes.filter((node) => node.displayName !== "Layer") as node (String(node.id))} + {#each $nodeGraph.nodes.flatMap((node, nodeIndex) => (node.displayName !== "Layer" ? [{ node, nodeIndex }] : [])) as { node, nodeIndex } (String(node.id))} {@const exposedInputsOutputs = [...node.exposedInputs, ...node.exposedOutputs]} {@const clipPathId = `${Math.random()}`.substring(2)}
{node.primaryInput} data @@ -795,6 +803,7 @@ data-datatype={parameter.dataType} style:--data-color={`var(--color-data-${parameter.dataType})`} style:--data-color-dim={`var(--color-data-${parameter.dataType}-dim)`} + bind:this={inputs[nodeIndex][index + 1]} > {parameter.dataType} data @@ -813,12 +822,13 @@ data-datatype={node.primaryOutput.dataType} style:--data-color={`var(--color-data-${node.primaryOutput.dataType})`} style:--data-color-dim={`var(--color-data-${node.primaryOutput.dataType}-dim)`} + bind:this={outputs[nodeIndex][0]} > {node.primaryOutput.dataType} data {/if} - {#each node.exposedOutputs as parameter} + {#each node.exposedOutputs as parameter, outputIndex} {parameter.dataType} data