블로그 이미지
재미있게 게임을 만들고 싶은 아저씨.. jorisma

카테고리

분류 전체보기 (17)
Game programing (13)
System Programing (2)
Global Ilumination (1)
Shader (9)
Renderer Dev (0)
DirectX Document (1)
Engine Programing (0)
Diary (1)
Programming Tip (3)
Spring 2011 (0)
Total
Today
Yesterday

달력

« » 2024.12
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31

공지사항

태그목록

최근에 올라온 글

'Game programing/DirectX Document'에 해당되는 글 1건

  1. 2012.10.10 Resource Management Best Practices

By Chuck Walbourn, Software Design Engineer

Microsoft Game Technology Group

june 2005

 

 

Introduction

 

자동 텍스쳐 관리라고 알려진 텍스쳐 관리는 DirectX 6 버전 이후 부터 가능해졌다. Direct3D 9에서는 api를 사용하여  텍스쳐,버텍스 버퍼, 인텍스 버퍼를 일관된 인터페이스 형태로 자동 리소스 관리가 되었다. Direct3D 리소스 관리 시스템을 사용하여 응용프로그램은 광장히 쉽게 디바이스가 로스트 되는 상황을 해결할 수 있으며, 시스템이 제공하는 합리적인 비디오 메모리 리소스의 over-commit 양을 관리할 수 있게 되었다. ( over-commit 이란 실제 가능한 메모리 양 보다 더 많은 양을 할당할 수 있게 해주는 것을 뜻함 )

 

개발자들은 때때로 시스템의 불명확한 속성때문에 리소리 관리에 어려움을 겪어 왔다. 보통은 리소스 관리를 통해서 좋은 효과를 보지만 오히려 리소스를 관리하지 않는것이 효율이 더 좋을 때도 있다. 여기서는 일반적으로 리소스를 관리하는 가장 좋은 사례를 이야기 논의하며 리소스를 관리하거나 하지 않을시 어떻게 동작하는지, 그리고 어떻게 런타임 및 드라이버에서 일반적으로 리소스가 어떻게 관리되는지 좀 더 자세하게 이야기 할 것 입니다.

 

다음과 같은 기술 조항을 다룹니다.

 

* 비디오 메모리

* 리소스 관리

* 드라이버가 관리하는 리소스

* 디폴트 리소스

* 시스템 메모리 리소스

* 일반적인 권장 사항

 

비디오 메모리 ( Video Memory )

 

비디오 시스템이 리소스를 사용하기 위해, 그 리소스는 GPU가 접근 가능한 메모리에 위치하여야 합니다. 로칼 비디오 메모리는 최고의 효율을 GPU에 제공하며 특정 리소스( 렌더 타켓, 깊이/ 스텐실 버퍼 )는 반듯이 로칼 비디오 메모리에 위치하여야만 합니다. AGP( accelerated graphics port )의 도래로 시스템 메모리의 일부분을 GPU가 바로 접근가능하게 되었다. AGP aperture로 알려진 이 메모리 영역은 비지역( Non-local ) 메모리로 분류되며 다른 목적으로는 사용될 수 없다. 비지역 메모리는 CPU에서 쓰고 읽기가 가능하며 CPU는 로칼 비디오메모리에 직접 접근하는것은 보통 비효율적이다. 그래서 AGP aperture는 CPU와 GPU가 메모리 리소스를 공유하기에 이상적이다. AGP 메모리에 대하여 핵심적인 부분은 로칼 비디오 메모리 처럼 디바이스가 로스트된 상태에서는 무효화 되지만 반듯이 저장되어야 하는 영구적인 리소스가 위치 되어야 한다.

 

 

자료 1. GPU, CPU, 비디오 메모리, 시스템 메모리의 관계

 

           Ee418784.ManagingResources1(en-us,VS.85).gif

 

