gl/reflect: abstract away common uniform storage buffers

This commit is contained in:
chyyran 2022-11-28 22:56:20 -05:00
parent 2c953d638f
commit 83b7cd38a0
12 changed files with 383 additions and 445 deletions

1
Cargo.lock generated
View file

@ -456,6 +456,7 @@ name = "librashader-reflect"
version = "0.1.0"
dependencies = [
"bitflags",
"bytemuck",
"librashader-common",
"librashader-preprocess",
"naga",

View file

@ -21,6 +21,9 @@ naga = { version = "0.10.0", features = ["glsl-in", "spv-in", "spv-out", "glsl-o
rspirv = { version = "0.11.0+1.5.4", optional = true }
rspirv-reflect = { git = "https://github.com/Traverse-Research/rspirv-reflect", optional = true }
bytemuck = "1.12.3"
[features]
default = []
unstable-rust-pipeline = [ "naga", "rspirv", "rspirv-reflect" ]

View file

@ -13,6 +13,7 @@ pub mod semantics;
mod naga;
#[cfg(feature = "unstable-rust-pipeline")]
mod rspirv;
pub mod uniforms;
pub trait ReflectShader {
fn reflect(

View file

@ -0,0 +1,96 @@
use std::marker::PhantomData;
use crate::reflect::semantics::MemberOffset;
pub trait UniformScalar: Copy + bytemuck::Pod {}
impl UniformScalar for f32 {}
impl UniformScalar for i32 {}
impl UniformScalar for u32 {}
pub struct NoUniformBinder;
impl <T> BindUniform<Option<()>, T> for NoUniformBinder {
fn bind_uniform(_: T, _: Option<()>) -> Option<()> {
None
}
}
pub trait BindUniform<C, T> {
fn bind_uniform(value: T, ctx: C) -> Option<()>;
}
pub struct UniformBuffer<H=NoUniformBinder, C = Option<()>> {
pub ubo: Box<[u8]>,
pub push: Box<[u8]>,
_h: PhantomData<H>,
_c: PhantomData<C>
}
impl <H, C> UniformBuffer<H, C>
where H: BindUniform<C, f32>, H: BindUniform<C, u32>, H: BindUniform<C, i32>,
H: for <'a> BindUniform<C, &'a [f32; 4]>,
H: for <'a> BindUniform<C, &'a [f32; 16]>
{
pub fn new(ubo_size: usize, push_size: usize) -> Self {
UniformBuffer {
ubo: vec![0u8; ubo_size].into_boxed_slice(),
push: vec![0u8; push_size].into_boxed_slice(),
_h: Default::default(),
_c: Default::default(),
}
}
#[inline(always)]
fn write_scalar_inner<T: UniformScalar>(buffer: &mut [u8], value: T, ctx: C)
where H: BindUniform<C, T>
{
if let None = H::bind_uniform(value, ctx) {
let buffer = bytemuck::cast_slice_mut(buffer);
buffer[0] = value;
};
}
fn write_mat4_inner(buffer: &mut [u8], mat4: &[f32; 16], ctx: C) {
if let None = H::bind_uniform(mat4, ctx) {
let mat4 = bytemuck::cast_slice(mat4);
buffer.copy_from_slice(mat4);
}
}
fn write_vec4_inner(buffer: &mut [u8], vec4: impl Into<[f32; 4]>, ctx: C) {
let vec4 = vec4.into();
if let None = H::bind_uniform(&vec4, ctx) {
let vec4 = bytemuck::cast_slice(&vec4);
buffer.copy_from_slice(vec4);
}
}
pub fn bind_mat4(&mut self, offset: MemberOffset, value: &[f32; 16], ctx: C) {
let (buffer, offset) = match offset {
MemberOffset::Ubo(offset) => (&mut self.ubo, offset),
MemberOffset::PushConstant(offset) => (&mut self.push, offset),
};
let size = value.len() * std::mem::size_of::<f32>();
Self::write_mat4_inner(&mut buffer[offset..][..size], value, ctx);
}
pub fn bind_vec4(&mut self, offset: MemberOffset, value: impl Into<[f32; 4]>, ctx: C) {
let (buffer, offset) = match offset {
MemberOffset::Ubo(offset) => (&mut self.ubo, offset),
MemberOffset::PushConstant(offset) => (&mut self.push, offset),
};
Self::write_vec4_inner(&mut buffer[offset..][..16], value, ctx);
}
pub fn bind_scalar<T: UniformScalar>(&mut self, offset: MemberOffset, value: T, ctx: C)
where H: BindUniform<C, T>
{
let (buffer, offset) = match offset {
MemberOffset::Ubo(offset) => (&mut self.ubo, offset),
MemberOffset::PushConstant(offset) => (&mut self.push, offset),
};
Self::write_scalar_inner(&mut buffer[offset..][..4], value, ctx)
}
}

View file

@ -1,5 +1,6 @@
use gl::types::GLint;
use librashader_reflect::reflect::semantics::BindingStage;
use librashader_reflect::reflect::uniforms::{BindUniform, UniformBuffer, UniformScalar};
#[derive(Debug)]
pub enum VariableLocation {
@ -33,3 +34,79 @@ impl UniformLocation<GLint> {
validity
}
}
pub(crate) type BufferStorage = UniformBuffer<GlUniformBinder, UniformLocation<GLint>>;
pub trait GlUniformScalar: UniformScalar {
const FACTORY: unsafe fn(GLint, Self) -> ();
}
impl GlUniformScalar for f32 {
const FACTORY: unsafe fn(GLint, Self) -> () = gl::Uniform1f;
}
impl GlUniformScalar for i32 {
const FACTORY: unsafe fn(GLint, Self) -> () = gl::Uniform1i;
}
impl GlUniformScalar for u32 {
const FACTORY: unsafe fn(GLint, Self) -> () = gl::Uniform1ui;
}
pub(crate) struct GlUniformBinder;
impl<T> BindUniform<UniformLocation<GLint>, T> for GlUniformBinder
where T: GlUniformScalar
{
fn bind_uniform(value: T, location: UniformLocation<GLint>) -> Option<()> {
if location.is_valid(BindingStage::VERTEX | BindingStage::FRAGMENT) {
unsafe {
if location.is_valid(BindingStage::VERTEX) {
T::FACTORY(location.vertex, value);
}
if location.is_valid(BindingStage::FRAGMENT) {
T::FACTORY(location.fragment, value);
}
}
Some(())
} else {
None
}
}
}
impl BindUniform<UniformLocation<GLint>, &[f32; 4]> for GlUniformBinder {
fn bind_uniform(vec4: &[f32; 4], location: UniformLocation<GLint>) -> Option<()> {
if location.is_valid(BindingStage::VERTEX | BindingStage::FRAGMENT) {
unsafe {
if location.is_valid(BindingStage::VERTEX) {
gl::Uniform4fv(location.vertex, 1, vec4.as_ptr());
}
if location.is_valid(BindingStage::FRAGMENT) {
gl::Uniform4fv(location.fragment, 1, vec4.as_ptr());
}
}
Some(())
} else {
None
}
}
}
impl BindUniform<UniformLocation<GLint>, &[f32; 16]> for GlUniformBinder {
fn bind_uniform(mat4: &[f32; 16], location: UniformLocation<GLint>) -> Option<()> {
if location.is_valid(BindingStage::VERTEX | BindingStage::FRAGMENT) {
unsafe {
if location.is_valid(BindingStage::VERTEX) {
gl::UniformMatrix4fv(location.vertex, 1, gl::FALSE, mat4.as_ptr());
}
if location.is_valid(BindingStage::FRAGMENT) {
gl::UniformMatrix4fv(location.fragment, 1, gl::FALSE, mat4.as_ptr());
}
}
Some(())
}else {
None
}
}
}

View file

@ -25,6 +25,7 @@ use librashader_reflect::front::shaderc::GlslangCompilation;
use crate::options::{FilterChainOptions, FrameOptions};
use crate::samplers::SamplerSet;
use crate::texture::Texture;
use crate::binding::BufferStorage;
pub struct FilterChain {
passes: Box<[FilterPass]>,
@ -435,24 +436,18 @@ impl FilterChain {
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();
let uniform_storage = BufferStorage::new(reflection
.ubo
.as_ref()
.map(|ubo| ubo.size as usize)
.unwrap_or(0),
reflection
.push_constant
.as_ref()
.map(|push| push.size as usize)
.unwrap_or(0)
);
let mut uniform_bindings = FxHashMap::default();
for param in reflection.meta.parameter_meta.values() {
@ -500,8 +495,7 @@ impl FilterChain {
program,
ubo_location,
ubo_ring,
uniform_buffer,
push_buffer,
uniform_storage,
uniform_bindings,
source,
config

View file

@ -8,8 +8,9 @@ use librashader_preprocess::ShaderSource;
use librashader_presets::ShaderPassConfig;
use librashader_reflect::reflect::semantics::{BindingStage, MemberOffset, TextureBinding, TextureSemantics, UniformBinding, VariableSemantics};
use rustc_hash::FxHashMap;
use librashader_reflect::reflect::uniforms::UniformBuffer;
use crate::binding::{UniformLocation, VariableLocation};
use crate::binding::{BufferStorage, GlUniformBinder, UniformLocation, VariableLocation};
use crate::filter_chain::FilterCommon;
use crate::framebuffer::Viewport;
use crate::render_target::RenderTarget;
@ -17,111 +18,20 @@ use crate::samplers::SamplerSet;
use crate::texture::Texture;
use crate::util::{InlineRingBuffer, RingBuffer};
pub struct FilterPass {
pub reflection: ShaderReflection,
pub compiled: ShaderCompilerOutput<String, GlslangGlslContext>,
pub program: GLuint,
pub ubo_location: UniformLocation<GLuint>,
pub ubo_ring: Option<InlineRingBuffer<GLuint, 16>>,
pub uniform_buffer: Box<[u8]>,
pub push_buffer: Box<[u8]>,
pub(crate) uniform_storage: BufferStorage,
pub uniform_bindings: FxHashMap<UniformBinding, (VariableLocation, MemberOffset)>,
pub source: ShaderSource,
pub config: ShaderPassConfig,
}
impl FilterPass {
fn build_mat4(location: UniformLocation<GLint>, buffer: &mut [u8], mvp: &[f32; 16]) {
if location.is_valid(BindingStage::VERTEX | BindingStage::FRAGMENT) {
unsafe {
if location.is_valid(BindingStage::VERTEX) {
gl::UniformMatrix4fv(location.vertex, 1, gl::FALSE, mvp.as_ptr());
}
if location.is_valid(BindingStage::FRAGMENT) {
gl::UniformMatrix4fv(location.fragment, 1, gl::FALSE, mvp.as_ptr());
}
}
} else {
let mvp = bytemuck::cast_slice(mvp);
buffer.copy_from_slice(mvp);
}
}
fn build_vec4(location: UniformLocation<GLint>, buffer: &mut [u8], size: impl Into<[f32; 4]>) {
let vec4 = size.into();
if location.is_valid(BindingStage::VERTEX | BindingStage::FRAGMENT) {
unsafe {
if location.is_valid(BindingStage::VERTEX) {
gl::Uniform4fv(location.vertex, 1, vec4.as_ptr());
}
if location.is_valid(BindingStage::FRAGMENT) {
gl::Uniform4fv(location.fragment, 1, vec4.as_ptr());
}
}
} else {
let vec4 = bytemuck::cast_slice(&vec4);
buffer.copy_from_slice(vec4);
}
}
#[inline(always)]
fn build_uniform<T>(
location: UniformLocation<GLint>,
buffer: &mut [u8],
value: T,
glfn: unsafe fn(GLint, T) -> (),
) where
T: Copy,
T: bytemuck::Pod,
{
if location.is_valid(BindingStage::VERTEX | BindingStage::FRAGMENT) {
unsafe {
if location.is_valid(BindingStage::VERTEX) {
glfn(location.vertex, value);
}
if location.is_valid(BindingStage::FRAGMENT) {
glfn(location.fragment, value);
}
}
} else {
let buffer = bytemuck::cast_slice_mut(buffer);
buffer[0] = value;
}
}
fn build_uint(location: UniformLocation<GLint>, buffer: &mut [u8], value: u32) {
Self::build_uniform(location, buffer, value, gl::Uniform1ui)
}
fn build_sint(location: UniformLocation<GLint>, buffer: &mut [u8], value: i32) {
Self::build_uniform(location, buffer, value, gl::Uniform1i)
}
fn build_float(location: UniformLocation<GLint>, buffer: &mut [u8], value: f32) {
Self::build_uniform(location, buffer, value, gl::Uniform1f)
}
fn bind_texture(samplers: &SamplerSet, binding: &TextureBinding, texture: &Texture) {
unsafe {
// eprintln!("setting {} to texunit {}", texture.image.handle, binding.binding);
gl::ActiveTexture(gl::TEXTURE0 + binding.binding);
gl::BindTexture(gl::TEXTURE_2D, texture.image.handle);
gl::BindSampler(binding.binding,
samplers.get(texture.wrap_mode, texture.filter, texture.mip_filter));
}
}
pub fn get_format(&self) -> ShaderFormat {
let mut fb_format = ShaderFormat::R8G8B8A8Unorm;
if self.config.srgb_framebuffer {
fb_format = ShaderFormat::R8G8B8A8Srgb;
} else if self.config.float_framebuffer {
fb_format = ShaderFormat::R16G16B16A16Sfloat;
}
fb_format
}
// todo: fix rendertargets (i.e. non-final pass is internal, final pass is user provided fbo)
pub fn draw(
&mut self,
@ -152,7 +62,6 @@ impl FilterPass {
original,
source,
);
// shader_gl3:1514
if self.ubo_location.vertex != gl::INVALID_INDEX
&& self.ubo_location.fragment != gl::INVALID_INDEX
@ -167,7 +76,7 @@ impl FilterPass {
gl::UNIFORM_BUFFER,
0,
size as GLsizeiptr,
self.uniform_buffer.as_ptr().cast(),
self.uniform_storage.ubo.as_ptr().cast(),
);
gl::BindBuffer(gl::UNIFORM_BUFFER, 0);
@ -211,6 +120,30 @@ impl FilterPass {
}
}
fn bind_texture(samplers: &SamplerSet, binding: &TextureBinding, texture: &Texture) {
unsafe {
// eprintln!("setting {} to texunit {}", texture.image.handle, binding.binding);
gl::ActiveTexture(gl::TEXTURE0 + binding.binding);
gl::BindTexture(gl::TEXTURE_2D, texture.image.handle);
gl::BindSampler(binding.binding,
samplers.get(texture.wrap_mode, texture.filter, texture.mip_filter));
}
}
}
impl FilterPass {
pub fn get_format(&self) -> ShaderFormat {
let mut fb_format = ShaderFormat::R8G8B8A8Unorm;
if self.config.srgb_framebuffer {
fb_format = ShaderFormat::R8G8B8A8Srgb;
} else if self.config.float_framebuffer {
fb_format = ShaderFormat::R16G16B16A16Sfloat;
}
fb_format
}
// framecount should be pre-modded
fn build_semantics(
&mut self,
@ -228,12 +161,7 @@ impl FilterPass {
if let Some((location, offset)) =
self.uniform_bindings.get(&VariableSemantics::MVP.into())
{
let mvp_size = mvp.len() * std::mem::size_of::<f32>();
let (buffer, offset) = match offset {
MemberOffset::Ubo(offset) => (&mut self.uniform_buffer, *offset),
MemberOffset::PushConstant(offset) => (&mut self.push_buffer, *offset),
};
FilterPass::build_mat4(location.location(), &mut buffer[offset..][..mvp_size], mvp)
self.uniform_storage.bind_mat4(*offset, mvp, location.location());
}
// bind OutputSize
@ -241,12 +169,7 @@ impl FilterPass {
.uniform_bindings
.get(&VariableSemantics::Output.into())
{
let (buffer, offset) = match offset {
MemberOffset::Ubo(offset) => (&mut self.uniform_buffer, *offset),
MemberOffset::PushConstant(offset) => (&mut self.push_buffer, *offset),
};
FilterPass::build_vec4(location.location(), &mut buffer[offset..][..16], fb_size)
self.uniform_storage.bind_vec4(*offset, fb_size, location.location());
}
// bind FinalViewportSize
@ -254,15 +177,7 @@ impl FilterPass {
.uniform_bindings
.get(&VariableSemantics::FinalViewport.into())
{
let (buffer, offset) = match offset {
MemberOffset::Ubo(offset) => (&mut self.uniform_buffer, *offset),
MemberOffset::PushConstant(offset) => (&mut self.push_buffer, *offset),
};
FilterPass::build_vec4(
location.location(),
&mut buffer[offset..][..16],
viewport.output.size,
)
self.uniform_storage.bind_vec4(*offset,viewport.output.size, location.location());
}
// bind FrameCount
@ -270,11 +185,7 @@ impl FilterPass {
.uniform_bindings
.get(&VariableSemantics::FrameCount.into())
{
let (buffer, offset) = match offset {
MemberOffset::Ubo(offset) => (&mut self.uniform_buffer, *offset),
MemberOffset::PushConstant(offset) => (&mut self.push_buffer, *offset),
};
FilterPass::build_uint(location.location(), &mut buffer[offset..][..4], frame_count)
self.uniform_storage.bind_scalar(*offset, frame_count, location.location());
}
// bind FrameDirection
@ -282,15 +193,7 @@ impl FilterPass {
.uniform_bindings
.get(&VariableSemantics::FrameDirection.into())
{
let (buffer, offset) = match offset {
MemberOffset::Ubo(offset) => (&mut self.uniform_buffer, *offset),
MemberOffset::PushConstant(offset) => (&mut self.push_buffer, *offset),
};
FilterPass::build_sint(
location.location(),
&mut buffer[offset..][..4],
frame_direction,
)
self.uniform_storage.bind_scalar(*offset, frame_direction, location.location());
}
// bind Original sampler
@ -308,15 +211,8 @@ impl FilterPass {
.uniform_bindings
.get(&TextureSemantics::Original.semantics(0).into())
{
let (buffer, offset) = match offset {
MemberOffset::Ubo(offset) => (&mut self.uniform_buffer, *offset),
MemberOffset::PushConstant(offset) => (&mut self.push_buffer, *offset),
};
FilterPass::build_vec4(
location.location(),
&mut buffer[offset..][..16],
original.image.size,
);
self.uniform_storage
.bind_vec4(*offset,original.image.size, location.location());
}
// bind Source sampler
@ -335,15 +231,8 @@ impl FilterPass {
.uniform_bindings
.get(&TextureSemantics::Source.semantics(0).into())
{
let (buffer, offset) = match offset {
MemberOffset::Ubo(offset) => (&mut self.uniform_buffer, *offset),
MemberOffset::PushConstant(offset) => (&mut self.push_buffer, *offset),
};
FilterPass::build_vec4(
location.location(),
&mut buffer[offset..][..16],
source.image.size,
);
self.uniform_storage.bind_vec4(*offset,
source.image.size, location.location());
}
if let Some(binding) = self
@ -358,15 +247,8 @@ impl FilterPass {
.uniform_bindings
.get(&TextureSemantics::OriginalHistory.semantics(0).into())
{
let (buffer, offset) = match offset {
MemberOffset::Ubo(offset) => (&mut self.uniform_buffer, *offset),
MemberOffset::PushConstant(offset) => (&mut self.push_buffer, *offset),
};
FilterPass::build_vec4(
location.location(),
&mut buffer[offset..][..16],
original.image.size,
);
self.uniform_storage
.bind_vec4(*offset,original.image.size, location.location());
}
for (index, output) in parent.history_textures.iter().enumerate() {
@ -384,15 +266,8 @@ impl FilterPass {
.semantics(index + 1)
.into(),
) {
let (buffer, offset) = match offset {
MemberOffset::Ubo(offset) => (&mut self.uniform_buffer, *offset),
MemberOffset::PushConstant(offset) => (&mut self.push_buffer, *offset),
};
FilterPass::build_vec4(
location.location(),
&mut buffer[offset..][..16],
output.image.size,
);
self.uniform_storage
.bind_vec4(*offset,output.image.size, location.location());
}
}
@ -411,15 +286,8 @@ impl FilterPass {
.uniform_bindings
.get(&TextureSemantics::PassOutput.semantics(index).into())
{
let (buffer, offset) = match offset {
MemberOffset::Ubo(offset) => (&mut self.uniform_buffer, *offset),
MemberOffset::PushConstant(offset) => (&mut self.push_buffer, *offset),
};
FilterPass::build_vec4(
location.location(),
&mut buffer[offset..][..16],
output.image.size,
);
self.uniform_storage
.bind_vec4(*offset,output.image.size, location.location());
}
}
@ -441,15 +309,8 @@ impl FilterPass {
.uniform_bindings
.get(&TextureSemantics::PassFeedback.semantics(index).into())
{
let (buffer, offset) = match offset {
MemberOffset::Ubo(offset) => (&mut self.uniform_buffer, *offset),
MemberOffset::PushConstant(offset) => (&mut self.push_buffer, *offset),
};
FilterPass::build_vec4(
location.location(),
&mut buffer[offset..][..16],
feedback.image.size,
);
self.uniform_storage
.bind_vec4(*offset,feedback.image.size, location.location());
}
}
@ -463,12 +324,6 @@ impl FilterPass {
})
{
let id = id.as_str();
let (buffer, offset) = match offset {
MemberOffset::Ubo(offset) => (&mut self.uniform_buffer, *offset),
MemberOffset::PushConstant(offset) => (&mut self.push_buffer, *offset),
};
// todo: cache parameters.
// presets override params
let default = self
.source
@ -484,7 +339,8 @@ impl FilterPass {
.get(id)
.unwrap_or(&default);
FilterPass::build_float(location.location(), &mut buffer[offset..][..4], value)
self.uniform_storage
.bind_scalar(*offset, value, location.location());
}
// bind luts
@ -502,15 +358,8 @@ impl FilterPass {
.uniform_bindings
.get(&TextureSemantics::User.semantics(*index).into())
{
let (buffer, offset) = match offset {
MemberOffset::Ubo(offset) => (&mut self.uniform_buffer, *offset),
MemberOffset::PushConstant(offset) => (&mut self.push_buffer, *offset),
};
FilterPass::build_vec4(
location.location(),
&mut buffer[offset..][..16],
lut.image.size,
);
self.uniform_storage
.bind_vec4(*offset, lut.image.size, location.location());
}
}
}

View file

@ -31,7 +31,7 @@ mod tests {
fn triangle_gl() {
let (glfw, window, events, shader, vao) = hello_triangle::setup();
let mut filter =
FilterChain::load_from_path("../test/slang-shaders/crt/crt-royale.slangp", None)
FilterChain::load_from_path("../test/slang-shaders/vhs/VHSPro.slangp", None)
.unwrap();
hello_triangle::do_loop(glfw, window, events, shader, vao, &mut filter);
}

View file

@ -1,6 +1,9 @@
use gl::types::{GLenum, GLuint};
use gl::types::{GLenum, GLint, GLuint};
use librashader_common::Size;
use librashader_reflect::back::cross::GlVersion;
use librashader_reflect::reflect::semantics::BindingStage;
use librashader_reflect::reflect::uniforms::{BindUniform, UniformBuffer, UniformScalar};
use crate::binding::UniformLocation;
pub fn calc_miplevel(size: Size<u32>) -> u32 {
let mut size = std::cmp::max(size.width, size.height);
@ -126,4 +129,4 @@ pub fn gl_u16_to_version(version: u16) -> GlVersion {
460 => GlVersion::V4_60,
_ => GlVersion::V1_50
}
}
}

View file

@ -1,5 +1,6 @@
use gl::types::GLint;
use librashader_reflect::reflect::semantics::BindingStage;
use librashader_reflect::reflect::uniforms::{BindUniform, UniformBuffer, UniformScalar};
#[derive(Debug)]
pub enum VariableLocation {
@ -33,3 +34,79 @@ impl UniformLocation<GLint> {
validity
}
}
pub(crate) type BufferStorage = UniformBuffer<GlUniformBinder, UniformLocation<GLint>>;
pub trait GlUniformScalar: UniformScalar {
const FACTORY: unsafe fn(GLint, Self) -> ();
}
impl GlUniformScalar for f32 {
const FACTORY: unsafe fn(GLint, Self) -> () = gl::Uniform1f;
}
impl GlUniformScalar for i32 {
const FACTORY: unsafe fn(GLint, Self) -> () = gl::Uniform1i;
}
impl GlUniformScalar for u32 {
const FACTORY: unsafe fn(GLint, Self) -> () = gl::Uniform1ui;
}
pub(crate) struct GlUniformBinder;
impl<T> BindUniform<UniformLocation<GLint>, T> for GlUniformBinder
where T: GlUniformScalar
{
fn bind_uniform(value: T, location: UniformLocation<GLint>) -> Option<()> {
if location.is_valid(BindingStage::VERTEX | BindingStage::FRAGMENT) {
unsafe {
if location.is_valid(BindingStage::VERTEX) {
T::FACTORY(location.vertex, value);
}
if location.is_valid(BindingStage::FRAGMENT) {
T::FACTORY(location.fragment, value);
}
}
Some(())
} else {
None
}
}
}
impl BindUniform<UniformLocation<GLint>, &[f32; 4]> for GlUniformBinder {
fn bind_uniform(vec4: &[f32; 4], location: UniformLocation<GLint>) -> Option<()> {
if location.is_valid(BindingStage::VERTEX | BindingStage::FRAGMENT) {
unsafe {
if location.is_valid(BindingStage::VERTEX) {
gl::Uniform4fv(location.vertex, 1, vec4.as_ptr());
}
if location.is_valid(BindingStage::FRAGMENT) {
gl::Uniform4fv(location.fragment, 1, vec4.as_ptr());
}
}
Some(())
} else {
None
}
}
}
impl BindUniform<UniformLocation<GLint>, &[f32; 16]> for GlUniformBinder {
fn bind_uniform(mat4: &[f32; 16], location: UniformLocation<GLint>) -> Option<()> {
if location.is_valid(BindingStage::VERTEX | BindingStage::FRAGMENT) {
unsafe {
if location.is_valid(BindingStage::VERTEX) {
gl::UniformMatrix4fv(location.vertex, 1, gl::FALSE, mat4.as_ptr());
}
if location.is_valid(BindingStage::FRAGMENT) {
gl::UniformMatrix4fv(location.fragment, 1, gl::FALSE, mat4.as_ptr());
}
}
Some(())
}else {
None
}
}
}

View file

@ -1,4 +1,4 @@
use crate::binding::{UniformLocation, VariableLocation};
use crate::binding::{BufferStorage, UniformLocation, VariableLocation};
use crate::filter_pass::FilterPass;
use crate::framebuffer::{Framebuffer, GlImage, Viewport};
use crate::quad_render::DrawQuad;
@ -433,24 +433,17 @@ impl FilterChain {
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();
let uniform_storage = BufferStorage::new(reflection
.ubo
.as_ref()
.map(|ubo| ubo.size as usize)
.unwrap_or(0),
reflection
.push_constant
.as_ref()
.map(|push| push.size as usize)
.unwrap_or(0)
);
let mut uniform_bindings = FxHashMap::default();
for param in reflection.meta.parameter_meta.values() {
@ -498,8 +491,7 @@ impl FilterChain {
program,
ubo_location,
ubo_ring,
uniform_buffer,
push_buffer,
uniform_storage,
uniform_bindings,
source,
config

View file

@ -9,7 +9,7 @@ use librashader_presets::ShaderPassConfig;
use librashader_reflect::reflect::semantics::{BindingStage, MemberOffset, TextureBinding, TextureSemantics, UniformBinding, VariableSemantics};
use rustc_hash::FxHashMap;
use crate::binding::{UniformLocation, VariableLocation};
use crate::binding::{BufferStorage, UniformLocation, VariableLocation};
use crate::filter_chain::FilterCommon;
use crate::framebuffer::Viewport;
use crate::render_target::RenderTarget;
@ -23,103 +23,13 @@ pub struct FilterPass {
pub program: GLuint,
pub ubo_location: UniformLocation<GLuint>,
pub ubo_ring: Option<InlineRingBuffer<GLuint, 16>>,
pub uniform_buffer: Box<[u8]>,
pub push_buffer: Box<[u8]>,
pub(crate) uniform_storage: BufferStorage,
pub uniform_bindings: FxHashMap<UniformBinding, (VariableLocation, MemberOffset)>,
pub source: ShaderSource,
pub config: ShaderPassConfig,
}
impl FilterPass {
fn build_mat4(location: UniformLocation<GLint>, buffer: &mut [u8], mvp: &[f32; 16]) {
if location.is_valid(BindingStage::VERTEX | BindingStage::FRAGMENT) {
unsafe {
if location.is_valid(BindingStage::VERTEX) {
gl::UniformMatrix4fv(location.vertex, 1, gl::FALSE, mvp.as_ptr());
}
if location.is_valid(BindingStage::FRAGMENT) {
gl::UniformMatrix4fv(location.fragment, 1, gl::FALSE, mvp.as_ptr());
}
}
} else {
let mvp = bytemuck::cast_slice(mvp);
buffer.copy_from_slice(mvp);
}
}
fn build_vec4(location: UniformLocation<GLint>, buffer: &mut [u8], size: impl Into<[f32; 4]>) {
let vec4 = size.into();
if location.is_valid(BindingStage::VERTEX | BindingStage::FRAGMENT) {
unsafe {
if location.is_valid(BindingStage::VERTEX) {
gl::Uniform4fv(location.vertex, 1, vec4.as_ptr());
}
if location.is_valid(BindingStage::FRAGMENT) {
gl::Uniform4fv(location.fragment, 1, vec4.as_ptr());
}
}
} else {
let vec4 = bytemuck::cast_slice(&vec4);
buffer.copy_from_slice(vec4);
}
}
#[inline(always)]
fn build_uniform<T>(
location: UniformLocation<GLint>,
buffer: &mut [u8],
value: T,
glfn: unsafe fn(GLint, T) -> (),
) where
T: Copy,
T: bytemuck::Pod,
{
if location.is_valid(BindingStage::VERTEX | BindingStage::FRAGMENT) {
unsafe {
if location.is_valid(BindingStage::VERTEX) {
glfn(location.vertex, value);
}
if location.is_valid(BindingStage::FRAGMENT) {
glfn(location.fragment, value);
}
}
} else {
let buffer = bytemuck::cast_slice_mut(buffer);
buffer[0] = value;
}
}
fn build_uint(location: UniformLocation<GLint>, buffer: &mut [u8], value: u32) {
Self::build_uniform(location, buffer, value, gl::Uniform1ui)
}
fn build_sint(location: UniformLocation<GLint>, buffer: &mut [u8], value: i32) {
Self::build_uniform(location, buffer, value, gl::Uniform1i)
}
fn build_float(location: UniformLocation<GLint>, buffer: &mut [u8], value: f32) {
Self::build_uniform(location, buffer, value, gl::Uniform1f)
}
fn bind_texture(samplers: &SamplerSet, binding: &TextureBinding, texture: &Texture) {
unsafe {
// eprintln!("setting {} to texunit {}", texture.image.handle, binding.binding);
gl::BindTextureUnit(binding.binding, texture.image.handle);
gl::BindSampler(binding.binding,
samplers.get(texture.wrap_mode, texture.filter, texture.mip_filter));
}
}
pub fn get_format(&self) -> ShaderFormat {
let mut fb_format = ShaderFormat::R8G8B8A8Unorm;
if self.config.srgb_framebuffer {
fb_format = ShaderFormat::R8G8B8A8Srgb;
} else if self.config.float_framebuffer {
fb_format = ShaderFormat::R16G16B16A16Sfloat;
}
fb_format
}
// todo: fix rendertargets (i.e. non-final pass is internal, final pass is user provided fbo)
pub fn draw(
&mut self,
@ -135,7 +45,6 @@ impl FilterPass {
let framebuffer = output.framebuffer;
unsafe {
// gl::BindFramebuffer(gl::FRAMEBUFFER, framebuffer.handle);
gl::UseProgram(self.program);
}
@ -150,7 +59,6 @@ impl FilterPass {
original,
source,
);
// shader_gl3:1514
if self.ubo_location.vertex != gl::INVALID_INDEX
&& self.ubo_location.fragment != gl::INVALID_INDEX
@ -164,7 +72,7 @@ impl FilterPass {
*buffer,
0,
size as GLsizeiptr,
self.uniform_buffer.as_ptr().cast(),
self.uniform_storage.ubo.as_ptr().cast(),
);
if self.ubo_location.vertex != gl::INVALID_INDEX {
@ -205,6 +113,27 @@ impl FilterPass {
}
}
fn bind_texture(samplers: &SamplerSet, binding: &TextureBinding, texture: &Texture) {
unsafe {
// eprintln!("setting {} to texunit {}", texture.image.handle, binding.binding);
gl::BindTextureUnit(binding.binding, texture.image.handle);
gl::BindSampler(binding.binding,
samplers.get(texture.wrap_mode, texture.filter, texture.mip_filter));
}
}
}
impl FilterPass {
pub fn get_format(&self) -> ShaderFormat {
let mut fb_format = ShaderFormat::R8G8B8A8Unorm;
if self.config.srgb_framebuffer {
fb_format = ShaderFormat::R8G8B8A8Srgb;
} else if self.config.float_framebuffer {
fb_format = ShaderFormat::R16G16B16A16Sfloat;
}
fb_format
}
// framecount should be pre-modded
fn build_semantics(
&mut self,
@ -222,12 +151,7 @@ impl FilterPass {
if let Some((location, offset)) =
self.uniform_bindings.get(&VariableSemantics::MVP.into())
{
let mvp_size = mvp.len() * std::mem::size_of::<f32>();
let (buffer, offset) = match offset {
MemberOffset::Ubo(offset) => (&mut self.uniform_buffer, *offset),
MemberOffset::PushConstant(offset) => (&mut self.push_buffer, *offset),
};
FilterPass::build_mat4(location.location(), &mut buffer[offset..][..mvp_size], mvp)
self.uniform_storage.bind_mat4(*offset, mvp, location.location());
}
// bind OutputSize
@ -235,12 +159,7 @@ impl FilterPass {
.uniform_bindings
.get(&VariableSemantics::Output.into())
{
let (buffer, offset) = match offset {
MemberOffset::Ubo(offset) => (&mut self.uniform_buffer, *offset),
MemberOffset::PushConstant(offset) => (&mut self.push_buffer, *offset),
};
FilterPass::build_vec4(location.location(), &mut buffer[offset..][..16], fb_size)
self.uniform_storage.bind_vec4(*offset, fb_size, location.location());
}
// bind FinalViewportSize
@ -248,15 +167,7 @@ impl FilterPass {
.uniform_bindings
.get(&VariableSemantics::FinalViewport.into())
{
let (buffer, offset) = match offset {
MemberOffset::Ubo(offset) => (&mut self.uniform_buffer, *offset),
MemberOffset::PushConstant(offset) => (&mut self.push_buffer, *offset),
};
FilterPass::build_vec4(
location.location(),
&mut buffer[offset..][..16],
viewport.output.size,
)
self.uniform_storage.bind_vec4(*offset,viewport.output.size, location.location());
}
// bind FrameCount
@ -264,11 +175,7 @@ impl FilterPass {
.uniform_bindings
.get(&VariableSemantics::FrameCount.into())
{
let (buffer, offset) = match offset {
MemberOffset::Ubo(offset) => (&mut self.uniform_buffer, *offset),
MemberOffset::PushConstant(offset) => (&mut self.push_buffer, *offset),
};
FilterPass::build_uint(location.location(), &mut buffer[offset..][..4], frame_count)
self.uniform_storage.bind_scalar(*offset, frame_count, location.location());
}
// bind FrameDirection
@ -276,15 +183,7 @@ impl FilterPass {
.uniform_bindings
.get(&VariableSemantics::FrameDirection.into())
{
let (buffer, offset) = match offset {
MemberOffset::Ubo(offset) => (&mut self.uniform_buffer, *offset),
MemberOffset::PushConstant(offset) => (&mut self.push_buffer, *offset),
};
FilterPass::build_sint(
location.location(),
&mut buffer[offset..][..4],
frame_direction,
)
self.uniform_storage.bind_scalar(*offset, frame_direction, location.location());
}
// bind Original sampler
@ -302,15 +201,8 @@ impl FilterPass {
.uniform_bindings
.get(&TextureSemantics::Original.semantics(0).into())
{
let (buffer, offset) = match offset {
MemberOffset::Ubo(offset) => (&mut self.uniform_buffer, *offset),
MemberOffset::PushConstant(offset) => (&mut self.push_buffer, *offset),
};
FilterPass::build_vec4(
location.location(),
&mut buffer[offset..][..16],
original.image.size,
);
self.uniform_storage
.bind_vec4(*offset,original.image.size, location.location());
}
// bind Source sampler
@ -329,15 +221,8 @@ impl FilterPass {
.uniform_bindings
.get(&TextureSemantics::Source.semantics(0).into())
{
let (buffer, offset) = match offset {
MemberOffset::Ubo(offset) => (&mut self.uniform_buffer, *offset),
MemberOffset::PushConstant(offset) => (&mut self.push_buffer, *offset),
};
FilterPass::build_vec4(
location.location(),
&mut buffer[offset..][..16],
source.image.size,
);
self.uniform_storage.bind_vec4(*offset,
source.image.size, location.location());
}
if let Some(binding) = self
@ -352,15 +237,8 @@ impl FilterPass {
.uniform_bindings
.get(&TextureSemantics::OriginalHistory.semantics(0).into())
{
let (buffer, offset) = match offset {
MemberOffset::Ubo(offset) => (&mut self.uniform_buffer, *offset),
MemberOffset::PushConstant(offset) => (&mut self.push_buffer, *offset),
};
FilterPass::build_vec4(
location.location(),
&mut buffer[offset..][..16],
original.image.size,
);
self.uniform_storage
.bind_vec4(*offset,original.image.size, location.location());
}
for (index, output) in parent.history_textures.iter().enumerate() {
@ -378,15 +256,8 @@ impl FilterPass {
.semantics(index + 1)
.into(),
) {
let (buffer, offset) = match offset {
MemberOffset::Ubo(offset) => (&mut self.uniform_buffer, *offset),
MemberOffset::PushConstant(offset) => (&mut self.push_buffer, *offset),
};
FilterPass::build_vec4(
location.location(),
&mut buffer[offset..][..16],
output.image.size,
);
self.uniform_storage
.bind_vec4(*offset,output.image.size, location.location());
}
}
@ -405,15 +276,8 @@ impl FilterPass {
.uniform_bindings
.get(&TextureSemantics::PassOutput.semantics(index).into())
{
let (buffer, offset) = match offset {
MemberOffset::Ubo(offset) => (&mut self.uniform_buffer, *offset),
MemberOffset::PushConstant(offset) => (&mut self.push_buffer, *offset),
};
FilterPass::build_vec4(
location.location(),
&mut buffer[offset..][..16],
output.image.size,
);
self.uniform_storage
.bind_vec4(*offset,output.image.size, location.location());
}
}
@ -435,15 +299,8 @@ impl FilterPass {
.uniform_bindings
.get(&TextureSemantics::PassFeedback.semantics(index).into())
{
let (buffer, offset) = match offset {
MemberOffset::Ubo(offset) => (&mut self.uniform_buffer, *offset),
MemberOffset::PushConstant(offset) => (&mut self.push_buffer, *offset),
};
FilterPass::build_vec4(
location.location(),
&mut buffer[offset..][..16],
feedback.image.size,
);
self.uniform_storage
.bind_vec4(*offset,feedback.image.size, location.location());
}
}
@ -457,12 +314,6 @@ impl FilterPass {
})
{
let id = id.as_str();
let (buffer, offset) = match offset {
MemberOffset::Ubo(offset) => (&mut self.uniform_buffer, *offset),
MemberOffset::PushConstant(offset) => (&mut self.push_buffer, *offset),
};
// todo: cache parameters.
// presets override params
let default = self
.source
@ -478,7 +329,8 @@ impl FilterPass {
.get(id)
.unwrap_or(&default);
FilterPass::build_float(location.location(), &mut buffer[offset..][..4], value)
self.uniform_storage
.bind_scalar(*offset, value, location.location());
}
// bind luts
@ -496,15 +348,8 @@ impl FilterPass {
.uniform_bindings
.get(&TextureSemantics::User.semantics(*index).into())
{
let (buffer, offset) = match offset {
MemberOffset::Ubo(offset) => (&mut self.uniform_buffer, *offset),
MemberOffset::PushConstant(offset) => (&mut self.push_buffer, *offset),
};
FilterPass::build_vec4(
location.location(),
&mut buffer[offset..][..16],
lut.image.size,
);
self.uniform_storage
.bind_vec4(*offset, lut.image.size, location.location());
}
}
}