You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

不使用阶段缓冲区复制数据到VK_FORMAT_R8G8B8_UNORM线性平铺图像异常

Vulkan直接复制R8G8B8格式图像采样异常的问题解决

问题描述

在Vulkan中向图像复制数据有两种方式:使用阶段缓冲区或直接复制。使用阶段缓冲区时,VK_FORMAT_R8G8B8A8_UNORM和VK_FORMAT_R8G8B8_UNORM格式均能正常工作;但不使用阶段缓冲区时,VK_FORMAT_R8G8B8A8_UNORM格式工作正常,切换为VK_FORMAT_R8G8B8_UNORM格式后,采样结果异常。已确认不同格式下源数据均正确。

问题原因

直接复制路径采用**线性平铺(VK_IMAGE_TILING_LINEAR)**图像,这种图像的内存布局会遵循设备的行对齐要求。VK_FORMAT_R8G8B8_UNORM每个像素占3字节,当图像宽度×3无法满足设备对齐标准(通常为4或8字节)时,每行末尾会被填充空白字节。而源数据是紧密排列的无填充格式,直接用memcpy整段拷贝会导致后续行的数据错位,最终采样时出现异常。

VK_FORMAT_R8G8B8A8_UNORM每个像素占4字节,行长度天然符合4字节对齐要求,不会产生填充,因此直接拷贝可以正常工作。

解决方案

需要获取线性图像的实际行步长(stride),逐行拷贝有效像素数据,跳过设备自动添加的填充部分:

  1. 调用vkGetImageSubresourceLayout获取线性图像的内存布局,其中rowPitch字段为每行的实际字节数(包含填充)
  2. 逐行将源数据中的有效像素数据拷贝到映射的图像内存,每行仅拷贝宽度×像素字节数的内容,跳过填充区域

修改后的代码片段

在直接复制的分支中,替换原有的memcpy代码块:

// Map image memory
void *data;
VK_CHECK_RESULT(vkMapMemory(device, mappableMemory, 0, memReqs.size, 0, &data));

// 获取图像子资源布局,提取行步长
VkImageSubresource subresource = {};
subresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
subresource.mipLevel = 0;
subresource.arrayLayer = 0;
VkSubresourceLayout subresourceLayout;
vkGetImageSubresourceLayout(device, mappableImage, &subresource, &subresourceLayout);

// 逐行拷贝数据,处理行对齐填充
uint32_t pixelSize = 3; // R8G8B8格式每个像素占3字节
uint32_t srcRowSize = texture.width * pixelSize;
uint8_t* srcPtr = ktxTextureData;
uint8_t* dstPtr = static_cast<uint8_t*>(data);

for (uint32_t y = 0; y < texture.height; y++) {
    memcpy(dstPtr, srcPtr, srcRowSize);
    srcPtr += srcRowSize;
    dstPtr += subresourceLayout.rowPitch;
}

vkUnmapMemory(device, mappableMemory);

额外说明

  • 阶段缓冲区路径正常是因为vkCmdCopyBufferToImage会自动处理格式的对齐和布局转换,无需手动考虑行步长
  • 对于其他非4字节对齐的格式(如R5G6B5),直接复制时也需要用相同方式处理行步长

内容的提问来源于stack exchange,提问作者Camellia Ca

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 04:45:54