Vulkan 统一数据(内存)从 CPU 复制到 GPU 错误

Vulkan Uniform data(memory) copy from CPU to GPU error

我在从 CPU 向 GPU 端传输统一缓冲区数据时遇到问题。这似乎与 CPU/GPU 之间的内存对齐有关。我定义了一个uniform buffer对象如下:

const int MAX_LIGHTS_NUM = 32;
struct Lights {
    glm::vec3 Position;
    glm::vec3 Color;
    float Linear;
    float Quadratic;
    float intensity;
};
struct UniformBufferObject {
    glm::mat4 mvp;
    glm::mat4 model;
    glm::vec3 camPos;
    int RealLightsNum;
    Lights lights[MAX_LIGHTS_NUM];
};

在着色器代码中,它定义为:

layout(binding = 0) uniform UniformBufferObject {
    mat4 mvp;
    mat4 mMatrix;
    vec3 CameraPosition;
    int RealLightsNum;
    Light lights[MAX_LIGHTS_NUM];
} ubo;

我定义MAX_LIGHTS_NUM为32,当我从CPU一侧复制内存时,我打印所有ubo数据并确认CPU一侧的所有数据都是正确的:

void printUBO(UniformBufferObject &ubo) {
    LOGD("%s:%d Model matrix:%s", __FUNCTION__, __LINE__, glm::to_string(ubo.model).c_str());
    LOGD("%s:%d MVP matrix:%s", __FUNCTION__, __LINE__, glm::to_string(ubo.mvp).c_str());
    LOGD("%s:%d camPos:%s", __FUNCTION__, __LINE__, glm::to_string(ubo.camPos).c_str());
    LOGD("%s:%d ubo size:%d, RealLightsNum:%d", __FUNCTION__, __LINE__, sizeof(ubo), ubo.RealLightsNum);
    for (int i = 0; i < ubo.RealLightsNum; i++) {
        LOGD("%s:%d Light[%d].pos: %s",
             __FUNCTION__, __LINE__, i, glm::to_string(ubo.lights[i].Position).c_str());
        LOGD("%s:%d Light[%d].color: %s",
             __FUNCTION__, __LINE__, i, glm::to_string(ubo.lights[i].Color).c_str());
        LOGD("%s:%d Light[%d].(intensity/linear/quadratic: %f, %f, %f",
             __FUNCTION__, __LINE__, i, ubo.lights[i].intensity, ubo.lights[i].Linear, ubo.lights[i].Quadratic);
    }
}

void VulkanHal::drawPrimitive(int nodeId, unsigned int index, size_t offset, int mode, int count,
                              int type) {
    if (initialized == false) return;
//    LOGD("%s:%d nodeId:%d, index:%u offset:%u, mode:%d, count:%d, type:%d",
//         __FUNCTION__, __LINE__, nodeId, index, offset, mode, count, type);
//    LOGD("%s:%d -", __FUNCTION__, __LINE__);
//    LOGD("%s:%d ubo size:%d, RealLightsNum:%d", __FUNCTION__, __LINE__, sizeof(ubo), ubo.RealLightsNum);
    printUBO(ubo);
    int primIndex = getPrimitiveIndex(nodeId, index);
    void* data;
    vkMapMemory(device, uniformBuffersMemory[imageIndex][primIndex], 0, sizeof(ubo), 0, &data);
    memcpy(data, &ubo, sizeof(ubo));
    vkUnmapMemory(device, uniformBuffersMemory[imageIndex][primIndex]);
}

我截取了renderdoc ubo数据,发现RealLightsNum之前的数据都是正确的,但是Light数据只有第一个是部分正确的,也就是说位置是正确的,但是颜色第三通道是不正确的。

我怀疑这是内存对齐问题,但无法正确解决。有人可以帮忙吗,在此先感谢。

日志:

renderdoc 捕获:

我在 link Vulkan Memory Alignment for Uniform Buffers 上得到了答案。它说对于 CPU 侧的每个元素,我们应该设置对齐方式:

  • a int/float 对齐 4
  • 一个 vec2 的对齐方式为 8
  • 一个vec3, vec4, mat4 16的排列

我设置后就生效了:

const int MAX_LIGHTS_NUM = 32;
struct Lights {
    alignas(16) glm::vec3 Position;
    alignas(16) glm::vec3 Color;
    alignas(4) float Linear;
    alignas(4) float Quadratic;
    alignas(4) float intensity;
};

struct UniformBufferObject {
    alignas(16) glm::mat4 mvp;
    alignas(16) glm::mat4 model;
    alignas(16) glm::vec3 camPos;
    alignas(4) int RealLightsNum;
    Lights lights[MAX_LIGHTS_NUM];
};