use windows::Win32::Foundation::RECT;
use windows::Win32::Graphics::Direct3D12::*;
use windows::Win32::Graphics::Dxgi::Common::*;
use crate::directx::allocator::{DeviceAllocator, PooledBuffer};
use crate::gfx::render_types::RtParams;
use crate::gfx::rt_reflections::{RtParamsInputs, RtReflectionSettings};
use crate::directx::com;
use crate::directx::context::{DxContext, FRAMES, align256, 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::{
HDR_FORMAT, create_buffer, 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) -> Result<RtShaders, String> {
Ok(RtShaders {
vs: slang_builtins::FULLSCREEN_VERT.compile(hot_reload)?,
flat_ps: slang_builtins::RT_REFLECTIONS_FRAG.compile(hot_reload)?,
textured_ps: slang_builtins::RT_REFLECTIONS_FRAG_TEXTURED.compile(hot_reload)?,
})
}
fn create_rt_root_signature(device: &ID3D12Device) -> Result<ID3D12RootSignature, String> {
let table_range = |reg: u32| D3D12_DESCRIPTOR_RANGE {
RangeType: D3D12_DESCRIPTOR_RANGE_TYPE_SRV,
NumDescriptors: 1,
BaseShaderRegister: reg,
RegisterSpace: 0,
OffsetInDescriptorsFromTableStart: D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND,
};
let scene_range = table_range(4); let gbuffer_range = table_range(5); let rough_range = table_range(6); let cube_range = table_range(7); let pool_range = D3D12_DESCRIPTOR_RANGE {
RangeType: D3D12_DESCRIPTOR_RANGE_TYPE_SRV,
NumDescriptors: u32::MAX, BaseShaderRegister: 0, RegisterSpace: 1, OffsetInDescriptorsFromTableStart: 0,
};
let probe_cube_range = D3D12_DESCRIPTOR_RANGE {
RangeType: D3D12_DESCRIPTOR_RANGE_TYPE_SRV,
NumDescriptors: concinnity_render::uniforms::MAX_PROBES as u32,
BaseShaderRegister: 10, RegisterSpace: 0,
OffsetInDescriptorsFromTableStart: D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND,
};
let root_cbv = |reg: u32| D3D12_ROOT_PARAMETER {
ParameterType: D3D12_ROOT_PARAMETER_TYPE_CBV,
Anonymous: D3D12_ROOT_PARAMETER_0 {
Descriptor: D3D12_ROOT_DESCRIPTOR {
ShaderRegister: reg,
RegisterSpace: 0,
},
},
ShaderVisibility: D3D12_SHADER_VISIBILITY_PIXEL,
};
let root_srv = |reg: u32| D3D12_ROOT_PARAMETER {
ParameterType: D3D12_ROOT_PARAMETER_TYPE_SRV,
Anonymous: D3D12_ROOT_PARAMETER_0 {
Descriptor: D3D12_ROOT_DESCRIPTOR {
ShaderRegister: reg,
RegisterSpace: 0,
},
},
ShaderVisibility: D3D12_SHADER_VISIBILITY_PIXEL,
};
let table = |range: &D3D12_DESCRIPTOR_RANGE| D3D12_ROOT_PARAMETER {
ParameterType: D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE,
Anonymous: D3D12_ROOT_PARAMETER_0 {
DescriptorTable: D3D12_ROOT_DESCRIPTOR_TABLE {
NumDescriptorRanges: 1,
pDescriptorRanges: range,
},
},
ShaderVisibility: D3D12_SHADER_VISIBILITY_PIXEL,
};
let params = [
root_cbv(0), root_srv(0), root_srv(1), root_srv(2), root_srv(3), table(&scene_range), table(&gbuffer_range), table(&rough_range), table(&cube_range), table(&pool_range), root_srv(8), root_srv(9), table(&probe_cube_range), root_cbv(4), ];
let linear_clamp = |reg: u32| 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 repeat = D3D12_STATIC_SAMPLER_DESC {
AddressU: D3D12_TEXTURE_ADDRESS_MODE_WRAP,
AddressV: D3D12_TEXTURE_ADDRESS_MODE_WRAP,
AddressW: D3D12_TEXTURE_ADDRESS_MODE_WRAP,
ShaderRegister: 2, ..linear_clamp(2)
};
let samplers = [linear_clamp(0), linear_clamp(1), repeat, linear_clamp(3)];
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, "rt reflections root sig")
}
fn create_rt_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] = HDR_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 rt reflections PSO: {e}"))
}
pub(in crate::directx) struct RtReflectionsResources {
pub(in crate::directx) settings: RtReflectionSettings,
pub(in crate::directx) output: ID3D12Resource,
output_rtv: D3D12_CPU_DESCRIPTOR_HANDLE,
pub(in crate::directx) output_srv_gpu: D3D12_GPU_DESCRIPTOR_HANDLE,
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, D3D12_GPU_DESCRIPTOR_HANDLE),
}
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,
) -> Result<Self, String> {
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 output = create_rt_target(device, width, height, 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 = create_buffer(
alloc,
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| format!("map rt params ubo: {e}"))?;
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 flat_pso = dump_on_err(
info_queue,
create_rt_pso(device, &root_sig, &shaders.vs, &shaders.flat_ps),
)?;
let textured_pso = dump_on_err(
info_queue,
create_rt_pso(device, &root_sig, &shaders.vs, &shaders.textured_ps),
)?;
Ok(Self {
settings,
output,
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: D3D12_GPU_DESCRIPTOR_HANDLE,
) -> Result<(), String> {
let srv_cpu = D3D12_CPU_DESCRIPTOR_HANDLE {
ptr: srv_cpu_base.ptr + (self.output_srv_gpu.ptr - srv_gpu_base.ptr) as usize,
};
self.output = create_rt_target(device, width, height, 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>,
) -> Result<RebuiltRtPipelines, String> {
let shaders = compile_rt_shaders(hot_reload)?;
let flat_pso = dump_on_err(
info_queue,
create_rt_pso(device, &rt.root_sig, &shaders.vs, &shaders.flat_ps),
)?;
let textured_pso = dump_on_err(
info_queue,
create_rt_pso(device, &rt.root_sig, &shaders.vs, &shaders.textured_ps),
)?;
Ok(RebuiltRtPipelines {
flat_pso,
textured_pso,
})
}
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.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.env_map.prefilter_mip_count as f32,
});
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 = self.extent.render_width;
let h = self.extent.render_height;
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.geometry.vertex_buffer));
cmd.SetGraphicsRootShaderResourceView(3, com::gpu_va(&self.geometry.index_buffer));
cmd.SetGraphicsRootShaderResourceView(4, accel.geom_table_gva());
cmd.SetGraphicsRootDescriptorTable(5, self.hdr.srv_gpu);
cmd.SetGraphicsRootDescriptorTable(6, gbuffer.normal_depth_srv_gpu);
cmd.SetGraphicsRootDescriptorTable(7, gbuffer.roughness_srv_gpu);
cmd.SetGraphicsRootDescriptorTable(8, self.prefilter_cube_srv_gpu());
if textured {
cmd.SetGraphicsRootDescriptorTable(
9,
self.cull.bindless_pool_gpu[self.current_frame],
);
}
cmd.SetGraphicsRootShaderResourceView(10, accel.deformed_verts_gva());
cmd.SetGraphicsRootShaderResourceView(11, accel.skinned_index_gva());
cmd.SetGraphicsRootDescriptorTable(12, self.probe_cube_table_gpu());
cmd.SetGraphicsRootConstantBufferView(13, com::gpu_va(&self.probe.set_cbvs[frame_idx]));
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, reflection_srv);
}
}