QmlDesigner: Add shaders pre-compilation support functionality

Task-number: QDS-10499
Change-Id: Idc2722b5b9fea7e2914bae5a67ce289a27c236ae
Reviewed-by: Qt CI Patch Build Bot <ci_patchbuild_bot@qt.io>
Reviewed-by: Mahmoud Badri <mahmoud.badri@qt.io>
This commit is contained in:
Amr Essam
2023-09-01 16:43:56 +03:00
committed by Amr Elsayed
parent 5907e480a0
commit 5302567fca
4 changed files with 283 additions and 3 deletions

View File

@@ -44,6 +44,21 @@ QStringList CompositionNode::requiredNodes() const
return m_requiredNodes;
}
bool CompositionNode::isEnabled() const
{
return m_isEnabled;
}
void CompositionNode::setIsEnabled(bool newIsEnabled)
{
m_isEnabled = newIsEnabled;
}
CompositionNode::NodeType CompositionNode::type() const
{
return m_type;
}
void CompositionNode::parse(const QString &qenPath)
{
QFile qenFile(qenPath);

View File

@@ -17,6 +17,12 @@ class CompositionNode : public QObject
Q_PROPERTY(QObject *nodeUniformsModel READ uniformsModel NOTIFY uniformsModelChanged)
public:
enum NodeType {
SourceNode = 0,
DestinationNode,
CustomNode
};
CompositionNode(const QString &qenPath);
QString fragmentCode() const;
@@ -27,6 +33,11 @@ public:
QStringList requiredNodes() const;
NodeType type() const;
bool isEnabled() const;
void setIsEnabled(bool newIsEnabled);
signals:
void uniformsModelChanged();
@@ -34,10 +45,12 @@ private:
void parse(const QString &qenPath);
QString m_name;
NodeType m_type = CustomNode;
QString m_fragmentCode;
QString m_vertexCode;
QString m_description;
QStringList m_requiredNodes;
bool m_isEnabled;
EffectMakerUniformsModel m_unifomrsModel;
};

View File

@@ -112,7 +112,7 @@ const QString EffectMakerModel::getVSUniforms()
const QString EffectMakerModel::getFSUniforms()
{
QList<Uniform *> uniforms = allUniforms();
const QList<Uniform *> uniforms = allUniforms();
QString s;
s += "#version 440\n";
s += '\n';
@@ -227,4 +227,224 @@ void EffectMakerModel::resetEffectError(int type)
}
}
const QString EffectMakerModel::getDefineProperties()
{
// TODO
return QString();
}
const QString EffectMakerModel::getConstVariables()
{
// TODO
return QString();
}
int EffectMakerModel::getTagIndex(const QStringList &code, const QString &tag)
{
Q_UNUSED(code)
Q_UNUSED(tag)
// TODO
return 0;
}
QString EffectMakerModel::processVertexRootLine(const QString &line)
{
Q_UNUSED(line)
// TODO
return QString();
}
QString EffectMakerModel:: processFragmentRootLine(const QString &line)
{
Q_UNUSED(line)
// TODO
return QString();
}
QStringList EffectMakerModel::getDefaultRootVertexShader()
{
// TODO
return {};
}
QStringList EffectMakerModel::getDefaultRootFragmentShader()
{
// TODO
return {};
}
QStringList EffectMakerModel::removeTagsFromCode(const QStringList &codeLines)
{
Q_UNUSED(codeLines)
// TODO
return {};
}
QString EffectMakerModel::removeTagsFromCode(const QString &code)
{
Q_UNUSED(code)
// TODO
return QString();
}
QString EffectMakerModel::getCustomShaderVaryings(bool outState)
{
Q_UNUSED(outState)
// TODO
return QString();
}
QString EffectMakerModel::generateVertexShader(bool includeUniforms)
{
QString s;
if (includeUniforms)
s += getVSUniforms();
// Remove tags when not generating for features check
const bool removeTags = includeUniforms;
s += getDefineProperties();
s += getConstVariables();
// When the node is complete, add shader code in correct nodes order
// split to root and main parts
QString s_root;
QString s_main;
QStringList s_sourceCode;
m_shaderVaryingVariables.clear();
for (const CompositionNode *n : std::as_const(m_nodes)) {
if (!n->vertexCode().isEmpty() && n->isEnabled()) {
if (n->type() == CompositionNode::NodeType::SourceNode) {
s_sourceCode = n->vertexCode().split('\n');
} else if (n->type() == CompositionNode::NodeType::CustomNode) {
const QStringList vertexCode = n->vertexCode().split('\n');
int mainIndex = getTagIndex(vertexCode, QStringLiteral("main"));
int line = 0;
for (const QString &ss : vertexCode) {
if (mainIndex == -1 || line > mainIndex)
s_main += QStringLiteral(" ") + ss + '\n';
else if (line < mainIndex)
s_root += processVertexRootLine(ss);
line++;
}
}
}
}
if (s_sourceCode.isEmpty()) {
// If source nodes doesn't contain any code, use default one
s_sourceCode << getDefaultRootVertexShader();
}
if (removeTags) {
s_sourceCode = removeTagsFromCode(s_sourceCode);
s_root = removeTagsFromCode(s_root);
s_main = removeTagsFromCode(s_main);
}
s += getCustomShaderVaryings(true);
s += s_root + '\n';
int nodesIndex = getTagIndex(s_sourceCode, QStringLiteral("nodes"));
int line = 0;
for (const QString &ss : std::as_const(s_sourceCode))
s += (line++ == nodesIndex) ? s_main : ss + '\n';
return s;
}
QString EffectMakerModel::generateFragmentShader(bool includeUniforms)
{
QString s;
if (includeUniforms)
s += getFSUniforms();
// Remove tags when not generating for features check
const bool removeTags = includeUniforms;
s += getDefineProperties();
s += getConstVariables();
// When the node is complete, add shader code in correct nodes order
// split to root and main parts
QString s_root;
QString s_main;
QStringList s_sourceCode;
for (const CompositionNode *n : std::as_const(m_nodes)) {
if (!n->fragmentCode().isEmpty() && n->isEnabled()) {
if (n->type() == CompositionNode::NodeType::SourceNode) {
s_sourceCode = n->fragmentCode().split('\n');
} else if (n->type() == CompositionNode::NodeType::CustomNode) {
const QStringList fragmentCode = n->fragmentCode().split('\n');
int mainIndex = getTagIndex(fragmentCode, QStringLiteral("main"));
int line = 0;
for (const QString &ss : fragmentCode) {
if (mainIndex == -1 || line > mainIndex)
s_main += QStringLiteral(" ") + ss + '\n';
else if (line < mainIndex)
s_root += processFragmentRootLine(ss);
line++;
}
}
}
}
if (s_sourceCode.isEmpty()) {
// If source nodes doesn't contain any code, use default one
s_sourceCode << getDefaultRootFragmentShader();
}
if (removeTags) {
s_sourceCode = removeTagsFromCode(s_sourceCode);
s_root = removeTagsFromCode(s_root);
s_main = removeTagsFromCode(s_main);
}
s += getCustomShaderVaryings(false);
s += s_root + '\n';
int nodesIndex = getTagIndex(s_sourceCode, QStringLiteral("nodes"));
int line = 0;
for (const QString &ss : std::as_const(s_sourceCode))
s += (line++ == nodesIndex) ? s_main : ss + '\n';
return s;
}
void EffectMakerModel::bakeShaders()
{
resetEffectError(ErrorPreprocessor);
if (m_vertexShader == generateVertexShader() && m_fragmentShader == generateFragmentShader()) {
setShadersUpToDate(true);
return;
}
setShadersUpToDate(false);
// TODO: Compilation starts here
}
bool EffectMakerModel::shadersUpToDate() const
{
return m_shadersUpToDate;
}
void EffectMakerModel::setShadersUpToDate(bool UpToDate)
{
if (m_shadersUpToDate == UpToDate)
return;
m_shadersUpToDate = UpToDate;
emit shadersUpToDateChanged();
}
} // namespace QmlDesigner

