Skip to main content

fyrox_graphics/
framebuffer.rs

1// Copyright (c) 2019-present Dmitry Stepanov and Fyrox Engine contributors.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy
4// of this software and associated documentation files (the "Software"), to deal
5// in the Software without restriction, including without limitation the rights
6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7// copies of the Software, and to permit persons to whom the Software is
8// furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in all
11// copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19// SOFTWARE.
20
21#![warn(missing_docs)]
22
23//! Framebuffer objects are user-defined framebuffers which allow one to render to memory locations
24//! instead of the screen. First a framebuffer is created, and then textures are attached to it in order
25//! to control where the rendering data is stored. There are attachments for color, attachments for depth,
26//! and attachments for stencil, thereby providing memory for everything that is required for rendering.
27//! See [`GpuFrameBufferTrait`] docs for more info.
28
29use crate::sampler::GpuSampler;
30use crate::{
31    buffer::GpuBuffer,
32    core::{color::Color, math::Rect},
33    define_shared_wrapper,
34    error::FrameworkError,
35    geometry_buffer::GpuGeometryBuffer,
36    gpu_program::GpuProgram,
37    gpu_texture::{CubeMapFace, GpuTexture},
38    DrawParameters, ElementRange,
39};
40use bytemuck::Pod;
41use fyrox_core::define_as_any_trait;
42use std::cell::Cell;
43
44/// Frame buffer attachment kind.
45#[derive(Copy, Clone, PartialOrd, PartialEq, Hash, Debug, Eq)]
46pub enum AttachmentKind {
47    /// Color attachment, it should have a format that supports rendering (for example it cannot be
48    /// a compressed texture format).
49    Color,
50    /// Combined depth + stencil (usually it is 24 bits for depth and 8 for stencil) attachment.
51    DepthStencil,
52    /// Depth-only attachment. Usually it is 16 or 32 bits texture.
53    Depth,
54}
55
56/// A frame buffer attachment that tells the frame buffer object where to store the results of rendering.
57pub struct Attachment {
58    /// Current kind of attachment. Tells the renderer how the texture should be used
59    /// when rendering to the frame buffer. For example, is it color or depth?
60    pub kind: AttachmentKind,
61    /// A texture the renderer writes to. It may be writing the actual image, or it may just be writing
62    /// the depth, or stencil, depending on `kind`.
63    pub texture: GpuTexture,
64    /// Mip level of the texture where the rendering should happen. In most cases, it can be zero,
65    /// but this allows us to render multiple mipmap levels into a texture.
66    level: Cell<usize>,
67    /// Face of the cube map. This field is used only if the `texture` is a cube map.
68    /// This indicates which face of the cube will be rendered to.
69    cube_map_face: Cell<Option<CubeMapFace>>,
70}
71
72impl Attachment {
73    /// Mip level of the texture where the rendering should happen. In most cases, it can be zero.
74    pub fn level(&self) -> usize {
75        self.level.get()
76    }
77    /// Face of the cube map, this field is used only if the `texture` is a cube map.
78    /// This indicates which face of the cube will be rendered to.
79    pub fn cube_map_face(&self) -> Option<CubeMapFace> {
80        self.cube_map_face.get()
81    }
82    /// Mip level of the texture where the rendering should happen. In most cases, it can be zero.
83    pub fn set_level(&self, level: usize) {
84        self.level.set(level)
85    }
86    /// Face of the cube map, this field is used only if the `texture` is a cube map.
87    /// This indicates which face of the cube will be rendered to.
88    pub fn set_cube_map_face(&self, face: Option<CubeMapFace>) {
89        self.cube_map_face.set(face)
90    }
91    /// Creates a new [`AttachmentKind::Color`] attachment with the given texture.
92    pub fn color(texture: GpuTexture) -> Self {
93        Self {
94            kind: AttachmentKind::Color,
95            texture,
96            level: Cell::new(0),
97            cube_map_face: Cell::new(None),
98        }
99    }
100
101    /// Creates a new [`AttachmentKind::Color`] attachment with the given texture and the mip level.
102    pub fn color_with_level(texture: GpuTexture, level: usize) -> Self {
103        Self {
104            kind: AttachmentKind::Color,
105            texture,
106            level: Cell::new(level),
107            cube_map_face: Cell::new(None),
108        }
109    }
110
111    /// Creates a new [`AttachmentKind::Color`] attachment with the given texture and the cube map
112    /// face. The texture must be a cube map.
113    pub fn color_with_face(texture: GpuTexture, cube_map_face: CubeMapFace) -> Self {
114        Self {
115            kind: AttachmentKind::Color,
116            texture,
117            level: Cell::new(0),
118            cube_map_face: Cell::new(Some(cube_map_face)),
119        }
120    }
121
122    /// Creates a new [`AttachmentKind::Depth`] attachment with the given texture.
123    pub fn depth(texture: GpuTexture) -> Self {
124        Self {
125            kind: AttachmentKind::Depth,
126            texture,
127            level: Cell::new(0),
128            cube_map_face: Cell::new(None),
129        }
130    }
131
132    /// Creates a new [`AttachmentKind::DepthStencil`] attachment with the given texture.
133    pub fn depth_stencil(texture: GpuTexture) -> Self {
134        Self {
135            kind: AttachmentKind::DepthStencil,
136            texture,
137            level: Cell::new(0),
138            cube_map_face: Cell::new(None),
139        }
140    }
141}
142
143/// Defines a range of data in a particular buffer.
144#[derive(Default)]
145pub enum BufferDataUsage {
146    /// Use everything at once.
147    #[default]
148    UseEverything,
149    /// Use just a segment of data starting from the given `offset` with `size` bytes. It is used
150    /// in cases where you have a large buffer with lots of small blocks of information about
151    /// different objects. Instead of having a number of small buffers (which is memory- and performance
152    /// inefficient), you put everything into a large buffer and fill it lots of info at once and then
153    /// binding segments of the data the to the pipeline.
154    UseSegment {
155        /// Offset from the beginning of the buffer in bytes.
156        offset: usize,
157        /// Size of the data block in bytes.
158        size: usize,
159    },
160}
161
162/// A resource binding defines where to bind specific GPU resources.
163pub enum ResourceBinding {
164    /// Texture binding.
165    Texture {
166        /// A shared reference to a texture.
167        texture: GpuTexture,
168        /// A sampler that will be used to fetch the data from the texture.
169        sampler: GpuSampler,
170        /// Binding mode for the texture.
171        /// `glUniform1i` is used to assign this binding point to a texture uniform.
172        binding: usize,
173    },
174    /// Generic data buffer binding.
175    Buffer {
176        /// A shared reference to a buffer.
177        buffer: GpuBuffer,
178        /// Binding mode for the buffer.
179        binding: usize,
180        /// Data portion to use.
181        data_usage: BufferDataUsage,
182    },
183}
184
185impl ResourceBinding {
186    /// Creates a new explicit texture binding.
187    pub fn texture(texture: &GpuTexture, sampler: &GpuSampler, binding: usize) -> Self {
188        Self::Texture {
189            texture: texture.clone(),
190            sampler: sampler.clone(),
191            binding,
192        }
193    }
194
195    /// Creates a new explicit buffer binding.
196    pub fn buffer(buffer: &GpuBuffer, binding: usize, data_usage: BufferDataUsage) -> Self {
197        Self::Buffer {
198            buffer: buffer.clone(),
199            binding,
200            data_usage,
201        }
202    }
203}
204
205/// Resource binding group defines a set of bindings.
206pub struct ResourceBindGroup<'a> {
207    /// A reference to resource bindings array.
208    pub bindings: &'a [ResourceBinding],
209}
210
211/// Statistics for a single GPU draw call.
212#[derive(Debug, Copy, Clone, Default)]
213#[must_use]
214pub struct DrawCallStatistics {
215    /// Total number of rendered triangles.
216    pub triangles: usize,
217}
218
219define_as_any_trait!(GpuFrameBufferAsAny => GpuFrameBufferTrait);
220
221/// A part of a frame buffer from which to read the data.
222pub enum ReadTarget {
223    /// Read the depth attachment.
224    Depth,
225    /// Read the stencil attachment.
226    Stencil,
227    /// Read from one of the color attachments.
228    Color(usize),
229}
230
231/// Frame buffer is a set of images that is used as a storage for an image generated by a renderer.
232/// It consists of one or more color buffers and an optional depth/stencil buffer. Frame buffer is
233/// a high level abstraction that consolidates multiple images and supports drawing meshes to them
234/// with various drawing options.
235pub trait GpuFrameBufferTrait: GpuFrameBufferAsAny {
236    /// Returns a list of color attachments.
237    fn color_attachments(&self) -> &[Attachment];
238
239    /// Returns an optional depth/stencil attachment.
240    fn depth_attachment(&self) -> Option<&Attachment>;
241
242    /// Chooses a different face and mipmap level for the texture attached at the given index.
243    /// When the texture is a cubemap texture, this allows the framebuffer to render to a different
244    /// face of the cube. This method must not be called if the texture is not a cubemap.
245    fn set_cubemap_face(&self, attachment_index: usize, face: CubeMapFace, level: usize);
246
247    /// Performs data transfer from one frame buffer to another with scaling. It copies a region
248    /// defined by `src_x0`, `src_y0`, `src_x1`, `src_y1` coordinates from the frame buffer and
249    /// "pastes" it to the other frame buffer into a region defined by `dst_x0`, `dst_y0`, `dst_x1`,
250    /// `dst_y1` coordinates. If the source rectangle does not match the destination, the image will
251    /// be interpolated using nearest interpolation.
252    ///
253    /// This method can copy only specific parts of the image: `copy_color` tells the method to copy
254    /// the data from
255    fn blit_to(
256        &self,
257        dest: &GpuFrameBuffer,
258        src_x0: i32,
259        src_y0: i32,
260        src_x1: i32,
261        src_y1: i32,
262        dst_x0: i32,
263        dst_y0: i32,
264        dst_x1: i32,
265        dst_y1: i32,
266        copy_color: bool,
267        copy_depth: bool,
268        copy_stencil: bool,
269    );
270
271    /// Clears the frame buffer in the given viewport with the given set of optional values. This
272    /// method clears multiple attachments at once. What will be cleared defined by the provided
273    /// values. If `color` is not [`None`], then all the color attachments will be cleared with the
274    /// given color. The same applies to depth and stencil buffers.
275    fn clear(
276        &self,
277        viewport: Rect<i32>,
278        color: Option<Color>,
279        depth: Option<f32>,
280        stencil: Option<i32>,
281    );
282
283    /// Reads texture pixels.
284    fn read_pixels(&self, read_target: ReadTarget) -> Option<Vec<u8>>;
285
286    /// Draws the specified geometry buffer using the given GPU program and a set of resources. This
287    /// method the main method to draw anything.
288    ///
289    /// `geometry` - defines a [`GpuGeometryBuffer`], that contains vertices and index buffers and
290    /// essentially defines a mesh to render.
291    /// `viewport` - defines an area on screen that will be used to draw.
292    /// `program` - a [`GpuProgram`] defines a set of shaders (usually a pair of vertex + fragment)
293    /// that will define how the mesh will be rendered.
294    /// `params` - [`DrawParameters`] defines the state of graphics pipeline and essentially sets
295    /// a bunch of various parameters (such as backface culling, blending mode, various tests, etc.)
296    /// that will define how the rendering process is performed.
297    /// `resources` - a set of resource bind groups, that in their turn provides a set of resources
298    /// that bound to specific binding points.
299    /// `element_range` - defines which range of elements to draw.
300    fn draw(
301        &self,
302        geometry: &GpuGeometryBuffer,
303        viewport: Rect<i32>,
304        program: &GpuProgram,
305        params: &DrawParameters,
306        resources: &[ResourceBindGroup],
307        element_range: ElementRange,
308    ) -> Result<DrawCallStatistics, FrameworkError>;
309
310    /// Almost the same as [`Self::draw`], but draws multiple instances at once. The caller must
311    /// supply all the required data per each instance, it could be done in different ways. The data
312    /// could be supplied in vertex attributes, uniform buffers, textures, etc.
313    fn draw_instances(
314        &self,
315        instance_count: usize,
316        geometry: &GpuGeometryBuffer,
317        viewport: Rect<i32>,
318        program: &GpuProgram,
319        params: &DrawParameters,
320        resources: &[ResourceBindGroup],
321        element_range: ElementRange,
322    ) -> Result<DrawCallStatistics, FrameworkError>;
323}
324
325impl dyn GpuFrameBufferTrait {
326    /// Reads the pixels and reinterprets them using the given type.
327    pub fn read_pixels_of_type<T>(&self, read_target: ReadTarget) -> Option<Vec<T>>
328    where
329        T: Pod,
330    {
331        let mut bytes = self.read_pixels(read_target)?;
332        let typed = unsafe {
333            Vec::<T>::from_raw_parts(
334                bytes.as_mut_ptr() as *mut T,
335                bytes.len() / size_of::<T>(),
336                bytes.capacity() / size_of::<T>(),
337            )
338        };
339        std::mem::forget(bytes);
340        Some(typed)
341    }
342}
343
344define_shared_wrapper!(GpuFrameBuffer<dyn GpuFrameBufferTrait>);