Как загрузить несколько структур в один UBO? - PullRequest
0 голосов
/ 30 марта 2019

Я следую инструкциям на: Здесь .

Я закончил до загрузки моделей, поэтому мой код похож на этот момент.

Сейчас я пытаюсь передать другую структуру в объект 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 = &lightBufferInfo;
        ...

Теперь, аналогично UniformBufferObject, я планирую использовать функцию updateUniformBuffer(uint32_t currentImage) для изменения положения и цвета источников света, но сначала я просто попытался установить положение на нужное значение:

    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, &data);
        memcpy(data, &ubo, sizeof(ubo));
        vkUnmapMemory(device, uniformBuffersMemory[currentImage]);
    }

Я не понимаю, как работает смещение при попытке передать два объекта в единый буфер, поэтому я не знаю, как скопировать легкий объект в uniformBuffersMemory.

Как будут определены смещения, чтобы заставить это работать?

1 Ответ

1 голос
/ 30 марта 2019

Примечание перед прочтением: разделение данных для одного 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

Обратите внимание, что для более сложных распределений и сценариев вам необходимо правильно получить правильный размер выравнивания в зависимости от размера вашей структуры данных вместо использования простого 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);

Обратите внимание, что вы можете захотеть отображать только один раз после создания буферов из соображений производительности, а не для отображенияна каждом обновлении.Просто сохраните указатель смещения где-нибудь в вашем коде.

...