다음을 통해 공유


리소스 데이터 복사 및 액세스(Direct3D 10)

더 이상 리소스가 비디오 메모리 또는 시스템 메모리에 생성되는 것으로 생각할 필요가 없습니다. 또는 런타임이 메모리를 관리해야 하는지 여부입니다. 새 WDDM(Windows 디스플레이 드라이버 모델)의 아키텍처 덕분에 애플리케이션은 이제 다양한 사용 플래그가 있는 Direct3D 10 리소스를 만들어 애플리케이션이 리소스 데이터를 사용하는 방법을 나타냅니다. 새 드라이버 모델은 리소스에서 사용하는 메모리를 가상화합니다. 그러면 예상되는 사용량을 고려할 때 가능한 가장 성능이 좋은 메모리 영역에 리소스를 배치하는 운영 체제/드라이버/메모리 관리자의 책임이 됩니다.

Default case로 GPU에서 리소스를 사용할 수 있습니다. 물론 리소스 데이터를 CPU에서 사용할 수 있어야 하는 경우가 있습니다. 해당 프로세서가 성능에 영향을 주지 않고 액세스할 수 있도록 리소스 데이터를 전체에 복사하려면 API 메서드의 작동 방식에 대한 이해가 필요합니다.

리소스 데이터 복사

Direct3D에서 만들기 호출을 실행하면 메모리에 리소스가 만들어집니다. 비디오 메모리, 시스템 메모리 또는 다른 종류의 메모리에 리소스가 만들어질 수 있습니다. WDDM 드라이버 모델이 이 메모리를 가상화하므로 이제 애플리케이션에서 어떤 종류의 메모리 리소스가 만들어지는지 추적할 필요가 없습니다.

GPU에서 즉시 액세스할 수 있도록 모든 리소스가 비디오 메모리에 있는 것이 좋습니다. 그러나 CPU에서 리소스 데이터를 읽어야 하거나, CPU에서 쓴 리소스 데이터에 GPU가 액세스해야 하는 경우가 있습니다. Direct3D 10은 애플리케이션에 사용량 지정을 요청하여 이러한 다양한 시나리오를 처리한 다음 필요한 경우 리소스 데이터를 복사하는 몇 가지 방법을 제공합니다.

리소스가 만들어진 방법에 따라 기본 데이터에 직접 액세스할 수 없는 경우도 있습니다. 이 경우 원본 리소스에서 해당 프로세서가 액세스할 수 있는 다른 리소스로 리소스 데이터를 복사해야 할 수 있습니다. Direct3D 10의 관점에서 기본 리소스는 GPU에서 직접 액세스할 수 있으며, 동적 및 스테이징 리소스는 CPU에서 직접 액세스할 수 있습니다.

리소스가 만들어지면 해당 사용량을 변경할 수 없습니다. 대신, 리소스의 콘텐츠를 다른 사용법으로 만든 다른 리소스에 복사합니다. Direct3D 10은 세 가지 방법으로 이 기능을 제공합니다. 처음 두 메서드( ID3D10Device::CopyResourceID3D10Device::CopySubresourceRegion)는 리소스 데이터를 한 리소스에서 다른 리소스로 복사하도록 설계되었습니다. 세 번째 메서드(ID3D10Device::UpdateSubresource)는 메모리에서 리소스로 데이터를 복사하도록 설계되었습니다.

리소스는 크게 매핑 가능한 리소스와 매핑 불가능한 리소스로 나뉩니다. 동적 또는 준비 사용법으로 만든 리소스는 매핑 가능하고, 기본 또는 변경 불가능 사용법으로 만든 리소스는 매핑 불가능합니다.

매핑 불가능한 리소스 간에는 데이터가 매우 빠르게 복사됩니다. 이는 가장 일반적인 경우이고 잘 작동하도록 최적화되었기 때문입니다. 이러한 리소스는 CPU에서 직접 액세스할 수 없으므로 GPU에서 빠르게 조작할 수 있도록 최적화됩니다.

매핑 가능한 리소스 간 데이터 복사는 이보다 문제 발생 가능성이 높습니다. 리소스를 만들 때 지정한 사용법에 따라 성능이 결정되기 때문입니다. 예를 들어, GPU는 동적 리소스를 상당히 빠르게 읽을 수 있지만 쓸 수는 없으며 준비 리소스를 직접 읽거나 쓸 수 없습니다.

