rt(d3d9): add a runtime for direct3d 9

This commit is contained in:
chyyran 2024-03-03 01:07:07 -05:00 committed by Ronny Chan
parent 5feac91af2
commit b7071958bd
33 changed files with 3286 additions and 43 deletions

18
Cargo.lock generated
View file

@ -1645,6 +1645,24 @@ dependencies = [
"windows",
]
[[package]]
name = "librashader-runtime-d3d9"
version = "0.2.4"
dependencies = [
"array-concat",
"bytemuck",
"gfx-maths",
"librashader-cache",
"librashader-common",
"librashader-preprocess",
"librashader-presets",
"librashader-reflect",
"librashader-runtime",
"num-traits",
"thiserror",
"windows",
]
[[package]]
name = "librashader-runtime-gl"
version = "0.2.6"

View file

@ -14,7 +14,7 @@ members = [
"librashader-runtime-wgpu",
"librashader-cache",
"librashader-capi",
"librashader-build-script"]
"librashader-build-script", "librashader-runtime-d3d9"]
resolver = "2"
[workspace.dependencies]

View file

@ -14,6 +14,7 @@ description = "RetroArch shaders for all."
[features]
default = []
opengl = ["gl"]
d3d9 = ["windows"]
d3d11 = ["windows", "dxgi"]
d3d12 = ["windows", "dxgi"]
dxgi = ["windows"]
@ -36,6 +37,7 @@ features = [
"Win32_Foundation",
"Win32_Graphics_Dxgi_Common",
"Win32_Graphics_Direct3D",
"Win32_Graphics_Direct3D9",
"Win32_Graphics_Direct3D11",
"Win32_Graphics_Direct3D12",
]

View file

@ -0,0 +1,78 @@
use crate::{FilterMode, ImageFormat, WrapMode};
use windows::Win32::Graphics::Direct3D9;
//
impl From<ImageFormat> for Direct3D9::D3DFORMAT {
fn from(format: ImageFormat) -> Self {
match format {
ImageFormat::Unknown => Direct3D9::D3DFMT_UNKNOWN,
ImageFormat::R8Unorm => Direct3D9::D3DFMT_R8G8B8,
ImageFormat::R8Uint => Direct3D9::D3DFMT_R8G8B8,
ImageFormat::R8Sint => Direct3D9::D3DFMT_R8G8B8,
ImageFormat::R8G8B8A8Unorm => Direct3D9::D3DFMT_A8R8G8B8,
ImageFormat::R8G8B8A8Uint => Direct3D9::D3DFMT_A8R8G8B8,
ImageFormat::R8G8B8A8Sint => Direct3D9::D3DFMT_A8R8G8B8,
ImageFormat::R8G8B8A8Srgb => Direct3D9::D3DFMT_A8R8G8B8,
ImageFormat::A2B10G10R10UnormPack32 => Direct3D9::D3DFMT_A2B10G10R10,
ImageFormat::A2B10G10R10UintPack32 => Direct3D9::D3DFMT_A2B10G10R10,
ImageFormat::R16Sfloat => Direct3D9::D3DFMT_R16F,
ImageFormat::R16G16Uint => Direct3D9::D3DFMT_G16R16,
ImageFormat::R16G16Sint => Direct3D9::D3DFMT_G16R16,
ImageFormat::R16G16Sfloat => Direct3D9::D3DFMT_G16R16F,
ImageFormat::R16G16B16A16Uint => Direct3D9::D3DFMT_A16B16G16R16,
ImageFormat::R16G16B16A16Sint => Direct3D9::D3DFMT_A16B16G16R16,
ImageFormat::R16G16B16A16Sfloat => Direct3D9::D3DFMT_A16B16G16R16F,
ImageFormat::R32Sfloat => Direct3D9::D3DFMT_R32F,
_ => Direct3D9::D3DFMT_UNKNOWN,
}
}
}
//
impl From<Direct3D9::D3DFORMAT> for ImageFormat {
fn from(format: Direct3D9::D3DFORMAT) -> Self {
match format {
Direct3D9::D3DFMT_R8G8B8 => ImageFormat::R8Unorm,
Direct3D9::D3DFMT_A8R8G8B8 => ImageFormat::R8G8B8A8Unorm,
Direct3D9::D3DFMT_A2B10G10R10 => ImageFormat::A2B10G10R10UnormPack32,
Direct3D9::D3DFMT_R16F => ImageFormat::R16Sfloat,
Direct3D9::D3DFMT_G16R16 => ImageFormat::R16G16Uint,
Direct3D9::D3DFMT_G16R16F => ImageFormat::R16G16Sfloat,
Direct3D9::D3DFMT_A16B16G16R16 => ImageFormat::R16G16B16A16Uint,
Direct3D9::D3DFMT_A16B16G16R16F => ImageFormat::R16G16B16A16Sfloat,
Direct3D9::D3DFMT_R32F => ImageFormat::R32Sfloat,
_ => ImageFormat::Unknown,
}
}
}
impl From<WrapMode> for Direct3D9::D3DTEXTUREADDRESS {
fn from(value: WrapMode) -> Self {
match value {
WrapMode::ClampToBorder => Direct3D9::D3DTADDRESS_BORDER,
WrapMode::ClampToEdge => Direct3D9::D3DTADDRESS_CLAMP,
WrapMode::Repeat => Direct3D9::D3DTADDRESS_WRAP,
WrapMode::MirroredRepeat => Direct3D9::D3DTADDRESS_MIRROR,
}
}
}
impl From<FilterMode> for Direct3D9::D3DTEXTUREFILTER {
fn from(value: FilterMode) -> Self {
match value {
FilterMode::Linear => Direct3D9::D3DFILTER_LINEAR,
FilterMode::Nearest => Direct3D9::D3DFILTER_NEAREST,
}
}
}
// impl FilterMode {
// /// Get the mipmap filtering mode for the given combination.
// pub fn d3d9_mip(&self, mip: FilterMode) -> Direct3D9::D3DTEXTUREFILTER {
// match (self, mip) {
// (FilterMode::Linear, FilterMode::Linear) => Direct3D9::D3DFILTER_MIPLINEAR,
// (FilterMode::Linear, FilterMode::Nearest) => Direct3D9::D3DFILTER_LINEARMIPNEAREST,
// (FilterMode::Nearest, FilterMode::Linear) => Direct3D9::D3DFILTER_MIPNEAREST,
// _ => Direct3D9::D3DFILTER_MIPNEAREST
// }
// }
// }

View file

@ -16,6 +16,10 @@ pub mod wgpu;
#[cfg(all(target_os = "windows", feature = "dxgi"))]
pub mod dxgi;
/// Direct3D 9 common conversions.
#[cfg(all(target_os = "windows", feature = "d3d9"))]
pub mod d3d9;
/// Direct3D 11 common conversions.
#[cfg(all(target_os = "windows", feature = "d3d11"))]
pub mod d3d11;

View file

@ -9,10 +9,94 @@ use crate::reflect::ReflectShader;
/// The HLSL shader model version to target.
pub use spirv_cross::hlsl::ShaderModel as HlslShaderModel;
/// Buffer assignment information
#[derive(Debug, Clone)]
pub struct HlslBufferAssignment {
/// The name of the buffer
pub name: String,
/// The id of the buffer
pub id: u32,
}
/// Buffer assignment information
#[derive(Debug, Clone, Default)]
pub struct HlslBufferAssignments {
/// Buffer assignment information for UBO
pub ubo: Option<HlslBufferAssignment>,
/// Buffer assignment information for Push
pub push: Option<HlslBufferAssignment>,
}
impl HlslBufferAssignments {
fn find_mangled_id(mangled_name: &str) -> Option<u32> {
if !mangled_name.starts_with("_") {
return None;
}
let Some(next_underscore) = mangled_name[1..].find("_") else {
return None;
};
mangled_name[1..next_underscore + 1].parse().ok()
}
fn find_mangled_name(buffer_name: &str, uniform_name: &str, mangled_name: &str) -> bool {
// name prependded
if mangled_name[buffer_name.len()..].starts_with("_")
&& &mangled_name[buffer_name.len() + 1..] == uniform_name
{
return true;
}
return false;
}
// Check if the mangled name matches.
pub fn contains_uniform(&self, uniform_name: &str, mangled_name: &str) -> bool {
let is_likely_id_mangled = mangled_name.starts_with("_");
if !mangled_name.ends_with(uniform_name) {
return false;
}
if let Some(ubo) = &self.ubo {
if is_likely_id_mangled {
if let Some(id) = Self::find_mangled_id(mangled_name) {
if id == ubo.id {
return true;
}
}
}
// name prependded
if Self::find_mangled_name(&ubo.name, uniform_name, mangled_name) {
return true;
}
}
if let Some(push) = &self.push {
if is_likely_id_mangled {
if let Some(id) = Self::find_mangled_id(mangled_name) {
if id == push.id {
return true;
}
}
}
// name prependded
if Self::find_mangled_name(&push.name, uniform_name, mangled_name) {
return true;
}
}
return false;
}
}
/// The context for a HLSL compilation via spirv-cross.
pub struct CrossHlslContext {
/// The compiled HLSL program.
pub artifact: CompiledProgram<spirv_cross::hlsl::Target>,
pub vertex_buffers: HlslBufferAssignments,
pub fragment_buffers: HlslBufferAssignments,
}
impl FromCompilation<SpirvCompilation, SpirvCross> for HLSL {
@ -30,3 +114,30 @@ impl FromCompilation<SpirvCompilation, SpirvCross> for HLSL {
})
}
}
#[cfg(test)]
mod test {
use crate::back::hlsl::HlslBufferAssignments;
#[test]
pub fn mangled_id_test() {
assert_eq!(HlslBufferAssignments::find_mangled_id("_19_MVP"), Some(19));
assert_eq!(HlslBufferAssignments::find_mangled_id("_19"), None);
assert_eq!(HlslBufferAssignments::find_mangled_id("_19_"), Some(19));
assert_eq!(HlslBufferAssignments::find_mangled_id("19_"), None);
assert_eq!(HlslBufferAssignments::find_mangled_id("_19MVP"), None);
assert_eq!(
HlslBufferAssignments::find_mangled_id("_19_29_MVP"),
Some(19)
);
}
#[test]
pub fn mangled_name_test() {
assert!(HlslBufferAssignments::find_mangled_name(
"params",
"MVP",
"params_MVP"
));
}
}

View file

@ -1,9 +1,11 @@
use crate::back::hlsl::CrossHlslContext;
use crate::back::hlsl::{CrossHlslContext, HlslBufferAssignment, HlslBufferAssignments};
use crate::back::targets::HLSL;
use crate::back::{CompileShader, ShaderCompilerOutput};
use crate::error::ShaderCompileError;
use crate::reflect::cross::{CompiledAst, CompiledProgram, CrossReflect};
use spirv_cross::hlsl::ShaderModel as HlslShaderModel;
use spirv_cross::spirv::Decoration;
use spirv_cross::ErrorCode;
pub(crate) type HlslReflect = CrossReflect<spirv_cross::hlsl::Target>;
@ -22,6 +24,84 @@ impl CompileShader<HLSL> for CrossReflect<spirv_cross::hlsl::Target> {
self.vertex.set_compiler_options(&options)?;
self.fragment.set_compiler_options(&options)?;
// todo: options
let vertex_resources = self.vertex.get_shader_resources()?;
let fragment_resources = self.fragment.get_shader_resources()?;
let mut vertex_buffer_assignment = HlslBufferAssignments::default();
let mut fragment_buffer_assignment = HlslBufferAssignments::default();
if vertex_resources.uniform_buffers.len() > 1 {
return Err(ShaderCompileError::SpirvCrossCompileError(
ErrorCode::CompilationError(String::from(
"Cannot have more than one uniform buffer",
)),
));
}
if let Some(buf) = vertex_resources.uniform_buffers.first() {
vertex_buffer_assignment.ubo = Some(HlslBufferAssignment {
name: buf.name.clone(),
id: buf.id,
})
}
if vertex_resources.push_constant_buffers.len() > 1 {
return Err(ShaderCompileError::SpirvCrossCompileError(
ErrorCode::CompilationError(String::from(
"Cannot have more than one push constant buffer",
)),
));
}
if let Some(buf) = vertex_resources.push_constant_buffers.first() {
vertex_buffer_assignment.push = Some(HlslBufferAssignment {
name: buf.name.clone(),
id: buf.id,
})
}
if fragment_resources.uniform_buffers.len() > 1 {
return Err(ShaderCompileError::SpirvCrossCompileError(
ErrorCode::CompilationError(String::from(
"Cannot have more than one uniform buffer",
)),
));
}
if let Some(buf) = fragment_resources.uniform_buffers.first() {
fragment_buffer_assignment.ubo = Some(HlslBufferAssignment {
name: buf.name.clone(),
id: buf.id,
})
}
if fragment_resources.push_constant_buffers.len() > 1 {
return Err(ShaderCompileError::SpirvCrossCompileError(
ErrorCode::CompilationError(String::from(
"Cannot have more than one push constant buffer",
)),
));
}
if let Some(buf) = fragment_resources.push_constant_buffers.first() {
fragment_buffer_assignment.push = Some(HlslBufferAssignment {
name: buf.name.clone(),
id: buf.id,
})
}
if sm == HlslShaderModel::V3_0 {
for res in &fragment_resources.sampled_images {
let binding = self.fragment.get_decoration(res.id, Decoration::Binding)?;
self.fragment
.set_name(res.id, &format!("LIBRA_SAMPLER2D_{binding}"))?;
// self.fragment
// .unset_decoration(res.id, Decoration::Binding)?;
}
}
Ok(ShaderCompilerOutput {
vertex: self.vertex.compile()?,
fragment: self.fragment.compile()?,
@ -30,6 +110,9 @@ impl CompileShader<HLSL> for CrossReflect<spirv_cross::hlsl::Target> {
vertex: CompiledAst(self.vertex),
fragment: CompiledAst(self.fragment),
},
vertex_buffers: vertex_buffer_assignment,
fragment_buffers: fragment_buffer_assignment,
},
})
}

