.. _program_listing_file_Src_GraphicsEngineVulkan_scene_Texture.cpp: Program Listing for File Texture.cpp ==================================== |exhale_lsh| :ref:`Return to documentation for file ` (``Src/GraphicsEngineVulkan/scene/Texture.cpp``) .. |exhale_lsh| unicode:: U+021B0 .. UPWARDS ARROW WITH TIP LEFTWARDS .. code-block:: cpp #include "scene/Texture.hpp" #include "common/Utilities.hpp" #include "spdlog/spdlog.h" #include #include using namespace Kataglyphis; Kataglyphis::Texture::Texture() {} void Kataglyphis::Texture::createFromFile(VulkanDevice *device, VkCommandPool commandPool, const std::string &fileName) { int width, height; VkDeviceSize size; stbi_uc *image_data = loadTextureData(fileName, &width, &height, &size); mip_levels = static_cast(std::floor(std::log2(std::max(width, height)))) + 1; // create staging buffer to hold loaded data, ready to copy to device VulkanBuffer stagingBuffer; stagingBuffer.create(device, size, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); // copy image data to staging buffer void *data; vkMapMemory(device->getLogicalDevice(), stagingBuffer.getBufferMemory(), 0, size, 0, &data); memcpy(data, image_data, static_cast(size)); vkUnmapMemory(device->getLogicalDevice(), stagingBuffer.getBufferMemory()); // free original image data stbi_image_free(image_data); createImage(device, width, height, mip_levels, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); // copy data to image // transition image to be DST for copy operation vulkanImage.transitionImageLayout(device->getLogicalDevice(), device->getGraphicsQueue(), commandPool, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_ASPECT_COLOR_BIT, mip_levels); // copy data to image vulkanBufferManager.copyImageBuffer(device->getLogicalDevice(), device->getGraphicsQueue(), commandPool, stagingBuffer.getBuffer(), vulkanImage.getImage(), width, height); // generate mipmaps generateMipMaps(device->getPhysicalDevice(), device->getLogicalDevice(), commandPool, device->getGraphicsQueue(), vulkanImage.getImage(), VK_FORMAT_R8G8B8A8_SRGB, width, height, mip_levels); stagingBuffer.cleanUp(); createImageView(device, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_ASPECT_COLOR_BIT, mip_levels); } void Kataglyphis::Texture::setImage(VkImage image) { vulkanImage.setImage(image); } void Kataglyphis::Texture::setImageView(VkImageView imageView) { vulkanImageView.setImageView(imageView); } void Kataglyphis::Texture::createImage(VulkanDevice *device, uint32_t width, uint32_t height, uint32_t mip_levels, VkFormat format, VkImageTiling tiling, VkImageUsageFlags use_flags, VkMemoryPropertyFlags prop_flags) { vulkanImage.create(device, width, height, mip_levels, format, tiling, use_flags, prop_flags); } void Kataglyphis::Texture::createImageView(VulkanDevice *device, VkFormat format, VkImageAspectFlags aspect_flags, uint32_t mip_levels) { vulkanImageView.create(device, vulkanImage.getImage(), format, aspect_flags, mip_levels); } void Kataglyphis::Texture::cleanUp() { vulkanImageView.cleanUp(); vulkanImage.cleanUp(); } Kataglyphis::Texture::~Texture() {} stbi_uc * Kataglyphis::Texture::loadTextureData(const std::string &file_name, int *width, int *height, VkDeviceSize *image_size) { // number of channels image uses int channels; // load pixel data for image // std::string file_loc = "../Resources/Textures/" + file_name; stbi_uc *image = stbi_load(file_name.c_str(), width, height, &channels, STBI_rgb_alpha); if (!image) { spdlog::error("Failed to load a texture file! (" + file_name + ")"); } // calculate image size using given and known data *image_size = *width * *height * 4; return image; } void Kataglyphis::Texture::generateMipMaps(VkPhysicalDevice physical_device, VkDevice device, VkCommandPool command_pool, VkQueue queue, VkImage image, VkFormat image_format, int32_t width, int32_t height, uint32_t mip_levels) { // Check if image format supports linear blitting VkFormatProperties formatProperties; vkGetPhysicalDeviceFormatProperties(physical_device, image_format, &formatProperties); if (!(formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT)) { spdlog::error("Texture image format does not support linear blitting!"); } VkCommandBuffer command_buffer = commandBufferManager.beginCommandBuffer(device, command_pool); VkImageMemoryBarrier barrier{}; barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; barrier.image = image; barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; barrier.subresourceRange.baseArrayLayer = 0; barrier.subresourceRange.layerCount = 1; barrier.subresourceRange.levelCount = 1; // TEMP VARS needed for decreasing step by step for factor 2 int32_t tmp_width = width; int32_t tmp_height = height; // -- WE START AT 1 ! for (uint32_t i = 1; i < mip_levels; i++) { // WAIT for previous mip map level for being ready barrier.subresourceRange.baseMipLevel = i - 1; // HERE we TRANSITION for having a SRC format now barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier); // when barrier over we can now blit :) VkImageBlit blit{}; // -- OFFSETS describing the 3D-dimesnion of the region blit.srcOffsets[0] = { 0, 0, 0 }; blit.srcOffsets[1] = { tmp_width, tmp_height, 1 }; blit.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; // copy from previous level blit.srcSubresource.mipLevel = i - 1; blit.srcSubresource.baseArrayLayer = 0; blit.srcSubresource.layerCount = 1; // -- OFFSETS describing the 3D-dimesnion of the region blit.dstOffsets[0] = { 0, 0, 0 }; blit.dstOffsets[1] = { tmp_width > 1 ? tmp_width / 2 : 1, tmp_height > 1 ? tmp_height / 2 : 1, 1 }; blit.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; // -- COPY to next mipmap level blit.dstSubresource.mipLevel = i; blit.dstSubresource.baseArrayLayer = 0; blit.dstSubresource.layerCount = 1; vkCmdBlitImage(command_buffer, image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &blit, VK_FILTER_LINEAR); // REARRANGE image formats for having the correct image formats again barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier); if (tmp_width > 1) tmp_width /= 2; if (tmp_height > 1) tmp_height /= 2; } barrier.subresourceRange.baseMipLevel = mip_levels - 1; barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier); commandBufferManager.endAndSubmitCommandBuffer(device, command_pool, queue, command_buffer); }