use gl::{self, types::*};
use texture::Texture;
use cubemap::CubeMap;
use utils::Rect;
use state::{StateRef, State};
use buffer::BufferRange;
use std::mem;
use std::borrow::{Borrow, BorrowMut};
use std::hash::{Hash, Hasher};
use std::collections::hash_map::DefaultHasher;
use std::cell::Cell;
use std::rc::Rc;
use std::fmt;
#[cfg(feature = "gles")]
use std::ptr;
pub use self::attachments::*;
pub use self::renderbuffer::*;
use self::framebuffer::FrameBuffer;
mod attachments;
mod framebuffer;
mod renderbuffer;
#[derive(Debug)]
struct FboAttachments<C, D>{
color: Vec<C>,
depth: Option<D>,
hash: u64,
}
impl<C: BorrowColorAttach, D: BorrowDepthAttach> FboAttachments<C, D> {
fn new(color: Vec<C>, depth: Option<D>) -> FboAttachments<C,D>{
let mut hasher = DefaultHasher::new();
for color in color.iter(){
color.borrow_color_attachment().attachment_id().hash(&mut hasher);
}
if let Some(depth) = depth.as_ref(){
depth.borrow_depth_attachment().attachment_id().hash(&mut hasher);
}
let hash = hasher.finish();
FboAttachments{
color,
depth,
hash,
}
}
}
impl<C, D> FboAttachments<C, D> {
fn empty() -> FboAttachments<C,D>{
let hasher = DefaultHasher::new();
let hash = hasher.finish();
FboAttachments{
color: vec![],
depth: None,
hash,
}
}
}
pub struct Fbo<C = ColorAttachment, D = DepthAttachment>{
framebuffer: Rc<FrameBuffer>,
attachments: FboAttachments<C, D>,
fbo_state: Rc<Cell<u64>>,
}
impl<C: fmt::Debug, D: fmt::Debug> fmt::Debug for Fbo<C,D>{
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_struct("Fbo")
.field("id", &self.framebuffer.id())
.field("attachments", &self.attachments)
.finish()
}
}
pub struct Builder<'a>(pub(crate) &'a StateRef);
impl<'a> Builder<'a>{
#[cfg(not(any(feature = "gles", feature="webgl")))]
pub fn empty(&self) -> ::Result<Fbo<(),()>> {
let gl = self.0;
let mut id: GLuint = 0;
unsafe {
if gl.capabilities().supports_dsa(){
gl.CreateFramebuffers(1, &mut id);
}else{
gl.GenFramebuffers(1, &mut id);
}
}
let attachments = FboAttachments::empty();
let fbo_state = Rc::new(Cell::new(attachments.hash));
Ok(Fbo{
framebuffer: Rc::new(FrameBuffer::new(gl.clone())),
attachments,
fbo_state,
})
}
#[cfg(any(feature = "gles", feature="webgl"))]
pub fn empty(&self) -> ::Result<Fbo<(),()>> {
let gl = self.0;
let mut id: GLuint = 0;
unsafe {
gl.GenFramebuffers(1, &mut id);
}
let attachments = FboAttachments::empty();
let fbo_state = Rc::new(Cell::new(attachments.hash));
Ok(Fbo{
framebuffer: Rc::new(FrameBuffer::new(gl.clone())),
attachments,
fbo_state,
})
}
pub fn create(&self, w: u32, h: u32, color_format: ColorFormat) -> ::Result<Fbo>{
let color = ColorAttachmentBuilder(self.0).texture(w, h, color_format)?;
let depth = DepthAttachmentBuilder(self.0).render_buffer(w, h, DepthFormat::default())?;
self.from_color_depth(color, depth)
}
pub fn from_color<C: BorrowColorAttach>(&self, color: C) -> ::Result<Fbo<C>>{
let depth = if color.borrow_color_attachment().samples() > 0{
DepthAttachmentBuilder(self.0)
.render_buffer_multisampled(
color.borrow_color_attachment().width(),
color.borrow_color_attachment().height(),
color.borrow_color_attachment().samples(),
DepthFormat::default()
)?
}else{
DepthAttachmentBuilder(self.0)
.render_buffer(
color.borrow_color_attachment().width(),
color.borrow_color_attachment().height(),
DepthFormat::default()
)?
};
self.from_color_depth(color, depth)
}
pub fn from_color_no_depth<C: BorrowColorAttach>(&self, color: C) -> ::Result<Fbo<C>>{
Builder::new_(self.0, vec![color], None)
}
pub fn from_colors<C: BorrowColorAttach>(&self, color: Vec<C>) -> ::Result<Fbo<C>>{
if color.is_empty(){
return Err(::Error::new(::ErrorKind::NoColorAttachments, None));
}
let depth_w = color.iter().map(|color| color.borrow_color_attachment().width()).max().unwrap();
let depth_h = color.iter().map(|color| color.borrow_color_attachment().height()).max().unwrap();
let depth = DepthAttachmentBuilder(self.0)
.render_buffer(depth_w, depth_h, DepthFormat::default())?;
self.from_colors_depth(color, depth)
}
pub fn from_colors_no_depth<C: BorrowColorAttach>(&self, color: Vec<C>) -> ::Result<Fbo<C>>{
if color.is_empty(){
return Err(::Error::new(::ErrorKind::NoColorAttachments, None));
}
Builder::new_(self.0, color, None)
}
pub fn from_depth<D: BorrowDepthAttach>(&self, depth: D) -> ::Result<Fbo<ColorAttachment,D>>{
Builder::new_(self.0, vec![], Some(depth))
}
pub fn from_color_depth<C,D,OC,OD>(&self, color: OC, depth: OD) -> ::Result<Fbo<C,D>>
where
C: BorrowColorAttach,
D: BorrowDepthAttach,
OC: Into<Option<C>>,
OD: Into<Option<D>>,
{
let depth = depth.into();
let color = color.into();
if let (Some(depth), Some(color)) = (depth.as_ref(), color.as_ref()) {
if color.borrow_color_attachment().width() != depth.borrow_depth_attachment().width()
|| color.borrow_color_attachment().height() != depth.borrow_depth_attachment().height()
|| color.borrow_color_attachment().samples() != depth.borrow_depth_attachment().samples()
{
return Err(::Error::new(::ErrorKind::FormatNotSupported,
"color and depth attachment dimensions and number of samples have to be the same"));
}
}
Builder::new_(self.0, color.into_iter().collect(), depth)
}
pub fn from_colors_depth<C: BorrowColorAttach, D: BorrowDepthAttach>(&self, color: Vec<C>, depth: D) -> ::Result<Fbo<C,D>>{
if !color.is_empty(){
let w = color[0].borrow_color_attachment().width();
let h = color[0].borrow_color_attachment().height();
let s = color[0].borrow_color_attachment().samples();
let diff = color.iter().find(|c|
c.borrow_color_attachment().width() != w
|| c.borrow_color_attachment().height() != h
|| c.borrow_color_attachment().samples() != s
);
if diff.is_some(){
return Err(::Error::new(::ErrorKind::FormatNotSupported,
"color attachments dimensions and number of samples have to be the same"));
}
if w != depth.borrow_depth_attachment().width()
|| h != depth.borrow_depth_attachment().height()
|| s != depth.borrow_depth_attachment().samples()
{
return Err(::Error::new(::ErrorKind::FormatNotSupported,
"color and depth attachment dimensions and number of samples have to be the same"));
}
}
Builder::new_(self.0, color, Some(depth))
}
pub fn from_cubemap_face<C: Borrow<CubeMap>>(&self, cubemap: C, face: GLenum) -> ::Result<Fbo<CubemapFaceLevelRef<C>>>{
self.from_cubemap_face_level(cubemap, face, 0)
}
pub fn from_cubemap_face_level<C: Borrow<CubeMap>>(&self, cubemap: C, face: GLenum, level: u32) -> ::Result<Fbo<CubemapFaceLevelRef<C>>>{
let w = cubemap.borrow().width() >> level;
let h = cubemap.borrow().height() >> level;
let depth = DepthAttachmentBuilder(self.0)
.render_buffer(w, h, DepthFormat::default())?;
Builder::new_(self.0, vec![CubemapFaceLevelRef::new(cubemap, face, level as i32)], Some(depth))
}
pub fn from_cubemap_face_no_depth<C: Borrow<CubeMap>>(&self, cubemap: C, face: GLenum) -> ::Result<Fbo<CubemapFaceLevelRef<C>>>{
self.from_cubemap_face_level_no_depth(cubemap, face, 0)
}
pub fn from_cubemap_face_level_no_depth<C: Borrow<CubeMap>>(&self, cubemap: C, face: GLenum, level: u32) -> ::Result<Fbo<CubemapFaceLevelRef<C>>>{
Builder::new_(self.0, vec![CubemapFaceLevelRef::new(cubemap, face, level as i32)], None)
}
#[cfg(all(not(feature = "gles"), not(feature="webgl")))]
fn new_dsa<C: BorrowColorAttach, D: BorrowDepthAttach>(gl: &StateRef, color: Vec<C>, depth: Option<D>) -> ::Result<Fbo<C,D>>{
let max_attach = gl.capabilities().max_color_attachments;
unsafe{
if color.len() > max_attach as usize{
return Err(::Error::new(::ErrorKind::MaxColorAttachments, None));
}
let framebuffer = FrameBuffer::new_dsa(gl.clone());
if !color.is_empty(){
let attachments: Vec<u32> = (0..color.len()).map(|i| gl::COLOR_ATTACHMENT0 + i as u32).collect();
for (i, color) in color.iter().enumerate(){
color.borrow_color_attachment().color_attach(&framebuffer, gl::FRAMEBUFFER, gl::COLOR_ATTACHMENT0 + i as u32);
}
framebuffer.set_drawbuffers(&attachments);
}else{
framebuffer.set_no_drawbuffers();
}
if let Some(depth) = depth.as_ref(){
depth.borrow_depth_attachment().depth_attach(&framebuffer, gl::FRAMEBUFFER, gl::DEPTH_ATTACHMENT);
}
match framebuffer.check_status() {
gl::FRAMEBUFFER_COMPLETE => {
let attachments = FboAttachments::new(color, depth);
let fbo_state = Rc::new(Cell::new(attachments.hash));
Ok( Fbo{
framebuffer: Rc::new(framebuffer),
attachments,
fbo_state
})
},
status => Err(::Error::with_gl_code(::ErrorKind::FramebufferCreationError,None,status))
}
}
}
fn new_bind<C: BorrowColorAttach, D: BorrowDepthAttach>(gl: &StateRef, color: Vec<C>, depth: Option<D>) -> ::Result<Fbo<C,D>> {
let max_attach = gl.capabilities().max_color_attachments;
unsafe{
if color.len() > max_attach as usize{
return Err(::Error::new(::ErrorKind::MaxColorAttachments, None));
}
let framebuffer = FrameBuffer::new_bind(gl.clone());
framebuffer.bind();
if !color.is_empty(){
let attachments: Vec<u32> = (0..color.len()).map(|i| gl::COLOR_ATTACHMENT0 + i as u32).collect();
for (i, color) in color.iter().enumerate(){
color.borrow_color_attachment().color_attach(&framebuffer, gl::FRAMEBUFFER, gl::COLOR_ATTACHMENT0 + i as u32);
}
framebuffer.set_drawbuffers(&attachments);
}else{
framebuffer.set_no_drawbuffers();
}
if let Some(depth) = depth.as_ref(){
depth.borrow_depth_attachment().depth_attach(&framebuffer, gl::FRAMEBUFFER, gl::DEPTH_ATTACHMENT);
}
match framebuffer.check_status() {
gl::FRAMEBUFFER_COMPLETE => {
framebuffer.unbind();
let attachments = FboAttachments::new(color, depth);
let fbo_state = Rc::new(Cell::new(attachments.hash));
Ok( Fbo{
framebuffer: Rc::new(framebuffer),
attachments,
fbo_state,
})
},
status => {
framebuffer.unbind();
Err(::Error::with_gl_code(::ErrorKind::FramebufferCreationError,None,status))
}
}
}
}
#[cfg(all(not(feature = "gles"), not(feature="webgl")))]
fn new_<C: BorrowColorAttach, D: BorrowDepthAttach>(gl: &StateRef, color: Vec<C>, depth: Option<D>) -> ::Result<Fbo<C, D>>{
if gl.capabilities().supports_dsa(){
Builder::new_dsa(gl, color, depth)
}else{
Builder::new_bind(gl, color, depth)
}
}
#[cfg(any(feature = "gles", feature="webgl"))]
fn new_<C: BorrowColorAttach, D: BorrowDepthAttach>(gl: &StateRef, color: Vec<C>, depth: Option<D>) -> ::Result<Fbo<C, D>>{
Builder::new_bind(gl, color, depth)
}
}
impl<C, D> Fbo<C,D> {
pub fn with_depth<'a, D2: BorrowDepthAttach>(&'a self, depth: D2) -> ::Result<Fbo<&'a C, D2>>
where &'a C: BorrowColorAttach
{
let color = self.attachments.color.iter().collect();
self.with(color, depth)
}
pub fn with_colors<'a, C2: BorrowColorAttach>(&'a self, colors: Vec<C2>) -> ::Result<Fbo<C2, &'a D>>
where &'a D: BorrowDepthAttach
{
let depth = self.attachments.depth.as_ref();
self.with(colors, depth)
}
pub fn with_colors_no_depth<C2>(&self, colors: Vec<C2>) -> ::Result<Fbo<C2>>
where C2: BorrowColorAttach,
{
self.with(colors, None)
}
pub fn with<C2, D2, DD>(&self, colors: Vec<C2>, depth: DD) -> ::Result<Fbo<C2, D2>>
where
C2: BorrowColorAttach,
D2: BorrowDepthAttach,
DD: Into<Option<D2>>
{
let depth = depth.into();
if let Some(color) = colors.get(0){
let w = color.borrow_color_attachment().width();
let h = color.borrow_color_attachment().height();
let s = color.borrow_color_attachment().samples();
let diff = colors
.iter()
.skip(1)
.find(|c|
c.borrow_color_attachment().width() != w
|| c.borrow_color_attachment().height() != h
|| c.borrow_color_attachment().samples() != s
);
if diff.is_some(){
return Err(::Error::new(::ErrorKind::FormatNotSupported,
"color attachments dimensions and number of samples have to be the same"));
}
if let Some(depth) = depth.as_ref() {
if w != depth.borrow_depth_attachment().width()
|| h != depth.borrow_depth_attachment().height()
|| s != depth.borrow_depth_attachment().samples()
{
return Err(::Error::new(::ErrorKind::FormatNotSupported,
"color and depth attachment dimensions and number of samples have to be the same"));
}
}
}
let attachments = FboAttachments::new(colors, depth);
let new_fbo = Fbo{
framebuffer: self.framebuffer.clone(),
attachments,
fbo_state: self.fbo_state.clone(),
};
if new_fbo.attachments.hash != self.fbo_state.get(){
let max_attach = new_fbo.framebuffer.gl().capabilities().max_color_attachments;
if new_fbo.attachments.color.len() > max_attach as usize{
return Err(::Error::new(::ErrorKind::MaxColorAttachments, None));
}
new_fbo.bind_attachments();
match new_fbo.framebuffer.check_status(){
gl::FRAMEBUFFER_COMPLETE => {
Ok(new_fbo)
}
status => Err(::Error::with_gl_code(::ErrorKind::FramebufferCreationError,None,status))
}
}else{
Ok(new_fbo)
}
}
}
impl<C: BorrowColorAttach, D: BorrowDepthAttach> Fbo<C,D>{
#[cfg(not(any(feature = "gles", feature="webgl")))]
pub(crate) fn bind_attachments(&self){
if self.framebuffer.is_dsa() {
self.bind_attachments_dsa()
}else{
self.bind_attachments_no_dsa()
}
}
#[cfg(any(feature = "gles", feature="webgl"))]
pub(crate) fn bind_attachments(&self){
self.bind_attachments_no_dsa()
}
#[cfg(not(any(feature = "gles", feature="webgl")))]
fn bind_attachments_dsa(&self) {
if self.attachments.hash != self.fbo_state.get(){
self.fbo_state.set(self.attachments.hash);
unsafe{
if !self.attachments.color.is_empty(){
let attachments: Vec<u32> = (0..self.attachments.color.len())
.map(|i| gl::COLOR_ATTACHMENT0 + i as u32)
.collect();
for (i, color) in self.attachments.color.iter().enumerate(){
color.borrow_color_attachment().color_attach(
&self.framebuffer,
gl::FRAMEBUFFER,
gl::COLOR_ATTACHMENT0 + i as u32
);
}
self.framebuffer.set_drawbuffers(&attachments);
}else{
self.framebuffer.set_no_drawbuffers();
}
if let Some(depth) = self.attachments.depth.as_ref(){
depth
.borrow_depth_attachment()
.depth_attach(
&self.framebuffer,
gl::FRAMEBUFFER,
gl::DEPTH_ATTACHMENT
);
}else{
self.framebuffer.gl().NamedFramebufferTexture(
self.framebuffer.id(),
gl::DEPTH_ATTACHMENT,
0,
0)
}
}
}
}
fn bind_attachments_no_dsa(&self) {
if self.attachments.hash != self.fbo_state.get(){
self.fbo_state.set(self.attachments.hash);
unsafe{
self.framebuffer.bind();
if !self.attachments.color.is_empty(){
let attachments: Vec<u32> = (0..self.attachments.color.len())
.map(|i| gl::COLOR_ATTACHMENT0 + i as u32)
.collect();
for (i, color) in self.attachments.color.iter().enumerate(){
color.borrow_color_attachment().color_attach(
&self.framebuffer,
gl::FRAMEBUFFER,
gl::COLOR_ATTACHMENT0 + i as u32
);
}
self.framebuffer.set_drawbuffers(&attachments);
}else{
self.framebuffer.set_no_drawbuffers();
}
if let Some(depth) = self.attachments.depth.as_ref(){
depth.borrow_depth_attachment().depth_attach(
&self.framebuffer,
gl::FRAMEBUFFER,
gl::DEPTH_ATTACHMENT
);
}else{
self.framebuffer.gl().FramebufferTexture2D(
gl::FRAMEBUFFER,
gl::DEPTH_ATTACHMENT,
gl::TEXTURE_2D,
0,
0
);
}
self.framebuffer.unbind();
}
}
}
}
pub(crate) enum AttachmentTy {
Color(u32),
Depth
}
impl<C,D> Fbo<C,D>{
#[cfg(all(not(feature = "gles"), not(feature="webgl")))]
pub(crate) fn blit_<C2,D2>(&self, attachment: AttachmentTy, fbo2: &mut Fbo<C2,D2>, src_rect: &Rect, dst_rect: &Rect, state: Option<&State>) {
let gl = &self.framebuffer.gl();
if let Some(state) = state {
gl.state_mut().apply(state);
}else{
gl.state_mut().disable_scissors();
}
match attachment {
AttachmentTy::Color(attachment) => {
unsafe{
if self.framebuffer.is_dsa() && !self.framebuffer.needs_intel_hack() {
gl.NamedFramebufferReadBuffer(self.id(), gl::COLOR_ATTACHMENT0 + attachment);
gl.BlitNamedFramebuffer(
self.id(),
fbo2.id(),
src_rect.left as i32,
src_rect.bottom as i32,
src_rect.width as i32,
src_rect.height as i32,
dst_rect.left as i32,
dst_rect.bottom as i32,
dst_rect.width as i32,
dst_rect.height as i32,
gl::COLOR_BUFFER_BIT,
gl::LINEAR,
);
}else{
gl.BindFramebuffer(gl::READ_FRAMEBUFFER, self.id());
gl.BindFramebuffer(gl::DRAW_FRAMEBUFFER, fbo2.id());
gl.ReadBuffer(gl::COLOR_ATTACHMENT0 + attachment);
gl.BlitFramebuffer(
src_rect.left as i32,
src_rect.bottom as i32,
src_rect.width as i32,
src_rect.height as i32,
dst_rect.left as i32,
dst_rect.bottom as i32,
dst_rect.width as i32,
dst_rect.height as i32,
gl::COLOR_BUFFER_BIT,
gl::LINEAR
);
gl.BindFramebuffer(gl::READ_FRAMEBUFFER, 0);
gl.BindFramebuffer(gl::DRAW_FRAMEBUFFER, 0);
}
}
}
AttachmentTy::Depth => {
unsafe{
if self.framebuffer.is_dsa() && !self.framebuffer.needs_intel_hack() {
gl.BlitNamedFramebuffer(
self.id(),
fbo2.id(),
src_rect.left as i32,
src_rect.bottom as i32,
src_rect.width as i32,
src_rect.height as i32,
dst_rect.left as i32,
dst_rect.bottom as i32,
dst_rect.width as i32,
dst_rect.height as i32,
gl::DEPTH_BUFFER_BIT,
gl::NEAREST,
);
}else{
gl.BindFramebuffer(gl::READ_FRAMEBUFFER, self.id());
gl.BindFramebuffer(gl::DRAW_FRAMEBUFFER, fbo2.id());
gl.BlitFramebuffer(
src_rect.left as i32,
src_rect.bottom as i32,
src_rect.width as i32,
src_rect.height as i32,
dst_rect.left as i32,
dst_rect.bottom as i32,
dst_rect.width as i32,
dst_rect.height as i32,
gl::DEPTH_BUFFER_BIT,
gl::NEAREST
);
gl.BindFramebuffer(gl::READ_FRAMEBUFFER, 0);
gl.BindFramebuffer(gl::DRAW_FRAMEBUFFER, 0);
}
}
}
}
}
#[cfg(any(feature = "gles", feature="webgl"))]
pub(crate) fn blit_<C2,D2>(&self, attachment: AttachmentTy, fbo2: &mut Fbo<C2,D2>, src_rect: &Rect, dst_rect: &Rect, state: Option<&State>) {
let gl = &self.framebuffer.gl();
if let Some(state) = state {
gl.state_mut().apply(state);
}else{
gl.state_mut().disable_scissors();
}
match attachment {
AttachmentTy::Color(attachment) => {
unsafe{
gl.BindFramebuffer(gl::READ_FRAMEBUFFER, self.id());
gl.BindFramebuffer(gl::DRAW_FRAMEBUFFER, fbo2.id());
gl.ReadBuffer(gl::COLOR_ATTACHMENT0 + attachment);
#[cfg(target_os="emscripten")]
{
extern "C"{
fn emscripten_glBlitFramebuffer( srcX0: GLint,
srcY0: GLint,
srcX1: GLint,
srcY1: GLint,
dstX0: GLint,
dstY0: GLint,
dstX1: GLint,
dstY1: GLint,
mask: GLbitfield,
filter: GLenum);
}
emscripten_glBlitFramebuffer(
src_rect.left as i32,
src_rect.bottom as i32,
src_rect.width as i32,
src_rect.height as i32,
dst_rect.left as i32,
dst_rect.bottom as i32,
dst_rect.width as i32,
dst_rect.height as i32,
gl::COLOR_BUFFER_BIT,
gl::LINEAR
);
}
#[cfg(not(target_os="emscripten"))]
{
gl.BlitFramebuffer(
src_rect.left as i32,
src_rect.bottom as i32,
src_rect.width as i32,
src_rect.height as i32,
dst_rect.left as i32,
dst_rect.bottom as i32,
dst_rect.width as i32,
dst_rect.height as i32,
gl::COLOR_BUFFER_BIT,
gl::LINEAR
);
}
gl.BindFramebuffer(gl::READ_FRAMEBUFFER, 0);
gl.BindFramebuffer(gl::DRAW_FRAMEBUFFER, 0);
}
}
AttachmentTy::Depth => {
unsafe{
gl.BindFramebuffer(gl::READ_FRAMEBUFFER, self.id());
gl.BindFramebuffer(gl::DRAW_FRAMEBUFFER, fbo2.id());
#[cfg(target_os="emscripten")]
{
extern "C"{
fn emscripten_glBlitFramebuffer( srcX0: GLint,
srcY0: GLint,
srcX1: GLint,
srcY1: GLint,
dstX0: GLint,
dstY0: GLint,
dstX1: GLint,
dstY1: GLint,
mask: GLbitfield,
filter: GLenum);
}
emscripten_glBlitFramebuffer(
src_rect.left as i32,
src_rect.bottom as i32,
src_rect.width as i32,
src_rect.height as i32,
dst_rect.left as i32,
dst_rect.bottom as i32,
dst_rect.width as i32,
dst_rect.height as i32,
gl::DEPTH_BUFFER_BIT,
gl::NEAREST
);
}
#[cfg(not(target_os="emscripten"))]
gl.BlitFramebuffer(
src_rect.left as i32,
src_rect.bottom as i32,
src_rect.width as i32,
src_rect.height as i32,
dst_rect.left as i32,
dst_rect.bottom as i32,
dst_rect.width as i32,
dst_rect.height as i32,
gl::DEPTH_BUFFER_BIT,
gl::NEAREST
);
gl.BindFramebuffer(gl::READ_FRAMEBUFFER, 0);
gl.BindFramebuffer(gl::DRAW_FRAMEBUFFER, 0);
}
}
}
}
pub fn blit<C2,D2>(&self, fbo2: &mut Fbo<C2,D2>, src_rect: &Rect, dst_rect: &Rect){
self.blit_(AttachmentTy::Color(0), fbo2, src_rect, dst_rect, None)
}
pub fn blit_color_attachment<C2,D2>(&self, attachment: u32, fbo2: &mut Fbo<C2,D2>, src_rect: &Rect, dst_rect: &Rect){
self.blit_(AttachmentTy::Color(attachment), fbo2, src_rect, dst_rect, None)
}
pub fn blit_depth<C2,D2>(&self, fbo2: &mut Fbo<C2,D2>, src_rect: &Rect, dst_rect: &Rect){
self.blit_(AttachmentTy::Depth, fbo2, src_rect, dst_rect, None)
}
#[cfg(all(not(feature = "gles"), not(feature="webgl")))]
pub fn invalidate_color(&self){
let attachments = (0 .. self.attachments.color.len())
.map(|i| gl::COLOR_ATTACHMENT0 + i as u32)
.collect::<Vec<_>>();
unsafe{
self.framebuffer.gl().InvalidateNamedFramebufferData(
self.framebuffer.id(),
self.attachments.color.len() as i32,
attachments.as_ptr());
}
}
#[cfg(all(not(feature = "gles"), not(feature="webgl")))]
pub fn invalidate_depth(&self){
let attachments = [gl::DEPTH_ATTACHMENT];
unsafe{
self.framebuffer.gl().InvalidateFramebuffer(gl::FRAMEBUFFER, 1, attachments.as_ptr());
}
}
pub fn id(&self) -> GLuint{
self.framebuffer.id()
}
pub fn color(&self, idx: usize) -> Option<&C>{
self.attachments.color.get(idx)
}
pub fn depth(&self) -> Option<&D>{
self.attachments.depth.as_ref()
}
pub fn into_attachments(mut self) -> (Vec<C>, Option<D>){
(mem::replace(&mut self.attachments.color, vec![]), mem::replace(&mut self.attachments.depth, None))
}
#[cfg(feature="webgl")]
pub fn read_to<T>(&self, x: i32, y: i32, width: i32, height: i32, format: GLenum, ty: GLenum, pixels: &mut [T])
where for <'s> &'s mut [T]: gl::AsArrayBufferView<'s>
{
unsafe{
let gl = &self.framebuffer.gl();
gl.BindFramebuffer(gl::FRAMEBUFFER, self.id());
gl.ReadBuffer(gl::COLOR_ATTACHMENT0);
gl.ReadPixels(x, y, width, height, format, ty, Some(pixels));
}
}
#[cfg(not(feature="webgl"))]
pub unsafe fn read_to<T>(&self, x: i32, y: i32, width: i32, height: i32, format: GLenum, ty: GLenum, pixels: &mut [T]){
let gl = &self.framebuffer.gl();
gl.BindFramebuffer(gl::FRAMEBUFFER, self.id());
gl.ReadBuffer(gl::COLOR_ATTACHMENT0);
gl.ReadPixels(x, y, width, height, format, ty, pixels.as_mut_ptr() as *mut GLvoid);
}
#[cfg(feature="webgl")]
pub fn read_to_buffer<T, B: BufferRange<T>>(&self, x: i32, y: i32, width: i32, height: i32, format: GLenum, ty: GLenum, buffer: &mut B)
where for <'s> &'s mut [T]: gl::AsArrayBufferView<'s>
{
unsafe{
let gl = &self.framebuffer.gl();
gl.BindBuffer(gl::PIXEL_PACK_BUFFER, buffer.id());
gl.BindFramebuffer(gl::FRAMEBUFFER, self.id());
gl.ReadBuffer(gl::COLOR_ATTACHMENT0);
gl.ReadPixels_1(x, y, width, height, format, ty, (buffer.start() * mem::size_of::<T>()) as isize);
gl.BindBuffer(gl::PIXEL_PACK_BUFFER, 0);
}
}
#[cfg(not(feature="webgl"))]
pub unsafe fn read_to_buffer<T, B: BufferRange<T>>(&self, x: i32, y: i32, width: i32, height: i32, format: GLenum, ty: GLenum, buffer: &mut B){
let gl = &self.framebuffer.gl();
gl.BindBuffer(gl::PIXEL_PACK_BUFFER, buffer.id());
gl.BindFramebuffer(gl::FRAMEBUFFER, self.id());
gl.ReadBuffer(gl::COLOR_ATTACHMENT0);
gl.ReadPixels(x, y, width, height, format, ty, (buffer.start() * mem::size_of::<T>()) as *mut GLvoid);
gl.BindBuffer(gl::PIXEL_PACK_BUFFER, 0);
}
}
impl<C: BorrowColorAttach, D: BorrowDepthAttach> Fbo<C, D>{
pub fn viewport(&self) -> Rect{
Rect{ bottom: 0, left: 0, width: self.width(), height: self.height() }
}
pub fn width(&self) -> u32{
self.attachments.color.get(0).map(|c| c.borrow_color_attachment().width())
.unwrap_or_else(|| self.attachments.depth.as_ref().map(|d| d.borrow_depth_attachment().width()).unwrap())
}
pub fn height(&self) -> u32{
self.attachments.color.get(0).map(|c| c.borrow_color_attachment().height())
.unwrap_or_else(|| self.attachments.depth.as_ref().map(|d| d.borrow_depth_attachment().height()).unwrap())
}
pub fn aspect_ratio(&self) -> f32{
self.width() as f32 / self.height() as f32
}
pub fn size(&self) -> (u32, u32){
(self.width(), self.height())
}
}
impl<C: Borrow<ColorAttachment>, D: Borrow<DepthAttachment>> Fbo<C,D>{
pub fn color_tex(&self, idx: usize) -> Option<&Texture>{
self.color(idx).and_then(|color| match color.borrow(){
ColorAttachment::TextureLevel(tex, _) => Some(tex),
_ => None
})
}
#[cfg(not(feature="webgl"))]
pub fn color_cubemap(&self, idx: usize) -> Option<&CubeMap>{
self.color(idx).and_then(|color| match color.borrow(){
ColorAttachment::CubeMapLevel(cm, _) => Some(cm),
ColorAttachment::CubeMapFaceLevel(cm, _, _) => Some(cm),
_ => None
})
}
pub fn depth_tex(&self) -> Option<&Texture>{
self.attachments.depth.as_ref().and_then(|tex| match tex.borrow() {
DepthAttachment::TextureLevel(tex, _) => Some(tex),
_ => None
})
}
#[cfg(not(feature="webgl"))]
pub fn depth_cubemap(&self) -> Option<&CubeMap>{
self.attachments.depth.as_ref().and_then(|tex| match tex.borrow() {
DepthAttachment::CubeMapLevel(cm, _) => Some(cm),
DepthAttachment::CubeMapFaceLevel(cm, _, _) => Some(cm),
_ => None
})
}
}
impl<C: BorrowMut<ColorAttachment>, D: BorrowMut<DepthAttachment>> Fbo<C,D>{
pub fn color_tex_mut(&mut self, idx: usize) -> Option<&mut Texture>{
self.attachments.color.get_mut(idx).and_then(|color| match color.borrow_mut(){
ColorAttachment::TextureLevel(ref mut tex, _) => Some(tex),
_ => None
})
}
pub fn depth_tex_mut(&mut self) -> Option<&mut Texture>{
self.attachments.depth.as_mut().and_then(|tex| match tex.borrow_mut() {
DepthAttachment::TextureLevel(tex, _) => Some(tex),
_ => None
})
}
}