use euclid::default::Size2D;
use sparkle::gl;
use sparkle::gl::types::{GLuint, GLenum, GLint};
use std::rc::Rc;
use crate::GLContext;
use crate::NativeGLContextMethods;
#[derive(Debug)]
pub enum ColorAttachmentType {
Texture,
Renderbuffer,
}
impl Default for ColorAttachmentType {
fn default() -> ColorAttachmentType {
ColorAttachmentType::Renderbuffer
}
}
#[derive(Debug)]
pub enum ColorAttachment {
Renderbuffer(GLuint),
Texture(GLuint),
}
impl ColorAttachment {
pub fn color_attachment_type(&self) -> ColorAttachmentType {
match *self {
ColorAttachment::Renderbuffer(_) => ColorAttachmentType::Renderbuffer,
ColorAttachment::Texture(_) => ColorAttachmentType::Texture,
}
}
fn destroy(self, gl: &gl::Gl) {
match self {
ColorAttachment::Renderbuffer(id) => gl.delete_renderbuffers(&[id]),
ColorAttachment::Texture(tex_id) => gl.delete_textures(&[tex_id]),
}
}
}
pub struct DrawBuffer {
gl_: Rc<gl::Gl>,
size: Size2D<i32>,
framebuffer: GLuint,
color_attachment: Option<ColorAttachment>,
stencil_renderbuffer: GLuint,
depth_renderbuffer: GLuint,
packed_depth_stencil_renderbuffer: GLuint,
}
fn create_renderbuffer(gl_: &gl::Gl,
format: GLenum,
size: &Size2D<i32>) -> GLuint {
let ret = gl_.gen_renderbuffers(1)[0];
gl_.bind_renderbuffer(gl::RENDERBUFFER, ret);
gl_.renderbuffer_storage(gl::RENDERBUFFER, format, size.width, size.height);
gl_.bind_renderbuffer(gl::RENDERBUFFER, 0);
ret
}
impl DrawBuffer {
pub fn new<T: NativeGLContextMethods>(context: &GLContext<T>,
mut size: Size2D<i32>,
color_attachment_type: ColorAttachmentType)
-> Result<Self, &'static str>
{
const MIN_DRAWING_BUFFER_SIZE: i32 = 16;
use std::cmp;
let attrs = context.borrow_attributes();
let capabilities = context.borrow_capabilities();
debug!("Creating draw buffer {:?}, {:?}, attrs: {:?}, caps: {:?}",
size, color_attachment_type, attrs, capabilities);
if attrs.antialias && capabilities.max_samples == 0 {
error!("The given GLContext doesn't support requested antialising");
}
if attrs.preserve_drawing_buffer {
error!("preserveDrawingBuffer is not supported yet");
}
size.width = cmp::max(MIN_DRAWING_BUFFER_SIZE, size.width);
size.height = cmp::max(MIN_DRAWING_BUFFER_SIZE, size.height);
let mut draw_buffer = DrawBuffer {
gl_: context.clone_gl(),
size: size,
framebuffer: 0,
color_attachment: None,
stencil_renderbuffer: 0,
depth_renderbuffer: 0,
packed_depth_stencil_renderbuffer: 0,
};
context.make_current()?;
draw_buffer.init(context, color_attachment_type)?;
debug_assert_eq!(draw_buffer.gl().check_framebuffer_status(gl::FRAMEBUFFER),
gl::FRAMEBUFFER_COMPLETE);
debug_assert_eq!(draw_buffer.gl().get_error(),
gl::NO_ERROR);
Ok(draw_buffer)
}
#[inline(always)]
pub fn get_framebuffer(&self) -> GLuint {
self.framebuffer
}
#[inline(always)]
pub fn size(&self) -> Size2D<i32> {
self.size
}
#[inline(always)]
pub fn color_attachment_type(&self) -> ColorAttachmentType {
self.color_attachment.as_ref().unwrap().color_attachment_type()
}
pub fn get_bound_color_renderbuffer_id(&self) -> Option<GLuint> {
match self.color_attachment.as_ref().unwrap() {
&ColorAttachment::Renderbuffer(id) => Some(id),
_ => None,
}
}
pub fn get_bound_texture_id(&self) -> Option<GLuint> {
match self.color_attachment.as_ref().unwrap() {
&ColorAttachment::Renderbuffer(_) => None,
&ColorAttachment::Texture(id) => Some(id),
}
}
fn gl(&self) -> &gl::Gl {
&*self.gl_
}
fn init<T: NativeGLContextMethods>(&mut self,
context: &GLContext<T>,
color_attachment_type: ColorAttachmentType)
-> Result<(), &'static str> {
let attrs = context.borrow_attributes();
let formats = context.borrow_formats();
assert!(self.color_attachment.is_none(),
"Would leak color attachment!");
self.color_attachment = match color_attachment_type {
ColorAttachmentType::Renderbuffer => {
let color_renderbuffer =
create_renderbuffer(self.gl(), formats.color_renderbuffer, &self.size);
debug_assert!(color_renderbuffer != 0);
Some(ColorAttachment::Renderbuffer(color_renderbuffer))
},
ColorAttachmentType::Texture => {
let texture = self.gl().gen_textures(1)[0];
debug_assert!(texture != 0);
self.gl().bind_texture(gl::TEXTURE_2D, texture);
self.gl().tex_image_2d(gl::TEXTURE_2D, 0,
formats.texture_internal as GLint, self.size.width, self.size.height, 0, formats.texture, formats.texture_type, None);
self.gl().tex_parameter_i(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::NEAREST as GLint);
self.gl().tex_parameter_i(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::NEAREST as GLint);
self.gl().tex_parameter_i(gl::TEXTURE_2D, gl::TEXTURE_WRAP_S, gl::CLAMP_TO_EDGE as GLint);
self.gl().tex_parameter_i(gl::TEXTURE_2D, gl::TEXTURE_WRAP_T, gl::CLAMP_TO_EDGE as GLint);
self.gl().bind_texture(gl::TEXTURE_2D, 0);
debug_assert_eq!(self.gl().get_error(), gl::NO_ERROR);
Some(ColorAttachment::Texture(texture))
},
};
if attrs.depth && attrs.stencil && formats.packed_depth_stencil {
self.packed_depth_stencil_renderbuffer = create_renderbuffer(self.gl(), gl::DEPTH24_STENCIL8, &self.size);
debug_assert!(self.packed_depth_stencil_renderbuffer != 0);
} else {
if attrs.depth {
self.depth_renderbuffer = create_renderbuffer(self.gl(), formats.depth, &self.size);
debug_assert!(self.depth_renderbuffer != 0);
}
if attrs.stencil {
self.stencil_renderbuffer = create_renderbuffer(self.gl(), formats.stencil, &self.size);
debug_assert!(self.stencil_renderbuffer != 0);
}
}
self.framebuffer = self.gl().gen_framebuffers(1)[0];
debug_assert!(self.framebuffer != 0);
self.attach_to_framebuffer()
}
fn attach_to_framebuffer(&mut self) -> Result<(), &'static str> {
self.gl().bind_framebuffer(gl::FRAMEBUFFER, self.framebuffer);
debug_assert!(self.gl().is_framebuffer(self.framebuffer));
match *self.color_attachment.as_ref().unwrap() {
ColorAttachment::Renderbuffer(color_renderbuffer) => {
self.gl().framebuffer_renderbuffer(gl::FRAMEBUFFER,
gl::COLOR_ATTACHMENT0,
gl::RENDERBUFFER,
color_renderbuffer);
debug_assert!(self.gl().is_renderbuffer(color_renderbuffer));
},
ColorAttachment::Texture(texture_id) => {
self.gl().framebuffer_texture_2d(gl::FRAMEBUFFER,
gl::COLOR_ATTACHMENT0,
gl::TEXTURE_2D,
texture_id, 0);
},
}
if self.packed_depth_stencil_renderbuffer != 0 {
self.gl().framebuffer_renderbuffer(gl::FRAMEBUFFER,
gl::DEPTH_STENCIL_ATTACHMENT,
gl::RENDERBUFFER,
self.packed_depth_stencil_renderbuffer);
debug_assert!(self.gl().is_renderbuffer(self.packed_depth_stencil_renderbuffer));
}
if self.depth_renderbuffer != 0 {
self.gl().framebuffer_renderbuffer(gl::FRAMEBUFFER,
gl::DEPTH_ATTACHMENT,
gl::RENDERBUFFER,
self.depth_renderbuffer);
debug_assert!(self.gl().is_renderbuffer(self.depth_renderbuffer));
}
if self.stencil_renderbuffer != 0 {
self.gl().framebuffer_renderbuffer(gl::FRAMEBUFFER,
gl::STENCIL_ATTACHMENT,
gl::RENDERBUFFER,
self.stencil_renderbuffer);
debug_assert!(self.gl().is_renderbuffer(self.stencil_renderbuffer));
}
Ok(())
}
}
impl Drop for DrawBuffer {
fn drop(&mut self) {
if let Some(att) = self.color_attachment.take() {
att.destroy(self.gl());
}
self.gl().delete_framebuffers(&[self.framebuffer]);
self.gl().delete_renderbuffers(&[self.stencil_renderbuffer,
self.depth_renderbuffer,
self.packed_depth_stencil_renderbuffer]);
}
}