# UIKit 12: Core Animation va Offscreen Rendering — Render Server, 120Hz ProMotion va GPU Optimizatsiyasi

> **Holat:** 2026-yil sentabr. Ko'plab dasturchilar chiroyli soya (`shadow`), yumaloq burchak (`cornerRadius`) va shaffoflik (`alpha`) qo'shgach, ro'yxatni aylantirishda (scrolling) interfeys nega sakrab-sakrab qotayotganini (stutter / frame drop) tushunmay hayron bo'lishadi. ProMotion (120 Hz) ekranlar davrida har bir freymni chizish uchun tizimda bor-yo'g'i **8.33 millisoniya** vaqt bor! Ushbu darsda Core Animation'ning Render Server arxitekturasi, GPU'dagi Offscreen Rendering jarayoni, uning nima sababdan quvvat va unumdorlik qotili ekani hamda professional optimizatsiya usullari ko'rib chiqiladi.

## Mundarija

1. [Core Animation Render Pipeline: Commit'dan Ekrangacha](#render-pipeline)
2. [120 Hz ProMotion: 8.33 ms freym byudjeti](#frame-budget)
3. [Offscreen Rendering nima va u nega xavfli?](#offscreen-rendering)
4. [Klassik 4 ta Offscreen "jinoyatchi" va ularning mexanikasi](#culprits)
5. [Xcode Instruments bilan diagnostika: Color Offscreen-Rendered](#profiling)
6. [Xirurgik yechimlar: shadowPath, CAShapeLayer va shouldRasterize](#solutions)
7. [Amaliy kod: 120 FPS uchun optimizatsiya qilingan HighPerformanceCardView](#example)
8. [Senior Interview Savollari](#interview)
9. [Xulosa](#xulosa)
10. [Manbalar](#manbalar)

---

## 1. Core Animation Render Pipeline: Commit'dan Ekrangacha {#render-pipeline}

iOS tizimida `UIView` aslida chizish ishlarini o'zi bajarmaydi. U shunchaki `CALayer` uchun foydalanuvchi teginishlarini (touch events) boshqaruvchi yupqa qobiqdir. Ekranga chizishning butun mexanizmi **Core Animation Render Pipeline** orqali amalga oshadi:

```text
+-------------------+      IPC       +--------------------+      Draw       +---------------+
|   Bizning Ilova   | -------------> |   Render Server    | --------------> |      GPU      |
| (Main RunLoop)    | (mach port)    | (backboardd)       | (Metal / OpenGL)| (Frame Buffer)|
+-------------------+                +--------------------+                 +---------------+
         │                                     │                                    │
    1. Layout                             3. Decode                            5. Rasterize
    2. Display (draw)                     4. GPU buyruqlari                    6. Display
    3. Prepare (images)
    4. Commit
```

### Pipeline'ning 4 asosiy fazasi:
1. **Commit Transaction (Bizning ilovada):**
   * **Layout:** Auto Layout orqali view'larning `bounds` va `center` koordinatalari hisoblanadi.
   * **Display:** Agar `draw(_:)` chaqirilgan bo'lsa, CPU xotirasida bitmap chiziladi.
   * **Prepare:** Rasmlar (masalan, JPEG/PNG) ochiladi (decompress qilinadi).
   * **Commit:** Barcha layer daraxti (Layer Tree) serialize qilinib, **Render Server** jarayoniga IPC (Inter-Process Communication) orqali yuboriladi.

2. **Render Server (Alohida tizim jarayoni):**
   * Layer daraxtini qabul qiladi va uni GPU tushunadigan chizish buyruqlariga (Metal commands) aylantiradi.

3. **GPU (Grafik protsessor):**
   * Primitiv shakllar va teksturalarni piksellarga aylantiradi (Rasterization) va to'g'ridan-to'g'ri ekranning **Onscreen Framebuffer**iga chizadi.

---

## 2. 120 Hz ProMotion: 8.33 ms freym byudjeti {#frame-budget}

iPhone 13 Pro'dan boshlab barcha zamonaviy qurilmalarda ProMotion (120 Hz) displeylar o'rnatilgan:

| Ekran chastotasi | Bitta freym uchun byudjet | Izoh |
| :--- | :--- | :--- |
| **60 Hz** | **16.67 ms** | Standart displeylar. Freym kechiksa, 30 FPS'ga tushadi. |
| **120 Hz ProMotion** | **8.33 ms** | Ultra silliq harakat. GPU va CPU faqat 8.33 ms ichida butun Render Loop'ni yakunlashi shart! |

Agar bitta freymni chizish 8.4 ms vaqt olsa, o'sha freym tashlab yuboriladi (**Hitch / Frame Drop**) va foydalanuvchi buni ko'z bilan qotish (lag) sifatida sezadi.

---

## 3. Offscreen Rendering nima va u nega xavfli? {#offscreen-rendering}

Odatda GPU to'g'ridan-to'g'ri ekranga chiqariladigan xotira buferiga (**Onscreen Framebuffer**) chizadi:

```text
[ GPU ] ──── To'g'ridan-to'g'ri chizish ────► [ Onscreen Framebuffer ] ────► [ Ekran ]
```

Ammo ba'zi holatlarda GPU elementni darhol ekranga chiza olmaydi. Sababi: elementning yakuniy ko'rinishi bir nechta qatlamlar birlashuviga, burchaklar kesilishiga yoki uning orqasidagi obyektlar bilan aralashuviga bog'liq bo'ladi.

Bunday paytda GPU ekrandan tashqaridagi xotirada qo'shimcha vaqtinchalik bufer (**Offscreen Framebuffer**) ajratadi:

```text
                    +──────────────────────────────+
                    |    Offscreen Framebuffer     |
                    |    (Vaqtinchalik xotira)     |
                    +──────────────────────────────+
                               ▲          │
            1. Context Switch  │          │  2. Render natijasini nusxalash
                               │          ▼
[ GPU ] ──────────────────────────────────────────► [ Onscreen Framebuffer ] ────► [ Ekran ]
```

### Nega bu o'ta qimmat? (Context Switch):
1. **Pipeline to'xtashi:** GPU asosiy ekran buferidan offscreen buferga o'tishi kerak. Bunga **Context Switch** deyiladi.
2. **Xotira o'tkazuvchanligi (Memory Bandwidth):** Offscreen buferdagi piksellar tayyor bo'lgach, GPU ularni asosiy Onscreen buferga qayta nusxalashi (blit) kerak.
3. Agar ro'yxatda (UITableView/UICollectionView) 50 ta shunday karta bo'lsa, har bir freymda yuzlab Context Switch yuz beradi va 8.33 ms byudjet darhol portlaydi!

---

## 4. Klassik 4 ta Offscreen "jinoyatchi" va ularning mexanikasi {#culprits}

Quyidagi 4 holat deyarli har doim Offscreen Rendering keltirib chiqaradi:

### 1. `layer.masksToBounds = true` + `layer.cornerRadius`
Faqat `layer.cornerRadius` berish Offscreen Rendering chaqirmaydi (chunki u faqat background va borderni yumaloqlaydi). Ammo agar ichida rasm bo'lsa va siz:
```swift
import UIKit

@MainActor
func causeMasksToBoundsOffscreen(view: UIView) {
    view.layer.cornerRadius = 16
    view.layer.masksToBounds = true // yoki clipsToBounds = true
}
```
deb yozsangiz, GPU butun subview ierarxiyasini vaqtinchalik buferga chizib, so'ng maska bo'yicha burchaklarini kesishi kerak bo'ladi.

### 2. Dinamik Soya (`layer.shadowColor`) shadowPath'siz
Agar siz:
```swift
import UIKit

@MainActor
func causeDynamicShadowOffscreen(view: UIView) {
    view.layer.shadowColor = UIColor.black.cgColor
    view.layer.shadowOpacity = 0.5
    view.layer.shadowRadius = 8
    // shadowPath berilmagan!
}
```
GPU view'ning ichidagi barcha harf, rasm va chegaralarni bilmaguncha, soyaning shakli qanaqa bo'lishini hisoblay olmaydi! Shuning uchun u avval butun view'ni offscreen buferga render qilib, so'ng uning siluetidan soya yasaydi.

### 3. `layer.mask` (Maskalash)
Har qanday `CALayer` maskasi (masalan, gradient maska) ikkita buferni birlashtirishni talab qiladi.

### 4. `layer.allowsGroupOpacity = true` (Guruhli shaffoflik)
Agar view'ning o'zida ham, uning subview'larida ham `alpha < 1.0` bo'lsa, GPU ularning ustma-ust tushgan qismlarini to'g'ri hisoblash uchun oldin ularni guruh qilib offscreen'da chizib oladi.

---

## 5. Xcode Instruments bilan diagnostika: Color Offscreen-Rendered {#profiling}

Offscreen Rendering'ni ko'z bilan topib bo'lmaydi. Uni aniqlashning ikkita rasmiy yo'li bor:

### 1. iOS Simulyator menyusida:
* **Debug** -> **Color Offscreen-Rendered Yellow** ni yoqing.
* Ekranda Offscreen Rendering bo'layotgan barcha view'lar **sariq rangda (Yellow highlight)** yonadi!
* Agar ro'yxatni scroll qilganingizda hamma yoq sariqqa aylansa — demak GPU qiynalmoqda.

### 2. Xcode Instruments (Core Animation template):
* `Cmd + I` -> **Core Animation** profilini tanlang.
* Real qurilmada profiling o'tkazing:
  * **Hitch Time Ratio:** 1 soniyada necha ms qotish borligi (maqsad: `< 5 ms/s`).
  * **Frame Rate:** 120 FPS'dan tushib ketgan joylarni ko'rsatadi.

---

## 6. Xirurgik yechimlar: shadowPath, CAShapeLayer va shouldRasterize {#solutions}

Senior dasturchilar yuqoridagi 4 ta muammoni quyidagicha hal qiladilar:

### Yechim 1: `shadowPath` orqali 0-ms soya (Eng muhim!)
GPU'ga soyaning aniq geometriyasini oldindan berib qo'ysangiz, u subview'larni kutib o'tirmaydi va Offscreen Rendering **0 ga tushadi**:

```swift
import UIKit

@MainActor
func applyOptimizedShadow(to view: UIView, cornerRadius: CGFloat) {
    view.layer.shadowColor = UIColor.black.cgColor
    view.layer.shadowOpacity = 0.15
    view.layer.shadowRadius = 10
    view.layer.shadowOffset = CGSize(width: 0, height: 4)

    // GPU'ga aniq yo'l beriladi — Offscreen butunlay yo'qoladi:
    view.layer.shadowPath = UIBezierPath(
        roundedRect: view.bounds,
        cornerRadius: cornerRadius
    ).cgPath
}
```

> **Eslatma:** Agar view o'lchami (`bounds`) o'zgarsa (masalan, ekranning aylanishida yoki Auto Layout'da), `shadowPath` ni `layoutSubviews()` ichida yangilab turish shart!

---

### Yechim 2: `CAShapeLayer` orqali burchaklarni kesish
`masksToBounds = true` o'rniga faqat kerakli burchaklarni `UIBezierPath` bilan kesuvchi shape layer ishlatiladi yoki rasmlar Core Graphics yordamida fon oqimida (background thread) burchaklari kesilgan holda tayyorlanadi.

---

### Yechim 3: `shouldRasterize = true` (Qachon ishlatish kerak?)
Agar view juda murakkab bo'lsa (masalan, ichida ko'plab vektor belgilar, yaltiroq gradientlar bo'lsa), lekin uning o'zi **o'zgarmas (statik)** bo'lsa:

```swift
import UIKit

@MainActor
func cacheComplexStaticLayer(layer: CALayer) {
    layer.shouldRasterize = true
    // Ekran piksellar zichligini hisobga olish shart, aks holda rasm xiralashadi:
    layer.rasterizationScale = UIScreen.main.scale
}
```

* **Mexanikasi:** GPU bu view'ni bir marta offscreen buferda chizadi va xotirada (kashda) saqlab qo'yadi. Keyingi freymlarda shunchaki tayyor rasm sifatida ko'rsatadi.
* **Qachon ishlatilmaydi:** Agar view harakatchan yoki o'zgaruvchan bo'lsa (masalan, matn tez-tez o'zgarsa), har safar keshni yangilash Offscreen Rendering'dan ham ko'ra ko'proq xotira va quvvat sarflaydi!

---

## 7. Amaliy kod: 120 FPS uchun optimizatsiya qilingan HighPerformanceCardView {#example}

Quyida zamonaviy iOS ilovalarida 120 FPS bilan bexato ishlaydigan professional karta komponenti keltirilgan:

```swift
import UIKit

/// 120 Hz ProMotion ekranlarida hech qanday Offscreen Rendering hosil qilmaydigan
/// yuqori unumdorlikka ega karta komponenti.
public final class HighPerformanceCardView: UIView {

    private let titleLabel: UILabel = {
        let label = UILabel()
        label.translatesAutoresizingMaskIntoConstraints = false
        label.font = .systemFont(ofSize: 17, weight: .semibold)
        label.textColor = .label
        return label
    }()

    private let avatarImageView: UIImageView = {
        let iv = UIImageView()
        iv.translatesAutoresizingMaskIntoConstraints = false
        iv.contentMode = .scaleAspectFill
        // Muhim: masksToBounds o'rniga layer burchagi bilan ishlaymiz
        iv.layer.cornerRadius = 24
        iv.clipsToBounds = true
        return iv
    }()

    public override init(frame: CGRect) {
        super.init(frame: frame)
        setupView()
    }

    public required init?(coder: NSCoder) {
        super.init(coder: coder)
        setupView()
    }

    private func setupView() {
        backgroundColor = .secondarySystemBackground
        layer.cornerRadius = 16

        // 1. Soya sozlamalari
        layer.shadowColor = UIColor.black.cgColor
        layer.shadowOpacity = 0.08
        layer.shadowRadius = 12
        layer.shadowOffset = CGSize(width: 0, height: 6)

        // 2. Subview'larni joylash
        addSubview(avatarImageView)
        addSubview(titleLabel)

        NSLayoutConstraint.activate([
            avatarImageView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 16),
            avatarImageView.centerYAnchor.constraint(equalTo: centerYAnchor),
            avatarImageView.widthAnchor.constraint(equalToConstant: 48),
            avatarImageView.heightAnchor.constraint(equalToConstant: 48),

            titleLabel.leadingAnchor.constraint(equalTo: avatarImageView.trailingAnchor, constant: 12),
            titleLabel.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -16),
            titleLabel.centerYAnchor.constraint(equalTo: centerYAnchor)
        ])
    }

    public override func layoutSubviews() {
        super.layoutSubviews()

        // 3. O'lcham aniq bo'lganda shadowPath'ni yangilaymiz.
        // Bu Offscreen Rendering'ni butunlay yo'q qiladi!
        let path = UIBezierPath(roundedRect: bounds, cornerRadius: layer.cornerRadius)
        layer.shadowPath = path.cgPath
    }

    public func configure(title: String, image: UIImage?) {
        titleLabel.text = title
        avatarImageView.image = image
    }
}
```

---

## 8. Senior Interview Savollari {#interview}

### 1. `layer.cornerRadius` ning o'zi Offscreen Rendering chaqiradimi?
**Javob:** Yo'q! `cornerRadius` faqat layer'ning foni (background) va uning chegarasi (border) uchun qo'llanadi. Faqat `masksToBounds = true` yoki `clipsToBounds = true` qilinganda va subview'lar mavjud bo'lganda Offscreen Rendering yuz beradi.

### 2. Nega `shadowPath` ishlatish shart?
**Javob:** `shadowPath` berilmasa, GPU butun view va uning barcha subview'lari konturini (alfa kanalini) aniqlash uchun ularni offscreen xotiraga chizib olishga majbur bo'ladi. `shadowPath` berilganda esa GPU to'g'ridan-to'g'ri berilgan geometrik shakl bo'yicha soyani darhol chizib qo'yadi.

### 3. `shouldRasterize` qachon yordam beradi, qachon zarar keltiradi?
**Javob:** Agar murakkab subview'lar ierarxiyasiga ega view o'zgarmas (statik) bo'lsa, uni keshlab qo'yish unumdorlikni oshiradi. Ammo view tez-tez harakatlansa yoki uning kontenti o'zgarsa, har bir yangilanishda keshni qayta chizish qo'shimcha resurs talab qilib, ilovani qotirib qo'yadi.

---

## 9. Xulosa {#xulosa}

* iOS'da chizish ishlari **Bizning Ilova -> Render Server -> GPU** konveyeri bo'yicha ketadi.
* 120 Hz ProMotion ekranlarida bitta freym uchun vaqt bor-yo'g'i **8.33 ms**ni tashkil qiladi.
* **Offscreen Rendering** — GPU'ning ekrandan tashqaridagi buferga o'tib (Context Switch), so'ng uni asosiy ekranga nusxalash jarayonidir.
* Doimo soyalarga **`shadowPath`** bering.
* Interfeys unumdorligini tekshirish uchun simulyatorda **Color Offscreen-Rendered** va Xcode Instruments'da **Core Animation** vositasidan foydalaning.

---

## 10. Manbalar {#manbalar}

* [Apple Documentation: Core Animation Programming Guide](https://developer.apple.com/documentation/quartzcore)
* [WWDC: Demystify and eliminate hitches in the render loop](https://developer.apple.com/videos/play/wwdc2020/10077/)
* [WWDC: Explore ProMotion on iOS](https://developer.apple.com/videos/play/wwdc2021/10147/)
* [Apple Documentation: Optimizing ProMotion Refresh Rates](https://developer.apple.com/documentation/quartzcore/optimizing_promotion_refresh_rates_for_iphone_13_pro_and_ipad_pro)
