forked from qt-creator/qt-creator
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:
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -67,4 +67,9 @@ void EffectMakerUniformsModel::addUniform(Uniform *uniform)
|
||||
endInsertRows();
|
||||
}
|
||||
|
||||
QList<Uniform *> EffectMakerUniformsModel::uniforms() const
|
||||
{
|
||||
return m_uniforms;
|
||||
}
|
||||
|
||||
} // namespace QmlDesigner
|
||||
|
@@ -25,6 +25,8 @@ public:
|
||||
|
||||
void addUniform(Uniform *uniform);
|
||||
|
||||
QList<Uniform *> uniforms() const;
|
||||
|
||||
private:
|
||||
enum Roles {
|
||||
NameRole = Qt::UserRole + 1,
|
||||
|
@@ -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
|
@@ -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
|
@@ -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;
|
||||
|
@@ -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();
|
||||
|
Reference in New Issue
Block a user