0d1b0eae4c
- Changed default VISION_BACKEND from CPU to GPU for better performance. - Made `map_brush_style` public to allow external access. - Updated `Engine` struct to use pooled buffers for stroke snapshots, reducing memory allocations during brush strokes. - Introduced `CompositeSnapshot` for efficient background compositing without blocking the UI thread. - Implemented `SendPtr` for safe raw pointer handling across threads. - Enhanced `commit_pending_history` to recycle buffers and improve performance. - Fixed cursor preview issues for procedural brushes by removing unnecessary preset synchronization. - Added tests and documentation for new features and performance improvements.
3.6 KiB
3.6 KiB
Snapshot Freeze Analysis — Çizim Bitimindeki Takılma
Problem
Fırça darbesi bittiğinde (pen-up), UI thread kısa süreli donuyor. Bu takılma snapshot (geri alım) mekanizmasının UI thread'de yaptığı büyük bellek tahsislerinden kaynaklanıyor.
Kök Neden
end_stroke() ve begin_stroke() fonksiyonları UI thread'de çalışır ve her biri ~33MB'lik layer.pixels.clone() çağrısı yapar. 4K canvas'ta (3840×2160×4 = ~33MB) bu kopyalamalar allocate+copy gerektirir.
Suçlu İşlemler
| # | İşlem | Dosya:Satır | Boyut | Etki |
|---|---|---|---|---|
| 1 | layer.pixels.clone() — before snapshot |
stroke_cache.rs:125 |
~33MB alloc+copy | Yüksek |
| 2 | layer.pixels.clone() — after snapshot |
stroke_cache.rs:159 |
~33MB alloc+copy | En yüksek |
| 3 | active_stroke_mask.fill(0) |
stroke_cache.rs:224 |
~8MB sıfırlama | Orta |
| 4 | raw_pixel_backup.remove() — drop |
stroke_cache.rs:219 |
~33MB deallocation | Orta |
| 5 | apply_effects_and_sync_tiles() |
partial_composite.rs:206-268 |
~99MB+ (efektli katmanlarda) | Yüksek |
Akış Diyagramı
begin_stroke() end_stroke()
───────────── ─────────────
1. mask.fill(0) [pooled, ~8MB] 1. before = stroke_before.take()
2. before = layer.pixels.clone() [33MB] 2. after = layer.pixels.clone() [33MB] ← DONMA
3. stroke_before = Some(before) 3. thread::spawn(move || { ... })
4. rebuild_below_cache() 4. mask.fill(0) [pooled]
5. raw_pixel_backup.remove()
6. cached_selection_mask = None
Mevcut Havuz Mekanizmaları (İyi)
| Buffer | Boyut | Havuzda mı? | Not |
|---|---|---|---|
active_stroke_mask |
~8MB | Evet | begin_stroke'da fill(0), stroke arası korunur |
composite_scratch |
~33MB | Evet | Render sırasında kullanılır, serbest bırakılmaz |
below_cache |
~33MB | Evet | Stroke arası korunur, below_cache_dirty ile yönetilir |
Havuzlanmayan Buffer'lar (Kötü)
| Buffer | Boyut | Neden havuzlanmıyor? |
|---|---|---|
stroke_before (before snapshot) |
~33MB | Her begin_stroke'da clone(), her end_stroke'da consume edilir |
stroke_after (after snapshot) |
~33MB | Her end_stroke'da clone(), background thread'e move edilir |
Kritik Gözlem
end_stroke() çağrıldığında fırça darbesi bitmiştir. Bir sonraki begin_stroke()'a kadar layer.pixels'e hiçbir yazılmaz. Bu, background thread'in raw pointer ile okuma yapabileceği anlamına gelir — unsafe ama güvenli.
Optimize Edilmiş Akış (Hedef)
begin_stroke() end_stroke()
───────────── ─────────────
1. mask.fill(0) [pooled] 1. before = pooled buffer'dan take
2. copy layer.pixels → pooled buf 2. copy layer.pixels → pooled after buf
3. stroke_before = Some(pooled buf) 3. both buflar → background thread
4. rebuild_below_cache() 4. mask.fill(0) [pooled]
5. background thread: sub-rect çıkar, buffer'ları geri gönder
Dosyalar
hcie-engine-api/src/stroke_cache.rs— begin_stroke, end_strokehcie-engine-api/src/lib.rs— Engine struct tanımıhcie-document/src/lib.rs— push_draw_snapshot, push_draw_snapshot_subrecthcie-protocol/src/lib.rs— Layer struct tanımı