DirectStorage 개요
소개
이 항목에서는 Xbox Series X|S 콘솔 전용 DirectStorage API에 대한 개요를 제공합니다. 데스크톱의 DirectStorage에 대한 세부 정보는 데스크톱의 DirectStorage를 참조하세요.
PCIe 버스를 사용하여 연결된 최신 NVMe 저장 장치는 매우 높은 수준의 처리량 및 IOPS(초당 I/O 요청)을 달성할 수 있습니다. Win32 API의 오버헤드는 사용 가능한 저장소 대역폭을 활용할 수 있지만 이를 활용할 때 CPU 사용률이 과도하게 증가할 수 있음을 의미합니다. 워크로드가 많은 수의 작은 요청으로 구성된 경우 특히 그렇습니다.
DirectStorage API는 기본 NVMe 하드웨어와 긴밀하게 상호 작용하여 대부분의 운영 체제 오버헤드를 제거하도록 설계되었습니다. 이를 통해 낮은 CPU 사용량으로 더 높은 대역폭을 달성할 수 있습니다. 목표는 단일 CPU 코어의 최대 10%를 사용하면서 초당 최대 50,000개의 요청을 처리할 수 있도록 하는 것입니다.
기존 문제
각 본체 세대마다 고해상도 자산의 필요성이 증가하면서 게임 콘텐츠의 규모도 점점 커지고 있습니다. 기존 Xbox One 하드웨어 및 소프트웨어에는 개발자가 이 차세대 콘텐츠를 위해 하드 드라이브에서 메모리로 데이터를 가져오는 기능을 방해하는 몇 가지 제한 사항이 있습니다.
높은 CPU 사용량
- 기존 Win32 API가 오버헤드에서 전체 CPU 코어를 요구할 수 있습니다.
- 이는 타이틀의 요청 수와 크기를 기반으로 합니다.
디스크의 최대 대역폭이 부족합니다.
- Xbox Series X|S(NDA 항목)권한 부여 필요에서 파일 성능을 최대화하고, Xbox One에서 파일 성능을 최대화(NDA 항목)권한 부여 필요 백서는 이 내용에 대해 자세히 설명합니다.
디스크 요청의 우선 순위를 정할 수 없음
- 타이틀 요청의 우선 순위를 지정하는 기능이 없으면 응답 스트리밍 시스템을 생성하기 어려울 수 있습니다.
디스크 요청을 취소할 수 없음
- 요청을 취소하는 기능이 없으면 추론적 읽기 시스템을 생성하기 어려울 수 있습니다.
하드웨어 가속 압축 풀기 안 함
- 하드웨어 가속 압축 풀기를 사용하지 않으면 소프트웨어에서 압축 풀기를 수행하는 데 많은 CPU 리소스가 필요할 수 있습니다.
DirectStorage API 집합은 이러한 각 문제를 직접 해결합니다. 전반적인 효과는 Xbox 파일 시스템의 성능을 크게 향상시킵니다.
CPU 사용량
DirectStorage의 주요 설계 목표는 타이틀이 50K IOPS를 유지하고 단일 CPU 코어의 5~10%만 사용하도록 허용하는 것입니다. 이를 통해 타이틀은 NVMe 저장소 하위 시스템에서 최대 대역폭을 달성하는 동시에 다른 타이틀 요구 사항에 CPU를 사용할 수 있습니다.
DirectStorage는 하드웨어 압축 풀기도 지원합니다. 각 읽기 요청은 NVMe 드라이브에서 내장 하드웨어 압축 해제 블록으로 직접 라우팅될 수 있습니다. 따라서 타이틀에서 압축 해제에 CPU 리소스를 사용할 필요가 없습니다.
대기 중인 파이프라인 모델
DirectStorage는 여러 요청이 큐에 추가되는 일괄 처리 방법을 사용합니다. 나중에 큐가 다음 파이프라인 단계로 플러시됩니다. 이렇게 하면 파이프라인 단계 간 전환에 대한 전반적인 CPU 비용이 즉시 줄어듭니다. 기존 Win32 API 집합에는 각 요청에 대한 전환이 있습니다. DirectStorage 큐는 잠금을 사용하지 않는 알고리즘을 사용하여 경합을 최소화합니다. 각 큐가 플러시될 때 해당 타이틀의 제어 권한이 부여됩니다.
대부분의 경우 Win32 API를 사용하면 디스크의 데이터를 다른 버퍼로 복사해야 할 수 있습니다. 일부 경우에는 데이터를 두 번 이상 복사해야 할 수 있습니다. DirectStorage는 타이틀 제공 대상 버퍼를 각 파이프라인 계층에 직접 매핑하여 이 문제를 해결합니다. 하드웨어는 타이틀에서 제공하는 버퍼에 직접 기록됩니다.
이러한 변경으로 CPU 오버헤드가 크게 줄어듭니다.
압축 해제
하드웨어가 데이터를 압축 해제하는 기능이 향상되었습니다. 이제 NVMe 하위 시스템이 데이터를 제공할 수 있는 속도보다 더 빠르게 다양한 형식을 처리할 수 있습니다. 또한 DirectStorage는 적절한 압축 해제를 지원하므로 압축 및 압축 해제된 데이터에 대해 별도의 버퍼를 관리할 필요가 없습니다.
하드웨어는 BCPACK, DEFLATE를 지원하고 최종 콘텐츠를 스위즐 처리할 수 있는 기능을 제공합니다. 이러한 형식은 상호 배타적인 것이 아닙니다. 세 가지 모두 데이터에 적용할 수 있습니다. 이를 통해 타이틀에 가장 적합한 압축 비율과 성능을 제공하는 방법을 선택할 수 있습니다. 자산마다 서로 다른 압축 및 스위즐 설정을 사용할 수 있습니다.
큐 깊이
이전 권장 사항은 회전 드라이브에서 한 번에 12~16개의 비동기 요청만 유지하는 것이었습니다. 더 많아지면 성능에 대한 이점이 없고 더 적어지면 성능이 크게 저하되었습니다. 이로 인해 타이틀이 미해결 읽기 요청의 균형을 맞춰 권장 대상 내에 유지하기 위해 추가 작업을 수행하게 되었습니다.
DirectStorage를 통해 타이틀이 50,000 IOPS를 달성할 수 있도록 하는 목표로 권장 사항이 변경되었습니다. 타이틀은 더 이상 미해결 작업과 큐 깊이 사이에서 균형을 유지하려고 할 필요가 없습니다. 타이틀은 모든 미해결 요청을 제출해야 합니다. 일부 요청을 보류하는 것에 대한 이점은 없습니다. 대부분의 경우 요청을 보류하면 새 요청을 기다리면서 하드웨어가 중단되어 성능이 저하될 수 있습니다.
운영 체제는 경우에 따라 더 큰 읽기 요청을 여러 개의 작은 요청으로 분할해야 합니다(예: 디스크 조각화 처리). 하지만 이 사항은 DirectStorage 아키텍처에서 고려되었습니다. 50,000 IOPS 설계 목표는 하드웨어에 대한 최종 요청이 아니라 IO 작업의 타이틀 수를 기반으로 합니다.
알림
Win32 아키텍처에서는 읽기 완료 알림에 많은 양의 오버헤드가 소비됩니다. 타이틀은 OVERLAPPED 구조를 폴링하거나, 연결된 이벤트 핸들을 기다리거나, 동기 차단 읽기를 수행할 수 있습니다. 전반적으로 이로 인해 각 읽기 요청의 리소스 요구가 증가합니다.
DirectStorage는 알림의 두 비동기 개념을 유지하면서 세 번째 메서드를 추가합니다. DirectStorage에서는 동기식 차단 읽기를 지원하지 않습니다. 타이틀이 자체 시스템을 구현할 수는 있지만 권장되지는 않습니다.
첫 번째 비동기 메서드는 연관된 요청이 완료될 때 설정되는 상태 차단을 통해 구현됩니다. 타이틀은 읽기 완료 시점을 결정하기 위해 필요에 따라 차단을 폴링할 수 있습니다. 이는 완료를 위해 OVERLAPPED 구조를 폴링하는 Win32 메서드와 비슷합니다.
Windows 이벤트 개체를 사용하여 완료 신호를 전송하는 두 번째 비동기 메서드입니다. 이는 해당 Event 개체와 함께 OVERLAPPED 구조를 사용하는 것과 비슷합니다. 타이틀은 WaitForSingleObject 메서드를 사용하여 읽기 작업이 완료될 때까지 호출 스레드가 일시 중단되도록 할 수 있습니다.
세 번째 비동기 메서드는 ID3D12Fence를 사용하여 구현됩니다. 타이틀은 펜스 대기를 중단하거나 원하는 경우 펜스를 폴링할 수 있습니다. 완료된 요청을 직접 알리기 위해 GPU가 펜스를 사용할 수 있다는 이점도 있습니다.
DirectStorage 알림 시스템은 단일 읽기 요청에 바인딩되지 않습니다. 이전의 모든 읽기 요청이 완료되면 신호를 받는 큐에 배치된 항목입니다. 이를 통해 알림에 필요한 세분성에 따라 타이틀의 제어 권한이 부여됩니다. 알림은 항상 큐 순서로 신호를 받습니다. 큐는 FIFO(선입 선출) 큐로 간주될 수 있습니다. 타이틀은 마지막 관련 알림만 쿼리하면 됩니다. 이전에 큐에 추가된 모든 요청은 완료됩니다.
메모리 간 압축 해제
DirectStorage는 디스크 파일 대신 메모리를 압축 해제 원본으로 하여 압축 해제 하드웨어를 호출할 수 있는 큐 형식을 제공합니다. 그러면 압축된 자산이 파일 원본에서 압축되지 않았거나 파일 원본에서 압축 후 메모리에 캐시로 보관된 경우 압축 해제 하드웨어가 사용될 수 있습니다. 메모리 원본 큐에는 메모리 원본 요청만 허용되고, 파일 원본 큐에는 파일 원본 요청만 허용됩니다.
메모리 원본 요청에 압축 해제 옵션이 지정되지 않은 경우 압축 해제 하드웨어가 DMA 복사 엔진으로 작동할 수도 있습니다.
DirectStorage는 완료 알림을 순서대로 보장하지만 요청 처리를 시작할 시점은 보장하지 않습니다. 따라서 보류 중인 요청 간에 데이터 종속성이 없어야 합니다. 즉, 요청 A가 완료된 후 요청 B가 큐에 추가되지 않은 한 요청 A의 대상이 요청 B의 원본으로 사용될 수 없습니다.
메모리 기반 큐는 실시간 우선 순위를 사용하여 만들어야 합니다. 또한 메모리 기반 실시간 요청은 압축 해제가 필요한 디스크 기반 요청을 하기 전에 먼저 처리하는 것이 좋습니다. 디스크 기반 큐에 압축 해제 요청이 없는 경우에는 다른 형식에 영향을 주지 않고 두 큐 유형 전체가 병렬로 처리됩니다.
우선 순위
DirectStorage를 통해 각 큐에 우선 순위 수준이 할당됩니다. 큐의 각 항목은 큐의 우선 순위를 상속합니다. 실시간, 높음, 보통, 낮음의 네 가지 우선 순위 수준이 제공됩니다. 요청은 가중치 라운드 로빈 방식으로 처리됩니다. 예를 들어 우선 순위가 보통인 하나의 요청을 처리하기 전에 우선 순위가 높은 X 요청을 처리합니다. 우선 순위가 낮은 하나의 요청을 처리하기 전에 우선 순위가 보통인 Y 요청을 처리합니다.
우선 순위 가중치는 각 요청의 크기에 따라 계산됩니다. 각 우선 순위 사이의 기본 가중치는 대략 10배입니다. 이는 낮은 우선 순위 요청은 1KB, 보통 우선 순위 요청은 10KB, 높은 우선 순위 요청은 100KB가 각각 처리되었을 것이라는 의미입니다.
기존 Win32 읽기 요청은 동일한 우선 순위 시스템을 통해 라우팅됩니다. 모든 Win32 요청은 보통 우선 순위로 간주됩니다.
메모리 기반 큐는 실시간 우선 순위를 사용하여 만들어야 합니다.
취소
각 DirectStorage 읽기 요청에는 타이틀이 지정하는 64비트 마스크가 연결되어 있습니다. 이는 보류 중인 읽기 요청의 취소를 지원하기 위한 것입니다. 타이틀은 마스크 내의 특정 플래그 집합과 일치하는 요청을 취소할 수 있습니다.
취소가 지원되더라도 하드웨어가 읽기 요청을 처리할 수 있습니다. 타이틀 취소 요청은 최선의 시도입니다. 요청이 하드웨어에서 이미 처리 중인 경우 취소할 수 없습니다.
취소 요청은 최선의 시도이므로 타이틀은 읽기 요청 처리가 완료될 때까지 기다려야 합니다. 큐에서 나중에 알림을 받을 때까지 타이틀은 필요한 리소스를 릴리스할 수 없습니다. 그러나 이 기간 동안 이전 취소 요청에 사용된 플래그와 일치하는 새 요청을 큐에 추가할 수 있으며 취소되지 않습니다.
취소된 요청이 완료되면 성공으로 간주되며, 이는 요청이 취소되어 완전한 결과를 생성하지 않은 경우에도 마찬가지입니다. 즉, 요청에 대해 취소가 시도되는 경우 타이틀은 해당 요청이 완료되더라도 결과를 더 이상 사용하지 않습니다.
보장
Xbox One 및 Xbox One S 본체에는 최소 40MB/s가 보장됩니다. Xbox One X 본체는 최소 보장을 60MB/s로 늘렸습니다. 이러한 수치는 130MB/s 범위에 있는 실제 하드웨어 제한보다 훨씬 낮습니다. 이는 운영 체제로 인한 오버헤드 때문입니다.
DirectStorage는 운영 체제에서 발생하는 대부분의 오버헤드를 제거합니다. 이렇게 하면 하드웨어 제한에 가까운 최소 보장이 허용됩니다. 새로운 최소 성능 보장은 원시 데이터에 대해 250ms 범위에서 2.0GB/s입니다. 콘텐츠에 대한 압축 해제를 사용하면 최종 대역폭이 더 높아집니다.
향후 Xbox 본체는 NVMe 기반의 동적 사용자 설치 가능 드라이브 추가를 지원합니다. 내장형 드라이브에 대해 제공되는 것과 동일한 최소 성능 보장도 사용자 설치 가능 드라이브에 제공됩니다.
API 개요
DirectStorage 인터페이스는 Direct3D 인터페이스와 동일한 패턴을 따릅니다. 처음에는 타이틀이 싱글톤 팩터리를 획득합니다. 팩토리는 요청 큐를 만들고 파일을 여는 데 사용됩니다. 이러한 개체는 각각 하드웨어에 직접 매핑됩니다. 그런 다음 개별 요청이 큐에 추가되어 하드웨어에 제출됩니다.
IDStorageFactoryX
IDStorageFactoryX는 큐를 만들고, 파일을 열고, 보류 중인 요청을 제출하기 위한 기본 인터페이스입니다.
IDStorageFactoryX 개체에는 다음과 같은 메서드가 있습니다.
-
- 하나의 파일을 나타내는 IDStorageFileX 개체를 만듭니다.
-
- IDStorageQueueX 개체를 만듭니다. 읽기 요청을 만드는 데 사용됩니다.
-
- 완료 상태 플래그를 관리하는 IDStorageStatusArray 개체를 만듭니다.
-
- DirectStorage의 out-of-calling-thread 작업을 타이틀 정의 CPU 코어 집합으로 제한합니다.
-
참고 DirectStorage는 호출 스레드에서 대부분의 작업을 수행하고자 시도합니다. out-of-calling-thread 작업은 호출 스레드에서 작업을 수행할 수 없는 경우에만 발생합니다. 예를 들면 다음과 같습니다.
- IDStorageQueueX::Submit을 사용하는 동안 기본 리소스 파이프라인이 꽉 차서 큐의 모든 요청을 전달할 수 없습니다. 나머지 요청은 나중에 리소스가 사용 가능해지면 처리되며 DirectStorage 작업자 스레드에서 수행됩니다.
- ID3DFence 또는 IDStorageStatusArray에 완료 요청을 처리합니다.
-
- DirectStorage가 디버깅을 지원하기 위해 요청 큐에서 추가 유효성 검사를 수행할지 여부를 제어합니다.
-
- 암호화/압축이 해제 되기 전에 저장소 장치에서 로드한 콘텐츠를 일시적으로 저장하는 데 사용되는 스테이징 버퍼의 크기를 설정합니다. 메모리 소스인 큐만 사용돠는 경우에는 스테이징 버퍼의 크기가 0이 될 수 있습니다.
IDStorageFactoryX1
IDStorageFactoryX1 인터페이스는 GetStats 메서드로 IDStorageFactoryX 인터페이스를 확장합니다.
-
GetStats
- DirectStorage 통계를 가져옵니다. 이 기능은 DirectStorage를 기존 진단 및 원격 분석 파이프라인과 통합하는 데 사용할 수 있습니다. 처리가 최소화되어 자주 호출될 수 있습니다. 통계에 Win32 파일 IO 작업이 포함되어 있지 않습니다.
IDStorageFileX
IDStorageFactoryX 개체를 통해 DirectStorage에서 처음으로 모든 파일을 열어야 합니다. 이는 Win32 API 인터페이스에서 CreateFile을 사용하는 것과 같습니다.
파일이 FILE_SHARED_READ 사용 권한으로 열립니다. 필요한 경우 타이틀은 적절한 권한이 적용되는 경우 Win32 API를 사용하여 파일을 동시에 열 수 있습니다. 개발 중 느슨한 배포 및 패키지 배포가 모두 지원됩니다.
파일은 파일 개체에 대해 Close 함수를 명시적으로 호출하거나 일치하는 IDStorageFileX 개체에 대한 마지막 참조가 해제될 때 닫힙니다. 그러나 보류 중인 모든 I/O 작업을 완료해야 파일을 닫을 수 있습니다. 즉, 해당 파일에 대한 모든 미해결 I/O 작업이 완료될 때까지 파일을 닫기 위한 두 가지 방법 모두 차단이 됩니다.
게임은 GetHandle 함수를 호출하여 IDStorageFileX 개체에서 표시하는 파일로 win32 핸들을 가져올 수 있습니다. 핸들은 GENERIC_READ 사용 권한과 FILE_SHARE_READ 공유 모드를 통해 열립니다. 핸들은 파일 크기를 쿼리하는 등에 사용할 수 있습니다. 더 이상 필요하지 않은 경우에는 CloseHandle()을 사용하여 핸들을 닫아야 합니다.
IDStorageQueueX
읽기 요청은 IDStorageQueueX 개체를 통해 NVMe에 제출됩니다. 그러나 큐에서 타이틀이 Submit을 호출하거나 마지막 제출 이후 큐 용량의 절반 이상을 채운 Enqueue 메서드 중 하나가 자동 제출을 트리거할 때까지 요청이 장치에 제출되지 않습니다. 제출은 파이프라인에서 다음 단계에 대한 단일 전환으로 처리됩니다. 이를 통해 타이틀과 커널 간 전환에 CPU 비용이 발생하는 시기를 제어할 수 있습니다.
IDStorageQueueX 개체에는 다음 네 가지 속성이 있습니다.
-
- 큐가 파일 원본 요청 또는 메모리 원본 요청을 받을 수 있는지 여부를 지정합니다.
-
- 큐에 제출된 모든 요청의 우선 순위: 실시간, 높음, 보통 또는 낮음.
- 메모리 기반 큐는 실시간 우선 순위를 사용하여 만들어야 합니다.
- 요청은 우선 순위에 따라 가중치가 매겨진 라운드 로빈 순서로 처리됩니다.
- Win32 요청은 보통 우선 순위 수준에서 처리됩니다.
Capacity
- 큐가 보유할 수 있는 최대 미해결 요청 수입니다.
- 큐가 작동 중일 때 요청을 큐에 넣으려고 하면 하드웨어가 항목을 완료할 때까지 차단됩니다.
- 큐에 필요한 메모리 양은 대략적으로 큐 용량에 DSTORAGE_REQUEST 크기를 곱한 값입니다.
이름
- 전적으로 디버깅에 유용합니다. 이름은 DirectStorage 코드에서 사용되지 않지만 PIX(NDA 항목)권한 부여 필요와 같은 개발자 도구에는 표시될 수 있습니다.
하드웨어는 처리량을 극대화하기 위해 비동기적으로 요청을 처리합니다. 하지만 Win32와 달리 타이틀은 FIFO 순서로 완료될 때 알림을 받습니다. 완료 알림을 받으면 동일한 큐에 대한 모든 이전 요청도 완료되었음이 보장됩니다.
IDStorageQueueX1
IDStorageQueueX1 인터페이스는 EnqueueSetEvent 메서드를 사용하여 IDStorageQueueX 인터페이스를 확장합니다.
EnqueueRequest
이 인터페이스는 Win32 ReadFile 인터페이스와 기능적으로 동일합니다. 개별 읽기 요청이 생성되어 큐에 제출됩니다. 가장 큰 차이점은 DirectStorage에서는 많은 요청이 제출 전에 큐에서 대기할 수 있고 하드웨어 압축 해제 및 취소를 지원한다는 것입니다.
요청에는 다음과 같은 몇 가지 기본 속성이 있습니다.
요청 원본 Options.SourceType 및 Options.SourceIsPhysicalPages의 조합에 따라 DirectStorage에서는 다음 세 가지 속성 그룹 중 하나를 사용하여 원본 데이터가 있는 위치를 지정합니다.
File 및 FileOffset
- 이 그룹은 Options.SourceType이 DSTORAGE_REQUEST_SOURCE_FILE일 때 사용됩니다.
- File은 이전에 IDStorageFactoryX::OpenFile로 열렸습니다.
-
FileOffset은 압축 해제를 사용하는 경우 16바이트 정렬이어야 하며, 압축 해제를 사용하지 않을 경우에는 정렬 요구 사항이 없습니다.
- 비동기 읽기를 위해 파일 내에서 4KiB 정렬이 필요한 Win32의 주요 변경 사항입니다.
Source
- 이 그룹은 Options.SourceType이 DSTORAGE_REQUEST_SOURCE_MEMORY이고 Options.SourceIsPhysicalPages가 FALSE일 때 사용됩니다.
- 압축을 해제할 데이터를 보유하고 있는 메모리 버퍼입니다.
SourcePageArray 및 SourcePageOffset
- 이 그룹은 Options.SourceType이 DSTORAGE_REQUEST_SOURCE_MEMORY이고 Options.SourceIsPhysicalPages가 TRUE일 때 사용됩니다.
- Source와 비슷하지만 원본 메모리 버퍼를 64KB 물리적 페이지의 배열 및 첫 번째 페이지의 바이트 오프셋 형식으로 제공합니다.
- 물리적 64KB 페이지는 XMemAllocatePhysicalPages로 할당될 수 있습니다.
SourceSize
- 메모리 버퍼 또는 파일에서 읽을 원본 데이터의 크기(바이트)입니다.
IntermediateSize
- 이 요청에서 zlib 및 BCPACK 압축 해제가 모두 사용하도록 설정된 경우 IntermediateSize는 원본 데이터의 zlib 압축 해제가 끝나고 BCPACK 압축 해제가 시작되는 중간 크기를 지정하는 데 사용됩니다.
- 그렇지 않은 경우에는 0으로 설정해야 합니다.
요청 대상 Options.DestinationIsPhysicalPages에 따라 DirectStorage에서는 다음 두 가지 속성 그룹 중 하나를 사용하여 대상이 있는 위치를 지정합니다.
Destination
- 이 그룹은 Options.DestinationIsPhysicalPages가 FALSE일 때 사용됩니다.
- 최종 로드된 데이터를 위한 대상 버퍼입니다.
- 압축 해제는 공유 내부 버퍼를 사용하여 발생하며 실행 중인 것으로 간주될 수 있습니다.
DestinationPageArray 및 DestinationPageOffset
- 이 그룹은 Options.DestinationIsPhysicalPages가 TRUE일 때 사용됩니다.
- Destination과 비슷하지만 대상 메모리 버퍼를 64KB 물리적 페이지의 배열 및 첫 번째 페이지의 바이트 오프셋 형식으로 제공합니다.
- 물리적 64KB 페이지는 XMemAllocatePhysicalPages로 할당될 수 있습니다.
DestinationSize
- 로드된 최종 콘텐츠의 예상 크기(바이트)입니다. 대상에 작업을 수용할 충분한 공간이 있어야 합니다.
- 아 크기는 압축 해제를 사용하지 않는 경우 SourceSize와 동일하거나 압축 해제를 사용하는 경우 SourceSize보다 커야 합니다.
CancellationTag
- 타이틀로 정의된 임의의 64비트 태그입니다.
- 이 태그는 취소 요청에 대한 마스크로 사용됩니다.
이름
- 디버깅에 도움이 되는 선택적 문자열입니다. 이름은 PIX(NDA 항목)권한 부여 필요같은 개발자 도구 또는 IDStorageQueueX::RetrieveErrorRecord에서 얻은 오류 레코드에 표시될 수 있습니다. 요청 수명 주기 동안 이름 문자열에 액세스할 수 있어야 합니다.
옵션
-
ZlibDecompress
- RFC 1950 압축 해제 표준을 사용하여 데이터를 압축 해제해야 한다는 점을 나타냅니다.
-
BcpackMode
- 데이터 압축 해제에 사용해야 하는 BCPACK 모드를 나타냅니다.
- None은 유효한 옵션이며 데이터가 BCPACK 압축에 해당되지 않음을 의미합니다.
-
SwizzleMode
- 최종 데이터를 메모리에 스위즐 처리하는 방법을 나타냅니다. 현재 미리 보기 릴리스에서는 DSTORAGE_SWIZZLE_MODE_NONE이어야 합니다.
-
DestinationIsPhysicalPages
- 대상 버퍼가 Destination 대신 DestinationPageArray 및 DestinationPageOffset을 사용하여 지정되었음을 나타냅니다.
-
SourceType
- 요청은 메모리 원본으로서 Source/SourcePageArray 및 SourcePageOffset 속성을 갖거나 파일 원본으로서 File/FileOffset 속성을 가질 수 있습니다.
-
SourceIsPhysicalPages
- 원본 버퍼가 Source 대신 SourcePageArray 및 SourcePageOffset을 사용하여 지정되었음을 나타냅니다.
EnqueueStatus/EnqueueSignal/EnqueueSetEvent
요청은 일련의 관련 요청으로 큐에 추가하고 처리될 수 있습니다. 처리 상태가 큐의 특정 지점에 도달했을 때 알림을 위해 큐에 추가하는 방식으로 수행됩니다. 알림은 이전의 모든 읽기 요청이 완료된 경우에만 처리됩니다. 이렇게 하면 이전의 모든 요청에서 데이터를 즉시 사용할 수 있습니다.
타이틀에는 두 개의 폴링 방법과 알림 대기 방법이 있습니다. 타이틀은 ID3D12Fence 개체 또는 IDStorageStatusArrayX 개체 또는 이벤트 설정 작업을 삽입할 수 있습니다. ID3D12Fence는 ID3D12Fence 개체에 대해 예상대로 작동합니다. 타이틀 스레드는 Event에서 대기할 수 있고 CPU 및 GPU는 펜스를 폴링할 수 있습니다. IDStorageStatusArrayX 개체는 CPU에 의한 완료 폴링 및 가능한 읽기 오류에 대한 액세스를 허용합니다. EnqueueSetEvent 메서드를 사용하면 타이틀 스레드가 폴링 대신 지정된 이벤트를 기다릴 수 있습니다. 이는 Xbox 구현의 ID3D12Fence::SetEventOnCompletion이 신호될 때까지 펜스에서 회전하기 때문에 신호될 때까지 CPU 하드웨어 스레드가 소비되는 반면, EnqueueSetEvent는 타이틀 스레드가 WaitForSingleObject /WaitForMultipleObjects를 사용하여 이벤트가 신호될 때까지 CPU를 다른 스레드에 제공할 수 있도록 허용하기 때문에 ID3D12Fence::SetEventOnCompletion과 다릅니다.
앞에서 언급했듯이 기본 하드웨어가 성능을 위해 다시 정렬하기로 결정한 경우에도 모든 요청이 순서대로 완료됩니다. 알림은 큐에 있는 이전의 모든 요청이 완료될 때까지 신호를 받을 수 없습니다.
압축 해제
압축 해제는 전용 하드웨어를 사용하여 처리됩니다. 이렇게 하면 기존 압축 해제 알고리즘에서 CPU 오버 헤드가 제거됩니다. DirectStorage는 압축 해제를 위해 작업 버퍼로 사용하도록 초기화되는 동안 고정된 메모리 블록을 할당합니다. 이를 통해 적절한 압축 해제가 가능하며 압축된 데이터와 압축 해제된 데이터를 동시에 메모리에 보관할 필요가 없습니다.
압축 해제 하드웨어는 세 가지 작동 모드를 지원합니다. 이들 모드는 상호 배타적이지 않으므로 모드 조합을 지정할 수 있습니다. 압축 해제 모드는 DEFLATE, BCPACK 및 Swizzle 순서로 적용됩니다.
ZLibDecompress
- IETF RFC 1950 압축 표준입니다.
BCPack
- BCPack은 BCn 데이터를 위해 특별히 설계된 사용자 지정 엔트로피 코더입니다. 일반적으로 색 끝점이 팔레트 인덱스(즉, 가중치)와 분리되고 rANS 알고리즘을 사용하여 압축된다는 의미입니다.
Swizzle
- 스위즐 및 무작위 재생 모드는 콘텐츠 파이프라인에서 추가 최적화를 제공할 수 있습니다.
높은 엔트로피 데이터를 압축하면 압축이 실제로 크기가 늘어날 수 있습니다. 반대로 해당 압축 해제는 크기가 축소됩니다. DirectStorage는 축소되는 압축 해제를 허용하지 않으며, 타이틀에서 압축할 수 없는 높은 엔트로피 데이터를 감지하고 이러한 자산은 압축하지 않아야 합니다. 자세한 내용은 DirectStorage 및 XTBC(NDA 항목)를 사용하여 압축된 콘텐츠를 최적화하는 방법권한 부여 필요에 관한 가이드를 참조하세요.
스테이징 버퍼
DirectStorage는 암호 해독 및 압축 풀기 등의 작업을 수행하기 전에 원시 NVMe 저장소에서 읽은 모든 콘텐츠를 레지스트리의 설정을 변경하지 않고 복사하기 위해 내부적으로 버퍼를 사용합니다. 이 스테이징 버퍼를 사용하여 NVMe 드라이브와 암호 해독/압축 해제 실리콘은 파이프라인에서 병렬로 작동할 수 있습니다. 기본값은 32MiB이고 첫 번째 DirectStorage 공장 포인터가 검색될 때 할당됩니다.
타이틀이 메모리에서 메모리로의 압축 해제 작업만을 위해 DirectStorage를 사용하는 경우, 스테이징 버퍼가 필요하지 않으며 SetStagingBufferSize를 호출하여 스테이징 버퍼 크기를 0으로 설정할 수 있습니다. IDStorageQueueX 개체나 IDStorageFileX 개체가 없는 경우에만 SetStagingBufferSize를 호출할 수 있습니다.
DirectStorage의 현재 미리 보기 릴리스는 0 또는 32MiB의 스테이징 버퍼 크기만 지원합니다.
CancelRequestsWithTag
DirectStorage는 요청 취소를 지원합니다. 각 요청에는 관련 64비트 태그로 정의된 타이틀이 있습니다. 목적은 취소할 요청에 대한 비트 마스크 역할을 수행하는 것입니다. 타이틀에는 취소를 위한 마스크와 값이 제공됩니다. 큐는 다음 기준과 일치하는 모든 요청을 취소하려고 시도합니다. tag & mask == value
취소는 최선의 작업입니다. 파이프라인에서 요청이 있는 위치에 따라 취소할 수 없는 경우도 있습니다. 예를 들어 취소할 수 없는 하드웨어로 인해 요청이 압축 해제될 수 있습니다. API는 즉시 반환되며 취소된 모든 요청이 처리되도록 기다리는 것을 차단하지 않습니다. 타이틀은 취소된 요청과 관련된 리소스를 해제하기 전에 큐에서 이후 알림이 신호를 받을 때까지 기다려야 합니다.
CancelRequestsWithTag가 호출되는 동시에 큐에 요청을 추가하지 않도록 주의해야 합니다. 이 경우 동작이 정의되지 않습니다. 하지만 CancelRequestsWithTag에 대한 호출이 반환된 후 큐에 추가된 요청은 해당 기준이 일치하더라도 취소되지 않습니다. 이전에 큐에 추가된 요청만 취소됩니다.
GetErrorEvent/RetrieveErrorRecord
읽기로 인해 오류가 발생하는 경우 해당 읽기는 완료된 것으로 표시됩니다. 큐의 이후 알림은 신호 수신에 대해 차단되지 않습니다. 오류 알림은 GetErrorEvent로 검색할 수 있는 큐와 연결된 Event 개체를 통해 처리됩니다. 타이틀은 GetErrorEvent에 의해 반환된 Event에 대해 WaitForSingleObject를 사용할 수 있습니다. 이벤트가 신호를 받으면 타이틀은 RetrieveErrorRecord 함수를 호출하여 RetrieveErrorRecord 함수를 마지막으로 호출한 후 첫 번째 오류를 확인할 수 있습니다.
RetrieveErrorRecord에 의해 반환된 오류 레코드에는 마지막 RetrieveErrorRecord 이후 큐에서 처음 실패한 요청에 대한 데이터만 포함됩니다. 오류 이벤트에 신호가 전달되지 않거나 데이터가 이미 검색된 경우 오류 레코드의 데이터가 정의되지 않습니다.
쿼리
큐에 대한 정보를 가져옵니다. 여기에는 큐를 만드는 데 사용되는 DSTORAGE_QUEUE_DESC 구조 뿐만 아니라 빈 슬롯의 수와 자동 제출을 트리거하기 위해 큐에 삽입해야 하는 항목의 수가 포함됩니다.
모범 사례
Win32와 동일한 모범 사례가 DirectStorage에도 적용됩니다. 최적의 성능에 대한 임계값이 크게 변경되었습니다.
읽기 크기
회전 디스크에 대한 원래 권장 사항은 최소 128KiB 블록을 읽는 것이었습니다. 블록 크기가 클수록 성능이 계속 증가합니다. 512KiB 크기의 블록이 최고의 성능을 달성했습니다.
NVMe에 이동 부분이 없으면 임계값이 훨씬 낮아집니다. 읽기 성능은 32KiB 읽기로 시작하여 64KiB까지 유지됩니다. 이보다 크게 읽으면 성능이 증가하지 않습니다. 따라서 최적의 성능을 위해 패키지의 데이터를 더 큰 블록으로 병합하는 데 드는 노력이 줄어듭니다.
512KiB 이상일 경우 압축 해제를 사용한다면 하나의 대규모 요청보다 더 작은 규모의 요청을 여러 개 병렬 처리하는 것이 좋습니다. 하나의 큰 요청은 압축 해제를 강제로 직렬화하고, 여러 개의 동시 요청을 통해 여러 압축 해제 하드웨어 장치가 병렬로 작동하여 전체 처리량을 달성할 수 있습니다.
Microsoft GDK(게임 개발 키트)의 2022년 10월 릴리스에서 최대 단일 요청 크기가 대상 32MiB에서 소스 및 대상 메모리 사용의 경우 1GiB로 증가했습니다. 큰 읽기 크기를 허용하는 다른 스토리지 API에서 쉽게 포팅할 수 있도록 제공됩니다. 그러나 최대 처리량을 달성하기 위한 위의 크기 권장 사항은 동일한 상태로 유지됩니다. 큰 요청에서는 여러 압축 해제 하드웨어 장치가 병렬로 작동하지 않기 때문입니다.
Order
이전에는 회전 디스크를 사용하여 디스크에서 읽기 위치를 정렬하는 작업에 시간이 걸렸습니다. 디스크의 순차적 위치에서 읽는 것이 가장 이상적입니다. 이로 인해 검색 시간이 인수로 제거된 디스크 헤드로 최소한의 이동이 발생했습니다. 이렇게 하면 성능이 크게 향상될 수 있습니다. 위치별로 정렬된 임의 읽기를 제출하는 경우에도 이점이 있었으며 경우에 따라 2배 더 빠릅니다.
NVMe 드라이브에서 읽기 요청을 가능한 순차적으로 명령하는 것도 여전히 유용합니다. NVMe 드라이브는 64KiB 정렬 블록으로 읽습니다. 이로 인해 64KiB 블록에서 사용하지 않는 섹션을 읽는 데 대역폭이 낭비될 수 있습니다. 읽기 요청이 4KiB 전용인 경우 60KiB의 대역폭이 불필요하게 사용됩니다. NVMe는 추가 60KiB를 재사용하여 가능한 경우 보류 중인 다른 요청을 충족합니다. 예를 들어 32KiB에 대한 순차적 읽기와 그 다음 8KiB에 대한 순차적 읽기라는 두 개의 읽기를 가진 경우 드라이브에 여전히 하나의 64KiB 읽기만 있습니다.
큐 관리
회전 디스크에 대한 권장 사항은 큐 크기를 12에서 16 사이로 지정하는 것이었습니다. 더 큰 큐 깊이에는 이점이 없었고 더 작은 큐 깊이를 사용하여 성능이 크게 감소했습니다.
NVMe 사양에 따르면 NVMe 드라이브는 큐당 최대 65,536개 항목 깊이로 여러 개의 큐를 지원해야 합니다. DirectStorage는 이 요구 사항을 지원하므로 타이틀이 한 번에 수천 개의 요청을 제출할 수 있습니다.
이전에는 회전 디스크를 통해 타이틀이 큐 깊이를 12~16 범위로 유지하기 위해 보류 중인 요청을 버퍼링했습니다. DirectStorage의 권장 사항은 요청을 버퍼링하지 않고 요청이 생성되는 즉시 큐에 추가하는 것입니다. 전체 시스템은 파이프라인이며 타이틀 버퍼링은 파이프라인에 버블을 생성하여 성능을 심각하게 저하시킬 수 있습니다.
또 다른 권장 사항은 프레임당 생성된 최대 요청 수의 4배 이상의 용량을 가진 큐를 생성하는 것입니다. 이를 통해 충분한 용량을 확보하여 기존 요청이 완료되기를 기다리지 않고 새 요청을 추가할 수 있습니다.
알림 관리
일반적으로 큐에 추가된 알림 요청 수가 적을수록 좋습니다. 권장 사항은 타이틀의 요구 사항과 큐에 있는 알림 요청을 최소로 유지하는 것 사이의 균형을 찾는 것입니다. 각 요청 후 알림을 큐에 추가하면 해당 알림 처리 오버헤드가 증가하여 전반적인 성능이 저하됩니다.
한 가지 예는 콘텐츠를 그룹화하는 것일 수 있습니다. 예를 들어 SFS 텍스처, 지형 요구 사항(예: 메시 및 텍스처) 및 행위자 요구 사항(예: 메시, 텍스처 및 애니메이션)가 있습니다. 이를 통해 단일 알림을 개체를 만드는 데 필요한 모든 자산의 가용성에 바인딩할 수 있습니다.
ID3D12Fence 및 상태 배열 중 하나를 선택하는 것은 타이틀 요구 사항에 따라 다릅니다. GPU에서 데이터를 즉시 사용해야 합니까? 읽기가 완료될 때까지 검사 스레드가 일시 중단될 수 있습니까? 프레임의 특정 지점에서만 데이터를 처리할 수 있어서 정기적인 폴링으로 충분합니까?
고려 사항
동시 요청 수가 훨씬 많을 수 있으므로 타이틀의 다른 부분에 병목 현상이 발생하지 않도록 주의해야 합니다. 타이틀에서 요청을 관리하는 데 드는 비용으로 인해 DirectStorage의 절감 효과가 빠르게 저하될 수 있습니다. 요청당 모든 지원 코드를 확인하고 최소화할 수 있는 항목을 결정하는 것이 좋습니다.
각 요청에 새로운 메모리 블록을 할당해야 합니까?
- 메모리 시스템에는 새 블록을 찾고 내부 목록을 업데이트하기 위한 오버헤드가 있습니다.
- 메모리 블록을 최대한 재사용하는 것이 좋습니다.
관리자 업데이트에 잠금이 필요합니까?
- 더 많은 업데이트가 수행될수록 더 많은 경합이 발생합니다.
- 최대한의 잠금 해제를 고려하세요.
추론적 로딩이 사용됩니까?
- 더 많은 요청 생성으로 이어질 수 있는 취소 기능이 지원됩니다.
- 하지만 메모리를 추론 대상으로 사용할 수 있어야 합니다.
- 추론 임계값에 대한 엄격한 제한을 고려하세요.
참고 항목
DirectStorage 사용법 및 내부 상세 정보(NDA 항목)권한 부여 필요