Мой вопрос состоит из 2 частей:
Я учу Vulkan из этого урока (https://vulkan-tutorial.com) и в настоящее время ищет другой подход для загрузки единообразных данных (простые матрицы модели / вида / проекции) в локальную память устройства. Матрицы используются в вершинном шейдере.
В учебнике матрицы обновляются и копируются в промежуточный буфер (vkMapMemory
и т. д.) и затем копируются в локальный буфер конечного устройства путем создания буфера команд, записи vkCmdCopy
, отправив его и уничтожив буфер. Я пытаюсь сделать последний шаг в рамках обязательных командных буферов для рисования.
В то время как метод обучения приводит к быстрой анимации, в моем эксперименте эта функция отсутствует. Я попытался установить 2 bufferBarriers, чтобы убедиться, что копии сделаны (что, кажется, проблема), но это не помогло. Ресурсы правильно созданы и связаны — это работает нормально.
//update uniform buffer and copy it to the staging buffer
//(called every frame)
Tools::UniformBufferObject ubo;
//set the matrices
void* data;
data = device.mapMemory( uniformStagingMemory, 0, sizeof( ubo ), (vk::MemoryMapFlagBits) 0 );
memcpy( data, &ubo, sizeof( ubo ));
device.unmapMemory( uniformStagingMemory );//once: create a command buffer for each framebuffer of the swapchain
//queueFamily struct members set properly
//1st barrier: make transfer from host memory to staging buffer available / visible
vk::BufferMemoryBarrier bufMemBarrierStaging;
bufMemBarrierStaging.srcAccessMask = vk::AccessFlagBits::eHostWrite;
bufMemBarrierStaging.dstAccessMask = vk::AccessFlagBits::eTransferRead;
bufMemBarrierStaging.buffer = uniformStagingBuffer;
bufMemBarrierStaging.offset = 0;
bufMemBarrierStaging.size = sizeof( Tools::UniformBufferObject );
//2nd barrier: make transfer from staging buffer to device local buffer available / visible
vk::BufferMemoryBarrier bufMemBarrier;
bufMemBarrier.srcAccessMask = vk::AccessFlagBits::eTransferWrite;
bufMemBarrier.dstAccessMask = vk::AccessFlagBits::eUniformRead | vk::AccessFlagBits::eShaderRead;
bufMemBarrier.buffer = dataBuffer;
bufMemBarrier.offset = dataBufferOffsets[2];
bufMemBarrier.size = sizeof( Tools::UniformBufferObject );
for( size_t i = 0; i < cmdBuffers.size(); i++ ) {
//begin command buffer
cmdBuffers[i].pipelineBarrier(
vk::PipelineStageFlagBits::eHost, //srcPipelineStage
vk::PipelineStageFlagBits::eTransfer, //dstPipelineStage
(vk::DependencyFlagBits) 0,
nullptr, //memBarrier
bufMemBarrierStaging,
nullptr //imgBarrier
);
vk::BufferCopy copyRegion; //filled appropriate
cmdBuffers[i].copyBuffer( uniformStagingBuffer, dataBuffer, copyRegion );
cmdBuffers[i].pipelineBarrier(
vk::PipelineStageFlagBits::eTransfer, //srcPipelineStage
vk::PipelineStageFlagBits::eVertexShader, //dstPipelineStage
(vk::DependencyFlagBits) 0,
nullptr, //memBarrier
bufMemBarrier,
nullptr //imgBarrier
);
//renderpass stuff and drawing etc.
}
с
namespace Tools {
struct UniformBufferObject {
glm::mat4 model;
glm::mat4 view;
glm::mat4 proj;
};
};
vk::Buffer uniformStagingBuffer;
vk::DeviceMemory uniformStagingMemory;
//dataBuffer also contains the vertex and index data, is device local
vk::Buffer dataBuffer;
vk::DeviceMemory dataBufferMemory;
vk::vector<vk::DeviceSize> dataBufferOffsets;
std::vector<vk::CommandBuffer> cmdBuffers;
Я использую vkcpp
(https://github.com/KhronosGroup/Vulkan-Hpp).
Является ли причиной отсутствия анимации отсутствие согласованности данных — и я ошибся, пытаясь добиться этого?
Заранее спасибо!
Редактировать: Проблема части 2 была отсутствующей синхронизацией; промежуточный буфер был (частично) обновлен до того, как был прочитан во время рендеринга кадра. (Спасибо за разъяснение разницы между доступной / видимой памятью).
Если промежуточная буферная память не является хост-когерентной, вам необходимо vkFlushMappedMemoryRanges
после memcpy (память может оставаться отображенной). Если вы этого не сделаете, то нет никакой гарантии, что данные действительно видны для GPU.
Первый барьер (хост для передачи) на самом деле не нужен; Существует неявный барьер при отправке.
Еще одна проблема, которую я вижу, заключается в том, что у вас есть один промежуточный буфер, а это означает, что вам нужно дождаться окончания предыдущего кадра, прежде чем вы сможете загрузить новые данные.
Если упоминание «уничтожения» означает, что вы выделяете для каждого кадра … Сначала вы должны дождаться уничтожения, пока не будут выполнены все использованные буферы команд, во-вторых, не делайте этого. Распределение на стороне GPU стоит дорого, вместо этого предпочтительнее выделить один раз и использовать кольцевой буфер.
Объявление 1
Имеется в наличии а также видимый две половины зависимости памяти. В значительной степени должно произойти и то, и другое, чтобы быть действительной зависимостью от памяти.
Вы можете рассматривать это как последовательность состояний:
Ресурс написан src
→ сделано доступным src
→ сделано видимым для dst
→ используется dst
,
Целью этого является согласованность кэшей. Спецификация пытается избежать того, чтобы слово «кеш» было более абстрактным.
Вы отвечаете за то, чтобы сделать доступным и видимым. Операции, которые могут сделать что-то из этого, являются барьерами, событиями, согласованной отображаемой памятью или flush
а также invalidate
и некоторые другие …
AFAIK запланировано переписывание спецификации синхронизации, и номенклатура может измениться.