Синхронизировать буфер вершин в Vulkan? - PullRequest
0 голосов
/ 12 февраля 2019

У меня есть буфер вершин, который хранится в памяти устройства, а также буфер, видимый хосту и связанный с хостом.

Для записи в буфер вершин на стороне хоста я сопоставляю его, memcpy иunmap память устройства.

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

В настоящее время я записываю один раз в буфер вершин при запуске программы.

Затем буфер вершин остаетсято же самое во время цикла.

Я хотел бы изменить буфер вершин между каждым кадром со стороны хоста.

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

Для каждого кадра:

  1. Я жду на заборе.

  2. Я сбросил забор.

  3. Семафор получения сигналов № 1.

  4. Отправка очереди ожидает на семафоре #1 и сигнализирует семафор # 2 и сигнализирует об ограждении.

  5. Настоящее ожидает семафор # 2

Где в этом месте находится правильное место?поставить карту на стороне хоста / memcpy / unmap и как мне правильно синхронизировать ее с чтением устройства?

1 Ответ

0 голосов
/ 12 февраля 2019

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

Вы должны по крайней мере дважды буферизовать вещи.Если вы меняете данные вершин в каждом кадре, вам следует выделить достаточно памяти для хранения двух копий этих данных.Нет необходимости делать многократные выделения или даже делать несколько VkBuffer с (просто увеличьте выделение и буферы, затем выберите область хранения, которую вы будете использовать при ее привязке).Пока одна область памяти читается командами графического процессора, вы записываете в другую.

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

Но поскольку вы выполняете двойную буферизацию, как это,забор для памяти, в которую вы собираетесь записать, , а не забор для пакета, который вы отправили в последнем кадре.Это партия, которую вы передали в кадр до .Поскольку прошло некоторое время с тех пор, как графический процессор получил эту операцию, гораздо менее вероятно, что ЦПУ действительно придется ждать.То есть забор, надо надеяться, уже установлен.

Теперь вы не должны делать буквальный vkWaitForFences на этом заборе.Вы должны проверить, установлено ли оно, а если нет, пойти и сделать что-то еще полезное со своим временем.Но если у вас нет ничего полезного, чем вы могли бы заниматься, тогда, вероятно, все в порядке (вместо того, чтобы сидеть и крутиться на тесте).

Как только забор установлен, вы знаете, что можете свободно записывать в память.


Как узнать, что память, которую я записал с помощью memcpy, закончила отправку на устройство до того, как она будет прочитана проходом рендеринга?

Вы знаете, потому что память связна.Вот что VK_MEMORY_PROPERTY_HOST_COHERENT_BIT означает в этом контексте: изменения хоста в памяти устройства видны для графического процессора без необходимости явных операций видимости, и наоборот.

Ну ... почти.

Если вы не хотите использовать какую-либо синхронизацию, вы должны вызвать vkQueueSubmit для пакета чтения после того, как вы закончили модифицировать память на ЦП.Если их вызывают в неправильном порядке, вам понадобится барьер памяти.Например, у вас может быть некоторая часть пакетного ожидания для события, установленного хостом (через vkSetEvent), который сообщает графическому процессору, когда вы закончили писать.И, следовательно, вы можете отправить этот пакет перед выполнением записи в память.Но в этом случае вызов vkCmdWaitEvents должен включать маску этапа источника HOST (так как именно он устанавливает событие), и он должен иметь барьер памяти, флаг доступа к источнику которого также включает HOST_WRITE (так как это пишет ктов память).

Но в большинстве случаев проще просто записать в память перед отправкой пакета.Таким образом, вам не нужно использовать синхронизацию хоста / события.

...