Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions src/cache.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use crate::util::{hash_map_capacity_bytes, vector_capacity_bytes, MemoryUsage};
use crate::vertex::CustomVertex;
use ahash::{HashMap, HashMapExt};
use lyon::tessellation::VertexBuffers;
use std::collections::HashSet;
use std::num::NonZeroUsize;
use std::sync::Arc;

Expand Down Expand Up @@ -65,6 +67,47 @@ impl Cache {
std::mem::swap(&mut self.previous_frame, &mut self.current_frame);
self.current_frame.clear();
}

pub(crate) fn memory_usage(
&self,
visited_tessellations: &mut HashSet<*const CachedTessellation>,
) -> MemoryUsage {
let mut usage = MemoryUsage {
cpu_bytes: hash_map_capacity_bytes(&self.previous_frame)
.saturating_add(hash_map_capacity_bytes(&self.current_frame)),
gpu_buffer_bytes: 0,
gpu_texture_bytes: 0,
};

for tessellation in self
.previous_frame
.values()
.chain(self.current_frame.values())
{
usage.cpu_bytes = usage
.cpu_bytes
.saturating_add(cached_tessellation_heap_bytes(
tessellation,
visited_tessellations,
));
}

usage
}
}

pub(crate) fn cached_tessellation_heap_bytes(
tessellation: &Arc<CachedTessellation>,
visited_tessellations: &mut HashSet<*const CachedTessellation>,
) -> u64 {
if !visited_tessellations.insert(Arc::as_ptr(tessellation)) {
return 0;
}

std::mem::size_of::<CachedTessellation>() as u64
+ std::mem::size_of::<VertexBuffers<CustomVertex, u16>>() as u64
+ vector_capacity_bytes(&tessellation.vertex_buffers.vertices)
+ vector_capacity_bytes(&tessellation.vertex_buffers.indices)
}

#[cfg(test)]
Expand Down
83 changes: 83 additions & 0 deletions src/effect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ use std::collections::hash_map::DefaultHasher;
use std::hash::Hash;
use std::sync::OnceLock;

use crate::util::{texture_memory_size, vector_capacity_bytes, MemoryUsage};

// ── Error type ───────────────────────────────────────────────────────────────

/// Errors that can occur when working with the effect system.
Expand Down Expand Up @@ -154,6 +156,16 @@ pub(crate) struct LoadedEffect {
pub params_bind_group_layout: Option<wgpu::BindGroupLayout>,
}

impl LoadedEffect {
pub(crate) fn memory_usage(&self) -> MemoryUsage {
MemoryUsage {
cpu_bytes: vector_capacity_bytes(&self.passes),
gpu_buffer_bytes: 0,
gpu_texture_bytes: 0,
}
}
}

// ── Per-node effect instance ─────────────────────────────────────────────────

/// A per-node effect instance. Stored in a HashMap<usize, EffectInstance> on the Renderer,
Expand All @@ -178,6 +190,27 @@ pub(crate) struct EffectInstance {
pub backdrop_texture_id: Option<u64>,
}

impl EffectInstance {
pub(crate) fn memory_usage(&self) -> MemoryUsage {
let mut usage = MemoryUsage {
cpu_bytes: vector_capacity_bytes(&self.params),
gpu_buffer_bytes: 0,
gpu_texture_bytes: 0,
};

if let Some(params_buffer) = &self.params_buffer {
usage.gpu_buffer_bytes = usage.gpu_buffer_bytes.saturating_add(params_buffer.size());
}
if let Some(backdrop_material_params_buffer) = &self.backdrop_material_params_buffer {
usage.gpu_buffer_bytes = usage
.gpu_buffer_bytes
.saturating_add(backdrop_material_params_buffer.size());
}

usage
}
}

// ── Offscreen texture pool ───────────────────────────────────────────────────

/// A pooled offscreen texture with color, optional depth/stencil, and optional MSAA resolve
Expand All @@ -191,9 +224,44 @@ pub(crate) struct PooledTexture {
pub resolve_view: Option<wgpu::TextureView>,
pub width: u32,
pub height: u32,
pub format: wgpu::TextureFormat,
pub sample_count: u32,
}

