forked from qt-creator/qt-creator
QmlDesigner: Run QSB tool for modified shaders automatically
Raw shaders can no longer be used for ShaderEffect, but instead a precompiled QSB must be used. To facilitate this, we now automatically run QSB tool whenever a shader file changes. Task-number: QDS-6472 Change-Id: Ic2886b815c7f428ac393d1d7623105a4e82053ae Reviewed-by: Samuel Ghinet <samuel.ghinet@qt.io> Reviewed-by: Mahmoud Badri <mahmoud.badri@qt.io> Reviewed-by: <github-actions-qt-creator@cristianadam.eu> Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
This commit is contained in:
@@ -34,6 +34,8 @@
|
|||||||
#include <nodeinstanceclientinterface.h>
|
#include <nodeinstanceclientinterface.h>
|
||||||
#include <nodeinstanceserverinterface.h>
|
#include <nodeinstanceserverinterface.h>
|
||||||
|
|
||||||
|
#include <utils/filepath.h>
|
||||||
|
|
||||||
#include <QElapsedTimer>
|
#include <QElapsedTimer>
|
||||||
#include <QHash>
|
#include <QHash>
|
||||||
#include <QImage>
|
#include <QImage>
|
||||||
@@ -51,6 +53,10 @@ namespace ProjectExplorer {
|
|||||||
class Target;
|
class Target;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace Utils {
|
||||||
|
class QtcProcess;
|
||||||
|
}
|
||||||
|
|
||||||
namespace QmlDesigner {
|
namespace QmlDesigner {
|
||||||
|
|
||||||
class NodeInstanceServerProxy;
|
class NodeInstanceServerProxy;
|
||||||
@@ -229,7 +235,8 @@ private: // functions
|
|||||||
void updatePreviewImageForNode(const ModelNode &modelNode, const QImage &image);
|
void updatePreviewImageForNode(const ModelNode &modelNode, const QImage &image);
|
||||||
|
|
||||||
void updateWatcher(const QString &path);
|
void updateWatcher(const QString &path);
|
||||||
|
void handleShaderChanges();
|
||||||
|
void handleQsbProcessExit(Utils::QtcProcess *qsbProcess, const QString &shader);
|
||||||
void updateRotationBlocks();
|
void updateRotationBlocks();
|
||||||
void maybeResetOnPropertyChange(const PropertyName &name, const ModelNode &node,
|
void maybeResetOnPropertyChange(const PropertyName &name, const ModelNode &node,
|
||||||
PropertyChangeFlags flags);
|
PropertyChangeFlags flags);
|
||||||
@@ -278,7 +285,11 @@ private:
|
|||||||
QFileSystemWatcher *m_fileSystemWatcher;
|
QFileSystemWatcher *m_fileSystemWatcher;
|
||||||
QTimer m_resetTimer;
|
QTimer m_resetTimer;
|
||||||
QTimer m_updateWatcherTimer;
|
QTimer m_updateWatcherTimer;
|
||||||
|
QTimer m_generateQsbFilesTimer;
|
||||||
|
Utils::FilePath m_qsbPath;
|
||||||
QSet<QString> m_pendingUpdateDirs;
|
QSet<QString> m_pendingUpdateDirs;
|
||||||
|
QSet<QString> m_pendingQsbTargets;
|
||||||
|
int m_remainingQsbTargets;
|
||||||
QTimer m_rotBlockTimer;
|
QTimer m_rotBlockTimer;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -92,12 +92,18 @@
|
|||||||
#include <hdrimage.h>
|
#include <hdrimage.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <coreplugin/messagemanager.h>
|
||||||
|
|
||||||
|
#include <projectexplorer/kit.h>
|
||||||
#include <projectexplorer/target.h>
|
#include <projectexplorer/target.h>
|
||||||
|
|
||||||
#include <qmlprojectmanager/qmlmultilanguageaspect.h>
|
#include <qmlprojectmanager/qmlmultilanguageaspect.h>
|
||||||
|
|
||||||
#include <utils/algorithm.h>
|
#include <utils/algorithm.h>
|
||||||
#include <utils/qtcassert.h>
|
#include <utils/qtcassert.h>
|
||||||
|
#include <utils/qtcprocess.h>
|
||||||
|
|
||||||
|
#include <qtsupport/qtkitinformation.h>
|
||||||
|
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
#include <QMultiHash>
|
#include <QMultiHash>
|
||||||
@@ -161,6 +167,18 @@ NodeInstanceView::NodeInstanceView(ConnectionManagerInterface &connectionManager
|
|||||||
m_pendingUpdateDirs.clear();
|
m_pendingUpdateDirs.clear();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Since generating qsb files is asynchronous and can trigger directory changes, which in turn
|
||||||
|
// can trigger qsb generation, compressing qsb generation is necessary to avoid a lot of
|
||||||
|
// unnecessary generation when project with multiple shaders is opened.
|
||||||
|
m_generateQsbFilesTimer.setSingleShot(true);
|
||||||
|
m_generateQsbFilesTimer.setInterval(100);
|
||||||
|
QObject::connect(&m_generateQsbFilesTimer, &QTimer::timeout, [this] {
|
||||||
|
handleShaderChanges();
|
||||||
|
|
||||||
|
if (m_qsbPath.isEmpty() || m_remainingQsbTargets <= 0)
|
||||||
|
m_resetTimer.start();
|
||||||
|
});
|
||||||
|
|
||||||
connect(m_fileSystemWatcher, &QFileSystemWatcher::directoryChanged,
|
connect(m_fileSystemWatcher, &QFileSystemWatcher::directoryChanged,
|
||||||
[this](const QString &path) {
|
[this](const QString &path) {
|
||||||
const QSet<QString> pendingDirs = m_pendingUpdateDirs;
|
const QSet<QString> pendingDirs = m_pendingUpdateDirs;
|
||||||
@@ -177,8 +195,9 @@ NodeInstanceView::NodeInstanceView(ConnectionManagerInterface &connectionManager
|
|||||||
m_updateWatcherTimer.start();
|
m_updateWatcherTimer.start();
|
||||||
|
|
||||||
});
|
});
|
||||||
connect(m_fileSystemWatcher, &QFileSystemWatcher::fileChanged, [this] {
|
connect(m_fileSystemWatcher, &QFileSystemWatcher::fileChanged, [this](const QString &path) {
|
||||||
m_resetTimer.start();
|
m_pendingQsbTargets.insert(path);
|
||||||
|
m_generateQsbFilesTimer.start();
|
||||||
});
|
});
|
||||||
|
|
||||||
m_rotBlockTimer.setSingleShot(true);
|
m_rotBlockTimer.setSingleShot(true);
|
||||||
@@ -1461,6 +1480,17 @@ void NodeInstanceView::setTarget(ProjectExplorer::Target *newTarget)
|
|||||||
{
|
{
|
||||||
if (m_currentTarget != newTarget) {
|
if (m_currentTarget != newTarget) {
|
||||||
m_currentTarget = newTarget;
|
m_currentTarget = newTarget;
|
||||||
|
if (m_currentTarget && m_currentTarget->kit()) {
|
||||||
|
if (QtSupport::QtVersion *qtVer = QtSupport::QtKitAspect::qtVersion(m_currentTarget->kit())) {
|
||||||
|
m_qsbPath = qtVer->binPath().pathAppended("qsb").withExecutableSuffix();
|
||||||
|
if (!m_qsbPath.exists())
|
||||||
|
m_qsbPath.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_generateQsbFilesTimer.stop();
|
||||||
|
m_pendingQsbTargets.clear();
|
||||||
|
m_remainingQsbTargets = 0;
|
||||||
restartProcess();
|
restartProcess();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1904,7 +1934,86 @@ void NodeInstanceView::updateWatcher(const QString &path)
|
|||||||
m_fileSystemWatcher->removePaths(oldFiles);
|
m_fileSystemWatcher->removePaths(oldFiles);
|
||||||
if (!newFiles.isEmpty())
|
if (!newFiles.isEmpty())
|
||||||
m_fileSystemWatcher->addPaths(newFiles);
|
m_fileSystemWatcher->addPaths(newFiles);
|
||||||
|
|
||||||
|
for (const auto &newFile : qAsConst(newFiles)) {
|
||||||
|
if (!oldFiles.contains(newFile))
|
||||||
|
m_pendingQsbTargets.insert(newFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!m_pendingQsbTargets.isEmpty())
|
||||||
|
m_generateQsbFilesTimer.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void NodeInstanceView::handleQsbProcessExit(Utils::QtcProcess *qsbProcess, const QString &shader)
|
||||||
|
{
|
||||||
|
--m_remainingQsbTargets;
|
||||||
|
|
||||||
|
QString errStr = qsbProcess->errorString();
|
||||||
|
QByteArray stdErrStr = qsbProcess->readAllStandardError();
|
||||||
|
|
||||||
|
if (!errStr.isEmpty() || !stdErrStr.isEmpty()) {
|
||||||
|
Core::MessageManager::writeSilently(
|
||||||
|
QCoreApplication::translate("QmlDesigner::NodeInstanceView",
|
||||||
|
"Failed to generate QSB file for: %1")
|
||||||
|
.arg(shader));
|
||||||
|
if (!errStr.isEmpty())
|
||||||
|
Core::MessageManager::writeSilently(errStr);
|
||||||
|
if (!stdErrStr.isEmpty())
|
||||||
|
Core::MessageManager::writeSilently(QString::fromUtf8(stdErrStr));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_remainingQsbTargets <= 0)
|
||||||
|
m_resetTimer.start();
|
||||||
|
|
||||||
|
qsbProcess->deleteLater();
|
||||||
|
}
|
||||||
|
|
||||||
|
void NodeInstanceView::handleShaderChanges()
|
||||||
|
{
|
||||||
|
m_remainingQsbTargets += m_pendingQsbTargets.size();
|
||||||
|
|
||||||
|
for (const auto &shader : qAsConst(m_pendingQsbTargets)) {
|
||||||
|
// Run qsb for changed shader file
|
||||||
|
if (!m_qsbPath.isEmpty() && !shader.isEmpty()) {
|
||||||
|
const Utils::FilePath sourceFile = Utils::FilePath::fromString(shader);
|
||||||
|
const Utils::FilePath srcPath = sourceFile.absolutePath();
|
||||||
|
const Utils::FilePath outPath = Utils::FilePath::fromString(shader + ".qsb");
|
||||||
|
|
||||||
|
if (!sourceFile.exists() || (outPath.exists() && outPath.lastModified() > sourceFile.lastModified())) {
|
||||||
|
--m_remainingQsbTargets;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run QSB with same parameters as Qt build does
|
||||||
|
// TODO: Parameters should be configurable (QDS-6590)
|
||||||
|
const QStringList args = {"-s", "--glsl", "100 es,120,150", "--hlsl", "50", "--msl", "12",
|
||||||
|
"-o", outPath.toString(), shader};
|
||||||
|
auto qsbProcess = new Utils::QtcProcess;
|
||||||
|
qsbProcess->setWorkingDirectory(srcPath);
|
||||||
|
qsbProcess->setCommand({m_qsbPath, args});
|
||||||
|
qsbProcess->start();
|
||||||
|
|
||||||
|
if (!qsbProcess->waitForStarted()) {
|
||||||
|
handleQsbProcessExit(qsbProcess, shader);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (qsbProcess->state() == QProcess::Running) {
|
||||||
|
connect(qsbProcess, &Utils::QtcProcess::finished,
|
||||||
|
[thisView = QPointer<NodeInstanceView>(this), qsbProcess, shader]() {
|
||||||
|
if (thisView)
|
||||||
|
thisView->handleQsbProcessExit(qsbProcess, shader);
|
||||||
|
else
|
||||||
|
qsbProcess->deleteLater();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
handleQsbProcessExit(qsbProcess, shader);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_pendingQsbTargets.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void NodeInstanceView::updateRotationBlocks()
|
void NodeInstanceView::updateRotationBlocks()
|
||||||
|
Reference in New Issue
Block a user