forked from 1b-agent/opencadstudio-upstream
fix: free superseded wire buffers before allocating their replacement
The renderer shares one set of wire GPU buffers per wire_content_id across every pane and viewport slot. Every geometry edit mints a new content id, so every edit builds a new set -- and the old one stayed in wire_buffer_cache until the map exceeded sixteen entries. Sixteen entries is a sane bound for small drawings and fatal for large ones. The policy counted entries, not bytes. On the reproducer a single set is most of the card: the VRAM trace shows 572 MiB baseline rising to 1871 MiB on a 2048 MiB GT 1030. The second set has nowhere to go, so the first edit after opening exhausts VRAM. The perf log shows exactly that -- two `shared-fullupload` lines, an edit, then the process dies. Evict on every miss instead of at a count threshold, and do it before building the replacement rather than after, so the old allocation is released while the new one is still being sized. Dropping this slot's own handles first is what lets the retain see the superseded entry as garbage; a pane still drawing that content keeps its own clone and its strong count stays above one, so shared geometry is never pulled out from under a live viewport. The trade is that returning to a previous content id -- undo, mostly -- now rebuilds instead of hitting a warm entry. On a card where the alternative is dying, that is the right way round. Adds a PERF line reporting evicted and retained entry counts, so the policy can be watched rather than assumed. Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
1 parent
d0ef752532
commit
0d11421dea
23 files changed
+3736
-12
No files matched your search
@@ -20,6 +20,7 @@ language =
|
||||
.polish = Polski
|
||||
.chinese-traditional = 繁體中文
|
||||
.bulgarian = Български
|
||||
.greek = Ελληνικά
|
||||
|
||||
action =
|
||||
.close = إغلاق
|
||||
|
||||
@@ -20,6 +20,7 @@ language =
|
||||
.polish = Polski
|
||||
.chinese-traditional = 繁體中文
|
||||
.bulgarian = Български
|
||||
.greek = Ελληνικά
|
||||
|
||||
action =
|
||||
.close = Затвори
|
||||
|
||||
@@ -20,6 +20,7 @@ language =
|
||||
.polish = Polski
|
||||
.chinese-traditional = 繁體中文
|
||||
.bulgarian = Български
|
||||
.greek = Ελληνικά
|
||||
|
||||
action =
|
||||
.close = Zavřít
|
||||
|
||||
@@ -20,6 +20,7 @@ language =
|
||||
.polish = Polski
|
||||
.chinese-traditional = 繁體中文
|
||||
.bulgarian = Български
|
||||
.greek = Ελληνικά
|
||||
|
||||
action =
|
||||
.close = Schließen
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -20,6 +20,7 @@ language =
|
||||
.polish = Polski
|
||||
.chinese-traditional = 繁體中文
|
||||
.bulgarian = Български
|
||||
.greek = Ελληνικά
|
||||
|
||||
action =
|
||||
.close = Close
|
||||
|
||||
@@ -20,6 +20,7 @@ language =
|
||||
.polish = Polski
|
||||
.chinese-traditional = 繁體中文
|
||||
.bulgarian = Български
|
||||
.greek = Ελληνικά
|
||||
|
||||
action =
|
||||
.close = Cerrar
|
||||
|
||||
@@ -20,6 +20,7 @@ language =
|
||||
.polish = Polski
|
||||
.chinese-traditional = 繁體中文
|
||||
.bulgarian = Български
|
||||
.greek = Ελληνικά
|
||||
|
||||
action =
|
||||
.close = Sulje
|
||||
|
||||
@@ -20,6 +20,7 @@ language =
|
||||
.polish = Polski
|
||||
.chinese-traditional = 繁體中文
|
||||
.bulgarian = Български
|
||||
.greek = Ελληνικά
|
||||
|
||||
action =
|
||||
.close = Fermer
|
||||
|
||||
@@ -20,6 +20,7 @@ language =
|
||||
.polish = Polski
|
||||
.chinese-traditional = 繁體中文
|
||||
.bulgarian = Български
|
||||
.greek = Ελληνικά
|
||||
|
||||
action =
|
||||
.close = बंद करें
|
||||
|
||||
@@ -20,6 +20,7 @@ language =
|
||||
.polish = Polski
|
||||
.chinese-traditional = 繁體中文
|
||||
.bulgarian = Български
|
||||
.greek = Ελληνικά
|
||||
|
||||
action =
|
||||
.close = Bezárás
|
||||
|
||||
@@ -20,6 +20,7 @@ language =
|
||||
.polish = Polski
|
||||
.chinese-traditional = 繁體中文
|
||||
.bulgarian = Български
|
||||
.greek = Ελληνικά
|
||||
|
||||
action =
|
||||
.close = Chiudi
|
||||
|
||||
@@ -20,6 +20,7 @@ language =
|
||||
.polish = Polski
|
||||
.chinese-traditional = 繁體中文
|
||||
.bulgarian = Български
|
||||
.greek = Ελληνικά
|
||||
|
||||
action =
|
||||
.close = 閉じる
|
||||
|
||||
@@ -20,6 +20,7 @@ language =
|
||||
.polish = Polski
|
||||
.chinese-traditional = 繁體中文
|
||||
.bulgarian = Български
|
||||
.greek = Ελληνικά
|
||||
|
||||
action =
|
||||
.close = 닫기
|
||||
|
||||
@@ -20,6 +20,7 @@ language =
|
||||
.polish = Polski
|
||||
.chinese-traditional = 繁體中文
|
||||
.bulgarian = Български
|
||||
.greek = Ελληνικά
|
||||
|
||||
action =
|
||||
.close = Sluiten
|
||||
|
||||
@@ -20,6 +20,7 @@ language =
|
||||
.polish = Polski
|
||||
.chinese-traditional = 繁體中文
|
||||
.bulgarian = Български
|
||||
.greek = Ελληνικά
|
||||
|
||||
action =
|
||||
.close = Zamknij
|
||||
|
||||
@@ -20,6 +20,7 @@ language =
|
||||
.polish = Polski
|
||||
.chinese-traditional = 繁體中文
|
||||
.bulgarian = Български
|
||||
.greek = Ελληνικά
|
||||
|
||||
action =
|
||||
.close = Fechar
|
||||
|
||||
@@ -20,6 +20,7 @@ language =
|
||||
.polish = Polski
|
||||
.chinese-traditional = 繁體中文
|
||||
.bulgarian = Български
|
||||
.greek = Ελληνικά
|
||||
|
||||
action =
|
||||
.close = Замкнуть
|
||||
|
||||
@@ -20,6 +20,7 @@ language =
|
||||
.polish = Polski
|
||||
.chinese-traditional = 繁體中文
|
||||
.bulgarian = Български
|
||||
.greek = Ελληνικά
|
||||
|
||||
action =
|
||||
.close = Kapat
|
||||
|
||||
@@ -20,6 +20,7 @@ language =
|
||||
.polish = Polski
|
||||
.chinese-traditional = 繁體中文
|
||||
.bulgarian = Български
|
||||
.greek = Ελληνικά
|
||||
|
||||
action =
|
||||
.close = 关闭
|
||||
|
||||
@@ -20,6 +20,7 @@ language =
|
||||
.polish = Polski
|
||||
.chinese-traditional = 繁體中文
|
||||
.bulgarian = Български
|
||||
.greek = Ελληνικά
|
||||
|
||||
action =
|
||||
.close = 關閉
|
||||
|
||||
+6
-1
@@ -65,10 +65,12 @@ pub enum Language {
|
||||
PlPl,
|
||||
#[serde(rename = "zh-TW")]
|
||||
ZhTw,
|
||||
#[serde(rename = "el-GR")]
|
||||
ElGr,
|
||||
}
|
||||
|
||||
impl Language {
|
||||
pub const ALL: [Language; 21] = [
|
||||
pub const ALL: [Language; 22] = [
|
||||
Language::System,
|
||||
Language::EnUs,
|
||||
Language::BgBg,
|
||||
@@ -78,6 +80,7 @@ impl Language {
|
||||
Language::FrFr,
|
||||
Language::FiFi,
|
||||
Language::DeDe,
|
||||
Language::ElGr,
|
||||
Language::HuHu,
|
||||
Language::ItIt,
|
||||
Language::JaJp,
|
||||
@@ -115,6 +118,7 @@ impl Language {
|
||||
Language::HuHu => vec!["hu-HU".parse().expect("valid locale")],
|
||||
Language::PlPl => vec!["pl-PL".parse().expect("valid locale")],
|
||||
Language::ZhTw => vec!["zh-TW".parse().expect("valid locale")],
|
||||
Language::ElGr => vec!["el-GR".parse().expect("valid locale")],
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,6 +145,7 @@ impl Language {
|
||||
Language::HuHu => crate::tr!("language", "hungarian"),
|
||||
Language::PlPl => crate::tr!("language", "polish"),
|
||||
Language::ZhTw => crate::tr!("language", "chinese-traditional"),
|
||||
Language::ElGr => crate::tr!("language", "greek"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+29
-11
@@ -920,22 +920,40 @@ impl shader::Primitive for Primitive {
|
||||
let built = match cached {
|
||||
Some(entry) => entry,
|
||||
None => {
|
||||
// Free the superseded content BEFORE allocating its
|
||||
// replacement. Every geometry edit mints a new content
|
||||
// id, so a miss here means this slot's current buffers
|
||||
// are already dead weight — and on a large drawing one
|
||||
// set is most of the card (measured: 1.3 GB of a 2 GB
|
||||
// GT 1030). Holding the old set while the new one is
|
||||
// allocated is what exhausts VRAM after the first edit.
|
||||
//
|
||||
// Dropping this slot's handles first is what makes the
|
||||
// retain below able to see the old entry as garbage; a
|
||||
// pane still drawing that content keeps its own clone,
|
||||
// so shared geometry is never pulled out from under it.
|
||||
inner.gpu_wires = std::sync::Arc::new(Vec::new());
|
||||
inner.gpu_block_wires = std::sync::Arc::new(Vec::new());
|
||||
// Entries no slot still holds: only the cache references
|
||||
// them. An entry drawn by any pane keeps a strong count
|
||||
// ≥ 2, so this never drops live geometry.
|
||||
let held_before = pipeline.wire_buffer_cache.len();
|
||||
pipeline.wire_buffer_cache.retain(|_, (w, b, _)| {
|
||||
std::sync::Arc::strong_count(w) > 1
|
||||
|| std::sync::Arc::strong_count(b) > 1
|
||||
});
|
||||
if _perf {
|
||||
crate::perf_record!(
|
||||
"[perf] wire-cache evicted={} held={}",
|
||||
held_before - pipeline.wire_buffer_cache.len(),
|
||||
pipeline.wire_buffer_cache.len(),
|
||||
);
|
||||
}
|
||||
let entry =
|
||||
inner.build_wire_buffers(device, queue, &vp_wires[..], &draw_depths);
|
||||
pipeline
|
||||
.wire_buffer_cache
|
||||
.insert(vp.wire_content_id, entry.clone());
|
||||
// Evict entries no slot still holds (only the cache
|
||||
// references them). An entry drawn by any pane keeps a
|
||||
// strong count ≥ 2, so this never drops live geometry.
|
||||
if pipeline.wire_buffer_cache.len() > 16 {
|
||||
pipeline
|
||||
.wire_buffer_cache
|
||||
.retain(|_, (w, b, _)| {
|
||||
std::sync::Arc::strong_count(w) > 1
|
||||
|| std::sync::Arc::strong_count(b) > 1
|
||||
});
|
||||
}
|
||||
entry
|
||||
}
|
||||
};
|
||||
|
||||
Reference in new issue
Block a user