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