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::{FRAMES, align256, dump_on_err};
use crate::directx::cull::{
INDIRECT_COMMAND_STRIDE, compile_cull_shader, compile_cull_shader_phase2,
compile_cull_shader_shadow, create_cull_command_signature, create_cull_pso,
create_cull_root_signature,
};
use crate::directx::pipeline::{
compile_composite_shaders, compile_text_shaders, create_composite_pso,
create_composite_root_signature, create_text_pso, create_text_root_signature,
main_input_layout, serialize_and_create_root_sig,
};
use crate::directx::slang_builtins;
use crate::directx::slang_builtins::SlangCompile;
use crate::directx::texture::{HDR_FORMAT, create_buffer, create_uav_buffer};
use crate::gfx::shadow_bias;
pub(super) struct CompiledShaders {
pub shadow_vs: Option<Vec<u8>>,
pub text_vs: Vec<u8>,
pub text_ps: Vec<u8>,
}
pub(in crate::directx) fn world_entry(
world: &concinnity_core::components::ShaderPrograms,
entry: &str,
hot_reload: bool,
) -> Result<Vec<u8>, String> {
let req = crate::surface_source::Request {
platform: concinnity_core::platform::Platform::Hlsl,
pool_size: 0,
probe_count: concinnity_core::render::uniforms::MAX_PROBES,
hot_reload,
};
crate::surface_source::artifact(world, entry, &req).map(|c| c.into_owned())
}
pub(super) fn compile_all_shaders(hot_reload: bool) -> Result<CompiledShaders, String> {
let shadow_vs = Some(slang_builtins::SHADOW_VERT.compile(hot_reload)?);
let (text_vs, text_ps) = compile_text_shaders(hot_reload)?;
Ok(CompiledShaders {
shadow_vs,
text_vs,
text_ps,
})
}
pub(in crate::directx) fn compile_main_bindless_shaders(
hot_reload: bool,
) -> Result<(Vec<u8>, Vec<u8>), String> {
let vs = slang_builtins::MAIN_BINDLESS_VERT.compile(hot_reload)?;
let ps = slang_builtins::MAIN_BINDLESS_FRAG.compile(hot_reload)?;
Ok((vs, ps))
}
pub(in crate::directx) fn compile_shadow_bindless_vs(hot_reload: bool) -> Result<Vec<u8>, String> {
slang_builtins::SHADOW_BINDLESS_VERT.compile(hot_reload)
}
fn create_main_bindless_root_signature(
device: &ID3D12Device,
) -> Result<ID3D12RootSignature, String> {
let shadow_srv_ranges = [
D3D12_DESCRIPTOR_RANGE {
RangeType: D3D12_DESCRIPTOR_RANGE_TYPE_SRV,
NumDescriptors: 1,
BaseShaderRegister: 0, RegisterSpace: 0,
OffsetInDescriptorsFromTableStart: D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND,
},
D3D12_DESCRIPTOR_RANGE {
RangeType: D3D12_DESCRIPTOR_RANGE_TYPE_SRV,
NumDescriptors: 2,
BaseShaderRegister: 5, RegisterSpace: 0,
OffsetInDescriptorsFromTableStart: D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND,
},
];
let pool_srv_range = D3D12_DESCRIPTOR_RANGE {
RangeType: D3D12_DESCRIPTOR_RANGE_TYPE_SRV,
NumDescriptors: u32::MAX, BaseShaderRegister: 0, RegisterSpace: 1, OffsetInDescriptorsFromTableStart: 0,
};
let shadow_sampler_range = D3D12_DESCRIPTOR_RANGE {
RangeType: D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER,
NumDescriptors: 1,
BaseShaderRegister: 0, RegisterSpace: 0,
OffsetInDescriptorsFromTableStart: D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND,
};
let linear_cube_sampler_range = D3D12_DESCRIPTOR_RANGE {
RangeType: D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER,
NumDescriptors: 2,
BaseShaderRegister: 1, RegisterSpace: 0,
OffsetInDescriptorsFromTableStart: D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND,
};
let ssao_srv_range = D3D12_DESCRIPTOR_RANGE {
RangeType: D3D12_DESCRIPTOR_RANGE_TYPE_SRV,
NumDescriptors: 1,
BaseShaderRegister: 4, RegisterSpace: 0,
OffsetInDescriptorsFromTableStart: D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND,
};
let probe_cube_range = D3D12_DESCRIPTOR_RANGE {
RangeType: D3D12_DESCRIPTOR_RANGE_TYPE_SRV,
NumDescriptors: concinnity_core::render::uniforms::MAX_PROBES as u32,
BaseShaderRegister: 7, RegisterSpace: 0,
OffsetInDescriptorsFromTableStart: D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND,
};
let spot_shadow_srv_range = D3D12_DESCRIPTOR_RANGE {
RangeType: D3D12_DESCRIPTOR_RANGE_TYPE_SRV,
NumDescriptors: 1,
BaseShaderRegister: 7 + concinnity_core::render::uniforms::MAX_PROBES as u32 + 1, RegisterSpace: 0,
OffsetInDescriptorsFromTableStart: D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND,
};
let ltc_srv_range = D3D12_DESCRIPTOR_RANGE {
RangeType: D3D12_DESCRIPTOR_RANGE_TYPE_SRV,
NumDescriptors: 2,
BaseShaderRegister: 7 + concinnity_core::render::uniforms::MAX_PROBES as u32 + 3, RegisterSpace: 0,
OffsetInDescriptorsFromTableStart: D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND,
};
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_ALL,
},
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_ALL,
},
D3D12_ROOT_PARAMETER {
ParameterType: D3D12_ROOT_PARAMETER_TYPE_CBV,
Anonymous: D3D12_ROOT_PARAMETER_0 {
Descriptor: D3D12_ROOT_DESCRIPTOR {
ShaderRegister: 2,
RegisterSpace: 0,
},
},
ShaderVisibility: D3D12_SHADER_VISIBILITY_PIXEL,
},
D3D12_ROOT_PARAMETER {
ParameterType: D3D12_ROOT_PARAMETER_TYPE_CBV,
Anonymous: D3D12_ROOT_PARAMETER_0 {
Descriptor: D3D12_ROOT_DESCRIPTOR {
ShaderRegister: 3,
RegisterSpace: 0,
},
},
ShaderVisibility: D3D12_SHADER_VISIBILITY_PIXEL,
},
D3D12_ROOT_PARAMETER {
ParameterType: D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE,
Anonymous: D3D12_ROOT_PARAMETER_0 {
DescriptorTable: D3D12_ROOT_DESCRIPTOR_TABLE {
NumDescriptorRanges: shadow_srv_ranges.len() as u32,
pDescriptorRanges: shadow_srv_ranges.as_ptr(),
},
},
ShaderVisibility: D3D12_SHADER_VISIBILITY_PIXEL,
},
D3D12_ROOT_PARAMETER {
ParameterType: D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE,
Anonymous: D3D12_ROOT_PARAMETER_0 {
DescriptorTable: D3D12_ROOT_DESCRIPTOR_TABLE {
NumDescriptorRanges: 1,
pDescriptorRanges: &pool_srv_range,
},
},
ShaderVisibility: D3D12_SHADER_VISIBILITY_PIXEL,
},
D3D12_ROOT_PARAMETER {
ParameterType: D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE,
Anonymous: D3D12_ROOT_PARAMETER_0 {
DescriptorTable: D3D12_ROOT_DESCRIPTOR_TABLE {
NumDescriptorRanges: 1,
pDescriptorRanges: &shadow_sampler_range,
},
},
ShaderVisibility: D3D12_SHADER_VISIBILITY_PIXEL,
},
D3D12_ROOT_PARAMETER {
ParameterType: D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE,
Anonymous: D3D12_ROOT_PARAMETER_0 {
DescriptorTable: D3D12_ROOT_DESCRIPTOR_TABLE {
NumDescriptorRanges: 1,
pDescriptorRanges: &linear_cube_sampler_range,
},
},
ShaderVisibility: D3D12_SHADER_VISIBILITY_PIXEL,
},
D3D12_ROOT_PARAMETER {
ParameterType: D3D12_ROOT_PARAMETER_TYPE_SRV,
Anonymous: D3D12_ROOT_PARAMETER_0 {
Descriptor: D3D12_ROOT_DESCRIPTOR {
ShaderRegister: 3,
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: &ssao_srv_range,
},
},
ShaderVisibility: D3D12_SHADER_VISIBILITY_PIXEL,
},
D3D12_ROOT_PARAMETER {
ParameterType: D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE,
Anonymous: D3D12_ROOT_PARAMETER_0 {
DescriptorTable: D3D12_ROOT_DESCRIPTOR_TABLE {
NumDescriptorRanges: 1,
pDescriptorRanges: &probe_cube_range,
},
},
ShaderVisibility: D3D12_SHADER_VISIBILITY_PIXEL,
},
D3D12_ROOT_PARAMETER {
ParameterType: D3D12_ROOT_PARAMETER_TYPE_CBV,
Anonymous: D3D12_ROOT_PARAMETER_0 {
Descriptor: D3D12_ROOT_DESCRIPTOR {
ShaderRegister: 4, RegisterSpace: 0,
},
},
ShaderVisibility: D3D12_SHADER_VISIBILITY_PIXEL,
},
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_PIXEL,
},
D3D12_ROOT_PARAMETER {
ParameterType: D3D12_ROOT_PARAMETER_TYPE_CBV,
Anonymous: D3D12_ROOT_PARAMETER_0 {
Descriptor: D3D12_ROOT_DESCRIPTOR {
ShaderRegister: 5,
RegisterSpace: 0,
},
},
ShaderVisibility: D3D12_SHADER_VISIBILITY_PIXEL,
},
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_PIXEL,
},
D3D12_ROOT_PARAMETER {
ParameterType: D3D12_ROOT_PARAMETER_TYPE_SRV,
Anonymous: D3D12_ROOT_PARAMETER_0 {
Descriptor: D3D12_ROOT_DESCRIPTOR {
ShaderRegister: 7 + concinnity_core::render::uniforms::MAX_PROBES as u32, RegisterSpace: 0,
},
},
ShaderVisibility: D3D12_SHADER_VISIBILITY_PIXEL,
},
D3D12_ROOT_PARAMETER {
ParameterType: D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE,
Anonymous: D3D12_ROOT_PARAMETER_0 {
DescriptorTable: D3D12_ROOT_DESCRIPTOR_TABLE {
NumDescriptorRanges: 1,
pDescriptorRanges: &spot_shadow_srv_range,
},
},
ShaderVisibility: D3D12_SHADER_VISIBILITY_PIXEL,
},
D3D12_ROOT_PARAMETER {
ParameterType: D3D12_ROOT_PARAMETER_TYPE_SRV,
Anonymous: D3D12_ROOT_PARAMETER_0 {
Descriptor: D3D12_ROOT_DESCRIPTOR {
ShaderRegister: 7 + concinnity_core::render::uniforms::MAX_PROBES as u32 + 2, RegisterSpace: 0,
},
},
ShaderVisibility: D3D12_SHADER_VISIBILITY_PIXEL,
},
D3D12_ROOT_PARAMETER {
ParameterType: D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE,
Anonymous: D3D12_ROOT_PARAMETER_0 {
DescriptorTable: D3D12_ROOT_DESCRIPTOR_TABLE {
NumDescriptorRanges: 1,
pDescriptorRanges: <c_srv_range,
},
},
ShaderVisibility: D3D12_SHADER_VISIBILITY_PIXEL,
},
];
serialize_and_create_root_sig(device, ¶ms, "main bindless root sig")
}
pub(in crate::directx) fn create_shadow_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: 20,
},
},
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,
},
];
serialize_and_create_root_sig(device, ¶ms, "shadow root sig")
}
pub(in crate::directx) fn create_shadow_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_32BIT_CONSTANTS,
Anonymous: D3D12_ROOT_PARAMETER_0 {
Constants: D3D12_ROOT_CONSTANTS {
ShaderRegister: 2,
RegisterSpace: 0,
Num32BitValues: 1,
},
},
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,
},
];
serialize_and_create_root_sig(device, ¶ms, "shadow bindless root sig")
}
pub(in crate::directx) fn create_main_pso(
device: &ID3D12Device,
root_sig: &ID3D12RootSignature,
vs: &[u8],
ps: &[u8],
rtv_format: DXGI_FORMAT,
sample_count: u32,
) -> Result<ID3D12PipelineState, String> {
create_main_pso_filled(
device,
root_sig,
vs,
ps,
rtv_format,
sample_count,
D3D12_FILL_MODE_SOLID,
)
}
pub(in crate::directx) fn create_main_pso_wireframe(
device: &ID3D12Device,
root_sig: &ID3D12RootSignature,
vs: &[u8],
ps: &[u8],
rtv_format: DXGI_FORMAT,
sample_count: u32,
) -> Result<ID3D12PipelineState, String> {
create_main_pso_filled(
device,
root_sig,
vs,
ps,
rtv_format,
sample_count,
D3D12_FILL_MODE_WIREFRAME,
)
}
fn create_main_pso_filled(
device: &ID3D12Device,
root_sig: &ID3D12RootSignature,
vs: &[u8],
ps: &[u8],
rtv_format: DXGI_FORMAT,
sample_count: u32,
fill_mode: D3D12_FILL_MODE,
) -> Result<ID3D12PipelineState, String> {
let layout = main_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] = rtv_format;
a
},
DSVFormat: DXGI_FORMAT_D32_FLOAT,
SampleDesc: DXGI_SAMPLE_DESC {
Count: sample_count,
Quality: 0,
},
SampleMask: u32::MAX,
RasterizerState: D3D12_RASTERIZER_DESC {
FillMode: fill_mode,
CullMode: D3D12_CULL_MODE_NONE,
FrontCounterClockwise: true.into(),
DepthBias: 0,
DepthBiasClamp: 0.0,
SlopeScaledDepthBias: 0.0,
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];
arr[0] = D3D12_RENDER_TARGET_BLEND_DESC {
BlendEnable: false.into(),
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 main PSO: {e}"))
}
pub(in crate::directx) fn create_shadow_pso(
device: &ID3D12Device,
root_sig: &ID3D12RootSignature,
vs: &[u8],
) -> Result<ID3D12PipelineState, String> {
let layout = main_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(),
},
InputLayout: D3D12_INPUT_LAYOUT_DESC {
pInputElementDescs: layout.as_ptr(),
NumElements: layout.len() as u32,
},
PrimitiveTopologyType: D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE,
NumRenderTargets: 0,
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(),
DepthBias: shadow_bias::RASTER_CONSTANT as i32,
DepthBiasClamp: shadow_bias::RASTER_CLAMP,
SlopeScaledDepthBias: shadow_bias::RASTER_SLOPE,
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 {
..Default::default()
},
..Default::default()
};
unsafe { crate::directx::pso_library::create_graphics(device, &pso_desc) }
.map_err(|e| format!("create shadow PSO: {e}"))
}
pub(in crate::directx) struct BindlessMainShaders {
pub vs: Vec<u8>,
pub ps: Vec<u8>,
}
pub(in crate::directx) fn build_bucket_pipeline(
device: &ID3D12Device,
info_queue: Option<&ID3D12InfoQueue>,
targets: BucketPipelineTargets<'_>,
bucket: usize,
shader: crate::gfx::backend_init::WorldShader<'_>,
) -> Result<ID3D12PipelineState, String> {
let (vs, ps) = match shader.programs {
Some(programs) => (
world_entry(programs, "vertex_main_bindless", targets.hot_reload)?,
world_entry(programs, "fragment_main_bindless", targets.hot_reload)?,
),
None => (
targets.engine_default.vs.clone(),
targets.engine_default.ps.clone(),
),
};
if vs.is_empty() || ps.is_empty() {
return Err(format!(
"shader bucket {bucket} carries no vertex/fragment bytecode"
));
}
dump_on_err(
info_queue,
create_main_pso(
device,
targets.root_sig,
&vs,
&ps,
HDR_FORMAT,
targets.msaa_samples,
),
)
.map_err(|e| format!("shader bucket {bucket}: {e}"))
}
#[derive(Clone, Copy)]
pub(in crate::directx) struct BucketPipelineTargets<'a> {
pub root_sig: &'a ID3D12RootSignature,
pub msaa_samples: u32,
pub engine_default: &'a BindlessMainShaders,
pub hot_reload: bool,
}
fn build_world_pipeline_table(
device: &ID3D12Device,
info_queue: Option<&ID3D12InfoQueue>,
targets: BucketPipelineTargets<'_>,
bucket_shaders: &[crate::gfx::backend_init::WorldShader<'_>],
) -> Result<Vec<Option<ID3D12PipelineState>>, String> {
let mut table = Vec::with_capacity(bucket_shaders.len());
for (i, shader) in bucket_shaders.iter().enumerate() {
let bucket = i + 1;
if shader.deferred {
table.push(None);
continue;
}
table.push(Some(build_bucket_pipeline(
device, info_queue, targets, bucket, *shader,
)?));
}
Ok(table)
}
pub(super) struct MainPipelines {
pub main_bindless_root_sig: Option<ID3D12RootSignature>,
pub main_bindless_pso: Option<ID3D12PipelineState>,
pub world_pipelines: Vec<Option<ID3D12PipelineState>>,
pub bucket_stride: usize,
pub bindless_main_shaders: BindlessMainShaders,
pub object_buffer_resources: Vec<PooledBuffer>,
pub object_buffer_ptrs: Vec<*mut u8>,
pub cull_root_sig: Option<ID3D12RootSignature>,
pub cull_pso: Option<ID3D12PipelineState>,
pub cull_pso_phase2: Option<ID3D12PipelineState>,
pub cull_command_signature: Option<ID3D12CommandSignature>,
pub draw_args_buffer_resources: Vec<PooledBuffer>,
pub draw_args_buffer_ptrs: Vec<*mut u8>,
pub indirect_cmd_buffers: Vec<ID3D12Resource>,
pub cull_status_buffers: Vec<ID3D12Resource>,
pub indirect_cmd_buffers_2: Vec<ID3D12Resource>,
pub shadow_bindless_root_sig: Option<ID3D12RootSignature>,
pub shadow_bindless_pso: Option<ID3D12PipelineState>,
pub shadow_bindless_cmd_sig: Option<ID3D12CommandSignature>,
pub cull_pso_shadow: Option<ID3D12PipelineState>,
pub shadow_indirect_buffers: Vec<ID3D12Resource>,
pub shadow_cull_status_buffers: Vec<ID3D12Resource>,
pub gbuffer_bindless_root_sig: Option<ID3D12RootSignature>,
pub gbuffer_bindless_pso: Option<ID3D12PipelineState>,
pub gbuffer_bindless_cmd_sig: Option<ID3D12CommandSignature>,
pub prev_model_buffer_resources: Vec<PooledBuffer>,
pub prev_model_buffer_ptrs: Vec<*mut u8>,
}
#[derive(Clone, Copy)]
pub(super) struct MainPipelineShaders<'a> {
pub world_shaders: &'a [crate::gfx::backend_init::WorldShader<'a>],
}
#[derive(Clone, Copy)]
pub(super) struct MainPipelineConfig {
pub n_objects: usize,
pub n_instances: usize,
pub n_skinned: usize,
pub n_chunk_max: usize,
pub msaa_samples: u32,
}
#[derive(Clone, Copy)]
pub(super) struct MainPipelineFeatures {
pub occlusion_two_pass: bool,
pub shadow_enabled: bool,
pub gbuffer_enabled: bool,
pub hot_reload: bool,
}
pub(super) fn build_main_pipelines(
alloc: &DeviceAllocator,
info_queue: Option<&ID3D12InfoQueue>,
pipeline_shaders: MainPipelineShaders<'_>,
config: MainPipelineConfig,
features: MainPipelineFeatures,
) -> Result<MainPipelines, String> {
let device = alloc.device();
let MainPipelineShaders { world_shaders } = pipeline_shaders;
let world_default = world_shaders
.first()
.copied()
.ok_or_else(|| "BackendInit carried no shaders".to_string())?;
let bucket_shaders = world_shaders.get(1..).unwrap_or(&[]);
let MainPipelineConfig {
n_objects,
n_instances,
n_skinned,
n_chunk_max,
msaa_samples,
} = config;
let MainPipelineFeatures {
occlusion_two_pass,
shadow_enabled,
gbuffer_enabled,
hot_reload,
} = features;
let n_cull = n_objects
+ n_instances
+ n_chunk_max
+ crate::gfx::render_types::clone_reserve(n_objects)
+ n_skinned;
let (bvs, bps) = compile_main_bindless_shaders(hot_reload)?;
let bindless_main_shaders = BindlessMainShaders { vs: bvs, ps: bps };
let brs = dump_on_err(info_queue, create_main_bindless_root_signature(device))?;
let targets = BucketPipelineTargets {
root_sig: &brs,
msaa_samples,
engine_default: &bindless_main_shaders,
hot_reload,
};
let main_pso = build_bucket_pipeline(device, info_queue, targets, 0, world_default)?;
let world_pipelines = if bucket_shaders.is_empty() {
Vec::new()
} else {
let max = crate::gfx::render_types::MAX_SHADER_BUCKETS;
if bucket_shaders.len() + 1 > max {
return Err(format!(
"world declares {} Shaders but at most {max} can be routed",
bucket_shaders.len() + 1
));
}
build_world_pipeline_table(device, info_queue, targets, bucket_shaders)?
};
let main_bindless_root_sig = Some(brs);
let main_bindless_pso = Some(main_pso);
let bucket_count = 1 + world_pipelines.len();
let mut object_buffer_resources: Vec<PooledBuffer> = Vec::new();
let mut object_buffer_ptrs: Vec<*mut u8> = Vec::new();
if main_bindless_pso.is_some() && n_cull > 0 {
let object_buffer_size = align256(
(n_cull * std::mem::size_of::<crate::gfx::render_types::GpuObjectData>()) as u64,
);
for _ in 0..FRAMES + 1 {
let buf = create_buffer(
alloc,
object_buffer_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 object buffer: {e}"))?;
object_buffer_ptrs.push(ptr as *mut u8);
object_buffer_resources.push(buf);
}
}
let mut cull_root_sig: Option<ID3D12RootSignature> = None;
let mut cull_pso: Option<ID3D12PipelineState> = None;
let mut cull_pso_phase2: Option<ID3D12PipelineState> = None;
let mut cull_command_signature: Option<ID3D12CommandSignature> = None;
let mut draw_args_buffer_resources: Vec<PooledBuffer> = Vec::new();
let mut draw_args_buffer_ptrs: Vec<*mut u8> = Vec::new();
let mut indirect_cmd_buffers: Vec<ID3D12Resource> = Vec::new();
let mut cull_status_buffers: Vec<ID3D12Resource> = Vec::new();
let mut indirect_cmd_buffers_2: Vec<ID3D12Resource> = Vec::new();
let mut shadow_bindless_root_sig: Option<ID3D12RootSignature> = None;
let mut shadow_bindless_pso: Option<ID3D12PipelineState> = None;
let mut shadow_bindless_cmd_sig: Option<ID3D12CommandSignature> = None;
let mut cull_pso_shadow: Option<ID3D12PipelineState> = None;
let mut shadow_indirect_buffers: Vec<ID3D12Resource> = Vec::new();
let mut shadow_cull_status_buffers: Vec<ID3D12Resource> = Vec::new();
let mut gbuffer_bindless_root_sig: Option<ID3D12RootSignature> = None;
let mut gbuffer_bindless_pso: Option<ID3D12PipelineState> = None;
let mut gbuffer_bindless_cmd_sig: Option<ID3D12CommandSignature> = None;
let mut prev_model_buffer_resources: Vec<PooledBuffer> = Vec::new();
let mut prev_model_buffer_ptrs: Vec<*mut u8> = Vec::new();
if let (Some(bindless_root), true) = (
main_bindless_root_sig.as_ref(),
main_bindless_pso.is_some() && n_cull > 0,
) {
let cs = compile_cull_shader(hot_reload)?;
let crs = dump_on_err(info_queue, create_cull_root_signature(device))?;
let cps = dump_on_err(info_queue, create_cull_pso(device, &crs, &cs))?;
let csig = dump_on_err(
info_queue,
create_cull_command_signature(device, bindless_root),
)?;
if occlusion_two_pass {
let cs2 = compile_cull_shader_phase2(hot_reload)?;
cull_pso_phase2 = Some(dump_on_err(
info_queue,
create_cull_pso(device, &crs, &cs2),
)?);
}
let draw_args_size = align256(
(n_cull * std::mem::size_of::<crate::gfx::render_types::GpuDrawArgs>()) as u64,
);
let indirect_size =
align256((bucket_count * n_cull) as u64 * INDIRECT_COMMAND_STRIDE as u64);
let status_size = align256((n_cull as u64) * std::mem::size_of::<u32>() as u64);
for _ in 0..FRAMES + 1 {
let da = create_buffer(
alloc,
draw_args_size,
D3D12_HEAP_TYPE_UPLOAD,
D3D12_RESOURCE_STATE_GENERIC_READ,
)?;
let mut ptr = std::ptr::null_mut::<std::ffi::c_void>();
unsafe { da.Map(0, None, Some(&mut ptr)) }
.map_err(|e| format!("map draw args buffer: {e}"))?;
draw_args_buffer_ptrs.push(ptr as *mut u8);
draw_args_buffer_resources.push(da);
indirect_cmd_buffers.push(create_uav_buffer(
device,
indirect_size,
D3D12_RESOURCE_STATE_COMMON,
)?);
cull_status_buffers.push(create_uav_buffer(
device,
status_size,
D3D12_RESOURCE_STATE_COMMON,
)?);
if occlusion_two_pass {
indirect_cmd_buffers_2.push(create_uav_buffer(
device,
indirect_size,
D3D12_RESOURCE_STATE_COMMON,
)?);
}
}
if shadow_enabled {
let svs = compile_shadow_bindless_vs(hot_reload)?;
let sbrs = dump_on_err(info_queue, create_shadow_bindless_root_signature(device))?;
let sbpso = dump_on_err(info_queue, create_shadow_pso(device, &sbrs, &svs))?;
let sbsig = dump_on_err(info_queue, create_cull_command_signature(device, &sbrs))?;
let scs = compile_cull_shader_shadow(hot_reload)?;
cull_pso_shadow = Some(dump_on_err(
info_queue,
create_cull_pso(device, &crs, &scs),
)?);
let cascades = crate::gfx::render_types::NUM_SHADOW_CASCADES as u64;
let shadow_indirect_size =
align256(cascades * (n_cull as u64) * INDIRECT_COMMAND_STRIDE as u64);
for _ in 0..FRAMES {
shadow_indirect_buffers.push(create_uav_buffer(
device,
shadow_indirect_size,
D3D12_RESOURCE_STATE_COMMON,
)?);
shadow_cull_status_buffers.push(create_uav_buffer(
device,
status_size,
D3D12_RESOURCE_STATE_COMMON,
)?);
}
shadow_bindless_root_sig = Some(sbrs);
shadow_bindless_pso = Some(sbpso);
shadow_bindless_cmd_sig = Some(sbsig);
}
if gbuffer_enabled {
let (grs, gpso, gsig) = crate::directx::post::gbuffer::build_gbuffer_bindless(
device, info_queue, hot_reload,
)?;
let prev_model_size = align256((n_cull * std::mem::size_of::<[[f32; 4]; 4]>()) as u64);
for _ in 0..FRAMES {
let buf = create_buffer(
alloc,
prev_model_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 prev_model buffer: {e}"))?;
prev_model_buffer_ptrs.push(ptr as *mut u8);
prev_model_buffer_resources.push(buf);
}
gbuffer_bindless_root_sig = Some(grs);
gbuffer_bindless_pso = Some(gpso);
gbuffer_bindless_cmd_sig = Some(gsig);
}
cull_root_sig = Some(crs);
cull_pso = Some(cps);
cull_command_signature = Some(csig);
}
Ok(MainPipelines {
main_bindless_root_sig,
main_bindless_pso,
world_pipelines,
bucket_stride: n_cull,
bindless_main_shaders,
object_buffer_resources,
object_buffer_ptrs,
cull_root_sig,
cull_pso,
cull_pso_phase2,
cull_command_signature,
draw_args_buffer_resources,
draw_args_buffer_ptrs,
indirect_cmd_buffers,
cull_status_buffers,
indirect_cmd_buffers_2,
shadow_bindless_root_sig,
shadow_bindless_pso,
shadow_bindless_cmd_sig,
cull_pso_shadow,
shadow_indirect_buffers,
shadow_cull_status_buffers,
gbuffer_bindless_root_sig,
gbuffer_bindless_pso,
gbuffer_bindless_cmd_sig,
prev_model_buffer_resources,
prev_model_buffer_ptrs,
})
}
pub(super) fn build_shadow_pipeline(
device: &ID3D12Device,
info_queue: Option<&ID3D12InfoQueue>,
shadow_vs: Option<&[u8]>,
) -> Result<(Option<ID3D12RootSignature>, Option<ID3D12PipelineState>), String> {
if let Some(svs) = shadow_vs {
let sr = dump_on_err(info_queue, create_shadow_root_signature(device))?;
let sp = dump_on_err(info_queue, create_shadow_pso(device, &sr, svs))?;
Ok((Some(sr), Some(sp)))
} else {
Ok((None, None))
}
}
pub(super) fn build_text_pipeline(
device: &ID3D12Device,
info_queue: Option<&ID3D12InfoQueue>,
text_vs: &[u8],
text_ps: &[u8],
swap_format: DXGI_FORMAT,
has_atlases: bool,
) -> Result<(ID3D12RootSignature, Option<ID3D12PipelineState>), String> {
let text_root_sig = dump_on_err(info_queue, create_text_root_signature(device))?;
let text_pso = if has_atlases {
Some(dump_on_err(
info_queue,
create_text_pso(device, &text_root_sig, text_vs, text_ps, swap_format, 1),
)?)
} else {
None
};
Ok((text_root_sig, text_pso))
}
pub(super) fn build_composite_pipeline(
device: &ID3D12Device,
info_queue: Option<&ID3D12InfoQueue>,
swap_format: DXGI_FORMAT,
hot_reload: bool,
) -> Result<(ID3D12RootSignature, ID3D12PipelineState), String> {
let composite_root_sig = dump_on_err(info_queue, create_composite_root_signature(device))?;
let (composite_vs, composite_ps) = compile_composite_shaders(hot_reload)?;
let composite_pso = dump_on_err(
info_queue,
create_composite_pso(
device,
&composite_root_sig,
&composite_vs,
&composite_ps,
swap_format,
),
)?;
Ok((composite_root_sig, composite_pso))
}
#[cfg(test)]
mod tests {
#[test]
fn bindless_main_shaders_compile() {
if !crate::slangc_gate::slangc_available() {
return;
}
super::compile_main_bindless_shaders(false).expect("bindless main shaders must compile");
}
}