SSBO CPU 映射返回正确数据,但数据 'different' 到 GPU 上的 SSBO

SSBO CPU mapping returning correct data, but data is 'different' to the SSBO on GPU

我 运行 在尝试按如下方式使用 SSBO 时遇到问题:

    GLuint lightSSBO;
    glGenBuffers(1, &lightSSBO);
    glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, lightSSBO);
    glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(int) + sizeof(LightData) * 10, NULL, GL_DYNAMIC_DRAW);

    glBufferSubData(GL_SHADER_STORAGE_BUFFER, 0, sizeof(int), &lightCount);
    glBufferSubData(GL_SHADER_STORAGE_BUFFER, sizeof(int), sizeof(LightData) * lights.size(), &lights[0]);

    void* ptr = glMapBuffer(GL_SHADER_STORAGE_BUFFER, GL_READ_ONLY);

    std::cout << "lightCount value = " << *(int*)ptr << "\n";
    std::cout << "First Light: \n";
    ptr = (int*)ptr + 1;
    std::cout << "\tType: " << *(unsigned int*)ptr << "\n";
    ptr = (int*)ptr + 1;
    std::cout << "\tPosition: " << vec4ToString((glm::vec4*)ptr) << "\n";
    ptr = (float*)ptr + 4;
    std::cout << "\tDirection: " << vec4ToString((glm::vec4*)ptr) << "\n";
    ptr = (float*)ptr + 4;
    std::cout << "\tColour: " << vec4ToString((glm::vec4*)ptr) << "\n";
    ptr = (float*)ptr + 4;
    std::cout << "\tSize: " << *(float*)ptr << "\n";

    glUnmapBuffer(GL_SHADER_STORAGE_BUFFER);

在上面的代码摘录中,ptr 输出我分配给 lightCount 和 lights[0] 的正确值,这意味着缓冲区表面上具有正确的数据。但是,当我尝试在 GPU (Lights[0]) 上访问超过 lightCount 时,我得到了不正确的值。例如,Lights[0].Colour 只是 returns (0,0,0,?),尽管通过上面的 ptr 正确显示为 (1,0.2,0.2,1)。

struct LightData 
{
    uint Type;
    vec4 Position;
    vec4 Direction;
    vec4 Colour;
    float Size;
};

layout (std430, binding = 0) buffer LightBuffer
{
   int LightCount;
   LightData[] Lights;
};

void main()
{
   FragColor = Lights[0].Colour;
}

以及控制台的完整性输出:(正确的值)

lightCount value = 1
First Light:
        Type: 1
        Position: 0.000000, 0.000000, 0.000000, 1.000000
        Direction: -0.707107, 0.000000, -0.707107, 1.000000
        Colour: 1.000000, 0.200000, 0.200000, 1.000000
        Size: 0

注意:将着色器颜色更改为 Position 仍然 不正确并且如果我更改基础数据也不会更改,这表明 Light 阵列要么没有做到GPU 或我以某种方式错误地访问它。

有谁知道为什么会这样?此外,我对 glBufferData 的最终 GLenum 参数的 meaning/options 感到困惑,甚至 https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_shader_storage_buffer_object.txt 似乎也没有提到它。

GLSL 结构和 C++ 结构有不同的对齐规则。对于结构,规范规定:

If the member is a structure, the base alignment of the structure is N, where N is the largest base alignment value of any of its members, and rounded up to the base alignment of a vec4. The individual members of this substructure are then assigned offsets by applying this set of rules recursively, where the base offset of the first member of the sub-structure is equal to the aligned offset of the structure. The structure may have padding at the end; the base offset of the member following the sub-structure is rounded up to the next multiple of the base alignment of the structure.

我们来分析结构:

struct LightData 
{
    uint Type;
    vec4 Position;
    vec4 Direction;
    vec4 Colour;
    float Size;
};

最大的成员是一个vec4(=16字节对齐)。这意味着整个结构也需要对齐到 16 个字节。该结构的成员大小为 4 + 3 * 16 + 4 = 56 字节。 16 的下一个更大的倍数是 64,这意味着所有结构都需要在末尾额外填充 8 个字节。

由于在第一个结构之前有一个额外的成员int LightCount;,您还需要在它们之间添加填充以确保第一个结构从 16 字节偏移量开始。

与您的 glsl 缓冲区定义相匹配的 C++ 结构应如下所示:

struct LightData
{
    unsigned int Type;
    vec4 Position;
    vec4 Direction;
    vec4 Colour;
    float Size;
    int padding[2];
}

struct Buffer
{
    int LightCount;
    int padding[3];
    Light Lights[NUM_LIGHTS];
}