Skip to content

Reduce technical debt: shared logic, narrower APIs, file splits - #210

Merged
doubleailes merged 6 commits into
mainfrom
claude/tech-debt-cleanup
Oct 5, 2026
Merged

doubleailes merged 6 commits into
mainfrom
claude/tech-debt-cleanup

Conversation

@doubleailes

@doubleailes doubleailes commented Oct 5, 2026 •

Copy link
Copy Markdown
Owner

Summary

Pays off the technical-debt list in four commits: duplicated logic first, then rule violations and error-prone APIs, then the long functions and files.

Output. Every sample renders bit-identical at 16 spp except cornellbox_guided (see Intentional behaviour changes). Workspace tests pass; fmt and clippy (-D warnings, also with traversal-stats) are clean.

1. Duplicated logic — USD import and texture caches

  • attrs.rs: one value_at / prim_value plus decode_* helpers replace ~45 hand-written get_at::<sdf::Value> matches.
  • prune_reason(): the abstract / inactive / purpose / invisible rule, written once and shared by the traversal, the placement count and the prototype walk.
  • compose_with_parent(): the reset-xform-stack rule in one place.
  • assets.rs: one asset-path rule and one memoized, timed cached_asset() for every host decode.
  • CameraFrame: the one camera read that both build_camera and screen_projection derive from.
  • existing_tiles(): the one 10×10 UDIM sweep, used by preload, streaming, .tx conversion and maketx.
  • texture_cache.rs: the FIFO microcache set and MiB conversion, shared by the .tx cache and Ptex streaming. The unused legacy *_from_env wrappers are removed.

