본문 바로가기
iOS/설명

[iOS] High performance based drawing

by Sky Titan 2024. 2. 24.
728x90

https://medium.com/@almalehdev/high-performance-drawing-on-ios-part-1-f3a24a0dcb31

 

High performance drawing on iOS  — Part 1

How I optimized 2D drawing for my game, including what worked and what didn’t work

medium.com

https://medium.com/@almalehdev/high-performance-drawing-on-ios-part-2-2cb2bc957f6

 

High performance drawing on iOS — Part 2

This article covers two different ways to perform 2D drawing while leveraging the GPU hardware

medium.com

 iOS 앱 개발 시 View를 그리고 렌더링하는 과정에서 어떻게 하면 성능을 최적화 시킬 수 있는지에 대해서 정리한 굉장히 좋은 아티클을 발견했다.

 

draw(in:)을 touchesMoved에서 호출

 필자가 첫 번째로 구현했던 방법은 UIView의 draw() 함수를 touchesMoved에서, user가 손가락을 움직일 때마다 호출하는 것인데 iPhone 6s에선 문제가 없었으나 iPad Pro에서 100프로의 CPU 사용률을 보여줬다.

 

 iPhone 6s에서 문제가 없었던 것은 60FPS까지만 지원하기 때문이고, iPad Pro는 120FPS를 지원하고 물리적인 해상도도 더 컸기에 CPU의 성능 차이를 넘어서는 수준의 과부하가 발생했다.

 

touchesMoved에서 setNeedsDisplay호출

 두 번째로 사용한 방법은 draw를 직접 호출하는 대신, touchesMoved에서 touch된 포인트들의 정보를 Array에 담아놓고 setNeedsDisplay만 호출하는 방법이다.

 예를 들어 최대 60FPS를 지원하는 디바이스에서 60분의 1초보다 더 자주 화면을 그렸을 때 발생하는 성능 낭비를 줄이고, update cycle에 맞춰서 그리도록 최적화 시킨 방법이라고 볼 수 있다.

 

 이 방법을 사용하여 CPU 사용률이 60프로까지 떨어졌지만 여전히 계속 그림을 그릴 시 CPU 사용률이 100프로까지 올라갔다.

 

setNeedsDisplay()대신 setNeedsDisplay(in:) 호출

 그 다음 사용한 방법은 setNeedsDisplay(in:)을 호출하는 것이다. 즉 전체화면을 전부 다시 그리는 것이라 일부 영역에 대해서만 다시 그리도록 유도하는 방법이다.

 이 방법으로 무려 CPU 사용률이 20%까지 떨어졌다.

 

 하지만 여전히 앱을 계속 사용하면 CPU사용률이 증가했다.

 

이미 그려진 일부 영역을 이미지로 평탄화 시키기

 마지막에 적용한 방법은 그림을 계속 그려서 point의 개수가 일정 수준이상으로 늘어나거나, 사용자가 touch를 끝냈을 때 이 때까지 그려진 부분을 UIImage로 만드는 것이다.

 

 이 방법을 통해 앱을 계속 사용하더라도 CPU 사용률이 평균 25%를 유지하게 만들었다.

 

 

위의 부분까지는 CPU base 드로잉일 때의 성능이었고 싱글 스레드인 CPU의 한계를 넘기 위해 대규모 병렬처리가 가능한 GPU base drawing에 대한 테크닉도 아래에서 공유하고 있다.

 

 기본적인 전제는 Core Graphics는 CPU, Core Animation은 GPU를 사용하기에 CoreAnimation을 사용하여 그리는 방법이다.

 

CGContext대신 Sublayer를 사용하여 GPU 사용

  앞선 내용에선 CGContext로 그림을 그렸지만 이번엔 UIView가 가지고 있는 layer에 UIBezierPath를 이용하여 그린 sublayer를 추가하여 CoreAnimation을 사용하는 방식이다. 그리고 sublayer 개수가 일정 수준 이상으로 넘어가면 UIImage로 만들어 평탄화시킨다.

이 방식을 이용하여 CPU사용률을 15%까지 줄인다.

 

draw(layer:ctx:) 오버라이드해서 CGContext사용피하기

 마지막 방법은 CALayer를 그릴 때 호출되는 draw함수를 오버라이드하여 CGContext를 사용하지 않고 Array에 저장해놓은 point정보들을 활용하여 sublayer를 한 장 그려서 추가하는 방법이다.

 

 최종적으로 CPU사용률을 11%까지 줄이게 된다.

 

 

 요약하자면

  1. draw를 직접 호출하는 대신 setNeedsDisplay사용
    -> update cycle에 맞추어 화면을 그림으로써 불필요한 drawing 방지
  2. setNeedsDisplay() 대신 setNeedsDisplay(in:)을 사용
    -> 전체 화면을 그리는게 아닌 일부 영역만 그리도록 유도
  3. 이미 그려진 영역을 UIImage로 만들기
    -> 그려진 영역을 이미지화 시켜서 성능 최적화
  4. GPU base drawing을 사용
    1. CGContext대신 CALayer를 사용
      -> GPU 사용 유도
    2. draw(layer:ctx:) 오버라이드
      -> 그릴 때 CALayer를 사용함으로써 GPU 사용을 유도하고 한 번 그려질 때 하나의 sublayer만으로 그릴 수 있게 만듬
728x90

'iOS > 설명' 카테고리의 다른 글

[iOS] '==' vs '==='  (0) 2024.03.17
[iOS] OOP와 POP는 어떤 경우에 써야 할까 (고찰)  (0) 2024.03.17
[iOS] Mirror Struct  (0) 2024.02.24
[iOS] iOS Webview에서 텍스트 복사하는법  (0) 2023.07.15
[iOS] beginBackgroundTask  (0) 2023.06.24

댓글