impl PooledTexture {
pub(crate) fn memory_usage(&self) -> MemoryUsage {
let color_size =
texture_memory_size(self.color_texture.size(), self.format, self.sample_count, 1);
let resolve_size = self
.resolve_texture
.as_ref()
.map(|texture| texture_memory_size(texture.size(), self.format, 1, 1))
.unwrap_or(0);
let depth_stencil_size = if self.depth_stencil_view.is_some() {
texture_memory_size(
wgpu::Extent3d {
width: self.width,
height: self.height,
depth_or_array_layers: 1,
},
wgpu::TextureFormat::Depth24PlusStencil8,
self.sample_count,
1,
)
} else {
0
};

MemoryUsage {
cpu_bytes: 0,
gpu_buffer_bytes: 0,
gpu_texture_bytes: color_size
.saturating_add(resolve_size)
.saturating_add(depth_stencil_size),
}
}
}

/// Pool of reusable offscreen textures for effect compositing.
/// At frame start, all textures move back to `available`.
pub(crate) struct OffscreenTexturePool {
Expand Down Expand Up @@ -232,6 +300,20 @@ impl OffscreenTexturePool {
}
}

pub(crate) fn memory_usage(&self) -> MemoryUsage {
let mut usage = MemoryUsage {
cpu_bytes: vector_capacity_bytes(&self.available),
gpu_buffer_bytes: 0,
gpu_texture_bytes: 0,
};

for texture in &self.available {
usage.add(texture.memory_usage());
}

usage
}

/// Acquire a texture matching the given dimensions and sample count, plus a depth/stencil
/// attachment for render passes that write depth or stencil.
pub fn acquire_with_depth(
Expand Down Expand Up @@ -363,6 +445,7 @@ impl OffscreenTexturePool {
resolve_view,
width,
height,
format,
sample_count,
}
}
Expand Down
17 changes: 17 additions & 0 deletions src/gradient/normalize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,23 @@ pub(crate) struct NormalizedGradient {
}

impl NormalizedGradient {
pub(crate) fn heap_bytes(&self) -> u64 {
let stops_bytes = if self.stops.spilled() {
(self.stops.capacity() as u64)
.saturating_mul(std::mem::size_of::<NormalizedStop>() as u64)
} else {
0
};
let segments_bytes = if self.segments.spilled() {
(self.segments.capacity() as u64)
.saturating_mul(std::mem::size_of::<NormalizedSegment>() as u64)
} else {
0
};

stops_bytes.saturating_add(segments_bytes)
}

pub(crate) fn from_common(common: &GradientCommonDesc, kind: GradientKind) -> Self {
let is_conic = kind == GradientKind::Conic;

Expand Down
48 changes: 48 additions & 0 deletions src/gradient/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -530,6 +530,13 @@ impl Fill {
_ => None,
}
}

pub(crate) fn heap_bytes(&self) -> u64 {
match self {
Fill::Solid(_) => 0,
Fill::Gradient(gradient) => gradient.heap_bytes(),
}
}
}

impl From<Color> for Fill {
Expand Down Expand Up @@ -606,6 +613,10 @@ pub(crate) struct GradientData {
}

impl Gradient {
pub(crate) fn heap_bytes(&self) -> u64 {
self.data.heap_bytes()
}

pub fn new(desc: GradientDesc) -> Result<Self, GradientError> {
match desc {
GradientDesc::Linear(d) => Self::linear(d),
Expand Down Expand Up @@ -815,6 +826,43 @@ impl Gradient {
}
}

impl GradientData {
pub(crate) fn heap_bytes(&self) -> u64 {
self.ramp_cache_key
.heap_bytes()
.saturating_add(self.ramp.heap_bytes())
}
}

impl GradientRampCacheKey {
fn heap_bytes(&self) -> u64 {
if self.stops.spilled() {
(self.stops.capacity() as u64)
.saturating_mul(std::mem::size_of::<GradientRampStopKey>() as u64)
} else {
0
}
}
}

impl GradientRamp {
fn heap_bytes(&self) -> u64 {
match self {
GradientRamp::Constant(_) => 0,
GradientRamp::Pending(source) => {
std::mem::size_of::<GradientRampSource>() as u64 + source.heap_bytes()
}
GradientRamp::Sampled(_) => std::mem::size_of::<[[f32; 4]; RAMP_RESOLUTION]>() as u64,
}
}
}

impl GradientRampSource {
fn heap_bytes(&self) -> u64 {
self.normalized.heap_bytes()
}
}

impl HueComponentKey {
fn from_hue_component(hue_component: HueComponent) -> Self {
match hue_component {
Expand Down
Loading
Loading