Skip to content

Implement Mesh streaming #6109

Description

@reduz

Describe the project you are working on

Godot

Describe the problem or limitation you are having in your project

For large scenes, loading meshes (3D models) consume a lot of video memory and it takes a long time to do so.

Describe the feature / enhancement and how it helps to overcome the problem or limitation

Mesh streaming aims to solve this. A fixed amount of memory is reserved for streaming and then meshes are streamed in (higher detail) and out (lower detail) depending on proximity to the camera.

Describe how your proposal will work, with code, pseudo-code, mock-ups, and/or diagrams

Overview

The first thing that needs to be understood is that an efficient, modern mesh streaming has many requirements that need to be met:

  • Ability to split the mesh content into pages that can be loaded in and out in a fixed amount of video memory (to avoid fragmentation and defragmentation).
  • Ability to be culled entirely in the GPU, as we are intending to draw massive amount of objects.
  • Ability to use existing Godot materials.

To comply with the first requirement, this generally means using triangle strips and a fixed format that covers the most common use case (vertex, normal, tangent, uv). As such, colors, uv2, bones, weights, indices, etc. will not be supported.

This means that, with common encoding, a vertex will take 28 bytes. A common chunk of mesh can be 64 vertices, so this means 1792 bytes. If we want to be able to "cull" those chunks (so an object that hits the camera only has those chunks drawn if passing frustum and occlusion culling), the triangle strip should be self contained, otherwise it can just continue until the end.

In practice, this means that this needs to be a separate type of mesh, likely StreamedMesh that is registered separately in Godot than standard meshes.

As these chunks would need to be streamed form low detail to high detail, this means that different LOD versions would need to be stored separately so they can be streamed in and out (no index based LOD).

Rendering

Remember that our main goal is to still be able to use Godot materials, otherwise the workflow would get too complicated. To achieve this, the base algorithm would be more or less like this:

  • Cull all the instances of StreamMesh using a compute shader, mark them visible or invisible (occlusion culling can be added later, like two pass occlusion culling).
  • Count how many instances are visible for each material and how many chunks for each mesh with a compute shader.
  • Copy the indices of the visible chunks for all objects to a very large array.
  • Store the first index for every material.
  • Call a drawing primitive (draw arrays indirect) for every material using the proper start vertex offset.
  • In the shader, fetch the right vertex based on the gl_VertexIndex.

Of course, there are more things that need to be taken care of:

  • Finding the right LOD level
  • Determining when LOD levels need to be streamed-in or out
  • Culling objects that intersect the frustum so only chunks that intersect are visible.
  • Culling objects against the depth buffer if using two pass occlusion culling.

As it should be obvious, this render pass is separate from the regular geometry render pass in Godot.

Q: Is this like Nanite?
A: No, Nanite is more complex since it has a LOD hierarchy (which means a single large object can have multiple levels of detail). This just aims to be a good enough solution for most use cases that is not as complex to implement, but it could eventually be extended into something more akin to Nanite if there is more demand in the future.

If this enhancement will not be used often, can it be worked around with a few lines of script?

N/A

Is there a reason why this should be core and not an add-on in the asset library?

N/A

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions