Skip to content

Add options to lower precision/compressed vertex buffer#21926

Open
beicause wants to merge 42 commits intobevyengine:mainfrom
beicause:mesh-attributes-low-precision
Open

Add options to lower precision/compressed vertex buffer#21926
beicause wants to merge 42 commits intobevyengine:mainfrom
beicause:mesh-attributes-low-precision

Conversation

@beicause
Copy link
Contributor

@beicause beicause commented Nov 24, 2025

Objective

Resolves #21902.

Solution

This PR adopts a relatively transparent approach to reduce the GPU vertex buffer size. On CPU-side mesh can still use uncompressed Float32 data, and users are not required to insert compressed vertex formats. The vertex data is automatically processed into lower-precision/octahedral encoded data when uploading to the GPU.

To enable vertex attribute compression, just set the attribute_compression field of Mesh, or set mesh_attribute_compression of GltfLoaderSettings. If enabled, normal and tangent will be octahedral encoded Unorm16x2, uv0, uv1, joint weight and color will be corresponding Unorm16 or Float16. I also provide Unorm8x4 for vertex color if hdr isn't needed.

Update 2026-2-16

Removed previous approach that automatically compresses vertex buffer according to flags when uploading to GPU. Instead, I added compressed_mesh method to Mesh to construct compressed Mesh ahead of time. GltfLoader can also opt-in mesh compressing when loading. I also add an option to convert indices to u16, though I believe blender gltf exporter already uses u16 indices when possible.

Testing

I tested with several 2d and 3d examples and it seems to work.

@JMS55 JMS55 self-requested a review November 24, 2025 03:20
@JMS55
Copy link
Contributor

JMS55 commented Nov 24, 2025

This will need some thinking on how it interacts with Virtual Geometry and Solari, which rely on hardcoded vertex formats.

@greeble-dev
Copy link
Contributor

For the octahedral normals and tangents, encoding as UNORM16 or SNORM16 should give better accuracy? Unless there's some compatibility or performance reason to prefer floats.

For joint weights, I'd similarly suggest UNORM16. Unless Bevy wants to support joints weights greater than one - I don't know of any engine that does this. For bonus points it should also adjusted the encoded weights to make sure they sum to one.

@greeble-dev greeble-dev added C-Feature A new feature, making something new possible A-Rendering Drawing game state to the screen C-Performance A change motivated by improving speed, memory usage or compile times S-Needs-Review Needs reviewer attention (from anyone!) to move forward labels Nov 24, 2025
@github-actions
Copy link
Contributor

Your PR caused a change in the graphical output of an example or rendering test. This might be intentional, but it could also mean that something broke!
You can review it at https://pixel-eagle.com/project/B04F67C0-C054-4A6F-92EC-F599FEC2FD1D?filter=PR-21926

If it's expected, please add the M-Deliberate-Rendering-Change label.

If this change seems unrelated to your PR, you can consider updating your PR to target the latest main branch, either by rebasing or merging main into it.

2 similar comments
@github-actions
Copy link
Contributor

Your PR caused a change in the graphical output of an example or rendering test. This might be intentional, but it could also mean that something broke!
You can review it at https://pixel-eagle.com/project/B04F67C0-C054-4A6F-92EC-F599FEC2FD1D?filter=PR-21926

If it's expected, please add the M-Deliberate-Rendering-Change label.

If this change seems unrelated to your PR, you can consider updating your PR to target the latest main branch, either by rebasing or merging main into it.

@github-actions
Copy link
Contributor

Your PR caused a change in the graphical output of an example or rendering test. This might be intentional, but it could also mean that something broke!
You can review it at https://pixel-eagle.com/project/B04F67C0-C054-4A6F-92EC-F599FEC2FD1D?filter=PR-21926

If it's expected, please add the M-Deliberate-Rendering-Change label.

If this change seems unrelated to your PR, you can consider updating your PR to target the latest main branch, either by rebasing or merging main into it.

@greeble-dev
Copy link
Contributor

greeble-dev commented Nov 24, 2025

@beicause, I noticed that you also changed the UVs from FLOAT16 to UNORM16. That might be a problem for some assets since it's legitimate for UVs to be outside the [0, 1] range.

I'm not sure what the best option is for UVs, so maybe worth seeing if someone else has an opinion. From a glance it seems like Unreal supports FLOAT16/32. Godot does use UNORM16 but scales them to fit the maximum value if they're outside of [0, 1] (ref 1, ref 2).

@greeble-dev greeble-dev added the C-Dependencies A change to the crates that Bevy depends on label Nov 24, 2025
@greeble-dev
Copy link
Contributor

Also, could the PR description say whether compression is enabled by default? The description currently sounds like it's disabled by default, but the code seems like it's enabled by default. I can't speak for anyone else, but I'm guessing most people would prefer it to be disabled by default.

@beicause
Copy link
Contributor Author

I think using float16 for UVs is worthwhile because the benefits of unorm16 may be limited after thought.

