본문 바로가기
Vulkan

2. Surface And SwapChain

by SimonLee 2024. 6. 11.

서피스 란 ?

Vulkan은 플랫폼에 구애 받지 않는 API이므로 자체적으로 윈도우 시스템과 직접 인터페이스 할 수 없습니다.

Vulkan과 윈도우 시스템 간의 연결을 설정하여 결과를 화면에 표시하려면

WSI (Window System Integration) 확장을 사용해야 합니다.

이전 챕터 초기화 과정에서 VK_KHR_Surface, VK_KHR_android_surface  라이브러리를 인스턴스 기반 확장을 했습니다.

아래에서 Surface를 생성해봅니다.

 

스왑 체인 이란 ?

Vulkan에는 "기본 프레임 버퍼"라는 개념이 없으므로

화면에 시각화하기 전에 렌더링할 버퍼를 소유할 인프라가 필요.

이 인프라는 스왑 체인 으로 알려져 있으며 Vulkan에서 명시적으로 생성되어야 합니다.

스왑 체인은 렌더링 결과를 플랫폼이 지정한 프레젠테이션 윈도우/화면에 표시하는 메커니즘 이다.

 

스왑체인에 단일 다중 드로잉 이미지가 포함이 되며, 이를 색상 이미지라고 한다.

드로잉 이미지의 교체 또는 플립핑을 통해 화면에 표시가 되며,

이는 프레젠테이션 모드에 따라 달라진다.

 

수직 동기화 주기에 맞춰 업체이트 되는 방식 또는 드로잉 화면이 사용가능한 즉시 업데이트 되는 방식이 있다.

스왑체인 API를 사용하기 위해서 논리장치 생성시  "VK_KHR_swapchain" 이름으로 확장을 했었다.

Swap Chain은 백버퍼를 스왑하는 렌더링의 과정이라고 생각하자.

스왑 체인의 형태

 

스왑체인으로 부터 할당된 이미지 A, 이미지 B는 V-sync 타이밍 마다 스왑이 되면서 이미지의 내용이 출력이 됩니다. 


1) 서피스를 생성해보자

렌더링을 하기 위해서는 서피스를 생성해야 한다.

플랫폼에서 제공하는 WSI을 통해서만 서피스를 생성을 할 수 있다.

우리는 Android 플랫폼을 사용하기 때문에

WSI 에서 제공하는 전역 벌칸 인스턴스에서 확장판 ppEnabledExtensionNames 배열에

" VK_KHR_Surface, VK_KHR_android_surface " 를 넣어 줬다.

 

 VkAndroidSurfaceCreateInfoKHR 함수를 사용한다.

API 끝 부분에 KHR 이라고 쓰여져 있는 API들은 Vulkan에서 제공하지 않고, 확장판 API를 사용해야 한다는 의미이다.

 

> 앱으로 부터 ANativeWindow * 전달 받음.

서피스를 생성하기 위해 NativeWindow를 전달 받아야 한다.

Android 에서는 앱 실행시 전달받는 객체 android_app의 내부 변수 window로 세팅한다.

android_app *pApp
auto renderer = new VkRenderer(pApp->window);
VkRenderer(ANativeWindow* window);

 

> Surface를 생성한다.

2) 에서 생성한 VkAndroidSurfaceCreateInfoKHR 구조체를 인자로 사용해서 생성.

To create a VkSurfaceKHR object for an Android native window, call:

// Provided by VK_KHR_android_surface
VkResult vkCreateAndroidSurfaceKHR(
    VkInstance                                  instance,
    const VkAndroidSurfaceCreateInfoKHR*        pCreateInfo,
    const VkAllocationCallbacks*                pAllocator,
    VkSurfaceKHR*                               pSurface);

- instance is the instance to associate the surface with.

- pCreateInfo is a pointer to a VkAndroidSurfaceCreateInfoKHR structure containing parameters affecting the creation of the surface object.

- pAllocator is the allocator used for host memory allocated for the surface object when there is no more specific allocator available (see Memory Allocation).

- pSurface is a pointer to a VkSurfaceKHR handle in which the created surface object is returned.

 

> VkAndroidSurfaceCreateInfoKHR 메타 데이터

