繁体   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