reflect: allow binding uniform names to both UBO and Push Constants (#4)

This commit is contained in:
Ronny Chan 2023-01-29 01:57:09 -05:00 committed by GitHub
parent c3aecd336b
commit dffea95370
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 240 additions and 129 deletions

View file

@ -88,11 +88,6 @@ shaders.
Please report an issue if you run into a shader that works in RetroArch, but not under librashader. Please report an issue if you run into a shader that works in RetroArch, but not under librashader.
* Variables can only be bound in *either* the UBO or push constant block.
* RetroArch allows a variable to be present in both a push constant block and a UBO block at the same time. To make the
implementation a little cleaner, librashader only allows a variable to be in *either* a push constant block or a UBO
block. As far as I have tested, there are no shaders in [libretro/slang-shaders](https://github.com/libretro/slang-shaders)
that bind the same variable in both the push constant and the UBO block.
* Filter chains do not terminate at the backbuffer. * Filter chains do not terminate at the backbuffer.
* Unlike RetroArch, librashader does not have full knowledge of the entire rendering state and is designed to be pluggable * Unlike RetroArch, librashader does not have full knowledge of the entire rendering state and is designed to be pluggable
at any point in your render pipeline. Instead, filter chains terminate at a caller-provided output surface and viewport. at any point in your render pipeline. Instead, filter chains terminate at a caller-provided output surface and viewport.

View file

@ -1,4 +1,4 @@
use crate::reflect::semantics::MemberOffset; use crate::reflect::semantics::{MemberOffset, UniformMemberBlock};
use thiserror::Error; use thiserror::Error;
/// Error type for shader compilation. /// Error type for shader compilation.
@ -79,8 +79,10 @@ pub enum ShaderReflectError {
#[error("mismatched offset")] #[error("mismatched offset")]
MismatchedOffset { MismatchedOffset {
semantic: String, semantic: String,
vertex: MemberOffset, expected: usize,
fragment: MemberOffset, received: usize,
ty: UniformMemberBlock,
pass: usize
}, },
/// The size of the given uniform did not match up in both the vertex and fragment shader. /// The size of the given uniform did not match up in both the vertex and fragment shader.
#[error("mismatched component")] #[error("mismatched component")]
@ -88,6 +90,7 @@ pub enum ShaderReflectError {
semantic: String, semantic: String,
vertex: u32, vertex: u32,
fragment: u32, fragment: u32,
pass: usize
}, },
/// The binding number is already in use. /// The binding number is already in use.
#[error("the binding is already in use")] #[error("the binding is already in use")]

View file

@ -46,6 +46,7 @@
//! In the meanwhile, the only supported compilation type is [GlslangCompilation](crate::front::GlslangCompilation), //! In the meanwhile, the only supported compilation type is [GlslangCompilation](crate::front::GlslangCompilation),
//! which does transpilation via [shaderc](https://github.com/google/shaderc) and [SPIRV-Cross](https://github.com/KhronosGroup/SPIRV-Cross). //! which does transpilation via [shaderc](https://github.com/google/shaderc) and [SPIRV-Cross](https://github.com/KhronosGroup/SPIRV-Cross).
#![feature(type_alias_impl_trait)] #![feature(type_alias_impl_trait)]
#![feature(let_chains)]
/// Shader codegen backends. /// Shader codegen backends.
pub mod back; pub mod back;

View file

@ -1,11 +1,6 @@
use crate::error::{SemanticsErrorKind, ShaderCompileError, ShaderReflectError}; use crate::error::{SemanticsErrorKind, ShaderCompileError, ShaderReflectError};
use crate::front::GlslangCompilation; use crate::front::GlslangCompilation;
use crate::reflect::semantics::{ use crate::reflect::semantics::{BindingMeta, BindingStage, MemberOffset, PushReflection, ShaderReflection, ShaderSemantics, TextureBinding, TextureSemanticMap, TextureSemantics, TextureSizeMeta, TypeInfo, UboReflection, UniqueSemanticMap, UniqueSemantics, ValidateTypeSemantics, VariableMeta, MAX_BINDINGS_COUNT, MAX_PUSH_BUFFER_SIZE, UniformMemberBlock};
BindingMeta, BindingStage, MemberOffset, PushReflection, ShaderReflection, ShaderSemantics,
TextureBinding, TextureSemanticMap, TextureSemantics, TextureSizeMeta, TypeInfo, UboReflection,
UniqueSemanticMap, UniqueSemantics, ValidateTypeSemantics, VariableMeta, MAX_BINDINGS_COUNT,
MAX_PUSH_BUFFER_SIZE,
};
use crate::reflect::{align_uniform_size, ReflectShader}; use crate::reflect::{align_uniform_size, ReflectShader};
use std::ops::Deref; use std::ops::Deref;
@ -273,7 +268,7 @@ where
pass_number: usize, pass_number: usize,
semantics: &ShaderSemantics, semantics: &ShaderSemantics,
meta: &mut BindingMeta, meta: &mut BindingMeta,
offset_type: impl Fn(usize) -> MemberOffset, offset_type: UniformMemberBlock,
blame: SemanticErrorBlame, blame: SemanticErrorBlame,
) -> Result<(), ShaderReflectError> { ) -> Result<(), ShaderReflectError> {
let ranges = ast.get_active_buffer_ranges(resource.id)?; let ranges = ast.get_active_buffer_ranges(resource.id)?;
@ -298,13 +293,17 @@ where
match &parameter.semantics { match &parameter.semantics {
UniqueSemantics::FloatParameter => { UniqueSemantics::FloatParameter => {
let offset = offset_type(range.offset); let offset = range.offset;
if let Some(meta) = meta.parameter_meta.get(&name) { if let Some(meta) = meta.parameter_meta.get_mut(&name) {
if offset != meta.offset { if let Some(expected) = meta.offset.offset(offset_type)
&& expected != offset
{
return Err(ShaderReflectError::MismatchedOffset { return Err(ShaderReflectError::MismatchedOffset {
semantic: name, semantic: name,
vertex: meta.offset, expected,
fragment: offset, received: offset,
ty: offset_type,
pass: pass_number
}); });
} }
if meta.size != typeinfo.size { if meta.size != typeinfo.size {
@ -312,27 +311,34 @@ where
semantic: name, semantic: name,
vertex: meta.size, vertex: meta.size,
fragment: typeinfo.size, fragment: typeinfo.size,
pass: pass_number
}); });
} }
*meta.offset.offset_mut(offset_type) = Some(offset);
} else { } else {
meta.parameter_meta.insert( meta.parameter_meta.insert(
name.clone(), name.clone(),
VariableMeta { VariableMeta {
id: name, id: name,
offset, offset: MemberOffset::new(offset, offset_type),
size: typeinfo.size, size: typeinfo.size,
}, },
); );
} }
} }
semantics => { semantics => {
let offset = offset_type(range.offset); let offset = range.offset;
if let Some(meta) = meta.unique_meta.get(semantics) { if let Some(meta) = meta.unique_meta.get_mut(semantics) {
if offset != meta.offset { if let Some(expected) = meta.offset.offset(offset_type)
&& expected != offset
{
return Err(ShaderReflectError::MismatchedOffset { return Err(ShaderReflectError::MismatchedOffset {
semantic: name, semantic: name,
vertex: meta.offset, expected,
fragment: offset, received: offset,
ty: offset_type,
pass: pass_number
}); });
} }
if meta.size != typeinfo.size * typeinfo.columns { if meta.size != typeinfo.size * typeinfo.columns {
@ -340,14 +346,17 @@ where
semantic: name, semantic: name,
vertex: meta.size, vertex: meta.size,
fragment: typeinfo.size, fragment: typeinfo.size,
pass: pass_number
}); });
} }
*meta.offset.offset_mut(offset_type) = Some(offset);
} else { } else {
meta.unique_meta.insert( meta.unique_meta.insert(
*semantics, *semantics,
VariableMeta { VariableMeta {
id: name, id: name,
offset, offset: MemberOffset::new(offset, offset_type),
size: typeinfo.size * typeinfo.columns, size: typeinfo.size * typeinfo.columns,
}, },
); );
@ -368,14 +377,17 @@ where
} }
} }
// this will break if range is both ubo and push buf whatever let offset = range.offset;
let offset = offset_type(range.offset);
if let Some(meta) = meta.texture_size_meta.get_mut(&texture) { if let Some(meta) = meta.texture_size_meta.get_mut(&texture) {
if offset != meta.offset { if let Some(expected) = meta.offset.offset(offset_type)
&& expected != offset
{
return Err(ShaderReflectError::MismatchedOffset { return Err(ShaderReflectError::MismatchedOffset {
semantic: name, semantic: name,
vertex: meta.offset, expected,
fragment: offset, received: offset,
ty: offset_type,
pass: pass_number
}); });
} }
@ -383,12 +395,13 @@ where
SemanticErrorBlame::Vertex => BindingStage::VERTEX, SemanticErrorBlame::Vertex => BindingStage::VERTEX,
SemanticErrorBlame::Fragment => BindingStage::FRAGMENT, SemanticErrorBlame::Fragment => BindingStage::FRAGMENT,
}); });
*meta.offset.offset_mut(offset_type) = Some(offset);
} else { } else {
meta.texture_size_meta.insert( meta.texture_size_meta.insert(
texture, texture,
TextureSizeMeta { TextureSizeMeta {
offset, offset: MemberOffset::new(offset, offset_type),
// todo: fix this. to allow both
stage_mask: match blame { stage_mask: match blame {
SemanticErrorBlame::Vertex => BindingStage::VERTEX, SemanticErrorBlame::Vertex => BindingStage::VERTEX,
SemanticErrorBlame::Fragment => BindingStage::FRAGMENT, SemanticErrorBlame::Fragment => BindingStage::FRAGMENT,
@ -607,7 +620,7 @@ where
pass_number, pass_number,
semantics, semantics,
&mut meta, &mut meta,
MemberOffset::Ubo, UniformMemberBlock::Ubo,
SemanticErrorBlame::Vertex, SemanticErrorBlame::Vertex,
)?; )?;
} }
@ -619,7 +632,7 @@ where
pass_number, pass_number,
semantics, semantics,
&mut meta, &mut meta,
MemberOffset::Ubo, UniformMemberBlock::Ubo,
SemanticErrorBlame::Fragment, SemanticErrorBlame::Fragment,
)?; )?;
} }
@ -631,7 +644,7 @@ where
pass_number, pass_number,
semantics, semantics,
&mut meta, &mut meta,
MemberOffset::PushConstant, UniformMemberBlock::PushConstant,
SemanticErrorBlame::Vertex, SemanticErrorBlame::Vertex,
)?; )?;
} }
@ -643,7 +656,7 @@ where
pass_number, pass_number,
semantics, semantics,
&mut meta, &mut meta,
MemberOffset::PushConstant, UniformMemberBlock::PushConstant,
SemanticErrorBlame::Fragment, SemanticErrorBlame::Fragment,
)?; )?;
} }

