-
-
Notifications
You must be signed in to change notification settings - Fork 108
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: 140 on demand rendering (#497)
* feat: conditional rendering * chore: remove subscribe system * feat: on-demand automatic invalidation with prop changes * feat: invalidate once first when is `renderMode !== 'always'` * docs: performance page, on-demand rendering * chore: fix windowsize issue * chore(lint): fix maximum line length issues * feat: invalidate on-demand on window resize * feat: add advance method for manual mode * feat: fix manual first render with advance * docs: performance manual mode * docs: add badge with version * chore: correct typos and PR suggestions * chore: tell dont ask fix * feat: render state instead of internal
- Loading branch information
1 parent
83f0e06
commit f688c64
Showing
20 changed files
with
594 additions
and
54 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
<script setup lang="ts"> | ||
import { useTresContext } from '@tresjs/core' | ||
import { useGLTF } from '@tresjs/cientos' | ||
const { nodes } = await useGLTF('https://raw.githubusercontent.com/Tresjs/assets/main/models/gltf/blender-cube.glb', | ||
{ draco: true }) | ||
const model = nodes.Cube | ||
model.position.set(0, 1, 0) | ||
const state = useTresContext() | ||
state.invalidate() | ||
</script> | ||
|
||
<template> | ||
<primitive :object="model" /> | ||
</template> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
<script lang="ts" setup> | ||
import { ref } from 'vue' | ||
import { useRafFn } from '@vueuse/core' | ||
import { useState } from '../composables/state' | ||
const width = 160 | ||
const height = 40 | ||
const strokeWidth = 2 | ||
const updateInterval = 100 // Update interval in milliseconds | ||
const topOffset = 0 // Offset from the top | ||
const points = ref('') | ||
const frameTimes = ref([]) | ||
const maxFrames = ref(width / strokeWidth) | ||
let lastUpdateTime = performance.now() | ||
const { renderingTimes } = useState() | ||
useRafFn(({ timestamp }) => { | ||
if (timestamp - lastUpdateTime >= updateInterval) { | ||
lastUpdateTime = timestamp | ||
frameTimes.value.push(renderingTimes?.value) | ||
renderingTimes.value = 0 | ||
if (frameTimes.value.length > maxFrames.value) { | ||
frameTimes.value.shift() | ||
} | ||
points.value = frameTimes.value | ||
.map( | ||
(value, index) => | ||
`${index * strokeWidth},${ | ||
height + topOffset - strokeWidth / 2 - (value * (height + topOffset - strokeWidth)) / 2 | ||
}`, | ||
) | ||
.join(' ') | ||
} | ||
}) | ||
</script> | ||
|
||
<template> | ||
<div | ||
class="absolute | ||
right-2 | ||
top-2 | ||
flex | ||
px-4 | ||
py-1 | ||
justify-between | ||
gap-4 | ||
items-center | ||
mb-2 | ||
z-10 | ||
bg-white | ||
dark:bg-dark | ||
shadow-xl | ||
rounded | ||
border-4 | ||
border-solid | ||
bg-primary | ||
border-primary | ||
pointer-events-none | ||
overflow-hidden" | ||
> | ||
<label class="text-secondary text-xs w-1/3">Rendering Activity</label> | ||
|
||
<div | ||
class=" | ||
bg-gray-100 | ||
dark:bg-gray-600 | ||
relative | ||
w-2/3 | ||
p-1 | ||
rounded | ||
text-right | ||
text-xs | ||
focus:border-gray-200 | ||
outline-none | ||
border-none | ||
font-sans | ||
" | ||
> | ||
<svg | ||
:width="width" | ||
:height="height" | ||
xmlns="http://www.w3.org/2000/svg" | ||
fill="none" | ||
> | ||
<polyline | ||
:points="points" | ||
stroke="lightgray" | ||
:stroke-width="strokeWidth" | ||
stroke-linecap="round" | ||
stroke-linejoin="round" | ||
/> | ||
</svg> | ||
</div> | ||
</div> | ||
</template> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
<script setup lang="ts"> | ||
import { TresCanvas } from '@tresjs/core' | ||
import { BasicShadowMap, SRGBColorSpace, NoToneMapping } from 'three' | ||
import { OrbitControls } from '@tresjs/cientos' | ||
import { useState } from '../composables/state' | ||
import BlenderCube from './BlenderCube.vue' | ||
import GraphPane from './GraphPane.vue' | ||
import RenderingLogger from './RenderingLogger.vue' | ||
const { renderingTimes } = useState() | ||
function onRender() { | ||
renderingTimes.value = 1 | ||
} | ||
</script> | ||
|
||
<template> | ||
<GraphPane /> | ||
<TresCanvas | ||
render-mode="on-demand" | ||
clear-color="#82DBC5" | ||
@render="onRender" | ||
> | ||
<TresPerspectiveCamera | ||
:position="[5, 5, 5]" | ||
:look-at="[0, 0, 0]" | ||
/> | ||
<Suspense> | ||
<BlenderCube /> | ||
</Suspense> | ||
<TresGridHelper /> | ||
<RenderingLogger /> | ||
<TresAmbientLight :intensity="1" /> | ||
<TresDirectionalLight | ||
:position="[0, 8, 4]" | ||
:intensity="0.7" | ||
/> | ||
</TresCanvas> | ||
</template> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
<script setup lang="ts"> | ||
import { useRenderLoop, useTresContext } from '@tresjs/core' | ||
import { OrbitControls } from '@tresjs/cientos' | ||
import { onMounted } from 'vue' | ||
import { useState } from '../composables/state' | ||
const { renderingTimes } = useState() | ||
const state = useTresContext() | ||
function manualInvalidate() { | ||
state.invalidate() | ||
} | ||
onMounted(() => { | ||
manualInvalidate() | ||
}) | ||
</script> | ||
|
||
<template> | ||
<OrbitControls | ||
@change="manualInvalidate" | ||
/> | ||
</template> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import { reactive, toRefs } from 'vue' | ||
|
||
const state = reactive({ | ||
renderingTimes: 0, | ||
}) | ||
export function useState() { | ||
return { | ||
...toRefs(state), | ||
|
||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
# Scaling performance 🚀 | ||
|
||
> Quick guide with tips to improve performance of your Tres.js application. | ||
We are running WebGL on the browser, which can be quite expensive and it will depend on how powerful the user's device is. To make 3D accessible to everyone, we need to make sure our applications are optimized to run also on low-end devices. This guide will provide some tips to improve the performance of your Tres.js application. | ||
|
||
## On-demand rendering <Badge type="tip" text="^4.0.0" /> | ||
|
||
By default, Tres.js will render your scene on every frame. This is great for most applications, but if you are building a game or a complex application, you might want to control when the scene is rendered. | ||
|
||
Otherwise it might drain your device battery 🔋 🔜 🪫 and make your computer sound like an airplane 🛫. | ||
|
||
Ideally, you only want to **render the scene when necessary**, for example when the user interacts with the scene and the camera moves, or when objects in the scene are animated. | ||
|
||
You can do that by setting the `renderMode` prop to `on-demand` or `manual`: | ||
|
||
|
||
### Mode `on-demand` | ||
|
||
<ClientOnly> | ||
<div style="position: relative; aspect-ratio: 16/9; height: auto; margin: 2rem 0; border-radius: 8px; overflow:hidden;"> | ||
<onDemandRendering /> | ||
</div> | ||
</ClientOnly> | ||
|
||
|
||
```vue | ||
<TresCanvas render-mode="on-demand"> | ||
<!-- Your scene goes here --> | ||
</TresCanvas> | ||
``` | ||
|
||
#### Automatic Invalidation | ||
|
||
When using `render-mode="on-demand"`, Tres.js will automatically invalidate the current frame by observing component props and lifecycle hooks like `onMounted` and `onUnmounted`. It will also invalidate the frame when resizing the window or changing any prop from the `<TresCanvas>` component like `clearColor` or `antialias`. | ||
|
||
The code below updates TresMesh's position-x prop every second, triggering a new render. | ||
|
||
```vue | ||
<script setup> | ||
import { ref } from 'vue' | ||
const positionX = ref(0) | ||
setTimeout(() => { | ||
positionX.value = 1 | ||
}, 1000) | ||
</script> | ||
<template> | ||
<TresCanvas render-mode="on-demand"> | ||
<TresMesh :position-x="positionX"> | ||
<TresBoxGeometry /> | ||
<TresMeshBasicMaterial color="teal" /> | ||
</TresMesh> | ||
</TresCanvas> | ||
</template> | ||
``` | ||
|
||
#### Manual Invalidation | ||
|
||
Since it is not really possible to observe all the possible changes in your application, you can also manually invalidate the frame by calling the `invalidate()` method from the [`useTresContext` composable](../api/composables.md#usetrescontext): | ||
|
||
|
||
::: code-group | ||
|
||
```vue [App.vue] | ||
<script setup> | ||
import { TresCanvas } from '@tresjs/core' | ||
import Scene from './Scene.vue' | ||
</script> | ||
<template> | ||
<TresCanvas | ||
render-mode="manual" | ||
> | ||
<Scene /> | ||
</TresCanvas> | ||
</template> | ||
``` | ||
|
||
```vue [Scene.vue] | ||
<script setup> | ||
import { useTres } from '@tresjs/core' | ||
const boxRef = ref() | ||
const { invalidate } = useTres() | ||
watch(boxRef.value, () => { | ||
boxRef.value.position.x = 1 | ||
invalidate() | ||
}) | ||
</script> | ||
<template> | ||
<TresMesh ref="boxRef"> | ||
<TresBoxGeometry /> | ||
<TresMeshBasicMaterial color="teal" /> | ||
</TresMesh> | ||
</template> | ||
``` | ||
|
||
::: | ||
|
||
### Mode `always` | ||
|
||
In this rendering mode, Tres will continously render the scene on every frame. This is the default mode and the easiest to use, but it's also the most resource expensive one. | ||
|
||
|
||
### Mode `manual` | ||
|
||
If you want to have full control of when the scene is rendered, you can set the `render-mode` prop to `manual`: | ||
|
||
```vue | ||
<TresCanvas render-mode="manual"> | ||
<!-- Your scene goes here --> | ||
</TresCanvas> | ||
``` | ||
|
||
In this mode, Tres will not render the scene automatically. You will need to call the `advance()` method from the [`useTresContext` composable](../api/composables.md#usetrescontext) to render the scene: | ||
|
||
```vue | ||
<script setup> | ||
import { useTres } from '@tresjs/core' | ||
const { advance } = useTres() | ||
advance() | ||
</script> | ||
``` | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.