use concinnity_core::render::error::RenderResult;
use concinnity_core::render::post::device::PostBlend;
use windows::Win32::Graphics::Direct3D12::*;
use crate::directx::builtin_shaders;
use crate::directx::builtin_shaders::CompileProgram;
use crate::directx::context::{DxContext, dump_on_err};
use crate::directx::descriptor_slot::DescriptorTables;
use crate::directx::descriptor_slot::SrvSlot;
use crate::directx::pipeline::{create_blended_composite_pso, serialize_desc_and_create};
use crate::directx::post::fullscreen::FullscreenExtent;
use crate::directx::post::ssr::SSR_OUTPUT_FORMAT;
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) -> RenderResult<ReflCompShaders> {
Ok(ReflCompShaders {
vs: builtin_shaders::FULLSCREEN_VERT.compile(hot_reload)?,
blur_ps: builtin_shaders::REFLECTION_BLUR.compile(hot_reload)?,
composite_ps: builtin_shaders::REFLECTION_COMPOSITE.compile(hot_reload)?,
})
}
fn srv_table_root_sig(
device: &ID3D12Device,
count: u32,
name: &str,
) -> RenderResult<ID3D12RootSignature> {
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)
}
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: SrvSlot,
blur: ID3D12Resource,
blur_rtv: D3D12_CPU_DESCRIPTOR_HANDLE,
blur_srv_gpu: SrvSlot,
blur_extent: FullscreenExtent,
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, SrvSlot),
pub blur_rtv: D3D12_CPU_DESCRIPTOR_HANDLE,
pub blur_srv: (D3D12_CPU_DESCRIPTOR_HANDLE, SrvSlot),
}
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,
) -> RenderResult<Self> {
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_extent = FullscreenExtent {
width: bw,
height: bh,
};
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_blended_composite_pso(
device,
&blur_root_sig,
&shaders.vs,
&shaders.blur_ps,
SSR_OUTPUT_FORMAT,
PostBlend::Replace,
"reflection blur",
),
)?;
let composite_pso = dump_on_err(
info_queue,
create_blended_composite_pso(
device,
&composite_root_sig,
&shaders.vs,
&shaders.composite_ps,
SSR_OUTPUT_FORMAT,
PostBlend::Replace,
"reflection composite",
),
)?;
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_extent,
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: SrvSlot,
) -> RenderResult<()> {
let srv_cpu = |gpu: SrvSlot| gpu.cpu_in(srv_cpu_base, srv_gpu_base);
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_extent = FullscreenExtent {
width: bw,
height: bh,
};
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>,
) -> RenderResult<RebuiltReflectionComposite> {
let shaders = compile_refl_composite_shaders(hot_reload)?;
let blur_pso = dump_on_err(
info_queue,
create_blended_composite_pso(
device,
&rc.blur_root_sig,
&shaders.vs,
&shaders.blur_ps,
SSR_OUTPUT_FORMAT,
PostBlend::Replace,
"reflection blur",
),
)?;
let composite_pso = dump_on_err(
info_queue,
create_blended_composite_pso(
device,
&rc.composite_root_sig,
&shaders.vs,
&shaders.composite_ps,
SSR_OUTPUT_FORMAT,
PostBlend::Replace,
"reflection composite",
),
)?;
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: SrvSlot,
) {
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, rc.blur_extent);
unsafe {
cmd.SetPipelineState(&rc.blur_pso);
cmd.SetGraphicsRootSignature(&rc.blur_root_sig);
cmd.set_graphics_srv_table(0, reflection_srv);
cmd.set_graphics_srv_table(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_rtv,
FullscreenExtent {
width: self.targets.extent.render_width,
height: self.targets.extent.render_height,
},
);
unsafe {
cmd.SetPipelineState(&rc.composite_pso);
cmd.SetGraphicsRootSignature(&rc.composite_root_sig);
cmd.set_graphics_srv_table(0, reflection_srv);
cmd.set_graphics_srv_table(1, self.targets.hdr.srv_gpu);
cmd.set_graphics_srv_table(2, gbuffer.normal_depth_srv_gpu);
cmd.set_graphics_srv_table(3, gbuffer.roughness_srv_gpu);
cmd.set_graphics_srv_table(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 !concinnity_shader::dxc_available() {
return;
}
super::compile_refl_composite_shaders(false)
.expect("reflection composite shaders must compile");
}
}