首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >在Vulkan中,如何正确地将图像布局从传输最优转换为着色器读取优化,同时也改变队列所有权?

在Vulkan中,如何正确地将图像布局从传输最优转换为着色器读取优化,同时也改变队列所有权?
EN

Stack Overflow用户
提问于 2021-06-15 21:37:30
回答 1查看 1K关注 0票数 2

我目前正在用Vulkan API编写一个呈现引擎,如果可能的话,我的安装程序使用一个与图形操作不同的队列进行传输操作。当呈现图像时,它们应该在VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL布局中,但是由于图像当前由一个队列所拥有,该队列只使用传输位标记,所以我还需要同时将图像的所有权传递到图形队列。

但是,这似乎是由于某种原因而失败的,因为即使执行了带有管道屏障的命令缓冲区,映像仍然保留在VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL布局中,从而导致验证错误。

这是转换图像布局的方法:

代码语言:javascript
运行
复制
int PGraphicsContext::transitionImageLayout(VkImage image, VkImageLayout oldLayout, VkImageLayout newLayout)
{
    VkCommandBuffer cmdBuffer = this->beginTransferCmdBuffer();

    VkImageMemoryBarrier barrier{};
    barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
    barrier.oldLayout = oldLayout;
    barrier.newLayout = newLayout;
    barrier.image = image;
    barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
    barrier.subresourceRange.baseMipLevel = 0;
    barrier.subresourceRange.levelCount = 1;
    barrier.subresourceRange.baseArrayLayer = 0;
    barrier.subresourceRange.layerCount = 1;

    VkPipelineStageFlags srcStage;
    VkPipelineStageFlags dstStage;

    if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL)
    {
        PApplication::getInstance()->getLogger()->debug("Transitioning image layout from VK_IMAGE_LAYOUT_UNDEFINED to VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL");
        barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
        barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
        barrier.srcAccessMask = 0;
        barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;

        srcStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
        dstStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
    }
    else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
    {
        PApplication::getInstance()->getLogger()->debug("Transitioning image layout from VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL to VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL");

        if (this->queueFamilies[PQueueIndex::TRANSFER_QUEUE].index != this->queueFamilies[PQueueIndex::GRAPHICS_QUEUE].index)
        {
            barrier.srcQueueFamilyIndex = this->queueFamilies[PQueueIndex::TRANSFER_QUEUE].index;
            barrier.dstQueueFamilyIndex = this->queueFamilies[PQueueIndex::GRAPHICS_QUEUE].index;
        }
        else
        {
            barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
            barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
        }
        barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
        barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;

        srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
        dstStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
    }
    else
    {
        // TODO: implement this
        this->endTransferCmdBuffer(cmdBuffer);
        PApplication::getInstance()->getLogger()->error("Failed to transition image layout from 0x%X to 0x%X", oldLayout, newLayout);
        return PINE_ERROR_VULKAN_UNSUPPORTED_LAYER_TRANSITION;
    }

    vkCmdPipelineBarrier(cmdBuffer, srcStage, dstStage, {}, 0, nullptr, 0, nullptr, 1, &barrier);

    this->endTransferCmdBuffer(cmdBuffer);
    return PINE_SUCCESS;
}

如果传输队列的索引和图形队列的索引相同,意味着源队列和目标队列族索引都设置为VK_QUEUE_FAMILY_IGNORED,这似乎很好。

下面是一些日志消息的示例:

