Skip to content

add InfallibleMesh for mesh construction without unwraps#21986

Open
robtfm wants to merge 5 commits intobevyengine:mainfrom
robtfm:infallible-mesh
Open

add InfallibleMesh for mesh construction without unwraps#21986
robtfm wants to merge 5 commits intobevyengine:mainfrom
robtfm:infallible-mesh

Conversation

@robtfm
Copy link
Contributor

@robtfm robtfm commented Nov 30, 2025

Objective

Solution

builds on #21732 - could replace that, but i thought better to raise a different PR to keep the changes separated as this touches quite a lot more of the codebase. to see the diff for just this pr i created https://github.com/robtfm/bevy/pull/5/files which applies this onto the original pr.

  • add InfallibleMesh type for use during cpu-side mesh construction, which wraps Mesh and ensures that the internal mesh data can't be extracted.
  • modify primitives and examples to use the new type
  • bonus: clean up the From<primitives> for Mesh code: remove ~30 individual From<T> for Mesh implementations and replace with a blanket impl, supported by modifying Meshable::mesh to take self instead of &self. basically all existing implementations were copying or cloning self anyway, so this doesn't seem like it should cause any issues.

Changelog

Existing accessor methods on Mesh have been changed to return a Result<T, MeshAccessError> which will return an error if the Mesh has been extracted to the RenderWorld.
When constructing meshes we now recommend using the InfallibleMesh type which guarantees the internal data cannot be extracted prior to being converted into a normal mesh.

@robtfm robtfm added A-Rendering Drawing game state to the screen C-Usability A targeted quality-of-life change that makes Bevy easier to use labels Nov 30, 2025
@greeble-dev
Copy link
Contributor

I've skimmed the changes and couldn't spot any obvious issues.

I'm not sold on the way InfallibleMesh contains a Mesh and can deref to &Mesh. I think that constrains the implementation as it can only use data that's in Mesh, and every mutation has to make sure Mesh is in a valid state. Maybe that becomes a limitation if we want to support more expensive transforms that only happen when everything has finished - stuff like compression. So I'd rather all the mutating methods moved out of Mesh, and InfallibleMesh was opaque and the only way to get at the finished Mesh is via From<InfallibleMesh>.

But that's debatable, and the current version does solve an immediate problem. So maybe it's fine to get the current version in first, and make the case for follow on changes separately.

@robtfm
Copy link
Contributor Author

robtfm commented Dec 6, 2025

That would mean having InfallibleMesh as a separate asset type? I can see lots of issues arising from that, on first glance I’m definitely on the opposite team for the debate…

In case it helps with framing, I would have called it MeshBuilder if that wasn’t already taken.

@greeble-dev
Copy link
Contributor

greeble-dev commented Dec 6, 2025

No, I didn't intend for InfallibleMesh to be a separate asset type. It's just a temporary helper. So the only change to the current PR would be to remove the implicit deref - then InfallibleMesh can only be turned into a Mesh through Into<Mesh>. Maybe in the long-term the InfallibleMesh internals move away from Mesh. Or maybe they stay as they are.

Actually... just checking the PR - InfallibleMesh currently derives Asset? Is that intended? Feels wrong regardless of which approach is taken.

@robtfm
Copy link
Contributor Author

robtfm commented Dec 6, 2025

the reason for the deref was primitive builders that use mesh_a.merge(&mesh_b), where merge takes a mesh. there are alternative ways it could be managed (InfallibleMesh::merge could require another InfallibleMesh to merge with, though that seems unnecessarily restrictive).

we only allow read-only access to the mesh content though, so that we can still ensure it hasn't been extracted.

i don't think i've quite understood the point about compression though - if it modifies the mesh and we don't want it to, we just avoid providing an InfallibleMesh compression method ... but maybe that's not what you meant?

@greeble-dev
Copy link
Contributor

greeble-dev commented Dec 7, 2025

