如何正确调用命令缓冲区的 vulkan.hpp 构造函数?
How to properly call the vulkan.hpp constructor for command buffers?
我一直在更改一些 vulkan 代码以使用 vulkan.hpp 结构和方法。
因为我喜欢 RAII,所以我使用 Unique 包装器,不必明确处理资源管理。
到目前为止,我已经能够为我正在制作的每个包装方法创建 2 个版本,一个版本创建一个非唯一对象,另一个方法调用第一个,然后从第一个开始初始化一个唯一句柄。示例:
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]
= {<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()。
关于正确设置字段有什么建议吗?
您没有包括从 CreateCommandBuffersUnique
内部调用的 CreateCommandBuffers
函数的源代码或签名。但是,让我们假设它 returns 是 std::vector<vk::CommandBuffer>
的一种
看起来您正在循环处理这些内容并在 vk::UniqueCommandBuffer
个实例中手动包装它们。但是,您正在将 cb(这里假设为 vk::CommandBuffer
和 vk::Device
传递给 UniqueCommandBuffer
构造函数
u_command_buffers.push_back(vk::UniqueCommandBuffer(cb, device));
我实际上并不清楚它是如何编译的,除非 vk::Device
有一个 operator ()()
。无论如何,vk::UniqueCommandBuffer
的第二个参数应该是删除对象。
所以事实证明,所有 UniqueXXX
类型都派生自 UniqueHandle<>
,而 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
或复制它的行为,特别是创建一个 deleter 对象或 lambda 并将其作为第二个参数传递给UniqueCommandBuffer
构造函数。
根据我对 UniqueHandleTraits
的检查,看起来您只需更改此行即可修复您的代码:
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);
}
我一直在更改一些 vulkan 代码以使用 vulkan.hpp 结构和方法。
因为我喜欢 RAII,所以我使用 Unique 包装器,不必明确处理资源管理。
到目前为止,我已经能够为我正在制作的每个包装方法创建 2 个版本,一个版本创建一个非唯一对象,另一个方法调用第一个,然后从第一个开始初始化一个唯一句柄。示例:
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]
= {<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()。
关于正确设置字段有什么建议吗?
您没有包括从 CreateCommandBuffersUnique
内部调用的 CreateCommandBuffers
函数的源代码或签名。但是,让我们假设它 returns 是 std::vector<vk::CommandBuffer>
看起来您正在循环处理这些内容并在 vk::UniqueCommandBuffer
个实例中手动包装它们。但是,您正在将 cb(这里假设为 vk::CommandBuffer
和 vk::Device
传递给 UniqueCommandBuffer
构造函数
u_command_buffers.push_back(vk::UniqueCommandBuffer(cb, device));
我实际上并不清楚它是如何编译的,除非 vk::Device
有一个 operator ()()
。无论如何,vk::UniqueCommandBuffer
的第二个参数应该是删除对象。
所以事实证明,所有 UniqueXXX
类型都派生自 UniqueHandle<>
,而 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
或复制它的行为,特别是创建一个 deleter 对象或 lambda 并将其作为第二个参数传递给UniqueCommandBuffer
构造函数。
根据我对 UniqueHandleTraits
的检查,看起来您只需更改此行即可修复您的代码:
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);
}