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