#c #vulkan
#c #vulkan
Вопрос:
Когда моя сцена обновляется (например, появляется новая модель), я перезаписываю свои буферы командной строки draw в новые командные буферы ( tempBuffer
) с помощью функции buildCommandBuffers
.
У меня есть массив drawCmdBuffers
, который используется в основной процедуре рисования.
Чтобы немного усложнить ситуацию, моя buildCommandBuffers
функция может вызываться из таймера в отдельном потоке, поэтому функция визуализации потенциально все еще зацикливается, пока происходит это обновление. Это связано с тем, что обновление моей сцены может занять некоторое время, и мне нужно заблокировать память сцены, пока я создаю буфер команд (это отдельная проблема .. но каждая добавленная сетка запускает обновление сцены, и мне нужно иметь возможность буферизировать эти обновления).
У меня есть забор для каждого командного буфера, поэтому, как только я записал в свой «tempBuffer», я делаю следующее:
VK_CHECK_RESULT(vkEndCommandBuffer(tempBuffers[i]));
// wait for this command buffer to finish (we might be re-recording)
if (imagesInFlight[_image_index] != VK_NULL_HANDLE)
{
vkWaitForFences(device, 1, amp;imagesInFlight[_image_index], VK_TRUE, UINT64_MAX);
}
// destroy in use command buffer
vkFreeCommandBuffers(device, cmdPool, 1, amp;drawCmdBuffers[i]);
drawCmdBuffers[i] = tempBuffers[i];
Безопасно ли это? Или есть что-то, что я могу сделать, чтобы остановить доступ к drawCmdBuffers[i]
нему во время его обновления?
Комментарии:
1. Похоже, у вас есть забор для каждого кадра, а не для каждого командного буфера, верно?
2. У меня есть буфер команд для каждого кадра, поэтому они одинаковы
3. Я опубликовал ответ ниже, но я бы также рекомендовал перепроверить, что вам нужно повторно использовать командные буферы таким образом. Большинство приложений Vulkan, включая все «профессиональные», о которых я знаю, просто перезаписывают буферы команд в каждом кадре. Если запись не требует большой обработки в вашем коде, фактические вызовы записи достаточно дешевы, поэтому нет большой пользы от повторного использования командных буферов. И повторное использование может добавить много сложностей.
4. запись в моем приложении происходит очень медленно, в нем десятки тысяч объектов. Перезапись каждого кадра нецелесообразна. Мне действительно нужно ускорить запись, но это еще одна проблема на другой день!
Ответ №1:
Похоже, вам нужна синхронизация CPU-CPU (обычно мьютекс) для доступа к imagesInFlight[]
массиву и drawCommandBuffers[]
/ или отдельным элементам внутри него. В противном случае вы могли бы ожидать блокировки для самого последнего кадра, в то время как другой поток отправляет командные буферы для следующего кадра — и в этом случае, даже когда ваше ожидание завершится, графический процессор все равно будет использовать командный буфер.
Предполагая, что вам действительно нужно повторно использовать буферы команд, а не просто перезаписывать их каждый кадр, одним из подходов было бы заставить buildCommandBuffers
поток просто создать список изменений drawCmdBuffers
. Что-то вроде:
Визуализировать поток:
for each frame:
acquire mutex for command buffer edit list
for (i, cmdbuf) in command buffer edit list:
push drawCmdBuffers[i] and most recent frame fence to a deferred-destroy queue
drawCmdBuffers[i] = cmdbuf;
clear edit list
release mutex
submit render commands for frame, using drawCmdBuffers
Поток обновления:
on_timer:
acquire mutex for command buffer edit list
while fence at front of deferred-destroy queue has signaled:
free command buffer at front of queue
pop queue
release mutex
for each command buffer that needs to be replaced:
record command buffer
acquire mutex for command buffer edit list
append (cmdbuf_index, cmdbuf) to edit list
release mutex
Таким образом, только поток визуализации обращается drawCmdBuffers
напрямую, поэтому его не нужно защищать. У вас просто есть очередь изменений, идущих из потока таймера в поток рендеринга, и очередь буферов команд для освобождения, идущих из потока рендеринга в поток таймера (или в какой-либо другой поток, который фактически ожидает на заборах, вместо того, чтобы просто опрашивать их по каждому таймеру). В любой данный момент (за исключением кратковременного удержания мьютекса) дескриптор буфера команд находится только в одном из этих списков.