Files
imajviewer/docs/rapor-sag-tik-donma-analizi.md

128 lines
9.9 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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ıı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/veya `Transform` (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: `isShiftPressed` false dönebilir). `_onHardwareKey` ile 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)
ı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'de `ColorFiltered` (filtre shader'ı), adım 2'de `ColorFiltered` + `Transform` rotate (rotate shader'ı) render edilir; sonra `_warmupDone = true` ile 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` + `ColorFiltered` aynı 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
1. Donma süresi yaklaşık ne kadar — 1-2sn mi, 10sn+ mi? (B1 shader derleme süresi tahminini doğrular)
2. Debug build mi kullanıyorsun (install.sh ile kurulan), yoksa release mi? → Seçenek 2'yi doğrular
3. Slider UI hâlâ isteniyor mu, yoksa önce donma çözümü mü? (Sıra: warmup → slider opsiyonel)
4. 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?)