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