parent
a1022e0e30
commit
a6eb297a78
18 changed files with 1078 additions and 920 deletions
@ -1,921 +1,7 @@ |
|||||||
#define GLFW_INCLUDE_VULKAN |
#include "vulkan/application.hpp" |
||||||
#include <GLFW/glfw3.h> |
|
||||||
|
|
||||||
#include <glm/glm.hpp> |
#include <thread> |
||||||
|
|
||||||
#include <iostream> |
|
||||||
#include <vector> |
|
||||||
#include <cstring> |
|
||||||
#include <optional> |
|
||||||
#include <set> |
|
||||||
#include <fstream> |
|
||||||
#include <numeric> |
|
||||||
#include <chrono> |
|
||||||
#include <array> |
|
||||||
|
|
||||||
const std::vector<const char*> deviceExtensions = { |
|
||||||
VK_KHR_SWAPCHAIN_EXTENSION_NAME |
|
||||||
}; |
|
||||||
|
|
||||||
#ifndef NDEBUG |
|
||||||
#define ENABLE_VALIDATION_LAYERS |
|
||||||
#endif |
|
||||||
|
|
||||||
#ifdef ENABLE_VALIDATION_LAYERS |
|
||||||
const std::vector<const char*> validationLayers = { |
|
||||||
"VK_LAYER_KHRONOS_validation" |
|
||||||
}; |
|
||||||
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; |
|
||||||
} |
|
||||||
#endif |
|
||||||
|
|
||||||
constexpr int MAX_FRAMES_IN_FLIGHT = 2; |
|
||||||
|
|
||||||
class Timer { |
|
||||||
public: |
|
||||||
explicit Timer(){ |
|
||||||
start = std::chrono::system_clock::now(); |
|
||||||
} |
|
||||||
~Timer(){ |
|
||||||
size_t nanoseconds = (std::chrono::system_clock::now() - start).count(); |
|
||||||
printf("Timer: %zu mus\n", nanoseconds / 1000); |
|
||||||
} |
|
||||||
private: |
|
||||||
std::chrono::time_point<std::chrono::system_clock> start; |
|
||||||
}; |
|
||||||
|
|
||||||
std::vector<char> readFile(const std::string& fileName){ |
|
||||||
std::ifstream file(fileName, std::ios::ate | std::ios::binary); |
|
||||||
|
|
||||||
if (!file.is_open()){ |
|
||||||
throw std::runtime_error("failed to open file!"); |
|
||||||
} |
|
||||||
|
|
||||||
size_t fileSize = file.tellg(); |
|
||||||
std::vector<char> buffer(fileSize); |
|
||||||
|
|
||||||
file.seekg(0); |
|
||||||
file.read(buffer.data(), static_cast<std::streamsize>(fileSize)); |
|
||||||
file.close(); |
|
||||||
|
|
||||||
return buffer; |
|
||||||
} |
|
||||||
|
|
||||||
struct Vertex { |
|
||||||
glm::vec2 pos; |
|
||||||
glm::vec3 color; |
|
||||||
|
|
||||||
static VkVertexInputBindingDescription getBindingDescription(){ |
|
||||||
VkVertexInputBindingDescription bindingDescription {}; |
|
||||||
bindingDescription.binding = 0; |
|
||||||
bindingDescription.stride = sizeof(Vertex); |
|
||||||
bindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; |
|
||||||
return bindingDescription; |
|
||||||
} |
|
||||||
|
|
||||||
static std::array<VkVertexInputAttributeDescription, 2> getAttributeDescriptions(){ |
|
||||||
std::array<VkVertexInputAttributeDescription, 2> attributeDescriptions {}; |
|
||||||
|
|
||||||
attributeDescriptions[0].binding = 0; |
|
||||||
attributeDescriptions[0].location = 0; |
|
||||||
attributeDescriptions[0].format = VK_FORMAT_R32G32_SFLOAT; |
|
||||||
attributeDescriptions[0].offset = offsetof(Vertex, pos); |
|
||||||
|
|
||||||
attributeDescriptions[1].binding = 0; |
|
||||||
attributeDescriptions[1].location = 1; |
|
||||||
attributeDescriptions[1].format = VK_FORMAT_R32G32B32_SFLOAT; |
|
||||||
attributeDescriptions[1].offset = offsetof(Vertex, color); |
|
||||||
|
|
||||||
return attributeDescriptions; |
|
||||||
} |
|
||||||
}; |
|
||||||
|
|
||||||
const std::vector<Vertex> vertices = { |
|
||||||
{{0.0, -0.5}, {1, 0, 0}}, |
|
||||||
{{0.5, 0.5}, {0, 1, 0}}, |
|
||||||
{{-0.5, 0.5}, {0, 0, 1}} |
|
||||||
}; |
|
||||||
|
|
||||||
class MyApp { |
|
||||||
public: |
|
||||||
void run(){ |
|
||||||
initWindow(); |
|
||||||
initVulkan(); |
|
||||||
mainLoop(); |
|
||||||
cleanup(); |
|
||||||
} |
|
||||||
private: |
|
||||||
GLFWwindow *window = nullptr; |
|
||||||
|
|
||||||
void initWindow(){ |
|
||||||
glfwInit(); |
|
||||||
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); |
|
||||||
glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); |
|
||||||
window = glfwCreateWindow(800, 600, "Vulkan Simulation", nullptr, nullptr); |
|
||||||
} |
|
||||||
void initVulkan(){ |
|
||||||
createInstance(); |
|
||||||
createSurface(); |
|
||||||
pickPhysicalDevice(); |
|
||||||
createLogicalDevice(); |
|
||||||
createSwapchain(); |
|
||||||
createImageViews(); |
|
||||||
createRenderPass(); |
|
||||||
createGraphicsPipeline(); |
|
||||||
createFramebuffers(); |
|
||||||
createVertexBuffer(); |
|
||||||
createCommandPool(); |
|
||||||
createCommandBuffers(); |
|
||||||
createSyncObjects(); |
|
||||||
} |
|
||||||
|
|
||||||
VkInstance instance = VK_NULL_HANDLE; |
|
||||||
void createInstance(){ |
|
||||||
#ifdef ENABLE_VALIDATION_LAYERS |
|
||||||
if (!checkValidationLayerSupport()){ |
|
||||||
throw std::runtime_error("validation layers requested, but not available!"); |
|
||||||
} |
|
||||||
#endif |
|
||||||
|
|
||||||
VkApplicationInfo applicationInfo {}; |
|
||||||
applicationInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; |
|
||||||
applicationInfo.pApplicationName = "Coole Simulation"; |
|
||||||
applicationInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0); |
|
||||||
applicationInfo.pEngineName = "No Engine"; |
|
||||||
applicationInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0); |
|
||||||
applicationInfo.apiVersion = VK_API_VERSION_1_0; |
|
||||||
|
|
||||||
uint32_t glfwExtensionCount = 0; |
|
||||||
const char** glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount); |
|
||||||
|
|
||||||
VkInstanceCreateInfo instanceCreateInfo {}; |
|
||||||
instanceCreateInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; |
|
||||||
instanceCreateInfo.pApplicationInfo = &applicationInfo; |
|
||||||
instanceCreateInfo.enabledExtensionCount = glfwExtensionCount; |
|
||||||
instanceCreateInfo.ppEnabledExtensionNames = glfwExtensions; |
|
||||||
#ifdef ENABLE_VALIDATION_LAYERS |
|
||||||
instanceCreateInfo.enabledLayerCount = validationLayers.size(); |
|
||||||
instanceCreateInfo.ppEnabledLayerNames = validationLayers.data(); |
|
||||||
#else |
|
||||||
instanceCreateInfo.enabledLayerCount = 0; |
|
||||||
#endif |
|
||||||
|
|
||||||
if (vkCreateInstance(&instanceCreateInfo, nullptr, &instance) != VK_SUCCESS){ |
|
||||||
throw std::runtime_error("failed to create instance!"); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
VkSurfaceKHR surface = VK_NULL_HANDLE; |
|
||||||
void createSurface(){ |
|
||||||
if (glfwCreateWindowSurface(instance, window, nullptr, &surface) != VK_SUCCESS){ |
|
||||||
throw std::runtime_error("failed to create window surface!"); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; |
|
||||||
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 VkPhysicalDevice &device : devices){ |
|
||||||
if (isDeviceSuitable(device)){ |
|
||||||
physicalDevice = device; |
|
||||||
break; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
if (physicalDevice == VK_NULL_HANDLE){ |
|
||||||
throw std::runtime_error("failed to find a suitable GPU!"); |
|
||||||
} |
|
||||||
|
|
||||||
VkPhysicalDeviceProperties properties; |
|
||||||
vkGetPhysicalDeviceProperties(physicalDevice, &properties); |
|
||||||
printf("Picked physical device: %s\n", properties.deviceName); |
|
||||||
} |
|
||||||
|
|
||||||
struct QueueFamilyIndices { |
|
||||||
std::optional<uint32_t> graphicsFamily; |
|
||||||
std::optional<uint32_t> computeFamily; |
|
||||||
std::optional<uint32_t> presentFamily; |
|
||||||
bool isComplete() const { |
|
||||||
return graphicsFamily.has_value() && computeFamily.has_value() && presentFamily.has_value(); |
|
||||||
} |
|
||||||
std::set<uint32_t> uniqueQueueFamilies(){ |
|
||||||
return {graphicsFamily.value(), presentFamily.value(), computeFamily.value()}; |
|
||||||
} |
|
||||||
}; |
|
||||||
QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device){ |
|
||||||
uint32_t queueFamilyCount = 0; |
|
||||||
vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr); |
|
||||||
|
|
||||||
std::vector<VkQueueFamilyProperties> queueFamilies(queueFamilyCount); |
|
||||||
vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data()); |
|
||||||
|
|
||||||
QueueFamilyIndices indices {}; |
|
||||||
|
|
||||||
uint32_t i = 0; |
|
||||||
for (const VkQueueFamilyProperties& queueFamilyProperties : queueFamilies){ |
|
||||||
if (queueFamilyProperties.queueFlags & VK_QUEUE_GRAPHICS_BIT){ |
|
||||||
indices.graphicsFamily = i; |
|
||||||
} |
|
||||||
if (queueFamilyProperties.queueFlags & VK_QUEUE_COMPUTE_BIT){ |
|
||||||
indices.computeFamily = i; |
|
||||||
} |
|
||||||
VkBool32 presentSupport = false; |
|
||||||
vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, &presentSupport); |
|
||||||
if (presentSupport){ |
|
||||||
indices.presentFamily = i; |
|
||||||
} |
|
||||||
if (indices.isComplete()){ |
|
||||||
break; |
|
||||||
} |
|
||||||
i++; |
|
||||||
} |
|
||||||
return indices; |
|
||||||
} |
|
||||||
|
|
||||||
bool isDeviceSuitable(VkPhysicalDevice device){ |
|
||||||
VkPhysicalDeviceProperties deviceProperties; |
|
||||||
vkGetPhysicalDeviceProperties(device, &deviceProperties); |
|
||||||
|
|
||||||
VkPhysicalDeviceFeatures deviceFeatures; |
|
||||||
vkGetPhysicalDeviceFeatures(device, &deviceFeatures); |
|
||||||
|
|
||||||
QueueFamilyIndices indices = findQueueFamilies(device); |
|
||||||
|
|
||||||
bool extensionsSupported = checkDeviceExtensionSupport(device); |
|
||||||
|
|
||||||
bool swapChainAdequate = false; |
|
||||||
if (extensionsSupported){ |
|
||||||
SwapchainSupportDetails details = querySwapchainSupport(device); |
|
||||||
swapChainAdequate = !details.formats.empty() && !details.presentModes.empty(); |
|
||||||
} |
|
||||||
|
|
||||||
return indices.isComplete() && extensionsSupported && swapChainAdequate; |
|
||||||
} |
|
||||||
|
|
||||||
static bool checkDeviceExtensionSupport(VkPhysicalDevice device){ |
|
||||||
std::set<std::string> required(deviceExtensions.begin(), deviceExtensions.end()); |
|
||||||
|
|
||||||
uint32_t extensionCount; |
|
||||||
vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, nullptr); |
|
||||||
|
|
||||||
std::vector<VkExtensionProperties> availableExtensions(extensionCount); |
|
||||||
vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, availableExtensions.data()); |
|
||||||
|
|
||||||
for (const VkExtensionProperties& extension : availableExtensions){ |
|
||||||
required.erase(extension.extensionName); |
|
||||||
} |
|
||||||
|
|
||||||
return required.empty(); |
|
||||||
} |
|
||||||
|
|
||||||
struct SwapchainSupportDetails { |
|
||||||
VkSurfaceCapabilitiesKHR capabilities {}; |
|
||||||
std::vector<VkSurfaceFormatKHR> formats; |
|
||||||
std::vector<VkPresentModeKHR> presentModes; |
|
||||||
}; |
|
||||||
SwapchainSupportDetails querySwapchainSupport(VkPhysicalDevice device){ |
|
||||||
SwapchainSupportDetails details; |
|
||||||
|
|
||||||
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, surface, &details.capabilities); |
|
||||||
|
|
||||||
uint32_t formatCount; |
|
||||||
vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, nullptr); |
|
||||||
if (formatCount != 0){ |
|
||||||
details.formats.resize(formatCount); |
|
||||||
vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, details.formats.data()); |
|
||||||
} |
|
||||||
|
|
||||||
uint32_t presentModeCount; |
|
||||||
vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, nullptr); |
|
||||||
if (presentModeCount != 0){ |
|
||||||
details.presentModes.resize(presentModeCount); |
|
||||||
vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, details.presentModes.data()); |
|
||||||
} |
|
||||||
|
|
||||||
return details; |
|
||||||
} |
|
||||||
|
|
||||||
VkDevice device = VK_NULL_HANDLE; |
|
||||||
VkQueue graphicsQueue = VK_NULL_HANDLE; |
|
||||||
VkQueue presentQueue = VK_NULL_HANDLE; |
|
||||||
void createLogicalDevice(){ |
|
||||||
QueueFamilyIndices indices = findQueueFamilies(physicalDevice); |
|
||||||
|
|
||||||
std::vector<VkDeviceQueueCreateInfo> queueCreateInfos; |
|
||||||
|
|
||||||
float queuePriority = 1.0f; |
|
||||||
for (uint32_t queueFamily : indices.uniqueQueueFamilies()){ |
|
||||||
VkDeviceQueueCreateInfo queueCreateInfo {}; |
|
||||||
queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; |
|
||||||
queueCreateInfo.queueFamilyIndex = queueFamily; |
|
||||||
queueCreateInfo.queueCount = 1; |
|
||||||
queueCreateInfo.pQueuePriorities = &queuePriority; |
|
||||||
queueCreateInfos.push_back(queueCreateInfo); |
|
||||||
} |
|
||||||
|
|
||||||
VkPhysicalDeviceFeatures deviceFeatures {}; |
|
||||||
|
|
||||||
VkDeviceCreateInfo createInfo {}; |
|
||||||
createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; |
|
||||||
createInfo.pQueueCreateInfos = queueCreateInfos.data(); |
|
||||||
createInfo.queueCreateInfoCount = queueCreateInfos.size(); |
|
||||||
createInfo.pEnabledFeatures = &deviceFeatures; |
|
||||||
createInfo.enabledExtensionCount = deviceExtensions.size(); |
|
||||||
createInfo.ppEnabledExtensionNames = deviceExtensions.data(); |
|
||||||
#ifdef ENABLE_VALIDATION_LAYERS |
|
||||||
createInfo.enabledLayerCount = validationLayers.size(); |
|
||||||
createInfo.ppEnabledLayerNames = validationLayers.data(); |
|
||||||
#else |
|
||||||
createInfo.enabledLayerCount = 0; |
|
||||||
#endif |
|
||||||
|
|
||||||
if (vkCreateDevice(physicalDevice, &createInfo, nullptr, &device) != VK_SUCCESS){ |
|
||||||
throw std::runtime_error("failed to create logical device!"); |
|
||||||
} |
|
||||||
|
|
||||||
vkGetDeviceQueue(device, indices.graphicsFamily.value(), 0, &graphicsQueue); |
|
||||||
vkGetDeviceQueue(device, indices.presentFamily.value(), 0, &presentQueue); |
|
||||||
} |
|
||||||
|
|
||||||
static VkSurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector<VkSurfaceFormatKHR>& availableFormats){ |
|
||||||
for (const auto& availableFormat : availableFormats){ |
|
||||||
if (availableFormat.format == VK_FORMAT_B8G8R8A8_SRGB && availableFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR){ |
|
||||||
return availableFormat; |
|
||||||
} |
|
||||||
} |
|
||||||
return availableFormats[0]; |
|
||||||
} |
|
||||||
|
|
||||||
static VkPresentModeKHR chooseSwapPresentMode(const std::vector<VkPresentModeKHR>& availablePresentModes){ |
|
||||||
return VK_PRESENT_MODE_FIFO_KHR; |
|
||||||
} |
|
||||||
|
|
||||||
VkExtent2D chooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities){ |
|
||||||
if (capabilities.currentExtent.width != std::numeric_limits<uint32_t>::max()){ |
|
||||||
return capabilities.currentExtent; |
|
||||||
} else { |
|
||||||
int width, height; |
|
||||||
glfwGetFramebufferSize(window, &width, &height); |
|
||||||
|
|
||||||
VkExtent2D actualExtent = { |
|
||||||
static_cast<uint32_t>(width), |
|
||||||
static_cast<uint32_t>(height) |
|
||||||
}; |
|
||||||
|
|
||||||
actualExtent.width = std::clamp(actualExtent.width, capabilities.minImageExtent.width, capabilities.maxImageExtent.width); |
|
||||||
actualExtent.height = std::clamp(actualExtent.height, capabilities.minImageExtent.height, capabilities.maxImageExtent.height); |
|
||||||
|
|
||||||
return actualExtent; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
void createSwapchain(){ |
|
||||||
SwapchainSupportDetails swapchainSupport = querySwapchainSupport(physicalDevice); |
|
||||||
|
|
||||||
VkSurfaceFormatKHR surfaceFormat = chooseSwapSurfaceFormat(swapchainSupport.formats); |
|
||||||
VkPresentModeKHR presentMode = chooseSwapPresentMode(swapchainSupport.presentModes); |
|
||||||
VkExtent2D extent = chooseSwapExtent(swapchainSupport.capabilities); |
|
||||||
|
|
||||||
uint32_t imageCount = swapchainSupport.capabilities.minImageCount + 1; |
|
||||||
if (swapchainSupport.capabilities.maxImageCount > 0 && imageCount > swapchainSupport.capabilities.maxImageCount){ |
|
||||||
imageCount = swapchainSupport.capabilities.maxImageCount; |
|
||||||
} |
|
||||||
|
|
||||||
VkSwapchainCreateInfoKHR createInfo {}; |
|
||||||
createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; |
|
||||||
createInfo.surface = surface; |
|
||||||
createInfo.minImageCount = imageCount; |
|
||||||
createInfo.imageFormat = surfaceFormat.format; |
|
||||||
createInfo.imageColorSpace = surfaceFormat.colorSpace; |
|
||||||
createInfo.imageExtent = extent; |
|
||||||
createInfo.imageArrayLayers = 1; |
|
||||||
createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; |
|
||||||
|
|
||||||
QueueFamilyIndices indices = findQueueFamilies(physicalDevice); |
|
||||||
uint32_t queueFamilyIndices[] = {indices.graphicsFamily.value(), indices.presentFamily.value()}; |
|
||||||
|
|
||||||
if (indices.graphicsFamily != indices.presentFamily){ |
|
||||||
createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT; |
|
||||||
createInfo.queueFamilyIndexCount = 2; |
|
||||||
createInfo.pQueueFamilyIndices = queueFamilyIndices; |
|
||||||
} else { |
|
||||||
createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; |
|
||||||
} |
|
||||||
|
|
||||||
createInfo.preTransform = swapchainSupport.capabilities.currentTransform; |
|
||||||
createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; |
|
||||||
createInfo.presentMode = presentMode; |
|
||||||
createInfo.clipped = VK_TRUE; |
|
||||||
createInfo.oldSwapchain = VK_NULL_HANDLE; |
|
||||||
|
|
||||||
if (vkCreateSwapchainKHR(device, &createInfo, nullptr, &swapchain) != VK_SUCCESS){ |
|
||||||
throw std::runtime_error("failed to create swapchain!"); |
|
||||||
} |
|
||||||
|
|
||||||
vkGetSwapchainImagesKHR(device, swapchain, &imageCount, nullptr); |
|
||||||
swapchainImages.resize(imageCount); |
|
||||||
vkGetSwapchainImagesKHR(device, swapchain, &imageCount, swapchainImages.data()); |
|
||||||
|
|
||||||
swapchainImageFormat = surfaceFormat.format; |
|
||||||
swapchainExtent = extent; |
|
||||||
} |
|
||||||
|
|
||||||
void cleanupSwapchain(){ |
|
||||||
for (auto framebuffer : swapchainFramebuffers){ |
|
||||||
vkDestroyFramebuffer(device, framebuffer, nullptr); |
|
||||||
} |
|
||||||
for (auto imageView : swapchainImageViews){ |
|
||||||
vkDestroyImageView(device, imageView, nullptr); |
|
||||||
} |
|
||||||
vkDestroySwapchainKHR(device, swapchain, nullptr); |
|
||||||
} |
|
||||||
|
|
||||||
void recreateSwapchain(){ |
|
||||||
vkDeviceWaitIdle(device); |
|
||||||
|
|
||||||
cleanupSwapchain(); |
|
||||||
|
|
||||||
createSwapchain(); |
|
||||||
createImageViews(); |
|
||||||
createFramebuffers(); |
|
||||||
} |
|
||||||
|
|
||||||
VkSwapchainKHR swapchain = VK_NULL_HANDLE; |
|
||||||
std::vector<VkImage> swapchainImages; |
|
||||||
VkFormat swapchainImageFormat {}; |
|
||||||
VkExtent2D swapchainExtent {}; |
|
||||||
|
|
||||||
std::vector<VkImageView> swapchainImageViews; |
|
||||||
|
|
||||||
void createImageViews(){ |
|
||||||
swapchainImageViews.resize(swapchainImages.size()); |
|
||||||
for (size_t i = 0; i < swapchainImages.size(); i++){ |
|
||||||
VkImageViewCreateInfo createInfo {}; |
|
||||||
createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; |
|
||||||
createInfo.format = swapchainImageFormat; |
|
||||||
createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; |
|
||||||
createInfo.image = swapchainImages[i]; |
|
||||||
createInfo.components = { |
|
||||||
VK_COMPONENT_SWIZZLE_IDENTITY, |
|
||||||
VK_COMPONENT_SWIZZLE_IDENTITY, |
|
||||||
VK_COMPONENT_SWIZZLE_IDENTITY, |
|
||||||
VK_COMPONENT_SWIZZLE_IDENTITY, |
|
||||||
}; |
|
||||||
createInfo.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}; |
|
||||||
if (vkCreateImageView(device, &createInfo, nullptr, &swapchainImageViews[i]) != VK_SUCCESS){ |
|
||||||
throw std::runtime_error("failed to create image views!"); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
VkShaderModule createShaderModule(const std::vector<char> &code){ |
|
||||||
VkShaderModuleCreateInfo createInfo {}; |
|
||||||
createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; |
|
||||||
createInfo.codeSize = code.size(); |
|
||||||
createInfo.pCode = reinterpret_cast<const uint32_t *>(code.data()); |
|
||||||
|
|
||||||
VkShaderModule shaderModule; |
|
||||||
if (vkCreateShaderModule(device, &createInfo, nullptr, &shaderModule) != VK_SUCCESS){ |
|
||||||
throw std::runtime_error("failed to create shader module!"); |
|
||||||
} |
|
||||||
|
|
||||||
return shaderModule; |
|
||||||
} |
|
||||||
|
|
||||||
void createRenderPass(){ |
|
||||||
VkAttachmentDescription colorAttachment {}; |
|
||||||
colorAttachment.format = swapchainImageFormat; |
|
||||||
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; |
|
||||||
|
|
||||||
vkCreateRenderPass(device, &renderPassInfo, nullptr, &renderPass); |
|
||||||
} |
|
||||||
|
|
||||||
VkRenderPass renderPass = VK_NULL_HANDLE; |
|
||||||
VkPipelineLayout pipelineLayout = VK_NULL_HANDLE; |
|
||||||
|
|
||||||
void createGraphicsPipeline(){ |
|
||||||
auto vertShaderCode = readFile("shaders/vert.spv"); |
|
||||||
auto fragShaderCode = readFile("shaders/frag.spv"); |
|
||||||
|
|
||||||
VkShaderModule vertShaderModule = createShaderModule(vertShaderCode); |
|
||||||
VkShaderModule fragShaderModule = createShaderModule(fragShaderCode); |
|
||||||
|
|
||||||
|
|
||||||
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}; |
|
||||||
|
|
||||||
auto bindingDescription = Vertex::getBindingDescription(); |
|
||||||
auto attributeDescriptions = Vertex::getAttributeDescriptions(); |
|
||||||
|
|
||||||
VkPipelineVertexInputStateCreateInfo vertexInputInfo {}; |
|
||||||
vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; |
|
||||||
vertexInputInfo.vertexBindingDescriptionCount = 1; |
|
||||||
vertexInputInfo.pVertexBindingDescriptions = &bindingDescription; |
|
||||||
vertexInputInfo.vertexAttributeDescriptionCount = attributeDescriptions.size(); |
|
||||||
vertexInputInfo.pVertexAttributeDescriptions = attributeDescriptions.data(); |
|
||||||
|
|
||||||
VkPipelineInputAssemblyStateCreateInfo inputAssembly {}; |
|
||||||
inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; |
|
||||||
inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; |
|
||||||
inputAssembly.primitiveRestartEnable = VK_FALSE; |
|
||||||
|
|
||||||
std::vector<VkDynamicState> dynamicStates = { |
|
||||||
VK_DYNAMIC_STATE_VIEWPORT, |
|
||||||
VK_DYNAMIC_STATE_SCISSOR |
|
||||||
}; |
|
||||||
|
|
||||||
VkPipelineDynamicStateCreateInfo dynamicState {}; |
|
||||||
dynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; |
|
||||||
dynamicState.dynamicStateCount = dynamicStates.size(); |
|
||||||
dynamicState.pDynamicStates = dynamicStates.data(); |
|
||||||
|
|
||||||
VkPipelineViewportStateCreateInfo viewportState {}; |
|
||||||
viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; |
|
||||||
viewportState.viewportCount = 1; |
|
||||||
viewportState.scissorCount = 1; |
|
||||||
|
|
||||||
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; |
|
||||||
rasterizer.cullMode = VK_CULL_MODE_BACK_BIT; |
|
||||||
rasterizer.frontFace = VK_FRONT_FACE_CLOCKWISE; |
|
||||||
rasterizer.depthBiasClamp = VK_FALSE; |
|
||||||
|
|
||||||
VkPipelineMultisampleStateCreateInfo multisample {}; |
|
||||||
multisample.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; |
|
||||||
multisample.sampleShadingEnable = VK_FALSE; |
|
||||||
multisample.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.attachmentCount = 1; |
|
||||||
colorBlending.pAttachments = &colorBlendAttachment; |
|
||||||
|
|
||||||
VkPipelineLayoutCreateInfo pipelineLayoutInfo {}; |
|
||||||
pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; |
|
||||||
|
|
||||||
vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, &pipelineLayout); |
|
||||||
|
|
||||||
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 = &multisample; |
|
||||||
pipelineInfo.pColorBlendState = &colorBlending; |
|
||||||
pipelineInfo.pDynamicState = &dynamicState; |
|
||||||
|
|
||||||
pipelineInfo.layout = pipelineLayout; |
|
||||||
|
|
||||||
pipelineInfo.renderPass = renderPass; |
|
||||||
pipelineInfo.subpass = 0; |
|
||||||
} |
|
||||||
|
|
||||||
vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &graphicsPipeline); |
|
||||||
|
|
||||||
vkDestroyShaderModule(device, vertShaderModule, nullptr); |
|
||||||
vkDestroyShaderModule(device, fragShaderModule, nullptr); |
|
||||||
} |
|
||||||
|
|
||||||
VkPipeline graphicsPipeline = VK_NULL_HANDLE; |
|
||||||
|
|
||||||
std::vector<VkFramebuffer> swapchainFramebuffers; |
|
||||||
|
|
||||||
void createFramebuffers(){ |
|
||||||
swapchainFramebuffers.resize(swapchainImageViews.size()); |
|
||||||
for (size_t i = 0; i < swapchainImageViews.size(); i++){ |
|
||||||
VkImageView attachments[] = {swapchainImageViews[i]}; |
|
||||||
|
|
||||||
VkFramebufferCreateInfo framebufferInfo {}; |
|
||||||
framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; |
|
||||||
framebufferInfo.renderPass = renderPass; |
|
||||||
framebufferInfo.attachmentCount = 1; |
|
||||||
framebufferInfo.pAttachments = attachments; |
|
||||||
framebufferInfo.width = swapchainExtent.width; |
|
||||||
framebufferInfo.height = swapchainExtent.height; |
|
||||||
framebufferInfo.layers = 1; |
|
||||||
|
|
||||||
vkCreateFramebuffer(device, &framebufferInfo, nullptr, &swapchainFramebuffers[i]); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
VkBuffer vertexBuffer = VK_NULL_HANDLE; |
|
||||||
VkDeviceMemory vertexBufferMemory = VK_NULL_HANDLE; |
|
||||||
|
|
||||||
uint32_t findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags propertyFlags){ |
|
||||||
VkPhysicalDeviceMemoryProperties memoryProperties; |
|
||||||
vkGetPhysicalDeviceMemoryProperties(physicalDevice, &memoryProperties); |
|
||||||
|
|
||||||
for (uint32_t type = 0; type < memoryProperties.memoryTypeCount; type++){ |
|
||||||
if ((typeFilter & (1 << type)) && (memoryProperties.memoryTypes[type].propertyFlags & propertyFlags)){ |
|
||||||
return type; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
throw std::runtime_error("failed to find suitable memory type!"); |
|
||||||
} |
|
||||||
|
|
||||||
void createVertexBuffer(){ |
|
||||||
VkBufferCreateInfo bufferInfo{}; |
|
||||||
bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; |
|
||||||
bufferInfo.size = vertices.size() * sizeof(Vertex); |
|
||||||
bufferInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; |
|
||||||
bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; |
|
||||||
|
|
||||||
vkCreateBuffer(device, &bufferInfo, nullptr, &vertexBuffer); |
|
||||||
|
|
||||||
VkMemoryRequirements memoryRequirements; |
|
||||||
vkGetBufferMemoryRequirements(device, vertexBuffer, &memoryRequirements); |
|
||||||
|
|
||||||
VkMemoryAllocateInfo allocateInfo {}; |
|
||||||
allocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; |
|
||||||
allocateInfo.allocationSize = memoryRequirements.size; |
|
||||||
allocateInfo.memoryTypeIndex = findMemoryType(memoryRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); |
|
||||||
|
|
||||||
vkAllocateMemory(device, &allocateInfo, nullptr, &vertexBufferMemory); |
|
||||||
|
|
||||||
vkBindBufferMemory(device, vertexBuffer, vertexBufferMemory, 0); |
|
||||||
|
|
||||||
void* data; |
|
||||||
vkMapMemory(device, vertexBufferMemory, 0, bufferInfo.size, 0, &data); |
|
||||||
memcpy(data, vertices.data(), bufferInfo.size); |
|
||||||
vkUnmapMemory(device, vertexBufferMemory); |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
VkCommandPool commandPool = VK_NULL_HANDLE; |
|
||||||
|
|
||||||
std::vector<VkCommandBuffer> commandBuffers; |
|
||||||
|
|
||||||
void createCommandPool(){ |
|
||||||
QueueFamilyIndices indices = findQueueFamilies(physicalDevice); |
|
||||||
|
|
||||||
VkCommandPoolCreateInfo poolInfo {}; |
|
||||||
poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; |
|
||||||
poolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; |
|
||||||
poolInfo.queueFamilyIndex = indices.graphicsFamily.value(); |
|
||||||
|
|
||||||
vkCreateCommandPool(device, &poolInfo, nullptr, &commandPool); |
|
||||||
} |
|
||||||
|
|
||||||
void createCommandBuffers(){ |
|
||||||
commandBuffers.resize(MAX_FRAMES_IN_FLIGHT); |
|
||||||
|
|
||||||
VkCommandBufferAllocateInfo allocateInfo {}; |
|
||||||
allocateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; |
|
||||||
allocateInfo.commandPool = commandPool; |
|
||||||
allocateInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; |
|
||||||
allocateInfo.commandBufferCount = commandBuffers.size(); |
|
||||||
|
|
||||||
vkAllocateCommandBuffers(device, &allocateInfo, commandBuffers.data()); |
|
||||||
} |
|
||||||
|
|
||||||
void recordCommandBuffer(VkCommandBuffer commandBuffer, uint32_t imageIndex){ |
|
||||||
VkCommandBufferBeginInfo beginInfo {}; |
|
||||||
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; |
|
||||||
|
|
||||||
vkBeginCommandBuffer(commandBuffer, &beginInfo); |
|
||||||
|
|
||||||
VkRenderPassBeginInfo renderPassInfo {}; |
|
||||||
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; |
|
||||||
renderPassInfo.renderPass = renderPass; |
|
||||||
renderPassInfo.framebuffer = swapchainFramebuffers[imageIndex]; |
|
||||||
renderPassInfo.renderArea.offset = {0, 0}; |
|
||||||
renderPassInfo.renderArea.extent = swapchainExtent; |
|
||||||
|
|
||||||
VkClearValue clearColor = {{{0, 0, 0, 1}}}; |
|
||||||
renderPassInfo.clearValueCount = 1; |
|
||||||
renderPassInfo.pClearValues = &clearColor; |
|
||||||
|
|
||||||
vkCmdBeginRenderPass(commandBuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE); |
|
||||||
vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipeline); |
|
||||||
|
|
||||||
VkViewport viewport {}; |
|
||||||
viewport.x = 0; |
|
||||||
viewport.y = 0; |
|
||||||
viewport.width = static_cast<float>(swapchainExtent.width); |
|
||||||
viewport.height = static_cast<float>(swapchainExtent.height); |
|
||||||
viewport.minDepth = 0; |
|
||||||
viewport.maxDepth = 1; |
|
||||||
vkCmdSetViewport(commandBuffer, 0, 1, &viewport); |
|
||||||
|
|
||||||
VkRect2D scissor {}; |
|
||||||
scissor.offset = {0, 0}; |
|
||||||
scissor.extent = swapchainExtent; |
|
||||||
vkCmdSetScissor(commandBuffer, 0, 1, &scissor); |
|
||||||
|
|
||||||
VkBuffer buffers[] = {vertexBuffer}; |
|
||||||
VkDeviceSize offsets[] = {0}; |
|
||||||
vkCmdBindVertexBuffers(commandBuffer, 0, 1, buffers, offsets); |
|
||||||
|
|
||||||
vkCmdDraw(commandBuffer, vertices.size(), 1, 0, 0); |
|
||||||
|
|
||||||
vkCmdEndRenderPass(commandBuffer); |
|
||||||
vkEndCommandBuffer(commandBuffer); |
|
||||||
} |
|
||||||
|
|
||||||
std::vector<VkSemaphore> imageAvailableSemaphores; |
|
||||||
std::vector<VkSemaphore> renderFinishedSemaphores; |
|
||||||
std::vector<VkFence> inFlightFences; |
|
||||||
|
|
||||||
void createSyncObjects(){ |
|
||||||
imageAvailableSemaphores.resize(MAX_FRAMES_IN_FLIGHT); |
|
||||||
renderFinishedSemaphores.resize(MAX_FRAMES_IN_FLIGHT); |
|
||||||
inFlightFences.resize(MAX_FRAMES_IN_FLIGHT); |
|
||||||
|
|
||||||
VkSemaphoreCreateInfo semaphoreInfo {}; |
|
||||||
semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; |
|
||||||
|
|
||||||
VkFenceCreateInfo fenceInfo {}; |
|
||||||
fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; |
|
||||||
fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT; |
|
||||||
|
|
||||||
for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++){ |
|
||||||
vkCreateSemaphore(device, &semaphoreInfo, nullptr, &imageAvailableSemaphores[i]); |
|
||||||
vkCreateSemaphore(device, &semaphoreInfo, nullptr, &renderFinishedSemaphores[i]); |
|
||||||
vkCreateFence(device, &fenceInfo, nullptr, &inFlightFences[i]); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
size_t currentFrame = 0; |
|
||||||
|
|
||||||
void drawFrame(){ |
|
||||||
vkWaitForFences(device, 1, &inFlightFences[currentFrame], VK_TRUE, UINT64_MAX); |
|
||||||
|
|
||||||
uint32_t imageIndex; |
|
||||||
VkResult result = vkAcquireNextImageKHR(device, swapchain, UINT64_MAX, imageAvailableSemaphores[currentFrame], VK_NULL_HANDLE, &imageIndex); |
|
||||||
if (result == VK_ERROR_OUT_OF_DATE_KHR){ |
|
||||||
recreateSwapchain(); |
|
||||||
return; |
|
||||||
} |
|
||||||
|
|
||||||
vkResetFences(device, 1, &inFlightFences[currentFrame]); |
|
||||||
|
|
||||||
vkResetCommandBuffer(commandBuffers[currentFrame], 0); |
|
||||||
recordCommandBuffer(commandBuffers[currentFrame], imageIndex); |
|
||||||
|
|
||||||
VkSubmitInfo submitInfo {}; |
|
||||||
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; |
|
||||||
|
|
||||||
VkSemaphore waitSemaphores[] = {imageAvailableSemaphores[currentFrame]}; |
|
||||||
VkPipelineStageFlags waitStages[] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT}; |
|
||||||
submitInfo.waitSemaphoreCount = 1; |
|
||||||
submitInfo.pWaitSemaphores = waitSemaphores; |
|
||||||
submitInfo.pWaitDstStageMask = waitStages; |
|
||||||
submitInfo.commandBufferCount = 1; |
|
||||||
submitInfo.pCommandBuffers = &commandBuffers[currentFrame]; |
|
||||||
|
|
||||||
VkSemaphore signalSemaphores[] = {renderFinishedSemaphores[currentFrame]}; |
|
||||||
submitInfo.signalSemaphoreCount = 1; |
|
||||||
submitInfo.pSignalSemaphores = signalSemaphores; |
|
||||||
|
|
||||||
vkQueueSubmit(graphicsQueue, 1, &submitInfo, inFlightFences[currentFrame]); |
|
||||||
|
|
||||||
VkPresentInfoKHR presentInfo {}; |
|
||||||
presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; |
|
||||||
presentInfo.waitSemaphoreCount = 1; |
|
||||||
presentInfo.pWaitSemaphores = signalSemaphores; |
|
||||||
|
|
||||||
VkSwapchainKHR swapchains[] = {swapchain}; |
|
||||||
presentInfo.swapchainCount = 1; |
|
||||||
presentInfo.pSwapchains = swapchains; |
|
||||||
presentInfo.pImageIndices = &imageIndex; |
|
||||||
|
|
||||||
result = vkQueuePresentKHR(presentQueue, &presentInfo); |
|
||||||
if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR){ |
|
||||||
recreateSwapchain(); |
|
||||||
return; |
|
||||||
} |
|
||||||
|
|
||||||
currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT; |
|
||||||
} |
|
||||||
|
|
||||||
void mainLoop(){ |
|
||||||
while (!glfwWindowShouldClose(window)){ |
|
||||||
glfwPollEvents(); |
|
||||||
drawFrame(); |
|
||||||
} |
|
||||||
|
|
||||||
vkDeviceWaitIdle(device); |
|
||||||
} |
|
||||||
void cleanup(){ |
|
||||||
cleanupSwapchain(); |
|
||||||
vkDestroyBuffer(device, vertexBuffer, nullptr); |
|
||||||
vkFreeMemory(device, vertexBufferMemory, nullptr); |
|
||||||
for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++){ |
|
||||||
vkDestroySemaphore(device, imageAvailableSemaphores[i], nullptr); |
|
||||||
vkDestroySemaphore(device, renderFinishedSemaphores[i], nullptr); |
|
||||||
vkDestroyFence(device, inFlightFences[i], nullptr); |
|
||||||
} |
|
||||||
vkDestroyCommandPool(device, commandPool, nullptr); |
|
||||||
vkDestroyPipeline(device, graphicsPipeline, nullptr); |
|
||||||
vkDestroyPipelineLayout(device, pipelineLayout, nullptr); |
|
||||||
vkDestroyRenderPass(device, renderPass, nullptr); |
|
||||||
vkDestroyDevice(device, nullptr); |
|
||||||
vkDestroySurfaceKHR(instance, surface, nullptr); |
|
||||||
vkDestroyInstance(instance, nullptr); |
|
||||||
|
|
||||||
glfwDestroyWindow(window); |
|
||||||
glfwTerminate(); |
|
||||||
} |
|
||||||
}; |
|
||||||
|
|
||||||
int main() { |
int main() { |
||||||
|
Application application; |
||||||
MyApp app; |
|
||||||
try { |
|
||||||
app.run(); |
|
||||||
} catch (const std::exception& e){ |
|
||||||
std::cerr << e.what() << "\n"; |
|
||||||
return EXIT_FAILURE; |
|
||||||
} |
|
||||||
|
|
||||||
return EXIT_SUCCESS; |
|
||||||
} |
} |
||||||
|
@ -1,2 +0,0 @@ |
|||||||
#include "pipeline.hpp" |
|
||||||
|
|
@ -0,0 +1,107 @@ |
|||||||
|
#include "application.hpp" |
||||||
|
|
||||||
|
Application::Application() { |
||||||
|
instance = new Instance; |
||||||
|
swapchain = new Swapchain(instance); |
||||||
|
pipeline = new Pipeline(instance, swapchain->renderPass); |
||||||
|
buffer = new Buffer(instance); |
||||||
|
commandPool = new CommandPool(instance); |
||||||
|
createSyncObjects(); |
||||||
|
|
||||||
|
mainLoop(); |
||||||
|
} |
||||||
|
|
||||||
|
void Application::mainLoop() { |
||||||
|
while (!glfwWindowShouldClose(instance->window)){ |
||||||
|
glfwPollEvents(); |
||||||
|
drawFrame(); |
||||||
|
} |
||||||
|
vkDeviceWaitIdle(instance->device); |
||||||
|
} |
||||||
|
|
||||||
|
void Application::drawFrame() { |
||||||
|
vkWaitForFences(instance->device, 1, &inFlightFences[currentFrame], VK_TRUE, UINT64_MAX); |
||||||
|
|
||||||
|
uint32_t imageIndex; |
||||||
|
VkResult result = vkAcquireNextImageKHR(instance->device, swapchain->handle, UINT64_MAX, imageAvailableSemaphores[currentFrame], VK_NULL_HANDLE, &imageIndex); |
||||||
|
if (result == VK_ERROR_OUT_OF_DATE_KHR){ |
||||||
|
swapchain->recreateSwapchain(); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
vkResetFences(instance->device, 1, &inFlightFences[currentFrame]); |
||||||
|
|
||||||
|
vkResetCommandBuffer(commandPool->buffers[currentFrame], 0); |
||||||
|
recordCommandBuffer(commandPool->buffers[currentFrame], imageIndex); |
||||||
|
|
||||||
|
VkSubmitInfo submitInfo {}; |
||||||
|
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; |
||||||
|
|
||||||
|
VkSemaphore waitSemaphores[] = {imageAvailableSemaphores[currentFrame]}; |
||||||
|
VkPipelineStageFlags waitStages[] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT}; |
||||||
|
submitInfo.waitSemaphoreCount = 1; |
||||||
|
submitInfo.pWaitSemaphores = waitSemaphores; |
||||||
|
submitInfo.pWaitDstStageMask = waitStages; |
||||||
|
submitInfo.commandBufferCount = 1; |
||||||
|
submitInfo.pCommandBuffers = &commandPool->buffers[currentFrame]; |
||||||
|
|
||||||
|
VkSemaphore signalSemaphores[] = {renderFinishedSemaphores[currentFrame]}; |
||||||
|
submitInfo.signalSemaphoreCount = 1; |
||||||
|
submitInfo.pSignalSemaphores = signalSemaphores; |
||||||
|
|
||||||
|
vkQueueSubmit(instance->graphicsQueue, 1, &submitInfo, inFlightFences[currentFrame]); |
||||||
|
|
||||||
|
VkPresentInfoKHR presentInfo {}; |
||||||
|
presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; |
||||||
|
presentInfo.waitSemaphoreCount = 1; |
||||||
|
presentInfo.pWaitSemaphores = signalSemaphores; |
||||||
|
|
||||||
|
VkSwapchainKHR swapchains[] = {swapchain->handle}; |
||||||
|
presentInfo.swapchainCount = 1; |
||||||
|
presentInfo.pSwapchains = swapchains; |
||||||
|
presentInfo.pImageIndices = &imageIndex; |
||||||
|
|
||||||
|
result = vkQueuePresentKHR(instance->presentQueue, &presentInfo); |
||||||
|
if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR){ |
||||||
|
swapchain->recreateSwapchain(); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT; |
||||||
|
} |
||||||
|
|
||||||
|
void Application::createSyncObjects() { |
||||||
|
imageAvailableSemaphores.resize(MAX_FRAMES_IN_FLIGHT); |
||||||
|
renderFinishedSemaphores.resize(MAX_FRAMES_IN_FLIGHT); |
||||||
|
inFlightFences.resize(MAX_FRAMES_IN_FLIGHT); |
||||||
|
|
||||||
|
VkSemaphoreCreateInfo semaphoreInfo {}; |
||||||
|
semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; |
||||||
|
|
||||||
|
VkFenceCreateInfo fenceInfo {}; |
||||||
|
fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; |
||||||
|
fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT; |
||||||
|
|
||||||
|
for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++){ |
||||||
|
vkCreateSemaphore(instance->device, &semaphoreInfo, nullptr, &imageAvailableSemaphores[i]); |
||||||
|
vkCreateSemaphore(instance->device, &semaphoreInfo, nullptr, &renderFinishedSemaphores[i]); |
||||||
|
vkCreateFence(instance->device, &fenceInfo, nullptr, &inFlightFences[i]); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Application::~Application() { |
||||||
|
delete swapchain; |
||||||
|
for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++){ |
||||||
|
vkDestroySemaphore(instance->device, imageAvailableSemaphores[i], nullptr); |
||||||
|
vkDestroySemaphore(instance->device, renderFinishedSemaphores[i], nullptr); |
||||||
|
vkDestroyFence(instance->device, inFlightFences[i], nullptr); |
||||||
|
} |
||||||
|
delete commandPool; |
||||||
|
delete buffer; |
||||||
|
delete pipeline; |
||||||
|
delete instance; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,103 @@ |
|||||||
|
#pragma once |
||||||
|
|
||||||
|
#include <GLFW/glfw3.h> |
||||||
|
|
||||||
|
#include <glm/glm.hpp> |
||||||
|
|
||||||
|
#include <iostream> |
||||||
|
#include <vector> |
||||||
|
#include <cstring> |
||||||
|
#include <optional> |
||||||
|
#include <set> |
||||||
|
#include <fstream> |
||||||
|
#include <numeric> |
||||||
|
#include <chrono> |
||||||
|
#include <array> |
||||||
|
#include "vertex.hpp" |
||||||
|
#include "swapchain.hpp" |
||||||
|
#include "pipeline.hpp" |
||||||
|
#include "instance.hpp" |
||||||
|
#include "buffer.hpp" |
||||||
|
#include "command_pool.hpp" |
||||||
|
|
||||||
|
constexpr int MAX_FRAMES_IN_FLIGHT = 2; |
||||||
|
|
||||||
|
class Timer { |
||||||
|
public: |
||||||
|
explicit Timer(){ |
||||||
|
start = std::chrono::system_clock::now(); |
||||||
|
} |
||||||
|
~Timer(){ |
||||||
|
size_t nanoseconds = (std::chrono::system_clock::now() - start).count(); |
||||||
|
printf("Timer: %zu mus\n", nanoseconds / 1000); |
||||||
|
} |
||||||
|
private: |
||||||
|
std::chrono::time_point<std::chrono::system_clock> start; |
||||||
|
}; |
||||||
|
|
||||||
|
class Application { |
||||||
|
public: |
||||||
|
explicit Application(); |
||||||
|
~Application(); |
||||||
|
private: |
||||||
|
Instance* instance = nullptr; |
||||||
|
Swapchain* swapchain = nullptr; |
||||||
|
Pipeline* pipeline = nullptr; |
||||||
|
Buffer* buffer = nullptr; |
||||||
|
CommandPool* commandPool = nullptr; |
||||||
|
|
||||||
|
void recordCommandBuffer(VkCommandBuffer commandBuffer, uint32_t imageIndex){ |
||||||
|
VkCommandBufferBeginInfo beginInfo {}; |
||||||
|
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; |
||||||
|
|
||||||
|
vkBeginCommandBuffer(commandBuffer, &beginInfo); |
||||||
|
|
||||||
|
VkRenderPassBeginInfo renderPassInfo {}; |
||||||
|
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; |
||||||
|
renderPassInfo.renderPass = swapchain->renderPass; |
||||||
|
renderPassInfo.framebuffer = swapchain->frameBuffers[imageIndex]; |
||||||
|
renderPassInfo.renderArea.offset = {0, 0}; |
||||||
|
renderPassInfo.renderArea.extent = swapchain->extent; |
||||||
|
|
||||||
|
VkClearValue clearColor = {{{0, 0, 0, 1}}}; |
||||||
|
renderPassInfo.clearValueCount = 1; |
||||||
|
renderPassInfo.pClearValues = &clearColor; |
||||||
|
|
||||||
|
vkCmdBeginRenderPass(commandBuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE); |
||||||
|
vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline->graphicsPipeline); |
||||||
|
|
||||||
|
VkViewport viewport {}; |
||||||
|
viewport.x = 0; |
||||||
|
viewport.y = 0; |
||||||
|
viewport.width = static_cast<float>(swapchain->extent.width); |
||||||
|
viewport.height = static_cast<float>(swapchain->extent.height); |
||||||
|
viewport.minDepth = 0; |
||||||
|
viewport.maxDepth = 1; |
||||||
|
vkCmdSetViewport(commandBuffer, 0, 1, &viewport); |
||||||
|
|
||||||
|
VkRect2D scissor {}; |
||||||
|
scissor.offset = {0, 0}; |
||||||
|
scissor.extent = swapchain->extent; |
||||||
|
vkCmdSetScissor(commandBuffer, 0, 1, &scissor); |
||||||
|
|
||||||
|
VkBuffer buffers[] = {buffer->vertexHandle}; |
||||||
|
VkDeviceSize offsets[] = {0}; |
||||||
|
vkCmdBindVertexBuffers(commandBuffer, 0, 1, buffers, offsets); |
||||||
|
|
||||||
|
vkCmdDraw(commandBuffer, 3, 1, 0, 0); |
||||||
|
|
||||||
|
vkCmdEndRenderPass(commandBuffer); |
||||||
|
vkEndCommandBuffer(commandBuffer); |
||||||
|
} |
||||||
|
|
||||||
|
std::vector<VkSemaphore> imageAvailableSemaphores; |
||||||
|
std::vector<VkSemaphore> renderFinishedSemaphores; |
||||||
|
std::vector<VkFence> inFlightFences; |
||||||
|
|
||||||
|
size_t currentFrame = 0; |
||||||
|
|
||||||
|
void drawFrame(); |
||||||
|
void createSyncObjects(); |
||||||
|
|
||||||
|
void mainLoop(); |
||||||
|
}; |
@ -0,0 +1,55 @@ |
|||||||
|
#include <vector> |
||||||
|
#include <cstring> |
||||||
|
#include "buffer.hpp" |
||||||
|
#include "vertex.hpp" |
||||||
|
|
||||||
|
const std::vector<Vertex> vertices = { |
||||||
|
{{0.0, -0.5}, {1, 0, 0}}, |
||||||
|
{{0.5, 0.5}, {0, 1, 0}}, |
||||||
|
{{-0.5, 0.5}, {0, 0, 1}} |
||||||
|
}; |
||||||
|
|
||||||
|
Buffer::Buffer(Instance* instance) : instance(instance) { |
||||||
|
VkBufferCreateInfo bufferInfo{}; |
||||||
|
bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; |
||||||
|
bufferInfo.size = vertices.size() * sizeof(Vertex); |
||||||
|
bufferInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; |
||||||
|
bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; |
||||||
|
|
||||||
|
vkCreateBuffer(instance->device, &bufferInfo, nullptr, &vertexHandle); |
||||||
|
|
||||||
|
VkMemoryRequirements memoryRequirements; |
||||||
|
vkGetBufferMemoryRequirements(instance->device, vertexHandle, &memoryRequirements); |
||||||
|
|
||||||
|
VkMemoryAllocateInfo allocateInfo {}; |
||||||
|
allocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; |
||||||
|
allocateInfo.allocationSize = memoryRequirements.size; |
||||||
|
allocateInfo.memoryTypeIndex = findMemoryType(memoryRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); |
||||||
|
|
||||||
|
vkAllocateMemory(instance->device, &allocateInfo, nullptr, &vertexMemory); |
||||||
|
|
||||||
|
vkBindBufferMemory(instance->device, vertexHandle, vertexMemory, 0); |
||||||
|
|
||||||
|
void* data; |
||||||
|
vkMapMemory(instance->device, vertexMemory, 0, bufferInfo.size, 0, &data); |
||||||
|
memcpy(data, vertices.data(), bufferInfo.size); |
||||||
|
vkUnmapMemory(instance->device, vertexMemory); |
||||||
|
} |
||||||
|
|
||||||
|
uint32_t Buffer::findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags propertyFlags) { |
||||||
|
VkPhysicalDeviceMemoryProperties memoryProperties; |
||||||
|
vkGetPhysicalDeviceMemoryProperties(instance->physicalDevice, &memoryProperties); |
||||||
|
|
||||||
|
for (uint32_t type = 0; type < memoryProperties.memoryTypeCount; type++){ |
||||||
|
if ((typeFilter & (1 << type)) && (memoryProperties.memoryTypes[type].propertyFlags & propertyFlags)){ |
||||||
|
return type; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
throw std::runtime_error("failed to find suitable memory type!"); |
||||||
|
} |
||||||
|
|
||||||
|
Buffer::~Buffer() { |
||||||
|
vkFreeMemory(instance->device, vertexMemory, nullptr); |
||||||
|
vkDestroyBuffer(instance->device, vertexHandle, nullptr); |
||||||
|
} |
@ -0,0 +1,19 @@ |
|||||||
|
#pragma once |
||||||
|
|
||||||
|
#include <vulkan/vulkan_core.h> |
||||||
|
#include <stdexcept> |
||||||
|
#include "instance.hpp" |
||||||
|
|
||||||
|
|
||||||
|
class Buffer { |
||||||
|
public: |
||||||
|
explicit Buffer(Instance* instance); |
||||||
|
~Buffer(); |
||||||
|
VkBuffer vertexHandle = VK_NULL_HANDLE; |
||||||
|
|
||||||
|
private: |
||||||
|
Instance* instance = nullptr; |
||||||
|
VkDeviceMemory vertexMemory = VK_NULL_HANDLE; |
||||||
|
|
||||||
|
uint32_t findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags propertyFlags); |
||||||
|
}; |
@ -0,0 +1,32 @@ |
|||||||
|
#include "command_pool.hpp" |
||||||
|
#include "application.hpp" |
||||||
|
|
||||||
|
CommandPool::CommandPool(Instance *instance) : instance(instance) { |
||||||
|
Instance::QueueFamilyIndices indices = Instance::findQueueFamilies(instance->physicalDevice, instance->surface); |
||||||
|
|
||||||
|
VkCommandPoolCreateInfo poolInfo {}; |
||||||
|
poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; |
||||||
|
poolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; |
||||||
|
poolInfo.queueFamilyIndex = indices.graphicsFamily.value(); |
||||||
|
|
||||||
|
vkCreateCommandPool(instance->device, &poolInfo, nullptr, &handle); |
||||||
|
|
||||||
|
createBuffers(); |
||||||
|
} |
||||||
|
|
||||||
|
void CommandPool::createBuffers() { |
||||||
|
buffers.resize(MAX_FRAMES_IN_FLIGHT); |
||||||
|
|
||||||
|
VkCommandBufferAllocateInfo allocateInfo {}; |
||||||
|
allocateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; |
||||||
|
allocateInfo.commandPool = handle; |
||||||
|
allocateInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; |
||||||
|
allocateInfo.commandBufferCount = buffers.size(); |
||||||
|
|
||||||
|
vkAllocateCommandBuffers(instance->device, &allocateInfo, buffers.data()); |
||||||
|
} |
||||||
|
|
||||||
|
CommandPool::~CommandPool() { |
||||||
|
vkFreeCommandBuffers(instance->device, handle, buffers.size(), buffers.data()); |
||||||
|
vkDestroyCommandPool(instance->device, handle, nullptr); |
||||||
|
} |
@ -0,0 +1,16 @@ |
|||||||
|
#pragma once |
||||||
|
|
||||||
|
#include <vector> |
||||||
|
#include "instance.hpp" |
||||||
|
|
||||||
|
class CommandPool { |
||||||
|
public: |
||||||
|
explicit CommandPool(Instance* instance); |
||||||
|
~CommandPool(); |
||||||
|
std::vector<VkCommandBuffer> buffers; |
||||||
|
private: |
||||||
|
Instance* instance; |
||||||
|
VkCommandPool handle = VK_NULL_HANDLE; |
||||||
|
|
||||||
|
void createBuffers(); |
||||||
|
}; |
@ -0,0 +1,237 @@ |
|||||||
|
#include <stdexcept> |
||||||
|
#include <vector> |
||||||
|
#include "instance.hpp" |
||||||
|
#include "swapchain.hpp" |
||||||
|
#include <cstring> |
||||||
|
|
||||||
|
const std::vector<const char*> deviceExtensions = { |
||||||
|
VK_KHR_SWAPCHAIN_EXTENSION_NAME |
||||||
|
}; |
||||||
|
|
||||||
|
#ifndef NDEBUG |
||||||
|
#define ENABLE_VALIDATION_LAYERS |
||||||
|
#endif |
||||||
|
|
||||||
|
#ifdef ENABLE_VALIDATION_LAYERS |
||||||
|
const std::vector<const char*> validationLayers = { |
||||||
|
"VK_LAYER_KHRONOS_validation" |
||||||
|
}; |
||||||
|
bool checkValidationLayerSupport(); |
||||||
|
#endif |
||||||
|
|
||||||
|
#ifdef ENABLE_VALIDATION_LAYERS |
||||||
|
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; |
||||||
|
} |
||||||
|
#endif |
||||||
|
|
||||||
|
Instance::Instance() { |
||||||
|
initWindow(); |
||||||
|
|
||||||
|
createInstance(); |
||||||
|
createSurface(); |
||||||
|
pickPhysicalDevice(); |
||||||
|
createLogicalDevice(); |
||||||
|
} |
||||||
|
|
||||||
|
void Instance::initWindow() { |
||||||
|
glfwInit(); |
||||||
|
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); |
||||||
|
glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); |
||||||
|
window = glfwCreateWindow(800, 600, "Vulkan Simulation", nullptr, nullptr); |
||||||
|
} |
||||||
|
|
||||||
|
void Instance::createInstance() { |
||||||
|
#ifdef ENABLE_VALIDATION_LAYERS |
||||||
|
if (!checkValidationLayerSupport()){ |
||||||
|
throw std::runtime_error("validation layers requested, but not available!"); |
||||||
|
} |
||||||
|
#endif |
||||||
|
|
||||||
|
VkApplicationInfo applicationInfo {}; |
||||||
|
applicationInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; |
||||||
|
applicationInfo.pApplicationName = "Coole Simulation"; |
||||||
|
applicationInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0); |
||||||
|
applicationInfo.pEngineName = "No Engine"; |
||||||
|
applicationInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0); |
||||||
|
applicationInfo.apiVersion = VK_API_VERSION_1_0; |
||||||
|
|
||||||
|
uint32_t glfwExtensionCount = 0; |
||||||
|
const char** glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount); |
||||||
|
|
||||||
|
VkInstanceCreateInfo instanceCreateInfo {}; |
||||||
|
instanceCreateInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; |
||||||
|
instanceCreateInfo.pApplicationInfo = &applicationInfo; |
||||||
|
instanceCreateInfo.enabledExtensionCount = glfwExtensionCount; |
||||||
|
instanceCreateInfo.ppEnabledExtensionNames = glfwExtensions; |
||||||
|
#ifdef ENABLE_VALIDATION_LAYERS |
||||||
|
instanceCreateInfo.enabledLayerCount = validationLayers.size(); |
||||||
|
instanceCreateInfo.ppEnabledLayerNames = validationLayers.data(); |
||||||
|
#else |
||||||
|
instanceCreateInfo.enabledLayerCount = 0; |
||||||
|
#endif |
||||||
|
|
||||||
|
vkCreateInstance(&instanceCreateInfo, nullptr, &handle); |
||||||
|
} |
||||||
|
|
||||||
|
void Instance::createSurface() { |
||||||
|
glfwCreateWindowSurface(handle, window, nullptr, &surface); |
||||||
|
} |
||||||
|
|
||||||
|
void Instance::pickPhysicalDevice() { |
||||||
|
uint32_t deviceCount = 0; |
||||||
|
vkEnumeratePhysicalDevices(handle, &deviceCount, nullptr); |
||||||
|
if (deviceCount == 0){ |
||||||
|
throw std::runtime_error("failed to find GPUs with Vulkan support!"); |
||||||
|
} |
||||||
|
std::vector<VkPhysicalDevice> devices(deviceCount); |
||||||
|
vkEnumeratePhysicalDevices(handle, &deviceCount, devices.data()); |
||||||
|
|
||||||
|
for (const VkPhysicalDevice &device : devices){ |
||||||
|
if (isDeviceSuitable(device)){ |
||||||
|
physicalDevice = device; |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (physicalDevice == VK_NULL_HANDLE){ |
||||||
|
throw std::runtime_error("failed to find a suitable GPU!"); |
||||||
|
} |
||||||
|
|
||||||
|
VkPhysicalDeviceProperties properties; |
||||||
|
vkGetPhysicalDeviceProperties(physicalDevice, &properties); |
||||||
|
printf("Picked physical device: %s\n", properties.deviceName); |
||||||
|
} |
||||||
|
|
||||||
|
void Instance::createLogicalDevice() { |
||||||
|
QueueFamilyIndices indices = findQueueFamilies(physicalDevice, surface); |
||||||
|
|
||||||
|
std::vector<VkDeviceQueueCreateInfo> queueCreateInfos; |
||||||
|
|
||||||
|
float queuePriority = 1.0f; |
||||||
|
for (uint32_t queueFamily : indices.uniqueQueueFamilies()){ |
||||||
|
VkDeviceQueueCreateInfo queueCreateInfo {}; |
||||||
|
queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; |
||||||
|
queueCreateInfo.queueFamilyIndex = queueFamily; |
||||||
|
queueCreateInfo.queueCount = 1; |
||||||
|
queueCreateInfo.pQueuePriorities = &queuePriority; |
||||||
|
queueCreateInfos.push_back(queueCreateInfo); |
||||||
|
} |
||||||
|
|
||||||
|
VkPhysicalDeviceFeatures deviceFeatures {}; |
||||||
|
|
||||||
|
VkDeviceCreateInfo createInfo {}; |
||||||
|
createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; |
||||||
|
createInfo.pQueueCreateInfos = queueCreateInfos.data(); |
||||||
|
createInfo.queueCreateInfoCount = queueCreateInfos.size(); |
||||||
|
createInfo.pEnabledFeatures = &deviceFeatures; |
||||||
|
createInfo.enabledExtensionCount = deviceExtensions.size(); |
||||||
|
createInfo.ppEnabledExtensionNames = deviceExtensions.data(); |
||||||
|
#ifdef ENABLE_VALIDATION_LAYERS |
||||||
|
createInfo.enabledLayerCount = validationLayers.size(); |
||||||
|
createInfo.ppEnabledLayerNames = validationLayers.data(); |
||||||
|
#else |
||||||
|
createInfo.enabledLayerCount = 0; |
||||||
|
#endif |
||||||
|
|
||||||
|
vkCreateDevice(physicalDevice, &createInfo, nullptr, &device); |
||||||
|
|
||||||
|
vkGetDeviceQueue(device, indices.graphicsFamily.value(), 0, &graphicsQueue); |
||||||
|
vkGetDeviceQueue(device, indices.presentFamily.value(), 0, &presentQueue); |
||||||
|
} |
||||||
|
|
||||||
|
Instance::QueueFamilyIndices Instance::findQueueFamilies(VkPhysicalDevice device, VkSurfaceKHR surface) { |
||||||
|
uint32_t queueFamilyCount = 0; |
||||||
|
vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr); |
||||||
|
|
||||||
|
std::vector<VkQueueFamilyProperties> queueFamilies(queueFamilyCount); |
||||||
|
vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data()); |
||||||
|
|
||||||
|
QueueFamilyIndices indices {}; |
||||||
|
|
||||||
|
uint32_t i = 0; |
||||||
|
for (const VkQueueFamilyProperties& queueFamilyProperties : queueFamilies){ |
||||||
|
if (queueFamilyProperties.queueFlags & VK_QUEUE_GRAPHICS_BIT){ |
||||||
|
indices.graphicsFamily = i; |
||||||
|
} |
||||||
|
if (queueFamilyProperties.queueFlags & VK_QUEUE_COMPUTE_BIT){ |
||||||
|
indices.computeFamily = i; |
||||||
|
} |
||||||
|
VkBool32 presentSupport = false; |
||||||
|
vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, &presentSupport); |
||||||
|
if (presentSupport){ |
||||||
|
indices.presentFamily = i; |
||||||
|
} |
||||||
|
if (indices.isComplete()){ |
||||||
|
break; |
||||||
|
} |
||||||
|
i++; |
||||||
|
} |
||||||
|
return indices; |
||||||
|
} |
||||||
|
|
||||||
|
bool Instance::isDeviceSuitable(VkPhysicalDevice physicalDevice) { |
||||||
|
VkPhysicalDeviceProperties deviceProperties; |
||||||
|
vkGetPhysicalDeviceProperties(physicalDevice, &deviceProperties); |
||||||
|
|
||||||
|
VkPhysicalDeviceFeatures deviceFeatures; |
||||||
|
vkGetPhysicalDeviceFeatures(physicalDevice, &deviceFeatures); |
||||||
|
|
||||||
|
QueueFamilyIndices indices = findQueueFamilies(physicalDevice, surface); |
||||||
|
|
||||||
|
bool extensionsSupported = checkDeviceExtensionSupport(physicalDevice); |
||||||
|
|
||||||
|
bool swapChainAdequate = false; |
||||||
|
if (extensionsSupported){ |
||||||
|
SwapchainSupportDetails details = querySwapchainSupport(physicalDevice, surface); |
||||||
|
swapChainAdequate = !details.formats.empty() && !details.presentModes.empty(); |
||||||
|
} |
||||||
|
|
||||||
|
return indices.isComplete() && extensionsSupported && swapChainAdequate; |
||||||
|
} |
||||||
|
|
||||||
|
bool Instance::checkDeviceExtensionSupport(VkPhysicalDevice device) { |
||||||
|
std::set<std::string> required(deviceExtensions.begin(), deviceExtensions.end()); |
||||||
|
|
||||||
|
uint32_t extensionCount; |
||||||
|
vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, nullptr); |
||||||
|
|
||||||
|
std::vector<VkExtensionProperties> availableExtensions(extensionCount); |
||||||
|
vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, availableExtensions.data()); |
||||||
|
|
||||||
|
for (const VkExtensionProperties& extension : availableExtensions){ |
||||||
|
required.erase(extension.extensionName); |
||||||
|
} |
||||||
|
|
||||||
|
return required.empty(); |
||||||
|
} |
||||||
|
|
||||||
|
Instance::~Instance() { |
||||||
|
vkDestroyDevice(device, nullptr); |
||||||
|
vkDestroySurfaceKHR(handle, surface, nullptr); |
||||||
|
vkDestroyInstance(handle, nullptr); |
||||||
|
|
||||||
|
glfwDestroyWindow(window); |
||||||
|
glfwTerminate(); |
||||||
|
} |
@ -0,0 +1,46 @@ |
|||||||
|
#pragma once |
||||||
|
|
||||||
|
#include <vulkan/vulkan.h> |
||||||
|
#include <GLFW/glfw3.h> |
||||||
|
#include <optional> |
||||||
|
#include <set> |
||||||
|
|
||||||
|
class Instance { |
||||||
|
public: |
||||||
|
explicit Instance(); |
||||||
|
~Instance(); |
||||||
|
|
||||||
|
GLFWwindow *window = nullptr; |
||||||
|
VkSurfaceKHR surface = VK_NULL_HANDLE; |
||||||
|
VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; |
||||||
|
VkDevice device = VK_NULL_HANDLE; |
||||||
|
VkQueue graphicsQueue = VK_NULL_HANDLE; |
||||||
|
VkQueue presentQueue = VK_NULL_HANDLE; |
||||||
|
|
||||||
|
struct QueueFamilyIndices { |
||||||
|
std::optional<uint32_t> graphicsFamily; |
||||||
|
std::optional<uint32_t> computeFamily; |
||||||
|
std::optional<uint32_t> presentFamily; |
||||||
|
bool isComplete() const { |
||||||
|
return graphicsFamily.has_value() && computeFamily.has_value() && presentFamily.has_value(); |
||||||
|
} |
||||||
|
std::set<uint32_t> uniqueQueueFamilies(){ |
||||||
|
return {graphicsFamily.value(), presentFamily.value(), computeFamily.value()}; |
||||||
|
} |
||||||
|
}; |
||||||
|
static QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device, VkSurfaceKHR surface); |
||||||
|
|
||||||
|
private: |
||||||
|
VkInstance handle = VK_NULL_HANDLE; |
||||||
|
|
||||||
|
void initWindow(); |
||||||
|
void createInstance(); |
||||||
|
void createSurface(); |
||||||
|
void pickPhysicalDevice(); |
||||||
|
void createLogicalDevice(); |
||||||
|
|
||||||
|
bool isDeviceSuitable(VkPhysicalDevice physicalDevice); |
||||||
|
|
||||||
|
static bool checkDeviceExtensionSupport(VkPhysicalDevice device); |
||||||
|
|
||||||
|
}; |
@ -0,0 +1,149 @@ |
|||||||
|
#include <vector> |
||||||
|
#include <fstream> |
||||||
|
#include "pipeline.hpp" |
||||||
|
#include "vertex.hpp" |
||||||
|
|
||||||
|
std::vector<char> readFile(const std::string& fileName){ |
||||||
|
std::ifstream file(fileName, std::ios::ate | std::ios::binary); |
||||||
|
|
||||||
|
if (!file.is_open()){ |
||||||
|
throw std::runtime_error("failed to open file!"); |
||||||
|
} |
||||||
|
|
||||||
|
size_t fileSize = file.tellg(); |
||||||
|
std::vector<char> buffer(fileSize); |
||||||
|
|
||||||
|
file.seekg(0); |
||||||
|
file.read(buffer.data(), static_cast<std::streamsize>(fileSize)); |
||||||
|
file.close(); |
||||||
|
|
||||||
|
return buffer; |
||||||
|
} |
||||||
|
|
||||||
|
Pipeline::Pipeline(Instance* instance, VkRenderPass renderPass) : instance(instance) { |
||||||
|
auto vertShaderCode = readFile("shaders/vert.spv"); |
||||||
|
auto fragShaderCode = readFile("shaders/frag.spv"); |
||||||
|
|
||||||
|
VkShaderModule vertShaderModule = createShaderModule(vertShaderCode); |
||||||
|
VkShaderModule fragShaderModule = createShaderModule(fragShaderCode); |
||||||
|
|
||||||
|
|
||||||
|
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}; |
||||||
|
|
||||||
|
auto bindingDescription = Vertex::getBindingDescription(); |
||||||
|
auto attributeDescriptions = Vertex::getAttributeDescriptions(); |
||||||
|
|
||||||
|
VkPipelineVertexInputStateCreateInfo vertexInputInfo {}; |
||||||
|
vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; |
||||||
|
vertexInputInfo.vertexBindingDescriptionCount = 1; |
||||||
|
vertexInputInfo.pVertexBindingDescriptions = &bindingDescription; |
||||||
|
vertexInputInfo.vertexAttributeDescriptionCount = attributeDescriptions.size(); |
||||||
|
vertexInputInfo.pVertexAttributeDescriptions = attributeDescriptions.data(); |
||||||
|
|
||||||
|
VkPipelineInputAssemblyStateCreateInfo inputAssembly {}; |
||||||
|
inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; |
||||||
|
inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; |
||||||
|
inputAssembly.primitiveRestartEnable = VK_FALSE; |
||||||
|
|
||||||
|
std::vector<VkDynamicState> dynamicStates = { |
||||||
|
VK_DYNAMIC_STATE_VIEWPORT, |
||||||
|
VK_DYNAMIC_STATE_SCISSOR |
||||||
|
}; |
||||||
|
|
||||||
|
VkPipelineDynamicStateCreateInfo dynamicState {}; |
||||||
|
dynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; |
||||||
|
dynamicState.dynamicStateCount = dynamicStates.size(); |
||||||
|
dynamicState.pDynamicStates = dynamicStates.data(); |
||||||
|
|
||||||
|
VkPipelineViewportStateCreateInfo viewportState {}; |
||||||
|
viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; |
||||||
|
viewportState.viewportCount = 1; |
||||||
|
viewportState.scissorCount = 1; |
||||||
|
|
||||||
|
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; |
||||||
|
rasterizer.cullMode = VK_CULL_MODE_BACK_BIT; |
||||||
|
rasterizer.frontFace = VK_FRONT_FACE_CLOCKWISE; |
||||||
|
rasterizer.depthBiasClamp = VK_FALSE; |
||||||
|
|
||||||
|
VkPipelineMultisampleStateCreateInfo multisample {}; |
||||||
|
multisample.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; |
||||||
|
multisample.sampleShadingEnable = VK_FALSE; |
||||||
|
multisample.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.attachmentCount = 1; |
||||||
|
colorBlending.pAttachments = &colorBlendAttachment; |
||||||
|
|
||||||
|
VkPipelineLayoutCreateInfo pipelineLayoutInfo {}; |
||||||
|
pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; |
||||||
|
|
||||||
|
vkCreatePipelineLayout(instance->device, &pipelineLayoutInfo, nullptr, &pipelineLayout); |
||||||
|
|
||||||
|
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 = &multisample; |
||||||
|
pipelineInfo.pColorBlendState = &colorBlending; |
||||||
|
pipelineInfo.pDynamicState = &dynamicState; |
||||||
|
|
||||||
|
pipelineInfo.layout = pipelineLayout; |
||||||
|
|
||||||
|
pipelineInfo.renderPass = renderPass; |
||||||
|
pipelineInfo.subpass = 0; |
||||||
|
} |
||||||
|
|
||||||
|
vkCreateGraphicsPipelines(instance->device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &graphicsPipeline); |
||||||
|
|
||||||
|
vkDestroyShaderModule(instance->device, vertShaderModule, nullptr); |
||||||
|
vkDestroyShaderModule(instance->device, fragShaderModule, nullptr); |
||||||
|
} |
||||||
|
|
||||||
|
VkShaderModule Pipeline::createShaderModule(const std::vector<char> &code) { |
||||||
|
VkShaderModuleCreateInfo createInfo {}; |
||||||
|
createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; |
||||||
|
createInfo.codeSize = code.size(); |
||||||
|
createInfo.pCode = reinterpret_cast<const uint32_t *>(code.data()); |
||||||
|
|
||||||
|
VkShaderModule shaderModule; |
||||||
|
vkCreateShaderModule(instance->device, &createInfo, nullptr, &shaderModule); |
||||||
|
|
||||||
|
return shaderModule; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
Pipeline::~Pipeline() { |
||||||
|
vkDestroyPipeline(instance->device, graphicsPipeline, nullptr); |
||||||
|
vkDestroyPipelineLayout(instance->device, pipelineLayout, nullptr); |
||||||
|
} |
||||||
|
|
@ -0,0 +1,15 @@ |
|||||||
|
#pragma once |
||||||
|
|
||||||
|
#include <vulkan/vulkan.h> |
||||||
|
#include "instance.hpp" |
||||||
|
|
||||||
|
class Pipeline { |
||||||
|
public: |
||||||
|
explicit Pipeline(Instance* instance, VkRenderPass renderPass); |
||||||
|
~Pipeline(); |
||||||
|
VkPipeline graphicsPipeline = VK_NULL_HANDLE; |
||||||
|
private: |
||||||
|
VkShaderModule createShaderModule(const std::vector<char> &code); |
||||||
|
VkPipelineLayout pipelineLayout = VK_NULL_HANDLE; |
||||||
|
Instance* instance = nullptr; |
||||||
|
}; |
@ -0,0 +1,215 @@ |
|||||||
|
#include <limits> |
||||||
|
#include <stdexcept> |
||||||
|
#include "swapchain.hpp" |
||||||
|
|
||||||
|
SwapchainSupportDetails querySwapchainSupport(VkPhysicalDevice device, VkSurfaceKHR surface){ |
||||||
|
SwapchainSupportDetails details; |
||||||
|
|
||||||
|
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, surface, &details.capabilities); |
||||||
|
|
||||||
|
uint32_t formatCount; |
||||||
|
vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, nullptr); |
||||||
|
if (formatCount != 0){ |
||||||
|
details.formats.resize(formatCount); |
||||||
|
vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, details.formats.data()); |
||||||
|
} |
||||||
|
|
||||||
|
uint32_t presentModeCount; |
||||||
|
vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, nullptr); |
||||||
|
if (presentModeCount != 0){ |
||||||
|
details.presentModes.resize(presentModeCount); |
||||||
|
vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, details.presentModes.data()); |
||||||
|
} |
||||||
|
|
||||||
|
return details; |
||||||
|
} |
||||||
|
|
||||||
|
Swapchain::Swapchain(Instance* instance) |
||||||
|
: instance(instance) { |
||||||
|
createSwapchain(); |
||||||
|
createImageViews(); |
||||||
|
createRenderpass(); |
||||||
|
createFramebuffers(); |
||||||
|
} |
||||||
|
|
||||||
|
Swapchain::~Swapchain() { |
||||||
|
cleanupSwapchain(); |
||||||
|
vkDestroyRenderPass(instance->device, renderPass, nullptr); |
||||||
|
} |
||||||
|
|
||||||
|
VkExtent2D Swapchain::chooseSwapExtent(const VkSurfaceCapabilitiesKHR &capabilities) { |
||||||
|
if (capabilities.currentExtent.width != std::numeric_limits<uint32_t>::max()){ |
||||||
|
return capabilities.currentExtent; |
||||||
|
} else { |
||||||
|
int width, height; |
||||||
|
glfwGetFramebufferSize(instance->window, &width, &height); |
||||||
|
|
||||||
|
VkExtent2D actualExtent = { |
||||||
|
static_cast<uint32_t>(width), |
||||||
|
static_cast<uint32_t>(height) |
||||||
|
}; |
||||||
|
|
||||||
|
actualExtent.width = std::clamp(actualExtent.width, capabilities.minImageExtent.width, capabilities.maxImageExtent.width); |
||||||
|
actualExtent.height = std::clamp(actualExtent.height, capabilities.minImageExtent.height, capabilities.maxImageExtent.height); |
||||||
|
|
||||||
|
return actualExtent; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void Swapchain::createSwapchain() { |
||||||
|
SwapchainSupportDetails swapchainSupport = querySwapchainSupport(instance->physicalDevice, instance->surface); |
||||||
|
|
||||||
|
VkSurfaceFormatKHR surfaceFormat = chooseSwapSurfaceFormat(swapchainSupport.formats); |
||||||
|
VkPresentModeKHR presentMode = chooseSwapPresentMode(swapchainSupport.presentModes); |
||||||
|
extent = chooseSwapExtent(swapchainSupport.capabilities); |
||||||
|
|
||||||
|
uint32_t imageCount = swapchainSupport.capabilities.minImageCount + 1; |
||||||
|
if (swapchainSupport.capabilities.maxImageCount > 0 && imageCount > swapchainSupport.capabilities.maxImageCount){ |
||||||
|
imageCount = swapchainSupport.capabilities.maxImageCount; |
||||||
|
} |
||||||
|
|
||||||
|
VkSwapchainCreateInfoKHR createInfo {}; |
||||||
|
createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; |
||||||
|
createInfo.surface = instance->surface; |
||||||
|
createInfo.minImageCount = imageCount; |
||||||
|
createInfo.imageFormat = surfaceFormat.format; |
||||||
|
createInfo.imageColorSpace = surfaceFormat.colorSpace; |
||||||
|
createInfo.imageExtent = extent; |
||||||
|
createInfo.imageArrayLayers = 1; |
||||||
|
createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; |
||||||
|
|
||||||
|
Instance::QueueFamilyIndices indices = Instance::findQueueFamilies(instance->physicalDevice, instance->surface); |
||||||
|
uint32_t queueFamilyIndices[] = {indices.graphicsFamily.value(), indices.presentFamily.value()}; |
||||||
|
|
||||||
|
if (indices.graphicsFamily != indices.presentFamily){ |
||||||
|
createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT; |
||||||
|
createInfo.queueFamilyIndexCount = 2; |
||||||
|
createInfo.pQueueFamilyIndices = queueFamilyIndices; |
||||||
|
} else { |
||||||
|
createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; |
||||||
|
} |
||||||
|
|
||||||
|
createInfo.preTransform = swapchainSupport.capabilities.currentTransform; |
||||||
|
createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; |
||||||
|
createInfo.presentMode = presentMode; |
||||||
|
createInfo.clipped = VK_TRUE; |
||||||
|
createInfo.oldSwapchain = VK_NULL_HANDLE; |
||||||
|
|
||||||
|
vkCreateSwapchainKHR(instance->device, &createInfo, nullptr, &handle); |
||||||
|
|
||||||
|
vkGetSwapchainImagesKHR(instance->device, handle, &imageCount, nullptr); |
||||||
|
images.resize(imageCount); |
||||||
|
vkGetSwapchainImagesKHR(instance->device, handle, &imageCount, images.data()); |
||||||
|
|
||||||
|
imageFormat = surfaceFormat.format; |
||||||
|
} |
||||||
|
|
||||||
|
void Swapchain::cleanupSwapchain() { |
||||||
|
for (auto framebuffer : frameBuffers){ |
||||||
|
vkDestroyFramebuffer(instance->device, framebuffer, nullptr); |
||||||
|
} |
||||||
|
for (auto imageView : imageViews){ |
||||||
|
vkDestroyImageView(instance->device, imageView, nullptr); |
||||||
|
} |
||||||
|
vkDestroySwapchainKHR(instance->device, handle, nullptr); |
||||||
|
} |
||||||
|
|
||||||
|
void Swapchain::recreateSwapchain() { |
||||||
|
vkDeviceWaitIdle(instance->device); |
||||||
|
|
||||||
|
cleanupSwapchain(); |
||||||
|
|
||||||
|
createSwapchain(); |
||||||
|
createImageViews(); |
||||||
|
createFramebuffers(); |
||||||
|
} |
||||||
|
|
||||||
|
void Swapchain::createImageViews() { |
||||||
|
imageViews.resize(images.size()); |
||||||
|
for (size_t i = 0; i < images.size(); i++){ |
||||||
|
VkImageViewCreateInfo createInfo {}; |
||||||
|
createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; |
||||||
|
createInfo.format = imageFormat; |
||||||
|
createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; |
||||||
|
createInfo.image = images[i]; |
||||||
|
createInfo.components = { |
||||||
|
VK_COMPONENT_SWIZZLE_IDENTITY, |
||||||
|
VK_COMPONENT_SWIZZLE_IDENTITY, |
||||||
|
VK_COMPONENT_SWIZZLE_IDENTITY, |
||||||
|
VK_COMPONENT_SWIZZLE_IDENTITY, |
||||||
|
}; |
||||||
|
createInfo.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}; |
||||||
|
vkCreateImageView(instance->device, &createInfo, nullptr, &imageViews[i]); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void Swapchain::createFramebuffers() { |
||||||
|
frameBuffers.resize(imageViews.size()); |
||||||
|
for (size_t i = 0; i < imageViews.size(); i++){ |
||||||
|
VkImageView attachments[] = {imageViews[i]}; |
||||||
|
|
||||||
|
VkFramebufferCreateInfo framebufferInfo {}; |
||||||
|
framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; |
||||||
|
framebufferInfo.renderPass = renderPass; |
||||||
|
framebufferInfo.attachmentCount = 1; |
||||||
|
framebufferInfo.pAttachments = attachments; |
||||||
|
framebufferInfo.width = extent.width; |
||||||
|
framebufferInfo.height = extent.height; |
||||||
|
framebufferInfo.layers = 1; |
||||||
|
|
||||||
|
vkCreateFramebuffer(instance->device, &framebufferInfo, nullptr, &frameBuffers[i]); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
VkSurfaceFormatKHR Swapchain::chooseSwapSurfaceFormat(const std::vector<VkSurfaceFormatKHR> &availableFormats) { |
||||||
|
for (const auto& availableFormat : availableFormats){ |
||||||
|
if (availableFormat.format == VK_FORMAT_B8G8R8A8_SRGB && availableFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR){ |
||||||
|
return availableFormat; |
||||||
|
} |
||||||
|
} |
||||||
|
return availableFormats[0]; |
||||||
|
} |
||||||
|
|
||||||
|
VkPresentModeKHR Swapchain::chooseSwapPresentMode(const std::vector<VkPresentModeKHR> &availablePresentModes) { |
||||||
|
return VK_PRESENT_MODE_FIFO_KHR; |
||||||
|
} |
||||||
|
|
||||||
|
void Swapchain::createRenderpass() { |
||||||
|
VkAttachmentDescription colorAttachment {}; |
||||||
|
colorAttachment.format = imageFormat; |
||||||
|
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; |
||||||
|
|
||||||
|
vkCreateRenderPass(instance->device, &renderPassInfo, nullptr, &renderPass); |
||||||
|
} |
@ -0,0 +1,45 @@ |
|||||||
|
#pragma once |
||||||
|
|
||||||
|
#include <vulkan/vulkan.h> |
||||||
|
#include <vector> |
||||||
|
|
||||||
|
#include "instance.hpp" |
||||||
|
|
||||||
|
struct SwapchainSupportDetails { |
||||||
|
VkSurfaceCapabilitiesKHR capabilities {}; |
||||||
|
std::vector<VkSurfaceFormatKHR> formats {}; |
||||||
|
std::vector<VkPresentModeKHR> presentModes {}; |
||||||
|
}; |
||||||
|
SwapchainSupportDetails querySwapchainSupport(VkPhysicalDevice device, VkSurfaceKHR surface); |
||||||
|
|
||||||
|
class Swapchain { |
||||||
|
public: |
||||||
|
explicit Swapchain(Instance* instance); |
||||||
|
~Swapchain(); |
||||||
|
|
||||||
|
void recreateSwapchain(); |
||||||
|
void cleanupSwapchain(); |
||||||
|
|
||||||
|
VkRenderPass renderPass = VK_NULL_HANDLE; |
||||||
|
VkExtent2D extent {}; |
||||||
|
std::vector<VkFramebuffer> frameBuffers; |
||||||
|
VkSwapchainKHR handle = VK_NULL_HANDLE; |
||||||
|
private: |
||||||
|
Instance* instance = nullptr; |
||||||
|
std::vector<VkImage> images; |
||||||
|
VkFormat imageFormat {}; |
||||||
|
|
||||||
|
void createRenderpass(); |
||||||
|
void createFramebuffers(); |
||||||
|
|
||||||
|
static VkSurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector<VkSurfaceFormatKHR>& availableFormats); |
||||||
|
static VkPresentModeKHR chooseSwapPresentMode(const std::vector<VkPresentModeKHR>& availablePresentModes); |
||||||
|
|
||||||
|
VkExtent2D chooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities); |
||||||
|
|
||||||
|
void createSwapchain(); |
||||||
|
|
||||||
|
std::vector<VkImageView> imageViews; |
||||||
|
|
||||||
|
void createImageViews(); |
||||||
|
}; |
@ -0,0 +1 @@ |
|||||||
|
#include "vertex.hpp" |
@ -0,0 +1,34 @@ |
|||||||
|
#pragma once |
||||||
|
|
||||||
|
#include <glm/glm.hpp> |
||||||
|
#include <vulkan/vulkan.h> |
||||||
|
#include <array> |
||||||
|
|
||||||
|
struct Vertex { |
||||||
|
glm::vec2 pos; |
||||||
|
glm::vec3 color; |
||||||
|
|
||||||
|
static VkVertexInputBindingDescription getBindingDescription(){ |
||||||
|
VkVertexInputBindingDescription bindingDescription {}; |
||||||
|
bindingDescription.binding = 0; |
||||||
|
bindingDescription.stride = sizeof(Vertex); |
||||||
|
bindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; |
||||||
|
return bindingDescription; |
||||||
|
} |
||||||
|
|
||||||
|
static std::array<VkVertexInputAttributeDescription, 2> getAttributeDescriptions(){ |
||||||
|
std::array<VkVertexInputAttributeDescription, 2> attributeDescriptions {}; |
||||||
|
|
||||||
|
attributeDescriptions[0].binding = 0; |
||||||
|
attributeDescriptions[0].location = 0; |
||||||
|
attributeDescriptions[0].format = VK_FORMAT_R32G32_SFLOAT; |
||||||
|
attributeDescriptions[0].offset = offsetof(Vertex, pos); |
||||||
|
|
||||||
|
attributeDescriptions[1].binding = 0; |
||||||
|
attributeDescriptions[1].location = 1; |
||||||
|
attributeDescriptions[1].format = VK_FORMAT_R32G32B32_SFLOAT; |
||||||
|
attributeDescriptions[1].offset = offsetof(Vertex, color); |
||||||
|
|
||||||
|
return attributeDescriptions; |
||||||
|
} |
||||||
|
}; |
Loading…
Reference in new issue