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
qssguserrenderpassmanager.cpp
Go to the documentation of this file.
1// Copyright (C) 2025 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
7#include "graphobjects/qssgrenderuserpass_p.h"
8
9#include <QtCore/qloggingcategory.h>
10
12
13Q_STATIC_LOGGING_CATEGORY(QSSGUserRenderPassManagerLog, "qt.quick3d.runtimerender.userpassmanager")
14
15QSSGUserRenderPassManager::QSSGUserRenderPassManager(Private)
16{
17
18}
19
20QSSGUserRenderPassManager::~QSSGUserRenderPassManager()
21{
22 for (auto &rt : m_renderPassRenderTargets)
23 rt.reset();
24
25 const auto textures = m_trackedTextures.keys();
26 qDeleteAll(textures);
27}
28
29void QSSGUserRenderPassManager::setScheduledPasses(const UserPassSet &topLevelPasses)
30{
31 // The scene sync establishes the full set of top-level passes each frame.
32 // The manager keeps this set ordered by nesting depth and declaration
33 // order in updateUserPassOrder(), so the caller need not pre-sort it.
34 m_scheduledUserPasses = topLevelPasses;
35 m_passlistDirty = true;
36}
37
38void QSSGUserRenderPassManager::scheduleUserPass(QSSGRenderUserPass *userPasses)
39{
40 if (userPasses == nullptr)
41 return;
42
43 // Only top-level passes are scheduled. A sub-pass is invoked by its parent
44 // and renders into the parent's target, so it must never enter the scheduled
45 // list even if a provider requests its result (QTBUG-148554). This request
46 // typically comes from a RenderOutputProvider; since a sub-pass produces no
47 // output of its own, the provider will never get a texture, so warn (once
48 // per pass; the provider path requests every frame).
49 if (userPasses->role != QSSGRenderUserPass::Role::TopLevel) {
50 if (!m_subPassScheduleWarned.contains(userPasses)) {
51 m_subPassScheduleWarned.insert(userPasses);
52 qWarning() << "Ignoring request to schedule sub-pass" << userPasses
53 << "; a sub-pass renders into its parent's render target and produces no output of its own."
54 " A RenderOutputProvider must reference a top-level RenderPass.";
55 }
56 return;
57 }
58
59 m_subPassScheduleWarned.remove(userPasses);
60
61 auto it = std::find(m_scheduledUserPasses.begin(), m_scheduledUserPasses.end(), userPasses);
62 if (it == m_scheduledUserPasses.end()) {
63 m_scheduledUserPasses.push_back(userPasses);
64 m_passlistDirty = true;
65 }
66}
67
68void QSSGUserRenderPassManager::unscheduleUserPass(const QSSGRenderUserPass *userPasses)
69{
70 if (userPasses != nullptr) {
71 auto it = std::find(m_scheduledUserPasses.cbegin(), m_scheduledUserPasses.cend(), userPasses);
72 if (it != m_scheduledUserPasses.cend()) {
73 m_scheduledUserPasses.erase(it);
74 m_passlistDirty = true;
75 }
76 }
77}
78
79QSSGRhiRenderableTextureV2Ptr QSSGUserRenderPassManager::getOrCreateRenderableTexture(const QSSGRenderUserPass &userPass)
80{
81 auto it = m_renderPassRenderTargets.find(&userPass);
82 if (it != m_renderPassRenderTargets.end())
83 return it.value();
84
85 auto iter = m_renderPassRenderTargets.insert(&userPass, std::make_shared<QSSGRhiRenderableTextureV2>(shared_from_this(), QSSGRhiRenderableTextureV2::Private::Initialize));
86 return iter.value();
87}
88
89QSSGRhiRenderableTextureV2Ptr QSSGUserRenderPassManager::getUserPassTexureResult(const QSSGRenderUserPass &userPass) const
90{
91 const auto foundIt = m_renderPassRenderTargets.find(&userPass);
92 if (foundIt != m_renderPassRenderTargets.end())
93 return foundIt.value();
94
95 return {};
96}
97
98void QSSGUserRenderPassManager::updateUserPassOrder(bool forceUpdate)
99{
100 if (m_passlistDirty || forceUpdate) {
101 // Deeper-nested passes render first ("children before parents"), so a
102 // pass declared inside its consumer renders before it. Passes at equal
103 // depth render in declaration order, independent of the order a
104 // provider scheduled them.
105 std::sort(m_scheduledUserPasses.begin(), m_scheduledUserPasses.end(), [](const QSSGRenderUserPass *a, const QSSGRenderUserPass *b) {
106 if (a->m_nestingDepth != b->m_nestingDepth)
107 return a->m_nestingDepth > b->m_nestingDepth;
108 return a->m_declarationOrder < b->m_declarationOrder;
109 });
110 }
111
112 m_passlistDirty = false;
113}
114
115bool QSSGUserRenderPassManager::derefTexture(QRhiTexture *texture)
116{
117 auto foundIt = m_trackedTextures.find(texture);
118 const bool wasFound = foundIt != m_trackedTextures.end();
119
120 if (wasFound) {
121 if (!(foundIt.value() > 1)) {
122 m_trackedTextures.erase(foundIt);
123 m_deferredReleaseTextures.insert(texture);
124 } else {
125 foundIt.value() -= 1;
126 }
127 } else {
128 qWarning() << "QSSGUserRenderPassManager::derefTexture: Trying to deref a texture that is not tracked.";
129 }
130
131 return wasFound;
132}
133
134void QSSGUserRenderPassManager::refTexture(QRhiTexture *texture)
135{
136 auto it = m_trackedTextures.find(texture);
137 if (it != m_trackedTextures.end())
138 it.value() += 1;
139 else
140 m_trackedTextures.insert(texture, 1);
141}
142
143bool QSSGUserRenderPassManager::derefTexture(const std::unique_ptr<QRhiTexture> &texture)
144{
145 return derefTexture(texture.get());
146}
147
148void QSSGUserRenderPassManager::refTexture(const std::unique_ptr<QRhiTexture> &texture)
149{
150 refTexture(texture.get());
151}
152
153void QSSGUserRenderPassManager::unregisterManagedTexture(QSSGManagedRhiTexture *textureWrapper)
154{
155 QSSG_ASSERT(textureWrapper != nullptr, return);
156
157 const auto foundIt = std::find(m_trackedTextureWrappers.cbegin(), m_trackedTextureWrappers.cend(), textureWrapper);
158 if (foundIt != m_trackedTextureWrappers.cend()) {
159 auto &texture = textureWrapper->texture();
160 if (!derefTexture(texture))
161 qWarning("QSSGSharedRhiTextureWrapper: Texture was not tracked by the manager, possible resource leak.");
162 m_trackedTextureWrappers.erase(foundIt);
163 }
164}
165
166void QSSGUserRenderPassManager::registerManagedTexture(QSSGManagedRhiTexture *textureWrapper)
167{
168 QSSG_ASSERT(textureWrapper != nullptr, return);
169
170 const auto foundIt = std::find(m_trackedTextureWrappers.cbegin(), m_trackedTextureWrappers.cend(), textureWrapper);
171 if (foundIt == m_trackedTextureWrappers.cend()) {
172 refTexture(textureWrapper->texture());
173 m_trackedTextureWrappers.push_back(textureWrapper);
174 }
175}
176
177qsizetype QSSGUserRenderPassManager::acquireUserPassSlot()
178{
179 if (m_userPassSlotsUsed < maxUserPassSlots() - 1)
180 return qsizetype(m_userPassSlotsUsed++);
181
182 qWarning() << "Exceeded maximum number of user render passes(" << QSSGUserRenderPassManager::maxUserPassSlots() << ")";
183
184 return -1;
185}
186
187void QSSGUserRenderPassManager::resetForFrame()
188{
189 qDeleteAll(m_deferredReleaseTextures.cbegin(), m_deferredReleaseTextures.cend());
190 m_deferredReleaseTextures.clear();
191
192 m_userPassSlotsUsed = 0;
193}
194
195void QSSGUserRenderPassManager::releaseAll()
196{
197 m_scheduledUserPasses.clear();
198 m_subPassScheduleWarned.clear();
199
200 // NOTE: We own these so we'll invalidate them, meaning releasing their internal resources.
201 // Any shared pointers to them held elsewhere will become invalid, as in they will
202 // get their internal QRhiTexture released and set to nullptr.
203 for (auto &rt : m_renderPassRenderTargets)
204 rt->invalidate();
205
206 m_renderPassRenderTargets.clear();
207
208 { // We make a copy since invalidate() will call back into us to unregister itself!
209 auto cpy = m_trackedTextureWrappers;
210 for (auto &tw : cpy)
211 tw->invalidate();
212
213 cpy.clear();
214 }
215
216 qDeleteAll(m_deferredReleaseTextures.cbegin(), m_deferredReleaseTextures.cend());
217 m_deferredReleaseTextures.clear();
218
219 Q_ASSERT(m_trackedTextureWrappers.size() == 0);
220
221 if (m_trackedTextures.size() > 0) {
222 qWarning() << "QSSGUserRenderPassManager::releaseAll: There are still tracked textures (" << m_trackedTextures.size() << ") when releasing all. This indicates a resource leak.";
223 for (auto it = m_trackedTextures.constBegin(); it != m_trackedTextures.constEnd(); ++it)
224 qCDebug(QSSGUserRenderPassManagerLog) << "Texture: " << it.key()->name() << ", has ref count: " << it.value();
225 }
226}
227
228QT_END_NAMESPACE
Combined button and popup list for selecting options.