如何正确调用vulkan.hpp构造函数来获取命令缓冲区?

问题描述 投票:0回答:2

我一直在改变一些vulkan代码来使用vulkan.hpp结构和方法。

由于我喜欢RAII,因此我使用Unique包装器,而不必显式处理资源管理。

到目前为止,我已经能够创建我正在制作的每个包装方法的两个版本,一个版本创建一个非唯一对象,另一个方法调用第一个,然后从第一个初始化一个唯一的句柄。例:

vector<vk::UniqueFramebuffer> CreateFramebuffersUnique(vk::SurfaceKHR surface,
    vk::PhysicalDevice phys_device, vk::Device device, vk::RenderPass render_pass,
    vector<vk::ImageView> image_views)
{
    auto framebuffers =
        CreateFramebuffers(surface, phys_device, device, render_pass, image_views);
    vector<vk::UniqueFramebuffer> u_framebuffers;
    for(auto &fb : framebuffers)
        u_framebuffers.push_back(vk::UniqueFramebuffer(fb, device));

    return u_framebuffers;
}

上面的方法创建了一个帧缓冲区数组,然后在返回之前将每个帧缓冲区重新初始化为唯一的帧缓冲区。

我试着用命令缓冲区做同样的事情:

vector<vk::UniqueCommandBuffer> CreateCommandBuffersUnique(vk::SurfaceKHR &surface,
    vk::PhysicalDevice &phys_device, vk::Device &device, vk::RenderPass &render_pass,
    vk::Pipeline &graphics_pipeline, vk::CommandPool &command_pool,
    vector<vk::Framebuffer> &framebuffers)
{
    auto command_buffers = CreateCommandBuffers(surface, phys_device, device, render_pass,
        graphics_pipeline, command_pool, framebuffers);

    vector<vk::UniqueCommandBuffer> u_command_buffers;
    for(auto &cb : command_buffers)
        u_command_buffers.push_back(vk::UniqueCommandBuffer(cb, device));

    return u_command_buffers;
}

以上技术上有效,但在程序终止时,验证层会在重新分配时抱怨错误:

validation layer: vkFreeCommandBuffers: required parameter commandPool specified as VK_NULL_HANDLE
UNASSIGNED-GeneralParameterError-RequiredParameter
4096
validation layer: Invalid CommandPool Object 0x0. The Vulkan spec states: commandPool must be a valid VkCommandPool handle (https://www.khronos.org/registry/vulkan/specs/1.1-extensions/html/vkspec.html#VUID-vkFreeCommandBuffers-commandPool-parameter)
VUID-vkFreeCommandBuffers-commandPool-parameter

发生这种情况是因为未正确设置唯一句柄的命令池字段:

(gdb) p t_command_buffers[0]
$6 = {<vk::PoolFree<vk::Device, vk::CommandPool, vk::DispatchLoaderStatic>> = {m_owner = {m_device = 0x555555ec14e0}, m_pool = {m_commandPool = 0x0},
    m_dispatch = 0x7fffffffe2f7}, m_value = {m_commandBuffer = 0x555555fe6390}}

我查了一下,虽然有一个commandBuffer.getPool(),但是没有setPool()。

有关正确设置该字段的任何建议吗?

c++ resources gpu vulkan raii
2个回答
1
投票

你没有包括CreateCommandBuffers内部调用的CreateCommandBuffersUnique函数的来源或签名。但是,我们假设它返回一种std::vector<vk::CommandBuffer>

看起来你正在循环这些并在vk::UniqueCommandBuffer实例中手动包装它们。但是,你传递cb(假设这里是vk::CommandBuffervk::Device到这里的UniqueCommandBuffer构造函数)

u_command_buffers.push_back(vk::UniqueCommandBuffer(cb, device));

除非vk::Deviceoperator ()(),否则我实际上并不清楚如何编译。无论如何,vk::UniqueCommandBuffer的第二个参数应该是一个删除对象。

事实证明,所有UniqueXXX类型都来自UniqueHandle<>,而UniqueHandleTraits<...>又来自deleter,它专门针对每种类型,以便给它一个默认的UniqueHandleTraits<...>类型。大多数ObjectDestroy<...>专业使用Device模板,因为大多数对象只需要创建UniqueHandleTraits<CommandBuffer>来摧毁它们。然而,UniqueHandleTraits<DescriptorSet>PoolFree<...>专业使用vk::UniqueCommandBuffer(cb, device)); 作为他们的删除者。

因此,您的使用

vk::UniqueCommandBuffer(cb, 
    vk::PoolFree<Device, CommandPool,Dispatch> { device, {}, {} }
)

隐含地变成了

{}

device之后的那个vk::Device::allocateCommandBuffersUnique是应该指定游泳池的地方。

请参阅PoolFree<Device,CommandPool,Dispatch> deleter( *this, allocateInfo.commandPool, d ); for ( size_t i=0 ; i<allocateInfo.commandBufferCount ; i++ ) { commandBuffers.push_back( UniqueCommandBuffer( buffer[i], deleter ) ); } 的相应代码

vk::Device::allocateCommandBuffersUnique

要修复代码,您需要使用UniqueCommandBuffer或复制它的行为,特别是创建一个删除对象或lambda,并将其作为第二个参数传递给UniqueHandleTraits ctor。

基于我对 u_framebuffers.push_back(vk::UniqueFramebuffer(fb, device)); 的检查,看起来您只需更改此行就可以修复代码:

    u_framebuffers.push_back(vk::UniqueFramebuffer(fb, { device, command_pool }));

vector<vk::UniqueCommandBuffer> CreateCommandBuffersUnique(vk::SurfaceKHR &surface,
    vk::PhysicalDevice &phys_device, vk::Device &device, vk::RenderPass &render_pass,
    vk::Pipeline &graphics_pipeline, vk::CommandPool &command_pool,
    vector<vk::Framebuffer> &framebuffers)
{
    vk::CommandBufferAllocateInfo alloc_info(command_pool,
        vk::CommandBufferLevel::ePrimary, framebuffers.size());
    auto[result, u_command_buffers] = device.allocateCommandBuffersUnique(alloc_info);
    if (result != vk::Result::eSuccess)
        Log::RecordLogError("Failed to allocate command buffers!");

    auto command_buffers = CreateCommandBuffers(surface, phys_device, device, render_pass,
        graphics_pipeline, command_pool, framebuffers);

    for(auto &cb : command_buffers)
    {
        u_command_buffers[&cb - &command_buffers[0]].reset(cb);
    }

    return std::move(u_command_buffers);
}

0
投票

在玩游戏时我发现了一个解决问题的解决方案,虽然我更喜欢用我提供的框架缓冲区的例子做更接近的事情:

qazxswpoi
© www.soinside.com 2019 - 2024. All rights reserved.