2. Duplicated logic — integrator and fixtures

  • russian_roulette(), utils::exp3, TRACE_T_MIN, and surface_visibility() (shared by NEE and the learned light cache's training).
  • LightList::pick_index_at is forced inline; without it LLVM outlined it and cornellbox ran +0.6% instructions.
  • crust-rt/fixtures/ holds the scenes and ray batch the probes and the bench copied. mtlx_bench and jit_bench share bench_common.

3. Rule violations and error-prone APIs

  • Narrower public APIs:
    • crust-mtlx modules are private, so each item has one path, at the root.
    • crust-assets tiled is private; make_tx, make_tx_atomic, TxFormat and MadeTx are re-exported at the root.
    • crust-core: subsurface and commit_options are private, and the five LightList lookups only tests used are removed.
  • CRUST_BVH_PACKET_SAH is retired; its A/B is settled in the intersection-kernel design record.
  • RenderSettings::new (seven positional numbers) becomes Default plus named builders.
  • HitRecord.uv is an Option (replacing uv plus has_uv), and RayStats::merge destructures its fields.
  • Guiding draws from domain.rng() instead of a hand-rolled PCG32.
  • Two misplaced doc comments fixed.

4. Splits

  • subdiv.rs, crust-mtlx eval.rs and surface.rs, and crust-rt scene.rs become directories, with their tests in their own files.
  • Long functions broken up: tessellate_adaptive, the stats Display, closure::prepare, mesh_source, load_scene, main().
  • trace_path takes a PathContext (12 → 6 parameters).
  • CLI: logging.rs and products.rs split out of main.rs; the traversal-stats table moves to crust_core::traversal_report.

Intentional behaviour changes

  • Guided renders change, by noise only. The quadtree descent now uses the K_GUIDE domain's rng() (CLAUDE.md: no RNG outside openqmc). Against the old stream, cornellbox_guided relmse is 3.1e-2 / 2.1e-2 / 7.6e-3 at 16 / 64 / 256 spp (--indirect-clamp 0), with no plateau. Old and new renders sit equally far from an unguided reference. Re-record the cornellbox_guided golden.
  • Benchmark baselines must be re-recorded. The ray_throughput / traversal_probe / criterion fixtures now draw from openqmc::pcg::Rng instead of an inline LCG (the same rule), so their rays and soup/instance-field scenes differ from earlier runs.
  • CRUST_BVH_PACKET_SAH is gone. The per-triangle leaf cost is no longer selectable for all-triangle ranges; ranges holding other primitives keep it. Docs, site page and specs are updated.
  • Public module paths changed (crust_mtlx::parse::Doc → crust_mtlx::Doc, etc.). Every crate is publish = false and every in-workspace caller is updated.
  • Asset paths for lights: an unresolved relative path on an IES file, rect-light texture or dome texture is anchored against the layer that authored it, as textures already were.
  • Wider value decoding: every float reader accepts half; OpenPBR shader inputs accept vector3d and int-written bools.
  • crust:samplesPerPixel = 0 now renders 1 sample instead of 0.

Bug fixed

crust-rt commit: a geometry the build skips (an invalid disk or cylinder, or an instance of an empty scene) pushed no GeomTable. The tables are indexed by geom_id, so every later triangle mesh read its neighbour's bases, and the last one indexed past the end and panicked. Pinned by a_skipped_geometry_keeps_its_table_slot.

Measurements (callgrind, 2 spp)

  • cornellbox: −0.03% instructions overall.
  • materialx_surfaces: +0.055%, from the prepare_* split.
  • Passing &PathContext to out-of-line helpers cost 0.08% (by value, 0.23%), so it is kept to the inlined trace_path; the trap is documented on the type.

Not run here

  • The nightly bvh8 leg (no nightly toolchain installed).
  • The Zola site build (the env-var page changed).
  • cargo deny (no dependency changes).

🤖 Generated with Claude Code

https://claude.ai/code/session_019nwge6NCTPhuk1VPuZucRF

claude added 4 commits October 5, 2026 08:58
- attrs.rs: one value_at/prim_value plus decode_* helpers replace ~45
  hand-written get_at::<sdf::Value> matches. Every f32 reader now accepts
  Half, the OpenPBR shader inputs accept Vec3d and int-authored bools.
- prune_reason(): the abstract/inactive/purpose/invisible rule written
  once, shared by the traversal, the placement count and the prototype walk.
- compose_with_parent(): the reset-xform-stack rule at one site.
- assets.rs: one asset-path rule (authoring-layer anchoring everywhere,
  lights included) and one memoized, timed cached_asset() for every host
  decode.
- camera.rs: CameraFrame, the one read build_camera and
  screen_projection both derive from.
- existing_tiles(): the one 10x10 UDIM sweep (preload, streaming, .tx
  conversion, maketx); StreamingTexture::open loses its expand callback.
- texture_cache.rs: the FIFO microcache set and MiB conversion shared by
  the .tx tile cache and Ptex streaming; drop the unused legacy env
  wrappers (ptex_*_from_env, ptex_stream_enabled, budget_from_env).

All sample scenes bit-identical at 16 spp.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019nwge6NCTPhuk1VPuZucRF
- russian_roulette(): the survival test the surface bounce, the region
  phase scatter and the carried-medium scatter each pasted.
- utils::exp3 replaces six per-component exp copies.
- TRACE_T_MIN (crust_core::ray) names the 0.001 every trace asks for.
- surface_visibility(): the one shadow-ray visibility (cutouts included)
  NEE and the learned light cache's training share, replacing the
  cache's own copy of the occluded/cutout logic.
- LightList::pick_index_at forced inline: LLVM outlined it once
  trace_path grew, +0.6% instructions on cornellbox; inlined, the render
  runs 0.01% fewer than before this change (callgrind, 2 spp).
- crust-rt: fixtures/mod.rs holds the uv_sphere, the three scenes and
  the ray batch ray_throughput, traversal_probe and the criterion bench
  shared by copy; their LCGs become openqmc::pcg::Rng. ray_throughput
  now reads --layout before building the default scenes.
- mtlx_bench and jit_bench share bench_common (texture, points, A/B
  timer, material walk).

All sample scenes bit-identical at 16 spp.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019nwge6NCTPhuk1VPuZucRF
- LightList: drop find_by_geom, pick_at, find_by_geom_at, iter_at and
  infinite_seen_by, which only tests used; tests read the index forms.
- Retire CRUST_BVH_PACKET_SAH (its A/B is settled in the design record):
  packet-sized leaves are the only rule for all-triangle ranges, the
  per-primitive cost stays for ranges holding anything else. Removed from
  Config, CommitOptions and the builder; docs, specs and site updated.
- crust-core: subsurface and commit_options are crate-private.
- crust-mtlx: modules are private, every item has one path at the root
  (the nodedef tables and parse_literal/arity_of the tests use included).
- crust-assets: tiled is private; make_tx/make_tx_atomic/TxFormat/MadeTx
  re-exported at the root, and the dead accessors that exposed removed.
- RenderSettings::new(seven positional numbers) becomes Default plus
  with_resolution / with_max_depth / with_adaptive_sampling builders.
- HitRecord carries uv: Option<(f32, f32)> instead of uv plus has_uv.
- RayStats::merge destructures, so a new counter cannot be forgotten.
- Guiding's quadtree descent draws from the K_GUIDE domain's rng()
  instead of a hand-rolled PCG32. cornellbox_guided changes by noise only:
  relmse 3.1e-2 / 2.1e-2 / 7.6e-3 at 16/64/256 spp against the old
  stream, equally far from an unguided reference. Every other sample is
  bit-identical.
- Doc comments moved to their items (sample_bounce_direction in path.rs,
  triangulate in mesh.rs); tex_probe's gamma-2.2 lines say so.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019nwge6NCTPhuk1VPuZucRF
- scene/subdiv.rs (2,406 lines) -> subdiv/{mod,topology,uniform,adaptive,
  normals,tests}.rs; tessellate_adaptive (588 lines) becomes a driver over
  Cage, EdgeRating and Emitted, with tessellate_selected and
  tessellate_unselected for its two phases.
- stats.rs: the 687-line Display::fmt dispatches to write_scene /
  write_rays / write_textures / write_ptex / write_phases.
- closure::prepare: one prepare_* builder per BSDF kind over LeafInputs.
- usd_import/mesh.rs: mesh_source takes MeshNeeds (5 parameters, not 8)
  and delegates to effective_scheme, vtx_boundary, SharpnessArrays,
  refinable_chart and MeshSource::{tessellated,subdivided}.
- usd_import load_scene: ValidOptions, traverse_stage, resolve_camera and
  record_geometry_counters taken out; ImageCounters reuses its From impl.
- trace_path takes a PathContext (6 parameters, not 12). Kept to the
  inlined function only: handing &PathContext to the out-of-line helpers
  cost cornellbox 0.08% (by value, 0.23%); as committed it runs 0.03%
  fewer instructions than before (callgrind, 2 spp).
- crust-mtlx: eval.rs -> eval/{mod,apply,compile,tests}.rs; surface.rs ->
  surface/{mod,open_pbr,standard_surface,gltf_pbr}.rs.
- crust-rt scene.rs -> scene/{mod,tests}.rs; commit_with sizes the arrays
  in sized_primitives and expands each geometry through Expansion::add.
  Fixes a latent bug found on the way: a skipped geometry (invalid disk or
  cylinder, instance of an empty scene) pushed no GeomTable, but the
  tables are indexed by geom_id, so every later triangle mesh read its
  neighbour's bases and the last one indexed past the end (panic);
  pinned by a_skipped_geometry_keeps_its_table_slot.
- crust-render: logging setup -> logging.rs, RenderProducts ->
  products.rs, the traversal-stats table -> crust_core::traversal_report;
  main() loses load_scene / apply_overrides / select_products (212 lines).

Every sample bit-identical at 16 spp; workspace tests pass. MaterialX
shading costs +0.055% instructions on materialx_surfaces (prepare_*).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019nwge6NCTPhuk1VPuZucRF
@doubleailes
doubleailes force-pushed the claude/tech-debt-cleanup branch from 95a6234 to 8328036 Compare October 5, 2026 08:59
@qodo-code-review

Copy link
Copy Markdown
Contributor

PR Summary by Qodo

Modularize renderer internals and consolidate shared rendering logic

✨ Enhancement 🐞 Bug fix 🧪 Tests 📝 Documentation ⚙️ Configuration changes 🕐 40+ Minutes

Grey Divider

AI Description

• Split MaterialX, subdivision, ray-scene, logging, and EXR code into focused modules.
• Consolidate USD value decoding, asset loading, texture caches, and integrator logic.
• Replace legacy switches and constructors; update callers, tests, benchmarks, and documentation.
Diagram

graph TD
  CLI["Render CLI"] --> USD["USD importer"] --> RT["Ray scene"] --> Integrator["Path integrator"] --> MaterialX["MaterialX modules"]
  USD --> Assets["Asset loading"]
  CLI --> Logging["Logging module"]
  CLI --> Products["EXR products"]
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Stage independent refactors
  • ➕ Smaller reviews and easier regression isolation across crates.
  • ➖ Repeated compatibility work and more intermediate API transitions.
2. Move code without consolidating behavior
  • ➕ Lower immediate risk to sampling, decoding, and public APIs.
  • ➖ Retains duplicated rules and postpones the main maintenance benefit.

Recommendation: The focused modules and shared helpers are appropriate, but this is broader than a move-only refactor: constructors, public paths, RNG use, and the BVH switch change. If the work can be separated, staged PRs would make behavioral verification easier; otherwise review those changes independently of the large file moves.

Files changed (124) +11704 / -3646

Enhancement (2) +164 / -109
attrs.rsCentralize time-sampled USD decoding +127/-89

Centralize time-sampled USD decoding

• Adds value readers and typed decoders, including half-float and alternate authored-value support.

crates/crust-core/src/scene/usd_import/attrs.rs

settings.rsReplace positional render settings constructor +37/-20

Replace positional render settings constructor

• Adds Default and named builders for depth and adaptive sampling, replacing RenderSettings::new.

crates/crust-core/src/tracer/settings.rs

Refactor (77) +8674 / -3316
lib.rsWire shared texture-cache helpers +29/-40

Wire shared texture-cache helpers

• Integrates the common cache module and adjusts asset-loading exports and callers.

crates/crust-assets/src/lib.rs

ptex_stream.rsReuse streaming cache primitives +21/-52

Reuse streaming cache primitives

• Moves Ptex microcache and budget handling onto shared texture-cache helpers; removes legacy environment wrappers.

crates/crust-assets/src/ptex_stream.rs

texture_cache.rsShare FIFO cache sets and MiB conversions +78/-0

Share FIFO cache sets and MiB conversions

• Introduces a small fixed-size FIFO set and budget conversions used by Ptex and tiled textures, with unit tests.

crates/crust-assets/src/texture_cache.rs

cache.rsReuse common tile-cache bookkeeping +13/-58

Reuse common tile-cache bookkeeping

• Replaces duplicated microcache and memory-unit logic with the shared helpers.

crates/crust-assets/src/tiled/cache.rs

mod.rsAdjust tiled-texture module wiring +1/-4

Adjust tiled-texture module wiring

• Updates imports and exports for the shared cache and tile-discovery code.

crates/crust-assets/src/tiled/mod.rs

stream.rsUnify streamed tile discovery +35/-88

Unify streamed tile discovery

• Uses the common UDIM sweep and removes the expansion callback from StreamingTexture::open.

crates/crust-assets/src/tiled/stream.rs

mod.rsReuse common UDIM discovery +9/-31

Reuse common UDIM discovery

• Routes preloading and conversion through the shared tile enumeration.

crates/crust-assets/src/uv_texture/mod.rs

udim.rsAdd one UDIM tile sweep +32/-7

Add one UDIM tile sweep

• Introduces existing_tiles to enumerate on-disk UDIM and UVTILE files in consistent order.

crates/crust-assets/src/uv_texture/udim.rs

integrator.rsSimplify integrator benchmark setup +11/-19

Simplify integrator benchmark setup

• Reuses the revised render settings and benchmark setup APIs.

crates/crust-core/benches/integrator.rs

dtree.rsUse the sampler RNG for guide descent +13/-28

Use the sampler RNG for guide descent

• Replaces a private PCG implementation and two-float seed with the existing openqmc RNG stream.

crates/crust-core/src/guiding/dtree.rs

field.rsPass RNG through guiding field +6/-5

Pass RNG through guiding field

• Adapts guiding sampling to the direction tree’s RNG-based interface.

crates/crust-core/src/guiding/field.rs

sdtree.rsAdapt spatial guiding sampling +1/-1

Adapt spatial guiding sampling

• Forwards the RNG to the direction-tree sampler.

crates/crust-core/src/guiding/sdtree.rs

hittable.rsConsolidate trace near bounds +5/-10

Consolidate trace near bounds

• Uses the common trace near-bound rule instead of repeated literals.

crates/crust-core/src/hittable.rs

lib.rsTighten core exports and commit options +10/-8

Tighten core exports and commit options

• Removes the retired BVH option, narrows internal exports, and exposes the shared trace bound.

crates/crust-core/src/lib.rs

list.rsPrefer indexed light lookup paths +19/-59

Prefer indexed light lookup paths

• Removes redundant borrowed-light lookup variants and keeps indexed selection for integrator hot paths.

crates/crust-core/src/light/list.rs

light_cache.rsReduce light-cache duplication +10/-17

Reduce light-cache duplication

• Consolidates repeated cache handling and aligns lookup callers with indexed selection.

crates/crust-core/src/light_cache.rs

mod.rsExtract BSDF leaf preparation helpers +319/-230

Extract BSDF leaf preparation helpers

• Breaks a large closure-preparation match into lobe-specific helpers sharing leaf inputs.

crates/crust-core/src/material/closure/mod.rs

mx.rsAlign MaterialX closure helpers +4/-3

Align MaterialX closure helpers

• Adjusts closure helper usage to the extracted preparation code.

crates/crust-core/src/material/closure/mx.rs

material.rsUse reorganized material exports +1/-1

Use reorganized material exports

• Updates an import after MaterialX module visibility changes.

crates/crust-core/src/material/material.rs

materialx.rsUse root MaterialX API +1/-1

Use root MaterialX API

• Repoints an import to the crate-root re-export.

crates/crust-core/src/material/materialx.rs

preview_surface.rsAlign preview-surface helpers +2/-3

Align preview-surface helpers

• Updates shared helper use following the material refactor.

crates/crust-core/src/material/preview_surface.rs

medium.rsShare transmittance math +1/-1

Share transmittance math

• Uses the common vector exponential helper.

crates/crust-core/src/medium.rs

ray.rsDefine a shared trace near bound +11/-0

Define a shared trace near bound

• Adds TRACE_T_MIN for closest-hit and shadow traces.

crates/crust-core/src/ray.rs

rt_world.rsAlign world traversal interfaces +5/-6

Align world traversal interfaces

• Updates ray queries and scene-commit wiring for consolidated bounds and BVH options.

crates/crust-core/src/rt_world.rs

adaptive.rsIsolate adaptive subdivision +904/-0

Isolate adaptive subdivision

• Moves screen-rate adaptive tessellation from the former subdivision monolith.

crates/crust-core/src/scene/subdiv/adaptive.rs

mod.rsDefine subdivision module interface +172/-0

Define subdivision module interface

• Holds shared refinement requests and results while wiring adaptive, uniform, topology, normals, and tests.

crates/crust-core/src/scene/subdiv/mod.rs

normals.rsIsolate subdivision normal generation +66/-0

Isolate subdivision normal generation

• Moves smooth cage and refined-mesh normal calculations into a focused module.

crates/crust-core/src/scene/subdiv/normals.rs

topology.rsIsolate subdivision topology handling +166/-0

Isolate subdivision topology handling

• Moves topology and face-mapping support out of the subdivision monolith.

crates/crust-core/src/scene/subdiv/topology.rs

uniform.rsIsolate uniform refinement +243/-0

Isolate uniform refinement

• Moves uniform cage refinement into its own module.

crates/crust-core/src/scene/subdiv/uniform.rs

assets.rsCentralize USD asset resolution and caching +169/-0

Centralize USD asset resolution and caching

• Adds shared authored-path resolution, timed host loads, and positive or negative caching for UV textures and Ptex.

crates/crust-core/src/scene/usd_import/assets.rs

camera.rsShare camera interpretation +70/-52

Share camera interpretation

• Uses one CameraFrame read for render-camera construction and adaptive-subdivision projection.

crates/crust-core/src/scene/usd_import/camera.rs

instancing.rsReuse importer rules in prototypes +26/-73

Reuse importer rules in prototypes

• Routes prototype traversal through common pruning, transforms, mesh needs, and typed array decoding.

crates/crust-core/src/scene/usd_import/instancing.rs

light_links.rsSimplify light-link attribute reads +10/-23

Simplify light-link attribute reads

• Uses common importer value readers instead of repeated attribute decoding.

crates/crust-core/src/scene/usd_import/light_links.rs

lights.rsReuse asset and light-value readers +60/-99

Reuse asset and light-value readers

• Routes light maps and profiles through shared path and cache rules and uses common color decoding.

crates/crust-core/src/scene/usd_import/lights.rs

materials.rsDelegate material assets and values +25/-227

Delegate material assets and values

• Removes local path and asset-cache logic in favor of shared importer helpers; broadens accepted shader input representations.

crates/crust-core/src/scene/usd_import/materials.rs

mesh.rsConsolidate mesh needs and decoding +277/-220

Consolidate mesh needs and decoding

• Groups material-driven mesh requirements, shares array decoders, and centralizes tessellation counter updates.

crates/crust-core/src/scene/usd_import/mesh.rs

mod.rsShare traversal pruning and transforms +299/-227

Share traversal pruning and transforms

• Wires asset helpers and uses common pruning and parent-transform rules in traversal and placement counting.

crates/crust-core/src/scene/usd_import/mod.rs

preview.rsReuse preview-material decoders +11/-49

Reuse preview-material decoders

• Replaces repeated USD value matches with shared attribute and numeric decoding.

crates/crust-core/src/scene/usd_import/preview.rs

products.rsReuse render-product value reads +4/-24

Reuse render-product value reads

• Simplifies product import through common time-sampled attribute decoding.

crates/crust-core/src/scene/usd_import/products.rs

settings.rsBuild settings from shared defaults +19/-30

Build settings from shared defaults

• Uses RenderSettings::default and named builders instead of duplicate defaults and a positional constructor.

crates/crust-core/src/scene/usd_import/settings.rs

shapes.rsReuse shape attribute decoders +9/-42

Reuse shape attribute decoders

• Replaces local shape-value matches with shared importer reads.

crates/crust-core/src/scene/usd_import/shapes.rs

xform.rsCentralize parent-transform composition +29/-38

Centralize parent-transform composition

• Adds a common transform operation that accounts for USD reset-xform-stack behavior.

crates/crust-core/src/scene/usd_import/xform.rs

stats.rsMake counter aggregation more explicit +202/-48

Make counter aggregation more explicit

• Destructures ray counters during merging and consolidates report formatting and traversal-stat handling.

crates/crust-core/src/stats.rs

subsurface.rsUse shared subsurface trace bound +2/-15

Use shared subsurface trace bound

• Removes repeated near-bound handling in subsurface tracing.

crates/crust-core/src/subsurface.rs

mod.rsPass shared path context +12/-9

Pass shared path context

• Adapts tracer entry points to the integrator’s grouped render-path context.

crates/crust-core/src/tracer/mod.rs

path.rsConsolidate path sampling rules +159/-130

Consolidate path sampling rules

• Shares Russian roulette across vertex types, groups stable path inputs, and reuses common tracing and transmittance helpers.

crates/crust-core/src/tracer/path.rs

volume.rsReuse vector exponential helper +1/-1

Reuse vector exponential helper

• Switches volume math to the common helper.

crates/crust-core/src/volume.rs

world.rsAlign world construction with scene APIs +5/-1

Align world construction with scene APIs

• Updates scene-commit and world-building calls for the simplified options.

crates/crust-core/src/world.rs

jit_bench.rsShare MaterialX benchmark setup +23/-89

Share MaterialX benchmark setup

• Removes duplicate example setup in favor of common benchmark fixtures.

crates/crust-jit/examples/jit_bench.rs

mod.rsShare MaterialX benchmark fixtures +87/-0

Share MaterialX benchmark fixtures

• Adds reusable graph and shading setup for MaterialX and JIT benchmarks.

crates/crust-mtlx/examples/bench_common/mod.rs

mtlx_bench.rsUse shared MaterialX benchmark fixture +19/-88

Use shared MaterialX benchmark fixture

• Replaces locally duplicated benchmark setup with the common module.

crates/crust-mtlx/examples/mtlx_bench.rs

apply.rsIsolate MaterialX interpreter +517/-0

Isolate MaterialX interpreter

• Moves operation application and evaluation math into a dedicated module.

crates/crust-mtlx/src/eval/apply.rs

compile.rsIsolate MaterialX compiler +680/-0

Isolate MaterialX compiler

• Moves named-graph compilation and slot assignment into a dedicated module.

crates/crust-mtlx/src/eval/compile.rs

mod.rsDefine MaterialX evaluation interface +483/-0

Define MaterialX evaluation interface

• Holds program, operation, and shading-context types and wires compiler, interpreter, and tests.

crates/crust-mtlx/src/eval/mod.rs

lib.rsConsolidate MaterialX public paths +20/-11

Consolidate MaterialX public paths

• Makes implementation modules private and re-exports their intended public items at the crate root.

crates/crust-mtlx/src/lib.rs

parse.rsAlign parser imports +1/-1

Align parser imports

• Updates a reference after MaterialX module visibility changes.

crates/crust-mtlx/src/parse.rs

gltf_pbr.rsIsolate glTF PBR expansion +259/-0

Isolate glTF PBR expansion

• Moves the glTF PBR nodegraph expansion into its own surface module.

crates/crust-mtlx/src/surface/gltf_pbr.rs

mod.rsDefine shared surface infrastructure +525/-0

Define shared surface infrastructure

• Holds surface inputs and closure-building support and wires the three surface implementations.

crates/crust-mtlx/src/surface/mod.rs

open_pbr.rsIsolate OpenPBR expansion +332/-0

Isolate OpenPBR expansion

• Moves OpenPBR’s MaterialX nodegraph correspondence into a focused module.

crates/crust-mtlx/src/surface/open_pbr.rs

standard_surface.rsIsolate Standard Surface expansion +251/-0

Isolate Standard Surface expansion

• Moves Standard Surface’s nodegraph expansion into a focused module.

crates/crust-mtlx/src/surface/standard_surface.rs

light_occlusion.rsAdapt light-occlusion example +11/-7

Adapt light-occlusion example

• Updates scene and light API usage.

crates/crust-render/examples/light_occlusion.rs

maketx.rsUse shared tile discovery in maketx +4/-15

Use shared tile discovery in maketx

• Removes duplicated UDIM enumeration in the conversion example.

crates/crust-render/examples/maketx.rs

mtlx_shade.rsAdapt MaterialX shading example +1/-2

Adapt MaterialX shading example

• Uses MaterialX crate-root exports.

crates/crust-render/examples/mtlx_shade.rs

tex_probe.rsAdapt texture probe +2/-2

Adapt texture probe

• Updates the streaming-texture open call.

crates/crust-render/examples/tex_probe.rs

logging.rsExtract CLI logging configuration +268/-0

Extract CLI logging configuration

• Owns tracing layers, log files, level handling, and the stats-target exemption, with focused tests.

crates/crust-render/src/logging.rs

main.rsDelegate logging and product output +114/-809

Delegate logging and product output

• Removes inline subscriber and EXR-product logic from the CLI entry point and calls dedicated modules.

crates/crust-render/src/main.rs

products.rsExtract render-product EXR writer +378/-0

Extract render-product EXR writer

• Writes named AOV channels and color metadata for render products in a dedicated module.

crates/crust-render/src/products.rs

traversal.rsUse shared traversal fixtures +16/-116

Use shared traversal fixtures

• Replaces duplicated benchmark scene and ray construction with common fixtures.

crates/crust-rt/benches/traversal.rs

ray_throughput.rsUse shared ray throughput fixtures +26/-133

Use shared ray throughput fixtures

• Removes locally duplicated scene and ray setup.

crates/crust-rt/examples/ray_throughput.rs

traversal_probe.rsUse shared traversal setup +17/-35

Use shared traversal setup

• Reuses the common ray-scene fixtures in the probe example.

crates/crust-rt/examples/traversal_probe.rs

mod.rsShare ray and scene fixtures +129/-0

Share ray and scene fixtures

• Adds reusable deterministic geometry and ray construction for traversal benchmarks and examples.

crates/crust-rt/fixtures/mod.rs

build.rsAlways apply packet-aware triangle leaf costs +14/-23

Always apply packet-aware triangle leaf costs

• Removes the packet-SAH parameter and uses packet-sized costs for all-triangle BVH ranges.

crates/crust-rt/src/bvh/build.rs

lane_width.rsAlign lane-width helpers +2/-2

Align lane-width helpers

• Adjusts BVH packet helper usage for the simplified build policy.

crates/crust-rt/src/bvh/lane_width.rs

mod.rsSimplify BVH build wiring +2/-2

Simplify BVH build wiring

• Drops propagation of the retired packet-SAH option.

crates/crust-rt/src/bvh/mod.rs

mod.rsSeparate ray-scene implementation from tests +908/-0

Separate ray-scene implementation from tests

• Moves the scene builder and committed-scene implementation into the module directory and simplifies commit options.

crates/crust-rt/src/scene/mod.rs

common.rsAdd shared vector exponential +7/-0

Add shared vector exponential

• Introduces exp3 for repeated per-component exponential calculations.

crates/utils/src/common.rs

lib.rsExport shared math helper +1/-1

Export shared math helper

• Makes the common vector exponential available to renderer crates.

crates/utils/src/lib.rs

Tests (26) +2812 / -149
auto_tx.rsAdapt automatic TX test +1/-1

Adapt automatic TX test

• Updates the test to the revised streaming-texture interface.

crates/crust-assets/tests/auto_tx.rs

wide_gamut.rsAdapt wide-gamut texture test +1/-1

Adapt wide-gamut texture test

• Updates a test caller for the revised texture interface.

crates/crust-assets/tests/wide_gamut.rs

tests.rsExercise indexed light selection +11/-13

Exercise indexed light selection

• Updates light-list tests for the retained index-based interfaces.

crates/crust-core/src/light/tests.rs

tests.rsMove subdivision test suite +1069/-0

Move subdivision test suite

• Preserves the subdivision tests in the new module layout.

crates/crust-core/src/scene/subdiv/tests.rs

aovs.rsAdapt AOV render setup +6/-2

Adapt AOV render setup

• Migrates test render settings to named builders.

crates/crust-core/tests/aovs.rs

camera_buffer_ray.rsAdapt camera and ray test imports +1/-2

Adapt camera and ray test imports

• Updates test usage of reorganized public APIs.

crates/crust-core/tests/camera_buffer_ray.rs

guiding.rsAdapt path-guiding tests +18/-7

Adapt path-guiding tests

• Updates settings setup and guiding sampling calls for the revised APIs.

crates/crust-core/tests/guiding.rs

guiding_field.rsAdapt guiding-field tests +16/-7

Adapt guiding-field tests

• Uses RNG-backed guide sampling in field tests.

crates/crust-core/tests/guiding_field.rs

hair.rsAdapt hair rendering test imports +1/-2

Adapt hair rendering test imports

• Updates a test caller for reorganized APIs.

crates/crust-core/tests/hair.rs

learned_selection.rsExercise indexed learned selection +20/-19

Exercise indexed learned selection

• Updates learned-light-selection tests for retained index-based lookups.

crates/crust-core/tests/learned_selection.rs

lights.rsAdapt light tests +7/-7

Adapt light tests

• Uses revised light-list selection and render setup interfaces.

crates/crust-core/tests/lights.rs

lpe.rsAdapt light-path-expression tests +11/-2

Adapt light-path-expression tests

• Migrates test setup to the named render-settings builders.

crates/crust-core/tests/lpe.rs

mtlx_surfaces.rsAdapt MaterialX surface tests +1/-2

Adapt MaterialX surface tests

• Updates imports for the MaterialX crate-root API.

crates/crust-core/tests/mtlx_surfaces.rs

profile.rsAdapt profiling test setup +5/-1

Adapt profiling test setup

• Uses the revised render-settings construction.

crates/crust-core/tests/profile.rs

render_smoke.rsMigrate renderer smoke tests +69/-14

Migrate renderer smoke tests

• Replaces positional settings construction with explicit defaults and builders across rendering scenarios.

crates/crust-core/tests/render_smoke.rs

resolve.rsAdapt resolve tests +1/-2

Adapt resolve tests

• Updates API usage after the module reorganization.

crates/crust-core/tests/resolve.rs

stats.rsAdapt statistics test setup +5/-1

Adapt statistics test setup

• Uses revised render settings and statistics entry points.

crates/crust-core/tests/stats.rs

usd_inline.rsAdapt inline USD tests +9/-5

Adapt inline USD tests

• Updates scene-test settings and imports for revised construction APIs.

crates/crust-core/tests/usd_inline.rs

usd_scene.rsAdapt USD scene tests +16/-15

Adapt USD scene tests

• Migrates scene tests to named settings builders and reorganized APIs.

crates/crust-core/tests/usd_scene.rs

world_material.rsAdapt material-world tests +9/-9

Adapt material-world tests

• Updates world and material test callers for the new public API paths.

crates/crust-core/tests/world_material.rs

tests.rsMove MaterialX evaluation tests +525/-0

Move MaterialX evaluation tests

• Preserves graph-compilation and evaluation tests under the new module.

crates/crust-mtlx/src/eval/tests.rs

graph.rsAdapt MaterialX graph test +1/-1

Adapt MaterialX graph test

• Uses the revised crate-root API.

crates/crust-mtlx/tests/graph.rs

nodedefs.rsAdapt nodedef correspondence tests +3/-3

Adapt nodedef correspondence tests

• Imports surface and hair definitions through crate-root re-exports.

crates/crust-mtlx/tests/nodedefs.rs

tests.rsUpdate BVH build tests +14/-29

Update BVH build tests

• Adapts tests to unconditional packet-aware triangle leaf sizing.

crates/crust-rt/src/bvh/tests.rs

tests.rsMove ray-scene tests +991/-0

Move ray-scene tests

• Preserves scene construction and traversal tests separately from implementation.

crates/crust-rt/src/scene/tests.rs

kernel.rsAdapt kernel tests +1/-4

Adapt kernel tests

• Updates commit options and scene API callers.

crates/crust-rt/tests/kernel.rs

Documentation (18) +54 / -66
CLAUDE.mdClarify module and import conventions +2/-1

Clarify module and import conventions

• Adjusts repository guidance for the module organization used by the refactor.

CLAUDE.md

architecture.mdReflect reorganized renderer modules +8/-8

Reflect reorganized renderer modules

• Updates architecture descriptions and paths for the new module boundaries.

docs/architecture.md

color_management.mdUpdate color-management references +1/-1

Update color-management references

• Adjusts references to reorganized asset and rendering code.

docs/color_management.md

light_sampling.mdDocument retained light-selection APIs +4/-3

Document retained light-selection APIs

• Aligns light-sampling documentation with index-based selection.

docs/light_sampling.md

rust_leverage.mdRefresh Rust architecture reference +1/-1

Refresh Rust architecture reference

• Updates a code reference after the refactor.

docs/rust_leverage.md

shading_performance.mdRefresh shading code references +1/-1

Refresh shading code references

• Points performance guidance to the reorganized shading modules.

docs/shading_performance.md

simd.mdRefresh SIMD traversal reference +1/-1

Refresh SIMD traversal reference

• Updates a traversal reference for the new module layout.

docs/simd.md

design.mdRemove retired BVH tuning example +0/-5

Remove retired BVH tuning example

• Drops CLI design instructions for CRUST_BVH_PACKET_SAH.

openspec/specs/cli/design.md

spec.mdAlign CLI spec with current options +4/-5

Align CLI spec with current options

• Updates documented CLI and render-configuration behavior.

openspec/specs/cli/spec.md

design.mdRefresh image-output design references +1/-2

Refresh image-output design references

• Points product-output design documentation to the extracted writer.

openspec/specs/image-output/design.md

spec.mdClarify render-product output spec +2/-1

Clarify render-product output spec

• Updates EXR product-output expectations.

openspec/specs/image-output/spec.md

design.mdDocument fixed BVH leaf policy +4/-3

Document fixed BVH leaf policy

• Removes the retired packet-SAH alternative from kernel design guidance.

openspec/specs/intersection-kernel/design.md

spec.mdAlign kernel spec with commit options +7/-11

Align kernel spec with commit options

• Describes the retained packet-layout choices and unconditional leaf sizing.

openspec/specs/intersection-kernel/spec.md

design.mdRefresh light-sampling design +4/-4

Refresh light-sampling design

• Aligns design references with consolidated light selection.

openspec/specs/lighting/design.md

design.mdRefresh MaterialX design paths +6/-5

Refresh MaterialX design paths

• Documents the split evaluation and surface modules.

openspec/specs/materials/design.md

design.mdRefresh rendering design +7/-2

Refresh rendering design

• Aligns integrator and settings descriptions with shared path logic and named builders.

openspec/specs/rendering/design.md

design.mdRefresh USD importer references +1/-1

Refresh USD importer references

• Updates import-design references for consolidated helpers.

openspec/specs/usd-scene-import/design.md

environment-variables.mdRemove retired environment switches +0/-11

Remove retired environment switches

• Drops documentation for legacy cache wrappers and the removed BVH packet-SAH flag.

site/content/docs/reference/environment-variables.md

Other (1) +0 / -6
config.rsRetire BVH packet-SAH switch +0/-6

Retire BVH packet-SAH switch

• Removes CRUST_BVH_PACKET_SAH from configuration; packet-aware triangle leaf sizing is now unconditional.

crates/crust-core/src/config.rs

@qodo-code-review

qodo-code-review Bot commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Remediation recommended

1. Users cannot select the old BVH build ✗ Dismissed
Description
Removing packet_sah from CommitOptions and CRUST_BVH_PACKET_SAH from Config leaves BVH
construction unconditionally using packet-aware split costs for all-triangle ranges. When a caller
previously set the option to false, it can no longer build the per-triangle tree, which can differ
in shape and on exact-tie hits.
Code

crates/crust-rt/src/bvh/build.rs[R324-327]

+    if all_triangles
        && count <= MAX_LEAF
        && let Some(o) = &object
        && !splitting_pays(o, &bbox, count, true)
Relevance

●●● Strong

Removing a public BVH option changes caller-visible behavior and contradicts the claimed absence of
functional changes.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The new commit options contain only layout, the parsed configuration no longer has the switch, and
the all-triangle early-leaf decision calls splitting_pays with true. The removed option
previously selected the other cost rule.

crates/crust-rt/src/scene/mod.rs[50-63]
crates/crust-rt/src/bvh/build.rs[309-327]
crates/crust-rt/src/bvh/build.rs[429-445]
crates/crust-core/src/config.rs[163-171]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The refactor removes the existing per-triangle BVH build option and always applies packet-aware costs to all-triangle ranges.
## Fix Focus Areas
- crates/crust-rt/src/bvh/build.rs[321-327]
- crates/crust-rt/src/scene/mod.rs[50-63]
- crates/crust-core/src/config.rs[163-171]
## Recommended Fix
Retain the packet-SAH option in the scene commit and parsed configuration, and pass it through BVH construction to both split decisions. Keep the current packet-aware behavior as the default.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. The CLI no longer has a single-file layout ✗ Dismissed
Description
main.rs now declares logging and products as separate production modules, leaving
crust-render/src with three Rust source files instead of only main.rs. When maintainers inspect
the CLI under the prescribed single-file layout, they must follow those declarations to find the
logging setup and EXR-writing code.
Code

crates/crust-render/src/main.rs[R7-8]

+mod logging;
+mod products;
Relevance

●●● Strong

A directly matching precedent accepts findings requiring crust-render/src to contain only main.rs.

PR-#123

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
Rule 2230667 explicitly requires crust-render/src to contain only main.rs. The added module
declarations and their new production source files establish that the PR breaks that layout.

Rule 2230667: Restrict crust-render crate responsibilities to orchestration and I/O only
crates/crust-render/src/main.rs[7-8]
crates/crust-render/src/logging.rs[1-2]
crates/crust-render/src/products.rs[1-2]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The CLI now has additional production source files, contrary to the required single-file layout.

## Fix Focus Areas
- crates/crust-render/src/main.rs[7-8]
- crates/crust-render/src/logging.rs[1-2]
- crates/crust-render/src/products.rs[1-2]

## Recommended Fix
Move the extracted logging setup and EXR-writing helpers back into `main.rs`, then remove the two module declarations and their separate files.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Existing library imports stop compiling ✗ Dismissed
Description
Changing the MaterialX modules to private removes paths such as crust_mtlx::parse::Doc, even
though Doc remains available through a new crate-root re-export. Downstream code using those
previously public paths fails to compile; the same change also makes crust_assets::tiled and
crust_core::subsurface private.
Code

crates/crust-mtlx/src/lib.rs[R45-49]

+// Private modules: every public item has exactly one path, at the crate root.
+mod bsdf;
+mod eval;
+mod hair;
+mod parse;
Relevance

●●● Strong

Private modules break previously public import paths, contradicting the PR’s explicit promise to
preserve public APIs.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The branch declares all named MaterialX modules with mod rather than pub mod, while still
re-exporting types such as Doc at the root. It likewise declares the formerly public tiled and
subsurface modules private; crate-root re-exports do not preserve imports through the old module
paths.

crates/crust-mtlx/src/lib.rs[43-52]
crates/crust-mtlx/src/lib.rs[68-71]
crates/crust-assets/src/lib.rs[31-35]
crates/crust-core/src/lib.rs[41-45]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Previously public module paths become private, breaking downstream imports despite the stated goal of preserving public APIs.
## Fix Focus Areas
- crates/crust-mtlx/src/lib.rs[45-52]
- crates/crust-assets/src/lib.rs[31-35]
- crates/crust-core/src/lib.rs[41-45]
## Recommended Fix
Preserve the previously public modules, or provide compatibility paths for their public items. Keep the new crate-root re-exports where useful without requiring existing callers to change imports.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Informational

4. Guided renders no longer match pre-PR output ✗ Dismissed
Description
The guide sampler in path.rs now draws one Sobol dimension from K_GUIDE and then passes
guide_dom.rng() into field.sample. The old code passed Sobol dimensions 1–2 as [gs[1], gs[2]],
and DTree::sample turned those into a hashed PCG seed. Any render with path guiding enabled now
picks different guided directions for the same seed, so its output is no longer bit-identical to
pre-PR images, even though the PR says there are no functional changes.
Code

crates/crust-core/src/tracer/path.rs[R193-199]

+    let guide_dom = sampler.new_domain(K_GUIDE);
+    let gs = guide_dom.draw_sample_f32::<1>();

    if gs[0] < alpha {
        // Guide branch: draw from the field; the material's continuous
        // component supplies the value and the BSDF side of the mixture pdf.
-        if let Some((wi, p_guide)) = g.field.sample(rec.p, [gs[1], gs[2]])
+        if let Some((wi, p_guide)) = g.field.sample(rec.p, &mut guide_dom.rng())
Relevance

●●● Strong

Bit-identical output is explicitly promised; changing the guiding RNG stream is a concrete
behavioral regression.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
Old: draw_sample_f32::<4>() and g.field.sample(rec.p, [gs[1], gs[2]]), with DTree::sample
seeding pcg_f32 from the seed bits XOR 0x9E37_79B9_7F4A_7C15. New: draw_sample_f32::<1>(), then
field.sample(rec.p, &mut guide_dom.rng()), and DTree::sample calls rng.next_2d() at each level
of the tree. Because the random stream is different, each guided sample comes out different.

crates/crust-core/src/tracer/path.rs[188-205]
crates/crust-core/src/guiding/dtree.rs[141-170]
crates/crust-core/src/guiding/field.rs[100-110]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The guiding sampler now uses a different random source: the domain's `rng()` instead of a PCG stream seeded from Sobol dimensions 1–2. Guided renders therefore differ from pre-PR output, while the PR describes itself as a refactor with no functional changes.

## Fix Focus Areas
- crates/crust-core/src/tracer/path.rs[188-205]
- crates/crust-core/src/guiding/dtree.rs[141-170]

## Recommended Fix
If the change is intended, say in the PR description and changelog that guided renders change, and update any reference images. If it is not intended, restore the 4-dimension draw and seed the field sampler from `[gs[1], gs[2]]` as before.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


5. Benchmark rays and scenes differ from earlier runs ✗ Dismissed
Description
The shared ray_batch fixture, along with soup_scene and instance_field_scene in
ray_throughput, now uses openqmc::pcg::Rng instead of the old inline LCG. The seed literals are
the same, but the generator is not. The traversal bench and the throughput example now produce
different rays and different geometry, so their timings and hit counts can't be compared with
numbers recorded before this PR.
Code

crates/crust-rt/fixtures/mod.rs[R120-126]

+pub fn ray_batch(count: usize, extent: f32) -> Vec<Ray> {
+    let mut rng = Rng::new(0x2545_F491);
+    (0..count)
+        .map(|_| {
+            let origin = in_cube(&mut rng, 2.0 * extent);
+            let target = in_cube(&mut rng, 0.5 * extent);
+            Ray::new(origin, (target - origin).normalize())
Relevance

●● Moderate

Benchmark reproducibility concern is plausible, but historical evidence does not establish this
fixture change as team-blocking.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The old traversal.rs and ray_throughput.rs used an LCG (wrapping_mul(1_664_525),
wrapping_add(1_013_904_223)). The new fixture builds Rng::new(seed) and calls
next_2d()/next_f32(), so the same seed gives a different sequence.

crates/crust-rt/fixtures/mod.rs[108-126]
crates/crust-rt/examples/ray_throughput.rs[52-95]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The shared fixtures switched the benchmark RNG from an LCG to PCG, so the generated rays and scenes changed and old benchmark numbers no longer apply.

## Fix Focus Areas
- crates/crust-rt/fixtures/mod.rs[108-126]

## Recommended Fix
Keep the old LCG in the shared fixture so results stay comparable, or state in the PR that benchmark baselines must be re-recorded.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Context sources
✅ Compliance rules (platform): 127 rules
✅ Cross-repo context — repo relationships
  Explored: repo: doubleailes/OpenSubdiv-rs (sha: fca5fe05) — View relationship
Review mode: Auto: 🧠 Deep: This is a very broad refactor spanning multiple logic-heavy subsystems with hundreds of independent edit sites, making subtle regressions likely to be missed in a single review pass.

Grey Divider

Tip of the day
💡 Did you know, you can turn on the rule miner and Qodo learns your standards from review history

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread crates/crust-render/src/main.rs
Comment thread crates/crust-rt/src/bvh/build.rs
Comment thread crates/crust-mtlx/src/lib.rs
Comment thread crates/crust-core/src/tracer/path.rs
Comment thread crates/crust-rt/fixtures/mod.rs
@doubleailes doubleailes changed the title Refactor: modularize eval, surface, and logging code Reduce technical debt: shared logic, narrower APIs, file splits Oct 5, 2026
claude and others added 2 commits October 5, 2026 09:31
Also the docs site's landing-page version badge.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019nwge6NCTPhuk1VPuZucRF
@doubleailes
doubleailes merged commit 381b653 into main Oct 5, 2026
12 checks passed
@doubleailes
doubleailes deleted the claude/tech-debt-cleanup branch October 5, 2026 11:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants