Skip to main content

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}