Skip to content

[backport core/1.41] fix: 3D asset disappears when switching to image output in app mode#10230

Open
comfy-pr-bot wants to merge 1 commit intocore/1.41from
backport-9622-to-core-1.41
Open

[backport core/1.41] fix: 3D asset disappears when switching to image output in app mode#10230
comfy-pr-bot wants to merge 1 commit intocore/1.41from
backport-9622-to-core-1.41

Conversation

@comfy-pr-bot
Copy link
Member

@comfy-pr-bot comfy-pr-bot commented Mar 18, 2026

Backport of #9622 to core/1.41

Automatically created by backport workflow.

┆Issue is synchronized with this Notion page by Unito

…9622)

## Summary

Fix 3D asset disappearing when switching between 3D and image outputs in
app mode — missing `onUnmounted` cleanup leaked WebGL contexts.

## Changes

- **What**: Add `onUnmounted` hook to `Preview3d.vue` that calls
`viewer.cleanup()`, releasing the WebGL context when Vue destroys the
component via its v-if chain. Add unit tests covering init, cleanup on
unmount, and remount behavior.

## Review Focus

When switching outputs in app mode, Vue's v-if chain destroys and
recreates `Preview3d`. Without `onUnmounted` cleanup, the old `Load3d`
instance (WebGL context, RAF loop, ResizeObserver) leaks. After ~8-16
toggles, the browser's WebGL context limit is exhausted and new 3D
viewers silently fail to render.

<!-- Pipeline-Ticket: e36489d2-a9fb-47ca-9e27-88eb3170836b -->

---------

Co-authored-by: Alexander Brown <drjkl@comfy.org>
@comfy-pr-bot comfy-pr-bot requested a review from a team as a code owner March 18, 2026 01:08
@comfy-pr-bot comfy-pr-bot added the backport Backporting a PR onto a release candidate label Mar 18, 2026
@comfy-pr-bot comfy-pr-bot enabled auto-merge (squash) March 18, 2026 01:08
@github-actions
Copy link

github-actions bot commented Mar 18, 2026

🎨 Storybook: ✅ Built — View Storybook

Details

⏰ Completed at: 03/18/2026, 01:10:18 AM UTC

Links

@github-actions
Copy link

github-actions bot commented Mar 18, 2026

🎭 Playwright: ✅ 563 passed, 0 failed · 2 flaky

📊 Browser Reports
  • chromium: View Report (✅ 550 / ❌ 0 / ⚠️ 2 / ⏭️ 10)
  • chromium-2x: View Report (✅ 2 / ❌ 0 / ⚠️ 0 / ⏭️ 0)
  • chromium-0.5x: View Report (✅ 1 / ❌ 0 / ⚠️ 0 / ⏭️ 0)
  • mobile-chrome: View Report (✅ 10 / ❌ 0 / ⚠️ 0 / ⏭️ 0)

@github-actions
Copy link

📦 Bundle Size

⏳ Size data collection in progress…

⚡ Performance Report

⚠️ 6 regressions detected

