diff --git a/CMakeLists.txt b/CMakeLists.txt index 7d3f7de..1d480d3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,8 +3,7 @@ project(App) set(CMAKE_CXX_STANDARD 26) -add_executable(${PROJECT_NAME} src/main.cpp src/utils.cpp src/shader.cpp) -target_include_directories(${PROJECT_NAME} PUBLIC ./inc) -target_link_libraries(${PROJECT_NAME} PUBLIC vulkan glfw) - -file(COPY shaders DESTINATION ${PROJECT_BINARY_DIR}/) \ No newline at end of file +add_subdirectory(src/Common) +add_subdirectory(src/GPU) +add_subdirectory(src/PlatformWindow) +add_subdirectory(src/App) diff --git a/src/App/CMakeLists.txt b/src/App/CMakeLists.txt new file mode 100644 index 0000000..5549396 --- /dev/null +++ b/src/App/CMakeLists.txt @@ -0,0 +1,16 @@ +cmake_minimum_required(VERSION 3.30) + +project(App) + +add_executable(${PROJECT_NAME} + private/main.cpp + private/shader.cpp + private/swapchain.cpp + private/renderer.cpp + private/vulkan_utils.cpp +) + +target_include_directories(${PROJECT_NAME} PUBLIC public) +target_link_libraries(${PROJECT_NAME} PUBLIC Common GPU PlatformWindow) + +file(COPY private/shaders DESTINATION ${PROJECT_BINARY_DIR}/) \ No newline at end of file diff --git a/src/App/private/main.cpp b/src/App/private/main.cpp new file mode 100644 index 0000000..452ee56 --- /dev/null +++ b/src/App/private/main.cpp @@ -0,0 +1,436 @@ +#include + +#include "renderer.hpp" +#include "swapchain.hpp" +#include "vulkan_utils.hpp" + +#include "utils.hpp" + +#include + +#include +#include +#include +#include +#include + +class Application { +public: + void run() { + initWindow(); + initVulkan(); + mainLoop(); + cleanup(); + } + + void scheduleWindowResize(int sizeX, int sizeY) { + mWindowSizeDirtyFlagTime = std::chrono::system_clock::now(); + mWindowSizeDirtyFlag = true; + mWindowFramebufferSize = std::make_pair(sizeX, sizeY); + assert(!(sizeX <= 0 || sizeY <= 0)); + } + +private: + void initWindow() { + // glfwInitHint(GLFW_PLATFORM, GLFW_PLATFORM_X11); + glfwInit(); + + glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); + // glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); + mWindow = glfwCreateWindow(800, 600, "App", nullptr, nullptr); + scheduleWindowResize(800, 600); + + glfwSetWindowUserPointer(mWindow, this); + glfwSetFramebufferSizeCallback(mWindow, [](GLFWwindow* window, int sizeX, int sizeY){ + ((Application*) glfwGetWindowUserPointer(window))->scheduleWindowResize(sizeX, sizeY); + }); + } + + void initVulkan() { + const std::vector deviceExtensions = { + VK_KHR_SWAPCHAIN_EXTENSION_NAME, + }; + + uint32_t glfwExtensionCount = 0; + const char **glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount); + + mInstance = createInstance(glfwExtensions, glfwExtensionCount); + + createWindowSurface(); + pickPhysicalDevice(deviceExtensions); + findPhysicalDeviceQueueFamilies(); + createLogicalDevice(deviceExtensions); + getQueues(); + + int width, height; + glfwGetFramebufferSize(mWindow, &width, &height); + + SwapChain::CreateInfo swapChainCreateInfo { + .device = mDevice, + .physicalDevice = mPhysicalDevice, + .surface = mSurface, + .sizeX = width, + .sizeY = height, + .graphicsQueueFamilyIndex = mGraphicsQueueFamilyIndex, + .presentationQueueFamilyIndex = mPresentationQueueFamilyIndex, + }; + + mSwapChain.createSwapChain(swapChainCreateInfo); + mSwapChain.createSwapChainImageViews(mDevice); + + createCommandPool(); + createCommandBuffer(); + + Renderer::CreateInfo rendererCreateInfo { + mDevice, mPhysicalDevice, + mSwapChain.mSwapChainExtent, mSwapChain.mSwapChainFormat, + mCommandPool, mGraphicsQueue, + }; + + mRenderer.create(rendererCreateInfo); + + mSwapChain.createSwapChainFramebuffers(mDevice, mRenderer.mGraphicsRenderPass); + + createSynchronizationObjects(); + } + + void mainLoop() { + while (!glfwWindowShouldClose(mWindow)) { + glfwPollEvents(); + + if (mWindowSizeDirtyFlag) { + auto timeDelayMs = timeDeltaMs(mWindowSizeDirtyFlagTime); + if (timeDelayMs > mWindowSizeApplyMinDelay) { + recreateSwapChain(mWindowFramebufferSize.first, mWindowFramebufferSize.second); + mWindowSizeDirtyFlag = false; + } + } + + drawFrame(); + } + + vkDeviceWaitIdle(mDevice); + } + + void pickPhysicalDevice(const std::vector& deviceExtensions) { + + uint32_t deviceCount = 0; + vkEnumeratePhysicalDevices(mInstance, &deviceCount, nullptr); + + if (deviceCount == 0) { + throw std::runtime_error("no gpu with vulkan support"); + } + + std::vector devices(deviceCount); + vkEnumeratePhysicalDevices(mInstance, &deviceCount, devices.data()); + + for (const auto &device: devices) { + if (isDeviceSuitable(device, deviceExtensions)) { + mPhysicalDevice = device; + break; + } + } + + if (!mPhysicalDevice) throw std::runtime_error("no suitable gpu"); + } + + bool isDeviceSuitable(VkPhysicalDevice device, const std::vector& deviceExtensions) { + VkPhysicalDeviceProperties properties; + VkPhysicalDeviceFeatures features; + + vkGetPhysicalDeviceProperties(device, &properties); + vkGetPhysicalDeviceFeatures(device, &features); + + // if (properties.deviceType != VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) return false; + if (!features.geometryShader) return false; + if (!checkDeviceExtensions(device, deviceExtensions)) return false; + if (!SwapChain::checkDeviceSwapChain(device, mSurface)) return false; + + return true; + } + + void findPhysicalDeviceQueueFamilies() { + uint32_t queuesCount = 0; + vkGetPhysicalDeviceQueueFamilyProperties(mPhysicalDevice, &queuesCount, nullptr); + + std::vector queueFamilyProperties(queuesCount); + vkGetPhysicalDeviceQueueFamilyProperties(mPhysicalDevice, &queuesCount, queueFamilyProperties.data()); + + int index = 0; + for (const auto &familyProperty: queueFamilyProperties) { + if (familyProperty.queueFlags & VK_QUEUE_GRAPHICS_BIT) { + mGraphicsQueueFamilyIndex = index; + } + + VkBool32 presentationQueueSupport = false; + vkGetPhysicalDeviceSurfaceSupportKHR(mPhysicalDevice, index, mSurface, &presentationQueueSupport); + + if (presentationQueueSupport) { + mPresentationQueueFamilyIndex = index; + } + + index++; + } + + if (mPresentationQueueFamilyIndex == -1 || mGraphicsQueueFamilyIndex == -1) { + throw std::runtime_error("nu require queue families found"); + } + } + + void createLogicalDevice(const std::vector& deviceExtensions) { + float queuePriority = 1.f; + + VkDeviceQueueCreateInfo graphicsQueueCreateInfos{ + .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, + .queueFamilyIndex = mGraphicsQueueFamilyIndex, + .queueCount = 1, + .pQueuePriorities = &queuePriority, + }; + + VkDeviceQueueCreateInfo presentationQueueCreateInfos{ + .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, + .queueFamilyIndex = mPresentationQueueFamilyIndex, + .queueCount = 1, + .pQueuePriorities = &queuePriority, + }; + + std::vector queues = {graphicsQueueCreateInfos}; + + if (mPresentationQueueFamilyIndex != mGraphicsQueueFamilyIndex) { + queues.push_back(presentationQueueCreateInfos); + } + + VkPhysicalDeviceFeatures features{}; + + VkDeviceCreateInfo deviceCreateInfo{ + .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, + .queueCreateInfoCount = (uint32_t) queues.size(), + .pQueueCreateInfos = queues.data(), + .enabledExtensionCount = (uint32_t) deviceExtensions.size(), + .ppEnabledExtensionNames = deviceExtensions.data(), + .pEnabledFeatures = &features, + }; + + vkCreateDevice(mPhysicalDevice, &deviceCreateInfo, nullptr, &mDevice); + + if (mDevice == VK_NULL_HANDLE) throw std::runtime_error("failed to create vulkan logical device"); + } + + void getQueues() { + vkGetDeviceQueue(mDevice, mGraphicsQueueFamilyIndex, 0, &mGraphicsQueue); + vkGetDeviceQueue(mDevice, mPresentationQueueFamilyIndex, 0, &mPresentQueue); + } + + void createWindowSurface() { + if (glfwCreateWindowSurface(mInstance, mWindow, nullptr, &mSurface) != VK_SUCCESS) { + throw std::runtime_error("failed to create vulkan window surface"); + } + } + + void recreateSwapChain(int sizeX, int sizeY) { + vkDeviceWaitIdle(mDevice); + + mSwapChain.destroySwapChain(mDevice); + + SwapChain::CreateInfo createInfo { + .device = mDevice, + .physicalDevice = mPhysicalDevice, + .surface = mSurface, + .sizeX = sizeX, + .sizeY = sizeY, + .graphicsQueueFamilyIndex = mGraphicsQueueFamilyIndex, + .presentationQueueFamilyIndex = mPresentationQueueFamilyIndex, + }; + + mSwapChain.createSwapChain(createInfo); + mSwapChain.createSwapChainImageViews(mDevice); + mSwapChain.createSwapChainFramebuffers(mDevice, mRenderer.mGraphicsRenderPass); + } + + + void createSynchronizationObjects() { + VkSemaphoreCreateInfo semaphoreCreateInfo { + .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, + }; + + VkFenceCreateInfo fenceCreateInfo { + .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, + .flags = VK_FENCE_CREATE_SIGNALED_BIT, + }; + + bool failure = false; + + failure |= vkCreateSemaphore(mDevice, &semaphoreCreateInfo, nullptr, &mSemaphoreImageAcquired) != VK_SUCCESS; + failure |= vkCreateSemaphore(mDevice, &semaphoreCreateInfo, nullptr, &mSemaphoreFramebufferDrawn) != VK_SUCCESS; + failure |= vkCreateFence(mDevice, &fenceCreateInfo, nullptr, &mFenceCanStartNewFrame) != VK_SUCCESS; + + if (failure) throw std::runtime_error("failed to create synchronization objects"); + } + + void drawFrame() { + vkWaitForFences(mDevice, 1, &mFenceCanStartNewFrame, VK_TRUE, UINT64_MAX); + vkResetFences(mDevice, 1, &mFenceCanStartNewFrame); + + uint32_t imageIndex = 0; + vkAcquireNextImageKHR(mDevice, mSwapChain.mSwapChain, UINT64_MAX, mSemaphoreImageAcquired, VK_NULL_HANDLE, &imageIndex); + + vkResetCommandBuffer(mCommandBuffer, 0); + mRenderer.populateGraphicsCommandBuffer(mCommandBuffer, mSwapChain.mSwapChainFrameBuffers[imageIndex], + mSwapChain.mSwapChainExtent); + + VkSemaphore waitSemaphores[] = { mSemaphoreImageAcquired }; + VkSemaphore signalSemaphores[] = { mSemaphoreFramebufferDrawn }; + VkPipelineStageFlags waitStages[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT }; + + VkSubmitInfo submitInfo { + .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, + .waitSemaphoreCount = 1, + .pWaitSemaphores = waitSemaphores, + .pWaitDstStageMask = waitStages, + .commandBufferCount = 1, + .pCommandBuffers = &mCommandBuffer, + .signalSemaphoreCount = 1, + .pSignalSemaphores = signalSemaphores, + }; + + updateUniformBuffer(); + + if (vkQueueSubmit(mGraphicsQueue, 1, &submitInfo, mFenceCanStartNewFrame) != VK_SUCCESS) { + throw std::runtime_error("failed to submit to graphics queue"); + } + + VkSwapchainKHR swapchains[] = { mSwapChain.mSwapChain }; + + VkPresentInfoKHR presentInfo { + .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, + .waitSemaphoreCount = 1, + .pWaitSemaphores = signalSemaphores, + .swapchainCount = 1, + .pSwapchains = swapchains, + .pImageIndices = &imageIndex, + }; + + vkQueuePresentKHR(mPresentQueue, &presentInfo); + /* Somehow res is always VK_SUCCESS + if (res == VK_ERROR_OUT_OF_DATE_KHR || res == VK_SUBOPTIMAL_KHR) { + recreateSwapChain(); + } else if (res != VK_SUCCESS) { + throw std::runtime_error("cannot acquire new khr image"); + } + */ + } + + void updateUniformBuffer() const { + static float time = 0; + time += 0.001; + + glm::mat4 model = glm::rotate(glm::mat4(1.0f), (time * glm::radians(90.f)), glm::vec3(0.f, 0.f, 1.f)); + glm::mat4 view = glm::lookAt(glm::vec3{2.0f, 2.0f, 2.0f}, glm::vec3{0.0f, 0.0f, 0.0f}, -glm::vec3{0.0f, 0.0f, 1.0f}); + glm::mat4 perspective = glm::perspective(glm::radians(45.f), + (float) mWindowFramebufferSize.first / + (float) mWindowFramebufferSize.second, 0.1f, 10.f); + + auto transforms = perspective * view * model; + + CustomShader::UniformBuffer ubo { + .transforms = transforms, + .origin = glm::vec4(0, 0, 0, 0), + }; + + memcpy(mRenderer.mUniformBufferMemoryMapped, &ubo, sizeof(ubo)); + // ubo.transforms = + } + + void destroySynchronizationObjects() { + vkDestroySemaphore(mDevice, mSemaphoreImageAcquired, nullptr); + vkDestroySemaphore(mDevice, mSemaphoreFramebufferDrawn, nullptr); + vkDestroyFence(mDevice, mFenceCanStartNewFrame, nullptr); + } + + void cleanup() { + mRenderer.destroy(mDevice); + + destroyCommandPool(); + destroySynchronizationObjects(); + + mSwapChain.destroySwapChain(mDevice); + + vkDestroyDevice(mDevice, nullptr); + vkDestroySurfaceKHR(mInstance, mSurface, nullptr); + vkDestroyInstance(mInstance, nullptr); + glfwDestroyWindow(mWindow); + glfwTerminate(); + } + + void createCommandPool() { + VkCommandPoolCreateInfo createInfo{ + .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, + .flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, + .queueFamilyIndex = mGraphicsQueueFamilyIndex, + }; + + if (vkCreateCommandPool(mDevice, &createInfo, nullptr, &mCommandPool) != VK_SUCCESS) { + throw std::runtime_error("failed to create command pool"); + } + } + + void createCommandBuffer() { + VkCommandBufferAllocateInfo allocateInfo{ + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, + .commandPool = mCommandPool, + .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, + .commandBufferCount = 1, + }; + + if (vkAllocateCommandBuffers(mDevice, &allocateInfo, &mCommandBuffer) != VK_SUCCESS) { + throw std::runtime_error("failed to create command buffer"); + } + } + + void destroyCommandPool() { + vkDestroyCommandPool(mDevice, mCommandPool, nullptr); + } + +private: + GLFWwindow *mWindow = nullptr; + + bool mWindowSizeDirtyFlag = true; + TimePoint mWindowSizeDirtyFlagTime = std::chrono::system_clock::now(); + TimeMs mWindowSizeApplyMinDelay = 200; + std::pair mWindowFramebufferSize = { 0, 0 }; + + uint32_t mGraphicsQueueFamilyIndex = -1; + uint32_t mPresentationQueueFamilyIndex = -1; + + VkCommandPool mCommandPool = VK_NULL_HANDLE; + VkCommandBuffer mCommandBuffer = VK_NULL_HANDLE; + + VkInstance mInstance = VK_NULL_HANDLE; + VkPhysicalDevice mPhysicalDevice = VK_NULL_HANDLE; + VkDevice mDevice = VK_NULL_HANDLE; + + VkQueue mGraphicsQueue = VK_NULL_HANDLE; + VkQueue mPresentQueue = VK_NULL_HANDLE; + + VkSurfaceKHR mSurface = VK_NULL_HANDLE; + SwapChain mSwapChain; + + Renderer mRenderer; + + VkSemaphore mSemaphoreImageAcquired = VK_NULL_HANDLE; + VkSemaphore mSemaphoreFramebufferDrawn = VK_NULL_HANDLE; + VkFence mFenceCanStartNewFrame = VK_NULL_HANDLE; +}; + +int main() { + Application app; + + try { + app.run(); + } catch (const std::exception &e) { + std::cerr << e.what() << std::endl; + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} \ No newline at end of file diff --git a/src/App/private/renderer.cpp b/src/App/private/renderer.cpp new file mode 100644 index 0000000..874466d --- /dev/null +++ b/src/App/private/renderer.cpp @@ -0,0 +1,486 @@ +#include "renderer.hpp" + +#include +#include + +void Renderer::create(const CreateInfo &info) { + mShader.create(info.device); + + createRenderPass(info.device, info.format); + createGraphicsPipeline(info.device, info.extent2D); + + createVertexBuffer(info.physicalDevice, info.device, info.queue, info.commandPool); + createIndexBuffer(info.physicalDevice, info.device, info.queue, info.commandPool); + createUniformBuffer(info.physicalDevice, info.device); + + createDescriptorPool(info.device); + createDescriptorSets(info.device); +} + +void Renderer::destroy(VkDevice device) const { + // vkDestroyDescriptorSetLayout(mDevice, mDescriptorSet, nullptr); + vkDestroyDescriptorPool(device, mDescriptorPool, nullptr); + + vkUnmapMemory(device, mUniformBufferMemory); + destroyBuffer(device, mUniformBuffer, mUniformBufferMemory); + + destroyBuffer(device, mVertexBuffer, mVertexBufferMemory); + destroyBuffer(device, mIndexBuffer, mIndexBufferMemory); + + vkDestroyRenderPass(device, mGraphicsRenderPass, nullptr); + + destroyGraphicsPipeline(device); +} + +void Renderer::createDescriptorPool(VkDevice device) { + VkDescriptorPoolSize poolSize{ + .type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + .descriptorCount = 1, + }; + + VkDescriptorPoolCreateInfo createInfo{ + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, + .maxSets = 1, + .poolSizeCount = 1, + .pPoolSizes = &poolSize, + }; + + if (vkCreateDescriptorPool(device, &createInfo, nullptr, &mDescriptorPool) != VK_SUCCESS) { + throw std::runtime_error("failed to create descriptor pool"); + } +} + +void Renderer::createDescriptorSets(VkDevice device) { + VkDescriptorSetAllocateInfo allocateInfo{ + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, + .descriptorPool = mDescriptorPool, + .descriptorSetCount = 1, + .pSetLayouts = &mShader.mDescriptorSetLayout, + }; + + if (vkAllocateDescriptorSets(device, &allocateInfo, &mDescriptorSet) != VK_SUCCESS) { + throw std::runtime_error("failed to allocate descriptor set"); + } + + VkDescriptorBufferInfo bufferInfo{ + .buffer = mUniformBuffer, + .offset = 0, + .range = sizeof(CustomShader::UniformBuffer), + }; + + VkWriteDescriptorSet writeDescriptorSet{ + .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + .dstSet = mDescriptorSet, + .dstBinding = 0, + .dstArrayElement = 0, + .descriptorCount = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + .pBufferInfo = &bufferInfo, + }; + + vkUpdateDescriptorSets(device, 1, &writeDescriptorSet, 0, nullptr); +} + +void Renderer::createGraphicsPipeline(VkDevice device, VkExtent2D extent2D) { + + std::vector dynamicStates = { + VK_DYNAMIC_STATE_VIEWPORT, + VK_DYNAMIC_STATE_SCISSOR, + }; + + VkPipelineDynamicStateCreateInfo dynamicStateCreateInfo = { + .sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, + .dynamicStateCount = (uint32_t) dynamicStates.size(), + .pDynamicStates = dynamicStates.data(), + }; + + VkPipelineVertexInputStateCreateInfo vertexInputStateCreateInfo{ + .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, + .vertexBindingDescriptionCount = 1, + .pVertexBindingDescriptions = &mShader.mVertexInputDescription, + .vertexAttributeDescriptionCount = (uint32_t) mShader.mVertexAttributes.size(), + .pVertexAttributeDescriptions = mShader.mVertexAttributes.data(), + }; + + VkPipelineInputAssemblyStateCreateInfo inputAssemblyStateCreateInfo{ + .sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, + .topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, + .primitiveRestartEnable = VK_FALSE, + }; + + VkViewport viewport{ + .x = 0, + .y = 0, + .width = (float) extent2D.width, + .height = (float) extent2D.height, + .minDepth = 0.f, + .maxDepth = 1.f, + }; + + VkRect2D scissor{ + .offset = {0, 0}, + .extent = extent2D, + }; + + VkPipelineViewportStateCreateInfo viewportState{ + .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, + .viewportCount = 1, + .pViewports = &viewport, + .scissorCount = 1, + .pScissors = &scissor, + }; + + VkPipelineRasterizationStateCreateInfo rasterizationStateCreateInfo{ + .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, + .depthClampEnable = VK_FALSE, + .rasterizerDiscardEnable = VK_FALSE, + .polygonMode = VK_POLYGON_MODE_FILL, + .cullMode = VK_CULL_MODE_BACK_BIT, + .frontFace = VK_FRONT_FACE_CLOCKWISE, + + .depthBiasEnable = VK_FALSE, + .depthBiasConstantFactor = 0.f, + .depthBiasClamp = 0.f, + .depthBiasSlopeFactor = 0.f, + + .lineWidth = 1, + }; + + VkPipelineMultisampleStateCreateInfo multisampleStateCreateInfo{ + .sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, + .rasterizationSamples = VK_SAMPLE_COUNT_1_BIT, + .sampleShadingEnable = VK_FALSE, + }; + + VkPipelineColorBlendAttachmentState colorBlendAttachmentState{ + .blendEnable = VK_FALSE, + .colorWriteMask = (VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | + VK_COLOR_COMPONENT_A_BIT), + }; + + VkPipelineColorBlendStateCreateInfo colorBlendStateCreateInfo{ + .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, + .logicOpEnable = VK_FALSE, + .attachmentCount = 1, + .pAttachments = &colorBlendAttachmentState, + }; + + VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo{ + .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, + .setLayoutCount = 1, + .pSetLayouts = &mShader.mDescriptorSetLayout, + }; + + if (vkCreatePipelineLayout(device, &pipelineLayoutCreateInfo, nullptr, &mGraphicsPipelineLayout) != VK_SUCCESS) { + throw std::runtime_error("failed to create pipeline layout"); + } + + VkGraphicsPipelineCreateInfo graphicsPipelineCreateInfo{ + .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, + .stageCount = 2, + .pStages = mShader.mStageCreateInfos.data(), + .pVertexInputState = &vertexInputStateCreateInfo, + .pInputAssemblyState = &inputAssemblyStateCreateInfo, + .pViewportState = &viewportState, + .pRasterizationState = &rasterizationStateCreateInfo, + .pMultisampleState = &multisampleStateCreateInfo, + .pColorBlendState = &colorBlendStateCreateInfo, + .pDynamicState = &dynamicStateCreateInfo, + .layout = mGraphicsPipelineLayout, + .renderPass = mGraphicsRenderPass, + .subpass = 0, + }; + + if (vkCreateGraphicsPipelines(device, nullptr, 1, &graphicsPipelineCreateInfo, nullptr, &mGraphicsPipeline) != + VK_SUCCESS) { + throw std::runtime_error("failed to create graphics pipeline"); + } +} + +void Renderer::createRenderPass(VkDevice device, VkFormat format) { + VkAttachmentDescription colorAttachment{ + .format = format, + .samples = VK_SAMPLE_COUNT_1_BIT, + .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR, + .storeOp = VK_ATTACHMENT_STORE_OP_STORE, + .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE, + .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE, + .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, + .finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, + }; + + VkAttachmentReference colorAttachmentReference{ + .attachment = 0, + .layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + }; + + VkSubpassDescription subpassDescription{ + .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS, + .colorAttachmentCount = 1, + .pColorAttachments = &colorAttachmentReference, + }; + + VkSubpassDependency dependency{ + .srcSubpass = VK_SUBPASS_EXTERNAL, + .dstSubpass = 0, + .srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + .dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + .srcAccessMask = 0, + .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, + }; + + VkRenderPassCreateInfo renderPassCreateInfo{ + .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, + .attachmentCount = 1, + .pAttachments = &colorAttachment, + .subpassCount = 1, + .pSubpasses = &subpassDescription, + .dependencyCount = 1, + .pDependencies = &dependency + }; + + if (vkCreateRenderPass(device, &renderPassCreateInfo, nullptr, &mGraphicsRenderPass) != VK_SUCCESS) { + throw std::runtime_error("failed to create render pass"); + } +} + +void Renderer::destroyGraphicsPipeline(VkDevice device) const { + mShader.destroy(device); + vkDestroyPipelineLayout(device, mGraphicsPipelineLayout, nullptr); + vkDestroyPipeline(device, mGraphicsPipeline, nullptr); +} + +void Renderer::createIndexBuffer(VkPhysicalDevice physicalDevice, VkDevice device, VkQueue queue, + VkCommandPool commandPool) { + VkDeviceSize size = sizeof(indices[0]) * indices.size(); + + VkBuffer stagingBuffer; + VkDeviceMemory stagingBufferMemory; + + BufferCreateInfo stagingBufferInfo{ + &stagingBuffer, &stagingBufferMemory, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, size, + VK_BUFFER_USAGE_TRANSFER_SRC_BIT + }; + + createBuffer(physicalDevice, device, stagingBufferInfo); + + { + void *memory; + vkMapMemory(device, stagingBufferMemory, 0, size, 0, &memory); + memcpy(memory, indices.data(), size); + vkUnmapMemory(device, stagingBufferMemory); + } + + BufferCreateInfo indexBufferInfo{ + &mIndexBuffer, &mIndexBufferMemory, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, size, + VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT + }; + + createBuffer(physicalDevice, device, indexBufferInfo); + + copyBuffer(device, commandPool, queue, stagingBuffer, mIndexBuffer, size); + + destroyBuffer(device, stagingBuffer, stagingBufferMemory); +} + +void Renderer::createUniformBuffer(VkPhysicalDevice physicalDevice, VkDevice device) { + auto size = sizeof(CustomShader::UniformBuffer); + + BufferCreateInfo uniformCreateInfo{ + &mUniformBuffer, &mUniformBufferMemory, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, size, + VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT + }; + + createBuffer(physicalDevice, device, uniformCreateInfo); + + vkMapMemory(device, mUniformBufferMemory, 0, size, 0, &mUniformBufferMemoryMapped); +} + +void Renderer::createVertexBuffer(VkPhysicalDevice physicalDevice, VkDevice device, VkQueue queue, + VkCommandPool commandPool) { + size_t size = vertices.size() * sizeof(vertices[0]); + + VkBuffer stagingBuffer; + VkDeviceMemory stagingBufferMemory; + + BufferCreateInfo stagingInfo{ + &stagingBuffer, &stagingBufferMemory, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, size, + VK_BUFFER_USAGE_TRANSFER_SRC_BIT + }; + + createBuffer(physicalDevice, device, stagingInfo); + + { + void *memory; + vkMapMemory(device, stagingBufferMemory, 0, size, 0, &memory); + memcpy(memory, vertices.data(), size); + vkUnmapMemory(device, stagingBufferMemory); + } + + BufferCreateInfo vertexInfo{ + &mVertexBuffer, &mVertexBufferMemory, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, size, + VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT + }; + + createBuffer(physicalDevice, device, vertexInfo); + + copyBuffer(device, commandPool, queue, stagingBuffer, mVertexBuffer, size); + + destroyBuffer(device, stagingBuffer, stagingBufferMemory); +} + +void +Renderer::createBuffer(VkPhysicalDevice physicalDevice, VkDevice device, const BufferCreateInfo &bufferCreateInfo) { + + VkBufferCreateInfo createInfo{ + .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, + .size = bufferCreateInfo.size, + .usage = bufferCreateInfo.usage, + .sharingMode = VK_SHARING_MODE_EXCLUSIVE, + }; + + if (vkCreateBuffer(device, &createInfo, nullptr, bufferCreateInfo.buffer) != VK_SUCCESS) { + throw std::runtime_error("failed to create vertex buffer"); + } + + VkMemoryRequirements memoryRequirements; + vkGetBufferMemoryRequirements(device, *bufferCreateInfo.buffer, &memoryRequirements); + + VkMemoryAllocateInfo allocateInfo = { + .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, + .allocationSize = memoryRequirements.size, + .memoryTypeIndex = findMemoryType(physicalDevice, memoryRequirements.memoryTypeBits, bufferCreateInfo.properties), + }; + + if (vkAllocateMemory(device, &allocateInfo, nullptr, bufferCreateInfo.memory) != VK_SUCCESS) { + throw std::runtime_error("failed to allocate vertex buffer memory"); + } + + vkBindBufferMemory(device, *bufferCreateInfo.buffer, *bufferCreateInfo.memory, 0); +} + +void Renderer::destroyBuffer(VkDevice device, VkBuffer buffer, VkDeviceMemory memory) { + vkDestroyBuffer(device, buffer, nullptr); + vkFreeMemory(device, memory, nullptr); +} + +void +Renderer::populateGraphicsCommandBuffer(VkCommandBuffer commandBuffer, VkFramebuffer framebuffer, VkExtent2D extend) { + VkCommandBufferBeginInfo commandBufferBeginInfo{ + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, + }; + + if (vkBeginCommandBuffer(commandBuffer, &commandBufferBeginInfo) != VK_SUCCESS) { + throw std::runtime_error("failed to begin command buffer ="); + } + + VkClearValue clearColor{.color = {.float32 = {0.f, 0.f, 0.f, 1.f}}}; + + VkRenderPassBeginInfo renderPassBeginInfo{ + .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, + .renderPass = mGraphicsRenderPass, + .framebuffer = framebuffer, + .renderArea = { + .offset = {0, 0}, + .extent = extend, + }, + .clearValueCount = 1, + .pClearValues = &clearColor, + }; + + vkCmdBeginRenderPass(commandBuffer, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE); + vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, mGraphicsPipeline); + + VkViewport viewport{ + .x = 0, + .y = 0, + .width = (float) extend.width, + .height = (float) extend.height, + .minDepth = 0.f, + .maxDepth = 1.f, + }; + + VkRect2D scissor{ + .offset = {0, 0}, + .extent = extend, + }; + + vkCmdSetViewport(commandBuffer, 0, 1, &viewport); + vkCmdSetScissor(commandBuffer, 0, 1, &scissor); + + VkBuffer vertexBuffers[] = {mVertexBuffer}; + VkDeviceSize offsets[] = {0}; + vkCmdBindVertexBuffers(commandBuffer, 0, 1, vertexBuffers, offsets); + vkCmdBindIndexBuffer(commandBuffer, mIndexBuffer, 0, VK_INDEX_TYPE_UINT16); + + vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, mGraphicsPipelineLayout, 0, 1, + &mDescriptorSet, 0, + nullptr); + + vkCmdDrawIndexed(commandBuffer, indices.size(), 1, 0, 0, 0); + + vkCmdEndRenderPass(commandBuffer); + + if (vkEndCommandBuffer(commandBuffer) != VK_SUCCESS) { + throw std::runtime_error("failed to end command buffer"); + } +} + +uint32_t Renderer::findMemoryType(VkPhysicalDevice physicalDevice, uint32_t typeFilter, VkMemoryPropertyFlags flags) { + VkPhysicalDeviceMemoryProperties memoryProperties; + vkGetPhysicalDeviceMemoryProperties(physicalDevice, &memoryProperties); + + for (uint32_t i = 0; i < memoryProperties.memoryTypeCount; i++) { + if (typeFilter & (1 << i) && (memoryProperties.memoryTypes[i].propertyFlags & flags) == flags) { + return i; + } + } + + throw std::runtime_error("no suitable memory for vertex buffer found"); +} + +void Renderer::copyBuffer(VkDevice device, VkCommandPool commandPool, VkQueue queue, VkBuffer src, VkBuffer dst, + VkDeviceSize size) { + + VkCommandBufferAllocateInfo allocateInfo{ + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, + .commandPool = commandPool, + .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, + .commandBufferCount = 1 + }; + + VkCommandBuffer commandBuffer; + vkAllocateCommandBuffers(device, &allocateInfo, &commandBuffer); + + VkCommandBufferBeginInfo beginInfo{ + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, + .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, + }; + + vkBeginCommandBuffer(commandBuffer, &beginInfo); + + VkBufferCopy copyRegion{ + .srcOffset = 0, + .dstOffset = 0, + .size = size, + }; + + vkCmdCopyBuffer(commandBuffer, src, dst, 1, ©Region); + vkEndCommandBuffer(commandBuffer); + + VkSubmitInfo submitInfo{ + .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, + .commandBufferCount = 1, + .pCommandBuffers = &commandBuffer, + }; + + vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE); + vkQueueWaitIdle(queue); + + vkFreeCommandBuffers(device, commandPool, 1, &commandBuffer); +} \ No newline at end of file diff --git a/src/shader.cpp b/src/App/private/shader.cpp similarity index 100% rename from src/shader.cpp rename to src/App/private/shader.cpp diff --git a/shaders/compile.sh b/src/App/private/shaders/compile.sh similarity index 100% rename from shaders/compile.sh rename to src/App/private/shaders/compile.sh diff --git a/shaders/shader.frag b/src/App/private/shaders/shader.frag similarity index 100% rename from shaders/shader.frag rename to src/App/private/shaders/shader.frag diff --git a/shaders/shader.vert b/src/App/private/shaders/shader.vert similarity index 100% rename from shaders/shader.vert rename to src/App/private/shaders/shader.vert diff --git a/src/App/private/swapchain.cpp b/src/App/private/swapchain.cpp new file mode 100644 index 0000000..a6083cb --- /dev/null +++ b/src/App/private/swapchain.cpp @@ -0,0 +1,190 @@ +#include "swapchain.hpp" +#include +#include +#include + +void SwapChain::destroySwapChain(VkDevice device) { + for (const auto &buffer: mSwapChainFrameBuffers) { + vkDestroyFramebuffer(device, buffer, nullptr); + } + + for (const auto &imageView: mSwapChainImageViews) { + vkDestroyImageView(device, imageView, nullptr); + } + + vkDestroySwapchainKHR(device, mSwapChain, nullptr); +} + +void SwapChain::createSwapChain(const CreateInfo &info) { + SwapChainSupportDetails details = querySwapChainSupportDetails(info.physicalDevice, info.surface); + + VkSurfaceFormatKHR surfaceFormat = pickSwapChainSurfaceFormat(details); + VkPresentModeKHR presentMode = pickSwapChainPresentMode(details); + VkExtent2D extent2D = pickSwapChainExtent(details.capabilities, info.sizeX, info.sizeY); + + // +1 so will not have to wait for device to finish frame to query an image to render to + uint32_t imageCount = details.capabilities.minImageCount + 1; + + if (details.capabilities.maxImageCount > 0 && imageCount > details.capabilities.maxImageCount) { + imageCount = details.capabilities.maxImageCount; + } + + VkSwapchainCreateInfoKHR createInfo{ + .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, + .surface = info.surface, + .minImageCount = imageCount, + .imageFormat = surfaceFormat.format, + .imageExtent = extent2D, + .imageArrayLayers = 1, + .imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, + .preTransform = details.capabilities.currentTransform, + .compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, + .presentMode = presentMode, + .clipped = VK_TRUE, + .oldSwapchain = VK_NULL_HANDLE, + }; + + uint32_t queueIndices[] = {info.graphicsQueueFamilyIndex, info.presentationQueueFamilyIndex}; + + if (info.graphicsQueueFamilyIndex != info.presentationQueueFamilyIndex) { + createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT; + createInfo.queueFamilyIndexCount = 2; + createInfo.pQueueFamilyIndices = queueIndices; + } else { + createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; + } + + if (vkCreateSwapchainKHR(info.device, &createInfo, nullptr, &mSwapChain) != VK_SUCCESS) { + throw std::runtime_error("failed to create swap chain"); + } + + mSwapChainExtent = extent2D; + mSwapChainFormat = surfaceFormat.format; + + uint32_t createdImageCount = 0; + vkGetSwapchainImagesKHR(info.device, mSwapChain, &createdImageCount, nullptr); + mSwapChainImages.resize(createdImageCount); + vkGetSwapchainImagesKHR(info.device, mSwapChain, &createdImageCount, mSwapChainImages.data()); +} + + +void SwapChain::createSwapChainImageViews(VkDevice device) { + mSwapChainImageViews.resize(mSwapChainImages.size()); + + int i = 0; + for (const auto &image: mSwapChainImages) { + + VkImageViewCreateInfo createInfo{ + .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, + .image = image, + .viewType = VK_IMAGE_VIEW_TYPE_2D, + .format = mSwapChainFormat, + .components = { + .r = VK_COMPONENT_SWIZZLE_R, + .g = VK_COMPONENT_SWIZZLE_G, + .b = VK_COMPONENT_SWIZZLE_B, + .a = VK_COMPONENT_SWIZZLE_A, + }, + .subresourceRange = { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 1, + } + }; + + if (vkCreateImageView(device, &createInfo, nullptr, &mSwapChainImageViews[i]) != VK_SUCCESS) { + throw std::runtime_error("cannot create image view for the swap chain"); + } + + i++; + } +} + +void SwapChain::createSwapChainFramebuffers(VkDevice device, VkRenderPass renderPass) { + mSwapChainFrameBuffers.resize(mSwapChainImageViews.size()); + + for (size_t i = 0; i < mSwapChainImageViews.size(); i++) { + + VkImageView attachments[] = { + mSwapChainImageViews[i], + }; + + VkFramebufferCreateInfo framebufferCreateInfo{ + .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, + .renderPass = renderPass, + .attachmentCount = 1, + .pAttachments = attachments, + .width = mSwapChainExtent.width, + .height = mSwapChainExtent.height, + .layers = 1, + }; + + if (vkCreateFramebuffer(device, &framebufferCreateInfo, nullptr, &mSwapChainFrameBuffers[i]) != VK_SUCCESS) { + throw std::runtime_error("failed to create swapchain framebuffers"); + } + } +} + +VkSurfaceFormatKHR SwapChain::pickSwapChainSurfaceFormat(const SwapChainSupportDetails &details) { + for (const auto &format: details.formats) { + if (format.format == VK_FORMAT_B8G8R8A8_SRGB && format.colorSpace == VK_COLORSPACE_SRGB_NONLINEAR_KHR) { + return format; + } + } + + return details.formats.front(); +} + +VkPresentModeKHR SwapChain::pickSwapChainPresentMode(const SwapChainSupportDetails &details) { + for (const auto &mode: details.presentModes) { + if (mode == VK_PRESENT_MODE_MAILBOX_KHR) { + return mode; + } + } + + return VK_PRESENT_MODE_FIFO_KHR; // guaranteed exists +} + +VkExtent2D SwapChain::pickSwapChainExtent(const VkSurfaceCapabilitiesKHR &capabilities, int sizeX, int sizeY) { + // if set by vulkan just keep it + if (capabilities.currentExtent.width != std::numeric_limits::max()) { + return capabilities.currentExtent; + } + + VkExtent2D out = {(uint32_t) sizeX, (uint32_t) sizeY}; + + out.width = std::clamp(out.width, capabilities.minImageExtent.width, capabilities.maxImageExtent.width); + out.height = std::clamp(out.height, capabilities.minImageExtent.height, capabilities.maxImageExtent.height); + + return out; +} + +SwapChain::SwapChainSupportDetails +SwapChain::querySwapChainSupportDetails(VkPhysicalDevice device, VkSurfaceKHR surface) { + SwapChainSupportDetails details; + + vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, surface, &details.capabilities); + + uint32_t formatCount = 0; + vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, nullptr); + if (formatCount != 0) { + details.formats.resize(formatCount); + vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, details.formats.data()); + } + + uint32_t modeCount = 0; + vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &modeCount, nullptr); + if (modeCount != 0) { + details.presentModes.resize(modeCount); + vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &modeCount, details.presentModes.data()); + } + + return details; +} + +bool SwapChain::checkDeviceSwapChain(VkPhysicalDevice device, VkSurfaceKHR surface) { + SwapChainSupportDetails details = querySwapChainSupportDetails(device, surface); + return !(details.presentModes.empty() || details.formats.empty()); +} diff --git a/src/App/private/vulkan_utils.cpp b/src/App/private/vulkan_utils.cpp new file mode 100644 index 0000000..b7c3d92 --- /dev/null +++ b/src/App/private/vulkan_utils.cpp @@ -0,0 +1,86 @@ +#include "vulkan_utils.hpp" + +#include +#include + +bool checkValidationLayerSupport(const std::vector& validationLayers) { + uint32_t layerCount; + vkEnumerateInstanceLayerProperties(&layerCount, nullptr); + + std::vector availableLayers(layerCount); + vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data()); + + for (const auto &requestedLayer: validationLayers) { + bool presents = false; + for (auto &layer: availableLayers) { + if (strcmp(requestedLayer, layer.layerName) == 0) { + presents = true; + break; + } + } + + if (!presents) return false; + } + + return true; +} + +bool checkDeviceExtensions(VkPhysicalDevice device, const std::vector& deviceExtensions) { + uint32_t extensionsCount = 0; + vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionsCount, nullptr); + + std::vector extensions(extensionsCount); + vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionsCount, extensions.data()); + + for (const auto &requiredExtension: deviceExtensions) { + bool found = false; + for (const auto &extension: extensions) { + if (strcmp(requiredExtension, extension.extensionName) == 0) { + found = true; + break; + } + } + if (!found) return false; + } + + return true; +} + +VkInstance createInstance(const char ** instanceExtensions, uint32_t extensionsCount) { + VkInstance out = VK_NULL_HANDLE; + + const std::vector validationLayers = { +#ifdef NDEBUG +#else + "VK_LAYER_KHRONOS_validation" +#endif + }; + + if (!checkValidationLayerSupport(validationLayers)) { + throw std::runtime_error("no required validation layers present"); + } + + VkApplicationInfo appInfo{ + .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, + .pApplicationName = "App", + .applicationVersion = VK_MAKE_VERSION(0, 0, 0), + .pEngineName = "no", + .engineVersion = VK_MAKE_VERSION(0, 0, 0), + .apiVersion = VK_API_VERSION_1_0, + }; + + VkInstanceCreateInfo createInfo{ + .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, + .pApplicationInfo = &appInfo, + .enabledLayerCount = (uint32_t) validationLayers.size(), + .ppEnabledLayerNames = validationLayers.data(), + .enabledExtensionCount = extensionsCount, + .ppEnabledExtensionNames = instanceExtensions, + }; + + if (vkCreateInstance(&createInfo, nullptr, &out) != VK_SUCCESS) { + throw std::runtime_error("failed to create instance!"); + } + + return out; +} \ No newline at end of file diff --git a/inc/common.hpp b/src/App/public/common.hpp similarity index 99% rename from inc/common.hpp rename to src/App/public/common.hpp index 72089d6..2b4566a 100644 --- a/inc/common.hpp +++ b/src/App/public/common.hpp @@ -3,7 +3,6 @@ #define GLM_FORCE_RADIANS #include #include - #include struct Vertex { diff --git a/src/App/public/renderer.hpp b/src/App/public/renderer.hpp new file mode 100644 index 0000000..9688680 --- /dev/null +++ b/src/App/public/renderer.hpp @@ -0,0 +1,77 @@ +#pragma once + +#include "shader.hpp" +#include + +struct Renderer { + CustomShader mShader; + VkPipeline mGraphicsPipeline = VK_NULL_HANDLE; + VkRenderPass mGraphicsRenderPass = VK_NULL_HANDLE; + VkPipelineLayout mGraphicsPipelineLayout{}; // no uniforms used in the shader + + // Buffers + VkBuffer mVertexBuffer = VK_NULL_HANDLE; + VkDeviceMemory mVertexBufferMemory = VK_NULL_HANDLE; + + VkBuffer mIndexBuffer = VK_NULL_HANDLE; + VkDeviceMemory mIndexBufferMemory = VK_NULL_HANDLE; + + VkDescriptorPool mDescriptorPool = VK_NULL_HANDLE; + VkDescriptorSet mDescriptorSet = VK_NULL_HANDLE; + + VkBuffer mUniformBuffer = VK_NULL_HANDLE; + VkDeviceMemory mUniformBufferMemory = VK_NULL_HANDLE; + void* mUniformBufferMemoryMapped = nullptr; + + struct CreateInfo { + VkDevice device; + VkPhysicalDevice physicalDevice; + VkExtent2D extent2D; + VkFormat format; + + VkCommandPool commandPool; + VkQueue queue; + }; + + void create(const CreateInfo& info); + + void destroy(VkDevice device) const; + + void createDescriptorPool(VkDevice device); + + void createDescriptorSets(VkDevice device); + + void createGraphicsPipeline(VkDevice device, VkExtent2D extent2D); + + void createRenderPass(VkDevice device, VkFormat format); + + void + createIndexBuffer(VkPhysicalDevice physicalDevice, VkDevice device, VkQueue queue, VkCommandPool commandPool); + + void + createVertexBuffer(VkPhysicalDevice physicalDevice, VkDevice device, VkQueue queue, VkCommandPool commandPool); + + void createUniformBuffer(VkPhysicalDevice physicalDevice, VkDevice device); + + + void populateGraphicsCommandBuffer(VkCommandBuffer commandBuffer, VkFramebuffer framebuffer, VkExtent2D extend); + + struct BufferCreateInfo { + VkBuffer *buffer; + VkDeviceMemory *memory; + VkMemoryPropertyFlags properties; + VkDeviceSize size; + VkBufferUsageFlags usage; + }; + + static void createBuffer(VkPhysicalDevice physicalDevice, VkDevice device, const BufferCreateInfo& createInfo); + + static uint32_t findMemoryType(VkPhysicalDevice physicalDevice, uint32_t typeFilter, VkMemoryPropertyFlags flags); + + static void copyBuffer(VkDevice device, VkCommandPool commandPool, VkQueue queue, VkBuffer src, VkBuffer dst, + VkDeviceSize size); + + static void destroyBuffer(VkDevice device, VkBuffer buffer, VkDeviceMemory memory); + + void destroyGraphicsPipeline(VkDevice device) const; +}; \ No newline at end of file diff --git a/inc/shader.hpp b/src/App/public/shader.hpp similarity index 100% rename from inc/shader.hpp rename to src/App/public/shader.hpp diff --git a/src/App/public/swapchain.hpp b/src/App/public/swapchain.hpp new file mode 100644 index 0000000..75d7b86 --- /dev/null +++ b/src/App/public/swapchain.hpp @@ -0,0 +1,48 @@ +#pragma once + +#include +#include + +struct SwapChain { + + struct SwapChainSupportDetails { + VkSurfaceCapabilitiesKHR capabilities{}; + std::vector formats; + std::vector presentModes; + }; + + VkSwapchainKHR mSwapChain = VK_NULL_HANDLE; + VkFormat mSwapChainFormat{}; + VkExtent2D mSwapChainExtent{}; + std::vector mSwapChainImages; + std::vector mSwapChainImageViews; + std::vector mSwapChainFrameBuffers; + + void destroySwapChain(VkDevice device); + + struct CreateInfo { + VkDevice device = VK_NULL_HANDLE; + VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; + VkSurfaceKHR surface = VK_NULL_HANDLE; + int sizeX = 0; + int sizeY = 0; + uint32_t graphicsQueueFamilyIndex = -1; + uint32_t presentationQueueFamilyIndex = -1; + }; + + void createSwapChain(const CreateInfo &info); + + void createSwapChainImageViews(VkDevice device); + + void createSwapChainFramebuffers(VkDevice device, VkRenderPass renderPass); + + static VkSurfaceFormatKHR pickSwapChainSurfaceFormat(const SwapChainSupportDetails &details); + + static VkPresentModeKHR pickSwapChainPresentMode(const SwapChainSupportDetails &details); + + static VkExtent2D pickSwapChainExtent(const VkSurfaceCapabilitiesKHR &capabilities, int sizeX, int sizeY); + + static SwapChainSupportDetails querySwapChainSupportDetails(VkPhysicalDevice device, VkSurfaceKHR surface); + + static bool checkDeviceSwapChain(VkPhysicalDevice device, VkSurfaceKHR surface); +}; \ No newline at end of file diff --git a/src/App/public/vulkan_utils.hpp b/src/App/public/vulkan_utils.hpp new file mode 100644 index 0000000..a05428d --- /dev/null +++ b/src/App/public/vulkan_utils.hpp @@ -0,0 +1,9 @@ +#pragma once + +#include +#include + +bool checkValidationLayerSupport(const std::vector& validationLayers); +bool checkDeviceExtensions(VkPhysicalDevice device, const std::vector& deviceExtensions); + +VkInstance createInstance(const char ** instanceExtensions, uint32_t extensionsCount); \ No newline at end of file diff --git a/src/Common/CMakeLists.txt b/src/Common/CMakeLists.txt new file mode 100644 index 0000000..e28d6c4 --- /dev/null +++ b/src/Common/CMakeLists.txt @@ -0,0 +1,6 @@ +cmake_minimum_required(VERSION 3.30) + +project(Common) + +add_library(${PROJECT_NAME} utils.cpp) +target_include_directories(${PROJECT_NAME} PUBLIC .) diff --git a/src/utils.cpp b/src/Common/utils.cpp similarity index 100% rename from src/utils.cpp rename to src/Common/utils.cpp diff --git a/inc/utils.hpp b/src/Common/utils.hpp similarity index 100% rename from inc/utils.hpp rename to src/Common/utils.hpp diff --git a/src/GPU/CMakeLists.txt b/src/GPU/CMakeLists.txt new file mode 100644 index 0000000..45c72e6 --- /dev/null +++ b/src/GPU/CMakeLists.txt @@ -0,0 +1,7 @@ +cmake_minimum_required(VERSION 3.30) + +project(GPU) + +add_library(${PROJECT_NAME} private/GPU.cpp) +target_include_directories(${PROJECT_NAME} PUBLIC public) +target_link_libraries(${PROJECT_NAME} PUBLIC vulkan Common) \ No newline at end of file diff --git a/src/GPU/private/GPU.cpp b/src/GPU/private/GPU.cpp new file mode 100644 index 0000000..e69de29 diff --git a/src/GPU/public/GPU.hpp b/src/GPU/public/GPU.hpp new file mode 100644 index 0000000..7b9637e --- /dev/null +++ b/src/GPU/public/GPU.hpp @@ -0,0 +1 @@ +#pragma once \ No newline at end of file diff --git a/src/PlatformWindow/CMakeLists.txt b/src/PlatformWindow/CMakeLists.txt new file mode 100644 index 0000000..bf50477 --- /dev/null +++ b/src/PlatformWindow/CMakeLists.txt @@ -0,0 +1,7 @@ +cmake_minimum_required(VERSION 3.30) + +project(PlatformWindow) + +add_library(${PROJECT_NAME} private/PlatformWindow.cpp) +target_include_directories(${PROJECT_NAME} PUBLIC public) +target_link_libraries(${PROJECT_NAME} PUBLIC glfw GPU) \ No newline at end of file diff --git a/src/PlatformWindow/private/PlatformWindow.cpp b/src/PlatformWindow/private/PlatformWindow.cpp new file mode 100644 index 0000000..e69de29 diff --git a/src/PlatformWindow/public/PlatformWindow.hpp b/src/PlatformWindow/public/PlatformWindow.hpp new file mode 100644 index 0000000..7b9637e --- /dev/null +++ b/src/PlatformWindow/public/PlatformWindow.hpp @@ -0,0 +1 @@ +#pragma once \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp deleted file mode 100644 index a4c40d0..0000000 --- a/src/main.cpp +++ /dev/null @@ -1,1151 +0,0 @@ -#include "shader.hpp" -#include "utils.hpp" - -#include -#include - -#include -#include -#include -#include -#include - - -const std::vector gDeviceExtensions = { - VK_KHR_SWAPCHAIN_EXTENSION_NAME, -}; - -const std::vector gValidationLayers = { -#ifdef NDEBUG -#else - "VK_LAYER_KHRONOS_validation" -#endif -}; - -struct SwapChainSupportDetails { - VkSurfaceCapabilitiesKHR capabilities{}; - std::vector formats; - std::vector presentModes; -}; - -class Application { -public: - void run() { - initWindow(); - initVulkan(); - mainLoop(); - cleanup(); - } - - void scheduleWindowResize(int sizeX, int sizeY) { - mWindowSizeDirtyFlagTime = std::chrono::system_clock::now(); - mWindowSizeDirtyFlag = true; - mWindowFramebufferSize = std::make_pair(sizeX, sizeY); - assert(!(sizeX <= 0 || sizeY <= 0)); - } - -private: - void initWindow() { - // glfwInitHint(GLFW_PLATFORM, GLFW_PLATFORM_X11); - glfwInit(); - - glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); - // glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); - mWindow = glfwCreateWindow(800, 600, "App", nullptr, nullptr); - scheduleWindowResize(800, 600); - - glfwSetWindowUserPointer(mWindow, this); - glfwSetFramebufferSizeCallback(mWindow, [](GLFWwindow* window, int sizeX, int sizeY){ - ((Application*) glfwGetWindowUserPointer(window))->scheduleWindowResize(sizeX, sizeY); - }); - } - - void initVulkan() { - createInstance(); - createWindowSurface(); - pickPhysicalDevice(); - findPhysicalDeviceQueueFamilies(); - createLogicalDevice(); - getQueues(); - - int width, height; - glfwGetFramebufferSize(mWindow, &width, &height); - - createSwapChain(width, height); - createSwapChainImageViews(); - - mShader.create(mDevice); - - createRenderPass(); - createGraphicsPipeline(); - - createSwapChainFramebuffers(); - - createCommandPool(); - createCommandBuffer(); - - createSynchronizationObjects(); - - createVertexBuffer(); - createIndexBuffer(); - createUniformBuffer(); - - createDescriptorPool(); - createDescriptorSets(); - } - - void mainLoop() { - while (!glfwWindowShouldClose(mWindow)) { - glfwPollEvents(); - - if (mWindowSizeDirtyFlag) { - auto timeDelayMs = timeDeltaMs(mWindowSizeDirtyFlagTime); - if (timeDelayMs > mWindowSizeApplyMinDelay) { - recreateSwapChain(mWindowFramebufferSize.first, mWindowFramebufferSize.second); - mWindowSizeDirtyFlag = false; - } - } - - drawFrame(); - } - - vkDeviceWaitIdle(mDevice); - } - - void createInstance() { - if (!checkValidationLayerSupport()) { - throw std::runtime_error("no required validation layers present"); - } - - uint32_t glfwExtensionCount = 0; - const char **glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount); - - VkApplicationInfo appInfo{ - .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, - .pApplicationName = "App", - .applicationVersion = VK_MAKE_VERSION(0, 0, 0), - .pEngineName = "no", - .engineVersion = VK_MAKE_VERSION(0, 0, 0), - .apiVersion = VK_API_VERSION_1_0, - }; - - VkInstanceCreateInfo createInfo{ - .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, - .pApplicationInfo = &appInfo, - .enabledLayerCount = (uint32_t) gValidationLayers.size(), - .ppEnabledLayerNames = gValidationLayers.data(), - .enabledExtensionCount = glfwExtensionCount, - .ppEnabledExtensionNames = glfwExtensions, - }; - - if (vkCreateInstance(&createInfo, nullptr, &mInstance) != VK_SUCCESS) { - throw std::runtime_error("failed to create instance!"); - } - } - - static bool checkValidationLayerSupport() { - uint32_t layerCount; - vkEnumerateInstanceLayerProperties(&layerCount, nullptr); - - std::vector availableLayers(layerCount); - vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data()); - - for (const auto &requestedLayer: gValidationLayers) { - bool presents = false; - for (auto &layer: availableLayers) { - if (strcmp(requestedLayer, layer.layerName) == 0) { - presents = true; - break; - } - } - - if (!presents) return false; - } - - return true; - } - - void pickPhysicalDevice() { - uint32_t deviceCount = 0; - vkEnumeratePhysicalDevices(mInstance, &deviceCount, nullptr); - - if (deviceCount == 0) { - throw std::runtime_error("no gpu with vulkan support"); - } - - std::vector devices(deviceCount); - vkEnumeratePhysicalDevices(mInstance, &deviceCount, devices.data()); - - for (const auto &device: devices) { - if (isDeviceSuitable(device)) { - mPhysicalDevice = device; - break; - } - } - - if (!mPhysicalDevice) throw std::runtime_error("no suitable gpu"); - } - - bool isDeviceSuitable(VkPhysicalDevice device) { - VkPhysicalDeviceProperties properties; - VkPhysicalDeviceFeatures features; - - vkGetPhysicalDeviceProperties(device, &properties); - vkGetPhysicalDeviceFeatures(device, &features); - - // if (properties.deviceType != VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) return false; - if (!features.geometryShader) return false; - if (!checkDeviceExtensions(device)) return false; - if (!checkDeviceSwapChain(device)) return false; - - return true; - } - - bool checkDeviceSwapChain(VkPhysicalDevice device) { - SwapChainSupportDetails details = querySwapChainSupportDetails(device); - return !(details.presentModes.empty() || details.formats.empty()); - } - - static bool checkDeviceExtensions(VkPhysicalDevice device) { - uint32_t extensionsCount = 0; - vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionsCount, nullptr); - - std::vector extensions(extensionsCount); - vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionsCount, extensions.data()); - - for (const auto &requiredExtension: gDeviceExtensions) { - bool found = false; - for (const auto &extension: extensions) { - if (strcmp(requiredExtension, extension.extensionName) == 0) { - found = true; - break; - } - } - if (!found) return false; - } - - return true; - } - - void findPhysicalDeviceQueueFamilies() { - uint32_t queuesCount = 0; - vkGetPhysicalDeviceQueueFamilyProperties(mPhysicalDevice, &queuesCount, nullptr); - - std::vector queueFamilyProperties(queuesCount); - vkGetPhysicalDeviceQueueFamilyProperties(mPhysicalDevice, &queuesCount, queueFamilyProperties.data()); - - int index = 0; - for (const auto &familyProperty: queueFamilyProperties) { - if (familyProperty.queueFlags & VK_QUEUE_GRAPHICS_BIT) { - mGraphicsQueueFamilyIndex = index; - } - - VkBool32 presentationQueueSupport = false; - vkGetPhysicalDeviceSurfaceSupportKHR(mPhysicalDevice, index, mSurface, &presentationQueueSupport); - - if (presentationQueueSupport) { - mPresentationQueueFamilyIndex = index; - } - - index++; - } - - if (mPresentationQueueFamilyIndex == -1 || mGraphicsQueueFamilyIndex == -1) { - throw std::runtime_error("nu require queue families found"); - } - } - - SwapChainSupportDetails querySwapChainSupportDetails(VkPhysicalDevice device) { - SwapChainSupportDetails details; - - vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, mSurface, &details.capabilities); - - uint32_t formatCount = 0; - vkGetPhysicalDeviceSurfaceFormatsKHR(device, mSurface, &formatCount, nullptr); - if (formatCount != 0) { - details.formats.resize(formatCount); - vkGetPhysicalDeviceSurfaceFormatsKHR(device, mSurface, &formatCount, details.formats.data()); - } - - uint32_t modeCount = 0; - vkGetPhysicalDeviceSurfacePresentModesKHR(device, mSurface, &modeCount, nullptr); - if (modeCount != 0) { - details.presentModes.resize(modeCount); - vkGetPhysicalDeviceSurfacePresentModesKHR(device, mSurface, &modeCount, details.presentModes.data()); - } - - return details; - } - - void createLogicalDevice() { - float queuePriority = 1.f; - - VkDeviceQueueCreateInfo graphicsQueueCreateInfos{ - .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, - .queueFamilyIndex = mGraphicsQueueFamilyIndex, - .queueCount = 1, - .pQueuePriorities = &queuePriority, - }; - - VkDeviceQueueCreateInfo presentationQueueCreateInfos{ - .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, - .queueFamilyIndex = mPresentationQueueFamilyIndex, - .queueCount = 1, - .pQueuePriorities = &queuePriority, - }; - - std::vector queues = {graphicsQueueCreateInfos}; - - if (mPresentationQueueFamilyIndex != mGraphicsQueueFamilyIndex) { - queues.push_back(presentationQueueCreateInfos); - } - - VkPhysicalDeviceFeatures features{}; - - VkDeviceCreateInfo deviceCreateInfo{ - .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, - .queueCreateInfoCount = (uint32_t) queues.size(), - .pQueueCreateInfos = queues.data(), - .enabledExtensionCount = (uint32_t) gDeviceExtensions.size(), - .ppEnabledExtensionNames = gDeviceExtensions.data(), - .pEnabledFeatures = &features, - }; - - vkCreateDevice(mPhysicalDevice, &deviceCreateInfo, nullptr, &mDevice); - - if (mDevice == VK_NULL_HANDLE) throw std::runtime_error("failed to create vulkan logical device"); - } - - void getQueues() { - vkGetDeviceQueue(mDevice, mGraphicsQueueFamilyIndex, 0, &mGraphicsQueue); - vkGetDeviceQueue(mDevice, mPresentationQueueFamilyIndex, 0, &mPresentQueue); - } - - - void createWindowSurface() { - if (glfwCreateWindowSurface(mInstance, mWindow, nullptr, &mSurface) != VK_SUCCESS) { - throw std::runtime_error("failed to create vulkan window surface"); - } - } - - void recreateSwapChain(int sizeX, int sizeY) { - vkDeviceWaitIdle(mDevice); - - destroySwapChainFramebuffers(); - destroySwapChainImageViews(); - destroySwapChain(); - - createSwapChain(sizeX, sizeY); - createSwapChainImageViews(); - createSwapChainFramebuffers(); - } - - void createSwapChain(int sizeX, int sizeY) { - SwapChainSupportDetails details = querySwapChainSupportDetails(mPhysicalDevice); - - VkSurfaceFormatKHR surfaceFormat = pickSwapChainSurfaceFormat(details); - VkPresentModeKHR presentMode = pickSwapChainPresentMode(details); - VkExtent2D extent2D = pickSwapChainExtent(details.capabilities, sizeX, sizeY); - - // +1 so will not have to wait for device to finish frame to query an image to render to - uint32_t imageCount = details.capabilities.minImageCount + 1; - - if (details.capabilities.maxImageCount > 0 && imageCount > details.capabilities.maxImageCount) { - imageCount = details.capabilities.maxImageCount; - } - - VkSwapchainCreateInfoKHR createInfo{ - .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, - .surface = mSurface, - .minImageCount = imageCount, - .imageFormat = surfaceFormat.format, - .imageExtent = extent2D, - .imageArrayLayers = 1, - .imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, - .preTransform = details.capabilities.currentTransform, - .compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, - .presentMode = presentMode, - .clipped = VK_TRUE, - .oldSwapchain = VK_NULL_HANDLE, - }; - - uint32_t queueIndices[] = {mGraphicsQueueFamilyIndex, mPresentationQueueFamilyIndex}; - - if (mGraphicsQueueFamilyIndex != mPresentationQueueFamilyIndex) { - createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT; - createInfo.queueFamilyIndexCount = 2; - createInfo.pQueueFamilyIndices = queueIndices; - } else { - createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; - } - - if (vkCreateSwapchainKHR(mDevice, &createInfo, nullptr, &mSwapChain) != VK_SUCCESS) { - throw std::runtime_error("failed to create swap chain"); - } - - mSwapChainExtent = extent2D; - mSwapChainFormat = surfaceFormat.format; - - uint32_t createdImageCount = 0; - vkGetSwapchainImagesKHR(mDevice, mSwapChain, &createdImageCount, nullptr); - mSwapChainImages.resize(createdImageCount); - vkGetSwapchainImagesKHR(mDevice, mSwapChain, &createdImageCount, mSwapChainImages.data()); - } - - static VkSurfaceFormatKHR pickSwapChainSurfaceFormat(const SwapChainSupportDetails &details) { - for (const auto &format: details.formats) { - if (format.format == VK_FORMAT_B8G8R8A8_SRGB && format.colorSpace == VK_COLORSPACE_SRGB_NONLINEAR_KHR) { - return format; - } - } - - return details.formats.front(); - } - - static VkPresentModeKHR pickSwapChainPresentMode(const SwapChainSupportDetails &details) { - for (const auto &mode: details.presentModes) { - if (mode == VK_PRESENT_MODE_MAILBOX_KHR) { - return mode; - } - } - - return VK_PRESENT_MODE_FIFO_KHR; // guaranteed exists - } - - static VkExtent2D pickSwapChainExtent(const VkSurfaceCapabilitiesKHR &capabilities, int sizeX, int sizeY) { - // if set by vulkan just keep it - if (capabilities.currentExtent.width != std::numeric_limits::max()) { - return capabilities.currentExtent; - } - - VkExtent2D out = {(uint32_t) sizeX, (uint32_t) sizeY}; - - out.width = std::clamp(out.width, capabilities.minImageExtent.width, capabilities.maxImageExtent.width); - out.height = std::clamp(out.height, capabilities.minImageExtent.height, capabilities.maxImageExtent.height); - - return out; - } - - void createSwapChainImageViews() { - mSwapChainImageViews.resize(mSwapChainImages.size()); - - int i = 0; - for (const auto &image: mSwapChainImages) { - - VkImageViewCreateInfo createInfo{ - .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, - .image = image, - .viewType = VK_IMAGE_VIEW_TYPE_2D, - .format = mSwapChainFormat, - .components = { - .r = VK_COMPONENT_SWIZZLE_R, - .g = VK_COMPONENT_SWIZZLE_G, - .b = VK_COMPONENT_SWIZZLE_B, - .a = VK_COMPONENT_SWIZZLE_A, - }, - .subresourceRange = { - .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .baseMipLevel = 0, - .levelCount = 1, - .baseArrayLayer = 0, - .layerCount = 1, - } - }; - - if (vkCreateImageView(mDevice, &createInfo, nullptr, &mSwapChainImageViews[i]) != VK_SUCCESS) { - throw std::runtime_error("cannot create image view for the swap chain"); - } - - i++; - } - } - - void createSwapChainFramebuffers() { - mSwapChainFrameBuffers.resize(mSwapChainImageViews.size()); - - for (size_t i = 0; i < mSwapChainImageViews.size(); i++) { - - VkImageView attachments[] = { - mSwapChainImageViews[i], - }; - - VkFramebufferCreateInfo framebufferCreateInfo{ - .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, - .renderPass = mGraphicsRenderPass, - .attachmentCount = 1, - .pAttachments = attachments, - .width = mSwapChainExtent.width, - .height = mSwapChainExtent.height, - .layers = 1, - }; - - if (vkCreateFramebuffer(mDevice, &framebufferCreateInfo, nullptr, &mSwapChainFrameBuffers[i]) != VK_SUCCESS) { - throw std::runtime_error("failed to create swapchain framebuffers"); - } - } - } - - void destroySwapChainFramebuffers() { - for (const auto &buffer: mSwapChainFrameBuffers) { - vkDestroyFramebuffer(mDevice, buffer, nullptr); - } - } - - void destroySwapChain() { - vkDestroySwapchainKHR(mDevice, mSwapChain, nullptr); - } - - void destroySwapChainImageViews() { - for (const auto &imageView: mSwapChainImageViews) { - vkDestroyImageView(mDevice, imageView, nullptr); - } - } - - void createDescriptorPool() { - VkDescriptorPoolSize poolSize { - .type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, - .descriptorCount = 1, - }; - - VkDescriptorPoolCreateInfo createInfo { - .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, - .maxSets = 1, - .poolSizeCount = 1, - .pPoolSizes = &poolSize, - }; - - if (vkCreateDescriptorPool(mDevice, &createInfo, nullptr, &mDescriptorPool) != VK_SUCCESS) { - throw std::runtime_error("failed to create descriptor pool"); - } - } - - void createDescriptorSets() { - VkDescriptorSetAllocateInfo allocateInfo { - .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, - .descriptorPool = mDescriptorPool, - .descriptorSetCount = 1, - .pSetLayouts = &mShader.mDescriptorSetLayout, - }; - - if (vkAllocateDescriptorSets(mDevice, &allocateInfo, &mDescriptorSet) != VK_SUCCESS) { - throw std::runtime_error("failed to allocate descriptor set"); - } - - VkDescriptorBufferInfo bufferInfo { - .buffer = mUniformBuffer, - .offset = 0, - .range = sizeof(CustomShader::UniformBuffer), - }; - - VkWriteDescriptorSet writeDescriptorSet { - .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, - .dstSet = mDescriptorSet, - .dstBinding = 0, - .dstArrayElement = 0, - .descriptorCount = 1, - .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, - .pBufferInfo = &bufferInfo, - }; - - vkUpdateDescriptorSets(mDevice, 1, &writeDescriptorSet, 0, nullptr); - } - - void createGraphicsPipeline() { - - std::vector dynamicStates = { - VK_DYNAMIC_STATE_VIEWPORT, - VK_DYNAMIC_STATE_SCISSOR, - }; - - VkPipelineDynamicStateCreateInfo dynamicStateCreateInfo = { - .sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, - .dynamicStateCount = (uint32_t) dynamicStates.size(), - .pDynamicStates = dynamicStates.data(), - }; - - VkPipelineVertexInputStateCreateInfo vertexInputStateCreateInfo{ - .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, - .vertexBindingDescriptionCount = 1, - .pVertexBindingDescriptions = &mShader.mVertexInputDescription, - .vertexAttributeDescriptionCount = (uint32_t) mShader.mVertexAttributes.size(), - .pVertexAttributeDescriptions = mShader.mVertexAttributes.data(), - }; - - VkPipelineInputAssemblyStateCreateInfo inputAssemblyStateCreateInfo{ - .sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, - .topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, - .primitiveRestartEnable = VK_FALSE, - }; - - VkViewport viewport{ - .x = 0, - .y = 0, - .width = (float) mSwapChainExtent.width, - .height = (float) mSwapChainExtent.height, - .minDepth = 0.f, - .maxDepth = 1.f, - }; - - VkRect2D scissor{ - .offset = {0, 0}, - .extent = mSwapChainExtent, - }; - - VkPipelineViewportStateCreateInfo viewportState{ - .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, - .viewportCount = 1, - .pViewports = &viewport, - .scissorCount = 1, - .pScissors = &scissor, - }; - - VkPipelineRasterizationStateCreateInfo rasterizationStateCreateInfo{ - .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, - .depthClampEnable = VK_FALSE, - .rasterizerDiscardEnable = VK_FALSE, - .polygonMode = VK_POLYGON_MODE_FILL, - .cullMode = VK_CULL_MODE_BACK_BIT, - .frontFace = VK_FRONT_FACE_CLOCKWISE, - - .depthBiasEnable = VK_FALSE, - .depthBiasConstantFactor = 0.f, - .depthBiasClamp = 0.f, - .depthBiasSlopeFactor = 0.f, - - .lineWidth = 1, - }; - - VkPipelineMultisampleStateCreateInfo multisampleStateCreateInfo{ - .sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, - .rasterizationSamples = VK_SAMPLE_COUNT_1_BIT, - .sampleShadingEnable = VK_FALSE, - }; - - VkPipelineColorBlendAttachmentState colorBlendAttachmentState{ - .blendEnable = VK_FALSE, - .colorWriteMask = (VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | - VK_COLOR_COMPONENT_A_BIT), - }; - - VkPipelineColorBlendStateCreateInfo colorBlendStateCreateInfo{ - .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, - .logicOpEnable = VK_FALSE, - .attachmentCount = 1, - .pAttachments = &colorBlendAttachmentState, - }; - - VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo{ - .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, - .setLayoutCount = 1, - .pSetLayouts = &mShader.mDescriptorSetLayout, - }; - - if (vkCreatePipelineLayout(mDevice, &pipelineLayoutCreateInfo, nullptr, &mGraphicsPipelineLayout) != VK_SUCCESS) { - throw std::runtime_error("failed to create pipeline layout"); - } - - VkGraphicsPipelineCreateInfo graphicsPipelineCreateInfo{ - .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, - .stageCount = 2, - .pStages = mShader.mStageCreateInfos.data(), - .pVertexInputState = &vertexInputStateCreateInfo, - .pInputAssemblyState = &inputAssemblyStateCreateInfo, - .pViewportState = &viewportState, - .pRasterizationState = &rasterizationStateCreateInfo, - .pMultisampleState = &multisampleStateCreateInfo, - .pColorBlendState = &colorBlendStateCreateInfo, - .pDynamicState = &dynamicStateCreateInfo, - .layout = mGraphicsPipelineLayout, - .renderPass = mGraphicsRenderPass, - .subpass = 0, - }; - - if (vkCreateGraphicsPipelines(mDevice, nullptr, 1, &graphicsPipelineCreateInfo, nullptr, &mGraphicsPipeline) != - VK_SUCCESS) { - throw std::runtime_error("failed to create graphics pipeline"); - } - } - - void createRenderPass() { - VkAttachmentDescription colorAttachment{ - .format = mSwapChainFormat, - .samples = VK_SAMPLE_COUNT_1_BIT, - .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR, - .storeOp = VK_ATTACHMENT_STORE_OP_STORE, - .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE, - .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE, - .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, - .finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, - }; - - VkAttachmentReference colorAttachmentReference{ - .attachment = 0, - .layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, - }; - - VkSubpassDescription subpassDescription{ - .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS, - .colorAttachmentCount = 1, - .pColorAttachments = &colorAttachmentReference, - }; - - VkSubpassDependency dependency { - .srcSubpass = VK_SUBPASS_EXTERNAL, - .dstSubpass = 0, - .srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, - .dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, - .srcAccessMask = 0, - .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, - }; - - VkRenderPassCreateInfo renderPassCreateInfo{ - .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, - .attachmentCount = 1, - .pAttachments = &colorAttachment, - .subpassCount = 1, - .pSubpasses = &subpassDescription, - .dependencyCount = 1, - .pDependencies = &dependency - }; - - if (vkCreateRenderPass(mDevice, &renderPassCreateInfo, nullptr, &mGraphicsRenderPass) != VK_SUCCESS) { - throw std::runtime_error("failed to create render pass"); - } - } - - void destroyGraphicsPipeline() { - mShader.destroy(mDevice); - vkDestroyPipelineLayout(mDevice, mGraphicsPipelineLayout, nullptr); - vkDestroyPipeline(mDevice, mGraphicsPipeline, nullptr); - } - - void createCommandPool() { - VkCommandPoolCreateInfo createInfo{ - .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, - .flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, - .queueFamilyIndex = mGraphicsQueueFamilyIndex, - }; - - if (vkCreateCommandPool(mDevice, &createInfo, nullptr, &mCommandPool) != VK_SUCCESS) { - throw std::runtime_error("failed to create command pool"); - } - } - - void createCommandBuffer() { - VkCommandBufferAllocateInfo allocateInfo{ - .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, - .commandPool = mCommandPool, - .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, - .commandBufferCount = 1, - }; - - if (vkAllocateCommandBuffers(mDevice, &allocateInfo, &mCommandBuffer) != VK_SUCCESS) { - throw std::runtime_error("failed to create command buffer"); - } - } - - uint32_t findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags flags) { - VkPhysicalDeviceMemoryProperties memoryProperties; - vkGetPhysicalDeviceMemoryProperties(mPhysicalDevice, &memoryProperties); - - for (uint32_t i = 0; i < memoryProperties.memoryTypeCount; i++) { - if (typeFilter & (1 << i) && (memoryProperties.memoryTypes[i].propertyFlags & flags) == flags) { - return i; - } - } - - throw std::runtime_error("no suitable memory for vertex buffer found"); - } - - void createIndexBuffer() { - VkDeviceSize size = sizeof(indices[0]) * indices.size(); - - VkBuffer stagingBuffer; - VkDeviceMemory stagingBufferMemory; - - createBuffer(&stagingBuffer, &stagingBufferMemory, - VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, size, - VK_BUFFER_USAGE_TRANSFER_SRC_BIT); - - { - void* memory; - vkMapMemory(mDevice, stagingBufferMemory, 0, size, 0, &memory); - memcpy(memory, indices.data(), size); - vkUnmapMemory(mDevice, stagingBufferMemory); - } - - createBuffer(&mIndexBuffer, &mIndexBufferMemory, - VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, size, - VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT); - - copyBuffer(stagingBuffer, mIndexBuffer, size); - - destroyBuffer(stagingBuffer, stagingBufferMemory); - } - - void createUniformBuffer() { - auto size = sizeof(CustomShader::UniformBuffer); - createBuffer(&mUniformBuffer, &mUniformBufferMemory, - VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, size, - VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT); - - vkMapMemory(mDevice, mUniformBufferMemory, 0, size, 0, &mUniformBufferMemoryMapped); - } - - void createVertexBuffer() { - size_t size = vertices.size() * sizeof(vertices[0]); - - VkBuffer stagingBuffer; - VkDeviceMemory stagingBufferMemory; - - createBuffer(&stagingBuffer, &stagingBufferMemory, - VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, size, - VK_BUFFER_USAGE_TRANSFER_SRC_BIT); - - { - void* memory; - vkMapMemory(mDevice, stagingBufferMemory, 0, size, 0, &memory); - memcpy(memory, vertices.data(), size); - vkUnmapMemory(mDevice, stagingBufferMemory); - } - - createBuffer(&mVertexBuffer, &mVertexBufferMemory, - VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, size, - VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT); - - copyBuffer(stagingBuffer, mVertexBuffer, size); - - destroyBuffer(stagingBuffer, stagingBufferMemory); - } - - void copyBuffer(VkBuffer src, VkBuffer dst, VkDeviceSize size) { - VkCommandBufferAllocateInfo allocateInfo { - .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, - .commandPool = mCommandPool, - .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, - .commandBufferCount = 1 - }; - - VkCommandBuffer commandBuffer; - vkAllocateCommandBuffers(mDevice, &allocateInfo, &commandBuffer); - - VkCommandBufferBeginInfo beginInfo { - .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, - .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, - }; - - vkBeginCommandBuffer(commandBuffer, &beginInfo); - - VkBufferCopy copyRegion { - .srcOffset = 0, - .dstOffset = 0, - .size = size, - }; - - vkCmdCopyBuffer(commandBuffer, src, dst, 1, ©Region); - vkEndCommandBuffer(commandBuffer); - - VkSubmitInfo submitInfo { - .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, - .commandBufferCount = 1, - .pCommandBuffers = &commandBuffer, - }; - - vkQueueSubmit(mGraphicsQueue, 1, &submitInfo, VK_NULL_HANDLE); - vkQueueWaitIdle(mGraphicsQueue); - - vkFreeCommandBuffers(mDevice, mCommandPool, 1, &commandBuffer); - } - - void createBuffer(VkBuffer *buffer, VkDeviceMemory *memory, VkMemoryPropertyFlags properties, VkDeviceSize size, - VkBufferUsageFlags usage) { - - VkBufferCreateInfo createInfo { - .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, - .size = size, - .usage = usage, - .sharingMode = VK_SHARING_MODE_EXCLUSIVE, - }; - - if (vkCreateBuffer(mDevice, &createInfo, nullptr, buffer) != VK_SUCCESS) { - throw std::runtime_error("failed to create vertex buffer"); - } - - VkMemoryRequirements memoryRequirements; - vkGetBufferMemoryRequirements(mDevice, *buffer, &memoryRequirements); - - VkMemoryAllocateInfo allocateInfo = { - .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, - .allocationSize = memoryRequirements.size, - .memoryTypeIndex = findMemoryType(memoryRequirements.memoryTypeBits, properties), - }; - - if (vkAllocateMemory(mDevice, &allocateInfo, nullptr, memory) != VK_SUCCESS) { - throw std::runtime_error("failed to allocate vertex buffer memory"); - } - - vkBindBufferMemory(mDevice, *buffer, *memory, 0); - } - - void destroyBuffer(VkBuffer buffer, VkDeviceMemory memory) { - vkDestroyBuffer(mDevice, buffer, nullptr); - vkFreeMemory(mDevice, memory, nullptr); - } - - void populateGraphicsCommandBuffer(VkCommandBuffer commandBuffer, uint32_t swapChainImageIndex) { - VkCommandBufferBeginInfo commandBufferBeginInfo{ - .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, - }; - - if (vkBeginCommandBuffer(commandBuffer, &commandBufferBeginInfo) != VK_SUCCESS) { - throw std::runtime_error("failed to begin command buffer ="); - } - - VkClearValue clearColor{.color = {.float32 = {0.f, 0.f, 0.f, 1.f}}}; - - VkRenderPassBeginInfo renderPassBeginInfo{ - .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, - .renderPass = mGraphicsRenderPass, - .framebuffer = mSwapChainFrameBuffers[swapChainImageIndex], - .renderArea = { - .offset = {0, 0}, - .extent = mSwapChainExtent, - }, - .clearValueCount = 1, - .pClearValues = &clearColor, - }; - - vkCmdBeginRenderPass(commandBuffer, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE); - vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, mGraphicsPipeline); - - VkViewport viewport{ - .x = 0, - .y = 0, - .width = (float) mSwapChainExtent.width, - .height = (float) mSwapChainExtent.height, - .minDepth = 0.f, - .maxDepth = 1.f, - }; - - VkRect2D scissor{ - .offset = {0, 0}, - .extent = mSwapChainExtent, - }; - - vkCmdSetViewport(commandBuffer, 0, 1, &viewport); - vkCmdSetScissor(commandBuffer, 0, 1, &scissor); - - VkBuffer vertexBuffers[] = { mVertexBuffer }; - VkDeviceSize offsets[] = { 0 }; - vkCmdBindVertexBuffers(commandBuffer, 0, 1, vertexBuffers, offsets); - vkCmdBindIndexBuffer(commandBuffer, mIndexBuffer, 0, VK_INDEX_TYPE_UINT16); - - vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, mGraphicsPipelineLayout, 0, 1, - &mDescriptorSet, 0, - nullptr); - - vkCmdDrawIndexed(commandBuffer, indices.size(), 1, 0, 0, 0); - - vkCmdEndRenderPass(commandBuffer); - - if (vkEndCommandBuffer(commandBuffer) != VK_SUCCESS) { - throw std::runtime_error("failed to end command buffer"); - } - } - - void destroyCommandPool() { - vkDestroyCommandPool(mDevice, mCommandPool, nullptr); - } - - void createSynchronizationObjects() { - VkSemaphoreCreateInfo semaphoreCreateInfo { - .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, - }; - - VkFenceCreateInfo fenceCreateInfo { - .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, - .flags = VK_FENCE_CREATE_SIGNALED_BIT, - }; - - bool failure = false; - - failure |= vkCreateSemaphore(mDevice, &semaphoreCreateInfo, nullptr, &mSemaphoreImageAcquired) != VK_SUCCESS; - failure |= vkCreateSemaphore(mDevice, &semaphoreCreateInfo, nullptr, &mSemaphoreFramebufferDrawn) != VK_SUCCESS; - failure |= vkCreateFence(mDevice, &fenceCreateInfo, nullptr, &mFenceCanStartNewFrame) != VK_SUCCESS; - - if (failure) throw std::runtime_error("failed to create synchronization objects"); - } - - void drawFrame() { - vkWaitForFences(mDevice, 1, &mFenceCanStartNewFrame, VK_TRUE, UINT64_MAX); - vkResetFences(mDevice, 1, &mFenceCanStartNewFrame); - - uint32_t imageIndex = 0; - vkAcquireNextImageKHR(mDevice, mSwapChain, UINT64_MAX, mSemaphoreImageAcquired, VK_NULL_HANDLE, &imageIndex); - - vkResetCommandBuffer(mCommandBuffer, 0); - populateGraphicsCommandBuffer(mCommandBuffer, imageIndex); - - VkSemaphore waitSemaphores[] = { mSemaphoreImageAcquired }; - VkSemaphore signalSemaphores[] = { mSemaphoreFramebufferDrawn }; - VkPipelineStageFlags waitStages[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT }; - - VkSubmitInfo submitInfo { - .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, - .waitSemaphoreCount = 1, - .pWaitSemaphores = waitSemaphores, - .pWaitDstStageMask = waitStages, - .commandBufferCount = 1, - .pCommandBuffers = &mCommandBuffer, - .signalSemaphoreCount = 1, - .pSignalSemaphores = signalSemaphores, - }; - - updateUniformBuffer(); - - if (vkQueueSubmit(mGraphicsQueue, 1, &submitInfo, mFenceCanStartNewFrame) != VK_SUCCESS) { - throw std::runtime_error("failed to submit to graphics queue"); - } - - VkSwapchainKHR swapchains[] = { mSwapChain }; - - VkPresentInfoKHR presentInfo { - .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, - .waitSemaphoreCount = 1, - .pWaitSemaphores = signalSemaphores, - .swapchainCount = 1, - .pSwapchains = swapchains, - .pImageIndices = &imageIndex, - }; - - vkQueuePresentKHR(mPresentQueue, &presentInfo); - /* Somehow res is always VK_SUCCESS - if (res == VK_ERROR_OUT_OF_DATE_KHR || res == VK_SUBOPTIMAL_KHR) { - recreateSwapChain(); - } else if (res != VK_SUCCESS) { - throw std::runtime_error("cannot acquire new khr image"); - } - */ - } - - void updateUniformBuffer() { - static float time = 0; - time += 0.001; - - glm::mat4 model = glm::rotate(glm::mat4(1.0f), (time * glm::radians(90.f)), glm::vec3(0.f, 0.f, 1.f)); - glm::mat4 view = glm::lookAt(glm::vec3{2.0f, 2.0f, 2.0f}, glm::vec3{0.0f, 0.0f, 0.0f}, -glm::vec3{0.0f, 0.0f, 1.0f}); - glm::mat4 perspective = glm::perspective(glm::radians(45.f), - (float) mWindowFramebufferSize.first / - (float) mWindowFramebufferSize.second, 0.1f, 10.f); - - auto transforms = perspective * view * model; - - CustomShader::UniformBuffer ubo { - .transforms = transforms, - .origin = glm::vec4(0, 0, 0, 0), - }; - - memcpy(mUniformBufferMemoryMapped, &ubo, sizeof(ubo)); - // ubo.transforms = - } - - void destroySynchronizationObjects() { - vkDestroySemaphore(mDevice, mSemaphoreImageAcquired, nullptr); - vkDestroySemaphore(mDevice, mSemaphoreFramebufferDrawn, nullptr); - vkDestroyFence(mDevice, mFenceCanStartNewFrame, nullptr); - } - - void cleanup() { - // vkDestroyDescriptorSetLayout(mDevice, mDescriptorSet, nullptr); - vkDestroyDescriptorPool(mDevice, mDescriptorPool, nullptr); - - vkUnmapMemory(mDevice, mUniformBufferMemory); - destroyBuffer(mUniformBuffer, mUniformBufferMemory); - - destroyBuffer(mVertexBuffer, mVertexBufferMemory); - destroyBuffer(mIndexBuffer, mIndexBufferMemory); - - destroySynchronizationObjects(); - - destroyCommandPool(); - - destroySwapChainFramebuffers(); - - vkDestroyRenderPass(mDevice, mGraphicsRenderPass, nullptr); - - destroyGraphicsPipeline(); - destroySwapChainImageViews(); - destroySwapChain(); - - vkDestroyDevice(mDevice, nullptr); - vkDestroySurfaceKHR(mInstance, mSurface, nullptr); - vkDestroyInstance(mInstance, nullptr); - glfwDestroyWindow(mWindow); - glfwTerminate(); - } - -private: - GLFWwindow *mWindow = nullptr; - - bool mWindowSizeDirtyFlag = true; - TimePoint mWindowSizeDirtyFlagTime = std::chrono::system_clock::now(); - TimeMs mWindowSizeApplyMinDelay = 200; - std::pair mWindowFramebufferSize = { 0, 0 }; - - uint32_t mGraphicsQueueFamilyIndex = -1; - uint32_t mPresentationQueueFamilyIndex = -1; - - VkInstance mInstance = VK_NULL_HANDLE; - VkPhysicalDevice mPhysicalDevice = VK_NULL_HANDLE; - VkDevice mDevice = VK_NULL_HANDLE; - - VkQueue mGraphicsQueue = VK_NULL_HANDLE; - VkQueue mPresentQueue = VK_NULL_HANDLE; - - VkSurfaceKHR mSurface = VK_NULL_HANDLE; - VkSwapchainKHR mSwapChain = VK_NULL_HANDLE; - VkFormat mSwapChainFormat{}; - VkExtent2D mSwapChainExtent{}; - std::vector mSwapChainImages; - std::vector mSwapChainImageViews; - std::vector mSwapChainFrameBuffers; - - CustomShader mShader; - VkPipeline mGraphicsPipeline = VK_NULL_HANDLE; - VkRenderPass mGraphicsRenderPass = VK_NULL_HANDLE; - VkPipelineLayout mGraphicsPipelineLayout{}; // no uniforms used in the shader - - VkCommandPool mCommandPool = VK_NULL_HANDLE; - VkCommandBuffer mCommandBuffer = VK_NULL_HANDLE; - - VkSemaphore mSemaphoreImageAcquired = VK_NULL_HANDLE; - VkSemaphore mSemaphoreFramebufferDrawn = VK_NULL_HANDLE; - VkFence mFenceCanStartNewFrame = VK_NULL_HANDLE; - - // Buffers - VkBuffer mVertexBuffer = VK_NULL_HANDLE; - VkDeviceMemory mVertexBufferMemory = VK_NULL_HANDLE; - - VkBuffer mIndexBuffer = VK_NULL_HANDLE; - VkDeviceMemory mIndexBufferMemory = VK_NULL_HANDLE; - - VkDescriptorPool mDescriptorPool = VK_NULL_HANDLE; - VkDescriptorSet mDescriptorSet = VK_NULL_HANDLE; - - VkBuffer mUniformBuffer = VK_NULL_HANDLE; - VkDeviceMemory mUniformBufferMemory = VK_NULL_HANDLE; - void* mUniformBufferMemoryMapped = nullptr; -}; - -int main() { - Application app; - - try { - app.run(); - } catch (const std::exception &e) { - std::cerr << e.what() << std::endl; - return EXIT_FAILURE; - } - - return EXIT_SUCCESS; -} \ No newline at end of file