gl: clean up the huge lib.rs file
This commit is contained in:
parent
848d87021c
commit
d37fc0ccb5
7
Cargo.lock
generated
7
Cargo.lock
generated
|
@ -97,6 +97,12 @@ version = "0.6.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2c676a478f63e9fa2dd5368a42f28bba0d6c560b775f38583c8bbaa7fcd67c9c"
|
checksum = "2c676a478f63e9fa2dd5368a42f28bba0d6c560b775f38583c8bbaa7fcd67c9c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bytemuck"
|
||||||
|
version = "1.12.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "aaa3a8d9a1ca92e282c96a32d6511b695d7d994d1d102ba85d279f9b2756947f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "byteorder"
|
name = "byteorder"
|
||||||
version = "1.4.3"
|
version = "1.4.3"
|
||||||
|
@ -512,6 +518,7 @@ dependencies = [
|
||||||
name = "librashader-runtime-gl"
|
name = "librashader-runtime-gl"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"bytemuck",
|
||||||
"gl",
|
"gl",
|
||||||
"glfw",
|
"glfw",
|
||||||
"librashader",
|
"librashader",
|
||||||
|
|
26
README.md
26
README.md
|
@ -1,5 +1,27 @@
|
||||||
# librashader
|
# librashader
|
||||||
|
|
||||||
Pure Rust implementation of the RetroArch shader pipeline as a dynamic library.
|
A preprocessor, compiler, and runtime for RetroArch 'slang' shaders, rewritten in pure Rust.
|
||||||
|
|
||||||
Heavily WIP
|
Heavily WIP.
|
||||||
|
|
||||||
|
## License
|
||||||
|
**There is not yet a functioning implementation of librashader but this section outlines its licensing goals in contrast to
|
||||||
|
RetroArch.**
|
||||||
|
|
||||||
|
While librashader is an independent reimplementation of the RetroArch shader pipeline, referencing the RetroArch source
|
||||||
|
code was indispensable to its creation. As it is therefore considered a derivative work, the core parts of librashader
|
||||||
|
such as the preprocessor, the preset parser, the reflection library, and the runtimes, are all licensed under GPLv3.
|
||||||
|
|
||||||
|
The librashader C API, i.e. its headers and definitions, *not its implementation in `librashader_capi`*,
|
||||||
|
are unique to librashader and are more permissively licensed, and may allow you to use librashader in your permissively
|
||||||
|
licensed or proprietary project.
|
||||||
|
|
||||||
|
While the code for `librashader_capi` (`librashader.so` and `rashader.dll`) is still under GPLv3,
|
||||||
|
you may use librashader in a non-GPL work by linking against the MIT licensed `librashader_ld`,
|
||||||
|
which implements the librashader C API, and thunks its calls to any `librashader.so` or `rashader.dll`
|
||||||
|
library found in the load path, *provided that `librashader.so` or `rashader.dll` are distributed under the restrictions
|
||||||
|
of GPLv3*.
|
||||||
|
|
||||||
|
Note that if your project is not compatible with GPLv3, you **can not distribute `librashader.so` or `rashader.dll`**
|
||||||
|
alongside your project, **only `librashader-ld.so` or `rashader-ld.dll`**, which will do nothing without a librashader
|
||||||
|
implementation in the load path. The end user must obtain the implementation of librashader themselves.
|
|
@ -22,6 +22,15 @@ pub enum VariableSemantics {
|
||||||
FloatParameter = 5,
|
FloatParameter = 5,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl VariableSemantics {
|
||||||
|
pub const fn semantics(self) -> SemanticMap<VariableSemantics, ()> {
|
||||||
|
SemanticMap {
|
||||||
|
semantics: self,
|
||||||
|
index: ()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Copy, Clone, Hash)]
|
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Copy, Clone, Hash)]
|
||||||
#[repr(i32)]
|
#[repr(i32)]
|
||||||
pub enum TextureSemantics {
|
pub enum TextureSemantics {
|
||||||
|
@ -68,6 +77,13 @@ impl TextureSemantics {
|
||||||
pub fn is_array(&self) -> bool {
|
pub fn is_array(&self) -> bool {
|
||||||
!matches!(self, TextureSemantics::Original | TextureSemantics::Source)
|
!matches!(self, TextureSemantics::Original | TextureSemantics::Source)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub const fn semantics(self, index: u32) -> SemanticMap<TextureSemantics, u32> {
|
||||||
|
SemanticMap {
|
||||||
|
semantics: self,
|
||||||
|
index
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct TypeInfo {
|
pub struct TypeInfo {
|
||||||
|
|
|
@ -13,4 +13,5 @@ edition = "2021"
|
||||||
spirv_cross = "0.23.1"
|
spirv_cross = "0.23.1"
|
||||||
rustc-hash = "1.1.0"
|
rustc-hash = "1.1.0"
|
||||||
gl = "0.14.0"
|
gl = "0.14.0"
|
||||||
glfw = "0.47.0"
|
glfw = "0.47.0"
|
||||||
|
bytemuck = "1.12.3"
|
81
librashader-runtime-gl/src/filter_pass.rs
Normal file
81
librashader-runtime-gl/src/filter_pass.rs
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
use std::iter::Filter;
|
||||||
|
use gl::types::{GLint, GLuint};
|
||||||
|
use librashader_reflect::back::cross::GlslangGlslContext;
|
||||||
|
use librashader_reflect::back::ShaderCompilerOutput;
|
||||||
|
use librashader_reflect::reflect::ShaderReflection;
|
||||||
|
use librashader_reflect::reflect::TextureSemanticMap;
|
||||||
|
use librashader_reflect::reflect::VariableSemanticMap;
|
||||||
|
use rustc_hash::FxHashMap;
|
||||||
|
use librashader_reflect::reflect::semantics::{MemberOffset, SemanticMap, TextureSemantics, VariableMeta, VariableSemantics};
|
||||||
|
use crate::framebuffer::Framebuffer;
|
||||||
|
use crate::util::{Location, VariableLocation, RingBuffer, Size, Texture};
|
||||||
|
|
||||||
|
pub struct FilterPass {
|
||||||
|
pub reflection: ShaderReflection,
|
||||||
|
pub compiled: ShaderCompilerOutput<String, GlslangGlslContext>,
|
||||||
|
pub program: GLuint,
|
||||||
|
pub ubo_location: Location<GLuint>,
|
||||||
|
pub ubo_ring: Option<RingBuffer<GLuint, 16>>,
|
||||||
|
pub uniform_buffer: Box<[u8]>,
|
||||||
|
pub push_buffer: Box<[u8]>,
|
||||||
|
pub locations: FxHashMap<String, VariableLocation>,
|
||||||
|
pub framebuffer: Framebuffer,
|
||||||
|
pub feedback_framebuffer: Framebuffer,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FilterPass {
|
||||||
|
fn build_mvp(buffer: &mut [u8], mvp: &[f32]) {
|
||||||
|
let mvp = bytemuck::cast_slice(mvp);
|
||||||
|
buffer.copy_from_slice(mvp);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_vec4(buffer: &mut [u8], width: u32, height: u32) {
|
||||||
|
let vec4 = [width as f32, height as f32, 1.0 / width as f32, 1.0/ height as f32];
|
||||||
|
let vec4 = bytemuck::cast_slice(&vec4);
|
||||||
|
|
||||||
|
buffer.copy_from_slice(vec4);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_vec4_uniform(location: Location<GLint>, width: u32, height: u32) {
|
||||||
|
let vec4 = [width as f32, height as f32, 1.0 / width as f32, 1.0/ height as f32];
|
||||||
|
unsafe {
|
||||||
|
if location.vertex >= 0 {
|
||||||
|
gl::Uniform4fv(location.vertex, 1, vec4.as_ptr());
|
||||||
|
}
|
||||||
|
if location.fragment >= 0 {
|
||||||
|
gl::Uniform4fv(location.fragment, 1, vec4.as_ptr());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_semantics(&mut self, mvp: Option<&[f32]>, fb_size: Size, vp_size: Size, original: &Texture, source: &Texture) {
|
||||||
|
if let Some(variable) = self.reflection.meta.variable_meta.get(&VariableSemantics::MVP) {
|
||||||
|
let mvp = mvp.unwrap_or(&[
|
||||||
|
2f32, 0.0, 0.0, 0.0,
|
||||||
|
0.0, 2.0, 0.0, 0.0,
|
||||||
|
0.0, 0.0, 2.0, 0.0,
|
||||||
|
-1.0, -1.0, 0.0, 1.0
|
||||||
|
]);
|
||||||
|
|
||||||
|
let (buffer, offset) = match variable.offset {
|
||||||
|
MemberOffset::Ubo(offset) => (&mut self.uniform_buffer, offset),
|
||||||
|
MemberOffset::PushConstant(offset) => (&mut self.push_buffer, offset)
|
||||||
|
};
|
||||||
|
FilterPass::build_mvp(&mut buffer[offset..][..mvp.len()], mvp)
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(variable) = self.reflection.meta.variable_meta.get(&VariableSemantics::Output) {
|
||||||
|
let location = self.locations.get(&variable.id).expect("variable did not have location mapped").location();
|
||||||
|
if location.fragment >= 0 || location.vertex >= 0 {
|
||||||
|
FilterPass::build_vec4_uniform(location, fb_size.width, fb_size.height);
|
||||||
|
} else {
|
||||||
|
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(&mut buffer[offset..][..4], fb_size.width, fb_size.height)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
126
librashader-runtime-gl/src/framebuffer.rs
Normal file
126
librashader-runtime-gl/src/framebuffer.rs
Normal file
|
@ -0,0 +1,126 @@
|
||||||
|
use gl::types::{GLenum, GLsizei, GLuint};
|
||||||
|
use librashader::ShaderFormat;
|
||||||
|
use crate::util;
|
||||||
|
use crate::util::Size;
|
||||||
|
|
||||||
|
pub struct Framebuffer {
|
||||||
|
pub image: GLuint,
|
||||||
|
pub size: Size,
|
||||||
|
pub format: GLenum,
|
||||||
|
pub max_levels: u32,
|
||||||
|
pub levels: u32,
|
||||||
|
pub framebuffer: GLuint,
|
||||||
|
pub init: bool
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for Framebuffer {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
if self.framebuffer != 0 {
|
||||||
|
unsafe {
|
||||||
|
gl::DeleteFramebuffers(1, &self.framebuffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.image != 0 {
|
||||||
|
unsafe {
|
||||||
|
gl::DeleteTextures(1, &self.image);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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, framebuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
Framebuffer {
|
||||||
|
image: 0,
|
||||||
|
size: Size { width: 1, height: 1 },
|
||||||
|
format: 0,
|
||||||
|
max_levels,
|
||||||
|
levels: 0,
|
||||||
|
framebuffer,
|
||||||
|
init: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init(&mut self, mut size: Size, mut format: ShaderFormat) {
|
||||||
|
if format == ShaderFormat::Unknown {
|
||||||
|
format = ShaderFormat::R8G8B8A8Unorm;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.format = GLenum::from(format);
|
||||||
|
self.size = size;
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
gl::BindFramebuffer(gl::FRAMEBUFFER, self.framebuffer);
|
||||||
|
|
||||||
|
// 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(1, self.image);
|
||||||
|
|
||||||
|
if size.width == 0 {
|
||||||
|
size.width = 1;
|
||||||
|
}
|
||||||
|
if size.height == 0 {
|
||||||
|
size.height = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.levels = util::calc_miplevel(size.width, size.height);
|
||||||
|
if self.levels > self.max_levels {
|
||||||
|
self.levels = self.max_levels;
|
||||||
|
}
|
||||||
|
if self.levels == 0 {
|
||||||
|
self.levels = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
gl::TexStorage2D(gl::TEXTURE_2D, self.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(1, self.image);
|
||||||
|
|
||||||
|
self.levels = util::calc_miplevel(size.width, size.height);
|
||||||
|
if self.levels > self.max_levels {
|
||||||
|
self.levels = self.max_levels;
|
||||||
|
}
|
||||||
|
if self.levels == 0 {
|
||||||
|
self.levels = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
gl::TexStorage2D(gl::TEXTURE_2D, self.levels as GLsizei, gl::RGBA8, 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;
|
||||||
|
}
|
||||||
|
_ => panic!("failed to complete: {status}")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.init = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
gl::BindFramebuffer(gl::FRAMEBUFFER, 0);
|
||||||
|
gl::BindTexture(gl::TEXTURE_2D, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,8 @@
|
||||||
mod hello_triangle;
|
mod hello_triangle;
|
||||||
mod filter;
|
mod filter;
|
||||||
|
mod filter_pass;
|
||||||
|
mod util;
|
||||||
|
mod framebuffer;
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
@ -9,8 +12,10 @@ use gl::types::{GLenum, GLint, GLsizei, GLsizeiptr, GLuint};
|
||||||
use glfw::Key::P;
|
use glfw::Key::P;
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
use spirv_cross::spirv::Decoration;
|
use spirv_cross::spirv::Decoration;
|
||||||
|
use filter_pass::FilterPass;
|
||||||
|
use framebuffer::Framebuffer;
|
||||||
|
|
||||||
use librashader::{ShaderFormat, ShaderSource};
|
use librashader::{FilterMode, ShaderFormat, ShaderSource, WrapMode};
|
||||||
use librashader_presets::{ShaderPassConfig, ShaderPreset};
|
use librashader_presets::{ShaderPassConfig, ShaderPreset};
|
||||||
use librashader_reflect::back::{CompileShader, ShaderCompilerOutput};
|
use librashader_reflect::back::{CompileShader, ShaderCompilerOutput};
|
||||||
use librashader_reflect::back::cross::{GlslangGlslContext, GlVersion};
|
use librashader_reflect::back::cross::{GlslangGlslContext, GlVersion};
|
||||||
|
@ -20,6 +25,7 @@ use librashader_reflect::reflect::cross::CrossReflect;
|
||||||
use librashader_reflect::reflect::{ReflectSemantics, ReflectShader, ShaderReflection, UniformSemantic};
|
use librashader_reflect::reflect::{ReflectSemantics, ReflectShader, ShaderReflection, UniformSemantic};
|
||||||
use librashader_reflect::reflect::semantics::{MemberOffset, SemanticMap, TextureSemantics, VariableMeta, VariableSemantics};
|
use librashader_reflect::reflect::semantics::{MemberOffset, SemanticMap, TextureSemantics, VariableMeta, VariableSemantics};
|
||||||
use librashader_reflect::reflect::{TextureSemanticMap, VariableSemanticMap};
|
use librashader_reflect::reflect::{TextureSemanticMap, VariableSemanticMap};
|
||||||
|
use util::{Location, VariableLocation, RingBuffer, Size, Texture, TextureMeta, Viewport};
|
||||||
|
|
||||||
unsafe fn gl_compile_shader(stage: GLenum, source: &str) -> GLuint {
|
unsafe fn gl_compile_shader(stage: GLenum, source: &str) -> GLuint {
|
||||||
let shader = gl::CreateShader(stage);
|
let shader = gl::CreateShader(stage);
|
||||||
|
@ -35,226 +41,76 @@ unsafe fn gl_compile_shader(stage: GLenum, source: &str) -> GLuint {
|
||||||
shader
|
shader
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_pass_semantics(uniform_semantics: &mut FxHashMap<String, UniformSemantic>, texture_semantics: &mut FxHashMap<String, SemanticMap<TextureSemantics>>,
|
impl FilterChain {
|
||||||
|
fn load_pass_semantics(uniform_semantics: &mut FxHashMap<String, UniformSemantic>, texture_semantics: &mut FxHashMap<String, SemanticMap<TextureSemantics>>,
|
||||||
config: &ShaderPassConfig) {
|
config: &ShaderPassConfig) {
|
||||||
let Some(alias) = &config.alias else {
|
let Some(alias) = &config.alias else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Ignore empty aliases
|
// Ignore empty aliases
|
||||||
if alias.trim().is_empty() {
|
if alias.trim().is_empty() {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let index = config.id as u32;
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
let index = config.id as u32;
|
fn reflect_parameter(pipeline: GLuint, meta: &VariableMeta) -> VariableLocation {
|
||||||
|
// todo: support both ubo and pushco
|
||||||
|
// todo: fix this.
|
||||||
|
match meta.offset {
|
||||||
|
MemberOffset::Ubo(_) => {
|
||||||
|
let vert_name = format!("RARCH_UBO_VERTEX_INSTANCE.{}\0", meta.id);
|
||||||
|
let frag_name = format!("RARCH_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());
|
||||||
|
|
||||||
// PassOutput
|
VariableLocation::Ubo(Location {
|
||||||
texture_semantics.insert(alias.clone(), SemanticMap {
|
vertex,
|
||||||
semantics: TextureSemantics::PassOutput,
|
fragment
|
||||||
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
|
|
||||||
}));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct RingBuffer<T, const SIZE: usize> {
|
|
||||||
items: [T; SIZE],
|
|
||||||
index: usize
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <T, const SIZE: usize> RingBuffer<T, SIZE>
|
|
||||||
where T: Copy, T: Default
|
|
||||||
{
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
items: [T::default(); SIZE],
|
|
||||||
index: 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <T, const SIZE: usize> RingBuffer<T, SIZE> {
|
|
||||||
pub fn current(&self) -> &T {
|
|
||||||
&self.items[self.index]
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn next(&mut self) {
|
|
||||||
self.index += 1;
|
|
||||||
if self.index >= SIZE {
|
|
||||||
self.index = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct Location<T> {
|
|
||||||
vertex: T,
|
|
||||||
fragment: T,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum ParameterLocation {
|
|
||||||
Ubo(Location<GLint>),
|
|
||||||
Push(Location<GLint>),
|
|
||||||
}
|
|
||||||
pub struct FilterPass {
|
|
||||||
reflection: ShaderReflection,
|
|
||||||
compiled: ShaderCompilerOutput<String, GlslangGlslContext>,
|
|
||||||
program: GLuint,
|
|
||||||
ubo_location: Location<GLuint>,
|
|
||||||
ubo_ring: Option<RingBuffer<GLuint, 16>>,
|
|
||||||
uniform_buffer: Vec<u8>,
|
|
||||||
push_buffer: Vec<u8>,
|
|
||||||
locations: FxHashMap<String, ParameterLocation>,
|
|
||||||
framebuffer: Framebuffer,
|
|
||||||
feedback_framebuffer: Framebuffer,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Framebuffer {
|
|
||||||
image: GLuint,
|
|
||||||
size: Size,
|
|
||||||
format: GLenum,
|
|
||||||
max_levels: u32,
|
|
||||||
levels: u32,
|
|
||||||
framebuffer: GLuint,
|
|
||||||
init: bool
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for Framebuffer {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
if self.framebuffer != 0 {
|
|
||||||
unsafe {
|
|
||||||
gl::DeleteFramebuffers(1, &self.framebuffer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.image != 0 {
|
|
||||||
unsafe {
|
|
||||||
gl::DeleteTextures(1, &self.image);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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, framebuffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
Framebuffer {
|
|
||||||
image: 0,
|
|
||||||
size: Size { width: 1, height: 1 },
|
|
||||||
format: 0,
|
|
||||||
max_levels,
|
|
||||||
levels: 0,
|
|
||||||
framebuffer,
|
|
||||||
init: false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn init(&mut self, mut size: Size, mut format: ShaderFormat) {
|
|
||||||
if format == ShaderFormat::Unknown {
|
|
||||||
format = ShaderFormat::R8G8B8A8Unorm;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.format = GLenum::from(format);
|
|
||||||
self.size = size;
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
gl::BindFramebuffer(gl::FRAMEBUFFER, self.framebuffer);
|
|
||||||
|
|
||||||
// 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(1, self.image);
|
|
||||||
|
|
||||||
if size.width == 0 {
|
|
||||||
size.width = 1;
|
|
||||||
}
|
|
||||||
if size.height == 0 {
|
|
||||||
size.height = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.levels = calc_miplevel(size.width, size.height);
|
|
||||||
if self.levels > self.max_levels {
|
|
||||||
self.levels = self.max_levels;
|
|
||||||
}
|
|
||||||
if self.levels == 0 {
|
|
||||||
self.levels = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
gl::TexStorage2D(gl::TEXTURE_2D, self.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(1, self.image);
|
|
||||||
|
|
||||||
self.levels = calc_miplevel(size.width, size.height);
|
|
||||||
if self.levels > self.max_levels {
|
|
||||||
self.levels = self.max_levels;
|
|
||||||
}
|
|
||||||
if self.levels == 0 {
|
|
||||||
self.levels = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
gl::TexStorage2D(gl::TEXTURE_2D, self.levels as GLsizei, gl::RGBA8, 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;
|
|
||||||
}
|
|
||||||
_ => panic!("failed to complete: {status}")
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
self.init = true;
|
|
||||||
}
|
}
|
||||||
|
MemberOffset::PushConstant(_) => {
|
||||||
|
let vert_name = format!("RARCH_PUSH_VERTEX_INSTANCE.{}\0", meta.id);
|
||||||
|
let frag_name = format!("RARCH_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());
|
||||||
|
|
||||||
gl::BindFramebuffer(gl::FRAMEBUFFER, 0);
|
VariableLocation::Push(Location {
|
||||||
gl::BindTexture(gl::TEXTURE_2D, 0);
|
vertex,
|
||||||
|
fragment
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn calc_miplevel(width: u32, height: u32) -> u32 {
|
|
||||||
let mut size = std::cmp::max(width, height);
|
|
||||||
let mut levels = 0;
|
|
||||||
while size != 0 {
|
|
||||||
levels += 1;
|
|
||||||
size >>= 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return levels;
|
|
||||||
}
|
|
||||||
pub struct FilterChain {
|
pub struct FilterChain {
|
||||||
passes: Vec<FilterPass>,
|
passes: Vec<FilterPass>,
|
||||||
semantics: ReflectSemantics,
|
semantics: ReflectSemantics,
|
||||||
|
@ -264,38 +120,6 @@ pub struct FilterChain {
|
||||||
feedback: Vec<Texture>
|
feedback: Vec<Texture>
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reflect_parameter(pipeline: GLuint, meta: &VariableMeta) -> ParameterLocation {
|
|
||||||
// todo: support both ubo and pushco
|
|
||||||
// todo: fix this.
|
|
||||||
match meta.offset {
|
|
||||||
MemberOffset::Ubo(_) => {
|
|
||||||
let vert_name = format!("RARCH_UBO_VERTEX_INSTANCE.{}\0", meta.id);
|
|
||||||
let frag_name = format!("RARCH_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());
|
|
||||||
|
|
||||||
ParameterLocation::Ubo(Location {
|
|
||||||
vertex,
|
|
||||||
fragment
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
MemberOffset::PushConstant(_) => {
|
|
||||||
let vert_name = format!("RARCH_PUSH_VERTEX_INSTANCE.{}\0", meta.id);
|
|
||||||
let frag_name = format!("RARCH_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());
|
|
||||||
|
|
||||||
ParameterLocation::Push(Location {
|
|
||||||
vertex,
|
|
||||||
fragment
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FilterChain {
|
impl FilterChain {
|
||||||
pub fn load(path: impl AsRef<Path>) -> Result<FilterChain, Box<dyn Error>> {
|
pub fn load(path: impl AsRef<Path>) -> Result<FilterChain, Box<dyn Error>> {
|
||||||
|
@ -326,7 +150,7 @@ impl FilterChain {
|
||||||
// todo: this can probably be extracted out.
|
// todo: this can probably be extracted out.
|
||||||
|
|
||||||
for details in &passes {
|
for details in &passes {
|
||||||
load_pass_semantics(&mut uniform_semantics, &mut texture_semantics, details.0)
|
FilterChain::load_pass_semantics(&mut uniform_semantics, &mut texture_semantics, details.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// add lut params
|
// add lut params
|
||||||
|
@ -405,8 +229,8 @@ impl FilterChain {
|
||||||
let size = ubo.size;
|
let size = ubo.size;
|
||||||
let mut ring: RingBuffer<GLuint, 16> = RingBuffer::new();
|
let mut ring: RingBuffer<GLuint, 16> = RingBuffer::new();
|
||||||
unsafe {
|
unsafe {
|
||||||
gl::GenBuffers(16, ring.items.as_mut_ptr());
|
gl::GenBuffers(16, ring.items_mut().as_mut_ptr());
|
||||||
for buffer in &ring.items {
|
for buffer in ring.items() {
|
||||||
gl::BindBuffer(gl::UNIFORM_BUFFER, *buffer);
|
gl::BindBuffer(gl::UNIFORM_BUFFER, *buffer);
|
||||||
gl::BufferData(gl::UNIFORM_BUFFER, size as GLsizeiptr, std::ptr::null(), gl::STREAM_DRAW);
|
gl::BufferData(gl::UNIFORM_BUFFER, size as GLsizeiptr, std::ptr::null(), gl::STREAM_DRAW);
|
||||||
}
|
}
|
||||||
|
@ -417,17 +241,17 @@ impl FilterChain {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
let uniform_buffer = vec![0; reflection.ubo.as_ref().map(|ubo| ubo.size as usize).unwrap_or(0)];
|
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)];
|
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
|
// todo: reflect indexed parameters
|
||||||
let mut locations = FxHashMap::default();
|
let mut locations = FxHashMap::default();
|
||||||
for param in reflection.meta.parameter_meta.values() {
|
for param in reflection.meta.parameter_meta.values() {
|
||||||
locations.insert(param.id.clone(), reflect_parameter(program, param));
|
locations.insert(param.id.clone(), FilterChain::reflect_parameter(program, param));
|
||||||
}
|
}
|
||||||
|
|
||||||
for param in reflection.meta.variable_meta.values() {
|
for param in reflection.meta.variable_meta.values() {
|
||||||
locations.insert(param.id.clone(), reflect_parameter(program, param));
|
locations.insert(param.id.clone(), FilterChain::reflect_parameter(program, param));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -489,34 +313,36 @@ impl FilterChain {
|
||||||
|
|
||||||
|
|
||||||
// how much info do we actually need?
|
// how much info do we actually need?
|
||||||
fn frame(&mut self, count: u64, vp: &Viewport, input: &Texture, clear: bool) {
|
// fn frame(&mut self, count: u64, vp: &Viewport, input: &Texture, clear: bool) {
|
||||||
|
//
|
||||||
|
// // todo: make copy
|
||||||
|
//
|
||||||
|
// let original = Texture {
|
||||||
|
// handle: input.handle,
|
||||||
|
// format: self.preset.shaders.first().,
|
||||||
|
// size: Size {},
|
||||||
|
// padded_size: Size {}
|
||||||
|
// };
|
||||||
|
// // todo: deal with the mess that is frame history
|
||||||
|
// }
|
||||||
|
|
||||||
|
fn do_final_pass(&mut self, count: u64, vp: &Viewport, input: Texture, clear: bool, mvp: &[f32]) {
|
||||||
|
|
||||||
|
// todo: make copy
|
||||||
|
|
||||||
|
// todo: get filter info from pass data.
|
||||||
|
let original = TextureMeta {
|
||||||
|
texture: input,
|
||||||
|
filter: gl::LINEAR,
|
||||||
|
mip_filter: gl::LINEAR_MIPMAP_LINEAR,
|
||||||
|
wrap_mode: Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
// todo: deal with the mess that is frame history
|
// todo: deal with the mess that is frame history
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
|
||||||
struct Viewport {
|
|
||||||
x: i32,
|
|
||||||
y: i32,
|
|
||||||
width: i32,
|
|
||||||
height: i32
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
|
||||||
struct Size {
|
|
||||||
width: u32,
|
|
||||||
height: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
|
||||||
struct Texture {
|
|
||||||
handle: GLuint,
|
|
||||||
format: GLenum,
|
|
||||||
size: Size,
|
|
||||||
padded_size: Size
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
99
librashader-runtime-gl/src/util.rs
Normal file
99
librashader-runtime-gl/src/util.rs
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
use gl::types::{GLenum, GLint, GLuint};
|
||||||
|
use librashader::WrapMode;
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct Location<T> {
|
||||||
|
pub vertex: T,
|
||||||
|
pub fragment: T,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum VariableLocation {
|
||||||
|
Ubo(Location<GLint>),
|
||||||
|
Push(Location<GLint>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VariableLocation {
|
||||||
|
pub fn location(&self) -> Location<GLint> {
|
||||||
|
match self {
|
||||||
|
VariableLocation::Ubo(l) | VariableLocation::Push(l) => *l
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn calc_miplevel(width: u32, height: u32) -> u32 {
|
||||||
|
let mut size = std::cmp::max(width, height);
|
||||||
|
let mut levels = 0;
|
||||||
|
while size != 0 {
|
||||||
|
levels += 1;
|
||||||
|
size >>= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return levels;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct TextureMeta {
|
||||||
|
pub texture: Texture,
|
||||||
|
pub filter: GLenum,
|
||||||
|
pub mip_filter: GLenum,
|
||||||
|
pub wrap_mode: WrapMode
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct Viewport {
|
||||||
|
pub x: i32,
|
||||||
|
pub y: i32,
|
||||||
|
pub width: i32,
|
||||||
|
pub height: i32
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct Size {
|
||||||
|
pub width: u32,
|
||||||
|
pub height: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct Texture {
|
||||||
|
pub handle: GLuint,
|
||||||
|
pub format: GLenum,
|
||||||
|
pub size: Size,
|
||||||
|
pub padded_size: Size
|
||||||
|
}
|
||||||
|
|
||||||
|
impl <T, const SIZE: usize> RingBuffer<T, SIZE> {
|
||||||
|
pub fn current(&self) -> &T {
|
||||||
|
&self.items[self.index]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn next(&mut self) {
|
||||||
|
self.index += 1;
|
||||||
|
if self.index >= SIZE {
|
||||||
|
self.index = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct RingBuffer<T, const SIZE: usize> {
|
||||||
|
items: [T; SIZE],
|
||||||
|
index: usize
|
||||||
|
}
|
||||||
|
|
||||||
|
impl <T, const SIZE: usize> RingBuffer<T, SIZE>
|
||||||
|
where T: Copy, T: Default
|
||||||
|
{
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
items: [T::default(); SIZE],
|
||||||
|
index: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn items(&self) -> &[T; SIZE] {
|
||||||
|
&self.items
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn items_mut(&mut self) -> &mut [T; SIZE] {
|
||||||
|
&mut self.items
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue