diff --git a/src/entrypoint.cpp b/src/entrypoint.cpp index b803580..7674c66 100644 --- a/src/entrypoint.cpp +++ b/src/entrypoint.cpp @@ -1,5 +1,5 @@ #include "entrypoint.h" -#include "graphics/buffers.h" +#include "global.h" DeviceControl::devicelibrary deviceLibs; Debug::vulkandebuglibs debugController; Graphics::graphicspipeline graphicsPipeline; @@ -59,11 +59,15 @@ void initVulkan() { deviceLibs.createSwapChain(Global::window); deviceLibs.createImageViews(); graphicsPipeline.createRenderPass(); + buffers.createDescriptorSetLayout(); graphicsPipeline.createGraphicsPipeline(); graphicsPipeline.createFramebuffers(); graphicsPipeline.createCommandPool(); buffers.createVertexBuffer(); buffers.createIndexBuffer(); + buffers.createUniformBuffers(); + buffers.createDescriptorPool(); + buffers.createDescriptorSets(); graphicsPipeline.createCommandBuffer(); renderPresentation.createSyncObject(); } @@ -78,6 +82,9 @@ void mainLoop() { // This loop jus void cleanup() { // Similar to the last handoff, destroy the utils in a safe manner in the library! renderPresentation.cleanupSwapChain(); + buffers.destroyUniformBuffer(); + buffers.destroyDescriptorPool(); + vkDestroyDescriptorSetLayout(Global::device, Global::descriptorSetLayout, nullptr); graphicsPipeline.destroyGraphicsPipeline(); graphicsPipeline.destroyRenderPass(); diff --git a/src/global.cpp b/src/global.cpp index e4fe6b1..bbec249 100644 --- a/src/global.cpp +++ b/src/global.cpp @@ -21,7 +21,9 @@ namespace Global { VkQueue graphicsQueue; VkQueue presentQueue; GLFWwindow* window; - + VkDescriptorSetLayout descriptorSetLayout; + std::vector descriptorSets; + uint32_t currentFrame = 0; Global::QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device) { // First we feed in a integer we want to use to hold the number of queued items, that fills it, then we create that amount of default constructed *VkQueueFamilyProperties* structs. diff --git a/src/global.h b/src/global.h index f44f595..2ace031 100644 --- a/src/global.h +++ b/src/global.h @@ -1,4 +1,5 @@ #pragma once +#include #include #include #include @@ -7,6 +8,7 @@ #include #include #include "debug/vulkandebuglibs.h" +#include #include #define GLFW_INCLUDE_VULKAN @@ -24,7 +26,15 @@ namespace Global { extern VkQueue presentQueue; const int MAX_FRAMES_IN_FLIGHT = 2; extern GLFWwindow* window; + extern VkDescriptorSetLayout descriptorSetLayout; + extern uint32_t currentFrame; + extern std::vector descriptorSets; + struct UniformBufferObject { + glm::mat4 model; + glm::mat4 view; + glm::mat4 proj; + }; struct Vertex { glm::vec2 pos; glm::vec3 color; diff --git a/src/graphics/buffers.cpp b/src/graphics/buffers.cpp index ee4ca6c..1218530 100644 --- a/src/graphics/buffers.cpp +++ b/src/graphics/buffers.cpp @@ -1,14 +1,24 @@ #include "buffers.h" +#include #include -#include #include +#include "../devicelibrary.h" VkBuffer vertexBuffer; VkDeviceMemory vertexBufferMemory; VkBuffer indexBuffer; VkDeviceMemory indexBufferMemory; +VkDescriptorPool descriptorPool; +DeviceControl::devicelibrary deviceLibrary; + +std::vector uniformBuffers; +std::vector uniformBuffersMemory; +std::vector uniformBuffersMapped; + namespace Buffers { + + const std::vector vertices = { {{-0.5f, -0.5f}, {1.0f, 0.0f, 0.0f}}, {{0.5f, -0.5f}, {0.0f, 1.0f, 0.0f}}, @@ -155,4 +165,119 @@ namespace Buffers { std::vector bufferslibrary::getIndices() { return indices; } + + void bufferslibrary::createDescriptorSetLayout() { + // Create a table of pointers to data, a Descriptor Set! + VkDescriptorSetLayoutBinding uboLayoutBinding{}; + uboLayoutBinding.binding = 0; + uboLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + // Model-View-Projection matrix is in a single uniform buffer, so just 1 descriptor. + uboLayoutBinding.descriptorCount = 1; + // We are only using this buffer in the vertex shader, so set the flags thus. + uboLayoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT; + // Immutable Samplers is relevant for image sampling. + uboLayoutBinding.pImmutableSamplers = nullptr; + + VkDescriptorSetLayoutCreateInfo layoutInfo{}; + layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + layoutInfo.bindingCount = 1; + layoutInfo.pBindings = &uboLayoutBinding; + + if(vkCreateDescriptorSetLayout(Global::device, &layoutInfo, nullptr, &Global::descriptorSetLayout) != VK_SUCCESS) { + throw std::runtime_error("Failed to create descriptor set layout!"); + } + } + //void createMVPDescriptor() { + + //} + void bufferslibrary::createUniformBuffers() { + VkDeviceSize bufferSize = sizeof(Global::UniformBufferObject); + + uniformBuffers.resize(Global::MAX_FRAMES_IN_FLIGHT); + uniformBuffersMemory.resize(Global::MAX_FRAMES_IN_FLIGHT); + uniformBuffersMapped.resize(Global::MAX_FRAMES_IN_FLIGHT); + + for(size_t i = 0; i < Global::MAX_FRAMES_IN_FLIGHT; i++) { + createBuffer(bufferSize, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, uniformBuffers[i], uniformBuffersMemory[i]); + vkMapMemory(Global::device, uniformBuffersMemory[i], 0, bufferSize, 0, &uniformBuffersMapped[i]); + } + } + void bufferslibrary::updateUniformBuffer(uint32_t currentImage) { + static auto startTime = std::chrono::high_resolution_clock::now(); + // Calculate the time in seconds since rendering has began to floating point precision. + auto currentTime = std::chrono::high_resolution_clock::now(); + float time = std::chrono::duration(currentTime - startTime).count(); + + Global::UniformBufferObject ubo{}; + // Modify the model projection transformation to rotate around the Z over time. + ubo.model = glm::rotate(glm::mat4(1.0f), time * glm::radians(90.0f), glm::vec3(0.0f, 0.0f, 1.0f)); + // Modify the view transformation to look at the object from above at a 45 degree angle. + // This takes the eye position, center position, and the up direction. + ubo.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)); + // 45 degree field of view, set aspect ratio, and near and far clipping range. + ubo.proj = glm::perspective(glm::radians(45.0f), deviceLibrary.getSwapChainExtent().width / (float) deviceLibrary.getSwapChainExtent().height, 0.1f, 10.0f); + + // GLM was created for OpenGL, where the Y coordinate was inverted. This simply flips the sign. + ubo.proj[1][1] *= -1; + + memcpy(uniformBuffersMapped[currentImage], &ubo, sizeof(ubo)); + } + void bufferslibrary::destroyUniformBuffer() { + for(size_t i = 0; i < Global::MAX_FRAMES_IN_FLIGHT; i++) { + vkDestroyBuffer(Global::device, uniformBuffers[i],nullptr); + vkFreeMemory(Global::device, uniformBuffersMemory[i], nullptr); + } + } + void bufferslibrary::createDescriptorPool() { + VkDescriptorPoolSize poolSize{}; + poolSize.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + poolSize.descriptorCount = static_cast(Global::MAX_FRAMES_IN_FLIGHT); + + VkDescriptorPoolCreateInfo poolInfo{}; + poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; + poolInfo.poolSizeCount = 1; + poolInfo.pPoolSizes = &poolSize; + poolInfo.maxSets = static_cast(Global::MAX_FRAMES_IN_FLIGHT); + + if (vkCreateDescriptorPool(Global::device, &poolInfo, nullptr, &descriptorPool) != VK_SUCCESS) { + throw std::runtime_error("failed to create descriptor pool!"); + } + } + void bufferslibrary::createDescriptorSets() { + std::vector layouts(Global::MAX_FRAMES_IN_FLIGHT, Global::descriptorSetLayout); + VkDescriptorSetAllocateInfo allocInfo{}; + allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; + allocInfo.descriptorPool = descriptorPool; + allocInfo.descriptorSetCount = static_cast(Global::MAX_FRAMES_IN_FLIGHT); + allocInfo.pSetLayouts = layouts.data(); + + Global::descriptorSets.resize(Global::MAX_FRAMES_IN_FLIGHT); + if (vkAllocateDescriptorSets(Global::device, &allocInfo, Global::descriptorSets.data()) != VK_SUCCESS) { + throw std::runtime_error("failed to allocate descriptor sets!"); + } + for(size_t i = 0; i < Global::MAX_FRAMES_IN_FLIGHT; i++) { + VkDescriptorBufferInfo bufferInfo{}; + bufferInfo.buffer = uniformBuffers[i]; + bufferInfo.offset = 0; + bufferInfo.range = sizeof(Global::UniformBufferObject); + + VkWriteDescriptorSet descriptorWrite{}; + descriptorWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descriptorWrite.dstSet = Global::descriptorSets[i]; + descriptorWrite.dstBinding = 0; + descriptorWrite.dstArrayElement = 0; + + descriptorWrite.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + descriptorWrite.descriptorCount = 1; + + descriptorWrite.pBufferInfo = &bufferInfo; + descriptorWrite.pImageInfo = nullptr; // Optional + descriptorWrite.pTexelBufferView = nullptr; // Optional + + vkUpdateDescriptorSets(Global::device, 1, &descriptorWrite, 0, nullptr); + } + } + void bufferslibrary::destroyDescriptorPool() { + vkDestroyDescriptorPool(Global::device, descriptorPool, nullptr); + } } diff --git a/src/graphics/buffers.h b/src/graphics/buffers.h index fb8cfd2..c570097 100644 --- a/src/graphics/buffers.h +++ b/src/graphics/buffers.h @@ -1,5 +1,6 @@ #pragma once #include "../global.h" +#include namespace Buffers { class bufferslibrary { @@ -11,5 +12,12 @@ namespace Buffers { VkBuffer getIndexBuffer(); std::vector getVertices(); std::vector getIndices(); + void createDescriptorSetLayout(); + void createUniformBuffers(); + void updateUniformBuffer(uint32_t currentImage); + void destroyUniformBuffer(); + void createDescriptorPool(); + void createDescriptorSets(); + void destroyDescriptorPool(); }; } diff --git a/src/graphics/graphicspipeline.cpp b/src/graphics/graphicspipeline.cpp index 94f8331..0907bd0 100644 --- a/src/graphics/graphicspipeline.cpp +++ b/src/graphics/graphicspipeline.cpp @@ -96,11 +96,11 @@ namespace Graphics { rasterizer.depthClampEnable = VK_FALSE; rasterizer.rasterizerDiscardEnable = VK_FALSE; // MODE_FILL, fill polygons, MODE_LINE, draw wireframe, MODE_POINT, draw vertices. Anything other than fill requires GPU feature *fillModeNonSolid* - rasterizer.polygonMode = VK_POLYGON_MODE_LINE; + rasterizer.polygonMode = VK_POLYGON_MODE_FILL; rasterizer.lineWidth = 2.0f; // How to cull the faces, right here we cull the back faces and tell the rasterizer front facing vertices are ordered clockwise. rasterizer.cullMode = VK_CULL_MODE_BACK_BIT; - rasterizer.frontFace = VK_FRONT_FACE_CLOCKWISE; + rasterizer.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; // Whether or not to add depth values. e.x. for shadow maps. rasterizer.depthBiasEnable = VK_FALSE; @@ -147,8 +147,8 @@ namespace Graphics { VkPipelineLayoutCreateInfo pipelineLayoutInfo{}; pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; - pipelineLayoutInfo.setLayoutCount = 0; - pipelineLayoutInfo.pushConstantRangeCount = 0; + pipelineLayoutInfo.setLayoutCount = 1; + pipelineLayoutInfo.pSetLayouts = &Global::descriptorSetLayout; if (vkCreatePipelineLayout(Global::device, &pipelineLayoutInfo, nullptr, &pipelineLayout) != VK_SUCCESS) { throw std::runtime_error("failed to create pipeline layout!"); @@ -309,6 +309,8 @@ namespace Graphics { VkDeviceSize offsets[] = {0}; vkCmdBindVertexBuffers(commandBuffer, 0, 1, vertexBuffers, offsets); vkCmdBindIndexBuffer(commandBuffer, buffers.getIndexBuffer(), 0, VK_INDEX_TYPE_UINT16); + + vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &Global::descriptorSets[Global::currentFrame], 0, nullptr); vkCmdDrawIndexed(commandBuffer, static_cast(buffers.getIndices().size()), 1, 0, 0, 0); vkCmdEndRenderPass(commandBuffer); diff --git a/src/graphics/render.cpp b/src/graphics/render.cpp index 6cca75e..a7f6a45 100644 --- a/src/graphics/render.cpp +++ b/src/graphics/render.cpp @@ -9,7 +9,7 @@ namespace RenderPresent { std::vector inFlightFences; Graphics::graphicspipeline pipeline; DeviceControl::devicelibrary deviceLibs; - uint32_t currentFrame = 0; + Buffers::bufferslibrary buffers; void recreateSwapChain() { int width = 0, height = 0; @@ -38,39 +38,42 @@ namespace RenderPresent { // submit the recorded command buffer and present the image! void render::drawFrame() { - vkWaitForFences(Global::device, 1, &inFlightFences[currentFrame], VK_TRUE, UINT64_MAX); - vkResetFences(Global::device, 1, &inFlightFences[currentFrame]); + vkWaitForFences(Global::device, 1, &inFlightFences[Global::currentFrame], VK_TRUE, UINT64_MAX); + vkResetFences(Global::device, 1, &inFlightFences[Global::currentFrame]); uint32_t imageIndex; - VkResult result = vkAcquireNextImageKHR(Global::device, Global::swapChain, UINT64_MAX, imageAvailableSemaphores[currentFrame], VK_NULL_HANDLE, &imageIndex); + VkResult result = vkAcquireNextImageKHR(Global::device, Global::swapChain, UINT64_MAX, imageAvailableSemaphores[Global::currentFrame], VK_NULL_HANDLE, &imageIndex); if (result == VK_ERROR_OUT_OF_DATE_KHR) { recreateSwapChain(); return; } else if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) { throw std::runtime_error("failed to acquire swap chain image!"); } - vkResetFences(Global::device, 1, &inFlightFences[currentFrame]); - vkResetCommandBuffer(Global::commandBuffers[currentFrame], /*VkCommandBufferResetFlagBits*/ 0); - pipeline.recordCommandBuffer(Global::commandBuffers[currentFrame], imageIndex); + buffers.updateUniformBuffer(Global::currentFrame); + + vkResetFences(Global::device, 1, &inFlightFences[Global::currentFrame]); + + vkResetCommandBuffer(Global::commandBuffers[Global::currentFrame], /*VkCommandBufferResetFlagBits*/ 0); + pipeline.recordCommandBuffer(Global::commandBuffers[Global::currentFrame], imageIndex); VkSubmitInfo submitInfo{}; submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; - VkSemaphore waitSemaphores[] = {imageAvailableSemaphores[currentFrame]}; + VkSemaphore waitSemaphores[] = {imageAvailableSemaphores[Global::currentFrame]}; VkPipelineStageFlags waitStages[] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT}; submitInfo.waitSemaphoreCount = 1; submitInfo.pWaitSemaphores = waitSemaphores; submitInfo.pWaitDstStageMask = waitStages; submitInfo.commandBufferCount = 1; - submitInfo.pCommandBuffers = &Global::commandBuffers[currentFrame]; + submitInfo.pCommandBuffers = &Global::commandBuffers[Global::currentFrame]; - VkSemaphore signalSemaphores[] = {renderFinishedSemaphores[currentFrame]}; + VkSemaphore signalSemaphores[] = {renderFinishedSemaphores[Global::currentFrame]}; submitInfo.signalSemaphoreCount = 1; submitInfo.pSignalSemaphores = signalSemaphores; - if (vkQueueSubmit(Global::graphicsQueue, 1, &submitInfo, inFlightFences[currentFrame]) != VK_SUCCESS) { + if (vkQueueSubmit(Global::graphicsQueue, 1, &submitInfo, inFlightFences[Global::currentFrame]) != VK_SUCCESS) { throw std::runtime_error("failed to submit draw command buffer!"); } @@ -93,7 +96,7 @@ namespace RenderPresent { } else if (result != VK_SUCCESS) { throw std::runtime_error("failed to present swap chain image!"); } - currentFrame = (currentFrame + 1) % Global::MAX_FRAMES_IN_FLIGHT; + Global::currentFrame = (Global::currentFrame + 1) % Global::MAX_FRAMES_IN_FLIGHT; } #pragma info // SEMAPHORES diff --git a/src/shaders/vertex.vert b/src/shaders/vertex.vert index a32c132..3646155 100644 --- a/src/shaders/vertex.vert +++ b/src/shaders/vertex.vert @@ -1,4 +1,10 @@ #version 450 + +layout(binding = 0) uniform UniformBufferObject { + mat4 model; + mat4 view; + mat4 proj; +} ubo; // inPosition and inColor are vertex attributes, per-vertex properties defined in the vertex buffer! // Layout assigns indices to access these inputs, dvec3 takes 2 slots so we must index it at 2. https://www.khronos.org/opengl/wiki/Layout_Qualifier_(GLSL) layout(location = 0) in vec2 inPosition; @@ -7,6 +13,7 @@ layout(location = 1) in vec3 inColor; layout(location = 0) out vec3 fragColor; void main() { - gl_Position = vec4(inPosition, 0.0, 1.0); + gl_Position = ubo.proj * ubo.view * ubo.model * vec4(inPosition, 0.0, 1.0); fragColor = inColor; } +