이 챕터는 Vulkan과 Vulkan이 다루고 있는 문제들을 소개하면서 시작할 겁니다. 그 다음 첫번째 삼각형을 그리는데 필요한 것들을 알아보겠습니다. 이후 챕터들을 위한 큰 그림을 그리게 해줄 것입니다. Vulkan API의 구조와 일반적인 사용 패턴을 다루면서 끝내겠습니다.

Vulkan의 기원

이전 그래픽스 API와 마찬가지로, Vulkan은 GPU를 통한 cross-platform 추상화로 설계되었습니다. 이러한 API 대부분이 갖는 문제점은 구성 가능한 고정된 기능으로 대부분이 제한되었던 그래픽스 하드웨어가 설계된 시대였다는 점입니다. 프로그래머는 버텍스 데이터를 표준 형식으로 제공해야 했고, 라이팅과 셰이더 옵션을 GPU 제조 업체에게 의존해야 했습니다.

그래픽 카드 아키텍쳐가 성숙하면서, 점점 더 많은 프로그래밍 가능한 기능을 제공하기 시작했습니다. 이러한 모든 새로운 기능들은 어떻게든 기존의 API들에 통합되어야 했습니다. 이 결과로 최신 그래픽스 아키텍쳐에 프로그래머의 의도를 매핑하기 위해 그래픽스 드라이버 관점에서 이상적이지 않은 추상화와 많은 추측들이 생겼습니다. 게임의 퍼포먼스 향상을 위한 드라이버 업데이트가 많고, 때때로 상당히 차이가 나는 이유입니다. 드라이버의 복잡성 때문에, 어플리케이션 개발자는 셰이더에 허용되는 구문과 같은, 제조사 간의 불일치를 해결해야 했습니다. 이러한 새로운 기능들 외에도, 지난 10년 동안 강력한 그래픽 하드웨어를 지닌 모바일 디바이스들이 유입되었습니다. 모바일 GPU는 에너지와 공간 요구 사항에 따라 아키텍쳐가 다릅니다. 하나의 예로 tiled rendering을 보자면, 프로그래머에게 기능에 대한 좀 더 많은 제어 권한을 제공함으로써 성능 향상이라는 이점을 얻을 수 있습니다. 이러한 API의 또다른 한계는 제한된 multi-threading 지원으로 인해 CPU에서 병목 현상이 발생할 수 있다는 것입니다.

Vulkan은 이러한 문제를 최신 그래픽스 아키텍쳐를 위한 설계를 통해 해결했습니다. 프로그래머가 더 자세한 API를 사용하여 그들의 의도를 명백하게 지정하는 것으로 드라이버의 오버헤드를 줄이고, 다중 thread로 병렬적으로 명령어를 생성하고 제출하도록 합니다. 단일 컴파일러를 통해 표준화된 바이트 코드 형태로 전환하여 셰이더 컴파일의 불일치를 줄입니다. 마지막으로, 그래픽과 컴퓨팅 기능을 하나의 API로 통합하여, 최신 그래픽 카드의 범용 처리 기능을 인정합니다.

삼각형을 그리는데 필요한 것

이제 잘 동작하는 Vulkan 프로그램에서 삼각형을 그리는데 필요한 단계를 살펴보겠습니다. 여기서 소개된 모든 개념들은 다음 챕터에서 자세히 나올 것입니다. 개별적인 구성 요소들간의 관계로 큰 그림을 그릴 수 있도록 도와주는 것입니다.

Step 1 - 인스턴스와 물리적 장치 선택

Vulkan 어플리케이션은 VkInstance를 통해 Vulkan API 세팅을 시작합니다. 어플리케이션과 어떤 API 확장을 사용할지 설명하여 Instance가 생성됩니다. Instance가 생성된 후에는 Vulkan을 지원하는 하드웨어를 쿼리하고, 그것들 중에서 작업에 사용할 VkPhysicalDevice를 선택할 수 있습니다. VRAM 크기 및 장치 기능 같은 요소로 원하는 디바이스를 선택할 수 있습니다. 예를들면 “전용 그래픽 카드를 선호”처럼 말이죠.

Step 2 - 논리적 장치와 큐 패밀리

