#include "vk.h" #include #include #include #include "macroses.h" // инициализация void Vulkan::init(GLFWwindow* window) { createInstance(); // Создание экземпяра createWindowSurface(window); // Создание поверхности // Расширения для устройства: имена задаются внутри фигурных скобок в кавычках std::vector deviceExtensions({"VK_KHR_swapchain"}); selectPhysicalDevice(deviceExtensions); // Выбор физического устройства createLogicalDevice(deviceExtensions); // Создание физического устройства createSwapchain(window); // Создание списка показа } // завершение работы void Vulkan::destroy() { // Уничтожение информации о изображениях списка показа for (auto & imageView : swapChainImageViews) { vkDestroyImageView(logicalDevice, imageView, nullptr); } vkDestroySwapchainKHR(logicalDevice, swapChain, nullptr); // уничтожение цепочки показа vkDestroySurfaceKHR(instance, surface.surface, nullptr); // уничтожение поверхности vkDestroyDevice(logicalDevice, nullptr); // Уничтожение логического устройства vkDestroyInstance(instance, nullptr); // Уничтожение экземпляра Vulkan } #include // Проверка слоев на доступность. Возвращает true, если все слои доступны // по ссылке заполняет вектор недоступных слоев bool checkValidationLayerSupport(std::vector requestedLayers, std::vector & unavailableLayers) { bool layerAvailable; // флаг доступности слоя для цикла // Первым вызовом определим кол-во доступных слоев uint32_t layerCount; vkEnumerateInstanceLayerProperties(&layerCount, nullptr); // Вторым вызовом запишем в вектор доступные слои std::vector availableLayers(layerCount); vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data()); // Цикл по запрошенным слоям for (const char* layerName : requestedLayers) { layerAvailable = false; // Цикл по доступным слоям for (const auto& layerProperties : availableLayers) { // Сравнение строк if (strcmp(layerName, layerProperties.layerName) == 0) { layerAvailable = true; break; } } // Если слой не найден то заносим в массив недоступных if (!layerAvailable) { unavailableLayers.push_back(layerName); } } return unavailableLayers.size() == 0; } // Проверка слоев устройства на доступность. Возвращает true, если все слои доступны // по ссылке заполняет вектор недоступных слоев bool checkDeviceLayerSupport(VkPhysicalDevice physicalDevice, std::vector requestedLayers, std::vector & unavailableLayers) { bool layerAvailable; // флаг доступности слоя для цикла // Первым вызовом определим кол-во доступных слоев uint32_t layerCount; vkEnumerateDeviceLayerProperties(physicalDevice, &layerCount, nullptr); // Вторым вызовом запишем в вектор доступные слои std::vector availableLayers(layerCount); vkEnumerateDeviceLayerProperties(physicalDevice, &layerCount, availableLayers.data()); // Цикл по запрошенным слоям for (const char* layerName : requestedLayers) { layerAvailable = false; // Цикл по доступным слоям for (const auto& layerProperties : availableLayers) { // Сравнение строк if (strcmp(layerName, layerProperties.layerName) == 0) { layerAvailable = true; break; } } // Если слой не найден то заносим в массив недоступных if (!layerAvailable) { unavailableLayers.push_back(layerName); } } return unavailableLayers.size() == 0; } void Vulkan::createInstance() { // Структура с данными о приложении VkApplicationInfo appInfo{}; appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; appInfo.pApplicationName = "Vulkan Notes"; 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; // Расширения для glfw uint32_t glfwExtensionCount = 0; const char** glfwExtensions; glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount); // Инициализируем вектор расширений тем, что требуется для glfw std::vector extensions(glfwExtensions, glfwExtensions + glfwExtensionCount); // Подключение других расширений // extensions.push_back(ИМЯ_РАСШИРЕНИЯ); // Запишем данные об используемых расширениях в структуру createInfo.enabledExtensionCount = static_cast(extensions.size()); createInfo.ppEnabledExtensionNames = extensions.data(); // Подключение слоев std::vector validationLayers = { "VK_LAYER_KHRONOS_validation" }; if (states.VALIDATION) { createInfo.enabledLayerCount = static_cast(validationLayers.size()); createInfo.ppEnabledLayerNames = validationLayers.data(); } // Проверим доступность слоев std::vector unavailableLayers; if (!checkValidationLayerSupport(validationLayers, unavailableLayers)) { std::cout << "Запрошены недоступные слои:\n"; // Цикл по недоступным слоям for (const char* layer : unavailableLayers) std::cout << layer << "\n"; // Отправим исключение об отсутствующем слое throw std::runtime_error("Requested layer unavailable"); } // Создание экземпляра Vulkan VkResult result = vkCreateInstance(&createInfo, nullptr, &instance); if (result != VK_SUCCESS) { // Отправим исключение в случае ошибок создания экземпляра throw std::runtime_error("Instance create error"); } } #include // Выбор физического устройства на основании требований PhysicalDevice selectPhysicalDeviceByProperties(std::vector & devices, Surface & surface, std::vector &requestedExtensions) { int i; PhysicalDevice result; // физическое устройство (PhysicalDevice.h) for (const auto& device : devices) { // Запомним устройство result.device = device; // Получаем данные vkGetPhysicalDeviceProperties(device, &result.properties); vkGetPhysicalDeviceFeatures(device, &result.features); vkGetPhysicalDeviceMemoryProperties(device, &result.memory); // Данные по семействам очередей uint32_t queueFamilyPropertiesCount = 0; vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyPropertiesCount, nullptr); result.queueFamilyProperties.resize(queueFamilyPropertiesCount); vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyPropertiesCount, result.queueFamilyProperties.data()); // Данные по расширениям uint32_t extensionsCount = 0; vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionsCount, nullptr); std::vector extensions(extensionsCount); vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionsCount, extensions.data()); int availableExtensionsCount = 0; // подсчитаем совпадающие расширения for (auto extension1 : requestedExtensions) for (auto extension2 : extensions) if (strcmp(extension1, extension2.extensionName) == 0) { availableExtensionsCount++; break; } // Получение информации о поверхности VkSurfaceCapabilitiesKHR capabilities; vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, surface.surface, &capabilities); // Получение форматов поверхности uint32_t formatCount; vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface.surface, &formatCount, nullptr); std::vector formats(formatCount); vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface.surface, &formatCount, formats.data()); // Получение данных о поддерживаемых режимах показа uint32_t presentModeCount; vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface.surface, &presentModeCount, nullptr); std::vector presentModes(presentModeCount); vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface.surface, &presentModeCount, presentModes.data()); // Если есть форматы и режимы показа, то на данном устройстве можно создать список показа bool swapchainSupport = formatCount && presentModeCount; // Производим оценку if (availableExtensionsCount == requestedExtensions.size() && result.features.geometryShader && 4000 < result.memory.memoryHeaps[0].size / 1000 / 1000 && swapchainSupport) { // Заполним данные о поверхности surface.capabilities = capabilities; surface.formats = formats; surface.presentModes = presentModes; // Вернем устройство return result; } } // Если устройство не найдено - вернем пустую структуру return PhysicalDevice(); } // Выбор физического устройства void Vulkan::selectPhysicalDevice(std::vector &deviceExtensions) { // Узнаем количество доступных устройств uint32_t deviceCount = 0; vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr); // Проверка на отсутствие физических устройств if (deviceCount == 0) { throw std::runtime_error("Unable to find physical devices"); } // Создадим вектор нужного размера и заполним его данными std::vector devices(deviceCount); vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data()); // Выбор физического устройства на основании требований physicalDevice = selectPhysicalDeviceByProperties(devices, surface, deviceExtensions); // Если не удалось выбрать подходящее требованием устройство - выдадим исключение if (!physicalDevice.device) { throw std::runtime_error("failed to find a suitable GPU!"); } } // Выбор очередей void Vulkan::pickQueues() { queue.index = -1; for (int i = 0; i < physicalDevice.queueFamilyProperties.size(); i++) { // Проверка возможности вывода VkBool32 presentSupport = false; vkGetPhysicalDeviceSurfaceSupportKHR(physicalDevice.device, i, surface.surface, &presentSupport); // Проверка поддержки очередью графических операций if (physicalDevice.queueFamilyProperties[i].queueFlags & VK_QUEUE_GRAPHICS_BIT && presentSupport) { queue.index = i; queue.properties = physicalDevice.queueFamilyProperties[i]; break; } } } // Создание логического устройства void Vulkan::createLogicalDevice(std::vector &deviceExtensions) { // Выберем очереди pickQueues(); // Приоритеты очередей float priority[1] = {1}; // Данные о необходимых очередях VkDeviceQueueCreateInfo queueCreateInfo{}; queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; queueCreateInfo.queueFamilyIndex = queue.index; queueCreateInfo.queueCount = 1; queueCreateInfo.pQueuePriorities = priority; // слои для логического устройства std::vector layers; // Подключение других слоев // layers.push_back(ИМЯ_СЛОЯ); // Проверим доступность слоев std::vector unavailableLayers; if (!checkDeviceLayerSupport(physicalDevice.device, layers, unavailableLayers)) { std::cout << "Запрошены недоступные слои:\n"; // Цикл по недоступным слоям for (const char* layer : unavailableLayers) std::cout << layer << "\n"; // Отправим исключение об отсутствующем слое throw std::runtime_error("Requested layer unavailable"); } // Данные о создаваемом логическом устройстве VkDeviceCreateInfo createInfo{}; createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; createInfo.pQueueCreateInfos = &queueCreateInfo; createInfo.queueCreateInfoCount = 1; createInfo.enabledExtensionCount = deviceExtensions.size(); createInfo.ppEnabledExtensionNames = deviceExtensions.data(); createInfo.enabledLayerCount = layers.size(); createInfo.ppEnabledLayerNames = layers.data(); createInfo.pEnabledFeatures = nullptr;//&physicalDevice.features; // Создание логического устройства if (vkCreateDevice(physicalDevice.device, &createInfo, nullptr, &logicalDevice) != VK_SUCCESS) { // Отправим исключение в случае ошибок создания лог. устройства throw std::runtime_error("failed to create logical device!"); } // Получим дескриптор очереди логического устройства vkGetDeviceQueue(logicalDevice, queueCreateInfo.queueFamilyIndex, 0, &queue.descriptor); } // Создание поверхности окна void Vulkan::createWindowSurface(GLFWwindow* window) { if (glfwCreateWindowSurface(instance, window, nullptr, &surface.surface) != VK_SUCCESS) { throw std::runtime_error("Unable to create window surface"); } } // Создание цепочки показа void Vulkan::createSwapchain(GLFWwindow* window) { // Выбор формата surface.selectedFormat = surface.formats[0]; for (auto& format : surface.formats) { if (format.format == VK_FORMAT_B8G8R8A8_SRGB && format.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) { surface.selectedFormat = format; break; } } // Выбор режима показа surface.selectedPresentMode = VK_PRESENT_MODE_FIFO_KHR; for (auto& presentMode : surface.presentModes) { if (presentMode == VK_PRESENT_MODE_MAILBOX_KHR) { surface.selectedPresentMode = presentMode; break; } } // Выбор разрешения изображений // Разрешение окна int width, height; glfwGetFramebufferSize(window, &width, &height); // Выберем разрешение исходя из ограничений физического устройства surface.selectedExtent.width = CLAMP( surface.capabilities.minImageExtent.width , width , surface.capabilities.maxImageExtent.width ); surface.selectedExtent.height = CLAMP( surface.capabilities.minImageExtent.height , height , surface.capabilities.maxImageExtent.height ); // Выбор количества изображений в списке показа surface.imageCount = surface.capabilities.minImageCount + 1; // Если есть ограничение по максимуму изображений - применим его if (surface.capabilities.maxImageCount) surface.imageCount %= surface.capabilities.maxImageCount; // Заполнение данных о создаваемом списке показа VkSwapchainCreateInfoKHR createInfo{}; createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; createInfo.surface = surface.surface; createInfo.minImageCount = surface.imageCount; createInfo.imageFormat = surface.selectedFormat.format; createInfo.imageColorSpace = surface.selectedFormat.colorSpace; createInfo.imageExtent = surface.selectedExtent; createInfo.imageArrayLayers = 1; createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; createInfo.preTransform = surface.capabilities.currentTransform; createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; createInfo.presentMode = surface.selectedPresentMode; createInfo.clipped = VK_TRUE; createInfo.oldSwapchain = VK_NULL_HANDLE; // Создание списка показа if (vkCreateSwapchainKHR(logicalDevice, &createInfo, nullptr, &swapChain) != VK_SUCCESS) { throw std::runtime_error("Unable to create swap chain"); } // Получение изображений списка показа vkGetSwapchainImagesKHR(logicalDevice, swapChain, &surface.imageCount, nullptr); swapChainImages.resize(surface.imageCount); vkGetSwapchainImagesKHR(logicalDevice, swapChain, &surface.imageCount, swapChainImages.data()); // Зададим размер массива в соответствии с количеством изображений swapChainImageViews.resize(swapChainImages.size()); // Для каждого изображения из списка показа for (int i = 0; i < swapChainImages.size(); i++) { // Заполним данные о создаваемом объекте VkImageView VkImageViewCreateInfo createInfo{}; createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; createInfo.image = swapChainImages[i]; createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; createInfo.format = surface.selectedFormat.format; createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; createInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; createInfo.subresourceRange.baseMipLevel = 0; createInfo.subresourceRange.levelCount = 1; createInfo.subresourceRange.baseArrayLayer = 0; createInfo.subresourceRange.layerCount = 1; // Создание VkImageView if (vkCreateImageView(logicalDevice, &createInfo, nullptr, &swapChainImageViews[i]) != VK_SUCCESS) { throw std::runtime_error("Unable to create image views"); } } }