Skip to content

Commit a8f4f75

Browse files
Initial memory visualizer for Vulkan (#128)
* Initial stab at memory visualizer
1 parent b645446 commit a8f4f75

9 files changed

Lines changed: 320 additions & 38 deletions

File tree

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ thiserror = "1.0"
2626
# such as the ability to link/load a Vulkan library.
2727
ash = { version = ">=0.34, <=0.37", optional = true, default-features = false, features = ["debug"] }
2828
# Only needed for visualizer.
29-
imgui = { version = "0.8", optional = true }
29+
imgui = { version = "0.8", features = ["tables-api"], optional = true }
3030

3131
[target.'cfg(windows)'.dependencies]
3232
# Only needed for public-winapi interop helpers

src/allocator/dedicated_block_allocator/mod.rs

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
#[cfg(feature = "visualizer")]
44
pub(crate) mod visualizer;
55

6-
use super::{AllocationType, SubAllocator, SubAllocatorBase};
6+
use super::{resolve_backtrace, AllocationReport, AllocationType, SubAllocator, SubAllocatorBase};
77
use crate::{AllocationError, Result};
88
use log::{log, Level};
99

@@ -12,7 +12,7 @@ pub(crate) struct DedicatedBlockAllocator {
1212
size: u64,
1313
allocated: u64,
1414
name: Option<String>,
15-
backtrace: Option<String>,
15+
backtrace: Option<backtrace::Backtrace>,
1616
}
1717

1818
impl DedicatedBlockAllocator {
@@ -35,7 +35,7 @@ impl SubAllocator for DedicatedBlockAllocator {
3535
_allocation_type: AllocationType,
3636
_granularity: u64,
3737
name: &str,
38-
backtrace: Option<&str>,
38+
backtrace: Option<backtrace::Backtrace>,
3939
) -> Result<(u64, std::num::NonZeroU64)> {
4040
if self.allocated != 0 {
4141
return Err(AllocationError::OutOfMemory);
@@ -49,7 +49,7 @@ impl SubAllocator for DedicatedBlockAllocator {
4949

5050
self.allocated = size;
5151
self.name = Some(name.to_string());
52-
self.backtrace = backtrace.map(|s| s.to_owned());
52+
self.backtrace = backtrace;
5353

5454
#[allow(clippy::unwrap_used)]
5555
let dummy_id = std::num::NonZeroU64::new(1).unwrap();
@@ -86,7 +86,7 @@ impl SubAllocator for DedicatedBlockAllocator {
8686
) {
8787
let empty = "".to_string();
8888
let name = self.name.as_ref().unwrap_or(&empty);
89-
let backtrace = self.backtrace.as_ref().unwrap_or(&empty);
89+
let backtrace = resolve_backtrace(&self.backtrace);
9090

9191
log!(
9292
log_level,
@@ -107,6 +107,17 @@ impl SubAllocator for DedicatedBlockAllocator {
107107
)
108108
}
109109

110+
fn report_allocations(&self) -> Vec<AllocationReport> {
111+
vec![AllocationReport {
112+
name: self
113+
.name
114+
.clone()
115+
.unwrap_or_else(|| "<Unnamed Dedicated allocation>".to_owned()),
116+
size: self.size,
117+
backtrace: self.backtrace.clone(),
118+
}]
119+
}
120+
110121
fn size(&self) -> u64 {
111122
self.size
112123
}

src/allocator/free_list_allocator/mod.rs

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
#[cfg(feature = "visualizer")]
44
pub(crate) mod visualizer;
55

6-
use super::{AllocationType, SubAllocator, SubAllocatorBase};
6+
use super::{resolve_backtrace, AllocationReport, AllocationType, SubAllocator, SubAllocatorBase};
77
use crate::{AllocationError, Result};
88

99
use log::{log, Level};
@@ -26,7 +26,7 @@ pub(crate) struct MemoryChunk {
2626
pub(crate) offset: u64,
2727
pub(crate) allocation_type: AllocationType,
2828
pub(crate) name: Option<String>,
29-
pub(crate) backtrace: Option<String>, // Only used if STORE_STACK_TRACES is true
29+
pub(crate) backtrace: Option<backtrace::Backtrace>, // Only used if STORE_STACK_TRACES is true
3030
next: Option<std::num::NonZeroU64>,
3131
prev: Option<std::num::NonZeroU64>,
3232
}
@@ -156,7 +156,7 @@ impl SubAllocator for FreeListAllocator {
156156
allocation_type: AllocationType,
157157
granularity: u64,
158158
name: &str,
159-
backtrace: Option<&str>,
159+
backtrace: Option<backtrace::Backtrace>,
160160
) -> Result<(u64, std::num::NonZeroU64)> {
161161
let free_size = self.size - self.allocated;
162162
if size > free_size {
@@ -243,7 +243,7 @@ impl SubAllocator for FreeListAllocator {
243243
offset: free_chunk.offset,
244244
allocation_type,
245245
name: Some(name.to_string()),
246-
backtrace: backtrace.map(|s| s.to_owned()),
246+
backtrace,
247247
prev: free_chunk.prev,
248248
next: Some(first_fit_id),
249249
};
@@ -272,7 +272,7 @@ impl SubAllocator for FreeListAllocator {
272272

273273
chunk.allocation_type = allocation_type;
274274
chunk.name = Some(name.to_string());
275-
chunk.backtrace = backtrace.map(|s| s.to_owned());
275+
chunk.backtrace = backtrace;
276276

277277
self.remove_id_from_free_list(first_fit_id);
278278

@@ -356,7 +356,7 @@ impl SubAllocator for FreeListAllocator {
356356
}
357357
let empty = "".to_string();
358358
let name = chunk.name.as_ref().unwrap_or(&empty);
359-
let backtrace = chunk.backtrace.as_ref().unwrap_or(&empty);
359+
let backtrace = resolve_backtrace(&chunk.backtrace);
360360

361361
log!(
362362
log_level,
@@ -383,12 +383,30 @@ impl SubAllocator for FreeListAllocator {
383383
);
384384
}
385385
}
386+
387+
fn report_allocations(&self) -> Vec<AllocationReport> {
388+
self.chunks
389+
.iter()
390+
.filter(|(_key, chunk)| chunk.allocation_type != AllocationType::Free)
391+
.map(|(_key, chunk)| AllocationReport {
392+
name: chunk
393+
.name
394+
.clone()
395+
.unwrap_or_else(|| "<Unnamed FreeList allocation>".to_owned()),
396+
size: chunk.size,
397+
backtrace: chunk.backtrace.clone(),
398+
})
399+
.collect::<Vec<_>>()
400+
}
401+
386402
fn size(&self) -> u64 {
387403
self.size
388404
}
405+
389406
fn allocated(&self) -> u64 {
390407
self.allocated
391408
}
409+
392410
fn supports_general_allocations(&self) -> bool {
393411
true
394412
}

src/allocator/free_list_allocator/visualizer.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use super::{AllocationType, FreeListAllocator};
1+
use super::{resolve_backtrace, AllocationType, FreeListAllocator};
22
use crate::visualizer::{ColorScheme, SubAllocatorVisualizer};
33

44
impl SubAllocatorVisualizer for FreeListAllocator {
@@ -76,10 +76,11 @@ impl SubAllocatorVisualizer for FreeListAllocator {
7676
if let Some(name) = &chunk.name {
7777
ui.text(format!("name: {:?}", name));
7878
}
79-
if show_backtraces {
80-
if let Some(backtrace) = &chunk.backtrace {
81-
ui.text(format!("backtrace: {:}", backtrace));
82-
}
79+
if show_backtraces && chunk.backtrace.is_some() {
80+
ui.text(format!(
81+
"backtrace: {:}",
82+
resolve_backtrace(&chunk.backtrace)
83+
));
8384
}
8485
})
8586
}

src/allocator/mod.rs

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,24 @@ pub(crate) enum AllocationType {
1616
NonLinear,
1717
}
1818

19+
#[derive(Clone)]
20+
pub(crate) struct AllocationReport {
21+
pub(crate) name: String,
22+
pub(crate) size: u64,
23+
pub(crate) backtrace: Option<backtrace::Backtrace>,
24+
}
25+
26+
pub(crate) fn resolve_backtrace(backtrace: &Option<backtrace::Backtrace>) -> String {
27+
backtrace.as_ref().map_or_else(
28+
|| "".to_owned(),
29+
|bt| {
30+
let mut bt = bt.clone();
31+
bt.resolve();
32+
format!("{:?}", bt)
33+
},
34+
)
35+
}
36+
1937
#[cfg(feature = "visualizer")]
2038
pub(crate) trait SubAllocatorBase: crate::visualizer::SubAllocatorVisualizer {}
2139
#[cfg(not(feature = "visualizer"))]
@@ -29,7 +47,7 @@ pub(crate) trait SubAllocator: SubAllocatorBase + std::fmt::Debug {
2947
allocation_type: AllocationType,
3048
granularity: u64,
3149
name: &str,
32-
backtrace: Option<&str>,
50+
backtrace: Option<backtrace::Backtrace>,
3351
) -> Result<(u64, std::num::NonZeroU64)>;
3452

3553
fn free(&mut self, chunk_id: Option<std::num::NonZeroU64>) -> Result<()>;
@@ -47,6 +65,8 @@ pub(crate) trait SubAllocator: SubAllocatorBase + std::fmt::Debug {
4765
memory_block_index: usize,
4866
);
4967

68+
fn report_allocations(&self) -> Vec<AllocationReport>;
69+
5070
#[must_use]
5171
fn supports_general_allocations(&self) -> bool;
5272
#[must_use]

src/d3d12/mod.rs

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -360,7 +360,7 @@ impl MemoryType {
360360
&mut self,
361361
device: &ID3D12Device,
362362
desc: &AllocationCreateDesc<'_>,
363-
backtrace: Option<&str>,
363+
backtrace: Option<backtrace::Backtrace>,
364364
) -> Result<Allocation> {
365365
let allocation_type = AllocationType::Linear;
366366

@@ -428,7 +428,7 @@ impl MemoryType {
428428
allocation_type,
429429
1,
430430
desc.name,
431-
backtrace,
431+
backtrace.clone(),
432432
);
433433

434434
match allocation {
@@ -651,7 +651,7 @@ impl Allocator {
651651
let alignment = desc.alignment;
652652

653653
let backtrace = if self.debug_settings.store_stack_traces {
654-
Some(format!("{:?}", backtrace::Backtrace::new()))
654+
Some(backtrace::Backtrace::new_unresolved())
655655
} else {
656656
None
657657
};
@@ -662,10 +662,8 @@ impl Allocator {
662662
&desc.name, size, alignment
663663
);
664664
if self.debug_settings.log_stack_traces {
665-
let backtrace = backtrace
666-
.clone()
667-
.unwrap_or(format!("{:?}", backtrace::Backtrace::new()));
668-
debug!("Allocation stack trace: {}", &backtrace);
665+
let backtrace = backtrace::Backtrace::new();
666+
debug!("Allocation stack trace: {:?}", &backtrace);
669667
}
670668
}
671669

@@ -688,7 +686,7 @@ impl Allocator {
688686
})
689687
.ok_or(AllocationError::NoCompatibleMemoryTypeFound)?;
690688

691-
memory_type.allocate(&self.device, desc, backtrace.as_deref())
689+
memory_type.allocate(&self.device, desc, backtrace)
692690
}
693691

694692
pub fn free(&mut self, allocation: Allocation) -> Result<()> {

0 commit comments

Comments
 (0)