View file

@ -741,12 +741,16 @@ mod test {
use crate::reflect::ReflectShader;
use rustc_hash::FxHashMap;
use crate::back::hlsl::CrossHlslContext;
use crate::back::targets::HLSL;
use crate::back::{CompileShader, ShaderCompilerOutput};
use crate::front::{Glslang, ShaderInputCompiler};
use crate::reflect::semantics::{Semantic, ShaderSemantics, UniformSemantic, UniqueSemantics};
use librashader_common::map::FastHashMap;
use librashader_preprocess::ShaderSource;
use spirv_cross::glsl;
use spirv_cross::glsl::{CompilerOptions, Version};
use spirv_cross::hlsl::ShaderModel;
use spirv_cross::{glsl, hlsl};
#[test]
pub fn test_into() {
@ -763,8 +767,8 @@ mod test {
);
}
let spirv = Glslang::compile(&result).unwrap();
let mut reflect = CrossReflect::<glsl::Target>::try_from(&spirv).unwrap();
let _shader_reflection = reflect
let mut reflect = CrossReflect::<hlsl::Target>::try_from(&spirv).unwrap();
let shader_reflection = reflect
.reflect(
0,
&ShaderSemantics {
@ -773,10 +777,20 @@ mod test {
},
)
.unwrap();
let mut opts = CompilerOptions::default();
opts.version = Version::V4_60;
opts.enable_420_pack_extension = false;
// let compiled: ShaderCompilerOutput<String, CrossWgslContext> = <CrossReflect<glsl::Target> as CompileShader<WGSL>>::compile(reflect, Version::V3_30).unwrap();
let mut opts = hlsl::CompilerOptions::default();
opts.shader_model = ShaderModel::V3_0;
let compiled: ShaderCompilerOutput<String, CrossHlslContext> =
<CrossReflect<hlsl::Target> as CompileShader<HLSL>>::compile(
reflect,
Some(ShaderModel::V3_0),
)
.unwrap();
println!("{:?}", shader_reflection.meta);
println!("{}", compiled.fragment);
println!("{}", compiled.vertex);
// // eprintln!("{shader_reflection:#?}");
// eprintln!("{}", compiled.fragment)
// let mut loader = rspirv::dr::Loader::new();

View file

@ -5,13 +5,13 @@ use crate::hello_triangle::{DXSample, SampleCommandLine};
#[test]
fn triangle_d3d12() {
let sample = hello_triangle::d3d12_hello_triangle::Sample::new(
"../test/shaders_slang/bezel/Mega_Bezel/Presets/MBZ__0__SMOOTH-ADV.slangp",
// "../test/shaders_slang/bezel/Mega_Bezel/Presets/MBZ__0__SMOOTH-ADV.slangp",
// "../test/shaders_slang/crt/crt-lottes.slangp",
// "../test/basic.slangp",
// "../test/shaders_slang/handheld/console-border/gbc-lcd-grid-v2.slangp",
// "../test/Mega_Bezel_Packs/Duimon-Mega-Bezel/Presets/Advanced/Nintendo_GBA_SP/GBA_SP-[ADV]-[LCD-GRID]-[Night].slangp",
// "../test/slang-shaders/test/feedback.slangp",
// "../test/slang-shaders/test/history.slangp",
"../test/shaders_slang/test/history.slangp",
// "../test/shaders_slang/crt/crt-royale.slangp",
// "../test/slang-shaders/vhs/VHSPro.slangp",
&SampleCommandLine {

View file

@ -0,0 +1,63 @@
[package]
name = "librashader-runtime-d3d9"
edition = "2021"
license = "MPL-2.0 OR GPL-3.0-only"
version = "0.2.4"
authors = ["Ronny Chan <ronny@ronnychan.ca>"]
repository = "https://github.com/SnowflakePowered/librashader"
readme = "../README.md"
categories = ["emulators", "compilers", "graphics"]
keywords = ["shader", "retroarch", "SPIR-V"]
description = "RetroArch shaders for all."
[dependencies]
librashader-common = { path = "../librashader-common", features = ["d3d9", "d3d11"], version = "0.2.4" }
librashader-presets = { path = "../librashader-presets", version = "0.2.4" }
librashader-preprocess = { path = "../librashader-preprocess", version = "0.2.4" }
librashader-reflect = { path = "../librashader-reflect", version = "0.2.4" }
librashader-runtime = { path = "../librashader-runtime", version = "0.2.4" }
librashader-cache = { path = "../librashader-cache", version = "0.2.4", features = ["d3d"] }
thiserror = "1.0.37"
bytemuck = "1.12.3"
array-concat = "0.5.2"
num-traits = "0.2.18"
[target.'cfg(windows)'.dependencies.windows]
workspace = true
features = [
"Win32_Foundation",
"Win32_Graphics_Direct3D",
"Win32_Graphics_Direct3D9",
"Win32_Graphics_Direct3D_Fxc",
"Win32_System_Threading",
"Win32_Security",
"Foundation_Numerics"
]
[target.'cfg(windows)'.dev-dependencies.windows]
workspace = true
features = [
"Win32_Foundation",
"Win32_Graphics_Direct3D",
"Win32_Graphics_Direct3D9",
"Win32_Graphics_Direct3D9on12",
"Win32_Graphics_Direct3D_Fxc",
"Win32_Graphics_Gdi",
"Win32_Security",
"Win32_System_LibraryLoader",
"Win32_System_Threading",
"Win32_UI_WindowsAndMessaging",
"Win32_UI",
"Foundation_Numerics"
]
[[test]]
name = "triangle"
[dev-dependencies]
gfx-maths = "0.2.8"
[package.metadata.docs.rs]
features = ["librashader-cache/docsrs"]

View file

@ -0,0 +1,6 @@
pub fn main() {
#[cfg(all(target_os = "windows"))]
{
// println!("cargo:rustc-link-arg=/DELAYLOAD:D3DX9_43.dll");
}
}

View file

@ -0,0 +1,236 @@
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
.fragment_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 D3D9UniformScalar: UniformScalar + AsPrimitive<f32> + Copy {}
impl D3D9UniformScalar for u32 {}
impl D3D9UniformScalar for i32 {}
impl D3D9UniformScalar for f32 {}
pub(crate) struct D3D9UniformBinder;
impl<T> BindUniform<ConstantRegister, T, IDirect3DDevice9> for D3D9UniformBinder
where
T: D3D9UniformScalar,
{
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;
}
}

View file

@ -0,0 +1,430 @@
use std::ffi::c_void;
use windows::core::imp::BOOL;
use windows::core::{IntoParam, HRESULT, PCSTR};
use windows::Win32::Graphics::Direct3D::{ID3DBlob, ID3DInclude, D3D_SHADER_MACRO};
const D3DERR_INVALIDCALL: u32 = 0x8876086c;
#[allow(dead_code)]
pub mod raw {
use super::*;
#[link(name = "D3DX9_43", kind = "raw-dylib")]
extern "system" {
pub fn D3DXGetShaderVersion(p_function: *const c_void) -> u32;
pub fn D3DXGetShaderSize(p_function: *const c_void) -> u32;
pub fn D3DXGetShaderConstantTable(
p_function: *const c_void,
ppconstanttable: *mut *mut c_void,
) -> windows::core::HRESULT;
pub(super) fn D3DXCompileShader(
psrcdata: *const c_void,
srcdatasize: usize,
pdefines: *const D3D_SHADER_MACRO,
pinclude: *mut c_void,
pfunctionname: PCSTR,
pprofile: PCSTR,
flags: u32,
ppcode: *mut *mut c_void,
pperrormsgs: *mut *mut c_void,
ppconstantable: *mut *mut c_void,
) -> windows::core::HRESULT;
}
}
#[inline]
pub unsafe fn D3DXCompileShader<P1, P2, P3>(
psrcdata: *const c_void,
srcdatasize: usize,
pdefines: Option<*const D3D_SHADER_MACRO>,
pinclude: P1,
pfunctioname: P2,
pprofile: P3,
flags: u32,
ppcode: *mut Option<ID3DBlob>,
pperrormsgs: Option<*mut Option<ID3DBlob>>,
ppconstanttable: Option<*mut Option<ID3DXConstantTable>>,
) -> ::windows::core::Result<()>
where
P1: IntoParam<ID3DInclude>,
P2: IntoParam<PCSTR>,
P3: IntoParam<PCSTR>,
{
raw::D3DXCompileShader(
psrcdata,
srcdatasize,
::core::mem::transmute(pdefines.unwrap_or(::std::ptr::null())),
pinclude.into_param().abi(),
pfunctioname.into_param().abi(),
pprofile.into_param().abi(),
flags,
::core::mem::transmute(ppcode),
::core::mem::transmute(pperrormsgs.unwrap_or(::std::ptr::null_mut())),
::core::mem::transmute(ppconstanttable.unwrap_or(::std::ptr::null_mut())),
)
.ok()
}
#[repr(transparent)]
#[derive(PartialEq, Eq, Debug, Clone)]
pub struct ID3DXConstantTable(windows::core::IUnknown);
impl ID3DXConstantTable {
#[allow(non_snake_case)]
pub unsafe fn GetShaderConstantTable(
p_function: *const c_void,
) -> windows::core::Result<ID3DXConstantTable> {
let mut result__ = ::std::mem::zeroed();
raw::D3DXGetShaderConstantTable(p_function, &mut result__).from_abi(result__)
}
#[allow(non_snake_case)]
pub unsafe fn GetBufferPointer(&self) -> *mut c_void {
(windows::core::Interface::vtable(self).GetBufferPointer)(windows::core::Interface::as_raw(
self,
))
}
#[allow(non_snake_case)]
pub unsafe fn GetBufferSize(&self) -> usize {
(windows::core::Interface::vtable(self).GetBufferSize)(windows::core::Interface::as_raw(
self,
))
}
#[allow(non_snake_case)]
pub unsafe fn GetDesc(&self) -> windows::core::Result<D3DXCONSTANTTABLE_DESC> {
let mut result__ = ::std::mem::MaybeUninit::<D3DXCONSTANTTABLE_DESC>::zeroed();
(windows::core::Interface::vtable(self).GetDesc)(
windows::core::Interface::as_raw(self),
result__.as_mut_ptr(),
)
.ok()?;
Ok(result__.assume_init())
}
#[allow(non_snake_case)]
pub unsafe fn GetConstant(
&self,
hconstant: Option<D3DXHANDLE>,
index: u32,
) -> windows::core::Result<D3DXHANDLE> {
let handle = (windows::core::Interface::vtable(self).GetConstant)(
windows::core::Interface::as_raw(self),
hconstant.unwrap_or(D3DXHANDLE(std::ptr::null())),
index,
);
if handle.0 as u32 == D3DERR_INVALIDCALL {
return Err(HRESULT(D3DERR_INVALIDCALL as i32).into());
}
Ok(handle)
}
#[allow(non_snake_case)]
pub unsafe fn GetConstantDesc(
&self,
handle: D3DXHANDLE,
pdesc: *mut D3DXCONSTANT_DESC,
pcount: *mut u32,
) -> windows::core::Result<()> {
(windows::core::Interface::vtable(self).GetConstantDesc)(
windows::core::Interface::as_raw(self),
handle,
pdesc,
pcount,
)
.ok()
}
#[allow(non_snake_case)]
pub unsafe fn GetConstantByName<P>(
&self,
hconstant: Option<D3DXHANDLE>,
pname: P,
) -> windows::core::Result<D3DXHANDLE>
where
P: IntoParam<PCSTR>,
{
let handle = (windows::core::Interface::vtable(self).GetConstantByName)(
windows::core::Interface::as_raw(self),
hconstant.unwrap_or(D3DXHANDLE(std::ptr::null())),
pname.into_param().abi(),
);
if handle.0 as u32 == D3DERR_INVALIDCALL {
return Err(HRESULT(D3DERR_INVALIDCALL as i32).into());
}
Ok(handle)
}
#[allow(non_snake_case)]
pub unsafe fn GetConstantElement(
&self,
hconstant: D3DXHANDLE,
index: u32,
) -> windows::core::Result<D3DXHANDLE> {
let handle = (windows::core::Interface::vtable(self).GetConstantElement)(
windows::core::Interface::as_raw(self),
hconstant,
index,
);
if handle.0 as u32 == D3DERR_INVALIDCALL {
return Err(HRESULT(D3DERR_INVALIDCALL as i32).into());
}
Ok(handle)
}
#[allow(non_snake_case)]
pub unsafe fn GetSamplerIndex(&self, hconstant: Option<D3DXHANDLE>) -> u32 {
(windows::core::Interface::vtable(self).GetSamplerIndex)(
windows::core::Interface::as_raw(self),
hconstant.unwrap_or(D3DXHANDLE(std::ptr::null())),
)
}
}
impl windows::core::CanInto<windows::core::IUnknown> for ID3DXConstantTable {}
unsafe impl windows::core::Interface for ID3DXConstantTable {
type Vtable = ID3DXConstantTable_Vtbl;
}
#[repr(C)]
#[doc(hidden)]
#[allow(non_camel_case_types)]
#[allow(non_snake_case)]
pub struct ID3DXConstantTable_Vtbl {
pub base__: windows::core::IUnknown_Vtbl,
#[allow(non_snake_case)]
pub GetBufferPointer: unsafe extern "system" fn(this: *mut c_void) -> *mut c_void,
#[allow(non_snake_case)]
pub GetBufferSize: unsafe extern "system" fn(this: *mut c_void) -> usize,
#[allow(non_snake_case)]
pub GetDesc: unsafe extern "system" fn(
this: *mut c_void,
pdesc: *mut D3DXCONSTANTTABLE_DESC,
) -> windows::core::HRESULT,
#[allow(non_snake_case)]
pub GetConstantDesc: unsafe extern "system" fn(
this: *mut c_void,
hconstant: D3DXHANDLE,
pdesc: *mut D3DXCONSTANT_DESC,
pcount: *mut u32,
) -> windows::core::HRESULT,
#[allow(non_snake_case)]
pub GetSamplerIndex: unsafe extern "system" fn(this: *mut c_void, hconstant: D3DXHANDLE) -> u32,
#[allow(non_snake_case)]
pub GetConstant: unsafe extern "system" fn(
this: *mut c_void,
hconstant: D3DXHANDLE,
index: u32,
) -> D3DXHANDLE,
#[allow(non_snake_case)]
pub GetConstantByName: unsafe extern "system" fn(
this: *mut c_void,
hconstant: D3DXHANDLE,
pname: PCSTR,
) -> D3DXHANDLE,
#[allow(non_snake_case)]
pub GetConstantElement: unsafe extern "system" fn(
this: *mut c_void,
hconstant: D3DXHANDLE,
index: u32,
) -> D3DXHANDLE,
#[allow(non_snake_case)]
pub SetDefaults: unsafe extern "system" fn(
this: *mut c_void,
pdevice: *mut c_void,
) -> windows::core::HRESULT,
#[allow(non_snake_case)]
pub SetValue: unsafe extern "system" fn(
this: *mut c_void,
pdevice: *mut c_void,
hconstant: D3DXHANDLE,
pdata: *mut c_void,
bytes: u32,
) -> HRESULT,
#[allow(non_snake_case)]
pub SetBool:
unsafe extern "system" fn(this: *mut c_void, pdevice: *mut c_void, b: BOOL) -> HRESULT,
#[allow(non_snake_case)]
pub SetBoolArray: unsafe extern "system" fn(
this: *mut c_void,
pdevice: *mut c_void,
b: *const BOOL,
count: u32,
) -> HRESULT,
#[allow(non_snake_case)]
pub SetInt:
unsafe extern "system" fn(this: *mut c_void, pdevice: *mut c_void, n: i32) -> HRESULT,
#[allow(non_snake_case)]
pub SetIntArray: unsafe extern "system" fn(
this: *mut c_void,
pdevice: *mut c_void,
n: *const i32,
count: u32,
) -> HRESULT,
#[allow(non_snake_case)]
pub SetFloat: unsafe extern "system" fn(
this: *mut c_void,
pdevice: *mut c_void,
f: f32,
) -> windows::core::HRESULT,
#[allow(non_snake_case)]
pub SetFloatArray: unsafe extern "system" fn(
this: *mut c_void,
pdevice: *mut c_void,
b: *const f32,
count: u32,
) -> windows::core::HRESULT,
#[allow(non_snake_case)]
pub SetVector: unsafe extern "system" fn(
this: *mut c_void,
pdevice: *mut c_void,
pvector: *const ::windows::Foundation::Numerics::Vector4,
) -> windows::core::HRESULT,
#[allow(non_snake_case)]
pub SetVectorArray: unsafe extern "system" fn(
this: *mut c_void,
pdevice: *mut c_void,
pvector: *const ::windows::Foundation::Numerics::Vector4,
count: u32,
) -> HRESULT,
#[allow(non_snake_case)]
pub SetMatrix: unsafe extern "system" fn(
this: *mut c_void,
pdevice: *mut c_void,
pmatrix: *const ::windows::Foundation::Numerics::Matrix4x4,
) -> HRESULT,
#[allow(non_snake_case)]
pub SetMatrixArray: unsafe extern "system" fn(
this: *mut c_void,
pdevice: *mut c_void,
pmatrix: *const ::windows::Foundation::Numerics::Matrix4x4,
count: u32,
) -> HRESULT,
#[allow(non_snake_case)]
pub SetMatrixPointerArray: unsafe extern "system" fn(
this: *mut c_void,
pdevice: *mut c_void,
ppmatrix: *const *const ::windows::Foundation::Numerics::Matrix4x4,
count: u32,
) -> HRESULT,
#[allow(non_snake_case)]
pub SetMatrixTranspose: unsafe extern "system" fn(
this: *mut c_void,
pdevice: *mut c_void,
pmatrix: *const ::windows::Foundation::Numerics::Matrix4x4,
) -> HRESULT,
#[allow(non_snake_case)]
pub SetMatrixTransposeArray: unsafe extern "system" fn(
this: *mut c_void,
pdevice: *mut c_void,
pmatrix: *const ::windows::Foundation::Numerics::Matrix4x4,
count: u32,
) -> HRESULT,
#[allow(non_snake_case)]
pub SetMatrixTransposePointerArray: unsafe extern "system" fn(
this: *mut c_void,
pdevice: *mut c_void,
ppmatrix: *const *const ::windows::Foundation::Numerics::Matrix4x4,
count: u32,
) -> HRESULT,
}
#[repr(C)]
#[derive(Clone, Debug)]
#[allow(non_camel_case_types)]
#[allow(non_snake_case)]
pub struct D3DXCONSTANTTABLE_DESC {
pub Creator: PCSTR,
pub Version: u32,
pub Constants: u32,
}
#[repr(C)]
#[allow(non_camel_case_types)]
#[allow(non_snake_case)]
#[derive(Debug)]
pub struct D3DXCONSTANT_DESC {
pub Name: PCSTR,
pub RegisterSet: D3DXREGISTER_SET,
pub RegisterIndex: u32,
pub RegisterCount: u32,
pub Class: D3DXPARAMETER_CLASS,
pub Type: D3DXPARAMETER_TYPE,
pub Rows: u32,
pub Columns: u32,
pub Elements: u32,
pub StructMembers: u32,
pub Bytes: u32,
pub DefaultValue: *const c_void,
}
#[repr(u32)]
#[derive(PartialEq, Eq, Debug)]
#[allow(non_camel_case_types)]
#[allow(non_snake_case)]
pub enum D3DXREGISTER_SET {
D3DXRS_BOOL = 0,
D3DXRS_INT4 = 1,
D3DXRS_FLOAT4 = 2,
D3DXRS_SAMPLER = 3,
D3DXRS_FORCE_DWORD = 0x7fffffff,
}
#[repr(u32)]
#[derive(PartialEq, Eq, Debug)]
#[allow(non_camel_case_types)]
#[allow(non_snake_case)]
pub enum D3DXPARAMETER_CLASS {
D3DXPC_SCALAR = 0,
D3DXPC_VECTOR = 1,
D3DXPC_MATRIX_ROWS = 2,
D3DXPC_MATRIX_COLUMNS = 3,
D3DXPC_OBJECT = 4,
D3DXPC_STRUCT = 5,
D3DXPC_FORCE_DWORD = 0x7fffffff,
}
#[repr(u32)]
#[derive(Debug)]
#[allow(non_camel_case_types)]
#[allow(non_snake_case)]
pub enum D3DXPARAMETER_TYPE {
D3DXPT_VOID = 0,
D3DXPT_BOOL = 1,
D3DXPT_INT = 2,
D3DXPT_FLOAT = 3,
D3DXPT_STRING = 4,
D3DXPT_TEXTURE = 5,
D3DXPT_TEXTURE1D = 6,
D3DXPT_TEXTURE2D = 7,
D3DXPT_TEXTURE3D = 8,
D3DXPT_TEXTURECUBE = 9,
D3DXPT_SAMPLER = 10,
D3DXPT_SAMPLER1D = 11,
D3DXPT_SAMPLER2D = 12,
D3DXPT_SAMPLER3D = 13,
D3DXPT_SAMPLERCUBE = 14,
D3DXPT_PIXELSHADER = 15,
D3DXPT_VERTEXSHADER = 16,
D3DXPT_PIXELFRAGMENT = 17,
D3DXPT_VERTEXFRAGMENT = 18,
D3DXPT_UNSUPPORTED = 19,
D3DXPT_FORCE_DWORD = 0x7fffffff,
}
#[repr(transparent)]
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub struct D3DXHANDLE(pub *const c_void);
unsafe impl windows::core::ComInterface for ID3DXConstantTable {
const IID: windows::core::GUID =
windows::core::GUID::from_u128(0xab3c758f_93e_4356_b7_62_4d_b1_8f_1b_3a1);
}

View file

@ -0,0 +1,154 @@
use crate::error;
use crate::error::{assume_d3d_init, Result};
use array_concat::concat_arrays;
use bytemuck::offset_of;
use librashader_runtime::quad::{QuadType, VertexInput};
use windows::Win32::Foundation::FALSE;
use windows::Win32::Graphics::Direct3D9::{
IDirect3DDevice9, IDirect3DVertexBuffer9, IDirect3DVertexDeclaration9, D3DCMP_ALWAYS,
D3DCULL_NONE, D3DDECLMETHOD_DEFAULT, D3DDECLTYPE_FLOAT2, D3DDECLTYPE_FLOAT3,
D3DDECLTYPE_UNUSED, D3DDECLUSAGE_TEXCOORD, D3DPOOL_DEFAULT, D3DPT_TRIANGLESTRIP,
D3DRS_CLIPPING, D3DRS_CULLMODE, D3DRS_LIGHTING, D3DRS_ZENABLE, D3DRS_ZFUNC,
D3DTRANSFORMSTATETYPE, D3DTS_PROJECTION, D3DTS_VIEW, D3DVERTEXELEMENT9,
};
const OFFSCREEN_VBO_DATA: [VertexInput; 4] = [
VertexInput {
position: [-1.0, -1.0, 0.0, 1.0],
texcoord: [0.0, 1.0],
},
VertexInput {
position: [1.0, -1.0, 0.0, 1.0],
texcoord: [1.0, 1.0],
},
VertexInput {
position: [-1.0, 1.0, 0.0, 1.0],
texcoord: [0.0, 0.0],
},
VertexInput {
position: [1.0, 1.0, 0.0, 1.0],
texcoord: [1.0, 0.0],
},
];
const FINAL_VBO_DATA: [VertexInput; 4] = [
VertexInput {
position: [0.0, 0.0, 0.0, 1.0],
texcoord: [0.0, 1.0],
},
VertexInput {
position: [1.0, 0.0, 0.0, 1.0],
texcoord: [1.0, 1.0],
},
VertexInput {
position: [0.0, 1.0, 0.0, 1.0],
texcoord: [0.0, 0.0],
},
VertexInput {
position: [1.0, 1.0, 0.0, 1.0],
texcoord: [1.0, 0.0],
},
];
static VBO_DATA: &[VertexInput; 8] = &concat_arrays!(OFFSCREEN_VBO_DATA, FINAL_VBO_DATA);
pub(crate) struct DrawQuad {
vbo: IDirect3DVertexBuffer9,
vao: IDirect3DVertexDeclaration9,
}
impl DrawQuad {
pub fn new(device: &IDirect3DDevice9) -> error::Result<DrawQuad> {
unsafe {
let mut vbo = None;
device.CreateVertexBuffer(
2 * std::mem::size_of::<[VertexInput; 4]>() as u32,
0,
0,
D3DPOOL_DEFAULT,
&mut vbo,
std::ptr::null_mut(),
)?;
assume_d3d_init!(vbo, "CreateVertexBuffer");
let mut ptr = std::ptr::null_mut();
vbo.Lock(
0,
2 * std::mem::size_of::<[VertexInput; 4]>() as u32,
&mut ptr,
0,
)?;
std::ptr::copy_nonoverlapping(VBO_DATA.as_ptr(), ptr.cast::<VertexInput>(), 8);
vbo.Unlock()?;
let vao = device.CreateVertexDeclaration(Self::get_spirv_cross_vbo_desc().as_ptr())?;
Ok(DrawQuad { vbo, vao })
}
}
pub fn draw_quad(
&self,
device: &IDirect3DDevice9,
vbo_type: QuadType,
mvp: &[f32; 16],
) -> Result<()> {
let offset = match vbo_type {
QuadType::Offscreen => 0,
QuadType::Final => 4,
};
unsafe {
device.SetTransform(D3DTS_PROJECTION, mvp.as_ptr().cast())?;
device.SetTransform(D3DTS_VIEW, mvp.as_ptr().cast())?;
device.SetTransform(D3DTRANSFORMSTATETYPE(256), mvp.as_ptr().cast())?;
device.SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE.0 as u32)?;
device.SetRenderState(D3DRS_CLIPPING, FALSE.0 as u32)?;
device.SetRenderState(D3DRS_ZFUNC, D3DCMP_ALWAYS.0 as u32)?;
device.SetRenderState(D3DRS_ZENABLE, FALSE.0 as u32)?;
device.SetRenderState(D3DRS_LIGHTING, FALSE.0 as u32)?;
device.BeginScene()?;
device.SetStreamSource(0, &self.vbo, 0, std::mem::size_of::<VertexInput>() as u32)?;
// device.SetFVF(D3DFVF_XYZRHW | D3DFVF_TEX1)?;
device.SetVertexDeclaration(&self.vao)?;
device.DrawPrimitive(D3DPT_TRIANGLESTRIP, offset, 2)?;
device.EndScene()?;
}
Ok(())
}
pub fn get_spirv_cross_vbo_desc() -> [D3DVERTEXELEMENT9; 3] {
[
D3DVERTEXELEMENT9 {
Stream: 0,
Offset: offset_of!(VertexInput, position) as u16,
Type: D3DDECLTYPE_FLOAT3.0 as u8,
Method: D3DDECLMETHOD_DEFAULT.0 as u8,
Usage: D3DDECLUSAGE_TEXCOORD.0 as u8,
UsageIndex: 0,
},
D3DVERTEXELEMENT9 {
Stream: 0,
Offset: offset_of!(VertexInput, texcoord) as u16,
Type: D3DDECLTYPE_FLOAT2.0 as u8,
Method: D3DDECLMETHOD_DEFAULT.0 as u8,
Usage: D3DDECLUSAGE_TEXCOORD.0 as u8,
UsageIndex: 1,
},
D3DVERTEXELEMENT9 {
Stream: 0xFF,
Offset: 0,
Type: D3DDECLTYPE_UNUSED.0 as u8,
Method: 0,
Usage: 0,
UsageIndex: 0,
},
]
}
}

View file

@ -0,0 +1,53 @@
//! Direct3D 11 shader runtime errors.
//!
use librashader_preprocess::PreprocessError;
use librashader_presets::ParsePresetError;
use librashader_reflect::error::{ShaderCompileError, ShaderReflectError};
use librashader_runtime::image::ImageError;
use std::backtrace::Backtrace;
use std::string::FromUtf8Error;
use thiserror::Error;
/// Cumulative error type for Direct3D11 filter chains.
#[derive(Error, Debug)]
pub enum FilterChainError {
#[error("invariant assumption about d3d11 did not hold. report this as an issue.")]
Direct3DOperationError(&'static str),
#[error("direct3d driver error")]
Direct3DError {
#[from]
error: windows::core::Error,
backtrace: Backtrace,
},
#[error("shader preset parse error")]
ShaderPresetError(#[from] ParsePresetError),
#[error("shader preprocess error")]
ShaderPreprocessError(#[from] PreprocessError),
#[error("shader compile error")]
ShaderCompileError(#[from] ShaderCompileError),
#[error("shader reflect error")]
ShaderReflectError(#[from] ShaderReflectError),
#[error("lut loading error")]
LutLoadError(#[from] ImageError),
#[error("invalid hlsl uniform name")]
UniformNameError(#[from] FromUtf8Error),
}
macro_rules! assume_d3d_init {
($value:ident, $call:literal) => {
let $value = $value.ok_or($crate::error::FilterChainError::Direct3DOperationError(
$call,
))?;
};
(mut $value:ident, $call:literal) => {
let mut $value = $value.ok_or($crate::error::FilterChainError::Direct3DOperationError(
$call,
))?;
};
}
/// Macro for unwrapping result of a D3D function.
pub(crate) use assume_d3d_init;
/// Result type for Direct3D 11 filter chains.
pub type Result<T> = std::result::Result<T, FilterChainError>;

View file

@ -0,0 +1,422 @@
use crate::binding::{update_sampler_bindings, ConstantRegister, RegisterSet};
use crate::draw_quad::DrawQuad;
use crate::error::FilterChainError;
use crate::filter_pass::FilterPass;
use crate::graphics_pipeline::D3D9State;
use crate::luts::LutTexture;
use crate::options::{FilterChainOptionsD3D9, FrameOptionsD3D9};
use crate::samplers::SamplerSet;
use crate::texture::{D3D9InputTexture, D3D9Texture};
use crate::{error, util};
use librashader_cache::{cache_shader_object, CachedCompilation};
use librashader_common::map::FastHashMap;
use librashader_common::{ImageFormat, Size, Viewport};
use librashader_presets::context::VideoDriver;
use librashader_presets::{ShaderPassConfig, ShaderPreset, TextureConfig};
use librashader_reflect::back::hlsl::HlslShaderModel;
use librashader_reflect::back::targets::HLSL;
use librashader_reflect::back::{CompileReflectShader, CompileShader};
use librashader_reflect::front::SpirvCompilation;
use librashader_reflect::reflect::cross::SpirvCross;
use librashader_reflect::reflect::presets::{CompilePresetTarget, ShaderPassArtifact};
use librashader_reflect::reflect::semantics::ShaderSemantics;
use librashader_reflect::reflect::ReflectShader;
use librashader_runtime::binding::{BindingUtil, TextureInput};
use librashader_runtime::framebuffer::FramebufferInit;
use librashader_runtime::image::{Image, ImageError, UVDirection, ARGB8};
use librashader_runtime::quad::QuadType;
use librashader_runtime::render_target::RenderTarget;
use librashader_runtime::scaling::ScaleFramebuffer;
use librashader_runtime::uniforms::UniformStorage;
use std::collections::VecDeque;
use std::path::Path;
use crate::util::GetSize;
use windows::Win32::Graphics::Direct3D9::{IDirect3DDevice9, IDirect3DSurface9, IDirect3DTexture9};
pub struct FilterMutable {
pub(crate) passes_enabled: usize,
pub(crate) parameters: FastHashMap<String, f32>,
}
pub(crate) struct FilterCommon {
pub(crate) d3d9: IDirect3DDevice9,
pub(crate) luts: FastHashMap<usize, LutTexture>,
pub samplers: SamplerSet,
pub output_textures: Box<[Option<D3D9InputTexture>]>,
pub feedback_textures: Box<[Option<D3D9InputTexture>]>,
pub history_textures: Box<[Option<D3D9InputTexture>]>,
pub config: FilterMutable,
pub disable_mipmaps: bool,
pub(crate) draw_quad: DrawQuad,
}
/// A Direct3D 9 filter chain.
pub struct FilterChainD3D9 {
pub(crate) common: FilterCommon,
passes: Vec<FilterPass>,
output_framebuffers: Box<[D3D9Texture]>,
feedback_framebuffers: Box<[D3D9Texture]>,
history_framebuffers: VecDeque<D3D9Texture>,
default_options: FrameOptionsD3D9,
}
type ShaderPassMeta =
ShaderPassArtifact<impl CompileReflectShader<HLSL, SpirvCompilation, SpirvCross> + Send>;
fn compile_passes(
shaders: Vec<ShaderPassConfig>,
textures: &[TextureConfig],
disable_cache: bool,
) -> Result<(Vec<ShaderPassMeta>, ShaderSemantics), FilterChainError> {
let (passes, semantics) = if !disable_cache {
HLSL::compile_preset_passes::<
CachedCompilation<SpirvCompilation>,
SpirvCross,
FilterChainError,
>(shaders, &textures)?
} else {
HLSL::compile_preset_passes::<SpirvCompilation, SpirvCross, FilterChainError>(
shaders, &textures,
)?
};
Ok((passes, semantics))
}
impl FilterChainD3D9 {
fn init_passes(
device: &IDirect3DDevice9,
passes: Vec<ShaderPassMeta>,
semantics: &ShaderSemantics,
disable_cache: bool,
) -> error::Result<Vec<FilterPass>> {
let builder_fn = |(index, (config, source, mut reflect)): (usize, ShaderPassMeta)| {
let mut reflection = reflect.reflect(index, semantics)?;
let hlsl = reflect.compile(Some(HlslShaderModel::V3_0))?;
// eprintln!("===vs===\n{}", hlsl.vertex);
let (vs, vs_blob) = cache_shader_object(
"d3d9_sm3",
&[hlsl.vertex.as_bytes()],
|&[bytes]| util::d3d_compile_shader(bytes, b"main\0", b"vs_3_0\0"),
|blob| unsafe {
Ok((
device.CreateVertexShader(blob.GetBufferPointer().cast())?,
blob,
))
},
disable_cache,
)?;
// eprintln!("===ps===\n{}", hlsl.fragment);
let (ps, ps_blob) = cache_shader_object(
"d3d9_sm3",
&[hlsl.fragment.as_bytes()],
|&[bytes]| util::d3d_compile_shader(bytes, b"main\0", b"ps_3_0\0"),
|blob| unsafe {
Ok((
device.CreatePixelShader(blob.GetBufferPointer().cast())?,
blob,
))
},
disable_cache,
)?;
let uniform_storage = UniformStorage::new(
reflection.ubo.as_ref().map_or(0, |ubo| ubo.size as usize),
reflection
.push_constant
.as_ref()
.map_or(0, |push| push.size as usize),
);
let mut ps_constants = util::d3d_reflect_shader(ps_blob)?;
let vs_constants = util::d3d_reflect_shader(vs_blob)?;
let uniform_bindings = reflection.meta.create_binding_map(|param| {
ConstantRegister::reflect_register_assignment(
param,
&ps_constants,
&vs_constants,
&hlsl.context,
)
});
let gl_halfpixel = vs_constants.get("gl_HalfPixel").map(|o| o.assignment);
ps_constants.retain(|_, v| matches!(v.set, RegisterSet::Sampler));
update_sampler_bindings(&mut reflection.meta, &ps_constants);
// eprintln!("{:?}", ps_constants);
Ok(FilterPass {
reflection,
vertex_shader: vs,
pixel_shader: ps,
uniform_bindings,
uniform_storage,
gl_halfpixel,
source,
config,
})
};
let filters: Vec<error::Result<FilterPass>> =
passes.into_iter().enumerate().map(builder_fn).collect();
let filters: error::Result<Vec<FilterPass>> = filters.into_iter().collect();
let filters = filters?;
Ok(filters)
}
fn load_luts(
device: &IDirect3DDevice9,
textures: &[TextureConfig],
) -> error::Result<FastHashMap<usize, LutTexture>> {
let mut luts = FastHashMap::default();
let images = textures
.iter()
.map(|texture| Image::load(&texture.path, UVDirection::TopLeft))
.collect::<Result<Vec<Image<ARGB8>>, ImageError>>()?;
for (index, (texture, image)) in textures.iter().zip(images).enumerate() {
let texture = LutTexture::new(device, &image, &texture)?;
luts.insert(index, texture);
}
Ok(luts)
}
}
impl FilterChainD3D9 {
/// Load the shader preset at the given path into a filter chain.
pub unsafe fn load_from_path(
path: impl AsRef<Path>,
device: &IDirect3DDevice9,
options: Option<&FilterChainOptionsD3D9>,
) -> error::Result<FilterChainD3D9> {
// load passes from preset
let preset = ShaderPreset::try_parse_with_driver_context(path, VideoDriver::Direct3D11)?;
unsafe { Self::load_from_preset(preset, device, options) }
}
/// Load a filter chain from a pre-parsed `ShaderPreset`.
pub unsafe fn load_from_preset(
preset: ShaderPreset,
device: &IDirect3DDevice9,
options: Option<&FilterChainOptionsD3D9>,
) -> error::Result<FilterChainD3D9> {
let disable_cache = options.map_or(false, |o| o.disable_cache);
let (passes, semantics) = compile_passes(preset.shaders, &preset.textures, disable_cache)?;
let samplers = SamplerSet::new()?;
// initialize passes
let filters = FilterChainD3D9::init_passes(device, passes, &semantics, disable_cache)?;
// load luts
let luts = FilterChainD3D9::load_luts(device, &preset.textures)?;
let framebuffer_gen =
|| D3D9Texture::new(device, Size::new(1, 1), ImageFormat::R8G8B8A8Unorm, false);
let input_gen = || None;
let framebuffer_init = FramebufferInit::new(
filters.iter().map(|f| &f.reflection.meta),
&framebuffer_gen,
&input_gen,
);
// initialize output framebuffers
let (output_framebuffers, output_textures) = framebuffer_init.init_output_framebuffers()?;
// initialize feedback framebuffers
let (feedback_framebuffers, feedback_textures) =
framebuffer_init.init_output_framebuffers()?;
// initialize history
let (history_framebuffers, history_textures) = framebuffer_init.init_history()?;
let draw_quad = DrawQuad::new(device)?;
Ok(FilterChainD3D9 {
passes: filters,
output_framebuffers,
feedback_framebuffers,
history_framebuffers,
common: FilterCommon {
d3d9: device.clone(),
config: FilterMutable {
passes_enabled: preset.shader_count as usize,
parameters: preset
.parameters
.into_iter()
.map(|param| (param.name, param.value))
.collect(),
},
disable_mipmaps: options.map_or(false, |o| o.force_no_mipmaps),
luts,
samplers,
output_textures,
feedback_textures,
history_textures,
draw_quad,
},
default_options: Default::default(),
})
}
fn push_history(&mut self, input: &IDirect3DTexture9) -> error::Result<()> {
if let Some(mut back) = self.history_framebuffers.pop_back() {
back.copy_from(&self.common.d3d9, input)?;
self.history_framebuffers.push_front(back)
}
Ok(())
}
/// Process a frame with the input image.
///
/// ## Safety:
/// * `input` must be in `D3DPOOL_DEFAULT`.
pub unsafe fn frame(
&mut self,
input: IDirect3DTexture9,
viewport: &Viewport<IDirect3DSurface9>,
frame_count: usize,
options: Option<&FrameOptionsD3D9>,
) -> error::Result<()> {
let max = std::cmp::min(self.passes.len(), self.common.config.passes_enabled);
let passes = &mut self.passes[0..max];
if let Some(options) = options {
if options.clear_history {
for framebuffer in &mut self.history_framebuffers {
framebuffer.clear(&self.common.d3d9)?;
}
}
}
if passes.is_empty() {
return Ok(());
}
let options = options.unwrap_or(&self.default_options);
let filter = passes[0].config.filter;
let wrap_mode = passes[0].config.wrap_mode;
for (texture, fbo) in self
.common
.history_textures
.iter_mut()
.zip(self.history_framebuffers.iter())
{
*texture = Some(fbo.as_input(filter, filter, wrap_mode));
}
let original = D3D9InputTexture {
handle: input.clone(),
filter,
wrap: wrap_mode,
mipmode: filter,
is_srgb: false,
};
let mut source = original.clone();
// rescale render buffers to ensure all bindings are valid.
D3D9Texture::scale_framebuffers(
source.size(),
viewport.output.size()?,
original.size(),
&mut self.output_framebuffers,
&mut self.feedback_framebuffers,
passes,
None,
)?;
// Refresh inputs for feedback textures.
// Don't need to do this for outputs because they are yet to be bound.
for ((texture, fbo), pass) in self
.common
.feedback_textures
.iter_mut()
.zip(self.feedback_framebuffers.iter())
.zip(passes.iter())
{
*texture = Some(fbo.as_input(
pass.config.filter,
pass.config.filter,
pass.config.wrap_mode,
));
}
let passes_len = passes.len();
let (pass, last) = passes.split_at_mut(passes_len - 1);
let state_guard = D3D9State::new(&self.common.d3d9)?;
for (index, pass) in pass.iter_mut().enumerate() {
source.filter = pass.config.filter;
source.wrap = pass.config.wrap_mode;
source.is_srgb = pass.config.srgb_framebuffer;
let target = &self.output_framebuffers[index];
let target_rtv = target.as_output()?;
pass.draw(
&self.common.d3d9,
index,
&self.common,
pass.config.get_frame_count(frame_count),
options,
viewport,
&original,
&source,
RenderTarget::identity(&target_rtv),
QuadType::Offscreen,
)?;
source = D3D9InputTexture {
handle: target.handle.clone(),
filter: pass.config.filter,
wrap: pass.config.wrap_mode,
mipmode: pass.config.filter,
is_srgb: pass.config.srgb_framebuffer,
};
self.common.output_textures[index] = Some(source.clone());
}
// try to hint the optimizer
assert_eq!(last.len(), 1);
if let Some(pass) = last.iter_mut().next() {
source.filter = pass.config.filter;
source.wrap = pass.config.wrap_mode;
source.is_srgb = pass.config.srgb_framebuffer;
pass.draw(
&self.common.d3d9,
passes_len - 1,
&self.common,
pass.config.get_frame_count(frame_count),
options,
viewport,
&original,
&source,
RenderTarget::viewport(viewport),
QuadType::Final,
)?;
}
std::mem::swap(
&mut self.output_framebuffers,
&mut self.feedback_framebuffers,
);
drop(state_guard);
self.push_history(&input)?;
Ok(())
}
}

View file

@ -0,0 +1,229 @@
use crate::binding::{ConstantRegister, D3D9UniformBinder, D3D9UniformStorage, RegisterAssignment};
use crate::error;
use crate::filter_chain::FilterCommon;
use crate::options::FrameOptionsD3D9;
use crate::samplers::SamplerSet;
use crate::texture::D3D9InputTexture;
use librashader_common::map::FastHashMap;
use librashader_common::{ImageFormat, Size, Viewport};
use librashader_preprocess::ShaderSource;
use librashader_presets::ShaderPassConfig;
use librashader_reflect::reflect::semantics::{TextureBinding, UniformBinding};
use librashader_reflect::reflect::ShaderReflection;
use librashader_runtime::binding::{BindSemantics, UniformInputs};
use librashader_runtime::filter_pass::FilterPassMeta;
use librashader_runtime::quad::QuadType;
use librashader_runtime::render_target::RenderTarget;
use windows::Win32::Foundation::{FALSE, TRUE};
use crate::util::GetSize;
use windows::Win32::Graphics::Direct3D9::{
IDirect3DDevice9, IDirect3DPixelShader9, IDirect3DSurface9, IDirect3DVertexShader9,
D3DCLEAR_TARGET, D3DRS_SRGBWRITEENABLE, D3DSAMP_SRGBTEXTURE, D3DVIEWPORT9,
};
pub struct FilterPass {
pub reflection: ShaderReflection,
pub vertex_shader: IDirect3DVertexShader9,
pub pixel_shader: IDirect3DPixelShader9,
pub uniform_bindings: FastHashMap<UniformBinding, ConstantRegister>,
pub source: ShaderSource,
pub config: ShaderPassConfig,
pub uniform_storage: D3D9UniformStorage,
pub gl_halfpixel: Option<RegisterAssignment>,
}
impl FilterPassMeta for FilterPass {
fn framebuffer_format(&self) -> ImageFormat {
self.source.format
}
fn config(&self) -> &ShaderPassConfig {
&self.config
}
}
impl BindSemantics<D3D9UniformBinder, ConstantRegister> for FilterPass {
type InputTexture = D3D9InputTexture;
type SamplerSet = SamplerSet;
type DescriptorSet<'a> = ();
type DeviceContext = IDirect3DDevice9;
type UniformOffset = ConstantRegister;
fn bind_texture<'a>(
_: &mut Self::DescriptorSet<'a>,
samplers: &Self::SamplerSet,
binding: &TextureBinding,
texture: &Self::InputTexture,
device: &Self::DeviceContext,
) {
// eprintln!("binding s{}", binding.binding);
unsafe {
if let Err(e) = device.SetTexture(binding.binding, &texture.handle) {
println!(
"[librashader-runtime-d3d9] failed to texture at {}: {e}",
binding.binding
);
}
let setter = samplers.get(texture.wrap, texture.filter, texture.mipmode);
if let Err(e) = setter(&device, binding.binding) {
println!(
"[librashader-runtime-d3d9] failed to set sampler at {}: {e}",
binding.binding
);
}
if texture.is_srgb {
if let Err(e) = device.SetSamplerState(binding.binding, D3DSAMP_SRGBTEXTURE, 1u32) {
println!(
"[librashader-runtime-d3d9] failed to set srgb at {}: {e}",
binding.binding
);
}
} else {
if let Err(e) = device.SetSamplerState(binding.binding, D3DSAMP_SRGBTEXTURE, 0u32) {
println!(
"[librashader-runtime-d3d9] failed to set srgb at {}: {e}",
binding.binding
);
}
}
}
}
}
impl FilterPass {
// framecount should be pre-modded
fn build_semantics<'a>(
&mut self,
pass_index: usize,
parent: &FilterCommon,
mvp: &[f32; 16],
frame_count: u32,
options: &FrameOptionsD3D9,
fb_size: Size<u32>,
viewport_size: Size<u32>,
original: &D3D9InputTexture,
source: &D3D9InputTexture,
) {
Self::bind_semantics(
&parent.d3d9,
&parent.samplers,
&mut self.uniform_storage,
&mut (),
UniformInputs {
mvp,
frame_count,
rotation: options.rotation,
total_subframes: options.total_subframes,
current_subframe: options.current_subframe,
frame_direction: options.frame_direction,
framebuffer_size: fb_size,
viewport_size,
},
original,
source,
&self.uniform_bindings,
&self.reflection.meta.texture_meta,
parent.output_textures[0..pass_index]
.iter()
.map(|o| o.as_ref()),
parent.feedback_textures.iter().map(|o| o.as_ref()),
parent.history_textures.iter().map(|o| o.as_ref()),
parent.luts.iter().map(|(u, i)| (*u, i.as_ref())),
&self.source.parameters,
&parent.config.parameters,
);
}
pub(crate) fn draw(
&mut self,
device: &IDirect3DDevice9,
pass_index: usize,
parent: &FilterCommon,
frame_count: u32,
options: &FrameOptionsD3D9,
viewport: &Viewport<IDirect3DSurface9>,
original: &D3D9InputTexture,
source: &D3D9InputTexture,
output: RenderTarget<IDirect3DSurface9>,
vbo_type: QuadType,
) -> error::Result<()> {
if self.config.mipmap_input && !parent.disable_mipmaps {
unsafe {
source.handle.GenerateMipSubLevels();
}
}
let output_size = output.output.size()?;
// let viewport_size = viewport.output.size()?;
unsafe {
device.SetVertexShader(&self.vertex_shader)?;
device.SetPixelShader(&self.pixel_shader)?;
}
self.build_semantics(
pass_index,
parent,
output.mvp,
frame_count,
options,
output_size,
viewport.output.size()?,
original,
source,
);
unsafe {
if let Some(gl_halfpixel) = &self.gl_halfpixel {
let data = [
1.0 / output_size.width as f32,
1.0 / output_size.height as f32,
0.0,
0.0,
];
device.SetVertexShaderConstantF(
gl_halfpixel.index,
data.as_ptr(),
gl_halfpixel.count,
)?;
}
device.SetViewport(&D3DVIEWPORT9 {
X: output.x as u32,
Y: output.y as u32,
Width: output_size.width,
Height: output_size.height,
MinZ: 0.0,
MaxZ: 1.0,
})?;
device.SetRenderTarget(0, &*output.output)?;
device.Clear(
0,
std::ptr::null_mut(),
D3DCLEAR_TARGET as u32,
if cfg!(debug_assertions) {
0xFFFF00FF
} else {
0x0
},
0.0,
0,
)?;
}
if self.framebuffer_format() == ImageFormat::R8G8B8A8Srgb {
unsafe {
device.SetRenderState(D3DRS_SRGBWRITEENABLE, TRUE.0 as u32)?;
}
}
parent.draw_quad.draw_quad(device, vbo_type, output.mvp)?;
unsafe {
device.SetRenderState(D3DRS_SRGBWRITEENABLE, FALSE.0 as u32)?;
}
Ok(())
}
}

View file

@ -0,0 +1,27 @@
use crate::error;
use windows::Win32::Graphics::Direct3D9::{IDirect3DDevice9, IDirect3DStateBlock9, D3DSBT_ALL};
pub struct D3D9State {
state: IDirect3DStateBlock9,
}
impl D3D9State {
pub fn new(device: &IDirect3DDevice9) -> error::Result<D3D9State> {
let block = unsafe {
let block = device.CreateStateBlock(D3DSBT_ALL)?;
block.Capture()?;
block
};
Ok(D3D9State { state: block })
}
}
impl Drop for D3D9State {
fn drop(&mut self) {
if let Err(e) = unsafe { self.state.Apply() } {
println!("librashader-runtime-d3d9: [warn] failed to restore state {e:?}")
}
}
}

View file

@ -0,0 +1,20 @@
#![cfg(target_os = "windows")]
#![feature(type_alias_impl_trait)]
#![feature(error_generic_member_access)]
mod binding;
mod draw_quad;
mod filter_chain;
mod filter_pass;
mod graphics_pipeline;
mod luts;
mod samplers;
mod texture;
mod util;
mod d3dx;
pub mod error;
pub mod options;
use librashader_runtime::impl_filter_chain_parameters;
impl_filter_chain_parameters!(FilterChainD3D9);
pub use crate::filter_chain::FilterChainD3D9;

View file

@ -0,0 +1,65 @@
use crate::error;
use crate::error::assume_d3d_init;
use crate::texture::D3D9InputTexture;
use librashader_presets::TextureConfig;
use librashader_runtime::image::{Image, ARGB8};
use windows::Win32::Graphics::Direct3D9::{
IDirect3DDevice9, D3DFMT_A8R8G8B8, D3DLOCKED_RECT, D3DPOOL_MANAGED,
};
#[derive(Debug, Clone)]
pub(crate) struct LutTexture(D3D9InputTexture);
impl AsRef<D3D9InputTexture> for LutTexture {
fn as_ref(&self) -> &D3D9InputTexture {
&self.0
}
}
impl LutTexture {
pub fn new(
device: &IDirect3DDevice9,
source: &Image<ARGB8>,
config: &TextureConfig,
) -> error::Result<LutTexture> {
let mut texture = None;
unsafe {
device.CreateTexture(
source.size.width,
source.size.height,
if config.mipmap { 0 } else { 1 },
0,
D3DFMT_A8R8G8B8,
D3DPOOL_MANAGED,
&mut texture,
std::ptr::null_mut(),
)?;
}
assume_d3d_init!(texture, "CreateTexture");
unsafe {
let mut lock = D3DLOCKED_RECT::default();
texture.LockRect(0, &mut lock, std::ptr::null_mut(), 0)?;
std::ptr::copy_nonoverlapping(
source.bytes.as_ptr(),
lock.pBits.cast(),
source.bytes.len(),
);
texture.UnlockRect(0)?;
if config.mipmap {
texture.GenerateMipSubLevels();
}
}
Ok(LutTexture(D3D9InputTexture {
handle: texture,
filter: config.filter_mode,
wrap: config.wrap_mode,
mipmode: config.filter_mode,
is_srgb: false,
}))
}
}

View file

@ -0,0 +1,16 @@
//! Direct3D 9 shader runtime options.
use librashader_runtime::impl_default_frame_options;
impl_default_frame_options!(FrameOptionsD3D9);
/// Options for Direct3D 11 filter chain creation.
#[repr(C)]
#[derive(Default, Debug, Clone)]
pub struct FilterChainOptionsD3D9 {
/// Whether or not to explicitly disable mipmap
/// generation regardless of shader preset settings.
pub force_no_mipmaps: bool,
/// Disable the shader object cache. Shaders will be
/// recompiled rather than loaded from the cache.
pub disable_cache: bool,
}

View file

@ -0,0 +1,97 @@
use crate::error::Result;
use librashader_common::map::FastHashMap;
use librashader_common::{FilterMode, WrapMode};
use windows::Win32::Graphics::Direct3D9::{
IDirect3DDevice9, D3DSAMP_ADDRESSU, D3DSAMP_ADDRESSV, D3DSAMP_ADDRESSW, D3DSAMP_MAGFILTER,
D3DSAMP_MINFILTER, D3DSAMP_MIPFILTER, D3DTEXTUREADDRESS, D3DTEXTUREFILTER,
};
pub struct SamplerSet {
samplers: FastHashMap<
(WrapMode, FilterMode, FilterMode),
Box<dyn Fn(&IDirect3DDevice9, u32) -> Result<()>>,
>,
}
impl SamplerSet {
#[inline(always)]
pub fn get(
&self,
wrap: WrapMode,
filter: FilterMode,
mip_filter: FilterMode,
) -> &dyn Fn(&IDirect3DDevice9, u32) -> Result<()> {
// SAFETY: the sampler set is complete for the matrix
// wrap x filter x mipfilter
unsafe {
&*self
.samplers
.get(&(wrap, filter, mip_filter))
.unwrap_unchecked()
}
}
pub fn new() -> Result<SamplerSet> {
let mut samplers = FastHashMap::default();
let wrap_modes = &[
WrapMode::ClampToBorder,
WrapMode::ClampToEdge,
WrapMode::Repeat,
WrapMode::MirroredRepeat,
];
for wrap_mode in wrap_modes {
for filter_mode in &[FilterMode::Linear, FilterMode::Nearest] {
for mip_filter in &[FilterMode::Linear, FilterMode::Nearest] {
let sampler: Box<dyn Fn(&IDirect3DDevice9, u32) -> Result<()>> =
Box::new(|device: &IDirect3DDevice9, index| {
unsafe {
let wrap_mode = *wrap_mode;
let filter_mode = *filter_mode;
let mip_filter = *mip_filter;
device.SetSamplerState(
index,
D3DSAMP_ADDRESSU,
D3DTEXTUREADDRESS::from(wrap_mode).0 as u32,
)?;
device.SetSamplerState(
index,
D3DSAMP_ADDRESSV,
D3DTEXTUREADDRESS::from(wrap_mode).0 as u32,
)?;
device.SetSamplerState(
index,
D3DSAMP_ADDRESSW,
D3DTEXTUREADDRESS::from(wrap_mode).0 as u32,
)?;
device.SetSamplerState(
index,
D3DSAMP_MAGFILTER,
D3DTEXTUREFILTER::from(filter_mode).0 as u32,
)?;
device.SetSamplerState(
index,
D3DSAMP_MINFILTER,
D3DTEXTUREFILTER::from(mip_filter).0 as u32,
)?;
device.SetSamplerState(
index,
D3DSAMP_MIPFILTER,
D3DTEXTUREFILTER::from(mip_filter).0 as u32,
)?;
}
Ok(())
});
samplers.insert((*wrap_mode, *filter_mode, *mip_filter), sampler);
}
}
}
assert_eq!(samplers.len(), wrap_modes.len() * 2 * 2);
Ok(SamplerSet { samplers })
}
}

View file

@ -0,0 +1,228 @@
use crate::error;
use crate::error::{assume_d3d_init, FilterChainError};
use crate::util::GetSize;
use librashader_common::{FilterMode, ImageFormat, Size, WrapMode};
use librashader_presets::Scale2D;
use librashader_runtime::binding::TextureInput;
use librashader_runtime::scaling::{ScaleFramebuffer, ViewportSize};
use windows::Win32::Graphics::Direct3D9::{
IDirect3DDevice9, IDirect3DSurface9, IDirect3DTexture9, D3DCLEAR_TARGET, D3DFORMAT,
D3DPOOL_DEFAULT, D3DTEXF_LINEAR, D3DUSAGE_DYNAMIC, D3DUSAGE_RENDERTARGET,
};
/// An image view for use as a shader resource.
///
/// Contains an `ID3D11ShaderResourceView`, and a size.
#[derive(Debug, Clone)]
pub struct D3D9Texture {
/// A handle to the shader resource view.
pub handle: IDirect3DTexture9,
pub mipmap: bool,
pub original_format: ImageFormat,
}
impl TextureInput for D3D9InputTexture {
fn size(&self) -> Size<u32> {
GetSize::size(&self.handle).unwrap_or_else(|_| Size::new(0, 0))
}
}
impl AsRef<D3D9InputTexture> for D3D9InputTexture {
fn as_ref(&self) -> &D3D9InputTexture {
&self
}
}
#[derive(Debug, Clone)]
pub struct D3D9InputTexture {
/// A handle to the shader resource view.
pub handle: IDirect3DTexture9,
pub filter: FilterMode,
pub wrap: WrapMode,
pub mipmode: FilterMode,
pub is_srgb: bool,
}
impl D3D9Texture {
pub fn new(
device: &IDirect3DDevice9,
size: Size<u32>,
format: ImageFormat,
mipmap: bool,
) -> error::Result<Self> {
let mut texture = None;
// eprintln!("creating texture");
unsafe {
device.CreateTexture(
size.width,
size.height,
if mipmap { 0 } else { 1 },
D3DUSAGE_RENDERTARGET as u32,
format.into(),
D3DPOOL_DEFAULT,
&mut texture,
std::ptr::null_mut(),
)?;
}
// eprintln!("creating texture ok");
assume_d3d_init!(texture, "CreateTexture");
Ok(Self {
handle: texture,
mipmap,
original_format: format,
})
}
pub fn init(&mut self, size: Size<u32>, format: ImageFormat) -> error::Result<()> {
// let format = format.into();
unsafe {
let mut texture = None;
self.handle.GetDevice()?.CreateTexture(
size.width,
size.height,
if self.mipmap { 0 } else { 1 },
D3DUSAGE_RENDERTARGET as u32,
format.into(),
D3DPOOL_DEFAULT,
&mut texture,
std::ptr::null_mut(),
)?;
assume_d3d_init!(mut texture, "CreateTexture2D");
std::mem::swap(&mut self.handle, &mut texture);
drop(texture)
}
Ok(())
}
pub fn size(&self) -> error::Result<Size<u32>> {
let mut desc = Default::default();
unsafe {
self.handle.GetLevelDesc(0, &mut desc)?;
}
Ok(Size {
height: desc.Height,
width: desc.Width,
})
}
pub fn as_output(&self) -> error::Result<IDirect3DSurface9> {
unsafe { Ok(self.handle.GetSurfaceLevel(0)?) }
}
pub(crate) fn scale(
&mut self,
scaling: Scale2D,
format: ImageFormat,
viewport_size: &Size<u32>,
source_size: &Size<u32>,
original_size: &Size<u32>,
should_mipmap: bool,
) -> error::Result<Size<u32>> {
let size = source_size.scale_viewport(scaling, *viewport_size, *original_size);
if self.size()? != size || should_mipmap != self.mipmap {
self.mipmap = should_mipmap;
self.init(
size,
if format == ImageFormat::Unknown {
ImageFormat::R8G8B8A8Unorm
} else {
format
},
)?;
}
Ok(size)
}
pub fn clear(&mut self, device: &IDirect3DDevice9) -> error::Result<()> {
unsafe {
let surface = self.handle.GetSurfaceLevel(0)?;
device.SetRenderTarget(0, &surface)?;
device.Clear(0, std::ptr::null_mut(), D3DCLEAR_TARGET as u32, 0x0, 0.0, 0)?;
}
Ok(())
}
pub fn copy_from(
&mut self,
device: &IDirect3DDevice9,
input: &IDirect3DTexture9,
) -> error::Result<()> {
let mut desc = Default::default();
unsafe {
input.GetLevelDesc(0, &mut desc)?;
}
let size = Size {
width: desc.Width,
height: desc.Height,
};
if self.size()? != size || D3DFORMAT::from(self.original_format) != desc.Format {
eprintln!("[history] resizing");
self.init(size, ImageFormat::from(desc.Format))?;
}
unsafe {
let dest = self.handle.GetSurfaceLevel(0)?;
let source = input.GetSurfaceLevel(0)?;
device.StretchRect(
&source,
std::ptr::null(),
&dest,
std::ptr::null(),
D3DTEXF_LINEAR,
)?;
}
Ok(())
}
pub fn as_input(
&self,
filter: FilterMode,
mipmode: FilterMode,
wrap: WrapMode,
) -> D3D9InputTexture {
D3D9InputTexture {
handle: self.handle.clone(),
filter,
wrap,
mipmode,
is_srgb: self.original_format == ImageFormat::R8G8B8A8Srgb,
}
}
}
impl ScaleFramebuffer for D3D9Texture {
type Error = FilterChainError;
type Context = ();
fn scale(
&mut self,
scaling: Scale2D,
format: ImageFormat,
viewport_size: &Size<u32>,
source_size: &Size<u32>,
original_size: &Size<u32>,
should_mipmap: bool,
_context: &Self::Context,
) -> Result<Size<u32>, Self::Error> {
self.scale(
scaling,
format,
viewport_size,
source_size,
original_size,
should_mipmap,
)
}
}

View file

@ -0,0 +1,245 @@
use crate::error;
use crate::error::assume_d3d_init;
use std::mem::MaybeUninit;
use crate::binding::{ConstantDescriptor, RegisterAssignment, RegisterSet};
use crate::d3dx::{ID3DXConstantTable, D3DXCONSTANT_DESC, D3DXREGISTER_SET};
use librashader_common::map::FastHashMap;
use librashader_common::Size;
use windows::core::PCSTR;
use windows::Win32::Graphics::Direct3D::Fxc::{D3DCompile, D3DCOMPILE_AVOID_FLOW_CONTROL};
use windows::Win32::Graphics::Direct3D::ID3DBlob;
use windows::Win32::Graphics::Direct3D9::{IDirect3DSurface9, IDirect3DTexture9};
// const fn d3d9_format_fallback_list(format: D3DFORMAT) -> Option<&'static [D3DFORMAT]> {
// match format {
// DXGI_FORMAT_R32G32B32A32_FLOAT => Some(&[
// windows::Win32::Graphics::Dxgi::Common::DXGI_FORMAT_R32G32B32A32_FLOAT,
// DXGI_FORMAT_R16G16B16A16_FLOAT,
// DXGI_FORMAT_R32G32B32_FLOAT,
// DXGI_FORMAT_R11G11B10_FLOAT,
// DXGI_FORMAT_UNKNOWN,
// ]),
// DXGI_FORMAT_R16G16B16A16_FLOAT => Some(&[
// windows::Win32::Graphics::Dxgi::Common::DXGI_FORMAT_R16G16B16A16_FLOAT,
// DXGI_FORMAT_R32G32B32A32_FLOAT,
// DXGI_FORMAT_R32G32B32_FLOAT,
// DXGI_FORMAT_R11G11B10_FLOAT,
// DXGI_FORMAT_UNKNOWN,
// ]),
// DXGI_FORMAT_R8G8B8A8_UNORM => Some(&[
// windows::Win32::Graphics::Dxgi::Common::DXGI_FORMAT_R8G8B8A8_UNORM,
// DXGI_FORMAT_B8G8R8A8_UNORM,
// DXGI_FORMAT_B8G8R8X8_UNORM,
// DXGI_FORMAT_UNKNOWN,
// ]),
// DXGI_FORMAT_R8G8B8A8_UNORM_SRGB => Some(&[
// windows::Win32::Graphics::Dxgi::Common::DXGI_FORMAT_R8G8B8A8_UNORM_SRGB,
// DXGI_FORMAT_R8G8B8A8_UNORM,
// DXGI_FORMAT_B8G8R8A8_UNORM,
// DXGI_FORMAT_B8G8R8X8_UNORM,
// DXGI_FORMAT_UNKNOWN,
// ]),
// DXGI_FORMAT_B8G8R8A8_UNORM => Some(&[
// windows::Win32::Graphics::Dxgi::Common::DXGI_FORMAT_B8G8R8A8_UNORM,
// DXGI_FORMAT_R8G8B8A8_UNORM,
// DXGI_FORMAT_UNKNOWN,
// ]),
// DXGI_FORMAT_B8G8R8X8_UNORM => Some(&[
// windows::Win32::Graphics::Dxgi::Common::DXGI_FORMAT_B8G8R8X8_UNORM,
// DXGI_FORMAT_B8G8R8A8_UNORM,
// DXGI_FORMAT_R8G8B8A8_UNORM,
// DXGI_FORMAT_UNKNOWN,
// ]),
// DXGI_FORMAT_B5G6R5_UNORM => Some(&[
// windows::Win32::Graphics::Dxgi::Common::DXGI_FORMAT_B5G6R5_UNORM,
// DXGI_FORMAT_B8G8R8X8_UNORM,
// DXGI_FORMAT_B8G8R8A8_UNORM,
// DXGI_FORMAT_R8G8B8A8_UNORM,
// DXGI_FORMAT_UNKNOWN,
// ]),
// DXGI_FORMAT_EX_A4R4G4B4_UNORM | DXGI_FORMAT_B4G4R4A4_UNORM => Some(&[
// windows::Win32::Graphics::Dxgi::Common::DXGI_FORMAT_B4G4R4A4_UNORM,
// DXGI_FORMAT_B8G8R8A8_UNORM,
// DXGI_FORMAT_R8G8B8A8_UNORM,
// DXGI_FORMAT_UNKNOWN,
// ]),
// // DXGI_FORMAT_A8_UNORM => Some(&[
// // windows::Win32::Graphics::Dxgi::Common::DXGI_FORMAT_A8_UNORM,
// // DXGI_FORMAT_R8_UNORM,
// // DXGI_FORMAT_R8G8_UNORM,
// // DXGI_FORMAT_R8G8B8A8_UNORM,
// // DXGI_FORMAT_B8G8R8A8_UNORM,
// // DXGI_FORMAT_UNKNOWN,
// // ]),
// // DXGI_FORMAT_R8_UNORM => Some(&[
// // windows::Win32::Graphics::Dxgi::Common::DXGI_FORMAT_R8_UNORM,
// // DXGI_FORMAT_A8_UNORM,
// // DXGI_FORMAT_R8G8_UNORM,
// // DXGI_FORMAT_R8G8B8A8_UNORM,
// // DXGI_FORMAT_B8G8R8A8_UNORM,
// // DXGI_FORMAT_UNKNOWN,
// // ]),
// // _ => None,
// // }
// // }
//
// pub fn d3d9_get_closest_format(
// device: &IDirect3DDevice9,
// format: D3DFORMAT,
// restype: D3DRESOURCETYPE,
// format_support_mask: i32,
// ) -> error::Result<D3DFORMAT> {
// let d3d9 = unsafe { device.GetDirect3D()? };
// let (devtype, ordinal) = unsafe {
// let mut params = Default::default();
// device.GetCreationParameters(&mut params)?;
// (params.DeviceType, params.AdapterOrdinal)
// };
//
// let default_list = [format, D3DFMT_UNKNOWN];
// let format_support_list = d3d9_format_fallback_list(format).unwrap_or(&default_list);
// let format_support_mask = format_support_mask as u32;
//
// for supported in format_support_list {
// unsafe {
// if let Ok(_) = d3d9.CheckDeviceFormat(
// ordinal,devtype,
// format, format_support_mask,
// restype,
// format)
// {
// return Ok(*supported);
// }
// }
// }
//
// Ok(D3DFMT_UNKNOWN)
// }
pub fn d3d_compile_shader(source: &[u8], entry: &[u8], version: &[u8]) -> error::Result<ID3DBlob> {
unsafe {
let mut blob = None;
let mut errs = None;
let res = D3DCompile(
source.as_ptr().cast(),
source.len(),
None,
None,
None,
PCSTR(entry.as_ptr()),
PCSTR(version.as_ptr()),
D3DCOMPILE_AVOID_FLOW_CONTROL,
0,
&mut blob,
Some(&mut errs),
);
// let res = D3DXCompileShader(
// source.as_ptr().cast(),
// source.len(),
// None,
// None,
// PCSTR(entry.as_ptr()),
// PCSTR(version.as_ptr()),
// 0,
// &mut blob,
// Some(&mut errs),
// None,
// );
// if let Some(errs) = errs {
// let str = std::slice::from_raw_parts(
// errs.GetBufferPointer().cast::<u8>(),
// errs.GetBufferSize(),
// );
// let str = std::ffi::CStr::from_bytes_until_nul(str).unwrap();
// // eprintln!("{}", str.to_str().unwrap());
// }
res?;
assume_d3d_init!(blob, "D3DCompile");
Ok(blob)
}
}
pub fn d3d_reflect_shader(
shader: ID3DBlob,
) -> error::Result<FastHashMap<String, ConstantDescriptor>> {
unsafe {
let table = ID3DXConstantTable::GetShaderConstantTable(shader.GetBufferPointer())?;
let desc = table.GetDesc()?;
let mut scratch = Vec::with_capacity(16);
scratch.resize_with(16, MaybeUninit::zeroed);
let mut assignments = FastHashMap::default();
for assignment in 0..desc.Constants {
let constant = table.GetConstant(None, assignment)?;
let mut written = scratch.len() as u32;
table.GetConstantDesc(constant, scratch.as_mut_ptr().cast(), &mut written)?;
for i in 0..written as usize {
let desc: MaybeUninit<D3DXCONSTANT_DESC> =
std::mem::replace(&mut scratch[i], MaybeUninit::zeroed());
let desc = desc.assume_init();
// Only cN and sN allowed.
if desc.RegisterSet != D3DXREGISTER_SET::D3DXRS_FLOAT4
&& desc.RegisterSet != D3DXREGISTER_SET::D3DXRS_SAMPLER
{
continue;
}
let name = desc.Name.to_string()?;
assignments.insert(
name,
ConstantDescriptor {
assignment: RegisterAssignment {
index: desc.RegisterIndex,
count: desc.RegisterCount,
},
set: if desc.RegisterSet == D3DXREGISTER_SET::D3DXRS_SAMPLER {
RegisterSet::Sampler
} else {
RegisterSet::Float
},
},
);
}
}
Ok(assignments)
}
}
pub(crate) trait GetSize {
fn size(&self) -> error::Result<Size<u32>>;
}
impl GetSize for IDirect3DSurface9 {
fn size(&self) -> error::Result<Size<u32>> {
let mut desc = Default::default();
unsafe {
self.GetDesc(&mut desc)?;
}
Ok(Size {
height: desc.Height,
width: desc.Width,
})
}
}
impl GetSize for IDirect3DTexture9 {
fn size(&self) -> error::Result<Size<u32>> {
let mut desc = Default::default();
unsafe {
self.GetLevelDesc(0, &mut desc)?;
}
Ok(Size {
height: desc.Height,
width: desc.Width,
})
}
}

View file

@ -0,0 +1,578 @@
const WIDTH: i32 = 800;
const HEIGHT: i32 = 600;
const TITLE: &str = "librashader DirectX 9";
use windows::{
core::*, Win32::Foundation::*, Win32::Graphics::Direct3D9::*, Win32::System::LibraryLoader::*,
Win32::UI::WindowsAndMessaging::*,
};
use gfx_maths::Mat4;
use std::mem::transmute;
pub trait DXSample {
fn bind_to_window(&mut self, hwnd: &HWND) -> Result<()>;
fn update(&mut self) {}
fn render(&mut self) -> Result<()> {
Ok(())
}
fn on_key_up(&mut self, _key: u8) {}
fn on_key_down(&mut self, _key: u8) {}
fn title(&self) -> String {
TITLE.into()
}
fn window_size(&self) -> (i32, i32) {
(WIDTH, HEIGHT)
}
fn resize(&mut self, w: u32, h: u32) -> Result<()>;
}
#[inline]
pub fn loword(l: usize) -> u32 {
(l & 0xffff) as u32
}
#[inline]
pub fn hiword(l: usize) -> u32 {
((l >> 16) & 0xffff) as u32
}
fn run_sample<S>(mut sample: S) -> Result<()>
where
S: DXSample,
{
let instance = unsafe { GetModuleHandleA(None)? };
let wc = WNDCLASSEXA {
cbSize: std::mem::size_of::<WNDCLASSEXA>() as u32,
style: CS_HREDRAW | CS_VREDRAW,
lpfnWndProc: Some(wndproc::<S>),
hInstance: HINSTANCE::from(instance),
hCursor: unsafe { LoadCursorW(None, IDC_ARROW)? },
lpszClassName: s!("RustWindowClass"),
..Default::default()
};
let size = sample.window_size();
let atom = unsafe { RegisterClassExA(&wc) };
debug_assert_ne!(atom, 0);
let mut window_rect = RECT {
left: 0,
top: 0,
right: size.0,
bottom: size.1,
};
unsafe { AdjustWindowRect(&mut window_rect, WS_OVERLAPPEDWINDOW, false)? };
let mut title = sample.title();
title.push('\0');
let hwnd = unsafe {
CreateWindowExA(
WINDOW_EX_STYLE::default(),
s!("RustWindowClass"),
PCSTR(title.as_ptr()),
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
window_rect.right - window_rect.left,
window_rect.bottom - window_rect.top,
None, // no parent window
None, // no menus
instance,
Some(&sample as *const _ as _),
)
};
sample.bind_to_window(&hwnd).unwrap();
unsafe { ShowWindow(hwnd, SW_SHOW) };
loop {
let mut message = MSG::default();
if unsafe { PeekMessageA(&mut message, None, 0, 0, PM_REMOVE) }.into() {
unsafe {
TranslateMessage(&message);
DispatchMessageA(&message);
}
if message.message == WM_QUIT {
break;
}
}
}
Ok(())
}
fn sample_wndproc<S: DXSample>(sample: &mut S, message: u32, wparam: WPARAM) -> bool {
match message {
WM_KEYDOWN => {
sample.on_key_down(wparam.0 as u8);
true
}
WM_KEYUP => {
sample.on_key_up(wparam.0 as u8);
true
}
WM_PAINT => {
sample.update();
sample.render().unwrap();
true
}
WM_SIZE => {
sample.resize(loword(wparam.0), hiword(wparam.0)).unwrap();
true
}
_ => false,
}
}
extern "system" fn wndproc<S: DXSample>(
window: HWND,
message: u32,
wparam: WPARAM,
lparam: LPARAM,
) -> LRESULT {
match message {
WM_CREATE => {
unsafe {
let create_struct: &CREATESTRUCTA = transmute(lparam);
SetWindowLongPtrA(window, GWLP_USERDATA, create_struct.lpCreateParams as _);
}
LRESULT::default()
}
WM_DESTROY => {
unsafe { PostQuitMessage(0) };
LRESULT::default()
}
_ => {
let user_data = unsafe { GetWindowLongPtrA(window, GWLP_USERDATA) };
let sample = std::ptr::NonNull::<S>::new(user_data as _);
let handled = sample.map_or(false, |mut s| {
sample_wndproc(unsafe { s.as_mut() }, message, wparam)
});
if handled {
LRESULT::default()
} else {
unsafe { DefWindowProcA(window, message, wparam, lparam) }
}
}
}
}
// #[repr(C)]
// struct Vertex {
// position: [f32; 3],
// color: [f32; 3],
// }
#[repr(C)]
struct Vertex {
position: [f32; 4],
color: u32,
}
#[repr(C)]
#[derive(Default)]
struct TriangleUniforms {
projection_matrix: Mat4,
model_matrix: Mat4,
view_matrix: Mat4,
}
pub mod d3d9_hello_triangle {
use super::*;
use std::path::{Path, PathBuf};
use librashader_common::Viewport;
use librashader_runtime::quad::IDENTITY_MVP;
use librashader_runtime_d3d9::options::FilterChainOptionsD3D9;
use librashader_runtime_d3d9::FilterChainD3D9;
use std::time::Instant;
pub struct Sample {
pub direct3d: IDirect3D9,
pub resources: Option<Resources>,
pub filter: PathBuf,
}
pub struct Resources {
pub device: IDirect3DDevice9,
pub filter: FilterChainD3D9,
// pub depth_buffer: ID3D11Texture2D,
// pub depth_stencil_view: ID3D11DepthStencilView,
// pub triangle_vertices: ID3D11Buffer,
// pub triangle_indices: ID3D11Buffer,
// pub triangle_uniforms: ID3D11Buffer,
// pub vs: ID3D11VertexShader,
// pub ps: ID3D11PixelShader,
// pub input_layout: ID3D11InputLayout,
pub frame_start: Instant,
pub frame_end: Instant,
pub elapsed: f32,
pub frame_count: usize,
pub renderbuffer: IDirect3DTexture9,
// pub renderbufffer_rtv: ID3D11RenderTargetView,
// pub backbuffer: Option<ID3D11Texture2D>,
// pub backbuffer_rtv: Option<ID3D11RenderTargetView>,
// pub viewport: D3D11_VIEWPORT,
// pub shader_output: Option<ID3D11Texture2D>,
// pub deferred_context: ID3D11DeviceContext,
pub vbo: IDirect3DVertexBuffer9,
pub vao: IDirect3DVertexDeclaration9,
}
impl Sample {
pub(crate) fn new(filter: impl AsRef<Path>) -> Result<Self> {
// unsafe {
// let mut debug: Option<ID3D12Debug> = None;
// if let Some(debug) = D3D12GetDebugInterface(&mut debug).ok().and(debug) {
// eprintln!("enabling debug");
// debug.EnableDebugLayer();
// }
// }
// let direct3d = unsafe { Direct3DCreate9On12(D3D_SDK_VERSION, std::ptr::null_mut(), 0).unwrap() };
let direct3d = unsafe { Direct3DCreate9(D3D_SDK_VERSION).unwrap() };
Ok(Sample {
filter: filter.as_ref().to_path_buf(),
direct3d,
resources: None,
})
}
}
impl DXSample for Sample {
fn bind_to_window(&mut self, hwnd: &HWND) -> Result<()> {
let device = create_device(&self.direct3d, *hwnd)?;
let filter = unsafe {
FilterChainD3D9::load_from_path(
&self.filter,
&device,
Some(&FilterChainOptionsD3D9 {
force_no_mipmaps: false,
disable_cache: true,
}),
)
.unwrap()
};
let (vbo, vao) = create_triangle_buffers(&device)?;
let renderbuffer = unsafe {
let mut tex = None;
device.CreateTexture(
WIDTH as u32,
HEIGHT as u32,
1,
D3DUSAGE_RENDERTARGET as u32,
D3DFMT_A8R8G8B8,
D3DPOOL_DEFAULT,
&mut tex,
std::ptr::null_mut(),
)?;
tex.unwrap()
};
self.resources = Some(Resources {
device,
filter,
vbo,
vao,
frame_end: Instant::now(),
frame_start: Instant::now(),
elapsed: 0f32,
// renderbuffer,
// renderbufffer_rtv: render_rtv,
// deferred_context: context,
// viewport: D3D11_VIEWPORT {
// TopLeftX: 0.0,
// TopLeftY: 0.0,
// Width: WIDTH as f32,
// Height: HEIGHT as f32,
// MinDepth: D3D11_MIN_DEPTH,
// MaxDepth: D3D11_MAX_DEPTH,
// },
// shader_output: None,
frame_count: 0usize,
renderbuffer,
});
Ok(())
}
// fn resize(&mut self, _w: u32, _h: u32) -> Result<()> {
// unsafe {
// if let Some(resources) = self.resources.as_mut() {
// drop(resources.backbuffer_rtv.take());
// drop(resources.backbuffer.take());
// resources
// .swapchain
// .ResizeBuffers(0, 0, 0, DXGI_FORMAT_UNKNOWN, 0)
// .unwrap_or_else(|f| eprintln!("{f:?}"));
// let (rtv, backbuffer) = create_rtv(&self.device, &resources.swapchain)?;
//
// resources.backbuffer = Some(backbuffer);
// resources.backbuffer_rtv = Some(rtv);
// }
// }
// Ok(())
// }
fn render(&mut self) -> Result<()> {
let Some(resources) = &mut self.resources else {
return Ok(());
};
resources.frame_end = Instant::now();
let time = resources.frame_end - resources.frame_start;
let time = time.as_secs() as f32 * 1000.0;
// framelimit set to 60fps
if time < (1000.0f32 / 60.0f32) {
return Ok(());
}
resources.elapsed += 0.0000001 * time;
resources.elapsed %= 6.283_185_5_f32;
// resources.triangle_uniform_values.model_matrix = Mat4::rotate(Quaternion::axis_angle(Vec3::new(0.0, 0.0, 1.0), resources.elapsed));
unsafe {
resources
.device
.SetTransform(D3DTS_PROJECTION, IDENTITY_MVP.as_ptr().cast())?;
resources
.device
.SetTransform(D3DTS_VIEW, IDENTITY_MVP.as_ptr().cast())?;
resources
.device
.SetTransform(D3DTRANSFORMSTATETYPE(256), IDENTITY_MVP.as_ptr().cast())?;
let rendertarget = resources.renderbuffer.GetSurfaceLevel(0).unwrap();
let backbuffer = resources
.device
.GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO)?;
resources.device.SetRenderTarget(0, &rendertarget)?;
resources.device.Clear(
0,
std::ptr::null_mut(),
D3DCLEAR_TARGET as u32,
0xFF4d6699,
0.0,
0,
)?;
resources.device.BeginScene()?;
resources.device.SetStreamSource(
0,
&resources.vbo,
0,
std::mem::size_of::<Vertex>() as u32,
)?;
resources.device.SetVertexDeclaration(&resources.vao)?;
resources
.device
.SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE.0 as u32)?;
resources
.device
.SetRenderState(D3DRS_CLIPPING, FALSE.0 as u32)?;
resources
.device
.SetRenderState(D3DRS_ZFUNC, D3DCMP_ALWAYS.0 as u32)?;
resources
.device
.SetRenderState(D3DRS_ZENABLE, FALSE.0 as u32)?;
resources
.device
.SetRenderState(D3DRS_LIGHTING, FALSE.0 as u32)?;
resources.device.DrawPrimitive(D3DPT_TRIANGLELIST, 0, 1)?;
resources.device.EndScene()?;
resources
.filter
.frame(
resources.renderbuffer.clone(),
&Viewport {
x: 0.0,
y: 0.0,
mvp: None,
output: backbuffer.clone(),
},
0,
None,
)
.unwrap();
// resources.device.StretchRect(
// &rendertarget,
// std::ptr::null_mut(),
// &backbuffer,
// std::ptr::null_mut(),
// D3DTEXF_POINT,
// )?;
}
unsafe {
resources.device.Present(
std::ptr::null_mut(),
std::ptr::null_mut(),
None,
std::ptr::null_mut(),
)?;
}
resources.frame_count += 1;
Ok(())
}
fn resize(&mut self, _w: u32, _h: u32) -> Result<()> {
Ok(())
}
}
pub fn get_vbo_desc() -> [D3DVERTEXELEMENT9; 3] {
[
D3DVERTEXELEMENT9 {
Stream: 0,
Offset: 0,
Type: D3DDECLTYPE_FLOAT3.0 as u8,
Method: D3DDECLMETHOD_DEFAULT.0 as u8,
Usage: D3DDECLUSAGE_POSITION.0 as u8,
UsageIndex: 0,
},
D3DVERTEXELEMENT9 {
Stream: 0,
Offset: (std::mem::size_of::<f32>() * 4) as u16,
Type: D3DDECLTYPE_D3DCOLOR.0 as u8,
Method: D3DDECLMETHOD_DEFAULT.0 as u8,
Usage: D3DDECLUSAGE_COLOR.0 as u8,
UsageIndex: 0,
},
D3DVERTEXELEMENT9 {
Stream: 0xFF,
Offset: 0,
Type: D3DDECLTYPE_UNUSED.0 as u8,
Method: 0,
Usage: 0,
UsageIndex: 0,
},
]
}
fn create_triangle_buffers(
device: &IDirect3DDevice9,
) -> Result<(IDirect3DVertexBuffer9, IDirect3DVertexDeclaration9)> {
// let vertices = [
// Vertex {
// position: [0.5f32, -0.5, 0.0],
// color: [1.0, 0.0, 0.0],
// },
// Vertex {
// position: [-0.5, -0.5, 0.0],
// color: [0.0, 1.0, 0.0],
// },
// Vertex {
// position: [0.0, 0.5, 0.0],
// color: [0.0, 0.0, 1.0],
// },
// ];
const TRIANGLE_VERTICES: [Vertex; 3] = [
Vertex {
position: [0.5, -0.5, 0.0, 1.0],
color: 0xFFFF0000,
}, // Red
Vertex {
position: [-0.5, -0.5, 0.0, 1.0],
color: 0xFF00FF00,
}, // Green
Vertex {
position: [0.0, 0.5, 0.0, 1.0],
color: 0xFF0000FF,
}, // Blue
];
unsafe {
let mut vb = None;
device.CreateVertexBuffer(
(std::mem::size_of::<Vertex>() * 3) as u32,
0,
D3DFVF_XYZW | D3DFVF_DIFFUSE,
D3DPOOL_DEFAULT,
&mut vb,
std::ptr::null_mut(),
)?;
let vb = vb.unwrap();
let mut vertices = std::ptr::null_mut();
vb.Lock(0, 0, &mut vertices, 0)?;
std::ptr::copy_nonoverlapping(
TRIANGLE_VERTICES.as_ptr() as *const std::ffi::c_void,
vertices,
std::mem::size_of_val(&TRIANGLE_VERTICES),
);
vb.Unlock()?;
let vao = device.CreateVertexDeclaration(get_vbo_desc().as_ptr())?;
Ok((vb, vao))
}
}
fn create_device(d3d9: &IDirect3D9, hwnd: HWND) -> Result<IDirect3DDevice9> {
let mut present_params: D3DPRESENT_PARAMETERS = Default::default();
present_params.BackBufferWidth = WIDTH as u32;
present_params.BackBufferHeight = HEIGHT as u32;
present_params.Windowed = TRUE;
present_params.SwapEffect = D3DSWAPEFFECT_DISCARD;
present_params.hDeviceWindow = hwnd;
present_params.BackBufferFormat = D3DFMT_UNKNOWN;
present_params.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE as u32;
let mut device = None;
unsafe {
// d3d9.CreateDevice(
// D3DADAPTER_DEFAULT,
// D3DDEVTYPE_HAL,
// present_params.hDeviceWindow,
// D3DCREATE_HARDWARE_VERTEXPROCESSING as u32,
// &mut present_params,
// &mut device,
// )?;
d3d9.CreateDevice(
D3DADAPTER_DEFAULT,
D3DDEVTYPE_HAL,
present_params.hDeviceWindow,
D3DCREATE_HARDWARE_VERTEXPROCESSING as u32,
&mut present_params,
&mut device,
)?;
}
Ok(device.unwrap())
}
}
pub fn main<S: DXSample>(sample: S) -> Result<()> {
run_sample(sample)?;
Ok(())
}

View file

@ -0,0 +1,28 @@
mod hello_triangle;
const FILTER_PATH: &str = "../test/shaders_slang/test/feedback.slangp";
#[test]
fn triangle_d3d9() {
let sample = hello_triangle::d3d9_hello_triangle::Sample::new(
FILTER_PATH,
// Some(&FilterChainOptionsD3D9 {
// force_no_mipmaps: false,
// disable_cache: false,
// }),
// replace below with 'None' for the triangle
// None,
)
.unwrap();
// let sample = hello_triangle_old::d3d11_hello_triangle::Sample::new(
// "../test/slang-shaders/bezel/Mega_Bezel/Presets/MBZ__0__SMOOTH-ADV.slangp",
// Some(&FilterChainOptions {
// use_deferred_context: true,
// })
// )
// .unwrap();
// let sample = hello_triangle_old::d3d11_hello_triangle::Sample::new("../test/basic.slangp").unwrap();
hello_triangle::main(sample).unwrap();
}

View file

@ -63,7 +63,12 @@ impl<T> BindUniform<VariableLocation, T, ()> for GlUniformBinder
where
T: GlUniformScalar,
{
fn bind_uniform(block: UniformMemberBlock, value: T, location: VariableLocation, _: &()) -> Option<()> {
fn bind_uniform(
block: UniformMemberBlock,
value: T,
location: VariableLocation,
_: &(),
) -> Option<()> {
if let Some(location) = location.location(block)
&& location.bindable()
{
@ -89,7 +94,7 @@ impl BindUniform<VariableLocation, &[f32; 4], ()> for GlUniformBinder {
block: UniformMemberBlock,
vec4: &[f32; 4],
location: VariableLocation,
_: &()
_: &(),
) -> Option<()> {
if let Some(location) = location.location(block)
&& location.bindable()
@ -114,7 +119,7 @@ impl BindUniform<VariableLocation, &[f32; 16], ()> for GlUniformBinder {
block: UniformMemberBlock,
mat4: &[f32; 16],
location: VariableLocation,
_: &()
_: &(),
) -> Option<()> {
if let Some(location) = location.location(block)
&& location.bindable()

View file

@ -24,7 +24,8 @@ use std::sync::Arc;
pub struct FilterPass {
pub reflection: ShaderReflection,
pub(crate) uniform_storage: UniformStorage<NoUniformBinder, Option<()>, RawVulkanBuffer, Box<[u8]>, Arc<ash::Device>>,
pub(crate) uniform_storage:
UniformStorage<NoUniformBinder, Option<()>, RawVulkanBuffer, Box<[u8]>, Arc<ash::Device>>,
pub uniform_bindings: FastHashMap<UniformBinding, MemberOffset>,
pub source: ShaderSource,
pub config: ShaderPassConfig,

View file

@ -25,8 +25,13 @@ use wgpu::{BindGroupDescriptor, BindGroupEntry, BindingResource, BufferBinding,
pub struct FilterPass {
pub device: Arc<wgpu::Device>,
pub reflection: ShaderReflection,
pub(crate) uniform_storage:
UniformStorage<NoUniformBinder, Option<()>, WgpuStagedBuffer, WgpuStagedBuffer, Arc<wgpu::Device>>,
pub(crate) uniform_storage: UniformStorage<
NoUniformBinder,
Option<()>,
WgpuStagedBuffer,
WgpuStagedBuffer,
Arc<wgpu::Device>,
>,
pub uniform_bindings: FastHashMap<UniformBinding, MemberOffset>,
pub source: ShaderSource,
pub config: ShaderPassConfig,

View file

@ -15,7 +15,7 @@ pub trait TextureInput {
}
/// A uniform member offset with context that needs to be resolved.
pub trait ContextOffset<H, C, D=()>
pub trait ContextOffset<H, C, D = ()>
where
H: BindUniform<C, f32, D>,
H: BindUniform<C, u32, D>,
@ -124,7 +124,12 @@ where
) {
// Bind MVP
if let Some(offset) = uniform_bindings.get(&UniqueSemantics::MVP.into()) {
uniform_storage.bind_mat4(offset.offset(), uniform_inputs.mvp, offset.context(), device);
uniform_storage.bind_mat4(
offset.offset(),
uniform_inputs.mvp,
offset.context(),
device,
);
}
// Bind OutputSize
@ -133,7 +138,7 @@ where
offset.offset(),
uniform_inputs.framebuffer_size,
offset.context(),
device
device,
);
}
@ -143,7 +148,7 @@ where
offset.offset(),
uniform_inputs.viewport_size,
offset.context(),
device
device,
);
}
@ -153,7 +158,7 @@ where
offset.offset(),
uniform_inputs.frame_count,
offset.context(),
device
device,
);
}
@ -163,13 +168,18 @@ where
offset.offset(),
uniform_inputs.frame_direction,
offset.context(),
device
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);
uniform_storage.bind_scalar(
offset.offset(),
uniform_inputs.rotation,
offset.context(),
device,
);
}
// bind TotalSubFrames
@ -178,7 +188,7 @@ where
offset.offset(),
uniform_inputs.total_subframes,
offset.context(),
device
device,
);
}
@ -188,7 +198,7 @@ where
offset.offset(),
uniform_inputs.current_subframe,
offset.context(),
device
device,
);
}
@ -246,7 +256,12 @@ where
.semantics(index + 1)
.into(),
) {
uniform_storage.bind_vec4(offset.offset(), history.size(), offset.context(), device);
uniform_storage.bind_vec4(
offset.offset(),
history.size(),
offset.context(),
device,
);
}
}
@ -289,7 +304,12 @@ where
if let Some(offset) =
uniform_bindings.get(&TextureSemantics::PassFeedback.semantics(index).into())
{
uniform_storage.bind_vec4(offset.offset(), feedback.size(), offset.context(), device);
uniform_storage.bind_vec4(
offset.offset(),
feedback.size(),
offset.context(),
device,
);
}
}

