使用 glBufferData 将向量的向量传递给 SSBO 时出现段错误
Segfault when passing vector of vectors to SSBO using glBufferData
我试图将向量的向量传递给 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[];
};
每次它在 registerSSBO
内的 glBufferData
上发生段错误
glBufferData(GL_SHADER_STORAGE_BUFFER, getMem(), mBlocks.data(), GL_DYNAMIC_DRAW);
getMem()
在这种情况下 returns 大小为 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;
}
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<T>
是在没有显式构造函数的情况下定义的.因此,在 T
可平凡复制的范围内,它将平凡可复制。
如果您要将 C++ 对象复制到 GLSL,则 C++ 类型和布局必须与 GLSL 定义的相匹配。 std::vector
绝不匹配任何 GLSL 数组的布局。如果您的 GLSL 定义了 X 项的数组,那么唯一肯定会匹配的 C++ 类型是实际的 X 项数组(同样,必要但不足够足够)。
不多也不少。
我试图将向量的向量传递给 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[];
};
每次它在 registerSSBO
glBufferData
上发生段错误
glBufferData(GL_SHADER_STORAGE_BUFFER, getMem(), mBlocks.data(), GL_DYNAMIC_DRAW);
getMem()
在这种情况下 returns 大小为 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;
}
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<T>
是在没有显式构造函数的情况下定义的.因此,在 T
可平凡复制的范围内,它将平凡可复制。
如果您要将 C++ 对象复制到 GLSL,则 C++ 类型和布局必须与 GLSL 定义的相匹配。 std::vector
绝不匹配任何 GLSL 数组的布局。如果您的 GLSL 定义了 X 项的数组,那么唯一肯定会匹配的 C++ 类型是实际的 X 项数组(同样,必要但不足够足够)。
不多也不少。