fidget_core/render/
mod.rs1use crate::{
3 eval::{BulkEvaluator, Function, Trace, TracingEvaluator},
4 shape::{Shape, ShapeTape},
5};
6
7mod config;
8mod region;
9
10pub use config::{CancelToken, ThreadPool};
11pub use region::{ImageSize, RegionSize, VoxelSize};
12
13pub struct RenderHandle<F: Function> {
20 shape: Shape<F>,
21
22 i_tape: Option<ShapeTape<<F::IntervalEval as TracingEvaluator>::Tape>>,
23 f_tape: Option<ShapeTape<<F::FloatSliceEval as BulkEvaluator>::Tape>>,
24 g_tape: Option<ShapeTape<<F::GradSliceEval as BulkEvaluator>::Tape>>,
25
26 next: Option<(F::Trace, Box<Self>)>,
27}
28
29impl<F: Function> Clone for RenderHandle<F> {
30 #[inline]
31 fn clone(&self) -> Self {
32 Self {
33 shape: self.shape.clone(),
34 i_tape: self.i_tape.clone(),
35 f_tape: self.f_tape.clone(),
36 g_tape: self.g_tape.clone(),
37 next: None,
38 }
39 }
40}
41
42impl<F: Function> RenderHandle<F> {
43 pub fn new(shape: Shape<F>) -> Self {
47 Self {
48 shape,
49 i_tape: None,
50 f_tape: None,
51 g_tape: None,
52 next: None,
53 }
54 }
55
56 #[inline]
58 pub fn i_tape(
59 &mut self,
60 storage: &mut Vec<F::TapeStorage>,
61 ) -> &ShapeTape<<F::IntervalEval as TracingEvaluator>::Tape> {
62 self.i_tape.get_or_insert_with(|| {
63 self.shape.interval_tape(storage.pop().unwrap_or_default())
64 })
65 }
66
67 #[inline]
69 pub fn f_tape(
70 &mut self,
71 storage: &mut Vec<F::TapeStorage>,
72 ) -> &ShapeTape<<F::FloatSliceEval as BulkEvaluator>::Tape> {
73 self.f_tape.get_or_insert_with(|| {
74 self.shape
75 .float_slice_tape(storage.pop().unwrap_or_default())
76 })
77 }
78
79 #[inline]
81 pub fn g_tape(
82 &mut self,
83 storage: &mut Vec<F::TapeStorage>,
84 ) -> &ShapeTape<<F::GradSliceEval as BulkEvaluator>::Tape> {
85 self.g_tape.get_or_insert_with(|| {
86 self.shape
87 .grad_slice_tape(storage.pop().unwrap_or_default())
88 })
89 }
90
91 #[inline]
96 pub fn simplify(
97 &mut self,
98 trace: &F::Trace,
99 workspace: &mut F::Workspace,
100 shape_storage: &mut Vec<F::Storage>,
101 tape_storage: &mut Vec<F::TapeStorage>,
102 ) -> &mut Self {
103 let mut trace_storage = if let Some(neighbor) = &self.next {
105 if &neighbor.0 != trace {
106 let (trace, neighbor) = self.next.take().unwrap();
107 neighbor.recycle(shape_storage, tape_storage);
108 Some(trace)
109 } else {
111 None
112 }
113 } else {
114 None
115 };
116
117 #[allow(clippy::panicking_unwrap, clippy::unnecessary_unwrap)]
122 if self.next.is_none() {
123 let s = shape_storage.pop().unwrap_or_default();
124 let next = self.shape.simplify(trace, s, workspace).unwrap();
125 if next.size() >= self.shape.size() {
126 shape_storage.extend(next.recycle());
129 self
130 } else {
131 assert!(self.next.is_none());
132 if let Some(t) = trace_storage.as_mut() {
133 t.copy_from(trace);
134 } else {
135 trace_storage = Some(trace.clone());
136 }
137 self.next = Some((
138 trace_storage.unwrap(),
139 Box::new(RenderHandle {
140 shape: next,
141 i_tape: None,
142 f_tape: None,
143 g_tape: None,
144 next: None,
145 }),
146 ));
147 &mut self.next.as_mut().unwrap().1
148 }
149 } else {
150 &mut self.next.as_mut().unwrap().1
151 }
152 }
153
154 #[inline]
156 pub fn recycle(
157 mut self,
158 shape_storage: &mut Vec<F::Storage>,
159 tape_storage: &mut Vec<F::TapeStorage>,
160 ) {
161 if let Some((_trace, shape)) = self.next.take() {
163 shape.recycle(shape_storage, tape_storage);
164 }
165
166 if let Some(i_tape) = self.i_tape.take() {
167 tape_storage.extend(i_tape.recycle());
168 }
169 if let Some(g_tape) = self.g_tape.take() {
170 tape_storage.extend(g_tape.recycle());
171 }
172 if let Some(f_tape) = self.f_tape.take() {
173 tape_storage.extend(f_tape.recycle());
174 }
175
176 shape_storage.extend(self.shape.recycle());
178 }
179}
180
181#[derive(Debug, Eq, PartialEq)]
189pub struct TileSizes(Vec<usize>);
190
191#[derive(thiserror::Error, Debug)]
193pub enum TileSizeError {
194 #[error("bad tile sizes; {0} is not divisible by {1}")]
196 BadTileSize(usize, usize),
197
198 #[error("bad tile order; {0} is not larger than {1}")]
200 BadTileOrder(usize, usize),
201
202 #[error("tile size list must not be empty")]
204 EmptyTileSizes,
205}
206
207impl TileSizes {
208 pub fn new(sizes: &[usize]) -> Result<Self, TileSizeError> {
210 if sizes.is_empty() {
211 return Err(TileSizeError::EmptyTileSizes);
212 }
213 for i in 1..sizes.len() {
214 if sizes[i - 1] <= sizes[i] {
215 return Err(TileSizeError::BadTileOrder(
216 sizes[i - 1],
217 sizes[i],
218 ));
219 } else if !sizes[i - 1].is_multiple_of(sizes[i]) {
220 return Err(TileSizeError::BadTileSize(sizes[i - 1], sizes[i]));
221 }
222 }
223 Ok(Self(sizes.to_vec()))
224 }
225
226 #[allow(clippy::len_without_is_empty)]
228 pub fn len(&self) -> usize {
229 self.0.len()
230 }
231
232 pub fn iter(&self) -> impl Iterator<Item = &usize> {
234 self.0.iter()
235 }
236}
237
238impl std::ops::Index<usize> for TileSizes {
239 type Output = usize;
240
241 fn index(&self, i: usize) -> &Self::Output {
242 &self.0[i]
243 }
244}
245
246impl std::ops::Index<std::ops::RangeFrom<usize>> for TileSizes {
247 type Output = [usize];
248 fn index(&self, index: std::ops::RangeFrom<usize>) -> &Self::Output {
249 &self.0[index]
250 }
251}
252
253pub trait RenderHints {
259 fn tile_sizes_3d() -> TileSizes;
261
262 fn tile_sizes_2d() -> TileSizes;
264
265 fn simplify_tree_during_meshing(_d: usize) -> bool {
272 true
273 }
274}