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

492 lines
18 KiB
Rust
Raw Normal View History

2022-11-19 17:55:49 +11:00
#![feature(strict_provenance)]
mod hello_triangle;
2022-11-14 16:14:05 +11:00
mod filter;
2022-11-14 17:49:51 +11:00
mod filter_pass;
mod util;
mod framebuffer;
2022-11-20 10:48:54 +11:00
mod binding;
2022-11-09 17:11:25 +11:00
use std::collections::HashMap;
use std::error::Error;
2022-11-14 16:14:05 +11:00
use std::iter::Filter;
2022-11-20 14:03:58 +11:00
use std::ops::Deref;
2022-11-09 17:11:25 +11:00
use std::path::Path;
2022-11-14 16:14:05 +11:00
use gl::types::{GLenum, GLint, GLsizei, GLsizeiptr, GLuint};
use glfw::Key::P;
2022-11-09 17:11:25 +11:00
use rustc_hash::FxHashMap;
2022-11-12 17:23:49 +11:00
use spirv_cross::spirv::Decoration;
2022-11-14 17:49:51 +11:00
use filter_pass::FilterPass;
use framebuffer::Framebuffer;
2022-11-14 17:49:51 +11:00
use librashader::{FilterMode, ShaderFormat, ShaderSource, WrapMode};
2022-11-17 16:08:11 +11:00
use librashader::image::Image;
2022-11-14 16:14:05 +11:00
use librashader_presets::{ShaderPassConfig, ShaderPreset};
2022-11-12 17:23:49 +11:00
use librashader_reflect::back::{CompileShader, ShaderCompilerOutput};
use librashader_reflect::back::cross::{GlslangGlslContext, GlVersion};
use librashader_reflect::back::targets::{FromCompilation, GLSL};
2022-11-09 17:11:25 +11:00
use librashader_reflect::front::shaderc::GlslangCompilation;
use librashader_reflect::reflect::cross::CrossReflect;
2022-11-12 17:23:49 +11:00
use librashader_reflect::reflect::{ReflectSemantics, ReflectShader, ShaderReflection, UniformSemantic};
2022-11-20 10:48:54 +11:00
use librashader_reflect::reflect::semantics::{MemberOffset, SemanticMap, TextureSemantics, UniformMeta, VariableMeta, VariableSemantics};
use librashader_reflect::reflect::{TextureSemanticMap, VariableSemanticMap};
2022-11-20 10:48:54 +11:00
use binding::{UniformLocation, VariableLocation};
use util::{GlImage, RingBuffer, Size, Texture, Viewport};
use crate::binding::UniformBinding;
2022-11-09 17:11:25 +11:00
2022-11-14 16:14:05 +11:00
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
}
2022-11-19 17:55:49 +11:00
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,
];
2022-11-14 17:49:51 +11:00
impl FilterChain {
fn load_pass_semantics(uniform_semantics: &mut FxHashMap<String, UniformSemantic>, texture_semantics: &mut FxHashMap<String, SemanticMap<TextureSemantics>>,
2022-11-09 17:11:25 +11:00
config: &ShaderPassConfig) {
2022-11-14 17:49:51 +11:00
let Some(alias) = &config.alias else {
return;
};
2022-11-14 16:14:05 +11:00
2022-11-14 17:49:51 +11:00
// Ignore empty aliases
if alias.trim().is_empty() {
return;
2022-11-14 16:14:05 +11:00
}
2022-11-20 10:48:54 +11:00
let index = config.id as usize;
2022-11-14 17:49:51 +11:00
// 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
}));
2022-11-14 16:14:05 +11:00
}
2022-11-20 10:48:54 +11:00
fn reflect_uniform_location(pipeline: GLuint, meta: &impl UniformMeta) -> VariableLocation {
2022-11-14 17:49:51 +11:00
// todo: support both ubo and pushco
// todo: fix this.
2022-11-20 10:48:54 +11:00
match meta.offset() {
2022-11-14 17:49:51 +11:00
MemberOffset::Ubo(_) => {
2022-11-20 10:48:54 +11:00
let vert_name = format!("LIBRA_UBO_VERTEX_INSTANCE.{}\0", meta.id());
let frag_name = format!("LIBRA_UBO_FRAGMENT_INSTANCE.{}\0", meta.id());
2022-11-14 17:49:51 +11:00
unsafe {
let vertex = gl::GetUniformLocation(pipeline, vert_name.as_ptr().cast());
let fragment = gl::GetUniformLocation(pipeline, frag_name.as_ptr().cast());
2022-11-14 16:14:05 +11:00
2022-11-20 10:48:54 +11:00
VariableLocation::Ubo(UniformLocation {
2022-11-14 17:49:51 +11:00
vertex,
fragment
})
}
2022-11-14 16:14:05 +11:00
}
2022-11-14 17:49:51 +11:00
MemberOffset::PushConstant(_) => {
2022-11-20 10:48:54 +11:00
let vert_name = format!("LIBRA_PUSH_VERTEX_INSTANCE.{}\0", meta.id());
let frag_name = format!("LIBRA_PUSH_FRAGMENT_INSTANCE.{}\0", meta.id());
2022-11-14 17:49:51 +11:00
unsafe {
let vertex = gl::GetUniformLocation(pipeline, vert_name.as_ptr().cast());
let fragment = gl::GetUniformLocation(pipeline, frag_name.as_ptr().cast());
2022-11-14 16:14:05 +11:00
2022-11-20 10:48:54 +11:00
VariableLocation::Push(UniformLocation {
2022-11-14 17:49:51 +11:00
vertex,
fragment
})
2022-11-14 16:14:05 +11:00
}
}
}
}
}
2022-11-14 17:49:51 +11:00
2022-11-12 17:23:49 +11:00
pub struct FilterChain {
2022-11-14 16:14:05 +11:00
passes: Vec<FilterPass>,
2022-11-19 17:55:49 +11:00
common: FilterCommon,
pub quad_vao: GLuint,
}
pub struct FilterCommon {
2022-11-14 16:14:05 +11:00
semantics: ReflectSemantics,
preset: ShaderPreset,
original_history: Vec<Framebuffer>,
2022-11-17 17:21:29 +11:00
history: Vec<Texture>,
feedback: Vec<Texture>,
2022-11-20 10:48:54 +11:00
luts: FxHashMap<usize, Texture>,
2022-11-20 14:03:58 +11:00
outputs: Vec<Framebuffer>,
2022-11-19 17:55:49 +11:00
pub quad_vbo: GLuint,
2022-11-12 17:23:49 +11:00
}
2022-11-14 16:14:05 +11:00
impl FilterChain {
pub fn load(path: impl AsRef<Path>) -> Result<FilterChain, Box<dyn Error>> {
let preset = librashader_presets::ShaderPreset::try_parse(path)?;
let mut uniform_semantics: FxHashMap<String, UniformSemantic> = Default::default();
let mut texture_semantics: FxHashMap<String, SemanticMap<TextureSemantics>> = 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: ()
}));
}
2022-11-09 17:11:25 +11:00
2022-11-14 16:14:05 +11:00
(shader, source, reflect)
}).collect();
2022-11-09 17:11:25 +11:00
2022-11-14 16:14:05 +11:00
// todo: this can probably be extracted out.
2022-11-09 17:11:25 +11:00
2022-11-14 16:14:05 +11:00
for details in &passes {
2022-11-14 17:49:51 +11:00
FilterChain::load_pass_semantics(&mut uniform_semantics, &mut texture_semantics, details.0)
2022-11-14 16:14:05 +11:00
}
2022-11-09 17:11:25 +11:00
2022-11-14 16:14:05 +11:00
// add lut params
for (index, texture) in preset.textures.iter().enumerate() {
texture_semantics.insert(texture.name.clone(), SemanticMap {
semantics: TextureSemantics::User,
2022-11-20 10:48:54 +11:00
index
2022-11-14 16:14:05 +11:00
});
uniform_semantics.insert(format!("{}Size", texture.name), UniformSemantic::Texture(SemanticMap {
semantics: TextureSemantics::User,
2022-11-20 10:48:54 +11:00
index
2022-11-14 16:14:05 +11:00
}));
}
2022-11-09 17:11:25 +11:00
2022-11-14 16:14:05 +11:00
let semantics = ReflectSemantics {
uniform_semantics,
non_uniform_semantics: texture_semantics
};
2022-11-09 17:11:25 +11:00
2022-11-14 16:14:05 +11:00
let mut filters = Vec::new();
2022-11-20 14:03:58 +11:00
let mut output_framebuffers = Vec::new();
2022-11-14 16:14:05 +11:00
// initialize passes
for (index, (config, source, mut reflect)) in passes.into_iter().enumerate() {
let mut semantics = semantics.clone();
2022-11-20 10:48:54 +11:00
let reflection = reflect.reflect(index, &semantics)?;
2022-11-14 16:14:05 +11:00
let glsl = reflect.compile(GlVersion::V4_60)?;
2022-11-14 16:14:05 +11:00
let vertex_resources = glsl.context.compiler.vertex.get_shader_resources()?;
2022-11-14 16:14:05 +11:00
// 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());
2022-11-09 17:11:25 +11:00
2022-11-14 16:14:05 +11:00
let program = gl::CreateProgram();
gl::AttachShader(program, vertex);
gl::AttachShader(program, fragment);
2022-11-12 17:23:49 +11:00
2022-11-14 16:14:05 +11:00
for res in &vertex_resources.stage_inputs {
let loc = glsl.context.compiler.vertex.get_decoration(res.id, Decoration::Location)?;
2022-11-20 10:48:54 +11:00
let loc_name = format!("LIBRA_ATTRIBUTE_{loc}\0");
2022-11-19 17:55:49 +11:00
eprintln!("{loc_name}");
2022-11-14 16:14:05 +11:00
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")
}
2022-11-12 17:23:49 +11:00
2022-11-20 14:03:58 +11:00
gl::UseProgram(program);
for binding in &glsl.context.sampler_bindings {
2022-11-20 10:48:54 +11:00
let loc_name = format!("LIBRA_TEXTURE_{}\0", *binding);
2022-11-20 14:03:58 +11:00
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);
2022-11-14 16:14:05 +11:00
}
}
2022-11-12 17:23:49 +11:00
2022-11-20 14:03:58 +11:00
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()),
})
2022-11-14 16:14:05 +11:00
};
2022-11-12 17:23:49 +11:00
2022-11-14 16:14:05 +11:00
let ubo_ring = if let Some(ubo) = &reflection.ubo {
let size = ubo.size;
let mut ring: RingBuffer<GLuint, 16> = RingBuffer::new();
2022-11-12 17:23:49 +11:00
unsafe {
2022-11-14 17:49:51 +11:00
gl::GenBuffers(16, ring.items_mut().as_mut_ptr());
for buffer in ring.items() {
2022-11-14 16:14:05 +11:00
gl::BindBuffer(gl::UNIFORM_BUFFER, *buffer);
gl::BufferData(gl::UNIFORM_BUFFER, size as GLsizeiptr, std::ptr::null(), gl::STREAM_DRAW);
2022-11-12 17:23:49 +11:00
}
2022-11-14 16:14:05 +11:00
gl::BindBuffer(gl::UNIFORM_BUFFER, 0);
2022-11-12 17:23:49 +11:00
}
2022-11-14 16:14:05 +11:00
Some(ring)
} else {
None
};
2022-11-14 17:49:51 +11:00
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();
2022-11-14 16:14:05 +11:00
// todo: reflect indexed parameters
let mut locations = FxHashMap::default();
for param in reflection.meta.parameter_meta.values() {
2022-11-20 10:48:54 +11:00
locations.insert(UniformBinding::Parameter(param.id.clone()),
(FilterChain::reflect_uniform_location(program, param), param.offset));
2022-11-12 17:23:49 +11:00
}
2022-11-20 10:48:54 +11:00
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));
2022-11-12 17:23:49 +11:00
}
2022-11-20 14:03:58 +11:00
// need output framebuffers.
output_framebuffers.push(Framebuffer::new(1));
2022-11-20 10:48:54 +11:00
2022-11-14 16:14:05 +11:00
// eprintln!("{:#?}", semantics);
2022-11-20 14:03:58 +11:00
// eprintln!("{:#?}", reflection.meta);
// eprintln!("{:#?}", locations);
// eprintln!("{:#?}", reflection.push_constant);
2022-11-14 16:14:05 +11:00
// 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,
2022-11-20 10:48:54 +11:00
variable_bindings: locations,
2022-11-17 16:08:11 +11:00
source,
2022-11-14 16:14:05 +11:00
// no idea if this works.
// retroarch checks if feedback frames are used but we'll just init it tbh.
2022-11-17 16:08:11 +11:00
feedback_framebuffer: Framebuffer::new(1),
2022-11-17 17:21:29 +11:00
config: config.clone()
2022-11-14 16:14:05 +11:00
});
2022-11-12 17:23:49 +11:00
}
2022-11-17 16:08:11 +11:00
// load luts
let mut luts = FxHashMap::default();
2022-11-20 10:48:54 +11:00
for (index, texture) in preset.textures.iter().enumerate() {
2022-11-17 16:08:11 +11:00
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);
}
2022-11-20 10:48:54 +11:00
luts.insert(index, Texture {
2022-11-17 17:21:29 +11:00
image: GlImage {
2022-11-17 16:08:11 +11:00
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
});
}
2022-11-19 17:55:49 +11:00
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);
}
2022-11-14 16:14:05 +11:00
Ok(FilterChain {
passes: filters,
2022-11-19 17:55:49 +11:00
quad_vao,
common: FilterCommon {
semantics,
preset,
original_history: vec![],
history: vec![],
feedback: vec![],
luts,
2022-11-20 14:03:58 +11:00
outputs: output_framebuffers,
2022-11-19 17:55:49 +11:00
quad_vbo,
}
2022-11-14 16:14:05 +11:00
})
}
2022-11-09 17:11:25 +11:00
2022-11-14 16:14:05 +11:00
// how much info do we actually need?
2022-11-19 17:55:49 +11:00
pub fn frame(&mut self, count: u32, vp: &Viewport, input: GlImage, clear: bool) {
//
2022-11-19 18:28:22 +11:00
unsafe {
gl::BindFramebuffer(gl::FRAMEBUFFER, 0);
gl::BindVertexArray(self.quad_vao);
}
2022-11-19 17:55:49 +11:00
// 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();
2022-11-17 17:21:29 +11:00
let original = Texture {
image: input,
filter,
mip_filter: filter,
wrap_mode
};
2022-11-09 17:11:25 +11:00
2022-11-17 17:21:29 +11:00
let mut source = original.clone();
2022-11-20 14:03:58 +11:00
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;
2022-11-17 17:21:29 +11:00
// passes.build_semantics(&self, None, count, 1, vp, &original, &source);
}
2022-11-20 14:03:58 +11:00
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);
}
2022-11-19 17:55:49 +11:00
2022-11-19 18:28:22 +11:00
unsafe {
gl::BindFramebuffer(gl::FRAMEBUFFER, 0);
gl::BindVertexArray(0);
}
2022-11-14 17:49:51 +11:00
// todo: deal with the mess that is frame history
}
2022-11-11 18:26:57 +11:00
}
2022-11-14 16:14:05 +11:00
2022-11-09 17:11:25 +11:00
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn triangle() {
let (glfw, window, events, shader, vao) = hello_triangle::setup();
2022-11-19 17:55:49 +11:00
let mut filter = FilterChain::load("../test/basic.slangp").unwrap();
2022-11-17 17:21:29 +11:00
// FilterChain::load("../test/slang-shaders/crt/crt-royale.slangp").unwrap();
2022-11-14 16:14:05 +11:00
2022-11-19 17:55:49 +11:00
hello_triangle::do_loop(glfw, window, events, shader, vao, &mut filter );
}
2022-11-12 17:23:49 +11:00
// #[test]
// fn load_preset() {
//
// load("../test/basic.slangp")
// .unwrap();
// }
2022-11-09 17:11:25 +11:00
}