use windows::Win32::Foundation::RECT;
use windows::Win32::Graphics::Direct3D12::*;
use windows::Win32::Graphics::Dxgi::Common::*;
use crate::gfx::render_types::SsaoParams;
use crate::directx::com;
use crate::directx::context::{DxContext, dump_on_err};
use crate::directx::pipeline::serialize_desc_and_create;
use crate::directx::slang_builtins;
use crate::directx::texture::{
create_rt_target, transition_barrier, write_format_rtv, write_format_srv,
};
pub(crate) const SSAO_OCCLUSION_FORMAT: DXGI_FORMAT = DXGI_FORMAT_R8_UNORM;
struct SsaoShaders {
fullscreen_vs: Vec<u8>,
kernel_ps: Vec<u8>,
blur_ps: Vec<u8>,
}
fn compile_ssao_shaders(hot_reload: bool) -> Result<SsaoShaders, String> {
Ok(SsaoShaders {
fullscreen_vs: slang_builtins::FULLSCREEN_VERT.compile(hot_reload)?,
kernel_ps: slang_builtins::SSAO_KERNEL.compile(hot_reload)?,
blur_ps: slang_builtins::SSAO_BLUR.compile(hot_reload)?,
})
}
fn create_ssao_kernel_root_signature(device: &ID3D12Device) -> Result<ID3D12RootSignature, String> {
let gbuffer_range = D3D12_DESCRIPTOR_RANGE {
RangeType: D3D12_DESCRIPTOR_RANGE_TYPE_SRV,
NumDescriptors: 1,
BaseShaderRegister: 0, RegisterSpace: 0,
OffsetInDescriptorsFromTableStart: D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND,
};
let params = [
D3D12_ROOT_PARAMETER {
ParameterType: D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS,
Anonymous: D3D12_ROOT_PARAMETER_0 {
Constants: D3D12_ROOT_CONSTANTS {
ShaderRegister: 0,
RegisterSpace: 0,
Num32BitValues: 4,
},
},
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: &gbuffer_range,
},
},
ShaderVisibility: D3D12_SHADER_VISIBILITY_PIXEL,
},
];
let static_sampler = 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: 0,
RegisterSpace: 0,
ShaderVisibility: D3D12_SHADER_VISIBILITY_PIXEL,
..Default::default()
};
let desc = D3D12_ROOT_SIGNATURE_DESC {
NumParameters: params.len() as u32,
pParameters: params.as_ptr(),
NumStaticSamplers: 1,
pStaticSamplers: &static_sampler,
Flags: D3D12_ROOT_SIGNATURE_FLAG_NONE,
};
serialize_desc_and_create(device, &desc, "ssao kernel root sig")
}
fn create_ssao_blur_root_signature(device: &ID3D12Device) -> Result<ID3D12RootSignature, String> {
let ao_range = D3D12_DESCRIPTOR_RANGE {
RangeType: D3D12_DESCRIPTOR_RANGE_TYPE_SRV,
NumDescriptors: 1,
BaseShaderRegister: 0, RegisterSpace: 0,
OffsetInDescriptorsFromTableStart: D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND,
};
let gbuffer_range = D3D12_DESCRIPTOR_RANGE {
RangeType: D3D12_DESCRIPTOR_RANGE_TYPE_SRV,
NumDescriptors: 1,
BaseShaderRegister: 1, RegisterSpace: 0,
OffsetInDescriptorsFromTableStart: D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND,
};
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: &ao_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: &gbuffer_range,
},
},
ShaderVisibility: D3D12_SHADER_VISIBILITY_PIXEL,
},
];
let static_samplers = [0u32, 1].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()
});
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, "ssao blur root sig")
}
fn create_ssao_fullscreen_pso(
device: &ID3D12Device,
root_sig: &ID3D12RootSignature,
vs: &[u8],
ps: &[u8],
label: &str,
) -> 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] = SSAO_OCCLUSION_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 ssao {label} PSO: {e}"))
}
pub(in crate::directx) struct SsaoResources {
pub(in crate::directx) settings: crate::gfx::ssao::SsaoSettings,
pub(in crate::directx) ao_raw: ID3D12Resource,
pub(in crate::directx) ao_raw_rtv: D3D12_CPU_DESCRIPTOR_HANDLE,
pub(in crate::directx) ao_raw_srv_gpu: D3D12_GPU_DESCRIPTOR_HANDLE,
pub(in crate::directx) ao_rtv: D3D12_CPU_DESCRIPTOR_HANDLE,
pub(in crate::directx) ao_srv_gpu: D3D12_GPU_DESCRIPTOR_HANDLE,
pub(in crate::directx) kernel_root_sig: ID3D12RootSignature,
pub(in crate::directx) kernel_pso: ID3D12PipelineState,
pub(in crate::directx) blur_root_sig: ID3D12RootSignature,
pub(in crate::directx) blur_pso: ID3D12PipelineState,
}
#[derive(Clone, Copy)]
pub(in crate::directx) struct SsaoDeviceCtx<'a> {
pub device: &'a ID3D12Device,
pub info_queue: Option<&'a ID3D12InfoQueue>,
}
#[derive(Clone, Copy)]
pub(in crate::directx) struct SsaoDescriptorHandles {
pub ao_raw_rtv: D3D12_CPU_DESCRIPTOR_HANDLE,
pub ao_raw_srv: (D3D12_CPU_DESCRIPTOR_HANDLE, D3D12_GPU_DESCRIPTOR_HANDLE),
pub ao_rtv: D3D12_CPU_DESCRIPTOR_HANDLE,
pub ao_srv: (D3D12_CPU_DESCRIPTOR_HANDLE, D3D12_GPU_DESCRIPTOR_HANDLE),
}
impl SsaoResources {
pub(in crate::directx) fn new(
ctx: SsaoDeviceCtx,
width: u32,
height: u32,
settings: crate::gfx::ssao::SsaoSettings,
handles: SsaoDescriptorHandles,
ao_resource: &ID3D12Resource,
hot_reload: bool,
) -> Result<Self, String> {
let SsaoDeviceCtx { device, info_queue } = ctx;
let SsaoDescriptorHandles {
ao_raw_rtv,
ao_raw_srv,
ao_rtv,
ao_srv,
} = handles;
let ao_raw = create_rt_target(device, width, height, SSAO_OCCLUSION_FORMAT)?;
write_format_rtv(device, &ao_raw, ao_raw_rtv, SSAO_OCCLUSION_FORMAT);
write_format_srv(device, &ao_raw, ao_raw_srv.0, SSAO_OCCLUSION_FORMAT);
write_format_rtv(device, ao_resource, ao_rtv, SSAO_OCCLUSION_FORMAT);
write_format_srv(device, ao_resource, ao_srv.0, SSAO_OCCLUSION_FORMAT);
let shaders = compile_ssao_shaders(hot_reload)?;
let kernel_root_sig = dump_on_err(info_queue, create_ssao_kernel_root_signature(device))?;
let kernel_pso = dump_on_err(
info_queue,
create_ssao_fullscreen_pso(
device,
&kernel_root_sig,
&shaders.fullscreen_vs,
&shaders.kernel_ps,
"kernel",
),
)?;
let blur_root_sig = dump_on_err(info_queue, create_ssao_blur_root_signature(device))?;
let blur_pso = dump_on_err(
info_queue,
create_ssao_fullscreen_pso(
device,
&blur_root_sig,
&shaders.fullscreen_vs,
&shaders.blur_ps,
"blur",
),
)?;
Ok(Self {
settings,
ao_raw,
ao_raw_rtv,
ao_raw_srv_gpu: ao_raw_srv.1,
ao_rtv,
ao_srv_gpu: ao_srv.1,
kernel_root_sig,
kernel_pso,
blur_root_sig,
blur_pso,
})
}
}
pub(in crate::directx) struct RebuiltSsaoPipelines {
pub kernel_pso: ID3D12PipelineState,
pub blur_pso: ID3D12PipelineState,
}
impl SsaoResources {
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,
ao_resource: &ID3D12Resource,
) -> 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.ao_raw = create_rt_target(device, width, height, SSAO_OCCLUSION_FORMAT)?;
write_format_rtv(device, &self.ao_raw, self.ao_raw_rtv, SSAO_OCCLUSION_FORMAT);
write_format_srv(
device,
&self.ao_raw,
srv_cpu(self.ao_raw_srv_gpu),
SSAO_OCCLUSION_FORMAT,
);
write_format_rtv(device, ao_resource, self.ao_rtv, SSAO_OCCLUSION_FORMAT);
write_format_srv(
device,
ao_resource,
srv_cpu(self.ao_srv_gpu),
SSAO_OCCLUSION_FORMAT,
);
Ok(())
}
}
pub(in crate::directx) fn rebuild_ssao_pipelines(
device: &ID3D12Device,
ssao: &SsaoResources,
hot_reload: bool,
info_queue: Option<&ID3D12InfoQueue>,
) -> Result<RebuiltSsaoPipelines, String> {
let shaders = compile_ssao_shaders(hot_reload)?;
let kernel_pso = dump_on_err(
info_queue,
create_ssao_fullscreen_pso(
device,
&ssao.kernel_root_sig,
&shaders.fullscreen_vs,
&shaders.kernel_ps,
"kernel",
),
)?;
let blur_pso = dump_on_err(
info_queue,
create_ssao_fullscreen_pso(
device,
&ssao.blur_root_sig,
&shaders.fullscreen_vs,
&shaders.blur_ps,
"blur",
),
)?;
Ok(RebuiltSsaoPipelines {
kernel_pso,
blur_pso,
})
}
impl DxContext {
pub(in crate::directx) fn ssao_ao_srv_gpu(&self) -> D3D12_GPU_DESCRIPTOR_HANDLE {
match &self.ssao.resources {
Some(s) => s.ao_srv_gpu,
None => self.ssao.white_srv_gpu,
}
}
pub(in crate::directx) fn encode_ssao(
&self,
cmd: &ID3D12GraphicsCommandList,
fov_y_radians: f32,
aspect: f32,
) {
let ssao = match &self.ssao.resources {
Some(s) => s,
None => return,
};
let gbuffer_srv = match &self.gbuffer {
Some(g) => g.normal_depth_srv_gpu,
None => return,
};
let params = ssao.settings.params(fov_y_radians, aspect);
let w = self.extent.render_width;
let h = self.extent.render_height;
unsafe {
let vp = D3D12_VIEWPORT {
TopLeftX: 0.0,
TopLeftY: 0.0,
Width: w as f32,
Height: h as f32,
MinDepth: 0.0,
MaxDepth: 1.0,
};
cmd.RSSetViewports(&[vp]);
let scissor = RECT {
left: 0,
top: 0,
right: w as i32,
bottom: h as i32,
};
cmd.RSSetScissorRects(&[scissor]);
cmd.IASetPrimitiveTopology(
windows::Win32::Graphics::Direct3D::D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST,
);
}
let to_rt = transition_barrier(
&ssao.ao_raw,
D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE,
D3D12_RESOURCE_STATE_RENDER_TARGET,
);
unsafe { cmd.ResourceBarrier(&[to_rt]) };
unsafe {
cmd.OMSetRenderTargets(1, Some(&ssao.ao_raw_rtv), false, None);
cmd.SetPipelineState(&ssao.kernel_pso);
cmd.SetGraphicsRootSignature(&ssao.kernel_root_sig);
cmd.SetDescriptorHeaps(&[Some(self.descriptors.srv_heap.clone())]);
cmd.SetGraphicsRoot32BitConstants(
0,
4,
¶ms as *const SsaoParams as *const std::ffi::c_void,
0,
);
cmd.SetGraphicsRootDescriptorTable(1, gbuffer_srv);
cmd.IASetVertexBuffers(0, None);
cmd.IASetIndexBuffer(None);
cmd.DrawInstanced(3, 1, 0, 0);
}
let raw_to_psr = transition_barrier(
&ssao.ao_raw,
D3D12_RESOURCE_STATE_RENDER_TARGET,
D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE,
);
unsafe { cmd.ResourceBarrier(&[raw_to_psr]) };
unsafe {
cmd.OMSetRenderTargets(1, Some(&ssao.ao_rtv), false, None);
cmd.SetPipelineState(&ssao.blur_pso);
cmd.SetGraphicsRootSignature(&ssao.blur_root_sig);
cmd.SetGraphicsRootDescriptorTable(0, ssao.ao_raw_srv_gpu);
cmd.SetGraphicsRootDescriptorTable(1, gbuffer_srv);
cmd.IASetVertexBuffers(0, None);
cmd.IASetIndexBuffer(None);
cmd.DrawInstanced(3, 1, 0, 0);
}
unsafe {
cmd.SetDescriptorHeaps(&[
Some(self.descriptors.srv_heap.clone()),
Some(self.descriptors.sampler_heap.clone()),
]);
}
}
}