从 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}.

因此,如果我假设具有最佳布局的图像被压缩,但具有线性布局的图像没有被压缩,这就解释了问题。