parent
a1022e0e30
commit
a6eb297a78
18 changed files with 1078 additions and 920 deletions
@ -1,921 +1,7 @@ |
||||
#define GLFW_INCLUDE_VULKAN |
||||
#include <GLFW/glfw3.h> |
||||
#include "vulkan/application.hpp" |
||||
|
||||
#include <glm/glm.hpp> |
||||
|
||||
#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(); |
||||
} |
||||
}; |
||||
#include <thread> |
||||
|
||||
int main() { |
||||
|
||||
MyApp app; |
||||
try { |
||||
app.run(); |
||||
} catch (const std::exception& e){ |
||||
std::cerr << e.what() << "\n"; |
||||
return EXIT_FAILURE; |
||||
} |
||||
|
||||
return EXIT_SUCCESS; |
||||
Application application; |
||||
} |
||||
|
@ -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