use concinnity_core::gfx::transform::IDENTITY;
use std::cell::RefCell;
use windows::Win32::Foundation::RECT;
use windows::Win32::Graphics::Direct3D12::*;
use windows::Win32::Graphics::Dxgi::Common::*;
use crate::directx::allocator::{DeviceAllocator, PooledBuffer};
use crate::directx::com;
use crate::directx::context::{DxContext, FRAMES, align256, dump_on_err};
use crate::directx::pipeline::serialize_and_create_root_sig;
use crate::directx::slang_builtins;
use crate::directx::slang_builtins::SlangCompile;
use crate::directx::texture::{
create_buffer, create_main_depth_texture, write_format_rtv, write_format_srv,
};
pub(crate) const GBUFFER_NORMAL_DEPTH_FORMAT: DXGI_FORMAT = DXGI_FORMAT_R16G16B16A16_FLOAT;
pub(crate) const GBUFFER_ROUGHNESS_FORMAT: DXGI_FORMAT = DXGI_FORMAT_R8_UNORM;
pub(crate) const GBUFFER_VELOCITY_FORMAT: DXGI_FORMAT = DXGI_FORMAT_R16G16_FLOAT;
pub(in crate::directx) const GBUFFER_ROUGHNESS_CLEAR: [f32; 4] = [1.0, 0.0, 0.0, 0.0];
const GBUFFER_VIEW_UBO_SIZE: u64 = 256;
pub(in crate::directx) use concinnity_core::render::uniforms::GBufferView;
fn create_gbuffer_pso(
device: &ID3D12Device,
root_sig: &ID3D12RootSignature,
vs: &[u8],
ps: &[u8],
layout: &[D3D12_INPUT_ELEMENT_DESC],
) -> 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(),
},
InputLayout: D3D12_INPUT_LAYOUT_DESC {
pInputElementDescs: layout.as_ptr(),
NumElements: layout.len() as u32,
},
PrimitiveTopologyType: D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE,
NumRenderTargets: 3,
RTVFormats: {
let mut a = [DXGI_FORMAT_UNKNOWN; 8];
a[0] = GBUFFER_NORMAL_DEPTH_FORMAT;
a[1] = GBUFFER_ROUGHNESS_FORMAT;
a[2] = GBUFFER_VELOCITY_FORMAT;
a
},
DSVFormat: DXGI_FORMAT_D32_FLOAT,
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: true.into(),
..Default::default()
},
DepthStencilState: D3D12_DEPTH_STENCIL_DESC {
DepthEnable: true.into(),
DepthWriteMask: D3D12_DEPTH_WRITE_MASK_ALL,
DepthFunc: D3D12_COMPARISON_FUNC_LESS,
StencilEnable: false.into(),
..Default::default()
},
BlendState: D3D12_BLEND_DESC {
RenderTarget: {
let mut arr = [D3D12_RENDER_TARGET_BLEND_DESC::default(); 8];
let mt = D3D12_RENDER_TARGET_BLEND_DESC {
BlendEnable: false.into(),
RenderTargetWriteMask: D3D12_COLOR_WRITE_ENABLE_ALL.0 as u8,
..Default::default()
};
arr[0] = mt;
arr[1] = mt;
arr[2] = mt;
arr
},
..Default::default()
},
..Default::default()
};
unsafe { crate::directx::pso_library::create_graphics(device, &pso_desc) }
.map_err(|e| format!("create gbuffer prepass PSO: {e}"))
}
fn gbuffer_bindless_input_layout() -> Vec<D3D12_INPUT_ELEMENT_DESC> {
vec![
D3D12_INPUT_ELEMENT_DESC {
SemanticName: windows::core::s!("POSITION"),
SemanticIndex: 0,
Format: DXGI_FORMAT_R32G32B32_FLOAT,
InputSlot: 0,
AlignedByteOffset: 0,
InputSlotClass: D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA,
InstanceDataStepRate: 0,
},
D3D12_INPUT_ELEMENT_DESC {
SemanticName: windows::core::s!("NORMAL"),
SemanticIndex: 0,
Format: DXGI_FORMAT_R32G32B32_FLOAT,
InputSlot: 0,
AlignedByteOffset: 12,
InputSlotClass: D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA,
InstanceDataStepRate: 0,
},
D3D12_INPUT_ELEMENT_DESC {
SemanticName: windows::core::s!("COLOR"),
SemanticIndex: 0,
Format: DXGI_FORMAT_R32G32B32_FLOAT,
InputSlot: 0,
AlignedByteOffset: 36,
InputSlotClass: D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA,
InstanceDataStepRate: 0,
},
D3D12_INPUT_ELEMENT_DESC {
SemanticName: windows::core::s!("PREVPOSITION"),
SemanticIndex: 0,
Format: DXGI_FORMAT_R32G32B32_FLOAT,
InputSlot: 1,
AlignedByteOffset: 0,
InputSlotClass: D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA,
InstanceDataStepRate: 0,
},
]
}
fn create_gbuffer_bindless_root_signature(
device: &ID3D12Device,
) -> Result<ID3D12RootSignature, String> {
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: 1,
},
},
ShaderVisibility: D3D12_SHADER_VISIBILITY_VERTEX,
},
D3D12_ROOT_PARAMETER {
ParameterType: D3D12_ROOT_PARAMETER_TYPE_CBV,
Anonymous: D3D12_ROOT_PARAMETER_0 {
Descriptor: D3D12_ROOT_DESCRIPTOR {
ShaderRegister: 1,
RegisterSpace: 0,
},
},
ShaderVisibility: D3D12_SHADER_VISIBILITY_VERTEX,
},
D3D12_ROOT_PARAMETER {
ParameterType: D3D12_ROOT_PARAMETER_TYPE_SRV,
Anonymous: D3D12_ROOT_PARAMETER_0 {
Descriptor: D3D12_ROOT_DESCRIPTOR {
ShaderRegister: 0,
RegisterSpace: 0,
},
},
ShaderVisibility: D3D12_SHADER_VISIBILITY_VERTEX,
},
D3D12_ROOT_PARAMETER {
ParameterType: D3D12_ROOT_PARAMETER_TYPE_SRV,
Anonymous: D3D12_ROOT_PARAMETER_0 {
Descriptor: D3D12_ROOT_DESCRIPTOR {
ShaderRegister: 1,
RegisterSpace: 0,
},
},
ShaderVisibility: D3D12_SHADER_VISIBILITY_VERTEX,
},
D3D12_ROOT_PARAMETER {
ParameterType: D3D12_ROOT_PARAMETER_TYPE_SRV,
Anonymous: D3D12_ROOT_PARAMETER_0 {
Descriptor: D3D12_ROOT_DESCRIPTOR {
ShaderRegister: 2,
RegisterSpace: 0,
},
},
ShaderVisibility: D3D12_SHADER_VISIBILITY_VERTEX,
},
];
serialize_and_create_root_sig(device, ¶ms, "gbuffer bindless root sig")
}
const MODEL_HISTORY_THREADGROUP: u32 = 64;
fn uav_barrier(resource: &ID3D12Resource) -> D3D12_RESOURCE_BARRIER {
D3D12_RESOURCE_BARRIER {
Type: D3D12_RESOURCE_BARRIER_TYPE_UAV,
Flags: D3D12_RESOURCE_BARRIER_FLAG_NONE,
Anonymous: D3D12_RESOURCE_BARRIER_0 {
UAV: std::mem::ManuallyDrop::new(D3D12_RESOURCE_UAV_BARRIER {
pResource: com::borrowed(resource),
}),
},
}
}
fn create_model_history_root_signature(
device: &ID3D12Device,
) -> Result<ID3D12RootSignature, String> {
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: 4,
},
},
ShaderVisibility: D3D12_SHADER_VISIBILITY_ALL,
},
D3D12_ROOT_PARAMETER {
ParameterType: D3D12_ROOT_PARAMETER_TYPE_SRV,
Anonymous: D3D12_ROOT_PARAMETER_0 {
Descriptor: D3D12_ROOT_DESCRIPTOR {
ShaderRegister: 0,
RegisterSpace: 0,
},
},
ShaderVisibility: D3D12_SHADER_VISIBILITY_ALL,
},
D3D12_ROOT_PARAMETER {
ParameterType: D3D12_ROOT_PARAMETER_TYPE_UAV,
Anonymous: D3D12_ROOT_PARAMETER_0 {
Descriptor: D3D12_ROOT_DESCRIPTOR {
ShaderRegister: 0,
RegisterSpace: 0,
},
},
ShaderVisibility: D3D12_SHADER_VISIBILITY_ALL,
},
];
serialize_and_create_root_sig(device, ¶ms, "model history root sig")
}
pub(in crate::directx) fn build_model_history(
device: &ID3D12Device,
info_queue: Option<&ID3D12InfoQueue>,
hot_reload: bool,
) -> Result<(ID3D12RootSignature, ID3D12PipelineState), String> {
let cs = slang_builtins::MODEL_HISTORY.compile(hot_reload)?;
let root_sig = dump_on_err(info_queue, create_model_history_root_signature(device))?;
let pso = dump_on_err(
info_queue,
crate::directx::cull::create_cull_pso(device, &root_sig, &cs),
)?;
Ok((root_sig, pso))
}
type GbufferBindlessPipeline = (
ID3D12RootSignature,
ID3D12PipelineState,
ID3D12CommandSignature,
);
pub(in crate::directx) fn build_gbuffer_bindless(
device: &ID3D12Device,
info_queue: Option<&ID3D12InfoQueue>,
hot_reload: bool,
) -> Result<GbufferBindlessPipeline, String> {
let vs = slang_builtins::GBUFFER_BINDLESS_VERT.compile(hot_reload)?;
let ps = slang_builtins::GBUFFER_BINDLESS_FRAG.compile(hot_reload)?;
let root_sig = dump_on_err(info_queue, create_gbuffer_bindless_root_signature(device))?;
let layout = gbuffer_bindless_input_layout();
let pso = dump_on_err(
info_queue,
create_gbuffer_pso(device, &root_sig, &vs, &ps, &layout),
)?;
let cmd_sig = dump_on_err(
info_queue,
crate::directx::cull::create_cull_command_signature(device, &root_sig),
)?;
Ok((root_sig, pso, cmd_sig))
}
#[derive(Clone, Copy)]
pub(in crate::directx) struct GbufferSlots {
pub normal_depth_rtv: D3D12_CPU_DESCRIPTOR_HANDLE,
pub normal_depth_srv: (D3D12_CPU_DESCRIPTOR_HANDLE, D3D12_GPU_DESCRIPTOR_HANDLE),
pub roughness_rtv: D3D12_CPU_DESCRIPTOR_HANDLE,
pub roughness_srv: (D3D12_CPU_DESCRIPTOR_HANDLE, D3D12_GPU_DESCRIPTOR_HANDLE),
pub velocity_rtv: D3D12_CPU_DESCRIPTOR_HANDLE,
pub velocity_srv: (D3D12_CPU_DESCRIPTOR_HANDLE, D3D12_GPU_DESCRIPTOR_HANDLE),
pub depth_dsv: D3D12_CPU_DESCRIPTOR_HANDLE,
}
pub(in crate::directx) struct GbufferResources {
pub(in crate::directx) normal_depth: ID3D12Resource,
pub(in crate::directx) normal_depth_rtv: D3D12_CPU_DESCRIPTOR_HANDLE,
pub(in crate::directx) normal_depth_srv_gpu: D3D12_GPU_DESCRIPTOR_HANDLE,
pub(in crate::directx) roughness: ID3D12Resource,
pub(in crate::directx) roughness_rtv: D3D12_CPU_DESCRIPTOR_HANDLE,
pub(in crate::directx) roughness_srv_gpu: D3D12_GPU_DESCRIPTOR_HANDLE,
pub(in crate::directx) velocity: ID3D12Resource,
pub(in crate::directx) velocity_rtv: D3D12_CPU_DESCRIPTOR_HANDLE,
pub(in crate::directx) velocity_srv_gpu: D3D12_GPU_DESCRIPTOR_HANDLE,
pub(in crate::directx) depth: ID3D12Resource,
pub(in crate::directx) depth_dsv: D3D12_CPU_DESCRIPTOR_HANDLE,
pub(in crate::directx) view_ubo_resources: Vec<PooledBuffer>,
pub(in crate::directx) view_ubo_ptrs: Vec<*mut u8>,
pub(in crate::directx) prev_view_proj: RefCell<[[f32; 4]; 4]>,
}
#[derive(Clone, Copy)]
pub(in crate::directx) struct GbufferDeviceCtx<'a> {
pub alloc: &'a DeviceAllocator,
}
#[derive(Clone)]
pub(in crate::directx) struct GbufferPooled {
pub normal_depth: ID3D12Resource,
pub roughness: ID3D12Resource,
pub velocity: ID3D12Resource,
}
#[derive(Clone, Copy)]
pub(in crate::directx) struct GbufferExtent {
pub width: u32,
pub height: u32,
}
struct GbufferViewSlots {
normal_depth: (D3D12_CPU_DESCRIPTOR_HANDLE, D3D12_CPU_DESCRIPTOR_HANDLE),
roughness: (D3D12_CPU_DESCRIPTOR_HANDLE, D3D12_CPU_DESCRIPTOR_HANDLE),
velocity: (D3D12_CPU_DESCRIPTOR_HANDLE, D3D12_CPU_DESCRIPTOR_HANDLE),
}
fn write_pooled_views(
device: &ID3D12Device,
slots: GbufferViewSlots,
pooled: &GbufferPooled,
) -> (ID3D12Resource, ID3D12Resource, ID3D12Resource) {
for (res, (rtv, srv), format) in [
(
&pooled.normal_depth,
slots.normal_depth,
GBUFFER_NORMAL_DEPTH_FORMAT,
),
(&pooled.roughness, slots.roughness, GBUFFER_ROUGHNESS_FORMAT),
(&pooled.velocity, slots.velocity, GBUFFER_VELOCITY_FORMAT),
] {
write_format_rtv(device, res, rtv, format);
write_format_srv(device, res, srv, format);
}
(
pooled.normal_depth.clone(),
pooled.roughness.clone(),
pooled.velocity.clone(),
)
}
impl GbufferResources {
pub(in crate::directx) fn new(
ctx: GbufferDeviceCtx,
extent: GbufferExtent,
slots: GbufferSlots,
pooled: &GbufferPooled,
) -> Result<Self, String> {
let GbufferDeviceCtx { alloc } = ctx;
let device = alloc.device();
let GbufferExtent { width, height } = extent;
let (normal_depth, roughness, velocity) = write_pooled_views(
device,
GbufferViewSlots {
normal_depth: (slots.normal_depth_rtv, slots.normal_depth_srv.0),
roughness: (slots.roughness_rtv, slots.roughness_srv.0),
velocity: (slots.velocity_rtv, slots.velocity_srv.0),
},
pooled,
);
let depth = create_main_depth_texture(device, width, height, slots.depth_dsv, 1, true)?;
let view_size = align256(GBUFFER_VIEW_UBO_SIZE);
let mut view_ubo_resources: Vec<PooledBuffer> = Vec::with_capacity(FRAMES);
let mut view_ubo_ptrs: Vec<*mut u8> = Vec::with_capacity(FRAMES);
for _ in 0..FRAMES {
let buf = create_buffer(
alloc,
view_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 gbuffer view ubo: {e}"))?;
view_ubo_ptrs.push(ptr as *mut u8);
view_ubo_resources.push(buf);
}
Ok(Self {
normal_depth,
normal_depth_rtv: slots.normal_depth_rtv,
normal_depth_srv_gpu: slots.normal_depth_srv.1,
roughness,
roughness_rtv: slots.roughness_rtv,
roughness_srv_gpu: slots.roughness_srv.1,
velocity,
velocity_rtv: slots.velocity_rtv,
velocity_srv_gpu: slots.velocity_srv.1,
depth,
depth_dsv: slots.depth_dsv,
view_ubo_resources,
view_ubo_ptrs,
prev_view_proj: RefCell::new(IDENTITY),
})
}
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,
pooled: &GbufferPooled,
) -> Result<(), String> {
self.repoint_pooled(device, srv_cpu_base, srv_gpu_base, pooled);
self.depth = create_main_depth_texture(device, width, height, self.depth_dsv, 1, true)?;
Ok(())
}
pub(in crate::directx) fn repoint_pooled(
&mut self,
device: &ID3D12Device,
srv_cpu_base: D3D12_CPU_DESCRIPTOR_HANDLE,
srv_gpu_base: D3D12_GPU_DESCRIPTOR_HANDLE,
pooled: &GbufferPooled,
) {
let srv_cpu = |gpu: D3D12_GPU_DESCRIPTOR_HANDLE| D3D12_CPU_DESCRIPTOR_HANDLE {
ptr: srv_cpu_base.ptr + (gpu.ptr - srv_gpu_base.ptr) as usize,
};
let (normal_depth, roughness, velocity) = write_pooled_views(
device,
GbufferViewSlots {
normal_depth: (self.normal_depth_rtv, srv_cpu(self.normal_depth_srv_gpu)),
roughness: (self.roughness_rtv, srv_cpu(self.roughness_srv_gpu)),
velocity: (self.velocity_rtv, srv_cpu(self.velocity_srv_gpu)),
},
pooled,
);
self.normal_depth = normal_depth;
self.roughness = roughness;
self.velocity = velocity;
}
}
pub(in crate::directx) struct GbufferPrepassView {
pub jittered_vp: [[f32; 4]; 4],
pub cur_vp: [[f32; 4]; 4],
}
impl DxContext {
pub(in crate::directx) fn encode_gbuffer_prepass(
&self,
cmd: &ID3D12GraphicsCommandList,
frame_idx: usize,
view: GbufferPrepassView,
velocity_active: bool,
) {
let GbufferPrepassView {
jittered_vp,
cur_vp,
} = view;
let gb = match &self.gbuffer {
Some(g) => g,
None => return,
};
let prev_vp = if velocity_active {
*gb.prev_view_proj.borrow()
} else {
cur_vp
};
let view_uni = GBufferView {
jittered_vp,
cur_vp,
prev_vp,
view: self.view.matrix,
};
unsafe {
std::ptr::copy_nonoverlapping(
&view_uni as *const GBufferView as *const u8,
gb.view_ubo_ptrs[frame_idx],
std::mem::size_of::<GBufferView>(),
);
}
let view_gva = com::gpu_va(&gb.view_ubo_resources[frame_idx]);
let w = self.extent.render_width;
let h = self.extent.render_height;
let rtvs = [gb.normal_depth_rtv, gb.roughness_rtv, gb.velocity_rtv];
unsafe {
cmd.OMSetRenderTargets(3, Some(rtvs.as_ptr()), false, Some(&gb.depth_dsv));
cmd.ClearRenderTargetView(gb.normal_depth_rtv, &[0.0_f32; 4], None);
cmd.ClearRenderTargetView(gb.roughness_rtv, &GBUFFER_ROUGHNESS_CLEAR, None);
cmd.ClearRenderTargetView(gb.velocity_rtv, &[0.0_f32; 4], None);
cmd.ClearDepthStencilView(gb.depth_dsv, D3D12_CLEAR_FLAG_DEPTH, 1.0, 0, 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.IASetPrimitiveTopology(
windows::Win32::Graphics::Direct3D::D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST,
);
}
self.encode_gbuffer_prepass_gpu_driven(cmd, frame_idx, view_gva, velocity_active);
self.encode_model_history(cmd, frame_idx);
}
fn encode_model_history(&self, cmd: &ID3D12GraphicsCommandList, frame_idx: usize) {
let (Some(root_sig), Some(pso), true) = (
self.cull.model_history_root_sig.as_ref(),
self.cull.model_history_pso.as_ref(),
frame_idx < self.cull.prev_model_buffers.len(),
) else {
return;
};
let records = self.cull_count();
if records == 0 {
return;
}
let slots = match self
.cull
.model_history_prime
.swap(false, std::sync::atomic::Ordering::Relaxed)
{
true => 0..self.cull.prev_model_buffers.len(),
false => frame_idx..frame_idx + 1,
};
let params = concinnity_core::render::uniforms::ModelHistoryParams {
record_count: records as u32,
_pad: [0; 3],
};
let object_gva = com::gpu_va(&self.cull.object_buffer_resources[frame_idx]);
unsafe {
cmd.SetPipelineState(pso);
cmd.SetComputeRootSignature(root_sig);
cmd.SetComputeRoot32BitConstants(
0,
4,
¶ms as *const _ as *const std::ffi::c_void,
0,
);
cmd.SetComputeRootShaderResourceView(1, object_gva);
for slot in slots {
let history = &self.cull.prev_model_buffers[slot];
cmd.SetComputeRootUnorderedAccessView(2, com::gpu_va(history));
cmd.Dispatch((records as u32).div_ceil(MODEL_HISTORY_THREADGROUP), 1, 1);
cmd.ResourceBarrier(&[uav_barrier(history)]);
}
}
}
fn encode_gbuffer_prepass_gpu_driven(
&self,
cmd: &ID3D12GraphicsCommandList,
frame_idx: usize,
view_gva: u64,
velocity_active: bool,
) {
let (Some(pso), Some(root_sig), Some(cmd_sig), true) = (
self.cull.gbuffer_bindless_pso.as_ref(),
self.cull.gbuffer_bindless_root_sig.as_ref(),
self.cull.gbuffer_bindless_cmd_sig.as_ref(),
frame_idx < self.cull.prev_model_buffers.len(),
) else {
return;
};
let indirect = &self.cull.indirect_cmd_buffers[frame_idx];
let stride = crate::directx::cull::INDIRECT_COMMAND_STRIDE as usize;
let prefix = self.skinned_record_base();
let object_gva = com::gpu_va(&self.cull.object_buffer_resources[frame_idx]);
let frames = self.cull.prev_model_buffers.len();
let prev_model_gva =
com::gpu_va(&self.cull.prev_model_buffers[(frame_idx + frames - 1) % frames]);
let draw_args_gva = com::gpu_va(&self.cull.draw_args_buffer_resources[frame_idx]);
unsafe {
cmd.SetPipelineState(pso);
cmd.SetGraphicsRootSignature(root_sig);
cmd.IASetVertexBuffers(
0,
Some(&[
self.geometry.vertex_buffer_view,
self.geometry.vertex_buffer_view,
]),
);
cmd.IASetIndexBuffer(Some(&self.geometry.index_buffer_view));
cmd.SetGraphicsRootConstantBufferView(1, view_gva);
cmd.SetGraphicsRootShaderResourceView(2, object_gva);
cmd.SetGraphicsRootShaderResourceView(3, prev_model_gva);
cmd.SetGraphicsRootShaderResourceView(4, draw_args_gva);
cmd.ExecuteIndirect(
cmd_sig,
prefix as u32,
indirect,
0,
None::<&ID3D12Resource>,
0,
);
}
self.inc_draw_calls(1);
self.inc_draw_calls(self.execute_bucket_regions_shared_pso(
cmd,
cmd_sig,
indirect,
prefix as u32,
));
if self.draw.n_skinned > 0
&& let Some(cur_vbv) = self.skinned.deformed_vbvs.get(frame_idx)
{
let use_prev_pose = velocity_active
&& self
.skinned
.deformed_primed
.load(std::sync::atomic::Ordering::Relaxed);
let prev_frame_idx = if use_prev_pose {
(frame_idx + FRAMES - 1) % FRAMES
} else {
frame_idx
};
let prev_vbv = self
.skinned
.deformed_vbvs
.get(prev_frame_idx)
.copied()
.unwrap_or(*cur_vbv);
unsafe {
cmd.IASetVertexBuffers(0, Some(&[*cur_vbv, prev_vbv]));
cmd.IASetIndexBuffer(Some(&self.skinned.index_buffer_view));
cmd.ExecuteIndirect(
cmd_sig,
self.draw.n_skinned as u32,
indirect,
(prefix * stride) as u64,
None::<&ID3D12Resource>,
0,
);
}
self.inc_draw_calls(1);
self.skinned
.deformed_primed
.store(true, std::sync::atomic::Ordering::Relaxed);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn gb_view_uniforms_fits_ubo_allocation() {
assert!(std::mem::size_of::<GBufferView>() as u64 <= align256(GBUFFER_VIEW_UBO_SIZE));
}
}