nsi_ffi_wrap/output/pixel_format.rs
1use core::ops::Deref;
2use std::ffi::CStr;
3
4/// The scalar type of pixel channel data.
5///
6/// This enum represents the data type of individual channel values
7/// as specified by the ndspy format constants.
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
9#[repr(u32)]
10pub enum ScalarType {
11 /// 32-bit IEEE 754 floating point (f32)
12 #[default]
13 Float32 = 1,
14 /// 16-bit IEEE 754 floating point (f16/half)
15 Float16 = 12,
16 /// 32-bit unsigned integer (u32)
17 Unsigned32 = 2,
18 /// 32-bit signed integer (i32)
19 Signed32 = 3,
20 /// 16-bit unsigned integer (u16)
21 Unsigned16 = 4,
22 /// 16-bit signed integer (i16)
23 Signed16 = 5,
24 /// 8-bit unsigned integer (u8)
25 Unsigned8 = 6,
26 /// 8-bit signed integer (i8)
27 Signed8 = 7,
28}
29
30impl ScalarType {
31 /// Create a ScalarType from an ndspy type constant.
32 /// Returns None for unknown type values.
33 pub fn from_ndspy_type(type_: u32) -> Option<Self> {
34 match type_ {
35 1 => Some(ScalarType::Float32),
36 2 => Some(ScalarType::Unsigned32),
37 3 => Some(ScalarType::Signed32),
38 4 => Some(ScalarType::Unsigned16),
39 5 => Some(ScalarType::Signed16),
40 6 => Some(ScalarType::Unsigned8),
41 7 => Some(ScalarType::Signed8),
42 12 => Some(ScalarType::Float16),
43 _ => None,
44 }
45 }
46
47 /// Returns the size in bytes of this scalar type.
48 pub const fn size_bytes(self) -> usize {
49 match self {
50 ScalarType::Float32
51 | ScalarType::Unsigned32
52 | ScalarType::Signed32 => 4,
53 ScalarType::Float16
54 | ScalarType::Unsigned16
55 | ScalarType::Signed16 => 2,
56 ScalarType::Unsigned8 | ScalarType::Signed8 => 1,
57 }
58 }
59}
60
61/// Description of an [`OutputLayer`](crate::OUTPUT_LAYER) node
62/// inside a flat, raw pixel.
63#[derive(Debug, Clone, Default, PartialEq)]
64pub struct Layer {
65 name: String,
66 depth: LayerDepth,
67 offset: usize,
68}
69
70impl Layer {
71 /// The name of the layer.
72 #[inline]
73 pub fn name(&self) -> &str {
74 self.name.as_str()
75 }
76
77 /// The [depth](LayerDepth) of this layer.
78 #[inline]
79 pub fn depth(&self) -> LayerDepth {
80 self.depth
81 }
82
83 /// The channel offset of the layer inside the [`PixelFormat`].
84 #[inline]
85 pub fn offset(&self) -> usize {
86 self.offset
87 }
88
89 /// The number of channels in this layer. This is a shortcut for calling
90 /// `depth().channels()`.
91 #[inline]
92 pub fn channels(&self) -> usize {
93 self.depth.channels()
94 }
95
96 /// Returns true if the [depth](LayerDepth) of this layer contains an alpha
97 /// channel. This is a shortcut for calling `depth().has_alpha()`.
98 #[inline]
99 pub fn has_alpha(&self) -> bool {
100 self.depth.has_alpha()
101 }
102}
103
104/// The depth (number and type of channels) a pixel in a [`Layer`] is
105/// composed of.
106#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
107pub enum LayerDepth {
108 /// A single channel. Obtained when setting `"layertype"` `"scalar"` on an
109 /// [`OutputLayer`](crate::OUTPUT_LAYER).
110 #[default]
111 OneChannel,
112 /// A single channel with alpha. Obtained when setting `"layertype"`
113 /// `"scalar"` and `"withalpha"` `1` on an
114 /// [`OutputLayer`](crate::OUTPUT_LAYER).
115 OneChannelAndAlpha,
116 /// An `rgb` color triplet. Obtained when setting `"layertype"` `"color"`
117 /// on an [`OutputLayer`](crate::OUTPUT_LAYER).
118 Color,
119 /// An `rgb` color triplet with alpha. Obtained when setting `"layertype"`
120 /// `"color"` and `"withalpha"` `1` on an
121 /// [`OutputLayer`](crate::OUTPUT_LAYER).
122 ColorAndAlpha,
123 /// An `xyz` triplet. Obtained when setting `"layertype"` `"vector"` on an
124 /// [`OutputLayer`](crate::OUTPUT_LAYER).
125 Vector,
126 /// An `xyz` triplet with alpha. Obtained when setting `"layertype"`
127 /// `"vector"` and `"withalpha"` `1` on an
128 /// [`OutputLayer`](crate::OUTPUT_LAYER).
129 VectorAndAlpha,
130 /// An quadruple of values. Obtained when setting `"layertype"` `"quad"` on
131 /// an [`OutputLayer`](crate::OUTPUT_LAYER).
132 FourChannels,
133 /// An quadruple of values with alpha. Obtained when setting `"layertype"`
134 /// `"quad"` and `"withalpha"` `1` on an
135 /// [`OutputLayer`](crate::OUTPUT_LAYER).
136 FourChannelsAndAlpha,
137}
138
139impl LayerDepth {
140 /// Returns the number of channels this layer type consists of.
141 pub fn channels(&self) -> usize {
142 match self {
143 LayerDepth::OneChannel => 1,
144 LayerDepth::OneChannelAndAlpha => 2,
145 LayerDepth::Color => 3,
146 LayerDepth::Vector => 3,
147 LayerDepth::ColorAndAlpha => 4,
148 LayerDepth::VectorAndAlpha => 4,
149 LayerDepth::FourChannels => 4,
150 LayerDepth::FourChannelsAndAlpha => 5,
151 }
152 }
153
154 /// Returns `true`` if this layer contains an alpha channel.
155 pub fn has_alpha(&self) -> bool {
156 [
157 LayerDepth::OneChannelAndAlpha,
158 LayerDepth::ColorAndAlpha,
159 LayerDepth::VectorAndAlpha,
160 LayerDepth::FourChannelsAndAlpha,
161 ]
162 .contains(self)
163 }
164}
165
166/// Accessor for the pixel format the renderer sends in
167/// [`FnOpen`](crate::output::FnOpen), [`FnWrite`](crate::output::FnWrite) and
168/// [`FnFinish`](crate::output::FnFinish)
169///
170/// This is a stack of [`Layer`]s. Where each layer describes an
171/// [`OutputLayer`](crate::OUTPUT_LAYER).
172///
173/// # Example
174///
175/// A typical format for a pixel containing two such layers, an *RGBA* **color**
176/// & **alpha** output layer and a world space **normal**, will look like this:
177///
178/// | [`name`](Layer::name()) | [`depth`](Layer::depth()) | [`offset`](Layer::offset())
179/// |-------------------------|-----------------------------------------------------|----------------------------
180/// | `Ci` |[`ColorAndAlpha`](LayerDepth::ColorAndAlpha)(`rgba`) | `0`
181/// | `N_world` | [`Vector`](LayerDepth::Vector)(`xyz`) | `4`
182///
183/// ## RAW Layout
184///
185/// The resp. callbacks deliver pixels as a flat [`prim@f32`] buffer.
186/// For the above example the actual layout of a single pixel in the
187/// buffer is:
188///
189/// | Value | `r`ed | `g`reen | `b`lue | `a`lpha | `x` | `y` | `z`
190/// |--------|---------|---------|---------|---------|-----|-----|----
191/// | Offset | `0` | `1` | `2` | `3` | `4` | `5` | `6`
192///
193/// The `offset` is the offset into the pixel buffer to obtain the 1st element.
194/// For example, the **y** coordinate of the the normal will be stored in
195/// channel at offset `5` (`4` + `1`).
196///
197/// The pixel format is in the order in which
198/// [`OutputLayer`](crate::OUTPUT_LAYER)s were defined in the
199/// [ɴsɪ scene](https://nsi.readthedocs.io/en/latest/guidelines.html#basic-scene-anatomy).
200///
201/// # Accessing Layers
202///
203/// To access the [`Layer`]s inside a `PixelFormat` use the [`Deref`] operator
204/// to obtain the underlying [`Vec`]<`Layer`>.
205///
206/// # Examples
207///
208/// Dump all layers to `stdout` after a frame has finished rendering:
209///
210/// ```
211/// # #[cfg(feature = "output")]
212/// # {
213/// # use nsi_ffi_wrap as nsi;
214/// let finish = nsi::output::FinishCallback::new(
215/// |_: String,
216/// _: usize,
217/// _: usize,
218/// pixel_format: nsi::output::PixelFormat| {
219/// // Dump all layer descriptions to stdout.
220/// for layer in &*pixel_format {
221/// println!("{:?}", layer);
222/// }
223///
224/// nsi::output::Error::None
225/// },
226/// );
227/// # }
228/// ```
229#[derive(Debug, Default, Clone, PartialEq)]
230pub struct PixelFormat(Vec<Layer>);
231
232impl PixelFormat {
233 /// Groups the renderer's flat channel list into [`Layer`]s.
234 ///
235 /// ndspy names each channel `<layer>.<channel>`, so a layer boundary
236 /// is a change of the *name* part -- which is what this reads. The
237 /// previous implementation instead guessed boundaries from the
238 /// channel part, opening a layer only on `r`, `x` or `s`; a layer
239 /// led by anything else (`depth.z`, say) never opened one, so it was
240 /// merged into its predecessor, and with
241 /// `["Ci.r","Ci.g","Ci.b","Ci.a","albedo.r","albedo.g","albedo.b",
242 /// "N.x","N.y","N.z","depth.z"]` the whole `N` layer disappeared:
243 /// its name was overwritten by `depth` and a channel was lost.
244 ///
245 /// The one place a name change is *not* a boundary is a bare `a`
246 /// following a named layer -- that is the layer's alpha.
247 ///
248 /// ndspy passes exactly one `PtDspyDevFormat` per channel, so
249 /// [`channels()`](PixelFormat::channels) always equals `format.len()`
250 /// by construction here. That is not cosmetic: `nsi-display`'s
251 /// `shim_data` uses it as the length of a slice over the renderer's
252 /// bucket buffer, so over-reporting reads out of bounds and
253 /// under-reporting silently drops AOVs.
254 #[inline]
255 pub(crate) fn new(format: &[ndspy_sys::PtDspyDevFormat]) -> Self {
256 let mut layers = Vec::<Layer>::new();
257 // The layer being accumulated: its name, and one entry per
258 // channel seen so far.
259 let mut name = "";
260 let mut channel_ids = Vec::<&str>::new();
261 let mut offset = 0;
262
263 let mut flush =
264 |name: &str, ids: &mut Vec<&str>, offset: &mut usize| {
265 for depth in Self::depths_for(ids.len(), ids.first().copied()) {
266 let unnamed = if layers.is_empty() {
267 // The first unnamed layer is the beauty pass.
268 "Ci"
269 } else {
270 // A later one has no name to take: 3Delight sends
271 // built-in variables unprefixed, so all this layer
272 // is known by is its first channel.
273 ids.first().copied().unwrap_or("Ci")
274 };
275 layers.push(Layer {
276 name: if name.is_empty() { unnamed } else { name }
277 .to_string(),
278 depth,
279 offset: *offset,
280 });
281 *offset += depth.channels();
282 }
283 ids.clear();
284 };
285
286 for entry in format {
287 // SAFETY: `name` is a valid C string from the renderer.
288 let channel = unsafe { CStr::from_ptr(entry.name) }
289 .to_str()
290 .unwrap_or("<invalid>");
291 let (layer_name, channel_id) =
292 Self::split_into_layer_name_and_channel_id(channel);
293
294 // Two ways a channel can belong to the layer being
295 // accumulated. Its name must match -- and when there are no
296 // names, as 3Delight does for built-in variables, its role
297 // must continue the sequence.
298 // A bare `a` is the previous layer's alpha even though it
299 // carries no name of its own.
300 let named_the_same = layer_name == name
301 || (layer_name.is_empty() && "a" == channel_id);
302 let continues =
303 named_the_same && Self::continues(&channel_ids, channel_id);
304
305 if !channel_ids.is_empty() && !continues {
306 flush(name, &mut channel_ids, &mut offset);
307 }
308 if channel_ids.is_empty() {
309 name = layer_name;
310 }
311 channel_ids.push(channel_id);
312 }
313 flush(name, &mut channel_ids, &mut offset);
314
315 PixelFormat(layers)
316 }
317
318 /// Whether `channel_id` continues the layer accumulated so far.
319 ///
320 /// 3Delight names channels of built-in variables with no layer
321 /// prefix at all -- a beauty plus a depth arrives as
322 /// `["r","g","b","a","z"]`, measured, not assumed. So when the name
323 /// part cannot separate two layers, their channel *roles* must:
324 /// `r`,`g`,`b` and `x`,`y`,`z` are positional, so a channel
325 /// continues only from the position its predecessor left off, and
326 /// `a` closes a layer rather than opening one. A trailing `z` after
327 /// `r`,`g`,`b`,`a` is therefore a new layer -- it cannot be the
328 /// third channel of a group that already holds four.
329 fn continues(accumulated: &[&str], channel_id: &str) -> bool {
330 if accumulated.is_empty() {
331 return true;
332 }
333 // An alpha joins whatever it follows, and closes it.
334 if "a" == channel_id {
335 return accumulated.len() < 5 && accumulated.last() != Some(&"a");
336 }
337 let position = |id: &str| match id {
338 "r" | "x" => Some(0),
339 "g" | "y" => Some(1),
340 "b" | "z" => Some(2),
341 _ => None,
342 };
343 let family = |id: &str| match id {
344 "r" | "g" | "b" => Some(0),
345 "x" | "y" | "z" => Some(1),
346 _ => None,
347 };
348 match (position(channel_id), accumulated.first()) {
349 (Some(next), Some(first)) => {
350 next == accumulated.len() && family(channel_id) == family(first)
351 }
352 // A scalar -- an indexed channel, say -- is a layer of its own.
353 _ => false,
354 }
355 }
356
357 /// The [`LayerDepth`]s spanning `count` channels, whose first is
358 /// `first` (`x`, `y` or `z` meaning a vector rather than a colour).
359 ///
360 /// Normally one depth. ɴsɪ's layer types top out at five channels
361 /// (`quad` with alpha), and `LayerDepth` can represent no more, so a
362 /// longer run -- which 3Delight does not currently emit -- is split
363 /// into four-channel pieces sharing the layer's name. Splitting
364 /// rather than truncating keeps the total channel count equal to the
365 /// number of format entries, which is the invariant memory safety
366 /// downstream rests on.
367 fn depths_for(count: usize, first: Option<&str>) -> Vec<LayerDepth> {
368 let is_vector = matches!(first, Some("x" | "y" | "z"));
369 let mut depths = Vec::new();
370 let mut left = count;
371 while left > 5 {
372 depths.push(LayerDepth::FourChannels);
373 left -= 4;
374 }
375 depths.extend(match (left, is_vector) {
376 (0, _) => None,
377 (1, _) => Some(LayerDepth::OneChannel),
378 (2, _) => Some(LayerDepth::OneChannelAndAlpha),
379 (3, true) => Some(LayerDepth::Vector),
380 (3, false) => Some(LayerDepth::Color),
381 (4, true) => Some(LayerDepth::VectorAndAlpha),
382 (4, false) => Some(LayerDepth::ColorAndAlpha),
383 (5, _) => Some(LayerDepth::FourChannelsAndAlpha),
384 _ => unreachable!("the loop above leaves at most five"),
385 });
386 depths
387 }
388
389 /// Builds a `PixelFormat` from the format array ndspy hands a
390 /// display driver.
391 ///
392 /// Out-of-crate drivers (see the `nsi-display` crate) receive
393 /// `PtDspyDevFormat[]` in `DspyImageOpen` and need this to interpret
394 /// the buckets that follow.
395 #[inline]
396 pub fn from_ndspy(format: &[ndspy_sys::PtDspyDevFormat]) -> Self {
397 Self::new(format)
398 }
399
400 fn split_into_layer_name_and_channel_id(name: &str) -> (&str, &str) {
401 let mut split = name.rsplitn(3, '.');
402 // We know we never get an empty string so we can safely unwrap
403 // here.
404 let mut postfix = split.next().unwrap();
405 if "000" == postfix {
406 postfix = "s";
407 // Reset iterator.
408 split = name.rsplitn(2, '.');
409 }
410 // Get the layer name if there are more parts.
411 // For 2-part names like "beauty.r", this is "beauty".
412 // For 3+ part names, this is the leftmost part (skipping middle).
413 match split.last() {
414 Some(prefix) => (prefix, postfix),
415 None => ("", postfix),
416 }
417 }
418
419 /// Returns the total number of channels in a pixel.
420 /// This is the sum of the number of channels in all [`Layer`]s.
421 #[inline]
422 pub fn channels(&self) -> usize {
423 self.0
424 .iter()
425 .fold(0, |total, layer| total + layer.channels())
426 }
427}
428
429impl Deref for PixelFormat {
430 type Target = Vec<Layer>;
431
432 fn deref(&self) -> &Self::Target {
433 &self.0
434 }
435}
436
437impl AsRef<Vec<Layer>> for PixelFormat {
438 fn as_ref(&self) -> &Vec<Layer> {
439 &self.0
440 }
441}