diff --git a/.github/workflows/push.yaml b/.github/workflows/push.yaml index 5005b4b0..2e978baa 100644 --- a/.github/workflows/push.yaml +++ b/.github/workflows/push.yaml @@ -13,7 +13,7 @@ env: CARGO_GPU_COMMITSH: 31153a8edd3bc626d4b9fb0cd7bdb7a8b30797d3 # Nightly toolchain used for rustfmt in CI and in renderling-build's codegen. # Bump deliberately: reformat + regenerate shaders in the same PR. - RUST_NIGHTLY_TOOLCHAIN: nightly-2026-07-31 + RUST_NIGHTLY_TOOLCHAIN: nightly-2026-10-10 jobs: # Installs cargo deps and sets the cache directory for subsequent jobs diff --git a/AGENTS.md b/AGENTS.md index 94854df1..b969f2f1 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -17,7 +17,7 @@ Always format with `cargo +nightly fmt`. -The fmt toolchain is pinned to `nightly-2026-07-31` via `RUST_NIGHTLY_TOOLCHAIN` in `.github/workflows/push.yaml`. Use that toolchain locally too; bump it deliberately (reformat + regenerate shaders in the same PR). +The fmt toolchain is pinned to `nightly-2026-10-10` via `RUST_NIGHTLY_TOOLCHAIN` in `.github/workflows/push.yaml`. Use that toolchain locally too; bump it deliberately (reformat + regenerate shaders in the same PR). ## Disallowed Methods (clippy.toml) Avoid: `Vec{2,3,4}::normalize_or_zero`, `Mat4::to_scale_rotation_translation`, `f32::signum` diff --git a/crates/example-culling/src/main.rs b/crates/example-culling/src/main.rs index d93fc7e5..3592e2ca 100644 --- a/crates/example-culling/src/main.rs +++ b/crates/example-culling/src/main.rs @@ -208,8 +208,8 @@ impl TestAppHandler for CullingExample { let target = Vec3::ZERO; let up = Vec3::Y; let view = Mat4::look_at_rh(eye, target, up); - // let projection = Mat4::orthographic_rh(-10.0, 10.0, -10.0, 10.0, -10.0, - // 10.0); let view = Mat4::IDENTITY; + // let projection = Mat4::orthographic_rh(-10.0, 10.0, -10.0, 10.0, + // -10.0, 10.0); let view = Mat4::IDENTITY; CameraDescriptor::new(projection, view) }); diff --git a/crates/example/src/camera.rs b/crates/example/src/camera.rs index cec4b35d..d94d6d38 100644 --- a/crates/example/src/camera.rs +++ b/crates/example/src/camera.rs @@ -146,8 +146,8 @@ impl CameraController for WasdMouseCameraController { if let Some(prev) = self.last_tick.replace(now) { let dt = now - prev; - // We want the direction to be based solely on self.theta, because we - // don't want the camera to move in Y. + // We want the direction to be based solely on self.theta, because + // we don't want the camera to move in Y. let forward_direction = Vec3::NEG_Z; let left_direction = Vec3::NEG_X; let up_direction = Vec3::Y; @@ -194,7 +194,8 @@ impl CameraController for WasdMouseCameraController { } fn reset(&mut self, _bounds: Aabb) { - self.position = Vec3::ZERO; //center_max_z + (center_max_z - center_min_z) * 0.5; + self.position = Vec3::ZERO; //center_max_z + (center_max_z - + // center_min_z) * 0.5; self.theta = 0.0; self.phi = 0.0; self.speed = 2.0; diff --git a/crates/example/src/lib.rs b/crates/example/src/lib.rs index e083550a..1f444978 100644 --- a/crates/example/src/lib.rs +++ b/crates/example/src/lib.rs @@ -374,13 +374,14 @@ impl App { // if let Some(dir) = light.details.as_directional() { // log::info!("found a directional light to use for shadows"); // { - // let (p, j) = dir.get().shadow_mapping_projection_and_view( - // &light.node_transform.get_global_transform().into(), + // let (p, j) = + // dir.get().shadow_mapping_projection_and_view( + // &light.node_transform.get_global_transform().into(), // &self.camera.get(), // ); - // let mut guard = self.lighting.shadow_map.descriptor_lock(); - // guard.light_space_transform = p * j; - // } + // let mut guard = + // self.lighting.shadow_map.descriptor_lock(); + // guard.light_space_transform = p * j; } // self.lighting // .shadow_map diff --git a/crates/renderling-build/src/lib.rs b/crates/renderling-build/src/lib.rs index 47bc2965..4beca8d0 100644 --- a/crates/renderling-build/src/lib.rs +++ b/crates/renderling-build/src/lib.rs @@ -281,8 +281,9 @@ impl RenderlingPaths { std::fs::write(&filepath, contents).unwrap(); } // Just format the whole project. I know this is less than ideal, - // but people should be running with a formatter in their editor, and all of - // this is temporary given the wgsl-rs re-stacking happening this year (2026) + // but people should be running with a formatter in their editor, and + // all of this is temporary given the wgsl-rs re-stacking + // happening this year (2026) let fmt_toolchain = std::env::var("RUST_NIGHTLY_TOOLCHAIN").unwrap_or_else(|_| "nightly".into()); std::process::Command::new("cargo") diff --git a/crates/renderling/src/atlas/cpu.rs b/crates/renderling/src/atlas/cpu.rs index 4df8c55e..f1ac3348 100644 --- a/crates/renderling/src/atlas/cpu.rs +++ b/crates/renderling/src/atlas/cpu.rs @@ -482,8 +482,8 @@ impl Atlas { if total_dropped > 0 { log::trace!("repacking after dropping {total_dropped} frames from the atlas"); - // UNWRAP: safe because we can only remove frames from the atlas, which should - // only make it easier to pack. + // UNWRAP: safe because we can only remove frames from the atlas, + // which should only make it easier to pack. self.resize(runtime.as_ref(), self.get_size()).unwrap(); true } else { @@ -752,7 +752,8 @@ impl StagedResources { if packed_items.items.is_empty() { continue; } - // UNWRAP: safe because we know this index exists because we created it above + // UNWRAP: safe because we know this index exists because we created + // it above let layer = temporary_layers.get_mut(layer_index).unwrap(); for (frame_index, crunch::PackedItem { data: item, rect }) in packed_items.items.into_iter().enumerate() @@ -825,8 +826,8 @@ impl StagedResources { let prev_t = texture.cache; let mut t = texture.cache; debug_assert_eq!(t.size_px, size_px); - // copy the frame from the old texture to the new texture - // in a new destination + // copy the frame from the old texture to the new + // texture in a new destination encoder.copy_texture_to_texture( wgpu::TexelCopyTextureInfo { texture: &old_texture_array.texture, @@ -1216,7 +1217,8 @@ mod test { } #[test] - // Ensures that textures with different wrapping modes are rendered correctly. + // Ensures that textures with different wrapping modes are rendered + // correctly. fn uv_wrapping() { let icon_w = 32; let icon_h = 41; @@ -1483,7 +1485,8 @@ mod test { #[test] fn upkeep_trims_the_atlas() { - // tests that Atlas::upkeep trims out unused images and repacks the atlas + // tests that Atlas::upkeep trims out unused images and repacks the + // atlas let ctx = Context::headless(100, 100) .block() .with_default_atlas_texture_size(UVec3::new(512, 512, 2)); diff --git a/crates/renderling/src/atlas/shader.rs b/crates/renderling/src/atlas/shader.rs index cfd4f4be..7d980e63 100644 --- a/crates/renderling/src/atlas/shader.rs +++ b/crates/renderling/src/atlas/shader.rs @@ -31,7 +31,8 @@ impl AtlasTextureDescriptor { uv.x = self.modes.s.wrap(uv.x); uv.y = self.modes.t.wrap(uv.y); - // get the pixel index of the uv coordinate in terms of the original image + // get the pixel index of the uv coordinate in terms of the original + // image let mut px_index_s = (uv.x * self.size_px.x as f32) as u32; let mut px_index_t = (uv.y * self.size_px.y as f32) as u32; diff --git a/crates/renderling/src/bloom/cpu.rs b/crates/renderling/src/bloom/cpu.rs index 6e044733..3da9b46a 100644 --- a/crates/renderling/src/bloom/cpu.rs +++ b/crates/renderling/src/bloom/cpu.rs @@ -561,10 +561,11 @@ impl Bloom { } // Get all the bindgroups (which are what we're reading from), // starting with the hdr frame. - // Since `bindgroups` are one element greater (we pushed `hdr_texture_bindgroup` - // to the front) the last bindgroup will not be used, which is good - we - // don't need to read from the smallest texture during downsampling. - // UNWRAP: not safe but we want to panic + // Since `bindgroups` are one element greater (we pushed + // `hdr_texture_bindgroup` to the front) the last bindgroup will + // not be used, which is good - we don't need to read from the + // smallest texture during downsampling. UNWRAP: not safe but we + // want to panic let textures_guard = self.textures.read().expect("bloom textures read"); let hdr_texture_downsample_bindgroup_guard = self .hdr_texture_downsample_bindgroup diff --git a/crates/renderling/src/bloom/shader.rs b/crates/renderling/src/bloom/shader.rs index c8185c74..af5d199f 100644 --- a/crates/renderling/src/bloom/shader.rs +++ b/crates/renderling/src/bloom/shader.rs @@ -75,11 +75,11 @@ pub fn bloom_downsample_fragment( // d,e,g,h * 0.125 // e,f,h,i * 0.125 // j,k,l,m * 0.5 - // This shows 5 square areas that are being sampled. But some of them overlap, - // so to have an energy preserving downsample we need to make some adjustments. - // The weights are the distributed so that the sum of j,k,l,m (e.g.) - // contribute 0.5 to the final color output. The code below is written - // to effectively yield this sum. We get: + // This shows 5 square areas that are being sampled. But some of them + // overlap, so to have an energy preserving downsample we need to make + // some adjustments. The weights are the distributed so that the sum of + // j,k,l,m (e.g.) contribute 0.5 to the final color output. The code + // below is written to effectively yield this sum. We get: // 0.125*5 + 0.03125*4 + 0.0625*4 = 1 let f1 = 0.125; let f2 = 0.0625; diff --git a/crates/renderling/src/cubemap/cpu.rs b/crates/renderling/src/cubemap/cpu.rs index f611ffef..98dbf251 100644 --- a/crates/renderling/src/cubemap/cpu.rs +++ b/crates/renderling/src/cubemap/cpu.rs @@ -92,17 +92,18 @@ impl SceneCubemap { let camera = stage.geometry.new_camera(); stage.use_camera(&camera); - // By setting this to 90 degrees (PI/2 radians) we make sure the viewing field - // is exactly large enough to fill a single face of the cubemap such that all - // faces align correctly to each other at the edges. + // By setting this to 90 degrees (PI/2 radians) we make sure the viewing + // field is exactly large enough to fill a single face of the + // cubemap such that all faces align correctly to each other at + // the edges. let fovy = std::f32::consts::FRAC_PI_2; let aspect = self.cubemap_texture.width() as f32 / self.cubemap_texture.height() as f32; let projection = Mat4::perspective_lh(fovy, aspect, 1.0, 25.0); - // Render each face by rendering the scene from each camera angle into the - // cubemap + // Render each face by rendering the scene from each camera angle into + // the cubemap for (i, face) in CubemapFaceDirection::FACES.iter().enumerate() { - // Update the camera angle, no need to sync as calling `Stage::render` does this - // implicitly + // Update the camera angle, no need to sync as calling + // `Stage::render` does this implicitly camera.set_projection_and_view(projection, face.view()); let label_s = format!("scene-to-cubemap-{i}"); let view = self @@ -294,7 +295,8 @@ mod test { let _camera = stage .new_camera() .with_projection_and_view(projection, view); - // geometry is the "clip cube" where colors are normalized 3d space coords + // geometry is the "clip cube" where colors are normalized 3d space + // coords let _rez = stage .new_primitive() .with_vertices(stage.new_vertices(UNIT_POINTS.map(|unit_cube_point| { diff --git a/crates/renderling/src/cull/cpu.rs b/crates/renderling/src/cull/cpu.rs index 37cdc01c..db91ffe9 100644 --- a/crates/renderling/src/cull/cpu.rs +++ b/crates/renderling/src/cull/cpu.rs @@ -930,8 +930,8 @@ mod test { renderlet }; - // Do two renders, because depth pyramid operates on depth data one frame - // behind + // Do two renders, because depth pyramid operates on depth data one + // frame behind save_render("3_purple_cube"); save_render("3_purple_cube"); @@ -962,7 +962,8 @@ mod test { img_diff::save(format!("cull/debugging_pyramid_mip_{i}.png"), img); } - // The stage's slab, which contains the `Renderlet`s and their `BoundingSphere`s + // The stage's slab, which contains the `Renderlet`s and their + // `BoundingSphere`s let stage_slab = futures_lite::future::block_on({ let geometry: &Geometry = stage.as_ref(); geometry.slab_allocator().read(..) @@ -970,8 +971,8 @@ mod test { .unwrap(); let draw_calls = stage.draw_calls.read().unwrap(); let indirect_draws = draw_calls.drawing_strategy.as_indirect().unwrap(); - // The HZB slab, which contains a `DepthPyramidDescriptor` at index 0, and all - // the pyramid's mips + // The HZB slab, which contains a `DepthPyramidDescriptor` at index 0, + // and all the pyramid's mips let depth_pyramid_slab = futures_lite::future::block_on( indirect_draws .compute_culling diff --git a/crates/renderling/src/debug.rs b/crates/renderling/src/debug.rs index 03861e8c..1c480a88 100644 --- a/crates/renderling/src/debug.rs +++ b/crates/renderling/src/debug.rs @@ -63,9 +63,10 @@ pub mod shader { let distance = sdf_circle.distance(frag_coord.xy() + 0.5); - // Here we use `step_le`, which I have annotated with `#inline(always)`. - // I did this because without it, it seems to do the opposite of expected. - // I found this by inlining by hand. + // Here we use `step_le`, which I have annotated with + // `#inline(always)`. I did this because without it, it + // seems to do the opposite of expected. I found this by + // inlining by hand. let alpha = crate::math::step_le(sphere_aabb.max.z, 1.0); if distance.abs() < 0.5 { *frag_color = Vec4::new(0.0, 0.0, 0.0, 1.0 * alpha); diff --git a/crates/renderling/src/draw/cpu.rs b/crates/renderling/src/draw/cpu.rs index 693db965..581caf58 100644 --- a/crates/renderling/src/draw/cpu.rs +++ b/crates/renderling/src/draw/cpu.rs @@ -210,8 +210,8 @@ impl DrawCalls { depth_texture: &Texture, ) -> Result>, CullingError> { let num_draw_calls = self.draw_count(); - // Only do compute culling if there are things we need to draw, otherwise - // `wgpu` will err with something like: + // Only do compute culling if there are things we need to draw, + // otherwise `wgpu` will err with something like: // "Buffer with 'indirect draw upkeep' label binding size is zero" if num_draw_calls > 0 { log::trace!("num_draw_calls: {num_draw_calls}"); @@ -222,8 +222,9 @@ impl DrawCalls { if let Some(indirect) = &mut self.drawing_strategy.indirect { if indirect.draws.len() != self.renderlets.len() { indirect.invalidate(); - // Pre-upkeep to reclaim resources - this is necessary because - // the draw buffer has to be contiguous (it can't start with a bunch of trash) + // Pre-upkeep to reclaim resources - this is necessary + // because the draw buffer has to be + // contiguous (it can't start with a bunch of trash) let indirect_buffer = indirect.slab.commit(); if indirect_buffer.is_new_this_commit() { log::warn!("new indirect buffer"); diff --git a/crates/renderling/src/geometry/cpu.rs b/crates/renderling/src/geometry/cpu.rs index f40890aa..87e9e5c6 100644 --- a/crates/renderling/src/geometry/cpu.rs +++ b/crates/renderling/src/geometry/cpu.rs @@ -393,7 +393,8 @@ impl Geometry { pub fn use_camera(&self, camera: &Camera) { let id = camera.id(); log::info!("using camera: {id:?}"); - // Save a clone so we never lose the active camera, even if the user drops it + // Save a clone so we never lose the active camera, even if the user + // drops it self.descriptor.modify(|cfg| cfg.camera_id = id); *self.camera.lock().expect("geometry camera lock") = Some(camera.clone()); } diff --git a/crates/renderling/src/geometry/shader.rs b/crates/renderling/src/geometry/shader.rs index 461d7263..88df2374 100644 --- a/crates/renderling/src/geometry/shader.rs +++ b/crates/renderling/src/geometry/shader.rs @@ -25,9 +25,9 @@ impl SkinDescriptor { let joint_index = vertex.joints[i] as usize; let joint_id = slab.read(self.joints_array.at(joint_index)); let joint_transform = slab.read(joint_id); - // First apply the inverse bind matrix to bring the vertex into the joint's - // local space, then apply the joint's current transformation to move it - // into world space. + // First apply the inverse bind matrix to bring the vertex into the + // joint's local space, then apply the joint's current + // transformation to move it into world space. let inverse_bind_matrix = slab.read(self.inverse_bind_matrices_array.at(joint_index)); Mat4::from(joint_transform) * inverse_bind_matrix } diff --git a/crates/renderling/src/gltf.rs b/crates/renderling/src/gltf.rs index 2d9706ee..b7406146 100644 --- a/crates/renderling/src/gltf.rs +++ b/crates/renderling/src/gltf.rs @@ -820,7 +820,8 @@ impl GltfDocument { for gltf_material in document.materials() { Material::preprocess_images(gltf_material, &mut images)?; } - // Arc these images because they could be large and we don't want duplicates + // Arc these images because they could be large and we don't want + // duplicates let images = images.into_iter().map(Arc::new).collect::>(); log::debug!("Loading {} images into the atlas", images.len()); @@ -982,10 +983,11 @@ impl GltfDocument { let weights = node.weights().map(|w| w.to_vec()).unwrap_or_default(); // From the glTF spec: // - // A mesh with morph targets MAY also define an optional mesh.weights property - // that stores the default targets' weights. These weights MUST be used when - // node.weights is undefined. When mesh.weights is undefined, the default - // targets' weights are zeros. + // A mesh with morph targets MAY also define an optional + // mesh.weights property that stores the default + // targets' weights. These weights MUST be used when + // node.weights is undefined. When mesh.weights is undefined, the + // default targets' weights are zeros. let weights = if weights.is_empty() { if let Some(mesh) = node.mesh() { meshes[mesh.index()].weights.clone() diff --git a/crates/renderling/src/gltf/anime.rs b/crates/renderling/src/gltf/anime.rs index 939f8688..0095356c 100644 --- a/crates/renderling/src/gltf/anime.rs +++ b/crates/renderling/src/gltf/anime.rs @@ -302,8 +302,8 @@ impl Tween { .context(MissingPropertyIndexSnafu { index: prev_keyframe_ndx, })?; - // UNWRAP: safe because we know this is either the first index or was found - // above + // UNWRAP: safe because we know this is either the first index or was + // found above let [to_in, to, _] = self.properties .get_cubic(next_keyframe_ndx) @@ -423,8 +423,8 @@ impl Tween { // UNWRAP: safe because we know this was found above let from = self.properties.get(prev_keyframe_ndx).unwrap(); - // UNWRAP: safe because we know this is either the first index or was found - // above + // UNWRAP: safe because we know this is either the first index or was + // found above let to = self.properties.get(next_keyframe_ndx).unwrap(); let amount = (time - prev_time) / (next_time - prev_time); @@ -726,8 +726,8 @@ impl Animator { log::trace!(" {} properties", properties.len()); for (node_index, property) in properties.into_iter() { log::trace!(" {node_index} {}", property.description()); - // There's plenty of reasons why a node referenced by an animation might not - // exist in the animator's "nodes": + // There's plenty of reasons why a node referenced by an animation + // might not exist in the animator's "nodes": // * the node is not in this scene // * business logic has removed it // * ...and the beat goes on diff --git a/crates/renderling/src/lib.rs b/crates/renderling/src/lib.rs index 45fa297c..11d7fad9 100644 --- a/crates/renderling/src/lib.rs +++ b/crates/renderling/src/lib.rs @@ -445,8 +445,8 @@ mod test { } #[test] - // This tests our ability to draw a CMYK triangle in the top left corner, using - // CW geometry. + // This tests our ability to draw a CMYK triangle in the top left corner, + // using CW geometry. fn cmy_triangle_backface() { use img_diff::DiffCfg; @@ -694,8 +694,8 @@ mod test { img_diff::assert_img_eq("cmy_cube/remesh_before.png", img); frame.present(); - // Update the cube mesh to a pyramid by overwriting the `.vertices` field - // of `Renderlet` + // Update the cube mesh to a pyramid by overwriting the `.vertices` + // field of `Renderlet` let pyramid_points = pyramid_points(); let pyramid_geometry = stage .new_vertices(pyramid_indices().map(|i| cmy_gpu_vertex(pyramid_points[i as usize]))); @@ -755,8 +755,8 @@ mod test { } #[test] - // Tests that updating the material actually updates the rendering of an unlit - // mesh + // Tests that updating the material actually updates the rendering of an + // unlit mesh fn unlit_textured_cube_material() { let ctx = Context::headless(100, 100).block(); let stage = ctx.new_stage().with_background_color(Vec4::splat(0.0)); @@ -925,8 +925,8 @@ mod test { } #[test] - // Test to make sure that we can reconstruct a normal matrix without using the - // inverse transpose of a model matrix, so long as we have the T R S + // Test to make sure that we can reconstruct a normal matrix without using + // the inverse transpose of a model matrix, so long as we have the T R S // transformation components (we really only need the scale). // // see Eric's comment here https://computergraphics.stackexchange.com/questions/1502/why-is-the-transposed-inverse-of-the-model-view-matrix-used-to-transform-the-nor?newreg=ffeabc7602da4fa2bc15fb9c84179dff diff --git a/crates/renderling/src/light.rs b/crates/renderling/src/light.rs index 16714450..5843e697 100644 --- a/crates/renderling/src/light.rs +++ b/crates/renderling/src/light.rs @@ -111,9 +111,10 @@ mod test { #[cfg(feature = "gltf")] #[test] fn position_direction_sanity() { - // With GLTF, the direction of a light is given by the light's node's transform. - // Specifically we get the node's transform and use the rotation quaternion to - // rotate the vector Vec3::NEG_Z - the result is our direction. + // With GLTF, the direction of a light is given by the light's node's + // transform. Specifically we get the node's transform and use + // the rotation quaternion to rotate the vector Vec3::NEG_Z - + // the result is our direction. use glam::{Mat4, Quat}; println!("{:#?}", std::env::current_dir()); @@ -131,10 +132,10 @@ mod test { println!("position: {position}"); println!("direction: {direction}"); - // In Blender, our lights are sitting at (0, 0, 1) pointing at -Z, +Z, +X and - // +Y. But alas, it is a bit more complicated than that because this - // file is exported with UP being +Y, so Z and Y have been - // flipped... + // In Blender, our lights are sitting at (0, 0, 1) pointing at -Z, + // +Z, +X and +Y. But alas, it is a bit more complicated + // than that because this file is exported with UP being + // +Y, so Z and Y have been flipped... assert_eq!(Vec3::Y, position); let expected_direction = match node.name() { Some("light_negative_z") => Vec3::NEG_Y, @@ -204,8 +205,8 @@ mod test { fn next_light_sanity() { { let lights_array = Array::new(0, 1); - // When there's only one light we only need one invocation to check that one - // light (per tile) + // When there's only one light we only need one invocation to check + // that one light (per tile) let mut next_light = NextLightIndex::new(UVec3::new(0, 0, 0), 16, lights_array); assert_eq!(Some(0u32.into()), next_light.next()); assert_eq!(None, next_light.next()); @@ -231,7 +232,8 @@ mod test { assert_eq!(None, next_light.next()); } { - // With 256 lights (16*16), each fragment in the tile checks exactly one light + // With 256 lights (16*16), each fragment in the tile checks exactly + // one light let lights_array = Array::new(0, 16 * 16); let mut checked_lights = vec![]; for y in 0..16 { diff --git a/crates/renderling/src/light/cpu/test.rs b/crates/renderling/src/light/cpu/test.rs index af6862bd..7a164edc 100644 --- a/crates/renderling/src/light/cpu/test.rs +++ b/crates/renderling/src/light/cpu/test.rs @@ -337,11 +337,11 @@ fn clear_tiles_sanity() { let y = i as u32 / tile_dimensions.x; let tile_coord = UVec2::new(x, y); let distance = tile_coord.manhattan_distance(tile_dimensions) as f32; - // This should produce an image where pixels get darker towards the lower right - // corner. + // This should produce an image where pixels get darker towards + // the lower right corner. let min = distance / max_distance; - // This should produce an image where pixels get darker towards the upper left - // corner. + // This should produce an image where pixels get darker towards + // the upper left corner. let max = 1.0 - distance / max_distance; item.depth_min = crate::light::shader::quantize_depth_f32_to_u32(min); @@ -422,8 +422,8 @@ fn min_max_depth_sanity() { ); let label = Some("light-tiling-min-max-depth-test"); - // Clear the tiles, which is verified in `clear_tiles_sanity`, then assert the - // min/max depth + // Clear the tiles, which is verified in `clear_tiles_sanity`, then assert + // the min/max depth { let mut encoder = ctx .get_device() @@ -499,8 +499,8 @@ fn light_bins_sanity() { let light_bin = futures_lite::future::block_on(lighting.light_slab.read_array(tile.lights_array)) .unwrap(); - // Assert either the light is the correct one, or we're using the zero frustum - // optimization discussed in + // Assert either the light is the correct one, or we're using the zero + // frustum optimization discussed in if tile.depth_min != tile.depth_max { assert_eq!(light_bin[0], directional_light.id()); assert_eq!(light_bin[1], Id::NONE); diff --git a/crates/renderling/src/light/shader.rs b/crates/renderling/src/light/shader.rs index 2575c2de..11ead1e7 100644 --- a/crates/renderling/src/light/shader.rs +++ b/crates/renderling/src/light/shader.rs @@ -727,10 +727,11 @@ impl ShadowCalculation { if !crate::math::is_inside_clip_space(frag_pos_in_light_space.xyz()) { return 0.0; } - // The range of coordinates in the light's clip space is -1.0 to 1.0 for x and - // y, but the texture space is [0, 1], and Y increases downward, so we - // do this conversion to flip Y and also normalize to the range [0.0, - // 1.0]. Z should already be 0.0 to 1.0. + // The range of coordinates in the light's clip space is -1.0 to 1.0 for + // x and y, but the texture space is [0, 1], and Y increases + // downward, so we do this conversion to flip Y and also + // normalize to the range [0.0, 1.0]. Z should already be 0.0 to + // 1.0. let proj_coords_uv = (frag_pos_in_light_space.xy() * Vec2::new(1.0, -1.0) + Vec2::splat(1.0)) * Vec2::splat(0.5); @@ -739,8 +740,9 @@ impl ShadowCalculation { let shadow_map_atlas_texture = self.get_atlas_texture_at(light_slab, 0); // With these projected coordinates we can sample the depth map as the // resulting [0,1] coordinates from proj_coords directly correspond to - // the transformed NDC coordinates from the `ShadowMap::update` render pass. - // This gives us the closest depth from the light's point of view: + // the transformed NDC coordinates from the `ShadowMap::update` render + // pass. This gives us the closest depth from the light's point + // of view: let pcf_samples_2 = *pcf_samples as i32 / 2; let texel_size = 1.0 / Vec2::new( @@ -758,13 +760,15 @@ impl ShadowCalculation { let shadow_map_depth = shadow_map .sample_by_lod(*shadow_map_sampler, proj_coords, 0.0) .x; - // To get the current depth at this fragment we simply retrieve the projected - // vector's z coordinate which equals the depth of this fragment + // To get the current depth at this fragment we simply retrieve + // the projected vector's z coordinate which + // equals the depth of this fragment // from the light's perspective. let fragment_depth = frag_pos_in_light_space.z; - // If the `current_depth`, which is the depth of the fragment from the lights - // POV, is greater than the `closest_depth` of the shadow map at + // If the `current_depth`, which is the depth of the fragment + // from the lights POV, is greater than the + // `closest_depth` of the shadow map at // that fragment, the fragment is in shadow crate::println!("current_depth: {fragment_depth}"); crate::println!("closest_depth: {shadow_map_depth}"); @@ -1098,8 +1102,8 @@ impl LightTilingInvocation { let frag_depth: f32 = depth_texture.fetch(frag_pos).x; // Fragment depth scaled to min/max of u32 values // - // This is so we can compare with normal atomic ops instead of using the float - // extension + // This is so we can compare with normal atomic ops instead of using the + // float extension let frag_depth_u32 = quantize_depth_f32_to_u32(frag_depth); // The tile's index in all the tiles @@ -1149,10 +1153,10 @@ impl LightTilingInvocation { } } - // The difficulty here is that in SPIRV we can access `lighting_slab` atomically - // without wrapping it in a type, but on CPU we must pass an array of - // (something like) `AtomicU32`. I'm not sure how to model this interaction - // to test it on the CPU. + // The difficulty here is that in SPIRV we can access `lighting_slab` + // atomically without wrapping it in a type, but on CPU we must pass an + // array of (something like) `AtomicU32`. I'm not sure how to model this + // interaction to test it on the CPU. fn compute_light_lists(&self, geometry_slab: &[u32], lighting_slab: &mut [u32]) { let index = self.tile_index(); let tile_id = self.descriptor.tiles_array.at(index); @@ -1182,8 +1186,9 @@ impl LightTilingInvocation { // ndc_tile_max.extend(depth_max), // ); - // Get the frustum (here simplified to a line) in world coords, since we'll be - // using it to compare against the radius of illumination of each light + // Get the frustum (here simplified to a line) in world coords, since + // we'll be using it to compare against the radius of + // illumination of each light let tile_ndc_midpoint = self.tile_ndc_midpoint(); let tile_line_ndc = ( tile_ndc_midpoint.extend(depth_min), @@ -1206,8 +1211,8 @@ impl LightTilingInvocation { + LightingDescriptor::OFFSET_OF_ANALYTICAL_LIGHTS_ARRAY, ); - // Each invocation will calculate a few lights' contribution to the tile, until - // all lights have been visited + // Each invocation will calculate a few lights' contribution to the + // tile, until all lights have been visited let next_light = NextLightIndex::new( self.global_id, self.descriptor.tile_size, @@ -1233,7 +1238,8 @@ impl LightTilingInvocation { (distance, point_light.intensity.0 / 683.0) } LightStyle::Spot => { - // TODO: take into consideration the direction the spot light is pointing + // TODO: take into consideration the direction the spot + // light is pointing let spot_light = lighting_slab.read(light.into_spot_id()); let center = Mat4::from(transform).transform_point3(spot_light.position); let distance = crate::math::distance_to_line(center, tile_line.0, tile_line.1); @@ -1246,8 +1252,9 @@ impl LightTilingInvocation { radius_of_illumination(intensity_candelas, self.descriptor.minimum_illuminance_lux); let should_add = radius >= distance; if should_add { - // If the light should be added to the bin, get the next available index in the - // bin, then write the id of the light into that index. + // If the light should be added to the bin, get the next + // available index in the bin, then write the id + // of the light into that index. let next_index = crate::sync::atomic_i_increment::< { spirv_std::memory::Scope::Workgroup as u32 }, { spirv_std::memory::Semantics::WORKGROUP_MEMORY.bits() }, @@ -1255,10 +1262,12 @@ impl LightTilingInvocation { if next_index as usize >= tile_lights_array.len() { // We've already filled the bin, so abort. // - // TODO: Figure out a better way to handle light tile list overrun. + // TODO: Figure out a better way to handle light tile list + // overrun. break; } else { - // Get the id that corresponds to the next available index in the ratings bin + // Get the id that corresponds to the next available index + // in the ratings bin let binned_light_id = tile_lights_array.at(next_index as usize); // Write to that location lighting_slab.write(binned_light_id, &light_id); diff --git a/crates/renderling/src/light/shadow_map.rs b/crates/renderling/src/light/shadow_map.rs index 23b98d7a..320a1dbd 100644 --- a/crates/renderling/src/light/shadow_map.rs +++ b/crates/renderling/src/light/shadow_map.rs @@ -324,8 +324,9 @@ impl ShadowMap { let mut encoder = device .create_command_encoder(&wgpu::CommandEncoderDescriptor { label: Self::LABEL }); - // Update the lighting descriptor to point to this shadow map, which tells the - // vertex shader which shadow map we're updating. + // Update the lighting descriptor to point to this shadow map, which + // tells the vertex shader which shadow map we're + // updating. lighting.lighting_descriptor.modify(|ld| { let id = self.shadowmap_descriptor.id(); log::trace!("updating the shadow map {id:?} {i}"); @@ -372,7 +373,8 @@ impl ShadowMap { } log::trace!("rendered {count} renderlets to the shadow map"); } - // Then copy the depth texture to our shadow map atlas in the lighting struct + // Then copy the depth texture to our shadow map atlas in the + // lighting struct self.blitting_op.run( lighting.light_slab.runtime(), &mut encoder, @@ -408,8 +410,8 @@ mod test { .with_msaa_sample_count(4); // let hdr_path = - // std::path::PathBuf::from(std::env!("CARGO_WORKSPACE_DIR")).join("img/hdr/ - // night.hdr"); let hdr_img = + // std::path::PathBuf::from(std::env!("CARGO_WORKSPACE_DIR")).join(" + // img/hdr/ night.hdr"); let hdr_img = // AtlasImage::from_hdr_path(hdr_path).unwrap(); // let skybox = Skybox::new(&ctx, hdr_img, camera.id()); @@ -552,14 +554,16 @@ mod test { // use crate::texture::DepthTexture; // use image::Luma; // { - // // Ensure the state of the "update texture", which receives the depth - // of the scene on update let shadow_map_update_texture = - // DepthTexture::try_new_from(&ctx, - // shadows.update_texture.clone()).unwrap(); let mut - // shadow_map_update_img = shadow_map_update_texture.read_image().unwrap(); + // // Ensure the state of the "update texture", which receives + // the depth of the scene on update let + // shadow_map_update_texture = + // DepthTexture::try_new_from(&ctx, shadows.update_texture. + // clone()).unwrap(); let mut shadow_map_update_img = + // shadow_map_update_texture.read_image().unwrap(); // img_diff::normalize_gray_img(&mut shadow_map_update_img); // img_diff::save( - // "shadows/shadow_mapping_sanity/shadows_update_texture.png", + // + // "shadows/shadow_mapping_sanity/shadows_update_texture.png", // shadow_map_update_img, // ); // } @@ -573,8 +577,8 @@ mod test { // let shadow_depth_img = shadow_depth_img.into_luma8(); // let mut depth_img = shadow_depth_img.clone(); // img_diff::normalize_gray_img(&mut depth_img); - // img_diff::save("shadows/shadow_mapping_sanity/depth.png", depth_img); - // } + // img_diff::save("shadows/shadow_mapping_sanity/depth.png", + // depth_img); } // } // Now do the rendering *with the shadow map* to see if it works. @@ -633,8 +637,8 @@ mod test { stage.render(&frame.view()); let _img = frame.read_image().block().unwrap(); // img_diff::assert_img_eq( - // &format!("shadows/shadow_mapping_spots/light_pov_{i}.png"), - // img, + // &format!("shadows/shadow_mapping_spots/light_pov_{i}.png" + // ), img, // ); frame.present(); } @@ -700,8 +704,8 @@ mod test { stage.render(&frame.view()); let _img = frame.read_image().block().unwrap(); // img_diff::assert_img_eq( - // &format!("shadows/shadow_mapping_points/light_{i}_pov_{j}.png"), - // img, + // &format!("shadows/shadow_mapping_points/ + // light_{i}_pov_{j}.png"), img, // ); frame.present(); } diff --git a/crates/renderling/src/material/cpu.rs b/crates/renderling/src/material/cpu.rs index 3f864532..d624a276 100644 --- a/crates/renderling/src/material/cpu.rs +++ b/crates/renderling/src/material/cpu.rs @@ -81,7 +81,8 @@ impl Materials { /// Returns `true` if the atlas texture was recreated. #[must_use] pub fn commit(&self) -> (bool, SlabBuffer) { - // Atlas upkeep must be called first because it generates updates into the slab + // Atlas upkeep must be called first because it generates updates into + // the slab (self.atlas.upkeep(self.runtime()), self.slab.commit()) } diff --git a/crates/renderling/src/math.rs b/crates/renderling/src/math.rs index 26119c7c..6dca4181 100644 --- a/crates/renderling/src/math.rs +++ b/crates/renderling/src/math.rs @@ -33,8 +33,8 @@ impl Fetch for Image!(2D, type=f32, sampled, depth, multisampled=true) { fn fetch(&self, coords: UVec2) -> Self::Output { // TODO: check whether this is doing what we think it's doing. - // (We think its doing roughly the same thing as the non-multisampled version - // above) + // (We think its doing roughly the same thing as the non-multisampled + // version above) self.fetch_with(coords, sample_with::sample_index(0)) } } diff --git a/crates/renderling/src/pbr/ibl/cpu.rs b/crates/renderling/src/pbr/ibl/cpu.rs index 9e9f113e..95a97749 100644 --- a/crates/renderling/src/pbr/ibl/cpu.rs +++ b/crates/renderling/src/pbr/ibl/cpu.rs @@ -572,12 +572,12 @@ mod test { .unwrap(); stage.use_skybox(&skybox); - // Render once here because we found a bug where rendering before setting - // ibl would cause the primitive bindgroup to *not* be invalidated when - // ibl was set. + // Render once here because we found a bug where rendering before + // setting ibl would cause the primitive bindgroup to *not* be + // invalidated when ibl was set. // - // This essentially just ensures that `Stage::use_ibl` is invalidating the - // primitive bindgroup. + // This essentially just ensures that `Stage::use_ibl` is invalidating + // the primitive bindgroup. let frame = ctx.get_next_frame().unwrap(); stage.render(&frame.view()); frame.present(); diff --git a/crates/renderling/src/pbr/shader.rs b/crates/renderling/src/pbr/shader.rs index 7ead67ae..8ce14722 100644 --- a/crates/renderling/src/pbr/shader.rs +++ b/crates/renderling/src/pbr/shader.rs @@ -164,8 +164,9 @@ pub fn get_material( material_slab: &[u32], ) -> MaterialDescriptor { if material_id.is_none() { - // without an explicit material (or if the entire render has no lighting) - // the entity will not participate in any lighting calculations + // without an explicit material (or if the entire render has no + // lighting) the entity will not participate in any lighting + // calculations MaterialDescriptor { has_lighting: false, ..Default::default() @@ -540,11 +541,12 @@ where intensity: Candela(intensity_candelas), } = light_slab.read(light.into_point_id()); // Convert candelas into radiometric - // TODO: write true radiometric light conversions for Lux and Candela + // TODO: write true radiometric light conversions for Lux and + // Candela let intensity = intensity_candelas / 683.0; let position = transform.transform_point3(position); - // This definitely is the direction pointing from fragment to the light. - // It needs to stay this way. + // This definitely is the direction pointing from fragment to + // the light. It needs to stay this way. // For more info, see // let frag_to_light = position - in_pos; @@ -558,8 +560,8 @@ where let radiance = outgoing_radiance(color, albedo, attenuation, v, l, n, metallic, roughness); let shadow = if light.shadow_map_desc_id.is_some() { - // Shadow is 1.0 when the fragment is in the shadow of this light, - // and 0.0 in darkness + // Shadow is 1.0 when the fragment is in the shadow of this + // light, and 0.0 in darkness ShadowCalculation::new(light_slab, light, in_pos, n, l).run_point( light_slab, shadow_map, @@ -582,7 +584,8 @@ where } // Convert from candelas to a radiometric unit. // - // TODO: verify that spot light radiometric conversion is correct. + // TODO: verify that spot light radiometric conversion is + // correct. let intensity = // TODO: write true radiometric light conversions for Lux and Candela spot_light_descriptor.intensity.0 / (683.0 * 4.0 * core::f32::consts::PI); @@ -598,8 +601,8 @@ where roughness, ); let shadow = if light.shadow_map_desc_id.is_some() { - // Shadow is 1.0 when the fragment is in the shadow of this light, - // and 0.0 in darkness + // Shadow is 1.0 when the fragment is in the shadow of this + // light, and 0.0 in darkness ShadowCalculation::new(light_slab, light, in_pos, n, calculation.frag_to_light) .run_directional_or_spot(light_slab, shadow_map, shadow_map_sampler) } else { @@ -616,14 +619,16 @@ where } = light_slab.read(light.into_directional_id()); let direction = transform.transform_vector3(direction); let l = -direction.alt_norm_or_zero(); - // TODO: write true radiometric light conversions for Lux and Candela + // TODO: write true radiometric light conversions for Lux and + // Candela let attenuation = intensity_lux.0 / 683.0; let radiance = outgoing_radiance(color, albedo, attenuation, v, l, n, metallic, roughness); let shadow = if light.shadow_map_desc_id.is_some() { - // Shadow is 1.0 when the fragment is in the shadow of this light, - // and 0.0 in darkness + // Shadow is 1.0 when the fragment is in the shadow of + // this light, and 0.0 in + // darkness ShadowCalculation::new(light_slab, light, in_pos, n, l) .run_directional_or_spot(light_slab, shadow_map, shadow_map_sampler) } else { @@ -638,9 +643,9 @@ where } my_println!("lo: {lo:?}"); - // calculate reflectance at normal incidence; if dia-electric (like plastic) use - // F0 of 0.04 and if it's a metal, use the albedo color as F0 (metallic - // workflow) + // calculate reflectance at normal incidence; if dia-electric (like plastic) + // use F0 of 0.04 and if it's a metal, use the albedo color as F0 + // (metallic workflow) let f0: Vec3 = Vec3::splat(0.04).lerp(albedo, metallic); let cos_theta = n.dot(v).max(0.0); let fresnel = fresnel_schlick_roughness(cos_theta, f0, roughness); diff --git a/crates/renderling/src/stage/cpu.rs b/crates/renderling/src/stage/cpu.rs index ae3e530b..b09ede81 100644 --- a/crates/renderling/src/stage/cpu.rs +++ b/crates/renderling/src/stage/cpu.rs @@ -288,7 +288,8 @@ impl StageRendering<'_> { let mut draw_calls = self.stage.draw_calls.write().expect("draw_calls write"); let depth_texture = self.stage.depth_texture.read().expect("depth_texture read"); - // UNWRAP: safe because we know the depth texture format will always match + // UNWRAP: safe because we know the depth texture format will always + // match let maybe_indirect_buffer = draw_calls.pre_draw(&depth_texture).unwrap(); log::trace!("rendering"); @@ -1197,7 +1198,8 @@ impl Stage { let depth_texture = Texture::create_depth_texture(device, w, h, multisample_count, Some("stage-depth")); let msaa_render_target = Default::default(); - // UNWRAP: safe because no other references at this point (created above^) + // UNWRAP: safe because no other references at this point (created + // above^) let bloom = Bloom::new(ctx, &hdr_texture.read().expect("hdr_texture read")); let tonemapping = Tonemapping::new( runtime, diff --git a/crates/renderling/src/texture.rs b/crates/renderling/src/texture.rs index d928a816..ce7b0e19 100644 --- a/crates/renderling/src/texture.rs +++ b/crates/renderling/src/texture.rs @@ -819,8 +819,8 @@ pub async fn read_depth_texture_to_image( .iter() .copied() .map(|f| { - // Depth texture is stored as Depth32Float, but the values are normalized - // 0.0-1.0 + // Depth texture is stored as Depth32Float, but the values are + // normalized 0.0-1.0 (255.0 * f) as u8 }) .collect::>(); @@ -949,8 +949,8 @@ impl std::future::Future for MappedBuffer<'_> { std::task::Poll::Ready(result.map(|()| { let padded_buffer = this.buffer_slice.get_mapped_range(); let mut unpadded_buffer = vec![]; - // from the padded_buffer we write just the unpadded bytes into the - // unpadded_buffer + // from the padded_buffer we write just the unpadded bytes into + // the unpadded_buffer for chunk in padded_buffer.chunks(self.dimensions.padded_bytes_per_row) { unpadded_buffer .extend_from_slice(&chunk[..self.dimensions.unpadded_bytes_per_row]); diff --git a/crates/renderling/src/tonemapping.rs b/crates/renderling/src/tonemapping.rs index 5bf9cda9..71fe7424 100644 --- a/crates/renderling/src/tonemapping.rs +++ b/crates/renderling/src/tonemapping.rs @@ -120,8 +120,8 @@ pub fn tonemap(mut color: Vec4, slab: &[u32]) -> Vec4 { Tonemap::ACES_HILL => tone_map_aces_hill(color.xyz()).extend(color.w), Tonemap::ACES_HILL_EXPOSURE_BOOST => { // boost exposure as discussed in https://github.com/mrdoob/three.js/pull/19621 - // this factor is based on the exposure correction of Krzysztof Narkowicz in his - // implemetation of ACES tone mapping + // this factor is based on the exposure correction of Krzysztof + // Narkowicz in his implemetation of ACES tone mapping tone_map_aces_hill(color.xyz() / 0.6).extend(color.w) } Tonemap::REINHARD => { diff --git a/crates/renderling/src/tonemapping/cpu.rs b/crates/renderling/src/tonemapping/cpu.rs index 27e63d28..e1647580 100644 --- a/crates/renderling/src/tonemapping/cpu.rs +++ b/crates/renderling/src/tonemapping/cpu.rs @@ -170,8 +170,8 @@ impl Tonemapping { } pub fn set_hdr_texture(&self, device: &wgpu::Device, hdr_texture: &Texture) { - // UNWRAP: safe because the buffer is created in `Self::new` and guaranteed to - // exist + // UNWRAP: safe because the buffer is created in `Self::new` and + // guaranteed to exist let slab_buffer = self.slab.get_buffer().unwrap(); let bindgroup = create_bindgroup(device, Some("tonemapping"), hdr_texture, &slab_buffer); // UNWRAP: not safe but we want to panic diff --git a/crates/renderling/tests/wasm.rs b/crates/renderling/tests/wasm.rs index 13bbf9fb..08f90a4d 100644 --- a/crates/renderling/tests/wasm.rs +++ b/crates/renderling/tests/wasm.rs @@ -231,7 +231,8 @@ async fn implicit_isosceles_triangle() { cache: None, }) } - // The first time through render with handwritten WGSL to ensure the setup works + // The first time through render with handwritten WGSL to ensure the setup + // works let hand_written_wgsl_pipeline = { let vertex = runtime.device.create_shader_module(wgpu::include_wgsl!( "../src/tutorial/implicit_isosceles_vertex.wgsl" @@ -241,8 +242,8 @@ async fn implicit_isosceles_triangle() { .create_shader_module(wgpu::include_wgsl!("../src/tutorial/passthru.wgsl")); create_pipeline(runtime, &vertex, "main", &fragment, "main") }; - // The second time render with WGSL that is transpiled from Rust code and pulled - // in through the renderling linkage machinery. + // The second time render with WGSL that is transpiled from Rust code and + // pulled in through the renderling linkage machinery. let linkage_pipeline = { let vertex = renderling::linkage::implicit_isosceles_vertex::linkage(&runtime.device); let fragment = renderling::linkage::passthru_fragment::linkage(&runtime.device);