Forráskód Böngészése

chore(playground): add memory test comparing Tres and plain Vue/THREE canvas performance (#917)

Alvaro Saburido 3 hónapja
szülő
commit
45f215cc9a
1 módosított fájl, 133 hozzáadás és 41 törlés
  1. 133 41
      playground/vue/src/pages/advanced/MemoryTresObjects.vue

+ 133 - 41
playground/vue/src/pages/advanced/MemoryTresObjects.vue

@@ -1,72 +1,164 @@
 <script setup lang="ts">
 import { TresCanvas } from '@tresjs/core'
+import type { Group, Material } from 'three'
+import { BoxGeometry, Color, Mesh, MeshBasicMaterial, PerspectiveCamera, Scene, Vector3, WebGLRenderer } from 'three'
+import { onUnmounted, ref } from 'vue'
 
-const toggleMax = 1000
+const toggleMax = 400
 const numObjectsMax = 2000
-const startTimeMS = Date.now()
-
+const startTimeMS = ref(0)
+const width = 900
+const height = 600
 const toggleCount = ref(0)
-const show = ref(false)
-const msg = ref('Test is running.')
+const showTres = ref(false)
+const showVueThree = ref(false)
+const msg = ref('Click Start Test to begin.')
 const r = ref(null)
-const isPaused = ref(true)
 
 let intervalId: ReturnType<typeof setInterval>
-
-const startInterval = () => {
-  intervalId = setInterval(() => {
+const testVueThree = (() => {
+  let renderer: WebGLRenderer | null = null
+  let scene: Scene | null = null
+  let camera: PerspectiveCamera | null = null
+  let frameCount = 0
+  function testVueThree() {
     if (toggleCount.value < toggleMax) {
-      // NOTE: Make sure that objects are mounted by
-      // checking `!!r.value`.
       if (r.value) {
-        show.value = false
-        toggleCount.value++
+        if (renderer) {
+          if (frameCount < 2) {
+            frameCount++
+            renderer.render(scene!, camera!)
+          }
+          else {
+            camera?.removeFromParent()
+            scene!.children.forEach((m) => { ((m as Mesh).material as Material).dispose(); (m as Mesh).geometry.dispose() })
+            renderer.dispose()
+            frameCount = 0
+            camera = null
+            scene = null
+            renderer = null
+            showVueThree.value = false
+            toggleCount.value++
+          }
+        }
+        else {
+          renderer = new WebGLRenderer({ canvas: r.value })
+          renderer.setSize(width, height)
+          renderer.setClearColor(new Color('#EEE'))
+          scene = new Scene()
+          camera = new PerspectiveCamera()
+          camera.position.x = 10
+          camera.position.y = 10
+          camera.lookAt(new Vector3(0, 0, 0))
+          for (let i = 0; i < numObjectsMax; i++) {
+            scene.add(new Mesh(new BoxGeometry(), new MeshBasicMaterial()))
+          }
+          scene.add(camera)
+          renderer.render(scene, camera)
+        }
       }
-      else {
-        show.value = true
+      else if (!showVueThree.value) {
+        showVueThree.value = true
       }
     }
     else {
+      const elapsedSec = (Date.now() - startTimeMS.value) / 1000
+      msg.value = `Plain Vue/THREE test completed in ${elapsedSec} seconds.`
       clearInterval(intervalId)
-      const elapsedSec = (Date.now() - startTimeMS) / 1000
-      msg.value = `Test completed in ${elapsedSec} seconds.`
     }
-  }, 1000 / 120)
-}
-
-const togglePause = () => {
-  isPaused.value = !isPaused.value
-  if (!isPaused.value) {
-    startInterval()
   }
-  else {
-    clearInterval(intervalId)
+  return testVueThree
+})()
+const testTres = (() => {
+  let frameCount = 0
+  return () => {
+    if (toggleCount.value < toggleMax) {
+      if (r.value && frameCount < 2) {
+        // NOTE: Wait until Tres has actually rendered before
+        // removing the canvas.
+        ((r.value as Group).children[0] as Mesh).onAfterRender = () => { frameCount++ }
+      }
+      else {
+        if (frameCount < 1) {
+          showTres.value = true
+        }
+        else {
+          toggleCount.value++
+          showTres.value = false
+          frameCount = 0
+        }
+      }
+    }
+    else {
+      const elapsedSec = (Date.now() - startTimeMS.value) / 1000
+      msg.value = `Tres test completed in ${elapsedSec} seconds.`
+      clearInterval(intervalId)
+    }
   }
+})()
+const isStarted = ref(false)
+const startTestTres = () => {
+  isStarted.value = true
+  startTimeMS.value = Date.now()
+  // NOTE: Using `setInterval`; it typically will keep
+  // running in situations were `requestAnimationFrame` will pause.
+  intervalId = setInterval(testTres, 1000 / 60)
+  msg.value = 'Test is running...'
 }
-
-onUnmounted(() => clearInterval(intervalId))
+const startTestVueThree = () => {
+  isStarted.value = true
+  startTimeMS.value = Date.now()
+  // NOTE: Using `setInterval`; it typically will keep
+  // running in situations were `requestAnimationFrame` will pause.
+  intervalId = setInterval(testVueThree, 1000 / 60)
+  msg.value = 'Test is running...'
+}
+onUnmounted(() => {
+  clearInterval(intervalId)
+})
 </script>
 
 <template>
   <OverlayInfo>
-    <h1>Memory test: Tres Objects</h1>
+    <h1>Memory test: Canvases with objects – Tres vs Plain Vue/THREE</h1>
+    <p><span style="color: red">IMPORTANT</span> Epileptic warning: the tests run on this page cause the screen to flash rapidly.</p>
     <h2>Setup</h2>
-    <p>This page will successively create and remove a TresCanvas containing a number of objects.</p>
+    <p>This test will create and remove {{ toggleMax }} canvas instances with {{ numObjectsMax }} objects/materials/geometries each.</p>
+    <h2>Note</h2>
+    <ul>
+      <li>These tests are intended to help spot memory leaks.</li>
+      <li>Faster/slower test duration does not indicate a problem.</li>
+    </ul>
     <h2>Status</h2>
     <p>{{ msg }}</p>
-    <p>Number of TresCanvases created: {{ toggleCount }} / {{ toggleMax }}</p>
-    <p>Number of Objects per TresCanvas: {{ numObjectsMax }}</p>
-    <button style="padding: 8px 16px; margin-top: 10px;" @click="togglePause">
-      {{ isPaused ? 'Start Test' : 'Pause Test' }}
+    <p>Number of canvases created: {{ toggleCount }} / {{ toggleMax }}</p>
+    <button
+      v-if="!isStarted"
+      style="padding: 8px 16px; margin-top: 10px;"
+      @click="startTestTres"
+    >
+      Start Tres test
+    </button>
+
+    <button
+      v-if="!isStarted"
+      style="padding: 8px 16px; margin-top: 10px;"
+      @click="startTestVueThree"
+    >
+      Start plain Vue/THREE test
     </button>
   </OverlayInfo>
-  <div v-if="show" style="width: 90%; height: 90%; border: 1px solid #F00">
-    <TresCanvas clear-color="black">
-      <TresGroup ref="r" />
-      <TresMesh v-for="_, i of Array.from({ length: numObjectsMax })" :key="i">
-        <TresMeshBasicMaterial />
-        <TresBoxGeometry />
-      </TresMesh>
+  <div v-if="showTres" :style="{ width: `${width}px`, height: `${height}px` }">
+    <TresCanvas clear-color="#EEE">
+      <TresGroup ref="r">
+        <TresMesh v-for="_, i of Array.from({ length: numObjectsMax })" :key="i">
+          <TresMeshBasicMaterial />
+          <TresBoxGeometry />
+        </TresMesh>
+      </TresGroup>
     </TresCanvas>
   </div>
+  <div v-if="showVueThree">
+    <canvas ref="r" clear-color="black"></canvas>
+  </div>
 </template>