Metric Baseline PR (n=3) Δ Sig
canvas-idle: style recalcs 124 124 +0% ⚠️ z=208.6
canvas-mouse-sweep: style recalcs 170 172 +1% ⚠️ z=42.9
dom-widget-clipping: style recalcs 43 43 -2% ⚠️ z=55.9
subgraph-dom-widget-clipping: style recalcs 74 72 -3% ⚠️ z=37.1
subgraph-idle: style recalcs 121 122 +1% ⚠️ z=145.9
subgraph-mouse-sweep: style recalcs 166 154 -7% ⚠️ z=41.1
All metrics
Metric Baseline PR (n=3) Δ Sig
canvas-idle: style recalcs 124 124 +0% ⚠️ z=208.6
canvas-idle: layouts 0 0 +0%
canvas-idle: task duration 419ms 400ms -5% z=0.1
canvas-mouse-sweep: style recalcs 170 172 +1% ⚠️ z=42.9
canvas-mouse-sweep: layouts 12 12 +0%
canvas-mouse-sweep: task duration 864ms 869ms +1% z=0.0
dom-widget-clipping: style recalcs 43 43 -2% ⚠️ z=55.9
dom-widget-clipping: layouts 0 0 +0%
dom-widget-clipping: task duration 366ms 357ms -3% z=-0.6
subgraph-dom-widget-clipping: style recalcs 74 72 -3% ⚠️ z=37.1
subgraph-dom-widget-clipping: layouts 0 0 +0%
subgraph-dom-widget-clipping: task duration 419ms 405ms -3% z=1.2
subgraph-idle: style recalcs 121 122 +1% ⚠️ z=145.9
subgraph-idle: layouts 0 0
subgraph-idle: task duration 396ms 369ms -7% z=-0.2
subgraph-mouse-sweep: style recalcs 166 154 -7% ⚠️ z=41.1
subgraph-mouse-sweep: layouts 16 16 +0%
subgraph-mouse-sweep: task duration 892ms 694ms -22% z=-1.3
Historical variance (last 10 runs)
Metric μ σ CV
canvas-idle: style recalcs 11 1 4.9%
canvas-idle: layouts 0 0 0.0%
canvas-idle: task duration 397ms 35ms 8.7%
canvas-idle: DOM nodes 22 1 5.9%
canvas-idle: script duration 26ms 2ms 8.9%
canvas-idle: event listeners 10 4 38.6%
canvas-idle: TBT 0ms 0ms 0.0%
canvas-idle: frame duration 17ms 0ms 0.0%
canvas-mouse-sweep: style recalcs 79 2 2.8%
canvas-mouse-sweep: layouts 12 0 0.0%
canvas-mouse-sweep: task duration 866ms 58ms 6.7%
canvas-mouse-sweep: DOM nodes 62 2 3.6%
canvas-mouse-sweep: script duration 136ms 7ms 5.5%
canvas-mouse-sweep: event listeners 9 4 46.3%
canvas-mouse-sweep: TBT 0ms 0ms 0.0%
canvas-mouse-sweep: frame duration 17ms 0ms 0.0%
dom-widget-clipping: style recalcs 13 1 4.1%
dom-widget-clipping: layouts 0 0 0.0%
dom-widget-clipping: task duration 368ms 18ms 4.8%
dom-widget-clipping: DOM nodes 22 2 7.1%
dom-widget-clipping: script duration 68ms 4ms 5.6%
dom-widget-clipping: event listeners 8 8 99.9%
dom-widget-clipping: TBT 0ms 0ms 0.0%
dom-widget-clipping: frame duration 17ms 0ms 0.0%
subgraph-dom-widget-clipping: style recalcs 48 1 1.4%
subgraph-dom-widget-clipping: layouts 0 0 0.0%
subgraph-dom-widget-clipping: task duration 380ms 21ms 5.5%
subgraph-dom-widget-clipping: DOM nodes 22 1 5.7%
subgraph-dom-widget-clipping: script duration 129ms 8ms 5.8%
subgraph-dom-widget-clipping: event listeners 17 7 41.7%
subgraph-dom-widget-clipping: TBT 0ms 0ms 0.0%
subgraph-dom-widget-clipping: frame duration 17ms 0ms 0.0%
subgraph-idle: style recalcs 11 1 6.9%
subgraph-idle: layouts 0 0 0.0%
subgraph-idle: task duration 375ms 31ms 8.3%
subgraph-idle: DOM nodes 22 2 7.8%
subgraph-idle: script duration 21ms 3ms 12.7%
subgraph-idle: event listeners 12 8 63.4%
subgraph-idle: TBT 0ms 0ms 0.0%
subgraph-idle: frame duration 17ms 0ms 0.0%
subgraph-mouse-sweep: style recalcs 80 2 2.2%
subgraph-mouse-sweep: layouts 16 0 0.0%
subgraph-mouse-sweep: task duration 784ms 70ms 9.0%
subgraph-mouse-sweep: DOM nodes 67 2 3.1%
subgraph-mouse-sweep: script duration 102ms 7ms 7.3%
subgraph-mouse-sweep: event listeners 8 4 51.6%
subgraph-mouse-sweep: TBT 0ms 0ms 0.0%
subgraph-mouse-sweep: frame duration 17ms 0ms 0.0%
Trend (last 10 commits on main)
Metric Trend Dir Latest
canvas-idle: style recalcs █▅▁▃▁▃▆█▆█ ➡️ 12
canvas-idle: layouts ▄▄▄▄▄▄▄▄▄▄ ➡️ 0
canvas-idle: task duration ▃▃▅▆▂█▃▁▃▃ ➡️ 391ms
canvas-idle: DOM nodes █▄▁▂▂▅▆▆▇▇ ➡️ 24
canvas-idle: script duration ▅▃▆▇▅█▄▁▅▆ ➡️ 27ms
canvas-idle: event listeners █▂▁▂▇██▂█▆ 📈 11
canvas-idle: TBT ▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
canvas-idle: frame duration ▁▆█▆▆▃▁▁▃▁ ➡️ 17ms
canvas-mouse-sweep: style recalcs ▂▁▄▄▆▇▆▃█▅ ➡️ 79
canvas-mouse-sweep: layouts ▄▄▄▄▄▄▄▄▄▄ ➡️ 12
canvas-mouse-sweep: task duration ▃▂▄▄▅█▆▁▆▄ ➡️ 868ms
canvas-mouse-sweep: DOM nodes ▁▁▄▂▅█▇▃▆▆ ➡️ 64
canvas-mouse-sweep: script duration ▆▆▆▅▅█▆▁▅▆ ➡️ 139ms
canvas-mouse-sweep: event listeners ▁▇▁▁▁██▇▁█ 📈 13
canvas-mouse-sweep: TBT ▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
canvas-mouse-sweep: frame duration ▄▁██▁▅██▅▄ ➡️ 17ms
dom-widget-clipping: style recalcs ▄█▇▇▁▇▄▇▂▅ ➡️ 13
dom-widget-clipping: layouts ▄▄▄▄▄▄▄▄▄▄ ➡️ 0
dom-widget-clipping: task duration ▃▅▆▅▂▇█▁▅▅ ➡️ 371ms
dom-widget-clipping: DOM nodes ▄█▇▅▁▅▄▇▃▄ ➡️ 21
dom-widget-clipping: script duration ▅▇▇▆▃█▇▁▇▇ ➡️ 71ms
dom-widget-clipping: event listeners ▅██▁▁▁▁█▁▁ 📉 2
dom-widget-clipping: TBT ▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
dom-widget-clipping: frame duration ▄█▇▅▇▇▅▅▁▇ ➡️ 17ms
subgraph-dom-widget-clipping: style recalcs ▃▁▆█▇▃▆▇█▅ ➡️ 48
subgraph-dom-widget-clipping: layouts ▄▄▄▄▄▄▄▄▄▄ ➡️ 0
subgraph-dom-widget-clipping: task duration ▅▂▅█▂▆█▁▂▇ ➡️ 398ms
subgraph-dom-widget-clipping: DOM nodes ▂▁▅▅▅▁▇▅█▄ ➡️ 22
subgraph-dom-widget-clipping: script duration ▅▂▄█▂▅▇▁▂▅ ➡️ 131ms
subgraph-dom-widget-clipping: event listeners ▁▅██▁▁█▅█▅ 📈 16
subgraph-dom-widget-clipping: TBT ▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
subgraph-dom-widget-clipping: frame duration ▄█▄▄▄▃▁▆▃▃ ➡️ 17ms
subgraph-idle: style recalcs ▅▁▂▁▆▃▃██▇ ➡️ 12
subgraph-idle: layouts ▄▄▄▄▄▄▄▄▄▄ ➡️ 0
subgraph-idle: task duration ▁▃▆▅▂█▅▁▁▄ ➡️ 378ms
subgraph-idle: DOM nodes ▄▁▂▁▅▃▂▇█▇ ➡️ 24
subgraph-idle: script duration ▂▃▇▆▂█▅▂▁▅ ➡️ 22ms
subgraph-idle: event listeners ▁▁▁▁▅▄▁███ 📈 21
subgraph-idle: TBT ▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
subgraph-idle: frame duration ▃▆▆▆▃▆▁▃▆█ ➡️ 17ms
subgraph-mouse-sweep: style recalcs ▄▂▄█▅▆▃▁▃▄ ➡️ 81
subgraph-mouse-sweep: layouts ▄▄▄▄▄▄▄▄▄▄ ➡️ 16
subgraph-mouse-sweep: task duration ▄▄▅▇▄█▆▁▃▅ ➡️ 785ms
subgraph-mouse-sweep: DOM nodes ▃▂▃█▄▅▃▁▄▃ ➡️ 66
subgraph-mouse-sweep: script duration ▅▆▇▆▅██▁▄▆ ➡️ 105ms
subgraph-mouse-sweep: event listeners ▁▁▁█▇▂▁▇▇▁ ➡️ 5
subgraph-mouse-sweep: TBT ▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
subgraph-mouse-sweep: frame duration ▄▆▄▆▃▃█▁▃▃ ➡️ 17ms
Raw data
{
  "timestamp": "2026-03-18T01:13:20.645Z",
  "gitSha": "63d79965f4bc9c9af536427479bb5a0971a51671",
  "branch": "backport-9622-to-core-1.41",
  "measurements": [
    {
      "name": "canvas-idle",
      "durationMs": 2007.852000000014,
      "styleRecalcs": 123,
      "styleRecalcDurationMs": 18.738999999999997,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 424.99600000000004,
      "heapDeltaBytes": -3600688
    },
    {
      "name": "canvas-idle",
      "durationMs": 2028.1610000000114,
      "styleRecalcs": 124,
      "styleRecalcDurationMs": 21.788999999999998,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 390.414,
      "heapDeltaBytes": -3735260
    },
    {
      "name": "canvas-idle",
      "durationMs": 2038.7710000000538,
      "styleRecalcs": 124,
      "styleRecalcDurationMs": 21.283000000000005,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 384.11899999999997,
      "heapDeltaBytes": -3342916
    },
    {
      "name": "canvas-mouse-sweep",
      "durationMs": 2057.7480000000037,
      "styleRecalcs": 183,
      "styleRecalcDurationMs": 57.15699999999999,
      "layouts": 12,
      "layoutDurationMs": 3.6809999999999996,
      "taskDurationMs": 1039.8509999999999,
      "heapDeltaBytes": -3495688
    },
    {
      "name": "canvas-mouse-sweep",
      "durationMs": 1798.014999999964,
      "styleRecalcs": 163,
      "styleRecalcDurationMs": 41.779,
      "layouts": 12,
      "layoutDurationMs": 3.064,
      "taskDurationMs": 759.55,
      "heapDeltaBytes": -4030132
    },
    {
      "name": "canvas-mouse-sweep",
      "durationMs": 1863.0259999999907,
      "styleRecalcs": 170,
      "styleRecalcDurationMs": 47.173,
      "layouts": 12,
      "layoutDurationMs": 3.269,
      "taskDurationMs": 806.572,
      "heapDeltaBytes": -3226076
    },
    {
      "name": "dom-widget-clipping",
      "durationMs": 527.9110000000173,
      "styleRecalcs": 39,
      "styleRecalcDurationMs": 12.662,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 345.40999999999997,
      "heapDeltaBytes": 6496776
    },
    {
      "name": "dom-widget-clipping",
      "durationMs": 607.0689999999672,
      "styleRecalcs": 46,
      "styleRecalcDurationMs": 16.315,
      "layouts": 1,
      "layoutDurationMs": 0.1950000000000001,
      "taskDurationMs": 377.185,
      "heapDeltaBytes": 8256908
    },
    {
      "name": "dom-widget-clipping",
      "durationMs": 580.0100000000157,
      "styleRecalcs": 43,
      "styleRecalcDurationMs": 13.064000000000002,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 347.97599999999994,
      "heapDeltaBytes": 7639112
    },
    {
      "name": "subgraph-dom-widget-clipping",
      "durationMs": 578.5220000000209,
      "styleRecalcs": 73,
      "styleRecalcDurationMs": 14.309000000000001,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 397.95799999999997,
      "heapDeltaBytes": 15267900
    },
    {
      "name": "subgraph-dom-widget-clipping",
      "durationMs": 561.4030000000412,
      "styleRecalcs": 71,
      "styleRecalcDurationMs": 14.8,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 399.164,
      "heapDeltaBytes": 15452840
    },
    {
      "name": "subgraph-dom-widget-clipping",
      "durationMs": 584.0759999999818,
      "styleRecalcs": 72,
      "styleRecalcDurationMs": 15.207999999999998,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 416.674,
      "heapDeltaBytes": -8829712
    },
    {
      "name": "subgraph-idle",
      "durationMs": 2011.342999999954,
      "styleRecalcs": 123,
      "styleRecalcDurationMs": 21.332,
      "layouts": 1,
      "layoutDurationMs": 0.178,
      "taskDurationMs": 381.95799999999997,
      "heapDeltaBytes": -2990572
    },
    {
      "name": "subgraph-idle",
      "durationMs": 1995.6849999999804,
      "styleRecalcs": 121,
      "styleRecalcDurationMs": 18.647000000000006,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 363.01099999999997,
      "heapDeltaBytes": -4310540
    },
    {
      "name": "subgraph-idle",
      "durationMs": 2011.1279999999851,
      "styleRecalcs": 122,
      "styleRecalcDurationMs": 17.707,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 361.41900000000004,
      "heapDeltaBytes": -3738272
    },
    {
      "name": "subgraph-mouse-sweep",
      "durationMs": 1691.30100000001,
      "styleRecalcs": 154,
      "styleRecalcDurationMs": 42.157,
      "layouts": 16,
      "layoutDurationMs": 3.7410000000000005,
      "taskDurationMs": 686.332,
      "heapDeltaBytes": -6536624
    },
    {
      "name": "subgraph-mouse-sweep",
      "durationMs": 1682.2839999999815,
      "styleRecalcs": 154,
      "styleRecalcDurationMs": 42.963,
      "layouts": 16,
      "layoutDurationMs": 3.913,
      "taskDurationMs": 700.446,
      "heapDeltaBytes": -6152700
    },
    {
      "name": "subgraph-mouse-sweep",
      "durationMs": 1678.4999999999854,
      "styleRecalcs": 154,
      "styleRecalcDurationMs": 43.014,
      "layouts": 16,
      "layoutDurationMs": 4.141000000000001,
      "taskDurationMs": 695.053,
      "heapDeltaBytes": -6460636
    }
  ]
}

@dosubot dosubot bot added the size:XL This PR changes 500-999 lines, ignoring generated files. label Mar 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backport Backporting a PR onto a release candidate size:XL This PR changes 500-999 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants