gpu_allocator/d3d12/
visualizer.rs

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        // Draw each window.
185        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}