View file

@ -194,11 +194,59 @@ pub struct PushReflection {
/// A uniform can be bound to **either** the UBO, or as a Push Constant. Binding /// A uniform can be bound to **either** the UBO, or as a Push Constant. Binding
/// the same variable name to both locations will result in indeterminate results. /// the same variable name to both locations will result in indeterminate results.
#[derive(Debug, Copy, Clone, PartialEq, Eq)] #[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum MemberOffset { pub struct MemberOffset {
/// The offset of the uniform member within the UBO. /// The offset of the uniform member within the UBO.
Ubo(usize), pub ubo: Option<usize>,
/// The offset of the uniform member within the Push Constant range. /// The offset of the uniform member within the Push Constant range.
PushConstant(usize), pub push: Option<usize>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
/// The block where a uniform member is located.
pub enum UniformMemberBlock {
/// The offset is for a UBO.
Ubo,
/// The offset is for a push constant block.
PushConstant
}
impl UniformMemberBlock {
/// A list of valid member block types.
pub const TYPES: [UniformMemberBlock; 2] = [UniformMemberBlock::Ubo, UniformMemberBlock::PushConstant];
}
impl MemberOffset {
pub(crate) fn new(off: usize, ty: UniformMemberBlock) -> Self {
match ty {
UniformMemberBlock::Ubo => {
MemberOffset {
ubo: Some(off),
push: None,
}
}
UniformMemberBlock::PushConstant => {
MemberOffset {
ubo: None,
push: Some(off),
}
}
}
}
pub fn offset(&self, ty: UniformMemberBlock) -> Option<usize> {
match ty {
UniformMemberBlock::Ubo => {self.ubo}
UniformMemberBlock::PushConstant => {self.push}
}
}
pub(crate) fn offset_mut(&mut self, ty: UniformMemberBlock) -> &mut Option<usize> {
match ty {
UniformMemberBlock::Ubo => {&mut self.ubo}
UniformMemberBlock::PushConstant => {&mut self.push}
}
}
} }
/// Reflection information about a non-texture related uniform variable. /// Reflection information about a non-texture related uniform variable.