// Provided by VK_KHR_android_surface
typedef struct VkAndroidSurfaceCreateInfoKHR {
    VkStructureType                   sType;
    const void*                       pNext;
    VkAndroidSurfaceCreateFlagsKHR    flags;
    struct ANativeWindow*             window;
} VkAndroidSurfaceCreateInfoKHR;

- sType VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR

- pNext is NULL or a pointer to a structure extending this structure.

- flags is reserved for future use.

- window is a pointer to the ANativeWindow to associate the surface with.

 

> 정상적으로 서피스가 생성이 되었는지 확인한다.

 

모든 서피스가 전부 프레젠테이션을 지원하는 것은 아니다.

프레젠 테이션을 지원하지 않으면 화면에 출력이 되지 않는다.

위 함수는 물리 장치의 큐 패밀리가 서피스 프레젠테이션이 지원되는지 확인한다.

pSupported = Vktrue, 프레젠테이션 지원

// Provided by VK_KHR_surface
VkResult vkGetPhysicalDeviceSurfaceSupportKHR(
    VkPhysicalDevice                            physicalDevice,
    uint32_t                                    queueFamilyIndex,
    VkSurfaceKHR                                surface,
    VkBool32*                                   pSupported);

- physicalDevice is the physical device.

- queueFamilyIndex is the queue family.

- surface is the surface.

- pSupported is a pointer to a VkBool32, which is set to VK_TRUE to indicate support, and VK_FALSE otherwise.

 

Usage >>

VkAndroidSurfaceCreateInfoKHR surfaceCreateInfo {
        .sType = VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR,
        .window = mWindow
};
VkBool32 support;
vkCreateAndroidSurfaceKHR(mInstance, &surfaceCreateInfo, nullptr, &mSurface);
vkGetPhysicalDeviceSurfaceSupportKHR(mPhysicalDevice,
                                     mQueueFamilyIndices.graphicsFamily,
                                     mSurface,
                                     &support);
if (!support) {
    aout << "surface is not supported to presentation" << endl;
}

 

2) SwapChain 을 생성해보자.

swapchain API를 사용하기 위해서는 VK_KHR_swapchain 장치확장이 필요합니다.

인스턴스 기반이 아닌 장치 기반 확장을 해야 한다.

장치 기반 확장판을 먼저 나열하여 지원하는 스왑체인 라이브러리가 있는지 확인하자

안드로이드에서는 libvulkan.so, ndk 에서 " VK_KHR_swapchain " 확장판을 사용할수 있게 구현이 되어있다.

 

// Provided by VK_VERSION_1_0
VkResult vkEnumerateDeviceExtensionProperties(
    VkPhysicalDevice                            physicalDevice,
    const char*                                 pLayerName,
    uint32_t*                                   pPropertyCount,
    VkExtensionProperties*                      pProperties);

- physicalDevice : 물리 장치

- pLayerName : nullptr 로 설정

- pPropertyCount : 확장 특성 개수

- pProperties : 지원하는 확장판 포인터

 

pProperties.extensionName을 출력해보면 아마 " VK_KHR_swapchain " 이 있을것이다.

논리 장치 생성시, 논리 장치 메타데이터 deviceCreateInfo.ppEnabledExtensionNames 포인터에

" VK_KHR_swapchain " 를 넣어 주어야 한다.

 

장치 기반 확장판 검색하는 코드 >

bool VkRenderer::CheckDeviceExtension(VkPhysicalDevice device, std::vector<std::string> &request) {
    uint32_t deviceExtensionCount;
    vkEnumerateDeviceExtensionProperties(mPhysicalDevice, nullptr, &deviceExtensionCount, nullptr);
    std::vector<VkExtensionProperties> extensions(deviceExtensionCount);
    vkEnumerateDeviceExtensionProperties(mPhysicalDevice, nullptr, &deviceExtensionCount,
    extensions.data());

    mDeviceExtensionNames.clear();
    for (const auto &properties: extensions) {
        auto it = find(request.begin(), request.end(), properties.extensionName);
        if (it != request.end()) {
            mDeviceExtensionNames.push_back(properties.extensionName);
        }
    }
    if (mDeviceExtensionNames.empty()) return false;
    return true;
}

 

