수동 PSO 캐싱은 게임 빌드를 플레이하여 파이프라인 스테이트 오브젝트(PSO) 정보를 번들 캐시(Bundled Cache)에 수집해야 하지만, PSO 프리캐싱은 컴포넌트의 렌더링 때 사용할 수 있는 모든 PSO를 자동으로 수집하고 비동기 컴파일을 수행합니다.
PSO 프리캐싱 환경설정
다음과 같은 콘솔 변수가 PSO 프리캐싱을 제어합니다.
| 콘솔 변수 | 설명 | 디폴트 상태 |
|---|---|---|
| PSO 프리캐싱을 활성화하는 글로벌 콘솔 변수입니다. 이 콘솔 변수는 RHI 플래그 | Enabled |
| 컴포넌트가 사용하는 PSO를 프리캐싱합니다. | Enabled |
| 모든 리소스( | 비활성화됨 |
| 필요한 모든 PSO가 컴파일될 때까지 컴포넌트 프록시 생성을 대기합니다. 프록시 생성 시 PSO가 여전히 컴파일 중이라면, 해당 PSO는 높은 우선순위로 표시됩니다. | Enabled |
| 컴포넌트의 프록시 생성 중에도 여전히 PSO가 컴파일 중인 경우, 머티리얼을 디폴트 머티리얼로 대체할 수 있는 옵션을 추가합니다. 이 CVar은 | 0(아래 참조) |
| 로딩 때 높은 우선순위의 PSO만 기다립니다. 필수가 아닌 모든 PSO는 게임플레이 중에 계속 컴파일합니다. PSO는 프록시에서 필요로 하고 아직 컴파일이 끝나지 않은 경우 높은 우선순위로 표시됩니다. | 비활성화됨 |
| 엔진 스타트업 시 글로벌 컴퓨트 및 그래픽 PSO도 프리캐싱할지 여부를 지정합니다. | Enabled |
글로벌 셰이더 PSO 프리캐싱
특정 글로벌 셰이더(Global Shader) PSO는 처음 사용할 때 런타임 히치를 일으킬 수 있으므로 프리캐싱됩니다. 이러한 PSO는 엔진을 시작할 때 컴파일되며 기본적으로 r.PSOPrecache.GlobalShaders 콘솔 변수를 사용하여 활성화됩니다.
게임에서 사용할 수 있는 모든 글로벌 컴퓨트 셰이더 순열은 프리캐싱됩니다.
static EShaderPermutationPrecacheRequest ShouldPrecachePermutation(const FShaderPermutationParameters& Parameters)이는 지정된 순열을 런타임 때 사용할 수 있는지 확인하는 데 사용되며, 이를 위해 현재 콘솔 변수 설정이 특정 조합을 제외하는지 확인합니다. 기본적으로는 ShouldCompilePermutation을 사용하므로 프리캐시 순열은 컴파일된 순열의 하위 집합이어야 합니다.
대부분의 글로벌 그래픽 PSO는 로딩 직후 처음 몇 프레임 동안 생성되는데, 이러한 프레임에서 히치가 발생할 수 있습니다. 매우 작은 PSO 번들 캐시를 사용하여 이러한 글로벌 그래픽 PSO를 수집하는 것도 도움이 됩니다. 그러나 특정 글로벌 그래픽 PSO 순열도 런타임에 생성되고 컴파일되므로 이 역시 프리캐싱되어야 합니다. 글로벌 그래픽 PSO의 경우, 그래픽 PSO를 컴파일하는 데 필요한 올바른 렌더 상태를 모두 수집하려면 특정 PSO 컬렉터가 필요합니다.
PSO 프리캐싱은 현재 다음과 같은 글로벌 셰이더 유형에 구현됩니다.
슬레이트
디퍼드 라이트(Deferred Lights)
캐스케이드 파티클 시뮬레이션(Cascade Particle Simulation)
볼류메트릭 포그(Volumetric fog)
스타트업 때 모든 글로벌 PSO를 컴파일하는 작업을 시간이 다소 소요되며 일반적으로 메인 메뉴를 탐색하는 동안 진행됩니다. 스타트업 때 엔진을 차단하지는 않지만 초기 로딩 화면에서 PSO 컴파일 대기 단계의 일부분이 되어야 합니다.
컴포넌트 PSO 프리캐싱
프리미티브 컴포넌트(Primitive Components)(UPrimitiveComponent)는 로딩 직후(PostLoad 중) 렌더링에 필요한 모든 PSO를 프리캐싱합니다. 이 프리캐싱은 다음과 같이 컴파일에 필요한 모든 파이프라인 스테이트 정보를 수집합니다.
머티리얼
버텍스 팩토리(Vertex factories)
버텍스 엘리먼트 정보(Vertex element information)
특정 프리캐싱 파라미터(Specific precache parameters)
언리얼 엔진은 이 정보를 사용하여 컴포넌트가 렌더링될 수 있는 가능한 모든 메시 패스 프로세서를 반복작업합니다. 각 메시 패스 프로세서는 렌더링 때 필요하면 사용할 수 있는 PSO 이니셜라이저를 추가합니다. 백그라운드 작업은 공유된 PSO 캐시를 확인하여 필요한 캐시가 프리캐싱되고 있지 않은지 확인하고 이러한 요청을 비동기식으로 컴파일합니다.
단일 컴포넌트가 베이스, 커스텀 뎁스, 깊이, 왜곡, 섀도, 가상 섀도 맵, 속도 등 수많은 패스에서 모두 올바르게 렌더링하려면 PSO가 많이 필요할 수 있습니다. 컴포넌트가 한 패스에서 렌더링되고 다른 패스에서는 렌더링되지 않을 수도 있으므로 그래픽 아티팩트가 발생하지 않게 하려면 컴포넌트를 준비하기 전에 먼저 이러한 모든 PSO를 준비하는 것이 중요합니다.
UE가 프리미티브 컴포넌트(Primitive Component)의 프리미티브 프록시(Primitive Proxy)를 생성했고 필요한 PSO를 아직 컴파일 중이라면, 다음과 같은 몇 가지 옵션이 있습니다.
PSO 컴파일이 완료될 때까지 프록시 생성을 지연시킵니다(기본값). 그러면 PSO가 준비될 때까지 드로를 효과적으로 건너뜁니다.
머티리얼을 엔진의 디폴트 머티리얼로 대체합니다.
계속 진행합니다. 히치가 발생할 수 있습니다. 드로는 PSO 컴파일 시 차단됩니다.
프록시 생성 딜레이 전략
r.PSOPrecache.ProxyCreationDelayStrategy 콘솔 변수는 r.PSOPrecache.ProxyCreationWhenPSOReady 콘솔 변수에 의존합니다. ProxyCreationWhenPSOReady가 1(활성화)로 설정된 경우, ProxyCreationDelayStrategy는 그 값에 따라 다음 동작을 실행합니다.
| 값 | 비헤이비어 |
|---|---|
0 | PSO가 준비될 때까지 드로를 건너뜁니다. |
1 | PSO가 준비될 때까지 엔진의 디폴트 머티리얼로 예비 전환합니다. |
로딩 화면
초기 로딩 화면을 설정할 때 PSO 프리캐시 요청을 염두에 두는 것을 적극 권장합니다. .
게임의 초기 로딩 화면을 설정할 때, 현재 진행 중인 모든 PSO 프리캐시 요청을 대기하도록 설정해야 합니다. 그렇지 않으면 랜드스케이프 터레인처럼 지연 프록시 생성을 지원하지 않는 컴포넌트에서 눈에 띄는 튀는 현상이나 런타임 히치가 발생할 수 있습니다. 랜드스케이프 터레인 등의 메시를 디폴트 머티리얼로 대체하거나 렌더링에서 제외하는 것은 바람직하지 않기 때문입니다.
FShaderPipelineCache::NumPrecompilesRemaining()은 번들 캐시 및 PSO 프리캐싱 모두에 남은 PSO 프리캐시 컴파일 수를 확인하는 데 유용합니다. 로딩 화면 로직을 수정하여 이 숫자를 확인하고 이 숫자가 0이 될 때까지 로딩 화면을 유지할 수 있습니다. 대부분의 경우, 드라이버 캐시가 비어 있는 상태에서 초기 PSO 컴파일 시간은 중간 사양 CPU에서는 1분 미만이 소요됩니다.
시스템 리소스 관리
PSO 프리캐싱은 백그라운드 스레드를 사용하는 비동기 컴파일에 의존하며 시스템 메모리와 퍼포먼스에 영향을 줍니다. 이 섹션에서는 이러한 리소스를 프로젝트에 맞게 사용하도록 조정하고 최적화하는 데 사용할 수 있는 옵션을 설명합니다.
메모리
UE는 런타임 시스템 메모리를 절약하기 위해 컴파일이 끝난 뒤 프리캐싱용으로 컴파일했던 PSO를 삭제합니다. 그 이유는 애플리케이션이 프리캐싱한 PSO의 양이 매우 큰 경우 PSO를 정리하지 않으면 해당 애플리케이션의 메모리 점유율이 굉장히 커질 수 있기 때문입니다(수백 MB에서 심지어는 GB까지).
PSO 프리캐싱은 기본 압축 드라이버 캐시의 존재 여부에 의존합니다. 프리캐싱 후 PSO를 삭제해도 드라이버 캐시에 남습니다. 런타임 시 PSO가 필요하면 그래픽 드라이버가 압축 드라이버 캐시에서 PSO를 로드합니다. 하지만 이런 방식도 리소스 집약적일 수 있으며, 처음에 이러한 캐시에서 가져올 때 몇 밀리초가 소요될 수 있습니다. D3D12.PSOPrecache.KeepLowLevel을 사용하여 D3D12에서 프리캐싱된 PSO 삭제를 비활성화할 수 있습니다.
어떤 독립 하드웨어 공급업체(IHV)의 드라이버 캐시에서 PSO를 생성하면 속도가 느려질 수 있습니다. NVIDIA의 경우, 메모리에 일정량의 프리캐싱된 그래픽을 유지하고 PSO를 계산하기 위해 r.PSOPrecache.KeepInMemoryUntilUsed를 사용하는 옵션이 있습니다. 이는 마지막 N개의 프리캐싱된 PSO를 메모리에 유지하여 드라이버 캐시 퍼포먼스 저하를 방지해 줍니다. 메모리에 유지되는 PSO의 수는 r.PSOPrecache.KeepInMemoryGraphicsMaxNum 및 r.PSOPrecache.KeepInMemoryComputeMaxNum을 사용하여 컴퓨트와 그래픽에 대해 개별적으로 조정할 수 있습니다. 이 옵션을 사용하기로 결정한 경우 다양한 설정으로 애플리케이션의 메모리 비용을 테스트하여 PSO 생성 성능과 메모리 오버헤드 간의 허용 가능한 절충점을 정하는 것이 좋습니다.
퍼포먼스
기본적으로 언리얼 엔진은 PSO 프리캐싱 스레드 풀을 사용하여 PSO를 비동기식으로 컴파일합니다. r.pso.PrecompileThreadPoolSize 또는 r.pso.PrecompileThreadPoolPercentOfHardwareThreads가 설정되어 있으면 스레드 풀이 사용됩니다. 그렇지 않으면 PSO 컴파일은 엔진의 나머지 워크로드와 함께 예약되는 일반 백그라운드 작업을 사용하는 것으로 예비 전환합니다.
| 콘솔 변수 | 설명 | 디폴트 상태 |
|---|---|---|
| 풀에서 사용할 정확한 스레드 양을 설정합니다. | 0 |
| 스레드 풀 크기를 사용할 수 있는 하드웨어 스레드의 백분율로 설정하고, 해당 크기의 스레드 풀을 생성합니다. 기본 크기는 하드웨어 스레드의 75%를 나타내는 75입니다. | 75 |
| PSO 스레드 풀에서 사용할 최소 스레드 수입니다. | 2 |
| PSO 스레드 풀에서 사용할 최대 스레드 수입니다. 기본값은 최대 스레드 수에 제한이 없는 | INT_Max |
추가 참고 사항:
스레드 풀의 최대 스레드 수가 무제한인 경우 프로세서는 많지만 메모리가 많지 않은 시스템에서 PSO를 컴파일할 때 시스템 메모리가 부족할 수 있습니다. PSO를 컴파일하는 각 스레드는 최대 2GB의 메모리를 사용할 수 있으므로 이 양을 제한하는 것이 프로젝트에 합리적일 수 있습니다.
게임플레이 때 하드웨어 스레드의 75%는 큰 수치일 수 있습니다. 일반 전경 스레드와의 경합이 눈에 띄게 발생하여 프레임이 다소 떨어질 수 있습니다. 로딩 시간에 이 값을 늘리고 게임플레이 때 다시 줄이면 도움이 될 수 있지만, PSO 컴파일이 지연되고 프록시 생성 지연이 증가할 수 있습니다. 이 방법은 런타임 히치를 발생시키지 않을 것입니다.
명령줄 실행인자 -clearPSODriverCache를 사용하여 드라이버 캐시를 강제로 정리할 수 있는데, 게임을 최초로 스타트업하는 환경에서 테스트 용도로 사용하는 것이 좋습니다.
또한, 코어가 많은 PC에서 테스트할 때는 명령줄 실행인자 -corelimit=n(여기서 n은 코어 수)을 사용하여 코어 수를 8로 제한하거나 소비자 등급 PC의 일반적인 코어 수로 제한하는 것이 좋으며, -processaffinity=n을 사용하면 Windows가 게임을 물리적 코어 n개에만 예약하도록 추가적으로 보장할 수 있습니다. 이렇게 하면 최종 사용자 환경을 더 정확하게 모사할 수 있습니다.
게임의 스무드니스를 평가하는 모든 테스트 실행에 일관적으로 -clearPSODriverCache 스위치를 사용하세요. 이 스위치가 없으면 그래픽 드라이버가 빌드하고 이전 실행에서 남은 PSO 캐시가 히치를 가릴 수 있습니다.
유효성 검사 및 트래킹
PSO 프리캐싱 시스템의 퍼포먼스를 검증하고 추적하는 몇 가지 옵션이 있습니다.
r.PSOPrecache.Validation을 다음 값으로 사용하여 유효성 검사를 활성화할 수 있습니다.
| 콘솔 변수 | 설명 |
|---|---|
0 | 비활성화됨 |
1 | 높은 수준의 숫자만 사용하는 가벼운 트래킹입니다. 이는 퍼포먼스에 미치는 영향을 최소화하며 출시된 타이틀에 사용할 수 있습니다. |
2 | PSO 프리캐싱 누락을 자세히 추적하고 로깅합니다. |
PSO 프리캐싱 유효성 검사가 활성화되면, stat PSOPrecache 콘솔 명령을 사용하여 수집된 통계를 검사할 수 있습니다.
PSO 프리캐싱 유효성 검사 시스템이 수집한 통계입니다. 콘솔 명령 stat PSOPrecache를 사용하여 통계를 확인할 수 있습니다.
통계는 3개 그룹으로 나뉩니다.
| 그룹 | 설명 |
|---|---|
셰이더 전용 PSO(Shader-only PSOs) | 이 통계들은 사용된 RHI 셰이더만 추적하고 PSO의 다른 모든 스테이트 정보는 무시합니다. 적어도 모든 셰이더가 미리 캐시되었는지 혹은 다른 렌더 스테이트에 뭔가 누락되었거나 문제가 있는지 확인하는 데 유용합니다. |
최소 PSO(Minimal PSOs) | 렌더 타깃 정보를 제외한 모든 렌더 통계 및 버텍스 엘리먼트 정보와 셰이더를 포함합니다. 렌더 타깃 정보는 드로 시간 때 유효성 검사에만 사용할 수 있지만, 최소 PSO 통계는 MeshDrawCommand 빌드 중에도 업데이트하고 확인할 수 있습니다. |
전체 PSO(Full PSOs) | 그래픽 API가 사용하는 완전한 런타임 필수 PSO 스테이트입니다. 최소 PSO와 같지만, 더욱 많은 렌더 타깃 정보를 가지고 있습니다. |
각 그룹에서 다음 파라미터를 추적합니다.
| Parameter | 설명 |
|---|---|
누락됨(Missed) | 프리캐싱되지 않았지만 드로/방출 시간에 필요하기 때문에 프리캐싱했어야 하는 PSO의 수입니다. 가능한 이유로는 잘못된 셰이더, 렌더 타깃 스테이트, 버텍스 어트리뷰트, 렌더 타깃 정보 등이 있습니다. |
추적되지 않음(Untracked) | 프리캐싱이 활성화되지 않은 PSO의 수입니다. 가능한 이유로는 유효성 검사 비활성화, 글로벌 머티리얼, 지원되지 않는 버텍스 팩토리, 지원되지 않는 메시 패스 프로세서 타입 등이 있습니다. 특정 디버그 정보가 없는 출시 빌드에서는 추적되지 않은 PSO가 누락된 PSO로 대신 표시됩니다. |
히트 | 성공적으로 프리캐싱되어 런타임 시 사용된 PSO의 수입니다. |
너무 늦음(Too late) | 프리캐싱을 위해 큐에 등록되었지만 필요한 시점에 컴파일되지 않은 PSO의 수입니다. |
사용됨(Used) | 런타임 시 사용된 PSO의 수입니다. 위 모든 PSO의 합계입니다. |
프리캐싱됨(Precached) | 실제 사용 여부와는 상관없이 프리캐싱된 PSO의 수입니다. |
셰이더 파이프라인 캐시(Shader Pipeline Cache)는 PSO 컴파일 자체로 인해 탐지된 실제 런타임 히치 횟수에 대한 정보도 제공합니다. 런타임 PSO를 컴파일하는 데 일정 시간(밀리초)보다 오래 걸리는 경우 PSO 컴파일이 히치로 표시됩니다. 디폴트 한계치는 20밀리초입니다. r.PSO.RuntimeCreationHitchThreshold로 이 값을 수정할 수 있지만, 최대한 작게 유지해야 합니다.
기본값인 20밀리초는 높은데, 드라이버 캐시의 첫 번째 히트가 오래 걸릴 수 있기 때문입니다.
PSO 프리캐싱 정보 수집
로그 파일(Log file), Visual Studio 디버거와 언리얼 인사이트(Unreal Insights)를 사용하여 PSO 프리캐싱에 대한 더 많은 정보를 얻고 런타임 시 특정 PSO가 계속 히치를 일으키는 이유를 조사할 수 있습니다. 정확한 PSO 프리캐싱 스테이트는 PSO 유효성 검사를 활성화해야만 로그와 언리얼 인사이트에서 확인할 수 있습니다(위의 유효성 검사 및 트래킹 참조).
PSO 누락 또는 너무 늦음이 발견되면 UE는 로그에 다음 정보를 출력합니다.
PSO PRECACHING MISS:
Type: FullPSO
PSOPrecachingState: Missed
Material: M_AdvancedSkyDome
VertexFactoryType: FLocalVertexFactory
MDCStatsCategory: StaticMeshComponent
MeshPassName: SkyPass
Shader Hashes:
VertexShader: EC68796503F829FDEACC56B913C4CA86C6AD3C16
PixelShader: 651BF1ABBAEC0B74C8D2A5E917702A00EF29817B
인사이트는 PSO 프리캐싱을 디버깅하는 편리한 툴입니다. 게임 프레임 스테이트 시리즈에 PSOPrecache: Missed 타이머와 PSOPrecache: Too Late 타이머를 추가하면, 일정 기간 동안 PSO 컴파일 때문에 생기는 모든 히치의 개요를 간편하게 확인할 수 있습니다. 아래 스크린샷에는 PSO 프리캐싱 누락으로 인한 5~10밀리초 정도의 히치가 몇 개 있지만, 플레이어의 눈에 띌 만한 117밀리초의 긴 히치도 있습니다. 이러한 긴 히치는 PSO 컴파일에서 비롯된 것이 아닙니다.
이미지를 클릭하면 확대됩니다.
확대해서 보면 반투명 패스에서 발생한 것을 확인할 수 있습니다. 로그에는 더 자세한 정보가 나와 있을 것입니다.
이미지를 클릭하면 확대됩니다.
글로벌 PSO 유효성 검사 헬퍼 오브젝트에서 더 자세한 정보를 확인할 수 있습니다. 유효성 검사가 전체 트래킹(r.PSOPrecache.Validation=2)으로 설정되면 메시 패스 프로세서와 버텍스 팩토리 타입별로 숫자를 그룹화하므로 특정 누락이 발생한 지점을 추적하는 데 도움이 됩니다. 또한 프리캐싱된 모든 PSO의 출처를 더 명확하게 파악할 수 있으며, 너무 많은 셰이더를 프리캐싱하지 않는 이상치를 찾는 데 도움이 됩니다.
이러한 패스별 및 버텍스별 팩토리 통계가 직접 노출되지는 않지만, 디버깅 때 통계를 수집하는 데이터 구조체를 탐색함으로써 검사할 수 있습니다. 그들은 PSOPrecacheValidation.cpp에 있습니다.
FullPSOPrecacheStatsCollectorShadersOnlyPSOPrecacheStatsCollectorMinimalPSOPrecacheStatsCollector
아래 스크린샷은 예시를 보여줍니다.
이미지를 클릭하면 확대됩니다.
엔진 기능으로 PSO 프리캐싱 확장
이 섹션에서는 PSO 프리캐싱을 위해 지원 오브젝트를 확장하는 방법에 대한 정보를 제공합니다.
UPrimitiveComponent
UPrimitiveComponent는 PSO 이니셜라이저 설정에 필요한 모든 정보를 수집합니다. 이 컴포넌트에는 머티리얼 인스턴스, 버텍스 팩토리(가능한 버텍스 엘리먼트 세트 포함), 그리고 FMeshPassProcessor에 사용되는 최종 셰이더 또는 렌더 상태에 영향을 줄 파라미터 세트가 필요합니다.
이 파라미터는 FPSOPrecacheParams에 저장되며 정확한 기본값은 UPrimitiveComponent::SetupPrecachePSOParams에 설정됩니다.
PSO 프리캐싱을 위한 베이스 엔트리 함수는 다음과 같습니다.
/** Precache all PSOs which can be used by the primitive component */
ENGINE_API virtual void PrecachePSOs();대부분의 경우 파생 컴포넌트는 이 함수를 구현할 필요가 없으며, 간단히 프리캐싱 파라미터 컬렉션 함수를 오버라이드할 수 있습니다.
/**
* Collect all the data required for PSO precaching
*/
struct FComponentPSOPrecacheParams
{
EPSOPrecachePriority Priority = EPSOPrecachePriority::Medium;
모든 기능을 보여주는 예시는 UStaticMeshComponent::CollectPSOPrecacheData에서 확인할 수 있으며, 더 간단한 사용 사례는 WaterMeshComponent::CollectPSOPrecacheData에서 확인할 수 있습니다.
FVertexFactory
새 버텍스 팩토리는 EVertexFactoryFlags::SupportsPSOPrecaching 플래그를 사용하여 PSO 프리캐싱을 지원한다고 플래깅해야 합니다. 해당 플래그는 버텍스 팩토리 선언 매크로 IMPLEMENT_VERTEX_FACTORY_TYPE과 함께 제공될 수 있습니다.
그런 다음, 버텍스 팩토리는 다음 함수를 구현해야 합니다.
static void GetPSOPrecacheVertexFetchElements(EVertexInputStreamType VertexInputStreamType, FVertexDeclarationElementList& Elements);명시적인 버텍스 엘리먼트 세트가 제공되지 않으면 PSO 프리캐싱 중에 FVertexFactory::GetPSOPrecacheVertexFetchElements가 사용됩니다.
고정 버텍스 엘리먼트 세트는 버텍스 팩토리에 EVertexFactoryFlags::SupportsManualVertexFetch 플래그가 설정되어 있거나 고정 버텍스 엘리먼트 세트가 셰이더에서 사용되는 경우 유효합니다.
버텍스 엘리먼트 목록이 메시의 버텍스 버퍼 데이터에 종속된 경우, 올바른 세트는 FPSOPrecacheVertexFactoryData에서 제공해야 합니다. 이는 UPrimitiveComponent::CollectPSOPrecacheData 중에 발생해야 합니다. 예시는 UStaticMeshComponent::CollectPSOPrecacheData 및 FLocalVertexFactory::GetVertexElements를 참조하세요.
FMeshPassProcessor
메시 패스 프로세서는 다음 함수를 구현해서, 주어진 FPSOPrecacheParams로 특정 머티리얼을 드로잉할 때 사용할 수 있는 모든 PSO를 수집해야 합니다.
virtual void CollectPSOInitializers(const FSceneTexturesConfig& SceneTexturesConfig, const FMaterial& Material, const FPSOPrecacheVertexFactoryData& VertexFactoryData, const FPSOPrecacheParams& PreCacheParams, TArray<FPSOPrecacheData>& PSOInitializers) override {}로직은 대부분 AddMeshBatch와 같지만(이상적으로는 부분적으로 공유 가능), AddMeshBatch는 MeshDrawCommand 빌드 시점에 호출되지만, PSO 프리캐싱 시스템은 훨씬 일찍 정보를 수집하려고 시도합니다(컴포넌트의 PostLoad).
간단한 예시는 FDistortionMeshProcessor::CollectPSOInitializers를, 더 포괄적인 예시는 FBasePassMeshProcessor::CollectPSOInitializers를 참조하세요.
IPSOCollector
이제 일부 머티리얼 셰이더는 메시 패스 프로세서를 거치거나 헤어, 나나이트, 레이 트레이싱 다이내믹 지오메트리 업데이트 등 EMeshPass::Type을 정의하지 않아도 됩니다. 대신 이 경우 기본 인터페이스에서 직접 파생해야 할 수 있습니다.
IPSOCollector에는 구현해야 하는 단일 가상 함수가 있습니다.
// Collect all PSO for given material, vertex factory & params
virtual void CollectPSOInitializers(const FSceneTexturesConfig& SceneTexturesConfig, const FMaterial& Material, const FPSOPrecacheVertexFactoryData& VertexFactoryData, const FPSOPrecacheParams& PreCacheParams, TArray<FPSOPrecacheData>& PSOInitializers) = 0;또한 PSO 컬렉터는 글로벌 FRegisterPSOCollectorCreateFunction을 통해 생성 가능하도록 등록해야 합니다. 엔진에는 FTranslucentLightingMaterialPSOCollector, FRayTracingDynamicGeometryPSOCollector 등 몇 가지 간단한 예시가 있습니다.
GlobalPSOCollector
이 페이지의 초반에 언급했듯 몇몇 글로벌 그래픽 PSO는 런타임 순열을 컴파일할 수 있는 스타트업 때 이미 프리캐싱되었습니다. 이를 위해 GlobalPSOCollector가 사용됩니다. 이는 IPSOCollector의 간소화된 버전입니다. 글로벌 PSO 컬렉터 함수를 제공하는 글로벌 FRegisterGlobalPSOCollectorFunction 오브젝트를 선언해야 합니다.
typedef void (*GlobalPSOCollectorFunction)(const FSceneTexturesConfig& SceneTexturesConfig, int32 GlobalPSOCollectorIndex, TArray<FPSOPrecacheData>& PSOInitializers);몇 가지 사용 방법 예시는 DeferredLightGlobalPSOCollector 또는 RegisterVolumetricFogGlobalPSOCollector를 참조하세요.
PSO 프리캐싱 누락 디버그
위의 PSO 프리캐싱 누락이 발생하는 지점을 디버그하려면 Visual Studio에서 수동 디버깅을 사용해야 합니다.
최소 PSO 스테이트의 누락을 디버깅하는 것은 간단합니다. 드로 시간이 아닌 MeshDrawCommand 빌드 때 누락을 트리거할 수 있기 때문입니다. 전체 PSO 계산에 필요한 최종 렌더 타깃 정보는 드로잉 때만 이용할 수 있으므로 디버깅이 더 어렵습니다.
LogPSOMissInfo 함수는 추적된 내부 스테이트를 업데이트하며, 런타임 시 누락이 발생했을 때 디버거로 중단할 수 있는 편리한 위치에 있습니다. 호출 스택과 감시 창에서는 사용된 머티리얼과 렌더 패스, 버텍스 팩토리, FPrimitiveSceneProxy에 대한 자세한 정보를 확인할 수 있습니다. 또한, ComponentForDebuggingOnly 멤버를 사용하여 UPrimitiveComponent에 대한 정보를 가져올 수도 있습니다. 이 함수가 수집된 누락을 발견할 때 이 데이터의 대부분은 로그 파일에도 출력됩니다.
하지만 LogPSOMissInfo가 실행될 즈음에는 보통 PSO 프리캐싱이 해당 컴포넌트에서 이미 수행된 상태입니다. 해당 컴포넌트의 PSO 프리캐싱 때 잘못된 셰이더 또는 렌더 스테이트를 사용한 이유를 찾으려면, 주어진 패스에서 해당 컴포넌트 및/또는 머티리얼의 PSO 프리캐싱 때 중단점을 추가해야 합니다.
r.PSOPrecache.BreakOnMaterialName은 PSO 프리캐싱 도중 지정된 이름을 가진 머티리얼을 찾으면 실행을 중단할 때 유용합니다. 이는 특정 렌더 상태가 런타임 상태와 비교해서 왜 다른지 파악하는 데 도움이 될 수 있습니다. r.PSOPrecache.BreakOnPassName 및 r.PSOPrecache.BreakOnShaderHash도 문제가 있는 PSO의 범위를 좁히는 데 사용할 수 있습니다. 이 정보는 앞서도 말했듯이 로그에서 확인할 수 있습니다.
r.PSOPrecache.UseBackgroundThreadForCollection은 PSO 이니셜라이저 컬렉션을 위한 백그라운드 스레드 작업을 비활성화하여 PSO 프리캐싱 누락을 디버깅하는 동안 컴포넌트 정보나 기타 상태를 쉽게 추적할 수 있도록 만드는 데 유용합니다.
FPSOPrecacheParams의 값을 확인해야 할 수도 있습니다. 이러한 값이 PSO에서 사용되는 셰이더 및 렌더 상태에도 영향을 줄 수 있기 때문입니다.