다음을 통해 공유


루트 서명 버전 1.1

루트 서명 버전 1.1의 목적은 설명자 힙의 설명자가 변경되지 않거나 설명자가 가리키는 데이터가 변경되지 않을 때 애플리케이션이 드라이버에 알릴 수 있도록 하는 것입니다. 이렇게 하면 일정 시간 동안 설명자 또는 설명자가 가리키는 메모리가 정적인지 알 수 있도록 드라이버를 최적화할 수 있습니다.

개요

루트 서명 버전 1.0을 사용하면, 설명자 힙의 콘텐츠와 이것이 가리키는 메모리를, 참조되는 명령 목록/번들이 GPU에서 잠재적으로 진행 중일 때 애플리케이션에서 언제든지 자유롭게 변경할 수 있습니다. 하지만 참조되는 명령이 기록된 후에 애플리케이션이 설명자나 메모리를 변경할 수 있는 유연성이 실제로 필요하지 않은 경우가 종종 있습니다.

애플리케이션은 대개 다음과 같은 작업을 쉽게 처리합니다.

  • 명령 목록이나 번들에 설명자 테이블이나 루트 설명자를 바인딩하기 전에 설명자(및 이것이 가리킬 수 있는 메모리)를 설정합니다.
  • 참조하는 명령 목록/번들이 마지막으로 실행을 마칠 때까지 설명자가 변경되지 않도록 합니다.
  • 설명자가 가리키는 데이터가 동일한 전체 기간 동안 변경되지 않도록 합니다.

또는 데이터가 짧은 기간 동안 변경되지 않는다는 것을 애플리케이션이 약속만 할 수도 있습니다. 특히, 명령 목록 실행 중에 루트 매개 변수 바인딩이 현재 데이터를 가리키는 기간 동안 시간에 따라 데이터가 정적 상태가 될 수 있습니다. 다시 말해, 애플리케이션은 설정 시 정적이라는 것을 아는 루트 매개 변수를 통해 설정된 시간 간격 사이 GPU 타임라인에 일부 데이터를 업데이트하는 작업을 실행하려고 할 수도 있습니다.

설명자나 데이터 설명자가 가리키고 변경되지 않는 경우 드라이버가 수행할 수 있는 특정 최적화는 하드웨어 공급업체에 따라 다르며, 중요한 점은 성능 향상 이외의 동작을 변경하지 않는다는 것입니다. 애플리케이션 의도에 대해 최대한 많은 지식을 보존해도 애플리케이션에 부담을 주지 않습니다.

한 가지 최적화는, 데이터 및 설명자의 정적 상태에 대해 애플리케이션이 프라미스할 수 있는 사항을 알고 있는 경우, 많은 드라이버가 셰이더에 의해 보다 효율적인 메모리 액세스를 생성할 수 있습니다. 예를 들어, 특정 하드웨어가 루트 인수 크기에 민감하지 않은 경우 드라이버는 힙의 설명자에 액세스하는 간접 참조 수준을 루트 설명자로 변환하여 제거할 수 있습니다.

버전 1.1을 사용하는 개발자를 위한 추가 작업은 가능할 때마다 데이터의 정적 상태와 휘발성에 대한 프라미스를 해서 드라이버가 적절한 최적화를 수행할 수 있도록 하는 것입니다. 개발자는 정적 상태에 대해 프라미스를 하지 않아도 됩니다.

루트 서명 1.0은 변하지 않고 계속 작동하지만 루트 서명을 다시 컴파일하는 애플리케이션은 이제 루트 서명 1.1을 기본값으로 사용합니다(원하는 경우 버전 1.0을 적용할 수 있는 옵션 포함).

정적 및 휘발성 플래그

다음 플래그는 개별 루트 인수가 설정될 때 이것을 최적으로 처리하는 방법에 대한 전략을 드라이버가 선택할 수 있도록 하는 루트 서명의 일부이며, 원래 컴파일되었을 때 PSO(Pipeline State Object)에 동일한 가정을 포함합니다. 루트 서명은 PSO의 일부이기 때문입니다.

다음 플래그는 앱에 의해 설정되고 설명자 또는 데이터에 적용됩니다.