논리 장치 생성시 확장판 이름이 들어간 배열의 포인터를 넣어주는 코드 >

vector<string> extensionNames {
    "VK_KHR_swapchain"
};
CheckDeviceExtension(mPhysicalDevice, extensionNames);
deviceCreateInfo.enabledExtensionCount = static_cast<uint32_t>(mDeviceExtensionNames.size());
deviceCreateInfo.ppEnabledExtensionNames = mDeviceExtensionNames.data();

 

--------------------------------------------------------------------------------------------------------------------

이제 스왑체인 객체 생성 및 사용 가능하다

 

스왑체인 객체를 생성하기 위하여

아래 6가지의 확장판 함수를 통해 쿼리 및 설정하는 작업이 필요하다.

1. Surface Capabilities (화면 기능) 쿼리 및 설정

2. 프리젠테이션 모드 쿼리 및 설정.

3. Surface Format (pixel format, color space)  쿼리 및 설정.

4. Swap Extent (스왑 범위)

5. Swap Chain Info 설정

2 - 1) Surface Capabilities ( 화면 기능 )

물리적 장치에서 지원되는 이미지 Surface Capabilities을 지정해야한다.

최소/최대 이미지의 개수 , 최소/최대/현재 이미지의 크기, 화면 변형 기능 들을 포함한다.

vkGetPhysicalDeviceSurfaceCapabilitiesKHR 함수에서 VkSurfaceCapabilitiesKHR 구조체를 얻어올 수 있습니다.

// Provided by VK_KHR_surface
VkResult vkGetPhysicalDeviceSurfaceCapabilitiesKHR(
    VkPhysicalDevice                            physicalDevice,
    VkSurfaceKHR                                surface,
    VkSurfaceCapabilitiesKHR*                   pSurfaceCapabilities);
- physicalDevice : 물리장치
- surface : 서피스
- pSurfaceCapabilities : 전달 받을 Surface Capabilities 포인터

 

 

전달받은 서피스 Capabilities

이 정보는 서피스 스왑체인 생성 필요한 정보를 포함한다.

// Provided by VK_KHR_surface
typedef struct VkSurfaceCapabilitiesKHR {
    uint32_t                         minImageCount;
    uint32_t                         maxImageCount;
    VkExtent2D                       currentExtent;
    VkExtent2D                       minImageExtent;
    VkExtent2D                       maxImageExtent;
    uint32_t                         maxImageArrayLayers;
    VkSurfaceTransformFlagsKHR       supportedTransforms;
    VkSurfaceTransformFlagBitsKHR    currentTransform;
    VkCompositeAlphaFlagsKHR         supportedCompositeAlpha;
    VkImageUsageFlags                supportedUsageFlags;
} VkSurfaceCapabilitiesKHR;

- minImageCount : 이미지의 최소 개수

  버퍼링을 의미하여 2인 경우 더블 버퍼링, 3인 경우 트리플 버퍼링, 

  minImageCount는 최소 이미지 개수 이기 때문에 더 많은 이미지를 가질 수도 있음.

- maxImageCount : 이미지의 최대 개수

- currentExtent : 현재 이미지의 크기 범위

- minImageExtent : 최소 이미지 크기 범위

- maxImageExtent : 최대 이미지 크기 범위

- maxImageArrayLayers : 가능한 최대 이미지 배열 수

- supportedTransforms : 화면에서 지원되는 변형 기능 (회전)

- supportedCompositeAlpha : 화면에서 지원되는 알파

- supportedUsageFlags : 화면에서 지원되는 용도

 

s21 지원되는 서피스 기능

supportedCompositeAlpha 값도

현 기기에서는 " VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR " 을 지원을 안함.

VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR 만 지원.

윈도우 매니저에서 알파 컨트롤 하라는듯..

 

Vulkan API에서는 프레젠테이션 엔진이 이미지의 알파 구성 요소를 처리하는 방식을 알 수 없습니다.

대신 애플리케이션은 네이티브 윈도우 시스템 명령을 사용하여 합성 알파 블렌딩 모드를 설정해야 합니다.

애플리케이션이 네이티브 윈도우 시스템 명령을 사용하여 블렌딩 모드를 설정하지 않으면 플랫폼별 기본값이 사용됩니다.

