I am trying to load compressed images using S3TC (BC / DXT) compression in Vulkan, but so far I have not been very lucky.
Here's what the Vulkan spec says about compressed images:
https://www.khronos.org/registry/dataformat/specs/1.1/dataformat.1.1.html#S3TC :
Compressed texture images stored using S3TC compressed image formats are presented as a set of 4x4 texel blocks, where each block contains 64 or 128 bits of texel data. The image is encoded as a normal 2D bitmap in which each 4x4 block is treated as one pixel.
https://www.khronos.org/registry/vulkan/specs/1.0/xhtml/vkspec.html#resources-images :
For images created with linear tiles, rowPitch, arrayPitch, and depthPitch describe the layout of the subresource in linear memory. For uncompressed formats, rowPitch is the number of bytes between texels with the same x coordinate in adjacent lines (y coordinates differ by one). arrayPitch is the number of bytes between texels with the same x and y coordinates in neighboring arrays of the image array (array level values โโdiffer by one). depthPitch is the number of bytes between texels with the same x and y coordinates in adjacent slices of a three-dimensional image (z coordinates differ by one). Expressed as an addressing formula, the texel start byte in the subresource has the address:
// (x, y, z, layer) are in texel coordinates
address (x, y, z, layer) = layerarrayPitch + zdepthPitch + yrowPitch + xtexelSize + offset
For compressed formats, rowPitch represents the number of bytes between compressed blocks in adjacent lines. arrayPitch is the number of bytes between blocks in adjacent arrays. depthPitch is the number of bytes between blocks in adjacent slices of a three-dimensional image.
// (x, y, z, layer) are in square coordinates
address (x, y, z, layer) = layerarrayPitch + zdepthPitch + yrowPitch + xblockSize + offset;
arrayPitch undefined for images that were not created as arrays. depthPitch is defined only for 3D images.
For color formats, the aspectMask member of VkImageSubresource must be VK_IMAGE_ASPECT_COLOR_BIT. For depth / stencil formats, the aspect should be either VK_IMAGE_ASPECT_DEPTH_BIT or VK_IMAGE_ASPECT_STENCIL_BIT. In implementations that store depth and stencil aspects separately, querying each of these subresource layouts returns a different offset and size representing the area of โโmemory used for that aspect. In implementations that store aspects of depth and stencils, they alternate, return the same offsets and sizes, and represent the distribution of alternating memory.
My image is a normal 2D image (0 layers, 1 mipmap), so there is no arrayPitch or depthPitch. Since S3TC compression is directly supported by the hardware, it should be possible to use image data without unpacking it first. In OpenGL, this can be done using glCompressedTexImage2D, and this has worked for me in the past.
In OpenGL, I used GL_COMPRESSED_RGBA_S3TC_DXT1_EXT as the image format, for Vulkan I use VK_FORMAT_BC1_RGBA_UNORM_BLOCK, which should be equivalent. Here is my code to display image data:
auto dds = load_dds("img.dds"); auto *srcData = static_cast<uint8_t*>(dds.data()); auto *destData = static_cast<uint8_t*>(vkImageMapPtr); // Pointer to mapped memory of VkImage destData += layout.offset(); // layout = VkImageLayout of the image assert((w %4) == 0); assert((h %4) == 0); assert(blockSize == 8); // S3TC BC1 auto wBlocks = w /4; auto hBlocks = h /4; for(auto y=decltype(hBlocks){0};y<hBlocks;++y) { auto *rowDest = destData +y *layout.rowPitch(); // rowPitch is 0 auto *rowSrc = srcData +y *(wBlocks *blockSize); for(auto x=decltype(wBlocks){0};x<wBlocks;++x) { auto *pxDest = rowDest +x *blockSize; auto *pxSrc = rowSrc +x *blockSize; // 4x4 image block memcpy(pxDest,pxSrc,blockSize); // 64Bit per block } }
And here is the code to initialize the image:
vk::Device device = ...; // Initialization vk::AllocationCallbacks allocatorCallbacks = ...; // Initialization [...] // Load the dds data uint32_t width = dds.width(); uint32_t height = dds.height(); auto format = dds.format(); // = vk::Format::eBc1RgbaUnormBlock; vk::Extent3D extent(width,height,1); vk::ImageCreateInfo imageInfo( vk::ImageCreateFlagBits(0), vk::ImageType::e2D,format, extent,1,1, vk::SampleCountFlagBits::e1, vk::ImageTiling::eLinear, vk::ImageUsageFlagBits::eSampled | vk::ImageUsageFlagBits::eColorAttachment, vk::SharingMode::eExclusive, 0,nullptr, vk::ImageLayout::eUndefined ); vk::Image img = nullptr; device.createImage(&imageInfo,&allocatorCallbacks,&img); vk::MemoryRequirements memRequirements; device.getImageMemoryRequirements(img,&memRequirements); uint32_t typeIndex = 0; get_memory_type(memRequirements.memoryTypeBits(),vk::MemoryPropertyFlagBits::eHostVisible,typeIndex); // -> typeIndex is set to 1 auto szMem = memRequirements.size(); vk::MemoryAllocateInfo memAlloc(szMem,typeIndex); vk::DeviceMemory mem; device.allocateMemory(&memAlloc,&allocatorCallbacks,&mem); // Note: Using the default allocation (nullptr) doesn't change anything device.bindImageMemory(img,mem,0); uint32_t mipLevel = 0; vk::ImageSubresource resource( vk::ImageAspectFlagBits::eColor, mipLevel, 0 ); vk::SubresourceLayout layout; device.getImageSubresourceLayout(img,&resource,&layout); auto *srcData = device.mapMemory(mem,0,szMem,vk::MemoryMapFlagBits(0)); [...] // Map the dds-data (See code from first post) device.unmapMemory(mem);
The code works without problems, but the resulting image is incorrect. This is the original image:

And this is the result:

I am sure that the problem is in the first updated code that I published, however, if it is not, I wrote a small adaptation of the demonstration of the triangle from the Vulkan SDK, which gives the same result, It can be downloaded here . The source code is included, all that I changed from the demo triangle is the demo_prepare_texture_image function in tri.c (lines 803 - 903) and the files "dds.cpp" and "dds.h". "dds.cpp" contains code for loading dds and displaying image memory.
I use gli to load dds data (which should "work fine with Vulkan"), which is also included in the download above. To create a project, the Vulkan SDK directory must be added to the tri project, and the path to the dds must be changed (tri.c, Line 809).
The original image ("x64 / Debug / test.dds" in the project) uses DXT1 compression. I tested other equipment with the same result.
Any sample code to initialize / map compressed images will also help a lot.