typedef enum D3D12_DESCRIPTOR_RANGE_FLAGS
{
    D3D12_DESCRIPTOR_RANGE_FLAG_NONE    = 0,
    D3D12_DESCRIPTOR_RANGE_FLAG_DESCRIPTORS_VOLATILE    = 0x1,
    D3D12_DESCRIPTOR_RANGE_FLAG_DATA_VOLATILE   = 0x2,
    D3D12_DESCRIPTOR_RANGE_FLAG_DATA_STATIC_WHILE_SET_AT_EXECUTE    = 0x4,
    D3D12_DESCRIPTOR_RANGE_FLAG_DATA_STATIC = 0x8
} D3D12_DESCRIPTOR_RANGE_FLAGS;

typedef enum D3D12_ROOT_DESCRIPTOR_FLAGS
{
    D3D12_ROOT_DESCRIPTOR_FLAG_NONE = 0,
    D3D12_ROOT_DESCRIPTOR_FLAG_DATA_VOLATILE    = 0x2,
    D3D12_ROOT_DESCRIPTOR_FLAG_DATA_STATIC_WHILE_SET_AT_EXECUTE = 0x4,
    D3D12_ROOT_DESCRIPTOR_FLAG_DATA_STATIC  = 0x8
} D3D12_ROOT_DESCRIPTOR_FLAGS;

DESCRIPTORS_VOLATILE

이 플래그가 설정되면, 루트 설명자 테이블이 가리키는 설명자 힙의 설명자가 애플리케이션에 의해 언제든지 변경될 수 있습니다. 단, 설명자 테이블을 바인딩하는 명령 목록/번들이 제출되고 실행이 완료되지 않은 경우는 제외됩니다. 예를 들어, 명령 목록을 기록하고 실행을 위해 명령 목록을 제출하기 전에 명령 목록이 참조하는 설명자 힙에서 설명자를 변경하는 것이 유효합니다. 이 동작은 루트 서명 버전 1.0에서만 지원됩니다.

DESCRIPTORS_VOLATILE 플래그가 설정 되지 않은 경우 설명자는 정적입니다. 이 모드에는 플래그가 없습니다. 정적 설명자란 설명자 테이블이 명령 목록/번들에 설정될 때 루트 설명자 테이블이 가리키는 설명자 힙의 설명자가 초기화되면 명령 목록/번들이 마지막 실행을 마칠 때 까지 설명자를 변경할 수 없다는 의미입니다. 루트 서명 버전 1.1의 경우 정적 설명자가 기본 가정이며 애플리케이션은 필요할 때 DESCRIPTORS_VOLATILE 플래그를 지정해야 합니다.

정적 설명자가 포함된 설명자 테이블을 사용하는 번들의 경우, 번들이 기록될 때(번들이 호출될 때가 아님) 설명자는 시작 준비가 되어 있어야 하며 번들이 마지막 실행을 마칠 때까지 변경되지 않아야 합니다. 정적 설명자를 가리키는 설명자 테이블은 번들 기록 중에 설정되어야 하며 번들로 상속되지 않아야 합니다. 번들에 설정되고 명령 목록으로 다시 반환된 정적 설명자가 포함된 설명자 테이블을 명령 목록이 사용하는 것은 유효합니다.

설명자가 정적이면 DESCRIPTORS_VOLATILE 플래그를 설정해야 하는 동작이 또 다른 변경됩니다. 버퍼 뷰(Texture1D/2D/3D/Cube 뷰와 반대)의 범위를 벗어난 액세스는 유효하지 않으며 읽기 또는 삭제 쓰기의 기본값 반환보다는 가능한 디바이스 재설정을 포함하는 정의되지 않은 결과를 생성합니다. 애플리케이션이 하드웨어 범위를 벗어나는 액세스 검사에 종속될 수 있는 기능을 제거하는 목적은 드라이버가 정적 설명자 액세스를 루트 설명자 액세스로 승격시킬 수 있도록(더 효율적이라고 간주되는 경우) 허용하는 것입니다. 루트 설명자는 범위를 벗어나는 검사를 지원하지 않습니다.

