Details
-
Bug
-
Resolution: Done
-
P0: Blocker
-
None
-
5.11
-
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.