Share via


리소스 데이터 복사 및 액세스

사용법 플래그는 성능이 가장 좋은 메모리 영역에 리소스를 배치할 수 있도록 애플리케이션에서 리소스 데이터를 사용하는 방법을 나타냅니다. 리소스 데이터는 성능에 영향을 주지 않고 CPU나 GPU에서 액세스할 수 있도록 전체 리소스에 복사됩니다.

리소스를 비디오 메모리 또는 시스템 메모리에 만들어지는 것으로 생각하거나 런타임에서 메모리를 관리해야 하는지 여부를 결정할 필요가 없습니다. 애플리케이션에서 WDDM(Windows Display Driver Model)의 아키텍처를 바탕으로 서로 다른 사용법 플래그를 가진 Direct3D 리소스를 만들어 리소스 데이터가 사용되는 방법을 나타냅니다. 이 드라이버 모델은 리소스에서 사용하는 메모리를 가상화합니다. 사용법이 예상되는 상황에서 성능이 가장 좋은 메모리 영역에 리소스를 배치하는 것은 운영 체제/드라이버/메모리 관리자의 책임입니다.

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

리소스 데이터 복사

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

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

리소스가 만들어진 방법에 따라 기본 데이터에 직접 액세스할 수 없는 경우도 있습니다. 이 경우 원본 리소스에서 해당 프로세서가 액세스할 수 있는 다른 리소스로 리소스 데이터를 복사해야 할 수 있습니다. Direct3D 측면에서는 GPU에서 기본 리소스에 직접 액세스할 수 있고 CPU에서 동적인 준비 리소스에 직접 액세스할 수 있습니다.

리소스가 만들어지면 해당 사용법을 변경할 수 없습니다. 대신, 리소스의 콘텐츠를 다른 사용법으로 만든 다른 리소스에 복사합니다. 리소스 간에 리소스 데이터를 복사하거나 메모리에서 리소스로 데이터를 복사합니다.

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

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

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

CPU에서 데이터를 읽을 수 있도록 기본 사용법의 리소스에서 준비 사용법의 리소스로 데이터를 복사하려는 애플리케이션은 주의해야 합니다. GPU 다시 읽기 문제가 발생할 수 있기 때문입니다. 아래의 리소스 데이터 액세스를 참조하세요.

리소스 데이터 액세스

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

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

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

성능 고려 사항

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

GPU/CPU 병렬 처리의 최악의 시나리오는 한 프로세서가 다른 프로세서의 작업 결과를 기다리게 하는 것입니다. Direct3D는 Copy 메서드를 비동기식으로 만들어 이를 방지합니다. 즉, 메서드가 반환되는 시간까지 복사가 실행되지 않습니다.

이로써 CPU가 데이터에 액세스할 때, 즉 Map이 호출될 때까지 애플리케이션에서 데이터를 실제로 복사하는 데 따른 성능 저하를 피할 수 있습니다. 데이터가 실제로 복사된 후 Map 메서드를 호출할 경우 성능 저하가 발생하지 않습니다. 반대로 데이터가 복사되기 전에 Map 메서드를 호출하면 파이프라인 정지가 발생합니다.

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

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

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

위의 네 가지 상황 중 하나에서 네 번째가 성능에 가장 중요합니다. 애플리케이션에서 리소스나 하위 리소스를 복사하는 호출을 실행하면 명령 버퍼에서 이 호출이 대기됩니다.

그런 다음 애플리케이션에서 명령 버퍼가 플러시되기 전에 복사 호출의 대상이었던 준비 리소스를 매핑하려고 하면 Copy 메서드 호출뿐만 아니라 명령 버퍼에 있는 다른 버퍼링된 명령도 모두 실행되어야 하기 때문에 파이프라인 정지가 발생합니다. 그러면 GPU에서 명령 버퍼를 비우고 마지막으로 CPU에 필요한 리소스를 채우는 동안 CPU에서 준비 리소스에 액세스하려고 대기하고 있기 때문에 GPU와 CPU가 동기화됩니다. GPU가 복사를 마치면 CPU가 준비 리소스에 액세스하기 시작하지만 이 시간 동안 GPU가 유효 상태로 있게 됩니다.

런타임 시 이러한 일이 자주 발생하면 성능이 심각하게 저하됩니다. 이러한 이유로 기본 사용법으로 만든 리소스를 매핑할 때 주의를 기울여야 합니다. 애플리케이션에서 명령 버퍼가 비워지고 이러한 명령이 모두 실행을 마칠 때까지 기다린 다음 해당 준비 리소스 매핑을 시도해야 합니다.

애플리케이션에서 얼마나 오래 기다려야 할까요? CPU와 GPU 간의 병렬 처리를 최대한 활용하기 위해서는 두 프레임 이상 대기해야 합니다. 애플리케이션에서 명령 버퍼에 호출을 제출하여 프레임 N을 처리하는 동안 GPU는 이전 프레임인 N-1의 호출을 실행합니다.

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

Frame 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 ...

 

리소스