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