Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qquick3dresourceloader.cpp
Go to the documentation of this file.
1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
5
6
9#include <QtQuick3DRuntimeRender/private/qssgrenderresourceloader_p.h>
10
11QT_BEGIN_NAMESPACE
12
13/*!
14 \qmltype ResourceLoader
15 \inqmlmodule QtQuick3D
16 \inherits Object3D
17
18 \brief Allows pre-loading of 3D resources.
19
20 ResourceLoader is used to pre-load resources for Qt Quick 3D. Normally
21 resources are only loaded when they are needed to render a frame, and are
22 unloaded when they are not used to render the scene. This aggressive
23 approach to resource lifetimes means that only the bare minimum of GPU
24 resources are used to render a frame, but for some dynamic scenes this
25 can lead to resources being loaded and released frequently. The
26 ResourceLoader component enables a finer grain control on the lifetimes
27 of resources in the scene. Resources listed in the ResourceLoader
28 component are loaded into GPU memory and will remain there until they
29 are removed from the ResourceLoader lists or the ResourceLoader is
30 destroyed.
31
32 ResourceLoader can also be used to make sure that large resources are
33 available before rendering a frame. Since resources are loaded only
34 when needed for a frame, this can lead to frames being dropped waiting
35 for a large resource to be loaded. By pre-loading large resources before
36 showing a scene, there is no risk of dropping any frames due to resources
37 being loaded during an animation.
38
39 For usage examples, see \l {Qt Quick 3D - Principled Material Example}
40
41*/
42
43/*!
44 \qmlproperty List<url> ResourceLoader::meshSources
45
46 This property defines a list of locations of mesh files containing geometry.
47 When a mesh file is added to this list, it will be loaded to the GPU and
48 cached. If these same mesh files are source is used by a /c Model they will
49 not need to be loaded again.
50
51*/
52
53/*!
54 \qmlproperty List<QtQuick3D::Texture> ResourceLoader::textures
55
56 This property defines a list of Texture resources that will be loaded to the
57 GPU and cached.
58
59*/
60
61/*!
62 \qmlproperty List<QtQuick3D::Geometry> ResourceLoader::geometries
63
64 This property defines a list of Geometry resources that will be loaded to the
65 GPU and cached.
66
67*/
68
69QQuick3DResourceLoader::QQuick3DResourceLoader(QQuick3DObject *parent)
70 : QQuick3DObject(*(new QQuick3DObjectPrivate(QQuick3DObjectPrivate::Type::ResourceLoader)), parent)
71{
72}
73
74const QList<QUrl> &QQuick3DResourceLoader::meshSources() const
75{
76 return m_meshSources;
77}
78
79void QQuick3DResourceLoader::setMeshSources(const QList<QUrl> &newMeshSources)
80{
81 if (m_meshSources == newMeshSources)
82 return;
83 m_meshSources = newMeshSources;
84 emit meshSourcesChanged();
85 markDirty(QQuick3DResourceLoader::MeshesDirty);
86}
87
88
89QQmlListProperty<QQuick3DGeometry> QQuick3DResourceLoader::geometries()
90{
91 return QQmlListProperty<QQuick3DGeometry>(this,
92 nullptr,
93 QQuick3DResourceLoader::qmlAppendGeometry,
94 QQuick3DResourceLoader::qmlGeometriesCount,
95 QQuick3DResourceLoader::qmlGeometryAt,
96 QQuick3DResourceLoader::qmlClearGeometries);
97}
98
99QQmlListProperty<QQuick3DTexture> QQuick3DResourceLoader::textures()
100{
101 return QQmlListProperty<QQuick3DTexture>(this,
102 nullptr,
103 QQuick3DResourceLoader::qmlAppendTexture,
104 QQuick3DResourceLoader::qmlTexturesCount,
105 QQuick3DResourceLoader::qmlTextureAt,
106 QQuick3DResourceLoader::qmlClearTextures);
107}
108
109void QQuick3DResourceLoader::onGeometryDestroyed(QObject *object)
110{
111 bool found = false;
112 for (int i = 0; i < m_geometries.size(); ++i) {
113 if (m_geometries[i] == object) {
114 m_geometries.removeAt(i--);
115 found = true;
116 }
117 }
118 if (found)
119 markDirty(QQuick3DResourceLoader::GeometriesDirty);
120}
121
122void QQuick3DResourceLoader::onTextureDestroyed(QObject *object)
123{
124 bool found = false;
125 for (int i = 0; i < m_textures.size(); ++i) {
126 if (m_textures[i] == object) {
127 m_textures.removeAt(i--);
128 found = true;
129 }
130 }
131 if (found)
132 markDirty(QQuick3DResourceLoader::TexturesDirty);
133}
134
135QSSGRenderGraphObject *QQuick3DResourceLoader::updateSpatialNode(QSSGRenderGraphObject *node)
136{
137 if (!node) {
138 markAllDirty();
139 node = new QSSGRenderResourceLoader();
140 }
141 QQuick3DObject::updateSpatialNode(node);
142 int dirtyAttribute = 0;
143
144 auto resourceLoaderNode = static_cast<QSSGRenderResourceLoader *>(node);
145 if (m_dirtyAttributes & MeshesDirty) {
146 resourceLoaderNode->meshes.clear();
147 for (const auto &mesh : std::as_const(m_meshSources))
148 resourceLoaderNode->meshes.push_back(QSSGRenderPath(QQuick3DModel::translateMeshSource(mesh, this)));
149 }
150
151 if (m_dirtyAttributes & TexturesDirty) {
152 resourceLoaderNode->textures.clear();
153 for (const auto &texture : std::as_const(m_textures)) {
154 auto graphObject = QQuick3DObjectPrivate::get(texture)->spatialNode;
155 if (graphObject)
156 resourceLoaderNode->textures.push_back(graphObject);
157 else
158 dirtyAttribute |= TexturesDirty;
159 }
160 }
161
162 if (m_dirtyAttributes & GeometriesDirty) {
163 resourceLoaderNode->geometries.clear();
164 for (const auto &geometry : std::as_const(m_geometries)) {
165 auto graphObject = QQuick3DObjectPrivate::get(geometry)->spatialNode;
166 if (graphObject)
167 resourceLoaderNode->geometries.push_back(graphObject);
168 else
169 dirtyAttribute |= GeometriesDirty;
170 }
171 }
172
173 m_dirtyAttributes = dirtyAttribute;
174 return resourceLoaderNode;
175
176}
177
178void QQuick3DResourceLoader::markAllDirty()
179{
180 m_dirtyAttributes = 0xffffffff;
181 QQuick3DObject::markAllDirty();
182}
183
184void QQuick3DResourceLoader::itemChange(ItemChange change, const ItemChangeData &value)
185{
186 if (change == QQuick3DObject::ItemSceneChange)
187 updateSceneManager(value.sceneManager);
188}
189
190void QQuick3DResourceLoader::qmlAppendGeometry(QQmlListProperty<QQuick3DGeometry> *list, QQuick3DGeometry *geometry)
191{
192 if (geometry == nullptr)
193 return;
194 QQuick3DResourceLoader *self = static_cast<QQuick3DResourceLoader *>(list->object);
195 self->m_geometries.push_back(geometry);
196
197 self->markDirty(QQuick3DResourceLoader::GeometriesDirty);
198
199 if (geometry->parentItem() == nullptr) {
200 // If the material has no parent, check if it has a hierarchical parent that's a QQuick3DObject
201 // and re-parent it to that, e.g., inline materials
202 QQuick3DObject *parentItem = qobject_cast<QQuick3DObject *>(geometry->parent());
203 if (parentItem) {
204 geometry->setParentItem(parentItem);
205 } else { // If no valid parent was found, make sure the material refs our scene manager
206 const auto &sceneManager = QQuick3DObjectPrivate::get(self)->sceneManager;
207 if (sceneManager) {
208 QQuick3DObjectPrivate::get(geometry)->refSceneManager(*sceneManager);
209 }
210 }
211 }
212
213 // Make sure geometries are removed when destroyed
214 connect(geometry, &QQuick3DGeometry::destroyed, self, &QQuick3DResourceLoader::onGeometryDestroyed);
215}
216
217QQuick3DGeometry *QQuick3DResourceLoader::qmlGeometryAt(QQmlListProperty<QQuick3DGeometry> *list, qsizetype index)
218{
219 QQuick3DResourceLoader *self = static_cast<QQuick3DResourceLoader *>(list->object);
220
221 if (index >= self->m_geometries.size()) {
222 qWarning("The index exceeds the range of valid geometries.");
223 return nullptr;
224 }
225
226 return self->m_geometries.at(index);
227}
228
229qsizetype QQuick3DResourceLoader::qmlGeometriesCount(QQmlListProperty<QQuick3DGeometry> *list)
230{
231 QQuick3DResourceLoader *self = static_cast<QQuick3DResourceLoader *>(list->object);
232 return self->m_geometries.size();
233}
234
235void QQuick3DResourceLoader::qmlClearGeometries(QQmlListProperty<QQuick3DGeometry> *list)
236{
237 QQuick3DResourceLoader *self = static_cast<QQuick3DResourceLoader *>(list->object);
238 for (const auto &geometry : std::as_const(self->m_geometries)) {
239 if (geometry->parentItem() == nullptr)
240 QQuick3DObjectPrivate::get(geometry)->derefSceneManager();
241 disconnect(geometry, &QQuick3DGeometry::destroyed, self, &QQuick3DResourceLoader::onGeometryDestroyed);
242 }
243
244 self->m_geometries.clear();
245 self->markDirty(QQuick3DResourceLoader::GeometriesDirty);
246}
247
248void QQuick3DResourceLoader::qmlAppendTexture(QQmlListProperty<QQuick3DTexture> *list, QQuick3DTexture *texture)
249{
250 if (texture == nullptr)
251 return;
252 QQuick3DResourceLoader *self = static_cast<QQuick3DResourceLoader *>(list->object);
253 self->m_textures.push_back(texture);
254
255 self->markDirty(QQuick3DResourceLoader::TexturesDirty);
256
257 if (texture->parentItem() == nullptr) {
258 // If the material has no parent, check if it has a hierarchical parent that's a QQuick3DObject
259 // and re-parent it to that, e.g., inline materials
260 QQuick3DObject *parentItem = qobject_cast<QQuick3DObject *>(texture->parent());
261 if (parentItem) {
262 texture->setParentItem(parentItem);
263 } else { // If no valid parent was found, make sure the material refs our scene manager
264 const auto &sceneManager = QQuick3DObjectPrivate::get(self)->sceneManager;
265 if (sceneManager) {
266 QQuick3DObjectPrivate::get(texture)->refSceneManager(*sceneManager);
267 }
268 }
269 }
270 // Make sure TextureData are removed when destroyed
271 connect(texture, &QQuick3DTextureData::destroyed, self, &QQuick3DResourceLoader::onTextureDestroyed);
272}
273
274QQuick3DTexture *QQuick3DResourceLoader::qmlTextureAt(QQmlListProperty<QQuick3DTexture> *list, qsizetype index)
275{
276 QQuick3DResourceLoader *self = static_cast<QQuick3DResourceLoader *>(list->object);
277 if (index >= self->m_textures.size()) {
278 qWarning("The index exceeds the range of valid texture data.");
279 return nullptr;
280 }
281
282 return self->m_textures.at(index);
283}
284
285qsizetype QQuick3DResourceLoader::qmlTexturesCount(QQmlListProperty<QQuick3DTexture> *list)
286{
287 QQuick3DResourceLoader *self = static_cast<QQuick3DResourceLoader *>(list->object);
288 return self->m_textures.size();
289}
290
291void QQuick3DResourceLoader::qmlClearTextures(QQmlListProperty<QQuick3DTexture> *list)
292{
293 QQuick3DResourceLoader *self = static_cast<QQuick3DResourceLoader *>(list->object);
294 for (const auto &data : std::as_const(self->m_textures)) {
295 if (data->parentItem() == nullptr)
296 QQuick3DObjectPrivate::get(data)->derefSceneManager();
297 disconnect(data, &QQuick3DTextureData::destroyed, self, &QQuick3DResourceLoader::onTextureDestroyed);
298 }
299 self->m_textures.clear();
300 self->markDirty(QQuick3DResourceLoader::TexturesDirty);
301}
302
303void QQuick3DResourceLoader::markDirty(ResourceLoaderDirtyType type)
304{
305 if (!(m_dirtyAttributes & quint32(type))) {
306 m_dirtyAttributes |= quint32(type);
307 update();
308 }
309}
310
311void QQuick3DResourceLoader::updateSceneManager(QQuick3DSceneManager *sceneManager)
312{
313 if (sceneManager) {
314 for (auto &geometry : m_geometries)
315 if (!geometry->parentItem() && !QQuick3DObjectPrivate::get(geometry)->sceneManager)
316 QQuick3DObjectPrivate::refSceneManager(geometry, *sceneManager);
317 for (auto &texture : m_textures)
318 if (!texture->parentItem() && !QQuick3DObjectPrivate::get(texture)->sceneManager)
319 QQuick3DObjectPrivate::refSceneManager(texture, *sceneManager);
320 } else {
321 for (auto &geometry : m_geometries)
322 QQuick3DObjectPrivate::derefSceneManager(geometry);
323 for (auto &texture : m_textures)
324 QQuick3DObjectPrivate::derefSceneManager(texture);
325 }
326}
327
328QT_END_NAMESPACE