From 361535b29e61a196662bdfd3c0308a4b6f2a0d17 Mon Sep 17 00:00:00 2001 From: Sascha Willems Date: Sat, 8 Aug 2020 22:54:34 +0200 Subject: [PATCH] Split texture class into header and implementation Moved include to base class --- base/VulkanTexture.cpp | 875 ++++++++++++++++ base/VulkanTexture.h | 97 ++ base/VulkanTexture.hpp | 948 ------------------ base/vulkanexamplebase.h | 1 + examples/bloom/bloom.cpp | 1 - examples/computecloth/computecloth.cpp | 1 - examples/computenbody/computenbody.cpp | 1 - .../computeparticles/computeparticles.cpp | 1 - .../computeraytracing/computeraytracing.cpp | 1 - examples/computeshader/computeshader.cpp | 1 - examples/deferred/deferred.cpp | 1 - .../deferredmultisampling.cpp | 1 - examples/deferredshadows/deferredshadows.cpp | 1 - .../descriptorindexing/descriptorindexing.cpp | 1 - examples/descriptorsets/descriptorsets.cpp | 1 - examples/displacement/displacement.cpp | 1 - .../distancefieldfonts/distancefieldfonts.cpp | 1 - examples/gltfloading/gltfloading.cpp | 1 - .../gltfscenerendering/gltfscenerendering.h | 1 - examples/gltfskinning/gltfskinning.h | 1 - examples/hdr/hdr.cpp | 1 - examples/indirectdraw/indirectdraw.cpp | 1 - examples/instancing/instancing.cpp | 1 - examples/multisampling/multisampling.cpp | 1 - .../negativeviewportheight.cpp | 1 - examples/parallaxmapping/parallaxmapping.cpp | 1 - examples/particlefire/particlefire.cpp | 1 - examples/pbribl/pbribl.cpp | 1 - examples/pbrtexture/pbrtexture.cpp | 1 - examples/pushdescriptors/pushdescriptors.cpp | 1 - examples/radialblur/radialblur.cpp | 1 - .../shadowmappingcascade.cpp | 1 - .../shadowmappingomni/shadowmappingomni.cpp | 1 - .../specializationconstants.cpp | 1 - .../sphericalenvmapping.cpp | 1 - examples/ssao/ssao.cpp | 1 - examples/subpasses/subpasses.cpp | 1 - .../terraintessellation.cpp | 1 - examples/tessellation/tessellation.cpp | 1 - examples/texturearray/texturearray.cpp | 1 - examples/texturecubemap/texturecubemap.cpp | 1 - .../texturecubemaparray.cpp | 1 - examples/vulkanscene/vulkanscene.cpp | 1 - 43 files changed, 973 insertions(+), 987 deletions(-) create mode 100644 base/VulkanTexture.cpp create mode 100644 base/VulkanTexture.h delete mode 100644 base/VulkanTexture.hpp diff --git a/base/VulkanTexture.cpp b/base/VulkanTexture.cpp new file mode 100644 index 00000000..a548eb66 --- /dev/null +++ b/base/VulkanTexture.cpp @@ -0,0 +1,875 @@ +/* +* Vulkan texture loader +* +* Copyright(C) by Sascha Willems - www.saschawillems.de +* +* This code is licensed under the MIT license(MIT) (http://opensource.org/licenses/MIT) +*/ + +#pragma once + +#include + +namespace vks +{ + void Texture::updateDescriptor() + { + descriptor.sampler = sampler; + descriptor.imageView = view; + descriptor.imageLayout = imageLayout; + } + + void Texture::destroy() + { + vkDestroyImageView(device->logicalDevice, view, nullptr); + vkDestroyImage(device->logicalDevice, image, nullptr); + if (sampler) + { + vkDestroySampler(device->logicalDevice, sampler, nullptr); + } + vkFreeMemory(device->logicalDevice, deviceMemory, nullptr); + } + + ktxResult Texture::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\nThe file may be part of the additional asset pack.\n\nRun \"download_assets.py\" in the repository root to download the latest version.", -1); + } + size_t size = AAsset_getLength(asset); + assert(size > 0); + ktx_uint8_t *textureData = new ktx_uint8_t[size]; + AAsset_read(asset, textureData, size); + AAsset_close(asset); + result = ktxTexture_CreateFromMemory(textureData, size, KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT, target); + delete[] textureData; +#else + if (!vks::tools::fileExists(filename)) { + vks::tools::exitFatal("Could not load texture from " + filename + "\n\nThe file may be part of the additional asset pack.\n\nRun \"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; + } + + /** + * Load a 2D texture including all mip levels + * + * @param filename File to load (supports .ktx) + * @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 Texture2D::loadFromFile(std::string filename, VkFormat format, vks::VulkanDevice *device, VkQueue copyQueue, VkImageUsageFlags imageUsageFlags, VkImageLayout imageLayout, bool forceLinear) + { + ktxTexture* ktxTexture; + ktxResult result = loadKTXFile(filename, &ktxTexture); + assert(result == KTX_SUCCESS); + + this->device = device; + width = ktxTexture->baseWidth; + height = ktxTexture->baseHeight; + mipLevels = ktxTexture->numLevels; + + ktx_uint8_t *ktxTextureData = ktxTexture_GetData(ktxTexture); + ktx_size_t ktxTextureSize = ktxTexture_GetSize(ktxTexture); + + // Get device properties 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; + + VkMemoryAllocateInfo memAllocInfo = vks::initializers::memoryAllocateInfo(); + 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; + + VkBufferCreateInfo bufferCreateInfo = vks::initializers::bufferCreateInfo(); + bufferCreateInfo.size = ktxTextureSize; + // 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, ktxTextureData, ktxTextureSize); + vkUnmapMemory(device->logicalDevice, stagingMemory); + + // Setup buffer copy regions for each mip level + std::vector bufferCopyRegions; + + for (uint32_t i = 0; i < mipLevels; i++) + { + ktx_size_t offset; + KTX_error_code result = ktxTexture_GetImageOffset(ktxTexture, i, 0, 0, &offset); + assert(result == KTX_SUCCESS); + + VkBufferImageCopy bufferCopyRegion = {}; + bufferCopyRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + bufferCopyRegion.imageSubresource.mipLevel = i; + bufferCopyRegion.imageSubresource.baseArrayLayer = 0; + bufferCopyRegion.imageSubresource.layerCount = 1; + bufferCopyRegion.imageExtent.width = std::max(1u, ktxTexture->baseWidth >> i); + bufferCopyRegion.imageExtent.height = std::max(1u, ktxTexture->baseHeight >> i); + bufferCopyRegion.imageExtent.depth = 1; + bufferCopyRegion.bufferOffset = offset; + + bufferCopyRegions.push_back(bufferCopyRegion); + } + + // Create optimal tiled target image + VkImageCreateInfo imageCreateInfo = vks::initializers::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; + } + 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 + vks::tools::setImageLayout( + 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(bufferCopyRegions.size()), + bufferCopyRegions.data() + ); + + // Change texture image layout to shader read after all mip levels have been copied + this->imageLayout = imageLayout; + vks::tools::setImageLayout( + 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; + + VkImageCreateInfo imageCreateInfo = vks::initializers::imageCreateInfo(); + 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 + memcpy(data, ktxTextureData, memReqs.size); + + vkUnmapMemory(device->logicalDevice, mappableMemory); + + // Linear tiled images don't need to be staged + // and can be directly used as textures + image = mappableImage; + deviceMemory = mappableMemory; + this->imageLayout = imageLayout; + + // Setup image memory barrier + vks::tools::setImageLayout(copyCmd, image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_UNDEFINED, imageLayout); + + device->flushCommandBuffer(copyCmd, copyQueue); + } + + ktxTexture_Destroy(ktxTexture); + + // Create a default sampler + 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; + // Only enable anisotropic filtering if enabled on the device + samplerCreateInfo.maxAnisotropy = device->enabledFeatures.samplerAnisotropy ? device->properties.limits.maxSamplerAnisotropy : 1.0f; + samplerCreateInfo.anisotropyEnable = device->enabledFeatures.samplerAnisotropy; + 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 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) + */ + void Texture2D::fromBuffer(void* buffer, VkDeviceSize bufferSize, VkFormat format, uint32_t texWidth, uint32_t texHeight, vks::VulkanDevice *device, VkQueue copyQueue, VkFilter filter, VkImageUsageFlags imageUsageFlags, VkImageLayout imageLayout) + { + assert(buffer); + + this->device = device; + width = texWidth; + height = texHeight; + mipLevels = 1; + + VkMemoryAllocateInfo memAllocInfo = vks::initializers::memoryAllocateInfo(); + 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; + + VkBufferCreateInfo bufferCreateInfo = vks::initializers::bufferCreateInfo(); + 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 + VkImageCreateInfo imageCreateInfo = vks::initializers::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; + } + 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 + vks::tools::setImageLayout( + 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; + vks::tools::setImageLayout( + 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; + samplerCreateInfo.maxAnisotropy = 1.0f; + 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(); + } + + /** + * Load a 2D texture array including all mip levels + * + * @param filename File to load (supports .ktx) + * @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 Texture2DArray::loadFromFile(std::string filename, VkFormat format, vks::VulkanDevice *device, VkQueue copyQueue, VkImageUsageFlags imageUsageFlags, VkImageLayout imageLayout) + { + ktxTexture* ktxTexture; + ktxResult result = loadKTXFile(filename, &ktxTexture); + assert(result == KTX_SUCCESS); + + this->device = device; + 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); + + VkMemoryAllocateInfo memAllocInfo = vks::initializers::memoryAllocateInfo(); + VkMemoryRequirements memReqs; + + // Create a host-visible staging buffer that contains the raw image data + VkBuffer stagingBuffer; + VkDeviceMemory stagingMemory; + + VkBufferCreateInfo bufferCreateInfo = vks::initializers::bufferCreateInfo(); + bufferCreateInfo.size = ktxTextureSize; + // 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, ktxTextureData, ktxTextureSize); + vkUnmapMemory(device->logicalDevice, stagingMemory); + + // Setup buffer copy regions for each layer including all of its miplevels + std::vector bufferCopyRegions; + + for (uint32_t layer = 0; layer < layerCount; layer++) + { + for (uint32_t level = 0; level < mipLevels; level++) + { + ktx_size_t offset; + KTX_error_code result = ktxTexture_GetImageOffset(ktxTexture, level, layer, 0, &offset); + assert(result == KTX_SUCCESS); + + VkBufferImageCopy bufferCopyRegion = {}; + bufferCopyRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + bufferCopyRegion.imageSubresource.mipLevel = level; + bufferCopyRegion.imageSubresource.baseArrayLayer = layer; + bufferCopyRegion.imageSubresource.layerCount = 1; + bufferCopyRegion.imageExtent.width = ktxTexture->baseWidth >> level; + bufferCopyRegion.imageExtent.height = ktxTexture->baseHeight >> level; + bufferCopyRegion.imageExtent.depth = 1; + bufferCopyRegion.bufferOffset = offset; + + bufferCopyRegions.push_back(bufferCopyRegion); + } + } + + // Create optimal tiled target image + VkImageCreateInfo imageCreateInfo = vks::initializers::imageCreateInfo(); + 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; + + vks::tools::setImageLayout( + 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(bufferCopyRegions.size()), + bufferCopyRegions.data()); + + // Change texture image layout to shader read after all faces have been copied + this->imageLayout = imageLayout; + vks::tools::setImageLayout( + copyCmd, + image, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + imageLayout, + subresourceRange); + + device->flushCommandBuffer(copyCmd, copyQueue); + + // Create sampler + VkSamplerCreateInfo samplerCreateInfo = vks::initializers::samplerCreateInfo(); + 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; + samplerCreateInfo.maxAnisotropy = device->enabledFeatures.samplerAnisotropy ? device->properties.limits.maxSamplerAnisotropy : 1.0f; + samplerCreateInfo.anisotropyEnable = device->enabledFeatures.samplerAnisotropy; + 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 + VkImageViewCreateInfo viewCreateInfo = vks::initializers::imageViewCreateInfo(); + 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 + ktxTexture_Destroy(ktxTexture); + 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(); + } + + /** + * Load a cubemap texture including all mip levels from a single file + * + * @param filename File to load (supports .ktx) + * @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 TextureCubeMap::loadFromFile(std::string filename, VkFormat format, vks::VulkanDevice *device, VkQueue copyQueue, VkImageUsageFlags imageUsageFlags, VkImageLayout imageLayout) + { + ktxTexture* ktxTexture; + ktxResult result = loadKTXFile(filename, &ktxTexture); + assert(result == KTX_SUCCESS); + + this->device = device; + width = ktxTexture->baseWidth; + height = ktxTexture->baseHeight; + mipLevels = ktxTexture->numLevels; + + ktx_uint8_t *ktxTextureData = ktxTexture_GetData(ktxTexture); + ktx_size_t ktxTextureSize = ktxTexture_GetSize(ktxTexture); + + VkMemoryAllocateInfo memAllocInfo = vks::initializers::memoryAllocateInfo(); + VkMemoryRequirements memReqs; + + // Create a host-visible staging buffer that contains the raw image data + VkBuffer stagingBuffer; + VkDeviceMemory stagingMemory; + + VkBufferCreateInfo bufferCreateInfo = vks::initializers::bufferCreateInfo(); + bufferCreateInfo.size = ktxTextureSize; + // 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, ktxTextureData, ktxTextureSize); + vkUnmapMemory(device->logicalDevice, stagingMemory); + + // Setup buffer copy regions for each face including all of its mip levels + std::vector bufferCopyRegions; + + for (uint32_t face = 0; face < 6; face++) + { + for (uint32_t level = 0; level < mipLevels; level++) + { + ktx_size_t offset; + KTX_error_code result = ktxTexture_GetImageOffset(ktxTexture, level, 0, face, &offset); + assert(result == KTX_SUCCESS); + + VkBufferImageCopy bufferCopyRegion = {}; + bufferCopyRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + bufferCopyRegion.imageSubresource.mipLevel = level; + bufferCopyRegion.imageSubresource.baseArrayLayer = face; + bufferCopyRegion.imageSubresource.layerCount = 1; + bufferCopyRegion.imageExtent.width = ktxTexture->baseWidth >> level; + bufferCopyRegion.imageExtent.height = ktxTexture->baseHeight >> level; + bufferCopyRegion.imageExtent.depth = 1; + bufferCopyRegion.bufferOffset = offset; + + bufferCopyRegions.push_back(bufferCopyRegion); + } + } + + // Create optimal tiled target image + VkImageCreateInfo imageCreateInfo = vks::initializers::imageCreateInfo(); + 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; + + vks::tools::setImageLayout( + 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(bufferCopyRegions.size()), + bufferCopyRegions.data()); + + // Change texture image layout to shader read after all faces have been copied + this->imageLayout = imageLayout; + vks::tools::setImageLayout( + copyCmd, + image, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + imageLayout, + subresourceRange); + + device->flushCommandBuffer(copyCmd, copyQueue); + + // Create sampler + VkSamplerCreateInfo samplerCreateInfo = vks::initializers::samplerCreateInfo(); + 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; + samplerCreateInfo.maxAnisotropy = device->enabledFeatures.samplerAnisotropy ? device->properties.limits.maxSamplerAnisotropy : 1.0f; + samplerCreateInfo.anisotropyEnable = device->enabledFeatures.samplerAnisotropy; + 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 + VkImageViewCreateInfo viewCreateInfo = vks::initializers::imageViewCreateInfo(); + 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 + ktxTexture_Destroy(ktxTexture); + 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(); + } + +} diff --git a/base/VulkanTexture.h b/base/VulkanTexture.h new file mode 100644 index 00000000..09cf56f2 --- /dev/null +++ b/base/VulkanTexture.h @@ -0,0 +1,97 @@ +/* +* Vulkan texture loader +* +* Copyright(C) 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 + +#include "vulkan/vulkan.h" + +#include +#include + +#include "VulkanBuffer.h" +#include "VulkanDevice.h" +#include "VulkanTools.h" + +#if defined(__ANDROID__) +# include +#endif + +namespace vks +{ +class Texture +{ + public: + vks::VulkanDevice * device; + VkImage image; + VkImageLayout imageLayout; + VkDeviceMemory deviceMemory; + VkImageView view; + uint32_t width, height; + uint32_t mipLevels; + uint32_t layerCount; + VkDescriptorImageInfo descriptor; + VkSampler sampler; + + void updateDescriptor(); + void destroy(); + ktxResult loadKTXFile(std::string filename, ktxTexture **target); +}; + +class Texture2D : public Texture +{ + public: + void loadFromFile( + std::string filename, + VkFormat format, + vks::VulkanDevice *device, + VkQueue copyQueue, + VkImageUsageFlags imageUsageFlags = VK_IMAGE_USAGE_SAMPLED_BIT, + VkImageLayout imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + bool forceLinear = false); + void fromBuffer( + void * buffer, + VkDeviceSize bufferSize, + VkFormat format, + uint32_t texWidth, + uint32_t texHeight, + vks::VulkanDevice *device, + VkQueue copyQueue, + VkFilter filter = VK_FILTER_LINEAR, + VkImageUsageFlags imageUsageFlags = VK_IMAGE_USAGE_SAMPLED_BIT, + VkImageLayout imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); +}; + +class Texture2DArray : public Texture +{ + public: + void loadFromFile( + std::string filename, + VkFormat format, + vks::VulkanDevice *device, + VkQueue copyQueue, + VkImageUsageFlags imageUsageFlags = VK_IMAGE_USAGE_SAMPLED_BIT, + VkImageLayout imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); +}; + +class TextureCubeMap : public Texture +{ + public: + void loadFromFile( + std::string filename, + VkFormat format, + vks::VulkanDevice *device, + VkQueue copyQueue, + VkImageUsageFlags imageUsageFlags = VK_IMAGE_USAGE_SAMPLED_BIT, + VkImageLayout imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); +}; +} // namespace vks diff --git a/base/VulkanTexture.hpp b/base/VulkanTexture.hpp deleted file mode 100644 index 54e68e37..00000000 --- a/base/VulkanTexture.hpp +++ /dev/null @@ -1,948 +0,0 @@ -/* -* Vulkan texture loader -* -* Copyright(C) 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 - -#include "vulkan/vulkan.h" - -#include -#include - -#include "VulkanTools.h" -#include "VulkanDevice.h" -#include "VulkanBuffer.h" - -#if defined(__ANDROID__) -#include -#endif - -namespace vks -{ - /** @brief Vulkan texture base class */ - class Texture { - public: - vks::VulkanDevice *device; - 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); - } - - 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\nThe file may be part of the additional asset pack.\n\nRun \"download_assets.py\" in the repository root to download the latest version.", -1); - } - size_t size = AAsset_getLength(asset); - assert(size > 0); - ktx_uint8_t *textureData = new ktx_uint8_t[size]; - AAsset_read(asset, textureData, size); - AAsset_close(asset); - result = ktxTexture_CreateFromMemory(textureData, size, KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT, target); - delete[] textureData; -#else - if (!vks::tools::fileExists(filename)) { - vks::tools::exitFatal("Could not load texture from " + filename + "\n\nThe file may be part of the additional asset pack.\n\nRun \"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; - } - }; - - /** @brief 2D texture */ - class Texture2D : public Texture { - public: - /** - * Load a 2D texture including all mip levels - * - * @param filename File to load (supports .ktx) - * @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, - vks::VulkanDevice *device, - VkQueue copyQueue, - VkImageUsageFlags imageUsageFlags = VK_IMAGE_USAGE_SAMPLED_BIT, - VkImageLayout imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, - bool forceLinear = false) - { - ktxTexture* ktxTexture; - ktxResult result = loadKTXFile(filename, &ktxTexture); - assert(result == KTX_SUCCESS); - - this->device = device; - width = ktxTexture->baseWidth; - height = ktxTexture->baseHeight; - mipLevels = ktxTexture->numLevels; - - ktx_uint8_t *ktxTextureData = ktxTexture_GetData(ktxTexture); - ktx_size_t ktxTextureSize = ktxTexture_GetSize(ktxTexture); - - // Get device properties 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; - - VkMemoryAllocateInfo memAllocInfo = vks::initializers::memoryAllocateInfo(); - 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; - - VkBufferCreateInfo bufferCreateInfo = vks::initializers::bufferCreateInfo(); - bufferCreateInfo.size = ktxTextureSize; - // 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, ktxTextureData, ktxTextureSize); - vkUnmapMemory(device->logicalDevice, stagingMemory); - - // Setup buffer copy regions for each mip level - std::vector bufferCopyRegions; - - for (uint32_t i = 0; i < mipLevels; i++) - { - ktx_size_t offset; - KTX_error_code result = ktxTexture_GetImageOffset(ktxTexture, i, 0, 0, &offset); - assert(result == KTX_SUCCESS); - - VkBufferImageCopy bufferCopyRegion = {}; - bufferCopyRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - bufferCopyRegion.imageSubresource.mipLevel = i; - bufferCopyRegion.imageSubresource.baseArrayLayer = 0; - bufferCopyRegion.imageSubresource.layerCount = 1; - bufferCopyRegion.imageExtent.width = std::max(1u, ktxTexture->baseWidth >> i); - bufferCopyRegion.imageExtent.height = std::max(1u, ktxTexture->baseHeight >> i); - bufferCopyRegion.imageExtent.depth = 1; - bufferCopyRegion.bufferOffset = offset; - - bufferCopyRegions.push_back(bufferCopyRegion); - } - - // Create optimal tiled target image - VkImageCreateInfo imageCreateInfo = vks::initializers::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; - } - 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 - vks::tools::setImageLayout( - 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(bufferCopyRegions.size()), - bufferCopyRegions.data() - ); - - // Change texture image layout to shader read after all mip levels have been copied - this->imageLayout = imageLayout; - vks::tools::setImageLayout( - 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; - - VkImageCreateInfo imageCreateInfo = vks::initializers::imageCreateInfo(); - 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 - memcpy(data, ktxTextureData, memReqs.size); - - vkUnmapMemory(device->logicalDevice, mappableMemory); - - // Linear tiled images don't need to be staged - // and can be directly used as textures - image = mappableImage; - deviceMemory = mappableMemory; - this->imageLayout = imageLayout; - - // Setup image memory barrier - vks::tools::setImageLayout(copyCmd, image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_UNDEFINED, imageLayout); - - device->flushCommandBuffer(copyCmd, copyQueue); - } - - ktxTexture_Destroy(ktxTexture); - - // Create a default sampler - 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; - // Only enable anisotropic filtering if enabled on the device - samplerCreateInfo.maxAnisotropy = device->enabledFeatures.samplerAnisotropy ? device->properties.limits.maxSamplerAnisotropy : 1.0f; - samplerCreateInfo.anisotropyEnable = device->enabledFeatures.samplerAnisotropy; - 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 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) - */ - void fromBuffer( - void* buffer, - VkDeviceSize bufferSize, - VkFormat format, - uint32_t texWidth, - uint32_t texHeight, - vks::VulkanDevice *device, - 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; - width = texWidth; - height = texHeight; - mipLevels = 1; - - VkMemoryAllocateInfo memAllocInfo = vks::initializers::memoryAllocateInfo(); - 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; - - VkBufferCreateInfo bufferCreateInfo = vks::initializers::bufferCreateInfo(); - 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 - VkImageCreateInfo imageCreateInfo = vks::initializers::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; - } - 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 - vks::tools::setImageLayout( - 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; - vks::tools::setImageLayout( - 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; - samplerCreateInfo.maxAnisotropy = 1.0f; - 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 2D texture array including all mip levels - * - * @param filename File to load (supports .ktx) - * @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, - vks::VulkanDevice *device, - VkQueue copyQueue, - VkImageUsageFlags imageUsageFlags = VK_IMAGE_USAGE_SAMPLED_BIT, - VkImageLayout imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) - { - ktxTexture* ktxTexture; - ktxResult result = loadKTXFile(filename, &ktxTexture); - assert(result == KTX_SUCCESS); - - this->device = device; - 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); - - VkMemoryAllocateInfo memAllocInfo = vks::initializers::memoryAllocateInfo(); - VkMemoryRequirements memReqs; - - // Create a host-visible staging buffer that contains the raw image data - VkBuffer stagingBuffer; - VkDeviceMemory stagingMemory; - - VkBufferCreateInfo bufferCreateInfo = vks::initializers::bufferCreateInfo(); - bufferCreateInfo.size = ktxTextureSize; - // 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, ktxTextureData, ktxTextureSize); - vkUnmapMemory(device->logicalDevice, stagingMemory); - - // Setup buffer copy regions for each layer including all of its miplevels - std::vector bufferCopyRegions; - - for (uint32_t layer = 0; layer < layerCount; layer++) - { - for (uint32_t level = 0; level < mipLevels; level++) - { - ktx_size_t offset; - KTX_error_code result = ktxTexture_GetImageOffset(ktxTexture, level, layer, 0, &offset); - assert(result == KTX_SUCCESS); - - VkBufferImageCopy bufferCopyRegion = {}; - bufferCopyRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - bufferCopyRegion.imageSubresource.mipLevel = level; - bufferCopyRegion.imageSubresource.baseArrayLayer = layer; - bufferCopyRegion.imageSubresource.layerCount = 1; - bufferCopyRegion.imageExtent.width = ktxTexture->baseWidth >> level; - bufferCopyRegion.imageExtent.height = ktxTexture->baseHeight >> level; - bufferCopyRegion.imageExtent.depth = 1; - bufferCopyRegion.bufferOffset = offset; - - bufferCopyRegions.push_back(bufferCopyRegion); - } - } - - // Create optimal tiled target image - VkImageCreateInfo imageCreateInfo = vks::initializers::imageCreateInfo(); - 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; - - vks::tools::setImageLayout( - 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(bufferCopyRegions.size()), - bufferCopyRegions.data()); - - // Change texture image layout to shader read after all faces have been copied - this->imageLayout = imageLayout; - vks::tools::setImageLayout( - copyCmd, - image, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - imageLayout, - subresourceRange); - - device->flushCommandBuffer(copyCmd, copyQueue); - - // Create sampler - VkSamplerCreateInfo samplerCreateInfo = vks::initializers::samplerCreateInfo(); - 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; - samplerCreateInfo.maxAnisotropy = device->enabledFeatures.samplerAnisotropy ? device->properties.limits.maxSamplerAnisotropy : 1.0f; - samplerCreateInfo.anisotropyEnable = device->enabledFeatures.samplerAnisotropy; - 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 - VkImageViewCreateInfo viewCreateInfo = vks::initializers::imageViewCreateInfo(); - 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 - ktxTexture_Destroy(ktxTexture); - 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 - * - * @param filename File to load (supports .ktx) - * @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, - vks::VulkanDevice *device, - VkQueue copyQueue, - VkImageUsageFlags imageUsageFlags = VK_IMAGE_USAGE_SAMPLED_BIT, - VkImageLayout imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) - { - ktxTexture* ktxTexture; - ktxResult result = loadKTXFile(filename, &ktxTexture); - assert(result == KTX_SUCCESS); - - this->device = device; - width = ktxTexture->baseWidth; - height = ktxTexture->baseHeight; - mipLevels = ktxTexture->numLevels; - - ktx_uint8_t *ktxTextureData = ktxTexture_GetData(ktxTexture); - ktx_size_t ktxTextureSize = ktxTexture_GetSize(ktxTexture); - - VkMemoryAllocateInfo memAllocInfo = vks::initializers::memoryAllocateInfo(); - VkMemoryRequirements memReqs; - - // Create a host-visible staging buffer that contains the raw image data - VkBuffer stagingBuffer; - VkDeviceMemory stagingMemory; - - VkBufferCreateInfo bufferCreateInfo = vks::initializers::bufferCreateInfo(); - bufferCreateInfo.size = ktxTextureSize; - // 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, ktxTextureData, ktxTextureSize); - vkUnmapMemory(device->logicalDevice, stagingMemory); - - // Setup buffer copy regions for each face including all of its miplevels - std::vector bufferCopyRegions; - - for (uint32_t face = 0; face < 6; face++) - { - for (uint32_t level = 0; level < mipLevels; level++) - { - ktx_size_t offset; - KTX_error_code result = ktxTexture_GetImageOffset(ktxTexture, level, 0, face, &offset); - assert(result == KTX_SUCCESS); - - VkBufferImageCopy bufferCopyRegion = {}; - bufferCopyRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - bufferCopyRegion.imageSubresource.mipLevel = level; - bufferCopyRegion.imageSubresource.baseArrayLayer = face; - bufferCopyRegion.imageSubresource.layerCount = 1; - bufferCopyRegion.imageExtent.width = ktxTexture->baseWidth >> level; - bufferCopyRegion.imageExtent.height = ktxTexture->baseHeight >> level; - bufferCopyRegion.imageExtent.depth = 1; - bufferCopyRegion.bufferOffset = offset; - - bufferCopyRegions.push_back(bufferCopyRegion); - } - } - - // Create optimal tiled target image - VkImageCreateInfo imageCreateInfo = vks::initializers::imageCreateInfo(); - 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; - - vks::tools::setImageLayout( - 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(bufferCopyRegions.size()), - bufferCopyRegions.data()); - - // Change texture image layout to shader read after all faces have been copied - this->imageLayout = imageLayout; - vks::tools::setImageLayout( - copyCmd, - image, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - imageLayout, - subresourceRange); - - device->flushCommandBuffer(copyCmd, copyQueue); - - // Create sampler - VkSamplerCreateInfo samplerCreateInfo = vks::initializers::samplerCreateInfo(); - 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; - samplerCreateInfo.maxAnisotropy = device->enabledFeatures.samplerAnisotropy ? device->properties.limits.maxSamplerAnisotropy : 1.0f; - samplerCreateInfo.anisotropyEnable = device->enabledFeatures.samplerAnisotropy; - 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 - VkImageViewCreateInfo viewCreateInfo = vks::initializers::imageViewCreateInfo(); - 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 - ktxTexture_Destroy(ktxTexture); - 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(); - } - }; - -} diff --git a/base/vulkanexamplebase.h b/base/vulkanexamplebase.h index a391b9d6..938e2850 100644 --- a/base/vulkanexamplebase.h +++ b/base/vulkanexamplebase.h @@ -50,6 +50,7 @@ #include "VulkanSwapChain.h" #include "VulkanBuffer.h" #include "VulkanDevice.h" +#include "VulkanTexture.h" #include "VulkanInitializers.hpp" #include "camera.hpp" diff --git a/examples/bloom/bloom.cpp b/examples/bloom/bloom.cpp index ba949b7b..e790f60a 100644 --- a/examples/bloom/bloom.cpp +++ b/examples/bloom/bloom.cpp @@ -19,7 +19,6 @@ #include #include "vulkanexamplebase.h" -#include "VulkanTexture.hpp" #include "VulkanglTFModel.h" #define ENABLE_VALIDATION false diff --git a/examples/computecloth/computecloth.cpp b/examples/computecloth/computecloth.cpp index 47d1e782..1e346995 100644 --- a/examples/computecloth/computecloth.cpp +++ b/examples/computecloth/computecloth.cpp @@ -20,7 +20,6 @@ #include #include "vulkanexamplebase.h" -#include "VulkanTexture.hpp" #include "VulkanglTFModel.h" #define ENABLE_VALIDATION false diff --git a/examples/computenbody/computenbody.cpp b/examples/computenbody/computenbody.cpp index eb31abcc..f0118312 100644 --- a/examples/computenbody/computenbody.cpp +++ b/examples/computenbody/computenbody.cpp @@ -20,7 +20,6 @@ #include #include "vulkanexamplebase.h" -#include "VulkanTexture.hpp" #define VERTEX_BUFFER_BIND_ID 0 #define ENABLE_VALIDATION false diff --git a/examples/computeparticles/computeparticles.cpp b/examples/computeparticles/computeparticles.cpp index ac885daa..3cbcb808 100644 --- a/examples/computeparticles/computeparticles.cpp +++ b/examples/computeparticles/computeparticles.cpp @@ -22,7 +22,6 @@ #include #include "vulkanexamplebase.h" -#include "VulkanTexture.hpp" #define VERTEX_BUFFER_BIND_ID 0 #define ENABLE_VALIDATION false diff --git a/examples/computeraytracing/computeraytracing.cpp b/examples/computeraytracing/computeraytracing.cpp index 57a00d17..8ccbe828 100644 --- a/examples/computeraytracing/computeraytracing.cpp +++ b/examples/computeraytracing/computeraytracing.cpp @@ -19,7 +19,6 @@ #include #include "vulkanexamplebase.h" -#include "VulkanTexture.hpp" #define VERTEX_BUFFER_BIND_ID 0 #define ENABLE_VALIDATION false diff --git a/examples/computeshader/computeshader.cpp b/examples/computeshader/computeshader.cpp index b9e5416f..a55876ef 100644 --- a/examples/computeshader/computeshader.cpp +++ b/examples/computeshader/computeshader.cpp @@ -19,7 +19,6 @@ #include #include "vulkanexamplebase.h" -#include "VulkanTexture.hpp" #define VERTEX_BUFFER_BIND_ID 0 #define ENABLE_VALIDATION false diff --git a/examples/deferred/deferred.cpp b/examples/deferred/deferred.cpp index 3e9bc1ef..6fb361ff 100644 --- a/examples/deferred/deferred.cpp +++ b/examples/deferred/deferred.cpp @@ -19,7 +19,6 @@ #include #include "vulkanexamplebase.h" -#include "VulkanTexture.hpp" #include "VulkanglTFModel.h" #define ENABLE_VALIDATION false diff --git a/examples/deferredmultisampling/deferredmultisampling.cpp b/examples/deferredmultisampling/deferredmultisampling.cpp index ccd7309e..eb36d13a 100644 --- a/examples/deferredmultisampling/deferredmultisampling.cpp +++ b/examples/deferredmultisampling/deferredmultisampling.cpp @@ -20,7 +20,6 @@ #include #include "vulkanexamplebase.h" #include "VulkanFrameBuffer.hpp" -#include "VulkanTexture.hpp" #include "VulkanglTFModel.h" #define ENABLE_VALIDATION false diff --git a/examples/deferredshadows/deferredshadows.cpp b/examples/deferredshadows/deferredshadows.cpp index 12876ac1..bd22cd39 100644 --- a/examples/deferredshadows/deferredshadows.cpp +++ b/examples/deferredshadows/deferredshadows.cpp @@ -21,7 +21,6 @@ #include #include "vulkanexamplebase.h" #include "VulkanFrameBuffer.hpp" -#include "VulkanTexture.hpp" #include "VulkanglTFModel.h" #define VERTEX_BUFFER_BIND_ID 0 diff --git a/examples/descriptorindexing/descriptorindexing.cpp b/examples/descriptorindexing/descriptorindexing.cpp index 152075c3..409ab2e9 100644 --- a/examples/descriptorindexing/descriptorindexing.cpp +++ b/examples/descriptorindexing/descriptorindexing.cpp @@ -23,7 +23,6 @@ #include #include "vulkanexamplebase.h" -#include "VulkanTexture.hpp" #define ENABLE_VALIDATION false diff --git a/examples/descriptorsets/descriptorsets.cpp b/examples/descriptorsets/descriptorsets.cpp index f86bc386..7e6002c7 100644 --- a/examples/descriptorsets/descriptorsets.cpp +++ b/examples/descriptorsets/descriptorsets.cpp @@ -22,7 +22,6 @@ #include #include "vulkanexamplebase.h" -#include "VulkanTexture.hpp" #include "VulkanglTFModel.h" #define ENABLE_VALIDATION false diff --git a/examples/displacement/displacement.cpp b/examples/displacement/displacement.cpp index 3f811965..840aeaa1 100644 --- a/examples/displacement/displacement.cpp +++ b/examples/displacement/displacement.cpp @@ -19,7 +19,6 @@ #include #include "vulkanexamplebase.h" -#include "VulkanTexture.hpp" #include "VulkanglTFModel.h" #define ENABLE_VALIDATION false diff --git a/examples/distancefieldfonts/distancefieldfonts.cpp b/examples/distancefieldfonts/distancefieldfonts.cpp index 337196b5..4ae44b3d 100644 --- a/examples/distancefieldfonts/distancefieldfonts.cpp +++ b/examples/distancefieldfonts/distancefieldfonts.cpp @@ -23,7 +23,6 @@ #include #include "vulkanexamplebase.h" -#include "VulkanTexture.hpp" #define VERTEX_BUFFER_BIND_ID 0 #define ENABLE_VALIDATION false diff --git a/examples/gltfloading/gltfloading.cpp b/examples/gltfloading/gltfloading.cpp index ae2e76e9..82fbc58a 100644 --- a/examples/gltfloading/gltfloading.cpp +++ b/examples/gltfloading/gltfloading.cpp @@ -39,7 +39,6 @@ #include #include "vulkanexamplebase.h" -#include "VulkanTexture.hpp" #define ENABLE_VALIDATION false diff --git a/examples/gltfscenerendering/gltfscenerendering.h b/examples/gltfscenerendering/gltfscenerendering.h index e5c81a26..bfee3629 100644 --- a/examples/gltfscenerendering/gltfscenerendering.h +++ b/examples/gltfscenerendering/gltfscenerendering.h @@ -36,7 +36,6 @@ #include #include "vulkanexamplebase.h" -#include "VulkanTexture.hpp" #define ENABLE_VALIDATION false diff --git a/examples/gltfskinning/gltfskinning.h b/examples/gltfskinning/gltfskinning.h index 9266c4c0..9423c01c 100644 --- a/examples/gltfskinning/gltfskinning.h +++ b/examples/gltfskinning/gltfskinning.h @@ -35,7 +35,6 @@ #endif #include "tiny_gltf.h" -#include "VulkanTexture.hpp" #include "vulkanexamplebase.h" #include diff --git a/examples/hdr/hdr.cpp b/examples/hdr/hdr.cpp index 8052cf6f..51b8fcba 100644 --- a/examples/hdr/hdr.cpp +++ b/examples/hdr/hdr.cpp @@ -22,7 +22,6 @@ #include #include "vulkanexamplebase.h" #include "VulkanglTFModel.h" -#include "VulkanTexture.hpp" #define ENABLE_VALIDATION false diff --git a/examples/indirectdraw/indirectdraw.cpp b/examples/indirectdraw/indirectdraw.cpp index f35d3ff6..92426cc1 100644 --- a/examples/indirectdraw/indirectdraw.cpp +++ b/examples/indirectdraw/indirectdraw.cpp @@ -34,7 +34,6 @@ #include #include "vulkanexamplebase.h" -#include "VulkanTexture.hpp" #include "VulkanglTFModel.h" #define VERTEX_BUFFER_BIND_ID 0 diff --git a/examples/instancing/instancing.cpp b/examples/instancing/instancing.cpp index a86d7d49..5241a959 100644 --- a/examples/instancing/instancing.cpp +++ b/examples/instancing/instancing.cpp @@ -21,7 +21,6 @@ #include #include "vulkanexamplebase.h" -#include "VulkanTexture.hpp" #include "VulkanglTFModel.h" #define VERTEX_BUFFER_BIND_ID 0 diff --git a/examples/multisampling/multisampling.cpp b/examples/multisampling/multisampling.cpp index 232e675b..f3fb9a34 100644 --- a/examples/multisampling/multisampling.cpp +++ b/examples/multisampling/multisampling.cpp @@ -19,7 +19,6 @@ #include #include "vulkanexamplebase.h" -#include "VulkanTexture.hpp" #include "VulkanglTFModel.h" #define ENABLE_VALIDATION false diff --git a/examples/negativeviewportheight/negativeviewportheight.cpp b/examples/negativeviewportheight/negativeviewportheight.cpp index 4d277818..6ac2ab8c 100644 --- a/examples/negativeviewportheight/negativeviewportheight.cpp +++ b/examples/negativeviewportheight/negativeviewportheight.cpp @@ -22,7 +22,6 @@ #include #include "vulkanexamplebase.h" -#include "VulkanTexture.hpp" #define ENABLE_VALIDATION false diff --git a/examples/parallaxmapping/parallaxmapping.cpp b/examples/parallaxmapping/parallaxmapping.cpp index bbd2d5f1..fabfc5f6 100644 --- a/examples/parallaxmapping/parallaxmapping.cpp +++ b/examples/parallaxmapping/parallaxmapping.cpp @@ -20,7 +20,6 @@ #include #include "vulkanexamplebase.h" -#include "VulkanTexture.hpp" #include "VulkanglTFModel.h" #define ENABLE_VALIDATION false diff --git a/examples/particlefire/particlefire.cpp b/examples/particlefire/particlefire.cpp index 17374aed..d809fe5a 100644 --- a/examples/particlefire/particlefire.cpp +++ b/examples/particlefire/particlefire.cpp @@ -21,7 +21,6 @@ #include #include "vulkanexamplebase.h" -#include "VulkanTexture.hpp" #include "VulkanglTFModel.h" #define ENABLE_VALIDATION false diff --git a/examples/pbribl/pbribl.cpp b/examples/pbribl/pbribl.cpp index cf0b48a1..2c56aa7c 100644 --- a/examples/pbribl/pbribl.cpp +++ b/examples/pbribl/pbribl.cpp @@ -24,7 +24,6 @@ #include #include "vulkanexamplebase.h" -#include "VulkanTexture.hpp" #include "VulkanglTFModel.h" #define ENABLE_VALIDATION false diff --git a/examples/pbrtexture/pbrtexture.cpp b/examples/pbrtexture/pbrtexture.cpp index 16553780..175932de 100644 --- a/examples/pbrtexture/pbrtexture.cpp +++ b/examples/pbrtexture/pbrtexture.cpp @@ -24,7 +24,6 @@ #include #include "vulkanexamplebase.h" -#include "VulkanTexture.hpp" #include "VulkanglTFModel.h" #define ENABLE_VALIDATION false diff --git a/examples/pushdescriptors/pushdescriptors.cpp b/examples/pushdescriptors/pushdescriptors.cpp index 4c9bac5b..2176fe5d 100644 --- a/examples/pushdescriptors/pushdescriptors.cpp +++ b/examples/pushdescriptors/pushdescriptors.cpp @@ -27,7 +27,6 @@ #include #include "vulkanexamplebase.h" -#include "VulkanTexture.hpp" #include "VulkanglTFModel.h" #define ENABLE_VALIDATION false diff --git a/examples/radialblur/radialblur.cpp b/examples/radialblur/radialblur.cpp index e67b65bb..f683d9fc 100644 --- a/examples/radialblur/radialblur.cpp +++ b/examples/radialblur/radialblur.cpp @@ -19,7 +19,6 @@ #include #include "vulkanexamplebase.h" -#include "VulkanTexture.hpp" #include "VulkanglTFModel.h" #define ENABLE_VALIDATION false diff --git a/examples/shadowmappingcascade/shadowmappingcascade.cpp b/examples/shadowmappingcascade/shadowmappingcascade.cpp index 2fd337e6..3bde2743 100644 --- a/examples/shadowmappingcascade/shadowmappingcascade.cpp +++ b/examples/shadowmappingcascade/shadowmappingcascade.cpp @@ -30,7 +30,6 @@ #include #include "vulkanexamplebase.h" -#include "VulkanTexture.hpp" #include "VulkanglTFModel.h" #define ENABLE_VALIDATION false diff --git a/examples/shadowmappingomni/shadowmappingomni.cpp b/examples/shadowmappingomni/shadowmappingomni.cpp index 77ce9851..e0132ccc 100644 --- a/examples/shadowmappingomni/shadowmappingomni.cpp +++ b/examples/shadowmappingomni/shadowmappingomni.cpp @@ -19,7 +19,6 @@ #include #include "vulkanexamplebase.h" -#include "VulkanTexture.hpp" #include "VulkanglTFModel.h" #define ENABLE_VALIDATION false diff --git a/examples/specializationconstants/specializationconstants.cpp b/examples/specializationconstants/specializationconstants.cpp index ebf95b18..5c4ca48f 100644 --- a/examples/specializationconstants/specializationconstants.cpp +++ b/examples/specializationconstants/specializationconstants.cpp @@ -21,7 +21,6 @@ #include #include "vulkanexamplebase.h" -#include "VulkanTexture.hpp" #include "VulkanglTFModel.h" #define ENABLE_VALIDATION false diff --git a/examples/sphericalenvmapping/sphericalenvmapping.cpp b/examples/sphericalenvmapping/sphericalenvmapping.cpp index 9a3d6dc9..a20705ee 100644 --- a/examples/sphericalenvmapping/sphericalenvmapping.cpp +++ b/examples/sphericalenvmapping/sphericalenvmapping.cpp @@ -24,7 +24,6 @@ #include #include "vulkanexamplebase.h" -#include "VulkanTexture.hpp" #include "VulkanglTFModel.h" #define VERTEX_BUFFER_BIND_ID 0 diff --git a/examples/ssao/ssao.cpp b/examples/ssao/ssao.cpp index 738f48fb..988b18c5 100644 --- a/examples/ssao/ssao.cpp +++ b/examples/ssao/ssao.cpp @@ -20,7 +20,6 @@ #include #include "vulkanexamplebase.h" -#include "VulkanTexture.hpp" #include "VulkanglTFModel.h" #define ENABLE_VALIDATION false diff --git a/examples/subpasses/subpasses.cpp b/examples/subpasses/subpasses.cpp index 6f499365..8fbe6bfc 100644 --- a/examples/subpasses/subpasses.cpp +++ b/examples/subpasses/subpasses.cpp @@ -30,7 +30,6 @@ #include #include "vulkanexamplebase.h" -#include "VulkanTexture.hpp" #include "VulkanglTFModel.h" #define ENABLE_VALIDATION false diff --git a/examples/terraintessellation/terraintessellation.cpp b/examples/terraintessellation/terraintessellation.cpp index 52add4cd..a99df1ae 100644 --- a/examples/terraintessellation/terraintessellation.cpp +++ b/examples/terraintessellation/terraintessellation.cpp @@ -20,7 +20,6 @@ #include #include "vulkanexamplebase.h" -#include "VulkanTexture.hpp" #include "VulkanglTFModel.h" #include "frustum.hpp" #include diff --git a/examples/tessellation/tessellation.cpp b/examples/tessellation/tessellation.cpp index e975e56e..62193bcc 100644 --- a/examples/tessellation/tessellation.cpp +++ b/examples/tessellation/tessellation.cpp @@ -22,7 +22,6 @@ #include #include "vulkanexamplebase.h" -#include "VulkanTexture.hpp" #include "VulkanglTFModel.h" #define ENABLE_VALIDATION false diff --git a/examples/texturearray/texturearray.cpp b/examples/texturearray/texturearray.cpp index d8baf605..57ba4f1f 100644 --- a/examples/texturearray/texturearray.cpp +++ b/examples/texturearray/texturearray.cpp @@ -20,7 +20,6 @@ #include #include "vulkanexamplebase.h" -#include "VulkanTexture.hpp" #include #include diff --git a/examples/texturecubemap/texturecubemap.cpp b/examples/texturecubemap/texturecubemap.cpp index cb082bc8..d7609dbe 100644 --- a/examples/texturecubemap/texturecubemap.cpp +++ b/examples/texturecubemap/texturecubemap.cpp @@ -19,7 +19,6 @@ #include #include "vulkanexamplebase.h" -#include "VulkanTexture.hpp" #include "VulkanglTFModel.h" #include #include diff --git a/examples/texturecubemaparray/texturecubemaparray.cpp b/examples/texturecubemaparray/texturecubemaparray.cpp index b269bc96..e2044237 100644 --- a/examples/texturecubemaparray/texturecubemaparray.cpp +++ b/examples/texturecubemaparray/texturecubemaparray.cpp @@ -19,7 +19,6 @@ #include #include "vulkanexamplebase.h" -#include "VulkanTexture.hpp" #include "VulkanglTFModel.h" #include #include diff --git a/examples/vulkanscene/vulkanscene.cpp b/examples/vulkanscene/vulkanscene.cpp index 5899f53a..8c4185c9 100644 --- a/examples/vulkanscene/vulkanscene.cpp +++ b/examples/vulkanscene/vulkanscene.cpp @@ -24,7 +24,6 @@ #include #include "vulkanexamplebase.h" -#include "VulkanTexture.hpp" #include "VulkanglTFModel.h" #define ENABLE_VALIDATION false