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

source code of qtquick3d/src/runtimerender/qssgrendershadercodegenerator.cpp