Vulkan - 异步纹理上传 - 图像过渡问题

Vulkan - Asynchronous Texture Upload - Image Transition Issue

我正在使用传输队列将数据上传到 GPU 本地内存以供图形队列使用。我相信我需要 3 个障碍,一个是从传输队列中释放纹理对象,一个是在图形队列中获取它,一个是将它从 TRANSFER_DST_OPTIMAL 转换到 SHADER_READ_ONLY_OPTIMAL。我认为我的障碍是不正确的,因为这是我得到的错误,而且我在 Nvidia 硬件上看到了正确的渲染输出。是否缺少任何同步?

UNASSIGNED-CoreValidation-DrawState-InvalidImageLayout(ERROR / SPEC): msgNum: 1303270965 - 
Validation Error: [ UNASSIGNED-CoreValidation-DrawState-InvalidImageLayout ] Object 0: 
handle = 0x562696461ca0, type = VK_OBJECT_TYPE_COMMAND_BUFFER; | MessageID = 0x4dae5635 | 
Submitted command buffer expects VkImage 0x1c000000001c[] (subresource: aspectMask 0x1 array 
layer 0, mip level 0) to be in layout VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL--instead, 
current layout is VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL.

我认为我做错的是没有正确指定 stageMasks

    VkImageMemoryBarrier tex_barrier = {0};
    /* layout transition - UNDEFINED -> TRANSFER_DST */
    tex_barrier.srcAccessMask = 0;
    tex_barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
    tex_barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
    tex_barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
    tex_barrier.srcQueueFamilyIndex = -1; 
    tex_barrier.dstQueueFamilyIndex = -1;
    tex_barrier.subresourceRange = (VkImageSubresourceRange) { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 };

    vkCmdPipelineBarrier(transfer_cmdbuffs[0], 
                         VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
                         VK_PIPELINE_STAGE_TRANSFER_BIT,
                         0,
                         0, NULL, 0, NULL, 1, &tex_barrier);

    /* queue ownership transfer */
    tex_barrier.srcAccessMask = 0;
    tex_barrier.dstAccessMask = 0;
    tex_barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
    tex_barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
    tex_barrier.srcQueueFamilyIndex = device.transfer_queue_family_index;
    tex_barrier.dstQueueFamilyIndex = device.graphics_queue_family_index;
    
    vkCmdPipelineBarrier(transfer_cmdbuffs[0],
                         VK_PIPELINE_STAGE_TRANSFER_BIT,
                         VK_PIPELINE_STAGE_TRANSFER_BIT,
                         0,
                         0, NULL, 0, NULL, 1, &tex_barrier);
                         
    tex_barrier.srcAccessMask = 0;
    tex_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
    tex_barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
    tex_barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
    tex_barrier.srcQueueFamilyIndex = device.transfer_queue_family_index;
    tex_barrier.dstQueueFamilyIndex = device.graphics_queue_family_index;
    
    vkCmdPipelineBarrier(transfer_cmdbuffs[0],
                         VK_PIPELINE_STAGE_TRANSFER_BIT,
                         VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
                         0,
                         0, NULL, 0, NULL, 1, &tex_barrier);
    

进行所有权转移是一个双向过程:转移源必须释放资源,接收方必须获取资源。我所说的“源”和“接收者”是指 队列 本身。您不能仅仅让队列获得资源的所有权;该队列必须发出命令以声明它的所有权。

您需要在源队列上提交释放屏障操作。它必须指定源队列系列以及目标队列系列。然后,您必须使用相同的源和目标在接收队列上提交获取屏障操作。并且您必须通过信号量确保这些操作的顺序。因此,vkQueueSubmit 获取调用必须等待释放操作提交后的信号量(时间线信号量也可以)。

现在,由于这些是 pipeline/memory 障碍,您还可以自由指定布局转换。您不需要第三个障碍来更改布局,但两个障碍必须为 acquire/release 操作指定相同的 source/destination 布局。