이전 글 : Physical devices and queue families
다음 글 : Window surface
Logical device and queue
논리적 디바이스와 큐
Introduction
사용하려는 물리적 장치를 선택한 후에는 논리적 장치를 설정하여 인터페이스 할 필요가 있습니다. 논리적 디바이스 작성 프로세스는 인스턴스 작성 프로세스와 유사하며 사용 하려는 기능을 명시 해야 합니다. 또한 앞에서 어떤 대기열 패밀리를 사용할 수 있는지 검토 했으므로 그 검토에 의하여 , 지금 생성 할 대기열을 지정 해야 합니다. 다양한 요구 사항이 있는 경우 동일한 물리적 장치에서 여러 논리적 장치를 만들 수도 있습니다.
논리 장치 핸들을 저장할 새 클래스 멤버를 추가하는 것으로 시작 하십시오.
그런 다음 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
대기열 핸들 얻기
대기열은 논리 장치와 함께 자동으로 생성되지만 아직 인터페이스를 처리 할 핸들이 없습니다. 먼저 그래픽 대기열에 핸들을 저장하는 클래스 멤버를 추가합니다.
장치 대기열은 장치가 파괴 될 때 암시적으로 정리 되므로 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