Нужно ли передавать право собственности * обратно * в очередь передачи при следующей передаче? - PullRequest
2 голосов
/ 20 февраля 2020

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

vkBeginCommandBuffer(...);

// Submission guarantees the host write being complete, as per
// https://www.khronos.org/registry/vulkan/specs/1.0/html/vkspec.html#synchronization-submission-host-writes
// So no need for a barrier before the transfer

// Copy the staging buffer contents to the vertex buffer
VkBufferCopy vertexCopyRegion = {
    .srcOffset = stagingMemoryOffset,
    .dstOffset = vertexMemoryOffset,
    .size      = vertexDataSize};

vkCmdCopyBuffer(
    commandBuffer,
    stagingBuffer,
    vertexBuffer,
    1,
    &vertexCopyRegion);


// If the graphics queue and transfer queue are the same queue
if (isUnifiedGraphicsAndTransferQueue)
{
    // If there is a semaphore signal + wait between this being submitted and
    // the vertex buffer being used, then skip this pipeline barrier.

    // Pipeline barrier before using the vertex data
    // Note that this can apply to all buffers uploaded in the same way, so
    // ideally batch all copies before this.
    VkMemoryBarrier memoryBarrier = {
        ...
        .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,                           
        .dstAccessMask = VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT};

    vkCmdPipelineBarrier(
        ...
        VK_PIPELINE_STAGE_TRANSFER_BIT ,      // srcStageMask
        VK_PIPELINE_STAGE_VERTEX_INPUT_BIT,   // dstStageMask
        1,                                    // memoryBarrierCount
        &memoryBarrier,                       // pMemoryBarriers
        ...);


    vkEndCommandBuffer(...);

    vkQueueSubmit(unifiedQueue, ...);
}
else
{
    // Pipeline barrier to start a queue ownership transfer after the copy
    VkBufferMemoryBarrier bufferMemoryBarrier = {
        ...
        .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,                           
        .dstAccessMask = 0,
        .srcQueueFamilyIndex = transferQueueFamilyIndex,
        .dstQueueFamilyIndex = graphicsQueueFamilyIndex,
        .buffer = vertexBuffer,
        ...};

    vkCmdPipelineBarrier(
        ...
        VK_PIPELINE_STAGE_TRANSFER_BIT ,      // srcStageMask
        VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, // dstStageMask
        1,                                    // bufferMemoryBarrierCount
        &bufferMemoryBarrier,                 // pBufferMemoryBarriers
        ...);


    vkEndCommandBuffer(...);

    // Ensure a semaphore is signalled here which will be waited on by the graphics queue.
    vkQueueSubmit(transferQueue, ...);

    // Record a command buffer for the graphics queue.
    vkBeginCommandBuffer(...);

    // Pipeline barrier before using the vertex buffer, after finalising the ownership transfer
    VkBufferMemoryBarrier bufferMemoryBarrier = {
        ...
        .srcAccessMask = 0,                           
        .dstAccessMask = VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT,
        .srcQueueFamilyIndex = transferQueueFamilyIndex,
        .dstQueueFamilyIndex = graphicsQueueFamilyIndex,
        .buffer = vertexBuffer,
        ...};

    vkCmdPipelineBarrier(
        ...
        VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,    // srcStageMask
        VK_PIPELINE_STAGE_VERTEX_INPUT_BIT,   // dstStageMask
        ...
        1,                                    // bufferMemoryBarrierCount
        &bufferMemoryBarrier,                 // pBufferMemoryBarriers
        ...);


    vkEndCommandBuffer(...);

    vkQueueSubmit(graphicsQueue, ...);
}

В этом примере я упрощаю его до значения:

map updated buffer which is host coherent
perform transfer in transfer queue to device local memory
    make sure to put a buffer memory barrier to handle the queue ownership transfer
perform normal draw commands
    make sure to put a buffer memory barrier to handle receiving of buffer in queue ownership

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

ie

//begin with transfer ownership
submit(copy)
submit(ownership to graphics)
submit(draw)
submit(ownership to transfer)
submit(copy)
submit(ownership to graphics)
submit(draw)
submit(draw)
submit(draw)
submit(ownership to transfer)
submit(copy)
submit(ownership to graphics)
submit(draw)

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

1 Ответ

3 голосов
/ 20 февраля 2020

Вы можете использовать Ресурс в любом семействе очередей (без передачи), если вы не возражаете, что данные становятся неопределенными. Вам по-прежнему нужен семафор, чтобы убедиться в отсутствии опасности для памяти.

Старое значение c:

ПРИМЕЧАНИЕ

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

Примеры не упоминают об этом, потому что они являются только примерами .

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

Затем вы сигнализируете семафор по копии и ждете его в первом следующем тираже, который вы отправите. , Это означает, что копия закончила писать до того, как тираж (и любой последующий тираж) прочитает ее в графической очереди.

Например:

submit(copy, release ownership from tranfer, semaphore signal)
submit(semaphore wait, acquire ownership to graphics, draw)
submit(draw)
submit(draw)
submit(draw)
submit(draw)
submit(draw, semaphore signal)
submit(semaphore wait, copy, release ownership from tranfer, semaphore signal)
submit(semaphore wait, acquire ownership to graphics, draw)
submit(draw)
submit(draw)
etc

Хотя обратите внимание, что вышеуказанный подход практически сериализует эти два вида доступов, так что это может быть неоптимальным. Использование двойной буферизации (или вообще N-буферизации) может быть лучше. Если у вас есть больше буферов, вы можете начать копировать в один, не думая, что он уже используется чем-то другим. Это означает, что копия может получиться в параллельном тираже, что было бы здорово.

...