use crate::parameters::RuntimeParameters; use crate::uniforms::{BindUniform, NoUniformBinder, UniformStorage}; use librashader_common::map::{FastHashMap, ShortString}; use librashader_common::Size; use librashader_preprocess::ShaderParameter; use librashader_reflect::reflect::semantics::{ BindingMeta, MemberOffset, Semantic, TextureBinding, TextureSemantics, UniformBinding, UniformMeta, UniqueSemantics, }; use std::ops::{Deref, DerefMut}; /// Trait for input textures used during uniform binding, pub trait TextureInput { /// Gets the size of this input texture. fn size(&self) -> Size; } /// A uniform member offset with context that needs to be resolved. pub trait ContextOffset where H: BindUniform, H: BindUniform, H: BindUniform, H: for<'a> BindUniform, H: for<'a> BindUniform, { /// Gets the `MemberOffset` part of the offset. fn offset(&self) -> MemberOffset; /// Gets the context part of the offset. fn context(&self) -> C; } impl ContextOffset, D> for MemberOffset where H: BindUniform, f32, D>, H: BindUniform, u32, D>, H: BindUniform, i32, D>, H: for<'a> BindUniform, &'a [f32; 4], D>, H: for<'a> BindUniform, &'a [f32; 16], D>, { fn offset(&self) -> MemberOffset { *self } fn context(&self) -> Option<()> { None } } /// Inputs to binding semantics pub struct UniformInputs<'a> { /// MVP pub mvp: &'a [f32; 16], /// FrameCount pub frame_count: u32, /// Rotation pub rotation: u32, /// TotalSubFrames pub total_subframes: u32, /// CurrentSubFrame pub current_subframe: u32, /// FrameDirection pub frame_direction: i32, /// OutputSize pub framebuffer_size: Size, /// FinalViewportSize pub viewport_size: Size, } /// Trait that abstracts binding of semantics to shader uniforms. pub trait BindSemantics, U = Box<[u8]>, P = Box<[u8]>> where C: Copy, U: Deref + DerefMut, P: Deref + DerefMut, H: BindUniform, H: BindUniform, H: BindUniform, H: for<'b> BindUniform, H: for<'b> BindUniform, { /// The type of the input texture used for semantic binding. type InputTexture: TextureInput; /// The set of texture samplers available. type SamplerSet; /// The descriptor set or object that holds sampler and texture bindings. type DescriptorSet<'a>; /// The device context containing the state of the graphics processor. type DeviceContext; /// The type of uniform offsets to use. type UniformOffset: ContextOffset; /// Bind a texture to the input descriptor set fn bind_texture<'a>( descriptors: &mut Self::DescriptorSet<'a>, samplers: &Self::SamplerSet, binding: &TextureBinding, texture: &Self::InputTexture, device: &Self::DeviceContext, ); #[allow(clippy::too_many_arguments)] /// Write uniform and texture semantics to the provided storages. fn bind_semantics<'a>( device: &Self::DeviceContext, sampler_set: &Self::SamplerSet, uniform_storage: &mut UniformStorage, descriptor_set: &mut Self::DescriptorSet<'a>, uniform_inputs: UniformInputs<'_>, original: &Self::InputTexture, source: &Self::InputTexture, uniform_bindings: &FastHashMap, texture_meta: &FastHashMap, TextureBinding>, pass_outputs: impl Iterator>>, pass_feedback: impl Iterator>>, original_history: impl Iterator>>, lookup_textures: impl Iterator)>, parameter_defaults: &FastHashMap, runtime_parameters: &RuntimeParameters, ) { let runtime_parameters = runtime_parameters.parameters.load(); // Bind MVP if let Some(offset) = uniform_bindings.get(&UniqueSemantics::MVP.into()) { uniform_storage.bind_mat4( offset.offset(), uniform_inputs.mvp, offset.context(), device, ); } // Bind OutputSize if let Some(offset) = uniform_bindings.get(&UniqueSemantics::Output.into()) { uniform_storage.bind_vec4( offset.offset(), uniform_inputs.framebuffer_size, offset.context(), device, ); } // bind FinalViewportSize if let Some(offset) = uniform_bindings.get(&UniqueSemantics::FinalViewport.into()) { uniform_storage.bind_vec4( offset.offset(), uniform_inputs.viewport_size, offset.context(), device, ); } // bind FrameCount if let Some(offset) = uniform_bindings.get(&UniqueSemantics::FrameCount.into()) { uniform_storage.bind_scalar( offset.offset(), uniform_inputs.frame_count, offset.context(), device, ); } // bind FrameDirection if let Some(offset) = uniform_bindings.get(&UniqueSemantics::FrameDirection.into()) { uniform_storage.bind_scalar( offset.offset(), uniform_inputs.frame_direction, offset.context(), device, ); } // bind Rotation if let Some(offset) = uniform_bindings.get(&UniqueSemantics::Rotation.into()) { uniform_storage.bind_scalar( offset.offset(), uniform_inputs.rotation, offset.context(), device, ); } // bind TotalSubFrames if let Some(offset) = uniform_bindings.get(&UniqueSemantics::TotalSubFrames.into()) { uniform_storage.bind_scalar( offset.offset(), uniform_inputs.total_subframes, offset.context(), device, ); } // bind CurrentSubFrames if let Some(offset) = uniform_bindings.get(&UniqueSemantics::CurrentSubFrame.into()) { uniform_storage.bind_scalar( offset.offset(), uniform_inputs.current_subframe, offset.context(), device, ); } // bind Original sampler if let Some(binding) = texture_meta.get(&TextureSemantics::Original.semantics(0)) { Self::bind_texture(descriptor_set, sampler_set, binding, original, device); } // bind OriginalSize if let Some(offset) = uniform_bindings.get(&TextureSemantics::Original.semantics(0).into()) { uniform_storage.bind_vec4(offset.offset(), original.size(), offset.context(), device); } // bind Source sampler if let Some(binding) = texture_meta.get(&TextureSemantics::Source.semantics(0)) { Self::bind_texture(descriptor_set, sampler_set, binding, source, device); } // bind SourceSize if let Some(offset) = uniform_bindings.get(&TextureSemantics::Source.semantics(0).into()) { uniform_storage.bind_vec4(offset.offset(), source.size(), offset.context(), device); } // OriginalHistory0 aliases OriginalHistory // bind OriginalHistory0 sampler if let Some(binding) = texture_meta.get(&TextureSemantics::OriginalHistory.semantics(0)) { Self::bind_texture(descriptor_set, sampler_set, binding, original, device); } // bind OriginalHistory0Size if let Some(offset) = uniform_bindings.get(&TextureSemantics::OriginalHistory.semantics(0).into()) { uniform_storage.bind_vec4(offset.offset(), original.size(), offset.context(), device); } // bind OriginalHistory1-.. for (index, history) in original_history.enumerate() { let Some(history) = history else { continue; }; let history = history.as_ref(); if let Some(binding) = texture_meta.get(&TextureSemantics::OriginalHistory.semantics(index + 1)) { Self::bind_texture(descriptor_set, sampler_set, binding, history, device); } if let Some(offset) = uniform_bindings.get( &TextureSemantics::OriginalHistory .semantics(index + 1) .into(), ) { uniform_storage.bind_vec4( offset.offset(), history.size(), offset.context(), device, ); } } // bind PassOutput0.. // The caller should be responsible for limiting this up to // pass_index for (index, output) in pass_outputs.enumerate() { let Some(output) = output else { continue; }; let output = output.as_ref(); if let Some(binding) = texture_meta.get(&TextureSemantics::PassOutput.semantics(index)) { Self::bind_texture(descriptor_set, sampler_set, binding, output, device); } if let Some(offset) = uniform_bindings.get(&TextureSemantics::PassOutput.semantics(index).into()) { uniform_storage.bind_vec4(offset.offset(), output.size(), offset.context(), device); } } // bind PassFeedback0.. for (index, feedback) in pass_feedback.enumerate() { let Some(output) = feedback else { continue; }; let feedback = output.as_ref(); if let Some(binding) = texture_meta.get(&TextureSemantics::PassFeedback.semantics(index)) { Self::bind_texture(descriptor_set, sampler_set, binding, feedback, device); } if let Some(offset) = uniform_bindings.get(&TextureSemantics::PassFeedback.semantics(index).into()) { uniform_storage.bind_vec4( offset.offset(), feedback.size(), offset.context(), device, ); } } // bind User parameters for (id, offset) in uniform_bindings .iter() .filter_map(|(binding, value)| match binding { UniformBinding::Parameter(id) => Some((id, value)), _ => None, }) { let id = id.as_str(); let default = parameter_defaults.get(id).map_or(0f32, |f| f.initial); let value = *runtime_parameters.get(id).unwrap_or(&default); uniform_storage.bind_scalar(offset.offset(), value, offset.context(), device); } // bind luts for (index, lut) in lookup_textures { let lut = lut.as_ref(); if let Some(binding) = texture_meta.get(&TextureSemantics::User.semantics(index)) { Self::bind_texture(descriptor_set, sampler_set, binding, lut, device); } if let Some(offset) = uniform_bindings.get(&TextureSemantics::User.semantics(index).into()) { uniform_storage.bind_vec4(offset.offset(), lut.size(), offset.context(), device); } } } } pub struct BindingRequirements { pub(crate) required_history: usize, pub(crate) uses_final_pass_as_feedback: bool, } /// Trait for objects that can be used to create a binding map. pub trait BindingUtil { /// Create the uniform binding map with the given reflection information. fn create_binding_map( &self, f: impl Fn(&dyn UniformMeta) -> T, ) -> FastHashMap; /// Calculate the number of required images for history. fn calculate_requirements<'a>(pass_meta: impl Iterator) -> BindingRequirements where Self: 'a; } impl BindingUtil for BindingMeta { fn create_binding_map( &self, f: impl Fn(&dyn UniformMeta) -> T, ) -> FastHashMap { let mut uniform_bindings = FastHashMap::default(); for param in self.parameter_meta.values() { uniform_bindings.insert(UniformBinding::Parameter(param.id.clone()), f(param)); } for (semantics, param) in &self.unique_meta { uniform_bindings.insert(UniformBinding::SemanticVariable(*semantics), f(param)); } for (semantics, param) in &self.texture_size_meta { uniform_bindings.insert(UniformBinding::TextureSize(*semantics), f(param)); } uniform_bindings } fn calculate_requirements<'a>(pass_meta: impl Iterator) -> BindingRequirements where Self: 'a, { let mut required_images = 0; let mut len: i64 = 0; let mut latest_feedback_pass: i64 = -1; for pass in pass_meta { len += 1; // If a shader uses history size, but not history, we still need to keep the texture. let history_texture_max_index = pass .texture_meta .iter() .filter(|(semantics, _)| semantics.semantics == TextureSemantics::OriginalHistory) .map(|(semantic, _)| semantic.index) .fold(0, std::cmp::max); let history_texture_size_max_index = pass .texture_size_meta .iter() .filter(|(semantics, _)| semantics.semantics == TextureSemantics::OriginalHistory) .map(|(semantic, _)| semantic.index) .fold(0, std::cmp::max); let feedback_max_index = pass .texture_meta .iter() .filter(|(semantics, _)| semantics.semantics == TextureSemantics::PassFeedback) .map(|(semantic, _)| semantic.index as i64) .fold(-1, std::cmp::max); let feedback_max_size_index = pass .texture_size_meta .iter() .filter(|(semantics, _)| semantics.semantics == TextureSemantics::PassFeedback) .map(|(semantic, _)| semantic.index as i64) .fold(-1, std::cmp::max); latest_feedback_pass = std::cmp::max(latest_feedback_pass, feedback_max_index); latest_feedback_pass = std::cmp::max(latest_feedback_pass, feedback_max_size_index); required_images = std::cmp::max(required_images, history_texture_max_index); required_images = std::cmp::max(required_images, history_texture_size_max_index); } let uses_feedback = if latest_feedback_pass.is_negative() { false } else { // Technically = but we can be permissive here // account for off by 1 latest_feedback_pass + 1 >= len }; BindingRequirements { required_history: required_images, uses_final_pass_as_feedback: uses_feedback, } } } #[macro_export] macro_rules! impl_default_frame_options { ($ty:ident) => { /// Options for each frame. #[repr(C)] #[derive(Debug, Clone)] pub struct $ty { /// Whether or not to clear the history buffers. pub clear_history: bool, /// The direction of rendering. /// -1 indicates that the frames are played in reverse order. pub frame_direction: i32, /// The rotation of the output. 0 = 0deg, 1 = 90deg, 2 = 180deg, 3 = 270deg. pub rotation: u32, /// The total number of subframes ran. Default is 1. pub total_subframes: u32, // The current sub frame. Default is 1. pub current_subframe: u32, } impl Default for $ty { fn default() -> Self { Self { clear_history: false, frame_direction: 1, rotation: 0, total_subframes: 1, current_subframe: 1, } } } }; }