View file

@ -33,7 +33,8 @@ mod tests {
#[test] #[test]
fn triangle_d3d11() { fn triangle_d3d11() {
let sample = hello_triangle::d3d11_hello_triangle::Sample::new( let sample = hello_triangle::d3d11_hello_triangle::Sample::new(
"../test/slang-shaders/presets/crt-royale-kurozumi.slangp", // "../test/slang-shaders/presets/crt-royale-kurozumi.slangp",
"../test/slang-shaders/bezel/Mega_Bezel/Presets/MBZ__0__SMOOTH-ADV.slangp",
// "../test/basic.slangp", // "../test/basic.slangp",
Some(&FilterChainOptionsD3D11 { Some(&FilterChainOptionsD3D11 {
use_deferred_context: false, use_deferred_context: false,

View file

@ -1,18 +1,35 @@
use gl::types::GLint; use gl::types::GLint;
use librashader_reflect::reflect::semantics::BindingStage; use librashader_reflect::reflect::semantics::{BindingStage, UniformMemberBlock};
use librashader_runtime::uniforms::{BindUniform, UniformScalar, UniformStorage}; use librashader_runtime::uniforms::{BindUniform, UniformScalar, UniformStorage};
#[derive(Debug)] #[derive(Debug, Copy, Clone)]
pub enum VariableLocation { pub struct VariableLocation {
Ubo(UniformLocation<GLint>), pub(crate) ubo: Option<UniformLocation<GLint>>,
Push(UniformLocation<GLint>), pub(crate) push: Option<UniformLocation<GLint>>,
} }
impl VariableLocation { impl VariableLocation {
pub fn location(&self) -> UniformLocation<GLint> { pub fn location(&self, offset_type: UniformMemberBlock) -> Option<UniformLocation<GLint>> {
match self { let value = match offset_type {
VariableLocation::Ubo(l) | VariableLocation::Push(l) => *l, UniformMemberBlock::Ubo => {
self.ubo
}
UniformMemberBlock::PushConstant => {
self.push
}
};
value
}
pub fn is_valid(&self, offset_type: UniformMemberBlock, stage: BindingStage) -> bool {
let value = self.location(offset_type);
let mut validity = false;
if stage.contains(BindingStage::FRAGMENT) {
validity = validity || value.is_some_and(|f| f.fragment >= 0);
} }
if stage.contains(BindingStage::VERTEX) {
validity = validity || value.is_some_and(|f| f.vertex >= 0);
}
validity
} }
} }
@ -33,9 +50,13 @@ impl UniformLocation<GLint> {
} }
validity validity
} }
pub fn bindable(&self) -> bool {
self.is_valid(BindingStage::VERTEX | BindingStage::FRAGMENT)
}
} }
pub(crate) type GlUniformStorage = UniformStorage<GlUniformBinder, UniformLocation<GLint>>; pub(crate) type GlUniformStorage = UniformStorage<GlUniformBinder, VariableLocation>;
pub trait GlUniformScalar: UniformScalar { pub trait GlUniformScalar: UniformScalar {
const FACTORY: unsafe fn(GLint, Self) -> (); const FACTORY: unsafe fn(GLint, Self) -> ();
@ -54,19 +75,19 @@ impl GlUniformScalar for u32 {
} }
pub(crate) struct GlUniformBinder; pub(crate) struct GlUniformBinder;
impl<T> BindUniform<UniformLocation<GLint>, T> for GlUniformBinder impl<T> BindUniform<VariableLocation, T> for GlUniformBinder
where where
T: GlUniformScalar, T: GlUniformScalar,
{ {
fn bind_uniform(value: T, location: UniformLocation<GLint>) -> Option<()> { fn bind_uniform(block: UniformMemberBlock, value: T, location: VariableLocation) -> Option<()> {
if location.is_valid(BindingStage::VERTEX | BindingStage::FRAGMENT) { if let Some(location) = location.location(block)
unsafe { && location.bindable()
if location.is_valid(BindingStage::VERTEX) { {
T::FACTORY(location.vertex, value); if location.is_valid(BindingStage::VERTEX) {
} unsafe { T::FACTORY(location.vertex, value); }
if location.is_valid(BindingStage::FRAGMENT) { }
T::FACTORY(location.fragment, value); if location.is_valid(BindingStage::FRAGMENT) {
} unsafe { T::FACTORY(location.fragment, value); }
} }
Some(()) Some(())
} else { } else {
@ -75,9 +96,11 @@ where
} }
} }
impl BindUniform<UniformLocation<GLint>, &[f32; 4]> for GlUniformBinder { impl BindUniform<VariableLocation, &[f32; 4]> for GlUniformBinder {
fn bind_uniform(vec4: &[f32; 4], location: UniformLocation<GLint>) -> Option<()> { fn bind_uniform(block: UniformMemberBlock, vec4: &[f32; 4], location: VariableLocation) -> Option<()> {
if location.is_valid(BindingStage::VERTEX | BindingStage::FRAGMENT) { if let Some(location) = location.location(block)
&& location.bindable()
{
unsafe { unsafe {
if location.is_valid(BindingStage::VERTEX) { if location.is_valid(BindingStage::VERTEX) {
gl::Uniform4fv(location.vertex, 1, vec4.as_ptr()); gl::Uniform4fv(location.vertex, 1, vec4.as_ptr());
@ -93,9 +116,11 @@ impl BindUniform<UniformLocation<GLint>, &[f32; 4]> for GlUniformBinder {
} }
} }
impl BindUniform<UniformLocation<GLint>, &[f32; 16]> for GlUniformBinder { impl BindUniform<VariableLocation, &[f32; 16]> for GlUniformBinder {
fn bind_uniform(mat4: &[f32; 16], location: UniformLocation<GLint>) -> Option<()> { fn bind_uniform(block: UniformMemberBlock, mat4: &[f32; 16], location: VariableLocation) -> Option<()> {
if location.is_valid(BindingStage::VERTEX | BindingStage::FRAGMENT) { if let Some(location) = location.location(block)
&& location.bindable()
{
unsafe { unsafe {
if location.is_valid(BindingStage::VERTEX) { if location.is_valid(BindingStage::VERTEX) {
gl::UniformMatrix4fv(location.vertex, 1, gl::FALSE, mat4.as_ptr()); gl::UniformMatrix4fv(location.vertex, 1, gl::FALSE, mat4.as_ptr());

View file

@ -53,30 +53,37 @@ pub struct FilterMutable {
impl<T: GLInterface> FilterChainImpl<T> { impl<T: GLInterface> FilterChainImpl<T> {
fn reflect_uniform_location(pipeline: GLuint, meta: &impl UniformMeta) -> VariableLocation { fn reflect_uniform_location(pipeline: GLuint, meta: &impl UniformMeta) -> VariableLocation {
// todo: support both ubo and pushco
// todo: fix this.
match meta.offset() {
MemberOffset::Ubo(_) => {
let vert_name = format!("LIBRA_UBO_VERTEX_INSTANCE.{}\0", meta.id());
let frag_name = format!("LIBRA_UBO_FRAGMENT_INSTANCE.{}\0", meta.id());
unsafe {
let vertex = gl::GetUniformLocation(pipeline, vert_name.as_ptr().cast());
let fragment = gl::GetUniformLocation(pipeline, frag_name.as_ptr().cast());
VariableLocation::Ubo(UniformLocation { vertex, fragment }) let mut location = VariableLocation {
} ubo: None,
} push: None,
MemberOffset::PushConstant(_) => { };
let vert_name = format!("LIBRA_PUSH_VERTEX_INSTANCE.{}\0", meta.id());
let frag_name = format!("LIBRA_PUSH_FRAGMENT_INSTANCE.{}\0", meta.id());
unsafe {
let vertex = gl::GetUniformLocation(pipeline, vert_name.as_ptr().cast());
let fragment = gl::GetUniformLocation(pipeline, frag_name.as_ptr().cast());
VariableLocation::Push(UniformLocation { vertex, fragment }) let offset = meta.offset();
}
if offset.ubo.is_some() {
let vert_name = format!("LIBRA_UBO_VERTEX_INSTANCE.{}\0", meta.id());
let frag_name = format!("LIBRA_UBO_FRAGMENT_INSTANCE.{}\0", meta.id());
unsafe {
let vertex = gl::GetUniformLocation(pipeline, vert_name.as_ptr().cast());
let fragment = gl::GetUniformLocation(pipeline, frag_name.as_ptr().cast());
location.ubo = Some(UniformLocation { vertex, fragment })
} }
} }
if offset.push.is_some() {
let vert_name = format!("LIBRA_PUSH_VERTEX_INSTANCE.{}\0", meta.id());
let frag_name = format!("LIBRA_PUSH_FRAGMENT_INSTANCE.{}\0", meta.id());
unsafe {
let vertex = gl::GetUniformLocation(pipeline, vert_name.as_ptr().cast());
let fragment = gl::GetUniformLocation(pipeline, frag_name.as_ptr().cast());
location.push = Some(UniformLocation { vertex, fragment })
}
}
location
} }
} }

View file

@ -48,17 +48,17 @@ impl TextureInput for InputTexture {
} }
} }
impl ContextOffset<GlUniformBinder, UniformLocation<GLint>> for UniformOffset { impl ContextOffset<GlUniformBinder, VariableLocation> for UniformOffset {
fn offset(&self) -> MemberOffset { fn offset(&self) -> MemberOffset {
self.offset self.offset
} }
fn context(&self) -> UniformLocation<GLint> { fn context(&self) -> VariableLocation {
self.location.location() self.location
} }
} }
impl<T: GLInterface> BindSemantics<GlUniformBinder, UniformLocation<GLint>> for FilterPass<T> { impl<T: GLInterface> BindSemantics<GlUniformBinder, VariableLocation> for FilterPass<T> {
type InputTexture = InputTexture; type InputTexture = InputTexture;
type SamplerSet = SamplerSet; type SamplerSet = SamplerSet;
type DescriptorSet<'a> = (); type DescriptorSet<'a> = ();

View file

@ -6,6 +6,7 @@
#![feature(strict_provenance)] #![feature(strict_provenance)]
#![feature(type_alias_impl_trait)] #![feature(type_alias_impl_trait)]
#![feature(let_chains)] #![feature(let_chains)]
#![feature(is_some_and)]
mod binding; mod binding;
mod filter_chain; mod filter_chain;
@ -35,7 +36,7 @@ mod tests {
fn triangle_gl() { fn triangle_gl() {
let (glfw, window, events, shader, vao) = gl::gl3::hello_triangle::setup(); let (glfw, window, events, shader, vao) = gl::gl3::hello_triangle::setup();
let mut filter = FilterChainGL::load_from_path( let mut filter = FilterChainGL::load_from_path(
"../test/slang-shaders/crt/crt-lottes.slangp", "../test/slang-shaders/bezel/Mega_Bezel/Presets/MBZ__0__SMOOTH-ADV.slangp",
Some(&FilterChainOptionsGL { Some(&FilterChainOptionsGL {
gl_version: 0, gl_version: 0,
use_dsa: false, use_dsa: false,

View file

@ -50,6 +50,7 @@ where
/// Trait that abstracts binding of semantics to shader uniforms. /// Trait that abstracts binding of semantics to shader uniforms.
pub trait BindSemantics<H = NoUniformBinder, C = Option<()>, S = Box<[u8]>> pub trait BindSemantics<H = NoUniformBinder, C = Option<()>, S = Box<[u8]>>
where where
C: Copy,
S: Deref<Target = [u8]> + DerefMut, S: Deref<Target = [u8]> + DerefMut,
H: BindUniform<C, f32>, H: BindUniform<C, f32>,
H: BindUniform<C, u32>, H: BindUniform<C, u32>,

View file

@ -1,4 +1,4 @@
use librashader_reflect::reflect::semantics::MemberOffset; use librashader_reflect::reflect::semantics::{MemberOffset, UniformMemberBlock};
use std::marker::PhantomData; use std::marker::PhantomData;
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};
@ -15,7 +15,7 @@ pub trait BindUniform<C, T> {
/// A `BindUniform` implementation should not write to a backing buffer from a [`UniformStorage`](crate::uniforms::UniformStorage). /// A `BindUniform` implementation should not write to a backing buffer from a [`UniformStorage`](crate::uniforms::UniformStorage).
/// If the binding is successful and no writes to a backing buffer is necessary, this function should return `Some(())`. /// If the binding is successful and no writes to a backing buffer is necessary, this function should return `Some(())`.
/// If this function returns `None`, then the value will instead be written to the backing buffer. /// If this function returns `None`, then the value will instead be written to the backing buffer.
fn bind_uniform(value: T, ctx: C) -> Option<()>; fn bind_uniform(block: UniformMemberBlock, value: T, ctx: C) -> Option<()>;
} }
/// A trait to access the raw pointer to a backing uniform storage. /// A trait to access the raw pointer to a backing uniform storage.
@ -62,7 +62,7 @@ where
/// All uniform data is thus written into the backing buffer storage. /// All uniform data is thus written into the backing buffer storage.
pub struct NoUniformBinder; pub struct NoUniformBinder;
impl<T> BindUniform<Option<()>, T> for NoUniformBinder { impl<T> BindUniform<Option<()>, T> for NoUniformBinder {
fn bind_uniform(_: T, _: Option<()>) -> Option<()> { fn bind_uniform(_: UniformMemberBlock, _: T, _: Option<()>) -> Option<()> {
None None
} }
} }
@ -86,10 +86,22 @@ where
pub fn inner_ubo(&self) -> &S { pub fn inner_ubo(&self) -> &S {
&self.ubo &self.ubo
} }
pub(crate) fn buffer(&mut self, ty: UniformMemberBlock) -> &mut [u8] {
match ty {
UniformMemberBlock::Ubo => {
self.ubo.deref_mut()
}
UniformMemberBlock::PushConstant => {
self.push.deref_mut()
}
}
}
} }
impl<H, C, S> UniformStorage<H, C, S> impl<H, C, S> UniformStorage<H, C, S>
where where
C: Copy,
S: Deref<Target = [u8]> + DerefMut, S: Deref<Target = [u8]> + DerefMut,
{ {
#[inline(always)] #[inline(always)]
@ -105,19 +117,19 @@ where
where where
H: BindUniform<C, T>, H: BindUniform<C, T>,
{ {
if H::bind_uniform(value, ctx).is_some() { for ty in UniformMemberBlock::TYPES {
return; if H::bind_uniform(ty, value, ctx).is_some() {
continue;
}
if let Some(offset) = offset.offset(ty) {
let buffer = self.buffer(ty);
Self::write_scalar_inner(
&mut buffer[offset..][..std::mem::size_of::<T>()],
value,
)
}
} }
let (buffer, offset) = match offset {
MemberOffset::Ubo(offset) => (self.ubo.deref_mut(), offset),
MemberOffset::PushConstant(offset) => (self.push.deref_mut(), offset),
};
Self::write_scalar_inner(
&mut buffer[offset..][..std::mem::size_of::<T>()],
value,
)
} }
/// Create a new `UniformStorage` with the given backing storage /// Create a new `UniformStorage` with the given backing storage
@ -145,6 +157,7 @@ impl<H, C> UniformStorage<H, C, Box<[u8]>> {
impl<H, C, S> UniformStorage<H, C, S> impl<H, C, S> UniformStorage<H, C, S>
where where
C: Copy,
S: Deref<Target = [u8]> + DerefMut, S: Deref<Target = [u8]> + DerefMut,
H: for<'a> BindUniform<C, &'a [f32; 4]>, H: for<'a> BindUniform<C, &'a [f32; 4]>,
{ {
@ -157,24 +170,26 @@ where
#[inline(always)] #[inline(always)]
pub fn bind_vec4(&mut self, offset: MemberOffset, value: impl Into<[f32; 4]>, ctx: C) { pub fn bind_vec4(&mut self, offset: MemberOffset, value: impl Into<[f32; 4]>, ctx: C) {
let vec4 = value.into(); let vec4 = value.into();
if H::bind_uniform(&vec4, ctx).is_some() {
return;
for ty in UniformMemberBlock::TYPES {
if H::bind_uniform(ty, &vec4, ctx).is_some() {
continue;
}
if let Some(offset) = offset.offset(ty) {
let buffer = self.buffer(ty);
Self::write_vec4_inner(
&mut buffer[offset..][..4 * std::mem::size_of::<f32>()],
&vec4,
);
}
} }
let (buffer, offset) = match offset {
MemberOffset::Ubo(offset) => (self.ubo.deref_mut(), offset),
MemberOffset::PushConstant(offset) => (self.push.deref_mut(), offset),
};
Self::write_vec4_inner(
&mut buffer[offset..][..4 * std::mem::size_of::<f32>()],
&vec4,
);
} }
} }
impl<H, C, S> UniformStorage<H, C, S> impl<H, C, S> UniformStorage<H, C, S>
where where
C: Copy,
S: Deref<Target = [u8]> + DerefMut, S: Deref<Target = [u8]> + DerefMut,
H: for<'a> BindUniform<C, &'a [f32; 16]>, H: for<'a> BindUniform<C, &'a [f32; 16]>,
{ {
@ -187,16 +202,17 @@ where
/// Bind a `mat4` to the given offset. /// Bind a `mat4` to the given offset.
#[inline(always)] #[inline(always)]
pub fn bind_mat4(&mut self, offset: MemberOffset, value: &[f32; 16], ctx: C) { pub fn bind_mat4(&mut self, offset: MemberOffset, value: &[f32; 16], ctx: C) {
if H::bind_uniform(value, ctx).is_some() { for ty in UniformMemberBlock::TYPES {
return; if H::bind_uniform(ty, value, ctx).is_some() {
continue;
}
if let Some(offset) = offset.offset(ty) {
let buffer = self.buffer(ty);
Self::write_mat4_inner(
&mut buffer[offset..][..16 * std::mem::size_of::<f32>()],
value,
);
}
} }
let (buffer, offset) = match offset {
MemberOffset::Ubo(offset) => (self.ubo.deref_mut(), offset),
MemberOffset::PushConstant(offset) => (self.push.deref_mut(), offset),
};
Self::write_mat4_inner(
&mut buffer[offset..][..16 * std::mem::size_of::<f32>()],
value,
);
} }
} }