Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Meshlet GLTF processor #13431

Open
wants to merge 39 commits into
base: main
Choose a base branch
from
Open

Meshlet GLTF processor #13431

wants to merge 39 commits into from

Conversation

JMS55
Copy link
Contributor

@JMS55 JMS55 commented May 20, 2024

Objective

  • Allow importing GLTF scenes (gltf + separate bin, gltf embedded, or glb) and converting all meshes to MeshletMeshes

Solution

  • bevy_gltf has two new features - meshlet, and meshlet_processor. The former is needed for loading processed GLTF files containing MeshletMeshes. The latter is needed for the conversion process (with the asset_processor feature).
  • RawGltfLoader - Similiar to GltfLoader, but returns the underlying gltf-rs Gltf json structure in a RawGltf, instead of parsing it into bevy's Gltf type (which stores handles to meshes/materials/images/etc).
  • MeshletMeshGltfSaver - Takes a RawGltf in, converts each mesh primitive to a MeshletMesh, adds the BEVY_meshlet_mesh extension to the primitive which points to a slice within buffer 0 (the GLB BIN buffer), and then appends the serialized MeshletMesh to that slice of the buffer. Then saves the file as a GLB.
  • Added asset processor aliases, so that users can write "MeshletMeshProcessor" in their meta files instead of the very verbose underlying LoadAndSave type path.

TODO

  • Remove the original mesh data from the processed asset to save a significant amount of bytes (can only be done with single-file embedded GLTF or GLB files)
  • Do we need logs in the asset saver? It takes a significant amount of time, and the asset server itself does not seem to log that something is busy processing.
  • Handle other types of meshes better? Should we automatically skip meshes with alpha blended or masked materials which are unsupported by the meshlet feature? Should we automatically remove extra vertex attributes like vertex colors, or should it be an error (MeshletMesh only supports a a pre-defined set of vertex attributes)?
  • Should bevy_gltf meshlet_processor feature imply bevy_asset asset_processor? How should the features be setup? I'm not entirely sure the current setup is correct across bevy_pbr/bevy_gltf/bevy_internal/bevy.
  • Using single-file embedded GLTF's means that images are embedded in the processed file, instead of being separate files that can be run through the asset processor themselves.
  • Performance. There's definitely a lot that could be improved/parallelized, both in the asset saver and in MeshletMesh::from_mesh().

Testing

  • Did you test these changes? If so, how?
    • Tested on bistro and the bunny
  • Are there any parts that need more testing?
    • There's a lot of permutations that need testing:
    • GLTF + separate bin containing mesh data (not recommended, but probably needs testing and maybe a warning in the asset saver)
    • GLTF with embedded bin
    • GLB
    • All the above, with various combinations of meshes (different unsupported vertex attributes, extensions like Blender_bevy_components_workflow, etc)
    • Need to test that bevy_gltf can be compiled individually with the meshlet/meshlet_processor features turned on or off. Feature flags are hard to get right, I might have missed something.
    • Forgetting to add the MeshletPlugin
    • Need to make sure existing examples and asset workflows did not break.
  • How can other people (reviewers) test your changes? Is there anything specific they need to know?
    • Copy the meta file from assets/models/bunny.gltf.meta. Run bevy with features asset_processor,meshlet_processer, and make sure to both add the MeshletPlugin to your app, and set AssetMode::Processed in AssetPlugin. See the meshlet example for more details.
    • If you want to spawn an entire GLTF scene instead of pulling out a single MeshletMesh, spawn a SceneBundle using foo.gltf#Scene0. Same exact API as how it works for non-meshlet mesh GLTFs.

Changelog

  • Added bevy::gltf RawGltf and RawGltfLoader
  • bevy::gltf::GltfLoaderSettings is now marked as #[serde(default)]
  • Added AssetApp and AssetProcessor register_asset_processor_with_alias()

image