// Provided by VK_KHR_surface
typedef enum VkCompositeAlphaFlagBitsKHR {
    VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR = 0x00000001,
    VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR = 0x00000002,
    VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR = 0x00000004,
    VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR = 0x00000008,
} VkCompositeAlphaFlagBitsKHR;

 

Usage >>

// capabilities 가져오는 부분
VkSurfaceCapabilitiesKHR capabilities;
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, surface, &capabilities);

// 사용하는 부분
VkSwapchainCreateInfoKHR swapChainCreateInfo = {};
swapChainCreateInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
swapChainCreateInfo.imageExtent = capabilities.currentExtent;
swapChainCreateInfo.minImageCount = capabilities.minImageCount + 1;
...
vkCreateSwapchainKHR(mDevice, &swapChainCreateInfo, nullptr, &mSwapChain);

 

2 - 2) 프리젠테이션 모드

 

물리장치에서 지원되는 프리젠테이션 모드를 검색할 수 있다.

vkGetPhysicalDeviceSurfacePresentModesKHR() 확장판 함수 사용.

 

검색한 결과에서 VkPresentModeKHR 을

가능하면 "VK_PRESENT_MODE_MAILBOX_KHR" 을 선택하고,

없는 경우 "VK_PRESENT_MODE_FIFO_KHR"을 선택 하도록 한다.

 

s21 지원되는 프레젠테이션 모드

 

프레젠테이션 모드 검색

// Provided by VK_KHR_surface
VkResult vkGetPhysicalDeviceSurfacePresentModesKHR(
    VkPhysicalDevice                            physicalDevice,
    VkSurfaceKHR                                surface,
    uint32_t*                                   pPresentModeCount,
    VkPresentModeKHR*                           pPresentModes);

- physicalDevice : 물리 장치

- surface : 스왑체인 할 서피스

- pPresentModeCount : pPresentMode 개수

- pPresentModes : 지원되는 프레젠트 모드 포인터

 

 

프레젠테이션 모드 정보 관리 열거형

VK_PRESENT_MODE_IMMEDIATE_KHR = 0 이 모드는 수직동기화를 기다리지 않고 프리젠테이션 요청을 즉시 렌더링
프리젠테이션 요청의 대한 내부 큐 관리 필요하지 않다.
이 모드에서는 화면 깨짐 현상 ( 티어링 ) 이 발생할 수 있다.
VK_PRESENT_MODE_MAILOBOX_KHR = 1 대기열이 꽉 찼을 때 애플리케이션을 차단하는 대신
이미 대기열에 있는 이미지를 최신 이미지로 교체하기만 하면 됩니다.
이 모드를 사용하면 찢어짐을 방지하면서 프레임을 최대한 빠르게 렌더링할 수 있으므로 표준 수직 동기화보다 대기 시간 문제가 줄어듭니다.
이는 일반적으로 "삼중 버퍼링"으로 알려져 있지만 세 개의 버퍼가 있다고 해서 반드시 프레임 속도가 잠금 해제되는 것은 아닙니다.
VK_PRESENT_MODE_FIFO_KHR = 2 스왑 체인은 디스플레이가 새로 고쳐질 때,
디스플레이가 대기열 앞쪽에서 이미지를 가져오고
프로그램이 대기열 뒤쪽에 렌더링된 이미지를 삽입하는 대기열입니다.
대기열이 가득 차면 프로그램은 기다려야 합니다. 이는 현대 게임에서 볼 수 있는 수직 동기화와 가장 유사합니다. 디스플레이가 새로 고쳐지는 순간을 " vertical blank"이라고 합니다.
VK_PRESENT_MODE_FIFO_RELAXED_KHR = 3 FIFO 모드와 동일하며, 차이점은
vertical blank를 기다리는 대신 이미지가 도착하면 바로 디스플레이에 전송됨.
화면깨짐현상 발생(tearing)

Usage >> 

// 가져오는 부분
std::vector<VkPresentModeKHR> presentationModes;
uint32_t presentationCount = 0;
vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentationCount, nullptr);
if (presentationCount != 0)
{
    swapChainDetails.presentationModes.resize(presentationCount);
    vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentationCount, 
			presentationModes.data());
}

