Skip to main content

fyrox_graphics_gl/
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
21pub use crate::{
22    buffer::GlBuffer, geometry_buffer::GlGeometryBuffer, program::GlProgram, sampler::GlSampler,
23    server::GlGraphicsServer, texture::GlTexture, ToGlConstant,
24};
25use crate::{server::FrameBufferBindingPoint, texture::PixelDescriptor};
26use fyrox_graphics::{
27    buffer::GpuBufferTrait,
28    core::{color::Color, math::Rect},
29    error::FrameworkError,
30    framebuffer::ReadTarget,
31    framebuffer::{
32        Attachment, AttachmentKind, BufferDataUsage, DrawCallStatistics, GpuFrameBuffer,
33        GpuFrameBufferTrait, ResourceBindGroup, ResourceBinding,
34    },
35    geometry_buffer::GpuGeometryBuffer,
36    gpu_program::GpuProgram,
37    gpu_texture::image_2d_size_bytes,
38    gpu_texture::{CubeMapFace, GpuTextureKind, GpuTextureTrait, PixelElementKind},
39    ColorMask, DrawParameters, ElementRange,
40};
41use glow::{HasContext, PixelPackData};
42use std::rc::Weak;
43
44pub struct GlFrameBuffer {
45    state: Weak<GlGraphicsServer>,
46    fbo: Option<glow::Framebuffer>,
47    depth_attachment: Option<Attachment>,
48    color_attachments: Vec<Attachment>,
49}
50
51unsafe fn set_attachment(
52    server: &GlGraphicsServer,
53    gl_attachment_kind: u32,
54    texture: &GlTexture,
55    level: i32,
56    cube_map_face: Option<CubeMapFace>,
57) {
58    match texture.kind() {
59        GpuTextureKind::Line { .. } => {
60            server.gl.framebuffer_texture(
61                glow::FRAMEBUFFER,
62                gl_attachment_kind,
63                Some(texture.id()),
64                level,
65            );
66        }
67        GpuTextureKind::Rectangle { .. } => {
68            server.gl.framebuffer_texture_2d(
69                glow::FRAMEBUFFER,
70                gl_attachment_kind,
71                glow::TEXTURE_2D,
72                Some(texture.id()),
73                level,
74            );
75        }
76        GpuTextureKind::Cube { .. } => {
77            server.gl.framebuffer_texture_2d(
78                glow::FRAMEBUFFER,
79                gl_attachment_kind,
80                cube_map_face.map_or(glow::TEXTURE_CUBE_MAP_POSITIVE_X, |face| face.into_gl()),
81                Some(texture.id()),
82                level,
83            );
84        }
85        GpuTextureKind::Volume { .. } => {
86            server.gl.framebuffer_texture_3d(
87                glow::FRAMEBUFFER,
88                gl_attachment_kind,
89                glow::TEXTURE_3D,
90                Some(texture.id()),
91                level,
92                0,
93            );
94        }
95    }
96}
97
98impl GlFrameBuffer {
99    pub fn new(
100        server: &GlGraphicsServer,
101        depth_attachment: Option<Attachment>,
102        color_attachments: Vec<Attachment>,
103    ) -> Result<Self, FrameworkError> {
104        unsafe {
105            let fbo = server.gl.create_framebuffer()?;
106
107            server.set_framebuffer(FrameBufferBindingPoint::ReadWrite, Some(fbo));
108
109            if let Some(depth_attachment) = depth_attachment.as_ref() {
110                let depth_attachment_kind = match depth_attachment.kind {
111                    AttachmentKind::Color => {
112                        panic!("Attempt to use color attachment as depth/stencil!")
113                    }
114                    AttachmentKind::DepthStencil => glow::DEPTH_STENCIL_ATTACHMENT,
115                    AttachmentKind::Depth => glow::DEPTH_ATTACHMENT,
116                };
117                let texture = depth_attachment
118                    .texture
119                    .as_any()
120                    .downcast_ref::<GlTexture>()
121                    .unwrap();
122                set_attachment(
123                    server,
124                    depth_attachment_kind,
125                    texture,
126                    depth_attachment.level() as i32,
127                    depth_attachment.cube_map_face(),
128                );
129            }
130
131            let mut color_buffers = Vec::new();
132            for (i, color_attachment) in color_attachments.iter().enumerate() {
133                assert_eq!(color_attachment.kind, AttachmentKind::Color);
134                let color_attachment_kind = glow::COLOR_ATTACHMENT0 + i as u32;
135                let texture = color_attachment
136                    .texture
137                    .as_any()
138                    .downcast_ref::<GlTexture>()
139                    .unwrap();
140                set_attachment(
141                    server,
142                    color_attachment_kind,
143                    texture,
144                    color_attachment.level() as i32,
145                    color_attachment.cube_map_face(),
146                );
147                color_buffers.push(color_attachment_kind);
148            }
149
150            if color_buffers.is_empty() {
151                server.gl.draw_buffers(&[glow::NONE])
152            } else {
153                server.gl.draw_buffers(&color_buffers);
154            }
155
156            if server.gl.check_framebuffer_status(glow::FRAMEBUFFER) != glow::FRAMEBUFFER_COMPLETE {
157                return Err(FrameworkError::FailedToConstructFBO);
158            }
159
160            server.set_framebuffer(FrameBufferBindingPoint::ReadWrite, None);
161
162            Ok(Self {
163                state: server.weak(),
164                fbo: Some(fbo),
165                depth_attachment,
166                color_attachments,
167            })
168        }
169    }
170
171    pub fn backbuffer(server: &GlGraphicsServer) -> Self {
172        Self {
173            state: server.weak(),
174            fbo: None,
175            depth_attachment: None,
176            color_attachments: Default::default(),
177        }
178    }
179
180    /// None is possible only for back buffer.
181    pub fn id(&self) -> Option<glow::Framebuffer> {
182        self.fbo
183    }
184}
185
186impl GpuFrameBufferTrait for GlFrameBuffer {
187    fn color_attachments(&self) -> &[Attachment] {
188        &self.color_attachments
189    }
190
191    fn depth_attachment(&self) -> Option<&Attachment> {
192        self.depth_attachment.as_ref()
193    }
194
195    fn set_cubemap_face(&self, attachment_index: usize, face: CubeMapFace, level: usize) {
196        let server = self.state.upgrade().unwrap();
197
198        unsafe {
199            server.set_framebuffer(FrameBufferBindingPoint::ReadWrite, self.fbo);
200
201            let attachment = &self.color_attachments[attachment_index];
202            attachment.set_cube_map_face(Some(face));
203            attachment.set_level(level);
204            let texture = attachment
205                .texture
206                .as_any()
207                .downcast_ref::<GlTexture>()
208                .unwrap();
209            server.gl.framebuffer_texture_2d(
210                glow::FRAMEBUFFER,
211                glow::COLOR_ATTACHMENT0 + attachment_index as u32,
212                face.into_gl(),
213                Some(texture.id()),
214                level as i32,
215            );
216        }
217    }
218
219    fn blit_to(
220        &self,
221        dest: &GpuFrameBuffer,
222        src_x0: i32,
223        src_y0: i32,
224        src_x1: i32,
225        src_y1: i32,
226        dst_x0: i32,
227        dst_y0: i32,
228        dst_x1: i32,
229        dst_y1: i32,
230        copy_color: bool,
231        copy_depth: bool,
232        copy_stencil: bool,
233    ) {
234        let server = self.state.upgrade().unwrap();
235
236        let source = self;
237        let dest = dest.as_any().downcast_ref::<GlFrameBuffer>().unwrap();
238
239        let mut mask = 0;
240        if copy_color {
241            mask |= glow::COLOR_BUFFER_BIT;
242        }
243        if copy_depth {
244            mask |= glow::DEPTH_BUFFER_BIT;
245        }
246        if copy_stencil {
247            mask |= glow::STENCIL_BUFFER_BIT;
248        }
249
250        unsafe {
251            server.set_framebuffer(FrameBufferBindingPoint::Read, source.id());
252            server.set_framebuffer(FrameBufferBindingPoint::Write, dest.id());
253            server.gl.blit_framebuffer(
254                src_x0,
255                src_y0,
256                src_x1,
257                src_y1,
258                dst_x0,
259                dst_y0,
260                dst_x1,
261                dst_y1,
262                mask,
263                glow::NEAREST,
264            );
265        }
266    }
267
268    fn read_pixels(&self, read_target: ReadTarget) -> Option<Vec<u8>> {
269        let server = self.state.upgrade()?;
270
271        server.set_framebuffer(FrameBufferBindingPoint::Read, self.id());
272
273        let texture = match read_target {
274            ReadTarget::Depth | ReadTarget::Stencil => &self.depth_attachment.as_ref()?.texture,
275            ReadTarget::Color(index) => {
276                unsafe {
277                    server
278                        .gl
279                        .read_buffer(glow::COLOR_ATTACHMENT0 + index as u32);
280                }
281
282                &self.color_attachments.get(index)?.texture
283            }
284        };
285
286        if let GpuTextureKind::Rectangle { width, height } = texture.kind() {
287            let pixel_kind = texture.pixel_kind();
288            let pixel_info = PixelDescriptor::from(pixel_kind);
289            let mut buffer = vec![0; image_2d_size_bytes(pixel_kind, width, height)];
290            unsafe {
291                server.gl.read_pixels(
292                    0,
293                    0,
294                    width as i32,
295                    height as i32,
296                    pixel_info.format,
297                    pixel_info.data_type,
298                    PixelPackData::Slice(Some(buffer.as_mut_slice())),
299                );
300            }
301            Some(buffer)
302        } else {
303            None
304        }
305    }
306
307    fn clear(
308        &self,
309        viewport: Rect<i32>,
310        color: Option<Color>,
311        depth: Option<f32>,
312        stencil: Option<i32>,
313    ) {
314        let server = self.state.upgrade().unwrap();
315
316        server.set_scissor_test(false);
317        server.set_viewport(viewport);
318        server.set_framebuffer(FrameBufferBindingPoint::ReadWrite, self.id());
319
320        unsafe {
321            // Special route for default buffer.
322            if self.fbo == Default::default() {
323                let mut mask = 0;
324
325                if let Some(color) = color {
326                    server.set_color_write(ColorMask::default());
327                    server.set_clear_color(color);
328                    mask |= glow::COLOR_BUFFER_BIT;
329                }
330                if let Some(depth) = depth {
331                    server.set_depth_write(true);
332                    server.set_clear_depth(depth);
333                    mask |= glow::DEPTH_BUFFER_BIT;
334                }
335                if let Some(stencil) = stencil {
336                    server.set_stencil_mask(0xFFFF_FFFF);
337                    server.set_clear_stencil(stencil);
338                    mask |= glow::STENCIL_BUFFER_BIT;
339                }
340
341                server.gl.clear(mask);
342            }
343
344            // Custom routes for specific frame buffer attachments.
345            if let Some(depth_stencil) = self.depth_attachment.as_ref() {
346                server.set_depth_write(true);
347                server.set_stencil_mask(0xFFFF_FFFF);
348
349                match depth_stencil.kind {
350                    AttachmentKind::Color => unreachable!("depth cannot be color!"),
351                    AttachmentKind::DepthStencil => match (depth, stencil) {
352                        (Some(depth), Some(stencil)) => {
353                            server.gl.clear_buffer_depth_stencil(
354                                glow::DEPTH_STENCIL,
355                                0,
356                                depth,
357                                stencil,
358                            );
359                        }
360                        (Some(depth), None) => {
361                            let values = [depth];
362                            server.gl.clear_buffer_f32_slice(glow::DEPTH, 0, &values);
363                        }
364                        (None, Some(stencil)) => {
365                            let values = [stencil];
366                            server.gl.clear_buffer_i32_slice(glow::STENCIL, 0, &values);
367                        }
368                        (None, None) => {
369                            // Nothing to do
370                        }
371                    },
372                    AttachmentKind::Depth => {
373                        if let Some(depth) = depth {
374                            let values = [depth];
375                            server.gl.clear_buffer_f32_slice(glow::DEPTH, 0, &values);
376                        }
377                    }
378                }
379            }
380
381            if let Some(color) = color {
382                server.set_color_write(ColorMask::default());
383
384                for (i, attachment) in self.color_attachments.iter().enumerate() {
385                    match attachment.texture.pixel_kind().element_kind() {
386                        PixelElementKind::Float | PixelElementKind::NormalizedUnsignedInteger => {
387                            let fvalues = color.as_frgba();
388                            server.gl.clear_buffer_f32_slice(
389                                glow::COLOR,
390                                i as u32,
391                                &fvalues.data.0[0],
392                            )
393                        }
394                        PixelElementKind::Integer => {
395                            let values = [
396                                color.r as i32,
397                                color.g as i32,
398                                color.b as i32,
399                                color.a as i32,
400                            ];
401                            server
402                                .gl
403                                .clear_buffer_i32_slice(glow::COLOR, i as u32, &values);
404                        }
405                        PixelElementKind::UnsignedInteger => {
406                            let values = [
407                                color.r as u32,
408                                color.g as u32,
409                                color.b as u32,
410                                color.a as u32,
411                            ];
412                            server
413                                .gl
414                                .clear_buffer_u32_slice(glow::COLOR, i as u32, &values);
415                        }
416                    }
417                }
418            }
419        }
420    }
421
422    fn draw(
423        &self,
424        geometry: &GpuGeometryBuffer,
425        viewport: Rect<i32>,
426        program: &GpuProgram,
427        params: &DrawParameters,
428        resources: &[ResourceBindGroup],
429        element_range: ElementRange,
430    ) -> Result<DrawCallStatistics, FrameworkError> {
431        let server = self.state.upgrade().unwrap();
432        let geometry = geometry
433            .as_any()
434            .downcast_ref::<GlGeometryBuffer>()
435            .unwrap();
436
437        pre_draw(self.id(), &server, viewport, program, params, resources);
438
439        let (offset, element_count) = match element_range {
440            ElementRange::Full => (0, geometry.element_count.get()),
441            ElementRange::Specific { offset, count } => (offset, count),
442        };
443
444        let last_element_index = offset + element_count;
445
446        if last_element_index > geometry.element_count.get() {
447            Err(FrameworkError::InvalidElementRange {
448                start: offset,
449                end: last_element_index,
450                total: geometry.element_count.get(),
451            })
452        } else {
453            let index_per_element = geometry.element_kind.index_per_element();
454            let start_index = offset * index_per_element;
455            let index_count = element_count * index_per_element;
456
457            unsafe {
458                if index_count > 0 {
459                    server.set_vertex_array_object(Some(geometry.vertex_array_object));
460
461                    let indices = (start_index * size_of::<u32>()) as i32;
462                    server.gl.draw_elements(
463                        geometry.mode(),
464                        index_count as i32,
465                        glow::UNSIGNED_INT,
466                        indices,
467                    );
468                }
469            }
470
471            Ok(DrawCallStatistics {
472                triangles: element_count,
473            })
474        }
475    }
476
477    fn draw_instances(
478        &self,
479        instance_count: usize,
480        geometry: &GpuGeometryBuffer,
481        viewport: Rect<i32>,
482        program: &GpuProgram,
483        params: &DrawParameters,
484        resources: &[ResourceBindGroup],
485        element_range: ElementRange,
486    ) -> Result<DrawCallStatistics, FrameworkError> {
487        let server = self.state.upgrade().unwrap();
488        let geometry = geometry
489            .as_any()
490            .downcast_ref::<GlGeometryBuffer>()
491            .unwrap();
492
493        pre_draw(self.id(), &server, viewport, program, params, resources);
494
495        let (offset, element_count) = match element_range {
496            ElementRange::Full => (0, geometry.element_count.get()),
497            ElementRange::Specific { offset, count } => (offset, count),
498        };
499
500        let last_element_index = offset + element_count;
501
502        if last_element_index > geometry.element_count.get() {
503            Err(FrameworkError::InvalidElementRange {
504                start: offset,
505                end: last_element_index,
506                total: geometry.element_count.get(),
507            })
508        } else {
509            let index_per_element = geometry.element_kind.index_per_element();
510            let start_index = offset * index_per_element;
511            let index_count = geometry.element_count.get() * index_per_element;
512
513            unsafe {
514                if index_count > 0 {
515                    server.set_vertex_array_object(Some(geometry.vertex_array_object));
516                    let indices = (start_index * size_of::<u32>()) as i32;
517                    server.gl.draw_elements_instanced(
518                        geometry.mode(),
519                        index_count as i32,
520                        glow::UNSIGNED_INT,
521                        indices,
522                        instance_count as i32,
523                    )
524                }
525            }
526
527            Ok(DrawCallStatistics {
528                triangles: geometry.element_count.get() * instance_count,
529            })
530        }
531    }
532}
533
534fn pre_draw(
535    fbo: Option<glow::Framebuffer>,
536    server: &GlGraphicsServer,
537    viewport: Rect<i32>,
538    program: &GpuProgram,
539    params: &DrawParameters,
540    resources: &[ResourceBindGroup],
541) {
542    server.set_framebuffer(FrameBufferBindingPoint::ReadWrite, fbo);
543    server.set_viewport(viewport);
544    server.apply_draw_parameters(params);
545    let program = program.as_any().downcast_ref::<GlProgram>().unwrap();
546    server.set_program(Some(program.id));
547
548    for bind_group in resources {
549        for binding in bind_group.bindings {
550            match binding {
551                ResourceBinding::Texture {
552                    texture,
553                    sampler,
554                    binding: shader_location,
555                } => {
556                    let texture = texture.as_any().downcast_ref::<GlTexture>().unwrap();
557                    texture.bind(server, *shader_location as u32);
558                    let sampler = sampler.as_any().downcast_ref::<GlSampler>().unwrap();
559                    unsafe {
560                        server
561                            .gl
562                            .bind_sampler(*shader_location as u32, Some(sampler.id))
563                    };
564                }
565                ResourceBinding::Buffer {
566                    buffer,
567                    binding,
568                    data_usage: data_location,
569                } => {
570                    let gl_buffer = buffer
571                        .as_any()
572                        .downcast_ref::<GlBuffer>()
573                        .expect("Must be OpenGL buffer");
574
575                    unsafe {
576                        let actual_binding = *binding as u32;
577
578                        match data_location {
579                            BufferDataUsage::UseSegment { offset, size } => {
580                                assert_ne!(*size, 0);
581                                server.gl.bind_buffer_range(
582                                    gl_buffer.kind().into_gl(),
583                                    actual_binding,
584                                    Some(gl_buffer.id),
585                                    *offset as i32,
586                                    *size as i32,
587                                );
588                            }
589                            BufferDataUsage::UseEverything => {
590                                server.gl.bind_buffer_base(
591                                    gl_buffer.kind().into_gl(),
592                                    actual_binding,
593                                    Some(gl_buffer.id),
594                                );
595                            }
596                        }
597                    }
598                }
599            }
600        }
601    }
602}
603
604impl Drop for GlFrameBuffer {
605    fn drop(&mut self) {
606        if let Some(state) = self.state.upgrade() {
607            state.delete_framebuffer(self.fbo);
608        }
609    }
610}