Reduce QQuickItemPrivate memory usage

- Avoid mixed type bitfields: There are compilers which generally
  support bitfields, but will not combine members if the type is
  different. Requires explicitly casting bitfield members to bool in a
  few places to avoid ambiguous overloads.
- Move all containment mask members into extraData: extraData will be
  allocated anyway to store the contains meta-method, so we might as
  well move the mask pointer there.
- Do not store quickMask separately; instead, cast mask to QQuickItem on
  demand. qobject_cast<QQuickItem *> is fast, as it only has to check a
  single flag.
- Do not store a QMetaMethod for the mask's contains method. Instead
  store only the index, and retrieve the metaObject on demand.

Change-Id: Ib2120c3b01ea037eb17c6c903a2977c38cd6c40c
Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@qt.io>
This commit is contained in:
Fabian Kosmale 2022-08-08 15:06:59 +02:00
parent b51ad14bc6
commit 3edebbb764
3 changed files with 71 additions and 59 deletions

View File

@ -6373,7 +6373,7 @@ bool QQuickItemPrivate::setEffectiveVisibleRecur(bool newEffectiveVisible)
for (int ii = 0; ii < childItems.count(); ++ii)
childVisibilityChanged |= QQuickItemPrivate::get(childItems.at(ii))->setEffectiveVisibleRecur(newEffectiveVisible);
itemChange(QQuickItem::ItemVisibleHasChanged, effectiveVisible);
itemChange(QQuickItem::ItemVisibleHasChanged, bool(effectiveVisible));
#if QT_CONFIG(accessibility)
if (isAccessible) {
QAccessibleEvent ev(q, effectiveVisible ? QAccessible::ObjectShow : QAccessible::ObjectHide);
@ -6432,7 +6432,7 @@ void QQuickItemPrivate::setEffectiveEnableRecur(QQuickItem *scope, bool newEffec
QQuickDeliveryAgentPrivate::DontChangeSubFocusItem);
}
itemChange(QQuickItem::ItemEnabledHasChanged, effectiveEnable);
itemChange(QQuickItem::ItemEnabledHasChanged, bool(effectiveEnable));
emit q->enabledChanged();
}
@ -6764,7 +6764,7 @@ void QQuickItem::setAntialiasing(bool aa)
d->antialiasing = aa;
d->dirty(QQuickItemPrivate::Antialiasing);
d->itemChange(ItemAntialiasingHasChanged, d->antialiasing);
d->itemChange(ItemAntialiasingHasChanged, bool(d->antialiasing));
emit antialiasingChanged(antialiasing());
}
@ -8217,12 +8217,13 @@ void QQuickItem::setKeepTouchGrab(bool keep)
bool QQuickItem::contains(const QPointF &point) const
{
Q_D(const QQuickItem);
if (d->mask) {
if (d->quickMask)
return d->quickMask->contains(point - d->quickMask->position());
if (d->extra.isAllocated() && d->extra->mask) {
if (auto quickMask = qobject_cast<QQuickItem *>(d->extra->mask))
return quickMask->contains(point - quickMask->position());
bool res = false;
d->extra->maskContains.invoke(d->mask,
QMetaMethod maskContains = d->extra->mask->metaObject()->method(d->extra->maskContainsIndex);
maskContains.invoke(d->extra->mask,
Qt::DirectConnection,
Q_RETURN_ARG(bool, res),
Q_ARG(QPointF, point));
@ -8300,33 +8301,44 @@ bool QQuickItem::contains(const QPointF &point) const
QObject *QQuickItem::containmentMask() const
{
Q_D(const QQuickItem);
return d->mask.data();
if (!d->extra.isAllocated())
return nullptr;
return d->extra->mask.data();
}
void QQuickItem::setContainmentMask(QObject *mask)
{
Q_D(QQuickItem);
const bool extraDataExists = d->extra.isAllocated();
// an Item can't mask itself (to prevent infinite loop in contains())
if (d->mask.data() == mask || mask == static_cast<QObject *>(this))
if (mask == static_cast<QObject *>(this))
return;
// mask is null, and we had no mask
if (!extraDataExists && !mask)
return;
// mask is non-null and the same
if (extraDataExists && d->extra->mask == mask)
return;
QQuickItem *quickMask = qobject_cast<QQuickItem *>(d->mask);
QQuickItem *quickMask = d->extra.isAllocated() ? qobject_cast<QQuickItem *>(d->extra->mask)
: nullptr;
if (quickMask) {
QQuickItemPrivate *maskPrivate = QQuickItemPrivate::get(quickMask);
maskPrivate->registerAsContainmentMask(this, false); // removed from use as my mask
}
if (!extraDataExists)
d->extra.value(); // ensure extra exists
if (mask) {
int methodIndex = mask->metaObject()->indexOfMethod(QByteArrayLiteral("contains(QPointF)"));
if (methodIndex < 0) {
qmlWarning(this) << QStringLiteral("QQuickItem: Object set as mask does not have an invokable contains method, ignoring it.");
return;
}
d->extra.value().maskContains = mask->metaObject()->method(methodIndex);
d->extra->maskContainsIndex = methodIndex;
}
d->mask = mask;
d->extra->mask = mask;
quickMask = qobject_cast<QQuickItem *>(mask);
d->quickMask = quickMask;
if (quickMask) {
QQuickItemPrivate *maskPrivate = QQuickItemPrivate::get(quickMask);
maskPrivate->registerAsContainmentMask(this, true); // telling maskPrivate that "this" is using it as mask

View File

@ -378,6 +378,11 @@ public:
int hideRefCount;
// updated recursively for child items as well
int recursiveEffectRefCount;
// Mask contains() method index
int maskContainsIndex;
// Contains mask
QPointer<QObject> mask;
QSGOpacityNode *opacityNode;
QQuickDefaultClipNode *clipNode;
@ -385,9 +390,6 @@ public:
// subsceneDeliveryAgent is set only if this item is the root of a subscene, not on all items within.
QQuickDeliveryAgent *subsceneDeliveryAgent = nullptr;
// Mask contains() method
QMetaMethod maskContains;
QObjectList resourcesList;
// Although acceptedMouseButtons is inside ExtraData, we actually store
@ -397,7 +399,7 @@ public:
Qt::MouseButtons acceptedMouseButtons;
Qt::MouseButtons acceptedMouseButtonsWithoutHandlers;
QQuickItem::TransformOrigin origin:5;
uint origin:5; // QQuickItem::TransformOrigin
uint transparentForPositioner : 1;
// 26 bits padding
@ -410,9 +412,6 @@ public:
Q_DECLARE_FLAGS(ExtraDataTags, ExtraDataTag)
QLazilyAllocated<ExtraData, ExtraDataTags> extra;
// Contains mask
QPointer<QObject> mask;
QPointer<QQuickItem> quickMask;
// If the mask is an Item, inform it that it's being used as a mask (true) or is no longer being used (false)
virtual void registerAsContainmentMask(QQuickItem * /* maskedItem */, bool /* set */) { }
@ -436,53 +435,53 @@ public:
// Bit 0
quint32 flags:7;
bool widthValidFlag:1;
bool heightValidFlag:1;
bool componentComplete:1;
bool keepMouse:1;
bool keepTouch:1;
bool hoverEnabled:1;
bool smooth:1;
bool antialiasing:1;
bool focus:1;
quint32 widthValidFlag:1;
quint32 heightValidFlag:1;
quint32 componentComplete:1;
quint32 keepMouse:1;
quint32 keepTouch:1;
quint32 hoverEnabled:1;
quint32 smooth:1;
quint32 antialiasing:1;
quint32 focus:1;
// Bit 16
bool activeFocus:1;
bool notifiedFocus:1;
bool notifiedActiveFocus:1;
bool filtersChildMouseEvents:1;
bool explicitVisible:1;
bool effectiveVisible:1;
bool explicitEnable:1;
bool effectiveEnable:1;
bool polishScheduled:1;
bool inheritedLayoutMirror:1;
bool effectiveLayoutMirror:1;
bool isMirrorImplicit:1;
bool inheritMirrorFromParent:1;
bool inheritMirrorFromItem:1;
bool isAccessible:1;
bool culled:1;
quint32 activeFocus:1;
quint32 notifiedFocus:1;
quint32 notifiedActiveFocus:1;
quint32 filtersChildMouseEvents:1;
quint32 explicitVisible:1;
quint32 effectiveVisible:1;
quint32 explicitEnable:1;
quint32 effectiveEnable:1;
quint32 polishScheduled:1;
quint32 inheritedLayoutMirror:1;
quint32 effectiveLayoutMirror:1;
quint32 isMirrorImplicit:1;
quint32 inheritMirrorFromParent:1;
quint32 inheritMirrorFromItem:1;
quint32 isAccessible:1;
quint32 culled:1;
// Bit 32
bool hasCursor:1;
bool subtreeCursorEnabled:1;
bool subtreeHoverEnabled:1;
bool activeFocusOnTab:1;
bool implicitAntialiasing:1;
bool antialiasingValid:1;
quint32 hasCursor:1;
quint32 subtreeCursorEnabled:1;
quint32 subtreeHoverEnabled:1;
quint32 activeFocusOnTab:1;
quint32 implicitAntialiasing:1;
quint32 antialiasingValid:1;
// isTabFence: When true, the item acts as a fence within the tab focus chain.
// This means that the item and its children will be skipped from the tab focus
// chain when navigating from its parent or any of its siblings. Similarly,
// when any of the item's descendants gets focus, the item constrains the tab
// focus chain and prevents tabbing outside.
bool isTabFence:1;
bool replayingPressEvent:1;
bool touchEnabled:1;
bool hasCursorHandler:1;
quint32 isTabFence:1;
quint32 replayingPressEvent:1;
quint32 touchEnabled:1;
quint32 hasCursorHandler:1;
// set true when this item does not expect events via a subscene delivery agent; false otherwise
bool maybeHasSubsceneDeliveryAgent:1;
quint32 maybeHasSubsceneDeliveryAgent:1;
// set true if this item or any child wants QQuickItemPrivate::transformChanged() to visit all children
// (e.g. when parent has ItemIsViewport and child has ItemObservesViewport)
bool subtreeTransformChangedEnabled:1;
quint32 subtreeTransformChangedEnabled:1;
enum DirtyType {
TransformOrigin = 0x00000001,
@ -1002,7 +1001,8 @@ void QQuickItemPrivate::markSortedChildrenDirty(QQuickItem *child)
QQuickItem::TransformOrigin QQuickItemPrivate::origin() const
{
return extra.isAllocated()?extra->origin:QQuickItem::Center;
return extra.isAllocated() ? QQuickItem::TransformOrigin(extra->origin)
: QQuickItem::Center;
}
QSGTransformNode *QQuickItemPrivate::itemNode()

View File

@ -555,7 +555,7 @@ void QQuickDeliveryAgentPrivate::notifyFocusChangesRecur(QQuickItem **items, int
if (item && itemPrivate->notifiedActiveFocus != itemPrivate->activeFocus) {
itemPrivate->notifiedActiveFocus = itemPrivate->activeFocus;
itemPrivate->itemChange(QQuickItem::ItemActiveFocusHasChanged, itemPrivate->activeFocus);
itemPrivate->itemChange(QQuickItem::ItemActiveFocusHasChanged, bool(itemPrivate->activeFocus));
itemPrivate->notifyChangeListeners(QQuickItemPrivate::Focus, &QQuickItemChangeListener::itemFocusChanged, item, reason);
emit item->activeFocusChanged(itemPrivate->activeFocus);
}