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
qssgrenderpass.cpp
Go to the documentation of this file.
1// Copyright (C) 2022 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
11#include "extensionapi/qssgrenderextensions.h"
14
15#include "../utils/qssgassert_p.h"
16
17#include <QtQuick/private/qsgrenderer_p.h>
18#include <qtquick3d_tracepoints_p.h>
19
21
22static const char defaultFragOutputs[][12] = {
23 "fragOutput",
24 "fragOutput1",
25 "fragOutput2",
26 "fragOutput3",
27};
28
30{
31 QSSG_ASSERT(std::size(defaultFragOutputs) > index, return defaultFragOutputs[0]);
32 return defaultFragOutputs[index];
33}
34
35static inline QMatrix4x4 correctMVPForScissor(QRectF viewportRect, QRect scissorRect, bool isYUp) {
36 const auto &scissorCenter = scissorRect.center();
37 const auto &viewCenter = viewportRect.center();
38 const float scaleX = viewportRect.width() / float(scissorRect.width());
39 const float scaleY = viewportRect.height() / float(scissorRect.height());
40 const float dx = 2 * (viewCenter.x() - scissorCenter.x()) / scissorRect.width();
41 const float dyRect = isYUp ? (scissorCenter.y() - viewCenter.y())
42 : (viewCenter.y() - scissorCenter.y());
43 const float dy = 2 * dyRect / scissorRect.height();
44
45 return QMatrix4x4(scaleX, 0.0f, 0.0f, dx,
46 0.0f, scaleY, 0.0f, dy,
47 0.0f, 0.0f, 1.0f, 0.0f,
48 0.0f, 0.0f, 0.0f, 1.0f);
49}
50
55
56
57// MOTION VECTOR PASS
58
60{
61 Q_UNUSED(renderer)
62 using namespace RenderHelpers;
63
64 QSSG_ASSERT(!data.renderedCameras.isEmpty(), return);
65 camera = data.renderedCameras[0];
66 QMatrix4x4 viewProjection(Qt::Uninitialized);
67 QMatrix4x4 cameraGlobalTransform(Qt::Uninitialized);
68 cameraGlobalTransform = data.getGlobalTransform(*camera);
69 data.renderedCameras[0]->calculateViewProjectionMatrix(cameraGlobalTransform, viewProjection);
70
71 const auto &layerPrepResult = data.layerPrepResult;
72 const auto &rhiCtx = renderer.contextInterface()->rhiContext();
73
74 const auto &renderedOpaques = data.getSortedOpaqueRenderableObjects(*camera);
75 const auto &renderedTransparent = data.getSortedTransparentRenderableObjects(*camera);
76
77 rhiMotionVectorTexture = data.getRenderResult(QSSGRenderResult::Key::MotionVectorTexture);
78 bool textureReady = rhiMotionVectorTexture &&
79 rhiPrepareMotionVectorTexture(rhiCtx.get(), layerPrepResult.textureDimensions(), rhiMotionVectorTexture);
80
81 if (!textureReady) {
82 rhiMotionVectorTexture = nullptr;
83 enabled = false;
84 return;
85 }
86
87 motionVectorMapManager = data.requestMotionVectorMapManager();
88 ps = data.getPipelineState();
89 ps.flags |= { QSSGRhiGraphicsPipelineState::Flag::DepthTestEnabled,
90 QSSGRhiGraphicsPipelineState::Flag::DepthWriteEnabled };
91 ps.samples = 1;
92 ps.viewCount = data.layer.viewCount;
93
94 for (int i = 0; i < MaxBuckets; ++i)
95 motionVectorPassObjects[i].clear();
96 enabled = false;
97
98 for (const auto &handles : { &renderedOpaques, &renderedTransparent }) {
99 for (const auto &handle : *handles) {
100 if (handle.obj->type == QSSGRenderableObject::Type::DefaultMaterialMeshSubset ||
101 handle.obj->type == QSSGRenderableObject::Type::CustomMaterialMeshSubset) {
102 QSSGSubsetRenderable *renderable(static_cast<QSSGSubsetRenderable *>(handle.obj));
103 if (handle.obj->renderableFlags.isMotionVectorParticipant()) {
104 bool skin = renderable->modelContext.model.usesBoneTexture();
105 bool instance = renderable->modelContext.model.instanceCount() > 0;
106 bool morph = renderable->modelContext.model.morphTargets.size() > 0;
107 int bucketIndex = (int(skin) << 2) | (int(instance) << 1) | int(morph);
108 motionVectorPassObjects[bucketIndex].push_back(handle);
109
110 QSSGRhiGraphicsPipelineState tempPs = ps;
111
112 rhiPrepareMotionVectorRenderable(rhiCtx.get(),
113 this,
114 data,
115 viewProjection,
116 *handle.obj,
117 rhiMotionVectorTexture->rpDesc,
118 &tempPs,
119 *motionVectorMapManager);
120 enabled = true;
121 }
122 }
123 }
124 }
125}
126
128{
129 using namespace RenderHelpers;
130 if (enabled) {
131 const auto &rhiCtx = renderer.contextInterface()->rhiContext();
132 QSSG_ASSERT(rhiCtx->rhi()->isRecordingFrame(), return);
133 QRhiCommandBuffer *cb = rhiCtx->commandBuffer();
134 cb->debugMarkBegin(QByteArrayLiteral("Quick3D motion vector map"));
135 Q_TRACE_SCOPE(QSSG_renderPass, QStringLiteral("Quick3D motion vector map"));
136
137 if (Q_LIKELY(rhiMotionVectorTexture && rhiMotionVectorTexture->isValid())) {
138 cb->beginPass(rhiMotionVectorTexture->rt, QColor(0, 0, 0, 0), { 1.0f, 0 }, nullptr, rhiCtx->commonPassFlags());
139 QSSGRHICTX_STAT(rhiCtx, beginRenderPass(rhiMotionVectorTexture->rt));
140 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderPass);
141
142 rhiRenderMotionVector(rhiCtx.get(),
143 ps,
144 motionVectorPassObjects,
145 MaxBuckets);
146
147 cb->endPass();
148 QSSGRHICTX_STAT(rhiCtx, endRenderPass());
149 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DRenderPass, 0, QByteArrayLiteral("motion_vector_map"));
150 }
151 cb->debugMarkEnd();
152 }
153}
154
156{
157 QSSG_CHECK(motionVectorMapManager);
158 motionVectorMapManager->endFrame();
159 camera = nullptr;
160 ps = {};
161 rhiMotionVectorTexture = nullptr;
162 for (int i = 0; i < MaxBuckets; ++i)
163 motionVectorPassObjects[i].clear();
164}
165
166// SHADOW PASS
167
169{
170 Q_UNUSED(renderer)
171 using namespace RenderHelpers;
172
173 QSSG_ASSERT(!data.renderedCameras.isEmpty(), return);
174 camera = data.renderedCameras[0];
175
176 QSSG_ASSERT(shadowPassObjects.isEmpty(), shadowPassObjects.clear());
177
178 data.getShadowCastingObjects(*camera, shadowPassObjects, castingObjectsBox, receivingObjectsBox);
179
180 globalLights = data.globalLights;
181 enabled = !shadowPassObjects.isEmpty() || !globalLights.isEmpty();
182
183 if (enabled) {
184 shadowMapManager = data.requestShadowMapManager();
185
186 ps = data.getPipelineState();
187 ps.flags |= { QSSGRhiGraphicsPipelineState::Flag::DepthTestEnabled, QSSGRhiGraphicsPipelineState::Flag::DepthWriteEnabled };
188 // Try reducing self-shadowing and artifacts.
189 ps.depthBias = 2;
190 ps.slopeScaledDepthBias = 1.5f;
191
192 if (!debugCamera) {
193 debugCamera = std::make_unique<QSSGRenderCamera>(QSSGRenderGraphObject::Type::OrthographicCamera);
194 }
195 }
196}
197
199{
200 using namespace RenderHelpers;
201
202 // INPUT: Sorted opaque and transparent + depth, global lights (scoped lights not supported) and camera.
203
204 // DEPENDECY: None
205
206 // OUTPUT: Texture (Shadowmap Texture Atlas)
207
208 // CONDITION: Lights (shadowPassObjects)
209
210 if (enabled) {
211 const auto &rhiCtx = renderer.contextInterface()->rhiContext();
212 QSSG_ASSERT(rhiCtx->rhi()->isRecordingFrame(), return);
213 QRhiCommandBuffer *cb = rhiCtx->commandBuffer();
214 cb->debugMarkBegin(QByteArrayLiteral("Quick3D shadow map"));
215 Q_TRACE_SCOPE(QSSG_renderPass, QStringLiteral("Quick3D shadow map"));
216 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderPass);
217
218 QSSG_CHECK(shadowMapManager);
219 rhiRenderShadowMap(rhiCtx.get(),
220 this,
221 ps,
222 *shadowMapManager,
223 *camera,
224 debugCamera.get(),
225 globalLights, // scoped lights are not relevant here
226 shadowPassObjects,
227 renderer,
228 castingObjectsBox,
229 receivingObjectsBox);
230
231 cb->debugMarkEnd();
232 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DRenderPass, 0, QByteArrayLiteral("shadow_map"));
233 }
234}
235
237{
238 enabled = false;
239 camera = nullptr;
240 castingObjectsBox = {};
241 receivingObjectsBox = {};
242 ps = {};
243 shadowPassObjects.clear();
244 globalLights.clear();
245}
246
247// REFLECTIONMAP PASS
248
250{
251 // Read the environment variable to check if the old behavior of including the screen texture objects
252 // in the reflection pass should be kept for compatibility reasons.
253 // Besides being expensive, we shouldn't be using the main screen texture in the probes at all.
254 m_includeSTO = qEnvironmentVariableIntValue("QT_QUICK3D_REFLECTION_PASS_INCLUDE_SCREEN_TEXTURE_OBJECTS") != 0;
255}
256
258{
259 Q_UNUSED(renderer);
260 Q_UNUSED(data);
261
262 QSSG_ASSERT(!data.renderedCameras.isEmpty(), return);
263 QSSGRenderCamera *camera = data.renderedCameras[0];
264
265 ps = data.getPipelineState();
266 ps.flags |= { QSSGRhiGraphicsPipelineState::Flag::DepthTestEnabled,
267 QSSGRhiGraphicsPipelineState::Flag::DepthWriteEnabled,
268 QSSGRhiGraphicsPipelineState::Flag::BlendEnabled };
269
270 reflectionProbes = { data.reflectionProbesView.begin(), data.reflectionProbesView.end() };
271 reflectionMapManager = data.requestReflectionMapManager();
272
273 const auto &sortedOpaqueObjects = data.getSortedOpaqueRenderableObjects(*camera);
274 const auto &sortedTransparentObjects = data.getSortedTransparentRenderableObjects(*camera);
275 QSSGRenderableObjectList emptyList{};
276 const auto &sortedScreenTextureObjects = m_includeSTO ? data.getSortedScreenTextureRenderableObjects(*camera) : emptyList;
277
278 QSSG_ASSERT(reflectionPassObjects.isEmpty(), reflectionPassObjects.clear());
279
280 // NOTE: We should consider keeping track of the reflection casting objects to avoid
281 // filtering this list on each prep.
282 for (const auto &handles : { &sortedOpaqueObjects, &sortedTransparentObjects, &sortedScreenTextureObjects }) {
283 for (const auto &handle : *handles) {
284 if (handle.obj->renderableFlags.testFlag(QSSGRenderableObjectFlag::CastsReflections))
285 reflectionPassObjects.push_back(handle);
286 }
287 }
288}
289
291{
292 using namespace RenderHelpers;
293
294 // INPUT: Reflection probes, sorted opaque and transparent
295
296 // DEPENDECY: None
297
298 // OUTPUT: Cube maps (1 per probe)
299
300 // NOTE: Full pass with a sky box pass
301
302 // CONDITION: Probes and sorted opaque and transparent
303
304 const auto &rhiCtx = renderer.contextInterface()->rhiContext();
305 QSSG_ASSERT(rhiCtx->rhi()->isRecordingFrame(), return);
306 QRhiCommandBuffer *cb = rhiCtx->commandBuffer();
307
308 const auto *layerData = QSSGLayerRenderData::getCurrent(renderer);
309 QSSG_ASSERT(layerData, return);
310
311 QSSG_CHECK(reflectionMapManager);
312 if (!reflectionPassObjects.isEmpty() || !reflectionProbes.isEmpty()) {
313 cb->debugMarkBegin(QByteArrayLiteral("Quick3D reflection map"));
314 Q_TRACE_SCOPE(QSSG_renderPass, QStringLiteral("Quick3D reflection map"));
315 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderPass);
316 rhiRenderReflectionMap(rhiCtx.get(),
317 this,
318 *layerData,
319 &ps,
320 *reflectionMapManager,
321 reflectionProbes,
322 reflectionPassObjects,
323 renderer);
324
325 cb->debugMarkEnd();
326 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DRenderPass, 0, QByteArrayLiteral("reflection_map"));
327 }
328}
329
331{
332 ps = {};
333 reflectionProbes.clear();
334 reflectionPassObjects.clear();
335}
336
337// ZPrePass
339{
340 using namespace RenderHelpers;
341
342 // INPUT: Item2Ds + depth write + depth prepass
343
344 // DEPENDECY: none
345
346 // OUTPUT: Depth buffer attchment for current target
347
348 // NOTE: Could we make the depth pass more complete and just do a blit here?
349 //
350 // 1. If we have a depth map, just do a blit and then update with the rest
351 // 2. If we don't have a depth map (and/or SSAO) consider using a lower lod level.
352
353 // CONDITION: Input + globally enabled or ?
354
355 QSSG_ASSERT(!data.renderedCameras.isEmpty(), return);
356 QSSGRenderCamera *camera = data.renderedCameras[0];
357
358 const auto &rhiCtx = renderer.contextInterface()->rhiContext();
359 QSSG_ASSERT(rhiCtx->rhi()->isRecordingFrame(), return);
360 QRhiCommandBuffer *cb = rhiCtx->commandBuffer();
361 ps = data.getPipelineState();
362
363 renderedDepthWriteObjects = data.getSortedRenderedDepthWriteObjects(*camera);
364 renderedOpaqueDepthPrepassObjects = data.getSortedrenderedOpaqueDepthPrepassObjects(*camera);
365
366 cb->debugMarkBegin(QByteArrayLiteral("Quick3D prepare Z prepass"));
367 Q_TRACE_SCOPE(QSSG_renderPass, QStringLiteral("Quick3D prepare Z prepass"));
368 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderPass);
369 active = rhiPrepareDepthPass(rhiCtx.get(), this, ps, rhiCtx->mainRenderPassDescriptor(), data,
370 renderedDepthWriteObjects, renderedOpaqueDepthPrepassObjects,
371 rhiCtx->mainPassSampleCount(), data.layer.viewCount);
372 data.setZPrePassPrepResult(active);
373 cb->debugMarkEnd();
374 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DRenderPass, 0, QByteArrayLiteral("prepare_z_prepass"));
375}
376
378{
379 using namespace RenderHelpers;
380
381 const auto &rhiCtx = renderer.contextInterface()->rhiContext();
382 QSSG_ASSERT(rhiCtx->rhi()->isRecordingFrame(), return);
383
384 bool needsSetViewport = true;
385 QRhiCommandBuffer *cb = rhiCtx->commandBuffer();
386
387 if (active) {
388 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderPass);
389 Q_TRACE_SCOPE(QSSG_renderPass, QStringLiteral("Quick3D render Z prepass"));
390 cb->debugMarkBegin(QByteArrayLiteral("Quick3D render Z prepass"));
391 rhiRenderDepthPass(rhiCtx.get(), ps, renderedDepthWriteObjects, renderedOpaqueDepthPrepassObjects, &needsSetViewport);
392 cb->debugMarkEnd();
393 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DRenderPass, 0, QByteArrayLiteral("render_z_prepass"));
394 }
395}
396
398{
399 renderedDepthWriteObjects.clear();
400 renderedOpaqueDepthPrepassObjects.clear();
401 ps = {};
402 active = false;
403}
404
405// SSAO PASS
407{
408 using namespace RenderHelpers;
409
410 // Assumption for now is that all passes are keept alive and only reset once a frame is done.
411 // I.e., holding data like this should be safe (If that's no longer the case we need to do ref counting
412 // for shared data).
413
414 const auto &rhiCtx = renderer.contextInterface()->rhiContext();
415 QSSG_ASSERT(rhiCtx->rhi()->isRecordingFrame(), return);
416
417 rhiAoTexture = data.getRenderResult(QSSGRenderResult::Key::AoTexture);
418 rhiDepthTexture = data.getRenderResult(QSSGRenderResult::Key::DepthTexture);
419 QSSG_ASSERT_X(!data.renderedCameras.isEmpty(), "Preparing AO pass failed, missing camera", return);
420 camera = data.renderedCameras[0];
421 QSSG_ASSERT_X((rhiDepthTexture && rhiDepthTexture->isValid()), "Preparing AO pass failed, missing equired texture(s)", return);
422
423 const auto &shaderCache = renderer.contextInterface()->shaderCache();
424 ssaoShaderPipeline = shaderCache->getBuiltInRhiShaders().getRhiSsaoShader(data.layer.viewCount);
425 aoSettings = { data.layer.aoStrength, data.layer.aoDistance, data.layer.aoSoftness, data.layer.aoBias, data.layer.aoSamplerate, data.layer.aoDither };
426
427 ps = data.getPipelineState();
428 const auto &layerPrepResult = data.layerPrepResult;
429 const bool ready = rhiAoTexture && rhiPrepareAoTexture(rhiCtx.get(), layerPrepResult.textureDimensions(), rhiAoTexture, data.layer.viewCount);
430
431 if (Q_UNLIKELY(!ready))
432 rhiAoTexture = nullptr;
433}
434
436{
437 using namespace RenderHelpers;
438
439 // INPUT: Camera + depth map
440
441 // DEPENDECY: Depth map (zprepass)
442
443 // OUTPUT: AO Texture
444
445 // NOTE:
446
447 // CONDITION: SSAO enabled
448 QSSG_ASSERT(camera && rhiDepthTexture && rhiDepthTexture->isValid(), return);
449
450 const auto &rhiCtx = renderer.contextInterface()->rhiContext();
451 QSSG_ASSERT(rhiCtx->rhi()->isRecordingFrame(), return);
452
453 QRhiCommandBuffer *cb = rhiCtx->commandBuffer();
454 cb->debugMarkBegin(QByteArrayLiteral("Quick3D SSAO map"));
455 Q_TRACE_SCOPE(QSSG_renderPass, QStringLiteral("Quick3D SSAO map"));
456 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderPass);
457
458 if (Q_LIKELY(rhiAoTexture && rhiAoTexture->isValid())) {
459 rhiRenderAoTexture(rhiCtx.get(),
460 this,
461 renderer,
462 *ssaoShaderPipeline,
463 ps,
464 aoSettings,
465 *rhiAoTexture,
466 *rhiDepthTexture,
467 *camera);
468 }
469
470 cb->debugMarkEnd();
471 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DRenderPass, 0, QByteArrayLiteral("ssao_map"));
472}
473
475{
476 rhiDepthTexture = nullptr;
477 rhiAoTexture = nullptr;
478 camera = nullptr;
479 ps = {};
480 aoSettings = {};
481}
482
483// DEPTH TEXTURE PASS
484void DepthMapPass::renderPrep(QSSGRenderer &renderer, QSSGLayerRenderData &data)
485{
486 using namespace RenderHelpers;
487
488 QSSG_ASSERT(!data.renderedCameras.isEmpty(), return);
489 QSSGRenderCamera *camera = data.renderedCameras[0];
490
491 const auto &rhiCtx = renderer.contextInterface()->rhiContext();
492 QSSG_ASSERT(rhiCtx->rhi()->isRecordingFrame(), return);
493 const auto &layerPrepResult = data.layerPrepResult;
494 bool ready = false;
495 ps = data.getPipelineState();
496
497 if (m_multisampling) {
498 ps.samples = rhiCtx->mainPassSampleCount();
499 rhiDepthTexture = data.getRenderResult(QSSGRenderResult::Key::DepthTextureMS);
500 } else {
501 ps.samples = 1;
502 rhiDepthTexture = data.getRenderResult(QSSGRenderResult::Key::DepthTexture);
503 }
504
505 if (Q_LIKELY(rhiDepthTexture && rhiPrepareDepthTexture(rhiCtx.get(), layerPrepResult.textureDimensions(), rhiDepthTexture, data.layer.viewCount, ps.samples))) {
506 sortedOpaqueObjects = data.getSortedOpaqueRenderableObjects(*camera);
507 sortedTransparentObjects = data.getSortedTransparentRenderableObjects(*camera);
508 // the depth texture is always non-MSAA, but is a 2D array with multiview
509 ready = rhiPrepareDepthPass(rhiCtx.get(), this, ps, rhiDepthTexture->rpDesc, data,
510 sortedOpaqueObjects, sortedTransparentObjects,
511 ps.samples, data.layer.viewCount);
512 }
513
514 if (Q_UNLIKELY(!ready))
515 rhiDepthTexture = nullptr;
516}
517
518void DepthMapPass::renderPass(QSSGRenderer &renderer)
519{
520 using namespace RenderHelpers;
521
522 // INPUT: sorted objects (opaque + transparent) (maybe...)
523
524 // DEPENDECY: If this is only used for the AO case, that dictates if this should be done or not.
525
526 // OUTPUT: Texture
527
528 // NOTE: Why are we prepping opaque + transparent object if we're not using them? And why are we staying compatible with 5.15?
529 // Only used for AO? Merge into the AO pass?
530
531 // NOTES:
532 //
533 // 1: If requested, use this and blit it in the z-pre pass.
534 // 2. Why are we handling the transparent objects in the render prep (only)?
535
536 // CONDITION:
537
538 const auto &rhiCtx = renderer.contextInterface()->rhiContext();
539 QSSG_ASSERT(rhiCtx->rhi()->isRecordingFrame(), return);
540 QRhiCommandBuffer *cb = rhiCtx->commandBuffer();
541 cb->debugMarkBegin(QByteArrayLiteral("Quick3D depth texture"));
542
543 if (Q_LIKELY(rhiDepthTexture && rhiDepthTexture->isValid())) {
544 bool needsSetViewport = true;
545 cb->beginPass(rhiDepthTexture->rt, Qt::transparent, { 1.0f, 0 }, nullptr, rhiCtx->commonPassFlags());
546 QSSGRHICTX_STAT(rhiCtx, beginRenderPass(rhiDepthTexture->rt));
547 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderPass);
548 // NB! We do not pass sortedTransparentObjects in the 4th
549 // argument to stay compatible with the 5.15 code base,
550 // which also does not include semi-transparent objects in
551 // the depth texture. In addition, capturing after the
552 // opaque pass, not including transparent objects, is part
553 // of the contract for screen reading custom materials,
554 // both for depth and color.
555 rhiRenderDepthPass(rhiCtx.get(), ps, sortedOpaqueObjects, {}, &needsSetViewport);
556 cb->endPass();
557 QSSGRHICTX_STAT(rhiCtx, endRenderPass());
558 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DRenderPass, 0, QByteArrayLiteral("depth_texture"));
559 }
560
561 cb->debugMarkEnd();
562}
563
564void DepthMapPass::resetForFrame()
565{
566 rhiDepthTexture = nullptr;
567 sortedOpaqueObjects.clear();
568 sortedTransparentObjects.clear();
569 ps = {};
570}
571
572// NORMAL TEXTURE PASS
573
574void NormalPass::renderPrep(QSSGRenderer &renderer, QSSGLayerRenderData &data)
575{
576 using namespace RenderHelpers;
577
578 QSSG_ASSERT(!data.renderedCameras.isEmpty(), return);
579 QSSGRenderCamera *camera = data.renderedCameras[0];
580
581 const auto &rhiCtx = renderer.contextInterface()->rhiContext();
582 QRhi *rhi = rhiCtx->rhi();
583 QSSG_ASSERT(rhi->isRecordingFrame(), return);
584 const auto &layerPrepResult = data.layerPrepResult;
585
586 // the normal texture is not multiview-dependent and it is always a 2D texture
587 // (so not an array with multiview either)
588
589 // the normal texture is always non-MSAA
590
591 ps = data.getPipelineState();
592 ps.samples = 1;
593 ps.viewCount = 1;
594
595 sortedOpaqueObjects = data.getSortedOpaqueRenderableObjects(*camera);
596
597 // transparent objects are not included in the normal texture pass
598
599 QSSGShaderFeatures shaderFeatures = data.getShaderFeatures();
600 shaderFeatures.set(QSSGShaderFeatures::Feature::NormalPass, true);
601
602 normalTexture = data.getRenderResult(QSSGRenderResult::Key::NormalTexture);
603
604 const QSize size = layerPrepResult.textureDimensions();
605 bool needsBuild = false;
606
607 if (!normalTexture->texture) {
608 QRhiTexture::Format format = QRhiTexture::RGBA16F;
609 if (!rhi->isTextureFormatSupported(format)) {
610 qWarning("No float formats, not great");
611 format = QRhiTexture::RGBA8;
612 }
613 normalTexture->texture = rhiCtx->rhi()->newTexture(format, size, 1, QRhiTexture::RenderTarget);
614 needsBuild = true;
615 normalTexture->texture->setName(QByteArrayLiteral("Normal texture"));
616 } else if (normalTexture->texture->pixelSize() != size) {
617 normalTexture->texture->setPixelSize(size);
618 needsBuild = true;
619 }
620
621 if (!normalTexture->depthStencil) {
622 normalTexture->depthStencil = rhi->newRenderBuffer(QRhiRenderBuffer::DepthStencil, size);
623 needsBuild = true;
624 } else if (normalTexture->depthStencil->pixelSize() != size) {
625 normalTexture->depthStencil->setPixelSize(size);
626 needsBuild = true;
627 }
628
629 if (needsBuild) {
630 if (!normalTexture->texture->create()) {
631 qWarning("Failed to build normal texture (size %dx%d, format %d)",
632 size.width(), size.height(), int(normalTexture->texture->format()));
633 normalTexture->reset();
634 return;
635 }
636
637 if (!normalTexture->depthStencil->create()) {
638 qWarning("Failed to build depth-stencil buffer for normal texture (size %dx%d)",
639 size.width(), size.height());
640 normalTexture->reset();
641 return;
642 }
643
644 normalTexture->resetRenderTarget();
645
646 QRhiTextureRenderTargetDescription rtDesc;
647 QRhiColorAttachment colorAttachment(normalTexture->texture);
648 rtDesc.setColorAttachments({ colorAttachment });
649 rtDesc.setDepthStencilBuffer(normalTexture->depthStencil);
650
651 normalTexture->rt = rhi->newTextureRenderTarget(rtDesc);
652 normalTexture->rt->setName(QByteArrayLiteral("Normal texture RT"));
653 normalTexture->rpDesc = normalTexture->rt->newCompatibleRenderPassDescriptor();
654 normalTexture->rt->setRenderPassDescriptor(normalTexture->rpDesc);
655 if (!normalTexture->rt->create()) {
656 qWarning("Failed to build render target for normal texture");
657 normalTexture->reset();
658 return;
659 }
660 }
661
662 rhiPrepareNormalPass(rhiCtx.get(), this, ps, normalTexture->rpDesc, data, sortedOpaqueObjects);
663}
664
665void NormalPass::renderPass(QSSGRenderer &renderer)
666{
667 using namespace RenderHelpers;
668
669 const auto &rhiCtx = renderer.contextInterface()->rhiContext();
670 QSSG_ASSERT(rhiCtx->rhi()->isRecordingFrame(), return);
671 QRhiCommandBuffer *cb = rhiCtx->commandBuffer();
672 cb->debugMarkBegin(QByteArrayLiteral("Quick3D normal texture"));
673
674 if (Q_LIKELY(normalTexture && normalTexture->isValid())) {
675 bool needsSetViewport = true;
676 cb->beginPass(normalTexture->rt, Qt::transparent, { 1.0f, 0 }, nullptr, rhiCtx->commonPassFlags());
677 QSSGRHICTX_STAT(rhiCtx, beginRenderPass(normalTexture->rt));
678 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderPass);
679
680 rhiRenderNormalPass(rhiCtx.get(), ps, sortedOpaqueObjects, &needsSetViewport);
681
682 cb->endPass();
683 QSSGRHICTX_STAT(rhiCtx, endRenderPass());
684 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DRenderPass, 0, QByteArrayLiteral("normal_texture"));
685 }
686
687 cb->debugMarkEnd();
688}
689
690void NormalPass::resetForFrame()
691{
692 normalTexture = nullptr;
693 depthBuffer = nullptr;
694 sortedOpaqueObjects.clear();
695 ps = {};
696}
697
698// SCREEN TEXTURE PASS
699
701{
702 using namespace RenderHelpers;
703
704 QSSG_ASSERT(!data.renderedCameras.isEmpty(), return);
705 QSSGRenderCamera *camera = data.renderedCameras[0];
706
707 const auto &rhiCtx = renderer.contextInterface()->rhiContext();
708 QSSG_ASSERT(rhiCtx->rhi()->isRecordingFrame(), return);
709 rhiScreenTexture = data.getRenderResult(QSSGRenderResult::Key::ScreenTexture);
710 auto &layer = data.layer;
711 const auto &layerPrepResult = data.layerPrepResult;
712 wantsMips = layerPrepResult.getFlags().requiresMipmapsForScreenTexture();
713 sortedOpaqueObjects = data.getSortedOpaqueRenderableObjects(*camera);
714 ps = data.getPipelineState();
715 ps.samples = 1; // screen texture is always non-MSAA
716 ps.viewCount = data.layer.viewCount; // but is a 2D texture array when multiview
717
718 if (layer.background == QSSGRenderLayer::Background::Color)
719 clearColor = QColor::fromRgbF(layer.clearColor.x(), layer.clearColor.y(), layer.clearColor.z());
720
721 if (rhiCtx->rhi()->isFeatureSupported(QRhi::TexelFetch)) {
722 if (layer.background == QSSGRenderLayer::Background::SkyBoxCubeMap && layer.skyBoxCubeMap) {
723 if (!skyboxCubeMapPass)
724 skyboxCubeMapPass = SkyboxCubeMapPass();
725
726 skyboxCubeMapPass->skipTonemapping = true;
727 skyboxCubeMapPass->renderPrep(renderer, data);
728
729 // The pass expects to output to the main render target, but we have
730 // our own texture here, possibly with a differing sample count, so
731 // override the relevant settings once renderPrep() is done.
732 skyboxCubeMapPass->ps.samples = ps.samples;
733
734 skyboxPass = std::nullopt;
735 } else if (layer.background == QSSGRenderLayer::Background::SkyBox && layer.lightProbe) {
736 if (!skyboxPass)
737 skyboxPass = SkyboxPass();
738
739 skyboxPass->skipTonemapping = true;
740 skyboxPass->renderPrep(renderer, data);
741
742 skyboxPass->ps.samples = ps.samples;
743
744 skyboxCubeMapPass = std::nullopt;
745 }
746 }
747
748 bool ready = false;
749 if (Q_LIKELY(rhiScreenTexture && rhiPrepareScreenTexture(rhiCtx.get(), layerPrepResult.textureDimensions(), wantsMips, rhiScreenTexture, layer.viewCount))) {
750 ready = true;
751 if (skyboxCubeMapPass)
752 skyboxCubeMapPass->rpDesc = rhiScreenTexture->rpDesc;
753 if (skyboxPass)
754 skyboxPass->rpDesc = rhiScreenTexture->rpDesc;
755 // NB: not compatible with disabling LayerEnableDepthTest
756 // because there are effectively no "opaque" objects then.
757 // Disable Tonemapping for all materials in the screen pass texture
758 shaderFeatures = data.getShaderFeatures();
759 shaderFeatures.disableTonemapping();
760 const auto &sortedOpaqueObjects = data.getSortedOpaqueRenderableObjects(*camera);
761 for (const auto &handle : sortedOpaqueObjects) {
762 // Reflection cube maps are not available at this point, make sure they are turned off.
763 bool recRef = handle.obj->renderableFlags.receivesReflections();
764 handle.obj->renderableFlags.setReceivesReflections(false);
765 rhiPrepareRenderableForScreenMapPass(rhiCtx.get(), this, data, *handle.obj, rhiScreenTexture->rpDesc, &ps, shaderFeatures, 1, data.layer.viewCount);
766 handle.obj->renderableFlags.setReceivesReflections(recRef);
767 }
768 }
769
770 if (Q_UNLIKELY(!ready))
771 rhiScreenTexture = nullptr;
772}
773
775{
776 using namespace RenderHelpers;
777
778 // INPUT: Sorted opaque objects + depth objects
779
780 // DEPENDECY: Depth pass (if enabled)
781
782 // OUTPUT: Texture (screen texture).
783
784 // NOTE: Used for refrection and effects (?)
785
786 // CONDITION:
787
788 const auto &rhiCtx = renderer.contextInterface()->rhiContext();
789 QSSG_ASSERT(rhiCtx->rhi()->isRecordingFrame(), return);
790 QRhiCommandBuffer *cb = rhiCtx->commandBuffer();
791
792 cb->debugMarkBegin(QByteArrayLiteral("Quick3D screen texture"));
793
794 if (Q_LIKELY(rhiScreenTexture && rhiScreenTexture->isValid())) {
795 cb->beginPass(rhiScreenTexture->rt, clearColor, { 1.0f, 0 }, nullptr, rhiCtx->commonPassFlags());
796 QSSGRHICTX_STAT(rhiCtx, beginRenderPass(rhiScreenTexture->rt));
797 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderPass);
798
799 bool needsSetViewport = true;
800 for (const auto &handle : std::as_const(sortedOpaqueObjects))
801 rhiRenderRenderable(rhiCtx.get(), ps, *handle.obj, &needsSetViewport);
802
803 if (skyboxCubeMapPass)
804 skyboxCubeMapPass->renderPass(renderer);
805 else if (skyboxPass)
806 skyboxPass->renderPass(renderer);
807
808 QRhiResourceUpdateBatch *rub = nullptr;
809 if (wantsMips) {
810 rub = rhiCtx->rhi()->nextResourceUpdateBatch();
811 rub->generateMips(rhiScreenTexture->texture);
812 }
813 cb->endPass(rub);
814 QSSGRHICTX_STAT(rhiCtx, endRenderPass());
815 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DRenderPass, 0, QByteArrayLiteral("screen_texture"));
816 }
817
818 cb->debugMarkEnd();
819}
820
822{
823 rhiScreenTexture = nullptr;
824 if (skyboxPass)
825 skyboxPass->resetForFrame();
826 if (skyboxCubeMapPass)
827 skyboxCubeMapPass->resetForFrame();
828 ps = {};
829 wantsMips = false;
830 clearColor = Qt::transparent;
831 shaderFeatures = {};
832 sortedOpaqueObjects.clear();
833}
834
836{
837 QSSG_ASSERT(!data.renderedCameras.isEmpty(), return);
838 QSSGRenderCamera *camera = data.renderedCameras[0];
839
840 const auto &rhiCtx = renderer.contextInterface()->rhiContext();
841 QSSG_ASSERT(rhiCtx->rhi()->isRecordingFrame(), return);
842 rhiScreenTexture = data.getRenderResult(QSSGRenderResult::Key::ScreenTexture);
843 QSSG_ASSERT_X(rhiScreenTexture && rhiScreenTexture->isValid(), "Invalid screen texture!", return);
844
845 const auto &layer = data.layer;
846 const auto shaderFeatures = data.getShaderFeatures();
847 const bool layerEnableDepthTest = layer.layerFlags.testFlag(QSSGRenderLayer::LayerFlag::EnableDepthTest);
848
849 QRhiRenderPassDescriptor *mainRpDesc = rhiCtx->mainRenderPassDescriptor();
850 const int samples = rhiCtx->mainPassSampleCount();
851 const int viewCount = data.layer.viewCount;
852
853 // NOTE: We're piggybacking on the screen map pass for now, but we could do better.
854 ps = data.getPipelineState();
855 const bool depthTestEnabled = (data.screenMapPass.ps.flags.testFlag(QSSGRhiGraphicsPipelineState::Flag::DepthTestEnabled));
856 ps.flags.setFlag(QSSGRhiGraphicsPipelineState::Flag::DepthTestEnabled, depthTestEnabled);
857 const bool depthWriteEnabled = (data.screenMapPass.ps.flags.testFlag(QSSGRhiGraphicsPipelineState::Flag::DepthWriteEnabled));
858 ps.flags.setFlag(QSSGRhiGraphicsPipelineState::Flag::DepthWriteEnabled, depthWriteEnabled);
859 sortedScreenTextureObjects = data.getSortedScreenTextureRenderableObjects(*camera);
860 for (const auto &handle : std::as_const(sortedScreenTextureObjects)) {
861 QSSGRenderableObject *theObject = handle.obj;
862 const auto depthWriteMode = theObject->depthWriteMode;
863 ps.flags.setFlag(QSSGRhiGraphicsPipelineState::Flag::BlendEnabled, theObject->renderableFlags.hasTransparency());
864 const bool curDepthWriteEnabled = !(depthWriteMode == QSSGDepthDrawMode::Never || depthWriteMode == QSSGDepthDrawMode::OpaquePrePass
865 || data.isZPrePassActive() || !layerEnableDepthTest);
866 ps.flags.setFlag(QSSGRhiGraphicsPipelineState::Flag::DepthWriteEnabled, curDepthWriteEnabled);
867 RenderHelpers::rhiPrepareRenderable(rhiCtx.get(), this, data, *theObject, mainRpDesc, &ps, shaderFeatures, samples, viewCount);
868 }
869}
870
872{
873 if (QSSG_GUARD(rhiScreenTexture && rhiScreenTexture->isValid())) {
874 const auto &rhiCtx = renderer.contextInterface()->rhiContext();
875 QSSG_ASSERT(rhiCtx->rhi()->isRecordingFrame(), return);
876 QRhiCommandBuffer *cb = rhiCtx->commandBuffer();
877
878 // 3. Screen texture depended objects
879 cb->debugMarkBegin(QByteArrayLiteral("Quick3D render screen texture dependent"));
880 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderPass);
881 Q_TRACE(QSSG_renderPass_entry, QStringLiteral("Quick3D render screen texture dependent"));
882 bool needsSetViewport = true;
883 for (const auto &handle : std::as_const(sortedScreenTextureObjects)) {
884 QSSGRenderableObject *theObject = handle.obj;
885 RenderHelpers::rhiRenderRenderable(rhiCtx.get(), ps, *theObject, &needsSetViewport);
886 }
887 cb->debugMarkEnd();
888 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DRenderPass, 0, QByteArrayLiteral("screen_texture_dependent"));
889 Q_TRACE(QSSG_renderPass_exit);
890 }
891}
892
894{
895 sortedScreenTextureObjects.clear();
896 rhiScreenTexture = nullptr;
897 ps = {};
898}
899
902 QSSGPassKey passKey,
903 QSSGRhiGraphicsPipelineState &ps,
904 QSSGShaderFeatures shaderFeatures,
905 QRhiRenderPassDescriptor *rpDesc,
906 const QSSGRenderableObjectList &sortedOpaqueObjects)
907{
908 const auto &rhiCtx = ctx.rhiContext();
909 QSSG_ASSERT(rpDesc && rhiCtx->rhi()->isRecordingFrame(), return);
910
911 const auto &layer = data.layer;
912 const bool layerEnableDepthTest = layer.layerFlags.testFlag(QSSGRenderLayer::LayerFlag::EnableDepthTest);
913
914 for (const auto &handle : std::as_const(sortedOpaqueObjects)) {
915 QSSGRenderableObject *theObject = handle.obj;
916 const auto depthWriteMode = theObject->depthWriteMode;
917 const bool curDepthWriteEnabled = !(depthWriteMode == QSSGDepthDrawMode::Never ||
918 depthWriteMode == QSSGDepthDrawMode::OpaquePrePass ||
919 data.isZPrePassActive() || !layerEnableDepthTest);
920 ps.flags.setFlag(QSSGRhiGraphicsPipelineState::Flag::DepthWriteEnabled, curDepthWriteEnabled);
921 RenderHelpers::rhiPrepareRenderable(rhiCtx.get(), passKey, data, *theObject, rpDesc, &ps, shaderFeatures, ps.samples, ps.viewCount);
922 }
923}
924
926 const QSSGRhiGraphicsPipelineState &ps,
927 const QSSGRenderableObjectList &sortedOpaqueObjects)
928{
929 const auto &rhiCtx = ctx.rhiContext();
930 QSSG_ASSERT(rhiCtx->rhi()->isRecordingFrame(), return);
931 bool needsSetViewport = true;
932 for (const auto &handle : std::as_const(sortedOpaqueObjects)) {
933 QSSGRenderableObject *theObject = handle.obj;
934 RenderHelpers::rhiRenderRenderable(rhiCtx.get(), ps, *theObject, &needsSetViewport);
935 }
936}
937
939{
940 auto *ctx = renderer.contextInterface();
941 const auto &rhiCtx = ctx->rhiContext();
942 QSSG_ASSERT(rhiCtx->rhi()->isRecordingFrame(), return);
943 QSSG_ASSERT(!data.renderedCameras.isEmpty() && data.renderedCameraData.has_value() , return);
944 QSSGRenderCamera *camera = data.renderedCameras[0];
945
946 ps = data.getPipelineState();
947 ps.samples = rhiCtx->mainPassSampleCount();
948 ps.viewCount = data.layer.viewCount;
949 ps.depthFunc = QRhiGraphicsPipeline::LessOrEqual;
950 ps.flags.setFlag(QSSGRhiGraphicsPipelineState::Flag::BlendEnabled, false);
951
952 // opaque objects (or, this list is empty when LayerEnableDepthTest is disabled)
953 sortedOpaqueObjects = data.getSortedOpaqueRenderableObjects(*camera);
954 shaderFeatures = data.getShaderFeatures();
955
956 QRhiRenderPassDescriptor *mainRpDesc = rhiCtx->mainRenderPassDescriptor();
957 prep(*ctx, data, this, ps, shaderFeatures, mainRpDesc, sortedOpaqueObjects);
958}
959
961{
962 auto *ctx = renderer.contextInterface();
963 const auto &rhiCtx = ctx->rhiContext();
964 QSSG_ASSERT(rhiCtx->rhi()->isRecordingFrame(), return);
965 QRhiCommandBuffer *cb = rhiCtx->commandBuffer();
966
967 cb->debugMarkBegin(QByteArrayLiteral("Quick3D render opaque"));
968 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderPass);
969 Q_TRACE(QSSG_renderPass_entry, QStringLiteral("Quick3D render opaque"));
970 render(*ctx, ps, sortedOpaqueObjects);
971 cb->debugMarkEnd();
972 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DRenderPass, 0, QByteArrayLiteral("opaque_pass"));
973 Q_TRACE(QSSG_renderPass_exit);
974}
975
977{
978 sortedOpaqueObjects.clear();
979 ps = {};
980 shaderFeatures = {};
981}
982
985 QSSGPassKey passKey,
986 QSSGRhiGraphicsPipelineState &ps,
987 QSSGShaderFeatures shaderFeatures,
988 QRhiRenderPassDescriptor *rpDesc,
989 const QSSGRenderableObjectList &sortedTransparentObjects,
990 bool oit)
991{
992 const auto &rhiCtx = ctx.rhiContext();
993 QSSG_ASSERT(rpDesc && rhiCtx->rhi()->isRecordingFrame(), return);
994
995 const bool zPrePassActive = data.isZPrePassActive();
996 for (const auto &handle : std::as_const(sortedTransparentObjects)) {
997 QSSGRenderableObject *theObject = handle.obj;
998 const auto depthWriteMode = theObject->depthWriteMode;
999 const bool curDepthWriteEnabled = (depthWriteMode == QSSGDepthDrawMode::Always && !zPrePassActive);
1000 ps.flags.setFlag(QSSGRhiGraphicsPipelineState::Flag::DepthWriteEnabled, curDepthWriteEnabled);
1001 if (!(theObject->renderableFlags.isCompletelyTransparent())) {
1002 RenderHelpers::rhiPrepareRenderable(rhiCtx.get(), passKey, data, *theObject, rpDesc, &ps, shaderFeatures,
1003 ps.samples, ps.viewCount, nullptr, nullptr, QSSGRenderTextureCubeFaceNone, nullptr, oit);
1004 }
1005 }
1006}
1007
1009 const QSSGRhiGraphicsPipelineState &ps,
1010 const QSSGRenderableObjectList &sortedTransparentObjects)
1011{
1012 const auto &rhiCtx = ctx.rhiContext();
1013 QSSG_ASSERT(rhiCtx->rhi()->isRecordingFrame(), return);
1014 // If scissorRect is set, Item2Ds will be drawn by a workaround of modifying
1015 // viewport, not using actual 3D scissor test.
1016 // It means non-opaque objects may be affected by this viewport setting.
1017 bool needsSetViewport = true;
1018 for (const auto &handle : std::as_const(sortedTransparentObjects)) {
1019 QSSGRenderableObject *theObject = handle.obj;
1020 if (!theObject->renderableFlags.isCompletelyTransparent())
1021 RenderHelpers::rhiRenderRenderable(rhiCtx.get(), ps, *theObject, &needsSetViewport);
1022 }
1023}
1024
1026{
1027 auto *ctx = renderer.contextInterface();
1028 const auto &rhiCtx = ctx->rhiContext();
1029
1030 QSSG_ASSERT(!data.renderedCameras.isEmpty() && data.renderedCameraData.has_value() , return);
1031 QSSGRenderCamera *camera = data.renderedCameras[0];
1032
1033 QRhiRenderPassDescriptor *mainRpDesc = rhiCtx->mainRenderPassDescriptor();
1034
1035 ps = data.getPipelineState();
1036 ps.samples = rhiCtx->mainPassSampleCount();
1037 ps.viewCount = data.layer.viewCount;
1038
1039 // transparent objects (or, without LayerEnableDepthTest, all objects)
1040 ps.flags.setFlag(QSSGRhiGraphicsPipelineState::Flag::BlendEnabled, true);
1041 ps.flags.setFlag(QSSGRhiGraphicsPipelineState::Flag::DepthWriteEnabled, false);
1042
1043 shaderFeatures = data.getShaderFeatures();
1044 sortedTransparentObjects = data.getSortedTransparentRenderableObjects(*camera);
1045
1046 prep(*ctx, data, this, ps, shaderFeatures, mainRpDesc, sortedTransparentObjects);
1047}
1048
1050{
1051 auto *ctx = renderer.contextInterface();
1052 const auto &rhiCtx = ctx->rhiContext();
1053 QSSG_ASSERT(rhiCtx->rhi()->isRecordingFrame(), return);
1054 QRhiCommandBuffer *cb = rhiCtx->commandBuffer();
1055
1056 cb->debugMarkBegin(QByteArrayLiteral("Quick3D render alpha"));
1057 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderPass);
1058 Q_TRACE(QSSG_renderPass_entry, QStringLiteral("Quick3D render alpha"));
1059 render(*ctx, ps, sortedTransparentObjects);
1060 cb->debugMarkEnd();
1061 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DRenderPass, 0, QByteArrayLiteral("transparent_pass"));
1062 Q_TRACE(QSSG_renderPass_exit);
1063}
1064
1066{
1067 sortedTransparentObjects.clear();
1068 ps = {};
1069 shaderFeatures = {};
1070}
1071
1073{
1074 if (!skipPrep) {
1075 const auto &rhiCtx = renderer.contextInterface()->rhiContext();
1076 QSSG_ASSERT(rhiCtx->rhi()->isRecordingFrame(), return);
1077 QSSG_ASSERT(!data.renderedCameras.isEmpty(), return);
1078 QSSG_ASSERT(data.renderedCameras.count() == data.layer.viewCount, return);
1079 layer = &data.layer;
1080 QSSG_ASSERT(layer, return);
1081
1082 rpDesc = rhiCtx->mainRenderPassDescriptor();
1083 ps = data.getPipelineState();
1084 ps.samples = rhiCtx->mainPassSampleCount();
1085 ps.viewCount = data.layer.viewCount;
1086 ps.polygonMode = QRhiGraphicsPipeline::Fill;
1087
1088 // When there are effects, then it is up to the last pass of the
1089 // last effect to perform tonemapping, neither the skybox nor the
1090 // main render pass should alter the colors then.
1091 skipTonemapping = layer->firstEffect != nullptr;
1092
1093 RenderHelpers::rhiPrepareSkyBox(rhiCtx.get(), this, *layer, data.renderedCameras, renderer);
1094 skipPrep = true;
1095 }
1096}
1097
1099{
1100 const auto &rhiCtx = renderer.contextInterface()->rhiContext();
1101 QSSG_ASSERT(rhiCtx->rhi()->isRecordingFrame(), return);
1102 QSSG_ASSERT(layer, return);
1103
1104 QRhiShaderResourceBindings *srb = layer->skyBoxSrb;
1105 QSSG_ASSERT(srb, return);
1106
1107 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderPass);
1108 Q_TRACE_SCOPE(QSSG_renderPass, QStringLiteral("Quick3D render skybox"));
1109
1110 // Note: We get the shader here, as the screen map pass might modify the state of
1111 // the tonemap mode.
1112
1113 QSSGRenderLayer::TonemapMode tonemapMode = skipTonemapping && (layer->tonemapMode != QSSGRenderLayer::TonemapMode::Custom) ? QSSGRenderLayer::TonemapMode::None : layer->tonemapMode;
1114 const auto &shaderCache = renderer.contextInterface()->shaderCache();
1115 auto shaderPipeline = shaderCache->getBuiltInRhiShaders().getRhiSkyBoxShader(tonemapMode, layer->skyBoxIsRgbe8, layer->viewCount);
1116 QSSG_CHECK(shaderPipeline);
1117 QSSGRhiGraphicsPipelineStatePrivate::setShaderPipeline(ps, shaderPipeline.get());
1118 renderer.rhiQuadRenderer()->recordRenderQuad(rhiCtx.get(), &ps, srb, rpDesc, { QSSGRhiQuadRenderer::DepthTest | QSSGRhiQuadRenderer::RenderBehind });
1119 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DRenderPass, 0, QByteArrayLiteral("skybox_map"));
1120}
1121
1123{
1124 ps = {};
1125 layer = nullptr;
1126 skipPrep = false;
1127}
1128
1130{
1131 const auto &rhiCtx = renderer.contextInterface()->rhiContext();
1132 QSSG_ASSERT(rhiCtx->rhi()->isRecordingFrame(), return);
1133 QSSG_ASSERT(!data.renderedCameras.isEmpty(), return);
1134 QSSG_ASSERT(data.renderedCameras.count() == data.layer.viewCount, return);
1135 layer = &data.layer;
1136 QSSG_ASSERT(layer, return);
1137
1138 rpDesc = rhiCtx->mainRenderPassDescriptor();
1139 ps = data.getPipelineState();
1140 ps.samples = rhiCtx->mainPassSampleCount();
1141 ps.viewCount = data.layer.viewCount;
1142 ps.polygonMode = QRhiGraphicsPipeline::Fill;
1143
1144 QSSGRenderLayer::TonemapMode tonemapMode = skipTonemapping && (layer->tonemapMode != QSSGRenderLayer::TonemapMode::Custom) ? QSSGRenderLayer::TonemapMode::None : layer->tonemapMode;
1145
1146 const auto &shaderCache = renderer.contextInterface()->shaderCache();
1147 skyBoxCubeShader = shaderCache->getBuiltInRhiShaders().getRhiSkyBoxCubeShader(tonemapMode, !data.layer.skyBoxIsSrgb, data.layer.viewCount);
1148
1149 RenderHelpers::rhiPrepareSkyBox(rhiCtx.get(), this, *layer, data.renderedCameras, renderer, uint(tonemapMode));
1150}
1151
1153{
1154 const auto &rhiCtx = renderer.contextInterface()->rhiContext();
1155 QSSG_ASSERT(rhiCtx->rhi()->isRecordingFrame(), return);
1156 QSSG_ASSERT(layer && skyBoxCubeShader, return);
1157
1158 QRhiShaderResourceBindings *srb = layer->skyBoxSrb;
1159 QSSG_ASSERT(srb, return);
1160
1161 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderPass);
1162 Q_TRACE_SCOPE(QSSG_renderPass, QStringLiteral("Quick3D render cubemap skybox"));
1163
1164 QSSGRhiGraphicsPipelineStatePrivate::setShaderPipeline(ps, skyBoxCubeShader.get());
1165 renderer.rhiCubeRenderer()->recordRenderCube(rhiCtx.get(), &ps, srb, rpDesc, { QSSGRhiQuadRenderer::DepthTest | QSSGRhiQuadRenderer::RenderBehind });
1166 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DRenderPass, 0, QByteArrayLiteral("skybox_cube"));
1167}
1168
1170{
1171 ps = {};
1172 layer = nullptr;
1173}
1174
1176{
1177 const auto &rhiCtx = renderer.contextInterface()->rhiContext();
1178 QSSG_ASSERT(rhiCtx->rhi()->isRecordingFrame(), return);
1179 const auto &layer = data.layer;
1180
1181 const auto &item2Ds = data.getRenderableItem2Ds();
1182 prepdItem2DRenderers.reserve(size_t(item2Ds.size()));
1183 // NOTE: This marks the start of the 2D sub-scene rendering as it might result in
1184 // a nested 3D scene to be rendered and if we don't save the state here, we can
1185 // end up with a mismatched state in the QtQuick3D renderer.
1186 // See the end of this function for the corresponding end call (endSubLayerRender()).
1187 renderer.beginSubLayerRender(data);
1188 for (const auto *item2D: std::as_const(item2Ds)) {
1189 // Find data for item
1190 auto item2DData = data.getItem2DRenderer(*item2D);
1191 const auto &mvps = data.getItem2DMvps(*item2D);
1192 QSGRenderer *renderer2d = item2DData;
1193
1194 QSSG_ASSERT(renderer2d, continue);
1195
1196 // NOTE: We shouldn't get into this state...
1197 if (renderer2d && renderer2d->currentRhi() != rhiCtx->rhi()) {
1198 static bool contextWarningShown = false;
1199 if (!contextWarningShown) {
1200 qWarning () << "Scene with embedded 2D content can only be rendered in one window.";
1201 contextWarningShown = true;
1202 }
1203 continue;
1204 }
1205
1206 // Set the projection matrix
1207
1208 auto layerPrepResult = data.layerPrepResult;
1209
1210 QRhiRenderTarget *renderTarget = rhiCtx->renderTarget();
1211 auto *rpd = renderTarget->renderPassDescriptor();
1212 renderer2d->setDevicePixelRatio(renderTarget->devicePixelRatio());
1213 const QRect deviceRect(QPoint(0, 0), renderTarget->pixelSize());
1214 const int viewCount = data.layer.viewCount;
1215 if (layer.scissorRect.isValid()) {
1216 QRect effScissor = layer.scissorRect & layerPrepResult.getViewport().toRect();
1217 QMatrix4x4 correctionMat = correctMVPForScissor(layerPrepResult.getViewport(),
1218 effScissor,
1219 rhiCtx->rhi()->isYUpInNDC());
1220 for (int viewIndex = 0; viewIndex < viewCount; ++viewIndex) {
1221 const QMatrix4x4 projectionMatrix = correctionMat * mvps[viewIndex];
1222 renderer2d->setProjectionMatrix(projectionMatrix, viewIndex);
1223 }
1224 renderer2d->setViewportRect(effScissor);
1225 } else {
1226 for (int viewIndex = 0; viewIndex < viewCount; ++viewIndex)
1227 renderer2d->setProjectionMatrix(mvps[viewIndex], viewIndex);
1228 renderer2d->setViewportRect(RenderHelpers::correctViewportCoordinates(layerPrepResult.getViewport(), deviceRect));
1229 }
1230 renderer2d->setDeviceRect(deviceRect);
1231 QSGRenderTarget sgRt(renderTarget, rpd, rhiCtx->commandBuffer());
1232 sgRt.multiViewCount = data.layer.viewCount;
1233 renderer2d->setRenderTarget(sgRt);
1234 renderer2d->prepareSceneInline();
1235 prepdItem2DRenderers.push_back(renderer2d);
1236 }
1237 renderer.endSubLayerRender(data);
1238}
1239
1241{
1242 QSSG_ASSERT(prepdItem2DRenderers.size() > 0, return);
1243
1244 const auto &rhiCtx = renderer.contextInterface()->rhiContext();
1245 QSSG_ASSERT(rhiCtx->rhi()->isRecordingFrame(), return);
1246 QRhiCommandBuffer *cb = rhiCtx->commandBuffer();
1247
1248 cb->debugMarkBegin(QByteArrayLiteral("Quick3D render 2D sub-scene"));
1249 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderPass);
1250 Q_TRACE_SCOPE(QSSG_renderPass, QStringLiteral("Quick3D render 2D sub-scene"));
1251 QSSGLayerRenderData *data = QSSGLayerRenderData::getCurrent(renderer);
1252 renderer.beginSubLayerRender(*data);
1253 for (QSGRenderer *renderer2d : std::as_const(prepdItem2DRenderers))
1254 renderer2d->renderSceneInline();
1255 renderer.endSubLayerRender(*data);
1256 cb->debugMarkEnd();
1257 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DRenderPass, 0, QByteArrayLiteral("2D_sub_scene"));
1258}
1259
1261{
1262 prepdItem2DRenderers.clear();
1263}
1264
1266{
1267 const auto &rhiCtx = renderer.contextInterface()->rhiContext();
1268 QSSG_ASSERT(rhiCtx->rhi()->isRecordingFrame(), return);
1269 QSSG_ASSERT(!data.renderedCameras.isEmpty(), return);
1270 QSSG_ASSERT(data.renderedCameras.count() == data.layer.viewCount, return);
1271 layer = &data.layer;
1272 QSSG_ASSERT(layer, return);
1273
1274 const auto &shaderCache = renderer.contextInterface()->shaderCache();
1275 gridShader = shaderCache->getBuiltInRhiShaders().getRhiGridShader(data.layer.viewCount);
1276
1277 ps = data.getPipelineState();
1278 ps.samples = rhiCtx->mainPassSampleCount();
1279 ps.viewCount = data.layer.viewCount;
1280 ps.flags.setFlag(QSSGRhiGraphicsPipelineState::Flag::BlendEnabled, true);
1281 ps.polygonMode = QRhiGraphicsPipeline::Fill;
1282
1283 RenderHelpers::rhiPrepareGrid(rhiCtx.get(), this, *layer, data.renderedCameras, renderer);
1284}
1285
1287{
1288 const auto &rhiCtx = renderer.contextInterface()->rhiContext();
1289 QSSG_ASSERT(gridShader && rhiCtx->rhi()->isRecordingFrame(), return);
1290 QRhiCommandBuffer *cb = rhiCtx->commandBuffer();
1291
1292 cb->debugMarkBegin(QByteArrayLiteral("Quick3D render grid"));
1293 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderPass);
1294 Q_TRACE_SCOPE(QSSG_renderPass, QStringLiteral("Quick3D render grid"));
1295 QSSGRhiGraphicsPipelineStatePrivate::setShaderPipeline(ps, gridShader.get());
1296 QRhiShaderResourceBindings *srb = layer->gridSrb;
1297 QRhiRenderPassDescriptor *rpDesc = rhiCtx->mainRenderPassDescriptor();
1298 renderer.rhiQuadRenderer()->recordRenderQuad(rhiCtx.get(), &ps, srb, rpDesc, { QSSGRhiQuadRenderer::DepthTest });
1299 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DRenderPass, 0, QByteArrayLiteral("render_grid"));
1300}
1301
1303{
1304 ps = {};
1305 layer = nullptr;
1306}
1307
1309{
1310 const auto &rhiCtx = renderer.contextInterface()->rhiContext();
1311 QSSG_ASSERT(rhiCtx->rhi()->isRecordingFrame(), return);
1312 QSSGRhiContextPrivate *rhiCtxD = QSSGRhiContextPrivate::get(rhiCtx.get());
1313 QSSG_ASSERT(!data.renderedCameras.isEmpty(), return);
1314 QSSG_ASSERT(data.renderedCameras.count() == data.layer.viewCount, return);
1315
1316 const auto &shaderCache = renderer.contextInterface()->shaderCache();
1317 debugObjectShader = shaderCache->getBuiltInRhiShaders().getRhiDebugObjectShader(data.layer.viewCount);
1318 ps = data.getPipelineState();
1319 ps.samples = rhiCtx->mainPassSampleCount();
1320 ps.viewCount = data.layer.viewCount;
1321
1322 // debug objects
1323 const auto &debugDraw = renderer.contextInterface()->debugDrawSystem();
1324 if (debugDraw && debugDraw->hasContent()) {
1325 QRhi *rhi = rhiCtx->rhi();
1326 QRhiResourceUpdateBatch *rub = rhi->nextResourceUpdateBatch();
1327 debugDraw->prepareGeometry(rhiCtx.get(), rub);
1328 QSSGRhiDrawCallData &dcd = rhiCtxD->drawCallData({ this, nullptr, nullptr, 0 });
1329 if (!dcd.ubuf) {
1330 dcd.ubuf = rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 64 * data.renderedCameras.count());
1331 dcd.ubuf->create();
1332 }
1333 char *ubufData = dcd.ubuf->beginFullDynamicBufferUpdateForCurrentFrame();
1334 QMatrix4x4 viewProjection(Qt::Uninitialized);
1335 QMatrix4x4 cameraGlobalTransform(Qt::Uninitialized);
1336 for (qsizetype viewIdx = 0; viewIdx < data.renderedCameras.count(); ++viewIdx) {
1337 cameraGlobalTransform = data.getGlobalTransform(*data.renderedCameras[viewIdx]);
1338 data.renderedCameras[viewIdx]->calculateViewProjectionMatrix(cameraGlobalTransform, viewProjection);
1339 viewProjection = rhi->clipSpaceCorrMatrix() * viewProjection;
1340 memcpy(ubufData, viewProjection.constData() + viewIdx * 64, 64);
1341 }
1342 dcd.ubuf->endFullDynamicBufferUpdateForCurrentFrame();
1343
1344 QSSGRhiShaderResourceBindingList bindings;
1345 bindings.addUniformBuffer(0, QRhiShaderResourceBinding::VertexStage, dcd.ubuf);
1346 dcd.srb = rhiCtxD->srb(bindings);
1347
1348 rhiCtx->commandBuffer()->resourceUpdate(rub);
1349 }
1350}
1351
1353{
1354 const auto &rhiCtx = renderer.contextInterface()->rhiContext();
1355 QSSG_ASSERT(debugObjectShader && rhiCtx->rhi()->isRecordingFrame(), return);
1356 QRhiCommandBuffer *cb = rhiCtx->commandBuffer();
1357 QSSGRhiContextPrivate *rhiCtxD = QSSGRhiContextPrivate::get(rhiCtx.get());
1358
1359 const auto &debugDraw = renderer.contextInterface()->debugDrawSystem();
1360 if (debugDraw && debugDraw->hasContent()) {
1361 cb->debugMarkBegin(QByteArrayLiteral("Quick 3D debug objects"));
1362 Q_TRACE_SCOPE(QSSG_renderPass, QStringLiteral("Quick 3D debug objects"));
1363 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderPass);
1364 QSSGRhiGraphicsPipelineStatePrivate::setShaderPipeline(ps, debugObjectShader.get());
1365 QSSGRhiDrawCallData &dcd = rhiCtxD->drawCallData({ this, nullptr, nullptr, 0 });
1366 QRhiShaderResourceBindings *srb = dcd.srb;
1367 QRhiRenderPassDescriptor *rpDesc = rhiCtx->mainRenderPassDescriptor();
1368 debugDraw->recordRenderDebugObjects(rhiCtx.get(), &ps, srb, rpDesc);
1369 cb->debugMarkEnd();
1370 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DRenderPass, 0, QByteArrayLiteral("debug_objects"));
1371 }
1372}
1373
1375{
1376 ps = {};
1377}
1378
1380{
1381 Q_UNUSED(renderer);
1382 auto &frameData = data.getFrameData();
1383 for (const auto &p : std::as_const(extensions)) {
1384 p->prepareRender(frameData);
1385 if (p->mode() == QSSGRenderExtension::RenderMode::Standalone)
1386 p->render(frameData);
1387 }
1388}
1389
1391{
1392 auto *data = QSSGLayerRenderData::getCurrent(renderer);
1393 QSSG_ASSERT(data, return);
1394 auto &frameData = data->getFrameData();
1395 for (const auto &p : std::as_const(extensions)) {
1396 if (p->mode() == QSSGRenderExtension::RenderMode::Main)
1397 p->render(frameData);
1398 }
1399}
1400
1402{
1403 for (const auto &p : std::as_const(extensions))
1404 p->resetForFrame();
1405
1406 // TODO: We should track if we need to update this list.
1407 extensions.clear();
1408}
1409
1410static quint32 nextMultipleOf(quint32 value, quint32 multiple)
1411{
1412 return multiple * ((value / multiple) + 1);
1413}
1414
1415static quint32 ensureFreeNodes(quint32 value, quint32 multiple)
1416{
1417 quint32 multipleOf = nextMultipleOf(value, multiple);
1418 if (multipleOf - value < multiple)
1419 multipleOf += multiple;
1420 return multipleOf;
1421}
1422
1424{
1425 auto *ctx = renderer.contextInterface();
1426 const auto &rhiCtx = ctx->rhiContext();
1427 auto *rhi = rhiCtx->rhi();
1428
1429 QSSG_ASSERT(!data.renderedCameras.isEmpty() && data.renderedCameraData.has_value() , return);
1430 QSSGRenderCamera *camera = data.renderedCameras[0];
1431
1432 ps = data.getPipelineState();
1433 ps.samples = rhiCtx->mainPassSampleCount();
1434 ps.viewCount = rhiCtx->mainPassViewCount();
1435
1436 ps.flags.setFlag(QSSGRhiGraphicsPipelineState::Flag::BlendEnabled, true);
1437 ps.flags.setFlag(QSSGRhiGraphicsPipelineState::Flag::DepthWriteEnabled, false);
1438
1439 shaderFeatures = data.getShaderFeatures();
1440 sortedTransparentObjects = data.getSortedTransparentRenderableObjects(*camera);
1441
1442 if (method == QSSGRenderLayer::OITMethod::WeightedBlended) {
1443 ps.colorAttachmentCount = 2;
1444
1445 rhiAccumTexture = data.getRenderResult(QSSGRenderResult::Key::AccumTexture);
1446 rhiRevealageTexture = data.getRenderResult(QSSGRenderResult::Key::RevealageTexture);
1447 if (ps.samples > 1)
1448 rhiDepthTexture = data.getRenderResult(QSSGRenderResult::Key::DepthTextureMS);
1449 else
1450 rhiDepthTexture = data.getRenderResult(QSSGRenderResult::Key::DepthTexture);
1451 if (!rhiDepthTexture->isValid())
1452 return;
1453 auto &oitrt = data.getOitRenderContext();
1454 if (!oitrt.oitRenderTarget || oitrt.oitRenderTarget->pixelSize() != data.layerPrepResult.textureDimensions()
1455 || rhiDepthTexture->texture != oitrt.oitRenderTarget->description().depthTexture()
1456 || ps.samples != oitrt.oitRenderTarget->sampleCount()) {
1457 if (oitrt.oitRenderTarget) {
1458 rhiAccumTexture->texture->destroy();
1459 rhiRevealageTexture->texture->destroy();
1460 oitrt.oitRenderTarget->destroy();
1461 oitrt.renderPassDescriptor->destroy();
1462 oitrt.oitRenderTarget = nullptr;
1463 }
1464 const QRhiTexture::Flags textureFlags = QRhiTexture::RenderTarget;
1465 if (ps.viewCount >= 2) {
1466 rhiAccumTexture->texture = rhi->newTextureArray(QRhiTexture::RGBA16F, ps.viewCount, data.layerPrepResult.textureDimensions(), ps.samples, textureFlags);
1467 rhiRevealageTexture->texture = rhi->newTextureArray(QRhiTexture::R16F, ps.viewCount, data.layerPrepResult.textureDimensions(), ps.samples, textureFlags);
1468 } else {
1469 rhiAccumTexture->texture = rhi->newTexture(QRhiTexture::RGBA16F, data.layerPrepResult.textureDimensions(), ps.samples, textureFlags);
1470 rhiRevealageTexture->texture = rhi->newTexture(QRhiTexture::R16F, data.layerPrepResult.textureDimensions(), ps.samples, textureFlags);
1471 }
1472 rhiAccumTexture->texture->create();
1473 rhiRevealageTexture->texture->create();
1474
1475 QRhiTextureRenderTargetDescription desc;
1476 desc.setColorAttachments({{rhiAccumTexture->texture}, {rhiRevealageTexture->texture}});
1477 desc.setDepthTexture(rhiDepthTexture->texture);
1478
1479 if (oitrt.oitRenderTarget == nullptr) {
1480 oitrt.oitRenderTarget = rhi->newTextureRenderTarget(desc, QRhiTextureRenderTarget::PreserveDepthStencilContents);
1481 oitrt.renderPassDescriptor = oitrt.oitRenderTarget->newCompatibleRenderPassDescriptor();
1482 oitrt.oitRenderTarget->setRenderPassDescriptor(oitrt.renderPassDescriptor);
1483 oitrt.oitRenderTarget->create();
1484
1485 renderTarget = oitrt.oitRenderTarget;
1486 }
1487 }
1488 QSSGRhiContextPrivate *rhiCtxD = QSSGRhiContextPrivate::get(rhiCtx.get());
1489 const auto &shaderCache = renderer.contextInterface()->shaderCache();
1490 clearPipeline = shaderCache->getBuiltInRhiShaders().getRhiClearMRTShader();
1491
1492 QSSGRhiShaderResourceBindingList bindings;
1493 QVector4D clearData[2];
1494 clearData[0] = QVector4D(0.0, 0.0, 0.0, 0.0);
1495 clearData[1] = QVector4D(1.0, 1.0, 1.0, 1.0);
1496
1497 QSSGRhiDrawCallData &dcd(rhiCtxD->drawCallData({ this, clearPipeline.get(), nullptr, 0 }));
1498 QRhiBuffer *&ubuf = dcd.ubuf;
1499 const int ubufSize = sizeof(clearData);
1500 if (!ubuf) {
1501 ubuf = rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, ubufSize);
1502 ubuf->create();
1503 }
1504
1505 QRhiResourceUpdateBatch *rub = rhi->nextResourceUpdateBatch();
1506 rub->updateDynamicBuffer(ubuf, 0, ubufSize, &clearData);
1507 renderer.rhiQuadRenderer()->prepareQuad(rhiCtx.get(), rub);
1508
1509 bindings.addUniformBuffer(0, QRhiShaderResourceBinding::FragmentStage, ubuf);
1510
1511 clearSrb = rhiCtxD->srb(bindings);
1512
1513 ps.targetBlend[0].srcAlpha = QRhiGraphicsPipeline::One;
1514 ps.targetBlend[0].srcColor = QRhiGraphicsPipeline::One;
1515 ps.targetBlend[0].dstAlpha = QRhiGraphicsPipeline::One;
1516 ps.targetBlend[0].dstColor = QRhiGraphicsPipeline::One;
1517 ps.targetBlend[1].srcAlpha = QRhiGraphicsPipeline::Zero;
1518 ps.targetBlend[1].srcColor = QRhiGraphicsPipeline::Zero;
1519 ps.targetBlend[1].dstAlpha = QRhiGraphicsPipeline::OneMinusSrcAlpha;
1520 ps.targetBlend[1].dstColor = QRhiGraphicsPipeline::OneMinusSrcAlpha;
1521
1522 TransparentPass::prep(*ctx, data, this, ps, shaderFeatures, oitrt.renderPassDescriptor, sortedTransparentObjects, true);
1523 } else if (method == QSSGRenderLayer::OITMethod::LinkedList) {
1524 if (this->rub) {
1525 rhiCtx->commandBuffer()->resourceUpdate(this->rub);
1526 this->rub = nullptr;
1527 }
1528 // same as transparent pass
1529 // transparent objects (or, without LayerEnableDepthTest, all objects)
1530 ps.flags.setFlag(QSSGRhiGraphicsPipelineState::Flag::BlendEnabled, true);
1531 ps.flags.setFlag(QSSGRhiGraphicsPipelineState::Flag::DepthWriteEnabled, false);
1532 ps.targetBlend[0].srcAlpha = QRhiGraphicsPipeline::One;
1533 ps.targetBlend[0].srcColor = QRhiGraphicsPipeline::SrcAlpha;
1534 ps.targetBlend[0].dstAlpha = QRhiGraphicsPipeline::OneMinusSrcAlpha;
1535 ps.targetBlend[0].dstColor = QRhiGraphicsPipeline::OneMinusSrcAlpha;
1536
1537 shaderFeatures = data.getShaderFeatures();
1538 sortedTransparentObjects = data.getSortedTransparentRenderableObjects(*camera);
1539
1540#ifdef QSSG_OIT_USE_BUFFERS
1541 auto &oitCtx = data.getOitRenderContext();
1542 rhiABuffer = oitCtx.aBuffer;
1543 rhiAuxBuffer = oitCtx.auxBuffer;
1544 rhiCounterBuffer = oitCtx.counterBuffer;
1545#else
1546 rhiABufferImage = data.getRenderResult(QSSGRenderResult::Key::ABufferImage);
1547 rhiAuxiliaryImage = data.getRenderResult(QSSGRenderResult::Key::AuxiliaryImage);
1548 rhiCounterImage = data.getRenderResult(QSSGRenderResult::Key::CounterImage);
1549#endif
1550 QSize dim = data.layerPrepResult.textureDimensions();
1551 dim.setWidth(dim.width() * ps.samples);
1552 dim.setHeight(dim.height() * ps.viewCount);
1553#ifdef QSSG_OIT_USE_BUFFERS
1554 if (!rhiAuxBuffer || rhiAuxBuffer->size() != (dim.width() * dim.height() * 4u) || currentNodeCount == 0 || currentNodeCount != reportedNodeCount)
1555#else
1556 if (!rhiAuxiliaryImage->texture || rhiAuxiliaryImage->texture->pixelSize() != dim || currentNodeCount == 0 || currentNodeCount != reportedNodeCount)
1557#endif
1558 {
1559 quint32 extraNodeCount = 0;
1560#ifdef QSSG_OIT_USE_BUFFERS
1561 if (rhiAuxBuffer && rhiAuxBuffer->size() != (dim.width() * dim.height() * 4u)) {
1562 if (rhiAuxBuffer->size() < (dim.width() * dim.height() * 4u))
1563 extraNodeCount = ensureFreeNodes((dim.width() * dim.height() * 4u) - rhiAuxBuffer->size(), 32u * 1024u);
1564 rhiAuxBuffer->destroy();
1565 rhiAuxBuffer = nullptr;
1566 }
1567#else
1568 if (rhiABufferImage->texture) {
1569 const auto s = rhiAuxiliaryImage->texture->pixelSize();
1570 if (s.width() * s.height() < dim.width() * dim.height())
1571 extraNodeCount = ensureFreeNodes(s.width() * s.height() - dim.width() * dim.height(), 32u * 1024u);
1572 rhiABufferImage->texture->destroy();
1573 rhiAuxiliaryImage->texture->destroy();
1574 }
1575#endif
1576
1577 if (reportedNodeCount) {
1578 currentNodeCount = reportedNodeCount + extraNodeCount;
1579 } else {
1580 quint32 size = RenderHelpers::rhiCalculateABufferSize(data.layerPrepResult.textureDimensions(), 4 * ps.samples * ps.viewCount);
1581 currentNodeCount = ensureFreeNodes(size * size, 32u * 1024u);
1582 }
1583 data.layer.oitNodeCount = currentNodeCount;
1584
1585#ifdef QSSG_OIT_USE_BUFFERS
1586 if (rhiABuffer && currentNodeCount * 16 != rhiABuffer->size()) {
1587 rhiABuffer->destroy();
1588 rhiABuffer = nullptr;
1589 }
1590 if (!rhiABuffer) {
1591 rhiABuffer = rhi->newBuffer(QRhiBuffer::Static, QRhiBuffer::StorageBuffer, currentNodeCount * 16);
1592 rhiABuffer->create();
1593 }
1594 if (!rhiAuxBuffer) {
1595 rhiAuxBuffer = rhi->newBuffer(QRhiBuffer::Static, QRhiBuffer::StorageBuffer, dim.width() * dim.height() * 4);
1596 rhiAuxBuffer->create();
1597 }
1598 if (!rhiCounterBuffer) {
1599 rhiCounterBuffer = rhi->newBuffer(QRhiBuffer::Static, QRhiBuffer::StorageBuffer, 4);
1600 rhiCounterBuffer->create();
1601 oitCtx.counterBuffer = rhiCounterBuffer;
1602 }
1603 oitCtx.aBuffer = rhiABuffer;
1604 oitCtx.auxBuffer = rhiAuxBuffer;
1605#else
1606 quint32 sizeWithLayers = RenderHelpers::rhiCalculateABufferSize(currentNodeCount);
1607 const QRhiTexture::Flags textureFlags = QRhiTexture::UsedWithLoadStore;
1608 rhiABufferImage->texture = rhi->newTexture(QRhiTexture::RGBA32UI, QSize(sizeWithLayers, sizeWithLayers), 1, textureFlags);
1609 rhiABufferImage->texture->create();
1610 rhiAuxiliaryImage->texture = rhi->newTexture(QRhiTexture::R32UI, dim, 1, textureFlags);
1611 rhiAuxiliaryImage->texture->create();
1612 if (!rhiCounterImage->texture) {
1613 rhiCounterImage->texture = rhi->newTexture(QRhiTexture::R32UI, QSize(1, 1), 1, textureFlags | QRhiTexture::UsedAsTransferSource);
1614 rhiCounterImage->texture->create();
1615
1616 auto &oitrt = data.getOitRenderContext();
1617 readbackImage = rhi->newTexture(QRhiTexture::R32UI, QSize(1, 1), 1, QRhiTexture::UsedAsTransferSource);
1618 readbackImage->create();
1619 oitrt.copyTexture = readbackImage;
1620 }
1621#endif
1622 }
1623 QRhiResourceUpdateBatch *rub = rhiCtx->rhi()->nextResourceUpdateBatch();
1624
1625 QSSGRhiContextPrivate *rhiCtxD = QSSGRhiContextPrivate::get(rhiCtx.get());
1626 const auto &shaderCache = renderer.contextInterface()->shaderCache();
1627#ifdef QSSG_OIT_USE_BUFFERS
1628 clearPipeline = shaderCache->getBuiltInRhiShaders().getRhiClearBufferShader();
1629#else
1630 clearPipeline = shaderCache->getBuiltInRhiShaders().getRhiClearImageShader();
1631#endif
1632 QSSGRhiShaderResourceBindingList bindings;
1633 quint32 clearImageData[8] = {0};
1634
1635 QSSGRhiDrawCallData &dcd(rhiCtxD->drawCallData({ this, clearPipeline.get(), nullptr, 0 }));
1636 QRhiBuffer *&ubuf = dcd.ubuf;
1637 const int ubufSize = sizeof(clearImageData);
1638 if (!ubuf) {
1639 ubuf = rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, ubufSize);
1640 ubuf->create();
1641 }
1642
1643 clearImageData[4] = data.layerPrepResult.textureDimensions().width();
1644 clearImageData[5] = data.layerPrepResult.textureDimensions().height();
1645 clearImageData[6] = ps.samples;
1646 clearImageData[7] = ps.viewCount;
1647
1648 rub->updateDynamicBuffer(ubuf, 0, ubufSize, clearImageData);
1649 quint32 zero = 0;
1650#ifdef QSSG_OIT_USE_BUFFERS
1651 rub->uploadStaticBuffer(rhiCounterBuffer, 0, 4, &zero);
1652#else
1653 rub->uploadTexture(rhiCounterImage->texture, {{0, 0, {&zero, 4}}});
1654#endif
1655
1656 bindings.addUniformBuffer(0, QRhiShaderResourceBinding::FragmentStage, ubuf);
1657#ifdef QSSG_OIT_USE_BUFFERS
1658 bindings.addStorageBuffer(1, QRhiShaderResourceBinding::FragmentStage, rhiAuxBuffer);
1659#else
1660 bindings.addImageStore(1, QRhiShaderResourceBinding::FragmentStage, rhiAuxiliaryImage->texture, 0);
1661#endif
1662
1663 clearSrb = rhiCtxD->srb(bindings);
1664
1665 renderer.rhiQuadRenderer()->prepareQuad(rhiCtx.get(), rub);
1666
1667 TransparentPass::prep(*ctx, data, this, ps, shaderFeatures, rhiCtx->mainRenderPassDescriptor(), sortedTransparentObjects, true);
1668 }
1669}
1670
1672{
1673 auto *ctx = renderer.contextInterface();
1674 const auto &rhiCtx = ctx->rhiContext();
1675 QSSG_ASSERT(rhiCtx->rhi()->isRecordingFrame(), return);
1676 QRhiCommandBuffer *cb = rhiCtx->commandBuffer();
1677
1678 if (method == QSSGRenderLayer::OITMethod::WeightedBlended) {
1679 if (Q_LIKELY(renderTarget)) {
1680 cb->beginPass(renderTarget, Qt::black, {});
1681
1682 QRhiShaderResourceBindings *srb = clearSrb;
1683 QSSG_ASSERT(srb, return);
1684 ps.flags.setFlag(QSSGRhiGraphicsPipelineState::Flag::BlendEnabled, false);
1685 QSSGRhiGraphicsPipelineStatePrivate::setShaderPipeline(ps, clearPipeline.get());
1686 renderer.rhiQuadRenderer()->recordRenderQuad(rhiCtx.get(), &ps, srb, renderTarget->renderPassDescriptor(), {});
1687 ps.flags.setFlag(QSSGRhiGraphicsPipelineState::Flag::BlendEnabled, true);
1688
1689 cb->debugMarkBegin(QByteArrayLiteral("Quick3D render order-independent alpha"));
1690 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderPass);
1691 Q_TRACE(QSSG_renderPass_entry, QStringLiteral("Quick3D render order-independent alpha"));
1692 ps.flags.setFlag(QSSGRhiGraphicsPipelineState::Flag::DepthTestEnabled, true);
1693 ps.flags.setFlag(QSSGRhiGraphicsPipelineState::Flag::DepthWriteEnabled, false);
1694 TransparentPass::render(*ctx, ps, sortedTransparentObjects);
1695 cb->debugMarkEnd();
1696 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DRenderPass, 0, QByteArrayLiteral("transparent_order_independent_pass"));
1697 Q_TRACE(QSSG_renderPass_exit);
1698
1699 cb->endPass();
1700 }
1701 } else if (method == QSSGRenderLayer::OITMethod::LinkedList) {
1702 cb->debugMarkBegin(QByteArrayLiteral("Quick3D render alpha"));
1703 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderPass);
1704 Q_TRACE(QSSG_renderPass_entry, QStringLiteral("Quick3D render alpha"));
1705
1706 QRhiShaderResourceBindings *srb = clearSrb;
1707 QSSG_ASSERT(srb, return);
1708 ps.flags.setFlag(QSSGRhiGraphicsPipelineState::Flag::BlendEnabled, true);
1709 QSSGRhiGraphicsPipelineStatePrivate::setShaderPipeline(ps, clearPipeline.get());
1710 renderer.rhiQuadRenderer()->recordRenderQuad(rhiCtx.get(), &ps, srb, rhiCtx->mainRenderPassDescriptor(), {});
1711
1712 TransparentPass::render(*ctx, ps, sortedTransparentObjects);
1713#ifdef QSSG_OIT_USE_BUFFERS
1714 QRhiResourceUpdateBatch *rub = rhiCtx->rhi()->nextResourceUpdateBatch();
1715 QRhiReadbackResult *result = nullptr;
1716 if (results.size() > 1) {
1717 result = results.takeLast();
1718 result->pixelSize = {};
1719 result->data = {};
1720 result->format = QRhiTexture::UnknownFormat;
1721 } else {
1722 result = new QRhiReadbackResult();
1723 }
1724 const auto completedFunc = [this, result](){
1725 if (result) {
1726 const quint32 *d = reinterpret_cast<const quint32 *>(result->data.constData());
1727 quint32 nodeCount = *d;
1728 if (nodeCount)
1729 this->reportedNodeCount = ensureFreeNodes(nodeCount, 32u * 1024u);
1730 this->results.append(result);
1731 }
1732 };
1733 result->completed = completedFunc;
1734 rub->readBackBuffer(rhiCounterBuffer, 0, 4, result);
1735 this->rub = rub;
1736#else
1737 QRhiResourceUpdateBatch *rub = rhiCtx->rhi()->nextResourceUpdateBatch();
1738 rub->copyTexture(readbackImage, rhiCounterImage->texture);
1739 QRhiReadbackDescription rbdesc;
1740 rbdesc.setTexture(readbackImage);
1741 QRhiReadbackResult *result = nullptr;
1742 if (results.size() > 1) {
1743 result = results.takeLast();
1744 result->pixelSize = {};
1745 result->data = {};
1746 result->format = QRhiTexture::UnknownFormat;
1747 } else {
1748 result = new QRhiReadbackResult();
1749 }
1750 const auto completedFunc = [this, result](){
1751 if (result) {
1752 const quint32 *d = reinterpret_cast<const quint32 *>(result->data.constData());
1753 quint32 nodeCount = *d;
1754 if (nodeCount)
1755 this->reportedNodeCount = ensureFreeNodes(nodeCount, 32u * 1024u);
1756 this->results.append(result);
1757 }
1758 };
1759 result->completed = completedFunc;
1760 rub->readBackTexture(rbdesc, result);
1761 this->rub = rub;
1762#endif
1763 cb->debugMarkEnd();
1764 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DRenderPass, 0, QByteArrayLiteral("transparent_order_independent_pass"));
1765 Q_TRACE(QSSG_renderPass_exit);
1766 }
1767}
1768
1770{
1771 if (method == QSSGRenderLayer::OITMethod::WeightedBlended)
1772 return Type::Standalone;
1773 return Type::Main;
1774}
1775
1776
1778{
1779 sortedTransparentObjects.clear();
1780 ps = {};
1781 shaderFeatures = {};
1782 rhiAccumTexture = nullptr;
1783 rhiRevealageTexture = nullptr;
1784 rhiDepthTexture = nullptr;
1785 rhiCounterImage = nullptr;
1786}
1787
1789{
1790 using namespace RenderHelpers;
1791
1792 QSSG_ASSERT(!data.renderedCameras.isEmpty(), return);
1793
1794 const auto &rhiCtx = renderer.contextInterface()->rhiContext();
1795 QSSG_ASSERT(rhiCtx->rhi()->isRecordingFrame(), return);
1796 const auto &shaderCache = renderer.contextInterface()->shaderCache();
1797
1798 ps = data.getPipelineState();
1799 ps.samples = rhiCtx->mainPassSampleCount();
1800 ps.viewCount = rhiCtx->mainPassViewCount();
1801
1802 if (method == QSSGRenderLayer::OITMethod::WeightedBlended) {
1803 rhiAccumTexture = data.getRenderResult(QSSGRenderResult::Key::AccumTexture);
1804 rhiRevealageTexture = data.getRenderResult(QSSGRenderResult::Key::RevealageTexture);
1805 compositeShaderPipeline = shaderCache->getBuiltInRhiShaders().getRhiOitCompositeShader(method, ps.samples > 1 ? true : false);
1806 } else if (method == QSSGRenderLayer::OITMethod::LinkedList) {
1807#ifdef QSSG_OIT_USE_BUFFERS
1808 rhiABuffer = data.getOitRenderContext().aBuffer;
1809 rhiAuxBuffer = data.getOitRenderContext().auxBuffer;
1810 compositeShaderPipeline = shaderCache->getBuiltInRhiShaders().getRhiOitCompositeShader(method, ps.samples > 1 ? true : false, true);
1811#else
1812 rhiABufferImage = data.getRenderResult(QSSGRenderResult::Key::ABufferImage);
1813 rhiAuxiliaryImage = data.getRenderResult(QSSGRenderResult::Key::AuxiliaryImage);
1814 compositeShaderPipeline = shaderCache->getBuiltInRhiShaders().getRhiOitCompositeShader(method, ps.samples > 1 ? true : false);
1815#endif
1816
1817 QSSGRhiContextPrivate *rhiCtxD = QSSGRhiContextPrivate::get(rhiCtx.get());
1818 QSSGRhiDrawCallData &dcd(rhiCtxD->drawCallData({ this, nullptr, nullptr, 0 }));
1819 QRhiBuffer *&ubuf = dcd.ubuf;
1820 const int ubufSize = 6 * sizeof(quint32);
1821 if (!ubuf) {
1822 ubuf = rhiCtx->rhi()->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, ubufSize);
1823 ubuf->create();
1824 }
1825#ifdef QSSG_OIT_USE_BUFFERS
1826 const quint32 values[6] = { 0,
1827 quint32(data.layer.oitNodeCount),
1828 quint32(ps.samples),
1829 0,
1830 quint32(data.layerPrepResult.textureDimensions().width()),
1831 quint32(data.layerPrepResult.textureDimensions().height())
1832 };
1833#else
1834 const quint32 sizeWithLayers = rhiABufferImage->texture->pixelSize().width();
1835 const quint32 values[6] = { sizeWithLayers,
1836 sizeWithLayers * sizeWithLayers,
1837 quint32(ps.samples),
1838 0,
1839 quint32(data.layerPrepResult.textureDimensions().width()),
1840 quint32(data.layerPrepResult.textureDimensions().height())
1841 };
1842#endif
1843 QRhiResourceUpdateBatch *rub = rhiCtx->rhi()->nextResourceUpdateBatch();
1844 rub->updateDynamicBuffer(ubuf, 0, ubufSize, values);
1845 renderer.rhiQuadRenderer()->prepareQuad(rhiCtx.get(), rub);
1846 }
1847}
1848
1850{
1851 using namespace RenderHelpers;
1852
1853 const auto &rhiCtx = renderer.contextInterface()->rhiContext();
1854 QSSG_ASSERT(rhiCtx->rhi()->isRecordingFrame(), return);
1855 QRhiCommandBuffer *cb = rhiCtx->commandBuffer();
1856
1857 QSSGRhiContextPrivate *rhiCtxD = QSSGRhiContextPrivate::get(rhiCtx.get());
1858
1859 if (!rhiAccumTexture->texture || !rhiRevealageTexture->texture)
1860 return;
1861
1862 if (method == QSSGRenderLayer::OITMethod::WeightedBlended) {
1863 QSSGRhiShaderResourceBindingList bindings;
1864
1865 QRhiSampler *sampler = rhiCtx->sampler({ QRhiSampler::Nearest,
1866 QRhiSampler::Nearest,
1867 QRhiSampler::None,
1868 QRhiSampler::ClampToEdge,
1869 QRhiSampler::ClampToEdge,
1870 QRhiSampler::ClampToEdge });
1871 bindings.addTexture(1, QRhiShaderResourceBinding::FragmentStage, rhiAccumTexture->texture, sampler);
1872 bindings.addTexture(2, QRhiShaderResourceBinding::FragmentStage, rhiRevealageTexture->texture, sampler);
1873
1874 compositeSrb = rhiCtxD->srb(bindings);
1875
1876 QRhiShaderResourceBindings *srb = compositeSrb;
1877 QSSG_ASSERT(srb, return);
1878
1879 cb->debugMarkBegin(QByteArrayLiteral("Quick3D revealage"));
1880 QSSGRhiGraphicsPipelineStatePrivate::setShaderPipeline(ps, compositeShaderPipeline.get());
1881 ps.flags.setFlag(QSSGRhiGraphicsPipelineState::Flag::BlendEnabled, true);
1882 renderer.rhiQuadRenderer()->recordRenderQuad(rhiCtx.get(), &ps, srb, rhiCtx->mainRenderPassDescriptor(),
1883 { QSSGRhiQuadRenderer::UvCoords | QSSGRhiQuadRenderer::DepthTest | QSSGRhiQuadRenderer::PremulBlend});
1884 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DRenderPass, 0, QByteArrayLiteral("revealage"));
1885 cb->debugMarkEnd();
1886 } else if (method == QSSGRenderLayer::OITMethod::LinkedList) {
1887 QSSGRhiShaderResourceBindingList bindings;
1888
1889 QSSGRhiContextPrivate *rhiCtxD = QSSGRhiContextPrivate::get(rhiCtx.get());
1890 QSSGRhiDrawCallData &dcd(rhiCtxD->drawCallData({ this, nullptr, nullptr, 0 }));
1891 QRhiBuffer *&ubuf = dcd.ubuf;
1892
1893 bindings.addUniformBuffer(0, QRhiShaderResourceBinding::FragmentStage, ubuf);
1894#ifdef QSSG_OIT_USE_BUFFERS
1895 bindings.addStorageBuffer(1, QRhiShaderResourceBinding::FragmentStage, rhiABuffer);
1896 bindings.addStorageBuffer(2, QRhiShaderResourceBinding::FragmentStage, rhiAuxBuffer);
1897#else
1898 bindings.addImageLoad(1, QRhiShaderResourceBinding::FragmentStage, rhiABufferImage->texture, 0);
1899 bindings.addImageLoad(2, QRhiShaderResourceBinding::FragmentStage, rhiAuxiliaryImage->texture, 0);
1900#endif
1901
1902 compositeSrb = rhiCtxD->srb(bindings);
1903
1904 QRhiShaderResourceBindings *srb = compositeSrb;
1905 QSSG_ASSERT(srb, return);
1906
1907 cb->debugMarkBegin(QByteArrayLiteral("Quick3D oit-composite"));
1908 QSSGRhiGraphicsPipelineStatePrivate::setShaderPipeline(ps, compositeShaderPipeline.get());
1909 ps.flags.setFlag(QSSGRhiGraphicsPipelineState::Flag::BlendEnabled, true);
1910 renderer.rhiQuadRenderer()->recordRenderQuad(rhiCtx.get(), &ps, srb, rhiCtx->mainRenderPassDescriptor(),
1911 { QSSGRhiQuadRenderer::UvCoords | QSSGRhiQuadRenderer::DepthTest | QSSGRhiQuadRenderer::PremulBlend});
1912 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DRenderPass, 0, QByteArrayLiteral("oit-composite"));
1913 }
1914}
1915
1917{
1918 ps = {};
1919 shaderFeatures = {};
1920 rhiAccumTexture = nullptr;
1921 rhiRevealageTexture = nullptr;
1922}
1923
1924Q_STATIC_LOGGING_CATEGORY(lcUserRenderPass, "qt.quick3d.rhi.userrenderpass")
1925
1927{
1928 QSSG_ASSERT(!data.renderedCameras.isEmpty(), return);
1929 const auto &rhiCtx = renderer.contextInterface()->rhiContext();
1930 QSSG_ASSERT(rhiCtx->rhi()->isRecordingFrame(), return);
1931
1932 for (auto *passNode : std::as_const(userPasses)) {
1933 QSSG_ASSERT(passNode != nullptr, continue);
1934 prepareTopLevelPass(renderer, data, passNode);
1935 }
1936}
1937
1939{
1940
1941 const auto &rhiCtx = renderer.contextInterface()->rhiContext();
1942 QSSG_ASSERT(rhiCtx->rhi()->isRecordingFrame(), return);
1943 QRhiCommandBuffer *cb = rhiCtx->commandBuffer();
1944
1945 for (UserPassData &passData : userPassData) {
1946
1947 const auto &ps = passData.ps;
1948 const auto &renderables = passData.renderables;
1949 const auto &clearColor = passData.clearColor;
1950 const auto &depthStencilClearValue = passData.depthStencilClearValue;
1951 const auto &renderableTexture = passData.renderableTexture;
1952
1953 cb->debugMarkBegin(QByteArrayLiteral("Quick3D UserRenderPass"));
1954
1955 if (Q_LIKELY(renderableTexture && renderableTexture->isValid())) {
1956 cb->beginPass(renderableTexture->getRenderTarget().get(), clearColor, depthStencilClearValue, nullptr, rhiCtx->commonPassFlags());
1957 if (passData.skyboxCubeMapPass) {
1958 passData.skyboxCubeMapPass->renderPass(renderer);
1959 } else if (passData.skyboxPass) {
1960 passData.skyboxPass->renderPass(renderer);
1961 } else if (passData.item2DPass) {
1962 passData.item2DPass->renderPass(renderer);
1963 } else {
1964 // Regular User Passes
1965 bool needsSetViewport = true;
1966 if (passData.index >= 0) {
1967 for (const auto &handle : std::as_const(renderables))
1968 RenderHelpers::rhiRenderRenderable(rhiCtx.get(), ps, *handle.obj, &needsSetViewport, QSSGRenderTextureCubeFaceNone, qsizetype(passData.index));
1969 }
1970 }
1971 QRhiResourceUpdateBatch *rub = nullptr;
1972
1973 // Sub Passes
1974 for (auto &subPassData : passData.subPassData) {
1975 const auto &subPs = subPassData.ps;
1976 const auto &subRenderables = subPassData.renderables;
1977
1978 if (subPassData.skyboxCubeMapPass) {
1979 subPassData.skyboxCubeMapPass->renderPass(renderer);
1980 } else if (subPassData.skyboxPass) {
1981 subPassData.skyboxPass->renderPass(renderer);
1982 } else if (subPassData.item2DPass) {
1983 subPassData.item2DPass->renderPass(renderer);
1984 } else {
1985 bool needsSetViewport = true;
1986 if (subPassData.index >= 0) {
1987 for (const auto &handle : std::as_const(subRenderables))
1988 RenderHelpers::rhiRenderRenderable(rhiCtx.get(), subPs, *handle.obj, &needsSetViewport, QSSGRenderTextureCubeFaceNone, qsizetype(subPassData.index));
1989 }
1990 }
1991 }
1992
1993 cb->endPass(rub);
1994 }
1995
1996 cb->debugMarkEnd();
1997 }
1998}
1999
2001{
2002 qCDebug(lcUserRenderPass) << "resetForFrame in UserRenderPass";
2003 userPasses.clear();
2004
2005 userPassData.clear();
2006}
2007
2009 QSSGLayerRenderData &data,
2010 QSSGRenderUserPass *passNode,
2011 std::vector<UserPassData> &outData,
2012 QSSGRhiRenderableTextureV2Ptr renderableTexture)
2013{
2014 // NOTE: Only top-level passes should create their own renderable texture by passing in a null renderable texture.
2015 // Ideally we this should be more explict...
2016 const bool isTopLevelPass = (renderableTexture == nullptr);
2017
2018 QSSGRenderCamera *camera = data.renderedCameras[0];
2019 const auto &rhiCtx = renderer.contextInterface()->rhiContext();
2020
2021 const QSize targetSize = data.layerPrepResult.textureDimensions();
2022
2023 static const auto needsRebuild = [](QRhiTexture *texture, const QSize &size, QRhiTexture::Format format) {
2024 return !texture || texture->pixelSize() != size || texture->format() != format;
2025 };
2026
2027 qCDebug(lcUserRenderPass, "renderPrep in UserRenderPass");
2028
2029 const QSSGResourceId currentPassId = QSSGRenderGraphObjectUtils::getResourceId(*passNode);
2030 if (visitedPasses.find(currentPassId) != visitedPasses.end()) {
2031 qWarning("UserRenderPass: Circular dependency detected in SubRenderPass chain. Ignoring pass.");
2032 return;
2033 }
2034
2035 // Check max depth
2036 if (visitedPasses.size() >= MAX_SUBPASS_DEPTH) {
2037 qWarning("UserRenderPass: Maximum SubRenderPass nesting depth (%zu) exceeded. Ignoring pass.", MAX_SUBPASS_DEPTH);
2038 return;
2039 }
2040
2041 visitedPasses.insert(currentPassId);
2042
2043 UserPassData currentPassData;
2044 currentPassData.clearColor = passNode->clearColor;
2045 currentPassData.depthStencilClearValue = passNode->depthStencilClearValue;
2046 if (isTopLevelPass)
2047 renderableTexture = currentPassData.renderableTexture = data.requestUserRenderPassManager()->getOrCreateRenderableTexture(*passNode);
2048 else
2049 currentPassData.renderableTexture = renderableTexture;
2050
2051 QSSGRenderableObjectList &renderables = currentPassData.renderables;
2052 QSSGRhiGraphicsPipelineState &ps = currentPassData.ps;
2053 const auto &renderTarget = currentPassData.renderableTexture;
2054
2055 // Initial pipeline state from layer data
2056 ps = data.getPipelineState();
2057
2058 bool renderablesFiltered = false;
2059
2060 bool needsDepthStencilRenderBuffer = false;
2061 QSSGAllocateTexturePtr depthTextureAllocCommand;
2062
2063 QVarLengthArray<QSSGColorAttachment *, 16> colorAttachments;
2064 QVarLengthArray<QSSGResourceId, 4> subPassIds;
2065
2066 // Process commands in passNode
2067 for (const QSSGCommand *theCommand : std::as_const(passNode->commands)) {
2068 QSSG_ASSERT(theCommand != nullptr, continue);
2069
2070 qCDebug(lcUserRenderPass) << "Exec. command: >" << theCommand->typeAsString() << "--" << theCommand->debugString();
2071
2072 switch (theCommand->m_type) {
2073 case CommandType::ColorAttachment:
2074 {
2075 const QSSGColorAttachment *colorAttachCmd = static_cast<const QSSGColorAttachment *>(theCommand);
2076 colorAttachments.push_back(const_cast<QSSGColorAttachment *>(colorAttachCmd));
2077 }
2078 break;
2079 case CommandType::DepthTextureAttachment:
2080 {
2081 QSSG_ASSERT(depthTextureAllocCommand == nullptr, break);
2082 const QSSGDepthTextureAttachment *depthAttachCmd = static_cast<const QSSGDepthTextureAttachment *>(theCommand);
2083 needsDepthStencilRenderBuffer = false;
2084 depthTextureAllocCommand = depthAttachCmd->m_textureCmd;
2085 }
2086 break;
2087 case CommandType::AddShaderDefine:
2088 {
2089 const auto *defineCmd = static_cast<const QSSGAddShaderDefine *>(theCommand);
2090 const auto &defineName = defineCmd->m_name;
2091 if (defineName.size() > 0) {
2092 QByteArray value = QByteArray::number(defineCmd->m_value);
2093 currentPassData.shaderDefines.push_back({ defineName, value });
2094 }
2095 }
2096 break;
2097 case CommandType::RenderablesFilter:
2098 {
2099 auto filterCommand = static_cast<const QSSGRenderablesFilterCommand *>(theCommand);
2100
2101 // Use the filter to select which renderables to include
2102 // renderableTypes can be: None (0x0), Opaque (0x1), Transparent (0x2), or both
2103 enum RenderableType : quint8 {
2104 None = 0x0,
2105 Opaque = 0x1,
2106 Transparent = 0x2,
2107 };
2108
2109 if (filterCommand->renderableTypes & RenderableType::Opaque) // Opaque
2110 renderables = data.getSortedOpaqueRenderableObjects(*camera, 0, filterCommand->layerMask);
2111 if (filterCommand->renderableTypes & RenderableType::Transparent) // Transparent
2112 renderables += data.getSortedTransparentRenderableObjects(*camera, 0, filterCommand->layerMask);
2113
2114 // NOTE: If renderableTypes is None (0x0), no renderables are added
2115 // NOTE: If no filter is run, all opaque objects are rendered.
2116 renderablesFiltered = true;
2117 }
2118 break;
2119
2120 case CommandType::PipelineStateOverride:
2121 {
2122 auto pipelineCommand = static_cast<const QSSGPipelineStateOverrideCommand *>(theCommand);
2123 if (pipelineCommand->m_depthTestEnabled)
2124 ps.flags.setFlag(QSSGRhiGraphicsPipelineState::Flag::DepthTestEnabled, *pipelineCommand->m_depthTestEnabled);
2125 if (pipelineCommand->m_depthWriteEnabled)
2126 ps.flags.setFlag(QSSGRhiGraphicsPipelineState::Flag::DepthWriteEnabled, *pipelineCommand->m_depthWriteEnabled);
2127 if (pipelineCommand->m_blendEnabled)
2128 ps.flags.setFlag(QSSGRhiGraphicsPipelineState::Flag::BlendEnabled, *pipelineCommand->m_blendEnabled);
2129 if (pipelineCommand->m_usesStencilReference)
2130 ps.flags.setFlag(QSSGRhiGraphicsPipelineState::Flag::UsesStencilRef, *pipelineCommand->m_usesStencilReference);
2131 if (pipelineCommand->m_usesScissor)
2132 ps.flags.setFlag(QSSGRhiGraphicsPipelineState::Flag::UsesScissor, *pipelineCommand->m_usesScissor);
2133 if (pipelineCommand->m_depthFunction)
2134 ps.depthFunc = *pipelineCommand->m_depthFunction;
2135 if (pipelineCommand->m_cullMode)
2136 ps.cullMode = *pipelineCommand->m_cullMode;
2137 if (pipelineCommand->m_polygonMode)
2138 ps.polygonMode = *pipelineCommand->m_polygonMode;
2139 if (pipelineCommand->m_stencilOpFrontState)
2140 ps.stencilOpFrontState = *pipelineCommand->m_stencilOpFrontState;
2141 if (pipelineCommand->m_stencilWriteMask)
2142 ps.stencilWriteMask = *pipelineCommand->m_stencilWriteMask;
2143 if (pipelineCommand->m_stencilReference)
2144 ps.stencilRef = *pipelineCommand->m_stencilReference;
2145 if (pipelineCommand->m_viewport)
2146 ps.viewport = *pipelineCommand->m_viewport;
2147 if (pipelineCommand->m_scissor)
2148 ps.scissor = *pipelineCommand->m_scissor;
2149 if (pipelineCommand->m_targetBlend0)
2150 ps.targetBlend[0] = *pipelineCommand->m_targetBlend0;
2151 if (pipelineCommand->m_targetBlend1)
2152 ps.targetBlend[1] = *pipelineCommand->m_targetBlend1;
2153 if (pipelineCommand->m_targetBlend2)
2154 ps.targetBlend[2] = *pipelineCommand->m_targetBlend2;
2155 if (pipelineCommand->m_targetBlend3)
2156 ps.targetBlend[3] = *pipelineCommand->m_targetBlend3;
2157 if (pipelineCommand->m_targetBlend4)
2158 ps.targetBlend[4] = *pipelineCommand->m_targetBlend4;
2159 if (pipelineCommand->m_targetBlend5)
2160 ps.targetBlend[5] = *pipelineCommand->m_targetBlend5;
2161 if (pipelineCommand->m_targetBlend6)
2162 ps.targetBlend[6] = *pipelineCommand->m_targetBlend6;
2163 if (pipelineCommand->m_targetBlend7)
2164 ps.targetBlend[7] = *pipelineCommand->m_targetBlend7;
2165 break;
2166 }
2167 case CommandType::DepthStencilAttachment:
2168 //auto depthStencilCommand = static_cast<const QSSGDepthStencilAttachment *>(theCommand);
2169 QSSG_ASSERT(depthTextureAllocCommand == nullptr, break);
2170 needsDepthStencilRenderBuffer = true;
2171 break;
2172 case CommandType::SubRenderPass:
2173 {
2174 auto subPassCommand = static_cast<const QSSGSubRenderPass *>(theCommand);
2175 if (subPassCommand && subPassCommand->m_userPassId != QSSGResourceId::Invalid)
2176 subPassIds.append(subPassCommand->m_userPassId);
2177 break;
2178 }
2179 default:
2180 qWarning() << "Effect command" << theCommand->typeAsString() << "not implemented";
2181 break;
2182 }
2183 }
2184
2185 if (colorAttachments.size() > 4) {
2186 colorAttachments.resize(4);
2187 qWarning() << "UserRenderPass supports up to 4 color attachments only.";
2188 }
2189
2190 // m_passNode contains state for this UserRenderPass
2191
2192 if (isTopLevelPass) {
2193 // 1) Setup the render target
2194 bool needsBuild = !renderTarget->isValid();
2195
2196 const qsizetype oldAttachmentCount = renderTarget->colorAttachmentCount();
2197 // If the number of attachments has changed, we need to rebuild.
2198 if (oldAttachmentCount != colorAttachments.size())
2199 needsBuild = true;
2200
2201 // Even if the render target is valid we need to check if the textures are still compatible.
2202 if (!needsBuild) {
2203 // Color attachments
2204 for (int i = 0; i != oldAttachmentCount; ++i) {
2205 const auto &colorAttachment = colorAttachments.at(i);
2206 QSSG_ASSERT(colorAttachment != nullptr, continue);
2207 const auto expectedFormat = QSSGBufferManager::toRhiFormat(colorAttachment->format());
2208 const auto &texture = renderTarget->getColorTexture(i);
2209 needsBuild = needsBuild || needsRebuild(&(*texture->texture()), targetSize, expectedFormat);
2210 }
2211
2212 // depthStencilBuffer
2213 if (needsDepthStencilRenderBuffer != (renderTarget->getDepthStencil() != nullptr))
2214 needsBuild = true;
2215
2216 // depthTexture
2217 if (depthTextureAllocCommand) {
2218 const auto format = QSSGBufferManager::toRhiFormat(depthTextureAllocCommand->format());
2219 const auto &depthTextureWrapper = renderTarget->getDepthTexture();
2220 needsBuild = depthTextureWrapper == nullptr;
2221 needsBuild = needsBuild || needsRebuild(&(*depthTextureWrapper->texture()), targetSize, format);
2222 } else {
2223 if (renderTarget->getDepthTexture() != nullptr)
2224 needsBuild = true;
2225 }
2226 }
2227
2228 if (needsBuild) {
2229 renderTarget->reset();
2230
2231 QRhiTextureRenderTargetDescription rtDesc;
2232
2233 // If no attachments are specified, create one.
2234 const qsizetype colorAttachmentCount = qMax<qsizetype>(colorAttachments.size(), 1);
2235
2236 bool createSucceeded = true;
2237 bool colorAllocatorsNeedsUpdate = false;
2238
2239 {
2240 // Used to set the color attachments in rtDesc below (don't let the raw ptrs leave this scope).
2241 QVarLengthArray<QRhiTexture *, 4> textures;
2242 for (qsizetype i = 0; i < colorAttachmentCount && createSucceeded ; ++i) {
2243 const auto &colorAttCmd = colorAttachments.at(i);
2244 const auto &name = colorAttCmd->m_name;
2245 const auto format = QSSGBufferManager::toRhiFormat(colorAttCmd->format());
2246 const auto &allocateTexCmd = colorAttCmd->m_textureCmd;
2247 if (allocateTexCmd->texture() && !needsRebuild(&(*allocateTexCmd->texture()->texture()), targetSize, format)) {
2248 // NOTE: The QRhiTexture is tracked by the QSSGUserRenderPassManager, even if we create a new
2249 // shared pointer wrapper here, it will ask the manager before destroying it.
2250 textures.push_back(allocateTexCmd->texture()->texture().get());
2251 } else {
2252 auto *tex = rhiCtx->rhi()->newTexture(format, targetSize, ps.samples, QRhiTexture::RenderTarget);
2253 tex->setName(name);
2254 createSucceeded = createSucceeded && tex->create();
2255 textures.push_back(tex);
2256 // We created a new texture, so we need to update the allocator.
2257 // NOTE: Since the render target type will take ownership of the texture, we need to
2258 // request the textures once the render target description is set!
2259 colorAllocatorsNeedsUpdate = true;
2260 }
2261 }
2262
2263 rtDesc.setColorAttachments(textures.cbegin(), textures.cend());
2264 }
2265
2266 if (needsDepthStencilRenderBuffer) {
2267 auto renderBuffer = rhiCtx->rhi()->newRenderBuffer(QRhiRenderBuffer::DepthStencil, targetSize, ps.samples);
2268 if (renderBuffer->create())
2269 rtDesc.setDepthStencilBuffer(renderBuffer);
2270 } else if (depthTextureAllocCommand) {
2271 const auto format = QSSGBufferManager::toRhiFormat(depthTextureAllocCommand->format());
2272 if (depthTextureAllocCommand->texture() && !needsRebuild(&(*depthTextureAllocCommand->texture()->texture()), targetSize, format)) {
2273 rtDesc.setDepthTexture(depthTextureAllocCommand->texture()->texture().get());
2274 } else {
2275 // Create new depth texture
2276 QRhiTexture *depthTex = rhiCtx->rhi()->newTexture(format, targetSize, ps.samples, QRhiTexture::RenderTarget);
2277 if (depthTex->create())
2278 rtDesc.setDepthTexture(depthTex);
2279 }
2280 }
2281
2282 if (createSucceeded) {
2283 // Set description takes ownership of rtDesc and the textures inside it (Color + Depth).
2284 renderTarget->setDescription(rhiCtx->rhi(), std::move(rtDesc), passNode->renderTargetFlags);
2285
2286 // Now we can update the allocators for the color attachments that were created here.
2287 if (colorAllocatorsNeedsUpdate) {
2288 for (qsizetype i = 0; i < colorAttachmentCount; ++i) {
2289 const auto &colorAttCmd = colorAttachments.at(i);
2290 const auto &allocateTexCmd = colorAttCmd->m_textureCmd;
2291 allocateTexCmd->setTexture(renderTarget->getColorTexture(i));
2292 }
2293 }
2294
2295 if (depthTextureAllocCommand)
2296 depthTextureAllocCommand->setTexture(renderTarget->getDepthTexture());
2297
2298 } else {
2299 renderTarget->resetRenderTarget();
2300 qWarning() << "Failed to create textures for UserRenderPass";
2301 }
2302 }
2303
2304 }
2305
2306 Q_ASSERT(renderTarget->isValid());
2307
2308 ps.colorAttachmentCount = int(renderTarget->colorAttachmentCount());
2309
2310 // Subpasses
2311 for (const auto &subPassId : std::as_const(subPassIds)) {
2312 QSSGRenderUserPass *userPassNode = QSSGRenderGraphObjectUtils::getResource<QSSGRenderUserPass>(subPassId);
2313 QSSG_ASSERT(userPassNode && userPassNode->type == QSSGRenderGraphObject::Type::RenderPass, continue);
2314 prepareSubPass(renderer, data, userPassNode, currentPassData.subPassData, currentPassData.renderableTexture);
2315 }
2316
2317 if (passNode->passMode == QSSGRenderUserPass::PassModes::UserPass) {
2318 // If no filter is specified, render all opaque objects
2319 if (!renderablesFiltered && renderables.isEmpty())
2320 renderables = data.getSortedOpaqueRenderableObjects(*camera);
2321
2322 if (passNode->materialMode == QSSGRenderUserPass::MaterialModes::AugmentMaterial) {
2323
2324 QSSGUserShaderAugmentation shaderAugmentation = passNode->shaderAugmentation;
2325
2326 QSSG_ASSERT(shaderAugmentation.outputs.size() == 0, shaderAugmentation.outputs.clear());
2327 shaderAugmentation.defines = std::move(currentPassData.shaderDefines);
2328
2329 for (int i = 0, end = colorAttachments.size(); i < end; ++i) {
2330 const auto &colorAttCmd = colorAttachments.at(i);
2331 const auto &name = colorAttCmd->m_name;
2332 if (name.size() > 0)
2333 shaderAugmentation.outputs.push_back(name);
2334 else
2335 shaderAugmentation.outputs.push_back(getDefaultOutputName(size_t(i)));
2336 }
2337
2338 QSSGShaderFeatures shaderFeatures = data.getShaderFeatures();
2339 shaderFeatures.disableTonemapping();
2340
2341 currentPassData.index = RenderHelpers::rhiPrepareAugmentedUserPass(&(*rhiCtx), this, ps, renderTarget->getRenderPassDescriptor().get(), shaderAugmentation, data, renderables, shaderFeatures);
2342 } else if (passNode->materialMode == QSSGRenderUserPass::MaterialModes::OverrideMaterial) {
2343 // Every renderable will use the override material
2344 QSSGShaderFeatures shaderFeatures = data.getShaderFeatures();
2345 shaderFeatures.disableTonemapping();
2346
2347 currentPassData.index = RenderHelpers::rhiPrepareOverrideMaterialUserPass(&(*rhiCtx), this, ps, renderTarget->getRenderPassDescriptor().get(), passNode->overrideMaterial, data, renderables, shaderFeatures);
2348
2349 } else {
2350 // Use original material of the renderables
2351 QSSGShaderFeatures shaderFeatures = data.getShaderFeatures();
2352 shaderFeatures.disableTonemapping();
2353 currentPassData.index = RenderHelpers::rhiPrepareOriginalMaterialUserPass(&(*rhiCtx), this, ps, renderTarget->getRenderPassDescriptor().get(), data, renderables, shaderFeatures);
2354 }
2355 outData.push_back(currentPassData);
2356 } else {
2357 // Wrapped Built-in Passes
2358 if (passNode->passMode == QSSGRenderUserPass::PassModes::SkyboxPass) {
2359 if (rhiCtx->rhi()->isFeatureSupported(QRhi::TexelFetch)) {
2360 if (data.layer.background == QSSGRenderLayer::Background::SkyBoxCubeMap && data.layer.skyBoxCubeMap) {
2361 if (!currentPassData.skyboxCubeMapPass)
2362 currentPassData.skyboxCubeMapPass = SkyboxCubeMapPass();
2363
2364 currentPassData.skyboxCubeMapPass->skipTonemapping = true;
2365 currentPassData.skyboxCubeMapPass->renderPrep(renderer, data);
2366 currentPassData.skyboxCubeMapPass->ps.samples = ps.samples;
2367 currentPassData.skyboxCubeMapPass->rpDesc = renderTarget->getRenderPassDescriptor().get();
2368
2369 currentPassData.skyboxPass = std::nullopt;
2370 } else if (data.layer.background == QSSGRenderLayer::Background::SkyBox && data.layer.lightProbe) {
2371 if (!currentPassData.skyboxPass)
2372 currentPassData.skyboxPass = SkyboxPass();
2373
2374 currentPassData.skyboxPass->skipTonemapping = true;
2375 currentPassData.skyboxPass->renderPrep(renderer, data);
2376 currentPassData.skyboxPass->ps.samples = ps.samples;
2377 currentPassData.skyboxPass->rpDesc = renderTarget->getRenderPassDescriptor().get();
2378
2379 currentPassData.skyboxCubeMapPass = std::nullopt;
2380 }
2381 outData.push_back(currentPassData);
2382 }
2383 } else if (passNode->passMode == QSSGRenderUserPass::PassModes::Item2DPass) {
2384 if (!currentPassData.item2DPass)
2385 currentPassData.item2DPass = Item2DPass();
2386 const bool hasItem2Ds = (data.item2DsView.size() > 0);
2387 if (hasItem2Ds) {
2388 //backup render target
2389 QSSGRhiContextPrivate *rhiCtxD = QSSGRhiContextPrivate::get(renderer.contextInterface()->rhiContext().get());
2390 QRhiRenderTarget *prevRenderTarget = rhiCtx->renderTarget();
2391 rhiCtxD->setRenderTarget(renderTarget->getRenderTarget().get());
2392 currentPassData.item2DPass->renderPrep(renderer, data);
2393 //restore render target
2394 rhiCtxD->setRenderTarget(prevRenderTarget);
2395 outData.push_back(currentPassData);
2396 }
2397 }
2398 }
2399}
2400
2401QT_END_NAMESPACE
void renderPass(QSSGRenderer &renderer) final
void renderPrep(QSSGRenderer &renderer, QSSGLayerRenderData &data) final
void resetForFrame() final
void renderPass(QSSGRenderer &renderer) final
void renderPrep(QSSGRenderer &renderer, QSSGLayerRenderData &data) final
void resetForFrame() final
void renderPrep(QSSGRenderer &renderer, QSSGLayerRenderData &data) final
void renderPass(QSSGRenderer &renderer) final
void resetForFrame() final
static constexpr int MaxBuckets
QSSGRenderCamera * camera
void renderPrep(QSSGRenderer &renderer, QSSGLayerRenderData &data) final
void renderPass(QSSGRenderer &renderer) final
void renderPrep(QSSGRenderer &renderer, QSSGLayerRenderData &data) final
void resetForFrame() final
void renderPass(QSSGRenderer &renderer) final
void resetForFrame() final
void renderPrep(QSSGRenderer &renderer, QSSGLayerRenderData &data) final
QRhiTexture * readbackImage
Type passType() const final
void renderPass(QSSGRenderer &renderer) final
void renderPrep(QSSGRenderer &renderer, QSSGLayerRenderData &data) final
void resetForFrame() final
void renderPass(QSSGRenderer &renderer) final
virtual ~QSSGRenderPass()
friend class QSSGRenderContextInterface
void renderPrep(QSSGRenderer &renderer, QSSGLayerRenderData &data) final
void renderPass(QSSGRenderer &renderer) final
void resetForFrame() final
void renderPrep(QSSGRenderer &renderer, QSSGLayerRenderData &data) final
void resetForFrame() final
void renderPass(QSSGRenderer &renderer) final
const QSSGRenderCamera * camera
void renderPrep(QSSGRenderer &renderer, QSSGLayerRenderData &data) final
void renderPass(QSSGRenderer &renderer) final
void resetForFrame() final
void renderPass(QSSGRenderer &renderer) final
void renderPrep(QSSGRenderer &renderer, QSSGLayerRenderData &data) final
QSSGRenderCamera * camera
void renderPrep(QSSGRenderer &renderer, QSSGLayerRenderData &data) final
void resetForFrame() final
void renderPass(QSSGRenderer &renderer) final
void renderPass(QSSGRenderer &renderer) final
void renderPrep(QSSGRenderer &renderer, QSSGLayerRenderData &data) final
void resetForFrame() final
void renderPass(QSSGRenderer &renderer) final
void resetForFrame() final
void renderPrep(QSSGRenderer &renderer, QSSGLayerRenderData &data) final
void renderPass(QSSGRenderer &renderer) final
void renderPrep(QSSGRenderer &renderer, QSSGLayerRenderData &data) final
void resetForFrame() final
void renderPrep(QSSGRenderer &renderer, QSSGLayerRenderData &data) final
void renderPass(QSSGRenderer &renderer) final
void resetForFrame() final
void renderPrep(QSSGRenderer &renderer, QSSGLayerRenderData &data) final
void resetForFrame() final
void renderPass(QSSGRenderer &renderer) final
void preparePassImpl(QSSGRenderer &renderer, QSSGLayerRenderData &data, QSSGRenderUserPass *passNode, std::vector< UserPassData > &outData, QSSGRhiRenderableTextureV2Ptr renderableTexture)
void resetForFrame() final
void renderPass(QSSGRenderer &renderer) final
void renderPrep(QSSGRenderer &renderer, QSSGLayerRenderData &data) final
Combined button and popup list for selecting options.
void rhiPrepareSkyBox(QSSGRhiContext *rhiCtx, QSSGPassKey passKey, QSSGRenderLayer &layer, QSSGRenderCameraList &cameras, QSSGRenderer &renderer, uint tonemapMode=0)
void rhiPrepareGrid(QSSGRhiContext *rhiCtx, QSSGPassKey passKey, QSSGRenderLayer &layer, QSSGRenderCameraList &cameras, QSSGRenderer &renderer)
QT_BEGIN_NAMESPACE Q_STATIC_LOGGING_CATEGORY(lcSynthesizedIterableAccess, "qt.iterable.synthesized", QtWarningMsg)
#define QSSG_ASSERT_X(cond, msg, action)
#define QSSG_CHECK(cond)
#define QSSG_ASSERT(cond, action)
#define QSSG_GUARD(cond)
static QMatrix4x4 correctMVPForScissor(QRectF viewportRect, QRect scissorRect, bool isYUp)
static quint32 ensureFreeNodes(quint32 value, quint32 multiple)
static QT_BEGIN_NAMESPACE const char defaultFragOutputs[][12]
static QByteArrayView getDefaultOutputName(size_t index)
static quint32 nextMultipleOf(quint32 value, quint32 multiple)