use windows::Win32::Foundation::RECT;
use windows::Win32::Graphics::Direct3D12::*;
use windows::Win32::Graphics::Dxgi::Common::*;
use super::allocator::{DeviceAllocator, PooledBuffer};
use super::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};
use crate::directx::upload_ring::{UPLOAD_ALIGN, UploadRing, align_up};
use crate::gfx::render_types::LineVertex;
const OCCLUDED_ALPHA: f32 = 0.12;
pub(in crate::directx) use concinnity_core::render::uniforms::LineView;
pub(in crate::directx) struct LineState {
pub resources: Option<LineResources>,
pub build_failed: bool,
}
impl LineState {
pub(in crate::directx) fn empty() -> Self {
Self {
resources: None,
build_failed: false,
}
}
}
pub(in crate::directx) struct LineResources {
root_sig: ID3D12RootSignature,
pub(in crate::directx) pso: ID3D12PipelineState,
view_ubo_resources: Vec<PooledBuffer>,
view_ubo_ptrs: Vec<*mut u8>,
vertices: UploadRing,
depth_srv_gpu: D3D12_GPU_DESCRIPTOR_HANDLE,
}
impl LineResources {
fn new(
alloc: &DeviceAllocator,
msaa_samples: u32,
depth_srv_gpu: D3D12_GPU_DESCRIPTOR_HANDLE,
info_queue: Option<&ID3D12InfoQueue>,
hot_reload: bool,
) -> Result<Self, String> {
let device = alloc.device();
let (vs, ps) = compile_line_shaders(msaa_samples, hot_reload)?;
let root_sig = dump_on_err(info_queue, create_line_root_signature(device))?;
let pso = dump_on_err(info_queue, create_line_pso(device, &root_sig, &vs, &ps))?;
let view_size = align256(std::mem::size_of::<LineView>() as u64);
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 line view ubo: {e}"))?;
view_ubo_ptrs.push(ptr as *mut u8);
view_ubo_resources.push(buf);
}
Ok(Self {
root_sig,
pso,
view_ubo_resources,
view_ubo_ptrs,
vertices: UploadRing::new(FRAMES),
depth_srv_gpu,
})
}
}
fn compile_line_shaders(msaa_samples: u32, hot_reload: bool) -> Result<(Vec<u8>, Vec<u8>), String> {
let frag = if msaa_samples > 1 {
&slang_builtins::LINE_FRAG_MSAA
} else {
&slang_builtins::LINE_FRAG
};
let vs = slang_builtins::LINE_VERT.compile(hot_reload)?;
let ps = frag.compile(hot_reload)?;
Ok((vs, ps))
}
pub(in crate::directx) fn rebuild_line_pso(
device: &ID3D12Device,
lines: &LineResources,
msaa_samples: u32,
hot_reload: bool,
info_queue: Option<&ID3D12InfoQueue>,
) -> Result<ID3D12PipelineState, String> {
let (vs, ps) = compile_line_shaders(msaa_samples, hot_reload)?;
dump_on_err(
info_queue,
create_line_pso(device, &lines.root_sig, &vs, &ps),
)
}
fn create_line_root_signature(device: &ID3D12Device) -> Result<ID3D12RootSignature, String> {
let depth_range = D3D12_DESCRIPTOR_RANGE {
RangeType: D3D12_DESCRIPTOR_RANGE_TYPE_SRV,
NumDescriptors: 1,
BaseShaderRegister: 0, RegisterSpace: 0,
OffsetInDescriptorsFromTableStart: D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND,
};
let params = [
D3D12_ROOT_PARAMETER {
ParameterType: D3D12_ROOT_PARAMETER_TYPE_CBV,
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_DESCRIPTOR_TABLE,
Anonymous: D3D12_ROOT_PARAMETER_0 {
DescriptorTable: D3D12_ROOT_DESCRIPTOR_TABLE {
NumDescriptorRanges: 1,
pDescriptorRanges: &depth_range,
},
},
ShaderVisibility: D3D12_SHADER_VISIBILITY_PIXEL,
},
];
let desc = D3D12_ROOT_SIGNATURE_DESC {
NumParameters: params.len() as u32,
pParameters: params.as_ptr(),
NumStaticSamplers: 0,
pStaticSamplers: std::ptr::null(),
Flags: D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT,
};
serialize_desc_and_create(device, &desc, "line root sig")
}
fn line_input_layout() -> [D3D12_INPUT_ELEMENT_DESC; 3] {
[
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!("TEXCOORD"),
SemanticIndex: 0,
Format: DXGI_FORMAT_R32_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_R32G32B32A32_FLOAT,
InputSlot: 0,
AlignedByteOffset: 16,
InputSlotClass: D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA,
InstanceDataStepRate: 0,
},
]
}
fn create_line_pso(
device: &ID3D12Device,
root_sig: &ID3D12RootSignature,
vs: &[u8],
ps: &[u8],
) -> Result<ID3D12PipelineState, String> {
let layout = line_input_layout();
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: 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: true.into(),
SrcBlend: D3D12_BLEND_SRC_ALPHA,
DestBlend: D3D12_BLEND_INV_SRC_ALPHA,
BlendOp: D3D12_BLEND_OP_ADD,
SrcBlendAlpha: D3D12_BLEND_SRC_ALPHA,
DestBlendAlpha: D3D12_BLEND_INV_SRC_ALPHA,
BlendOpAlpha: D3D12_BLEND_OP_ADD,
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 line PSO: {e}"))
}
impl DxContext {
pub(in crate::directx) fn ensure_line_pipeline(&mut self, has_lines: bool) {
if !has_lines || self.lines.resources.is_some() || self.lines.build_failed {
return;
}
let info_queue = self.diagnostics.info_queue.clone();
match LineResources::new(
&self.alloc,
self.hdr.msaa_samples,
self.main_depth_srv_gpu,
info_queue.as_ref(),
self.hot_reload.enabled,
) {
Ok(r) => self.lines.resources = Some(r),
Err(e) => {
self.lines.build_failed = true;
tracing::error!("line pipeline: {}", e);
}
}
}
pub(in crate::directx) fn encode_lines(
&self,
cmd: &ID3D12GraphicsCommandList,
frame_idx: usize,
vp: [[f32; 4]; 4],
vertices: &[LineVertex],
) -> Result<(), String> {
let Some(lines) = self.lines.resources.as_ref() else {
return Ok(());
};
if vertices.is_empty() {
return Ok(());
}
let view_uni = LineView {
vp,
occluded_alpha: OCCLUDED_ALPHA,
_pad: [0.0; 3],
};
unsafe {
std::ptr::copy_nonoverlapping(
&view_uni as *const LineView as *const u8,
lines.view_ubo_ptrs[frame_idx],
std::mem::size_of::<LineView>(),
);
}
let view_gva = com::gpu_va(&lines.view_ubo_resources[frame_idx]);
let vertex_bytes: &[u8] = bytemuck::cast_slice(vertices);
lines.vertices.reserve(
&self.alloc,
frame_idx,
align_up(vertex_bytes.len() as u64, UPLOAD_ALIGN),
)?;
let vertex_gva = lines.vertices.push(frame_idx, vertex_bytes)?;
let vertex_view = D3D12_VERTEX_BUFFER_VIEW {
BufferLocation: vertex_gva,
SizeInBytes: vertex_bytes.len() as u32,
StrideInBytes: std::mem::size_of::<LineVertex>() as u32,
};
let scene_rtv = self.hdr_scene_rtv();
let w = self.extent.render_width;
let h = self.extent.render_height;
unsafe {
cmd.OMSetRenderTargets(1, Some(&scene_rtv), false, None);
cmd.RSSetViewports(&[D3D12_VIEWPORT {
TopLeftX: 0.0,
TopLeftY: 0.0,
Width: w as f32,
Height: h as f32,
MinDepth: 0.0,
MaxDepth: 1.0,
}]);
cmd.RSSetScissorRects(&[RECT {
left: 0,
top: 0,
right: w as i32,
bottom: h as i32,
}]);
cmd.IASetPrimitiveTopology(
windows::Win32::Graphics::Direct3D::D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST,
);
cmd.IASetVertexBuffers(0, Some(&[vertex_view]));
cmd.SetPipelineState(&lines.pso);
cmd.SetGraphicsRootSignature(&lines.root_sig);
cmd.SetDescriptorHeaps(&[Some(self.descriptors.srv_heap.clone())]);
cmd.SetGraphicsRootConstantBufferView(0, view_gva);
cmd.SetGraphicsRootDescriptorTable(1, lines.depth_srv_gpu);
cmd.DrawInstanced(vertices.len() as u32, 1, 0, 0);
}
self.inc_draw_calls(1);
Ok(())
}
}