diff --git a/src/framebuffer/default_fb.rs b/src/framebuffer/default_fb.rs index 8995c412bf5..c63adc1aa77 100644 --- a/src/framebuffer/default_fb.rs +++ b/src/framebuffer/default_fb.rs @@ -5,7 +5,7 @@ use std::rc::Rc; use crate::backend::Facade; use crate::context::Context; -use crate::DrawParameters; +use crate::{DrawParameters, BlitMask}; use crate::FboAttachments; use crate::Rect; use crate::BlitTarget; @@ -17,10 +17,11 @@ use crate::uniforms; use crate::{Program, Surface}; use crate::DrawError; -use crate::{fbo, gl}; -use crate::framebuffer; +use crate::fbo; use crate::index; use crate::vertex; +use crate::framebuffer::{SimpleFrameBuffer, MultiOutputFrameBuffer}; +use crate::uniforms::MagnifySamplerFilter; /// One of the color attachments on the default framebuffer. #[derive(Copy, Clone, Debug)] @@ -112,29 +113,21 @@ impl Surface for DefaultFramebuffer { } #[inline] - fn blit_from_frame(&self, source_rect: &Rect, target_rect: &BlitTarget, - filter: uniforms::MagnifySamplerFilter) - { + fn blit_buffers_from_frame(&self, source_rect: &Rect, target_rect: &BlitTarget, filter: MagnifySamplerFilter, mask: BlitMask) { ops::blit(&self.context, None, self.get_attachments(), - gl::COLOR_BUFFER_BIT, source_rect, target_rect, filter.to_glenum()) + mask.to_glenum(), source_rect, target_rect, filter.to_glenum()) } #[inline] - fn blit_from_simple_framebuffer(&self, source: &framebuffer::SimpleFrameBuffer<'_>, - source_rect: &Rect, target_rect: &BlitTarget, - filter: uniforms::MagnifySamplerFilter) - { + fn blit_buffers_from_simple_framebuffer(&self, source: &SimpleFrameBuffer<'_>, source_rect: &Rect, target_rect: &BlitTarget, filter: MagnifySamplerFilter, mask: BlitMask) { ops::blit(&self.context, source.get_attachments(), self.get_attachments(), - gl::COLOR_BUFFER_BIT, source_rect, target_rect, filter.to_glenum()) + mask.to_glenum(), source_rect, target_rect, filter.to_glenum()) } #[inline] - fn blit_from_multioutput_framebuffer(&self, source: &framebuffer::MultiOutputFrameBuffer<'_>, - source_rect: &Rect, target_rect: &BlitTarget, - filter: uniforms::MagnifySamplerFilter) - { + fn blit_buffers_from_multioutput_framebuffer(&self, source: &MultiOutputFrameBuffer<'_>, source_rect: &Rect, target_rect: &BlitTarget, filter: MagnifySamplerFilter, mask: BlitMask) { ops::blit(&self.context, source.get_attachments(), self.get_attachments(), - gl::COLOR_BUFFER_BIT, source_rect, target_rect, filter.to_glenum()) + mask.to_glenum(), source_rect, target_rect, filter.to_glenum()) } } diff --git a/src/framebuffer/mod.rs b/src/framebuffer/mod.rs index d37adb14daf..e5031eec3b9 100644 --- a/src/framebuffer/mod.rs +++ b/src/framebuffer/mod.rs @@ -73,7 +73,7 @@ use crate::texture::TextureAnyImage; use crate::backend::Facade; use crate::context::Context; -use crate::CapabilitiesSource; +use crate::{CapabilitiesSource, BlitMask}; use crate::version::Version; use crate::version::Api; @@ -88,7 +88,7 @@ use crate::uniforms; use crate::{Program, Surface}; use crate::DrawError; -use crate::{fbo, gl}; +use crate::fbo; pub use self::default_fb::{DefaultFramebufferAttachment, DefaultFramebuffer}; pub use self::render_buffer::{RenderBuffer, RenderBufferAny, DepthRenderBuffer}; @@ -96,6 +96,7 @@ pub use self::render_buffer::{StencilRenderBuffer, DepthStencilRenderBuffer}; pub use self::render_buffer::CreationError as RenderBufferCreationError; pub use crate::fbo::is_dimensions_mismatch_supported; pub use crate::fbo::ValidationError; +use crate::uniforms::MagnifySamplerFilter; mod default_fb; mod render_buffer; @@ -323,29 +324,21 @@ impl<'a> Surface for SimpleFrameBuffer<'a> { } #[inline] - fn blit_from_frame(&self, source_rect: &Rect, target_rect: &BlitTarget, - filter: uniforms::MagnifySamplerFilter) - { + fn blit_buffers_from_frame(&self, source_rect: &Rect, target_rect: &BlitTarget, filter: MagnifySamplerFilter, mask: BlitMask) { ops::blit(&self.context, None, self.get_attachments(), - gl::COLOR_BUFFER_BIT, source_rect, target_rect, filter.to_glenum()) + mask.to_glenum(), source_rect, target_rect, filter.to_glenum()) } #[inline] - fn blit_from_simple_framebuffer(&self, source: &SimpleFrameBuffer<'_>, - source_rect: &Rect, target_rect: &BlitTarget, - filter: uniforms::MagnifySamplerFilter) - { + fn blit_buffers_from_simple_framebuffer(&self, source: &SimpleFrameBuffer<'_>, source_rect: &Rect, target_rect: &BlitTarget, filter: MagnifySamplerFilter, mask: BlitMask) { ops::blit(&self.context, source.get_attachments(), self.get_attachments(), - gl::COLOR_BUFFER_BIT, source_rect, target_rect, filter.to_glenum()) + mask.to_glenum(), source_rect, target_rect, filter.to_glenum()) } #[inline] - fn blit_from_multioutput_framebuffer(&self, source: &MultiOutputFrameBuffer<'_>, - source_rect: &Rect, target_rect: &BlitTarget, - filter: uniforms::MagnifySamplerFilter) - { + fn blit_buffers_from_multioutput_framebuffer(&self, source: &MultiOutputFrameBuffer<'_>, source_rect: &Rect, target_rect: &BlitTarget, filter: MagnifySamplerFilter, mask: BlitMask) { ops::blit(&self.context, source.get_attachments(), self.get_attachments(), - gl::COLOR_BUFFER_BIT, source_rect, target_rect, filter.to_glenum()) + mask.to_glenum(), source_rect, target_rect, filter.to_glenum()) } } @@ -591,29 +584,21 @@ impl<'a> Surface for MultiOutputFrameBuffer<'a> { } #[inline] - fn blit_from_frame(&self, source_rect: &Rect, target_rect: &BlitTarget, - filter: uniforms::MagnifySamplerFilter) - { + fn blit_buffers_from_frame(&self, source_rect: &Rect, target_rect: &BlitTarget, filter: MagnifySamplerFilter, mask: BlitMask) { ops::blit(&self.context, None, self.get_attachments(), - gl::COLOR_BUFFER_BIT, source_rect, target_rect, filter.to_glenum()) + mask.to_glenum(), source_rect, target_rect, filter.to_glenum()) } #[inline] - fn blit_from_simple_framebuffer(&self, source: &SimpleFrameBuffer<'_>, - source_rect: &Rect, target_rect: &BlitTarget, - filter: uniforms::MagnifySamplerFilter) - { + fn blit_buffers_from_simple_framebuffer(&self, source: &SimpleFrameBuffer<'_>, source_rect: &Rect, target_rect: &BlitTarget, filter: MagnifySamplerFilter, mask: BlitMask) { ops::blit(&self.context, source.get_attachments(), self.get_attachments(), - gl::COLOR_BUFFER_BIT, source_rect, target_rect, filter.to_glenum()) + mask.to_glenum(), source_rect, target_rect, filter.to_glenum()) } #[inline] - fn blit_from_multioutput_framebuffer(&self, source: &MultiOutputFrameBuffer<'_>, - source_rect: &Rect, target_rect: &BlitTarget, - filter: uniforms::MagnifySamplerFilter) - { + fn blit_buffers_from_multioutput_framebuffer(&self, source: &MultiOutputFrameBuffer<'_>, source_rect: &Rect, target_rect: &BlitTarget, filter: MagnifySamplerFilter, mask: BlitMask) { ops::blit(&self.context, source.get_attachments(), self.get_attachments(), - gl::COLOR_BUFFER_BIT, source_rect, target_rect, filter.to_glenum()) + mask.to_glenum(), source_rect, target_rect, filter.to_glenum()) } } @@ -760,29 +745,30 @@ impl Surface for EmptyFrameBuffer { } #[inline] - fn blit_from_frame(&self, source_rect: &Rect, target_rect: &BlitTarget, - filter: uniforms::MagnifySamplerFilter) + fn blit_buffers_from_frame(&self, source_rect: &Rect, target_rect: &BlitTarget, + filter: uniforms::MagnifySamplerFilter, mask: BlitMask) { ops::blit(&self.context, None, self.get_attachments(), - gl::COLOR_BUFFER_BIT, source_rect, target_rect, filter.to_glenum()) + mask.to_glenum(), source_rect, target_rect, filter.to_glenum()) } #[inline] - fn blit_from_simple_framebuffer(&self, source: &SimpleFrameBuffer<'_>, + fn blit_buffers_from_simple_framebuffer(&self, source: &SimpleFrameBuffer<'_>, source_rect: &Rect, target_rect: &BlitTarget, - filter: uniforms::MagnifySamplerFilter) + filter: uniforms::MagnifySamplerFilter, mask: BlitMask) { ops::blit(&self.context, source.get_attachments(), self.get_attachments(), - gl::COLOR_BUFFER_BIT, source_rect, target_rect, filter.to_glenum()) + mask.to_glenum(), source_rect, target_rect, filter.to_glenum()) } #[inline] - fn blit_from_multioutput_framebuffer(&self, source: &MultiOutputFrameBuffer<'_>, + fn blit_buffers_from_multioutput_framebuffer(&self, source: &MultiOutputFrameBuffer<'_>, source_rect: &Rect, target_rect: &BlitTarget, - filter: uniforms::MagnifySamplerFilter) + filter: uniforms::MagnifySamplerFilter, + mask: BlitMask) { ops::blit(&self.context, source.get_attachments(), self.get_attachments(), - gl::COLOR_BUFFER_BIT, source_rect, target_rect, filter.to_glenum()) + mask.to_glenum(), source_rect, target_rect, filter.to_glenum()) } } diff --git a/src/lib.rs b/src/lib.rs index 84fdf04e65a..b03fb26677c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -186,6 +186,7 @@ pub use memoffset::offset_of as __glium_offset_of; pub use crate::backend::glutin::Display; #[cfg(feature = "glutin")] pub use crate::backend::glutin::headless::Headless as HeadlessRenderer; +use crate::uniforms::MagnifySamplerFilter; /// Trait for objects that describe the capabilities of an OpenGL backend. pub trait CapabilitiesSource { @@ -492,6 +493,75 @@ pub struct BlitTarget { pub height: i32, } +/// Mask specifying, which kinds of buffers to copy when blitting between two frame buffers. +#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)] +pub struct BlitMask { + + /// If the color buffer should be copied. + pub color: bool, + + /// If the depth buffer should be copied. + pub depth: bool, + + /// If the stencil buffer should be copied. + pub stencil: bool, +} + +impl BlitMask { + + /// Constructs a bit mask, that will only copy the color buffer + pub fn color() -> Self { + BlitMask { color: true, depth: false, stencil: false } + } + + /// Constructs a bit mask, that will only copy the depth buffer + pub fn depth() -> Self { + BlitMask { color: false, depth: true, stencil: false } + } + + /// Constructs a bit mask, that will only copy the stencil buffer + pub fn stencil() -> Self { + BlitMask { color: false, depth: false, stencil: true } + } + + /// Constructs a bit mask, that will copy the color and the depth buffer. + pub fn color_and_depth() -> Self { + BlitMask { color: true, depth: true, stencil: false } + } + + /// Constructs a bit mask, that will copy the color and the stencil buffer. + pub fn color_and_stencil() -> Self { + BlitMask { color: true, depth: false, stencil: true } + } + + /// Constructs a bit mask, that will copy the depth and the stencil buffer. + pub fn depth_and_stencil() -> Self { + BlitMask { color: false, depth: true, stencil: true } + } + + /// Constructs a bit mask, that will copy the color, depth and stencil buffer. + pub fn color_and_depth_and_stencil() -> Self { + BlitMask { color: true, depth: true, stencil: true } + } +} + +impl ToGlEnum for BlitMask { + #[inline] + fn to_glenum(&self) -> gl::types::GLenum { + let mut mask = 0; + if self.color { + mask = mask | gl::COLOR_BUFFER_BIT; + } + if self.depth { + mask = mask | gl::DEPTH_BUFFER_BIT; + } + if self.stencil { + mask = mask | gl::STENCIL_BUFFER_BIT; + } + mask + } +} + /// Object that can be drawn upon. /// /// # What does the GPU do when you draw? @@ -796,18 +866,49 @@ pub trait Surface { U: uniforms::Uniforms; /// Blits from the default framebuffer. + #[inline] fn blit_from_frame(&self, source_rect: &Rect, target_rect: &BlitTarget, - filter: uniforms::MagnifySamplerFilter); + filter: uniforms::MagnifySamplerFilter) + { + self.blit_buffers_from_frame(source_rect, target_rect, filter, BlitMask::color()) + } /// Blits from a simple framebuffer. + #[inline] fn blit_from_simple_framebuffer(&self, source: &framebuffer::SimpleFrameBuffer<'_>, source_rect: &Rect, target_rect: &BlitTarget, - filter: uniforms::MagnifySamplerFilter); + filter: uniforms::MagnifySamplerFilter) + { + self.blit_buffers_from_simple_framebuffer(source, source_rect, target_rect, filter, + BlitMask::color()) + } /// Blits from a multi-output framebuffer. + #[inline] fn blit_from_multioutput_framebuffer(&self, source: &framebuffer::MultiOutputFrameBuffer<'_>, source_rect: &Rect, target_rect: &BlitTarget, - filter: uniforms::MagnifySamplerFilter); + filter: uniforms::MagnifySamplerFilter) + { + self.blit_buffers_from_multioutput_framebuffer(source, source_rect, target_rect, filter, + BlitMask::color()) + } + + /// Blits from the default framebuffer. + fn blit_buffers_from_frame(&self, source_rect: &Rect, target_rect: &BlitTarget, + filter: uniforms::MagnifySamplerFilter, mask: BlitMask); + + /// Blits from a simple framebuffer. + fn blit_buffers_from_simple_framebuffer(&self, source: &framebuffer::SimpleFrameBuffer<'_>, + source_rect: &Rect, target_rect: &BlitTarget, + filter: uniforms::MagnifySamplerFilter, + mask: BlitMask); + + /// Blits from a multi-output framebuffer. + fn blit_buffers_from_multioutput_framebuffer(&self, source: &framebuffer::MultiOutputFrameBuffer<'_>, + source_rect: &Rect, target_rect: &BlitTarget, + filter: uniforms::MagnifySamplerFilter, + mask: BlitMask); + /// Copies a rectangle of pixels from this surface to another surface. /// @@ -824,6 +925,7 @@ pub trait Surface { fn blit_color(&self, source_rect: &Rect, target: &S, target_rect: &BlitTarget, filter: uniforms::MagnifySamplerFilter) where S: Surface; + /// Copies the entire surface to a target surface. See `blit_color`. #[inline] fn blit_whole_color_to(&self, target: &S, target_rect: &BlitTarget, @@ -1216,30 +1318,26 @@ impl Surface for Frame { target.blit_from_frame(source_rect, target_rect, filter) } - #[inline] - fn blit_from_frame(&self, source_rect: &Rect, target_rect: &BlitTarget, - filter: uniforms::MagnifySamplerFilter) - { + fn blit_buffers_from_frame(&self, source_rect: &Rect, target_rect: &BlitTarget, filter: MagnifySamplerFilter, mask: BlitMask) { ops::blit(&self.context, None, self.get_attachments(), - gl::COLOR_BUFFER_BIT, source_rect, target_rect, filter.to_glenum()) + mask.to_glenum(), source_rect, target_rect, filter.to_glenum()) } - #[inline] - fn blit_from_simple_framebuffer(&self, source: &framebuffer::SimpleFrameBuffer<'_>, - source_rect: &Rect, target_rect: &BlitTarget, - filter: uniforms::MagnifySamplerFilter) - { + fn blit_buffers_from_simple_framebuffer(&self, source: &framebuffer::SimpleFrameBuffer<'_>, + source_rect: &Rect, target_rect: &BlitTarget, + filter: uniforms::MagnifySamplerFilter, + mask: BlitMask) { ops::blit(&self.context, source.get_attachments(), self.get_attachments(), - gl::COLOR_BUFFER_BIT, source_rect, target_rect, filter.to_glenum()) + mask.to_glenum(), source_rect, target_rect, filter.to_glenum()) } - #[inline] - fn blit_from_multioutput_framebuffer(&self, source: &framebuffer::MultiOutputFrameBuffer<'_>, - source_rect: &Rect, target_rect: &BlitTarget, - filter: uniforms::MagnifySamplerFilter) - { + fn blit_buffers_from_multioutput_framebuffer(&self, + source: &framebuffer::MultiOutputFrameBuffer<'_>, + source_rect: &Rect, target_rect: &BlitTarget, + filter: uniforms::MagnifySamplerFilter, + mask: BlitMask) { ops::blit(&self.context, source.get_attachments(), self.get_attachments(), - gl::COLOR_BUFFER_BIT, source_rect, target_rect, filter.to_glenum()) + mask.to_glenum(), source_rect, target_rect, filter.to_glenum()) } } diff --git a/src/ops/blit.rs b/src/ops/blit.rs index 21d59d94033..153442b43f4 100644 --- a/src/ops/blit.rs +++ b/src/ops/blit.rs @@ -15,6 +15,10 @@ pub fn blit(context: &Context, source: Option<&ValidatedAttachments<'_>>, target: Option<&ValidatedAttachments<'_>>, mask: gl::types::GLbitfield, src_rect: &Rect, target_rect: &BlitTarget, filter: gl::types::GLenum) { + assert!( + (mask & gl::DEPTH_BUFFER_BIT == 0 && mask & gl::STENCIL_BUFFER_BIT == 0) || filter == gl::NEAREST, + "Blitting the depth and/or stencil buffer with filter being anything other than GL_NEAREST is an invalid operation." + ); unsafe { let mut ctxt = context.make_current(); diff --git a/tests/blit.rs b/tests/blit.rs index 45cc8faaf6f..2a4ca19f075 100644 --- a/tests/blit.rs +++ b/tests/blit.rs @@ -1,7 +1,9 @@ #[macro_use] extern crate glium; -use glium::{Surface, BlitTarget, Rect}; +use glium::{Surface, BlitTarget, Rect, BlitMask}; +use glium::framebuffer::SimpleFrameBuffer; +use glium::uniforms::MagnifySamplerFilter; mod support; @@ -56,3 +58,38 @@ fn blit_texture_to_window() { display.assert_no_error(None); } + +#[test] +fn blit_color_and_depth_buffer() { + let display = support::build_display(); + + // source frame buffer + let src_tex_color = support::build_unicolor_texture2d(&display, 0.0, 0.5, 1.0); + let src_tex_depth = support::build_constant_depth_texture(&display, 0.5); + let src_frame_buffer = SimpleFrameBuffer::with_depth_buffer(&display, &src_tex_color, &src_tex_depth).unwrap(); + + // destination frame buffer + let dst_tex_color = support::build_unicolor_texture2d(&display, 0.0, 0.0, 0.0); + let dst_tex_depth = support::build_constant_depth_texture(&display, 0.0); + let dst_frame_buffer = SimpleFrameBuffer::with_depth_buffer(&display, &dst_tex_color, &dst_tex_depth).unwrap(); + + // blit + let src_rect = Rect {left: 0, bottom: 0, width: 2, height: 2, }; + let dst_rect = BlitTarget { left: 0, bottom: 0, width: 2, height: 2, }; + dst_frame_buffer.blit_buffers_from_simple_framebuffer( + &src_frame_buffer, + &src_rect, + &dst_rect, + MagnifySamplerFilter::Nearest, + BlitMask::color_and_depth() + ); + + // check result + let color_data: Vec> = dst_tex_color.read(); + assert_eq!(color_data, vec![ + vec![(0, 127, 255, 255), (0, 127, 255, 255),], + vec![(0, 127, 255, 255), (0, 127, 255, 255),], + ]); + // todo: how to check dst_tex_depth? There is no .read() on a DepthTexture2d... + display.assert_no_error(None); +} diff --git a/tests/support/mod.rs b/tests/support/mod.rs index 4f16b694a3e..cbf1f9a24d4 100644 --- a/tests/support/mod.rs +++ b/tests/support/mod.rs @@ -91,6 +91,16 @@ pub fn build_unicolor_texture2d(facade: &F, red: f32, green: f32, blu ]).unwrap() } +/// Builds a 2x2 depth texture. +pub fn build_constant_depth_texture(facade: &F, depth: f32) -> glium::texture::DepthTexture2d +where F: Facade + ?Sized +{ + glium::texture::DepthTexture2d::new(facade, vec![ + vec![depth, depth], + vec![depth, depth], + ]).unwrap() +} + /// Builds a vertex buffer, index buffer, and program, to draw red `(1.0, 0.0, 0.0, 1.0)` to the whole screen. pub fn build_fullscreen_red_pipeline(facade: &F) -> (glium::vertex::VertexBufferAny, glium::index::IndexBufferAny, glium::Program) where F: Facade