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
qssgrhieffectsystem.cpp
Go to the documentation of this file.
1// Copyright (C) 2008-2012 NVIDIA Corporation.
2// Copyright (C) 2020 The Qt Company Ltd.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
4
5#include <QtQuick3DRuntimeRender/private/qssgrhieffectsystem_p.h>
6#include <QtQuick3DRuntimeRender/private/qssgrenderer_p.h>
7#include <QtQuick3DRuntimeRender/private/qssgrhiquadrenderer_p.h>
10#include <qtquick3d_tracepoints_p.h>
11
12#include <QtQuick3DUtils/private/qssgassert_p.h>
13
14#include <QtCore/qloggingcategory.h>
15
17
19Q_LOGGING_CATEGORY(lcEffectSystem, "qt.quick3d.effects");
20
39
40QSSGRhiEffectSystem::QSSGRhiEffectSystem(const std::shared_ptr<QSSGRenderContextInterface> &sgContext)
41 : m_sgContext(sgContext)
42{
43}
44
46{
47 releaseResources();
48}
49
51{
52 if (outputSize.isEmpty()) {
53 releaseResources();
54 return;
55 }
56 m_outSize = outputSize;
57}
58
59QSSGRhiEffectTexture *QSSGRhiEffectSystem::findTexture(const QByteArray &bufferName)
60{
61 auto findTexture = [bufferName](const QSSGRhiEffectTexture *rt){ return rt->name == bufferName; };
62 const auto foundIt = std::find_if(m_textures.cbegin(), m_textures.cend(), findTexture);
63 QSSGRhiEffectTexture *result = foundIt == m_textures.cend() ? nullptr : *foundIt;
64 return result;
65}
66
67QSSGRhiEffectTexture *QSSGRhiEffectSystem::getTexture(const QByteArray &bufferName,
68 const QSize &size,
70 bool isFinalOutput,
71 const QSSGRenderEffect *inEffect)
72{
73 QSSGRhiEffectTexture *result = findTexture(bufferName);
74 const bool gotMatch = result != nullptr;
75
76 // If not found, look for an unused texture
77 if (!result) {
78 // ### This could be enhanced to try to find a texture with the right
79 // size/format/flags first. It is not essential because the texture will be
80 // recreated below if the size or format does not match, but it would
81 // be more optimal (for Effects with Buffers with sizeMultipliers on
82 // them, or ones that use different formats) to look for a matching
83 // size/format too instead of picking the first unused texture.
84 auto findUnused = [](const QSSGRhiEffectTexture *rt){ return rt->name.isEmpty(); };
85 const auto found = std::find_if(m_textures.cbegin(), m_textures.cend(), findUnused);
86 if (found != m_textures.cend()) {
87 result = *found;
88 result->desc = {};
89 }
90 }
91
92 if (!result) {
94 m_textures.append(result);
95 }
96
97 const auto &rhiCtx = m_sgContext->rhiContext();
98 QRhi *rhi = rhiCtx->rhi();
99 const bool formatChanged = result->texture && result->texture->format() != format;
100 const bool needsRebuild = result->texture && (result->texture->pixelSize() != size || formatChanged);
101
102 QRhiTexture::Flags flags = QRhiTexture::RenderTarget;
103 if (isFinalOutput) // play nice with progressive/temporal AA
105
106 if (!result->texture) {
107 if (rhiCtx->mainPassViewCount() >= 2)
108 result->texture = rhi->newTextureArray(format, rhiCtx->mainPassViewCount(), size, 1, flags);
109 else
110 result->texture = rhi->newTexture(format, size, 1, flags);
111 result->texture->create();
112 } else if (needsRebuild) {
113 result->texture->setFlags(flags);
114 result->texture->setPixelSize(size);
115 result->texture->setFormat(format);
116 result->texture->create();
117 }
118
119 if (!result->renderTarget) {
120 QRhiColorAttachment colorAttachment(result->texture);
121 colorAttachment.setMultiViewCount(rhiCtx->mainPassViewCount());
123 result->renderTarget = rhi->newTextureRenderTarget(desc);
125 result->renderTarget->setRenderPassDescriptor(result->renderPassDescriptor);
126 result->renderTarget->create();
127 m_pendingClears.insert(result->renderTarget);
128 } else if (needsRebuild) {
129 if (formatChanged) {
130 delete result->renderPassDescriptor;
131 result->renderPassDescriptor = result->renderTarget->newCompatibleRenderPassDescriptor();
132 result->renderTarget->setRenderPassDescriptor(result->renderPassDescriptor);
133 }
134 result->renderTarget->create();
135 m_pendingClears.insert(result->renderTarget);
136 }
137
138 if (!gotMatch) {
139 QByteArray rtName = inEffect->debugObjectName.toLatin1();
140 rtName += QByteArrayLiteral(" effect pass ");
141 rtName += bufferName;
142 result->renderTarget->setName(rtName);
143 }
144
145 result->name = bufferName;
146 return result;
147}
148
149void QSSGRhiEffectSystem::releaseTexture(QSSGRhiEffectTexture *texture)
150{
151 // Mark as unused by setting the name to empty, unless the Buffer had scene
152 // lifetime on it (then it needs to live on for ever).
153 if (!texture->flags.isSceneLifetime())
154 texture->name = {};
155}
156
157void QSSGRhiEffectSystem::releaseTextures()
158{
159 for (auto *t : std::as_const(m_textures))
160 releaseTexture(t);
161}
162
164 QRhiTexture *inTexture,
165 QRhiTexture *inDepthTexture,
166 QVector2D cameraClipRange)
167{
168 QSSG_ASSERT(m_sgContext != nullptr, return inTexture);
169 const auto &rhiContext = m_sgContext->rhiContext();
170 const auto &renderer = m_sgContext->renderer();
171 QSSG_ASSERT(rhiContext && renderer, return inTexture);
172
173 m_depthTexture = inDepthTexture;
174 m_cameraClipRange = cameraClipRange;
175
176 m_currentUbufIndex = 0;
177 auto *currentEffect = &firstEffect;
178 QSSGRhiEffectTexture firstTex{ inTexture, nullptr, nullptr, {}, {}, {} };
179 auto *latestOutput = doRenderEffect(currentEffect, &firstTex);
180 firstTex.texture = nullptr; // make sure we don't delete inTexture when we go out of scope
181
182 while ((currentEffect = currentEffect->m_nextEffect)) {
183 auto *effectOut = doRenderEffect(currentEffect, latestOutput);
184 releaseTexture(latestOutput);
185 latestOutput = effectOut;
186 }
187
188 releaseTextures();
189 return latestOutput ? latestOutput->texture : nullptr;
190}
191
192void QSSGRhiEffectSystem::releaseResources()
193{
194 qDeleteAll(m_textures);
195 m_textures.clear();
196
197 m_shaderPipelines.clear();
198}
199
201{
203 for (const QSSGRenderEffect::Command &c : inEffect->commands) {
204 QSSGCommand *cmd = c.command;
205 if (cmd->m_type == CommandType::BindTarget) {
206 QSSGBindTarget *targetCmd = static_cast<QSSGBindTarget *>(cmd);
208 ? inEffect->outputFormat : targetCmd->m_outputFormat.format;
209 }
210 }
211 return format;
212}
213
214QSSGRhiEffectTexture *QSSGRhiEffectSystem::doRenderEffect(const QSSGRenderEffect *inEffect,
215 QSSGRhiEffectTexture *inTexture)
216{
217 // Run through the effect commands and render the effect.
218 qCDebug(lcEffectSystem) << "START effect " << inEffect->className;
219 QSSGRhiEffectTexture *finalOutputTexture = nullptr;
220 QSSGRhiEffectTexture *currentOutput = nullptr;
221 QSSGRhiEffectTexture *currentInput = inTexture;
222 for (const QSSGRenderEffect::Command &c : inEffect->commands) {
223 QSSGCommand *theCommand = c.command;
224 qCDebug(lcEffectSystem).noquote() << " >" << theCommand->typeAsString() << "--" << theCommand->debugString();
225
226 switch (theCommand->m_type) {
228 allocateBufferCmd(static_cast<QSSGAllocateBuffer *>(theCommand), inTexture, inEffect);
229 break;
230
232 auto *applyCommand = static_cast<QSSGApplyBufferValue *>(theCommand);
233
234 /*
235 BufferInput { buffer: buf }
236 -> INPUT (qt_inputTexture) in the shader samples the texture for Buffer buf in the pass
237 BufferInput { sampler: "ttt" }
238 -> ttt in the shader samples the input texture for the pass
239 (ttt also needs to be a TextureInput with a Texture{} to get the sampler declared in the shader code,
240 beware that without the BufferInput the behavior would change: ttt would then sample a dummy texture)
241 BufferInput { buffer: buf; sampler: "ttt" }
242 -> ttt in the shader samples the texture for Buffer buf in the pass
243 */
244
245 auto *buffer = applyCommand->m_bufferName.isEmpty() ? inTexture : findTexture(applyCommand->m_bufferName);
246 if (applyCommand->m_samplerName.isEmpty())
247 currentInput = buffer;
248 else
249 addTextureToShaderPipeline(applyCommand->m_samplerName, buffer->texture, buffer->desc);
250 break;
251 }
252
254 applyInstanceValueCmd(static_cast<QSSGApplyInstanceValue *>(theCommand), inEffect);
255 break;
256
258 applyValueCmd(static_cast<QSSGApplyValue *>(theCommand), inEffect);
259 break;
260
262 auto *bindCmd = static_cast<QSSGBindBuffer *>(theCommand);
263 currentOutput = findTexture(bindCmd->m_bufferName);
264 break;
265 }
266
268 bindShaderCmd(static_cast<QSSGBindShader *>(theCommand), inEffect);
269 break;
270
272 auto targetCmd = static_cast<QSSGBindTarget*>(theCommand);
273 // matches overriddenOutputFormat()
275 inEffect->outputFormat : targetCmd->m_outputFormat.format;
276 // f is now either Unknown (common case), or if the effect overrides the output format, then that
278 currentInput->texture->format() : QSSGBufferManager::toRhiFormat(f);
279 qCDebug(lcEffectSystem) << " Target format override" << QSSGBaseTypeHelpers::toString(f) << "Effective RHI format" << rhiFormat;
280 // Make sure we use different names for each effect inside one frame
281 QByteArray tmpName = QByteArrayLiteral("__output_").append(QByteArray::number(m_currentUbufIndex));
282 currentOutput = getTexture(tmpName, m_outSize, rhiFormat, true, inEffect);
283 finalOutputTexture = currentOutput;
284 break;
285 }
286
288 renderCmd(currentInput, currentOutput);
289 currentInput = inTexture; // default input for each new pass is defined to be original input
290 break;
291
292 default:
293 qWarning() << "Effect command" << theCommand->typeAsString() << "not implemented";
294 break;
295 }
296 }
297 // TODO: release textures used by this effect now, instead of after processing all the effects
298 qCDebug(lcEffectSystem) << "END effect " << inEffect->className;
299 return finalOutputTexture;
300}
301
302void QSSGRhiEffectSystem::allocateBufferCmd(const QSSGAllocateBuffer *inCmd, QSSGRhiEffectTexture *inTexture, const QSSGRenderEffect *inEffect)
303{
304 // Note: Allocate is used both to allocate new, and refer to buffer created earlier
305 QSize bufferSize(m_outSize * qreal(inCmd->m_sizeMultiplier));
306
307 QSSGRenderTextureFormat f = inCmd->m_format;
308 QRhiTexture::Format rhiFormat = (f == QSSGRenderTextureFormat::Unknown) ? inTexture->texture->format()
310
311 QSSGRhiEffectTexture *buf = getTexture(inCmd->m_name, bufferSize, rhiFormat, false, inEffect);
312 auto filter = QSSGRhiHelpers::toRhi(inCmd->m_filterOp);
313 auto tiling = QSSGRhiHelpers::toRhi(inCmd->m_texCoordOp);
314 buf->desc = { filter, filter, QRhiSampler::None, tiling, tiling, QRhiSampler::Repeat };
315 buf->flags = inCmd->m_bufferFlags;
316}
317
318void QSSGRhiEffectSystem::applyInstanceValueCmd(const QSSGApplyInstanceValue *inCmd, const QSSGRenderEffect *inEffect)
319{
320 if (!m_currentShaderPipeline)
321 return;
322
323 const bool setAll = inCmd->m_propertyName.isEmpty();
324 for (const QSSGRenderEffect::Property &property : std::as_const(inEffect->properties)) {
325 if (setAll || property.name == inCmd->m_propertyName) {
326 m_currentShaderPipeline->setUniformValue(m_currentUBufData, property.name, property.value, property.shaderDataType);
327 //qCDebug(lcEffectSystem) << "setUniformValue" << property.name << toString(property.shaderDataType) << "to" << property.value;
328 }
329 }
330 for (const QSSGRenderEffect::TextureProperty &textureProperty : std::as_const(inEffect->textureProperties)) {
331 if (setAll || textureProperty.name == inCmd->m_propertyName) {
332 bool texAdded = false;
333 QSSGRenderImage *image = textureProperty.texImage;
334 if (image) {
335 const auto &theBufferManager(m_sgContext->bufferManager());
336 const QSSGRenderImageTexture texture = theBufferManager->loadRenderImage(image);
337 if (texture.m_texture) {
339 QSSGRhiHelpers::toRhi(textureProperty.minFilterType),
340 QSSGRhiHelpers::toRhi(textureProperty.magFilterType),
341 textureProperty.mipFilterType != QSSGRenderTextureFilterOp::None ? QSSGRhiHelpers::toRhi(textureProperty.mipFilterType) : QRhiSampler::None,
342 QSSGRhiHelpers::toRhi(textureProperty.horizontalClampType),
343 QSSGRhiHelpers::toRhi(textureProperty.verticalClampType),
344 QSSGRhiHelpers::toRhi(textureProperty.zClampType)
345 };
346 addTextureToShaderPipeline(textureProperty.name, texture.m_texture, desc);
347 texAdded = true;
348 }
349 }
350 if (!texAdded) {
351 // Something went wrong, e.g. image file not found. Still need to add a dummy texture for the shader
352 qCDebug(lcEffectSystem) << "Using dummy texture for property" << textureProperty.name;
353 addTextureToShaderPipeline(textureProperty.name, nullptr, {});
354 }
355 }
356 }
357}
358
359void QSSGRhiEffectSystem::applyValueCmd(const QSSGApplyValue *inCmd, const QSSGRenderEffect *inEffect)
360{
361 if (!m_currentShaderPipeline)
362 return;
363
364 const auto &properties = inEffect->properties;
365 const auto foundIt = std::find_if(properties.cbegin(), properties.cend(), [inCmd](const QSSGRenderEffect::Property &prop) {
366 return (prop.name == inCmd->m_propertyName);
367 });
368
369 if (foundIt != properties.cend())
370 m_currentShaderPipeline->setUniformValue(m_currentUBufData, inCmd->m_propertyName, inCmd->m_value, foundIt->shaderDataType);
371 else
372 qWarning() << "Could not find effect property" << inCmd->m_propertyName;
373}
374
375static const char *effect_builtin_textureMapUV =
376 "vec2 qt_effectTextureMapUV(vec2 uv)\n"
377 "{\n"
378 " return uv;\n"
379 "}\n";
380
382 "vec2 qt_effectTextureMapUV(vec2 uv)\n"
383 "{\n"
384 " return vec2(uv.x, 1.0 - uv.y);\n"
385 "}\n";
386
389 QSSGShaderLibraryManager &shaderLib,
390 QSSGShaderCache &shaderCache,
391 bool isYUpInFramebuffer,
392 int viewCount)
393{
394 const auto &key = inCmd.m_shaderPathKey;
395 qCDebug(lcEffectSystem) << " generating new shader pipeline for: " << key;
396
397 generator.beginProgram();
398
399 {
400 const QByteArray src = shaderLib.getShaderSource(inCmd.m_shaderPathKey, QSSGShaderCache::ShaderType::Vertex);
402 // The variation based on isYUpInFramebuffer is captured in 'key' as
403 // well, so it is safe to vary the source code here.
404 vStage->append(isYUpInFramebuffer ? effect_builtin_textureMapUV : effect_builtin_textureMapUVFlipped);
405 vStage->append(src);
406 }
407 {
408 const QByteArray src = shaderLib.getShaderSource(inCmd.m_shaderPathKey, QSSGShaderCache::ShaderType::Fragment);
410 fStage->append(src);
411 }
412
413 return generator.compileGeneratedRhiShader(key,
414 shaderLib.getShaderMetaData(inCmd.m_shaderPathKey, QSSGShaderCache::ShaderType::Fragment).features,
415 shaderLib,
416 shaderCache,
418 viewCount,
419 false);
420}
421
422void QSSGRhiEffectSystem::bindShaderCmd(const QSSGBindShader *inCmd, const QSSGRenderEffect *inEffect)
423{
425 timer.start();
426
427 m_currentTextures.clear();
428 m_pendingClears.clear();
429 m_currentShaderPipeline = nullptr;
430
431 const auto &rhiCtx = m_sgContext->rhiContext();
432 QRhi *rhi = rhiCtx->rhi();
433 const auto &shaderLib = m_sgContext->shaderLibraryManager();
434 const auto &shaderCache = m_sgContext->shaderCache();
435
436 // Now we need a proper unique key (unique in the scene), the filenames are
437 // not sufficient. This means that using the same shader source files in
438 // multiple Effects in the same scene will work. It wouldn't if all those
439 // Effects reused the same QSSGRhiShaderPipeline (i.e. if the only cache
440 // key was the m_shaderPathKey).
442 cacheKey.m_shaderPathKey = inCmd->m_shaderPathKey;
443 cacheKey.m_cmd = quintptr(inCmd);
444 cacheKey.m_ubufIndex = m_currentUbufIndex;
445 cacheKey.updateHashCode();
446
447 // look for a runtime pipeline
448 const auto it = m_shaderPipelines.constFind(cacheKey);
449 if (it != m_shaderPipelines.cend())
450 m_currentShaderPipeline = (*it).get();
451
452 QByteArray qsbcKey;
453 QSSGShaderFeatures features;
454 if (!m_currentShaderPipeline) { // don't spend time if already got the pipeline
455 features = shaderLib->getShaderMetaData(inCmd->m_shaderPathKey, QSSGShaderCache::ShaderType::Fragment).features;
456 qsbcKey = QQsbCollection::EntryDesc::generateSha(inCmd->m_shaderPathKey, QQsbCollection::toFeatureSet(features));
457 }
458
459 // Check if there's a build-time generated entry for this effect
460 if (!m_currentShaderPipeline && !shaderLib->m_preGeneratedShaderEntries.isEmpty()) {
461 const QQsbCollection::EntryMap &pregenEntries = shaderLib->m_preGeneratedShaderEntries;
462 const auto foundIt = pregenEntries.constFind(QQsbCollection::Entry(qsbcKey));
463 if (foundIt != pregenEntries.cend()) {
464 // The result here is always a new QSSGRhiShaderPipeline, which
465 // fulfills the requirements of our local cache (cmd/ubufIndex in
466 // cacheKey, not needed here since the result is a new object).
467 const auto &shader = shaderCache->newPipelineFromPregenerated(inCmd->m_shaderPathKey,
468 features,
469 *foundIt,
470 *inEffect,
472 m_shaderPipelines.insert(cacheKey, shader);
473 m_currentShaderPipeline = shader.get();
474 }
475 }
476
477 if (!m_currentShaderPipeline) {
478 // Try the persistent (disk-based) cache then. The result here is
479 // always a new QSSGRhiShaderPipeline, which fulfills the requirements
480 // of our local cache (cmd/ubufIndex in cacheKey, not needed here since
481 // the result is a new object). Alternatively, the result may be null
482 // if there was no hit.
483 const auto &shaderPipeline = shaderCache->tryNewPipelineFromPersistentCache(qsbcKey,
484 inCmd->m_shaderPathKey,
485 features,
487 if (shaderPipeline) {
488 m_shaderPipelines.insert(cacheKey, shaderPipeline);
489 m_currentShaderPipeline = shaderPipeline.get();
490 }
491 }
492
493 if (!m_currentShaderPipeline) {
494 // Final option, generate the shader pipeline
495 Q_TRACE_SCOPE(QSSG_generateShader);
496 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DGenerateShader);
497 const auto &generator = m_sgContext->shaderProgramGenerator();
498 if (auto stages = buildShaderForEffect(*inCmd, *generator, *shaderLib, *shaderCache, rhi->isYUpInFramebuffer(), rhiCtx->mainPassViewCount())) {
499 m_shaderPipelines.insert(cacheKey, stages);
500 m_currentShaderPipeline = stages.get();
501 }
502 Q_QUICK3D_PROFILE_END_WITH_ID(QQuick3DProfiler::Quick3DGenerateShader, 0, inEffect->profilingId);
503 }
504
505 const auto &rhiContext = m_sgContext->rhiContext();
506
507 if (m_currentShaderPipeline) {
508 const void *cacheKey1 = reinterpret_cast<const void *>(this);
509 const void *cacheKey2 = reinterpret_cast<const void *>(qintptr(m_currentUbufIndex));
510 QSSGRhiDrawCallData &dcd = QSSGRhiContextPrivate::get(rhiContext.get())->drawCallData({ cacheKey1, cacheKey2, nullptr, 0 });
511 m_currentShaderPipeline->ensureCombinedMainLightsUniformBuffer(&dcd.ubuf);
512 m_currentUBufData = dcd.ubuf->beginFullDynamicBufferUpdateForCurrentFrame();
513 } else {
514 m_currentUBufData = nullptr;
515 }
516
517 QSSGRhiContextStats::get(*rhiContext).registerEffectShaderGenerationTime(timer.elapsed());
518}
519
520void QSSGRhiEffectSystem::renderCmd(QSSGRhiEffectTexture *inTexture, QSSGRhiEffectTexture *target)
521{
522 if (!m_currentShaderPipeline)
523 return;
524
525 if (!target) {
526 qWarning("No effect render target?");
527 return;
528 }
529
530 // the shader only uses one of these (or none)
531 addTextureToShaderPipeline(QByteArrayLiteral("qt_inputTexture"), inTexture->texture, inTexture->desc);
532 addTextureToShaderPipeline(QByteArrayLiteral("qt_inputTextureArray"), inTexture->texture, inTexture->desc);
533
534 const auto &rhiContext = m_sgContext->rhiContext();
535 const auto &renderer = m_sgContext->renderer();
536
537 QRhiCommandBuffer *cb = rhiContext->commandBuffer();
538 cb->debugMarkBegin(QByteArrayLiteral("Post-processing effect"));
539 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderPass);
540
541 for (QRhiTextureRenderTarget *rt : m_pendingClears) {
542 // Effects like motion blur use an accumulator texture that should
543 // start out empty (and they are sampled in the first pass), so such
544 // textures need an explicit clear. It is not applicable for the common
545 // case of outputting into a texture because that will get a clear
546 // anyway when rendering the quad.
547 if (rt != target->renderTarget) {
548 cb->beginPass(rt, Qt::transparent, { 1.0f, 0 }, nullptr, rhiContext->commonPassFlags());
549 QSSGRHICTX_STAT(rhiContext, beginRenderPass(rt));
550 cb->endPass();
551 QSSGRHICTX_STAT(rhiContext, endRenderPass());
552 }
553 }
554 m_pendingClears.clear();
555
556 const QSize inputSize = inTexture->texture->pixelSize();
557 const QSize outputSize = target->texture->pixelSize();
558 addCommonEffectUniforms(inputSize, outputSize);
559
560 QSSGRhiContextPrivate *rhiCtxD = QSSGRhiContextPrivate::get(rhiContext.get());
561
562 const void *cacheKey1 = reinterpret_cast<const void *>(this);
563 const void *cacheKey2 = reinterpret_cast<const void *>(qintptr(m_currentUbufIndex));
564 QSSGRhiDrawCallData &dcd = rhiCtxD->drawCallData({ cacheKey1, cacheKey2, nullptr, 0 });
566 m_currentUBufData = nullptr;
567
568 QRhiResourceUpdateBatch *rub = rhiContext->rhi()->nextResourceUpdateBatch();
569 renderer->rhiQuadRenderer()->prepareQuad(rhiContext.get(), rub);
570
571 // do resource bindings
572 const QRhiShaderResourceBinding::StageFlags VISIBILITY_ALL =
575 for (const QSSGRhiTexture &rhiTex : m_currentTextures) {
576 int binding = m_currentShaderPipeline->bindingForTexture(rhiTex.name);
577 if (binding < 0) // may not be used in the shader (think qt_inputTexture, it's not given a shader samples INPUT)
578 continue;
579 qCDebug(lcEffectSystem) << " -> texture binding" << binding << "for" << rhiTex.name;
580 // Make sure to bind all samplers even if the texture is missing, otherwise we can get crash on some graphics APIs
581 QRhiTexture *texture = rhiTex.texture ? rhiTex.texture : rhiContext->dummyTexture({}, rub);
582 bindings.addTexture(binding,
584 texture,
585 rhiContext->sampler(rhiTex.samplerDesc));
586 }
587 bindings.addUniformBuffer(0, VISIBILITY_ALL, dcd.ubuf);
588
589 QRhiShaderResourceBindings *srb = rhiCtxD->srb(bindings);
590
592 ps.viewport = QRhiViewport(0, 0, float(outputSize.width()), float(outputSize.height()));
593 ps.samples = rhiContext->mainPassSampleCount();
594 ps.viewCount = rhiContext->mainPassViewCount();
596
597 renderer->rhiQuadRenderer()->recordRenderQuadPass(rhiContext.get(), &ps, srb, target->renderTarget, QSSGRhiQuadRenderer::UvCoords);
598 m_currentUbufIndex++;
599 cb->debugMarkEnd();
600 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DRenderPass, 0, QByteArrayLiteral("post_processing_effect"));
601}
602
603void QSSGRhiEffectSystem::addCommonEffectUniforms(const QSize &inputSize, const QSize &outputSize)
604{
605 const auto &rhiContext = m_sgContext->rhiContext();
606 QRhi *rhi = rhiContext->rhi();
607
608 QMatrix4x4 mvp;
609 if (rhi->isYUpInFramebuffer() != rhi->isYUpInNDC())
610 mvp.data()[5] = -1.0f;
611 m_currentShaderPipeline->setUniformValue(m_currentUBufData, "qt_modelViewProjection", mvp, QSSGRenderShaderValue::Matrix4x4);
612
613 QVector2D size(inputSize.width(), inputSize.height());
614 m_currentShaderPipeline->setUniformValue(m_currentUBufData, "qt_inputSize", size, QSSGRenderShaderValue::Vec2);
615
616 size = QVector2D(outputSize.width(), outputSize.height());
617 m_currentShaderPipeline->setUniformValue(m_currentUBufData, "qt_outputSize", size, QSSGRenderShaderValue::Vec2);
618
619 float fc = float(m_sgContext->renderer()->frameCount());
620 m_currentShaderPipeline->setUniformValue(m_currentUBufData, "qt_frame_num", fc, QSSGRenderShaderValue::Float);
621
622 // Bames and values for uniforms that are also used by default and/or
623 // custom materials must always match, effects must not deviate.
624
625 m_currentShaderPipeline->setUniformValue(m_currentUBufData, "qt_cameraProperties", m_cameraClipRange, QSSGRenderShaderValue::Vec2);
626
627 float vp = rhi->isYUpInFramebuffer() ? 1.0f : -1.0f;
628 m_currentShaderPipeline->setUniformValue(m_currentUBufData, "qt_normalAdjustViewportFactor", vp, QSSGRenderShaderValue::Float);
629
630 const float nearClip = rhi->isClipDepthZeroToOne() ? 0.0f : -1.0f;
631 m_currentShaderPipeline->setUniformValue(m_currentUBufData, "qt_nearClipValue", nearClip, QSSGRenderShaderValue::Float);
632
633 if (m_depthTexture) {
634 static const QSSGRhiSamplerDescription depthSamplerDesc {
638 };
639 addTextureToShaderPipeline("qt_depthTexture", m_depthTexture, depthSamplerDesc);
640 addTextureToShaderPipeline("qt_depthTextureArray", m_depthTexture, depthSamplerDesc);
641 }
642}
643
644void QSSGRhiEffectSystem::addTextureToShaderPipeline(const QByteArray &name,
646 const QSSGRhiSamplerDescription &samplerDescription)
647{
648 if (!m_currentShaderPipeline)
649 return;
650
653 bool validDescription = samplerDescription.magFilter != QRhiSampler::None;
654
655 // This is a map for a reason: there can be multiple calls to this function
656 // for the same 'name', with a different 'texture', take the last value
657 // into account only.
658 m_currentTextures.insert(name, { name, texture, validDescription ? samplerDescription : defaultDescription});
659}
660
\inmodule QtCore
Definition qbytearray.h:57
bool isEmpty() const noexcept
Returns true if the byte array has size 0; otherwise returns false.
Definition qbytearray.h:107
static QByteArray number(int, int base=10)
Returns a byte-array representing the whole number n as text.
\inmodule QtCore
const_iterator constFind(const Key &key) const noexcept
Definition qhash.h:1299
const_iterator cend() const noexcept
Definition qhash.h:1218
void clear() noexcept(std::is_nothrow_destructible< Node >::value)
Removes all items from the hash and frees up all memory used by it.
Definition qhash.h:951
iterator insert(const Key &key, const T &value)
Inserts a new item with the key and a value of value.
Definition qhash.h:1303
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.
static FeatureSet toFeatureSet(const T &ssgFeatureSet)
virtual void endFullDynamicBufferUpdateForCurrentFrame()
To be called when the entire contents of the buffer data has been updated in the memory block returne...
Definition qrhi.cpp:3980
\inmodule QtGui
Definition qrhi.h:576
\inmodule QtGui
Definition qrhi.h:1651
\inmodule QtGui
Definition qrhi.h:1142
virtual QRhiRenderPassDescriptor * newCompatibleRenderPassDescriptor() const =0
QRhiRenderPassDescriptor * renderPassDescriptor() const
Definition qrhi.h:1164
\inmodule QtGui
Definition qrhi.h:1731
\inmodule QtGui
Definition qrhi.h:1030
@ ClampToEdge
Definition qrhi.h:1040
\inmodule QtGui
Definition qrhi.h:1214
\inmodule QtGui
Definition qrhi.h:1184
\inmodule QtGui
Definition qrhi.h:895
@ UsedAsTransferSource
Definition qrhi.h:902
@ RenderTarget
Definition qrhi.h:898
virtual bool create()=0
Creates the corresponding native graphics resources.
Format
Specifies the texture format.
Definition qrhi.h:914
\inmodule QtGui
Definition qrhi.h:85
\inmodule QtGuiPrivate \inheaderfile rhi/qrhi.h
Definition qrhi.h:1804
bool isClipDepthZeroToOne() const
Definition qrhi.cpp:10069
QRhiTexture * newTextureArray(QRhiTexture::Format format, int arraySize, const QSize &pixelSize, int sampleCount=1, QRhiTexture::Flags flags={})
Definition qrhi.cpp:10636
bool isYUpInFramebuffer() const
Definition qrhi.cpp:10030
bool isYUpInNDC() const
Definition qrhi.cpp:10044
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
static const char * toString(QSSGRenderTextureCubeFace value)
static QRhiTexture::Format toRhiFormat(const QSSGRenderTextureFormat format)
static QSSGRhiContextPrivate * get(QSSGRhiContext *q)
static QSSGRhiContextStats & get(QSSGRhiContext &rhiCtx)
QRhiTexture * process(const QSSGRenderEffect &firstEffect, QRhiTexture *inTexture, QRhiTexture *inDepthTexture, QVector2D cameraClipRange)
void setup(QSize outputSize)
static QSSGRhiShaderPipelinePtr buildShaderForEffect(const QSSGBindShader &inCmd, QSSGProgramGenerator &generator, QSSGShaderLibraryManager &shaderLib, QSSGShaderCache &shaderCache, bool isYUpInFramebuffer, int viewCount)
static QSSGRenderTextureFormat::Format overriddenOutputFormat(const QSSGRenderEffect *inEffect)
QSSGRhiEffectSystem(const std::shared_ptr< QSSGRenderContextInterface > &sgContext)
void setUniformValue(char *ubufData, const char *name, const QVariant &value, QSSGRenderShaderValue::Type type)
void ensureCombinedMainLightsUniformBuffer(QRhiBuffer **ubuf)
int bindingForTexture(const char *name, int hint=-1)
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)
void clear()
Definition qset.h:61
const_iterator constFind(const T &value) const
Definition qset.h:161
iterator insert(const T &value)
Definition qset.h:155
\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 bool isEmpty() const noexcept
Returns true if either of the width and height is less than or equal to 0; otherwise returns false.
Definition qsize.h:124
void start(int msec)
Starts or restarts the timer with a timeout interval of msec milliseconds.
Definition qtimer.cpp:241
The QVector2D class represents a vector or vertex in 2D space.
Definition qvectornd.h:31
qDeleteAll(list.begin(), list.end())
QSet< QString >::iterator it
QRhiSampler::Filter toRhi(QSSGRenderTextureFilterOp op)
Combined button and popup list for selecting options.
@ transparent
Definition qnamespace.h:47
Definition image.cpp:4
#define QByteArrayLiteral(str)
Definition qbytearray.h:52
static const QCssKnownValue properties[NumProperties - 1]
@ None
Definition qhash.cpp:531
static QByteArray cacheKey(Args &&...args)
#define qWarning
Definition qlogging.h:166
#define Q_LOGGING_CATEGORY(name,...)
#define qCDebug(category,...)
#define Q_DECLARE_LOGGING_CATEGORY(name)
GLbitfield stages
GLuint64 key
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLsizei const GLubyte * commands
GLfloat GLfloat f
GLenum src
GLenum GLuint buffer
GLenum GLuint GLenum GLsizei const GLchar * buf
GLenum target
GLbitfield flags
GLenum GLuint texture
GLint GLint GLint GLint GLint GLint GLint GLbitfield GLenum filter
GLuint name
GLint GLsizei GLsizei GLenum format
const GLubyte * c
GLuint shader
Definition qopenglext.h:665
GLdouble GLdouble t
Definition qopenglext.h:243
GLuint64EXT * result
[6]
#define Q_QUICK3D_PROFILE_START(Type)
#define Q_QUICK3D_PROFILE_END_WITH_ID(Type, Payload, POID)
#define Q_QUICK3D_PROFILE_END_WITH_STRING(Type, Payload, Str)
#define QSSG_ASSERT(cond, action)
#define QSSGRHICTX_STAT(ctx, f)
std::shared_ptr< QSSGRhiShaderPipeline > QSSGRhiShaderPipelinePtr
static const char * effect_builtin_textureMapUVFlipped
static const char * effect_builtin_textureMapUV
static QT_BEGIN_NAMESPACE const QRhiShaderResourceBinding::StageFlags VISIBILITY_ALL
SSL_CTX int(* cb)(SSL *ssl, unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen, void *arg)
#define Q_TRACE_SCOPE(x,...)
Definition qtrace_p.h:146
size_t quintptr
Definition qtypes.h:167
double qreal
Definition qtypes.h:187
ptrdiff_t qintptr
Definition qtypes.h:166
const char property[13]
Definition qwizard.cpp:101
QRandomGenerator generator(sseq)
QObject::connect nullptr
QTimer * timer
[3]
QSvgRenderer * renderer
[0]
QByteArray generateSha() const
QSSGRenderTextureFormat m_outputFormat
QSSGRhiSamplerDescription desc
QRhiRenderPassDescriptor * renderPassDescriptor
QSSGAllocateBufferFlags flags
QRhiTextureRenderTarget * renderTarget
QSSGRhiEffectTexture & operator=(const QSSGRhiEffectTexture &)=delete
static void setShaderPipeline(QSSGRhiGraphicsPipelineState &ps, const QSSGRhiShaderPipeline *pipeline)
\variable QSSGRhiGraphicsPipelineState::depthFunc
QRhiSampler::Filter magFilter