use librashader_common::map::FastHashMap; use librashader_reflect::back::hlsl::CrossHlslContext; use librashader_reflect::reflect::semantics::{ BindingMeta, MemberOffset, UniformMemberBlock, UniformMeta, }; use librashader_runtime::binding::ContextOffset; use librashader_runtime::uniforms::{BindUniform, UniformScalar, UniformStorage}; use num_traits::AsPrimitive; use std::fmt::Debug; use windows::Win32::Graphics::Direct3D9::IDirect3DDevice9; // only cN (float) or sN (sampler) registers allowed. #[derive(Debug, Copy, Clone)] pub enum RegisterSet { Float, Sampler, } #[derive(Debug)] pub struct ConstantDescriptor { pub assignment: RegisterAssignment, pub set: RegisterSet, } #[derive(Debug, Copy, Clone)] pub struct RegisterAssignment { pub index: u32, pub count: u32, } #[derive(Debug, Copy, Clone)] pub struct ConstantRegister { pub register: VariableRegister, pub _offset: MemberOffset, } impl ConstantRegister { pub fn reflect_register_assignment( meta: &dyn UniformMeta, ps_constants: &FastHashMap, vs_constants: &FastHashMap, context: &CrossHlslContext, ) -> Self { let uniform_name = meta.id(); // Yeah this is n^2 but ah well. let ps = ps_constants .iter() .find_map(|(mangled_name, register)| { if context .fragment_buffers .contains_uniform(uniform_name, mangled_name) { Some(register) } else { None } }) .map(|c| c.assignment); let vs = vs_constants .iter() .find_map(|(mangled_name, register)| { if context .vertex_buffers .contains_uniform(uniform_name, mangled_name) { Some(register) } else { None } }) .map(|c| c.assignment); ConstantRegister { register: VariableRegister { ps, vs }, _offset: meta.offset(), } } } #[derive(Debug, Copy, Clone)] pub struct VariableRegister { pub(crate) ps: Option, pub(crate) vs: Option, } impl ContextOffset for ConstantRegister { fn offset(&self) -> MemberOffset { self._offset } fn context(&self) -> ConstantRegister { *self } } pub(crate) type D3D9UniformStorage = UniformStorage, Box<[u8]>, IDirect3DDevice9>; /// Trait for uniform scalars that can be converted to f32 as required by SM3.0 /// Mostly used to get around orphan rule. #[allow(unused)] trait D3D9UniformScalar: UniformScalar + AsPrimitive + Copy {} impl D3D9UniformScalar for u32 {} impl D3D9UniformScalar for i32 {} impl D3D9UniformScalar for f32 {} pub(crate) struct D3D9UniformBinder; impl BindUniform for D3D9UniformBinder { fn bind_uniform( _block: UniformMemberBlock, value: T, context: ConstantRegister, device: &IDirect3DDevice9, ) -> Option<()> { let zeroed = T::zeroed().as_(); let value = [value.as_(), zeroed, zeroed, zeroed]; let location = &context.register; unsafe { if let Some(location) = location.vs { if let Err(err) = device.SetVertexShaderConstantF(location.index, value.as_ptr(), location.count) { println!( "[librashader-runtime-d3d9] unable to bind vertex {}: {err}", location.index ); } } } unsafe { if let Some(location) = location.ps { if let Err(err) = device.SetPixelShaderConstantF(location.index, value.as_ptr(), location.count) { println!( "[librashader-runtime-d3d9] unable to bind vertex {}: {err}", location.index ); } } } Some(()) } } impl BindUniform for D3D9UniformBinder { fn bind_uniform( _block: UniformMemberBlock, vec4: &[f32; 4], context: ConstantRegister, device: &IDirect3DDevice9, ) -> Option<()> { let location = &context.register; unsafe { if let Some(location) = location.vs { if let Err(err) = device.SetVertexShaderConstantF(location.index, vec4.as_ptr(), location.count) { println!( "[librashader-runtime-d3d9] unable to bind vertex {}: {err}", location.index ); } } } unsafe { if let Some(location) = location.ps { if let Err(err) = device.SetPixelShaderConstantF(location.index, vec4.as_ptr(), location.count) { println!( "[librashader-runtime-d3d9] unable to bind fragment {}: {err}", location.index ); } } } Some(()) } } impl BindUniform for D3D9UniformBinder { fn bind_uniform( _block: UniformMemberBlock, mat4: &[f32; 16], context: ConstantRegister, device: &IDirect3DDevice9, ) -> Option<()> { let location = &context.register; unsafe { if let Some(location) = location.vs { if let Err(err) = device.SetVertexShaderConstantF(location.index, mat4.as_ptr(), location.count) { println!( "[librashader-runtime-d3d9] unable to bind vertex {}: {err}", location.index ); } } } unsafe { if let Some(location) = location.ps { if let Err(err) = device.SetPixelShaderConstantF(location.index, mat4.as_ptr(), location.count) { println!( "[librashader-runtime-d3d9] unable to bind fragment {}: {err}", location.index ); } } } Some(()) } } pub fn update_sampler_bindings( meta: &mut BindingMeta, ps_constants: &FastHashMap, ) { for (_, binding) in meta.texture_meta.iter_mut() { let Some(descriptor) = ps_constants.get(&format!("LIBRA_SAMPLER2D_{}", binding.binding)) else { continue; }; // eprintln!( // "updating binding {} to {}", // binding.binding, descriptor.assignment.index // ); binding.binding = descriptor.assignment.index; } }