1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
//! Wasm-safe, thread-free "trace these pixels x samples" tracer over
//! [`FrameScene`] -- the shared core both the desktop's threaded
//! `renderer::gpu::hybrid::cpu_trace_range` and a browser's per-Worker CPU tracer call.
//!
//! No threads, no [`std::time::Instant`], no filesystem access: every function here can
//! run inside a wasm32 Web Worker as well as a native thread.
//!
//! # Determinism and partitioning
//!
//! A pixel's sample stream is a pure function of `(pixel, sample_num)` alone (see
//! [`trace_pixels_interleaved`]'s own doc), so ANY partition of a frame's pixels across
//! however many callers -- one thread, one Worker, any `first_pixel`/`stride` choice --
//! produces per-pixel bit-identical summed radiance, as long as every pixel is covered
//! by exactly one partition. [`scatter_interleaved`] adds a partition's results back
//! into a full-frame buffer using the same `first_pixel`/`stride` convention, so summing
//! several partitions' scatters reproduces a single unpartitioned trace exactly.
use Vec3;
use crate::;
use FrameScene;
/// One `(pixel, sample_num)` sample, traced through the real
/// [`trace_spectral_ray_with_finish_soa`] -- never a reimplementation of the estimator,
/// only of the per-sample seed/jitter construction around it.
///
/// Takes the batch's `plane_soa` arena (built once by the caller) rather than
/// rebuilding it from `scene.planes` on every one of the many calls a batch makes.
///
/// `rot` is `pixel`'s [`PixelRotations`], computed once per pixel by the caller: it is a
/// pure function of `pixel` alone, so passing the precomputed value to
/// [`sample_draws`] yields the same draws as recomputing it for every sample.
/// Traces one interleaved partition of the frame's pixels and returns one SUMMED
/// [`Vec3`] per owned pixel.
///
/// Sample indices `[sample_offset, sample_offset + spp)` are traced for pixels
/// `first_pixel, first_pixel + stride, first_pixel + 2 * stride, …` (stopping once a
/// pixel index would reach `scene.width * scene.height`), in that same ascending
/// order. A non-finite sample is dropped but still counted, like the GPU reduction it
/// merges with (see [`add_finite_sample`]).
///
/// Because a pixel's sample stream depends only on `(pixel, sample_num)` -- never on
/// `first_pixel`, `stride`, or which caller happens to own that pixel -- any partition
/// of a frame's pixels into disjoint interleaved sets, traced independently and
/// recombined with [`scatter_interleaved`], reproduces the exact same per-pixel sums a
/// single unpartitioned call over the whole frame would produce. That is what lets a
/// desktop thread pool and a browser's Web Worker pool call this same function with
/// different `first_pixel`/`stride` splits and still agree bit-for-bit with each other
/// and with a single-threaded reference trace.
/// Adds one partition's `sums` back into the full-frame buffer `dst`.
///
/// `sums` must be in the same `first_pixel, first_pixel + stride, …` order
/// [`trace_pixels_interleaved`] produced them in -- this scatters them into `dst` at
/// those same pixel indices. ADDS rather than overwrites, matching every other
/// accumulation buffer in this crate (see `renderer::gpu::hybrid`'s "Merge convention"
/// doc): summing several partitions' scatters into the same zero-initialised buffer
/// reproduces a single unpartitioned trace's sums exactly.
///
/// # Panics
///
/// Panics if `sums` has more entries than `dst` has pixels starting at `first_pixel`
/// and stepping by `stride` -- i.e. if it was not produced by a
/// [`trace_pixels_interleaved`] call against a `dst`-shaped frame with the same
/// `first_pixel`/`stride`.