9.9 KiB
Rapor — Sağ Tuş Efektlerinde Donma Analizi ve Slider UI Değerlendirmesi
Tarih: 2026-08-06 Durum: Analiz — B teşhisi netleşti, uygulama bekliyor
1. Semptom
- Sağ tuş ile kullanılan özelliklerde (contrast / saturation / brightness / rotate) uygulama geçici olarak donuyor.
- Donma süresi 15sn - 1dk arası (kısa değil, ciddi bir tek seferlik maliyet).
- Release build kullanılıyor (.deb'den kurulu) → Dart JIT hipotezi elendi, donma release AOT'ta da var.
- Donma kalıcı değil: kullanıcı "kapatayım mı" diye soruyor, "bekle" deyince kendiliğinden düzeliyor.
- Donma sırasında yapılan işlemler (sağ tık + aşağı çekme, pan) donma açıldığında yapılmış olarak görülüyor — olaylar işleniyor, sadece görsel gecikiyor.
- Sadece çok pencereli (multi-instance) kullanımda oluyor, tek pencerede olmuyor.
- Her pencere açılışında ilk efekt kullanımında bir kez oluyor; o donma açıldıktan sonra aynı pencerede tekrar olmuyor.
- Eskiden sadece rotate'te olurdu; şimdi tüm efektlerde oluyor, en çok rotate'te.
- Donan pencere kilitli kalıyor; diğer pencereler normal çalışıyor. Task bardan kapatılabiliyor.
- Kullanıcı şüphesi: klavye tuşlarının (Ctrl/Shift) basılı tutulmasıyla ilgili olabilir — "farklı bir şey olma ihtimali yüksek".
- Soru: bu özellikleri açılan kutu içinde slider UI ile yapsak takılmalar çözülür mü?
2. Mevcut Mimari (lib/widgets/image_canvas.dart)
| Katman | Mekanizma |
|---|---|
| Mod seçimi | _onPointerDown: sağ tık + _ctrlPressed/_shiftPressed state'i → contrast (yok) / rotate (Shift) / saturation (Ctrl) / brightness (Ctrl+Shift) |
| Modifier takibi | HardwareKeyboard.instance.addHandler(_onHardwareKey) — global, return false (yutmaz) |
| Değer güncelleme | _onPointerMove: dy * sensitivity → _markRenderDirty() |
| Render eşitleme | _renderTick ValueNotifier + scheduleFrameCallback — kare başına max 1 rebuild |
| Image pipeline | ResizeImage.resizeIfNeeded(cacheWidth: viewport×2) + provider memoize (03 Ağu fix) |
| Efekt | ColorFiltered (birleşik 4x5 matrix, cache'li) + rotate için Transform |
| Varsayılan | _isFilled = true (BoxFit.cover), FilterQuality.high |
3. Kök Neden Analizi — Geliştirilmiş Teşhis (kullanıcı geri bildirimiyle)
Kullanıcının verdiği yeni bilgiler adayları eliyor:
| Gözlem | Hangi adayı eler/güçlendirir |
|---|---|
| Donma geçici, kendiliğinden düzeliyor | Aday A (kalıcı frame üretimi durması) elendi → B güçlendi |
| Donma sırasında yapılan işlemler sonradan görülüyor | UI thread olayları işliyor; sadece render/raster geride → Aday A elendi, B güçlendi |
| Sadece çok pencerede oluyor | Process başına soğuk kaynak → B güçlendi |
| Her pencerede İLK efekt kullanımında bir kez, sonra tekrar olmuyor | İlk kullanım maliyeti — shader derleme / kaynak ısınması → B1 (aşağıda) |
| Eskiden sadece rotate, şimdi tüm efektlerde | Efekt başına ilk-kullanım maliyeti → B1 |
| En çok rotate'te | Rotate en pahalı (Transform + ColorFiltered birlikte) → B |
Teşhis: B1 — İlk Kullanım Render Maliyeti (shader derleme + kaynak ısınması) ⭐⭐
Semptom setinin tamamını açıklayan tek hipotez:
- Her pencere ayrı process (multi-instance). Her process'in kendi Skia/GL kaynakları soğuk başlar.
- İlk kez
ColorFiltered(color matrix shader) ve/veyaTransform(rotate) kullanıldığında llvmpipe shader'ı derler + ilgili render path'i kurar — bu tek seferlik maliyet 1-10sn+ sürebilir (yazılımsal render, 4 CPU çekirdeği). - Sonraki kullanımlarda shader cache'li → donma tekrar etmez. "Bir kere oluyor, açıldıktan sonra tekrar olmuyor" = tam olarak bu.
- Tüm efektlerde olması: brightness (03 Ağu'da eklendi, yeni birleşik 4x5 matrix) dahil her efekt kendi shader'ını ilk kez derliyor. Eskiden sadece rotate'te olması: o günlerde contrast/saturation zaten ısınmış/kullanılmıştı; rotate her oturumda en son eklenen ve en pahalı olandı.
- En çok rotate'te: rotate iki shader path'ini birleştirir (ColorFiltered matrix + Transform composite) → en uzun ilk derleme.
- Çok pencerede olması: her pencere soğuk başlar → her pencerede ilk efekt kullanımında ayrı donma. Tek pencerede donma tek seferlik olduğu için "sorun yok" gibi görünür.
Aday B — llvmpipe raster thread uzun kare (arka plan maliyeti)
- Donma anında UI thread çalışıyor (işlemler işleniyor) ama görsel gecikiyor → raster/kompozisyon kuyruğu geride.
- llvmpipe'ta her kare 15-50ms (03 Ağu ölçümü); ilk shader derlemesi bu kareleri 100x şişirir.
- B1'in "ilk kullanım" tetikleyicisi, B'nin "uzun kare" mekanizması — ikisi aynı olayın iki yüzü: soğuk kaynakla ilk efekt frame'i çok pahalı.
Aday C — Bellek baskısı / swap thrashing
- Çok pencerede olması C'yi güçlendirir (her process ayrı decode + doku). Ama "tekrar olmuyor" imzası C ile tam uymuyor (bellek baskısı her seferinde olur). İkincil faktör olabilir.
Aday D — _clamp() build sırasında findRenderObject()
- Donma yapmaz (null döner), performans etkisi minimal. Kırmızı ringa.
4. Klavye Hipotezi Değerlendirmesi (kullanıcının şüphesi)
Donmaya klavye neden olmuyor. _onHardwareKey handler'ı sadece iki boolean set/clear ediyor — maliyeti ihmal edilebilir, render tetiklemiyor, event yutmuyor. Kullanıcının "farklı bir şey" sezgisi doğru.
Ancak klavye yanlış mod tetikleme (takılma hissi) üretebilir:
- X11'de Shift durumu pointer event sırasında güvenilmez (skill'de belgeli:
isShiftPressedfalse dönebilir)._onHardwareKeyile takip doğru çözüm ama yine de key repeat / focus kaybı durumlarında state bozulabilir. - Shift basılıyken sağ tık → kullanıcı farkında olmadan rotate modu açılır → en pahalı efekt + beklenmedik davranış → "takılıyor" hissi.
- Alt + tık XFCE WM tarafından yutulur (bilinen pitfall) — zaten Ctrl/Ctrl+Shift'e geçilmişti.
5. Slider UI Değerlendirmesi (kullanıcının önerisi)
Kısa cevap: B1 (ilk kullanım shader derlemesi) teşhisi doğruysa, slider UI donmayı ÇÖZMEZ — çünkü donma render maliyetinden değil, ilk kullanımdaki tek seferlik shader derlemesinden kaynaklanıyor. Slider ile contrast'ı ilk kez açtığın anda yine aynı shader derlenecek ve yine aynı donma olacak. Ama doğru tasarımla yine de değerli:
| Bileşen | Slider UI etkisi |
|---|---|
| İlk kullanım shader derlemesi (B1) | Çözmez — shader yine o an derlenir. Çözüm warmup (aşağıda) |
| Olay hızı | Aynı kalır — slider da sürüklerken onChanged pointer hızında çağrılır. Ama ilk derleme bittikten sonra gerisi akıcıdır |
| Mod bilinirliği | Çözer — hangi modda olduğun görünür; gizli modifier mapping'i biter. "Takılıyor" hissinin büyük kısmı modun ne olduğunu bilmemektir |
| Klavye bağımlılığı | Tamamen kaldırır — HardwareKeyboard handler'ına, X11 Shift güvenilmezliğine, Alt yutma pitfall'ına gerek kalmaz |
| Render maliyeti (ilk derleme sonrası) | Ancak tasarımla düşer — onChangeEnd'de uygula + sürükleme sırasında düşük çözünürlüklü önizleme (küçük cacheWidth) yapılırsa render başına maliyet ciddi düşer |
Sonuç: Slider UI tek başına "sihirli çözüm" değil. Donmanın asıl çözümü B1'e karşı: shader warmup + ilk derlemeyi kullanıcı etkileşiminden önce yaptırmak.
6. Önerilen Çözüm Yolları (B1 teşhisine göre)
Seçenek 1 — Shader Warmup (ilk derlemeyi öne çek) ⭐ UYGULANDI (2026-08-06)
Açılışta, kullanıcı daha sağ tıka basmadan efekt shader'ları derletilir:
ImageCanvas.initState→ ilk frame sonrası_startWarmup().- Görünmez 1x1 gizli katman (
_buildWarmupLayer): adım 1'deColorFiltered(filtre shader'ı), adım 2'deColorFiltered+Transformrotate (rotate shader'ı) render edilir; sonra_warmupDone = trueile katman kaldırılır. - Kullanıcı ilk sağ tıka bastığında shader'lar hazırdır → donma hissi kaybolmalı.
- Maliyet: pencere açılışında 2-3 ekstra gizli frame (arka planda, etkileşim yokken).
- Not: çok pencerede her process kendi warmup'ını yapar — aynı mantık.
- Doğrulama: release build + kurulum yapıldı (2026-08-06). Kullanıcı test edecek — ilk sağ tık donması yoksa teşhis doğrulanır.
Seçenek 2 — Release build ile test (JIT yükü teyidi)
- GÜNCELLEME: Kullanıcı zaten release build kullanıyor (.deb'den kurulu) — JIT hipotezi elendi. Donma release AOT'ta da oluyor.
- 15sn-1dk süre, llvmpipe'ta ilk render path kurulumunun (shader derleme + layer/doku hazırlığı) toplam maliyeti olabilir; "bir kez oluyor, tekrar olmuyor" imzası korunuyor.
Seçenek 3 — Rotate maliyetini düşür
- Rotate aktifken
FilterQuality.high→FilterQuality.medium(rotate sırasında görsel kalite farkı minimal, maliyet düşer). Transform+ColorFilteredaynı anda aktifken tek composite path önerilir (mevcut yapı zaten tek ColorFiltered kullanıyor).
Seçenek 4 — Slider UI (UX netliği, donma çözümü değil)
- Donma B1'den kaynaklanıyorsa slider UI donmayı çözmez; ama mod bilinirliği + klavye bağımsızlığı sağlar.
- Warmup (Seçenek 1) uygulandıktan sonra slider UI istenirse eklenir — ikisi birlikte: donma yok + kullanım net.
7. Kalan Açık Sorular
- Donma süresi yaklaşık ne kadar — 1-2sn mi, 10sn+ mi? (B1 shader derleme süresi tahminini doğrular)
- Debug build mi kullanıyorsun (install.sh ile kurulan), yoksa release mi? → Seçenek 2'yi doğrular
- Slider UI hâlâ isteniyor mu, yoksa önce donma çözümü mü? (Sıra: warmup → slider opsiyonel)
- Warmup için "açılışta arkada 1-3sn hazırlık" kabul edilir mi? (Alternatif: ilk efekt kullanımında 1 kez donma kabul edilir mi?)