QmlDesigner: Add core uniforms and features for shaders

Task-number: QDS-10499
Change-Id: I12b39d29133accd012ac019def0c4a34ee84d8e5
Reviewed-by: Miikka Heikkinen <miikka.heikkinen@qt.io>
Reviewed-by: Mahmoud Badri <mahmoud.badri@qt.io>
This commit is contained in:
Amr Essam
2023-08-30 13:43:24 +03:00
committed by Amr Elsayed
parent 2f2d8bf9e8
commit a5a5b00f58
10 changed files with 254 additions and 2 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -4,6 +4,7 @@
#include "effectmakermodel.h"
#include "compositionnode.h"
#include "uniform.h"
#include <utils/qtcassert.h>
@@ -54,4 +55,101 @@ void EffectMakerModel::removeNode(int idx)
endRemoveRows();
}
const QList<Uniform *> EffectMakerModel::allUniforms()
{
QList<Uniform *> uniforms = {};
for (const auto &node : std::as_const(m_nodes))
uniforms.append(static_cast<EffectMakerUniformsModel *>(node->uniformsModel())->uniforms());
return uniforms;
}
const QString EffectMakerModel::getBufUniform()
{
QList<Uniform *> 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<Uniform *> 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

View File

@@ -3,12 +3,15 @@
#pragma once
#include "shaderfeatures.h"
#include <QMap>
#include <QStandardItemModel>
namespace QmlDesigner {
class CompositionNode;
class Uniform;
class EffectMakerModel : public QAbstractListModel
{
@@ -42,10 +45,18 @@ private:
bool isValidIndex(int idx) const;
const QList<Uniform *> allUniforms();
const QString getBufUniform();
const QString getVSUniforms();
const QString getFSUniforms();
QList<CompositionNode *> m_nodes;
int m_selectedIndex = -1;
bool m_isEmpty = true;
ShaderFeatures m_shaderFeatures;
};
} // namespace QmlDesigner

View File

@@ -67,4 +67,9 @@ void EffectMakerUniformsModel::addUniform(Uniform *uniform)
endInsertRows();
}
QList<Uniform *> EffectMakerUniformsModel::uniforms() const
{
return m_uniforms;
}
} // namespace QmlDesigner

View File

@@ -25,6 +25,8 @@ public:
void addUniform(Uniform *uniform);
QList<Uniform *> uniforms() const;
private:
enum Roles {
NameRole = Qt::UserRole + 1,

View File

@@ -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 <QStringList>
#include <QDebug>
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

View File

@@ -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 <QFlags>
#include <QString>
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

View File

@@ -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;

View File

@@ -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();