diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMakerPreview.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMakerPreview.qml index c6ffe83a168..03d587e8195 100644 --- a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMakerPreview.qml +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMakerPreview.qml @@ -18,7 +18,7 @@ Column { property var effectMakerModel: EffectMakerBackend.effectMakerModel property alias source: source // The delay in ms to wait until updating the effect - readonly property int updateDelay: 200 + readonly property int updateDelay: 100 // Create a dummy parent to host the effect qml object function createNewComponent() { @@ -171,7 +171,7 @@ Column { id: componentParent width: source.width height: source.height - anchors.centerIn: parent + anchors.centerIn: parent scale: 1 //TODO should come from toolbar // Cache the layer. This way heavy shaders rendering doesn't // slow down code editing & rest of the UI. @@ -183,16 +183,16 @@ Column { target: effectMakerModel function onShadersBaked() { console.log("Shaders Baked!") - //updateTimer.restart(); // Disable for now + updateTimer.restart() } } Timer { id: updateTimer - interval: updateDelay; + interval: updateDelay onTriggered: { - effectMakerModel.updateQmlComponent(); - createNewComponent(); + effectMakerModel.updateQmlComponent() + createNewComponent() } } } diff --git a/src/plugins/effectmakernew/CMakeLists.txt b/src/plugins/effectmakernew/CMakeLists.txt index d01c97618c7..21646e31322 100644 --- a/src/plugins/effectmakernew/CMakeLists.txt +++ b/src/plugins/effectmakernew/CMakeLists.txt @@ -3,7 +3,7 @@ find_package(Qt6 OPTIONAL_COMPONENTS Gui Quick ShaderTools) add_qtc_plugin(EffectMakerNew CONDITION TARGET QmlDesigner AND TARGET Qt::ShaderTools PLUGIN_DEPENDS - QtCreator::Core QtCreator::QmlDesigner QtCreator::ProjectExplorer + QtCreator::Core QtCreator::QmlDesigner QtCreator::ProjectExplorer QtCreator::QmlProjectManager DEPENDS Qt::Core QtCreator::Utils Qt::CorePrivate Qt::Widgets Qt::Qml Qt::QmlPrivate Qt::Quick Qt::ShaderTools Qt::ShaderToolsPrivate diff --git a/src/plugins/effectmakernew/effectmakermodel.cpp b/src/plugins/effectmakernew/effectmakermodel.cpp index da8403108f3..94de609c2e8 100644 --- a/src/plugins/effectmakernew/effectmakermodel.cpp +++ b/src/plugins/effectmakernew/effectmakermodel.cpp @@ -7,8 +7,7 @@ #include "syntaxhighlighterdata.h" #include "uniform.h" -#include -#include +#include #include #include @@ -17,6 +16,10 @@ #include #include +#include + +#include +#include namespace EffectMaker { @@ -49,8 +52,14 @@ EffectMakerModel::EffectMakerModel(QObject *parent) m_vertexShaderFile.setFileTemplate(QDir::tempPath() + "/dsem_XXXXXX.vert.qsb"); m_fragmentShaderFile.setFileTemplate(QDir::tempPath() + "/dsem_XXXXXX.frag.qsb"); if (!m_vertexSourceFile.open() || !m_fragmentSourceFile.open() - || !m_vertexShaderFile.open() || !m_fragmentShaderFile.open()) + || !m_vertexShaderFile.open() || !m_fragmentShaderFile.open()) { qWarning() << "Unable to open temporary files"; + } else { + m_vertexSourceFilename = m_vertexSourceFile.fileName(); + m_fragmentSourceFilename = m_fragmentSourceFile.fileName(); + m_vertexShaderFilename = m_vertexShaderFile.fileName(); + m_fragmentShaderFilename = m_fragmentShaderFile.fileName(); + } } QHash EffectMakerModel::roleNames() const @@ -720,6 +729,24 @@ QString EffectMakerModel::generateFragmentShader(bool includeUniforms) return s; } +void EffectMakerModel::handleQsbProcessExit(Utils::Process *qsbProcess, const QString &shader) +{ + --m_remainingQsbTargets; + + const QString errStr = qsbProcess->errorString(); + if (!errStr.isEmpty()) + qWarning() << QString("Failed to generate QSB file for: %1 %2").arg(shader, errStr); + + if (m_remainingQsbTargets <= 0) { + Q_EMIT shadersBaked(); + setShadersUpToDate(true); + + // TODO: Mark shaders as baked, required by export later + } + + qsbProcess->deleteLater(); +} + // Generates string of the custom properties (uniforms) into ShaderEffect component // Also generates QML images elements for samplers. void EffectMakerModel::updateCustomUniforms() @@ -790,6 +817,14 @@ void EffectMakerModel::updateCustomUniforms() void EffectMakerModel::bakeShaders() { + const QString failMessage = "Shader baking failed: "; + + const ProjectExplorer::Target *target = ProjectExplorer::ProjectTree::currentTarget(); + if (!target) { + qWarning() << failMessage << "Target not found"; + return; + } + resetEffectError(ErrorPreprocessor); if (m_vertexShader == generateVertexShader() && m_fragmentShader == generateFragmentShader()) { setShadersUpToDate(true); @@ -813,12 +848,34 @@ void EffectMakerModel::bakeShaders() QString fs = m_fragmentShader; writeToFile(fs.toUtf8(), m_fragmentSourceFile.fileName(), FileType::Text); - //TODO: Compile shaders using external qsb tools + QtSupport::QtVersion *qtVer = QtSupport::QtKitAspect::qtVersion(target->kit()); + if (!qtVer) { + qWarning() << failMessage << "Qt version not found"; + return; + } - Q_EMIT shadersBaked(); - setShadersUpToDate(true); + Utils::FilePath qsbPath = qtVer->binPath().pathAppended("qsb").withExecutableSuffix(); + if (!qsbPath.exists()) { + qWarning() << failMessage << "QSB tool not found"; + return; + } - // TODO: Mark shaders as baked, required by export later + m_remainingQsbTargets = 2; // We only have 2 shaders + const QStringList srcPaths = {m_vertexSourceFilename, m_fragmentSourceFilename}; + const QStringList outPaths = {m_vertexShaderFilename, m_fragmentShaderFilename}; + for (int i = 0; i < 2; ++i) { + const auto workDir = Utils::FilePath::fromString(outPaths[i]); + QStringList args = {"-s", "--glsl", "\"300 es,120,150,440\"", "--hlsl", "50", "--msl", "12"}; + args << "-o" << outPaths[i] << srcPaths[i]; + + auto qsbProcess = new Utils::Process(this); + connect(qsbProcess, &Utils::Process::done, this, [=] { + handleQsbProcessExit(qsbProcess, srcPaths[i]); + }); + qsbProcess->setWorkingDirectory(workDir.absolutePath()); + qsbProcess->setCommand({qsbPath, args}); + qsbProcess->start(); + } } bool EffectMakerModel::shadersUpToDate() const @@ -857,8 +914,6 @@ QString EffectMakerModel::getQmlComponentString(bool localFiles) }; QString customImagesString = getQmlImagesString(localFiles); - QString vertexShaderFilename = "file:///" + m_fragmentShaderFile.fileName(); - QString fragmentShaderFilename = "file:///" + m_vertexShaderFile.fileName(); QString s; QString l1 = localFiles ? QStringLiteral(" ") : QStringLiteral(""); QString l2 = localFiles ? QStringLiteral(" ") : QStringLiteral(" "); @@ -896,8 +951,8 @@ QString EffectMakerModel::getQmlComponentString(bool localFiles) s += '\n' + customImagesString; s += '\n'; - s += l2 + "vertexShader: '" + vertexShaderFilename + "'\n"; - s += l2 + "fragmentShader: '" + fragmentShaderFilename + "'\n"; + s += l2 + "vertexShader: 'file://" + m_vertexShaderFilename + "'\n"; + s += l2 + "fragmentShader: 'file://" + m_fragmentShaderFilename + "'\n"; s += l2 + "anchors.fill: parent\n"; if (m_shaderFeatures.enabled(ShaderFeatures::GridMesh)) { QString gridSize = QString("%1, %2").arg(m_shaderFeatures.gridMeshWidth()).arg(m_shaderFeatures.gridMeshHeight()); @@ -909,21 +964,6 @@ QString EffectMakerModel::getQmlComponentString(bool localFiles) return s; } -Utils::FilePath EffectMakerModel::qsbPath() const -{ - const ProjectExplorer::Target *target = ProjectExplorer::ProjectTree::currentTarget(); - if (target) { - if (QtSupport::QtVersion *qtVer = QtSupport::QtKitAspect::qtVersion(target->kit())) { - Utils::FilePath path = qtVer->binPath().pathAppended("qsb").withExecutableSuffix(); - if (path.exists()) - return path; - } - } - - qWarning() << "Shader baking failed, QSB not found."; - return {}; -} - void EffectMakerModel::updateQmlComponent() { // Clear possible QML runtime errors diff --git a/src/plugins/effectmakernew/effectmakermodel.h b/src/plugins/effectmakernew/effectmakermodel.h index 8f34dc8fe43..1cd68e70a72 100644 --- a/src/plugins/effectmakernew/effectmakermodel.h +++ b/src/plugins/effectmakernew/effectmakermodel.h @@ -12,6 +12,10 @@ #include #include +namespace ProjectExplorer { +class Target; +} + namespace Utils { class Process; } @@ -127,8 +131,7 @@ private: QString getCustomShaderVaryings(bool outState); QString generateVertexShader(bool includeUniforms = true); QString generateFragmentShader(bool includeUniforms = true); - - Utils::FilePath qsbPath() const; + void handleQsbProcessExit(Utils::Process *qsbProcess, const QString &shader); void updateCustomUniforms(); void bakeShaders(); @@ -142,6 +145,7 @@ private: bool m_isEmpty = true; // True when shaders haven't changed since last baking bool m_shadersUpToDate = true; + int m_remainingQsbTargets = 0; QMap m_effectErrors; ShaderFeatures m_shaderFeatures; QStringList m_shaderVaryingVariables; @@ -154,6 +158,10 @@ private: QTemporaryFile m_vertexSourceFile; QTemporaryFile m_fragmentShaderFile; QTemporaryFile m_vertexShaderFile; + QString m_fragmentSourceFilename; + QString m_vertexSourceFilename; + QString m_fragmentShaderFilename; + QString m_vertexShaderFilename; // Used in exported QML, at root of the file QString m_exportedRootPropertiesString; // Used in exported QML, at ShaderEffect component of the file