src/core/Texture.cpp
void
Texture2D::fromBuffer(void *buffer, VkDeviceSize bufferSize, VkFormat format, uint32_t texWidth, uint32_t texHeight,
                      VulkanDevice *device, VkQueue copyQueue, VkFilter filter, VkImageUsageFlags imageUsageFlags,
                      VkImageLayout imageLayout) {
    assert(buffer);

    this->device = device;
    width = texWidth;
    height = texHeight;
    mipLevels = 1;

    // Create a host-visible staging buffer that contains the raw image data
    VkBuffer stagingBuffer;
    VkDeviceMemory stagingMemory;

    CHECK_RESULT(device->createBuffer(
            VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
            VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
            bufferSize,
            &stagingBuffer,
            &stagingMemory, buffer));


    // Create the memory backing up the buffer handle
    VkMemoryRequirements memReqs;
    vkGetBufferMemoryRequirements(device->logicalDevice, stagingBuffer, &memReqs);
    VkMemoryAllocateInfo memAlloc = Populate::memoryAllocateInfo();
    memAlloc.allocationSize = memReqs.size;


    // Copy texture data into staging buffer
    uint8_t *data;

    CHECK_RESULT(vkMapMemory(device->logicalDevice, stagingMemory, 0, memReqs.size, 0, (void **) &data));
    memcpy(data, buffer, bufferSize);
    vkUnmapMemory(device->logicalDevice, stagingMemory);

    VkBufferImageCopy bufferCopyRegion = {};
    bufferCopyRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
    bufferCopyRegion.imageSubresource.mipLevel = 0;
    bufferCopyRegion.imageSubresource.baseArrayLayer = 0;
    bufferCopyRegion.imageSubresource.layerCount = 1;
    bufferCopyRegion.imageExtent.width = width;
    bufferCopyRegion.imageExtent.height = height;
    bufferCopyRegion.imageExtent.depth = 1;
    bufferCopyRegion.bufferOffset = 0;

    // Create optimal tiled target image
    VkImageCreateInfo imageCreateInfo = Populate::imageCreateInfo();
    imageCreateInfo.imageType = VK_IMAGE_TYPE_2D;
    imageCreateInfo.format = format;
    imageCreateInfo.mipLevels = mipLevels;
    imageCreateInfo.arrayLayers = 1;
    imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
    imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
    imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
    imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
    imageCreateInfo.extent = {width, height, 1};
    imageCreateInfo.usage = imageUsageFlags;
    // Ensure that the TRANSFER_DST bit is set for staging
    if (!(imageCreateInfo.usage & VK_IMAGE_USAGE_TRANSFER_DST_BIT)) {
        imageCreateInfo.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
    }

    CHECK_RESULT(vkCreateImage(device->logicalDevice, &imageCreateInfo, nullptr, &image));

    vkGetImageMemoryRequirements(device->logicalDevice, image, &memReqs);

    memAlloc.allocationSize = memReqs.size;
    memAlloc.memoryTypeIndex = device->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);

    CHECK_RESULT(vkAllocateMemory(device->logicalDevice, &memAlloc, nullptr, &deviceMemory));

    CHECK_RESULT(vkBindImageMemory(device->logicalDevice, image, deviceMemory, 0));

    VkImageSubresourceRange subresourceRange = {};
    subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
    subresourceRange.baseMipLevel = 0;
    subresourceRange.levelCount = mipLevels;
    subresourceRange.layerCount = 1;

    // Use a separate command buffer for texture loading
    VkCommandBuffer copyCmd = device->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true);

    // Image barrier for optimal image (target)
    // Optimal image will be used as destination for the copy
    Utils::setImageLayout(
            copyCmd,
            image,
            VK_IMAGE_LAYOUT_UNDEFINED,
            VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
            subresourceRange,
            VK_PIPELINE_STAGE_HOST_BIT,
            VK_PIPELINE_STAGE_TRANSFER_BIT);

    // Copy mip levels from staging buffer
    vkCmdCopyBufferToImage(
            copyCmd,
            stagingBuffer,
            image,
            VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
            1,
            &bufferCopyRegion
    );

    // Change texture image layout to shader read after all mip levels have been copied
    this->imageLayout = imageLayout;
    Utils::setImageLayout(
            copyCmd,
            image,
            VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
            imageLayout,
            subresourceRange,
            VK_PIPELINE_STAGE_TRANSFER_BIT,
            VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);

    device->flushCommandBuffer(copyCmd, copyQueue);

    // Clean up staging resources
    vkFreeMemory(device->logicalDevice, stagingMemory, nullptr);
    vkDestroyBuffer(device->logicalDevice, stagingBuffer, nullptr);

    // Create sampler
    VkSamplerCreateInfo samplerCreateInfo = {};
    samplerCreateInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
    samplerCreateInfo.magFilter = filter;
    samplerCreateInfo.minFilter = filter;
    samplerCreateInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
    samplerCreateInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT;
    samplerCreateInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT;
    samplerCreateInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT;
    samplerCreateInfo.mipLodBias = 0.0f;
    samplerCreateInfo.compareOp = VK_COMPARE_OP_NEVER;
    samplerCreateInfo.minLod = 0.0f;
    samplerCreateInfo.maxLod = 0.0f;
    samplerCreateInfo.maxAnisotropy = 1.0f;

    CHECK_RESULT(vkCreateSampler(device->logicalDevice, &samplerCreateInfo, nullptr, &sampler));

    // Create image view
    VkImageViewCreateInfo viewCreateInfo = {};
    viewCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
    viewCreateInfo.pNext = NULL;
    viewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
    viewCreateInfo.format = format;
    viewCreateInfo.components = {VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B,
                                 VK_COMPONENT_SWIZZLE_A};
    viewCreateInfo.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1};
    viewCreateInfo.subresourceRange.levelCount = 1;
    viewCreateInfo.image = image;

    CHECK_RESULT(vkCreateImageView(device->logicalDevice, &viewCreateInfo, nullptr, &view));

    // Update descriptor image info member that can be used for setting up descriptor sets
    updateDescriptor();
}
/**
* Creates a 2D texture from a buffer
*
* @param buffer Buffer containing texture data to upload
* @param bufferSize Size of the buffer in machine units
* @param width Width of the texture to create
* @param height Height of the texture to create
* @param format Vulkan format of the image data stored in the file
* @param device Vulkan device to create the texture on
* @param copyQueue Queue used for the texture staging copy commands (must support transfer)
* @param (Optional) filter Texture filtering for the sampler (defaults to VK_FILTER_LINEAR)
* @param (Optional) imageUsageFlags Usage flags for the texture's image (defaults to VK_IMAGE_USAGE_SAMPLED_BIT)
* @param (Optional) imageLayout Usage layout for the texture (defaults VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
*/
Creates a 2D texture from a buffer
Creates a 2D texture from a buffer
void
Texture2D::fromBuffer(void *buffer, VkDeviceSize bufferSize, VkFormat format, uint32_t texWidth, uint32_t texHeight,
                      VulkanDevice *device, VkQueue copyQueue, VkFilter filter, VkImageUsageFlags imageUsageFlags,
                      VkImageLayout imageLayout) {
    assert(buffer);
    this->device = device;
    width = texWidth;
    height = texHeight;
    mipLevels = 1;
    VkBuffer stagingBuffer;
    VkDeviceMemory stagingMemory;
    CHECK_RESULT(device->createBuffer(
            VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
            VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
            bufferSize,
            &stagingBuffer,
            &stagingMemory, buffer));
    VkMemoryRequirements memReqs;
    vkGetBufferMemoryRequirements(device->logicalDevice, stagingBuffer, &memReqs);
    VkMemoryAllocateInfo memAlloc = Populate::memoryAllocateInfo();
    memAlloc.allocationSize = memReqs.size;
    uint8_t *data;
    CHECK_RESULT(vkMapMemory(device->logicalDevice, stagingMemory, 0, memReqs.size, 0, (void **) &data));
    memcpy(data, buffer, bufferSize);
    vkUnmapMemory(device->logicalDevice, stagingMemory);
    VkBufferImageCopy bufferCopyRegion = {};
    bufferCopyRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
    bufferCopyRegion.imageSubresource.mipLevel = 0;
    bufferCopyRegion.imageSubresource.baseArrayLayer = 0;
    bufferCopyRegion.imageSubresource.layerCount = 1;
    bufferCopyRegion.imageExtent.width = width;
    bufferCopyRegion.imageExtent.height = height;
    bufferCopyRegion.imageExtent.depth = 1;
    bufferCopyRegion.bufferOffset = 0;
    VkImageCreateInfo imageCreateInfo = Populate::imageCreateInfo();
    imageCreateInfo.imageType = VK_IMAGE_TYPE_2D;
    imageCreateInfo.format = format;
    imageCreateInfo.mipLevels = mipLevels;
    imageCreateInfo.arrayLayers = 1;
    imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
    imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
    imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
    imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
    imageCreateInfo.extent = {width, height, 1};
    imageCreateInfo.usage = imageUsageFlags;
    if (!(imageCreateInfo.usage & VK_IMAGE_USAGE_TRANSFER_DST_BIT)) {
        imageCreateInfo.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
    }
    CHECK_RESULT(vkCreateImage(device->logicalDevice, &imageCreateInfo, nullptr, &image));
    vkGetImageMemoryRequirements(device->logicalDevice, image, &memReqs);
    memAlloc.allocationSize = memReqs.size;
    memAlloc.memoryTypeIndex = device->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
    CHECK_RESULT(vkAllocateMemory(device->logicalDevice, &memAlloc, nullptr, &deviceMemory));
    CHECK_RESULT(vkBindImageMemory(device->logicalDevice, image, deviceMemory, 0));
    VkImageSubresourceRange subresourceRange = {};
    subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
    subresourceRange.baseMipLevel = 0;
    subresourceRange.levelCount = mipLevels;
    subresourceRange.layerCount = 1;
    VkCommandBuffer copyCmd = device->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true);
    Utils::setImageLayout(
            copyCmd,
            image,
            VK_IMAGE_LAYOUT_UNDEFINED,
            VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
            subresourceRange,
            VK_PIPELINE_STAGE_HOST_BIT,
            VK_PIPELINE_STAGE_TRANSFER_BIT);
    vkCmdCopyBufferToImage(
            copyCmd,
            stagingBuffer,
            image,
            VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
            1,
            &bufferCopyRegion
    );
    this->imageLayout = imageLayout;
    Utils::setImageLayout(
            copyCmd,
            image,
            VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
            imageLayout,
            subresourceRange,
            VK_PIPELINE_STAGE_TRANSFER_BIT,
            VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
    device->flushCommandBuffer(copyCmd, copyQueue);
    vkFreeMemory(device->logicalDevice, stagingMemory, nullptr);
    vkDestroyBuffer(device->logicalDevice, stagingBuffer, nullptr);
    VkSamplerCreateInfo samplerCreateInfo = {};
    samplerCreateInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
    samplerCreateInfo.magFilter = filter;
    samplerCreateInfo.minFilter = filter;
    samplerCreateInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
    samplerCreateInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT;
    samplerCreateInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT;
    samplerCreateInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT;
    samplerCreateInfo.mipLodBias = 0.0f;
    samplerCreateInfo.compareOp = VK_COMPARE_OP_NEVER;
    samplerCreateInfo.minLod = 0.0f;
    samplerCreateInfo.maxLod = 0.0f;
    samplerCreateInfo.maxAnisotropy = 1.0f;
    CHECK_RESULT(vkCreateSampler(device->logicalDevice, &samplerCreateInfo, nullptr, &sampler));
    VkImageViewCreateInfo viewCreateInfo = {};
    viewCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
    viewCreateInfo.pNext = NULL;
    viewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
    viewCreateInfo.format = format;
    viewCreateInfo.components = {VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B,
                                 VK_COMPONENT_SWIZZLE_A};
    viewCreateInfo.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1};
    viewCreateInfo.subresourceRange.levelCount = 1;
    viewCreateInfo.image = image;
    CHECK_RESULT(vkCreateImageView(device->logicalDevice, &viewCreateInfo, nullptr, &view));
    updateDescriptor();
}
void Texture2D :: fromBuffer ( void * buffer , VkDeviceSize bufferSize , VkFormat format , uint32_t texWidth , uint32_t texHeight , VulkanDevice * device , VkQueue copyQueue , VkFilter filter , VkImageUsageFlags imageUsageFlags , VkImageLayout imageLayout ) { assert ( buffer ) ; this -> device = device ; width = texWidth ; height = texHeight ; mipLevels = 1 ; VkBuffer stagingBuffer ; VkDeviceMemory stagingMemory ; CHECK_RESULT ( device -> createBuffer ( VK_BUFFER_USAGE_TRANSFER_SRC_BIT , VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT , bufferSize , & stagingBuffer , & stagingMemory , buffer ) ) ; VkMemoryRequirements memReqs ; vkGetBufferMemoryRequirements ( device -> logicalDevice , stagingBuffer , & memReqs ) ; VkMemoryAllocateInfo memAlloc = Populate :: memoryAllocateInfo ( ) ; memAlloc . allocationSize = memReqs . size ; uint8_t * data ; CHECK_RESULT ( vkMapMemory ( device -> logicalDevice , stagingMemory , 0 , memReqs . size , 0 , ( void * * ) & data ) ) ; memcpy ( data , buffer , bufferSize ) ; vkUnmapMemory ( device -> logicalDevice , stagingMemory ) ; VkBufferImageCopy bufferCopyRegion = { } ; bufferCopyRegion . imageSubresource . aspectMask = VK_IMAGE_ASPECT_COLOR_BIT ; bufferCopyRegion . imageSubresource . mipLevel = 0 ; bufferCopyRegion . imageSubresource . baseArrayLayer = 0 ; bufferCopyRegion . imageSubresource . layerCount = 1 ; bufferCopyRegion . imageExtent . width = width ; bufferCopyRegion . imageExtent . height = height ; bufferCopyRegion . imageExtent . depth = 1 ; bufferCopyRegion . bufferOffset = 0 ; VkImageCreateInfo imageCreateInfo = Populate :: imageCreateInfo ( ) ; imageCreateInfo . imageType = VK_IMAGE_TYPE_2D ; imageCreateInfo . format = format ; imageCreateInfo . mipLevels = mipLevels ; imageCreateInfo . arrayLayers = 1 ; imageCreateInfo . samples = VK_SAMPLE_COUNT_1_BIT ; imageCreateInfo . tiling = VK_IMAGE_TILING_OPTIMAL ; imageCreateInfo . sharingMode = VK_SHARING_MODE_EXCLUSIVE ; imageCreateInfo . initialLayout = VK_IMAGE_LAYOUT_UNDEFINED ; imageCreateInfo . extent = { width , height , 1 } ; imageCreateInfo . usage = imageUsageFlags ; if ( ! ( imageCreateInfo . usage & VK_IMAGE_USAGE_TRANSFER_DST_BIT ) ) { imageCreateInfo . usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT ; } CHECK_RESULT ( vkCreateImage ( device -> logicalDevice , & imageCreateInfo , nullptr , & image ) ) ; vkGetImageMemoryRequirements ( device -> logicalDevice , image , & memReqs ) ; memAlloc . allocationSize = memReqs . size ; memAlloc . memoryTypeIndex = device -> getMemoryType ( memReqs . memoryTypeBits , VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT ) ; CHECK_RESULT ( vkAllocateMemory ( device -> logicalDevice , & memAlloc , nullptr , & deviceMemory ) ) ; CHECK_RESULT ( vkBindImageMemory ( device -> logicalDevice , image , deviceMemory , 0 ) ) ; VkImageSubresourceRange subresourceRange = { } ; subresourceRange . aspectMask = VK_IMAGE_ASPECT_COLOR_BIT ; subresourceRange . baseMipLevel = 0 ; subresourceRange . levelCount = mipLevels ; subresourceRange . layerCount = 1 ; VkCommandBuffer copyCmd = device -> createCommandBuffer ( VK_COMMAND_BUFFER_LEVEL_PRIMARY , true ) ; Utils :: setImageLayout ( copyCmd , image , VK_IMAGE_LAYOUT_UNDEFINED , VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL , subresourceRange , VK_PIPELINE_STAGE_HOST_BIT , VK_PIPELINE_STAGE_TRANSFER_BIT ) ; vkCmdCopyBufferToImage ( copyCmd , stagingBuffer , image , VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL , 1 , & bufferCopyRegion ) ; this -> imageLayout = imageLayout ; Utils :: setImageLayout ( copyCmd , image , VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL , imageLayout , subresourceRange , VK_PIPELINE_STAGE_TRANSFER_BIT , VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT ) ; device -> flushCommandBuffer ( copyCmd , copyQueue ) ; vkFreeMemory ( device -> logicalDevice , stagingMemory , nullptr ) ; vkDestroyBuffer ( device -> logicalDevice , stagingBuffer , nullptr ) ; VkSamplerCreateInfo samplerCreateInfo = { } ; samplerCreateInfo . sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO ; samplerCreateInfo . magFilter = filter ; samplerCreateInfo . minFilter = filter ; samplerCreateInfo . mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR ; samplerCreateInfo . addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT ; samplerCreateInfo . addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT ; samplerCreateInfo . addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT ; samplerCreateInfo . mipLodBias = 0.0f ; samplerCreateInfo . compareOp = VK_COMPARE_OP_NEVER ; samplerCreateInfo . minLod = 0.0f ; samplerCreateInfo . maxLod = 0.0f ; samplerCreateInfo . maxAnisotropy = 1.0f ; CHECK_RESULT ( vkCreateSampler ( device -> logicalDevice , & samplerCreateInfo , nullptr , & sampler ) ) ; VkImageViewCreateInfo viewCreateInfo = { } ; viewCreateInfo . sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO ; viewCreateInfo . pNext = NULL ; viewCreateInfo . viewType = VK_IMAGE_VIEW_TYPE_2D ; viewCreateInfo . format = format ; viewCreateInfo . components = { VK_COMPONENT_SWIZZLE_R , VK_COMPONENT_SWIZZLE_G , VK_COMPONENT_SWIZZLE_B , VK_COMPONENT_SWIZZLE_A } ; viewCreateInfo . subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT , 0 , 1 , 0 , 1 } ; viewCreateInfo . subresourceRange . levelCount = 1 ; viewCreateInfo . image = image ; CHECK_RESULT ( vkCreateImageView ( device -> logicalDevice , & viewCreateInfo , nullptr , & view ) ) ; updateDescriptor ( ) ; }
Creates a 2D texture from a buffer
Creates a 2D texture from a buffer
// Create a host-visible staging buffer that contains the raw image data // Create the memory backing up the buffer handle // Copy texture data into staging buffer // Create optimal tiled target image // Ensure that the TRANSFER_DST bit is set for staging // Use a separate command buffer for texture loading // Image barrier for optimal image (target) // Optimal image will be used as destination for the copy // Copy mip levels from staging buffer // Change texture image layout to shader read after all mip levels have been copied // Clean up staging resources // Create sampler // Create image view // Update descriptor image info member that can be used for setting up descriptor sets