Я пытаюсь перенести игровой движок OpenGL 3D на Vulkan. На игровой сцене имеется большое количество трехмерных объектов, и у каждого есть свои атрибуты (матрица модели, источники света и т. Д.), И объекты являются полностью динамическими, что означает, что некоторые трехмерные объекты могут входить, а другие могут быть удалены во время игры. , в OpenGL я сгруппировал атрибут 3D-объекта в единый буфер в шейдере (упрощенный код):
layout(std140, set = 0, binding = 0) uniform object_attrib
{
vec3 light_pos;
vec3 light_color;
mat4 model;
mat4 view_projection;
...
} params;
Сейчас я пытаюсь использовать этот единый буфер для всех трехмерных объектов игровой сцены, чтобы визуализировать их с помощью Vulkan.
Я использую один проход рендеринга Vulkan, в рамках begin-render-pass и end-render-pass, я использую цикл for-each для прохождения каждого 3D-объекта и выполнения следующих действий для их рендеринга. См. Псевдокод ниже.
vkBeginCommandBuffer(cmdBuffer, ...);
vkCmdBeginRenderPass(cmdBuffer, ...);
for(object3D obj : scene->objects)
{
// Step 1 - update object's uniform data by memcpy()
_updateUniformBuffer(obj);
// Step 2 - build draw command for this object
// bind vertex buffer, bind index buffer, bind pipeline, ..., draw
_buildDrawCommands(obj);
}
vkCmdEndRenderPass(cmdBuffer, ...);
vkEndCommandBuffer(cmdBuffer, ...);
vkQueueSubmit(...); // Finally, submit the commands to queue to render the scene
Очевидно, что мое решение не будет работать, поскольку все команды Vulkan в буфере выполняются на GPU только после вызова vkQueueSubmit (). Но вызов _updateUniformBuffer (obj) (by memcpy (...)) «перемежается» с записью команды, и он выполняется немедленно, и поэтому последовательность перепутана, и, наконец, каждый объект не получит свои собственные атрибуты.
Таким образом, может возникнуть вопрос, каково решение для Vulkan для правильного многократного обновления унифицированного буфера для каждого объекта в течение одного прохода рендеринга и обеспечения того, чтобы каждый объект получил свои правильные данные атрибута?
Прежде чем опубликовать этот вопрос, я попытался подумать о следующих решениях, но ни одно из них не кажется удачным:
- Использование render-pass-per-object и использования fence, чтобы убедиться, что один объект полностью отрисован, пока я не начну рендерить следующий. Если есть 1000 объектов, будет 1000 проходов рендеринга на кадр? Это невозможно.
- Могу ли я повторно отправлять буфер команд в течение одного прохода рендеринга? Я имею в виду, что я отправляю буфер команд сразу после того, как команда draw для одного объекта построена для визуализации объекта, использую fence, чтобы убедиться, что рендеринг завершен, затем перехожу к следующему объекту. Это будет один проход рендеринга и 1000 вызовов vkQueueSubmit ()
- Использование динамического унифицированного буфера, который создает огромный унифицированный буфер, содержит данные для 1000 объектов. Это сложно реализовать, так как номер объекта является динамическим.
- Использование константы push? Это также невозможно, поскольку максимальный размер данных составляет всего 128 байт.