본문 바로가기

Vulkan

Setup - Base code

이전 글 : Development environment

다음 글 : Drawing Triangle - Creating an instance



Drawing Triangle - Base code


General structure


이전 글에서는 모든 적절한 구성으로 이루어진 Vulkan 프로젝트를 만들고 샘플 코드로 테스트 하였습니다. 이번 글에은 다음 코드로 부터 시작합니다.


#include <vulkan/vulkan.h>

#include <iostream>
#include <stdexcept>
#include <functional>

class HelloTriangleApplication {
public:
   void run() {
       initVulkan();
       mainLoop();
       cleanup();
   }

private:
   void initVulkan() {

   }

   void mainLoop() {

   }

   void cleanup() {

   }
};

int main() {
   HelloTriangleApplication app;

   try {
       app.run();
   } catch (const std::runtime_error& e) {
       std::cerr << e.what() << std::endl;
       return EXIT_FAILURE;
   }

   return EXIT_SUCCESS;
}


먼저 함수, 구조 및 열거 형을 제공하는 LunarG SDK의 Vulkan 헤더파일을 include 합니다.

stdexcept 및 iostream 헤더는 exception 처리와 파일 입력 관련을 위해 사용 됩니다.

function 헤더 파일은 사용되는 각종 자원 관리를 위해 필요합니다.


프로그램 자체는 Vulkan 객체를 private 클래스 멤버로 저장하고 각각을 초기화 하는 함수를 추가하는 클래스로 래핑되며 이 모든 것은 initVulkan 함수에서 호출됩니다.


일단 모든 준비가 완료 되면 메인 루프로 들어가서 프레임 렌더링을 시작 하게 될 것입니다

mainLoop 함수는 창을 닫을 때까지 반복되는 루프를 포함 합니다.

창을 닫고 mainLoop가 종료되면 메모리 정리 기능에서 사용한 자원의 할당을 해제 합니다.


실행 중에 치명적인 오류가 발생하면 main 함수로 전달 되어 명령 프롬프트에 인쇄어 설명 메시지와 함께 std :: runtime_error 예외가 발생합니다.

이러한 오류의 한 예로 Vulkan 을 작동시키는데 필요한 특정 필수 확장 기능이 지원되지 않을 경우에 발생할 수 있습니다.


대략적으로 이 장 다음에 나오는 모든 장에서는 initVulkan 및 하나 이상의 새로운 Vulkan 객체 에서 호출 될 새 함수를 하나씩 추가하며,  메모리를 정리할 때 마지막에 해제 해야하는 private class member 들을 추가할 것 입니다.



Resource management


malloc 함수로 할당 된 메모리의 사용 후에 free 함수의 호출을 요구하는 것처럼, 우리가 생성하는 모든 Vulkan 객체는 더 이상 필요하지 않을 때 명시 적으로 파기 해야 합니다.

최신 C ++ 코드에서는 <memory> 헤더의 유틸리티 함수를 통해 자동 리소스 관리를 수행 할 수 있지만 본 튜토리얼에서는 Vulkan 객체의 할당 및 할당 해제에 대해 명시 적으로 사용 하는 것을 선택 했습니다. 결국 Vulkan의 오류를 피하기 위해 모든 작업을 명시해야 하므로 API가 작동하는 방식을 배우기 위해 객체의 생성과 소멸을 정확하게 명시하는 것이 좋습니다.


이 튜토리얼을 수행 한 후 예를 들어 std :: shared_ptr를 오버로드하여 자동 리소스 관리를 독자적으로 사용하여 구현할 수 있습니다. RAII 의 사용을 큰 Vulkan 프로그램에 권장하는 방법 이지만 학습 목적으로는 무엇이 진행되고 있는지를 아는 것이 좋습니다.


Vulkan 객체는 vkCreateXXX 와 같은 함수로 직접 생성 되거나 vkAllocateXXX 와 같은 함수로 다른 객체를 통해 할당됩니다. 객체가 더 이상 어디에서나 사용되지 않도록 확인한 후에는 vkDestroyXXX 및 vkFreeXXX를 사용하여 객체를 제거 해야 합니다.


이러한 함수의 매개 변수는 일반적으로 객체 유형에 따라 다르지만 모두 pAllocator라는 하나의 매개 변수를 사용하도록 준비 되어 있습니다. 이 매개 변수는 사용자 지정 메모리 할당 자에 대한 콜백을 지정할 수 있는 선택적 매개 변수입니다. 튜토리얼에서는 이 매개 변수를 무시하고 항상 nullptr을 인수로 전달할 것 입니다.


Integrating GLFW


Vulkan은 오프 스크린 렌더링을 사용하고 싶다면 창을 만들지 않고도 완벽하게 작동하지만 실제로는 뭔가를 보여주는 것이 훨씬 재미 있습니다!

먼저 #include <vulkan/vulkan.h> 줄을 다음과 같이 바꿉니다.

