# Double Buffer Architecture Research v21
## Focus: 二合一的真正瓶頸 — PaintText 的 column 範圍

---

## 核心洞察

capture 存在的唯一理由：**PaintText 只渲染 rcRect.left ~ rcRect.right 的 column，不是整行。**

```c
// gtwvt.c PaintText:
rcRect = hb_gt_wvt_GetColRowFromXYRect( pWVT, ps.rcPaint );
for( iRow = rcRect.top; iRow <= rcRect.bottom; ++iRow )
{
   iCol = startCol = rcRect.left;   // ← 從 dirty 左邊界開始
   while( iCol <= rcRect.right )     // ← 到 dirty 右邊界結束
   {
      TextOut(...)                   // ← 只畫這些 column
   }
}
```

如果 ps.rcPaint 只覆蓋 col 5-10，TextOut 只畫 col 5-10。
col 0-4 和 col 11-79 不被 TextOut 處理。

### 有 capture 時（v4）
```
capture: screen col 5-10 → buffer col 5-10
         buffer col 0-4 保留上一幀（= screen，因為上一幀 writeback 過）
         buffer col 11-79 同上
TextOut: buffer col 5-10 → 新內容
writeback: buffer（正確）→ screen
```

### 無 capture 時（v5/v6）
```
skip capture: buffer 保留上一幀的所有 column
TextOut: buffer col 5-10 → 新內容
         buffer col 0-4 保留上一幀 ← 如果上一幀 = 當前畫面，正確
         buffer col 11-79 同上      ← 如果上一幀 ≠ 當前畫面，錯誤！
writeback: buffer（可能混合新舊）→ screen
```

### 何時「上一幀 ≠ 當前畫面」？
1. 畫面切換（RestScreen）— 整個畫面改變
2. 第一次 WM_PAINT 後的第二次（如果有非 dirty 列變化）
3. 其他視窗覆蓋後移開

## 二合一的可能路徑

### 路徑 A：強制 PaintText 渲染整行
```c
rcRect.left = 0;
rcRect.right = maxCol;
```
- **優點**：消除 capture 的需要
- **問題**：之前測試造成「BUYSELL 表身欄位移動不正常」
- **需要驗證**：不正常的確切原因（可能是其他 bug）

### 路徑 B：capture 只在 dirty region ≠ 全行寬時執行
```c
if( ps.rcPaint.left > 0 || ps.rcPaint.right < clientW )
   BitBlt( buffer ← screen, ps.rcPaint );  // partial row: need capture
// else: full row width, TextOut covers everything, skip capture
```
- **優點**：大部分 dirty（全行寬）跳過 capture
- **缺點**：partial dirty 仍需 capture
- **效果**：~70% 的 WM_PAINT 可跳過（估計）

### 路徑 C：writeback 只寫 TextOut 覆蓋的 column
```c
// writeback 的範圍 = max(capture 範圍, TextOut 範圍)
// 而非 dirty row 整行
```
- **問題**：ScanAndDrawUda 可能在 TextOut 範圍外畫 UDA
- **複雜度高**

### 路徑 D：capture from buffer（自己抄自己）→ NOP
在穩定狀態下 `BitBlt(buffer ← screen)` 等於 `BitBlt(buffer ← buffer)`（因為上一幀 writeback 後 screen = buffer）。
這是 NOP。效能成本 = BitBlt 的 overhead（~0.05ms）而非實際複製（因為 Windows DWM 可能做 copy-on-write）。
**capture 的實際效能成本可能趨近於零！**

## 路徑 D 的驗證

```
穩定狀態：
  buffer 有上一幀的完整畫面
  screen 有上一幀的 writeback 結果
  兩者相同

capture: BitBlt(buffer ← screen) → 複製相同像素 → NOP
  Windows DWM 可能檢測到 source = dest content → 跳過實際複製
  即使不跳過，記憶體複製 dirty region（~50x20 pixels = 4KB）< 0.01ms

TextOut: 覆蓋 dirty cells → 有效工作

writeback: BitBlt(screen ← buffer) → 有效工作
```

### 如果路徑 D 成立
capture 的效能成本趨近零。省掉它的收益趨近零。
**二合一沒有實質意義。**

## 最終結論

**capture 的效能成本在穩定狀態下趨近零**（自己抄自己 = NOP）。
只在畫面切換時有實際複製（~0.1ms），但此時 capture 是必要的（避免 stale content）。

**二合一（跳過 capture）的收益 = ~0.01ms/幀，風險 = 畫面切換閃爍。不值得。**

**v4 full = FINAL。不再追求二合一。**

---

## 未來真正有價值的優化方向

| 方向 | 收益 | 風險 |
|------|------|------|
| 減少不必要的 WM_PAINT（DispBegin/DispEnd） | 高 | 低 |
| 減少 TextOut 的 cell 數（只畫變化的 cell） | 中 | 高 |
| DWM 整合（Direct Composition） | 高 | 非常高 |

**建議先做「減少不必要的 WM_PAINT」— 也就是在更多地方加 DispBegin/DispEnd。**
