Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qssgrenderhelpers.cpp
Go to the documentation of this file.
1// Copyright (C) 2023 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
5
6#include "qssgrenderer_p.h"
14#include <QtQuick3DUtils/private/qssgassert_p.h>
15
16#include <QtCore/qbitarray.h>
17
19
20static constexpr float QSSG_PI = float(M_PI);
21static constexpr float QSSG_HALFPI = float(M_PI_2);
22
23static const QRhiShaderResourceBinding::StageFlags RENDERER_VISIBILITY_ALL =
25
27 QSSGSubsetRenderable &subsetRenderable,
28 const QSSGShaderFeatures &featureSet)
29{
30 auto &renderer(subsetRenderable.renderer);
31 const auto &shaderPipeline = QSSGRendererPrivate::getShaderPipelineForDefaultMaterial(*renderer, subsetRenderable, featureSet);
32 if (shaderPipeline)
34 return shaderPipeline;
35}
36
38 QSSGParticlesRenderable &particleRenderable)
39{
40 const auto &renderer(particleRenderable.renderer);
41 const auto &shaderCache = renderer->contextInterface()->shaderCache();
42 auto featureLevel = particleRenderable.particles.m_featureLevel;
43 const auto &shaderPipeline = shaderCache->getBuiltInRhiShaders().getRhiParticleShader(featureLevel, ps->viewCount);
44 if (shaderPipeline)
46 return shaderPipeline;
47}
48
50 QSSGRhiContext *rhiCtx,
51 const QSSGLayerRenderData &inData,
52 char *ubufData,
54 QSSGSubsetRenderable &subsetRenderable,
55 const QSSGRenderCameraList &cameras,
56 const QVector2D *depthAdjust,
57 const QMatrix4x4 *alteredModelViewProjection)
58{
59 const auto &renderer(subsetRenderable.renderer);
60 const QMatrix4x4 clipSpaceCorrMatrix = rhiCtx->rhi()->clipSpaceCorrMatrix();
61 QSSGRenderMvpArray alteredMvpList;
62 if (alteredModelViewProjection)
63 alteredMvpList[0] = *alteredModelViewProjection;
64
65 const auto &modelNode = subsetRenderable.modelContext.model;
66 QRhiTexture *lightmapTexture = inData.getLightmapTexture(subsetRenderable.modelContext);
67
68 const QMatrix4x4 &localInstanceTransform(modelNode.localInstanceTransform);
69 const QMatrix4x4 &globalInstanceTransform(modelNode.globalInstanceTransform);
70 const QMatrix4x4 &modelMatrix(modelNode.usesBoneTexture() ? QMatrix4x4() : subsetRenderable.globalTransform);
71
73 shaderPipeline,
74 ubufData,
75 ps,
76 subsetRenderable.material,
77 subsetRenderable.shaderDescription,
78 inData.getDefaultMaterialPropertyTable(),
79 cameras,
80 alteredModelViewProjection ? alteredMvpList : subsetRenderable.modelContext.modelViewProjections,
81 subsetRenderable.modelContext.normalMatrix,
82 modelMatrix,
83 clipSpaceCorrMatrix,
84 localInstanceTransform,
85 globalInstanceTransform,
86 toDataView(modelNode.morphWeights),
87 subsetRenderable.firstImage,
88 subsetRenderable.opacity,
89 inData,
90 subsetRenderable.lights,
91 subsetRenderable.reflectionProbe,
92 subsetRenderable.renderableFlags.receivesShadows(),
93 subsetRenderable.renderableFlags.receivesReflections(),
94 depthAdjust,
95 lightmapTexture);
96}
97
98std::pair<QSSGBoxPoints, QSSGBoxPoints> RenderHelpers::calculateSortedObjectBounds(const QSSGRenderableObjectList &sortedOpaqueObjects,
99 const QSSGRenderableObjectList &sortedTransparentObjects)
100{
101 QSSGBounds3 boundsCasting;
102 QSSGBounds3 boundsReceiving;
103 for (const auto handles : { &sortedOpaqueObjects, &sortedTransparentObjects }) {
104 // Since we may have nodes that are not a child of the camera parent we go through all
105 // the opaque objects and include them in the bounds. Failing to do this can result in
106 // too small bounds.
107 for (const QSSGRenderableObjectHandle &handle : *handles) {
108 const QSSGRenderableObject &obj = *handle.obj;
109 // We skip objects not casting or receiving shadows since they don't influence or need to be covered by the shadow map
110 if (obj.renderableFlags.castsShadows())
111 boundsCasting.include(obj.globalBounds);
112 if (obj.renderableFlags.receivesShadows())
113 boundsReceiving.include(obj.globalBounds);
114 }
115 }
116 return { boundsCasting.toQSSGBoxPointsNoEmptyCheck(), boundsReceiving.toQSSGBoxPointsNoEmptyCheck() };
117}
118
119static QVector3D calcCenter(const QSSGBoxPoints &vertices)
120{
121 QVector3D center = vertices[0];
122 for (int i = 1; i < 8; ++i) {
123 center += vertices[i];
124 }
125 return center * 0.125f;
126}
127
129{
130 QSSGBounds3 bounds;
131 for (int i = 0; i < 8; ++i) {
132 const float distanceZ = QVector3D::dotProduct(points[i], forward);
133 const float distanceY = QVector3D::dotProduct(points[i], up);
134 const float distanceX = QVector3D::dotProduct(points[i], right);
135 bounds.include(QVector3D(distanceX, distanceY, distanceZ));
136 }
137 return bounds;
138}
139
141{
142 QMatrix4x4 viewProjection;
143 inCamera.calculateViewProjectionMatrix(viewProjection);
144
145 bool invertible = false;
146 QMatrix4x4 inv = viewProjection.inverted(&invertible);
147 Q_ASSERT(invertible);
148
149 return { inv.map(QVector3D(-1, -1, -1)), inv.map(QVector3D(+1, -1, -1)), inv.map(QVector3D(+1, +1, -1)),
150 inv.map(QVector3D(-1, +1, -1)), inv.map(QVector3D(-1, -1, +1)), inv.map(QVector3D(+1, -1, +1)),
151 inv.map(QVector3D(+1, +1, +1)), inv.map(QVector3D(-1, +1, +1)) };
152}
153
155{
156 Q_ASSERT(inProbe != nullptr);
157
158 // setup light matrix
159 quint32 mapRes = 1 << inProbe->reflectionMapRes;
160 QRectF theViewport(0.0f, 0.0f, (float)mapRes, (float)mapRes);
161 static const QQuaternion rotOfs[6] {
162 QQuaternion::fromEulerAngles(0.f, qRadiansToDegrees(-QSSG_HALFPI), qRadiansToDegrees(QSSG_PI)),
163 QQuaternion::fromEulerAngles(0.f, qRadiansToDegrees(QSSG_HALFPI), qRadiansToDegrees(QSSG_PI)),
164 QQuaternion::fromEulerAngles(qRadiansToDegrees(QSSG_HALFPI), 0.f, 0.f),
165 QQuaternion::fromEulerAngles(qRadiansToDegrees(-QSSG_HALFPI), 0.f, 0.f),
166 QQuaternion::fromEulerAngles(0.f, qRadiansToDegrees(QSSG_PI), qRadiansToDegrees(-QSSG_PI)),
167 QQuaternion::fromEulerAngles(0.f, 0.f, qRadiansToDegrees(QSSG_PI)),
168 };
169
170 const QVector3D inProbePos = inProbe->getGlobalPos();
171 const QVector3D inProbePivot = inProbe->pivot;
172
173 for (int i = 0; i < 6; ++i) {
174 inCameras[i].parent = nullptr;
175 inCameras[i].clipNear = 1.0f;
176 inCameras[i].clipFar = qMax<float>(2.0f, 10000.0f);
177 inCameras[i].fov = qDegreesToRadians(90.f);
178
179 inCameras[i].localTransform = QSSGRenderNode::calculateTransformMatrix(inProbePos, QSSGRenderNode::initScale, inProbePivot, rotOfs[i]);
180 inCameras[i].calculateGlobalVariables(theViewport);
181 }
182}
183
184static void setupCameraForShadowMap(const QSSGRenderCamera &inCamera,
185 const QSSGRenderLight *inLight,
186 QSSGRenderCamera &theCamera,
187 const QSSGBoxPoints &castingBox,
188 const QSSGBoxPoints &receivingBox)
189{
190 using namespace RenderHelpers;
191
192 // setup light matrix
193 quint32 mapRes = inLight->m_shadowMapRes;
194 QRectF theViewport(0.0f, 0.0f, (float)mapRes, (float)mapRes);
195 theCamera.clipNear = 1.0f;
196 theCamera.clipFar = inLight->m_shadowMapFar;
197 // Setup camera projection
198 QVector3D inLightPos = inLight->getGlobalPos();
199 QVector3D inLightDir = inLight->getDirection();
200 QVector3D inLightPivot = inLight->pivot;
201
202 inLightPos -= inLightDir * inCamera.clipNear;
203 theCamera.fov = qDegreesToRadians(90.f);
204 theCamera.parent = nullptr;
205
206 if (inLight->type == QSSGRenderLight::Type::DirectionalLight) {
207 Q_ASSERT(theCamera.type == QSSGRenderCamera::Type::OrthographicCamera);
208 const QVector3D forward = inLightDir.normalized();
209 const QVector3D right = qFuzzyCompare(qAbs(forward.y()), 1.0f)
210 ? QVector3D::crossProduct(forward, QVector3D(1, 0, 0)).normalized()
211 : QVector3D::crossProduct(forward, QVector3D(0, 1, 0)).normalized();
212 const QVector3D up = QVector3D::crossProduct(right, forward).normalized();
213
214 // Calculate bounding box of the scene camera frustum
215 const QSSGBoxPoints frustumPoints = computeFrustumBounds(inCamera);
216 const QSSGBounds3 frustumBounds = calculateShadowCameraBoundingBox(frustumPoints, forward, up, right);
217 const QSSGBounds3 sceneCastingBounds = calculateShadowCameraBoundingBox(castingBox, forward, up, right);
218 const QSSGBounds3 boundsReceiving = calculateShadowCameraBoundingBox(receivingBox, forward, up, right);
219
220 QVector3D finalDims;
221 QVector3D center;
222 // Select smallest bounds from either scene or camera frustum
223 if (sceneCastingBounds.isFinite() && boundsReceiving.isFinite() // handle empty scene
224 && sceneCastingBounds.extents().lengthSquared() < frustumBounds.extents().lengthSquared()) {
225 center = calcCenter(castingBox);
226 const QVector3D centerReceiving = calcCenter(receivingBox);
227
228 // Since we need to make sure every rendered geometry can get a valid depth value from the shadow map
229 // we need to expand the scene bounding box along its z-axis so that it covers also receiving objects in the scene.
230 //
231 // We take the z dimensions of the casting bounds and expand it to include the z dimensions of the receiving objects.
232 // We call the casting bounding box 'a' and the receiving bounding box 'b'.
233
234 // length of boxes
235 const float aLength = sceneCastingBounds.dimensions().z();
236 const float bLength = boundsReceiving.dimensions().z();
237
238 // center position of boxes
239 const float aCenter = QVector3D::dotProduct(center, forward);
240 const float bCenter = QVector3D::dotProduct(centerReceiving, forward);
241
242 // distance between boxes
243 const float d = bCenter - aCenter;
244
245 // start/end positions
246 const float a0 = 0.f;
247 const float a1 = aLength;
248 const float b0 = (aLength * 0.5f) + d - (bLength * 0.5f);
249 const float b1 = (aLength * 0.5f) + d + (bLength * 0.5f);
250
251 // goal start/end position
252 const float ap0 = qMin(a0, b0);
253 const float ap1 = qMax(a1, b1);
254 // goal length
255 const float length = ap1 - ap0;
256 // goal center postion
257 const float c = (ap1 + ap0) * 0.5f;
258
259 // how much to move in forward direction
260 const float move = c - aLength * 0.5f;
261
262 center = center + forward * move;
263 finalDims = sceneCastingBounds.dimensions();
264 finalDims.setZ(length);
265 } else {
266 center = calcCenter(frustumPoints);
267 finalDims = frustumBounds.dimensions();
268 }
269
270 // Expand dimensions a little bit to avoid precision problems
271 finalDims *= 1.05f;
272
273 // Apply bounding box parameters to shadow map camera projection matrix
274 // so that the whole scene is fit inside the shadow map
275 theViewport.setHeight(finalDims.y());
276 theViewport.setWidth(finalDims.x());
277 theCamera.clipNear = -0.5f * finalDims.z();
278 theCamera.clipFar = 0.5f * finalDims.z();
279 theCamera.localTransform = QSSGRenderNode::calculateTransformMatrix(center, QSSGRenderNode::initScale, inLightPivot, QQuaternion::fromDirection(forward, up));
280 } else if (inLight->type == QSSGRenderLight::Type::PointLight) {
281 theCamera.lookAt(inLightPos, QVector3D(0, 1.0, 0), QVector3D(0, 0, 0), inLightPivot);
282 }
283
284 theCamera.calculateGlobalVariables(theViewport);
285}
286
288 QSSGRhiShaderPipeline *shaderPipeline,
289 QSSGRenderableImage *renderableImage,
291 bool isCustomMaterialMeshSubset = false)
292{
293 static const auto imageAffectsAlpha = [](QSSGRenderableImage::Type mapType) {
294 return mapType == QSSGRenderableImage::Type::BaseColor ||
295 mapType == QSSGRenderableImage::Type::Diffuse ||
296 mapType == QSSGRenderableImage::Type::Translucency ||
297 mapType == QSSGRenderableImage::Type::Opacity;
298 };
299
300 while (renderableImage) {
301 const auto mapType = renderableImage->m_mapType;
302 if (imageAffectsAlpha(mapType)) {
303 const char *samplerName = QSSGMaterialShaderGenerator::getSamplerName(mapType);
304 const int samplerHint = int(mapType);
305 int samplerBinding = shaderPipeline->bindingForTexture(samplerName, samplerHint);
306 if (samplerBinding >= 0) {
307 QRhiTexture *texture = renderableImage->m_texture.m_texture;
308 if (samplerBinding >= 0 && texture) {
309 const bool mipmapped = texture->flags().testFlag(QRhiTexture::MipMapped);
310 QRhiSampler *sampler = rhiCtx->sampler({ QSSGRhiHelpers::toRhi(renderableImage->m_imageNode.m_minFilterType),
311 QSSGRhiHelpers::toRhi(renderableImage->m_imageNode.m_magFilterType),
312 mipmapped ? QSSGRhiHelpers::toRhi(renderableImage->m_imageNode.m_mipFilterType) : QRhiSampler::None,
313 QSSGRhiHelpers::toRhi(renderableImage->m_imageNode.m_horizontalTilingMode),
314 QSSGRhiHelpers::toRhi(renderableImage->m_imageNode.m_verticalTilingMode),
315 QSSGRhiHelpers::toRhi(renderableImage->m_imageNode.m_depthTilingMode)
316 });
317 bindings.addTexture(samplerBinding, RENDERER_VISIBILITY_ALL, texture, sampler);
318 }
319 } // else this is not necessarily an error, e.g. having metalness/roughness maps with metalness disabled
320 }
321 renderableImage = renderableImage->m_nextImage;
322 }
323 // For custom Materials we can't know which maps affect alpha, so map all
324 if (isCustomMaterialMeshSubset) {
325 QVector<QShaderDescription::InOutVariable> samplerVars =
326 shaderPipeline->fragmentStage()->shader().description().combinedImageSamplers();
328 auto it = std::find_if(samplerVars.cbegin(), samplerVars.cend(),
329 [&var](const QShaderDescription::InOutVariable &v) { return var.binding == v.binding; });
330 if (it == samplerVars.cend())
331 samplerVars.append(var);
332 }
333
334 int maxSamplerBinding = -1;
335 for (const QShaderDescription::InOutVariable &var : samplerVars)
336 maxSamplerBinding = qMax(maxSamplerBinding, var.binding);
337
338 // Will need to set unused image-samplers to something dummy
339 // because the shader code contains all custom property textures,
340 // and not providing a binding for all of them is invalid with some
341 // graphics APIs (and will need a real texture because setting a
342 // null handle or similar is not permitted with some of them so the
343 // srb does not accept null QRhiTextures either; but first let's
344 // figure out what bindings are unused in this frame)
345 QBitArray samplerBindingsSpecified(maxSamplerBinding + 1);
346
347 if (maxSamplerBinding >= 0) {
348 // custom property textures
349 int customTexCount = shaderPipeline->extraTextureCount();
350 for (int i = 0; i < customTexCount; ++i) {
351 const QSSGRhiTexture &t(shaderPipeline->extraTextureAt(i));
352 const int samplerBinding = shaderPipeline->bindingForTexture(t.name);
353 if (samplerBinding >= 0) {
354 samplerBindingsSpecified.setBit(samplerBinding);
355 QRhiSampler *sampler = rhiCtx->sampler(t.samplerDesc);
356 bindings.addTexture(samplerBinding,
358 t.texture,
359 sampler);
360 }
361 }
362 }
363
364 // use a dummy texture for the unused samplers in the shader
367 QRhiResourceUpdateBatch *resourceUpdates = rhiCtx->rhi()->nextResourceUpdateBatch();
368 QRhiTexture *dummyTexture = rhiCtx->dummyTexture({}, resourceUpdates);
369 QRhiTexture *dummyCubeTexture = rhiCtx->dummyTexture(QRhiTexture::CubeMap, resourceUpdates);
370 rhiCtx->commandBuffer()->resourceUpdate(resourceUpdates);
371
372 for (const QShaderDescription::InOutVariable &var : samplerVars) {
373 if (!samplerBindingsSpecified.testBit(var.binding)) {
374 QRhiTexture *t = var.type == QShaderDescription::SamplerCube ? dummyCubeTexture : dummyTexture;
375 bindings.addTexture(var.binding, RENDERER_VISIBILITY_ALL, t, dummySampler);
376 }
377 }
378 }
379}
380
381static void setupCubeShadowCameras(const QSSGRenderLight *inLight, QSSGRenderCamera inCameras[6])
382{
383 Q_ASSERT(inLight != nullptr);
384 Q_ASSERT(inLight->type != QSSGRenderLight::Type::DirectionalLight);
385
386 // setup light matrix
387 quint32 mapRes = inLight->m_shadowMapRes;
388 QRectF theViewport(0.0f, 0.0f, (float)mapRes, (float)mapRes);
389 static const QQuaternion rotOfs[6] {
390 QQuaternion::fromEulerAngles(0.f, qRadiansToDegrees(-QSSG_HALFPI), qRadiansToDegrees(QSSG_PI)),
391 QQuaternion::fromEulerAngles(0.f, qRadiansToDegrees(QSSG_HALFPI), qRadiansToDegrees(QSSG_PI)),
392 QQuaternion::fromEulerAngles(qRadiansToDegrees(QSSG_HALFPI), 0.f, 0.f),
393 QQuaternion::fromEulerAngles(qRadiansToDegrees(-QSSG_HALFPI), 0.f, 0.f),
394 QQuaternion::fromEulerAngles(0.f, qRadiansToDegrees(QSSG_PI), qRadiansToDegrees(-QSSG_PI)),
395 QQuaternion::fromEulerAngles(0.f, 0.f, qRadiansToDegrees(QSSG_PI)),
396 };
397
398 const QVector3D inLightPos = inLight->getGlobalPos();
399 const QVector3D inLightPivot = inLight->pivot;
400
401 for (int i = 0; i < 6; ++i) {
402 inCameras[i].parent = nullptr;
403 inCameras[i].clipNear = 1.0f;
404 inCameras[i].clipFar = qMax<float>(2.0f, inLight->m_shadowMapFar);
405 inCameras[i].fov = qDegreesToRadians(90.f);
406 inCameras[i].localTransform = QSSGRenderNode::calculateTransformMatrix(inLightPos, QSSGRenderNode::initScale, inLightPivot, rotOfs[i]);
407 inCameras[i].calculateGlobalVariables(theViewport);
408 }
409
410 /*
411 if ( inLight->type == RenderLightTypes::Point ) return;
412
413 QVector3D viewDirs[6];
414 QVector3D viewUp[6];
415 QMatrix3x3 theDirMatrix( inLight->m_GlobalTransform.getUpper3x3() );
416
417 viewDirs[0] = theDirMatrix.transform( QVector3D( 1.f, 0.f, 0.f ) );
418 viewDirs[2] = theDirMatrix.transform( QVector3D( 0.f, -1.f, 0.f ) );
419 viewDirs[4] = theDirMatrix.transform( QVector3D( 0.f, 0.f, 1.f ) );
420 viewDirs[0].normalize(); viewDirs[2].normalize(); viewDirs[4].normalize();
421 viewDirs[1] = -viewDirs[0];
422 viewDirs[3] = -viewDirs[2];
423 viewDirs[5] = -viewDirs[4];
424
425 viewUp[0] = viewDirs[2];
426 viewUp[1] = viewDirs[2];
427 viewUp[2] = viewDirs[5];
428 viewUp[3] = viewDirs[4];
429 viewUp[4] = viewDirs[2];
430 viewUp[5] = viewDirs[2];
431
432 for (int i = 0; i < 6; ++i)
433 {
434 inCameras[i].LookAt( inLightPos, viewUp[i], inLightPos + viewDirs[i] );
435 inCameras[i].CalculateGlobalVariables( theViewport, QVector2D( theViewport.m_Width,
436 theViewport.m_Height ) );
437 }
438 */
439}
440
441static int setupInstancing(QSSGSubsetRenderable *renderable, QSSGRhiGraphicsPipelineState *ps, QSSGRhiContext *rhiCtx, const QVector3D &cameraDirection, const QVector3D &cameraPosition)
442{
443 // TODO: non-static so it can be used from QSSGCustomMaterialSystem::rhiPrepareRenderable()?
444 const bool instancing = QSSGLayerRenderData::prepareInstancing(rhiCtx, renderable, cameraDirection, cameraPosition, renderable->instancingLodMin, renderable->instancingLodMax);
445 int instanceBufferBinding = 0;
446 if (instancing) {
448 // set up new bindings for instanced buffers
449 const quint32 stride = renderable->modelContext.model.instanceTable->stride();
450 QVarLengthArray<QRhiVertexInputBinding, 8> bindings;
451 std::copy(ia.inputLayout.cbeginBindings(), ia.inputLayout.cendBindings(), std::back_inserter(bindings));
452 bindings.append({ stride, QRhiVertexInputBinding::PerInstance });
453 instanceBufferBinding = bindings.size() - 1;
454 ia.inputLayout.setBindings(bindings.cbegin(), bindings.cend());
455 }
456 return instanceBufferBinding;
457}
458
460 QSSGPassKey passKey,
461 const QSSGLayerRenderData &inData,
464 const QSSGRenderableObjectList &sortedOpaqueObjects,
465 QSSGRenderCamera &inCamera,
468{
469 using namespace RenderHelpers;
470
471 if ((inData.layer.background == QSSGRenderLayer::Background::SkyBox && inData.layer.lightProbe) ||
472 inData.layer.background == QSSGRenderLayer::Background::SkyBoxCubeMap)
473 rhiPrepareSkyBoxForReflectionMap(rhiCtx, passKey, inData.layer, inCamera, renderer, pEntry, cubeFace);
474
475 QSSGShaderFeatures features = inData.getShaderFeatures();
476 // because of alteredCamera/alteredMvp below
478
479 const auto &defaultMaterialShaderKeyProperties = inData.getDefaultMaterialPropertyTable();
480
481 for (const auto &handle : sortedOpaqueObjects) {
482 QSSGRenderableObject &inObject = *handle.obj;
483
484 QMatrix4x4 modelViewProjection;
485 if (inObject.type == QSSGRenderableObject::Type::DefaultMaterialMeshSubset || inObject.type == QSSGRenderableObject::Type::CustomMaterialMeshSubset) {
486 QSSGSubsetRenderable &renderable(static_cast<QSSGSubsetRenderable &>(inObject));
487 const bool hasSkinning = defaultMaterialShaderKeyProperties.m_boneCount.getValue(renderable.shaderDescription) > 0;
488 modelViewProjection = hasSkinning ? pEntry->m_viewProjection
489 : pEntry->m_viewProjection * renderable.globalTransform;
490 }
491
492 // here we pass on our own alteredCamera and alteredModelViewProjection
493 rhiPrepareRenderable(rhiCtx, passKey, inData, inObject, pEntry->m_rhiRenderPassDesc, ps, features, 1, 1,
494 &inCamera, &modelViewProjection, cubeFace, pEntry);
495 }
496}
497
498static inline void addDepthTextureBindings(QSSGRhiContext *rhiCtx,
499 QSSGRhiShaderPipeline *shaderPipeline,
501{
502 if (shaderPipeline->depthTexture()) {
503 const int depthTextureBinding = shaderPipeline->bindingForTexture("qt_depthTexture", int(QSSGRhiSamplerBindingHints::DepthTexture));
504 const int depthTextureArrayBinding = shaderPipeline->bindingForTexture("qt_depthTextureArray", int(QSSGRhiSamplerBindingHints::DepthTextureArray));
505 if (depthTextureBinding >= 0 || depthTextureArrayBinding >= 0) {
506 // nearest min/mag, no mipmap
509 if (depthTextureBinding >= 0)
510 bindings.addTexture(depthTextureBinding, QRhiShaderResourceBinding::FragmentStage, shaderPipeline->depthTexture(), sampler);
511 if (depthTextureArrayBinding >= 0)
512 bindings.addTexture(depthTextureBinding, QRhiShaderResourceBinding::FragmentStage, shaderPipeline->depthTexture(), sampler);
513 } // else ignore, not an error
514 }
515
516 // SSAO texture
517 if (shaderPipeline->ssaoTexture()) {
518 const int ssaoTextureBinding = shaderPipeline->bindingForTexture("qt_aoTexture", int(QSSGRhiSamplerBindingHints::AoTexture));
519 const int ssaoTextureArrayBinding = shaderPipeline->bindingForTexture("qt_aoTextureArray", int(QSSGRhiSamplerBindingHints::AoTextureArray));
520 if (ssaoTextureBinding >= 0 || ssaoTextureArrayBinding >= 0) {
521 // linear min/mag, no mipmap
524 if (ssaoTextureBinding >= 0) {
525 bindings.addTexture(ssaoTextureBinding,
527 shaderPipeline->ssaoTexture(), sampler);
528 }
529 if (ssaoTextureArrayBinding >= 0) {
530 bindings.addTexture(ssaoTextureArrayBinding,
532 shaderPipeline->ssaoTexture(), sampler);
533 }
534 } // else ignore, not an error
535 }
536}
537
539 const QSSGLayerRenderData &inData,
540 QSSGPassKey passKey,
541 QSSGShadowMapEntry *pEntry,
543 const QVector2D *depthAdjust,
544 const QSSGRenderableObjectList &sortedOpaqueObjects,
545 QSSGRenderCamera &inCamera,
546 bool orthographic,
548{
549 QSSGShaderFeatures featureSet;
550 if (orthographic)
552 else
554
555 // Do note how updateUniformsForDefaultMaterial() get a single camera and a
556 // custom mvp; make sure multiview is disabled in the shader generator using
557 // the common flag, instead of it having to write logic for checking for
558 // OrthoShadowPoss || CubeShadowPass.
560
561 const auto cubeFaceIdx = QSSGBaseTypeHelpers::indexOfCubeFace(cubeFace);
562 const auto &defaultMaterialShaderKeyProperties = inData.getDefaultMaterialPropertyTable();
564
565 for (const auto &handle : sortedOpaqueObjects) {
566 QSSGRenderableObject *theObject = handle.obj;
567 QSSG_ASSERT(theObject->renderableFlags.castsShadows(), continue);
568
569 QSSGShaderFeatures objectFeatureSet = featureSet;
570 const bool isOpaqueDepthPrePass = theObject->depthWriteMode == QSSGDepthDrawMode::OpaquePrePass;
571 if (isOpaqueDepthPrePass)
573
574 QSSGRhiDrawCallData *dcd = nullptr;
575 QMatrix4x4 modelViewProjection;
576 QSSGSubsetRenderable &renderable(static_cast<QSSGSubsetRenderable &>(*theObject));
577 if (theObject->type == QSSGRenderableObject::Type::DefaultMaterialMeshSubset || theObject->type == QSSGRenderableObject::Type::CustomMaterialMeshSubset) {
578 const bool hasSkinning = defaultMaterialShaderKeyProperties.m_boneCount.getValue(renderable.shaderDescription) > 0;
579 modelViewProjection = hasSkinning ? pEntry->m_lightVP
580 : pEntry->m_lightVP * renderable.globalTransform;
581 const quintptr entryIdx = quintptr(cubeFace != QSSGRenderTextureCubeFaceNone) * (cubeFaceIdx + (quintptr(renderable.subset.offset) << 3));
582 dcd = &rhiCtxD->drawCallData({ passKey, &renderable.modelContext.model,
583 pEntry, entryIdx });
584 }
585
587 QSSGRhiShaderPipelinePtr shaderPipeline;
588 QSSGSubsetRenderable &subsetRenderable(static_cast<QSSGSubsetRenderable &>(*theObject));
589 if (theObject->type == QSSGSubsetRenderable::Type::DefaultMaterialMeshSubset) {
590 const auto &material = static_cast<const QSSGRenderDefaultMaterial &>(subsetRenderable.getMaterial());
591 ps->cullMode = QSSGRhiHelpers::toCullMode(material.cullMode);
592 const bool blendParticles = defaultMaterialShaderKeyProperties.m_blendParticles.getValue(subsetRenderable.shaderDescription);
593
594 shaderPipeline = shadersForDefaultMaterial(ps, subsetRenderable, objectFeatureSet);
595 if (!shaderPipeline)
596 continue;
597 shaderPipeline->ensureCombinedMainLightsUniformBuffer(&dcd->ubuf);
598 char *ubufData = dcd->ubuf->beginFullDynamicBufferUpdateForCurrentFrame();
599 // calls updateUni with an alteredCamera and alteredModelViewProjection
600 QSSGRenderCameraList cameras({ &inCamera });
601 updateUniformsForDefaultMaterial(*shaderPipeline, rhiCtx, inData, ubufData, ps, subsetRenderable, cameras, depthAdjust, &modelViewProjection);
602 if (blendParticles)
603 QSSGParticleRenderer::updateUniformsForParticleModel(*shaderPipeline, ubufData, &subsetRenderable.modelContext.model, subsetRenderable.subset.offset);
604 dcd->ubuf->endFullDynamicBufferUpdateForCurrentFrame();
605 if (blendParticles)
606 QSSGParticleRenderer::prepareParticlesForModel(*shaderPipeline, rhiCtx, bindings, &subsetRenderable.modelContext.model);
607 } else if (theObject->type == QSSGSubsetRenderable::Type::CustomMaterialMeshSubset) {
608 const auto &material = static_cast<const QSSGRenderCustomMaterial &>(subsetRenderable.getMaterial());
609 ps->cullMode = QSSGRhiHelpers::toCullMode(material.m_cullMode);
610
611 QSSGCustomMaterialSystem &customMaterialSystem(*subsetRenderable.renderer->contextInterface()->customMaterialSystem().get());
612 shaderPipeline = customMaterialSystem.shadersForCustomMaterial(ps, material, subsetRenderable, inData.getDefaultMaterialPropertyTable(), objectFeatureSet);
613 if (!shaderPipeline)
614 continue;
615 shaderPipeline->ensureCombinedMainLightsUniformBuffer(&dcd->ubuf);
616 char *ubufData = dcd->ubuf->beginFullDynamicBufferUpdateForCurrentFrame();
617 // inCamera is the shadow camera, not the same as inData.renderedCameras
618 QSSGRenderCameraList cameras({ &inCamera });
619 customMaterialSystem.updateUniformsForCustomMaterial(*shaderPipeline, rhiCtx, inData, ubufData, ps, material, subsetRenderable,
620 cameras, depthAdjust, &modelViewProjection);
621 dcd->ubuf->endFullDynamicBufferUpdateForCurrentFrame();
622 }
623
624 if (theObject->type == QSSGRenderableObject::Type::DefaultMaterialMeshSubset || theObject->type == QSSGRenderableObject::Type::CustomMaterialMeshSubset) {
625
628 ia = subsetRenderable.subset.rhi.ia;
629 const QSSGRenderCameraDataList &cameraDatas(*inData.renderedCameraData);
630 int instanceBufferBinding = setupInstancing(&subsetRenderable, ps, rhiCtx, cameraDatas[0].direction, cameraDatas[0].position);
631 QSSGRhiHelpers::bakeVertexInputLocations(&ia, *shaderPipeline, instanceBufferBinding);
632
633
634 bindings.addUniformBuffer(0, RENDERER_VISIBILITY_ALL, dcd->ubuf);
635
636 // Depth and SSAO textures, in case a custom material's shader code does something with them.
637 addDepthTextureBindings(rhiCtx, shaderPipeline.get(), bindings);
638
639 if (isOpaqueDepthPrePass) {
641 shaderPipeline.get(),
642 subsetRenderable.firstImage,
643 bindings,
644 (theObject->type == QSSGRenderableObject::Type::CustomMaterialMeshSubset));
645 }
646
647 // There is no screen texture at this stage. But the shader from a
648 // custom material may rely on it, and an object with that material
649 // can end up in the shadow map's object list. So bind a dummy
650 // texture then due to the lack of other options.
651 const int screenTextureBinding = shaderPipeline->bindingForTexture("qt_screenTexture", int(QSSGRhiSamplerBindingHints::ScreenTexture));
652 const int screenTextureArrayBinding = shaderPipeline->bindingForTexture("qt_screenTextureArray", int(QSSGRhiSamplerBindingHints::ScreenTextureArray));
653 if (screenTextureBinding >= 0 || screenTextureArrayBinding >= 0) {
656 if (screenTextureBinding >= 0) {
657 QRhiResourceUpdateBatch *resourceUpdates = rhiCtx->rhi()->nextResourceUpdateBatch();
658 QRhiTexture *dummyTexture = rhiCtx->dummyTexture({}, resourceUpdates);
659 rhiCtx->commandBuffer()->resourceUpdate(resourceUpdates);
660 bindings.addTexture(screenTextureBinding,
662 dummyTexture, sampler);
663 }
664 if (screenTextureArrayBinding >= 0) {
665 QRhiResourceUpdateBatch *resourceUpdates = rhiCtx->rhi()->nextResourceUpdateBatch();
666 QRhiTexture *dummyTexture = rhiCtx->dummyTexture({}, resourceUpdates, QSize(64, 64), Qt::black, rhiCtx->mainPassViewCount());
667 rhiCtx->commandBuffer()->resourceUpdate(resourceUpdates);
668 bindings.addTexture(screenTextureArrayBinding,
670 dummyTexture, sampler);
671 }
672 }
673
674 QRhiShaderResourceBindings *srb = rhiCtxD->srb(bindings);
675 subsetRenderable.rhiRenderData.shadowPass.pipeline = rhiCtxD->pipeline(*ps,
676 pEntry->m_rhiRenderPassDesc,
677 srb);
678 subsetRenderable.rhiRenderData.shadowPass.srb[cubeFaceIdx] = srb;
679 }
680 }
681}
682
684{
685 // Assuming default values in the other TargetBlend fields
686 switch (materialBlend) {
689 targetBlend->dstColor = QRhiGraphicsPipeline::One;
690 targetBlend->srcAlpha = QRhiGraphicsPipeline::One;
691 targetBlend->dstAlpha = QRhiGraphicsPipeline::One;
692 break;
696 targetBlend->srcAlpha = QRhiGraphicsPipeline::One;
697 targetBlend->dstAlpha = QRhiGraphicsPipeline::One;
698 break;
699 default:
700 // Use SourceOver for everything else
703 targetBlend->srcAlpha = QRhiGraphicsPipeline::One;
705 break;
706 }
707}
708
710 QSSGPassKey passKey,
711 const QSSGLayerRenderData &inData,
712 QSSGRenderableObject &inObject,
713 QRhiRenderPassDescriptor *renderPassDescriptor,
715 QSSGShaderFeatures featureSet,
716 int samples,
717 int viewCount,
718 QSSGRenderCamera *alteredCamera,
719 QMatrix4x4 *alteredModelViewProjection,
722{
723 const auto &defaultMaterialShaderKeyProperties = inData.getDefaultMaterialPropertyTable();
724
725 switch (inObject.type) {
726 case QSSGRenderableObject::Type::DefaultMaterialMeshSubset:
727 {
728 QSSGSubsetRenderable &subsetRenderable(static_cast<QSSGSubsetRenderable &>(inObject));
729
730 if ((cubeFace == QSSGRenderTextureCubeFaceNone) && subsetRenderable.reflectionProbeIndex >= 0 && subsetRenderable.renderableFlags.testFlag(QSSGRenderableObjectFlag::ReceivesReflections))
732
733 if ((cubeFace != QSSGRenderTextureCubeFaceNone)) {
734 // Disable tonemapping for the reflection pass
735 featureSet.disableTonemapping();
736 }
737
738 if (subsetRenderable.renderableFlags.rendersWithLightmap())
740
741 const auto &shaderPipeline = shadersForDefaultMaterial(ps, subsetRenderable, featureSet);
742 if (shaderPipeline) {
743 // Unlike the subsetRenderable (which is allocated per frame so is
744 // not persistent in any way), the model reference is persistent in
745 // the sense that it references the model node in the scene graph.
746 // Combined with the layer node (multiple View3Ds may share the
747 // same scene!), this is suitable as a key to get the uniform
748 // buffers that were used with the rendering of the same model in
749 // the previous frame.
751 const auto &modelNode = subsetRenderable.modelContext.model;
752 const bool blendParticles = defaultMaterialShaderKeyProperties.m_blendParticles.getValue(subsetRenderable.shaderDescription);
753
754
755 // NOTE:
756 // - entryIdx should 0 for QSSGRenderTextureCubeFaceNone.
757 // In all other cases the entryIdx is a combination of the cubeface idx and the subset offset, where the lower bits
758 // are the cubeface idx.
759 const auto cubeFaceIdx = QSSGBaseTypeHelpers::indexOfCubeFace(cubeFace);
760 const quintptr entryIdx = quintptr(cubeFace != QSSGRenderTextureCubeFaceNone) * (cubeFaceIdx + (quintptr(subsetRenderable.subset.offset) << 3));
761 // If there's an entry we merge that with the address of the material
762 const auto entryPartA = reinterpret_cast<quintptr>(&subsetRenderable.material);
763 const auto entryPartB = reinterpret_cast<quintptr>(entry);
764 const void *entryId = reinterpret_cast<const void *>(entryPartA ^ entryPartB);
765
767 QSSGRhiDrawCallData &dcd = rhiCtxD->drawCallData({ passKey, &modelNode, entryId, entryIdx });
768
769 shaderPipeline->ensureCombinedMainLightsUniformBuffer(&dcd.ubuf);
771 if (alteredCamera) {
772 Q_ASSERT(alteredModelViewProjection);
773 QSSGRenderCameraList cameras({ alteredCamera });
774 updateUniformsForDefaultMaterial(*shaderPipeline, rhiCtx, inData, ubufData, ps, subsetRenderable, cameras, nullptr, alteredModelViewProjection);
775 } else {
776 Q_ASSERT(!alteredModelViewProjection);
777 updateUniformsForDefaultMaterial(*shaderPipeline, rhiCtx, inData, ubufData, ps, subsetRenderable, inData.renderedCameras, nullptr, nullptr);
778 }
779
780 if (blendParticles)
781 QSSGParticleRenderer::updateUniformsForParticleModel(*shaderPipeline, ubufData, &subsetRenderable.modelContext.model, subsetRenderable.subset.offset);
782 dcd.ubuf->endFullDynamicBufferUpdateForCurrentFrame();
783
784 if (blendParticles)
785 QSSGParticleRenderer::prepareParticlesForModel(*shaderPipeline, rhiCtx, bindings, &subsetRenderable.modelContext.model);
786
787 // Skinning
788 if (QRhiTexture *boneTexture = inData.getBonemapTexture(subsetRenderable.modelContext)) {
789 int binding = shaderPipeline->bindingForTexture("qt_boneTexture");
790 if (binding >= 0) {
791 QRhiSampler *boneSampler = rhiCtx->sampler({ QRhiSampler::Nearest,
797 });
798 bindings.addTexture(binding,
800 boneTexture,
801 boneSampler);
802 }
803 }
804 // Morphing
805 auto *targetsTexture = subsetRenderable.subset.rhi.targetsTexture;
806 if (targetsTexture) {
807 int binding = shaderPipeline->bindingForTexture("qt_morphTargetTexture");
808 if (binding >= 0) {
809 QRhiSampler *targetsSampler = rhiCtx->sampler({ QRhiSampler::Nearest,
815 });
816 bindings.addTexture(binding, QRhiShaderResourceBinding::VertexStage, subsetRenderable.subset.rhi.targetsTexture, targetsSampler);
817 }
818 }
819
820 ps->samples = samples;
821 ps->viewCount = viewCount;
822
823 const auto &material = static_cast<const QSSGRenderDefaultMaterial &>(subsetRenderable.getMaterial());
824 ps->cullMode = QSSGRhiHelpers::toCullMode(material.cullMode);
825 fillTargetBlend(&ps->targetBlend, material.blendMode);
826
828
829 ia = subsetRenderable.subset.rhi.ia;
830 const QSSGRenderCameraDataList &cameraDatas(*inData.renderedCameraData);
831 QVector3D cameraDirection = cameraDatas[0].direction;
832 if (alteredCamera)
833 cameraDirection = alteredCamera->getScalingCorrectDirection();
834 QVector3D cameraPosition = cameraDatas[0].position;
835 if (alteredCamera)
836 cameraPosition = alteredCamera->getGlobalPos();
837 int instanceBufferBinding = setupInstancing(&subsetRenderable, ps, rhiCtx, cameraDirection, cameraPosition);
838 QSSGRhiHelpers::bakeVertexInputLocations(&ia, *shaderPipeline, instanceBufferBinding);
839
840 bindings.addUniformBuffer(0, RENDERER_VISIBILITY_ALL, dcd.ubuf, 0, shaderPipeline->ub0Size());
841
842 if (shaderPipeline->isLightingEnabled()) {
843 bindings.addUniformBuffer(1, RENDERER_VISIBILITY_ALL, dcd.ubuf,
844 shaderPipeline->ub0LightDataOffset(),
845 shaderPipeline->ub0LightDataSize());
846 }
847
848 // Texture maps
849 QSSGRenderableImage *renderableImage = subsetRenderable.firstImage;
850 while (renderableImage) {
851 const char *samplerName = QSSGMaterialShaderGenerator::getSamplerName(renderableImage->m_mapType);
852 const int samplerHint = int(renderableImage->m_mapType);
853 int samplerBinding = shaderPipeline->bindingForTexture(samplerName, samplerHint);
854 if (samplerBinding >= 0) {
855 QRhiTexture *texture = renderableImage->m_texture.m_texture;
856 if (samplerBinding >= 0 && texture) {
857 const bool mipmapped = texture->flags().testFlag(QRhiTexture::MipMapped);
858 QSSGRhiSamplerDescription samplerDesc = {
859 QSSGRhiHelpers::toRhi(renderableImage->m_imageNode.m_minFilterType),
860 QSSGRhiHelpers::toRhi(renderableImage->m_imageNode.m_magFilterType),
861 mipmapped ? QSSGRhiHelpers::toRhi(renderableImage->m_imageNode.m_mipFilterType) : QRhiSampler::None,
862 QSSGRhiHelpers::toRhi(renderableImage->m_imageNode.m_horizontalTilingMode),
863 QSSGRhiHelpers::toRhi(renderableImage->m_imageNode.m_verticalTilingMode),
864 QSSGRhiHelpers::toRhi(renderableImage->m_imageNode.m_depthTilingMode)
865 };
866 rhiCtx->checkAndAdjustForNPoT(texture, &samplerDesc);
867 QRhiSampler *sampler = rhiCtx->sampler(samplerDesc);
868 bindings.addTexture(samplerBinding, RENDERER_VISIBILITY_ALL, texture, sampler);
869 }
870 } // else this is not necessarily an error, e.g. having metalness/roughness maps with metalness disabled
871 renderableImage = renderableImage->m_nextImage;
872 }
873
874 if (shaderPipeline->isLightingEnabled()) {
875 // Shadow map textures
876 const int shadowMapCount = shaderPipeline->shadowMapCount();
877 for (int i = 0; i < shadowMapCount; ++i) {
878 QSSGRhiShadowMapProperties &shadowMapProperties(shaderPipeline->shadowMapAt(i));
879 QRhiTexture *texture = shadowMapProperties.shadowMapTexture;
883 const QByteArray &name(shadowMapProperties.shadowMapTextureUniformName);
884 if (shadowMapProperties.cachedBinding < 0)
885 shadowMapProperties.cachedBinding = shaderPipeline->bindingForTexture(name);
886 if (shadowMapProperties.cachedBinding < 0) {
887 qWarning("No combined image sampler for shadow map texture '%s'", name.data());
888 continue;
889 }
890 bindings.addTexture(shadowMapProperties.cachedBinding, QRhiShaderResourceBinding::FragmentStage,
892 }
893
894 // Prioritize reflection texture over Light Probe texture because
895 // reflection texture also contains the irradiance and pre filtered
896 // values for the light probe.
898 int reflectionSampler = shaderPipeline->bindingForTexture("qt_reflectionMap");
901 QRhiTexture* reflectionTexture = inData.getReflectionMapManager()->reflectionMapEntry(subsetRenderable.reflectionProbeIndex)->m_rhiPrefilteredCube;
902 if (reflectionSampler >= 0 && reflectionTexture)
903 bindings.addTexture(reflectionSampler, QRhiShaderResourceBinding::FragmentStage, reflectionTexture, sampler);
904 } else if (shaderPipeline->lightProbeTexture()) {
905 int binding = shaderPipeline->bindingForTexture("qt_lightProbe", int(QSSGRhiSamplerBindingHints::LightProbe));
906 if (binding >= 0) {
907 auto tiling = shaderPipeline->lightProbeTiling();
909 QSSGRhiHelpers::toRhi(tiling.first), QSSGRhiHelpers::toRhi(tiling.second), QRhiSampler::Repeat });
911 shaderPipeline->lightProbeTexture(), sampler);
912 } else {
913 qWarning("Could not find sampler for lightprobe");
914 }
915 }
916
917 // Screen Texture
918 if (shaderPipeline->screenTexture()) {
919 const int screenTextureBinding = shaderPipeline->bindingForTexture("qt_screenTexture", int(QSSGRhiSamplerBindingHints::ScreenTexture));
920 const int screenTextureArrayBinding = shaderPipeline->bindingForTexture("qt_screenTextureArray", int(QSSGRhiSamplerBindingHints::ScreenTextureArray));
921 if (screenTextureBinding >= 0 || screenTextureArrayBinding >= 0) {
922 // linear min/mag, mipmap filtering depends on the
923 // texture, with SCREEN_TEXTURE there are no mipmaps, but
924 // once SCREEN_MIP_TEXTURE is seen the texture (the same
925 // one) has mipmaps generated.
926 QRhiSampler::Filter mipFilter = shaderPipeline->screenTexture()->flags().testFlag(QRhiTexture::MipMapped)
930 if (screenTextureBinding >= 0) {
931 bindings.addTexture(screenTextureBinding,
933 shaderPipeline->screenTexture(), sampler);
934 }
935 if (screenTextureArrayBinding >= 0) {
936 bindings.addTexture(screenTextureArrayBinding,
938 shaderPipeline->screenTexture(), sampler);
939 }
940 } // else ignore, not an error
941 }
942
943 if (shaderPipeline->lightmapTexture()) {
944 int binding = shaderPipeline->bindingForTexture("qt_lightmap", int(QSSGRhiSamplerBindingHints::LightmapTexture));
945 if (binding >= 0) {
948 bindings.addTexture(binding,
950 shaderPipeline->lightmapTexture(), sampler);
951 } // else ignore, not an error
952 }
953 }
954
955 // Depth and SSAO textures
956 addDepthTextureBindings(rhiCtx, shaderPipeline.get(), bindings);
957
958 // Instead of always doing a QHash find in srb(), store the binding
959 // list and the srb object in the per-model+material
960 // QSSGRhiUniformBufferSet. While this still needs comparing the
961 // binding list, to see if something has changed, it results in
962 // significant gains with lots of models in the scene (because the
963 // srb hash table becomes large then, so avoiding the lookup as
964 // much as possible is helpful)
965 QRhiShaderResourceBindings *&srb = dcd.srb;
966 bool srbChanged = false;
967 if (!srb || bindings != dcd.bindings) {
968 srb = rhiCtxD->srb(bindings);
969 dcd.bindings = bindings;
970 srbChanged = true;
971 }
972
973 if (cubeFace != QSSGRenderTextureCubeFaceNone)
974 subsetRenderable.rhiRenderData.reflectionPass.srb[cubeFaceIdx] = srb;
975 else
976 subsetRenderable.rhiRenderData.mainPass.srb = srb;
977
978 const auto pipelineKey = QSSGGraphicsPipelineStateKey::create(*ps, renderPassDescriptor, srb);
979 if (dcd.pipeline
980 && !srbChanged
981 && dcd.renderTargetDescriptionHash == pipelineKey.extra.renderTargetDescriptionHash // we have the hash code anyway, use it to early out upon mismatch
982 && dcd.renderTargetDescription == pipelineKey.renderTargetDescription
983 && dcd.ps == *ps)
984 {
985 if (cubeFace != QSSGRenderTextureCubeFaceNone)
986 subsetRenderable.rhiRenderData.reflectionPass.pipeline = dcd.pipeline;
987 else
988 subsetRenderable.rhiRenderData.mainPass.pipeline = dcd.pipeline;
989 } else {
990 if (cubeFace != QSSGRenderTextureCubeFaceNone) {
991 subsetRenderable.rhiRenderData.reflectionPass.pipeline = rhiCtxD->pipeline(pipelineKey,
992 renderPassDescriptor,
993 srb);
994 dcd.pipeline = subsetRenderable.rhiRenderData.reflectionPass.pipeline;
995 } else {
996 subsetRenderable.rhiRenderData.mainPass.pipeline = rhiCtxD->pipeline(pipelineKey,
997 renderPassDescriptor,
998 srb);
999 dcd.pipeline = subsetRenderable.rhiRenderData.mainPass.pipeline;
1000 }
1001 dcd.renderTargetDescriptionHash = pipelineKey.extra.renderTargetDescriptionHash;
1002 dcd.renderTargetDescription = pipelineKey.renderTargetDescription;
1003 dcd.ps = *ps;
1004 }
1005 }
1006 break;
1007 }
1008 case QSSGRenderableObject::Type::CustomMaterialMeshSubset:
1009 {
1010 QSSGSubsetRenderable &subsetRenderable(static_cast<QSSGSubsetRenderable &>(inObject));
1011 const QSSGRenderCustomMaterial &material = static_cast<const QSSGRenderCustomMaterial &>(subsetRenderable.getMaterial());
1012 QSSGCustomMaterialSystem &customMaterialSystem(*subsetRenderable.renderer->contextInterface()->customMaterialSystem().get());
1013
1014 featureSet.set(QSSGShaderFeatures::Feature::LightProbe, inData.layer.lightProbe || material.m_iblProbe);
1015
1016 if ((cubeFace == QSSGRenderTextureCubeFaceNone) && subsetRenderable.reflectionProbeIndex >= 0 && subsetRenderable.renderableFlags.testFlag(QSSGRenderableObjectFlag::ReceivesReflections))
1018
1019 if (cubeFace != QSSGRenderTextureCubeFaceNone) {
1020 // Disable tonemapping for the reflection pass
1021 featureSet.disableTonemapping();
1022 }
1023
1024 if (subsetRenderable.renderableFlags.rendersWithLightmap())
1026
1027 customMaterialSystem.rhiPrepareRenderable(ps, passKey, subsetRenderable, featureSet,
1028 material, inData, renderPassDescriptor, samples, viewCount,
1029 alteredCamera, cubeFace, alteredModelViewProjection, entry);
1030 break;
1031 }
1032 case QSSGRenderableObject::Type::Particles:
1033 {
1034 QSSGParticlesRenderable &particleRenderable(static_cast<QSSGParticlesRenderable &>(inObject));
1035 const auto &shaderPipeline = shadersForParticleMaterial(ps, particleRenderable);
1036 if (shaderPipeline) {
1037 QSSGParticleRenderer::rhiPrepareRenderable(*shaderPipeline, passKey, rhiCtx, ps, particleRenderable, inData, renderPassDescriptor, samples, viewCount,
1038 alteredCamera, cubeFace, entry);
1039 }
1040 break;
1041 }
1042 }
1043}
1044
1047 QSSGRenderableObject &object,
1048 bool *needsSetViewport,
1050{
1051 switch (object.type) {
1052 case QSSGRenderableObject::Type::DefaultMaterialMeshSubset:
1053 {
1054 QSSGSubsetRenderable &subsetRenderable(static_cast<QSSGSubsetRenderable &>(object));
1055
1056 QRhiGraphicsPipeline *ps = subsetRenderable.rhiRenderData.mainPass.pipeline;
1057 QRhiShaderResourceBindings *srb = subsetRenderable.rhiRenderData.mainPass.srb;
1058
1059 if (cubeFace != QSSGRenderTextureCubeFaceNone) {
1060 const auto cubeFaceIdx = QSSGBaseTypeHelpers::indexOfCubeFace(cubeFace);
1061 ps = subsetRenderable.rhiRenderData.reflectionPass.pipeline;
1062 srb = subsetRenderable.rhiRenderData.reflectionPass.srb[cubeFaceIdx];
1063 }
1064
1065 if (!ps || !srb)
1066 return;
1067
1068 QRhiBuffer *vertexBuffer = subsetRenderable.subset.rhi.vertexBuffer->buffer();
1069 QRhiBuffer *indexBuffer = subsetRenderable.subset.rhi.indexBuffer ? subsetRenderable.subset.rhi.indexBuffer->buffer() : nullptr;
1070
1071 QRhiCommandBuffer *cb = rhiCtx->commandBuffer();
1072 // QRhi optimizes out unnecessary binding of the same pipline
1073 cb->setGraphicsPipeline(ps);
1074 cb->setShaderResources(srb);
1075
1076 if (*needsSetViewport) {
1077 cb->setViewport(state.viewport);
1079 cb->setScissor(state.scissor);
1080 *needsSetViewport = false;
1081 }
1082
1083 QRhiCommandBuffer::VertexInput vertexBuffers[2];
1084 int vertexBufferCount = 1;
1085 vertexBuffers[0] = QRhiCommandBuffer::VertexInput(vertexBuffer, 0);
1086 quint32 instances = 1;
1087 if ( subsetRenderable.modelContext.model.instancing()) {
1088 instances = subsetRenderable.modelContext.model.instanceCount();
1089 // If the instance count is 0, the bail out before trying to do any
1090 // draw calls. Making an instanced draw call with a count of 0 is invalid
1091 // for Metal and likely other API's as well.
1092 // It is possible that the particale system may produce 0 instances here
1093 if (instances == 0)
1094 return;
1095 vertexBuffers[1] = QRhiCommandBuffer::VertexInput(subsetRenderable.instanceBuffer, 0);
1097 }
1098 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderCall);
1100 cb->setStencilRef(state.stencilRef);
1101 if (indexBuffer) {
1102 cb->setVertexInput(0, vertexBufferCount, vertexBuffers, indexBuffer, 0, subsetRenderable.subset.rhi.indexBuffer->indexFormat());
1103 cb->drawIndexed(subsetRenderable.subset.lodCount(subsetRenderable.subsetLevelOfDetail), instances, subsetRenderable.subset.lodOffset(subsetRenderable.subsetLevelOfDetail));
1104 QSSGRHICTX_STAT(rhiCtx, drawIndexed(subsetRenderable.subset.lodCount(subsetRenderable.subsetLevelOfDetail), instances));
1105 } else {
1106 cb->setVertexInput(0, vertexBufferCount, vertexBuffers);
1107 cb->draw(subsetRenderable.subset.count, instances, subsetRenderable.subset.offset);
1108 QSSGRHICTX_STAT(rhiCtx, draw(subsetRenderable.subset.count, instances));
1109 }
1110 Q_QUICK3D_PROFILE_END_WITH_IDS(QQuick3DProfiler::Quick3DRenderCall, (subsetRenderable.subset.count | quint64(instances) << 32),
1111 QVector<int>({subsetRenderable.modelContext.model.profilingId,
1112 subsetRenderable.material.profilingId}));
1113 break;
1114 }
1115 case QSSGRenderableObject::Type::CustomMaterialMeshSubset:
1116 {
1117 QSSGSubsetRenderable &subsetRenderable(static_cast<QSSGSubsetRenderable &>(object));
1118 QSSGCustomMaterialSystem &customMaterialSystem(*subsetRenderable.renderer->contextInterface()->customMaterialSystem().get());
1119 customMaterialSystem.rhiRenderRenderable(rhiCtx, subsetRenderable, needsSetViewport, cubeFace, state);
1120 break;
1121 }
1122 case QSSGRenderableObject::Type::Particles:
1123 {
1124 QSSGParticlesRenderable &renderable(static_cast<QSSGParticlesRenderable &>(object));
1125 QSSGParticleRenderer::rhiRenderRenderable(rhiCtx, renderable, needsSetViewport, cubeFace, state);
1126 break;
1127 }
1128 }
1129}
1130
1132 QSSGPassKey passKey,
1134 QSSGRenderShadowMap &shadowMapManager,
1135 const QSSGRenderCamera &camera,
1136 const QSSGShaderLightList &globalLights,
1137 const QSSGRenderableObjectList &sortedOpaqueObjects,
1139 const QSSGBoxPoints &castingObjectsBox,
1140 const QSSGBoxPoints &receivingObjectsBox)
1141{
1143
1144 static const auto rhiRenderOneShadowMap = [](QSSGRhiContext *rhiCtx,
1146 const QSSGRenderableObjectList &sortedOpaqueObjects,
1147 int cubeFace) {
1148 QRhiCommandBuffer *cb = rhiCtx->commandBuffer();
1149 bool needsSetViewport = true;
1150
1151 for (const auto &handle : sortedOpaqueObjects) {
1152 QSSGRenderableObject *theObject = handle.obj;
1153 QSSG_ASSERT(theObject->renderableFlags.castsShadows(), continue);
1154 if (theObject->type == QSSGRenderableObject::Type::DefaultMaterialMeshSubset || theObject->type == QSSGRenderableObject::Type::CustomMaterialMeshSubset) {
1155 QSSGSubsetRenderable *renderable(static_cast<QSSGSubsetRenderable *>(theObject));
1156
1157 QRhiBuffer *vertexBuffer = renderable->subset.rhi.vertexBuffer->buffer();
1158 QRhiBuffer *indexBuffer = renderable->subset.rhi.indexBuffer
1159 ? renderable->subset.rhi.indexBuffer->buffer()
1160 : nullptr;
1161
1162 // Ideally we shouldn't need to deal with this, as only "valid" objects should be processed at this point.
1163 if (!renderable->rhiRenderData.shadowPass.pipeline)
1164 continue;
1165
1166 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderCall);
1167
1168 cb->setGraphicsPipeline(renderable->rhiRenderData.shadowPass.pipeline);
1169
1170 QRhiShaderResourceBindings *srb = renderable->rhiRenderData.shadowPass.srb[cubeFace];
1171 cb->setShaderResources(srb);
1172
1173 if (needsSetViewport) {
1174 cb->setViewport(ps->viewport);
1175 needsSetViewport = false;
1176 }
1177
1178 QRhiCommandBuffer::VertexInput vertexBuffers[2];
1179 int vertexBufferCount = 1;
1180 vertexBuffers[0] = QRhiCommandBuffer::VertexInput(vertexBuffer, 0);
1181 quint32 instances = 1;
1182 if (renderable->modelContext.model.instancing()) {
1183 instances = renderable->modelContext.model.instanceCount();
1184 vertexBuffers[1] = QRhiCommandBuffer::VertexInput(renderable->instanceBuffer, 0);
1186 }
1187 if (indexBuffer) {
1188 cb->setVertexInput(0, vertexBufferCount, vertexBuffers, indexBuffer, 0, renderable->subset.rhi.indexBuffer->indexFormat());
1189 cb->drawIndexed(renderable->subset.count, instances, renderable->subset.offset);
1190 QSSGRHICTX_STAT(rhiCtx, drawIndexed(renderable->subset.count, instances));
1191 } else {
1192 cb->setVertexInput(0, vertexBufferCount, vertexBuffers);
1193 cb->draw(renderable->subset.count, instances, renderable->subset.offset);
1194 QSSGRHICTX_STAT(rhiCtx, draw(renderable->subset.count, instances));
1195 }
1196 Q_QUICK3D_PROFILE_END_WITH_IDS(QQuick3DProfiler::Quick3DRenderCall, (renderable->subset.count | quint64(instances) << 32),
1197 QVector<int>({renderable->modelContext.model.profilingId,
1198 renderable->material.profilingId}));
1199 }
1200 }
1201 };
1202
1203 static const auto rhiBlurShadowMap = [](QSSGRhiContext *rhiCtx,
1204 QSSGShadowMapEntry *pEntry,
1206 float shadowFilter,
1207 float shadowMapFar,
1208 bool orthographic) {
1209 // may not be able to do the blur pass if the number of max color
1210 // attachments is the gl/vk spec mandated minimum of 4, and we need 6.
1211 // (applicable only to !orthographic, whereas orthographic always works)
1212 if (!pEntry->m_rhiBlurRenderTarget0 || !pEntry->m_rhiBlurRenderTarget1)
1213 return;
1214
1215 QRhi *rhi = rhiCtx->rhi();
1217 QRhiTexture *map = orthographic ? pEntry->m_rhiDepthMap : pEntry->m_rhiDepthCube;
1218 QRhiTexture *workMap = orthographic ? pEntry->m_rhiDepthCopy : pEntry->m_rhiCubeCopy;
1219 const QSize size = map->pixelSize();
1220 ps.viewport = QRhiViewport(0, 0, float(size.width()), float(size.height()));
1221
1222 const auto &shaderCache = renderer.contextInterface()->shaderCache();
1223
1224 const auto &blurXPipeline = orthographic ? shaderCache->getBuiltInRhiShaders().getRhiOrthographicShadowBlurXShader()
1225 : shaderCache->getBuiltInRhiShaders().getRhiCubemapShadowBlurXShader();
1226 if (!blurXPipeline)
1227 return;
1229
1230 ps.colorAttachmentCount = orthographic ? 1 : 6;
1231
1233
1234 // construct a key that is unique for this frame (we use a dynamic buffer
1235 // so even if the same key gets used in the next frame, just updating the
1236 // contents on the same QRhiBuffer is ok due to QRhi's internal double buffering)
1237 QSSGRhiDrawCallData &dcd = rhiCtxD->drawCallData({ map, nullptr, nullptr, 0 });
1238 if (!dcd.ubuf) {
1240 dcd.ubuf->create();
1241 }
1242
1243 // the blur also needs Y reversed in order to get correct results (while
1244 // the second blur step would end up with the correct orientation without
1245 // this too, but we need to blur the correct fragments in the second step
1246 // hence the flip is important)
1247 QMatrix4x4 flipY;
1248 // correct for D3D and Metal but not for Vulkan because there the Y is down
1249 // in NDC so that kind of self-corrects...
1250 if (rhi->isYUpInFramebuffer() != rhi->isYUpInNDC())
1251 flipY.data()[5] = -1.0f;
1252 float cameraProperties[2] = { shadowFilter, shadowMapFar };
1253 char *ubufData = dcd.ubuf->beginFullDynamicBufferUpdateForCurrentFrame();
1254 memcpy(ubufData, flipY.constData(), 64);
1255 memcpy(ubufData + 64, cameraProperties, 8);
1256 dcd.ubuf->endFullDynamicBufferUpdateForCurrentFrame();
1257
1261
1263 bindings.addUniformBuffer(0, RENDERER_VISIBILITY_ALL, dcd.ubuf);
1265 QRhiShaderResourceBindings *srb = rhiCtxD->srb(bindings);
1266
1267 QSSGRhiQuadRenderer::Flags quadFlags;
1268 if (orthographic) // orthoshadowshadowblurx and y have attr_uv as well
1269 quadFlags |= QSSGRhiQuadRenderer::UvCoords;
1270 renderer.rhiQuadRenderer()->prepareQuad(rhiCtx, nullptr);
1271 renderer.rhiQuadRenderer()->recordRenderQuadPass(rhiCtx, &ps, srb, pEntry->m_rhiBlurRenderTarget0, quadFlags);
1272
1273 // repeat for blur Y, now depthCopy -> depthMap or cubeCopy -> depthCube
1274
1275 const auto &blurYPipeline = orthographic ? shaderCache->getBuiltInRhiShaders().getRhiOrthographicShadowBlurYShader()
1276 : shaderCache->getBuiltInRhiShaders().getRhiCubemapShadowBlurYShader();
1277 if (!blurYPipeline)
1278 return;
1280
1281 bindings.clear();
1282 bindings.addUniformBuffer(0, RENDERER_VISIBILITY_ALL, dcd.ubuf);
1284 srb = rhiCtxD->srb(bindings);
1285
1286 renderer.rhiQuadRenderer()->prepareQuad(rhiCtx, nullptr);
1287 renderer.rhiQuadRenderer()->recordRenderQuadPass(rhiCtx, &ps, srb, pEntry->m_rhiBlurRenderTarget1, quadFlags);
1288 };
1289
1290 QRhi *rhi = rhiCtx->rhi();
1291 QRhiCommandBuffer *cb = rhiCtx->commandBuffer();
1292
1293 // We need to deal with a clip depth range of [0, 1] or
1294 // [-1, 1], depending on the graphics API underneath.
1295 QVector2D depthAdjust; // (d + depthAdjust[0]) * depthAdjust[1] = d mapped to [0, 1]
1296 if (rhi->isClipDepthZeroToOne()) {
1297 // d is [0, 1] so no need for any mapping
1298 depthAdjust[0] = 0.0f;
1299 depthAdjust[1] = 1.0f;
1300 } else {
1301 // d is [-1, 1]
1302 depthAdjust[0] = 1.0f;
1303 depthAdjust[1] = 0.5f;
1304 }
1305
1306 // Create shadow map for each light in the scene
1307 for (int i = 0, ie = globalLights.size(); i != ie; ++i) {
1308 if (!globalLights[i].shadows || globalLights[i].light->m_fullyBaked)
1309 continue;
1310
1311 QSSGShadowMapEntry *pEntry = shadowMapManager.shadowMapEntry(i);
1312 if (!pEntry)
1313 continue;
1314
1315 Q_ASSERT(pEntry->m_rhiDepthStencil);
1316 const bool orthographic = pEntry->m_rhiDepthMap && pEntry->m_rhiDepthCopy;
1317 if (orthographic) {
1318 const QSize size = pEntry->m_rhiDepthMap->pixelSize();
1319 ps.viewport = QRhiViewport(0, 0, float(size.width()), float(size.height()));
1320
1321 const auto &light = globalLights[i].light;
1322 const auto cameraType = (light->type == QSSGRenderLight::Type::DirectionalLight) ? QSSGRenderCamera::Type::OrthographicCamera : QSSGRenderCamera::Type::CustomCamera;
1323 QSSGRenderCamera theCamera(cameraType);
1324 setupCameraForShadowMap(camera, light, theCamera, castingObjectsBox, receivingObjectsBox);
1325 theCamera.calculateViewProjectionMatrix(pEntry->m_lightVP);
1326 pEntry->m_lightView = theCamera.globalTransform.inverted(); // pre-calculate this for the material
1327
1328 rhiPrepareResourcesForShadowMap(rhiCtx, layerData, passKey, pEntry, &ps, &depthAdjust,
1329 sortedOpaqueObjects, theCamera, true, QSSGRenderTextureCubeFaceNone);
1330
1331 // Render into the 2D texture pEntry->m_rhiDepthMap, using
1332 // pEntry->m_rhiDepthStencil as the (throwaway) depth/stencil buffer.
1333 QRhiTextureRenderTarget *rt = pEntry->m_rhiRenderTargets[0];
1334 cb->beginPass(rt, Qt::white, { 1.0f, 0 }, nullptr, rhiCtx->commonPassFlags());
1335 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderPass);
1336 QSSGRHICTX_STAT(rhiCtx, beginRenderPass(rt));
1337 rhiRenderOneShadowMap(rhiCtx, &ps, sortedOpaqueObjects, 0);
1338 cb->endPass();
1339 QSSGRHICTX_STAT(rhiCtx, endRenderPass());
1340 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DRenderPass, 0, QByteArrayLiteral("shadow_map"));
1341
1342 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderPass);
1343 rhiBlurShadowMap(rhiCtx, pEntry, renderer, globalLights[i].light->m_shadowFilter, globalLights[i].light->m_shadowMapFar, true);
1344 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DRenderPass, 0, QByteArrayLiteral("shadow_map_blur"));
1345 } else {
1346 Q_ASSERT(pEntry->m_rhiDepthCube && pEntry->m_rhiCubeCopy);
1347 const QSize size = pEntry->m_rhiDepthCube->pixelSize();
1348 ps.viewport = QRhiViewport(0, 0, float(size.width()), float(size.height()));
1349
1350 QSSGRenderCamera theCameras[6] { QSSGRenderCamera{QSSGRenderCamera::Type::PerspectiveCamera},
1351 QSSGRenderCamera{QSSGRenderCamera::Type::PerspectiveCamera},
1352 QSSGRenderCamera{QSSGRenderCamera::Type::PerspectiveCamera},
1353 QSSGRenderCamera{QSSGRenderCamera::Type::PerspectiveCamera},
1354 QSSGRenderCamera{QSSGRenderCamera::Type::PerspectiveCamera},
1355 QSSGRenderCamera{QSSGRenderCamera::Type::PerspectiveCamera} };
1356 setupCubeShadowCameras(globalLights[i].light, theCameras);
1357 pEntry->m_lightView = QMatrix4x4();
1358
1359 const bool swapYFaces = !rhi->isYUpInFramebuffer();
1360 for (const auto face : QSSGRenderTextureCubeFaces) {
1361 theCameras[quint8(face)].calculateViewProjectionMatrix(pEntry->m_lightVP);
1362 pEntry->m_lightCubeView[quint8(face)] = theCameras[quint8(face)].globalTransform.inverted(); // pre-calculate this for the material
1363
1364 rhiPrepareResourcesForShadowMap(rhiCtx, layerData, passKey, pEntry, &ps, &depthAdjust,
1365 sortedOpaqueObjects, theCameras[quint8(face)], false, face);
1366 }
1367
1368 for (const auto face : QSSGRenderTextureCubeFaces) {
1369 // Render into one face of the cubemap texture pEntry->m_rhiDephCube, using
1370 // pEntry->m_rhiDepthStencil as the (throwaway) depth/stencil buffer.
1371
1373 // FACE S T GL
1374 // +x -z, -y right
1375 // -x +z, -y left
1376 // +y +x, +z top
1377 // -y +x, -z bottom
1378 // +z +x, -y front
1379 // -z -x, -y back
1380 // FACE S T D3D
1381 // +x -z, +y right
1382 // -x +z, +y left
1383 // +y +x, -z bottom
1384 // -y +x, +z top
1385 // +z +x, +y front
1386 // -z -x, +y back
1387 if (swapYFaces) {
1388 // +Y and -Y faces get swapped (D3D, Vulkan, Metal).
1389 // See shadowMapping.glsllib. This is complemented there by reversing T as well.
1390 if (outFace == QSSGRenderTextureCubeFace::PosY)
1392 else if (outFace == QSSGRenderTextureCubeFace::NegY)
1394 }
1395 QRhiTextureRenderTarget *rt = pEntry->m_rhiRenderTargets[quint8(outFace)];
1396 cb->beginPass(rt, Qt::white, { 1.0f, 0 }, nullptr, rhiCtx->commonPassFlags());
1397 QSSGRHICTX_STAT(rhiCtx, beginRenderPass(rt));
1398 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderPass);
1399 rhiRenderOneShadowMap(rhiCtx, &ps, sortedOpaqueObjects, quint8(face));
1400 cb->endPass();
1401 QSSGRHICTX_STAT(rhiCtx, endRenderPass());
1402 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DRenderPass, 0, QSSG_RENDERPASS_NAME("shadow_cube", 0, outFace));
1403 }
1404
1405 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderPass);
1406 rhiBlurShadowMap(rhiCtx, pEntry, renderer, globalLights[i].light->m_shadowFilter, globalLights[i].light->m_shadowMapFar, false);
1407 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DRenderPass, 0, QByteArrayLiteral("shadow_cube_blur"));
1408 }
1409 }
1410}
1411
1413 QSSGPassKey passKey,
1414 const QSSGLayerRenderData &inData,
1416 QSSGRenderReflectionMap &reflectionMapManager,
1417 const QVector<QSSGRenderReflectionProbe *> &reflectionProbes,
1418 const QSSGRenderableObjectList &reflectionPassObjects,
1420{
1421 QRhi *rhi = rhiCtx->rhi();
1422 QRhiCommandBuffer *cb = rhiCtx->commandBuffer();
1423
1424 const bool renderSkybox = (inData.layer.background == QSSGRenderLayer::Background::SkyBox ||
1425 inData.layer.background == QSSGRenderLayer::Background::SkyBoxCubeMap)
1427
1428 for (int i = 0, ie = reflectionProbes.size(); i != ie; ++i) {
1429 QSSGReflectionMapEntry *pEntry = reflectionMapManager.reflectionMapEntry(i);
1430 if (!pEntry)
1431 continue;
1432
1433 if (!pEntry->m_needsRender)
1434 continue;
1435
1436 if (reflectionProbes[i]->refreshMode == QSSGRenderReflectionProbe::ReflectionRefreshMode::FirstFrame && pEntry->m_rendered)
1437 continue;
1438
1439 if (reflectionProbes[i]->texture)
1440 continue;
1441
1442 Q_ASSERT(pEntry->m_rhiDepthStencil);
1443 Q_ASSERT(pEntry->m_rhiCube);
1444
1445 const QSize size = pEntry->m_rhiCube->pixelSize();
1446 ps->viewport = QRhiViewport(0, 0, float(size.width()), float(size.height()));
1447
1448 QSSGRenderCamera theCameras[6] { QSSGRenderCamera{QSSGRenderCamera::Type::PerspectiveCamera},
1449 QSSGRenderCamera{QSSGRenderCamera::Type::PerspectiveCamera},
1450 QSSGRenderCamera{QSSGRenderCamera::Type::PerspectiveCamera},
1451 QSSGRenderCamera{QSSGRenderCamera::Type::PerspectiveCamera},
1452 QSSGRenderCamera{QSSGRenderCamera::Type::PerspectiveCamera},
1453 QSSGRenderCamera{QSSGRenderCamera::Type::PerspectiveCamera} };
1454 setupCubeReflectionCameras(reflectionProbes[i], theCameras);
1455 const bool swapYFaces = !rhi->isYUpInFramebuffer();
1456 for (const auto face : QSSGRenderTextureCubeFaces) {
1457 const auto cubeFaceIdx = QSSGBaseTypeHelpers::indexOfCubeFace(face);
1458 theCameras[cubeFaceIdx].calculateViewProjectionMatrix(pEntry->m_viewProjection);
1459
1460 rhiPrepareResourcesForReflectionMap(rhiCtx, passKey, inData, pEntry, ps,
1461 reflectionPassObjects, theCameras[cubeFaceIdx], renderer, face);
1462 }
1463 QRhiRenderPassDescriptor *renderPassDesc = nullptr;
1464 for (auto face : QSSGRenderTextureCubeFaces) {
1466 face = pEntry->m_timeSliceFace;
1467
1469 // Faces are swapped similarly to shadow maps due to differences in backends
1470 // Prefilter step handles correcting orientation differences in the final render
1471 if (swapYFaces) {
1472 if (outFace == QSSGRenderTextureCubeFace::PosY)
1474 else if (outFace == QSSGRenderTextureCubeFace::NegY)
1476 }
1477 QRhiTextureRenderTarget *rt = pEntry->m_rhiRenderTargets[quint8(outFace)];
1478 cb->beginPass(rt, reflectionProbes[i]->clearColor, { 1.0f, 0 }, nullptr, rhiCtx->commonPassFlags());
1479 QSSGRHICTX_STAT(rhiCtx, beginRenderPass(rt));
1480 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderPass);
1481
1482 if (renderSkybox && pEntry->m_skyBoxSrbs[quint8(face)]) {
1483 const auto &shaderCache = renderer.contextInterface()->shaderCache();
1484 const bool isSkyBox = inData.layer.background == QSSGRenderLayer::Background::SkyBox;
1485 const auto &shaderPipeline = isSkyBox ? shaderCache->getBuiltInRhiShaders().getRhiSkyBoxShader(QSSGRenderLayer::TonemapMode::None, inData.layer.skyBoxIsRgbe8, 1)
1486 : shaderCache->getBuiltInRhiShaders().getRhiSkyBoxCubeShader(1);
1487 Q_ASSERT(shaderPipeline);
1489 QRhiShaderResourceBindings *srb = pEntry->m_skyBoxSrbs[quint8(face)];
1490 if (!renderPassDesc)
1491 renderPassDesc = rt->newCompatibleRenderPassDescriptor();
1492 rt->setRenderPassDescriptor(renderPassDesc);
1493 isSkyBox ? renderer.rhiQuadRenderer()->recordRenderQuad(rhiCtx, ps, srb, renderPassDesc, {})
1494 : renderer.rhiCubeRenderer()->recordRenderCube(rhiCtx, ps, srb, renderPassDesc, {});
1495 }
1496
1497 bool needsSetViewport = true;
1498 for (const auto &handle : reflectionPassObjects)
1499 rhiRenderRenderable(rhiCtx, *ps, *handle.obj, &needsSetViewport, face);
1500
1501 cb->endPass();
1502 QSSGRHICTX_STAT(rhiCtx, endRenderPass());
1503 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DRenderPass, 0, QSSG_RENDERPASS_NAME("reflection_cube", 0, outFace));
1504
1506 break;
1507 }
1508 if (renderPassDesc)
1509 renderPassDesc->deleteLater();
1510
1511 pEntry->renderMips(rhiCtx);
1512
1514 pEntry->m_timeSliceFace = QSSGBaseTypeHelpers::next(pEntry->m_timeSliceFace); // Wraps
1515
1516 if (reflectionProbes[i]->refreshMode == QSSGRenderReflectionProbe::ReflectionRefreshMode::FirstFrame)
1517 pEntry->m_rendered = true;
1518
1519 reflectionProbes[i]->hasScheduledUpdate = false;
1520 pEntry->m_needsRender = false;
1521 }
1522}
1523
1525{
1526 QRhi *rhi = rhiCtx->rhi();
1527 bool needsBuild = false;
1528
1529 if (!renderableTex->texture) {
1530 QRhiTexture::Flags flags = QRhiTexture::RenderTarget;
1531 // the ambient occlusion texture is always non-msaa, even if multisampling is used in the main pass
1532 if (rhiCtx->mainPassViewCount() <= 1)
1533 renderableTex->texture = rhi->newTexture(QRhiTexture::RGBA8, size, 1, flags);
1534 else
1535 renderableTex->texture = rhi->newTextureArray(QRhiTexture::RGBA8, rhiCtx->mainPassViewCount(), size, 1, flags);
1536 needsBuild = true;
1537 } else if (renderableTex->texture->pixelSize() != size) {
1538 renderableTex->texture->setPixelSize(size);
1539 needsBuild = true;
1540 }
1541
1542 if (needsBuild) {
1543 if (!renderableTex->texture->create()) {
1544 qWarning("Failed to build ambient occlusion texture (size %dx%d)", size.width(), size.height());
1545 renderableTex->reset();
1546 return false;
1547 }
1548 renderableTex->resetRenderTarget();
1550 QRhiColorAttachment colorAttachment(renderableTex->texture);
1551 colorAttachment.setMultiViewCount(rhiCtx->mainPassViewCount());
1552 desc.setColorAttachments({ colorAttachment });
1553 renderableTex->rt = rhi->newTextureRenderTarget(desc);
1554 renderableTex->rt->setName(QByteArrayLiteral("Ambient occlusion"));
1555 renderableTex->rpDesc = renderableTex->rt->newCompatibleRenderPassDescriptor();
1556 renderableTex->rt->setRenderPassDescriptor(renderableTex->rpDesc);
1557 if (!renderableTex->rt->create()) {
1558 qWarning("Failed to build render target for ambient occlusion texture");
1559 renderableTex->reset();
1560 return false;
1561 }
1562 }
1563
1564 return true;
1565}
1566
1568 QSSGPassKey passKey,
1570 QSSGRhiShaderPipeline &shaderPipeline,
1573 const QSSGRhiRenderableTexture &rhiAoTexture,
1574 const QSSGRhiRenderableTexture &rhiDepthTexture,
1575 const QSSGRenderCamera &camera)
1576{
1578
1579 // no texelFetch in GLSL <= 120 and GLSL ES 100
1580 if (!rhiCtx->rhi()->isFeatureSupported(QRhi::TexelFetch)) {
1581 QRhiCommandBuffer *cb = rhiCtx->commandBuffer();
1582 // just clear and stop there
1583 cb->beginPass(rhiAoTexture.rt, Qt::white, { 1.0f, 0 });
1584 QSSGRHICTX_STAT(rhiCtx, beginRenderPass(rhiAoTexture.rt));
1585 cb->endPass();
1586 QSSGRHICTX_STAT(rhiCtx, endRenderPass());
1587 return;
1588 }
1589
1591
1592 const float R2 = ao.aoDistance * ao.aoDistance * 0.16f;
1593 const QSize textureSize = rhiAoTexture.texture->pixelSize();
1594 const float rw = float(textureSize.width());
1595 const float rh = float(textureSize.height());
1596 const float fov = camera.verticalFov(rw / rh);
1597 const float tanHalfFovY = tanf(0.5f * fov * (rh / rw));
1598 const float invFocalLenX = tanHalfFovY * (rw / rh);
1599
1600 const QVector4D aoProps(ao.aoStrength * 0.01f, ao.aoDistance * 0.4f, ao.aoSoftness * 0.02f, ao.aoBias);
1601 const QVector4D aoProps2(float(ao.aoSamplerate), (ao.aoDither) ? 1.0f : 0.0f, 0.0f, 0.0f);
1602 const QVector4D aoScreenConst(1.0f / R2, rh / (2.0f * tanHalfFovY), 1.0f / rw, 1.0f / rh);
1603 const QVector4D uvToEyeConst(2.0f * invFocalLenX, -2.0f * tanHalfFovY, -invFocalLenX, tanHalfFovY);
1604 const QVector2D cameraProps(camera.clipNear, camera.clipFar);
1605
1606 // layout(std140, binding = 0) uniform buf {
1607 // vec4 aoProperties;
1608 // vec4 aoProperties2;
1609 // vec4 aoScreenConst;
1610 // vec4 uvToEyeConst;
1611 // vec2 cameraProperties;
1612
1613 const int UBUF_SIZE = 72;
1614 QSSGRhiDrawCallData &dcd(rhiCtxD->drawCallData({ passKey, nullptr, nullptr, 0 }));
1615 if (!dcd.ubuf) {
1617 dcd.ubuf->create();
1618 }
1619
1620 char *ubufData = dcd.ubuf->beginFullDynamicBufferUpdateForCurrentFrame();
1621 memcpy(ubufData, &aoProps, 16);
1622 memcpy(ubufData + 16, &aoProps2, 16);
1623 memcpy(ubufData + 32, &aoScreenConst, 16);
1624 memcpy(ubufData + 48, &uvToEyeConst, 16);
1625 memcpy(ubufData + 64, &cameraProps, 8);
1626 dcd.ubuf->endFullDynamicBufferUpdateForCurrentFrame();
1627
1631 bindings.addUniformBuffer(0, RENDERER_VISIBILITY_ALL, dcd.ubuf);
1632 // binding 1 is either a sampler2D or sampler2DArray, matching
1633 // rhiDepthTexture.texture, no special casing needed here
1635 QRhiShaderResourceBindings *srb = rhiCtxD->srb(bindings);
1636
1637 renderer.rhiQuadRenderer()->prepareQuad(rhiCtx, nullptr);
1638 renderer.rhiQuadRenderer()->recordRenderQuadPass(rhiCtx, &ps, srb, rhiAoTexture.rt, {});
1639}
1640
1642{
1643 QRhi *rhi = rhiCtx->rhi();
1644 bool needsBuild = false;
1645 QRhiTexture::Flags flags = QRhiTexture::RenderTarget;
1646 if (wantsMips)
1648
1649 if (!renderableTex->texture) {
1650 // always non-msaa, even if multisampling is used in the main pass
1651 if (rhiCtx->mainPassViewCount() <= 1)
1652 renderableTex->texture = rhi->newTexture(QRhiTexture::RGBA8, size, 1, flags);
1653 else
1654 renderableTex->texture = rhi->newTextureArray(QRhiTexture::RGBA8, rhiCtx->mainPassViewCount(), size, 1, flags);
1655 needsBuild = true;
1656 } else if (renderableTex->texture->pixelSize() != size) {
1657 renderableTex->texture->setPixelSize(size);
1658 needsBuild = true;
1659 }
1660
1661 if (!renderableTex->depthStencil && !renderableTex->depthTexture) {
1662 if (rhiCtx->mainPassViewCount() <= 1)
1663 renderableTex->depthStencil = rhi->newRenderBuffer(QRhiRenderBuffer::DepthStencil, size);
1664 else
1665 renderableTex->depthTexture = rhi->newTextureArray(QRhiTexture::D24S8, rhiCtx->mainPassViewCount(), size, 1, QRhiTexture::RenderTarget);
1666 needsBuild = true;
1667 } else {
1668 if (renderableTex->depthStencil && renderableTex->depthStencil->pixelSize() != size) {
1669 renderableTex->depthStencil->setPixelSize(size);
1670 needsBuild = true;
1671 } else if (renderableTex->depthTexture && renderableTex->depthTexture->pixelSize() != size) {
1672 renderableTex->depthTexture->setPixelSize(size);
1673 needsBuild = true;
1674 }
1675 }
1676
1677 if (needsBuild) {
1678 if (!renderableTex->texture->create()) {
1679 qWarning("Failed to build screen texture (size %dx%d)", size.width(), size.height());
1680 renderableTex->reset();
1681 return false;
1682 }
1683 if (renderableTex->depthStencil && !renderableTex->depthStencil->create()) {
1684 qWarning("Failed to build depth-stencil buffer for screen texture (size %dx%d)",
1685 size.width(), size.height());
1686 renderableTex->reset();
1687 return false;
1688 } else if (renderableTex->depthTexture && !renderableTex->depthTexture->create()) {
1689 qWarning("Failed to build depth-stencil texture array (multiview) for screen texture (size %dx%d)",
1690 size.width(), size.height());
1691 renderableTex->reset();
1692 return false;
1693 }
1694 renderableTex->resetRenderTarget();
1696 QRhiColorAttachment colorAttachment(renderableTex->texture);
1697 colorAttachment.setMultiViewCount(rhiCtx->mainPassViewCount());
1698 desc.setColorAttachments({ colorAttachment });
1699 if (renderableTex->depthStencil)
1700 desc.setDepthStencilBuffer(renderableTex->depthStencil);
1701 else if (renderableTex->depthTexture)
1702 desc.setDepthTexture(renderableTex->depthTexture);
1703 renderableTex->rt = rhi->newTextureRenderTarget(desc);
1704 renderableTex->rt->setName(QByteArrayLiteral("Screen texture"));
1705 renderableTex->rpDesc = renderableTex->rt->newCompatibleRenderPassDescriptor();
1706 renderableTex->rt->setRenderPassDescriptor(renderableTex->rpDesc);
1707 if (!renderableTex->rt->create()) {
1708 qWarning("Failed to build render target for screen texture");
1709 renderableTex->reset();
1710 return false;
1711 }
1712 }
1713
1714 return true;
1715}
1716
1718{
1720 QRhiCommandBuffer *cb = rhiCtx->commandBuffer();
1721 cb->debugMarkBegin(QByteArrayLiteral("Quick3D prepare grid"));
1722
1724
1725 int uniformBinding = 0;
1726 const int ubufSize = cameras.count() >= 2 ? 276 : 148;
1727
1728 QSSGRhiDrawCallData &dcd(rhiCtxD->drawCallData({ passKey, nullptr, nullptr, 0 })); // Change to Grid?
1729
1730 QRhi *rhi = rhiCtx->rhi();
1731 if (!dcd.ubuf) {
1732 dcd.ubuf = rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, ubufSize);
1733 dcd.ubuf->create();
1734 }
1735
1736 // Param
1737 const float nearF = cameras[0]->clipNear;
1738 const float farF = cameras[0]->clipFar;
1739 const float scale = layer.gridScale;
1740 const quint32 gridFlags = layer.gridFlags;
1741
1742 const float yFactor = rhi->isYUpInNDC() ? 1.0f : -1.0f;
1743
1744 quint32 ubufOffset = 0;
1745 char *ubufData = dcd.ubuf->beginFullDynamicBufferUpdateForCurrentFrame();
1746
1747 for (qsizetype viewIdx = 0; viewIdx < cameras.count(); ++viewIdx) {
1748 QMatrix4x4 viewProj(Qt::Uninitialized);
1749 cameras[viewIdx]->calculateViewProjectionMatrix(viewProj);
1750 QMatrix4x4 invViewProj = viewProj.inverted();
1751 quint32 viewDataOffset = ubufOffset;
1752 memcpy(ubufData + viewDataOffset + viewIdx * 64, viewProj.constData(), 64);
1753 viewDataOffset += 64 * cameras.count();
1754 memcpy(ubufData + viewDataOffset + viewIdx * 64, invViewProj.constData(), 64);
1755 }
1756 ubufOffset += (64 + 64) * cameras.count();
1757
1758 memcpy(ubufData + ubufOffset, &nearF, 4);
1759 ubufOffset += 4;
1760 memcpy(ubufData + ubufOffset, &farF, 4);
1761 ubufOffset += 4;
1762 memcpy(ubufData + ubufOffset, &scale, 4);
1763 ubufOffset += 4;
1764 memcpy(ubufData + ubufOffset, &yFactor, 4);
1765 ubufOffset += 4;
1766 memcpy(ubufData + ubufOffset, &gridFlags, 4);
1767
1768 dcd.ubuf->endFullDynamicBufferUpdateForCurrentFrame();
1769
1770 bindings.addUniformBuffer(uniformBinding, RENDERER_VISIBILITY_ALL, dcd.ubuf);
1771
1772 layer.gridSrb = rhiCtxD->srb(bindings);
1773 renderer.rhiQuadRenderer()->prepareQuad(rhiCtx, nullptr);
1774
1775 cb->debugMarkEnd();
1776}
1777
1779 QSSGPassKey passKey,
1781 QSSGRenderCameraList &cameras,
1783 QSSGReflectionMapEntry *entry = nullptr,
1785{
1787 const bool cubeMapMode = layer.background == QSSGRenderLayer::Background::SkyBoxCubeMap;
1788 const QSSGRenderImageTexture lightProbeTexture =
1789 cubeMapMode ? renderer.contextInterface()->bufferManager()->loadRenderImage(layer.skyBoxCubeMap, QSSGBufferManager::MipModeDisable)
1790 : renderer.contextInterface()->bufferManager()->loadRenderImage(layer.lightProbe, QSSGBufferManager::MipModeBsdf);
1791 const bool hasValidTexture = lightProbeTexture.m_texture != nullptr;
1792 if (hasValidTexture) {
1793 if (cubeFace == QSSGRenderTextureCubeFaceNone)
1794 layer.skyBoxIsRgbe8 = lightProbeTexture.m_flags.isRgbe8();
1795
1797
1800 cubeMapMode ? QRhiSampler::None : QRhiSampler::Linear, // cube map doesn't have mipmaps
1804 int samplerBinding = 1; //the shader code is hand-written, so we don't need to look that up
1805 const quint32 ubufSize = cameras.count() >= 2 ? 416 : 240; // same ubuf layout for both skybox and skyboxcube
1806 bindings.addTexture(samplerBinding,
1808 lightProbeTexture.m_texture, sampler);
1809
1810 const auto cubeFaceIdx = QSSGBaseTypeHelpers::indexOfCubeFace(cubeFace);
1811 const quintptr entryIdx = quintptr(cubeFace != QSSGRenderTextureCubeFaceNone) * cubeFaceIdx;
1812 QSSGRhiDrawCallData &dcd = rhiCtxD->drawCallData({ passKey, nullptr, entry, entryIdx });
1813
1814 QRhi *rhi = rhiCtx->rhi();
1815 if (!dcd.ubuf) {
1816 dcd.ubuf = rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, ubufSize);
1817 dcd.ubuf->create();
1818 }
1819
1820 float adjustY = rhi->isYUpInNDC() ? 1.0f : -1.0f;
1821 const float exposure = layer.lightProbeSettings.probeExposure;
1822 // orientation
1823 const QMatrix3x3 &rotationMatrix(layer.lightProbeSettings.probeOrientation);
1824 const float blurAmount = layer.skyboxBlurAmount;
1825 const float maxMipLevel = float(lightProbeTexture.m_mipmapCount - 2);
1826
1827 const QVector4D skyboxProperties = {
1828 adjustY,
1829 exposure,
1830 blurAmount,
1831 maxMipLevel
1832 };
1833
1834 char *ubufData = dcd.ubuf->beginFullDynamicBufferUpdateForCurrentFrame();
1835 quint32 ubufOffset = 0;
1836 // skyboxProperties
1837 memcpy(ubufData + ubufOffset, &skyboxProperties, 16);
1838 ubufOffset += 16;
1839 // orientation
1840 memcpy(ubufData + ubufOffset, rotationMatrix.constData(), 12);
1841 ubufOffset += 16;
1842 memcpy(ubufData + ubufOffset, (char *)rotationMatrix.constData() + 12, 12);
1843 ubufOffset += 16;
1844 memcpy(ubufData + ubufOffset, (char *)rotationMatrix.constData() + 24, 12);
1845 ubufOffset += 16;
1846
1847 for (qsizetype viewIdx = 0; viewIdx < cameras.count(); ++viewIdx) {
1848 const QMatrix4x4 &inverseProjection = cameras[viewIdx]->projection.inverted();
1849 const QMatrix4x4 &viewMatrix = cameras[viewIdx]->globalTransform;
1850 QMatrix4x4 viewProjection(Qt::Uninitialized); // For cube mode
1851 cameras[viewIdx]->calculateViewProjectionWithoutTranslation(0.1f, 5.0f, viewProjection);
1852
1853 quint32 viewDataOffset = ubufOffset;
1854 memcpy(ubufData + viewDataOffset + viewIdx * 64, viewProjection.constData(), 64);
1855 viewDataOffset += cameras.count() * 64;
1856 memcpy(ubufData + viewDataOffset + viewIdx * 64, inverseProjection.constData(), 64);
1857 viewDataOffset += cameras.count() * 64;
1858 memcpy(ubufData + viewDataOffset + viewIdx * 48, viewMatrix.constData(), 48);
1859 }
1860 dcd.ubuf->endFullDynamicBufferUpdateForCurrentFrame();
1861
1862 bindings.addUniformBuffer(0, RENDERER_VISIBILITY_ALL, dcd.ubuf);
1863
1864 if (cubeFace != QSSGRenderTextureCubeFaceNone) {
1865 const auto cubeFaceIdx = QSSGBaseTypeHelpers::indexOfCubeFace(cubeFace);
1866 entry->m_skyBoxSrbs[cubeFaceIdx] = rhiCtxD->srb(bindings);
1867 } else {
1868 layer.skyBoxSrb = rhiCtxD->srb(bindings);
1869 }
1870
1871 if (cubeMapMode)
1872 renderer.rhiCubeRenderer()->prepareCube(rhiCtx, nullptr);
1873 else
1874 renderer.rhiQuadRenderer()->prepareQuad(rhiCtx, nullptr);
1875 }
1876}
1877
1879 QSSGPassKey passKey,
1881 QSSGRenderCameraList &cameras,
1883{
1884 QRhiCommandBuffer *cb = rhiCtx->commandBuffer();
1885 cb->debugMarkBegin(QByteArrayLiteral("Quick3D prepare skybox"));
1886
1887 rhiPrepareSkyBox_helper(rhiCtx, passKey, layer, cameras, renderer);
1888
1889 cb->debugMarkEnd();
1890}
1891
1893 QSSGPassKey passKey,
1895 QSSGRenderCamera &inCamera,
1899{
1900 QRhiCommandBuffer *cb = rhiCtx->commandBuffer();
1901 cb->debugMarkBegin(QByteArrayLiteral("Quick3D prepare skybox for reflection cube map"));
1902
1903 QSSGRenderCameraList cameras({ &inCamera });
1904 rhiPrepareSkyBox_helper(rhiCtx, passKey, layer, cameras, renderer, entry, cubeFace);
1905
1906 cb->debugMarkEnd();
1907}
1908
1910 QSSGPassKey passKey,
1911 const QSSGRhiGraphicsPipelineState &basePipelineState,
1913 QSSGLayerRenderData &inData,
1914 const QSSGRenderableObjectList &sortedOpaqueObjects,
1915 const QSSGRenderableObjectList &sortedTransparentObjects,
1916 int samples,
1917 int viewCount)
1918{
1919 static const auto rhiPrepareDepthPassForObject = [](QSSGRhiContext *rhiCtx,
1920 QSSGPassKey passKey,
1921 QSSGLayerRenderData &inData,
1925 QSSGRhiShaderPipelinePtr shaderPipeline;
1927
1928 const bool isOpaqueDepthPrePass = obj->depthWriteMode == QSSGDepthDrawMode::OpaquePrePass;
1929 QSSGShaderFeatures featureSet;
1931 if (isOpaqueDepthPrePass)
1933
1934 QSSGRhiDrawCallData *dcd = nullptr;
1935 if (obj->type == QSSGRenderableObject::Type::DefaultMaterialMeshSubset || obj->type == QSSGRenderableObject::Type::CustomMaterialMeshSubset) {
1936 QSSGSubsetRenderable &subsetRenderable(static_cast<QSSGSubsetRenderable &>(*obj));
1937 const void *modelNode = &subsetRenderable.modelContext.model;
1938 dcd = &rhiCtxD->drawCallData({ passKey, modelNode, &subsetRenderable.material, 0 });
1939 }
1940
1941 if (obj->type == QSSGRenderableObject::Type::DefaultMaterialMeshSubset) {
1942 QSSGSubsetRenderable &subsetRenderable(static_cast<QSSGSubsetRenderable &>(*obj));
1943 const auto &material = static_cast<const QSSGRenderDefaultMaterial &>(subsetRenderable.getMaterial());
1944 ps->cullMode = QSSGRhiHelpers::toCullMode(material.cullMode);
1945
1946 shaderPipeline = shadersForDefaultMaterial(ps, subsetRenderable, featureSet);
1947 if (shaderPipeline) {
1948 shaderPipeline->ensureCombinedMainLightsUniformBuffer(&dcd->ubuf);
1949 char *ubufData = dcd->ubuf->beginFullDynamicBufferUpdateForCurrentFrame();
1950 updateUniformsForDefaultMaterial(*shaderPipeline, rhiCtx, inData, ubufData, ps, subsetRenderable, inData.renderedCameras, nullptr, nullptr);
1951 dcd->ubuf->endFullDynamicBufferUpdateForCurrentFrame();
1952 } else {
1953 return false;
1954 }
1955 } else if (obj->type == QSSGRenderableObject::Type::CustomMaterialMeshSubset) {
1956 QSSGSubsetRenderable &subsetRenderable(static_cast<QSSGSubsetRenderable &>(*obj));
1957
1958 const auto &customMaterial = static_cast<const QSSGRenderCustomMaterial &>(subsetRenderable.getMaterial());
1959
1960 ps->cullMode = QSSGRhiHelpers::toCullMode(customMaterial.m_cullMode);
1961
1962 QSSGCustomMaterialSystem &customMaterialSystem(*subsetRenderable.renderer->contextInterface()->customMaterialSystem().get());
1963 shaderPipeline = customMaterialSystem.shadersForCustomMaterial(ps, customMaterial, subsetRenderable, inData.getDefaultMaterialPropertyTable(), featureSet);
1964
1965 if (shaderPipeline) {
1966 shaderPipeline->ensureCombinedMainLightsUniformBuffer(&dcd->ubuf);
1967 char *ubufData = dcd->ubuf->beginFullDynamicBufferUpdateForCurrentFrame();
1968 customMaterialSystem.updateUniformsForCustomMaterial(*shaderPipeline, rhiCtx, inData, ubufData, ps, customMaterial, subsetRenderable,
1969 inData.renderedCameras, nullptr, nullptr);
1970 dcd->ubuf->endFullDynamicBufferUpdateForCurrentFrame();
1971 } else {
1972 return false;
1973 }
1974 }
1975
1976 // the rest is common, only relying on QSSGSubsetRenderableBase, not the subclasses
1977 if (obj->type == QSSGRenderableObject::Type::DefaultMaterialMeshSubset || obj->type == QSSGRenderableObject::Type::CustomMaterialMeshSubset) {
1978 QSSGSubsetRenderable &subsetRenderable(static_cast<QSSGSubsetRenderable &>(*obj));
1980 ia = subsetRenderable.subset.rhi.ia;
1981
1982 const QSSGRenderCameraDataList &cameraDatas(*inData.renderedCameraData);
1983 int instanceBufferBinding = setupInstancing(&subsetRenderable, ps, rhiCtx, cameraDatas[0].direction, cameraDatas[0].position);
1984 QSSGRhiHelpers::bakeVertexInputLocations(&ia, *shaderPipeline, instanceBufferBinding);
1985
1987 bindings.addUniformBuffer(0, RENDERER_VISIBILITY_ALL, dcd->ubuf);
1988
1989 // Depth and SSAO textures, in case a custom material's shader code does something with them.
1990 addDepthTextureBindings(rhiCtx, shaderPipeline.get(), bindings);
1991
1992 if (isOpaqueDepthPrePass) {
1994 shaderPipeline.get(),
1995 subsetRenderable.firstImage,
1996 bindings,
1997 (obj->type == QSSGRenderableObject::Type::CustomMaterialMeshSubset));
1998 }
1999
2000 QRhiShaderResourceBindings *srb = rhiCtxD->srb(bindings);
2001
2002 subsetRenderable.rhiRenderData.depthPrePass.pipeline = rhiCtxD->pipeline(*ps,
2003 rpDesc,
2004 srb);
2005 subsetRenderable.rhiRenderData.depthPrePass.srb = srb;
2006 }
2007
2008 return true;
2009 };
2010
2011 // Phase 1 (prepare) for the Z prepass or the depth texture generation.
2012 // These renders opaque (Z prepass), or opaque and transparent (depth
2013 // texture), objects with depth test/write enabled, and color write
2014 // disabled, using a very simple set of shaders.
2015
2016 QSSGRhiGraphicsPipelineState ps = basePipelineState; // viewport and others are filled out already
2017 // We took a copy of the pipeline state since we do not want to conflict
2018 // with what rhiPrepare() collects for its own use. So here just change
2019 // whatever we need.
2020
2021 ps.samples = samples;
2022 ps.viewCount = viewCount;
2024 ps.targetBlend.colorWrite = {};
2025
2026 for (const QSSGRenderableObjectHandle &handle : sortedOpaqueObjects) {
2027 if (!rhiPrepareDepthPassForObject(rhiCtx, passKey, inData, handle.obj, rpDesc, &ps))
2028 return false;
2029 }
2030
2031 for (const QSSGRenderableObjectHandle &handle : sortedTransparentObjects) {
2032 if (!rhiPrepareDepthPassForObject(rhiCtx, passKey, inData, handle.obj, rpDesc, &ps))
2033 return false;
2034 }
2035
2036 return true;
2037}
2038
2040 const QSSGRhiGraphicsPipelineState &pipelineState,
2041 const QSSGRenderableObjectList &sortedOpaqueObjects,
2042 const QSSGRenderableObjectList &sortedTransparentObjects,
2043 bool *needsSetViewport)
2044{
2045 static const auto rhiRenderDepthPassForImp = [](QSSGRhiContext *rhiCtx,
2046 const QSSGRhiGraphicsPipelineState &pipelineState,
2047 const QSSGRenderableObjectList &objects,
2048 bool *needsSetViewport) {
2049 for (const auto &oh : objects) {
2050 QSSGRenderableObject *obj = oh.obj;
2051
2052 // casts to SubsetRenderableBase so it works for both default and custom materials
2053 if (obj->type == QSSGRenderableObject::Type::DefaultMaterialMeshSubset || obj->type == QSSGRenderableObject::Type::CustomMaterialMeshSubset) {
2054 QRhiCommandBuffer *cb = rhiCtx->commandBuffer();
2055 QSSGSubsetRenderable *subsetRenderable(static_cast<QSSGSubsetRenderable *>(obj));
2056
2057 QRhiBuffer *vertexBuffer = subsetRenderable->subset.rhi.vertexBuffer->buffer();
2058 QRhiBuffer *indexBuffer = subsetRenderable->subset.rhi.indexBuffer
2059 ? subsetRenderable->subset.rhi.indexBuffer->buffer()
2060 : nullptr;
2061
2062 QRhiGraphicsPipeline *ps = subsetRenderable->rhiRenderData.depthPrePass.pipeline;
2063 if (!ps)
2064 return;
2065
2066 QRhiShaderResourceBindings *srb = subsetRenderable->rhiRenderData.depthPrePass.srb;
2067 if (!srb)
2068 return;
2069
2070 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderCall);
2071 cb->setGraphicsPipeline(ps);
2072 cb->setShaderResources(srb);
2073
2074 if (*needsSetViewport) {
2075 cb->setViewport(pipelineState.viewport);
2076 *needsSetViewport = false;
2077 }
2078
2079 QRhiCommandBuffer::VertexInput vertexBuffers[2];
2080 int vertexBufferCount = 1;
2081 vertexBuffers[0] = QRhiCommandBuffer::VertexInput(vertexBuffer, 0);
2082 quint32 instances = 1;
2083 if (subsetRenderable->modelContext.model.instancing()) {
2084 instances = subsetRenderable->modelContext.model.instanceCount();
2085 vertexBuffers[1] = QRhiCommandBuffer::VertexInput(subsetRenderable->instanceBuffer, 0);
2087 }
2088
2089 if (indexBuffer) {
2090 cb->setVertexInput(0, vertexBufferCount, vertexBuffers, indexBuffer, 0, subsetRenderable->subset.rhi.indexBuffer->indexFormat());
2091 cb->drawIndexed(subsetRenderable->subset.count, instances, subsetRenderable->subset.offset);
2092 QSSGRHICTX_STAT(rhiCtx, drawIndexed(subsetRenderable->subset.count, instances));
2093 } else {
2094 cb->setVertexInput(0, vertexBufferCount, vertexBuffers);
2095 cb->draw(subsetRenderable->subset.count, instances, subsetRenderable->subset.offset);
2096 QSSGRHICTX_STAT(rhiCtx, draw(subsetRenderable->subset.count, instances));
2097 }
2098 Q_QUICK3D_PROFILE_END_WITH_IDS(QQuick3DProfiler::Quick3DRenderCall, (subsetRenderable->subset.count | quint64(instances) << 32),
2099 QVector<int>({subsetRenderable->modelContext.model.profilingId,
2100 subsetRenderable->material.profilingId}));
2101 }
2102 }
2103 };
2104
2105 rhiRenderDepthPassForImp(rhiCtx, pipelineState, sortedOpaqueObjects, needsSetViewport);
2106 rhiRenderDepthPassForImp(rhiCtx, pipelineState, sortedTransparentObjects, needsSetViewport);
2107}
2108
2110{
2111 QRhi *rhi = rhiCtx->rhi();
2112 bool needsBuild = false;
2113
2114 if (!renderableTex->texture) {
2119 qWarning("Depth texture not supported");
2120 // the depth texture is always non-msaa, even if multisampling is used in the main pass
2121 if (rhiCtx->mainPassViewCount() <= 1)
2122 renderableTex->texture = rhiCtx->rhi()->newTexture(format, size, 1, QRhiTexture::RenderTarget);
2123 else
2124 renderableTex->texture = rhiCtx->rhi()->newTextureArray(format, rhiCtx->mainPassViewCount(), size, 1, QRhiTexture::RenderTarget);
2125 needsBuild = true;
2126 } else if (renderableTex->texture->pixelSize() != size) {
2127 renderableTex->texture->setPixelSize(size);
2128 needsBuild = true;
2129 }
2130
2131 if (needsBuild) {
2132 if (!renderableTex->texture->create()) {
2133 qWarning("Failed to build depth texture (size %dx%d, format %d)",
2134 size.width(), size.height(), int(renderableTex->texture->format()));
2135 renderableTex->reset();
2136 return false;
2137 }
2138 renderableTex->resetRenderTarget();
2140 rtDesc.setDepthTexture(renderableTex->texture);
2141 renderableTex->rt = rhi->newTextureRenderTarget(rtDesc);
2142 renderableTex->rt->setName(QByteArrayLiteral("Depth texture"));
2143 renderableTex->rpDesc = renderableTex->rt->newCompatibleRenderPassDescriptor();
2144 renderableTex->rt->setRenderPassDescriptor(renderableTex->rpDesc);
2145 if (!renderableTex->rt->create()) {
2146 qWarning("Failed to build render target for depth texture");
2147 renderableTex->reset();
2148 return false;
2149 }
2150 }
2151
2152 return true;
2153}
2154
\inmodule QtCore
Definition qbitarray.h:13
\inmodule QtCore
Definition qbytearray.h:57
Definition qlist.h:75
The QMatrix4x4 class represents a 4x4 transformation matrix in 3D space.
Definition qmatrix4x4.h:25
float * data()
Returns a pointer to the raw data of this matrix.
QMatrix4x4 inverted(bool *invertible=nullptr) const
Returns the inverse of this matrix.
const float * constData() const
Returns a constant pointer to the raw data of this matrix.
Definition qmatrix4x4.h:147
The QQuaternion class represents a quaternion consisting of a vector and scalar.
\inmodule QtCore\reentrant
Definition qrect.h:484
\inmodule QtGui
Definition qrhi.h:846
virtual char * beginFullDynamicBufferUpdateForCurrentFrame()
Definition qrhi.cpp:3970
@ Dynamic
Definition qrhi.h:851
@ UniformBuffer
Definition qrhi.h:857
virtual bool create()=0
Creates the corresponding native graphics resources.
\inmodule QtGui
Definition qrhi.h:576
\inmodule QtGui
Definition qrhi.h:1651
void resourceUpdate(QRhiResourceUpdateBatch *resourceUpdates)
Sometimes committing resource updates is necessary or just more convenient without starting a render ...
Definition qrhi.cpp:9384
QPair< QRhiBuffer *, quint32 > VertexInput
Synonym for QPair<QRhiBuffer *, quint32>.
Definition qrhi.h:1680
\inmodule QtGui
Definition qrhi.h:1270
\inmodule QtGui
Definition qrhi.h:1142
void setRenderPassDescriptor(QRhiRenderPassDescriptor *desc)
Sets the QRhiRenderPassDescriptor desc for use with this render target.
Definition qrhi.h:1165
\inmodule QtGui
Definition qrhi.h:1731
QRhi * rhi() const
Definition qrhi.cpp:3603
void setName(const QByteArray &name)
Sets a name for the object.
Definition qrhi.cpp:3581
\inmodule QtGui
Definition qrhi.h:1030
Filter
Specifies the minification, magnification, or mipmap filtering.
Definition qrhi.h:1032
@ ClampToEdge
Definition qrhi.h:1040
\inmodule QtGui
Definition qrhi.h:1214
QShader shader() const
Definition qrhi.h:397
void setDepthTexture(QRhiTexture *texture)
Sets the texture for depth-stencil.
Definition qrhi.h:643
\inmodule QtGui
Definition qrhi.h:1184
virtual QRhiRenderPassDescriptor * newCompatibleRenderPassDescriptor()=0
\inmodule QtGui
Definition qrhi.h:895
@ UsedWithGenerateMips
Definition qrhi.h:903
@ MipMapped
Definition qrhi.h:900
@ RenderTarget
Definition qrhi.h:898
@ CubeMap
Definition qrhi.h:899
Format
Specifies the texture format.
Definition qrhi.h:914
Flags flags() const
Definition qrhi.h:992
QSize pixelSize() const
Definition qrhi.h:975
void setPixelSize(const QSize &sz)
Sets the texture size, specified in pixels, to sz.
Definition qrhi.h:976
\inmodule QtGui
Definition qrhi.h:85
\inmodule QtGuiPrivate \inheaderfile rhi/qrhi.h
Definition qrhi.h:1804
QRhiBuffer * newBuffer(QRhiBuffer::Type type, QRhiBuffer::UsageFlags usage, quint32 size)
Definition qrhi.cpp:10508
bool isClipDepthZeroToOne() const
Definition qrhi.cpp:10069
bool isTextureFormatSupported(QRhiTexture::Format format, QRhiTexture::Flags flags={}) const
Definition qrhi.cpp:10102
QRhiTexture * newTextureArray(QRhiTexture::Format format, int arraySize, const QSize &pixelSize, int sampleCount=1, QRhiTexture::Flags flags={})
Definition qrhi.cpp:10636
QMatrix4x4 clipSpaceCorrMatrix() const
Definition qrhi.cpp:10091
bool isYUpInFramebuffer() const
Definition qrhi.cpp:10030
bool isYUpInNDC() const
Definition qrhi.cpp:10044
bool isFeatureSupported(QRhi::Feature feature) const
Definition qrhi.cpp:10110
QRhiRenderBuffer * newRenderBuffer(QRhiRenderBuffer::Type type, const QSize &pixelSize, int sampleCount=1, QRhiRenderBuffer::Flags flags={}, QRhiTexture::Format backingFormatHint=QRhiTexture::UnknownFormat)
Definition qrhi.cpp:10535
QRhiTextureRenderTarget * newTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc, QRhiTextureRenderTarget::Flags flags={})
Definition qrhi.cpp:10682
QRhiTexture * newTexture(QRhiTexture::Format format, const QSize &pixelSize, int sampleCount=1, QRhiTexture::Flags flags={})
Definition qrhi.cpp:10562
@ TexelFetch
Definition qrhi.h:1852
QRhiResourceUpdateBatch * nextResourceUpdateBatch()
Definition qrhi.cpp:9252
static constexpr QSSGRenderTextureCubeFace next(QSSGRenderTextureCubeFace face)
static constexpr QSSGRenderTextureCubeFaceT indexOfCubeFace(QSSGRenderTextureCubeFace face) noexcept
Class representing 3D range or axis aligned bounding box.
void include(const QVector3D &v)
expands the volume to include v
void updateUniformsForCustomMaterial(QSSGRhiShaderPipeline &shaderPipeline, QSSGRhiContext *rhiCtx, const QSSGLayerRenderData &inData, char *ubufData, QSSGRhiGraphicsPipelineState *ps, const QSSGRenderCustomMaterial &material, QSSGSubsetRenderable &renderable, const QSSGRenderCameraList &cameras, const QVector2D *depthAdjust, const QMatrix4x4 *alteredModelViewProjection)
void rhiRenderRenderable(QSSGRhiContext *rhiCtx, QSSGSubsetRenderable &renderable, bool *needsSetViewport, QSSGRenderTextureCubeFace cubeFace, const QSSGRhiGraphicsPipelineState &state)
QSSGRhiShaderPipelinePtr shadersForCustomMaterial(QSSGRhiGraphicsPipelineState *ps, const QSSGRenderCustomMaterial &material, QSSGSubsetRenderable &renderable, const QSSGShaderDefaultMaterialKeyProperties &defaultMaterialShaderKeyProperties, const QSSGShaderFeatures &featureSet)
void rhiPrepareRenderable(QSSGRhiGraphicsPipelineState *ps, QSSGPassKey passKey, QSSGSubsetRenderable &renderable, const QSSGShaderFeatures &featureSet, const QSSGRenderCustomMaterial &material, const QSSGLayerRenderData &layerData, QRhiRenderPassDescriptor *renderPassDescriptor, int samples, int viewCount, QSSGRenderCamera *camera=nullptr, QSSGRenderTextureCubeFace cubeFace=QSSGRenderTextureCubeFaceNone, QMatrix4x4 *modelViewProjection=nullptr, QSSGReflectionMapEntry *entry=nullptr)
static QSSGGraphicsPipelineStateKey create(const QSSGRhiGraphicsPipelineState &state, const QRhiRenderPassDescriptor *rpDesc, const QRhiShaderResourceBindings *srb)
static QSSGLayerRenderData * getCurrent(const QSSGRenderer &renderer)
static bool prepareInstancing(QSSGRhiContext *rhiCtx, QSSGSubsetRenderable *renderable, const QVector3D &cameraDirection, const QVector3D &cameraPosition, float minThreshold, float maxThreshold)
static void updateUniformsForParticleModel(QSSGRhiShaderPipeline &shaderPipeline, char *ubufData, const QSSGRenderModel *model, quint32 offset)
static void rhiPrepareRenderable(QSSGRhiShaderPipeline &shaderPipeline, QSSGPassKey passKey, QSSGRhiContext *rhiCtx, QSSGRhiGraphicsPipelineState *ps, QSSGParticlesRenderable &renderable, const QSSGLayerRenderData &inData, QRhiRenderPassDescriptor *renderPassDescriptor, int samples, int viewCount, QSSGRenderCamera *alteredCamera=nullptr, QSSGRenderTextureCubeFace cubeFace=QSSGRenderTextureCubeFaceNone, QSSGReflectionMapEntry *entry=nullptr)
static void rhiRenderRenderable(QSSGRhiContext *rhiCtx, QSSGParticlesRenderable &renderable, bool *needsSetViewport, QSSGRenderTextureCubeFace cubeFace, const QSSGRhiGraphicsPipelineState &state)
static void prepareParticlesForModel(QSSGRhiShaderPipeline &shaderPipeline, QSSGRhiContext *rhiCtx, QSSGRhiShaderResourceBindingList &bindings, const QSSGRenderModel *model)
QSSGReflectionMapEntry * reflectionMapEntry(int probeIdx)
QSSGShadowMapEntry * shadowMapEntry(int lightIdx)
static QSSGRhiShaderPipelinePtr getShaderPipelineForDefaultMaterial(QSSGRenderer &renderer, QSSGSubsetRenderable &inRenderable, const QSSGShaderFeatures &inFeatureSet)
static QSSGRhiContextPrivate * get(QSSGRhiContext *q)
\inmodule QtQuick3D
int mainPassViewCount() const
Returns the multiview count used in the main render pass.
QRhiCommandBuffer * commandBuffer() const
QRhiTexture * dummyTexture(QRhiTexture::Flags flags, QRhiResourceUpdateBatch *rub, const QSize &size=QSize(64, 64), const QColor &fillColor=Qt::black, int arraySize=0)
void checkAndAdjustForNPoT(QRhiTexture *texture, QSSGRhiSamplerDescription *samplerDescription)
Adjusts samplerDescription's tiling and filtering modes based on the pixel size of texture.
QRhiCommandBuffer::BeginPassFlags commonPassFlags() const
QRhi * rhi() const
QRhiSampler * sampler(const QSSGRhiSamplerDescription &samplerDescription)
QRhiGraphicsPipeline::CullMode cullMode
QRhiGraphicsPipeline::TargetBlend targetBlend
QRhiTexture * ssaoTexture() const
const QRhiShaderStage * fragmentStage() const
QRhiTexture * depthTexture() const
int bindingForTexture(const char *name, int hint=-1)
const QRhiShaderStage * vertexStage() const
const QSSGRhiTexture & extraTextureAt(int index) const
void addUniformBuffer(int binding, QRhiShaderResourceBinding::StageFlags stage, QRhiBuffer *buf, int offset=0, int size=0)
void addTexture(int binding, QRhiShaderResourceBinding::StageFlags stage, QRhiTexture *tex, QRhiSampler *sampler)
const_iterator cend() const noexcept
Definition qset.h:142
QList< InOutVariable > combinedImageSamplers() const
QShaderDescription description() const
Definition qshader.cpp:370
\inmodule QtCore
Definition qsize.h:25
constexpr int height() const noexcept
Returns the height.
Definition qsize.h:133
constexpr int width() const noexcept
Returns the width.
Definition qsize.h:130
constexpr size_type size() const noexcept
qsizetype count() const
The QVector2D class represents a vector or vertex in 2D space.
Definition qvectornd.h:31
The QVector3D class represents a vector or vertex in 3D space.
Definition qvectornd.h:171
QVector3D normalized() const noexcept
Returns the normalized unit vector form of this vector.
Definition qvectornd.h:695
constexpr float y() const noexcept
Returns the y coordinate of this point.
Definition qvectornd.h:671
static constexpr float dotProduct(QVector3D v1, QVector3D v2) noexcept
Returns the dot product of v1 and v2.
Definition qvectornd.h:770
static constexpr QVector3D crossProduct(QVector3D v1, QVector3D v2) noexcept
Returns the cross-product of vectors v1 and v2, which is normal to the plane spanned by v1 and v2.
Definition qvectornd.h:775
The QVector4D class represents a vector or vertex in 4D space.
Definition qvectornd.h:330
QCamera * camera
Definition camera.cpp:19
QMap< QString, QString > map
[6]
QSet< QString >::iterator it
direction
else opt state
[0]
QRhiGraphicsPipeline::CullMode toCullMode(QSSGCullFaceMode cullFaceMode)
void bakeVertexInputLocations(QSSGRhiInputAssemblerState *ia, const QSSGRhiShaderPipeline &shaders, int instanceBufferBinding)
QRhiSampler::Filter toRhi(QSSGRenderTextureFilterOp op)
Combined button and popup list for selecting options.
@ white
Definition qnamespace.h:31
@ black
Definition qnamespace.h:30
constexpr Initialization Uninitialized
void rhiPrepareGrid(QSSGRhiContext *rhiCtx, QSSGPassKey passKey, QSSGRenderLayer &layer, QSSGRenderCameraList &cameras, QSSGRenderer &renderer)
bool rhiPrepareScreenTexture(QSSGRhiContext *rhiCtx, const QSize &size, bool wantsMips, QSSGRhiRenderableTexture *renderableTex)
void rhiPrepareSkyBoxForReflectionMap(QSSGRhiContext *rhiCtx, QSSGPassKey passKey, QSSGRenderLayer &layer, QSSGRenderCamera &inCamera, QSSGRenderer &renderer, QSSGReflectionMapEntry *entry, QSSGRenderTextureCubeFace cubeFace)
void rhiRenderAoTexture(QSSGRhiContext *rhiCtx, QSSGPassKey passKey, QSSGRenderer &renderer, QSSGRhiShaderPipeline &shaderPipeline, QSSGRhiGraphicsPipelineState &ps, const QSSGAmbientOcclusionSettings &ao, const QSSGRhiRenderableTexture &rhiAoTexture, const QSSGRhiRenderableTexture &rhiDepthTexture, const QSSGRenderCamera &camera)
std::pair< QSSGBoxPoints, QSSGBoxPoints > calculateSortedObjectBounds(const QSSGRenderableObjectList &sortedOpaqueObjects, const QSSGRenderableObjectList &sortedTransparentObjects)
bool rhiPrepareAoTexture(QSSGRhiContext *rhiCtx, const QSize &size, QSSGRhiRenderableTexture *renderableTex)
bool rhiPrepareDepthTexture(QSSGRhiContext *rhiCtx, const QSize &size, QSSGRhiRenderableTexture *renderableTex)
Q_QUICK3DRUNTIMERENDER_EXPORT void rhiRenderRenderable(QSSGRhiContext *rhiCtx, const QSSGRhiGraphicsPipelineState &state, QSSGRenderableObject &object, bool *needsSetViewport, QSSGRenderTextureCubeFace cubeFace=QSSGRenderTextureCubeFaceNone)
void rhiRenderShadowMap(QSSGRhiContext *rhiCtx, QSSGPassKey passKey, QSSGRhiGraphicsPipelineState &ps, QSSGRenderShadowMap &shadowMapManager, const QSSGRenderCamera &camera, const QSSGShaderLightList &globalLights, const QSSGRenderableObjectList &sortedOpaqueObjects, QSSGRenderer &renderer, const QSSGBoxPoints &castingObjectsBox, const QSSGBoxPoints &receivingObjectsBox)
bool rhiPrepareDepthPass(QSSGRhiContext *rhiCtx, QSSGPassKey passKey, const QSSGRhiGraphicsPipelineState &basePipelineState, QRhiRenderPassDescriptor *rpDesc, QSSGLayerRenderData &inData, const QSSGRenderableObjectList &sortedOpaqueObjects, const QSSGRenderableObjectList &sortedTransparentObjects, int samples, int viewCount)
void rhiPrepareSkyBox(QSSGRhiContext *rhiCtx, QSSGPassKey passKey, QSSGRenderLayer &layer, QSSGRenderCameraList &cameras, QSSGRenderer &renderer)
Q_QUICK3DRUNTIMERENDER_EXPORT void rhiPrepareRenderable(QSSGRhiContext *rhiCtx, QSSGPassKey passKey, const QSSGLayerRenderData &inData, QSSGRenderableObject &inObject, QRhiRenderPassDescriptor *renderPassDescriptor, QSSGRhiGraphicsPipelineState *ps, QSSGShaderFeatures featureSet, int samples, int viewCount, QSSGRenderCamera *alteredCamera=nullptr, QMatrix4x4 *alteredModelViewProjection=nullptr, QSSGRenderTextureCubeFace cubeFace=QSSGRenderTextureCubeFaceNone, QSSGReflectionMapEntry *entry=nullptr)
void rhiRenderReflectionMap(QSSGRhiContext *rhiCtx, QSSGPassKey passKey, const QSSGLayerRenderData &inData, QSSGRhiGraphicsPipelineState *ps, QSSGRenderReflectionMap &reflectionMapManager, const QVector< QSSGRenderReflectionProbe * > &reflectionProbes, const QSSGRenderableObjectList &reflectionPassObjects, QSSGRenderer &renderer)
void rhiRenderDepthPass(QSSGRhiContext *rhiCtx, const QSSGRhiGraphicsPipelineState &ps, const QSSGRenderableObjectList &sortedOpaqueObjects, const QSSGRenderableObjectList &sortedTransparentObjects, bool *needsSetViewport)
static const int UBUF_SIZE
#define QByteArrayLiteral(str)
Definition qbytearray.h:52
EGLOutputLayerEXT layer
bool qFuzzyCompare(qfloat16 p1, qfloat16 p2) noexcept
Definition qfloat16.h:333
#define qWarning
Definition qlogging.h:166
constexpr float qRadiansToDegrees(float radians)
Definition qmath.h:281
#define M_PI_2
Definition qmath.h:213
#define M_PI
Definition qmath.h:209
constexpr float qDegreesToRadians(float degrees)
Definition qmath.h:260
constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:40
constexpr const T & qMax(const T &a, const T &b)
Definition qminmax.h:42
constexpr T qAbs(const T &t)
Definition qnumeric.h:328
GLsizei const GLfloat * v
[13]
GLuint64 GLenum void * handle
GLsizei samples
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLuint sampler
GLenum GLuint GLenum GLsizei length
GLdouble GLdouble right
GLenum face
const void GLsizei GLsizei stride
GLenum type
GLbitfield flags
GLenum GLuint texture
GLuint name
GLint GLsizei GLsizei GLenum format
GLfixed GLfixed GLint GLint GLfixed points
GLhandleARB obj
[2]
const GLubyte * c
GLuint entry
GLdouble GLdouble t
Definition qopenglext.h:243
GLenum GLenum GLenum GLenum GLenum scale
const void GLsizei GLsizei GLint vertexBufferCount
#define Q_QUICK3D_PROFILE_START(Type)
#define QSSG_RENDERPASS_NAME(passName, level, face)
#define Q_QUICK3D_PROFILE_END_WITH_STRING(Type, Payload, Str)
#define Q_QUICK3D_PROFILE_END_WITH_IDS(Type, Payload, POIDs)
static qreal position(const QQuickItem *item, QQuickAnchors::Anchor anchorLine)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define QSSG_ASSERT(cond, action)
std::array< QVector3D, 8 > QSSGBoxPoints
QSSGDataView< T > toDataView(const T &type)
std::array< QMatrix4x4, 2 > QSSGRenderMvpArray
static constexpr QSSGRenderTextureCubeFace QSSGRenderTextureCubeFaces[]
QSSGRenderTextureCubeFace
constexpr QSSGRenderTextureCubeFace QSSGRenderTextureCubeFaceNone
#define QSSGRHICTX_STAT(ctx, f)
std::shared_ptr< QSSGRhiShaderPipeline > QSSGRhiShaderPipelinePtr
SSL_CTX int(* cb)(SSL *ssl, unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen, void *arg)
#define a0
#define a1
unsigned int quint32
Definition qtypes.h:50
size_t quintptr
Definition qtypes.h:167
unsigned long long quint64
Definition qtypes.h:61
ptrdiff_t qsizetype
Definition qtypes.h:165
unsigned char quint8
Definition qtypes.h:46
static void rhiPrepareSkyBox_helper(QSSGRhiContext *rhiCtx, QSSGPassKey passKey, QSSGRenderLayer &layer, QSSGRenderCameraList &cameras, QSSGRenderer &renderer, QSSGReflectionMapEntry *entry=nullptr, QSSGRenderTextureCubeFace cubeFace=QSSGRenderTextureCubeFaceNone)
static QSSGBounds3 calculateShadowCameraBoundingBox(const QSSGBoxPoints &points, const QVector3D &forward, const QVector3D &up, const QVector3D &right)
static const QRhiShaderResourceBinding::StageFlags RENDERER_VISIBILITY_ALL
static QT_BEGIN_NAMESPACE constexpr float QSSG_PI
static void setupCameraForShadowMap(const QSSGRenderCamera &inCamera, const QSSGRenderLight *inLight, QSSGRenderCamera &theCamera, const QSSGBoxPoints &castingBox, const QSSGBoxPoints &receivingBox)
static void setupCubeReflectionCameras(const QSSGRenderReflectionProbe *inProbe, QSSGRenderCamera inCameras[6])
static void rhiPrepareResourcesForReflectionMap(QSSGRhiContext *rhiCtx, QSSGPassKey passKey, const QSSGLayerRenderData &inData, QSSGReflectionMapEntry *pEntry, QSSGRhiGraphicsPipelineState *ps, const QSSGRenderableObjectList &sortedOpaqueObjects, QSSGRenderCamera &inCamera, QSSGRenderer &renderer, QSSGRenderTextureCubeFace cubeFace)
static QVector3D calcCenter(const QSSGBoxPoints &vertices)
static void updateUniformsForDefaultMaterial(QSSGRhiShaderPipeline &shaderPipeline, QSSGRhiContext *rhiCtx, const QSSGLayerRenderData &inData, char *ubufData, QSSGRhiGraphicsPipelineState *ps, QSSGSubsetRenderable &subsetRenderable, const QSSGRenderCameraList &cameras, const QVector2D *depthAdjust, const QMatrix4x4 *alteredModelViewProjection)
static void addDepthTextureBindings(QSSGRhiContext *rhiCtx, QSSGRhiShaderPipeline *shaderPipeline, QSSGRhiShaderResourceBindingList &bindings)
static void addOpaqueDepthPrePassBindings(QSSGRhiContext *rhiCtx, QSSGRhiShaderPipeline *shaderPipeline, QSSGRenderableImage *renderableImage, QSSGRhiShaderResourceBindingList &bindings, bool isCustomMaterialMeshSubset=false)
static constexpr float QSSG_HALFPI
static QSSGRhiShaderPipelinePtr shadersForDefaultMaterial(QSSGRhiGraphicsPipelineState *ps, QSSGSubsetRenderable &subsetRenderable, const QSSGShaderFeatures &featureSet)
static QSSGRhiShaderPipelinePtr shadersForParticleMaterial(QSSGRhiGraphicsPipelineState *ps, QSSGParticlesRenderable &particleRenderable)
static void rhiPrepareResourcesForShadowMap(QSSGRhiContext *rhiCtx, const QSSGLayerRenderData &inData, QSSGPassKey passKey, QSSGShadowMapEntry *pEntry, QSSGRhiGraphicsPipelineState *ps, const QVector2D *depthAdjust, const QSSGRenderableObjectList &sortedOpaqueObjects, QSSGRenderCamera &inCamera, bool orthographic, QSSGRenderTextureCubeFace cubeFace)
static void setupCubeShadowCameras(const QSSGRenderLight *inLight, QSSGRenderCamera inCameras[6])
static QSSGBoxPoints computeFrustumBounds(const QSSGRenderCamera &inCamera)
static int setupInstancing(QSSGSubsetRenderable *renderable, QSSGRhiGraphicsPipelineState *ps, QSSGRhiContext *rhiCtx, const QVector3D &cameraDirection, const QVector3D &cameraPosition)
static void fillTargetBlend(QRhiGraphicsPipeline::TargetBlend *targetBlend, QSSGRenderDefaultMaterial::MaterialBlendMode materialBlend)
myFilter draw(painter, QPoint(0, 0), originalPixmap)
QSvgRenderer * renderer
[0]
static const char * getSamplerName(QSSGRenderableImage::Type type)
static void setRhiMaterialProperties(const QSSGRenderContextInterface &, QSSGRhiShaderPipeline &shaders, char *ubufData, QSSGRhiGraphicsPipelineState *inPipelineState, const QSSGRenderGraphObject &inMaterial, const QSSGShaderDefaultMaterialKey &inKey, const QSSGShaderDefaultMaterialKeyProperties &inProperties, const QSSGRenderCameraList &inCameras, const QSSGRenderMvpArray &inModelViewProjections, const QMatrix3x3 &inNormalMatrix, const QMatrix4x4 &inGlobalTransform, const QMatrix4x4 &clipSpaceCorrMatrix, const QMatrix4x4 &localInstanceTransform, const QMatrix4x4 &globalInstanceTransform, const QSSGDataView< float > &inMorphWeights, QSSGRenderableImage *inFirstImage, float inOpacity, const QSSGLayerRenderData &inRenderProperties, const QSSGShaderLightListView &inLights, const QSSGShaderReflectionProbe &reflectionProbe, bool receivesShadows, bool receivesReflections, const QVector2D *shadowDepthAdjust, QRhiTexture *lightmapTexture)
QSSGRenderImageTextureFlags m_flags
static constexpr QVector3D initScale
static QMatrix4x4 calculateTransformMatrix(QVector3D position, QVector3D scale, QVector3D pivot, QQuaternion rotation)
static void setShaderPipeline(QSSGRhiGraphicsPipelineState &ps, const QSSGRhiShaderPipeline *pipeline)
static const InputAssemblerState & get(const QSSGRhiGraphicsPipelineState &ps)
QRhiTextureRenderTarget * rt
\variable QSSGRhiGraphicsPipelineState::depthFunc
constexpr bool isSet(Feature feature) const
void set(Feature feature, bool val)