709QSSGRenderGraphObject *QQuick3DEffect::updateSpatialNode(QSSGRenderGraphObject *node)
711 using namespace QSSGShaderUtils;
713 const auto &renderContext = QQuick3DObjectPrivate::get(
this)->sceneManager->wattached->rci();
714 if (!renderContext) {
715 qWarning(
"QQuick3DEffect: No render context interface?");
719 QSSGRenderEffect *effectNode =
static_cast<QSSGRenderEffect *>(node);
720 bool newBackendNode =
false;
722 effectNode =
new QSSGRenderEffect;
723 newBackendNode =
true;
726 bool shadersOrBuffersMayChange =
false;
727 if (m_dirtyAttributes & Dirty::EffectChainDirty)
728 shadersOrBuffersMayChange =
true;
730 const bool fullUpdate = newBackendNode || effectNode->incompleteBuildTimeObject || (m_dirtyAttributes & Dirty::TextureDirty);
732 if (fullUpdate || shadersOrBuffersMayChange) {
736 effectNode->properties.clear();
737 effectNode->textureProperties.clear();
739 QMetaMethod propertyDirtyMethod;
740 const int idx = metaObject()->indexOfSlot(
"onPropertyDirty()");
742 propertyDirtyMethod = metaObject()->method(idx);
745 QSSGShaderCustomMaterialAdapter::StringPairList uniforms;
746 QSSGShaderCustomMaterialAdapter::StringPairList multiViewDependentSamplers;
747 const int propCount = metaObject()->propertyCount();
748 int propOffset = metaObject()->propertyOffset();
751 const QMetaObject *superClass = metaObject()->superClass();
752 while (superClass && qstrcmp(superClass->className(),
"QQuick3DEffect") != 0) {
753 propOffset = superClass->propertyOffset();
754 superClass = superClass->superClass();
757 using TextureInputProperty = QPair<QQuick3DShaderUtilsTextureInput *,
const char *>;
759 QVector<TextureInputProperty> textureProperties;
760 for (
int i = propOffset; i != propCount; ++i) {
761 const QMetaProperty property = metaObject()->property(i);
762 if (Q_UNLIKELY(!property.isValid()))
765 const auto name = property.name();
766 QMetaType propType = property.metaType();
767 QVariant propValue = property.read(
this);
768 if (propType == QMetaType(QMetaType::QVariant))
769 propType = propValue.metaType();
771 if (propType.id() >= QMetaType::User) {
772 if (propType.id() == qMetaTypeId<QQuick3DShaderUtilsTextureInput *>()) {
773 if (QQuick3DShaderUtilsTextureInput *texture = property.read(
this).value<QQuick3DShaderUtilsTextureInput *>())
774 textureProperties.push_back({texture, name});
776 }
else if (propType == QMetaType(QMetaType::QObjectStar)) {
777 if (QQuick3DShaderUtilsTextureInput *texture = qobject_cast<QQuick3DShaderUtilsTextureInput *>(propValue.value<QObject *>()))
778 textureProperties.push_back({texture, name});
780 const auto type = uniformType(propType);
781 if (type != QSSGRenderShaderValue::Unknown) {
782 uniforms.append({ uniformTypeName(propType), name });
783 effectNode->properties.push_back({ name, uniformTypeName(propType),
784 propValue, uniformType(propType), i});
787 if (property.hasNotifySignal() && propertyDirtyMethod.isValid())
788 connect(
this, property.notifySignal(),
this, propertyDirtyMethod);
798 const auto processTextureProperty = [&](QQuick3DShaderUtilsTextureInput &texture,
const QByteArray &name) {
799 QSSGRenderEffect::TextureProperty texProp;
800 QQuick3DTexture *tex = texture.texture();
802 connect(&texture, &QQuick3DShaderUtilsTextureInput::enabledChanged,
this, &QQuick3DEffect::onTextureDirty);
803 connect(&texture, &QQuick3DShaderUtilsTextureInput::textureChanged,
this, &QQuick3DEffect::onTextureDirty);
806 if (texture.enabled && tex)
807 texProp.texImage = tex->getRenderImage();
809 texProp.shaderDataType = QSSGRenderShaderValue::Texture;
812 texProp.minFilterType = tex->minFilter() == QQuick3DTexture::Nearest ? QSSGRenderTextureFilterOp::Nearest
813 : QSSGRenderTextureFilterOp::Linear;
814 texProp.magFilterType = tex->magFilter() == QQuick3DTexture::Nearest ? QSSGRenderTextureFilterOp::Nearest
815 : QSSGRenderTextureFilterOp::Linear;
816 texProp.mipFilterType = tex->generateMipmaps() ? (tex->mipFilter() == QQuick3DTexture::Nearest ? QSSGRenderTextureFilterOp::Nearest
817 : QSSGRenderTextureFilterOp::Linear)
818 : QSSGRenderTextureFilterOp::None;
819 texProp.horizontalClampType = tex->horizontalTiling() == QQuick3DTexture::Repeat ? QSSGRenderTextureCoordOp::Repeat
820 : (tex->horizontalTiling() == QQuick3DTexture::ClampToEdge ? QSSGRenderTextureCoordOp::ClampToEdge
821 : QSSGRenderTextureCoordOp::MirroredRepeat);
822 texProp.verticalClampType = tex->verticalTiling() == QQuick3DTexture::Repeat ? QSSGRenderTextureCoordOp::Repeat
823 : (tex->verticalTiling() == QQuick3DTexture::ClampToEdge ? QSSGRenderTextureCoordOp::ClampToEdge
824 : QSSGRenderTextureCoordOp::MirroredRepeat);
825 texProp.zClampType = tex->depthTiling() == QQuick3DTexture::Repeat ? QSSGRenderTextureCoordOp::Repeat
826 : (tex->depthTiling() == QQuick3DTexture::ClampToEdge) ? QSSGRenderTextureCoordOp::ClampToEdge
827 : QSSGRenderTextureCoordOp::MirroredRepeat;
857 if (tex && !tex->hasSourceData()) {
858 multiViewDependentSamplers.append({ QByteArrayLiteral(
"sampler2D"), name });
860 if (tex && QQuick3DObjectPrivate::get(tex)->type == QQuick3DObjectPrivate::Type::ImageCube)
861 uniforms.append({ QByteArrayLiteral(
"samplerCube"), name });
862 else if (tex && tex->textureData() && tex->textureData()->depth() > 0)
863 uniforms.append({ QByteArrayLiteral(
"sampler3D"), name });
865 uniforms.append({ QByteArrayLiteral(
"sampler2D"), name });
868 effectNode->textureProperties.push_back(texProp);
872 for (
const auto &property : std::as_const(textureProperties))
873 processTextureProperty(*property.first, property.second);
875 if (effectNode->incompleteBuildTimeObject) {
876 const auto names = dynamicPropertyNames();
877 for (
const auto &name : names) {
878 QVariant propValue = property(name.constData());
879 QMetaType propType = propValue.metaType();
880 if (propType == QMetaType(QMetaType::QVariant))
881 propType = propValue.metaType();
883 if (propType.id() >= QMetaType::User) {
884 if (propType.id() == qMetaTypeId<QQuick3DShaderUtilsTextureInput *>()) {
885 if (QQuick3DShaderUtilsTextureInput *texture = propValue.value<QQuick3DShaderUtilsTextureInput *>())
886 textureProperties.push_back({texture, name});
888 }
else if (propType.id() == QMetaType::QObjectStar) {
889 if (QQuick3DShaderUtilsTextureInput *texture = qobject_cast<QQuick3DShaderUtilsTextureInput *>(propValue.value<QObject *>()))
890 textureProperties.push_back({texture, name});
892 const auto type = uniformType(propType);
893 if (type != QSSGRenderShaderValue::Unknown) {
894 uniforms.append({ uniformTypeName(propType), name });
895 effectNode->properties.push_back({ name, uniformTypeName(propType),
896 propValue, uniformType(propType), -1 });
901 qWarning(
"No known uniform conversion found for effect property %s. Skipping", name.constData());
906 for (
const auto &property : std::as_const(textureProperties))
907 processTextureProperty(*property.first, property.second);
911 uniforms.append({
"mat4",
"qt_modelViewProjection" });
912 uniforms.append({
"vec2",
"qt_inputSize" });
913 uniforms.append({
"vec2",
"qt_outputSize" });
914 uniforms.append({
"float",
"qt_frame_num" });
915 uniforms.append({
"float",
"qt_fps" });
916 uniforms.append({
"vec2",
"qt_cameraProperties" });
917 uniforms.append({
"float",
"qt_normalAdjustViewportFactor" });
918 uniforms.append({
"float",
"qt_nearClipValue" });
919 uniforms.append({
"vec4",
"qt_rhi_properties" });
924 QSSGShaderCustomMaterialAdapter::StringPairList builtinVertexInputs;
925 builtinVertexInputs.append({
"vec3",
"attr_pos" });
926 builtinVertexInputs.append({
"vec2",
"attr_uv" });
928 QSSGShaderCustomMaterialAdapter::StringPairList builtinVertexOutputs;
929 builtinVertexOutputs.append({
"vec2",
"qt_inputUV" });
930 builtinVertexOutputs.append({
"vec2",
"qt_textureUV" });
931 builtinVertexOutputs.append({
"flat uint",
"qt_viewIndex" });
935 resetShaderDependentEffectFlags(effectNode);
937 if (!m_passes.isEmpty()) {
938 const QQmlContext *context = qmlContext(
this);
939 effectNode->resetCommands();
940 for (QQuick3DShaderUtilsRenderPass *pass : std::as_const(m_passes)) {
947 QByteArray shaderPathKey(
"effect pipeline--");
948 QSSGRenderEffect::ShaderPrepPassData passData;
949 for (QQuick3DShaderUtilsShader::Stage stage : { QQuick3DShaderUtilsShader::Stage::Vertex, QQuick3DShaderUtilsShader::Stage::Fragment }) {
950 QQuick3DShaderUtilsShader *shader =
nullptr;
951 for (QQuick3DShaderUtilsShader *s : pass->m_shaders) {
952 if (s->stage == stage) {
959 QSSGShaderCache::ShaderType type = QSSGShaderCache::ShaderType::Vertex;
960 if (stage == QQuick3DShaderUtilsShader::Stage::Fragment)
961 type = QSSGShaderCache::ShaderType::Fragment;
984 code = QSSGShaderUtils::resolveShader(shader->shader, context, shaderPathKey);
986 if (!shaderPathKey.isEmpty())
987 shaderPathKey.append(
'>');
988 shaderPathKey +=
"DEFAULT";
989 if (type == QSSGShaderCache::ShaderType::Vertex)
990 code = default_effect_vertex_shader;
992 code = default_effect_fragment_shader;
995 for (
auto pathKeyIndex : { QSSGRenderCustomMaterial::RegularShaderPathKeyIndex, QSSGRenderCustomMaterial::MultiViewShaderPathKeyIndex }) {
996 QSSGShaderCustomMaterialAdapter::ShaderCodeAndMetaData result;
997 QSSGShaderCustomMaterialAdapter::CustomShaderPrepWorkData scratch;
999 QSSGShaderCustomMaterialAdapter::beginPrepareCustomShader(
1004 pathKeyIndex == QSSGRenderCustomMaterial::RegularShaderPathKeyIndex ?
false :
true);
1006 QSSGShaderCustomMaterialAdapter::StringPairList multiViewDependentUniforms;
1007 if (result.second.flags.testFlag(QSSGCustomShaderMetaData::UsesProjectionMatrix)
1008 || result.second.flags.testFlag(QSSGCustomShaderMetaData::UsesInverseProjectionMatrix))
1010 multiViewDependentUniforms.append({
"mat4",
"qt_projectionMatrix" });
1011 multiViewDependentUniforms.append({
"mat4",
"qt_inverseProjectionMatrix" });
1014 multiViewDependentUniforms.append({
"mat4",
"qt_viewMatrix" });
1016 accumulateEffectFlagsFromShader(effectNode, result.second);
1018 QSSGShaderCustomMaterialAdapter::finishPrepareCustomShader(
1023 pathKeyIndex == QSSGRenderCustomMaterial::RegularShaderPathKeyIndex ?
false :
true,
1025 type == QSSGShaderCache::ShaderType::Vertex ? builtinVertexInputs : builtinVertexOutputs,
1026 type == QSSGShaderCache::ShaderType::Vertex ? builtinVertexOutputs : QSSGShaderCustomMaterialAdapter::StringPairList(),
1027 multiViewDependentSamplers,
1028 multiViewDependentUniforms);
1030 if (type == QSSGShaderCache::ShaderType::Vertex) {
1032 insertVertexMainArgs(result.first);
1033 passData.vertexShaderCode[pathKeyIndex] = result.first;
1034 passData.vertexMetaData[pathKeyIndex] = result.second;
1036 passData.fragmentShaderCode[pathKeyIndex] = result.first;
1037 passData.fragmentMetaData[pathKeyIndex] = result.second;
1042 effectNode->commands.push_back(
nullptr);
1043 passData.bindShaderCmdIndex = effectNode->commands.size() - 1;
1046 passData.shaderPathKeyPrefix = shaderPathKey;
1047 effectNode->shaderPrepData.passes.append(passData);
1048 effectNode->shaderPrepData.valid =
true;
1050 effectNode->commands.push_back(
new QSSGApplyInstanceValue);
1053 QQuick3DShaderUtilsBuffer *outputBuffer = pass->outputBuffer;
1055 const QByteArray &outBufferName = outputBuffer->name;
1056 if (outBufferName.isEmpty()) {
1058 auto outputFormat = QQuick3DShaderUtilsBuffer::mapTextureFormat(outputBuffer->format());
1059 effectNode->commands.push_back(
new QSSGBindTarget(outputFormat));
1060 effectNode->outputFormat = outputFormat;
1063 effectNode->commands.push_back(outputBuffer->cloneCommand());
1064 connect(outputBuffer, &QQuick3DShaderUtilsBuffer::changed,
this, &QQuick3DEffect::onPassDirty, Qt::UniqueConnection);
1066 effectNode->commands.push_back(
new QSSGBindBuffer(outBufferName));
1070 effectNode->commands.push_back(
new QSSGBindTarget(QSSGRenderTextureFormat::Unknown));
1071 effectNode->outputFormat = QSSGRenderTextureFormat::Unknown;
1075 const auto &extraCommands = pass->m_commands;
1076 for (
const auto &command : extraCommands) {
1077 const int bufferCount = command->bufferCount();
1078 for (
int i = 0; i != bufferCount; ++i) {
1079 effectNode->commands.push_back(command->bufferAt(i)->cloneCommand());
1080 connect(command->bufferAt(i), &QQuick3DShaderUtilsBuffer::changed,
this, &QQuick3DEffect::onPassDirty, Qt::UniqueConnection);
1082 effectNode->commands.push_back(command->cloneCommand());
1085 effectNode->commands.push_back(
new QSSGRender);
1090 if (m_dirtyAttributes & Dirty::PropertyDirty) {
1091 for (
const auto &prop : std::as_const(effectNode->properties)) {
1092 auto p = metaObject()->property(prop.pid);
1093 if (Q_LIKELY(p.isValid()))
1094 prop.value = p.read(
this);
1098 m_dirtyAttributes = 0;
1100 DebugViewHelpers::ensureDebugObjectName(effectNode,
this);