gpucpuvulkanuniform

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


I encounter an issue when I transmit uniform buffer data from CPU to GPU side. It seems something to do with memory alignment between CPU/GPU. I define a uniform buffer object as follows:

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];
};

and in shader code, it defines as:

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

I define MAX_LIGHTS_NUM as 32. When I copy memory from on CPU side, I print all ubo data and confirm that all data on CPU side are correct:

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]);
}

I capture renderdoc ubo data, and found that the data till RealLightsNum are correct, but Light data only the first one is partially correct, which means the position is correct, but color third channel is not correct.

I doubt this is an memory alignment issue, but can't figure it out correctly. Could somebody help on this, thanks in advance.

Logs: enter image description here

renderdoc capture: enter image description here


Solution

  • I got the answer on link Vulkan Memory Alignment for Uniform Buffers. It says for each elements on CPU side, we should set alignment like:

    It works after I set as follows:

    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];
    };