use std::cell::Cell;
use windows::Win32::Graphics::Direct3D12::*;
use crate::gfx::fullscreen::{FullscreenPass, encode_fullscreen};
use crate::directx::context::{DxContext, dump_on_err};
use crate::directx::pipeline::{create_composite_pso, serialize_desc_and_create};
use crate::directx::post::gbuffer::GbufferResources;
use crate::directx::slang_builtins;
use crate::directx::texture::{HDR_FORMAT, create_rt_target, write_format_rtv, write_hdr_srv};
struct TaaShaders {
resolve_vs: Vec<u8>,
resolve_ps: Vec<u8>,
}
fn compile_taa_shaders(hot_reload: bool) -> Result<TaaShaders, String> {
Ok(TaaShaders {
resolve_vs: slang_builtins::FULLSCREEN_VERT.compile(hot_reload)?,
resolve_ps: slang_builtins::TAA_FRAG.compile(hot_reload)?,
})
}
fn create_taa_root_signature(device: &ID3D12Device) -> Result<ID3D12RootSignature, String> {
let make_range = |reg: u32| D3D12_DESCRIPTOR_RANGE {
RangeType: D3D12_DESCRIPTOR_RANGE_TYPE_SRV,
NumDescriptors: 1,
BaseShaderRegister: reg,
RegisterSpace: 0,
OffsetInDescriptorsFromTableStart: D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND,
};
let scene_range = make_range(0);
let velocity_range = make_range(1);
let history_range = make_range(2);
let params = [
D3D12_ROOT_PARAMETER {
ParameterType: D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE,
Anonymous: D3D12_ROOT_PARAMETER_0 {
DescriptorTable: D3D12_ROOT_DESCRIPTOR_TABLE {
NumDescriptorRanges: 1,
pDescriptorRanges: &scene_range,
},
},
ShaderVisibility: D3D12_SHADER_VISIBILITY_PIXEL,
},
D3D12_ROOT_PARAMETER {
ParameterType: D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE,
Anonymous: D3D12_ROOT_PARAMETER_0 {
DescriptorTable: D3D12_ROOT_DESCRIPTOR_TABLE {
NumDescriptorRanges: 1,
pDescriptorRanges: &velocity_range,
},
},
ShaderVisibility: D3D12_SHADER_VISIBILITY_PIXEL,
},
D3D12_ROOT_PARAMETER {
ParameterType: D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE,
Anonymous: D3D12_ROOT_PARAMETER_0 {
DescriptorTable: D3D12_ROOT_DESCRIPTOR_TABLE {
NumDescriptorRanges: 1,
pDescriptorRanges: &history_range,
},
},
ShaderVisibility: D3D12_SHADER_VISIBILITY_PIXEL,
},
D3D12_ROOT_PARAMETER {
ParameterType: D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS,
Anonymous: D3D12_ROOT_PARAMETER_0 {
Constants: D3D12_ROOT_CONSTANTS {
ShaderRegister: 0,
RegisterSpace: 0,
Num32BitValues: 1,
},
},
ShaderVisibility: D3D12_SHADER_VISIBILITY_PIXEL,
},
];
let linear_clamp = |reg: u32| D3D12_STATIC_SAMPLER_DESC {
Filter: D3D12_FILTER_MIN_MAG_MIP_LINEAR,
AddressU: D3D12_TEXTURE_ADDRESS_MODE_CLAMP,
AddressV: D3D12_TEXTURE_ADDRESS_MODE_CLAMP,
AddressW: D3D12_TEXTURE_ADDRESS_MODE_CLAMP,
ComparisonFunc: D3D12_COMPARISON_FUNC_ALWAYS,
BorderColor: D3D12_STATIC_BORDER_COLOR_OPAQUE_BLACK,
MinLOD: 0.0,
MaxLOD: f32::MAX,
ShaderRegister: reg,
RegisterSpace: 0,
ShaderVisibility: D3D12_SHADER_VISIBILITY_PIXEL,
..Default::default()
};
let static_samplers = [linear_clamp(0), linear_clamp(1), linear_clamp(2)];
let desc = D3D12_ROOT_SIGNATURE_DESC {
NumParameters: params.len() as u32,
pParameters: params.as_ptr(),
NumStaticSamplers: static_samplers.len() as u32,
pStaticSamplers: static_samplers.as_ptr(),
Flags: D3D12_ROOT_SIGNATURE_FLAG_NONE,
};
serialize_desc_and_create(device, &desc, "taa root sig")
}
pub(in crate::directx) struct TaaResources {
pub(in crate::directx) history: [ID3D12Resource; 2],
pub(in crate::directx) history_rtv: [D3D12_CPU_DESCRIPTOR_HANDLE; 2],
pub(in crate::directx) history_srv_gpu: [D3D12_GPU_DESCRIPTOR_HANDLE; 2],
pub(in crate::directx) taa_root_sig: ID3D12RootSignature,
pub(in crate::directx) taa_pso: ID3D12PipelineState,
pub(in crate::directx) frame: Cell<u32>,
}
impl TaaResources {
pub(in crate::directx) fn new(
device: &ID3D12Device,
width: u32,
height: u32,
history_rtv: [D3D12_CPU_DESCRIPTOR_HANDLE; 2],
history_srv: [(D3D12_CPU_DESCRIPTOR_HANDLE, D3D12_GPU_DESCRIPTOR_HANDLE); 2],
info_queue: Option<&ID3D12InfoQueue>,
hot_reload: bool,
) -> Result<Self, String> {
let history = [
create_rt_target(device, width, height, HDR_FORMAT)?,
create_rt_target(device, width, height, HDR_FORMAT)?,
];
for i in 0..2 {
write_hdr_srv(device, &history[i], history_srv[i].0);
write_format_rtv(device, &history[i], history_rtv[i], HDR_FORMAT);
}
let shaders = compile_taa_shaders(hot_reload)?;
let taa_root_sig = dump_on_err(info_queue, create_taa_root_signature(device))?;
let taa_pso = dump_on_err(
info_queue,
create_composite_pso(
device,
&taa_root_sig,
&shaders.resolve_vs,
&shaders.resolve_ps,
HDR_FORMAT,
),
)?;
Ok(Self {
history,
history_rtv,
history_srv_gpu: [history_srv[0].1, history_srv[1].1],
taa_root_sig,
taa_pso,
frame: Cell::new(0),
})
}
pub(in crate::directx) fn output_index(&self) -> usize {
(self.frame.get() % 2) as usize
}
pub(in crate::directx) fn resize_to(
&mut self,
device: &ID3D12Device,
width: u32,
height: u32,
srv_cpu_base: D3D12_CPU_DESCRIPTOR_HANDLE,
srv_gpu_base: D3D12_GPU_DESCRIPTOR_HANDLE,
) -> Result<(), String> {
let srv_cpu = |gpu: D3D12_GPU_DESCRIPTOR_HANDLE| D3D12_CPU_DESCRIPTOR_HANDLE {
ptr: srv_cpu_base.ptr + (gpu.ptr - srv_gpu_base.ptr) as usize,
};
self.history = [
create_rt_target(device, width, height, HDR_FORMAT)?,
create_rt_target(device, width, height, HDR_FORMAT)?,
];
for i in 0..2 {
write_hdr_srv(device, &self.history[i], srv_cpu(self.history_srv_gpu[i]));
write_format_rtv(device, &self.history[i], self.history_rtv[i], HDR_FORMAT);
}
self.frame.set(0);
Ok(())
}
}
pub(in crate::directx) struct RebuiltTaaPipelines {
pub taa_pso: ID3D12PipelineState,
}
pub(in crate::directx) fn rebuild_taa_pipelines(
device: &ID3D12Device,
taa: &TaaResources,
hot_reload: bool,
info_queue: Option<&ID3D12InfoQueue>,
) -> Result<RebuiltTaaPipelines, String> {
let shaders = compile_taa_shaders(hot_reload)?;
let taa_pso = dump_on_err(
info_queue,
create_composite_pso(
device,
&taa.taa_root_sig,
&shaders.resolve_vs,
&shaders.resolve_ps,
HDR_FORMAT,
),
)?;
Ok(RebuiltTaaPipelines { taa_pso })
}
impl DxContext {
pub(in crate::directx) fn encode_taa(&self, cmd: &ID3D12GraphicsCommandList) {
let Some(taa) = &self.taa else { return };
let Some(gbuffer) = &self.gbuffer else { return };
let cur = taa.output_index();
encode_fullscreen(
&TaaResolvePass {
ctx: self,
taa,
gbuffer,
cur,
},
cmd,
);
}
}
struct TaaResolvePass<'a> {
ctx: &'a DxContext,
taa: &'a TaaResources,
gbuffer: &'a GbufferResources,
cur: usize,
}
impl FullscreenPass for TaaResolvePass<'_> {
type Rec = ID3D12GraphicsCommandList;
fn begin(&self, cmd: &Self::Rec) {
self.ctx.bind_fullscreen_rt(
cmd,
&self.taa.history[self.cur],
self.taa.history_rtv[self.cur],
);
}
fn draw(&self, cmd: &Self::Rec) {
let hist = 1 - self.cur;
let history_valid: f32 = if self.taa.frame.get() > 0 { 1.0 } else { 0.0 };
unsafe {
cmd.SetPipelineState(&self.taa.taa_pso);
cmd.SetGraphicsRootSignature(&self.taa.taa_root_sig);
cmd.SetGraphicsRootDescriptorTable(0, self.ctx.scene_srv_for_post());
cmd.SetGraphicsRootDescriptorTable(1, self.gbuffer.velocity_srv_gpu);
cmd.SetGraphicsRootDescriptorTable(2, self.taa.history_srv_gpu[hist]);
cmd.SetGraphicsRoot32BitConstants(
3,
1,
&history_valid as *const f32 as *const std::ffi::c_void,
0,
);
cmd.IASetPrimitiveTopology(
windows::Win32::Graphics::Direct3D::D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST,
);
cmd.IASetVertexBuffers(0, None);
cmd.IASetIndexBuffer(None);
cmd.DrawInstanced(3, 1, 0, 0);
}
}
fn end(&self, _cmd: &Self::Rec) {}
}