Skip to content

Meshlet builder improvements redux #15886

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

Merged
merged 30 commits into from
Oct 23, 2024
Merged
Changes from 28 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
063a8b1
WIP: Proper error projection
JMS55 Oct 9, 2024
d8fd980
WIP: LOD selection (not working), some cleanup
JMS55 Oct 10, 2024
718e663
Fix: Forgot to set parent_group_error for children after building par…
JMS55 Oct 10, 2024
1ddf173
Misc
JMS55 Oct 10, 2024
8b71b24
Use zeux's error projection code
JMS55 Oct 11, 2024
4f96881
Misc
JMS55 Oct 11, 2024
cac11bc
Pack group errors 2x16float
JMS55 Oct 11, 2024
b5d8a0c
Clippy
JMS55 Oct 11, 2024
634eaa2
Try to fix ortho, use 0.5px threshold
JMS55 Oct 12, 2024
b093698
Fixes for error projection
JMS55 Oct 12, 2024
31dd6f6
Go back to previous codegen, slightly faster
JMS55 Oct 12, 2024
3a48456
Update bunny URL
JMS55 Oct 12, 2024
27c03f8
Add missing doc comment
JMS55 Oct 12, 2024
d9f5b75
Remove perspective comment, I don't actually like how it behaves
JMS55 Oct 12, 2024
2fa9731
Larger meshlet group size, constant seed for metis, smallvec optimiza…
JMS55 Oct 12, 2024
a88a5a7
Factor out constant
JMS55 Oct 12, 2024
8db943e
Misc refactor
JMS55 Oct 12, 2024
0abbb47
Small rename
JMS55 Oct 12, 2024
a1c2ee3
Use position-only vertices instead of triangle edges to determine mes…
JMS55 Oct 12, 2024
62d3605
Misc
JMS55 Oct 12, 2024
99722b0
Use position-only vertex remap count to save a bit of memory when all…
JMS55 Oct 12, 2024
86f0b70
Fix segfault when generating vertex remap
JMS55 Oct 12, 2024
24187a4
Lock group borders manually for better simplification
JMS55 Oct 13, 2024
bad03cf
Small doc fix
JMS55 Oct 13, 2024
6eda2aa
Comment missing a word
JMS55 Oct 13, 2024
aff12ab
Retry stuck meshlets when building the DAG
JMS55 Oct 13, 2024
d65879c
Update bunny asset url
JMS55 Oct 13, 2024
1ca60e7
CI lints
JMS55 Oct 13, 2024
41dee16
Merge commit '50d38f2757e7205632fcab0cc92e79831a307ae0' into meshlet-…
JMS55 Oct 23, 2024
02b0ada
Use a non-broken metis manual link
JMS55 Oct 23, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1209,7 +1209,7 @@ setup = [
"curl",
"-o",
"assets/models/bunny.meshlet_mesh",
"https://raw.githubusercontent.com/JMS55/bevy_meshlet_asset/8443bbdee0bf517e6c297dede7f6a46ab712ee4c/bunny.meshlet_mesh",
"https://raw.githubusercontent.com/JMS55/bevy_meshlet_asset/8483db58832542383820c3f44e4730e566910be7/bunny.meshlet_mesh",
],
]

