GPU 프로파일링

게임의 GPU 사용량을 최적화시키는 법입니다.

Choose your operating system:

Windows

macOS

Linux

GPU 에는 많은 유닛들이 병렬 작업중이어서 프레임의 각기 다른 부분에 대해 각기 다른 유닛이 바인딩되는 일이 흔합니다. 벙목현상과 GPU 병목현상이 무엇인지 찾을 때는 GPU 비용이 어디로 가는지 살펴보는 것이 좋습니다.

GPU 비용은 어디로 가는가

ProfileGPU 명령으로 여러 패스의 GPU 비용과, 가끔은 드로 콜 까지도 빠르게 알아낼 수 있습니다. 마우스 기반 UI 또는 텍스트 버전을 사용할 수도 있습니다. r.ProfileGPU.ShowUI 로 UI 를 억제시킬 수 있습니다. 데이터는 GPU 타임스탬프를 기반으로 하며, 보통 꽤나 정확합니다. 일정한 최적화로 수치의 안정성이 떨어질 수 있으며, 수치를 비판적으로 보는 것이 좋습니다. 몇몇 드라이버의 경우 셰이더 사용 이후 셰이더 비용을 몇 초 최적화시켜주는 경향이 있는 것을 발견했습니다. 이는 주목할만한 것으로, 약간 기다렸다가 한 번 더 측정하여 확신을 더하는 것도 좋을 수 있습니다.

ProfileGPU.png

콘솔: ProfileGPU

단축키: Ctrl+Shift+,

...
 1.2% 0.13ms   ClearTranslucentVolumeLighting 1 draws 128 prims 256 verts
42.4% 4.68ms   Lights 0 draws 0 prims 0 verts
   42.4% 4.68ms   DirectLighting 0 draws 0 prims 0 verts
       0.8% 0.09ms   NonShadowedLights 0 draws 0 prims 0 verts
          0.7% 0.08ms   StandardDeferredLighting 3 draws 0 prims 0 verts
          0.1% 0.01ms   InjectNonShadowedTranslucentLighting 6 draws 120 prims 240 verts
      12.3% 1.36ms   RenderTestMap.DirectionalLightImmovable_1 1 draws 0 prims 0 verts
          1.4% 0.15ms   TranslucencyShadowMapGeneration 0 draws 0 prims 0 verts
...

ProfileGPU 에는 라이트 이름이 표시되어, 아티스트가 올바른 라이트 소스를 최적화하기가 쉬워집니다.

각 프레임마다 하이 레벨 비용을 살펴보고 적정 수준에 대한 감을 잡는 것이 좋습니다 (예로 드로 콜이 무겁고, 머티리얼이 복잡하고, 트라이앵글 메시가 빽빽하고, 뷰 거리가 멀고 등):

  • EarlyZPass: 기본적으로 부분 Z 패스를 사용합니다. DBuffer 데칼은 풀 Z 패스를 요합니다. 이는 r.EarlyZPass r.EarlyZPassMovable 로 커스터마이징 가능합니다.

  • 베이스 패스: 디퍼드 사용시 단순 머티리얼은 대역폭에 묶일 수 있습니다. 실제 버텍스 및 픽셀 셰이더는 머티리얼 그래프에 정의됩니다. 동적인 오브젝트에는 간접광에 대한 부가 비용이 있습니다.

  • 섀도 맵 렌더링: 실제 버텍스 및 픽셀 셰이더는 머티리얼 그래프에 정의됩니다. 픽셀 셰이더는 마스크드 또는 반투명 머티리얼에만 사용됩니다.

  • 그림자 투사/필터링: r.ShadowQuality 으로 셰이더 비용을 조절합니다. 대부분의 라이트에 그림자 드리우기를 끕니다. 스태틱 또는 스테이셔너리 라이트를 고려해 보세요.

  • 오클루전 컬링: HZB 오클루전에는 고정비가 높으나 오브젝트별 비용은 작습니다. r.HZBOcclusion 로 토글, 켜지 않고 더 나아보이나 확인해 보세요.

  • 디퍼드 라이팅: 건드린 픽셀에 비례하며, 라이트 함수, IES 프로파일, 그림자 받기, 에어리어 라이트, 복잡한 셰이딩 모델과 함께 더욱 비싸집니다.

  • 타일드 디퍼드 라이팅: r.TiledDeferredShading 로 GPU 라이트를 토글시켜 끄거나, r.TiledDeferredShading.MinimumCount 로 타일드 메서드 또는 논-디퍼드 메서드 사용 시기를 결정합니다.

  • 인바이런먼트 리플렉션: r.NoTiledReflections 로 토글시켜 프로브가 매우 적지 않은 이상 보통은 느린 논-타일드 메서드를 사용합니다.

  • 앰비언트 오클루전: 퀄리티 조절이 가능하며, 복수의 패스를 사용하여 효율적인 커다란 이펙트를 만들 수 있습니다.

  • 포스트 프로세싱: 몇몇 패스는 공유되므로, 표시 플래그를 토글시키면서 이펙트가 퍼포먼스 가치가 있는지 확인해 봅니다.

