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

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