본문 바로가기

Vulkan

Setup - Logical device and queue

이전 글 : Physical devices and queue families
다음 글 : Window surface



Logical device and queue

논리적 디바이스와 큐


Introduction


사용하려는 물리적 장치를 선택한 후에는 논리적 장치를 설정하여 인터페이스 할 필요가 있습니다. 논리적 디바이스 작성 프로세스는 인스턴스 작성 프로세스와 유사하며 사용 하려는 기능을 명시 해야 합니다. 또한 앞에서 어떤 대기열 패밀리를 사용할 수 있는지 검토  했으므로 그 검토에 의하여 , 지금 생성 할 대기열을 지정 해야 합니다. 다양한 요구 사항이 있는 경우 동일한 물리적 장치에서 여러 논리적 장치를 만들 수도 있습니다.


논리 장치 핸들을 저장할 새 클래스 멤버를 추가하는 것으로 시작 하십시오.


VkDevice device;


그런 다음 initVulkan 에서 호출 된 createLogicalDevice 함수를 추가 하십시오.


void initVulkan() {
   createInstance();
   setupDebugCallback();
   pickPhysicalDevice();
   createLogicalDevice();
}

void createLogicalDevice() {

}



Specifying the queues to be created

작성 될 대기열 기술


논리 장치의 생성에는 구조체에 세부 사항을 다시 지정하는 작업이 포함 됩니다.이 중 첫 번째 항목은 VkDeviceQueueCreateInfo 입니다. 이 구조는 단일 대기열 패밀리에 대해 원하는 대기열의 수를 기술 합니다. 지금은 그래픽 기능이 있는 대기열에만 관심이 있기 때문에 다음과 같이 지정 합니다..


QueueFamilyIndices indices = findQueueFamilies(physicalDevice);

VkDeviceQueueCreateInfo queueCreateInfo = {};
queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
queueCreateInfo.queueFamilyIndex = indices.graphicsFamily;
queueCreateInfo.queueCount = 1;


현재 사용 가능한 드라이버는 각 패밀리 대기열에 대해 적은 수의 대기열을 만들 수 있도록 허용하며 둘 이상의 대기열을 필요로 하지는 않습니다. 이는 여러 스레드에 모든 명령 버퍼를 작성한 다음 하나의 낮은 오버 헤드 호출로 주 스레드에서 모든 명령 버퍼를 동시에 처리 할 수 있기 때문입니다.


Vulkan은 우선 순위를 대기열에 할당하여 0.0 에서 1.0 사이의 부동 소수점 수를 사용하여 명령 버퍼 실행을 예약하는 데 적용을 합니다. 이러한 지정은 대기열 큐가 하나 뿐인 경우에도 필요합니다.


float queuePriority = 1.0f;
queueCreateInfo.pQueuePriorities = &queuePriority;



Specifying used device features

사용하는 장치 기능 지정


다음에 지정할 정보는 사용할 장치의 기능 집합 입니다. 이들은 이전 장에서 vkGetPhysicalDeviceFeatures  (지오메트리 셰이더와 같이) 에 대해 지원을 요청한 기능입니다. 지금 우리는 특별한 것을 필요로 하지 않으므로 간단하게 정의하기 위해 다음과 같이 모든 입력 인수를 VK_FALSE로 남겨 둘 것 입니다. 벌컨 (Vulkan)으로 더 재미있는 일을 시작하게 되면  우리는이 구조를 새로 작성하기 위하여 다시  돌아올 것입니다.


VkPhysicalDeviceFeatures deviceFeatures = {};




Creating the logical device

논리적 장치의 생성


이전의 두 가지 구조를 사용하여 VkDeviceCreateInfo 기본 구조를 채울 수 있습니다.


VkDeviceCreateInfo createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;


먼저 큐 생성 정보 및 장치 기능 구조체에 포인터를 추가합니다.


createInfo.pQueueCreateInfos = &queueCreateInfo;
createInfo.queueCreateInfoCount = 1;

createInfo.pEnabledFeatures = &deviceFeatures;

나머지 정보는 VkInstanceCreateInfo 구조체와 유사하며 확장 및 유효성 검사 계층을 지정 해야 합니다. 차이점은 이번에는 이러한 장치 지정을 여기서 한번만 한다는 것 입니다.


장치 특정 확장의 예는 VK_KHR_swapchain 입니다.이 장치를 사용하면 해당 장치의 렌더링 된 이미지를 Windows 에 표시 할 수 있습니다. 이 기능이 부족한 Vulkan 장치가 시스템에 있을 수 있습니다 (예 : 컴퓨팅 연산 만 지원하는 경우). 우리는 스왑 체인 에 대하여 설명 하는 강좌에서에서 이 내용을 다시 살펴볼 것 입니다.


