XR SDK 디스플레이 하위 시스템은 텍스처 할당, 프레임 라이프사이클, 케이던스 차단을 위한 인터페이스를 제공합니다.
여러 기기 SDK의 경우 일반적인 그래픽스 API가 아닌 SDK 자체를 통해 텍스처를 할당해야 합니다. XR SDK 디스플레이 하위 시스템을 사용하면 외부 플러그인을 사용하여 SDK 텍스처에 블리팅하거나 복사할 필요가 없습니다.
디스플레이 하위 시스템을 사용하면 플러그인 공급자가 텍스처를 할당할 수 있습니다. 가능한 경우 Unity는 불필요한 복사본을 피하기 위해 텍스처에 직접 렌더링합니다. 또한 필요한 경우 텍스처를 자동으로 할당할 수도 있습니다.
다음과 같은 경우 Unity는 텍스처에 직접 렌더링할 수 없으며, 대신 임시 텍스처로 렌더링한 후 텍스처에 블리팅하거나 복사합니다.
EXT_multisampled_render_to_texture
확장을 참조하십시오.kUnityXRRenderTextureFlagsLockedWidthHeight
플래그가 설정되고 renderScale이 1.0이 아닌 경우kUnityXRRenderTextureFlagsWriteOnly
플래그가 설정되고 Unity가 텍스처에서 다시 읽어와야 하는 경우PC와 모바일 모두에서 엔진은 항상 공급자의 텍스처로 확인됩니다. 엔진이 암묵적 확인(텍스처로의 멀티샘플 렌더링 확장을 지원하는 모바일)을 수행하거나 명시적 확인을 수행합니다.
모바일에서 공급자는 kUnityXRRenderTextureFlagsAutoResolve
플래그를 활성화하고 1개의 샘플로 해당 텍스처를 생성해야 합니다.
UnityXRFrameSetupHints.appSetup.sRGB
를 사용하여 Unity가 sRGB 텍스처 포맷으로의 렌더링을 기대하는지 확인합니다. 궁극적으로 공급자는 UnityXRRenderTextureDesc
의 colorFormat
필드에서 출력 텍스처 포맷을 선택합니다. 포맷이 sRGB 타입이면 Unity는 활성 프로젝트가 선택하는 색 공간에 따라 sRGB 쓰기를 활성화하거나 비활성화합니다. 항상 컴포지터에서 sRGB-리니어 전환을 사용하여 sRGB 텍스처에서 샘플링해야 합니다.
SDK에 뎁스 정보가 필요한 경우 위의 컬러 버퍼와 동일한 방식으로 뎁스 버퍼를 얻을 수 있습니다. UnityXRRenderTextureDesc
의 nativeDepthTex
값은 네이티브 리소스를 지정합니다. nativeDepthTex
가 kUnityXRRenderTextureIdDontCare
로 설정된 경우 Unity는 기본적으로 유사한 설명의 텍스처 간에 뎁스 버퍼를 공유합니다.
SDK에 뎁스 정보가 필요하지 않은 경우에는 불필요한 확인을 피하기 위해 UnityXRRenderTextureDesc::depthFormat
을 kUnityXRDepthTextureFormatNone
으로 설정해야 합니다.
제출하는 동안(아래 이동 프레임 제출 섹션 참조) SDK에 Unity가 렌더링할 이중 또는 삼중 버퍼 이미지가 필요한 경우 이를 처리하기 위해 각 프레임마다 다른 텍스처 ID를 지정할 수 있습니다. 공급자 플러그인은 UnityXRRenderTextureId
컬렉션의 관리를 책임집니다.
프레임의 라이프사이클을 담당하는 메서드에는 다음의 두 가지가 있습니다. PopulateNextFrameDesc
는 렌더링 시작 직전에 발생하고, SubmitCurrentFrame
은 렌더링이 완료된 직후에 발생합니다. 두 가지 메서드 모두 그래픽스 스레드에서 호출됩니다.
PopulateNextFrameDesc
동안 디스플레이 공급자는 다음의 작업을 수행해야 합니다.
SubmitCurrentFrame
에서 이 작업을 수행할 수도 있습니다.nextFrame
파라미터를 통해 다음에 렌더링할 텍스처 ID를 Unity에 알립니다.SubmitCurrentFrame
메서드 동안 디스플레이 공급자는 다음의 작업을 수행해야 합니다.
PopulateNextFrameDesc
동안 대기하는 대신 케이던스를 기다립니다.HMD 디스플레이로 렌더링 시 최대한 낮은 지연 시간과 최대 처리량을 유지하려면 포즈를 얻고 텍스처를 제출할 때 정확한 타이밍을 보장해야 합니다. 각 HMD에는 해당 컴포지터가 실행되는 새로고침 속도가 있습니다. 이 속도보다 빠르게 렌더링하면 타이밍 불일치 또는 중복 작업으로 인해 환경이 최적화되지 않습니다.
Unity는 디스플레이 공급자가 프레임 라이프사이클 동안 프레임 케이던스를 차단하거나 기다릴 것으로 기대합니다. Unity는 차단 호출에서 ‘깨어난’ 직후 렌더링 커맨드 제출을 시작합니다. 특정 창 내에서 깨어나기 시간을 컴포지터와 동기화해야 합니다. 일부 SDK는 휴리스틱을 기반으로 부동 깨어나기 시간 창을 제공합니다. Oculus에서는 이를 “사전 대기열”이라고 부릅니다(자세한 내용은 Oculus 개발자 문서 참조). Valve에서는 이를 “러닝 스타트”라고 부릅니다(이 프레젠테이션의 18, 19 슬라이드 참조).
Unity는 포즈별 그래픽스 커맨드를 제출하기 전에 프레임 라이프사이클이 완료되기를 기다립니다.
공급자는 PopulateNextFrameDesc
또는 SubmitCurrentFrame
에서 케이던스를 기다릴 수 있습니다.
Unity가 그래픽스 스레드의 프레임에 대한 그래픽스 커맨드를 제출하는 동안 다음 프레임의 시뮬레이션 루프가 메인 스레드에서 실행됩니다. 여기에는 물리, 스크립트 로직 등이 포함되어 있습니다. PopulateNextFrameDesc
는 모든 렌더링 커맨드가 제출된 후, 그리고 다음 프레임의 시뮬레이션과 그에 대해 예약된 그래픽스 작업이 모두 완료된 후에만 그래픽스 스레드에서 호출됩니다. PopulateNextFrameDesc
가 기다리는 그래픽스 작업 중 하나는 현재 프레임에 대한 SubmitCurrentFrame
입니다. 이는 SubmitCurrentFrame
에서 케이던스를 기다려야 하는 이유입니다. 또한 Unity는 PopulateNextFrameDesc
가 완료될 때까지 렌더링을 시작하지 않습니다.
이러한 세부 정보를 염두에 두면 PopulateNextFrameDesc
가 아니라 SubmitCurrentFrame
에서 케이던스를 기다리는 것에 대한 몇 가지 장단점을 파악할 수 있습니다. 예를 들어 SubmitCurrentFrame
에서 케이던스를 기다리면, 애플리케이션이 시뮬레이션 동안 부하가 높은 그래픽스 작업을 예약할 경우 성능 문제가 발생할 수 있습니다. SubmitCurrentFrame
은 렌더링 후 실행되도록 예약되었으므로, 애플리케이션이 예약한 그래픽스 작업은 SubmitCurrentFrame
후에 그리고 PopulateNextFrameDesc
전에 실행됩니다. 이 경우 공급자는 SubmitCurrentFrame
을 기다린 후 Unity가 렌더링을 시작하기를 기대하며 깨어납니다. 하지만 Unity는 PopulateNextFrameDesc
를 호출하기 전에 애플리케이션이 예약한 그래픽스 작업을 처리하므로, Unity는 렌더링을 시작할 수 있습니다. 렌더링을 위한 깨어나기 시간과 업데이트 메서드에 예약된 그래픽스 작업 처리 간의 지연으로 인해 지연이 발생할 수 있습니다. 개발자는 렌더링 후 그래픽스 작업을 예약하여 그래픽스 작업이 SubmitCurrentFrame
전에 예약되도록 보장함으로써 최적화를 수행할 수 있습니다.
SubmitCurrentFrame
에서 케이던스를 기다리는 공급자는 그래픽스 작업 계산이 메인 스레드에서 병렬로 실행되도록 허용하지만, PopulateNextFrameDesc
에서 케이던스를 기다리는 경우에는 Unity 메인 스레드가 완전히 차단됩니다. 이는 시뮬레이션 및 기타 그래픽스 작업이 이미 완료되었기 때문에 허용됩니다. 시뮬레이션 또는 그래픽스 스레드가 시간을 너무 많이 소모하고 기기의 타겟 프레임 속도를 초과할 경우 문제가 발생할 수 있습니다. 이로 인해 PopulateNextFrameDesc
가 케이던스의 다음 사이클을 기다리는 동안 프레임 속도가 절반으로 줄어들 수 있습니다.
Unity가 SubmitCurrentFrame
을 호출하면 마지막 프레임으로 설정한 텍스처가 렌더링되거나, Unity가 렌더링 커맨드를 그래픽스 드라이버에 제출하여 렌더링합니다. Unity 작업을 완료하면 컴포지터에 전달할 수 있습니다.
렌더링할 다음 프레임을 차단하거나 획득한 후에는 다음 프레임에서 렌더링할 텍스처와 렌더 패스의 레이아웃을 Unity에 알려야 합니다(아래의 렌더 패스 참조).
UnityXRRenderPass
에는 컬링 패스와 씬 그래프 이동이 포함될 수 있습니다. 이는 리소스를 많이 소모하는 작업이므로, 싱글 패스 렌더링 등과 같은 트릭을 통해 Unity가 작업을 수행하는 횟수를 제한해야 합니다.
각 UnityXRRenderPass
에는 출력 텍스처(텍스처 배열일 수 있음), 그리고 뷰, 투사 매트릭스, 렌더링할 사각형 또는 텍스처 배열 슬라이스 등과 같은 UnityXRRenderParams
출력이 포함되어 있습니다.
각 프레임에 대해 디스플레이 공급자는 UnityXRRenderPass
를 설정하고, Unity가 다음 프레임에 렌더링되는 UnityXRRenderTextureId
를 작성합니다.
UnityXRRenderPass
에 대한 사용 사례에는 다음이 포함됩니다.
API는 다음의 추가 사례를 지원합니다(단, 현재는 Unity가 올바르게 응답하지 않을 수도 있음).
다음과 같이 가정할 수 있습니다.
참고: 이 설정은 사용자 셰이더에 영향을 미치므로 Unity 프로젝트와 XR SDK는 싱글 패스 렌더링에 대해 동일한 설정(활성화/비활성화)을 사용해야 합니다. 싱글 패스 렌더링이 활성화되었는지 확인하려면 UnityXRFrameSetupHints.appSetup.singlePassRendering
을 사용하십시오.
cullingPassIndex
가 동일한 값으로 설정된 경우 두 개의 렌더링 패스가 컬링 패스를 공유할 수 있습니다. cullingPassIndex
는 사용할 UnityXRCullingPass
를 선택합니다. 컬링 패스는 UnityXRNextFrameDesc
에 작성해야 합니다.