瀏覽代碼

fix: force memory free allocation when disposing materials and geometries (#463)

Alvaro Saburido 1 年之前
父節點
當前提交
1ef353363d

+ 1 - 1
playground/package.json

@@ -13,7 +13,7 @@
     "vue-router": "^4.2.5"
   },
   "devDependencies": {
-    "@tresjs/leches": "^0.13.0",
+    "@tresjs/leches": "0.15.0-next.3",
     "@tweakpane/plugin-essentials": "^0.2.0",
     "unplugin-auto-import": "^0.17.1",
     "vite-plugin-glsl": "^1.2.0",

+ 7 - 0
playground/src/pages/empty.vue

@@ -0,0 +1,7 @@
+<script setup>
+
+</script>
+
+<template>
+  <div />
+</template>

+ 49 - 0
playground/src/pages/perf/AkuAku.vue

@@ -0,0 +1,49 @@
+<script setup lang="ts">
+import { useGLTF } from '@tresjs/cientos'
+import { useControls } from '@tresjs/leches'
+
+const { nodes } = await useGLTF(
+  'https://raw.githubusercontent.com/Tresjs/assets/main/models/gltf/aku-aku/AkuAku.gltf',
+  { draco: true },
+)
+
+const model = nodes.AkuAku
+
+/* useControls({
+  button: {
+    label: 'Manual dispose',
+    type: 'button',
+    onClick() {
+      disposeModel()
+    },
+  },
+}) */
+
+function disposeModel() {
+  console.log('disposingModel')
+  model.traverse((child) => {
+    if (child.isMesh) {
+      // Dispose of the material
+      if (child.material) {
+        child.material.dispose()
+      }
+        
+      // Dispose of the geometry
+      if (child.geometry) {
+        child.geometry.dispose()
+      }
+    }
+  })
+  console.log('disposingModel Finished')
+}
+
+model.traverse((child) => {
+  if (child.material) {
+    console.log('child.material', child.material.uuid)
+  }
+})
+</script>
+
+<template>
+  <primitive :object="model" />
+</template>

+ 65 - 0
playground/src/pages/perf/index.vue

@@ -0,0 +1,65 @@
+<script setup lang="ts">
+import { TresCanvas } from '@tresjs/core'
+import { BasicShadowMap, SRGBColorSpace, NoToneMapping } from 'three'
+import { TresLeches, Perf, useControls } from '@tresjs/leches'
+import '@tresjs/leches/styles'
+import { OrbitControls } from '@tresjs/cientos'
+import { useRouter } from 'vue-router'
+import AkuAku from './AkuAku.vue'
+
+const gl = {
+  clearColor: '#82DBC5',
+  shadows: true,
+  alpha: false,
+  shadowMapType: BasicShadowMap,
+  outputColorSpace: SRGBColorSpace,
+  toneMapping: NoToneMapping,
+}
+
+const router = useRouter()
+
+const { sphere } = useControls({
+  sphere: true,
+})
+
+const ctx = ref(null)
+
+watchEffect(() => {
+  if (!ctx.value) return
+  console.log('ctx', ctx.value)
+})
+
+useControls({
+  button: {
+    label: 'Render dispose',
+    type: 'button',
+    onClick() {
+      ctx.value.dispose()
+    },
+  },
+
+})
+</script>
+
+<template>
+  <TresLeches />
+  <TresCanvas
+    v-bind="gl"
+    ref="ctx"
+  >
+    <Perf />
+    <TresPerspectiveCamera :position="[3, 3, 3]" />
+    <OrbitControls />
+    <Suspense> 
+      <AkuAku v-if="sphere" />
+    </Suspense>
+    <!--  <TresMesh
+      v-if="sphere.value"
+      :position="[0, 0, 0]"
+    >
+      <TresSphereGeometry />
+      <TresMeshStandardMaterial color="teal" />
+    </TresMesh> -->
+    <TresAmbientLight :intensity="1" />
+  </TresCanvas>
+</template>

+ 10 - 0
playground/src/router.ts

@@ -71,6 +71,16 @@ const routes = [
     name: 'Click Blocking Box',
     component: () => import('./pages/click-blocking-box.vue'),
   },
+  {
+    path: '/perf',
+    name: 'Perf',
+    component: () => import('./pages/perf/index.vue'),
+  },
+  {
+    path: '/empty',
+    name: 'empty',
+    component: () => import('./pages/empty.vue'),
+  },
 ]
 export const router = createRouter({
   history: createWebHistory(),

+ 33 - 10
pnpm-lock.yaml

@@ -150,8 +150,8 @@ importers:
         version: 4.2.5(vue@3.3.9)
     devDependencies:
       '@tresjs/leches':
-        specifier: ^0.13.0
-        version: 0.13.0(vue@3.3.9)
+        specifier: 0.15.0-next.3
+        version: 0.15.0-next.3(three@0.158.0)(vite@4.5.0)(vue@3.3.9)
       '@tweakpane/plugin-essentials':
         specifier: ^0.2.0
         version: 0.2.0(tweakpane@4.0.1)
@@ -311,7 +311,6 @@ packages:
 
   /@alvarosabu/utils@3.1.1:
     resolution: {integrity: sha512-DdZNe0i0UsqA2X/+JtaznG4qGorU24FaHqxMAhVspQaC1my+YAVQLpfw8GilHpzMxPxX4sGVzuTnBF8GOdG5oA==}
-    dev: false
 
   /@ampproject/remapping@2.2.1:
     resolution: {integrity: sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==}
@@ -1742,6 +1741,20 @@ packages:
       - '@vue/composition-api'
       - react
 
+  /@tresjs/core@3.5.1(three@0.158.0)(vue@3.3.9):
+    resolution: {integrity: sha512-j7fHT3X8NamQk+n5A20r/UUkInHWQOJr5dzzcuOCdD6s3FFc3WMYtbRq+cJN8FR8mS0vpSK6h9s82com8cmRNA==}
+    peerDependencies:
+      three: '>=0.133'
+      vue: '>=3.3'
+    dependencies:
+      '@alvarosabu/utils': 3.1.1
+      '@vueuse/core': 10.6.1(vue@3.3.9)
+      three: 0.158.0
+      vue: 3.3.9(typescript@5.3.2)
+    transitivePeerDependencies:
+      - '@vue/composition-api'
+    dev: true
+
   /@tresjs/eslint-config-base@0.2.1(@typescript-eslint/eslint-plugin@6.13.0)(@typescript-eslint/parser@6.13.0)(eslint@8.54.0):
     resolution: {integrity: sha512-9fkwDaNu4nLKujeERi5d1S7+ZdZpxBE+g/jUbM4ywhn/+5P7Qv8dXlo1vB05LteX5cTBnZxHQTFrJGK+sMcFdg==}
     peerDependencies:
@@ -1806,16 +1819,22 @@ packages:
       - supports-color
     dev: true
 
-  /@tresjs/leches@0.13.0(vue@3.3.9):
-    resolution: {integrity: sha512-Fgvwl6q3Hb3DVlM71WHhqG0QSXOGcdcQN6d2EU78etLBUj7SQSq2mBeS0WNZ8r0bdNfB1tNkrPkA3uBdMS4yuw==}
+  /@tresjs/leches@0.15.0-next.3(three@0.158.0)(vite@4.5.0)(vue@3.3.9):
+    resolution: {integrity: sha512-kI8DovuzZ/mnSY3ifEiKJ1fd3aBoM5hYSfX0uwlJ6KWblVafrhds1J8j5aoFq6Kx0pjDVsY72fL8itAfm0ZrzQ==}
     peerDependencies:
+      three: '>=0.133'
       vue: '>=3.3.4'
     dependencies:
-      '@unocss/core': 0.57.2
+      '@tresjs/core': 3.5.1(three@0.158.0)(vue@3.3.9)
+      '@types/three': 0.158.3
+      '@unocss/core': 0.57.7
       '@vueuse/components': 10.5.0(vue@3.3.9)
+      three: 0.158.0
+      vite-plugin-css-injected-by-js: 3.3.0(vite@4.5.0)
       vue: 3.3.9(typescript@5.3.2)
     transitivePeerDependencies:
       - '@vue/composition-api'
+      - vite
     dev: true
 
   /@trysound/sax@0.2.0:
@@ -2150,10 +2169,6 @@ packages:
       unconfig: 0.3.11
     dev: true
 
-  /@unocss/core@0.57.2:
-    resolution: {integrity: sha512-iTmowhObigxeqcxtEW4v+mAEQtFslifTG0Fiw8kXs3+t4L6fcnjj0i7/FtBbz+nOxrWyt2EzdkUyjpLGQa/yCw==}
-    dev: true
-
   /@unocss/core@0.57.7:
     resolution: {integrity: sha512-1d36M0CV3yC80J0pqOa5rH1BX6g2iZdtKmIb3oSBN4AWnMCSrrJEPBrUikyMq2TEQTrYWJIVDzv5A9hBUat3TA==}
     dev: true
@@ -8187,6 +8202,14 @@ packages:
     resolution: {integrity: sha512-Bww2Xd5tOGsZ1yZ9rQiGneryvsL1u86znPrqeQjCsXPsG72pnSdV5lcQA+cy8UNDguMqyTJiCevlNUbLnT85UA==}
     dev: true
 
+  /vite-plugin-css-injected-by-js@3.3.0(vite@4.5.0):
+    resolution: {integrity: sha512-xG+jyHNCmUqi/TXp6q88wTJGeAOrNLSyUUTp4qEQ9QZLGcHWQQsCsSSKa59rPMQr8sOzfzmWDd8enGqfH/dBew==}
+    peerDependencies:
+      vite: '>2.0.0-0'
+    dependencies:
+      vite: 4.5.0
+    dev: true
+
   /vite-plugin-dts@3.6.3(typescript@5.3.2)(vite@5.0.2):
     resolution: {integrity: sha512-NyRvgobl15rYj65coi/gH7UAEH+CpSjh539DbGb40DfOTZSvDLNYTzc8CK4460W+LqXuMK7+U3JAxRB3ksrNPw==}
     engines: {node: ^14.18.0 || >=16.0.0}

+ 7 - 2
src/components/TresCanvas.vue

@@ -101,8 +101,13 @@ const mountCustomRenderer = (context: TresContext) => {
   render(h(InternalComponent), scene.value as unknown as TresObject)
 }
 
-const dispose = (context: TresContext) => {
+const dispose = (context: TresContext, force = false) => {
   scene.value.children = []
+  if (force) {
+    context.renderer.value.dispose()
+    context.renderer.value.renderLists.dispose()
+    context.renderer.value.forceContextLoss()
+  }
   mountCustomRenderer(context)
   resume()
 }
@@ -111,7 +116,7 @@ const disableRender = computed(() => props.disableRender)
 
 const context = shallowRef<TresContext | null>(null)
 
-defineExpose({ context })
+defineExpose({ context, dispose: () => dispose(context.value as TresContext, true) })
 
 onMounted(() => {
   const existingCanvas = canvas as Ref<HTMLCanvasElement>

+ 0 - 1
src/composables/useTresContextProvider/index.ts

@@ -85,7 +85,6 @@ export function useTresContextProvider({
   }
 
   provide('useTres', toProvide)
-
   return toProvide
 }
 

+ 9 - 4
src/core/nodeOps.ts

@@ -123,9 +123,15 @@ export const nodeOps: RendererOptions<TresObject, TresObject> = {
       const disposeMaterialsAndGeometries = (object3D: Object3D) => {
         const tresObject3D = object3D as TresObject3D
 
-        if (!object3D.userData.tres__materialViaProp) tresObject3D.material?.dispose()
-        if (!object3D.userData.tres__geometryViaProp)
+        if (!object3D.userData.tres__materialViaProp) {
+          tresObject3D.material?.dispose()
+          tresObject3D.material = undefined
+        }
+          
+        if (!object3D.userData.tres__geometryViaProp) {
           tresObject3D.geometry?.dispose()
+          tresObject3D.geometry = undefined
+        }
       }
 
       const deregisterAtPointerEventHandler = scene?.userData.tres__deregisterAtPointerEventHandler
@@ -161,6 +167,7 @@ export const nodeOps: RendererOptions<TresObject, TresObject> = {
           deregisterCamera?.(object as Camera)
       }
 
+      node.removeFromParent?.()
       object3D.traverse((child: Object3D) => {
         disposeMaterialsAndGeometries(child)
         deregisterCameraIfRequired(child)
@@ -172,8 +179,6 @@ export const nodeOps: RendererOptions<TresObject, TresObject> = {
       deregisterAtPointerEventHandlerIfRequired?.(object3D as TresObject)
     }
 
-    node.removeFromParent?.()
-
     node.dispose?.()
   },
   patchProp(node, prop, _prevValue, nextValue) {