From 7ad2e316499d4ed8bc4d0f0a3fb5e66b2aed18fe Mon Sep 17 00:00:00 2001 From: alvarosabu Date: Fri, 5 Jan 2024 07:17:06 +0100 Subject: [PATCH 01/15] feat: conditional rendering --- .../src/pages/rendering-modes/index.vue | 14 ++++ .../src/pages/rendering-modes/scene.vue | 22 ++++++ playground/src/router.ts | 5 ++ src/components/TresCanvas.vue | 6 +- src/composables/useRenderer/index.ts | 46 +++++++++--- .../useTresContextProvider/index.ts | 70 +++++++++++++++---- 6 files changed, 137 insertions(+), 26 deletions(-) create mode 100644 playground/src/pages/rendering-modes/index.vue create mode 100644 playground/src/pages/rendering-modes/scene.vue diff --git a/playground/src/pages/rendering-modes/index.vue b/playground/src/pages/rendering-modes/index.vue new file mode 100644 index 000000000..4916464b5 --- /dev/null +++ b/playground/src/pages/rendering-modes/index.vue @@ -0,0 +1,14 @@ + + + \ No newline at end of file diff --git a/playground/src/pages/rendering-modes/scene.vue b/playground/src/pages/rendering-modes/scene.vue new file mode 100644 index 000000000..52db82e1e --- /dev/null +++ b/playground/src/pages/rendering-modes/scene.vue @@ -0,0 +1,22 @@ + + + \ No newline at end of file diff --git a/playground/src/router.ts b/playground/src/router.ts index 7c4aad550..e14cfa4e8 100644 --- a/playground/src/router.ts +++ b/playground/src/router.ts @@ -76,6 +76,11 @@ const routes = [ name: 'Perf', component: () => import('./pages/perf/index.vue'), }, + { + path: '/rendering-modes', + name: 'Rendering Modes', + component: () => import('./pages/rendering-modes/index.vue'), + }, { path: '/empty', name: 'empty', diff --git a/src/components/TresCanvas.vue b/src/components/TresCanvas.vue index fa86b472e..7da51c636 100644 --- a/src/components/TresCanvas.vue +++ b/src/components/TresCanvas.vue @@ -45,6 +45,7 @@ export interface TresCanvasProps useLegacyLights?: boolean outputColorSpace?: ColorSpace toneMappingExposure?: number + renderMode?: 'always' | 'on-demand' // required by useTresContextProvider camera?: TresCamera @@ -65,6 +66,7 @@ const props = withDefaults(defineProps(), { preserveDrawingBuffer: undefined, logarithmicDepthBuffer: undefined, failIfMajorPerformanceCaveat: undefined, + renderMode: 'always', }) const { logWarning } = useLogger() @@ -129,8 +131,8 @@ onMounted(() => { context.value = useTresContextProvider({ scene: scene.value, canvas: existingCanvas, - windowSize: props.windowSize, - disableRender, + windowSize: props.windowSize ?? true, + disableRender: disableRender.value ?? false, rendererOptions: props, }) diff --git a/src/composables/useRenderer/index.ts b/src/composables/useRenderer/index.ts index a073ad326..b8062ea9d 100644 --- a/src/composables/useRenderer/index.ts +++ b/src/composables/useRenderer/index.ts @@ -96,6 +96,7 @@ export interface UseRendererOptions extends TransformToMaybeRefOrGetter windowSize?: MaybeRefOrGetter preset?: MaybeRefOrGetter + renderMode?: MaybeRefOrGetter<'always' | 'on-demand'> } /** @@ -110,7 +111,7 @@ export function useRenderer( canvas, options, disableRender, - contextParts: { sizes, camera }, + contextParts: { sizes, camera, internal }, }: { canvas: MaybeRef @@ -159,6 +160,30 @@ export function useRenderer( const { logError } = useLogger() + // TheLoop + + const { resume, onLoop } = useRenderLoop() + + onLoop(() => { + if (camera.value && !toValue(disableRender) && internal.frames.value > 0) + renderer.value.render(scene, camera.value) + + // Call subscribers' render callbacks + internal.subscribers.value.forEach(({ callback }) => callback()) + + // Reset priority + internal.priority.value = 0 + + internal.frames.value = Math.max(0, internal.frames.value - 1) + + if (toValue(options.renderMode) === 'always') { + internal.frames.value = 1 + } + + }) + + resume() + const getThreeRendererDefaults = () => { const plainRenderer = new WebGLRenderer() @@ -189,6 +214,15 @@ export function useRenderer( merge(renderer.value, rendererPresets[rendererPreset]) } + // Render mode + + const renderMode = toValue(options.renderMode) + + if (renderMode === 'always') { + // If the render mode is 'always', ensure there's always a frame pending + internal.frames.value = Math.max(1, internal.frames.value) + } + const getValue = (option: MaybeRefOrGetter, pathInThree: string): T | undefined => { const value = toValue(option) @@ -234,17 +268,7 @@ export function useRenderer( }) - const { pause, resume, onLoop } = useRenderLoop() - - onLoop(() => { - if (camera.value && !toValue(disableRender)) - renderer.value.render(scene, camera.value) - }) - - resume() - onUnmounted(() => { - pause() // TODO should the render loop pause itself if there is no more renderer? 🤔 What if there is another renderer which needs the loop? renderer.value.dispose() renderer.value.forceContextLoss() }) diff --git a/src/composables/useTresContextProvider/index.ts b/src/composables/useTresContextProvider/index.ts index 378814eb2..7af6abeba 100644 --- a/src/composables/useTresContextProvider/index.ts +++ b/src/composables/useTresContextProvider/index.ts @@ -9,6 +9,32 @@ import type { UseRendererOptions } from '../useRenderer' import { useRenderer } from '../useRenderer' import { extend } from '../../core/catalogue' +export interface InternalSubscriber { + callback: (timestamp: number) => void + priority: number +} + +export interface InternalState { + priority: Ref + frames: Ref + subscribers: Ref + maxFrames: number + subscribe: (callback: (timestamp: number) => void, priority: number) => () => void +} + +export interface PerformanceState { + maxFrames: number + fps: { + value: number + accumulator: number[] + } + memory: { + currentMem: number + allocatedMem: number + accumulator: number[] + } +} + export interface TresContext { scene: ShallowRef sizes: { height: Ref; width: Ref; aspectRatio: ComputedRef } @@ -18,18 +44,9 @@ export interface TresContext { controls: Ref<(EventDispatcher & { enabled: boolean }) | null> renderer: ShallowRef raycaster: ShallowRef - perf: { - maxFrames: number - fps: { - value: number - accumulator: number[] - } - memory: { - currentMem: number - allocatedMem: number - accumulator: number[] - } - } + perf: PerformanceState + internal: InternalState + invalidate: () => void registerCamera: (camera: Camera) => void setCameraActive: (cameraOrUuid: Camera | string) => void deregisterCamera: (camera: Camera) => void @@ -74,15 +91,40 @@ export function useTresContextProvider({ setCameraActive, } = useCamera({ sizes, scene }) + // Initialize internal state + const internal: InternalState = { + priority: ref(0), + frames: ref(0), + subscribers: ref([]), + maxFrames: 60, + subscribe: (callback, priority) => { + const subscription = { callback, priority } + internal.subscribers.value.push(subscription) + // Sort subscribers based on priority + internal.subscribers.value.sort((a, b) => b.priority - a.priority) + + // Return an unsubscribe function + return () => { + const index = internal.subscribers.value.indexOf(subscription) + if (index > -1) internal.subscribers.value.splice(index, 1) + } + }, + } + const { renderer } = useRenderer( { scene, canvas, options: rendererOptions, - contextParts: { sizes, camera }, + contextParts: { sizes, camera, internal }, disableRender, }) + function invalidate(frames = 1) { + // Increase the frame count, ensuring not to exceed a maximum if desired + internal.frames.value = Math.min(internal.maxFrames, internal.frames.value + frames) + } + const toProvide: TresContext = { sizes, scene: localScene, @@ -103,7 +145,9 @@ export function useTresContextProvider({ accumulator: [], }, }, + internal, extend, + invalidate, registerCamera, setCameraActive, deregisterCamera, From 5d72244ff92c18a02db7b1425232b9223a2c4196 Mon Sep 17 00:00:00 2001 From: alvarosabu Date: Fri, 5 Jan 2024 10:39:22 +0100 Subject: [PATCH 02/15] chore: remove subscribe system --- src/composables/useRenderer/index.ts | 3 --- .../useTresContextProvider/index.ts | 20 ------------------- 2 files changed, 23 deletions(-) diff --git a/src/composables/useRenderer/index.ts b/src/composables/useRenderer/index.ts index b8062ea9d..9370d7e91 100644 --- a/src/composables/useRenderer/index.ts +++ b/src/composables/useRenderer/index.ts @@ -168,9 +168,6 @@ export function useRenderer( if (camera.value && !toValue(disableRender) && internal.frames.value > 0) renderer.value.render(scene, camera.value) - // Call subscribers' render callbacks - internal.subscribers.value.forEach(({ callback }) => callback()) - // Reset priority internal.priority.value = 0 diff --git a/src/composables/useTresContextProvider/index.ts b/src/composables/useTresContextProvider/index.ts index 7af6abeba..f293ef801 100644 --- a/src/composables/useTresContextProvider/index.ts +++ b/src/composables/useTresContextProvider/index.ts @@ -9,17 +9,10 @@ import type { UseRendererOptions } from '../useRenderer' import { useRenderer } from '../useRenderer' import { extend } from '../../core/catalogue' -export interface InternalSubscriber { - callback: (timestamp: number) => void - priority: number -} - export interface InternalState { priority: Ref frames: Ref - subscribers: Ref maxFrames: number - subscribe: (callback: (timestamp: number) => void, priority: number) => () => void } export interface PerformanceState { @@ -95,20 +88,7 @@ export function useTresContextProvider({ const internal: InternalState = { priority: ref(0), frames: ref(0), - subscribers: ref([]), maxFrames: 60, - subscribe: (callback, priority) => { - const subscription = { callback, priority } - internal.subscribers.value.push(subscription) - // Sort subscribers based on priority - internal.subscribers.value.sort((a, b) => b.priority - a.priority) - - // Return an unsubscribe function - return () => { - const index = internal.subscribers.value.indexOf(subscription) - if (index > -1) internal.subscribers.value.splice(index, 1) - } - }, } const { renderer } = useRenderer( From b2cfa4a92b4af49c988a849c6f2b4a92e9a93328 Mon Sep 17 00:00:00 2001 From: alvarosabu Date: Fri, 5 Jan 2024 11:44:00 +0100 Subject: [PATCH 03/15] feat: on-demand automatic invalidation with prop changes --- .../src/pages/rendering-modes/scene.vue | 8 ++++++- src/components/TresCanvas.vue | 2 +- src/composables/useRenderer/index.ts | 2 +- .../useTresContextProvider/index.ts | 7 +++++- src/core/nodeOps.ts | 22 ++++++++++++++++++- src/devtools/plugin.ts | 5 ++++- 6 files changed, 40 insertions(+), 6 deletions(-) diff --git a/playground/src/pages/rendering-modes/scene.vue b/playground/src/pages/rendering-modes/scene.vue index 52db82e1e..91bded00c 100644 --- a/playground/src/pages/rendering-modes/scene.vue +++ b/playground/src/pages/rendering-modes/scene.vue @@ -8,13 +8,19 @@ function onControlChange() { invalidate() } +const positionX = ref(0) + +setTimeout(() => { + positionX.value = 1 +}, 3000) + invalidate()