#include "first_app.hpp" #include #include #define GLM_FORCE_RADIANS #define GLM_FORCE_DEPTH_ZERO_TO_ONE #include #include namespace cve { struct SimplePushConstantData{ glm::mat2 transform{1.f}; // Identity matrix glm::vec2 offset; alignas(16) glm::vec3 color; }; FirstApp::FirstApp() { loadGameObjects(); createPipelineLayout(); recreateSwapChain(); createCommandBuffers(); } FirstApp::~FirstApp() { vkDestroyPipelineLayout(cveDevice.device(), pipelineLayout, nullptr); } void FirstApp::run() { while (!cveWindow.shouldClose()) { glfwPollEvents(); drawFrame(); } vkDeviceWaitIdle(cveDevice.device()); } void FirstApp::loadGameObjects() { std::vector verticies { {{0.0f, -0.5f}, {1.0f, 0.0f, 0.0f}}, {{0.5f, 0.5f}, {0.0f, 1.0f, 0.0f}}, {{-0.5f, 0.5f}, {0.0f, 0.0f, 1.0f}} }; auto cveModel = std::make_shared(cveDevice, verticies); auto triangle = CveGameObject::createGameObject(); triangle.model = cveModel; triangle.color = {0.1f, 0.8f, 0.1f}; triangle.transform2d.translation.x = .2f; triangle.transform2d.scale = {2.f, .5f}; triangle.transform2d.rotation = .25f * glm::two_pi(); gameObjects.push_back(std::move(triangle)); } void FirstApp::createPipelineLayout() { VkPushConstantRange pushConstantRange{}; pushConstantRange.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT; pushConstantRange.offset = 0; pushConstantRange.size = sizeof(SimplePushConstantData); VkPipelineLayoutCreateInfo pipelineLayoutInfo{}; pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; pipelineLayoutInfo.setLayoutCount = 0; pipelineLayoutInfo.pSetLayouts = nullptr; pipelineLayoutInfo.pushConstantRangeCount = 1; pipelineLayoutInfo.pPushConstantRanges = &pushConstantRange; if (vkCreatePipelineLayout(cveDevice.device(), &pipelineLayoutInfo, nullptr, &pipelineLayout) != VK_SUCCESS) { throw std::runtime_error("Failed to create pipeline layout"); } } void FirstApp::createPipeline() { assert(cveSwapChain != nullptr && "Cannot create pipeline before swap chain"); assert(pipelineLayout != nullptr && "Cannot create pipeline before pipeline layout"); PipelineConfigInfo pipelineConfig{}; CvePipeline::defaultPipelineConfigInfo(pipelineConfig); pipelineConfig.renderPass = cveSwapChain->getRenderPass(); pipelineConfig.pipelineLayout = pipelineLayout; cvePipeline = std::make_unique( cveDevice, "shaders/simple_shader.vert.spv", "shaders/simple_shader.frag.spv", pipelineConfig ); } void FirstApp::recreateSwapChain() { auto extent = cveWindow.getExtent(); while (extent.width == 0 || extent.height == 0) { extent = cveWindow.getExtent(); glfwWaitEvents(); } vkDeviceWaitIdle(cveDevice.device()); if (cveSwapChain == nullptr) { cveSwapChain = std::make_unique(cveDevice, extent); } else { cveSwapChain = std::make_unique(cveDevice, extent, std::move(cveSwapChain)); if (cveSwapChain->imageCount() != commandBuffers.size()) { freeCommandBuffers(); createCommandBuffers(); } } // Check if render pass is compatible createPipeline(); } void FirstApp::createCommandBuffers() { commandBuffers.resize(cveSwapChain->imageCount()); VkCommandBufferAllocateInfo allocInfo{}; allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; allocInfo.commandPool = cveDevice.getCommandPool(); allocInfo.commandBufferCount = static_cast(commandBuffers.size()); if (vkAllocateCommandBuffers(cveDevice.device(), &allocInfo, commandBuffers.data()) != VK_SUCCESS) { throw std::runtime_error("Failed to allocate command buffers"); } } void FirstApp::freeCommandBuffers() { vkFreeCommandBuffers(cveDevice.device(), cveDevice.getCommandPool(), static_cast(commandBuffers.size()), commandBuffers.data()); commandBuffers.clear(); } void FirstApp::recordCommandBuffer(int imageIndex) { VkCommandBufferBeginInfo beginInfo{}; beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; if (vkBeginCommandBuffer(commandBuffers[imageIndex], &beginInfo) != VK_SUCCESS) { throw std::runtime_error("Failed to begin command buffer recording"); } VkRenderPassBeginInfo renderPassInfo{}; renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; renderPassInfo.renderPass = cveSwapChain->getRenderPass(); renderPassInfo.framebuffer = cveSwapChain->getFrameBuffer(imageIndex); renderPassInfo.renderArea.offset = {0, 0}; renderPassInfo.renderArea.extent = cveSwapChain->getSwapChainExtent(); std::array clearValues{}; clearValues[0].color = {0.01f, 0.01f, 0.01f, 1.0f}; clearValues[1].depthStencil = {1.0f, 0}; renderPassInfo.clearValueCount = static_cast(clearValues.size()); renderPassInfo.pClearValues = clearValues.data(); vkCmdBeginRenderPass(commandBuffers[imageIndex], &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE); VkViewport viewport{}; viewport.x = 0.0f; viewport.y = 0.0f; viewport.width = static_cast(cveSwapChain->getSwapChainExtent().width); viewport.height = static_cast(cveSwapChain->getSwapChainExtent().height); viewport.minDepth = 0.0f; viewport.maxDepth = 1.0f; VkRect2D scissor{{0, 0}, cveSwapChain->getSwapChainExtent()}; vkCmdSetViewport(commandBuffers[imageIndex], 0, 1, &viewport); vkCmdSetScissor(commandBuffers[imageIndex], 0, 1, &scissor); renderGameObjects(commandBuffers[imageIndex]); vkCmdEndRenderPass(commandBuffers[imageIndex]); if (vkEndCommandBuffer(commandBuffers[imageIndex]) != VK_SUCCESS) { throw std::runtime_error("Failed to record command buffer!"); } } void FirstApp::renderGameObjects(VkCommandBuffer commandBuffer) { cvePipeline->bind(commandBuffer); for (auto& obj: gameObjects) { obj.transform2d.rotation = glm::mod(obj.transform2d.rotation + 0.001f, glm::two_pi()); SimplePushConstantData push{}; push.offset = obj.transform2d.translation; push.color = obj.color; push.transform = obj.transform2d.mat2(); vkCmdPushConstants( commandBuffer, pipelineLayout, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(SimplePushConstantData), &push ); obj.model->bind(commandBuffer); obj.model->draw(commandBuffer); } } void FirstApp::drawFrame() { uint32_t imageIndex; auto result = cveSwapChain->acquireNextImage(&imageIndex); if (result == VK_ERROR_OUT_OF_DATE_KHR) { recreateSwapChain(); return; } if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) { throw std::runtime_error("Failed to acquire swap chain image!"); } recordCommandBuffer(imageIndex); result = cveSwapChain->submitCommandBuffers(&commandBuffers[imageIndex], &imageIndex); if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR || cveWindow.wasWindowResized()) { cveWindow.resetWindowResizedFlag(); recreateSwapChain(); return; } if (result != VK_SUCCESS) { throw std::runtime_error("Failed to present swap chain image!"); } } }