diff options
Diffstat (limited to 'src/plugins')
66 files changed, 3424 insertions, 1546 deletions
diff --git a/src/plugins/qmltooling/packetprotocol/qpacketprotocol.cpp b/src/plugins/qmltooling/packetprotocol/qpacketprotocol.cpp index d20ddf9dc0..e541810330 100644 --- a/src/plugins/qmltooling/packetprotocol/qpacketprotocol.cpp +++ b/src/plugins/qmltooling/packetprotocol/qpacketprotocol.cpp @@ -124,12 +124,9 @@ QPacketProtocol::QPacketProtocol(QIODevice *dev, QObject *parent) Q_ASSERT(4 == sizeof(qint32)); Q_ASSERT(dev); - QObject::connect(dev, SIGNAL(readyRead()), - this, SLOT(readyToRead())); - QObject::connect(dev, SIGNAL(aboutToClose()), - this, SLOT(aboutToClose())); - QObject::connect(dev, SIGNAL(bytesWritten(qint64)), - this, SLOT(bytesWritten(qint64))); + QObject::connect(dev, &QIODevice::readyRead, this, &QPacketProtocol::readyToRead); + QObject::connect(dev, &QIODevice::aboutToClose, this, &QPacketProtocol::aboutToClose); + QObject::connect(dev, &QIODevice::bytesWritten, this, &QPacketProtocol::bytesWritten); } /*! @@ -247,12 +244,9 @@ void QPacketProtocol::readyToRead() // Check sizing constraints if (d->inProgressSize > MAX_PACKET_SIZE) { - QObject::disconnect(d->dev, SIGNAL(readyRead()), - this, SLOT(readyToRead())); - QObject::disconnect(d->dev, SIGNAL(aboutToClose()), - this, SLOT(aboutToClose())); - QObject::disconnect(d->dev, SIGNAL(bytesWritten(qint64)), - this, SLOT(bytesWritten(qint64))); + disconnect(d->dev, &QIODevice::readyRead, this, &QPacketProtocol::readyToRead); + disconnect(d->dev, &QIODevice::aboutToClose, this, &QPacketProtocol::aboutToClose); + disconnect(d->dev, &QIODevice::bytesWritten, this, &QPacketProtocol::bytesWritten); d->dev = 0; emit invalidPacket(); return; diff --git a/src/plugins/qmltooling/packetprotocol/qpacketprotocol_p.h b/src/plugins/qmltooling/packetprotocol/qpacketprotocol_p.h index 8f95a081e9..7fd722f17f 100644 --- a/src/plugins/qmltooling/packetprotocol/qpacketprotocol_p.h +++ b/src/plugins/qmltooling/packetprotocol/qpacketprotocol_p.h @@ -74,7 +74,7 @@ Q_SIGNALS: void readyRead(); void invalidPacket(); -private Q_SLOTS: +private: void aboutToClose(); void bytesWritten(qint64 bytes); void readyToRead(); diff --git a/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.cpp b/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.cpp index e08436b7a3..fe88d686fc 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.cpp +++ b/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.cpp @@ -63,8 +63,12 @@ QT_BEGIN_NAMESPACE QQmlEngineDebugServiceImpl::QQmlEngineDebugServiceImpl(QObject *parent) : QQmlEngineDebugService(2, parent), m_watch(new QQmlWatcher(this)), m_statesDelegate(0) { - QObject::connect(m_watch, SIGNAL(propertyChanged(int,int,QMetaProperty,QVariant)), - this, SLOT(propertyChanged(int,int,QMetaProperty,QVariant))); + connect(m_watch, &QQmlWatcher::propertyChanged, + this, &QQmlEngineDebugServiceImpl::propertyChanged); + + // Move the message into the correct thread for processing + connect(this, &QQmlEngineDebugServiceImpl::scheduleMessage, + this, &QQmlEngineDebugServiceImpl::processMessage, Qt::QueuedConnection); } QQmlEngineDebugServiceImpl::~QQmlEngineDebugServiceImpl() @@ -285,10 +289,12 @@ void QQmlEngineDebugServiceImpl::buildObjectDump(QDataStream &message, prop.value = expr->expression(); QObject *scope = expr->scopeObject(); if (scope) { - QString methodName = QString::fromLatin1(QMetaObjectPrivate::signal(scope->metaObject(), signalHandler->signalIndex()).name()); - if (!methodName.isEmpty()) { - prop.name = QLatin1String("on") + methodName[0].toUpper() - + methodName.mid(1); + const QByteArray methodName = QMetaObjectPrivate::signal(scope->metaObject(), + signalHandler->signalIndex()).name(); + const QLatin1String methodNameStr(methodName); + if (methodNameStr.size() != 0) { + prop.name = QLatin1String("on") + QChar(methodNameStr.at(0)).toUpper() + + methodNameStr.mid(1); } } } @@ -420,7 +426,7 @@ QQmlEngineDebugServiceImpl::objectData(QObject *object) void QQmlEngineDebugServiceImpl::messageReceived(const QByteArray &message) { - QMetaObject::invokeMethod(this, "processMessage", Qt::QueuedConnection, Q_ARG(QByteArray, message)); + emit scheduleMessage(message); } /*! @@ -516,12 +522,12 @@ void QQmlEngineDebugServiceImpl::processMessage(const QByteArray &message) ds >> file >> lineNumber >> columnNumber >> recurse >> dumpProperties; - QList<QObject*> objects = objectForLocationInfo(file, lineNumber, columnNumber); + const QList<QObject*> objects = objectForLocationInfo(file, lineNumber, columnNumber); rs << QByteArray("FETCH_OBJECTS_FOR_LOCATION_R") << queryId << objects.count(); - foreach (QObject *object, objects) { + for (QObject *object : objects) { if (recurse) prepareDeferredObjects(object); buildObjectDump(rs, object, recurse, dumpProperties); @@ -656,7 +662,7 @@ bool QQmlEngineDebugServiceImpl::setBinding(int objectId, filename, line, column); QQmlPropertyPrivate::takeSignalExpression(property, qmlExpression); } else if (property.isProperty()) { - QQmlBinding *binding = new QQmlBinding(expression.toString(), object, QQmlContextData::get(context), filename, line, column); + QQmlBinding *binding = QQmlBinding::create(&QQmlPropertyPrivate::get(property)->core, expression.toString(), object, QQmlContextData::get(context), filename, line, column); binding->setTarget(property); QQmlPropertyPrivate::setBinding(binding); binding->update(); @@ -749,7 +755,7 @@ bool QQmlEngineDebugServiceImpl::setMethodBody(int objectId, const QString &meth if (!prop || !prop->isVMEFunction()) return false; - QMetaMethod metaMethod = object->metaObject()->method(prop->coreIndex); + QMetaMethod metaMethod = object->metaObject()->method(prop->coreIndex()); QList<QByteArray> paramNames = metaMethod.parameterNames(); QString paramStr; @@ -758,10 +764,8 @@ bool QQmlEngineDebugServiceImpl::setMethodBody(int objectId, const QString &meth paramStr.append(QString::fromUtf8(paramNames.at(ii))); } - QString jsfunction = QLatin1String("(function ") + method + QLatin1Char('(') + paramStr + - QLatin1String(") {"); - jsfunction += body; - jsfunction += QLatin1String("\n})"); + const QString jsfunction = QLatin1String("(function ") + method + QLatin1Char('(') + paramStr + + QLatin1String(") {") + body + QLatin1String("\n})"); QQmlVMEMetaObject *vmeMetaObject = QQmlVMEMetaObject::get(object); Q_ASSERT(vmeMetaObject); // the fact we found the property above should guarentee this @@ -770,12 +774,12 @@ bool QQmlEngineDebugServiceImpl::setMethodBody(int objectId, const QString &meth QV4::Scope scope(v4); int lineNumber = 0; - QV4::ScopedFunctionObject oldMethod(scope, vmeMetaObject->vmeMethod(prop->coreIndex)); + QV4::ScopedFunctionObject oldMethod(scope, vmeMetaObject->vmeMethod(prop->coreIndex())); if (oldMethod && oldMethod->d()->function) { lineNumber = oldMethod->d()->function->compiledFunction->location.line; } QV4::ScopedValue v(scope, QQmlJavaScriptExpression::evalFunction(contextData, object, jsfunction, contextData->urlString(), lineNumber)); - vmeMetaObject->setVmeMethod(prop->coreIndex, v); + vmeMetaObject->setVmeMethod(prop->coreIndex(), v); return true; } diff --git a/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.h b/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.h index cb75a63850..2e40eb4de8 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.h +++ b/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.h @@ -101,16 +101,18 @@ public: void setStatesDelegate(QQmlDebugStatesDelegate *) Q_DECL_OVERRIDE; +signals: + void scheduleMessage(const QByteArray &); + protected: virtual void messageReceived(const QByteArray &) Q_DECL_OVERRIDE; -private Q_SLOTS: - void processMessage(const QByteArray &msg); - void propertyChanged(int id, int objectId, const QMetaProperty &property, const QVariant &value); - private: friend class QQmlDebuggerServiceFactory; + void processMessage(const QByteArray &msg); + void propertyChanged(int id, int objectId, const QMetaProperty &property, const QVariant &value); + void prepareDeferredObjects(QObject *); void buildObjectList(QDataStream &, QQmlContext *, const QList<QPointer<QObject> > &instances); diff --git a/src/plugins/qmltooling/qmldbg_debugger/qqmlnativedebugservice.cpp b/src/plugins/qmltooling/qmldbg_debugger/qqmlnativedebugservice.cpp index 99f5b760ba..9c198a8afc 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qqmlnativedebugservice.cpp +++ b/src/plugins/qmltooling/qmldbg_debugger/qqmlnativedebugservice.cpp @@ -143,15 +143,15 @@ public: void BreakPointHandler::handleSetBreakpoint(QJsonObject *response, const QJsonObject &arguments) { TRACE_PROTOCOL("SET BREAKPOINT" << arguments); - QString type = arguments.value(QStringLiteral("type")).toString(); + QString type = arguments.value(QLatin1String("type")).toString(); - QString fileName = arguments.value(QStringLiteral("file")).toString(); + QString fileName = arguments.value(QLatin1String("file")).toString(); if (fileName.isEmpty()) { setError(response, QStringLiteral("breakpoint has no file name")); return; } - int line = arguments.value(QStringLiteral("line")).toInt(-1); + int line = arguments.value(QLatin1String("line")).toInt(-1); if (line < 0) { setError(response, QStringLiteral("breakpoint has an invalid line number")); return; @@ -161,9 +161,9 @@ void BreakPointHandler::handleSetBreakpoint(QJsonObject *response, const QJsonOb bp.id = m_lastBreakpoint++; bp.fileName = fileName.mid(fileName.lastIndexOf('/') + 1); bp.lineNumber = line; - bp.enabled = arguments.value(QStringLiteral("enabled")).toBool(true); - bp.condition = arguments.value(QStringLiteral("condition")).toString(); - bp.ignoreCount = arguments.value(QStringLiteral("ignorecount")).toInt(); + bp.enabled = arguments.value(QLatin1String("enabled")).toBool(true); + bp.condition = arguments.value(QLatin1String("condition")).toString(); + bp.ignoreCount = arguments.value(QLatin1String("ignorecount")).toInt(); m_breakPoints.append(bp); m_haveBreakPoints = true; @@ -174,7 +174,7 @@ void BreakPointHandler::handleSetBreakpoint(QJsonObject *response, const QJsonOb void BreakPointHandler::handleRemoveBreakpoint(QJsonObject *response, const QJsonObject &arguments) { - int id = arguments.value(QStringLiteral("id")).toInt(); + int id = arguments.value(QLatin1String("id")).toInt(); removeBreakPoint(id); response->insert(QStringLiteral("id"), id); } @@ -208,7 +208,7 @@ private: void handleDebuggerDeleted(QObject *debugger); - QV4::ReturnedValue evaluateExpression(QV4::Scope &scope, const QString &expression); + void evaluateExpression(QV4::Scope &scope, const QString &expression); bool checkCondition(const QString &expression); QStringList breakOnSignals; @@ -241,12 +241,11 @@ private: bool NativeDebugger::checkCondition(const QString &expression) { QV4::Scope scope(m_engine); - QV4::ReturnedValue result = evaluateExpression(scope, expression); - QV4::ScopedValue val(scope, result); - return val->booleanValue(); + evaluateExpression(scope, expression); + return scope.result.booleanValue(); } -QV4::ReturnedValue NativeDebugger::evaluateExpression(QV4::Scope &scope, const QString &expression) +void NativeDebugger::evaluateExpression(QV4::Scope &scope, const QString &expression) { m_runningJob = true; @@ -261,12 +260,10 @@ QV4::ReturnedValue NativeDebugger::evaluateExpression(QV4::Scope &scope, const Q // That is a side-effect of inheritContext. script.inheritContext = true; script.parse(); - QV4::ScopedValue result(scope); if (!m_engine->hasException) - result = script.run(); + scope.result = script.run(); m_runningJob = false; - return result->asReturnedValue(); } NativeDebugger::NativeDebugger(QQmlNativeDebugServiceImpl *service, QV4::ExecutionEngine *engine) @@ -290,7 +287,7 @@ void NativeDebugger::signalEmitted(const QString &signal) //Normalize to Lower case. QString signalName = signal.left(signal.indexOf(QLatin1Char('('))).toLower(); - foreach (const QString &signal, breakOnSignals) { + for (const QString &signal : qAsConst(breakOnSignals)) { if (signal == signalName) { // TODO: pause debugger break; @@ -301,19 +298,19 @@ void NativeDebugger::signalEmitted(const QString &signal) void NativeDebugger::handleCommand(QJsonObject *response, const QString &cmd, const QJsonObject &arguments) { - if (cmd == QStringLiteral("backtrace")) + if (cmd == QLatin1String("backtrace")) handleBacktrace(response, arguments); - else if (cmd == QStringLiteral("variables")) + else if (cmd == QLatin1String("variables")) handleVariables(response, arguments); - else if (cmd == QStringLiteral("expressions")) + else if (cmd == QLatin1String("expressions")) handleExpressions(response, arguments); - else if (cmd == QStringLiteral("stepin")) + else if (cmd == QLatin1String("stepin")) handleContinue(response, StepIn); - else if (cmd == QStringLiteral("stepout")) + else if (cmd == QLatin1String("stepout")) handleContinue(response, StepOut); - else if (cmd == QStringLiteral("stepover")) + else if (cmd == QLatin1String("stepover")) handleContinue(response, StepOver); - else if (cmd == QStringLiteral("continue")) + else if (cmd == QLatin1String("continue")) handleContinue(response, NotStepping); } @@ -334,7 +331,7 @@ static void decodeContext(const QString &context, QV4::ExecutionContext **execut void NativeDebugger::handleBacktrace(QJsonObject *response, const QJsonObject &arguments) { - int limit = arguments.value(QStringLiteral("limit")).toInt(0); + int limit = arguments.value(QLatin1String("limit")).toInt(0); QJsonArray frameArray; QV4::ExecutionContext *executionContext = m_engine->currentContext; @@ -343,16 +340,16 @@ void NativeDebugger::handleBacktrace(QJsonObject *response, const QJsonObject &a if (heapFunctionObject) { QJsonObject frame; - frame[QStringLiteral("language")] = QStringLiteral("js"); - frame[QStringLiteral("context")] = encodeContext(executionContext); + frame.insert(QStringLiteral("language"), QStringLiteral("js")); + frame.insert(QStringLiteral("context"), encodeContext(executionContext)); if (QV4::Function *function = heapFunctionObject->function) { if (QV4::Heap::String *functionName = function->name()) - frame[QStringLiteral("function")] = functionName->toQString(); - frame[QStringLiteral("file")] = function->sourceFile(); + frame.insert(QStringLiteral("function"), functionName->toQString()); + frame.insert(QStringLiteral("file"), function->sourceFile()); } int line = executionContext->d()->lineNumber; - frame[QStringLiteral("line")] = (line < 0 ? -line : line); + frame.insert(QStringLiteral("line"), (line < 0 ? -line : line)); frameArray.push_back(frame); } @@ -463,7 +460,7 @@ void NativeDebugger::handleVariables(QJsonObject *response, const QJsonObject &a { TRACE_PROTOCOL("Build variables"); QV4::ExecutionContext *executionContext = 0; - decodeContext(arguments.value(QStringLiteral("context")).toString(), &executionContext); + decodeContext(arguments.value(QLatin1String("context")).toString(), &executionContext); if (!executionContext) { setError(response, QStringLiteral("No execution context passed")); return; @@ -478,8 +475,8 @@ void NativeDebugger::handleVariables(QJsonObject *response, const QJsonObject &a TRACE_PROTOCOL("Engine: " << engine); Collector collector(engine); - QJsonArray expanded = arguments.value(QStringLiteral("expanded")).toArray(); - foreach (const QJsonValue &ex, expanded) + const QJsonArray expanded = arguments.value(QLatin1String("expanded")).toArray(); + for (const QJsonValue &ex : expanded) collector.m_expanded.append(ex.toString()); TRACE_PROTOCOL("Expanded: " << collector.m_expanded); @@ -515,7 +512,7 @@ void NativeDebugger::handleExpressions(QJsonObject *response, const QJsonObject { TRACE_PROTOCOL("Evaluate expressions"); QV4::ExecutionContext *executionContext = 0; - decodeContext(arguments.value(QStringLiteral("context")).toString(), &executionContext); + decodeContext(arguments.value(QLatin1String("context")).toString(), &executionContext); if (!executionContext) { setError(response, QStringLiteral("No execution context passed")); return; @@ -530,36 +527,36 @@ void NativeDebugger::handleExpressions(QJsonObject *response, const QJsonObject TRACE_PROTOCOL("Engines: " << engine << m_engine); Collector collector(engine); - QJsonArray expanded = arguments.value(QStringLiteral("expanded")).toArray(); - foreach (const QJsonValue &ex, expanded) + const QJsonArray expanded = arguments.value(QLatin1String("expanded")).toArray(); + for (const QJsonValue &ex : expanded) collector.m_expanded.append(ex.toString()); TRACE_PROTOCOL("Expanded: " << collector.m_expanded); QJsonArray output; QV4::Scope scope(engine); - QJsonArray expressions = arguments.value(QStringLiteral("expressions")).toArray(); - foreach (const QJsonValue &expr, expressions) { - QString expression = expr.toObject().value(QStringLiteral("expression")).toString(); - QString name = expr.toObject().value(QStringLiteral("name")).toString(); + const QJsonArray expressions = arguments.value(QLatin1String("expressions")).toArray(); + for (const QJsonValue &expr : expressions) { + QString expression = expr.toObject().value(QLatin1String("expression")).toString(); + QString name = expr.toObject().value(QLatin1String("name")).toString(); TRACE_PROTOCOL("Evaluate expression: " << expression); m_runningJob = true; - QV4::ReturnedValue eval = evaluateExpression(scope, expression); - QV4::ScopedValue result(scope, eval); + evaluateExpression(scope, expression); + QV4::ScopedValue result(scope, scope.result); m_runningJob = false; if (result->isUndefined()) { QJsonObject dict; - dict[QStringLiteral("name")] = name; - dict[QStringLiteral("valueencoded")] = QStringLiteral("undefined"); + dict.insert(QStringLiteral("name"), name); + dict.insert(QStringLiteral("valueencoded"), QStringLiteral("undefined")); output.append(dict); - } else if (result.ptr && result.ptr->_val) { + } else if (result.ptr && result.ptr->rawValue()) { collector.collect(&output, QString(), name, *result); } else { QJsonObject dict; - dict[QStringLiteral("name")] = name; - dict[QStringLiteral("valueencoded")] = QStringLiteral("notaccessible"); + dict.insert(QStringLiteral("name"), name); + dict.insert(QStringLiteral("valueencoded"), QStringLiteral("notaccessible")); output.append(dict); } TRACE_PROTOCOL("EXCEPTION: " << engine->hasException); @@ -751,7 +748,8 @@ void QQmlNativeDebugServiceImpl::engineAboutToBeRemoved(QJSEngine *engine) TRACE_PROTOCOL("Removing engine" << engine); if (engine) { QV4::ExecutionEngine *executionEngine = QV8Engine::getV4(engine->handle()); - foreach (NativeDebugger *debugger, m_debuggers) { + const auto debuggersCopy = m_debuggers; + for (NativeDebugger *debugger : debuggersCopy) { if (debugger->engine() == executionEngine) m_debuggers.removeAll(debugger); } @@ -762,9 +760,9 @@ void QQmlNativeDebugServiceImpl::engineAboutToBeRemoved(QJSEngine *engine) void QQmlNativeDebugServiceImpl::stateAboutToBeChanged(QQmlDebugService::State state) { if (state == Enabled) { - foreach (NativeDebugger *debugger, m_debuggers) { + for (NativeDebugger *debugger : qAsConst(m_debuggers)) { QV4::ExecutionEngine *engine = debugger->engine(); - if (!engine->debugger) + if (!engine->debugger()) engine->setDebugger(debugger); } } @@ -776,17 +774,17 @@ void QQmlNativeDebugServiceImpl::messageReceived(const QByteArray &message) TRACE_PROTOCOL("Native message received: " << message); QJsonObject request = QJsonDocument::fromJson(message).object(); QJsonObject response; - QJsonObject arguments = request.value(QStringLiteral("arguments")).toObject(); - QString cmd = request.value(QStringLiteral("command")).toString(); + QJsonObject arguments = request.value(QLatin1String("arguments")).toObject(); + QString cmd = request.value(QLatin1String("command")).toString(); - if (cmd == QStringLiteral("setbreakpoint")) { + if (cmd == QLatin1String("setbreakpoint")) { m_breakHandler->handleSetBreakpoint(&response, arguments); - } else if (cmd == QStringLiteral("removebreakpoint")) { + } else if (cmd == QLatin1String("removebreakpoint")) { m_breakHandler->handleRemoveBreakpoint(&response, arguments); - } else if (cmd == QStringLiteral("echo")) { + } else if (cmd == QLatin1String("echo")) { response.insert(QStringLiteral("result"), arguments); } else { - foreach (NativeDebugger *debugger, m_debuggers) + for (NativeDebugger *debugger : qAsConst(m_debuggers)) if (debugger) debugger->handleCommand(&response, cmd, arguments); } diff --git a/src/plugins/qmltooling/qmldbg_debugger/qqmlwatcher.cpp b/src/plugins/qmltooling/qmldbg_debugger/qqmlwatcher.cpp index 392080dd51..1214212727 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qqmlwatcher.cpp +++ b/src/plugins/qmltooling/qmldbg_debugger/qqmlwatcher.cpp @@ -69,7 +69,7 @@ public: QQmlWatcher *parent = 0); public slots: - void notifyValueChanged(); + void notifyValueChanged(); // Needs to be a slot because of QQmlPropertyPrivate::connect() private: friend class QQmlWatcher; @@ -88,7 +88,8 @@ QQmlWatchProxy::QQmlWatchProxy(int id, QQmlWatcher *parent) : QObject(parent), m_id(id), m_watch(parent), m_object(0), m_debugId(debugId), m_expr(exp) { - QObject::connect(m_expr, SIGNAL(valueChanged()), this, SLOT(notifyValueChanged())); + QObject::connect(m_expr, &QQmlExpression::valueChanged, + this, &QQmlWatchProxy::notifyValueChanged); } QQmlWatchProxy::QQmlWatchProxy(int id, diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debugger.cpp b/src/plugins/qmltooling/qmldbg_debugger/qv4debugger.cpp index 53f2eab5ff..44810dd4cb 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qv4debugger.cpp +++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debugger.cpp @@ -79,6 +79,8 @@ QV4Debugger::QV4Debugger(QV4::ExecutionEngine *engine) static int pauseReasonId = qRegisterMetaType<QV4Debugger::PauseReason>(); Q_UNUSED(debuggerId); Q_UNUSED(pauseReasonId); + connect(this, &QV4Debugger::scheduleJob, + this, &QV4Debugger::runJobUnpaused, Qt::QueuedConnection); } QV4::ExecutionEngine *QV4Debugger::engine() const @@ -320,7 +322,7 @@ void QV4Debugger::runInEngine_havingLock(QV4DebugJob *job) if (state() == Paused) m_runningCondition.wakeAll(); else - QMetaObject::invokeMethod(this, "runJobUnpaused", Qt::QueuedConnection); + emit scheduleJob(); m_jobIsRunning.wait(&m_lock); m_runningJob = 0; } diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debugger.h b/src/plugins/qmltooling/qmldbg_debugger/qv4debugger.h index 3a5b6080cb..cd412e573d 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qv4debugger.h +++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debugger.h @@ -140,15 +140,14 @@ public: signals: void debuggerPaused(QV4Debugger *self, QV4Debugger::PauseReason reason); - -private slots: - void runJobUnpaused(); + void scheduleJob(); private: // requires lock to be held void pauseAndWait(PauseReason reason); bool reallyHitTheBreakPoint(const QString &filename, int linenr); void runInEngine_havingLock(QV4DebugJob *job); + void runJobUnpaused(); QV4::ExecutionEngine *m_engine; QV4::PersistentValue m_currentContext; diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.cpp b/src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.cpp index 756b6b28be..773bc937f5 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.cpp +++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.cpp @@ -52,7 +52,7 @@ QV4DebuggerAgent::QV4DebuggerAgent(QV4DebugServiceImpl *debugService) QV4Debugger *QV4DebuggerAgent::pausedDebugger() const { - foreach (QV4Debugger *debugger, m_debuggers) { + for (QV4Debugger *debugger : m_debuggers) { if (debugger->state() == QV4Debugger::Paused) return debugger; } @@ -147,13 +147,13 @@ void QV4DebuggerAgent::pause(QV4Debugger *debugger) const void QV4DebuggerAgent::pauseAll() const { - foreach (QV4Debugger *debugger, m_debuggers) + for (QV4Debugger *debugger : m_debuggers) pause(debugger); } void QV4DebuggerAgent::resumeAll() const { - foreach (QV4Debugger *debugger, m_debuggers) + for (QV4Debugger *debugger : m_debuggers) if (debugger->state() == QV4Debugger::Paused) debugger->resume(QV4Debugger::FullThrottle); } @@ -161,7 +161,7 @@ void QV4DebuggerAgent::resumeAll() const int QV4DebuggerAgent::addBreakPoint(const QString &fileName, int lineNumber, bool enabled, const QString &condition) { if (enabled) - foreach (QV4Debugger *debugger, m_debuggers) + for (QV4Debugger *debugger : qAsConst(m_debuggers)) debugger->addBreakPoint(fileName, lineNumber, condition); int id = m_breakPoints.size(); @@ -178,7 +178,7 @@ void QV4DebuggerAgent::removeBreakPoint(int id) m_breakPoints.remove(id); if (breakPoint.enabled) - foreach (QV4Debugger *debugger, m_debuggers) + for (QV4Debugger *debugger : qAsConst(m_debuggers)) debugger->removeBreakPoint(breakPoint.fileName, breakPoint.lineNr); } @@ -195,7 +195,7 @@ void QV4DebuggerAgent::enableBreakPoint(int id, bool onoff) return; breakPoint.enabled = onoff; - foreach (QV4Debugger *debugger, m_debuggers) { + for (QV4Debugger *debugger : qAsConst(m_debuggers)) { if (onoff) debugger->addBreakPoint(breakPoint.fileName, breakPoint.lineNr, breakPoint.condition); else @@ -218,14 +218,14 @@ void QV4DebuggerAgent::setBreakOnThrow(bool onoff) { if (onoff != m_breakOnThrow) { m_breakOnThrow = onoff; - foreach (QV4Debugger *debugger, m_debuggers) + for (QV4Debugger *debugger : qAsConst(m_debuggers)) debugger->setBreakOnThrow(onoff); } } void QV4DebuggerAgent::clearAllPauseRequests() { - foreach (QV4Debugger *debugger, m_debuggers) + for (QV4Debugger *debugger : qAsConst(m_debuggers)) debugger->clearPauseRequest(); } diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.h b/src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.h index 1c7eb50ac7..39ac4d4dcb 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.h +++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.h @@ -72,7 +72,6 @@ public: void setBreakOnThrow(bool onoff); void clearAllPauseRequests(); -public slots: void debuggerPaused(QV4Debugger *debugger, QV4Debugger::PauseReason reason); void handleDebuggerDeleted(QObject *debugger); diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debugjob.cpp b/src/plugins/qmltooling/qmldbg_debugger/qv4debugjob.cpp index a2d2fff72b..4e4048f6ad 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qv4debugjob.cpp +++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debugjob.cpp @@ -205,7 +205,7 @@ void ValueLookupJob::run() QQmlContextData::get(engine->qmlEngine()->rootContext()), scopeObject.data())); } - foreach (const QJsonValue &handle, handles) { + for (const QJsonValue &handle : handles) { QV4DataCollector::Ref ref = handle.toInt(); if (!collector->isValidRef(ref)) { exception = QString::fromLatin1("Invalid Ref: %1").arg(ref); @@ -258,7 +258,7 @@ GatherSourcesJob::GatherSourcesJob(QV4::ExecutionEngine *engine) void GatherSourcesJob::run() { - foreach (QV4::CompiledData::CompilationUnit *unit, engine->compilationUnits) { + for (QV4::CompiledData::CompilationUnit *unit : qAsConst(engine->compilationUnits)) { QString fileName = unit->fileName(); if (!fileName.isEmpty()) sources.append(fileName); diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debugjob.h b/src/plugins/qmltooling/qmldbg_debugger/qv4debugjob.h index 721f42b7c2..aff2ea1b6c 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qv4debugjob.h +++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debugjob.h @@ -65,7 +65,7 @@ class JavaScriptJob : public QV4DebugJob public: JavaScriptJob(QV4::ExecutionEngine *engine, int frameNr, const QString &script); - void run(); + void run() override; bool hasExeption() const; protected: @@ -90,7 +90,7 @@ class BacktraceJob: public CollectJob int toFrame; public: BacktraceJob(QV4DataCollector *collector, int fromFrame, int toFrame); - void run(); + void run() override; }; class FrameJob: public CollectJob @@ -100,7 +100,7 @@ class FrameJob: public CollectJob public: FrameJob(QV4DataCollector *collector, int frameNr); - void run(); + void run() override; bool wasSuccessful() const; }; @@ -123,7 +123,7 @@ class ValueLookupJob: public CollectJob public: ValueLookupJob(const QJsonArray &handles, QV4DataCollector *collector); - void run(); + void run() override; const QString &exceptionMessage() const; }; @@ -137,7 +137,7 @@ class ExpressionEvalJob: public JavaScriptJob public: ExpressionEvalJob(QV4::ExecutionEngine *engine, int frameNr, const QString &expression, QV4DataCollector *collector); - virtual void handleResult(QV4::ScopedValue &value); + void handleResult(QV4::ScopedValue &value) override; const QString &exceptionMessage() const; const QJsonObject &returnValue() const; const QJsonArray &refs() const; @@ -150,7 +150,7 @@ class GatherSourcesJob: public QV4DebugJob public: GatherSourcesJob(QV4::ExecutionEngine *engine); - void run(); + void run() override; const QStringList &result() const; }; @@ -161,7 +161,7 @@ class EvalJob: public JavaScriptJob public: EvalJob(QV4::ExecutionEngine *engine, const QString &script); - virtual void handleResult(QV4::ScopedValue &result); + void handleResult(QV4::ScopedValue &result) override; bool resultAsBoolean() const; }; diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp b/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp index 5ee9e5e9e9..de97b5437b 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp +++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp @@ -90,7 +90,7 @@ public: TRACE_PROTOCOL(qDebug() << "handling command" << command() << "..."); req = request; - seq = req.value(QStringLiteral("seq")); + seq = req.value(QLatin1String("seq")); debugService = s; handleRequest(); @@ -128,7 +128,7 @@ protected: void createErrorResponse(const QString &msg) { - QJsonValue command = req.value(QStringLiteral("command")); + QJsonValue command = req.value(QLatin1String("command")); response.insert(QStringLiteral("command"), command); addRequestSequence(); addSuccess(false); @@ -152,11 +152,11 @@ class UnknownV8CommandHandler: public V8CommandHandler public: UnknownV8CommandHandler(): V8CommandHandler(QString()) {} - virtual void handleRequest() + void handleRequest() override { - QString msg = QStringLiteral("unimplemented command \""); - msg += req.value(QStringLiteral("command")).toString(); - msg += QLatin1Char('"'); + QString msg = QLatin1String("unimplemented command \"") + + req.value(QLatin1String("command")).toString() + + QLatin1Char('"'); createErrorResponse(msg); } }; @@ -167,7 +167,7 @@ class V8VersionRequest: public V8CommandHandler public: V8VersionRequest(): V8CommandHandler(QStringLiteral("version")) {} - virtual void handleRequest() + void handleRequest() override { addCommand(); addRequestSequence(); @@ -186,26 +186,26 @@ class V8SetBreakPointRequest: public V8CommandHandler public: V8SetBreakPointRequest(): V8CommandHandler(QStringLiteral("setbreakpoint")) {} - virtual void handleRequest() + void handleRequest() override { // decypher the payload: - QJsonObject args = req.value(QStringLiteral("arguments")).toObject(); + QJsonObject args = req.value(QLatin1String("arguments")).toObject(); if (args.isEmpty()) return; - QString type = args.value(QStringLiteral("type")).toString(); - if (type != QStringLiteral("scriptRegExp")) { + QString type = args.value(QLatin1String("type")).toString(); + if (type != QLatin1String("scriptRegExp")) { createErrorResponse(QStringLiteral("breakpoint type \"%1\" is not implemented").arg(type)); return; } - QString fileName = args.value(QStringLiteral("target")).toString(); + QString fileName = args.value(QLatin1String("target")).toString(); if (fileName.isEmpty()) { createErrorResponse(QStringLiteral("breakpoint has no file name")); return; } - int line = args.value(QStringLiteral("line")).toInt(-1); + int line = args.value(QLatin1String("line")).toInt(-1); if (line < 0) { createErrorResponse(QStringLiteral("breakpoint has an invalid line number")); return; @@ -237,14 +237,14 @@ class V8ClearBreakPointRequest: public V8CommandHandler public: V8ClearBreakPointRequest(): V8CommandHandler(QStringLiteral("clearbreakpoint")) {} - virtual void handleRequest() + void handleRequest() override { // decypher the payload: - QJsonObject args = req.value(QStringLiteral("arguments")).toObject(); + QJsonObject args = req.value(QLatin1String("arguments")).toObject(); if (args.isEmpty()) return; - int id = args.value(QStringLiteral("breakpoint")).toInt(-1); + int id = args.value(QLatin1String("breakpoint")).toInt(-1); if (id < 0) { createErrorResponse(QStringLiteral("breakpoint has an invalid number")); return; @@ -270,13 +270,13 @@ class V8BacktraceRequest: public V8CommandHandler public: V8BacktraceRequest(): V8CommandHandler(QStringLiteral("backtrace")) {} - virtual void handleRequest() + void handleRequest() override { // decypher the payload: - QJsonObject arguments = req.value(QStringLiteral("arguments")).toObject(); - int fromFrame = arguments.value(QStringLiteral("fromFrame")).toInt(0); - int toFrame = arguments.value(QStringLiteral("toFrame")).toInt(fromFrame + 10); + QJsonObject arguments = req.value(QLatin1String("arguments")).toObject(); + int fromFrame = arguments.value(QLatin1String("fromFrame")).toInt(0); + int toFrame = arguments.value(QLatin1String("toFrame")).toInt(fromFrame + 10); // no idea what the bottom property is for, so we'll ignore it. QV4Debugger *debugger = debugService->debuggerAgent.pausedDebugger(); @@ -303,11 +303,11 @@ class V8FrameRequest: public V8CommandHandler public: V8FrameRequest(): V8CommandHandler(QStringLiteral("frame")) {} - virtual void handleRequest() + void handleRequest() override { // decypher the payload: - QJsonObject arguments = req.value(QStringLiteral("arguments")).toObject(); - const int frameNr = arguments.value(QStringLiteral("number")).toInt( + QJsonObject arguments = req.value(QLatin1String("arguments")).toObject(); + const int frameNr = arguments.value(QLatin1String("number")).toInt( debugService->selectedFrame()); QV4Debugger *debugger = debugService->debuggerAgent.pausedDebugger(); @@ -345,13 +345,13 @@ class V8ScopeRequest: public V8CommandHandler public: V8ScopeRequest(): V8CommandHandler(QStringLiteral("scope")) {} - virtual void handleRequest() + void handleRequest() override { // decypher the payload: - QJsonObject arguments = req.value(QStringLiteral("arguments")).toObject(); - const int frameNr = arguments.value(QStringLiteral("frameNumber")).toInt( + QJsonObject arguments = req.value(QLatin1String("arguments")).toObject(); + const int frameNr = arguments.value(QLatin1String("frameNumber")).toInt( debugService->selectedFrame()); - const int scopeNr = arguments.value(QStringLiteral("number")).toInt(0); + const int scopeNr = arguments.value(QLatin1String("number")).toInt(0); QV4Debugger *debugger = debugService->debuggerAgent.pausedDebugger(); if (!debugger) { @@ -390,11 +390,11 @@ class V8LookupRequest: public V8CommandHandler public: V8LookupRequest(): V8CommandHandler(QStringLiteral("lookup")) {} - virtual void handleRequest() + void handleRequest() override { // decypher the payload: - QJsonObject arguments = req.value(QStringLiteral("arguments")).toObject(); - QJsonArray handles = arguments.value(QStringLiteral("handles")).toArray(); + QJsonObject arguments = req.value(QLatin1String("arguments")).toObject(); + QJsonArray handles = arguments.value(QLatin1String("handles")).toArray(); QV4Debugger *debugger = debugService->debuggerAgent.pausedDebugger(); if (!debugger) { @@ -430,10 +430,10 @@ class V8ContinueRequest: public V8CommandHandler public: V8ContinueRequest(): V8CommandHandler(QStringLiteral("continue")) {} - virtual void handleRequest() + void handleRequest() override { // decypher the payload: - QJsonObject arguments = req.value(QStringLiteral("arguments")).toObject(); + QJsonObject arguments = req.value(QLatin1String("arguments")).toObject(); QV4Debugger *debugger = debugService->debuggerAgent.pausedDebugger(); if (!debugger) { @@ -445,17 +445,17 @@ public: if (arguments.empty()) { debugger->resume(QV4Debugger::FullThrottle); } else { - QJsonObject arguments = req.value(QStringLiteral("arguments")).toObject(); - QString stepAction = arguments.value(QStringLiteral("stepaction")).toString(); - const int stepcount = arguments.value(QStringLiteral("stepcount")).toInt(1); + QJsonObject arguments = req.value(QLatin1String("arguments")).toObject(); + QString stepAction = arguments.value(QLatin1String("stepaction")).toString(); + const int stepcount = arguments.value(QLatin1String("stepcount")).toInt(1); if (stepcount != 1) qWarning() << "Step count other than 1 is not supported."; - if (stepAction == QStringLiteral("in")) { + if (stepAction == QLatin1String("in")) { debugger->resume(QV4Debugger::StepIn); - } else if (stepAction == QStringLiteral("out")) { + } else if (stepAction == QLatin1String("out")) { debugger->resume(QV4Debugger::StepOut); - } else if (stepAction == QStringLiteral("next")) { + } else if (stepAction == QLatin1String("next")) { debugger->resume(QV4Debugger::StepOver); } else { createErrorResponse(QStringLiteral("continue command has invalid stepaction")); @@ -476,7 +476,7 @@ class V8DisconnectRequest: public V8CommandHandler public: V8DisconnectRequest(): V8CommandHandler(QStringLiteral("disconnect")) {} - virtual void handleRequest() + void handleRequest() override { debugService->debuggerAgent.removeAllBreakPoints(); debugService->debuggerAgent.resumeAll(); @@ -494,18 +494,18 @@ class V8SetExceptionBreakRequest: public V8CommandHandler public: V8SetExceptionBreakRequest(): V8CommandHandler(QStringLiteral("setexceptionbreak")) {} - virtual void handleRequest() + void handleRequest() override { bool wasEnabled = debugService->debuggerAgent.breakOnThrow(); //decypher the payload: - QJsonObject arguments = req.value(QStringLiteral("arguments")).toObject(); - QString type = arguments.value(QStringLiteral("type")).toString(); - bool enabled = arguments.value(QStringLiteral("number")).toBool(!wasEnabled); + QJsonObject arguments = req.value(QLatin1String("arguments")).toObject(); + QString type = arguments.value(QLatin1String("type")).toString(); + bool enabled = arguments.value(QLatin1String("number")).toBool(!wasEnabled); - if (type == QStringLiteral("all")) { + if (type == QLatin1String("all")) { // that's fine - } else if (type == QStringLiteral("uncaught")) { + } else if (type == QLatin1String("uncaught")) { createErrorResponse(QStringLiteral("breaking only on uncaught exceptions is not supported yet")); return; } else { @@ -534,11 +534,11 @@ class V8ScriptsRequest: public V8CommandHandler public: V8ScriptsRequest(): V8CommandHandler(QStringLiteral("scripts")) {} - virtual void handleRequest() + void handleRequest() override { //decypher the payload: - QJsonObject arguments = req.value(QStringLiteral("arguments")).toObject(); - int types = arguments.value(QStringLiteral("types")).toInt(-1); + QJsonObject arguments = req.value(QLatin1String("arguments")).toObject(); + int types = arguments.value(QLatin1String("types")).toInt(-1); if (types < 0 || types > 7) { createErrorResponse(QStringLiteral("invalid types value in scripts command")); return; @@ -558,7 +558,7 @@ public: debugger->runInEngine(&job); QJsonArray body; - foreach (const QString &source, job.result()) { + for (const QString &source : job.result()) { QJsonObject src; src[QLatin1String("name")] = source; src[QLatin1String("scriptType")] = 4; @@ -606,10 +606,10 @@ class V8EvaluateRequest: public V8CommandHandler public: V8EvaluateRequest(): V8CommandHandler(QStringLiteral("evaluate")) {} - virtual void handleRequest() + void handleRequest() override { - QJsonObject arguments = req.value(QStringLiteral("arguments")).toObject(); - QString expression = arguments.value(QStringLiteral("expression")).toString(); + QJsonObject arguments = req.value(QLatin1String("arguments")).toObject(); + QString expression = arguments.value(QLatin1String("expression")).toString(); int frame = -1; QV4Debugger *debugger = debugService->debuggerAgent.pausedDebugger(); @@ -624,7 +624,7 @@ public: } debugger = debuggers.first(); } else { - frame = arguments.value(QStringLiteral("frame")).toInt(0); + frame = arguments.value(QLatin1String("frame")).toInt(0); } ExpressionEvalJob job(debugger->engine(), frame, expression, debugger->collector()); @@ -706,7 +706,7 @@ void QV4DebugServiceImpl::engineAboutToBeRemoved(QJSEngine *engine) if (engine){ const QV4::ExecutionEngine *ee = QV8Engine::getV4(engine->handle()); if (ee) { - QV4Debugger *debugger = qobject_cast<QV4Debugger *>(ee->debugger); + QV4Debugger *debugger = qobject_cast<QV4Debugger *>(ee->debugger()); if (debugger) debuggerAgent.removeDebugger(debugger); } @@ -718,9 +718,10 @@ void QV4DebugServiceImpl::stateAboutToBeChanged(State state) { QMutexLocker lock(&m_configMutex); if (state == Enabled) { - foreach (QV4Debugger *debugger, debuggerAgent.debuggers()) { + const auto debuggers = debuggerAgent.debuggers(); + for (QV4Debugger *debugger : debuggers) { QV4::ExecutionEngine *ee = debugger->engine(); - if (!ee->debugger) + if (!ee->debugger()) ee->setDebugger(debugger); } } @@ -737,7 +738,7 @@ void QV4DebugServiceImpl::signalEmitted(const QString &signal) //Normalize to Lower case. QString signalName = signal.left(signal.indexOf(QLatin1Char('('))).toLower(); - foreach (const QString &signal, breakOnSignals) { + for (const QString &signal : qAsConst(breakOnSignals)) { if (signal == signalName) { // TODO: pause debugger break; @@ -802,9 +803,9 @@ void QV4DebugServiceImpl::handleV8Request(const QByteArray &payload) QJsonDocument request = QJsonDocument::fromJson(payload); QJsonObject o = request.object(); - QJsonValue type = o.value(QStringLiteral("type")); - if (type.toString() == QStringLiteral("request")) { - QJsonValue command = o.value(QStringLiteral("command")); + QJsonValue type = o.value(QLatin1String("type")); + if (type.toString() == QLatin1String("request")) { + QJsonValue command = o.value(QLatin1String("command")); V8CommandHandler *h = v8CommandHandler(command.toString()); if (h) h->handle(o, this); diff --git a/src/plugins/qmltooling/qmldbg_inspector/globalinspector.cpp b/src/plugins/qmltooling/qmldbg_inspector/globalinspector.cpp index 2150b68f32..eb254ca1e0 100644 --- a/src/plugins/qmltooling/qmldbg_inspector/globalinspector.cpp +++ b/src/plugins/qmltooling/qmldbg_inspector/globalinspector.cpp @@ -92,7 +92,7 @@ void GlobalInspector::setSelectedItems(const QList<QQuickItem *> &items) QList<QObject*> objectList; objectList.reserve(items.count()); - foreach (QQuickItem *item, items) + for (QQuickItem *item : items) objectList << item; sendCurrentObjects(objectList); @@ -113,7 +113,7 @@ void GlobalInspector::sendCurrentObjects(const QList<QObject*> &objects) QList<int> debugIds; debugIds.reserve(objects.count()); - foreach (QObject *object, objects) + for (QObject *object : objects) debugIds << QQmlDebugService::idForObject(object); ds << debugIds; @@ -148,10 +148,6 @@ public: m_component.setData(qml, filename); } -signals: - void result(int requestId, bool success); - -public slots: void tryCreateObject(QQmlComponent::Status status) { switch (status) { @@ -171,7 +167,7 @@ public slots: else emit result(m_requestId, false); } - delete this; + deleteLater(); // The component might send more signals return; } default: @@ -179,6 +175,9 @@ public slots: } } +signals: + void result(int requestId, bool success); + private: QQmlComponent m_component; int m_requestId; @@ -195,7 +194,7 @@ bool GlobalInspector::createQmlObject(int requestId, const QString &qml, QObject return false; QString imports; - foreach (const QString &s, importList) + for (const QString &s : importList) imports += s + QLatin1Char('\n'); ObjectCreator *objectCreator = new ObjectCreator(requestId, parentContext->engine(), parent); @@ -224,7 +223,7 @@ void GlobalInspector::removeWindow(QQuickWindow *window) void GlobalInspector::setParentWindow(QQuickWindow *window, QWindow *parentWindow) { - foreach (QmlJSDebugger::QQuickWindowInspector *inspector, m_windowInspectors) { + for (QmlJSDebugger::QQuickWindowInspector *inspector : qAsConst(m_windowInspectors)) { if (inspector->quickWindow() == window) inspector->setParentWindow(parentWindow); } @@ -235,7 +234,8 @@ bool GlobalInspector::syncSelectedItems(const QList<QQuickItem *> &items) bool selectionChanged = false; // Disconnect and remove items that are no longer selected - foreach (const QPointer<QQuickItem> &item, m_selectedItems) { + const auto selectedItemsCopy = m_selectedItems; + for (const QPointer<QQuickItem> &item : selectedItemsCopy) { if (!item) // Don't see how this can happen due to handling of destroyed() continue; if (items.contains(item)) @@ -248,14 +248,14 @@ bool GlobalInspector::syncSelectedItems(const QList<QQuickItem *> &items) } // Connect and add newly selected items - foreach (QQuickItem *item, items) { + for (QQuickItem *item : items) { if (m_selectedItems.contains(item)) continue; selectionChanged = true; connect(item, &QObject::destroyed, this, &GlobalInspector::removeFromSelectedItems); m_selectedItems.append(item); - foreach (QQuickWindowInspector *inspector, m_windowInspectors) { + for (QQuickWindowInspector *inspector : qAsConst(m_windowInspectors)) { if (inspector->isEnabled() && inspector->quickWindow() == item->window()) { m_highlightItems.insert(item, new SelectionHighlight(titleForItem(item), item, inspector->overlay())); @@ -315,12 +315,12 @@ void GlobalInspector::processMessage(const QByteArray &message) ds >> requestId >> command; if (command == ENABLE) { - foreach (QQuickWindowInspector *inspector, m_windowInspectors) + for (QQuickWindowInspector *inspector : qAsConst(m_windowInspectors)) inspector->setEnabled(true); success = !m_windowInspectors.isEmpty(); } else if (command == DISABLE) { setSelectedItems(QList<QQuickItem*>()); - foreach (QQuickWindowInspector *inspector, m_windowInspectors) + for (QQuickWindowInspector *inspector : qAsConst(m_windowInspectors)) inspector->setEnabled(false); success = !m_windowInspectors.isEmpty(); } else if (command == SELECT) { @@ -328,7 +328,7 @@ void GlobalInspector::processMessage(const QByteArray &message) ds >> debugIds; QList<QQuickItem *> selectedObjects; - foreach (int debugId, debugIds) { + for (int debugId : qAsConst(debugIds)) { if (QQuickItem *obj = qobject_cast<QQuickItem *>(QQmlDebugService::objectForId(debugId))) selectedObjects << obj; @@ -342,7 +342,7 @@ void GlobalInspector::processMessage(const QByteArray &message) } else if (command == SHOW_APP_ON_TOP) { bool showOnTop; ds >> showOnTop; - foreach (QmlJSDebugger::QQuickWindowInspector *inspector, m_windowInspectors) + for (QmlJSDebugger::QQuickWindowInspector *inspector : qAsConst(m_windowInspectors)) inspector->setShowAppOnTop(showOnTop); success = !m_windowInspectors.isEmpty(); } else if (command == CREATE_OBJECT) { diff --git a/src/plugins/qmltooling/qmldbg_inspector/globalinspector.h b/src/plugins/qmltooling/qmldbg_inspector/globalinspector.h index 7bbe6d6aa2..338eee14c3 100644 --- a/src/plugins/qmltooling/qmldbg_inspector/globalinspector.h +++ b/src/plugins/qmltooling/qmldbg_inspector/globalinspector.h @@ -72,10 +72,8 @@ public: signals: void messageToClient(const QString &name, const QByteArray &data); -private slots: - void sendResult(int requestId, bool success); - private: + void sendResult(int requestId, bool success); void sendCurrentObjects(const QList<QObject *> &objects); void removeFromSelectedItems(QObject *object); QString titleForItem(QQuickItem *item) const; diff --git a/src/plugins/qmltooling/qmldbg_inspector/highlight.cpp b/src/plugins/qmltooling/qmldbg_inspector/highlight.cpp index 26eb0f8ed8..88a6ea6b6d 100644 --- a/src/plugins/qmltooling/qmldbg_inspector/highlight.cpp +++ b/src/plugins/qmltooling/qmldbg_inspector/highlight.cpp @@ -72,24 +72,22 @@ void Highlight::setItem(QQuickItem *item) m_item->disconnect(this); if (item) { - connect(item, SIGNAL(xChanged()), SLOT(adjust())); - connect(item, SIGNAL(yChanged()), SLOT(adjust())); - connect(item, SIGNAL(widthChanged()), SLOT(adjust())); - connect(item, SIGNAL(heightChanged()), SLOT(adjust())); - connect(item, SIGNAL(rotationChanged()), SLOT(adjust())); - connect(item, SIGNAL(transformOriginChanged(TransformOrigin)), - SLOT(adjust())); + connect(item, &QQuickItem::xChanged, this, &Highlight::adjust); + connect(item, &QQuickItem::yChanged, this, &Highlight::adjust); + connect(item, &QQuickItem::widthChanged, this, &Highlight::adjust); + connect(item, &QQuickItem::heightChanged, this, &Highlight::adjust); + connect(item, &QQuickItem::rotationChanged, this, &Highlight::adjust); + connect(item, &QQuickItem::transformOriginChanged, this, &Highlight::adjust); } QQuickWindow *view = item->window(); QQuickItem * contentItem = view->contentItem(); if (contentItem) { - connect(contentItem, SIGNAL(xChanged()), SLOT(adjust())); - connect(contentItem, SIGNAL(yChanged()), SLOT(adjust())); - connect(contentItem, SIGNAL(widthChanged()), SLOT(adjust())); - connect(contentItem, SIGNAL(heightChanged()), SLOT(adjust())); - connect(contentItem, SIGNAL(rotationChanged()), SLOT(adjust())); - connect(contentItem, SIGNAL(transformOriginChanged(TransformOrigin)), - SLOT(adjust())); + connect(contentItem, &QQuickItem::xChanged, this, &Highlight::adjust); + connect(contentItem, &QQuickItem::yChanged, this, &Highlight::adjust); + connect(contentItem, &QQuickItem::widthChanged, this, &Highlight::adjust); + connect(contentItem, &QQuickItem::heightChanged, this, &Highlight::adjust); + connect(contentItem, &QQuickItem::rotationChanged, this, &Highlight::adjust); + connect(contentItem, &QQuickItem::transformOriginChanged, this, &Highlight::adjust); } m_item = item; setContentsSize(view->size()); diff --git a/src/plugins/qmltooling/qmldbg_inspector/highlight.h b/src/plugins/qmltooling/qmldbg_inspector/highlight.h index 4a85cb4d50..2bf4fc1ad5 100644 --- a/src/plugins/qmltooling/qmldbg_inspector/highlight.h +++ b/src/plugins/qmltooling/qmldbg_inspector/highlight.h @@ -65,8 +65,6 @@ protected: private: void initRenderDetails(); - -private slots: void adjust(); private: @@ -86,13 +84,12 @@ public: void paint(QPainter *painter); void showName(const QPointF &displayPoint); -private slots: - void disableNameDisplay(); - private: QPointF m_displayPoint; QString m_name; bool m_nameDisplayActive; + + void disableNameDisplay(); }; /** diff --git a/src/plugins/qmltooling/qmldbg_inspector/qqmlinspectorservice.cpp b/src/plugins/qmltooling/qmldbg_inspector/qqmlinspectorservice.cpp index 48a3f656b0..ab1aeebf64 100644 --- a/src/plugins/qmltooling/qmldbg_inspector/qqmlinspectorservice.cpp +++ b/src/plugins/qmltooling/qmldbg_inspector/qqmlinspectorservice.cpp @@ -55,23 +55,27 @@ public: void setParentWindow(QQuickWindow *window, QWindow *parent) Q_DECL_OVERRIDE; void removeWindow(QQuickWindow *window) Q_DECL_OVERRIDE; +signals: + void scheduleMessage(const QByteArray &message); + protected: virtual void messageReceived(const QByteArray &) Q_DECL_OVERRIDE; -private slots: - void messageFromClient(const QByteArray &message); - private: friend class QQmlInspectorServiceFactory; QmlJSDebugger::GlobalInspector *checkInspector(); QmlJSDebugger::GlobalInspector *m_globalInspector; QHash<QQuickWindow *, QWindow *> m_waitingWindows; + + void messageFromClient(const QByteArray &message); }; QQmlInspectorServiceImpl::QQmlInspectorServiceImpl(QObject *parent): QQmlInspectorService(1, parent), m_globalInspector(0) { + connect(this, &QQmlInspectorServiceImpl::scheduleMessage, + this, &QQmlInspectorServiceImpl::messageFromClient, Qt::QueuedConnection); } QmlJSDebugger::GlobalInspector *QQmlInspectorServiceImpl::checkInspector() @@ -122,8 +126,8 @@ void QQmlInspectorServiceImpl::setParentWindow(QQuickWindow *window, QWindow *pa void QQmlInspectorServiceImpl::messageReceived(const QByteArray &message) { - QMetaObject::invokeMethod(this, "messageFromClient", Qt::QueuedConnection, - Q_ARG(QByteArray, message)); + // Move the message to the right thread via queued signal + emit scheduleMessage(message); } void QQmlInspectorServiceImpl::messageFromClient(const QByteArray &message) diff --git a/src/plugins/qmltooling/qmldbg_local/qlocalclientconnection.cpp b/src/plugins/qmltooling/qmldbg_local/qlocalclientconnection.cpp index 01c24f2395..64b26bdd0d 100644 --- a/src/plugins/qmltooling/qmldbg_local/qlocalclientconnection.cpp +++ b/src/plugins/qmltooling/qmldbg_local/qlocalclientconnection.cpp @@ -65,10 +65,8 @@ public: void waitForConnection(); void flush(); -private slots: - void connectionEstablished(); - private: + void connectionEstablished(); bool connectToServer(); bool m_block; @@ -135,7 +133,8 @@ bool QLocalClientConnection::connectToServer() { m_socket = new QLocalSocket; m_socket->setParent(this); - QObject::connect(m_socket, SIGNAL(connected()), this, SLOT(connectionEstablished())); + QObject::connect(m_socket, &QLocalSocket::connected, + this, &QLocalClientConnection::connectionEstablished); m_socket->connectToServer(m_filename); qDebug("QML Debugger: Connecting to socket %s...", m_filename.toLatin1().constData()); return true; diff --git a/src/plugins/qmltooling/qmldbg_native/qqmlnativedebugconnector.cpp b/src/plugins/qmltooling/qmldbg_native/qqmlnativedebugconnector.cpp index 3145601612..9eeb285951 100644 --- a/src/plugins/qmltooling/qmldbg_native/qqmlnativedebugconnector.cpp +++ b/src/plugins/qmltooling/qmldbg_native/qqmlnativedebugconnector.cpp @@ -205,7 +205,7 @@ QQmlNativeDebugConnector::QQmlNativeDebugConnector() QQmlNativeDebugConnector::~QQmlNativeDebugConnector() { - foreach (QQmlDebugService *service, m_services) { + for (QQmlDebugService *service : qAsConst(m_services)) { service->stateAboutToBeChanged(QQmlDebugService::NotConnected); service->setState(QQmlDebugService::NotConnected); service->stateChanged(QQmlDebugService::NotConnected); @@ -232,12 +232,12 @@ void QQmlNativeDebugConnector::addEngine(QJSEngine *engine) Q_ASSERT(!m_engines.contains(engine)); TRACE_PROTOCOL("Add engine to connector:" << engine); - foreach (QQmlDebugService *service, m_services) + for (QQmlDebugService *service : qAsConst(m_services)) service->engineAboutToBeAdded(engine); announceObjectAvailability(QLatin1String("qmlengine"), engine, true); - foreach (QQmlDebugService *service, m_services) + for (QQmlDebugService *service : qAsConst(m_services)) service->engineAdded(engine); m_engines.append(engine); @@ -248,12 +248,12 @@ void QQmlNativeDebugConnector::removeEngine(QJSEngine *engine) Q_ASSERT(m_engines.contains(engine)); TRACE_PROTOCOL("Remove engine from connector:" << engine); - foreach (QQmlDebugService *service, m_services) + for (QQmlDebugService *service : qAsConst(m_services)) service->engineAboutToBeRemoved(engine); announceObjectAvailability(QLatin1String("qmlengine"), engine, false); - foreach (QQmlDebugService *service, m_services) + for (QQmlDebugService *service : qAsConst(m_services)) service->engineRemoved(engine); m_engines.removeOne(engine); diff --git a/src/plugins/qmltooling/qmldbg_native/qqmlnativedebugconnector.h b/src/plugins/qmltooling/qmldbg_native/qqmlnativedebugconnector.h index 03b5b5eb1e..1184925e53 100644 --- a/src/plugins/qmltooling/qmldbg_native/qqmlnativedebugconnector.h +++ b/src/plugins/qmltooling/qmldbg_native/qqmlnativedebugconnector.h @@ -63,11 +63,9 @@ public: bool open(const QVariantHash &configuration) Q_DECL_OVERRIDE; static void setDataStreamVersion(int version); -private slots: +private: void sendMessage(const QString &name, const QByteArray &message); void sendMessages(const QString &name, const QList<QByteArray> &messages); - -private: void announceObjectAvailability(const QString &objectType, QObject *object, bool available); QVector<QQmlDebugService *> m_services; diff --git a/src/plugins/qmltooling/qmldbg_profiler/qqmlenginecontrolservice.cpp b/src/plugins/qmltooling/qmldbg_profiler/qqmlenginecontrolservice.cpp index 6b653d5a54..0ed13d5105 100644 --- a/src/plugins/qmltooling/qmldbg_profiler/qqmlenginecontrolservice.cpp +++ b/src/plugins/qmltooling/qmldbg_profiler/qqmlenginecontrolservice.cpp @@ -122,10 +122,10 @@ void QQmlEngineControlServiceImpl::stateChanged(State) { // We flush everything for any kind of state change, to avoid complicated timing issues. QMutexLocker lock(&dataMutex); - foreach (QJSEngine *engine, startingEngines) + for (QJSEngine *engine : qAsConst(startingEngines)) emit attachedToEngine(engine); startingEngines.clear(); - foreach (QJSEngine *engine, stoppingEngines) + for (QJSEngine *engine : qAsConst(stoppingEngines)) emit detachedFromEngine(engine); stoppingEngines.clear(); } diff --git a/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.cpp b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.cpp index f161f988de..a4320098c0 100644 --- a/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.cpp +++ b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.cpp @@ -48,20 +48,21 @@ QQmlProfilerAdapter::QQmlProfilerAdapter(QQmlProfilerService *service, QQmlEngin next(0) { setService(service); - engine->enableProfiler(); - connect(this, SIGNAL(profilingEnabled(quint64)), engine->profiler, SLOT(startProfiling(quint64))); - connect(this, SIGNAL(profilingEnabledWhileWaiting(quint64)), - engine->profiler, SLOT(startProfiling(quint64)), Qt::DirectConnection); - connect(this, SIGNAL(profilingDisabled()), engine->profiler, SLOT(stopProfiling())); - connect(this, SIGNAL(profilingDisabledWhileWaiting()), - engine->profiler, SLOT(stopProfiling()), Qt::DirectConnection); - connect(this, SIGNAL(dataRequested(bool)), engine->profiler, SLOT(reportData(bool))); - connect(this, SIGNAL(referenceTimeKnown(QElapsedTimer)), - engine->profiler, SLOT(setTimer(QElapsedTimer))); - connect(engine->profiler, - SIGNAL(dataReady(QVector<QQmlProfilerData>,QQmlProfiler::LocationHash)), - this, - SLOT(receiveData(QVector<QQmlProfilerData>,QQmlProfiler::LocationHash))); + engine->profiler = new QQmlProfiler; + connect(this, &QQmlProfilerAdapter::profilingEnabled, + engine->profiler, &QQmlProfiler::startProfiling); + connect(this, &QQmlAbstractProfilerAdapter::profilingEnabledWhileWaiting, + engine->profiler, &QQmlProfiler::startProfiling, Qt::DirectConnection); + connect(this, &QQmlAbstractProfilerAdapter::profilingDisabled, + engine->profiler, &QQmlProfiler::stopProfiling); + connect(this, &QQmlAbstractProfilerAdapter::profilingDisabledWhileWaiting, + engine->profiler, &QQmlProfiler::stopProfiling, Qt::DirectConnection); + connect(this, &QQmlAbstractProfilerAdapter::dataRequested, + engine->profiler, &QQmlProfiler::reportData); + connect(this, &QQmlAbstractProfilerAdapter::referenceTimeKnown, + engine->profiler, &QQmlProfiler::setTimer); + connect(engine->profiler, &QQmlProfiler::dataReady, + this, &QQmlProfilerAdapter::receiveData); } // convert to QByteArrays that can be sent to the debug client diff --git a/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.h b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.h index 96cdcd6d38..1fee5c389f 100644 --- a/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.h +++ b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.h @@ -63,7 +63,6 @@ public: qint64 sendMessages(qint64 until, QList<QByteArray> &messages, bool trackLocations) Q_DECL_OVERRIDE; -public slots: void receiveData(const QVector<QQmlProfilerData> &new_data, const QQmlProfiler::LocationHash &locations); diff --git a/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.cpp b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.cpp index a587188630..c1f6f93ef1 100644 --- a/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.cpp +++ b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.cpp @@ -51,6 +51,8 @@ #include <QtCore/qthread.h> #include <QtCore/qcoreapplication.h> +#include <algorithm> + QT_BEGIN_NAMESPACE Q_QML_DEBUG_PLUGIN_LOADER(QQmlAbstractProfilerAdapter) @@ -92,16 +94,18 @@ void QQmlProfilerServiceImpl::dataReady(QQmlAbstractProfilerAdapter *profiler) m_startTimes.insert(0, profiler); if (dataComplete) { QList<QJSEngine *> enginesToRelease; - foreach (QJSEngine *engine, m_stoppingEngines) { - foreach (QQmlAbstractProfilerAdapter *engineProfiler, m_engineProfilers.values(engine)) { - if (m_startTimes.values().contains(engineProfiler)) { + for (QJSEngine *engine : qAsConst(m_stoppingEngines)) { + const auto range = qAsConst(m_engineProfilers).equal_range(engine); + const auto startTimesEnd = m_startTimes.cend(); + for (auto it = range.first; it != range.second; ++it) { + if (std::find(m_startTimes.cbegin(), startTimesEnd, *it) != startTimesEnd) { enginesToRelease.append(engine); break; } } } sendMessages(); - foreach (QJSEngine *engine, enginesToRelease) { + for (QJSEngine *engine : qAsConst(enginesToRelease)) { m_stoppingEngines.removeOne(engine); emit detachedFromEngine(engine); } @@ -130,8 +134,9 @@ void QQmlProfilerServiceImpl::engineAdded(QJSEngine *engine) "QML profilers have to be added from the engine thread"); QMutexLocker lock(&m_configMutex); - foreach (QQmlAbstractProfilerAdapter *profiler, m_engineProfilers.values(engine)) - profiler->stopWaiting(); + const auto range = qAsConst(m_engineProfilers).equal_range(engine); + for (auto it = range.first; it != range.second; ++it) + (*it)->stopWaiting(); } void QQmlProfilerServiceImpl::engineAboutToBeRemoved(QJSEngine *engine) @@ -141,7 +146,9 @@ void QQmlProfilerServiceImpl::engineAboutToBeRemoved(QJSEngine *engine) QMutexLocker lock(&m_configMutex); bool isRunning = false; - foreach (QQmlAbstractProfilerAdapter *profiler, m_engineProfilers.values(engine)) { + const auto range = qAsConst(m_engineProfilers).equal_range(engine); + for (auto it = range.first; it != range.second; ++it) { + QQmlAbstractProfilerAdapter *profiler = *it; if (profiler->isRunning()) isRunning = true; profiler->startWaiting(); @@ -160,7 +167,9 @@ void QQmlProfilerServiceImpl::engineRemoved(QJSEngine *engine) "QML profilers have to be removed from the engine thread"); QMutexLocker lock(&m_configMutex); - foreach (QQmlAbstractProfilerAdapter *profiler, m_engineProfilers.values(engine)) { + const auto range = qAsConst(m_engineProfilers).equal_range(engine); + for (auto it = range.first; it != range.second; ++it) { + QQmlAbstractProfilerAdapter *profiler = *it; removeProfilerFromStartTimes(profiler); delete profiler; } @@ -183,7 +192,7 @@ void QQmlProfilerServiceImpl::addGlobalProfiler(QQmlAbstractProfilerAdapter *pro // Global profilers are started whenever any engine profiler is started and stopped when // all engine profilers are stopped. quint64 features = 0; - foreach (QQmlAbstractProfilerAdapter *engineProfiler, m_engineProfilers) + for (QQmlAbstractProfilerAdapter *engineProfiler : qAsConst(m_engineProfilers)) features |= engineProfiler->features(); if (features != 0) @@ -231,7 +240,9 @@ void QQmlProfilerServiceImpl::startProfiling(QJSEngine *engine, quint64 features d << m_timer.nsecsElapsed() << (int)Event << (int)StartTrace; bool startedAny = false; if (engine != 0) { - foreach (QQmlAbstractProfilerAdapter *profiler, m_engineProfilers.values(engine)) { + const auto range = qAsConst(m_engineProfilers).equal_range(engine); + for (auto it = range.first; it != range.second; ++it) { + QQmlAbstractProfilerAdapter *profiler = *it; if (!profiler->isRunning()) { profiler->startProfiling(features); startedAny = true; @@ -249,12 +260,12 @@ void QQmlProfilerServiceImpl::startProfiling(QJSEngine *engine, quint64 features startedAny = true; } } - foreach (QJSEngine *profiledEngine, engines) + for (QJSEngine *profiledEngine : qAsConst(engines)) d << idForObject(profiledEngine); } if (startedAny) { - foreach (QQmlAbstractProfilerAdapter *profiler, m_globalProfilers) { + for (QQmlAbstractProfilerAdapter *profiler : qAsConst(m_globalProfilers)) { if (!profiler->isRunning()) profiler->startProfiling(features); } @@ -294,7 +305,7 @@ void QQmlProfilerServiceImpl::stopProfiling(QJSEngine *engine) if (stopping.isEmpty()) return; - foreach (QQmlAbstractProfilerAdapter *profiler, m_globalProfilers) { + for (QQmlAbstractProfilerAdapter *profiler : qAsConst(m_globalProfilers)) { if (!profiler->isRunning()) continue; m_startTimes.insert(-1, profiler); @@ -308,10 +319,10 @@ void QQmlProfilerServiceImpl::stopProfiling(QJSEngine *engine) emit stopFlushTimer(); m_waitingForStop = true; - foreach (QQmlAbstractProfilerAdapter *profiler, reporting) + for (QQmlAbstractProfilerAdapter *profiler : qAsConst(reporting)) profiler->reportData(m_useMessageTypes); - foreach (QQmlAbstractProfilerAdapter *profiler, stopping) + for (QQmlAbstractProfilerAdapter *profiler : qAsConst(stopping)) profiler->stopProfiling(); } @@ -327,7 +338,7 @@ void QQmlProfilerServiceImpl::sendMessages() traceEnd << m_timer.nsecsElapsed() << (int)Event << (int)EndTrace; QSet<QJSEngine *> seen; - foreach (QQmlAbstractProfilerAdapter *profiler, m_startTimes) { + for (QQmlAbstractProfilerAdapter *profiler : qAsConst(m_startTimes)) { for (QMultiHash<QJSEngine *, QQmlAbstractProfilerAdapter *>::iterator i(m_engineProfilers.begin()); i != m_engineProfilers.end(); ++i) { if (i.value() == profiler && !seen.contains(i.key())) { @@ -367,7 +378,7 @@ void QQmlProfilerServiceImpl::sendMessages() emit messagesToClient(name(), messages); // Restart flushing if any profilers are still running - foreach (const QQmlAbstractProfilerAdapter *profiler, m_engineProfilers) { + for (const QQmlAbstractProfilerAdapter *profiler : qAsConst(m_engineProfilers)) { if (profiler->isRunning()) { emit startFlushTimer(); break; @@ -409,14 +420,16 @@ void QQmlProfilerServiceImpl::messageReceived(const QByteArray &message) if (!stream.atEnd()) { stream >> flushInterval; m_flushTimer.setInterval(flushInterval); + auto timerStart = static_cast<void(QTimer::*)()>(&QTimer::start); if (flushInterval > 0) { - connect(&m_flushTimer, SIGNAL(timeout()), this, SLOT(flush())); - connect(this, SIGNAL(startFlushTimer()), &m_flushTimer, SLOT(start())); - connect(this, SIGNAL(stopFlushTimer()), &m_flushTimer, SLOT(stop())); + connect(&m_flushTimer, &QTimer::timeout, this, &QQmlProfilerServiceImpl::flush); + connect(this, &QQmlProfilerServiceImpl::startFlushTimer, &m_flushTimer, timerStart); + connect(this, &QQmlProfilerServiceImpl::stopFlushTimer, &m_flushTimer, &QTimer::stop); } else { - disconnect(&m_flushTimer, SIGNAL(timeout()), this, SLOT(flush())); - disconnect(this, SIGNAL(startFlushTimer()), &m_flushTimer, SLOT(start())); - disconnect(this, SIGNAL(stopFlushTimer()), &m_flushTimer, SLOT(stop())); + disconnect(&m_flushTimer, &QTimer::timeout, this, &QQmlProfilerServiceImpl::flush); + disconnect(this, &QQmlProfilerServiceImpl::startFlushTimer, &m_flushTimer, timerStart); + disconnect(this, &QQmlProfilerServiceImpl::stopFlushTimer, + &m_flushTimer, &QTimer::stop); } } if (!stream.atEnd()) @@ -434,20 +447,24 @@ void QQmlProfilerServiceImpl::messageReceived(const QByteArray &message) void QQmlProfilerServiceImpl::flush() { QMutexLocker lock(&m_configMutex); + QList<QQmlAbstractProfilerAdapter *> reporting; - foreach (QQmlAbstractProfilerAdapter *profiler, m_engineProfilers) { + for (QQmlAbstractProfilerAdapter *profiler : qAsConst(m_engineProfilers)) { if (profiler->isRunning()) { m_startTimes.insert(-1, profiler); - profiler->reportData(m_useMessageTypes); + reporting.append(profiler); } } - foreach (QQmlAbstractProfilerAdapter *profiler, m_globalProfilers) { + for (QQmlAbstractProfilerAdapter *profiler : qAsConst(m_globalProfilers)) { if (profiler->isRunning()) { m_startTimes.insert(-1, profiler); - profiler->reportData(m_useMessageTypes); + reporting.append(profiler); } } + + for (QQmlAbstractProfilerAdapter *profiler : qAsConst(reporting)) + profiler->reportData(m_useMessageTypes); } QT_END_NAMESPACE diff --git a/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.h b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.h index 42efdefd12..bbfc32b681 100644 --- a/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.h +++ b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.h @@ -99,9 +99,6 @@ signals: void startFlushTimer(); void stopFlushTimer(); -private slots: - void flush(); - protected: virtual void stateAboutToBeChanged(State state) Q_DECL_OVERRIDE; virtual void messageReceived(const QByteArray &) Q_DECL_OVERRIDE; @@ -112,6 +109,7 @@ private: void sendMessages(); void addEngineProfiler(QQmlAbstractProfilerAdapter *profiler, QJSEngine *engine); void removeProfilerFromStartTimes(const QQmlAbstractProfilerAdapter *profiler); + void flush(); QElapsedTimer m_timer; QTimer m_flushTimer; diff --git a/src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.cpp b/src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.cpp index c3bbb86e3a..eee1dd7eae 100644 --- a/src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.cpp +++ b/src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.cpp @@ -46,27 +46,26 @@ QV4ProfilerAdapter::QV4ProfilerAdapter(QQmlProfilerService *service, QV4::Execut m_functionCallPos(0), m_memoryPos(0) { setService(service); - engine->enableProfiler(); - connect(this, SIGNAL(profilingEnabled(quint64)), - this, SLOT(forwardEnabled(quint64))); - connect(this, SIGNAL(profilingEnabledWhileWaiting(quint64)), - this, SLOT(forwardEnabledWhileWaiting(quint64)), Qt::DirectConnection); - connect(this, SIGNAL(v4ProfilingEnabled(quint64)), - engine->profiler, SLOT(startProfiling(quint64))); - connect(this, SIGNAL(v4ProfilingEnabledWhileWaiting(quint64)), - engine->profiler, SLOT(startProfiling(quint64)), Qt::DirectConnection); - connect(this, SIGNAL(profilingDisabled()), engine->profiler, SLOT(stopProfiling())); - connect(this, SIGNAL(profilingDisabledWhileWaiting()), engine->profiler, SLOT(stopProfiling()), + engine->setProfiler(new QV4::Profiling::Profiler(engine)); + connect(this, &QQmlAbstractProfilerAdapter::profilingEnabled, + this, &QV4ProfilerAdapter::forwardEnabled); + connect(this, &QQmlAbstractProfilerAdapter::profilingEnabledWhileWaiting, + this, &QV4ProfilerAdapter::forwardEnabledWhileWaiting, Qt::DirectConnection); + connect(this, &QV4ProfilerAdapter::v4ProfilingEnabled, + engine->profiler(), &QV4::Profiling::Profiler::startProfiling); + connect(this, &QV4ProfilerAdapter::v4ProfilingEnabledWhileWaiting, + engine->profiler(), &QV4::Profiling::Profiler::startProfiling, Qt::DirectConnection); + connect(this, &QQmlAbstractProfilerAdapter::profilingDisabled, + engine->profiler(), &QV4::Profiling::Profiler::stopProfiling); + connect(this, &QQmlAbstractProfilerAdapter::profilingDisabledWhileWaiting, + engine->profiler(), &QV4::Profiling::Profiler::stopProfiling, Qt::DirectConnection); - connect(this, SIGNAL(dataRequested(bool)), engine->profiler, SLOT(reportData(bool))); - connect(this, SIGNAL(referenceTimeKnown(QElapsedTimer)), - engine->profiler, SLOT(setTimer(QElapsedTimer))); - connect(engine->profiler, SIGNAL(dataReady(QV4::Profiling::FunctionLocationHash, - QVector<QV4::Profiling::FunctionCallProperties>, - QVector<QV4::Profiling::MemoryAllocationProperties>)), - this, SLOT(receiveData(QV4::Profiling::FunctionLocationHash, - QVector<QV4::Profiling::FunctionCallProperties>, - QVector<QV4::Profiling::MemoryAllocationProperties>))); + connect(this, &QQmlAbstractProfilerAdapter::dataRequested, + engine->profiler(), &QV4::Profiling::Profiler::reportData); + connect(this, &QQmlAbstractProfilerAdapter::referenceTimeKnown, + engine->profiler(), &QV4::Profiling::Profiler::setTimer); + connect(engine->profiler(), &QV4::Profiling::Profiler::dataReady, + this, &QV4ProfilerAdapter::receiveData); } qint64 QV4ProfilerAdapter::appendMemoryEvents(qint64 until, QList<QByteArray> &messages, diff --git a/src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.h b/src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.h index 13a595f6eb..5d5b83f7ca 100644 --- a/src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.h +++ b/src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.h @@ -70,18 +70,13 @@ public: virtual qint64 sendMessages(qint64 until, QList<QByteArray> &messages, bool trackLocations) override; -signals: - void v4ProfilingEnabled(quint64 v4Features); - void v4ProfilingEnabledWhileWaiting(quint64 v4Features); - -public slots: void receiveData(const QV4::Profiling::FunctionLocationHash &, const QVector<QV4::Profiling::FunctionCallProperties> &, const QVector<QV4::Profiling::MemoryAllocationProperties> &); -private slots: - void forwardEnabled(quint64 features); - void forwardEnabledWhileWaiting(quint64 features); +signals: + void v4ProfilingEnabled(quint64 v4Features); + void v4ProfilingEnabledWhileWaiting(quint64 v4Features); private: QV4::Profiling::FunctionLocationHash m_functionLocations; @@ -93,6 +88,8 @@ private: qint64 appendMemoryEvents(qint64 until, QList<QByteArray> &messages, QQmlDebugPacket &d); qint64 finalizeMessages(qint64 until, QList<QByteArray> &messages, qint64 callNext, QQmlDebugPacket &d); + void forwardEnabled(quint64 features); + void forwardEnabledWhileWaiting(quint64 features); static quint64 translateFeatures(quint64 qmlFeatures); }; diff --git a/src/plugins/qmltooling/qmldbg_quickprofiler/qquickprofileradapter.cpp b/src/plugins/qmltooling/qmldbg_quickprofiler/qquickprofileradapter.cpp index bebf8806c6..0c9fc36463 100644 --- a/src/plugins/qmltooling/qmldbg_quickprofiler/qquickprofileradapter.cpp +++ b/src/plugins/qmltooling/qmldbg_quickprofiler/qquickprofileradapter.cpp @@ -51,20 +51,20 @@ QQuickProfilerAdapter::QQuickProfilerAdapter(QObject *parent) : QQuickProfiler::initialize(this); // We can always do DirectConnection here as all methods are protected by mutexes - connect(this, SIGNAL(profilingEnabled(quint64)), - QQuickProfiler::s_instance, SLOT(startProfilingImpl(quint64)), Qt::DirectConnection); - connect(this, SIGNAL(profilingEnabledWhileWaiting(quint64)), - QQuickProfiler::s_instance, SLOT(startProfilingImpl(quint64)), Qt::DirectConnection); - connect(this, SIGNAL(referenceTimeKnown(QElapsedTimer)), - QQuickProfiler::s_instance, SLOT(setTimer(QElapsedTimer)), Qt::DirectConnection); - connect(this, SIGNAL(profilingDisabled()), - QQuickProfiler::s_instance, SLOT(stopProfilingImpl()), Qt::DirectConnection); - connect(this, SIGNAL(profilingDisabledWhileWaiting()), - QQuickProfiler::s_instance, SLOT(stopProfilingImpl()), Qt::DirectConnection); - connect(this, SIGNAL(dataRequested(bool)), - QQuickProfiler::s_instance, SLOT(reportDataImpl(bool)), Qt::DirectConnection); - connect(QQuickProfiler::s_instance, SIGNAL(dataReady(QVector<QQuickProfilerData>)), - this, SLOT(receiveData(QVector<QQuickProfilerData>)), Qt::DirectConnection); + connect(this, &QQmlAbstractProfilerAdapter::profilingEnabled, + QQuickProfiler::s_instance, &QQuickProfiler::startProfilingImpl, Qt::DirectConnection); + connect(this, &QQmlAbstractProfilerAdapter::profilingEnabledWhileWaiting, + QQuickProfiler::s_instance, &QQuickProfiler::startProfilingImpl, Qt::DirectConnection); + connect(this, &QQmlAbstractProfilerAdapter::referenceTimeKnown, + QQuickProfiler::s_instance, &QQuickProfiler::setTimer, Qt::DirectConnection); + connect(this, &QQmlAbstractProfilerAdapter::profilingDisabled, + QQuickProfiler::s_instance, &QQuickProfiler::stopProfilingImpl, Qt::DirectConnection); + connect(this, &QQmlAbstractProfilerAdapter::profilingDisabledWhileWaiting, + QQuickProfiler::s_instance, &QQuickProfiler::stopProfilingImpl, Qt::DirectConnection); + connect(this, &QQmlAbstractProfilerAdapter::dataRequested, + QQuickProfiler::s_instance, &QQuickProfiler::reportDataImpl, Qt::DirectConnection); + connect(QQuickProfiler::s_instance, &QQuickProfiler::dataReady, + this, &QQuickProfilerAdapter::receiveData, Qt::DirectConnection); } QQuickProfilerAdapter::~QQuickProfilerAdapter() diff --git a/src/plugins/qmltooling/qmldbg_quickprofiler/qquickprofileradapter.h b/src/plugins/qmltooling/qmldbg_quickprofiler/qquickprofileradapter.h index f1ba411ac5..1ad020afd6 100644 --- a/src/plugins/qmltooling/qmldbg_quickprofiler/qquickprofileradapter.h +++ b/src/plugins/qmltooling/qmldbg_quickprofiler/qquickprofileradapter.h @@ -62,8 +62,6 @@ public: QQuickProfilerAdapter(QObject *parent = 0); ~QQuickProfilerAdapter(); qint64 sendMessages(qint64 until, QList<QByteArray> &messages, bool trackLocations) override; - -public slots: void receiveData(const QVector<QQuickProfilerData> &new_data); private: diff --git a/src/plugins/qmltooling/qmldbg_server/qqmldebugserver.cpp b/src/plugins/qmltooling/qmldbg_server/qqmldebugserver.cpp index cbde86e389..f449989598 100644 --- a/src/plugins/qmltooling/qmldbg_server/qqmldebugserver.cpp +++ b/src/plugins/qmltooling/qmldbg_server/qqmldebugserver.cpp @@ -149,15 +149,6 @@ public: static void cleanup(); -private slots: - void wakeEngine(QJSEngine *engine); - void sendMessage(const QString &name, const QByteArray &message); - void sendMessages(const QString &name, const QList<QByteArray> &messages); - void changeServiceState(const QString &serviceName, QQmlDebugService::State state); - void removeThread(); - void receiveMessage(); - void invalidPacket(); - private: friend class QQmlDebugServerThread; friend class QQmlDebugServerFactory; @@ -179,6 +170,13 @@ private: bool canSendMessage(const QString &name); void doSendMessage(const QString &name, const QByteArray &message); + void wakeEngine(QJSEngine *engine); + void sendMessage(const QString &name, const QByteArray &message); + void sendMessages(const QString &name, const QList<QByteArray> &messages); + void changeServiceState(const QString &serviceName, QQmlDebugService::State state); + void removeThread(); + void receiveMessage(); + void invalidPacket(); QQmlDebugServerConnection *m_connection; QHash<QString, QQmlDebugService *> m_plugins; @@ -203,18 +201,22 @@ void QQmlDebugServerImpl::cleanup() if (!server) return; - for (QHash<QString, QQmlDebugService *>::ConstIterator i = server->m_plugins.constBegin(); - i != server->m_plugins.constEnd(); ++i) { - server->m_changeServiceStateCalls.ref(); - QMetaObject::invokeMethod(server, "changeServiceState", Qt::QueuedConnection, - Q_ARG(QString, i.key()), - Q_ARG(QQmlDebugService::State, - QQmlDebugService::NotConnected)); + { + QObject signalSource; + for (QHash<QString, QQmlDebugService *>::ConstIterator i = server->m_plugins.constBegin(); + i != server->m_plugins.constEnd(); ++i) { + server->m_changeServiceStateCalls.ref(); + QString key = i.key(); + // Process this in the server's thread. + connect(&signalSource, &QObject::destroyed, server, [key, server](){ + server->changeServiceState(key, QQmlDebugService::NotConnected); + }, Qt::QueuedConnection); + } } // Wait for changeServiceState calls to finish // (while running an event loop because some services - // might again use slots to execute stuff in the GUI thread) + // might again defer execution of stuff in the GUI thread) QEventLoop loop; while (!server->m_changeServiceStateCalls.testAndSetOrdered(0, 0)) loop.processEvents(); @@ -293,7 +295,7 @@ QQmlDebugServerImpl::QQmlDebugServerImpl() : // Remove the thread immmediately when it finishes, so that we don't have to wait for the // event loop to signal that. - QObject::connect(&m_thread, SIGNAL(finished()), this, SLOT(removeThread()), + QObject::connect(&m_thread, &QThread::finished, this, &QQmlDebugServerImpl::removeThread, Qt::DirectConnection); m_thread.setObjectName(QStringLiteral("QQmlDebugServerThread")); parseArguments(); @@ -587,12 +589,12 @@ void QQmlDebugServerImpl::addEngine(QJSEngine *engine) QMutexLocker locker(&m_helloMutex); Q_ASSERT(!m_engineConditions.contains(engine)); - foreach (QQmlDebugService *service, m_plugins) + for (QQmlDebugService *service : qAsConst(m_plugins)) service->engineAboutToBeAdded(engine); m_engineConditions[engine].waitForServices(&m_helloMutex, m_plugins.count()); - foreach (QQmlDebugService *service, m_plugins) + for (QQmlDebugService *service : qAsConst(m_plugins)) service->engineAdded(engine); } @@ -604,12 +606,12 @@ void QQmlDebugServerImpl::removeEngine(QJSEngine *engine) QMutexLocker locker(&m_helloMutex); Q_ASSERT(m_engineConditions.contains(engine)); - foreach (QQmlDebugService *service, m_plugins) + for (QQmlDebugService *service : qAsConst(m_plugins)) service->engineAboutToBeRemoved(engine); m_engineConditions[engine].waitForServices(&m_helloMutex, m_plugins.count()); - foreach (QQmlDebugService *service, m_plugins) + for (QQmlDebugService *service : qAsConst(m_plugins)) service->engineRemoved(engine); m_engineConditions.remove(engine); @@ -631,15 +633,15 @@ bool QQmlDebugServerImpl::addService(const QString &name, QQmlDebugService *serv if (!service || m_plugins.contains(name)) return false; - connect(service, SIGNAL(messageToClient(QString,QByteArray)), - this, SLOT(sendMessage(QString,QByteArray))); - connect(service, SIGNAL(messagesToClient(QString,QList<QByteArray>)), - this, SLOT(sendMessages(QString,QList<QByteArray>))); + connect(service, &QQmlDebugService::messageToClient, + this, &QQmlDebugServerImpl::sendMessage); + connect(service, &QQmlDebugService::messagesToClient, + this, &QQmlDebugServerImpl::sendMessages); - connect(service, SIGNAL(attachedToEngine(QJSEngine*)), - this, SLOT(wakeEngine(QJSEngine*)), Qt::QueuedConnection); - connect(service, SIGNAL(detachedFromEngine(QJSEngine*)), - this, SLOT(wakeEngine(QJSEngine*)), Qt::QueuedConnection); + connect(service, &QQmlDebugService::attachedToEngine, + this, &QQmlDebugServerImpl::wakeEngine, Qt::QueuedConnection); + connect(service, &QQmlDebugService::detachedFromEngine, + this, &QQmlDebugServerImpl::wakeEngine, Qt::QueuedConnection); service->setState(QQmlDebugService::Unavailable); m_plugins.insert(name, service); @@ -659,15 +661,15 @@ bool QQmlDebugServerImpl::removeService(const QString &name) m_plugins.remove(name); service->setState(QQmlDebugService::NotConnected); - disconnect(service, SIGNAL(detachedFromEngine(QJSEngine*)), - this, SLOT(wakeEngine(QJSEngine*))); - disconnect(service, SIGNAL(attachedToEngine(QJSEngine*)), - this, SLOT(wakeEngine(QJSEngine*))); + disconnect(service, &QQmlDebugService::detachedFromEngine, + this, &QQmlDebugServerImpl::wakeEngine); + disconnect(service, &QQmlDebugService::attachedToEngine, + this, &QQmlDebugServerImpl::wakeEngine); - disconnect(service, SIGNAL(messagesToClient(QString,QList<QByteArray>)), - this, SLOT(sendMessages(QString,QList<QByteArray>))); - disconnect(service, SIGNAL(messageToClient(QString,QByteArray)), - this, SLOT(sendMessage(QString,QByteArray))); + disconnect(service, &QQmlDebugService::messagesToClient, + this, &QQmlDebugServerImpl::sendMessages); + disconnect(service, &QQmlDebugService::messageToClient, + this, &QQmlDebugServerImpl::sendMessage); return true; } @@ -701,11 +703,11 @@ void QQmlDebugServerImpl::sendMessages(const QString &name, const QList<QByteArr if (m_clientSupportsMultiPackets) { QQmlDebugPacket out; out << name; - foreach (const QByteArray &message, messages) + for (const QByteArray &message : messages) out << message; m_protocol->send(out.data()); } else { - foreach (const QByteArray &message, messages) + for (const QByteArray &message : messages) doSendMessage(name, message); } m_connection->flush(); @@ -738,8 +740,10 @@ void QQmlDebugServerImpl::EngineCondition::wake() void QQmlDebugServerImpl::setDevice(QIODevice *socket) { m_protocol = new QPacketProtocol(socket, this); - QObject::connect(m_protocol, SIGNAL(readyRead()), this, SLOT(receiveMessage())); - QObject::connect(m_protocol, SIGNAL(invalidPacket()), this, SLOT(invalidPacket())); + QObject::connect(m_protocol, &QPacketProtocol::readyRead, + this, &QQmlDebugServerImpl::receiveMessage); + QObject::connect(m_protocol, &QPacketProtocol::invalidPacket, + this, &QQmlDebugServerImpl::invalidPacket); if (blockingMode()) m_protocol->waitForReadyRead(-1); diff --git a/src/plugins/qmltooling/qmldbg_tcp/qtcpserverconnection.cpp b/src/plugins/qmltooling/qmldbg_tcp/qtcpserverconnection.cpp index 3d64312b16..b305c3f535 100644 --- a/src/plugins/qmltooling/qmldbg_tcp/qtcpserverconnection.cpp +++ b/src/plugins/qmltooling/qmldbg_tcp/qtcpserverconnection.cpp @@ -65,10 +65,8 @@ public: void waitForConnection(); void flush(); -private slots: - void newConnection(); - private: + void newConnection(); bool listen(); int m_portFrom; @@ -152,7 +150,8 @@ void QTcpServerConnection::flush() bool QTcpServerConnection::listen() { m_tcpServer = new QTcpServer(this); - QObject::connect(m_tcpServer, SIGNAL(newConnection()), this, SLOT(newConnection())); + QObject::connect(m_tcpServer, &QTcpServer::newConnection, + this, &QTcpServerConnection::newConnection); QHostAddress hostaddress; if (!m_hostaddress.isEmpty()) { if (!hostaddress.setAddress(m_hostaddress)) { diff --git a/src/plugins/qmltooling/shared/qqmldebugserver.h b/src/plugins/qmltooling/shared/qqmldebugserver.h index 424c7c4120..109f1e246c 100644 --- a/src/plugins/qmltooling/shared/qqmldebugserver.h +++ b/src/plugins/qmltooling/shared/qqmldebugserver.h @@ -58,7 +58,7 @@ QT_BEGIN_NAMESPACE -class QQmlDebugServer : protected QQmlDebugConnector +class QQmlDebugServer : public QQmlDebugConnector { Q_OBJECT public: diff --git a/src/plugins/scenegraph/d3d12/d3d12.pro b/src/plugins/scenegraph/d3d12/d3d12.pro index 6ba18acf22..9cca5458ee 100644 --- a/src/plugins/scenegraph/d3d12/d3d12.pro +++ b/src/plugins/scenegraph/d3d12/d3d12.pro @@ -12,19 +12,22 @@ QMAKE_TARGET_DESCRIPTION = "Quick D3D12 Renderer for Qt." SOURCES += \ $$PWD/qsgd3d12adaptation.cpp \ $$PWD/qsgd3d12renderloop.cpp \ + $$PWD/qsgd3d12threadedrenderloop.cpp \ $$PWD/qsgd3d12renderer.cpp \ $$PWD/qsgd3d12context.cpp \ $$PWD/qsgd3d12rendercontext.cpp \ - $$PWD/qsgd3d12rectanglenode.cpp \ + $$PWD/qsgd3d12internalrectanglenode.cpp \ $$PWD/qsgd3d12material.cpp \ $$PWD/qsgd3d12builtinmaterials.cpp \ $$PWD/qsgd3d12texture.cpp \ - $$PWD/qsgd3d12imagenode.cpp \ + $$PWD/qsgd3d12internalimagenode.cpp \ $$PWD/qsgd3d12glyphnode.cpp \ $$PWD/qsgd3d12glyphcache.cpp \ $$PWD/qsgd3d12layer.cpp \ $$PWD/qsgd3d12shadereffectnode.cpp \ - $$PWD/qsgd3d12painternode.cpp + $$PWD/qsgd3d12painternode.cpp \ + $$PWD/qsgd3d12publicnodes.cpp \ + $$PWD/qsgd3d12spritenode.cpp NO_PCH_SOURCES += \ $$PWD/qsgd3d12engine.cpp @@ -32,23 +35,26 @@ NO_PCH_SOURCES += \ HEADERS += \ $$PWD/qsgd3d12adaptation_p.h \ $$PWD/qsgd3d12renderloop_p.h \ + $$PWD/qsgd3d12threadedrenderloop_p.h \ $$PWD/qsgd3d12renderer_p.h \ $$PWD/qsgd3d12context_p.h \ $$PWD/qsgd3d12rendercontext_p.h \ $$PWD/qsgd3d12engine_p.h \ $$PWD/qsgd3d12engine_p_p.h \ - $$PWD/qsgd3d12rectanglenode_p.h \ + $$PWD/qsgd3d12internalrectanglenode_p.h \ $$PWD/qsgd3d12material_p.h \ $$PWD/qsgd3d12builtinmaterials_p.h \ $$PWD/qsgd3d12texture_p.h \ - $$PWD/qsgd3d12imagenode_p.h \ + $$PWD/qsgd3d12internalimagenode_p.h \ $$PWD/qsgd3d12glyphnode_p.h \ $$PWD/qsgd3d12glyphcache_p.h \ $$PWD/qsgd3d12layer_p.h \ $$PWD/qsgd3d12shadereffectnode_p.h \ - $$PWD/qsgd3d12painternode_p.h + $$PWD/qsgd3d12painternode_p.h \ + $$PWD/qsgd3d12publicnodes_p.h \ + $$PWD/qsgd3d12spritenode_p.h -LIBS += -ldxgi -ld3d12 -ld3dcompiler +LIBS += -ldxgi -ld3d12 -ld3dcompiler -ldcomp include($$PWD/shaders/shaders.pri) diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12adaptation.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12adaptation.cpp index 2762177e5d..b93da0ae01 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12adaptation.cpp +++ b/src/plugins/scenegraph/d3d12/qsgd3d12adaptation.cpp @@ -39,6 +39,7 @@ #include "qsgd3d12adaptation_p.h" #include "qsgd3d12renderloop_p.h" +#include "qsgd3d12threadedrenderloop_p.h" #include "qsgd3d12context_p.h" QT_BEGIN_NAMESPACE @@ -68,6 +69,16 @@ QSGContextFactoryInterface::Flags QSGD3D12Adaptation::flags(const QString &) con QSGRenderLoop *QSGD3D12Adaptation::createWindowManager() { + static bool threaded = false; + static bool envChecked = false; + if (!envChecked) { + envChecked = true; + threaded = qgetenv("QSG_RENDER_LOOP") == QByteArrayLiteral("threaded"); + } + + if (threaded) + return new QSGD3D12ThreadedRenderLoop; + return new QSGD3D12RenderLoop; } diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12builtinmaterials.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12builtinmaterials.cpp index ca92062120..fc3ea4e22e 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12builtinmaterials.cpp +++ b/src/plugins/scenegraph/d3d12/qsgd3d12builtinmaterials.cpp @@ -102,6 +102,7 @@ QSGMaterialType *QSGD3D12VertexColorMaterial::type() const int QSGD3D12VertexColorMaterial::compare(const QSGMaterial *other) const { + Q_UNUSED(other); Q_ASSERT(other && type() == other->type()); // As the vertex color material has all its state in the vertex attributes // defined by the geometry, all such materials will be equal. @@ -148,6 +149,11 @@ QSGD3D12Material::UpdateResults QSGD3D12VertexColorMaterial::updatePipeline(cons return r; } +QSGD3D12FlatColorMaterial::QSGD3D12FlatColorMaterial() + : m_color(QColor(255, 255, 255)) +{ +} + QSGMaterialType QSGD3D12FlatColorMaterial::mtype; QSGMaterialType *QSGD3D12FlatColorMaterial::type() const @@ -224,6 +230,7 @@ QSGMaterialType *QSGD3D12SmoothColorMaterial::type() const int QSGD3D12SmoothColorMaterial::compare(const QSGMaterial *other) const { + Q_UNUSED(other); Q_ASSERT(other && type() == other->type()); return 0; } @@ -353,6 +360,12 @@ QSGD3D12Material::UpdateResults QSGD3D12TextureMaterial::updatePipeline(const QS return r; } +void QSGD3D12TextureMaterial::setTexture(QSGTexture *texture) +{ + m_texture = texture; + setFlag(Blending, m_texture ? m_texture->hasAlphaChannel() : false); +} + QSGD3D12SmoothTextureMaterial::QSGD3D12SmoothTextureMaterial() { setFlag(RequiresFullMatrixExceptTranslate, true); diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12builtinmaterials_p.h b/src/plugins/scenegraph/d3d12/qsgd3d12builtinmaterials_p.h index 34ae73d2d6..8e488f8cd1 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12builtinmaterials_p.h +++ b/src/plugins/scenegraph/d3d12/qsgd3d12builtinmaterials_p.h @@ -80,6 +80,7 @@ private: class QSGD3D12FlatColorMaterial : public QSGD3D12Material { public: + QSGD3D12FlatColorMaterial(); QSGMaterialType *type() const override; int compare(const QSGMaterial *other) const override; @@ -129,7 +130,7 @@ public: ExtraState *extraState, quint8 *constantBuffer) override; - void setTexture(QSGTexture *texture) { m_texture = texture; } + void setTexture(QSGTexture *texture); QSGTexture *texture() const { return m_texture; } void setMipmapFiltering(QSGTexture::Filtering filter) { m_mipmap_filtering = filter; } diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12context.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12context.cpp index ce44bc7a38..9b88af995d 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12context.cpp +++ b/src/plugins/scenegraph/d3d12/qsgd3d12context.cpp @@ -39,12 +39,15 @@ #include "qsgd3d12context_p.h" #include "qsgd3d12rendercontext_p.h" -#include "qsgd3d12rectanglenode_p.h" -#include "qsgd3d12imagenode_p.h" +#include "qsgd3d12internalrectanglenode_p.h" +#include "qsgd3d12internalimagenode_p.h" #include "qsgd3d12glyphnode_p.h" #include "qsgd3d12layer_p.h" #include "qsgd3d12shadereffectnode_p.h" #include "qsgd3d12painternode_p.h" +#include "qsgd3d12publicnodes_p.h" +#include "qsgd3d12spritenode_p.h" +#include <QtQuick/qquickwindow.h> QT_BEGIN_NAMESPACE @@ -53,14 +56,14 @@ QSGRenderContext *QSGD3D12Context::createRenderContext() return new QSGD3D12RenderContext(this); } -QSGRectangleNode *QSGD3D12Context::createRectangleNode() +QSGInternalRectangleNode *QSGD3D12Context::createInternalRectangleNode() { - return new QSGD3D12RectangleNode; + return new QSGD3D12InternalRectangleNode; } -QSGImageNode *QSGD3D12Context::createImageNode() +QSGInternalImageNode *QSGD3D12Context::createInternalImageNode() { - return new QSGD3D12ImageNode; + return new QSGD3D12InternalImageNode; } QSGPainterNode *QSGD3D12Context::createPainterNode(QQuickPaintedItem *item) @@ -77,11 +80,6 @@ QSGGlyphNode *QSGD3D12Context::createGlyphNode(QSGRenderContext *renderContext, return new QSGD3D12GlyphNode(rc); } -QSGNinePatchNode *QSGD3D12Context::createNinePatchNode() -{ - return nullptr; -} - QSGLayer *QSGD3D12Context::createLayer(QSGRenderContext *renderContext) { QSGD3D12RenderContext *rc = static_cast<QSGD3D12RenderContext *>(renderContext); @@ -108,17 +106,37 @@ QSize QSGD3D12Context::minimumFBOSize() const QSurfaceFormat QSGD3D12Context::defaultSurfaceFormat() const { - return QSurfaceFormat::defaultFormat(); + QSurfaceFormat format = QSurfaceFormat::defaultFormat(); + + if (QQuickWindow::hasDefaultAlphaBuffer()) + format.setAlphaBufferSize(8); + + return format; } QSGRendererInterface *QSGD3D12Context::rendererInterface(QSGRenderContext *renderContext) { - QSGD3D12RenderContext *rc = static_cast<QSGD3D12RenderContext *>(renderContext); - if (!rc->engine()) { - qWarning("No D3D12 engine available yet (no render thread due to window not exposed?)"); - return nullptr; - } - return rc->engine(); + return static_cast<QSGD3D12RenderContext *>(renderContext); +} + +QSGRectangleNode *QSGD3D12Context::createRectangleNode() +{ + return new QSGD3D12RectangleNode; +} + +QSGImageNode *QSGD3D12Context::createImageNode() +{ + return new QSGD3D12ImageNode; +} + +QSGNinePatchNode *QSGD3D12Context::createNinePatchNode() +{ + return new QSGD3D12NinePatchNode; +} + +QSGSpriteNode *QSGD3D12Context::createSpriteNode() +{ + return new QSGD3D12SpriteNode; } QT_END_NAMESPACE diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12context_p.h b/src/plugins/scenegraph/d3d12/qsgd3d12context_p.h index 0564c4edac..70cc606b52 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12context_p.h +++ b/src/plugins/scenegraph/d3d12/qsgd3d12context_p.h @@ -61,11 +61,10 @@ public: QSGD3D12Context(QObject *parent = 0) : QSGContext(parent) { } QSGRenderContext *createRenderContext() override; - QSGRectangleNode *createRectangleNode() override; - QSGImageNode *createImageNode() override; + QSGInternalRectangleNode *createInternalRectangleNode() override; + QSGInternalImageNode *createInternalImageNode() override; QSGPainterNode *createPainterNode(QQuickPaintedItem *item) override; QSGGlyphNode *createGlyphNode(QSGRenderContext *renderContext, bool preferNativeGlyphNode) override; - QSGNinePatchNode *createNinePatchNode() override; QSGLayer *createLayer(QSGRenderContext *renderContext) override; QSGGuiThreadShaderEffectManager *createGuiThreadShaderEffectManager() override; QSGShaderEffectNode *createShaderEffectNode(QSGRenderContext *renderContext, @@ -73,6 +72,11 @@ public: QSize minimumFBOSize() const override; QSurfaceFormat defaultSurfaceFormat() const override; QSGRendererInterface *rendererInterface(QSGRenderContext *renderContext) override; + QSGRectangleNode *createRectangleNode() override; + QSGImageNode *createImageNode() override; + QSGNinePatchNode *createNinePatchNode() override; + QSGSpriteNode *createSpriteNode() override; + }; QT_END_NAMESPACE diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp index 45b9de9ece..2644c693ce 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp +++ b/src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp @@ -42,6 +42,7 @@ #include "cs_mipmapgen.hlslh" #include <QString> #include <QColor> +#include <QLoggingCategory> #include <qmath.h> #include <qalgorithms.h> @@ -69,18 +70,30 @@ QT_BEGIN_NAMESPACE { static bool value = qgetenv("QSG_RENDERER_DEBUG").contains(QT_STRINGIFY(variable)); return value; } DECLARE_DEBUG_VAR(render) +DECLARE_DEBUG_VAR(descheap) +DECLARE_DEBUG_VAR(buffer) +DECLARE_DEBUG_VAR(texture) + +// Except for system info on startup. +Q_LOGGING_CATEGORY(QSG_LOG_INFO_GENERAL, "qt.scenegraph.general") + + +// Any changes to the defaults below must be reflected in adaptations.qdoc as +// well and proven by qmlbench or similar. static const int DEFAULT_SWAP_CHAIN_BUFFER_COUNT = 3; static const int DEFAULT_FRAME_IN_FLIGHT_COUNT = 2; static const int DEFAULT_WAITABLE_SWAP_CHAIN_MAX_LATENCY = 0; -static const int MAX_DRAW_CALLS_PER_LIST = 128; +static const int MAX_DRAW_CALLS_PER_LIST = 4096; static const int MAX_CACHED_ROOTSIG = 16; static const int MAX_CACHED_PSO = 64; static const int GPU_CBVSRVUAV_DESCRIPTORS = 512; +static const DXGI_FORMAT RT_COLOR_FORMAT = DXGI_FORMAT_R8G8B8A8_UNORM; + static const int BUCKETS_PER_HEAP = 8; // must match freeMap static const int DESCRIPTORS_PER_BUCKET = 32; // the bit map (freeMap) is quint32 static const int MAX_DESCRIPTORS_PER_HEAP = BUCKETS_PER_HEAP * DESCRIPTORS_PER_BUCKET; @@ -94,8 +107,8 @@ D3D12_CPU_DESCRIPTOR_HANDLE QSGD3D12CPUDescriptorHeapManager::allocate(D3D12_DES if (heap.freeMap[bucket]) { uint freePos = qCountTrailingZeroBits(heap.freeMap[bucket]); heap.freeMap[bucket] &= ~(1UL << freePos); - if (Q_UNLIKELY(debug_render())) - qDebug("descriptor handle type %x reserve in bucket %d index %d", type, bucket, freePos); + if (Q_UNLIKELY(debug_descheap())) + qDebug("descriptor handle heap %p type %x reserve in bucket %d index %d", &heap, type, bucket, freePos); freePos += bucket * DESCRIPTORS_PER_BUCKET; h = heap.start; h.ptr += freePos * heap.handleSize; @@ -121,7 +134,7 @@ D3D12_CPU_DESCRIPTOR_HANDLE QSGD3D12CPUDescriptorHeapManager::allocate(D3D12_DES heap.start = heap.heap->GetCPUDescriptorHandleForHeapStart(); - if (Q_UNLIKELY(debug_render())) + if (Q_UNLIKELY(debug_descheap())) qDebug("new descriptor heap, type %x, start %llu", type, heap.start.ptr); heap.freeMap[0] = 0xFFFFFFFE; @@ -145,8 +158,8 @@ void QSGD3D12CPUDescriptorHeapManager::release(D3D12_CPU_DESCRIPTOR_HANDLE handl const int bucket = pos / DESCRIPTORS_PER_BUCKET; const int indexInBucket = pos - bucket * DESCRIPTORS_PER_BUCKET; heap.freeMap[bucket] |= 1UL << indexInBucket; - if (Q_UNLIKELY(debug_render())) - qDebug("free descriptor handle type %x bucket %d index %d", type, bucket, indexInBucket); + if (Q_UNLIKELY(debug_descheap())) + qDebug("free descriptor handle heap %p type %x bucket %d index %d", &heap, type, bucket, indexInBucket); return; } } @@ -184,18 +197,22 @@ static void getHardwareAdapter(IDXGIFactory1 *factory, IDXGIAdapter1 **outAdapte DXGI_ADAPTER_DESC1 desc; adapter->GetDesc1(&desc); const QString name = QString::fromUtf16((char16_t *) desc.Description); - qDebug("Adapter %d: '%s' (flags 0x%x)", adapterIndex, qPrintable(name), desc.Flags); + qCDebug(QSG_LOG_INFO_GENERAL, "Adapter %d: '%s' (flags 0x%x)", adapterIndex, qPrintable(name), desc.Flags); } if (qEnvironmentVariableIsSet("QT_D3D_ADAPTER_INDEX")) { const int adapterIndex = qEnvironmentVariableIntValue("QT_D3D_ADAPTER_INDEX"); - if (SUCCEEDED(factory->EnumAdapters1(adapterIndex, &adapter)) - && SUCCEEDED(D3D12CreateDevice(adapter.Get(), fl, _uuidof(ID3D12Device), nullptr))) { + if (SUCCEEDED(factory->EnumAdapters1(adapterIndex, &adapter))) { adapter->GetDesc1(&desc); const QString name = QString::fromUtf16((char16_t *) desc.Description); - qDebug("Using requested adapter '%s'", qPrintable(name)); - *outAdapter = adapter.Detach(); - return; + HRESULT hr = D3D12CreateDevice(adapter.Get(), fl, _uuidof(ID3D12Device), nullptr); + if (SUCCEEDED(hr)) { + qCDebug(QSG_LOG_INFO_GENERAL, "Using requested adapter '%s'", qPrintable(name)); + *outAdapter = adapter.Detach(); + return; + } else { + qWarning("Failed to create device for requested adapter '%s': 0x%x", qPrintable(name), hr); + } } } @@ -206,7 +223,7 @@ static void getHardwareAdapter(IDXGIFactory1 *factory, IDXGIAdapter1 **outAdapte if (SUCCEEDED(D3D12CreateDevice(adapter.Get(), fl, _uuidof(ID3D12Device), nullptr))) { const QString name = QString::fromUtf16((char16_t *) desc.Description); - qDebug("Using adapter '%s'", qPrintable(name)); + qCDebug(QSG_LOG_INFO_GENERAL, "Using adapter '%s'", qPrintable(name)); break; } } @@ -270,7 +287,7 @@ void QSGD3D12DeviceManager::ensureCreated() } if (warp) { - qDebug("Using WARP"); + qCDebug(QSG_LOG_INFO_GENERAL, "Using WARP"); m_factory->EnumWarpAdapter(IID_PPV_ARGS(&adapter)); HRESULT hr = D3D12CreateDevice(adapter.Get(), D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&m_device)); if (FAILED(hr)) { @@ -283,14 +300,14 @@ void QSGD3D12DeviceManager::ensureCreated() if (SUCCEEDED(adapter.As(&adapter3))) { DXGI_QUERY_VIDEO_MEMORY_INFO vidMemInfo; if (SUCCEEDED(adapter3->QueryVideoMemoryInfo(0, DXGI_MEMORY_SEGMENT_GROUP_LOCAL, &vidMemInfo))) { - qDebug("Video memory info: LOCAL: Budget %llu KB CurrentUsage %llu KB AvailableForReservation %llu KB CurrentReservation %llu KB", - vidMemInfo.Budget / 1024, vidMemInfo.CurrentUsage / 1024, - vidMemInfo.AvailableForReservation / 1024, vidMemInfo.CurrentReservation / 1024); + qCDebug(QSG_LOG_INFO_GENERAL, "Video memory info: LOCAL: Budget %llu KB CurrentUsage %llu KB AvailableForReservation %llu KB CurrentReservation %llu KB", + vidMemInfo.Budget / 1024, vidMemInfo.CurrentUsage / 1024, + vidMemInfo.AvailableForReservation / 1024, vidMemInfo.CurrentReservation / 1024); } if (SUCCEEDED(adapter3->QueryVideoMemoryInfo(0, DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL, &vidMemInfo))) { - qDebug("Video memory info: NON-LOCAL: Budget %llu KB CurrentUsage %llu KB AvailableForReservation %llu KB CurrentReservation %llu KB", - vidMemInfo.Budget / 1024, vidMemInfo.CurrentUsage / 1024, - vidMemInfo.AvailableForReservation / 1024, vidMemInfo.CurrentReservation / 1024); + qCDebug(QSG_LOG_INFO_GENERAL, "Video memory info: NON-LOCAL: Budget %llu KB CurrentUsage %llu KB AvailableForReservation %llu KB CurrentReservation %llu KB", + vidMemInfo.Budget / 1024, vidMemInfo.CurrentUsage / 1024, + vidMemInfo.AvailableForReservation / 1024, vidMemInfo.CurrentReservation / 1024); } } } @@ -313,14 +330,14 @@ QSGD3D12Engine::~QSGD3D12Engine() delete d; } -bool QSGD3D12Engine::attachToWindow(WId window, const QSize &size, float dpr, int surfaceFormatSamples) +bool QSGD3D12Engine::attachToWindow(WId window, const QSize &size, float dpr, int surfaceFormatSamples, bool alpha) { if (d->isInitialized()) { qWarning("QSGD3D12Engine: Cannot attach active engine to window"); return false; } - d->initialize(window, size, dpr, surfaceFormatSamples); + d->initialize(window, size, dpr, surfaceFormatSamples, alpha); return d->isInitialized(); } @@ -480,14 +497,15 @@ void QSGD3D12Engine::queueTextureResize(uint id, const QSize &size) d->queueTextureResize(id, size); } -void QSGD3D12Engine::queueTextureUpload(uint id, const QImage &image, const QPoint &dstPos) +void QSGD3D12Engine::queueTextureUpload(uint id, const QImage &image, const QPoint &dstPos, TextureUploadFlags flags) { - d->queueTextureUpload(id, QVector<QImage>() << image, QVector<QPoint>() << dstPos); + d->queueTextureUpload(id, QVector<QImage>() << image, QVector<QPoint>() << dstPos, flags); } -void QSGD3D12Engine::queueTextureUpload(uint id, const QVector<QImage> &images, const QVector<QPoint> &dstPos) +void QSGD3D12Engine::queueTextureUpload(uint id, const QVector<QImage> &images, const QVector<QPoint> &dstPos, + TextureUploadFlags flags) { - d->queueTextureUpload(id, images, dstPos); + d->queueTextureUpload(id, images, dstPos, flags); } void QSGD3D12Engine::releaseTexture(uint id) @@ -535,31 +553,11 @@ void QSGD3D12Engine::simulateDeviceLoss() d->simulateDeviceLoss(); } -QSGRendererInterface::GraphicsApi QSGD3D12Engine::graphicsApi() const -{ - return Direct3D12; -} - -void *QSGD3D12Engine::getResource(Resource resource) const +void *QSGD3D12Engine::getResource(QQuickWindow *, QSGRendererInterface::Resource resource) const { return d->getResource(resource); } -QSGRendererInterface::ShaderType QSGD3D12Engine::shaderType() const -{ - return HLSL; -} - -QSGRendererInterface::ShaderCompilationTypes QSGD3D12Engine::shaderCompilationType() const -{ - return OfflineCompilation; -} - -QSGRendererInterface::ShaderSourceTypes QSGD3D12Engine::shaderSourceType() const -{ - return ShaderByteCode; -} - static inline quint32 alignedSize(quint32 size, quint32 byteAlign) { return (size + byteAlign - 1) & ~(byteAlign - 1); @@ -684,6 +682,13 @@ void QSGD3D12EnginePrivate::releaseResources() commandQueue = nullptr; copyCommandQueue = nullptr; + +#ifndef Q_OS_WINRT + dcompTarget = nullptr; + dcompVisual = nullptr; + dcompDevice = nullptr; +#endif + swapChain = nullptr; delete presentFence; @@ -696,7 +701,7 @@ void QSGD3D12EnginePrivate::releaseResources() // 'window' must be kept, may just be a device loss } -void QSGD3D12EnginePrivate::initialize(WId w, const QSize &size, float dpr, int surfaceFormatSamples) +void QSGD3D12EnginePrivate::initialize(WId w, const QSize &size, float dpr, int surfaceFormatSamples, bool alpha) { if (initialized) return; @@ -705,6 +710,7 @@ void QSGD3D12EnginePrivate::initialize(WId w, const QSize &size, float dpr, int windowSize = size; windowDpr = dpr; windowSamples = qMax(1, surfaceFormatSamples); // may be -1 or 0, whereas windowSamples is uint and >= 1 + windowAlpha = alpha; swapChainBufferCount = qMin(qEnvironmentVariableIntValue("QT_D3D_BUFFER_COUNT"), MAX_SWAP_CHAIN_BUFFER_COUNT); if (swapChainBufferCount < 2) @@ -720,22 +726,53 @@ void QSGD3D12EnginePrivate::initialize(WId w, const QSize &size, float dpr, int else waitableSwapChainMaxLatency = qBound(0, qEnvironmentVariableIntValue(latReqEnvVar), 16); - qDebug("d3d12 engine init. swap chain buffer count %d, max frames prepared without blocking %d", - swapChainBufferCount, frameInFlightCount); + if (qEnvironmentVariableIsSet("QSG_INFO")) + const_cast<QLoggingCategory &>(QSG_LOG_INFO_GENERAL()).setEnabled(QtDebugMsg, true); + + qCDebug(QSG_LOG_INFO_GENERAL, "d3d12 engine init. swap chain buffer count %d, max frames prepared without blocking %d", + swapChainBufferCount, frameInFlightCount); if (waitableSwapChainMaxLatency) - qDebug("Swap chain frame latency waitable object enabled. Frame latency is %d", waitableSwapChainMaxLatency); + qCDebug(QSG_LOG_INFO_GENERAL, "Swap chain frame latency waitable object enabled. Frame latency is %d", waitableSwapChainMaxLatency); - if (qEnvironmentVariableIntValue("QT_D3D_DEBUG") != 0) { - qDebug("Enabling debug layer"); + const bool debugLayer = qEnvironmentVariableIntValue("QT_D3D_DEBUG") != 0; + if (debugLayer) { + qCDebug(QSG_LOG_INFO_GENERAL, "Enabling debug layer"); +#if !defined(Q_OS_WINRT) || !defined(NDEBUG) ComPtr<ID3D12Debug> debugController; if (SUCCEEDED(D3D12GetDebugInterface(IID_PPV_ARGS(&debugController)))) debugController->EnableDebugLayer(); +#else + qCDebug(QSG_LOG_INFO_GENERAL, "Using DebugInterface will not allow certification to pass"); +#endif } QSGD3D12DeviceManager *dev = deviceManager(); device = dev->ref(); dev->registerDeviceLossObserver(this); + if (debugLayer) { + ComPtr<ID3D12InfoQueue> infoQueue; + if (SUCCEEDED(device->QueryInterface(IID_PPV_ARGS(&infoQueue)))) { + infoQueue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_CORRUPTION, true); + infoQueue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_ERROR, true); + const bool breakOnWarning = qEnvironmentVariableIntValue("QT_D3D_DEBUG_BREAK_ON_WARNING") != 0; + infoQueue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_WARNING, breakOnWarning); + D3D12_INFO_QUEUE_FILTER filter = {}; + D3D12_MESSAGE_ID suppressedMessages[] = { + // When using a render target other than the default one we + // have no way to know the custom clear color, if there is one. + D3D12_MESSAGE_ID_CLEARRENDERTARGETVIEW_MISMATCHINGCLEARVALUE + }; + filter.DenyList.NumIDs = _countof(suppressedMessages); + filter.DenyList.pIDList = suppressedMessages; + // setting the filter would enable Info messages which we don't need + D3D12_MESSAGE_SEVERITY infoSev = D3D12_MESSAGE_SEVERITY_INFO; + filter.DenyList.NumSeverities = 1; + filter.DenyList.pSeverityList = &infoSev; + infoQueue->PushStorageFilter(&filter); + } + } + D3D12_COMMAND_QUEUE_DESC queueDesc = {}; queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT; if (FAILED(device->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&commandQueue)))) { @@ -752,28 +789,91 @@ void QSGD3D12EnginePrivate::initialize(WId w, const QSize &size, float dpr, int #ifndef Q_OS_WINRT HWND hwnd = reinterpret_cast<HWND>(w); - DXGI_SWAP_CHAIN_DESC swapChainDesc = {}; - swapChainDesc.BufferCount = swapChainBufferCount; - swapChainDesc.BufferDesc.Width = windowSize.width() * windowDpr; - swapChainDesc.BufferDesc.Height = windowSize.height() * windowDpr; - swapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; - swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; - swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; // D3D12 requires the flip model - swapChainDesc.OutputWindow = hwnd; - swapChainDesc.SampleDesc.Count = 1; // Flip does not support MSAA so no choice here - swapChainDesc.Windowed = TRUE; - if (waitableSwapChainMaxLatency) - swapChainDesc.Flags = DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT; + if (windowAlpha) { + // Go through DirectComposition for semi-transparent windows since the + // traditional approaches won't fly with flip model swapchains. + HRESULT hr = DCompositionCreateDevice(nullptr, IID_PPV_ARGS(&dcompDevice)); + if (SUCCEEDED(hr)) { + hr = dcompDevice->CreateTargetForHwnd(hwnd, true, &dcompTarget); + if (SUCCEEDED(hr)) { + hr = dcompDevice->CreateVisual(&dcompVisual); + if (FAILED(hr)) { + qWarning("Failed to create DirectComposition visual: 0x%x", hr); + windowAlpha = false; + } + } else { + qWarning("Failed to create DirectComposition target: 0x%x", hr); + windowAlpha = false; + } + } else { + qWarning("Failed to create DirectComposition device: 0x%x", hr); + windowAlpha = false; + } + } - ComPtr<IDXGISwapChain> baseSwapChain; - HRESULT hr = dev->dxgi()->CreateSwapChain(commandQueue.Get(), &swapChainDesc, &baseSwapChain); - if (FAILED(hr)) { - qWarning("Failed to create swap chain: 0x%x", hr); - return; + if (windowAlpha) { + DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {}; + swapChainDesc.Width = windowSize.width() * windowDpr; + swapChainDesc.Height = windowSize.height() * windowDpr; + swapChainDesc.Format = RT_COLOR_FORMAT; + swapChainDesc.SampleDesc.Count = 1; + swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + swapChainDesc.BufferCount = swapChainBufferCount; + swapChainDesc.Scaling = DXGI_SCALING_STRETCH; + swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; + swapChainDesc.AlphaMode = DXGI_ALPHA_MODE_PREMULTIPLIED; + if (waitableSwapChainMaxLatency) + swapChainDesc.Flags = DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT; + + ComPtr<IDXGISwapChain1> baseSwapChain; + HRESULT hr = dev->dxgi()->CreateSwapChainForComposition(commandQueue.Get(), &swapChainDesc, nullptr, &baseSwapChain); + if (SUCCEEDED(hr)) { + if (SUCCEEDED(baseSwapChain.As(&swapChain))) { + hr = dcompVisual->SetContent(swapChain.Get()); + if (SUCCEEDED(hr)) { + hr = dcompTarget->SetRoot(dcompVisual.Get()); + if (FAILED(hr)) { + qWarning("SetRoot failed for DirectComposition target: 0x%x", hr); + windowAlpha = false; + } + } else { + qWarning("SetContent failed for DirectComposition visual: 0x%x", hr); + windowAlpha = false; + } + } else { + qWarning("Failed to cast swap chain"); + windowAlpha = false; + } + } else { + qWarning("Failed to create swap chain for composition: 0x%x", hr); + windowAlpha = false; + } } - if (FAILED(baseSwapChain.As(&swapChain))) { - qWarning("Failed to cast swap chain"); - return; + + if (!windowAlpha) { + DXGI_SWAP_CHAIN_DESC swapChainDesc = {}; + swapChainDesc.BufferCount = swapChainBufferCount; + swapChainDesc.BufferDesc.Width = windowSize.width() * windowDpr; + swapChainDesc.BufferDesc.Height = windowSize.height() * windowDpr; + swapChainDesc.BufferDesc.Format = RT_COLOR_FORMAT; + swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; // D3D12 requires the flip model + swapChainDesc.OutputWindow = hwnd; + swapChainDesc.SampleDesc.Count = 1; // Flip does not support MSAA so no choice here + swapChainDesc.Windowed = TRUE; + if (waitableSwapChainMaxLatency) + swapChainDesc.Flags = DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT; + + ComPtr<IDXGISwapChain> baseSwapChain; + HRESULT hr = dev->dxgi()->CreateSwapChain(commandQueue.Get(), &swapChainDesc, &baseSwapChain); + if (FAILED(hr)) { + qWarning("Failed to create swap chain: 0x%x", hr); + return; + } + if (FAILED(baseSwapChain.As(&swapChain))) { + qWarning("Failed to cast swap chain"); + return; + } } dev->dxgi()->MakeWindowAssociation(hwnd, DXGI_MWA_NO_ALT_ENTER); @@ -781,7 +881,7 @@ void QSGD3D12EnginePrivate::initialize(WId w, const QSize &size, float dpr, int DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {}; swapChainDesc.Width = windowSize.width() * windowDpr; swapChainDesc.Height = windowSize.height() * windowDpr; - swapChainDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + swapChainDesc.Format = RT_COLOR_FORMAT; swapChainDesc.SampleDesc.Count = 1; swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; swapChainDesc.BufferCount = swapChainBufferCount; @@ -798,7 +898,7 @@ void QSGD3D12EnginePrivate::initialize(WId w, const QSize &size, float dpr, int return; } if (FAILED(baseSwapChain.As(&swapChain))) { - qWarning("Failed to case swap chain"); + qWarning("Failed to cast swap chain"); return; } @@ -932,7 +1032,7 @@ ID3D12Resource *QSGD3D12EnginePrivate::createColorBuffer(D3D12_CPU_DESCRIPTOR_HA const QVector4D &clearColor, uint samples) { D3D12_CLEAR_VALUE clearValue = {}; - clearValue.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + clearValue.Format = RT_COLOR_FORMAT; clearValue.Color[0] = clearColor.x(); clearValue.Color[1] = clearColor.y(); clearValue.Color[2] = clearColor.z(); @@ -947,7 +1047,7 @@ ID3D12Resource *QSGD3D12EnginePrivate::createColorBuffer(D3D12_CPU_DESCRIPTOR_HA rtDesc.Height = size.height(); rtDesc.DepthOrArraySize = 1; rtDesc.MipLevels = 1; - rtDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + rtDesc.Format = RT_COLOR_FORMAT; rtDesc.SampleDesc = makeSampleDesc(rtDesc.Format, samples); rtDesc.Flags = D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET; @@ -1013,7 +1113,9 @@ void QSGD3D12EnginePrivate::setupDefaultRenderTargets() device->CreateRenderTargetView(defaultRT[i].Get(), nullptr, defaultRTV[i]); } else { const QSize size(windowSize.width() * windowDpr, windowSize.height() * windowDpr); - const QColor cc(Qt::white); // ### what if setClearColor? non-fatal but debug layer warns... + // Not optimal if the user called setClearColor, but there's so + // much we can do. The debug layer warning is suppressed so we're good to go. + const QColor cc(Qt::white); const QVector4D clearColor(cc.redF(), cc.greenF(), cc.blueF(), cc.alphaF()); ID3D12Resource *msaaRT = createColorBuffer(defaultRTV[i], size, clearColor, windowSamples); if (msaaRT) @@ -1054,7 +1156,7 @@ void QSGD3D12EnginePrivate::setWindowSize(const QSize &size, float dpr) const int w = windowSize.width() * windowDpr; const int h = windowSize.height() * windowDpr; - HRESULT hr = swapChain->ResizeBuffers(swapChainBufferCount, w, h, DXGI_FORMAT_R8G8B8A8_UNORM, + HRESULT hr = swapChain->ResizeBuffers(swapChainBufferCount, w, h, RT_COLOR_FORMAT, waitableSwapChainMaxLatency ? DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT : 0); if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET) { deviceManager()->deviceLossDetected(); @@ -1138,7 +1240,7 @@ void QSGD3D12EnginePrivate::resolveMultisampledTarget(ID3D12Resource *msaa, barriers[1].Transition.StateAfter = D3D12_RESOURCE_STATE_RESOLVE_DEST; commandList->ResourceBarrier(2, barriers); - commandList->ResolveSubresource(resolve, 0, msaa, 0, DXGI_FORMAT_R8G8B8A8_UNORM); + commandList->ResolveSubresource(resolve, 0, msaa, 0, RT_COLOR_FORMAT); barriers[0].Transition.pResource = msaa; barriers[0].Transition.StateBefore = D3D12_RESOURCE_STATE_RESOLVE_SOURCE; @@ -1193,7 +1295,7 @@ void QSGD3D12EnginePrivate::ensureBuffer(Buffer *buf) // buffer contents rebuild with a slightly larger total size does // not lead to creating a new buffer. const quint32 sz = alignedSize(buf->cpuDataRef.size, 4096); - if (Q_UNLIKELY(debug_render())) + if (Q_UNLIKELY(debug_buffer())) qDebug("new buffer[pf=%d] of size %d (actual data size %d)", currentPFrameIndex, sz, buf->cpuDataRef.size); bfd.buffer.Attach(createBuffer(sz)); bfd.resourceSize = sz; @@ -1215,7 +1317,7 @@ void QSGD3D12EnginePrivate::updateBuffer(Buffer *buf) return; } for (const auto &r : qAsConst(buf->cpuDataRef.dirty)) { - if (Q_UNLIKELY(debug_render())) + if (Q_UNLIKELY(debug_buffer())) qDebug("%p o %d s %d", buf, r.first, r.second); memcpy(p + r.first, buf->cpuDataRef.p + r.first, r.second); } @@ -1226,7 +1328,7 @@ void QSGD3D12EnginePrivate::updateBuffer(Buffer *buf) void QSGD3D12EnginePrivate::ensureDevice() { if (!initialized && window) - initialize(window, windowSize, windowDpr, windowSamples); + initialize(window, windowSize, windowDpr, windowSamples, windowAlpha); } void QSGD3D12EnginePrivate::beginFrame() @@ -1296,13 +1398,13 @@ void QSGD3D12EnginePrivate::beginFrame() for (uint id : qAsConst(prevFrameData.buffersUsedInFrame)) { Buffer &b(buffers[id - 1]); if (b.d[currentPFrameIndex].buffer && b.d[currentPFrameIndex].dataSize == b.cpuDataRef.size) { - if (Q_UNLIKELY(debug_render())) + if (Q_UNLIKELY(debug_buffer())) qDebug() << "frame" << frameIndex << "takes dirty" << b.d[prevPFrameIndex].dirty << "from frame" << frameIndex - delta << "for buffer" << id; for (const auto &range : qAsConst(b.d[prevPFrameIndex].dirty)) addDirtyRange(&b.cpuDataRef.dirty, range.first, range.second, b.cpuDataRef.size); } else { - if (Q_UNLIKELY(debug_render())) + if (Q_UNLIKELY(debug_buffer())) qDebug() << "frame" << frameIndex << "makes all dirty from frame" << frameIndex - delta << "for buffer" << id; addDirtyRange(&b.cpuDataRef.dirty, 0, b.cpuDataRef.size, b.cpuDataRef.size); @@ -1316,7 +1418,7 @@ void QSGD3D12EnginePrivate::beginFrame() const quint64 finishedFrameIndex = frameIndex - frameInFlightCount; // we know since we just blocked for this // pfd conveniently refers to the same slot that was used by that frame if (!pfd.pendingTextureUploads.isEmpty()) { - if (Q_UNLIKELY(debug_render())) + if (Q_UNLIKELY(debug_texture())) qDebug("Removing texture upload data for frame %d", finishedFrameIndex); for (uint id : qAsConst(pfd.pendingTextureUploads)) { const int idx = id - 1; @@ -1330,13 +1432,13 @@ void QSGD3D12EnginePrivate::beginFrame() t.lastWaitFenceValue = 0; t.stagingBuffers.clear(); t.stagingHeaps.clear(); - if (Q_UNLIKELY(debug_render())) + if (Q_UNLIKELY(debug_texture())) qDebug("Cleaned staging data for texture %u", id); } } pfd.pendingTextureUploads.clear(); if (!pfd.pendingTextureMipMap.isEmpty()) { - if (Q_UNLIKELY(debug_render())) + if (Q_UNLIKELY(debug_texture())) qDebug() << "cleaning mipmap generation data for " << pfd.pendingTextureMipMap; // no special cleanup is needed as mipmap generation uses the frame's resources pfd.pendingTextureMipMap.clear(); @@ -1350,7 +1452,7 @@ void QSGD3D12EnginePrivate::beginFrame() } } if (!hasPending) { - if (Q_UNLIKELY(debug_render())) + if (Q_UNLIKELY(debug_texture())) qDebug("no more pending textures"); copyCommandAllocator->Reset(); } @@ -1471,7 +1573,7 @@ void QSGD3D12EnginePrivate::endDrawCalls(bool lastInFrame) PersistentFrameData &pfd(pframeData[currentPFrameIndex]); // Now is the time to sync all the changed areas in the buffers. - if (Q_UNLIKELY(debug_render())) + if (Q_UNLIKELY(debug_buffer())) qDebug() << "buffers used in drawcall set" << pfd.buffersUsedInDrawCallSet; for (uint id : qAsConst(pfd.buffersUsedInDrawCallSet)) updateBuffer(&buffers[id - 1]); @@ -1496,12 +1598,12 @@ void QSGD3D12EnginePrivate::endDrawCalls(bool lastInFrame) pfd.pendingTextureMipMap.insert(id); } if (topFenceValue) { - if (Q_UNLIKELY(debug_render())) + if (Q_UNLIKELY(debug_texture())) qDebug("added wait for texture fence %llu", topFenceValue); commandQueue->Wait(textureUploadFence.Get(), topFenceValue); // Generate mipmaps after the wait, when necessary. if (!pfd.pendingTextureMipMap.isEmpty()) { - if (Q_UNLIKELY(debug_render())) + if (Q_UNLIKELY(debug_texture())) qDebug() << "starting mipmap generation for" << pfd.pendingTextureMipMap; for (uint id : qAsConst(pfd.pendingTextureMipMap)) mipmapper.queueGenerate(textures[id - 1]); @@ -1580,8 +1682,8 @@ void QSGD3D12EnginePrivate::endLayer() // Root signature: // [0] CBV - always present -// [1] table with 1 SRV per texture (optional) -// one static sampler per texture (optional) +// [1] table with one SRV per texture (must be a table since root descriptor SRVs cannot be textures) - optional +// one static sampler per texture - optional // // SRVs can be created freely via QSGD3D12CPUDescriptorHeapManager and stored // in QSGD3D12TextureView. The engine will copy them onto a dedicated, @@ -1612,17 +1714,17 @@ void QSGD3D12EnginePrivate::finalizePipeline(const QSGD3D12PipelineState &pipeli rootParams[0].Descriptor.RegisterSpace = 0; ++rootParamCount; + D3D12_DESCRIPTOR_RANGE tvDescRange; if (pipelineState.shaders.rootSig.textureViewCount > 0) { rootParams[rootParamCount].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; rootParams[rootParamCount].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL; rootParams[rootParamCount].DescriptorTable.NumDescriptorRanges = 1; - D3D12_DESCRIPTOR_RANGE descRange; - descRange.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV; - descRange.NumDescriptors = pipelineState.shaders.rootSig.textureViewCount; - descRange.BaseShaderRegister = 0; // t0, t1, ... - descRange.RegisterSpace = 0; - descRange.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND; - rootParams[rootParamCount].DescriptorTable.pDescriptorRanges = &descRange; + tvDescRange.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV; + tvDescRange.NumDescriptors = pipelineState.shaders.rootSig.textureViewCount; + tvDescRange.BaseShaderRegister = 0; // t0, t1, ... + tvDescRange.RegisterSpace = 0; + tvDescRange.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND; + rootParams[rootParamCount].DescriptorTable.pDescriptorRanges = &tvDescRange; ++rootParamCount; } @@ -1657,7 +1759,8 @@ void QSGD3D12EnginePrivate::finalizePipeline(const QSGD3D12PipelineState &pipeli ComPtr<ID3DBlob> signature; ComPtr<ID3DBlob> error; if (FAILED(D3D12SerializeRootSignature(&desc, D3D_ROOT_SIGNATURE_VERSION_1, &signature, &error))) { - qWarning("Failed to serialize root signature"); + QByteArray msg(static_cast<const char *>(error->GetBufferPointer()), error->GetBufferSize()); + qWarning("Failed to serialize root signature: %s", qPrintable(msg)); return; } if (FAILED(device->CreateRootSignature(0, signature->GetBufferPointer(), signature->GetBufferSize(), @@ -1762,7 +1865,7 @@ void QSGD3D12EnginePrivate::finalizePipeline(const QSGD3D12PipelineState &pipeli psoDesc.SampleMask = UINT_MAX; psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE(pipelineState.topologyType); psoDesc.NumRenderTargets = 1; - psoDesc.RTVFormats[0] = DXGI_FORMAT_R8G8B8A8_UNORM; + psoDesc.RTVFormats[0] = RT_COLOR_FORMAT; psoDesc.DSVFormat = DXGI_FORMAT_D24_UNORM_S8_UINT; psoDesc.SampleDesc = defaultRT[0]->GetDesc().SampleDesc; @@ -2046,7 +2149,7 @@ void QSGD3D12EnginePrivate::ensureGPUDescriptorHeap(int cbvSrvUavDescriptorCount while (pfd.cbvSrvUavNextFreeDescriptorIndex + cbvSrvUavDescriptorCount > newSize) newSize *= 2; if (newSize != pfd.gpuCbvSrvUavHeapSize) { - if (Q_UNLIKELY(debug_render())) + if (Q_UNLIKELY(debug_descheap())) qDebug("Out of space for SRVs, creating new CBV-SRV-UAV descriptor heap with descriptor count %d", newSize); deferredDelete(pfd.gpuCbvSrvUavHeap); createCbvSrvUavHeap(currentPFrameIndex, newSize); @@ -2077,6 +2180,11 @@ void QSGD3D12EnginePrivate::present() return; } +#ifndef Q_OS_WINRT + if (dcompDevice) + dcompDevice->Commit(); +#endif + ++presentFrameIndex; } @@ -2132,7 +2240,7 @@ void QSGD3D12EnginePrivate::releaseBuffer(uint id) const int idx = id - 1; Q_ASSERT(idx < buffers.count()); - if (Q_UNLIKELY(debug_render())) + if (Q_UNLIKELY(debug_buffer())) qDebug("releasing buffer %u", id); Buffer &b(buffers[idx]); @@ -2166,7 +2274,7 @@ void QSGD3D12EnginePrivate::resetBuffer(uint id, const quint8 *data, int size) Q_ASSERT(idx < buffers.count() && buffers[idx].entryInUse()); Buffer &b(buffers[idx]); - if (Q_UNLIKELY(debug_render())) + if (Q_UNLIKELY(debug_buffer())) qDebug("reset buffer %u, size %d", id, size); b.cpuDataRef.p = data; @@ -2218,7 +2326,7 @@ uint QSGD3D12EnginePrivate::genTexture() return id; } -static inline DXGI_FORMAT textureFormat(QImage::Format format, bool wantsAlpha, bool mipmap, +static inline DXGI_FORMAT textureFormat(QImage::Format format, bool wantsAlpha, bool mipmap, bool force32bit, QImage::Format *imageFormat, int *bytesPerPixel) { DXGI_FORMAT f = DXGI_FORMAT_R8G8B8A8_UNORM; @@ -2230,8 +2338,12 @@ static inline DXGI_FORMAT textureFormat(QImage::Format format, bool wantsAlpha, case QImage::Format_Grayscale8: case QImage::Format_Indexed8: case QImage::Format_Alpha8: - f = DXGI_FORMAT_R8_UNORM; - bpp = 1; + if (!force32bit) { + f = DXGI_FORMAT_R8_UNORM; + bpp = 1; + } else { + convFormat = QImage::Format_RGBA8888; + } break; case QImage::Format_RGB32: f = DXGI_FORMAT_B8G8R8A8_UNORM; @@ -2307,7 +2419,9 @@ void QSGD3D12EnginePrivate::createTexture(uint id, const QSize &size, QImage::Fo textureDesc.Height = adjustedSize.height(); textureDesc.DepthOrArraySize = 1; textureDesc.MipLevels = !t.mipmap() ? 1 : QSGD3D12Engine::mipMapLevels(adjustedSize); - textureDesc.Format = textureFormat(format, t.alpha(), t.mipmap(), nullptr, nullptr); + textureDesc.Format = textureFormat(format, t.alpha(), t.mipmap(), + createFlags.testFlag(QSGD3D12Engine::TextureAlways32Bit), + nullptr, nullptr); textureDesc.SampleDesc.Count = 1; textureDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN; if (t.mipmap()) @@ -2344,7 +2458,7 @@ void QSGD3D12EnginePrivate::createTexture(uint id, const QSize &size, QImage::Fo } } - if (Q_UNLIKELY(debug_render())) + if (Q_UNLIKELY(debug_texture())) qDebug("created texture %u, size %dx%d, miplevels %d", id, adjustedSize.width(), adjustedSize.height(), textureDesc.MipLevels); } @@ -2365,7 +2479,7 @@ void QSGD3D12EnginePrivate::queueTextureResize(uint id, const QSize &size) return; } - if (Q_UNLIKELY(debug_render())) + if (Q_UNLIKELY(debug_texture())) qDebug("resizing texture %u, size %dx%d", id, size.width(), size.height()); D3D12_RESOURCE_DESC textureDesc = t.texture->GetDesc(); @@ -2417,11 +2531,12 @@ void QSGD3D12EnginePrivate::queueTextureResize(uint id, const QSize &size) t.fenceValue = nextTextureUploadFenceValue.fetchAndAddAcquire(1) + 1; copyCommandQueue->Signal(textureUploadFence.Get(), t.fenceValue); - if (Q_UNLIKELY(debug_render())) + if (Q_UNLIKELY(debug_texture())) qDebug("submitted old content copy for texture %u on the copy queue, fence %llu", id, t.fenceValue); } -void QSGD3D12EnginePrivate::queueTextureUpload(uint id, const QVector<QImage> &images, const QVector<QPoint> &dstPos) +void QSGD3D12EnginePrivate::queueTextureUpload(uint id, const QVector<QImage> &images, const QVector<QPoint> &dstPos, + QSGD3D12Engine::TextureUploadFlags flags) { Q_ASSERT(id); Q_ASSERT(images.count() == dstPos.count()); @@ -2449,7 +2564,7 @@ void QSGD3D12EnginePrivate::queueTextureUpload(uint id, const QVector<QImage> &i t.fenceValue = nextTextureUploadFenceValue.fetchAndAddAcquire(1) + 1; - if (Q_UNLIKELY(debug_render())) + if (Q_UNLIKELY(debug_texture())) qDebug("adding upload for texture %u on the copy queue, fence %llu", id, t.fenceValue); D3D12_RESOURCE_DESC textureDesc = t.texture->GetDesc(); @@ -2458,14 +2573,16 @@ void QSGD3D12EnginePrivate::queueTextureUpload(uint id, const QVector<QImage> &i int totalSize = 0; for (const QImage &image : images) { int bytesPerPixel; - textureFormat(image.format(), t.alpha(), t.mipmap(), nullptr, &bytesPerPixel); + textureFormat(image.format(), t.alpha(), t.mipmap(), + flags.testFlag(QSGD3D12Engine::TextureUploadAlways32Bit), + nullptr, &bytesPerPixel); const int w = !t.mipmap() ? image.width() : adjustedTextureSize.width(); const int h = !t.mipmap() ? image.height() : adjustedTextureSize.height(); const int stride = alignedSize(w * bytesPerPixel, D3D12_TEXTURE_DATA_PITCH_ALIGNMENT); totalSize += alignedSize(h * stride, D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT); } - if (Q_UNLIKELY(debug_render())) + if (Q_UNLIKELY(debug_texture())) qDebug("%d sub-uploads, heap size %d bytes", images.count(), totalSize); // Instead of individual committed resources for each upload buffer, @@ -2490,8 +2607,10 @@ void QSGD3D12EnginePrivate::queueTextureUpload(uint id, const QVector<QImage> &i for (int i = 0; i < images.count(); ++i) { QImage::Format convFormat; int bytesPerPixel; - textureFormat(images[i].format(), t.alpha(), t.mipmap(), &convFormat, &bytesPerPixel); - if (Q_UNLIKELY(debug_render() && i == 0)) + textureFormat(images[i].format(), t.alpha(), t.mipmap(), + flags.testFlag(QSGD3D12Engine::TextureUploadAlways32Bit), + &convFormat, &bytesPerPixel); + if (Q_UNLIKELY(debug_texture() && i == 0)) qDebug("source image format %d, target format %d, bpp %d", images[i].format(), convFormat, bytesPerPixel); QImage convImage = images[i].format() == convFormat ? images[i] : images[i].convertToFormat(convFormat); @@ -2566,7 +2685,7 @@ void QSGD3D12EnginePrivate::releaseTexture(uint id) const int idx = id - 1; Q_ASSERT(idx < textures.count()); - if (Q_UNLIKELY(debug_render())) + if (Q_UNLIKELY(debug_texture())) qDebug("releasing texture %d", id); Texture &t(textures[idx]); @@ -2825,7 +2944,7 @@ void QSGD3D12EnginePrivate::createRenderTarget(uint id, const QSize &size, const textureDesc.Height = size.height(); textureDesc.DepthOrArraySize = 1; textureDesc.MipLevels = 1; - textureDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + textureDesc.Format = RT_COLOR_FORMAT; textureDesc.SampleDesc.Count = 1; textureDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN; diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12engine_p.h b/src/plugins/scenegraph/d3d12/qsgd3d12engine_p.h index 2ebe1e733a..b30994fe0d 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12engine_p.h +++ b/src/plugins/scenegraph/d3d12/qsgd3d12engine_p.h @@ -281,13 +281,13 @@ inline uint qHash(const QSGD3D12PipelineState &key, uint seed = 0) + key.topologyType; } -class QSGD3D12Engine : public QSGRendererInterface +class QSGD3D12Engine { public: QSGD3D12Engine(); ~QSGD3D12Engine(); - bool attachToWindow(WId window, const QSize &size, float dpr, int surfaceFormatSamples); + bool attachToWindow(WId window, const QSize &size, float dpr, int surfaceFormatSamples, bool alpha); void releaseResources(); bool hasResources() const; void setWindowSize(const QSize &size, float dpr); @@ -349,16 +349,22 @@ public: static QSize mipMapAdjustedSourceSize(const QSize &size); enum TextureCreateFlag { - TextureWithAlpha = 0x1, - TextureWithMipMaps = 0x2 + TextureWithAlpha = 0x01, + TextureWithMipMaps = 0x02, + TextureAlways32Bit = 0x04 }; Q_DECLARE_FLAGS(TextureCreateFlags, TextureCreateFlag) + enum TextureUploadFlag { + TextureUploadAlways32Bit = 0x01 + }; + Q_DECLARE_FLAGS(TextureUploadFlags, TextureUploadFlag) + uint genTexture(); void createTexture(uint id, const QSize &size, QImage::Format format, TextureCreateFlags flags); void queueTextureResize(uint id, const QSize &size); - void queueTextureUpload(uint id, const QImage &image, const QPoint &dstPos = QPoint()); - void queueTextureUpload(uint id, const QVector<QImage> &images, const QVector<QPoint> &dstPos); + void queueTextureUpload(uint id, const QImage &image, const QPoint &dstPos = QPoint(), TextureUploadFlags flags = 0); + void queueTextureUpload(uint id, const QVector<QImage> &images, const QVector<QPoint> &dstPos, TextureUploadFlags flags = 0); void releaseTexture(uint id); void useTexture(uint id); @@ -372,12 +378,7 @@ public: void simulateDeviceLoss(); - // QSGRendererInterface - GraphicsApi graphicsApi() const override; - void *getResource(Resource resource) const override; - ShaderType shaderType() const override; - ShaderCompilationTypes shaderCompilationType() const override; - ShaderSourceTypes shaderSourceType() const override; + void *getResource(QQuickWindow *window, QSGRendererInterface::Resource resource) const; private: QSGD3D12EnginePrivate *d; @@ -386,6 +387,7 @@ private: Q_DECLARE_OPERATORS_FOR_FLAGS(QSGD3D12Engine::ClearFlags) Q_DECLARE_OPERATORS_FOR_FLAGS(QSGD3D12Engine::TextureCreateFlags) +Q_DECLARE_OPERATORS_FOR_FLAGS(QSGD3D12Engine::TextureUploadFlags) QT_END_NAMESPACE diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12engine_p_p.h b/src/plugins/scenegraph/d3d12/qsgd3d12engine_p_p.h index 6cd7cbd24e..1048ed63e7 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12engine_p_p.h +++ b/src/plugins/scenegraph/d3d12/qsgd3d12engine_p_p.h @@ -56,6 +56,7 @@ #include <d3d12.h> #include <dxgi1_4.h> +#include <dcomp.h> #include <wrl/client.h> using namespace Microsoft::WRL; @@ -130,7 +131,7 @@ struct QSGD3D12CPUWaitableFence class QSGD3D12EnginePrivate : public QSGD3D12DeviceManager::DeviceLossObserver { public: - void initialize(WId w, const QSize &size, float dpr, int surfaceFormatSamples); + void initialize(WId w, const QSize &size, float dpr, int surfaceFormatSamples, bool alpha); bool isInitialized() const { return initialized; } void releaseResources(); void setWindowSize(const QSize &size, float dpr); @@ -169,7 +170,8 @@ public: uint genTexture(); void createTexture(uint id, const QSize &size, QImage::Format format, QSGD3D12Engine::TextureCreateFlags flags); void queueTextureResize(uint id, const QSize &size); - void queueTextureUpload(uint id, const QVector<QImage> &images, const QVector<QPoint> &dstPos); + void queueTextureUpload(uint id, const QVector<QImage> &images, const QVector<QPoint> &dstPos, + QSGD3D12Engine::TextureUploadFlags flags); void releaseTexture(uint id); void useTexture(uint id); @@ -269,6 +271,7 @@ private: QSize windowSize; float windowDpr; uint windowSamples; + bool windowAlpha; int swapChainBufferCount; int frameInFlightCount; int waitableSwapChainMaxLatency; @@ -432,6 +435,12 @@ private: }; DeviceLossTester devLossTest; + +#ifndef Q_OS_WINRT + ComPtr<IDCompositionDevice> dcompDevice; + ComPtr<IDCompositionTarget> dcompTarget; + ComPtr<IDCompositionVisual> dcompVisual; +#endif }; inline uint qHash(const QSGD3D12EnginePrivate::PersistentFrameData::PendingRelease &pr, uint seed = 0) diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12glyphcache.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12glyphcache.cpp index 45ef202e83..915917c3d5 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12glyphcache.cpp +++ b/src/plugins/scenegraph/d3d12/qsgd3d12glyphcache.cpp @@ -42,6 +42,11 @@ QT_BEGIN_NAMESPACE +// Convert A8 glyphs to 32-bit in the engine. This is here to work around +// QTBUG-55330 for AMD cards. +// If removing, textmask.hlsl must be adjusted! (.a -> .r) +#define ALWAYS_32BIT + // NOTE: Avoid categorized logging. It is slow. #define DECLARE_DEBUG_VAR(variable) \ @@ -77,7 +82,11 @@ void QSGD3D12GlyphCache::createTextureData(int width, int height) const QImage::Format imageFormat = m_format == QFontEngine::Format_A8 ? QImage::Format_Alpha8 : QImage::Format_ARGB32_Premultiplied; - m_engine->createTexture(m_id, m_size, imageFormat, QSGD3D12Engine::TextureWithAlpha); + m_engine->createTexture(m_id, m_size, imageFormat, QSGD3D12Engine::TextureWithAlpha +#ifdef ALWAYS_32BIT + | QSGD3D12Engine::TextureAlways32Bit +#endif + ); } void QSGD3D12GlyphCache::resizeTextureData(int width, int height) @@ -146,7 +155,11 @@ void QSGD3D12GlyphCache::endFillTexture() Q_ASSERT(m_id); - m_engine->queueTextureUpload(m_id, m_glyphImages, m_glyphPos); + m_engine->queueTextureUpload(m_id, m_glyphImages, m_glyphPos +#ifdef ALWAYS_32BIT + , QSGD3D12Engine::TextureUploadAlways32Bit +#endif + ); // Nothing else left to do, it is up to the text material to call // useTexture() which will then add the texture dependency to the frame. diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12imagenode.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12internalimagenode.cpp index 9bb360bc5e..aa163cacbf 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12imagenode.cpp +++ b/src/plugins/scenegraph/d3d12/qsgd3d12internalimagenode.cpp @@ -37,16 +37,16 @@ ** ****************************************************************************/ -#include "qsgd3d12imagenode_p.h" +#include "qsgd3d12internalimagenode_p.h" QT_BEGIN_NAMESPACE -QSGD3D12ImageNode::QSGD3D12ImageNode() +QSGD3D12InternalImageNode::QSGD3D12InternalImageNode() { setMaterial(&m_material); } -void QSGD3D12ImageNode::setFiltering(QSGTexture::Filtering filtering) +void QSGD3D12InternalImageNode::setFiltering(QSGTexture::Filtering filtering) { if (m_material.filtering() == filtering) return; @@ -56,7 +56,7 @@ void QSGD3D12ImageNode::setFiltering(QSGTexture::Filtering filtering) markDirty(DirtyMaterial); } -void QSGD3D12ImageNode::setMipmapFiltering(QSGTexture::Filtering filtering) +void QSGD3D12InternalImageNode::setMipmapFiltering(QSGTexture::Filtering filtering) { if (m_material.mipmapFiltering() == filtering) return; @@ -66,7 +66,7 @@ void QSGD3D12ImageNode::setMipmapFiltering(QSGTexture::Filtering filtering) markDirty(DirtyMaterial); } -void QSGD3D12ImageNode::setVerticalWrapMode(QSGTexture::WrapMode wrapMode) +void QSGD3D12InternalImageNode::setVerticalWrapMode(QSGTexture::WrapMode wrapMode) { if (m_material.verticalWrapMode() == wrapMode) return; @@ -76,7 +76,7 @@ void QSGD3D12ImageNode::setVerticalWrapMode(QSGTexture::WrapMode wrapMode) markDirty(DirtyMaterial); } -void QSGD3D12ImageNode::setHorizontalWrapMode(QSGTexture::WrapMode wrapMode) +void QSGD3D12InternalImageNode::setHorizontalWrapMode(QSGTexture::WrapMode wrapMode) { if (m_material.horizontalWrapMode() == wrapMode) return; @@ -86,7 +86,7 @@ void QSGD3D12ImageNode::setHorizontalWrapMode(QSGTexture::WrapMode wrapMode) markDirty(DirtyMaterial); } -void QSGD3D12ImageNode::updateMaterialAntialiasing() +void QSGD3D12InternalImageNode::updateMaterialAntialiasing() { if (m_antialiasing) setMaterial(&m_smoothMaterial); @@ -94,18 +94,18 @@ void QSGD3D12ImageNode::updateMaterialAntialiasing() setMaterial(&m_material); } -void QSGD3D12ImageNode::setMaterialTexture(QSGTexture *texture) +void QSGD3D12InternalImageNode::setMaterialTexture(QSGTexture *texture) { m_material.setTexture(texture); m_smoothMaterial.setTexture(texture); } -QSGTexture *QSGD3D12ImageNode::materialTexture() const +QSGTexture *QSGD3D12InternalImageNode::materialTexture() const { return m_material.texture(); } -bool QSGD3D12ImageNode::updateMaterialBlending() +bool QSGD3D12InternalImageNode::updateMaterialBlending() { const bool alpha = m_material.flags() & QSGMaterial::Blending; if (materialTexture() && alpha != materialTexture()->hasAlphaChannel()) { @@ -115,7 +115,7 @@ bool QSGD3D12ImageNode::updateMaterialBlending() return false; } -bool QSGD3D12ImageNode::supportsWrap(const QSize &) const +bool QSGD3D12InternalImageNode::supportsWrap(const QSize &) const { return true; } diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12imagenode_p.h b/src/plugins/scenegraph/d3d12/qsgd3d12internalimagenode_p.h index ef4b38884a..26284740ee 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12imagenode_p.h +++ b/src/plugins/scenegraph/d3d12/qsgd3d12internalimagenode_p.h @@ -37,8 +37,8 @@ ** ****************************************************************************/ -#ifndef QSGD3D12IMAGENODE_P_H -#define QSGD3D12IMAGENODE_P_H +#ifndef QSGD3D12INTERNALIMAGENODE_P_H +#define QSGD3D12INTERNALIMAGENODE_P_H // // W A R N I N G @@ -51,15 +51,15 @@ // We mean it. // -#include <private/qsgbasicimagenode_p.h> +#include <private/qsgbasicinternalimagenode_p.h> #include "qsgd3d12builtinmaterials_p.h" QT_BEGIN_NAMESPACE -class QSGD3D12ImageNode : public QSGBasicImageNode +class QSGD3D12InternalImageNode : public QSGBasicInternalImageNode { public: - QSGD3D12ImageNode(); + QSGD3D12InternalImageNode(); void setMipmapFiltering(QSGTexture::Filtering filtering) override; void setFiltering(QSGTexture::Filtering filtering) override; @@ -79,4 +79,4 @@ private: QT_END_NAMESPACE -#endif // QSGD3D12IMAGENODE_P_H +#endif // QSGD3D12INTERNALIMAGENODE_P_H diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12rectanglenode.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12internalrectanglenode.cpp index 7548f5cbc0..2d9c5b55d1 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12rectanglenode.cpp +++ b/src/plugins/scenegraph/d3d12/qsgd3d12internalrectanglenode.cpp @@ -37,16 +37,16 @@ ** ****************************************************************************/ -#include "qsgd3d12rectanglenode_p.h" +#include "qsgd3d12internalrectanglenode_p.h" QT_BEGIN_NAMESPACE -QSGD3D12RectangleNode::QSGD3D12RectangleNode() +QSGD3D12InternalRectangleNode::QSGD3D12InternalRectangleNode() { setMaterial(&m_material); } -void QSGD3D12RectangleNode::updateMaterialAntialiasing() +void QSGD3D12InternalRectangleNode::updateMaterialAntialiasing() { if (m_antialiasing) setMaterial(&m_smoothMaterial); @@ -54,7 +54,7 @@ void QSGD3D12RectangleNode::updateMaterialAntialiasing() setMaterial(&m_material); } -void QSGD3D12RectangleNode::updateMaterialBlending(QSGNode::DirtyState *state) +void QSGD3D12InternalRectangleNode::updateMaterialBlending(QSGNode::DirtyState *state) { // smoothed material is always blended, so no change in material state if (material() == &m_material) { diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12rectanglenode_p.h b/src/plugins/scenegraph/d3d12/qsgd3d12internalrectanglenode_p.h index 95f734bc4c..2fc3c69285 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12rectanglenode_p.h +++ b/src/plugins/scenegraph/d3d12/qsgd3d12internalrectanglenode_p.h @@ -37,8 +37,8 @@ ** ****************************************************************************/ -#ifndef QSGD3D12RECTANGLENODE_P_H -#define QSGD3D12RECTANGLENODE_P_H +#ifndef QSGD3D12INTERNALRECTANGLENODE_P_H +#define QSGD3D12INTERNALRECTANGLENODE_P_H // // W A R N I N G @@ -51,15 +51,15 @@ // We mean it. // -#include <private/qsgbasicrectanglenode_p.h> +#include <private/qsgbasicinternalrectanglenode_p.h> #include "qsgd3d12builtinmaterials_p.h" QT_BEGIN_NAMESPACE -class QSGD3D12RectangleNode : public QSGBasicRectangleNode +class QSGD3D12InternalRectangleNode : public QSGBasicInternalRectangleNode { public: - QSGD3D12RectangleNode(); + QSGD3D12InternalRectangleNode(); private: void updateMaterialAntialiasing() override; @@ -71,4 +71,4 @@ private: QT_END_NAMESPACE -#endif // QSGD3D12RECTANGLENODE_P_H +#endif // QSGD3D12INTERNALRECTANGLENODE_P_H diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12painternode.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12painternode.cpp index 7837c3a2d3..b22c42f2e5 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12painternode.cpp +++ b/src/plugins/scenegraph/d3d12/qsgd3d12painternode.cpp @@ -84,10 +84,9 @@ QSGD3D12PainterNode::QSGD3D12PainterNode(QQuickPaintedItem *item) m_dirtyGeometry(false), m_dirtyContents(false) { - m_material.setFlag(QSGMaterial::Blending); + setGeometry(&m_geometry); m_material.setTexture(m_texture); setMaterial(&m_material); - setGeometry(&m_geometry); } QSGD3D12PainterNode::~QSGD3D12PainterNode() diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12painternode_p.h b/src/plugins/scenegraph/d3d12/qsgd3d12painternode_p.h index 67246cc3cd..7f4842b3a6 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12painternode_p.h +++ b/src/plugins/scenegraph/d3d12/qsgd3d12painternode_p.h @@ -65,6 +65,7 @@ public: QSGD3D12PainterTexture(QSGD3D12Engine *engine); void bind() override; + bool hasAlphaChannel() const override { return true; } QImage *image() { return &m_image; } diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12publicnodes.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12publicnodes.cpp new file mode 100644 index 0000000000..783caa280f --- /dev/null +++ b/src/plugins/scenegraph/d3d12/qsgd3d12publicnodes.cpp @@ -0,0 +1,254 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgd3d12publicnodes_p.h" + +// for rebuildGeometry +#include <private/qsgdefaultninepatchnode_p.h> +#include <private/qsgdefaultimagenode_p.h> + +QT_BEGIN_NAMESPACE + +QSGD3D12RectangleNode::QSGD3D12RectangleNode() + : m_geometry(QSGGeometry::defaultAttributes_Point2D(), 4) +{ + QSGGeometry::updateRectGeometry(&m_geometry, QRectF()); + setMaterial(&m_material); + setGeometry(&m_geometry); +#ifdef QSG_RUNTIME_DESCRIPTION + qsgnode_set_description(this, QLatin1String("rectangle")); +#endif +} + +void QSGD3D12RectangleNode::setRect(const QRectF &rect) +{ + QSGGeometry::updateRectGeometry(&m_geometry, rect); + markDirty(QSGNode::DirtyGeometry); +} + +QRectF QSGD3D12RectangleNode::rect() const +{ + const QSGGeometry::Point2D *pts = m_geometry.vertexDataAsPoint2D(); + return QRectF(pts[0].x, + pts[0].y, + pts[3].x - pts[0].x, + pts[3].y - pts[0].y); +} + +void QSGD3D12RectangleNode::setColor(const QColor &color) +{ + if (color != m_material.color()) { + m_material.setColor(color); + markDirty(QSGNode::DirtyMaterial); + } +} + +QColor QSGD3D12RectangleNode::color() const +{ + return m_material.color(); +} + +QSGD3D12ImageNode::QSGD3D12ImageNode() + : m_geometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 4), + m_texCoordMode(QSGD3D12ImageNode::NoTransform), + m_isAtlasTexture(false), + m_ownsTexture(false) +{ + setGeometry(&m_geometry); + setMaterial(&m_material); + m_material.setMipmapFiltering(QSGTexture::None); +#ifdef QSG_RUNTIME_DESCRIPTION + qsgnode_set_description(this, QLatin1String("image")); +#endif +} + +QSGD3D12ImageNode::~QSGD3D12ImageNode() +{ + if (m_ownsTexture) + delete m_material.texture(); +} + +void QSGD3D12ImageNode::setFiltering(QSGTexture::Filtering filtering) +{ + if (m_material.filtering() == filtering) + return; + + m_material.setFiltering(filtering); + markDirty(DirtyMaterial); +} + +QSGTexture::Filtering QSGD3D12ImageNode::filtering() const +{ + return m_material.filtering(); +} + +void QSGD3D12ImageNode::setMipmapFiltering(QSGTexture::Filtering filtering) +{ + if (m_material.mipmapFiltering() == filtering) + return; + + m_material.setMipmapFiltering(filtering); + markDirty(DirtyMaterial); +} + +QSGTexture::Filtering QSGD3D12ImageNode::mipmapFiltering() const +{ + return m_material.mipmapFiltering(); +} + +void QSGD3D12ImageNode::setRect(const QRectF &r) +{ + if (m_rect == r) + return; + + m_rect = r; + QSGDefaultImageNode::rebuildGeometry(&m_geometry, texture(), m_rect, m_sourceRect, m_texCoordMode); + markDirty(DirtyGeometry); +} + +QRectF QSGD3D12ImageNode::rect() const +{ + return m_rect; +} + +void QSGD3D12ImageNode::setSourceRect(const QRectF &r) +{ + if (m_sourceRect == r) + return; + + m_sourceRect = r; + QSGDefaultImageNode::rebuildGeometry(&m_geometry, texture(), m_rect, m_sourceRect, m_texCoordMode); + markDirty(DirtyGeometry); +} + +QRectF QSGD3D12ImageNode::sourceRect() const +{ + return m_sourceRect; +} + +void QSGD3D12ImageNode::setTexture(QSGTexture *texture) +{ + Q_ASSERT(texture); + + if (m_ownsTexture) + delete m_material.texture(); + + m_material.setTexture(texture); + QSGDefaultImageNode::rebuildGeometry(&m_geometry, texture, m_rect, m_sourceRect, m_texCoordMode); + + DirtyState dirty = DirtyMaterial; + const bool wasAtlas = m_isAtlasTexture; + m_isAtlasTexture = texture->isAtlasTexture(); + if (wasAtlas || m_isAtlasTexture) + dirty |= DirtyGeometry; + + markDirty(dirty); +} + +QSGTexture *QSGD3D12ImageNode::texture() const +{ + return m_material.texture(); +} + +void QSGD3D12ImageNode::setTextureCoordinatesTransform(TextureCoordinatesTransformMode mode) +{ + if (m_texCoordMode == mode) + return; + + m_texCoordMode = mode; + QSGDefaultImageNode::rebuildGeometry(&m_geometry, texture(), m_rect, m_sourceRect, m_texCoordMode); + markDirty(DirtyMaterial); +} + +QSGD3D12ImageNode::TextureCoordinatesTransformMode QSGD3D12ImageNode::textureCoordinatesTransform() const +{ + return m_texCoordMode; +} + +void QSGD3D12ImageNode::setOwnsTexture(bool owns) +{ + m_ownsTexture = owns; +} + +bool QSGD3D12ImageNode::ownsTexture() const +{ + return m_ownsTexture; +} + +QSGD3D12NinePatchNode::QSGD3D12NinePatchNode() + : m_geometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 4) +{ + m_geometry.setDrawingMode(QSGGeometry::DrawTriangleStrip); + setGeometry(&m_geometry); + setMaterial(&m_material); +} + +QSGD3D12NinePatchNode::~QSGD3D12NinePatchNode() +{ + delete m_material.texture(); +} + +void QSGD3D12NinePatchNode::setTexture(QSGTexture *texture) +{ + delete m_material.texture(); + m_material.setTexture(texture); +} + +void QSGD3D12NinePatchNode::setBounds(const QRectF &bounds) +{ + m_bounds = bounds; +} + +void QSGD3D12NinePatchNode::setDevicePixelRatio(qreal ratio) +{ + m_devicePixelRatio = ratio; +} + +void QSGD3D12NinePatchNode::setPadding(qreal left, qreal top, qreal right, qreal bottom) +{ + m_padding = QVector4D(left, top, right, bottom); +} + +void QSGD3D12NinePatchNode::update() +{ + QSGDefaultNinePatchNode::rebuildGeometry(m_material.texture(), &m_geometry, m_padding, m_bounds, m_devicePixelRatio); + markDirty(QSGNode::DirtyGeometry | QSGNode::DirtyMaterial); +} + +QT_END_NAMESPACE diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12publicnodes_p.h b/src/plugins/scenegraph/d3d12/qsgd3d12publicnodes_p.h new file mode 100644 index 0000000000..6150083aaf --- /dev/null +++ b/src/plugins/scenegraph/d3d12/qsgd3d12publicnodes_p.h @@ -0,0 +1,136 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSGD3D12PUBLICNODES_P_H +#define QSGD3D12PUBLICNODES_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtQuick/qsgrectanglenode.h> +#include <QtQuick/qsgimagenode.h> +#include <QtQuick/qsgninepatchnode.h> +#include "qsgd3d12builtinmaterials_p.h" + +QT_BEGIN_NAMESPACE + +class QSGD3D12RectangleNode : public QSGRectangleNode +{ +public: + QSGD3D12RectangleNode(); + + void setRect(const QRectF &rect) override; + QRectF rect() const override; + + void setColor(const QColor &color) override; + QColor color() const override; + +private: + QSGGeometry m_geometry; + QSGD3D12FlatColorMaterial m_material; +}; + +class QSGD3D12ImageNode : public QSGImageNode +{ +public: + QSGD3D12ImageNode(); + ~QSGD3D12ImageNode(); + + void setRect(const QRectF &rect) override; + QRectF rect() const override; + + void setSourceRect(const QRectF &r) override; + QRectF sourceRect() const override; + + void setTexture(QSGTexture *texture) override; + QSGTexture *texture() const override; + + void setFiltering(QSGTexture::Filtering filtering) override; + QSGTexture::Filtering filtering() const override; + + void setMipmapFiltering(QSGTexture::Filtering filtering) override; + QSGTexture::Filtering mipmapFiltering() const override; + + void setTextureCoordinatesTransform(TextureCoordinatesTransformMode mode) override; + TextureCoordinatesTransformMode textureCoordinatesTransform() const override; + + void setOwnsTexture(bool owns) override; + bool ownsTexture() const override; + +private: + QSGGeometry m_geometry; + QSGD3D12TextureMaterial m_material; + QRectF m_rect; + QRectF m_sourceRect; + TextureCoordinatesTransformMode m_texCoordMode; + uint m_isAtlasTexture : 1; + uint m_ownsTexture : 1; +}; + +class QSGD3D12NinePatchNode : public QSGNinePatchNode +{ +public: + QSGD3D12NinePatchNode(); + ~QSGD3D12NinePatchNode(); + + void setTexture(QSGTexture *texture) override; + void setBounds(const QRectF &bounds) override; + void setDevicePixelRatio(qreal ratio) override; + void setPadding(qreal left, qreal top, qreal right, qreal bottom) override; + void update() override; + +private: + QSGGeometry m_geometry; + QSGD3D12TextureMaterial m_material; + QRectF m_bounds; + qreal m_devicePixelRatio; + QVector4D m_padding; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12rendercontext.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12rendercontext.cpp index b32bfe063a..4ee4656e63 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12rendercontext.cpp +++ b/src/plugins/scenegraph/d3d12/qsgd3d12rendercontext.cpp @@ -64,8 +64,22 @@ bool QSGD3D12RenderContext::isValid() const return m_engine != nullptr; } +void QSGD3D12RenderContext::initialize(void *) +{ + if (m_initialized) + return; + + m_initialized = true; + emit initialized(); +} + void QSGD3D12RenderContext::invalidate() { + if (!m_initialized) + return; + + m_initialized = false; + if (Q_UNLIKELY(debug_render())) qDebug("rendercontext invalidate engine %p, %d/%d/%d", m_engine, m_texturesToDelete.count(), m_textures.count(), m_fontEnginesToClean.count()); @@ -101,6 +115,11 @@ QSGRenderer *QSGD3D12RenderContext::createRenderer() return new QSGD3D12Renderer(this); } +int QSGD3D12RenderContext::maxTextureSize() const +{ + return 16384; // D3D12_REQ_TEXTURE2D_U_OR_V_DIMENSION +} + void QSGD3D12RenderContext::renderNextFrame(QSGRenderer *renderer, uint fbo) { static_cast<QSGD3D12Renderer *>(renderer)->renderScene(fbo); @@ -114,16 +133,37 @@ void QSGD3D12RenderContext::setEngine(QSGD3D12Engine *engine) m_engine = engine; if (m_engine) - emit initialized(); + initialize(nullptr); } -void QSGD3D12RenderContext::ensureInitializedEmitted() +QSGRendererInterface::GraphicsApi QSGD3D12RenderContext::graphicsApi() const { - if (!m_pendingInitialized) - return; + return Direct3D12; +} - m_pendingInitialized = false; - emit initialized(); +void *QSGD3D12RenderContext::getResource(QQuickWindow *window, Resource resource) const +{ + if (!m_engine) { + qWarning("getResource: No D3D12 engine available yet (window not exposed?)"); + return nullptr; + } + // window can be ignored since the rendercontext and engine are both per window + return m_engine->getResource(window, resource); +} + +QSGRendererInterface::ShaderType QSGD3D12RenderContext::shaderType() const +{ + return HLSL; +} + +QSGRendererInterface::ShaderCompilationTypes QSGD3D12RenderContext::shaderCompilationType() const +{ + return RuntimeCompilation | OfflineCompilation; +} + +QSGRendererInterface::ShaderSourceTypes QSGD3D12RenderContext::shaderSourceType() const +{ + return ShaderSourceString | ShaderSourceFile | ShaderByteCode; } QT_END_NAMESPACE diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12rendercontext_p.h b/src/plugins/scenegraph/d3d12/qsgd3d12rendercontext_p.h index 86a300831d..35aca100f4 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12rendercontext_p.h +++ b/src/plugins/scenegraph/d3d12/qsgd3d12rendercontext_p.h @@ -52,30 +52,37 @@ // #include <private/qsgcontext_p.h> +#include <qsgrendererinterface.h> QT_BEGIN_NAMESPACE class QSGD3D12Engine; -class QSGD3D12RenderContext : public QSGRenderContext +class QSGD3D12RenderContext : public QSGRenderContext, public QSGRendererInterface { public: QSGD3D12RenderContext(QSGContext *ctx); bool isValid() const override; + void initialize(void *context) override; void invalidate() override; void renderNextFrame(QSGRenderer *renderer, uint fbo) override; QSGTexture *createTexture(const QImage &image, uint flags) const override; QSGRenderer *createRenderer() override; + int maxTextureSize() const override; void setEngine(QSGD3D12Engine *engine); QSGD3D12Engine *engine() { return m_engine; } - void ensureInitializedEmitted(); - void setInitializedPending() { m_pendingInitialized = true; } + // QSGRendererInterface + GraphicsApi graphicsApi() const override; + void *getResource(QQuickWindow *window, Resource resource) const override; + ShaderType shaderType() const override; + ShaderCompilationTypes shaderCompilationType() const override; + ShaderSourceTypes shaderSourceType() const override; private: QSGD3D12Engine *m_engine = nullptr; - bool m_pendingInitialized = false; + bool m_initialized = false; }; QT_END_NAMESPACE diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12renderer.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12renderer.cpp index 5e5d7a13f8..ce633ae996 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12renderer.cpp +++ b/src/plugins/scenegraph/d3d12/qsgd3d12renderer.cpp @@ -451,6 +451,7 @@ struct RenderNodeState : public QSGRenderNode::RenderState bool scissorEnabled() const { return m_scissorEnabled; } int stencilValue() const { return m_stencilValue; } bool stencilEnabled() const { return m_stencilEnabled; } + const QRegion *clipRegion() const override { return nullptr; } const QMatrix4x4 *m_projectionMatrix; QRect m_scissorRect; diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12renderloop.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12renderloop.cpp index 067b0d35f6..c53a1fa6c1 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12renderloop.cpp +++ b/src/plugins/scenegraph/d3d12/qsgd3d12renderloop.cpp @@ -42,16 +42,12 @@ #include "qsgd3d12context_p.h" #include "qsgd3d12rendercontext_p.h" #include "qsgd3d12shadereffectnode_p.h" -#include <private/qsgrenderer_p.h> #include <private/qquickwindow_p.h> #include <private/qquickprofiler_p.h> #include <private/qquickanimatorcontroller_p.h> -#include <private/qquickprofiler_p.h> -#include <private/qqmldebugserviceinterfaces_p.h> -#include <private/qqmldebugconnector_p.h> #include <QElapsedTimer> -#include <QQueue> #include <QGuiApplication> +#include <QScreen> QT_BEGIN_NAMESPACE @@ -64,140 +60,9 @@ QT_BEGIN_NAMESPACE DECLARE_DEBUG_VAR(loop) DECLARE_DEBUG_VAR(time) -/* - The D3D render loop mostly mirrors the threaded OpenGL render loop. - - There are two classes here. QSGD3D12RenderLoop and QSGD3D12RenderThread. All - communication between the two is based on event passing and we have a number - of custom events. - - Render loop is per process, render thread is per window. The - QSGD3D12RenderContext and QSGD3D12Engine are per window as well. The former - is created (but not owned) by QQuickWindow. The D3D device is per process. - - In this implementation, the render thread is never blocked and the GUI - thread will initiate a polishAndSync which will block and wait for the - render thread to pick it up and release the block only after the render - thread is done syncing. The reason for this is: - - 1. Clear blocking paradigm. We only have one real "block" point - (polishAndSync()) and all blocking is initiated by GUI and picked up by - Render at specific times based on events. This makes the execution - deterministic. - - 2. Render does not have to interact with GUI. This is done so that the - render thread can run its own animation system which stays alive even when - the GUI thread is blocked doing I/O, object instantiation, QPainter-painting - or any other non-trivial task. - - The render thread has affinity to the GUI thread until a window is shown. - From that moment and until the window is destroyed, it will have affinity to - the render thread. (moved back at the end of run for cleanup). - */ - -// Passed from the RL to the RT when a window is removed obscured and should be -// removed from the render loop. -const QEvent::Type WM_Obscure = QEvent::Type(QEvent::User + 1); - -// Passed from the RL to RT when GUI has been locked, waiting for sync. -const QEvent::Type WM_RequestSync = QEvent::Type(QEvent::User + 2); - -// Passed by the RT to itself to trigger another render pass. This is typically -// a result of QQuickWindow::update(). -const QEvent::Type WM_RequestRepaint = QEvent::Type(QEvent::User + 3); - -// Passed by the RL to the RT to maybe release resource if no windows are -// rendering. -const QEvent::Type WM_TryRelease = QEvent::Type(QEvent::User + 4); - -// Passed by the RL to the RT when a QQuickWindow::grabWindow() is called. -const QEvent::Type WM_Grab = QEvent::Type(QEvent::User + 5); -// Passed by the window when there is a render job to run. -const QEvent::Type WM_PostJob = QEvent::Type(QEvent::User + 6); - -class QSGD3D12WindowEvent : public QEvent -{ -public: - QSGD3D12WindowEvent(QQuickWindow *c, QEvent::Type type) : QEvent(type), window(c) { } - QQuickWindow *window; -}; - -class QSGD3D12TryReleaseEvent : public QSGD3D12WindowEvent -{ -public: - QSGD3D12TryReleaseEvent(QQuickWindow *win, bool destroy) - : QSGD3D12WindowEvent(win, WM_TryRelease), destroying(destroy) { } - bool destroying; -}; - -class QSGD3D12SyncEvent : public QSGD3D12WindowEvent -{ -public: - QSGD3D12SyncEvent(QQuickWindow *c, bool inExpose, bool force) - : QSGD3D12WindowEvent(c, WM_RequestSync) - , size(c->size()) - , dpr(c->effectiveDevicePixelRatio()) - , syncInExpose(inExpose) - , forceRenderPass(force) { } - QSize size; - float dpr; - bool syncInExpose; - bool forceRenderPass; -}; - -class QSGD3D12GrabEvent : public QSGD3D12WindowEvent -{ -public: - QSGD3D12GrabEvent(QQuickWindow *c, QImage *result) - : QSGD3D12WindowEvent(c, WM_Grab), image(result) { } - QImage *image; -}; - -class QSGD3D12JobEvent : public QSGD3D12WindowEvent -{ -public: - QSGD3D12JobEvent(QQuickWindow *c, QRunnable *postedJob) - : QSGD3D12WindowEvent(c, WM_PostJob), job(postedJob) { } - ~QSGD3D12JobEvent() { delete job; } - QRunnable *job; -}; - -class QSGD3D12EventQueue : public QQueue<QEvent *> -{ -public: - void addEvent(QEvent *e) { - mutex.lock(); - enqueue(e); - if (waiting) - condition.wakeOne(); - mutex.unlock(); - } - - QEvent *takeEvent(bool wait) { - mutex.lock(); - if (isEmpty() && wait) { - waiting = true; - condition.wait(&mutex); - waiting = false; - } - QEvent *e = dequeue(); - mutex.unlock(); - return e; - } - - bool hasMoreEvents() { - mutex.lock(); - bool has = !isEmpty(); - mutex.unlock(); - return has; - } - -private: - QMutex mutex; - QWaitCondition condition; - bool waiting = false; -}; +// This render loop operates on the gui (main) thread. +// Conceptually it matches the OpenGL 'windows' render loop. static inline int qsgrl_animation_interval() { @@ -205,648 +70,206 @@ static inline int qsgrl_animation_interval() return refreshRate < 1 ? 16 : int(1000 / refreshRate); } -class QSGD3D12RenderThread : public QThread -{ - Q_OBJECT - -public: - QSGD3D12RenderThread(QSGD3D12RenderLoop *rl, QSGRenderContext *renderContext) - : renderLoop(rl) - { - rc = static_cast<QSGD3D12RenderContext *>(renderContext); - vsyncDelta = qsgrl_animation_interval(); - } - - ~QSGD3D12RenderThread() - { - delete rc; - } - - bool event(QEvent *e); - void run(); - - void syncAndRender(); - void sync(bool inExpose); - - void requestRepaint() - { - if (sleeping) - stopEventProcessing = true; - if (exposedWindow) - pendingUpdate |= RepaintRequest; - } - - void processEventsAndWaitForMore(); - void processEvents(); - void postEvent(QEvent *e); - - enum UpdateRequest { - SyncRequest = 0x01, - RepaintRequest = 0x02, - ExposeRequest = 0x04 | RepaintRequest | SyncRequest - }; - - QSGD3D12Engine *engine = nullptr; - QSGD3D12RenderLoop *renderLoop; - QSGD3D12RenderContext *rc; - QAnimationDriver *rtAnim = nullptr; - volatile bool active = false; - uint pendingUpdate = 0; - bool sleeping = false; - bool syncResultedInChanges = false; - float vsyncDelta; - QMutex mutex; - QWaitCondition waitCondition; - QQuickWindow *exposedWindow = nullptr; - bool stopEventProcessing = false; - QSGD3D12EventQueue eventQueue; - QElapsedTimer threadTimer; - qint64 syncTime; - qint64 renderTime; - qint64 sinceLastTime; - -public slots: - void onSceneGraphChanged() { - syncResultedInChanges = true; - } -}; - -bool QSGD3D12RenderThread::event(QEvent *e) +QSGD3D12RenderLoop::QSGD3D12RenderLoop() { - switch (e->type()) { - - case WM_Obscure: - Q_ASSERT(!exposedWindow || exposedWindow == static_cast<QSGD3D12WindowEvent *>(e)->window); - if (Q_UNLIKELY(debug_loop())) - qDebug() << "RT - WM_Obscure" << exposedWindow; - mutex.lock(); - if (exposedWindow) { - QQuickWindowPrivate::get(exposedWindow)->fireAboutToStop(); - if (Q_UNLIKELY(debug_loop())) - qDebug("RT - WM_Obscure - window removed"); - exposedWindow = nullptr; - } - waitCondition.wakeOne(); - mutex.unlock(); - return true; - - case WM_RequestSync: { - QSGD3D12SyncEvent *wme = static_cast<QSGD3D12SyncEvent *>(e); - if (sleeping) - stopEventProcessing = true; - // One thread+engine for each window. However, the native window may - // change in some (quite artificial) cases, e.g. due to a hide - - // destroy - show on the QWindow. - bool needsWindow = !engine->window(); - if (engine->window() && engine->window() != wme->window->winId()) { - if (Q_UNLIKELY(debug_loop())) - qDebug("RT - WM_RequestSync - native window handle changes for active engine"); - engine->waitGPU(); - QQuickWindowPrivate::get(wme->window)->cleanupNodesOnShutdown(); - QSGD3D12ShaderEffectNode::cleanupMaterialTypeCache(); - rc->invalidate(); - engine->releaseResources(); - needsWindow = true; - // Be nice and emit the rendercontext's initialized() later on at - // some point so that QQuickWindow::sceneGraphInitialized() behaves - // in a manner similar to GL. - rc->setInitializedPending(); - } - if (needsWindow) { - // Must only ever get here when there is no window or releaseResources() has been called. - const int samples = wme->window->format().samples(); - if (Q_UNLIKELY(debug_loop())) - qDebug() << "RT - WM_RequestSync - initializing D3D12 engine" << wme->window - << wme->size << wme->dpr << samples; - engine->attachToWindow(wme->window->winId(), wme->size, wme->dpr, samples); - } - exposedWindow = wme->window; - engine->setWindowSize(wme->size, wme->dpr); - if (Q_UNLIKELY(debug_loop())) - qDebug() << "RT - WM_RequestSync" << exposedWindow; - pendingUpdate |= SyncRequest; - if (wme->syncInExpose) { - if (Q_UNLIKELY(debug_loop())) - qDebug("RT - WM_RequestSync - triggered from expose"); - pendingUpdate |= ExposeRequest; - } - if (wme->forceRenderPass) { - if (Q_UNLIKELY(debug_loop())) - qDebug("RT - WM_RequestSync - repaint regardless"); - pendingUpdate |= RepaintRequest; - } - return true; - } - - case WM_TryRelease: { - if (Q_UNLIKELY(debug_loop())) - qDebug("RT - WM_TryRelease"); - mutex.lock(); - renderLoop->lockedForSync = true; - QSGD3D12TryReleaseEvent *wme = static_cast<QSGD3D12TryReleaseEvent *>(e); - // Only when no windows are exposed anymore or we are shutting down. - if (!exposedWindow || wme->destroying) { - if (Q_UNLIKELY(debug_loop())) - qDebug("RT - WM_TryRelease - invalidating rc"); - if (wme->window) { - QQuickWindowPrivate *wd = QQuickWindowPrivate::get(wme->window); - if (wme->destroying) { - // QSGNode destruction may release graphics resources in use so wait first. - engine->waitGPU(); - // Bye bye nodes... - wd->cleanupNodesOnShutdown(); - QSGD3D12ShaderEffectNode::cleanupMaterialTypeCache(); - } - rc->invalidate(); - QCoreApplication::processEvents(); - QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); - if (wme->destroying) - delete wd->animationController; - } - if (wme->destroying) - active = false; - if (sleeping) - stopEventProcessing = true; - } else { - if (Q_UNLIKELY(debug_loop())) - qDebug("RT - WM_TryRelease - not releasing because window is still active"); - } - waitCondition.wakeOne(); - renderLoop->lockedForSync = false; - mutex.unlock(); - return true; - } - - case WM_Grab: { - if (Q_UNLIKELY(debug_loop())) - qDebug("RT - WM_Grab"); - QSGD3D12GrabEvent *wme = static_cast<QSGD3D12GrabEvent *>(e); - Q_ASSERT(wme->window); - Q_ASSERT(wme->window == exposedWindow || !exposedWindow); - mutex.lock(); - if (wme->window) { - // Grabbing is generally done by rendering a frame and reading the - // color buffer contents back, without presenting, and then - // creating a QImage from the returned data. It is terribly - // inefficient since it involves a full blocking wait for the GPU. - // However, our hands are tied by the existing, synchronous APIs of - // QQuickWindow and such. - QQuickWindowPrivate *wd = QQuickWindowPrivate::get(wme->window); - rc->ensureInitializedEmitted(); - wd->syncSceneGraph(); - wd->renderSceneGraph(wme->window->size()); - *wme->image = engine->executeAndWaitReadbackRenderTarget(); - } - if (Q_UNLIKELY(debug_loop())) - qDebug("RT - WM_Grab - waking gui to handle result"); - waitCondition.wakeOne(); - mutex.unlock(); - return true; - } - - case WM_PostJob: { - if (Q_UNLIKELY(debug_loop())) - qDebug("RT - WM_PostJob"); - QSGD3D12JobEvent *wme = static_cast<QSGD3D12JobEvent *>(e); - Q_ASSERT(wme->window == exposedWindow); - if (exposedWindow) { - wme->job->run(); - delete wme->job; - wme->job = nullptr; - if (Q_UNLIKELY(debug_loop())) - qDebug("RT - WM_PostJob - job done"); - } - return true; - } - - case WM_RequestRepaint: - if (Q_UNLIKELY(debug_loop())) - qDebug("RT - WM_RequestPaint"); - // When GUI posts this event, it is followed by a polishAndSync, so we - // must not exit the event loop yet. - pendingUpdate |= RepaintRequest; - break; + if (Q_UNLIKELY(debug_loop())) + qDebug("new d3d12 render loop"); - default: - break; - } + sg = new QSGD3D12Context; - return QThread::event(e); -} + m_anims = sg->createAnimationDriver(this); + connect(m_anims, &QAnimationDriver::started, this, &QSGD3D12RenderLoop::onAnimationStarted); + connect(m_anims, &QAnimationDriver::stopped, this, &QSGD3D12RenderLoop::onAnimationStopped); + m_anims->install(); -void QSGD3D12RenderThread::postEvent(QEvent *e) -{ - eventQueue.addEvent(e); + m_vsyncDelta = qsgrl_animation_interval(); } -void QSGD3D12RenderThread::processEvents() +QSGD3D12RenderLoop::~QSGD3D12RenderLoop() { - while (eventQueue.hasMoreEvents()) { - QEvent *e = eventQueue.takeEvent(false); - event(e); - delete e; - } + delete sg; } -void QSGD3D12RenderThread::processEventsAndWaitForMore() +void QSGD3D12RenderLoop::show(QQuickWindow *window) { - stopEventProcessing = false; - while (!stopEventProcessing) { - QEvent *e = eventQueue.takeEvent(true); - event(e); - delete e; - } + if (Q_UNLIKELY(debug_loop())) + qDebug() << "show" << window; } -void QSGD3D12RenderThread::run() +void QSGD3D12RenderLoop::hide(QQuickWindow *window) { if (Q_UNLIKELY(debug_loop())) - qDebug("RT - run()"); - - engine = new QSGD3D12Engine; - rc->setEngine(engine); - - rtAnim = rc->sceneGraphContext()->createAnimationDriver(nullptr); - rtAnim->install(); - - if (QQmlDebugConnector::service<QQmlProfilerService>()) - QQuickProfiler::registerAnimationCallback(); - - while (active) { - if (exposedWindow) - syncAndRender(); - - processEvents(); - QCoreApplication::processEvents(); - - if (pendingUpdate == 0 || !exposedWindow) { - if (Q_UNLIKELY(debug_loop())) - qDebug("RT - done drawing, sleep"); - sleeping = true; - processEventsAndWaitForMore(); - sleeping = false; - } - } - - if (Q_UNLIKELY(debug_loop())) - qDebug("RT - run() exiting"); - - delete rtAnim; - rtAnim = nullptr; - - rc->moveToThread(renderLoop->thread()); - moveToThread(renderLoop->thread()); - - rc->setEngine(nullptr); - delete engine; - engine = nullptr; + qDebug() << "hide" << window; } -void QSGD3D12RenderThread::sync(bool inExpose) +void QSGD3D12RenderLoop::resize(QQuickWindow *window) { - if (Q_UNLIKELY(debug_loop())) - qDebug("RT - sync"); - - mutex.lock(); - Q_ASSERT_X(renderLoop->lockedForSync, "QSGD3D12RenderThread::sync()", "sync triggered with gui not locked"); - - // Recover from device loss. - if (!engine->hasResources()) { - if (Q_UNLIKELY(debug_loop())) - qDebug("RT - sync - device was lost, resetting scenegraph"); - QQuickWindowPrivate::get(exposedWindow)->cleanupNodesOnShutdown(); - QSGD3D12ShaderEffectNode::cleanupMaterialTypeCache(); - rc->invalidate(); - } - - if (engine->window()) { - QQuickWindowPrivate *wd = QQuickWindowPrivate::get(exposedWindow); - bool hadRenderer = wd->renderer != nullptr; - // If the scene graph was touched since the last sync() make sure it sends the - // changed signal. - if (wd->renderer) - wd->renderer->clearChangedFlag(); + if (!m_windows.contains(window) || window->size().isEmpty()) + return; - rc->ensureInitializedEmitted(); - wd->syncSceneGraph(); + if (Q_UNLIKELY(debug_loop())) + qDebug() << "resize" << window; - if (!hadRenderer && wd->renderer) { - if (Q_UNLIKELY(debug_loop())) - qDebug("RT - created renderer"); - syncResultedInChanges = true; - connect(wd->renderer, &QSGRenderer::sceneGraphChanged, this, - &QSGD3D12RenderThread::onSceneGraphChanged, Qt::DirectConnection); - } + const WindowData &data(m_windows[window]); - // Process deferred deletes now, directly after the sync as deleteLater - // on the GUI must now also have resulted in SG changes and the delete - // is a safe operation. - QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); - } + if (!data.exposed) + return; - if (!inExpose) { - if (Q_UNLIKELY(debug_loop())) - qDebug("RT - sync complete, waking gui"); - waitCondition.wakeOne(); - mutex.unlock(); - } + if (data.engine) + data.engine->setWindowSize(window->size(), window->effectiveDevicePixelRatio()); } -void QSGD3D12RenderThread::syncAndRender() +void QSGD3D12RenderLoop::windowDestroyed(QQuickWindow *window) { - if (Q_UNLIKELY(debug_time())) { - sinceLastTime = threadTimer.nsecsElapsed(); - threadTimer.start(); - } - Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphRenderLoopFrame); - - QElapsedTimer waitTimer; - waitTimer.start(); - if (Q_UNLIKELY(debug_loop())) - qDebug("RT - syncAndRender()"); - - syncResultedInChanges = false; - QQuickWindowPrivate *wd = QQuickWindowPrivate::get(exposedWindow); - - const bool repaintRequested = (pendingUpdate & RepaintRequest) || wd->customRenderStage; - const bool syncRequested = pendingUpdate & SyncRequest; - const bool exposeRequested = (pendingUpdate & ExposeRequest) == ExposeRequest; - pendingUpdate = 0; - - if (syncRequested) - sync(exposeRequested); - -#ifndef QSG_NO_RENDER_TIMING - if (Q_UNLIKELY(debug_time())) - syncTime = threadTimer.nsecsElapsed(); -#endif - Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRenderLoopFrame); + qDebug() << "window destroyed" << window; - if (!syncResultedInChanges && !repaintRequested) { - if (Q_UNLIKELY(debug_loop())) - qDebug("RT - no changes, render aborted"); - int waitTime = vsyncDelta - (int) waitTimer.elapsed(); - if (waitTime > 0) - msleep(waitTime); + if (!m_windows.contains(window)) return; - } - - if (Q_UNLIKELY(debug_loop())) - qDebug("RT - rendering started"); - - if (rtAnim->isRunning()) { - wd->animationController->lock(); - rtAnim->advance(); - wd->animationController->unlock(); - } - - bool canRender = wd->renderer != nullptr; - // Recover from device loss. - if (!engine->hasResources()) { - if (Q_UNLIKELY(debug_loop())) - qDebug("RT - syncAndRender - device was lost, posting FullUpdateRequest"); - // Cannot do anything here because gui is not locked. Request a new - // sync+render round on the gui thread and let the sync handle it. - QCoreApplication::postEvent(exposedWindow, new QEvent(QEvent::Type(QQuickWindowPrivate::FullUpdateRequest))); - canRender = false; - } - - if (canRender) { - wd->renderSceneGraph(engine->windowSize()); - if (Q_UNLIKELY(debug_time())) - renderTime = threadTimer.nsecsElapsed(); - Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRenderLoopFrame); - // The engine is able to have multiple frames in flight. This in effect is - // similar to BufferQueueingOpenGL. Provide an env var to force the - // traditional blocking swap behavior, just in case. - static bool blockOnEachFrame = qEnvironmentVariableIntValue("QT_D3D_BLOCKING_PRESENT") != 0; - - if (!wd->customRenderStage || !wd->customRenderStage->swap()) - engine->present(); - - if (blockOnEachFrame) - engine->waitGPU(); + QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window); + wd->fireAboutToStop(); - // The concept of "frame swaps" is quite misleading by default, when - // blockOnEachFrame is not used, but emit it for compatibility. - wd->fireFrameSwapped(); - } else { - Q_QUICK_SG_PROFILE_SKIP(QQuickProfiler::SceneGraphRenderLoopFrame, 1); - if (Q_UNLIKELY(debug_loop())) - qDebug("RT - window not ready, skipping render"); - } + WindowData &data(m_windows[window]); + QSGD3D12Engine *engine = data.engine; + QSGD3D12RenderContext *rc = data.rc; + m_windows.remove(window); - if (Q_UNLIKELY(debug_loop())) - qDebug("RT - rendering done"); + // QSGNode destruction may release graphics resources in use so wait first. + engine->waitGPU(); - if (exposeRequested) { - if (Q_UNLIKELY(debug_loop())) - qDebug("RT - wake gui after initial expose"); - waitCondition.wakeOne(); - mutex.unlock(); - } + // Bye bye nodes... + wd->cleanupNodesOnShutdown(); - if (Q_UNLIKELY(debug_time())) - qDebug("Frame rendered with 'd3d12' renderloop in %dms, sync=%d, render=%d, swap=%d - (on render thread)", - int(threadTimer.elapsed()), - int((syncTime/1000000)), - int((renderTime - syncTime) / 1000000), - int(threadTimer.elapsed() - renderTime / 1000000)); + QSGD3D12ShaderEffectNode::cleanupMaterialTypeCache(); - Q_QUICK_SG_PROFILE_END(QQuickProfiler::SceneGraphRenderLoopFrame); + rc->invalidate(); - static int devLossTest = qEnvironmentVariableIntValue("QT_D3D_TEST_DEVICE_LOSS"); - if (devLossTest > 0) { - static QElapsedTimer kt; - static bool timerRunning = false; - if (!timerRunning) { - kt.start(); - timerRunning = true; - } else if (kt.elapsed() > 5000) { - --devLossTest; - kt.restart(); - engine->simulateDeviceLoss(); - } - } -} - -template<class T> T *windowFor(const QVector<T> &list, QQuickWindow *window) -{ - for (const T &t : list) { - if (t.window == window) - return const_cast<T *>(&t); - } - return nullptr; -} - -QSGD3D12RenderLoop::QSGD3D12RenderLoop() -{ - if (Q_UNLIKELY(debug_loop())) - qDebug("new d3d12 render loop ctor"); + if (m_windows.isEmpty()) + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); - sg = new QSGD3D12Context; + delete rc; + delete engine; - anim = sg->createAnimationDriver(this); - connect(anim, &QAnimationDriver::started, this, &QSGD3D12RenderLoop::onAnimationStarted); - connect(anim, &QAnimationDriver::stopped, this, &QSGD3D12RenderLoop::onAnimationStopped); - anim->install(); + delete wd->animationController; } -QSGD3D12RenderLoop::~QSGD3D12RenderLoop() +void QSGD3D12RenderLoop::exposeWindow(QQuickWindow *window) { - if (Q_UNLIKELY(debug_loop())) - qDebug("new d3d12 render loop dtor"); + WindowData data; + data.exposed = true; + data.engine = new QSGD3D12Engine; + data.rc = static_cast<QSGD3D12RenderContext *>(QQuickWindowPrivate::get(window)->context); + data.rc->setEngine(data.engine); + m_windows[window] = data; - delete sg; -} + const int samples = window->format().samples(); + const bool alpha = window->format().alphaBufferSize() > 0; + const qreal dpr = window->effectiveDevicePixelRatio(); -void QSGD3D12RenderLoop::show(QQuickWindow *window) -{ if (Q_UNLIKELY(debug_loop())) - qDebug() << "show" << window; -} + qDebug() << "initializing D3D12 engine" << window << window->size() << dpr << samples << alpha; -void QSGD3D12RenderLoop::hide(QQuickWindow *window) -{ - if (Q_UNLIKELY(debug_loop())) - qDebug() << "hide" << window; - - if (window->isExposed()) - handleObscurity(windowFor(windows, window)); - - releaseResources(window); + data.engine->attachToWindow(window->winId(), window->size(), dpr, samples, alpha); } -void QSGD3D12RenderLoop::resize(QQuickWindow *window) +void QSGD3D12RenderLoop::obscureWindow(QQuickWindow *window) { - if (!window->isExposed() || window->size().isEmpty()) - return; - - if (Q_UNLIKELY(debug_loop())) - qDebug() << "resize" << window << window->size(); + m_windows[window].exposed = false; + QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window); + wd->fireAboutToStop(); } -void QSGD3D12RenderLoop::windowDestroyed(QQuickWindow *window) +void QSGD3D12RenderLoop::exposureChanged(QQuickWindow *window) { if (Q_UNLIKELY(debug_loop())) - qDebug() << "window destroyed" << window; + qDebug() << "exposure changed" << window << window->isExposed(); - WindowData *w = windowFor(windows, window); - if (!w) - return; + if (window->isExposed()) { + if (!m_windows.contains(window)) + exposeWindow(window); - handleObscurity(w); - handleResourceRelease(w, true); + // Stop non-visual animation timer as we now have a window rendering. + if (m_animationTimer && somethingVisible()) { + killTimer(m_animationTimer); + m_animationTimer = 0; + } + // If we have a pending timer and we get an expose, we need to stop it. + // Otherwise we get two frames and two animation ticks in the same time interval. + if (m_updateTimer) { + killTimer(m_updateTimer); + m_updateTimer = 0; + } - QSGD3D12RenderThread *thread = w->thread; - while (thread->isRunning()) - QThread::yieldCurrentThread(); + WindowData &data(m_windows[window]); + data.exposed = true; + data.updatePending = true; - Q_ASSERT(thread->thread() == QThread::currentThread()); - delete thread; + render(); - for (int i = 0; i < windows.size(); ++i) { - if (windows.at(i).window == window) { - windows.removeAt(i); - break; - } - } -} - -void QSGD3D12RenderLoop::exposureChanged(QQuickWindow *window) -{ - if (Q_UNLIKELY(debug_loop())) - qDebug() << "exposure changed" << window; + } else if (m_windows.contains(window)) { + obscureWindow(window); - if (window->isExposed()) { - handleExposure(window); - } else { - WindowData *w = windowFor(windows, window); - if (w) - handleObscurity(w); + // Potentially start the non-visual animation timer if nobody is rendering. + if (m_anims->isRunning() && !somethingVisible() && !m_animationTimer) + m_animationTimer = startTimer(m_vsyncDelta); } } QImage QSGD3D12RenderLoop::grab(QQuickWindow *window) { - if (Q_UNLIKELY(debug_loop())) - qDebug() << "grab" << window; - - WindowData *w = windowFor(windows, window); - // Have to support invisible (but created()'ed) windows as well. - // Unlike with GL, leaving that case for QQuickWindow to handle is not feasible. - const bool tempExpose = !w; - if (tempExpose) { - handleExposure(window); - w = windowFor(windows, window); - Q_ASSERT(w); - } - - if (!w->thread->isRunning()) - return QImage(); - - if (!window->handle()) - window->create(); + const bool tempExpose = !m_windows.contains(window); + if (tempExpose) + exposeWindow(window); - QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window); - wd->polishItems(); + m_windows[window].grabOnly = true; - QImage result; - w->thread->mutex.lock(); - lockedForSync = true; - w->thread->postEvent(new QSGD3D12GrabEvent(window, &result)); - w->thread->waitCondition.wait(&w->thread->mutex); - lockedForSync = false; - w->thread->mutex.unlock(); + renderWindow(window); - result.setDevicePixelRatio(window->effectiveDevicePixelRatio()); + QImage grabbed = m_grabContent; + m_grabContent = QImage(); if (tempExpose) - handleObscurity(w); + obscureWindow(window); - return result; + return grabbed; } -void QSGD3D12RenderLoop::update(QQuickWindow *window) +bool QSGD3D12RenderLoop::somethingVisible() const { - WindowData *w = windowFor(windows, window); - if (!w) - return; - - if (w->thread == QThread::currentThread()) { - w->thread->requestRepaint(); - return; + for (auto it = m_windows.constBegin(), itEnd = m_windows.constEnd(); it != itEnd; ++it) { + if (it.key()->isVisible() && it.key()->isExposed()) + return true; } + return false; +} - // We set forceRenderPass because we want to make sure the QQuickWindow - // actually does a full render pass after the next sync. - w->forceRenderPass = true; - scheduleUpdate(w); +void QSGD3D12RenderLoop::maybePostUpdateTimer() +{ + if (!m_updateTimer) { + if (Q_UNLIKELY(debug_loop())) + qDebug("starting update timer"); + m_updateTimer = startTimer(m_vsyncDelta / 3); + } } -void QSGD3D12RenderLoop::maybeUpdate(QQuickWindow *window) +void QSGD3D12RenderLoop::update(QQuickWindow *window) { - WindowData *w = windowFor(windows, window); - if (w) - scheduleUpdate(w); + maybeUpdate(window); } -// called in response to window->requestUpdate() -void QSGD3D12RenderLoop::handleUpdateRequest(QQuickWindow *window) +void QSGD3D12RenderLoop::maybeUpdate(QQuickWindow *window) { - if (Q_UNLIKELY(debug_loop())) - qDebug() << "handleUpdateRequest" << window; + if (!m_windows.contains(window) || !somethingVisible()) + return; - WindowData *w = windowFor(windows, window); - if (w) - polishAndSync(w, false); + m_windows[window].updatePending = true; + maybePostUpdateTimer(); } QAnimationDriver *QSGD3D12RenderLoop::animationDriver() const { - return anim; + return m_anims; } QSGContext *QSGD3D12RenderLoop::sceneGraphContext() const @@ -856,6 +279,8 @@ QSGContext *QSGD3D12RenderLoop::sceneGraphContext() const QSGRenderContext *QSGD3D12RenderLoop::createRenderContext(QSGContext *) const { + // The rendercontext and engine are per-window, like with the threaded + // loop, but unlike the non-threaded OpenGL variants. return sg->createRenderContext(); } @@ -863,19 +288,15 @@ void QSGD3D12RenderLoop::releaseResources(QQuickWindow *window) { if (Q_UNLIKELY(debug_loop())) qDebug() << "releaseResources" << window; - - WindowData *w = windowFor(windows, window); - if (w) - handleResourceRelease(w, false); } void QSGD3D12RenderLoop::postJob(QQuickWindow *window, QRunnable *job) { - WindowData *w = windowFor(windows, window); - if (w && w->thread && w->thread->exposedWindow) - w->thread->postEvent(new QSGD3D12JobEvent(window, job)); - else - delete job; + Q_UNUSED(window); + Q_ASSERT(job); + Q_ASSERT(window); + job->run(); + delete job; } QSurface::SurfaceType QSGD3D12RenderLoop::windowSurfaceType() const @@ -885,283 +306,229 @@ QSurface::SurfaceType QSGD3D12RenderLoop::windowSurfaceType() const bool QSGD3D12RenderLoop::interleaveIncubation() const { - bool somethingVisible = false; - for (const WindowData &w : windows) { - if (w.window->isVisible() && w.window->isExposed()) { - somethingVisible = true; - break; - } - } - return somethingVisible && anim->isRunning(); -} - -int QSGD3D12RenderLoop::flags() const -{ - return SupportsGrabWithoutExpose; + return m_anims->isRunning() && somethingVisible(); } -bool QSGD3D12RenderLoop::event(QEvent *e) +void QSGD3D12RenderLoop::onAnimationStarted() { - if (e->type() == QEvent::Timer) { - QTimerEvent *te = static_cast<QTimerEvent *>(e); - if (te->timerId() == animationTimer) { - anim->advance(); - emit timeToIncubate(); - return true; + if (!somethingVisible()) { + if (!m_animationTimer) { + if (Q_UNLIKELY(debug_loop())) + qDebug("starting non-visual animation timer"); + m_animationTimer = startTimer(m_vsyncDelta); } + } else { + maybePostUpdateTimer(); } - - return QObject::event(e); -} - -void QSGD3D12RenderLoop::onAnimationStarted() -{ - startOrStopAnimationTimer(); - - for (const WindowData &w : qAsConst(windows)) - w.window->requestUpdate(); } void QSGD3D12RenderLoop::onAnimationStopped() { - startOrStopAnimationTimer(); + if (m_animationTimer) { + if (Q_UNLIKELY(debug_loop())) + qDebug("stopping non-visual animation timer"); + killTimer(m_animationTimer); + m_animationTimer = 0; + } } -void QSGD3D12RenderLoop::startOrStopAnimationTimer() +bool QSGD3D12RenderLoop::event(QEvent *event) { - int exposedWindowCount = 0; - const WindowData *exposed = nullptr; - - for (int i = 0; i < windows.size(); ++i) { - const WindowData &w(windows[i]); - if (w.window->isVisible() && w.window->isExposed()) { - ++exposedWindowCount; - exposed = &w; + switch (event->type()) { + case QEvent::Timer: + { + QTimerEvent *te = static_cast<QTimerEvent *>(event); + if (te->timerId() == m_animationTimer) { + if (Q_UNLIKELY(debug_loop())) + qDebug("animation tick while no windows exposed"); + m_anims->advance(); + } else if (te->timerId() == m_updateTimer) { + if (Q_UNLIKELY(debug_loop())) + qDebug("update timeout - rendering"); + killTimer(m_updateTimer); + m_updateTimer = 0; + render(); } + return true; } - - if (animationTimer && (exposedWindowCount == 1 || !anim->isRunning())) { - killTimer(animationTimer); - animationTimer = 0; - // If animations are running, make sure we keep on animating - if (anim->isRunning()) - exposed->window->requestUpdate(); - } else if (!animationTimer && exposedWindowCount != 1 && anim->isRunning()) { - animationTimer = startTimer(qsgrl_animation_interval()); + default: + break; } + + return QObject::event(event); } -void QSGD3D12RenderLoop::handleExposure(QQuickWindow *window) +void QSGD3D12RenderLoop::render() { - if (Q_UNLIKELY(debug_loop())) - qDebug() << "handleExposure" << window; - - WindowData *w = windowFor(windows, window); - if (!w) { - if (Q_UNLIKELY(debug_loop())) - qDebug("adding window to list"); - WindowData win; - win.window = window; - QSGRenderContext *rc = QQuickWindowPrivate::get(window)->context; // will transfer ownership - win.thread = new QSGD3D12RenderThread(this, rc); - win.updateDuringSync = false; - win.forceRenderPass = true; // also covered by polishAndSync(inExpose=true), but doesn't hurt - windows.append(win); - w = &windows.last(); - } - - // set this early as we'll be rendering shortly anyway and this avoids - // special casing exposure in polishAndSync. - w->thread->exposedWindow = window; - - if (w->window->size().isEmpty() - || (w->window->isTopLevel() && !w->window->geometry().intersects(w->window->screen()->availableGeometry()))) { -#ifndef QT_NO_DEBUG - qWarning().noquote().nospace() << "QSGD3D12RenderLoop: expose event received for window " - << w->window << " with invalid geometry: " << w->window->geometry() - << " on " << w->window->screen(); -#endif + bool rendered = false; + for (auto it = m_windows.begin(), itEnd = m_windows.end(); it != itEnd; ++it) { + if (it->updatePending) { + it->updatePending = false; + renderWindow(it.key()); + rendered = true; + } } - if (!w->window->handle()) - w->window->create(); - - // Start render thread if it is not running - if (!w->thread->isRunning()) { + if (!rendered) { if (Q_UNLIKELY(debug_loop())) - qDebug("starting render thread"); - // Push a few things to the render thread. - QQuickAnimatorController *controller = QQuickWindowPrivate::get(w->window)->animationController; - if (controller->thread() != w->thread) - controller->moveToThread(w->thread); - if (w->thread->thread() == QThread::currentThread()) { - w->thread->rc->moveToThread(w->thread); - w->thread->moveToThread(w->thread); - } - - w->thread->active = true; - w->thread->start(); - - if (!w->thread->isRunning()) - qFatal("Render thread failed to start, aborting application."); + qDebug("render - no changes, sleep"); + QThread::msleep(m_vsyncDelta); } - polishAndSync(w, true); + if (m_anims->isRunning()) { + if (Q_UNLIKELY(debug_loop())) + qDebug("render - advancing animations"); - startOrStopAnimationTimer(); -} + m_anims->advance(); -void QSGD3D12RenderLoop::handleObscurity(WindowData *w) -{ - if (Q_UNLIKELY(debug_loop())) - qDebug() << "handleObscurity" << w->window; + // It is not given that animations triggered another maybeUpdate() + // and thus another render pass, so to keep things running, + // make sure there is another frame pending. + maybePostUpdateTimer(); - if (w->thread->isRunning()) { - w->thread->mutex.lock(); - w->thread->postEvent(new QSGD3D12WindowEvent(w->window, WM_Obscure)); - w->thread->waitCondition.wait(&w->thread->mutex); - w->thread->mutex.unlock(); + emit timeToIncubate(); } - - startOrStopAnimationTimer(); } -void QSGD3D12RenderLoop::scheduleUpdate(WindowData *w) +void QSGD3D12RenderLoop::renderWindow(QQuickWindow *window) { - if (!QCoreApplication::instance()) - return; + if (Q_UNLIKELY(debug_loop())) + qDebug() << "renderWindow" << window; - if (!w || !w->thread->isRunning()) + QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window); + if (!m_windows.contains(window) || !window->geometry().isValid()) return; - QThread *current = QThread::currentThread(); - if (current != QCoreApplication::instance()->thread() && (current != w->thread || !lockedForSync)) { - qWarning() << "Updates can only be scheduled from GUI thread or from QQuickItem::updatePaintNode()"; + WindowData &data(m_windows[window]); + if (!data.exposed) { // not the same as window->isExposed(), when grabbing invisible windows for instance + if (Q_UNLIKELY(debug_loop())) + qDebug("renderWindow - not exposed, abort"); return; } - if (current == w->thread) { - w->updateDuringSync = true; - return; - } + if (!data.grabOnly) + wd->flushFrameSynchronousEvents(); - w->window->requestUpdate(); -} + QElapsedTimer renderTimer; + qint64 renderTime = 0, syncTime = 0, polishTime = 0; + const bool profileFrames = debug_time(); + if (profileFrames) + renderTimer.start(); + Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphPolishFrame); -void QSGD3D12RenderLoop::handleResourceRelease(WindowData *w, bool destroying) -{ - if (Q_UNLIKELY(debug_loop())) - qDebug() << "handleResourceRelease" << (destroying ? "destroying" : "hide/releaseResources") << w->window; - - w->thread->mutex.lock(); - if (w->thread->isRunning() && w->thread->active) { - QQuickWindow *window = w->window; - - // Note that window->handle() is typically null by this time because - // the platform window is already destroyed. This should not be a - // problem for the D3D cleanup. - - w->thread->postEvent(new QSGD3D12TryReleaseEvent(window, destroying)); - w->thread->waitCondition.wait(&w->thread->mutex); - - // Avoid a shutdown race condition. - // If SG is invalidated and 'active' becomes false, the thread's run() - // method will exit. handleExposure() relies on QThread::isRunning() (because it - // potentially needs to start the thread again) and our mutex cannot be used to - // track the thread stopping, so we wait a few nanoseconds extra so the thread - // can exit properly. - if (!w->thread->active) - w->thread->wait(); - } - w->thread->mutex.unlock(); -} + wd->polishItems(); -void QSGD3D12RenderLoop::polishAndSync(WindowData *w, bool inExpose) -{ - if (Q_UNLIKELY(debug_loop())) - qDebug() << "polishAndSync" << (inExpose ? "(in expose)" : "(normal)") << w->window; + if (profileFrames) + polishTime = renderTimer.nsecsElapsed(); + Q_QUICK_SG_PROFILE_SWITCH(QQuickProfiler::SceneGraphPolishFrame, + QQuickProfiler::SceneGraphRenderLoopFrame); + + emit window->afterAnimating(); - QQuickWindow *window = w->window; - if (!w->thread || !w->thread->exposedWindow) { + // The native window may change in some (quite artificial) cases, e.g. due + // to a hide - destroy - show on the QWindow. + bool needsWindow = !data.engine->window(); + if (data.engine->window() && data.engine->window() != window->winId()) { if (Q_UNLIKELY(debug_loop())) - qDebug("polishAndSync - not exposed, abort"); - return; + qDebug("sync - native window handle changes for active engine"); + data.engine->waitGPU(); + wd->cleanupNodesOnShutdown(); + QSGD3D12ShaderEffectNode::cleanupMaterialTypeCache(); + data.rc->invalidate(); + data.engine->releaseResources(); + needsWindow = true; + } + if (needsWindow) { + // Must only ever get here when there is no window or releaseResources() has been called. + const int samples = window->format().samples(); + const bool alpha = window->format().alphaBufferSize() > 0; + const qreal dpr = window->effectiveDevicePixelRatio(); + if (Q_UNLIKELY(debug_loop())) + qDebug() << "sync - reinitializing D3D12 engine" << window << window->size() << dpr << samples << alpha; + data.engine->attachToWindow(window->winId(), window->size(), dpr, samples, alpha); } - // Flush pending touch events. - QQuickWindowPrivate::get(window)->flushFrameSynchronousEvents(); - // The delivery of the event might have caused the window to stop rendering - w = windowFor(windows, window); - if (!w || !w->thread || !w->thread->exposedWindow) { + // Recover from device loss. + if (!data.engine->hasResources()) { if (Q_UNLIKELY(debug_loop())) - qDebug("polishAndSync - removed after touch event flushing, abort"); - return; + qDebug("sync - device was lost, resetting scenegraph"); + wd->cleanupNodesOnShutdown(); + QSGD3D12ShaderEffectNode::cleanupMaterialTypeCache(); + data.rc->invalidate(); } - QElapsedTimer timer; - qint64 polishTime = 0; - qint64 waitTime = 0; - qint64 syncTime = 0; - if (Q_UNLIKELY(debug_time())) - timer.start(); - Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphPolishAndSync); + data.rc->initialize(nullptr); - QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window); - wd->polishItems(); + wd->syncSceneGraph(); - if (Q_UNLIKELY(debug_time())) - polishTime = timer.nsecsElapsed(); - Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphPolishAndSync); + if (profileFrames) + syncTime = renderTimer.nsecsElapsed(); + Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRenderLoopFrame); - w->updateDuringSync = false; + wd->renderSceneGraph(window->size()); - emit window->afterAnimating(); + if (profileFrames) + renderTime = renderTimer.nsecsElapsed(); + Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRenderLoopFrame); - if (Q_UNLIKELY(debug_loop())) - qDebug("polishAndSync - lock for sync"); - w->thread->mutex.lock(); - lockedForSync = true; - w->thread->postEvent(new QSGD3D12SyncEvent(window, inExpose, w->forceRenderPass)); - w->forceRenderPass = false; + if (!data.grabOnly) { + // The engine is able to have multiple frames in flight. This in effect is + // similar to BufferQueueingOpenGL. Provide an env var to force the + // traditional blocking swap behavior, just in case. + static bool blockOnEachFrame = qEnvironmentVariableIntValue("QT_D3D_BLOCKING_PRESENT") != 0; - if (Q_UNLIKELY(debug_loop())) - qDebug("polishAndSync - wait for sync"); - if (Q_UNLIKELY(debug_time())) - waitTime = timer.nsecsElapsed(); - Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphPolishAndSync); - w->thread->waitCondition.wait(&w->thread->mutex); - lockedForSync = false; - w->thread->mutex.unlock(); - if (Q_UNLIKELY(debug_loop())) - qDebug("polishAndSync - unlock after sync"); + if (window->isVisible()) { + data.engine->present(); + if (blockOnEachFrame) + data.engine->waitGPU(); + // The concept of "frame swaps" is quite misleading by default, when + // blockOnEachFrame is not used, but emit it for compatibility. + wd->fireFrameSwapped(); + } else { + if (blockOnEachFrame) + data.engine->waitGPU(); + } + } else { + m_grabContent = data.engine->executeAndWaitReadbackRenderTarget(); + data.grabOnly = false; + } - if (Q_UNLIKELY(debug_time())) - syncTime = timer.nsecsElapsed(); - Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphPolishAndSync); + qint64 swapTime = 0; + if (profileFrames) + swapTime = renderTimer.nsecsElapsed(); + Q_QUICK_SG_PROFILE_END(QQuickProfiler::SceneGraphRenderLoopFrame); - if (!animationTimer && anim->isRunning()) { - if (Q_UNLIKELY(debug_loop())) - qDebug("polishAndSync - advancing animations"); - anim->advance(); - // We need to trigger another sync to keep animations running... - w->window->requestUpdate(); - emit timeToIncubate(); - } else if (w->updateDuringSync) { - w->window->requestUpdate(); + if (Q_UNLIKELY(debug_time())) { + static QTime lastFrameTime = QTime::currentTime(); + qDebug("Frame rendered with 'd3d12' renderloop in %dms, polish=%d, sync=%d, render=%d, swap=%d, frameDelta=%d", + int(swapTime / 1000000), + int(polishTime / 1000000), + int((syncTime - polishTime) / 1000000), + int((renderTime - syncTime) / 1000000), + int((swapTime - renderTime) / 10000000), + int(lastFrameTime.msecsTo(QTime::currentTime()))); + lastFrameTime = QTime::currentTime(); } - if (Q_UNLIKELY(debug_time())) - qDebug().nospace() - << "Frame prepared with 'd3d12' renderloop" - << ", polish=" << (polishTime / 1000000) - << ", lock=" << (waitTime - polishTime) / 1000000 - << ", blockedForSync=" << (syncTime - waitTime) / 1000000 - << ", animations=" << (timer.nsecsElapsed() - syncTime) / 1000000 - << " - (on gui thread) " << window; - - Q_QUICK_SG_PROFILE_END(QQuickProfiler::SceneGraphPolishAndSync); + // Simulate device loss if requested. + static int devLossTest = qEnvironmentVariableIntValue("QT_D3D_TEST_DEVICE_LOSS"); + if (devLossTest > 0) { + static QElapsedTimer kt; + static bool timerRunning = false; + if (!timerRunning) { + kt.start(); + timerRunning = true; + } else if (kt.elapsed() > 5000) { + --devLossTest; + kt.restart(); + data.engine->simulateDeviceLoss(); + } + } } -#include "qsgd3d12renderloop.moc" +int QSGD3D12RenderLoop::flags() const +{ + return SupportsGrabWithoutExpose; +} QT_END_NAMESPACE diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12renderloop_p.h b/src/plugins/scenegraph/d3d12/qsgd3d12renderloop_p.h index 732f8dd5d2..c0333ffad0 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12renderloop_p.h +++ b/src/plugins/scenegraph/d3d12/qsgd3d12renderloop_p.h @@ -58,7 +58,6 @@ QT_BEGIN_NAMESPACE class QSGD3D12Engine; class QSGD3D12Context; class QSGD3D12RenderContext; -class QSGD3D12RenderThread; class QSGD3D12RenderLoop : public QSGRenderLoop { @@ -80,7 +79,6 @@ public: void update(QQuickWindow *window) override; void maybeUpdate(QQuickWindow *window) override; - void handleUpdateRequest(QQuickWindow *window) override; QAnimationDriver *animationDriver() const override; @@ -94,34 +92,37 @@ public: bool interleaveIncubation() const override; int flags() const override; - bool event(QEvent *e) override; + bool event(QEvent *event) override; public Q_SLOTS: void onAnimationStarted(); void onAnimationStopped(); private: + void exposeWindow(QQuickWindow *window); + void obscureWindow(QQuickWindow *window); + void renderWindow(QQuickWindow *window); + void render(); + void maybePostUpdateTimer(); + bool somethingVisible() const; + + QSGD3D12Context *sg; + QAnimationDriver *m_anims; + int m_vsyncDelta; + int m_updateTimer = 0; + int m_animationTimer = 0; + struct WindowData { - QQuickWindow *window; - QSGD3D12RenderThread *thread; - uint updateDuringSync : 1; - uint forceRenderPass : 1; + QSGD3D12RenderContext *rc = nullptr; + QSGD3D12Engine *engine = nullptr; + bool updatePending = false; + bool grabOnly = false; + bool exposed = false; }; - void startOrStopAnimationTimer(); - void handleExposure(QQuickWindow *window); - void handleObscurity(WindowData *w); - void scheduleUpdate(WindowData *w); - void handleResourceRelease(WindowData *w, bool destroying); - void polishAndSync(WindowData *w, bool inExpose); - - QSGD3D12Context *sg; - QAnimationDriver *anim; - int animationTimer = 0; - bool lockedForSync = false; - QVector<WindowData> windows; + QHash<QQuickWindow *, WindowData> m_windows; - friend class QSGD3D12RenderThread; + QImage m_grabContent; }; QT_END_NAMESPACE diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12shadereffectnode.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12shadereffectnode.cpp index f77719f876..62771eb8f9 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12shadereffectnode.cpp +++ b/src/plugins/scenegraph/d3d12/qsgd3d12shadereffectnode.cpp @@ -41,7 +41,9 @@ #include "qsgd3d12rendercontext_p.h" #include "qsgd3d12texture_p.h" #include "qsgd3d12engine_p.h" +#include <QtCore/qthreadpool.h> #include <QtCore/qfile.h> +#include <QtCore/qfileselector.h> #include <QtQml/qqmlfile.h> #include <qsgtextureprovider.h> @@ -59,7 +61,7 @@ QT_BEGIN_NAMESPACE static bool debug_ ## variable() \ { static bool value = qgetenv("QSG_RENDERER_DEBUG").contains(QT_STRINGIFY(variable)); return value; } -DECLARE_DEBUG_VAR(render) +DECLARE_DEBUG_VAR(shader) void QSGD3D12ShaderLinker::reset(const QByteArray &vertBlob, const QByteArray &fragBlob) { @@ -134,8 +136,7 @@ void QSGD3D12ShaderLinker::feedSamplers(const QSGShaderEffectNode::ShaderData &s for (int i = 0; i < shader.shaderInfo.variables.count(); ++i) { const auto &var(shader.shaderInfo.variables.at(i)); if (var.type == QSGGuiThreadShaderEffectManager::ShaderInfo::Sampler) { - const auto &vd(shader.varData.at(i)); - Q_ASSERT(vd.specialType == QSGShaderEffectNode::VariableData::Unused); + Q_ASSERT(shader.varData.at(i).specialType == QSGShaderEffectNode::VariableData::Unused); samplers.insert(var.bindPoint); } } @@ -597,7 +598,7 @@ QRectF QSGD3D12ShaderEffectNode::updateNormalizedTextureSubRect(bool supportsAtl void QSGD3D12ShaderEffectNode::syncMaterial(SyncData *syncData) { - if (Q_UNLIKELY(debug_render())) + if (Q_UNLIKELY(debug_shader())) qDebug() << "shadereffect node sync" << syncData->dirty; if (bool(m_material.flags() & QSGMaterial::Blending) != syncData->blending) { @@ -714,7 +715,7 @@ void QSGD3D12ShaderEffectNode::syncMaterial(SyncData *syncData) markDirty(QSGNode::DirtyMaterial); - if (Q_UNLIKELY(debug_render())) + if (Q_UNLIKELY(debug_shader())) m_material.linker.dump(); } else { if (syncData->dirty & QSGShaderEffectNode::DirtyShaderConstant) { @@ -723,7 +724,7 @@ void QSGD3D12ShaderEffectNode::syncMaterial(SyncData *syncData) if (!syncData->fragment.dirtyConstants->isEmpty()) m_material.linker.feedConstants(*syncData->fragment.shader, syncData->fragment.dirtyConstants); markDirty(QSGNode::DirtyMaterial); - if (Q_UNLIKELY(debug_render())) + if (Q_UNLIKELY(debug_shader())) m_material.linker.dump(); } @@ -735,7 +736,7 @@ void QSGD3D12ShaderEffectNode::syncMaterial(SyncData *syncData) m_material.linker.linkTextureSubRects(); m_material.updateTextureProviders(false); markDirty(QSGNode::DirtyMaterial); - if (Q_UNLIKELY(debug_render())) + if (Q_UNLIKELY(debug_shader())) m_material.linker.dump(); } } @@ -777,12 +778,12 @@ bool QSGD3D12GuiThreadShaderEffectManager::hasSeparateSamplerAndTextureObjects() QString QSGD3D12GuiThreadShaderEffectManager::log() const { - return QString(); + return m_log; } QSGGuiThreadShaderEffectManager::Status QSGD3D12GuiThreadShaderEffectManager::status() const { - return Compiled; + return m_status; } struct RefGuard { @@ -791,17 +792,101 @@ struct RefGuard { IUnknown *p; }; -bool QSGD3D12GuiThreadShaderEffectManager::reflect(const QByteArray &src, ShaderInfo *result) +class QSGD3D12ShaderCompileTask : public QRunnable { - const QString fn = QQmlFile::urlToLocalFileOrQrc(src); - QFile f(fn); - if (!f.open(QIODevice::ReadOnly)) { - qWarning("ShaderEffect: Failed to read %s", qPrintable(fn)); - return false; +public: + QSGD3D12ShaderCompileTask(QSGD3D12GuiThreadShaderEffectManager *mgr, + QSGD3D12GuiThreadShaderEffectManager::ShaderInfo::Type typeHint, + const QByteArray &src, + QSGD3D12GuiThreadShaderEffectManager::ShaderInfo *result) + : mgr(mgr), typeHint(typeHint), src(src), result(result) { } + + void run() override; + +private: + QSGD3D12GuiThreadShaderEffectManager *mgr; + QSGD3D12GuiThreadShaderEffectManager::ShaderInfo::Type typeHint; + QByteArray src; + QSGD3D12GuiThreadShaderEffectManager::ShaderInfo *result; +}; + +void QSGD3D12ShaderCompileTask::run() +{ + const char *target = typeHint == QSGD3D12GuiThreadShaderEffectManager::ShaderInfo::TypeVertex ? "vs_5_0" : "ps_5_0"; + ID3DBlob *bytecode = nullptr; + ID3DBlob *errors = nullptr; + HRESULT hr = D3DCompile(src.constData(), src.size(), nullptr, nullptr, nullptr, + "main", target, 0, 0, &bytecode, &errors); + if (FAILED(hr) || !bytecode) { + qWarning("HLSL shader compilation failed: 0x%x", hr); + if (errors) { + mgr->m_log += QString::fromUtf8(static_cast<const char *>(errors->GetBufferPointer()), errors->GetBufferSize()); + errors->Release(); + } + mgr->m_status = QSGGuiThreadShaderEffectManager::Error; + emit mgr->shaderCodePrepared(false, typeHint, src, result); + emit mgr->logAndStatusChanged(); + return; } - result->blob = f.readAll(); - f.close(); + result->blob.resize(bytecode->GetBufferSize()); + memcpy(result->blob.data(), bytecode->GetBufferPointer(), result->blob.size()); + bytecode->Release(); + + const bool ok = mgr->reflect(result); + mgr->m_status = ok ? QSGGuiThreadShaderEffectManager::Compiled : QSGGuiThreadShaderEffectManager::Error; + emit mgr->shaderCodePrepared(ok, typeHint, src, result); + emit mgr->logAndStatusChanged(); +} + +static const int BYTECODE_MAGIC = 0x43425844; // 'DXBC' + +void QSGD3D12GuiThreadShaderEffectManager::prepareShaderCode(ShaderInfo::Type typeHint, const QByteArray &src, ShaderInfo *result) +{ + // The D3D12 backend's ShaderEffect implementation supports both HLSL + // source strings and bytecode or source in files as input. Bytecode is + // strongly recommended, but in order to make ShaderEffect users' (and + // anything that stiches shader strings together dynamically, e.g. + // qtgraphicaleffects) life easier, and since we link to d3dcompiler + // anyways, compiling from source is also supported. + + QByteArray shaderSourceCode = src; + QUrl srcUrl(QString::fromUtf8(src)); + if (!srcUrl.scheme().compare(QLatin1String("qrc"), Qt::CaseInsensitive) || srcUrl.isLocalFile()) { + if (!m_fileSelector) { + m_fileSelector = new QFileSelector(this); + m_fileSelector->setExtraSelectors(QStringList() << QStringLiteral("hlsl")); + } + const QString fn = m_fileSelector->select(QQmlFile::urlToLocalFileOrQrc(srcUrl)); + QFile f(fn); + if (!f.open(QIODevice::ReadOnly)) { + qWarning("ShaderEffect: Failed to read %s", qPrintable(fn)); + emit shaderCodePrepared(false, typeHint, src, result); + return; + } + QByteArray blob = f.readAll(); + f.close(); + if (blob.size() > 4) { + const quint32 *p = reinterpret_cast<const quint32 *>(blob.constData()); + if (*p == BYTECODE_MAGIC) { + // already compiled D3D bytecode, skip straight to reflection + result->blob = blob; + const bool ok = reflect(result); + m_status = ok ? Compiled : Error; + emit shaderCodePrepared(ok, typeHint, src, result); + emit logAndStatusChanged(); + return; + } + // assume the file contained HLSL source code + shaderSourceCode = blob; + } + } + + QThreadPool::globalInstance()->start(new QSGD3D12ShaderCompileTask(this, typeHint, shaderSourceCode, result)); +} + +bool QSGD3D12GuiThreadShaderEffectManager::reflect(ShaderInfo *result) +{ ID3D12ShaderReflection *reflector; HRESULT hr = D3DReflect(result->blob.constData(), result->blob.size(), IID_PPV_ARGS(&reflector)); if (FAILED(hr)) { @@ -854,7 +939,7 @@ bool QSGD3D12GuiThreadShaderEffectManager::reflect(const QByteArray &src, Shader return false; } - if (Q_UNLIKELY(debug_render())) + if (Q_UNLIKELY(debug_shader())) qDebug("Shader reflection size %d type %d v%u.%u input elems %d cbuffers %d boundres %d", result->blob.size(), result->type, major, minor, ieCount, cbufferCount, boundResCount); @@ -951,7 +1036,7 @@ bool QSGD3D12GuiThreadShaderEffectManager::reflect(const QByteArray &src, Shader } } - if (Q_UNLIKELY(debug_render())) { + if (Q_UNLIKELY(debug_shader())) { qDebug() << "Input:" << result->inputParameters; qDebug() << "Variables:" << result->variables << "cbuffer size" << result->constantDataSize; } diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12shadereffectnode_p.h b/src/plugins/scenegraph/d3d12/qsgd3d12shadereffectnode_p.h index edeaba899b..ee17e59130 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12shadereffectnode_p.h +++ b/src/plugins/scenegraph/d3d12/qsgd3d12shadereffectnode_p.h @@ -60,6 +60,7 @@ class QSGD3D12RenderContext; class QSGD3D12GuiThreadShaderEffectManager; class QSGD3D12ShaderEffectNode; class QSGD3D12Texture; +class QFileSelector; class QSGD3D12ShaderLinker { @@ -160,7 +161,15 @@ public: QString log() const override; Status status() const override; - bool reflect(const QByteArray &src, ShaderInfo *result) override; + void prepareShaderCode(ShaderInfo::Type typeHint, const QByteArray &src, ShaderInfo *result) override; + +private: + bool reflect(ShaderInfo *result); + QString m_log; + Status m_status = Uncompiled; + QFileSelector *m_fileSelector = nullptr; + + friend class QSGD3D12ShaderCompileTask; }; QT_END_NAMESPACE diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12spritenode.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12spritenode.cpp new file mode 100644 index 0000000000..bad222dbaa --- /dev/null +++ b/src/plugins/scenegraph/d3d12/qsgd3d12spritenode.cpp @@ -0,0 +1,314 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgd3d12spritenode_p.h" +#include "qsgd3d12material_p.h" + +#include "vs_sprite.hlslh" +#include "ps_sprite.hlslh" + +QT_BEGIN_NAMESPACE + +struct SpriteVertex +{ + float x; + float y; + float tx; + float ty; +}; + +struct SpriteVertices +{ + SpriteVertex v1; + SpriteVertex v2; + SpriteVertex v3; + SpriteVertex v4; +}; + +class QSGD3D12SpriteMaterial : public QSGD3D12Material +{ +public: + QSGD3D12SpriteMaterial(); + ~QSGD3D12SpriteMaterial(); + + QSGMaterialType *type() const override { static QSGMaterialType type; return &type; } + + int compare(const QSGMaterial *other) const override + { + return this - static_cast<const QSGD3D12SpriteMaterial *>(other); + } + + int constantBufferSize() const override; + void preparePipeline(QSGD3D12PipelineState *pipelineState) override; + UpdateResults updatePipeline(const QSGD3D12MaterialRenderState &state, + QSGD3D12PipelineState *pipelineState, + ExtraState *extraState, + quint8 *constantBuffer) override; + + QSGTexture *texture; + + float animT; + float animX1; + float animY1; + float animX2; + float animY2; + float animW; + float animH; +}; + +QSGD3D12SpriteMaterial::QSGD3D12SpriteMaterial() + : texture(nullptr), + animT(0.0f), + animX1(0.0f), + animY1(0.0f), + animX2(0.0f), + animY2(0.0f), + animW(1.0f), + animH(1.0f) +{ + setFlag(Blending, true); +} + +QSGD3D12SpriteMaterial::~QSGD3D12SpriteMaterial() +{ + delete texture; +} + +static const int SPRITE_CB_SIZE_0 = 16 * sizeof(float); // float4x4 +static const int SPRITE_CB_SIZE_1 = 4 * sizeof(float); // float4 +static const int SPRITE_CB_SIZE_2 = 3 * sizeof(float); // float3 +static const int SPRITE_CB_SIZE_3 = sizeof(float); // float +static const int SPRITE_CB_SIZE = SPRITE_CB_SIZE_0 + SPRITE_CB_SIZE_1 + SPRITE_CB_SIZE_2 + SPRITE_CB_SIZE_3; + +int QSGD3D12SpriteMaterial::constantBufferSize() const +{ + return QSGD3D12Engine::alignedConstantBufferSize(SPRITE_CB_SIZE); +} + +void QSGD3D12SpriteMaterial::preparePipeline(QSGD3D12PipelineState *pipelineState) +{ + pipelineState->shaders.vs = g_VS_Sprite; + pipelineState->shaders.vsSize = sizeof(g_VS_Sprite); + pipelineState->shaders.ps = g_PS_Sprite; + pipelineState->shaders.psSize = sizeof(g_PS_Sprite); + + pipelineState->shaders.rootSig.textureViewCount = 1; +} + +QSGD3D12Material::UpdateResults QSGD3D12SpriteMaterial::updatePipeline(const QSGD3D12MaterialRenderState &state, + QSGD3D12PipelineState *, + ExtraState *, + quint8 *constantBuffer) +{ + QSGD3D12Material::UpdateResults r = UpdatedConstantBuffer; + quint8 *p = constantBuffer; + + if (state.isMatrixDirty()) + memcpy(p, state.combinedMatrix().constData(), SPRITE_CB_SIZE_0); + p += SPRITE_CB_SIZE_0; + + { + const float v[] = { animX1, animY1, animX2, animY2 }; + memcpy(p, v, SPRITE_CB_SIZE_1); + } + p += SPRITE_CB_SIZE_1; + + { + const float v[] = { animW, animH, animT }; + memcpy(p, v, SPRITE_CB_SIZE_2); + } + p += SPRITE_CB_SIZE_2; + + if (state.isOpacityDirty()) { + const float opacity = state.opacity(); + memcpy(p, &opacity, SPRITE_CB_SIZE_3); + } + + texture->bind(); + + return r; +} + +static QSGGeometry::Attribute Sprite_Attributes[] = { + QSGGeometry::Attribute::createWithSemantic(0, 2, QSGGeometry::TypeFloat, QSGGeometry::Attribute::POSITION), + QSGGeometry::Attribute::createWithSemantic(1, 2, QSGGeometry::TypeFloat, QSGGeometry::Attribute::TEXCOORD), +}; + +static QSGGeometry::AttributeSet Sprite_AttributeSet = { 2, 4 * sizeof(float), Sprite_Attributes }; + +QSGD3D12SpriteNode::QSGD3D12SpriteNode() + : m_material(new QSGD3D12SpriteMaterial) + , m_geometryDirty(true) + , m_sheetSize(QSize(64, 64)) +{ + m_geometry = new QSGGeometry(Sprite_AttributeSet, 4, 6); + m_geometry->setDrawingMode(QSGGeometry::DrawTriangles); + + quint16 *indices = m_geometry->indexDataAsUShort(); + indices[0] = 0; + indices[1] = 1; + indices[2] = 2; + indices[3] = 1; + indices[4] = 3; + indices[5] = 2; + + setGeometry(m_geometry); + setMaterial(m_material); + setFlag(OwnsGeometry, true); + setFlag(OwnsMaterial, true); +} + +void QSGD3D12SpriteNode::setTexture(QSGTexture *texture) +{ + m_material->texture = texture; + m_geometryDirty = true; + markDirty(DirtyMaterial); +} + +void QSGD3D12SpriteNode::setTime(float time) +{ + m_material->animT = time; + markDirty(DirtyMaterial); +} + +void QSGD3D12SpriteNode::setSourceA(const QPoint &source) +{ + if (m_sourceA != source) { + m_sourceA = source; + m_material->animX1 = static_cast<float>(source.x()) / m_sheetSize.width(); + m_material->animY1 = static_cast<float>(source.y()) / m_sheetSize.height(); + markDirty(DirtyMaterial); + } +} + +void QSGD3D12SpriteNode::setSourceB(const QPoint &source) +{ + if (m_sourceB != source) { + m_sourceB = source; + m_material->animX2 = static_cast<float>(source.x()) / m_sheetSize.width(); + m_material->animY2 = static_cast<float>(source.y()) / m_sheetSize.height(); + markDirty(DirtyMaterial); + } +} + +void QSGD3D12SpriteNode::setSpriteSize(const QSize &size) +{ + if (m_spriteSize != size) { + m_spriteSize = size; + m_material->animW = static_cast<float>(size.width()) / m_sheetSize.width(); + m_material->animH = static_cast<float>(size.height()) / m_sheetSize.height(); + markDirty(DirtyMaterial); + } +} + +void QSGD3D12SpriteNode::setSheetSize(const QSize &size) +{ + if (m_sheetSize != size) { + m_sheetSize = size; + + // Update all dependent properties + m_material->animX1 = static_cast<float>(m_sourceA.x()) / m_sheetSize.width(); + m_material->animY1 = static_cast<float>(m_sourceA.y()) / m_sheetSize.height(); + m_material->animX2 = static_cast<float>(m_sourceB.x()) / m_sheetSize.width(); + m_material->animY2 = static_cast<float>(m_sourceB.y()) / m_sheetSize.height(); + m_material->animW = static_cast<float>(m_spriteSize.width()) / m_sheetSize.width(); + m_material->animH = static_cast<float>(m_spriteSize.height()) / m_sheetSize.height(); + + markDirty(DirtyMaterial); + } +} + +void QSGD3D12SpriteNode::setSize(const QSizeF &size) +{ + if (m_size != size) { + m_size = size; + m_geometryDirty = true; + } +} + +void QSGD3D12SpriteNode::setFiltering(QSGTexture::Filtering filtering) +{ + m_material->texture->setFiltering(filtering); + markDirty(DirtyMaterial); +} + +void QSGD3D12SpriteNode::update() +{ + if (m_geometryDirty) { + m_geometryDirty = false; + updateGeometry(); + } +} + +void QSGD3D12SpriteNode::updateGeometry() +{ + if (!m_material->texture) + return; + + SpriteVertices *p = static_cast<SpriteVertices *>(m_geometry->vertexData()); + const QRectF texRect = m_material->texture->normalizedTextureSubRect(); + + p->v1.tx = texRect.topLeft().x(); + p->v1.ty = texRect.topLeft().y(); + + p->v2.tx = texRect.topRight().x(); + p->v2.ty = texRect.topRight().y(); + + p->v3.tx = texRect.bottomLeft().x(); + p->v3.ty = texRect.bottomLeft().y(); + + p->v4.tx = texRect.bottomRight().x(); + p->v4.ty = texRect.bottomRight().y(); + + p->v1.x = 0; + p->v1.y = 0; + + p->v2.x = m_size.width(); + p->v2.y = 0; + + p->v3.x = 0; + p->v3.y = m_size.height(); + + p->v4.x = m_size.width(); + p->v4.y = m_size.height(); + + markDirty(DirtyGeometry); +} + +QT_END_NAMESPACE diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12spritenode_p.h b/src/plugins/scenegraph/d3d12/qsgd3d12spritenode_p.h new file mode 100644 index 0000000000..265bec7c78 --- /dev/null +++ b/src/plugins/scenegraph/d3d12/qsgd3d12spritenode_p.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSGD3D12SPRITENODE_H +#define QSGD3D12SPRITENODE_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <private/qsgadaptationlayer_p.h> + +QT_BEGIN_NAMESPACE + +class QSGD3D12SpriteMaterial; + +class QSGD3D12SpriteNode : public QSGSpriteNode +{ +public: + QSGD3D12SpriteNode(); + + void setTexture(QSGTexture *texture) override; + void setTime(float time) override; + void setSourceA(const QPoint &source) override; + void setSourceB(const QPoint &source) override; + void setSpriteSize(const QSize &size) override; + void setSheetSize(const QSize &size) override; + void setSize(const QSizeF &size) override; + void setFiltering(QSGTexture::Filtering filtering) override; + void update() override; + +private: + void updateGeometry(); + + QSGD3D12SpriteMaterial *m_material; + QSGGeometry *m_geometry; + bool m_geometryDirty; + QPoint m_sourceA; + QPoint m_sourceB; + QSize m_spriteSize; + QSize m_sheetSize; + QSizeF m_size; +}; + +QT_END_NAMESPACE + +#endif // QSGD3D12SPRITENODE_H diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12texture.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12texture.cpp index c4ba80bd48..a5f3eb7a31 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12texture.cpp +++ b/src/plugins/scenegraph/d3d12/qsgd3d12texture.cpp @@ -52,7 +52,13 @@ void QSGD3D12Texture::create(const QImage &image, uint flags) const bool alphaRequest = flags & QSGRenderContext::CreateTexture_Alpha; m_alphaWanted = alphaRequest && image.hasAlphaChannel(); - m_image = image; + // The engine maps 8-bit formats to R8. This is fine for glyphs and such + // but may not be what apps expect for ordinary image data. The OpenGL + // implementation maps these to ARGB32_Pre so let's follow suit. + if (image.depth() != 8) + m_image = image; + else + m_image = image.convertToFormat(m_alphaWanted ? QImage::Format_ARGB32_Premultiplied : QImage::Format_RGB32); m_id = m_engine->genTexture(); Q_ASSERT(m_id); diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12threadedrenderloop.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12threadedrenderloop.cpp new file mode 100644 index 0000000000..a803f67380 --- /dev/null +++ b/src/plugins/scenegraph/d3d12/qsgd3d12threadedrenderloop.cpp @@ -0,0 +1,1183 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgd3d12threadedrenderloop_p.h" +#include "qsgd3d12engine_p.h" +#include "qsgd3d12context_p.h" +#include "qsgd3d12rendercontext_p.h" +#include "qsgd3d12shadereffectnode_p.h" +#include <private/qsgrenderer_p.h> +#include <private/qquickwindow_p.h> +#include <private/qquickanimatorcontroller_p.h> +#include <private/qquickprofiler_p.h> +#include <private/qqmldebugserviceinterfaces_p.h> +#include <private/qqmldebugconnector_p.h> +#include <QElapsedTimer> +#include <QQueue> +#include <QGuiApplication> + +QT_BEGIN_NAMESPACE + +// NOTE: Avoid categorized logging. It is slow. + +#define DECLARE_DEBUG_VAR(variable) \ + static bool debug_ ## variable() \ + { static bool value = qgetenv("QSG_RENDERER_DEBUG").contains(QT_STRINGIFY(variable)); return value; } + +DECLARE_DEBUG_VAR(loop) +DECLARE_DEBUG_VAR(time) + + +// NOTE: The threaded renderloop is not currently safe to use in practice as it +// is prone to deadlocks, in particular when multiple windows are active. This +// is because DXGI's limitation of relying on the gui message pump in certain +// cases. See +// https://msdn.microsoft.com/en-us/library/windows/desktop/ee417025(v=vs.85).aspx#multithreading_and_dxgi +// +// This means that if swap chain functions like create, release, and +// potentially even Present, are called outside the gui thread, then the +// application must ensure the gui thread does not ever block and wait for the +// render thread - since on the render thread a DXGI call may be in turn +// waiting for the gui thread to deliver a window message... +// +// Ensuring this is impossible with the current design where the gui thread +// must block at certain points, waiting for the render thread. Qt moves out +// rendering from the main thread, in order to make application's life easier, +// whereas the typical DXGI-compatible model would require moving work, but not +// windowing and presenting, out to additional threads. + + +/* + The D3D render loop mostly mirrors the threaded OpenGL render loop. + + There are two classes here. QSGD3D12ThreadedRenderLoop and + QSGD3D12RenderThread. All communication between the two is based on event + passing and we have a number of custom events. + + Render loop is per process, render thread is per window. The + QSGD3D12RenderContext and QSGD3D12Engine are per window as well. The former + is created (but not owned) by QQuickWindow. The D3D device is per process. + + In this implementation, the render thread is never blocked and the GUI + thread will initiate a polishAndSync which will block and wait for the + render thread to pick it up and release the block only after the render + thread is done syncing. The reason for this is: + + 1. Clear blocking paradigm. We only have one real "block" point + (polishAndSync()) and all blocking is initiated by GUI and picked up by + Render at specific times based on events. This makes the execution + deterministic. + + 2. Render does not have to interact with GUI. This is done so that the + render thread can run its own animation system which stays alive even when + the GUI thread is blocked doing I/O, object instantiation, QPainter-painting + or any other non-trivial task. + + The render thread has affinity to the GUI thread until a window is shown. + From that moment and until the window is destroyed, it will have affinity to + the render thread. (moved back at the end of run for cleanup). + */ + +// Passed from the RL to the RT when a window is removed obscured and should be +// removed from the render loop. +const QEvent::Type WM_Obscure = QEvent::Type(QEvent::User + 1); + +// Passed from the RL to RT when GUI has been locked, waiting for sync. +const QEvent::Type WM_RequestSync = QEvent::Type(QEvent::User + 2); + +// Passed by the RT to itself to trigger another render pass. This is typically +// a result of QQuickWindow::update(). +const QEvent::Type WM_RequestRepaint = QEvent::Type(QEvent::User + 3); + +// Passed by the RL to the RT to maybe release resource if no windows are +// rendering. +const QEvent::Type WM_TryRelease = QEvent::Type(QEvent::User + 4); + +// Passed by the RL to the RT when a QQuickWindow::grabWindow() is called. +const QEvent::Type WM_Grab = QEvent::Type(QEvent::User + 5); + +// Passed by the window when there is a render job to run. +const QEvent::Type WM_PostJob = QEvent::Type(QEvent::User + 6); + +class QSGD3D12WindowEvent : public QEvent +{ +public: + QSGD3D12WindowEvent(QQuickWindow *c, QEvent::Type type) : QEvent(type), window(c) { } + QQuickWindow *window; +}; + +class QSGD3D12TryReleaseEvent : public QSGD3D12WindowEvent +{ +public: + QSGD3D12TryReleaseEvent(QQuickWindow *win, bool destroy) + : QSGD3D12WindowEvent(win, WM_TryRelease), destroying(destroy) { } + bool destroying; +}; + +class QSGD3D12SyncEvent : public QSGD3D12WindowEvent +{ +public: + QSGD3D12SyncEvent(QQuickWindow *c, bool inExpose, bool force) + : QSGD3D12WindowEvent(c, WM_RequestSync) + , size(c->size()) + , dpr(c->effectiveDevicePixelRatio()) + , syncInExpose(inExpose) + , forceRenderPass(force) { } + QSize size; + float dpr; + bool syncInExpose; + bool forceRenderPass; +}; + +class QSGD3D12GrabEvent : public QSGD3D12WindowEvent +{ +public: + QSGD3D12GrabEvent(QQuickWindow *c, QImage *result) + : QSGD3D12WindowEvent(c, WM_Grab), image(result) { } + QImage *image; +}; + +class QSGD3D12JobEvent : public QSGD3D12WindowEvent +{ +public: + QSGD3D12JobEvent(QQuickWindow *c, QRunnable *postedJob) + : QSGD3D12WindowEvent(c, WM_PostJob), job(postedJob) { } + ~QSGD3D12JobEvent() { delete job; } + QRunnable *job; +}; + +class QSGD3D12EventQueue : public QQueue<QEvent *> +{ +public: + void addEvent(QEvent *e) { + mutex.lock(); + enqueue(e); + if (waiting) + condition.wakeOne(); + mutex.unlock(); + } + + QEvent *takeEvent(bool wait) { + mutex.lock(); + if (isEmpty() && wait) { + waiting = true; + condition.wait(&mutex); + waiting = false; + } + QEvent *e = dequeue(); + mutex.unlock(); + return e; + } + + bool hasMoreEvents() { + mutex.lock(); + bool has = !isEmpty(); + mutex.unlock(); + return has; + } + +private: + QMutex mutex; + QWaitCondition condition; + bool waiting = false; +}; + +static inline int qsgrl_animation_interval() +{ + const qreal refreshRate = QGuiApplication::primaryScreen() ? QGuiApplication::primaryScreen()->refreshRate() : 0; + return refreshRate < 1 ? 16 : int(1000 / refreshRate); +} + +class QSGD3D12RenderThread : public QThread +{ + Q_OBJECT + +public: + QSGD3D12RenderThread(QSGD3D12ThreadedRenderLoop *rl, QSGRenderContext *renderContext) + : renderLoop(rl) + { + rc = static_cast<QSGD3D12RenderContext *>(renderContext); + vsyncDelta = qsgrl_animation_interval(); + } + + ~QSGD3D12RenderThread() + { + delete rc; + } + + bool event(QEvent *e); + void run(); + + void syncAndRender(); + void sync(bool inExpose); + + void requestRepaint() + { + if (sleeping) + stopEventProcessing = true; + if (exposedWindow) + pendingUpdate |= RepaintRequest; + } + + void processEventsAndWaitForMore(); + void processEvents(); + void postEvent(QEvent *e); + + enum UpdateRequest { + SyncRequest = 0x01, + RepaintRequest = 0x02, + ExposeRequest = 0x04 | RepaintRequest | SyncRequest + }; + + QSGD3D12Engine *engine = nullptr; + QSGD3D12ThreadedRenderLoop *renderLoop; + QSGD3D12RenderContext *rc; + QAnimationDriver *rtAnim = nullptr; + volatile bool active = false; + uint pendingUpdate = 0; + bool sleeping = false; + bool syncResultedInChanges = false; + float vsyncDelta; + QMutex mutex; + QWaitCondition waitCondition; + QQuickWindow *exposedWindow = nullptr; + bool stopEventProcessing = false; + QSGD3D12EventQueue eventQueue; + QElapsedTimer threadTimer; + qint64 syncTime; + qint64 renderTime; + qint64 sinceLastTime; + +public slots: + void onSceneGraphChanged() { + syncResultedInChanges = true; + } +}; + +bool QSGD3D12RenderThread::event(QEvent *e) +{ + switch (e->type()) { + + case WM_Obscure: + Q_ASSERT(!exposedWindow || exposedWindow == static_cast<QSGD3D12WindowEvent *>(e)->window); + if (Q_UNLIKELY(debug_loop())) + qDebug() << "RT - WM_Obscure" << exposedWindow; + mutex.lock(); + if (exposedWindow) { + QQuickWindowPrivate::get(exposedWindow)->fireAboutToStop(); + if (Q_UNLIKELY(debug_loop())) + qDebug("RT - WM_Obscure - window removed"); + exposedWindow = nullptr; + } + waitCondition.wakeOne(); + mutex.unlock(); + return true; + + case WM_RequestSync: { + QSGD3D12SyncEvent *wme = static_cast<QSGD3D12SyncEvent *>(e); + if (sleeping) + stopEventProcessing = true; + // One thread+engine for each window. However, the native window may + // change in some (quite artificial) cases, e.g. due to a hide - + // destroy - show on the QWindow. + bool needsWindow = !engine->window(); + if (engine->window() && engine->window() != wme->window->winId()) { + if (Q_UNLIKELY(debug_loop())) + qDebug("RT - WM_RequestSync - native window handle changes for active engine"); + engine->waitGPU(); + QQuickWindowPrivate::get(wme->window)->cleanupNodesOnShutdown(); + QSGD3D12ShaderEffectNode::cleanupMaterialTypeCache(); + rc->invalidate(); + engine->releaseResources(); + needsWindow = true; + } + if (needsWindow) { + // Must only ever get here when there is no window or releaseResources() has been called. + const int samples = wme->window->format().samples(); + const bool alpha = wme->window->format().alphaBufferSize() > 0; + if (Q_UNLIKELY(debug_loop())) + qDebug() << "RT - WM_RequestSync - initializing D3D12 engine" << wme->window + << wme->size << wme->dpr << samples << alpha; + engine->attachToWindow(wme->window->winId(), wme->size, wme->dpr, samples, alpha); + } + exposedWindow = wme->window; + engine->setWindowSize(wme->size, wme->dpr); + if (Q_UNLIKELY(debug_loop())) + qDebug() << "RT - WM_RequestSync" << exposedWindow; + pendingUpdate |= SyncRequest; + if (wme->syncInExpose) { + if (Q_UNLIKELY(debug_loop())) + qDebug("RT - WM_RequestSync - triggered from expose"); + pendingUpdate |= ExposeRequest; + } + if (wme->forceRenderPass) { + if (Q_UNLIKELY(debug_loop())) + qDebug("RT - WM_RequestSync - repaint regardless"); + pendingUpdate |= RepaintRequest; + } + return true; + } + + case WM_TryRelease: { + if (Q_UNLIKELY(debug_loop())) + qDebug("RT - WM_TryRelease"); + mutex.lock(); + renderLoop->lockedForSync = true; + QSGD3D12TryReleaseEvent *wme = static_cast<QSGD3D12TryReleaseEvent *>(e); + // Only when no windows are exposed anymore or we are shutting down. + if (!exposedWindow || wme->destroying) { + if (Q_UNLIKELY(debug_loop())) + qDebug("RT - WM_TryRelease - invalidating rc"); + if (wme->window) { + QQuickWindowPrivate *wd = QQuickWindowPrivate::get(wme->window); + if (wme->destroying) { + // QSGNode destruction may release graphics resources in use so wait first. + engine->waitGPU(); + // Bye bye nodes... + wd->cleanupNodesOnShutdown(); + QSGD3D12ShaderEffectNode::cleanupMaterialTypeCache(); + } + rc->invalidate(); + QCoreApplication::processEvents(); + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + if (wme->destroying) + delete wd->animationController; + } + if (wme->destroying) + active = false; + if (sleeping) + stopEventProcessing = true; + } else { + if (Q_UNLIKELY(debug_loop())) + qDebug("RT - WM_TryRelease - not releasing because window is still active"); + } + waitCondition.wakeOne(); + renderLoop->lockedForSync = false; + mutex.unlock(); + return true; + } + + case WM_Grab: { + if (Q_UNLIKELY(debug_loop())) + qDebug("RT - WM_Grab"); + QSGD3D12GrabEvent *wme = static_cast<QSGD3D12GrabEvent *>(e); + Q_ASSERT(wme->window); + Q_ASSERT(wme->window == exposedWindow || !exposedWindow); + mutex.lock(); + if (wme->window) { + // Grabbing is generally done by rendering a frame and reading the + // color buffer contents back, without presenting, and then + // creating a QImage from the returned data. It is terribly + // inefficient since it involves a full blocking wait for the GPU. + // However, our hands are tied by the existing, synchronous APIs of + // QQuickWindow and such. + QQuickWindowPrivate *wd = QQuickWindowPrivate::get(wme->window); + rc->initialize(nullptr); + wd->syncSceneGraph(); + wd->renderSceneGraph(wme->window->size()); + *wme->image = engine->executeAndWaitReadbackRenderTarget(); + } + if (Q_UNLIKELY(debug_loop())) + qDebug("RT - WM_Grab - waking gui to handle result"); + waitCondition.wakeOne(); + mutex.unlock(); + return true; + } + + case WM_PostJob: { + if (Q_UNLIKELY(debug_loop())) + qDebug("RT - WM_PostJob"); + QSGD3D12JobEvent *wme = static_cast<QSGD3D12JobEvent *>(e); + Q_ASSERT(wme->window == exposedWindow); + if (exposedWindow) { + wme->job->run(); + delete wme->job; + wme->job = nullptr; + if (Q_UNLIKELY(debug_loop())) + qDebug("RT - WM_PostJob - job done"); + } + return true; + } + + case WM_RequestRepaint: + if (Q_UNLIKELY(debug_loop())) + qDebug("RT - WM_RequestPaint"); + // When GUI posts this event, it is followed by a polishAndSync, so we + // must not exit the event loop yet. + pendingUpdate |= RepaintRequest; + break; + + default: + break; + } + + return QThread::event(e); +} + +void QSGD3D12RenderThread::postEvent(QEvent *e) +{ + eventQueue.addEvent(e); +} + +void QSGD3D12RenderThread::processEvents() +{ + while (eventQueue.hasMoreEvents()) { + QEvent *e = eventQueue.takeEvent(false); + event(e); + delete e; + } +} + +void QSGD3D12RenderThread::processEventsAndWaitForMore() +{ + stopEventProcessing = false; + while (!stopEventProcessing) { + QEvent *e = eventQueue.takeEvent(true); + event(e); + delete e; + } +} + +void QSGD3D12RenderThread::run() +{ + if (Q_UNLIKELY(debug_loop())) + qDebug("RT - run()"); + + engine = new QSGD3D12Engine; + rc->setEngine(engine); + + rtAnim = rc->sceneGraphContext()->createAnimationDriver(nullptr); + rtAnim->install(); + + if (QQmlDebugConnector::service<QQmlProfilerService>()) + QQuickProfiler::registerAnimationCallback(); + + while (active) { + if (exposedWindow) + syncAndRender(); + + processEvents(); + QCoreApplication::processEvents(); + + if (pendingUpdate == 0 || !exposedWindow) { + if (Q_UNLIKELY(debug_loop())) + qDebug("RT - done drawing, sleep"); + sleeping = true; + processEventsAndWaitForMore(); + sleeping = false; + } + } + + if (Q_UNLIKELY(debug_loop())) + qDebug("RT - run() exiting"); + + delete rtAnim; + rtAnim = nullptr; + + rc->moveToThread(renderLoop->thread()); + moveToThread(renderLoop->thread()); + + rc->setEngine(nullptr); + delete engine; + engine = nullptr; +} + +void QSGD3D12RenderThread::sync(bool inExpose) +{ + if (Q_UNLIKELY(debug_loop())) + qDebug("RT - sync"); + + mutex.lock(); + Q_ASSERT_X(renderLoop->lockedForSync, "QSGD3D12RenderThread::sync()", "sync triggered with gui not locked"); + + // Recover from device loss. + if (!engine->hasResources()) { + if (Q_UNLIKELY(debug_loop())) + qDebug("RT - sync - device was lost, resetting scenegraph"); + QQuickWindowPrivate::get(exposedWindow)->cleanupNodesOnShutdown(); + QSGD3D12ShaderEffectNode::cleanupMaterialTypeCache(); + rc->invalidate(); + } + + if (engine->window()) { + QQuickWindowPrivate *wd = QQuickWindowPrivate::get(exposedWindow); + bool hadRenderer = wd->renderer != nullptr; + // If the scene graph was touched since the last sync() make sure it sends the + // changed signal. + if (wd->renderer) + wd->renderer->clearChangedFlag(); + + rc->initialize(nullptr); + wd->syncSceneGraph(); + + if (!hadRenderer && wd->renderer) { + if (Q_UNLIKELY(debug_loop())) + qDebug("RT - created renderer"); + syncResultedInChanges = true; + connect(wd->renderer, &QSGRenderer::sceneGraphChanged, this, + &QSGD3D12RenderThread::onSceneGraphChanged, Qt::DirectConnection); + } + + // Process deferred deletes now, directly after the sync as deleteLater + // on the GUI must now also have resulted in SG changes and the delete + // is a safe operation. + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + } + + if (!inExpose) { + if (Q_UNLIKELY(debug_loop())) + qDebug("RT - sync complete, waking gui"); + waitCondition.wakeOne(); + mutex.unlock(); + } +} + +void QSGD3D12RenderThread::syncAndRender() +{ + if (Q_UNLIKELY(debug_time())) { + sinceLastTime = threadTimer.nsecsElapsed(); + threadTimer.start(); + } + Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphRenderLoopFrame); + + QElapsedTimer waitTimer; + waitTimer.start(); + + if (Q_UNLIKELY(debug_loop())) + qDebug("RT - syncAndRender()"); + + syncResultedInChanges = false; + QQuickWindowPrivate *wd = QQuickWindowPrivate::get(exposedWindow); + + const bool repaintRequested = (pendingUpdate & RepaintRequest) || wd->customRenderStage; + const bool syncRequested = pendingUpdate & SyncRequest; + const bool exposeRequested = (pendingUpdate & ExposeRequest) == ExposeRequest; + pendingUpdate = 0; + + if (syncRequested) + sync(exposeRequested); + +#ifndef QSG_NO_RENDER_TIMING + if (Q_UNLIKELY(debug_time())) + syncTime = threadTimer.nsecsElapsed(); +#endif + Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRenderLoopFrame); + + if (!syncResultedInChanges && !repaintRequested) { + if (Q_UNLIKELY(debug_loop())) + qDebug("RT - no changes, render aborted"); + int waitTime = vsyncDelta - (int) waitTimer.elapsed(); + if (waitTime > 0) + msleep(waitTime); + return; + } + + if (Q_UNLIKELY(debug_loop())) + qDebug("RT - rendering started"); + + if (rtAnim->isRunning()) { + wd->animationController->lock(); + rtAnim->advance(); + wd->animationController->unlock(); + } + + bool canRender = wd->renderer != nullptr; + // Recover from device loss. + if (!engine->hasResources()) { + if (Q_UNLIKELY(debug_loop())) + qDebug("RT - syncAndRender - device was lost, posting FullUpdateRequest"); + // Cannot do anything here because gui is not locked. Request a new + // sync+render round on the gui thread and let the sync handle it. + QCoreApplication::postEvent(exposedWindow, new QEvent(QEvent::Type(QQuickWindowPrivate::FullUpdateRequest))); + canRender = false; + } + + if (canRender) { + wd->renderSceneGraph(engine->windowSize()); + if (Q_UNLIKELY(debug_time())) + renderTime = threadTimer.nsecsElapsed(); + Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRenderLoopFrame); + + // The engine is able to have multiple frames in flight. This in effect is + // similar to BufferQueueingOpenGL. Provide an env var to force the + // traditional blocking swap behavior, just in case. + static bool blockOnEachFrame = qEnvironmentVariableIntValue("QT_D3D_BLOCKING_PRESENT") != 0; + + if (!wd->customRenderStage || !wd->customRenderStage->swap()) + engine->present(); + + if (blockOnEachFrame) + engine->waitGPU(); + + // The concept of "frame swaps" is quite misleading by default, when + // blockOnEachFrame is not used, but emit it for compatibility. + wd->fireFrameSwapped(); + } else { + Q_QUICK_SG_PROFILE_SKIP(QQuickProfiler::SceneGraphRenderLoopFrame, 1); + if (Q_UNLIKELY(debug_loop())) + qDebug("RT - window not ready, skipping render"); + } + + if (Q_UNLIKELY(debug_loop())) + qDebug("RT - rendering done"); + + if (exposeRequested) { + if (Q_UNLIKELY(debug_loop())) + qDebug("RT - wake gui after initial expose"); + waitCondition.wakeOne(); + mutex.unlock(); + } + + if (Q_UNLIKELY(debug_time())) + qDebug("Frame rendered with 'd3d12' renderloop in %dms, sync=%d, render=%d, swap=%d - (on render thread)", + int(threadTimer.elapsed()), + int((syncTime/1000000)), + int((renderTime - syncTime) / 1000000), + int(threadTimer.elapsed() - renderTime / 1000000)); + + Q_QUICK_SG_PROFILE_END(QQuickProfiler::SceneGraphRenderLoopFrame); + + static int devLossTest = qEnvironmentVariableIntValue("QT_D3D_TEST_DEVICE_LOSS"); + if (devLossTest > 0) { + static QElapsedTimer kt; + static bool timerRunning = false; + if (!timerRunning) { + kt.start(); + timerRunning = true; + } else if (kt.elapsed() > 5000) { + --devLossTest; + kt.restart(); + engine->simulateDeviceLoss(); + } + } +} + +template<class T> T *windowFor(const QVector<T> &list, QQuickWindow *window) +{ + for (const T &t : list) { + if (t.window == window) + return const_cast<T *>(&t); + } + return nullptr; +} + +QSGD3D12ThreadedRenderLoop::QSGD3D12ThreadedRenderLoop() +{ + if (Q_UNLIKELY(debug_loop())) + qDebug("d3d12 THREADED render loop ctor"); + + sg = new QSGD3D12Context; + + anim = sg->createAnimationDriver(this); + connect(anim, &QAnimationDriver::started, this, &QSGD3D12ThreadedRenderLoop::onAnimationStarted); + connect(anim, &QAnimationDriver::stopped, this, &QSGD3D12ThreadedRenderLoop::onAnimationStopped); + anim->install(); +} + +QSGD3D12ThreadedRenderLoop::~QSGD3D12ThreadedRenderLoop() +{ + if (Q_UNLIKELY(debug_loop())) + qDebug("d3d12 THREADED render loop dtor"); + + delete sg; +} + +void QSGD3D12ThreadedRenderLoop::show(QQuickWindow *window) +{ + if (Q_UNLIKELY(debug_loop())) + qDebug() << "show" << window; +} + +void QSGD3D12ThreadedRenderLoop::hide(QQuickWindow *window) +{ + if (Q_UNLIKELY(debug_loop())) + qDebug() << "hide" << window; + + if (window->isExposed()) + handleObscurity(windowFor(windows, window)); + + releaseResources(window); +} + +void QSGD3D12ThreadedRenderLoop::resize(QQuickWindow *window) +{ + if (!window->isExposed() || window->size().isEmpty()) + return; + + if (Q_UNLIKELY(debug_loop())) + qDebug() << "resize" << window << window->size(); +} + +void QSGD3D12ThreadedRenderLoop::windowDestroyed(QQuickWindow *window) +{ + if (Q_UNLIKELY(debug_loop())) + qDebug() << "window destroyed" << window; + + WindowData *w = windowFor(windows, window); + if (!w) + return; + + handleObscurity(w); + handleResourceRelease(w, true); + + QSGD3D12RenderThread *thread = w->thread; + while (thread->isRunning()) + QThread::yieldCurrentThread(); + + Q_ASSERT(thread->thread() == QThread::currentThread()); + delete thread; + + for (int i = 0; i < windows.size(); ++i) { + if (windows.at(i).window == window) { + windows.removeAt(i); + break; + } + } +} + +void QSGD3D12ThreadedRenderLoop::exposureChanged(QQuickWindow *window) +{ + if (Q_UNLIKELY(debug_loop())) + qDebug() << "exposure changed" << window; + + if (window->isExposed()) { + handleExposure(window); + } else { + WindowData *w = windowFor(windows, window); + if (w) + handleObscurity(w); + } +} + +QImage QSGD3D12ThreadedRenderLoop::grab(QQuickWindow *window) +{ + if (Q_UNLIKELY(debug_loop())) + qDebug() << "grab" << window; + + WindowData *w = windowFor(windows, window); + // Have to support invisible (but created()'ed) windows as well. + // Unlike with GL, leaving that case for QQuickWindow to handle is not feasible. + const bool tempExpose = !w; + if (tempExpose) { + handleExposure(window); + w = windowFor(windows, window); + Q_ASSERT(w); + } + + if (!w->thread->isRunning()) + return QImage(); + + if (!window->handle()) + window->create(); + + QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window); + wd->polishItems(); + + QImage result; + w->thread->mutex.lock(); + lockedForSync = true; + w->thread->postEvent(new QSGD3D12GrabEvent(window, &result)); + w->thread->waitCondition.wait(&w->thread->mutex); + lockedForSync = false; + w->thread->mutex.unlock(); + + result.setDevicePixelRatio(window->effectiveDevicePixelRatio()); + + if (tempExpose) + handleObscurity(w); + + return result; +} + +void QSGD3D12ThreadedRenderLoop::update(QQuickWindow *window) +{ + WindowData *w = windowFor(windows, window); + if (!w) + return; + + if (w->thread == QThread::currentThread()) { + w->thread->requestRepaint(); + return; + } + + // We set forceRenderPass because we want to make sure the QQuickWindow + // actually does a full render pass after the next sync. + w->forceRenderPass = true; + scheduleUpdate(w); +} + +void QSGD3D12ThreadedRenderLoop::maybeUpdate(QQuickWindow *window) +{ + WindowData *w = windowFor(windows, window); + if (w) + scheduleUpdate(w); +} + +// called in response to window->requestUpdate() +void QSGD3D12ThreadedRenderLoop::handleUpdateRequest(QQuickWindow *window) +{ + if (Q_UNLIKELY(debug_loop())) + qDebug() << "handleUpdateRequest" << window; + + WindowData *w = windowFor(windows, window); + if (w) + polishAndSync(w, false); +} + +QAnimationDriver *QSGD3D12ThreadedRenderLoop::animationDriver() const +{ + return anim; +} + +QSGContext *QSGD3D12ThreadedRenderLoop::sceneGraphContext() const +{ + return sg; +} + +QSGRenderContext *QSGD3D12ThreadedRenderLoop::createRenderContext(QSGContext *) const +{ + return sg->createRenderContext(); +} + +void QSGD3D12ThreadedRenderLoop::releaseResources(QQuickWindow *window) +{ + if (Q_UNLIKELY(debug_loop())) + qDebug() << "releaseResources" << window; + + WindowData *w = windowFor(windows, window); + if (w) + handleResourceRelease(w, false); +} + +void QSGD3D12ThreadedRenderLoop::postJob(QQuickWindow *window, QRunnable *job) +{ + WindowData *w = windowFor(windows, window); + if (w && w->thread && w->thread->exposedWindow) + w->thread->postEvent(new QSGD3D12JobEvent(window, job)); + else + delete job; +} + +QSurface::SurfaceType QSGD3D12ThreadedRenderLoop::windowSurfaceType() const +{ + return QSurface::OpenGLSurface; +} + +bool QSGD3D12ThreadedRenderLoop::interleaveIncubation() const +{ + bool somethingVisible = false; + for (const WindowData &w : windows) { + if (w.window->isVisible() && w.window->isExposed()) { + somethingVisible = true; + break; + } + } + return somethingVisible && anim->isRunning(); +} + +int QSGD3D12ThreadedRenderLoop::flags() const +{ + return SupportsGrabWithoutExpose; +} + +bool QSGD3D12ThreadedRenderLoop::event(QEvent *e) +{ + if (e->type() == QEvent::Timer) { + QTimerEvent *te = static_cast<QTimerEvent *>(e); + if (te->timerId() == animationTimer) { + anim->advance(); + emit timeToIncubate(); + return true; + } + } + + return QObject::event(e); +} + +void QSGD3D12ThreadedRenderLoop::onAnimationStarted() +{ + startOrStopAnimationTimer(); + + for (const WindowData &w : qAsConst(windows)) + w.window->requestUpdate(); +} + +void QSGD3D12ThreadedRenderLoop::onAnimationStopped() +{ + startOrStopAnimationTimer(); +} + +void QSGD3D12ThreadedRenderLoop::startOrStopAnimationTimer() +{ + int exposedWindowCount = 0; + const WindowData *exposed = nullptr; + + for (int i = 0; i < windows.size(); ++i) { + const WindowData &w(windows[i]); + if (w.window->isVisible() && w.window->isExposed()) { + ++exposedWindowCount; + exposed = &w; + } + } + + if (animationTimer && (exposedWindowCount == 1 || !anim->isRunning())) { + killTimer(animationTimer); + animationTimer = 0; + // If animations are running, make sure we keep on animating + if (anim->isRunning()) + exposed->window->requestUpdate(); + } else if (!animationTimer && exposedWindowCount != 1 && anim->isRunning()) { + animationTimer = startTimer(qsgrl_animation_interval()); + } +} + +void QSGD3D12ThreadedRenderLoop::handleExposure(QQuickWindow *window) +{ + if (Q_UNLIKELY(debug_loop())) + qDebug() << "handleExposure" << window; + + WindowData *w = windowFor(windows, window); + if (!w) { + if (Q_UNLIKELY(debug_loop())) + qDebug("adding window to list"); + WindowData win; + win.window = window; + QSGRenderContext *rc = QQuickWindowPrivate::get(window)->context; // will transfer ownership + win.thread = new QSGD3D12RenderThread(this, rc); + win.updateDuringSync = false; + win.forceRenderPass = true; // also covered by polishAndSync(inExpose=true), but doesn't hurt + windows.append(win); + w = &windows.last(); + } + + // set this early as we'll be rendering shortly anyway and this avoids + // special casing exposure in polishAndSync. + w->thread->exposedWindow = window; + + if (w->window->size().isEmpty() + || (w->window->isTopLevel() && !w->window->geometry().intersects(w->window->screen()->availableGeometry()))) { +#ifndef QT_NO_DEBUG + qWarning().noquote().nospace() << "QSGD3D12ThreadedRenderLoop: expose event received for window " + << w->window << " with invalid geometry: " << w->window->geometry() + << " on " << w->window->screen(); +#endif + } + + if (!w->window->handle()) + w->window->create(); + + // Start render thread if it is not running + if (!w->thread->isRunning()) { + if (Q_UNLIKELY(debug_loop())) + qDebug("starting render thread"); + // Push a few things to the render thread. + QQuickAnimatorController *controller = QQuickWindowPrivate::get(w->window)->animationController; + if (controller->thread() != w->thread) + controller->moveToThread(w->thread); + if (w->thread->thread() == QThread::currentThread()) { + w->thread->rc->moveToThread(w->thread); + w->thread->moveToThread(w->thread); + } + + w->thread->active = true; + w->thread->start(); + + if (!w->thread->isRunning()) + qFatal("Render thread failed to start, aborting application."); + } + + polishAndSync(w, true); + + startOrStopAnimationTimer(); +} + +void QSGD3D12ThreadedRenderLoop::handleObscurity(WindowData *w) +{ + if (Q_UNLIKELY(debug_loop())) + qDebug() << "handleObscurity" << w->window; + + if (w->thread->isRunning()) { + w->thread->mutex.lock(); + w->thread->postEvent(new QSGD3D12WindowEvent(w->window, WM_Obscure)); + w->thread->waitCondition.wait(&w->thread->mutex); + w->thread->mutex.unlock(); + } + + startOrStopAnimationTimer(); +} + +void QSGD3D12ThreadedRenderLoop::scheduleUpdate(WindowData *w) +{ + if (!QCoreApplication::instance()) + return; + + if (!w || !w->thread->isRunning()) + return; + + QThread *current = QThread::currentThread(); + if (current != QCoreApplication::instance()->thread() && (current != w->thread || !lockedForSync)) { + qWarning() << "Updates can only be scheduled from GUI thread or from QQuickItem::updatePaintNode()"; + return; + } + + if (current == w->thread) { + w->updateDuringSync = true; + return; + } + + w->window->requestUpdate(); +} + +void QSGD3D12ThreadedRenderLoop::handleResourceRelease(WindowData *w, bool destroying) +{ + if (Q_UNLIKELY(debug_loop())) + qDebug() << "handleResourceRelease" << (destroying ? "destroying" : "hide/releaseResources") << w->window; + + w->thread->mutex.lock(); + if (w->thread->isRunning() && w->thread->active) { + QQuickWindow *window = w->window; + + // Note that window->handle() is typically null by this time because + // the platform window is already destroyed. This should not be a + // problem for the D3D cleanup. + + w->thread->postEvent(new QSGD3D12TryReleaseEvent(window, destroying)); + w->thread->waitCondition.wait(&w->thread->mutex); + + // Avoid a shutdown race condition. + // If SG is invalidated and 'active' becomes false, the thread's run() + // method will exit. handleExposure() relies on QThread::isRunning() (because it + // potentially needs to start the thread again) and our mutex cannot be used to + // track the thread stopping, so we wait a few nanoseconds extra so the thread + // can exit properly. + if (!w->thread->active) + w->thread->wait(); + } + w->thread->mutex.unlock(); +} + +void QSGD3D12ThreadedRenderLoop::polishAndSync(WindowData *w, bool inExpose) +{ + if (Q_UNLIKELY(debug_loop())) + qDebug() << "polishAndSync" << (inExpose ? "(in expose)" : "(normal)") << w->window; + + QQuickWindow *window = w->window; + if (!w->thread || !w->thread->exposedWindow) { + if (Q_UNLIKELY(debug_loop())) + qDebug("polishAndSync - not exposed, abort"); + return; + } + + // Flush pending touch events. + QQuickWindowPrivate::get(window)->flushFrameSynchronousEvents(); + // The delivery of the event might have caused the window to stop rendering + w = windowFor(windows, window); + if (!w || !w->thread || !w->thread->exposedWindow) { + if (Q_UNLIKELY(debug_loop())) + qDebug("polishAndSync - removed after touch event flushing, abort"); + return; + } + + QElapsedTimer timer; + qint64 polishTime = 0; + qint64 waitTime = 0; + qint64 syncTime = 0; + if (Q_UNLIKELY(debug_time())) + timer.start(); + Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphPolishAndSync); + + QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window); + wd->polishItems(); + + if (Q_UNLIKELY(debug_time())) + polishTime = timer.nsecsElapsed(); + Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphPolishAndSync); + + w->updateDuringSync = false; + + emit window->afterAnimating(); + + if (Q_UNLIKELY(debug_loop())) + qDebug("polishAndSync - lock for sync"); + w->thread->mutex.lock(); + lockedForSync = true; + w->thread->postEvent(new QSGD3D12SyncEvent(window, inExpose, w->forceRenderPass)); + w->forceRenderPass = false; + + if (Q_UNLIKELY(debug_loop())) + qDebug("polishAndSync - wait for sync"); + if (Q_UNLIKELY(debug_time())) + waitTime = timer.nsecsElapsed(); + Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphPolishAndSync); + w->thread->waitCondition.wait(&w->thread->mutex); + lockedForSync = false; + w->thread->mutex.unlock(); + if (Q_UNLIKELY(debug_loop())) + qDebug("polishAndSync - unlock after sync"); + + if (Q_UNLIKELY(debug_time())) + syncTime = timer.nsecsElapsed(); + Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphPolishAndSync); + + if (!animationTimer && anim->isRunning()) { + if (Q_UNLIKELY(debug_loop())) + qDebug("polishAndSync - advancing animations"); + anim->advance(); + // We need to trigger another sync to keep animations running... + w->window->requestUpdate(); + emit timeToIncubate(); + } else if (w->updateDuringSync) { + w->window->requestUpdate(); + } + + if (Q_UNLIKELY(debug_time())) + qDebug().nospace() + << "Frame prepared with 'd3d12' renderloop" + << ", polish=" << (polishTime / 1000000) + << ", lock=" << (waitTime - polishTime) / 1000000 + << ", blockedForSync=" << (syncTime - waitTime) / 1000000 + << ", animations=" << (timer.nsecsElapsed() - syncTime) / 1000000 + << " - (on gui thread) " << window; + + Q_QUICK_SG_PROFILE_END(QQuickProfiler::SceneGraphPolishAndSync); +} + +#include "qsgd3d12threadedrenderloop.moc" + +QT_END_NAMESPACE diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12threadedrenderloop_p.h b/src/plugins/scenegraph/d3d12/qsgd3d12threadedrenderloop_p.h new file mode 100644 index 0000000000..46f62948f1 --- /dev/null +++ b/src/plugins/scenegraph/d3d12/qsgd3d12threadedrenderloop_p.h @@ -0,0 +1,129 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSGD3D12THREADEDRENDERLOOP_P_H +#define QSGD3D12THREADEDRENDERLOOP_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <private/qsgrenderloop_p.h> + +QT_BEGIN_NAMESPACE + +class QSGD3D12Engine; +class QSGD3D12Context; +class QSGD3D12RenderContext; +class QSGD3D12RenderThread; + +class QSGD3D12ThreadedRenderLoop : public QSGRenderLoop +{ + Q_OBJECT + +public: + QSGD3D12ThreadedRenderLoop(); + ~QSGD3D12ThreadedRenderLoop(); + + void show(QQuickWindow *window) override; + void hide(QQuickWindow *window) override; + void resize(QQuickWindow *window) override; + + void windowDestroyed(QQuickWindow *window) override; + + void exposureChanged(QQuickWindow *window) override; + + QImage grab(QQuickWindow *window) override; + + void update(QQuickWindow *window) override; + void maybeUpdate(QQuickWindow *window) override; + void handleUpdateRequest(QQuickWindow *window) override; + + QAnimationDriver *animationDriver() const override; + + QSGContext *sceneGraphContext() const override; + QSGRenderContext *createRenderContext(QSGContext *) const override; + + void releaseResources(QQuickWindow *window) override; + void postJob(QQuickWindow *window, QRunnable *job) override; + + QSurface::SurfaceType windowSurfaceType() const override; + bool interleaveIncubation() const override; + int flags() const override; + + bool event(QEvent *e) override; + +public Q_SLOTS: + void onAnimationStarted(); + void onAnimationStopped(); + +private: + struct WindowData { + QQuickWindow *window; + QSGD3D12RenderThread *thread; + uint updateDuringSync : 1; + uint forceRenderPass : 1; + }; + + void startOrStopAnimationTimer(); + void handleExposure(QQuickWindow *window); + void handleObscurity(WindowData *w); + void scheduleUpdate(WindowData *w); + void handleResourceRelease(WindowData *w, bool destroying); + void polishAndSync(WindowData *w, bool inExpose); + + QSGD3D12Context *sg; + QAnimationDriver *anim; + int animationTimer = 0; + bool lockedForSync = false; + QVector<WindowData> windows; + + friend class QSGD3D12RenderThread; +}; + +QT_END_NAMESPACE + +#endif // QSGD3D12THREADEDRENDERLOOP_P_H diff --git a/src/plugins/scenegraph/d3d12/shaders/shaders.pri b/src/plugins/scenegraph/d3d12/shaders/shaders.pri index 41f0ee80f7..963f4c5d8c 100644 --- a/src/plugins/scenegraph/d3d12/shaders/shaders.pri +++ b/src/plugins/scenegraph/d3d12/shaders/shaders.pri @@ -108,6 +108,16 @@ shadereffectdefault_pshader.header = ps_shadereffectdefault.hlslh shadereffectdefault_pshader.entry = PS_DefaultShaderEffect shadereffectdefault_pshader.type = ps_5_0 +sprite_VSPS = $$PWD/sprite.hlsl +sprite_vshader.input = sprite_VSPS +sprite_vshader.header = vs_sprite.hlslh +sprite_vshader.entry = VS_Sprite +sprite_vshader.type = vs_5_0 +sprite_pshader.input = sprite_VSPS +sprite_pshader.header = ps_sprite.hlslh +sprite_pshader.entry = PS_Sprite +sprite_pshader.type = ps_5_0 + tdr_CS = $$PWD/tdr.hlsl tdr_cshader.input = tdr_CS tdr_cshader.header = cs_tdr.hlslh @@ -125,6 +135,7 @@ HLSL_SHADERS = \ textmask_vshader textmask_pshader24 textmask_pshader32 textmask_pshader8 \ styledtext_vshader styledtext_pshader outlinedtext_vshader outlinedtext_pshader \ shadereffectdefault_vshader shadereffectdefault_pshader \ + sprite_vshader sprite_pshader \ tdr_cshader load(hlsl_bytecode_header) diff --git a/src/plugins/scenegraph/d3d12/shaders/sprite.hlsl b/src/plugins/scenegraph/d3d12/shaders/sprite.hlsl new file mode 100644 index 0000000000..d4e3b066ee --- /dev/null +++ b/src/plugins/scenegraph/d3d12/shaders/sprite.hlsl @@ -0,0 +1,43 @@ +struct VSInput +{ + float4 position : POSITION; + float2 coord : TEXCOORD0; +}; + +cbuffer ConstantBuffer : register(b0) +{ + float4x4 mvp; + float4 animPos; + float3 animData; + float opacity; +}; + +struct PSInput +{ + float4 position : SV_POSITION; + float4 fTexS : TEXCOORD0; + float progress : TEXCOORD1; +}; + +Texture2D tex : register(t0); +SamplerState samp : register(s0); + +PSInput VS_Sprite(VSInput input) +{ + PSInput result; + + result.position = mul(mvp, input.position); + result.progress = animData.z; + + // Calculate frame location in texture + result.fTexS.xy = animPos.xy + input.coord.xy * animData.xy; + // Next frame is also passed, for interpolation + result.fTexS.zw = animPos.zw + input.coord.xy * animData.xy; + + return result; +} + +float4 PS_Sprite(PSInput input) : SV_TARGET +{ + return lerp(tex.Sample(samp, input.fTexS.xy), tex.Sample(samp, input.fTexS.zw), input.progress) * opacity; +} diff --git a/src/plugins/scenegraph/d3d12/shaders/textmask.hlsl b/src/plugins/scenegraph/d3d12/shaders/textmask.hlsl index f9d92e8ee9..bb9381e7c0 100644 --- a/src/plugins/scenegraph/d3d12/shaders/textmask.hlsl +++ b/src/plugins/scenegraph/d3d12/shaders/textmask.hlsl @@ -45,7 +45,7 @@ float4 PS_TextMask32(PSInput input) : SV_TARGET float4 PS_TextMask8(PSInput input) : SV_TARGET { - return colorVec * tex.Sample(samp, input.coord).r; + return colorVec * tex.Sample(samp, input.coord).a; } struct StyledPSInput @@ -66,8 +66,8 @@ StyledPSInput VS_StyledText(VSInput input) float4 PS_StyledText(StyledPSInput input) : SV_TARGET { - float glyph = tex.Sample(samp, input.coord).r; - float style = clamp(tex.Sample(samp, input.shiftedCoord).r - glyph, 0.0, 1.0); + float glyph = tex.Sample(samp, input.coord).a; + float style = clamp(tex.Sample(samp, input.shiftedCoord).a - glyph, 0.0, 1.0); return style * styleColor + glyph * colorVec; } @@ -95,10 +95,10 @@ OutlinedPSInput VS_OutlinedText(VSInput input) float4 PS_OutlinedText(OutlinedPSInput input) : SV_TARGET { - float glyph = tex.Sample(samp, input.coord).r; - float outline = clamp(clamp(tex.Sample(samp, input.coordUp).r - + tex.Sample(samp, input.coordDown).r - + tex.Sample(samp, input.coordLeft).r - + tex.Sample(samp, input.coordRight).r, 0.0, 1.0) - glyph, 0.0, 1.0); + float glyph = tex.Sample(samp, input.coord).a; + float outline = clamp(clamp(tex.Sample(samp, input.coordUp).a + + tex.Sample(samp, input.coordDown).a + + tex.Sample(samp, input.coordLeft).a + + tex.Sample(samp, input.coordRight).a, 0.0, 1.0) - glyph, 0.0, 1.0); return outline * styleColor + glyph * colorVec; } |