애플리케이션이 설명자에 액세스할 때 안전한 범위를 벗어난 메모리 액세스 동작에 의존하는 경우 해당 설명자에 액세스하는 설명자 범위를 DESCRIPTORS_VOLATILE 표시해야 합니다.

DATA_VOLATILE

이 플래그가 설정되면 설명자가 가리키는 데이터는 CPU에 의해 언제든지 변경될 수 있습니다. 단, 설명자 테이블을 바인딩하는 명령 목록/번들이 제출되고 실행이 완료되지 않은 경우는 예외입니다. 이 동작은 루트 서명 버전 1.0에서만 지원됩니다.

이 플래그는 설명자 범위 플래그와 루트 설명자 플래그 모두에서 사용할 수 있습니다.

DATA_STATIC_WHILE_SET_AT_EXECUTE

이 플래그가 설정되면, GPU 타임라인에서 실행되는 동안 기본 루트 설명자 또는 설명자 테이블이 명령 목록/번들에 설정된 시점부터 후속 그리기/디스패치가 해당 데이터를 더 이상 참조하지 않을 때까지, 설명자가 가리키는 데이터를 변경할 수 없습니다.

GPU에 루트 설명자 또는 설명자 테이블이 설정되기 전에는, 동일한 명령 목록/번들로도 이 데이터가 변경될 수 있습니다. 데이터를 가리키는 루트 설명자 또는 설명자 테이블이 명령 목록/번들에 아직 설정되어 있더라도 이것을 참조하는 그리기/디스패치가 완료된 경우에는 데이터를 변경할 수 있습니다. 단, 이렇게 하려면 다음에 루트 설명자 또는 설명자 테이블이 역참조되기 전에 설명자 테이블이 명령 목록에 다시 바인딩되어야 합니다. 이렇게 하면 루트 설명자 또는 설명자 테이블이 가리키는 데이터가 변경되었다는 것을 드라이버가 알 수 있습니다.

DATA_STATIC_WHILE_SET_AT_EXECUTE DATA_VOLATILE 간의 중요한 차이점은 드라이버가 추가 상태 추적을 수행하지 않고 명령 목록의 데이터 복사본이 설명자가 가리키는 데이터를 변경했는지 여부를 알 수 없는 DATA_VOLATILE 입니다. 따라서 instance 경우 드라이버가 명령 목록에 모든 종류의 데이터 프리페치 명령을 삽입할 수 있는 경우(예: 알려진 데이터에 대한 셰이더 액세스를 보다 효율적으로 만들기 위해) DATA_STATIC_WHILE_SET_AT_EXECUTE SetGraphicsRootDescriptorTable, SetComputeRootDescriptorTable을 통해 설정된 순간에만 데이터 사전 가져오기를 수행해야 한다는 것을 드라이버에 알립니다. 또는 상수 버퍼 뷰, 셰이더 리소스 뷰 또는 순서가 지정되지 않은 액세스 뷰를 설정하는 메서드 중 하나입니다.

번들의 경우, 실행 시 설정되는 동안 데이터가 정적이라는 프라미스는 번들의 각 실행에 고유하게 적용됩니다.

이 플래그는 설명자 범위 플래그와 루트 설명자 플래그 모두에서 사용할 수 있습니다.

DATA_STATIC

이 플래그가 설정되면, 기록 중 메모리를 참조하는 루트 설명자 또는 설명자 테이블이 명령 목록/번들에 설정될 때까지 설명자가 가리키는 데이터가 초기화되며 명령 목록/번들이 마지막으로 실행을 마칠 때까지 데이터를 변경할 수 없습니다.

번들의 경우 정적 지속 기간은, 호출 명령 목록의 기록과 대조적으로, 번들 기록 중 루트 설명자 또는 설명자 테이블 설정에서 시작됩니다. 또한 정적 데이터를 가리키는 설명자 테이블은 번들에 설정되어야 하며 상속되지 않아야 합니다. 명령 목록이 번들에 설정된 정적 데이터를 가리키는 설명자 테이블을 사용하고 명령 목록에 다시 반환하는 것은 유효합니다.

이 플래그는 설명자 범위 플래그와 루트 설명자 플래그 모두에서 사용할 수 있습니다.

플래그 결합

