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>);