mtsamis / box2d-optimized

A very fast and scalable physics engine, based on Box2D.
MIT License
73 stars 11 forks source link

Crash after creating 28 dynamic objects at runtime. #9

Closed RonYanDaik closed 5 months ago

RonYanDaik commented 1 year ago

For some reason after proceeding with Step function if i try create new dynamic object and number of dynamic object is more then 28 I get crash at ContactManager::FindNewContacts.

I don't have such issue with original lib. Am I suppose to suppose to set up something? Thank you.

image

image

inside b2BroadPhase::UpdateAndQuery(T* callback) image

RonYanDaik commented 1 year ago

I did some more testing and found out that the crash happens depending from number of static bodies (or fixtures) in the scene.

Crash happens: image

crash does not happen: image

crash does not happen: image

RonYanDaik commented 1 year ago

Apparently problem is here:

image

m_rootStatic members ore not valid (not null but also not a value). Thats why (root->IsLeaf()) gives false May be it got somewhere deleted and not nullified?

mtsamis commented 1 year ago

Hi, thanks for the detailed summary. In general the broadphase detector will use slightly different algorithms depending on the ratio of dynamic to static bodies because then it can do faster detection. This can be seen here https://github.com/mtsamis/box2d-optimized/blob/505bb43175e6126d13dba398c2cc5d618c818f89/include/box2d/b2_broad_phase.h#L278-L284 where the heuristic is enabled when the number of static bodies is > 1/8 of the total number of bodies. It looks like m_rootStatic is left invalid in one of the transitions of using the other broadphase strategy.

Do you have code that can help me reproduce that? I can have a look.

RonYanDaik commented 1 year ago

I've got new exception. this time it tries to access right member which is invalid.

image

RonYanDaik commented 1 year ago

Do you have code that can help me reproduce that? I can have a look.

I think I've managed to reproduce it in Tiles test.

this is the code for entire Tiles class. I've changed static ground creation and added creation of bodies in Step() function.

class Tiles : public Test
{
public:
    enum
    {
        e_count = 2
      };

    Tiles()
    {
        m_fixtureCount = 0;
        b2Timer timer;

        {
            float a = 10.5f;
            b2BodyDef bd;
            bd.position.y = -a;

            int32 N = 4;
            int32 M = 2;
            b2Vec2 position;
            position.y = 0.0f;
            for (int32 j = 0; j < M; ++j)
            {
                position.x = -N * a;
                for (int32 i = 0; i < N; ++i)
                {
                    bd.position = position;
                    b2Body* ground = m_world->CreateBody(&bd);

                    b2PolygonShape shape;
                    shape.SetAsBox(a, a*0.5);
                    ground->CreateFixture(&shape, 0.0f);
                    ++m_fixtureCount;
                    position.x += 2.0f * a;
                    //code line bellow wil lbreak static bodies
                    //ground->SetTransform(position,0.0f);
                }
                position.y -= 2.0f * a;
            }

        }

        {
            float a = 0.5f;
            b2PolygonShape shape;
            shape.SetAsBox(a, a);

            b2Vec2 x(-7.0f, 0.75f);
            b2Vec2 y;
            b2Vec2 deltaX(0.5625f, 1.25f);
            b2Vec2 deltaY(1.125f, 0.0f);

            for (int32 i = 0; i < e_count; ++i)
            {
                y = x;

                for (int32 j = i; j < e_count; ++j)
                {
                    b2BodyDef bd;
                    bd.type = b2_dynamicBody;
                    bd.position = y;
                    bd.position.y = 50 - bd.position.y;

                    b2Body* body = m_world->CreateBody(&bd);
                    body->CreateFixture(&shape, 5.0f);
                    ++m_fixtureCount;
                    y += deltaY;
                }

                x += deltaX;
            }
        }

        m_createTime = timer.GetMilliseconds();
    }

    void Step(Settings& settings) override
    {
        /*
        const b2ContactManager& cm = m_world->GetContactManager();
        int32 height = cm.m_broadPhase.GetTreeHeight();
        int32 leafCount = cm.m_broadPhase.GetCount();
        int32 minimumNodeCount = 2 * leafCount - 1;
        float minimumHeight = ceilf(logf(float(minimumNodeCount)) / logf(2.0f));
        g_debugDraw.DrawString(5, m_textLine, "dynamic tree height = %d, min = %d", height, int32(minimumHeight));
        m_textLine += m_textIncrement;
        */

        Test::Step(settings);

        g_debugDraw.DrawString(5, m_textLine, "create time = %6.2f ms, fixture count = %d",
          m_createTime, m_fixtureCount);
        m_textLine += m_textIncrement;

        float a = 0.5f;
        b2PolygonShape shape;
        shape.SetAsBox(a, a);
        static int cnt =0;

        if(cnt<1500)
        {
            cnt++;

            b2BodyDef bd;
            bd.type = b2_dynamicBody;
            bd.position.y = 50;

            b2Body* body = m_world->CreateBody(&bd);
            body->CreateFixture(&shape, 5.0f);
        }
    }

    static Test* Create()
    {
        return new Tiles;
    }

    int32 m_fixtureCount;
    float m_createTime;
};

static int testIndex = RegisterTest("Benchmark", "Tiles", Tiles::Create);

I've commented line int32 height = cm.m_broadPhase.GetTreeHeight(); because it also gives exception.

RonYanDaik commented 1 year ago

Maybe I'm onto something here? Looks like oldNodes points to m_rootStatic and here its being freed withb2Free(oldNodes). And from here on m_rootStatic has invalid values. image

RonYanDaik commented 1 year ago

And from here on m_rootStatic has invalid values.

Adding m_rootStatic = nullptr; there fixed the crash but I have no idea if it will break speed of algorithm or not.