@JMS55 JMS55 added A-Assets Load files from disk to use for things like images, models, and sounds A-Scenes Serialized ECS data stored on the disk labels May 20, 2024
@JMS55 JMS55 added this to the 0.15 milestone May 20, 2024
@alice-i-cecile alice-i-cecile added D-Complex Quite challenging from either a design or technical perspective. Ask for help! S-Waiting-on-Author The author needs to make changes or address concerns before this can be merged X-Uncontroversial This work is generally agreed upon labels May 21, 2024
@JMS55 JMS55 modified the milestones: 0.15, 0.14 May 24, 2024
@JMS55 JMS55 removed the S-Waiting-on-Author The author needs to make changes or address concerns before this can be merged label May 24, 2024
@JMS55 JMS55 marked this pull request as ready for review May 24, 2024 18:23
/// Registers the given `processor` in the [`App`]'s [`AssetProcessor`] along with an extra alias.
///
/// This alias can be used in meta files to refer to this asset processor without using the full type name.
fn register_asset_processor_with_alias<P: Process>(
Copy link
Contributor

Choose a reason for hiding this comment

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

Would it be better to not have an extra method here, and instead require asset processors to implement TypePath? (Probably not a now thing.)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Didn't think about TypePath, that might be a decent alternative.

#[cfg(feature = "meshlet")]
app.register_asset_processor_with_alias::<bevy_asset::processor::LoadAndSave<RawGltfLoader, meshlet_saver::MeshletMeshGltfSaver>>(
meshlet_saver::MeshletMeshGltfSaver.into(),
"MeshletMeshProcessor"
Copy link
Contributor

Choose a reason for hiding this comment

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

I think it would be a good idea to still include Gltf & the bevy namespace here (maybe like bevy::asset::GltfMeshletMeshProcessor?) - it's still going to be a lot shorter than the 3 type paths without the alias.

Copy link
Contributor Author

@JMS55 JMS55 May 25, 2024

Choose a reason for hiding this comment

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

Technically bevy::asset would be inconsistent with the other auto generated processor names which use bevy_asset due to using std::any::type_name(), but I get what you're saying.

..Default::default()
})
} else {
panic!("glTF file contained a MeshletMesh, but the meshlet cargo feature is not enabled.")
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think that a panic is appropriate here since it doesn't represent a code bug. A warning & skipping this primitive, or returning an error for the entire load might be preferable.

Comment on lines 110 to 111
"byteOffset": glb_buffer.len(),
"byteLength": meshlet_mesh_bytes.len(),
Copy link
Contributor

Choose a reason for hiding this comment

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

Should these fields be nested in an instance of BufferView?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I could, it's just more work though, and I'd need a custom type and accessory anyways. I don't see the point, it's just extra work.

Copy link
Contributor

Choose a reason for hiding this comment

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

Given the conversation on the other comment, I agree. I almost think it's more important to make clear that nobody should rely on this format, it's effectively an implementation detail (which is fine) - and that bevy is likely to change/remove it in later versions.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I should probably remove the version number from the error message and change the naming to magic number or something, actually. The only purpose of it is to tell users "bevy changed, you need to regenerate your assets".

let mut meshlet_mesh_bytes = meshlet_mesh.into_bytes().expect("TODO");

let extension = json!({
"version": MESHLET_MESH_ASSET_VERSION,
Copy link
Contributor

Choose a reason for hiding this comment

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

Not a problem for now, but: is the meshlet data a well-defined/version-safe format, or is it going to be tied to engine version and isn't designed for interchange?

If it's the latter, pushing it into gltf doesn't seem like the right answer. In the long run it would be best if bevy had binary formats for processed assets which were tied to the engine version & needed very few checks/very little processing before use. Obviously that's very inconvenient for checked-in examples / tests and I don't know how to square that circle.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's tied to the engine version. It's an opaque blob meant to be readable only by bevy's renderer with the same version that produced it in the first place.

Agreed GLTF isn't the best, I'd rather it be a bevy scene, but for now GLTF is the only workable option until scenes can reference other assets and the asset system can handle subassets better.


#[cfg(feature = "meshlet_processor")]
{
let meshlet_mesh = MeshletMesh::from_mesh(&mesh).expect("TODO");
Copy link
Contributor

Choose a reason for hiding this comment

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

TODO - guess this needs an appropriate expect message? Or to propagate the error?

for mesh in &mut gltf.meshes {
for primitive in &mut mesh.primitives {
let meshlet_mesh = meshlet_meshes.pop_front().unwrap();
let mut meshlet_mesh_bytes = meshlet_mesh.into_bytes().expect("TODO");
Copy link
Contributor

Choose a reason for hiding this comment

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

Another error TODO.

}

// Pad JSON buffer if needed
let mut gltf_bytes = gltf.to_vec().expect("TODO");
Copy link
Contributor

Choose a reason for hiding this comment

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

Another error TODO.

}
} else {
parent.spawn(PbrBundle {
// TODO: handle missing label handle errors here?
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 need to be resolved now?

Copy link
Contributor Author

@JMS55 JMS55 May 25, 2024

Choose a reason for hiding this comment

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

Don't think so. There shouldn't ever be a missing label error afaik, as they're created earlier in the function. This is also an existing comment, it just looks new because I moved the code blocks around a bit.

Copy link
Contributor

Choose a reason for hiding this comment

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

Right you are, missed that this was part of the move.

@alice-i-cecile alice-i-cecile added the S-Needs-Review Needs reviewer attention (from anyone!) to move forward label May 26, 2024
/// Registers the given `processor` in the [`App`]'s [`AssetProcessor`] along with an extra alias.
///
/// This alias can be used in meta files to refer to this asset processor without using the full type name.
fn register_asset_processor_with_alias<P: Process>(
Copy link
Member

@cart cart May 28, 2024

Choose a reason for hiding this comment

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

Still on vacation so this is just a quick review: I think this approach is solid, especially in the short term. I do think migrating to TypePath + supporting short names is the right long term move (this code was written in a pre-TypePath-merge world), but even in that case, it doesn't fully solve the "generic flattening / LoadAndSave erasure problem". We'd need to use that in combination with something like what @JMS55 suggested awhile back on discord (define a new short type name that shims LoadAndSave<X, Y>). Rather than discuss whether or not that is the "right" final approach here, we should just merge "aliases" as they are simple and they get the job done.

Random musings: to support the more general "one to many asset transformations" space, we'll need to make meta serialization more type erased + dynamic than it currently is. That might also allow us to rethink how things like LoadAndSave are expressed.

JMS55 and others added 2 commits May 29, 2024 10:10
Co-authored-by: François Mockers <mockersf@gmail.com>
@JMS55 JMS55 mentioned this pull request May 29, 2024
40 tasks
@alice-i-cecile alice-i-cecile added S-Waiting-on-Author The author needs to make changes or address concerns before this can be merged S-Needs-SME Decision or review from an SME is required X-Contentious There are nontrivial implications that should be thought through and removed S-Needs-Review Needs reviewer attention (from anyone!) to move forward X-Uncontroversial This work is generally agreed upon labels Jun 3, 2024
@alice-i-cecile alice-i-cecile removed this from the 0.14 milestone Jun 4, 2024
@alice-i-cecile alice-i-cecile removed the S-Needs-SME Decision or review from an SME is required label Jun 4, 2024
@alice-i-cecile
Copy link
Member

I've discussed this PR with Jasmine. Our conclusion is that getting this into a genuinely nice state for 0.14 isn't feasible. Furthermore, I'm worried that this isn't a good candidate for Bevy's first real pre-processing workflow and is going to be much less fraught to tackle after we've nailed down the basics.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-Assets Load files from disk to use for things like images, models, and sounds A-Scenes Serialized ECS data stored on the disk D-Complex Quite challenging from either a design or technical perspective. Ask for help! S-Waiting-on-Author The author needs to make changes or address concerns before this can be merged X-Contentious There are nontrivial implications that should be thought through
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants