Подтвердить что ты не робот

Почему VkFence необходим для каждого командного буфера swapchain, когда он уже использует семафоры?

Я использую ровно 3 изображения для swapchain и один VkCommandBuffer (CB) для swapchain изображения. Синхронизация GPU выполняется с двумя семафорами, одна для presentation_finished и одна для rendering_finished. Текущий режим VK_PRESENT_MODE_MAILBOX_KHR (быстрый обзор).

Теперь, когда я запускаю свой пример, не дожидаясь каких-либо CB-ограждений, уровни проверки сообщают об этой ошибке, как только любое swapchain-изображение используется во второй раз:

Вызов vkBeginCommandBuffer() активного CB до его завершения. Перед этим вызовом вы должны проверить CB-забор.

На первый взгляд это кажется мне удобным, так как обработка команд из ЦБ может быть просто еще не закончена. Но чем больше я думаю об этом, тем больше я пришел к выводу, что этого не должно быть вообще.

В моем понимании, когда vkAcquireImageKHR возвращает определенный индекс изображения, он подразумевает, что возвращаемое изображение должно быть завершено рендерингом.

Это потому, что я передаю семафор rendering_finished на vkQueueSubmit, который должен быть передан при завершении рендеринга, и vkQueuePresentKHR, чтобы дождаться, пока он не станет сигналом перед представлением изображения.

В спецификации VkQueuePresentInfoKHR указано:

pWaitSemaphores, если не VK_NULL_HANDLE, представляет собой массив объектов VkSemaphore с элементами waitSemaphoreCount и указывает семафоры, ожидающие до выдачи настоящего запроса

Значение: я никогда не буду представлять изображение, которое не завершило рендеринг, и, следовательно, связанный с ним ЦБ не может быть использован больше, как только изображение будет представлено.

Второй семафор presentation_finished сигнализируется vkAqcuireImageKHR и передается в тот же vkQueueSubmit (для запуска рендеринга). Это означает, что рендеринг любого изображения начнется не раньше, чем разрешено движком презентации.

В заключение: настоящий запрос от vkQueuePresentKHR не выдается до того, как рендеринг изображения будет завершен, а vkAcquireImageKHR будет блокироваться до тех пор, пока изображение не будет доступно, и также никогда не вернет полученные изображения.

Что мне не хватает, что делает забор необходимым?


Я включил минимальный пример кода, содержащий только концептуально важные части, чтобы проиллюстрировать проблему.

VkImage[] swapchain_images;
VkCommandBuffer[] command_buffers;

VkSemaphore rendering_finished;
VkSemaphore presentation_finished;

void RenderLoop()
{
    /* Acquire an image from the swapchain. Block until one is available.
       Signal presentation_finished when we are allowed to render into the image */
    int index;
    vkAcquireImageKHR(device, swapchain, UINT64_MAX, presentation_finished, nullptr, &index);

    /* (...) Frambuffer creation, etc. */

    /* Begin CB: The command pool is flagged to reset the command buffer on reuse */
    VkCommandBuffer cb = command_buffers[index];
    vkBeginCommandBuffer(cb, ...);

    /* (...) Trivial rendering of a single color image */

    /* End CB */
    vkEndCommandBuffer(cb);


    /* Queue the rendering and wait for presentation_finished.
       When rendering is finished, signal rendering_finished.

       The VkSubmitInfo will have these important members set among others:
       .pWaitSemaphores = &presentation_finished;
       .pSignalSemaphores = &rendering_finished;
    */
    vkQueueSubmit(render_queue, &submit_info);

    /* Submit the presentation request as soon as the rendering_finished
       semaphore gets signalled

       The VkPresentInfoKHR will have these important members set among others:
       .pWaitSemaphores = &rendering_finished;
    */
    vkQueuePresentKHR(present_queue, &present_info);
}

Вставка забора при отправке CB в очередь рендеринга и ожидание его перед использованием этого CB снова, очевидно, устраняет проблему, но, как объяснено, кажется излишним.

4b9b3361

Ответ 1

vkAcquireNextImageKHR разрешено возвращать изображение, которое все еще является пунктом назначения и/или источником текущих асинхронных операций. Это означает, что у вас нет гарантии, что буфер команд доступен во время повторного использования. Было бы правильным вставить дополнительные, отдельные командные буферы для записи на полученное изображение, если эти команды настроены на ожидание семафора presentation_finished; но для безопасного повторного использования этого командного буфера вы должны ждать на заводе, прошедшем в vkQueueSubmit.

См. раздел 29.6. WSI Swapchain в спецификации Vulkan с расширениями KHR:

Приложение может получить использование презентабельного изображения с помощью vkAcquireNextImageKHR. После приобретения презентабельного изображения и перед его модификацией приложение должно использовать примитив синхронизации, чтобы убедиться, что движок презентации закончил чтение с изображения. Затем приложение может преобразовать макет изображения, команды рендеринга очереди это и т.д. Наконец, приложение представляет изображение с помощью vkQueuePresentKHR, который освобождает получение изображения.

См. также эти примечания для vkAcquireNextImageKHR

При успешном завершении vkAcquireNextImageKHR получает презентабельное изображение, которое приложение может использовать, и устанавливает pImageIndex в индекс этого изображения. Механизм представления может не закончить считывание с изображения во время его приобретения, поэтому приложение должно использовать семафор и/или ограждение, чтобы гарантировать, что макет и содержимое изображения не изменены до тех пор, пока показания движка презентации завершены.

[...]

Как упоминалось выше, механизм представления может быть асинхронным относительно приложения и/или логического устройства. vkAcquireNextImageKHR может вернуться, как только он сможет определить, какое изображение будет получено, и может гарантировать, что семафор и забор будут сигнализироваться механизмом представления; и не может успешно вернуться раньше. Приложение использует таймаут, чтобы указать, как долго vkAcquireNextImageKHR ожидает, что изображение станет приобретенным.

Это показывает, что vkAcquireNextImageKHR не требуется блокировать операцию презентации, и транзитно не требуется блокировать графическую команду, которую сама операция представления ожидает.