[vk] Drawing triangle - setup - Instance

2021. 8. 5. 02:02그래픽스/vk

1. Creating an instance

- instance 는 소프트웨어 구조체, 응용프로그램의 상태를 다른 앱이나 현재앱의 문맥에서 수행되는 라이브러리부터 논리적으로 분리시킴

- 시스템의 물리 장치는 인스턴스의 구성원으로 표현, 각각 특정 능력을 가지고, 가용한 큐의 선택을 포함

- vulkan은 앱의 하위시스템, vulkan라이브러리에 한번 연결하고 초기화하면 일부 상태를 추적

-> 추적하기 위해서는 객체들을 저장해야함. 이를 인스턴스 장치, Vkinstance 객체로 표현.

 

- 인스턴스를 생성, vulkan 라이브러리를 초기화해야함

- 인스턴스는 응용프로그램과 vk 라이브러리간의 연결

- 이를 작성하려면 응용 프로그램에 대한 세부정보를 드라이버에 지정해야함

 

 

 

2. vkCreateInstance - 인스턴스 생성 함수

VkResult vkCreateInstance(
const VkInstanceCreateInfo* pCreateInfo,
const VkAllocationCallbacks* pAllocator,
VkInstance* pInstance);

- 초기화할 VkInstance 타입과, 전달해야할 두가지 포인터를 볼 수 있다.

- 여기서 pAllocator는 사용자 정의 할당자 콜백에 대한포인터로 튜토리얼에선 nullptr을 사용한다

  (사용자 정의 메모리 할당)

- 생성 정보가 있는 구조체는 VKInstanceCreateInfo 타입으로 필수적인 정보가 담겨있다.

- 그리고 마지막 파라미터는 실제 생성한 인스턴스를 다룰 VkInstance 타입이다.

 

vulkan은 많은정보를 함수대신 구조체를 통해 전달

- 인스턴스를 만드는데 필요한 충분한 정보를 제공하기위해 하나이상의 구조체를 채워야함.

 

VkResult타입의 값을 반환하며 이를 통해 오류코드를 확인할 수 있음.

결과는 VkInstance멤버 구조체 변수에 저장됨.

if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) {
    throw std::runtime_error("failed to create instance!");
}

 

 

 

 

3. VkInstanceCreateInfo

- vkInstanceCreateInfo의 구조체 의 멤버는 다음과 같다.

typedef struct VkInstanceCreateInfo {
VkStructureType sType;
const void* pNext;
VkInstanceCreateFlags flags;
const VkApplicationInfo* pApplicationInfo;
uint32_t enabledLayerCount;
const char* const* ppEnabledLayerNames;
uint32_t enabledExtensionCount;
const char* const* ppEnabledExtensionNames;
} VkInstanceCreateInfo;

 

아래 구조체는 옵션이 이아니라 무조건 vulkan 드라이버에게 알려야할 정보

- global extensions 과 사용할 validation layers를 알려주는 구조체

- global : 전체 프로그램에 적용됨 (not specific decive)

 

- vulkan의 많은 구조체는sType멤버에 타입을 명시적으로 지정해야함.

- pNext 또한 확장을 위해 많은 구조체에 존재하지만 지금은 nullptr로 초기화.

- flag는 향후 사용을 위해 예약되어 있음. 0으로 설정

- pApplicationInfo는 응용프로그램에 대한 추가정보를 전달하는 선택적인 구조체포인터

  (nullptr로 설정 가능)

- enabledLayerCountppEnabledLayerNames : 각각 활성화하고 싶은 인스턴스 레이어의 수와 이름.

  (현재는 count는 0, 이름은 nullptr로 설정하여 유효성 검사 레이어를 안할 수 있음)

- enabledExtensionCount , ppEnabledExtensionNames : 전역 확장을 정함

vulkan은 플랫폼에 무관한 API
-> 윈도우 시스템과 인터페이스 하기 위한 확장이필요.
-> GLFW 에 필요한 확장기능을 가진 구조체를 리턴하는 편리한 내장함수가 있음.
-> glfwGetRequiredInstanceExtension 으로 필요한 구조체 정보를 얻어 설정해주면 됨.

 

Vulkan이 인스턴스를 생성하는데 모든것을 지정햇으면, vkCreateInstance호출을 최종적으로 실행할 수 있음

 

 

 

 

3. 1 VkApplicationInfo

typedef struct VkApplicationInfo{
VkStructureType sType;
const void* pNext;
const char* pApplicationName;
uint32_t applicationVersion;
const char* pEngineName;
uint32_t engineVersion;
uint32_t apiVersion;
} VkApplicationInfo;

인스턴스 를 만들려면 어플리케이션에대한 몇몇 정보를 알려줄 구조체를 작성해야함

- 해당 구조체의 데이터는 선택사항

- 하지만 그래픽 엔진이 특수 행동을 하는지 알려 드라이버에 특정 정보를 최적화하는데 유용한 정보를 제공할 수 있음

(e.g. because it uses a well-known graphics engine with certain special behavior).

이러한 구조체는 VkApplicationInfo라고함 

    VkApplicationInfo appInfo{};
    appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;   // 해당 구조체 타입을 명시
    appInfo.pApplicationName = "Hello Triangle";         // '\0' 으로 끝나는 문자열
    appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0); // 어플리케이션의 버전
    appInfo.pEngineName = "No Engine";                   //응용프로그램이 기반으로하는 엔진, 미들웨어 
    appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0);   // 엔진, 미들웨어의 버전
    appInfo.apiVersion = VK_API_VERSION_1_0;          // vulkan api 버전. 절대적인 최소버전으로 설정

 

 

 

 