기본 사용량이 있는 리소스에서 스테이징 사용량이 있는 리소스로 데이터를 복사하려는 애플리케이션(CPU가 데이터를 읽을 수 있도록 허용(예: GPU 읽기 저장 문제)) 주의해야 합니다. 이 마지막 사례에 대한 자세한 내용은 리소스 데이터 액세스를 참조하세요.

리소스 데이터 액세스

리소스에 액세스하려면 리소스 매핑이 필요합니다. 기본적으로 매핑은 애플리케이션에서 CPU에 메모리에 대한 액세스 권한을 주려고 함을 의미합니다. CPU에서 기본 메모리에 액세스할 수 있도록 리소스를 매핑하는 프로세스는 성능 병목 현상을 일으킬 수 있습니다. 따라서 이 작업을 언제, 어떻게 수행할지 결정할 때 신중을 기해야 합니다.

애플리케이션에서 잘못된 시간에 리소스 매핑을 시도할 경우 성능이 급격히 저하되어 중지로 이어질 수 있습니다. 애플리케이션에서 해당 작업이 완료되기 전에 작업 결과에 액세스하려고 하면 파이프라인 정지가 발생합니다.

잘못된 시간에 매핑 작업을 수행하면 강제로 GPU와 CPU가 서로 동기화되어 성능이 심각하게 저하될 수 있습니다. GPU에서 CPU가 매핑할 수 있는 리소스에 복사를 마치기 전에 애플리케이션에서 리소스에 액세스하려고 할 경우 이러한 동기화가 수행됩니다.

CPU는 D3D10_USAGE_STAGING 플래그를 사용하여 만든 리소스에서만 읽을 수 있습니다. 이 플래그를 사용하여 만든 리소스는 파이프라인의 출력으로 설정할 수 없으므로 CPU가 GPU에서 생성된 리소스의 데이터를 읽으려는 경우 데이터를 스테이징 플래그로 만든 리소스에 복사해야 합니다. 애플리케이션은 ID3D10Device::CopyResource 또는 ID3D10Device::CopySubresourceRegion 메서드를 사용하여 한 리소스의 내용을 다른 리소스에 복사하여 이 작업을 수행할 수 있습니다. 그러면 애플리케이션이 적절한 Map 메서드를 호출하여 이 리소스에 액세스할 수 있습니다. 리소스에 대한 액세스가 더 이상 필요하지 않은 경우 애플리케이션은 해당 Unmap 메서드를 호출해야 합니다. 예를 들어 ID3D10Texture2D::MapID3D10Texture2D::Unmap입니다. 다른 Map 메서드는 입력 플래그에 따라 몇 가지 특정 값을 반환합니다. 자세한 내용은 지도 설명 섹션 을 참조하세요.

참고

애플리케이션이 Map 메서드를 호출하면 액세스할 리소스 데이터에 대한 포인터를 받습니다. 런타임은 기능 수준에 따라 포인터에 특정 맞춤이 있는지 확인합니다. D3D_FEATURE_LEVEL_10_0 이상에서는 포인터가 16바이트에 맞춰집니다. D3D_FEATURE_LEVEL_10_0 미만의 경우 포인터가 4바이트에 맞춰집니다. 16 바이트 맞춤을 사용하면 애플리케이션이 다시 정렬하거나 복사하지 않고 데이터에 대해 기본적으로 SSE 최적화 작업을 수행할 수 있습니다.

 

성능 고려 사항

주 유형의 프로세서가 2개(CPU 하나 이상 및 GPU 하나 이상) 있는 병렬 아키텍처로 실행되는 컴퓨터로 PC를 생각하는 것이 가장 좋습니다. 병렬 아키텍처와 마찬가지로 유휴 상태가 되지 않도록 각 프로세서에 충분한 작업을 예약할 때 그리고 한 프로세서의 작업이 다른 프로세서의 작업을 기다리지 않을 때 최고의 성능이 발휘됩니다.

GPU/CPU 병렬 처리의 최악의 시나리오는 한 프로세서가 다른 프로세서의 작업 결과를 기다리게 하는 것입니다. Direct3D 10은 ID3D10Device::CopyResourceID3D10Device::CopySubresourceRegion 메서드를 비동기화하여 이 비용을 제거하려고 합니다. 메서드가 반환될 때까지 복사본이 반드시 실행되지는 않습니다. 이로써 CPU가 데이터에 액세스할 때, 즉 Map이 호출될 때까지 애플리케이션에서 데이터를 실제로 복사하는 데 따른 성능 저하를 피할 수 있습니다. 데이터가 실제로 복사된 후 Map 메서드를 호출할 경우 성능 저하가 발생하지 않습니다. 반대로 데이터가 복사되기 전에 Map 메서드를 호출하면 파이프라인 정지가 발생합니다.

