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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
use crate::buffer::{Buffer, BufferViewMut};
use crate::pipeline::graphics::util::BufferDescriptor;
use crate::pipeline::graphics::{TransformFeedback, TypedTransformFeedbackLayout};
use staticvec::StaticVec;

/// Encodes a description of a (set of) buffer(s) or buffer region(s) that can record the output
/// feedback from the transform stage of a graphics pipeline.
pub trait TransformFeedbackBuffers {
    fn encode<'a>(
        self,
        context: &'a mut TransformFeedbackBuffersEncodingContext,
    ) -> TransformFeedbackBuffersEncoding<'a>;
}

/// Helper trait for the implementation of [FeedbackBuffers] for tuple types.
pub trait TransformFeedbackBuffer {
    fn encode(self, encoding: &mut TransformFeedbackBuffersEncoding);
}

/// Sub-trait of [TransformFeedbackBuffers], where a type statically describes the feedback
/// attribute layout supported by the feedback buffers.
///
/// Transform feedback buffers that implement this trait may be bound to graphics pipelines with a
/// matching [TypedTransformFeedbackLayout] without further runtime checks.
///
/// # Unsafe
///
/// This trait must only by implemented for [FeedbackBuffers] types if the feedback buffers encoding
/// for any instance of the the type is guaranteed to be bit-wise compatible with the output
/// feedback recorded from the graphics pipeline.
pub unsafe trait TypedTransformFeedbackBuffers: TransformFeedbackBuffers {
    /// A type statically associated with a feedback attribute layout with which any instance of
    /// these [TypedFeedbackBuffers] is compatible.
    type Layout: TypedTransformFeedbackLayout;
}

/// Helper trait for the implementation of [TypedFeedbackBuffers] for tuple types.
pub unsafe trait TypedTransformFeedbackBuffer: TransformFeedbackBuffer {
    type TransformFeedback: TransformFeedback;
}

// Note that currently the FeedbackBuffersEncodingContext's only use is to serve as a form of
// lifetime erasure, it ensures if a buffer is mutable borrowed for transform feedback, then it
// should be impossible to create an IndexBufferEncoding for that pipeline task that also uses that
// buffer in safe Rust, without having to keep the actual borrow of that buffer alive (the resulting
// pipeline task needs to be `'static`).

/// Context required for the creation of a new [TransformFeedbackBuffersEncoding].
///
/// See [TransformFeedbackBuffersEncoding::new].
pub struct TransformFeedbackBuffersEncodingContext(());

impl TransformFeedbackBuffersEncodingContext {
    pub(crate) fn new() -> Self {
        TransformFeedbackBuffersEncodingContext(())
    }
}

/// An encoding of a description of a (set of) buffer(s) or buffer region(s) that can serve as the
/// feedback input data source(s) for a graphics pipeline.
///
/// See also [TransformFeedbackBuffers].
///
/// Contains slots for up to 16 buffers or buffer regions.
pub struct TransformFeedbackBuffersEncoding<'a> {
    #[allow(unused)]
    context: &'a mut TransformFeedbackBuffersEncodingContext,
    descriptors: StaticVec<BufferDescriptor, 16>,
}

impl<'a> TransformFeedbackBuffersEncoding<'a> {
    /// Returns a new empty [TransformFeedbackBuffersEncoding] for the given `context`.
    pub fn new(context: &'a mut TransformFeedbackBuffersEncodingContext) -> Self {
        TransformFeedbackBuffersEncoding {
            context,
            descriptors: StaticVec::new(),
        }
    }

    /// Adds a new buffer or buffer region to the description in the next free binding slot.
    ///
    /// # Panics
    ///
    /// Panics if called when all 16 feedback buffer slots have already been filled.
    pub fn add_feedback_buffer<'b, V, T>(&mut self, buffer: V)
    where
        V: Into<BufferViewMut<'b, [T]>>,
        T: 'b,
    {
        self.descriptors
            .push(BufferDescriptor::from_buffer_view(*buffer.into()));
    }

    pub(crate) fn into_descriptors(self) -> StaticVec<BufferDescriptor, 16> {
        self.descriptors
    }
}

impl<'a, T> TransformFeedbackBuffer for &'a mut Buffer<[T]> {
    fn encode(self, encoding: &mut TransformFeedbackBuffersEncoding) {
        encoding.add_feedback_buffer(self);
    }
}

unsafe impl<'a, T> TypedTransformFeedbackBuffer for &'a mut Buffer<[T]>
where
    T: TransformFeedback,
{
    type TransformFeedback = T;
}

impl<'a, T> TransformFeedbackBuffer for BufferViewMut<'a, [T]> {
    fn encode(self, encoding: &mut TransformFeedbackBuffersEncoding) {
        encoding.add_feedback_buffer(self);
    }
}

unsafe impl<'a, T> TypedTransformFeedbackBuffer for BufferViewMut<'a, [T]>
where
    T: TransformFeedback,
{
    type TransformFeedback = T;
}

macro_rules! impl_transform_feedback_buffers {
    ($($T:ident),*) => {
        #[allow(unused_parens)]
        impl<$($T),*> TransformFeedbackBuffers for ($($T),*)
        where
            $($T: TransformFeedbackBuffer),*
        {
            fn encode<'a>(
                self,
                context: &'a mut TransformFeedbackBuffersEncodingContext
            ) -> TransformFeedbackBuffersEncoding<'a> {
                let mut encoding = TransformFeedbackBuffersEncoding::new(context);

                #[allow(unused_parens, non_snake_case)]
                let ($($T),*) = self;

                $(
                    $T.encode(&mut encoding);
                )*

                encoding
            }
        }

        #[allow(unused_parens)]
        unsafe impl<$($T),*> TypedTransformFeedbackBuffers for ($($T),*)
        where
            $($T: TypedTransformFeedbackBuffer),*
        {
            #[allow(unused_parens)]
            type Layout = ($($T::TransformFeedback),*);
        }
    }
}

impl_transform_feedback_buffers!(T0);
impl_transform_feedback_buffers!(T0, T1);
impl_transform_feedback_buffers!(T0, T1, T2);
impl_transform_feedback_buffers!(T0, T1, T2, T3);
impl_transform_feedback_buffers!(T0, T1, T2, T3, T4);
impl_transform_feedback_buffers!(T0, T1, T2, T3, T4, T5);
impl_transform_feedback_buffers!(T0, T1, T2, T3, T4, T5, T6);
impl_transform_feedback_buffers!(T0, T1, T2, T3, T4, T5, T6, T7);
impl_transform_feedback_buffers!(T0, T1, T2, T3, T4, T5, T6, T7, T8);
impl_transform_feedback_buffers!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9);
impl_transform_feedback_buffers!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10);
impl_transform_feedback_buffers!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11);
impl_transform_feedback_buffers!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12);
impl_transform_feedback_buffers!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13);
impl_transform_feedback_buffers!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14);
impl_transform_feedback_buffers!(
    T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15
);