2017-09-22 108 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

因爲,正如我向你展示的鏈接所顯示的那樣,英語不會那樣工作。 –

相關問題