Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qssgrendershadercodegenerator.cpp
Go to the documentation of this file.
1// Copyright (C) 2008-2012 NVIDIA Corporation.
2// Copyright (C) 2019 The Qt Company Ltd.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
4
6
7#include <QtQuick3DUtils/private/qssgutils_p.h>
8
10#include <QtQuick3DRuntimeRender/private/qssgrendershaderlibrarymanager_p.h>
11#include <QtQuick3DRuntimeRender/private/qssgshaderresourcemergecontext_p.h>
12
14
15template<typename T>
16static inline void addStartCond(QByteArray &block, const T &var)
17{
18 // must use #if not #ifdef, as we test for the value, because featureset flags
19 // are written out even when 0, think for example #define QSSG_ENABLE_SSM 0
20 if (var.conditionType == QSSGRenderShaderMetadata::Uniform::Regular)
21 block += QString::asprintf("#if %s\n", var.conditionName.constData()).toUtf8();
22 else if (var.conditionType == QSSGRenderShaderMetadata::Uniform::Negated)
23 block += QString::asprintf("#if !%s\n", var.conditionName.constData()).toUtf8();
24}
25
26template<typename T>
27static inline void addEndCond(QByteArray &block, const T &var)
28{
29 if (var.conditionType != QSSGRenderShaderMetadata::Uniform::None)
30 block += QByteArrayLiteral("#endif\n");
31}
32
34{
35 // never null; so safe to call strlen on.
36 const char *m_vertexShader{ "" };
37 const char *m_fragmentShader{ "" };
38
40 QSSGShaderGeneratedProgramOutput(const char *vs, const char *fs)
42 {
43 }
44};
45
46QSSGStageGeneratorBase::QSSGStageGeneratorBase(QSSGShaderGeneratorStage inStage)
47
48 : m_stage(inStage)
49{
50}
51
52void QSSGStageGeneratorBase::begin(QSSGShaderGeneratorStageFlags inEnabledStages)
53{
54 m_incoming.clear();
55 m_outgoing = nullptr;
56 m_flatIncoming.clear();
57 m_flatOutgoing = nullptr;
58 m_includes.clear();
59 m_uniforms.clear();
60 m_uniformArrays.clear();
61 m_constantBuffers.clear();
62 m_constantBufferParams.clear();
63 m_codeBuilder.clear();
64 m_finalBuilder.clear();
65 m_enabledStages = inEnabledStages;
66 m_addedFunctions.clear();
67 m_addedDefinitions.clear();
68 m_addedTypeDeclarations.clear();
69 // the shared buffers will be cleared elsewhere.
70}
71
72void QSSGStageGeneratorBase::addIncoming(const QByteArray &name, const QByteArray &type)
73{
74 m_incoming.insert(name, type);
75 m_flatIncoming.remove(name);
76}
77
78void QSSGStageGeneratorBase::addOutgoing(const QByteArray &name, const QByteArray &type)
79{
80 if (m_outgoing == nullptr) {
81 Q_ASSERT(false);
82 return;
83 }
84 m_outgoing->insert(name, type);
85 if (m_flatOutgoing)
86 m_flatOutgoing->remove(name);
87}
88
89void QSSGStageGeneratorBase::addFlatIncoming(const QByteArray &name, const QByteArray &type)
90{
91 m_flatIncoming.insert(name, type);
92 m_incoming.remove(name);
93}
94
95void QSSGStageGeneratorBase::addFlatOutgoing(const QByteArray &name, const QByteArray &type)
96{
97 if (m_flatOutgoing == nullptr) {
98 Q_ASSERT(false);
99 return;
100 }
101 m_flatOutgoing->insert(name, type);
102 if (m_outgoing)
103 m_outgoing->remove(name);
104}
105
106void QSSGStageGeneratorBase::addUniform(const QByteArray &name, const QByteArray &type)
107{
108 m_uniforms.insert(name, type);
109}
110
111void QSSGStageGeneratorBase::addUniformArray(const QByteArray &name, const QByteArray &type, quint32 size)
112{
113 m_uniformArrays.insert(name, qMakePair(size, type));
114}
115
116void QSSGStageGeneratorBase::addConstantBuffer(const QByteArray &name, const QByteArray &layout)
117{
118 m_constantBuffers.insert(name, layout);
119}
120
121void QSSGStageGeneratorBase::addConstantBufferParam(const QByteArray &cbName, const QByteArray &paramName, const QByteArray &type)
122{
123 TParamPair theParamPair(paramName, type);
124 TConstantBufferParamPair theBufferParamPair(cbName, theParamPair);
125 m_constantBufferParams.push_back(theBufferParamPair);
126}
127
128QSSGStageGeneratorBase &QSSGStageGeneratorBase::operator<<(const QByteArray &data)
129{
130 m_codeBuilder.append(data);
131 return *this;
132}
133
134void QSSGStageGeneratorBase::append(const QByteArray &data)
135{
136 m_codeBuilder.append(data);
137 m_codeBuilder.append("\n");
138}
139
140QSSGShaderGeneratorStage QSSGStageGeneratorBase::stage() const { return m_stage; }
141
142void QSSGStageGeneratorBase::addShaderPass2Marker(QSSGStageGeneratorBase::ShaderItemType itemType)
143{
144 Q_ASSERT(m_mergeContext);
145 m_finalBuilder.append(QByteArrayLiteral("//@@") + QByteArray::number(int(itemType)) + QByteArrayLiteral("\n"));
146}
147
148void QSSGStageGeneratorBase::addShaderItemMap(QSSGStageGeneratorBase::ShaderItemType itemType, const TStrTableStrMap &itemMap, ShaderItemMapFlags flags)
149{
150 m_finalBuilder.append("\n");
151
152 Q_ASSERT(m_mergeContext);
153 for (TStrTableStrMap::const_iterator iter = itemMap.begin(), end = itemMap.end(); iter != end; ++iter) {
154 const QByteArray name = iter.key();
155 switch (itemType) {
156 case ShaderItemType::VertexInput:
157 m_mergeContext->registerInput(QSSGShaderGeneratorStage::Vertex, iter.value(), name);
158 break;
159 case ShaderItemType::Input:
160 m_mergeContext->registerInput(m_stage, iter.value(), name, flags.testFlag(ShaderItemMapFlag::Flat));
161 break;
162 case ShaderItemType::Output:
163 m_mergeContext->registerOutput(m_stage, iter.value(), name, flags.testFlag(ShaderItemMapFlag::Flat));
164 break;
165 case ShaderItemType::Uniform:
166 if (iter.value().startsWith(QByteArrayLiteral("sampler")))
167 m_mergeContext->registerSampler(iter.value(), name);
168 else
169 m_mergeContext->registerUniformMember(iter.value(), name);
170 break;
171 default:
172 qWarning("Unknown shader item %d", int(itemType));
173 Q_UNREACHABLE();
174 }
175 }
176}
177
178void QSSGStageGeneratorBase::addShaderIncomingMap()
179{
180 addShaderItemMap(ShaderItemType::VertexInput, m_incoming);
181 addShaderPass2Marker(ShaderItemType::VertexInput);
182}
183
184void QSSGStageGeneratorBase::addShaderUniformMap()
185{
186 addShaderItemMap(ShaderItemType::Uniform, m_uniforms);
187 for (TStrTableSizedStrMap::const_iterator iter = m_uniformArrays.begin(), end = m_uniformArrays.end(); iter != end; ++iter) {
188 const QByteArray name = iter.key() +
189 "[" + QByteArray::number(iter.value().first) + "]";
190 if (iter.value().second.startsWith(QByteArrayLiteral("sampler")))
191 m_mergeContext->registerSampler(iter.value().second, name);
192 else
193 m_mergeContext->registerUniformMember(iter.value().second, name);
194 }
195 addShaderPass2Marker(ShaderItemType::Uniform);
196}
197
198void QSSGStageGeneratorBase::addShaderOutgoingMap()
199{
200 if (m_outgoing)
201 addShaderItemMap(ShaderItemType::Output, *m_outgoing);
202 if (m_flatOutgoing)
203 addShaderItemMap(ShaderItemType::Output, *m_flatOutgoing, ShaderItemMapFlag::Flat);
204
205 addShaderPass2Marker(ShaderItemType::Output);
206}
207
208void QSSGStageGeneratorBase::addShaderConstantBufferItemMap(const QByteArray &itemType, const TStrTableStrMap &cbMap, TConstantBufferParamArray cbParamsArray)
209{
210 m_finalBuilder.append("\n");
211
212 // iterate over all constant buffers
213 for (TStrTableStrMap::const_iterator iter = cbMap.begin(), end = cbMap.end(); iter != end; ++iter) {
214 m_finalBuilder.append(iter.value());
215 m_finalBuilder.append(" ");
216 m_finalBuilder.append(itemType);
217 m_finalBuilder.append(" ");
218 m_finalBuilder.append(iter.key());
219 m_finalBuilder.append(" {\n");
220 // iterate over all param entries and add match
221 for (TConstantBufferParamArray::const_iterator iter1 = cbParamsArray.begin(), end = cbParamsArray.end(); iter1 != end;
222 ++iter1) {
223 if (iter1->first == iter.key()) {
224 m_finalBuilder.append(iter1->second.second);
225 m_finalBuilder.append(" ");
226 m_finalBuilder.append(iter1->second.first);
227 m_finalBuilder.append(";\n");
228 }
229 }
230
231 m_finalBuilder.append("};\n");
232 }
233}
234
235void QSSGStageGeneratorBase::appendShaderCode() { m_finalBuilder.append(m_codeBuilder); }
236
237void QSSGStageGeneratorBase::addInclude(const QByteArray &name) { m_includes.insert(name); }
238
239void QSSGStageGeneratorBase::buildShaderSourcePass1(QSSGShaderResourceMergeContext *mergeContext)
240{
241 m_mergeContext = mergeContext;
242 addShaderIncomingMap();
243 addShaderUniformMap();
244 addShaderConstantBufferItemMap("uniform", m_constantBuffers, m_constantBufferParams);
245 addShaderOutgoingMap();
246 m_mergeContext = nullptr;
247
248 for (auto iter = m_addedDefinitions.begin(), end = m_addedDefinitions.end();
249 iter != end; ++iter) {
250 m_finalBuilder.append("#ifndef ");
251 m_finalBuilder.append(iter.key());
252 m_finalBuilder.append("\n");
253 m_finalBuilder.append("#define ");
254 m_finalBuilder.append(iter.key());
255 if (!iter.value().isEmpty())
256 m_finalBuilder.append(QByteArrayLiteral(" ") + iter.value());
257 m_finalBuilder.append("\n#endif\n");
258 }
259
260 for (const auto& value : std::as_const(m_addedTypeDeclarations)) {
261 m_finalBuilder.append(value);
262 m_finalBuilder.append("\n");
263 }
264
265 // Sort for deterministic shader text when printing/debugging
266 QList<QByteArray> sortedIncludes(m_includes.begin(), m_includes.end());
267 std::sort(sortedIncludes.begin(), sortedIncludes.end());
268
269 for (const auto &include : sortedIncludes) {
270 m_finalBuilder.append("#include \"");
271 m_finalBuilder.append(include);
272 m_finalBuilder.append("\"\n");
273 }
274
275 appendShaderCode();
276}
277
278QByteArray QSSGStageGeneratorBase::buildShaderSourcePass2(QSSGShaderResourceMergeContext *mergeContext)
279{
280 static const char *prefix = "//@@";
281 const int prefixLen = 4;
282 const int typeLen = 1;
283 int from = 0;
284 for (; ;) {
285 int pos = m_finalBuilder.indexOf(prefix, from);
286 if (pos >= 0) {
287 from = pos;
288 ShaderItemType itemType = ShaderItemType(m_finalBuilder.mid(pos + prefixLen, typeLen).toInt());
289 switch (itemType) {
290 case ShaderItemType::VertexInput:
291 if (m_stage == QSSGShaderGeneratorStage::Vertex) {
292 QByteArray block;
293 for (const QSSGShaderResourceMergeContext::InOutVar &var : mergeContext->m_inOutVars) {
294 if (var.stagesInputIn.testFlag(m_stage))
295 block += QString::asprintf("layout(location = %d) in %s %s;\n", var.location, var.type.constData(), var.name.constData()).toUtf8();
296 }
297 m_finalBuilder.replace(pos, prefixLen + typeLen, block);
298 }
299 break;
300 case ShaderItemType::Input:
301 {
302 QByteArray block;
303 for (const QSSGShaderResourceMergeContext::InOutVar &var : mergeContext->m_inOutVars) {
304 if (var.stagesInputIn.testFlag(m_stage))
305 block += QString::asprintf("layout(location = %d) in %s%s %s;\n", var.location, var.flat ? "flat " : "", var.type.constData(), var.name.constData()).toUtf8();
306 }
307 m_finalBuilder.replace(pos, prefixLen + typeLen, block);
308 }
309 break;
310 case ShaderItemType::Output:
311 {
312 QByteArray block;
313 for (const QSSGShaderResourceMergeContext::InOutVar &var : mergeContext->m_inOutVars) {
314 if (var.stageOutputFrom.testFlag(m_stage))
315 block += QString::asprintf("layout(location = %d) out %s%s %s;\n", var.location, var.flat ? "flat " : "", var.type.constData(), var.name.constData()).toUtf8();
316 }
317 m_finalBuilder.replace(pos, prefixLen + typeLen, block);
318 }
319 break;
320 case ShaderItemType::Uniform:
321 {
322 QByteArray block;
323
324 for (const auto &sampler : std::as_const(mergeContext->m_samplers)) {
325 addStartCond(block, sampler);
326 block += QString::asprintf("layout(binding = %d) uniform %s %s;\n",
327 sampler.binding,
328 sampler.type.constData(),
329 sampler.name.constData()).toUtf8();
330 addEndCond(block, sampler);
331 }
332
333 if (!mergeContext->m_uniformMembers.isEmpty()) {
334 // The layout (offsets of the members) of the main
335 // uniform block cannot be different in the stages.
336 // (f.ex., a given member must be assumed to be at same
337 // offset both in the vertex and the fragment shader)
338 // Therefore we output everything in all stages.
339 block += QByteArrayLiteral("layout(std140, binding = 0) uniform cbMain {\n");
340 for (auto iter = mergeContext->m_uniformMembers.cbegin(), end = mergeContext->m_uniformMembers.cend();
341 iter != end; ++iter)
342 {
343 addStartCond(block, iter.value());
344 block += QString::asprintf(" %s %s;\n", iter.value().type.constData(), iter.value().name.constData()).toUtf8();
345 addEndCond(block, iter.value());
346 }
347 // No instance name for this uniform block. This is
348 // essential since custom material shader code will not use
349 // any instance name prefix when accessing the members. So
350 // while the internal stuff for default/principled material
351 // could be fixed up with prefixing everything, custom
352 // materials cannot. So leave it out.
353 block += QByteArrayLiteral("};\n");
354 }
355 m_finalBuilder.replace(pos, prefixLen + typeLen, block);
356 }
357 break;
358 default:
359 Q_UNREACHABLE_RETURN(m_finalBuilder);
360 }
361 } else {
362 break;
363 }
364 }
365
366 return m_finalBuilder;
367}
368
369void QSSGStageGeneratorBase::addFunction(const QByteArray &functionName)
370{
371 if (!m_addedFunctions.contains(functionName)) {
372 m_addedFunctions.push_back(functionName);
373 QByteArray includeName;
374 includeName = "func" + functionName + ".glsllib";
375 addInclude(includeName);
376 }
377}
378
379void QSSGStageGeneratorBase::addDefinition(const QByteArray &name, const QByteArray &value)
380{
381 m_addedDefinitions.insert(name, value);
382}
383
384void QSSGStageGeneratorBase::addTypeDeclaration(const QByteArray &typeName, const QByteArray &snippet)
385{
386 if (snippet.isEmpty())
387 return;
388 m_addedTypeDeclarations.insert(typeName, snippet);
389}
390
391void QSSGProgramGenerator::linkStages()
392{
393 // Link stages incoming to outgoing variables.
394 QSSGStageGeneratorBase *previous = nullptr;
395 quint32 theStageId = 1;
396 for (quint32 idx = 0, end = quint32(QSSGShaderGeneratorStage::StageCount); idx < end; ++idx, theStageId = theStageId << 1) {
397 QSSGStageGeneratorBase *thisStage = nullptr;
398 QSSGShaderGeneratorStage theStageEnum = static_cast<QSSGShaderGeneratorStage>(theStageId);
399 if ((m_enabledStages & theStageEnum)) {
400 thisStage = &internalGetStage(theStageEnum);
401 if (previous) {
402 previous->m_outgoing = &thisStage->m_incoming;
403 previous->m_flatOutgoing = &thisStage->m_flatIncoming;
404 }
405 previous = thisStage;
406 }
407 }
408}
409
410void QSSGProgramGenerator::beginProgram(QSSGShaderGeneratorStageFlags inEnabledStages)
411{
412 m_vs.begin(inEnabledStages);
413 m_fs.begin(inEnabledStages);
414 m_enabledStages = inEnabledStages;
415 linkStages();
416}
417
418QSSGShaderGeneratorStageFlags QSSGProgramGenerator::getEnabledStages() const { return m_enabledStages; }
419
420QSSGStageGeneratorBase &QSSGProgramGenerator::internalGetStage(QSSGShaderGeneratorStage inStage)
421{
422 switch (inStage) {
423 case QSSGShaderGeneratorStage::Vertex:
424 return m_vs;
425 case QSSGShaderGeneratorStage::Fragment:
426 return m_fs;
427 default:
428 Q_ASSERT(false);
429 break;
430 }
431 return m_vs;
432}
433
434QSSGStageGeneratorBase *QSSGProgramGenerator::getStage(QSSGShaderGeneratorStage inStage)
435{
436 if ((m_enabledStages & inStage))
437 return &internalGetStage(inStage);
438 return nullptr;
439}
440
441void QSSGProgramGenerator::registerShaderMetaDataFromSource(QSSGShaderResourceMergeContext *mergeContext, const QByteArray &contents, QSSGShaderGeneratorStage stage)
442{
443 QSSGRenderShaderMetadata::ShaderMetaData meta = QSSGRenderShaderMetadata::getShaderMetaData(contents);
444
445 for (const QSSGRenderShaderMetadata::Uniform &u : std::as_const(meta.uniforms)) {
446 if (u.type.startsWith(QByteArrayLiteral("sampler"))) {
447 if (u.multiview && mergeContext->viewCount >= 2) {
448 // 'sampler2D qt_screenTexture' becomes 'sampler2DArray qt_screenTextureArray'
449 mergeContext->registerSampler(u.type + QByteArrayLiteral("Array"), u.name + QByteArrayLiteral("Array"), u.condition, u.conditionName);
450 } else {
451 mergeContext->registerSampler(u.type, u.name, u.condition, u.conditionName);
452 }
453 } else {
454 if (u.multiview && mergeContext->viewCount >= 2) {
455 const QByteArray name = u.name + "[" + QByteArray::number(mergeContext->viewCount) + "]";
456 mergeContext->registerUniformMember(u.type, name, u.condition, u.conditionName);
457 } else {
458 mergeContext->registerUniformMember(u.type, u.name, u.condition, u.conditionName);
459 }
460 }
461 }
462
463 for (const QSSGRenderShaderMetadata::InputOutput &inputVar : std::as_const(meta.inputs)) {
464 if (inputVar.stage == stage)
465 mergeContext->registerInput(stage, inputVar.type, inputVar.name, inputVar.flat);
466 }
467
468 for (const QSSGRenderShaderMetadata::InputOutput &outputVar : std::as_const(meta.outputs)) {
469 if (outputVar.stage == stage)
470 mergeContext->registerOutput(stage, outputVar.type, outputVar.name, outputVar.flat);
471 }
472
473 for (auto it = mergeContext->m_inOutVars.cbegin(), end = mergeContext->m_inOutVars.cend(); it != end; ++it) {
474 if (it->stagesInputIn == int(QSSGShaderGeneratorStage::Fragment) && it->stageOutputFrom == 0)
475 qWarning("Fragment stage input %s is not output from vertex stage; expect errors.", it.key().constData());
476 }
477}
478
479QSSGRhiShaderPipelinePtr QSSGProgramGenerator::compileGeneratedRhiShader(const QByteArray &inMaterialInfoString,
480 const QSSGShaderFeatures &inFeatureSet,
481 QSSGShaderLibraryManager &shaderLibraryManager,
482 QSSGShaderCache &theCache,
483 QSSGRhiShaderPipeline::StageFlags stageFlags,
484 int viewCount,
485 bool perTargetCompilation)
486{
487 // No stages enabled
488 if (((quint32)m_enabledStages) == 0) {
489 Q_ASSERT(false);
490 return nullptr;
491 }
492
493 QSSGShaderResourceMergeContext mergeContext;
494 mergeContext.viewCount = viewCount;
495
496 for (quint32 stageIdx = 0; stageIdx < static_cast<quint32>(QSSGShaderGeneratorStage::StageCount); ++stageIdx) {
497 QSSGShaderGeneratorStage stageName = static_cast<QSSGShaderGeneratorStage>(1 << stageIdx);
498 if (m_enabledStages & stageName) {
499 QSSGStageGeneratorBase &theStage(internalGetStage(stageName));
500 theStage.buildShaderSourcePass1(&mergeContext);
501 }
502 }
503
504 for (quint32 stageIdx = 0; stageIdx < static_cast<quint32>(QSSGShaderGeneratorStage::StageCount); ++stageIdx) {
505 QSSGShaderGeneratorStage stageName = static_cast<QSSGShaderGeneratorStage>(1 << stageIdx);
506 if (m_enabledStages & stageName) {
507 QSSGStageGeneratorBase &theStage(internalGetStage(stageName));
508 shaderLibraryManager.resolveIncludeFiles(theStage.m_finalBuilder, inMaterialInfoString);
509 registerShaderMetaDataFromSource(&mergeContext, theStage.m_finalBuilder, stageName);
510 }
511 }
512
513 for (quint32 stageIdx = 0; stageIdx < static_cast<quint32>(QSSGShaderGeneratorStage::StageCount); ++stageIdx) {
514 QSSGShaderGeneratorStage stageName = static_cast<QSSGShaderGeneratorStage>(1 << stageIdx);
515 if (m_enabledStages & stageName) {
516 QSSGStageGeneratorBase &theStage(internalGetStage(stageName));
517 theStage.buildShaderSourcePass2(&mergeContext);
518 }
519 }
520
521 // qDebug("VERTEX:\n%s\n\n", m_vs.m_finalBuilder.constData());
522 // qDebug("FRAGMENT:\n%s\n\n", m_fs.m_finalBuilder.constData());
523
524 return theCache.compileForRhi(inMaterialInfoString,
525 m_vs.m_finalBuilder,
526 m_fs.m_finalBuilder,
527 inFeatureSet,
528 stageFlags,
529 viewCount,
530 perTargetCompilation);
531}
532
533QSSGVertexShaderGenerator::QSSGVertexShaderGenerator()
534 : QSSGStageGeneratorBase(QSSGShaderGeneratorStage::Vertex)
535{}
536
537QSSGFragmentShaderGenerator::QSSGFragmentShaderGenerator()
538 : QSSGStageGeneratorBase(QSSGShaderGeneratorStage::Fragment)
539{}
540
541void QSSGFragmentShaderGenerator::addShaderIncomingMap()
542{
543 addShaderItemMap(ShaderItemType::Input, m_incoming);
544 addShaderItemMap(ShaderItemType::Input, m_flatIncoming, ShaderItemMapFlag::Flat);
545 addShaderPass2Marker(ShaderItemType::Input);
546}
547
548void QSSGFragmentShaderGenerator::addShaderOutgoingMap()
549{
550 addShaderPass2Marker(ShaderItemType::Output);
551}
552
553QT_END_NAMESPACE
static void addEndCond(QByteArray &block, const T &var)
QSSGShaderGeneratedProgramOutput(const char *vs, const char *fs)