librashader/librashader-runtime-d3d9/src/binding.rs

239 lines
7.2 KiB
Rust

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<String, ConstantDescriptor>,
vs_constants: &FastHashMap<String, ConstantDescriptor>,
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<RegisterAssignment>,
pub(crate) vs: Option<RegisterAssignment>,
}
impl ContextOffset<D3D9UniformBinder, ConstantRegister, IDirect3DDevice9> for ConstantRegister {
fn offset(&self) -> MemberOffset {
self._offset
}
fn context(&self) -> ConstantRegister {
*self
}
}
pub(crate) type D3D9UniformStorage =
UniformStorage<D3D9UniformBinder, ConstantRegister, Box<[u8]>, 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<f32> + Copy {}
impl D3D9UniformScalar for u32 {}
impl D3D9UniformScalar for i32 {}
impl D3D9UniformScalar for f32 {}
pub(crate) struct D3D9UniformBinder;
impl<T: D3D9UniformScalar> BindUniform<ConstantRegister, T, IDirect3DDevice9>
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<ConstantRegister, &[f32; 4], IDirect3DDevice9> 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<ConstantRegister, &[f32; 16], IDirect3DDevice9> 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<String, ConstantDescriptor>,
) {
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;
}
}