주의 : 이 문서는 초심자 튜토리얼이 아닙니다. 기본 개념 정도는 안다고 가정합니다. 초심자는 [ Vulkan Tutorial ] 이나 [ Vulkan Samples Tutorial ] 을 보면서 같이 보시기 바랍니다.

주의 : 완전히 이해하고 작성한 글이 아니므로 잘못된 내용이 포함되어 있을 수 있습니다.

주의 : 이상하면 참고자료를 확인하세요.

정보 : 본문의 소스 코드는 Vulkan C++ exmaples and demos 를 기반으로 하고 있습니다. 



독립적인 Video Card 를 장착한 일반적인 PC 에서의 메모리는 다음과 같이 구성되어 있습니다.



Windows 10 의 작업관리자의 성능탭을 보면 다음과 같이 그 정보가 나와 있습니다.



여기에서 공유 메모리는 호스트 램에 할당될 수 있는 메모리를 의미합니다. 워낙 디바이스 램의 크기가 작다보니 나온 방식이죠. 어쨌든 메모리 속성은 다음과 같이 매핑될 수 있습니다.



VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT 를 지정하면 GPU 전용 메모리에 할당되고, VK_PROEPRTY_HOST_VISIBLE_BIT 를 지정하면 공유 메모리에 할당됩니다. 아무 것도 지정하지 않는다면 CPU 전용 메모리가 되겠죠. 물론 거의 사용되지 않긴 합니다. HOST_VISIBLE 인 경우에는 항상 HOST_COHERENT 이거나 HOST_CACHED 여야만 합니다. 


일반적으로 이미지는 DEVICE_LOCAL 로 생성되고, 버퍼는 HOST_VISIBLE 로 생성되는 것 같습니다. 그리고 버퍼의 경우에는 HOST_VISIBLE 과 함께 HOST_COHERENT 를 사용하고 있는 것 같습니다. 아마도 버퍼에 대한 쓰기 연산이 많기 때문인 것으로 보입니다.


샘플의 "texture" 프로젝트에서는 텍스쳐를 생성해서 메모리를 바인딩합니다.



DEVICE_LOCAL 을 지정했으므로 이것은 GPU 전용 메모리에 할당될 것이라 예측할 수 있습니다. 실제로도 그렇죠.



그럼 이 텍스쳐를 공유 메모리에 할당하는 것도 가능할까요? 속성을 HOST_VISIBLE | HOST_COHERENT 로 변경해 봤습니다.



하지만 이렇게 하면 일치하는 메모리 타입을 찾는데 실패해서 크래시가 납니다( 위의 코드에서 getMemoryType() 호출 ). 


하지만 아직까지 메모리 타입과 힙에 대해서 제대로 이해하지 못했기 때문에 에러가 나도 뭘 봐야할지 갑갑하기만 할 것입니다. 그래서 이 부분에 대해서 좀 더 이해할 필요가 있습니다.


vkGetImageMemoryRequirements() 는 이미지를 위한 메모리 요구사항을 반환합니다.



Memory Management Basics ] 에서 언급했듯이, Vulkan 은 오브젝트 생성과 메모리 할당을 분리하고 있습니다. 그래서 두 번째 인자인 image 에는 이미지의 해상도나 포맷 정보가 포함되어 있죠. 그러면 vkGetImageMemoryRequirements() 는 이를 가져가서 세 번째 인자에다가 적절한 요구사항을 채워 줍니다.



  • size 는 이미지를 생성하는데 필요한 바이트 단위 메모리 크기입니다.
  • alignment 는 이미지를 생성하는 데 필요한 메모리가 몇 바이트 단위에서 정렬되어 있어야 하는지를 의미합니다.
  • memoryTypeBits 는 이미지를 위해 지원되는 메모리 타입들에 대한 비트마스크입니다. i 번째 비트는 VkPhysicalDeviceMemoryProperties 의 i 번째 메모리 타입을 의미합니다.


제가 vkGetImageMemoryRequirements() 를 호출했을 때 다음과 같은 결과를 획득했습니다.



이 이미지를 지원하는 메모리 타입은 1 번과 7 번이라는 의미가 됩니다( 1 번과 7 번 비트가 1 이기 때문입니다 ). 


CapsViewer 를 열어 보도록 하겠습니다. CapsViewer 의 Memory 탭의 내용은 메모리 타입과 힙에 대한 정보를 담고 있습니다. 


스크롤을 마지막까지 내리면 힙 정보가 있죠. 이는 VkPhysicalDeviceMemoryProperties 의 memoryHeaps 에 정의되어 있는 정보를 보여줍니다.



두 개의 힙이 존재하는 것을 보실 수 있습니다. 0 번 힙에는 HEAP_DEVICE_LOCAL 로 지정되어 있고, 1 번 힙에는 HEAP_DEVICE_LOCAL 이 지정되어 있지 않습니다. 이제 스크롤을 위로 올려 보면, 각 메모리 타입에 대한 정의를 볼 수 있습니다. 이는 VkPhysicalDeviceMemoryProperties 의 memoryTypes 에 정의되어 있는 정보를 보여줍니다.


예상하고 계시겠지만 HeapIndex 가 0 이라면 DEVICE_LOCAL 만 활성화될 수 있고, 1 이라면 DEVICE_LOCAL 은 절대 활성화될 수 없겠죠. 



아까 위에서 이미지를 위해서 1 번과 7 번 메모리 타입이 지원된다고 했었죠( 10000010 )? 1 번 메모리 타입은 순수하게 호스트를 위해서만 사용될 수 있습니다. 그러므로 HOST_VISIBLE | HOST_COHERENT 를 지정할 수가 없죠. 


7 번은 아래와 같이 DEVICE_LOCAL 로만 사용할 수 있는 메모리 타입입니다.



그러므로 이 이미지를 위한 메모리는 순수하게 GPU-Only 이거나 CPU-Only 여야 한다는 것을 알 수 있습니다.


어떤 리소스가 어떤 메모리 타입을 사용할 수 있느냐는 리소스를 생성할 때 사용한 정보에 의존합니다. 이 경우에는 VkImageCreateInfo 의 내용에 의존하겠죠. 아직까지 정확하게 어떤 항목들이 영향을 주는지는 잘 모르겠지만, 아마도 usage 가 가장 큰 영향을 주지 않을까 생각하고 있습니다.


정리


리소스의 메모리는 힙에 할당됩니다. 그 힙은 크게 HEAP_DEVICE_LOCAL 플래그를 포함하는 힙과 그렇지 않은 힙으로 나뉩니다. 어떤 힙을 사용하느냐에 따라서 메모리 타입이 결정이 되는데요, 특정 리소스는 그것이 사용할 수 있는 메모리 타입의 종류가 정해져 있습니다.


힙과 메모리 타입의 종류는 VkPhysicalDeviceMemoryProperties 에 저장되어 있으며, CapsViewer 에서 일목요연하게 확인하실 수 있습니다.

+ Recent posts