698QSSGRenderGraphObject *QQuick3DEffect::updateSpatialNode(QSSGRenderGraphObject *node)
700 using namespace QSSGShaderUtils;
702 const auto &renderContext = QQuick3DObjectPrivate::get(
this)->sceneManager->wattached->rci();
703 if (!renderContext) {
704 qWarning(
"QQuick3DEffect: No render context interface?");
708 QSSGRenderEffect *effectNode =
static_cast<QSSGRenderEffect *>(node);
709 bool newBackendNode =
false;
711 effectNode =
new QSSGRenderEffect;
712 newBackendNode =
true;
715 bool shadersOrBuffersMayChange =
false;
716 if (m_dirtyAttributes & Dirty::EffectChainDirty)
717 shadersOrBuffersMayChange =
true;
719 const bool fullUpdate = newBackendNode || effectNode->incompleteBuildTimeObject || (m_dirtyAttributes & Dirty::TextureDirty);
721 if (fullUpdate || shadersOrBuffersMayChange) {
725 effectNode->properties.clear();
726 effectNode->textureProperties.clear();
728 QMetaMethod propertyDirtyMethod;
729 const int idx = metaObject()->indexOfSlot(
"onPropertyDirty()");
731 propertyDirtyMethod = metaObject()->method(idx);
734 QSSGShaderCustomMaterialAdapter::StringPairList uniforms;
735 QSSGShaderCustomMaterialAdapter::StringPairList multiViewDependentSamplers;
736 const int propCount = metaObject()->propertyCount();
737 int propOffset = metaObject()->propertyOffset();
740 const QMetaObject *superClass = metaObject()->superClass();
741 while (superClass && qstrcmp(superClass->className(),
"QQuick3DEffect") != 0) {
742 propOffset = superClass->propertyOffset();
743 superClass = superClass->superClass();
746 using TextureInputProperty = QPair<QQuick3DShaderUtilsTextureInput *,
const char *>;
748 QVector<TextureInputProperty> textureProperties;
749 for (
int i = propOffset; i != propCount; ++i) {
750 const QMetaProperty property = metaObject()->property(i);
751 if (Q_UNLIKELY(!property.isValid()))
754 const auto name = property.name();
755 QMetaType propType = property.metaType();
756 QVariant propValue = property.read(
this);
757 if (propType == QMetaType(QMetaType::QVariant))
758 propType = propValue.metaType();
760 if (propType.id() >= QMetaType::User) {
761 if (propType.id() == qMetaTypeId<QQuick3DShaderUtilsTextureInput *>()) {
762 if (QQuick3DShaderUtilsTextureInput *texture = property.read(
this).value<QQuick3DShaderUtilsTextureInput *>())
763 textureProperties.push_back({texture, name});
765 }
else if (propType == QMetaType(QMetaType::QObjectStar)) {
766 if (QQuick3DShaderUtilsTextureInput *texture = qobject_cast<QQuick3DShaderUtilsTextureInput *>(propValue.value<QObject *>()))
767 textureProperties.push_back({texture, name});
769 const auto type = uniformType(propType);
770 if (type != QSSGRenderShaderValue::Unknown) {
771 uniforms.append({ uniformTypeName(propType), name });
772 effectNode->properties.push_back({ name, uniformTypeName(propType),
773 propValue, uniformType(propType), i});
776 if (property.hasNotifySignal() && propertyDirtyMethod.isValid())
777 connect(
this, property.notifySignal(),
this, propertyDirtyMethod);
787 const auto processTextureProperty = [&](QQuick3DShaderUtilsTextureInput &texture,
const QByteArray &name) {
788 QSSGRenderEffect::TextureProperty texProp;
789 QQuick3DTexture *tex = texture.texture();
791 connect(&texture, &QQuick3DShaderUtilsTextureInput::enabledChanged,
this, &QQuick3DEffect::onTextureDirty);
792 connect(&texture, &QQuick3DShaderUtilsTextureInput::textureChanged,
this, &QQuick3DEffect::onTextureDirty);
795 if (texture.enabled && tex)
796 texProp.texImage = tex->getRenderImage();
798 texProp.shaderDataType = QSSGRenderShaderValue::Texture;
801 texProp.minFilterType = tex->minFilter() == QQuick3DTexture::Nearest ? QSSGRenderTextureFilterOp::Nearest
802 : QSSGRenderTextureFilterOp::Linear;
803 texProp.magFilterType = tex->magFilter() == QQuick3DTexture::Nearest ? QSSGRenderTextureFilterOp::Nearest
804 : QSSGRenderTextureFilterOp::Linear;
805 texProp.mipFilterType = tex->generateMipmaps() ? (tex->mipFilter() == QQuick3DTexture::Nearest ? QSSGRenderTextureFilterOp::Nearest
806 : QSSGRenderTextureFilterOp::Linear)
807 : QSSGRenderTextureFilterOp::None;
808 texProp.horizontalClampType = tex->horizontalTiling() == QQuick3DTexture::Repeat ? QSSGRenderTextureCoordOp::Repeat
809 : (tex->horizontalTiling() == QQuick3DTexture::ClampToEdge ? QSSGRenderTextureCoordOp::ClampToEdge
810 : QSSGRenderTextureCoordOp::MirroredRepeat);
811 texProp.verticalClampType = tex->verticalTiling() == QQuick3DTexture::Repeat ? QSSGRenderTextureCoordOp::Repeat
812 : (tex->verticalTiling() == QQuick3DTexture::ClampToEdge ? QSSGRenderTextureCoordOp::ClampToEdge
813 : QSSGRenderTextureCoordOp::MirroredRepeat);
814 texProp.zClampType = tex->depthTiling() == QQuick3DTexture::Repeat ? QSSGRenderTextureCoordOp::Repeat
815 : (tex->depthTiling() == QQuick3DTexture::ClampToEdge) ? QSSGRenderTextureCoordOp::ClampToEdge
816 : QSSGRenderTextureCoordOp::MirroredRepeat;
846 if (tex && !tex->hasSourceData()) {
847 multiViewDependentSamplers.append({ QByteArrayLiteral(
"sampler2D"), name });
849 if (tex && QQuick3DObjectPrivate::get(tex)->type == QQuick3DObjectPrivate::Type::ImageCube)
850 uniforms.append({ QByteArrayLiteral(
"samplerCube"), name });
851 else if (tex && tex->textureData() && tex->textureData()->depth() > 0)
852 uniforms.append({ QByteArrayLiteral(
"sampler3D"), name });
854 uniforms.append({ QByteArrayLiteral(
"sampler2D"), name });
857 effectNode->textureProperties.push_back(texProp);
861 for (
const auto &property : std::as_const(textureProperties))
862 processTextureProperty(*property.first, property.second);
864 if (effectNode->incompleteBuildTimeObject) {
865 const auto names = dynamicPropertyNames();
866 for (
const auto &name : names) {
867 QVariant propValue = property(name.constData());
868 QMetaType propType = propValue.metaType();
869 if (propType == QMetaType(QMetaType::QVariant))
870 propType = propValue.metaType();
872 if (propType.id() >= QMetaType::User) {
873 if (propType.id() == qMetaTypeId<QQuick3DShaderUtilsTextureInput *>()) {
874 if (QQuick3DShaderUtilsTextureInput *texture = propValue.value<QQuick3DShaderUtilsTextureInput *>())
875 textureProperties.push_back({texture, name});
877 }
else if (propType.id() == QMetaType::QObjectStar) {
878 if (QQuick3DShaderUtilsTextureInput *texture = qobject_cast<QQuick3DShaderUtilsTextureInput *>(propValue.value<QObject *>()))
879 textureProperties.push_back({texture, name});
881 const auto type = uniformType(propType);
882 if (type != QSSGRenderShaderValue::Unknown) {
883 uniforms.append({ uniformTypeName(propType), name });
884 effectNode->properties.push_back({ name, uniformTypeName(propType),
885 propValue, uniformType(propType), -1 });
890 qWarning(
"No known uniform conversion found for effect property %s. Skipping", name.constData());
895 for (
const auto &property : std::as_const(textureProperties))
896 processTextureProperty(*property.first, property.second);
900 uniforms.append({
"mat4",
"qt_modelViewProjection" });
901 uniforms.append({
"vec2",
"qt_inputSize" });
902 uniforms.append({
"vec2",
"qt_outputSize" });
903 uniforms.append({
"float",
"qt_frame_num" });
904 uniforms.append({
"float",
"qt_fps" });
905 uniforms.append({
"vec2",
"qt_cameraProperties" });
906 uniforms.append({
"float",
"qt_normalAdjustViewportFactor" });
907 uniforms.append({
"float",
"qt_nearClipValue" });
908 uniforms.append({
"vec4",
"qt_rhi_properties" });
913 QSSGShaderCustomMaterialAdapter::StringPairList builtinVertexInputs;
914 builtinVertexInputs.append({
"vec3",
"attr_pos" });
915 builtinVertexInputs.append({
"vec2",
"attr_uv" });
917 QSSGShaderCustomMaterialAdapter::StringPairList builtinVertexOutputs;
918 builtinVertexOutputs.append({
"vec2",
"qt_inputUV" });
919 builtinVertexOutputs.append({
"vec2",
"qt_textureUV" });
920 builtinVertexOutputs.append({
"flat uint",
"qt_viewIndex" });
924 resetShaderDependentEffectFlags(effectNode);
926 if (!m_passes.isEmpty()) {
927 const QQmlContext *context = qmlContext(
this);
928 effectNode->resetCommands();
929 for (QQuick3DShaderUtilsRenderPass *pass : std::as_const(m_passes)) {
936 QByteArray shaderPathKey(
"effect pipeline--");
937 QSSGRenderEffect::ShaderPrepPassData passData;
938 for (QQuick3DShaderUtilsShader::Stage stage : { QQuick3DShaderUtilsShader::Stage::Vertex, QQuick3DShaderUtilsShader::Stage::Fragment }) {
939 QQuick3DShaderUtilsShader *shader =
nullptr;
940 for (QQuick3DShaderUtilsShader *s : pass->m_shaders) {
941 if (s->stage == stage) {
948 QSSGShaderCache::ShaderType type = QSSGShaderCache::ShaderType::Vertex;
949 if (stage == QQuick3DShaderUtilsShader::Stage::Fragment)
950 type = QSSGShaderCache::ShaderType::Fragment;
973 code = QSSGShaderUtils::resolveShader(shader->shader, context, shaderPathKey);
975 if (!shaderPathKey.isEmpty())
976 shaderPathKey.append(
'>');
977 shaderPathKey +=
"DEFAULT";
978 if (type == QSSGShaderCache::ShaderType::Vertex)
979 code = default_effect_vertex_shader;
981 code = default_effect_fragment_shader;
984 for (
auto pathKeyIndex : { QSSGRenderCustomMaterial::RegularShaderPathKeyIndex, QSSGRenderCustomMaterial::MultiViewShaderPathKeyIndex }) {
985 QSSGShaderCustomMaterialAdapter::ShaderCodeAndMetaData result;
986 QSSGShaderCustomMaterialAdapter::CustomShaderPrepWorkData scratch;
988 QSSGShaderCustomMaterialAdapter::beginPrepareCustomShader(
993 pathKeyIndex == QSSGRenderCustomMaterial::RegularShaderPathKeyIndex ?
false :
true);
995 QSSGShaderCustomMaterialAdapter::StringPairList multiViewDependentUniforms;
996 if (result.second.flags.testFlag(QSSGCustomShaderMetaData::UsesProjectionMatrix)
997 || result.second.flags.testFlag(QSSGCustomShaderMetaData::UsesInverseProjectionMatrix))
999 multiViewDependentUniforms.append({
"mat4",
"qt_projectionMatrix" });
1000 multiViewDependentUniforms.append({
"mat4",
"qt_inverseProjectionMatrix" });
1003 multiViewDependentUniforms.append({
"mat4",
"qt_viewMatrix" });
1005 accumulateEffectFlagsFromShader(effectNode, result.second);
1007 QSSGShaderCustomMaterialAdapter::finishPrepareCustomShader(
1012 pathKeyIndex == QSSGRenderCustomMaterial::RegularShaderPathKeyIndex ?
false :
true,
1014 type == QSSGShaderCache::ShaderType::Vertex ? builtinVertexInputs : builtinVertexOutputs,
1015 type == QSSGShaderCache::ShaderType::Vertex ? builtinVertexOutputs : QSSGShaderCustomMaterialAdapter::StringPairList(),
1016 multiViewDependentSamplers,
1017 multiViewDependentUniforms);
1019 if (type == QSSGShaderCache::ShaderType::Vertex) {
1021 insertVertexMainArgs(result.first);
1022 passData.vertexShaderCode[pathKeyIndex] = result.first;
1023 passData.vertexMetaData[pathKeyIndex] = result.second;
1025 passData.fragmentShaderCode[pathKeyIndex] = result.first;
1026 passData.fragmentMetaData[pathKeyIndex] = result.second;
1031 effectNode->commands.push_back(
nullptr);
1032 passData.bindShaderCmdIndex = effectNode->commands.size() - 1;
1035 passData.shaderPathKeyPrefix = shaderPathKey;
1036 effectNode->shaderPrepData.passes.append(passData);
1037 effectNode->shaderPrepData.valid =
true;
1039 effectNode->commands.push_back(
new QSSGApplyInstanceValue);
1042 QQuick3DShaderUtilsBuffer *outputBuffer = pass->outputBuffer;
1044 const QByteArray &outBufferName = outputBuffer->name;
1045 if (outBufferName.isEmpty()) {
1047 auto outputFormat = QQuick3DShaderUtilsBuffer::mapTextureFormat(outputBuffer->format());
1048 effectNode->commands.push_back(
new QSSGBindTarget(outputFormat));
1049 effectNode->outputFormat = outputFormat;
1052 effectNode->commands.push_back(outputBuffer->cloneCommand());
1053 connect(outputBuffer, &QQuick3DShaderUtilsBuffer::changed,
this, &QQuick3DEffect::onPassDirty, Qt::UniqueConnection);
1055 effectNode->commands.push_back(
new QSSGBindBuffer(outBufferName));
1059 effectNode->commands.push_back(
new QSSGBindTarget(QSSGRenderTextureFormat::Unknown));
1060 effectNode->outputFormat = QSSGRenderTextureFormat::Unknown;
1064 const auto &extraCommands = pass->m_commands;
1065 for (
const auto &command : extraCommands) {
1066 const int bufferCount = command->bufferCount();
1067 for (
int i = 0; i != bufferCount; ++i) {
1068 effectNode->commands.push_back(command->bufferAt(i)->cloneCommand());
1069 connect(command->bufferAt(i), &QQuick3DShaderUtilsBuffer::changed,
this, &QQuick3DEffect::onPassDirty, Qt::UniqueConnection);
1071 effectNode->commands.push_back(command->cloneCommand());
1074 effectNode->commands.push_back(
new QSSGRender);
1079 if (m_dirtyAttributes & Dirty::PropertyDirty) {
1080 for (
const auto &prop : std::as_const(effectNode->properties)) {
1081 auto p = metaObject()->property(prop.pid);
1082 if (Q_LIKELY(p.isValid()))
1083 prop.value = p.read(
this);
1087 m_dirtyAttributes = 0;
1089 DebugViewHelpers::ensureDebugObjectName(effectNode,
this);