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"
|
||||
checksum = "2c676a478f63e9fa2dd5368a42f28bba0d6c560b775f38583c8bbaa7fcd67c9c"
|
||||
|
||||
[[package]]
|
||||
name = "bytemuck"
|
||||
version = "1.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aaa3a8d9a1ca92e282c96a32d6511b695d7d994d1d102ba85d279f9b2756947f"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.4.3"
|
||||
|
@ -512,6 +518,7 @@ dependencies = [
|
|||
name = "librashader-runtime-gl"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
"gl",
|
||||
"glfw",
|
||||
"librashader",
|
||||
|
|
26
README.md
26
README.md
|
@ -1,5 +1,27 @@
|
|||
# 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,
|
||||
}
|
||||
|
||||
impl VariableSemantics {
|
||||
pub const fn semantics(self) -> SemanticMap<VariableSemantics, ()> {
|
||||
SemanticMap {
|
||||
semantics: self,
|
||||
index: ()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Copy, Clone, Hash)]
|
||||
#[repr(i32)]
|
||||
pub enum TextureSemantics {
|
||||
|
@ -68,6 +77,13 @@ impl TextureSemantics {
|
|||
pub fn is_array(&self) -> bool {
|
||||
!matches!(self, TextureSemantics::Original | TextureSemantics::Source)
|
||||
}
|
||||
|
||||
pub const fn semantics(self, index: u32) -> SemanticMap<TextureSemantics, u32> {
|
||||
SemanticMap {
|
||||
semantics: self,
|
||||
index
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TypeInfo {
|
||||
|
|
|
@ -14,3 +14,4 @@ spirv_cross = "0.23.1"
|
|||
rustc-hash = "1.1.0"
|
||||
gl = "0.14.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 filter;
|
||||
mod filter_pass;
|
||||
mod util;
|
||||
mod framebuffer;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::error::Error;
|
||||
|
@ -9,8 +12,10 @@ use gl::types::{GLenum, GLint, GLsizei, GLsizeiptr, GLuint};
|
|||
use glfw::Key::P;
|
||||
use rustc_hash::FxHashMap;
|
||||
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_reflect::back::{CompileShader, ShaderCompilerOutput};
|
||||
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::semantics::{MemberOffset, SemanticMap, TextureSemantics, VariableMeta, VariableSemantics};
|
||||
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 {
|
||||
let shader = gl::CreateShader(stage);
|
||||
|
@ -35,7 +41,8 @@ unsafe fn gl_compile_shader(stage: GLenum, source: &str) -> GLuint {
|
|||
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) {
|
||||
let Some(alias) = &config.alias else {
|
||||
return;
|
||||
|
@ -67,204 +74,9 @@ fn load_pass_semantics(uniform_semantics: &mut FxHashMap<String, UniformSemantic
|
|||
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;
|
||||
}
|
||||
|
||||
gl::BindFramebuffer(gl::FRAMEBUFFER, 0);
|
||||
gl::BindTexture(gl::TEXTURE_2D, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
passes: Vec<FilterPass>,
|
||||
semantics: ReflectSemantics,
|
||||
preset: ShaderPreset,
|
||||
original_history: Vec<Framebuffer>,
|
||||
history: Vec<Texture>,
|
||||
feedback: Vec<Texture>
|
||||
}
|
||||
|
||||
pub fn reflect_parameter(pipeline: GLuint, meta: &VariableMeta) -> ParameterLocation {
|
||||
fn reflect_parameter(pipeline: GLuint, meta: &VariableMeta) -> VariableLocation {
|
||||
// todo: support both ubo and pushco
|
||||
// todo: fix this.
|
||||
match meta.offset {
|
||||
|
@ -275,7 +87,7 @@ pub fn reflect_parameter(pipeline: GLuint, meta: &VariableMeta) -> ParameterLoca
|
|||
let vertex = gl::GetUniformLocation(pipeline, vert_name.as_ptr().cast());
|
||||
let fragment = gl::GetUniformLocation(pipeline, frag_name.as_ptr().cast());
|
||||
|
||||
ParameterLocation::Ubo(Location {
|
||||
VariableLocation::Ubo(Location {
|
||||
vertex,
|
||||
fragment
|
||||
})
|
||||
|
@ -288,15 +100,27 @@ pub fn reflect_parameter(pipeline: GLuint, meta: &VariableMeta) -> ParameterLoca
|
|||
let vertex = gl::GetUniformLocation(pipeline, vert_name.as_ptr().cast());
|
||||
let fragment = gl::GetUniformLocation(pipeline, frag_name.as_ptr().cast());
|
||||
|
||||
ParameterLocation::Push(Location {
|
||||
VariableLocation::Push(Location {
|
||||
vertex,
|
||||
fragment
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
pub struct FilterChain {
|
||||
passes: Vec<FilterPass>,
|
||||
semantics: ReflectSemantics,
|
||||
preset: ShaderPreset,
|
||||
original_history: Vec<Framebuffer>,
|
||||
history: Vec<Texture>,
|
||||
feedback: Vec<Texture>
|
||||
}
|
||||
|
||||
|
||||
impl FilterChain {
|
||||
pub fn load(path: impl AsRef<Path>) -> Result<FilterChain, Box<dyn Error>> {
|
||||
let preset = librashader_presets::ShaderPreset::try_parse(path)?;
|
||||
|
@ -326,7 +150,7 @@ impl FilterChain {
|
|||
// todo: this can probably be extracted out.
|
||||
|
||||
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
|
||||
|
@ -405,8 +229,8 @@ impl FilterChain {
|
|||
let size = ubo.size;
|
||||
let mut ring: RingBuffer<GLuint, 16> = RingBuffer::new();
|
||||
unsafe {
|
||||
gl::GenBuffers(16, ring.items.as_mut_ptr());
|
||||
for buffer in &ring.items {
|
||||
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);
|
||||
}
|
||||
|
@ -417,17 +241,17 @@ impl FilterChain {
|
|||
None
|
||||
};
|
||||
|
||||
let uniform_buffer = vec![0; reflection.ubo.as_ref().map(|ubo| ubo.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)];
|
||||
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(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() {
|
||||
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?
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
#[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)]
|
||||
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