몇몇 통합 비디오 솔루션은 시스템의 모든 메모리를 주소 지정할 수 있는 통합 메모리 설계(UMA : Unified Memory Achitecture)를 사용하고 있다. Directx3D는 비디오 메모리의 구성의 원리를 적용하여 응용프로그램의 어떠한 변화 없이 UMA를 지원 한다. 그리고 드라이버는 UMA의 속성과 하드웨어 구현의 특정 양식에 대한 이점을 가지지만 예전의 설계( 통합되어 있지 않은 구조 )에서 처럼 상당히 유사히 작동되게 보장한다. 

 

자료 2. 통합 메모리 설계에서는 GPU 와 CPU의 동일하게 접근한다.  

 

            Ee418784.ManagingResources2(en-us,VS.85).gif

 

메니지드 리소스( Managed Resources )

 

대부분의 리소스는 POOL_MANAGED에서 관리되는 리소스로 생성하여야 한다. 모든 리소스는 시스템 메모리에서 생성되며 필요에 따라 비디오 메모리에 복사하게 될 것 이다. 디바이스 로스트 상황에서도 시스템 메모리에서 복사해 자동으로 처리 될 것이다. 관리하고 있는 모든 리소스가 단 한번에 비디오 메모리로 딱 맞춰질 필요가 없기 때문에 메모리를 over-commit 할 수 있다. 이 메모리는 언제든 렌더링 될 수 있는 작은 비디오 메모리 워킹 세트들이 채워져 있다. 중요하는것은 대부분의 백업 메모리( backing-store system memory )는 시간이 지남에 따라 디스크의 페이지에서 넘어간다. 그래서 리셋 명령이 실행되면 lost-device된 비디오 메모리를 복원하기 위해 다시 페이지로 돌리는 과정이 필요한데 이때 많은 시간이 소요된다. 

 

런타임 메니저는 리소스의 마지막 사용 시간에 대한 타임스탬프를 가지고 있다.  필요한 메니지드 리소스( managed resource )를 로딩하는 과정에서 메모리 할당에 실패할때도 이 LRU 방식의 타임스탬프에 근거하여 리소스를 해제하게 된다. SetPriority()를 통해 타임스탬프 보다 더 높은 우선 순위를 가질수 있는데 그래서 일반적으로 빈번히 사용되는 리소스는 더 높은 우선 순위값을 세팅해야 한다. Direct3D 9.0은 비디오 메모리에 대하여 드라이버를 통해서만 정보를 제공해 왔고  런타임에서는 리소스 할당하기 위해 다른 연속된 리소스들을 제거해야 할때도 있다. 앞서 말한 우선순위를 통해 어떤 리소스가  제거된 직후 다시 필요하게 되는 그런 상황을 방지하는데 도움이 된다. 응용프로그램은 EvictManagedResources()를 사용할 수 있는데 이 함수는 모든 메니지드 리소스를 강제로 제거할 수 있다. 다시 말해 다음 프레임을 위해서 필요한 모든 리소스를 다시 로드하는 작업은 시간을 많이 소요할 수 있다. 하지만 이것은 작업데이터(working set)가 크게 변하는 레벨 트랜지션( level transitions )과 비디오 메모리 단편화에는 매우 유용하다.

 

frame count는 런타임 메니저에서 제거하기 위해 선택된 리소스가 지금 프레임 보다 먼저 사용되졌는지 확인할 수 있다. 이것은 비디오 메모리에 넣을 수 있는 양보다 더 많은 리소스가 한프레임내에서 사용되어지는 "thrashing" 상황( 실제 메모리보다 사용하는 data가 더 많아서, paging이 과도 하게 일어나는 상황 )에서 사용될 수 있다. frame count는 LRU( Least Recently Used : 가장 예전에 사용된 ) 보다는 MRU( Most Recently Used : 가장 최근에 사용된 )로 바꾸는 전환정책을 작동하게 한다. 특히나 thrashing 상황에서는 약간의 성능 향상에 도움이 된다.  thrashing 상황은 렌더링 효율에 심각한 문제를 만들어 낸다. 여기서 현재 프레임은 EndScene()을 기준으로 하며 그래서 메니지드 리소스를 사용하는 모든 응용프로그램은 일정하게 이 함수를 호출할 필요가 있다. (BeginScene 호출 이후, EndScene 호출될 때, 이때 사용하는 managed 리소스의 양이 비디오 메모리 보다 많으면, thrashing 상황이 발생한다.)

 

