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
qquick3drenderpass.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
8#include <QtQuick3D/private/qquick3dscenemanager_p.h>
9#include <QtQuick3DUtils/private/qssgassert_p.h>
10#include <QtQuick3DRuntimeRender/private/qssgrenderuserpass_p.h>
11#include <QtQuick3DRuntimeRender/private/qssgshadermaterialadapter_p.h>
12#include <QtQuick3DRuntimeRender/private/qssgrendershadertokenizer_p.h>
13
14#include <QtCore/QLoggingCategory>
15
17
18Q_LOGGING_CATEGORY(lcQuick3DRenderPass, "qt.quick3d.renderpass")
19
20/*!
21 \qmltype RenderPass
22 \inherits Object3D
23 \inqmlmodule QtQuick3D
24 \brief Defines a custom render pass for rendering 3D content.
25 \since 6.11
26
27 A RenderPass defines a rendering step and the render target it writes
28 into. It is the combination of three concerns:
29
30 \list
31 \li \b {Where to render} — one or more output textures declared as
32 \l ColorAttachment or \l DepthTextureAttachment commands in
33 \l {RenderPass::commands}{commands}.
34 \li \b {What to render} — which scene objects the pass draws,
35 controlled by \l RenderablesFilter commands. Sub-divisions of
36 work within the same render target are described using
37 \l SubRenderPass commands.
38 \li \b {How to render} — the \l materialMode and any
39 \l PipelineStateOverride commands.
40 \endlist
41
42 A RenderPass becomes active for a scene when it is placed as a child
43 of a \l View3D or a \l Node, or of another RenderPass.
44
45 \section1 Render order
46
47 Nesting a RenderPass inside another RenderPass expresses ordering:
48 nested passes render \e before their parent, so a pass that produces a
49 texture consumed by another pass is declared as a child of its
50 consumer, and results merge towards the top of the pass hierarchy.
51 Only RenderPass ancestors count towards the nesting depth; other
52 ancestors, such as \l Node items used for grouping, do not affect the
53 order. The relative render order of passes at the same nesting depth
54 is not defined; use nesting to express an ordering requirement.
55
56 Nesting affects ordering only; a nested pass still renders into its
57 own render target. To render into the parent pass's render target,
58 reference the pass from a \l SubRenderPass command instead. A pass
59 referenced by a SubRenderPass command is invoked by its parent and
60 never renders on its own.
61
62 The following example sets up a simple off-screen pass that renders
63 all scene objects into a custom texture, which can then be consumed
64 by a material or a \l SimpleQuadRenderer:
65
66 \qml
67 import QtQuick3D
68
69 View3D {
70 // Declare the off-screen color buffer
71 RenderPassTexture {
72 id: myColorTexture
73 format: RenderPassTexture.RGBA8
74 }
75
76 // The render pass: where + what + how
77 RenderPass {
78 id: myRenderPass
79 commands: [
80 // Where: attach the texture as the color output
81 ColorAttachment {
82 name: "color0"
83 target: myColorTexture
84 },
85 // What: render all objects into it
86 RenderablesFilter {
87 renderableTypes: RenderablesFilter.Opaque | RenderablesFilter.Transparent
88 }
89 ]
90 }
91 }
92 \endqml
93
94 \section1 Exposing data to the shaders
95
96 As with Effects and Custom Materials, the RenderPass will expose and
97 update user-defined properties to the shaders automatically. Any QML
98 properties declared on a RenderPass subtype will be available as
99 uniforms in the shader.
100
101 \sa SubRenderPass, RenderOutputProvider, RenderablesFilter
102*/
103
104QQuick3DRenderPass::QQuick3DRenderPass(QQuick3DObject *parent)
105 : QQuick3DObject(*(new QQuick3DObjectPrivate(QQuick3DObjectPrivate::Type::RenderPass, QQuick3DObjectPrivate::Flags::RequiresSecondaryUpdate)), parent)
106 , QQuick3DPropertyChangedTracker(this, QQuick3DSuperClassInfo<QQuick3DRenderPass>())
107{
108}
109
110QQuick3DRenderPass::AugmentSplitResult QQuick3DRenderPass::splitAugmentShader(const QByteArray &augment,
111 QByteArray &preamble,
112 QByteArray &body)
113{
114 // We have to pick apart the shader string such that the contents of the
115 // "void MAIN_FRAGMENT_AUGMENT() { }" function are taken out, and get added to the end of
116 // the shader generation to overwrite the "output" of the shader. Everything outside the
117 // function ends up as preamble code that is pasted in before the real main() (helper
118 // functions, resolvable #includes etc.).
119 //
120 // Tokenize the augment shader rather than doing a plain string search plus raw brace
121 // counting: the keyword is then ignored if it only appears in a comment, and braces inside
122 // comments don't throw off the matching.
123 QSSGShaderTokenizer tok;
124 tok.initialize(augment.constData());
125
126 const char *const begin = augment.constData();
127 const char *const end = begin + augment.size();
128
129 const char *funcStart = nullptr; // Start of the return type ("void") of the augment function.
130 const char *bodyStart = nullptr; // First character inside the function's outer braces.
131 const char *bodyEnd = nullptr; // The matching closing brace of the function body.
132 const char *afterFunc = nullptr; // First character after the closing brace.
133
134 const char *prevIdentStart = nullptr;
135 QByteArrayView prevIdent;
136
137 for (QSSGShaderTokenizer::Token t = tok.next(); t != QSSGShaderTokenizer::Token_EOF; t = tok.next()) {
138 if (t != QSSGShaderTokenizer::Token_Identifier)
139 continue;
140
141 const QByteArrayView id(tok.identifier, tok.pos - tok.identifier);
142 // The augment entry point is declared as "void MAIN_FRAGMENT_AUGMENT()"; requiring the
143 // preceding "void" avoids matching a call site or other mention of the name.
144 if (id == QByteArrayLiteral("MAIN_FRAGMENT_AUGMENT") && prevIdent == QByteArrayLiteral("void")) {
145 funcStart = prevIdentStart;
146 // Scan forward to the opening brace (skipping the parameter list); stop early on a
147 // semicolon, which would indicate a declaration without a body.
148 QSSGShaderTokenizer::Token n = tok.next();
149 while (n != QSSGShaderTokenizer::Token_EOF
150 && n != QSSGShaderTokenizer::Token_OpenBrace
151 && n != QSSGShaderTokenizer::Token_SemiColon) {
152 n = tok.next();
153 }
154 if (n == QSSGShaderTokenizer::Token_OpenBrace) {
155 bodyStart = tok.pos; // tok.pos is one past the '{'.
156 // Track brace depth to find the matching closing brace.
157 int depth = 1;
158 for (QSSGShaderTokenizer::Token b = tok.next(); b != QSSGShaderTokenizer::Token_EOF; b = tok.next()) {
159 if (b == QSSGShaderTokenizer::Token_OpenBrace) {
160 ++depth;
161 } else if (b == QSSGShaderTokenizer::Token_CloseBrace) {
162 if (--depth == 0) {
163 bodyEnd = tok.pos - 1; // tok.pos is one past the '}'.
164 afterFunc = tok.pos;
165 break;
166 }
167 }
168 }
169 }
170 break;
171 }
172
173 prevIdentStart = tok.identifier;
174 prevIdent = id;
175 }
176
177 if (!funcStart)
178 return AugmentSplitResult::FunctionNotFound;
179 if (!bodyStart)
180 return AugmentSplitResult::OpeningBraceNotFound;
181 if (!bodyEnd)
182 return AugmentSplitResult::ClosingBraceNotFound;
183
184 body = QByteArray(bodyStart, bodyEnd - bodyStart);
185 preamble = QByteArray(begin, funcStart - begin);
186 preamble += QByteArray(afterFunc, end - afterFunc);
187 return AugmentSplitResult::Ok;
188}
189
190QSSGRenderGraphObject *QQuick3DRenderPass::updateSpatialNode(QSSGRenderGraphObject *node)
191{
192 QSSGRenderUserPass *renderPassNode = static_cast<QSSGRenderUserPass *>(node);
193
194 bool newBackendNode = false;
195 if (!renderPassNode) {
196 renderPassNode = new QSSGRenderUserPass;
197 newBackendNode = true;
198 }
199
200 // The role follows the SubRenderPass reference count maintained by the
201 // commands referencing this pass. A role change forces a full update, which
202 // rebuilds the command list and thereby marks the scene manager's pass set
203 // dirty, so synchronize() re-derives the classification-dependent state
204 // (nesting depth, declaration order) for the new role.
205 const bool isSubPass = (m_subRenderPassRef > 0);
206 const bool subPassChanged = (renderPassNode->role == QSSGRenderUserPass::Role::SubPass) != isSubPass;
207
208 renderPassNode->role = isSubPass ? QSSGRenderUserPass::Role::SubPass : QSSGRenderUserPass::Role::TopLevel;
209
210 const bool fullUpdate = newBackendNode || subPassChanged || (m_dirtyAttributes & Dirty::TextureDirty) || (m_dirtyAttributes & CommandsDirty);
211
212 auto &shaderAugmentation = renderPassNode->shaderAugmentation;
213 auto &uniformProps = shaderAugmentation.propertyUniforms;
214
215 if (fullUpdate) {
216 markAllDirty();
217
218 // Properties -> uniforms.
219 uniformProps = extractProperties();
220
221 // Commands
222 renderPassNode->resetCommands();
223 clearDirty(Dirty::CommandsDirty);
224 for (QQuick3DShaderUtilsRenderCommand *command : std::as_const(m_commands)) {
225 if (auto *cmd = command->cloneCommand())
226 renderPassNode->commands.push_back(cmd);
227 else
228 markDirty(CommandsDirty, true); // Try again next time
229 }
230
231 // The command list drives the top-level/sub-pass classification (a pass
232 // referenced by a SubRenderPass command is a sub-pass). Rebuilding it may
233 // have changed that, so ask synchronize() to re-derive the classification.
234 if (QQuick3DSceneManager *sceneManager = QQuick3DObjectPrivate::get(this)->sceneManager)
235 sceneManager->userRenderPassesDirty = true;
236 }
237
238 // Update the property values
239 if (m_dirtyAttributes & Dirty::PropertyDirty) {
240 for (const auto &prop : std::as_const(uniformProps)) {
241 auto p = metaObject()->property(prop.pid);
242 if (Q_LIKELY(p.isValid())) {
243 QVariant v = p.read(this);
244 if (v.isValid()) {
245 if (v.metaType().id() == qMetaTypeId<QQuick3DTexture *>()) {
246 QQuick3DTexture *tex = v.value<QQuick3DTexture *>();
247 auto *po = QQuick3DObjectPrivate::get(tex);
248 QSSGRenderImage *ri = static_cast<QSSGRenderImage *>(po->spatialNode);
249 prop.value = QVariant::fromValue(ri);
250 } else {
251 prop.value = v;
252 }
253 }
254 }
255 }
256
257 clearDirty(Dirty(Dirty::PropertyDirty | Dirty::TextureDirty));
258 }
259
260 // Clear Dirty
261 if (m_dirtyAttributes & Dirty::ClearDirty) {
262 renderPassNode->renderTargetFlags = QRhiTextureRenderTarget::Flags(m_renderTargetFlags.toInt());
263 renderPassNode->clearColor = m_clearColor;
264 renderPassNode->depthStencilClearValue = { m_depthClearValue, m_stencilClearValue };
265
266 clearDirty(Dirty::ClearDirty);
267 }
268
269 if (m_dirtyAttributes & Dirty::PassTypeDirty) {
270 switch (m_passMode) {
271 case UserPass:
272 renderPassNode->passMode = QSSGRenderUserPass::UserPass;
273 break;
274 case SkyboxPass:
275 renderPassNode->passMode = QSSGRenderUserPass::SkyboxPass;
276 break;
277 case Item2DPass:
278 renderPassNode->passMode = QSSGRenderUserPass::Item2DPass;
279 break;
280 }
281
282 clearDirty(Dirty::PassTypeDirty);
283 }
284
285 // If not a user pass, we're done
286 if (m_passMode != UserPass)
287 return renderPassNode;
288
289 renderPassNode->materialMode = QSSGRenderUserPass::MaterialModes(m_materialMode);
290 clearDirty(Dirty::MaterialModeDirty);
291
292 if (renderPassNode->materialMode == QSSGRenderUserPass::OverrideMaterial) {
293 clearDirty(Dirty::OverrideMaterialDirty);
294 if (m_overrideMaterial) {
295 // Set the backend material
296 QSSGRenderGraphObject *graphObject = QQuick3DObjectPrivate::get(m_overrideMaterial)->spatialNode;
297 if (graphObject)
298 renderPassNode->overrideMaterial = graphObject;
299 else
300 markDirty(OverrideMaterialDirty, true); // Try again next time
301 } else {
302 // Set nullptr
303 renderPassNode->overrideMaterial = nullptr;
304 }
305 } else if (renderPassNode->materialMode == QSSGRenderUserPass::OriginalMaterial) {
306 // Nothing to do
307 } else if (renderPassNode->materialMode == QSSGRenderUserPass::AugmentMaterial) {
308 // Augment Shaders
309 if (m_dirtyAttributes & AugmentShaderDirty) {
310 QByteArray augmentSnippet;
311 QByteArray augmentPreamble;
312 if (!m_augmentShader.isEmpty()) {
313 const QQmlContext *context = qmlContext(this);
314 QByteArray shaderPathKey("augment material --");
315 QByteArray augment = QSSGShaderUtils::resolveShader(m_augmentShader, context, shaderPathKey);
316
317 switch (QQuick3DRenderPass::splitAugmentShader(augment, augmentPreamble, augmentSnippet)) {
318 case AugmentSplitResult::Ok:
319 break;
320 case AugmentSplitResult::FunctionNotFound:
321 qWarning("QQuick3DRenderPass: Could not find MAIN_FRAGMENT_AUGMENT() function in shader %s", qPrintable(m_augmentShader.toString()));
322 break;
323 case AugmentSplitResult::OpeningBraceNotFound:
324 qWarning("QQuick3DRenderPass: Could not find the opening brace of MAIN_FRAGMENT_AUGMENT() in shader %s", qPrintable(m_augmentShader.toString()));
325 break;
326 case AugmentSplitResult::ClosingBraceNotFound:
327 qWarning("QQuick3DRenderPass: Could not find the closing brace of MAIN_FRAGMENT_AUGMENT() in shader %s", qPrintable(m_augmentShader.toString()));
328 break;
329 }
330 }
331
332 renderPassNode->shaderAugmentation.body = augmentSnippet;
333 renderPassNode->shaderAugmentation.preamble = augmentPreamble;
334 renderPassNode->markDirty(QSSGRenderUserPass::DirtyFlag::ShaderDirty);
335 }
336 clearDirty(Dirty::AugmentShaderDirty);
337 }
338
339 return renderPassNode;
340}
341
342void QQuick3DRenderPass::itemChange(ItemChange change, const ItemChangeData &value)
343{
344 if (change == QQuick3DObject::ItemSceneChange) {
345 updateSceneManager(value.sceneManager);
346 } else if (change == QQuick3DObject::ItemParentHasChanged) {
347 // The parent determines the pass's validity and nesting depth, both
348 // derived in synchronize(). A same-manager reparent does not travel
349 // through pass registration or removal, so mark the set dirty here.
350 if (QQuick3DSceneManager *sceneManager = QQuick3DObjectPrivate::get(this)->sceneManager)
351 sceneManager->userRenderPassesDirty = true;
352 }
353}
354
355void QQuick3DRenderPass::markTrackedPropertyDirty(QMetaProperty property, DirtyPropertyHint hint)
356{
357 Q_UNUSED(property);
358
359 // FIXME: As with the property tracking for Effects and Custom materials we
360 // should really track which property changed and only update that one.
361 if (hint == DirtyPropertyHint::Reference) {
362 // FIXME: We should verify that the property is actually a texture property.
363 markDirty(Dirty::TextureDirty);
364 } else {
365 markDirty(Dirty::PropertyDirty);
366 }
367}
368
369void QQuick3DRenderPass::onMaterialDestroyed(QObject *object)
370{
371 if (m_overrideMaterial == object) {
372 m_overrideMaterial = nullptr;
373 emit overrideMaterialChanged();
374 markDirty(OverrideMaterialDirty);
375 }
376}
377
378void QQuick3DRenderPass::onCommandChanged()
379{
380 markDirty(CommandsDirty);
381}
382
383void QQuick3DRenderPass::qmlAppendCommand(QQmlListProperty<QQuick3DShaderUtilsRenderCommand> *list, QQuick3DShaderUtilsRenderCommand *command)
384{
385 if (!command)
386 return;
387
388 QQuick3DRenderPass *that = qobject_cast<QQuick3DRenderPass *>(list->object);
389
390 if (!command->parentItem())
391 command->setParentItem(that);
392
393 that->m_commands.push_back(command);
394 that->markDirty(CommandsDirty);
395
396 // Re-clone commands whenever a property changes so the render thread
397 // sees the updated state.
398 QObject::connect(command, &QQuick3DShaderUtilsRenderCommand::changed, that, &QQuick3DRenderPass::onCommandChanged);
399}
400
401QQuick3DShaderUtilsRenderCommand *QQuick3DRenderPass::qmlCommandAt(QQmlListProperty<QQuick3DShaderUtilsRenderCommand> *list, qsizetype index)
402{
403 QQuick3DRenderPass *that = qobject_cast<QQuick3DRenderPass *>(list->object);
404 return that->m_commands.at(index);
405}
406
407qsizetype QQuick3DRenderPass::qmlCommandCount(QQmlListProperty<QQuick3DShaderUtilsRenderCommand> *list)
408{
409 QQuick3DRenderPass *that = qobject_cast<QQuick3DRenderPass *>(list->object);
410 return that->m_commands.size();
411}
412
413void QQuick3DRenderPass::qmlCommandClear(QQmlListProperty<QQuick3DShaderUtilsRenderCommand> *list)
414{
415 QQuick3DRenderPass *that = qobject_cast<QQuick3DRenderPass *>(list->object);
416 for (QQuick3DShaderUtilsRenderCommand *cmd : std::as_const(that->m_commands))
417 QObject::disconnect(cmd, nullptr, that, nullptr);
418 that->m_commands.clear();
419 that->markDirty(CommandsDirty);
420}
421
422void QQuick3DRenderPass::updateSceneManager(QQuick3DSceneManager *sceneManager)
423{
424 if (sceneManager) {
425 // Handle inline override material that may not have had a scene manager when it was set
426 if (m_overrideMaterial && !m_overrideMaterial->parentItem() && !QQuick3DObjectPrivate::get(m_overrideMaterial)->sceneManager) {
427 if (!m_overrideMaterialRefed) {
428 QQuick3DObjectPrivate::refSceneManager(m_overrideMaterial, *sceneManager);
429 m_overrideMaterialRefed = true;
430 }
431 }
432 } else {
433 // Deref the material when scene manager is removed
434 if (m_overrideMaterial && m_overrideMaterialRefed) {
435 QQuick3DObjectPrivate::derefSceneManager(m_overrideMaterial);
436 m_overrideMaterialRefed = false;
437 }
438 }
439}
440
441void QQuick3DRenderPass::markDirty(Dirty type, bool requestSecondaryUpdate)
442{
443 if (!(m_dirtyAttributes & quint32(type))) {
444 m_dirtyAttributes |= quint32(type);
445 update();
446 }
447
448 if (requestSecondaryUpdate)
449 QQuick3DObjectPrivate::get(this)->requestSecondaryUpdate();
450}
451
452void QQuick3DRenderPass::clearDirty(Dirty type)
453{
454 m_dirtyAttributes &= ~quint32(type);
455}
456
457/*!
458 \qmlproperty list<RenderCommand> RenderPass::commands
459 This property holds the list of render commands for the render pass.
460
461 The commands in the list are executed in the order they appear in the list.
462
463 \note The commands for RenderPass and Effects are similar but not the same, only
464 those marked as compatible can be used with this RenderPass.
465
466 \sa SubRenderPass,
467 PipelineStateOverride,
468 RenderablesFilter,
469 RenderPassTexture,
470 ColorAttachment,
471 DepthTextureAttachment,
472 DepthStencilAttachment,
473 AddDefine,
474 renderTargetBlend
475*/
476
477QQmlListProperty<QQuick3DShaderUtilsRenderCommand> QQuick3DRenderPass::commands()
478{
479 return QQmlListProperty<QQuick3DShaderUtilsRenderCommand>(this,
480 nullptr,
481 QQuick3DRenderPass::qmlAppendCommand,
482 QQuick3DRenderPass::qmlCommandCount,
483 QQuick3DRenderPass::qmlCommandAt,
484 QQuick3DRenderPass::qmlCommandClear);
485}
486
487/*!
488 \qmlproperty color RenderPass::clearColor
489 This property holds the clear color for the render pass.
490
491 \default Qt.black
492*/
493QColor QQuick3DRenderPass::clearColor() const
494{
495 return m_clearColor;
496}
497
498void QQuick3DRenderPass::setClearColor(const QColor &newClearColor)
499{
500 if (m_clearColor == newClearColor)
501 return;
502 m_clearColor = newClearColor;
503 emit clearColorChanged();
504 markDirty(ClearDirty);
505}
506
507/*!
508 \qmlproperty RenderPass::MaterialModes RenderPass::materialMode
509 Controls how object materials are handled when rendering into this pass.
510
511 \value RenderPass.OriginalMaterial
512 Objects are rendered using their own assigned materials, with full
513 lighting, textures, and material properties applied normally. This is
514 the standard mode for rendering a faithful copy of the scene into a
515 custom render target — for example, a secondary viewpoint for a
516 reflection probe, a rear-view camera, or a picture-in-picture effect.
517 The \c overrideMaterial, \c augmentShader, and \c shaders properties
518 are not used in this mode.
519
520 \value RenderPass.AugmentMaterial
521 Each object is rendered with its own material, but the contents of
522 the \c {MAIN_FRAGMENT_AUGMENT()} function defined in \l augmentShader
523 are injected after the original material's output definition. This
524 allows the augment code to read the material's computed color and write
525 to additional color outputs defined by \l ColorAttachment commands in
526 the pass. This is useful for multi-render-target (MRT) passes that need
527 per-material shading, such as writing the lit color to one attachment
528 and a world-space normal to another in a single draw call.
529
530 \value RenderPass.OverrideMaterial
531 All objects rendered by this pass use the single \l overrideMaterial
532 instead of their own. This is useful for depth-only passes, shadow maps,
533 silhouette or outline effects, and any other case where you want all
534 geometry to be shaded identically regardless of what material is
535 assigned to it. The \c augmentShader property is not used in this mode.
536
537 \default RenderPass.OriginalMaterial
538*/
539QQuick3DRenderPass::MaterialModes QQuick3DRenderPass::materialMode() const
540{
541 return m_materialMode;
542}
543
544void QQuick3DRenderPass::setMaterialMode(MaterialModes newMaterialMode)
545{
546 if (m_materialMode == newMaterialMode)
547 return;
548 m_materialMode = newMaterialMode;
549 emit materialModeChanged();
550 markDirty(MaterialModeDirty);
551}
552
553/*!
554 \qmlproperty Material RenderPass::overrideMaterial
555 This property holds the override material for the render pass when
556 \l{RenderPass::materialMode}{materialMode} is set to \c OverrideMaterial.
557*/
558QQuick3DMaterial *QQuick3DRenderPass::overrideMaterial() const
559{
560 return m_overrideMaterial;
561}
562
563void QQuick3DRenderPass::setOverrideMaterial(QQuick3DMaterial *newOverrideMaterial)
564{
565 if (m_overrideMaterial == newOverrideMaterial)
566 return;
567
568 // Deref the old material if we had ref'd it
569 if (m_overrideMaterial && m_overrideMaterialRefed) {
570 QQuick3DObjectPrivate::derefSceneManager(m_overrideMaterial);
571 m_overrideMaterialRefed = false;
572 }
573
574 QQuick3DObjectPrivate::attachWatcher(this, &QQuick3DRenderPass::setOverrideMaterial, newOverrideMaterial, m_overrideMaterial);
575
576 m_overrideMaterial = newOverrideMaterial;
577
578 // Handle inline material declarations by ensuring they get registered with the scene manager
579 if (m_overrideMaterial && m_overrideMaterial->parentItem() == nullptr) {
580 // If the material has no parent, check if it has a hierarchical parent that's a QQuick3DObject
581 // and re-parent it to that, e.g., inline materials
582 QQuick3DObject *parentItem = qobject_cast<QQuick3DObject *>(m_overrideMaterial->parent());
583 if (parentItem) {
584 m_overrideMaterial->setParentItem(parentItem);
585 } else {
586 // If no valid parent was found, make sure the material refs our scene manager
587 const auto &sceneManager = QQuick3DObjectPrivate::get(this)->sceneManager;
588 if (sceneManager) {
589 QQuick3DObjectPrivate::refSceneManager(m_overrideMaterial, *sceneManager);
590 m_overrideMaterialRefed = true;
591 }
592 // else: If there's no scene manager, defer until one is set, see itemChange()
593 }
594 }
595
596 emit overrideMaterialChanged();
597 markDirty(OverrideMaterialDirty);
598}
599
600/*!
601 \qmlproperty url RenderPass::augmentShader
602 This property holds the augment shader URL for the render pass when
603 \l{RenderPass::materialMode}{materialMode} is set to \c AugmentMaterial.
604
605 The shader file should contain a function with the following signature:
606 \badcode
607 void MAIN_FRAGMENT_AUGMENT() {
608 // Custom shader code here
609 }
610 \endcode
611
612 This function will be combined with the existing fragment shader of the material
613 being used by the object being rendered in this render pass. Allowing users to
614 augment the existing material shader with custom code.
615*/
616QUrl QQuick3DRenderPass::augmentShader() const
617{
618 return m_augmentShader;
619}
620
621void QQuick3DRenderPass::setAugmentShader(const QUrl &newAugmentShader)
622{
623 if (m_augmentShader == newAugmentShader)
624 return;
625 m_augmentShader = newAugmentShader;
626 emit augmentShaderChanged();
627 markDirty(AugmentShaderDirty);
628}
629
630/*!
631 \qmlproperty RenderPass::PassMode RenderPass::passMode
632 This property holds the pass mode for the render pass.
633
634 In addition to standard user render passes, Qt Quick 3D supports
635 users to manually triggering internal render passes for rendering
636 the skybox and 2D items.
637
638 \value RenderPass.UserPass A user specified render pass.
639 \value RenderPass.SkyboxPass Qt Quick 3D's built-in skybox render pass.
640 \value RenderPass.Item2DPass Qt Quick 3D's built-in 2D item render pass.
641 \default RenderPass.UserPass
642*/
643
644QQuick3DRenderPass::PassMode QQuick3DRenderPass::passMode() const
645{
646 return m_passMode;
647}
648
649void QQuick3DRenderPass::setPassMode(PassMode newPassMode)
650{
651 if (m_passMode == newPassMode)
652 return;
653 m_passMode = newPassMode;
654 emit passModeChanged();
655 markDirty(PassTypeDirty);
656}
657
658/*!
659 \qmlproperty real RenderPass::depthClearValue
660 This property holds the depth clear value for the render pass.
661
662 \default 1.0
663*/
664float QQuick3DRenderPass::depthClearValue() const
665{
666 return m_depthClearValue;
667}
668
669void QQuick3DRenderPass::setDepthClearValue(float newDepthClearValue)
670{
671 if (qFuzzyCompare(m_depthClearValue, newDepthClearValue))
672 return;
673 m_depthClearValue = newDepthClearValue;
674 emit depthClearValueChanged();
675 markDirty(ClearDirty);
676}
677
678/*!
679 \qmlproperty int RenderPass::stencilClearValue
680 This property holds the stencil clear value for the render pass.
681
682 \default 0
683*/
684quint32 QQuick3DRenderPass::stencilClearValue() const
685{
686 return m_stencilClearValue;
687}
688
689void QQuick3DRenderPass::setStencilClearValue(quint32 newStencilClearValue)
690{
691 if (m_stencilClearValue == newStencilClearValue)
692 return;
693 m_stencilClearValue = newStencilClearValue;
694 emit stencilClearValueChanged();
695 markDirty(ClearDirty);
696}
697
698/*!
699 \qmlproperty RenderPass::RenderTargetFlags RenderPass::renderTargetFlags
700 This property holds the render target flags for the render pass. These flags affect how
701 the render target contents are handled at the beginning and end of each frame.
702
703 \value RenderPass.None No special behavior. Color and depth/stencil contents are cleared at the start of each frame.
704 \value RenderPass.PreserveColorContents Preserve the color contents of the render target between frames, so the previous frame's output remains until explicitly overwritten.
705 \value RenderPass.PreserveDepthStencilContents Preserve the depth and stencil contents of the render target between frames.
706 \value RenderPass.DoNotStoreDepthStencilContents Do not store the depth and stencil contents of the render target after rendering (may improve performance on tiled GPUs).
707
708 \default RenderPass.None
709
710 \sa QRhiTextureRenderTarget::Flags
711*/
712
713QQuick3DRenderPass::RenderTargetFlags QQuick3DRenderPass::renderTargetFlags() const
714{
715 return m_renderTargetFlags;
716}
717
718void QQuick3DRenderPass::setRenderTargetFlags(RenderTargetFlags newRenderTargetFlags)
719{
720 if (m_renderTargetFlags == newRenderTargetFlags)
721 return;
722 m_renderTargetFlags = newRenderTargetFlags;
723 emit renderTargetFlagsChanged();
724 markDirty(ClearDirty);
725}
726
727// A reference change can flip the pass's role, so the pass is updated to
728// mirror the new role to its backend node, and the scene manager's pass set is
729// marked dirty so the classification-dependent state (nesting depth,
730// declaration order) is re-derived. The scene manager may be null when the
731// pass has not entered a scene yet; registration sets the flag then.
732static void notifySubPassRefChange(QQuick3DRenderPass *pass)
733{
734 pass->update();
735 if (QQuick3DSceneManager *sceneManager = QQuick3DObjectPrivate::get(pass)->sceneManager)
736 sceneManager->userRenderPassesDirty = true;
737}
738
739void QQuick3DRenderPassPrivateHelper::subPassRef(QQuick3DRenderPass *pass)
740{
741 Q_ASSERT(pass);
742 ++pass->m_subRenderPassRef;
744}
745
746void QQuick3DRenderPassPrivateHelper::subPassDeref(QQuick3DRenderPass *pass)
747{
748 Q_ASSERT(pass);
749 if (QSSG_GUARD(pass->m_subRenderPassRef > 0))
750 --pass->m_subRenderPassRef;
752}
753
754QT_END_NAMESPACE
void subPassDeref(QQuick3DRenderPass *pass)
void subPassRef(QQuick3DRenderPass *pass)
Combined button and popup list for selecting options.
static void notifySubPassRefChange(QQuick3DRenderPass *pass)