Войти
ПрограммированиеСтатьиГрафика

Синхронизации в Vulkan (3 стр)

Автор:

Примеры барьеров.

vkCmdDispatch( ... );  // читаем из buffer
vkCmdPipelineBarrier( VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
                     VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, ... );
vkCmdDispatch( ... );  // пишем в buffer
Здесь глобальный барьер (global execution barrier), этого достаточно в случае записи после чтения, но глобальный барьер повлияет на другие команды, которые не используют buffer, поэтому перепишем код так:
vkCmdDispatch( ... );  // читаем из buffer
VkBufferMemoryBarrier  barrier;
barrier.srcAccessMask = 0;
barrier.dstAccessMask = 0;
barrier.buffer = buffer;
barrier.offset = 0;
barrier.size = VK_WHOLE_SIZE;
vkCmdPipelineBarrier( VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
                     VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, ..., 1, &barrier, ... );
vkCmdDispatch( ... );  // пишем в buffer
Теперь барьер затронет только эти две команды, а также последующие команды, если они используют buffer.

vkCmdDispatch( ... );  // пишем в buffer
VkBufferMemoryBarrier  barrier;
barrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT;
barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
barrier.buffer = buffer;
barrier.offset = 0;
barrier.size = VK_WHOLE_SIZE;
vkCmdPipelineBarrier( VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
                     VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, ..., 1, &barrier, ... );
vkCmdDispatch( ... );  // читаем buffer
Чтение после записи требует инвалидации кэша (memory dependency), в srcAccessMask указываем, что запись была в шейдере, в dstAccessMask указываем, что эти данные нужны нам для чтения в шейдере. Если неправильно указать dstAccessMask, то инвалидация кэша может произойти позднее, например на последнем этапе конвеера (bottom of pipe), где обычно происходят все синхронизации.

vkCmdDispatch( ... );  // пишем в buffer
VkBufferMemoryBarrier  barrier;
barrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT;
barrier.dstAccessMask = VK_ACCESS_SHADER_WRITE_BIT;
barrier.buffer = buffer;
barrier.offset = 0;
barrier.size = VK_WHOLE_SIZE;
vkCmdPipelineBarrier( VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
                     VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, ..., 1, &barrier, ... );
vkCmdDispatch( ... );  // перезаписываем buffer
Здесь мы указываем в каком порядке будет происходить запись, то есть сначала должен быть записан результат первого вызова vkCmdDispatch, а потом данные могут быть перезаписаны следующим вызовом vkCmdDispatch. В этом случае драйвер может произвести запись из кэша в глобальную память только после второго вызова, уже учитывая перезапись. Чтение данных буфера в шейдере приведет к неопределенному поведению.

vkCmdDispatch( ... );  // читаем и пишем в buffer
VkBufferMemoryBarrier  barrier;
barrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT; // | VK_ACCESS_SHADER_READ_BIT;
barrier.dstAccessMask = VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT;
barrier.buffer = buffer;
barrier.offset = 0;
barrier.size = VK_WHOLE_SIZE;
vkCmdPipelineBarrier( VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
                     VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, ..., 1, &barrier, ... );
vkCmdDispatch( ... );  // читаем и пишем в buffer
Здесь обе команды и читают и записывают данные. Для srcAccessMask указывать флаг VK_ACCESS_SHADER_READ_BIT не требуется. Для dstAccessMask обязательно указать флаг чтение и записи, чтобы был виден результат предыдущей записи.


Ообенности рисования в текстуру.
Если включен блендинг или логические операции, то обязательно в dstAccessMask указывать VK_ACCESS_COLOR_ATTACHMENT_READ_BIT и VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, в остальных случаях достаточно только VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT.
Если включен тест глубины, но выключена запись, то мы можем указать image layout как VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL, такой image layout позволяет одновременно с тестом глубины читать текстуру в шейдере.


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

// рисуем в image
vkCmdBeginRenderPass(...);
...
vkCmdEndRenderPass(...);
...
// пишем в image
VkImageMemoryBarrier barrier = {};
barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
barrier.dstAccessMask = VK_ACCESS_SHADER_WRITE_BIT;
barrier.oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
barrier.newLayout = VK_IMAGE_LAYOUT_GENERAL;
vkCmdPipelineBarrier(..., 1, &barrier, …);
vkCmdDispatch(...);
Сделали правильный image layout transition, но получили долгое разжатие. Драйвер не знает будет ли перезаписана текстура полностью или только ее часть, поэтому обязан произвести разжатие данных при переходе на новый layout.
// рисуем в image
vkCmdBeginRenderPass(...);
...
vkCmdEndRenderPass(...);
...
// пишем в image, предыдущие данные не важны
VkImageMemoryBarrier barrier = {};
barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_OUTPUT_BIT;
barrier.dstAccessMask = VK_ACCESS_SHADER_WRITE_BIT;
barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
barrier.newLayout = VK_IMAGE_LAYOUT_GENERAL;
vkCmdPipelineBarrier(..., 1, &barrier, …);
vkCmdDispatch(...);
Предыдущий layout не определен, поэтому драйвер пропустит разжатие, также может отбросить предыдущую запись из кэша в память.

Страницы: 1 2 3 4 5 Следующая »

#Vulkan

26 февраля 2019 (Обновление: 29 мар. 2019)

Комментарии [36]