# 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/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)

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'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?)