사용할 하드웨어를 선택한 후에는 VkDevice (논리적 디바이스)를 생성해야 합니다. 여기서 다중 뷰포트 렌더링 및 64비트 부동 소수점과 같이 사용하려고 하는 것들을 VkPhysicalDeviceFeatures에 더 구체적으로 설명합니다. 그리기 명령이나 메모리 작업과 같이 Vulkan으로 수행되는 대부분의 작업은 VkQueue에 제출하여 비동기적으로 실행됩니다. 큐는 각 큐 패밀리가 해당 큐에서 특정 작업 세트를 지원하는 큐 패밀리에서 할당됩니다. 예를 들어, 그래픽, 컴퓨트, 메모리 전송 작업에 대해 별도의 큐 패밀리가 있을 수 있습니다. 큐 패밀리의 가용성은 물리적 장치 선택의 구별 요소로 사용될 수도 있습니다. Vulkan을 지원하는 장치가 그래픽 기능을 제공하지 않을 수도 있지만, 오늘날 Vulkan을 지원하는 모든 그래픽 카드는 일반적으로 우리가 관심을 가지고 있는 모든 큐 작업을 지원합니다.

Step 3 - 창 표시와 스왑체인

오프스크린 렌더링에만 관심이 있는 것이 아닌 이상, 당신은 렌더링된 이미지를 표시할 창을 만들어야 합니다. Windows는 기본 플랫폼 API나 GLFW, SDL 같은 라이브러리로 만들 수 있습니다. 우리는 이 튜토리얼에서 GLFW를 사용할 것이고 다음 챕터에서 자세히 다루겠습니다.

실제로 창에 렌더링하려면 창 표면 (VkSurfaceKHR)과 스왑체인(VkSwapchainKHR)이 필요합니다. KHR 접미사는 이러한 객체가 Vulkan 확장의 일부임을 나타내는 것입니다. Vulkan API 자체는 완전히 플랫폼에 구애받지 않으므로 창 관리자와 상호작용하기 위해 표준화된 WSI(Window System Interface) 확장을 사용해야 합니다. surface는 렌더링할 창에 대한 플랫폼 간 추상화이며, 일반적으로 기본 창 핸들에 대한 참조를 제공하여 인스턴스화 됩니다. 예를 들면 Windows의 HWND 같은 것입니다. 운 좋게도 GLFW 라이브러리는 이에 대한 플랫폼 별 세부 정보를 처리하는 기능이 내장되어 있습니다.

스왑체인은 렌더타겟의 모음입니다. 기본 목적은 현재 렌더링 중인 이미지가 현재 화면에 있는 이미지와 다른지 확인하는 것입니다. 이는 완전한 이미지만 표시되도록 하는데 중요합니다. 프레임을 그릴 때마다 스왑체인에 렌더링할 이미지를 제공하도록 요청해야 합니다. 프레임 그리기를 마치면 이미지가 스왑체인에서 반환되어 어느 시점엔가 화면에 표시됩니다. 렌더링 대상의 수와 완성된 이미지를 화면에 표시하기 위한 조건은 현재 모드에 따라 다릅니다. 일반적으로 현재 모드는 이중 버퍼링(vsync)나 삼중 버퍼링입니다. 스왑체인 생성 챕터에서 이에 대해 알아보겠습니다.

일부 플랫폼에서는 VK_KHR_displayVK_KHR_display_swapchain 확장을 통해 창 관리자와 상호작용하지 않고 디스플레이에 직접 렌더링할 수 있습니다. 이를 통해 전체화면을 나타내는 surface를 만들 수 있으며, 예를 들면 고유한 창 관리자를 구현할 수도 있습니다.

Step 4 - 이미지 뷰와 프레임 버퍼

스왑체인에서 가져온 이미지를 그리려면, VkImageViewVkFramebuffer로 래핑해야 합니다. 이미지 뷰는 사용할 이미지의 특정 부분을 참조하고, 프레임 버퍼는 색상, 깊이 및 스텐실 대상에 사용할 이미지 뷰를 참조합니다. 스왑체인에는 다양한 이미지가 존재 가능하므로, 각각에 대한 이미지 뷰와 프레임 버퍼를 미리 생성하고 그릴 때 올바른 이미지를 선택합니다.

Step 5 - 렌더패스

Vulkan의 렌더패스는 렌더링 작업 중에 사용되는 이미지 유형, 이미지 사용 방법 및 컨텐츠 처리 방법을 설명합니다. 초기 삼각형 렌더링 어플리케이션에서 Vulkan에 단일 이미지를 색상 대상으로 사용할 것이며, 그리기 작업 직전에 단색으로 지워지기를 원한다고 알릴 것입니다. 렌더패스는 이미지 유형만 설명하지만 VkFramebuffer는 실제로 특정 이미지를 이러한 슬롯에 바인딩합니다.