use concinnity_core::gfx::render_types::{CompositeParams, TextDrawCall, TextUniforms, TextVertex};
use concinnity_core::render::error::RenderResult;
use concinnity_core::render::fullscreen;
use concinnity_core::render::fullscreen::TextBindCache;
use windows::Win32::Foundation::RECT;
use windows::Win32::Graphics::Direct3D12::*;
use windows::Win32::Graphics::Dxgi::Common::DXGI_FORMAT_R16_UINT;
use crate::directx::context::DxContext;
use crate::directx::descriptor_slot::DescriptorTables;
use crate::directx::descriptor_slot::SrvSlot;
use crate::directx::graph_exec::{CompositeRenderTarget, CompositeResolution};
use crate::directx::root_constants::RootConstants;
use crate::directx::texture::transition_barrier;
use crate::directx::upload_ring::UPLOAD_ALIGN;
pub(crate) struct DxCompositeArgs {
back_buffer: ID3D12Resource,
back_buffer_rtv: D3D12_CPU_DESCRIPTOR_HANDLE,
scene_srv: SrvSlot,
width: u32,
height: u32,
frame_idx: usize,
channel_view: u32,
reactive_valid: bool,
}
#[derive(Clone, Copy)]
pub(in crate::directx) struct CompositeSources {
pub scene_srv: SrvSlot,
pub reactive_valid: bool,
}
impl 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]);
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]);
cmd.SetDescriptorHeaps(&[
Some(self.descriptors.srv_heap.clone()),
Some(self.descriptors.sampler_heap.clone()),
]);
}
}
fn composite_draw(&self, cmd: &Self::Rec, args: &Self::Args) {
unsafe {
cmd.SetPipelineState(&self.composite.pso);
cmd.SetGraphicsRootSignature(&self.composite.root_sig);
cmd.set_graphics_srv_table(0, args.scene_srv);
let bloom = self
.bloom
.as_ref()
.map_or(self.ssao.white_srv_gpu, |b| b.top_srv_gpu());
cmd.set_graphics_srv_table(1, bloom);
let (depth_near, depth_far) = CompositeParams::depth_view_range(
self.state.view.near,
self.state.view.view_distance,
);
let composite = CompositeParams {
post: self.post_process,
fade: self.state.view.scene_fade,
view_mode: args.channel_view,
depth_near,
depth_far,
reactive_valid: if args.reactive_valid { 1.0 } else { 0.0 },
};
cmd.set_graphics_root_constants(2, &composite);
cmd.set_graphics_srv_table(3, self.scene.color_lut.srv_gpu);
let (nd_srv, rough_srv, motion_srv) = match self.gbuffer.as_ref() {
Some(g) => (
g.normal_depth_srv_gpu,
g.roughness_srv_gpu,
g.velocity_srv_gpu,
),
None => (
self.ssao.white_srv_gpu,
self.ssao.white_srv_gpu,
self.ssao.white_srv_gpu,
),
};
cmd.set_graphics_srv_table(4, nd_srv);
cmd.set_graphics_srv_table(5, rough_srv);
cmd.set_graphics_srv_table(6, self.ssao_ao_srv_gpu());
cmd.set_graphics_srv_table(7, motion_srv);
cmd.set_graphics_srv_table(8, self.targets.reactive_mask.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.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.set_graphics_root_constants(0, &text_push);
cmd.set_graphics_sampler_table(2, self.descriptors.text_sampler_gpu);
}
true
}
fn text_draw(
&self,
cmd: &Self::Rec,
args: &Self::Args,
_idx: usize,
call: &TextDrawCall,
binds: &mut TextBindCache,
) -> RenderResult<()> {
if call.vertices.is_empty() || self.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 fullscreen::clip_rect_to_scissor(clip, ui, (args.width, args.height)) {
None => return Ok(()),
Some(rect) => rect,
}
}
None => (0, 0, args.width, args.height),
};
let atlas_idx = call.atlas_slot.min(self.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 {
if binds.scissor_changed(scissor) {
let (x, y, w, h) = scissor;
cmd.RSSetScissorRects(&[RECT {
left: x,
top: y,
right: x + w as i32,
bottom: y + h as i32,
}]);
}
if binds.atlas_changed(atlas_idx) {
cmd.set_graphics_srv_table(1, self.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],
sources: CompositeSources,
resolution: CompositeResolution,
) -> RenderResult<()> {
let CompositeRenderTarget {
back_buffer,
back_buffer_rtv,
} = render_target;
let CompositeResolution { width, height } = resolution;
let text_bytes = fullscreen::text_upload_bytes(text_calls, UPLOAD_ALIGN);
self.text
.upload
.reserve(&self.hw.alloc, frame_idx, text_bytes)?;
let args = DxCompositeArgs {
back_buffer: back_buffer.clone(),
back_buffer_rtv,
scene_srv: sources.scene_srv,
width,
height,
frame_idx,
channel_view: if self.state.view.mode.is_gbuffer_channel() {
self.state.view.mode as u32
} else {
0
},
reactive_valid: sources.reactive_valid,
};
fullscreen::encode_composite_chain(self, cmd, &args, text_calls)
}
}