diff --git a/.gitignore b/.gitignore index e257658..dd4c5db 100644 --- a/.gitignore +++ b/.gitignore @@ -12,21 +12,10 @@ *.gch *.pch -# Compiled Dynamic libraries -*.so -*.dylib -*.dll - # Fortran module files *.mod *.smod -# Compiled Static libraries -*.lai -*.la -*.a -*.lib - # Executables *.exe *.out diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..47ba8c3 --- /dev/null +++ b/.vscode/c_cpp_properties.json @@ -0,0 +1,17 @@ +{ + "configurations": [ + { + "name": "some_name", + "includePath": [ + "${workspaceFolder}/include", + "C:/VulkanSDK/1.2.189.2/Include", + "${workspaceFolder}/../dependencies/GLFW/include" + ], + "compilerPath": "C:/MinGW/bin/g++.exe", + "cStandard": "c11", + "cppStandard": "c++11", + "intelliSenseMode": "gcc-x86" + } + ], + "version": 4 +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..0e1b4a0 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,46 @@ +{ + "files.associations": { + "vector": "cpp", + "array": "cpp", + "atomic": "cpp", + "*.tcc": "cpp", + "cctype": "cpp", + "clocale": "cpp", + "cmath": "cpp", + "cstdarg": "cpp", + "cstddef": "cpp", + "cstdint": "cpp", + "cstdio": "cpp", + "cstdlib": "cpp", + "cwchar": "cpp", + "cwctype": "cpp", + "deque": "cpp", + "unordered_map": "cpp", + "exception": "cpp", + "algorithm": "cpp", + "functional": "cpp", + "iterator": "cpp", + "memory": "cpp", + "memory_resource": "cpp", + "numeric": "cpp", + "random": "cpp", + "string": "cpp", + "system_error": "cpp", + "tuple": "cpp", + "type_traits": "cpp", + "utility": "cpp", + "fstream": "cpp", + "initializer_list": "cpp", + "iosfwd": "cpp", + "iostream": "cpp", + "istream": "cpp", + "limits": "cpp", + "new": "cpp", + "ostream": "cpp", + "sstream": "cpp", + "stdexcept": "cpp", + "streambuf": "cpp", + "typeinfo": "cpp", + "cstring": "cpp" + } +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..bb37bab --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,40 @@ +{ + "tasks": [ + { + "type": "cppbuild", + "label": "C/C++: g++.exe сборка активного файла", + "command": "C:/MinGW/bin/g++.exe", + "args": [ + "-fdiagnostics-color=always", + "${workspaceRoot}/src/*.cpp", + + "-I${workspaceRoot}/include", + + "--std=c++11", + + "-IC:/VulkanSDK/1.2.189.2/Include", + "-LC:/VulkanSDK/1.2.189.2/Lib32", + + "-I${workspaceRoot}/../dependencies/GLFW/include", + "-L${workspaceRoot}/../dependencies/GLFW/lib-mingw", + "-static", + "-lvulkan-1", + "-lglfw3dll", + "-o", + "${workspaceRoot}/${workspaceFolderBasename}.exe" + ], + "options": { + "cwd": "${fileDirname}" + }, + "problemMatcher": [ + "$gcc" + ], + "group": { + "kind": "build", + "isDefault": true + }, + "detail": "Задача создана отладчиком." + } + ], + "version": "2.0.0" +} \ No newline at end of file diff --git a/glfw3.dll b/glfw3.dll new file mode 100644 index 0000000..f0f4e36 Binary files /dev/null and b/glfw3.dll differ diff --git a/include/PhysicalDevice.h b/include/PhysicalDevice.h new file mode 100644 index 0000000..23b5e41 --- /dev/null +++ b/include/PhysicalDevice.h @@ -0,0 +1,19 @@ +#ifndef PHYSICALDEVICE_H +#define PHYSICALDEVICE_H + +#include + +#include + +typedef struct _PhysicalDevice +{ + VkPhysicalDevice device; // устройство + VkPhysicalDeviceProperties properties; // параметры + VkPhysicalDeviceFeatures features; // функции + VkPhysicalDeviceMemoryProperties memory; // память + std::vector queueFamilyProperties; // семейства очередей + + uint32_t pickQueueFamily(VkQueueFlags); +} PhysicalDevice; + +#endif // PHYSICALDEVICE_H diff --git a/include/vk.h b/include/vk.h new file mode 100644 index 0000000..1bb1f45 --- /dev/null +++ b/include/vk.h @@ -0,0 +1,31 @@ +#ifndef VK_H +#define VK_H + +#include +#include + +#include "PhysicalDevice.h" + +class Vulkan +{ + public: + void init(); // инициализация + void destroy(); // завершение работы + private: + VkInstance instance; // Экземпляр Vulkan + PhysicalDevice physicalDevice; // Физическое устройство + VkDevice logicalDevice; // логическое устройство + VkQueue graphicalQueue; // очередь для работы с графикой + // Структура для хранения флагов + struct + { + const bool VALIDATION = true; // Использование слоев проверки + } states; + + + void createInstance(); // Создание экземпяра Vulkan + void selectPhysicalDevice(std::vector &deviceExtensions); // Выбор физического устройства + void createLogicalDevice(std::vector &deviceExtensions); // Создание логического устройства +}; + +#endif // VK_H \ No newline at end of file diff --git a/src/PhysicalDevice.cpp b/src/PhysicalDevice.cpp new file mode 100644 index 0000000..92fbf4a --- /dev/null +++ b/src/PhysicalDevice.cpp @@ -0,0 +1,16 @@ +#include "PhysicalDevice.h" + +// Возвращает индекс первой попавшейся очереди, соответствующей требуемым флагам +uint32_t PhysicalDevice::pickQueueFamily(VkQueueFlags flags) +{ + // Цикл по параметрам семейств очередей + for (uint32_t index = 0; index < queueFamilyProperties.size(); index++) + { + // Если очередь соответствует требованиям по возможностям очереди + if (queueFamilyProperties[index].queueFlags & flags) + { + // возвращаем её индекс + return index; + } + } +} \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..07e7fc1 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,53 @@ +#include "vk.h" + + +#include + +void vkInit(); + +int main(int argc, char* argv[]) { + + // Инициализация GLFW + glfwInit(); + + // Проверка доступности Vulkan + if (glfwVulkanSupported()) + { + // объект класса-обертки Vulkan API + Vulkan vulkan; + + // Отключим создание контекста + glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); + // Отключим возможность изменения размеров окна + glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); + // Создание окна + GLFWwindow* window = glfwCreateWindow(800, 600, "Vulkan window", nullptr, nullptr); + + // Инициализация Vulkan API + vulkan.init(); + + // Жизненный цикл + while(!glfwWindowShouldClose(window)) { + // Обработка событий + glfwPollEvents(); + } + + // Уничтожение окна + glfwDestroyWindow(window); + + // Завершение работы с Vulkan + vulkan.destroy(); + } + else + std::cout << "There is no Vulkan Supported\n"; + + // Завершение работы с GLFW + glfwTerminate(); + + return 0; +} + + + + + diff --git a/src/vk.cpp b/src/vk.cpp new file mode 100644 index 0000000..bb84eb8 --- /dev/null +++ b/src/vk.cpp @@ -0,0 +1,287 @@ +#include "vk.h" + +#include +#include +#include + +// инициализация +void Vulkan::init() +{ + createInstance(); // Создание экземпяра + // Расширения для устройства: имена задаются внутри фигурных скобок в кавычках + std::vector deviceExtensions({}); + selectPhysicalDevice(deviceExtensions); // Выбор физического устройства + createLogicalDevice(deviceExtensions); // Создание физического устройства +} + +// завершение работы +void Vulkan::destroy() +{ + 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, 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; + } + + + // Производим оценку + if (availableExtensionsCount == requestedExtensions.size() + && result.features.geometryShader + && 4000 < result.memory.memoryHeaps[0].size / 1000 / 1000) + 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, deviceExtensions); + + // Если не удалось выбрать подходящее требованием устройство - выдадим исключение + if (!physicalDevice.device) + { + throw std::runtime_error("failed to find a suitable GPU!"); + } +} + +void Vulkan::createLogicalDevice(std::vector &deviceExtensions) +{ + // Приоритеты очередей + float priority[1] = {1}; + // Данные о необходимых очередях + VkDeviceQueueCreateInfo queueCreateInfo{}; + queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + queueCreateInfo.queueFamilyIndex = physicalDevice.pickQueueFamily(VK_QUEUE_GRAPHICS_BIT); + 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, &graphicalQueue); +}