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.reflectionProbesView.begin(), data.reflectionProbesView.end() };
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: data.layer.viewCount);
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: data.layer.viewCount);
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, viewCount: data.layer.viewCount);
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
378 if (m_multisampling) {
379 ps.samples = rhiCtx->mainPassSampleCount();
380 rhiDepthTexture = data.getRenderResult(id: QSSGFrameData::RenderResult::DepthTextureMS);
381 } else {
382 ps.samples = 1;
383 rhiDepthTexture = data.getRenderResult(id: QSSGFrameData::RenderResult::DepthTexture);
384 }
385
386 if (Q_LIKELY(rhiDepthTexture && rhiPrepareDepthTexture(rhiCtx.get(), layerPrepResult.textureDimensions(), rhiDepthTexture, data.layer.viewCount, ps.samples))) {
387 sortedOpaqueObjects = data.getSortedOpaqueRenderableObjects(camera: *camera);
388 sortedTransparentObjects = data.getSortedTransparentRenderableObjects(camera: *camera);
389 // the depth texture is always non-MSAA, but is a 2D array with multiview
390 ready = rhiPrepareDepthPass(rhiCtx: rhiCtx.get(), passKey: this, basePipelineState: ps, rpDesc: rhiDepthTexture->rpDesc, inData&: data,
391 sortedOpaqueObjects, sortedTransparentObjects,
392 samples: ps.samples, viewCount: data.layer.viewCount);
393 }
394
395 if (Q_UNLIKELY(!ready))
396 rhiDepthTexture = nullptr;
397}
398
399void DepthMapPass::renderPass(QSSGRenderer &renderer)
400{
401 using namespace RenderHelpers;
402
403 // INPUT: sorted objects (opaque + transparent) (maybe...)
404
405 // DEPENDECY: If this is only used for the AO case, that dictates if this should be done or not.
406
407 // OUTPUT: Texture
408
409 // NOTE: Why are we prepping opaque + transparent object if we're not using them? And why are we staying compatible with 5.15?
410 // Only used for AO? Merge into the AO pass?
411
412 // NOTES:
413 //
414 // 1: If requested, use this and blit it in the z-pre pass.
415 // 2. Why are we handling the transparent objects in the render prep (only)?
416
417 // CONDITION:
418
419 const auto &rhiCtx = renderer.contextInterface()->rhiContext();
420 QSSG_ASSERT(rhiCtx->rhi()->isRecordingFrame(), return);
421 QRhiCommandBuffer *cb = rhiCtx->commandBuffer();
422 cb->debugMarkBegin(QByteArrayLiteral("Quick3D depth texture"));
423
424 if (Q_LIKELY(rhiDepthTexture && rhiDepthTexture->isValid())) {
425 bool needsSetViewport = true;
426 cb->beginPass(rt: rhiDepthTexture->rt, colorClearValue: Qt::transparent, depthStencilClearValue: { 1.0f, 0 }, resourceUpdates: nullptr, flags: rhiCtx->commonPassFlags());
427 QSSGRHICTX_STAT(rhiCtx, beginRenderPass(rhiDepthTexture->rt));
428 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderPass);
429 // NB! We do not pass sortedTransparentObjects in the 4th
430 // argument to stay compatible with the 5.15 code base,
431 // which also does not include semi-transparent objects in
432 // the depth texture. In addition, capturing after the
433 // opaque pass, not including transparent objects, is part
434 // of the contract for screen reading custom materials,
435 // both for depth and color.
436 rhiRenderDepthPass(rhiCtx: rhiCtx.get(), ps, sortedOpaqueObjects, sortedTransparentObjects: {}, needsSetViewport: &needsSetViewport);
437 cb->endPass();
438 QSSGRHICTX_STAT(rhiCtx, endRenderPass());
439 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DRenderPass, 0, QByteArrayLiteral("depth_texture"));
440 }
441
442 cb->debugMarkEnd();
443}
444
445void DepthMapPass::resetForFrame()
446{
447 rhiDepthTexture = nullptr;
448 sortedOpaqueObjects.clear();
449 sortedTransparentObjects.clear();
450 ps = {};
451}
452
453// SCREEN TEXTURE PASS
454
455void ScreenMapPass::renderPrep(QSSGRenderer &renderer, QSSGLayerRenderData &data)
456{
457 using namespace RenderHelpers;
458
459 QSSG_ASSERT(!data.renderedCameras.isEmpty(), return);
460 QSSGRenderCamera *camera = data.renderedCameras[0];
461
462 const auto &rhiCtx = renderer.contextInterface()->rhiContext();
463 QSSG_ASSERT(rhiCtx->rhi()->isRecordingFrame(), return);
464 rhiScreenTexture = data.getRenderResult(id: QSSGFrameData::RenderResult::ScreenTexture);
465 auto &layer = data.layer;
466 const auto &layerPrepResult = data.layerPrepResult;
467 wantsMips = layerPrepResult.flags.requiresMipmapsForScreenTexture();
468 sortedOpaqueObjects = data.getSortedOpaqueRenderableObjects(camera: *camera);
469 ps = data.getPipelineState();
470 ps.samples = 1; // screen texture is always non-MSAA
471 ps.viewCount = data.layer.viewCount; // but is a 2D texture array when multiview
472
473 if (layer.background == QSSGRenderLayer::Background::Color)
474 clearColor = QColor::fromRgbF(r: layer.clearColor.x(), g: layer.clearColor.y(), b: layer.clearColor.z());
475
476 if (rhiCtx->rhi()->isFeatureSupported(feature: QRhi::TexelFetch)) {
477 if (layer.background == QSSGRenderLayer::Background::SkyBoxCubeMap && layer.skyBoxCubeMap) {
478 if (!skyboxCubeMapPass)
479 skyboxCubeMapPass = SkyboxCubeMapPass();
480
481 skyboxCubeMapPass->skipTonemapping = true;
482 skyboxCubeMapPass->renderPrep(renderer, data);
483
484 // The pass expects to output to the main render target, but we have
485 // our own texture here, possibly with a differing sample count, so
486 // override the relevant settings once renderPrep() is done.
487 skyboxCubeMapPass->ps.samples = ps.samples;
488
489 skyboxPass = std::nullopt;
490 } else if (layer.background == QSSGRenderLayer::Background::SkyBox && layer.lightProbe) {
491 if (!skyboxPass)
492 skyboxPass = SkyboxPass();
493
494 skyboxPass->skipTonemapping = true;
495 skyboxPass->renderPrep(renderer, data);
496
497 skyboxPass->ps.samples = ps.samples;
498
499 skyboxCubeMapPass = std::nullopt;
500 }
501 }
502
503 bool ready = false;
504 if (Q_LIKELY(rhiScreenTexture && rhiPrepareScreenTexture(rhiCtx.get(), layerPrepResult.textureDimensions(), wantsMips, rhiScreenTexture, layer.viewCount))) {
505 ready = true;
506 if (skyboxCubeMapPass)
507 skyboxCubeMapPass->rpDesc = rhiScreenTexture->rpDesc;
508 if (skyboxPass)
509 skyboxPass->rpDesc = rhiScreenTexture->rpDesc;
510 // NB: not compatible with disabling LayerEnableDepthTest
511 // because there are effectively no "opaque" objects then.
512 // Disable Tonemapping for all materials in the screen pass texture
513 shaderFeatures = data.getShaderFeatures();
514 shaderFeatures.disableTonemapping();
515 const auto &sortedOpaqueObjects = data.getSortedOpaqueRenderableObjects(camera: *camera);
516 for (const auto &handle : sortedOpaqueObjects) {
517 // Reflection cube maps are not available at this point, make sure they are turned off.
518 bool recRef = handle.obj->renderableFlags.receivesReflections();
519 handle.obj->renderableFlags.setReceivesReflections(false);
520 rhiPrepareRenderable(rhiCtx: rhiCtx.get(), passKey: this, inData: data, inObject&: *handle.obj, renderPassDescriptor: rhiScreenTexture->rpDesc, ps: &ps, featureSet: shaderFeatures, samples: 1, viewCount: data.layer.viewCount);
521 handle.obj->renderableFlags.setReceivesReflections(recRef);
522 }
523 }
524
525 if (Q_UNLIKELY(!ready))
526 rhiScreenTexture = nullptr;
527}
528
529void ScreenMapPass::renderPass(QSSGRenderer &renderer)
530{
531 using namespace RenderHelpers;
532
533 // INPUT: Sorted opaque objects + depth objects
534
535 // DEPENDECY: Depth pass (if enabled)
536
537 // OUTPUT: Texture (screen texture).
538
539 // NOTE: Used for refrection and effects (?)
540
541 // CONDITION:
542
543 const auto &rhiCtx = renderer.contextInterface()->rhiContext();
544 QSSG_ASSERT(rhiCtx->rhi()->isRecordingFrame(), return);
545 QRhiCommandBuffer *cb = rhiCtx->commandBuffer();
546
547 cb->debugMarkBegin(QByteArrayLiteral("Quick3D screen texture"));
548
549 if (Q_LIKELY(rhiScreenTexture && rhiScreenTexture->isValid())) {
550 cb->beginPass(rt: rhiScreenTexture->rt, colorClearValue: clearColor, depthStencilClearValue: { 1.0f, 0 }, resourceUpdates: nullptr, flags: rhiCtx->commonPassFlags());
551 QSSGRHICTX_STAT(rhiCtx, beginRenderPass(rhiScreenTexture->rt));
552 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderPass);
553
554 bool needsSetViewport = true;
555 for (const auto &handle : std::as_const(t&: sortedOpaqueObjects))
556 rhiRenderRenderable(rhiCtx: rhiCtx.get(), state: ps, object&: *handle.obj, needsSetViewport: &needsSetViewport);
557
558 if (skyboxCubeMapPass)
559 skyboxCubeMapPass->renderPass(renderer);
560 else if (skyboxPass)
561 skyboxPass->renderPass(renderer);
562
563 QRhiResourceUpdateBatch *rub = nullptr;
564 if (wantsMips) {
565 rub = rhiCtx->rhi()->nextResourceUpdateBatch();
566 rub->generateMips(tex: rhiScreenTexture->texture);
567 }
568 cb->endPass(resourceUpdates: rub);
569 QSSGRHICTX_STAT(rhiCtx, endRenderPass());
570 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DRenderPass, 0, QByteArrayLiteral("screen_texture"));
571 }
572
573 cb->debugMarkEnd();
574}
575
576void ScreenMapPass::resetForFrame()
577{
578 rhiScreenTexture = nullptr;
579 if (skyboxPass)
580 skyboxPass->resetForFrame();
581 if (skyboxCubeMapPass)
582 skyboxCubeMapPass->resetForFrame();
583 ps = {};
584 wantsMips = false;
585 clearColor = Qt::transparent;
586 shaderFeatures = {};
587 sortedOpaqueObjects.clear();
588}
589
590void ScreenReflectionPass::renderPrep(QSSGRenderer &renderer, QSSGLayerRenderData &data)
591{
592 QSSG_ASSERT(!data.renderedCameras.isEmpty(), return);
593 QSSGRenderCamera *camera = data.renderedCameras[0];
594
595 const auto &rhiCtx = renderer.contextInterface()->rhiContext();
596 QSSG_ASSERT(rhiCtx->rhi()->isRecordingFrame(), return);
597 rhiScreenTexture = data.getRenderResult(id: QSSGFrameData::RenderResult::ScreenTexture);
598 QSSG_ASSERT_X(rhiScreenTexture && rhiScreenTexture->isValid(), "Invalid screen texture!", return);
599
600 const auto &layer = data.layer;
601 const auto shaderFeatures = data.getShaderFeatures();
602 const bool layerEnableDepthTest = layer.layerFlags.testFlag(flag: QSSGRenderLayer::LayerFlag::EnableDepthTest);
603
604 QRhiRenderPassDescriptor *mainRpDesc = rhiCtx->mainRenderPassDescriptor();
605 const int samples = rhiCtx->mainPassSampleCount();
606 const int viewCount = data.layer.viewCount;
607
608 // NOTE: We're piggybacking on the screen map pass for now, but we could do better.
609 ps = data.getPipelineState();
610 const bool depthTestEnabled = (data.screenMapPass.ps.flags.testFlag(flag: QSSGRhiGraphicsPipelineState::Flag::DepthTestEnabled));
611 ps.flags.setFlag(flag: QSSGRhiGraphicsPipelineState::Flag::DepthTestEnabled, on: depthTestEnabled);
612 const bool depthWriteEnabled = (data.screenMapPass.ps.flags.testFlag(flag: QSSGRhiGraphicsPipelineState::Flag::DepthWriteEnabled));
613 ps.flags.setFlag(flag: QSSGRhiGraphicsPipelineState::Flag::DepthWriteEnabled, on: depthWriteEnabled);
614 sortedScreenTextureObjects = data.getSortedScreenTextureRenderableObjects(camera: *camera);
615 for (const auto &handle : std::as_const(t&: sortedScreenTextureObjects)) {
616 QSSGRenderableObject *theObject = handle.obj;
617 const auto depthWriteMode = theObject->depthWriteMode;
618 ps.flags.setFlag(flag: QSSGRhiGraphicsPipelineState::Flag::BlendEnabled, on: theObject->renderableFlags.hasTransparency());
619 const bool curDepthWriteEnabled = !(depthWriteMode == QSSGDepthDrawMode::Never || depthWriteMode == QSSGDepthDrawMode::OpaquePrePass
620 || data.isZPrePassActive() || !layerEnableDepthTest);
621 ps.flags.setFlag(flag: QSSGRhiGraphicsPipelineState::Flag::DepthWriteEnabled, on: curDepthWriteEnabled);
622 RenderHelpers::rhiPrepareRenderable(rhiCtx: rhiCtx.get(), passKey: this, inData: data, inObject&: *theObject, renderPassDescriptor: mainRpDesc, ps: &ps, featureSet: shaderFeatures, samples, viewCount);
623 }
624}
625
626void ScreenReflectionPass::renderPass(QSSGRenderer &renderer)
627{
628 if (QSSG_GUARD(rhiScreenTexture && rhiScreenTexture->isValid())) {
629 const auto &rhiCtx = renderer.contextInterface()->rhiContext();
630 QSSG_ASSERT(rhiCtx->rhi()->isRecordingFrame(), return);
631 QRhiCommandBuffer *cb = rhiCtx->commandBuffer();
632
633 // 3. Screen texture depended objects
634 cb->debugMarkBegin(QByteArrayLiteral("Quick3D render screen texture dependent"));
635 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderPass);
636 Q_TRACE(QSSG_renderPass_entry, QStringLiteral("Quick3D render screen texture dependent"));
637 bool needsSetViewport = true;
638 for (const auto &handle : std::as_const(t&: sortedScreenTextureObjects)) {
639 QSSGRenderableObject *theObject = handle.obj;
640 RenderHelpers::rhiRenderRenderable(rhiCtx: rhiCtx.get(), state: ps, object&: *theObject, needsSetViewport: &needsSetViewport);
641 }
642 cb->debugMarkEnd();
643 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DRenderPass, 0, QByteArrayLiteral("screen_texture_dependent"));
644 Q_TRACE(QSSG_renderPass_exit);
645 }
646}
647
648void ScreenReflectionPass::resetForFrame()
649{
650 sortedScreenTextureObjects.clear();
651 rhiScreenTexture = nullptr;
652 ps = {};
653}
654
655void OpaquePass::prep(const QSSGRenderContextInterface &ctx,
656 QSSGLayerRenderData &data,
657 QSSGPassKey passKey,
658 QSSGRhiGraphicsPipelineState &ps,
659 QSSGShaderFeatures shaderFeatures,
660 QRhiRenderPassDescriptor *rpDesc,
661 const QSSGRenderableObjectList &sortedOpaqueObjects)
662{
663 const auto &rhiCtx = ctx.rhiContext();
664 QSSG_ASSERT(rpDesc && rhiCtx->rhi()->isRecordingFrame(), return);
665
666 const auto &layer = data.layer;
667 const bool layerEnableDepthTest = layer.layerFlags.testFlag(flag: QSSGRenderLayer::LayerFlag::EnableDepthTest);
668
669 for (const auto &handle : std::as_const(t: sortedOpaqueObjects)) {
670 QSSGRenderableObject *theObject = handle.obj;
671 const auto depthWriteMode = theObject->depthWriteMode;
672 const bool curDepthWriteEnabled = !(depthWriteMode == QSSGDepthDrawMode::Never ||
673 depthWriteMode == QSSGDepthDrawMode::OpaquePrePass ||
674 data.isZPrePassActive() || !layerEnableDepthTest);
675 ps.flags.setFlag(flag: QSSGRhiGraphicsPipelineState::Flag::DepthWriteEnabled, on: curDepthWriteEnabled);
676 RenderHelpers::rhiPrepareRenderable(rhiCtx: rhiCtx.get(), passKey, inData: data, inObject&: *theObject, renderPassDescriptor: rpDesc, ps: &ps, featureSet: shaderFeatures, samples: ps.samples, viewCount: ps.viewCount);
677 }
678}
679
680void OpaquePass::render(const QSSGRenderContextInterface &ctx,
681 const QSSGRhiGraphicsPipelineState &ps,
682 const QSSGRenderableObjectList &sortedOpaqueObjects)
683{
684 const auto &rhiCtx = ctx.rhiContext();
685 QSSG_ASSERT(rhiCtx->rhi()->isRecordingFrame(), return);
686 bool needsSetViewport = true;
687 for (const auto &handle : std::as_const(t: sortedOpaqueObjects)) {
688 QSSGRenderableObject *theObject = handle.obj;
689 RenderHelpers::rhiRenderRenderable(rhiCtx: rhiCtx.get(), state: ps, object&: *theObject, needsSetViewport: &needsSetViewport);
690 }
691}
692
693void OpaquePass::renderPrep(QSSGRenderer &renderer, QSSGLayerRenderData &data)
694{
695 auto *ctx = renderer.contextInterface();
696 const auto &rhiCtx = ctx->rhiContext();
697 QSSG_ASSERT(rhiCtx->rhi()->isRecordingFrame(), return);
698 QSSG_ASSERT(!data.renderedCameras.isEmpty() && data.renderedCameraData.has_value() , return);
699 QSSGRenderCamera *camera = data.renderedCameras[0];
700
701 ps = data.getPipelineState();
702 ps.samples = rhiCtx->mainPassSampleCount();
703 ps.viewCount = data.layer.viewCount;
704 ps.depthFunc = QRhiGraphicsPipeline::LessOrEqual;
705 ps.flags.setFlag(flag: QSSGRhiGraphicsPipelineState::Flag::BlendEnabled, on: false);
706
707 // opaque objects (or, this list is empty when LayerEnableDepthTest is disabled)
708 sortedOpaqueObjects = data.getSortedOpaqueRenderableObjects(camera: *camera);
709 shaderFeatures = data.getShaderFeatures();
710
711 QRhiRenderPassDescriptor *mainRpDesc = rhiCtx->mainRenderPassDescriptor();
712 prep(ctx: *ctx, data, passKey: this, ps, shaderFeatures, rpDesc: mainRpDesc, sortedOpaqueObjects);
713}
714
715void OpaquePass::renderPass(QSSGRenderer &renderer)
716{
717 auto *ctx = renderer.contextInterface();
718 const auto &rhiCtx = ctx->rhiContext();
719 QSSG_ASSERT(rhiCtx->rhi()->isRecordingFrame(), return);
720 QRhiCommandBuffer *cb = rhiCtx->commandBuffer();
721
722 cb->debugMarkBegin(QByteArrayLiteral("Quick3D render opaque"));
723 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderPass);
724 Q_TRACE(QSSG_renderPass_entry, QStringLiteral("Quick3D render opaque"));
725 render(ctx: *ctx, ps, sortedOpaqueObjects);
726 cb->debugMarkEnd();
727 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DRenderPass, 0, QByteArrayLiteral("opaque_pass"));
728 Q_TRACE(QSSG_renderPass_exit);
729}
730
731void OpaquePass::resetForFrame()
732{
733 sortedOpaqueObjects.clear();
734 ps = {};
735 shaderFeatures = {};
736}
737
738void TransparentPass::prep(const QSSGRenderContextInterface &ctx,
739 QSSGLayerRenderData &data,
740 QSSGPassKey passKey,
741 QSSGRhiGraphicsPipelineState &ps,
742 QSSGShaderFeatures shaderFeatures,
743 QRhiRenderPassDescriptor *rpDesc,
744 const QSSGRenderableObjectList &sortedTransparentObjects,
745 bool oit)
746{
747 const auto &rhiCtx = ctx.rhiContext();
748 QSSG_ASSERT(rpDesc && rhiCtx->rhi()->isRecordingFrame(), return);
749
750 const bool zPrePassActive = data.isZPrePassActive();
751 for (const auto &handle : std::as_const(t: sortedTransparentObjects)) {
752 QSSGRenderableObject *theObject = handle.obj;
753 const auto depthWriteMode = theObject->depthWriteMode;
754 const bool curDepthWriteEnabled = (depthWriteMode == QSSGDepthDrawMode::Always && !zPrePassActive);
755 ps.flags.setFlag(flag: QSSGRhiGraphicsPipelineState::Flag::DepthWriteEnabled, on: curDepthWriteEnabled);
756 if (!(theObject->renderableFlags.isCompletelyTransparent())) {
757 RenderHelpers::rhiPrepareRenderable(rhiCtx: rhiCtx.get(), passKey, inData: data, inObject&: *theObject, renderPassDescriptor: rpDesc, ps: &ps, featureSet: shaderFeatures,
758 samples: ps.samples, viewCount: ps.viewCount, alteredCamera: nullptr, alteredModelViewProjection: nullptr, cubeFace: QSSGRenderTextureCubeFaceNone, entry: nullptr, oit);
759 }
760 }
761}
762
763void TransparentPass::render(const QSSGRenderContextInterface &ctx,
764 const QSSGRhiGraphicsPipelineState &ps,
765 const QSSGRenderableObjectList &sortedTransparentObjects)
766{
767 const auto &rhiCtx = ctx.rhiContext();
768 QSSG_ASSERT(rhiCtx->rhi()->isRecordingFrame(), return);
769 // If scissorRect is set, Item2Ds will be drawn by a workaround of modifying
770 // viewport, not using actual 3D scissor test.
771 // It means non-opaque objects may be affected by this viewport setting.
772 bool needsSetViewport = true;
773 for (const auto &handle : std::as_const(t: sortedTransparentObjects)) {
774 QSSGRenderableObject *theObject = handle.obj;
775 if (!theObject->renderableFlags.isCompletelyTransparent())
776 RenderHelpers::rhiRenderRenderable(rhiCtx: rhiCtx.get(), state: ps, object&: *theObject, needsSetViewport: &needsSetViewport);
777 }
778}
779
780void TransparentPass::renderPrep(QSSGRenderer &renderer, QSSGLayerRenderData &data)
781{
782 auto *ctx = renderer.contextInterface();
783 const auto &rhiCtx = ctx->rhiContext();
784
785 QSSG_ASSERT(!data.renderedCameras.isEmpty() && data.renderedCameraData.has_value() , return);
786 QSSGRenderCamera *camera = data.renderedCameras[0];
787
788 QRhiRenderPassDescriptor *mainRpDesc = rhiCtx->mainRenderPassDescriptor();
789
790 ps = data.getPipelineState();
791 ps.samples = rhiCtx->mainPassSampleCount();
792 ps.viewCount = data.layer.viewCount;
793
794 // transparent objects (or, without LayerEnableDepthTest, all objects)
795 ps.flags.setFlag(flag: QSSGRhiGraphicsPipelineState::Flag::BlendEnabled, on: true);
796 ps.flags.setFlag(flag: QSSGRhiGraphicsPipelineState::Flag::DepthWriteEnabled, on: false);
797
798 shaderFeatures = data.getShaderFeatures();
799 sortedTransparentObjects = data.getSortedTransparentRenderableObjects(camera: *camera);
800
801 prep(ctx: *ctx, data, passKey: this, ps, shaderFeatures, rpDesc: mainRpDesc, sortedTransparentObjects);
802}
803
804void TransparentPass::renderPass(QSSGRenderer &renderer)
805{
806 auto *ctx = renderer.contextInterface();
807 const auto &rhiCtx = ctx->rhiContext();
808 QSSG_ASSERT(rhiCtx->rhi()->isRecordingFrame(), return);
809 QRhiCommandBuffer *cb = rhiCtx->commandBuffer();
810
811 cb->debugMarkBegin(QByteArrayLiteral("Quick3D render alpha"));
812 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderPass);
813 Q_TRACE(QSSG_renderPass_entry, QStringLiteral("Quick3D render alpha"));
814 render(ctx: *ctx, ps, sortedTransparentObjects);
815 cb->debugMarkEnd();
816 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DRenderPass, 0, QByteArrayLiteral("transparent_pass"));
817 Q_TRACE(QSSG_renderPass_exit);
818}
819
820void TransparentPass::resetForFrame()
821{
822 sortedTransparentObjects.clear();
823 ps = {};
824 shaderFeatures = {};
825}
826
827void SkyboxPass::renderPrep(QSSGRenderer &renderer, QSSGLayerRenderData &data)
828{
829 if (!skipPrep) {
830 const auto &rhiCtx = renderer.contextInterface()->rhiContext();
831 QSSG_ASSERT(rhiCtx->rhi()->isRecordingFrame(), return);
832 QSSG_ASSERT(!data.renderedCameras.isEmpty(), return);
833 QSSG_ASSERT(data.renderedCameras.count() == data.layer.viewCount, return);
834 layer = &data.layer;
835 QSSG_ASSERT(layer, return);
836
837 rpDesc = rhiCtx->mainRenderPassDescriptor();
838 ps = data.getPipelineState();
839 ps.samples = rhiCtx->mainPassSampleCount();
840 ps.viewCount = data.layer.viewCount;
841 ps.polygonMode = QRhiGraphicsPipeline::Fill;
842
843 // When there are effects, then it is up to the last pass of the
844 // last effect to perform tonemapping, neither the skybox nor the
845 // main render pass should alter the colors then.
846 skipTonemapping = layer->firstEffect != nullptr;
847
848 RenderHelpers::rhiPrepareSkyBox(rhiCtx: rhiCtx.get(), passKey: this, layer&: *layer, cameras&: data.renderedCameras, renderer);
849 skipPrep = true;
850 }
851}
852
853void SkyboxPass::renderPass(QSSGRenderer &renderer)
854{
855 const auto &rhiCtx = renderer.contextInterface()->rhiContext();
856 QSSG_ASSERT(rhiCtx->rhi()->isRecordingFrame(), return);
857 QSSG_ASSERT(layer, return);
858
859 QRhiShaderResourceBindings *srb = layer->skyBoxSrb;
860 QSSG_ASSERT(srb, return);
861
862 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderPass);
863 Q_TRACE_SCOPE(QSSG_renderPass, QStringLiteral("Quick3D render skybox"));
864
865 // Note: We get the shader here, as the screen map pass might modify the state of
866 // the tonemap mode.
867
868 QSSGRenderLayer::TonemapMode tonemapMode = skipTonemapping && (layer->tonemapMode != QSSGRenderLayer::TonemapMode::Custom) ? QSSGRenderLayer::TonemapMode::None : layer->tonemapMode;
869 const auto &shaderCache = renderer.contextInterface()->shaderCache();
870 auto shaderPipeline = shaderCache->getBuiltInRhiShaders().getRhiSkyBoxShader(tonemapMode, isRGBE: layer->skyBoxIsRgbe8, viewCount: layer->viewCount);
871 QSSG_CHECK(shaderPipeline);
872 QSSGRhiGraphicsPipelineStatePrivate::setShaderPipeline(ps, pipeline: shaderPipeline.get());
873 renderer.rhiQuadRenderer()->recordRenderQuad(rhiCtx: rhiCtx.get(), ps: &ps, srb, rpDesc, flags: { QSSGRhiQuadRenderer::DepthTest | QSSGRhiQuadRenderer::RenderBehind });
874 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DRenderPass, 0, QByteArrayLiteral("skybox_map"));
875}
876
877void SkyboxPass::resetForFrame()
878{
879 ps = {};
880 layer = nullptr;
881 skipPrep = false;
882}
883
884void SkyboxCubeMapPass::renderPrep(QSSGRenderer &renderer, QSSGLayerRenderData &data)
885{
886 const auto &rhiCtx = renderer.contextInterface()->rhiContext();
887 QSSG_ASSERT(rhiCtx->rhi()->isRecordingFrame(), return);
888 QSSG_ASSERT(!data.renderedCameras.isEmpty(), return);
889 QSSG_ASSERT(data.renderedCameras.count() == data.layer.viewCount, return);
890 layer = &data.layer;
891 QSSG_ASSERT(layer, return);
892
893 rpDesc = rhiCtx->mainRenderPassDescriptor();
894 ps = data.getPipelineState();
895 ps.samples = rhiCtx->mainPassSampleCount();
896 ps.viewCount = data.layer.viewCount;
897 ps.polygonMode = QRhiGraphicsPipeline::Fill;
898
899 const auto &shaderCache = renderer.contextInterface()->shaderCache();
900 skyBoxCubeShader = shaderCache->getBuiltInRhiShaders().getRhiSkyBoxCubeShader(viewCount: data.layer.viewCount);
901
902 RenderHelpers::rhiPrepareSkyBox(rhiCtx: rhiCtx.get(), passKey: this, layer&: *layer, cameras&: data.renderedCameras, renderer);
903}
904
905void SkyboxCubeMapPass::renderPass(QSSGRenderer &renderer)
906{
907 const auto &rhiCtx = renderer.contextInterface()->rhiContext();
908 QSSG_ASSERT(rhiCtx->rhi()->isRecordingFrame(), return);
909 QSSG_ASSERT(layer && skyBoxCubeShader, return);
910
911 QRhiShaderResourceBindings *srb = layer->skyBoxSrb;
912 QSSG_ASSERT(srb, return);
913
914 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderPass);
915 Q_TRACE_SCOPE(QSSG_renderPass, QStringLiteral("Quick3D render skybox"));
916
917 QSSGRhiGraphicsPipelineStatePrivate::setShaderPipeline(ps, pipeline: skyBoxCubeShader.get());
918 renderer.rhiCubeRenderer()->recordRenderCube(rhiCtx: rhiCtx.get(), ps: &ps, srb, rpDesc, flags: { QSSGRhiQuadRenderer::DepthTest | QSSGRhiQuadRenderer::RenderBehind });
919 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DRenderPass, 0, QByteArrayLiteral("skybox_cube"));
920}
921
922void SkyboxCubeMapPass::resetForFrame()
923{
924 ps = {};
925 layer = nullptr;
926}
927
928void Item2DPass::renderPrep(QSSGRenderer &renderer, QSSGLayerRenderData &data)
929{
930 const auto &rhiCtx = renderer.contextInterface()->rhiContext();
931 QSSG_ASSERT(rhiCtx->rhi()->isRecordingFrame(), return);
932 const auto &layer = data.layer;
933
934 ps = data.getPipelineState();
935
936 // objects rendered by Qt Quick 2D
937 ps.flags.setFlag(flag: QSSGRhiGraphicsPipelineState::Flag::BlendEnabled, on: false);
938
939 item2Ds = data.getRenderableItem2Ds();
940 item2DDataMap.reserve(n: size_t(item2Ds.size()));
941 prepdItem2DRenderers.reserve(n: size_t(item2Ds.size()));
942 renderer.populateItem2DDataMapForLayer(layer: data.layer, item2DDataMap);
943 // NOTE: This marks the start of the 2D sub-scene rendering as it might result in
944 // a nested 3D scene to be rendered and if we don't save the state here, we can
945 // end up with a mismatched state in the QtQuick3D renderer.
946 // See the end of this function for the corresponding end call (endSubLayerRender()).
947 renderer.beginSubLayerRender(inLayer&: data);
948 for (const auto &item2D: std::as_const(t&: item2Ds)) {
949 // Find data for item
950 auto item2DData = getItem2DData(item2D);
951 const auto &mvps = item2DData.mvps;
952 QSGRenderer *renderer2d = item2DData.renderer;
953 QRhiRenderPassDescriptor *rpd = item2DData.rpd;
954
955 // NOTE: We shouldn't get into this state...
956 if (renderer2d && renderer2d->currentRhi() != rhiCtx->rhi()) {
957 static bool contextWarningShown = false;
958 if (!contextWarningShown) {
959 qWarning () << "Scene with embedded 2D content can only be rendered in one window.";
960 contextWarningShown = true;
961 }
962 continue;
963 }
964
965 // Set the projection matrix
966
967 auto layerPrepResult = data.layerPrepResult;
968
969 QRhiRenderTarget *renderTarget = rhiCtx->renderTarget();
970 renderer2d->setDevicePixelRatio(renderTarget->devicePixelRatio());
971 const QRect deviceRect(QPoint(0, 0), renderTarget->pixelSize());
972 const int viewCount = data.layer.viewCount;
973 if (layer.scissorRect.isValid()) {
974 QRect effScissor = layer.scissorRect & layerPrepResult.viewport.toRect();
975 QMatrix4x4 correctionMat = correctMVPForScissor(viewportRect: layerPrepResult.viewport,
976 scissorRect: effScissor,
977 isYUp: rhiCtx->rhi()->isYUpInNDC());
978 for (int viewIndex = 0; viewIndex < viewCount; ++viewIndex) {
979 const QMatrix4x4 projectionMatrix = correctionMat * mvps[viewIndex];
980 renderer2d->setProjectionMatrix(matrix: projectionMatrix, index: viewIndex);
981 }
982 renderer2d->setViewportRect(effScissor);
983 } else {
984 for (int viewIndex = 0; viewIndex < viewCount; ++viewIndex)
985 renderer2d->setProjectionMatrix(matrix: mvps[viewIndex], index: viewIndex);
986 renderer2d->setViewportRect(RenderHelpers::correctViewportCoordinates(layerViewport: layerPrepResult.viewport, deviceRect));
987 }
988 renderer2d->setDeviceRect(deviceRect);
989 QSGRenderTarget sgRt(renderTarget, rpd, rhiCtx->commandBuffer());
990 sgRt.multiViewCount = data.layer.viewCount;
991 renderer2d->setRenderTarget(sgRt);
992 renderer2d->prepareSceneInline();
993 prepdItem2DRenderers.push_back(x: renderer2d);
994 }
995 renderer.endSubLayerRender(inLayer&: data);
996}
997
998void Item2DPass::renderPass(QSSGRenderer &renderer)
999{
1000 QSSG_ASSERT(!item2Ds.isEmpty(), return);
1001
1002 const auto &rhiCtx = renderer.contextInterface()->rhiContext();
1003 QSSG_ASSERT(rhiCtx->rhi()->isRecordingFrame(), return);
1004 QRhiCommandBuffer *cb = rhiCtx->commandBuffer();
1005
1006 cb->debugMarkBegin(QByteArrayLiteral("Quick3D render 2D sub-scene"));
1007 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderPass);
1008 Q_TRACE_SCOPE(QSSG_renderPass, QStringLiteral("Quick3D render 2D sub-scene"));
1009 QSSGLayerRenderData *data = QSSGLayerRenderData::getCurrent(renderer);
1010 renderer.beginSubLayerRender(inLayer&: *data);
1011 for (QSGRenderer *renderer2d : std::as_const(t&: prepdItem2DRenderers))
1012 renderer2d->renderSceneInline();
1013 renderer.endSubLayerRender(inLayer&: *data);
1014 cb->debugMarkEnd();
1015 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DRenderPass, 0, QByteArrayLiteral("2D_sub_scene"));
1016}
1017
1018void Item2DPass::resetForFrame()
1019{
1020 item2Ds.clear();
1021 item2DDataMap.clear();
1022 prepdItem2DRenderers.clear();
1023 ps = {};
1024}
1025
1026QSSGRenderer::Item2DData Item2DPass::getItem2DData(QSSGRenderItem2D *item2D)
1027{
1028 const auto foundIt = item2DDataMap.find(x: item2D);
1029 return (foundIt != item2DDataMap.cend()) ? foundIt->second : QSSGRenderer::Item2DData{};
1030}
1031
1032void InfiniteGridPass::renderPrep(QSSGRenderer &renderer, QSSGLayerRenderData &data)
1033{
1034 const auto &rhiCtx = renderer.contextInterface()->rhiContext();
1035 QSSG_ASSERT(rhiCtx->rhi()->isRecordingFrame(), return);
1036 QSSG_ASSERT(!data.renderedCameras.isEmpty(), return);
1037 QSSG_ASSERT(data.renderedCameras.count() == data.layer.viewCount, return);
1038 layer = &data.layer;
1039 QSSG_ASSERT(layer, return);
1040
1041 const auto &shaderCache = renderer.contextInterface()->shaderCache();
1042 gridShader = shaderCache->getBuiltInRhiShaders().getRhiGridShader(viewCount: data.layer.viewCount);
1043
1044 ps = data.getPipelineState();
1045 ps.samples = rhiCtx->mainPassSampleCount();
1046 ps.viewCount = data.layer.viewCount;
1047 ps.flags.setFlag(flag: QSSGRhiGraphicsPipelineState::Flag::BlendEnabled, on: true);
1048 ps.polygonMode = QRhiGraphicsPipeline::Fill;
1049
1050 RenderHelpers::rhiPrepareGrid(rhiCtx: rhiCtx.get(), passKey: this, layer&: *layer, cameras&: data.renderedCameras, renderer);
1051}
1052
1053void InfiniteGridPass::renderPass(QSSGRenderer &renderer)
1054{
1055 const auto &rhiCtx = renderer.contextInterface()->rhiContext();
1056 QSSG_ASSERT(gridShader && rhiCtx->rhi()->isRecordingFrame(), return);
1057 QRhiCommandBuffer *cb = rhiCtx->commandBuffer();
1058
1059 cb->debugMarkBegin(QByteArrayLiteral("Quick3D render grid"));
1060 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderPass);
1061 Q_TRACE_SCOPE(QSSG_renderPass, QStringLiteral("Quick3D render grid"));
1062 QSSGRhiGraphicsPipelineStatePrivate::setShaderPipeline(ps, pipeline: gridShader.get());
1063 QRhiShaderResourceBindings *srb = layer->gridSrb;
1064 QRhiRenderPassDescriptor *rpDesc = rhiCtx->mainRenderPassDescriptor();
1065 renderer.rhiQuadRenderer()->recordRenderQuad(rhiCtx: rhiCtx.get(), ps: &ps, srb, rpDesc, flags: { QSSGRhiQuadRenderer::DepthTest });
1066 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DRenderPass, 0, QByteArrayLiteral("render_grid"));
1067}
1068
1069void InfiniteGridPass::resetForFrame()
1070{
1071 ps = {};
1072 layer = nullptr;
1073}
1074
1075void DebugDrawPass::renderPrep(QSSGRenderer &renderer, QSSGLayerRenderData &data)
1076{
1077 const auto &rhiCtx = renderer.contextInterface()->rhiContext();
1078 QSSG_ASSERT(rhiCtx->rhi()->isRecordingFrame(), return);
1079 QSSGRhiContextPrivate *rhiCtxD = QSSGRhiContextPrivate::get(q: rhiCtx.get());
1080 QSSG_ASSERT(!data.renderedCameras.isEmpty(), return);
1081 QSSG_ASSERT(data.renderedCameras.count() == data.layer.viewCount, return);
1082
1083 const auto &shaderCache = renderer.contextInterface()->shaderCache();
1084 debugObjectShader = shaderCache->getBuiltInRhiShaders().getRhiDebugObjectShader(viewCount: data.layer.viewCount);
1085 ps = data.getPipelineState();
1086 ps.samples = rhiCtx->mainPassSampleCount();
1087 ps.viewCount = data.layer.viewCount;
1088
1089 // debug objects
1090 const auto &debugDraw = renderer.contextInterface()->debugDrawSystem();
1091 if (debugDraw && debugDraw->hasContent()) {
1092 QRhi *rhi = rhiCtx->rhi();
1093 QRhiResourceUpdateBatch *rub = rhi->nextResourceUpdateBatch();
1094 debugDraw->prepareGeometry(rhiCtx: rhiCtx.get(), rub);
1095 QSSGRhiDrawCallData &dcd = rhiCtxD->drawCallData(key: { .cid: this, .model: nullptr, .entry: nullptr, .entryIdx: 0 });
1096 if (!dcd.ubuf) {
1097 dcd.ubuf = rhi->newBuffer(type: QRhiBuffer::Dynamic, usage: QRhiBuffer::UniformBuffer, size: 64 * data.renderedCameras.count());
1098 dcd.ubuf->create();
1099 }
1100 char *ubufData = dcd.ubuf->beginFullDynamicBufferUpdateForCurrentFrame();
1101 QMatrix4x4 viewProjection(Qt::Uninitialized);
1102 QMatrix4x4 cameraGlobalTransform(Qt::Uninitialized);
1103 for (qsizetype viewIdx = 0; viewIdx < data.renderedCameras.count(); ++viewIdx) {
1104 cameraGlobalTransform = data.getGlobalTransform(node: *data.renderedCameras[viewIdx]);
1105 data.renderedCameras[viewIdx]->calculateViewProjectionMatrix(globalTransform: cameraGlobalTransform, outMatrix&: viewProjection);
1106 viewProjection = rhi->clipSpaceCorrMatrix() * viewProjection;
1107 memcpy(dest: ubufData, src: viewProjection.constData() + viewIdx * 64, n: 64);
1108 }
1109 dcd.ubuf->endFullDynamicBufferUpdateForCurrentFrame();
1110
1111 QSSGRhiShaderResourceBindingList bindings;
1112 bindings.addUniformBuffer(binding: 0, stage: QRhiShaderResourceBinding::VertexStage, buf: dcd.ubuf);
1113 dcd.srb = rhiCtxD->srb(bindings);
1114
1115 rhiCtx->commandBuffer()->resourceUpdate(resourceUpdates: rub);
1116 }
1117}
1118
1119void DebugDrawPass::renderPass(QSSGRenderer &renderer)
1120{
1121 const auto &rhiCtx = renderer.contextInterface()->rhiContext();
1122 QSSG_ASSERT(debugObjectShader && rhiCtx->rhi()->isRecordingFrame(), return);
1123 QRhiCommandBuffer *cb = rhiCtx->commandBuffer();
1124 QSSGRhiContextPrivate *rhiCtxD = QSSGRhiContextPrivate::get(q: rhiCtx.get());
1125
1126 const auto &debugDraw = renderer.contextInterface()->debugDrawSystem();
1127 if (debugDraw && debugDraw->hasContent()) {
1128 cb->debugMarkBegin(QByteArrayLiteral("Quick 3D debug objects"));
1129 Q_TRACE_SCOPE(QSSG_renderPass, QStringLiteral("Quick 3D debug objects"));
1130 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderPass);
1131 QSSGRhiGraphicsPipelineStatePrivate::setShaderPipeline(ps, pipeline: debugObjectShader.get());
1132 QSSGRhiDrawCallData &dcd = rhiCtxD->drawCallData(key: { .cid: this, .model: nullptr, .entry: nullptr, .entryIdx: 0 });
1133 QRhiShaderResourceBindings *srb = dcd.srb;
1134 QRhiRenderPassDescriptor *rpDesc = rhiCtx->mainRenderPassDescriptor();
1135 debugDraw->recordRenderDebugObjects(rhiCtx: rhiCtx.get(), ps: &ps, srb, rpDesc);
1136 cb->debugMarkEnd();
1137 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DRenderPass, 0, QByteArrayLiteral("debug_objects"));
1138 }
1139}
1140
1141void DebugDrawPass::resetForFrame()
1142{
1143 ps = {};
1144}
1145
1146void UserPass::renderPrep(QSSGRenderer &renderer, QSSGLayerRenderData &data)
1147{
1148 Q_UNUSED(renderer);
1149 auto &frameData = data.getFrameData();
1150 for (const auto &p : std::as_const(t&: extensions)) {
1151 p->prepareRender(data&: frameData);
1152 if (p->mode() == QSSGRenderExtension::RenderMode::Standalone)
1153 p->render(data&: frameData);
1154 }
1155}
1156
1157void UserPass::renderPass(QSSGRenderer &renderer)
1158{
1159 auto *data = QSSGLayerRenderData::getCurrent(renderer);
1160 QSSG_ASSERT(data, return);
1161 auto &frameData = data->getFrameData();
1162 for (const auto &p : std::as_const(t&: extensions)) {
1163 if (p->mode() == QSSGRenderExtension::RenderMode::Main)
1164 p->render(data&: frameData);
1165 }
1166}
1167
1168void UserPass::resetForFrame()
1169{
1170 for (const auto &p : std::as_const(t&: extensions))
1171 p->resetForFrame();
1172
1173 // TODO: We should track if we need to update this list.
1174 extensions.clear();
1175}
1176
1177void OITRenderPass::renderPrep(QSSGRenderer &renderer, QSSGLayerRenderData &data)
1178{
1179 auto *ctx = renderer.contextInterface();
1180 const auto &rhiCtx = ctx->rhiContext();
1181 auto *rhi = rhiCtx->rhi();
1182
1183 QSSG_ASSERT(!data.renderedCameras.isEmpty() && data.renderedCameraData.has_value() , return);
1184 QSSGRenderCamera *camera = data.renderedCameras[0];
1185
1186 ps = data.getPipelineState();
1187 ps.samples = rhiCtx->mainPassSampleCount();
1188 ps.viewCount = rhiCtx->mainPassViewCount();
1189
1190 ps.flags.setFlag(flag: QSSGRhiGraphicsPipelineState::Flag::BlendEnabled, on: true);
1191 ps.flags.setFlag(flag: QSSGRhiGraphicsPipelineState::Flag::DepthWriteEnabled, on: false);
1192
1193 shaderFeatures = data.getShaderFeatures();
1194 sortedTransparentObjects = data.getSortedTransparentRenderableObjects(camera: *camera);
1195
1196 if (method == QSSGRenderLayer::OITMethod::WeightedBlended) {
1197 ps.colorAttachmentCount = 2;
1198
1199 rhiAccumTexture = data.getRenderResult(id: QSSGFrameData::RenderResult::AccumTexture);
1200 rhiRevealageTexture = data.getRenderResult(id: QSSGFrameData::RenderResult::RevealageTexture);
1201 if (ps.samples > 1)
1202 rhiDepthTexture = data.getRenderResult(id: QSSGFrameData::RenderResult::DepthTextureMS);
1203 else
1204 rhiDepthTexture = data.getRenderResult(id: QSSGFrameData::RenderResult::DepthTexture);
1205 if (!rhiDepthTexture->isValid())
1206 return;
1207 auto &oitrt = data.getOitRenderContext();
1208 if (!oitrt.oitRenderTarget || oitrt.oitRenderTarget->pixelSize() != data.layerPrepResult.textureDimensions()
1209 || rhiDepthTexture->texture != oitrt.oitRenderTarget->description().depthTexture()
1210 || ps.samples != oitrt.oitRenderTarget->sampleCount()) {
1211 if (oitrt.oitRenderTarget) {
1212 rhiAccumTexture->texture->destroy();
1213 rhiRevealageTexture->texture->destroy();
1214 oitrt.oitRenderTarget->destroy();
1215 oitrt.renderPassDescriptor->destroy();
1216 oitrt.oitRenderTarget = nullptr;
1217 }
1218 const QRhiTexture::Flags textureFlags = QRhiTexture::RenderTarget;
1219 if (ps.viewCount >= 2) {
1220 rhiAccumTexture->texture = rhi->newTextureArray(format: QRhiTexture::RGBA16F, arraySize: ps.viewCount, pixelSize: data.layerPrepResult.textureDimensions(), sampleCount: ps.samples, flags: textureFlags);
1221 rhiRevealageTexture->texture = rhi->newTextureArray(format: QRhiTexture::R16F, arraySize: ps.viewCount, pixelSize: data.layerPrepResult.textureDimensions(), sampleCount: ps.samples, flags: textureFlags);
1222 } else {
1223 rhiAccumTexture->texture = rhi->newTexture(format: QRhiTexture::RGBA16F, pixelSize: data.layerPrepResult.textureDimensions(), sampleCount: ps.samples, flags: textureFlags);
1224 rhiRevealageTexture->texture = rhi->newTexture(format: QRhiTexture::R16F, pixelSize: data.layerPrepResult.textureDimensions(), sampleCount: ps.samples, flags: textureFlags);
1225 }
1226 rhiAccumTexture->texture->create();
1227 rhiRevealageTexture->texture->create();
1228
1229 QRhiTextureRenderTargetDescription desc;
1230 desc.setColorAttachments({{rhiAccumTexture->texture}, {rhiRevealageTexture->texture}});
1231 desc.setDepthTexture(rhiDepthTexture->texture);
1232
1233 if (oitrt.oitRenderTarget == nullptr) {
1234 oitrt.oitRenderTarget = rhi->newTextureRenderTarget(desc, flags: QRhiTextureRenderTarget::PreserveDepthStencilContents);
1235 oitrt.renderPassDescriptor = oitrt.oitRenderTarget->newCompatibleRenderPassDescriptor();
1236 oitrt.oitRenderTarget->setRenderPassDescriptor(oitrt.renderPassDescriptor);
1237 oitrt.oitRenderTarget->create();
1238
1239 renderTarget = oitrt.oitRenderTarget;
1240 }
1241 }
1242 QSSGRhiContextPrivate *rhiCtxD = QSSGRhiContextPrivate::get(q: rhiCtx.get());
1243 const auto &shaderCache = renderer.contextInterface()->shaderCache();
1244 clearPipeline = shaderCache->getBuiltInRhiShaders().getRhiClearMRTShader();
1245
1246 QSSGRhiShaderResourceBindingList bindings;
1247 QVector4D clearData[2];
1248 clearData[0] = QVector4D(0.0, 0.0, 0.0, 0.0);
1249 clearData[1] = QVector4D(1.0, 1.0, 1.0, 1.0);
1250
1251 QSSGRhiDrawCallData &dcd(rhiCtxD->drawCallData(key: { .cid: this, .model: nullptr, .entry: nullptr, .entryIdx: 0 }));
1252 QRhiBuffer *&ubuf = dcd.ubuf;
1253 const int ubufSize = sizeof(clearData);
1254 if (!ubuf) {
1255 ubuf = rhi->newBuffer(type: QRhiBuffer::Dynamic, usage: QRhiBuffer::UniformBuffer, size: ubufSize);
1256 ubuf->create();
1257 }
1258
1259 QRhiResourceUpdateBatch *rub = rhi->nextResourceUpdateBatch();
1260 rub->updateDynamicBuffer(buf: ubuf, offset: 0, size: ubufSize, data: &clearData);
1261 renderer.rhiQuadRenderer()->prepareQuad(rhiCtx: rhiCtx.get(), maybeRub: rub);
1262
1263 bindings.addUniformBuffer(binding: 0, stage: QRhiShaderResourceBinding::FragmentStage, buf: ubuf);
1264
1265 clearSrb = rhiCtxD->srb(bindings);
1266
1267 ps.targetBlend[0].srcAlpha = QRhiGraphicsPipeline::One;
1268 ps.targetBlend[0].srcColor = QRhiGraphicsPipeline::One;
1269 ps.targetBlend[0].dstAlpha = QRhiGraphicsPipeline::One;
1270 ps.targetBlend[0].dstColor = QRhiGraphicsPipeline::One;
1271 ps.targetBlend[1].srcAlpha = QRhiGraphicsPipeline::Zero;
1272 ps.targetBlend[1].srcColor = QRhiGraphicsPipeline::Zero;
1273 ps.targetBlend[1].dstAlpha = QRhiGraphicsPipeline::OneMinusSrcAlpha;
1274 ps.targetBlend[1].dstColor = QRhiGraphicsPipeline::OneMinusSrcAlpha;
1275
1276 TransparentPass::prep(ctx: *ctx, data, passKey: this, ps, shaderFeatures, rpDesc: oitrt.renderPassDescriptor, sortedTransparentObjects, oit: true);
1277 }
1278}
1279
1280void OITRenderPass::renderPass(QSSGRenderer &renderer)
1281{
1282 auto *ctx = renderer.contextInterface();
1283 const auto &rhiCtx = ctx->rhiContext();
1284 QSSG_ASSERT(rhiCtx->rhi()->isRecordingFrame(), return);
1285 QRhiCommandBuffer *cb = rhiCtx->commandBuffer();
1286
1287 if (method == QSSGRenderLayer::OITMethod::WeightedBlended) {
1288 if (Q_LIKELY(renderTarget)) {
1289 cb->beginPass(rt: renderTarget, colorClearValue: Qt::black, depthStencilClearValue: {});
1290
1291 QRhiShaderResourceBindings *srb = clearSrb;
1292 QSSG_ASSERT(srb, return);
1293 ps.flags.setFlag(flag: QSSGRhiGraphicsPipelineState::Flag::BlendEnabled, on: false);
1294 QSSGRhiGraphicsPipelineStatePrivate::setShaderPipeline(ps, pipeline: clearPipeline.get());
1295 renderer.rhiQuadRenderer()->recordRenderQuad(rhiCtx: rhiCtx.get(), ps: &ps, srb, rpDesc: renderTarget->renderPassDescriptor(), flags: {});
1296 ps.flags.setFlag(flag: QSSGRhiGraphicsPipelineState::Flag::BlendEnabled, on: true);
1297
1298 cb->debugMarkBegin(QByteArrayLiteral("Quick3D render order-independent alpha"));
1299 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderPass);
1300 Q_TRACE(QSSG_renderPass_entry, QStringLiteral("Quick3D render order-independent alpha"));
1301 ps.flags.setFlag(flag: QSSGRhiGraphicsPipelineState::Flag::DepthTestEnabled, on: true);
1302 ps.flags.setFlag(flag: QSSGRhiGraphicsPipelineState::Flag::DepthWriteEnabled, on: false);
1303 TransparentPass::render(ctx: *ctx, ps, sortedTransparentObjects);
1304 cb->debugMarkEnd();
1305 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DRenderPass, 0, QByteArrayLiteral("transparent_order_independent_pass"));
1306 Q_TRACE(QSSG_renderPass_exit);
1307
1308 cb->endPass();
1309 }
1310 }
1311}
1312
1313QSSGRenderPass::Type OITRenderPass::passType() const
1314{
1315 if (method == QSSGRenderLayer::OITMethod::WeightedBlended)
1316 return Type::Standalone;
1317 return Type::Main;
1318}
1319
1320
1321void OITRenderPass::resetForFrame()
1322{
1323 sortedTransparentObjects.clear();
1324 ps = {};
1325 shaderFeatures = {};
1326 rhiAccumTexture = nullptr;
1327 rhiRevealageTexture = nullptr;
1328 rhiDepthTexture = nullptr;
1329}
1330
1331void OITCompositePass::renderPrep(QSSGRenderer &renderer, QSSGLayerRenderData &data)
1332{
1333 using namespace RenderHelpers;
1334
1335 QSSG_ASSERT(!data.renderedCameras.isEmpty(), return);
1336
1337 const auto &rhiCtx = renderer.contextInterface()->rhiContext();
1338 QSSG_ASSERT(rhiCtx->rhi()->isRecordingFrame(), return);
1339 const auto &shaderCache = renderer.contextInterface()->shaderCache();
1340
1341 ps = data.getPipelineState();
1342 ps.samples = rhiCtx->mainPassSampleCount();
1343 ps.viewCount = rhiCtx->mainPassViewCount();
1344
1345 if (method == QSSGRenderLayer::OITMethod::WeightedBlended) {
1346 rhiAccumTexture = data.getRenderResult(id: QSSGFrameData::RenderResult::AccumTexture);
1347 rhiRevealageTexture = data.getRenderResult(id: QSSGFrameData::RenderResult::RevealageTexture);
1348 compositeShaderPipeline = shaderCache->getBuiltInRhiShaders().getRhiOitCompositeShader(method, multisample: ps.samples > 1 ? true : false);
1349 }
1350}
1351
1352void OITCompositePass::renderPass(QSSGRenderer &renderer)
1353{
1354 using namespace RenderHelpers;
1355
1356 const auto &rhiCtx = renderer.contextInterface()->rhiContext();
1357 QSSG_ASSERT(rhiCtx->rhi()->isRecordingFrame(), return);
1358 QRhiCommandBuffer *cb = rhiCtx->commandBuffer();
1359
1360 QSSGRhiContextPrivate *rhiCtxD = QSSGRhiContextPrivate::get(q: rhiCtx.get());
1361
1362 if (!rhiAccumTexture->texture || !rhiRevealageTexture->texture)
1363 return;
1364
1365 if (method == QSSGRenderLayer::OITMethod::WeightedBlended) {
1366 QSSGRhiShaderResourceBindingList bindings;
1367
1368 QRhiSampler *sampler = rhiCtx->sampler(samplerDescription: { .minFilter: QRhiSampler::Nearest,
1369 .magFilter: QRhiSampler::Nearest,
1370 .mipmap: QRhiSampler::None,
1371 .hTiling: QRhiSampler::ClampToEdge,
1372 .vTiling: QRhiSampler::ClampToEdge,
1373 .zTiling: QRhiSampler::ClampToEdge });
1374 bindings.addTexture(binding: 1, stage: QRhiShaderResourceBinding::FragmentStage, tex: rhiAccumTexture->texture, sampler);
1375 bindings.addTexture(binding: 2, stage: QRhiShaderResourceBinding::FragmentStage, tex: rhiRevealageTexture->texture, sampler);
1376
1377 compositeSrb = rhiCtxD->srb(bindings);
1378
1379 QRhiShaderResourceBindings *srb = compositeSrb;
1380 QSSG_ASSERT(srb, return);
1381
1382 cb->debugMarkBegin(QByteArrayLiteral("Quick3D revealage"));
1383 QSSGRhiGraphicsPipelineStatePrivate::setShaderPipeline(ps, pipeline: compositeShaderPipeline.get());
1384 ps.flags.setFlag(flag: QSSGRhiGraphicsPipelineState::Flag::BlendEnabled, on: true);
1385 renderer.rhiQuadRenderer()->recordRenderQuad(rhiCtx: rhiCtx.get(), ps: &ps, srb, rpDesc: rhiCtx->mainRenderPassDescriptor(),
1386 flags: { QSSGRhiQuadRenderer::UvCoords | QSSGRhiQuadRenderer::DepthTest | QSSGRhiQuadRenderer::PremulBlend});
1387 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DRenderPass, 0, QByteArrayLiteral("revealage"));
1388 cb->debugMarkEnd();
1389 }
1390}
1391
1392void OITCompositePass::resetForFrame()
1393{
1394 ps = {};
1395 shaderFeatures = {};
1396 rhiAccumTexture = nullptr;
1397 rhiRevealageTexture = nullptr;
1398}
1399
1400QT_END_NAMESPACE
1401

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