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