2017-09-22 207 views
0

我可以从R32G32B32A32图像中转储内容以获取屏幕截图。我想从R32G32_SFLOAT图像中读出一个像素。但结果看起来很奇怪。下面如何在Vulkan中读取gpu中的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,我知道这是没有意义的。但如果不改变格式,图像中仍然存在很多“漏洞”,而且这些值是致命的错误。

回答

4

我真的不知道,如果它是一个问题,但如果我理解你的代码,你想要把imagefilename。 所以你想读这张图片。但是,你说这个图像的旧布局(不是分段的)是UNDEFINED布局。实现可以自由地假定你不关心存储在其中的数据。使用真正的布局,而不是(我认为它是COLOR_ATTACHMENT或类似的东西)。

此外,您正在使用一个分段映像和一个分段缓冲区。我不明白你为什么要做这样的事情?为什么不简单使用vkCmdCopyImageToBuffer函数imagestaging_buffer

顺便说一句,与Vulkan它不是因为一些代码在某些GPU上适用此代码是正确的。

此外,我认为你必须使用内存屏障后,你的意思是HOST_STAGE和HOST_READ缓冲区。在规格上,它是写:

信令栅栏和等待在主机上并不能保证内存的结果访问将是可见的主机,如通过围栏定义的存储器依赖的访问范围只包括设备访问权限。必须使用内存屏障或其他内存依赖性来保证这一点。有关更多信息,请参阅主机访问类型的说明。

+0

请问您对上次陈述发表评论吗?我无法看到基于某些可用功能集编写的编写良好且正确同步的代码如何在支持功能列表的一个GPU驱动程序对上工作,而无法在支持相同功能的另一个GPU驱动程序对上工作。 –

+1

有一个误解。我的意思是某些GPU需要比其他GPU更多的同步或适当的布局转换。例如,对于NVIDIA,您可能不需要完全同步所有内容以使其正常工作。 AMD显卡需要更多的努力。但是,我没有说代码是正确的。正如我刚才所说的,一个错误的代码可以在NVIDIA上运行,但在AMD上不会运行,OP的情况就是这样。它的代码适用于某些GPU,但不适用于所有人;) –

+0

我使用分段映像的原因是我希望看到在不同格式的src和dst映像上使用vkCmdBlitImage时会发生什么情况。 Yap它是彩色附件。写入“未定义”真的意味着我现在不关心格式和同步。我希望这个功能可以用于非彩色附件图像。 –

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值不保证图像的内容被保留。

+1

[“你”与“你”作为礼貌写作形式](https://english.stackexchange.com/q/30185/25305) –

+0

@NicolBolas作为有礼貌的写作形式,'你'有什么问题? ;-) – Ekzuzy

+2

因为,正如我向你展示的链接所显示的那样,英语不会那样工作。 –