three_d/core/
render_target.rs

1//!
2//! Functionality for rendering to the screen or into textures.
3//!
4
5mod clear_state;
6#[doc(inline)]
7pub use clear_state::*;
8
9mod color_target;
10#[doc(inline)]
11pub use color_target::*;
12
13mod depth_target;
14#[doc(inline)]
15pub use depth_target::*;
16
17mod multisample;
18#[doc(inline)]
19pub use multisample::*;
20
21mod color_target_multisample;
22#[doc(inline)]
23pub use color_target_multisample::*;
24
25mod depth_target_multisample;
26#[doc(inline)]
27pub use depth_target_multisample::*;
28
29use crate::core::*;
30
31use crate::context::Framebuffer;
32///
33/// Adds additional functionality to clear, read from and write to the screen (see [RenderTarget::screen]) or a color texture and
34/// a depth texture at the same time (see [RenderTarget::new]).
35/// If you only want to perform an operation on either a color texture or depth texture, see [ColorTarget] and [DepthTarget] respectively.
36/// A render target purely adds functionality, so it can be created each time it is needed, the actual data is saved in the textures.
37///
38pub struct RenderTarget<'a> {
39    id: Option<Framebuffer>,
40    color: Option<ColorTarget<'a>>,
41    depth: Option<DepthTarget<'a>>,
42    pub(crate) context: Context,
43    width: u32,
44    height: u32,
45}
46
47impl<'a> RenderTarget<'a> {
48    ///
49    /// Returns the screen render target for this context.
50    /// Write to this render target to draw something on the screen.
51    ///
52    pub fn screen(context: &Context, width: u32, height: u32) -> Self {
53        Self {
54            context: context.clone(),
55            id: None,
56            color: None,
57            depth: None,
58            width,
59            height,
60        }
61    }
62
63    ///
64    /// Constructs a new render target that enables rendering into the given [ColorTarget] and [DepthTarget].
65    ///
66    pub fn new(color: ColorTarget<'a>, depth: DepthTarget<'a>) -> Self {
67        let width = color.width();
68        let height = color.height();
69        Self {
70            context: color.context.clone(),
71            id: Some(new_framebuffer(&color.context)),
72            color: Some(color),
73            depth: Some(depth),
74            width,
75            height,
76        }
77    }
78
79    /// The width of this target.
80    pub fn width(&self) -> u32 {
81        self.width
82    }
83
84    /// The height of this target.
85    pub fn height(&self) -> u32 {
86        self.height
87    }
88
89    ///
90    /// Clears the color and depth of this render target as defined by the given clear state.
91    ///
92    pub fn clear(&self, clear_state: ClearState) -> &Self {
93        self.clear_partially(self.scissor_box(), clear_state)
94    }
95
96    ///
97    /// Clears the color and depth of the part of this render target that is inside the given scissor box.
98    ///
99    pub fn clear_partially(&self, scissor_box: ScissorBox, clear_state: ClearState) -> &Self {
100        self.context.set_scissor(scissor_box);
101        self.bind(crate::context::DRAW_FRAMEBUFFER);
102        clear_state.apply(&self.context);
103        self
104    }
105
106    ///
107    /// Writes whatever rendered in the `render` closure into this render target.
108    ///
109    pub fn write<E: std::error::Error>(
110        &self,
111        render: impl FnOnce() -> Result<(), E>,
112    ) -> Result<&Self, E> {
113        self.write_partially(self.scissor_box(), render)
114    }
115
116    ///
117    /// Writes whatever rendered in the `render` closure into the part of this render target defined by the scissor box.
118    ///
119    pub fn write_partially<E: std::error::Error>(
120        &self,
121        scissor_box: ScissorBox,
122        render: impl FnOnce() -> Result<(), E>,
123    ) -> Result<&Self, E> {
124        self.context.set_scissor(scissor_box);
125        self.bind(crate::context::DRAW_FRAMEBUFFER);
126        render()?;
127        if let Some(ref color) = self.color {
128            color.generate_mip_maps();
129        }
130        Ok(self)
131    }
132
133    ///
134    /// Returns the colors of the pixels in this render target.
135    /// The number of channels per pixel and the data format for each channel returned from this function is specified by the generic parameter `T`.
136    ///
137    /// **Note:**
138    /// The base type of the generic parameter `T` must match the base type of the render target, for example if the render targets base type is `u8`, the base type of `T` must also be `u8`.
139    ///
140    /// **Web:**
141    /// The generic parameter `T` is limited to:
142    /// - Unsigned byte RGBA (Specify `T` as either `Vec4<u8>` or `[u8; 4]`) which works with any render target using `u8` as its base type.
143    /// - 32-bit float RGBA (Specify `T` as either `Vec4<f32>` or `[f32; 4]`) which works with any render target using `f16` or `f32` as its base type.
144    ///
145    pub fn read_color<T: TextureDataType>(&self) -> Vec<T> {
146        self.read_color_partially(self.scissor_box())
147    }
148
149    ///
150    /// Returns the colors of the pixels in this render target inside the given scissor box.
151    /// The number of channels per pixel and the data format for each channel returned from this function is specified by the generic parameter `T`.
152    ///
153    /// **Note:**
154    /// The base type of the generic parameter `T` must match the base type of the render target, for example if the render targets base type is `u8`, the base type of `T` must also be `u8`.
155    ///
156    /// **Web:**
157    /// The generic parameter `T` is limited to:
158    /// - Unsigned byte RGBA (Specify `T` as either `Vec4<u8>` or `[u8; 4]`) which works with any render target using `u8` as its base type.
159    /// - 32-bit float RGBA (Specify `T` as either `Vec4<f32>` or `[f32; 4]`) which works with any render target using `f16` or `f32` as its base type.
160    ///
161    pub fn read_color_partially<T: TextureDataType>(&self, scissor_box: ScissorBox) -> Vec<T> {
162        if self.id.is_some() && self.color.is_none() {
163            panic!("Cannot read color from a render target without a color target");
164        }
165        let format = format_from_data_type::<T>();
166        let data_type = T::data_type();
167
168        // On web, the read format needs to be RGBA and f16 is not supported (see https://webglfundamentals.org/webgl/lessons/webgl-readpixels.html).
169        #[cfg(target_arch = "wasm32")]
170        if format != crate::context::RGBA
171            || !(data_type == crate::context::UNSIGNED_BYTE || data_type == crate::context::FLOAT)
172        {
173            panic!("Only the texture data types `Vec4<T>` and `[T; 4]` where `T` is either `u8` or `f32` are supported when reading color from a render target on web.");
174        }
175
176        self.bind(crate::context::DRAW_FRAMEBUFFER);
177        self.bind(crate::context::READ_FRAMEBUFFER);
178        let data_size = std::mem::size_of::<T>();
179        let mut bytes =
180            vec![0u8; scissor_box.width as usize * scissor_box.height as usize * data_size];
181        unsafe {
182            self.context.read_pixels(
183                scissor_box.x,
184                scissor_box.y,
185                scissor_box.width as i32,
186                scissor_box.height as i32,
187                format,
188                data_type,
189                crate::context::PixelPackData::Slice(&mut bytes),
190            );
191        }
192        let mut pixels = from_byte_slice(&bytes).to_vec();
193        flip_y(
194            &mut pixels,
195            scissor_box.width as usize,
196            scissor_box.height as usize,
197        );
198        pixels
199    }
200
201    ///
202    /// Returns the depth values in this render target.
203    ///
204    #[cfg(not(target_arch = "wasm32"))]
205    pub fn read_depth(&self) -> Vec<f32> {
206        self.read_depth_partially(self.scissor_box())
207    }
208
209    ///
210    /// Returns the depth values in this render target inside the given scissor box.
211    ///
212    #[cfg(not(target_arch = "wasm32"))]
213    pub fn read_depth_partially(&self, scissor_box: ScissorBox) -> Vec<f32> {
214        if self.id.is_some() && self.depth.is_none() {
215            panic!("cannot read depth from a render target without a depth target");
216        }
217        self.bind(crate::context::DRAW_FRAMEBUFFER);
218        self.bind(crate::context::READ_FRAMEBUFFER);
219        let mut pixels = vec![0u8; scissor_box.width as usize * scissor_box.height as usize * 4];
220        unsafe {
221            self.context.read_pixels(
222                scissor_box.x,
223                scissor_box.y,
224                scissor_box.width as i32,
225                scissor_box.height as i32,
226                crate::context::DEPTH_COMPONENT,
227                crate::context::FLOAT,
228                crate::context::PixelPackData::Slice(&mut pixels),
229            );
230        }
231        from_byte_slice(&pixels).to_vec()
232    }
233
234    ///
235    /// Creates a [RenderTarget] with the given low-level [Framebuffer]. Should only be used if the [Framebuffer] is used for something else, ie. to be able
236    /// to combine this crate with functionality of another crate. Also see [Self::into_framebuffer].
237    ///
238    pub fn from_framebuffer(
239        context: &Context,
240        width: u32,
241        height: u32,
242        framebuffer: Framebuffer,
243    ) -> Self {
244        Self {
245            id: Some(framebuffer),
246            color: None,
247            depth: None,
248            context: context.clone(),
249            width,
250            height,
251        }
252    }
253
254    ///
255    /// Transforms this [RenderTarget] into a low-level [Framebuffer]. Should only be used if the [Framebuffer] is used for something else, ie. to be able
256    /// to combine this crate with functionality of another crate. Also see [Self::from_framebuffer].
257    ///
258    pub fn into_framebuffer(mut self) -> Option<Framebuffer> {
259        self.id.take()
260    }
261
262    pub(in crate::core) fn blit_to(&self, target: &RenderTarget) {
263        self.bind(crate::context::DRAW_FRAMEBUFFER);
264        target.bind(crate::context::DRAW_FRAMEBUFFER);
265        let target_is_screen = target.color.is_none() && target.depth.is_none();
266        let mask = if self.color.is_some() && (target.color.is_some() || target_is_screen) {
267            let mut mask = crate::context::COLOR_BUFFER_BIT;
268            if self.depth.is_some() && (target.depth.is_some() || target_is_screen) {
269                mask |= crate::context::DEPTH_BUFFER_BIT;
270            }
271            mask
272        } else if self.depth.is_some() && (target.depth.is_some() || target_is_screen) {
273            crate::context::DEPTH_BUFFER_BIT
274        } else {
275            unreachable!()
276        };
277        self.context
278            .set_scissor(ScissorBox::new_at_origo(target.width, target.height));
279        unsafe {
280            self.context
281                .bind_framebuffer(crate::context::READ_FRAMEBUFFER, self.id);
282
283            self.context.blit_framebuffer(
284                0,
285                0,
286                self.width as i32,
287                self.height as i32,
288                0,
289                0,
290                target.width as i32,
291                target.height as i32,
292                mask,
293                crate::context::NEAREST,
294            );
295        }
296    }
297
298    fn new_color(color: ColorTarget<'a>) -> Self {
299        let width = color.width();
300        let height = color.height();
301        Self {
302            context: color.context.clone(),
303            id: Some(new_framebuffer(&color.context)),
304            color: Some(color),
305            depth: None,
306            width,
307            height,
308        }
309    }
310
311    fn new_depth(depth: DepthTarget<'a>) -> Self {
312        let width = depth.width();
313        let height = depth.height();
314        Self {
315            context: depth.context.clone(),
316            id: Some(new_framebuffer(&depth.context)),
317            depth: Some(depth),
318            color: None,
319            width,
320            height,
321        }
322    }
323
324    fn bind(&self, target: u32) {
325        unsafe {
326            self.context.bind_framebuffer(target, self.id);
327        }
328        if let Some(ref color) = self.color {
329            color.bind(&self.context);
330        }
331        if let Some(ref depth) = self.depth {
332            depth.bind();
333        }
334    }
335}
336
337impl Drop for RenderTarget<'_> {
338    fn drop(&mut self) {
339        unsafe {
340            if let Some(id) = self.id {
341                self.context.delete_framebuffer(id);
342            }
343        }
344    }
345}
346
347fn size_with_mip(size: u32, mip: Option<u32>) -> u32 {
348    if let Some(mip) = mip {
349        size / 2u32.pow(mip)
350    } else {
351        size
352    }
353}
354
355fn new_framebuffer(context: &Context) -> crate::context::Framebuffer {
356    unsafe {
357        context
358            .create_framebuffer()
359            .expect("Failed creating frame buffer")
360    }
361}
362
363#[cfg(debug_assertions)]
364fn multisample_sanity_check(context: &Context, number_of_samples: u32) {
365    let max_samples: u32 = unsafe {
366        context
367            .get_parameter_i32(crate::context::MAX_SAMPLES)
368            .try_into()
369            .unwrap()
370    };
371    if number_of_samples > max_samples {
372        panic!("number_of_samples ({}) for multisample target is larger than supported number of samples: {}", number_of_samples, max_samples);
373    }
374    if (number_of_samples != 0) && number_of_samples & (number_of_samples - 1) != 0 {
375        panic!("number_of_samples ({}) for multisample target must be a power of 2 (and larger than 0).", number_of_samples);
376    }
377}
378
379macro_rules! impl_render_target_core_extensions_body {
380    () => {
381        ///
382        /// Returns the scissor box that encloses the entire target.
383        ///
384        pub fn scissor_box(&self) -> ScissorBox {
385            ScissorBox::new_at_origo(self.width(), self.height())
386        }
387
388        ///
389        /// Returns the viewport that encloses the entire target.
390        ///
391        pub fn viewport(&self) -> Viewport {
392            Viewport::new_at_origo(self.width(), self.height())
393        }
394    };
395}
396
397macro_rules! impl_render_target_core_extensions {
398    // 2 generic arguments with bounds
399    ($name:ident < $a:ident : $ta:tt , $b:ident : $tb:tt >) => {
400        impl<$a: $ta, $b: $tb> $name<$a, $b> {
401            impl_render_target_core_extensions_body!();
402        }
403    };
404    // 1 generic argument with bound
405    ($name:ident < $a:ident : $ta:tt >) => {
406        impl<$a: $ta> $name<$a> {
407            impl_render_target_core_extensions_body!();
408        }
409    };
410    // 1 liftetime argument
411    ($name:ident < $lt:lifetime >) => {
412        impl<$lt> $name<$lt> {
413            impl_render_target_core_extensions_body!();
414        }
415    };
416    // without any arguments
417    ($name:ty) => {
418        impl $name {
419            impl_render_target_core_extensions_body!();
420        }
421    };
422}
423
424impl_render_target_core_extensions!(RenderTarget<'a>);
425impl_render_target_core_extensions!(ColorTarget<'a>);
426impl_render_target_core_extensions!(DepthTarget<'a>);
427impl_render_target_core_extensions!(
428    RenderTargetMultisample<C: TextureDataType, D: DepthTextureDataType>
429);
430impl_render_target_core_extensions!(ColorTargetMultisample<C: TextureDataType>);
431impl_render_target_core_extensions!(DepthTargetMultisample<D: DepthTextureDataType>);