개발자에게 그들의 응용프로그램에서 메니지 리소스가 어떻게 행동하는지에 대한 더 많은 정보를 찾는 방법은 IDirect3DQuery9 Interface를 통해 RESOURCEMANAGER event query를 사용하는 것이다. 이것은 debug runtime을 사용할 때만 동작한다. 그래서 응용프로그램에 의존되지 않은 정보지만,  runtime에 의해 관리되어지는 리소스에 대한 더 많은 정보를 제공할 것이다.

 

리소스 관리자가 어떻게 동작하는지를 이해하는 것은 당신의 응용프로그램을 디버깅하고 변경하는 데, 도움을 줄 수 있는 반면, 당신의 응용프로그램이 드라이버나 현재 runtime 메니저의 세세한 구현에 너무 얽매이지 않도록하기 위해서 중요하다. 드라이버의 리버젼이나 하드웨어의 변화에 따라 이러한 동작은 상당히 변할 수도 있다. 그리고 다음 버젼의 Direct3D는 상당히 향상되고, 대중화된 리소스 관리를 가질 것이다.

 

드라이버가 관리하는 리소스 ( Driver-Managed Resources )

 

Direct3D 드라이버는 D3DCAPS2_CANMANAGERESOURCE 라고 불리는 runtime 메니저를 대신해 리소스 관리를 처리할 수 있는 드라이버 관리 텍스쳐( driver managed texture )를 구현하는것이 자유롭다.  극히 일부이지만 이런 기능을 구현한 드라이버의 리소스 메니져의 정확한 동작 방식은 매우 다양할 수 있고, 어떻게 구현되었는지에 대한 자세한 내용은 드라이버 벤더에 문의해야 한다. runtime 매니저가 항상 사용되기를  원할 때는 디바이스를 생성할 때, D3DCREATE_DISABLE_DRIVER_MANAGEMENT를 사용하면 된다.

 

디폴트 리소스 ( Default Resource )

 

메니지드 리소스는 간단하며 효율적이며 사용하기 쉽다, 하지만 비디오 메모리를 직접 사용하는게 낫거나 필요할때가 있다. 그럴때 리소스를 POOL_DEFAULT로 생성해라. 이런 리소스를 사용하는것은 응용프로그램을 조금 더 복잡하게 하는 요인이 된다. 예를 들어 lost-device 상황에서 모든 POOL_DEFAULT로 생성된 리소스에 대하여 처리하는 코드를 작성해 주어야한다. 그리고 그 코드를 복사할 때도 성능상 여러가지 상황을 고려해 주어야 한다. USAGE_WRITEONLY로 지정하는 것이 실패하거나 lock을 걸수 있는 render target의 생성 실패는 성능에 심각한 부담을 지어준다.

 

만약 어떤 hint flag를 사용하지 않고, Lock을 POOL_DEFAULT 리소스에 호출하는 것은 GPU가 POOL_MANAGED 리소스를 가지고 동작할 때보다 stall(아무 동작도 하지 못하는 상태)에 빠지기 쉽다. 리소스의 위치에 따라 리턴된 포인터는 임시 시스템 메모리 버퍼(temporary system memorly buffer)를 가르키거나  AGP 메모리를 바로 가르키는 포인터일 수 있다. 만약 임시 시스템 메모리 버퍼라면, data는 Unlock이 호출된 뒤 비디오 메모리로 전송되어져야 한다. 만약 AGP 메모리 영역이라면, 임시 복사는 피할 수 있으나, 요구되는 캐쉬 동작은 성능에 나쁜 영향을 끼칠 수 있다

 

