870void QSSGShaderCustomMaterialAdapter::beginPrepareCustomShader(
871 CustomShaderPrepWorkData *workData,
872 ShaderCodeAndMetaData *codeAndMetaData,
873 const QByteArray &shaderCode,
874 QSSGShaderCache::ShaderType type,
875 bool multiViewCompatible)
877 QByteArrayList inputs;
878 QByteArrayList outputs;
881 tok.initialize(shaderCode);
883 QSSGCustomShaderMetaData md = {};
885 result.reserve(1024);
893 if (!QSSGRhiContextPrivate::shaderDebuggingEnabled())
894 result.prepend(
"#line 1\n");
895 const char *lastPos = shaderCode.constData();
897 int funcFinderState = 0;
898 int useJointTexState = -1;
899 int useJointNormalTexState = -1;
900 QByteArray currentShadedFunc;
901 Tokenizer::Token t = tok.next();
902 while (t != Tokenizer::Token_EOF) {
904 case Tokenizer::Token_Comment:
906 case Tokenizer::Token_Identifier:
908 QByteArray id = QByteArray::fromRawData(lastPos, tok.pos - lastPos);
909 if (id.trimmed() == QByteArrayLiteral(
"VARYING")) {
915 while (t != Tokenizer::Token_EOF) {
916 QByteArray data = QByteArray::fromRawData(lastPos, tok.pos - lastPos);
917 if (t == Tokenizer::Token_Identifier) {
918 if (vtype.isEmpty()) {
919 vtype = data.trimmed();
920 if (vtype == QByteArrayLiteral(
"flat")) {
924 }
else if (vname.isEmpty()) {
925 vname = data.trimmed();
928 if (t == Tokenizer::Token_SemiColon)
933 if (type == QSSGShaderCache::ShaderType::Vertex)
934 outputs.append((vflat ?
"flat " :
"") + vtype +
" " + vname);
936 inputs.append((vflat ?
"flat " :
"") + vtype +
" " + vname);
938 const QByteArray trimmedId = id.trimmed();
939 if (funcFinderState == 0 && trimmedId == QByteArrayLiteral(
"void")) {
940 funcFinderState += 1;
941 }
else if (funcFinderState == 1) {
942 auto begin = qssg_func_injectarg_tab;
943 const auto end = qssg_func_injectarg_tab + (
sizeof(qssg_func_injectarg_tab) /
sizeof(qssg_func_injectarg_tab[0]));
944 const auto foundIt = std::find_if(begin, end, [trimmedId](
const QByteArrayView &entry) {
return entry == trimmedId; });
945 if (foundIt != end) {
946 currentShadedFunc = trimmedId;
947 funcFinderState += 1;
953 if (trimmedId == QByteArrayLiteral(
"SCREEN_TEXTURE"))
954 md.flags |= QSSGCustomShaderMetaData::UsesScreenTexture;
955 else if (trimmedId == QByteArrayLiteral(
"SCREEN_MIP_TEXTURE"))
956 md.flags |= QSSGCustomShaderMetaData::UsesScreenMipTexture;
957 else if (trimmedId == QByteArrayLiteral(
"DEPTH_TEXTURE"))
958 md.flags |= QSSGCustomShaderMetaData::UsesDepthTexture;
959 else if (trimmedId == QByteArrayLiteral(
"NORMAL_ROUGHNESS_TEXTURE"))
960 md.flags |= QSSGCustomShaderMetaData::UsesNormalTexture;
961 else if (trimmedId == QByteArrayLiteral(
"AO_TEXTURE"))
962 md.flags |= QSSGCustomShaderMetaData::UsesAoTexture;
963 else if (trimmedId == QByteArrayLiteral(
"POSITION"))
964 md.flags |= QSSGCustomShaderMetaData::OverridesPosition;
965 else if (trimmedId == QByteArrayLiteral(
"PROJECTION_MATRIX"))
966 md.flags |= QSSGCustomShaderMetaData::UsesProjectionMatrix;
967 else if (trimmedId == QByteArrayLiteral(
"INVERSE_PROJECTION_MATRIX"))
968 md.flags |= QSSGCustomShaderMetaData::UsesInverseProjectionMatrix;
969 else if (trimmedId == QByteArrayLiteral(
"VIEW_MATRIX"))
970 md.flags |= QSSGCustomShaderMetaData::UsesViewMatrix;
971 else if (trimmedId == QByteArrayLiteral(
"VAR_COLOR"))
972 md.flags |= QSSGCustomShaderMetaData::UsesVarColor;
973 else if (trimmedId == QByteArrayLiteral(
"SHARED_VARS"))
974 md.flags |= QSSGCustomShaderMetaData::UsesSharedVars;
975 else if (trimmedId == QByteArrayLiteral(
"IBL_ORIENTATION"))
976 md.flags |= QSSGCustomShaderMetaData::UsesIblOrientation;
977 else if (trimmedId == QByteArrayLiteral(
"LIGHTMAP"))
978 md.flags |= QSSGCustomShaderMetaData::UsesLightmap;
979 else if (trimmedId == QByteArrayLiteral(
"VIEW_INDEX"))
980 md.flags |= QSSGCustomShaderMetaData::UsesViewIndex;
981 else if (trimmedId == QByteArrayLiteral(
"INPUT"))
982 md.flags |= QSSGCustomShaderMetaData::UsesInputTexture;
983 else if (trimmedId == QByteArrayLiteral(
"CLEARCOAT_AMOUNT"))
984 md.flags |= QSSGCustomShaderMetaData::UsesClearcoat;
985 else if (trimmedId == QByteArrayLiteral(
"CLEARCOAT_FRESNEL_SCALE") ||
986 trimmedId == QByteArrayLiteral(
"CLEARCOAT_FRESNEL_BIAS"))
987 md.flags |= QSSGCustomShaderMetaData::UsesClearcoatFresnelScaleBias;
988 else if (trimmedId == QByteArrayLiteral(
"FRESNEL_SCALE") ||
989 trimmedId == QByteArrayLiteral(
"FRESNEL_BIAS"))
990 md.flags |= QSSGCustomShaderMetaData::UsesFresnelScaleBias;
991 else if (trimmedId == QByteArrayLiteral(
"MOTION_VECTOR_TEXTURE"))
992 md.flags |= QSSGCustomShaderMetaData::UsesMotionVectorTexture;
993 else if (trimmedId == QByteArrayLiteral(
"TRANSMISSION_FACTOR")) {
994 md.flags |= QSSGCustomShaderMetaData::UsesTransmission;
995 md.flags |= QSSGCustomShaderMetaData::UsesScreenTexture;
996 md.flags |= QSSGCustomShaderMetaData::UsesScreenMipTexture;
999 for (
const QSSGCustomMaterialVariableSubstitution &subst : qssg_var_subst_tab) {
1000 if (trimmedId == subst.builtin) {
1002 newExpr.assign(subst.actualName);
1003 if (subst.multiViewDependent && multiViewCompatible) {
1004 if (subst.builtin.endsWith(QByteArrayLiteral(
"_TEXTURE"))
1005 || subst.builtin == QByteArrayLiteral(
"INPUT"))
1007 newExpr += QByteArrayLiteral(
"Array");
1009 newExpr += QByteArrayLiteral(
"[qt_viewIndex]");
1012 id.replace(subst.builtin, newExpr);
1013 if (trimmedId == QByteArrayLiteral(
"BONE_TRANSFORMS")) {
1014 useJointTexState = 0;
1015 md.flags |= QSSGCustomShaderMetaData::UsesSkinning;
1016 }
else if (trimmedId == QByteArrayLiteral(
"BONE_NORMAL_TRANSFORMS")) {
1017 useJointNormalTexState = 0;
1018 md.flags |= QSSGCustomShaderMetaData::UsesSkinning;
1020 if (trimmedId == QByteArrayLiteral(
"MORPH_POSITION") ||
1021 trimmedId == QByteArrayLiteral(
"MORPH_NORMAL") ||
1022 trimmedId == QByteArrayLiteral(
"MORPH_TANGENT") ||
1023 trimmedId == QByteArrayLiteral(
"MORPH_BINORMAL"))
1024 md.flags |= QSSGCustomShaderMetaData::UsesMorphing;
1032 case Tokenizer::Token_OpenParen:
1033 result += QByteArray::fromRawData(lastPos, tok.pos - lastPos);
1034 if (funcFinderState == 2) {
1035 result += QByteArrayLiteral(
"/*%QT_ARGS_");
1036 result += currentShadedFunc;
1037 result += QByteArrayLiteral(
"%*/");
1038 for (
const QSSGCustomMaterialVariableSubstitution &subst : qssg_var_subst_tab) {
1039 if (currentShadedFunc == subst.builtin) {
1040 currentShadedFunc = subst.actualName.toByteArray();
1044 md.customFunctions.insert(currentShadedFunc);
1045 currentShadedFunc.clear();
1047 funcFinderState = 0;
1049 case Tokenizer::Token_OpenBraket:
1051 result += QByteArray::fromRawData(lastPos, tok.pos - lastPos - 1);
1052 if (useJointTexState == 0) {
1053 result += QByteArrayLiteral(
"(2 * (");
1056 }
else if (useJointNormalTexState == 0) {
1057 result += QByteArrayLiteral(
"(1 + 2 * (");
1058 ++useJointNormalTexState;
1062 if (useJointTexState >= 0)
1064 else if (useJointNormalTexState >= 0)
1065 ++useJointNormalTexState;
1066 result += QByteArrayLiteral(
"[");
1068 case Tokenizer::Token_CloseBraket:
1070 result += QByteArray::fromRawData(lastPos, tok.pos - lastPos - 1);
1075 if (useJointTexState <= 0 && useJointNormalTexState <= 0) {
1076 result += QByteArrayLiteral(
"]");
1079 if (useJointTexState > 1) {
1080 result += QByteArrayLiteral(
"]");
1083 }
else if (useJointNormalTexState > 1) {
1084 result += QByteArrayLiteral(
"]");
1085 --useJointNormalTexState;
1088 result += QByteArrayLiteral(
"))");
1089 useJointTexState = -1;
1090 useJointNormalTexState = -1;
1093 result += QByteArray::fromRawData(lastPos, tok.pos - lastPos);
1102 workData->inputs = inputs;
1103 workData->outputs = outputs;
1105 *codeAndMetaData = { result, md };
1108void QSSGShaderCustomMaterialAdapter::finishPrepareCustomShader(
1110 const CustomShaderPrepWorkData &workData,
1111 const ShaderCodeAndMetaData &codeAndMetaData,
1112 QSSGShaderCache::ShaderType type,
1113 bool multiViewCompatible,
1114 const StringPairList &baseUniforms,
1115 const StringPairList &baseInputs,
1116 const StringPairList &baseOutputs,
1117 const StringPairList &multiViewDependentSamplers,
1118 const StringPairList &multiViewDependentUniforms)
1120 StringPairList allUniforms = baseUniforms;
1122 for (
const StringPair &samplerTypeAndName : multiViewDependentSamplers) {
1123 if (multiViewCompatible)
1124 allUniforms.append({
"sampler2DArray", samplerTypeAndName.second });
1126 allUniforms.append(samplerTypeAndName);
1129 const QSSGCustomShaderMetaData &md(codeAndMetaData.second);
1136 if (md.flags.testFlag(QSSGCustomShaderMetaData::UsesDepthTexture)) {
1137 if (multiViewCompatible)
1138 allUniforms.append({
"sampler2DArray",
"qt_depthTextureArray" });
1140 allUniforms.append({
"sampler2D",
"qt_depthTexture" });
1144 if ((md.flags.testFlag(QSSGCustomShaderMetaData::UsesScreenTexture) || md.flags.testFlag(QSSGCustomShaderMetaData::UsesScreenMipTexture))) {
1145 if (multiViewCompatible)
1146 allUniforms.append({
"sampler2DArray",
"qt_screenTextureArray" });
1148 allUniforms.append({
"sampler2D",
"qt_screenTexture" });
1152 if (md.flags.testFlag(QSSGCustomShaderMetaData::UsesAoTexture)) {
1153 if (multiViewCompatible)
1154 allUniforms.append({
"sampler2DArray",
"qt_aoTextureArray" });
1156 allUniforms.append({
"sampler2D",
"qt_aoTexture" });
1159 if (md.flags.testFlag(QSSGCustomShaderMetaData::UsesNormalTexture))
1160 allUniforms.append({
"sampler2D",
"qt_normalTexture" });
1163 if (md.flags.testFlag(QSSGCustomShaderMetaData::UsesInputTexture)) {
1164 if (multiViewCompatible)
1165 allUniforms.append({
"sampler2DArray",
"qt_inputTextureArray" });
1167 allUniforms.append({
"sampler2D",
"qt_inputTexture" });
1170 if (md.flags.testFlag(QSSGCustomShaderMetaData::UsesLightmap))
1171 allUniforms.append({
"sampler2D",
"qt_lightmap" });
1173 if (md.flags.testFlag(QSSGCustomShaderMetaData::UsesMotionVectorTexture))
1174 allUniforms.append({
"sampler2D",
"qt_motionVectorTexture" });
1176 static const char *metaStart =
"#ifdef QQ3D_SHADER_META\n/*{\n \"uniforms\": [\n";
1177 static const char *metaEnd =
" ]\n}*/\n#endif\n";
1178 dst->append(metaStart);
1180 for (
int i = 0, count = allUniforms.size(); i < count; ++i) {
1181 const auto &typeAndName(allUniforms[i]);
1182 dst->append(
" { \"type\": \"" + typeAndName.first +
"\", \"name\": \"" + typeAndName.second +
"\" }");
1183 if (i < count - 1 || !multiViewDependentUniforms.isEmpty())
1190 for (
int i = 0, count = multiViewDependentUniforms.size(); i < count; ++i) {
1191 const auto &typeAndName(multiViewDependentUniforms[i]);
1192 dst->append(
" { \"type\": \"" + typeAndName.first +
"\", \"name\": \"" + typeAndName.second +
"\", \"multiview_dependent\": true }");
1198 dst->append(metaEnd);
1200 const char *stageStr = type == QSSGShaderCache::ShaderType::Vertex ?
"vertex" :
"fragment";
1201 StringPairList allInputs = baseInputs;
1202 QVarLengthArray<
bool, 16> inputIsFlat(allInputs.count(),
false);
1203 for (
const QByteArray &inputTypeAndName : workData.inputs) {
1204 const QByteArrayList typeAndName = inputTypeAndName.split(
' ');
1205 if (typeAndName.size() == 2) {
1206 allInputs.append({ typeAndName[0].trimmed(), typeAndName[1].trimmed() });
1207 inputIsFlat.append(
false);
1208 }
else if (typeAndName.size() == 3 && typeAndName[0].startsWith(
"flat")) {
1209 allInputs.append({ typeAndName[1].trimmed(), typeAndName[2].trimmed() });
1210 inputIsFlat.append(
true);
1213 if (!allInputs.isEmpty()) {
1214 static const char *metaStart =
"#ifdef QQ3D_SHADER_META\n/*{\n \"inputs\": [\n";
1215 static const char *metaEnd =
" ]\n}*/\n#endif\n";
1216 dst->append(metaStart);
1217 for (
int i = 0, count = allInputs.size(); i < count; ++i) {
1218 dst->append(
" { \"type\": \"" + allInputs[i].first
1219 +
"\", \"name\": \"" + allInputs[i].second
1220 +
"\", \"stage\": \"" + stageStr
1221 + (inputIsFlat[i] ?
"\", \"flat\": true" :
"\"")
1227 dst->append(metaEnd);
1230 StringPairList allOutputs = baseOutputs;
1231 QVarLengthArray<
bool, 16> outputIsFlat(allOutputs.count(),
false);
1232 for (
const QByteArray &outputTypeAndName : workData.outputs) {
1233 const QByteArrayList typeAndName = outputTypeAndName.split(
' ');
1234 if (typeAndName.size() == 2) {
1235 allOutputs.append({ typeAndName[0].trimmed(), typeAndName[1].trimmed() });
1236 outputIsFlat.append(
false);
1237 }
else if (typeAndName.size() == 3 && typeAndName[0].startsWith(
"flat")) {
1238 allOutputs.append({ typeAndName[1].trimmed(), typeAndName[2].trimmed() });
1239 outputIsFlat.append(
true);
1242 if (!allOutputs.isEmpty()) {
1243 static const char *metaStart =
"#ifdef QQ3D_SHADER_META\n/*{\n \"outputs\": [\n";
1244 static const char *metaEnd =
" ]\n}*/\n#endif\n";
1245 dst->append(metaStart);
1246 for (
int i = 0, count = allOutputs.size(); i < count; ++i) {
1247 dst->append(
" { \"type\": \"" + allOutputs[i].first
1248 +
"\", \"name\": \"" + allOutputs[i].second
1249 +
"\", \"stage\": \"" + stageStr
1250 + (outputIsFlat[i] ?
"\", \"flat\": true" :
"\"")
1256 dst->append(metaEnd);