// 사용하는 부분
// VK_PRESENT_MODE_MAILBOX_KHR 로 사용.
VkSwapchainCreateInfoKHR swapChainCreateInfo = {};
swapChainCreateInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
swapChainCreateInfo.presentMode = presentationModes[0];
...
vkCreateSwapchainKHR(mDevice, &swapChainCreateInfo, nullptr, &mSwapChain);

 

 

2 - 3) Surface Format  설정

 

물리장치에서 지원되는 픽셀 포맷과 컬러 스페이스를 얻는다.

 

Vk_format은 색상의 채널과 유형을 함께 지정합니다.

예를 들면 VK_FORMAT_B8G8R8A8_SRGB는

BGRA 각 채널을 8비트 부호없는 정수와 함께 저장하여 픽셀당 총 32비트를 저장한다는 의미 입니다.

색 공간(VkColorSpaceKHR)은 이미지 데이터를 해석하는 방법의 대해서 정의해 놓은 자료구조이다.

컬러 스페이스는 색 재현율 이라고도 한다.

 

색 공간이 가능한 경우 SRGB를 사용하면 더 정확한 인식색상을 얻을 수 있기 때문에 다음 조합을 추천한다.

format : VK_FORMAT_R8G8B8A8_SRGB

colorSpace : VK_COLOR_SPACE_SRGB_NONLINEAR_KHR

 

필자는 아래 안드로이드 에러가 발생해서 

// ERROR: Format allocation info not found for format: 38

surfaceFormat을 쿼리하지 않고 바로 넣어 주었음 - -;;;

 

S21 지원되는 format

 

// Provided by VK_KHR_surface
VkResult vkGetPhysicalDeviceSurfaceFormatsKHR(
    VkPhysicalDevice                            physicalDevice,
    VkSurfaceKHR                                surface,
    uint32_t*                                   pSurfaceFormatCount,
    VkSurfaceFormatKHR*                         pSurfaceFormats);

- physicalDevice : 물리장치

- surface : 서피스

- pSurfaceFormatCount : pSurfaceFormats 개수, pSurfaceFormats가 널이면 개수를 얻는 용도로도 사용.

- pSurfaceFormats : VkSurfaceFormatKHR 배열 포인터

 

VkSurfaceFormatKHR 

// Provided by VK_KHR_surface
typedef struct VkSurfaceFormatKHR {
    VkFormat           format;
    VkColorSpaceKHR    colorSpace;
} VkSurfaceFormatKHR;

- format : 색상의 채널과 유형

- colorSpace : 색 공간

 

Usage >>

// 설정하는 부분
uint32_t formatCount = 0;
vector<VkSurfaceFormatKHR> formats;
vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, nullptr);
if (formatCount != 0)
{
    formats.resize(formatCount);
    vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, formats.data());
}

// 스왑 체인 메타데이터의 imageFormat과 imageColorSpace 에 넣어주자.
// format : VK_FORMAT_R8G8B8A8_UNORM
// color space : VK_COLOR_SPACE_SRGB_NONLINEAR_KHR
VkSwapchainCreateInfoKHR swapChainCreateInfo = {};
swapChainCreateInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
swapChainCreateInfo.imageFormat = formats[0].format;				
swapChainCreateInfo.imageColorSpace = formats[0].colorSpace;
...
vkCreateSwapchainKHR(mDevice, &swapChainCreateInfo, nullptr, &mSwapChain);

 

2 - 4) Swap Extent

 

물리장치에서 지원되는 Swap Extent(범위)는 해상도 입니다.

픽셀 단위로 그리는 창의 해상도와 정확하게 동일합니다.

Surface Capabilities 쿼리를 전송하면,

VkSurfaceCapabilitiesKHR 내부 변수 중 currentExtent 값이 채워지게 되는데 해당 값을 사용합니다.

해당 값이 min보다 작으면 min 값, max보다 크면 max값 그 외면 해상도로 설정해 줍니다.

typedef struct VkExtent2D {
    uint32_t    width;
    uint32_t    height;
} VkExtent2D;
VkExtent2D extent = swapChainDetails.surfaceCapabilities.currentExtent;

 

2 - 5) Swap Chain  생성

 

위에서 얻은 정보로 스왑체인 메타 데이터 생성후 스왑 체인을 생성합니다.

 

