diff --git a/Cargo.lock b/Cargo.lock index 59a8ee7..c2bdea9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -472,9 +472,11 @@ name = "librashader-runtime" version = "0.1.0" dependencies = [ "bytemuck", + "librashader-common", "librashader-preprocess", "librashader-presets", "librashader-reflect", + "num-traits", "rustc-hash", ] diff --git a/librashader-runtime-d3d11/src/framebuffer.rs b/librashader-runtime-d3d11/src/framebuffer.rs index 9b12b92..3bd5c03 100644 --- a/librashader-runtime-d3d11/src/framebuffer.rs +++ b/librashader-runtime-d3d11/src/framebuffer.rs @@ -47,43 +47,7 @@ impl OwnedFramebuffer { return Ok(self.size); } - let width; - let height; - - match scaling.x { - Scaling { - scale_type: ScaleType::Input, - factor, - } => width = source.view.size.width * factor, - Scaling { - scale_type: ScaleType::Absolute, - factor, - } => width = factor.into(), - Scaling { - scale_type: ScaleType::Viewport, - factor, - } => width = viewport_size.width * factor, - }; - - match scaling.y { - Scaling { - scale_type: ScaleType::Input, - factor, - } => height = source.view.size.height * factor, - Scaling { - scale_type: ScaleType::Absolute, - factor, - } => height = factor.into(), - Scaling { - scale_type: ScaleType::Viewport, - factor, - } => height = viewport_size.height * factor, - }; - - let size = Size { - width: width.round() as u32, - height: height.round() as u32, - }; + let size = librashader_runtime::scaling::scale(scaling, source.view.size, *viewport_size); if self.size != size { self.size = size; diff --git a/librashader-runtime-d3d11/src/util.rs b/librashader-runtime-d3d11/src/util.rs index b289966..d873b15 100644 --- a/librashader-runtime-d3d11/src/util.rs +++ b/librashader-runtime-d3d11/src/util.rs @@ -166,13 +166,3 @@ pub fn d3d11_create_input_layout(device: &ID3D11Device, desc: &[D3D11_INPUT_ELEM // todo: d3d11.c 2097 pub type Result = std::result::Result>; -pub fn calc_miplevel(size: Size) -> u32 { - let mut size = std::cmp::max(size.width, size.height); - let mut levels = 0; - while size != 0 { - levels += 1; - size >>= 1; - } - - levels -} \ No newline at end of file diff --git a/librashader-runtime-gl/src/filter_chain.rs b/librashader-runtime-gl/src/filter_chain.rs index 18b97ea..fbf866b 100644 --- a/librashader-runtime-gl/src/filter_chain.rs +++ b/librashader-runtime-gl/src/filter_chain.rs @@ -1,7 +1,8 @@ use crate::binding::{UniformLocation, VariableLocation}; use crate::filter_pass::FilterPass; -use crate::framebuffer::{Framebuffer, GlImage, Viewport}; -use crate::quad_render::DrawQuad; +use crate::framebuffer::{GlImage, Viewport}; +use crate::gl::gl3::Gl3Framebuffer; +use crate::gl::gl3::{Gl3DrawQuad, Gl3LutLoad, Gl3UboRing}; use crate::render_target::RenderTarget; use crate::util; use crate::util::{gl_get_version, gl_u16_to_version, InlineRingBuffer}; @@ -26,14 +27,15 @@ use crate::options::{FilterChainOptions, FrameOptions}; use crate::samplers::SamplerSet; use crate::texture::Texture; use crate::binding::BufferStorage; +use crate::gl::{DrawQuad, Framebuffer, LoadLut, UboRing}; pub struct FilterChain { passes: Box<[FilterPass]>, common: FilterCommon, - pub(crate) draw_quad: DrawQuad, - output_framebuffers: Box<[Framebuffer]>, - feedback_framebuffers: Box<[Framebuffer]>, - history_framebuffers: VecDeque, + pub(crate) draw_quad: Gl3DrawQuad, + output_framebuffers: Box<[Gl3Framebuffer]>, + feedback_framebuffers: Box<[Gl3Framebuffer]>, + history_framebuffers: VecDeque, } pub struct FilterCommon { @@ -109,24 +111,24 @@ impl FilterChain { // initialize output framebuffers let mut output_framebuffers = Vec::new(); - output_framebuffers.resize_with(filters.len(), || Framebuffer::new(1)); + output_framebuffers.resize_with(filters.len(), || Gl3Framebuffer::new(1)); let mut output_textures = Vec::new(); output_textures.resize_with(filters.len(), Texture::default); // initialize feedback framebuffers let mut feedback_framebuffers = Vec::new(); - feedback_framebuffers.resize_with(filters.len(), || Framebuffer::new(1)); + feedback_framebuffers.resize_with(filters.len(), || Gl3Framebuffer::new(1)); let mut feedback_textures = Vec::new(); feedback_textures.resize_with(filters.len(), Texture::default); // load luts - let luts = FilterChain::load_luts(&preset.textures)?; + let luts = Gl3LutLoad::load_luts(&preset.textures)?; let (history_framebuffers, history_textures) = FilterChain::init_history(&filters, default_filter, default_wrap); // create vertex objects - let draw_quad = DrawQuad::new(); + let draw_quad = Gl3DrawQuad::new(); Ok(FilterChain { passes: filters, @@ -207,79 +209,6 @@ impl FilterChain { Ok((passes, semantics)) } - fn load_luts(textures: &[TextureConfig]) -> Result> { - let mut luts = FxHashMap::default(); - let pixel_unpack = unsafe { - let mut binding = 0; - gl::GetIntegerv(gl::PIXEL_UNPACK_BUFFER_BINDING, &mut binding); - binding - }; - - for (index, texture) in textures.iter().enumerate() { - let image = Image::load(&texture.path)?; - let levels = if texture.mipmap { - util::calc_miplevel(image.size) - } else { - 1u32 - }; - - let mut handle = 0; - unsafe { - gl::GenTextures(1, &mut handle); - gl::BindTexture(gl::TEXTURE_2D, handle); - gl::TexStorage2D( - gl::TEXTURE_2D, - levels as GLsizei, - gl::RGBA8, - image.size.width as GLsizei, - image.size.height as GLsizei, - ); - - gl::PixelStorei(gl::UNPACK_ROW_LENGTH, 0); - gl::PixelStorei(gl::UNPACK_ALIGNMENT, 4); - gl::BindBuffer(gl::PIXEL_UNPACK_BUFFER, 0); - gl::TexSubImage2D( - gl::TEXTURE_2D, - 0, - 0, - 0, - image.size.width as GLsizei, - image.size.height as GLsizei, - gl::RGBA, - gl::UNSIGNED_BYTE, - image.bytes.as_ptr().cast(), - ); - - let mipmap = levels > 1; - if mipmap { - gl::GenerateMipmap(gl::TEXTURE_2D); - } - - gl::BindTexture(gl::TEXTURE_2D, 0); - } - - luts.insert( - index, - Texture { - image: GlImage { - handle, - format: gl::RGBA8, - size: image.size, - padded_size: Size::default(), - }, - filter: texture.filter_mode, - mip_filter: texture.filter_mode, - wrap_mode: texture.wrap_mode, - }, - ); - } - - unsafe { - gl::BindBuffer(gl::PIXEL_UNPACK_BUFFER, pixel_unpack as GLuint); - }; - Ok(luts) - } - fn init_passes( version: GlVersion, passes: Vec, @@ -352,21 +281,7 @@ impl FilterChain { }; let ubo_ring = if let Some(ubo) = &reflection.ubo { - let size = ubo.size; - let mut ring: InlineRingBuffer = InlineRingBuffer::new(); - unsafe { - gl::GenBuffers(16, ring.items_mut().as_mut_ptr()); - for buffer in ring.items() { - gl::BindBuffer(gl::UNIFORM_BUFFER, *buffer); - gl::BufferData( - gl::UNIFORM_BUFFER, - size as GLsizeiptr, - std::ptr::null(), - gl::STREAM_DRAW, - ); - } - gl::BindBuffer(gl::UNIFORM_BUFFER, 0); - } + let ring = Gl3UboRing::new(ubo.size); Some(ring) } else { None @@ -445,7 +360,7 @@ impl FilterChain { filters: &[FilterPass], filter: FilterMode, wrap_mode: WrapMode, - ) -> (VecDeque, Box<[Texture]>) { + ) -> (VecDeque, Box<[Texture]>) { let mut required_images = 0; for pass in filters { @@ -479,7 +394,7 @@ impl FilterChain { eprintln!("[history] using frame history with {required_images} images"); let mut framebuffers = VecDeque::with_capacity(required_images); - framebuffers.resize_with(required_images, || Framebuffer::new(1)); + framebuffers.resize_with(required_images, || Gl3Framebuffer::new(1)); let mut history_textures = Vec::new(); history_textures.resize_with(required_images, || Texture { @@ -516,7 +431,7 @@ impl FilterChain { if let Some(options) = options { if options.clear_history { for framebuffer in &self.history_framebuffers { - framebuffer.clear() + framebuffer.clear::() } } } @@ -527,7 +442,7 @@ impl FilterChain { // do not need to rebind FBO 0 here since first `draw` will // bind automatically. - self.draw_quad.bind_vao(); + self.draw_quad.bind_vertices(); let filter = passes[0].config.filter; let wrap_mode = passes[0].config.wrap_mode; @@ -641,7 +556,7 @@ impl FilterChain { self.push_history(input)?; - self.draw_quad.unbind_vao(); + self.draw_quad.unbind_vertices(); Ok(()) } diff --git a/librashader-runtime-gl/src/filter_pass.rs b/librashader-runtime-gl/src/filter_pass.rs index bb3a995..eedbca9 100644 --- a/librashader-runtime-gl/src/filter_pass.rs +++ b/librashader-runtime-gl/src/filter_pass.rs @@ -14,6 +14,8 @@ use librashader_runtime::uniforms::UniformStorage; use crate::binding::{BufferStorage, GlUniformBinder, UniformLocation, VariableLocation}; use crate::filter_chain::FilterCommon; use crate::framebuffer::Viewport; +use crate::gl::gl3::{Gl3BindTexture, Gl3UboRing}; +use crate::gl::{BindTexture, Framebuffer, UboRing}; use crate::render_target::RenderTarget; use crate::samplers::SamplerSet; use crate::texture::Texture; @@ -25,7 +27,7 @@ pub struct FilterPass { pub compiled: ShaderCompilerOutput, pub program: GLuint, pub ubo_location: UniformLocation, - pub ubo_ring: Option>, + pub ubo_ring: Option>, pub(crate) uniform_storage: BufferStorage, pub uniform_bindings: FxHashMap, pub source: ShaderSource, @@ -34,7 +36,7 @@ pub struct FilterPass { impl FilterPass { // todo: fix rendertargets (i.e. non-final pass is internal, final pass is user provided fbo) - pub fn draw( + pub(crate) fn draw( &mut self, pass_index: usize, parent: &FilterCommon, @@ -68,36 +70,14 @@ impl FilterPass { && self.ubo_location.fragment != gl::INVALID_INDEX { if let (Some(ubo), Some(ring)) = (&self.reflection.ubo, &mut self.ubo_ring) { - let size = ubo.size; - let buffer = ring.current(); - - unsafe { - gl::BindBuffer(gl::UNIFORM_BUFFER, *buffer); - gl::BufferSubData( - gl::UNIFORM_BUFFER, - 0, - size as GLsizeiptr, - self.uniform_storage.ubo.as_ptr().cast(), - ); - gl::BindBuffer(gl::UNIFORM_BUFFER, 0); - - if self.ubo_location.vertex != gl::INVALID_INDEX { - gl::BindBufferBase(gl::UNIFORM_BUFFER, self.ubo_location.vertex, *buffer); - } - if self.ubo_location.fragment != gl::INVALID_INDEX { - gl::BindBufferBase(gl::UNIFORM_BUFFER, self.ubo_location.fragment, *buffer); - } - } - ring.next() + ring.bind_for_frame(ubo, &self.ubo_location, &self.uniform_storage) } } unsafe { // can't use framebuffer.clear because it will unbind. - gl::ColorMask(gl::TRUE, gl::TRUE, gl::TRUE, gl::TRUE); - gl::ClearColor(0.0f32, 0.0f32, 0.0f32, 0.0f32); - gl::Clear(gl::COLOR_BUFFER_BIT); - // + framebuffer.clear::(); + gl::Viewport( output.x, output.y, @@ -122,14 +102,7 @@ impl FilterPass { } fn bind_texture(samplers: &SamplerSet, binding: &TextureBinding, texture: &Texture) { - unsafe { - // eprintln!("setting {} to texunit {}", texture.image.handle, binding.binding); - gl::ActiveTexture(gl::TEXTURE0 + binding.binding); - - gl::BindTexture(gl::TEXTURE_2D, texture.image.handle); - gl::BindSampler(binding.binding, - samplers.get(texture.wrap_mode, texture.filter, texture.mip_filter)); - } + Gl3BindTexture::bind_texture(samplers, binding, texture) } } diff --git a/librashader-runtime-gl/src/framebuffer.rs b/librashader-runtime-gl/src/framebuffer.rs index 68abd8f..7a4bef5 100644 --- a/librashader-runtime-gl/src/framebuffer.rs +++ b/librashader-runtime-gl/src/framebuffer.rs @@ -5,347 +5,14 @@ use librashader_common::{FilterMode, ImageFormat, Size, WrapMode}; use librashader_presets::{Scale2D, ScaleType, Scaling}; use crate::error::FilterChainError; use crate::error::Result; - -#[derive(Debug)] -pub struct Framebuffer { - pub image: GLuint, - pub handle: GLuint, - pub size: Size, - pub format: GLenum, - pub max_levels: u32, - pub mip_levels: u32, - is_raw: bool, -} - -impl Framebuffer { - pub fn new(max_levels: u32) -> Framebuffer { - let mut framebuffer = 0; - unsafe { - gl::GenFramebuffers(1, &mut framebuffer); - gl::BindFramebuffer(gl::FRAMEBUFFER, framebuffer); - gl::BindFramebuffer(gl::FRAMEBUFFER, 0); - } - - Framebuffer { - image: 0, - size: Size { - width: 1, - height: 1, - }, - format: 0, - max_levels, - mip_levels: 0, - handle: framebuffer, - is_raw: false, - } - } - - pub fn new_from_raw( - texture: GLuint, - handle: GLuint, - format: GLenum, - size: Size, - miplevels: u32, - ) -> Framebuffer { - Framebuffer { - image: texture, - size, - format, - max_levels: miplevels, - mip_levels: miplevels, - handle, - is_raw: true, - } - } - - pub(crate) fn as_texture(&self, filter: FilterMode, wrap_mode: WrapMode) -> Texture { - Texture { - image: GlImage { - handle: self.image, - format: self.format, - size: self.size, - padded_size: Default::default(), - }, - filter, - mip_filter: filter, - wrap_mode, - } - } - - pub(crate) fn scale( - &mut self, - scaling: Scale2D, - format: ImageFormat, - viewport: &Viewport, - _original: &Texture, - source: &Texture, - ) -> Result> { - if self.is_raw { - return Ok(self.size); - } - - let width; - let height; - - match scaling.x { - Scaling { - scale_type: ScaleType::Input, - factor, - } => width = source.image.size.width * factor, - Scaling { - scale_type: ScaleType::Absolute, - factor, - } => width = factor.into(), - Scaling { - scale_type: ScaleType::Viewport, - factor, - } => width = viewport.output.size.width * factor, - }; - - match scaling.y { - Scaling { - scale_type: ScaleType::Input, - factor, - } => height = source.image.size.height * factor, - Scaling { - scale_type: ScaleType::Absolute, - factor, - } => height = factor.into(), - Scaling { - scale_type: ScaleType::Viewport, - factor, - } => height = viewport.output.size.height * factor, - }; - - let size = Size { - width: width.round() as u32, - height: height.round() as u32, - }; - - if self.size != size { - self.size = size; - - self.init( - size, - if format == ImageFormat::Unknown { - ImageFormat::R8G8B8A8Unorm - } else { - format - }, - )?; - } - Ok(size) - } - - 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); - } - } - - 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 { - self.init(image.size, image.format)?; - } - - unsafe { - gl::BindFramebuffer(gl::FRAMEBUFFER, self.handle); - - gl::FramebufferTexture2D( - gl::READ_FRAMEBUFFER, - gl::COLOR_ATTACHMENT0, - gl::TEXTURE_2D, - image.handle, - 0, - ); - - gl::FramebufferTexture2D( - gl::DRAW_FRAMEBUFFER, - gl::COLOR_ATTACHMENT1, - gl::TEXTURE_2D, - self.image, - 0, - ); - gl::ReadBuffer(gl::COLOR_ATTACHMENT0); - gl::DrawBuffer(gl::COLOR_ATTACHMENT1); - 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. - 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); - } - - Ok(()) - } - - pub(crate) fn init(&mut self, mut size: Size, format: impl Into) -> Result<()> { - if self.is_raw { - return Ok(()); - } - self.format = format.into(); - self.size = size; - - unsafe { - gl::BindFramebuffer(gl::FRAMEBUFFER, self.handle); - - // reset the framebuffer image - if self.image != 0 { - gl::FramebufferTexture2D( - gl::FRAMEBUFFER, - gl::COLOR_ATTACHMENT0, - gl::TEXTURE_2D, - 0, - 0, - ); - gl::DeleteTextures(1, &self.image); - } - - gl::GenTextures(1, &mut self.image); - gl::BindTexture(gl::TEXTURE_2D, self.image); - - if size.width == 0 { - size.width = 1; - } - if size.height == 0 { - size.height = 1; - } - - self.mip_levels = util::calc_miplevel(size); - if self.mip_levels > self.max_levels { - self.mip_levels = self.max_levels; - } - if self.mip_levels == 0 { - self.mip_levels = 1; - } - - gl::TexStorage2D( - gl::TEXTURE_2D, - self.mip_levels as GLsizei, - self.format, - size.width as GLsizei, - size.height as GLsizei, - ); - - gl::FramebufferTexture2D( - gl::FRAMEBUFFER, - gl::COLOR_ATTACHMENT0, - gl::TEXTURE_2D, - self.image, - 0, - ); - - let status = gl::CheckFramebufferStatus(gl::FRAMEBUFFER); - if status != gl::FRAMEBUFFER_COMPLETE { - match status { - gl::FRAMEBUFFER_UNSUPPORTED => { - eprintln!("unsupported fbo"); - - gl::FramebufferTexture2D( - gl::FRAMEBUFFER, - gl::COLOR_ATTACHMENT0, - gl::TEXTURE_2D, - 0, - 0, - ); - gl::DeleteTextures(1, &self.image); - gl::GenTextures(1, &mut self.image); - gl::BindTexture(gl::TEXTURE_2D, self.image); - - self.mip_levels = util::calc_miplevel(size); - if self.mip_levels > self.max_levels { - self.mip_levels = self.max_levels; - } - if self.mip_levels == 0 { - self.mip_levels = 1; - } - - gl::TexStorage2D( - gl::TEXTURE_2D, - self.mip_levels as GLsizei, - ImageFormat::R8G8B8A8Unorm.into(), - size.width as GLsizei, - size.height as GLsizei, - ); - gl::FramebufferTexture2D( - gl::FRAMEBUFFER, - gl::COLOR_ATTACHMENT0, - gl::TEXTURE_2D, - self.image, - 0, - ); - // self.init = - // gl::CheckFramebufferStatus(gl::FRAMEBUFFER) == gl::FRAMEBUFFER_COMPLETE; - } - _ => return Err(FilterChainError::FramebufferInit(status)) - } - } - - gl::BindFramebuffer(gl::FRAMEBUFFER, 0); - gl::BindTexture(gl::TEXTURE_2D, 0); - } - - Ok(()) - } -} - -impl Drop for Framebuffer { - fn drop(&mut self) { - unsafe { - if self.handle != 0 { - gl::DeleteFramebuffers(1, &self.handle); - } - if self.image != 0 { - gl::DeleteTextures(1, &self.image); - } - } - } -} +use crate::gl::Framebuffer; +use crate::gl::gl3::Gl3Framebuffer; #[derive(Debug, Copy, Clone)] pub struct Viewport<'a> { pub x: i32, pub y: i32, - pub output: &'a Framebuffer, + pub output: &'a Gl3Framebuffer, pub mvp: Option<&'a [f32; 16]>, } diff --git a/librashader-runtime-gl/src/quad_render.rs b/librashader-runtime-gl/src/gl/gl3/draw_quad.rs similarity index 90% rename from librashader-runtime-gl/src/quad_render.rs rename to librashader-runtime-gl/src/gl/gl3/draw_quad.rs index 0018ccc..63231ad 100644 --- a/librashader-runtime-gl/src/quad_render.rs +++ b/librashader-runtime-gl/src/gl/gl3/draw_quad.rs @@ -1,4 +1,5 @@ use gl::types::{GLsizei, GLsizeiptr, GLuint}; +use crate::gl::DrawQuad; #[rustfmt::skip] static QUAD_VBO_DATA: &[f32; 16] = &[ @@ -8,13 +9,13 @@ static QUAD_VBO_DATA: &[f32; 16] = &[ 1.0f32, 1.0f32, 1.0f32, 1.0f32, ]; -pub struct DrawQuad { +pub struct Gl3DrawQuad { vbo: GLuint, vao: GLuint, } -impl DrawQuad { - pub fn new() -> DrawQuad { +impl DrawQuad for Gl3DrawQuad { + fn new() -> Gl3DrawQuad { let mut vbo = 0; let mut vao = 0; @@ -31,11 +32,10 @@ impl DrawQuad { gl::GenVertexArrays(1, &mut vao); } - - DrawQuad { vbo, vao } + Self { vbo, vao } } - pub fn bind_vao(&self) { + fn bind_vertices(&self) { unsafe { gl::BindVertexArray(self.vao); gl::EnableVertexAttribArray(0); @@ -65,7 +65,7 @@ impl DrawQuad { } } - pub fn unbind_vao(&self) { + fn unbind_vertices(&self) { unsafe { gl::BindVertexArray(0); gl::BindBuffer(gl::ARRAY_BUFFER, 0); @@ -74,3 +74,4 @@ impl DrawQuad { } } } + diff --git a/librashader-runtime-gl/src/gl/gl3/framebuffer.rs b/librashader-runtime-gl/src/gl/gl3/framebuffer.rs new file mode 100644 index 0000000..9ed1ef1 --- /dev/null +++ b/librashader-runtime-gl/src/gl/gl3/framebuffer.rs @@ -0,0 +1,305 @@ +use gl::types::{GLenum, GLint, GLsizei, GLuint}; +use librashader_common::{FilterMode, ImageFormat, Size, WrapMode}; +use librashader_presets::Scale2D; +use crate::{GlImage, Viewport}; +use crate::error::{FilterChainError, Result}; +use crate::gl::Framebuffer; +use crate::texture::Texture; + +#[derive(Debug)] +pub struct Gl3Framebuffer { + pub image: GLuint, + pub handle: GLuint, + pub size: Size, + pub format: GLenum, + pub max_levels: u32, + pub mip_levels: u32, + is_raw: bool, +} + + +impl Framebuffer for Gl3Framebuffer { + fn new(max_levels: u32) -> Gl3Framebuffer { + let mut framebuffer = 0; + unsafe { + gl::GenFramebuffers(1, &mut framebuffer); + gl::BindFramebuffer(gl::FRAMEBUFFER, framebuffer); + gl::BindFramebuffer(gl::FRAMEBUFFER, 0); + } + + Gl3Framebuffer { + image: 0, + size: Size { + width: 1, + height: 1, + }, + format: 0, + max_levels, + mip_levels: 0, + handle: framebuffer, + is_raw: false, + } + } + fn new_from_raw( + texture: GLuint, + handle: GLuint, + format: GLenum, + size: Size, + miplevels: u32, + ) -> Gl3Framebuffer { + Gl3Framebuffer { + image: texture, + size, + format, + max_levels: miplevels, + mip_levels: miplevels, + handle, + is_raw: true, + } + } + fn as_texture(&self, filter: FilterMode, wrap_mode: WrapMode) -> Texture { + Texture { + image: GlImage { + handle: self.image, + format: self.format, + size: self.size, + padded_size: Default::default(), + }, + filter, + mip_filter: filter, + wrap_mode, + } + } + fn scale( + &mut self, + scaling: Scale2D, + format: ImageFormat, + viewport: &Viewport, + _original: &Texture, + source: &Texture, + ) -> Result> { + if self.is_raw { + return Ok(self.size); + } + + let size = librashader_runtime::scaling::scale(scaling, source.image.size, viewport.output.size); + + if self.size != size { + self.size = size; + + self.init( + size, + if format == ImageFormat::Unknown { + ImageFormat::R8G8B8A8Unorm + } else { + format + }, + )?; + } + Ok(size) + } + fn clear(&self) { + unsafe { + if REBIND { + 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); + if REBIND { + gl::BindFramebuffer(gl::FRAMEBUFFER, 0); + } + } + } + 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 { + self.init(image.size, image.format)?; + } + + unsafe { + gl::BindFramebuffer(gl::FRAMEBUFFER, self.handle); + + gl::FramebufferTexture2D( + gl::READ_FRAMEBUFFER, + gl::COLOR_ATTACHMENT0, + gl::TEXTURE_2D, + image.handle, + 0, + ); + + gl::FramebufferTexture2D( + gl::DRAW_FRAMEBUFFER, + gl::COLOR_ATTACHMENT1, + gl::TEXTURE_2D, + self.image, + 0, + ); + gl::ReadBuffer(gl::COLOR_ATTACHMENT0); + gl::DrawBuffer(gl::COLOR_ATTACHMENT1); + 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. + 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); + } + + Ok(()) + } + fn init(&mut self, mut size: Size, format: impl Into) -> Result<()> { + if self.is_raw { + return Ok(()); + } + self.format = format.into(); + self.size = size; + + unsafe { + gl::BindFramebuffer(gl::FRAMEBUFFER, self.handle); + + // reset the framebuffer image + if self.image != 0 { + gl::FramebufferTexture2D( + gl::FRAMEBUFFER, + gl::COLOR_ATTACHMENT0, + gl::TEXTURE_2D, + 0, + 0, + ); + gl::DeleteTextures(1, &self.image); + } + + gl::GenTextures(1, &mut self.image); + gl::BindTexture(gl::TEXTURE_2D, self.image); + + if size.width == 0 { + size.width = 1; + } + if size.height == 0 { + size.height = 1; + } + + self.mip_levels = librashader_runtime::scaling::calc_miplevel(size); + if self.mip_levels > self.max_levels { + self.mip_levels = self.max_levels; + } + if self.mip_levels == 0 { + self.mip_levels = 1; + } + + gl::TexStorage2D( + gl::TEXTURE_2D, + self.mip_levels as GLsizei, + self.format, + size.width as GLsizei, + size.height as GLsizei, + ); + + gl::FramebufferTexture2D( + gl::FRAMEBUFFER, + gl::COLOR_ATTACHMENT0, + gl::TEXTURE_2D, + self.image, + 0, + ); + + let status = gl::CheckFramebufferStatus(gl::FRAMEBUFFER); + if status != gl::FRAMEBUFFER_COMPLETE { + match status { + gl::FRAMEBUFFER_UNSUPPORTED => { + eprintln!("unsupported fbo"); + + gl::FramebufferTexture2D( + gl::FRAMEBUFFER, + gl::COLOR_ATTACHMENT0, + gl::TEXTURE_2D, + 0, + 0, + ); + gl::DeleteTextures(1, &self.image); + gl::GenTextures(1, &mut self.image); + gl::BindTexture(gl::TEXTURE_2D, self.image); + + self.mip_levels = librashader_runtime::scaling::calc_miplevel(size); + if self.mip_levels > self.max_levels { + self.mip_levels = self.max_levels; + } + if self.mip_levels == 0 { + self.mip_levels = 1; + } + + gl::TexStorage2D( + gl::TEXTURE_2D, + self.mip_levels as GLsizei, + ImageFormat::R8G8B8A8Unorm.into(), + size.width as GLsizei, + size.height as GLsizei, + ); + gl::FramebufferTexture2D( + gl::FRAMEBUFFER, + gl::COLOR_ATTACHMENT0, + gl::TEXTURE_2D, + self.image, + 0, + ); + // self.init = + // gl::CheckFramebufferStatus(gl::FRAMEBUFFER) == gl::FRAMEBUFFER_COMPLETE; + } + _ => return Err(FilterChainError::FramebufferInit(status)) + } + } + + gl::BindFramebuffer(gl::FRAMEBUFFER, 0); + gl::BindTexture(gl::TEXTURE_2D, 0); + } + + Ok(()) + } +} + +impl Drop for Gl3Framebuffer { + fn drop(&mut self) { + unsafe { + if self.handle != 0 { + gl::DeleteFramebuffers(1, &self.handle); + } + if self.image != 0 { + gl::DeleteTextures(1, &self.image); + } + } + } +} \ No newline at end of file diff --git a/librashader-runtime-gl/src/gl/gl3/lut_load.rs b/librashader-runtime-gl/src/gl/gl3/lut_load.rs new file mode 100644 index 0000000..369890c --- /dev/null +++ b/librashader-runtime-gl/src/gl/gl3/lut_load.rs @@ -0,0 +1,85 @@ +use gl::types::{GLsizei, GLuint}; +use rustc_hash::FxHashMap; +use librashader_common::image::Image; +use librashader_common::Size; +use librashader_presets::TextureConfig; +use crate::gl::LoadLut; +use crate::{GlImage, util}; +use crate::texture::Texture; +use crate::error::Result; + +pub struct Gl3LutLoad; +impl LoadLut for Gl3LutLoad { + fn load_luts(textures: &[TextureConfig]) -> Result> { + let mut luts = FxHashMap::default(); + let pixel_unpack = unsafe { + let mut binding = 0; + gl::GetIntegerv(gl::PIXEL_UNPACK_BUFFER_BINDING, &mut binding); + binding + }; + + for (index, texture) in textures.iter().enumerate() { + let image = Image::load(&texture.path)?; + let levels = if texture.mipmap { + librashader_runtime::scaling::calc_miplevel(image.size) + } else { + 1u32 + }; + + let mut handle = 0; + unsafe { + gl::GenTextures(1, &mut handle); + gl::BindTexture(gl::TEXTURE_2D, handle); + gl::TexStorage2D( + gl::TEXTURE_2D, + levels as GLsizei, + gl::RGBA8, + image.size.width as GLsizei, + image.size.height as GLsizei, + ); + + gl::PixelStorei(gl::UNPACK_ROW_LENGTH, 0); + gl::PixelStorei(gl::UNPACK_ALIGNMENT, 4); + gl::BindBuffer(gl::PIXEL_UNPACK_BUFFER, 0); + gl::TexSubImage2D( + gl::TEXTURE_2D, + 0, + 0, + 0, + image.size.width as GLsizei, + image.size.height as GLsizei, + gl::RGBA, + gl::UNSIGNED_BYTE, + image.bytes.as_ptr().cast(), + ); + + let mipmap = levels > 1; + if mipmap { + gl::GenerateMipmap(gl::TEXTURE_2D); + } + + gl::BindTexture(gl::TEXTURE_2D, 0); + } + + luts.insert( + index, + Texture { + image: GlImage { + handle, + format: gl::RGBA8, + size: image.size, + padded_size: Size::default(), + }, + filter: texture.filter_mode, + mip_filter: texture.filter_mode, + wrap_mode: texture.wrap_mode, + }, + ); + } + + unsafe { + gl::BindBuffer(gl::PIXEL_UNPACK_BUFFER, pixel_unpack as GLuint); + }; + Ok(luts) + } +} \ No newline at end of file diff --git a/librashader-runtime-gl/src/gl/gl3/mod.rs b/librashader-runtime-gl/src/gl/gl3/mod.rs new file mode 100644 index 0000000..34b59ec --- /dev/null +++ b/librashader-runtime-gl/src/gl/gl3/mod.rs @@ -0,0 +1,11 @@ +mod lut_load; +mod draw_quad; +mod ubo_ring; +mod framebuffer; +mod texture_bind; + +pub use lut_load::*; +pub use draw_quad::*; +pub use ubo_ring::*; +pub use framebuffer::*; +pub use texture_bind::*; \ No newline at end of file diff --git a/librashader-runtime-gl/src/gl/gl3/texture_bind.rs b/librashader-runtime-gl/src/gl/gl3/texture_bind.rs new file mode 100644 index 0000000..2afee51 --- /dev/null +++ b/librashader-runtime-gl/src/gl/gl3/texture_bind.rs @@ -0,0 +1,19 @@ +use librashader_reflect::reflect::semantics::TextureBinding; +use crate::gl::BindTexture; +use crate::samplers::SamplerSet; +use crate::texture::Texture; + +pub struct Gl3BindTexture; + +impl BindTexture for Gl3BindTexture { + fn bind_texture(samplers: &SamplerSet, binding: &TextureBinding, texture: &Texture) { + unsafe { + // eprintln!("setting {} to texunit {}", texture.image.handle, binding.binding); + gl::ActiveTexture(gl::TEXTURE0 + binding.binding); + + gl::BindTexture(gl::TEXTURE_2D, texture.image.handle); + gl::BindSampler(binding.binding, + samplers.get(texture.wrap_mode, texture.filter, texture.mip_filter)); + } + } +} \ No newline at end of file diff --git a/librashader-runtime-gl/src/gl/gl3/ubo_ring.rs b/librashader-runtime-gl/src/gl/gl3/ubo_ring.rs new file mode 100644 index 0000000..c86e0d4 --- /dev/null +++ b/librashader-runtime-gl/src/gl/gl3/ubo_ring.rs @@ -0,0 +1,59 @@ +use gl::types::{GLsizei, GLsizeiptr, GLuint}; +use librashader_reflect::reflect::semantics::UboReflection; +use librashader_runtime::uniforms::{UniformStorage, UniformStorageAccess}; +use crate::binding::UniformLocation; +use crate::gl::UboRing; +use crate::util::{InlineRingBuffer, RingBuffer}; + +pub struct Gl3UboRing { + ring: InlineRingBuffer +} + + +impl UboRing for Gl3UboRing { + fn new(buffer_size: u32) -> Self { + let mut ring: InlineRingBuffer = InlineRingBuffer::new(); + unsafe { + gl::GenBuffers(SIZE as GLsizei, ring.items_mut().as_mut_ptr()); + for buffer in ring.items() { + gl::BindBuffer(gl::UNIFORM_BUFFER, *buffer); + gl::BufferData( + gl::UNIFORM_BUFFER, + buffer_size as GLsizeiptr, + std::ptr::null(), + gl::STREAM_DRAW, + ); + } + gl::BindBuffer(gl::UNIFORM_BUFFER, 0); + } + Gl3UboRing { + ring + } + } + + fn bind_for_frame(&mut self, ubo: &UboReflection, ubo_location: &UniformLocation, storage: &impl UniformStorageAccess) { + let size = ubo.size; + let buffer = self.ring.current(); + + unsafe { + gl::BindBuffer(gl::UNIFORM_BUFFER, *buffer); + gl::BufferSubData( + gl::UNIFORM_BUFFER, + 0, + size as GLsizeiptr, + storage.ubo_pointer().cast(), + ); + gl::BindBuffer(gl::UNIFORM_BUFFER, 0); + + if ubo_location.vertex != gl::INVALID_INDEX { + gl::BindBufferBase(gl::UNIFORM_BUFFER, ubo_location.vertex, *buffer); + } + if ubo_location.fragment != gl::INVALID_INDEX { + gl::BindBufferBase(gl::UNIFORM_BUFFER, ubo_location.fragment, *buffer); + } + } + self.ring.next() + } +} + + diff --git a/librashader-runtime-gl/src/gl/gl46/draw_quad.rs b/librashader-runtime-gl/src/gl/gl46/draw_quad.rs new file mode 100644 index 0000000..08d233c --- /dev/null +++ b/librashader-runtime-gl/src/gl/gl46/draw_quad.rs @@ -0,0 +1,64 @@ +use gl::types::{GLint, GLsizei, GLsizeiptr, GLuint}; +use crate::gl::DrawQuad; + +#[rustfmt::skip] +static QUAD_VBO_DATA: &[f32; 16] = &[ + 0.0f32, 0.0f32, 0.0f32, 0.0f32, + 1.0f32, 0.0f32, 1.0f32, 0.0f32, + 0.0f32, 1.0f32, 0.0f32, 1.0f32, + 1.0f32, 1.0f32, 1.0f32, 1.0f32, +]; + +pub struct Gl46DrawQuad { + vbo: GLuint, + vao: GLuint, +} + +impl DrawQuad for Gl46DrawQuad { + fn new() -> Self { + let mut vbo = 0; + let mut vao = 0; + + unsafe { + gl::CreateBuffers(1, &mut vbo); + gl::NamedBufferData( + vbo, + std::mem::size_of_val(QUAD_VBO_DATA) as GLsizeiptr, + QUAD_VBO_DATA.as_ptr().cast(), + gl::STATIC_DRAW, + ); + gl::CreateVertexArrays(1, &mut vao); + + gl::EnableVertexArrayAttrib(vao, 0); + gl::EnableVertexArrayAttrib(vao, 1); + + gl::VertexArrayVertexBuffer(vao, 0, + vbo, 0, 4 * std::mem::size_of::() as GLint + ); + + gl::VertexArrayAttribFormat(vao, 0, 2, + gl::FLOAT, gl::FALSE, 0); + gl::VertexArrayAttribFormat(vao, 1, 2, + gl::FLOAT, gl::FALSE, + 2 * std::mem::size_of::() as GLuint); + + gl::VertexArrayAttribBinding(vao, 0, 0); + gl::VertexArrayAttribBinding(vao, 1, 0); + + } + + Self { vbo, vao } + } + + fn bind_vertices(&self) { + unsafe { + gl::BindVertexArray(self.vao); + } + } + + fn unbind_vertices(&self) { + unsafe { + gl::BindVertexArray(0); + } + } +} diff --git a/librashader-runtime-gl/src/gl/gl46/framebuffer.rs b/librashader-runtime-gl/src/gl/gl46/framebuffer.rs new file mode 100644 index 0000000..6c88bb7 --- /dev/null +++ b/librashader-runtime-gl/src/gl/gl46/framebuffer.rs @@ -0,0 +1,305 @@ +use gl::types::{GLenum, GLint, GLsizei, GLuint}; +use librashader_common::{FilterMode, ImageFormat, Size, WrapMode}; +use librashader_presets::Scale2D; +use crate::{GlImage, Viewport}; +use crate::error::{FilterChainError, Result}; +use crate::gl::Framebuffer; +use crate::texture::Texture; + +#[derive(Debug)] +pub struct Gl46Framebuffer { + pub image: GLuint, + pub handle: GLuint, + pub size: Size, + pub format: GLenum, + pub max_levels: u32, + pub mip_levels: u32, + is_raw: bool, +} + + +impl Framebuffer for Gl46Framebuffer { + fn new(max_levels: u32) -> Gl46Framebuffer { + let mut framebuffer = 0; + unsafe { + gl::GenFramebuffers(1, &mut framebuffer); + gl::BindFramebuffer(gl::FRAMEBUFFER, framebuffer); + gl::BindFramebuffer(gl::FRAMEBUFFER, 0); + } + + Gl46Framebuffer { + image: 0, + size: Size { + width: 1, + height: 1, + }, + format: 0, + max_levels, + mip_levels: 0, + handle: framebuffer, + is_raw: false, + } + } + fn new_from_raw( + texture: GLuint, + handle: GLuint, + format: GLenum, + size: Size, + miplevels: u32, + ) -> Gl46Framebuffer { + Gl46Framebuffer { + image: texture, + size, + format, + max_levels: miplevels, + mip_levels: miplevels, + handle, + is_raw: true, + } + } + fn as_texture(&self, filter: FilterMode, wrap_mode: WrapMode) -> Texture { + Texture { + image: GlImage { + handle: self.image, + format: self.format, + size: self.size, + padded_size: Default::default(), + }, + filter, + mip_filter: filter, + wrap_mode, + } + } + fn scale( + &mut self, + scaling: Scale2D, + format: ImageFormat, + viewport: &Viewport, + _original: &Texture, + source: &Texture, + ) -> Result> { + if self.is_raw { + return Ok(self.size); + } + + let size = librashader_runtime::scaling::scale(scaling, source.image.size, viewport.output.size); + + if self.size != size { + self.size = size; + + self.init( + size, + if format == ImageFormat::Unknown { + ImageFormat::R8G8B8A8Unorm + } else { + format + }, + )?; + } + Ok(size) + } + fn clear(&self) { + unsafe { + if REBIND { + 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); + if REBIND { + gl::BindFramebuffer(gl::FRAMEBUFFER, 0); + } + } + } + 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 { + self.init(image.size, image.format)?; + } + + unsafe { + gl::BindFramebuffer(gl::FRAMEBUFFER, self.handle); + + gl::FramebufferTexture2D( + gl::READ_FRAMEBUFFER, + gl::COLOR_ATTACHMENT0, + gl::TEXTURE_2D, + image.handle, + 0, + ); + + gl::FramebufferTexture2D( + gl::DRAW_FRAMEBUFFER, + gl::COLOR_ATTACHMENT1, + gl::TEXTURE_2D, + self.image, + 0, + ); + gl::ReadBuffer(gl::COLOR_ATTACHMENT0); + gl::DrawBuffer(gl::COLOR_ATTACHMENT1); + 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. + 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); + } + + Ok(()) + } + fn init(&mut self, mut size: Size, format: impl Into) -> Result<()> { + if self.is_raw { + return Ok(()); + } + self.format = format.into(); + self.size = size; + + unsafe { + gl::BindFramebuffer(gl::FRAMEBUFFER, self.handle); + + // reset the framebuffer image + if self.image != 0 { + gl::FramebufferTexture2D( + gl::FRAMEBUFFER, + gl::COLOR_ATTACHMENT0, + gl::TEXTURE_2D, + 0, + 0, + ); + gl::DeleteTextures(1, &self.image); + } + + gl::GenTextures(1, &mut self.image); + gl::BindTexture(gl::TEXTURE_2D, self.image); + + if size.width == 0 { + size.width = 1; + } + if size.height == 0 { + size.height = 1; + } + + self.mip_levels = librashader_runtime::scaling::calc_miplevel(size); + if self.mip_levels > self.max_levels { + self.mip_levels = self.max_levels; + } + if self.mip_levels == 0 { + self.mip_levels = 1; + } + + gl::TexStorage2D( + gl::TEXTURE_2D, + self.mip_levels as GLsizei, + self.format, + size.width as GLsizei, + size.height as GLsizei, + ); + + gl::FramebufferTexture2D( + gl::FRAMEBUFFER, + gl::COLOR_ATTACHMENT0, + gl::TEXTURE_2D, + self.image, + 0, + ); + + let status = gl::CheckFramebufferStatus(gl::FRAMEBUFFER); + if status != gl::FRAMEBUFFER_COMPLETE { + match status { + gl::FRAMEBUFFER_UNSUPPORTED => { + eprintln!("unsupported fbo"); + + gl::FramebufferTexture2D( + gl::FRAMEBUFFER, + gl::COLOR_ATTACHMENT0, + gl::TEXTURE_2D, + 0, + 0, + ); + gl::DeleteTextures(1, &self.image); + gl::GenTextures(1, &mut self.image); + gl::BindTexture(gl::TEXTURE_2D, self.image); + + self.mip_levels = librashader_runtime::scaling::calc_miplevel(size); + if self.mip_levels > self.max_levels { + self.mip_levels = self.max_levels; + } + if self.mip_levels == 0 { + self.mip_levels = 1; + } + + gl::TexStorage2D( + gl::TEXTURE_2D, + self.mip_levels as GLsizei, + ImageFormat::R8G8B8A8Unorm.into(), + size.width as GLsizei, + size.height as GLsizei, + ); + gl::FramebufferTexture2D( + gl::FRAMEBUFFER, + gl::COLOR_ATTACHMENT0, + gl::TEXTURE_2D, + self.image, + 0, + ); + // self.init = + // gl::CheckFramebufferStatus(gl::FRAMEBUFFER) == gl::FRAMEBUFFER_COMPLETE; + } + _ => return Err(FilterChainError::FramebufferInit(status)) + } + } + + gl::BindFramebuffer(gl::FRAMEBUFFER, 0); + gl::BindTexture(gl::TEXTURE_2D, 0); + } + + Ok(()) + } +} + +impl Drop for Gl46Framebuffer { + fn drop(&mut self) { + unsafe { + if self.handle != 0 { + gl::DeleteFramebuffers(1, &self.handle); + } + if self.image != 0 { + gl::DeleteTextures(1, &self.image); + } + } + } +} \ No newline at end of file diff --git a/librashader-runtime-gl/src/gl/gl46/lut_load.rs b/librashader-runtime-gl/src/gl/gl46/lut_load.rs new file mode 100644 index 0000000..5911503 --- /dev/null +++ b/librashader-runtime-gl/src/gl/gl46/lut_load.rs @@ -0,0 +1,85 @@ +use gl::types::{GLsizei, GLuint}; +use rustc_hash::FxHashMap; +use librashader_common::image::Image; +use librashader_common::Size; +use librashader_presets::TextureConfig; +use crate::gl::LoadLut; +use crate::{GlImage, util}; +use crate::texture::Texture; +use crate::error::Result; + +pub struct Gl46LutLoad; +impl LoadLut for Gl46LutLoad { + fn load_luts(textures: &[TextureConfig]) -> Result> { + let mut luts = FxHashMap::default(); + let pixel_unpack = unsafe { + let mut binding = 0; + gl::GetIntegerv(gl::PIXEL_UNPACK_BUFFER_BINDING, &mut binding); + binding + }; + + unsafe { + gl::BindBuffer(gl::PIXEL_UNPACK_BUFFER, 0); + } + + for (index, texture) in textures.iter().enumerate() { + let image = Image::load(&texture.path)?; + let levels = if texture.mipmap { + librashader_runtime::scaling::calc_miplevel(image.size) + } else { + 1u32 + }; + + let mut handle = 0; + unsafe { + gl::CreateTextures(gl::TEXTURE_2D,1, &mut handle); + + gl::TextureStorage2D( + handle, + levels as GLsizei, + gl::RGBA8, + image.size.width as GLsizei, + image.size.height as GLsizei, + ); + + gl::PixelStorei(gl::UNPACK_ROW_LENGTH, 0); + gl::PixelStorei(gl::UNPACK_ALIGNMENT, 4); + + gl::TextureSubImage2D( + handle, + 0, 0, 0, + image.size.width as GLsizei, + image.size.height as GLsizei, + gl::RGBA, + gl::UNSIGNED_BYTE, + image.bytes.as_ptr().cast(), + ); + + let mipmap = levels > 1; + if mipmap { + gl::GenerateTextureMipmap(handle); + } + } + + luts.insert( + index, + Texture { + image: GlImage { + handle, + format: gl::RGBA8, + size: image.size, + padded_size: Size::default(), + }, + filter: texture.filter_mode, + mip_filter: texture.filter_mode, + wrap_mode: texture.wrap_mode, + }, + ); + } + + unsafe { + gl::BindBuffer(gl::PIXEL_UNPACK_BUFFER, pixel_unpack as GLuint); + }; + Ok(luts) + } +} \ No newline at end of file diff --git a/librashader-runtime-gl/src/gl/gl46/mod.rs b/librashader-runtime-gl/src/gl/gl46/mod.rs new file mode 100644 index 0000000..34b59ec --- /dev/null +++ b/librashader-runtime-gl/src/gl/gl46/mod.rs @@ -0,0 +1,11 @@ +mod lut_load; +mod draw_quad; +mod ubo_ring; +mod framebuffer; +mod texture_bind; + +pub use lut_load::*; +pub use draw_quad::*; +pub use ubo_ring::*; +pub use framebuffer::*; +pub use texture_bind::*; \ No newline at end of file diff --git a/librashader-runtime-gl/src/gl/gl46/texture_bind.rs b/librashader-runtime-gl/src/gl/gl46/texture_bind.rs new file mode 100644 index 0000000..89adae3 --- /dev/null +++ b/librashader-runtime-gl/src/gl/gl46/texture_bind.rs @@ -0,0 +1,17 @@ +use librashader_reflect::reflect::semantics::TextureBinding; +use crate::gl::BindTexture; +use crate::samplers::SamplerSet; +use crate::texture::Texture; + +pub struct Gl46BindTexture; + +impl BindTexture for Gl46BindTexture { + fn bind_texture(samplers: &SamplerSet, binding: &TextureBinding, texture: &Texture) { + unsafe { + // eprintln!("setting {} to texunit {}", texture.image.handle, binding.binding); + gl::BindTextureUnit(binding.binding, texture.image.handle); + gl::BindSampler(binding.binding, + samplers.get(texture.wrap_mode, texture.filter, texture.mip_filter)); + } + } +} \ No newline at end of file diff --git a/librashader-runtime-gl/src/gl/gl46/ubo_ring.rs b/librashader-runtime-gl/src/gl/gl46/ubo_ring.rs new file mode 100644 index 0000000..049a1af --- /dev/null +++ b/librashader-runtime-gl/src/gl/gl46/ubo_ring.rs @@ -0,0 +1,56 @@ +use gl::types::{GLsizei, GLsizeiptr, GLuint}; +use librashader_reflect::reflect::semantics::UboReflection; +use librashader_runtime::uniforms::{UniformStorage, UniformStorageAccess}; +use crate::binding::UniformLocation; +use crate::gl::UboRing; +use crate::util::{InlineRingBuffer, RingBuffer}; + +pub struct Gl46UboRing { + ring: InlineRingBuffer +} + + +impl UboRing for Gl46UboRing { + fn new(buffer_size: u32) -> Self { + let mut ring: InlineRingBuffer = InlineRingBuffer::new(); + unsafe { + gl::CreateBuffers(SIZE as GLsizei, ring.items_mut().as_mut_ptr()); + for buffer in ring.items() { + gl::NamedBufferData( + *buffer, + buffer_size as GLsizeiptr, + std::ptr::null(), + gl::STREAM_DRAW, + ); + } + }; + + Gl46UboRing { + ring + } + } + + fn bind_for_frame(&mut self, ubo: &UboReflection, ubo_location: &UniformLocation, storage: &impl UniformStorageAccess) { + let size = ubo.size; + let buffer = self.ring.current(); + + unsafe { + gl::NamedBufferSubData( + *buffer, + 0, + size as GLsizeiptr, + storage.ubo_pointer().cast(), + ); + + if ubo_location.vertex != gl::INVALID_INDEX { + gl::BindBufferBase(gl::UNIFORM_BUFFER, ubo_location.vertex, *buffer); + } + if ubo_location.fragment != gl::INVALID_INDEX { + gl::BindBufferBase(gl::UNIFORM_BUFFER, ubo_location.fragment, *buffer); + } + } + self.ring.next() + } +} + + diff --git a/librashader-runtime-gl/src/gl/mod.rs b/librashader-runtime-gl/src/gl/mod.rs new file mode 100644 index 0000000..5712bd9 --- /dev/null +++ b/librashader-runtime-gl/src/gl/mod.rs @@ -0,0 +1,56 @@ +pub(crate) mod gl3; +mod gl46; + +use gl::types::{GLenum, GLint, GLsizei, GLuint}; +use rustc_hash::FxHashMap; +use librashader_common::{FilterMode, ImageFormat, Size, WrapMode}; +use librashader_presets::{Scale2D, TextureConfig}; +use librashader_reflect::reflect::semantics::{TextureBinding, UboReflection}; +use librashader_runtime::uniforms::{UniformStorage, UniformStorageAccess}; +use crate::binding::UniformLocation; +use crate::texture::Texture; +use crate::error::{FilterChainError, Result}; +use crate::{GlImage, Viewport}; +use crate::samplers::SamplerSet; + +pub trait LoadLut { + fn load_luts(textures: &[TextureConfig]) -> Result>; +} + +pub trait DrawQuad { + fn new() -> Self; + fn bind_vertices(&self); + fn unbind_vertices(&self); +} + +pub trait UboRing { + fn new(buffer_size: u32) -> Self; + fn bind_for_frame(&mut self, ubo: &UboReflection, ubo_location: &UniformLocation, storage: &impl UniformStorageAccess); +} + +pub trait Framebuffer { + fn new(max_levels: u32) -> Self; + fn new_from_raw( + texture: GLuint, + handle: GLuint, + format: GLenum, + size: Size, + miplevels: u32, + ) -> Self; + fn as_texture(&self, filter: FilterMode, wrap_mode: WrapMode) -> Texture; + fn scale( + &mut self, + scaling: Scale2D, + format: ImageFormat, + viewport: &Viewport, + _original: &Texture, + source: &Texture, + ) -> Result>; + fn clear(&self); + fn copy_from(&mut self, image: &GlImage) -> Result<()>; + fn init(&mut self, size: Size, format: impl Into) -> Result<()>; +} + +pub trait BindTexture { + fn bind_texture(samplers: &SamplerSet, binding: &TextureBinding, texture: &Texture); +} \ No newline at end of file diff --git a/librashader-runtime-gl/src/hello_triangle.rs b/librashader-runtime-gl/src/gl3_hello_triangle.rs similarity index 99% rename from librashader-runtime-gl/src/hello_triangle.rs rename to librashader-runtime-gl/src/gl3_hello_triangle.rs index 36a59e8..3041127 100644 --- a/librashader-runtime-gl/src/hello_triangle.rs +++ b/librashader-runtime-gl/src/gl3_hello_triangle.rs @@ -8,7 +8,9 @@ use gl::types::{GLchar, GLenum, GLint, GLsizei, GLuint}; use librashader_common::Size; use crate::filter_chain::FilterChain; -use crate::framebuffer::{Framebuffer, GlImage, Viewport}; +use crate::framebuffer::{GlImage, Viewport}; +use crate::gl::Framebuffer; +use crate::gl::gl3::Gl3Framebuffer; const WIDTH: u32 = 900; const HEIGHT: u32 = 700; @@ -462,7 +464,7 @@ void main() let (fb_width, fb_height) = window.get_framebuffer_size(); let (vp_width, vp_height) = window.get_size(); - let output = Framebuffer::new_from_raw( + let output = Gl3Framebuffer::new_from_raw( output_texture, output_framebuffer_handle, gl::RGBA8, diff --git a/librashader-runtime-gl/src/lib.rs b/librashader-runtime-gl/src/lib.rs index 4532369..3b1d76f 100644 --- a/librashader-runtime-gl/src/lib.rs +++ b/librashader-runtime-gl/src/lib.rs @@ -5,7 +5,6 @@ mod binding; mod filter_chain; mod filter_pass; mod framebuffer; -mod quad_render; mod render_target; mod util; pub mod error; @@ -13,14 +12,14 @@ pub mod error; mod samplers; pub use filter_chain::FilterChain; -pub use framebuffer::Framebuffer; pub use framebuffer::GlImage; pub use framebuffer::Viewport; #[cfg(test)] -mod hello_triangle; +mod gl3_hello_triangle; mod texture; mod options; +mod gl; #[cfg(test)] mod tests { @@ -29,11 +28,11 @@ mod tests { #[test] fn triangle_gl() { - let (glfw, window, events, shader, vao) = hello_triangle::setup(); + let (glfw, window, events, shader, vao) = gl3_hello_triangle::setup(); let mut filter = -// FilterChain::load_from_path("../test/slang-shaders/vhs/VHSPro.slangp", None) - FilterChain::load_from_path("../test/slang-shaders/bezel/Mega_Bezel/Presets/MBZ__0__SMOOTH-ADV.slangp", None) + FilterChain::load_from_path("../test/slang-shaders/vhs/VHSPro.slangp", None) + // FilterChain::load_from_path("../test/slang-shaders/bezel/Mega_Bezel/Presets/MBZ__0__SMOOTH-ADV.slangp", None) .unwrap(); - hello_triangle::do_loop(glfw, window, events, shader, vao, &mut filter); + gl3_hello_triangle::do_loop(glfw, window, events, shader, vao, &mut filter); } } diff --git a/librashader-runtime-gl/src/render_target.rs b/librashader-runtime-gl/src/render_target.rs index 44e2d5c..e275fe6 100644 --- a/librashader-runtime-gl/src/render_target.rs +++ b/librashader-runtime-gl/src/render_target.rs @@ -1,4 +1,6 @@ -use crate::framebuffer::{Framebuffer, Viewport}; +use crate::framebuffer::Viewport; +use crate::gl::Framebuffer; +use crate::gl::gl3::Gl3Framebuffer; #[rustfmt::skip] static DEFAULT_MVP: &[f32; 16] = &[ @@ -9,15 +11,15 @@ static DEFAULT_MVP: &[f32; 16] = &[ ]; #[derive(Debug, Copy, Clone)] -pub struct RenderTarget<'a> { +pub(crate) struct RenderTarget<'a> { pub mvp: &'a [f32; 16], - pub framebuffer: &'a Framebuffer, + pub framebuffer: &'a Gl3Framebuffer, pub x: i32, pub y: i32 } impl<'a> RenderTarget<'a> { - pub fn new(backbuffer: &'a Framebuffer, mvp: Option<&'a [f32; 16]>, x: i32, y: i32) -> Self { + pub fn new(backbuffer: &'a Gl3Framebuffer, mvp: Option<&'a [f32; 16]>, x: i32, y: i32) -> Self { if let Some(mvp) = mvp { RenderTarget { framebuffer: backbuffer, diff --git a/librashader-runtime-gl/src/util.rs b/librashader-runtime-gl/src/util.rs index 75cb29d..9498205 100644 --- a/librashader-runtime-gl/src/util.rs +++ b/librashader-runtime-gl/src/util.rs @@ -5,17 +5,6 @@ use librashader_reflect::reflect::semantics::BindingStage; use librashader_runtime::uniforms::{BindUniform, UniformStorage, UniformScalar}; use crate::binding::UniformLocation; -pub fn calc_miplevel(size: Size) -> u32 { - let mut size = std::cmp::max(size.width, size.height); - let mut levels = 0; - while size != 0 { - levels += 1; - size >>= 1; - } - - levels -} - pub trait RingBuffer { fn current(&self) -> &T; fn current_mut(&mut self) -> &mut T; diff --git a/librashader-runtime/Cargo.toml b/librashader-runtime/Cargo.toml index 560d6e3..3219c8f 100644 --- a/librashader-runtime/Cargo.toml +++ b/librashader-runtime/Cargo.toml @@ -6,8 +6,10 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +librashader-common = { path = "../librashader-common" } librashader-presets = { path = "../librashader-presets" } librashader-preprocess = { path = "../librashader-preprocess" } librashader-reflect = { path = "../librashader-reflect" } bytemuck = "1.12.3" -rustc-hash = "1.1.0" \ No newline at end of file +rustc-hash = "1.1.0" +num-traits = "0.2.15" \ No newline at end of file diff --git a/librashader-runtime/src/lib.rs b/librashader-runtime/src/lib.rs index e94188a..f3b0491 100644 --- a/librashader-runtime/src/lib.rs +++ b/librashader-runtime/src/lib.rs @@ -1,2 +1,3 @@ pub mod semantics; pub mod uniforms; +pub mod scaling; diff --git a/librashader-runtime/src/scaling.rs b/librashader-runtime/src/scaling.rs new file mode 100644 index 0000000..08cfe9a --- /dev/null +++ b/librashader-runtime/src/scaling.rs @@ -0,0 +1,58 @@ +use std::ops::Mul; +use librashader_common::Size; +use librashader_presets::{Scale2D, ScaleFactor, ScaleType, Scaling}; +use num_traits::AsPrimitive; + +pub fn scale(scaling: Scale2D, source: Size, viewport: Size) -> Size +where T: Mul + Copy + 'static, + f32: AsPrimitive, +{ + let width: f32; + let height: f32; + + match scaling.x { + Scaling { + scale_type: ScaleType::Input, + factor, + } => width = source.width * factor, + Scaling { + scale_type: ScaleType::Absolute, + factor, + } => width = factor.into(), + Scaling { + scale_type: ScaleType::Viewport, + factor, + } => width = viewport.width * factor, + }; + + match scaling.y { + Scaling { + scale_type: ScaleType::Input, + factor, + } => height = source.height * factor, + Scaling { + scale_type: ScaleType::Absolute, + factor, + } => height = factor.into(), + Scaling { + scale_type: ScaleType::Viewport, + factor, + } => height = viewport.height * factor, + }; + + Size { + width: width.round().as_(), + height: height.round().as_(), + } +} + +pub fn calc_miplevel(size: Size) -> u32 { + let mut size = std::cmp::max(size.width, size.height); + let mut levels = 0; + while size != 0 { + levels += 1; + size >>= 1; + } + + levels +} \ No newline at end of file diff --git a/librashader-runtime/src/uniforms.rs b/librashader-runtime/src/uniforms.rs index c62e5d9..0c6d5d4 100644 --- a/librashader-runtime/src/uniforms.rs +++ b/librashader-runtime/src/uniforms.rs @@ -17,6 +17,21 @@ pub trait BindUniform { fn bind_uniform(value: T, ctx: C) -> Option<()>; } +pub trait UniformStorageAccess { + fn ubo_pointer(&self) -> *const u8; + fn push_pointer(&self) -> *const u8; +} + +impl UniformStorageAccess for UniformStorage { + fn ubo_pointer(&self) -> *const u8 { + self.ubo.as_ptr() + } + + fn push_pointer(&self) -> *const u8 { + self.push.as_ptr() + } +} + pub struct UniformStorage> { pub ubo: Box<[u8]>, pub push: Box<[u8]>,