代码语言:javascript
运行
复制
[15 JUN 23:17:27][Taiga::DEBUG]: Transitioning image layout from VK_IMAGE_LAYOUT_UNDEFINED to VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL
[15 JUN 23:17:27][Taiga::DEBUG]: Transitioning image layout from VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL to VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL
[15 JUN 23:17:27][Taiga::ERROR]: Validation Error: [ UNASSIGNED-CoreValidation-DrawState-InvalidImageLayout ] Object 0: handle = 0x7f34b4842668, type = VK_OBJECT_TYPE_COMMAND_BUFFER; | MessageID = 0x4dae5635 | Submitted command buffer expects VkImage 0xec4bec000000000b[] (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.

只有当this->queueFamilies[PQueueIndex::TRANSFER_QUEUE].index是与this->queueFamilies[PQueueIndex::GRAPHICS_QUEUE].index不同的队列族索引时,才会发生验证错误。

是否有可能同时传输所有权和转换布局,或者正确的方法是首先记录一个命令缓冲区,该缓冲区只将图像的所有权从传输传递到图形队列,然后再记录第二个命令缓冲区,该缓冲区实际上传输布局,还是应该放弃使用单独的传输队列(用于图像)的整个想法?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2021-06-16 18:47:06

好的,我找到了解决问题的方法。

文档指出,在执行第一个屏障之后,我还必须在另一个队列中发出相同的管道屏障命令,这一点我完全错过了。

因此,这里有一个可行的解决方案,尽管它不是最优的,因为它在CPU上的一个线程上同步运行,并且每次我想转换布局时都重新创建命令缓冲区。

代码语言:javascript
运行
复制
int PGraphicsContext::beginCmdBuffer(VkCommandBuffer *cmdBuffer, const PQueueIndex queueIndex)
{
    if (queueIndex != PQueueIndex::GRAPHICS_QUEUE && queueIndex != PQueueIndex::TRANSFER_QUEUE)
        return PINE_ERROR_INVALID_ARGUMENT;

    VkCommandBufferAllocateInfo allocInfo{};
    allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
    allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
    allocInfo.commandPool = queueIndex == PQueueIndex::TRANSFER_QUEUE ? this->transferCommandPool : this->graphicsCommandPool;
    allocInfo.commandBufferCount = 1;

    vkAllocateCommandBuffers(this->device, &allocInfo, cmdBuffer);

    VkCommandBufferBeginInfo beginInfo{};
    beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
    beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;

    vkBeginCommandBuffer(*cmdBuffer, &beginInfo);
    return PINE_SUCCESS;
}

int PGraphicsContext::endCmdBuffer(VkCommandBuffer cmdBuffer, const PQueueIndex queueIndex, VkFence fence, const VkSemaphore *waitSemaphore, VkPipelineStageFlags *waitStageFlags, const VkSemaphore *signalSemaphore)
{
    vkEndCommandBuffer(cmdBuffer);

    VkSubmitInfo submitInfo{};
    submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
    submitInfo.commandBufferCount = 1;
    submitInfo.pCommandBuffers = &cmdBuffer;
    if (waitSemaphore != nullptr)
    {
        submitInfo.waitSemaphoreCount = 1;
        submitInfo.pWaitSemaphores = waitSemaphore;
        submitInfo.pWaitDstStageMask = waitStageFlags;
    }
    if (signalSemaphore != nullptr)
    {
        submitInfo.signalSemaphoreCount = 1;
        submitInfo.pSignalSemaphores = signalSemaphore;
    }

    vkQueueSubmit(this->queueFamilies[queueIndex].queue, 1, &submitInfo, fence);

    return PINE_SUCCESS;
}

int PGraphicsContext::transitionImageLayout(VkImage image, VkImageLayout oldLayout, VkImageLayout newLayout)
{
    VkCommandBuffer cmdBuffer;
    this->beginCmdBuffer(&cmdBuffer);

    VkImageMemoryBarrier barrier{};
    barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
    barrier.oldLayout = oldLayout;
    barrier.newLayout = newLayout;
    barrier.image = image;
    barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
    barrier.subresourceRange.baseMipLevel = 0;
    barrier.subresourceRange.levelCount = 1;
    barrier.subresourceRange.baseArrayLayer = 0;
    barrier.subresourceRange.layerCount = 1;

    if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL)
    {
        barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
        barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
        barrier.srcAccessMask = 0;
        barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;

        vkCmdPipelineBarrier(cmdBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, {}, 0, nullptr, 0, nullptr, 1, &barrier);

        this->endCmdBuffer(cmdBuffer, PQueueIndex::TRANSFER_QUEUE, this->syncFence);
        vkWaitForFences(this->device, 1, &this->syncFence, VK_TRUE, UINT64_MAX);
        vkResetFences(this->device, 1, &this->syncFence);
        vkFreeCommandBuffers(this->device, this->transferCommandPool, 1, &cmdBuffer);
    }
    else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
    {
        barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;

        if (this->queueFamilies[PQueueIndex::TRANSFER_QUEUE].index != this->queueFamilies[PQueueIndex::GRAPHICS_QUEUE].index)
        {
            barrier.dstAccessMask = VK_IMAGE_LAYOUT_UNDEFINED;
            barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
            barrier.srcQueueFamilyIndex = this->queueFamilies[PQueueIndex::TRANSFER_QUEUE].index;
            barrier.dstQueueFamilyIndex = this->queueFamilies[PQueueIndex::GRAPHICS_QUEUE].index;

            VkSemaphoreCreateInfo semaphoreInfo{};
            semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;

            VkSemaphore transferSemaphore;
            if (vkCreateSemaphore(this->device, &semaphoreInfo, this->allocator, &transferSemaphore) != VK_SUCCESS)
            {
                this->endCmdBuffer(cmdBuffer, PQueueIndex::TRANSFER_QUEUE);
                return PINE_ERROR_VULKAN_UNSUPPORTED_LAYER_TRANSITION;
            }

            vkCmdPipelineBarrier(cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, {}, 0, nullptr, 0, nullptr, 1, &barrier);

            this->endCmdBuffer(cmdBuffer, PQueueIndex::TRANSFER_QUEUE, VK_NULL_HANDLE, nullptr, nullptr, &transferSemaphore);

            barrier.srcAccessMask = VK_IMAGE_LAYOUT_UNDEFINED;
            barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
            VkCommandBuffer queueBuffer;
            this->beginCmdBuffer(&queueBuffer, PQueueIndex::GRAPHICS_QUEUE);
            vkCmdPipelineBarrier(queueBuffer, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, {}, 0, nullptr, 0, nullptr, 1, &barrier);

            VkPipelineStageFlags flags = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
            this->endCmdBuffer(queueBuffer, PQueueIndex::GRAPHICS_QUEUE, this->syncFence, &transferSemaphore, &flags, nullptr);
            vkWaitForFences(this->device, 1, &this->syncFence, VK_TRUE, UINT64_MAX);
            vkResetFences(this->device, 1, &this->syncFence);

            vkFreeCommandBuffers(this->device, this->transferCommandPool, 1, &cmdBuffer);
            vkFreeCommandBuffers(this->device, this->graphicsCommandPool, 1, &queueBuffer);

            vkDestroySemaphore(this->device, transferSemaphore, this->allocator);
        }
        else
        {
            barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
            barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;

            vkCmdPipelineBarrier(cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, {}, 0, nullptr, 0, nullptr, 1, &barrier);

            this->endCmdBuffer(cmdBuffer, PQueueIndex::TRANSFER_QUEUE, this->syncFence);
            vkWaitForFences(this->device, 1, &this->syncFence, VK_TRUE, UINT64_MAX);
            vkResetFences(this->device, 1, &this->syncFence);
            vkFreeCommandBuffers(this->device, this->transferCommandPool, 1, &cmdBuffer);
        }
    }
    else
    {
        // TODO: implement this
        this->endCmdBuffer(cmdBuffer, PQueueIndex::TRANSFER_QUEUE, this->syncFence);
        vkWaitForFences(this->device, 1, &this->syncFence, VK_TRUE, UINT64_MAX);
        vkResetFences(this->device, 1, &this->syncFence);
        vkFreeCommandBuffers(this->device, this->transferCommandPool, 1, &cmdBuffer);
        PApplication::getInstance()->getLogger()->error("Failed to transition image layout from 0x%X to 0x%X", oldLayout, newLayout);
        return PINE_ERROR_VULKAN_UNSUPPORTED_LAYER_TRANSITION;
    }

    return PINE_SUCCESS;
}

我现在增加了在结束命令缓冲区时向队列提交操作提供等待和信号信号量的功能。在transitionImageLayout()方法中,我现在使用它来创建一个信号量,当我还需要更改所有权时,当传输队列使用管道屏障完成时,就会发出信号。然后,我还在图形队列上创建了第二个命令缓冲区,它在运行相同的管道屏障命令之前等待信号量。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/67993790

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档