Kaynağa Gözat

feat: starting from scratch due memory performance issues

alvarosabu 1 yıl önce
ebeveyn
işleme
1840095270

+ 1 - 6
playground/src/components/TheExperience.vue

@@ -21,10 +21,6 @@ const wireframe = ref(true)
 const canvas = ref()
 const meshRef = ref()
 
-const { isVisible } = useControls({
-  isVisible: true,
-})
-
 watchEffect(() => {
   if (meshRef.value) {
     console.log(meshRef.value)
@@ -33,12 +29,12 @@ watchEffect(() => {
 </script>
 
 <template>
-  <TresLeches />
   <TresCanvas
     v-bind="gl"
     ref="canvas"
     window-size
     class="awiwi"
+    render-mode="on-demand"
     :style="{ background: '#008080' }"
   >
     <TresPerspectiveCamera
@@ -76,7 +72,6 @@ watchEffect(() => {
         color="#D3FC8A"
       />
     </TresMesh>
-    <TheSphere v-if="isVisible" />
     <TresAxesHelper :args="[1]" />
     <TresDirectionalLight
       :position="[0, 2, 4]"

+ 1 - 1
plugins/vite-plugin-tres/client/.nuxt/manifest/latest.json

@@ -1 +1 @@
-{"id":"dev","timestamp":1704885707754}
+{"id":"dev","timestamp":1704897175593}

+ 1 - 1
plugins/vite-plugin-tres/client/.nuxt/manifest/meta/dev.json

@@ -1 +1 @@
-{"id":"dev","timestamp":1704885707754,"matcher":{"static":{},"wildcard":{},"dynamic":{}},"prerendered":[]}
+{"id":"dev","timestamp":1704897175593,"matcher":{"static":{},"wildcard":{},"dynamic":{}},"prerendered":[]}

+ 3 - 3
plugins/vite-plugin-tres/client/.nuxt/nitro.json

@@ -1,5 +1,5 @@
 {
-  "date": "2024-01-10T11:21:58.549Z",
+  "date": "2024-01-10T14:32:59.738Z",
   "preset": "nitro-dev",
   "framework": {
     "name": "nuxt",
@@ -9,9 +9,9 @@
     "nitro": "2.8.1"
   },
   "dev": {
-    "pid": 82846,
+    "pid": 8422,
     "workerAddress": {
-      "socketPath": "/var/folders/66/14k3nnbx1g505216sq4xdfdc0000gn/T/nitro/worker-82846-2.sock"
+      "socketPath": "/var/folders/66/14k3nnbx1g505216sq4xdfdc0000gn/T/nitro/worker-8422-2.sock"
     }
   }
 }

+ 23 - 239
plugins/vite-plugin-tres/client/composables/useDevtoolsHook.ts

@@ -1,259 +1,43 @@
-import type { TresContext, TresObject } from '@tresjs/core'
-import { DoubleSide, MeshBasicMaterial } from 'three'
-import type { Mesh, Object3D, type Scene, type WebGLRenderer } from 'three'
-import { reactive, shallowReactive } from 'vue'
-import type { SceneGraphObject } from '../types'
-import { HightlightMesh } from '../utils/highlightedMesh'
+import { reactive, ref, toRefs } from 'vue'
+import type { DevtoolsContextPayload, DevtoolsPerformancePayload, DevtoolsEvent } from '@tresjs/core'
 
-interface FPSState {
-  value: number
-  accumulator: number[]
-  lastLoggedTime: number
-  logInterval: number
-}
-
-interface MemoryState {
-  currentMem: number
-  averageMem: number
-  maxMemory: number
-  allocatedMem: number
-  accumulator: number[]
-  lastLoggedTime: number
-  logInterval: number
-}
-
-interface RendererState {
-  info: {
-    render: {
-      frame: number
-      calls: number
-      triangles: number
-      points: number
-      lines: number
-    }
-    memory: {
-      geometries: number
-      textures: number
-    }
-    programs: WebGLProgram[]
-  }
-}
-
-interface InternalState {
-  selectedObject: Object3D | null
-  prevInstance: Object3D | null
-  highlightMesh: Mesh | null
-}
-
-interface DevtoolsHookReturn {
-  scene: {
-    objects: number
-    graph: Record<string, unknown>
-    value: Scene | undefined
-  }
-  fps: FPSState
-  memory: MemoryState
-  renderer: RendererState
-  internal: InternalState
-  highlightObject: (object: TresObject) => void
-  selectObject: (object: TresObject) => void
-}
-
-const scene = reactive<{
-  objects: number
-  graph: Record<string, unknown>
-  value: Scene | undefined
-}>({
-  objects: 0,
-  graph: {},
-  value: undefined,
+const state = reactive({
+  connected: false,
+  scene: {},
 })
 
-const gl = {
-  internal: reactive<InternalState>({
-    selectedObject: null,
-    prevInstance: null,
-    highlightMesh: null,
-  }),
-  fps: reactive<FPSState>({
-    value: 0,
-    accumulator: [],
-    lastLoggedTime: Date.now(),
-    logInterval: 1000,
-  }),
-  memory: reactive<MemoryState>({
-    currentMem: 0,
-    averageMem: 0,
-    maxMemory: 0,
-    allocatedMem: 0,
-    accumulator: [],
-    lastLoggedTime: Date.now(),
-    logInterval: 1000,
-  }),
-  renderer: reactive<RendererState>({
-    info: {
-      render: {
-        frame: 0,
-        calls: 0,
-        triangles: 0,
-        points: 0,
-        lines: 0,
-      },
-      memory: {
-        geometries: 0,
-        textures: 0,
-      },
-      programs: [],
-    },
-  }),
-}
+function handlePerformanceEvent(payload: DevtoolsPerformancePayload) {
 
-const icons: Record<string, string> = {
-  scene: 'i-carbon-web-services-container',
-  perspectivecamera: 'i-carbon-video',
-  mesh: 'i-carbon-cube',
-  group: 'i-carbon-group-objects',
-  ambientlight: 'i-carbon-light',
-  directionallight: 'i-carbon-light',
-  spotlight: 'i-iconoir-project-curve-3d',
-  position: 'i-iconoir-axes',
-  rotation: 'i-carbon-rotate-clockwise',
-  scale: 'i-iconoir-ellipse-3d-three-points',
-  bone: 'i-ph-bone',
-  skinnedmesh: 'carbon:3d-print-mesh',
 }
 
-function createNode(object: TresObject) {
-  const node: SceneGraphObject = {
-    uuid: object.uuid,
-    name: object.name,
-    type: object.type,
-    icon: icons[object.type.toLowerCase()] || 'i-carbon-cube',
-    position: {
-      x: object.position.x,
-      y: object.position.y,
-      z: object.position.z,
-    },
-    rotation: {
-      x: object.rotation.x,
-      y: object.rotation.y,
-      z: object.rotation.z,
-    },
-    children: [],
-  }
-
-  if (object.type === 'Mesh') {
-    node.material = object.material
-    node.geometry = object.geometry
-    node.scale = {
-      x: object.scale.x,
-      y: object.scale.y,
-      z: object.scale.z,
-    }
-  }
-
-  if (object.type.includes('Light')) {
-    node.color = object.color.getHexString()
-    node.intensity = object.intensity
-  }
-  return node
+function handleContextEvent(payload: DevtoolsContextPayload) {
+  state.scene = { ...payload.scene }
+/*   console.count('handleContextEvent') */
 }
 
-function getSceneGraph(scene: TresObject) {
+export function useDevtoolsHook() {
+  if (window.parent.parent.__TRES__DEVTOOLS__) return
   
-  function buildGraph(object: TresObject, node: SceneGraphObject) {
-    object.children.forEach((child: TresObject) => {
-      const childNode = createNode(child)
-      if (child.type !== 'HightlightMesh') {
-        node.children.push(childNode)
-      }
-      buildGraph(child, childNode)
-    })
-  }
-
-  const root = createNode(scene)
-  buildGraph(scene, root)
-
-  return root
-}
-
-function countObjectsInScene(scene: Scene) {
-  let count = 0
+  function cb(event: DevtoolsEvent) {
+    state.connected = true
 
-  scene.traverse((object) => {
-    // Check if the object is a 3D object
-    if (object.isObject3D) {
-      count++
+    if (event.type === 'performance') {
+      handlePerformanceEvent(event.payload)
+    }
+    else if (event.type === 'context') {
+      handleContextEvent(event.payload)
     }
-  })
-
-  return count
-}
-
-function createHighlightMesh(object: Object3D): Mesh {
-  const highlightMaterial = new MeshBasicMaterial({
-    color: 0xa7e6d7, // Highlight color
-    transparent: true,
-    opacity: 0.2,
-    depthTest: false, // So the highlight is always visible
-    side: DoubleSide, // To e
-  })
-  // Clone the geometry of the object. You might need a more complex approach 
-  // if the object's geometry is not straightforward.
-  const highlightMesh = new HightlightMesh(object.geometry.clone(), highlightMaterial)
-
-  return highlightMesh
-}
-
-function highlightObject(object: TresObject) {
-  const [instance] = scene.value.getObjectsByProperty('uuid', object.uuid)
-  if (gl.internal.prevInstance && gl.internal.highlightMesh && gl.internal.highlightMesh.parent) {
-    gl.internal.prevInstance.remove(gl.internal.highlightMesh)
-  }
-  
-  if (instance.isMesh) {
-    const newHighlightMesh = createHighlightMesh(instance)
-    instance.add(newHighlightMesh)
 
-    gl.internal.highlightMesh = newHighlightMesh
-    gl.internal.prevInstance = instance
   }
-}
-
-function selectObject(object: TresObject) {
-  gl.internal.selectedObject = object
-}
-
-export function useDevtoolsHook(): DevtoolsHookReturn {
-  // Connect with Core
+  // Init devtools hook
   const tresGlobalHook = {
-    cb(context: TresContext) {
-      scene.value = context.scene.value
-      scene.objects = countObjectsInScene(context.scene.value)
-      scene.graph = getSceneGraph(context.scene.value as unknown as TresObject)
-
-      Object.assign(gl.renderer.info.render, context.renderer.value.info.render)
-      Object.assign(gl.renderer.info.memory, context.renderer.value.info.memory)
-      gl.renderer.info.programs = [...context.renderer.value.info.programs as WebGLProgram[]]
-      Object.assign(gl.fps, context.perf.fps)
-      gl.fps.accumulator = [...context.perf.fps.accumulator]
-      Object.assign(gl.memory, context.perf.memory)
-      gl.memory.accumulator = [...context.perf.memory.accumulator]
-
-      /* 
-      console.log('Devtools hook updated', context.renderer.value.info.render.triangles) */
-    },
+    cb,
   }
 
   window.parent.parent.__TRES__DEVTOOLS__ = tresGlobalHook
 
   return {
-    scene,
-    fps: gl.fps,
-    memory: gl.memory,
-    renderer: gl.renderer,
-    internal: gl.internal,
-    highlightObject,
-    selectObject,
+    state,
+    ...toRefs(state),
   }
-}
+} 

+ 10 - 3
plugins/vite-plugin-tres/client/pages/index.vue

@@ -1,13 +1,20 @@
 <!-- eslint-disable max-len -->
 <script setup lang="ts">
-import { ref } from 'vue'
+import { ref, watch, watchEffect } from 'vue'
+import { useDevtoolsHook } from '../composables/useDevtoolsHook'
+
+const { scene, connected, state } = useDevtoolsHook()
+
+watch(scene, () => {
+  console.log('scene changed', scene.value)
+})
 
 // Scene Graph
 </script>
 
 <template>
   <div class="panel-grids-center h-full">
-    <div class="max-w-300 w-full px20 ma">
+    <!-- <div class="max-w-300 w-full px20 ma">
       <div class="flex flex-wrap gap2">
         <RouterLink
           to="/scene-graph"
@@ -30,7 +37,7 @@ import { ref } from 'vue'
           <code>Performance</code>
         </RouterLink>
       </div>
-    </div>
+    </div> -->
     <!-- <div
       v-if="scene.objects > 0"
       class="flex flex-col gap-2"

+ 2 - 2
plugins/vite-plugin-tres/client/pages/scene-graph.vue

@@ -12,9 +12,9 @@ const { scene, internal } = useDevtoolsHook()
       class="h-full p4 overflow-y-scroll"
       min-size="20"
     >
-      <div v-if="scene.objects > 0">
+      <!-- <div v-if="scene.objects > 0">
         <SceneGraphItem :item="scene.graph" />
-      </div>
+      </div> -->
     </Pane>
     <Pane
       class="h-full"

+ 19 - 1
src/composables/useRenderer/index.ts

@@ -1,4 +1,4 @@
-import { Color, WebGLRenderer } from 'three'
+import { Color, MathUtils, WebGLRenderer } from 'three'
 import { shallowRef, watchEffect, onUnmounted, type MaybeRef, computed, watch, nextTick } from 'vue'
 import {
   toValue,
@@ -12,6 +12,7 @@ import type { Scene, ToneMapping,
   ShadowMapType,
   WebGLRendererParameters,
 } from 'three'
+import type { DevtoolsContextPayload, DevtoolsPerformancePayload } from '../../devtools'
 import { useLogger } from '../useLogger'
 import type { TresColor } from '../../types'
 import { useRenderLoop } from '../useRenderLoop'
@@ -177,10 +178,27 @@ export function useRenderer(
 
   const { resume, onLoop } = useRenderLoop()
 
+  function sendDevtoolEvent(
+    payload: DevtoolsContextPayload | DevtoolsPerformancePayload,
+    type: 'context' | 'performance' = 'context',
+  ) {
+    if (!window.__TRES__DEVTOOLS__) return
+
+    window.__TRES__DEVTOOLS__?.cb({
+      id: MathUtils.generateUUID(),
+      timestamp: Date.now(),
+      type,
+      payload,
+    })
+  }
+
   onLoop(() => {
     if (camera.value && !toValue(disableRender) && internal.frames.value > 0) {
       renderer.value.render(scene, camera.value)
       emit('render', renderer.value)
+      /* sendDevtoolEvent({
+        scene,
+      }) */
     }
 
     // Reset priority

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

@@ -230,7 +230,7 @@ export function useTresContextProvider({
     
     // Check if the accumulated time is greater than or equal to the interval
     if (accumulatedTime >= interval) {
-      window.__TRES__DEVTOOLS__.cb(toProvide)
+      /* window.__TRES__DEVTOOLS__.cb(toProvide) */
     
       // Reset the accumulated time
       accumulatedTime = 0

+ 2 - 1
src/devtools/index.ts

@@ -1 +1,2 @@
-export { registerTresDevtools } from './plugin'
+export { registerTresDevtools } from './plugin'
+export * from './types'

+ 2 - 3
src/devtools/plugin.ts

@@ -1,7 +1,7 @@
 import type {
-  App as DevtoolsApp,  } from '@vue/devtools-api'
+  App as DevtoolsApp } from '@vue/devtools-api'
 import {
-  addCustomTab
+  addCustomTab,
 } from '@vue/devtools-api'
 import { reactive } from 'vue'
 import type { Mesh, Object3D } from 'three'
@@ -9,7 +9,6 @@ import { createHighlightMesh, editSceneObject } from '../utils'
 import { bytesToKB, calculateMemoryUsage } from '../utils/perf'
 import type { TresContext } from '../composables'
 import type { TresObject } from './../types'
-import { toastMessage } from './utils'
 
 export interface Tags {
   label: string

+ 65 - 0
src/devtools/types.ts

@@ -0,0 +1,65 @@
+import type { Mesh, Object3D, Scene } from 'three'
+
+// Performance Devtools
+export interface FPSState {
+  value: number
+  accumulator: number[]
+  lastLoggedTime: number
+  logInterval: number
+}
+
+export interface MemoryState {
+  currentMem: number
+  averageMem: number
+  maxMemory: number
+  allocatedMem: number
+  accumulator: number[]
+  lastLoggedTime: number
+  logInterval: number
+}
+
+export interface RendererState {
+  info: {
+    render: {
+      frame: number
+      calls: number
+      triangles: number
+      points: number
+      lines: number
+    }
+    memory: {
+      geometries: number
+      textures: number
+    }
+    programs: WebGLProgram[]
+  }
+}
+
+export interface InternalState {
+  selectedObject: Object3D | null
+  prevInstance: Object3D | null
+  highlightMesh: Mesh | null
+}
+
+export interface DevtoolsPerformancePayload {
+  fps: FPSState
+  memory: MemoryState
+  renderer: RendererState
+  internal: InternalState
+}
+
+// Context Devtools
+export interface DevtoolsContextPayload {
+  scene: {
+    objects: number
+    graph: Record<string, unknown>
+    value: Scene | undefined
+  }
+}
+
+export interface DevtoolsEvent<T extends DevtoolsPerformancePayload | DevtoolsContextPayload> {
+  id: string
+  timestamp: number
+  type: 'performance' | 'context'
+  payload: T
+}

+ 0 - 27
src/devtools/utils.ts

@@ -1,27 +0,0 @@
-/**
- * Shows a toast or console.log
- *
- * @param message - message to log
- * @param type - different color of the tooltip
- */
-export function toastMessage(
-  message: string,
-  type?: 'normal' | 'error' | 'warn' | undefined,
-) {
-  const tresMessage = `▲ ■ ●${message}`
-
-  if (typeof __VUE_DEVTOOLS_TOAST__ === 'function') {
-    // No longer available :(
-    __VUE_DEVTOOLS_TOAST__(tresMessage, type)
-  }
-  else if (type === 'error') {
-    console.error(tresMessage)
-  }
-  else if (type === 'warn') {
-    console.warn(tresMessage)
-  }
-  else {
-    // eslint-disable-next-line no-console
-    console.log(tresMessage)
-  }
-}

+ 63 - 0
src/types/devtools.ts

@@ -0,0 +1,63 @@
+import type { Mesh, Object3D, Scene } from 'three'
+
+// Performance Devtools
+export interface FPSState {
+  value: number
+  accumulator: number[]
+  lastLoggedTime: number
+  logInterval: number
+}
+
+export interface MemoryState {
+  currentMem: number
+  averageMem: number
+  maxMemory: number
+  allocatedMem: number
+  accumulator: number[]
+  lastLoggedTime: number
+  logInterval: number
+}
+
+export interface RendererState {
+  info: {
+    render: {
+      frame: number
+      calls: number
+      triangles: number
+      points: number
+      lines: number
+    }
+    memory: {
+      geometries: number
+      textures: number
+    }
+    programs: WebGLProgram[]
+  }
+}
+
+export interface InternalState {
+  selectedObject: Object3D | null
+  prevInstance: Object3D | null
+  highlightMesh: Mesh | null
+}
+
+export interface DevtoolsPerformancePayload {
+  fps: FPSState
+  memory: MemoryState
+  renderer: RendererState
+  internal: InternalState
+}
+
+// Context Devtools
+export interface DevtoolsContextPayload {
+  scene: {
+    objects: number
+    graph: Record<string, unknown>
+    value: Scene | undefined
+  }
+}
+
+export interface DevtoolsEvent {
+  type: 'performance' | 'context'
+  payload: DevtoolsPerformancePayload | DevtoolsContextPayload
+}