Vulkan - 1 единый буфер, N мешей - динамический VkDeviceMemory - PullRequest
0 голосов
/ 01 ноября 2018

Предположим, я рендеринг простых кубов в случайных позициях.

Имея 3 из них в качестве начального числа кубов, приложение получает дескриптор VkBuffer и связывает его с VkDeviceMemory для последовательного хранения в нем матриц моделей всех кубов, к которым впоследствии осуществляется доступ. шейдером через набор дескрипторов. У VkDeviceMemory достаточно памяти для этих 3 кубов.

Что я хочу сделать, так это то, что каждый раз, когда пользователь нажимает клавишу, где-то должен появляться новый куб. Мой вопрос: как мне изменить размер этой памяти? Не могли бы вы дать обзор шагов, которые я должен пройти?

Я понимаю, что могу использовать отдельные VkBuffer / VkDeviceMemory для каждого куба, но я не хочу этого делать. Везде, где я читаю, говорится, что это своего рода анти-паттерн.

Должен ли я просто отказаться от VkDeviceMemory, выделить новый с правильным размером и назвать его днем? А как насчет наборов дескрипторов, им нужна какая-то особая обработка?

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

РЕДАКТИРОВАТЬ: я также понимаю, что выделять один маленький кусок за раз, это плохая идея. Что меня интересует, так это само перераспределение и его последствия.

Ответы [ 3 ]

0 голосов
/ 02 ноября 2018

Согласитесь с тем, что сказал Джерико, но есть дополнительная опция, которая заключается в том, чтобы не ограничивать себя одним VkBuffer.

Как правило, вы хотите думать о VkDeviceMemory в кратных страницах памяти (4 КБ), а в некоторых устройствах даже например, кратно 64 КБ. Даже если вы выделите нечто меньшее, чем это, вы, скорее всего, будете использовать столько памяти, поскольку ядро ​​ОС не может дать вам вещи меньшими порциями.

Таким образом, если каждому преобразованию требуется 64 B, то вы можете просто планировать выделение в виде кусков 1k преобразований. Выделите одну пару 64 КиБ VkBuffer / VkDeviceMemory, и, когда она заполнится, выделите вторую пару, когда она заполнится, выделите третью пару и т. Д.

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

Если вы сделаете это, то каждый раз, когда вы выделяете новый блок, вам нужен новый набор дескрипторов для него. Создайте это в то же время, а затем между отрисовками просто свяжите набор дескрипторов для буфера, который вы собираетесь использовать.

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

0 голосов
/ 14 ноября 2018

Чтобы ответить на вопрос «как перераспределить и начать использовать новую память», не обращая внимания на вопросы о стратегии распределения: перераспределение ничем не отличается от выделения новой вещи, заполнения ее данными, которые вы хотите, и затем начала использовать их , Таким образом, вам необходимо выполнить все те же шаги, что и для первоначального распределения.

Следует помнить, что большинство объектов, на которые ссылаются в буфере команд, нельзя безопасно изменить, пока этот буфер команд не завершит выполнение. Как правило, вы будете записывать команды для кадра N + 1, пока команды для кадра N все еще выполняются. Таким образом, вы хотите избежать обновления изменяемых объектов (например, наборов дескрипторов), чтобы начать использовать новое распределение; вместо этого вы хотите новый набор дескрипторов.

Итак, вот список вещей, которые вам нужны:

  1. Сам буфер: VkBuffer и VkDeviceMemory. Если вы выделили дополнительное пространство в текущем VkDeviceMemory, так что оно достаточно велико как для старого, так и для нового VkBuffer s, тогда вам не нужен новый объект VkDeviceMemory. В любом случае создайте новый VkBuffer нужного размера и привяжите его к неиспользованной части объекта VkDeviceMemory.

  2. Способ привязки буфера к конвейеру: a VkDescriptorSet. Вы будете использовать тот же макет набора дескрипторов, что и раньше, это не изменится. Поэтому выделите новый набор дескрипторов из вашего пула дескрипторов и используйте vkUpdateDescriptorSet, чтобы установить дескриптор буфера, чтобы он указывал на ваш новый буфер (вы также можете скопировать другие дескрипторы из вашего предыдущего набора дескрипторов, если их не нужно менять).

  3. Наконец, при построении буфера команд для кадра, в котором вы хотите использовать новый буфер, передайте новый дескриптор, установленный в vkCmdBindDescriptorSets вместо старого.

  4. В конце концов, после того как все буферы команд, которые использовали старый набор буферов и дескрипторов, были завершены, вы можете освободить набор буферов и дескрипторов. Для набора дескрипторов вы можете просто вернуть его в пул или оставить его и использовать повторно в следующий раз, когда вам потребуется перераспределить буфер. Память устройства, используемая старым буфером, затем может быть освобождена, или вы можете оставить ее для дальнейшего использования.

0 голосов
/ 01 ноября 2018

VkDeviceMemory имеет достаточно памяти для этих 3 кубов.

Почему? Если вы хотите поддерживать произвольное количество кубов, то вам следует управлять своей памятью так, чтобы вы могли иметь дело с изменениями в количестве таких вещей, как преобразования с минимальным количеством перераспределений.

Должен ли я просто отказаться от VkDeviceMemory, выделить новый с правильным размером и назвать его днем?

Для структур, в которых число является переменным, вы должны выполнить распределение для своих текущих потребностей, а также для вероятных будущих потребностей. В то же время вы не хотите чрезмерно перераспределять. Для таких вещей, как преобразования, которые очень малы по отношению к объему памяти, обычно доступному на современных графических процессорах, вполне разумно начинать с выделения, скажем, 1024 уникальных преобразований. Если это простое преобразование mat4, оно потребляет всего 512 байт, поэтому 1k из них будет занимать только половину мегабайта памяти. Это тривиально по сравнению с типичной загрузкой памяти для текстур или даже сложных сеток.

Если вы на самом деле потребляете их все, вы можете перераспределить больше. В зависимости от вашего вероятного шаблона использования вы можете перераспределить с фиксированным размером блока, например, 1024, или сделать экспоненциально увеличивающееся выделение, например, всегда выделяя в два раза больше вашей текущей стороны. Вы можете зайти в Google vector reallocation для получения дополнительной информации о стратегиях работы с непрерывной памятью, которая может выйти за пределы ее нынешних границ. Вот статья по теме

...