#c #buffer #offset #vulkan
#c #буфер #смещение #vulkan
Вопрос:
Я следую инструкциям по: Здесь.
Я завершил загрузку моделей, поэтому мой код похож на этот момент.
Сейчас я пытаюсь передать другую структуру в объект Uniform Buffer аналогично ранее показанному.
Я создал другую структуру, определенную вне приложения, для хранения данных следующим образом:
struct Light{
alignas(16) glm::vec3 position;
alignas(16) glm::vec3 colour;
};
После выполнения этого я изменил размер единого буфера следующим образом:
void createUniformBuffers() {
VkDeviceSize bufferSize = sizeof(CameraUBO) sizeof(Light);
...
Далее, при создании наборов дескрипторов я добавил lightBufferInfo под уже определенным BufferInfo, как показано ниже:
...
for (size_t i = 0; i < swapChainImages.size(); i ) {
VkDescriptorBufferInfo bufferInfo = {};
bufferInfo.buffer = uniformBuffers[i];
bufferInfo.offset = 0;
bufferInfo.range = sizeof(UniformBufferObject);
VkDescriptorBufferInfo lightBufferInfo = {};
lightBufferInfo.buffer = uniformBuffers[i];
lightBufferInfo.offset = 0;
lightBufferInfo.range = sizeof(Light);
...
Затем я добавил это в массив descriptorWrites:
...
descriptorWrites[2].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
descriptorWrites[2].dstSet = descriptorSets[i];
descriptorWrites[2].dstBinding = 2;
descriptorWrites[2].dstArrayElement = 0;
descriptorWrites[2].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
descriptorWrites[2].descriptorCount = 1;
descriptorWrites[2].pBufferInfo = amp;lightBufferInfo;
...
Теперь, аналогично UniformBufferObject, я планирую использовать updateUniformBuffer(uint32_t currentImage)
функцию для изменения положения и цвета источников света, но сначала я просто попытался установить position на желаемое значение:
void updateUniformBuffer(uint32_t currentImage) {
...
ubo.proj[1][1] *= -1;
Light light = {};
light.position = glm::vec3(0, 10, 10);
light.colour = glm::vec3(1, 1, 0);
void* data;
vkMapMemory(device, uniformBuffersMemory[currentImage], 0, sizeof(ubo), 0, amp;data);
memcpy(data, amp;ubo, sizeof(ubo));
vkUnmapMemory(device, uniformBuffersMemory[currentImage]);
}
Я не понимаю, как работает смещение при попытке передать два объекта в единый буфер, поэтому я не знаю, как скопировать объект light в uniformBuffersMemory
.
Как будут определены смещения, чтобы заставить это работать?
Ответ №1:
Примечание перед дальнейшим чтением: разделение данных для одного UBO на две разные структуры и дескрипторы немного усложняет передачу данных, поскольку все ваши размеры и записи должны быть приведены в соответствие с minUniformBufferAlignment
свойством вашего устройства, что немного усложняет ваш код. Если вы начинаете с Vulkan, вы можете захотеть разделить данные либо на два UBO (создав два буфера), либо просто передать все значения как одну структуру.
Но если вы хотите продолжить так, как вы описали в своем посте:
Сначала вам нужно правильно определить размер вашего массива, потому что ваши копии должны быть выровнены по minUniformBufferAlignment
вы, вероятно, не сможете просто скопировать ваши данные light в область сразу после ваших других данных. Если на вашем устройстве minUniformBufferAlignment
256 байт, и вы хотите скопировать более двух структур хоста, размер ваших единообразных буферов должен быть не менее 2 * 256 байт, а не просто sizeof(matrices)
sizeof(lights)
. Таким образом, вам нужно соответствующим образом скорректировать свое bufferSize
место в VkDeviceSize
структуре.
Далее вам нужно компенсировать свой lightBufferInfo
VkDescriptorBufferInfo
:
lightBufferInfo.offset = std::max(sizeof(Light), minUniformBufferOffsetAlignment);
Это позволит вашему вершинному шейдеру узнать, с чего начать выборку данных для этой привязки.
Например, на большинстве графических процессоров NVidia, minUniformBufferOffsetAlignment
равно 256 байтам, где размер вашей Light
структуры равен 32 байтам. Итак, чтобы это работало на таком графическом процессоре, вам нужно выровнять по 256 байтам вместо 32.
Проверка вашей настройки в RenderDoc должна выглядеть примерно так:
Обратите внимание, что для более сложных распределений и сценариев вам нужно будет правильно получить правильный размер выравнивания в зависимости от размера вашей структуры данных вместо использования простого max
, подобного приведенному выше.
И теперь при обновлении ваших унифицированных буферов вам также необходимо сопоставить и скопировать с соответствующим смещением:
void* mapped = nullptr;
// Copy matrix data to offset for binding 0
vkMapMemory(device, uniformBuffersMemory[currentImage].memory, 0, sizeof(ubo), 0, amp;mapped);
memcpy(mapped, amp;ubo, sizeof(ubo));
vkUnmapMemory(device, uniformBuffersMemory[currentImage].memory);
// Copy light data to offset for binding 1
vkMapMemory(device, uniformBuffersMemory[currentImage].memory, std::max(sizeof(ubo), minUniformBufferOffsetAlignment), sizeof(Light), 0, amp;mapped);
memcpy(mapped, amp;uboLight, sizeof(Light));
vkUnmapMemory(device, uniformBuffersMemory[currentImage].memory);
Обратите внимание, что вы можете захотеть сопоставлять только один раз после создания буферов по соображениям производительности, а не при каждом обновлении. Просто сохраните указатель смещения где-нибудь в своем коде.
Комментарии:
1. Спасибо. Если бы я должен был создать два буфера, было бы это сделано внутри
createUniformBuffers()
функции простоcreateBuffer(bufferSize1, ...
иcreateBuffer(bufferSize2, ...
для каждого изображения цепочки подкачки?2. Да, вам пришлось бы создать другой буфер (для каждого образа swapchain), который содержит только данные light, и привязать их к
VkDescriptorBufferInfo
для вашегоlightBufferInfo
.3. Ценю помощь. С точки зрения производительности, один из методов значительно быстрее другого?