어떤 패스는 잇따르는 패스에 효과가 있을 수 있습니다. 몇 가지 예제라면:

  • EarlyZ 풀 패스는 추가적인 드로 콜과 GPU 비용이 들지만, 베이스 패스에서의 픽셀 처리를 피할 수 있어 그 비용을 크게 줄일 수 있습니다.

  • HZB 최적화로 인해 컬링이 보다 보수화될 수가 있습니다.

  • 그림자를 켜면, 화면의 큰 부분이 그림자 안에 있는 경우 라이트의 라이팅 비용이 줄어들 수 있습니다.

GPU 병목현상이란?

종종 퍼포먼스 비용은 픽셀 양에 비례합니다. 그에 대한 테스트로 r.SetRes 를 사용해서 렌더링 해상도를 바꾸거나 에디터에서 뷰포트 스케일을 조절할 수 있습니다. r.ScreenPercentage 를 사용하면 훨씬 더 편리해지지만, 염두에 둘 것은 이 기능이 사용되면 부가적인 업샘플링 비용이 생긴다는 점입니다.

퍼포먼스 변화가 측정되는 경우, 픽셀 관련된 무언가에 묶인 것입니다. 보통 메모리 대역폭 (읽기 쓰기) 또는 연산 (ALU) 에 묶인 경우지만, 희귀한 경우 특정 유닛이 포화되는 경우가 있습니다 (예: MRT 익스포트). 관련된 패스에서 메모리( 또는 연산)를 낮추어 퍼포먼스 차이가 확인 가능한 경우, 메모리 대역폭( 또는 ALU 장치)에 묵였음을 알 수 있습니다. 변화가 꼭 같지는 않을 수 있습니다. 테스트일 뿐이니까요. 그저 이제 그 쪽 비용을 줄여야 퍼포먼스가 향상된다는 것을 알았습니다.

섀도 맵 해상도는 화면 해상도에 비례하지 않지만 ( r.Shadow.MaxResolution 사용), 그림자를 드리우는 마스크드 또는 반투명 머티리얼로 된 매우 커다란 영역이 있지 않고서야, 픽셀 셰이더에 묶이지 않습니다. 종종 섀도 맵 렌더링은 버텍스 처리 또는 트라이앵글 처리에 묶입니다 (원인: 빽빽한 메시, LOD 없음, 테셀레이션, 월드 포지션 오프셋 사용 등). 섀도 맵 렌더링 비용은 라이트 수, 캐스케이드/큐브맵 면 수, 라이트 프러스텀 내 그림자를 드리우는 오브젝트 수에 비례합니다. 매우 자주 병목현상이 발생하는 곳으로, 커다란 콘텐츠 변화만이 비용을 줄일 수 있습니다.

테셀레이션이 심한 메시, 와이어프레임이 단색으로 표시되는 부분은 쿼드 활용도가 떨어지는 문제를 겪을 수 있습니다. 왜냐하면 GPU 가 2x2 픽셀 블록의 트라이앵글을 처리한 뒤 약간 후 트라이앵글 밖의 픽셀을 거부하기 때문입니다. 밉맵 계산에 필요한 부분입니다. 더 큰 트라이앵글의 경우, 이것이 문제가 되지는 않지만, 트라이앵글이 작거나 매우 긴 경우 처리되는 픽셀은 많아도 이미지에 실제로 공헌하는 것은 적어 퍼포먼스 문제가 생길 수 있습니다. 이러한 상황은 디퍼드 셰이딩으로 개선되기는 하는데, 라이팅에서의 쿼드 활용도가 매우 좋기 때문입니다. 하지만 문제는 베이스 패스 도중에도 지속되므로 복잡한 머티리얼은 꽤나 느리게 렌더링될 수 있습니다. 이에 대한 해법은 밀도가 덜한 메시를 사용하는 것입니다. 레벨 오브 디테일 메시를 통해 (원거리에서) 문제가 되는 부분만 처리해 줄 수 있습니다.

r.EarlyZPass 를 조절하면서 씬이 Early Z 풀 패스의 혜택을 제대로 보고 있는지 확인할 수 있습니다 (베이스 패스 도중 오버드로는 줄고 드로 콜은 늘고).

해상도를 변경해도 별다른 변화가 없다면, 버텍스 처리 (버텍스 셰이더, 또는 테셀레이션) 비용에 묶여있을 수가 있습니다. 종종, 콘텐츠를 바꿔보면 확인이 가능할 것입니다. 전형적인 원인이라면:

  • 버텍스가 너무 많은 경우 (레벨 오브 디테일 메시 사용)

  • 조악한 밉맵의 텍스처를 사용한 복잡한 WorldPositionOffset / Displacement 머티리얼인 경우 (머티리얼 조정)

  • 테셀레이션 (가급적 피하고, 테셀레이션 팩터를 조절합니다. 가장 빠른 방법: show Tessellation , 일부 하드웨어는 테셀레이션 레벨이 클 수록 심하게 영향받습니다.)

  • UV 또는 노멀 이음새가 많아 버텍스가 많아진 경우 (펼친 UV 확인 - 아일랜드가 많으면 나쁘고, 평면 셰이딩된 메시에는 트라이앵글당 버텍스가 셋).

  • 버텍스 특성이 너무 많은 경우 (여분의 UV 채널)

  • 버텍스 수가 적절한지 확인, 임포터 코드가 버텍스를 결합하지 못했을 수가 있는 경우 (위치, UV, 노멀이 같은 버텍스 합침).

