簡體   English   中英

Vulkan 驗證錯誤表明我的圖像布局錯誤,但只有兩次我第一次呈現來自交換鏈的圖像

[英]Vulkan validation errors states my images are in the wrong layout, but only the two first times I present images from the swapchain

我一直在按照本指南使用 Vulkan-Hpp 中的 raii 標頭在 Vulkan 中設置一些簡單的渲染。 我跳過了圖形管道基礎部分(渲染通道章節除外),只是為了看看我是否只能讓渲染通道工作並呈現來自交換鏈的圖像。

我現在可以將當前交換鏈圖像清除為某種顏色並呈現它。 但是,對於我嘗試呈現的前兩幀,這失敗了,之后它運行順利,沒有任何驗證錯誤。 我完全不知道為什么會發生這種情況,所以我只想詳細說明我所知道的和我嘗試過的,希望有人可能在這里知道答案。

我在前兩幀得到的錯誤如下:

Validation Error: [ VUID-VkPresentInfoKHR-pImageIndices-01296 ] Object 0: handle = 0x1f5d50ee1e0, type = VK_OBJECT_TYPE_QUEUE; | MessageID = 0xc7aabc16 | vkQueuePresentKHR(): pSwapchains[0] images passed to present must be in layout VK_IMAGE_LAYOUT_PRESENT_SRC_KHR or VK_IMAGE_LAYOUT_SHARED_PRESENT_KHR but is in VK_IMAGE_LAYOUT_UNDEFINED. The Vulkan spec states: Each element of pImageIndices must be the index of a presentable image acquired from the swapchain specified by the corresponding element of the pSwapchains array, and the presented image subresource must be in the VK_IMAGE_LAYOUT_PRESENT_SRC_KHR layout at the time the operation is executed on a VkDevice (https://github.com/KhronosGroup/Vulkan-Docs/search?q=)VUID-VkPresentInfoKHR-pImageIndices-01296)

這使得前兩幀或其他東西看起來可能存在同步問題。 由於我仍在使用 Vulkan 進行早期測試,因此我只是使用device.waitIdle()而不是與信號量和柵欄正確同步。 我知道使用waitIdle是一個緩慢的解決方案,但我認為它至少可以保持同步,所以我不確定這是否是一個同步問題。

我的交換鏈有 3 個圖像,所以如果在第一輪通過圖像呈現圖像存在問題,那么我應該得到三個錯誤......

presentKHR function 即使在前兩幀也返回vk::Result::Success 我也嘗試關閉驗證層,當我這樣做時,前兩幀能夠呈現,所以它可能是驗證層中的錯誤?

我的一些初始化代碼:

// After swapchain creation

auto images = m_swapchain.getImages();

for (auto& image : images) {
    m_images.emplace(image, createImageView(image));
}

m_renderPass = createRenderPass();

m_frameBuffers.reserve(m_images.size());

for (auto& [image, imageView] : m_images) {
    m_frameBuffers.push_back(createFrameBuffer(imageView));
}

auto [result, imageIndex] = m_swapchain.acquireNextImage(
            std::numeric_limits<uint64_t>().max(),
            *m_imageAvailableSemaphore
        );
// I use a semaphore here because the Vulkan spec states that I must use a semaphore or fence here

m_imageIndex = imageIndex;


// Functions called above

vk::raii::ImageView Swapchain::createImageView(const vk::Image& image) const {
    try {
        return m_context.getDevice().createImageView(
            vk::ImageViewCreateInfo{
                .flags            = {},
                .image            = image,
                .viewType         = vk::ImageViewType::e2D,
                .format           = m_surfaceFormat.format,
                .components       = vk::ComponentMapping{
                    .r = vk::ComponentSwizzle::eIdentity,
                    .g = vk::ComponentSwizzle::eIdentity,
                    .b = vk::ComponentSwizzle::eIdentity,
                    .a = vk::ComponentSwizzle::eIdentity
                },
                .subresourceRange = vk::ImageSubresourceRange{
                    .aspectMask     = vk::ImageAspectFlagBits::eColor,
                    .baseMipLevel   = 0,
                    .levelCount     = 1,
                    .baseArrayLayer = 0,
                    .layerCount     = 1
                }
            }
        );
    }
    catch (const std::exception& e) {
        // Error handling...
    }
}

vk::raii::RenderPass Swapchain::createRenderPass() const {
    auto attachments = std::array{
        vk::AttachmentDescription{
            .flags          = {},
            .format         = m_surfaceFormat.format,
            .samples        = vk::SampleCountFlagBits::e1,
            .loadOp         = vk::AttachmentLoadOp::eClear,
            .storeOp        = vk::AttachmentStoreOp::eStore,
            .stencilLoadOp  = vk::AttachmentLoadOp::eDontCare,
            .stencilStoreOp = vk::AttachmentStoreOp::eDontCare,
            .initialLayout  = vk::ImageLayout::eUndefined,
            .finalLayout    = vk::ImageLayout::ePresentSrcKHR
        }
    };

    auto attachmentReferences = std::array{
        vk::AttachmentReference{
            .attachment = 0,
            .layout     = vk::ImageLayout::eColorAttachmentOptimal
        }
    };

    auto subpasses = std::array{
        vk::SubpassDescription{
            .flags                   = {},
            .pipelineBindPoint       = vk::PipelineBindPoint::eGraphics,
            .inputAttachmentCount    = 0,
            .pInputAttachments       = nullptr,
            .colorAttachmentCount    = static_cast<uint32_t>(attachmentReferences.size()),
            .pColorAttachments       = attachmentReferences.data(),
            .pResolveAttachments     = nullptr,
            .pDepthStencilAttachment = nullptr,
            .preserveAttachmentCount = 0,
            .pPreserveAttachments    = nullptr
        }
    };

    try {
        return m_context.getDevice().createRenderPass(
            vk::RenderPassCreateInfo{
                .flags           = {},
                .attachmentCount = static_cast<uint32_t>(attachments.size()),
                .pAttachments    = attachments.data(),
                .subpassCount    = static_cast<uint32_t>(subpasses.size()),
                .pSubpasses      = subpasses.data(),
                .dependencyCount = 0,
                .pDependencies   = nullptr
            }
        );
    }
    catch (const std::exception& e) {
        // Error handling...
    }
}

vk::raii::Framebuffer Swapchain::createFrameBuffer(const vk::raii::ImageView& imageView) const {
    try {
        return m_context.getDevice().createFramebuffer(
            vk::FramebufferCreateInfo{
                .flags           = {},
                .renderPass      = *m_renderPass,
                .attachmentCount = 1,
                .pAttachments    = &*imageView,
                .width           = m_imageExtent.width,
                .height          = m_imageExtent.height,
                .layers          = 1
            }
        );
    }
    catch (const std::exception& e) {
        // Error handling...
    }
}

每幀執行的渲染代碼:

// The actual render function called every frame

void Renderer::render() {
    m_context->recordCommands(
        [&]() {
            m_swapchain->beginRenderPassCommand(0.125f, 0.125f, 0.125f);
            m_swapchain->endRenderPassCommand();
        }
    );

    m_context->submitRecording();

    m_swapchain->swap();
}

void GraphicsContext::recordCommands(const Application::Recording& recording) {
    m_device.waitIdle();

    m_commandBuffer.reset();
    m_commandBuffer.begin(
        vk::CommandBufferBeginInfo{
            .flags            = {},
            .pInheritanceInfo = {}
        }
    );

    recording();

    m_commandBuffer.end();
}

void Swapchain::beginRenderPassCommand(
        float clearColorRed,
        float clearColorGreen,
        float clearColorBlue
) {
    auto clearValues = std::array{
        vk::ClearValue(
            vk::ClearColorValue(
                std::array{
                    clearColorRed,
                    clearColorGreen,
                    clearColorBlue,
                    1.0f
                }
            )
        )
    };

    m_context.getCommandBuffer().beginRenderPass(
        vk::RenderPassBeginInfo{
            .renderPass      = *m_renderPass,
            .framebuffer     = *m_frameBuffers[m_imageIndex],
            .renderArea      = vk::Rect2D{
                .offset = vk::Offset2D{
                    .x = 0,
                    .y = 0
                },
                .extent = m_imageExtent
            },
            .clearValueCount = static_cast<uint32_t>(clearValues.size()),
            .pClearValues    = clearValues.data()
        },
        vk::SubpassContents::eInline
    );
}

void Swapchain::endRenderPassCommand() {
    m_context.getCommandBuffer().endRenderPass();
}

void GraphicsContext::submitRecording() {
    m_device.waitIdle();

    m_graphicsQueue.submit(
        vk::SubmitInfo{
            .waitSemaphoreCount   = 0,
            .pWaitSemaphores      = nullptr,
            .pWaitDstStageMask    = nullptr,
            .commandBufferCount   = 1,
            .pCommandBuffers      = &*m_commandBuffer,
            .signalSemaphoreCount = 0,
            .pSignalSemaphores    = nullptr
        }
    );
}

void Swapchain::swap() {
    m_context.getDevice().waitIdle();

    auto presentResult = m_context.getPresentQueue().presentKHR(
        vk::PresentInfoKHR{
            .waitSemaphoreCount = 0,
            .pWaitSemaphores    = nullptr,
            .swapchainCount     = 1,
            .pSwapchains        = &*m_swapchain,
            .pImageIndices      = &m_imageIndex,
            .pResults           = nullptr
        }
    );

    m_context.getDevice().waitIdle();

    auto [result, imageIndex] = m_swapchain.acquireNextImage(
        std::numeric_limits<uint64_t>().max(),
        *m_imageAvailableSemaphore
    );

    m_imageIndex = imageIndex;
}

在特定情況或顯式同步之外,GPU 上的操作以任意順序執行。

您的圖形提交和隊列演示之間沒有同步。 因此,無論您何時發布它們,它們都可以按照實現所需的任何順序執行。

但是,由於圖形操作作用於演示操作使用的 object,因此存在事實上的依賴關系。 圖形操作必須先行動。 但是您沒有實際的同步來強制執行這種依賴關系。

因此驗證錯誤。 您需要確保隊列呈現發生在渲染操作之后。

我現在已經確認此問題是由驗證層中的錯誤引起的。 這是 Github 上的問題: https://github.com/KhronosGroup/Vulkan-ValidationLayers/issues/4422

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM