如何将多个结构加载到单个UBO中?

时间:2019-03-29 22:57:35

标签: c++ buffer offset vulkan

我正在关注Here上的教程。

在加载模型之前,我已经完成了工作,因此我的代码与此类似。

我现在正尝试以与先前所示类似的方式将另一个结构传递给统一缓冲区对象。

我已经在应用程序外部创建了另一个结构来存储数据,如下所示:

struct Light{
    alignas(16) glm::vec3 position;
    alignas(16) glm::vec3 colour;
};

完成此操作后,我通过以下方式调整了统一缓冲区的大小:

    void createUniformBuffers() {
        VkDeviceSize bufferSize = sizeof(CameraUBO) + sizeof(Light);
    ...

接下来,在创建描述符集时,我在已定义的bufferInfo下方添加了lightBufferInfo,如下所示:

        ...
        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 个答案:

答案 0 :(得分:1)

进一步阅读前的注意事项:将单个UBO的数据分为两个不同的结构和描述符会使传递数据更加复杂,因为所有大小和写入都需要与设备的minUniformBufferAlignment属性对齐,使您的代码更加复杂。如果您从Vulkan开始,则可能需要将数据拆分为两个UBO(创建两个缓冲区),或者只是将所有值作为单个结构传递。

但是,如果您想继续按照帖子中的描述进行操作:

首先,您需要适当调整数组的大小,因为您的副本需要与minUniformBufferAlignment对齐,您可能无法仅将光照数据复制到紧随其他数据之后的区域。如果您的设备的minUniformBufferAlignment为256字节,并且要复制两个主机结构,则统一缓冲区的大小至少应为2 * 256字节,而不仅仅是sizeof(matrices) + {{1} }。因此,您需要在sizeof(lights)结构中调整bufferSize

接下来,您需要抵消VkDeviceSize lightBufferInfo

VkDescriptorBufferInfo

这将使您的顶点着色器知道从何处开始获取该绑定的数据。

在大多数NVidia GPU上,例如lightBufferInfo.offset = std::max(sizeof(Light), minUniformBufferOffsetAlignment); 是256个字节,而minUniformBufferOffsetAlignment结构的大小是32个字节。因此,要在这样的GPU上执行此操作,您必须对齐256个字节而不是32个字节。

然后在RenderDoc中检查您的设置应该类似于以下内容:

enter image description here

请注意,对于更复杂的分配和方案,您需要根据数据结构的大小正确获取正确的对齐大小,而不是像上面那样使用简单的Light

现在,在更新统一缓冲区时,您还需要映射并复制到适当的偏移量:

max

请注意,出于性能原因,您可能只想在创建缓冲区后才映射一次,而不是每次更新都映射一次。只需将偏移量指针存储在代码中的某个位置即可。