You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Bullet3d 3.17库内崩溃排查请求:程序偶发退出码139

问题分析与解决:Bullet3D 3.17偶发崩溃(退出码139)

崩溃信息

程序偶发崩溃,退出码139(对应SIGSEGV段错误),堆栈跟踪如下:

#0  0x0000559e3a3e96f2 in removeleaf(btDbvt*, btDbvtNode*) ()
#1  0x0000559e3a3ebfc6 in btDbvt::update(btDbvtNode*, btDbvtAabbMm&) ()
#2  0x0000559e3a3cb303 in btCompoundShape::updateChildTransform(int, btTransform const&, bool) ()
#3  0x0000559e3a3cb45e in btCompoundShape::setLocalScaling(btVector3 const&) ()
#4  0x0000559e3a3cb3ff in btCompoundShape::setLocalScaling(btVector3 const&) ()

相关代码

Asset::ptr_t asset = Asset::loadAsset("box.GLB");
btCollisionShape_ptr shape = asset->createCollisionShape();
shape->setLocalScaling({1, 1, 1});

std::shared_ptr<btCollisionShape> Asset::createCollisionShape() {
  std::shared_ptr<Asset> self = shared_from_this();
  ASSERT(self, "Failed to get self shared? possible constructed via new?");

  class LocalShape {
  public:
    LocalShape(std::shared_ptr<Asset> parent)
        : parent_(parent) {
    }

    std::shared_ptr<Asset> parent_;
    btCompoundShape shape_;
  };

  auto proxy = std::make_shared<LocalShape>(self);
  proxy->shape_.addChildShape(btTransform::getIdentity(), collisionShape_.get());
  return std::shared_ptr<btCollisionShape>(proxy, &(proxy->shape_));
}

核心原因

问题出在shared_ptr的别名构造用法上:

  • 代码中返回的std::shared_ptr<btCollisionShape>是通过proxy(shared_ptr<LocalShape>)别名构造的,它共享proxy的引用计数,但指向的是LocalShape内部的shape_成员。
  • 当所有持有该btCollisionShape指针的shared_ptr销毁时,proxy的引用计数归零,LocalShape对象被销毁,其内部的btCompoundShape也随之被释放。
  • 如果此时Bullet物理世界仍在引用这个已释放的btCompoundShape(比如后续的物理模拟、形状更新操作),就会触发段错误,这也是崩溃偶发的原因——取决于shared_ptr的销毁时机与Bullet操作的时序。

修复方案

方案1:调整LocalShape的继承关系,直接管理btCompoundShape生命周期

让LocalShape继承自btCompoundShape,这样shared_ptr可以直接管理碰撞形状的内存,避免成员变量被提前释放:

std::shared_ptr<btCollisionShape> Asset::createCollisionShape() {
  std::shared_ptr<Asset> self = shared_from_this();
  ASSERT(self, "Failed to get self shared? possible constructed via new?");

  class LocalShape : public btCompoundShape {
  public:
    LocalShape(std::shared_ptr<Asset> parent)
        : parent_(parent) {
    }

    std::shared_ptr<Asset> parent_;
  };

  auto proxy = std::make_shared<LocalShape>(self);
  proxy->addChildShape(btTransform::getIdentity(), collisionShape_.get());
  return proxy;
}

这种方式下,shared_ptr直接持有LocalShape(即btCompoundShape)的实例,只有当所有引用销毁时才会释放内存,确保Bullet操作期间形状始终有效。

方案2:额外检查子形状生命周期

确认collisionShape_(btCompoundShape的子形状)在btCompoundShape的整个使用周期内都有效。由于代码中parent_持有Asset的引用,只要LocalShape存在,Asset就不会被销毁,这部分无需额外调整,但需确保collisionShape_不会被Asset提前释放。

方案3:排查多线程访问问题

如果程序涉及多线程,需确保btCompoundShape的操作(如setLocalScaling)是单线程执行的,Bullet的碰撞形状不是线程安全的,多线程并发操作会导致未定义行为。

内容的提问来源于stack exchange,提问作者Max

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 06:21:10