2017-01-29 12:53:57 +01:00
/*
* Vulkan texture loader
*
2019-08-02 20:41:44 +02:00
* Copyright ( C ) by Sascha Willems - www . saschawillems . de
2017-01-29 12:53:57 +01:00
*
* This code is licensed under the MIT license ( MIT ) ( http : //opensource.org/licenses/MIT)
*/
# pragma once
# include <stdlib.h>
# include <string>
# include <fstream>
# include <vector>
# include "vulkan/vulkan.h"
2019-08-02 20:41:44 +02:00
# include <ktx.h>
# include <ktxvulkan.h>
2017-01-29 12:53:57 +01:00
2017-02-12 13:10:05 +01:00
# include "VulkanTools.h"
2017-02-12 10:16:07 +01:00
# include "VulkanDevice.hpp"
2017-02-12 10:44:51 +01:00
# include "VulkanBuffer.hpp"
2017-01-29 12:53:57 +01:00
# if defined(__ANDROID__)
# include <android/asset_manager.h>
# endif
namespace vks
{
/** @brief Vulkan texture base class */
class Texture {
public :
2017-02-12 10:16:07 +01:00
vks : : VulkanDevice * device ;
2017-01-29 12:53:57 +01:00
VkImage image ;
VkImageLayout imageLayout ;
VkDeviceMemory deviceMemory ;
VkImageView view ;
uint32_t width , height ;
uint32_t mipLevels ;
uint32_t layerCount ;
VkDescriptorImageInfo descriptor ;
VkSampler sampler ;
void updateDescriptor ( )
{
descriptor . sampler = sampler ;
descriptor . imageView = view ;
descriptor . imageLayout = imageLayout ;
}
void destroy ( )
{
vkDestroyImageView ( device - > logicalDevice , view , nullptr ) ;
vkDestroyImage ( device - > logicalDevice , image , nullptr ) ;
if ( sampler )
{
vkDestroySampler ( device - > logicalDevice , sampler , nullptr ) ;
}
vkFreeMemory ( device - > logicalDevice , deviceMemory , nullptr ) ;
}
2019-08-02 20:41:44 +02:00
ktxResult loadKTXFile ( std : : string filename , ktxTexture * * target )
{
ktxResult result = KTX_SUCCESS ;
# if defined(__ANDROID__)
AAsset * asset = AAssetManager_open ( androidApp - > activity - > assetManager , filename . c_str ( ) , AASSET_MODE_STREAMING ) ;
if ( ! asset ) {
vks : : tools : : exitFatal ( " Could not load texture from " + filename + " \n \n The file may be part of the additional asset pack. \n \n Run \" download_assets.py \" in the repository root to download the latest version. " , - 1 ) ;
}
size_t size = AAsset_getLength ( asset ) ;
assert ( size > 0 ) ;
2019-08-03 18:20:27 +02:00
ktx_uint8_t * textureData = new ktx_uint8_t [ size ] ;
2019-08-02 20:41:44 +02:00
AAsset_read ( asset , textureData , size ) ;
AAsset_close ( asset ) ;
result = ktxTexture_CreateFromMemory ( textureData , size , KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT , target ) ;
2019-08-03 18:20:27 +02:00
delete [ ] textureData ;
2019-08-02 20:41:44 +02:00
# else
if ( ! vks : : tools : : fileExists ( filename ) ) {
vks : : tools : : exitFatal ( " Could not load texture from " + filename + " \n \n The file may be part of the additional asset pack. \n \n Run \" download_assets.py \" in the repository root to download the latest version. " , - 1 ) ;
}
result = ktxTexture_CreateFromNamedFile ( filename . c_str ( ) , KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT , target ) ;
# endif
return result ;
}
2017-01-29 12:53:57 +01:00
} ;
/** @brief 2D texture */
class Texture2D : public Texture {
public :
/**
* Load a 2 D texture including all mip levels
*
2019-08-02 20:41:44 +02:00
* @ param filename File to load ( supports . ktx )
2017-01-29 12:53:57 +01:00
* @ 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 ) 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 )
* @ param ( Optional ) forceLinear Force linear tiling ( not advised , defaults to false )
*
*/
void loadFromFile (
std : : string filename ,
VkFormat format ,
2017-02-12 10:16:07 +01:00
vks : : VulkanDevice * device ,
2017-01-29 12:53:57 +01:00
VkQueue copyQueue ,
VkImageUsageFlags imageUsageFlags = VK_IMAGE_USAGE_SAMPLED_BIT ,
VkImageLayout imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL ,
bool forceLinear = false )
{
2019-08-02 20:41:44 +02:00
ktxTexture * ktxTexture ;
ktxResult result = loadKTXFile ( filename , & ktxTexture ) ;
assert ( result = = KTX_SUCCESS ) ;
2017-01-29 12:53:57 +01:00
this - > device = device ;
2019-08-02 20:41:44 +02:00
width = ktxTexture - > baseWidth ;
height = ktxTexture - > baseHeight ;
mipLevels = ktxTexture - > numLevels ;
ktx_uint8_t * ktxTextureData = ktxTexture_GetData ( ktxTexture ) ;
ktx_size_t ktxTextureSize = ktxTexture_GetSize ( ktxTexture ) ;
2017-01-29 12:53:57 +01:00
// Get device properites for the requested texture format
VkFormatProperties formatProperties ;
vkGetPhysicalDeviceFormatProperties ( device - > physicalDevice , format , & formatProperties ) ;
// Only use linear tiling if requested (and supported by the device)
// Support for linear tiling is mostly limited, so prefer to use
// optimal tiling instead
// On most implementations linear tiling will only support a very
// limited amount of formats and features (mip maps, cubemaps, arrays, etc.)
VkBool32 useStaging = ! forceLinear ;
2017-02-12 11:12:42 +01:00
VkMemoryAllocateInfo memAllocInfo = vks : : initializers : : memoryAllocateInfo ( ) ;
2017-01-29 12:53:57 +01:00
VkMemoryRequirements memReqs ;
// Use a separate command buffer for texture loading
VkCommandBuffer copyCmd = device - > createCommandBuffer ( VK_COMMAND_BUFFER_LEVEL_PRIMARY , true ) ;
if ( useStaging )
{
// Create a host-visible staging buffer that contains the raw image data
VkBuffer stagingBuffer ;
VkDeviceMemory stagingMemory ;
2017-02-12 11:12:42 +01:00
VkBufferCreateInfo bufferCreateInfo = vks : : initializers : : bufferCreateInfo ( ) ;
2019-08-02 20:41:44 +02:00
bufferCreateInfo . size = ktxTextureSize ;
2017-01-29 12:53:57 +01:00
// This buffer is used as a transfer source for the buffer copy
bufferCreateInfo . usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT ;
bufferCreateInfo . sharingMode = VK_SHARING_MODE_EXCLUSIVE ;
VK_CHECK_RESULT ( vkCreateBuffer ( device - > logicalDevice , & bufferCreateInfo , nullptr , & stagingBuffer ) ) ;
// Get memory requirements for the staging buffer (alignment, memory type bits)
vkGetBufferMemoryRequirements ( device - > logicalDevice , stagingBuffer , & memReqs ) ;
memAllocInfo . allocationSize = memReqs . size ;
// Get memory type index for a host visible buffer
memAllocInfo . memoryTypeIndex = device - > getMemoryType ( memReqs . memoryTypeBits , VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT ) ;
VK_CHECK_RESULT ( vkAllocateMemory ( device - > logicalDevice , & memAllocInfo , nullptr , & stagingMemory ) ) ;
VK_CHECK_RESULT ( vkBindBufferMemory ( device - > logicalDevice , stagingBuffer , stagingMemory , 0 ) ) ;
// Copy texture data into staging buffer
uint8_t * data ;
VK_CHECK_RESULT ( vkMapMemory ( device - > logicalDevice , stagingMemory , 0 , memReqs . size , 0 , ( void * * ) & data ) ) ;
2019-08-02 20:41:44 +02:00
memcpy ( data , ktxTextureData , ktxTextureSize ) ;
2017-01-29 12:53:57 +01:00
vkUnmapMemory ( device - > logicalDevice , stagingMemory ) ;
// Setup buffer copy regions for each mip level
std : : vector < VkBufferImageCopy > bufferCopyRegions ;
for ( uint32_t i = 0 ; i < mipLevels ; i + + )
{
2019-08-02 20:41:44 +02:00
ktx_size_t offset ;
2019-09-13 22:02:46 +02:00
KTX_error_code result = ktxTexture_GetImageOffset ( ktxTexture , i , 0 , 0 , & offset ) ;
2019-09-13 22:05:57 +02:00
assert ( result = = KTX_SUCCESS ) ;
2019-08-02 20:41:44 +02:00
2017-01-29 12:53:57 +01:00
VkBufferImageCopy bufferCopyRegion = { } ;
bufferCopyRegion . imageSubresource . aspectMask = VK_IMAGE_ASPECT_COLOR_BIT ;
bufferCopyRegion . imageSubresource . mipLevel = i ;
bufferCopyRegion . imageSubresource . baseArrayLayer = 0 ;
bufferCopyRegion . imageSubresource . layerCount = 1 ;
2020-02-07 09:15:13 +00:00
bufferCopyRegion . imageExtent . width = std : : max ( 1u , ktxTexture - > baseWidth > > i ) ;
bufferCopyRegion . imageExtent . height = std : : max ( 1u , ktxTexture - > baseHeight > > i ) ;
2017-01-29 12:53:57 +01:00
bufferCopyRegion . imageExtent . depth = 1 ;
bufferCopyRegion . bufferOffset = offset ;
bufferCopyRegions . push_back ( bufferCopyRegion ) ;
}
// Create optimal tiled target image
2017-02-12 11:12:42 +01:00
VkImageCreateInfo imageCreateInfo = vks : : initializers : : imageCreateInfo ( ) ;
2017-01-29 12:53:57 +01:00
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 ;
}
VK_CHECK_RESULT ( vkCreateImage ( device - > logicalDevice , & imageCreateInfo , nullptr , & image ) ) ;
vkGetImageMemoryRequirements ( device - > logicalDevice , image , & memReqs ) ;
memAllocInfo . allocationSize = memReqs . size ;
memAllocInfo . memoryTypeIndex = device - > getMemoryType ( memReqs . memoryTypeBits , VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT ) ;
VK_CHECK_RESULT ( vkAllocateMemory ( device - > logicalDevice , & memAllocInfo , nullptr , & deviceMemory ) ) ;
VK_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 ;
// Image barrier for optimal image (target)
// Optimal image will be used as destination for the copy
2017-02-12 13:10:05 +01:00
vks : : tools : : setImageLayout (
2017-01-29 12:53:57 +01:00
copyCmd ,
image ,
VK_IMAGE_LAYOUT_UNDEFINED ,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL ,
subresourceRange ) ;
// Copy mip levels from staging buffer
vkCmdCopyBufferToImage (
copyCmd ,
stagingBuffer ,
image ,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL ,
static_cast < uint32_t > ( bufferCopyRegions . size ( ) ) ,
bufferCopyRegions . data ( )
) ;
// Change texture image layout to shader read after all mip levels have been copied
this - > imageLayout = imageLayout ;
2017-02-12 13:10:05 +01:00
vks : : tools : : setImageLayout (
2017-01-29 12:53:57 +01:00
copyCmd ,
image ,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL ,
imageLayout ,
subresourceRange ) ;
device - > flushCommandBuffer ( copyCmd , copyQueue ) ;
// Clean up staging resources
vkFreeMemory ( device - > logicalDevice , stagingMemory , nullptr ) ;
vkDestroyBuffer ( device - > logicalDevice , stagingBuffer , nullptr ) ;
}
else
{
// Prefer using optimal tiling, as linear tiling
// may support only a small set of features
// depending on implementation (e.g. no mip maps, only one layer, etc.)
// Check if this support is supported for linear tiling
assert ( formatProperties . linearTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT ) ;
VkImage mappableImage ;
VkDeviceMemory mappableMemory ;
2017-02-12 11:12:42 +01:00
VkImageCreateInfo imageCreateInfo = vks : : initializers : : imageCreateInfo ( ) ;
2017-01-29 12:53:57 +01:00
imageCreateInfo . imageType = VK_IMAGE_TYPE_2D ;
imageCreateInfo . format = format ;
imageCreateInfo . extent = { width , height , 1 } ;
imageCreateInfo . mipLevels = 1 ;
imageCreateInfo . arrayLayers = 1 ;
imageCreateInfo . samples = VK_SAMPLE_COUNT_1_BIT ;
imageCreateInfo . tiling = VK_IMAGE_TILING_LINEAR ;
imageCreateInfo . usage = imageUsageFlags ;
imageCreateInfo . sharingMode = VK_SHARING_MODE_EXCLUSIVE ;
imageCreateInfo . initialLayout = VK_IMAGE_LAYOUT_UNDEFINED ;
// Load mip map level 0 to linear tiling image
VK_CHECK_RESULT ( vkCreateImage ( device - > logicalDevice , & imageCreateInfo , nullptr , & mappableImage ) ) ;
// Get memory requirements for this image
// like size and alignment
vkGetImageMemoryRequirements ( device - > logicalDevice , mappableImage , & memReqs ) ;
// Set memory allocation size to required memory size
memAllocInfo . allocationSize = memReqs . size ;
// Get memory type that can be mapped to host memory
memAllocInfo . memoryTypeIndex = device - > getMemoryType ( memReqs . memoryTypeBits , VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT ) ;
// Allocate host memory
VK_CHECK_RESULT ( vkAllocateMemory ( device - > logicalDevice , & memAllocInfo , nullptr , & mappableMemory ) ) ;
// Bind allocated image for use
VK_CHECK_RESULT ( vkBindImageMemory ( device - > logicalDevice , mappableImage , mappableMemory , 0 ) ) ;
// Get sub resource layout
// Mip map count, array layer, etc.
VkImageSubresource subRes = { } ;
subRes . aspectMask = VK_IMAGE_ASPECT_COLOR_BIT ;
subRes . mipLevel = 0 ;
VkSubresourceLayout subResLayout ;
void * data ;
// Get sub resources layout
// Includes row pitch, size offsets, etc.
vkGetImageSubresourceLayout ( device - > logicalDevice , mappableImage , & subRes , & subResLayout ) ;
// Map image memory
VK_CHECK_RESULT ( vkMapMemory ( device - > logicalDevice , mappableMemory , 0 , memReqs . size , 0 , & data ) ) ;
// Copy image data into memory
2019-08-03 09:46:41 +02:00
memcpy ( data , ktxTextureData , memReqs . size ) ;
2017-01-29 12:53:57 +01:00
vkUnmapMemory ( device - > logicalDevice , mappableMemory ) ;
// Linear tiled images don't need to be staged
// and can be directly used as textures
image = mappableImage ;
deviceMemory = mappableMemory ;
2018-05-02 14:24:24 +02:00
this - > imageLayout = imageLayout ;
2017-01-29 12:53:57 +01:00
// Setup image memory barrier
2017-02-12 13:10:05 +01:00
vks : : tools : : setImageLayout ( copyCmd , image , VK_IMAGE_ASPECT_COLOR_BIT , VK_IMAGE_LAYOUT_UNDEFINED , imageLayout ) ;
2017-01-29 12:53:57 +01:00
device - > flushCommandBuffer ( copyCmd , copyQueue ) ;
}
2019-08-02 20:41:44 +02:00
ktxTexture_Destroy ( ktxTexture ) ;
2017-01-29 12:53:57 +01:00
// Create a defaultsampler
VkSamplerCreateInfo samplerCreateInfo = { } ;
samplerCreateInfo . sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO ;
samplerCreateInfo . magFilter = VK_FILTER_LINEAR ;
samplerCreateInfo . minFilter = VK_FILTER_LINEAR ;
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 ;
// Max level-of-detail should match mip level count
samplerCreateInfo . maxLod = ( useStaging ) ? ( float ) mipLevels : 0.0f ;
2017-06-14 23:39:41 +02:00
// Only enable anisotropic filtering if enabled on the devicec
samplerCreateInfo . maxAnisotropy = device - > enabledFeatures . samplerAnisotropy ? device - > properties . limits . maxSamplerAnisotropy : 1.0f ;
2018-01-05 20:47:27 +01:00
samplerCreateInfo . anisotropyEnable = device - > enabledFeatures . samplerAnisotropy ;
2017-01-29 12:53:57 +01:00
samplerCreateInfo . borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE ;
VK_CHECK_RESULT ( vkCreateSampler ( device - > logicalDevice , & samplerCreateInfo , nullptr , & sampler ) ) ;
// Create image view
// Textures are not directly accessed by the shaders and
// are abstracted by image views containing additional
// information and sub resource ranges
VkImageViewCreateInfo viewCreateInfo = { } ;
viewCreateInfo . sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO ;
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 } ;
// Linear tiling usually won't support mip maps
// Only set mip map count if optimal tiling is used
viewCreateInfo . subresourceRange . levelCount = ( useStaging ) ? mipLevels : 1 ;
viewCreateInfo . image = image ;
VK_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 2 D 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 )
*/
void fromBuffer (
void * buffer ,
VkDeviceSize bufferSize ,
VkFormat format ,
2019-01-21 00:05:51 +08:00
uint32_t texWidth ,
uint32_t texHeight ,
2017-02-12 10:16:07 +01:00
vks : : VulkanDevice * device ,
2017-01-29 12:53:57 +01:00
VkQueue copyQueue ,
VkFilter filter = VK_FILTER_LINEAR ,
VkImageUsageFlags imageUsageFlags = VK_IMAGE_USAGE_SAMPLED_BIT ,
VkImageLayout imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL )
{
assert ( buffer ) ;
this - > device = device ;
2019-01-21 00:05:51 +08:00
width = texWidth ;
height = texHeight ;
2017-01-29 12:53:57 +01:00
mipLevels = 1 ;
2017-02-12 11:12:42 +01:00
VkMemoryAllocateInfo memAllocInfo = vks : : initializers : : memoryAllocateInfo ( ) ;
2017-01-29 12:53:57 +01:00
VkMemoryRequirements memReqs ;
// Use a separate command buffer for texture loading
VkCommandBuffer copyCmd = device - > createCommandBuffer ( VK_COMMAND_BUFFER_LEVEL_PRIMARY , true ) ;
// Create a host-visible staging buffer that contains the raw image data
VkBuffer stagingBuffer ;
VkDeviceMemory stagingMemory ;
2017-02-12 11:12:42 +01:00
VkBufferCreateInfo bufferCreateInfo = vks : : initializers : : bufferCreateInfo ( ) ;
2017-01-29 12:53:57 +01:00
bufferCreateInfo . size = bufferSize ;
// This buffer is used as a transfer source for the buffer copy
bufferCreateInfo . usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT ;
bufferCreateInfo . sharingMode = VK_SHARING_MODE_EXCLUSIVE ;
VK_CHECK_RESULT ( vkCreateBuffer ( device - > logicalDevice , & bufferCreateInfo , nullptr , & stagingBuffer ) ) ;
// Get memory requirements for the staging buffer (alignment, memory type bits)
vkGetBufferMemoryRequirements ( device - > logicalDevice , stagingBuffer , & memReqs ) ;
memAllocInfo . allocationSize = memReqs . size ;
// Get memory type index for a host visible buffer
memAllocInfo . memoryTypeIndex = device - > getMemoryType ( memReqs . memoryTypeBits , VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT ) ;
VK_CHECK_RESULT ( vkAllocateMemory ( device - > logicalDevice , & memAllocInfo , nullptr , & stagingMemory ) ) ;
VK_CHECK_RESULT ( vkBindBufferMemory ( device - > logicalDevice , stagingBuffer , stagingMemory , 0 ) ) ;
// Copy texture data into staging buffer
uint8_t * data ;
VK_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
2017-02-12 11:12:42 +01:00
VkImageCreateInfo imageCreateInfo = vks : : initializers : : imageCreateInfo ( ) ;
2017-01-29 12:53:57 +01:00
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 ;
}
VK_CHECK_RESULT ( vkCreateImage ( device - > logicalDevice , & imageCreateInfo , nullptr , & image ) ) ;
vkGetImageMemoryRequirements ( device - > logicalDevice , image , & memReqs ) ;
memAllocInfo . allocationSize = memReqs . size ;
memAllocInfo . memoryTypeIndex = device - > getMemoryType ( memReqs . memoryTypeBits , VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT ) ;
VK_CHECK_RESULT ( vkAllocateMemory ( device - > logicalDevice , & memAllocInfo , nullptr , & deviceMemory ) ) ;
VK_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 ;
// Image barrier for optimal image (target)
// Optimal image will be used as destination for the copy
2017-02-12 13:10:05 +01:00
vks : : tools : : setImageLayout (
2017-01-29 12:53:57 +01:00
copyCmd ,
image ,
VK_IMAGE_LAYOUT_UNDEFINED ,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL ,
subresourceRange ) ;
// 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 ;
2017-02-12 13:10:05 +01:00
vks : : tools : : setImageLayout (
2017-01-29 12:53:57 +01:00
copyCmd ,
image ,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL ,
imageLayout ,
subresourceRange ) ;
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 ;
2017-06-01 22:32:39 +02:00
samplerCreateInfo . maxAnisotropy = 1.0f ;
2017-01-29 12:53:57 +01:00
VK_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 ;
VK_CHECK_RESULT ( vkCreateImageView ( device - > logicalDevice , & viewCreateInfo , nullptr , & view ) ) ;
// Update descriptor image info member that can be used for setting up descriptor sets
updateDescriptor ( ) ;
}
} ;
/** @brief 2D array texture */
class Texture2DArray : public Texture {
public :
/**
* Load a 2 D texture array including all mip levels
*
2019-08-02 20:41:44 +02:00
* @ param filename File to load ( supports . ktx )
2017-01-29 12:53:57 +01:00
* @ 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 ) 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 )
*
*/
void loadFromFile (
std : : string filename ,
VkFormat format ,
2017-02-12 10:16:07 +01:00
vks : : VulkanDevice * device ,
2017-01-29 12:53:57 +01:00
VkQueue copyQueue ,
VkImageUsageFlags imageUsageFlags = VK_IMAGE_USAGE_SAMPLED_BIT ,
VkImageLayout imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL )
{
2019-08-02 20:41:44 +02:00
ktxTexture * ktxTexture ;
ktxResult result = loadKTXFile ( filename , & ktxTexture ) ;
assert ( result = = KTX_SUCCESS ) ;
2017-01-29 12:53:57 +01:00
this - > device = device ;
2019-08-02 20:41:44 +02:00
width = ktxTexture - > baseWidth ;
height = ktxTexture - > baseHeight ;
layerCount = ktxTexture - > numLayers ;
mipLevels = ktxTexture - > numLevels ;
ktx_uint8_t * ktxTextureData = ktxTexture_GetData ( ktxTexture ) ;
ktx_size_t ktxTextureSize = ktxTexture_GetSize ( ktxTexture ) ;
2017-01-29 12:53:57 +01:00
2017-02-12 11:12:42 +01:00
VkMemoryAllocateInfo memAllocInfo = vks : : initializers : : memoryAllocateInfo ( ) ;
2017-01-29 12:53:57 +01:00
VkMemoryRequirements memReqs ;
// Create a host-visible staging buffer that contains the raw image data
VkBuffer stagingBuffer ;
VkDeviceMemory stagingMemory ;
2017-02-12 11:12:42 +01:00
VkBufferCreateInfo bufferCreateInfo = vks : : initializers : : bufferCreateInfo ( ) ;
2019-08-02 20:41:44 +02:00
bufferCreateInfo . size = ktxTextureSize ;
2017-01-29 12:53:57 +01:00
// This buffer is used as a transfer source for the buffer copy
bufferCreateInfo . usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT ;
bufferCreateInfo . sharingMode = VK_SHARING_MODE_EXCLUSIVE ;
VK_CHECK_RESULT ( vkCreateBuffer ( device - > logicalDevice , & bufferCreateInfo , nullptr , & stagingBuffer ) ) ;
// Get memory requirements for the staging buffer (alignment, memory type bits)
vkGetBufferMemoryRequirements ( device - > logicalDevice , stagingBuffer , & memReqs ) ;
memAllocInfo . allocationSize = memReqs . size ;
// Get memory type index for a host visible buffer
memAllocInfo . memoryTypeIndex = device - > getMemoryType ( memReqs . memoryTypeBits , VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT ) ;
VK_CHECK_RESULT ( vkAllocateMemory ( device - > logicalDevice , & memAllocInfo , nullptr , & stagingMemory ) ) ;
VK_CHECK_RESULT ( vkBindBufferMemory ( device - > logicalDevice , stagingBuffer , stagingMemory , 0 ) ) ;
// Copy texture data into staging buffer
uint8_t * data ;
VK_CHECK_RESULT ( vkMapMemory ( device - > logicalDevice , stagingMemory , 0 , memReqs . size , 0 , ( void * * ) & data ) ) ;
2019-08-02 20:41:44 +02:00
memcpy ( data , ktxTextureData , ktxTextureSize ) ;
2017-01-29 12:53:57 +01:00
vkUnmapMemory ( device - > logicalDevice , stagingMemory ) ;
2020-01-12 12:54:41 +01:00
// Setup buffer copy regions for each layer including all of its miplevels
2017-01-29 12:53:57 +01:00
std : : vector < VkBufferImageCopy > bufferCopyRegions ;
for ( uint32_t layer = 0 ; layer < layerCount ; layer + + )
{
for ( uint32_t level = 0 ; level < mipLevels ; level + + )
{
2019-08-02 20:41:44 +02:00
ktx_size_t offset ;
2019-09-13 22:02:46 +02:00
KTX_error_code result = ktxTexture_GetImageOffset ( ktxTexture , level , layer , 0 , & offset ) ;
2019-09-13 22:05:57 +02:00
assert ( result = = KTX_SUCCESS ) ;
2019-08-02 20:41:44 +02:00
2017-01-29 12:53:57 +01:00
VkBufferImageCopy bufferCopyRegion = { } ;
bufferCopyRegion . imageSubresource . aspectMask = VK_IMAGE_ASPECT_COLOR_BIT ;
bufferCopyRegion . imageSubresource . mipLevel = level ;
bufferCopyRegion . imageSubresource . baseArrayLayer = layer ;
bufferCopyRegion . imageSubresource . layerCount = 1 ;
2019-08-02 20:41:44 +02:00
bufferCopyRegion . imageExtent . width = ktxTexture - > baseWidth > > level ;
bufferCopyRegion . imageExtent . height = ktxTexture - > baseHeight > > level ;
2017-01-29 12:53:57 +01:00
bufferCopyRegion . imageExtent . depth = 1 ;
bufferCopyRegion . bufferOffset = offset ;
bufferCopyRegions . push_back ( bufferCopyRegion ) ;
}
}
// Create optimal tiled target image
2017-02-12 11:12:42 +01:00
VkImageCreateInfo imageCreateInfo = vks : : initializers : : imageCreateInfo ( ) ;
2017-01-29 12:53:57 +01:00
imageCreateInfo . imageType = VK_IMAGE_TYPE_2D ;
imageCreateInfo . format = format ;
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 ;
}
imageCreateInfo . arrayLayers = layerCount ;
imageCreateInfo . mipLevels = mipLevels ;
VK_CHECK_RESULT ( vkCreateImage ( device - > logicalDevice , & imageCreateInfo , nullptr , & image ) ) ;
vkGetImageMemoryRequirements ( device - > logicalDevice , image , & memReqs ) ;
memAllocInfo . allocationSize = memReqs . size ;
memAllocInfo . memoryTypeIndex = device - > getMemoryType ( memReqs . memoryTypeBits , VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT ) ;
VK_CHECK_RESULT ( vkAllocateMemory ( device - > logicalDevice , & memAllocInfo , nullptr , & deviceMemory ) ) ;
VK_CHECK_RESULT ( vkBindImageMemory ( device - > logicalDevice , image , deviceMemory , 0 ) ) ;
// Use a separate command buffer for texture loading
VkCommandBuffer copyCmd = device - > createCommandBuffer ( VK_COMMAND_BUFFER_LEVEL_PRIMARY , true ) ;
// Image barrier for optimal image (target)
// Set initial layout for all array layers (faces) of the optimal (target) tiled texture
VkImageSubresourceRange subresourceRange = { } ;
subresourceRange . aspectMask = VK_IMAGE_ASPECT_COLOR_BIT ;
subresourceRange . baseMipLevel = 0 ;
subresourceRange . levelCount = mipLevels ;
subresourceRange . layerCount = layerCount ;
2017-02-12 13:10:05 +01:00
vks : : tools : : setImageLayout (
2017-01-29 12:53:57 +01:00
copyCmd ,
image ,
VK_IMAGE_LAYOUT_UNDEFINED ,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL ,
subresourceRange ) ;
// Copy the layers and mip levels from the staging buffer to the optimal tiled image
vkCmdCopyBufferToImage (
copyCmd ,
stagingBuffer ,
image ,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL ,
static_cast < uint32_t > ( bufferCopyRegions . size ( ) ) ,
bufferCopyRegions . data ( ) ) ;
// Change texture image layout to shader read after all faces have been copied
this - > imageLayout = imageLayout ;
2017-02-12 13:10:05 +01:00
vks : : tools : : setImageLayout (
2017-01-29 12:53:57 +01:00
copyCmd ,
image ,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL ,
imageLayout ,
subresourceRange ) ;
device - > flushCommandBuffer ( copyCmd , copyQueue ) ;
// Create sampler
2017-02-12 11:12:42 +01:00
VkSamplerCreateInfo samplerCreateInfo = vks : : initializers : : samplerCreateInfo ( ) ;
2017-01-29 12:53:57 +01:00
samplerCreateInfo . magFilter = VK_FILTER_LINEAR ;
samplerCreateInfo . minFilter = VK_FILTER_LINEAR ;
samplerCreateInfo . mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR ;
samplerCreateInfo . addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE ;
samplerCreateInfo . addressModeV = samplerCreateInfo . addressModeU ;
samplerCreateInfo . addressModeW = samplerCreateInfo . addressModeU ;
samplerCreateInfo . mipLodBias = 0.0f ;
2017-06-14 23:39:41 +02:00
samplerCreateInfo . maxAnisotropy = device - > enabledFeatures . samplerAnisotropy ? device - > properties . limits . maxSamplerAnisotropy : 1.0f ;
2018-01-05 20:47:27 +01:00
samplerCreateInfo . anisotropyEnable = device - > enabledFeatures . samplerAnisotropy ;
2017-01-29 12:53:57 +01:00
samplerCreateInfo . compareOp = VK_COMPARE_OP_NEVER ;
samplerCreateInfo . minLod = 0.0f ;
samplerCreateInfo . maxLod = ( float ) mipLevels ;
samplerCreateInfo . borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE ;
VK_CHECK_RESULT ( vkCreateSampler ( device - > logicalDevice , & samplerCreateInfo , nullptr , & sampler ) ) ;
// Create image view
2017-02-12 11:12:42 +01:00
VkImageViewCreateInfo viewCreateInfo = vks : : initializers : : imageViewCreateInfo ( ) ;
2017-01-29 12:53:57 +01:00
viewCreateInfo . viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY ;
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 . layerCount = layerCount ;
viewCreateInfo . subresourceRange . levelCount = mipLevels ;
viewCreateInfo . image = image ;
VK_CHECK_RESULT ( vkCreateImageView ( device - > logicalDevice , & viewCreateInfo , nullptr , & view ) ) ;
// Clean up staging resources
2019-08-02 20:41:44 +02:00
ktxTexture_Destroy ( ktxTexture ) ;
2017-01-29 12:53:57 +01:00
vkFreeMemory ( device - > logicalDevice , stagingMemory , nullptr ) ;
vkDestroyBuffer ( device - > logicalDevice , stagingBuffer , nullptr ) ;
// Update descriptor image info member that can be used for setting up descriptor sets
updateDescriptor ( ) ;
}
} ;
/** @brief Cube map texture */
class TextureCubeMap : public Texture {
public :
/**
* Load a cubemap texture including all mip levels from a single file
*
2019-08-02 20:41:44 +02:00
* @ param filename File to load ( supports . ktx )
2017-01-29 12:53:57 +01:00
* @ 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 ) 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 )
*
*/
void loadFromFile (
std : : string filename ,
VkFormat format ,
2017-02-12 10:16:07 +01:00
vks : : VulkanDevice * device ,
2017-01-29 12:53:57 +01:00
VkQueue copyQueue ,
VkImageUsageFlags imageUsageFlags = VK_IMAGE_USAGE_SAMPLED_BIT ,
VkImageLayout imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL )
{
2019-08-02 20:41:44 +02:00
ktxTexture * ktxTexture ;
ktxResult result = loadKTXFile ( filename , & ktxTexture ) ;
assert ( result = = KTX_SUCCESS ) ;
2017-01-29 12:53:57 +01:00
this - > device = device ;
2019-08-02 20:41:44 +02:00
width = ktxTexture - > baseWidth ;
height = ktxTexture - > baseHeight ;
mipLevels = ktxTexture - > numLevels ;
ktx_uint8_t * ktxTextureData = ktxTexture_GetData ( ktxTexture ) ;
ktx_size_t ktxTextureSize = ktxTexture_GetSize ( ktxTexture ) ;
2017-01-29 12:53:57 +01:00
2017-02-12 11:12:42 +01:00
VkMemoryAllocateInfo memAllocInfo = vks : : initializers : : memoryAllocateInfo ( ) ;
2017-01-29 12:53:57 +01:00
VkMemoryRequirements memReqs ;
// Create a host-visible staging buffer that contains the raw image data
VkBuffer stagingBuffer ;
VkDeviceMemory stagingMemory ;
2017-02-12 11:12:42 +01:00
VkBufferCreateInfo bufferCreateInfo = vks : : initializers : : bufferCreateInfo ( ) ;
2019-08-02 20:41:44 +02:00
bufferCreateInfo . size = ktxTextureSize ;
2017-01-29 12:53:57 +01:00
// This buffer is used as a transfer source for the buffer copy
bufferCreateInfo . usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT ;
bufferCreateInfo . sharingMode = VK_SHARING_MODE_EXCLUSIVE ;
VK_CHECK_RESULT ( vkCreateBuffer ( device - > logicalDevice , & bufferCreateInfo , nullptr , & stagingBuffer ) ) ;
// Get memory requirements for the staging buffer (alignment, memory type bits)
vkGetBufferMemoryRequirements ( device - > logicalDevice , stagingBuffer , & memReqs ) ;
memAllocInfo . allocationSize = memReqs . size ;
// Get memory type index for a host visible buffer
memAllocInfo . memoryTypeIndex = device - > getMemoryType ( memReqs . memoryTypeBits , VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT ) ;
VK_CHECK_RESULT ( vkAllocateMemory ( device - > logicalDevice , & memAllocInfo , nullptr , & stagingMemory ) ) ;
VK_CHECK_RESULT ( vkBindBufferMemory ( device - > logicalDevice , stagingBuffer , stagingMemory , 0 ) ) ;
// Copy texture data into staging buffer
uint8_t * data ;
VK_CHECK_RESULT ( vkMapMemory ( device - > logicalDevice , stagingMemory , 0 , memReqs . size , 0 , ( void * * ) & data ) ) ;
2019-08-02 20:41:44 +02:00
memcpy ( data , ktxTextureData , ktxTextureSize ) ;
2017-01-29 12:53:57 +01:00
vkUnmapMemory ( device - > logicalDevice , stagingMemory ) ;
2020-01-12 12:54:41 +01:00
// Setup buffer copy regions for each face including all of its miplevels
2017-01-29 12:53:57 +01:00
std : : vector < VkBufferImageCopy > bufferCopyRegions ;
for ( uint32_t face = 0 ; face < 6 ; face + + )
{
for ( uint32_t level = 0 ; level < mipLevels ; level + + )
{
2019-08-02 20:41:44 +02:00
ktx_size_t offset ;
2019-09-13 22:02:46 +02:00
KTX_error_code result = ktxTexture_GetImageOffset ( ktxTexture , level , 0 , face , & offset ) ;
2019-09-13 22:05:57 +02:00
assert ( result = = KTX_SUCCESS ) ;
2019-08-02 20:41:44 +02:00
2017-01-29 12:53:57 +01:00
VkBufferImageCopy bufferCopyRegion = { } ;
bufferCopyRegion . imageSubresource . aspectMask = VK_IMAGE_ASPECT_COLOR_BIT ;
bufferCopyRegion . imageSubresource . mipLevel = level ;
bufferCopyRegion . imageSubresource . baseArrayLayer = face ;
bufferCopyRegion . imageSubresource . layerCount = 1 ;
2019-08-02 20:41:44 +02:00
bufferCopyRegion . imageExtent . width = ktxTexture - > baseWidth > > level ;
bufferCopyRegion . imageExtent . height = ktxTexture - > baseHeight > > level ;
2017-01-29 12:53:57 +01:00
bufferCopyRegion . imageExtent . depth = 1 ;
bufferCopyRegion . bufferOffset = offset ;
bufferCopyRegions . push_back ( bufferCopyRegion ) ;
}
}
// Create optimal tiled target image
2017-02-12 11:12:42 +01:00
VkImageCreateInfo imageCreateInfo = vks : : initializers : : imageCreateInfo ( ) ;
2017-01-29 12:53:57 +01:00
imageCreateInfo . imageType = VK_IMAGE_TYPE_2D ;
imageCreateInfo . format = format ;
imageCreateInfo . mipLevels = mipLevels ;
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 ;
}
// Cube faces count as array layers in Vulkan
imageCreateInfo . arrayLayers = 6 ;
// This flag is required for cube map images
imageCreateInfo . flags = VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT ;
VK_CHECK_RESULT ( vkCreateImage ( device - > logicalDevice , & imageCreateInfo , nullptr , & image ) ) ;
vkGetImageMemoryRequirements ( device - > logicalDevice , image , & memReqs ) ;
memAllocInfo . allocationSize = memReqs . size ;
memAllocInfo . memoryTypeIndex = device - > getMemoryType ( memReqs . memoryTypeBits , VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT ) ;
VK_CHECK_RESULT ( vkAllocateMemory ( device - > logicalDevice , & memAllocInfo , nullptr , & deviceMemory ) ) ;
VK_CHECK_RESULT ( vkBindImageMemory ( device - > logicalDevice , image , deviceMemory , 0 ) ) ;
// Use a separate command buffer for texture loading
VkCommandBuffer copyCmd = device - > createCommandBuffer ( VK_COMMAND_BUFFER_LEVEL_PRIMARY , true ) ;
// Image barrier for optimal image (target)
// Set initial layout for all array layers (faces) of the optimal (target) tiled texture
VkImageSubresourceRange subresourceRange = { } ;
subresourceRange . aspectMask = VK_IMAGE_ASPECT_COLOR_BIT ;
subresourceRange . baseMipLevel = 0 ;
subresourceRange . levelCount = mipLevels ;
subresourceRange . layerCount = 6 ;
2017-02-12 13:10:05 +01:00
vks : : tools : : setImageLayout (
2017-01-29 12:53:57 +01:00
copyCmd ,
image ,
VK_IMAGE_LAYOUT_UNDEFINED ,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL ,
subresourceRange ) ;
// Copy the cube map faces from the staging buffer to the optimal tiled image
vkCmdCopyBufferToImage (
copyCmd ,
stagingBuffer ,
image ,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL ,
static_cast < uint32_t > ( bufferCopyRegions . size ( ) ) ,
bufferCopyRegions . data ( ) ) ;
// Change texture image layout to shader read after all faces have been copied
this - > imageLayout = imageLayout ;
2017-02-12 13:10:05 +01:00
vks : : tools : : setImageLayout (
2017-01-29 12:53:57 +01:00
copyCmd ,
image ,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL ,
imageLayout ,
subresourceRange ) ;
device - > flushCommandBuffer ( copyCmd , copyQueue ) ;
// Create sampler
2017-02-12 11:12:42 +01:00
VkSamplerCreateInfo samplerCreateInfo = vks : : initializers : : samplerCreateInfo ( ) ;
2017-01-29 12:53:57 +01:00
samplerCreateInfo . magFilter = VK_FILTER_LINEAR ;
samplerCreateInfo . minFilter = VK_FILTER_LINEAR ;
samplerCreateInfo . mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR ;
samplerCreateInfo . addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE ;
samplerCreateInfo . addressModeV = samplerCreateInfo . addressModeU ;
samplerCreateInfo . addressModeW = samplerCreateInfo . addressModeU ;
samplerCreateInfo . mipLodBias = 0.0f ;
2017-06-14 23:39:41 +02:00
samplerCreateInfo . maxAnisotropy = device - > enabledFeatures . samplerAnisotropy ? device - > properties . limits . maxSamplerAnisotropy : 1.0f ;
2018-01-05 20:47:27 +01:00
samplerCreateInfo . anisotropyEnable = device - > enabledFeatures . samplerAnisotropy ;
2017-01-29 12:53:57 +01:00
samplerCreateInfo . compareOp = VK_COMPARE_OP_NEVER ;
samplerCreateInfo . minLod = 0.0f ;
samplerCreateInfo . maxLod = ( float ) mipLevels ;
samplerCreateInfo . borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE ;
VK_CHECK_RESULT ( vkCreateSampler ( device - > logicalDevice , & samplerCreateInfo , nullptr , & sampler ) ) ;
// Create image view
2017-02-12 11:12:42 +01:00
VkImageViewCreateInfo viewCreateInfo = vks : : initializers : : imageViewCreateInfo ( ) ;
2017-01-29 12:53:57 +01:00
viewCreateInfo . viewType = VK_IMAGE_VIEW_TYPE_CUBE ;
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 . layerCount = 6 ;
viewCreateInfo . subresourceRange . levelCount = mipLevels ;
viewCreateInfo . image = image ;
VK_CHECK_RESULT ( vkCreateImageView ( device - > logicalDevice , & viewCreateInfo , nullptr , & view ) ) ;
// Clean up staging resources
2019-08-02 20:41:44 +02:00
ktxTexture_Destroy ( ktxTexture ) ;
2017-01-29 12:53:57 +01:00
vkFreeMemory ( device - > logicalDevice , stagingMemory , nullptr ) ;
vkDestroyBuffer ( device - > logicalDevice , stagingBuffer , nullptr ) ;
// Update descriptor image info member that can be used for setting up descriptor sets
updateDescriptor ( ) ;
}
} ;
2018-04-27 10:26:33 +02:00
}