mirror of
https://github.com/storytold/effectcraft.git
synced 2026-10-24 10:09:32 -06:00
gpu: upload cache drops textures of freed buffers on every insert
Entries whose layer buffer was freed were pruned only once the cache went over its budget (1.5 GiB), so an animated layer, which makes a new buffer every frame, left a dead 4K texture per frame allocated until the cache filled up.
This commit is contained in:
@@ -919,6 +919,12 @@ impl GpuContext {
|
||||
|
||||
/// Upload a (layer cache) buffer, reusing the texture while the same `Arc` is alive: static
|
||||
/// layers upload once, not every frame.
|
||||
/// Entries in the layer upload cache.
|
||||
#[cfg(test)]
|
||||
pub(crate) fn upload_entries(&self) -> usize {
|
||||
self.uploads.lock().map(|u| u.map.len()).unwrap_or_default()
|
||||
}
|
||||
|
||||
pub(crate) fn upload_buf(&self, buf: &Arc<Buf>) -> Option<GpuImage> {
|
||||
let key = Arc::as_ptr(buf) as usize;
|
||||
if let Ok(mut u) = self.uploads.lock() {
|
||||
@@ -939,14 +945,14 @@ impl GpuContext {
|
||||
u.bytes -= old.bytes;
|
||||
}
|
||||
u.bytes += bytes;
|
||||
// Drop textures of freed buffers, then the least recently used over budget.
|
||||
// Drop textures of freed buffers at once (a layer whose buffer changes every frame
|
||||
// makes a new one each time, and nothing can hit the old one again), then the least
|
||||
// recently used over budget.
|
||||
let budget = self.upload_budget();
|
||||
if u.bytes > budget {
|
||||
let dead: Vec<usize> = u.map.iter().filter(|(_, e)| e.buf.strong_count() == 0).map(|(k, _)| *k).collect();
|
||||
for k in dead {
|
||||
if let Some(e) = u.map.remove(&k) {
|
||||
u.bytes -= e.bytes;
|
||||
}
|
||||
let dead: Vec<usize> = u.map.iter().filter(|(_, e)| e.buf.strong_count() == 0).map(|(k, _)| *k).collect();
|
||||
for k in dead {
|
||||
if let Some(e) = u.map.remove(&k) {
|
||||
u.bytes -= e.bytes;
|
||||
}
|
||||
}
|
||||
if u.bytes > budget && !self.upload_full_warned.swap(true, std::sync::atomic::Ordering::Relaxed) {
|
||||
|
||||
@@ -1044,3 +1044,19 @@ fn static_effect_layers_draw_from_the_cache() {
|
||||
assert!(frames[1] == frames[2]);
|
||||
check("static effect layer vs CPU", compare_at(&s, opts(), t(2)), 0.0);
|
||||
}
|
||||
|
||||
/// The layer upload cache drops the textures of freed buffers when it inserts, not only once
|
||||
/// it is over budget (an animated layer makes a new buffer every frame).
|
||||
#[test]
|
||||
fn upload_cache_drops_textures_of_freed_buffers() {
|
||||
let Some(g) = gpu() else { return };
|
||||
let img = effectcraft_raster::Image::new(256, 256);
|
||||
let before = g.ctx.upload_entries();
|
||||
for _ in 0..6 {
|
||||
let buf = std::sync::Arc::new(effectcraft_effects::Buf { img: img.clone(), offset: [0.0; 2], scale: 1.0 });
|
||||
let up = g.ctx.upload_buf(&buf).expect("upload");
|
||||
drop((up, buf));
|
||||
}
|
||||
let kept = g.ctx.upload_entries();
|
||||
assert!(kept <= before + 1, "the upload cache kept {kept} textures of freed buffers (had {before})");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user