11 changes: 6 additions & 5 deletions crates/bevy_pbr/Cargo.toml
Original file line number Diff line number Diff line change
@@ -18,7 +18,7 @@ shader_format_glsl = ["bevy_render/shader_format_glsl"]
trace = ["bevy_render/trace"]
ios_simulator = ["bevy_render/ios_simulator"]
# Enables the meshlet renderer for dense high-poly scenes (experimental)
meshlet = ["dep:lz4_flex", "dep:range-alloc", "dep:bevy_tasks"]
meshlet = ["dep:lz4_flex", "dep:range-alloc", "dep:half", "dep:bevy_tasks"]
# Enables processing meshes into meshlet meshes
meshlet_processor = [
"meshlet",
@@ -50,16 +50,17 @@ bevy_window = { path = "../bevy_window", version = "0.15.0-dev" }
# other
bitflags = "2.3"
fixedbitset = "0.5"
# meshlet
lz4_flex = { version = "0.11", default-features = false, features = [
"frame",
], optional = true }
derive_more = { version = "1", default-features = false, features = [
"error",
"from",
"display",
] }
# meshlet
lz4_flex = { version = "0.11", default-features = false, features = [
"frame",
], optional = true }
range-alloc = { version = "0.1.3", optional = true }
half = { version = "2", features = ["bytemuck"], optional = true }
meshopt = { version = "0.3.0", optional = true }
metis = { version = "0.2", optional = true }
itertools = { version = "0.13", optional = true }
28 changes: 22 additions & 6 deletions crates/bevy_pbr/src/meshlet/asset.rs
Original file line number Diff line number Diff line change
@@ -9,6 +9,7 @@ use bevy_reflect::TypePath;
use bevy_tasks::block_on;
use bytemuck::{Pod, Zeroable};
use derive_more::derive::{Display, Error, From};
use half::f16;
use lz4_flex::frame::{FrameDecoder, FrameEncoder};
use std::io::{Read, Write};

@@ -51,6 +52,8 @@ pub struct MeshletMesh {
pub(crate) meshlets: Arc<[Meshlet]>,
/// Spherical bounding volumes.
pub(crate) meshlet_bounding_spheres: Arc<[MeshletBoundingSpheres]>,
/// Meshlet group and parent group simplification errors.
pub(crate) meshlet_simplification_errors: Arc<[MeshletSimplificationError]>,
}

/// A single meshlet within a [`MeshletMesh`].
@@ -90,12 +93,12 @@ pub struct Meshlet {
#[derive(Copy, Clone, Pod, Zeroable)]
#[repr(C)]
pub struct MeshletBoundingSpheres {
/// The bounding sphere used for frustum and occlusion culling for this meshlet.
pub self_culling: MeshletBoundingSphere,
/// The bounding sphere used for determining if this meshlet is at the correct level of detail for a given view.
pub self_lod: MeshletBoundingSphere,
/// The bounding sphere used for determining if this meshlet's parent is at the correct level of detail for a given view.
pub parent_lod: MeshletBoundingSphere,
/// Bounding sphere used for frustum and occlusion culling for this meshlet.
pub culling_sphere: MeshletBoundingSphere,
/// Bounding sphere used for determining if this meshlet's group is at the correct level of detail for a given view.
pub lod_group_sphere: MeshletBoundingSphere,
/// Bounding sphere used for determining if this meshlet's parent group is at the correct level of detail for a given view.
pub lod_parent_group_sphere: MeshletBoundingSphere,
}

/// A spherical bounding volume used for a [`Meshlet`].
@@ -106,6 +109,16 @@ pub struct MeshletBoundingSphere {
pub radius: f32,
}

/// Simplification error used for choosing level of detail for a [`Meshlet`].
#[derive(Copy, Clone, Pod, Zeroable)]
#[repr(C)]
pub struct MeshletSimplificationError {
/// Simplification error used for determining if this meshlet's group is at the correct level of detail for a given view.
pub group_error: f16,
/// Simplification error used for determining if this meshlet's parent group is at the correct level of detail for a given view.
pub parent_group_error: f16,
}

/// An [`AssetSaver`] for `.meshlet_mesh` [`MeshletMesh`] assets.
pub struct MeshletMeshSaver;

@@ -139,6 +152,7 @@ impl AssetSaver for MeshletMeshSaver {
write_slice(&asset.indices, &mut writer)?;
write_slice(&asset.meshlets, &mut writer)?;
write_slice(&asset.meshlet_bounding_spheres, &mut writer)?;
write_slice(&asset.meshlet_simplification_errors, &mut writer)?;
writer.finish()?;

Ok(())
@@ -179,6 +193,7 @@ impl AssetLoader for MeshletMeshLoader {
let indices = read_slice(reader)?;
let meshlets = read_slice(reader)?;
let meshlet_bounding_spheres = read_slice(reader)?;
let meshlet_simplification_errors = read_slice(reader)?;

Ok(MeshletMesh {
vertex_positions,
@@ -187,6 +202,7 @@ impl AssetLoader for MeshletMeshLoader {
indices,
meshlets,
meshlet_bounding_spheres,
meshlet_simplification_errors,
})
}

52 changes: 27 additions & 25 deletions crates/bevy_pbr/src/meshlet/cull_clusters.wgsl
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#import bevy_pbr::meshlet_bindings::{
meshlet_cluster_meshlet_ids,
meshlet_bounding_spheres,
meshlet_simplification_errors,
meshlet_cluster_instance_ids,
meshlet_instance_uniforms,
meshlet_second_pass_candidates,
@@ -13,6 +14,7 @@
meshlet_hardware_raster_indirect_args,
meshlet_raster_clusters,
meshlet_raster_cluster_rightmost_slot,
MeshletBoundingSphere,
}
#import bevy_render::maths::affine3_to_square

@@ -48,8 +50,8 @@ fn cull_clusters(
let world_from_local = affine3_to_square(instance_uniform.world_from_local);
let world_scale = max(length(world_from_local[0]), max(length(world_from_local[1]), length(world_from_local[2])));
let bounding_spheres = meshlet_bounding_spheres[meshlet_id];
let culling_bounding_sphere_center = world_from_local * vec4(bounding_spheres.self_culling.center, 1.0);
let culling_bounding_sphere_radius = world_scale * bounding_spheres.self_culling.radius;
let culling_bounding_sphere_center = world_from_local * vec4(bounding_spheres.culling_sphere.center, 1.0);
let culling_bounding_sphere_radius = world_scale * bounding_spheres.culling_sphere.radius;

#ifdef MESHLET_FIRST_CULLING_PASS
// Frustum culling
@@ -60,28 +62,19 @@ fn cull_clusters(
}
}

// Calculate view-space LOD bounding sphere for the cluster
let lod_bounding_sphere_center = world_from_local * vec4(bounding_spheres.self_lod.center, 1.0);
let lod_bounding_sphere_radius = world_scale * bounding_spheres.self_lod.radius;
let lod_bounding_sphere_center_view_space = (view.view_from_world * vec4(lod_bounding_sphere_center.xyz, 1.0)).xyz;

// Calculate view-space LOD bounding sphere for the cluster's parent
let parent_lod_bounding_sphere_center = world_from_local * vec4(bounding_spheres.parent_lod.center, 1.0);
let parent_lod_bounding_sphere_radius = world_scale * bounding_spheres.parent_lod.radius;
let parent_lod_bounding_sphere_center_view_space = (view.view_from_world * vec4(parent_lod_bounding_sphere_center.xyz, 1.0)).xyz;

// Check LOD cut (cluster error imperceptible, and parent error not imperceptible)
let lod_is_ok = lod_error_is_imperceptible(lod_bounding_sphere_center_view_space, lod_bounding_sphere_radius);
let parent_lod_is_ok = lod_error_is_imperceptible(parent_lod_bounding_sphere_center_view_space, parent_lod_bounding_sphere_radius);
// Check LOD cut (cluster group error imperceptible, and parent group error not imperceptible)
let simplification_errors = unpack2x16float(meshlet_simplification_errors[meshlet_id]);
let lod_is_ok = lod_error_is_imperceptible(bounding_spheres.lod_group_sphere, simplification_errors.x, world_from_local, world_scale);
let parent_lod_is_ok = lod_error_is_imperceptible(bounding_spheres.lod_parent_group_sphere, simplification_errors.y, world_from_local, world_scale);
if !lod_is_ok || parent_lod_is_ok { return; }
#endif

// Project the culling bounding sphere to view-space for occlusion culling
#ifdef MESHLET_FIRST_CULLING_PASS
let previous_world_from_local = affine3_to_square(instance_uniform.previous_world_from_local);
let previous_world_from_local_scale = max(length(previous_world_from_local[0]), max(length(previous_world_from_local[1]), length(previous_world_from_local[2])));
let occlusion_culling_bounding_sphere_center = previous_world_from_local * vec4(bounding_spheres.self_culling.center, 1.0);
let occlusion_culling_bounding_sphere_radius = previous_world_from_local_scale * bounding_spheres.self_culling.radius;
let occlusion_culling_bounding_sphere_center = previous_world_from_local * vec4(bounding_spheres.culling_sphere.center, 1.0);
let occlusion_culling_bounding_sphere_radius = previous_world_from_local_scale * bounding_spheres.culling_sphere.radius;
let occlusion_culling_bounding_sphere_center_view_space = (previous_view.view_from_world * vec4(occlusion_culling_bounding_sphere_center.xyz, 1.0)).xyz;
#else
let occlusion_culling_bounding_sphere_center = culling_bounding_sphere_center;
@@ -148,14 +141,23 @@ fn cull_clusters(
meshlet_raster_clusters[buffer_slot] = cluster_id;
}

// https://stackoverflow.com/questions/21648630/radius-of-projected-sphere-in-screen-space/21649403#21649403
fn lod_error_is_imperceptible(cp: vec3<f32>, r: f32) -> bool {
let d2 = dot(cp, cp);
let r2 = r * r;
let sphere_diameter_uv = view.clip_from_view[0][0] * r / sqrt(d2 - r2);
let view_size = f32(max(view.viewport.z, view.viewport.w));
let sphere_diameter_pixels = sphere_diameter_uv * view_size;
return sphere_diameter_pixels < 1.0;
// https://github.com/zeux/meshoptimizer/blob/1e48e96c7e8059321de492865165e9ef071bffba/demo/nanite.cpp#L115
fn lod_error_is_imperceptible(lod_sphere: MeshletBoundingSphere, simplification_error: f32, world_from_local: mat4x4<f32>, world_scale: f32) -> bool {
let sphere_world_space = (world_from_local * vec4(lod_sphere.center, 1.0)).xyz;
let radius_world_space = world_scale * lod_sphere.radius;
let error_world_space = world_scale * simplification_error;

var projected_error = error_world_space;
if view.clip_from_view[3][3] != 1.0 {
// Perspective
let distance_to_closest_point_on_sphere = distance(sphere_world_space, view.world_position) - radius_world_space;
let distance_to_closest_point_on_sphere_clamped_to_znear = max(distance_to_closest_point_on_sphere, view.clip_from_view[3][2]);
projected_error /= distance_to_closest_point_on_sphere_clamped_to_znear;
}
projected_error *= view.clip_from_view[1][1] * 0.5;
projected_error *= view.viewport.w;

return projected_error < 1.0;
}

// https://zeux.io/2023/01/12/approximate-projected-bounds
405 changes: 275 additions & 130 deletions crates/bevy_pbr/src/meshlet/from_mesh.rs

Large diffs are not rendered by default.

27 changes: 14 additions & 13 deletions crates/bevy_pbr/src/meshlet/meshlet_bindings.wgsl
Original file line number Diff line number Diff line change
@@ -25,9 +25,9 @@ fn get_meshlet_triangle_count(meshlet: ptr<function, Meshlet>) -> u32 {
}

struct MeshletBoundingSpheres {
self_culling: MeshletBoundingSphere,
self_lod: MeshletBoundingSphere,
parent_lod: MeshletBoundingSphere,
culling_sphere: MeshletBoundingSphere,
lod_group_sphere: MeshletBoundingSphere,
lod_parent_group_sphere: MeshletBoundingSphere,
}

struct MeshletBoundingSphere {
@@ -62,16 +62,17 @@ var<push_constant> cluster_count: u32;
var<push_constant> meshlet_raster_cluster_rightmost_slot: u32;
@group(0) @binding(0) var<storage, read> meshlet_cluster_meshlet_ids: array<u32>; // Per cluster
@group(0) @binding(1) var<storage, read> meshlet_bounding_spheres: array<MeshletBoundingSpheres>; // Per meshlet
@group(0) @binding(2) var<storage, read> meshlet_cluster_instance_ids: array<u32>; // Per cluster
@group(0) @binding(3) var<storage, read> meshlet_instance_uniforms: array<Mesh>; // Per entity instance
@group(0) @binding(4) var<storage, read> meshlet_view_instance_visibility: array<u32>; // 1 bit per entity instance, packed as a bitmask
@group(0) @binding(5) var<storage, read_write> meshlet_second_pass_candidates: array<atomic<u32>>; // 1 bit per cluster , packed as a bitmask
@group(0) @binding(6) var<storage, read_write> meshlet_software_raster_indirect_args: DispatchIndirectArgs; // Single object shared between all workgroups/clusters/triangles
@group(0) @binding(7) var<storage, read_write> meshlet_hardware_raster_indirect_args: DrawIndirectArgs; // Single object shared between all workgroups/clusters/triangles
@group(0) @binding(8) var<storage, read_write> meshlet_raster_clusters: array<u32>; // Single object shared between all workgroups/clusters/triangles
@group(0) @binding(9) var depth_pyramid: texture_2d<f32>; // From the end of the last frame for the first culling pass, and from the first raster pass for the second culling pass
@group(0) @binding(10) var<uniform> view: View;
@group(0) @binding(11) var<uniform> previous_view: PreviousViewUniforms;
@group(0) @binding(2) var<storage, read> meshlet_simplification_errors: array<u32>; // Per meshlet
@group(0) @binding(3) var<storage, read> meshlet_cluster_instance_ids: array<u32>; // Per cluster
@group(0) @binding(4) var<storage, read> meshlet_instance_uniforms: array<Mesh>; // Per entity instance
@group(0) @binding(5) var<storage, read> meshlet_view_instance_visibility: array<u32>; // 1 bit per entity instance, packed as a bitmask
@group(0) @binding(6) var<storage, read_write> meshlet_second_pass_candidates: array<atomic<u32>>; // 1 bit per cluster , packed as a bitmask
@group(0) @binding(7) var<storage, read_write> meshlet_software_raster_indirect_args: DispatchIndirectArgs; // Single object shared between all workgroups/clusters/triangles
@group(0) @binding(8) var<storage, read_write> meshlet_hardware_raster_indirect_args: DrawIndirectArgs; // Single object shared between all workgroups/clusters/triangles
@group(0) @binding(9) var<storage, read_write> meshlet_raster_clusters: array<u32>; // Single object shared between all workgroups/clusters/triangles
@group(0) @binding(10) var depth_pyramid: texture_2d<f32>; // From the end of the last frame for the first culling pass, and from the first raster pass for the second culling pass
@group(0) @binding(11) var<uniform> view: View;
@group(0) @binding(12) var<uniform> previous_view: PreviousViewUniforms;

fn should_cull_instance(instance_id: u32) -> bool {
let bit_offset = instance_id % 32u;
22 changes: 18 additions & 4 deletions crates/bevy_pbr/src/meshlet/meshlet_mesh_manager.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use super::{
asset::{Meshlet, MeshletBoundingSpheres},
asset::{Meshlet, MeshletBoundingSpheres, MeshletSimplificationError},
persistent_buffer::PersistentGpuBuffer,
MeshletMesh,
};
@@ -26,7 +26,8 @@ pub struct MeshletMeshManager {
pub indices: PersistentGpuBuffer<Arc<[u8]>>,
pub meshlets: PersistentGpuBuffer<Arc<[Meshlet]>>,
pub meshlet_bounding_spheres: PersistentGpuBuffer<Arc<[MeshletBoundingSpheres]>>,
meshlet_mesh_slices: HashMap<AssetId<MeshletMesh>, [Range<BufferAddress>; 6]>,
pub meshlet_simplification_errors: PersistentGpuBuffer<Arc<[MeshletSimplificationError]>>,
meshlet_mesh_slices: HashMap<AssetId<MeshletMesh>, [Range<BufferAddress>; 7]>,
}

impl FromWorld for MeshletMeshManager {
@@ -42,6 +43,10 @@ impl FromWorld for MeshletMeshManager {
"meshlet_bounding_spheres",
render_device,
),
meshlet_simplification_errors: PersistentGpuBuffer::new(
"meshlet_simplification_errors",
render_device,
),
meshlet_mesh_slices: HashMap::new(),
}
}
@@ -81,6 +86,9 @@ impl MeshletMeshManager {
let meshlet_bounding_spheres_slice = self
.meshlet_bounding_spheres
.queue_write(Arc::clone(&meshlet_mesh.meshlet_bounding_spheres), ());
let meshlet_simplification_errors_slice = self
.meshlet_simplification_errors
.queue_write(Arc::clone(&meshlet_mesh.meshlet_simplification_errors), ());

[
vertex_positions_slice,
@@ -89,11 +97,12 @@ impl MeshletMeshManager {
indices_slice,
meshlets_slice,
meshlet_bounding_spheres_slice,
meshlet_simplification_errors_slice,
]
};

// If the MeshletMesh asset has not been uploaded to the GPU yet, queue it for uploading
let [_, _, _, _, meshlets_slice, _] = self
let [_, _, _, _, meshlets_slice, _, _] = self
.meshlet_mesh_slices
.entry(asset_id)
.or_insert_with_key(queue_meshlet_mesh)
@@ -106,7 +115,7 @@ impl MeshletMeshManager {

pub fn remove(&mut self, asset_id: &AssetId<MeshletMesh>) {
if let Some(
[vertex_positions_slice, vertex_normals_slice, vertex_uvs_slice, indices_slice, meshlets_slice, meshlet_bounding_spheres_slice],
[vertex_positions_slice, vertex_normals_slice, vertex_uvs_slice, indices_slice, meshlets_slice, meshlet_bounding_spheres_slice, meshlet_simplification_errors_slice],
) = self.meshlet_mesh_slices.remove(asset_id)
{
self.vertex_positions
@@ -117,6 +126,8 @@ impl MeshletMeshManager {
self.meshlets.mark_slice_unused(meshlets_slice);
self.meshlet_bounding_spheres
.mark_slice_unused(meshlet_bounding_spheres_slice);
self.meshlet_simplification_errors
.mark_slice_unused(meshlet_simplification_errors_slice);
}
}
}
@@ -145,4 +156,7 @@ pub fn perform_pending_meshlet_mesh_writes(
meshlet_mesh_manager
.meshlet_bounding_spheres
.perform_writes(&render_queue, &render_device);
meshlet_mesh_manager
.meshlet_simplification_errors
.perform_writes(&render_queue, &render_device);
}
1 change: 1 addition & 0 deletions crates/bevy_pbr/src/meshlet/mod.rs
Original file line number Diff line number Diff line change
@@ -290,6 +290,7 @@ impl Plugin for MeshletPlugin {
}
}

/// The meshlet mesh equivalent of [`bevy_render::mesh::Mesh3d`].
#[derive(Component, Clone, Debug, Default, Deref, DerefMut, Reflect, PartialEq, Eq, From)]
#[reflect(Component, Default)]
#[require(Transform, Visibility)]
68 changes: 40 additions & 28 deletions crates/bevy_pbr/src/meshlet/persistent_buffer_impls.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,42 @@
use super::{
asset::{Meshlet, MeshletBoundingSpheres},
asset::{Meshlet, MeshletBoundingSpheres, MeshletSimplificationError},
persistent_buffer::PersistentGpuBufferable,
};
use alloc::sync::Arc;
use bevy_math::Vec2;

impl PersistentGpuBufferable for Arc<[Meshlet]> {
type Metadata = (u64, u64, u64);

fn size_in_bytes(&self) -> usize {
self.len() * size_of::<Meshlet>()
}

fn write_bytes_le(
&self,
(vertex_position_offset, vertex_attribute_offset, index_offset): Self::Metadata,
buffer_slice: &mut [u8],
) {
let vertex_position_offset = (vertex_position_offset * 8) as u32;
let vertex_attribute_offset = (vertex_attribute_offset as usize / size_of::<u32>()) as u32;
let index_offset = index_offset as u32;

for (i, meshlet) in self.iter().enumerate() {
let size = size_of::<Meshlet>();
let i = i * size;
let bytes = bytemuck::cast::<_, [u8; size_of::<Meshlet>()]>(Meshlet {
start_vertex_position_bit: meshlet.start_vertex_position_bit
+ vertex_position_offset,
start_vertex_attribute_id: meshlet.start_vertex_attribute_id
+ vertex_attribute_offset,
start_index_id: meshlet.start_index_id + index_offset,
..*meshlet
});
buffer_slice[i..(i + size)].clone_from_slice(&bytes);
}
}
}

impl PersistentGpuBufferable for Arc<[u8]> {
type Metadata = ();

@@ -41,43 +73,23 @@ impl PersistentGpuBufferable for Arc<[Vec2]> {
}
}

impl PersistentGpuBufferable for Arc<[Meshlet]> {
type Metadata = (u64, u64, u64);
impl PersistentGpuBufferable for Arc<[MeshletBoundingSpheres]> {
type Metadata = ();

fn size_in_bytes(&self) -> usize {
self.len() * size_of::<Meshlet>()
self.len() * size_of::<MeshletBoundingSpheres>()
}

fn write_bytes_le(
&self,
(vertex_position_offset, vertex_attribute_offset, index_offset): Self::Metadata,
buffer_slice: &mut [u8],
) {
let vertex_position_offset = (vertex_position_offset * 8) as u32;
let vertex_attribute_offset = (vertex_attribute_offset as usize / size_of::<u32>()) as u32;
let index_offset = index_offset as u32;

for (i, meshlet) in self.iter().enumerate() {
let size = size_of::<Meshlet>();
let i = i * size;
let bytes = bytemuck::cast::<_, [u8; size_of::<Meshlet>()]>(Meshlet {
start_vertex_position_bit: meshlet.start_vertex_position_bit
+ vertex_position_offset,
start_vertex_attribute_id: meshlet.start_vertex_attribute_id
+ vertex_attribute_offset,
start_index_id: meshlet.start_index_id + index_offset,
..*meshlet
});
buffer_slice[i..(i + size)].clone_from_slice(&bytes);
}
fn write_bytes_le(&self, _: Self::Metadata, buffer_slice: &mut [u8]) {
buffer_slice.clone_from_slice(bytemuck::cast_slice(self));
}
}

impl PersistentGpuBufferable for Arc<[MeshletBoundingSpheres]> {
impl PersistentGpuBufferable for Arc<[MeshletSimplificationError]> {
type Metadata = ();

fn size_in_bytes(&self) -> usize {
self.len() * size_of::<MeshletBoundingSpheres>()
self.len() * size_of::<MeshletSimplificationError>()
}

fn write_bytes_le(&self, _: Self::Metadata, buffer_slice: &mut [u8]) {
3 changes: 3 additions & 0 deletions crates/bevy_pbr/src/meshlet/resource_manager.rs
Original file line number Diff line number Diff line change
@@ -135,6 +135,7 @@ impl ResourceManager {
storage_buffer_read_only_sized(false, None),
storage_buffer_read_only_sized(false, None),
storage_buffer_read_only_sized(false, None),
storage_buffer_read_only_sized(false, None),
storage_buffer_sized(false, None),
storage_buffer_sized(false, None),
storage_buffer_sized(false, None),
@@ -624,6 +625,7 @@ pub fn prepare_meshlet_view_bind_groups(
let entries = BindGroupEntries::sequential((
cluster_meshlet_ids.as_entire_binding(),
meshlet_mesh_manager.meshlet_bounding_spheres.binding(),
meshlet_mesh_manager.meshlet_simplification_errors.binding(),
cluster_instance_ids.as_entire_binding(),
instance_manager.instance_uniforms.binding().unwrap(),
view_resources.instance_visibility.as_entire_binding(),
@@ -652,6 +654,7 @@ pub fn prepare_meshlet_view_bind_groups(
let entries = BindGroupEntries::sequential((
cluster_meshlet_ids.as_entire_binding(),
meshlet_mesh_manager.meshlet_bounding_spheres.binding(),
meshlet_mesh_manager.meshlet_simplification_errors.binding(),
cluster_instance_ids.as_entire_binding(),
instance_manager.instance_uniforms.binding().unwrap(),
view_resources.instance_visibility.as_entire_binding(),
2 changes: 1 addition & 1 deletion examples/3d/meshlet.rs
Original file line number Diff line number Diff line change
@@ -17,7 +17,7 @@ use camera_controller::{CameraController, CameraControllerPlugin};
use std::{f32::consts::PI, path::Path, process::ExitCode};

const ASSET_URL: &str =
"https://raw.githubusercontent.com/JMS55/bevy_meshlet_asset/8443bbdee0bf517e6c297dede7f6a46ab712ee4c/bunny.meshlet_mesh";
"https://raw.githubusercontent.com/JMS55/bevy_meshlet_asset/8483db58832542383820c3f44e4730e566910be7/bunny.meshlet_mesh";

fn main() -> ExitCode {
if !Path::new("./assets/models/bunny.meshlet_mesh").exists() {