읽고 쓰기 반복( read-write cycle )을 유발하는 write-combing 문제점을 피하기 위해서 AGP aperture 메모리를 가르키는 포인터를 사용하여 데이터의 전체 캐쉬라인을 쓸때에는 특히나 주의를 기울여야 하며, 메모리의 순차적 접근을 지향한다. 만일 응용프로그램이 데이터 생성중에 임의 접근해야 하거나 버퍼에 메니지드 리소스를 사용할 생각이 없다면 시스템 메모리 복사하여 작업을 하여야 한다. 일단 데이터가 생성되면 캐쉬 write-combing 작동을 위한 높은 비용을 피하기 위해 생성된 데이터를 locked 리소스 메모리에 보낼 수 있다.

 

LOCK_NOOVERWRITE 프래그는 어떤 리소스를 효과적인 방법으로 데이터를 추가할때 사용되며, 완벽하지는 않지만 같은 리소스에 대한 여러번의 Lock 과 Unlock의 호출을 피할 수 있다. 다양한 Lock flag를 적절히 사용하는 것은 lock을 건 메모리를 채울 때, 캐쉬에 유리한 data 접근 방법을 선택할 수 있으므로 성능최적화에 중요하다.

 

리소스 관리와 디폴트 리소스의 혼합 ( Using Both Managed and Default Resources )

 

POOL_MANAGED와 POOL_DEFAULT를 같이 사용하면 비디오 메모리의 단편화와 런타임 메니저의 입장에서 사용비디오 메모리 확보에 혼란을 야기시킬 수 있다. 이론적으로는 POOL_MANAGED 리소스를 호출하기전에 모든 POOL_DEFAULT 리소스를 생성하거나, POOL_DEFAULT 리소스를 할당받기 전에 EvictManagedResource 함수를 호출하여야 한다. 중요한것은 비디오 메모리에 있는 POOL_DEFAULT로 만든 모든 할당된 메모리는 리소스 메니저라고 해도 어떤 목적에서든 해제전까지 사용될 수 없음을 기억하라.

 

이전의 다른 Direct3D버전과는 달리 9버전에서는 runtime 메니저가 비디오 메모리 부족으로 관리되지 않는( unmanaged ) 리소스를 할당하다가 실패하기 전에 관리되지 않는 리소스는 해제하게 한다. 그러나 이것은 잠재적으로 추가적인 단편화를 발생시키거나 최적의 장소가 아닌 위치에 리소스를 할당 되도록 한다. ( 예를 들어 static texture를 로칼 비디오 메모리에 위치 하지 못하는것 ) 다시 말해, 필요한 모든 unmanaged 리소스는 managed 리소스를 사용하기 전에 먼저 할당하는것이 최선의 방법이다.( POOL_DEFAULT를 사용한 리소스는 POOL_MANAGED가 사용하기전에 먼저 할당해야 한다 ) 

 

동적 디폴트 리소스 ( Dynamic Default Resource )

 

아주 빈번히 생성되고, 업데이트되는 데이터는 백업 메모리( backing-store system memory )에 둘 필요가 없다. 왜냐면 디바이스가 복원될때 모든 정보가 다시 생성되기 때문이다. 그러나 이런 데이터는 일반적으로 USAGE_DYNAMIC 플래그를 지정하고 POOL_DEFAULT로 생성하는것이 최선이다. 그렇게 함으로 드라이버가 자주 업데이트 될 리소스를 배치할 때 최적의 위치를 결정할 수 있다. 이것은 일반적으로 비지역 비디오 메모리에 위치하는것을 의미하는데 그러므로 일반적으로 GPU가 로칼 비디오 메모리에 접근하기 보다 훨씬 느리다. 통합 메모리 설계( UMA )에서는 드라이버가 CPU의 쓰기 접근을 최적화하기 위해 동적 리소스를 특정한 위치에 둘 수 있다.

 

이 USAGE_DYNAMIC usage는 전형적으로 소프트웨어 스키닝과 버텍스/인덱스 버퍼로 채워지는 CPU기반 파티클 시스템에 사용된다. 그리고 LOCK_DISCARD 프래그는 이전 프레임에서 지금도 사용되어지는 리소스일지라도 stall이 발생하지 않는다. 이런 경우에 메니지드 리소스를 사용하면 시스템 메모리 버퍼에서 비디오메모리로 복사하게 되는데 단 1~2 프레임 동안 사용되고 업데이트 시킨다. 비지역 비디오 메모리를 가진 시스템은 이런한 동적상황에서 적절히 사용하면 추가적인 복사 과정을 줄일 수 있다.

 

