Culling: Difference between revisions

From Resonite Wiki
creat
 
typo & make headlessuserculling feel less like an advertisement
 
Line 3: Line 3:
== Why cull? ==
== Why cull? ==


As worlds get large, assets become plentifyl, and more users join a world, there will be things that are immediately relevant to a user's experience and things that are irrelevant to a user's experience. In general, things that are far away from a user, obscured by walls, separated via rooms, or out of a user's view range are not relevant to a user's immediate experience. If calculations or rendering was done on these irrelevant objects, then excess work is being done for little to no gain, and performance can overall suffer due to the extra work.
As worlds get large, assets become plentiful, and more users join a world, there will be things that are immediately relevant to a user's experience and things that are irrelevant to a user's experience. In general, things that are far away from a user, obscured by walls, separated via rooms, or out of a user's view range are not relevant to a user's immediate experience. If calculations or rendering was done on these irrelevant objects, then excess work is being done for little to no gain, and performance can overall suffer due to the extra work.


By culling things that have no relevance to the user's current experience, the user's computer does not need to perform extra work on those things, which can slightly or significantly improve performance depending on the complexity of a world.
By culling things that have no relevance to the user's current experience, the user's computer does not need to perform extra work on those things, which can slightly or significantly improve performance depending on the complexity of a world.
Line 19: Line 19:
=== Skinned meshes ===
=== Skinned meshes ===


Skinned meshes have a few extra considerations due to the bones being able to deform the visual of the mesh. The <code>BoundsComputeMethod</code> field on a [[Component:SkinnedMeshRednerer|SkinnedMeshRednerer]] provides a few different options for how the bounds of a skinned mesh should be calculated, and thus when it should be culled:
Skinned meshes have a few extra considerations due to the bones being able to deform the visual of the mesh. The <code>BoundsComputeMethod</code> field on a [[Component:SkinnedMeshRenderer|SkinnedMeshRenderer]] provides a few different options for how the bounds of a skinned mesh should be calculated, and thus when it should be culled:


