From 7770063537e17f148c14a0c72c4109a904ed0565 Mon Sep 17 00:00:00 2001 From: Lillian Salehi Date: Thu, 24 Oct 2024 21:05:36 -0500 Subject: [PATCH] Small fixes --- .gitignore | 1 - src/graphics/buffers.cpp | 572 +++++++++++++++++++++------------------ 2 files changed, 314 insertions(+), 259 deletions(-) diff --git a/.gitignore b/.gitignore index 703bd38..2df1f92 100644 --- a/.gitignore +++ b/.gitignore @@ -6,7 +6,6 @@ *.slo *.lo *.o -*.obj # Compiled Shader files *.spv diff --git a/src/graphics/buffers.cpp b/src/graphics/buffers.cpp index f9d7af0..06f6676 100644 --- a/src/graphics/buffers.cpp +++ b/src/graphics/buffers.cpp @@ -8,280 +8,336 @@ VkDescriptorPool descriptorPool; std::vector uniformBuffers; std::vector uniformBuffersMemory; -std::vector uniformBuffersMapped; +std::vector uniformBuffersMapped; namespace buffers_libs { - uint32_t Buffers::findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties) { - // Graphics cards offer different types of memory to allocate from, here we query to find the right type of memory for our needs. - // Query the available types of memory to iterate over. - VkPhysicalDeviceMemoryProperties memProperties; - vkGetPhysicalDeviceMemoryProperties(Global::physicalDevice, &memProperties); - // iterate over and see if any of the memory types match our needs, in this case, HOST_VISIBLE and HOST_COHERENT. These will be explained shortly. - for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++) { - if ((typeFilter & (1 << i)) && (memProperties.memoryTypes[i].propertyFlags & properties) == properties) { - return i; - } - } - throw std::runtime_error("failed to find suitable memory type!"); - } - - void copyBuffer(VkBuffer srcBuffer, VkBuffer dstBuffer, VkDeviceSize size) { - VkCommandBufferAllocateInfo allocInfo{}; - allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; - allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; - allocInfo.commandPool = Global::commandPool; - allocInfo.commandBufferCount = 1; - - VkCommandBuffer commandBuffer; - vkAllocateCommandBuffers(Global::device, &allocInfo, &commandBuffer); - - VkCommandBufferBeginInfo beginInfo{}; - beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; - beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; - - vkBeginCommandBuffer(commandBuffer, &beginInfo); - - VkBufferCopy copyRegion{}; - copyRegion.size = size; - vkCmdCopyBuffer(commandBuffer, srcBuffer, dstBuffer, 1, ©Region); - - vkEndCommandBuffer(commandBuffer); - - VkSubmitInfo submitInfo{}; - submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; - submitInfo.commandBufferCount = 1; - submitInfo.pCommandBuffers = &commandBuffer; - - vkQueueSubmit(Global::graphicsQueue, 1, &submitInfo, VK_NULL_HANDLE); - vkQueueWaitIdle(Global::graphicsQueue); - - vkFreeCommandBuffers(Global::device, Global::commandPool, 1, &commandBuffer); - } - - void Buffers::createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkBuffer& buffer, VkDeviceMemory& bufferMemory) { - VkBufferCreateInfo bufferInfo{}; - bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; - bufferInfo.size = size; - bufferInfo.usage = usage; - bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - - if (vkCreateBuffer(Global::device, &bufferInfo, nullptr, &buffer) != VK_SUCCESS) { - throw std::runtime_error("failed to create buffer!"); - } - - VkMemoryRequirements memRequirements; - vkGetBufferMemoryRequirements(Global::device, buffer, &memRequirements); - - VkMemoryAllocateInfo allocInfo{}; - allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; - allocInfo.allocationSize = memRequirements.size; - allocInfo.memoryTypeIndex = findMemoryType(memRequirements.memoryTypeBits, properties); - - if (vkAllocateMemory(Global::device, &allocInfo, nullptr, &bufferMemory) != VK_SUCCESS) { - throw std::runtime_error("failed to allocate buffer memory!"); - } - - vkBindBufferMemory(Global::device, buffer, bufferMemory, 0); - } - - void Buffers::createIndexBuffer() { - VkDeviceSize bufferSize = sizeof(Global::indices[0]) * Global::indices.size(); - - VkBuffer stagingBuffer; - VkDeviceMemory stagingBufferMemory; - - createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, stagingBuffer, stagingBufferMemory); - - void* data; - vkMapMemory(Global::device, stagingBufferMemory, 0, bufferSize, 0, &data); - memcpy(data, Global::indices.data(), (size_t) bufferSize); - - vkUnmapMemory(Global::device, stagingBufferMemory); - - createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, indexBuffer, indexBufferMemory); - - copyBuffer(stagingBuffer, indexBuffer, bufferSize); - - vkDestroyBuffer(Global::device, stagingBuffer, nullptr); - vkFreeMemory(Global::device, stagingBufferMemory, nullptr); - } - void Buffers::createVertexBuffer() { - // Create a Vertex Buffer to hold the vertex information in memory so it doesn't have to be hardcoded! - // Size denotes the size of the buffer in bytes, usage in this case is the buffer behaviour, using a bitwise OR. - // Sharing mode denostes the same as the images in the swap chain! in this case, only the graphics queue uses this buffer, so we make it exclusive. - VkDeviceSize bufferSize = sizeof(Global::vertices[0]) * Global::vertices.size(); - - VkBuffer stagingBuffer; - VkDeviceMemory stagingBufferMemory; - createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, stagingBuffer, stagingBufferMemory); - - void* data; - vkMapMemory(Global::device, stagingBufferMemory, 0, bufferSize, 0, &data); - memcpy(data, Global::vertices.data(), (size_t) bufferSize); - vkUnmapMemory(Global::device, stagingBufferMemory); - - createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, vertexBuffer, vertexBufferMemory); - - copyBuffer(stagingBuffer, vertexBuffer, bufferSize); - - vkDestroyBuffer(Global::device, stagingBuffer, nullptr); - vkFreeMemory(Global::device, stagingBufferMemory, nullptr); - } - - void Buffers::destroyBuffers() { - vkDestroyBuffer(Global::device, indexBuffer, nullptr); - vkFreeMemory(Global::device, indexBufferMemory, nullptr); - - vkDestroyBuffer(Global::device, vertexBuffer, nullptr); - vkFreeMemory(Global::device, vertexBufferMemory, nullptr); - } - VkBuffer Buffers::getVertexBuffer() { - return vertexBuffer; - } - VkBuffer Buffers::getIndexBuffer() { - return indexBuffer; - } - -// ------------------------------ Uniform Buffer Setup -------------------------------- // - void Buffers::createDescriptorSetLayout() { - // Create a table of pointers to data, a Descriptor Set! - // --------------------- UBO Layout --------------------- // - 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; - - // --------------- Texture Sampler Layout --------------- // - VkDescriptorSetLayoutBinding samplerLayoutBinding{}; - samplerLayoutBinding.binding = 1; - samplerLayoutBinding.descriptorCount = 1; - samplerLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - samplerLayoutBinding.pImmutableSamplers = nullptr; - samplerLayoutBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; - - std::array bindings = {uboLayoutBinding, samplerLayoutBinding}; - VkDescriptorSetLayoutCreateInfo layoutInfo{}; - layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; - layoutInfo.bindingCount = static_cast(bindings.size()); - layoutInfo.pBindings = bindings.data(); - - if(vkCreateDescriptorSetLayout(Global::device, &layoutInfo, nullptr, &Global::descriptorSetLayout) != VK_SUCCESS) { - throw std::runtime_error("Failed to create descriptor set layout!"); +uint32_t Buffers::findMemoryType(uint32_t typeFilter, + VkMemoryPropertyFlags properties) { + // Graphics cards offer different types of memory to allocate from, here we + // query to find the right type of memory for our needs. Query the available + // types of memory to iterate over. + VkPhysicalDeviceMemoryProperties memProperties; + vkGetPhysicalDeviceMemoryProperties(Global::physicalDevice, &memProperties); + // iterate over and see if any of the memory types match our needs, in this + // case, HOST_VISIBLE and HOST_COHERENT. These will be explained shortly. + for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++) { + if ((typeFilter & (1 << i)) && (memProperties.memoryTypes[i].propertyFlags & + properties) == properties) { + return i; } } - void Buffers::createUniformBuffers() { - // Map the uniform buffer to memory as a pointer we can use to write data to later. This stays mapped to memory for the applications lifetime. - // This technique is called "persistent mapping", not having to map the buffer every time we need to update it increases performance, though not free - 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); + throw std::runtime_error("failed to find suitable memory type!"); +} - 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 copyBuffer(VkBuffer srcBuffer, VkBuffer dstBuffer, VkDeviceSize size) { + VkCommandBufferAllocateInfo allocInfo{}; + allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + allocInfo.commandPool = Global::commandPool; + allocInfo.commandBufferCount = 1; + + VkCommandBuffer commandBuffer; + vkAllocateCommandBuffers(Global::device, &allocInfo, &commandBuffer); + + VkCommandBufferBeginInfo beginInfo{}; + beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + + vkBeginCommandBuffer(commandBuffer, &beginInfo); + + VkBufferCopy copyRegion{}; + copyRegion.size = size; + vkCmdCopyBuffer(commandBuffer, srcBuffer, dstBuffer, 1, ©Region); + + vkEndCommandBuffer(commandBuffer); + + VkSubmitInfo submitInfo{}; + submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submitInfo.commandBufferCount = 1; + submitInfo.pCommandBuffers = &commandBuffer; + + vkQueueSubmit(Global::graphicsQueue, 1, &submitInfo, VK_NULL_HANDLE); + vkQueueWaitIdle(Global::graphicsQueue); + + vkFreeCommandBuffers(Global::device, Global::commandPool, 1, &commandBuffer); +} + +void Buffers::createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, + VkMemoryPropertyFlags properties, VkBuffer &buffer, + VkDeviceMemory &bufferMemory) { + VkBufferCreateInfo bufferInfo{}; + bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + bufferInfo.size = size; + bufferInfo.usage = usage; + bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + + if (vkCreateBuffer(Global::device, &bufferInfo, nullptr, &buffer) != + VK_SUCCESS) { + throw std::runtime_error("failed to create buffer!"); } - void Buffers::updateUniformBuffer(uint32_t currentImage) { - // Update the uniform buffer every frame to change the position, but notably, use chrono to know exactly how much to move - // so we aren't locked to the framerate as the world time. - 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(); + VkMemoryRequirements memRequirements; + vkGetBufferMemoryRequirements(Global::device, buffer, &memRequirements); - Global::UniformBufferObject ubo{}; - ubo.time = time; - // Modify the model projection transformation to rotate around the Z over time. - ubo.model = glm::rotate(glm::mat4(1.0f), time * glm::radians(30.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*time, 2.0f), glm::vec3(0.0f, 0.0f, 0.5f), 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), device_libs::DeviceControl::getSwapChainExtent().width / (float) device_libs::DeviceControl::getSwapChainExtent().height, 0.1f, 100.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)); + VkMemoryAllocateInfo allocInfo{}; + allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + allocInfo.allocationSize = memRequirements.size; + allocInfo.memoryTypeIndex = + findMemoryType(memRequirements.memoryTypeBits, properties); + + if (vkAllocateMemory(Global::device, &allocInfo, nullptr, &bufferMemory) != + VK_SUCCESS) { + throw std::runtime_error("failed to allocate buffer memory!"); } - void Buffers::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 Buffers::createDescriptorPool() { - // Create a pool to be used to allocate descriptor sets. - std::array poolSizes{}; - poolSizes[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; - poolSizes[0].descriptorCount = static_cast(Global::MAX_FRAMES_IN_FLIGHT); - poolSizes[1].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - poolSizes[1].descriptorCount = static_cast(Global::MAX_FRAMES_IN_FLIGHT); - - VkDescriptorPoolCreateInfo poolInfo{}; - poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; - poolInfo.poolSizeCount = static_cast(poolSizes.size()); - poolInfo.pPoolSizes = poolSizes.data(); - 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 Buffers::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(); + vkBindBufferMemory(Global::device, buffer, bufferMemory, 0); +} - 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!"); - } +void Buffers::createIndexBuffer() { + VkDeviceSize bufferSize = sizeof(Global::indices[0]) * Global::indices.size(); - 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); + VkBuffer stagingBuffer; + VkDeviceMemory stagingBufferMemory; - VkDescriptorImageInfo imageInfo{}; - imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - imageInfo.imageView = Global::textureImageView; - imageInfo.sampler = Global::textureSampler; + createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | + VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + stagingBuffer, stagingBufferMemory); - std::array descriptorWrites{}; + void *data; + vkMapMemory(Global::device, stagingBufferMemory, 0, bufferSize, 0, &data); + memcpy(data, Global::indices.data(), (size_t)bufferSize); - descriptorWrites[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; - descriptorWrites[0].dstSet = Global::descriptorSets[i]; - descriptorWrites[0].dstBinding = 0; - descriptorWrites[0].dstArrayElement = 0; - descriptorWrites[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; - descriptorWrites[0].descriptorCount = 1; - descriptorWrites[0].pBufferInfo = &bufferInfo; + vkUnmapMemory(Global::device, stagingBufferMemory); - descriptorWrites[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; - descriptorWrites[1].dstSet = Global::descriptorSets[i]; - descriptorWrites[1].dstBinding = 1; - descriptorWrites[1].dstArrayElement = 0; - descriptorWrites[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - descriptorWrites[1].descriptorCount = 1; - descriptorWrites[1].pImageInfo = &imageInfo; + createBuffer( + bufferSize, + VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, indexBuffer, indexBufferMemory); - vkUpdateDescriptorSets(Global::device, static_cast(descriptorWrites.size()), descriptorWrites.data(), 0, nullptr); - } - } - void Buffers::destroyDescriptorPool() { - vkDestroyDescriptorPool(Global::device, descriptorPool, nullptr); + copyBuffer(stagingBuffer, indexBuffer, bufferSize); + + vkDestroyBuffer(Global::device, stagingBuffer, nullptr); + vkFreeMemory(Global::device, stagingBufferMemory, nullptr); +} +void Buffers::createVertexBuffer() { + // Create a Vertex Buffer to hold the vertex information in memory so it + // doesn't have to be hardcoded! Size denotes the size of the buffer in bytes, + // usage in this case is the buffer behaviour, using a bitwise OR. Sharing + // mode denostes the same as the images in the swap chain! in this case, only + // the graphics queue uses this buffer, so we make it exclusive. + VkDeviceSize bufferSize = + sizeof(Global::vertices[0]) * Global::vertices.size(); + + VkBuffer stagingBuffer; + VkDeviceMemory stagingBufferMemory; + createBuffer(bufferSize, + VK_BUFFER_USAGE_TRANSFER_SRC_BIT | + VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | + VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + stagingBuffer, stagingBufferMemory); + + void *data; + vkMapMemory(Global::device, stagingBufferMemory, 0, bufferSize, 0, &data); + memcpy(data, Global::vertices.data(), (size_t)bufferSize); + vkUnmapMemory(Global::device, stagingBufferMemory); + + createBuffer( + bufferSize, + VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, vertexBuffer, vertexBufferMemory); + + copyBuffer(stagingBuffer, vertexBuffer, bufferSize); + + vkDestroyBuffer(Global::device, stagingBuffer, nullptr); + vkFreeMemory(Global::device, stagingBufferMemory, nullptr); +} + +void Buffers::destroyBuffers() { + vkDestroyBuffer(Global::device, indexBuffer, nullptr); + vkFreeMemory(Global::device, indexBufferMemory, nullptr); + + vkDestroyBuffer(Global::device, vertexBuffer, nullptr); + vkFreeMemory(Global::device, vertexBufferMemory, nullptr); +} +VkBuffer Buffers::getVertexBuffer() { return vertexBuffer; } +VkBuffer Buffers::getIndexBuffer() { return indexBuffer; } + +// ------------------------------ Uniform Buffer Setup +// -------------------------------- // +void Buffers::createDescriptorSetLayout() { + // Create a table of pointers to data, a Descriptor Set! + // --------------------- UBO Layout --------------------- // + 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; + + // --------------- Texture Sampler Layout --------------- // + VkDescriptorSetLayoutBinding samplerLayoutBinding{}; + samplerLayoutBinding.binding = 1; + samplerLayoutBinding.descriptorCount = 1; + samplerLayoutBinding.descriptorType = + VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + samplerLayoutBinding.pImmutableSamplers = nullptr; + samplerLayoutBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; + + std::array bindings = {uboLayoutBinding, + samplerLayoutBinding}; + VkDescriptorSetLayoutCreateInfo layoutInfo{}; + layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + layoutInfo.bindingCount = static_cast(bindings.size()); + layoutInfo.pBindings = bindings.data(); + + if (vkCreateDescriptorSetLayout(Global::device, &layoutInfo, nullptr, + &Global::descriptorSetLayout) != VK_SUCCESS) { + throw std::runtime_error("Failed to create descriptor set layout!"); } } +void Buffers::createUniformBuffers() { + // Map the uniform buffer to memory as a pointer we can use to write data to + // later. This stays mapped to memory for the applications lifetime. This + // technique is called "persistent mapping", not having to map the buffer + // every time we need to update it increases performance, though not free + 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 Buffers::updateUniformBuffer(uint32_t currentImage) { + // Update the uniform buffer every frame to change the position, but notably, + // use chrono to know exactly how much to move so we aren't locked to the + // framerate as the world time. + + 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{}; + ubo.time = time; + // Modify the model projection transformation to rotate around the Z over + // time. + ubo.model = glm::rotate(glm::mat4(1.0f), time * glm::radians(30.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 * sin(time), 2.0f), + glm::vec3(0.0f, 0.0f, 0.5f), 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), + device_libs::DeviceControl::getSwapChainExtent().width / + (float)device_libs::DeviceControl::getSwapChainExtent().height, + 0.1f, 100.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 Buffers::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 Buffers::createDescriptorPool() { + // Create a pool to be used to allocate descriptor sets. + std::array poolSizes{}; + poolSizes[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + poolSizes[0].descriptorCount = + static_cast(Global::MAX_FRAMES_IN_FLIGHT); + poolSizes[1].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + poolSizes[1].descriptorCount = + static_cast(Global::MAX_FRAMES_IN_FLIGHT); + + VkDescriptorPoolCreateInfo poolInfo{}; + poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; + poolInfo.poolSizeCount = static_cast(poolSizes.size()); + poolInfo.pPoolSizes = poolSizes.data(); + 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 Buffers::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); + + VkDescriptorImageInfo imageInfo{}; + imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + imageInfo.imageView = Global::textureImageView; + imageInfo.sampler = Global::textureSampler; + + std::array descriptorWrites{}; + + descriptorWrites[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descriptorWrites[0].dstSet = Global::descriptorSets[i]; + descriptorWrites[0].dstBinding = 0; + descriptorWrites[0].dstArrayElement = 0; + descriptorWrites[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + descriptorWrites[0].descriptorCount = 1; + descriptorWrites[0].pBufferInfo = &bufferInfo; + + descriptorWrites[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descriptorWrites[1].dstSet = Global::descriptorSets[i]; + descriptorWrites[1].dstBinding = 1; + descriptorWrites[1].dstArrayElement = 0; + descriptorWrites[1].descriptorType = + VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + descriptorWrites[1].descriptorCount = 1; + descriptorWrites[1].pImageInfo = &imageInfo; + + vkUpdateDescriptorSets(Global::device, + static_cast(descriptorWrites.size()), + descriptorWrites.data(), 0, nullptr); + } +} +void Buffers::destroyDescriptorPool() { + vkDestroyDescriptorPool(Global::device, descriptorPool, nullptr); +} +} // namespace buffers_libs