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
qssgshadermaterialadapter.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
5/* clang-format off */
6
7#include <QtQuick3DRuntimeRender/private/qssgshadermaterialadapter_p.h>
9#include <QtQuick3DRuntimeRender/private/qssgrhicustommaterialsystem_p.h>
10
12
13QSSGShaderMaterialAdapter::~QSSGShaderMaterialAdapter() = default;
14
15QSSGShaderMaterialAdapter *QSSGShaderMaterialAdapter::create(const QSSGRenderGraphObject &materialNode)
16{
17 switch (materialNode.type) {
18 case QSSGRenderGraphObject::Type::DefaultMaterial:
19 case QSSGRenderGraphObject::Type::PrincipledMaterial:
20 case QSSGRenderGraphObject::Type::SpecularGlossyMaterial:
21 return new QSSGShaderDefaultMaterialAdapter(static_cast<const QSSGRenderDefaultMaterial &>(materialNode));
22
23 case QSSGRenderGraphObject::Type::CustomMaterial:
24 return new QSSGShaderCustomMaterialAdapter(static_cast<const QSSGRenderCustomMaterial &>(materialNode));
25
26 default:
27 break;
28 }
29
30 return nullptr;
31}
32
33bool QSSGShaderMaterialAdapter::isUnshaded()
34{
35 return false;
36}
37
38bool QSSGShaderMaterialAdapter::hasCustomShaderSnippet(QSSGShaderCache::ShaderType)
39{
40 return false;
41}
42
43QByteArray QSSGShaderMaterialAdapter::customShaderSnippet(QSSGShaderCache::ShaderType,
44 QSSGShaderLibraryManager &,
45 bool)
46{
47 return QByteArray();
48}
49
50bool QSSGShaderMaterialAdapter::hasCustomShaderFunction(QSSGShaderCache::ShaderType,
51 const QByteArray &,
52 QSSGShaderLibraryManager &)
53{
54 return false;
55}
56
57void QSSGShaderMaterialAdapter::setCustomPropertyUniforms(char *,
58 QSSGRhiShaderPipeline &,
59 const QSSGRenderContextInterface &)
60{
61}
62
63bool QSSGShaderMaterialAdapter::usesSharedVariables()
64{
65 return false;
66}
67
68
69
70QSSGShaderDefaultMaterialAdapter::QSSGShaderDefaultMaterialAdapter(const QSSGRenderDefaultMaterial &material)
71 : m_material(material)
72{
73}
74
75bool QSSGShaderDefaultMaterialAdapter::isPrincipled()
76{
77 return m_material.type == QSSGRenderGraphObject::Type::PrincipledMaterial;
78}
79
80bool QSSGShaderDefaultMaterialAdapter::isSpecularGlossy()
81{
82 return m_material.type == QSSGRenderGraphObject::Type::SpecularGlossyMaterial;
83}
84
85bool QSSGShaderDefaultMaterialAdapter::isMetalnessEnabled()
86{
87 return m_material.isMetalnessEnabled();
88}
89
90bool QSSGShaderDefaultMaterialAdapter::isSpecularEnabled()
91{
92 return m_material.isSpecularEnabled();
93}
94
95bool QSSGShaderDefaultMaterialAdapter::isVertexColorsEnabled()
96{
97 return m_material.isVertexColorsEnabled();
98}
99
100bool QSSGShaderDefaultMaterialAdapter::isVertexColorsMaskEnabled()
101{
102 return m_material.isVertexColorsMaskEnabled();
103}
104
105bool QSSGShaderDefaultMaterialAdapter::isInvertOpacityMapValue()
106{
107 return m_material.isInvertOpacityMapValue();
108}
109
110bool QSSGShaderDefaultMaterialAdapter::isBaseColorSingleChannelEnabled()
111{
112 return m_material.isBaseColorSingleChannelEnabled();
113}
114
115bool QSSGShaderDefaultMaterialAdapter::isSpecularAmountSingleChannelEnabled()
116{
117 return m_material.isSpecularAmountSingleChannelEnabled();
118}
119
120bool QSSGShaderDefaultMaterialAdapter::isEmissiveSingleChannelEnabled()
121{
122 return m_material.isEmissiveSingleChannelEnabled();
123}
124
125bool QSSGShaderDefaultMaterialAdapter::isClearcoatEnabled()
126{
127 return m_material.isClearcoatEnabled();
128}
129
130bool QSSGShaderDefaultMaterialAdapter::isTransmissionEnabled()
131{
132 return m_material.isTransmissionEnabled();
133}
134
135bool QSSGShaderDefaultMaterialAdapter::hasLighting()
136{
137 return m_material.hasLighting();
138}
139
140bool QSSGShaderDefaultMaterialAdapter::usesCustomSkinning()
141{
142 return false;
143}
144
145bool QSSGShaderDefaultMaterialAdapter::usesCustomMorphing()
146{
147 return false;
148}
149
150QSSGRenderDefaultMaterial::MaterialSpecularModel QSSGShaderDefaultMaterialAdapter::specularModel()
151{
152 return m_material.specularModel;
153}
154
155QSSGRenderDefaultMaterial::MaterialAlphaMode QSSGShaderDefaultMaterialAdapter::alphaMode()
156{
157 return m_material.alphaMode;
158}
159
160QSSGRenderDefaultMaterial::VertexColorMaskFlags QSSGShaderDefaultMaterialAdapter::vertexColorRedMask()
161{
162 return m_material.vertexColorRedMask;
163}
164
165QSSGRenderDefaultMaterial::VertexColorMaskFlags QSSGShaderDefaultMaterialAdapter::vertexColorGreenMask()
166{
167 return m_material.vertexColorGreenMask;
168}
169
170QSSGRenderDefaultMaterial::VertexColorMaskFlags QSSGShaderDefaultMaterialAdapter::vertexColorBlueMask()
171{
172 return m_material.vertexColorBlueMask;
173}
174
175QSSGRenderDefaultMaterial::VertexColorMaskFlags QSSGShaderDefaultMaterialAdapter::vertexColorAlphaMask()
176{
177 return m_material.vertexColorAlphaMask;
178}
179
180QSSGRenderImage *QSSGShaderDefaultMaterialAdapter::iblProbe()
181{
182 return m_material.iblProbe;
183}
184
185QVector3D QSSGShaderDefaultMaterialAdapter::emissiveColor()
186{
187 return m_material.emissiveColor;
188}
189
190QVector4D QSSGShaderDefaultMaterialAdapter::color()
191{
192 return m_material.color;
193}
194
195QVector3D QSSGShaderDefaultMaterialAdapter::specularTint()
196{
197 return m_material.specularTint;
198}
199
200float QSSGShaderDefaultMaterialAdapter::ior()
201{
202 return m_material.ior;
203}
204
205bool QSSGShaderDefaultMaterialAdapter::isFresnelScaleBiasEnabled()
206{
207 return m_material.fresnelScaleBiasEnabled;
208}
209
210float QSSGShaderDefaultMaterialAdapter::fresnelScale()
211{
212 return m_material.fresnelScale;
213}
214
215float QSSGShaderDefaultMaterialAdapter::fresnelBias()
216{
217 return m_material.fresnelBias;
218}
219
220float QSSGShaderDefaultMaterialAdapter::fresnelPower()
221{
222 return m_material.fresnelPower;
223}
224
225bool QSSGShaderDefaultMaterialAdapter::isClearcoatFresnelScaleBiasEnabled()
226{
227 return m_material.clearcoatFresnelScaleBiasEnabled;
228}
229
230float QSSGShaderDefaultMaterialAdapter::clearcoatFresnelScale()
231{
232 return m_material.clearcoatFresnelScale;
233}
234
235float QSSGShaderDefaultMaterialAdapter::clearcoatFresnelBias()
236{
237 return m_material.clearcoatFresnelBias;
238}
239
240float QSSGShaderDefaultMaterialAdapter::clearcoatFresnelPower()
241{
242 return m_material.clearcoatFresnelPower;
243}
244
245float QSSGShaderDefaultMaterialAdapter::metalnessAmount()
246{
247 return m_material.metalnessAmount;
248}
249
250float QSSGShaderDefaultMaterialAdapter::specularAmount()
251{
252 return m_material.specularAmount;
253}
254
255float QSSGShaderDefaultMaterialAdapter::specularRoughness()
256{
257 return m_material.specularRoughness;
258}
259
260float QSSGShaderDefaultMaterialAdapter::bumpAmount()
261{
262 return m_material.bumpAmount;
263}
264
265float QSSGShaderDefaultMaterialAdapter::translucentFallOff()
266{
267 return m_material.translucentFalloff;
268}
269
270float QSSGShaderDefaultMaterialAdapter::diffuseLightWrap()
271{
272 return m_material.diffuseLightWrap;
273}
274
275float QSSGShaderDefaultMaterialAdapter::occlusionAmount()
276{
277 return m_material.occlusionAmount;
278}
279
280float QSSGShaderDefaultMaterialAdapter::alphaCutOff()
281{
282 return m_material.alphaCutoff;
283}
284
285float QSSGShaderDefaultMaterialAdapter::pointSize()
286{
287 return m_material.pointSize;
288}
289
290float QSSGShaderDefaultMaterialAdapter::lineWidth()
291{
292 return m_material.lineWidth;
293}
294
295float QSSGShaderDefaultMaterialAdapter::heightAmount()
296{
297 return m_material.heightAmount;
298}
299
300float QSSGShaderDefaultMaterialAdapter::minHeightSamples()
301{
302 return m_material.minHeightSamples;
303}
304
305float QSSGShaderDefaultMaterialAdapter::maxHeightSamples()
306{
307 return m_material.maxHeightSamples;
308}
309
310float QSSGShaderDefaultMaterialAdapter::clearcoatAmount()
311{
312 return m_material.clearcoatAmount;
313}
314
315float QSSGShaderDefaultMaterialAdapter::clearcoatRoughnessAmount()
316{
317 return m_material.clearcoatRoughnessAmount;
318}
319
320float QSSGShaderDefaultMaterialAdapter::clearcoatNormalStrength()
321{
322 return m_material.clearcoatNormalStrength;
323}
324
325float QSSGShaderDefaultMaterialAdapter::transmissionFactor()
326{
327 return m_material.transmissionFactor;
328}
329
330float QSSGShaderDefaultMaterialAdapter::thicknessFactor()
331{
332 return m_material.thicknessFactor;
333}
334
335float QSSGShaderDefaultMaterialAdapter::attenuationDistance()
336{
337 return m_material.attenuationDistance;
338}
339
340QVector3D QSSGShaderDefaultMaterialAdapter::attenuationColor()
341{
342 return m_material.attenuationColor;
343}
344
345QSSGShaderCustomMaterialAdapter::QSSGShaderCustomMaterialAdapter(const QSSGRenderCustomMaterial &material)
346 : m_material(material)
347{
348}
349
350// Act like Principled. Lighting is always on, specular, metalness, etc. support should all be enabled.
351// Unlike Principled, the *enabled values do not depend on the metalness or specularAmount values
352// (we cannot tell what those are if they are written in the shader).
353
354bool QSSGShaderCustomMaterialAdapter::isPrincipled()
355{
356 return true;
357}
358
359bool QSSGShaderCustomMaterialAdapter::isSpecularGlossy()
360{
361 return false;
362}
363
364bool QSSGShaderCustomMaterialAdapter::isMetalnessEnabled()
365{
366 return true;
367}
368
369bool QSSGShaderCustomMaterialAdapter::isSpecularEnabled()
370{
371 return true;
372}
373
374bool QSSGShaderCustomMaterialAdapter::isVertexColorsEnabled()
375{
376 return m_material.m_renderFlags.testFlag(QSSGRenderCustomMaterial::RenderFlag::VarColor);
377}
378
379bool QSSGShaderCustomMaterialAdapter::isVertexColorsMaskEnabled()
380{
381 return false;
382}
383
384bool QSSGShaderCustomMaterialAdapter::isInvertOpacityMapValue()
385{
386 return false;
387}
388
389bool QSSGShaderCustomMaterialAdapter::isBaseColorSingleChannelEnabled()
390{
391 return false;
392}
393
394bool QSSGShaderCustomMaterialAdapter::isSpecularAmountSingleChannelEnabled()
395{
396 return false;
397}
398
399bool QSSGShaderCustomMaterialAdapter::isEmissiveSingleChannelEnabled()
400{
401 return false;
402}
403
404bool QSSGShaderCustomMaterialAdapter::isClearcoatEnabled()
405{
406 return m_material.m_renderFlags.testFlag(QSSGRenderCustomMaterial::RenderFlag::Clearcoat);
407}
408
409bool QSSGShaderCustomMaterialAdapter::isTransmissionEnabled()
410{
411 return m_material.m_renderFlags.testFlag(QSSGRenderCustomMaterial::RenderFlag::Transmission);
412}
413
414bool QSSGShaderCustomMaterialAdapter::hasLighting()
415{
416 return true;
417}
418
419bool QSSGShaderCustomMaterialAdapter::usesCustomSkinning()
420{
421 return m_material.m_renderFlags.testFlag(QSSGRenderCustomMaterial::RenderFlag::Skinning);
422}
423
424bool QSSGShaderCustomMaterialAdapter::usesCustomMorphing()
425{
426 return m_material.m_renderFlags.testFlag(QSSGRenderCustomMaterial::RenderFlag::Morphing);
427}
428
429QSSGRenderDefaultMaterial::MaterialSpecularModel QSSGShaderCustomMaterialAdapter::specularModel()
430{
431 return QSSGRenderDefaultMaterial::MaterialSpecularModel::SchlickGGX;
432}
433
434QSSGRenderDefaultMaterial::MaterialAlphaMode QSSGShaderCustomMaterialAdapter::alphaMode()
435{
436 return QSSGRenderDefaultMaterial::MaterialAlphaMode::Default;
437}
438
439QSSGRenderDefaultMaterial::VertexColorMaskFlags QSSGShaderCustomMaterialAdapter::vertexColorRedMask()
440{
441 return QSSGRenderDefaultMaterial::NoMask;
442}
443
444QSSGRenderDefaultMaterial::VertexColorMaskFlags QSSGShaderCustomMaterialAdapter::vertexColorGreenMask()
445{
446 return QSSGRenderDefaultMaterial::NoMask;
447}
448
449QSSGRenderDefaultMaterial::VertexColorMaskFlags QSSGShaderCustomMaterialAdapter::vertexColorBlueMask()
450{
451 return QSSGRenderDefaultMaterial::NoMask;
452}
453
454QSSGRenderDefaultMaterial::VertexColorMaskFlags QSSGShaderCustomMaterialAdapter::vertexColorAlphaMask()
455{
456 return QSSGRenderDefaultMaterial::NoMask;
457}
458
459QSSGRenderImage *QSSGShaderCustomMaterialAdapter::iblProbe()
460{
461 return m_material.m_iblProbe;
462}
463
464// The following are the values that get set into uniforms such as
465// qt_material_properties etc. When a custom shader is present, these values
466// are not used at all. However, a CustomMaterial is also valid without a
467// vertex/fragment shader, or with no custom shaders at all. Therefore the
468// values here must match the defaults of PrincipledMaterial, in order to make
469// PrincipledMaterial { } and CustomMaterial { } identical.
470
471QVector3D QSSGShaderCustomMaterialAdapter::emissiveColor()
472{
473 return QVector3D(0, 0, 0);
474}
475
476QVector4D QSSGShaderCustomMaterialAdapter::color()
477{
478 return QVector4D(1, 1, 1, 1);
479}
480
481QVector3D QSSGShaderCustomMaterialAdapter::specularTint()
482{
483 return QVector3D(1, 1, 1);
484}
485
486float QSSGShaderCustomMaterialAdapter::ior()
487{
488 return 1.45f;
489}
490
491bool QSSGShaderCustomMaterialAdapter::isFresnelScaleBiasEnabled()
492{
493 return m_material.m_renderFlags.testFlag(QSSGRenderCustomMaterial::RenderFlag::FresnelScaleBias);
494}
495
496float QSSGShaderCustomMaterialAdapter::fresnelScale()
497{
498 return 1.0f;
499}
500
501float QSSGShaderCustomMaterialAdapter::fresnelBias()
502{
503 return 0.0f;
504}
505
506float QSSGShaderCustomMaterialAdapter::fresnelPower()
507{
508 return 0.0f;
509}
510
511bool QSSGShaderCustomMaterialAdapter::isClearcoatFresnelScaleBiasEnabled()
512{
513 return m_material.m_renderFlags.testFlag(QSSGRenderCustomMaterial::RenderFlag::ClearcoatFresnelScaleBias);
514}
515
516float QSSGShaderCustomMaterialAdapter::clearcoatFresnelScale()
517{
518 return 1.0f;
519}
520
521float QSSGShaderCustomMaterialAdapter::clearcoatFresnelBias()
522{
523 return 0.0f;
524}
525
526float QSSGShaderCustomMaterialAdapter::clearcoatFresnelPower()
527{
528 return 0.0f;
529}
530
531float QSSGShaderCustomMaterialAdapter::metalnessAmount()
532{
533 return 0.0f;
534}
535
536float QSSGShaderCustomMaterialAdapter::specularAmount()
537{
538 return 0.5f;
539}
540
541float QSSGShaderCustomMaterialAdapter::specularRoughness()
542{
543 return 0.0f;
544}
545
546float QSSGShaderCustomMaterialAdapter::bumpAmount()
547{
548 return 0.0f;
549}
550
551float QSSGShaderCustomMaterialAdapter::translucentFallOff()
552{
553 return 0.0f;
554}
555
556float QSSGShaderCustomMaterialAdapter::diffuseLightWrap()
557{
558 return 0.0f;
559}
560
561float QSSGShaderCustomMaterialAdapter::occlusionAmount()
562{
563 return 1.0f;
564}
565
566float QSSGShaderCustomMaterialAdapter::alphaCutOff()
567{
568 return 0.5f;
569}
570
571float QSSGShaderCustomMaterialAdapter::pointSize()
572{
573 return 1.0f;
574}
575
576float QSSGShaderCustomMaterialAdapter::lineWidth()
577{
578 return m_material.m_lineWidth;
579}
580
581float QSSGShaderCustomMaterialAdapter::heightAmount()
582{
583 return 0.0f;
584}
585
586float QSSGShaderCustomMaterialAdapter::minHeightSamples()
587{
588 return 0.0f;
589}
590
591float QSSGShaderCustomMaterialAdapter::maxHeightSamples()
592{
593 return 0.0f;
594}
595
596float QSSGShaderCustomMaterialAdapter::clearcoatAmount()
597{
598 return 0.0f;
599}
600
601float QSSGShaderCustomMaterialAdapter::clearcoatRoughnessAmount()
602{
603 return 0.0f;
604}
605
606float QSSGShaderCustomMaterialAdapter::clearcoatNormalStrength()
607{
608 return 1.0f;
609}
610
611float QSSGShaderCustomMaterialAdapter::transmissionFactor()
612{
613 return 0.0f;
614}
615
616float QSSGShaderCustomMaterialAdapter::thicknessFactor()
617{
618 return 0.0f;
619}
620
621float QSSGShaderCustomMaterialAdapter::attenuationDistance()
622{
623 return std::numeric_limits<float>::infinity();
624}
625
626QVector3D QSSGShaderCustomMaterialAdapter::attenuationColor()
627{
628 return { 1.0f, 1.0f, 1.0f };
629}
630
631bool QSSGShaderCustomMaterialAdapter::isUnshaded()
632{
633 return m_material.m_shadingMode == QSSGRenderCustomMaterial::ShadingMode::Unshaded;
634}
635
636bool QSSGShaderCustomMaterialAdapter::hasCustomShaderSnippet(QSSGShaderCache::ShaderType type)
637{
638 if (type == QSSGShaderCache::ShaderType::Vertex)
639 return m_material.m_customShaderPresence.testFlag(QSSGRenderCustomMaterial::CustomShaderPresenceFlag::Vertex);
640
641 return m_material.m_customShaderPresence.testFlag(QSSGRenderCustomMaterial::CustomShaderPresenceFlag::Fragment);
642}
643
644QByteArray QSSGShaderCustomMaterialAdapter::customShaderSnippet(QSSGShaderCache::ShaderType type,
645 QSSGShaderLibraryManager &shaderLibraryManager,
646 bool multiViewCompatible)
647{
648 if (hasCustomShaderSnippet(type)) {
649 const QByteArray shaderPathKey = m_material.m_shaderPathKey[multiViewCompatible ? QSSGRenderCustomMaterial::MultiViewShaderPathKeyIndex
650 : QSSGRenderCustomMaterial::RegularShaderPathKeyIndex];
651 return shaderLibraryManager.getShaderSource(shaderPathKey, type);
652 }
653
654 return QByteArray();
655}
656
657bool QSSGShaderCustomMaterialAdapter::hasCustomShaderFunction(QSSGShaderCache::ShaderType shaderType,
658 const QByteArray &funcName,
659 QSSGShaderLibraryManager &shaderLibraryManager)
660{
661 if (hasCustomShaderSnippet(shaderType))
662 return shaderLibraryManager.getShaderMetaData(m_material.m_shaderPathKey[QSSGRenderCustomMaterial::RegularShaderPathKeyIndex],
663 shaderType).customFunctions.contains(funcName);
664
665 return false;
666}
667
668void QSSGShaderCustomMaterialAdapter::setCustomPropertyUniforms(char *ubufData,
669 QSSGRhiShaderPipeline &shaderPipeline,
670 const QSSGRenderContextInterface &context)
671{
672 context.customMaterialSystem()->applyRhiShaderPropertyValues(ubufData, m_material, shaderPipeline);
673}
674
675bool QSSGShaderCustomMaterialAdapter::usesSharedVariables()
676{
677 return m_material.m_usesSharedVariables;
678}
679
680namespace {
681
682// Custom material shader substitution table.
683// Must be in sync with the shader generator.
684static const QSSGCustomMaterialVariableSubstitution qssg_var_subst_tab[] = {
685 // uniform (block members)
686 { "MODELVIEWPROJECTION_MATRIX", "qt_modelViewProjection", true },
687 { "VIEWPROJECTION_MATRIX", "qt_viewProjectionMatrix", true },
688 { "MODEL_MATRIX", "qt_modelMatrix", false },
689 { "VIEW_MATRIX", "qt_viewMatrix", true },
690 { "NORMAL_MATRIX", "qt_normalMatrix", false },
691 { "BONE_TRANSFORMS", "qt_getTexMatrix", false },
692 { "BONE_NORMAL_TRANSFORMS", "qt_getTexMatrix", false },
693 { "PROJECTION_MATRIX", "qt_projectionMatrix", true },
694 { "INVERSE_PROJECTION_MATRIX", "qt_inverseProjectionMatrix", true },
695 { "CAMERA_POSITION", "qt_cameraPosition", true },
696 { "CAMERA_DIRECTION", "qt_cameraDirection", true },
697 { "CAMERA_PROPERTIES", "qt_cameraProperties", false },
698 { "FRAMEBUFFER_Y_UP", "qt_rhi_properties.x", false },
699 { "NDC_Y_UP", "qt_rhi_properties.y", false },
700 { "NEAR_CLIP_VALUE", "qt_rhi_properties.z", false },
701 { "IBL_MAXMIPMAP", "qt_lightProbeProperties.y", false },
702 { "IBL_HORIZON", "qt_lightProbeProperties.z", false },
703 { "IBL_EXPOSE", "qt_lightProbeProperties.w", false },
704
705 // outputs
706 { "POSITION", "gl_Position", false },
707 { "FRAGCOLOR", "fragOutput", false },
708 { "POINT_SIZE", "gl_PointSize", false },
709
710 // fragment inputs
711 { "FRAGCOORD", "gl_FragCoord", false },
712
713 // functions
714 { "DIRECTIONAL_LIGHT", "qt_directionalLightProcessor", false },
715 { "POINT_LIGHT", "qt_pointLightProcessor", false },
716 { "SPOT_LIGHT", "qt_spotLightProcessor", false },
717 { "AMBIENT_LIGHT", "qt_ambientLightProcessor", false },
718 { "SPECULAR_LIGHT", "qt_specularLightProcessor", false },
719 { "MAIN", "qt_customMain", false },
720 { "POST_PROCESS", "qt_customPostProcessor", false },
721 { "IBL_PROBE", "qt_iblProbeProcessor", false },
722
723 // textures
724 { "SCREEN_TEXTURE", "qt_screenTexture", true },
725 { "SCREEN_MIP_TEXTURE", "qt_screenTexture", true }, // same resource as SCREEN_TEXTURE under the hood
726 { "DEPTH_TEXTURE", "qt_depthTexture", true },
727 { "AO_TEXTURE", "qt_aoTexture", true },
728 { "IBL_TEXTURE", "qt_lightProbe", false },
729 { "LIGHTMAP", "qt_lightmap", false },
730 { "NORMAL_ROUGHNESS_TEXTURE", "qt_normalTexture", false },
731
732 // For shaded only: vertex outputs, for convenience and perf. (only those
733 // that are always present when lighting is enabled) The custom vertex main
734 // can also calculate on its own and pass them on with VARYING but that's a
735 // bit wasteful since we calculate these anyways.
736 { "VAR_WORLD_NORMAL", "qt_varNormal", false },
737 { "VAR_WORLD_TANGENT", "qt_varTangent", false },
738 { "VAR_WORLD_BINORMAL", "qt_varBinormal", false },
739 { "VAR_WORLD_POSITION", "qt_varWorldPos", false },
740 // vertex color is always enabled for custom materials (shaded)
741 { "VAR_COLOR", "qt_varColor", false },
742
743 // effects
744 { "INPUT", "qt_inputTexture", true },
745 { "INPUT_UV", "qt_inputUV", false },
746 { "TEXTURE_UV", "qt_textureUV", false },
747 { "INPUT_SIZE", "qt_inputSize", false },
748 { "OUTPUT_SIZE", "qt_outputSize", false },
749 { "FRAME", "qt_frame_num", false },
750
751 // instancing
752 { "INSTANCE_COLOR", "qt_instanceColor", false },
753 { "INSTANCE_DATA", "qt_instanceData", false },
754 { "INSTANCE_INDEX", "gl_InstanceIndex", false },
755
756 // morphing
757 { "MORPH_POSITION", "qt_getTargetPositionFromTargetId", false },
758 { "MORPH_NORMAL", "qt_getTargetNormalFromTargetId", false },
759 { "MORPH_TANGENT", "qt_getTargetTangentFromTargetId", false },
760 { "MORPH_BINORMAL", "qt_getTargetBinormalFromTargetId", false },
761 { "MORPH_WEIGHTS", "qt_morphWeights", false },
762
763 // custom variables
764 { "SHARED_VARS", "struct QT_SHARED_VARS", false },
765
766 // multiview
767 { "VIEW_INDEX", "qt_viewIndex", false }
768};
769
770// Functions that, if present, get an argument list injected.
771static const QByteArrayView qssg_func_injectarg_tab[] = {
772 "DIRECTIONAL_LIGHT",
773 "POINT_LIGHT",
774 "SPOT_LIGHT",
775 "AMBIENT_LIGHT",
776 "SPECULAR_LIGHT",
777 "MAIN",
778 "POST_PROCESS",
779 "IBL_PROBE"
780};
781
782// This is based on the Qt Quick shader rewriter (with fixes)
783struct Tokenizer {
784 enum Token {
785 Token_Comment,
786 Token_OpenBrace,
787 Token_CloseBrace,
788 Token_OpenParen,
789 Token_CloseParen,
790 Token_SemiColon,
791 Token_Identifier,
792 Token_OpenBraket,
793 Token_CloseBraket,
794 Token_Unspecified,
795
796 Token_EOF
797 };
798
799 void initialize(const QByteArray &input);
800 Token next();
801
802 const char *stream;
803 const char *pos;
804 const char *identifier;
805};
806
807void Tokenizer::initialize(const QByteArray &input)
808{
809 stream = input.constData();
810 pos = input;
811 identifier = input;
812}
813
814Tokenizer::Token Tokenizer::next()
815{
816 while (*pos) {
817 char c = *pos++;
818 switch (c) {
819 case '/':
820 if (*pos == '/') {
821 // '//' comment
822 ++pos;
823 while (*pos && *pos != '\n') ++pos;
824 return Token_Comment;
825 } else if (*pos == '*') {
826 // /* */ comment
827 ++pos;
828 while (*pos && (*pos != '*' || pos[1] != '/')) ++pos;
829 if (*pos) pos += 2;
830 return Token_Comment;
831 }
832 return Token_Unspecified;
833
834 case ';': return Token_SemiColon;
835 case '\0': return Token_EOF;
836 case '{': return Token_OpenBrace;
837 case '}': return Token_CloseBrace;
838 case '(': return Token_OpenParen;
839 case ')': return Token_CloseParen;
840 case '[': return Token_OpenBraket;
841 case ']': return Token_CloseBraket;
842
843 case ' ':
844 case '\n':
845 case '\r': break;
846 default:
847 // Identifier...
848 if ((c >= 'a' && c <= 'z' ) || (c >= 'A' && c <= 'Z' ) || c == '_') {
849 identifier = pos - 1;
850 while (*pos && ((*pos >= 'a' && *pos <= 'z')
851 || (*pos >= 'A' && *pos <= 'Z')
852 || *pos == '_'
853 || (*pos >= '0' && *pos <= '9'))) {
854 ++pos;
855 }
856 return Token_Identifier;
857 } else {
858 return Token_Unspecified;
859 }
860 }
861 }
862
863 return Token_EOF;
864}
865} // namespace
866
867void QSSGShaderCustomMaterialAdapter::beginPrepareCustomShader(
868 CustomShaderPrepWorkData *workData,
869 ShaderCodeAndMetaData *codeAndMetaData,
870 const QByteArray &shaderCode,
871 QSSGShaderCache::ShaderType type,
872 bool multiViewCompatible)
873{
874 QByteArrayList inputs;
875 QByteArrayList outputs;
876
877 Tokenizer tok;
878 tok.initialize(shaderCode);
879
880 QSSGCustomShaderMetaData md = {};
881 QByteArray result;
882 result.reserve(1024);
883 // If shader debugging is not enabled we reset the line count to make error message
884 // when a shader fails more useful. When shader debugging is enabled the whole shader
885 // will be printed and not just the user written part, so in that case we do not want
886 // to adjust the line numbers.
887 //
888 // NOTE: This is not perfect, we do expend the custom material and effect shaders, so
889 // there cane still be cases where the reported line numbers are slightly off.
890 if (!QSSGRhiContextPrivate::shaderDebuggingEnabled())
891 result.prepend("#line 1\n");
892 const char *lastPos = shaderCode.constData();
893
894 int funcFinderState = 0;
895 int useJointTexState = -1;
896 int useJointNormalTexState = -1;
897 QByteArray currentShadedFunc;
898 Tokenizer::Token t = tok.next();
899 while (t != Tokenizer::Token_EOF) {
900 switch (t) {
901 case Tokenizer::Token_Comment:
902 break;
903 case Tokenizer::Token_Identifier:
904 {
905 QByteArray id = QByteArray::fromRawData(lastPos, tok.pos - lastPos);
906 if (id.trimmed() == QByteArrayLiteral("VARYING")) {
907 QByteArray vtype;
908 QByteArray vname;
909 bool vflat = false;
910 lastPos = tok.pos;
911 t = tok.next();
912 while (t != Tokenizer::Token_EOF) {
913 QByteArray data = QByteArray::fromRawData(lastPos, tok.pos - lastPos);
914 if (t == Tokenizer::Token_Identifier) {
915 if (vtype.isEmpty()) {
916 vtype = data.trimmed();
917 if (vtype == QByteArrayLiteral("flat")) {
918 vflat = true;
919 vtype.clear();
920 }
921 } else if (vname.isEmpty()) {
922 vname = data.trimmed();
923 }
924 }
925 if (t == Tokenizer::Token_SemiColon)
926 break;
927 lastPos = tok.pos;
928 t = tok.next();
929 }
930 if (type == QSSGShaderCache::ShaderType::Vertex)
931 outputs.append((vflat ? "flat " : "") + vtype + " " + vname);
932 else
933 inputs.append((vflat ? "flat " : "") + vtype + " " + vname);
934 } else {
935 const QByteArray trimmedId = id.trimmed();
936 if (funcFinderState == 0 && trimmedId == QByteArrayLiteral("void")) {
937 funcFinderState += 1;
938 } else if (funcFinderState == 1) {
939 auto begin = qssg_func_injectarg_tab;
940 const auto end = qssg_func_injectarg_tab + (sizeof(qssg_func_injectarg_tab) / sizeof(qssg_func_injectarg_tab[0]));
941 const auto foundIt = std::find_if(begin, end, [trimmedId](const QByteArrayView &entry) { return entry == trimmedId; });
942 if (foundIt != end) {
943 currentShadedFunc = trimmedId;
944 funcFinderState += 1;
945 }
946 } else {
947 funcFinderState = 0;
948 }
949
950 if (trimmedId == QByteArrayLiteral("SCREEN_TEXTURE"))
951 md.flags |= QSSGCustomShaderMetaData::UsesScreenTexture;
952 else if (trimmedId == QByteArrayLiteral("SCREEN_MIP_TEXTURE"))
953 md.flags |= QSSGCustomShaderMetaData::UsesScreenMipTexture;
954 else if (trimmedId == QByteArrayLiteral("DEPTH_TEXTURE"))
955 md.flags |= QSSGCustomShaderMetaData::UsesDepthTexture;
956 else if (trimmedId == QByteArrayLiteral("NORMAL_ROUGHNESS_TEXTURE"))
957 md.flags |= QSSGCustomShaderMetaData::UsesNormalTexture;
958 else if (trimmedId == QByteArrayLiteral("AO_TEXTURE"))
959 md.flags |= QSSGCustomShaderMetaData::UsesAoTexture;
960 else if (trimmedId == QByteArrayLiteral("POSITION"))
961 md.flags |= QSSGCustomShaderMetaData::OverridesPosition;
962 else if (trimmedId == QByteArrayLiteral("PROJECTION_MATRIX"))
963 md.flags |= QSSGCustomShaderMetaData::UsesProjectionMatrix;
964 else if (trimmedId == QByteArrayLiteral("INVERSE_PROJECTION_MATRIX"))
965 md.flags |= QSSGCustomShaderMetaData::UsesInverseProjectionMatrix;
966 else if (trimmedId == QByteArrayLiteral("VIEW_MATRIX"))
967 md.flags |= QSSGCustomShaderMetaData::UsesViewMatrix;
968 else if (trimmedId == QByteArrayLiteral("VAR_COLOR"))
969 md.flags |= QSSGCustomShaderMetaData::UsesVarColor;
970 else if (trimmedId == QByteArrayLiteral("SHARED_VARS"))
971 md.flags |= QSSGCustomShaderMetaData::UsesSharedVars;
972 else if (trimmedId == QByteArrayLiteral("IBL_ORIENTATION"))
973 md.flags |= QSSGCustomShaderMetaData::UsesIblOrientation;
974 else if (trimmedId == QByteArrayLiteral("LIGHTMAP"))
975 md.flags |= QSSGCustomShaderMetaData::UsesLightmap;
976 else if (trimmedId == QByteArrayLiteral("VIEW_INDEX"))
977 md.flags |= QSSGCustomShaderMetaData::UsesViewIndex;
978 else if (trimmedId == QByteArrayLiteral("INPUT"))
979 md.flags |= QSSGCustomShaderMetaData::UsesInputTexture;
980 else if (trimmedId == QByteArrayLiteral("CLEARCOAT_AMOUNT"))
981 md.flags |= QSSGCustomShaderMetaData::UsesClearcoat;
982 else if (trimmedId == QByteArrayLiteral("CLEARCOAT_FRESNEL_SCALE") ||
983 trimmedId == QByteArrayLiteral("CLEARCOAT_FRESNEL_BIAS"))
984 md.flags |= QSSGCustomShaderMetaData::UsesClearcoatFresnelScaleBias;
985 else if (trimmedId == QByteArrayLiteral("FRESNEL_SCALE") ||
986 trimmedId == QByteArrayLiteral("FRESNEL_BIAS"))
987 md.flags |= QSSGCustomShaderMetaData::UsesFresnelScaleBias;
988 else if (trimmedId == QByteArrayLiteral("TRANSMISSION_FACTOR")) {
989 md.flags |= QSSGCustomShaderMetaData::UsesTransmission;
990 md.flags |= QSSGCustomShaderMetaData::UsesScreenTexture;
991 md.flags |= QSSGCustomShaderMetaData::UsesScreenMipTexture;
992 }
993
994 for (const QSSGCustomMaterialVariableSubstitution &subst : qssg_var_subst_tab) {
995 if (trimmedId == subst.builtin) {
996 QByteArray newExpr;
997 newExpr.assign(subst.actualName);
998 if (subst.multiViewDependent && multiViewCompatible) {
999 if (subst.builtin.endsWith(QByteArrayLiteral("_TEXTURE"))
1000 || subst.builtin == QByteArrayLiteral("INPUT"))
1001 {
1002 newExpr += QByteArrayLiteral("Array"); // e.g. qt_depthTexture -> qt_depthTextureArray
1003 } else {
1004 newExpr += QByteArrayLiteral("[qt_viewIndex]"); // e.g. qt_viewProjectionMatrix -> qt_viewProjectionMatrix[qt_viewIndex]
1005 }
1006 }
1007 id.replace(subst.builtin, newExpr); // replace, not assignment, to keep whitespace etc.
1008 if (trimmedId == QByteArrayLiteral("BONE_TRANSFORMS")) {
1009 useJointTexState = 0;
1010 md.flags |= QSSGCustomShaderMetaData::UsesSkinning;
1011 } else if (trimmedId == QByteArrayLiteral("BONE_NORMAL_TRANSFORMS")) {
1012 useJointNormalTexState = 0;
1013 md.flags |= QSSGCustomShaderMetaData::UsesSkinning;
1014 }
1015 if (trimmedId == QByteArrayLiteral("MORPH_POSITION") ||
1016 trimmedId == QByteArrayLiteral("MORPH_NORMAL") ||
1017 trimmedId == QByteArrayLiteral("MORPH_TANGENT") ||
1018 trimmedId == QByteArrayLiteral("MORPH_BINORMAL"))
1019 md.flags |= QSSGCustomShaderMetaData::UsesMorphing;
1020 break;
1021 }
1022 }
1023 result += id;
1024 }
1025 }
1026 break;
1027 case Tokenizer::Token_OpenParen:
1028 result += QByteArray::fromRawData(lastPos, tok.pos - lastPos);
1029 if (funcFinderState == 2) {
1030 result += QByteArrayLiteral("/*%QT_ARGS_");
1031 result += currentShadedFunc;
1032 result += QByteArrayLiteral("%*/");
1033 for (const QSSGCustomMaterialVariableSubstitution &subst : qssg_var_subst_tab) {
1034 if (currentShadedFunc == subst.builtin) {
1035 currentShadedFunc = subst.actualName.toByteArray();
1036 break;
1037 }
1038 }
1039 md.customFunctions.insert(currentShadedFunc);
1040 currentShadedFunc.clear();
1041 }
1042 funcFinderState = 0;
1043 break;
1044 case Tokenizer::Token_OpenBraket:
1045 // copy everything as-is up to the [
1046 result += QByteArray::fromRawData(lastPos, tok.pos - lastPos - 1);
1047 if (useJointTexState == 0) {
1048 result += QByteArrayLiteral("(2 * (");
1049 ++useJointTexState;
1050 break;
1051 } else if (useJointNormalTexState == 0) {
1052 result += QByteArrayLiteral("(1 + 2 * (");
1053 ++useJointNormalTexState;
1054 break;
1055 }
1056
1057 if (useJointTexState >= 0)
1058 ++useJointTexState;
1059 else if (useJointNormalTexState >= 0)
1060 ++useJointNormalTexState;
1061 result += QByteArrayLiteral("[");
1062 break;
1063 case Tokenizer::Token_CloseBraket:
1064 // copy everything as-is up to the ]
1065 result += QByteArray::fromRawData(lastPos, tok.pos - lastPos - 1);
1066 // This implementation will not allow mixed usages of BONE_TRANSFORMS and
1067 // BONE_NORMAL_TRANSFORMS.
1068 // For example, BONE_TRANSFORM[int(BONE_NORMAL_TRANFORMS[i][0].x)]
1069 // cannot be compiled successfully.
1070 if (useJointTexState <= 0 && useJointNormalTexState <= 0) {
1071 result += QByteArrayLiteral("]");
1072 break;
1073 }
1074 if (useJointTexState > 1) {
1075 result += QByteArrayLiteral("]");
1076 --useJointTexState;
1077 break;
1078 } else if (useJointNormalTexState > 1) {
1079 result += QByteArrayLiteral("]");
1080 --useJointNormalTexState;
1081 break;
1082 }
1083 result += QByteArrayLiteral("))");
1084 useJointTexState = -1;
1085 useJointNormalTexState = -1;
1086 break;
1087 default:
1088 result += QByteArray::fromRawData(lastPos, tok.pos - lastPos);
1089 break;
1090 }
1091 lastPos = tok.pos;
1092 t = tok.next();
1093 }
1094
1095 result += '\n';
1096
1097 workData->inputs = inputs;
1098 workData->outputs = outputs;
1099
1100 *codeAndMetaData = { result, md };
1101}
1102
1103void QSSGShaderCustomMaterialAdapter::finishPrepareCustomShader(
1104 QByteArray *dst,
1105 const CustomShaderPrepWorkData &workData,
1106 const ShaderCodeAndMetaData &codeAndMetaData,
1107 QSSGShaderCache::ShaderType type,
1108 bool multiViewCompatible,
1109 const StringPairList &baseUniforms,
1110 const StringPairList &baseInputs,
1111 const StringPairList &baseOutputs,
1112 const StringPairList &multiViewDependentSamplers,
1113 const StringPairList &multiViewDependentUniforms)
1114{
1115 StringPairList allUniforms = baseUniforms;
1116
1117 for (const StringPair &samplerTypeAndName : multiViewDependentSamplers) {
1118 if (multiViewCompatible)
1119 allUniforms.append({ "sampler2DArray", samplerTypeAndName.second });
1120 else
1121 allUniforms.append(samplerTypeAndName);
1122 }
1123
1124 const QSSGCustomShaderMetaData &md(codeAndMetaData.second);
1125
1126 // We either have qt_depthTexture or qt_depthTextureArray (or none of them),
1127 // but never both. We do not generally support binding a 2D texture to a
1128 // sampler2DArray binding point and vice versa. Therefore it is up to the
1129 // shader snippet to ifdef with QSHADER_VIEW_COUNT if it wants to support
1130 // both multiview and non-multiview rendering.
1131 if (md.flags.testFlag(QSSGCustomShaderMetaData::UsesDepthTexture)) {
1132 if (multiViewCompatible)
1133 allUniforms.append({ "sampler2DArray", "qt_depthTextureArray" });
1134 else
1135 allUniforms.append({ "sampler2D", "qt_depthTexture" });
1136 }
1137
1138 // And the same pattern for qt_screenTexture(Array).
1139 if ((md.flags.testFlag(QSSGCustomShaderMetaData::UsesScreenTexture) || md.flags.testFlag(QSSGCustomShaderMetaData::UsesScreenMipTexture))) {
1140 if (multiViewCompatible)
1141 allUniforms.append({ "sampler2DArray", "qt_screenTextureArray" });
1142 else
1143 allUniforms.append({ "sampler2D", "qt_screenTexture" });
1144 }
1145
1146 // And for SSAO.
1147 if (md.flags.testFlag(QSSGCustomShaderMetaData::UsesAoTexture)) {
1148 if (multiViewCompatible)
1149 allUniforms.append({ "sampler2DArray", "qt_aoTextureArray" });
1150 else
1151 allUniforms.append({ "sampler2D", "qt_aoTexture" });
1152 }
1153
1154 if (md.flags.testFlag(QSSGCustomShaderMetaData::UsesNormalTexture))
1155 allUniforms.append({ "sampler2D", "qt_normalTexture" });
1156
1157 // Input texture for post-processing effects.
1158 if (md.flags.testFlag(QSSGCustomShaderMetaData::UsesInputTexture)) {
1159 if (multiViewCompatible)
1160 allUniforms.append({ "sampler2DArray", "qt_inputTextureArray" });
1161 else
1162 allUniforms.append({ "sampler2D", "qt_inputTexture" });
1163 }
1164
1165 if (md.flags.testFlag(QSSGCustomShaderMetaData::UsesLightmap))
1166 allUniforms.append({ "sampler2D", "qt_lightmap" });
1167
1168 static const char *metaStart = "#ifdef QQ3D_SHADER_META\n/*{\n \"uniforms\": [\n";
1169 static const char *metaEnd = " ]\n}*/\n#endif\n";
1170 dst->append(metaStart);
1171
1172 for (int i = 0, count = allUniforms.size(); i < count; ++i) {
1173 const auto &typeAndName(allUniforms[i]);
1174 dst->append(" { \"type\": \"" + typeAndName.first + "\", \"name\": \"" + typeAndName.second + "\" }");
1175 if (i < count - 1 || !multiViewDependentUniforms.isEmpty())
1176 dst->append(",");
1177 dst->append("\n");
1178 }
1179
1180 // multiViewDependentUniforms are not in allUniforms. For these we leave it to the
1181 // metadata processor to add an array suffix (e.g., qt_projectionMatrix[2]) when needed.
1182 for (int i = 0, count = multiViewDependentUniforms.size(); i < count; ++i) {
1183 const auto &typeAndName(multiViewDependentUniforms[i]);
1184 dst->append(" { \"type\": \"" + typeAndName.first + "\", \"name\": \"" + typeAndName.second + "\", \"multiview_dependent\": true }");
1185 if (i < count - 1)
1186 dst->append(",");
1187 dst->append("\n");
1188 }
1189
1190 dst->append(metaEnd);
1191
1192 const char *stageStr = type == QSSGShaderCache::ShaderType::Vertex ? "vertex" : "fragment";
1193 StringPairList allInputs = baseInputs;
1194 QVarLengthArray<bool, 16> inputIsFlat(allInputs.count(), false);
1195 for (const QByteArray &inputTypeAndName : workData.inputs) {
1196 const QByteArrayList typeAndName = inputTypeAndName.split(' ');
1197 if (typeAndName.size() == 2) {
1198 allInputs.append({ typeAndName[0].trimmed(), typeAndName[1].trimmed() });
1199 inputIsFlat.append(false);
1200 } else if (typeAndName.size() == 3 && typeAndName[0].startsWith("flat")) {
1201 allInputs.append({ typeAndName[1].trimmed(), typeAndName[2].trimmed() });
1202 inputIsFlat.append(true);
1203 }
1204 }
1205 if (!allInputs.isEmpty()) {
1206 static const char *metaStart = "#ifdef QQ3D_SHADER_META\n/*{\n \"inputs\": [\n";
1207 static const char *metaEnd = " ]\n}*/\n#endif\n";
1208 dst->append(metaStart);
1209 for (int i = 0, count = allInputs.size(); i < count; ++i) {
1210 dst->append(" { \"type\": \"" + allInputs[i].first
1211 + "\", \"name\": \"" + allInputs[i].second
1212 + "\", \"stage\": \"" + stageStr
1213 + (inputIsFlat[i] ? "\", \"flat\": true" : "\"")
1214 + " }");
1215 if (i < count - 1)
1216 dst->append(",");
1217 dst->append("\n");
1218 }
1219 dst->append(metaEnd);
1220 }
1221
1222 StringPairList allOutputs = baseOutputs;
1223 QVarLengthArray<bool, 16> outputIsFlat(allOutputs.count(), false);
1224 for (const QByteArray &outputTypeAndName : workData.outputs) {
1225 const QByteArrayList typeAndName = outputTypeAndName.split(' ');
1226 if (typeAndName.size() == 2) {
1227 allOutputs.append({ typeAndName[0].trimmed(), typeAndName[1].trimmed() });
1228 outputIsFlat.append(false);
1229 } else if (typeAndName.size() == 3 && typeAndName[0].startsWith("flat")) {
1230 allOutputs.append({ typeAndName[1].trimmed(), typeAndName[2].trimmed() });
1231 outputIsFlat.append(true);
1232 }
1233 }
1234 if (!allOutputs.isEmpty()) {
1235 static const char *metaStart = "#ifdef QQ3D_SHADER_META\n/*{\n \"outputs\": [\n";
1236 static const char *metaEnd = " ]\n}*/\n#endif\n";
1237 dst->append(metaStart);
1238 for (int i = 0, count = allOutputs.size(); i < count; ++i) {
1239 dst->append(" { \"type\": \"" + allOutputs[i].first
1240 + "\", \"name\": \"" + allOutputs[i].second
1241 + "\", \"stage\": \"" + stageStr
1242 + (outputIsFlat[i] ? "\", \"flat\": true" : "\"")
1243 + " }");
1244 if (i < count - 1)
1245 dst->append(",");
1246 dst->append("\n");
1247 }
1248 dst->append(metaEnd);
1249 }
1250}
1251
1252QList<QByteArrayView> QtQuick3DEditorHelpers::CustomMaterial::preprocessorVars()
1253{
1254 QList<QByteArrayView> k;
1255 k.reserve(std::size(qssg_var_subst_tab));
1256 for (const auto &v : qssg_var_subst_tab)
1257 k.push_back(v.builtin);
1258 return k;
1259}
1260
1261QT_END_NAMESPACE