샘플러는 데이터를 가리키지 않기 때문에 DATA 플래그를 전혀 지원하지 않는 샘플러 설명자 범위를 제외하고 한 번에 최대 하나의 DATA 플래그를 지정할 수 있습니다.

SRV 및 CBV 설명자 범위에 대한 데이터 플래그가 없으면 기본 DATA_STATIC_WHILE_SET_AT_EXECUTE 동작이 가정됩니다. DATA_STATIC 대신 이 기본값을 선택하는 이유는 DATA_STATIC_WHILE_SET_AT_EXECUTE 대부분의 경우 안전한 기본값일 가능성이 훨씬 높지만 DATA_VOLATILE 기본값보다 최적화 기회를 더 잘 얻을 수 있기 때문입니다.

UAV 설명자 범위에 대한 데이터 플래그가 없으면 일반적으로 UAV가 기록되는 경우 DATA_VOLATILE 동작의 기본값이 가정됩니다.

DESCRIPTORS_VOLATILE DATA_STATIC 결합할 없지만 다른 데이터 플래그와 결합할 수 있습니다 . DESCRIPTORS_VOLATILE DATA_STATIC_WHILE_SET_AT_EXECUTE 결합할 수 있는 이유는 휘발성 설명자가 명령 목록/번들 실행 중에 설명자를 준비해야 하고, DATA_STATIC_WHILE_SET_AT_EXECUTE 명령 목록/번들 실행의 하위 집합 내에서 정적성에 대해서만 약속하기 때문입니다.

플래그 요약

다음 표에는 사용할 수 있는 플래그 조합에 대한 요약이 나와 있습니다.

유효한 D3D12_DESCRIPTOR_RANGE_FLAGS 설정 Description
플래그 설정 없음 설명자는 정적(기본값)입니다. 데이터에 대한 기본 가정: SRV/CBV: DATA_STATIC_WHILE_SET_AT_EXECUTE 및 UAV: DATA_VOLATILE. SRV/CBV에 대한 기본값은 대부분의 루트 서명에 대한 사용량 패턴에 안전하게 맞습니다.
DATA_STATIC 설명자와 데이터는 모두 정적입니다. 따라서 드라이버 최적화의 가능성이 최대화됩니다.
DATA_VOLATILE 설명자는 정적이며 데이터는 휘발성입니다.
DATA_STATIC_WHILE_SET_AT_EXECUTE 설명자는 정적이며 데이터는 실행 시 설정되는 동안 정적입니다.
DESCRIPTORS_VOLATILE 설명자는 휘발성이며 SRV/CBV: DATA_STATIC_WHILE_SET_AT_EXECUTE 및 UAV: DATA_VOLATILE 데이터에 대한 기본 가정이 수행됩니다.
DESCRIPTORS_VOLATILE | DATA_VOLATILE 설명자와 데이터는 모두 휘발성이며 루트 서명 1.0과 같습니다.
DESCRIPTORS_VOLATILE | DATA_STATIC_WHILE_SET_AT_EXECUTE 설명자는 휘발성이지만 명령 목록 실행 중에는 설명자를 여전히 변경할 수 없습니다. 따라서 실행 중에 루트 설명자 테이블을 통해 설정되는 동안 데이터가 정적이라는 추가 선언을 결합하는 것이 유효합니다. 기본 설명자는 데이터가 정적일 것으로 프라미스되는 시간보다 오래 동안 효과적으로 정적입니다.

 

유효한 D3D12_ROOT_DESCRIPTOR_FLAGS 설정 Description
플래그 설정 없음 데이터에 대한 기본 가정: SRV/CBV: DATA_STATIC_WHILE_SET_AT_EXECUTE 및 UAV: DATA_VOLATILE. SRV/CBV에 대한 기본값은 대부분의 루트 서명에 대한 사용량 패턴에 안전하게 맞습니다.
DATA_STATIC 데이터는 정적이며 드라이버 최적화 가능성이 가장 높습니다.
DATA_STATIC_WHILE_SET_AT_EXECUTE 데이터는 실행 시 설정되는 동안 정적입니다.
DATA_VOLATILE 루트 서명 1.0과 같습니다.

 