Swap chain 생성

// Provided by VK_KHR_swapchain
VkResult vkCreateSwapchainKHR(
    VkDevice                                    device,
    const VkSwapchainCreateInfoKHR*             pCreateInfo,
    const VkAllocationCallbacks*                pAllocator,
    VkSwapchainKHR*                             pSwapchain);

- device : 논리장치

- pCreateInfo : 스왑 체인 메타데이터

- pAllocator : 호스트 메모리 할당 제어

- pSwapChain : 생성된 스왑체인 포인터

 

 

스왑 체인 메타데이터 생성

// Provided by VK_KHR_swapchain
typedef struct VkSwapchainCreateInfoKHR {
    VkStructureType                  sType;
    const void*                      pNext;
    VkSwapchainCreateFlagsKHR        flags;
    VkSurfaceKHR                     surface;
    uint32_t                         minImageCount;
    VkFormat                         imageFormat;
    VkColorSpaceKHR                  imageColorSpace;
    VkExtent2D                       imageExtent;
    uint32_t                         imageArrayLayers;
    VkImageUsageFlags                imageUsage;
    VkSharingMode                    imageSharingMode;
    uint32_t                         queueFamilyIndexCount;
    const uint32_t*                  pQueueFamilyIndices;
    VkSurfaceTransformFlagBitsKHR    preTransform;
    VkCompositeAlphaFlagBitsKHR      compositeAlpha;
    VkPresentModeKHR                 presentMode;
    VkBool32                         clipped;
    VkSwapchainKHR                   oldSwapchain;
} VkSwapchainCreateInfoKHR;

- sType : VK_STRUCT_TYPE_SWAPCHAIN_CREATE_INFO_KHR

- surface : 벌칸 서피스 설정
-  minImageCount : 스왑 체인에 포함할 이미지 개수, 작동하는데 필요한 최소 개수
렌더링할 다른 이미지를 얻기 전에 드라이버가 내부 작업 완료할때까지 기다려야 하기 때문에
최소 이미지 보다 하나 많은 이미지를 요청한다.
-  imageFormat : Surface pixel format
-  imageColorSpace : color space
-  imageExtent : 해상도 width, height
-  imageArrayLayers : 각 이미지가 구성되는 레이어의 양을 의미, 3D가 아닌이상 값은 1.
-  imageUsage : 스왑체인의 이미지를 어떤 작업에 사용할지 지정. (사용 목적)     
-  imageSharingMode : 여러 큐에서 접근되는 이미지를 처리하는 방법에는 2가지 있음.
    VK_SHARING_MODE_EXCLUSIVE : 이미지는 한번에 하나의 큐에서 소유하고, 다른 큐에서 사용하기전에 소유권을 명시적으로 이전함. 속도 빠름.
   VK_SHARING_MODE_CONCURRENT : 명시적 소유권 없이 여러 큐에서 이미지 사용 가능.
-  queueFamilyIndexCount :큐 패밀리 인덱스 개수  (0 이상이면 Concurrent 모드에서 사용)
-  pQueueFamilyIndices : 큐 패밀리 인덱스 ( Concurrent 모드에서 사용 )
-  preTransform : 스왑 체인의 이미지에 특정 변환을 적용할수 있음. 변환 원하지 않으려면 현재 변환 지정하면 됨.
- compositeAlpha : 다른 시스템의 창과 혼합하는 데 알파 채널 사용해야하는지 여부                  VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR : 알파채널 사용 X
- presentMode : 위에서 설정한 프리젠트 모드
- clipped : VK_TRUE인 경우 가려진 픽셀 색상에 신경안 씀.
- oldSwapchain : Vulkan을 사용하면 창 크기가 조정된 이유로 애프리케이션이 실행 되는 동안 스왑체인이 유효하지 않거나 최적화되지 않을 수 있음.
이 경우 스왑 체인이 실제로 처음부터 다시 생성되어야 함.

 

ImageUsageFlagBits

이미지를 어떻게 사용할지 벌칸 드라이버에 알려주어 최적화를 수행하게 합니다.

