Pipeline
Pipeline
hpp>
#include <array>
#include <fstream>
#include <filesystem>
#include <renderer/Renderer.hpp>
#include <renderer/RenderPass.hpp>
#include <renderer/SwapChain.hpp>
#include <renderer/VulkanHandler.hpp>
// The following define ensures that the implementation gets added
#define STB_IMAGE_IMPLEMENTATION
#include <stb/stb_image.h>
Pipeline::Pipeline()
: _vulkanHandler (nullptr), _pipelineLayout (VK_NULL_HANDLE), _vertexBuffer
(VK_NULL_HANDLE), _vertexBufferMemory (VK_NULL_HANDLE), _indexBuffer
(VK_NULL_HANDLE), _indexBufferMemory(VK_NULL_HANDLE), _textureImage
(VK_NULL_HANDLE), _textureImageMemory (VK_NULL_HANDLE), _textureImageView
(VK_NULL_HANDLE), _textureSampler (VK_NULL_HANDLE), _descriptorSetLayout
(VK_NULL_HANDLE), _descriptorPool (VK_NULL_HANDLE) {
createDescriptorSetLayout();
createGraphicsPipeline(swapChain, renderPass);
// createTextureImage();
// createTextureImageView();
// createTextureSampler();
createUniformBuffers();
createDescriptorPool();
createDescriptorSets();
}
void Pipeline::deinit() {
vkDestroyPipeline(_vulkanHandler->_logicalDevice, _pipeline, nullptr);
vkDestroyPipelineLayout(_vulkanHandler->_logicalDevice, _pipelineLayout,
nullptr);
vkDestroyDescriptorPool(_vulkanHandler->_logicalDevice, _descriptorPool,
nullptr);
vkDestroyDescriptorSetLayout(_vulkanHandler->_logicalDevice,
_descriptorSetLayout, nullptr);
VkVertexInputBindingDescription bindingDescription =
Vertex::getBindingDescription();
Vertex::AttributeDescriptions attributeDescriptions =
Vertex::getAttributeDescriptions();
vertexInputInfo.vertexBindingDescriptionCount = 1;
vertexInputInfo.vertexAttributeDescriptionCount =
static_cast<uint32_t>(attributeDescriptions.size());
vertexInputInfo.pVertexBindingDescriptions = &bindingDescription;
vertexInputInfo.pVertexAttributeDescriptions = attributeDescriptions.data();
rasterizer.cullMode = VK_CULL_MODE_BACK_BIT;
rasterizer.frontFace = VK_FRONT_FACE_CLOCKWISE;
rasterizer.depthBiasEnable = VK_FALSE;
rasterizer.depthBiasConstantFactor = 0.0f;
rasterizer.depthBiasClamp = 0.0f;
rasterizer.depthBiasSlopeFactor = 0.0f;
if (vkCreatePipelineLayout(_vulkanHandler->_logicalDevice, &pipelineLayoutInfo,
nullptr, &_pipelineLayout) != VK_SUCCESS) {
throw std::runtime_error("Failed to create pipeline layout!");
}
pipelineInfo.pVertexInputState = &vertexInputInfo;
pipelineInfo.pInputAssemblyState = &inputAssembly;
pipelineInfo.pViewportState = &viewportState;
pipelineInfo.pRasterizationState = &rasterizer;
pipelineInfo.pMultisampleState = &multisampling;
pipelineInfo.pDepthStencilState = nullptr;
pipelineInfo.pColorBlendState = &colorBlending;
pipelineInfo.pDynamicState = nullptr;
pipelineInfo.layout = _pipelineLayout;
pipelineInfo.renderPass = renderPass->_renderPass;
pipelineInfo.subpass = 0;
if (vkCreateGraphicsPipelines(_vulkanHandler->_logicalDevice, VK_NULL_HANDLE,
1, &pipelineInfo, nullptr, &_pipeline) != VK_SUCCESS) {
throw std::runtime_error("Failed to create graphics pipeline!");
}
vkDestroyShaderModule(_vulkanHandler->_logicalDevice, fragShaderModule,
nullptr);
vkDestroyShaderModule(_vulkanHandler->_logicalDevice, vertShaderModule,
nullptr);
}
if (!file.is_open()) {
throw std::runtime_error("Failed to open file '" + filename + " with
current path '" + std::filesystem::current_path().string() + "'!");
}
file.seekg(0);
file.read(buffer.data(), fileSize);
file.close();
return buffer;
}
VkShaderModule shaderModule;
if (vkCreateShaderModule(_vulkanHandler->_logicalDevice, &createInfo, nullptr,
&shaderModule) != VK_SUCCESS) {
throw std::runtime_error("failed to create shader module!");
}
return shaderModule;
}
void Pipeline::createTextureImage() {
int texWidth, texHeight, texChannels;
stbi_uc* pixels = stbi_load("textures/arial_a.png", &texWidth, &texHeight,
&texChannels, STBI_rgb_alpha);
VkDeviceSize imageSize = texWidth * texHeight * 4;
if (!pixels) {
throw std::runtime_error("Failed to load texture image!");
}
VkBuffer stagingBuffer;
VkDeviceMemory stagingBufferMemory;
createBuffer(imageSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
stagingBuffer, stagingBufferMemory);
void* data;
vkMapMemory(_vulkanHandler->_logicalDevice, stagingBufferMemory, 0, imageSize,
0, &data);
memcpy(data, pixels, static_cast<size_t>(imageSize));
vkUnmapMemory(_vulkanHandler->_logicalDevice, stagingBufferMemory);
stbi_image_free(pixels);
transitionImageLayout(_textureImage, VK_FORMAT_R8G8B8A8_UNORM,
VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
copyBufferToImage(stagingBuffer, _textureImage,
static_cast<uint32_t>(texWidth), static_cast<uint32_t>(texHeight));
transitionImageLayout(_textureImage, VK_FORMAT_R8G8B8A8_UNORM,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
transitionImageLayout(_textureImage, VK_FORMAT_R8G8B8A8_UNORM,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
vkDestroyBuffer(_vulkanHandler->_logicalDevice, stagingBuffer, nullptr);
vkFreeMemory(_vulkanHandler->_logicalDevice, stagingBufferMemory, nullptr);
}
VkMemoryRequirements memoryRequirements;
vkGetImageMemoryRequirements(_vulkanHandler->_logicalDevice, image,
&memoryRequirements);
barrier.image = image;
barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
barrier.subresourceRange.baseMipLevel = 0;
barrier.subresourceRange.levelCount = 1;
barrier.subresourceRange.baseArrayLayer = 0;
barrier.subresourceRange.layerCount = 1;
VkPipelineStageFlags sourceStage;
VkPipelineStageFlags destinationStage;
sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
} else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && newLayout ==
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) {
barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
destinationStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
} else {
throw std::invalid_argument("Unsupported layout transition!");
}
vkCmdPipelineBarrier(
commandBuffer,
sourceStage, destinationStage,
0,
0, nullptr,
0, nullptr,
1, &barrier
);
_vulkanHandler->endSingleTimeCommands(commandBuffer);
}
region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
region.imageSubresource.mipLevel = 0;
region.imageSubresource.baseArrayLayer = 0;
region.imageSubresource.layerCount = 1;
_vulkanHandler->endSingleTimeCommands(commandBuffer);
}
void Pipeline::createTextureImageView() {
_textureImageView = _vulkanHandler->createImageView(_textureImage,
VK_FORMAT_R8G8B8A8_UNORM);
}
void Pipeline::createTextureSampler() {
VkSamplerCreateInfo samplerInfo {};
samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
samplerInfo.magFilter = VK_FILTER_LINEAR;
samplerInfo.minFilter = VK_FILTER_LINEAR;
samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER;
samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER;
samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER;
samplerInfo.anisotropyEnable = VK_TRUE;
samplerInfo.maxAnisotropy = properties.limits.maxSamplerAnisotropy;
samplerInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK;
samplerInfo.unnormalizedCoordinates = VK_FALSE;
samplerInfo.compareEnable = VK_FALSE;
samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS;
samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
samplerInfo.mipLodBias = 0.0f;
samplerInfo.minLod = 0.0f;
samplerInfo.maxLod = 0.0f;
VkMemoryRequirements memoryRequirements;
vkGetBufferMemoryRequirements(_vulkanHandler->_logicalDevice, buffer,
&memoryRequirements);
VkBufferCopy copyRegion{};
copyRegion.size = size;
vkCmdCopyBuffer(commandBuffer, srcBuffer, dstBuffer, 1, ©Region);
_vulkanHandler->endSingleTimeCommands(commandBuffer);
}
void Pipeline::createVertexBuffer() {
VkDeviceSize bufferSize = sizeof(vertices[0]) * vertices.size();
createBuffer(bufferSize, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
_vertexBuffer, _vertexBufferMemory);
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(_vulkanHandler->_logicalDevice, stagingBufferMemory, 0, bufferSize,
0, &data);
memcpy(data, vertices.data(), (size_t) bufferSize);
vkUnmapMemory(_vulkanHandler->_logicalDevice, stagingBufferMemory);
createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT |
VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
_vertexBuffer, _vertexBufferMemory);
void Pipeline::createIndexBuffer() {
VkDeviceSize bufferSize = sizeof(indices[0]) * 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(_vulkanHandler->_logicalDevice, stagingBufferMemory, 0, bufferSize,
0, &data);
memcpy(data, indices.data(), (size_t) bufferSize);
vkUnmapMemory(_vulkanHandler->_logicalDevice, stagingBufferMemory);
createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT |
VK_BUFFER_USAGE_INDEX_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
_indexBuffer, _indexBufferMemory);
void Pipeline::createUniformBuffers() {
VkDeviceSize bufferSize = sizeof(UniformBufferObject);
_uniformBuffers.resize(Renderer::MAX_FRAMES_IN_FLIGHT);
_uniformBuffersMemory.resize(Renderer::MAX_FRAMES_IN_FLIGHT);
void* data;
vkMapMemory(_vulkanHandler->_logicalDevice,
_uniformBuffersMemory[currentImage], 0, sizeof(ubo), 0, &data);
memcpy(data, &ubo, sizeof(ubo));
vkUnmapMemory(_vulkanHandler->_logicalDevice,
_uniformBuffersMemory[currentImage]);
}
void Pipeline::createShaderStorageBuffers() {
_shaderStorageBuffers.resize(Renderer::MAX_FRAMES_IN_FLIGHT);
_shaderStorageBuffersMemory.resize(Renderer::MAX_FRAMES_IN_FLIGHT);
}
createBuffer(bufferSize, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
_shaderStorageBuffers[currentImage], _shaderStorageBuffersMemory[currentImage]);
void* data;
vkMapMemory(_vulkanHandler->_logicalDevice,
_uniformBuffersMemory[currentImage], 0, sizeof(color), 0, &data);
memcpy(data, &color, sizeof(color));
vkUnmapMemory(_vulkanHandler->_logicalDevice,
_uniformBuffersMemory[currentImage]);
}
void Pipeline::createDescriptorPool() {
std::array<VkDescriptorPoolSize, 2> poolSizes {};
poolSizes[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
poolSizes[0].descriptorCount =
static_cast<uint32_t>(Renderer::MAX_FRAMES_IN_FLIGHT);
poolSizes[1].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
poolSizes[1].descriptorCount =
static_cast<uint32_t>(Renderer::MAX_FRAMES_IN_FLIGHT);
poolInfo.maxSets = static_cast<uint32_t>(Renderer::MAX_FRAMES_IN_FLIGHT);
void Pipeline::createDescriptorSetLayout() {
VkDescriptorSetLayoutBinding uboLayoutBinding {};
uboLayoutBinding.binding = 0;
uboLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
uboLayoutBinding.descriptorCount = 1;
uboLayoutBinding.pImmutableSamplers = nullptr;
uboLayoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
if (vkCreateDescriptorSetLayout(_vulkanHandler->_logicalDevice, &layoutInfo,
nullptr, &_descriptorSetLayout) != VK_SUCCESS) {
throw std::runtime_error("Failed to create descriptor set layout!");
}
}
void Pipeline::createDescriptorSets() {
std::vector<VkDescriptorSetLayout> layouts(Renderer::MAX_FRAMES_IN_FLIGHT,
_descriptorSetLayout);
_descriptorSets.resize(Renderer::MAX_FRAMES_IN_FLIGHT);
if (vkAllocateDescriptorSets(_vulkanHandler->_logicalDevice, &allocInfo,
_descriptorSets.data()) != VK_SUCCESS) {
throw std::runtime_error("Failed to allocate descriptor sets!");
}
descriptorWrites[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
descriptorWrites[0].dstSet = _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[0].pImageInfo = nullptr;
descriptorWrites[0].pTexelBufferView = nullptr;
descriptorWrites[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
descriptorWrites[1].dstSet = _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].pBufferInfo = nullptr;
descriptorWrites[1].pImageInfo = &imageInfo;
descriptorWrites[1].pTexelBufferView = nullptr;
vkUpdateDescriptorSets(_vulkanHandler->_logicalDevice,
static_cast<uint32_t>(descriptorWrites.size()), descriptorWrites.data(), 0,
nullptr);
}
}
void Pipeline::clearVertexData() {
vertices.clear();
indices.clear();
}
vertices.push_back(topLeft);
vertices.push_back(bottomLeft);
vertices.push_back(topRight);
vertices.push_back(bottomRight);
indices.push_back(index);
indices.push_back(index + 2);
indices.push_back(index + 3);
indices.push_back(index);
indices.push_back(index + 3);
indices.push_back(index + 1);
}