Files
hcie-rust-v3.05/docs/compose/specs/snapshot-freeze-analysis.md
T
phantom 0d1b0eae4c fix: optimize brush stroke handling and improve cursor preview
- 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.
2026-07-11 08:53:17 +03:00

3.6 KiB
Raw Blame History

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_stroke
  • hcie-engine-api/src/lib.rs — Engine struct tanımı
  • hcie-document/src/lib.rs — push_draw_snapshot, push_draw_snapshot_subrect
  • hcie-protocol/src/lib.rs — Layer struct tanımı