use concinnity_core::gfx::render_types::RtParams;
use concinnity_core::render::error::RenderResult;
use concinnity_core::render::post::rt_reflections::{RtParamsInputs, RtReflectionSettings};
use windows::Win32::Foundation::RECT;
use windows::Win32::Graphics::Direct3D12::*;
use crate::directx::allocator::{DeviceAllocator, PooledBuffer};
use crate::directx::builtin_shaders;
use crate::directx::builtin_shaders::CompileProgram;
use crate::directx::com;
use crate::directx::context::{DxContext, FRAMES, align256, dump_on_err};
use crate::directx::descriptor_slot::DescriptorTables;
use crate::directx::descriptor_slot::SrvSlot;
use crate::directx::error::map_hresult;
use crate::directx::pso::{Blend, GraphicsPso};
use crate::directx::root_sig::{Range, RootSig, SamplerState, Visibility};
use crate::directx::texture::{
HDR_FORMAT, create_rt_target, transition_barrier, write_format_rtv, write_format_srv,
};
const RT_PARAMS_UBO_SIZE: u64 = 144;
struct RtShaders {
vs: Vec<u8>,
flat_ps: Vec<u8>,
textured_ps: Vec<u8>,
}
fn compile_rt_shaders(hot_reload: bool) -> RenderResult<RtShaders> {
Ok(RtShaders {
vs: builtin_shaders::FULLSCREEN_VERT.compile(hot_reload)?,
flat_ps: builtin_shaders::RT_REFLECTIONS_FRAG.compile(hot_reload)?,
textured_ps: builtin_shaders::RT_REFLECTIONS_FRAG_TEXTURED.compile(hot_reload)?,
})
}
fn create_rt_root_signature(device: &ID3D12Device) -> RenderResult<ID3D12RootSignature> {
use Visibility::Pixel;
RootSig::new()
.cbv(0, Pixel) .srv(0, Pixel) .srv(1, Pixel) .srv(2, Pixel) .srv(3, Pixel) .srv_table(4, 1, Pixel) .srv_table(5, 1, Pixel) .srv_table(6, 1, Pixel) .srv_table(7, 1, Pixel) .table(&[Range::bindless_srv(1)], Pixel) .srv(8, Pixel) .srv(9, Pixel) .srv_table(10, 1, Pixel)
.cbv(4, Pixel) .srv(11, Pixel) .cbv(5, Pixel) .srv(12, Pixel) .static_sampler(SamplerState::LinearClamp, 0, Pixel)
.static_sampler(SamplerState::LinearClamp, 1, Pixel)
.static_sampler(SamplerState::LinearWrap, 2, Pixel)
.static_sampler(SamplerState::LinearClamp, 3, Pixel)
.build(device, "rt reflections root sig")
}
fn create_rt_psos(
device: &ID3D12Device,
info_queue: Option<&ID3D12InfoQueue>,
root_sig: &ID3D12RootSignature,
shaders: &RtShaders,
) -> RenderResult<RebuiltRtPipelines> {
let pso = |ps: &[u8], label: &str| {
dump_on_err(
info_queue,
GraphicsPso::fullscreen(root_sig, &shaders.vs, ps, HDR_FORMAT, Blend::Opaque)
.build(device, label),
)
};
Ok(RebuiltRtPipelines {
flat_pso: pso(&shaders.flat_ps, "rt reflections flat")?,
textured_pso: pso(&shaders.textured_ps, "rt reflections textured")?,
})
}
pub(in crate::directx) struct RtReflectionsResources {
pub(in crate::directx) settings: RtReflectionSettings,
pub(in crate::directx) output: ID3D12Resource,
extent: (u32, u32),
output_rtv: D3D12_CPU_DESCRIPTOR_HANDLE,
pub(in crate::directx) output_srv_gpu: SrvSlot,
params_ubo_resources: Vec<PooledBuffer>,
params_ubo_ptrs: Vec<*mut u8>,
root_sig: ID3D12RootSignature,
flat_pso: ID3D12PipelineState,
textured_pso: ID3D12PipelineState,
}
pub(in crate::directx) struct RtBuildContext<'a> {
pub alloc: &'a DeviceAllocator,
pub width: u32,
pub height: u32,
}
#[derive(Clone, Copy)]
pub(in crate::directx) struct RtOutputDescriptors {
pub output_rtv: D3D12_CPU_DESCRIPTOR_HANDLE,
pub output_srv: (D3D12_CPU_DESCRIPTOR_HANDLE, SrvSlot),
}
pub(in crate::directx) struct RtBuildInit<'a> {
pub info_queue: Option<&'a ID3D12InfoQueue>,
pub hot_reload: bool,
}
impl RtReflectionsResources {
pub(in crate::directx) fn new(
ctx: RtBuildContext,
settings: RtReflectionSettings,
descriptors: RtOutputDescriptors,
init: RtBuildInit,
) -> RenderResult<Self> {
let RtBuildContext {
alloc,
width,
height,
} = ctx;
let device = alloc.device();
let RtOutputDescriptors {
output_rtv,
output_srv,
} = descriptors;
let RtBuildInit {
info_queue,
hot_reload,
} = init;
let extent = settings.trace_extent(width, height);
let output = create_rt_target(device, extent.0, extent.1, HDR_FORMAT)?;
write_format_rtv(device, &output, output_rtv, HDR_FORMAT);
write_format_srv(device, &output, output_srv.0, HDR_FORMAT);
let params_size = align256(RT_PARAMS_UBO_SIZE);
let mut params_ubo_resources: Vec<PooledBuffer> = Vec::with_capacity(FRAMES);
let mut params_ubo_ptrs: Vec<*mut u8> = Vec::with_capacity(FRAMES);
for _ in 0..FRAMES {
let buf = alloc.alloc_buffer(
params_size,
D3D12_HEAP_TYPE_UPLOAD,
D3D12_RESOURCE_STATE_GENERIC_READ,
)?;
let mut ptr = std::ptr::null_mut::<std::ffi::c_void>();
unsafe { buf.Map(0, None, Some(&mut ptr)) }
.map_err(|e| map_hresult(e.code(), "map rt params ubo"))?;
params_ubo_ptrs.push(ptr as *mut u8);
params_ubo_resources.push(buf);
}
let shaders = compile_rt_shaders(hot_reload)?;
let root_sig = dump_on_err(info_queue, create_rt_root_signature(device))?;
let RebuiltRtPipelines {
flat_pso,
textured_pso,
} = create_rt_psos(device, info_queue, &root_sig, &shaders)?;
Ok(Self {
settings,
output,
extent,
output_rtv,
output_srv_gpu: output_srv.1,
params_ubo_resources,
params_ubo_ptrs,
root_sig,
flat_pso,
textured_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 = self.output_srv_gpu.cpu_in(srv_cpu_base, srv_gpu_base);
self.extent = self.settings.trace_extent(width, height);
self.output = create_rt_target(device, self.extent.0, self.extent.1, HDR_FORMAT)?;
write_format_rtv(device, &self.output, self.output_rtv, HDR_FORMAT);
write_format_srv(device, &self.output, srv_cpu, HDR_FORMAT);
Ok(())
}
}
pub(in crate::directx) struct RebuiltRtPipelines {
pub flat_pso: ID3D12PipelineState,
pub textured_pso: ID3D12PipelineState,
}
pub(in crate::directx) fn rebuild_rt_reflections_pipelines(
device: &ID3D12Device,
rt: &RtReflectionsResources,
hot_reload: bool,
info_queue: Option<&ID3D12InfoQueue>,
) -> RenderResult<RebuiltRtPipelines> {
create_rt_psos(
device,
info_queue,
&rt.root_sig,
&compile_rt_shaders(hot_reload)?,
)
}
pub(in crate::directx) fn swap_rt_reflections_pipelines(
rt: &mut RtReflectionsResources,
rebuilt: RebuiltRtPipelines,
) {
rt.flat_pso = rebuilt.flat_pso;
rt.textured_pso = rebuilt.textured_pso;
}
impl DxContext {
pub(in crate::directx) fn encode_rt_reflections(
&self,
cmd: &ID3D12GraphicsCommandList,
frame_idx: usize,
fov_y_radians: f32,
aspect: f32,
cam_pos: [f32; 3],
) {
let (rt, accel, gbuffer) = match (&self.rt_reflections, &self.rt.accel, &self.gbuffer) {
(Some(r), Some(a), Some(g)) => (r, a, g),
_ => return,
};
let reflection_srv = rt.output_srv_gpu;
let v = self.state.view.matrix;
let inv_view_rot = [
[v[0][0], v[1][0], v[2][0], 0.0],
[v[0][1], v[1][1], v[2][1], 0.0],
[v[0][2], v[1][2], v[2][2], 0.0],
[0.0, 0.0, 0.0, 1.0],
];
let params = rt.settings.params(RtParamsInputs {
fov_y_radians,
aspect,
inv_view_rot,
cam_pos,
sun_dir: self.fog.sun_dir,
sun_color: self.fog.sun_color,
prefilter_mip_count: self.scene.env_map.prefilter_mip_count as f32,
sky_rot: self.state.view.sky_rot,
});
unsafe {
std::ptr::copy_nonoverlapping(
¶ms as *const RtParams as *const u8,
rt.params_ubo_ptrs[frame_idx],
std::mem::size_of::<RtParams>(),
);
}
let params_gva = com::gpu_va(&rt.params_ubo_resources[frame_idx]);
let textured = self.cull.main_bindless_pso.is_some();
let pso = if textured {
&rt.textured_pso
} else {
&rt.flat_pso
};
let out_to_rt = transition_barrier(
&rt.output,
D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE,
D3D12_RESOURCE_STATE_RENDER_TARGET,
);
unsafe { cmd.ResourceBarrier(&[out_to_rt]) };
let (w, h) = rt.extent;
unsafe {
cmd.OMSetRenderTargets(1, Some(&rt.output_rtv), false, None);
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.SetPipelineState(pso);
cmd.SetGraphicsRootSignature(&rt.root_sig);
cmd.SetDescriptorHeaps(&[Some(self.descriptors.srv_heap.clone())]);
cmd.SetGraphicsRootConstantBufferView(0, params_gva);
cmd.SetGraphicsRootShaderResourceView(1, accel.tlas_gva());
cmd.SetGraphicsRootShaderResourceView(
2,
com::gpu_va(&self.scene.geometry.vertex_buffer),
);
cmd.SetGraphicsRootShaderResourceView(
3,
com::gpu_va(&self.scene.geometry.index_buffer),
);
cmd.SetGraphicsRootShaderResourceView(4, accel.geom_table_gva());
cmd.set_graphics_srv_table(5, self.targets.hdr.srv_gpu);
cmd.set_graphics_srv_table(6, gbuffer.normal_depth_srv_gpu);
cmd.set_graphics_srv_table(7, gbuffer.roughness_srv_gpu);
cmd.set_graphics_srv_table(8, self.prefilter_cube_srv_gpu());
if textured {
cmd.set_graphics_srv_table(9, self.cull.bindless_pool_gpu[self.current_frame]);
}
cmd.SetGraphicsRootShaderResourceView(10, accel.deformed_verts_gva());
cmd.SetGraphicsRootShaderResourceView(11, accel.skinned_index_gva());
self.probe_bindings(frame_idx).bind(cmd, 12);
cmd.IASetPrimitiveTopology(
windows::Win32::Graphics::Direct3D::D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST,
);
cmd.IASetVertexBuffers(0, None);
cmd.IASetIndexBuffer(None);
cmd.DrawInstanced(3, 1, 0, 0);
}
let out_to_psr = transition_barrier(
&rt.output,
D3D12_RESOURCE_STATE_RENDER_TARGET,
D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE,
);
unsafe { cmd.ResourceBarrier(&[out_to_psr]) };
self.encode_reflection_composite(cmd, frame_idx, reflection_srv);
}
}