1005void QSSGShaderCustomMaterialAdapter::beginPrepareCustomShader(
1006 CustomShaderPrepWorkData *workData,
1007 ShaderCodeAndMetaData *codeAndMetaData,
1008 const QByteArray &shaderCode,
1009 QSSGShaderCache::ShaderType type,
1010 bool multiViewCompatible)
1012 QByteArrayList inputs;
1013 QByteArrayList outputs;
1016 tok.initialize(shaderCode);
1018 QSSGCustomShaderMetaData md = {};
1020 result.reserve(1024);
1028 if (!QSSGRhiContextPrivate::shaderDebuggingEnabled())
1029 result.prepend(
"#line 1\n");
1030 const char *lastPos = shaderCode.constData();
1032 int funcFinderState = 0;
1033 int useJointTexState = -1;
1034 int useJointNormalTexState = -1;
1035 QByteArray currentShadedFunc;
1036 Tokenizer::Token t = tok.next();
1037 while (t != Tokenizer::Token_EOF) {
1039 case Tokenizer::Token_Comment:
1041 case Tokenizer::Token_Identifier:
1043 QByteArray id = QByteArray::fromRawData(lastPos, tok.pos - lastPos);
1044 if (id.trimmed() == QByteArrayLiteral(
"VARYING")) {
1050 while (t != Tokenizer::Token_EOF) {
1051 QByteArray data = QByteArray::fromRawData(lastPos, tok.pos - lastPos);
1052 if (t == Tokenizer::Token_Identifier) {
1053 if (vtype.isEmpty()) {
1054 vtype = data.trimmed();
1055 if (vtype == QByteArrayLiteral(
"flat")) {
1059 }
else if (vname.isEmpty()) {
1060 vname = data.trimmed();
1063 if (t == Tokenizer::Token_SemiColon)
1068 if (type == QSSGShaderCache::ShaderType::Vertex)
1069 outputs.append((vflat ?
"flat " :
"") + vtype +
" " + vname);
1071 inputs.append((vflat ?
"flat " :
"") + vtype +
" " + vname);
1073 const QByteArray trimmedId = id.trimmed();
1074 if (funcFinderState == 0 && trimmedId == QByteArrayLiteral(
"void")) {
1075 funcFinderState += 1;
1076 }
else if (funcFinderState == 1) {
1077 auto begin = qssg_func_injectarg_tab;
1078 const auto end = qssg_func_injectarg_tab + (
sizeof(qssg_func_injectarg_tab) /
sizeof(qssg_func_injectarg_tab[0]));
1079 const auto foundIt = std::find_if(begin, end, [trimmedId](
const QByteArrayView &entry) {
return entry == trimmedId; });
1080 if (foundIt != end) {
1081 currentShadedFunc = trimmedId;
1082 funcFinderState += 1;
1085 funcFinderState = 0;
1088 if (trimmedId == QByteArrayLiteral(
"SCREEN_TEXTURE"))
1089 md.flags |= QSSGCustomShaderMetaData::UsesScreenTexture;
1090 else if (trimmedId == QByteArrayLiteral(
"SCREEN_MIP_TEXTURE"))
1091 md.flags |= QSSGCustomShaderMetaData::UsesScreenMipTexture;
1092 else if (trimmedId == QByteArrayLiteral(
"DEPTH_TEXTURE"))
1093 md.flags |= QSSGCustomShaderMetaData::UsesDepthTexture;
1094 else if (trimmedId == QByteArrayLiteral(
"NORMAL_ROUGHNESS_TEXTURE"))
1095 md.flags |= QSSGCustomShaderMetaData::UsesNormalTexture;
1096 else if (trimmedId == QByteArrayLiteral(
"AO_TEXTURE"))
1097 md.flags |= QSSGCustomShaderMetaData::UsesAoTexture;
1098 else if (trimmedId == QByteArrayLiteral(
"POSITION"))
1099 md.flags |= QSSGCustomShaderMetaData::OverridesPosition;
1100 else if (trimmedId == QByteArrayLiteral(
"PROJECTION_MATRIX"))
1101 md.flags |= QSSGCustomShaderMetaData::UsesProjectionMatrix;
1102 else if (trimmedId == QByteArrayLiteral(
"INVERSE_PROJECTION_MATRIX"))
1103 md.flags |= QSSGCustomShaderMetaData::UsesInverseProjectionMatrix;
1104 else if (trimmedId == QByteArrayLiteral(
"VIEW_MATRIX"))
1105 md.flags |= QSSGCustomShaderMetaData::UsesViewMatrix;
1106 else if (trimmedId == QByteArrayLiteral(
"VAR_COLOR"))
1107 md.flags |= QSSGCustomShaderMetaData::UsesVarColor;
1108 else if (trimmedId == QByteArrayLiteral(
"SHARED_VARS"))
1109 md.flags |= QSSGCustomShaderMetaData::UsesSharedVars;
1110 else if (trimmedId == QByteArrayLiteral(
"IBL_ORIENTATION"))
1111 md.flags |= QSSGCustomShaderMetaData::UsesIblOrientation;
1112 else if (trimmedId == QByteArrayLiteral(
"LIGHTMAP"))
1113 md.flags |= QSSGCustomShaderMetaData::UsesLightmap;
1114 else if (trimmedId == QByteArrayLiteral(
"VIEW_INDEX"))
1115 md.flags |= QSSGCustomShaderMetaData::UsesViewIndex;
1116 else if (trimmedId == QByteArrayLiteral(
"INPUT"))
1117 md.flags |= QSSGCustomShaderMetaData::UsesInputTexture;
1118 else if (trimmedId == QByteArrayLiteral(
"CLEARCOAT_AMOUNT"))
1119 md.flags |= QSSGCustomShaderMetaData::UsesClearcoat;
1120 else if (trimmedId == QByteArrayLiteral(
"SHEEN_COLOR")
1121 || trimmedId == QByteArrayLiteral(
"SHEEN_ROUGHNESS"))
1122 md.flags |= QSSGCustomShaderMetaData::UsesSheen;
1123 else if (trimmedId == QByteArrayLiteral(
"ANISOTROPY_STRENGTH")
1124 || trimmedId == QByteArrayLiteral(
"ANISOTROPY_ROTATION"))
1125 md.flags |= QSSGCustomShaderMetaData::UsesAnisotropy;
1126 else if (trimmedId == QByteArrayLiteral(
"IRIDESCENCE_FACTOR")
1127 || trimmedId == QByteArrayLiteral(
"IRIDESCENCE_IOR")
1128 || trimmedId == QByteArrayLiteral(
"IRIDESCENCE_THICKNESS"))
1129 md.flags |= QSSGCustomShaderMetaData::UsesIridescence;
1130 else if (trimmedId == QByteArrayLiteral(
"DISPERSION"))
1131 md.flags |= QSSGCustomShaderMetaData::UsesDispersion;
1132 else if (trimmedId == QByteArrayLiteral(
"CLEARCOAT_FRESNEL_SCALE") ||
1133 trimmedId == QByteArrayLiteral(
"CLEARCOAT_FRESNEL_BIAS"))
1134 md.flags |= QSSGCustomShaderMetaData::UsesClearcoatFresnelScaleBias;
1135 else if (trimmedId == QByteArrayLiteral(
"FRESNEL_SCALE") ||
1136 trimmedId == QByteArrayLiteral(
"FRESNEL_BIAS"))
1137 md.flags |= QSSGCustomShaderMetaData::UsesFresnelScaleBias;
1138 else if (trimmedId == QByteArrayLiteral(
"MOTION_VECTOR_TEXTURE"))
1139 md.flags |= QSSGCustomShaderMetaData::UsesMotionVectorTexture;
1140 else if (trimmedId == QByteArrayLiteral(
"TRANSMISSION_FACTOR")) {
1141 md.flags |= QSSGCustomShaderMetaData::UsesTransmission;
1142 md.flags |= QSSGCustomShaderMetaData::UsesScreenTexture;
1143 md.flags |= QSSGCustomShaderMetaData::UsesScreenMipTexture;
1146 for (
const QSSGCustomMaterialVariableSubstitution &subst : qssg_var_subst_tab) {
1147 if (trimmedId == subst.builtin) {
1149 newExpr.assign(subst.actualName);
1150 if (subst.multiViewDependent && multiViewCompatible) {
1151 if (subst.builtin.endsWith(QByteArrayLiteral(
"_TEXTURE"))
1152 || subst.builtin == QByteArrayLiteral(
"INPUT"))
1154 newExpr += QByteArrayLiteral(
"Array");
1156 newExpr += QByteArrayLiteral(
"[qt_viewIndex]");
1159 id.replace(subst.builtin, newExpr);
1160 if (trimmedId == QByteArrayLiteral(
"BONE_TRANSFORMS")) {
1161 useJointTexState = 0;
1162 md.flags |= QSSGCustomShaderMetaData::UsesSkinning;
1163 }
else if (trimmedId == QByteArrayLiteral(
"BONE_NORMAL_TRANSFORMS")) {
1164 useJointNormalTexState = 0;
1165 md.flags |= QSSGCustomShaderMetaData::UsesSkinning;
1167 if (trimmedId == QByteArrayLiteral(
"MORPH_POSITION") ||
1168 trimmedId == QByteArrayLiteral(
"MORPH_NORMAL") ||
1169 trimmedId == QByteArrayLiteral(
"MORPH_TANGENT") ||
1170 trimmedId == QByteArrayLiteral(
"MORPH_BINORMAL"))
1171 md.flags |= QSSGCustomShaderMetaData::UsesMorphing;
1179 case Tokenizer::Token_OpenParen:
1180 result += QByteArray::fromRawData(lastPos, tok.pos - lastPos);
1181 if (funcFinderState == 2) {
1182 result += QByteArrayLiteral(
"/*%QT_ARGS_");
1183 result += currentShadedFunc;
1184 result += QByteArrayLiteral(
"%*/");
1185 for (
const QSSGCustomMaterialVariableSubstitution &subst : qssg_var_subst_tab) {
1186 if (currentShadedFunc == subst.builtin) {
1187 currentShadedFunc = subst.actualName.toByteArray();
1191 md.customFunctions.insert(currentShadedFunc);
1192 currentShadedFunc.clear();
1194 funcFinderState = 0;
1196 case Tokenizer::Token_OpenBraket:
1198 result += QByteArray::fromRawData(lastPos, tok.pos - lastPos - 1);
1199 if (useJointTexState == 0) {
1200 result += QByteArrayLiteral(
"(2 * (");
1203 }
else if (useJointNormalTexState == 0) {
1204 result += QByteArrayLiteral(
"(1 + 2 * (");
1205 ++useJointNormalTexState;
1209 if (useJointTexState >= 0)
1211 else if (useJointNormalTexState >= 0)
1212 ++useJointNormalTexState;
1213 result += QByteArrayLiteral(
"[");
1215 case Tokenizer::Token_CloseBraket:
1217 result += QByteArray::fromRawData(lastPos, tok.pos - lastPos - 1);
1222 if (useJointTexState <= 0 && useJointNormalTexState <= 0) {
1223 result += QByteArrayLiteral(
"]");
1226 if (useJointTexState > 1) {
1227 result += QByteArrayLiteral(
"]");
1230 }
else if (useJointNormalTexState > 1) {
1231 result += QByteArrayLiteral(
"]");
1232 --useJointNormalTexState;
1235 result += QByteArrayLiteral(
"))");
1236 useJointTexState = -1;
1237 useJointNormalTexState = -1;
1240 result += QByteArray::fromRawData(lastPos, tok.pos - lastPos);
1249 workData->inputs = inputs;
1250 workData->outputs = outputs;
1252 if (md.flags.testFlag(QSSGCustomShaderMetaData::UsesDispersion)
1253 && !md.flags.testFlag(QSSGCustomShaderMetaData::UsesTransmission)) {
1254 qWarning(
"Custom material writes DISPERSION but never sets TRANSMISSION_FACTOR; "
1255 "dispersion requires transmission and will have no effect");
1258 *codeAndMetaData = { result, md };
1261void QSSGShaderCustomMaterialAdapter::finishPrepareCustomShader(
1263 const CustomShaderPrepWorkData &workData,
1264 const ShaderCodeAndMetaData &codeAndMetaData,
1265 QSSGShaderCache::ShaderType type,
1266 bool multiViewCompatible,
1267 const StringPairList &baseUniforms,
1268 const StringPairList &baseInputs,
1269 const StringPairList &baseOutputs,
1270 const StringPairList &multiViewDependentSamplers,
1271 const StringPairList &multiViewDependentUniforms)
1273 StringPairList allUniforms = baseUniforms;
1275 for (
const StringPair &samplerTypeAndName : multiViewDependentSamplers) {
1276 if (multiViewCompatible)
1277 allUniforms.append({
"sampler2DArray", samplerTypeAndName.second });
1279 allUniforms.append(samplerTypeAndName);
1282 const QSSGCustomShaderMetaData &md(codeAndMetaData.second);
1289 if (md.flags.testFlag(QSSGCustomShaderMetaData::UsesDepthTexture)) {
1290 if (multiViewCompatible)
1291 allUniforms.append({
"sampler2DArray",
"qt_depthTextureArray" });
1293 allUniforms.append({
"sampler2D",
"qt_depthTexture" });
1297 if ((md.flags.testFlag(QSSGCustomShaderMetaData::UsesScreenTexture) || md.flags.testFlag(QSSGCustomShaderMetaData::UsesScreenMipTexture))) {
1298 if (multiViewCompatible)
1299 allUniforms.append({
"sampler2DArray",
"qt_screenTextureArray" });
1301 allUniforms.append({
"sampler2D",
"qt_screenTexture" });
1305 if (md.flags.testFlag(QSSGCustomShaderMetaData::UsesAoTexture)) {
1306 if (multiViewCompatible)
1307 allUniforms.append({
"sampler2DArray",
"qt_aoTextureArray" });
1309 allUniforms.append({
"sampler2D",
"qt_aoTexture" });
1312 if (md.flags.testFlag(QSSGCustomShaderMetaData::UsesNormalTexture))
1313 allUniforms.append({
"sampler2D",
"qt_normalTexture" });
1316 if (md.flags.testFlag(QSSGCustomShaderMetaData::UsesInputTexture)) {
1317 if (multiViewCompatible)
1318 allUniforms.append({
"sampler2DArray",
"qt_inputTextureArray" });
1320 allUniforms.append({
"sampler2D",
"qt_inputTexture" });
1323 if (md.flags.testFlag(QSSGCustomShaderMetaData::UsesLightmap))
1324 allUniforms.append({
"sampler2D",
"qt_lightmap" });
1326 if (md.flags.testFlag(QSSGCustomShaderMetaData::UsesMotionVectorTexture))
1327 allUniforms.append({
"sampler2D",
"qt_motionVectorTexture" });
1329 static const char *metaStart =
"#ifdef QQ3D_SHADER_META\n/*{\n \"uniforms\": [\n";
1330 static const char *metaEnd =
" ]\n}*/\n#endif\n";
1331 dst->append(metaStart);
1333 for (
int i = 0, count = allUniforms.size(); i < count; ++i) {
1334 const auto &typeAndName(allUniforms[i]);
1335 dst->append(
" { \"type\": \"" + typeAndName.first +
"\", \"name\": \"" + typeAndName.second +
"\" }");
1336 if (i < count - 1 || !multiViewDependentUniforms.isEmpty())
1343 for (
int i = 0, count = multiViewDependentUniforms.size(); i < count; ++i) {
1344 const auto &typeAndName(multiViewDependentUniforms[i]);
1345 dst->append(
" { \"type\": \"" + typeAndName.first +
"\", \"name\": \"" + typeAndName.second +
"\", \"multiview_dependent\": true }");
1351 dst->append(metaEnd);
1353 const char *stageStr = type == QSSGShaderCache::ShaderType::Vertex ?
"vertex" :
"fragment";
1354 StringPairList allInputs = baseInputs;
1355 QVarLengthArray<
bool, 16> inputIsFlat(allInputs.count(),
false);
1356 for (
const QByteArray &inputTypeAndName : workData.inputs) {
1357 const QByteArrayList typeAndName = inputTypeAndName.split(
' ');
1358 if (typeAndName.size() == 2) {
1359 allInputs.append({ typeAndName[0].trimmed(), typeAndName[1].trimmed() });
1360 inputIsFlat.append(
false);
1361 }
else if (typeAndName.size() == 3 && typeAndName[0].startsWith(
"flat")) {
1362 allInputs.append({ typeAndName[1].trimmed(), typeAndName[2].trimmed() });
1363 inputIsFlat.append(
true);
1366 if (!allInputs.isEmpty()) {
1367 static const char *metaStart =
"#ifdef QQ3D_SHADER_META\n/*{\n \"inputs\": [\n";
1368 static const char *metaEnd =
" ]\n}*/\n#endif\n";
1369 dst->append(metaStart);
1370 for (
int i = 0, count = allInputs.size(); i < count; ++i) {
1371 dst->append(
" { \"type\": \"" + allInputs[i].first
1372 +
"\", \"name\": \"" + allInputs[i].second
1373 +
"\", \"stage\": \"" + stageStr
1374 + (inputIsFlat[i] ?
"\", \"flat\": true" :
"\"")
1380 dst->append(metaEnd);
1383 StringPairList allOutputs = baseOutputs;
1384 QVarLengthArray<
bool, 16> outputIsFlat(allOutputs.count(),
false);
1385 for (
const QByteArray &outputTypeAndName : workData.outputs) {
1386 const QByteArrayList typeAndName = outputTypeAndName.split(
' ');
1387 if (typeAndName.size() == 2) {
1388 allOutputs.append({ typeAndName[0].trimmed(), typeAndName[1].trimmed() });
1389 outputIsFlat.append(
false);
1390 }
else if (typeAndName.size() == 3 && typeAndName[0].startsWith(
"flat")) {
1391 allOutputs.append({ typeAndName[1].trimmed(), typeAndName[2].trimmed() });
1392 outputIsFlat.append(
true);
1395 if (!allOutputs.isEmpty()) {
1396 static const char *metaStart =
"#ifdef QQ3D_SHADER_META\n/*{\n \"outputs\": [\n";
1397 static const char *metaEnd =
" ]\n}*/\n#endif\n";
1398 dst->append(metaStart);
1399 for (
int i = 0, count = allOutputs.size(); i < count; ++i) {
1400 dst->append(
" { \"type\": \"" + allOutputs[i].first
1401 +
"\", \"name\": \"" + allOutputs[i].second
1402 +
"\", \"stage\": \"" + stageStr
1403 + (outputIsFlat[i] ?
"\", \"flat\": true" :
"\"")
1409 dst->append(metaEnd);