View File

@@ -18,6 +18,7 @@ struct EffectError {
Q_PROPERTY(QString message MEMBER m_message)
Q_PROPERTY(int line MEMBER m_line)
Q_PROPERTY(int type MEMBER m_type)
public:
QString m_message;
int m_line = -1;
@@ -30,6 +31,7 @@ class EffectMakerModel : public QAbstractListModel
Q_PROPERTY(bool isEmpty MEMBER m_isEmpty NOTIFY isEmptyChanged)
Q_PROPERTY(int selectedIndex MEMBER m_selectedIndex NOTIFY selectedIndexChanged)
Q_PROPERTY(bool shadersUpToDate READ shadersUpToDate WRITE setShadersUpToDate NOTIFY shadersUpToDateChanged)
public:
EffectMakerModel(QObject *parent = nullptr);
@@ -44,10 +46,14 @@ public:
Q_INVOKABLE void removeNode(int idx);
bool shadersUpToDate() const;
void setShadersUpToDate(bool newShadersUpToDate);
signals:
void isEmptyChanged();
void selectedIndexChanged(int idx);
void effectErrorChanged();
void shadersUpToDateChanged();
private:
enum Roles {
@@ -55,6 +61,15 @@ private:
UniformsRole
};
enum ErrorTypes {
ErrorCommon = -1,
ErrorQMLParsing,
ErrorVert,
ErrorFrag,
ErrorQMLRuntime,
ErrorPreprocessor
};
bool isValidIndex(int idx) const;
const QList<Uniform *> allUniforms();
@@ -68,14 +83,31 @@ private:
void setEffectError(const QString &errorMessage, int type, int lineNumber);
void resetEffectError(int type);
const QString getDefineProperties();
const QString getConstVariables();
int getTagIndex(const QStringList &code, const QString &tag);
QString processVertexRootLine(const QString &line);
QString processFragmentRootLine(const QString &line);
QStringList getDefaultRootVertexShader();
QStringList getDefaultRootFragmentShader();
QStringList removeTagsFromCode(const QStringList &codeLines);
QString removeTagsFromCode(const QString &code);
QString getCustomShaderVaryings(bool outState);
QString generateVertexShader(bool includeUniforms = true);
QString generateFragmentShader(bool includeUniforms = true);
void bakeShaders();
QList<CompositionNode *> m_nodes;
int m_selectedIndex = -1;
bool m_isEmpty = true;
// True when shaders haven't changed since last baking
bool m_shadersUpToDate = true;
QMap<int, EffectError> m_effectErrors;
ShaderFeatures m_shaderFeatures;
QStringList m_shaderVaryingVariables;
QString m_fragmentShader;
QString m_vertexShader;
};
} // namespace QmlDesigner