1use crate::core::lowdiscrepancy::primes::{PRIMES, PRIME_SUMS};
2use crate::core::prelude::*;
3
4use std::cell::Cell;
5use std::sync::Arc;
6use std::sync::OnceLock;
7use std::sync::RwLock;
8
9const K_MAX_RESOLUTION: i32 = 128;
10static RADICAL_INVERSE_PERMUTATIONS: OnceLock<Vec<u16>> = OnceLock::new();
11
12fn get_radical_inverse_permutations() -> &'static Vec<u16> {
13 let v = RADICAL_INVERSE_PERMUTATIONS.get_or_init(|| -> Vec<u16> {
14 let mut v = Vec::new();
15 let mut rng = RNG::new();
16 compute_radical_inverse_permutations(&mut v, &mut rng);
17 return v;
18 });
19 return v;
20}
21
22fn math_mod(a: i64, b: i64) -> u64 {
23 let result = a - (a / b) * b;
24 return if result < 0 {
25 (result + b) as u64
26 } else {
27 result as u64
28 };
29}
30
31fn extended_gcd(a: u64, b: u64) -> (i64, i64) {
32 if b == 0 {
33 return (1, 0);
34 }
35 let d = (a / b) as i64;
36 let (xp, yp) = extended_gcd(b, a % b);
37 return (yp, xp - (d * yp));
38}
39
40fn multiplicative_inverse(a: i64, n: i64) -> u64 {
41 let (x, _y) = extended_gcd(a as u64, n as u64);
42 return math_mod(x, n);
43}
44
45#[derive(Debug, Default, PartialEq, Clone)]
46pub struct HaltonSampler {
47 base: BaseGlobalSampler,
48 base_scales: [i32; 2],
49 base_exponents: [i32; 2],
50 sample_stride: i32,
51 mult_inverse: [i32; 2],
52 pixel_for_offset: Cell<Point2i>,
53 offset_for_current_pixel: Cell<i64>,
54 sample_at_pixel_center: bool,
55}
56
57impl HaltonSampler {
58 pub fn new(samples_per_pixel: u32, sample_bounds: &Bounds2i, sample_at_center: bool) -> Self {
59 let _ = get_radical_inverse_permutations();
60
61 let res = sample_bounds.max - sample_bounds.min;
63 const BASES: [i32; 2] = [2, 3];
64
65 let mut base_scales = [1, 1];
66 let mut base_exponents = [1, 1];
67 for i in 0..2 {
68 let base = BASES[i];
70 let mut scale = 1;
71 let mut exp = 0;
72 while scale < i32::min(res[i], K_MAX_RESOLUTION) {
73 scale *= base;
74 exp += 1;
75 }
76 base_scales[i] = scale;
77 base_exponents[i] = exp;
78 }
79
80 let sample_stride = base_scales[0] * base_scales[1];
82 let mult_inverse = [
83 multiplicative_inverse(base_scales[1] as i64, base_scales[0] as i64) as i32,
84 multiplicative_inverse(base_scales[0] as i64, base_scales[1] as i64) as i32,
85 ];
86
87 let pixel_for_offset = Cell::new(Point2i::new(i32::MAX, i32::MAX));
88 let offset_for_current_pixel = Cell::new(0);
89 let base = BaseGlobalSampler::new(samples_per_pixel);
90
91 HaltonSampler {
92 base,
93 base_scales,
94 base_exponents,
95 sample_stride,
96 mult_inverse,
97 pixel_for_offset,
98 offset_for_current_pixel,
99 sample_at_pixel_center: sample_at_center,
100 }
101 }
102
103 fn permutation_for_dimension(dim: u32) -> &'static [u16] {
104 let table_size = PRIMES.len();
105 if dim as usize >= table_size {
106 panic!("HaltonSampler can only sample {} dimensions.", table_size);
107 }
108 let start = PRIME_SUMS[dim as usize] as usize;
109 let rip = get_radical_inverse_permutations();
110 return &rip[start..];
111 }
112}
113
114impl GlobalSampler for HaltonSampler {
115 fn get_index_for_sample(&self, sample_num: i64) -> i64 {
116 let sample_stride = self.sample_stride as i64;
117 if self.base.base.current_pixel != self.pixel_for_offset.get() {
118 self.offset_for_current_pixel.set(0);
120 if sample_stride > 1 {
121 let pm = [
122 math_mod(
123 self.base.base.current_pixel[0] as i64,
124 K_MAX_RESOLUTION as i64,
125 ),
126 math_mod(
127 self.base.base.current_pixel[1] as i64,
128 K_MAX_RESOLUTION as i64,
129 ),
130 ];
131 const BASES: [u64; 2] = [2, 3];
132 for i in 0..2 {
133 let dim_offset =
134 inverse_radical_inverse(BASES[i], pm[i], self.base_exponents[i] as usize);
135 let addition = (dim_offset
136 * ((self.sample_stride / self.base_scales[i]) * self.mult_inverse[i])
137 as u64) as i64;
138 self.offset_for_current_pixel
139 .set(self.offset_for_current_pixel.get() + addition);
140 }
141 let offset_for_current_pixel = self.offset_for_current_pixel.get() % sample_stride;
142 self.offset_for_current_pixel.set(offset_for_current_pixel);
143 }
144 self.pixel_for_offset.set(self.base.base.current_pixel);
145 }
146 return self.offset_for_current_pixel.get() + sample_num * sample_stride;
147 }
148
149 fn sample_dimension(&self, index: i64, dim: u32) -> Float {
150 if self.sample_at_pixel_center && (dim == 0 || dim == 1) {
151 return 0.5;
152 }
153 return match dim {
154 0 => radical_inverse(dim, (index >> self.base_exponents[0]) as u64),
155 1 => radical_inverse(dim, (index / self.base_scales[1] as i64) as u64),
156 _ => scrambled_radical_inverse(
157 dim,
158 index as u64,
159 HaltonSampler::permutation_for_dimension(dim),
160 ),
161 };
162 }
163
164 fn get_base(&mut self) -> &mut BaseGlobalSampler {
165 return &mut self.base;
166 }
167}
168
169impl Sampler for HaltonSampler {
170 fn start_pixel(&mut self, p: &Point2i) {
171 let _p = ProfilePhase::new(Prof::StartPixel);
172
173 self.base.base.start_pixel(p);
174 self.base.dimension = 0;
175 self.base.interval_sample_index = self.get_index_for_sample(0);
176 self.base.array_end_dim = BaseGlobalSampler::array_start_dim
177 + (self.base.base.sample_array1d.len() + 2 * self.base.base.sample_array2d.len())
178 as u32;
179 let samples1d_array_sizes = self.base.base.samples1d_array_sizes.len();
180 {
182 let l = samples1d_array_sizes;
183 for i in 0..l {
184 let n_samples =
185 self.base.base.samples1d_array_sizes[i] * self.base.base.samples_per_pixel;
186 for j in 0..n_samples {
187 let index = self.get_index_for_sample(j as i64);
188 self.base.base.sample_array1d[i][j as usize] =
189 self.sample_dimension(index, self.base.array_end_dim + i as u32);
190 }
191 }
192 }
193 {
195 let dim = BaseGlobalSampler::array_start_dim + samples1d_array_sizes as u32;
196 let l = self.base.base.samples2d_array_sizes.len();
197 for i in 0..l {
198 let n_samples =
199 self.base.base.samples2d_array_sizes[i] * self.base.base.samples_per_pixel;
200 for j in 0..n_samples {
201 let index = self.get_index_for_sample(j as i64);
202 self.base.base.sample_array2d[i][j as usize].x =
203 self.sample_dimension(index, dim);
204 self.base.base.sample_array2d[i][j as usize].y =
205 self.sample_dimension(index, dim + 1);
206 }
207 }
208 }
209 }
210
211 fn start_next_sample(&mut self) -> bool {
212 self.base.dimension = 0;
213 self.base.interval_sample_index =
214 self.get_index_for_sample((self.base.base.current_pixel_sample_index + 1) as i64);
215 return self.base.base.start_next_sample();
216 }
217
218 fn set_sample_number(&mut self, sample_num: u32) -> bool {
219 self.base.dimension = 0;
220 self.base.interval_sample_index = self.get_index_for_sample(sample_num as i64);
221 return self.base.base.set_sample_number(sample_num);
222 }
223
224 fn get_1d(&mut self) -> Float {
225 let _p = ProfilePhase::new(Prof::GetSample);
226
227 if self.base.dimension >= BaseGlobalSampler::array_start_dim
228 && self.base.dimension < self.base.array_end_dim
229 {
230 self.base.dimension = self.base.array_end_dim;
231 }
232 let d = self.base.dimension;
233 let x = self.sample_dimension(self.base.interval_sample_index, d);
234 self.base.dimension += 1;
235 return x;
236 }
237
238 fn get_2d(&mut self) -> Vector2f {
239 let _p = ProfilePhase::new(Prof::GetSample);
240
241 if self.base.dimension + 1 >= BaseGlobalSampler::array_start_dim
242 && self.base.dimension < self.base.array_end_dim
243 {
244 self.base.dimension = self.base.array_end_dim;
245 }
246 let d = self.base.dimension;
247 let x = self.sample_dimension(self.base.interval_sample_index, d);
248 let y = self.sample_dimension(self.base.interval_sample_index, d + 1);
249 let p = Point2f::new(x, y);
250 self.base.dimension += 2;
251 return p;
252 }
253
254 fn request_1d_array(&mut self, n: u32) {
255 self.base.request_1d_array(n);
256 }
257 fn request_2d_array(&mut self, n: u32) {
258 self.base.request_2d_array(n);
259 }
260 fn get_1d_array(&mut self, n: u32) -> Option<Vec<Float>> {
261 return self.base.get_1d_array(n);
262 }
263 fn get_2d_array(&mut self, n: u32) -> Option<Vec<Vector2f>> {
264 return self.base.get_2d_array(n);
265 }
266
267 fn clone_with_seed(&self, _seed: u32) -> Arc<RwLock<dyn Sampler>> {
268 return Arc::new(RwLock::new(self.clone()));
269 }
270 fn get_samples_per_pixel(&self) -> u32 {
271 return self.base.base.samples_per_pixel;
272 }
273}
274
275pub fn create_halton_sampler(
276 params: &ParamSet,
277 sample_bounds: &Bounds2i,
278) -> Result<Arc<RwLock<dyn Sampler>>, PbrtError> {
279 let mut nsamp = params.find_one_int("pixelsamples", 16) as u32;
280 let sample_at_center = params.find_one_bool("samplepixelcenter", false);
281
282 let _ = get_radical_inverse_permutations();
284 {
287 let options = PbrtOptions::get();
288 if options.quick_render {
289 nsamp = 1;
290 }
291 }
292
293 return Ok(Arc::new(RwLock::new(HaltonSampler::new(
294 nsamp,
295 sample_bounds,
296 sample_at_center,
297 ))));
298}