从 vulkan 帧缓冲区下载的图像失真
Image downloaded form vulkan frame buffer is distorted
我正在使用 vulkan 进行离屏渲染。由于我在设备内存中获得了渲染图像,因此我将其下载到主机内存以用于其他目的。一切正常,直到图像的宽度是 8 的倍数,否则图像会失真。
这是 vulkan 的特性,还是我哪里弄错了?
帧缓冲区代码:
colorImageInfo = VkImageCreateInfo(
imageType=VK_IMAGE_TYPE_2D,
format=VK_FORMAT_B8G8R8A8_UNORM,
mipLevels=1,
arrayLayers=1,
samples=1,
tiling=VK_IMAGE_TILING_OPTIMAL,
usage=VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
initialLayout=VK_IMAGE_LAYOUT_UNDEFINED,
extent=[self.width, self.height, 1]
)
colorImage = vkCreateImage(self.inst.device, colorImageInfo, None)
self.colorImage = colorImage
memReqs = vkGetImageMemoryRequirements(self.inst.device, colorImage)
memReqs = cdh.cdata_dict(memReqs)
memProps = cdh.cdata_dict(self.inst.memoryProperties)
memoryType = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT
typeBits = memReqs["memoryTypeBits"]
for i in range(32):
if (typeBits & 1) == 1:
if (memProps["memoryTypes"][i]["propertyFlags"] & memoryType) == memoryType:
memType = i
break
typeBits >>= 1
memAllocInfo = VkMemoryAllocateInfo(
memoryTypeIndex=memType,
allocationSize=memReqs["size"]
)
memAlloc = vkAllocateMemory(self.inst.device, memAllocInfo, None)
# print(memAlloc)
vkBindImageMemory(self.inst.device, colorImage, memAlloc, 0)
subresourceRange = VkImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1)
imageViewInfo = VkImageViewCreateInfo(
viewType=VK_IMAGE_VIEW_TYPE_2D,
format=VK_FORMAT_B8G8R8A8_UNORM,
image=colorImage,
subresourceRange=subresourceRange
)
colorImageView = vkCreateImageView(self.inst.device, imageViewInfo, None)
# print(colorImageView)
# depth Image
# create frame buffer
framebuffferCreateInfo = VkFramebufferCreateInfo(
pAttachments=[colorImageView],
width=self.width,
height=self.height,
layers=1,
renderPass=self.inst.renderPass
)
self.fb = vkCreateFramebuffer(self.inst.device, framebuffferCreateInfo, None)
复制图片到主机可见内存代码:
colorImageInfo = VkImageCreateInfo(
imageType=VK_IMAGE_TYPE_2D,
format=self.inst.colorFormat,
mipLevels=1,
arrayLayers=1,
samples=1,
tiling=VK_IMAGE_TILING_LINEAR,
usage=VK_IMAGE_USAGE_TRANSFER_DST_BIT,
initialLayout=VK_IMAGE_LAYOUT_UNDEFINED,
extent=[self.width, self.height, 1]
)
colorImage = vkCreateImage(self.inst.device, colorImageInfo, None)
memReqs = vkGetImageMemoryRequirements(self.inst.device, colorImage)
memReqs = cdh.cdata_dict(memReqs)
memProps = cdh.cdata_dict(self.inst.memoryProperties)
memoryType = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT
typeBits = memReqs["memoryTypeBits"]
for i in range(32):
if (typeBits & 1) == 1:
if (memProps["memoryTypes"][i]["propertyFlags"] & memoryType) == memoryType:
memType = i
break
typeBits >>= 1
memAllocInfo = VkMemoryAllocateInfo(
memoryTypeIndex=memType,
allocationSize=memReqs["size"]
)
memAlloc = vkAllocateMemory(self.inst.device, memAllocInfo, None)
vkBindImageMemory(self.inst.device, colorImage, memAlloc, 0)
commandBufferAllocateInfo = VkCommandBufferAllocateInfo(
commandPool=self.inst.commandPool,
level=VK_COMMAND_BUFFER_LEVEL_PRIMARY,
commandBufferCount=1
)
cmdBuff = vkAllocateCommandBuffers(self.inst.device, commandBufferAllocateInfo)
cmdBuff = ffi.addressof(cmdBuff, 0)[0]
beginInfo = VkCommandBufferBeginInfo(
sType=VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
flags=VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT
)
vkBeginCommandBuffer(cmdBuff, beginInfo)
subresourceRange = VkImageSubresourceRange(
aspectMask=VK_IMAGE_ASPECT_COLOR_BIT,
baseMipLevel=0,
levelCount=1,
baseArrayLayer=0,
layerCount=1
)
barrier = VkImageMemoryBarrier(
oldLayout=VK_IMAGE_LAYOUT_UNDEFINED,
newLayout=VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
srcQueueFamilyIndex=VK_QUEUE_FAMILY_IGNORED,
dstQueueFamilyIndex=VK_QUEUE_FAMILY_IGNORED,
image=colorImage,
srcAccessMask=0,
dstAccessMask=VK_ACCESS_TRANSFER_WRITE_BIT,
subresourceRange=subresourceRange
)
vkCmdPipelineBarrier(cmdBuff, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, None, 0,
None, 1, barrier)
# source image barrier
barrier = VkImageMemoryBarrier(
oldLayout=VK_IMAGE_LAYOUT_GENERAL,
newLayout=VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
srcQueueFamilyIndex=VK_QUEUE_FAMILY_IGNORED,
dstQueueFamilyIndex=VK_QUEUE_FAMILY_IGNORED,
image=self.colorImage,
srcAccessMask=VK_ACCESS_MEMORY_READ_BIT,
dstAccessMask=VK_ACCESS_TRANSFER_READ_BIT,
subresourceRange=subresourceRange
)
vkCmdPipelineBarrier(cmdBuff, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, None, 0,
None,
1, barrier)
srcSubresource = VkImageSubresourceLayers(
aspectMask=VK_IMAGE_ASPECT_COLOR_BIT,
layerCount=1
)
dstSubresource = VkImageSubresourceLayers(
aspectMask=VK_IMAGE_ASPECT_COLOR_BIT,
layerCount=1
)
imageCopy = VkImageCopy(
srcSubresource,
VkOffset3D(0, 0, 0),
dstSubresource,
VkOffset3D(0, 0, 0),
VkExtent3D(self.width, self.height)
)
vkCmdCopyImage(cmdBuff, self.colorImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
colorImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, imageCopy)
barrier = VkImageMemoryBarrier(
oldLayout=VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
newLayout=VK_IMAGE_LAYOUT_GENERAL,
srcQueueFamilyIndex=VK_QUEUE_FAMILY_IGNORED,
dstQueueFamilyIndex=VK_QUEUE_FAMILY_IGNORED,
image=colorImage,
srcAccessMask=VK_ACCESS_TRANSFER_WRITE_BIT,
dstAccessMask=VK_ACCESS_MEMORY_READ_BIT,
subresourceRange=subresourceRange
)
vkCmdPipelineBarrier(cmdBuff, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, None, 0,
None,
1, barrier)
barrier = VkImageMemoryBarrier(
oldLayout=VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
newLayout=VK_IMAGE_LAYOUT_GENERAL,
srcQueueFamilyIndex=VK_QUEUE_FAMILY_IGNORED,
dstQueueFamilyIndex=VK_QUEUE_FAMILY_IGNORED,
image=self.colorImage,
srcAccessMask=VK_ACCESS_TRANSFER_READ_BIT,
dstAccessMask=VK_ACCESS_MEMORY_READ_BIT,
subresourceRange=subresourceRange
)
vkCmdPipelineBarrier(cmdBuff, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, None, 0,
None,
1, barrier)
vkEndCommandBuffer(cmdBuff)
self.getImageSubmitInfo = VkSubmitInfo(
pCommandBuffers=[cmdBuff]
)
self.imageMemory = memAlloc
下载图片代码:
vkQueueSubmit(self.inst.graphicsQueue, 1, self.getImageSubmitInfo, VK_NULL_HANDLE)
vkQueueWaitIdle(self.inst.graphicsQueue)
pb = vkMapMemory(self.inst.device, self.imageMemory, 0, self.width * self.height * 4, 0)
out = np.frombuffer(pb, np.uint8)
out = out.reshape((self.height, self.width, 4))
vkUnmapMemory(self.inst.device, self.imageMemory)
这就是我发现的 19.4.1. Buffer and Image Addressing。
这是 vkCmdCopyImage 复制压缩数据的方式:
rowLength = region->bufferRowLength;
if (rowLength == 0)
rowLength = region->imageExtent.width;
imageHeight = region->bufferImageHeight;
if (imageHeight == 0)
imageHeight = region->imageExtent.height;
compressedTexelBlockSizeInBytes = <compressed texel block size taken from the src/dstImage>;
rowLength /= compressedTexelBlockWidth;
imageHeight /= compressedTexelBlockHeight;
address of (x,y,z) = region->bufferOffset + (((z * imageHeight) + y) * rowLength + x) * compressedTexelBlockSizeInBytes;
where x,y,z range from (0,0,0) to region->imageExtent.{width/compressedTexelBlockWidth,height/compressedTexelBlockHeight,depth/compressedTexelBlockDepth}.
因此,如果我假设具有最佳布局的图像被压缩,但具有线性布局的图像没有被压缩,这就解释了问题。
我正在使用 vulkan 进行离屏渲染。由于我在设备内存中获得了渲染图像,因此我将其下载到主机内存以用于其他目的。一切正常,直到图像的宽度是 8 的倍数,否则图像会失真。
这是 vulkan 的特性,还是我哪里弄错了?
帧缓冲区代码:
colorImageInfo = VkImageCreateInfo(
imageType=VK_IMAGE_TYPE_2D,
format=VK_FORMAT_B8G8R8A8_UNORM,
mipLevels=1,
arrayLayers=1,
samples=1,
tiling=VK_IMAGE_TILING_OPTIMAL,
usage=VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
initialLayout=VK_IMAGE_LAYOUT_UNDEFINED,
extent=[self.width, self.height, 1]
)
colorImage = vkCreateImage(self.inst.device, colorImageInfo, None)
self.colorImage = colorImage
memReqs = vkGetImageMemoryRequirements(self.inst.device, colorImage)
memReqs = cdh.cdata_dict(memReqs)
memProps = cdh.cdata_dict(self.inst.memoryProperties)
memoryType = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT
typeBits = memReqs["memoryTypeBits"]
for i in range(32):
if (typeBits & 1) == 1:
if (memProps["memoryTypes"][i]["propertyFlags"] & memoryType) == memoryType:
memType = i
break
typeBits >>= 1
memAllocInfo = VkMemoryAllocateInfo(
memoryTypeIndex=memType,
allocationSize=memReqs["size"]
)
memAlloc = vkAllocateMemory(self.inst.device, memAllocInfo, None)
# print(memAlloc)
vkBindImageMemory(self.inst.device, colorImage, memAlloc, 0)
subresourceRange = VkImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1)
imageViewInfo = VkImageViewCreateInfo(
viewType=VK_IMAGE_VIEW_TYPE_2D,
format=VK_FORMAT_B8G8R8A8_UNORM,
image=colorImage,
subresourceRange=subresourceRange
)
colorImageView = vkCreateImageView(self.inst.device, imageViewInfo, None)
# print(colorImageView)
# depth Image
# create frame buffer
framebuffferCreateInfo = VkFramebufferCreateInfo(
pAttachments=[colorImageView],
width=self.width,
height=self.height,
layers=1,
renderPass=self.inst.renderPass
)
self.fb = vkCreateFramebuffer(self.inst.device, framebuffferCreateInfo, None)
复制图片到主机可见内存代码:
colorImageInfo = VkImageCreateInfo(
imageType=VK_IMAGE_TYPE_2D,
format=self.inst.colorFormat,
mipLevels=1,
arrayLayers=1,
samples=1,
tiling=VK_IMAGE_TILING_LINEAR,
usage=VK_IMAGE_USAGE_TRANSFER_DST_BIT,
initialLayout=VK_IMAGE_LAYOUT_UNDEFINED,
extent=[self.width, self.height, 1]
)
colorImage = vkCreateImage(self.inst.device, colorImageInfo, None)
memReqs = vkGetImageMemoryRequirements(self.inst.device, colorImage)
memReqs = cdh.cdata_dict(memReqs)
memProps = cdh.cdata_dict(self.inst.memoryProperties)
memoryType = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT
typeBits = memReqs["memoryTypeBits"]
for i in range(32):
if (typeBits & 1) == 1:
if (memProps["memoryTypes"][i]["propertyFlags"] & memoryType) == memoryType:
memType = i
break
typeBits >>= 1
memAllocInfo = VkMemoryAllocateInfo(
memoryTypeIndex=memType,
allocationSize=memReqs["size"]
)
memAlloc = vkAllocateMemory(self.inst.device, memAllocInfo, None)
vkBindImageMemory(self.inst.device, colorImage, memAlloc, 0)
commandBufferAllocateInfo = VkCommandBufferAllocateInfo(
commandPool=self.inst.commandPool,
level=VK_COMMAND_BUFFER_LEVEL_PRIMARY,
commandBufferCount=1
)
cmdBuff = vkAllocateCommandBuffers(self.inst.device, commandBufferAllocateInfo)
cmdBuff = ffi.addressof(cmdBuff, 0)[0]
beginInfo = VkCommandBufferBeginInfo(
sType=VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
flags=VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT
)
vkBeginCommandBuffer(cmdBuff, beginInfo)
subresourceRange = VkImageSubresourceRange(
aspectMask=VK_IMAGE_ASPECT_COLOR_BIT,
baseMipLevel=0,
levelCount=1,
baseArrayLayer=0,
layerCount=1
)
barrier = VkImageMemoryBarrier(
oldLayout=VK_IMAGE_LAYOUT_UNDEFINED,
newLayout=VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
srcQueueFamilyIndex=VK_QUEUE_FAMILY_IGNORED,
dstQueueFamilyIndex=VK_QUEUE_FAMILY_IGNORED,
image=colorImage,
srcAccessMask=0,
dstAccessMask=VK_ACCESS_TRANSFER_WRITE_BIT,
subresourceRange=subresourceRange
)
vkCmdPipelineBarrier(cmdBuff, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, None, 0,
None, 1, barrier)
# source image barrier
barrier = VkImageMemoryBarrier(
oldLayout=VK_IMAGE_LAYOUT_GENERAL,
newLayout=VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
srcQueueFamilyIndex=VK_QUEUE_FAMILY_IGNORED,
dstQueueFamilyIndex=VK_QUEUE_FAMILY_IGNORED,
image=self.colorImage,
srcAccessMask=VK_ACCESS_MEMORY_READ_BIT,
dstAccessMask=VK_ACCESS_TRANSFER_READ_BIT,
subresourceRange=subresourceRange
)
vkCmdPipelineBarrier(cmdBuff, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, None, 0,
None,
1, barrier)
srcSubresource = VkImageSubresourceLayers(
aspectMask=VK_IMAGE_ASPECT_COLOR_BIT,
layerCount=1
)
dstSubresource = VkImageSubresourceLayers(
aspectMask=VK_IMAGE_ASPECT_COLOR_BIT,
layerCount=1
)
imageCopy = VkImageCopy(
srcSubresource,
VkOffset3D(0, 0, 0),
dstSubresource,
VkOffset3D(0, 0, 0),
VkExtent3D(self.width, self.height)
)
vkCmdCopyImage(cmdBuff, self.colorImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
colorImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, imageCopy)
barrier = VkImageMemoryBarrier(
oldLayout=VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
newLayout=VK_IMAGE_LAYOUT_GENERAL,
srcQueueFamilyIndex=VK_QUEUE_FAMILY_IGNORED,
dstQueueFamilyIndex=VK_QUEUE_FAMILY_IGNORED,
image=colorImage,
srcAccessMask=VK_ACCESS_TRANSFER_WRITE_BIT,
dstAccessMask=VK_ACCESS_MEMORY_READ_BIT,
subresourceRange=subresourceRange
)
vkCmdPipelineBarrier(cmdBuff, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, None, 0,
None,
1, barrier)
barrier = VkImageMemoryBarrier(
oldLayout=VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
newLayout=VK_IMAGE_LAYOUT_GENERAL,
srcQueueFamilyIndex=VK_QUEUE_FAMILY_IGNORED,
dstQueueFamilyIndex=VK_QUEUE_FAMILY_IGNORED,
image=self.colorImage,
srcAccessMask=VK_ACCESS_TRANSFER_READ_BIT,
dstAccessMask=VK_ACCESS_MEMORY_READ_BIT,
subresourceRange=subresourceRange
)
vkCmdPipelineBarrier(cmdBuff, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, None, 0,
None,
1, barrier)
vkEndCommandBuffer(cmdBuff)
self.getImageSubmitInfo = VkSubmitInfo(
pCommandBuffers=[cmdBuff]
)
self.imageMemory = memAlloc
下载图片代码:
vkQueueSubmit(self.inst.graphicsQueue, 1, self.getImageSubmitInfo, VK_NULL_HANDLE)
vkQueueWaitIdle(self.inst.graphicsQueue)
pb = vkMapMemory(self.inst.device, self.imageMemory, 0, self.width * self.height * 4, 0)
out = np.frombuffer(pb, np.uint8)
out = out.reshape((self.height, self.width, 4))
vkUnmapMemory(self.inst.device, self.imageMemory)
这就是我发现的 19.4.1. Buffer and Image Addressing。
这是 vkCmdCopyImage 复制压缩数据的方式:
rowLength = region->bufferRowLength;
if (rowLength == 0)
rowLength = region->imageExtent.width;
imageHeight = region->bufferImageHeight;
if (imageHeight == 0)
imageHeight = region->imageExtent.height;
compressedTexelBlockSizeInBytes = <compressed texel block size taken from the src/dstImage>;
rowLength /= compressedTexelBlockWidth;
imageHeight /= compressedTexelBlockHeight;
address of (x,y,z) = region->bufferOffset + (((z * imageHeight) + y) * rowLength + x) * compressedTexelBlockSizeInBytes;
where x,y,z range from (0,0,0) to region->imageExtent.{width/compressedTexelBlockWidth,height/compressedTexelBlockHeight,depth/compressedTexelBlockDepth}.
因此,如果我假设具有最佳布局的图像被压缩,但具有线性布局的图像没有被压缩,这就解释了问题。