From dffea953707fd0a04dac5eb4cee9826712d6ca09 Mon Sep 17 00:00:00 2001 From: Ronny Chan Date: Sun, 29 Jan 2023 01:57:09 -0500 Subject: [PATCH] reflect: allow binding uniform names to both UBO and Push Constants (#4) --- README.md | 5 -- librashader-reflect/src/error.rs | 9 +- librashader-reflect/src/lib.rs | 1 + librashader-reflect/src/reflect/cross.rs | 73 ++++++++------- librashader-reflect/src/reflect/semantics.rs | 54 ++++++++++- librashader-runtime-d3d11/src/lib.rs | 3 +- librashader-runtime-gl/src/binding.rs | 75 ++++++++++------ .../src/filter_chain/filter_impl.rs | 47 +++++----- librashader-runtime-gl/src/filter_pass.rs | 8 +- librashader-runtime-gl/src/lib.rs | 3 +- librashader-runtime/src/binding.rs | 1 + librashader-runtime/src/uniforms.rs | 90 +++++++++++-------- 12 files changed, 240 insertions(+), 129 deletions(-) diff --git a/README.md b/README.md index ef2a3f7..3dd889b 100644 --- a/README.md +++ b/README.md @@ -88,11 +88,6 @@ shaders. 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. * 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. diff --git a/librashader-reflect/src/error.rs b/librashader-reflect/src/error.rs index 5e0be8e..c508556 100644 --- a/librashader-reflect/src/error.rs +++ b/librashader-reflect/src/error.rs @@ -1,4 +1,4 @@ -use crate::reflect::semantics::MemberOffset; +use crate::reflect::semantics::{MemberOffset, UniformMemberBlock}; use thiserror::Error; /// Error type for shader compilation. @@ -79,8 +79,10 @@ pub enum ShaderReflectError { #[error("mismatched offset")] MismatchedOffset { semantic: String, - vertex: MemberOffset, - fragment: MemberOffset, + expected: usize, + received: usize, + ty: UniformMemberBlock, + pass: usize }, /// The size of the given uniform did not match up in both the vertex and fragment shader. #[error("mismatched component")] @@ -88,6 +90,7 @@ pub enum ShaderReflectError { semantic: String, vertex: u32, fragment: u32, + pass: usize }, /// The binding number is already in use. #[error("the binding is already in use")] diff --git a/librashader-reflect/src/lib.rs b/librashader-reflect/src/lib.rs index eeedef0..f0671b3 100644 --- a/librashader-reflect/src/lib.rs +++ b/librashader-reflect/src/lib.rs @@ -46,6 +46,7 @@ //! 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). #![feature(type_alias_impl_trait)] +#![feature(let_chains)] /// Shader codegen backends. pub mod back; diff --git a/librashader-reflect/src/reflect/cross.rs b/librashader-reflect/src/reflect/cross.rs index fd83db8..28c0f54 100644 --- a/librashader-reflect/src/reflect/cross.rs +++ b/librashader-reflect/src/reflect/cross.rs @@ -1,11 +1,6 @@ use crate::error::{SemanticsErrorKind, ShaderCompileError, ShaderReflectError}; use crate::front::GlslangCompilation; -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, -}; +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}; use crate::reflect::{align_uniform_size, ReflectShader}; use std::ops::Deref; @@ -273,7 +268,7 @@ where pass_number: usize, semantics: &ShaderSemantics, meta: &mut BindingMeta, - offset_type: impl Fn(usize) -> MemberOffset, + offset_type: UniformMemberBlock, blame: SemanticErrorBlame, ) -> Result<(), ShaderReflectError> { let ranges = ast.get_active_buffer_ranges(resource.id)?; @@ -298,13 +293,17 @@ where match ¶meter.semantics { UniqueSemantics::FloatParameter => { - let offset = offset_type(range.offset); - if let Some(meta) = meta.parameter_meta.get(&name) { - if offset != meta.offset { + let offset = range.offset; + if let Some(meta) = meta.parameter_meta.get_mut(&name) { + if let Some(expected) = meta.offset.offset(offset_type) + && expected != offset + { return Err(ShaderReflectError::MismatchedOffset { semantic: name, - vertex: meta.offset, - fragment: offset, + expected, + received: offset, + ty: offset_type, + pass: pass_number }); } if meta.size != typeinfo.size { @@ -312,27 +311,34 @@ where semantic: name, vertex: meta.size, fragment: typeinfo.size, + pass: pass_number }); } + + *meta.offset.offset_mut(offset_type) = Some(offset); } else { meta.parameter_meta.insert( name.clone(), VariableMeta { id: name, - offset, + offset: MemberOffset::new(offset, offset_type), size: typeinfo.size, }, ); } } semantics => { - let offset = offset_type(range.offset); - if let Some(meta) = meta.unique_meta.get(semantics) { - if offset != meta.offset { + let offset = range.offset; + if let Some(meta) = meta.unique_meta.get_mut(semantics) { + if let Some(expected) = meta.offset.offset(offset_type) + && expected != offset + { return Err(ShaderReflectError::MismatchedOffset { semantic: name, - vertex: meta.offset, - fragment: offset, + expected, + received: offset, + ty: offset_type, + pass: pass_number }); } if meta.size != typeinfo.size * typeinfo.columns { @@ -340,14 +346,17 @@ where semantic: name, vertex: meta.size, fragment: typeinfo.size, + pass: pass_number }); } + + *meta.offset.offset_mut(offset_type) = Some(offset); } else { meta.unique_meta.insert( *semantics, VariableMeta { id: name, - offset, + offset: MemberOffset::new(offset, offset_type), size: typeinfo.size * typeinfo.columns, }, ); @@ -368,14 +377,17 @@ where } } - // this will break if range is both ubo and push buf whatever - let offset = offset_type(range.offset); + let offset = range.offset; 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 { semantic: name, - vertex: meta.offset, - fragment: offset, + expected, + received: offset, + ty: offset_type, + pass: pass_number }); } @@ -383,12 +395,13 @@ where SemanticErrorBlame::Vertex => BindingStage::VERTEX, SemanticErrorBlame::Fragment => BindingStage::FRAGMENT, }); + + *meta.offset.offset_mut(offset_type) = Some(offset); } else { meta.texture_size_meta.insert( texture, TextureSizeMeta { - offset, - // todo: fix this. to allow both + offset: MemberOffset::new(offset, offset_type), stage_mask: match blame { SemanticErrorBlame::Vertex => BindingStage::VERTEX, SemanticErrorBlame::Fragment => BindingStage::FRAGMENT, @@ -607,7 +620,7 @@ where pass_number, semantics, &mut meta, - MemberOffset::Ubo, + UniformMemberBlock::Ubo, SemanticErrorBlame::Vertex, )?; } @@ -619,7 +632,7 @@ where pass_number, semantics, &mut meta, - MemberOffset::Ubo, + UniformMemberBlock::Ubo, SemanticErrorBlame::Fragment, )?; } @@ -631,7 +644,7 @@ where pass_number, semantics, &mut meta, - MemberOffset::PushConstant, + UniformMemberBlock::PushConstant, SemanticErrorBlame::Vertex, )?; } @@ -643,7 +656,7 @@ where pass_number, semantics, &mut meta, - MemberOffset::PushConstant, + UniformMemberBlock::PushConstant, SemanticErrorBlame::Fragment, )?; } diff --git a/librashader-reflect/src/reflect/semantics.rs b/librashader-reflect/src/reflect/semantics.rs index 064f829..27876b2 100644 --- a/librashader-reflect/src/reflect/semantics.rs +++ b/librashader-reflect/src/reflect/semantics.rs @@ -194,11 +194,59 @@ pub struct PushReflection { /// 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. #[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub enum MemberOffset { +pub struct MemberOffset { /// The offset of the uniform member within the UBO. - Ubo(usize), + pub ubo: Option, /// The offset of the uniform member within the Push Constant range. - PushConstant(usize), + pub push: Option, +} + + +#[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 { + match ty { + UniformMemberBlock::Ubo => {self.ubo} + UniformMemberBlock::PushConstant => {self.push} + } + } + + pub(crate) fn offset_mut(&mut self, ty: UniformMemberBlock) -> &mut Option { + match ty { + UniformMemberBlock::Ubo => {&mut self.ubo} + UniformMemberBlock::PushConstant => {&mut self.push} + } + } } /// Reflection information about a non-texture related uniform variable. diff --git a/librashader-runtime-d3d11/src/lib.rs b/librashader-runtime-d3d11/src/lib.rs index 2ca36b8..251e992 100644 --- a/librashader-runtime-d3d11/src/lib.rs +++ b/librashader-runtime-d3d11/src/lib.rs @@ -33,7 +33,8 @@ mod tests { #[test] fn triangle_d3d11() { 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", Some(&FilterChainOptionsD3D11 { use_deferred_context: false, diff --git a/librashader-runtime-gl/src/binding.rs b/librashader-runtime-gl/src/binding.rs index 55d4efe..2778f6b 100644 --- a/librashader-runtime-gl/src/binding.rs +++ b/librashader-runtime-gl/src/binding.rs @@ -1,18 +1,35 @@ use gl::types::GLint; -use librashader_reflect::reflect::semantics::BindingStage; +use librashader_reflect::reflect::semantics::{BindingStage, UniformMemberBlock}; use librashader_runtime::uniforms::{BindUniform, UniformScalar, UniformStorage}; -#[derive(Debug)] -pub enum VariableLocation { - Ubo(UniformLocation), - Push(UniformLocation), +#[derive(Debug, Copy, Clone)] +pub struct VariableLocation { + pub(crate) ubo: Option>, + pub(crate) push: Option>, } impl VariableLocation { - pub fn location(&self) -> UniformLocation { - match self { - VariableLocation::Ubo(l) | VariableLocation::Push(l) => *l, + pub fn location(&self, offset_type: UniformMemberBlock) -> Option> { + let value = match offset_type { + 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 { } validity } + + pub fn bindable(&self) -> bool { + self.is_valid(BindingStage::VERTEX | BindingStage::FRAGMENT) + } } -pub(crate) type GlUniformStorage = UniformStorage>; +pub(crate) type GlUniformStorage = UniformStorage; pub trait GlUniformScalar: UniformScalar { const FACTORY: unsafe fn(GLint, Self) -> (); @@ -54,19 +75,19 @@ impl GlUniformScalar for u32 { } pub(crate) struct GlUniformBinder; -impl BindUniform, T> for GlUniformBinder +impl BindUniform for GlUniformBinder where T: GlUniformScalar, { - fn bind_uniform(value: T, location: UniformLocation) -> 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); - } + fn bind_uniform(block: UniformMemberBlock, value: T, location: VariableLocation) -> Option<()> { + if let Some(location) = location.location(block) + && location.bindable() + { + if location.is_valid(BindingStage::VERTEX) { + unsafe { T::FACTORY(location.vertex, value); } + } + if location.is_valid(BindingStage::FRAGMENT) { + unsafe { T::FACTORY(location.fragment, value); } } Some(()) } else { @@ -75,9 +96,11 @@ where } } -impl BindUniform, &[f32; 4]> for GlUniformBinder { - fn bind_uniform(vec4: &[f32; 4], location: UniformLocation) -> Option<()> { - if location.is_valid(BindingStage::VERTEX | BindingStage::FRAGMENT) { +impl BindUniform for GlUniformBinder { + fn bind_uniform(block: UniformMemberBlock, vec4: &[f32; 4], location: VariableLocation) -> Option<()> { + if let Some(location) = location.location(block) + && location.bindable() + { unsafe { if location.is_valid(BindingStage::VERTEX) { gl::Uniform4fv(location.vertex, 1, vec4.as_ptr()); @@ -93,9 +116,11 @@ impl BindUniform, &[f32; 4]> for GlUniformBinder { } } -impl BindUniform, &[f32; 16]> for GlUniformBinder { - fn bind_uniform(mat4: &[f32; 16], location: UniformLocation) -> Option<()> { - if location.is_valid(BindingStage::VERTEX | BindingStage::FRAGMENT) { +impl BindUniform for GlUniformBinder { + fn bind_uniform(block: UniformMemberBlock, mat4: &[f32; 16], location: VariableLocation) -> Option<()> { + if let Some(location) = location.location(block) + && location.bindable() + { unsafe { if location.is_valid(BindingStage::VERTEX) { gl::UniformMatrix4fv(location.vertex, 1, gl::FALSE, mat4.as_ptr()); diff --git a/librashader-runtime-gl/src/filter_chain/filter_impl.rs b/librashader-runtime-gl/src/filter_chain/filter_impl.rs index 0e2b73d..869e486 100644 --- a/librashader-runtime-gl/src/filter_chain/filter_impl.rs +++ b/librashader-runtime-gl/src/filter_chain/filter_impl.rs @@ -53,30 +53,37 @@ pub struct FilterMutable { impl FilterChainImpl { 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 }) - } - } - 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()); + let mut location = VariableLocation { + ubo: None, + push: None, + }; - 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 } } diff --git a/librashader-runtime-gl/src/filter_pass.rs b/librashader-runtime-gl/src/filter_pass.rs index d3d852e..f963f56 100644 --- a/librashader-runtime-gl/src/filter_pass.rs +++ b/librashader-runtime-gl/src/filter_pass.rs @@ -48,17 +48,17 @@ impl TextureInput for InputTexture { } } -impl ContextOffset> for UniformOffset { +impl ContextOffset for UniformOffset { fn offset(&self) -> MemberOffset { self.offset } - fn context(&self) -> UniformLocation { - self.location.location() + fn context(&self) -> VariableLocation { + self.location } } -impl BindSemantics> for FilterPass { +impl BindSemantics for FilterPass { type InputTexture = InputTexture; type SamplerSet = SamplerSet; type DescriptorSet<'a> = (); diff --git a/librashader-runtime-gl/src/lib.rs b/librashader-runtime-gl/src/lib.rs index 0aca4a6..aa8223a 100644 --- a/librashader-runtime-gl/src/lib.rs +++ b/librashader-runtime-gl/src/lib.rs @@ -6,6 +6,7 @@ #![feature(strict_provenance)] #![feature(type_alias_impl_trait)] #![feature(let_chains)] +#![feature(is_some_and)] mod binding; mod filter_chain; @@ -35,7 +36,7 @@ mod tests { fn triangle_gl() { let (glfw, window, events, shader, vao) = gl::gl3::hello_triangle::setup(); 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 { gl_version: 0, use_dsa: false, diff --git a/librashader-runtime/src/binding.rs b/librashader-runtime/src/binding.rs index cdd7cb7..1b40680 100644 --- a/librashader-runtime/src/binding.rs +++ b/librashader-runtime/src/binding.rs @@ -50,6 +50,7 @@ where /// Trait that abstracts binding of semantics to shader uniforms. pub trait BindSemantics, S = Box<[u8]>> where + C: Copy, S: Deref + DerefMut, H: BindUniform, H: BindUniform, diff --git a/librashader-runtime/src/uniforms.rs b/librashader-runtime/src/uniforms.rs index 52c8440..d516bbb 100644 --- a/librashader-runtime/src/uniforms.rs +++ b/librashader-runtime/src/uniforms.rs @@ -1,4 +1,4 @@ -use librashader_reflect::reflect::semantics::MemberOffset; +use librashader_reflect::reflect::semantics::{MemberOffset, UniformMemberBlock}; use std::marker::PhantomData; use std::ops::{Deref, DerefMut}; @@ -15,7 +15,7 @@ pub trait BindUniform { /// 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 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. @@ -62,7 +62,7 @@ where /// All uniform data is thus written into the backing buffer storage. pub struct NoUniformBinder; impl BindUniform, T> for NoUniformBinder { - fn bind_uniform(_: T, _: Option<()>) -> Option<()> { + fn bind_uniform(_: UniformMemberBlock, _: T, _: Option<()>) -> Option<()> { None } } @@ -86,10 +86,22 @@ where pub fn inner_ubo(&self) -> &S { &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 UniformStorage where + C: Copy, S: Deref + DerefMut, { #[inline(always)] @@ -105,19 +117,19 @@ where where H: BindUniform, { - if H::bind_uniform(value, ctx).is_some() { - return; + for ty in UniformMemberBlock::TYPES { + 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::()], + 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::()], - value, - ) } /// Create a new `UniformStorage` with the given backing storage @@ -145,6 +157,7 @@ impl UniformStorage> { impl UniformStorage where + C: Copy, S: Deref + DerefMut, H: for<'a> BindUniform, { @@ -157,24 +170,26 @@ where #[inline(always)] pub fn bind_vec4(&mut self, offset: MemberOffset, value: impl Into<[f32; 4]>, ctx: C) { 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::()], + &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::()], - &vec4, - ); } } impl UniformStorage where + C: Copy, S: Deref + DerefMut, H: for<'a> BindUniform, { @@ -187,16 +202,17 @@ where /// Bind a `mat4` to the given offset. #[inline(always)] pub fn bind_mat4(&mut self, offset: MemberOffset, value: &[f32; 16], ctx: C) { - if H::bind_uniform(value, ctx).is_some() { - return; + for ty in UniformMemberBlock::TYPES { + 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::()], + 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::()], - value, - ); } }