librashader/librashader-runtime-gl/src/framebuffer.rs

359 lines
10 KiB
Rust
Raw Normal View History

2022-11-20 15:16:57 +11:00
use crate::util;
2022-11-27 18:10:11 +11:00
use crate::texture::Texture;
use gl::types::{GLenum, GLint, GLsizei, GLuint};
2022-11-29 17:57:04 +11:00
use librashader_common::{FilterMode, ImageFormat, Size, WrapMode};
2022-11-20 14:03:58 +11:00
use librashader_presets::{Scale2D, ScaleType, Scaling};
2022-11-22 09:44:38 +11:00
use crate::error::FilterChainError;
use crate::error::Result;
2022-11-14 17:49:51 +11:00
2022-11-20 14:03:58 +11:00
#[derive(Debug)]
2022-11-14 17:49:51 +11:00
pub struct Framebuffer {
pub image: GLuint,
2022-11-21 18:56:03 +11:00
pub handle: GLuint,
2022-11-21 19:01:26 +11:00
pub size: Size<u32>,
2022-11-14 17:49:51 +11:00
pub format: GLenum,
pub max_levels: u32,
pub levels: u32,
2022-11-21 18:56:03 +11:00
is_raw: bool,
2022-11-14 17:49:51 +11:00
}
impl Framebuffer {
pub fn new(max_levels: u32) -> Framebuffer {
let mut framebuffer = 0;
unsafe {
gl::GenFramebuffers(1, &mut framebuffer);
gl::BindFramebuffer(gl::FRAMEBUFFER, framebuffer);
2022-11-19 17:55:49 +11:00
gl::BindFramebuffer(gl::FRAMEBUFFER, 0);
2022-11-14 17:49:51 +11:00
}
Framebuffer {
image: 0,
2022-11-20 15:16:57 +11:00
size: Size {
width: 1,
height: 1,
},
2022-11-14 17:49:51 +11:00
format: 0,
max_levels,
levels: 0,
2022-11-20 15:16:19 +11:00
handle: framebuffer,
2022-11-22 08:21:50 +11:00
is_raw: false,
2022-11-14 17:49:51 +11:00
}
}
2022-11-20 15:16:57 +11:00
pub fn new_from_raw(
texture: GLuint,
handle: GLuint,
format: GLenum,
2022-11-21 19:01:26 +11:00
size: Size<u32>,
2022-11-20 15:16:57 +11:00
miplevels: u32,
) -> Framebuffer {
2022-11-20 14:03:58 +11:00
Framebuffer {
image: texture,
size,
format,
max_levels: miplevels,
levels: miplevels,
2022-11-20 15:16:57 +11:00
handle,
2022-11-22 08:21:50 +11:00
is_raw: true,
2022-11-20 14:03:58 +11:00
}
}
2022-11-21 18:56:03 +11:00
pub(crate) fn as_texture(&self, filter: FilterMode, wrap_mode: WrapMode) -> Texture {
2022-11-20 14:03:58 +11:00
Texture {
image: GlImage {
handle: self.image,
format: self.format,
size: self.size,
2022-11-20 15:16:57 +11:00
padded_size: Default::default(),
2022-11-20 14:03:58 +11:00
},
filter,
mip_filter: filter,
2022-11-20 15:16:57 +11:00
wrap_mode,
2022-11-20 14:03:58 +11:00
}
}
2022-11-21 18:56:03 +11:00
pub(crate) fn scale(
2022-11-20 15:16:57 +11:00
&mut self,
scaling: Scale2D,
2022-11-29 17:57:04 +11:00
format: ImageFormat,
2022-11-20 15:16:57 +11:00
viewport: &Viewport,
_original: &Texture,
source: &Texture,
2022-11-22 09:44:38 +11:00
) -> Result<Size<u32>> {
2022-11-21 18:56:03 +11:00
if self.is_raw {
2022-11-22 09:44:38 +11:00
return Ok(self.size);
2022-11-21 18:56:03 +11:00
}
2022-11-22 09:47:38 +11:00
let width;
let height;
2022-11-20 14:03:58 +11:00
match scaling.x {
Scaling {
scale_type: ScaleType::Input,
2022-11-20 15:16:57 +11:00
factor,
} => width = source.image.size.width * factor,
2022-11-20 14:03:58 +11:00
Scaling {
scale_type: ScaleType::Absolute,
2022-11-20 15:16:57 +11:00
factor,
} => width = factor.into(),
2022-11-20 14:03:58 +11:00
Scaling {
scale_type: ScaleType::Viewport,
2022-11-20 15:16:57 +11:00
factor,
} => width = viewport.output.size.width * factor,
2022-11-20 14:03:58 +11:00
};
match scaling.y {
Scaling {
scale_type: ScaleType::Input,
2022-11-20 15:16:57 +11:00
factor,
} => height = source.image.size.height * factor,
2022-11-20 14:03:58 +11:00
Scaling {
scale_type: ScaleType::Absolute,
2022-11-20 15:16:57 +11:00
factor,
} => height = factor.into(),
2022-11-20 14:03:58 +11:00
Scaling {
scale_type: ScaleType::Viewport,
2022-11-20 15:16:57 +11:00
factor,
} => height = viewport.output.size.height * factor,
2022-11-20 14:03:58 +11:00
};
let size = Size {
width: width.round() as u32,
2022-11-20 15:16:57 +11:00
height: height.round() as u32,
2022-11-20 14:03:58 +11:00
};
if self.size != size {
self.size = size;
2022-11-20 15:16:57 +11:00
self.init(
size,
2022-11-29 17:57:04 +11:00
if format == ImageFormat::Unknown {
ImageFormat::R8G8B8A8Unorm
2022-11-20 15:16:57 +11:00
} else {
format
},
2022-11-22 09:44:38 +11:00
)?;
2022-11-20 14:03:58 +11:00
}
2022-11-22 09:44:38 +11:00
Ok(size)
2022-11-20 14:03:58 +11:00
}
2022-11-21 18:56:03 +11:00
pub(crate) fn clear(&self) {
unsafe {
gl::BindFramebuffer(gl::FRAMEBUFFER, self.handle);
gl::ColorMask(gl::TRUE, gl::TRUE, gl::TRUE, gl::TRUE);
gl::ClearColor(0.0, 0.0, 0.0, 0.0);
gl::Clear(gl::COLOR_BUFFER_BIT);
gl::BindFramebuffer(gl::FRAMEBUFFER, 0);
}
}
2022-11-26 18:38:15 +11:00
pub(crate) fn copy_from(&mut self, image: &GlImage) -> Result<()> {
// todo: may want to use a shader and draw a quad to be faster.
if image.size != self.size || image.format != self.format {
2022-11-22 09:44:38 +11:00
self.init(image.size, image.format)?;
}
unsafe {
gl::BindFramebuffer(gl::FRAMEBUFFER, self.handle);
2022-11-22 08:21:50 +11:00
gl::FramebufferTexture2D(
gl::READ_FRAMEBUFFER,
gl::COLOR_ATTACHMENT0,
gl::TEXTURE_2D,
image.handle,
0,
);
2022-11-22 08:21:50 +11:00
gl::FramebufferTexture2D(
gl::DRAW_FRAMEBUFFER,
gl::COLOR_ATTACHMENT1,
gl::TEXTURE_2D,
self.image,
0,
);
2022-11-29 10:34:37 +11:00
gl::ReadBuffer(gl::COLOR_ATTACHMENT0);
gl::DrawBuffer(gl::COLOR_ATTACHMENT1);
2022-11-22 08:21:50 +11:00
gl::BlitFramebuffer(
0,
0,
self.size.width as GLint,
self.size.height as GLint,
0,
0,
self.size.width as GLint,
self.size.height as GLint,
gl::COLOR_BUFFER_BIT,
gl::NEAREST,
);
// cleanup after ourselves.
2022-11-22 08:21:50 +11:00
gl::FramebufferTexture2D(
gl::READ_FRAMEBUFFER,
gl::COLOR_ATTACHMENT0,
gl::TEXTURE_2D,
0,
0,
);
gl::FramebufferTexture2D(
gl::DRAW_FRAMEBUFFER,
gl::COLOR_ATTACHMENT1,
gl::TEXTURE_2D,
0,
0,
);
// set this back to color_attachment 0
gl::FramebufferTexture2D(
gl::FRAMEBUFFER,
gl::COLOR_ATTACHMENT0,
gl::TEXTURE_2D,
self.image,
0,
);
gl::BindFramebuffer(gl::FRAMEBUFFER, 0);
}
2022-11-22 09:44:38 +11:00
Ok(())
}
2022-11-21 18:56:03 +11:00
2022-11-22 09:44:38 +11:00
pub(crate) fn init(&mut self, mut size: Size<u32>, format: impl Into<GLenum>) -> Result<()> {
2022-11-21 18:56:03 +11:00
if self.is_raw {
2022-11-22 09:44:38 +11:00
return Ok(());
2022-11-21 18:56:03 +11:00
}
2022-11-19 17:55:49 +11:00
self.format = format.into();
2022-11-14 17:49:51 +11:00
self.size = size;
unsafe {
2022-11-20 15:16:19 +11:00
gl::BindFramebuffer(gl::FRAMEBUFFER, self.handle);
2022-11-14 17:49:51 +11:00
// reset the framebuffer image
if self.image != 0 {
2022-11-20 15:16:57 +11:00
gl::FramebufferTexture2D(
gl::FRAMEBUFFER,
gl::COLOR_ATTACHMENT0,
gl::TEXTURE_2D,
0,
0,
);
2022-11-14 17:49:51 +11:00
gl::DeleteTextures(1, &self.image);
}
gl::GenTextures(1, &mut self.image);
2022-11-19 17:55:49 +11:00
gl::BindTexture(gl::TEXTURE_2D, self.image);
2022-11-14 17:49:51 +11:00
if size.width == 0 {
size.width = 1;
}
if size.height == 0 {
size.height = 1;
}
2022-11-24 18:08:34 +11:00
self.levels = util::calc_miplevel(size);
2022-11-14 17:49:51 +11:00
if self.levels > self.max_levels {
self.levels = self.max_levels;
}
if self.levels == 0 {
self.levels = 1;
}
2022-11-20 15:16:57 +11:00
gl::TexStorage2D(
gl::TEXTURE_2D,
self.levels as GLsizei,
self.format,
size.width as GLsizei,
size.height as GLsizei,
);
2022-11-29 10:34:37 +11:00
2022-11-20 15:16:57 +11:00
gl::FramebufferTexture2D(
gl::FRAMEBUFFER,
gl::COLOR_ATTACHMENT0,
gl::TEXTURE_2D,
self.image,
0,
);
2022-11-14 17:49:51 +11:00
let status = gl::CheckFramebufferStatus(gl::FRAMEBUFFER);
if status != gl::FRAMEBUFFER_COMPLETE {
match status {
gl::FRAMEBUFFER_UNSUPPORTED => {
eprintln!("unsupported fbo");
2022-11-20 15:16:57 +11:00
gl::FramebufferTexture2D(
gl::FRAMEBUFFER,
gl::COLOR_ATTACHMENT0,
gl::TEXTURE_2D,
0,
0,
);
2022-11-14 17:49:51 +11:00
gl::DeleteTextures(1, &self.image);
gl::GenTextures(1, &mut self.image);
2022-11-29 10:34:37 +11:00
gl::BindTexture(gl::TEXTURE_2D, self.image);
2022-11-14 17:49:51 +11:00
2022-11-24 18:08:34 +11:00
self.levels = util::calc_miplevel(size);
2022-11-14 17:49:51 +11:00
if self.levels > self.max_levels {
self.levels = self.max_levels;
}
if self.levels == 0 {
self.levels = 1;
}
2022-11-20 15:16:57 +11:00
gl::TexStorage2D(
gl::TEXTURE_2D,
self.levels as GLsizei,
2022-11-29 17:57:04 +11:00
ImageFormat::R8G8B8A8Unorm.into(),
2022-11-20 15:16:57 +11:00
size.width as GLsizei,
size.height as GLsizei,
);
gl::FramebufferTexture2D(
gl::FRAMEBUFFER,
gl::COLOR_ATTACHMENT0,
gl::TEXTURE_2D,
self.image,
0,
);
2022-11-21 18:56:03 +11:00
// self.init =
// gl::CheckFramebufferStatus(gl::FRAMEBUFFER) == gl::FRAMEBUFFER_COMPLETE;
2022-11-14 17:49:51 +11:00
}
2022-11-22 09:44:38 +11:00
_ => return Err(FilterChainError::FramebufferInit(status))
2022-11-14 17:49:51 +11:00
}
}
gl::BindFramebuffer(gl::FRAMEBUFFER, 0);
gl::BindTexture(gl::TEXTURE_2D, 0);
}
2022-11-22 09:44:38 +11:00
Ok(())
2022-11-14 17:49:51 +11:00
}
}
2022-11-19 17:55:49 +11:00
impl Drop for Framebuffer {
fn drop(&mut self) {
unsafe {
2022-11-20 15:16:19 +11:00
if self.handle != 0 {
gl::DeleteFramebuffers(1, &self.handle);
2022-11-19 17:55:49 +11:00
}
if self.image != 0 {
gl::DeleteTextures(1, &self.image);
}
}
}
2022-11-20 15:16:57 +11:00
}
2022-11-21 18:56:03 +11:00
#[derive(Debug, Copy, Clone)]
pub struct Viewport<'a> {
pub x: i32,
pub y: i32,
pub output: &'a Framebuffer,
pub mvp: Option<&'a [f32; 16]>,
2022-11-21 18:56:03 +11:00
}
#[derive(Default, Debug, Copy, Clone)]
pub struct GlImage {
pub handle: GLuint,
pub format: GLenum,
2022-11-21 19:01:26 +11:00
pub size: Size<u32>,
pub padded_size: Size<u32>,
2022-11-21 18:56:03 +11:00
}