// Provided by VK_VERSION_1_0
typedef enum VkImageUsageFlagBits {
    VK_IMAGE_USAGE_TRANSFER_SRC_BIT = 0x00000001,
    VK_IMAGE_USAGE_TRANSFER_DST_BIT = 0x00000002,
    VK_IMAGE_USAGE_SAMPLED_BIT = 0x00000004,
    VK_IMAGE_USAGE_STORAGE_BIT = 0x00000008,
    VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT = 0x00000010,
    VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT = 0x00000020,
    VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT = 0x00000040,
    VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT = 0x00000080,
	...
} VkImageUsageFlagBits;

- VK_IMAGE_USAGE_TRANSFER_SRC_BIT : 이미지 복사시, 소스 이미지가 사용될때

- VK_IMAGE_USAGE_TRANSFER_DST_BIT :  이미지 복사시,  타겟 이미지로 사용될때

- VK_IMAGE_USAGE_SAMPLED_BIT : 이미지가 샘플러일때 사용

- VK_IMAGE_USAGE_STORAGE_BIT : 이미지가 스토리지 용일때 사용

- VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT : 이미지가 첨부로 사용될때 ( 프레임버퍼에서 사용 )

- VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT : 스텐실, 뎁스 첨부로 이미지가 사용될때

 


Usage >>

스왑체인을 생성한다.

// Creation information for swap chain
VkSwapchainCreateInfoKHR swapChainCreateInfo = {};
swapChainCreateInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
swapChainCreateInfo.surface = mSurface;
swapChainCreateInfo.imageFormat = surfaceFormat.format;
swapChainCreateInfo.imageColorSpace = surfaceFormat.colorSpace;
swapChainCreateInfo.presentMode = presentMode;
swapChainCreateInfo.imageExtent = extent;
swapChainCreateInfo.minImageCount = imageCount;
swapChainCreateInfo.imageArrayLayers = 1;
swapChainCreateInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
swapChainCreateInfo.preTransform = swapChainDetails.surfaceCapabilities.currentTransform;
swapChainCreateInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
swapChainCreateInfo.clipped = VK_TRUE;
swapChainCreateInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
swapChainCreateInfo.queueFamilyIndexCount = 0;
swapChainCreateInfo.pQueueFamilyIndices = nullptr;

// IF old swap chain been destroyed and this one replaces it, then link old one to quickly hand over responsibilities
swapChainCreateInfo.oldSwapchain = VK_NULL_HANDLE;

// Create Swapchain
VkResult result = vkCreateSwapchainKHR(mDevice, &swapChainCreateInfo, nullptr, &mSwapchain);
if (result != VK_SUCCESS)
{
    throw std::runtime_error("Failed to create a Swapchain!");
}

 

 

6. 스왑 체인 이미지 뷰 생성

스왑체인 생성할때 최소 이미지의 개수를 지정했지만, 실제로 더 많은 이미지가 생성됐을수 있다.

스왑 체인에 몇개의 이미지를 가지고 있는지 물어봐야 한다.

스왑 체인의 이미지는 Window System에게 소유권있기 때문에 파괴하면 안된다.

오직 스왑 체인 이미지만이 스왑하여 화면에 출력이 가능하다.

 

minInageCount를 4로 설정했는데, s21 기준으로 생성된 스왑체인 이미지 개수는 6개

 

스왑체인 이미지 얻기

// Provided by VK_KHR_swapchain
VkResult vkGetSwapchainImagesKHR(
    VkDevice                                    device,
    VkSwapchainKHR                              swapchain,
    uint32_t*                                   pSwapchainImageCount,
    VkImage*                                    pSwapchainImages);

- device : 논리장치

- swapchain : 이미지의 개수를 얻을 스왑 체인

- pSwapchainImageCount : 이미지 개수 얻어올때 사용 하거나, 얻으려는 vkImage 개수를 정의한 변수의 포인터

- pSwapchainImages : vkImage 포인터. 개수를 얻어올때는 nullptr로 설정.


Usage >>

// Get swap chain images (first count, then values)
uint32_t swapChainImageCount;
vkGetSwapchainImagesKHR(mDevice, swapchain, &swapChainImageCount, nullptr);
std::vector<VkImage> images(swapChainImageCount);
vkGetSwapchainImagesKHR(mDevice, swapchain, &swapChainImageCount, images.data());

 

 

