use concinnity_core::gfx::render_types;
use concinnity_core::render::depth::DEPTH_CLEAR;
use concinnity_core::render::error::{RenderError, RenderResult};
use concinnity_core::render::planar_reflection::PixelRect;
use concinnity_core::render::probe_bake::{
CAPTURE_FACES, ProbeBake, ProbeBakeDevice, capture_ring_slot,
};
use concinnity_core::render::probe_book::ProbeBook;
use concinnity_core::render::reflection_probe::{self, PrefilterPlan, ProbePlacement};
use windows::Win32::Graphics::Direct3D::D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
use windows::Win32::Graphics::Direct3D12::*;
use windows::Win32::Graphics::Dxgi::Common::*;
use super::allocator::{DeviceAllocator, PooledBuffer};
use super::com;
use super::context::{DxContext, FRAMES};
use super::error::map_hresult;
use super::light_cull::ClusterGrid;
use super::probe_prefilter::PrefilterGpu;
use super::texture::{
HDR_FORMAT, create_hdr_color_target, create_hdr_resolve_target, transition_barrier,
};
use crate::directx::depth::optimized_clear;
use crate::directx::descriptor_slot::DescriptorTables;
use crate::directx::descriptor_slot::SrvSlot;
pub(super) const PLAN: PrefilterPlan = PrefilterPlan::RUNTIME;
const PROBE_FACE_SIZE: u32 = PLAN.face_size();
pub(crate) struct RenderingBake {
eye: [f32; 3],
capture_distance: Option<f32>,
sample_count: u32,
color: ID3D12Resource,
_depth: ID3D12Resource,
resolve: Option<ID3D12Resource>,
_rtv_heap: ID3D12DescriptorHeap,
_dsv_heap: ID3D12DescriptorHeap,
rtv: D3D12_CPU_DESCRIPTOR_HANDLE,
dsv: D3D12_CPU_DESCRIPTOR_HANDLE,
_view_cbvs: Vec<PooledBuffer>,
view_gvas: Vec<u64>,
light_gva: u64,
shadow_gva: u64,
_light_cbv: PooledBuffer,
_shadow_cbv: PooledBuffer,
prefilter: PrefilterGpu,
cmd_allocs: Vec<ID3D12CommandAllocator>,
cmd_lists: Vec<ID3D12GraphicsCommandList>,
last_fence_value: u64,
}
pub(crate) struct PrefilteringBake {
gpu: PrefilterGpu,
cmd_allocs: Vec<ID3D12CommandAllocator>,
cmd_lists: Vec<ID3D12GraphicsCommandList>,
last_fence_value: u64,
}
pub(super) type DxProbeBake = ProbeBake<RenderingBake, PrefilteringBake>;
#[derive(Clone, Copy)]
pub(in crate::directx) struct FaceTargets {
pub rtv: D3D12_CPU_DESCRIPTOR_HANDLE,
pub dsv: D3D12_CPU_DESCRIPTOR_HANDLE,
}
#[derive(Clone, Copy)]
pub(in crate::directx) struct FaceUniforms<'a> {
pub view_gva: u64,
pub light_gva: u64,
pub shadow_ubo_gva: u64,
pub clusters: Option<ClusterGrid<'a>>,
}
#[derive(Clone, Copy)]
pub(in crate::directx) struct IndirectDraw<'a> {
pub indirect: &'a ID3D12Resource,
pub indirect_offset: u32,
pub object_gva: u64,
pub material_params_gva: u64,
}
#[derive(Clone, Copy)]
pub(in crate::directx) struct FaceExtent {
pub width: u32,
pub height: u32,
pub area: Option<PixelRect>,
}
impl DxContext {
pub(super) fn set_reflection_probes(&mut self, declared: &[reflection_probe::ProbePlacement]) {
let placements = reflection_probe::resolve_placements(
declared,
self.state.draw.objects.iter().map(|o| (o.bb_min, o.bb_max)),
);
let placed = self.with_probe_bake(|bake, ctx| bake.place(ctx, placements));
crate::probe_report::report_probe_placement(placed);
}
fn bake_ring_slot(&self) -> usize {
capture_ring_slot(FRAMES)
}
pub(in crate::directx) fn probe_cube_table_gpu(&self) -> SrvSlot {
SrvSlot::at(
&self.descriptors.srv_heap,
self.descriptors.srv_descriptor_size,
self.descriptors.layout.probe_cubes_srv_slot,
)
}
pub(super) fn bake_pending_probes(&mut self) {
let report = self.with_probe_bake(|bake, ctx| bake.advance(ctx, &()));
crate::probe_report::report_probe_bake(report);
}
fn with_probe_bake<R>(&mut self, f: impl FnOnce(&mut DxProbeBake, &mut Self) -> R) -> R {
let mut bake = std::mem::take(&mut self.probe.bake);
let out = f(&mut bake, self);
self.probe.bake = bake;
out
}
fn fence_reached(&self, fence_value: u64) -> bool {
let completed = unsafe { self.frame_sync.fence.GetCompletedValue() };
completed >= fence_value
}
fn start_probe_capture(
&mut self,
index: usize,
placement: ProbePlacement,
) -> RenderResult<RenderingBake> {
let eye = placement.position;
let slot = self.bake_ring_slot();
self.build_object_buffer(slot);
if let Some(params) = self.cull.material_params.as_mut() {
params.upload(slot);
}
self.build_draw_args_buffer(
slot,
eye,
concinnity_core::render::model_history::HistoryMode::Untracked,
);
let alloc = &self.hw.alloc;
let device = &self.hw.device;
let sample_count = self.targets.hdr.msaa_samples.max(1);
let size = PROBE_FACE_SIZE;
let rtv_heap = create_rtv_heap(device)?;
let dsv_heap = create_dsv_heap(device)?;
let rtv = unsafe { rtv_heap.GetCPUDescriptorHandleForHeapStart() };
let dsv = unsafe { dsv_heap.GetCPUDescriptorHandleForHeapStart() };
let color = create_hdr_color_target(
device,
size,
size,
sample_count,
rtv,
self.state.view.clear_color,
)?;
let depth = create_bake_depth(device, size, sample_count, dsv)?;
let resolve = if sample_count > 1 {
Some(create_hdr_resolve_target(device, size, size)?)
} else {
None
};
let light_bytes = unsafe {
std::slice::from_raw_parts(
&self.uniforms.light_uniforms as *const render_types::LightUniforms as *const u8,
std::mem::size_of::<render_types::LightUniforms>(),
)
};
let (light_cbv, light_gva) = make_snapshot_cbv(alloc, light_bytes)?;
let shadow_bytes = unsafe {
std::slice::from_raw_parts(
&self.shadow.uniforms as *const render_types::ShadowUniforms as *const u8,
std::mem::size_of::<render_types::ShadowUniforms>(),
)
};
let (shadow_cbv, shadow_gva) = make_snapshot_cbv(alloc, shadow_bytes)?;
let prefilter_mip_count = self.scene.env_map.prefilter_mip_count as f32;
let mut view_cbvs = Vec::with_capacity(CAPTURE_FACES);
let mut view_gvas = Vec::with_capacity(CAPTURE_FACES);
for face in 0..CAPTURE_FACES {
let vp = reflection_probe::face_view_projection(eye, face);
let view_mat = reflection_probe::face_view_matrix(eye, face);
let view = super::draw::ViewUniforms {
vp,
view: view_mat,
elapsed: 0.0,
reflections_enabled: 0.0,
cam_pos: [eye[0], eye[1], eye[2]],
prefilter_mip_count,
shade_mode: 0.0,
ambient_occlusion: 0.0,
sky_rot: self.state.view.sky_rot,
};
let cbv = alloc.alloc_buffer(
256,
D3D12_HEAP_TYPE_UPLOAD,
D3D12_RESOURCE_STATE_GENERIC_READ,
)?;
let mut ptr = std::ptr::null_mut::<std::ffi::c_void>();
unsafe { cbv.Map(0, None, Some(&mut ptr)) }
.map_err(|e| map_hresult(e.code(), "probe: map view cbv"))?;
unsafe {
std::ptr::copy_nonoverlapping(
&view as *const super::draw::ViewUniforms as *const u8,
ptr as *mut u8,
std::mem::size_of::<super::draw::ViewUniforms>(),
);
}
view_gvas.push(com::gpu_va(&cbv));
view_cbvs.push(cbv);
}
let cubes = self
.probe
.gpu
.cubes
.as_ref()
.ok_or_else(|| RenderError::Other("probe: no cube array for a placement".into()))?;
let prefilter = PrefilterGpu::new(self, &PLAN, cubes, index)?;
Ok(RenderingBake {
eye,
capture_distance: placement.capture_distance,
sample_count,
color,
_depth: depth,
resolve,
_rtv_heap: rtv_heap,
_dsv_heap: dsv_heap,
rtv,
dsv,
_view_cbvs: view_cbvs,
view_gvas,
light_gva,
shadow_gva,
_light_cbv: light_cbv,
_shadow_cbv: shadow_cbv,
prefilter,
cmd_allocs: Vec::with_capacity(CAPTURE_FACES),
cmd_lists: Vec::with_capacity(CAPTURE_FACES),
last_fence_value: 0,
})
}
fn record_probe_face(&self, bake: &mut RenderingBake, face: usize) -> RenderResult<()> {
let slot = self.bake_ring_slot();
let (eye, view_gva, light_gva, shadow_gva) = (
bake.eye,
bake.view_gvas[face],
bake.light_gva,
bake.shadow_gva,
);
let frustum = reflection_probe::face_frustum(eye, face, bake.capture_distance);
let alloc: ID3D12CommandAllocator = unsafe {
self.hw
.device
.CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT)
}
.map_err(|e| map_hresult(e.code(), "probe: face allocator"))?;
let cmd: ID3D12GraphicsCommandList = unsafe {
self.hw
.device
.CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, &alloc, None)
}
.map_err(|e| map_hresult(e.code(), "probe: face cmd list"))?;
bake.cmd_allocs.push(alloc);
bake.cmd_lists.push(cmd.clone());
self.encode_probe_cull(&cmd, slot, &frustum, eye);
let (rtv, dsv) = (bake.rtv, bake.dsv);
let indirect = &self.cull.indirect_cmd_buffers[slot];
let object_gva = com::gpu_va(&self.cull.object_buffer_resources[slot]);
self.encode_main_into_face(
&cmd,
FaceTargets { rtv, dsv },
FaceUniforms {
view_gva,
light_gva,
shadow_ubo_gva: shadow_gva,
clusters: None,
},
IndirectDraw {
indirect,
indirect_offset: 0,
object_gva,
material_params_gva: self.material_params_gva(slot),
},
FaceExtent {
width: PROBE_FACE_SIZE,
height: PROBE_FACE_SIZE,
area: None,
},
);
self.copy_face_to_capture(&cmd, bake, face)?;
unsafe { cmd.Close() }.map_err(|e| map_hresult(e.code(), "probe: face close"))?;
let list: ID3D12CommandList = windows::core::Interface::cast(&cmd)
.map_err(|e| map_hresult(e.code(), "probe: face cast"))?;
unsafe { self.hw.command_queue.ExecuteCommandLists(&[Some(list)]) };
let fence_val = self.frame_sync.next_fence_value.get();
self.frame_sync.next_fence_value.set(fence_val + 1);
unsafe {
self.hw
.command_queue
.Signal(&self.frame_sync.fence, fence_val)
}
.map_err(|e| map_hresult(e.code(), "probe: face signal"))?;
bake.last_fence_value = fence_val;
Ok(())
}
fn copy_face_to_capture(
&self,
cmd: &ID3D12GraphicsCommandList,
bake: &RenderingBake,
face: usize,
) -> RenderResult<()> {
let sample_count = bake.sample_count;
let dst_subresource = face as u32 * bake.prefilter.mips();
let dst_loc = D3D12_TEXTURE_COPY_LOCATION {
pResource: com::borrowed(bake.prefilter.capture()),
Type: D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX,
Anonymous: D3D12_TEXTURE_COPY_LOCATION_0 {
SubresourceIndex: dst_subresource,
},
};
if sample_count > 1 {
let resolve = bake
.resolve
.as_ref()
.expect("a multisampled probe bake has a resolve image");
unsafe {
cmd.ResourceBarrier(&[
transition_barrier(
&bake.color,
D3D12_RESOURCE_STATE_RENDER_TARGET,
D3D12_RESOURCE_STATE_RESOLVE_SOURCE,
),
transition_barrier(
resolve,
D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE,
D3D12_RESOURCE_STATE_RESOLVE_DEST,
),
]);
cmd.ResolveSubresource(resolve, 0, &bake.color, 0, HDR_FORMAT);
cmd.ResourceBarrier(&[
transition_barrier(
resolve,
D3D12_RESOURCE_STATE_RESOLVE_DEST,
D3D12_RESOURCE_STATE_COPY_SOURCE,
),
transition_barrier(
&bake.color,
D3D12_RESOURCE_STATE_RESOLVE_SOURCE,
D3D12_RESOURCE_STATE_RENDER_TARGET,
),
]);
let src_loc = D3D12_TEXTURE_COPY_LOCATION {
pResource: com::borrowed(resolve),
Type: D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX,
Anonymous: D3D12_TEXTURE_COPY_LOCATION_0 {
SubresourceIndex: 0,
},
};
cmd.CopyTextureRegion(&dst_loc, 0, 0, 0, &src_loc, None);
cmd.ResourceBarrier(&[transition_barrier(
resolve,
D3D12_RESOURCE_STATE_COPY_SOURCE,
D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE,
)]);
}
} else {
unsafe {
cmd.ResourceBarrier(&[transition_barrier(
&bake.color,
D3D12_RESOURCE_STATE_RENDER_TARGET,
D3D12_RESOURCE_STATE_COPY_SOURCE,
)]);
let src_loc = D3D12_TEXTURE_COPY_LOCATION {
pResource: com::borrowed(&bake.color),
Type: D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX,
Anonymous: D3D12_TEXTURE_COPY_LOCATION_0 {
SubresourceIndex: 0,
},
};
cmd.CopyTextureRegion(&dst_loc, 0, 0, 0, &src_loc, None);
cmd.ResourceBarrier(&[transition_barrier(
&bake.color,
D3D12_RESOURCE_STATE_COPY_SOURCE,
D3D12_RESOURCE_STATE_RENDER_TARGET,
)]);
}
}
Ok(())
}
fn record_prefilter_mip(&self, bake: &mut PrefilteringBake, mip: u32) -> RenderResult<()> {
if mip == 0 {
return self.record_prefilter_step(bake, |ctx, cmd, bake| {
ctx.encode_probe_pyramid(cmd, &bake.gpu, &PLAN)
});
}
let last = mip + 1 == PLAN.mips();
self.record_prefilter_step(bake, |ctx, cmd, bake| {
ctx.encode_probe_ggx_mip(cmd, &PLAN, mip)?;
if last {
let barriers = ctx.probe.gpu.bound_cubes().cube_barriers(
bake.gpu.cube(),
D3D12_RESOURCE_STATE_UNORDERED_ACCESS,
super::probe_set::PROBE_CUBES_STATE,
);
unsafe { cmd.ResourceBarrier(&barriers) };
}
Ok(())
})
}
fn record_prefilter_step(
&self,
bake: &mut PrefilteringBake,
encode: impl FnOnce(&Self, &ID3D12GraphicsCommandList, &PrefilteringBake) -> RenderResult<()>,
) -> RenderResult<()> {
let alloc: ID3D12CommandAllocator = unsafe {
self.hw
.device
.CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT)
}
.map_err(|e| map_hresult(e.code(), "probe: convolve allocator"))?;
let cmd: ID3D12GraphicsCommandList = unsafe {
self.hw
.device
.CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, &alloc, None)
}
.map_err(|e| map_hresult(e.code(), "probe: convolve cmd list"))?;
bake.cmd_allocs.push(alloc);
bake.cmd_lists.push(cmd.clone());
unsafe {
cmd.SetDescriptorHeaps(&[Some(self.descriptors.srv_heap.clone())]);
}
encode(self, &cmd, bake)?;
unsafe { cmd.Close() }.map_err(|e| map_hresult(e.code(), "probe: convolve close"))?;
let list: ID3D12CommandList = windows::core::Interface::cast(&cmd)
.map_err(|e| map_hresult(e.code(), "probe: convolve cast"))?;
unsafe { self.hw.command_queue.ExecuteCommandLists(&[Some(list)]) };
let fence_val = self.frame_sync.next_fence_value.get();
self.frame_sync.next_fence_value.set(fence_val + 1);
unsafe {
self.hw
.command_queue
.Signal(&self.frame_sync.fence, fence_val)
}
.map_err(|e| map_hresult(e.code(), "probe: convolve signal"))?;
bake.last_fence_value = fence_val;
Ok(())
}
pub(in crate::directx) fn encode_main_into_face(
&self,
cmd: &ID3D12GraphicsCommandList,
targets: FaceTargets,
uniforms: FaceUniforms<'_>,
draw: IndirectDraw<'_>,
extent: FaceExtent,
) {
let FaceTargets { rtv, dsv } = targets;
let FaceUniforms {
view_gva,
light_gva,
shadow_ubo_gva,
clusters,
} = uniforms;
let (cluster_params_gva, cluster_list_gva) = match clusters {
Some(grid) => (grid.params_gva, com::gpu_va(grid.lists)),
None => (
self.cluster_params_gva(self.current_frame, false),
self.cluster_list_gva(),
),
};
let IndirectDraw {
indirect,
indirect_offset,
object_gva,
material_params_gva,
} = draw;
let FaceExtent {
width,
height,
area,
} = extent;
let scissor = match area {
Some(r) => windows::Win32::Foundation::RECT {
left: r.x as i32,
top: r.y as i32,
right: (r.x + r.width) as i32,
bottom: (r.y + r.height) as i32,
},
None => windows::Win32::Foundation::RECT {
left: 0,
top: 0,
right: width as i32,
bottom: height as i32,
},
};
let bindless_pso = self
.cull
.main_bindless_pso
.as_ref()
.expect("bindless PSO is live");
let bindless_root = self
.cull
.main_bindless_root_sig
.as_ref()
.expect("bindless root signature is live alongside its PSO");
let cull_sig = self
.cull
.cull_command_signature
.as_ref()
.expect("cull command signature is live alongside the bindless PSO");
let local_lights_gva = com::gpu_va(&self.uniforms.local_light_buffer);
unsafe {
cmd.OMSetRenderTargets(1, Some(&rtv), false, Some(&dsv));
cmd.ClearRenderTargetView(rtv, &self.state.view.clear_color, Some(&[scissor]));
cmd.ClearDepthStencilView(
dsv,
D3D12_CLEAR_FLAG_DEPTH,
DEPTH_CLEAR,
0,
Some(&[scissor]),
);
let vp = D3D12_VIEWPORT {
TopLeftX: 0.0,
TopLeftY: 0.0,
Width: width as f32,
Height: height as f32,
MinDepth: 0.0,
MaxDepth: 1.0,
};
cmd.RSSetViewports(&[vp]);
cmd.RSSetScissorRects(&[scissor]);
cmd.IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
cmd.IASetVertexBuffers(0, Some(&[self.scene.geometry.vertex_buffer_view]));
cmd.IASetIndexBuffer(Some(&self.scene.geometry.index_buffer_view));
cmd.SetDescriptorHeaps(&[
Some(self.descriptors.srv_heap.clone()),
Some(self.descriptors.sampler_heap.clone()),
]);
cmd.SetPipelineState(bindless_pso);
cmd.SetGraphicsRootSignature(bindless_root);
cmd.SetGraphicsRootConstantBufferView(1, view_gva);
cmd.SetGraphicsRootConstantBufferView(2, light_gva);
cmd.SetGraphicsRootConstantBufferView(3, shadow_ubo_gva);
cmd.set_graphics_srv_table(4, self.shadow.srv_gpu);
cmd.set_graphics_srv_table(5, self.cull.bindless_pool_gpu[self.current_frame]);
cmd.set_graphics_sampler_table(6, self.descriptors.shadow_sampler_gpu);
cmd.set_graphics_sampler_table(7, self.descriptors.linear_sampler_gpu);
cmd.SetGraphicsRootShaderResourceView(8, object_gva);
cmd.SetGraphicsRootShaderResourceView(
super::material_params::MATERIAL_PARAMS_ROOT_PARAM,
material_params_gva,
);
cmd.SetGraphicsRootShaderResourceView(12, local_lights_gva);
cmd.SetGraphicsRootConstantBufferView(13, cluster_params_gva);
cmd.SetGraphicsRootShaderResourceView(14, cluster_list_gva);
self.bind_local_light_tables(cmd, super::draw::LocalLightParams::BINDLESS);
cmd.set_graphics_srv_table(9, self.ssao_ao_srv_gpu());
self.bind_main_probe_set(
cmd,
com::gpu_va(&self.uniforms.probe_set_empty_cbv),
self.probe.gpu.stand_in_records.gpu_va(),
);
cmd.ExecuteIndirect(
cull_sig,
self.skinned_record_base() as u32,
indirect,
indirect_offset as u64,
None::<&ID3D12Resource>,
0,
);
}
self.inc_draw_calls(1);
if self.draws_sky(concinnity_core::gfx::view_modes::ViewMode::Lit) {
self.encode_sky(cmd, view_gva);
}
}
}
impl ProbeBakeDevice for DxContext {
type Capture = RenderingBake;
type Prefilter = PrefilteringBake;
type Frame<'f> = ();
fn book(&mut self) -> &mut ProbeBook {
&mut self.probe.book
}
fn capture_supported(&self) -> bool {
let slot = self.bake_ring_slot();
self.cull.main_bindless_pso.is_some()
&& self.cull.cull_kernels.is_some()
&& self.cull.object_buffer_resources.len() > slot
&& self.cull.draw_args_buffer_resources.len() > slot
&& self.cull.indirect_cmd_buffers.len() > slot
&& self.probe.prefilter.is_some()
}
fn capture_ready(&self, prefilter_in_flight: bool) -> bool {
self.cull_count() > 0 && !prefilter_in_flight
}
fn reserve_cubes(&mut self, count: usize) -> RenderResult<()> {
self.reserve_probe_cubes(&PLAN, count)
}
fn start_capture(
&mut self,
_frame: &(),
index: usize,
placement: ProbePlacement,
) -> RenderResult<RenderingBake> {
self.start_probe_capture(index, placement)
}
fn render_face(
&mut self,
_frame: &(),
capture: &mut RenderingBake,
face: usize,
) -> RenderResult<()> {
self.record_probe_face(capture, face)
}
fn capture_retired(&self, capture: &RenderingBake) -> bool {
self.fence_reached(capture.last_fence_value)
}
fn begin_prefilter(
&mut self,
_index: usize,
capture: RenderingBake,
) -> RenderResult<PrefilteringBake> {
Ok(PrefilteringBake {
gpu: capture.prefilter,
cmd_allocs: Vec::with_capacity(PLAN.mips() as usize),
cmd_lists: Vec::with_capacity(PLAN.mips() as usize),
last_fence_value: 0,
})
}
fn prefilter_mip(&mut self, prefilter: &mut PrefilteringBake, mip: u32) -> RenderResult<()> {
self.record_prefilter_mip(prefilter, mip)
}
fn prefilter_retired(&self, prefilter: &PrefilteringBake) -> bool {
self.fence_reached(prefilter.last_fence_value)
}
fn finish_prefilter(&mut self, prefilter: PrefilteringBake) {
drop(prefilter);
}
fn abandon(&mut self, capture: Option<RenderingBake>, prefilter: Option<PrefilteringBake>) {
self.wait_idle();
drop((capture, prefilter));
}
}
fn make_snapshot_cbv(alloc: &DeviceAllocator, bytes: &[u8]) -> RenderResult<(PooledBuffer, u64)> {
let size = (((bytes.len() as u64) + 255) & !255).max(256);
let cbv = alloc.alloc_buffer(
size,
D3D12_HEAP_TYPE_UPLOAD,
D3D12_RESOURCE_STATE_GENERIC_READ,
)?;
let mut ptr = std::ptr::null_mut::<std::ffi::c_void>();
unsafe { cbv.Map(0, None, Some(&mut ptr)) }
.map_err(|e| map_hresult(e.code(), "probe: map snapshot cbv"))?;
unsafe {
std::ptr::copy_nonoverlapping(bytes.as_ptr(), ptr as *mut u8, bytes.len());
}
let gva = com::gpu_va(&cbv);
Ok((cbv, gva))
}
fn create_rtv_heap(device: &ID3D12Device) -> RenderResult<ID3D12DescriptorHeap> {
let desc = D3D12_DESCRIPTOR_HEAP_DESC {
Type: D3D12_DESCRIPTOR_HEAP_TYPE_RTV,
NumDescriptors: 1,
Flags: D3D12_DESCRIPTOR_HEAP_FLAG_NONE,
NodeMask: 0,
};
unsafe { device.CreateDescriptorHeap(&desc) }
.map_err(|e| map_hresult(e.code(), "probe: rtv heap"))
}
fn create_dsv_heap(device: &ID3D12Device) -> RenderResult<ID3D12DescriptorHeap> {
let desc = D3D12_DESCRIPTOR_HEAP_DESC {
Type: D3D12_DESCRIPTOR_HEAP_TYPE_DSV,
NumDescriptors: 1,
Flags: D3D12_DESCRIPTOR_HEAP_FLAG_NONE,
NodeMask: 0,
};
unsafe { device.CreateDescriptorHeap(&desc) }
.map_err(|e| map_hresult(e.code(), "probe: dsv heap"))
}
fn create_bake_depth(
device: &ID3D12Device,
size: u32,
sample_count: u32,
dsv_cpu: D3D12_CPU_DESCRIPTOR_HANDLE,
) -> RenderResult<ID3D12Resource> {
let heap_props = D3D12_HEAP_PROPERTIES {
Type: D3D12_HEAP_TYPE_DEFAULT,
..Default::default()
};
let clear_value = optimized_clear();
let desc = D3D12_RESOURCE_DESC {
Dimension: D3D12_RESOURCE_DIMENSION_TEXTURE2D,
Width: size as u64,
Height: size,
DepthOrArraySize: 1,
MipLevels: 1,
Format: DXGI_FORMAT_D32_FLOAT,
SampleDesc: DXGI_SAMPLE_DESC {
Count: sample_count,
Quality: 0,
},
Flags: D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL,
..Default::default()
};
let mut tex_opt: Option<ID3D12Resource> = None;
unsafe {
device.CreateCommittedResource(
&heap_props,
D3D12_HEAP_FLAG_NONE,
&desc,
D3D12_RESOURCE_STATE_DEPTH_WRITE,
Some(&clear_value),
&mut tex_opt,
)
}
.map_err(|e| map_hresult(e.code(), "probe: create face depth"))?;
let texture = tex_opt
.ok_or_else(|| RenderError::Other("probe: create face depth returned None".to_string()))?;
let dsv_desc = D3D12_DEPTH_STENCIL_VIEW_DESC {
Format: DXGI_FORMAT_D32_FLOAT,
ViewDimension: if sample_count > 1 {
D3D12_DSV_DIMENSION_TEXTURE2DMS
} else {
D3D12_DSV_DIMENSION_TEXTURE2D
},
Flags: D3D12_DSV_FLAG_NONE,
..Default::default()
};
unsafe { device.CreateDepthStencilView(&texture, Some(&dsv_desc), dsv_cpu) };
Ok(texture)
}