From e079154feb20b8b628e08b6d8fd873c955b0282f Mon Sep 17 00:00:00 2001 From: nearnshaw Date: Wed, 17 Sep 2025 16:18:46 -0300 Subject: [PATCH 1/5] First drat --- .../sdk7/optimizing/art-optimization.md | 102 +++++++++++++ .../optimizing/performance-optimization.md | 137 +++++++++--------- static/images/FPS.png | Bin 0 -> 230289 bytes static/images/debug-panel.png | Bin 0 -> 8044 bytes 4 files changed, 174 insertions(+), 65 deletions(-) create mode 100644 content/creator/sdk7/optimizing/art-optimization.md create mode 100644 static/images/FPS.png create mode 100644 static/images/debug-panel.png diff --git a/content/creator/sdk7/optimizing/art-optimization.md b/content/creator/sdk7/optimizing/art-optimization.md new file mode 100644 index 000000000..12e424113 --- /dev/null +++ b/content/creator/sdk7/optimizing/art-optimization.md @@ -0,0 +1,102 @@ +--- +date: 2021-01-11 +title: Art optimization +description: Optimize your scene's art to load fast and run smoothly for all players. +categories: + - development-guide +type: Document +url: /creator/development-guide/sdk7/art-optimization/ +weight: 1 +--- + +There are several aspects you can optimize in your scenes' art to ensure the best possible experience for players who visit them. This document covers some best practices when building 3D models, textures, and other art assets that can make a big difference in how fast your scene loads and how smoothly it runs for players. + +See [Code optimization]({{< ref "/content/creator/sdk7/optimizing/performance-optimization.md" >}}) for more information on how to optimize your scene's code. + +Keep in mind that many players may be visiting Decentraland using hardware that is not built for gaming, and via the browser, which limits how much of the hardware's processing power is available to use. The experience of visiting your scene should be smooth for everyone. + +The Decentraland explorer enforces many optimizations at engine level. These optimizations make a big difference, but the challenge of rendering multiple user-generated experiences simultaneously is a big one. We need your help to make things run smoothly. + +## Monitor Performance + +The best metric to know how well a scene is performing is the FPS (Frames Per Second). When running a scene in preview, open the debug panel to see it. You should aim to always have 30 FPS or more. + +To see the current FPS (Frames Per Second) of the explorer + +1) Open the debug panel, on the top-right corner of the screen. + + +2) Check Average FPS in the **PERFORMANCE** tab. + + +Check this value periodically, as it may vary over time depending on what you're doing in your scene. + +Keep in mind that the performance you experience in preview may differ from that in production: + +- Surrounding neighboring scenes might have a negative impact +- If multiple players are in the scene, rendering all their avatars can have a negative impact on the FPS +- The compression of the scenes' 3D models into asset bundles can have a positive impact +- Some players visiting your scene may be running on less powerful hardware + +It's always a good practice to try deploying your scene first to the [test environment]({{< ref "/content/creator/sdk7/publishing/publishing.md#the-test-server">}}) to do some more thorough testing. + +Always ask players for feedback. Never take for granted that how you experience the scene is the same for everyone else. + + +## Keep an eye on metrics + +When working with the [Creator Hub]({{< ref "/content/creator/scene-editor/editor-installation.md" >}}), you can see stats about the resources used by 3D models in your scene, and if they pass any of the [scene limitations]({{< ref "/content/creator/sdk7/optimizing/scene-limitations.md" >}}). + + + +You can expand this menu to view details. + + + +Here are some tips for improving on these metrics: + +## Transparent materials + +Transparent materials are always more expensive in terms of performance than opaque materials. Avoid using blended transparencies. Blended transparencies have to bypass quite a few of the rendering optimizations. If possible, favor opaque or alpha tested geometry. See [Alpha materials]({{< ref "/content/creator/3d-modeling/materials.md#Alpha" >}}) and [Transparent Maps]({{< ref "/content/creator/3d-modeling/textures.md#Transparent-Maps" >}}) for more details. + +## Merging meshes + +Merging meshes is a great way to reduce the number of draw calls and improve performance. Instead of executing one draw call per mesh, you can merge them into a single draw call. + +This however is not always ideal in cases where the meshes might not all be visible at the same time. If the meshes are separate, the engine is able to cull the ones that are not visible. See [Meshes on large scenes]({{< ref "/content/creator/3d-modeling/meshes.md#Meshes-on-large-scenes" >}}) for more details. + +## Face Culling + +Face culling is a great way to improve performance. It allows the engine to skip rendering the faces that are not visible. Only untoggle **backface culling** in your models if you need a model to be renderer in both sides (for example, a group of leafs of a tree made by 3D planes). + +## Texture atlases + +Texture atlases are a great way to reduce the number of textures in your scene. It allows you to share the same texture across multiple models. See [Shared textures betewen GlTF models]({{< ref "/content/creator/3d-modeling/textures.md#Shared-textures-betewen-GlTFs-models" >}}) for more details. + +You could even use a single texture as an atlas map, shared across all models in the scene. It's better to have 1 large shared texture of 1024x1024 pixels instead of several small ones. + +{{< hint warning >}} +**📔 Note**: Avoid using the same image file for both the albedo texture and the normal map or the emissive map of a material. Use separate files, even if identical. Assigning a same image file to different types of texture properties may introduce unwanted visual artifacts when compressed to asset bundles. +{{< /hint >}} + + +## Other Best practices + + +- _.glb_ is a compressed format, it will always weigh less than a _.gltf_. On the other hand, with _.gltf_ it's easy to share texture images by exporting textures as a separate file. You can have the best of both worlds by using the [following pipeline](https://github.com/AnalyticalGraphicsInc/gltf-pipeline), that allows you to have _.glb_ models with external texture files. + +- Avoid skinned meshes. They can drag down the performance significantly. + +{{< hint info >}} +**💡 Tip**: Read more on 3D model best practices in the [3D Modeling Section](/creator/3d-modeling/3d-models +{{< /hint >}} + +### Asset Bundle conversion + +After your scene is deployed, the Decentraland content servers run a process to compress every _.gltf_ and _.glb_ model in your scene to asset bundle format. This format is _significantly_ lighter, making scenes a lot faster to load and smoother to run on the browser. This proces is carried out automatically, so you don't need to do anything to trigger it, the process may typically take 15 minutes or more. + +{{< hint warning >}} +**📔 Note**: If you make _any_ change to a 3D model file, even if just a name change, it will be considered a new file, and must be converted to asset bundle format again. + +When planning an event in Decentraland, make sure you deploy your scene several hours in advance, so that the models are all converted to asset bundles by then. +{{< /hint >}} \ No newline at end of file diff --git a/content/creator/sdk7/optimizing/performance-optimization.md b/content/creator/sdk7/optimizing/performance-optimization.md index 860998608..6aa8cc621 100644 --- a/content/creator/sdk7/optimizing/performance-optimization.md +++ b/content/creator/sdk7/optimizing/performance-optimization.md @@ -1,7 +1,7 @@ --- date: 2021-01-11 -title: Performance optimization -description: Optimize your scene to load fast and run smoothly for all players. +title: Code optimization +description: Optimize your scene's code to load fast and run smoothly for all players. categories: - development-guide type: Document @@ -12,15 +12,39 @@ url: /creator/development-guide/sdk7/performance-optimization/ weight: 1 --- -There are several aspects you can optimize in your scenes to ensure the best possible experience for players who visit them. This document covers some best practices that can make a big difference in how fast your scene loads and how smoothly it runs for players that are on it or on neighboring scenes. +There are several aspects you can optimize in your scene's code to ensure the best possible experience for players who visit them. This document covers some best practices that can make a big difference in how fast your scene loads and how smoothly it runs for players that are on it or on neighboring scenes. + +See [Art optimization]({{< ref "/content/creator/sdk7/optimizing/art-optimization.md" >}}) for more information on how to optimize your scene's art. Keep in mind that many players may be visiting Decentraland using hardware that is not built for gaming, and via the browser, which limits how much of the hardware's processing power is available to use. The experience of visiting your scene should be smooth for everyone. -The Decentraland explorer enforces many optimizations at engine level. These optimizations make a big difference, but the challenge of rendering multiple user-generated experiences simultaneously in a browser is a big one. We need your help to make things run smoothly. +## Monitor Performance + +The best metric to know how well a scene is performing is the FPS (Frames Per Second). When running a scene in preview, open the debug panel to see it. You should aim to always have 30 FPS or more. + +To see the current FPS (Frames Per Second) of the explorer -## Timing +1) Open the debug panel, on the top-right corner of the screen. + + +2) Check Average FPS in the **PERFORMANCE** tab. + + +Check this value periodically, as it may vary over time depending on what you're doing in your scene. + +Keep in mind that the performance you experience in preview may differ from that in production: -### Video playing +- Surrounding neighboring scenes might have a negative impact +- If multiple players are in the scene, rendering all their avatars can have a negative impact on the FPS +- The compression of the scenes' 3D models into asset bundles can have a positive impact +- Some players visiting your scene may be running on less powerful hardware + +It's always a good practice to try deploying your scene first to the [test environment]({{< ref "/content/creator/sdk7/publishing/publishing.md#the-test-server">}}) to do some more thorough testing. + +Always ask players for feedback. Never take for granted that how you experience the scene is the same for everyone else. + + +## Video playing Playing videos is one of the most expensive things for the engine to handle. If your scene includes videos, make sure that only _ONE_ VideoTexture is in use at a time. You can have dozens of planes sharing the same VideoTexture without significant impact on performance, but as soon as you add a second VideoTexture, its effects on framerate become very noticeable. @@ -34,7 +58,7 @@ You should also avoid having videos playing in regions where they can't be seen. **💡 Tip**: When players are standing outside your scene, VideoTextures are not updated on every frame. This helps reduce the impact for surrounding scenes. It's nevertheless ideal only turn on the playing of any videos when players [step inside your scene]({{< ref "/content/creator/sdk7/interactivity/event-listeners.md#player-enters-or-leaves-scene">}}) . {{< /hint >}} -### Lazy loading +## Lazy loading If your scene is large, or has indoor areas that are not always visible, you can choose to not load the entire set of entities from the very start. Instead, load the content by region as the player visits different parts of the scene. This can significantly reduce the load time of the scene, and also the amount of textures and 3D content that the engine needs to handle on every frame. @@ -56,63 +80,71 @@ You can also toggle animations on or off for entities that are far or occluded. **💡 Tip**: When an entity is far away and small enough, it's culled by the engine. This culling helps at a drawcall level, removing entities from the engine is always better. This culling also doesn't take occlusion by other entities into account, so entities that are not so small but hidden by a wall are still rendered. {{< /hint >}} -### Async blocks - -Blocks of [async code]({{< ref "/content/creator/sdk7/programming-patterns/async-functions.md" >}}) are processed in a separate thread from the rest of the scene, to prevent blocking the progress of everything else. +## Mutable vs non-mutable data -Any processes that rely on responses from asynchronous services, such as `getPlayerData()` or `getRealm()` should always run in async blocks, as they otherwise block the rest of the scene's loading while waiting for a response. The same applies to any calls to third party servers. - -Note that the scene will be considered fully loaded when everything that isn't async is done. Async processes might still be running when the player enters the scene. Avoid situations where an async process results in the loading of an entity that could potentially get the player stuck inside of its geometry. +When dealing with data, you can choose to deal with mutable or with immutable (read-only) versions of a component. The `.get()` function in a component returns an immutable version of the component. You can only read its values, but can't change any of the properties on it. See [mutable data]({{< ref "/content/creator/sdk7/programming-patterns/mutable-data.md" >}}) for more details. -### Rely on Events +The `.getMutable()` function returns representation of the component that allows you to change its values. Use mutable versions only when you plan to make changes to a component. Dealing with immutable versions of components results in a huge gain in performance. -Try to make the scene's logic rely on listening to [events]({{< ref "/content/creator/sdk7/interactivity/event-listeners.md" >}}) as much as possible, instead of running checks every frame. +```ts +const immutableTransform = Transform.get(myEntity) -The `update()` function in a [system]({{< ref "/content/creator/sdk7/architecture/systems.md">}}) runs on every frame, 30 times per second (ideally). Avoid doing recurring checks if you can instead subscribe to an event. +// the following does NOT work: +// immutableTransform.position.y = 2 -For example, instead of constantly checking the player's wearables, you can subscribe to the `onProfileChanged` event, and check the player's wearables only when they've changed. +const mutableTransform = Transform.getMutable(myEntity) -If you must use a system, avoid doing checks or adjustments on every single frame. You can include a timer as part of the update function and only run the check once per every full second, or whatever period makes sense. +// the following DOES change the entity's position +mutableTransform.position.y = 2 +``` -## Optimize 3D models +It's especially useful to deal with immutable versions of data when iterating over a lot of entities, and especially if the data is not changing. For example, if you're iterating over all the entities in the scene and checking their position, you can use the immutable version of the Transform component to read the position, and only fetch the mutable version when you need to change the position. -There are several ways in which your 3D models can be optimized to be lighter. -When working with the [Creator Hub]({{< ref "/content/creator/scene-editor/editor-installation.md" >}}), you can see stats about the resources used by 3D models in your scene, and if they pass any of the [scene limitations]({{< ref "/content/creator/sdk7/optimizing/scene-limitations.md" >}}). +## Async blocks - +Blocks of [async code]({{< ref "/content/creator/sdk7/programming-patterns/async-functions.md" >}}) are processed in a separate thread from the rest of the scene, to prevent blocking the progress of everything else. -You can expand this menu to view details. +Any processes that rely on responses from asynchronous services, such as `getPlayerData()` or `getRealm()` should always run in async blocks, as they otherwise block the rest of the scene's loading while waiting for a response. The same applies to any calls to third party servers. - +Note that the scene will be considered fully loaded when everything that isn't async is done. Async processes might still be running when the player enters the scene. Avoid situations where an async process results in the loading of an entity that could potentially get the player stuck inside of its geometry. -Here are some tips for improving on these metrics: +## Rely on Events -- When possible, share textures across 3D models. A good practice is to use a single texture as an atlas map, shared across all models in the scene. It's better to have 1 large shared texture of 1024x1024 pixels instead of several small ones. +Try to make the scene's logic rely on listening to [events]({{< ref "/content/creator/sdk7/interactivity/event-listeners.md" >}}) as much as possible, instead of running checks every frame. - > Note: Avoid using the same image file for both the albedo texture and the normal map or the emissive map of a material. Use separate files, even if identical. Assigning a same image file to different types of texture properties may introduce unwanted visual artifacts when compressed to asset bundles. +The `update()` function in a [system]({{< ref "/content/creator/sdk7/architecture/systems.md">}}) runs on every frame, 30 times per second (ideally). Avoid doing recurring checks if you can instead subscribe to an event. -- _.glb_ is a compressed format, it will always weigh less than a _.gltf_. On the other hand, with _.gltf_ it's easy to share texture images by exporting textures as a separate file. You can have the best of both worlds by using the [following pipeline](https://github.com/AnalyticalGraphicsInc/gltf-pipeline), that allows you to have _.glb_ models with external texture files. +For example, instead of constantly checking the player's wearables, you can subscribe to the `onProfileChanged` event, and check the player's wearables only when they've changed. -- Avoid using blended transparencies. Blended transparencies have to bypass quite a few of the rendering optimizations. If possible, favor opaque or alpha tested geometry. +Likewise, you can use the `onChange()` method to subscribe to changes in any component, and only run the check when the component's value changes. See [Subscribe to changes]({{< ref "/content/creator/sdk7/architecture/subscribe-to-changes.md" >}}) for more information. -- Avoid skinned meshes. They can drag down the performance significantly. +```ts +MyComponent.onChange(myEntity, (componentData) => { + console.log(componentData.value1) + }) +``` -{{< hint info >}} -**💡 Tip**: Read more on 3D model best practices in the [3D Modeling Section](/creator/3d-modeling/3d-models +{{< hint warning >}} +**📔 Note**: Do not use `onChange()` inside a System, as that would subscribe a new copy of the function on every frame of the game loop, and could potentially lead to crashes. {{< /hint >}} -### Asset Bundle conversion +You can also combine this approach with [Querying components]({{< ref "/content/creator/sdk7/architecture/querying-components.md" >}}), to bulk-subscribe every entity in the scene that has a certain component to their own function. -About once a day, the Decentraland content servers run a process to compress every _.gltf_ and _.glb_ model in every newly deployed scene to asset bundle format. This format is _significantly_ lighter, making scenes a lot faster to load and smoother to run on the browser. +```ts +export function main() { + for (const [entity] of engine.getEntitiesWith(MyComponent)) { + MyComponent.onChange(entity, (componentData) => { + if (!componentData) return + console.log(componentData.value1) + console.log(componentData.value2) + }) + } +} +``` -{{< hint info >}} -**💡 Tip**: When planning an event in Decentraland, make sure you deploy your scene a day in advance, so that the models are all converted to asset bundles by then. If you don't want to spoil the surprize before the event, you can deploy a version of your scene that includes all the final 3D models in the project folder, but where these are not visible or where their size is set to 0. -{{< /hint >}} +If you must use a system, avoid doing checks or adjustments on every single frame. You can include a timer as part of the update function and only run the check once per every full second, or whatever period makes sense. -{{< hint warning >}} -**📔 Note**: If you make _any_ change to a 3D model file, even if just a name change, it will be considered a new file, and must be converted to asset bundle format again. -{{< /hint >}} ## Connectivity @@ -134,31 +166,6 @@ Avoid making adjustments to the UI on every frame, those are especially costly a Avoid having many hidden UI elements, these also have an effect on performance even if not being rendered. When possible, try to create UI components on demand. -## Monitor Performance - -The best metric to know how well a scene is performing is the FPS (Frames Per Second). In preview, you can see the current scene FPS in the debug panel. You should aim to always have 30 FPS or more. - -In the deployed scene, you can toggle the panel that shows these metrics by writing `/showfps` into the chat window. - -One of the main bottlenecks in a scene’s performance is usually the sending of messages between the scene’s code and the engine. - -When you run a scene in preview, note that on the top-right corner it says “Y = Toggle Panel”. Hit Y on the keyboard to open a panel with some useful information that gets updated in real time. - -As you interact with things that involve messages between the SDK and the engine, you’ll notice the ‘Processed Messages’ number grows. You should closely watch the ‘Pending on Queue’ number, it should always be 0 or close to 0. This tells you how many of these messages didn't get to be processed, and got pushed to a queue. If the ‘Pending on Queue’ count starts to grow, then you’ve entered the danger zone and should think about doing more optimizations to your scene. - -{{< hint warning >}} -**📔 Note**: Don’t keep the panel open while you’re not using it, since it has a negative impact on performance. -{{< /hint >}} - -Keep in mind that the performance you experience in preview may differ from that in production: - -- Surrounding neighboring scenes might have a negative impact -- The compression of the scenes' 3D models into asset bundles can have a positive impact -- Some players visiting your scene may be running on less powerful hardware - -It's always a good practice to try deploying your scene first to the [test environment]({{< ref "/content/creator/sdk7/publishing/publishing.md#the-test-server">}}) to do some more thorough testing. - -Always ask players for feedback. Never take for granted that how you experience the scene is the same for everyone else.