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