유효성 검사 레이어 장에서 언급 했듯이 인스턴스에 대해 수행 한 것과 동일한 유효성 검사 레이어를 장치에 사용할 수 있습니다. 지금은 이러한 각각의 기기별 확장 프로그램이 필요하지 않습니다.


createInfo.enabledExtensionCount = 0;

if (enableValidationLayers) {
   createInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size());
   createInfo.ppEnabledLayerNames = validationLayers.data();
} else {
   createInfo.enabledLayerCount = 0;
}


이제는 우리는  vkCreateDevice 함수를 호출하여 논리적 장치를 인스턴스화 할 준비가 되었습니다.


if (vkCreateDevice(physicalDevice, &createInfo, nullptr, &device) != VK_SUCCESS) {
   throw std::runtime_error("failed to create logical device!");
}


매개 변수는 인터페이스 할 실제 장치, 방금 지정한 대기열 및 사용 정보, 선택적 할당 콜백 포인터 및 논리적 장치 핸들을 저장하는 변수에 대한 포인터입니다. 인스턴스 작성 함수와 마찬가지로 이 호출은 오류를 반환 할 수 있습니다 존재하지 않는 확장 기능을 사용하거나 지원되지 않는 기능의 원하는 사용법을 지정할 경우 오류를 반환 할 것 입니다.


vkDestroyDevice 함수를 사용하여 장치를 정리 해야 합니다.

void cleanup() {
   vkDestroyDevice(device, nullptr);
   ...
}


논리적 장치는 인스턴스와 직접 상호 작용하지 않으므로 매개 변수에  인스턴스가 포함되지 않습니다.


Retrieving queue handles

대기열 핸들 얻기


대기열은 논리 장치와 함께 자동으로 생성되지만 아직 인터페이스를 처리 할 핸들이 없습니다. 먼저 그래픽 대기열에 핸들을 저장하는 클래스 멤버를 추가합니다.


VkQueue graphicsQueue;


장치 대기열은 장치가 파괴 될 때 암시적으로 정리 되므로 cleanup 정리시 아무 것도 할 필요가 없습니다.


vkGetDeviceQueue 함수를 사용하여 각 대기열 패밀리의 대기열 핸들을 검색 할 수 있습니다. 매개 변수는 논리 장치, 대기열 패밀리, 대기열 인덱스 및 대기열 핸들을 저장할 변수에 대한 포인터입니다.이 패밀리에서 하나의 대기열 만 작성하기 때문에 인덱스로  0을 사용 합니다.


vkGetDeviceQueue(device, indices.graphicsFamily, 0, &graphicsQueue);


논리 장치와 큐 핸들을 사용하면 실제로 그래픽 카드를 사용하여 작업을 시작할 수 있습니다! 다음 장에서는 윈도우 시스템에 결과를 보여주기 위한 리소스를 설정할 것 입니다.


C++ 코드 입니다.

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

#include <iostream>
#include <stdexcept>
#include <vector>
#include <cstring>

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

const std::vector<const char*> validationLayers = {
   "VK_LAYER_LUNARG_standard_validation"
};

#ifdef NDEBUG
const bool enableValidationLayers = false;
#else
const bool enableValidationLayers = true;
#endif

VkResult CreateDebugReportCallbackEXT(VkInstance instance, const VkDebugReportCallbackCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDebugReportCallbackEXT* pCallback) {
   auto func = (PFN_vkCreateDebugReportCallbackEXT) vkGetInstanceProcAddr(instance, "vkCreateDebugReportCallbackEXT");
   if (func != nullptr) {
       return func(instance, pCreateInfo, pAllocator, pCallback);
   } else {
       return VK_ERROR_EXTENSION_NOT_PRESENT;
   }
}

void DestroyDebugReportCallbackEXT(VkInstance instance, VkDebugReportCallbackEXT callback, const VkAllocationCallbacks* pAllocator) {
   auto func = (PFN_vkDestroyDebugReportCallbackEXT) vkGetInstanceProcAddr(instance, "vkDestroyDebugReportCallbackEXT");
   if (func != nullptr) {
       func(instance, callback, pAllocator);
   }
}

struct QueueFamilyIndices {
   int graphicsFamily = -1;

   bool isComplete() {
       return graphicsFamily >= 0;
   }
};

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

