godotengine / godot-proposals

Godot Improvement Proposals (GIPs)
MIT License
1.16k stars 97 forks source link

Optimizing 3D mesh into list of triangle strips/fans to save VRAM #8785

Open sammonius opened 10 months ago

sammonius commented 10 months ago

Describe the project you are working on

An 3D single-player exploration game involving a large open map.

Describe the problem or limitation you are having in your project

The main limit to the game's performance is the triangle count of the map. If it's too large, the RAM/VRAM usage becomes way too high. I'm trying to avoid loading up parts when needed since the player will be able to teleport at times.

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

Godot could automatically convert the list of triangles in a mesh to a list of triangle fans or strips, which would save a lot of memory. For example, a cube could go from storing 36 verticies (12 triangles), to 8 verticies (2 triangle strips). A small list of triangles would be stored alongside in case there's any gaps that couldn't be filled automatically.

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

var optimized_mesh = TriangleStripMesh.create_from_mesh(my_mesh)
my_mesh_instance.set_mesh(optimized_mesh)

The output could be visualized as converting a triangle grid to a square or hexagon one.

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

No.

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

It would have to be implemented in the core renderer.

Calinou commented 10 months ago

I don't think triangle strips are necessarily faster to render, and they're not suited for every type of geometry anyway. Can you link to some benchmarks?

The main limit to the game's performance is the triangle count of the map.

This is generally not the case on modern desktop GPUs, especially with automated mesh LOD and manual HLOD put into place. As long as you don't have too many triangles smaller than a pixel on screen, you should generally be fine.

sammonius commented 10 months ago

Sorry about the confusion, I thought performance referred to both memory and framerate. Triangle fans/strips wouldn't speed up the rendering but they would take up a lot less vram.

clayjohn commented 10 months ago

@sammonius We already have something that does this, but better. Instead of triangle strips/fans, you should be using indexed geometry. SurfaceTool has a handy function to index geometry: SurfaceTool.index().

sammonius commented 10 months ago

(previous close was by accident)

AThousandShips commented 10 months ago

Note that triangle fans aren't supported in general and deprecated on Direct3D, and not universally supported in Vulkan either, this is because caching, triangle fans are really bad for memory coherence

sammonius commented 10 months ago

@AThousandShips Thanks for the info. That's probably for the best anyways since triangle strips would be more efficent for constructing a solid mesh (to be honest I didn't know what a triangle strip was before making this post).

SurfaceTool.index() seems like it would be really useful for all sorts of meshes. Why not just index all geometry by default?

Calinou commented 10 months ago

SurfaceTool.index() seems like it would be really useful for all sorts of meshes. Why not just index all geometry by default?

This is already done in all relevant importers/loaders:

https://github.com/godotengine/godot/blob/fbaab3cf537a892295aabdfd02c8052e370e6669/editor/import/resource_importer_obj.cpp#L399

https://github.com/godotengine/godot/blob/fbaab3cf537a892295aabdfd02c8052e370e6669/scene/resources/importer_mesh.cpp#L1304

https://github.com/godotengine/godot/blob/fbaab3cf537a892295aabdfd02c8052e370e6669/scene/resources/mesh.cpp#L2223

https://github.com/godotengine/godot/blob/fbaab3cf537a892295aabdfd02c8052e370e6669/modules/gltf/gltf_document.cpp#L2429

sammonius commented 10 months ago

@Calinou Ok, thanks.

I guess that just leaves the idea of converting meshes to triangle strips automatically. Could that somehow be combined with indexing to save memory?

clayjohn commented 10 months ago

@sammonius Triangle strips will only be more efficient than indexed triangle lists in very rare cases. The trouble with triangle strips is that they only work for geometry that can be well represented by a row of triangles where each triangle shares an edge with the next triangle. Modern meshes used in games often require more complex representation and may even have floating triangles not connected to others.

On modern hardware, indexed vertices are cached, so there is no performance benefit to using triangle strips at render time.

This stack overflow answer includes some nice analysis on the subject. https://stackoverflow.com/a/17926147

sammonius commented 10 months ago

Modern meshes used in games often require more complex representation and may even have floating triangles not connected to others.

@clayjohn That could be worked around by just storing multiple triangle strips. I'm not sure about Direct3D or OpenGL, but Vulkan allows passing a list of triangle strips as one mesh by separating the strips with a special character (like what \0 does in strings), so it could still save a lot of memory without slowing down the rendering.

EDIT: All 3 API's support this feature as something called "primitive restart".

The only scenario I can think of where triangle strips would have a negative impact is if most of the mesh was made of separate triangles that were used for VFX through a vertex shader (like shattering glass for example). There would probably need be an option to disable triangle strips for cases like those.

sammonius commented 10 months ago

(This could probably be made in an addon with SurfaceTool now that I think about it)