diff --git a/.gitignore b/.gitignore index e257658..bf75e78 100644 --- a/.gitignore +++ b/.gitignore @@ -12,23 +12,14 @@ *.gch *.pch -# Compiled Dynamic libraries -*.so -*.dylib -*.dll - # Fortran module files *.mod *.smod -# Compiled Static libraries -*.lai -*.la -*.a -*.lib - # Executables *.exe *.out *.app +# SPIR-V shaders +*.spv \ No newline at end of file diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..bc50ed2 --- /dev/null +++ b/.vscode/c_cpp_properties.json @@ -0,0 +1,18 @@ +{ + "configurations": [ + { + "name": "some_name", + "includePath": [ + "${workspaceFolder}/include", + "C:/VulkanSDK/1.2.189.2/Include", + "${workspaceFolder}/../dependencies/GLFW/include", + "${workspaceFolder}/../dependencies/glm" + ], + "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..3513148 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,50 @@ +{ + "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", + "list": "cpp", + "unordered_set": "cpp", + "map": "cpp", + "set": "cpp" + } +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..82d8283 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,41 @@ +{ + "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", + "-I${workspaceRoot}/../dependencies/glm", + "-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..48f51bd --- /dev/null +++ b/include/PhysicalDevice.h @@ -0,0 +1,17 @@ +#ifndef PHYSICALDEVICE_H +#define PHYSICALDEVICE_H + +#include + +#include + +typedef struct _PhysicalDevice +{ + VkPhysicalDevice device; // устройство + VkPhysicalDeviceProperties properties; // параметры + VkPhysicalDeviceFeatures features; // функции + VkPhysicalDeviceMemoryProperties memory; // память + std::vector queueFamilyProperties; // семейства очередей +} PhysicalDevice; + +#endif // PHYSICALDEVICE_H diff --git a/include/Queue.h b/include/Queue.h new file mode 100644 index 0000000..59b6e6f --- /dev/null +++ b/include/Queue.h @@ -0,0 +1,13 @@ +#ifndef QUEUE_H +#define QUEUE_H + +#include + +typedef struct _Queue +{ + uint32_t index; + VkQueue descriptor; + VkQueueFamilyProperties properties; +} Queue; + +#endif // QUEUE_H diff --git a/include/Surface.h b/include/Surface.h new file mode 100644 index 0000000..1946e48 --- /dev/null +++ b/include/Surface.h @@ -0,0 +1,21 @@ +#ifndef SURFACE_H +#define SURFACE_H + +#include + +#include + +typedef struct _Surface +{ + VkSurfaceKHR surface; // Поверхность окна + VkSurfaceCapabilitiesKHR capabilities; // общая информация + std::vector formats; // формат поверхности + std::vector presentModes; // режим показа + // Данные о списке показа + VkSurfaceFormatKHR selectedFormat; // выбранный формат поверхности + VkPresentModeKHR selectedPresentMode; // выбранный режим показа + VkExtent2D selectedExtent; // выбранное разрешение + uint32_t imageCount; // количество изображений +} Surface; + +#endif // SURFACE_H \ No newline at end of file diff --git a/include/Vertex.h b/include/Vertex.h new file mode 100644 index 0000000..fcc5205 --- /dev/null +++ b/include/Vertex.h @@ -0,0 +1,12 @@ +#ifndef VERTEX_H +#define VERTEX_H + +#include + +typedef struct _Vertex +{ + glm::vec2 position; + glm::vec3 color; +} Vertex; + +#endif // VERTEX_H \ No newline at end of file diff --git a/include/macroses.h b/include/macroses.h new file mode 100644 index 0000000..70bd360 --- /dev/null +++ b/include/macroses.h @@ -0,0 +1 @@ +#define CLAMP(min, value, max) (value > min) ? min : (max < value) ? max : value; \ No newline at end of file diff --git a/include/vk.h b/include/vk.h new file mode 100644 index 0000000..1119179 --- /dev/null +++ b/include/vk.h @@ -0,0 +1,58 @@ +#ifndef VK_H +#define VK_H + +#include +#include + +#include "PhysicalDevice.h" +#include "Surface.h" +#include "Queue.h" + +class Vulkan +{ + public: + void init(GLFWwindow* window); // инициализация + void destroy(); // завершение работы + private: + VkInstance instance; // Экземпляр Vulkan + PhysicalDevice physicalDevice; // Физическое устройство + VkDevice logicalDevice; // логическое устройство + Queue queue; // очередь + Surface surface; // Поверхность окна + VkSwapchainKHR swapChain; // Список показа + std::vector swapChainImages; // Изображения из списка показа + std::vector swapChainImageViews; // Информация об изображениях из списка показа + VkRenderPass renderPass; // Проходы рендера + VkPipelineLayout pipelineLayout; // Раскладка конвейера + VkPipeline graphicsPipeline; // Графический конвейер + VkCommandPool commandPool; // Пул команд + std::vector commandBuffers; // Буферы команд + VkBuffer vertexBuffer; // Буфер вершин + VkDeviceMemory vertexBufferMemory; // Память буфера вершин + VkBuffer indexBuffer; // Буфер индексов + VkDeviceMemory indexBufferMemory; // Память буфера индексов + + // Структура для хранения флагов + struct + { + const bool VALIDATION = true; // Использование слоев проверки + } states; + + + void createInstance(); // Создание экземпяра Vulkan + void selectPhysicalDevice(std::vector &deviceExtensions); // Выбор физического устройства + void pickQueues(); // Выбор очередей + void createLogicalDevice(std::vector &deviceExtensions); // Создание логического устройства + void createWindowSurface(GLFWwindow* window); // Создание поверхности окна + void createSwapchain(GLFWwindow* window); // Создание цепочки показа + void createRenderpass(); // Создание проходов рендера + VkShaderModule createShaderModule(const char * filename); // Создание шейдерного модуля + void createGraphicPipeline(); // Создание графического конвеера + void createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkBuffer& buffer, VkDeviceMemory& bufferMemory); // Создание произвольного буфера данных + void createCommandPool(); // Создание пула команд + void copyBuffer(VkBuffer srcBuffer, VkBuffer dstBuffer, VkDeviceSize size); // Копирование между буферами данных + void createVertexBuffer(); // Создание буфера вершин + void createIndexBuffer(); // Создание буфера индексов +}; + +#endif // VK_H \ No newline at end of file diff --git a/shaders/shader.frag b/shaders/shader.frag new file mode 100644 index 0000000..13009da --- /dev/null +++ b/shaders/shader.frag @@ -0,0 +1,9 @@ +#version 450 + +layout(location = 0) in vec3 fragColor; + +layout(location = 0) out vec4 outColor; + +void main() { + outColor = vec4(fragColor, 1.0); +} \ No newline at end of file diff --git a/shaders/shader.vert b/shaders/shader.vert new file mode 100644 index 0000000..cb40e1e --- /dev/null +++ b/shaders/shader.vert @@ -0,0 +1,11 @@ +#version 450 + +layout(location = 0) in vec2 inPosition; +layout(location = 1) in vec3 inColor; + +layout(location = 0) out vec3 fragColor; + +void main() { + gl_Position = vec4(inPosition, 0.0, 1.0); + fragColor = inColor; +} \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..846b86d --- /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(window); + + // Жизненный цикл + 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..e766e15 --- /dev/null +++ b/src/vk.cpp @@ -0,0 +1,922 @@ +#include "vk.h" +#include "Vertex.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); // Создание списка показа + createRenderpass(); // Создание проходов рендера + createGraphicPipeline(); // Создание графического конвейера + createCommandPool(); // Создание пула команд + createVertexBuffer(); // Создание буфера вершин + createIndexBuffer(); // Создание буфера индексов +} + +// завершение работы +void Vulkan::destroy() +{ + vkDestroyBuffer(logicalDevice, indexBuffer, nullptr); // Уничтожение буфера индексов + vkFreeMemory(logicalDevice, indexBufferMemory, nullptr); // Освобождение памяти буфера индексов + + vkDestroyBuffer(logicalDevice, vertexBuffer, nullptr); // Уничтожение буфера вершин + vkFreeMemory(logicalDevice, vertexBufferMemory, nullptr); // Освобождение памяти буфера вершин + + vkDestroyCommandPool(logicalDevice, commandPool, nullptr); // Уничтожение командного пула + + vkDestroyPipeline(logicalDevice, graphicsPipeline, nullptr); // Уничтожение графического конвейера + vkDestroyPipelineLayout(logicalDevice, pipelineLayout, nullptr); // Уничтожение раскладки графического конвейера + vkDestroyRenderPass(logicalDevice, renderPass, nullptr); // Уничтожение проходов рендера + + // Уничтожение информации о изображениях списка показа + 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"); + } + } +} + +// Создание проходов рендера +void Vulkan::createRenderpass() +{ + // Информация о прикреплении + VkAttachmentDescription colorAttachment{}; + colorAttachment.format = surface.selectedFormat.format; + colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT; + colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + + // Информация о выходном прикреплении + VkAttachmentReference colorAttachmentRef{}; + colorAttachmentRef.attachment = 0; + colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + + // Информация о подпроходе + VkSubpassDescription subpass{}; + subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subpass.colorAttachmentCount = 1; + subpass.pColorAttachments = &colorAttachmentRef; + + // Зависимости подпрохода + VkSubpassDependency dependency{}; + dependency.srcSubpass = VK_SUBPASS_EXTERNAL; + dependency.dstSubpass = 0; + dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependency.srcAccessMask = 0; + dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + + // Информация о создаваемом проходе рендера + VkRenderPassCreateInfo renderPassInfo{}; + renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + renderPassInfo.attachmentCount = 1; + renderPassInfo.pAttachments = &colorAttachment; + renderPassInfo.subpassCount = 1; + renderPassInfo.pSubpasses = &subpass; + renderPassInfo.dependencyCount = 1; + renderPassInfo.pDependencies = &dependency; + + // Создание проходов рендера + if (vkCreateRenderPass(logicalDevice, &renderPassInfo, nullptr, &renderPass) != VK_SUCCESS) + { + throw std::runtime_error("Unable to create render pass"); + } + +} + +#include +// Считывание бинарного файла, содержащего шейдер +void readFile(const char * filename, std::vector& buffer) +{ + // откроем файл как бинарный и установим курсор в конец файла + std::ifstream file(filename, std::ios::ate | std::ios::binary); + // если файл не открыт - генерируем исключение + if (!file.is_open()) + { + throw std::runtime_error("Can't open file"); + } + // определим размер файла + size_t fileSize = (size_t) file.tellg(); + // создадим буфер + buffer.resize(fileSize); + + // перенесем курсор в начало файла + file.seekg(0); + // считаем данные в буфер + file.read(buffer.data(), fileSize); + // закроем файл + file.close(); +} + +// Создание шейдерного модуля +VkShaderModule Vulkan::createShaderModule(const char * filename) +{ + // буфер для чтения из файла + std::vector buffer; + // считаем шейдер из файла + readFile(filename, buffer); + + // Информация о создаваемом шейдерном модуле + VkShaderModuleCreateInfo createInfo{}; + createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + createInfo.codeSize = buffer.size(); + createInfo.pCode = reinterpret_cast(buffer.data()); + + // Создание шейдерного модуля + VkShaderModule shaderModule; + if (vkCreateShaderModule(logicalDevice, &createInfo, nullptr, &shaderModule) != VK_SUCCESS) { + throw std::runtime_error("Unable to create shader module"); + } + + return shaderModule; +} + +// Создание графического конвеера +void Vulkan::createGraphicPipeline() +{ + // Входные данные вершин + VkPipelineVertexInputStateCreateInfo vertexInputInfo{}; + vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; + + // Привязка + VkVertexInputBindingDescription bindingDescription{}; + bindingDescription.binding = 0; + bindingDescription.stride = sizeof(Vertex); + bindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; + + // Описание атрибута + VkVertexInputAttributeDescription attributeDescriptions[2]; + + attributeDescriptions[0].binding = 0; + attributeDescriptions[0].location = 0; + attributeDescriptions[0].format = VK_FORMAT_R32G32_SFLOAT; + attributeDescriptions[0].offset = offsetof(Vertex, position); + + attributeDescriptions[1].binding = 0; + attributeDescriptions[1].location = 1; + attributeDescriptions[1].format = VK_FORMAT_R32G32B32_SFLOAT; + attributeDescriptions[1].offset = offsetof(Vertex, color); + + vertexInputInfo.vertexBindingDescriptionCount = 1; + vertexInputInfo.vertexAttributeDescriptionCount = 2; + vertexInputInfo.pVertexBindingDescriptions = &bindingDescription; + vertexInputInfo.pVertexAttributeDescriptions = attributeDescriptions; + + // Входной сборщик + VkPipelineInputAssemblyStateCreateInfo inputAssembly{}; + inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; + inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + inputAssembly.primitiveRestartEnable = VK_FALSE; + + // Область просмотра + VkViewport viewport{}; + viewport.x = 0.0f; + viewport.y = 0.0f; + viewport.width = surface.selectedExtent.width; + viewport.height = surface.selectedExtent.height; + viewport.minDepth = 0.0f; + viewport.maxDepth = 1.0f; + + // Прямоугольник отсечения + VkRect2D scissor{}; + scissor.offset = {0, 0}; + scissor.extent = surface.selectedExtent; + + // Состояние области просмотра + VkPipelineViewportStateCreateInfo viewportState{}; + viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; + viewportState.viewportCount = 1; + viewportState.pViewports = &viewport; + viewportState.scissorCount = 1; + viewportState.pScissors = &scissor; + + // Растеризатор + VkPipelineRasterizationStateCreateInfo rasterizer{}; + rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; + rasterizer.depthClampEnable = VK_FALSE; + rasterizer.rasterizerDiscardEnable = VK_FALSE; + rasterizer.polygonMode = VK_POLYGON_MODE_FILL; + rasterizer.lineWidth = 1.0f; + rasterizer.cullMode = VK_CULL_MODE_BACK_BIT; + rasterizer.frontFace = VK_FRONT_FACE_CLOCKWISE; + rasterizer.depthBiasEnable = VK_FALSE; + + // Мультисэмплинг + VkPipelineMultisampleStateCreateInfo multisampling{}; + multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; + multisampling.sampleShadingEnable = VK_FALSE; + multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; + + // Смешивание цветов для буфера + VkPipelineColorBlendAttachmentState colorBlendAttachment{}; + colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; + colorBlendAttachment.blendEnable = VK_FALSE; + + // Глобальные настройки смешивания цветов + VkPipelineColorBlendStateCreateInfo colorBlending{}; + colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; + colorBlending.logicOpEnable = VK_FALSE; + colorBlending.logicOp = VK_LOGIC_OP_COPY; + colorBlending.attachmentCount = 1; + colorBlending.pAttachments = &colorBlendAttachment; + colorBlending.blendConstants[0] = 0.0f; + colorBlending.blendConstants[1] = 0.0f; + colorBlending.blendConstants[2] = 0.0f; + colorBlending.blendConstants[3] = 0.0f; + + // раскладка конвейера + VkPipelineLayoutCreateInfo pipelineLayoutInfo{}; + pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + pipelineLayoutInfo.setLayoutCount = 0; + pipelineLayoutInfo.pushConstantRangeCount = 0; + + if (vkCreatePipelineLayout(logicalDevice, &pipelineLayoutInfo, nullptr, &pipelineLayout) != VK_SUCCESS) + { + throw std::runtime_error("Unable to create pipeline layout"); + } + + // Создание шейдеров + VkShaderModule vertShaderModule = createShaderModule("shaders/vert.spv"); + VkShaderModule fragShaderModule = createShaderModule("shaders/frag.spv"); + + VkPipelineShaderStageCreateInfo vertShaderStageInfo{}; + vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + vertShaderStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT; + vertShaderStageInfo.module = vertShaderModule; + vertShaderStageInfo.pName = "main"; + + VkPipelineShaderStageCreateInfo fragShaderStageInfo{}; + fragShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + fragShaderStageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT; + fragShaderStageInfo.module = fragShaderModule; + fragShaderStageInfo.pName = "main"; + + // Шейдерные стадии + VkPipelineShaderStageCreateInfo shaderStages[] = {vertShaderStageInfo, fragShaderStageInfo}; + + + // Информация о создаваемом конвейере + VkGraphicsPipelineCreateInfo pipelineInfo{}; + pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; + pipelineInfo.stageCount = 2; + pipelineInfo.pStages = shaderStages; + pipelineInfo.pVertexInputState = &vertexInputInfo; + pipelineInfo.pInputAssemblyState = &inputAssembly; + pipelineInfo.pViewportState = &viewportState; + pipelineInfo.pRasterizationState = &rasterizer; + pipelineInfo.pMultisampleState = &multisampling; + pipelineInfo.pColorBlendState = &colorBlending; + pipelineInfo.layout = pipelineLayout; + pipelineInfo.renderPass = renderPass; + pipelineInfo.subpass = 0; + pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; + + // Создание графического конвейера + if (vkCreateGraphicsPipelines(logicalDevice, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &graphicsPipeline) != VK_SUCCESS) + { + throw std::runtime_error("Unable to create graphics pipeline"); + } + + // Удаление шейдерных модулей + vkDestroyShaderModule(logicalDevice, fragShaderModule, nullptr); + vkDestroyShaderModule(logicalDevice, vertShaderModule, nullptr); +} + +// Создание произвольного буфера данных +void Vulkan::createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkBuffer& buffer, VkDeviceMemory& bufferMemory) +{ + // Информация о создаваемом буфере + VkBufferCreateInfo bufferInfo{}; + bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + bufferInfo.size = size; + bufferInfo.usage = usage; + bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + + // Создание буфера + if (vkCreateBuffer(logicalDevice, &bufferInfo, nullptr, &buffer) != VK_SUCCESS) + { + throw std::runtime_error("Unable to create buffer"); + } + + // Требования к памяти + VkMemoryRequirements memRequirements; + vkGetBufferMemoryRequirements(logicalDevice, buffer, &memRequirements); + + // Поиск индекса типа подходящей памяти + uint32_t index_memory; + for (index_memory = 0; index_memory < physicalDevice.memory.memoryTypeCount; index_memory++) + { + if ((memRequirements.memoryTypeBits & (1 << index_memory)) + && (physicalDevice.memory.memoryTypes[index_memory].propertyFlags & properties) == properties) + { + break; + } + } + + // Если индекс равен размеру массива - поиск не удался и нужно выдать исключение + if (index_memory == physicalDevice.memory.memoryTypeCount) + throw std::runtime_error("Unable to find suitable memory type"); + + // Информация о выделяемой памяти + VkMemoryAllocateInfo allocInfo{}; + allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + allocInfo.allocationSize = memRequirements.size; + allocInfo.memoryTypeIndex = index_memory; + + // Выделение памяти + if (vkAllocateMemory(logicalDevice, &allocInfo, nullptr, &bufferMemory) != VK_SUCCESS) + { + throw std::runtime_error("Unable to allocate buffer memory"); + } + + // Привязка выделенной памяти к буферу + vkBindBufferMemory(logicalDevice, buffer, bufferMemory, 0); +} + +// Создание пула команд +void Vulkan::createCommandPool() +{ + // Информация о создаваемом командном пуле + VkCommandPoolCreateInfo poolInfo{}; + poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + poolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; + poolInfo.queueFamilyIndex = queue.index; + + // Создание командного пула + if (vkCreateCommandPool(logicalDevice, &poolInfo, nullptr, &commandPool) != VK_SUCCESS) + { + throw std::runtime_error("Unable to create graphics command pool"); + } + + // Выделим память под буферы команд + commandBuffers.resize(swapChainImages.size()); + + // Информация о выделяемых буферах + VkCommandBufferAllocateInfo allocInfo{}; + allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + allocInfo.commandPool = commandPool; + allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + allocInfo.commandBufferCount = (uint32_t) commandBuffers.size(); + + // Выделение буферов команд из пула команд + if (vkAllocateCommandBuffers(logicalDevice, &allocInfo, commandBuffers.data()) != VK_SUCCESS) + { + throw std::runtime_error("Unable to allocate command buffers"); + } +} + +// Копирование между буферами данных +void Vulkan::copyBuffer(VkBuffer srcBuffer, VkBuffer dstBuffer, VkDeviceSize size) +{ + // Информация о выделяемом буфере команд + VkCommandBufferAllocateInfo allocInfo{}; + allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + allocInfo.commandPool = commandPool; + allocInfo.commandBufferCount = 1; + + // Дескриптор и выделение буфера команд + VkCommandBuffer commandBuffer; + vkAllocateCommandBuffers(logicalDevice, &allocInfo, &commandBuffer); + + // Начало записи команд + VkCommandBufferBeginInfo beginInfo{}; + beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + vkBeginCommandBuffer(commandBuffer, &beginInfo); + + // Операция копирования + VkBufferCopy copyRegion{}; + copyRegion.size = size; + vkCmdCopyBuffer(commandBuffer, srcBuffer, dstBuffer, 1, ©Region); + + // Конец записи команд + vkEndCommandBuffer(commandBuffer); + + // Информация о запускаемых буферах команд + VkSubmitInfo submitInfo{}; + submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submitInfo.commandBufferCount = 1; + submitInfo.pCommandBuffers = &commandBuffer; + + // Запуск командного буфера копирования и ожидание завершения + vkQueueSubmit(queue.descriptor, 1, &submitInfo, VK_NULL_HANDLE); + vkQueueWaitIdle(queue.descriptor); + + // Освобождение командного буфера копирования + vkFreeCommandBuffers(logicalDevice, commandPool, 1, &commandBuffer); +} + +// Создание вершинного буфера +void Vulkan::createVertexBuffer() +{ + //Вершины, записываемые в буфер + Vertex vertices[] = { + { {-0.5f, -0.5f}, {1.0f, 0.0f, 0.0f} }, + { { 0.5f, -0.5f}, {0.0f, 1.0f, 0.0f} }, + { { 0.5f, 0.5f}, {0.0f, 0.0f, 1.0f} }, + { {-0.5f, 0.5f}, {1.0f, 1.0f, 1.0f} } + }; + // Размер буфера в байтах + VkDeviceSize bufferSize = sizeof(Vertex) * 4; + + // Промежуточный буфер для переноса на устройство + VkBuffer stagingBuffer; + VkDeviceMemory stagingBufferMemory; + createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, stagingBuffer, stagingBufferMemory); + + // Отображение памяти буфера + void* data; + vkMapMemory(logicalDevice, stagingBufferMemory, 0, bufferSize, 0, &data); + // Копирование вершин в промежуточный буфер + memcpy(data, vertices, (size_t) bufferSize); + // Прекращение отображения памяти буфера + vkUnmapMemory(logicalDevice, stagingBufferMemory); + + // Создание буфера вершин + createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, vertexBuffer, vertexBufferMemory); + // Копирование из промежуточного в буфер вершин + copyBuffer(stagingBuffer, vertexBuffer, bufferSize); + + // Освобождение памяти и уничтожение буферов + vkDestroyBuffer(logicalDevice, stagingBuffer, nullptr); + vkFreeMemory(logicalDevice, stagingBufferMemory, nullptr); +} + +// Создание буфера индексов +void Vulkan::createIndexBuffer() +{ + // Индексы, записываемые в буфер + uint16_t indices[] = {0, 1, 2, 2, 3, 0}; + // Размер буфера в байтах + VkDeviceSize bufferSize = sizeof(uint16_t) * 6; + + // Промежуточный буфер для переноса на устройство + VkBuffer stagingBuffer; + VkDeviceMemory stagingBufferMemory; + createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, stagingBuffer, stagingBufferMemory); + + // Отображение памяти буфера + void* data; + vkMapMemory(logicalDevice, stagingBufferMemory, 0, bufferSize, 0, &data); + // Копирование вершин в промежуточный буфер + memcpy(data, indices, (size_t) bufferSize); + // Прекращение отображения памяти буфера + vkUnmapMemory(logicalDevice, stagingBufferMemory); + + // Создание буфера вершин + createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, indexBuffer, indexBufferMemory); + // Копирование из промежуточного в буфер вершин + copyBuffer(stagingBuffer, indexBuffer, bufferSize); + + // Освобождение памяти и уничтожение буферов + vkDestroyBuffer(logicalDevice, stagingBuffer, nullptr); + vkFreeMemory(logicalDevice, stagingBufferMemory, nullptr); +}