Direct3D 11 과 같은 초기 그래픽 API는 새 드로 콜을 발행하기 전에 바로 GPU 파라미터를 설정하고자 수십 개의 별도 호출을 해야 했습니다. Direct3D 12 (D3D12), Vulkan, Metal 같은 최신 그래픽 API는 파이프라인 스테이트 오브젝트 (PSO, Pipeline State Object) 라는 사전 설정된 GPU 스테이트 정보 패키지를 사용하여 GPU 스테이트를 더 빠르게 바꿉니다.
이렇게 하면 렌더링 효율이 크게 향상되지만, 새 PSO를 온디맨드로 생성하는 시간이 100밀리초 이상이 걸릴 수 있습니다. 애플리케이션이 가능한 모든 파라미터를 설정해야 하기 때문입니다. 따라서 PSO가 효율을 발휘하려면 필요하기 한참 전에 PSO를 생성해야 합니다.
언리얼 엔진(UE) 과 같이 고도로 프로그래밍 가능한 리얼타임 렌더링 환경에서 대량의 콘텐츠를 보유한 애플리케이션은 너무 많은 GPU 스테이트 파라미터를 가집니다. 따라서 수동으로 미리 PSO를 설정하는 게 실용적입니다. 이러한 복잡성을 해결하기 위해 UE는 런타임에서 GPU 스테이트 관련 데이터를 애플리케이션 빌드로부터 수집한 다음, 이 캐시된 데이터를 사용하여 새 PSO를 사용되기 한참 전에 미리 생성할 수 있습니다. 이는 가능한 GPU 스테이트를 애플리케이션에서 사용된 것들로 좁힙니다. 실행 중인 애플리케이션에서 수집한 PSO 디스크립션을 PSO 캐시(PSO cache) 라고 합니다.
언리얼은 다음 단계를 거쳐 PSO를 수집합니다.
-
게임을 플레이합니다.
-
실제로 드로되는 것을 기록합니다.
-
빌드에 이 정보를 포함시킵니다.
이후 후속 플레이에서 게임은 렌더링 코드가 요구하기 전에 필요한 GPU 스테이트를 생성할 수 있습니다.
이 문서에서는 UE에서 사용 가능한 PSO 타입과 상세한 PSO 캐시 생성 프로세스를 설명합니다.
용어 및 지원되는 PSO 타입
이 문서에서는 GPU 스테이트를 D3D12 API에서 쓰는 파이프라인 스테이트 오브젝트(PSO)라는 용어로 지칭합니다. 다른 API는 약간 다른 이름을 사용할 수 있습니다. 예를 들어 Vulkan은 pipeline(파이프라인) 을, Metal은 pipeline state(파이프라인 스테이트) 를 사용합니다. 그러나 개념 면에서는 모두 비슷합니다.
PSO 캐시 라는 용어는 게임이 스테이트를 빠르게 생성할 수 있도록 빌드에 PSO 디스크립션이 포함된 파일을 가리킵니다. 즉 PSO 캐시는 빠르게 생성할 PSO 목록입니다.
언리얼 엔진은 다음 두 가지 타입의 PSO를 사용합니다.
- 그래픽 PSO 는 애플리케이션의 그래픽 파이프라인 스테이트를 나타내며 설정 가능한 다수의 변수로 이뤄져 있습니다.
- 컴퓨팅 PSO 는 일반적으로 컴퓨팅 셰이더 의 형태를 취합니다.
위 PSO 타입 외에 레이 트레이싱 PSO 도 있지만, UE 5.0은 레이 트레이싱 데이터용 PSO 캐싱을 지원하지 않습니다.
이 문서의 정보 대부분은 그래픽 PSO 컬렉션에 대한 것입니다. 컴퓨팅 PSO는 빌드에 다르게 포함되어 있습니다(셰이더 라이브러리의 모든 컴퓨팅 셰이더는 자동으로 쿠킹 도중 셰이더를 위해 생성된 PSO 엔트리를 갖습니다).
기록된 캐시 및 스테이블 캐시
PSO에서 가장 중요한 데이터는 셰이더 정보입니다. 그러나 언리얼의 셰이더는 개발자가 머티리얼을 미세조정함에 따라 여러 빌드에서 달라질 수 있습니다. 애플리케이션을 오래 플레이한 뒤에 PSO 전체를 버리는 일이 없도록 PSO 캐시 파일은 두 가지 종류로 나뉩니다.
.upipelinecache
파일, 즉 기록된 PSO 캐시 는 애플리케이션 빌드 실행 시 기록됩니다.- 기록된 캐시의 셰이더는 바이트코드의 SHA 해시에 의해 식별됩니다.
.spc
파일, 즉 스테이블 PSO 캐시 는 맵 파일 쿠킹 시 생성되며 개발자가 프로젝트 내의 맵이나 셰이더를 변경할 때 이를 예측하여 변경되는 셰이더 정보를 포함합니다.- 이는 머티리얼 이름, 버텍스 팩토리 이름, 셰이더 타입 등 여러 빌드 간에 동일하게 유지될 것으로 예상되는 안정적이고 하이레벨 디스크립션에 의해 식별됩니다. 이 디스크립션은 스테이블 키(stable key) 라고 불리며
.shk
파일(기존에는.scl.csv
)로 표현됩니다.
- 이는 머티리얼 이름, 버텍스 팩토리 이름, 셰이더 타입 등 여러 빌드 간에 동일하게 유지될 것으로 예상되는 안정적이고 하이레벨 디스크립션에 의해 식별됩니다. 이 디스크립션은 스테이블 키(stable key) 라고 불리며
이렇게 하면 기록된 데이터는 자주 변경되더라도 비교적 안정을 유지합니다. 애플리케이션에 극단적인 대규모 변경이 적용된 경우 PSO 캐시를 다시 기록해야 할 수도 있지만, 애플리케이션의 전반적인 콘텐츠가 마무리되었다면 그대로 두어도 됩니다.
여러 플랫폼 및 그래픽 API에서의 PSO
이 문서에서 설명하는 캐시는 하이레벨 PSO 디스크립션을 포함하며, 이는 FGraphicsPipelineStateInitializer에 대한 엔진의 코드 맵에 있습니다. 그러나 PSO 데이터는 범용이 아닙니다. 언리얼 엔진의 각 렌더링 하드웨어 인터페이스(Rendering Hardware Interface, RHI) 는 서로 다른 프로퍼티를 가지며 다른 렌더링 패스를 실행할 수도 있습니다.
따라서 PSO 캐시의 콘텐츠는 플랫폼 및 렌더링 레벨에 따라 다를 수 있습니다. PSO 캐시 간 정보는 상호 호환되지 않습니다. 예를 들어 D3D12 RHI로 실행되는 게임에서 수집된 캐시는 Vulkan에서 실행되는 동일한 게임에서 사용할 수 없습니다.
두 개 이상의 그래픽 API를 사용 가능한 플랫폼에 배포하고 애플리케이션이 그중에서 하나를 선택할 수 있게 하려면, 캐시 파일을 빌드에 두 개 이상(API당 하나) 포함해야 합니다. 예를 들어 이 글을 쓰는 시점에서 OpenGL ES는 Android에서 여전히 유효한 API입니다. Android에 애플리케이션을 GLES 및 Vulkan으로 배포하는 경우, RHI마다 하나씩 총 2개의 서로 다른 캐시 파일을 수집하고 포함해야 합니다.
GLES에는 PSO 콘셉트가 없습니다. 그 대신 프로그램 오브젝트(program object) 라는 유사한 콘셉트를 사용합니다.
하드웨어가 고정된 플랫폼은 보통 이러한 하이레벨 PSO 캐시를 필요로 하지 않고, 혜택을 받지도 못합니다. 이런 플랫폼은 자체 솔루션이 있거나 런타임 퍼포먼스 페널티를 피할 수 있는 방법이 있습니다. 그런 플랫폼용으로 개발하려는 경우 해당 플랫폼 문서를 참조하세요.
이 캐시는 D3D11 같은 레거시 API도 지원하지 않습니다.
수집 흐름
이 섹션에서는 사전에 기록된 캐시가 포함되지 않은 빌드로 처음부터 PSO 캐시용 데이터를 수집하는 상황을 가정하겠습니다. PSO 캐시 수집 프로세스는 반복적입니다. 즉 깨끗한 슬레이트에서 시작하기보다는, 이전 데이터를 잃지 않고 시간이 갈수록 더해 가는 방식입니다. 그러나 캐시 파일이 오래된 경우에는 보통 PSO 캐시를 재생성하는 것이 좋습니다. 처음 수집된 이후로 코드 또는 콘텐츠 변화가 많아 연관성을 잃었을 수도 있기 때문입니다.
아래 섹션에는 PSO 캐시를 수집하고 프로젝트에서 이를 구현하기 위한 단계가 나열되어 있습니다.
1. 구성 및 필수 세팅
프로젝트가 PSO 캐시를 기록하도록 구성하려면 다음 단계를 따릅니다.
-
프로젝트의 DefaultEngine.ini 또는 (플랫폼)Engine.ini 파일을 엽니다.
(플랫폼)Engine.ini
파일은 보통 (프로젝트)/Config/(플랫폼) 디렉터리에 있습니다. 이 디렉터리를 사용하면 PSO 캐시가 필요하지 않을 다른 플랫폼이 이 세팅을 사용하는 걸 방지합니다. -
Engine.ini
파일에서 다음 값을 설정합니다.[DevOptions.Shaders] NeedsShaderStableKeys=true
-
DefaultGame.ini
파일에서 다음 값을 설정합니다.[/Script/UnrealEd.ProjectPackagingSettings] bShareMaterialShaderCode=True bSharedMaterialNativeLibraries=True
-
처음부터 시작하는 경우
(프로젝트)/Build/(플랫폼)/PipelineCache
에 파일이 없는지 확인하세요. 쿠커가 기록된 캐시 파일을 찾는 위치입니다. PSO를 처음 수집하는 경우 이 폴더가 존재하지 않을 수도 있습니다. -
CVar
r.ShaderPipelineCache.Enabled
가 1로 설정되어 있는지 확인합니다.
2. 기록된 PSO 캐시 수집하기
PSO 캐시를 기록하려면 다음 단계를 따릅니다.
-
-logPSO 명령줄 스위치와 함께 패키지로 만든 애플리케이션을 실행합니다.
-
애플리케이션에서 최대한 많은 패스를 실행합니다. 예를 들어 모든 애플리케이션 레벨을 플레이하고 그래픽 세팅을 변경합니다.
-
애플리케이션을 실행할 때마다 녹화된 캐시 파일(
rec.pipelinecache
)이Saved/CollectedPSOs
폴더에 생성될 것입니다. 이를 수집하여 컴퓨터의 새 디렉터리에 넣습니다. 이 가이드에서는C:\PSOCache
디렉터리를 사용합니다.
기록된 PSO 캐시를 수집할 때의 최종 목표는 사용자가 애플리케이션에서 볼 수 있는 머티리얼과 비주얼 이펙트를 모두 보는 것입니다. 그러므로 다양한 그래픽 세팅 조합을 사용하여 최대한 많은 장소를 방문해야 합니다.
한 번의 실행으로 PSO 캐시를 전부 수집할 필요는 없습니다. 애플리케이션 개발 과정에서 여러 번 실행하거나 여러 사람이 이 작업을 나눠 맡아도 됩니다. 기록된 PSO 캐시는 수동으로 제거하기 전까지 제거되지 않으므로 개발을 진행하며 서서히 축적할 수 있습니다.
3. PSO 캐시 변환하기(확장이라고도 함)
PSO 데이터를 이전 단계에서 유용한 포맷으로 변환하려면 다음 단계를 따릅니다.
-
프로젝트 콘텐츠를 쿠킹합니다. 그렇게 하려면 애플리케이션을 패키징합니다.
-
(프로젝트)/Saved/Cooked/[플랫폼]/[프로젝트 이름]/Metadata/PipelineCaches
폴더를 엽니다. 이 디렉터리에서 스테이블 셰이더 키(.shk
) 파일을rec.pipelinecache
파일이 있는 폴더로 복사합니다. 예를 들면C:\PSOCache
폴더입니다. -
아래 인수를 사용하여
ShaderPipelineCacheTools
커맨드릿을 실행합니다(현재 디렉터리가 엔진 설치 디렉터리인 경우).Engine\Binaries\Win64\UnrealEditor-Cmd.exe -run=ShaderPipelineCacheTools expand C:\PSOCache*.rec.upipelinecache C:\PSOCache*.shk C:\PSOCache[접두사]_[프로젝트 이름]_[셰이더 포맷 이름].spc
입력하는 파일 이름은 다음과 같아야 합니다.
- [접두사] – 임의의 스트링입니다. 보통 수집이 발생한 순간을 나타냅니다. 예를 들면 현재 빌드의 체인지리스트일 수도 있습니다.
- [프로젝트 이름] – 프로젝트의 이름입니다. 예를 들면 다음과 같습니다. ShooterGame. 프로젝트의 이름과 정확하게 일치해야 하며, 그렇지 않으면 수집되지 않습니다.
- [셰이더 포맷 이름] – 프로젝트의 셰이더 포맷입니다. 셰이더 포맷의 이름과 정확하게 일치해야 합니다. D3D12의 경우 PCD3D_SM5입니다.
위 명명 규칙을 사용한 프로젝트 ShooterGame의 전체 이름은 다음과 같습니다. CL11122333_ShooterGame_PCD3D_SM5.spc
4. 애플리케이션에 PSO 캐시 포함시키기
-
이전 섹션에서 생성된
.spc
파일을 Build/[플랫폼 이름]/PipelineCaches 폴더에 넣습니다. 예를 들면 다음과 같습니다. Build/Windows/PipelineCaches. -
프로젝트를 다시 쿠킹 또는 패키징합니다. 쿠커가 PSO 캐시 파일을 선택해야 하며, 로그에는 끝부분에 다음과 같은 부분이 있어야 합니다.
LogCook: Display: ---- Running UShaderPipelineCacheToolsCommandlet for platform WindowsClient shader format PCD3D_SM5 LogCook: Display: With Args: build "../../../TestGame/Build/Windows/PipelineCaches/*TestGame_PCD3D_SM5.spc" "d:/build/++Test/Sync/TestGame/Saved/Cooked/WindowsClient/TestGame/Metadata/PipelineCaches/ShaderStableInfo-Global-PCD3D_SM5.shk" "d:/build/++Test/Sync/TestGame/Saved/Cooked/WindowsClient/TestGame/Metadata/PipelineCaches/ShaderStableInfo-TestGame-PCD3D_SM5.shk" "d:/build/++Test/Sync/TestGame/Saved/Cooked/WindowsClient/TestGame/Content/PipelineCaches/Windows/TestGame_PCD3D_SM5.stable.upipelinecache" LogShaderPipelineCacheTools: Display: Sorting input stable cache files into chronological order for merge processing... LogShaderPipelineCacheTools: Display: Loading d:/build/++Test/Sync/TestGame/Saved/Cooked/WindowsClient/TestGame/Metadata/PipelineCaches/ShaderStableInfo-Global-PCD3D_SM5.shk... LogShaderPipelineCacheTools: Display: Loading d:/build/++Test/Sync/TestGame/Saved/Cooked/WindowsClient/TestGame/Metadata/PipelineCaches/ShaderStableInfo-TestGame-PCD3D_SM5.shk... LogShaderPipelineCacheTools: Display: Loaded 3554 shader info lines from d:/build/++Test/Sync/TestGame/Saved/Cooked/WindowsClient/TestGame/Metadata/PipelineCaches/ShaderStableInfo-Global-PCD3D_SM5.shk. LogShaderPipelineCacheTools: Display: Loaded 3833694 shader info lines from d:/build/++Test/Sync/TestGame/Saved/Cooked/WindowsClient/TestGame/Metadata/PipelineCaches/ShaderStableInfo-TestGame-PCD3D_SM5.shk. LogShaderPipelineCacheTools: Display: Loaded 3837248 unique shader info lines total. LogShaderPipelineCacheTools: Display: Loaded 13238 stable PSOs from ../../../TestGame/Build/Windows/PipelineCaches/++Test+GoldMaster-CL-17412694-TestGame_PCD3D_SM5.spc. 2329 PSOs rejected, 5840141 PSOs merged LogShaderPipelineCacheTools: Display: Re-deduplicated into 35084 binary PSOs [Usage Mask Merged = 3]. LogShaderPipelineCacheTools: Display: Running sanity check (consistency of vertex format). LogShaderPipelineCacheTools: Display: 0 vertex shaders are used with an inconsistent vertex format LogShaderPipelineCacheTools: Display: === Sanitizing results === LogShaderPipelineCacheTools: Display: Before sanitization: .................................................................... 35382 PSOs LogShaderPipelineCacheTools: Display: Filtered out due to inconsistent vertex declaration for the same vertex shader:.......... 0 PSOs LogShaderPipelineCacheTools: Display: Filtered out due to VS being possibly incompatible with an empty vertex declaration:..... 1 PSOs LogShaderPipelineCacheTools: Display: ----- LogShaderPipelineCacheTools: Display: Number of PSOs after sanity checks:...................................................... 35381 PSOs LogShaderPipelineCacheTools: Display: Wrote 35381 binary PSOs (graphics: 34834 compute: 547 RT: 0), (18453KB) to d:/build/++Test/Sync/TestGame/Saved/Cooked/WindowsClient/TestGame/Content/PipelineCaches/Windows/TestGame_PCD3D_SM5.stable.upipelinecache LogCook: Display: ---- Done running UShaderPipelineCacheToolsCommandlet for platform WindowsClient
제대로 작동했는지 검증하려면, 로그 맨 끝에 표시되는 작성된 바이너리 PSO 수를 표시하는 라인을 확인하세요. 이 라인의 그래픽 PSO 수는 0보다 커야 합니다.
LogShaderPipelineCacheTools: Display: **Wrote 35381 binary PSOs** (graphics: 34834 compute: 547 RT: 0), (18453KB) to d:/build/++Test/Sync/TestGame/Saved/Cooked/WindowsClient/TestGame/Content/PipelineCaches/Windows/TestGame_PCD3D_SM5.stable.upipelinecache LogCook: Display: ---- Done running UShaderPipelineCacheToolsCommandlet for platform WindowsClient
5. PSO 커버리지 테스트하기
PSO 캐시에 커버리지가 충분히 있는지 확인하려면 새로 패키지로 만든 애플리케이션을
-logpso
명령으로 실행하고 로그 출력을 관찰합니다. 다음과 유사한 라인이 표시될 것입니다.[2021.10.06-20.06.22:848][ 0]LogRHI: Opened FPipelineCacheFile: ../../../ShooterGame/Content/PipelineCaches/Windows/ShooterGame_PCD3D_SM5.stable.upipelinecache (GUID: EA50968D47BDE9A04A8524BCEB51615D) with 269 entries.
이 숫자는 패키징 로그의 작성된 바이너리 PSO 수와 일치해야 합니다. 예를 들어 로그에서 35381개의 바이너리 PSO를 작성했다고 보고했다면 35381개의 엔트리가 있어야 합니다.
빌드가 로그에서 "새 그래픽 PSO가 발견됐습니다(Encountered a new graphics PSO)"라는 메시지를 출력했는지도 확인해야 합니다. 캐시 수집 도중에 동일한 조건(예: 동일한 엔진 퀄리티 세팅) 하에 동일한 콘텐츠를 보고 있다면 일어나서는 안 되는 일입니다.
캐시 파티셔닝
이 글을 작성하는 시점에서 결과 PSO 캐시는 게임 빌드에 포함된 단일 모놀리식 파일입니다. 게임의 디폴트 행동은 해당 파일을 시작 시에 열고 거기에서 PSO 컴파일을 시작하는 것입니다. 그러나 모든 PSO가 항상 연관성이 있는 것은 아닙니다. 예를 들어 일부는 다른 레벨에서 수집됐을 수도 있고, 일부는 다른 그래픽 세팅에서 기록됐을 수도 있습니다.
모든 것을 불필요하게 컴파일하지 않도록 각 PSO는 현재 게임 사용 마스크(game usage mask) 라는 비트마스크와 연결되어 있습니다. 애플리케이션은 SetGameUsageMaskWithComparison
함수를 사용하여 다른 레벨에서 기록된 PSO나 다른 그래픽(퀄리티) 세팅으로 기록된 PSO를 컴파일하지 않을 수 있습니다. 그런 함수의 예시는 다음과 같습니다.
void SetPSOCacheUsageMask(int32 QualityLevel, int32 MapIndex)
{
uint64 GameMask = 0;
const int32 kMaxQualityLevels = 4;
GameMask |= (1ULL << static_cast<uint64>(QualityLevel));
check(MapIndex < 64 - kMaxQualityLevels);
GameMask |= (1ULL << static_cast<uint64>(kMaxQualityLevels + MapIndex));
// default bit-wise AND comparison will work, no need to overload comparison function
FShaderPipelineCache::SetGameUsageMaskWithComparison(GameMask);
}
게임에 맵이 60개 넘게 있고 보다 세밀한 퀄리티 세팅을 인코딩하고 싶은 경우 마스크를 다르게 생성할 수 있습니다. 예를 들어 uint64를 여러 비트필드의 구조로 취급하고 비교할 커스텀 비교 함수를 사용하는 것입니다. 예:
union
{
uint64 Packed;
struct
{
uint64 MaterialQuality : 4;
uint64 ShadowQuality : 4;
uint64 MapIndex : 16;
};
};
이 함수는 사용자 세팅(UGameUserSettings) 로드/저장 시 등 애플리케이션에 의해 스타트업 중 초기에 설정되어야 합니다. 이는 다음과 같은 사례에서 사용됩니다.
-
기록 프로세스 도중 - 기록된 PSO가 현재 사용 마스크와 연관됩니다.
-
컴파일 도중 – 현재 사용 마스크와 일치하는 PSO만 캐시에서 컴파일됩니다.
컴파일은 아주 일찍 시작되므로 일시정지 (아래 참조) 상태를 시작 디폴트로 설정하고, 올바른 마스크가 설정된 뒤에 명시적으로 다시 활성화하는 것이 좋습니다.
한계 및 향후 과제
현재 마스크 콘셉트는 내재적으로 수동 수집에 의존합니다. 그에 비해 쿠킹 도중 캐시에 자동으로 추가되는 컴퓨팅 PSO는 모두 0xffffffffffffffff 마스크를 갖습니다(모두 1초). PSO 캐시를 프로그래밍 방식으로 채운다는 것은 (일부) 그래픽 PSO에도 자동 캐싱을 적용한다는 것을 의미하며, 사용자 마스크 콘셉트는 다른 접근법으로 발전하거나 지원이 중단될 수도 있습니다.
게임 콘텐츠가 다수의 다운로드로 분할된다면 PSO 캐시를 각각의 독립된 콘텐츠 번들에 해당하는 청크로 분할하기는 어렵습니다. 이 문서에서는 캐시 분할을 다루지 않습니다. 쿠킹 및 청킹 문서를 참조하세요.
PSO 컴파일 제어하기
캐시에 포함된 PSO는 렌더링 코드에서 필요할 때 사용 가능하도록 컴파일되어야 합니다. 디폴트 세팅을 사용하는 새 프로젝트에서 번들화된 PSO 캐시 파일은 애플리케이션이 시작된 뒤 자동으로 열리며 컴파일도 자동으로 시작됩니다. 이를 원하지 않는 경우(예를 들어 커스텀 사용 마스크를 설정하려는 경우) r.ShaderPipelineCache.StartupMode=0
을 사용하여 컴파일을 일시정지한 뒤 나중에 FShaderPipelineCache::ResumeBatching()
으로 재개할 수 있습니다.
PSO 컴파일 시 사용 가능한 스타트업 모드에는 몇 가지가 있습니다.
값 | 모드 | 설명 |
---|---|---|
0 | 일시정지(Paused) | 컴파일이 재개될 때까지 정지됩니다. |
1 | 고속(Fast) | 로딩 화면이나 게임 내 인터랙티브하지 않은 기타 부분에서는 고속 모드를 사용하는 것을 권장합니다. |
2 | 백그라운드(Background) | 백그라운드 모드는 플레이어가 UI를 이동하는 도중에 컴파일하기에 적합합니다. |
3 | 프리컴파일(Precompile) | 고속과 백그라운드 모드의 프로퍼티를 결합합니다. 별도의 사전 컴파일 사용 마스크(r.ShaderPipelineCache.PreCompileMask 로 설정됨)를 사용하여 고속 모드에서 일치하는 PSO를 컴파일하지만, 나머지 부분에서는 백그라운드 모드를 사용합니다(일반 사용 마스크와 일치하는 부분만). |
게임이 로딩 화면을 더 오래 유지하려는 경우 FShaderPipelineCache::NumPrecompilesRemaining()
을 호출하여 컴파일이 완료될 때까지 처리되지 않은 PSO 수를 쿼리할 수 있습니다.
모드는 더욱 세밀하게 설정할 수 있습니다. 즉, 프리컴파일에 사용된 배치 크기와 프레임당 사전 컴파일에 사용할 타깃 시간을 한꺼번에 설정할 수 있습니다. PSO 컴파일에 걸리는 실제 시간은 게임이 제어할 수 없는 사항입니다.
사용자 캐시 파일
게임에 PSO 캐시를 제공하더라도 사용자는 수집 도중 커버되지 않은 콘텐츠를 마주할 수 있습니다. 일부 드라이버는 자체 캐시를 제공하지만, 드라이버 행동에서 더욱 독립적으로 행동하기 위해 게임은 기본적으로 누락된 PSO 파일을 로컬 사용자 캐시 파일에 수집 및 저장하려고 시도합니다. 이 PSO는 게임의 저장 디렉터리(FPaths::ProjectSavedDir()
)에 위치해 있으며, 게임의 사용자 세팅과 동일한 디렉터리입니다. 애플리케이션은 이러한 사용자 캐시 파일을 시작 시에 로드하고 그 콘텐츠를 빌드에 포함된 파일과 병합합니다.
사용자 캐시 PSO 파일은 기록된 캐시 포맷에 있습니다. 즉 이 파일은 SHA 해시를 사용하여 셰이더를 참조하며, 콘텐츠가 많이 변경되는 대규모 게임 업데이트에서는 사용할 수 없습니다. 그렇기 때문에 각 파일에는 게임 버전 이 임베딩되며, 애플리케이션 실행 시 이 버전을 체크합니다. 이는 아래 예시와 같이 DefaultGame.ini에 환경설정이 되어 있으며 애플리케이션이 콘텐츠 변경 또는 상당한 렌더링 코드 변경을 포함하는 업데이트 등 호환되지 않을 수 있는 업데이트를 배포할 때마다 증가되어야 합니다.
[ShaderPipelineCache.CacheFile]
GameVersion=1234
기본적으로 GameVersion
은 EngineVersion
에서 가져오며, 그런 다음 일반적으로 Perforce 체인지리스트를 세밀하게 트래킹하여 두 가지 다른 빌드에 의해 작성된 사용자 캐시를 관련 변경 사항이 없더라도 호환되지 않게 합니다.
캐시 파일이 무한정 커지지 않도록 애플리케이션이 로딩 시에 즉시 그 안의 엔트리를 '가비지 컬렉션'합니다. 이는 마지막으로 사용된 엔트리 시간을 바탕으로 하며 CVar r.ShaderPipelineCache.UserCacheUnusedElementRetainDays
를 통해 설정이 가능합니다. 디폴트 값은 30일입니다.
Vulkan 및 OpenGL ES RHI는 RHI 내에 자체적으로 더 낮은 로우 레벨 파이프라인 캐시를 갖습니다. PSO는 컴파일된 뒤(소스가 캐시든, 코드에 의한 생성이든) 그 캐시에 저장되며 다음 시작 시에 사용됩니다. 이 그래픽 API를 사용할 때 사용자 캐시를 활성화할 필요는 없을 수도 있습니다.
Q&A
PSO를 얼마나 자주 수집해야 하나요?
이상적으로는 콘텐츠가 추가되거나, 업데이트되거나, 많이 변경될 때마다 프로젝트의 PSO 데이터를 다시 캡처해야 합니다. 그러나 실제로는 빠른 개발 페이스 때문에 그렇게 할 수가 없습니다. 프로젝트마다 PSO 요구 사항이 다르기 때문에 엄격한 일정을 수립하는 것은 불가능합니다. 일반적으로는 빌드가 자주 히치를 일으킨다고 느낄 때 PSO 캐시를 업데이트하는 것이 좋습니다. 특히 최신 캐시가 없는 빌드의 퍼포먼스는 절대 측정하지 마세요. 최소한 이를 측정하여 비교 용도로 써서는 안 됩니다.
특수 PSO 캐싱 레벨을 만들어야 하나요?
프로젝트의 표준 레벨에서 필요한 PSO 데이터를 모두 캡처할 수 있지만, 일부 프로젝트에서는 특수 PSO 캡처 레벨을 사용하는 것이 좋습니다. 이러한 레벨은 특정 타입의 에셋을 모두 스폰하도록 구성하여 그 PSO 데이터를 캡처할 수 있습니다. 이는 프로젝트에 잠금 해제하는 데 시간이 걸리는 콘텐츠나 동적으로 스폰되는 콘텐츠가 있는 경우에 특히 그렇습니다.
PSO를 수집한 적이 없는데 빌드에 PSO 캐시가 있네요!?
나이아가라 콘텐츠가 있다면 그럴 수 있습니다. 컴퓨팅 PSO는 쿠킹 프로세스 도중 캐시에 자동으로 추가됩니다(프로젝트에서 캐시를 활성화한 경우).
빌드에 PSO 캐시가 있는데도 게임이 계속 히치를 일으킵니다.
우선 다음을 확인하세요.
-
게임이 캐시에서 지원하는 그래픽 API로 실행되는지 여부(예: D3D12지만 D3D11은 지원하지 않음 )
-
파일이 게임 시작 시에 열렸는지 여부
-
백그라운드에서 올바른 PSO가 컴파일되고 있는지 여부
이를 확인하려면 로그 파일을 검토하고 LogRHI 로깅 카테고리를 참조합니다. 다음과 같은 라인이 있어야 합니다.
LogRHI: Opened FPipelineCacheFile: ../../../TestGame/Content/PipelineCaches/Windows/TestGame_PCD3D_SM5.stable.upipelinecache (GUID: 91C5586843C2B5CEE3F4F7BE47E71253) with 908 entries.
LogRHI: Display: Opened pipeline cache after state change and enqueued 908 of 908 tasks for precompile.
엔트리 및 태스크 수는 다르겠지만 0이 아니어야 합니다.
둘째로 새 PSO가 몇 개 발견됐는지 확인하세요. 그렇게 하려면 빌드를 -logPSO
명령줄 스위치와 함께(또는 사용자 캐시 파일을 활성화한 상태로) 실행하고 다음과 같은 라인이 얼마나 자주 나타나는지 봅니다.
LogRHI: Display: Encountered a new graphics PSO: 4233039161
PSO:
뒤의 숫자는 다를 것입니다. 그 수가 상당히 크고, 히치와 관련이 있는 경우 캐시에 문제가 있을 확률이 높습니다. 현재 콘텐츠에서 캐시를 다시 수집하세요. 아예 처음부터 다시 시작해야 할 수도 있습니다.
이런 라인이 없거나 D3D11에서 실행 중임에도 계속 빌드가 히치를 일으키는 경우, 그 원인이 PSO와 직접 연관되어 있지 않을 수도 있습니다. 이런 경우에는 CPU 프로파일러로 게임을 프로파일링하여 히치의 원인을 파악할 것을 권장합니다.