diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2b5600e..74f86c8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,6 +22,8 @@ jobs: run: cargo fmt --all -- --check - name: Check run: cargo check ${{ matrix.features }} + - name: Clippy + run: cargo clippy ${{ matrix.features }} - name: Build run: cargo build ${{ matrix.features }} - name: Test diff --git a/benches/with_input.rs b/benches/with_input.rs index 4481a82..613500f 100644 --- a/benches/with_input.rs +++ b/benches/with_input.rs @@ -56,9 +56,9 @@ impl Mesh { let mut vertices = Vec::new(); let mut indices = Vec::new(); - for (_, model) in models.iter().enumerate() { + for model in models.iter() { let mesh = &model.mesh; - assert!(mesh.positions.len() % 3 == 0); + assert!(mesh.positions.len().is_multiple_of(3)); vertices.reserve(mesh.indices.len()); indices.extend_from_slice(&mesh.indices); @@ -280,7 +280,7 @@ fn with_input(c: &mut Criterion) { BenchmarkId::new("decode_index_buffer", input_name), &encoded_indices, |b, encoded_indices| { - b.iter(|| decode_index_buffer(&mut decoded_indices, &encoded_indices).unwrap()); + b.iter(|| decode_index_buffer(&mut decoded_indices, encoded_indices).unwrap()); }, ); } @@ -303,7 +303,7 @@ fn with_input(c: &mut Criterion) { |b, data| { let mut buffer = vec![0; encode_index_sequence_bound(data.len(), vertex_count)]; - b.iter(|| encode_index_sequence(&mut buffer, &data, IndexEncodingVersion::default())); + b.iter(|| encode_index_sequence(&mut buffer, data, IndexEncodingVersion::default())); }, ); @@ -318,7 +318,7 @@ fn with_input(c: &mut Criterion) { BenchmarkId::new("decode_index_sequence", input_name), &buffer, |b, buffer| { - b.iter(|| decode_index_sequence(&mut decoded_indices, &buffer)); + b.iter(|| decode_index_sequence(&mut decoded_indices, buffer)); }, ); diff --git a/bin/codec_bench.rs b/bin/codec_bench.rs index c42c700..cb391b7 100644 --- a/bin/codec_bench.rs +++ b/bin/codec_bench.rs @@ -1,3 +1,5 @@ +#![allow(clippy::identity_op)] + use meshopt_rs::index::IndexEncodingVersion; use meshopt_rs::index::buffer::{decode_index_buffer, encode_index_buffer, encode_index_buffer_bound}; use meshopt_rs::vertex::Position; @@ -46,19 +48,19 @@ fn bench_codecs(vertices: &[Vertex], indices: &[u32], bestvd: &mut f64, bestid: if verbose { println!( "source: vertex data {} bytes, index data {} bytes", - vertices.len() * std::mem::size_of::(), + std::mem::size_of_val(vertices), indices.len() * 4 ); } for pass in 0..if verbose { 2 } else { 1 } { if pass == 1 { - optimize_vertex_cache_strip(&mut ib, &indices, vertices.len()); + optimize_vertex_cache_strip(&mut ib, indices, vertices.len()); } else { - optimize_vertex_cache(&mut ib, &indices, vertices.len()); + optimize_vertex_cache(&mut ib, indices, vertices.len()); } - optimize_vertex_fetch(&mut vb, &mut ib, &vertices); + optimize_vertex_fetch(&mut vb, &mut ib, vertices); vc.resize_with(vc.capacity(), Default::default); let vc_size = encode_vertex_buffer(&mut vc, &vb, VertexEncodingVersion::V0).unwrap(); @@ -95,7 +97,7 @@ fn bench_codecs(vertices: &[Vertex], indices: &[u32], bestvd: &mut f64, bestid: let vertex_time = (t1 - t0).as_secs_f64(); let index_time = (t2 - t1).as_secs_f64(); - let vertex_throughput = (vertices.len() * std::mem::size_of::()) as f64 / GB / vertex_time; + let vertex_throughput = std::mem::size_of_val(vertices) as f64 / GB / vertex_time; let index_throughput = (indices.len() * 4) as f64 / GB / index_time; if verbose { @@ -197,7 +199,7 @@ fn main() { let mut vertices = Vec::with_capacity(((N + 1) * (N + 1)) as usize); - let verbose = std::env::args().find(|a| a == "-v").is_some(); + let verbose = std::env::args().any(|a| a == "-v"); for x in 0..=N { for y in 0..=N { diff --git a/examples/demo.rs b/examples/demo.rs index 33bee79..838b0de 100644 --- a/examples/demo.rs +++ b/examples/demo.rs @@ -1,3 +1,5 @@ +#![allow(clippy::identity_op)] + use meshopt_rs::index::buffer::*; use meshopt_rs::index::generator::*; use meshopt_rs::index::*; @@ -77,9 +79,9 @@ impl Mesh { let mut vertices = Vec::new(); let mut indices = Vec::new(); - for (_, model) in models.iter().enumerate() { + for model in models.iter() { let mesh = &model.mesh; - assert!(mesh.positions.len() % 3 == 0); + assert!(mesh.positions.len().is_multiple_of(3)); vertices.reserve(mesh.indices.len()); indices.extend_from_slice(&mesh.indices); @@ -135,7 +137,7 @@ impl Mesh { } pub fn is_valid(&self) -> bool { - if self.indices.len() % 3 != 0 { + if !self.indices.len().is_multiple_of(3) { return false; } @@ -200,7 +202,7 @@ impl Mesh { std::slice::from_raw_parts(v.as_ptr() as *const u8, std::mem::size_of::() * v.len()) }; - let hash = Self::hash_range(&data); + let hash = Self::hash_range(data); h1 ^= hash; h2 = h2.wrapping_add(hash); @@ -405,7 +407,7 @@ fn compress_data(data: &[T]) -> usize { ); let mut compressor = CompressorOxide::new(flags); - let src = unsafe { std::slice::from_raw_parts(data.as_ptr() as *const u8, std::mem::size_of::() * data.len()) }; + let src = unsafe { std::slice::from_raw_parts(data.as_ptr() as *const u8, std::mem::size_of_val(data)) }; // Taken from miniz.c as miniz_oxide has no equivalent function let bound = (128 + (src.len() * 110) / 100).max(128 + src.len() + ((src.len() / (31 * 1024)) + 1) * 5); @@ -471,7 +473,7 @@ fn encode_index_sequence1(data: &[u32], vertex_count: usize, desc: char) { let start = Instant::now(); let mut buffer = vec![0; encode_index_sequence_bound(data.len(), vertex_count)]; - let size = encode_index_sequence(&mut buffer, &data, IndexEncodingVersion::default()); + let size = encode_index_sequence(&mut buffer, data, IndexEncodingVersion::default()); buffer.resize(size, 0); let encode = start.elapsed(); @@ -534,7 +536,7 @@ where let start = Instant::now(); let mut vbuf = vec![0; encode_vertex_buffer_bound(pv.len(), std::mem::size_of::())]; - let vb_size = encode_vertex_buffer(&mut vbuf, &pv, VertexEncodingVersion::default()).unwrap(); + let vb_size = encode_vertex_buffer(&mut vbuf, pv, VertexEncodingVersion::default()).unwrap(); vbuf.resize(vb_size, 0); let encode = start.elapsed(); @@ -681,7 +683,7 @@ fn simplify_mesh_complete(mesh: &Mesh) { lods[0] = mesh.indices.clone(); for i in 1..LOD_COUNT { - let (source, mut lod) = { + let (source, lod) = { let (s, l) = lods.split_at_mut(i); (s.last_mut().unwrap(), l.first_mut().unwrap()) }; @@ -698,14 +700,7 @@ fn simplify_mesh_complete(mesh: &Mesh) { } lod.resize(source.len(), Default::default()); - let size = simplify( - &mut lod, - &source, - &mesh.vertices, - target_index_count, - target_error, - None, - ); + let size = simplify(lod, source, &mesh.vertices, target_index_count, target_error, None); lod.resize(size, Default::default()); } @@ -981,7 +976,7 @@ fn meshlets(mesh: &Mesh, scan: bool) { // trivial accept: we can't ever backface cull this meshlet accepted += (bounds.cone_cutoff >= 1.0) as usize; - accepted_s8 += (bounds.cone_cutoff_s8 >= 127) as usize; + accepted_s8 += (bounds.cone_cutoff_s8 == 127) as usize; // perspective projection: dot(normalize(cone_apex - camera_position), cone_axis) > cone_cutoff let mview = [ @@ -1146,7 +1141,7 @@ where for model in models.iter() { let mesh = &model.mesh; - assert!(mesh.positions.len() % 3 == 0); + assert!(mesh.positions.len().is_multiple_of(3)); for i in 0..mesh.indices.len() { let pi = mesh.indices[i] as usize; @@ -1300,7 +1295,7 @@ fn process(mesh: &Mesh) { strip.resize(size, 0); #[cfg(feature = "experimental")] - encode_index_sequence1(&mut strip, copystrip.vertices.len(), 'D'); + encode_index_sequence1(&strip, copystrip.vertices.len(), 'D'); pack_vertex(©); encode_vertex(©); @@ -1320,7 +1315,7 @@ fn process(mesh: &Mesh) { fn process_dev(#[allow(unused)] mesh: &Mesh) { #[cfg(feature = "experimental")] - tessellation_adjacency(&mesh); + tessellation_adjacency(mesh); } fn main() -> Result<(), Box> { diff --git a/src/cluster.rs b/src/cluster.rs index fcd0487..924d9f9 100644 --- a/src/cluster.rs +++ b/src/cluster.rs @@ -214,11 +214,10 @@ fn finish_meshlet(meshlet: &Meshlet, meshlet_triangles: &mut [u8]) { } } +#[allow(clippy::too_many_arguments)] fn append_meshlet( meshlet: &mut Meshlet, - a: u32, - b: u32, - c: u32, + abc: [u32; 3], used: &mut [u8], meshlets: &mut [Meshlet], meshlet_vertices: &mut [u32], @@ -227,6 +226,8 @@ fn append_meshlet( max_vertices: usize, max_triangles: usize, ) -> bool { + let [a, b, c] = abc; + let av = used[a as usize]; let bv = used[b as usize]; let cv = used[c as usize]; @@ -259,21 +260,21 @@ fn append_meshlet( if av == UNUSED { av = meshlet.vertex_count as u8; used[a as usize] = av; - meshlet_vertices[(meshlet.vertex_offset + meshlet.vertex_count) as usize] = a.try_into().unwrap(); + meshlet_vertices[(meshlet.vertex_offset + meshlet.vertex_count) as usize] = a; meshlet.vertex_count += 1; } if bv == UNUSED { bv = meshlet.vertex_count as u8; used[b as usize] = bv; - meshlet_vertices[(meshlet.vertex_offset + meshlet.vertex_count) as usize] = b.try_into().unwrap(); + meshlet_vertices[(meshlet.vertex_offset + meshlet.vertex_count) as usize] = b; meshlet.vertex_count += 1; } if cv == UNUSED { cv = meshlet.vertex_count as u8; used[c as usize] = cv; - meshlet_vertices[(meshlet.vertex_offset + meshlet.vertex_count) as usize] = c.try_into().unwrap(); + meshlet_vertices[(meshlet.vertex_offset + meshlet.vertex_count) as usize] = c; meshlet.vertex_count += 1; } @@ -313,9 +314,7 @@ where let v = points[indices[i] as usize].pos()[axis as usize]; // swap(m, i) unconditionally - let t = indices[m]; - indices[m] = indices[i]; - indices[i] = t; + indices.swap(m, i); // when v >= pivot, we swap i with m without advancing it, preserving invariants m += (v < pivot) as usize; @@ -383,8 +382,10 @@ where // split axis is one where the variance is largest let axis: u32 = if vars[0] >= vars[1] && vars[0] >= vars[2] { 0 + } else if vars[1] >= vars[2] { + 1 } else { - if vars[1] >= vars[2] { 1 } else { 2 } + 2 }; let split = mean[axis as usize]; @@ -471,17 +472,17 @@ fn kd_tree_nearest( /// Returns worst case size requirement for [build_meshlets]. pub fn build_meshlets_bound(index_count: usize, max_vertices: usize, max_triangles: usize) -> usize { - assert!(index_count % 3 == 0); - assert!(max_vertices >= 3 && max_vertices <= MESHLET_MAX_VERTICES); - assert!(max_triangles >= 1 && max_triangles <= MESHLET_MAX_TRIANGLES); - assert!(max_triangles % 4 == 0); // ensures the caller will compute output space properly as index data is 4b aligned + assert!(index_count.is_multiple_of(3)); + assert!((3..=MESHLET_MAX_VERTICES).contains(&max_vertices)); + assert!((1..=MESHLET_MAX_TRIANGLES).contains(&max_triangles)); + assert!(max_triangles.is_multiple_of(4)); // ensures the caller will compute output space properly as index data is 4b aligned // meshlet construction is limited by max vertices and max triangles per meshlet // the worst case is that the input is an unindexed stream since this equally stresses both limits // note that we assume that in the worst case, we leave 2 vertices unpacked in each meshlet - if we have space for 3 we can pack any triangle let max_vertices_conservative = max_vertices - 2; - let meshlet_limit_vertices = (index_count + max_vertices_conservative - 1) / max_vertices_conservative; - let meshlet_limit_triangles = (index_count / 3 + max_triangles - 1) / max_triangles; + let meshlet_limit_vertices = index_count.div_ceil(max_vertices_conservative); + let meshlet_limit_triangles = (index_count / 3).div_ceil(max_triangles); if meshlet_limit_vertices > meshlet_limit_triangles { meshlet_limit_vertices @@ -510,10 +511,10 @@ pub fn build_meshlets_scan( max_vertices: usize, max_triangles: usize, ) -> usize { - assert!(indices.len() % 3 == 0); - assert!(max_vertices >= 3 && max_vertices <= MESHLET_MAX_VERTICES); - assert!(max_triangles >= 1 && max_triangles <= MESHLET_MAX_TRIANGLES); - assert!(max_triangles % 4 == 0); // ensures the caller will compute output space properly as index data is 4b aligned + assert!(indices.len().is_multiple_of(3)); + assert!((3..=MESHLET_MAX_VERTICES).contains(&max_vertices)); + assert!((1..=MESHLET_MAX_TRIANGLES).contains(&max_triangles)); + assert!(max_triangles.is_multiple_of(4)); // ensures the caller will compute output space properly as index data is 4b aligned // index of the vertex in the meshlet, `UNUSED` if the vertex isn't used let mut used = vec![UNUSED; vertex_count]; @@ -521,15 +522,11 @@ pub fn build_meshlets_scan( let mut meshlet = Meshlet::default(); let mut meshlet_offset = 0; - for abc in indices.chunks_exact(3) { - let (a, b, c) = (abc[0], abc[1], abc[2]); - + for abc in indices.as_chunks().0 { // appends triangle to the meshlet and writes previous meshlet to the output if full meshlet_offset += append_meshlet( &mut meshlet, - a, - b, - c, + *abc, &mut used, meshlets, meshlet_vertices, @@ -564,6 +561,7 @@ pub fn build_meshlets_scan( /// * `meshlet_triangles`: must contain enough space for all meshlets, worst case size is equal to `max_meshlets` * `max_triangles * 3` /// * `max_vertices` and `max_triangles`: must not exceed implementation limits (`max_vertices` <= 255 - not 256!, `max_triangles` <= 512) /// * `cone_weight`: should be set to 0 when cone culling is not used, and a value between 0 and 1 otherwise to balance between cluster size and cone culling efficiency +#[allow(clippy::too_many_arguments)] pub fn build_meshlets( meshlets: &mut [Meshlet], meshlet_vertices: &mut [u32], @@ -577,11 +575,11 @@ pub fn build_meshlets( where Vertex: Position, { - assert!(indices.len() % 3 == 0); + assert!(indices.len().is_multiple_of(3)); - assert!(max_vertices >= 3 && max_vertices <= MESHLET_MAX_VERTICES); - assert!(max_triangles >= 1 && max_triangles <= max_triangles); - assert!(max_triangles % 4 == 0); // ensures the caller will compute output space properly as index data is 4b aligned + assert!((3..=MESHLET_MAX_VERTICES).contains(&max_vertices)); + assert!((1..=MESHLET_MAX_TRIANGLES).contains(&max_triangles)); + assert!(max_triangles.is_multiple_of(4)); // ensures the caller will compute output space properly as index data is 4b aligned let mut adjacency = TriangleAdjacency::default(); build_triangle_adjacency(&mut adjacency, indices, vertices.len()); @@ -594,7 +592,7 @@ where // for each triangle, precompute centroid & normal to use for scoring let mut triangles = vec![Cone::default(); face_count]; - let mesh_area = compute_triangle_cones(&mut triangles, indices, &vertices); + let mesh_area = compute_triangle_cones(&mut triangles, indices, vertices); // assuming each meshlet is a square patch, expected radius is sqrt(expected area) let triangle_area_avg = if face_count == 0 { @@ -605,10 +603,7 @@ where let meshlet_expected_radius = (triangle_area_avg * max_triangles as f32).sqrt() * 0.5; // build a kd-tree for nearest neighbor lookup - let mut kdindices = vec![0u32; face_count]; - for i in 0..face_count { - kdindices[i] = i as u32; - } + let mut kdindices: Vec = (0..face_count).map(|c| c as u32).collect(); let mut nodes = vec![KdNode::default(); face_count * 2]; kd_tree_build(0, &mut nodes, &triangles, &mut kdindices, /* leaf_size= */ 8); @@ -626,7 +621,7 @@ where let mut best_extra = 5; let mut best_score = f32::MAX; - let meshlet_cone = get_meshlet_cone(&meshlet_cone_acc, meshlet.triangle_count as u32); + let meshlet_cone = get_meshlet_cone(&meshlet_cone_acc, meshlet.triangle_count); for i in 0..meshlet.vertex_count { let index = meshlet_vertices[meshlet.vertex_offset as usize + i as usize] as usize; @@ -635,8 +630,8 @@ where let neighbouts_offset = adjacency.offsets[index] as usize; let neighbours = &adjacency.data[neighbouts_offset..neighbouts_offset + neighbours_size]; - for j in 0..neighbours_size { - let triangle = neighbours[j] as usize; + for triangle in neighbours { + let triangle = *triangle as usize; assert!(!emitted_flags[triangle]); let a = indices[triangle * 3 + 0] as usize; @@ -695,15 +690,12 @@ where } if let Some(best_triangle) = best_triangle { - let abc = &indices[best_triangle * 3..best_triangle * 3 + 3]; - let (a, b, c) = (abc[0], abc[1], abc[2]); + let abc: [u32; 3] = indices[best_triangle * 3..best_triangle * 3 + 3].try_into().unwrap(); // add meshlet to the output; when the current meshlet is full we reset the accumulated bounds if append_meshlet( &mut meshlet, - a, - b, - c, + abc, &mut used, meshlets, meshlet_vertices, @@ -716,6 +708,8 @@ where meshlet_cone_acc = Cone::default(); } + let [a, b, c] = abc; + live_triangles[a as usize] -= 1; live_triangles[b as usize] -= 1; live_triangles[c as usize] -= 1; @@ -723,7 +717,7 @@ where // remove emitted triangle from adjacency data // this makes sure that we spend less time traversing these lists on subsequent iterations for index in abc { - let index = *index as usize; + let index = index as usize; let neighbours_offset = adjacency.offsets[index] as usize; let neighbours_size = adjacency.counts[index] as usize; @@ -797,7 +791,7 @@ pub fn compute_cluster_bounds(indices: &[u32], vertices: &[Vertex]) -> B where Vertex: Position, { - assert!(indices.len() % 3 == 0); + assert!(indices.len().is_multiple_of(3)); assert!(indices.len() / 3 <= MESHLET_MAX_TRIANGLES); // compute triangle normals and gather triangle corners diff --git a/src/index/buffer.rs b/src/index/buffer.rs index ce7e653..a2566e8 100644 --- a/src/index/buffer.rs +++ b/src/index/buffer.rs @@ -24,7 +24,13 @@ const CODE_AUX_ENCODING_TABLE: [u8; 16] = [ ]; fn rotate_triangle(b: u32, c: u32, next: u32) -> i32 { - if b == next { 1 } else { if c == next { 2 } else { 0 } } + if b == next { + 1 + } else if c == next { + 2 + } else { + 0 + } } fn get_edge_fifo(fifo: &EdgeFifo, a: u32, b: u32, c: u32, offset: usize) -> i32 { @@ -116,7 +122,7 @@ where /// /// * `buffer`: must contain enough space for the encoded index buffer (use [encode_index_buffer_bound] to compute worst case size) pub fn encode_index_buffer(mut buffer: &mut [u8], indices: &[u32], version: IndexEncodingVersion) -> Option { - assert!(indices.len() % 3 == 0); + assert!(indices.len().is_multiple_of(3)); let buffer_len = buffer.len(); @@ -175,13 +181,11 @@ pub fn encode_index_buffer(mut buffer: &mut [u8], indices: &[u32], version: Inde let mut fec = if fc >= 1 && fc < fecmax { fc + } else if c == next { + next += 1; + 0 } else { - if c == next { - next += 1; - 0 - } else { - 15 - } + 15 }; if fec == 15 && version >= 1 { @@ -242,25 +246,21 @@ pub fn encode_index_buffer(mut buffer: &mut [u8], indices: &[u32], version: Inde } else { 15 }; - let feb = if fb >= 0 && fb < 14 { + let feb = if (0..14).contains(&fb) { fb + 1 + } else if b == next { + next += 1; + 0 } else { - if b == next { - next += 1; - 0 - } else { - 15 - } + 15 }; - let fec = if fc >= 0 && fc < 14 { + let fec = if (0..14).contains(&fc) { fc + 1 + } else if c == next { + next += 1; + 0 } else { - if c == next { - next += 1; - 0 - } else { - 15 - } + 15 }; // we encode feb & fec in 4 bits using a table if possible, and as a full byte otherwise @@ -268,7 +268,7 @@ pub fn encode_index_buffer(mut buffer: &mut [u8], indices: &[u32], version: Inde let codeauxindex = get_code_aux_index(codeaux, &codeaux_table); // <14 encodes an index into codeaux table, 14 encodes fea=0, 15 encodes fea=15 - if fea == 0 && codeauxindex >= 0 && codeauxindex < 14 && !reset { + if fea == 0 && (0..14).contains(&codeauxindex) && !reset { write_byte(&mut code, ((15 << 4) | codeauxindex) as u8); } else { write_byte(&mut code, ((15 << 4) | 14 | fea) as u8); @@ -337,17 +337,17 @@ pub fn encode_index_buffer(mut buffer: &mut [u8], indices: &[u32], version: Inde /// Returns worst case size requirement for [encode_index_buffer]. pub fn encode_index_buffer_bound(index_count: usize, vertex_count: usize) -> usize { - assert_eq!(index_count % 3, 0); + assert!(index_count.is_multiple_of(3)); // compute number of bits required for each index - let mut vertex_bits = 1; + let mut vertex_bits: usize = 1; while vertex_bits < 32 && vertex_count > (1 << vertex_bits) { vertex_bits += 1; } // worst-case encoding is 2 header bytes + 3 varint-7 encoded index deltas - let vertex_groups = (vertex_bits + 1 + 6) / 7; + let vertex_groups = (vertex_bits + 1).div_ceil(7); 1 + (index_count / 3) * (2 + 3 * vertex_groups) + 16 } @@ -626,7 +626,7 @@ mod test { impl From for U16 { fn from(index: u32) -> Self { - Self { 0: index as u16 } + Self(index as u16) } } diff --git a/src/index/generator.rs b/src/index/generator.rs index ffcdef5..cede2cd 100644 --- a/src/index/generator.rs +++ b/src/index/generator.rs @@ -124,7 +124,7 @@ where if destination[index] == INVALID_INDEX { match table.entry(lookup(index)) { Entry::Occupied(entry) => { - let value = *entry.get() as usize; + let value = *entry.get(); assert!(destination[value] != INVALID_INDEX); destination[index] = destination[value]; } @@ -200,7 +200,7 @@ pub fn generate_vertex_remap_multi(destination: &mut [u32], indices: Option<&[u3 generate_vertex_remap_inner(destination, indices, vertex_count, |index| StreamVertex { streams, - index: index as usize, + index, }) } @@ -294,7 +294,7 @@ pub fn generate_shadow_index_buffer_multi(destination: &mut [u32], indices: &[u3 generate_shadow_index_buffer_inner(destination, indices, vertex_count, |index| StreamVertex { streams, - index: index as usize, + index, }) } @@ -324,6 +324,7 @@ fn build_position_remap(indices: &[u32], vertices: &Stream) -> Vec { /// - 5, 6: opposing edge for edge 1, 2 /// - 7, 8: opposing edge for edge 2, 0 /// - 9, 10, 11: dominant vertices for corners 0, 1, 2 +/// /// The resulting patch can be rendered with hardware tessellation using PN-AEN and displacement mapping. /// See "Tessellation on Any Budget" (John McDonald, GDC 2011) for implementation details. /// @@ -392,7 +393,8 @@ pub fn generate_tessellation_index_buffer(destination: &mut [u32], indices: &[u3 /// Each triangle is converted into a 6-vertex patch with the following layout: /// - 0, 2, 4: original triangle vertices /// - 1, 3, 5: vertices adjacent to edges 02, 24 and 40 -/// The resulting patch can be rendered with geometry shaders using e.g. `VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY``. +/// +/// The resulting patch can be rendered with geometry shaders using e.g. `VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY`. /// This can be used to implement algorithms like silhouette detection/expansion and other forms of GS-driven rendering. /// /// # Arguments @@ -420,12 +422,9 @@ pub fn generate_adjacency_index_buffer(destination: &mut [u32], indices: &[u32], let i2 = i[NEXT[e + 1]]; assert!((i0 as usize) < vertices.len() && (i1 as usize) < vertices.len() && (i2 as usize) < vertices.len()); - match edge_vertex_table.entry(Edge((remap[i0 as usize], remap[i1 as usize]))) { - Entry::Vacant(entry) => { - // store vertex opposite to the edge - entry.insert(i2); - } - _ => {} + if let Entry::Vacant(entry) = edge_vertex_table.entry(Edge((remap[i0 as usize], remap[i1 as usize]))) { + // store vertex opposite to the edge + entry.insert(i2); } } } diff --git a/src/index/mod.rs b/src/index/mod.rs index 90b3d28..7eec840 100644 --- a/src/index/mod.rs +++ b/src/index/mod.rs @@ -23,6 +23,7 @@ pub enum IndexEncodingVersion { V1, } +#[allow(clippy::derivable_impls)] impl Default for IndexEncodingVersion { fn default() -> Self { #[cfg(not(feature = "experimental"))] @@ -36,12 +37,12 @@ impl Default for IndexEncodingVersion { } } -impl Into for IndexEncodingVersion { - fn into(self) -> u8 { - match self { - Self::V0 => 0, +impl From for u8 { + fn from(value: IndexEncodingVersion) -> u8 { + match value { + IndexEncodingVersion::V0 => 0, #[cfg(feature = "experimental")] - Self::V1 => 1, + IndexEncodingVersion::V1 => 1, } } } diff --git a/src/index/sequence.rs b/src/index/sequence.rs index 53ce98f..c7a11d7 100644 --- a/src/index/sequence.rs +++ b/src/index/sequence.rs @@ -26,7 +26,7 @@ pub fn encode_index_sequence(buffer: &mut [u8], indices: &[u32], version: IndexE let version: u8 = version.into(); let mut data = buffer; - write_byte(&mut data, SEQUENCE_HEADER | (version as u8)); + write_byte(&mut data, SEQUENCE_HEADER | version); let mut last: [u32; 2] = [0; 2]; let mut current = 0; @@ -61,7 +61,7 @@ pub fn encode_index_sequence(buffer: &mut [u8], indices: &[u32], version: IndexE return 0; } - data.write(&[0; 4]).unwrap(); + data.write_all(&[0; 4]).unwrap(); buffer_len - data.len() } @@ -69,16 +69,16 @@ pub fn encode_index_sequence(buffer: &mut [u8], indices: &[u32], version: IndexE /// Returns worst case size requirement for [encode_index_sequence]. pub fn encode_index_sequence_bound(index_count: usize, vertex_count: usize) -> usize { // compute number of bits required for each index - let mut vertex_bits = 1; + let mut vertex_bits: usize = 1; while vertex_bits < 32 && vertex_count > (1 << vertex_bits) { vertex_bits += 1; } // worst-case encoding is 1 varint-7 encoded index delta for a K bit value and an extra bit - let vertex_groups = (vertex_bits + 1 + 1 + 6) / 7; + let vertex_groups = (vertex_bits + 1 + 1).div_ceil(7); - return 1 + index_count * vertex_groups + 4; + 1 + index_count * vertex_groups + 4 } /// Decodes index data from an array of bytes generated by [encode_index_sequence]. @@ -113,7 +113,7 @@ where let mut last: [u32; 2] = Default::default(); - for i in 0..destination.len() { + for i in destination { let mut v = decode_v_byte(&mut data); // decode the index of the last baseline @@ -127,7 +127,7 @@ where // update last for the next iteration that uses it last[current] = index; - destination[i] = T::from(index); + *i = T::from(index); } if data.len() == 4 { @@ -164,7 +164,7 @@ mod test { impl From for U16 { fn from(index: u32) -> Self { - Self { 0: index as u16 } + Self(index as u16) } } diff --git a/src/lib.rs b/src/lib.rs index 2eac9cb..d553441 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,6 +4,9 @@ //! //! * `experimental`: Enables experimental APIs which have unstable interface and might have implementation that's not fully tested or optimized +#![allow(clippy::identity_op)] +#![allow(clippy::erasing_op)] + #[cfg(feature = "experimental")] pub mod cluster; pub mod index; @@ -114,6 +117,10 @@ impl<'a> Stream<'a> { pub fn len(&self) -> usize { self.data.len() / self.stride } + + pub fn is_empty(&self) -> bool { + self.data.is_empty() + } } #[derive(Clone, Copy, Default, Debug)] diff --git a/src/overdraw.rs b/src/overdraw.rs index 25b65f3..b10e521 100644 --- a/src/overdraw.rs +++ b/src/overdraw.rs @@ -115,11 +115,9 @@ fn rasterize(buffer: &mut OverdrawBuffer, mut v1: Vector3, mut v2: Vector3, mut let sign = sign as usize; // check if all CXn are non-negative - if cx1 | cx2 | cx3 >= 0 { - if zx >= buffer.z[y][x][sign] { - buffer.z[y][x][sign] = zx; - buffer.overdraw[y][x][sign] += 1; - } + if cx1 | cx2 | cx3 >= 0 && zx >= buffer.z[y][x][sign] { + buffer.z[y][x][sign] = zx; + buffer.overdraw[y][x][sign] += 1; } // signed left shift is UB for negative numbers so use unsigned-signed casts @@ -146,7 +144,7 @@ pub fn analyze_overdraw(indices: &[u32], vertices: &[Vertex]) -> Overdra where Vertex: Position, { - assert!(indices.len() % 3 == 0); + assert!(indices.len().is_multiple_of(3)); let mut result = OverdrawStatistics::default(); @@ -329,9 +327,9 @@ fn calculate_sort_order_radix(sort_order: &mut [u32], sort_data: &[f32], sort_ke // compute offsets based on histogram data let mut histogram_sum = 0; - for i in 0..histogram.len() { - let count = histogram[i]; - histogram[i] = histogram_sum; + for h in &mut histogram { + let count = *h; + *h = histogram_sum; histogram_sum += count; } @@ -340,7 +338,7 @@ fn calculate_sort_order_radix(sort_order: &mut [u32], sort_data: &[f32], sort_ke // compute sort order based on offsets for i in 0..sort_keys.len() { let idx = &mut histogram[sort_keys[i] as usize]; - sort_order[*idx as usize] = i as u32; + sort_order[*idx] = i as u32; *idx += 1; } } @@ -376,7 +374,7 @@ fn generate_hard_boundaries( cache_size: u32, cache_timestamps: &mut [u32], ) -> usize { - let mut timestamp = cache_size as u32 + 1; + let mut timestamp = cache_size + 1; let face_count = indices.len() / 3; @@ -553,7 +551,7 @@ where // sort clusters using sort data let mut sort_keys = vec![0u16; clusters.len()]; let mut sort_order = vec![0u32; clusters.len()]; - calculate_sort_order_radix(&mut sort_order, &mut sort_data, &mut sort_keys); + calculate_sort_order_radix(&mut sort_order, &sort_data, &mut sort_keys); // fill output buffer let mut offset = 0; diff --git a/src/simplify.rs b/src/simplify.rs index f66692b..ed51aa6 100644 --- a/src/simplify.rs +++ b/src/simplify.rs @@ -274,6 +274,7 @@ fn classify_vertices( let openinc = loopback; let openout = loop_; + #[allow(clippy::needless_range_loop)] for vertex in 0..vertex_count { let offset = adjacency.offsets[vertex] as usize; let count = adjacency.counts[vertex] as usize; @@ -818,15 +819,15 @@ fn dump_edge_collapses(collapses: &[Collapse], vertex_kind: &[VertexKind]) { let mut ckinds = [[0usize; KIND_COUNT]; KIND_COUNT]; let mut cerrors = [[f32::MAX; KIND_COUNT]; KIND_COUNT]; - for i in 0..collapses.len() { - let i0 = collapses[i].v0; - let i1 = collapses[i].v1; + for c in collapses { + let i0 = c.v0; + let i1 = c.v1; let k0 = vertex_kind[i0 as usize] as usize; let k1 = vertex_kind[i1 as usize] as usize; ckinds[k0][k1] += 1; - cerrors[k0][k1] = cerrors[k0][k1].min(unsafe { collapses[i].u.error }); + cerrors[k0][k1] = cerrors[k0][k1].min(unsafe { c.u.error }); } for k0 in 0..KIND_COUNT { @@ -864,6 +865,7 @@ fn dump_locked_collapses(indices: &[u32], vertex_kind: &[VertexKind]) { } } + #[allow(clippy::needless_range_loop)] for k0 in 0..KIND_COUNT { for k1 in 0..KIND_COUNT { if locked_collapses[k0][k1] != 0 { @@ -889,9 +891,9 @@ fn sort_edge_collapses(sort_order: &mut [u32], collapses: &[Collapse]) { // compute offsets based on histogram data let mut histogram_sum = 0; - for i in 0..(1 << SORT_BITS) { - let count = histogram[i]; - histogram[i] = histogram_sum; + for h in &mut histogram { + let count = *h; + *h = histogram_sum; histogram_sum += count; } @@ -907,6 +909,7 @@ fn sort_edge_collapses(sort_order: &mut [u32], collapses: &[Collapse]) { } } +#[allow(clippy::too_many_arguments)] fn perform_edge_collapses( collapse_remap: &mut [u32], collapse_locked: &mut [bool], @@ -945,7 +948,7 @@ fn perform_edge_collapses( // we limit the error in each pass based on the error of optimal last collapse; since many collapses will be locked // as they will share vertices with other successfull collapses, we need to increase the acceptable error by some factor let error_goal = if edge_collapse_goal < collapses.len() { - 1.5 * unsafe { collapses[collapse_order[edge_collapse_goal as usize] as usize].u.error } + 1.5 * unsafe { collapses[collapse_order[edge_collapse_goal] as usize].u.error } } else { f32::MAX }; @@ -1061,7 +1064,7 @@ fn remap_edge_loops(loop_: &mut [u32], collapse_remap: &[u32]) { } fn compute_vertex_ids(vertex_ids: &mut [u32], vertex_positions: &[Vector3], grid_size: i32) { - assert!(grid_size >= 1 && grid_size <= 1024); + assert!((1..=1024).contains(&grid_size)); let cell_scale = (grid_size - 1) as f32; for (pos, id) in vertex_positions.iter().zip(vertex_ids.iter_mut()) { @@ -1120,7 +1123,7 @@ fn count_vertex_cells(table: &mut HashMap, vertex 0 } Entry::Vacant(entry) => { - entry.insert(*id as u32); + entry.insert(*id); 1 } }; @@ -1218,7 +1221,11 @@ fn filter_triangles( destination[result * 3..result * 3 + 3].copy_from_slice(&abc); - let p = hash::VertexPosition(unsafe { std::mem::transmute(abc) }); + let p = hash::VertexPosition([ + f32::from_bits(abc[0]), + f32::from_bits(abc[1]), + f32::from_bits(abc[2]), + ]); match tritable.entry(p) { Entry::Occupied(_entry) => {} @@ -1297,8 +1304,8 @@ where #[cfg(feature = "trace")] { let mut unique_positions = 0; - for i in 0..vertices.len() { - unique_positions += (remap[i] as usize == i) as u32; + for (i, remapped) in remap.iter().enumerate().take(vertices.len()) { + unique_positions += (*remapped as usize == i) as u32; } println!( @@ -1433,7 +1440,7 @@ where ); #[cfg(feature = "trace")] - dump_locked_collapses(&result, &vertex_kind); + dump_locked_collapses(result, &vertex_kind); // result_error is quadratic; we need to remap it back to linear if let Some(result_error) = result_error { @@ -1514,20 +1521,22 @@ where let mut grid_size = next_grid_size; grid_size = if grid_size <= min_grid { min_grid + 1 + } else if grid_size >= max_grid { + max_grid - 1 } else { - if grid_size >= max_grid { max_grid - 1 } else { grid_size } + grid_size }; compute_vertex_ids(&mut vertex_ids, &vertex_positions, grid_size); - let triangles = count_triangles(&vertex_ids, &indices); + let triangles = count_triangles(&vertex_ids, indices); #[cfg(feature = "trace")] println!( "pass {pass} ({}): grid size {grid_size}, triangles {triangles}, {}", - if pass == 0 { - "guess" - } else { - if pass <= INTERPOLATION_PASSES { "lerp" } else { "binary" } + match pass { + 0 => "guess", + 1..=INTERPOLATION_PASSES => "lerp", + _ => "binary", }, if triangles <= target_index_count / 3 { "under" @@ -1582,7 +1591,7 @@ where // build a quadric for each target cell let mut cell_quadrics = vec![Quadric::default(); cell_count]; - fill_cell_quadrics(&mut cell_quadrics, &indices, &vertex_positions, &vertex_cells); + fill_cell_quadrics(&mut cell_quadrics, indices, &vertex_positions, &vertex_cells); // for each target cell, find the vertex with the minimal error let mut cell_remap = vec![INVALID_INDEX; cell_count]; @@ -1600,7 +1609,7 @@ where // note that we need to filter out triangles that we've already output because we very frequently generate redundant triangles between cells :( let mut tritable = HashMap::with_capacity_and_hasher(min_triangles, hash::BuildPositionHasher::default()); - let write = filter_triangles(destination, &mut tritable, &indices, &vertex_cells, &cell_remap); + let write = filter_triangles(destination, &mut tritable, indices, &vertex_cells, &cell_remap); assert!(write <= target_index_count); #[cfg(feature = "trace")] @@ -1670,8 +1679,10 @@ where let mut grid_size = next_grid_size; grid_size = if grid_size <= min_grid { min_grid + 1 + } else if grid_size >= max_grid { + max_grid - 1 } else { - if grid_size >= max_grid { max_grid - 1 } else { grid_size } + grid_size }; compute_vertex_ids(&mut vertex_ids, &vertex_positions, grid_size); @@ -1679,13 +1690,13 @@ where #[cfg(feature = "trace")] println!( - "pass {pass} ({}): grid size {grid_size}, vertices {vertices}, {}", - if pass == 0 { - "guess" - } else { - if pass <= INTERPOLATION_PASSES { "lerp" } else { "binary" } + "pass {pass} ({}): grid size {grid_size}, triangles {vertices}, {}", + match pass { + 0 => "guess", + 1..=INTERPOLATION_PASSES => "lerp", + _ => "binary", }, - if vertices <= target_vertex_count { + if vertices <= target_vertex_count / 3 { "under" } else { "over" diff --git a/src/spatial_order.rs b/src/spatial_order.rs index cbb0ba8..8e9b649 100644 --- a/src/spatial_order.rs +++ b/src/spatial_order.rs @@ -118,7 +118,7 @@ pub fn spatial_sort_triangles(destination: &mut [u32], indices: &[u32], where Vertex: Position, { - assert!(indices.len() % 3 == 0); + assert!(indices.len().is_multiple_of(3)); let face_count = indices.len() / 3; diff --git a/src/stripify.rs b/src/stripify.rs index 4a159a1..f0df109 100644 --- a/src/stripify.rs +++ b/src/stripify.rs @@ -47,7 +47,7 @@ fn find_strip_next(buffer: &[[u32; 3]], e: (u32, u32)) -> i32 { /// * `destination`: must contain enough space for the target index buffer, worst case can be computed with [stripify_bound] /// * `restart_index`: should be `INVALID_INDEX` or `0` to use degenerate triangles pub fn stripify(destination: &mut [u32], indices: &[u32], vertex_count: usize, restart_index: u32) -> usize { - assert!(indices.len() % 3 == 0); + assert!(indices.len().is_multiple_of(3)); const BUFFER_CAPACITY: usize = 8; @@ -227,7 +227,7 @@ pub fn stripify(destination: &mut [u32], indices: &[u32], vertex_count: usize, r /// Returns worst case size requirement for [stripify]. pub fn stripify_bound(index_count: usize) -> usize { - assert!(index_count % 3 == 0); + assert!(index_count.is_multiple_of(3)); // worst case without restarts is 2 degenerate indices and 3 indices per triangle // worst case with restarts is 1 restart index and 3 indices per triangle diff --git a/src/util.rs b/src/util.rs index 1bc2e44..1c0d6b4 100644 --- a/src/util.rs +++ b/src/util.rs @@ -8,16 +8,16 @@ pub(crate) fn zero_inverse(value: f32) -> f32 { } pub(crate) fn as_bytes(data: &[T]) -> &[u8] { - unsafe { std::slice::from_raw_parts(data.as_ptr() as *const u8, data.len() * std::mem::size_of::()) } + unsafe { std::slice::from_raw_parts(data.as_ptr() as *const u8, std::mem::size_of_val(data)) } } pub(crate) fn as_mut_bytes(data: &mut [T]) -> &mut [u8] { - unsafe { std::slice::from_raw_parts_mut(data.as_mut_ptr() as *mut u8, data.len() * std::mem::size_of::()) } + unsafe { std::slice::from_raw_parts_mut(data.as_mut_ptr() as *mut u8, std::mem::size_of_val(data)) } } pub(crate) fn read_byte(data: &mut R) -> u8 { let mut byte = [0]; - data.read(&mut byte).unwrap(); // `read` always succeeds on slices + data.read_exact(&mut byte).unwrap(); byte[0] } diff --git a/src/vertex/buffer.rs b/src/vertex/buffer.rs index d76450b..78dfb2e 100644 --- a/src/vertex/buffer.rs +++ b/src/vertex/buffer.rs @@ -38,7 +38,7 @@ fn encode_bytes_group_zero(buffer: &[u8]) -> bool { } fn encode_bytes_group_measure(buffer: &[u8], bits: usize) -> usize { - assert!(bits >= 1 && bits <= 8); + assert!((1..=8).contains(&bits)); match bits { 1 => { @@ -65,14 +65,14 @@ fn encode_bytes_group_measure(buffer: &[u8], bits: usize) -> usize { } fn encode_bytes_group(data: &mut W, buffer: &[u8], bits: usize) -> Option { - assert!(bits >= 1 && bits <= 8); + assert!((1..=8).contains(&bits)); match bits { 1 => Some(0), 8 => write_exact(data, &buffer[0..BYTE_GROUP_SIZE]), _ => { let byte_size = 8 / bits; - assert!(BYTE_GROUP_SIZE % byte_size == 0); + assert!(BYTE_GROUP_SIZE.is_multiple_of(byte_size)); // fixed portion: bits bits for each value // variable portion: full byte for each out-of-range value (using 1...1 as sentinel) @@ -97,9 +97,9 @@ fn encode_bytes_group(data: &mut W, buffer: &[u8], bits: usize) -> Opt written += write_byte(data, byte); } - for i in 0..BYTE_GROUP_SIZE { - if buffer[i] >= sentinel { - written += write_byte(data, buffer[i]); + for b in buffer.iter().take(BYTE_GROUP_SIZE) { + if *b >= sentinel { + written += write_byte(data, *b); } } @@ -109,10 +109,10 @@ fn encode_bytes_group(data: &mut W, buffer: &[u8], bits: usize) -> Opt } fn encode_bytes(data: &mut [u8], buffer: &[u8]) -> Option { - assert!(buffer.len() % BYTE_GROUP_SIZE == 0); + assert!(buffer.len().is_multiple_of(BYTE_GROUP_SIZE)); // round number of groups to 4 to get number of header bytes - let header_size = (buffer.len() / BYTE_GROUP_SIZE + 3) / 4; + let header_size = (buffer.len() / BYTE_GROUP_SIZE).div_ceil(4); if data.len() < header_size { return None; @@ -175,16 +175,15 @@ fn encode_vertex_block( // we sometimes encode elements we didn't fill when rounding to BYTE_GROUP_SIZE let mut buffer = [0u8; VERTEX_BLOCK_MAX_SIZE]; - assert!(VERTEX_BLOCK_MAX_SIZE % BYTE_GROUP_SIZE == 0); + assert!(VERTEX_BLOCK_MAX_SIZE.is_multiple_of(BYTE_GROUP_SIZE)); let mut written_sum = 0; - for k in 0..vertex_size { + for (k, mut p) in last_vertex.iter().copied().enumerate().take(vertex_size) { let mut vertex_offset = k; - let mut p = last_vertex[k]; - for i in 0..vertex_count { - buffer[i] = zigzag8(vertex_data[vertex_offset].wrapping_sub(p)); + for b in buffer.iter_mut().take(vertex_count) { + *b = zigzag8(vertex_data[vertex_offset].wrapping_sub(p)); p = vertex_data[vertex_offset]; @@ -233,7 +232,7 @@ where match bitslog2 { 0 => { - buffer.write(&[0; BYTE_GROUP_SIZE]).unwrap(); + buffer.write_all(&[0; BYTE_GROUP_SIZE]).unwrap(); } 1 => { let mut data_var_pos = data_pos + 4; @@ -262,18 +261,18 @@ where data.set_position(data_var_pos); } 3 => { - data.read(&mut buf).unwrap(); - buffer.write(&buf).unwrap(); + data.read_exact(&mut buf).unwrap(); + buffer.write_all(&buf).unwrap(); } _ => unreachable!("Unexpected bit length"), // unreachable since bitslog2 is a 2-bit value } } fn decode_bytes(data: &mut Cursor<&[u8]>, buffer: &mut [u8]) -> Result<(), DecodeError> { - assert!(buffer.len() % BYTE_GROUP_SIZE == 0); + assert!(buffer.len().is_multiple_of(BYTE_GROUP_SIZE)); // round number of groups to 4 to get number of header bytes - let header_size = (buffer.len() / BYTE_GROUP_SIZE + 3) / 4; + let header_size = (buffer.len() / BYTE_GROUP_SIZE).div_ceil(4); let raw_data = &data.get_ref()[data.position() as usize..]; @@ -318,15 +317,13 @@ fn decode_vertex_block( let vertex_count_aligned = (vertex_count + BYTE_GROUP_SIZE - 1) & !(BYTE_GROUP_SIZE - 1); - for k in 0..vertex_size { + for (k, mut p) in last_vertex.iter().copied().enumerate().take(vertex_size) { decode_bytes(data, &mut buffer[0..vertex_count_aligned])?; let mut vertex_offset = k; - let mut p = last_vertex[k]; - - for i in 0..vertex_count { - let v = unzigzag8(buffer[i]).wrapping_add(p); + for b in buffer.iter().take(vertex_count) { + let v = unzigzag8(*b).wrapping_add(p); transposed[vertex_offset] = v; p = v; @@ -362,7 +359,7 @@ pub fn encode_vertex_buffer( let vertex_size = std::mem::size_of::(); assert!(vertex_size > 0 && vertex_size <= 256); - assert!(vertex_size % 4 == 0); + assert!(vertex_size.is_multiple_of(4)); let vertex_data = as_bytes(vertices); @@ -375,7 +372,7 @@ pub fn encode_vertex_buffer( let version: u8 = version.into(); - let mut written_sum = write_byte(&mut data, (VERTEX_HEADER | version) as u8); + let mut written_sum = write_byte(&mut data, VERTEX_HEADER | version); let mut first_vertex = [0; 256]; if !vertices.is_empty() { @@ -397,7 +394,7 @@ pub fn encode_vertex_buffer( }; let written = encode_vertex_block( - &mut data, + data, &vertex_data[vertex_offset * vertex_size..], block_size, vertex_size, @@ -435,12 +432,12 @@ pub fn encode_vertex_buffer( /// Returns worst case size requirement for [encode_vertex_buffer]. pub fn encode_vertex_buffer_bound(vertex_count: usize, vertex_size: usize) -> usize { assert!(vertex_size > 0 && vertex_size <= 256); - assert!(vertex_size % 4 == 0); + assert!(vertex_size.is_multiple_of(4)); let vertex_block_size = get_vertex_block_size(vertex_size); - let vertex_block_count = (vertex_count + vertex_block_size - 1) / vertex_block_size; + let vertex_block_count = vertex_count.div_ceil(vertex_block_size); - let vertex_block_header_size = (vertex_block_size / BYTE_GROUP_SIZE + 3) / 4; + let vertex_block_header_size = (vertex_block_size / BYTE_GROUP_SIZE).div_ceil(4); let vertex_block_data_size = vertex_block_size; let tail_size = vertex_size.max(TAIL_MAX_SIZE); @@ -462,7 +459,7 @@ pub fn decode_vertex_buffer(destination: &mut [Vertex], buffer: &[u8]) - let vertex_count = destination.len(); assert!(vertex_size > 0 && vertex_size <= 256); - assert!(vertex_size % 4 == 0); + assert!(vertex_size.is_multiple_of(4)); let vertex_data = as_mut_bytes(destination); diff --git a/src/vertex/cache.rs b/src/vertex/cache.rs index da20b1d..2040169 100644 --- a/src/vertex/cache.rs +++ b/src/vertex/cache.rs @@ -29,7 +29,7 @@ pub fn analyze_vertex_cache( warp_size: usize, primgroup_size: usize, ) -> VertexCacheStatistics { - assert!(indices.len() % 3 == 0); + assert!(indices.len().is_multiple_of(3)); assert!(cache_size >= 3); assert!(warp_size == 0 || warp_size >= 3); @@ -81,7 +81,7 @@ pub fn analyze_vertex_cache( result.warps_executed += (warp_offset > 0) as u32; - result.acmr = if indices.len() == 0 { + result.acmr = if indices.is_empty() { 0.0 } else { result.vertices_transformed as f32 / (indices.len() as f32 / 3.0) @@ -92,7 +92,7 @@ pub fn analyze_vertex_cache( result.vertices_transformed as f32 / unique_vertex_count as f32 }; - return result; + result } const CACHE_SIZE_MAX: usize = 16; @@ -212,10 +212,10 @@ fn optimize_vertex_cache_table( vertex_count: usize, table: &VertexScoreTable, ) { - assert!(indices.len() % 3 == 0); + assert!(indices.len().is_multiple_of(3)); // guard for empty meshes - if indices.len() == 0 || vertex_count == 0 { + if indices.is_empty() || vertex_count == 0 { return; } @@ -320,9 +320,7 @@ fn optimize_vertex_cache_table( let mut best_score = 0.0; // update cache positions, vertex scores and triangle scores, and find next best triangle - for i in 0..cache_write { - let index = cache[i] as usize; - + for (i, index) in cache.iter().map(|index| *index as usize).enumerate().take(cache_write) { let cache_position = if i >= cache_size { -1 } else { i as i32 }; // update vertex score @@ -395,11 +393,11 @@ pub fn optimize_vertex_cache_strip(destination: &mut [u32], indices: &[u32], ver /// * `destination`: must contain enough space for the resulting index buffer (`indices.len()` elements) /// * `cache_size`: should be less than the actual GPU cache size to avoid cache thrashing pub fn optimize_vertex_cache_fifo(destination: &mut [u32], indices: &[u32], vertex_count: usize, cache_size: u32) { - assert!(indices.len() % 3 == 0); + assert!(indices.len().is_multiple_of(3)); assert!(cache_size >= 3); // guard for empty meshes - if indices.len() == 0 || vertex_count == 0 { + if indices.is_empty() || vertex_count == 0 { return; } @@ -424,7 +422,7 @@ pub fn optimize_vertex_cache_fifo(destination: &mut [u32], indices: &[u32], vert let mut current_vertex = 0; - let mut timestamp = cache_size as u32 + 1; + let mut timestamp = cache_size + 1; let mut input_cursor = 1; // vertex to restart from in case of dead-end let mut output_triangle = 0; @@ -444,11 +442,11 @@ pub fn optimize_vertex_cache_fifo(destination: &mut [u32], indices: &[u32], vert let abc = &indices[triangle * 3..triangle * 3 + 3]; // output indices - destination[output_triangle * 3..output_triangle * 3 + 3].copy_from_slice(&abc); + destination[output_triangle * 3..output_triangle * 3 + 3].copy_from_slice(abc); output_triangle += 1; // update dead-end stack - dead_end[dead_end_top..dead_end_top + 3].copy_from_slice(&abc); + dead_end[dead_end_top..dead_end_top + 3].copy_from_slice(abc); dead_end_top += 3; for i in abc { diff --git a/src/vertex/fetch.rs b/src/vertex/fetch.rs index f78f651..2f4e14f 100644 --- a/src/vertex/fetch.rs +++ b/src/vertex/fetch.rs @@ -17,7 +17,7 @@ pub struct VertexFetchStatistics { /// /// Results may not match actual GPU performance. pub fn analyze_vertex_fetch(indices: &[u32], vertex_count: usize, vertex_size: usize) -> VertexFetchStatistics { - assert!(indices.len() % 3 == 0); + assert!(indices.len().is_multiple_of(3)); assert!(vertex_size > 0 && vertex_size <= 256); let mut result = VertexFetchStatistics::default(); @@ -41,7 +41,7 @@ pub fn analyze_vertex_fetch(indices: &[u32], vertex_count: usize, vertex_size: u let end_address = start_address + vertex_size; let start_tag = start_address / CACHE_LINE; - let end_tag = (end_address + CACHE_LINE - 1) / CACHE_LINE; + let end_tag = end_address.div_ceil(CACHE_LINE); assert!(start_tag < end_tag); @@ -74,7 +74,7 @@ pub fn analyze_vertex_fetch(indices: &[u32], vertex_count: usize, vertex_size: u /// /// * `destination`: must contain the exact space for the resulting remap table (`vertex_count` elements) pub fn optimize_vertex_fetch_remap(destination: &mut [u32], indices: &[u32]) -> usize { - assert!(indices.len() % 3 == 0); + assert!(indices.len().is_multiple_of(3)); destination[..].fill(INVALID_INDEX); @@ -106,7 +106,7 @@ pub fn optimize_vertex_fetch(destination: &mut [Vertex], indices: &mut [ where Vertex: Position + Copy, { - assert!(indices.len() % 3 == 0); + assert!(indices.len().is_multiple_of(3)); // build vertex remap table let mut vertex_remap = vec![INVALID_INDEX; vertices.len()]; diff --git a/src/vertex/mod.rs b/src/vertex/mod.rs index 602bd2b..5c62e15 100644 --- a/src/vertex/mod.rs +++ b/src/vertex/mod.rs @@ -11,21 +11,17 @@ pub enum DecodeError { UnexpectedEof, } +#[derive(Default)] pub enum VertexEncodingVersion { /// Decodable by all versions + #[default] V0, } -impl Default for VertexEncodingVersion { - fn default() -> Self { - Self::V0 - } -} - -impl Into for VertexEncodingVersion { - fn into(self) -> u8 { - match self { - Self::V0 => 0, +impl From for u8 { + fn from(value: VertexEncodingVersion) -> Self { + match value { + VertexEncodingVersion::V0 => 0, } } }