use windows::Win32::Foundation::RECT;
use windows::Win32::Graphics::Direct3D12::*;
use windows::Win32::Graphics::Dxgi::Common::DXGI_FORMAT_R16_UINT;
use crate::gfx::render_types::{CompositeParams, TextDrawCall, TextVertex};
use crate::directx::context::DxContext;
use crate::directx::upload_ring::UPLOAD_ALIGN;
use concinnity_core::gfx::render_types::TextUniforms;
use crate::directx::graph_exec::{CompositeRenderTarget, CompositeResolution};
use crate::directx::pipeline::COMPOSITE_ROOT_CONSTANTS;
use crate::directx::texture::transition_barrier;
pub(crate) struct DxCompositeArgs {
back_buffer: ID3D12Resource,
back_buffer_rtv: D3D12_CPU_DESCRIPTOR_HANDLE,
scene_srv: D3D12_GPU_DESCRIPTOR_HANDLE,
width: u32,
height: u32,
frame_idx: usize,
channel_view: u32,
}
impl crate::gfx::fullscreen::CompositeEncoder for DxContext {
type Rec = ID3D12GraphicsCommandList;
type Args = DxCompositeArgs;
fn begin_composite(&self, cmd: &Self::Rec, args: &Self::Args) {
let to_rt = transition_barrier(
&args.back_buffer,
D3D12_RESOURCE_STATE_PRESENT,
D3D12_RESOURCE_STATE_RENDER_TARGET,
);
unsafe { cmd.ResourceBarrier(&[to_rt]) };
unsafe {
cmd.OMSetRenderTargets(1, Some(&args.back_buffer_rtv), false, None);
let vp = D3D12_VIEWPORT {
TopLeftX: 0.0,
TopLeftY: 0.0,
Width: args.width as f32,
Height: args.height as f32,
MinDepth: 0.0,
MaxDepth: 1.0,
};
cmd.RSSetViewports(&[vp]);
let scissor = RECT {
left: 0,
top: 0,
right: args.width as i32,
bottom: args.height as i32,
};
cmd.RSSetScissorRects(&[scissor]);
}
}
fn composite_draw(&self, cmd: &Self::Rec, args: &Self::Args) {
unsafe {
cmd.SetPipelineState(&self.composite.pso);
cmd.SetGraphicsRootSignature(&self.composite.root_sig);
cmd.SetDescriptorHeaps(&[
Some(self.descriptors.srv_heap.clone()),
Some(self.descriptors.sampler_heap.clone()),
]);
cmd.SetGraphicsRootDescriptorTable(0, args.scene_srv);
cmd.SetGraphicsRootDescriptorTable(1, self.bloom.mip_srv_gpus[0]);
let composite = CompositeParams {
post: self.post_process,
fade: self.view.scene_fade,
view_mode: args.channel_view,
far: self.view.far,
};
cmd.SetGraphicsRoot32BitConstants(
2,
COMPOSITE_ROOT_CONSTANTS,
&composite as *const CompositeParams as *const std::ffi::c_void,
0,
);
cmd.SetGraphicsRootDescriptorTable(3, self.color_lut.srv_gpu);
let (nd_srv, rough_srv) = match self.gbuffer.as_ref() {
Some(g) => (g.normal_depth_srv_gpu, g.roughness_srv_gpu),
None => (self.ssao.white_srv_gpu, self.ssao.white_srv_gpu),
};
cmd.SetGraphicsRootDescriptorTable(4, nd_srv);
cmd.SetGraphicsRootDescriptorTable(5, rough_srv);
cmd.SetGraphicsRootDescriptorTable(6, self.ssao_ao_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.inc_draw_calls(1);
}
fn begin_text(&self, cmd: &Self::Rec, args: &Self::Args) -> bool {
let Some(text_pso) = &self.text.pso else {
return false;
};
if self.descriptors.text_atlas_srv_gpus.is_empty() {
return false;
}
let text_push = TextUniforms {
win_width: args.width as f32,
win_height: args.height as f32,
_pad: [0.0; 2],
};
unsafe {
cmd.SetPipelineState(text_pso);
cmd.SetGraphicsRootSignature(&self.text.root_sig);
cmd.SetDescriptorHeaps(&[
Some(self.descriptors.srv_heap.clone()),
Some(self.descriptors.sampler_heap.clone()),
]);
cmd.SetGraphicsRoot32BitConstants(
0,
4,
&text_push as *const TextUniforms as *const std::ffi::c_void,
0,
);
cmd.SetGraphicsRootDescriptorTable(2, self.descriptors.text_sampler_gpu);
}
true
}
fn text_draw(
&self,
cmd: &Self::Rec,
args: &Self::Args,
call: &TextDrawCall,
) -> Result<(), String> {
if call.vertices.is_empty() || self.descriptors.text_atlas_srv_gpus.is_empty() {
return Ok(());
}
let ui = (args.width as f32, args.height as f32);
let scissor = match call.clip_rect {
Some(clip) => {
match crate::gfx::fullscreen::clip_rect_to_scissor(
clip,
ui,
(args.width, args.height),
) {
None => return Ok(()),
Some((x, y, w, h)) => RECT {
left: x,
top: y,
right: x + w as i32,
bottom: y + h as i32,
},
}
}
None => RECT {
left: 0,
top: 0,
right: args.width as i32,
bottom: args.height as i32,
},
};
unsafe { cmd.RSSetScissorRects(&[scissor]) };
let atlas_idx = call
.atlas_slot
.min(self.descriptors.text_atlas_srv_gpus.len() - 1);
let vert_bytes = bytemuck::cast_slice(&call.vertices);
let idx_bytes = bytemuck::cast_slice(&call.indices);
let vert_va = self.text.upload.push(args.frame_idx, vert_bytes)?;
let idx_va = self.text.upload.push(args.frame_idx, idx_bytes)?;
let vbv = D3D12_VERTEX_BUFFER_VIEW {
BufferLocation: vert_va,
SizeInBytes: vert_bytes.len() as u32,
StrideInBytes: std::mem::size_of::<TextVertex>() as u32,
};
let ibv = D3D12_INDEX_BUFFER_VIEW {
BufferLocation: idx_va,
SizeInBytes: idx_bytes.len() as u32,
Format: DXGI_FORMAT_R16_UINT,
};
unsafe {
cmd.SetGraphicsRootDescriptorTable(1, self.descriptors.text_atlas_srv_gpus[atlas_idx]);
cmd.IASetVertexBuffers(0, Some(&[vbv]));
cmd.IASetIndexBuffer(Some(&ibv));
cmd.DrawIndexedInstanced(call.indices.len() as u32, 1, 0, 0, 0);
}
self.inc_draw_calls(1);
Ok(())
}
fn end_composite(&self, cmd: &Self::Rec, args: &Self::Args) {
let to_present = transition_barrier(
&args.back_buffer,
D3D12_RESOURCE_STATE_RENDER_TARGET,
D3D12_RESOURCE_STATE_PRESENT,
);
unsafe { cmd.ResourceBarrier(&[to_present]) };
}
}
impl DxContext {
pub(in crate::directx) fn encode_composite_and_text(
&self,
cmd: &ID3D12GraphicsCommandList,
frame_idx: usize,
render_target: CompositeRenderTarget<'_>,
text_calls: &[TextDrawCall],
scene_srv: D3D12_GPU_DESCRIPTOR_HANDLE,
resolution: CompositeResolution,
) -> Result<(), String> {
let CompositeRenderTarget {
back_buffer,
back_buffer_rtv,
} = render_target;
let CompositeResolution { width, height } = resolution;
let text_bytes = crate::gfx::fullscreen::text_upload_bytes(text_calls, UPLOAD_ALIGN);
self.text
.upload
.reserve(&self.alloc, frame_idx, text_bytes)?;
let args = DxCompositeArgs {
back_buffer: back_buffer.clone(),
back_buffer_rtv,
scene_srv,
width,
height,
frame_idx,
channel_view: if self.view.mode.is_gbuffer_channel() {
self.view.mode as u32
} else {
0
},
};
crate::gfx::fullscreen::encode_composite_chain(self, cmd, &args, text_calls)
}
}