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