923void QSSGShaderCustomMaterialAdapter::beginPrepareCustomShader(
924 CustomShaderPrepWorkData *workData,
925 ShaderCodeAndMetaData *codeAndMetaData,
926 const QByteArray &shaderCode,
927 QSSGShaderCache::ShaderType type,
928 bool multiViewCompatible)
930 QByteArrayList inputs;
931 QByteArrayList outputs;
933 using Tokenizer = QSSGShaderTokenizer;
935 tok.initialize(shaderCode);
937 QSSGCustomShaderMetaData md = {};
939 result.reserve(1024);
947 if (!QSSGRhiContextPrivate::shaderDebuggingEnabled())
948 result.prepend(
"#line 1\n");
949 const char *lastPos = shaderCode.constData();
951 int funcFinderState = 0;
952 int useJointTexState = -1;
953 int useJointNormalTexState = -1;
954 QByteArray currentShadedFunc;
955 Tokenizer::Token t = tok.next();
956 while (t != Tokenizer::Token_EOF) {
958 case Tokenizer::Token_Comment:
960 case Tokenizer::Token_Identifier:
962 QByteArray id = QByteArray::fromRawData(lastPos, tok.pos - lastPos);
963 if (id.trimmed() == QByteArrayLiteral(
"VARYING")) {
969 while (t != Tokenizer::Token_EOF) {
970 QByteArray data = QByteArray::fromRawData(lastPos, tok.pos - lastPos);
971 if (t == Tokenizer::Token_Identifier) {
972 if (vtype.isEmpty()) {
973 vtype = data.trimmed();
974 if (vtype == QByteArrayLiteral(
"flat")) {
978 }
else if (vname.isEmpty()) {
979 vname = data.trimmed();
982 if (t == Tokenizer::Token_SemiColon)
987 if (type == QSSGShaderCache::ShaderType::Vertex)
988 outputs.append((vflat ?
"flat " :
"") + vtype +
" " + vname);
990 inputs.append((vflat ?
"flat " :
"") + vtype +
" " + vname);
992 const QByteArray trimmedId = id.trimmed();
993 if (funcFinderState == 0 && trimmedId == QByteArrayLiteral(
"void")) {
994 funcFinderState += 1;
995 }
else if (funcFinderState == 1) {
996 auto begin = qssg_func_injectarg_tab;
997 const auto end = qssg_func_injectarg_tab + (
sizeof(qssg_func_injectarg_tab) /
sizeof(qssg_func_injectarg_tab[0]));
998 const auto foundIt = std::find_if(begin, end, [trimmedId](
const QByteArrayView &entry) {
return entry == trimmedId; });
999 if (foundIt != end) {
1000 currentShadedFunc = trimmedId;
1001 funcFinderState += 1;
1004 funcFinderState = 0;
1007 if (trimmedId == QByteArrayLiteral(
"SCREEN_TEXTURE"))
1008 md.flags |= QSSGCustomShaderMetaData::UsesScreenTexture;
1009 else if (trimmedId == QByteArrayLiteral(
"SCREEN_MIP_TEXTURE"))
1010 md.flags |= QSSGCustomShaderMetaData::UsesScreenMipTexture;
1011 else if (trimmedId == QByteArrayLiteral(
"DEPTH_TEXTURE"))
1012 md.flags |= QSSGCustomShaderMetaData::UsesDepthTexture;
1013 else if (trimmedId == QByteArrayLiteral(
"NORMAL_ROUGHNESS_TEXTURE"))
1014 md.flags |= QSSGCustomShaderMetaData::UsesNormalTexture;
1015 else if (trimmedId == QByteArrayLiteral(
"AO_TEXTURE"))
1016 md.flags |= QSSGCustomShaderMetaData::UsesAoTexture;
1017 else if (trimmedId == QByteArrayLiteral(
"POSITION"))
1018 md.flags |= QSSGCustomShaderMetaData::OverridesPosition;
1019 else if (trimmedId == QByteArrayLiteral(
"PROJECTION_MATRIX"))
1020 md.flags |= QSSGCustomShaderMetaData::UsesProjectionMatrix;
1021 else if (trimmedId == QByteArrayLiteral(
"INVERSE_PROJECTION_MATRIX"))
1022 md.flags |= QSSGCustomShaderMetaData::UsesInverseProjectionMatrix;
1023 else if (trimmedId == QByteArrayLiteral(
"VIEW_MATRIX"))
1024 md.flags |= QSSGCustomShaderMetaData::UsesViewMatrix;
1025 else if (trimmedId == QByteArrayLiteral(
"VAR_COLOR"))
1026 md.flags |= QSSGCustomShaderMetaData::UsesVarColor;
1027 else if (trimmedId == QByteArrayLiteral(
"SHARED_VARS"))
1028 md.flags |= QSSGCustomShaderMetaData::UsesSharedVars;
1029 else if (trimmedId == QByteArrayLiteral(
"IBL_ORIENTATION"))
1030 md.flags |= QSSGCustomShaderMetaData::UsesIblOrientation;
1031 else if (trimmedId == QByteArrayLiteral(
"LIGHTMAP"))
1032 md.flags |= QSSGCustomShaderMetaData::UsesLightmap;
1033 else if (trimmedId == QByteArrayLiteral(
"VIEW_INDEX"))
1034 md.flags |= QSSGCustomShaderMetaData::UsesViewIndex;
1035 else if (trimmedId == QByteArrayLiteral(
"INPUT"))
1036 md.flags |= QSSGCustomShaderMetaData::UsesInputTexture;
1037 else if (trimmedId == QByteArrayLiteral(
"CLEARCOAT_AMOUNT"))
1038 md.flags |= QSSGCustomShaderMetaData::UsesClearcoat;
1039 else if (trimmedId == QByteArrayLiteral(
"SHEEN_COLOR")
1040 || trimmedId == QByteArrayLiteral(
"SHEEN_ROUGHNESS"))
1041 md.flags |= QSSGCustomShaderMetaData::UsesSheen;
1042 else if (trimmedId == QByteArrayLiteral(
"ANISOTROPY_STRENGTH")
1043 || trimmedId == QByteArrayLiteral(
"ANISOTROPY_ROTATION"))
1044 md.flags |= QSSGCustomShaderMetaData::UsesAnisotropy;
1045 else if (trimmedId == QByteArrayLiteral(
"IRIDESCENCE_FACTOR")
1046 || trimmedId == QByteArrayLiteral(
"IRIDESCENCE_IOR")
1047 || trimmedId == QByteArrayLiteral(
"IRIDESCENCE_THICKNESS"))
1048 md.flags |= QSSGCustomShaderMetaData::UsesIridescence;
1049 else if (trimmedId == QByteArrayLiteral(
"DISPERSION"))
1050 md.flags |= QSSGCustomShaderMetaData::UsesDispersion;
1051 else if (trimmedId == QByteArrayLiteral(
"CLEARCOAT_FRESNEL_SCALE") ||
1052 trimmedId == QByteArrayLiteral(
"CLEARCOAT_FRESNEL_BIAS"))
1053 md.flags |= QSSGCustomShaderMetaData::UsesClearcoatFresnelScaleBias;
1054 else if (trimmedId == QByteArrayLiteral(
"FRESNEL_SCALE") ||
1055 trimmedId == QByteArrayLiteral(
"FRESNEL_BIAS"))
1056 md.flags |= QSSGCustomShaderMetaData::UsesFresnelScaleBias;
1057 else if (trimmedId == QByteArrayLiteral(
"MOTION_VECTOR_TEXTURE"))
1058 md.flags |= QSSGCustomShaderMetaData::UsesMotionVectorTexture;
1059 else if (trimmedId == QByteArrayLiteral(
"TRANSMISSION_FACTOR")) {
1060 md.flags |= QSSGCustomShaderMetaData::UsesTransmission;
1061 md.flags |= QSSGCustomShaderMetaData::UsesScreenTexture;
1062 md.flags |= QSSGCustomShaderMetaData::UsesScreenMipTexture;
1065 for (
const QSSGCustomMaterialVariableSubstitution &subst : qssg_var_subst_tab) {
1066 if (trimmedId == subst.builtin) {
1068 newExpr.assign(subst.actualName);
1069 if (subst.multiViewDependent && multiViewCompatible) {
1070 if (subst.builtin.endsWith(QByteArrayLiteral(
"_TEXTURE"))
1071 || subst.builtin == QByteArrayLiteral(
"INPUT"))
1073 newExpr += QByteArrayLiteral(
"Array");
1075 newExpr += QByteArrayLiteral(
"[qt_viewIndex]");
1078 id.replace(subst.builtin, newExpr);
1079 if (trimmedId == QByteArrayLiteral(
"BONE_TRANSFORMS")) {
1080 useJointTexState = 0;
1081 md.flags |= QSSGCustomShaderMetaData::UsesSkinning;
1082 }
else if (trimmedId == QByteArrayLiteral(
"BONE_NORMAL_TRANSFORMS")) {
1083 useJointNormalTexState = 0;
1084 md.flags |= QSSGCustomShaderMetaData::UsesSkinning;
1086 if (trimmedId == QByteArrayLiteral(
"MORPH_POSITION") ||
1087 trimmedId == QByteArrayLiteral(
"MORPH_NORMAL") ||
1088 trimmedId == QByteArrayLiteral(
"MORPH_TANGENT") ||
1089 trimmedId == QByteArrayLiteral(
"MORPH_BINORMAL"))
1090 md.flags |= QSSGCustomShaderMetaData::UsesMorphing;
1098 case Tokenizer::Token_OpenParen:
1099 result += QByteArray::fromRawData(lastPos, tok.pos - lastPos);
1100 if (funcFinderState == 2) {
1101 result += QByteArrayLiteral(
"/*%QT_ARGS_");
1102 result += currentShadedFunc;
1103 result += QByteArrayLiteral(
"%*/");
1104 for (
const QSSGCustomMaterialVariableSubstitution &subst : qssg_var_subst_tab) {
1105 if (currentShadedFunc == subst.builtin) {
1106 currentShadedFunc = subst.actualName.toByteArray();
1110 md.customFunctions.insert(currentShadedFunc);
1111 currentShadedFunc.clear();
1113 funcFinderState = 0;
1115 case Tokenizer::Token_OpenBraket:
1117 result += QByteArray::fromRawData(lastPos, tok.pos - lastPos - 1);
1118 if (useJointTexState == 0) {
1119 result += QByteArrayLiteral(
"(2 * (");
1122 }
else if (useJointNormalTexState == 0) {
1123 result += QByteArrayLiteral(
"(1 + 2 * (");
1124 ++useJointNormalTexState;
1128 if (useJointTexState >= 0)
1130 else if (useJointNormalTexState >= 0)
1131 ++useJointNormalTexState;
1132 result += QByteArrayLiteral(
"[");
1134 case Tokenizer::Token_CloseBraket:
1136 result += QByteArray::fromRawData(lastPos, tok.pos - lastPos - 1);
1141 if (useJointTexState <= 0 && useJointNormalTexState <= 0) {
1142 result += QByteArrayLiteral(
"]");
1145 if (useJointTexState > 1) {
1146 result += QByteArrayLiteral(
"]");
1149 }
else if (useJointNormalTexState > 1) {
1150 result += QByteArrayLiteral(
"]");
1151 --useJointNormalTexState;
1154 result += QByteArrayLiteral(
"))");
1155 useJointTexState = -1;
1156 useJointNormalTexState = -1;
1159 result += QByteArray::fromRawData(lastPos, tok.pos - lastPos);
1168 workData->inputs = inputs;
1169 workData->outputs = outputs;
1171 if (md.flags.testFlag(QSSGCustomShaderMetaData::UsesDispersion)
1172 && !md.flags.testFlag(QSSGCustomShaderMetaData::UsesTransmission)) {
1173 qWarning(
"Custom material writes DISPERSION but never sets TRANSMISSION_FACTOR; "
1174 "dispersion requires transmission and will have no effect");
1177 *codeAndMetaData = { result, md };
1180void QSSGShaderCustomMaterialAdapter::finishPrepareCustomShader(
1182 const CustomShaderPrepWorkData &workData,
1183 const ShaderCodeAndMetaData &codeAndMetaData,
1184 QSSGShaderCache::ShaderType type,
1185 bool multiViewCompatible,
1186 const StringPairList &baseUniforms,
1187 const StringPairList &baseInputs,
1188 const StringPairList &baseOutputs,
1189 const StringPairList &multiViewDependentSamplers,
1190 const StringPairList &multiViewDependentUniforms)
1192 StringPairList allUniforms = baseUniforms;
1194 for (
const StringPair &samplerTypeAndName : multiViewDependentSamplers) {
1195 if (multiViewCompatible)
1196 allUniforms.append({
"sampler2DArray", samplerTypeAndName.second });
1198 allUniforms.append(samplerTypeAndName);
1201 const QSSGCustomShaderMetaData &md(codeAndMetaData.second);
1208 if (md.flags.testFlag(QSSGCustomShaderMetaData::UsesDepthTexture)) {
1209 if (multiViewCompatible)
1210 allUniforms.append({
"sampler2DArray",
"qt_depthTextureArray" });
1212 allUniforms.append({
"sampler2D",
"qt_depthTexture" });
1216 if ((md.flags.testFlag(QSSGCustomShaderMetaData::UsesScreenTexture) || md.flags.testFlag(QSSGCustomShaderMetaData::UsesScreenMipTexture))) {
1217 if (multiViewCompatible)
1218 allUniforms.append({
"sampler2DArray",
"qt_screenTextureArray" });
1220 allUniforms.append({
"sampler2D",
"qt_screenTexture" });
1224 if (md.flags.testFlag(QSSGCustomShaderMetaData::UsesAoTexture)) {
1225 if (multiViewCompatible)
1226 allUniforms.append({
"sampler2DArray",
"qt_aoTextureArray" });
1228 allUniforms.append({
"sampler2D",
"qt_aoTexture" });
1231 if (md.flags.testFlag(QSSGCustomShaderMetaData::UsesNormalTexture))
1232 allUniforms.append({
"sampler2D",
"qt_normalTexture" });
1235 if (md.flags.testFlag(QSSGCustomShaderMetaData::UsesInputTexture)) {
1236 if (multiViewCompatible)
1237 allUniforms.append({
"sampler2DArray",
"qt_inputTextureArray" });
1239 allUniforms.append({
"sampler2D",
"qt_inputTexture" });
1242 if (md.flags.testFlag(QSSGCustomShaderMetaData::UsesLightmap))
1243 allUniforms.append({
"sampler2D",
"qt_lightmap" });
1245 if (md.flags.testFlag(QSSGCustomShaderMetaData::UsesMotionVectorTexture))
1246 allUniforms.append({
"sampler2D",
"qt_motionVectorTexture" });
1248 static const char *metaStart =
"#ifdef QQ3D_SHADER_META\n/*{\n \"uniforms\": [\n";
1249 static const char *metaEnd =
" ]\n}*/\n#endif\n";
1250 dst->append(metaStart);
1252 for (
int i = 0, count = allUniforms.size(); i < count; ++i) {
1253 const auto &typeAndName(allUniforms[i]);
1254 dst->append(
" { \"type\": \"" + typeAndName.first +
"\", \"name\": \"" + typeAndName.second +
"\" }");
1255 if (i < count - 1 || !multiViewDependentUniforms.isEmpty())
1262 for (
int i = 0, count = multiViewDependentUniforms.size(); i < count; ++i) {
1263 const auto &typeAndName(multiViewDependentUniforms[i]);
1264 dst->append(
" { \"type\": \"" + typeAndName.first +
"\", \"name\": \"" + typeAndName.second +
"\", \"multiview_dependent\": true }");
1270 dst->append(metaEnd);
1272 const char *stageStr = type == QSSGShaderCache::ShaderType::Vertex ?
"vertex" :
"fragment";
1273 StringPairList allInputs = baseInputs;
1274 QVarLengthArray<
bool, 16> inputIsFlat(allInputs.count(),
false);
1275 for (
const QByteArray &inputTypeAndName : workData.inputs) {
1276 const QByteArrayList typeAndName = inputTypeAndName.split(
' ');
1277 if (typeAndName.size() == 2) {
1278 allInputs.append({ typeAndName[0].trimmed(), typeAndName[1].trimmed() });
1279 inputIsFlat.append(
false);
1280 }
else if (typeAndName.size() == 3 && typeAndName[0].startsWith(
"flat")) {
1281 allInputs.append({ typeAndName[1].trimmed(), typeAndName[2].trimmed() });
1282 inputIsFlat.append(
true);
1285 if (!allInputs.isEmpty()) {
1286 static const char *metaStart =
"#ifdef QQ3D_SHADER_META\n/*{\n \"inputs\": [\n";
1287 static const char *metaEnd =
" ]\n}*/\n#endif\n";
1288 dst->append(metaStart);
1289 for (
int i = 0, count = allInputs.size(); i < count; ++i) {
1290 dst->append(
" { \"type\": \"" + allInputs[i].first
1291 +
"\", \"name\": \"" + allInputs[i].second
1292 +
"\", \"stage\": \"" + stageStr
1293 + (inputIsFlat[i] ?
"\", \"flat\": true" :
"\"")
1299 dst->append(metaEnd);
1302 StringPairList allOutputs = baseOutputs;
1303 QVarLengthArray<
bool, 16> outputIsFlat(allOutputs.count(),
false);
1304 for (
const QByteArray &outputTypeAndName : workData.outputs) {
1305 const QByteArrayList typeAndName = outputTypeAndName.split(
' ');
1306 if (typeAndName.size() == 2) {
1307 allOutputs.append({ typeAndName[0].trimmed(), typeAndName[1].trimmed() });
1308 outputIsFlat.append(
false);
1309 }
else if (typeAndName.size() == 3 && typeAndName[0].startsWith(
"flat")) {
1310 allOutputs.append({ typeAndName[1].trimmed(), typeAndName[2].trimmed() });
1311 outputIsFlat.append(
true);
1314 if (!allOutputs.isEmpty()) {
1315 static const char *metaStart =
"#ifdef QQ3D_SHADER_META\n/*{\n \"outputs\": [\n";
1316 static const char *metaEnd =
" ]\n}*/\n#endif\n";
1317 dst->append(metaStart);
1318 for (
int i = 0, count = allOutputs.size(); i < count; ++i) {
1319 dst->append(
" { \"type\": \"" + allOutputs[i].first
1320 +
"\", \"name\": \"" + allOutputs[i].second
1321 +
"\", \"stage\": \"" + stageStr
1322 + (outputIsFlat[i] ?
"\", \"flat\": true" :
"\"")
1328 dst->append(metaEnd);