Uploaded image for project: 'Qt'
  1. Qt
  2. QTBUG-69352

Component bookkeeping breaks on backend side when creating QEntity's with parent specified in the ctor after the initial init of the scene

    XMLWordPrintable

Details

    • Bug
    • Resolution: Done
    • P0: Blocker
    • None
    • 5.11
    • Qt3D
    • None
    •  5376853866e49b5a6841c6c41affef5a3d3d4bb5

    Description

      We have something like the following:

      entity1 = new QEntity(parent)
      // ... add components like transforms on it
      entity2 = new QEntity(entity1)
      // ... add components like transforms on it
      
      // and then:
      entity1->addComponent(some_QLayer)
      entity2->addComponent(some_QLayer)
      

      where these entities are created "on the fly", meaning the scene is up and running already (no problems when doing this pattern before kicking off the aspect engine).

      Now, in our case almost always the addComponent(QLayer) attempt on entity2 is "lost" internally on the backend side, meaning the backend node (Entity) does not actually know that there is a Layer on it (some_QLayer's corresponding Layer's id will not be listed in entity2's corresponding Entity's layerIds() even though on the frontend side everything looks fine), thus resulting in broken filtering and rendering.

      The immediate problem is d->m_changeArbiter being null in QEntity::addComponent, which leads to not doing d->notifyObservers about the component addition. This in turn breaks the internal bookkeeping on the backend side. What exactly triggers this is not clear however.

      entity1 works as expected.

      Now, changing to separate setParent calls

      entity1 = new QEntity
      entity1->setParent(parent)
      entity2 = new QEntity
      entity2->setParent(entity1)
      entity1->addComponent(some_QLayer)
      entity2->addComponent(some_QLayer)
      

      makes it all work as expected. Hence the key to trigger these issues is to specify a parent in the constructor..

      almost always because very rarely things show up as expected, there could some scheduling thing in there due to an async invokeMethod or something

      At first glance the obvious difference stems from QNodePrivate::init:

      void QNodePrivate::init(QNode *parent)
      {
          if (!parent)
              return;
      
          // If we have a QNode parent that has a scene (and hence change arbiter),
          // copy these to this QNode. If valid, then also notify the backend
          // in a deferred way when the object is fully constructed. This is delayed
          // until the object is fully constructed as it involves calling a virtual
          // function of QNode.
          m_parentId = parent->id();
          const auto parentPrivate = get(parent);
          m_scene = parentPrivate->m_scene;
          Q_Q(QNode);
          if (m_scene) {
              // schedule the backend notification and scene registering -> set observers through scene
              QMetaObject::invokeMethod(q, "_q_postConstructorInit", Qt::QueuedConnection);
          }
      }
      

      with a suspicious async call. In the explicit setParent case _q_setParentHelper seems to take care of similar initialization without any need for async stuff (since we are not in the ctor anymore).

      Perhaps related to QTBUG-65829 ?

      I realize the description is a bit vague right now with no easy way to reproduce. We'll see what we can do about clearing this up...but the workaround looks promising for now.

      Attachments

        For Gerrit Dashboard: QTBUG-69352
        # Subject Branch Project Status CR V

        Activity

          People

            lemire_p Paul Lemire
            lagocs Laszlo Agocs
            Votes:
            0 Vote for this issue
            Watchers:
            4 Start watching this issue

            Dates

              Created:
              Updated:
              Resolved:

              Gerrit Reviews

                There are no open Gerrit changes