View file

@ -69,7 +69,7 @@ impl<T, D> BindUniform<Option<()>, T, D> for NoUniformBinder {
}
/// A helper to bind uniform variables to UBO or Push Constant Buffers.
pub struct UniformStorage<H = NoUniformBinder, C = Option<()>, U = Box<[u8]>, P = Box<[u8]>, D=()>
pub struct UniformStorage<H = NoUniformBinder, C = Option<()>, U = Box<[u8]>, P = Box<[u8]>, D = ()>
where
U: Deref<Target = [u8]> + DerefMut,
P: Deref<Target = [u8]> + DerefMut,
@ -78,7 +78,7 @@ where
push: P,
_h: PhantomData<H>,
_c: PhantomData<C>,
_d: PhantomData<D>
_d: PhantomData<D>,
}
impl<H, C, U, P, D> UniformStorage<H, C, U, P, D>
@ -118,8 +118,13 @@ where
/// Bind a scalar to the given offset.
#[inline(always)]
pub fn bind_scalar<T: UniformScalar>(&mut self, offset: MemberOffset, value: T, ctx: C, device: &D)
where
pub fn bind_scalar<T: UniformScalar>(
&mut self,
offset: MemberOffset,
value: T,
ctx: C,
device: &D,
) where
H: BindUniform<C, T, D>,
{
for ty in UniformMemberBlock::TYPES {
@ -193,7 +198,13 @@ where
}
/// Bind a `vec4` to the given offset.
#[inline(always)]
pub fn bind_vec4(&mut self, offset: MemberOffset, value: impl Into<[f32; 4]>, ctx: C, device: &D) {
pub fn bind_vec4(
&mut self,
offset: MemberOffset,
value: impl Into<[f32; 4]>,
ctx: C,
device: &D,
) {
let vec4 = value.into();
for ty in UniformMemberBlock::TYPES {

View file

@ -4,11 +4,12 @@
layout(set = 0, binding = 0, std140) uniform UBO
{
mat4 MVP;
float ColorMod2;
float ColorMod;
uint FrameCount;
};
layout(push_constant) uniform Push {
float ColorMod;
float ColorMod2;
} params;
#pragma name StockShader
@ -32,5 +33,5 @@ layout(location = 0) out vec4 FragColor;
layout(binding = 1) uniform sampler2D Source;
void main()
{
FragColor = texture(Source, vTexCoord) * params.ColorMod;
FragColor = texture(Source, vTexCoord) * ColorMod * params.ColorMod2;
}

View file

@ -7,10 +7,3 @@ float_framebuffer0 = "false"
srgb_framebuffer0 = "false"
ColorMod = "1.700000"
shader1 = "basic.slang"
wrap_mode1 = "clamp_to_border"
mipmap_input1 = "true"
alias1 = ""
float_framebuffer0 = "false"
srgb_framebuffer0 = "false"
ColorMod = "1.700000"