fix: prevent WebGLRenderer leak in app mode 3D preview (#9766)

## Summary
Reuse the Load3d instance when switching between 3D results in app mode
instead of creating a new WebGLRenderer each time. Add onUnmounted
cleanup to Preview3d to release WebGL resources when the component is
removed.

## Screenshots (if applicable)
before


https://github.com/user-attachments/assets/c9818d10-941f-4994-9b48-2710c88454e7



after


https://github.com/user-attachments/assets/36361763-6800-4bc8-8089-14d64b7fcd16

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9766-fix-prevent-WebGLRenderer-leak-in-app-mode-3D-preview-3216d73d365081e19305d7255b71bc49)
by [Unito](https://www.unito.io)
This commit is contained in:
Terry Jia
2026-03-11 22:12:20 -07:00
committed by GitHub
parent b61029b9da
commit 852d77159e
2 changed files with 29 additions and 2 deletions

View File

@@ -357,7 +357,8 @@ export const useLoad3dViewer = (node?: LGraphNode) => {
}
/**
* Initialize viewer in standalone mode (for asset preview)
* Initialize viewer in standalone mode (for asset preview).
* Creates the Load3d instance once; subsequent calls reuse it.
*/
const initializeStandaloneViewer = async (
containerRef: HTMLElement,
@@ -366,6 +367,11 @@ export const useLoad3dViewer = (node?: LGraphNode) => {
if (!containerRef) return
try {
if (load3d) {
await loadStandaloneModel(modelUrl)
return
}
isStandaloneMode.value = true
load3d = new Load3d(containerRef, {
@@ -392,6 +398,23 @@ export const useLoad3dViewer = (node?: LGraphNode) => {
setupAnimationEvents()
} catch (error) {
console.error('Error initializing standalone 3D viewer:', error)
useToastStore().addAlert(t('toastMessages.failedToLoadModel'))
}
}
/**
* Load a new model into an existing standalone viewer,
* reusing the same WebGLRenderer.
*/
const loadStandaloneModel = async (modelUrl: string) => {
if (!load3d) return
try {
await load3d.loadModel(modelUrl)
isSplatModel.value = load3d.isSplatModel()
isPlyModel.value = load3d.isPlyModel()
} catch (error) {
console.error('Error loading model in standalone viewer:', error)
useToastStore().addAlert('Failed to load 3D model')
}
}

View File

@@ -1,5 +1,5 @@
<script setup lang="ts">
import { ref, useTemplateRef, watch } from 'vue'
import { onUnmounted, ref, useTemplateRef, watch } from 'vue'
import Load3DControls from '@/components/load3d/Load3DControls.vue'
import AnimationControls from '@/components/load3d/controls/AnimationControls.vue'
@@ -19,6 +19,10 @@ watch([containerRef, () => modelUrl], async () => {
await viewer.value.initializeStandaloneViewer(containerRef.value, modelUrl)
})
onUnmounted(() => {
viewer.value.cleanup()
})
//TODO: refactor to add control buttons
</script>
<template>