use windows::Win32::Graphics::Direct3D12::*;
use windows::Win32::Graphics::Dxgi::Common::*;
use crate::directx::com;
use crate::directx::context::dump_on_err;
use crate::directx::pipeline::serialize_desc_and_create;
use crate::directx::slang_builtins;
use crate::directx::slang_builtins::SlangCompile;
use crate::directx::texture::uav_barrier;
const HIZ_PARAMS_DWORDS: u32 = 4;
#[derive(Copy, Clone)]
#[repr(C)]
struct HizParams {
dst_w: u32,
dst_h: u32,
src_mip: u32,
sample_count: u32,
}
pub(super) struct HiZResources {
pub(super) root_sig: ID3D12RootSignature,
pub(super) init_single_pso: ID3D12PipelineState,
pub(super) init_msaa_pso: ID3D12PipelineState,
pub(super) downsample_pso: ID3D12PipelineState,
pub(super) texture: ID3D12Resource,
pub(super) width: u32,
pub(super) height: u32,
pub(super) mip_count: u32,
pub(super) rest_state: D3D12_RESOURCE_STATES,
pub(super) srv_cpu: D3D12_CPU_DESCRIPTOR_HANDLE,
pub(super) srv_gpu: D3D12_GPU_DESCRIPTOR_HANDLE,
pub(super) depth_srv_gpu: D3D12_GPU_DESCRIPTOR_HANDLE,
pub(super) mip_uav_cpus: Vec<D3D12_CPU_DESCRIPTOR_HANDLE>,
pub(super) mip_uav_gpus: Vec<D3D12_GPU_DESCRIPTOR_HANDLE>,
}
type HizShaders = (Vec<u8>, Vec<u8>, Vec<u8>);
pub(in crate::directx) fn compile_hiz_shaders(hot_reload: bool) -> Result<HizShaders, String> {
let init_single = slang_builtins::HIZ_INIT_SINGLE.compile(hot_reload)?;
let init_msaa = slang_builtins::HIZ_INIT_MSAA.compile(hot_reload)?;
let downsample = slang_builtins::HIZ_DOWNSAMPLE.compile(hot_reload)?;
Ok((init_single, init_msaa, downsample))
}
pub(in crate::directx) fn create_hiz_root_signature(
device: &ID3D12Device,
) -> Result<ID3D12RootSignature, String> {
let srv_range = D3D12_DESCRIPTOR_RANGE {
RangeType: D3D12_DESCRIPTOR_RANGE_TYPE_SRV,
NumDescriptors: 1,
BaseShaderRegister: 0, RegisterSpace: 0,
OffsetInDescriptorsFromTableStart: D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND,
};
let uav_range = D3D12_DESCRIPTOR_RANGE {
RangeType: D3D12_DESCRIPTOR_RANGE_TYPE_UAV,
NumDescriptors: 2,
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: HIZ_PARAMS_DWORDS,
},
},
ShaderVisibility: D3D12_SHADER_VISIBILITY_ALL,
},
D3D12_ROOT_PARAMETER {
ParameterType: D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE,
Anonymous: D3D12_ROOT_PARAMETER_0 {
DescriptorTable: D3D12_ROOT_DESCRIPTOR_TABLE {
NumDescriptorRanges: 1,
pDescriptorRanges: &srv_range,
},
},
ShaderVisibility: D3D12_SHADER_VISIBILITY_ALL,
},
D3D12_ROOT_PARAMETER {
ParameterType: D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE,
Anonymous: D3D12_ROOT_PARAMETER_0 {
DescriptorTable: D3D12_ROOT_DESCRIPTOR_TABLE {
NumDescriptorRanges: 1,
pDescriptorRanges: &uav_range,
},
},
ShaderVisibility: D3D12_SHADER_VISIBILITY_ALL,
},
];
let desc = D3D12_ROOT_SIGNATURE_DESC {
NumParameters: params.len() as u32,
pParameters: params.as_ptr(),
Flags: D3D12_ROOT_SIGNATURE_FLAG_NONE,
..Default::default()
};
serialize_desc_and_create(device, &desc, "hiz root sig")
}
fn create_hiz_pso(
device: &ID3D12Device,
root_sig: &ID3D12RootSignature,
cs: &[u8],
label: &str,
) -> Result<ID3D12PipelineState, String> {
let desc = D3D12_COMPUTE_PIPELINE_STATE_DESC {
pRootSignature: com::borrowed(root_sig),
CS: D3D12_SHADER_BYTECODE {
pShaderBytecode: cs.as_ptr() as _,
BytecodeLength: cs.len(),
},
..Default::default()
};
unsafe { crate::directx::pso_library::create_compute(device, &desc) }
.map_err(|e| format!("create {label} PSO: {e}"))
}
pub(super) fn hiz_mip_count(width: u32, height: u32) -> u32 {
let m = width.max(height).max(1);
32 - m.leading_zeros()
}
fn create_hiz_texture(
device: &ID3D12Device,
width: u32,
height: u32,
mip_count: u32,
) -> Result<ID3D12Resource, String> {
let heap_props = D3D12_HEAP_PROPERTIES {
Type: D3D12_HEAP_TYPE_DEFAULT,
..Default::default()
};
let desc = D3D12_RESOURCE_DESC {
Dimension: D3D12_RESOURCE_DIMENSION_TEXTURE2D,
Width: width as u64,
Height: height,
DepthOrArraySize: 1,
MipLevels: mip_count as u16,
Format: DXGI_FORMAT_R32_FLOAT,
SampleDesc: DXGI_SAMPLE_DESC {
Count: 1,
Quality: 0,
},
Flags: D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS,
..Default::default()
};
let mut tex: Option<ID3D12Resource> = None;
unsafe {
device.CreateCommittedResource(
&heap_props,
D3D12_HEAP_FLAG_NONE,
&desc,
D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE,
None,
&mut tex,
)
}
.map_err(|e| format!("create hiz texture: {e}"))?;
tex.ok_or_else(|| "create hiz texture returned None".to_string())
}
pub(in crate::directx) fn write_hiz_srv(
device: &ID3D12Device,
tex: &ID3D12Resource,
mip_count: u32,
srv_cpu: D3D12_CPU_DESCRIPTOR_HANDLE,
) {
let desc = D3D12_SHADER_RESOURCE_VIEW_DESC {
Format: DXGI_FORMAT_R32_FLOAT,
ViewDimension: D3D12_SRV_DIMENSION_TEXTURE2D,
Shader4ComponentMapping: D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING,
Anonymous: D3D12_SHADER_RESOURCE_VIEW_DESC_0 {
Texture2D: D3D12_TEX2D_SRV {
MostDetailedMip: 0,
MipLevels: mip_count,
PlaneSlice: 0,
ResourceMinLODClamp: 0.0,
},
},
};
unsafe { device.CreateShaderResourceView(tex, Some(&desc), srv_cpu) };
}
pub(in crate::directx) fn write_hiz_mip_uav(
device: &ID3D12Device,
tex: &ID3D12Resource,
mip: u32,
uav_cpu: D3D12_CPU_DESCRIPTOR_HANDLE,
) {
let desc = D3D12_UNORDERED_ACCESS_VIEW_DESC {
Format: DXGI_FORMAT_R32_FLOAT,
ViewDimension: D3D12_UAV_DIMENSION_TEXTURE2D,
Anonymous: D3D12_UNORDERED_ACCESS_VIEW_DESC_0 {
Texture2D: D3D12_TEX2D_UAV {
MipSlice: mip,
PlaneSlice: 0,
},
},
};
unsafe { device.CreateUnorderedAccessView(tex, None, Some(&desc), uav_cpu) };
}
#[derive(Clone, Copy)]
pub(super) struct HiZDeviceCtx<'a> {
pub device: &'a ID3D12Device,
pub info_queue: Option<&'a ID3D12InfoQueue>,
pub hot_reload: bool,
}
pub(super) struct HiZTarget {
pub width: u32,
pub height: u32,
pub srv_cpu: D3D12_CPU_DESCRIPTOR_HANDLE,
pub srv_gpu: D3D12_GPU_DESCRIPTOR_HANDLE,
pub depth_srv_gpu: D3D12_GPU_DESCRIPTOR_HANDLE,
pub mip_uav_cpus: Vec<D3D12_CPU_DESCRIPTOR_HANDLE>,
pub mip_uav_gpus: Vec<D3D12_GPU_DESCRIPTOR_HANDLE>,
}
impl HiZResources {
pub(super) fn new(ctx: HiZDeviceCtx, target: HiZTarget) -> Result<Self, String> {
let HiZDeviceCtx {
device,
info_queue,
hot_reload,
} = ctx;
let HiZTarget {
width,
height,
srv_cpu,
srv_gpu,
depth_srv_gpu,
mip_uav_cpus,
mip_uav_gpus,
} = target;
let mip_count = hiz_mip_count(width, height).min(mip_uav_cpus.len() as u32);
if mip_count == 0 {
return Err("hiz: zero mip count".into());
}
let (init_single_cs, init_msaa_cs, downsample_cs) = compile_hiz_shaders(hot_reload)?;
let root_sig = dump_on_err(info_queue, create_hiz_root_signature(device))?;
let init_single_pso = dump_on_err(
info_queue,
create_hiz_pso(device, &root_sig, &init_single_cs, "hiz init_single"),
)?;
let init_msaa_pso = dump_on_err(
info_queue,
create_hiz_pso(device, &root_sig, &init_msaa_cs, "hiz init_msaa"),
)?;
let downsample_pso = dump_on_err(
info_queue,
create_hiz_pso(device, &root_sig, &downsample_cs, "hiz downsample"),
)?;
let texture = create_hiz_texture(device, width, height, mip_count)?;
write_hiz_srv(device, &texture, mip_count, srv_cpu);
for (mip, &cpu) in mip_uav_cpus.iter().take(mip_count as usize).enumerate() {
write_hiz_mip_uav(device, &texture, mip as u32, cpu);
}
Ok(Self {
root_sig,
init_single_pso,
init_msaa_pso,
downsample_pso,
texture,
width,
height,
mip_count,
rest_state: D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE,
srv_cpu,
srv_gpu,
depth_srv_gpu,
mip_uav_cpus,
mip_uav_gpus,
})
}
pub(super) fn resize_to(
&mut self,
device: &ID3D12Device,
width: u32,
height: u32,
) -> Result<(), String> {
let new_mip_count = hiz_mip_count(width, height).min(self.mip_uav_cpus.len() as u32);
let texture = create_hiz_texture(device, width, height, new_mip_count)?;
write_hiz_srv(device, &texture, new_mip_count, self.srv_cpu);
for (mip, &cpu) in self
.mip_uav_cpus
.iter()
.take(new_mip_count as usize)
.enumerate()
{
write_hiz_mip_uav(device, &texture, mip as u32, cpu);
}
self.texture = texture;
self.width = width;
self.height = height;
self.mip_count = new_mip_count;
self.rest_state = D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE;
Ok(())
}
pub(super) fn swap_pipelines(
&mut self,
init_single_pso: ID3D12PipelineState,
init_msaa_pso: ID3D12PipelineState,
downsample_pso: ID3D12PipelineState,
) {
self.init_single_pso = init_single_pso;
self.init_msaa_pso = init_msaa_pso;
self.downsample_pso = downsample_pso;
}
}
impl crate::directx::context::DxContext {
pub(in crate::directx) fn encode_hiz_build(&self, cmd: &ID3D12GraphicsCommandList) {
let Some(hiz) = self.cull.hiz.as_ref() else {
return;
};
let msaa = self.hdr.msaa_samples > 1;
let init_params = HizParams {
dst_w: hiz.width,
dst_h: hiz.height,
src_mip: 0,
sample_count: self.hdr.msaa_samples.max(1),
};
unsafe {
cmd.SetComputeRootSignature(&hiz.root_sig);
cmd.SetDescriptorHeaps(&[Some(self.descriptors.srv_heap.clone())]);
cmd.SetPipelineState(if msaa {
&hiz.init_msaa_pso
} else {
&hiz.init_single_pso
});
cmd.SetComputeRoot32BitConstants(
0,
HIZ_PARAMS_DWORDS,
&init_params as *const HizParams as *const std::ffi::c_void,
0,
);
cmd.SetComputeRootDescriptorTable(1, hiz.depth_srv_gpu);
cmd.SetComputeRootDescriptorTable(2, hiz.mip_uav_gpus[0]);
cmd.Dispatch(hiz.width.div_ceil(8), hiz.height.div_ceil(8), 1);
}
unsafe { cmd.ResourceBarrier(&[uav_barrier(&hiz.texture)]) };
let mut cur_w = hiz.width;
let mut cur_h = hiz.height;
for mip in 1..hiz.mip_count {
let next_w = (cur_w / 2).max(1);
let next_h = (cur_h / 2).max(1);
let params = HizParams {
dst_w: next_w,
dst_h: next_h,
src_mip: mip - 1,
sample_count: 0,
};
unsafe {
cmd.SetPipelineState(&hiz.downsample_pso);
cmd.SetComputeRoot32BitConstants(
0,
HIZ_PARAMS_DWORDS,
¶ms as *const HizParams as *const std::ffi::c_void,
0,
);
cmd.SetComputeRootDescriptorTable(1, hiz.srv_gpu);
cmd.SetComputeRootDescriptorTable(2, hiz.mip_uav_gpus[(mip - 1) as usize]);
cmd.Dispatch(next_w.div_ceil(8), next_h.div_ceil(8), 1);
cmd.ResourceBarrier(&[uav_barrier(&hiz.texture)]);
}
cur_w = next_w;
cur_h = next_h;
}
}
}