aboutsummaryrefslogtreecommitdiffstats
path: root/src/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins')
-rw-r--r--src/plugins/qmltooling/packetprotocol/qpacketprotocol.cpp18
-rw-r--r--src/plugins/qmltooling/packetprotocol/qpacketprotocol_p.h2
-rw-r--r--src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.cpp38
-rw-r--r--src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.h10
-rw-r--r--src/plugins/qmltooling/qmldbg_debugger/qqmlnativedebugservice.cpp108
-rw-r--r--src/plugins/qmltooling/qmldbg_debugger/qqmlwatcher.cpp5
-rw-r--r--src/plugins/qmltooling/qmldbg_debugger/qv4debugger.cpp4
-rw-r--r--src/plugins/qmltooling/qmldbg_debugger/qv4debugger.h5
-rw-r--r--src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.cpp16
-rw-r--r--src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.h1
-rw-r--r--src/plugins/qmltooling/qmldbg_debugger/qv4debugjob.cpp4
-rw-r--r--src/plugins/qmltooling/qmldbg_debugger/qv4debugjob.h14
-rw-r--r--src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp121
-rw-r--r--src/plugins/qmltooling/qmldbg_inspector/globalinspector.cpp32
-rw-r--r--src/plugins/qmltooling/qmldbg_inspector/globalinspector.h4
-rw-r--r--src/plugins/qmltooling/qmldbg_inspector/highlight.cpp26
-rw-r--r--src/plugins/qmltooling/qmldbg_inspector/highlight.h7
-rw-r--r--src/plugins/qmltooling/qmldbg_inspector/qqmlinspectorservice.cpp14
-rw-r--r--src/plugins/qmltooling/qmldbg_local/qlocalclientconnection.cpp7
-rw-r--r--src/plugins/qmltooling/qmldbg_native/qqmlnativedebugconnector.cpp10
-rw-r--r--src/plugins/qmltooling/qmldbg_native/qqmlnativedebugconnector.h4
-rw-r--r--src/plugins/qmltooling/qmldbg_profiler/qqmlenginecontrolservice.cpp4
-rw-r--r--src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.cpp29
-rw-r--r--src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.h1
-rw-r--r--src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.cpp71
-rw-r--r--src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.h4
-rw-r--r--src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.cpp39
-rw-r--r--src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.h13
-rw-r--r--src/plugins/qmltooling/qmldbg_quickprofiler/qquickprofileradapter.cpp28
-rw-r--r--src/plugins/qmltooling/qmldbg_quickprofiler/qquickprofileradapter.h2
-rw-r--r--src/plugins/qmltooling/qmldbg_server/qqmldebugserver.cpp88
-rw-r--r--src/plugins/qmltooling/qmldbg_tcp/qtcpserverconnection.cpp7
-rw-r--r--src/plugins/qmltooling/shared/qqmldebugserver.h2
-rw-r--r--src/plugins/scenegraph/d3d12/d3d12.pro20
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12adaptation.cpp11
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12builtinmaterials.cpp13
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12builtinmaterials_p.h3
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12context.cpp54
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12context_p.h10
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp361
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12engine_p.h26
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12engine_p_p.h13
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12glyphcache.cpp17
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12internalimagenode.cpp (renamed from src/plugins/scenegraph/d3d12/qsgd3d12imagenode.cpp)22
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12internalimagenode_p.h (renamed from src/plugins/scenegraph/d3d12/qsgd3d12imagenode_p.h)12
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12internalrectanglenode.cpp (renamed from src/plugins/scenegraph/d3d12/qsgd3d12rectanglenode.cpp)8
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12internalrectanglenode_p.h (renamed from src/plugins/scenegraph/d3d12/qsgd3d12rectanglenode_p.h)12
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12painternode.cpp3
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12painternode_p.h1
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12publicnodes.cpp254
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12publicnodes_p.h136
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12rendercontext.cpp52
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12rendercontext_p.h15
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12renderer.cpp1
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12renderloop.cpp1219
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12renderloop_p.h41
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12shadereffectnode.cpp123
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12shadereffectnode_p.h11
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12spritenode.cpp314
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12spritenode_p.h90
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12texture.cpp8
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12threadedrenderloop.cpp1183
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12threadedrenderloop_p.h129
-rw-r--r--src/plugins/scenegraph/d3d12/shaders/shaders.pri11
-rw-r--r--src/plugins/scenegraph/d3d12/shaders/sprite.hlsl43
-rw-r--r--src/plugins/scenegraph/d3d12/shaders/textmask.hlsl16
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;
}