为什么 push_back 会出现分段错误?
Why does push_back give a segmentation error?
项目简介:
我正在开发 Opengl ray-tracer,它能够加载 obj 文件并 ray-trace 它。我的应用程序使用 assimp 加载 obj 文件,然后使用着色器存储 objects 将所有三角形面(顶点和索引)发送到片段着色器。基本结构是将结果从片段着色器渲染到四边形。
当我加载更大的 obj-s(超过 100 个三角形)时,计算机需要花费很多时间来完成交集,所以我开始创建一个 BVH-tree 来加快这个过程。我的 BVH 根据 AABB 中包含的三角形面的平均中值递归地将 space 分成两个 axis-aligned-bounding-boxes。
我成功构建了 BVH 树结构(CPU),现在我想将它转换为一个简单的数组,然后将它发送到片段着色器(到着色器存储缓冲区)。
这里是负责将BVH根节点转成数组的方法:
BvhNode bvhNode; //global variable
BvhNode* putNodeIntoArray() {
int size=bvhNode.getNumberOfNodes();
BvhNode nodesArray[size];
int current_index = 0;
vector<BvhNode> tempNodes;
tempNodes.push_back(bvhNode);
BvhNode current_node;
while (!tempNodes.empty()) {
current_node = tempNodes.at(0);
tempNodes.erase(tempNodes.begin());
nodesArray[current_index] = current_node;
if(!current_node.isLeaf)
{
tempNodes.push_back(current_node.children.at(0)); // Segmentation error occurs here!
tempNodes.push_back(current_node.children.at(1));
}
current_index++;
}
return nodesArray;
}
关于问题:
我不知道为什么,但它给了我一个分段错误,当我想 push_back 第一个 child 到 tempNodes
向量(确切的位置可以是由上面的评论看到)。在我看来 current_node.children.at(0)
不存在,但实际上根据调试器存在。
我尝试编写引用 (&) 运算符:tempNodes.push_back(¤t_node.children.at(0));
,但在这种情况下,它给了我 objects 的奇怪坐标。我试图将函数中的变量定义为全局变量 - 试图避免范围问题 - 以及将 current_node
变量定义为指针。不幸的是 none 给了我更好的结果。
这是我的 BvhNode class,如果有帮助:
class BvhNode {
public:
BBox bBox;
int depthOfNode;
vector<BvhNode> children;
int order;
bool isLeaf;
bool createdEmpty = false;
vector<glm::vec3> primitiveCoordinates;
BvhNode() {}
BvhNode(BvhNode *pNode) {}
BvhNode(vector<glm::vec3> &primitiveCoordinates) {
this->primitiveCoordinates = primitiveCoordinates; }
void buildTree(vector<glm::vec3>& indicesPerFaces, int depth) {... }
更新 1:
我根据评论更新了方法。所以我将返回值的类型更改为 vector insted of BvhNode*。该算法工作正常,直到它到达将叶节点放入 std::vector 的过程。所以当它开始将图形的最后一层放入向量时,它给了我这个错误:
Program received signal SIGSEGV, Segmentation fault.
0x00007fbda4d69c01 in __GI___libc_free (mem=0x555be3a9dba0) at malloc.c:3123
3123 malloc.c: No such file or directory.
我设法将七个节点(表示树的所有深度级别,叶子级别除外)放入向量中。我也尝试 运行 valgring,但实际上 valgrind 没有给我任何错误,不像在 CLion 中那样。
这是我修改的方法。我评论了分段错误的地方和修复。
BvhNode bvhNode;
vector<BvhNode> putNodeIntoArray() {
int size=bvhNode.getNumberOfNodes();
// FIX: I modified the array into an std::vector
vector<BvhNode> nodesArray(size);
int current_index = 0;
vector<BvhNode> tempNodes;
tempNodes.push_back(bvhNode);
BvhNode current_node;
while (!tempNodes.empty()) {
current_node = tempNodes.front();// Segmentation error!!
tempNodes.erase(tempNodes.begin());
nodesArray.at(current_index)=current_node;
nodesArray.at(current_index).children.clear();
// FIX: I also modified this not to iterate through leaves' children, because they don't exist.
if(!current_node.children.empty())
{
tempNodes.push_back(current_node.children.at(0));
tempNodes.push_back(current_node.children.at(1));
}
current_index++;
}
return nodesArray;
}
您的向量按值在各处存储 BvhNode
。
这意味着每次你 push_back
一个节点,它的复制构造函数被调用,它又复制节点内的 children
向量成员,它复制它自己的元素等。
这基本上会导致每次插入或删除节点时完整的子树 copied/freed。
这反过来会导致内存碎片,最终会导致向量重新分配失败并导致段错误。
没有完整的代码,我可以推荐这两个东西:
将子项存储为(智能)指针而不是按值
为向量创建自定义分配器以启用更细粒度的调试,并检查分配失败
实际上问题出在 Bvh 树的创建上。因为我想以 2*i+1 和 2*i+2 的形式达到 children ,所以我使用一种方法使二叉树满(每一层都有最大节点数)。在这种方法中,我不得不将空节点推送到树中。
我是这样创建空节点的:
BvhNode emptyNode;
而不是这个:
BvhNode *emptyNode = new BvhNode();
因此,当方法完成时,emptyNode
的生命周期结束。这导致了分段错误。
此外,还有一个问题,正如 Balázs Kovacsis 指出的那样,我在创建平面二叉树期间按值存储了 BvhNode。这个选项使应用程序变慢了很多,这是另一个可能导致分段错误的地方。
项目简介:
我正在开发 Opengl ray-tracer,它能够加载 obj 文件并 ray-trace 它。我的应用程序使用 assimp 加载 obj 文件,然后使用着色器存储 objects 将所有三角形面(顶点和索引)发送到片段着色器。基本结构是将结果从片段着色器渲染到四边形。
当我加载更大的 obj-s(超过 100 个三角形)时,计算机需要花费很多时间来完成交集,所以我开始创建一个 BVH-tree 来加快这个过程。我的 BVH 根据 AABB 中包含的三角形面的平均中值递归地将 space 分成两个 axis-aligned-bounding-boxes。
我成功构建了 BVH 树结构(CPU),现在我想将它转换为一个简单的数组,然后将它发送到片段着色器(到着色器存储缓冲区)。
这里是负责将BVH根节点转成数组的方法:
BvhNode bvhNode; //global variable
BvhNode* putNodeIntoArray() {
int size=bvhNode.getNumberOfNodes();
BvhNode nodesArray[size];
int current_index = 0;
vector<BvhNode> tempNodes;
tempNodes.push_back(bvhNode);
BvhNode current_node;
while (!tempNodes.empty()) {
current_node = tempNodes.at(0);
tempNodes.erase(tempNodes.begin());
nodesArray[current_index] = current_node;
if(!current_node.isLeaf)
{
tempNodes.push_back(current_node.children.at(0)); // Segmentation error occurs here!
tempNodes.push_back(current_node.children.at(1));
}
current_index++;
}
return nodesArray;
}
关于问题:
我不知道为什么,但它给了我一个分段错误,当我想 push_back 第一个 child 到 tempNodes
向量(确切的位置可以是由上面的评论看到)。在我看来 current_node.children.at(0)
不存在,但实际上根据调试器存在。
我尝试编写引用 (&) 运算符:tempNodes.push_back(¤t_node.children.at(0));
,但在这种情况下,它给了我 objects 的奇怪坐标。我试图将函数中的变量定义为全局变量 - 试图避免范围问题 - 以及将 current_node
变量定义为指针。不幸的是 none 给了我更好的结果。
这是我的 BvhNode class,如果有帮助:
class BvhNode {
public:
BBox bBox;
int depthOfNode;
vector<BvhNode> children;
int order;
bool isLeaf;
bool createdEmpty = false;
vector<glm::vec3> primitiveCoordinates;
BvhNode() {}
BvhNode(BvhNode *pNode) {}
BvhNode(vector<glm::vec3> &primitiveCoordinates) {
this->primitiveCoordinates = primitiveCoordinates; }
void buildTree(vector<glm::vec3>& indicesPerFaces, int depth) {... }
更新 1:
我根据评论更新了方法。所以我将返回值的类型更改为 vector insted of BvhNode*。该算法工作正常,直到它到达将叶节点放入 std::vector 的过程。所以当它开始将图形的最后一层放入向量时,它给了我这个错误:
Program received signal SIGSEGV, Segmentation fault.
0x00007fbda4d69c01 in __GI___libc_free (mem=0x555be3a9dba0) at malloc.c:3123
3123 malloc.c: No such file or directory.
我设法将七个节点(表示树的所有深度级别,叶子级别除外)放入向量中。我也尝试 运行 valgring,但实际上 valgrind 没有给我任何错误,不像在 CLion 中那样。
这是我修改的方法。我评论了分段错误的地方和修复。
BvhNode bvhNode;
vector<BvhNode> putNodeIntoArray() {
int size=bvhNode.getNumberOfNodes();
// FIX: I modified the array into an std::vector
vector<BvhNode> nodesArray(size);
int current_index = 0;
vector<BvhNode> tempNodes;
tempNodes.push_back(bvhNode);
BvhNode current_node;
while (!tempNodes.empty()) {
current_node = tempNodes.front();// Segmentation error!!
tempNodes.erase(tempNodes.begin());
nodesArray.at(current_index)=current_node;
nodesArray.at(current_index).children.clear();
// FIX: I also modified this not to iterate through leaves' children, because they don't exist.
if(!current_node.children.empty())
{
tempNodes.push_back(current_node.children.at(0));
tempNodes.push_back(current_node.children.at(1));
}
current_index++;
}
return nodesArray;
}
您的向量按值在各处存储 BvhNode
。
这意味着每次你 push_back
一个节点,它的复制构造函数被调用,它又复制节点内的 children
向量成员,它复制它自己的元素等。
这基本上会导致每次插入或删除节点时完整的子树 copied/freed。
这反过来会导致内存碎片,最终会导致向量重新分配失败并导致段错误。
没有完整的代码,我可以推荐这两个东西:
将子项存储为(智能)指针而不是按值
为向量创建自定义分配器以启用更细粒度的调试,并检查分配失败
实际上问题出在 Bvh 树的创建上。因为我想以 2*i+1 和 2*i+2 的形式达到 children ,所以我使用一种方法使二叉树满(每一层都有最大节点数)。在这种方法中,我不得不将空节点推送到树中。
我是这样创建空节点的:
BvhNode emptyNode;
而不是这个:
BvhNode *emptyNode = new BvhNode();
因此,当方法完成时,emptyNode
的生命周期结束。这导致了分段错误。
此外,还有一个问题,正如 Balázs Kovacsis 指出的那样,我在创建平面二叉树期间按值存储了 BvhNode。这个选项使应用程序变慢了很多,这是另一个可能导致分段错误的地方。