Примечание перед прочтением: разделение данных для одного UBO на две разные структуры и дескрипторы делает передачу данных немного более сложной, так как все ваши размеры и записи должны быть согласованы со свойством minUniformBufferAlignment
вашего устройства, что делаетВаш код немного сложнее.Если вы начинаете с Vulkan, вы можете разделить данные либо на два UBO (создав два буфера), либо просто передать все значения в виде единой структуры.
Но если вы хотите продолжить с тем, как выописано в вашем посте:
Сначала вам нужно правильно настроить размер массива, потому что ваши копии должны быть выровнены по minUniformBufferAlignment
, вы, вероятно, не можете просто скопировать ваши легкие данные в область сразу после ваших других данных.Если ваше устройство имеет 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 должна выглядеть примерно так:
![enter image description here](https://i.stack.imgur.com/eWJ2r.png)
Обратите внимание, что для более сложных распределений и сценариев вам необходимо правильно получить правильный размер выравнивания в зависимости от размера вашей структуры данных вместо использования простого max
, как описано выше.
И теперь при обновлении ваших унифицированных буферов вам также нужно отобразить и скопировать с соответствующим смещением:
void* mapped = nullptr;
// Copy matrix data to offset for binding 0
vkMapMemory(device, uniformBuffersMemory[currentImage].memory, 0, sizeof(ubo), 0, &mapped);
memcpy(mapped, &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, &mapped);
memcpy(mapped, &uboLight, sizeof(Light));
vkUnmapMemory(device, uniformBuffersMemory[currentImage].memory);
Обратите внимание, что вы можете захотеть отображать только один раз после создания буферов из соображений производительности, а не для отображенияна каждом обновлении.Просто сохраните указатель смещения где-нибудь в вашем коде.