The scenario I'm imagining is that Mesh becomes more optimised for loading and GPU upload at some point in the future - maybe that's through compression, or interleaving attributes, or packing everything into a single allocation. Or maybe there's extra validation added to make sure attributes match up. These kinds of things are difficult to do incrementally as attributes are added - they want the attributes finalised first so they can validate or optimize everything in a single step.

If InfallibleMesh can be deref'd to Mesh at any time then that could be difficult. It would be easier if the only way to get a Mesh value is through calling Into<Mesh> (or some other function), so it's clear that the user is done adding attributes and any validation/optimization can now be done.

But I'm just speculating. Maybe the API ends up looking completely different.

@robtfm
Copy link
Contributor Author

robtfm commented Dec 7, 2025

you're right about the Asset derive - removed, thanks.

regarding invalid mesh states, i see what you're saying now, but agree we can cross that bridge when we come to it.

@greeble-dev
Copy link
Contributor

greeble-dev commented Dec 8, 2025

Sounds good to me. Personally I'd be minded to approve this PR even though I'm not keen on the deref - it's still a step in the right direction.

@beicause
Copy link
Contributor

beicause commented Dec 8, 2025

The scenario I'm imagining is that Mesh becomes more optimised for loading and GPU upload at some point in the future - maybe that's through compression, or interleaving attributes, or packing everything into a single allocation. Or maybe there's extra validation added to make sure attributes match up.

@greeble-dev 😅I realize that is a bit difficult and greatly breaks compatibility. Because bevy_picking or the physics engine may read the vertex data of the Mesh component so we cannot store only the GPU optimized interleaved vertex attributes on the Mesh. I would rather treat the Mesh as a component that is generic for both CPU and GPU, and add a separate GPU-friendly “raw” mesh component in some way (not sure if it’s feasible). In that case, I’m not sure if InfallibleMesh still makes sense.

@greeble-dev
Copy link
Contributor

My guess is that there'll be pressure to keep Mesh3d as struct Mesh3d(Handle<Mesh>), so users who just want to put scenes together are insulated from the complexity - whether it's optimized or not it's always a Handle<Mesh>. And bevy_picking and physics engines would be expected to deal with that complexity. Only speculating though.

@beicause
Copy link
Contributor

beicause commented Dec 9, 2025

Iterating over compressed or interleaved vertex attributes might degrade mesh performance for CPU reads, not the complexity of handling it. If the goal is just to have a mesh that’s efficient for GPU uploads, a mechanism like Mesh3d<Handle<Mesh>> -> RawMesh3d<Handle<RawMesh>> -> Rendering makes more sense to me.

I think the current Mesh interface is fine. Even modifying it can panic if it's extracted, I guess most users would still use Mesh over InfallibleMesh.

@greeble-dev
Copy link
Contributor

In my view, the key issue is separating the mesh types optimized for writing from the mesh types optimized for reading.

I'm also assuming there would be multiple types optimised for different kinds of reads - so having physics be able to read GPU mesh data is a useful convenience in some situations, but for situations where performance is more important there would be a separate type optimized for physics.

So I see this PR as a step forward to separating read and write types. I'm not so worried about which of those types ends up getting called Mesh.

@alice-i-cecile alice-i-cecile added the S-Needs-Review Needs reviewer attention (from anyone!) to move forward label Dec 15, 2025
@kfc35 kfc35 self-requested a review January 16, 2026 06:43
Copy link
Contributor

@kfc35 kfc35 left a comment

Choose a reason for hiding this comment

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

Reviewing this PR since I have context from the 0.18 release and the current strange state Mesh is in, and would like to see it continue into a better state. I agree that this PR helps with that.

I couldn’t find anything that should block this pull request logic wise. I think the only small concern I have as someone with less context is if the panicking in bevy_render is acceptable behavior, but given that there have not been any complaints or bug reports about it, I assume it is fine.

As for conflicts with main (if this PR is still a candidate for moving forward), I know that one conflict is just with intersections.rs as a result of the bug fix from #22356: should be resolvable by following what’s on main to use ok()? instead of the expects used in this PR

