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
//! Configure vertex attributes.
//!
//! This module has types and helpers for describing the shape/structure of vertex
//! data to be sent to the GPU.
//!
//! See the [`buffer`](crate::buffer) module to use the vertex data itself.

use std::mem::MaybeUninit;

/// Vertex attribute info. This struct describes how vertex buffers are
/// layed out and used (i.e. the shape of the vertex data).
#[derive(Debug)]
#[doc(alias = "C3D_AttrInfo")]
pub struct Info(pub(crate) citro3d_sys::C3D_AttrInfo);

/// A shader input register, usually corresponding to a single vertex attribute
/// (e.g. position or color). These are called `v0`, `v1`, ... `v15` in the
/// [picasso](https://github.com/devkitPro/picasso/blob/master/Manual.md)
/// shader language.
#[derive(Debug, Clone, Copy)]
pub struct Register(libc::c_int);

impl Register {
    /// Get a register corresponding to the given index.
    ///
    /// # Errors
    ///
    /// Returns an error for `n >= 16`.
    pub fn new(n: u16) -> crate::Result<Self> {
        if n < 16 {
            Ok(Self(n.into()))
        } else {
            Err(crate::Error::TooManyAttributes)
        }
    }
}

/// An attribute index. This is the attribute's actual index in the input buffer,
/// and may correspond to any [`Register`] (or multiple) as input in the shader
/// program.
#[allow(dead_code)]
#[derive(Debug, Clone, Copy)]
pub struct Index(u8);

/// The data format of an attribute.
#[repr(u8)]
#[derive(Debug, Clone, Copy)]
#[doc(alias = "GPU_FORMATS")]
pub enum Format {
    /// A signed byte, i.e. [`i8`].
    Byte = ctru_sys::GPU_BYTE,
    /// An unsigned byte, i.e. [`u8`].
    UnsignedByte = ctru_sys::GPU_UNSIGNED_BYTE,
    /// A float, i.e. [`f32`].
    Float = ctru_sys::GPU_FLOAT,
    /// A short integer, i.e. [`i16`].
    Short = ctru_sys::GPU_SHORT,
}

impl From<Format> for u8 {
    fn from(value: Format) -> Self {
        value as u8
    }
}

// SAFETY: the RWLock ensures unique access when mutating the global struct, and
// we trust citro3d to Do The Right Thing™ and not mutate it otherwise.
unsafe impl Sync for Info {}
unsafe impl Send for Info {}

impl Default for Info {
    #[doc(alias = "AttrInfo_Init")]
    fn default() -> Self {
        let mut raw = MaybeUninit::zeroed();
        let raw = unsafe {
            citro3d_sys::AttrInfo_Init(raw.as_mut_ptr());
            raw.assume_init()
        };
        Self(raw)
    }
}

impl Info {
    /// Construct a new attribute info structure with no attributes.
    pub fn new() -> Self {
        Self::default()
    }

    pub(crate) fn copy_from(raw: *const citro3d_sys::C3D_AttrInfo) -> Option<Self> {
        if raw.is_null() {
            None
        } else {
            // This is less efficient than returning a pointer or something, but it's
            // safer since we don't know the lifetime of the pointee
            Some(Self(unsafe { *raw }))
        }
    }

    /// Add an attribute loader to the attribute info. The resulting attribute index
    /// indicates the registration order of the attributes.
    ///
    /// # Parameters
    ///
    /// * `register`: the shader program input register for this attribute.
    /// * `format`: the data format of this attribute.
    /// * `count`: the number of elements in each attribute (up to 4, corresponding
    ///   to `xyzw` / `rgba` / `stpq`).
    ///
    /// # Errors
    ///
    /// * If `count > 4`
    /// * If this attribute info already has the maximum number of attributes.
    #[doc(alias = "AttrInfo_AddLoader")]
    pub fn add_loader(
        &mut self,
        register: Register,
        format: Format,
        count: u8,
    ) -> crate::Result<Index> {
        if count > 4 {
            return Err(crate::Error::InvalidSize);
        }

        // SAFETY: the &mut self.0 reference is only used to access fields in
        // the attribute info, not stored somewhere for later use
        let ret = unsafe {
            citro3d_sys::AttrInfo_AddLoader(&mut self.0, register.0, format.into(), count.into())
        };

        let Ok(idx) = ret.try_into() else {
            return Err(crate::Error::TooManyAttributes);
        };

        Ok(Index(idx))
    }

    pub(crate) fn permutation(&self) -> u64 {
        self.0.permutation
    }

    /// Get the number of registered attributes.
    pub fn attr_count(&self) -> libc::c_int {
        self.0.attrCount
    }
}