3.2 CreateInstance 전체 코드

    void createInstance() {
    	/*
    		ApplicationInfo 구조체 초기화
    	*/
        VkApplicationInfo appInfo{};
        appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
        appInfo.pApplicationName = "Hello Triangle";
        appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
        appInfo.pEngineName = "No Engine";
        appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0);
        appInfo.apiVersion = VK_API_VERSION_1_0;
	/*
    		createInfo 구조체의 초기화
    	*/
        VkInstanceCreateInfo createInfo{};
        createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;  // 타입을 명시
        createInfo.pApplicationInfo = &appInfo;  // 위에서 정의한 appinfo 구조체 포인터로 전달
        /*
			glfw에서 확장자 정보를 가져옴
        */
        uint32_t glfwExtensionCount = 0; 
        const char** glfwExtensions;
        glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);
        createInfo.enabledExtensionCount = glfwExtensionCount;
        createInfo.ppEnabledExtensionNames = glfwExtensions;
        /*
			레이어 설정 -> 지금은 0, name은 기본값이 nullptr인것같다.
        */
        createInfo.enabledLayerCount = 0;
        /*
			모든것이 잘 되면 instance 에 대한 핸들이 VkInstance클래스 멤버에 저장됨.

			거의 모든 Vulkan 함수는 VK_SUCCESS 또는 오류 코드인 VkResult유형의 값을 반환함.

			인스턴스가 성공적으로 작성되었는지 확인하려면 반환 값이 성공인지 확인해야함.
        */
        if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) {
            throw std::runtime_error("failed to create instance!");
        }
    }

 

 

 

 

 

4. Checking for extension support -> ?>?

확장 기능은 플랫폼 독립적인 API인 경우 많이 쓰이는 것.

- 확장 기능을 활성화시키려면 명시적으로 알려줘야함.

 

vkCreateInstance

- 오류 코드중 하나 VK_ERROR_EXTENSION_NOT_PRESENT

- 이 오류코드가 발생하면, 특정 확장 기능을 수정해서 오류를 없애야함.

 

 

vkEnumerateInstanceExtensionProperties

VkResult vkEnumerateInstanceExtensionProperties (
const char* pLayerName,
uint32_t* pPropertyCount,
VkExtensionProperties* pProperties);

- 지원되는 확장 목록을 검색하기 위한 함수

- 확장 기능의 수를 저장하는 변수에 대한 포인터와 

- 확장 기능의 세부사항을 저장하는 VkExtensionProperties의 배열을 인수로 받음

- 특정 유효성 검사 레이어를 통해 확장을 필터링 할 수 있는 선택적인 첫 번째 매개변수가 있음

  (여기선 무시하며 nullptr)

- 확장 기능 정보를 저장하기위해 배열을 할당하려면 먼저 얼마나 많은 배열이 있는지 알아야함.(최대치)

- 마지막 매개변수를 nullptr로 넘김으로써, 확장기능의 최대개수를 요청할 수 있음.

uint32_t extensionCount = 0;
vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, nullptr);

std::vector 를 사용해 VkExtensionProperties를 확장 기능의 개수만큼 배열을 생성.

std::vector<VkExtensionProperties> extensions(extensionCount);

확장 기능 세부정보에 대해 물어볼 수 있음

vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, extensions.data());

VkExtensionProperties 구조체에는 확장 기능의 이름과 버전이 들어있음.

- 간단히 for 루프를 통해 이들을 나열할 수 있음.

std::cout << "available extensions:\n";

for (const auto& extension : extensions) {
    std::cout << '\t' << extension.extensionName << '\n';
}

vulkan 지원에 대한 몇가지 세부 정보를 제공하려는 경우 createInstance 함수(위에서 만든 멤버함수)에 추가할 수 있음.

As a challenge, try to create a function that checks
if all of the extensions returned by glfwGetRequiredInstanceExtensions are included in
the supported extensions list.
(glfw에서 반환된 모든 확장 기능이, 지원되는 확장목록에 포함되어있는 함수인지 확인하는함수를 만들어봐라)

 

 

glfw만 이용한 확장 방법

   std::vector<const char*> getRequiredExtensions() {
        uint32_t glfwExtensionCount = 0;
        const char** glfwExtensions;
        glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);

        std::vector<const char*> extensions(glfwExtensions, glfwExtensions + glfwExtensionCount);

        if (enableValidationLayers) {
            extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
        }

        return extensions;
    }

createInstance 함수에서 createInfo를 설정할때 값을 넘겨주면된다. 

        auto extensions = getRequiredExtensions();
        createInfo.enabledExtensionCount = static_cast<uint32_t>(extensions.size());
        createInfo.ppEnabledExtensionNames = extensions.data()

 

5. Cleaning up

- VkInstance 는 오직 프로그램이 종료되기전에 제거해야함

- vkDestroyInstance 함수를 사용

- 매개변수는 간단, allocation and deallocation functions 은 추가적인 allcator callback 을 파라미터로 가짐

-> nullptr로 무시할 수 있다.

- 모든 Vk 리소스는 모두 인스턴스가 삭제되기전에 정리해야함.

 

 

 

 

- 이제 인스턴스 생성 후 보다 복잡한 단계를 계속하기 전에

-유효성 검사 레이어를 확인하여 디버깅 옵션을 평가해야함

it's time to evaluate our debugging options by checking out validation layers.

 

C++ code

 

 

 

https://vulkan-tutorial.com/en/Drawing_a_triangle/Setup/Instance

https://openmynotepad.tistory.com/86?category=1020021