From 10eb2c31069b852f931e13bf668645d6bd790e2d Mon Sep 17 00:00:00 2001 From: chyyran Date: Mon, 21 Nov 2022 02:13:10 -0500 Subject: [PATCH] gl: implement passfeedback and original history --- librashader-preprocess/src/pragma.rs | 43 +++- librashader-presets/Cargo.toml | 1 + librashader-presets/src/parse/preset.rs | 2 +- librashader-presets/src/parse/value.rs | 18 +- librashader-reflect/src/reflect/cross.rs | 1 + librashader-reflect/src/reflect/mod.rs | 5 +- librashader-reflect/src/reflect/semantics.rs | 7 +- librashader-runtime-gl/src/filter_chain.rs | 239 ++++++++++--------- librashader-runtime-gl/src/filter_pass.rs | 106 ++++++-- librashader-runtime-gl/src/framebuffer.rs | 65 ++++- librashader-runtime-gl/src/hello_triangle.rs | 59 +++-- librashader-runtime-gl/src/lib.rs | 3 +- librashader-runtime-gl/src/quad_render.rs | 35 +++ librashader-runtime-gl/src/render_target.rs | 2 +- librashader-runtime-gl/src/util.rs | 47 ---- 15 files changed, 404 insertions(+), 229 deletions(-) create mode 100644 librashader-runtime-gl/src/quad_render.rs diff --git a/librashader-preprocess/src/pragma.rs b/librashader-preprocess/src/pragma.rs index 5d1ba0d..b56389d 100644 --- a/librashader-preprocess/src/pragma.rs +++ b/librashader-preprocess/src/pragma.rs @@ -6,6 +6,7 @@ use nom::number::complete::float; use nom::sequence::delimited; use nom::IResult; use std::str::FromStr; +use nom::character::complete::multispace1; #[derive(Debug)] pub(crate) struct ShaderMeta { @@ -15,18 +16,25 @@ pub(crate) struct ShaderMeta { } fn parse_parameter_string(input: &str) -> Result { - fn parse_parameter_string_inner(input: &str) -> IResult<&str, ShaderParameter> { + fn parse_parameter_string_name(input: &str) -> IResult<&str, (&str, &str)> { let (input, _) = tag("#pragma parameter ")(input)?; - let (input, name) = take_while(|c| c != ' ')(input)?; - let (input, _) = tag(" ")(input)?; + let (input, name) = take_while(|c| c != ' ' && c != '\t')(input)?; + let (input, _) = multispace1(input)?; let (input, description) = delimited(tag("\""), is_not("\""), tag("\""))(input)?; - let (input, _) = tag(" ")(input)?; + let (input, _) = multispace1(input)?; + Ok(( + input, + (name, description) + )) + } + + fn parse_parameter_string_inner<'a, 'b>(name: &'a str, description: &'a str, input: &'b str) -> IResult<&'b str, ShaderParameter> { let (input, initial) = float(input)?; - let (input, _) = tag(" ")(input)?; + let (input, _) = multispace1(input)?; let (input, minimum) = float(input)?; - let (input, _) = tag(" ")(input)?; + let (input, _) = multispace1(input)?; let (input, maximum) = float(input)?; - let (input, _) = tag(" ")(input)?; + let (input, _) = multispace1(input)?; let (input, step) = float(input)?; Ok(( input, @@ -41,11 +49,26 @@ fn parse_parameter_string(input: &str) -> Result) -> Result { diff --git a/librashader-presets/Cargo.toml b/librashader-presets/Cargo.toml index e09de6b..582b2bf 100644 --- a/librashader-presets/Cargo.toml +++ b/librashader-presets/Cargo.toml @@ -10,3 +10,4 @@ thiserror = "1.0.37" nom = "7.1.1" nom_locate = "4.0.0" librashader = { path = "../librashader" } +num-traits = "0.2" \ No newline at end of file diff --git a/librashader-presets/src/parse/preset.rs b/librashader-presets/src/parse/preset.rs index db2e4d8..da31823 100644 --- a/librashader-presets/src/parse/preset.rs +++ b/librashader-presets/src/parse/preset.rs @@ -139,7 +139,7 @@ pub fn resolve_values(mut values: Vec) -> ShaderPreset { Value::FrameCountMod(_, value) => Some(*value), _ => None, }) - .unwrap_or(1), + .unwrap_or(0), srgb_framebuffer: shader_values .iter() .find_map(|f| match f { diff --git a/librashader-presets/src/parse/value.rs b/librashader-presets/src/parse/value.rs index cede313..077a972 100644 --- a/librashader-presets/src/parse/value.rs +++ b/librashader-presets/src/parse/value.rs @@ -6,6 +6,8 @@ use nom::character::complete::digit1; use nom::combinator::{eof, map_res}; use nom::IResult; +use num_traits::cast::ToPrimitive; + use crate::parse::token::do_lex; use std::fs::File; @@ -66,14 +68,28 @@ impl Value { fn from_int(input: Span) -> Result { i32::from_str(input.trim()).map_err(|_| { - eprintln!("{input}"); ParsePresetError::ParserError { offset: input.location_offset(), row: input.location_line(), col: input.get_column(), kind: ParseErrorKind::Int, } + }).or_else(|e| { + let result = f32::from_str(input.trim()) + .map_err(|_| e)?; + let result = result.trunc().to_i32() + .ok_or(ParsePresetError::ParserError { + offset: input.location_offset(), + row: input.location_line(), + col: input.get_column(), + kind: ParseErrorKind::Int, + })?; + + eprintln!("falling back to float trunc {result}"); + Ok(result) }) + + } fn from_ul(input: Span) -> Result { diff --git a/librashader-reflect/src/reflect/cross.rs b/librashader-reflect/src/reflect/cross.rs index 6b841c6..4f8a62e 100644 --- a/librashader-reflect/src/reflect/cross.rs +++ b/librashader-reflect/src/reflect/cross.rs @@ -260,6 +260,7 @@ where if descriptor_set != 0 { return Err(blame.error(SemanticsErrorKind::InvalidDescriptorSet(descriptor_set))); } + let size = ast.get_declared_struct_size(ubo.base_type_id)?; Ok(UboData { descriptor_set, diff --git a/librashader-reflect/src/reflect/mod.rs b/librashader-reflect/src/reflect/mod.rs index 72cd815..e5ae912 100644 --- a/librashader-reflect/src/reflect/mod.rs +++ b/librashader-reflect/src/reflect/mod.rs @@ -1,3 +1,4 @@ +use std::ops::Index; use crate::error::ShaderReflectError; use crate::reflect::semantics::{ SemanticMap, TextureImage, TextureSemantics, TextureSizeMeta, VariableMeta, VariableSemantics, @@ -97,7 +98,9 @@ impl TextureSemanticMap for FxHashMap { if let Some(semantics) = TextureSemantics::TEXTURE_SEMANTICS .iter() - .find(|f| name.starts_with(f.texture_name())) + .find(|f| { + name.starts_with(f.texture_name()) + }) { if semantics.is_array() { let index = &name[semantics.texture_name().len()..]; diff --git a/librashader-reflect/src/reflect/semantics.rs b/librashader-reflect/src/reflect/semantics.rs index 5ed9a23..5d80532 100644 --- a/librashader-reflect/src/reflect/semantics.rs +++ b/librashader-reflect/src/reflect/semantics.rs @@ -63,10 +63,13 @@ pub enum TextureSemantics { } impl TextureSemantics { - pub const TEXTURE_SEMANTICS: [TextureSemantics; 6] = [ - TextureSemantics::Original, + pub(crate) const TEXTURE_SEMANTICS: [TextureSemantics; 6] = [ TextureSemantics::Source, + // originalhistory needs to come first, otherwise + // the name lookup implementation will prioritize Original + // when reflecting semantics. TextureSemantics::OriginalHistory, + TextureSemantics::Original, TextureSemantics::PassOutput, TextureSemantics::PassFeedback, TextureSemantics::User, diff --git a/librashader-runtime-gl/src/filter_chain.rs b/librashader-runtime-gl/src/filter_chain.rs index 65641d6..dcc6cf8 100644 --- a/librashader-runtime-gl/src/filter_chain.rs +++ b/librashader-runtime-gl/src/filter_chain.rs @@ -1,3 +1,4 @@ +use std::collections::VecDeque; use crate::binding::{UniformBinding, UniformLocation, VariableLocation}; use crate::filter_pass::FilterPass; use crate::framebuffer::Framebuffer; @@ -6,8 +7,8 @@ use crate::util; use crate::util::{GlImage, InlineRingBuffer, Size, Texture, Viewport}; use gl::types::{GLenum, GLint, GLsizei, GLsizeiptr, GLuint}; use librashader::image::Image; -use librashader::{FilterMode, ShaderSource}; -use librashader_presets::{ShaderPassConfig, ShaderPreset, TextureConfig}; +use librashader::{FilterMode, ShaderSource, WrapMode}; +use librashader_presets::{ScaleType, ShaderPassConfig, ShaderPreset, TextureConfig}; use librashader_reflect::back::cross::{GlVersion, GlslangGlslContext}; use librashader_reflect::back::targets::{CompilerBackend, FromCompilation, GLSL}; use librashader_reflect::back::CompileShader; @@ -19,11 +20,27 @@ use rustc_hash::FxHashMap; use spirv_cross::spirv::Decoration; use std::error::Error; use std::path::Path; +use crate::quad_render::DrawQuad; + +pub struct FilterChain { + passes: Box<[FilterPass]>, + common: FilterCommon, + filter_vao: GLuint, + output_framebuffers: Box<[Framebuffer]>, + feedback_framebuffers: Box<[Framebuffer]>, + history_framebuffers: VecDeque, +} + +pub struct FilterCommon { + semantics: ReflectSemantics, + pub(crate) preset: ShaderPreset, + pub(crate) luts: FxHashMap, + pub output_textures: Box<[Texture]>, + pub feedback_textures: Box<[Texture]>, + pub history_textures: Box<[Texture]>, + pub(crate) draw_quad: DrawQuad, +} -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, -]; impl FilterChain { fn load_pass_semantics( @@ -103,24 +120,6 @@ impl FilterChain { } } -pub struct FilterChain { - passes: Box<[FilterPass]>, - common: FilterCommon, - quad_vao: GLuint, - output_framebuffers: Box<[Framebuffer]>, -} - -pub struct FilterCommon { - semantics: ReflectSemantics, - pub(crate) preset: ShaderPreset, - pub original_history: Box<[Framebuffer]>, - pub history: Vec, - pub feedback: Vec, - pub(crate) luts: FxHashMap, - pub output_textures: Box<[Texture]>, - pub(crate) quad_vbo: GLuint, -} - type ShaderPassMeta<'a> = ( &'a ShaderPassConfig, ShaderSource, @@ -330,7 +329,6 @@ impl FilterChain { .vertex .get_decoration(res.id, Decoration::Location)?; let loc_name = format!("LIBRA_ATTRIBUTE_{loc}\0"); - eprintln!("{loc_name}"); gl::BindAttribLocation(program, loc, loc_name.as_str().as_ptr().cast()) } gl::LinkProgram(program); @@ -443,7 +441,7 @@ impl FilterChain { ); } - // eprintln!("{:#?}", semantics); + // eprintln!("{:#?}", reflection.meta.texture_meta); // eprintln!("{:#?}", reflection.meta); // eprintln!("{:#?}", locations); // eprintln!("{:#?}", reflection.push_constant); @@ -469,7 +467,7 @@ impl FilterChain { Ok(filters.into_boxed_slice()) } - pub fn init_history(filters: &[FilterPass]) -> Box<[Framebuffer]> { + pub fn init_history(filters: &[FilterPass], filter: FilterMode, wrap_mode: WrapMode) -> (VecDeque, Box<[Texture]>) { let mut required_images = 0; for pass in filters { @@ -493,56 +491,27 @@ impl FilterChain { required_images = std::cmp::max(required_images, texture_size_count); } - // not ussing frame history; - if required_images < 2 { - eprintln!("not using frame history"); - return Vec::new().into_boxed_slice(); + // not using frame history; + if required_images <= 1 { + println!("[history] not using frame history"); + return (VecDeque::new(), Box::new([])) } // history0 is aliased with the original - required_images -= 1; - let mut framebuffers = Vec::new(); + 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.into_boxed_slice() - } - pub fn init_feedback(filters: &[FilterPass]) -> Box<[Framebuffer]> { - let mut required_images = 0; + let mut history_textures = Vec::new(); + history_textures.resize_with(required_images, || Texture { + image: Default::default(), + filter, + mip_filter: filter, + wrap_mode + }); - for pass in filters { - // If a shader uses history size, but not history, we still need to keep the texture. - let texture_count = pass - .reflection - .meta - .texture_meta - .iter() - .filter(|(semantics, _)| semantics.semantics == TextureSemantics::OriginalHistory) - .count(); - let texture_size_count = pass - .reflection - .meta - .texture_size_meta - .iter() - .filter(|(semantics, _)| semantics.semantics == TextureSemantics::OriginalHistory) - .count(); - - required_images = std::cmp::max(required_images, texture_count); - required_images = std::cmp::max(required_images, texture_size_count); - } - - // not ussing frame history; - if required_images < 2 { - eprintln!("not using frame history"); - return Vec::new().into_boxed_slice(); - } - - // history0 is aliased with the original - required_images -= 1; - - let mut framebuffers = Vec::new(); - framebuffers.resize_with(required_images, || Framebuffer::new(1)); - framebuffers.into_boxed_slice() + (framebuffers, history_textures.into_boxed_slice()) } pub fn load(path: impl AsRef) -> Result> { @@ -553,49 +522,50 @@ impl FilterChain { // initialize passes let filters = FilterChain::init_passes(passes, &semantics)?; + let default_filter = filters.first().map(|f| f.config.filter).unwrap_or_default(); + let default_wrap = filters.first().map(|f| f.config.wrap_mode).unwrap_or_default(); + // initialize output framebuffers let mut output_framebuffers = Vec::new(); output_framebuffers.resize_with(filters.len(), || Framebuffer::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)); + let mut feedback_textures = Vec::new(); + feedback_textures.resize_with(filters.len(), Texture::default); + // load luts let luts = FilterChain::load_luts(&preset.textures)?; - let original_history = FilterChain::init_history(&filters); + let (history_framebuffers, history_textures) = + FilterChain::init_history(&filters, default_filter, default_wrap); // create VBO objects - let mut quad_vbo = 0; - unsafe { - gl::GenBuffers(1, &mut quad_vbo); - gl::BindBuffer(gl::ARRAY_BUFFER, quad_vbo); - gl::BufferData( - gl::ARRAY_BUFFER, - std::mem::size_of_val(QUAD_VBO_DATA) as GLsizeiptr, - QUAD_VBO_DATA.as_ptr().cast(), - gl::STATIC_DRAW, - ); - gl::BindBuffer(gl::ARRAY_BUFFER, 0); - } + let draw_quad = DrawQuad::new(); - let mut quad_vao = 0; + let mut filter_vao = 0; unsafe { - gl::GenVertexArrays(1, &mut quad_vao); + gl::GenVertexArrays(1, &mut filter_vao); } Ok(FilterChain { passes: filters, output_framebuffers: output_framebuffers.into_boxed_slice(), - quad_vao, + feedback_framebuffers: feedback_framebuffers.into_boxed_slice(), + history_framebuffers, + filter_vao, common: FilterCommon { semantics, preset, - original_history, - history: vec![], - feedback: vec![], luts, output_textures: output_textures.into_boxed_slice(), - quad_vbo, + feedback_textures: feedback_textures.into_boxed_slice(), + history_textures, + draw_quad, }, }) } @@ -605,35 +575,58 @@ impl FilterChain { self.output_framebuffers[index].as_texture(config.filter, config.wrap_mode) } - pub fn frame(&mut self, count: usize, vp: &Viewport, input: GlImage, _clear: bool) { + pub fn push_history(&mut self, input: &GlImage) { + if let Some(mut back) = self.history_framebuffers.pop_back() { + + if back.size != input.size + || (input.format != 0 && input.format != back.format) { + eprintln!("[history] resizing"); + back.init(input.size, input.format); + } + + if back.is_initialized() { + back.copy_from(&input); + } + + self.history_framebuffers.push_front(back) + } + } + + pub fn frame(&mut self, count: usize, viewport: &Viewport, input: &GlImage, clear: bool) { + if clear { + for framebuffer in &self.history_framebuffers { + framebuffer.clear() + } + } + + if self.passes.is_empty() { return; } unsafe { gl::BindFramebuffer(gl::FRAMEBUFFER, 0); - gl::BindVertexArray(self.quad_vao); + gl::BindVertexArray(self.filter_vao); } - // todo: copy framebuffer - // shader_gl3: 2067 - let filter = self - .common - .preset - .shaders - .first() - .map(|f| f.filter) - .unwrap_or_default(); - let wrap_mode = self - .common - .preset - .shaders - .first() - .map(|f| f.wrap_mode) - .unwrap_or_default(); + let filter = self.passes[0].config.filter; + let wrap_mode = self.passes[0].config.wrap_mode; + // update history + for (texture, fbo) in self.common.history_textures.iter_mut().zip(self.history_framebuffers.iter()) { + texture.image = fbo.as_texture(filter, wrap_mode).image; + } + + for ((texture, fbo), pass) in self.common.feedback_textures.iter_mut() + .zip(self.feedback_framebuffers.iter()) + .zip(self.passes.iter()) + { + texture.image = fbo.as_texture(pass.config.filter, pass.config.wrap_mode).image; + } + + // shader_gl3: 2067 let original = Texture { - image: input, + image: *input, filter, mip_filter: filter, wrap_mode, @@ -650,7 +643,7 @@ impl FilterChain { let _framebuffer_size = target.scale( pass.config.scaling.clone(), pass.get_format(), - vp, + viewport, &original, &source, ); @@ -660,9 +653,13 @@ impl FilterChain { pass.draw( index, &self.common, - (count % pass.config.frame_count_mod as usize) as u32, + if pass.config.frame_count_mod > 0 { + count % pass.config.frame_count_mod as usize + } else { + count + } as u32, 1, - vp, + viewport, &original, &source, RenderTarget::new(target, None), @@ -681,19 +678,29 @@ impl FilterChain { pass.draw( passes_len - 1, &self.common, - (count % pass.config.frame_count_mod as usize) as u32, + if pass.config.frame_count_mod > 0 { + count % pass.config.frame_count_mod as usize + } else { + count + } as u32, 1, - vp, + viewport, &original, &source, - RenderTarget::new(vp.output, vp.mvp), + RenderTarget::new(viewport.output, viewport.mvp), ); } + // swap feedback framebuffers with output + for (output, feedback) in self.output_framebuffers.iter_mut().zip(self.feedback_framebuffers.iter_mut()) { + std::mem::swap(output, feedback); + } + + self.push_history(&input); unsafe { gl::BindFramebuffer(gl::FRAMEBUFFER, 0); gl::BindVertexArray(0); } - // todo: deal with the mess that is frame history } + } diff --git a/librashader-runtime-gl/src/filter_pass.rs b/librashader-runtime-gl/src/filter_pass.rs index 25b0bcd..8331d8a 100644 --- a/librashader-runtime-gl/src/filter_pass.rs +++ b/librashader-runtime-gl/src/filter_pass.rs @@ -93,8 +93,11 @@ impl FilterPass { Self::build_uniform(location, buffer, value, gl::Uniform1f) } - fn bind_texture(binding: &TextureImage, texture: &Texture) { + fn bind_texture(binding: &TextureImage, texture: &Texture, semantic: TextureSemantics) { unsafe { + if texture.image.handle == 0 { + eprintln!("[WARNING] trying to bind {semantic:?} texture 0 to slot {} ", binding.binding) + } // eprintln!("setting {} to texunit {}", texture.image.handle, binding.binding); gl::ActiveTexture(gl::TEXTURE0 + binding.binding); gl::BindTexture(gl::TEXTURE_2D, texture.image.handle); @@ -220,7 +223,7 @@ impl FilterPass { gl::EnableVertexAttribArray(0); gl::EnableVertexAttribArray(1); - gl::BindBuffer(gl::ARRAY_BUFFER, parent.quad_vbo); + gl::BindBuffer(gl::ARRAY_BUFFER, parent.draw_quad.vbo); /// the provided pointers are of OpenGL provenance with respect to the buffer bound to quad_vbo, /// and not a known provenance to the Rust abstract machine, therefore we give it invalid pointers. @@ -344,8 +347,7 @@ impl FilterPass { .texture_meta .get(&TextureSemantics::Original.semantics(0)) { - eprintln!("setting original binding to {}", binding.binding); - FilterPass::bind_texture(binding, original); + FilterPass::bind_texture(binding, original, TextureSemantics::Original); } // bind OriginalSize @@ -372,7 +374,7 @@ impl FilterPass { .get(&TextureSemantics::Source.semantics(0)) { // eprintln!("setting source binding to {}", binding.binding); - FilterPass::bind_texture(binding, source); + FilterPass::bind_texture(binding, source, TextureSemantics::Source); } // bind SourceSize @@ -391,14 +393,60 @@ impl FilterPass { ); } - for (index, output) in parent.output_textures[0..pass_index].iter().enumerate() { + + if let Some(binding) = self.reflection.meta.texture_meta.get(&TextureSemantics::OriginalHistory.semantics(0)) { + FilterPass::bind_texture(binding, original, TextureSemantics::OriginalHistory); + } + if let Some((location, offset)) = self + .variable_bindings + .get(&TextureSemantics::OriginalHistory.semantics(0).into()) + { + let (buffer, offset) = match offset { + MemberOffset::Ubo(offset) => (&mut self.uniform_buffer, *offset), + MemberOffset::PushConstant(offset) => (&mut self.push_buffer, *offset), + }; + FilterPass::build_vec4( + location.location(), + &mut buffer[offset..][..16], + original.image.size, + ); + } + + for (index, output) in parent.history_textures.iter().enumerate() { + if let Some(binding) = self + .reflection + .meta + .texture_meta + .get(&TextureSemantics::OriginalHistory.semantics(index + 1)) + { + FilterPass::bind_texture(binding, output, TextureSemantics::OriginalHistory); + } + + if let Some((location, offset)) = self + .variable_bindings + .get(&TextureSemantics::OriginalHistory.semantics(index + 1).into()) + { + let (buffer, offset) = match offset { + MemberOffset::Ubo(offset) => (&mut self.uniform_buffer, *offset), + MemberOffset::PushConstant(offset) => (&mut self.push_buffer, *offset), + }; + FilterPass::build_vec4( + location.location(), + &mut buffer[offset..][..16], + output.image.size, + ); + } + } + + // PassOutput + for (index, output) in parent.output_textures.iter().enumerate() { if let Some(binding) = self .reflection .meta .texture_meta .get(&TextureSemantics::PassOutput.semantics(index)) { - FilterPass::bind_texture(binding, output); + FilterPass::bind_texture(binding, output, TextureSemantics::PassOutput); } if let Some((location, offset)) = self @@ -417,20 +465,32 @@ impl FilterPass { } } - // // todo: history - // - // // if let Some(binding) = self.reflection.meta.texture_meta.get(&TextureSemantics::OriginalHistory.semantics(0)) { - // // FilterPass::set_texture(binding, original); - // // } - // // if let Some(variable) = self.reflection.meta.texture_size_meta.get(&TextureSemantics::OriginalHistory.semantics(0)) { - // // let location = self.locations.get(&variable.id).expect("variable did not have location mapped").location(); - // // let (buffer, offset) = match variable.offset { - // // MemberOffset::Ubo(offset) => (&mut self.uniform_buffer, offset), - // // MemberOffset::PushConstant(offset) => (&mut self.push_buffer, offset) - // // }; - // // FilterPass::build_vec4(location, &mut buffer[offset..][..4], original.image.size); - // // } - // + // PassFeedback + for (index, feedback) in parent.feedback_textures.iter().enumerate() { + if let Some(binding) = self + .reflection + .meta + .texture_meta + .get(&TextureSemantics::PassFeedback.semantics(index)) + { + FilterPass::bind_texture(binding, feedback, TextureSemantics::PassFeedback); + } + + if let Some((location, offset)) = self + .variable_bindings + .get(&TextureSemantics::PassFeedback.semantics(index).into()) + { + let (buffer, offset) = match offset { + MemberOffset::Ubo(offset) => (&mut self.uniform_buffer, *offset), + MemberOffset::PushConstant(offset) => (&mut self.push_buffer, *offset), + }; + FilterPass::build_vec4( + location.location(), + &mut buffer[offset..][..16], + feedback.image.size, + ); + } + } // bind float parameters for (id, (location, offset)) in @@ -475,7 +535,7 @@ impl FilterPass { .texture_meta .get(&TextureSemantics::User.semantics(*index)) { - FilterPass::bind_texture(binding, lut); + FilterPass::bind_texture(binding, lut, TextureSemantics::User); } if let Some((location, offset)) = self @@ -493,7 +553,5 @@ impl FilterPass { ); } } - - // // todo history } } diff --git a/librashader-runtime-gl/src/framebuffer.rs b/librashader-runtime-gl/src/framebuffer.rs index 3c32ecf..1603004 100644 --- a/librashader-runtime-gl/src/framebuffer.rs +++ b/librashader-runtime-gl/src/framebuffer.rs @@ -1,6 +1,6 @@ use crate::util; use crate::util::{GlImage, Size, Texture, Viewport}; -use gl::types::{GLenum, GLsizei, GLuint}; +use gl::types::{GLenum, GLint, GLsizei, GLuint}; use librashader::{FilterMode, ShaderFormat, WrapMode}; use librashader_presets::{Scale2D, ScaleType, Scaling}; @@ -131,7 +131,68 @@ impl Framebuffer { size } - fn init(&mut self, mut size: Size, format: impl Into) { + pub(crate) fn is_initialized(&self) -> bool { + self.init + } + + pub 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 fn copy_from(&mut self, image: &GlImage) { + 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::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); + } + } + // todo: fix panic + pub(crate) fn init(&mut self, mut size: Size, format: impl Into) { + self.init = false; self.format = format.into(); self.size = size; diff --git a/librashader-runtime-gl/src/hello_triangle.rs b/librashader-runtime-gl/src/hello_triangle.rs index ff4b3ac..12592c9 100644 --- a/librashader-runtime-gl/src/hello_triangle.rs +++ b/librashader-runtime-gl/src/hello_triangle.rs @@ -10,8 +10,8 @@ use crate::filter_chain::FilterChain; use crate::framebuffer::Framebuffer; use crate::util::{GlImage, Size, Viewport}; -const WIDTH: u32 = 900; -const HEIGHT: u32 = 700; +const WIDTH: u32 = 1920; +const HEIGHT: u32 = 1080; const TITLE: &str = "Hello From OpenGL World!"; pub fn compile_program(vertex: &str, fragment: &str) -> GLuint { @@ -102,7 +102,7 @@ extern "system" fn debug_callback( ) { unsafe { let message = CStr::from_ptr(message); - eprintln!("{message:?}"); + println!("[gl] {message:?}"); } } @@ -414,7 +414,9 @@ pub fn do_loop( } let fullscreen_fbo = [ - -1.0f32, -1.0, 0.0, 1.0, -1.0, 0.0, -1.0, 1.0, 0.0, -1.0, 1.0, 0.0, 1.0, -1.0, 0.0, + -1.0f32, -1.0, 0.0, 1.0, + -1.0, 0.0, -1.0, 1.0, 0.0, + -1.0, 1.0, 0.0, 1.0, -1.0, 0.0, 1.0, 1.0, 0.0, ]; @@ -459,17 +461,21 @@ void main() gl::GenVertexArrays(1, &mut quad_vao); } - let fb = Framebuffer::new_from_raw( + let (fb_width, fb_height) = window.get_framebuffer_size(); + let (vp_width, vp_height) = window.get_size(); + + let output = Framebuffer::new_from_raw( output_texture, output_framebuffer_handle, gl::RGBA8, Size { - width: WIDTH, - height: HEIGHT, + width: vp_width as u32, + height: vp_height as u32, }, 1, ); + while !window.should_close() { glfw.poll_events(); for (_, event) in glfw::flush_messages(&events) { @@ -479,7 +485,7 @@ void main() unsafe { // render to fb gl::BindFramebuffer(gl::FRAMEBUFFER, rendered_framebuffer); - gl::Viewport(0, 0, WIDTH as GLsizei, HEIGHT as GLsizei); + gl::Viewport(0, 0, vp_width, vp_height); // clear color clear_color(Color(0.3, 0.4, 0.6, 1.0)); @@ -500,24 +506,28 @@ void main() gl::BindFramebuffer(gl::FRAMEBUFFER, 0); } + let viewport = Viewport { + x: 0, + y: 0, + output: &output, + mvp: None, + }; + + let rendered = GlImage { + handle: rendered_texture, + format: gl::RGBA8, + size: Size { + width: fb_width as u32, + height: fb_height as u32, + }, + padded_size: Default::default(), + }; + unsafe { filter.frame( framecount, - &Viewport { - x: 0, - y: 0, - output: &fb, - mvp: None, - }, - GlImage { - handle: rendered_texture, - format: gl::RGBA8, - size: Size { - width: WIDTH, - height: HEIGHT, - }, - padded_size: Default::default(), - }, + &viewport, + &rendered, false, ) } @@ -561,6 +571,9 @@ fn glfw_handle_event(window: &mut glfw::Window, event: glfw::WindowEvent) { Event::Key(Key::Escape, _, Action::Press, _) => { window.set_should_close(true); } + Event::Size(width, height) => { + window.set_size(width, height) + } _ => {} } } diff --git a/librashader-runtime-gl/src/lib.rs b/librashader-runtime-gl/src/lib.rs index 12092d3..42ff537 100644 --- a/librashader-runtime-gl/src/lib.rs +++ b/librashader-runtime-gl/src/lib.rs @@ -8,6 +8,7 @@ mod framebuffer; mod hello_triangle; mod render_target; mod util; +mod quad_render; #[cfg(test)] mod tests { @@ -17,7 +18,7 @@ mod tests { #[test] fn triangle() { let (glfw, window, events, shader, vao) = hello_triangle::setup(); - let mut filter = FilterChain::load("../test/slang-shaders/crt/crt-geom.slangp").unwrap(); + let mut filter = FilterChain::load("../test/slang-shaders/bezel/Mega_Bezel/Presets/MBZ__0__SMOOTH-ADV-GLASS.slangp").unwrap(); // FilterChain::load("../test/slang-shaders/crt/crt-royale.slangp").unwrap(); diff --git a/librashader-runtime-gl/src/quad_render.rs b/librashader-runtime-gl/src/quad_render.rs new file mode 100644 index 0000000..b9c59d1 --- /dev/null +++ b/librashader-runtime-gl/src/quad_render.rs @@ -0,0 +1,35 @@ +use gl::types::{GLsizeiptr, GLuint}; + + +#[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 DrawQuad { + pub vbo: GLuint +} + +impl DrawQuad { + pub fn new() -> DrawQuad { + let mut vbo = 0; + unsafe { + gl::GenBuffers(1, &mut vbo); + gl::BindBuffer(gl::ARRAY_BUFFER, vbo); + gl::BufferData( + gl::ARRAY_BUFFER, + std::mem::size_of_val(QUAD_VBO_DATA) as GLsizeiptr, + QUAD_VBO_DATA.as_ptr().cast(), + gl::STATIC_DRAW, + ); + gl::BindBuffer(gl::ARRAY_BUFFER, 0); + } + + DrawQuad { + vbo + } + } +} \ No newline at end of file diff --git a/librashader-runtime-gl/src/render_target.rs b/librashader-runtime-gl/src/render_target.rs index 6176650..27c7128 100644 --- a/librashader-runtime-gl/src/render_target.rs +++ b/librashader-runtime-gl/src/render_target.rs @@ -1,5 +1,5 @@ use crate::framebuffer::Framebuffer; -use crate::util::Viewport; +use crate::util::{Texture, Viewport}; #[rustfmt::skip] static DEFAULT_MVP: &[f32] = &[ diff --git a/librashader-runtime-gl/src/util.rs b/librashader-runtime-gl/src/util.rs index 2f79b6e..40fda89 100644 --- a/librashader-runtime-gl/src/util.rs +++ b/librashader-runtime-gl/src/util.rs @@ -92,53 +92,6 @@ where } } - -pub struct AllocRingBuffer { - items: Box<[T]>, - index: usize -} - -impl AllocRingBuffer - where - T: Default, -{ - pub fn new(len: usize) -> Self { - let mut items = Vec::new(); - items.resize_with(len, T::default); - - Self { - items: items.into_boxed_slice(), - index: 0, - } - } - - pub fn items(&self) -> &[T] { - &self.items - } - - pub fn items_mut(&mut self) -> &mut [T] { - &mut self.items - } -} - -impl RingBuffer for AllocRingBuffer { - fn current(&self) -> &T { - &self.items[self.index] - } - - fn current_mut(&mut self) -> &mut T { - &mut self.items[self.index] - } - - fn next(&mut self) { - self.index += 1; - if self.index >= self.items.len() { - self.index = 0 - } - } -} - - pub unsafe fn gl_compile_shader(stage: GLenum, source: &str) -> GLuint { let shader = gl::CreateShader(stage); gl::ShaderSource(