我一直在尝试使用Vulkan中的S3TC(BC / DXT)压缩加载压缩图像,但到目前为止,我没有太多运气。
以下是Vulkan规范关于压缩图像的内容:
https://www.khronos.org/registry/dataformat/specs/1.1/dataformat.1.1.html#S3TC:
使用S3TC压缩图像格式存储的压缩纹理图像表示为4×4纹素块的集合,其中每个块包含64或128位纹素数据。图像被编码为普通的2D光栅图像,其中每个4×4块被视为单个像素。
https://www.khronos.org/registry/vulkan/specs/1.0/xhtml/vkspec.html#resources-images:
对于使用线性平铺创建的图像,rowPitch,arrayPitch和depthPitch描述线性内存中子资源的布局。对于未压缩格式,rowPitch是相邻行中具有相同x坐标的纹素之间的字节数(y坐标相差一)。 arrayPitch是纹素之间的字节数,在图像的相邻数组层中具有相同的x和y坐标(数组层值相差一)。 depthPitch是在3D图像的相邻切片中具有相同x和y坐标的纹素之间的字节数(z坐标相差1)。表示为寻址公式,子资源中texel的起始字节具有地址:
//(x,y,z,layer)采用纹理坐标
地址(x,y,z,图层)=图层 arrayPitch + z depthPitch + y rowPitch + x texelSize + offset
对于压缩格式,rowPitch是相邻行中压缩块之间的字节数。 arrayPitch是相邻阵列层中块之间的字节数。 depthPitch是3D图像的相邻切片中块之间的字节数。
对于未创建为数组的图像,未定义//(x,y,z,layer)采用块坐标
地址(x,y,z,图层)=图层 arrayPitch + z depthPitch + y rowPitch + x blockSize + offset;
arrayPitch。 depthPitch仅针对3D图像定义。 对于颜色格式,VkImageSubresource的aspectMask成员必须为VK_IMAGE_ASPECT_COLOR_BIT。对于深度/模板格式,方面必须是VK_IMAGE_ASPECT_DEPTH_BIT或VK_IMAGE_ASPECT_STENCIL_BIT。在分别存储深度和模板方面的实现上,查询每个子资源布局将返回不同的偏移量和大小,表示用于该方面的内存区域。在存储深度和模板方面交错的实现上,返回相同的偏移量和大小,并表示交叉存储器分配。
我的图像是普通的2D图像(0层,1个mipmap),因此没有 arrayPitch 或 depthPitch 。由于硬件直接支持S3TC压缩,因此应该可以在不对其进行解压缩的情况下使用图像数据。在OpenGL中,这可以使用 glCompressedTexImage2D 来完成,这在过去对我有用。
在OpenGL中,我使用了GL_COMPRESSED_RGBA_S3TC_DXT1_EXT作为图像格式,对于Vulkan,我使用的是VK_FORMAT_BC1_RGBA_UNORM_BLOCK,它应该是等效的。 这是我映射图像数据的代码:
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
}
}
这是初始化图像的代码:
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);
代码运行没有问题,但生成的图像不正确。这是源图像:
这就是结果:
我确定问题出在我发布的第一个代码剪辑中,但是,如果没有,我已经编写了一个很小的改编版本来自Vulkan SDK的三角形演示,它会产生相同的结果。它可以下载here。包含了源代码,我从三角形演示中更改的是 tri.c 中的“demo_prepare_texture_image”函数(第803行到第903行)以及“dds.cpp”和“dds” .h“文件。 “dds.cpp”包含加载dds和映射图像内存的代码。
我正在使用gli来加载dds-data(应该与Vulkan完美配合),这也包含在上面的下载中。要构建项目,必须将Vulkan SDK include目录添加到“tri”项目中,并且必须更改dds的路径(tri.c,第809行)。
源映像(项目中的“x64 / Debug / test.dds”)使用DXT1压缩。我也在不同的硬件上测试过,结果相同。
用于初始化/映射压缩图像的任何示例代码也会有很大帮助。
答案 0 :(得分:3)