From 19cd09d32b98c54a26b01daa5c60f5dae91c972b Mon Sep 17 00:00:00 2001 From: chyyran Date: Sat, 19 Nov 2022 22:09:01 -0500 Subject: [PATCH] gl: move chains to to filter_chain module --- librashader-runtime-gl/src/filter.rs | 0 librashader-runtime-gl/src/filter_chain.rs | 435 +++++++++++++++++++ librashader-runtime-gl/src/filter_pass.rs | 2 +- librashader-runtime-gl/src/hello_triangle.rs | 2 +- librashader-runtime-gl/src/lib.rs | 435 +------------------ librashader-runtime-gl/src/util.rs | 13 + 6 files changed, 452 insertions(+), 435 deletions(-) delete mode 100644 librashader-runtime-gl/src/filter.rs create mode 100644 librashader-runtime-gl/src/filter_chain.rs diff --git a/librashader-runtime-gl/src/filter.rs b/librashader-runtime-gl/src/filter.rs deleted file mode 100644 index e69de29..0000000 diff --git a/librashader-runtime-gl/src/filter_chain.rs b/librashader-runtime-gl/src/filter_chain.rs new file mode 100644 index 0000000..692ae7d --- /dev/null +++ b/librashader-runtime-gl/src/filter_chain.rs @@ -0,0 +1,435 @@ +use rustc_hash::FxHashMap; +use gl::types::{GLenum, GLint, GLsizei, GLsizeiptr, GLuint}; +use librashader_presets::{ShaderPassConfig, ShaderPreset}; +use librashader_reflect::reflect::semantics::{MemberOffset, SemanticMap, TextureSemantics, UniformMeta, VariableSemantics}; +use librashader_reflect::reflect::{ReflectSemantics, ReflectShader, UniformSemantic}; +use std::path::Path; +use std::error::Error; +use librashader::{FilterMode, ShaderSource}; +use librashader_reflect::back::cross::GlVersion; +use librashader_reflect::back::targets::{FromCompilation, GLSL}; +use spirv_cross::spirv::Decoration; +use librashader::image::Image; +use librashader_reflect::back::CompileShader; +use crate::binding::{UniformBinding, UniformLocation, VariableLocation}; +use crate::filter_pass::FilterPass; +use crate::framebuffer::Framebuffer; +use crate::util; +use crate::util::{GlImage, RingBuffer, Size, Texture, Viewport}; + +static QUAD_VBO_DATA: &'static [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(uniform_semantics: &mut FxHashMap, texture_semantics: &mut FxHashMap>, + config: &ShaderPassConfig) { + let Some(alias) = &config.alias else { + return; + }; + + // Ignore empty aliases + if alias.trim().is_empty() { + return; + } + + let index = config.id as usize; + + // PassOutput + texture_semantics.insert(alias.clone(), SemanticMap { + semantics: TextureSemantics::PassOutput, + index + }); + uniform_semantics.insert(format!("{alias}Size"), UniformSemantic::Texture(SemanticMap { + semantics: TextureSemantics::PassOutput, + index + })); + + // PassFeedback + texture_semantics.insert(format!("{alias}Feedback"), SemanticMap { + semantics: TextureSemantics::PassFeedback, + index + }); + uniform_semantics.insert(format!("{alias}FeedbackSize"), UniformSemantic::Texture(SemanticMap { + semantics: TextureSemantics::PassFeedback, + index + })); + } + + fn reflect_uniform_location(pipeline: GLuint, meta: &impl UniformMeta) -> VariableLocation { + // todo: support both ubo and pushco + // todo: fix this. + match meta.offset() { + MemberOffset::Ubo(_) => { + let vert_name = format!("LIBRA_UBO_VERTEX_INSTANCE.{}\0", meta.id()); + let frag_name = format!("LIBRA_UBO_FRAGMENT_INSTANCE.{}\0", meta.id()); + unsafe { + let vertex = gl::GetUniformLocation(pipeline, vert_name.as_ptr().cast()); + let fragment = gl::GetUniformLocation(pipeline, frag_name.as_ptr().cast()); + + VariableLocation::Ubo(UniformLocation { + vertex, + fragment + }) + } + } + MemberOffset::PushConstant(_) => { + let vert_name = format!("LIBRA_PUSH_VERTEX_INSTANCE.{}\0", meta.id()); + let frag_name = format!("LIBRA_PUSH_FRAGMENT_INSTANCE.{}\0", meta.id()); + unsafe { + let vertex = gl::GetUniformLocation(pipeline, vert_name.as_ptr().cast()); + let fragment = gl::GetUniformLocation(pipeline, frag_name.as_ptr().cast()); + + VariableLocation::Push(UniformLocation { + vertex, + fragment + }) + } + } + } + } + +} + +pub struct FilterChain { + passes: Vec, + common: FilterCommon, + pub quad_vao: GLuint, +} + +pub struct FilterCommon { + semantics: ReflectSemantics, + preset: ShaderPreset, + original_history: Vec, + history: Vec, + feedback: Vec, + luts: FxHashMap, + outputs: Vec, + pub quad_vbo: GLuint, +} + +impl FilterChain { + pub fn load(path: impl AsRef) -> Result> { + let preset = librashader_presets::ShaderPreset::try_parse(path)?; + let mut uniform_semantics: FxHashMap = Default::default(); + let mut texture_semantics: FxHashMap> = Default::default(); + + let mut passes: Vec<(&ShaderPassConfig, ShaderSource, _)> = preset.shaders.iter() + .map(|shader| { + eprintln!("[gl] loading {}", &shader.name.display()); + let source: ShaderSource = librashader_preprocess::load_shader_source(&shader.name) + .unwrap(); + + let spirv = librashader_reflect::front::shaderc::compile_spirv(&source) + .unwrap(); + let mut reflect = GLSL::from_compilation(spirv).unwrap(); + + for parameter in source.parameters.iter() { + uniform_semantics.insert(parameter.id.clone(), UniformSemantic::Variable(SemanticMap { + semantics: VariableSemantics::FloatParameter, + index: () + })); + } + + (shader, source, reflect) + }).collect(); + + // todo: this can probably be extracted out. + + for details in &passes { + FilterChain::load_pass_semantics(&mut uniform_semantics, &mut texture_semantics, details.0) + } + + // add lut params + for (index, texture) in preset.textures.iter().enumerate() { + texture_semantics.insert(texture.name.clone(), SemanticMap { + semantics: TextureSemantics::User, + index + }); + + uniform_semantics.insert(format!("{}Size", texture.name), UniformSemantic::Texture(SemanticMap { + semantics: TextureSemantics::User, + index + })); + } + + let semantics = ReflectSemantics { + uniform_semantics, + non_uniform_semantics: texture_semantics + }; + + let mut filters = Vec::new(); + let mut output_framebuffers = Vec::new(); + + // initialize passes + for (index, (config, source, mut reflect)) in passes.into_iter().enumerate() { + let mut semantics = semantics.clone(); + + let reflection = reflect.reflect(index, &semantics)?; + let glsl = reflect.compile(GlVersion::V4_60)?; + + let vertex_resources = glsl.context.compiler.vertex.get_shader_resources()?; + + // todo: split this out. + let (program, ubo_location) = unsafe { + let vertex = util::gl_compile_shader(gl::VERTEX_SHADER, glsl.vertex.as_str()); + let fragment = util::gl_compile_shader(gl::FRAGMENT_SHADER, glsl.fragment.as_str()); + + let program = gl::CreateProgram(); + gl::AttachShader(program, vertex); + gl::AttachShader(program, fragment); + + for res in &vertex_resources.stage_inputs { + let loc = glsl.context.compiler.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); + gl::DeleteShader(vertex); + gl::DeleteShader(fragment); + + let mut status = 0; + gl::GetProgramiv(program, gl::LINK_STATUS, &mut status); + if status != 1 { + panic!("failed to link program") + } + + gl::UseProgram(program); + + for binding in &glsl.context.sampler_bindings { + let loc_name = format!("LIBRA_TEXTURE_{}\0", *binding); + let location = gl::GetUniformLocation(program, loc_name.as_str().as_ptr().cast()); + if location >= 0 { + // eprintln!("setting sampler {location} to sample from {binding}"); + gl::Uniform1i(location, *binding as GLint); + } + } + + gl::UseProgram(0); + (program, UniformLocation { + vertex: gl::GetUniformBlockIndex(program, b"LIBRA_UBO_VERTEX\0".as_ptr().cast()), + fragment: gl::GetUniformBlockIndex(program, b"LIBRA_UBO_FRAGMENT\0".as_ptr().cast()), + }) + }; + + let ubo_ring = if let Some(ubo) = &reflection.ubo { + let size = ubo.size; + let mut ring: RingBuffer = RingBuffer::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); + } + Some(ring) + } else { + None + }; + + let uniform_buffer = vec![0; reflection.ubo.as_ref().map(|ubo| ubo.size as usize).unwrap_or(0)].into_boxed_slice(); + let push_buffer = vec![0; reflection.push_constant.as_ref().map(|push| push.size as usize).unwrap_or(0)].into_boxed_slice(); + + // todo: reflect indexed parameters + let mut locations = FxHashMap::default(); + for param in reflection.meta.parameter_meta.values() { + locations.insert(UniformBinding::Parameter(param.id.clone()), + (FilterChain::reflect_uniform_location(program, param), param.offset)); + } + + for (semantics, param) in &reflection.meta.variable_meta { + locations.insert(UniformBinding::SemanticVariable(semantics.clone()), + (FilterChain::reflect_uniform_location(program, param), param.offset)); + } + + for (semantics, param) in &reflection.meta.texture_size_meta { + locations.insert(UniformBinding::TextureSize(semantics.clone()), + (FilterChain::reflect_uniform_location(program, param), param.offset)); + } + + // need output framebuffers. + output_framebuffers.push(Framebuffer::new(1)); + + // eprintln!("{:#?}", semantics); + // eprintln!("{:#?}", reflection.meta); + // eprintln!("{:#?}", locations); + // eprintln!("{:#?}", reflection.push_constant); + // eprintln!("====fragment===="); + // eprintln!("{:#}", glsl.fragment); + // eprintln!("====vertex===="); + // eprintln!("{:#}", glsl.vertex); + + filters.push(FilterPass { + reflection, + compiled: glsl, + program, + ubo_location, + ubo_ring, + uniform_buffer, + push_buffer, + variable_bindings: locations, + source, + // no idea if this works. + // retroarch checks if feedback frames are used but we'll just init it tbh. + feedback_framebuffer: Framebuffer::new(1), + config: config.clone() + }); + } + + // load luts + let mut luts = FxHashMap::default(); + + for (index, texture) in preset.textures.iter().enumerate() { + let image = Image::load(&texture.path)?; + let levels = if texture.mipmap { + util::calc_miplevel(image.width, image.height) + } 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.width as GLsizei, image.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.width as GLsizei, image.height as GLsizei, + gl::RGBA, gl::UNSIGNED_BYTE, image.bytes.as_ptr().cast()); + + let mipmap = levels > 1; + let linear = texture.filter_mode == FilterMode::Linear; + + // set mipmaps and wrapping + + if mipmap { + gl::GenerateMipmap(gl::TEXTURE_2D); + } + + gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_S, GLenum::from(texture.wrap_mode) as GLint); + gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_T, GLenum::from(texture.wrap_mode) as GLint); + + if !linear { + gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::NEAREST as GLint); + gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::NEAREST as GLint); + } else { + gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::LINEAR as GLint); + if mipmap { + gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, + gl::LINEAR_MIPMAP_LINEAR as GLint); + } else { + gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, + gl::LINEAR as GLint); + } + } + + gl::BindTexture(gl::TEXTURE_2D, 0); + } + + luts.insert(index, Texture { + image: GlImage { + handle, + format: gl::RGBA8, + size: Size { + width: image.width, + height: image.height + }, + padded_size: Size::default() + }, + filter: texture.filter_mode, + mip_filter: texture.filter_mode, + wrap_mode: texture.wrap_mode + }); + } + + 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 mut quad_vao = 0; + unsafe { + gl::GenVertexArrays(1, &mut quad_vao); + } + + Ok(FilterChain { + passes: filters, + quad_vao, + common: FilterCommon { + semantics, + preset, + original_history: vec![], + history: vec![], + feedback: vec![], + luts, + outputs: output_framebuffers, + quad_vbo, + } + }) + } + + pub fn frame(&mut self, count: u32, vp: &Viewport, input: GlImage, clear: bool) { + // + unsafe { + gl::BindFramebuffer(gl::FRAMEBUFFER, 0); + gl::BindVertexArray(self.quad_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 original = Texture { + image: input, + filter, + mip_filter: filter, + wrap_mode + }; + + let mut source = original.clone(); + + let passes_len = self.passes.len(); + let (pass, last) = self.passes.split_at_mut(passes_len - 1); + + for (index, pass) in pass.iter_mut().enumerate() { + { + let target = &mut self.common.outputs[index]; + let framebuffer_size = target.scale(pass.config.scaling.clone(), pass.get_format(), vp, &original, &source); + } + let target = &self.common.outputs[index]; + pass.draw(&self.common, None, count, 1, vp, &original, &source, &target); + let target = target.as_texture(pass.config.filter, pass.config.wrap_mode); + + // todo: update-pass-outputs + source = target; + // passes.build_semantics(&self, None, count, 1, vp, &original, &source); + } + + assert_eq!(last.len(), 1); + for pass in last { + source.filter = pass.config.filter; + source.mip_filter = pass.config.filter; + pass.draw(&self.common, None, count, 1, vp, &original, &source, &vp.output); + } + + 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 d5ab0a7..b46605a 100644 --- a/librashader-runtime-gl/src/filter_pass.rs +++ b/librashader-runtime-gl/src/filter_pass.rs @@ -10,9 +10,9 @@ use rustc_hash::FxHashMap; use librashader::{ShaderFormat, ShaderSource}; use librashader_presets::{Scale2D, ScaleType, Scaling, ShaderPassConfig, ShaderPreset}; use librashader_reflect::reflect::semantics::{MemberOffset, SemanticMap, TextureImage, TextureSemantics, VariableMeta, VariableSemantics}; -use crate::{FilterChain, FilterCommon}; use crate::framebuffer::Framebuffer; use crate::binding::{UniformBinding, UniformLocation, VariableLocation}; +use crate::filter_chain::{FilterChain, FilterCommon}; use crate::util::{GlImage, RingBuffer, Size, Texture, Viewport}; pub struct FilterPass { diff --git a/librashader-runtime-gl/src/hello_triangle.rs b/librashader-runtime-gl/src/hello_triangle.rs index 2655c28..7a2d6f9 100644 --- a/librashader-runtime-gl/src/hello_triangle.rs +++ b/librashader-runtime-gl/src/hello_triangle.rs @@ -7,7 +7,7 @@ use glfw::{Context, Glfw, Window, WindowEvent}; use gl; use gl::types::{GLchar, GLenum, GLint, GLsizei, GLuint}; use glfw::Key::P; -use crate::FilterChain; +use crate::filter_chain::FilterChain; use crate::framebuffer::Framebuffer; use crate::util::{GlImage, Size, Viewport}; diff --git a/librashader-runtime-gl/src/lib.rs b/librashader-runtime-gl/src/lib.rs index d7cfc1d..d6f9850 100644 --- a/librashader-runtime-gl/src/lib.rs +++ b/librashader-runtime-gl/src/lib.rs @@ -1,11 +1,11 @@ #![feature(strict_provenance)] mod hello_triangle; -mod filter; mod filter_pass; mod util; mod framebuffer; mod binding; +mod filter_chain; use std::collections::HashMap; use std::error::Error; @@ -34,441 +34,10 @@ use binding::{UniformLocation, VariableLocation}; use util::{GlImage, RingBuffer, Size, Texture, Viewport}; use crate::binding::UniformBinding; -unsafe fn gl_compile_shader(stage: GLenum, source: &str) -> GLuint { - let shader = gl::CreateShader(stage); - gl::ShaderSource(shader, 1, &source.as_bytes().as_ptr().cast(), std::ptr::null()); - gl::CompileShader(shader); - let mut compile_status = 0; - gl::GetShaderiv(shader, gl::COMPILE_STATUS, &mut compile_status); - - if compile_status == 0 { - panic!("failed to compile") - } - shader -} - -static QUAD_VBO_DATA: &'static [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(uniform_semantics: &mut FxHashMap, texture_semantics: &mut FxHashMap>, - config: &ShaderPassConfig) { - let Some(alias) = &config.alias else { - return; - }; - - // Ignore empty aliases - if alias.trim().is_empty() { - return; - } - - let index = config.id as usize; - - // PassOutput - texture_semantics.insert(alias.clone(), SemanticMap { - semantics: TextureSemantics::PassOutput, - index - }); - uniform_semantics.insert(format!("{alias}Size"), UniformSemantic::Texture(SemanticMap { - semantics: TextureSemantics::PassOutput, - index - })); - - // PassFeedback - texture_semantics.insert(format!("{alias}Feedback"), SemanticMap { - semantics: TextureSemantics::PassFeedback, - index - }); - uniform_semantics.insert(format!("{alias}FeedbackSize"), UniformSemantic::Texture(SemanticMap { - semantics: TextureSemantics::PassFeedback, - index - })); - } - - fn reflect_uniform_location(pipeline: GLuint, meta: &impl UniformMeta) -> VariableLocation { - // todo: support both ubo and pushco - // todo: fix this. - match meta.offset() { - MemberOffset::Ubo(_) => { - let vert_name = format!("LIBRA_UBO_VERTEX_INSTANCE.{}\0", meta.id()); - let frag_name = format!("LIBRA_UBO_FRAGMENT_INSTANCE.{}\0", meta.id()); - unsafe { - let vertex = gl::GetUniformLocation(pipeline, vert_name.as_ptr().cast()); - let fragment = gl::GetUniformLocation(pipeline, frag_name.as_ptr().cast()); - - VariableLocation::Ubo(UniformLocation { - vertex, - fragment - }) - } - } - MemberOffset::PushConstant(_) => { - let vert_name = format!("LIBRA_PUSH_VERTEX_INSTANCE.{}\0", meta.id()); - let frag_name = format!("LIBRA_PUSH_FRAGMENT_INSTANCE.{}\0", meta.id()); - unsafe { - let vertex = gl::GetUniformLocation(pipeline, vert_name.as_ptr().cast()); - let fragment = gl::GetUniformLocation(pipeline, frag_name.as_ptr().cast()); - - VariableLocation::Push(UniformLocation { - vertex, - fragment - }) - } - } - } - } - -} - -pub struct FilterChain { - passes: Vec, - common: FilterCommon, - pub quad_vao: GLuint, -} - -pub struct FilterCommon { - semantics: ReflectSemantics, - preset: ShaderPreset, - original_history: Vec, - history: Vec, - feedback: Vec, - luts: FxHashMap, - outputs: Vec, - pub quad_vbo: GLuint, -} - -impl FilterChain { - pub fn load(path: impl AsRef) -> Result> { - let preset = librashader_presets::ShaderPreset::try_parse(path)?; - let mut uniform_semantics: FxHashMap = Default::default(); - let mut texture_semantics: FxHashMap> = Default::default(); - - let mut passes: Vec<(&ShaderPassConfig, ShaderSource, _)> = preset.shaders.iter() - .map(|shader| { - eprintln!("[gl] loading {}", &shader.name.display()); - let source: ShaderSource = librashader_preprocess::load_shader_source(&shader.name) - .unwrap(); - - let spirv = librashader_reflect::front::shaderc::compile_spirv(&source) - .unwrap(); - let mut reflect = GLSL::from_compilation(spirv).unwrap(); - - for parameter in source.parameters.iter() { - uniform_semantics.insert(parameter.id.clone(), UniformSemantic::Variable(SemanticMap { - semantics: VariableSemantics::FloatParameter, - index: () - })); - } - - (shader, source, reflect) - }).collect(); - - // todo: this can probably be extracted out. - - for details in &passes { - FilterChain::load_pass_semantics(&mut uniform_semantics, &mut texture_semantics, details.0) - } - - // add lut params - for (index, texture) in preset.textures.iter().enumerate() { - texture_semantics.insert(texture.name.clone(), SemanticMap { - semantics: TextureSemantics::User, - index - }); - - uniform_semantics.insert(format!("{}Size", texture.name), UniformSemantic::Texture(SemanticMap { - semantics: TextureSemantics::User, - index - })); - } - - let semantics = ReflectSemantics { - uniform_semantics, - non_uniform_semantics: texture_semantics - }; - - let mut filters = Vec::new(); - let mut output_framebuffers = Vec::new(); - - // initialize passes - for (index, (config, source, mut reflect)) in passes.into_iter().enumerate() { - let mut semantics = semantics.clone(); - - let reflection = reflect.reflect(index, &semantics)?; - let glsl = reflect.compile(GlVersion::V4_60)?; - - let vertex_resources = glsl.context.compiler.vertex.get_shader_resources()?; - - // todo: split this out. - let (program, ubo_location) = unsafe { - let vertex = gl_compile_shader(gl::VERTEX_SHADER, glsl.vertex.as_str()); - let fragment = gl_compile_shader(gl::FRAGMENT_SHADER, glsl.fragment.as_str()); - - let program = gl::CreateProgram(); - gl::AttachShader(program, vertex); - gl::AttachShader(program, fragment); - - for res in &vertex_resources.stage_inputs { - let loc = glsl.context.compiler.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); - gl::DeleteShader(vertex); - gl::DeleteShader(fragment); - - let mut status = 0; - gl::GetProgramiv(program, gl::LINK_STATUS, &mut status); - if status != 1 { - panic!("failed to link program") - } - - gl::UseProgram(program); - - for binding in &glsl.context.sampler_bindings { - let loc_name = format!("LIBRA_TEXTURE_{}\0", *binding); - let location = gl::GetUniformLocation(program, loc_name.as_str().as_ptr().cast()); - if location >= 0 { - // eprintln!("setting sampler {location} to sample from {binding}"); - gl::Uniform1i(location, *binding as GLint); - } - } - - gl::UseProgram(0); - (program, UniformLocation { - vertex: gl::GetUniformBlockIndex(program, b"LIBRA_UBO_VERTEX\0".as_ptr().cast()), - fragment: gl::GetUniformBlockIndex(program, b"LIBRA_UBO_FRAGMENT\0".as_ptr().cast()), - }) - }; - - let ubo_ring = if let Some(ubo) = &reflection.ubo { - let size = ubo.size; - let mut ring: RingBuffer = RingBuffer::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); - } - Some(ring) - } else { - None - }; - - let uniform_buffer = vec![0; reflection.ubo.as_ref().map(|ubo| ubo.size as usize).unwrap_or(0)].into_boxed_slice(); - let push_buffer = vec![0; reflection.push_constant.as_ref().map(|push| push.size as usize).unwrap_or(0)].into_boxed_slice(); - - // todo: reflect indexed parameters - let mut locations = FxHashMap::default(); - for param in reflection.meta.parameter_meta.values() { - locations.insert(UniformBinding::Parameter(param.id.clone()), - (FilterChain::reflect_uniform_location(program, param), param.offset)); - } - - for (semantics, param) in &reflection.meta.variable_meta { - locations.insert(UniformBinding::SemanticVariable(semantics.clone()), - (FilterChain::reflect_uniform_location(program, param), param.offset)); - } - - for (semantics, param) in &reflection.meta.texture_size_meta { - locations.insert(UniformBinding::TextureSize(semantics.clone()), - (FilterChain::reflect_uniform_location(program, param), param.offset)); - } - - // need output framebuffers. - output_framebuffers.push(Framebuffer::new(1)); - - // eprintln!("{:#?}", semantics); - // eprintln!("{:#?}", reflection.meta); - // eprintln!("{:#?}", locations); - // eprintln!("{:#?}", reflection.push_constant); - // eprintln!("====fragment===="); - // eprintln!("{:#}", glsl.fragment); - // eprintln!("====vertex===="); - // eprintln!("{:#}", glsl.vertex); - - filters.push(FilterPass { - reflection, - compiled: glsl, - program, - ubo_location, - ubo_ring, - uniform_buffer, - push_buffer, - variable_bindings: locations, - source, - // no idea if this works. - // retroarch checks if feedback frames are used but we'll just init it tbh. - feedback_framebuffer: Framebuffer::new(1), - config: config.clone() - }); - } - - // load luts - let mut luts = FxHashMap::default(); - - for (index, texture) in preset.textures.iter().enumerate() { - let image = Image::load(&texture.path)?; - let levels = if texture.mipmap { - util::calc_miplevel(image.width, image.height) - } 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.width as GLsizei, image.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.width as GLsizei, image.height as GLsizei, - gl::RGBA, gl::UNSIGNED_BYTE, image.bytes.as_ptr().cast()); - - let mipmap = levels > 1; - let linear = texture.filter_mode == FilterMode::Linear; - - // set mipmaps and wrapping - - if mipmap { - gl::GenerateMipmap(gl::TEXTURE_2D); - } - - gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_S, GLenum::from(texture.wrap_mode) as GLint); - gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_T, GLenum::from(texture.wrap_mode) as GLint); - - if !linear { - gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::NEAREST as GLint); - gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::NEAREST as GLint); - } else { - gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::LINEAR as GLint); - if mipmap { - gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, - gl::LINEAR_MIPMAP_LINEAR as GLint); - } else { - gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, - gl::LINEAR as GLint); - } - } - - gl::BindTexture(gl::TEXTURE_2D, 0); - } - - luts.insert(index, Texture { - image: GlImage { - handle, - format: gl::RGBA8, - size: Size { - width: image.width, - height: image.height - }, - padded_size: Size::default() - }, - filter: texture.filter_mode, - mip_filter: texture.filter_mode, - wrap_mode: texture.wrap_mode - }); - } - - 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 mut quad_vao = 0; - unsafe { - gl::GenVertexArrays(1, &mut quad_vao); - } - - Ok(FilterChain { - passes: filters, - quad_vao, - common: FilterCommon { - semantics, - preset, - original_history: vec![], - history: vec![], - feedback: vec![], - luts, - outputs: output_framebuffers, - quad_vbo, - } - }) - } - - - // how much info do we actually need? - pub fn frame(&mut self, count: u32, vp: &Viewport, input: GlImage, clear: bool) { - // - unsafe { - gl::BindFramebuffer(gl::FRAMEBUFFER, 0); - gl::BindVertexArray(self.quad_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 original = Texture { - image: input, - filter, - mip_filter: filter, - wrap_mode - }; - - let mut source = original.clone(); - - let passes_len = self.passes.len(); - let (pass, last) = self.passes.split_at_mut(passes_len - 1); - - for (index, pass) in pass.iter_mut().enumerate() { - { - let target = &mut self.common.outputs[index]; - let framebuffer_size = target.scale(pass.config.scaling.clone(), pass.get_format(), vp, &original, &source); - } - let target = &self.common.outputs[index]; - pass.draw(&self.common, None, count, 1, vp, &original, &source, &target); - let target = target.as_texture(pass.config.filter, pass.config.wrap_mode); - - // todo: update-pass-outputs - source = target; - // passes.build_semantics(&self, None, count, 1, vp, &original, &source); - } - - assert_eq!(last.len(), 1); - for pass in last { - source.filter = pass.config.filter; - source.mip_filter = pass.config.filter; - pass.draw(&self.common, None, count, 1, vp, &original, &source, &vp.output); - } - - unsafe { - gl::BindFramebuffer(gl::FRAMEBUFFER, 0); - gl::BindVertexArray(0); - } - // todo: deal with the mess that is frame history - } -} - #[cfg(test)] mod tests { + use crate::filter_chain::FilterChain; use super::*; #[test] diff --git a/librashader-runtime-gl/src/util.rs b/librashader-runtime-gl/src/util.rs index 160fdc4..0d7cebd 100644 --- a/librashader-runtime-gl/src/util.rs +++ b/librashader-runtime-gl/src/util.rs @@ -78,3 +78,16 @@ where T: Copy, T: Default &mut self.items } } + +pub unsafe fn gl_compile_shader(stage: GLenum, source: &str) -> GLuint { + let shader = gl::CreateShader(stage); + gl::ShaderSource(shader, 1, &source.as_bytes().as_ptr().cast(), std::ptr::null()); + gl::CompileShader(shader); + let mut compile_status = 0; + gl::GetShaderiv(shader, gl::COMPILE_STATUS, &mut compile_status); + + if compile_status == 0 { + panic!("failed to compile") + } + shader +}