* 그래픽스 프로세스에서는 정적 정점 버퍼에 lock를 실행 시킬시 퍼포먼스를 큰폭으로 하락 시킬 수 있다. lock의 호출은 호출한 응용프로그램에서 리턴 되어 돌아 오기전까지 그래픽스 프로세스가 버퍼로 부터 정점 데이터 또는 인덱스 데이터의 읽기를 완료하기 위해 대기하기 때문에 대폭적인 지연을 발생 시킨다. 이론상 점정 데이터 또는 인덱스 데이터가 전혀 변화하지 않아야 하지만 응용프로그램에서 매 프레임마다 그 데이터들을 변경하는 경우가 많다. 그래서 USAGE_DYNAMIC 프래그를 사용하는데 이는 빈번한 lock 처리에 최적화 되어있다. ( D3DLOCK_DISCARD 프래그를 사용하여 lock을 하면, 그래픽스 프로세스가 아직 버퍼를 사용하고 있을 지라도 새로운 메모리 영역을 리턴한다. 그러므로  lock을 호출하여 데이터 읽기가 완료 되지 않아도 그래픽스 프로세스는 기존에 데이터를 그대로 사용할 수 있다는 것이다. )

 

표준 텍스쳐는 lock되어질 수  없고 다만 UpdateSurface 나 UpdateTexture를 통해서만 업데이트 되어 진다. 어떤 시스템은 LOCK_DISCARD 패턴을 사용하여, Lock을 걸 수 있는 동적 텍스쳐를 지원하다. 하지만, 그러한 리소스를 사용하기전에 가용 비트(capabilities bit)를 (D3DCAP2_DYNAMICTEXTURES)로 적용하여야 한다. 자주 바뀌는 texture( video나 순차 )는 응용프로그램에서 POOL_DEFAULT와 POOL_SYSTEMMEM 리소스의 짝을 두어 생성할 수 있고, UpdateTexture API를 통해 비디오 메모리 업데이트할 수 있다. 일부만 자주 바뀌는 경우는 UpdateTexture 유형을 더 좋은 선택이다.

 

동적처리가 많은 시스템을 만들 때는 동적리소스가 유용하도록 주의를 기울여라. 정적 리소스는 POOL_MANAGED를 사용하여 지역 비디오 메모리를  활용할 수 있게 하며 제한된 버스와 메인 메모리의 대역폭을 생각하여 효과적으로 만들어야 한다. 덜 정적( semi-static)리소스는 지역 비디오 메모리로 자주 복사되어야 하는 부담은 있지만, 그것을 동적으로 사용해서 발생하는 지속적인 버스 트래픽 보다 효율적일 것 이다.

 

시스템 메모리 리소스 ( System Memory Resource )

 

리소스는 POOL_SYSTEMMEM로 생성할 수 있다. 그 리소스는 그래픽 파이프라인에서는 사용할 수 없는 반면, UpdateTexture와 UpdateSurface를 통한 POOL_DEFAULT 리소스를 업데이트함으로 사용할 수 있다. 만약 앞전에 언급된 방법중 하나로 사용되어진다면, stall을 발생할 수 있다하더라도 그것의 locking 방식은 간단하다.

 

비록 이런 리소스는 시스템 메모리에 있지만, POOL_SYSTEMMEM 리소스는 디바이스 드라이버에 지원되어진 용량과 형식에 제한 받는다. POOL_SCRATCH 리소스형은 runtime메니저에 의해서 지원되는 모든 형식과 용량을 사용할 수 있는 또 다른 시스템 메모리의 한 유형이지만, 디바이스는 접근할 수 없다. Scratch 리소스는 컨텐츠 툴에 의해 주로 사용되어지는 경향이 있다

 

자료 3. 비디오 RAM, AGP aperture, 시스템 RAM의 메모리 리소스

 

Ee418784.ManagingResources3(en-us,VS.85).gif

 

