← All issues

Fix WebContent jetsam when pinch-zooming reddit.com

c49ecd8

Source/WebCore/platform/graphics/ca/GraphicsLayerCA.cpp

- if (pageScaleFactor * m_size.width() > cMaxPixelDimension ...)
+ if (pageScaleFactor * deviceScaleFactor * m_size.width() > cMaxPixelDimension ...)
+ const size_t maxBackingStoreBytes = 16 * 1024 * 1024;
+ if (backingStoreBytes > maxBackingStoreBytes)
+ promotToTiledLayer();

WebKit의 compositing layer tree는 GPU 가속 렌더링을 위해 IOSurface를 backing store로 사용하는 Core Animation layer로 구성됩니다. 특정 크기 임계값 이하의 layer는 단일 full-resolution backing store("non-tiled")를 사용하고, 임계값을 초과하면 가시 영역만 rasterize하는 tile cache로 전환됩니다. 이 전환 여부는 requiresTiledLayer()가 결정하며, 현재 zoom level에서의 유효 해상도를 기준으로 평가합니다.

문제의 원인은 두 코드 경로 간 scale factor 처리의 불일치였습니다. requiresTiledLayer()는 tiling 임계값 비교 시 pageScaleFactor(논리적 zoom)만 참조했습니다. 반면 updateContentsScale()은 실제 backing store 할당에 pageScaleFactor × deviceScaleFactor(픽셀 밀도)를 사용하고 있었습니다. 3x Retina 기기에서 5x page zoom으로 접근하면, 398×398 CSS-pixel layer의 backing store는 15x 배율로 계산됩니다. 결과적으로 실제 픽셀 크기는 약 5,970×5,970에 달하고, IOSurface 하나당 ~65MB를 차지하게 됩니다. reddit.com처럼 이미지가 많은 페이지에서 이런 layer가 40개 이상 생성되면, 총 IOSurface 메모리는 ~2.6GB에 이릅니다. 이 시점에서 iOS jetsam, 즉 메모리 압박에 의한 OS 수준의 프로세스 강제 종료가 발생합니다. 수정에서는 두 가지 변경이 이루어졌습니다. 먼저 requiresTiledLayer()deviceScaleFactor를 반영하도록 수정되었습니다. 아울러 layer를 tiled backing으로 승격하기 전 16MB 바이트 상한도 명시적으로 추가되었습니다.

Scale path (before fix):
  requiresTiledLayer(): pageScaleFactor >= threshold        ← wrong
  updateContentsScale(): pageScaleFactor × deviceScaleFactor ← correct
  Result: 65MB IOSurface × 40 layers → 2.6GB → jetsam

Scale path (after fix):
  requiresTiledLayer(): pageScaleFactor × deviceScaleFactor ← corrected
  updateContentsScale(): 16MB byte-size cap enforced
  Result: layer promoted to tiled before huge allocation

재현 조건 하에서 16MB 상한 적용과 tier-promotion 로직 수정을 통해, peak IOSurface 메모리 사용량이 ~2.6GB에서 ~1.4GB로 감소되었습니다. 근본 원인인 두 관련 코드 경로 간 scale factor 적용 불일치는 WebKit compositing에서 반복적으로 등장하는 버그 패턴입니다.