【问题标题】:Segfault when passing vector of vectors to SSBO using glBufferData使用 glBufferData 将向量的向量传递给 SSBO 时出现段错误
【发布时间】:2022-01-16 03:52:03
【问题描述】:

我正在尝试将向量的向量传递给 SSB0,但是在使用 glBufferData 传递它时出现段错误。 C++中的结构是:

const uint16_t MAX_NODE_POOLS = 1927;

union Node
{
    uint32_t childDescriptor;
    uint32_t material;
};

struct NodePool
{
    NodePool() : mNodes({0}) {}
    std::array<Node, 8> mNodes;
};

struct Block
{
    Block(): ID(0) {}
    uint16_t ID;
    std::vector<NodePool> mNodePools;
    std::vector<uint16_t> mNodeMasks;
};

class Octree
{
public:
    ...
    void registerSSBO(GLuint &octreeSSBO) const;
    void generate();
    
    [[nodiscard]] inline uint64_t getMem() const { return mBlocks.size() *
    (
            sizeof(uint16_t) +                      // ID
            (sizeof(NodePool)*MAX_NODE_POOLS) +     // NodePools
            (sizeof(uint16_t)*MAX_NODE_POOLS)       // NodeMasks
            ); }
private:
     ...
    std::vector<Block> mBlocks;
};

...

void Octree::Octree::registerSSBO(GLuint &octreeSSBO) const
{
    glGenBuffers(1, &octreeSSBO);
    glBindBuffer(GL_SHADER_STORAGE_BUFFER, octreeSSBO);
    std::cout << getMem() << std::endl;
    glBufferData(GL_SHADER_STORAGE_BUFFER, getMem(), mBlocks.data(), GL_DYNAMIC_DRAW);
    glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, octreeSSBO);
    glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
}

我用数据填充块,然后像这样传递到 SSBO

...
octree.generate();
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);

octree.registerSSBO(octreeSSBO);
glBindVertexArray(0);
...

在我的着色器中,SSBO 的结构是这样的

#version 430 core
// To use 16 bit integers
#extension GL_NV_gpu_shader5 : enable
#define MAX_NODE_POOLS 1927

struct Node
{
    // Either a child descriptor or material depending on mNodeMasks
    int data;
};

struct NodePool
{
    Node mNodes[8];
};

struct Block
{
    uint16_t ID;
    NodePool mNodePools[MAX_NODE_POOLS];
    uint16_t mNodeMasks[MAX_NODE_POOLS];
};

layout (std430, binding=2) buffer octreeData
{
    Block blocks[];
};

每次在glBufferData 内部registerSSBO 出现段错误

glBufferData(GL_SHADER_STORAGE_BUFFER, getMem(), mBlocks.data(), GL_DYNAMIC_DRAW);

getMem() 在这种情况下返回35773920 字节的大小,这是我期望的值。我计算不正确吗? mBlocks.size()*sizeof(mBlocks)mBlocks.size()*sizeof(Block) 等较小的值不会导致应用程序出现段错误(但应用程序的行为不符合预期)

使用 valgrind 运行可防止发生段错误,但在 glBufferData 调用上给了我 20 个警告 Invalid read of size 16,但我无法弄清楚这可能表明什么?

在每个单独的警告中,它都会给我这样的问题:

Invalid read of size 16
...
Address 0x4cd830b0 is 16 bytes before a block of size 61,664 alloc'd
Invalid read of size 16
...
Address 0x4cd83080 is 0 bytes after a block of size 57,344 alloc'd
Invalid read of size 16
...
Address 0x4cd830a0 is 32 bytes before a block of size 61,664 in arena "client"

这是多余的样板还是我遗漏了什么?

编辑:

为了显示向量的大小合适,我将getMem() 更改为以下函数,结果相同

inline uint64_t getMem() const 
{
    uint64_t sum = 0;
    for(const auto& b: mBlocks)
    {
        for(const auto& np: b.mNodePools)
            sum += sizeof(np);
        for(const auto& nm: b.mNodeMasks)
            sum += sizeof(nm);
        sum += sizeof(b.ID);
    }
    return sum;
}

【问题讨论】:

  • sizeof(mBlocks.data()) -- 你知道这个值代表什么吗?它不会告诉您mBlocks.data() 拥有的字节数。事实上,它不会告诉你任何对你的程序运行重要的事情。
  • 是的,我知道,我不会将它用于任何有用的东西,只是在我出于好奇进行探索时打印在那里。已删除以免进一步分散注意力
  • 您可能想要并且更相关的是输出mBlocks.size()。打印 sizeof 向量不会给您元素的数量。第二件事是向量向量的布局是否与您正在与之通信的 API 兼容。如果不是,那么您不能使用向量的向量。
  • 您的代码没有显示您如何调整向量的大小。很可能您从不这样做,因此您访问的是空向量的元素。
  • 感谢您的回复,但我只是出于好奇才将其打印出来,我确实知道其中的区别,正如您在我的内存计算中看到的那样。我已经能够将矢量发送到 OpenGL。我不确定我是否能够发送向量向量,因此这里的问题。

标签: c++ opengl memory segmentation-fault


【解决方案1】:
uint16_t ID;
std::vector<NodePool> mNodePools;
std::vector<uint16_t> mNodeMasks;
...
glBufferData(GL_SHADER_STORAGE_BUFFER, getMem(), mBlocks.data(), GL_DYNAMIC_DRAW);

你不能那样做。您不能将大多数 C++ 标准库类型按字节复制到 OpenGL 中(或其他任何事情)。作为一般规则,如果一个类型不是trivially copyable(并且vector 肯定是不是),它绝对不能像这样扔给OpenGL(注意:这并不意味着你可以在 OpenGL 中抛出任何可简单复制的类型。简单可复制性是必要的,但还不够)。

您对std::array 的使用有效(也许。C++ 标准不保证您认为它对array 的布局做了什么),因为array&lt;T&gt; 是在没有显式构造函数的情况下定义的。因此,在T 可以轻松复制的范围内,它将可以轻松复制。

如果您要将 C++ 对象复制到 GLSL,则 C++ 类型和布局必须与 GLSL 定义相匹配。 std::vector 绝不匹配任何 GLSL 数组的布局。如果您的 GLSL 定义了一个 X 项数组,那么唯一肯定会匹配的 C++ 类型是一个实际的 X 项数组(同样,必要但不是足够)。

不多不少。

【讨论】:

  • 谢谢你的问题。删除向量并用普通数组替换它们修复了它。感谢您的帮助
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-08-07
  • 1970-01-01
  • 1970-01-01
  • 2019-07-31
  • 2021-08-05
  • 1970-01-01
相关资源
最近更新 更多