Как правильно вызвать конструктор vulkan.hpp для командных буферов? - PullRequest
0 голосов
/ 28 апреля 2019

Я изменил некоторый код vulkan для использования структур и методов vulkan.hpp.

Поскольку мне нравятся RAII, я использую уникальные обертки, чтобы не пришлось явно обрабатывать управление ресурсами.

До сих пор мне удавалось создать 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]
$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}}

Я проверил, и хотя есть команда Buffer.getPool (), там нет setPool ().

Есть предложения по правильной настройке поля?

Ответы [ 2 ]

1 голос
/ 28 апреля 2019

Вы не включили источник или подпись функции CreateCommandBuffers, которую вы вызываете из CreateCommandBuffersUnique. Однако предположим, что он возвращает тип 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<>, который, в свою очередь, происходит от 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, либо повторить его поведение, в частности, создайте delete object или lambda и передайте его в качестве второго параметра в UniqueCommandBuffer ctor.

Судя по моему исследованию UniqueHandleTraits, похоже, что вы сможете исправить свой код, просто изменив эту строку:

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

до

    u_framebuffers.push_back(vk::UniqueFramebuffer(fb, { device, command_pool }));
0 голосов
/ 28 апреля 2019

После игры я нашел решение, которое решает проблему, хотя я бы предпочел сделать что-то более близкое к примеру, который я предоставил с кадровыми буферами:

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);
}
...