Direct3D 10의 비동기 호출(대부분의 메서드, 특히 렌더링 호출)은 명령 버퍼라고 하는 항목에 저장됩니다. Microsoft Windows에서 비용이 많이 소요되는 사용자 모드에서 커널 모드로의 전환이 가급적 발생하지 않도록 이 버퍼는 그래픽 드라이버에 대해 내부이며 기본 하드웨어에 대한 호출을 일괄 처리하는 데 사용됩니다.

다음과 같은 네 가지 상황 중 하나에서는 명령 버퍼가 플러시되어 사용자/커널 모드 전환이 발생합니다.

  1. Present 가 호출됩니다.
  2. ID3D10Device::Flush 가 호출됩니다.
  3. 명령 버퍼가 꽉 찼습니다. 명령 버퍼의 크기는 동적이며 운영 체제와 그래픽 드라이버에 의해 제어됩니다.
  4. CPU에 명령 버퍼에서 실행 대기 중인 명령의 결과에 대한 액세스가 필요합니다.

위의 네 가지 상황 중 하나에서 네 번째가 성능에 가장 중요합니다. 애플리케이션이 ID3D10Device::CopyResource 또는 ID3D10Device::CopySubresourceRegion 호출을 발급하는 경우 이 호출은 명령 버퍼에 큐에 대기됩니다. 그러면 애플리케이션이 명령 버퍼가 플러시되기 전에 복사 호출의 대상이었던 준비 리소스를 매핑하려고 하면 복사 메서드 호출을 실행해야 할 뿐만 아니라 명령 버퍼의 다른 모든 버퍼링된 명령도 실행해야 하므로 파이프라인 중단이 발생합니다. 그러면 GPU에서 명령 버퍼를 비우고 마지막으로 CPU에 필요한 리소스를 채우는 동안 CPU에서 준비 리소스에 액세스하려고 대기하고 있기 때문에 GPU와 CPU가 동기화됩니다. GPU가 복사를 마치면 CPU가 준비 리소스에 액세스하기 시작하지만 이 시간 동안 GPU가 유효 상태로 있게 됩니다.

런타임 시 이러한 일이 자주 발생하면 성능이 심각하게 저하됩니다. 이러한 이유로 기본 사용법으로 만든 리소스를 매핑할 때 주의를 기울여야 합니다. 애플리케이션에서 명령 버퍼가 비워지고 이러한 명령이 모두 실행을 마칠 때까지 기다린 다음 해당 준비 리소스 매핑을 시도해야 합니다. 애플리케이션에서 얼마나 오래 기다려야 할까요? CPU와 GPU 간의 병렬 처리를 최대한 활용하기 위해서는 두 프레임 이상 대기해야 합니다. 애플리케이션에서 명령 버퍼에 호출을 제출하여 프레임 N을 처리하는 동안 GPU는 이전 프레임인 N-1의 호출을 실행합니다.

따라서 애플리케이션이 비디오 메모리에서 시작되고 프레임 N에서 ID3D10Device::CopyResource 또는 ID3D10Device::CopySubresourceRegion 을 호출하는 리소스를 매핑하려는 경우 이 호출은 애플리케이션이 다음 프레임에 대한 호출을 제출할 때 실제로 프레임 N+1에서 실행되기 시작합니다. 애플리케이션에서 프레임 N+2를 처리할 때 복사를 마쳐야 합니다.

프레임 GPU/CPU 상태
N
  • CPU가 현재 프레임에 대한 렌더링 호출을 실행합니다.
N+1
  • GPU가 프레임 N 중 CPU에서 보낸 호출을 실행하고 있습니다.
  • CPU가 현재 프레임에 대한 렌더링 호출을 실행합니다.
N+2
  • GPU가 프레임 N 중 CPU에서 보낸 호출 실행을 마쳤습니다. 결과가 준비되었습니다.
  • GPU가 프레임 N+1 중 CPU에서 보낸 호출을 실행하고 있습니다.
  • CPU가 현재 프레임에 대한 렌더링 호출을 실행합니다.
N+3
  • GPU가 프레임 N+1 중 CPU에서 보낸 호출 실행을 마쳤습니다. 결과가 준비되었습니다.
  • GPU가 프레임 N+2 중 CPU에서 보낸 호출을 실행하고 있습니다.
  • CPU가 현재 프레임에 대한 렌더링 호출을 실행합니다.
N+4 ...

 

리소스(Direct3D 10)