스왑체인 이미지뷰 생성하기

 

스왑체인 이미지에 직접 접근이 불가능하다.

대신 이미지뷰를 사용하기 때문에 이미지 마다 이미지 뷰를 생성해야 한다.

생성된 이미지뷰는 자료구조를 통해서 저장해 놓아야 하고, 추후 프런트, 백버퍼 이미지 참조하는데 사용된다.

 

이미지 뷰 생성

// Provided by VK_VERSION_1_0
VkResult vkCreateImageView(
    VkDevice                                    device,
    const VkImageViewCreateInfo*                pCreateInfo,
    const VkAllocationCallbacks*                pAllocator,
    VkImageView*                                pView);

- device : 이미지 뷰 생성할 논리 장치

- pCreateInfo  : 이미지 뷰 생성 메타 데이터 포인터

- pAllocator : 메모리 할당 프로세스 제어

- pView : 생성된 이미지 뷰

 

이미지 뷰 메타 데이터 생성

// Provided by VK_VERSION_1_0
typedef struct VkImageViewCreateInfo {
    VkStructureType            sType;
    const void*                pNext;
    VkImageViewCreateFlags     flags;
    VkImage                    image;
    VkImageViewType            viewType;
    VkFormat                   format;
    VkComponentMapping         components;
    VkImageSubresourceRange    subresourceRange;
} VkImageViewCreateInfo;

- sType : VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO

- pNext : 확장판 지정 구조체

- flags : 추후 사용

- viewType : 이미지 뷰 유형 ( 2D면 VK_IMAGE_VIEW_TYPE_2D )

- format : 스왑 체인생성할때 지정한 포맷

- components : 색상 / 깊이 / 스텐실이 색상 컴포넌트로 변환 된 후 재맵핑에 사용

- subresourceRagne : 밉맵 계층의 범위 선택과 배열 계층의 선택에 사용하며 뷰를 통해서 접근 가능.

 

Usage >>

for (VkImage image : images)
{
    // Store image handle
    SwapchainImage swapChainImage = {};
    swapChainImage.image = image;
    
    VkImageViewCreateInfo viewCreateInfo = {};
    viewCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
    viewCreateInfo.image = image;
    viewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
    viewCreateInfo.format = format;
    viewCreateInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
    viewCreateInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
    viewCreateInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
    viewCreateInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;

    // Subresources allow the view to view only a part of an image
    viewCreateInfo.subresourceRange.aspectMask = aspectFlags;
    viewCreateInfo.subresourceRange.baseMipLevel = 0;
    viewCreateInfo.subresourceRange.levelCount = 1;
    viewCreateInfo.subresourceRange.baseArrayLayer = 0;
    viewCreateInfo.subresourceRange.layerCount = 1;

    // Create image view and return it
    VkImageView imageView;
    VkResult result = vkCreateImageView(device, &viewCreateInfo, nullptr, &imageView);
    if (result != VK_SUCCESS)
    {
        throw std::runtime_error("Failed to create an Image View!");
    }
    
    swapChainImage.imageView = imageView;
    swapChainImages.push_back(swapChainImage);
}

 

 

 

Reference 

https://docs.vulkan.org/spec/latest/chapters/VK_KHR_surface/wsi.html

 

Window System Integration (WSI) :: Vulkan Documentation Project

The implementation will maintain a limited amount of history of timing information about previous presents. Because of the asynchronous nature of the presentation engine, the timing information for a given vkQueuePresentKHR command will become available so

docs.vulkan.org

 

https://carmencincotti.com/2022-12-19/how-to-render-a-webgpu-triangle-series-part-three-video/

 

Canvas Context and Swap Chain - Render a WebGPU Triangle | Video

We'll take an in-depth look how to configure our WebGPU Canvas Context, and also dive into what exactly the Swap Chain is.

carmencincotti.com

 

'Vulkan' 카테고리의 다른 글

5. Presentation And Synchronize ( Fence, Semaphore, Event )  (0) 2024.06.27
4. Pipeline Barrier And Image Layout  (0) 2024.06.24
3. Command buffer  (0) 2024.06.21
1. Vulkan Instance And device  (0) 2024.06.09
0. Vulkan을 시작하면서.....  (0) 2024.06.07