I would probably like to enable compression by default, as other engines seem to do this by default as well. Enabling compression generally provides a net benefit, and users can opt-out if issues arise.

@github-actions
Copy link
Contributor

Your PR caused a change in the graphical output of an example or rendering test. This might be intentional, but it could also mean that something broke!
You can review it at https://pixel-eagle.com/project/B04F67C0-C054-4A6F-92EC-F599FEC2FD1D?filter=PR-21926

If it's expected, please add the M-Deliberate-Rendering-Change label.

If this change seems unrelated to your PR, you can consider updating your PR to target the latest main branch, either by rebasing or merging main into it.

@greeble-dev greeble-dev added the X-Contentious There are nontrivial implications that should be thought through label Nov 24, 2025
@greeble-dev
Copy link
Contributor

Added X-Contentious as I expect there'll be various opinions on enabling by default. Also added C-Dependencies as the PR adds a dependency on half to bevy_mesh - although I'm not sure if the label is appropriate since half is already used by bevy_image.

@github-actions
Copy link
Contributor

Your PR caused a change in the graphical output of an example or rendering test. This might be intentional, but it could also mean that something broke!
You can review it at https://pixel-eagle.com/project/B04F67C0-C054-4A6F-92EC-F599FEC2FD1D?filter=PR-21926

If it's expected, please add the M-Deliberate-Rendering-Change label.

If this change seems unrelated to your PR, you can consider updating your PR to target the latest main branch, either by rebasing or merging main into it.

@JMS55
Copy link
Contributor

JMS55 commented Dec 1, 2025

Some quick drive by thoughts:

  • f16 UVs are definitely dangerous. We should probably avoid enabling that one by default.
  • f16 positions would be great to have. I'd like to use them in Solari.
  • We should take advantage of the f16 type in wgsl shaders where we can and where it's beneficial) to save registers

@greeble-dev
Copy link
Contributor

For positions, I'd suggest an AABB-relative unorm16 instead of f16. It's a faff to set up but the worst case error is much lower. I've seen both tried for moderate to high poly characters, and the f16 version had noticeable artifacts.

@JMS55
Copy link
Contributor

JMS55 commented Dec 1, 2025

The reason I suggest f16 is that it's one of the few supported BLAS formats for raytracing https://github.com/Vecvec/wgpu/blob/3e9ede6c38012d6c134e1ced689ded2f58dec49f/wgpu-types/src/features.rs#L1497-L1501, and it has pretty significant improvements over f32 https://zeux.io/2025/03/31/measuring-acceleration-structures/

@beicause beicause force-pushed the mesh-attributes-low-precision branch from 5999291 to 8b85876 Compare December 2, 2025 01:30
@github-actions
Copy link
Contributor

github-actions bot commented Dec 2, 2025

Your PR caused a change in the graphical output of an example or rendering test. This might be intentional, but it could also mean that something broke!
You can review it at https://pixel-eagle.com/project/B04F67C0-C054-4A6F-92EC-F599FEC2FD1D?filter=PR-21926

If it's expected, please add the M-Deliberate-Rendering-Change label.

If this change seems unrelated to your PR, you can consider updating your PR to target the latest main branch, either by rebasing or merging main into it.

@beicause
Copy link
Contributor Author

beicause commented Dec 2, 2025

I've added snorm16 positions relative to AABB. Its precision should be as good as unorm16. And it seems that the BLAS supports it?

@github-actions
Copy link
Contributor

github-actions bot commented Dec 2, 2025

Your PR caused a change in the graphical output of an example or rendering test. This might be intentional, but it could also mean that something broke!
You can review it at https://pixel-eagle.com/project/B04F67C0-C054-4A6F-92EC-F599FEC2FD1D?filter=PR-21926

If it's expected, please add the M-Deliberate-Rendering-Change label.

If this change seems unrelated to your PR, you can consider updating your PR to target the latest main branch, either by rebasing or merging main into it.

1 similar comment
@github-actions
Copy link
Contributor

github-actions bot commented Dec 2, 2025

Your PR caused a change in the graphical output of an example or rendering test. This might be intentional, but it could also mean that something broke!
You can review it at https://pixel-eagle.com/project/B04F67C0-C054-4A6F-92EC-F599FEC2FD1D?filter=PR-21926

If it's expected, please add the M-Deliberate-Rendering-Change label.

If this change seems unrelated to your PR, you can consider updating your PR to target the latest main branch, either by rebasing or merging main into it.

@JMS55
Copy link
Contributor

JMS55 commented Dec 2, 2025

This deserves a thorough review by me, but I unfortunately won't have time for the next while due to work. Apologies - it's something I'm definitely excited to land though!

@github-actions
Copy link
Contributor

Your PR caused a change in the graphical output of an example or rendering test. This might be intentional, but it could also mean that something broke!
You can review it at https://pixel-eagle.com/project/B04F67C0-C054-4A6F-92EC-F599FEC2FD1D?filter=PR-21926

If it's expected, please add the M-Deliberate-Rendering-Change label.