///
/// # Panics
/// Panics if [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`.
/// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`].=
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
/// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`].=
/// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`].

/// Panics if [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`.
/// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`].
/// Panics if the mesh does not have indices defined.
/// Panics when the mesh data has already been extracted to `RenderWorld`. To handle
Copy link
Contributor

Choose a reason for hiding this comment

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

This last panic should not be here, right?

/// towards the planes divided into the most triangles:
/// ```
/// # use bevy_asset::RenderAssetUsages;
/// # use bevy_mesh::{Mesh, PrimitiveTopology, Meshable, MeshBuilder};
Copy link
Contributor

Choose a reason for hiding this comment

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

Does this code example need to be updated with InfallibleMesh at all?

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.

Clicking approve as this seems like a reasonable step forward - I think it's good to separate the building of meshes from the read-optimized result. I do have a few concerns though:

  • The PR competes with #22128, although I prefer this PR.
  • I don't like the implicit deref to Mesh (comment), although that could be changed in a follow up.
  • InfallibleMesh is not an intuitive name. But MeshBuilder is already taken. Maybe CustomMeshBuilder? MutableMesh?

Also in case it helps when merging main, InfallibleMesh needs the skinned mesh bounds methods pasted in. And mesh picking needs the changes from #22356.

@greeble-dev greeble-dev added S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it M-Migration-Guide A breaking change to Bevy's public API that needs to be noted in a migration guide and removed S-Needs-Review Needs reviewer attention (from anyone!) to move forward labels Jan 29, 2026
@github-actions
Copy link
Contributor

It looks like your PR is a breaking change, but you didn't provide a migration guide.

Please review the instructions for writing migration guides, then expand or revise the content in the migration guides directory to reflect your changes.

@robtfm
Copy link
Contributor Author

robtfm commented Jan 29, 2026

if we changed InfallibleMesh to MeshBuilder, we could logically change MeshBuilder to MeshBuilderBuilder ... or maybe IntoMeshBuilder

@beicause
Copy link
Contributor

Personally, I think this is controversial and am not convinced about this PR:

  1. It introduces a large amount of duplicate APIs for InfallibleMesh and Mesh. Subsequent adding of more Mesh APIs requires duplication every time. This is a maintenance burden.

  2. Building a Mesh is already impossible to panic. And I feel removing the panic versions from Mesh is not good enough. For example, user code still need multiple checks or unwraps for Mesh. IMO code quality and Mesh access ergonomics are the priority.

@greeble-dev greeble-dev added the X-Contentious There are nontrivial implications that should be thought through label Jan 30, 2026
@greeble-dev
Copy link
Contributor

I'm adding the X-Contentious label. Partly because of disagreements. Partly because the longer term goals for meshes - interleaving, loading efficiency, assets-as-entities, etc - seem to be open questions and might be affected by the direction this PR is taking.

@alice-i-cecile alice-i-cecile added X-Needs-SME This type of work requires an SME to approve it. S-Waiting-on-SME This is currently waiting for an SME to resolve something controversial and removed S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it X-Contentious There are nontrivial implications that should be thought through labels Feb 2, 2026
@alice-i-cecile
Copy link
Member

Similarly not super keen on the prospect of additional code duplication here, but I agree that the mesh ergonomics situation with unwraps is not great. I am content to largely defer to our assorted SMEs here though.

@greeble-dev
Copy link
Contributor

greeble-dev commented Feb 3, 2026

For what it's worth, my guess is that the duplication is mostly temporary. So in the future Mesh will become more of a read-only interface, and so a lot of with_xxx-like functions fall away. But that's just a guess as we don't have a roadmap.

@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
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-Usability A targeted quality-of-life change that makes Bevy easier to use M-Migration-Guide A breaking change to Bevy's public API that needs to be noted in a migration guide S-Waiting-on-SME This is currently waiting for an SME to resolve something controversial X-Needs-SME This type of work requires an SME to approve it.

Projects

No open projects
Status: No status

Development

Successfully merging this pull request may close these issues.

5 participants