Repository navigation
feat(hq): the game's sky at dusk and night, Chunky's sun by day, fitted lanterns; 0.2.0 publishes the command - #39
Merged
Merged
Conversation
`--tonemap filmic-mid-grey` (`Tonemap::FilmicMidGrey`): Chunky's Hable curve (`HableToneMappingFilter`, `FILMIC_WORLDS` preset) read brighter by the gain that holds linear 0.18 at 0.18 before the display gamma, so 0.18 shows at 0.18^(1/2.2) = 0.458656, the value the game's transfer gives a decoded colour. At the preset it shows at 0.393285. The gain is closed form: with y = 2km and q = m / whiteScale + E/F, h(y) = m / whiteScale is a quadratic in y whose positive root gives k = 1.463761. A curve read k times brighter is the curve with A k^2, B k and W / k (the white held), so exposure 1 still means the light as measured. `HableCurve` (`white_scale`, `h`, `before_gamma`, `brightened`, `mid_grey_gain`) is ported from Delvewright's engine (GPL-3.0, stellarfeline/delvewright at 0fd300da7, `crates/delvec/src/compiler/view/scene.rs`), which writes this curve into every Chunky scene. `Tonemap::Filmic` now evaluates through the same `HableCurve` at the preset (the parameter products in float, the rest in double, as Chunky's `processPixel`), so there is one Hable implementation. Test `the_game_tone_curve_shows_mid_grey_as_the_game`: k within 1e-6 of 1.463761; the curve holds 0.18 within 1e-6; its seven floats are the engine's 0.32138956, 0.7318806, 0.1, 0.2, 0.02, 0.3, 7.651521; it equals the preset at k times the light within 1e-5 relative over 1e-4..7e2; 0.18 shows at the gamma filter's 8-bit level; the preset shows it at 0.393285; W is white. Log: test render::post::tests::downsample_averages ... ok test render::post::tests::tonemap_endpoints ... ok test render::post::tests::the_game_tone_curve_shows_mid_grey_as_the_game ... ok test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 75 filtered out; finished in 0.00s Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016ba8LkuKW2bd5C5HCDo7p2
A new HQ sky model, `HqSkyModel::Game` (`--sky game`), beside Chunky's (`HqSkyModel::Chunky`, `--sky chunky`, still the default in this commit). It ports the method Delvewright's engine lights its Chunky scenes with (GPL-3.0, stellarfeline/delvewright at 0fd300da7: `crates/delvec/src/compiler/view/scene.rs` `lightmap_sky` and `sky_cell`, `crates/delvec/src/compiler/view/sky_image.rs` `sky_map`): - The sun casts no light. The game has no directional light; nothing in its lightmap depends on the sun's direction. A sun of colour 0 now draws no next-event sample in the kernel (CPU, CUDA) and in pathtrace.wgsl. - The sky casts the lightmap's colour for a cell open to it: the 1.21.11 client's `lightmap.fsh` at sky light 15 with `SkyFactor` and `SkyLightColor` the `visual/sky_light_factor` and `visual/sky_light_color` attributes (now read from the jar's timeline, `EnvironmentState`), read at the 8-bit level of the RGBA8 lightmap and decoded with x^2.2. The light keeps the client's sky's directions: it is the client's sky with nothing drawn in it, scaled, plus the least uniform veil that makes its cosine-weighted mean over the upper hemisphere exactly that colour (scale up to Chunky's `Sky.MAX_INTENSITY` 50, the engine's rule). - The lens sees the client's own sky at the tick: the quick-mode sky (the client's `SkyRenderer` and `AtmosphericFogEnvironment.getBaseColor` evaluated per direction) with its disc, fog, sunrise fan, sun, moon and stars, drawn on the host into a 2048 x 1024 equirectangular map. Both maps go to every backend in the existing `sky` buffer; `Params::sky_map` (a new vec4, 416 bytes) carries their sizes. The kernel reads the lens's map before a path's first diffuse bounce and the light's after (`integrator::sky_lens`, `sky_light`, a bilinear `map_lookup`, mirrored line for line in pathtrace.wgsl; CUDA runs the Rust kernel). Two departures from the engine, both because Lumenwright owns its kernel or read the client again: - Chunky feeds one map to the lens and to the light, so the engine's lens sees the veil too (a pale noon zenith). Here only the light carries it. - The engine's clear colour leans toward the sky colour by 1 - clamp(min(512/16, 12)/32, 0.25, 1)^0.25 = 0.218. The client's `getBaseColor` calls `Mth.clampedLerp(x/32, 0.25, 1)`, and clampedLerp's bytecode is (delta, start, end): the weight is 1 - 0.53125^0.25 = 0.146, as Lumenwright's quick sky already had. A test now pins it. The sun is drawn at every altitude, as the client's `renderSunMoonAndStars` draws it (no altitude test in its bytecode); the engine draws it from -4.29°. A jar without environment attributes (before 1.21.11) is refused under the game's sky, naming `--sky chunky`. Tests: kernel `the_sky_maps_are_read_bilinearly` (texel centres, the seam, the poles, the second map), `the_lens_sees_one_map_and_a_bounce_is_lit_by_the_other` (a white top face under a light map of 0.3 shows 0.3 whatever the lens's map holds; a lit sun lights it, a black one does not); host `the_sky_light_is_the_clients_lightmap` (30 points against an f64 transcription of lightmap.fsh within half an 8-bit level; the engine's 0.374446 at tick 12782 within its level), `the_game_sky_casts_the_lightmaps_light` (the cosine-weighted mean of the light's map, integrated a second way through the kernel's lookup on a 720 x 180 grid, is the target within 2e-3), `the_clear_colour_leans_to_the_sky_as_the_client_does`, `day_cycle_layers_dimension_and_timeline` (the two new attributes); Metal parity `gpu_draws_the_game_sky_as_the_cpu_does` and two game-sky cases in `gpu_matches_cpu_in_hq_mode` and `cuda_matches_cpu_in_hq_mode`. Perturbation: pathtrace.wgsl's `sky_light` reading the lens's map reds both Metal tests (26/255 in the sky frame, 13.889% mean luminance on the game day case). Local log (M2 Pro): test tests::the_sky_maps_are_read_bilinearly ... ok test tests::the_lens_sees_one_map_and_a_bounce_is_lit_by_the_other ... ok test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 19 filtered out; finished in 0.00s test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s test assets::environment::tests::colour_helpers_match_argb ... ok test assets::environment::tests::no_attributes_is_none ... ok test render::sky::tests::the_sky_light_is_the_clients_lightmap ... ok test request::tests::the_clear_colour_leans_to_the_sky_as_the_client_does ... ok test assets::environment::tests::day_cycle_layers_dimension_and_timeline ... ok test scene::mesh::tests::lightmap_extremes ... ok game sky at dusk: light [0.13035227, 0.13035227, 0.36859095] cast [0.13037985821113043, 0.13035929900481383, 0.3685891005453283] (scale 2.107643, mean [0.06184742, 0.058470804, 0.10664413], veil [0, 0.0033766143, 0.06823889]) test render::sky::tests::the_game_sky_casts_the_lightmaps_light ... ok test result: ok. 7 passed; 0 failed; 0 ignored; 0 measured; 74 filtered out; finished in 0.05s hq sky: largest channel difference 0/255 between CPU and GPU (mean blue 136.5) test gpu_draws_the_sky_as_the_cpu_does ... ok hq game sky: largest channel difference 0/255 between CPU and GPU (mean red 167.1) test gpu_draws_the_game_sky_as_the_cpu_does ... ok hq showcase: mean luminance differs by 0.001% between CPU and GPU hq showcase: 0.000% of pixels differ by more than 16/255 between CPU and GPU hq showcase-dusk: mean luminance differs by 0.000% between CPU and GPU hq showcase-dusk: 0.000% of pixels differ by more than 16/255 between CPU and GPU hq showcase-sun: mean luminance differs by 0.000% between CPU and GPU hq showcase-sun: 0.000% of pixels differ by more than 16/255 between CPU and GPU test gpu_matches_cpu_in_quick_mode ... ok hq showcase-game-day: mean luminance differs by 0.000% between CPU and GPU hq showcase-game-day: 0.000% of pixels differ by more than 16/255 between CPU and GPU hq showcase-game-dusk: mean luminance differs by 0.000% between CPU and GPU hq showcase-game-dusk: 0.000% of pixels differ by more than 16/255 between CPU and GPU hq block-entities: mean luminance differs by 0.000% between CPU and GPU hq block-entities: 0.000% of pixels differ by more than 16/255 between CPU and GPU hq items: mean luminance differs by 0.000% between CPU and GPU hq items: 0.000% of pixels differ by more than 16/255 between CPU and GPU test gpu_matches_cpu_in_hq_mode ... ok test result: ok. 4 passed; 0 failed; 0 ignored; 0 measured; 10 filtered out; finished in 8.90s Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016ba8LkuKW2bd5C5HCDo7p2
…at shows mid-grey as the game does `--mode hq` now draws `--sky game` unless told `--sky chunky`, and its tone curve defaults by the sky: `filmic-mid-grey` under the game's sky, `gamma` (Chunky's default) under Chunky's. In the library `HqSkyModel::default()` is `Game` (render distance 192, the client's 12 chunks) and `HqOptions::default().tonemap` is `FilmicMidGrey`. Why the default: HQ mode's `--time` is a Minecraft tick, and the game's sky is the only sky that is a function of it. Chunky has no time of day: its Preetham sky keeps a sunset at any sun below the horizon, and the fade to night Lumenwright put under it was Lumenwright's own rule. The game's sky takes every value from the client jar at the tick (the timeline, the lightmap shader, the client's sky drawing), so it holds from noon through dusk to the night plateau with nothing authored but the veil's rule. It is also the look Delvewright's engine renders its Chunky scenes with, which is the standard these renders are compared with. Why Chunky's stays: it is the frame Chunky renders (the speed benchmark and the parity tests compare against Chunky's own scene, a Preetham sky and a sun at 45 degrees), and it is the sky with a sun that casts light and shadow, which a caller who wants Chunky's look, or states a sun direction for its shadows, asks for. Under the game's sky `--sun-intensity` and `--sky-intensity` are refused by name (the game's sun casts no light); `--sun-altitude` still moves the sun drawn in the sky. Tests: the parity tests' Chunky cases (`DAY_SUN`, `DUSK_SUN`, `SUN_IN_VIEW`, the sky-cache frame) now say `--sky chunky`; `cli_png_equals_library_pixels` compares the CLI's default and the library's game sky; new `chunky_scales_are_refused_under_the_game_sky`. Local render tests (M2 Pro): test every_item_definition_in_the_jar_resolves ... ok test cli_info_and_render ... ok test hq_cpu_produces_a_lit_image ... ok test gpu_draws_the_sky_as_the_cpu_does ... ok test gpu_draws_the_game_sky_as_the_cpu_does ... ok test quick_mode_draws_entities ... ok test chunky_scales_are_refused_under_the_game_sky ... ok test quick_top_down_colours_match_terrain ... ok test block_entities_render_in_both_modes ... ok test items_draw_in_both_modes ... ok test mipmaps_calm_distant_textures ... ok test gpu_matches_cpu_in_quick_mode ... ok test sign_text_reads_in_both_modes ... ok test renders_are_deterministic ... ok test gpu_matches_cpu_in_hq_mode ... ok test result: ok. 15 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 11.85s The treehouse camp at dusk (tick 12800, 1280 x 720, four bounces, the engine's emitter settings: intensity 159.003, sampling NONE), Metal at 1024 spp, against the engine's Chunky reference (Chunky 2.5.0-SNAPSHOT.474, 2048 spp, the scene's light and tone written by the engine's own functions at 0fd300da7). SSIM here is held down by the frames' emitter noise at this budget; the converged table is in docs/research/game-sky.md: reference ref/chunky-camp-dusk-2048.png: luma 54.2; sun-band (178, 97, 98) 359° (n=4208); sun-upper (166, 96, 103) 354° (n=7004); away-upper (151, 95, 110) 344° (n=7908) out/base-dusk.png: disp mean 19.87 p90 124 p99 156 SSIM 0.6807 luma 72.8 (ref 54.2); sun-band (249, 221, 123) 47° (n=4208); sun-upper (230, 217, 147) 51° (n=7004); away-upper (147, 145, 115) 56° (n=7908) out/c2-dusk-gamma.png: disp mean 14.45 p90 41 p99 126 SSIM 0.6749 luma 52.1 (ref 54.2); sun-band (205, 94, 57) 15° (n=4208); sun-upper (186, 92, 66) 13° (n=7004); away-upper (168, 92, 76) 10° (n=7908) out/c2-dusk-hable.png: disp mean 11.23 p90 36 p99 86 SSIM 0.6764 luma 51.5 (ref 54.2); sun-band (180, 96, 60) 18° (n=4208); sun-upper (168, 95, 69) 16° (n=7004); away-upper (157, 94, 78) 12° (n=7908) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016ba8LkuKW2bd5C5HCDo7p2
… its measurements docs/research/game-sky.md (new): the method HQ mode's default sky ports from Delvewright's engine, each value cited or authored, the five places Lumenwright departs from it, and the comparison with the engine's Chunky renders. chunky-parity.md now says it covers `--sky chunky`. The README's HQ description, flag table (`--sky`, `--tonemap filmic-mid-grey`, the Chunky-only scales) and limitations, CHANGELOG [Unreleased], and ACKNOWLEDGEMENTS (Delvewright's engine; the grey card) follow. Measured: the treehouse camp camera, CUDA 16384 spp, four bounces, the engine's emitter settings, against Chunky 2.5.0-SNAPSHOT.474 at 2048 spp under the light and tone the engine's own functions (0fd300da7) write. Tool output, cmp2.py: == dusk reference ref/chunky-camp-dusk-2048.png: luma 54.2; sun-band (178, 97, 98) 359° (n=4208); sun-upper (166, 96, 103) 354° (n=7004); away-upper (151, 95, 110) 344° (n=7908) base-dusk.png: disp mean 18.68 p90 124 p99 156 SSIM 0.7799 luma 73.5 (ref 54.2); sun-band (249, 221, 123) 47° (n=4208); sun-upper (230, 217, 147) 51° (n=7004); away-upper (147, 145, 115) 56° (n=7908) base-dusk-midgrey.png: disp mean 14.25 p90 90 p99 106 SSIM 0.7946 luma 68.7 (ref 54.2); sun-band (202, 188, 122) 50° (n=4208); sun-upper (193, 186, 141) 52° (n=7004); away-upper (142, 140, 115) 56° (n=7908) game-dusk-gamma.png: disp mean 9.72 p90 40 p99 126 SSIM 0.8103 luma 56.4 (ref 54.2); sun-band (205, 94, 57) 15° (n=4208); sun-upper (186, 92, 66) 13° (n=7004); away-upper (168, 92, 76) 10° (n=7908) game-dusk.png: disp mean 7.12 p90 35 p99 86 SSIM 0.8125 luma 56.0 (ref 54.2); sun-band (180, 96, 60) 18° (n=4208); sun-upper (168, 95, 69) 16° (n=7004); away-upper (157, 94, 78) 12° (n=7908) == day reference ref/chunky-camp-day-2048.png: luma 105.8; sun-band (213, 212, 204) 53° (n=4208); sun-upper (210, 210, 204) 60° (n=7004); away-upper (212, 211, 204) 52° (n=7908) base-day.png: disp mean 30.84 p90 65 p99 77 SSIM 0.7267 luma 113.4 (ref 105.8); sun-band (154, 172, 185) 205° (n=4208); sun-upper (139, 172, 196) 205° (n=7004); away-upper (147, 175, 195) 205° (n=7908) base-day-midgrey.png: disp mean 33.17 p90 68 p99 75 SSIM 0.7416 luma 110.3 (ref 105.8); sun-band (147, 159, 168) 206° (n=4208); sun-upper (135, 159, 174) 203° (n=7004); away-upper (142, 161, 174) 204° (n=7908) game-day-gamma.png: disp mean 9.67 p90 51 p99 51 SSIM 0.9348 luma 102.4 (ref 105.8); sun-band (182, 208, 255) 219° (n=4208); sun-upper (167, 196, 255) 220° (n=7004); away-upper (176, 203, 255) 219° (n=7908) game-day.png: disp mean 8.37 p90 47 p99 55 SSIM 0.9321 luma 98.3 (ref 105.8); sun-band (166, 181, 204) 216° (n=4208); sun-upper (156, 175, 204) 216° (n=7004); away-upper (162, 179, 204) 216° (n=7908) == the engine's choices put back (a86e929 + veil in the lens + clamp(x,0.25,1)), Metal 256 spp enginelike-dusk.png: disp mean 6.34 p90 30 p99 92 SSIM 0.6631 luma 55.3 (ref 54.2); sun-band (178, 99, 100) 359° (n=4208); sun-upper (166, 97, 105) 353° (n=7004); away-upper (151, 96, 111) 344° (n=7908) enginelike-day.png: disp mean 3.38 p90 13 p99 42 SSIM 0.8351 luma 106.0 (ref 105.8); sun-band (213, 213, 204) 60° (n=4208); sun-upper (210, 210, 204) 60° (n=7004); away-upper (211, 212, 204) 68° (n=7908) == noise floor game-dusk-seed1.png: disp mean 1.45 p90 7 p99 14 SSIM 0.9327 luma 56.0 (ref 56.0); sun-band (180, 96, 60) 18° (n=4208); sun-upper (168, 95, 69) 16° (n=7004); away-upper (157, 94, 78) 12° (n=7908) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016ba8LkuKW2bd5C5HCDo7p2
The README's speed table had no controlled variables: quick mode was timed only on Metal and HQ only on CUDA, day ran at 4096 spp and dusk at 16384, and Chunky was absent. docs/benchmark.md now holds a design written before any run (the design section), the tables measured to it, and the instruments by revision; the README links it in one line, and its two "Measured on" sections and "HQ mode and Chunky on one frame" move there (the anchors resolve on the new page; the old speed table, measured at 0b39f8f, is replaced by this one). # Benchmark design Written before any run of this benchmark. Every number in the tables comes from this design; a cell that cannot be measured is marked n/a with its reason. ## Instruments - Lumenwright: the `lumenwright` command built from `f291590` (the code head of `feat/lw-game-sky`; later commits on the branch are documentation), `--release`; on the PC with `--features cuda` and the PTX from `scripts/build-ptx.sh` at the same revision. - Chunky: `chunky-core-2.5.0-SNAPSHOT.474.g156e2bb.jar` (sha256 `79ea46233cfa4929e738c6074ff8aabf4f8a2e59cfe9925abc3888a782bad15c`) and its libraries, run headless (`-render <scene> -f`), the client jar 1.21.11 (sha1 `ba2df812c2d12e0219c489c4cd9a5e1f0760f5bd`) as its resources. JDK 17 on the Mac, JDK 21 on the PC (what each machine has). - World: the Treehouse Camp save the README's renders use (region files' sha256 list hashes to `526fa9df…`, the same on both machines). - Machines: Mac = Apple M2 Pro (10 CPU cores, 16 GPU cores), macOS; PC = AMD Ryzen 7 7800X3D (16 threads) and RTX 4070 Ti SUPER, Linux under WSL2. - Timers, both recorded for every run: the renderer's own (Chunky's `renderTime`; Lumenwright's `render` stage) and the whole command's wall clock (Python `time.time()` on the Mac, `/proc/uptime` on the PC), which includes reading the world and, for Chunky, starting the JVM and building its octree. - Runs: three per cell, the median reported with the three values. In question 1 the renderers alternate run by run (Chunky, then each Lumenwright backend, three rounds), so a load change falls on all of them alike. - Load: the one-minute load average and the three busiest processes are recorded before every run. Other workers share the Mac; a run is not discarded for load, it is reported with it. ## The frames - **F-chunky**: the whole-camp view, camera 140, 118, 14 looking at 56, 82, 58, vertical field of view 60°, 1280 × 720, 625 chunks (`--render-distance 12`), four bounces (Chunky `rayDepth` 5), Chunky's Preetham sky and a sun at altitude 45° from the south-east (Chunky altitude π/4, azimuth π/4), emitters at intensity 13 sampled `ONE`, gamma tone map, 250 samples per pixel. Lumenwright: `--sky chunky --tonemap gamma --time 4000 --sun-altitude 45 --sun-azimuth 135 --spp 250 --max-bounces 4`. Chunky: `docs/bench/treehouse-camp-bench.json`. It is the one frame both renderers draw by the same algorithm (Chunky cannot draw the game's sky with two maps). - **F-game**: the same camera, resolution, chunks and bounces under Lumenwright's defaults (the game's sky, the mid-grey tone curve, emitters 13 `ONE`), at tick 4000 (day) and 12800 (dusk). - **F-quick**: the same camera, resolution and chunks in quick mode at tick 4000. ## Questions 1. **Lumenwright against Chunky on the same machine.** Changes: the renderer (Chunky on the CPU; Lumenwright on each backend the machine has). Fixed: F-chunky (frame, camera, resolution, 250 spp, four bounces, sky mode, world), the machine; load recorded. Mac: Chunky CPU, Lumenwright Metal, Lumenwright CPU. PC: Chunky CPU, Lumenwright CUDA, Lumenwright CPU, Lumenwright Vulkan if WSL2 offers a hardware Vulkan adapter. 2. **Lumenwright's backends against each other.** Changes: the backend. Fixed: the frame (F-chunky for HQ, the runs of question 1; F-quick for quick mode), the machine; load recorded. HQ: as question 1. Quick: Mac CPU and Metal; PC CPU (the CUDA backend has no rasterizer and uses the CPU's). 3. **How cost scales with samples.** Changes: spp, at 64, 256 and 1024. Fixed: F-game (camera, resolution, bounces, sky mode), the backend (Metal on the Mac, CUDA on the PC), the machine; day and dusk each run at every spp, so they compare at the same spp; load recorded. The renders' quality budgets (4096 and 16384 spp in the README) are not speed cells. ## Tables (bench-table.py over the run logs) Session 1: ### Q1 and Q2 (HQ): F-chunky, 1280 x 720, 250 spp, four bounces | machine | renderer | own timer, median (runs) s | whole command, median (runs) s | 1-min load before each run | |---|---|---|---|---| | Mac | Chunky, CPU | 267.56 (289.74, 209.34, 267.56) | 275.55 (301.44, 213.62, 275.55) | 21.73, 123.60, 142.03 | | Mac | Lumenwright, Metal | 33.54 (33.54, 33.81, 32.03) | 33.98 (33.98, 34.86, 32.52) | 156.00, 99.89, 251.06 | | Mac | Lumenwright, CPU | 211.37 (211.37, 227.01, 146.87) | 211.83 (211.83, 227.35, 147.24) | 149.96, 96.09, 233.06 | | PC | Chunky, CPU | 78.60 (93.18, 77.60, 78.60) | 82.03 (97.69, 81.50, 82.03) | 8.01, 14.40, 14.38 | | PC | Lumenwright, CUDA | 6.14 (6.15, 6.05, 6.14) | 6.74 (6.74, 6.64, 6.74) | 13.98, 15.35, 15.66 | | PC | Lumenwright, CPU | 54.84 (54.67, 54.84, 55.05) | 55.14 (54.97, 55.14, 55.34) | 11.98, 13.14, 13.40 | Ratios (Chunky's median over Lumenwright's, own timer / whole command): - Mac, hq-metal: 7.98x / 8.11x - Mac, hq-cpu: 1.27x / 1.30x - PC, hq-cuda: 12.80x / 12.17x - PC, hq-cpu: 1.43x / 1.49x ### Q2 (quick): F-quick, 1280 x 720 | machine | backend | own timer, median (runs) s | whole command, median (runs) s | 1-min load | |---|---|---|---|---| | Mac | Metal | 0.05 (0.06, 0.04, 0.05) | 0.33 (0.36, 0.33, 0.33) | 204.90, 204.90, 204.90 | | Mac | CPU | 0.11 (0.12, 0.11, 0.09) | 0.39 (0.39, 0.40, 0.37) | 204.90, 204.90, 204.90 | | PC | CPU | 0.14 (0.14, 0.14, 0.13) | 0.48 (0.47, 0.48, 0.48) | 13.84, 13.84, 13.84 | ### Q3: F-game, 1280 x 720, four bounces, own timer (whole command), medians of three, s | spp | Mac Metal, day | Mac Metal, dusk | PC CUDA, day | PC CUDA, dusk | |---|---|---|---|---| | 64 | 5.21 (5.53) | 5.21 (5.54) | 1.07 (1.67) | 1.07 (1.69) | | 256 | 21.29 (22.24) | 21.50 (21.89) | 4.12 (4.73) | 4.12 (4.74) | | 1024 | 83.11 (83.45) | 83.50 (83.86) | 16.31 (16.91) | 16.48 (17.10) | Mac session 2 (Q1 again): | Mac | Chunky, CPU | 208.31 (179.02, 208.31, 277.95) | 212.33 (183.00, 212.33, 285.99) | 16.37, 74.67, 96.94 | | Mac | Lumenwright, Metal | 32.61 (32.34, 32.61, 32.93) | 33.08 (32.70, 33.08, 33.47) | 86.59, 84.84, 77.58 | | Mac | Lumenwright, CPU | 212.86 (131.59, 229.03, 212.86) | 213.39 (132.02, 229.58, 213.39) | 73.67, 75.15, 67.34 | Mac load (one-minute average) was 16 to 251 across both sessions from other work on the machine, so the Mac's CPU rows are reported and not used for a CPU comparison; the PC's load was 8 to 16 on 16 threads. Vulkan on the PC: n/a (WSL2 gives only a software adapter). The F-chunky Metal frame is byte-identical to the frame measured at 0b39f8f: display mean 1.66/255, SSIM 0.9594 against Chunky converged at 4000 spp (Chunky 250 spp: 1.99, 0.9575). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016ba8LkuKW2bd5C5HCDo7p2
…s on lumenwright-kernel 0.1.0 declares rust-version 1.85, but its `if let` chains (integrator.rs) are stable from 1.88. Measured with `cargo +<tc> check -p lumenwright-kernel --ignore-rust-version`: kernel on 1.87: exit=101 error[E0658]: `let` expressions in this position are unstable kernel on 1.88: exit=0 (and on 1.85, on the PC: exit=101, the same error). The library and the command declare 1.90: `cargo +1.90 check` passes for both, and 1.89 is refused (a dependency's own rust-version). The CI comment that said the crates carry no rust-version now says what they carry. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016ba8LkuKW2bd5C5HCDo7p2
…t; the rest has its own pages The README keeps the Treehouse Camp renders and their image licence, Install, a Quick start (one render command and one library snippet), a list of the documentation, and the licence. Everything else moved, its heading text unchanged so its anchor resolves on the page that now holds it: docs/renders.md (how the renders are drawn, each command and time, Get the camp, Render), docs/cli.md, docs/library.md (Using the crate, Library), docs/textures.md, docs/architecture.md, docs/backends.md, docs/testing.md, docs/limitations.md, docs/releasing.md; the benchmark moved to docs/benchmark.md in the previous commit. Install now states only what was checked: - Rust: `cargo +<tc> check` passes for lumenwright-cli (at v0.1.0, which the install command names, and at f291590) and lumenwright on 1.90, and both are refused on 1.89; lumenwright-kernel builds on 1.88 and not 1.87 (its manifest is fixed in the previous commit). Stated per crate. - C compiler: `cargo tree -e normal,build` shows `cc` and `ring` under lumenwright-cli only; none under lumenwright with or without its default features, nor under the kernel. - WSL2 Vulkan: the old text said wgpu reaches the GPU through dzn with WGPU_ALLOW_UNDERLYING_NONCOMPLIANT_ADAPTER=1. On the PC the benchmark ran on, `lumenwright backends` reports "vulkan: unavailable (only a software Vulkan adapter is available)" with and without the flag, so Install says that instead. The library snippet in docs/library.md had lost the `unifont` field the Request carries; it has it again. cli install from the tag is kept; the cli's crates.io publishing is untouched. Every anchor the old README had resolves (linkcheck over the repository's Markdown, code blocks skipped): 66 local links in 17 files, 0 broken #the-treehouse-camp-by-day-and-at-dusk: README.md #image-licence: README.md #install: README.md #using-the-crate: docs/library.md #quick-start: README.md #get-the-camp: docs/renders.md #render: docs/renders.md #textures-the-chunky-approach: docs/textures.md #command-line-render: docs/cli.md #library: docs/library.md #architecture: docs/architecture.md #backends: docs/backends.md #testing: docs/testing.md #measured-on-an-m2-pro-10-cpu-cores-16-gpu-cores: docs/benchmark.md #hq-mode-and-chunky-on-one-frame: docs/benchmark.md #measured-on-an-rtx-4070-ti-super-driver-61692-wsl2: docs/benchmark.md #known-limitations-v01: docs/limitations.md #releasing: docs/releasing.md #license: README.md Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016ba8LkuKW2bd5C5HCDo7p2
The README's commands, unchanged, rendered by f291590 on CUDA (RTX 4070 Ti SUPER, WSL2), now under HQ mode's defaults: the game's sky lit as the client lights the tick and the mid-grey tone curve. Each frame was rendered twice and the two PNGs are byte-identical. The four quick frames, re-rendered by f291590 on Metal, are byte-identical to the committed ones, so they are not in this commit. Held for the owner's look; the contact sheet (main / new / the engine's Chunky reference) is outside the repository. Run log (hq-showcase.sh): load 0.95 1.06 3.08 2/325 3453189 treehouse-camp-day-hq a exit=0 monotonic_wall=66.18 render 65.461s total 66.073s treehouse-camp-day-hq b exit=0 monotonic_wall=66.09 render 65.494s total 65.989s treehouse-camp-day-hq identical treehouse-bridge-day-hq a exit=0 monotonic_wall=90.66 render 90.054s total 90.550s treehouse-bridge-day-hq b exit=0 monotonic_wall=90.64 render 90.046s total 90.541s treehouse-bridge-day-hq identical treehouse-camp-dusk-hq a exit=0 monotonic_wall=262.45 render 261.823s total 262.342s treehouse-camp-dusk-hq b exit=0 monotonic_wall=262.51 render 261.899s total 262.410s treehouse-camp-dusk-hq identical treehouse-bridge-dusk-hq a exit=0 monotonic_wall=360.92 render 360.297s total 360.808s treehouse-bridge-dusk-hq b exit=0 monotonic_wall=360.86 render 360.238s total 360.755s treehouse-bridge-dusk-hq identical ALLDONE exit=0 Quick frames (quick.sh): treehouse-camp-day-quick a exit=0 total 1.003s treehouse-camp-day-quick b exit=0 total 0.494s treehouse-camp-day-quick: the two runs are byte-identical treehouse-camp-day-quick: byte-identical to main's docs/renders/treehouse-camp-day-quick.png treehouse-camp-dusk-quick a exit=0 total 0.409s treehouse-camp-dusk-quick b exit=0 total 0.490s treehouse-camp-dusk-quick: the two runs are byte-identical treehouse-camp-dusk-quick: byte-identical to main's docs/renders/treehouse-camp-dusk-quick.png treehouse-bridge-day-quick a exit=0 total 0.497s treehouse-bridge-day-quick b exit=0 total 0.398s treehouse-bridge-day-quick: the two runs are byte-identical treehouse-bridge-day-quick: byte-identical to main's docs/renders/treehouse-bridge-day-quick.png treehouse-bridge-dusk-quick a exit=0 total 0.328s treehouse-bridge-dusk-quick b exit=0 total 0.428s treehouse-bridge-dusk-quick: the two runs are byte-identical treehouse-bridge-dusk-quick: byte-identical to main's docs/renders/treehouse-bridge-dusk-quick.png sha1 of the committed frames: b95b689c5af298b299c3f9621a025842843c9c0a docs/renders/treehouse-bridge-day-hq.png 876be3b7201f32b33bff0ad3ccf2e0cb606117bb docs/renders/treehouse-bridge-dusk-hq.png 6df75b35196a258954639c49617092dd11b6f41d docs/renders/treehouse-camp-day-hq.png 138a00071454425e9908a185f0107bd974b0ea61 docs/renders/treehouse-camp-dusk-hq.png Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016ba8LkuKW2bd5C5HCDo7p2
…d the game's at dusk and night The owner's ruling: daytime keeps Chunky's sun light and shadows, the look approved before; dusk and night use the game's sky. Both stay selectable. `SkyLook::ByTime` (`--sky auto`, the default) weighs the two looks by where the client's `visual/sky_light_factor` at the tick stands between its night and day values over the day timeline (`sky::day_weight`; `environment::sky_light_factor_range` samples the track at its keyframes). The client's 1.21.11 day timeline marks the day plateau itself: sky_light_factor and sky_light_color hold their day values, and sunrise_sunset_color its zero alpha, from tick 730 to 11270; the factor then falls linearly to 0.24 at 13140, holds it to 22860 and rises back by 730. So the weight is 1 (Chunky's look, byte for byte) on that plateau, 0 (the game's) on the night plateau, and linear in ticks on the ramps. At the camp's dusk tick 12800 it is 0.181818. The mix is authored; its quantity is the client's. The mix is one render: Chunky's sky, read through its cache, is weighted into both of the game's maps (`sky::blend_chunky_sky`), and Chunky's sun lights and is drawn at the same weight. Light transport is linear in the sky's and the sun's radiance, so the frame's radiance is the weighted mix of the two looks'. Each look keeps its own tone curve and emitter intensity when the caller states none (`HqOptions::tonemap` and `emitter_intensity` are now `Option`): gamma and 13 for Chunky's, filmic-mid-grey and the game look's intensity for the game's, the curves mixed in display values (`post::tonemap_mix`) and the intensities linearly. `--sky chunky` and `--sky game` force either look; `--sun-intensity`/`--sky-intensity` are refused only under `--sky game`. `HqSkyModel` is now a struct (look, render distance, Chunky's sun and sky scales). No kernel change. Tests: `the_default_sky_follows_the_day_without_a_jump` (tick 6000 is `--sky chunky` byte for byte, 18000 is `--sky game`; one tick across either edge of the ramp moves a frame no more than one tick inside a look does; mid-ramp is neither), `the_day_weight_follows_the_sky_light_factor`, `the_tone_mix_is_each_curve_at_its_ends`, the range in `day_cycle_layers_dimension_and_timeline`. Perturbation: a hard switch at the plateau's edge reds the first (28.673/255 at the edge). Log: sky by time: one tick moves a frame by 0.040/255 inside Chunky's look, 0.057 at the day plateau's edge and 0.067 at the night's On the camp (Metal, 64 spp, one tick apart, mean / max / p99 of the 8-bit difference): 11269->11270 0.059/16/2 (both Chunky's), 11270->11271 0.067/10/2 (across the edge), 13139->13140 0.025/86/1, 13140->13141 0.022/44/1 (both the game's). The day frame at tick 4000 is byte-identical to main's (38ef002) at 32 spp on Metal. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016ba8LkuKW2bd5C5HCDo7p2
…at dusk The owner's ruling: a tunable emitter scale whose default matches the engine's reference at dusk. `GAME_EMITTER_INTENSITY` = 137.9628 (Chunky's 13 times 10.6125) is the emitter intensity of the game's look, authored by Lumenwright: neither a vanilla nor a Chunky value. Under the default sky the intensity is the looks' mixed by the day weight (Chunky's 13 by day), and `--emitter-intensity` (`HqOptions::emitter_intensity`) takes any value over it. Method (fit.py): two CUDA renders of the camp's whole-camp view at tick 12800 under the default sky (day weight 0.181818), 16384 spp, four bounces (Chunky rayDepth 5, the reference's), emitters sampled ONE (Lumenwright's default), at intensities 13 and 26 with the same seed, which draw the same random numbers, so the frame at any intensity k is HDR(13) + (k - 13)/13 x (HDR(26) - HDR(13)) in radiance. Lantern-lit pixels are those where the emitters' light at the engine's intensity 159.003 outshines the sky's (Rec. 709 luminance). Objective: their mean display luma under the default tone mix equals the reference's (Chunky at 2048 spp under the engine's light and lantern scale, as in docs/research/game-sky.md); bisection. Log: lantern-lit pixels: 128564 of 921600 (13.95%) reference mean display luma on them: 41.993 Lumenwright at 13: 28.622; at 159.003: 45.956 fitted emitter intensity at day weight 0.181818: k = 115.2423 (luma 41.993) the game look's intensity: (k - w * 13) / (1 - w) = 137.9628 = 13 x 10.6125 lit pixels at k: mean |error| 7.33, rms 9.29 luma per quarter of the frame (k): 118.5, 114.6, 116.6, 112.6 cross-check: a direct render at k=115.2423 (its own PNG, the default tone mix) reads lit-pixel luma 41.993; reference 41.993 linear model vs direct render, radiance: mean |diff| 6.75e-07, mean radiance 0.1174 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016ba8LkuKW2bd5C5HCDo7p2
…its kernel The owner's ruling for the next release: - lumenwright-cli loses `publish = false`; README Install is `cargo install lumenwright-cli`, with no git-tag install. - release.yml refuses unless all three crates are at the version, none is marked publish = false, and none has the version on crates.io; it publishes lumenwright-kernel, lumenwright and lumenwright-cli in that order, each once the index serves the one before. - The workspace is 0.2.0 (lumenwright's pin on the kernel `=0.2.0`, the command's on the library `0.2.0`, the PTX crate's lock follows); CHANGELOG's section is `[0.2.0] - Unreleased`, dated at release time, which the workflow refuses until then. Nothing is published here. `cargo publish --dry-run --workspace` is run on this commit and its result is reported with the round. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016ba8LkuKW2bd5C5HCDo7p2
…ts' measurements game-sky.md gains "By the time of day" (the client's day plateau, ticks 730-11270 on sky_light_factor, sky_light_color and the sunset glow's alpha; the weight; the one-render mix; the continuity measured) and "Lanterns" (the fit and its method, authored by Lumenwright), and a table of the defaults against the engine's reference. The README's HQ description, renders.md, cli.md (`--sky auto|game|chunky`, each look's tone curve and emitter intensity), the CHANGELOG and a note on the benchmark's F-game follow. The defaults at 0cd2b54, CUDA 16384 spp, four bounces, cmp2.py: default-dusk.png: disp mean 8.06 p90 33 p99 86 SSIM 0.8162 luma 59.6 (ref 54.2); sun-band (190, 127, 77) 27° (n=4208); sun-upper (178, 125, 90) 24° (n=7004); away-upper (156, 105, 87) 16° (n=7908) default-day.png: disp mean 31.46 p90 65 p99 79 SSIM 0.7161 luma 111.8 (ref 105.8); sun-band (154, 172, 185) 205° (n=4208); sun-upper (139, 172, 196) 205° (n=7004); away-upper (147, 175, 195) 205° (n=7908) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016ba8LkuKW2bd5C5HCDo7p2
… time of day The README's commands, unchanged, rendered by 0cd2b54 on CUDA (RTX 4070 Ti SUPER, WSL2) under the round-2 defaults; each frame rendered twice gave byte-identical PNGs. The two day frames are Chunky's look and are byte-identical to main's approved frames (38ef002), so against main only the two dusk frames change: the game's sky at day weight 0.181818 with the lanterns at the fitted intensity (115.2423 at that weight). Held for the owner's look; contact-sheet-2.png is outside the repository. Run log (hq-showcase.sh): load 1.96 4.73 5.93 3/329 1293547 treehouse-camp-day-hq a exit=0 monotonic_wall=91.10 render 90.492s total 91.001s treehouse-camp-day-hq b exit=0 monotonic_wall=91.03 render 90.453s total 90.925s treehouse-camp-day-hq identical treehouse-bridge-day-hq a exit=0 monotonic_wall=122.20 render 121.565s total 122.064s treehouse-bridge-day-hq b exit=0 monotonic_wall=122.17 render 121.583s total 122.066s treehouse-bridge-day-hq identical treehouse-camp-dusk-hq a exit=0 monotonic_wall=332.83 render 332.138s total 332.688s treehouse-camp-dusk-hq b exit=0 monotonic_wall=333.09 render 332.420s total 332.946s treehouse-camp-dusk-hq identical treehouse-bridge-dusk-hq a exit=0 monotonic_wall=450.00 render 448.040s total 448.692s treehouse-bridge-dusk-hq b exit=0 monotonic_wall=447.07 render 446.319s total 446.937s treehouse-bridge-dusk-hq identical ALLDONE exit=0 against main: treehouse-camp-day-hq.png: byte-identical to 38ef002's treehouse-bridge-day-hq.png: byte-identical to 38ef002's Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016ba8LkuKW2bd5C5HCDo7p2
A third game-sky case in gpu_matches_cpu_in_hq_mode and cuda_matches_cpu_in_hq_mode: the default sky at tick 12205 (day weight 0.5), where Chunky's sky is mixed into the maps and Chunky's sun lights and is drawn at half weight, under the same tolerances as the other cases (mean luminance within 3%, under 2% of pixels off by more than 16/255). Perturbation, Metal: pathtrace.wgsl drawing no sun sample whenever the sky's maps are on (the blend's sun skipped on the GPU) passes every earlier case and reds the new one: hq showcase-game-dusk: mean luminance differs by 0.000% between CPU and GPU hq showcase-game-dusk: 0.000% of pixels differ by more than 16/255 between CPU and GPU hq showcase-auto-mid: mean luminance differs by 3.655% between CPU and GPU thread 'gpu_matches_cpu_in_hq_mode' (7095725) panicked at crates/lumenwright-cli/tests/render.rs:608:9: test gpu_matches_cpu_in_hq_mode ... FAILED test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 15 filtered out; finished in 3.89s perturbed metal exit=101 Local Metal run on the head (M2 Pro): test gpu_draws_the_game_sky_as_the_cpu_does ... ok test gpu_draws_the_sky_as_the_cpu_does ... ok test gpu_matches_cpu_in_quick_mode ... ok hq showcase-auto-mid: mean luminance differs by 0.001% between CPU and GPU hq showcase-auto-mid: 0.000% of pixels differ by more than 16/255 between CPU and GPU test gpu_matches_cpu_in_hq_mode ... ok test result: ok. 4 passed; 0 failed; 0 ignored; 0 measured; 12 filtered out; finished in 9.04s metal parity exit=0 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016ba8LkuKW2bd5C5HCDo7p2
`HqSkyModel` became a struct when the sky went by the time of day, so the intra-doc link `HqSkyModel::Game` no longer resolved and CI's doc job (`cargo doc --no-deps` with RUSTDOCFLAGS=-Dwarnings) failed. It now links `SkyLook::Game`. The same command passes locally. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016ba8LkuKW2bd5C5HCDo7p2
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
HQ mode's dusk was Chunky's Preetham sky, which at a low sun reads yellow where the game's sunset is orange. This ports the method Delvewright's Chunky scenes use (the pinned client's day timeline at the tick), keeps Chunky's sun and shadows by day, fits the lanterns, and prepares 0.2.0.
Sky by time of day (
--sky auto, the default)--sky game): light from the client'slightmap.fshsky light at the tick (visual/sky_light_factor,visual/sky_light_color), the lens seeing the client's own sky (sky disc, fog, sunrise fan, sun, moon, stars) as an equirectangular map; no directional sun light (the game has none); Hable with the mid-grey gain k = 1.463761 (--tonemap filmic-mid-grey). Ported from Delvewright (GPL-3.0), attributed in ACKNOWLEDGEMENTS.--sky chunkyand--sky game.Lanterns
The game look's emitter intensity is 137.9628 (13 × 10.6125), authored by Lumenwright: fitted so lantern-lit pixels at dusk match the engine's Chunky reference in mean display luma (41.993), by bisection over exact linear interpolation of two renders, cross-checked by a direct render (6.75e-7). Quarter-frame fits spread 112.6–118.5. Chunky's look keeps 13;
--emitter-intensitytakes any value.Measured (CUDA, 16384 spp, four bounces, against the engine's Chunky reference)
The remaining sky difference is the engine's own: a veil in the lens (Chunky feeds one map to lens and light) and a clear-colour lean that departs from the client's
Mth.clampedLerp(pinned here by a test). Renders: the two dusk HQ frames change; day frames are byte-identical; re-rendered twice, byte-identical.Also
docs/benchmark.md): same frame, spp and machine per question. PC: Chunky CPU 78.60 s, LW CUDA 6.14 s (12.8×), LW CPU 54.84 s (1.43×).docs/pages, every old anchor resolving (66 links, 0 broken).lumenwright-kerneldeclared rust-version 1.85, which its let-chains do not build on; it is 1.88.lumenwright-cliis published (kernel, library, command in order; the workflow refuses unless all three are absent); Install iscargo install lumenwright-cli; CHANGELOG[0.2.0] - Unreleased.cargo publish --dry-run --workspacepasses; the command's package builds standalone against the packaged two.showcase-auto-mid) on Metal and CUDA against the CPU; a perturbation that skips the half-weight sun reds each.Gates: fmt, clippy
-D warnings(with and withoutcuda),cargo test --workspace(146 passed),build-ptx.sh, CUDA and Metal parity tests, publish dry-run: all 0.🤖 Generated with Claude Code
https://claude.ai/code/session_016ba8LkuKW2bd5C5HCDo7p2