If this change seems unrelated to your PR, you can consider updating your PR to target the latest main branch, either by rebasing or merging main into it.

Copy link
Contributor

@greeble-dev greeble-dev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requesting one small change following updates.

@greeble-dev
Copy link
Contributor

Oops, and just noticed solari is broken if COMPRESS_POSITION is enabled. Although the error varies.

cargo run --example solari --features "bevy_solari https free_camera http"

Sometimes:

wgpu error: Validation Error
Caused by:
  In a CommandEncoder, label = 'build_blas_command_encoder'
    Buffer ResourceErrorIdent { type: "Buffer", label: "general mesh slab 2 (vertex buffer)" } size is insufficient for provided size information (size: 2359368, required: 2392416

Sometimes:

thread 'Compute Task Pool (1)' (160960) panicked at crates\bevy_render\src\view\window\mod.rs:309:17:
Couldn't get swap chain texture, operation unrecoverable: Acquiring a texture failed with a generic error. Check error callbacks for more information

@greeble-dev
Copy link
Contributor

For what it's worth, Solari is the only issue I could find. Tested by screenshot comparison between compression off and everything enabled.

Native/Win10/Nvidia:

cargo run --example scene_viewer --features "free_camera pbr_anisotropy_texture jpeg" -- "assets/models/AnisotropyBarnLamp/AnisotropyBarnLamp.gltf"
cargo run --example animated_mesh
cargo run --example vertex_colors
cargo run --example meshlet --features "meshlet meshlet_processor https free_camera"

Chrome/WIn10/Nvidia/WebGL+WebGPU

cargo run --example scene_viewer --features "free_camera pbr_anisotropy_texture jpeg" --target wasm32-unknown-unknown
cargo run --example scene_viewer --features "free_camera pbr_anisotropy_texture jpeg webgpu" --target wasm32-unknown-unknown

Copy link
Contributor

@greeble-dev greeble-dev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm clicking approve as this seems 99% of the way there - I've done a bunch of testing and the only outstanding issue is Solari.

@beicause
Copy link
Contributor Author

beicause commented Feb 2, 2026

I don't think solari support the compressed Mesh in this PR yet. But I'm not planning to implement it myself as I'm unfamiliar with solari and I don't the hardware to run it.

Currently the vertex formats check in solari and meshlet is wrong because the formats of written buffer differs from the formats in Mesh::attributes, and meshlet processor should be possible to read uncompressed vertex buffer directly. I'll try addressing this.

@greeble-dev
Copy link
Contributor

For what it's worth, I've raised on Discord whether the PR could land even though Solari isn't working: https://discord.com/channels/691052431525675048/743663924229963868/1467852870975230054

Greeble: Re #21926 (mesh compression), would it be acceptable for this PR to land even though it doesn't currently work with Solari? This would only affect users who choose to enable the new compression feature - it's disabled by default. I'm asking because it's a big PR and I'm worried about it getting hung up on that one issue.

@beicause
Copy link
Contributor Author

beicause commented Feb 2, 2026

Update: I changed Mesh to always write the vertex buffer as is and you need to use the compressed_mesh method (or GltfLoaderSettings) to create the compressed Mesh, and compression flags is changed to a private field.

This seems more robust for me - attributes are consistent with the actual vertex buffer, and users can directly create and save the compressed Mesh.

Greeble: Re #21926 (mesh compression), would it be acceptable for this PR to land even though it doesn't currently work with Solari? This would only affect users who choose to enable the new compression feature - it's disabled by default. I'm asking because it's a big PR and I'm worried about it getting hung up on that one issue.

I don't think this a blocker. Solari and meshlet processor inherently have strict requirements for vertex formats and don't support custom vertex attributes.

@github-actions
Copy link
Contributor

github-actions bot commented Feb 2, 2026

Your PR caused a change in the graphical output of an example or rendering test. This might be intentional, but it could also mean that something broke!
You can review it at https://pixel-eagle.com/project/B04F67C0-C054-4A6F-92EC-F599FEC2FD1D?filter=PR-21926

If it's expected, please add the M-Deliberate-Rendering-Change label.

If this change seems unrelated to your PR, you can consider updating your PR to target the latest main branch, either by rebasing or merging main into it.

@cart cart added this to Rendering Feb 12, 2026
@github-project-automation github-project-automation bot moved this to Needs SME Triage in Rendering Feb 12, 2026
@cart cart removed this from Rendering Feb 12, 2026
@tychedelia tychedelia added this to the 0.19 milestone Feb 13, 2026
@tychedelia
Copy link
Member

Adding this to 0.19

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-Rendering Drawing game state to the screen C-Dependencies A change to the crates that Bevy depends on C-Feature A new feature, making something new possible C-Performance A change motivated by improving speed, memory usage or compile times S-Needs-Review Needs reviewer attention (from anyone!) to move forward X-Contentious There are nontrivial implications that should be thought through

Projects

No open projects
Status: No status

Development

Successfully merging this pull request may close these issues.

Use low precision format for vertex attributes

4 participants