867void QSSGShaderCustomMaterialAdapter::beginPrepareCustomShader(
868 CustomShaderPrepWorkData *workData,
869 ShaderCodeAndMetaData *codeAndMetaData,
870 const QByteArray &shaderCode,
871 QSSGShaderCache::ShaderType type,
872 bool multiViewCompatible)
874 QByteArrayList inputs;
875 QByteArrayList outputs;
878 tok.initialize(shaderCode);
880 QSSGCustomShaderMetaData md = {};
882 result.reserve(1024);
890 if (!QSSGRhiContextPrivate::shaderDebuggingEnabled())
891 result.prepend(
"#line 1\n");
892 const char *lastPos = shaderCode.constData();
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) {
901 case Tokenizer::Token_Comment:
903 case Tokenizer::Token_Identifier:
905 QByteArray id = QByteArray::fromRawData(lastPos, tok.pos - lastPos);
906 if (id.trimmed() == QByteArrayLiteral(
"VARYING")) {
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")) {
921 }
else if (vname.isEmpty()) {
922 vname = data.trimmed();
925 if (t == Tokenizer::Token_SemiColon)
930 if (type == QSSGShaderCache::ShaderType::Vertex)
931 outputs.append((vflat ?
"flat " :
"") + vtype +
" " + vname);
933 inputs.append((vflat ?
"flat " :
"") + vtype +
" " + vname);
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;
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;
994 for (
const QSSGCustomMaterialVariableSubstitution &subst : qssg_var_subst_tab) {
995 if (trimmedId == subst.builtin) {
997 newExpr.assign(subst.actualName);
998 if (subst.multiViewDependent && multiViewCompatible) {
999 if (subst.builtin.endsWith(QByteArrayLiteral(
"_TEXTURE"))
1000 || subst.builtin == QByteArrayLiteral(
"INPUT"))
1002 newExpr += QByteArrayLiteral(
"Array");
1004 newExpr += QByteArrayLiteral(
"[qt_viewIndex]");
1007 id.replace(subst.builtin, newExpr);
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;
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;
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();
1039 md.customFunctions.insert(currentShadedFunc);
1040 currentShadedFunc.clear();
1042 funcFinderState = 0;
1044 case Tokenizer::Token_OpenBraket:
1046 result += QByteArray::fromRawData(lastPos, tok.pos - lastPos - 1);
1047 if (useJointTexState == 0) {
1048 result += QByteArrayLiteral(
"(2 * (");
1051 }
else if (useJointNormalTexState == 0) {
1052 result += QByteArrayLiteral(
"(1 + 2 * (");
1053 ++useJointNormalTexState;
1057 if (useJointTexState >= 0)
1059 else if (useJointNormalTexState >= 0)
1060 ++useJointNormalTexState;
1061 result += QByteArrayLiteral(
"[");
1063 case Tokenizer::Token_CloseBraket:
1065 result += QByteArray::fromRawData(lastPos, tok.pos - lastPos - 1);
1070 if (useJointTexState <= 0 && useJointNormalTexState <= 0) {
1071 result += QByteArrayLiteral(
"]");
1074 if (useJointTexState > 1) {
1075 result += QByteArrayLiteral(
"]");
1078 }
else if (useJointNormalTexState > 1) {
1079 result += QByteArrayLiteral(
"]");
1080 --useJointNormalTexState;
1083 result += QByteArrayLiteral(
"))");
1084 useJointTexState = -1;
1085 useJointNormalTexState = -1;
1088 result += QByteArray::fromRawData(lastPos, tok.pos - lastPos);
1097 workData->inputs = inputs;
1098 workData->outputs = outputs;
1100 *codeAndMetaData = { result, md };
1103void QSSGShaderCustomMaterialAdapter::finishPrepareCustomShader(
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)
1115 StringPairList allUniforms = baseUniforms;
1117 for (
const StringPair &samplerTypeAndName : multiViewDependentSamplers) {
1118 if (multiViewCompatible)
1119 allUniforms.append({
"sampler2DArray", samplerTypeAndName.second });
1121 allUniforms.append(samplerTypeAndName);
1124 const QSSGCustomShaderMetaData &md(codeAndMetaData.second);
1131 if (md.flags.testFlag(QSSGCustomShaderMetaData::UsesDepthTexture)) {
1132 if (multiViewCompatible)
1133 allUniforms.append({
"sampler2DArray",
"qt_depthTextureArray" });
1135 allUniforms.append({
"sampler2D",
"qt_depthTexture" });
1139 if ((md.flags.testFlag(QSSGCustomShaderMetaData::UsesScreenTexture) || md.flags.testFlag(QSSGCustomShaderMetaData::UsesScreenMipTexture))) {
1140 if (multiViewCompatible)
1141 allUniforms.append({
"sampler2DArray",
"qt_screenTextureArray" });
1143 allUniforms.append({
"sampler2D",
"qt_screenTexture" });
1147 if (md.flags.testFlag(QSSGCustomShaderMetaData::UsesAoTexture)) {
1148 if (multiViewCompatible)
1149 allUniforms.append({
"sampler2DArray",
"qt_aoTextureArray" });
1151 allUniforms.append({
"sampler2D",
"qt_aoTexture" });
1154 if (md.flags.testFlag(QSSGCustomShaderMetaData::UsesNormalTexture))
1155 allUniforms.append({
"sampler2D",
"qt_normalTexture" });
1158 if (md.flags.testFlag(QSSGCustomShaderMetaData::UsesInputTexture)) {
1159 if (multiViewCompatible)
1160 allUniforms.append({
"sampler2DArray",
"qt_inputTextureArray" });
1162 allUniforms.append({
"sampler2D",
"qt_inputTexture" });
1165 if (md.flags.testFlag(QSSGCustomShaderMetaData::UsesLightmap))
1166 allUniforms.append({
"sampler2D",
"qt_lightmap" });
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);
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())
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 }");
1190 dst->append(metaEnd);
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);
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" :
"\"")
1219 dst->append(metaEnd);
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);
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" :
"\"")
1248 dst->append(metaEnd);