버전 1.1 API 요약

다음 API 호출은 버전 1.1을 사용하도록 설정합니다.

열거형

다음 열거형은 설명자 및 데이터 변동성을 지정하는 키 플래그를 포함합니다.

구조체

업데이트된 구조체(버전 1.0부터)는 휘발성/정적 플래그에 대한 참조를 포함합니다.

Functions

여기에 나열된 메서드는 루트 서명의 모든 버전에서 작동하도록 설계되었으므로 원래 D3D12SerializeRootSignatureD3D12CreateRootSignatureDeserializer 함수를 대체합니다. 직렬화된 형식은 CreateRootSignature API에 전달되는 형식입니다. 루트 서명이 포함된 셰이더가 작성된 경우, 직렬화된 루트 서명이 컴파일된 셰이더에 이미 포함되어 있습니다.

메서드

ID3D12VersionedRootSignatureDeserializer 인터페이스는 루트 서명 데이터 구조체를 역직렬화하기 위해 생성됩니다.

도우미 구조체

일부 버전 1.1 구조체의 초기화를 지원하기 위해 도우미 구조체가 추가되었습니다.

  • CD3DX12_DESCRIPTOR_RANGE1
  • CD3DX12_ROOT_PARAMETER1
  • CD3DX12_STATIC_SAMPLER1
  • CD3DX12_VERSIONED_ROOT_SIGNATURE_DESC

D3D12용 도우미 구조체 및 함수를 참조하세요.

정적 상태 플래그 위반에 따른 결과

앞서 설명한 설명자 및 데이터 플래그(뿐만 아니라 특정 플래그가 없을 때 암시되는 기본값)는 드라이버에 대한 애플리케이션의 작동 방식에 대한 프라미스를 정의합니다. 애플리케이션이 프라미스를 위반하면 유효하지 않은 동작이 됩니다. 즉, 결과가 정의되어 있지 않으며 드라이버와 하드웨어에 따라 다를 수 있습니다.

디버그 계층에는 애플리케이션이 프라미스를 준수하는지 검사하는 옵션이 있습니다. 여기에는 플래그를 설정하지 않고 루트 서명 버전 1.1을 사용하여 제공되는 기본 프라미스가 포함됩니다.

버전 관리

셰이더에 연결된 루트 서명을 컴파일할 때, 최신 HLSL 컴파일러는 기본적으로 버전 1.1의 루트 서명을 컴파일하는 반면 이전 HLSL 컴파일러는 1.0만 지원합니다. 루트 서명 1.1을 지원하지 않는 OS에서는 1.1 루트 서명이 작동하지 않습니다.

셰이더를 사용하여 컴파일된 루트 서명 버전은 /force_rootsig_ver <version>을 사용하여 특정 버전으로 적용할 수 있습니다. 컴파일러가 강제 적용된 버전에서 컴파일되는 루트 서명의 동작을 유지할 수 있으면 버전 강제 적용이 성공합니다. 예를 들어, 최적화 용도로만 사용되고 동작에 영향을 주지 않는 루트 서명에서 지원되지 않는 플래그를 삭제할 수 있습니다.

이렇게 하면 예를 들어, 애플리케이션을 빌드할 때 1.0 및 1.1 둘 다로 1.1 루트 서명을 컴파일하고 OS 지원 수준에 따라 런타임 시 적절한 버전을 선택할 수 있습니다. 하지만 애플리케이션이 셰이더와 별도로 루트 서명을 개별적으로(특히 여러 버전이 필요한 경우) 컴파일하는 것이 가장 공간 효율적입니다. 처음에 셰이더가 연결된 루트 서명으로 컴파일되지 않더라도, 셰이더와 루트 서명 호환성에 대한 컴파일러의 유효성을 검사하는 이점은 /verifyrootsignature 컴파일러 옵션을 사용하여 보존할 수 있습니다. 나중에 런타임 시, 원하는 루트 서명(OS에서 지원되는 적절한 버전)을 별도의 매개 변수로 전달하면서 루트 서명이 없는 셰이더를 사용하여 PSO를 만들 수 있습니다.

루트 서명 만들기

루트 서명

HLSL의 루트 서명 지정