1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
//! Render targets: the DSV heap with the main depth buffer, the HDR scene
//! target and its MSAA resolve, the views the depth- and scene-sampling passes
//! bind, and the render graph's transient pool.
use concinnity_core::render::error::RenderResult;
use concinnity_core::render::render_graph::{PoolGates, plan_pool_slots};
use windows::Win32::Graphics::Direct3D12::*;
use super::heap_layout::{DSV_MAIN_DEPTH_SLOT, RtvHeapLayout};
use super::{Features, InitGpu, heaps};
use crate::directx::context::{
DepthState, DxDescriptors, DxTargets, Extents, HdrState, SwapchainState,
};
use crate::directx::post::upscale::UpscaleState;
use crate::directx::reactive_mask::{ReactiveMask, ReactiveMaskSlots};
use crate::directx::texture::{
HDR_FORMAT, create_hdr_color_target, create_hdr_resolve_target, create_main_depth_texture,
write_hdr_srv,
};
use crate::directx::transient_pool::TransientResourcePool;
pub(super) struct TargetInputs<'a> {
pub(super) descriptors: &'a DxDescriptors,
pub(super) swapchain: &'a SwapchainState,
pub(super) rtv: &'a RtvHeapLayout,
pub(super) upscale: &'a UpscaleState,
pub(super) features: &'a Features,
pub(super) output: (u32, u32),
pub(super) clear_color: [f32; 4],
}
pub(super) fn build_targets(
gpu: &InitGpu<'_>,
inputs: TargetInputs<'_>,
) -> RenderResult<DxTargets> {
let TargetInputs {
descriptors,
swapchain,
rtv,
upscale,
features,
output: (width, height),
clear_color,
} = inputs;
let hw = gpu.hw;
let layout = &descriptors.layout;
let msaa_samples = features.msaa_samples;
// Off-screen scene render resolution. The active backend reports the
// resolved render dims (clamped to the backend's supported ratio
// range); a missing / failed upscaler leaves the scene at full output.
let (render_w, render_h) = match &upscale.backend {
Some(u) => u.extent().render,
None => (width, height),
};
let dsv_heap = heaps::create_dsv_heap(&hw.device)?;
let dsv_descriptor_size = heaps::descriptor_size(&hw.device, D3D12_DESCRIPTOR_HEAP_TYPE_DSV);
let main_dsv_cpu = heaps::cpu_handle(&dsv_heap, dsv_descriptor_size, DSV_MAIN_DEPTH_SLOT);
// Main depth buffer. Allowed as SRV so the projected-decal pass can
// sample it to reconstruct world positions; runtime `add_decal`
// needs this even when no decals were declared at init.
let depth_resource = create_main_depth_texture(
&hw.device,
render_w,
render_h,
main_dsv_cpu,
msaa_samples,
true,
)?;
// Off-screen HDR scene target
// The main + instanced passes render linear-light HDR into this; the
// composite pass tonemaps it onto the swapchain. RTV heap slot [FRAMES]
// (after the back-buffer RTVs) holds its render-target view.
let hdr_color_rtv = swapchain.rtv(rtv.hdr_slot);
let hdr_color = create_hdr_color_target(
&hw.device,
render_w,
render_h,
msaa_samples,
hdr_color_rtv,
clear_color,
)?;
// The resolved sample count decides which of two shapes the frame has:
// with MSAA the main pass resolves `hdr_color` into a separate
// single-sample spine (and the render graph carries both as resources),
// without it `hdr_color` is the spine and there is no resolve step at
// all. Log it so a verification run can say which shape it exercised.
tracing::info!("d3d12 HDR target: {msaa_samples}x MSAA");
let hdr_resolve = if msaa_samples > 1 {
Some(create_hdr_resolve_target(&hw.device, render_w, render_h)?)
} else {
None
};
// RTV for `hdr_resolve`: the projected-decal pass renders into the
// resolved scene target, so it needs a render-target view. Sits in
// the RTV heap right after the SSAO RTVs. Only created when MSAA is
// on (MSAA off uses the existing `hdr_color_rtv`).
let hdr_resolve_rtv = if let Some(resolve) = &hdr_resolve {
let rtv_handle = swapchain.rtv(rtv.decal_resolve_slot);
// SAFETY: the view descriptor and the resource it names are live for the call, and the
// destination handle addresses a slot this context reserved for the view in a heap it
// owns.
unsafe {
let rtv_desc = D3D12_RENDER_TARGET_VIEW_DESC {
Format: HDR_FORMAT,
ViewDimension: D3D12_RTV_DIMENSION_TEXTURE2D,
..Default::default()
};
hw.device
.CreateRenderTargetView(resolve, Some(&rtv_desc), rtv_handle);
}
Some(rtv_handle)
} else {
None
};
// The composite pass samples the resolved target (MSAA on) or the
// directly-rendered HDR target (MSAA off).
write_hdr_srv(
&hw.device,
hdr_resolve.as_ref().unwrap_or(&hdr_color),
descriptors.slot_cpu(layout.hdr_srv_slot),
);
// Main-depth SRV, shared by every depth-sampling decoration pass (decal,
// glass, lines) at their own t0. The DSV-only flag was dropped above so
// this is valid.
crate::directx::decal::write_main_depth_srv(
&hw.device,
&depth_resource,
descriptors.slot_cpu(layout.decal_depth_srv_slot),
msaa_samples,
);
// Transient pool: the graph-owned transient render targets. `bloom_top`
// (the bloom chain's top octave) is always managed; `ao_output` is placed
// only when SSAO is on (else `resource_for` returns None and the main pass
// binding 6 falls back to `ssao_white`). Built before the bloom chain, SSAO
// and the G-buffer, which read their placed resources back by label.
let transient_pool = TransientResourcePool::build(
hw.alloc.device(),
hw.alloc.queue(),
&plan_pool_slots(
PoolGates {
ssao: features.ssao_enabled,
gbuffer: features.gbuffer_enabled,
},
(render_w, render_h),
(width, height),
)?,
)?;
let reactive_mask = ReactiveMask::new(
&hw.device,
(render_w, render_h),
ReactiveMaskSlots {
rtv: swapchain.rtv(rtv.reactive_mask_base_slot),
null_rtv: swapchain.rtv(rtv.reactive_mask_base_slot + 1),
srv_cpu: descriptors.slot_cpu(layout.reactive_mask_srv_slot),
srv_gpu: descriptors.slot_gpu(layout.reactive_mask_srv_slot),
},
)?;
Ok(DxTargets {
hdr: HdrState {
color: hdr_color,
color_rtv: hdr_color_rtv,
resolve: hdr_resolve,
resolve_rtv: hdr_resolve_rtv,
srv_gpu: descriptors.slot_gpu(layout.hdr_srv_slot),
msaa_samples,
},
depth: DepthState {
dsv: main_dsv_cpu,
resource: depth_resource,
heap: dsv_heap,
},
main_depth_srv_gpu: descriptors.slot_gpu(layout.decal_depth_srv_slot),
extent: Extents {
render_width: render_w,
render_height: render_h,
output_width: width,
output_height: height,
},
transient_pool,
reactive_mask,
})
}