Merge pull request #25 from AEmotionStudio/bolt/optimize-render-overlays-9313858051447850514
⚡ Bolt: Optimize renderHtmlOverlays loop
This commit is contained in:
+7
-6
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user