1// Copyright (C) 2022 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
4#include "qssgrenderpass_p.h"
5#include "qssgrhiquadrenderer_p.h"
6#include "qssglayerrenderdata_p.h"
7#include "qssgrendercontextcore.h"
8#include "qssgdebugdrawsystem_p.h"
9#include "extensionapi/qssgrenderextensions.h"
10#include "qssgrenderhelpers_p.h"
11
12#include "../utils/qssgassert_p.h"
13
14#include <QtQuick/private/qsgrenderer_p.h>
15#include <qtquick3d_tracepoints_p.h>
16
17QT_BEGIN_NAMESPACE
18
19static inline QMatrix4x4 correctMVPForScissor(QRectF viewportRect, QRect scissorRect, bool isYUp) {
20 const auto &scissorCenter = scissorRect.center();
21 const auto &viewCenter = viewportRect.center();
22 const float scaleX = viewportRect.width() / float(scissorRect.width());
23 const float scaleY = viewportRect.height() / float(scissorRect.height());
24 const float dx = 2 * (viewCenter.x() - scissorCenter.x()) / scissorRect.width();
25 const float dyRect = isYUp ? (scissorCenter.y() - viewCenter.y())
26 : (viewCenter.y() - scissorCenter.y());
27 const float dy = 2 * dyRect / scissorRect.height();
28
29 return QMatrix4x4(scaleX, 0.0f, 0.0f, dx,
30 0.0f, scaleY, 0.0f, dy,
31 0.0f, 0.0f, 1.0f, 0.0f,
32 0.0f, 0.0f, 0.0f, 1.0f);
33}
34
35QSSGRenderPass::~QSSGRenderPass()
36{
37
38}
39
40// SHADOW PASS
41
42void ShadowMapPass::renderPrep(QSSGRenderer &renderer, QSSGLayerRenderData &data)
43{
44 Q_UNUSED(renderer)
45 using namespace RenderHelpers;
46
47 QSSG_ASSERT(!data.renderedCameras.isEmpty(), return);
48 camera = data.renderedCameras[0];
49
50 const auto &renderedDepthWriteObjects = data.getSortedRenderedDepthWriteObjects(camera: *camera);
51 const auto &renderedOpaqueDepthPrepassObjects = data.getSortedrenderedOpaqueDepthPrepassObjects(camera: *camera);
52
53 QSSG_ASSERT(shadowPassObjects.isEmpty(), shadowPassObjects.clear());
54
55 for (const auto &handles : { &renderedDepthWriteObjects, &renderedOpaqueDepthPrepassObjects }) {
56 for (const auto &handle : *handles) {
57 if (handle.obj->renderableFlags.castsShadows())
58 shadowPassObjects.push_back(t: handle);
59 }
60 }
61
62 globalLights = data.globalLights;
63
64 enabled = !shadowPassObjects.isEmpty() || !globalLights.isEmpty();
65
66 if (enabled) {
67 shadowMapManager = data.requestShadowMapManager();
68
69 ps = data.getPipelineState();
70 ps.flags |= { QSSGRhiGraphicsPipelineState::Flag::DepthTestEnabled, QSSGRhiGraphicsPipelineState::Flag::DepthWriteEnabled };
71 // Try reducing self-shadowing and artifacts.
72 ps.depthBias = 2;
73 ps.slopeScaledDepthBias = 1.5f;
74
75 const auto &sortedOpaqueObjects = data.getSortedOpaqueRenderableObjects(camera: *camera);
76 const auto &sortedTransparentObjects = data.getSortedTransparentRenderableObjects(camera: *camera);
77 const auto [casting, receiving] = calculateSortedObjectBounds(sortedOpaqueObjects,
78 sortedTransparentObjects);
79 castingObjectsBox = casting;
80 receivingObjectsBox = receiving;
81
82 if (!debugCamera) {
83 debugCamera = std::make_unique<QSSGRenderCamera>(args: QSSGRenderGraphObject::Type::OrthographicCamera);
84 }
85 }
86}
87
88void ShadowMapPass::renderPass(QSSGRenderer &renderer)
89{
90 using namespace RenderHelpers;
91
92 // INPUT: Sorted opaque and transparent + depth, global lights (scoped lights not supported) and camera.
93
94 // DEPENDECY: None
95
96 // OUTPUT: Textures or cube maps
97
98 // CONDITION: Lights (shadowPassObjects)
99
100 if (enabled) {
101 const auto &rhiCtx = renderer.contextInterface()->rhiContext();
102 QSSG_ASSERT(rhiCtx->rhi()->isRecordingFrame(), return);
103 QRhiCommandBuffer *cb = rhiCtx->commandBuffer();
104 cb->debugMarkBegin(QByteArrayLiteral("Quick3D shadow map"));
105 Q_TRACE_SCOPE(QSSG_renderPass, QStringLiteral("Quick3D shadow map"));
106 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderPass);
107
108 QSSG_CHECK(shadowMapManager);
109 rhiRenderShadowMap(rhiCtx: rhiCtx.get(),
110 passKey: this,
111 ps,
112 shadowMapManager&: *shadowMapManager,
113 camera: *camera,
114 debugCamera: debugCamera.get(),
115 globalLights, // scoped lights are not relevant here
116 sortedOpaqueObjects: shadowPassObjects,
117 renderer,
118 castingObjectsBox,
119 receivingObjectsBox);
120
121 cb->debugMarkEnd();
122 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DRenderPass, 0, QByteArrayLiteral("shadow_map"));
123 }
124}
125
126void ShadowMapPass::resetForFrame()
127{
128 enabled = false;
129 camera = nullptr;
130 castingObjectsBox = {};
131 receivingObjectsBox = {};
132 ps = {};
133 shadowPassObjects.clear();
134 globalLights.clear();
135}
136
137// REFLECTIONMAP PASS
138
139void ReflectionMapPass::renderPrep(QSSGRenderer &renderer, QSSGLayerRenderData &data)
140{
141 Q_UNUSED(renderer);
142 Q_UNUSED(data);
143
144 QSSG_ASSERT(!data.renderedCameras.isEmpty(), return);
145 QSSGRenderCamera *camera = data.renderedCameras[0];
146
147 ps = data.getPipelineState();
148 ps.flags |= { QSSGRhiGraphicsPipelineState::Flag::DepthTestEnabled,
149 QSSGRhiGraphicsPipelineState::Flag::DepthWriteEnabled,
150 QSSGRhiGraphicsPipelineState::Flag::BlendEnabled };
151
152 reflectionProbes = data.reflectionProbes;
153 reflectionMapManager = data.requestReflectionMapManager();
154
155 const auto &sortedOpaqueObjects = data.getSortedOpaqueRenderableObjects(camera: *camera);
156 const auto &sortedTransparentObjects = data.getSortedTransparentRenderableObjects(camera: *camera);
157 const auto &sortedScreenTextureObjects = data.getSortedScreenTextureRenderableObjects(camera: *camera);
158
159 QSSG_ASSERT(reflectionPassObjects.isEmpty(), reflectionPassObjects.clear());
160
161 // NOTE: We should consider keeping track of the reflection casting objects to avoid
162 // filtering this list on each prep.
163 for (const auto &handles : { &sortedOpaqueObjects, &sortedTransparentObjects, &sortedScreenTextureObjects }) {
164 for (const auto &handle : *handles) {
165 if (handle.obj->renderableFlags.testFlag(flag: QSSGRenderableObjectFlag::CastsReflections))
166 reflectionPassObjects.push_back(t: handle);
167 }
168 }
169}
170
171void ReflectionMapPass::renderPass(QSSGRenderer &renderer)
172{
173 using namespace RenderHelpers;
174
175 // INPUT: Reflection probes, sorted opaque and transparent
176
177 // DEPENDECY: None
178
179 // OUTPUT: Cube maps (1 per probe)
180
181 // NOTE: Full pass with a sky box pass
182
183 // CONDITION: Probes and sorted opaque and transparent
184
185 const auto &rhiCtx = renderer.contextInterface()->rhiContext();
186 QSSG_ASSERT(rhiCtx->rhi()->isRecordingFrame(), return);
187 QRhiCommandBuffer *cb = rhiCtx->commandBuffer();
188
189 const auto *layerData = QSSGLayerRenderData::getCurrent(renderer);
190 QSSG_ASSERT(layerData, return);
191
192 QSSG_CHECK(reflectionMapManager);
193 if (!reflectionPassObjects.isEmpty() || !reflectionProbes.isEmpty()) {
194 cb->debugMarkBegin(QByteArrayLiteral("Quick3D reflection map"));
195 Q_TRACE_SCOPE(QSSG_renderPass, QStringLiteral("Quick3D reflection map"));
196 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderPass);
197 rhiRenderReflectionMap(rhiCtx: rhiCtx.get(),
198 passKey: this,
199 inData: *layerData,
200 ps: &ps,
201 reflectionMapManager&: *reflectionMapManager,
202 reflectionProbes,
203 reflectionPassObjects,
204 renderer);
205
206 cb->debugMarkEnd();
207 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DRenderPass, 0, QByteArrayLiteral("reflection_map"));
208 }
209}
210
211void ReflectionMapPass::resetForFrame()
212{
213 ps = {};
214 reflectionProbes.clear();
215 reflectionPassObjects.clear();
216}
217
218// ZPrePass
219void ZPrePassPass::renderPrep(QSSGRenderer &renderer, QSSGLayerRenderData &data)
220{
221 using namespace RenderHelpers;
222
223 // INPUT: Item2Ds + depth write + depth prepass
224
225 // DEPENDECY: none
226
227 // OUTPUT: Depth buffer attchment for current target
228
229 // NOTE: Could we make the depth pass more complete and just do a blit here?
230 //
231 // 1. If we have a depth map, just do a blit and then update with the rest
232 // 2. If we don't have a depth map (and/or SSAO) consider using a lower lod level.
233
234 // CONDITION: Input + globally enabled or ?
235
236 QSSG_ASSERT(!data.renderedCameras.isEmpty(), return);
237 QSSGRenderCamera *camera = data.renderedCameras[0];
238
239 const auto &rhiCtx = renderer.contextInterface()->rhiContext();
240 QSSG_ASSERT(rhiCtx->rhi()->isRecordingFrame(), return);
241 QRhiCommandBuffer *cb = rhiCtx->commandBuffer();
242 ps = data.getPipelineState();
243
244 renderedDepthWriteObjects = data.getSortedRenderedDepthWriteObjects(camera: *camera);
245 renderedOpaqueDepthPrepassObjects = data.getSortedrenderedOpaqueDepthPrepassObjects(camera: *camera);
246
247 cb->debugMarkBegin(QByteArrayLiteral("Quick3D prepare Z prepass"));
248 Q_TRACE_SCOPE(QSSG_renderPass, QStringLiteral("Quick3D prepare Z prepass"));
249 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderPass);
250 active = rhiPrepareDepthPass(rhiCtx: rhiCtx.get(), passKey: this, basePipelineState: ps, rpDesc: rhiCtx->mainRenderPassDescriptor(), inData&: data,
251 sortedOpaqueObjects: renderedDepthWriteObjects, sortedTransparentObjects: renderedOpaqueDepthPrepassObjects,
252 samples: rhiCtx->mainPassSampleCount(), viewCount: rhiCtx->mainPassViewCount());
253 data.setZPrePassPrepResult(active);
254 cb->debugMarkEnd();
255 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DRenderPass, 0, QByteArrayLiteral("prepare_z_prepass"));
256}
257
258void ZPrePassPass::renderPass(QSSGRenderer &renderer)
259{
260 using namespace RenderHelpers;
261
262 const auto &rhiCtx = renderer.contextInterface()->rhiContext();
263 QSSG_ASSERT(rhiCtx->rhi()->isRecordingFrame(), return);
264
265 bool needsSetViewport = true;
266 QRhiCommandBuffer *cb = rhiCtx->commandBuffer();
267
268 if (active) {
269 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderPass);
270 Q_TRACE_SCOPE(QSSG_renderPass, QStringLiteral("Quick3D render Z prepass"));
271 cb->debugMarkBegin(QByteArrayLiteral("Quick3D render Z prepass"));
272 rhiRenderDepthPass(rhiCtx: rhiCtx.get(), ps, sortedOpaqueObjects: renderedDepthWriteObjects, sortedTransparentObjects: renderedOpaqueDepthPrepassObjects, needsSetViewport: &needsSetViewport);
273 cb->debugMarkEnd();
274 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DRenderPass, 0, QByteArrayLiteral("render_z_prepass"));
275 }
276}
277
278void ZPrePassPass::resetForFrame()
279{
280 renderedDepthWriteObjects.clear();
281 renderedOpaqueDepthPrepassObjects.clear();
282 ps = {};
283 active = false;
284}
285
286// SSAO PASS
287void SSAOMapPass::renderPrep(QSSGRenderer &renderer, QSSGLayerRenderData &data)
288{
289 using namespace RenderHelpers;
290
291 // Assumption for now is that all passes are keept alive and only reset once a frame is done.
292 // I.e., holding data like this should be safe (If that's no longer the case we need to do ref counting
293 // for shared data).
294
295 const auto &rhiCtx = renderer.contextInterface()->rhiContext();
296 QSSG_ASSERT(rhiCtx->rhi()->isRecordingFrame(), return);
297
298 rhiAoTexture = data.getRenderResult(id: QSSGFrameData::RenderResult::AoTexture);
299 rhiDepthTexture = data.getRenderResult(id: QSSGFrameData::RenderResult::DepthTexture);
300 QSSG_ASSERT_X(!data.renderedCameras.isEmpty(), "Preparing AO pass failed, missing camera", return);
301 camera = data.renderedCameras[0];
302 QSSG_ASSERT_X((rhiDepthTexture && rhiDepthTexture->isValid()), "Preparing AO pass failed, missing equired texture(s)", return);
303
304 const auto &shaderCache = renderer.contextInterface()->shaderCache();
305 ssaoShaderPipeline = shaderCache->getBuiltInRhiShaders().getRhiSsaoShader(viewCount: rhiCtx->mainPassViewCount());
306 aoSettings = { .aoStrength: data.layer.aoStrength, .aoDistance: data.layer.aoDistance, .aoSoftness: data.layer.aoSoftness, .aoBias: data.layer.aoBias, .aoSamplerate: data.layer.aoSamplerate, .aoDither: data.layer.aoDither };
307
308 ps = data.getPipelineState();
309 const auto &layerPrepResult = data.layerPrepResult;
310 const bool ready = rhiAoTexture && rhiPrepareAoTexture(rhiCtx: rhiCtx.get(), size: layerPrepResult.textureDimensions(), renderableTex: rhiAoTexture);
311
312 if (Q_UNLIKELY(!ready))
313 rhiAoTexture = nullptr;
314}
315
316void SSAOMapPass::renderPass(QSSGRenderer &renderer)
317{
318 using namespace RenderHelpers;
319
320 // INPUT: Camera + depth map
321
322 // DEPENDECY: Depth map (zprepass)
323
324 // OUTPUT: AO Texture
325
326 // NOTE:
327
328 // CONDITION: SSAO enabled
329 QSSG_ASSERT(camera && rhiDepthTexture && rhiDepthTexture->isValid(), return);
330
331 const auto &rhiCtx = renderer.contextInterface()->rhiContext();
332 QSSG_ASSERT(rhiCtx->rhi()->isRecordingFrame(), return);
333
334 QRhiCommandBuffer *cb = rhiCtx->commandBuffer();
335 cb->debugMarkBegin(QByteArrayLiteral("Quick3D SSAO map"));
336 Q_TRACE_SCOPE(QSSG_renderPass, QStringLiteral("Quick3D SSAO map"));
337 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderPass);
338
339 if (Q_LIKELY(rhiAoTexture && rhiAoTexture->isValid())) {
340 rhiRenderAoTexture(rhiCtx: rhiCtx.get(),
341 passKey: this,
342 renderer,
343 shaderPipeline&: *ssaoShaderPipeline,
344 ps,
345 ao: aoSettings,
346 rhiAoTexture: *rhiAoTexture,
347 rhiDepthTexture: *rhiDepthTexture,
348 camera: *camera);
349 }
350
351 cb->debugMarkEnd();
352 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DRenderPass, 0, QByteArrayLiteral("ssao_map"));
353}
354
355void SSAOMapPass::resetForFrame()
356{
357 rhiDepthTexture = nullptr;
358 rhiAoTexture = nullptr;
359 camera = nullptr;
360 ps = {};
361 aoSettings = {};
362}
363
364// DEPTH TEXTURE PASS
365void DepthMapPass::renderPrep(QSSGRenderer &renderer, QSSGLayerRenderData &data)
366{
367 using namespace RenderHelpers;
368
369 QSSG_ASSERT(!data.renderedCameras.isEmpty(), return);
370 QSSGRenderCamera *camera = data.renderedCameras[0];
371
372 const auto &rhiCtx = renderer.contextInterface()->rhiContext();
373 QSSG_ASSERT(rhiCtx->rhi()->isRecordingFrame(), return);
374 const auto &layerPrepResult = data.layerPrepResult;
375 bool ready = false;
376 ps = data.getPipelineState();
377 rhiDepthTexture = data.getRenderResult(id: QSSGFrameData::RenderResult::DepthTexture);
378 if (Q_LIKELY(rhiDepthTexture && rhiPrepareDepthTexture(rhiCtx.get(), layerPrepResult.textureDimensions(), rhiDepthTexture))) {
379 sortedOpaqueObjects = data.getSortedOpaqueRenderableObjects(camera: *camera);
380 sortedTransparentObjects = data.getSortedTransparentRenderableObjects(camera: *camera);
381 // the depth texture is always non-MSAA, but is a 2D array with multiview
382 ready = rhiPrepareDepthPass(rhiCtx: rhiCtx.get(), passKey: this, basePipelineState: ps, rpDesc: rhiDepthTexture->rpDesc, inData&: data,
383 sortedOpaqueObjects, sortedTransparentObjects,
384 samples: 1, viewCount: rhiCtx->mainPassViewCount());
385 }
386
387 if (Q_UNLIKELY(!ready))
388 rhiDepthTexture = nullptr;
389}
390
391void DepthMapPass::renderPass(QSSGRenderer &renderer)
392{
393 using namespace RenderHelpers;
394
395 // INPUT: sorted objects (opaque + transparent) (maybe...)
396
397 // DEPENDECY: If this is only used for the AO case, that dictates if this should be done or not.
398
399 // OUTPUT: Texture
400
401 // NOTE: Why are we prepping opaque + transparent object if we're not using them? And why are we staying compatible with 5.15?
402 // Only used for AO? Merge into the AO pass?
403
404 // NOTES:
405 //
406 // 1: If requested, use this and blit it in the z-pre pass.
407 // 2. Why are we handling the transparent objects in the render prep (only)?
408
409 // CONDITION:
410
411 const auto &rhiCtx = renderer.contextInterface()->rhiContext();
412 QSSG_ASSERT(rhiCtx->rhi()->isRecordingFrame(), return);
413 QRhiCommandBuffer *cb = rhiCtx->commandBuffer();
414 cb->debugMarkBegin(QByteArrayLiteral("Quick3D depth texture"));
415
416 if (Q_LIKELY(rhiDepthTexture && rhiDepthTexture->isValid())) {
417 bool needsSetViewport = true;
418 cb->beginPass(rt: rhiDepthTexture->rt, colorClearValue: Qt::transparent, depthStencilClearValue: { 1.0f, 0 }, resourceUpdates: nullptr, flags: rhiCtx->commonPassFlags());
419 QSSGRHICTX_STAT(rhiCtx, beginRenderPass(rhiDepthTexture->rt));
420 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderPass);
421 // NB! We do not pass sortedTransparentObjects in the 4th
422 // argument to stay compatible with the 5.15 code base,
423 // which also does not include semi-transparent objects in
424 // the depth texture. In addition, capturing after the
425 // opaque pass, not including transparent objects, is part
426 // of the contract for screen reading custom materials,
427 // both for depth and color.
428 rhiRenderDepthPass(rhiCtx: rhiCtx.get(), ps, sortedOpaqueObjects, sortedTransparentObjects: {}, needsSetViewport: &needsSetViewport);
429 cb->endPass();
430 QSSGRHICTX_STAT(rhiCtx, endRenderPass());
431 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DRenderPass, 0, QByteArrayLiteral("depth_texture"));
432 }
433
434 cb->debugMarkEnd();
435}
436
437void DepthMapPass::resetForFrame()
438{
439 rhiDepthTexture = nullptr;
440 sortedOpaqueObjects.clear();
441 sortedTransparentObjects.clear();
442 ps = {};
443}
444
445// SCREEN TEXTURE PASS
446
447void ScreenMapPass::renderPrep(QSSGRenderer &renderer, QSSGLayerRenderData &data)
448{
449 using namespace RenderHelpers;
450
451 QSSG_ASSERT(!data.renderedCameras.isEmpty(), return);
452 QSSGRenderCamera *camera = data.renderedCameras[0];
453
454 const auto &rhiCtx = renderer.contextInterface()->rhiContext();
455 QSSG_ASSERT(rhiCtx->rhi()->isRecordingFrame(), return);
456 rhiScreenTexture = data.getRenderResult(id: QSSGFrameData::RenderResult::ScreenTexture);
457 auto &layer = data.layer;
458 const auto &layerPrepResult = data.layerPrepResult;
459 wantsMips = layerPrepResult.flags.requiresMipmapsForScreenTexture();
460 sortedOpaqueObjects = data.getSortedOpaqueRenderableObjects(camera: *camera);
461 ps = data.getPipelineState();
462 ps.samples = 1; // screen texture is always non-MSAA
463 ps.viewCount = rhiCtx->mainPassViewCount(); // but is a 2D texture array when multiview
464
465 if (layer.background == QSSGRenderLayer::Background::Color)
466 clearColor = QColor::fromRgbF(r: layer.clearColor.x(), g: layer.clearColor.y(), b: layer.clearColor.z());
467
468 if (rhiCtx->rhi()->isFeatureSupported(feature: QRhi::TexelFetch)) {
469 if (layer.background == QSSGRenderLayer::Background::SkyBoxCubeMap && layer.skyBoxCubeMap) {
470 if (!skyboxCubeMapPass)
471 skyboxCubeMapPass = SkyboxCubeMapPass();
472
473 skyboxCubeMapPass->skipTonemapping = true;
474 skyboxCubeMapPass->renderPrep(renderer, data);
475
476 // The pass expects to output to the main render target, but we have
477 // our own texture here, possibly with a differing sample count, so
478 // override the relevant settings once renderPrep() is done.
479 skyboxCubeMapPass->ps.samples = ps.samples;
480
481 skyboxPass = std::nullopt;
482 } else if (layer.background == QSSGRenderLayer::Background::SkyBox && layer.lightProbe) {
483 if (!skyboxPass)
484 skyboxPass = SkyboxPass();
485
486 skyboxPass->skipTonemapping = true;
487 skyboxPass->renderPrep(renderer, data);
488
489 skyboxPass->ps.samples = ps.samples;
490
491 skyboxCubeMapPass = std::nullopt;
492 }
493 }
494
495 bool ready = false;
496 if (Q_LIKELY(rhiScreenTexture && rhiPrepareScreenTexture(rhiCtx.get(), layerPrepResult.textureDimensions(), wantsMips, rhiScreenTexture))) {
497 ready = true;
498 if (skyboxCubeMapPass)
499 skyboxCubeMapPass->rpDesc = rhiScreenTexture->rpDesc;
500 if (skyboxPass)
501 skyboxPass->rpDesc = rhiScreenTexture->rpDesc;
502 // NB: not compatible with disabling LayerEnableDepthTest
503 // because there are effectively no "opaque" objects then.
504 // Disable Tonemapping for all materials in the screen pass texture
505 shaderFeatures = data.getShaderFeatures();
506 shaderFeatures.disableTonemapping();
507 const auto &sortedOpaqueObjects = data.getSortedOpaqueRenderableObjects(camera: *camera);
508 for (const auto &handle : sortedOpaqueObjects) {
509 // Reflection cube maps are not available at this point, make sure they are turned off.
510 bool recRef = handle.obj->renderableFlags.receivesReflections();
511 handle.obj->renderableFlags.setReceivesReflections(false);
512 rhiPrepareRenderable(rhiCtx: rhiCtx.get(), passKey: this, inData: data, inObject&: *handle.obj, renderPassDescriptor: rhiScreenTexture->rpDesc, ps: &ps, featureSet: shaderFeatures, samples: 1, viewCount: rhiCtx->mainPassViewCount());
513 handle.obj->renderableFlags.setReceivesReflections(recRef);
514 }
515 }
516
517 if (Q_UNLIKELY(!ready))
518 rhiScreenTexture = nullptr;
519}
520
521void ScreenMapPass::renderPass(QSSGRenderer &renderer)
522{
523 using namespace RenderHelpers;
524
525 // INPUT: Sorted opaque objects + depth objects
526
527 // DEPENDECY: Depth pass (if enabled)
528
529 // OUTPUT: Texture (screen texture).
530
531 // NOTE: Used for refrection and effects (?)
532
533 // CONDITION:
534
535 const auto &rhiCtx = renderer.contextInterface()->rhiContext();
536 QSSG_ASSERT(rhiCtx->rhi()->isRecordingFrame(), return);
537 QRhiCommandBuffer *cb = rhiCtx->commandBuffer();
538
539 cb->debugMarkBegin(QByteArrayLiteral("Quick3D screen texture"));
540
541 if (Q_LIKELY(rhiScreenTexture && rhiScreenTexture->isValid())) {
542 cb->beginPass(rt: rhiScreenTexture->rt, colorClearValue: clearColor, depthStencilClearValue: { 1.0f, 0 }, resourceUpdates: nullptr, flags: rhiCtx->commonPassFlags());
543 QSSGRHICTX_STAT(rhiCtx, beginRenderPass(rhiScreenTexture->rt));
544 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderPass);
545
546 bool needsSetViewport = true;
547 for (const auto &handle : std::as_const(t&: sortedOpaqueObjects))
548 rhiRenderRenderable(rhiCtx: rhiCtx.get(), state: ps, object&: *handle.obj, needsSetViewport: &needsSetViewport);
549
550 if (skyboxCubeMapPass)
551 skyboxCubeMapPass->renderPass(renderer);
552 else if (skyboxPass)
553 skyboxPass->renderPass(renderer);
554
555 QRhiResourceUpdateBatch *rub = nullptr;
556 if (wantsMips) {
557 rub = rhiCtx->rhi()->nextResourceUpdateBatch();
558 rub->generateMips(tex: rhiScreenTexture->texture);
559 }
560 cb->endPass(resourceUpdates: rub);
561 QSSGRHICTX_STAT(rhiCtx, endRenderPass());
562 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DRenderPass, 0, QByteArrayLiteral("screen_texture"));
563 }
564
565 cb->debugMarkEnd();
566}
567
568void ScreenMapPass::resetForFrame()
569{
570 rhiScreenTexture = nullptr;
571 if (skyboxPass)
572 skyboxPass->resetForFrame();
573 if (skyboxCubeMapPass)
574 skyboxCubeMapPass->resetForFrame();
575 ps = {};
576 wantsMips = false;
577 clearColor = Qt::transparent;
578 shaderFeatures = {};
579 sortedOpaqueObjects.clear();
580}
581
582void ScreenReflectionPass::renderPrep(QSSGRenderer &renderer, QSSGLayerRenderData &data)
583{
584 QSSG_ASSERT(!data.renderedCameras.isEmpty(), return);
585 QSSGRenderCamera *camera = data.renderedCameras[0];
586
587 const auto &rhiCtx = renderer.contextInterface()->rhiContext();
588 QSSG_ASSERT(rhiCtx->rhi()->isRecordingFrame(), return);
589 rhiScreenTexture = data.getRenderResult(id: QSSGFrameData::RenderResult::ScreenTexture);
590 QSSG_ASSERT_X(rhiScreenTexture && rhiScreenTexture->isValid(), "Invalid screen texture!", return);
591
592 const auto &layer = data.layer;
593 const auto shaderFeatures = data.getShaderFeatures();
594 const bool layerEnableDepthTest = layer.layerFlags.testFlag(flag: QSSGRenderLayer::LayerFlag::EnableDepthTest);
595
596 QRhiRenderPassDescriptor *mainRpDesc = rhiCtx->mainRenderPassDescriptor();
597 const int samples = rhiCtx->mainPassSampleCount();
598 const int viewCount = rhiCtx->mainPassViewCount();
599
600 // NOTE: We're piggybacking on the screen map pass for now, but we could do better.
601 ps = data.getPipelineState();
602 const bool depthTestEnabled = (data.screenMapPass.ps.flags.testFlag(flag: QSSGRhiGraphicsPipelineState::Flag::DepthTestEnabled));
603 ps.flags.setFlag(flag: QSSGRhiGraphicsPipelineState::Flag::DepthTestEnabled, on: depthTestEnabled);
604 const bool depthWriteEnabled = (data.screenMapPass.ps.flags.testFlag(flag: QSSGRhiGraphicsPipelineState::Flag::DepthWriteEnabled));
605 ps.flags.setFlag(flag: QSSGRhiGraphicsPipelineState::Flag::DepthWriteEnabled, on: depthWriteEnabled);
606 sortedScreenTextureObjects = data.getSortedScreenTextureRenderableObjects(camera: *camera);
607 for (const auto &handle : std::as_const(t&: sortedScreenTextureObjects)) {
608 QSSGRenderableObject *theObject = handle.obj;
609 const auto depthWriteMode = theObject->depthWriteMode;
610 ps.flags.setFlag(flag: QSSGRhiGraphicsPipelineState::Flag::BlendEnabled, on: theObject->renderableFlags.hasTransparency());
611 const bool curDepthWriteEnabled = !(depthWriteMode == QSSGDepthDrawMode::Never || depthWriteMode == QSSGDepthDrawMode::OpaquePrePass
612 || data.isZPrePassActive() || !layerEnableDepthTest);
613 ps.flags.setFlag(flag: QSSGRhiGraphicsPipelineState::Flag::DepthWriteEnabled, on: curDepthWriteEnabled);
614 RenderHelpers::rhiPrepareRenderable(rhiCtx: rhiCtx.get(), passKey: this, inData: data, inObject&: *theObject, renderPassDescriptor: mainRpDesc, ps: &ps, featureSet: shaderFeatures, samples, viewCount);
615 }
616}
617
618void ScreenReflectionPass::renderPass(QSSGRenderer &renderer)
619{
620 if (QSSG_GUARD(rhiScreenTexture && rhiScreenTexture->isValid())) {
621 const auto &rhiCtx = renderer.contextInterface()->rhiContext();
622 QSSG_ASSERT(rhiCtx->rhi()->isRecordingFrame(), return);
623 QRhiCommandBuffer *cb = rhiCtx->commandBuffer();
624
625 // 3. Screen texture depended objects
626 cb->debugMarkBegin(QByteArrayLiteral("Quick3D render screen texture dependent"));
627 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderPass);
628 Q_TRACE(QSSG_renderPass_entry, QStringLiteral("Quick3D render screen texture dependent"));
629 bool needsSetViewport = true;
630 for (const auto &handle : std::as_const(t&: sortedScreenTextureObjects)) {
631 QSSGRenderableObject *theObject = handle.obj;
632 RenderHelpers::rhiRenderRenderable(rhiCtx: rhiCtx.get(), state: ps, object&: *theObject, needsSetViewport: &needsSetViewport);
633 }
634 cb->debugMarkEnd();
635 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DRenderPass, 0, QByteArrayLiteral("screen_texture_dependent"));
636 Q_TRACE(QSSG_renderPass_exit);
637 }
638}
639
640void ScreenReflectionPass::resetForFrame()
641{
642 sortedScreenTextureObjects.clear();
643 rhiScreenTexture = nullptr;
644 ps = {};
645}
646
647void OpaquePass::prep(const QSSGRenderContextInterface &ctx,
648 QSSGLayerRenderData &data,
649 QSSGPassKey passKey,
650 QSSGRhiGraphicsPipelineState &ps,
651 QSSGShaderFeatures shaderFeatures,
652 QRhiRenderPassDescriptor *rpDesc,
653 const QSSGRenderableObjectList &sortedOpaqueObjects)
654{
655 const auto &rhiCtx = ctx.rhiContext();
656 QSSG_ASSERT(rpDesc && rhiCtx->rhi()->isRecordingFrame(), return);
657
658 const auto &layer = data.layer;
659 const bool layerEnableDepthTest = layer.layerFlags.testFlag(flag: QSSGRenderLayer::LayerFlag::EnableDepthTest);
660
661 for (const auto &handle : std::as_const(t: sortedOpaqueObjects)) {
662 QSSGRenderableObject *theObject = handle.obj;
663 const auto depthWriteMode = theObject->depthWriteMode;
664 const bool curDepthWriteEnabled = !(depthWriteMode == QSSGDepthDrawMode::Never ||
665 depthWriteMode == QSSGDepthDrawMode::OpaquePrePass ||
666 data.isZPrePassActive() || !layerEnableDepthTest);
667 ps.flags.setFlag(flag: QSSGRhiGraphicsPipelineState::Flag::DepthWriteEnabled, on: curDepthWriteEnabled);
668 RenderHelpers::rhiPrepareRenderable(rhiCtx: rhiCtx.get(), passKey, inData: data, inObject&: *theObject, renderPassDescriptor: rpDesc, ps: &ps, featureSet: shaderFeatures, samples: ps.samples, viewCount: ps.viewCount);
669 }
670}
671
672void OpaquePass::render(const QSSGRenderContextInterface &ctx,
673 const QSSGRhiGraphicsPipelineState &ps,
674 const QSSGRenderableObjectList &sortedOpaqueObjects)
675{
676 const auto &rhiCtx = ctx.rhiContext();
677 QSSG_ASSERT(rhiCtx->rhi()->isRecordingFrame(), return);
678 bool needsSetViewport = true;
679 for (const auto &handle : std::as_const(t: sortedOpaqueObjects)) {
680 QSSGRenderableObject *theObject = handle.obj;
681 RenderHelpers::rhiRenderRenderable(rhiCtx: rhiCtx.get(), state: ps, object&: *theObject, needsSetViewport: &needsSetViewport);
682 }
683}
684
685void OpaquePass::renderPrep(QSSGRenderer &renderer, QSSGLayerRenderData &data)
686{
687 auto *ctx = renderer.contextInterface();
688 const auto &rhiCtx = ctx->rhiContext();
689 QSSG_ASSERT(rhiCtx->rhi()->isRecordingFrame(), return);
690 QSSG_ASSERT(!data.renderedCameras.isEmpty() && data.renderedCameraData.has_value() , return);
691 QSSGRenderCamera *camera = data.renderedCameras[0];
692
693 ps = data.getPipelineState();
694 ps.samples = rhiCtx->mainPassSampleCount();
695 ps.viewCount = rhiCtx->mainPassViewCount();
696 ps.depthFunc = QRhiGraphicsPipeline::LessOrEqual;
697 ps.flags.setFlag(flag: QSSGRhiGraphicsPipelineState::Flag::BlendEnabled, on: false);
698
699 // opaque objects (or, this list is empty when LayerEnableDepthTest is disabled)
700 sortedOpaqueObjects = data.getSortedOpaqueRenderableObjects(camera: *camera);
701 shaderFeatures = data.getShaderFeatures();
702
703 QRhiRenderPassDescriptor *mainRpDesc = rhiCtx->mainRenderPassDescriptor();
704 prep(ctx: *ctx, data, passKey: this, ps, shaderFeatures, rpDesc: mainRpDesc, sortedOpaqueObjects);
705}
706
707void OpaquePass::renderPass(QSSGRenderer &renderer)
708{
709 auto *ctx = renderer.contextInterface();
710 const auto &rhiCtx = ctx->rhiContext();
711 QSSG_ASSERT(rhiCtx->rhi()->isRecordingFrame(), return);
712 QRhiCommandBuffer *cb = rhiCtx->commandBuffer();
713
714 cb->debugMarkBegin(QByteArrayLiteral("Quick3D render opaque"));
715 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderPass);
716 Q_TRACE(QSSG_renderPass_entry, QStringLiteral("Quick3D render opaque"));
717 render(ctx: *ctx, ps, sortedOpaqueObjects);
718 cb->debugMarkEnd();
719 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DRenderPass, 0, QByteArrayLiteral("opaque_pass"));
720 Q_TRACE(QSSG_renderPass_exit);
721}
722
723void OpaquePass::resetForFrame()
724{
725 sortedOpaqueObjects.clear();
726 ps = {};
727 shaderFeatures = {};
728}
729
730void TransparentPass::prep(const QSSGRenderContextInterface &ctx,
731 QSSGLayerRenderData &data,
732 QSSGPassKey passKey,
733 QSSGRhiGraphicsPipelineState &ps,
734 QSSGShaderFeatures shaderFeatures,
735 QRhiRenderPassDescriptor *rpDesc,
736 const QSSGRenderableObjectList &sortedTransparentObjects)
737{
738 const auto &rhiCtx = ctx.rhiContext();
739 QSSG_ASSERT(rpDesc && rhiCtx->rhi()->isRecordingFrame(), return);
740
741 const bool zPrePassActive = data.isZPrePassActive();
742 for (const auto &handle : std::as_const(t: sortedTransparentObjects)) {
743 QSSGRenderableObject *theObject = handle.obj;
744 const auto depthWriteMode = theObject->depthWriteMode;
745 const bool curDepthWriteEnabled = (depthWriteMode == QSSGDepthDrawMode::Always && !zPrePassActive);
746 ps.flags.setFlag(flag: QSSGRhiGraphicsPipelineState::Flag::DepthWriteEnabled, on: curDepthWriteEnabled);
747 if (!(theObject->renderableFlags.isCompletelyTransparent()))
748 RenderHelpers::rhiPrepareRenderable(rhiCtx: rhiCtx.get(), passKey, inData: data, inObject&: *theObject, renderPassDescriptor: rpDesc, ps: &ps, featureSet: shaderFeatures, samples: ps.samples, viewCount: ps.viewCount);
749 }
750}
751
752void TransparentPass::render(const QSSGRenderContextInterface &ctx,
753 const QSSGRhiGraphicsPipelineState &ps,
754 const QSSGRenderableObjectList &sortedTransparentObjects)
755{
756 const auto &rhiCtx = ctx.rhiContext();
757 QSSG_ASSERT(rhiCtx->rhi()->isRecordingFrame(), return);
758 // If scissorRect is set, Item2Ds will be drawn by a workaround of modifying
759 // viewport, not using actual 3D scissor test.
760 // It means non-opaque objects may be affected by this viewport setting.
761 bool needsSetViewport = true;
762 for (const auto &handle : std::as_const(t: sortedTransparentObjects)) {
763 QSSGRenderableObject *theObject = handle.obj;
764 if (!theObject->renderableFlags.isCompletelyTransparent())
765 RenderHelpers::rhiRenderRenderable(rhiCtx: rhiCtx.get(), state: ps, object&: *theObject, needsSetViewport: &needsSetViewport);
766 }
767}
768
769void TransparentPass::renderPrep(QSSGRenderer &renderer, QSSGLayerRenderData &data)
770{
771 auto *ctx = renderer.contextInterface();
772 const auto &rhiCtx = ctx->rhiContext();
773
774 QSSG_ASSERT(!data.renderedCameras.isEmpty() && data.renderedCameraData.has_value() , return);
775 QSSGRenderCamera *camera = data.renderedCameras[0];
776
777 QRhiRenderPassDescriptor *mainRpDesc = rhiCtx->mainRenderPassDescriptor();
778
779 ps = data.getPipelineState();
780 ps.samples = rhiCtx->mainPassSampleCount();
781 ps.viewCount = rhiCtx->mainPassViewCount();
782
783 // transparent objects (or, without LayerEnableDepthTest, all objects)
784 ps.flags.setFlag(flag: QSSGRhiGraphicsPipelineState::Flag::BlendEnabled, on: true);
785 ps.flags.setFlag(flag: QSSGRhiGraphicsPipelineState::Flag::DepthWriteEnabled, on: false);
786
787 shaderFeatures = data.getShaderFeatures();
788 sortedTransparentObjects = data.getSortedTransparentRenderableObjects(camera: *camera);
789
790 prep(ctx: *ctx, data, passKey: this, ps, shaderFeatures, rpDesc: mainRpDesc, sortedTransparentObjects);
791}
792
793void TransparentPass::renderPass(QSSGRenderer &renderer)
794{
795 auto *ctx = renderer.contextInterface();
796 const auto &rhiCtx = ctx->rhiContext();
797 QSSG_ASSERT(rhiCtx->rhi()->isRecordingFrame(), return);
798 QRhiCommandBuffer *cb = rhiCtx->commandBuffer();
799
800 cb->debugMarkBegin(QByteArrayLiteral("Quick3D render alpha"));
801 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderPass);
802 Q_TRACE(QSSG_renderPass_entry, QStringLiteral("Quick3D render alpha"));
803 render(ctx: *ctx, ps, sortedTransparentObjects);
804 cb->debugMarkEnd();
805 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DRenderPass, 0, QByteArrayLiteral("transparent_pass"));
806 Q_TRACE(QSSG_renderPass_exit);
807}
808
809void TransparentPass::resetForFrame()
810{
811 sortedTransparentObjects.clear();
812 ps = {};
813 shaderFeatures = {};
814}
815
816void SkyboxPass::renderPrep(QSSGRenderer &renderer, QSSGLayerRenderData &data)
817{
818 if (!skipPrep) {
819 const auto &rhiCtx = renderer.contextInterface()->rhiContext();
820 QSSG_ASSERT(rhiCtx->rhi()->isRecordingFrame(), return);
821 QSSG_ASSERT(!data.renderedCameras.isEmpty(), return);
822 QSSG_ASSERT(data.renderedCameras.count() == rhiCtx->mainPassViewCount(), return);
823 layer = &data.layer;
824 QSSG_ASSERT(layer, return);
825
826 rpDesc = rhiCtx->mainRenderPassDescriptor();
827 ps = data.getPipelineState();
828 ps.samples = rhiCtx->mainPassSampleCount();
829 ps.viewCount = rhiCtx->mainPassViewCount();
830 ps.polygonMode = QRhiGraphicsPipeline::Fill;
831
832 // When there are effects, then it is up to the last pass of the
833 // last effect to perform tonemapping, neither the skybox nor the
834 // main render pass should alter the colors then.
835 skipTonemapping = layer->firstEffect != nullptr;
836
837 RenderHelpers::rhiPrepareSkyBox(rhiCtx: rhiCtx.get(), passKey: this, layer&: *layer, cameras&: data.renderedCameras, renderer);
838 skipPrep = true;
839 }
840}
841
842void SkyboxPass::renderPass(QSSGRenderer &renderer)
843{
844 const auto &rhiCtx = renderer.contextInterface()->rhiContext();
845 QSSG_ASSERT(rhiCtx->rhi()->isRecordingFrame(), return);
846 QSSG_ASSERT(layer, return);
847
848 QRhiShaderResourceBindings *srb = layer->skyBoxSrb;
849 QSSG_ASSERT(srb, return);
850
851 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderPass);
852 Q_TRACE_SCOPE(QSSG_renderPass, QStringLiteral("Quick3D render skybox"));
853
854 // Note: We get the shader here, as the screen map pass might modify the state of
855 // the tonemap mode.
856
857 QSSGRenderLayer::TonemapMode tonemapMode = skipTonemapping && (layer->tonemapMode != QSSGRenderLayer::TonemapMode::Custom) ? QSSGRenderLayer::TonemapMode::None : layer->tonemapMode;
858 const auto &shaderCache = renderer.contextInterface()->shaderCache();
859 auto shaderPipeline = shaderCache->getBuiltInRhiShaders().getRhiSkyBoxShader(tonemapMode, isRGBE: layer->skyBoxIsRgbe8, viewCount: rhiCtx->mainPassViewCount());
860 QSSG_CHECK(shaderPipeline);
861 QSSGRhiGraphicsPipelineStatePrivate::setShaderPipeline(ps, pipeline: shaderPipeline.get());
862 renderer.rhiQuadRenderer()->recordRenderQuad(rhiCtx: rhiCtx.get(), ps: &ps, srb, rpDesc, flags: { QSSGRhiQuadRenderer::DepthTest | QSSGRhiQuadRenderer::RenderBehind });
863 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DRenderPass, 0, QByteArrayLiteral("skybox_map"));
864}
865
866void SkyboxPass::resetForFrame()
867{
868 ps = {};
869 layer = nullptr;
870 skipPrep = false;
871}
872
873void SkyboxCubeMapPass::renderPrep(QSSGRenderer &renderer, QSSGLayerRenderData &data)
874{
875 const auto &rhiCtx = renderer.contextInterface()->rhiContext();
876 QSSG_ASSERT(rhiCtx->rhi()->isRecordingFrame(), return);
877 QSSG_ASSERT(!data.renderedCameras.isEmpty(), return);
878 QSSG_ASSERT(data.renderedCameras.count() == rhiCtx->mainPassViewCount(), return);
879 layer = &data.layer;
880 QSSG_ASSERT(layer, return);
881
882 rpDesc = rhiCtx->mainRenderPassDescriptor();
883 ps = data.getPipelineState();
884 ps.samples = rhiCtx->mainPassSampleCount();
885 ps.viewCount = rhiCtx->mainPassViewCount();
886 ps.polygonMode = QRhiGraphicsPipeline::Fill;
887
888 const auto &shaderCache = renderer.contextInterface()->shaderCache();
889 skyBoxCubeShader = shaderCache->getBuiltInRhiShaders().getRhiSkyBoxCubeShader(viewCount: rhiCtx->mainPassViewCount());
890
891 RenderHelpers::rhiPrepareSkyBox(rhiCtx: rhiCtx.get(), passKey: this, layer&: *layer, cameras&: data.renderedCameras, renderer);
892}
893
894void SkyboxCubeMapPass::renderPass(QSSGRenderer &renderer)
895{
896 const auto &rhiCtx = renderer.contextInterface()->rhiContext();
897 QSSG_ASSERT(rhiCtx->rhi()->isRecordingFrame(), return);
898 QSSG_ASSERT(layer && skyBoxCubeShader, return);
899
900 QRhiShaderResourceBindings *srb = layer->skyBoxSrb;
901 QSSG_ASSERT(srb, return);
902
903 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderPass);
904 Q_TRACE_SCOPE(QSSG_renderPass, QStringLiteral("Quick3D render skybox"));
905
906 QSSGRhiGraphicsPipelineStatePrivate::setShaderPipeline(ps, pipeline: skyBoxCubeShader.get());
907 renderer.rhiCubeRenderer()->recordRenderCube(rhiCtx: rhiCtx.get(), ps: &ps, srb, rpDesc, flags: { QSSGRhiQuadRenderer::DepthTest | QSSGRhiQuadRenderer::RenderBehind });
908 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DRenderPass, 0, QByteArrayLiteral("skybox_cube"));
909}
910
911void SkyboxCubeMapPass::resetForFrame()
912{
913 ps = {};
914 layer = nullptr;
915}
916
917void Item2DPass::renderPrep(QSSGRenderer &renderer, QSSGLayerRenderData &data)
918{
919 const auto &rhiCtx = renderer.contextInterface()->rhiContext();
920 QSSG_ASSERT(rhiCtx->rhi()->isRecordingFrame(), return);
921 const auto &layer = data.layer;
922
923 ps = data.getPipelineState();
924
925 // objects rendered by Qt Quick 2D
926 ps.flags.setFlag(flag: QSSGRhiGraphicsPipelineState::Flag::BlendEnabled, on: false);
927
928 item2Ds = data.getRenderableItem2Ds();
929 for (const auto &item2D: std::as_const(t&: item2Ds)) {
930 // Set the projection matrix
931 if (!item2D->m_renderer)
932 continue;
933 if (item2D->m_renderer && item2D->m_renderer->currentRhi() != renderer.contextInterface()->rhiContext()->rhi()) {
934 static bool contextWarningShown = false;
935 if (!contextWarningShown) {
936 qWarning () << "Scene with embedded 2D content can only be rendered in one window.";
937 contextWarningShown = true;
938 }
939 continue;
940 }
941
942 auto layerPrepResult = data.layerPrepResult;
943
944 QRhiRenderTarget *renderTarget = rhiCtx->renderTarget();
945 item2D->m_renderer->setDevicePixelRatio(renderTarget->devicePixelRatio());
946 const QRect deviceRect(QPoint(0, 0), renderTarget->pixelSize());
947 const int viewCount = rhiCtx->mainPassViewCount();
948 QSSG_ASSERT(item2D->mvps.count() == viewCount, return);
949 if (layer.scissorRect.isValid()) {
950 QRect effScissor = layer.scissorRect & layerPrepResult.viewport.toRect();
951 QMatrix4x4 correctionMat = correctMVPForScissor(viewportRect: layerPrepResult.viewport,
952 scissorRect: effScissor,
953 isYUp: rhiCtx->rhi()->isYUpInNDC());
954 for (int viewIndex = 0; viewIndex < viewCount; ++viewIndex) {
955 const QMatrix4x4 projectionMatrix = correctionMat * item2D->mvps[viewIndex];
956 item2D->m_renderer->setProjectionMatrix(matrix: projectionMatrix, index: viewIndex);
957 }
958 item2D->m_renderer->setViewportRect(effScissor);
959 } else {
960 for (int viewIndex = 0; viewIndex < viewCount; ++viewIndex)
961 item2D->m_renderer->setProjectionMatrix(matrix: item2D->mvps[viewIndex], index: viewIndex);
962 item2D->m_renderer->setViewportRect(RenderHelpers::correctViewportCoordinates(layerViewport: layerPrepResult.viewport, deviceRect));
963 }
964 item2D->m_renderer->setDeviceRect(deviceRect);
965 QRhiRenderPassDescriptor *oldRp = nullptr;
966 if (item2D->m_rp) {
967 // Changing render target, and so incompatible renderpass
968 // descriptors should be uncommon, but possible.
969 if (!item2D->m_rp->isCompatible(other: rhiCtx->mainRenderPassDescriptor()))
970 std::swap(a&: item2D->m_rp, b&: oldRp);
971 }
972 if (!item2D->m_rp) {
973 // Do not pass our object to the Qt Quick scenegraph. It may
974 // hold on to it, leading to lifetime and ownership issues.
975 // Rather, create a dedicated, compatible object.
976 item2D->m_rp = rhiCtx->mainRenderPassDescriptor()->newCompatibleRenderPassDescriptor();
977 QSSG_CHECK(item2D->m_rp);
978 }
979 QSGRenderTarget sgRt(renderTarget, item2D->m_rp, rhiCtx->commandBuffer());
980 sgRt.multiViewCount = rhiCtx->mainPassViewCount();
981 item2D->m_renderer->setRenderTarget(sgRt);
982 delete oldRp;
983 item2D->m_renderer->prepareSceneInline();
984 }
985}
986
987void Item2DPass::renderPass(QSSGRenderer &renderer)
988{
989 QSSG_ASSERT(!item2Ds.isEmpty(), return);
990
991 const auto &rhiCtx = renderer.contextInterface()->rhiContext();
992 QSSG_ASSERT(rhiCtx->rhi()->isRecordingFrame(), return);
993 QRhiCommandBuffer *cb = rhiCtx->commandBuffer();
994
995 cb->debugMarkBegin(QByteArrayLiteral("Quick3D render 2D sub-scene"));
996 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderPass);
997 Q_TRACE_SCOPE(QSSG_renderPass, QStringLiteral("Quick3D render 2D sub-scene"));
998 for (const auto &item : std::as_const(t&: item2Ds)) {
999 QSSGRenderItem2D *item2D = static_cast<QSSGRenderItem2D *>(item);
1000 if (item2D->m_renderer && item2D->m_renderer->currentRhi() == renderer.contextInterface()->rhiContext()->rhi())
1001 item2D->m_renderer->renderSceneInline();
1002 }
1003 cb->debugMarkEnd();
1004 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DRenderPass, 0, QByteArrayLiteral("2D_sub_scene"));
1005}
1006
1007void Item2DPass::resetForFrame()
1008{
1009 item2Ds.clear();
1010 ps = {};
1011}
1012
1013void InfiniteGridPass::renderPrep(QSSGRenderer &renderer, QSSGLayerRenderData &data)
1014{
1015 const auto &rhiCtx = renderer.contextInterface()->rhiContext();
1016 QSSG_ASSERT(rhiCtx->rhi()->isRecordingFrame(), return);
1017 QSSG_ASSERT(!data.renderedCameras.isEmpty(), return);
1018 QSSG_ASSERT(data.renderedCameras.count() == rhiCtx->mainPassViewCount(), return);
1019 layer = &data.layer;
1020 QSSG_ASSERT(layer, return);
1021
1022 const auto &shaderCache = renderer.contextInterface()->shaderCache();
1023 gridShader = shaderCache->getBuiltInRhiShaders().getRhiGridShader(viewCount: rhiCtx->mainPassViewCount());
1024
1025 ps = data.getPipelineState();
1026 ps.samples = rhiCtx->mainPassSampleCount();
1027 ps.viewCount = rhiCtx->mainPassViewCount();
1028 ps.flags.setFlag(flag: QSSGRhiGraphicsPipelineState::Flag::BlendEnabled, on: true);
1029
1030 RenderHelpers::rhiPrepareGrid(rhiCtx: rhiCtx.get(), passKey: this, layer&: *layer, cameras&: data.renderedCameras, renderer);
1031}
1032
1033void InfiniteGridPass::renderPass(QSSGRenderer &renderer)
1034{
1035 const auto &rhiCtx = renderer.contextInterface()->rhiContext();
1036 QSSG_ASSERT(gridShader && rhiCtx->rhi()->isRecordingFrame(), return);
1037 QRhiCommandBuffer *cb = rhiCtx->commandBuffer();
1038
1039 cb->debugMarkBegin(QByteArrayLiteral("Quick3D render grid"));
1040 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderPass);
1041 Q_TRACE_SCOPE(QSSG_renderPass, QStringLiteral("Quick3D render grid"));
1042 QSSGRhiGraphicsPipelineStatePrivate::setShaderPipeline(ps, pipeline: gridShader.get());
1043 QRhiShaderResourceBindings *srb = layer->gridSrb;
1044 QRhiRenderPassDescriptor *rpDesc = rhiCtx->mainRenderPassDescriptor();
1045 renderer.rhiQuadRenderer()->recordRenderQuad(rhiCtx: rhiCtx.get(), ps: &ps, srb, rpDesc, flags: { QSSGRhiQuadRenderer::DepthTest });
1046 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DRenderPass, 0, QByteArrayLiteral("render_grid"));
1047}
1048
1049void InfiniteGridPass::resetForFrame()
1050{
1051 ps = {};
1052 layer = nullptr;
1053}
1054
1055void DebugDrawPass::renderPrep(QSSGRenderer &renderer, QSSGLayerRenderData &data)
1056{
1057 const auto &rhiCtx = renderer.contextInterface()->rhiContext();
1058 QSSG_ASSERT(rhiCtx->rhi()->isRecordingFrame(), return);
1059 QSSGRhiContextPrivate *rhiCtxD = QSSGRhiContextPrivate::get(q: rhiCtx.get());
1060 QSSG_ASSERT(!data.renderedCameras.isEmpty(), return);
1061 QSSG_ASSERT(data.renderedCameras.count() == rhiCtx->mainPassViewCount(), return);
1062
1063 const auto &shaderCache = renderer.contextInterface()->shaderCache();
1064 debugObjectShader = shaderCache->getBuiltInRhiShaders().getRhiDebugObjectShader();
1065 ps = data.getPipelineState();
1066
1067 // debug objects
1068 const auto &debugDraw = renderer.contextInterface()->debugDrawSystem();
1069 if (debugDraw && debugDraw->hasContent()) {
1070 QRhi *rhi = rhiCtx->rhi();
1071 QRhiResourceUpdateBatch *rub = rhi->nextResourceUpdateBatch();
1072 debugDraw->prepareGeometry(rhiCtx: rhiCtx.get(), rub);
1073 QSSGRhiDrawCallData &dcd = rhiCtxD->drawCallData(key: { .cid: this, .model: nullptr, .entry: nullptr, .entryIdx: 0 });
1074 if (!dcd.ubuf) {
1075 dcd.ubuf = rhi->newBuffer(type: QRhiBuffer::Dynamic, usage: QRhiBuffer::UniformBuffer, size: 64 * data.renderedCameras.count());
1076 dcd.ubuf->create();
1077 }
1078 char *ubufData = dcd.ubuf->beginFullDynamicBufferUpdateForCurrentFrame();
1079 for (qsizetype viewIdx = 0; viewIdx < data.renderedCameras.count(); ++viewIdx) {
1080 QMatrix4x4 viewProjection(Qt::Uninitialized);
1081 data.renderedCameras[viewIdx]->calculateViewProjectionMatrix(outMatrix&: viewProjection);
1082 viewProjection = rhi->clipSpaceCorrMatrix() * viewProjection;
1083 memcpy(dest: ubufData, src: viewProjection.constData() + viewIdx * 64, n: 64);
1084 }
1085 dcd.ubuf->endFullDynamicBufferUpdateForCurrentFrame();
1086
1087 QSSGRhiShaderResourceBindingList bindings;
1088 bindings.addUniformBuffer(binding: 0, stage: QRhiShaderResourceBinding::VertexStage, buf: dcd.ubuf);
1089 dcd.srb = rhiCtxD->srb(bindings);
1090
1091 rhiCtx->commandBuffer()->resourceUpdate(resourceUpdates: rub);
1092 }
1093}
1094
1095void DebugDrawPass::renderPass(QSSGRenderer &renderer)
1096{
1097 const auto &rhiCtx = renderer.contextInterface()->rhiContext();
1098 QSSG_ASSERT(debugObjectShader && rhiCtx->rhi()->isRecordingFrame(), return);
1099 QRhiCommandBuffer *cb = rhiCtx->commandBuffer();
1100 QSSGRhiContextPrivate *rhiCtxD = QSSGRhiContextPrivate::get(q: rhiCtx.get());
1101
1102 const auto &debugDraw = renderer.contextInterface()->debugDrawSystem();
1103 if (debugDraw && debugDraw->hasContent()) {
1104 cb->debugMarkBegin(QByteArrayLiteral("Quick 3D debug objects"));
1105 Q_TRACE_SCOPE(QSSG_renderPass, QStringLiteral("Quick 3D debug objects"));
1106 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderPass);
1107 QSSGRhiGraphicsPipelineStatePrivate::setShaderPipeline(ps, pipeline: debugObjectShader.get());
1108 QSSGRhiDrawCallData &dcd = rhiCtxD->drawCallData(key: { .cid: this, .model: nullptr, .entry: nullptr, .entryIdx: 0 });
1109 QRhiShaderResourceBindings *srb = dcd.srb;
1110 QRhiRenderPassDescriptor *rpDesc = rhiCtx->mainRenderPassDescriptor();
1111 debugDraw->recordRenderDebugObjects(rhiCtx: rhiCtx.get(), ps: &ps, srb, rpDesc);
1112 cb->debugMarkEnd();
1113 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DRenderPass, 0, QByteArrayLiteral("debug_objects"));
1114 }
1115}
1116
1117void DebugDrawPass::resetForFrame()
1118{
1119 ps = {};
1120}
1121
1122void UserPass::renderPrep(QSSGRenderer &renderer, QSSGLayerRenderData &data)
1123{
1124 Q_UNUSED(renderer);
1125 auto &frameData = data.getFrameData();
1126 for (const auto &p : std::as_const(t&: extensions)) {
1127 p->prepareRender(data&: frameData);
1128 if (p->mode() == QSSGRenderExtension::RenderMode::Standalone)
1129 p->render(data&: frameData);
1130 }
1131}
1132
1133void UserPass::renderPass(QSSGRenderer &renderer)
1134{
1135 auto *data = QSSGLayerRenderData::getCurrent(renderer);
1136 QSSG_ASSERT(data, return);
1137 auto &frameData = data->getFrameData();
1138 for (const auto &p : std::as_const(t&: extensions)) {
1139 if (p->mode() == QSSGRenderExtension::RenderMode::Main)
1140 p->render(data&: frameData);
1141 }
1142}
1143
1144void UserPass::resetForFrame()
1145{
1146 for (const auto &p : std::as_const(t&: extensions))
1147 p->resetForFrame();
1148
1149 // TODO: We should track if we need to update this list.
1150 extensions.clear();
1151}
1152
1153QT_END_NAMESPACE
1154

source code of qtquick3d/src/runtimerender/rendererimpl/qssgrenderpass.cpp