#define GLFW_INCLUDE_VULKAN
#include <GLFW/glfw3.h>


그런 식으로 GLFW는 자체 정의를 포함하고 자동으로 Vulkan 헤더를 로드합니다. initWindow 함수를 추가하고 다른 호출 보다 먼저 실행 함수에서 이 함수 호출을 수행 하십시오. 이 함수를 사용하여 GLFW를 초기화 하고 창을 하나 만듭니다.


void run() {
   initWindow();
   initVulkan();
   mainLoop();
   cleanup();
}

private:
   void initWindow() {

   }


initWindow의 첫 번째 호출은 GLFW 라이브러리를 초기화하는 glfwInit () 이어야 합니다. GLFW는 원래 OpenGL 컨텍스트를 만들기 위해 디자인 되었기 때문에 함수 내부적으로 OpenGL 컨텍스트를 만들지 말도록 해야 합니다.


glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);


리사이징 된 윈도우를 처리하는 것은 나중에 처리 할 것이므로 다음과 같은 옵션으로 하여 윈도우 힌트 함수 호출에서 사용하지 않도록 주의 하십시오.

glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);


이제 남은 것은 실제 창을 만드는 것입니다. GLFWwindow * window 을 추가 하십시오; private 클래스 멤버로 설정하고 윈도우에 대한 주소를 저장하고 다음을 사용하여 창을 초기화합니다.


window = glfwCreateWindow(800, 600, "Vulkan", nullptr, nullptr);


처음 세 개의 매개 변수는 창의 너비, 높이 및 타이틀을 지정합니다. 네 번째 매개 변수를 사용하면 선택적으로 창을 여는 모니터를 지정할 수 있으며 마지막 매개 변수는 OpenGL에만  해당 됩니다.

너비와 높이 를 숫자 대신에 상수를 사용하는 것이 좋습니다. 우리는 이 값을 앞으로 두 번 만 사용 할 것이기 때문 입니다. HelloTriangleApplication 클래스 정의 위에 다음 행을 추가 합니다.


const int WIDTH = 800;
const int HEIGHT = 600;


다음과 같이 수정 합니다.

window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr);


이제 다음과 같은 initWindow 함수가 있어야 합니다.


void initWindow() {
   glfwInit();

   glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
   glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);

   window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr);
}


오류가 발생하거나 창을 닫을 때까지 응용 프로그램을 계속 실행 하려면 다음과 같이 mainLoop 함수에 이벤트 루프를 추가 해야 합니다.


void mainLoop() {
   while (!glfwWindowShouldClose(window)) {
       glfwPollEvents();
   }
}


이 코드는 상당히 간단 합니다. 사용자가 창을 닫을 때까지 윈도우 종료 버튼(x 버튼)을 누르는 것과 같은 각종 이벤트를 반복하고 검사합니다. 이것은 또한 나중에 하나의 프레임을 렌더링하는 함수를 호출 할 루프이기도 합니다.


창을 닫으면 리소스를 삭제하고 GLFW 자체를 종료하여 리소스를 정리해야 합니다. 이것이 우리의 첫 번째 정리 코드가 될 것입니다 :


void cleanup() {
   glfwDestroyWindow(window);

   glfwTerminate();
}

프로그램을 실행할 때 창을 닫아 응용 프로그램이 종료 될 때까지 Vulkan이 표시 됩니다. 이제 Vulkan 응용 프로그램의 기본 개념을 살펴 보았습니다. 이제 첫 번째 Vulkan 객체를 만들어 봅시다!


다음은 여기까지 작성된 cpp 코드 입니다.

#define GLFW_INCLUDE_VULKAN
#include <GLFW/glfw3.h>

#include <iostream>
#include <stdexcept>

const int WIDTH = 800;
const int HEIGHT = 600;

class HelloTriangleApplication {
public:
   void run() {
       initWindow();
       initVulkan();
       mainLoop();
       cleanup();
   }

private:
   GLFWwindow* window;

   void initWindow() {
       glfwInit();

       glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
       glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);

       window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr);
   }

   void initVulkan() {

   }

   void mainLoop() {
       while (!glfwWindowShouldClose(window)) {
           glfwPollEvents();
       }
   }

   void cleanup() {
       glfwDestroyWindow(window);

       glfwTerminate();
   }
};

int main() {
   HelloTriangleApplication app;

   try {
       app.run();
   } catch (const std::runtime_error& e) {
       std::cerr << e.what() << std::endl;
       return EXIT_FAILURE;
   }

   return EXIT_SUCCESS;
}



이전 글 : Development environment

다음 글 : Drawing Triangle - Creating an instance



'Vulkan' 카테고리의 다른 글

Setup - Validation layers  (0) 2018.01.21
Setup - Creating an instance  (0) 2018.01.20
Development environment  (0) 2018.01.19
Overview  (0) 2018.01.19
Introduction  (2) 2018.01.19