Skip to main content

mtl_gpu/vertex/
mod.rs

1//! Vertex descriptor types.
2//!
3//! Corresponds to `Metal/MTLVertexDescriptor.hpp`.
4//!
5//! Vertex descriptors describe the organization of vertex data in buffers
6//! for use with render pipelines.
7
8use std::ffi::c_void;
9use std::ptr::NonNull;
10
11use mtl_foundation::{Referencing, UInteger};
12use mtl_sys::{Class, msg_send_0, msg_send_1, msg_send_2, sel};
13
14use crate::enums::{VertexFormat, VertexStepFunction};
15
16/// Constant indicating that the stride should be computed dynamically.
17///
18/// C++ equivalent: `MTL::BufferLayoutStrideDynamic`
19pub const BUFFER_LAYOUT_STRIDE_DYNAMIC: UInteger = UInteger::MAX;
20
21// ============================================================================
22// VertexBufferLayoutDescriptor
23// ============================================================================
24
25/// Describes the layout of vertex data in a buffer.
26///
27/// C++ equivalent: `MTL::VertexBufferLayoutDescriptor`
28#[repr(transparent)]
29pub struct VertexBufferLayoutDescriptor(pub(crate) NonNull<c_void>);
30
31impl VertexBufferLayoutDescriptor {
32    /// Create a new vertex buffer layout descriptor.
33    ///
34    /// C++ equivalent: `static VertexBufferLayoutDescriptor* alloc()->init()`
35    pub fn new() -> Option<Self> {
36        unsafe {
37            let class = Class::get("MTLVertexBufferLayoutDescriptor")?;
38            let ptr: *mut c_void = msg_send_0(class.as_ptr(), sel!(alloc));
39            if ptr.is_null() {
40                return None;
41            }
42            let ptr: *mut c_void = msg_send_0(ptr, sel!(init));
43            Self::from_raw(ptr)
44        }
45    }
46
47    /// Create a VertexBufferLayoutDescriptor from a raw pointer.
48    ///
49    /// # Safety
50    ///
51    /// The pointer must be a valid Metal vertex buffer layout descriptor object.
52    #[inline]
53    pub unsafe fn from_raw(ptr: *mut c_void) -> Option<Self> {
54        NonNull::new(ptr).map(Self)
55    }
56
57    /// Get the raw pointer to the descriptor.
58    #[inline]
59    pub fn as_raw(&self) -> *mut c_void {
60        self.0.as_ptr()
61    }
62
63    // =========================================================================
64    // Properties
65    // =========================================================================
66
67    /// Get the stride, in bytes, between vertex data in the buffer.
68    ///
69    /// C++ equivalent: `NS::UInteger stride() const`
70    #[inline]
71    pub fn stride(&self) -> UInteger {
72        unsafe { msg_send_0(self.as_ptr(), sel!(stride)) }
73    }
74
75    /// Set the stride, in bytes, between vertex data in the buffer.
76    ///
77    /// C++ equivalent: `void setStride(NS::UInteger stride)`
78    #[inline]
79    pub fn set_stride(&self, stride: UInteger) {
80        unsafe {
81            let _: () = msg_send_1(self.as_ptr(), sel!(setStride:), stride);
82        }
83    }
84
85    /// Get the step function that determines how the data is fetched.
86    ///
87    /// C++ equivalent: `VertexStepFunction stepFunction() const`
88    #[inline]
89    pub fn step_function(&self) -> VertexStepFunction {
90        unsafe { msg_send_0(self.as_ptr(), sel!(stepFunction)) }
91    }
92
93    /// Set the step function that determines how the data is fetched.
94    ///
95    /// C++ equivalent: `void setStepFunction(MTL::VertexStepFunction stepFunction)`
96    #[inline]
97    pub fn set_step_function(&self, step_function: VertexStepFunction) {
98        unsafe {
99            let _: () = msg_send_1(self.as_ptr(), sel!(setStepFunction:), step_function);
100        }
101    }
102
103    /// Get the step rate that determines how often the data is fetched.
104    ///
105    /// C++ equivalent: `NS::UInteger stepRate() const`
106    #[inline]
107    pub fn step_rate(&self) -> UInteger {
108        unsafe { msg_send_0(self.as_ptr(), sel!(stepRate)) }
109    }
110
111    /// Set the step rate that determines how often the data is fetched.
112    ///
113    /// C++ equivalent: `void setStepRate(NS::UInteger stepRate)`
114    #[inline]
115    pub fn set_step_rate(&self, step_rate: UInteger) {
116        unsafe {
117            let _: () = msg_send_1(self.as_ptr(), sel!(setStepRate:), step_rate);
118        }
119    }
120}
121
122impl Clone for VertexBufferLayoutDescriptor {
123    fn clone(&self) -> Self {
124        unsafe {
125            msg_send_0::<*mut c_void>(self.as_ptr(), sel!(retain));
126        }
127        Self(self.0)
128    }
129}
130
131impl Drop for VertexBufferLayoutDescriptor {
132    fn drop(&mut self) {
133        unsafe {
134            msg_send_0::<()>(self.as_ptr(), sel!(release));
135        }
136    }
137}
138
139impl Referencing for VertexBufferLayoutDescriptor {
140    #[inline]
141    fn as_ptr(&self) -> *const c_void {
142        self.0.as_ptr()
143    }
144}
145
146unsafe impl Send for VertexBufferLayoutDescriptor {}
147unsafe impl Sync for VertexBufferLayoutDescriptor {}
148
149impl std::fmt::Debug for VertexBufferLayoutDescriptor {
150    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
151        f.debug_struct("VertexBufferLayoutDescriptor")
152            .field("stride", &self.stride())
153            .field("step_function", &self.step_function())
154            .field("step_rate", &self.step_rate())
155            .finish()
156    }
157}
158
159// ============================================================================
160// VertexBufferLayoutDescriptorArray
161// ============================================================================
162
163/// An array of vertex buffer layout descriptors.
164///
165/// C++ equivalent: `MTL::VertexBufferLayoutDescriptorArray`
166#[repr(transparent)]
167pub struct VertexBufferLayoutDescriptorArray(pub(crate) NonNull<c_void>);
168
169impl VertexBufferLayoutDescriptorArray {
170    /// Create a new vertex buffer layout descriptor array.
171    ///
172    /// C++ equivalent: `static VertexBufferLayoutDescriptorArray* alloc()->init()`
173    pub fn new() -> Option<Self> {
174        unsafe {
175            let class = Class::get("MTLVertexBufferLayoutDescriptorArray")?;
176            let ptr: *mut c_void = msg_send_0(class.as_ptr(), sel!(alloc));
177            if ptr.is_null() {
178                return None;
179            }
180            let ptr: *mut c_void = msg_send_0(ptr, sel!(init));
181            Self::from_raw(ptr)
182        }
183    }
184
185    /// Create a VertexBufferLayoutDescriptorArray from a raw pointer.
186    ///
187    /// # Safety
188    ///
189    /// The pointer must be a valid Metal vertex buffer layout descriptor array object.
190    #[inline]
191    pub unsafe fn from_raw(ptr: *mut c_void) -> Option<Self> {
192        NonNull::new(ptr).map(Self)
193    }
194
195    /// Get the raw pointer to the array.
196    #[inline]
197    pub fn as_raw(&self) -> *mut c_void {
198        self.0.as_ptr()
199    }
200
201    // =========================================================================
202    // Array Access
203    // =========================================================================
204
205    /// Get the descriptor at the specified index.
206    ///
207    /// C++ equivalent: `VertexBufferLayoutDescriptor* object(NS::UInteger index)`
208    pub fn object(&self, index: UInteger) -> Option<VertexBufferLayoutDescriptor> {
209        unsafe {
210            let ptr: *mut c_void =
211                msg_send_1(self.as_ptr(), sel!(objectAtIndexedSubscript:), index);
212            if ptr.is_null() {
213                return None;
214            }
215            // Retain since we're returning an owned reference
216            msg_send_0::<*mut c_void>(ptr, sel!(retain));
217            VertexBufferLayoutDescriptor::from_raw(ptr)
218        }
219    }
220
221    /// Set the descriptor at the specified index.
222    ///
223    /// C++ equivalent: `void setObject(const MTL::VertexBufferLayoutDescriptor* bufferDesc, NS::UInteger index)`
224    pub fn set_object(&self, buffer_desc: &VertexBufferLayoutDescriptor, index: UInteger) {
225        unsafe {
226            let _: () = msg_send_2(
227                self.as_ptr(),
228                sel!(setObject:atIndexedSubscript:),
229                buffer_desc.as_ptr(),
230                index,
231            );
232        }
233    }
234}
235
236impl Clone for VertexBufferLayoutDescriptorArray {
237    fn clone(&self) -> Self {
238        unsafe {
239            msg_send_0::<*mut c_void>(self.as_ptr(), sel!(retain));
240        }
241        Self(self.0)
242    }
243}
244
245impl Drop for VertexBufferLayoutDescriptorArray {
246    fn drop(&mut self) {
247        unsafe {
248            msg_send_0::<()>(self.as_ptr(), sel!(release));
249        }
250    }
251}
252
253impl Referencing for VertexBufferLayoutDescriptorArray {
254    #[inline]
255    fn as_ptr(&self) -> *const c_void {
256        self.0.as_ptr()
257    }
258}
259
260unsafe impl Send for VertexBufferLayoutDescriptorArray {}
261unsafe impl Sync for VertexBufferLayoutDescriptorArray {}
262
263impl std::fmt::Debug for VertexBufferLayoutDescriptorArray {
264    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
265        f.debug_struct("VertexBufferLayoutDescriptorArray").finish()
266    }
267}
268
269// ============================================================================
270// VertexAttributeDescriptor
271// ============================================================================
272
273/// Describes a vertex attribute in a vertex descriptor.
274///
275/// C++ equivalent: `MTL::VertexAttributeDescriptor`
276#[repr(transparent)]
277pub struct VertexAttributeDescriptor(pub(crate) NonNull<c_void>);
278
279impl VertexAttributeDescriptor {
280    /// Create a new vertex attribute descriptor.
281    ///
282    /// C++ equivalent: `static VertexAttributeDescriptor* alloc()->init()`
283    pub fn new() -> Option<Self> {
284        unsafe {
285            let class = Class::get("MTLVertexAttributeDescriptor")?;
286            let ptr: *mut c_void = msg_send_0(class.as_ptr(), sel!(alloc));
287            if ptr.is_null() {
288                return None;
289            }
290            let ptr: *mut c_void = msg_send_0(ptr, sel!(init));
291            Self::from_raw(ptr)
292        }
293    }
294
295    /// Create a VertexAttributeDescriptor from a raw pointer.
296    ///
297    /// # Safety
298    ///
299    /// The pointer must be a valid Metal vertex attribute descriptor object.
300    #[inline]
301    pub unsafe fn from_raw(ptr: *mut c_void) -> Option<Self> {
302        NonNull::new(ptr).map(Self)
303    }
304
305    /// Get the raw pointer to the descriptor.
306    #[inline]
307    pub fn as_raw(&self) -> *mut c_void {
308        self.0.as_ptr()
309    }
310
311    // =========================================================================
312    // Properties
313    // =========================================================================
314
315    /// Get the format of the vertex attribute.
316    ///
317    /// C++ equivalent: `VertexFormat format() const`
318    #[inline]
319    pub fn format(&self) -> VertexFormat {
320        unsafe { msg_send_0(self.as_ptr(), sel!(format)) }
321    }
322
323    /// Set the format of the vertex attribute.
324    ///
325    /// C++ equivalent: `void setFormat(MTL::VertexFormat format)`
326    #[inline]
327    pub fn set_format(&self, format: VertexFormat) {
328        unsafe {
329            let _: () = msg_send_1(self.as_ptr(), sel!(setFormat:), format);
330        }
331    }
332
333    /// Get the offset, in bytes, of this attribute in the vertex buffer.
334    ///
335    /// C++ equivalent: `NS::UInteger offset() const`
336    #[inline]
337    pub fn offset(&self) -> UInteger {
338        unsafe { msg_send_0(self.as_ptr(), sel!(offset)) }
339    }
340
341    /// Set the offset, in bytes, of this attribute in the vertex buffer.
342    ///
343    /// C++ equivalent: `void setOffset(NS::UInteger offset)`
344    #[inline]
345    pub fn set_offset(&self, offset: UInteger) {
346        unsafe {
347            let _: () = msg_send_1(self.as_ptr(), sel!(setOffset:), offset);
348        }
349    }
350
351    /// Get the index of the buffer that contains this attribute.
352    ///
353    /// C++ equivalent: `NS::UInteger bufferIndex() const`
354    #[inline]
355    pub fn buffer_index(&self) -> UInteger {
356        unsafe { msg_send_0(self.as_ptr(), sel!(bufferIndex)) }
357    }
358
359    /// Set the index of the buffer that contains this attribute.
360    ///
361    /// C++ equivalent: `void setBufferIndex(NS::UInteger bufferIndex)`
362    #[inline]
363    pub fn set_buffer_index(&self, buffer_index: UInteger) {
364        unsafe {
365            let _: () = msg_send_1(self.as_ptr(), sel!(setBufferIndex:), buffer_index);
366        }
367    }
368}
369
370impl Clone for VertexAttributeDescriptor {
371    fn clone(&self) -> Self {
372        unsafe {
373            msg_send_0::<*mut c_void>(self.as_ptr(), sel!(retain));
374        }
375        Self(self.0)
376    }
377}
378
379impl Drop for VertexAttributeDescriptor {
380    fn drop(&mut self) {
381        unsafe {
382            msg_send_0::<()>(self.as_ptr(), sel!(release));
383        }
384    }
385}
386
387impl Referencing for VertexAttributeDescriptor {
388    #[inline]
389    fn as_ptr(&self) -> *const c_void {
390        self.0.as_ptr()
391    }
392}
393
394unsafe impl Send for VertexAttributeDescriptor {}
395unsafe impl Sync for VertexAttributeDescriptor {}
396
397impl std::fmt::Debug for VertexAttributeDescriptor {
398    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
399        f.debug_struct("VertexAttributeDescriptor")
400            .field("format", &self.format())
401            .field("offset", &self.offset())
402            .field("buffer_index", &self.buffer_index())
403            .finish()
404    }
405}
406
407// ============================================================================
408// VertexAttributeDescriptorArray
409// ============================================================================
410
411/// An array of vertex attribute descriptors.
412///
413/// C++ equivalent: `MTL::VertexAttributeDescriptorArray`
414#[repr(transparent)]
415pub struct VertexAttributeDescriptorArray(pub(crate) NonNull<c_void>);
416
417impl VertexAttributeDescriptorArray {
418    /// Create a new vertex attribute descriptor array.
419    ///
420    /// C++ equivalent: `static VertexAttributeDescriptorArray* alloc()->init()`
421    pub fn new() -> Option<Self> {
422        unsafe {
423            let class = Class::get("MTLVertexAttributeDescriptorArray")?;
424            let ptr: *mut c_void = msg_send_0(class.as_ptr(), sel!(alloc));
425            if ptr.is_null() {
426                return None;
427            }
428            let ptr: *mut c_void = msg_send_0(ptr, sel!(init));
429            Self::from_raw(ptr)
430        }
431    }
432
433    /// Create a VertexAttributeDescriptorArray from a raw pointer.
434    ///
435    /// # Safety
436    ///
437    /// The pointer must be a valid Metal vertex attribute descriptor array object.
438    #[inline]
439    pub unsafe fn from_raw(ptr: *mut c_void) -> Option<Self> {
440        NonNull::new(ptr).map(Self)
441    }
442
443    /// Get the raw pointer to the array.
444    #[inline]
445    pub fn as_raw(&self) -> *mut c_void {
446        self.0.as_ptr()
447    }
448
449    // =========================================================================
450    // Array Access
451    // =========================================================================
452
453    /// Get the descriptor at the specified index.
454    ///
455    /// C++ equivalent: `VertexAttributeDescriptor* object(NS::UInteger index)`
456    pub fn object(&self, index: UInteger) -> Option<VertexAttributeDescriptor> {
457        unsafe {
458            let ptr: *mut c_void =
459                msg_send_1(self.as_ptr(), sel!(objectAtIndexedSubscript:), index);
460            if ptr.is_null() {
461                return None;
462            }
463            // Retain since we're returning an owned reference
464            msg_send_0::<*mut c_void>(ptr, sel!(retain));
465            VertexAttributeDescriptor::from_raw(ptr)
466        }
467    }
468
469    /// Set the descriptor at the specified index.
470    ///
471    /// C++ equivalent: `void setObject(const MTL::VertexAttributeDescriptor* attributeDesc, NS::UInteger index)`
472    pub fn set_object(&self, attribute_desc: &VertexAttributeDescriptor, index: UInteger) {
473        unsafe {
474            let _: () = msg_send_2(
475                self.as_ptr(),
476                sel!(setObject:atIndexedSubscript:),
477                attribute_desc.as_ptr(),
478                index,
479            );
480        }
481    }
482}
483
484impl Clone for VertexAttributeDescriptorArray {
485    fn clone(&self) -> Self {
486        unsafe {
487            msg_send_0::<*mut c_void>(self.as_ptr(), sel!(retain));
488        }
489        Self(self.0)
490    }
491}
492
493impl Drop for VertexAttributeDescriptorArray {
494    fn drop(&mut self) {
495        unsafe {
496            msg_send_0::<()>(self.as_ptr(), sel!(release));
497        }
498    }
499}
500
501impl Referencing for VertexAttributeDescriptorArray {
502    #[inline]
503    fn as_ptr(&self) -> *const c_void {
504        self.0.as_ptr()
505    }
506}
507
508unsafe impl Send for VertexAttributeDescriptorArray {}
509unsafe impl Sync for VertexAttributeDescriptorArray {}
510
511impl std::fmt::Debug for VertexAttributeDescriptorArray {
512    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
513        f.debug_struct("VertexAttributeDescriptorArray").finish()
514    }
515}
516
517// ============================================================================
518// VertexDescriptor
519// ============================================================================
520
521/// Describes the organization of vertex data for a render pipeline.
522///
523/// C++ equivalent: `MTL::VertexDescriptor`
524#[repr(transparent)]
525pub struct VertexDescriptor(pub(crate) NonNull<c_void>);
526
527impl VertexDescriptor {
528    /// Create a new vertex descriptor.
529    ///
530    /// C++ equivalent: `static VertexDescriptor* alloc()->init()`
531    pub fn new() -> Option<Self> {
532        unsafe {
533            let class = Class::get("MTLVertexDescriptor")?;
534            let ptr: *mut c_void = msg_send_0(class.as_ptr(), sel!(alloc));
535            if ptr.is_null() {
536                return None;
537            }
538            let ptr: *mut c_void = msg_send_0(ptr, sel!(init));
539            Self::from_raw(ptr)
540        }
541    }
542
543    /// Create a new vertex descriptor using the class method.
544    ///
545    /// C++ equivalent: `static VertexDescriptor* vertexDescriptor()`
546    pub fn vertex_descriptor() -> Option<Self> {
547        unsafe {
548            let class = Class::get("MTLVertexDescriptor")?;
549            let ptr: *mut c_void = msg_send_0(class.as_ptr(), sel!(vertexDescriptor));
550            if ptr.is_null() {
551                return None;
552            }
553            // Retain since this is an autoreleased object
554            msg_send_0::<*mut c_void>(ptr, sel!(retain));
555            Self::from_raw(ptr)
556        }
557    }
558
559    /// Create a VertexDescriptor from a raw pointer.
560    ///
561    /// # Safety
562    ///
563    /// The pointer must be a valid Metal vertex descriptor object.
564    #[inline]
565    pub unsafe fn from_raw(ptr: *mut c_void) -> Option<Self> {
566        NonNull::new(ptr).map(Self)
567    }
568
569    /// Get the raw pointer to the descriptor.
570    #[inline]
571    pub fn as_raw(&self) -> *mut c_void {
572        self.0.as_ptr()
573    }
574
575    // =========================================================================
576    // Properties
577    // =========================================================================
578
579    /// Get the array of vertex buffer layout descriptors.
580    ///
581    /// C++ equivalent: `VertexBufferLayoutDescriptorArray* layouts() const`
582    pub fn layouts(&self) -> VertexBufferLayoutDescriptorArray {
583        unsafe {
584            let ptr: *mut c_void = msg_send_0(self.as_ptr(), sel!(layouts));
585            // Retain since we're returning an owned reference
586            msg_send_0::<*mut c_void>(ptr, sel!(retain));
587            VertexBufferLayoutDescriptorArray::from_raw(ptr)
588                .expect("vertex descriptor layouts should not be null")
589        }
590    }
591
592    /// Get the array of vertex attribute descriptors.
593    ///
594    /// C++ equivalent: `VertexAttributeDescriptorArray* attributes() const`
595    pub fn attributes(&self) -> VertexAttributeDescriptorArray {
596        unsafe {
597            let ptr: *mut c_void = msg_send_0(self.as_ptr(), sel!(attributes));
598            // Retain since we're returning an owned reference
599            msg_send_0::<*mut c_void>(ptr, sel!(retain));
600            VertexAttributeDescriptorArray::from_raw(ptr)
601                .expect("vertex descriptor attributes should not be null")
602        }
603    }
604
605    // =========================================================================
606    // Methods
607    // =========================================================================
608
609    /// Reset the vertex descriptor to default values.
610    ///
611    /// C++ equivalent: `void reset()`
612    #[inline]
613    pub fn reset(&self) {
614        unsafe {
615            msg_send_0::<()>(self.as_ptr(), sel!(reset));
616        }
617    }
618}
619
620impl Clone for VertexDescriptor {
621    fn clone(&self) -> Self {
622        unsafe {
623            msg_send_0::<*mut c_void>(self.as_ptr(), sel!(retain));
624        }
625        Self(self.0)
626    }
627}
628
629impl Drop for VertexDescriptor {
630    fn drop(&mut self) {
631        unsafe {
632            msg_send_0::<()>(self.as_ptr(), sel!(release));
633        }
634    }
635}
636
637impl Referencing for VertexDescriptor {
638    #[inline]
639    fn as_ptr(&self) -> *const c_void {
640        self.0.as_ptr()
641    }
642}
643
644unsafe impl Send for VertexDescriptor {}
645unsafe impl Sync for VertexDescriptor {}
646
647impl std::fmt::Debug for VertexDescriptor {
648    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
649        f.debug_struct("VertexDescriptor").finish()
650    }
651}
652
653// ============================================================================
654// Tests
655// ============================================================================
656
657#[cfg(test)]
658mod tests {
659    use super::*;
660
661    #[test]
662    fn test_buffer_layout_stride_dynamic() {
663        assert_eq!(BUFFER_LAYOUT_STRIDE_DYNAMIC, UInteger::MAX);
664    }
665
666    #[test]
667    fn test_vertex_descriptor_creation() {
668        let desc = VertexDescriptor::new();
669        assert!(desc.is_some());
670    }
671
672    #[test]
673    fn test_vertex_descriptor_class_method() {
674        let desc = VertexDescriptor::vertex_descriptor();
675        assert!(desc.is_some());
676    }
677
678    #[test]
679    fn test_vertex_buffer_layout_descriptor() {
680        let desc = VertexBufferLayoutDescriptor::new();
681        assert!(desc.is_some());
682
683        let desc = desc.unwrap();
684        // Default stride should be 0
685        assert_eq!(desc.stride(), 0);
686
687        desc.set_stride(32);
688        assert_eq!(desc.stride(), 32);
689
690        // Default step function should be PER_VERTEX
691        assert_eq!(desc.step_function(), VertexStepFunction::PER_VERTEX);
692
693        desc.set_step_function(VertexStepFunction::PER_INSTANCE);
694        assert_eq!(desc.step_function(), VertexStepFunction::PER_INSTANCE);
695
696        // Default step rate should be 1
697        assert_eq!(desc.step_rate(), 1);
698
699        desc.set_step_rate(2);
700        assert_eq!(desc.step_rate(), 2);
701    }
702
703    #[test]
704    fn test_vertex_attribute_descriptor() {
705        let desc = VertexAttributeDescriptor::new();
706        assert!(desc.is_some());
707
708        let desc = desc.unwrap();
709        // Default format should be INVALID
710        assert_eq!(desc.format(), VertexFormat::INVALID);
711
712        desc.set_format(VertexFormat::FLOAT3);
713        assert_eq!(desc.format(), VertexFormat::FLOAT3);
714
715        // Default offset should be 0
716        assert_eq!(desc.offset(), 0);
717
718        desc.set_offset(16);
719        assert_eq!(desc.offset(), 16);
720
721        // Default buffer index should be 0
722        assert_eq!(desc.buffer_index(), 0);
723
724        desc.set_buffer_index(1);
725        assert_eq!(desc.buffer_index(), 1);
726    }
727
728    #[test]
729    fn test_vertex_descriptor_layouts_and_attributes() {
730        let desc = VertexDescriptor::new().unwrap();
731
732        // Get layouts array and configure
733        let layouts = desc.layouts();
734        let layout = layouts.object(0);
735        assert!(layout.is_some());
736
737        let layout = layout.unwrap();
738        layout.set_stride(32);
739        layout.set_step_function(VertexStepFunction::PER_VERTEX);
740
741        // Get attributes array and configure
742        let attributes = desc.attributes();
743        let attr = attributes.object(0);
744        assert!(attr.is_some());
745
746        let attr = attr.unwrap();
747        attr.set_format(VertexFormat::FLOAT3);
748        attr.set_offset(0);
749        attr.set_buffer_index(0);
750    }
751
752    #[test]
753    fn test_vertex_descriptor_reset() {
754        let desc = VertexDescriptor::new().unwrap();
755
756        // Configure some attributes
757        let attributes = desc.attributes();
758        let attr = attributes.object(0).unwrap();
759        attr.set_format(VertexFormat::FLOAT4);
760        attr.set_offset(16);
761
762        // Reset should clear to defaults
763        desc.reset();
764
765        let attr = attributes.object(0).unwrap();
766        assert_eq!(attr.format(), VertexFormat::INVALID);
767        assert_eq!(attr.offset(), 0);
768    }
769}