병렬 렌더링 개요

언리얼 엔진 4 의 병렬 렌더링에 대한 일반적 수준의 개요서입니다.

Windows
MacOS
Linux

스레드 개요

원래 렌더러는 렌더 스레드(Render Thread)에서 실행되었고, 그 스레드에 대한 명령은 나중 프레임에 실행되도록 게임 스레드(Game Thread)가 큐에 등록시켰습니다. 이러한 명령은 렌더 하드웨어 인터페이스(Render Hardware Interface, RHI) 층으로 호출하는데, 이 RHI 는 지원하는 플랫폼의 다양한 그래픽 API 에 대한 크로스 플랫폼 인터페이스 역할을 합니다.

이 프로세스의 효율을 높이고 지원 플랫폼의 성능을 활용하기 위해, 렌더 스레드는 이제 플랫폼 무관 그래픽 명령을 렌더러의 명령 목록(Command List) 속에 큐 등록(enque)시키는 프론트엔드 역할을 합니다. 그러면 새로운 RHI 스레드는 백엔드에서 적합한 그래픽 API 를 통해 변환(실행)합니다. 이러한 분리 덕에 게임 콘솔, DX12, Vulkan 과 같은 지원 플랫폼에서 독립적인 백엔드 병렬화가 가능합니다. 일반적으로, 프론트엔드에서 병렬 생성되는 것은 무엇이든 백엔드에서 병렬 변환(translate)됩니다.

명령 목록 시스템을 사용하지 않고 실행되는 명령이 몇 가지 있습니다. 예를 들면 Lock, Unlock 작업입니다. 이 명령은 렌더 스레드가 바로 내립니다. 그 경우, 엔진은 RHI 스레드가 플러싱되어 작업이 끝날 때까지 기다리거나, 데이터를 복사하여 큐 등록시켜야 합니다. 이에 대한 구현은 작업과 플랫폼에 따라 달라집니다.

그래픽 명령과 명령 목록

렌더 스레드가 큐 등록하는 명령은 FRHICommand 템플릿에서 파생된 구조체 인스턴스입니다. Execute 함수를 호출하는데, 이 함수는 변환 과정에서 호출되며, 필요한 데이터가 있으면 저장합니다. 명령을 GPU 에 제출할 때는 플랫폼의 최적 퍼포먼스 휴리스틱에 따라 플랫폼 별로 (프레임 당 여러 번 제출할지, 프레임 끝에 한꺼번에 제출할지 식으로) 다르게 제출할 수도 있고, FRHISubmitCommandsHint 명령으로 제출할 수도 있습니다.

변환에 사용되는 메인 인터페이스는 IRHICommandContext 입니다. 각 플랫폼 및 API 마다 파생된 RHICommandContext 가 있습니다. 변환 도중 RHICommandList 에는 작업의 컨텍스트가 되는 RHICommandContext 가 주어지며, 각 명령의 Execute 함수는 RHICommandContext API 속으로 호출합니다. 명령의 컨텍스트는 스테이트-섀도잉, 유효성 검사, 지정된 작업을 하는 데 필요한 API 전용 디테일을 담당합니다.

동기화

게임 스레드, 렌더 스레드, RHI 스레드, GPU 사이 렌더러 동기화는 복잡한 주제입니다. 최상위 레벨에서 언리얼 엔진 4 는 보통 "한 프레임 뒤쳐지는" 렌더러로 구성됩니다. 즉 렌더 스레드가 N 프레임을 처리하는 동안 게임 스레드는 N+1 프레임을 처리한다는 뜻으로, 렌더 스레드가 게임 스레드보다 실행 속도가 빠를 경우는 예외입니다. 여기에 RHI 스레드가 추가되면 살짝 복잡해지는데, RHI 스레드가 N 프레임을 처리하는 도중 N+1 프레임의 가시성 계산을 마치는 식으로 렌더 스레드를 RHI 스레드 앞으로 옮길 수 있기 때문입니다. 최종 결과는, 게임 스레드가 N+1 프레임을 처리하는 도중 렌더 스레드가 N 프레임 또는 N+1 프레임의 명령을 처리하고 있을 수 있으며, RHI 스레드 역시 실행 시간에 따라 N 프레임 또는 N+1 프레임의 명령을 전환하고 있을 수 있습니다. 이러한 보장은 FFrameEndSyncRHIThreadFence 라고 불리는 FRHICommandListImmediate 함수에 의해 중재됩니다. 또한 병렬화가 어떻게 구성되었는지에 관계없이, GPU 에 명령을 제출하는 순서는 싱글 스레드 렌더러에서 명령을 제출하는 순서와 동일합니다. 이는 일관성 보장을 위해 필수이며, 렌더링 코드가 변경되면 반드시 유지해야 합니다.

디버깅

이 동작을 제어하는 콘솔 변수가 여럿 있습니다. 이 단계들 중 많은 부분이 직교성을 띠(서로 무관하)므로 독립적으로 비활성화시켜 테스트할 수 있으며, 시간이 되면 새로운 플랫폼이 도입될 수 있습니다. 예:

명령

설명

r.rhicmdusedeferredcontexts

RHI 명령 디퍼드 컨텍스트 사용 - 0 으로 설정하면, 백엔드의 병렬화를 비활성화시킵니다. 기본값은 1입니다.

r.rhicmduseparallelalgorithms

RHI 명령 병렬 알고리즘 사용 - 0 으로 설정하면, 프론트엔드의 병렬화를 비활성화시킵니다. 기본값은 1입니다.

r.rhithread.enable

RHI 스레드.활성화 - 0 으로 설정하면, RHI 스레드를 완전 비활성화합니다. 명령 목록이 계속 생성될 수는 있지만, 일정 시점에 렌더 스레드에서 바로 전환될 것입니다.

r.rhicmdbypass

RHI 명령 바이패스 - 1 로 설정하면, 명령 목록 생성을 완전 비활성화시키고 렌더러가 원래 그랬던 것처럼 명령 목록 없이 렌더 스레드에서 바로 RHI 명령을 호출하도록 만듭니다. 참고로 RHI 스레드가 비활성화 되어있지 않으면 이 값은 무시됩니다.

Parallel_Rendering_00.png

UE4 에서의 병렬 명령 목록 생성 방식을 보여줍니다.

태그
Select Skin
Light
Dark

새로운 언리얼 엔진 4 문서 사이트에 오신 것을 환영합니다!

문서 사이트에 대한 의견을 모을 수 있는 피드백 시스템을 포함해서 여러가지 새로운 기능을 준비하고 있습니다. 아래 Documentation Feedback 포럼(영문) 또는 언리얼 엔진 네이버 공식 카페(한글) 중 편하신 곳에 의견이나 문제점을 알려 주세요.

새 시스템이 준비되면 알려 드리겠습니다.

네이버 카페
공식 포럼