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