如何在Vulkan中

时间:2017-09-22 11:04:08

标签: vulkan

我能够从R32G32B32A32图像中转储内容以获取截图。我想从R32G32_SFLOAT图像中读出一个像素。但结果看起来很奇怪。

enter image description here

下面是我的工作图像转储代码(没有验证错误)

void DumpImageToFile(VkTool::VulkanDevice &device, VkQueue graphics_queue, VkTool::Wrapper::CommandBuffers &command_buffer, VkImage image, uint32_t width, uint32_t height, const char *filename)
{
    auto image_create_info = VkTool::Initializer::GenerateImageCreateInfo(VK_IMAGE_TYPE_2D, VK_FORMAT_R8G8B8A8_UNORM, {width, height, 1},
        VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT, VK_SAMPLE_COUNT_1_BIT);
    VkTool::Wrapper::Image staging_image(device, image_create_info, VK_MEMORY_HEAP_DEVICE_LOCAL_BIT);
    auto buffer_create_info = VkTool::Initializer::GenerateBufferCreateInfo(width * height * 4, VK_BUFFER_USAGE_TRANSFER_DST_BIT);
    VkTool::Wrapper::Buffer staging_buffer(device, buffer_create_info, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);

    // Copy texture to buffer

    command_buffer.Begin();

    auto image_memory_barrier = VkTool::Initializer::GenerateImageMemoryBarrier(VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
    { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }, staging_image.Get());
    device.vkCmdPipelineBarrier(command_buffer.Get(), VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0
        , 0, nullptr, 0, nullptr, 1, &image_memory_barrier);


    image_memory_barrier = VkTool::Initializer::GenerateImageMemoryBarrier(VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
    { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }, image);
    device.vkCmdPipelineBarrier(command_buffer.Get(), VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0
        , 0, nullptr, 0, nullptr, 1, &image_memory_barrier);


    // Copy!!
    VkImageBlit region = {};
    region.srcSubresource = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1 };
    region.srcOffsets[0] = { 0, 0, 0 };
    region.srcOffsets[1] = { static_cast<int32_t>(width), static_cast<int32_t>(height), 1};
    region.dstSubresource = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1 };
    region.dstOffsets[0] = { 0, 0, 0 };
    region.dstOffsets[1] = { static_cast<int32_t>(width), static_cast<int32_t>(height), 1 };
    device.vkCmdBlitImage(command_buffer.Get(), image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, staging_image.Get(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &region, VK_FILTER_LINEAR);


    image_memory_barrier = VkTool::Initializer::GenerateImageMemoryBarrier(VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
    { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }, image);
    device.vkCmdPipelineBarrier(command_buffer.Get(), VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0
        , 0, nullptr, 0, nullptr, 1, &image_memory_barrier);

    image_memory_barrier = VkTool::Initializer::GenerateImageMemoryBarrier(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
    { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }, staging_image.Get());
    device.vkCmdPipelineBarrier(command_buffer.Get(), VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0
        , 0, nullptr, 0, nullptr, 1, &image_memory_barrier);

    auto buffer_image_copy = VkTool::Initializer::GenerateBufferImageCopy({ VK_IMAGE_ASPECT_COLOR_BIT , 0, 0, 1 }, { width, height, 1 });
    device.vkCmdCopyImageToBuffer(command_buffer.Get(), staging_image.Get(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, staging_buffer.Get(), 1, &buffer_image_copy);

    command_buffer.End();

    std::vector<VkCommandBuffer> raw_command_buffers = command_buffer.GetAll();
    auto submit_info = VkTool::Initializer::GenerateSubmitInfo(raw_command_buffers);
    VkTool::Wrapper::Fence fence(device);
    device.vkQueueSubmit(graphics_queue, 1, &submit_info, fence.Get());
    fence.Wait();
    fence.Destroy();

    const uint8_t *mapped_address = reinterpret_cast<const uint8_t *>(staging_buffer.MapMemory());
    lodepng::encode(filename, mapped_address, width, height);
    staging_buffer.UnmapMemory();

    staging_image.Destroy();
    staging_buffer.Destroy();
}

抱歉丑陋的自制包装纸,没有官方包装纸。基本上,它创建一个临时图像和缓冲区。使用vkCmdBlitImage从源图像复制到临时图像。然后使用vkCmdCopyImageToBuffer并将缓冲区映射到主机内存。这个方法适用于多个gpus,它不需要担心填充。(我想,如果我错了,请纠正我。)

但是,我没有运气使用此方法来读取R32G32_SFLOAT。起初我认为这是因为字节顺序,直到我把整个图像丢弃。

enter image description here

上图是我直接将R32G32_SFLOAT转换为R8G8B8A8_UNORM,我知道它没有意义。但是,如果不改变格式,图像中仍然存在很多“漏洞”,价值也是致命错误。

2 个答案:

答案 0 :(得分:4)

我不确定这是否是问题,但如果我理解您的代码,您希望将image放入filename。 所以你想从这个图像中读取。但是,您说这个图像的旧布局(不是暂存的)是UNDEFINED布局。该实现可以自由地假设您不关心存储在其中的数据。使用真实的布局(我认为它是COLOR_ATTACHMENT或类似的东西)。

此外,您正在使用一个临时映像和一个临时缓冲区。我真的不明白你为什么要做这样的事情?为什么不简单地将vkCmdCopyImageToBuffer功能与image一起用于staging_buffer

顺便说一下,使用Vulkan并不是因为一些代码在某些GPU上有效,这个代码是正确的。

另外,我认为在转移到隐含HOST_STAGE和HOST_READ的缓冲区后必须使用内存屏障。在规范中,它是写:

  

发送栅栏并在主机上等待并不能保证内存访问的结果对主机可见,因为由fence定义的内存依赖性的访问范围仅包括设备访问。必须使用内存屏障或其他内存依赖性来保证这一点。有关详细信息,请参阅主机访问类型的说明。

答案 1 :(得分:0)

这部分代码看起来很奇怪:

image_memory_barrier = VkTool::Initializer::GenerateImageMemoryBarrier(VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }, image);
device.vkCmdPipelineBarrier(command_buffer.Get(), VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier);

这基本上意味着在屏障之后您的源图像可能没有任何数据。用作源布局的UNDEFINED值不保证图像的内容得以保留。