일반적인 권장사항

 

올바른 리소스 관리에 대한 상세한 기술적인 구현을 가지고 응용프로그램의 성능을 목표치까지 올리는데 많은 시간이 소요될 것이다. 어떻게 리소스가 Direct3D로 보내질지 계획하고 시간내에 데이터가 로드하기 위한 건축학적인 설계는 더 복잡한 일이다. 아래는 응용프로그램에서 이런 결정들을 할때 권장할 수 있는 가장 좋은 방법들이다.

 

* 당신의 모든 리소스에 대해 전처리를 해라. 리소스를 읽어들일 때, 변환하고, 최적화하는 것이 개발 중에는 편리하나, 높은 성능에 대한 부담은 사용자 컴퓨터에 발생하게 한다. 전처리된 리소스는 읽어들이고, 사용하는 데 더 빠르고, 그리고 복잡한 오프라인 작업을 선택할 수 있다.

 

* 프레임 중에 많은 리소스를 생성하는 것을 피하라. 이 작업은 드라이버의 상호작용이 CPU와 GPU를 직렬화할 수 있고, 그에 따르는 연산은 자주 Kernel transition이 요구 되어지는 무거운 작업이다. 리소스 생성을 여러 프레임에 걸쳐 나누어서 하거나, 생성, 소멸을 하지 않고 재 사용하라. 이론적으로 당신은 최근에 렌더링되는 데 사용한 리소스를 해제하거나 locking하기 전에는 여러 프레임을 지나고 해야한다. ( 역자 - 최근에 렌더링에 사용하기 위해 만들어진 리소스가 바로 바로 해제하거나 lock을 거는것 보다 여러 프레임이후에 하면 그 중간에 다시 사용되어 질 수 있기 때문에 이런말을 하는거 같다. ) 

 

* 프레임의 끝에는 모든 리소스 channel(stream sources, texure sources, current indices)의 레퍼런스를 관리하라. 이렇게 하는것은 연결되어있는 레퍼런스를 정리하여 리소스 메니져로 하여금 더이상 사용되지 않을 리소스를 유지하지 않게 도와준다.

 

* texture는 밉맵( mip-maps )을 가진 압축 형식(DXTn)을 사용하고 texture atlas(여러 texture를 하나로 만들어 사용 ) 사용을 고려해라. 이것은 필요 대역폭을 상당히 줄여주고, 전체 리소스 크기를 줄여서 더 효율적으로 만들어 준다.

 

* geometry는 indexed geometry를 사용해라. 왜냐하면, 이것은 vertex buffer 리소스를 압축하는 데, 도움을 추고, 최근 비디오 하드웨어는 vertex의 재사용에 상당히 최적화되어 있다. Programmable Vertex Shader를 사용함으로써, 당신은 vertex 정보를 압축할 수 있고, vertex processing 중에 vertex 정보를 확장할 수 있다. 다시말해 이것은 필요 대역폭을 줄이고, vertex buffer 리소스를 좀 더 효율적으로 사용할 수 있도록 도와 준다.

 

* 당신의 리소스 관리에 과도한 최적화에 대해 주의해라. 다음 버젼의 드라이버, 하드웨어, 운영체제는 응용 프로그램에서 특정한 조합에 대해 너무 많은 조정을 가하면 잠재적으로 호환성에 문제를 일으킬 수도 있을 것이다. 대부분의 응용프로그램이 CPU Bound(CPU 속도가 가장 많은 부하를 차지한다는 뜻)이기 때문에 너무 과도한 CPU기반 관리는 일반적으로 그 성능 문제를 해결하기 보다는 성능 상 문제를 읽으킨다.

 

원문 번역하다가 번역본이 존재하여 그것을 토대로 정리 및 잘 이해가지 않는 부분은 다시 번역하였습니다.  

 

참고 번역본 - http://rushtoo.tistory.com/9

원문 - http://msdn.microsoft.com/en-us/library/windows/desktop/ee418784(v=vs.85).aspx

 

 

Posted by jorisma
, |

최근에 달린 댓글

최근에 받은 트랙백

글 보관함