1#![allow(clippy::new_without_default)]
2
3use windows::Win32::Graphics::Direct3D12::*;
4
5use super::Allocator;
6use crate::visualizer::{
7 render_allocation_reports_ui, AllocationReportVisualizeSettings, ColorScheme,
8 MemoryChunksVisualizationSettings,
9};
10
11struct AllocatorVisualizerBlockWindow {
12 memory_type_index: usize,
13 block_index: usize,
14 settings: MemoryChunksVisualizationSettings,
15}
16
17impl AllocatorVisualizerBlockWindow {
18 fn new(memory_type_index: usize, block_index: usize) -> Self {
19 Self {
20 memory_type_index,
21 block_index,
22 settings: Default::default(),
23 }
24 }
25}
26
27pub struct AllocatorVisualizer {
28 selected_blocks: Vec<AllocatorVisualizerBlockWindow>,
29 color_scheme: ColorScheme,
30 breakdown_settings: AllocationReportVisualizeSettings,
31}
32
33fn format_heap_type(heap_type: D3D12_HEAP_TYPE) -> &'static str {
34 let names = [
35 "D3D12_HEAP_TYPE_DEFAULT_INVALID",
36 "D3D12_HEAP_TYPE_DEFAULT",
37 "D3D12_HEAP_TYPE_UPLOAD",
38 "D3D12_HEAP_TYPE_READBACK",
39 "D3D12_HEAP_TYPE_CUSTOM",
40 ];
41
42 names[heap_type.0 as usize]
43}
44
45fn format_cpu_page_property(prop: D3D12_CPU_PAGE_PROPERTY) -> &'static str {
46 let names = [
47 "D3D12_CPU_PAGE_PROPERTY_UNKNOWN",
48 "D3D12_CPU_PAGE_PROPERTY_NOT_AVAILABLE",
49 "D3D12_CPU_PAGE_PROPERTY_WRITE_COMBINE",
50 "D3D12_CPU_PAGE_PROPERTY_WRITE_BACK",
51 ];
52
53 names[prop.0 as usize]
54}
55
56fn format_memory_pool(pool: D3D12_MEMORY_POOL) -> &'static str {
57 let names = [
58 "D3D12_MEMORY_POOL_UNKNOWN",
59 "D3D12_MEMORY_POOL_L0",
60 "D3D12_MEMORY_POOL_L1",
61 ];
62
63 names[pool.0 as usize]
64}
65
66impl AllocatorVisualizer {
67 pub fn new() -> Self {
68 Self {
69 selected_blocks: Vec::default(),
70 color_scheme: ColorScheme::default(),
71 breakdown_settings: Default::default(),
72 }
73 }
74
75 pub fn set_color_scheme(&mut self, color_scheme: ColorScheme) {
76 self.color_scheme = color_scheme;
77 }
78
79 pub fn render_memory_block_ui(&mut self, ui: &mut egui::Ui, alloc: &Allocator) {
80 ui.collapsing(
81 format!("Memory Types: ({} types)", alloc.memory_types.len()),
82 |ui| {
83 for (mem_type_idx, mem_type) in alloc.memory_types.iter().enumerate() {
84 ui.collapsing(
85 format!(
86 "Type: {} ({} blocks)",
87 mem_type_idx,
88 mem_type.memory_blocks.len()
89 ),
90 |ui| {
91 let mut total_block_size = 0;
92 let mut total_allocated = 0;
93
94 for block in mem_type.memory_blocks.iter().flatten() {
95 total_block_size += block.size;
96 total_allocated += block.sub_allocator.allocated();
97 }
98
99 let active_block_count = mem_type
100 .memory_blocks
101 .iter()
102 .filter(|block| block.is_some())
103 .count();
104
105 ui.label(format!("heap category: {:?}", mem_type.heap_category));
106 ui.label(format!(
107 "Heap Type: {} ({})",
108 format_heap_type(mem_type.heap_properties.Type),
109 mem_type.heap_properties.Type.0
110 ));
111 ui.label(format!(
112 "CpuPageProperty: {} ({})",
113 format_cpu_page_property(mem_type.heap_properties.CPUPageProperty),
114 mem_type.heap_properties.CPUPageProperty.0
115 ));
116 ui.label(format!(
117 "MemoryPoolPreference: {} ({})",
118 format_memory_pool(mem_type.heap_properties.MemoryPoolPreference),
119 mem_type.heap_properties.MemoryPoolPreference.0
120 ));
121 ui.label(format!("total block size: {} KiB", total_block_size / 1024));
122 ui.label(format!("total allocated: {} KiB", total_allocated / 1024));
123 ui.label(format!(
124 "committed resource allocations: {}",
125 mem_type.committed_allocations.num_allocations
126 ));
127 ui.label(format!(
128 "total committed resource allocations: {} KiB",
129 mem_type.committed_allocations.total_size
130 ));
131 ui.label(format!("block count: {}", active_block_count));
132
133 for (block_idx, block) in mem_type.memory_blocks.iter().enumerate() {
134 let Some(block) = block else { continue };
135
136 ui.collapsing(format!("Block: {}", block_idx), |ui| {
137 ui.label(format!("size: {} KiB", block.size / 1024));
138 ui.label(format!(
139 "allocated: {} KiB",
140 block.sub_allocator.allocated() / 1024
141 ));
142 ui.label(format!("D3D12 heap: {:?}", block.heap));
143 block.sub_allocator.draw_base_info(ui);
144
145 if block.sub_allocator.supports_visualization()
146 && ui.button("visualize").clicked()
147 && !self.selected_blocks.iter().any(|x| {
148 x.memory_type_index == mem_type_idx
149 && x.block_index == block_idx
150 })
151 {
152 self.selected_blocks.push(
153 AllocatorVisualizerBlockWindow::new(
154 mem_type_idx,
155 block_idx,
156 ),
157 );
158 }
159 });
160 }
161 },
162 );
163 }
164 },
165 );
166 }
167
168 pub fn render_memory_block_window(
169 &mut self,
170 ctx: &egui::Context,
171 allocator: &Allocator,
172 open: &mut bool,
173 ) {
174 egui::Window::new("Allocator Memory Blocks")
175 .open(open)
176 .show(ctx, |ui| self.render_breakdown_ui(ui, allocator));
177 }
178
179 pub fn render_memory_block_visualization_windows(
180 &mut self,
181 ctx: &egui::Context,
182 allocator: &Allocator,
183 ) {
184 let color_scheme = &self.color_scheme;
186
187 self.selected_blocks.retain_mut(|window| {
188 let mut open = true;
189
190 egui::Window::new(format!(
191 "Block Visualizer {}:{}",
192 window.memory_type_index, window.block_index
193 ))
194 .default_size([1920.0 * 0.5, 1080.0 * 0.5])
195 .open(&mut open)
196 .show(ctx, |ui| {
197 let memblock = &allocator.memory_types[window.memory_type_index].memory_blocks
198 [window.block_index]
199 .as_ref();
200 if let Some(memblock) = memblock {
201 ui.label(format!(
202 "Memory type {}, Memory block {}, Block size: {} KiB",
203 window.memory_type_index,
204 window.block_index,
205 memblock.size / 1024
206 ));
207
208 window
209 .settings
210 .ui(ui, allocator.debug_settings.store_stack_traces);
211
212 ui.separator();
213
214 memblock
215 .sub_allocator
216 .draw_visualization(color_scheme, ui, &window.settings);
217 } else {
218 ui.label("Deallocated memory block");
219 }
220 });
221
222 open
223 });
224 }
225
226 pub fn render_breakdown_ui(&mut self, ui: &mut egui::Ui, allocator: &Allocator) {
227 render_allocation_reports_ui(
228 ui,
229 &mut self.breakdown_settings,
230 allocator
231 .memory_types
232 .iter()
233 .flat_map(|memory_type| memory_type.memory_blocks.iter())
234 .flatten()
235 .flat_map(|memory_block| memory_block.sub_allocator.report_allocations()),
236 );
237 }
238
239 pub fn render_breakdown_window(
240 &mut self,
241 ctx: &egui::Context,
242 allocator: &Allocator,
243 open: &mut bool,
244 ) {
245 egui::Window::new("Allocator Breakdown")
246 .open(open)
247 .show(ctx, |ui| self.render_breakdown_ui(ui, allocator));
248 }
249}