private:
   GLFWwindow* window;

   VkInstance instance;
   VkDebugReportCallbackEXT callback;

   VkPhysicalDevice physicalDevice = VK_NULL_HANDLE;
   VkDevice device;

   VkQueue graphicsQueue;

   void initWindow() {
       glfwInit();

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

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

   void initVulkan() {
       createInstance();
       setupDebugCallback();
       pickPhysicalDevice();
       createLogicalDevice();
   }

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

   void cleanup() {
       vkDestroyDevice(device, nullptr);
       DestroyDebugReportCallbackEXT(instance, callback, nullptr);
       vkDestroyInstance(instance, nullptr);

       glfwDestroyWindow(window);

       glfwTerminate();
   }

   void createInstance() {
       if (enableValidationLayers && !checkValidationLayerSupport()) {
           throw std::runtime_error("validation layers requested, but not available!");
       }

       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;

       VkInstanceCreateInfo createInfo = {};
       createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
       createInfo.pApplicationInfo = &appInfo;

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

       if (enableValidationLayers) {
           createInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size());
           createInfo.ppEnabledLayerNames = validationLayers.data();
       } else {
           createInfo.enabledLayerCount = 0;
       }

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

   void setupDebugCallback() {
       if (!enableValidationLayers) return;

       VkDebugReportCallbackCreateInfoEXT createInfo = {};
       createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT;
       createInfo.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT;
       createInfo.pfnCallback = debugCallback;

       if (CreateDebugReportCallbackEXT(instance, &createInfo, nullptr, &callback) != VK_SUCCESS) {
           throw std::runtime_error("failed to set up debug callback!");
       }
   }

   void pickPhysicalDevice() {
       uint32_t deviceCount = 0;
       vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr);

       if (deviceCount == 0) {
           throw std::runtime_error("failed to find GPUs with Vulkan support!");
       }

       std::vector<VkPhysicalDevice> devices(deviceCount);
       vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data());

       for (const auto& device : devices) {
           if (isDeviceSuitable(device)) {
               physicalDevice = device;
               break;
           }
       }

       if (physicalDevice == VK_NULL_HANDLE) {
           throw std::runtime_error("failed to find a suitable GPU!");
       }
   }

   void createLogicalDevice() {
       QueueFamilyIndices indices = findQueueFamilies(physicalDevice);

       VkDeviceQueueCreateInfo queueCreateInfo = {};
       queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
       queueCreateInfo.queueFamilyIndex = indices.graphicsFamily;
       queueCreateInfo.queueCount = 1;

       float queuePriority = 1.0f;
       queueCreateInfo.pQueuePriorities = &queuePriority;

       VkPhysicalDeviceFeatures deviceFeatures = {};

       VkDeviceCreateInfo createInfo = {};
       createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;

       createInfo.pQueueCreateInfos = &queueCreateInfo;
       createInfo.queueCreateInfoCount = 1;

       createInfo.pEnabledFeatures = &deviceFeatures;

       createInfo.enabledExtensionCount = 0;

       if (enableValidationLayers) {
           createInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size());
           createInfo.ppEnabledLayerNames = validationLayers.data();
       } else {
           createInfo.enabledLayerCount = 0;
       }

       if (vkCreateDevice(physicalDevice, &createInfo, nullptr, &device) != VK_SUCCESS) {
           throw std::runtime_error("failed to create logical device!");
       }

       vkGetDeviceQueue(device, indices.graphicsFamily, 0, &graphicsQueue);
   }

   bool isDeviceSuitable(VkPhysicalDevice device) {
       QueueFamilyIndices indices = findQueueFamilies(device);

       return indices.isComplete();
   }

   QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device) {
       QueueFamilyIndices indices;

       uint32_t queueFamilyCount = 0;
       vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr);

       std::vector<VkQueueFamilyProperties> queueFamilies(queueFamilyCount);
       vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data());

       int i = 0;
       for (const auto& queueFamily : queueFamilies) {
           if (queueFamily.queueCount > 0 && queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) {
               indices.graphicsFamily = i;
           }

           if (indices.isComplete()) {
               break;
           }

           i++;
       }

       return indices;
   }

   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_REPORT_EXTENSION_NAME);
       }

       return extensions;
   }

   bool checkValidationLayerSupport() {
       uint32_t layerCount;
       vkEnumerateInstanceLayerProperties(&layerCount, nullptr);

       std::vector<VkLayerProperties> availableLayers(layerCount);
       vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data());

       for (const char* layerName : validationLayers) {
           bool layerFound = false;

           for (const auto& layerProperties : availableLayers) {
               if (strcmp(layerName, layerProperties.layerName) == 0) {
                   layerFound = true;
                   break;
               }
           }

           if (!layerFound) {
               return false;
           }
       }

       return true;
   }

   static VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objType, uint64_t obj, size_t location, int32_t code, const char* layerPrefix, const char* msg, void* userData) {
       std::cerr << "validation layer: " << msg << std::endl;

       return VK_FALSE;
   }
};

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;
}



이전 글 : Physical devices and queue families
다음 글 : Window surface



'Vulkan' 카테고리의 다른 글

Presentation - Swap chain  (1) 2018.01.23
Presentation - Window surface  (0) 2018.01.23
Setup - Physical devices and queue families  (0) 2018.01.21
Setup - Validation layers  (0) 2018.01.21
Setup - Creating an instance  (0) 2018.01.20