use windows::Win32::Graphics::Direct3D12::*;
use windows::Win32::Graphics::Dxgi::Common::*;
use crate::directx::com;
use crate::directx::context::{DxContext, dump_on_err};
use crate::directx::pipeline::serialize_desc_and_create;
use crate::directx::post::ssr::SSR_OUTPUT_FORMAT;
use crate::directx::slang_builtins;
use crate::directx::texture::{create_rt_target, write_format_rtv, write_format_srv};
struct ReflCompShaders {
vs: Vec<u8>,
blur_ps: Vec<u8>,
composite_ps: Vec<u8>,
}
fn compile_refl_composite_shaders(hot_reload: bool) -> Result<ReflCompShaders, String> {
Ok(ReflCompShaders {
vs: slang_builtins::FULLSCREEN_VERT.compile(hot_reload)?,
blur_ps: slang_builtins::REFLECTION_BLUR.compile(hot_reload)?,
composite_ps: slang_builtins::REFLECTION_COMPOSITE.compile(hot_reload)?,
})
}
fn srv_table_root_sig(
device: &ID3D12Device,
count: u32,
name: &str,
) -> Result<ID3D12RootSignature, String> {
let ranges: Vec<D3D12_DESCRIPTOR_RANGE> = (0..count)
.map(|i| D3D12_DESCRIPTOR_RANGE {
RangeType: D3D12_DESCRIPTOR_RANGE_TYPE_SRV,
NumDescriptors: 1,
BaseShaderRegister: i,
RegisterSpace: 0,
OffsetInDescriptorsFromTableStart: D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND,
})
.collect();
let params: Vec<D3D12_ROOT_PARAMETER> = ranges
.iter()
.map(|r| D3D12_ROOT_PARAMETER {
ParameterType: D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE,
Anonymous: D3D12_ROOT_PARAMETER_0 {
DescriptorTable: D3D12_ROOT_DESCRIPTOR_TABLE {
NumDescriptorRanges: 1,
pDescriptorRanges: r,
},
},
ShaderVisibility: D3D12_SHADER_VISIBILITY_PIXEL,
})
.collect();
let samplers: Vec<D3D12_STATIC_SAMPLER_DESC> = (0..count)
.map(|reg| 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()
})
.collect();
let desc = D3D12_ROOT_SIGNATURE_DESC {
NumParameters: params.len() as u32,
pParameters: params.as_ptr(),
NumStaticSamplers: samplers.len() as u32,
pStaticSamplers: samplers.as_ptr(),
Flags: D3D12_ROOT_SIGNATURE_FLAG_NONE,
};
serialize_desc_and_create(device, &desc, name)
}
fn create_fullscreen_pso(
device: &ID3D12Device,
root_sig: &ID3D12RootSignature,
vs: &[u8],
ps: &[u8],
) -> Result<ID3D12PipelineState, String> {
let pso_desc = D3D12_GRAPHICS_PIPELINE_STATE_DESC {
pRootSignature: com::borrowed(root_sig),
VS: D3D12_SHADER_BYTECODE {
pShaderBytecode: vs.as_ptr() as _,
BytecodeLength: vs.len(),
},
PS: D3D12_SHADER_BYTECODE {
pShaderBytecode: ps.as_ptr() as _,
BytecodeLength: ps.len(),
},
PrimitiveTopologyType: D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE,
NumRenderTargets: 1,
RTVFormats: {
let mut a = [DXGI_FORMAT_UNKNOWN; 8];
a[0] = SSR_OUTPUT_FORMAT;
a
},
DSVFormat: DXGI_FORMAT_UNKNOWN,
SampleDesc: DXGI_SAMPLE_DESC {
Count: 1,
Quality: 0,
},
SampleMask: u32::MAX,
RasterizerState: D3D12_RASTERIZER_DESC {
FillMode: D3D12_FILL_MODE_SOLID,
CullMode: D3D12_CULL_MODE_NONE,
FrontCounterClockwise: true.into(),
DepthClipEnable: false.into(),
..Default::default()
},
DepthStencilState: D3D12_DEPTH_STENCIL_DESC {
DepthEnable: false.into(),
DepthWriteMask: D3D12_DEPTH_WRITE_MASK_ZERO,
StencilEnable: false.into(),
..Default::default()
},
BlendState: D3D12_BLEND_DESC {
RenderTarget: {
let mut arr = [D3D12_RENDER_TARGET_BLEND_DESC::default(); 8];
arr[0] = D3D12_RENDER_TARGET_BLEND_DESC {
BlendEnable: false.into(),
RenderTargetWriteMask: D3D12_COLOR_WRITE_ENABLE_ALL.0 as u8,
..Default::default()
};
arr
},
..Default::default()
},
..Default::default()
};
unsafe { crate::directx::pso_library::create_graphics(device, &pso_desc) }
.map_err(|e| format!("create reflection composite PSO: {e}"))
}
pub(in crate::directx) struct ReflectionCompositeResources {
pub(in crate::directx) output: ID3D12Resource,
pub(in crate::directx) output_rtv: D3D12_CPU_DESCRIPTOR_HANDLE,
pub(in crate::directx) output_srv_gpu: D3D12_GPU_DESCRIPTOR_HANDLE,
blur: ID3D12Resource,
blur_rtv: D3D12_CPU_DESCRIPTOR_HANDLE,
blur_srv_gpu: D3D12_GPU_DESCRIPTOR_HANDLE,
blur_scale: u32,
blur_root_sig: ID3D12RootSignature,
blur_pso: ID3D12PipelineState,
composite_root_sig: ID3D12RootSignature,
composite_pso: ID3D12PipelineState,
}
#[derive(Clone, Copy)]
pub(in crate::directx) struct ReflectionCompositeSlots {
pub output_rtv: D3D12_CPU_DESCRIPTOR_HANDLE,
pub output_srv: (D3D12_CPU_DESCRIPTOR_HANDLE, D3D12_GPU_DESCRIPTOR_HANDLE),
pub blur_rtv: D3D12_CPU_DESCRIPTOR_HANDLE,
pub blur_srv: (D3D12_CPU_DESCRIPTOR_HANDLE, D3D12_GPU_DESCRIPTOR_HANDLE),
}
impl ReflectionCompositeResources {
pub(in crate::directx) fn new(
device: &ID3D12Device,
width: u32,
height: u32,
blur_scale: u32,
slots: ReflectionCompositeSlots,
info_queue: Option<&ID3D12InfoQueue>,
hot_reload: bool,
) -> Result<Self, String> {
let blur_scale = blur_scale.max(1);
let output = create_rt_target(device, width, height, SSR_OUTPUT_FORMAT)?;
write_format_rtv(device, &output, slots.output_rtv, SSR_OUTPUT_FORMAT);
write_format_srv(device, &output, slots.output_srv.0, SSR_OUTPUT_FORMAT);
let bw = (width / blur_scale).max(1);
let bh = (height / blur_scale).max(1);
let blur = create_rt_target(device, bw, bh, SSR_OUTPUT_FORMAT)?;
write_format_rtv(device, &blur, slots.blur_rtv, SSR_OUTPUT_FORMAT);
write_format_srv(device, &blur, slots.blur_srv.0, SSR_OUTPUT_FORMAT);
let shaders = compile_refl_composite_shaders(hot_reload)?;
let blur_root_sig = dump_on_err(
info_queue,
srv_table_root_sig(device, 2, "reflection blur root sig"),
)?;
let composite_root_sig = dump_on_err(
info_queue,
srv_table_root_sig(device, 5, "reflection composite root sig"),
)?;
let blur_pso = dump_on_err(
info_queue,
create_fullscreen_pso(device, &blur_root_sig, &shaders.vs, &shaders.blur_ps),
)?;
let composite_pso = dump_on_err(
info_queue,
create_fullscreen_pso(
device,
&composite_root_sig,
&shaders.vs,
&shaders.composite_ps,
),
)?;
Ok(Self {
output,
output_rtv: slots.output_rtv,
output_srv_gpu: slots.output_srv.1,
blur,
blur_rtv: slots.blur_rtv,
blur_srv_gpu: slots.blur_srv.1,
blur_scale,
blur_root_sig,
blur_pso,
composite_root_sig,
composite_pso,
})
}
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.output = create_rt_target(device, width, height, SSR_OUTPUT_FORMAT)?;
write_format_rtv(device, &self.output, self.output_rtv, SSR_OUTPUT_FORMAT);
write_format_srv(
device,
&self.output,
srv_cpu(self.output_srv_gpu),
SSR_OUTPUT_FORMAT,
);
let bw = (width / self.blur_scale).max(1);
let bh = (height / self.blur_scale).max(1);
self.blur = create_rt_target(device, bw, bh, SSR_OUTPUT_FORMAT)?;
write_format_rtv(device, &self.blur, self.blur_rtv, SSR_OUTPUT_FORMAT);
write_format_srv(
device,
&self.blur,
srv_cpu(self.blur_srv_gpu),
SSR_OUTPUT_FORMAT,
);
Ok(())
}
}
pub(in crate::directx) struct RebuiltReflectionComposite {
pub blur_pso: ID3D12PipelineState,
pub composite_pso: ID3D12PipelineState,
}
pub(in crate::directx) fn rebuild_reflection_composite_pipelines(
device: &ID3D12Device,
rc: &ReflectionCompositeResources,
hot_reload: bool,
info_queue: Option<&ID3D12InfoQueue>,
) -> Result<RebuiltReflectionComposite, String> {
let shaders = compile_refl_composite_shaders(hot_reload)?;
let blur_pso = dump_on_err(
info_queue,
create_fullscreen_pso(device, &rc.blur_root_sig, &shaders.vs, &shaders.blur_ps),
)?;
let composite_pso = dump_on_err(
info_queue,
create_fullscreen_pso(
device,
&rc.composite_root_sig,
&shaders.vs,
&shaders.composite_ps,
),
)?;
Ok(RebuiltReflectionComposite {
blur_pso,
composite_pso,
})
}
pub(in crate::directx) fn swap_reflection_composite_pipelines(
rc: &mut ReflectionCompositeResources,
rebuilt: RebuiltReflectionComposite,
) {
rc.blur_pso = rebuilt.blur_pso;
rc.composite_pso = rebuilt.composite_pso;
}
impl DxContext {
pub(in crate::directx) fn encode_reflection_composite(
&self,
cmd: &ID3D12GraphicsCommandList,
reflection_srv: D3D12_GPU_DESCRIPTOR_HANDLE,
) {
let Some(rc) = &self.reflection_composite else {
return;
};
let Some(gbuffer) = &self.gbuffer else {
return;
};
self.begin_fullscreen_rt(cmd, &rc.blur, rc.blur_rtv);
unsafe {
cmd.SetPipelineState(&rc.blur_pso);
cmd.SetGraphicsRootSignature(&rc.blur_root_sig);
cmd.SetGraphicsRootDescriptorTable(0, reflection_srv);
cmd.SetGraphicsRootDescriptorTable(1, gbuffer.roughness_srv_gpu);
cmd.IASetPrimitiveTopology(
windows::Win32::Graphics::Direct3D::D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST,
);
cmd.IASetVertexBuffers(0, None);
cmd.IASetIndexBuffer(None);
cmd.DrawInstanced(3, 1, 0, 0);
}
self.end_fullscreen_rt(cmd, &rc.blur);
self.bind_fullscreen_rt(cmd, &rc.output, rc.output_rtv);
unsafe {
cmd.SetPipelineState(&rc.composite_pso);
cmd.SetGraphicsRootSignature(&rc.composite_root_sig);
cmd.SetGraphicsRootDescriptorTable(0, reflection_srv);
cmd.SetGraphicsRootDescriptorTable(1, self.hdr.srv_gpu);
cmd.SetGraphicsRootDescriptorTable(2, gbuffer.normal_depth_srv_gpu);
cmd.SetGraphicsRootDescriptorTable(3, gbuffer.roughness_srv_gpu);
cmd.SetGraphicsRootDescriptorTable(4, rc.blur_srv_gpu);
cmd.IASetPrimitiveTopology(
windows::Win32::Graphics::Direct3D::D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST,
);
cmd.IASetVertexBuffers(0, None);
cmd.IASetIndexBuffer(None);
cmd.DrawInstanced(3, 1, 0, 0);
}
}
}
#[cfg(test)]
mod tests {
#[test]
fn reflection_composite_shaders_compile() {
if !crate::slangc_gate::slangc_available() {
return;
}
super::compile_refl_composite_shaders(false)
.expect("reflection composite shaders must compile");
}
}