* <code>Static</code> is a very cheap method based on the mesh alone. This does not require realtime computation, so ideally use this if possible.
* <code>Static</code> is a very cheap method based on the mesh alone. This does not require realtime computation, so ideally use this if possible.
Line 48: Line 48:
* [[CJ Avatar Culling]]: <code>resrec:///U-Crusher/R-800E5546BCDFE9CB8E385FDBE91A391FEBFADB15CB072EFC8864F011C58EC5AA</code>
* [[CJ Avatar Culling]]: <code>resrec:///U-Crusher/R-800E5546BCDFE9CB8E385FDBE91A391FEBFADB15CB072EFC8864F011C58EC5AA</code>
* [https://github.com/Raidriar796/HeadlessUserCulling HeadlessUserCulling] (by Raidriar)
* [https://github.com/Raidriar796/HeadlessUserCulling HeadlessUserCulling] (by Raidriar)
** The simplest, least in-game code, and most reliable culling system due to modding capabilities
** No setup in-world, exists as part of the host client that gets spawned out on hosting a world.
** Automatically used for new sessions without any need to spawn something out or attach something to the world manually
** Highly recommended if culling is done on a headless host, as it provides a few extra niceties for multi-session hosting.
** As of writing, only does distance-based culling, not any nearest-x-user culling
** As of writing, only does distance-based culling, not any nearest-x-user culling
** Only works on headless hosts. Highly recommended if culling is desired on a headless


[[Category:Optimization]]
[[Category:Optimization]]

Latest revision as of 18:11, 11 December 2025

Culling refers to not processing or rendering specific parts of a world to improve performance. Culling can be done in a rendering setting or a more general CPU setting, with the former having a little bit of a built-in implementation in Resonite already.

Why cull?

As worlds get large, assets become plentiful, and more users join a world, there will be things that are immediately relevant to a user's experience and things that are irrelevant to a user's experience. In general, things that are far away from a user, obscured by walls, separated via rooms, or out of a user's view range are not relevant to a user's immediate experience. If calculations or rendering was done on these irrelevant objects, then excess work is being done for little to no gain, and performance can overall suffer due to the extra work.

By culling things that have no relevance to the user's current experience, the user's computer does not need to perform extra work on those things, which can slightly or significantly improve performance depending on the complexity of a world.

Built-in culling

Resonite currently performs a form of frustrum culling such that objects that are outside the field of view are usually not rendered. An exception to this is when a shadow of the object is within the view, in which case the object will need to be rendered to cast the shadow even if it's outside the view frustrum.

This frustrum culling is only done for rendering and not for any world items, meaning that ProtoFlux and components are unaffected by this.

There is, of course, some work required to calculate the bounds required for the culling to function, but this is only done once for static meshes, making the complexity of a static mesh irrelevant for culling to function.

To take advantage of this culling, it may be desirable to break up large meshes where not every part of the mesh will need to be visible at once, such as sprawling indoor environments. On the other hand, if many small meshes are expected to almost always be visible at the same time, it may be desirable to combine them into one mesh to reduce the amount of frustrum calculations that need to be done.

Skinned meshes

Skinned meshes have a few extra considerations due to the bones being able to deform the visual of the mesh. The BoundsComputeMethod field on a SkinnedMeshRenderer provides a few different options for how the bounds of a skinned mesh should be calculated, and thus when it should be culled:

  • Static is a very cheap method based on the mesh alone. This does not require realtime computation, so ideally use this if possible.
  • FastDisjointRootApproximate first merges all bones into disjoint groups (any overlapping bones are merged into a single one) to reduce overall number of bones, then uses those to approximate the bounds in realtime. This is the fastest realtime method and is recommended if parts of a mesh are being culled when using Static.
  • MediumPerBoneApproximate computes mesh bounds from bounds of every single bone. This is more accurate, but also much slower.
  • SlowRealtimeAccurate uses the actual transformed geometry for bounds calculation, requiring the entire mesh to be processed every frame. This is extremely heavy in comparison, but it will respect things like blendshapes in addition to bones.
  • Proxy is slightly different from the others, but also potentially very cheap. It relies on the bounding box calculated for another SkinnedMeshRenderer referenced in the ProxyBoundsSource field. This is useful in cases where you have a large main mesh and you need the visiblity of smaller meshes to be linked to it.

World-based culling

Outside of what Resonite has built in, culling systems can be implemented in worlds to disable certain parts of the world that are irrelvant to the user's current state. By doing so, less meshes will be used for frustrum culling and less work will be done on ProtoFlux and components.

The specific way that world-based culling systems are implemented can vary wildly. However, the most straightforward way to cull parts of a world is by using Slot spatial variables to define regions of a world, then use a sampler on the user's view position to determine what region a user is in. From there, you can hook up whatever you want to be culled to the region that a user is in. This can be combined with parenting the local user under a slot whenever they change regions, allowing users in far-away or occluded parts of the map to be culled as part of the world.

Specific culling considerations for collider components

Avoid performing manual culling of colliders in such a way that they are activated/deactivated very often. Collider performance impact works differently than for rendered meshes; performance costs for colliders are already heavily optimized as they are only checked when they're relevant. Toggling them on and off regularly can disrupt this under-the-hood optimization process and may even be more expensive.

User-based culling

In addition to world-based culling, there exist culling systems that can disable the avatars of users who are far away or limit the number of users that are visible at once. This can alleviate pressure on a per-session basis without needing any special setup in the world.

There is no perfect user culling system. All of them have some disadvantage or oddity due to a current lack of flexibility in Resonite to make something that works consistently, for every avatar, and in a reliable and simple way. As such, an overview of existing culling systems will be provided here:

  • Cutout impostor system (by Kulza & Cyro): resrec:///U-Kulza/R-6083c855-deb7-48d6-846c-77a4531f657e
  • sctanf's culling system: resrec:///U-sctanf/R-88d5a6ed-074f-497c-9496-ae9e6007e866
  • CJ Avatar Culling: resrec:///U-Crusher/R-800E5546BCDFE9CB8E385FDBE91A391FEBFADB15CB072EFC8864F011C58EC5AA
  • HeadlessUserCulling (by Raidriar)
    • No setup in-world, exists as part of the host client that gets spawned out on hosting a world.
    • Highly recommended if culling is done on a headless host, as it provides a few extra niceties for multi-session hosting.
    • As of writing, only does distance-based culling, not any nearest-x-user culling