From a5a5b00f5865d057fc9eeb9121ca84fc160ab074 Mon Sep 17 00:00:00 2001 From: Amr Essam Date: Wed, 30 Aug 2023 13:43:24 +0300 Subject: [PATCH] QmlDesigner: Add core uniforms and features for shaders Task-number: QDS-10499 Change-Id: I12b39d29133accd012ac019def0c4a34ee84d8e5 Reviewed-by: Miikka Heikkinen Reviewed-by: Mahmoud Badri --- src/plugins/qmldesigner/CMakeLists.txt | 1 + .../effectmaker/compositionnode.cpp | 15 +++ .../effectmaker/effectmakermodel.cpp | 98 +++++++++++++++++++ .../components/effectmaker/effectmakermodel.h | 11 +++ .../effectmaker/effectmakeruniformsmodel.cpp | 5 + .../effectmaker/effectmakeruniformsmodel.h | 2 + .../components/effectmaker/shaderfeatures.cpp | 80 +++++++++++++++ .../components/effectmaker/shaderfeatures.h | 39 ++++++++ .../components/effectmaker/uniform.cpp | 4 +- .../components/effectmaker/uniform.h | 1 + 10 files changed, 254 insertions(+), 2 deletions(-) create mode 100644 src/plugins/qmldesigner/components/effectmaker/shaderfeatures.cpp create mode 100644 src/plugins/qmldesigner/components/effectmaker/shaderfeatures.h diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 7f94c8ff256..cd80c894ea6 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -723,6 +723,7 @@ extend_qtc_plugin(QmlDesigner uniform.cpp uniform.h effectutils.cpp effectutils.h effectmakercontextobject.cpp effectmakercontextobject.h + shaderfeatures.cpp shaderfeatures.h ) extend_qtc_plugin(QmlDesigner diff --git a/src/plugins/qmldesigner/components/effectmaker/compositionnode.cpp b/src/plugins/qmldesigner/components/effectmaker/compositionnode.cpp index 0414d3ef44c..eb2e7b3b322 100644 --- a/src/plugins/qmldesigner/components/effectmaker/compositionnode.cpp +++ b/src/plugins/qmldesigner/components/effectmaker/compositionnode.cpp @@ -84,6 +84,21 @@ void CompositionNode::parse(const QString &qenPath) QJsonArray jsonProps = json.value("properties").toArray(); for (const auto /*QJsonValueRef*/ &prop : jsonProps) m_unifomrsModel.addUniform(new Uniform(prop.toObject())); + + // Seek through code to get tags + QStringList shaderCodeLines; + shaderCodeLines += m_vertexCode.split('\n'); + shaderCodeLines += m_fragmentCode.split('\n'); + for (const QString &codeLine : std::as_const(shaderCodeLines)) { + QString trimmedLine = codeLine.trimmed(); + if (trimmedLine.startsWith("@requires")) { + // Get the required node, remove "@requires" + QString l = trimmedLine.sliced(9).trimmed(); + QString nodeName = trimmedLine.sliced(10); + if (!nodeName.isEmpty() && !m_requiredNodes.contains(nodeName)) + m_requiredNodes << nodeName; + } + } } } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.cpp b/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.cpp index 643eddc6b2f..228154fc6a4 100644 --- a/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.cpp +++ b/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.cpp @@ -4,6 +4,7 @@ #include "effectmakermodel.h" #include "compositionnode.h" +#include "uniform.h" #include @@ -54,4 +55,101 @@ void EffectMakerModel::removeNode(int idx) endRemoveRows(); } +const QList EffectMakerModel::allUniforms() +{ + QList uniforms = {}; + for (const auto &node : std::as_const(m_nodes)) + uniforms.append(static_cast(node->uniformsModel())->uniforms()); + return uniforms; +} + +const QString EffectMakerModel::getBufUniform() +{ + QList uniforms = allUniforms(); + QString s; + s += "layout(std140, binding = 0) uniform buf {\n"; + s += " mat4 qt_Matrix;\n"; + s += " float qt_Opacity;\n"; + if (m_shaderFeatures.enabled(ShaderFeatures::Time)) + s += " float iTime;\n"; + if (m_shaderFeatures.enabled(ShaderFeatures::Frame)) + s += " int iFrame;\n"; + if (m_shaderFeatures.enabled(ShaderFeatures::Resolution)) + s += " vec3 iResolution;\n"; + if (m_shaderFeatures.enabled(ShaderFeatures::Mouse)) + s += " vec4 iMouse;\n"; + for (const auto uniform : uniforms) { + // TODO: Check if uniform is already added. + if (uniform->type() != Uniform::Type::Sampler && uniform->type() != Uniform::Type::Define) { + QString type = Uniform::stringFromType(uniform->type()); + QString props = " " + type + " " + uniform->name() + ";\n"; + s += props; + } + } + s += "};\n"; + return s; +} + +const QString EffectMakerModel::getVSUniforms() +{ + QString s; + s += "#version 440\n"; + s += '\n'; + s += "layout(location = 0) in vec4 qt_Vertex;\n"; + s += "layout(location = 1) in vec2 qt_MultiTexCoord0;\n"; + s += "layout(location = 0) out vec2 texCoord;\n"; + if (m_shaderFeatures.enabled(ShaderFeatures::FragCoord)) + s += "layout(location = 1) out vec2 fragCoord;\n"; + s += '\n'; + s += getBufUniform(); + s += '\n'; + s += "out gl_PerVertex { vec4 gl_Position; };\n"; + s += '\n'; + return s; +} + +const QString EffectMakerModel::getFSUniforms() +{ + QList uniforms = allUniforms(); + QString s; + s += "#version 440\n"; + s += '\n'; + s += "layout(location = 0) in vec2 texCoord;\n"; + if (m_shaderFeatures.enabled(ShaderFeatures::FragCoord)) + s += "layout(location = 1) in vec2 fragCoord;\n"; + s += "layout(location = 0) out vec4 fragColor;\n"; + s += '\n'; + s += getBufUniform(); + s += '\n'; + + bool usesSource = m_shaderFeatures.enabled(ShaderFeatures::Source); + if (usesSource) + s += "layout(binding = 1) uniform sampler2D iSource;\n"; + + // Add sampler uniforms + int bindingIndex = usesSource ? 2 : 1; + for (const auto uniform : uniforms) { + // TODO: Check if uniform is already added. + if (uniform->type() == Uniform::Type::Sampler) { + // Start index from 2, 1 is source item + QString props = QString("layout(binding = %1) uniform sampler2D %2") + .arg(bindingIndex).arg(uniform->name()); + s += props + ";\n"; + bindingIndex++; + } + } + s += '\n'; + if (m_shaderFeatures.enabled(ShaderFeatures::BlurSources)) { + const int blurItems = 5; + for (int i = 1; i <= blurItems; i++) { + QString props = QString("layout(binding = %1) uniform sampler2D iSourceBlur%2") + .arg(bindingIndex).arg(QString::number(i)); + s += props + ";\n"; + bindingIndex++; + } + s += '\n'; + } + return s; +} + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.h b/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.h index 4193b918f22..de26b2f38f3 100644 --- a/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.h +++ b/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.h @@ -3,12 +3,15 @@ #pragma once +#include "shaderfeatures.h" + #include #include namespace QmlDesigner { class CompositionNode; +class Uniform; class EffectMakerModel : public QAbstractListModel { @@ -42,10 +45,18 @@ private: bool isValidIndex(int idx) const; + const QList allUniforms(); + + const QString getBufUniform(); + const QString getVSUniforms(); + const QString getFSUniforms(); + QList m_nodes; int m_selectedIndex = -1; bool m_isEmpty = true; + + ShaderFeatures m_shaderFeatures; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakeruniformsmodel.cpp b/src/plugins/qmldesigner/components/effectmaker/effectmakeruniformsmodel.cpp index 8bd3926ef1c..dac01905b67 100644 --- a/src/plugins/qmldesigner/components/effectmaker/effectmakeruniformsmodel.cpp +++ b/src/plugins/qmldesigner/components/effectmaker/effectmakeruniformsmodel.cpp @@ -67,4 +67,9 @@ void EffectMakerUniformsModel::addUniform(Uniform *uniform) endInsertRows(); } +QList EffectMakerUniformsModel::uniforms() const +{ + return m_uniforms; +} + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakeruniformsmodel.h b/src/plugins/qmldesigner/components/effectmaker/effectmakeruniformsmodel.h index ecaa8752d65..1d69d6d1b27 100644 --- a/src/plugins/qmldesigner/components/effectmaker/effectmakeruniformsmodel.h +++ b/src/plugins/qmldesigner/components/effectmaker/effectmakeruniformsmodel.h @@ -25,6 +25,8 @@ public: void addUniform(Uniform *uniform); + QList uniforms() const; + private: enum Roles { NameRole = Qt::UserRole + 1, diff --git a/src/plugins/qmldesigner/components/effectmaker/shaderfeatures.cpp b/src/plugins/qmldesigner/components/effectmaker/shaderfeatures.cpp new file mode 100644 index 00000000000..755b203d23c --- /dev/null +++ b/src/plugins/qmldesigner/components/effectmaker/shaderfeatures.cpp @@ -0,0 +1,80 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "shaderfeatures.h" +#include +#include + +namespace QmlDesigner { + +ShaderFeatures::ShaderFeatures() +{ +} + +// Browse the shaders and check which features are used in them. +void ShaderFeatures::update(const QString &vs, const QString &fs, const QString &qml) +{ + QStringList vsList = vs.split("\n"); + QStringList fsList = fs.split("\n"); + + const QStringList code = vsList + fsList; + Features newFeatures = {}; + m_gridMeshWidth = 1; + m_gridMeshHeight = 1; + for (const QString &line : code) + checkLine(line, newFeatures); + + // iTime may also be used in QML side, without being used in shaders. + // In this case enable the time helpers creation. + if (qml.contains("iTime")) + newFeatures.setFlag(Time, true); + + if (newFeatures != m_enabledFeatures) + m_enabledFeatures = newFeatures; +} + +bool ShaderFeatures::enabled(ShaderFeatures::Feature feature) const +{ + return m_enabledFeatures.testFlag(feature); +} + +void ShaderFeatures::checkLine(const QString &line, Features &features) +{ + if (line.contains("iTime")) + features.setFlag(Time, true); + + if (line.contains("iFrame")) + features.setFlag(Frame, true); + + if (line.contains("iResolution")) + features.setFlag(Resolution, true); + + if (line.contains("iSource")) + features.setFlag(Source, true); + + if (line.contains("iMouse")) + features.setFlag(Mouse, true); + + if (line.contains("fragCoord")) + features.setFlag(FragCoord, true); + + if (line.contains("@mesh")) { + // Get the mesh size, remove "@mesh" + QString l = line.trimmed().sliced(5); + QStringList list = l.split(QLatin1Char(',')); + if (list.size() >= 2) { + int w = list.at(0).trimmed().toInt(); + int h = list.at(1).trimmed().toInt(); + // Set size to max values + m_gridMeshWidth = std::max(m_gridMeshWidth, w); + m_gridMeshHeight = std::max(m_gridMeshHeight, h); + } + // If is bigger than default (1, 1), set the feature + if (m_gridMeshWidth > 1 || m_gridMeshHeight > 1) + features.setFlag(GridMesh, true); + } + if (line.contains("@blursources")) + features.setFlag(BlurSources, true); +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/effectmaker/shaderfeatures.h b/src/plugins/qmldesigner/components/effectmaker/shaderfeatures.h new file mode 100644 index 00000000000..35fb507066d --- /dev/null +++ b/src/plugins/qmldesigner/components/effectmaker/shaderfeatures.h @@ -0,0 +1,39 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#pragma once + +#include +#include + +namespace QmlDesigner { + +class ShaderFeatures +{ +public: + enum Feature { + Time = 1 << 0, + Frame = 1 << 1, + Resolution = 1 << 2, + Source = 1 << 3, + Mouse = 1 << 4, + FragCoord = 1 << 5, + GridMesh = 1 << 6, + BlurSources = 1 << 7 + }; + Q_DECLARE_FLAGS(Features, Feature) + + ShaderFeatures(); + void update(const QString &vs, const QString &fs, const QString &qml); + + bool enabled(ShaderFeatures::Feature feature) const; + +private: + void checkLine(const QString &line, ShaderFeatures::Features &features); + ShaderFeatures::Features m_enabledFeatures; + int m_gridMeshWidth = 1; + int m_gridMeshHeight = 1; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(ShaderFeatures::Features) +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/effectmaker/uniform.cpp b/src/plugins/qmldesigner/components/effectmaker/uniform.cpp index f83cf26881f..e15cbb885ca 100644 --- a/src/plugins/qmldesigner/components/effectmaker/uniform.cpp +++ b/src/plugins/qmldesigner/components/effectmaker/uniform.cpp @@ -261,7 +261,7 @@ QString Uniform::stringFromType(Uniform::Type type) else if (type == Type::Color) return "color"; else if (type == Type::Sampler) - return "image"; + return "sampler2D"; else if (type == Type::Define) return "define"; @@ -285,7 +285,7 @@ Uniform::Type Uniform::typeFromString(const QString &typeString) return Uniform::Type::Vec4; else if (typeString == "color") return Uniform::Type::Color; - else if (typeString == "image") + else if (typeString == "sampler2D") return Uniform::Type::Sampler; else if (typeString == "define") return Uniform::Type::Define; diff --git a/src/plugins/qmldesigner/components/effectmaker/uniform.h b/src/plugins/qmldesigner/components/effectmaker/uniform.h index 761a22199f5..7117a7591de 100644 --- a/src/plugins/qmldesigner/components/effectmaker/uniform.h +++ b/src/plugins/qmldesigner/components/effectmaker/uniform.h @@ -68,6 +68,7 @@ public: static QString stringFromType(Uniform::Type type); static Uniform::Type typeFromString(const QString &typeString); + static QString typeToUniform(Uniform::Type type); signals: void uniformValueChanged();