logo
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
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
//! Color slot backend interface.
//!
//! This interface defines the low-level API color slots must implement to be usable.
//!
//! # Note to backend contributors
//!
//! If you are implementing a backend, there is nothing here to implement, but you will likely want to use some traits /
//! types from this module to use as constraint.

use crate::{
  backend::{framebuffer::Framebuffer, texture::Texture as TextureBackend},
  context::GraphicsContext,
  framebuffer::FramebufferError,
  texture::{TexelUpload, Texture},
};
use crate::{
  pixel::{ColorPixel, PixelFormat, RenderablePixel},
  texture::{Dimensionable, Sampler},
};

/// A color slot.
///
/// A color slot represents the associated _color data_ within a [`Framebuffer`]. This type is entirely constructed at
/// compile-time to ensure type safety. Even though this trait lives on the backend side of luminance, no backend is
/// supposed to implement it, but instead use it.
///
/// Three types of color slots exist:
///
/// - None, represented by the `()` implementor.
/// - A single color [`Texture`]. This type of color slot is often suitable for renderable framebuffer.
/// - A tuple of different color [`Texture`]. This situation is mostly used for _multi render target_, allowing to
///   render (via a fragment shader) into different part of the color slot.
///
/// For color slots that have color textures, the pixel type must be a [`RenderablePixel`] as well as a [`ColorPixel`].
///
/// Feel free to have a look at the list of implementors of this trait to know which types you can use as color slots.
pub trait ColorSlot<B, D>
where
  B: ?Sized + Framebuffer<D>,
  D: Dimensionable,
  D::Size: Copy,
{
  /// The associated data.
  ///
  /// This type represents the available, constructed object that will be usable outside of the [`Framebuffer`]. There
  /// is no trait to implement on this type but you mostly want to map a single [`Texture`] or a tuple of texture by
  /// using this as a type family for your implementor type.
  type ColorTextures;

  /// Pixel format representing the color slot.
  ///
  /// Those [`PixelFormat`] represent the format of each part of the color slot.
  fn color_formats() -> Vec<PixelFormat>;

  /// Reify the color slots into 0, 1 or several textures.
  ///
  /// This function must construct and initialize all the required textures.
  fn reify_color_textures<C>(
    ctx: &mut C,
    size: D::Size,
    mipmaps: usize,
    sampler: &Sampler,
    framebuffer: &mut B::FramebufferRepr,
    attachment_index: usize,
  ) -> Result<Self::ColorTextures, FramebufferError>
  where
    C: GraphicsContext<Backend = B>;
}

impl<B, D> ColorSlot<B, D> for ()
where
  B: ?Sized + Framebuffer<D>,
  D: Dimensionable,
  D::Size: Copy,
{
  type ColorTextures = ();

  fn color_formats() -> Vec<PixelFormat> {
    Vec::new()
  }

  fn reify_color_textures<C>(
    _: &mut C,
    _: D::Size,
    _: usize,
    _: &Sampler,
    _: &mut B::FramebufferRepr,
    _: usize,
  ) -> Result<Self::ColorTextures, FramebufferError>
  where
    C: GraphicsContext<Backend = B>,
  {
    Ok(())
  }
}

impl<B, D, P> ColorSlot<B, D> for P
where
  B: ?Sized + Framebuffer<D> + TextureBackend<D, P>,
  D: Dimensionable,
  D::Size: Copy,
  Self: ColorPixel + RenderablePixel,
{
  type ColorTextures = Texture<B, D, P>;

  fn color_formats() -> Vec<PixelFormat> {
    vec![P::pixel_format()]
  }

  fn reify_color_textures<C>(
    ctx: &mut C,
    size: D::Size,
    mipmaps: usize,
    sampler: &Sampler,
    framebuffer: &mut B::FramebufferRepr,
    attachment_index: usize,
  ) -> Result<Self::ColorTextures, FramebufferError>
  where
    C: GraphicsContext<Backend = B>,
  {
    let texture = Texture::new(ctx, size, sampler.clone(), TexelUpload::reserve(mipmaps))?;

    unsafe { B::attach_color_texture(framebuffer, &texture.repr, attachment_index)? };

    Ok(texture)
  }
}

macro_rules! impl_color_slot_tuple {
  ($($pf:ident),*) => {
    impl<B, D, $($pf),*> ColorSlot<B, D> for ($($pf),*)
    where
      B: ?Sized + Framebuffer<D> + $(TextureBackend<D, $pf> +)*,
      D: Dimensionable,
      D::Size: Copy,
      $(
        $pf: ColorPixel + RenderablePixel
      ),*
    {
      type ColorTextures = ($(Texture<B, D, $pf>),*);

      fn color_formats() -> Vec<PixelFormat> {
        vec![$($pf::pixel_format()),*]

      }

      impl_reify_color_textures!{ $($pf),* }
    }
  }
}

// A small helper macro to implement reify_color_textures in impl_color_slot_tuple!.
//
// We need this macro so that we can implement the increment logic without having to do weird
// arithmetic at runtime or have dead code.
macro_rules! impl_reify_color_textures {
  ($pf:ident , $($pfr:ident),*) => {
    fn reify_color_textures<C>(
      ctx: &mut C,
      size: D::Size,
      mipmaps: usize,
      sampler: &Sampler,
      framebuffer: &mut B::FramebufferRepr,
      mut attachment_index: usize,
    ) -> Result<Self::ColorTextures, FramebufferError>
    where
      C: GraphicsContext<Backend = B>,
    {
      let textures = (
        // first element of the tuple
        <$pf as ColorSlot<B, D>>::reify_color_textures(
          ctx,
          size,
          mipmaps,
          sampler,
          framebuffer,
          attachment_index,
        )?,
        // rest of the tuple
        $({
          attachment_index += 1;
          let texture = <$pfr as ColorSlot<B, D>>::reify_color_textures(
            ctx,
            size,
            mipmaps,
            sampler,
            framebuffer,
            attachment_index,
          )?;

          texture
        }),*
      );

      Ok(textures)
    }
  }
}

macro_rules! impl_color_slot_tuples {
  ($first:ident , $second:ident) => {
    // stop at pairs
    impl_color_slot_tuple!($first, $second);
  };

  ($first:ident , $($pf:ident),*) => {
    // implement the same list without the first type (reduced by one)
    impl_color_slot_tuples!($($pf),*);
    // implement the current list
    impl_color_slot_tuple!($first, $($pf),*);
  };
}

impl_color_slot_tuples!(P0, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11);