빈도는 덜하지만, 다른 것에 묶일 수도 있습니다. 가능한 예라면:

  • 오브젝트 비용 (거의 CPU 지만 GPU 비용도 약간 있을 수 있습니다).

  • 트라이앵글 셋업 비용 (폴리곤이 매우 많은 메시에 버텍스 셰이더가 싼 경우, 섀도 맵 스태틱 메시를 예로 들 수 있겠는데, 거의 문제가 안됩니다).

  • 레벨 오브 디테일 (LOD) 메시 사용

  • 뷰 비용 (예: HZB 오클루전 컬링)

  • 씬 비용 (예: GPU 파티클 시뮬레이션)

라이브 GPU 프로파일러

라이브 GPU 프로파일러는 주요 렌더링 카테고리에 대한 실시간 프레임 단위 통계를 제공해줍니다. 라이브 GPU 프로파일러를 사용하려면, ` (~) 키를 눌러 콘솔을 열고 stat GPU 라 입력한 뒤 Enter 키를 칩니다. 뷰포트 옵션 드롭다운의 통계 서브메뉴를 통해서도 라이브 GPU 프로파일러를 띄울 수 있습니다.

GPU_Stats.png

통계는 누적식이며 계층구조가 없으므로, 이벤트 트리를 파 내려가지 않고도 주요 카테고리를 확인할 수 있습니다. 예를 들어 Shadow Projection 은 모든 뷰의 모든 라이트에 대해 드리워지는 그림자 투영 전체 합입니다. 화면상 GPU 통계에는 타이틀 실행시 GPU 에 걸리는 부하에 대해 간략히 시각적으로 분석해 보여줍니다. 어떤 변화가 끼치는 영향을 즉각적으로 측정하기에도 좋은데, 예를 들어 콘솔 변수를 변경하거나, 에디터에서 머티리얼을 변경하거나, (리컴파일 셰이더를 변경하고) 즉석에서 셰이더 변경 후 다시 컴파일하는 작업 등입니다. 타이틀 실행 도중 GPU 통계를 파일에 기록한 뒤 나중에 분석할 수도 있습니다.

기존 통계와 마찬가지로 stat startfile 이나 stat stopfile 와 같은 콘솔 명령으로 통계를 ue4stats 파일에 기록한 뒤, 나중에 언리얼 프론트엔드 툴 에서 파일을 열어 시각화시켜 볼 수 있습니다.

Saved_Profile.png

언리얼 프론트엔드로 보는 GPU 프로파일입니다. 합계, 포스트 프로세싱, 베이스 패스 시간이 표시됩니다.

통계는 코드에 플로트 카운터로 선언됩니다. 예:

DECLARE_FLOAT_COUNTER_STAT(TEXT("Postprocessing"), Stat_GPU_Postprocessing, STATGROUP_GPU);

그러면 렌더링 스레드의 코드 블록은 해당 통계 이름을 참조하는 SCOPED_GPU_STAT 매크로로 인스트루먼트 시킬 수 있습니다. 이는 SCOPED_DRAW_EVENT 와 비슷하게 작동합니다. 예:

SCOPED_GPU_STAT(RHICmdList, Stat_GPU_Postprocessing);

명시적으로 인스트루먼트 하지 않은 GPU 작업은 전부 잡는 [unaccounted] 통계에 포함됩니다. 이 수치가 너무 높아지면, 놓친 작업에 대해 SCOPED_GPU_STAT 이벤트가 필요하다는 것을 나타냅니다. 말할 것도 없이 드로 이벤트와는 달리 GPU 통계는 누적식입니다. 같은 통계에 대해 중복된 항목을 여러 번 추가하면 프레임에 합산됩니다. CPU 에 묶인 특별한 경우 GPU 타이밍이 CPU 병목 (거품) 현상에 영향을 받을 수 있습니다. CPU 가 따라잡을 때까지 GPU 가 기다리는 것인데, 드로 스레드 시간이 높은 데서 예상치 못한 결과가 나오는 경우 그 점 고려해 주시기 바랍니다. PlayStation 4 에서는 타이밍을 잡을 때 명령 목록을 제출하는 그 사이 시간을 제외시키는 것으로 병목 현상을 보정합니다. 앞으로 다른 최신 렌더링 API 에도 그와 같은 기능을 확장 적용하도록 하겠습니다.

언리얼 엔진 문서의 미래를 함께 만들어주세요! 더 나은 서비스를 제공할 수 있도록 문서 사용에 대한 피드백을 주세요.
설문조사에 참여해 주세요
건너뛰기