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 | QT_BEGIN_NAMESPACE |
34 | |
35 | class QQuick3DItem2D; |
36 | |
37 | class Q_QUICK3D_PRIVATE_EXPORT QQuick3DObjectPrivate : public QObjectPrivate |
38 | { |
39 | Q_DECLARE_PUBLIC(QQuick3DObject) |
40 | public: |
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 |
189 | { |
190 | (); |
191 | |
192 | int ; |
193 | QObjectList ; |
194 | |
195 | }; |
196 | QLazilyAllocated<ExtraData> ; |
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 | = 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 | |
293 | Q_DECLARE_OPERATORS_FOR_FLAGS(QQuick3DObjectPrivate::ChangeTypes) |
294 | Q_DECLARE_TYPEINFO(QQuick3DObjectPrivate::ChangeListener, Q_PRIMITIVE_TYPE); |
295 | Q_DECLARE_TYPEINFO(QQuick3DObjectPrivate::ConnectionKey, Q_PRIMITIVE_TYPE); |
296 | |
297 | inline 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 | |
302 | QT_END_NAMESPACE |
303 | |
304 | #endif // QSSGOBJECT_P_H |
305 | |