воскресенье, 6 марта 2016 г.

Vulkan API, Synchronization primitives, Fences.

Одним из ключевых изменений, произошедших в Вулкане, стало ручное управление потоками команд, что с одной стороны - позволило существенно снизить нагрузку на CPU, с другой - требует понимания принципов многопоточного программирования. Важнейшим инструментов многопоточного програмирования являются примитивы синхронизации. Вулкан предоставляет 4 типа таких примитивов, в этой части мы рассмотрим первый из них - Fences.



Synchronization primitives, Fences.


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

Вот представленные в Вулкане типы примитивов синхронизации (а так же их перевод, который будет использоваться дальше по тексту):
• Fences - преграда
• Semaphores - семафор
• Events - событие
• Barriers - барьер

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

Semaphores - обычно ассоциируются с ресурсами или группой ресурсов и могут быть использованы для маршалинга владения общими данными. Их статус не видим хосту.

Events - предоставляют примитивы точной синхронизации которые могут сигнализировать на уровне команд как хоста так и устройства или же могут ожидать сигналов от них.

Barriers - предоставляют синхронизацию выполнения и доступа к памяти между наборами команд.

Fences

Fences могут быть использованы хостом, чтоб определить завершение выполнения задач в очереди, отправленных через vkQueueSubmit и vkQueueBindSparse.
Состояние fence может быть либо signaled либо unsignaled. Хост может запросить статус конкретного fence или дождаться пока любой из них или все в группе преград не перейдут в состояние signaled.
Чтоб создать новый объект fence, используется команда
VkResult vkCreateFence(
VkDevice device,
const VkFenceCreateInfo * pCreateInfo,
const VkAllocationCallbacks * pAllocator,
VkFence * pFence);
• device - устройство, создающее fence-объект.
• pCreateInfo - указатель на структуру VkFenceCreateInfo, определяющее состояние fence-объекта.
• pAllocator - указатель на аллокатор.
• pFence - сюда вернется созданный fence-объект.

Возвращаемые коды ошибок:
Success
• VK_SUCCESS
Failure
• VK_ERROR_OUT_OF_HOST_MEMORY
• VK_ERROR_OUT_OF_DEVICE_MEMORY

Определение структуры VkFenceCreateInfo следующее:
typedef struct VkFenceCreateInfo {
VkStructureType sType;
const void * pNext;
VkFenceCreateFlags flags;
} VkFenceCreateInfo;

sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO
pNext = NULL

Поле flags структуры VkFenceCreateInfo содержит флаги опредлеяющие начальное состояние и поведение fence-объекта. Доступны следующие флаги:
typedef enum VkFenceCreateFlagBits {
VK_FENCE_CREATE_SIGNALED_BIT = 0x00000001,
} VkFenceCreateFlagBits;

Если флаг содержит VK_FENCE_CREATE_SIGNALED_BIT, тогда fence-объект создается в состоянии signaled, иначе он будет создан в состоянии unsignaled.

Fence-объект может быть предан в очередь как параметр пакета команд и когда соответствующее задание будет завершено состояние fence-объекта перейдет из состояния unsignaled в состояние signaled.

Чтоб удалить fence-объект необходимо вызвать команду:
void vkDestroyFence(
VkDevice device,
VkFence fence,
const VkAllocationCallbacks * pAllocator);

• fence-объект не должен быть связан ни с одной из незавершенных очередей

Чтоб узнать состояние fence-объекта со стороны хоста, необходимо вызвать функцию:
VkResult vkGetFenceStatus(VkDevice device, VkFence fence);
Возвращаемые значение:
Success
• VK_SUCCESS
• VK_NOT_READY
Failure
• VK_ERROR_OUT_OF_HOST_MEMORY
• VK_ERROR_OUT_OF_DEVICE_MEMORY
• VK_ERROR_DEVICE_LOST

Если выполнение функции vkGetFenceStatus было успешным, то функция вернет статус:
• VK_SUCCESS - обозначает что fence-объект находится в состоянии signaled.
• VK_NOT_READY - fence-объект находится в состоянии unsignaled.

Чтоб сбросить состояние одного или нескольких fence-объектов в состояние unsignaled, чтоб их можно было использовать после того как задачи в очереди были выполнены, необходимо вызвать команду:
VkResult vkResetFences(
VkDevice device,
uint32_t fenceCount,
const VkFence * pFences);
• device - устройство владеющее этими fence-объектами.
• fenceCount - количество fence-объектов, которые нужно сбросить.
• pFences - указатель на массив сбрасываемых fence-объектов.

Возвращаемые коды ошибок:
Success
• VK_SUCCESS
Failure
• VK_ERROR_OUT_OF_HOST_MEMORY
• VK_ERROR_OUT_OF_DEVICE_MEMORY

Чтоб заставить хост ожидать сигнала от одно или группы fence-объектов используется команда:
VkResult vkWaitForFences(
VkDevice device,
uint32_t fenceCount,
const VkFence * pFences,
VkBool32 waitAll,
uint64_t timeout);
• device - устройство владеющее fence-объектами.
• fenceCount - количество ожидаемых fence-объектов.
• pFences - указатель на массив fence-объектов.
• waitAll - условие при котором происходит разблокировка ожидания. Если значение waitAll равно VK_TRUE, то все все fence-объекты в массиве pFences должны просигнализировать, в противном случае достаточно чтоб хотя бы один из объектов послал сигнал.
• timeout - период таймаута в наносекундах. Значение подгоняется под ближайшую допустимую имплементацией Вулкана точность, которая может быть существенно больше одной наносекунды и даже больше запрашиваемого периода.

Возвращаемые коды ошибок:
Success
• VK_SUCCESS
• VK_TIMEOUT
Failure
• VK_ERROR_OUT_OF_HOST_MEMORY
• VK_ERROR_OUT_OF_DEVICE_MEMORY
• VK_ERROR_DEVICE_LOST

Если условие выполняется при вызове vkWaitForFences, то vkWaitForFences сразу же возвращает управление, в противном случае vkWaitForFences блокирует поток на время, указанное в timeout, в ожидании выполнения условия. Если timeout равен 0, то vkWaitForFences не ждет а просто возвращает текущее состояние всех fence-объектов, при этом будет возвращен VK_TIMEOUT, при том. что в действительности никакого ожидания не выполнялось. Если условие не выполняется за указанное время, то будет возвращена ошибка VK_TIMEOUT. Если условие выполняется за указанное время, то vkWaitForFences вернет VK_SUCCESS.

Fence-объекты сигнализируют когда устройство завершает выполнять задачу поставленную в очередь в сопровождении fence-объекта. Однако, это само по себе не является достаточным условием для хоста, чтоб гарантированно увидеть результаты записи устройством в память. Чтоб предоставить эти гарантии приложение должно вставить барьеры на операции работы с памятью между записью устройством и концом задачи, отправляющей сигнал через fence-объект. Чтоб гарантировать завершение операции записи, нужно установить флаг VK_ACCESS_HOST_READ_BIT в dstAccessMask и флаг VK_PIPELINE_STAGE_HOST_BIT в dstStageMask и соответствующие флаги в srcStageMask и srcAccessMask.

Если память была выделена без флага VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, то функция  vkInvalidateMappedMemoryRanges должна вызываться после того как был получен сигнал от fence-объекта, чтоб убедиться что записи видимы хосту.

Комментариев нет:

Отправить комментарий