diff --git a/base/vulkanframebuffer.hpp b/base/vulkanframebuffer.hpp new file mode 100644 index 00000000..5ba04933 --- /dev/null +++ b/base/vulkanframebuffer.hpp @@ -0,0 +1,210 @@ +/* +* Vulkan framebuffer class +* +* Copyright (C) 2016 by Sascha Willems - www.saschawillems.de +* +* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) +*/ + +#pragma once + +#include +#include +#include +#include "vulkan/vulkan.h" +#include "vulkandevice.hpp" +#include "vulkantools.h" + +namespace vk +{ + /** + * @brief Encapsulates a single frame buffer attachment + */ + struct FramebufferAttachment + { + VkImage image; + VkDeviceMemory memory; + VkImageView view; + VkFormat format; + VkImageSubresourceRange subresourceRange; + + /** + * @brief Returns true if the attachment has a depth component + */ + bool hasDepth() + { + std::vector formats = + { + VK_FORMAT_D16_UNORM, + VK_FORMAT_X8_D24_UNORM_PACK32, + VK_FORMAT_D32_SFLOAT, + VK_FORMAT_D16_UNORM_S8_UINT, + VK_FORMAT_D24_UNORM_S8_UINT, + VK_FORMAT_D32_SFLOAT_S8_UINT, + }; + return std::find(formats.begin(), formats.end(), format) != std::end(formats); + } + + /** + * @brief Returns true if the attachment has a stencil component + */ + bool hasStencil() + { + std::vector formats = + { + VK_FORMAT_S8_UINT, + VK_FORMAT_D16_UNORM_S8_UINT, + VK_FORMAT_D24_UNORM_S8_UINT, + VK_FORMAT_D32_SFLOAT_S8_UINT, + }; + return std::find(formats.begin(), formats.end(), format) != std::end(formats); + } + }; + + /** + * @brief Describes the attributes of a framebuffer to be created + */ + struct FramebufferCreateInfo + { + uint32_t width, height; + uint32_t layerCount; + VkFormat format; + // todo: could be derived from format + VkImageUsageFlags usage; + // todo: rename + bool sample; + }; + + /** + * @brief Encaspulates a complete Vulkan framebuffer with an arbitrary number and combination of attachments + */ + struct Framebuffer + { + private: + vk::VulkanDevice *vulkanDevice; + public: + uint32_t width, height; + VkFramebuffer framebuffer; + VkRenderPass renderPass; + VkSampler sampler; + std::vector attachments; + + /** + * Default constructor + * + * @param vulkanDevice Pointer to a valid VulkanDevice + */ + Framebuffer(vk::VulkanDevice *vulkanDevice) + { + assert(vulkanDevice); + this->vulkanDevice = vulkanDevice; + } + + /** + * Destroy and free resources used for the framebuffer and all of it's attachments + */ + void FreeResources(VkDevice device) + { + for (auto attachment : attachments) + { + vkDestroyImage(device, attachment.image, nullptr); + vkDestroyImageView(device, attachment.view, nullptr); + vkFreeMemory(device, attachment.memory, nullptr); + } + vkDestroySampler(device, sampler, nullptr); + vkDestroyRenderPass(device, renderPass, nullptr); + vkDestroyFramebuffer(device, framebuffer, nullptr); + } + + /** + * Add a new attachment described by createinfo to the framebuffer's attachment list + * + * @param createinfo Structure that specifices the framebuffer to be constructed + * @param layoutCmd A valid and active command buffer used for the initial layout transitions + * + * @return Index of the new attachment + */ + uint32_t addAttachment(vk::FramebufferCreateInfo createinfo, VkCommandBuffer layoutCmd) + { + vk::FramebufferAttachment attachment; + + attachment.format = createinfo.format; + + VkImageAspectFlags aspectMask = VK_FLAGS_NONE; + VkImageLayout imageLayout; + + // Select aspect mask and layout depending on usage + if (createinfo.usage & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT) + { + aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + imageLayout = createinfo.sample ? VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + } + + if (createinfo.usage & VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) + { + if (attachment.hasDepth()) + { + aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; + } + if (attachment.hasStencil()) + { + aspectMask = aspectMask | VK_IMAGE_ASPECT_STENCIL_BIT; + } + imageLayout = createinfo.sample ? VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + } + + assert(aspectMask > 0); + + VkImageCreateInfo image = vkTools::initializers::imageCreateInfo(); + image.imageType = VK_IMAGE_TYPE_2D; + image.format = createinfo.format; + image.extent.width = createinfo.width; + image.extent.height = createinfo.height; + image.extent.depth = 1; + image.mipLevels = 1; + image.arrayLayers = createinfo.layerCount; + image.samples = VK_SAMPLE_COUNT_1_BIT; + image.tiling = VK_IMAGE_TILING_OPTIMAL; + image.usage = createinfo.usage; + + VkMemoryAllocateInfo memAlloc = vkTools::initializers::memoryAllocateInfo(); + VkMemoryRequirements memReqs; + + // Create image for this attachment + VK_CHECK_RESULT(vkCreateImage(vulkanDevice->device, &image, nullptr, &attachment.image)); + vkGetImageMemoryRequirements(vulkanDevice->device, attachment.image, &memReqs); + memAlloc.allocationSize = memReqs.size; + memAlloc.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + VK_CHECK_RESULT(vkAllocateMemory(vulkanDevice->device, &memAlloc, nullptr, &attachment.memory)); + VK_CHECK_RESULT(vkBindImageMemory(vulkanDevice->device, attachment.image, attachment.memory, 0)); + + attachment.subresourceRange = {}; + attachment.subresourceRange.aspectMask = aspectMask; + attachment.subresourceRange.levelCount = 1; + attachment.subresourceRange.layerCount = createinfo.layerCount; + + // Set the initial layout to shader read instead of attachment + // Note that the render loop has to take care of the transition from read to attachment + vkTools::setImageLayout( + layoutCmd, + attachment.image, + aspectMask, + VK_IMAGE_LAYOUT_UNDEFINED, + imageLayout, + attachment.subresourceRange); + + VkImageViewCreateInfo imageView = vkTools::initializers::imageViewCreateInfo(); + imageView.viewType = (createinfo.layerCount == 1) ? VK_IMAGE_VIEW_TYPE_2D : VK_IMAGE_VIEW_TYPE_2D_ARRAY; + imageView.format = createinfo.format; + imageView.subresourceRange = attachment.subresourceRange; + //todo: workaround for depth+stencil attachments + imageView.subresourceRange.aspectMask = (attachment.hasDepth()) ? VK_IMAGE_ASPECT_DEPTH_BIT : aspectMask; + imageView.image = attachment.image; + VK_CHECK_RESULT(vkCreateImageView(vulkanDevice->device, &imageView, nullptr, &attachment.view)); + + attachments.push_back(attachment); + + return static_cast(attachments.size() - 1); + } + }; +} \ No newline at end of file