From 9935ec5077fea2d37913697937fb55c4bdbccd4d Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sun, 18 Jan 2026 04:43:04 +0000 Subject: [PATCH] perf: optimize renderHtmlOverlays by hoisting invariants - Moves invariant calculations (DPR, scaling, pivot points) out of the widget loop in `renderHtmlOverlays`. - Reduces redundant property accesses and arithmetic operations per frame. - Improves performance for workflows with many visible nodes. --- .jules/bolt.md | 13 +++++++------ src/magnify-glass/MagnifyGlass.ts | 24 +++++++++++------------- 2 files changed, 18 insertions(+), 19 deletions(-) diff --git a/.jules/bolt.md b/.jules/bolt.md index 7c59e74..96282e3 100644 --- a/.jules/bolt.md +++ b/.jules/bolt.md @@ -1,6 +1,7 @@ -## 2024-05-23 - Layout Thrashing in Render Loops -**Learning:** Mixing DOM reads (like `getBoundingClientRect` or `getComputedStyle`) with DOM writes (like `appendChild` or `innerHTML = ''`) inside a loop causes "Layout Thrashing", forcing the browser to synchronously recalculate layout on every iteration. This is a massive performance bottleneck. -**Action:** Always separate read and write phases. -1. **Read Phase:** Collect all necessary measurements (rects, styles) and store them in a temporary structure (e.g., `RenderTask[]`). -2. **Write Phase:** Perform all DOM mutations in one go, ideally using a `DocumentFragment` to batch appends before inserting into the live DOM. -**Critical:** Ensure logic that depends on element type (like detecting videos to keep the animation loop alive) is preserved in the Read Phase. +## 2024-05-23 - Loop Invariant Code Motion +**Learning:** Even simple arithmetic operations (`/`, `+`, `*`) and property accesses (`state.canvasScale`, `rect.width`) add up when performed inside nested loops running at 60 FPS (e.g., iterating over nodes and then widgets). +**Action:** Always check if variables calculated inside a loop depend on the loop iterator. If not, hoist them to the outer scope ("Redundant calculations in loops"). + +## 2024-05-23 - DOM Property Access in Loops +**Learning:** Accessing DOM properties like `canvas.width` or `element.style` inside a loop is much slower than accessing a local variable. +**Action:** Cache DOM properties into local variables before entering hot loops. diff --git a/src/magnify-glass/MagnifyGlass.ts b/src/magnify-glass/MagnifyGlass.ts index 70da44a..56aff2b 100644 --- a/src/magnify-glass/MagnifyGlass.ts +++ b/src/magnify-glass/MagnifyGlass.ts @@ -412,8 +412,17 @@ export class MagnifyGlass { const rect = this.litegraphCanvas.getBoundingClientRect(); const dpr = rect.width > 0 ? this.litegraphCanvas.width / rect.width : 1; - const cursorCssX = this.state.x / dpr; - const cursorCssY = this.state.y / dpr; + + // Pre-calculate invariant values for the frame + const currentScale = this.state.canvasScale; + const isVirtualZoomMode = currentScale < 0.7; + const canvasRect = rect; + // Pivot point (mouse position) in CSS coordinates + const pivotCssX = this.state.x / dpr; + const pivotCssY = this.state.y / dpr; + + const cursorCssX = pivotCssX; + const cursorCssY = pivotCssY; const cursorGraphX = (cursorCssX - this.state.canvasOffsetX) / this.state.canvasScale; const cursorGraphY = (cursorCssY - this.state.canvasOffsetY) / this.state.canvasScale; const targetGraphCenterX = cursorGraphX + this.config.offsetX; @@ -506,23 +515,12 @@ export class MagnifyGlass { // READ: Force layout calculation here, batched together const widgetRect = elementToProcess.getBoundingClientRect(); - // Optimization: Use the widgetRect to check overlap immediately before potentially expensive computations - // Reuse canvasRect and dpr from top level scope - const canvasRect = rect; - const dpr = rect.width > 0 ? this.litegraphCanvas.width / rect.width : 1; - const currentScale = this.state.canvasScale; - const isVirtualZoomMode = currentScale < 0.7; - // Widget position in CSS coordinates relative to canvas const widgetCssX = widgetRect.left - canvasRect.left; const widgetCssY = widgetRect.top - canvasRect.top; const widgetCssWidth = widgetRect.width; const widgetCssHeight = widgetRect.height; - // Pivot point (mouse position) in CSS coordinates - const pivotCssX = this.state.x / dpr; - const pivotCssY = this.state.y / dpr; - let finalWidgetCssX: number; let finalWidgetCssY: number; let finalWidgetCssWidth: number;