1// Copyright (C) 2019 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
4#ifndef QSSGOBJECT_P_H
5#define QSSGOBJECT_P_H
6
7//
8// W A R N I N G
9// -------------
10//
11// This file is not part of the Qt API. It exists purely as an
12// implementation detail. This header file may change from version to
13// version without notice, or even be removed.
14//
15// We mean it.
16//
17
18#include "qquick3dobject.h"
19
20#include "qtquick3dglobal_p.h"
21
22#include "qquick3dobjectchangelistener_p.h"
23
24#include "qquick3dscenemanager_p.h"
25
26#include <private/qobject_p.h>
27#include <private/qquickstate_p.h>
28#include <private/qqmlnotifier_p.h>
29#include <private/qlazilyallocated_p.h>
30#include <private/qssgrendergraphobject_p.h>
31#include <QtQuick3DUtils/private/qquick3dprofiler_p.h>
32
33QT_BEGIN_NAMESPACE
34
35class QQuick3DItem2D;
36
37class Q_QUICK3D_PRIVATE_EXPORT QQuick3DObjectPrivate : public QObjectPrivate
38{
39 Q_DECLARE_PUBLIC(QQuick3DObject)
40public:
41 using Type = QSSGRenderGraphObject::Type;
42
43 struct ConnectionKey
44 {
45 using Handle = void (QQuick3DObject::*)(QObject *);
46 QObject *context = nullptr;
47 Handle unusable = nullptr;
48 friend bool operator==(const ConnectionKey &a, const ConnectionKey &b) noexcept { return (a.context == b.context) && (a.unusable == b.unusable); }
49 };
50 using ConnectionMap = QHash<ConnectionKey, QMetaObject::Connection>;
51
52 template<typename SceneContext, typename CallContext, typename Setter>
53 static void attachWatcherPriv(SceneContext *sceneContext, CallContext *callContext, Setter setter, QQuick3DObject *newO, QObject *oldO)
54 {
55 static_assert(std::is_base_of_v<QQuick3DObject, SceneContext>, "The scene context must be a QQuick3DObject");
56 static_assert(std::is_member_function_pointer_v<Setter>, "The assumption is that the setter is a member function!");
57 static_assert(sizeof(ConnectionKey::Handle) >= sizeof(Setter), "The handle needs to be able to store the value of the setter");
58 // Sanity check: Make sure we have a valid context. If the sceneContext != callContext and we're here because the
59 // watched object just got destroyed by the parent (QObject dtor) and that parent also is used as the sceneContext
60 // then there's nothing more to do; all involved parties are being destroyed, so just bail out.
61 const bool validContext = static_cast<QObject *>(sceneContext) != static_cast<QObject *>(callContext) ? qobject_cast<QQuick3DObject *>(sceneContext) != nullptr : true;
62 if (validContext) {
63 auto sceneManager = QQuick3DObjectPrivate::get(sceneContext)->sceneManager;
64 auto &connectionMap = QQuick3DObjectPrivate::get(sceneContext)->connectionMap;
65 union
66 {
67 Setter s;
68 ConnectionKey::Handle h;
69 }; s = setter;
70 ConnectionKey key{.context: static_cast<QObject *>(callContext), .unusable: h};
71 // disconnect previous destruction listener
72 if (oldO) {
73 // NOTE: If the old object is inside the QObject's dtor (e.g., QObject::destroyed) we can't
74 // call deref (and there's no point anymore either).
75 if (auto old3dO = qobject_cast<QQuick3DObject *>(object: oldO))
76 QQuick3DObjectPrivate::derefSceneManager(obj: old3dO);
77
78 auto it = connectionMap.constFind(key);
79 if (it != connectionMap.cend()) {
80 QObject::disconnect(*it);
81 connectionMap.erase(it);
82 }
83 }
84
85 // Watch new object
86 if (newO) {
87 if (sceneManager)
88 QQuick3DObjectPrivate::refSceneManager(newO, *sceneManager);
89 auto connection = QObject::connect(newO, &QObject::destroyed, callContext, [callContext, setter](){ (callContext->*setter)(nullptr); });
90 connectionMap.insert(key, connection);
91 }
92 }
93 }
94
95 /*!
96 Attach a object-destroyed-watcher to an object that's not owned.
97 There are few checks here just to keep it simple
98 (The compiler should still fail with a varying degree of helpful messages when used incorrectly).
99
100 \a sceneContext - ususally the same as the callContext and only different if the calledContext is a non-QQuick3DObject class
101 (as is the case for QQuick3DShaderUtilsTextureInput)!
102 \a callContext - The object watching another object
103 \a setter - The function/slot that is called for the object (context).
104 \a newO - The new object being watched
105 \b oldO - The previous object that should no longer be watched.
106
107 Note: The \a setter is a function that takes one argument with a discardable return value.
108 */
109 template<typename Context, typename Setter, typename Object3D>
110 static void attachWatcher(Context *context, Setter setter, Object3D *newO, Object3D *oldO)
111 {
112 attachWatcherPriv(context, context, setter, newO, oldO);
113 }
114
115 static QQuick3DObjectPrivate *get(QQuick3DObject *item) { return item->d_func(); }
116 static const QQuick3DObjectPrivate *get(const QQuick3DObject *item) { return item->d_func(); }
117 static QSSGRenderGraphObject *updateSpatialNode(QQuick3DObject *o, QSSGRenderGraphObject *n) { return o->updateSpatialNode(node: n); }
118
119 explicit QQuick3DObjectPrivate(Type t);
120 ~QQuick3DObjectPrivate() override;
121 void init(QQuick3DObject *parent);
122
123 QQmlListProperty<QObject> data();
124 QQmlListProperty<QObject> resources();
125 QQmlListProperty<QQuick3DObject> children();
126
127 QQmlListProperty<QQuickState> states();
128 QQmlListProperty<QQuickTransition> transitions();
129
130 QString state() const;
131 void setState(const QString &);
132
133 // data property
134 static void data_append(QQmlListProperty<QObject> *, QObject *);
135 static qsizetype data_count(QQmlListProperty<QObject> *);
136 static QObject *data_at(QQmlListProperty<QObject> *, qsizetype);
137 static void data_clear(QQmlListProperty<QObject> *);
138
139 // resources property
140 static QObject *resources_at(QQmlListProperty<QObject> *, qsizetype);
141 static void resources_append(QQmlListProperty<QObject> *, QObject *);
142 static qsizetype resources_count(QQmlListProperty<QObject> *);
143 static void resources_clear(QQmlListProperty<QObject> *);
144
145 // children property
146 static void children_append(QQmlListProperty<QQuick3DObject> *, QQuick3DObject *);
147 static qsizetype children_count(QQmlListProperty<QQuick3DObject> *);
148 static QQuick3DObject *children_at(QQmlListProperty<QQuick3DObject> *, qsizetype);
149 static void children_clear(QQmlListProperty<QQuick3DObject> *);
150
151 void _q_resourceObjectDeleted(QObject *);
152 void _q_cleanupContentItem2D();
153 quint64 _q_createJSWrapper(QV4::ExecutionEngine *engine);
154
155 enum ChangeType {
156 Geometry = 0x01,
157 SiblingOrder = 0x02,
158 Visibility = 0x04,
159 Opacity = 0x08,
160 Destroyed = 0x10,
161 Parent = 0x20,
162 Children = 0x40,
163 Rotation = 0x80,
164 ImplicitWidth = 0x100,
165 ImplicitHeight = 0x200,
166 Enabled = 0x400,
167 };
168
169 Q_DECLARE_FLAGS(ChangeTypes, ChangeType)
170
171 struct ChangeListener
172 {
173 using ChangeTypes = QQuick3DObjectPrivate::ChangeTypes;
174
175 ChangeListener(QQuick3DObjectChangeListener *l = nullptr, ChangeTypes t = {}) : listener(l), types(t) {}
176
177 ChangeListener(QQuick3DObjectChangeListener *l) : listener(l), types(Geometry) {}
178
179 bool operator==(const ChangeListener &other) const
180 {
181 return listener == other.listener && types == other.types;
182 }
183
184 QQuick3DObjectChangeListener *listener;
185 ChangeTypes types;
186 };
187
188 struct ExtraData
189 {
190 ExtraData();
191
192 int hideRefCount;
193 QObjectList resourcesList;
194
195 };
196 QLazilyAllocated<ExtraData> extra;
197
198 QVector<QQuick3DObjectPrivate::ChangeListener> changeListeners;
199
200 void addItemChangeListener(QQuick3DObjectChangeListener *listener, ChangeTypes types);
201 void updateOrAddItemChangeListener(QQuick3DObjectChangeListener *listener, ChangeTypes types);
202 void removeItemChangeListener(QQuick3DObjectChangeListener *, ChangeTypes types);
203
204 QQuickStateGroup *_states();
205 QQuickStateGroup *_stateGroup;
206
207 enum DirtyType {
208 TransformOrigin = 0x00000001,
209 Transform = 0x00000002,
210 BasicTransform = 0x00000004,
211 Position = 0x00000008,
212 Size = 0x00000010,
213
214 ZValue = 0x00000020,
215 Content = 0x00000040,
216 Smooth = 0x00000080,
217 OpacityValue = 0x00000100,
218 ChildrenChanged = 0x00000200,
219 ChildrenStackingChanged = 0x00000400,
220 ParentChanged = 0x00000800,
221
222 Clip = 0x00001000,
223 Window = 0x00002000,
224
225 EffectReference = 0x00008000,
226 Visible = 0x00010000,
227 HideReference = 0x00020000,
228 Antialiasing = 0x00040000,
229
230 InstanceRootChanged = 0x00080000,
231 // When you add an attribute here, don't forget to update
232 // dirtyToString()
233
234 TransformUpdateMask = TransformOrigin | Transform | BasicTransform | Position | Window,
235 ComplexTransformUpdateMask = Transform | Window,
236 ContentUpdateMask = Size | Content | Smooth | Window | Antialiasing,
237 ChildrenUpdateMask = ChildrenChanged | ChildrenStackingChanged | EffectReference | Window
238 };
239
240 quint32 dirtyAttributes;
241 QString dirtyToString() const;
242 void dirty(DirtyType);
243 void addToDirtyList();
244 void removeFromDirtyList();
245 QQuick3DObject *nextDirtyItem;
246 QQuick3DObject **prevDirtyItem;
247
248 void setCulled(bool);
249
250 QPointer<QQuick3DSceneManager> sceneManager;
251 int sceneRefCount;
252
253 QQuick3DObject *parentItem;
254
255 QList<QQuick3DObject *> childItems;
256 void addChild(QQuick3DObject *);
257 void removeChild(QQuick3DObject *);
258 void siblingOrderChanged();
259
260 void refSceneManager(QQuick3DSceneManager &);
261 void derefSceneManager();
262
263 static void refSceneManager(QQuick3DObject *obj, QQuick3DSceneManager &mgr)
264 {
265 if (obj)
266 QQuick3DObjectPrivate::get(item: obj)->refSceneManager(mgr);
267 }
268 static void derefSceneManager(QQuick3DObject *obj)
269 {
270 if (obj)
271 QQuick3DObjectPrivate::get(item: obj)->derefSceneManager();
272 }
273
274 QQuick3DObject *subFocusItem;
275 void updateSubFocusItem(QQuick3DObject *scope, bool focus);
276
277 void itemChange(QQuick3DObject::ItemChange, const QQuick3DObject::ItemChangeData &);
278
279 virtual void updatePolish() {}
280
281 QSSGRenderGraphObject *spatialNode = nullptr;
282
283 Type type = Type::Unknown;
284 bool componentComplete = true;
285 bool preSyncNeeded = false;
286 bool culled;
287 bool sharedResource = false;
288 QQuick3DItem2D *contentItem2d = nullptr;
289 ConnectionMap connectionMap;
290 Q_QUICK3D_PROFILE_ID
291};
292
293Q_DECLARE_OPERATORS_FOR_FLAGS(QQuick3DObjectPrivate::ChangeTypes)
294Q_DECLARE_TYPEINFO(QQuick3DObjectPrivate::ChangeListener, Q_PRIMITIVE_TYPE);
295Q_DECLARE_TYPEINFO(QQuick3DObjectPrivate::ConnectionKey, Q_PRIMITIVE_TYPE);
296
297inline size_t qHash(const QQuick3DObjectPrivate::ConnectionKey &con, size_t seed = 0) noexcept
298{
299 return qHashBits(p: &con, size: sizeof(QQuick3DObjectPrivate::ConnectionKey), seed);
300}
301
302QT_END_NAMESPACE
303
304#endif // QSSGOBJECT_P_H
305

source code of qtquick3d/src/quick3d/qquick3dobject_p.h