Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/push.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`
4 changes: 2 additions & 2 deletions crates/example-culling/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
});

Expand Down
7 changes: 4 additions & 3 deletions crates/example/src/camera.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
11 changes: 6 additions & 5 deletions crates/example/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 3 additions & 2 deletions crates/renderling-build/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
17 changes: 10 additions & 7 deletions crates/renderling/src/atlas/cpu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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));
Expand Down
3 changes: 2 additions & 1 deletion crates/renderling/src/atlas/shader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
9 changes: 5 additions & 4 deletions crates/renderling/src/bloom/cpu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
10 changes: 5 additions & 5 deletions crates/renderling/src/bloom/shader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
18 changes: 10 additions & 8 deletions crates/renderling/src/cubemap/cpu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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| {
Expand Down
11 changes: 6 additions & 5 deletions crates/renderling/src/cull/cpu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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");

Expand Down Expand Up @@ -962,16 +962,17 @@ 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(..)
})
.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
Expand Down
7 changes: 4 additions & 3 deletions crates/renderling/src/debug.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
9 changes: 5 additions & 4 deletions crates/renderling/src/draw/cpu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -210,8 +210,8 @@ impl DrawCalls {
depth_texture: &Texture,
) -> Result<Option<SlabBuffer<wgpu::Buffer>>, 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}");
Expand All @@ -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");
Expand Down
3 changes: 2 additions & 1 deletion crates/renderling/src/geometry/cpu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}
Expand Down
6 changes: 3 additions & 3 deletions crates/renderling/src/geometry/shader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
12 changes: 7 additions & 5 deletions crates/renderling/src/gltf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::<Vec<_>>();

log::debug!("Loading {} images into the atlas", images.len());
Expand Down Expand Up @@ -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()
Expand Down
12 changes: 6 additions & 6 deletions crates/renderling/src/gltf/anime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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
Expand Down
Loading
Loading