Increase efficiency of Groups Muter/Bypasser calculations by abandoning LiteGraph's built in mechanism.

This commit is contained in:
rgthree
2024-01-13 14:04:12 -05:00
parent b32c8c4438
commit d136e81c8f
3 changed files with 77 additions and 4 deletions
+44 -1
View File
@@ -12,6 +12,7 @@ import {
SerializedLGraphNode,
IWidget,
LGraphGroup,
Vector4,
} from "../typings/litegraph.js";
import { fitString } from "./utils_canvas.js";
@@ -45,6 +46,8 @@ class FastGroupsService {
private runScheduleTimeout: number|null = null;
private runScheduleAnimation: number|null = null;
private cachedNodeBoundings: {[key: number]: Vector4}|null = null;
constructor() {
// Don't need to do anything, wait until a signal.
}
@@ -104,12 +107,52 @@ class FastGroupsService {
this.runScheduledForMs = null;
}
/**
* Returns the boundings for all nodes on the graph, then clears it after a short delay. This is
* to increase efficiency by caching the nodes' boundings when multiple groups are on the page.
*/
getBoundingsForAllNodes() {
if (!this.cachedNodeBoundings) {
this.cachedNodeBoundings = {};
for (const node of app.graph._nodes) {
this.cachedNodeBoundings[node.id] = node.getBounding();
}
setTimeout(() => {
this.cachedNodeBoundings = null;
}, 50);
}
return this.cachedNodeBoundings;
}
/**
* This overrides `LGraphGroup.prototype.recomputeInsideNodes` to be much more efficient when
* calculating for many groups at once (only compute all nodes once in `getBoundingsForAllNodes`).
*/
recomputeInsideNodesForGroup(group: LGraphGroup) {
const cachedBoundings = this.getBoundingsForAllNodes();
const nodes = group.graph._nodes;
group._nodes.length = 0;
for (const node of nodes) {
const node_bounding = cachedBoundings[node.id];
if (!node_bounding || !LiteGraph.overlapBounding(group._bounding, node_bounding)) {
continue;
}
group._nodes.push(node);
}
}
/**
* Everything goes through getGroupsUnsorted, so we only get groups once. However, LiteGraph's
* `recomputeInsideNodes` is inefficient when calling multiple groups (it iterates over all nodes
* each time). So, we'll do our own dang thing, once.
*/
private getGroupsUnsorted(now: number) {
const graph = app.graph as TLGraph;
if (!this.groupsUnsorted.length || now - this.msLastUnsorted > this.msThreshold) {
this.groupsUnsorted = [...graph._groups];
for (const group of this.groupsUnsorted) {
group.recomputeInsideNodes();
this.recomputeInsideNodesForGroup(group);
(group as any)._rgthreeHasAnyActiveNode = group._nodes.some(
(n) => n.mode === LiteGraph.ALWAYS,
);
+7 -2
View File
@@ -297,6 +297,7 @@ export const LiteGraph: {
//@rgthree
isValidConnection(type: string|string[], type: string|string[]):boolean;
overlapBounding(a: Vector4, b: Vector4) : boolean;
createNode<T extends LGraphNode = LGraphNode>(type: string): T;
/** Register a node class so it can be listed when the user wants to create a new one */
@@ -435,7 +436,8 @@ export declare class LGraph {
starttime: number;
status: typeof LGraph.STATUS_RUNNING | typeof LGraph.STATUS_STOPPED;
private _nodes: LGraphNode[];
// @rgthree, remove private; it's not really private b/c it's javascript.
_nodes: LGraphNode[];
// @rgthree, remove private; it's not really private b/c it's javascript.
_groups: LGraphGroup[];
private _nodes_by_id: Record<number, LGraphNode>;
@@ -1166,7 +1168,8 @@ export type SerializedLGraphGroup = {
};
export declare class LGraphGroup {
title: string;
private _bounding: Vector4;
// @rgthree - mark unprivate
_bounding: Vector4;
color: string;
font: string;
// @rgthree
@@ -1175,6 +1178,8 @@ export declare class LGraphGroup {
_pos: Vector2;
// @rgthree
_size: Vector2;
// @rgthree
graph: LGraph;
configure(o: SerializedLGraphGroup): void;
+26 -1
View File
@@ -21,6 +21,7 @@ class FastGroupsService {
this.runScheduledForMs = null;
this.runScheduleTimeout = null;
this.runScheduleAnimation = null;
this.cachedNodeBoundings = null;
}
addFastGroupNode(node) {
this.fastGroupNodes.push(node);
@@ -67,12 +68,36 @@ class FastGroupsService {
this.runScheduleAnimation = null;
this.runScheduledForMs = null;
}
getBoundingsForAllNodes() {
if (!this.cachedNodeBoundings) {
this.cachedNodeBoundings = {};
for (const node of app.graph._nodes) {
this.cachedNodeBoundings[node.id] = node.getBounding();
}
setTimeout(() => {
this.cachedNodeBoundings = null;
}, 50);
}
return this.cachedNodeBoundings;
}
recomputeInsideNodesForGroup(group) {
const cachedBoundings = this.getBoundingsForAllNodes();
const nodes = group.graph._nodes;
group._nodes.length = 0;
for (const node of nodes) {
const node_bounding = cachedBoundings[node.id];
if (!node_bounding || !LiteGraph.overlapBounding(group._bounding, node_bounding)) {
continue;
}
group._nodes.push(node);
}
}
getGroupsUnsorted(now) {
const graph = app.graph;
if (!this.groupsUnsorted.length || now - this.msLastUnsorted > this.msThreshold) {
this.groupsUnsorted = [...graph._groups];
for (const group of this.groupsUnsorted) {
group.recomputeInsideNodes();
this.recomputeInsideNodesForGroup(group);
group._rgthreeHasAnyActiveNode = group._nodes.some((n) => n.mode === LiteGraph.ALWAYS);
}
this.msLastUnsorted = now;