C++ Python 模块在 Blender 中崩溃,但在 Python 控制台中不会崩溃

C++ Python module crashes in Blender but not in Python console

本文关键字:崩溃 Python 控制台 但在 Blender C++ 模块      更新时间:2023-10-16

>问题

我正在尝试使用 Blender 2.82a 从 Python 3.7 调用我的C++代码(也发生在 2.83 中(。代码应优化相机路径。它可以在没有Blender的情况下使用,但是,我使用Blender设置具有相机路径的场景并查询场景中的深度值。

我尝试在 C++ 和 Python 控制台中调用优化函数。两者都没有任何问题。问题是,当我在Blender中调用它时,Blender崩溃了。

这是崩溃报告

# Blender 2.83.0, Commit date: 2020-06-03 14:38, Hash 211b6c29f771
# backtrace
./blender(BLI_system_backtrace+0x1d) [0x6989e9d]
./blender() [0xc1548f]
/lib/x86_64-linux-gnu/libpthread.so.0(+0x153c0) [0x7fa5fb3dc3c0]
/lib/x86_64-linux-gnu/libpthread.so.0(raise+0xcb) [0x7fa5fb3dc24b]
/lib/x86_64-linux-gnu/libpthread.so.0(+0x153c0) [0x7fa5fb3dc3c0]
./blender(_ZN5Eigen8IOFormatD1Ev+0xa3) [0x179bc43]
/home/name/Programs/blender-2.83.0-linux64/2.83/python/lib/python3.7/optFlowCam.cpython-37m-x86_64-linux-gnu.so(_Z2_zRK6CameraRKN5Eigen6MatrixIdLi9ELi1ELi0ELi9ELi1EEEiiRKSt8functionIFdRK3RayEE+0x2e2) [0x7fa5d1538e72]
/home/name/Programs/blender-2.83.0-linux64/2.83/python/lib/python3.7/optFlowCam.cpython-37m-x86_64-linux-gnu.so(_ZN11OpticalFlow13GradPathErrorERKSt6vectorI6CameraSaIS1_EEiiRKSt8functionIFdRK3RayEEd+0x5a7) [0x7fa5d1539c77]
/home/name/Programs/blender-2.83.0-linux64/2.83/python/lib/python3.7/optFlowCam.cpython-37m-x86_64-linux-gnu.so(_Z16_gradientDescentRKSt6vectorI6CameraSaIS0_EEiiRKSt8functionIFdRK3RayEEd+0x54b) [0x7fa5d153b5fb]
/home/name/Programs/blender-2.83.0-linux64/2.83/python/lib/python3.7/optFlowCam.cpython-37m-x86_64-linux-gnu.so(_ZN11OpticalFlow12OptimizePathERKSt6vectorI6CameraSaIS1_EEiiRKSt8functionIFdRK3RayEEdNS_18OptimizationMethodE+0x22) [0x7fa5d153bcb2]
/home/name/Programs/blender-2.83.0-linux64/2.83/python/lib/python3.7/optFlowCam.cpython-37m-x86_64-linux-gnu.so(+0x3d910) [0x7fa5d1533910]
/home/name/Programs/blender-2.83.0-linux64/2.83/python/lib/python3.7/optFlowCam.cpython-37m-x86_64-linux-gnu.so(+0x317ed) [0x7fa5d15277ed]
./blender(_PyMethodDef_RawFastCallKeywords+0x2f3) [0x570f373]
./blender(_PyCFunction_FastCallKeywords+0x25) [0x570f3f5]
./blender(_PyEval_EvalFrameDefault+0x7468) [0xc0fb48]
./blender(_PyEval_EvalCodeWithName+0xadc) [0x57c0d8c]
./blender(PyEval_EvalCodeEx+0x3e) [0x57c0ebe]
./blender(PyEval_EvalCode+0x1b) [0x57c0eeb]
./blender() [0x11f35ac]
./blender() [0x1600cde]
./blender() [0xec6a93]
./blender() [0xec6d07]
./blender(WM_operator_name_call_ptr+0x1a) [0xec720a]
./blender() [0x14f2082]
./blender() [0x15020d5]
./blender() [0xeca877]
./blender() [0xecaecc]
./blender(wm_event_do_handlers+0x310) [0xecb5e0]
./blender(WM_main+0x20) [0xec2230]
./blender(main+0x321) [0xb4bfd1]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf3) [0x7fa5facb50b3]
./blender() [0xc11c0c]

我正在使用 Eigen 进行线性代数计算,并使用 pybind11 将其编译成 python 模块。特征类型都是固定大小的,因为我不需要它们是动态的(问题的可能原因(。我在 Ubuntu 20.04 上使用gcc (Ubuntu 9.3.0-10ubuntu2) 9.3.0进行编译。我目前使用 c++11 标准,但这不是必需的。

目前的调查结果

使用faulthandler.enabled((它给了我

Fatal Python error: Segmentation fault
Current thread 0x00007fa5fab18040 (most recent call first):
File "/Text", line 16 in <module>

我已经发现它在程序中的同一行崩溃,这是当矩阵向量乘法的结果应该返回并插入到 std::vector 中时。我先打印了矢量和矩阵,以确保它们不包含垃圾并且工作正常。

我还尝试将其存储在中间变量中并打印出来,然后在打印时崩溃。乘法本身似乎不会导致段错误

我想,我尝试直接从 Blender 调用它发生的函数,但随后它工作并返回没有段错误的结果。

我怀疑这是某种内存对齐问题,并尝试了此处和 Eigen 纪录片中建议的所有内容。也就是说,我在每个 std::vector 中使用Eigen::aligned_allocator,仅将特征对象作为const &传递,并在具有特征类型成员的相机和光线类中具有EIGEN_MAKE_ALIGNED_OPERATOR_NEW

使用#define EIGEN_DONT_VECTORIZE#define EIGEN_DISABLE_UNALIGNED_ARRAY_ASSERT只能给我带来部分成功。它不再像以前那样在同一行崩溃。奇怪的是,如果我在返回之前也添加一个 cout,函数就会完成并返回。

发生碰撞的部件

该项目不是公开的,C++代码相当冗长,所以我只包含其中的一部分。如果您需要更多,请告诉我。其余的看起来非常相似,所以如果有什么概念上的错误,这里也可能是错误的。这不是一个最小的例子(它包含一些调试打印(,因为我不知道为什么会发生这种情况,并且错误并不总是在同一部分。

// in header
// this helped somehow
#define EIGEN_DONT_VECTORIZE
#define EIGEN_DISABLE_UNALIGNED_ARRAY_ASSERT
// ***********************************
#include <iostream>
#include <numeric>
#include <array>
#include <vector>
#include <Eigen/Dense>
#include <Eigen/StdVector>
#include "matrix_types.h"
#include "camera.h"
// *******************************************************************************
// Vector9d is a typedef
// in cpp
Vector9d _z(const Camera& cam, const Vector9d& derivX, int x_dir, int y_dir, const std::function<double(const Ray&)>& depthTest){
Eigen::IOFormat HeavyFmt(Eigen::FullPrecision, 0, ", ", ",n", "[", "]", "[", "]");
Matrix9d M0 = OpticalFlow::M(cam, x_dir, y_dir, depthTest);
std::cout << "M_n" << M0.format(HeavyFmt) << "n" <<std::endl;
Vector9d z = M0 * derivX;
return z;
}
std::vector<Vector9d, Eigen::aligned_allocator<Vector9d>> OpticalFlow::GradPathError(const std::vector<Camera>& pathPositions, int x_dir, int y_dir, const std::function<double(const Ray&)>& depthTest, double h){
int n = pathPositions.size()-1;
Eigen::IOFormat HeavyFmt(Eigen::FullPrecision);
Eigen::IOFormat HeavyMtxFmt(Eigen::FullPrecision, 0, ", ", ",n", "[", "]", "[", "]");
std::vector<Vector9d, Eigen::aligned_allocator<Vector9d>> gradPE;
gradPE.reserve(n+1);
// save values that will be used more often in calculations
std::vector<Vector9d, Eigen::aligned_allocator<Vector9d>> derivXs;
std::vector<Vector9d, Eigen::aligned_allocator<Vector9d>> zs;
std::vector<std::array<Matrix9d, 9>> gradMs;
derivXs.reserve(n+1);
zs.reserve(n+1);
gradMs.reserve(n+1);
for(int i = 0; i<n+1; ++i){
derivXs.push_back(_derivCamPath(pathPositions, i));
Camera cam = pathPositions[i];
Vector9d derivX = _derivCamPath(pathPositions, i);
zs.push_back(_z(cam, derivX, x_dir, y_dir, depthTest)); // <--- crashed here, if vectorization not turned off
gradMs.push_back(GradM(cam, x_dir, y_dir, depthTest, h));
}
for(int i = 0; i<n+1; ++i){
Vector9d derivZ = _derivZ(zs, i);
std::cout << "Zt_" << i << "n" << derivZ.format(HeavyFmt) << "n" << std::endl;
gradPE.push_back(1.0/(n+1) * _w(derivZ, derivXs[i], gradMs[i]));
}
// if this is included and vectorization turned off, it doesn't crash
// std::cout << "end" << std::endl;
return gradPE; // <-- crash here if vectorization is off
}

我希望有人能帮助我找到原因,或者我可以尝试进一步追踪它。我对C++不是很有经验,所以代码可能有明显的问题。

我想我找到了原因。

这一行实际上./blender(_ZN5Eigen8IOFormatD1Ev+0xd3) [0x2041673]以一种不太可读的格式命名了罪魁祸首。我使用 gdb 调试来自 Blender 的 python 调用,在回溯中,相同的行#0 0x0000000002041673 in Eigen::IOFormat::~IOFormat() ()

问题是我在程序中使用Eigen::IOFormat来调试打印矩阵和向量,以便更轻松地复制到另一个我只想检查值是否正确的程序。你可以在我在问题中发布的摘录代码中看到它。我只在这两个函数中使用它,而段错误只发生在这两个函数中。可能还有其他问题,但就目前而言,它似乎有效。