From 22c3fd050843d2a56fb927492501c3798d253257 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Fri, 25 Oct 2019 19:20:06 +0200 Subject: macOS: Remove assert that primary display always matches CGMainDisplayID Fixes: QTBUG-78707 Change-Id: Ia517f543728c76dcf19558e9e68ed97db7cfaaa4 Reviewed-by: Timur Pocheptsov --- src/plugins/platforms/cocoa/qcocoascreen.mm | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/plugins/platforms/cocoa/qcocoascreen.mm b/src/plugins/platforms/cocoa/qcocoascreen.mm index 392099d083..0e55838e05 100644 --- a/src/plugins/platforms/cocoa/qcocoascreen.mm +++ b/src/plugins/platforms/cocoa/qcocoascreen.mm @@ -552,10 +552,10 @@ QPixmap QCocoaScreen::grabWindow(WId view, int x, int y, int width, int height) */ QCocoaScreen *QCocoaScreen::primaryScreen() { - auto screen = static_cast(QGuiApplication::primaryScreen()->handle()); - Q_ASSERT_X(screen == get(CGMainDisplayID()), "QCocoaScreen", - "The application's primary screen should always be in sync with the main display"); - return screen; + // Note: The primary screen that Qt knows about may not match the current CGMainDisplayID() + // if macOS has not yet been able to inform us that the main display has changed, but we + // will update the primary screen accordingly once the reconfiguration callback comes in. + return static_cast(QGuiApplication::primaryScreen()->handle()); } QList QCocoaScreen::virtualSiblings() const -- cgit v1.2.3 From 7ac3bc9f830a8ea1ebdf4738e883bdc01522c31f Mon Sep 17 00:00:00 2001 From: Timur Pocheptsov Date: Fri, 25 Oct 2019 09:33:17 +0200 Subject: Network proxy (macOS) - fix a memory leak MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit there are several conditional statements where we return without releasing a dictionary (which is returned by a function having 'Copy' in its name, thus giving us the ownership == CFRelease is needed). QCFType fixes this issue. Fixes: QTBUG-79524 Change-Id: Id8a8616ad5b6ec21b5e8103bf52b1d9df9ca5c2f Reviewed-by: Mårten Nordheim --- src/network/kernel/qnetworkproxy_mac.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/network/kernel/qnetworkproxy_mac.cpp b/src/network/kernel/qnetworkproxy_mac.cpp index 92f91956b9..67fda24ea6 100644 --- a/src/network/kernel/qnetworkproxy_mac.cpp +++ b/src/network/kernel/qnetworkproxy_mac.cpp @@ -210,16 +210,14 @@ QList macQueryInternal(const QNetworkProxyQuery &query) QList result; // obtain a dictionary to the proxy settings: - CFDictionaryRef dict = SCDynamicStoreCopyProxies(NULL); + const QCFType dict = SCDynamicStoreCopyProxies(NULL); if (!dict) { qWarning("QNetworkProxyFactory::systemProxyForQuery: SCDynamicStoreCopyProxies returned NULL"); return result; // failed } - if (isHostExcluded(dict, query.peerHostName())) { - CFRelease(dict); + if (isHostExcluded(dict, query.peerHostName())) return result; // no proxy for this host - } // is there a PAC enabled? If so, use it first. CFNumberRef pacEnabled; @@ -329,7 +327,6 @@ QList macQueryInternal(const QNetworkProxyQuery &query) result << https; } - CFRelease(dict); return result; } -- cgit v1.2.3 From a9b34f5726923babed448e2b4af42f117ae53ca4 Mon Sep 17 00:00:00 2001 From: Francisco Boni Date: Tue, 15 Oct 2019 17:56:26 -0300 Subject: QRandom: add support for RDSEED on INTEL_ICL & MSVC We set the macro for RDSEED because neither MSVC nor the Intel compiler on Windows defines the macro. The implication is that when qRandomCpu() calls qCpuHasFeature() in simd.cpp, qDetectCpuFeatures() correctly receives the expected CPU features enabled in the build from qCompilerCpuFeatures, namely CpuFeatureRDSEED (qsimd_x86_p.h) Change-Id: I5741d4f956a93f21c358af8a4ee393c1741b85ee Reviewed-by: Thiago Macieira --- mkspecs/win32-icc/qmake.conf | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/mkspecs/win32-icc/qmake.conf b/mkspecs/win32-icc/qmake.conf index 3cb0d58824..af26c5bc15 100644 --- a/mkspecs/win32-icc/qmake.conf +++ b/mkspecs/win32-icc/qmake.conf @@ -22,7 +22,7 @@ QMAKE_CFLAGS_WARN_OFF = -W0 QMAKE_CFLAGS_DEBUG = $$QMAKE_CFLAGS_OPTIMIZE_DEBUG -Zi -MDd QMAKE_CFLAGS_UTF8_SOURCE = -Qoption,cpp,--unicode_source_kind,UTF-8 QMAKE_CFLAGS_LTCG = -Qipo -QMAKE_CFLAGS_DISABLE_LTCG = -Qno-ipo +QMAKE_CFLAGS_DISABLE_LTCG = -Qipo- QMAKE_CFLAGS_SSE2 = -QxSSE2 QMAKE_CFLAGS_SSE3 = -QxSSE3 @@ -39,6 +39,11 @@ QMAKE_CFLAGS_AVX512DQ += -QxCORE-AVX512 QMAKE_CFLAGS_AVX512BW += -QxCORE-AVX512 QMAKE_CFLAGS_AVX512VL += -QxCORE-AVX512 QMAKE_CFLAGS_F16C = $$QMAKE_CFLAGS_AVX2 +QMAKE_CFLAGS_RDRND = $$QMAKE_CFLAGS_AVX2 +# ICC on Windows lacks the mrdseed compiler option that sets the RDSEED macro +QMAKE_CFLAGS_RDSEED = -D__RDSEED__=1 +QMAKE_CFLAGS_ARCH_HASWELL = $$QMAKE_CFLAGS_AVX2 + QMAKE_CFLAGS_AESNI = -QxSSE2 QMAKE_CFLAGS_SHANI = -QxSSE4.2 -- cgit v1.2.3 From 360a2e79c8abd90fb274b6dcbede222e1e5e37db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Sun, 27 Oct 2019 18:23:10 +0100 Subject: macOS: Don't leak CFUUIDRefs when resolving NSScreen for platform screen Change-Id: I5609071346ef44dc9f16359db451ea9b29dd2b0d Reviewed-by: Timur Pocheptsov --- src/plugins/platforms/cocoa/qcocoascreen.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/platforms/cocoa/qcocoascreen.mm b/src/plugins/platforms/cocoa/qcocoascreen.mm index 0e55838e05..886198d9d5 100644 --- a/src/plugins/platforms/cocoa/qcocoascreen.mm +++ b/src/plugins/platforms/cocoa/qcocoascreen.mm @@ -597,7 +597,7 @@ NSScreen *QCocoaScreen::nativeScreen() const QCFType uuid = CGDisplayCreateUUIDFromDisplayID(m_displayId); for (NSScreen *screen in [NSScreen screens]) { - if (CGDisplayCreateUUIDFromDisplayID(screen.qt_displayId) == uuid) + if (QCFType(CGDisplayCreateUUIDFromDisplayID(screen.qt_displayId)) == uuid) return screen; } -- cgit v1.2.3 From 5982451ac60057948f19af9adcf2b9ebb611cdfa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Sun, 27 Oct 2019 18:28:03 +0100 Subject: macOS: Class initialize QCocoaScreen members Change-Id: I163858400da28668b8a85241e9e6b1d989227a3e Reviewed-by: Timur Pocheptsov --- src/plugins/platforms/cocoa/qcocoascreen.h | 8 ++++---- src/plugins/platforms/cocoa/qcocoascreen.mm | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/plugins/platforms/cocoa/qcocoascreen.h b/src/plugins/platforms/cocoa/qcocoascreen.h index 60243b79be..7ec9a2b5af 100644 --- a/src/plugins/platforms/cocoa/qcocoascreen.h +++ b/src/plugins/platforms/cocoa/qcocoascreen.h @@ -99,18 +99,18 @@ private: static void add(CGDirectDisplayID displayId); void remove(); - CGDirectDisplayID m_displayId = 0; + CGDirectDisplayID m_displayId = kCGNullDirectDisplay; QRect m_geometry; QRect m_availableGeometry; QDpi m_logicalDpi; - qreal m_refreshRate; - int m_depth; + qreal m_refreshRate = 0; + int m_depth = 0; QString m_name; QImage::Format m_format; QSizeF m_physicalSize; QCocoaCursor *m_cursor; - qreal m_devicePixelRatio; + qreal m_devicePixelRatio = 0; CVDisplayLinkRef m_displayLink = nullptr; dispatch_source_t m_displayLinkSource = nullptr; diff --git a/src/plugins/platforms/cocoa/qcocoascreen.mm b/src/plugins/platforms/cocoa/qcocoascreen.mm index 886198d9d5..7a54f616d0 100644 --- a/src/plugins/platforms/cocoa/qcocoascreen.mm +++ b/src/plugins/platforms/cocoa/qcocoascreen.mm @@ -127,7 +127,7 @@ void QCocoaScreen::cleanupScreens() void QCocoaScreen::remove() { - m_displayId = 0; // Prevent stale references during removal + m_displayId = kCGNullDirectDisplay; // Prevent stale references during removal // This may result in the application responding to QGuiApplication::screenRemoved // by moving the window to another screen, either by setGeometry, or by setScreen. -- cgit v1.2.3 From 3ee634d520b28143d4bbbf46c011449bade917de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Sun, 27 Oct 2019 18:29:03 +0100 Subject: macOS: Extend QCocoaScreen logging Change-Id: I91f89ff336b3f48aea91e50860264bd8359805cb Reviewed-by: Timur Pocheptsov --- src/plugins/platforms/cocoa/qcocoascreen.mm | 34 ++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/src/plugins/platforms/cocoa/qcocoascreen.mm b/src/plugins/platforms/cocoa/qcocoascreen.mm index 7a54f616d0..aaaf9d0dcc 100644 --- a/src/plugins/platforms/cocoa/qcocoascreen.mm +++ b/src/plugins/platforms/cocoa/qcocoascreen.mm @@ -54,6 +54,23 @@ QT_BEGIN_NAMESPACE +namespace CoreGraphics { + Q_NAMESPACE + enum DisplayChange { + Moved = kCGDisplayMovedFlag, + SetMain = kCGDisplaySetMainFlag, + SetMode = kCGDisplaySetModeFlag, + Added = kCGDisplayAddFlag, + Removed = kCGDisplayRemoveFlag, + Enabled = kCGDisplayEnabledFlag, + Disabled = kCGDisplayDisabledFlag, + Mirrored = kCGDisplayMirrorFlag, + UnMirrored = kCGDisplayUnMirrorFlag, + DesktopShapeChanged = kCGDisplayDesktopShapeChangedFlag + }; + Q_ENUM_NS(DisplayChange) +} + void QCocoaScreen::initializeScreens() { uint32_t displayCount = 0; @@ -73,6 +90,10 @@ void QCocoaScreen::initializeScreens() Q_UNUSED(userInfo); + qCDebug(lcQpaScreen).verbosity(0).nospace() << "Display reconfiguration" + << " (" << QFlags(flags) << ")" + << " for displayId=" << displayId; + QCocoaScreen *cocoaScreen = QCocoaScreen::get(displayId); if ((flags & kCGDisplayAddFlag) || !cocoaScreen) { @@ -99,16 +120,19 @@ void QCocoaScreen::initializeScreens() return; // Already reconfigured cocoaScreen->updateProperties(); - qCInfo(lcQpaScreen) << "Reconfigured" << cocoaScreen; + qCInfo(lcQpaScreen).nospace() << "Reconfigured " << + (primaryScreen() == cocoaScreen ? "primary " : "") + << cocoaScreen; } }, nullptr); } void QCocoaScreen::add(CGDirectDisplayID displayId) { + const bool isPrimary = CGDisplayIsMain(displayId); QCocoaScreen *cocoaScreen = new QCocoaScreen(displayId); - qCInfo(lcQpaScreen) << "Adding" << cocoaScreen; - QWindowSystemInterface::handleScreenAdded(cocoaScreen, CGDisplayIsMain(displayId)); + qCInfo(lcQpaScreen).nospace() << "Adding " << (isPrimary ? "new primary " : "") << cocoaScreen; + QWindowSystemInterface::handleScreenAdded(cocoaScreen, isPrimary); } QCocoaScreen::QCocoaScreen(CGDirectDisplayID displayId) @@ -140,6 +164,7 @@ void QCocoaScreen::remove() // QCocoaWindow::windowDidChangeScreen. At that point the window will appear to have // already changed its screen, but that's only true if comparing the Qt screens, // not when comparing the NSScreens. + qCInfo(lcQpaScreen).nospace() << "Removing " << (primaryScreen() == this ? "current primary " : "") << this; QWindowSystemInterface::handleScreenRemoved(this); } @@ -639,6 +664,7 @@ QDebug operator<<(QDebug debug, const QCocoaScreen *screen) debug << ", geometry=" << screen->geometry(); debug << ", dpr=" << screen->devicePixelRatio(); debug << ", name=" << screen->name(); + debug << ", displayId=" << screen->m_displayId; debug << ", native=" << screen->nativeScreen(); } debug << ')'; @@ -646,6 +672,8 @@ QDebug operator<<(QDebug debug, const QCocoaScreen *screen) } #endif // !QT_NO_DEBUG_STREAM +#include "qcocoascreen.moc" + QT_END_NAMESPACE @implementation NSScreen (QtExtras) -- cgit v1.2.3 From dcbe25bbbb603bc335d7cf0982a80293289b0d8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Sun, 27 Oct 2019 18:57:56 +0100 Subject: macOS: Only skip screen reconfigure if primary screen changed Change-Id: Ia5d208ace5086e8e92f95f859383773894a18768 Reviewed-by: Timur Pocheptsov --- src/plugins/platforms/cocoa/qcocoascreen.mm | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/plugins/platforms/cocoa/qcocoascreen.mm b/src/plugins/platforms/cocoa/qcocoascreen.mm index aaaf9d0dcc..40273bac84 100644 --- a/src/plugins/platforms/cocoa/qcocoascreen.mm +++ b/src/plugins/platforms/cocoa/qcocoascreen.mm @@ -114,11 +114,10 @@ void QCocoaScreen::initializeScreens() mainDisplay->updateProperties(); qCInfo(lcQpaScreen) << "Primary screen changed to" << mainDisplay; QWindowSystemInterface::handlePrimaryScreenChanged(mainDisplay); + if (cocoaScreen == mainDisplay) + return; // Already reconfigured } - if (cocoaScreen == mainDisplay) - return; // Already reconfigured - cocoaScreen->updateProperties(); qCInfo(lcQpaScreen).nospace() << "Reconfigured " << (primaryScreen() == cocoaScreen ? "primary " : "") -- cgit v1.2.3 From 5d4b5dab7fec0f2a511145209daea9a85e741cc7 Mon Sep 17 00:00:00 2001 From: Christian Ehrlicher Date: Sat, 26 Oct 2019 21:07:09 +0200 Subject: Widget examples: replace QItemDelegate with QStyledItemDelegate Replace QItemDelegate with QStyledItemDelegate in the examples since QItemDelegate is deprecated. Also fix up some unused documentation snippet references. Change-Id: I42b8780ad0c317b9a253cc722d0b471695ed253f Reviewed-by: Friedemann Kleint --- .../itemviews/spreadsheet/spreadsheetdelegate.cpp | 2 +- .../itemviews/spreadsheet/spreadsheetdelegate.h | 4 ++-- examples/widgets/widgets/icons/imagedelegate.cpp | 5 ++-- examples/widgets/widgets/icons/imagedelegate.h | 6 ++--- examples/widgets/widgets/icons/mainwindow.cpp | 28 +++++++--------------- 5 files changed, 17 insertions(+), 28 deletions(-) diff --git a/examples/widgets/itemviews/spreadsheet/spreadsheetdelegate.cpp b/examples/widgets/itemviews/spreadsheet/spreadsheetdelegate.cpp index eadd5fadb8..ad80a238aa 100644 --- a/examples/widgets/itemviews/spreadsheet/spreadsheetdelegate.cpp +++ b/examples/widgets/itemviews/spreadsheet/spreadsheetdelegate.cpp @@ -53,7 +53,7 @@ #include SpreadSheetDelegate::SpreadSheetDelegate(QObject *parent) - : QItemDelegate(parent) + : QStyledItemDelegate(parent) {} QWidget *SpreadSheetDelegate::createEditor(QWidget *parent, diff --git a/examples/widgets/itemviews/spreadsheet/spreadsheetdelegate.h b/examples/widgets/itemviews/spreadsheet/spreadsheetdelegate.h index c89459cadf..36c70d2391 100644 --- a/examples/widgets/itemviews/spreadsheet/spreadsheetdelegate.h +++ b/examples/widgets/itemviews/spreadsheet/spreadsheetdelegate.h @@ -51,9 +51,9 @@ #ifndef SPREADSHEETDELEGATE_H #define SPREADSHEETDELEGATE_H -#include +#include -class SpreadSheetDelegate : public QItemDelegate +class SpreadSheetDelegate : public QStyledItemDelegate { Q_OBJECT diff --git a/examples/widgets/widgets/icons/imagedelegate.cpp b/examples/widgets/widgets/icons/imagedelegate.cpp index 39c2e43134..4fd251aa1b 100644 --- a/examples/widgets/widgets/icons/imagedelegate.cpp +++ b/examples/widgets/widgets/icons/imagedelegate.cpp @@ -55,9 +55,8 @@ //! [0] ImageDelegate::ImageDelegate(QObject *parent) - : QItemDelegate(parent) -{ -} + : QStyledItemDelegate(parent) +{} //! [0] //! [1] diff --git a/examples/widgets/widgets/icons/imagedelegate.h b/examples/widgets/widgets/icons/imagedelegate.h index 3b76b78339..9d65304e2c 100644 --- a/examples/widgets/widgets/icons/imagedelegate.h +++ b/examples/widgets/widgets/icons/imagedelegate.h @@ -51,10 +51,10 @@ #ifndef IMAGEDELEGATE_H #define IMAGEDELEGATE_H -#include +#include //! [0] -class ImageDelegate : public QItemDelegate +class ImageDelegate : public QStyledItemDelegate { Q_OBJECT @@ -72,7 +72,7 @@ public: //! [1] //! [2] private slots: void emitCommitData(); -}; //! [2] +}; #endif diff --git a/examples/widgets/widgets/icons/mainwindow.cpp b/examples/widgets/widgets/icons/mainwindow.cpp index f342c18c4c..8e61260041 100644 --- a/examples/widgets/widgets/icons/mainwindow.cpp +++ b/examples/widgets/widgets/icons/mainwindow.cpp @@ -216,16 +216,13 @@ void MainWindow::changeIcon() QImage image(fileName); if (!image.isNull()) icon.addPixmap(QPixmap::fromImage(image), mode, state); -//! [8] //! [9] +//! [8] } -//! [9] //! [10] } -//! [10] - //! [11] previewArea->setIcon(icon); -} //! [11] +} void MainWindow::addSampleImages() { @@ -280,17 +277,15 @@ void MainWindow::loadImages(const QStringList &fileNames) .arg(QDir::toNativeSeparators(fileInfo.absolutePath()), fileInfo.fileName()) .arg(fileInfo2x.exists() ? fileInfo2x.fileName() : tr("")) .arg(image.width()).arg(image.height()); -//! [13] //! [14] QTableWidgetItem *fileItem = new QTableWidgetItem(imageName); fileItem->setData(Qt::UserRole, fileName); fileItem->setIcon(QPixmap::fromImage(image)); fileItem->setFlags((fileItem->flags() | Qt::ItemIsUserCheckable) & ~Qt::ItemIsEditable); fileItem->setToolTip(toolTip); -//! [14] +//! [13] //! [15] QIcon::Mode mode = QIcon::Normal; -//! [15] //! [16] QIcon::State state = QIcon::Off; if (guessModeStateAct->isChecked()) { if (imageName.contains(QLatin1String("_act"), Qt::CaseInsensitive)) @@ -302,13 +297,11 @@ void MainWindow::loadImages(const QStringList &fileNames) if (imageName.contains(QLatin1String("_on"), Qt::CaseInsensitive)) state = QIcon::On; -//! [16] //! [17] +//! [15] } -//! [17] //! [18] imagesTable->setItem(row, 0, fileItem); -//! [18] //! [19] QTableWidgetItem *modeItem = new QTableWidgetItem(IconPreviewArea::iconModeNames().at(IconPreviewArea::iconModes().indexOf(mode))); modeItem->setToolTip(toolTip); @@ -321,9 +314,9 @@ void MainWindow::loadImages(const QStringList &fileNames) imagesTable->openPersistentEditor(stateItem); fileItem->setCheckState(Qt::Checked); +//! [18] } } -//! [19] void MainWindow::useHighDpiPixmapsChanged(int checkState) { @@ -350,9 +343,7 @@ QWidget *MainWindow::createImagesGroupBox() //! [21] //! [22] - QStringList labels; -//! [22] //! [23] - labels << tr("Image") << tr("Mode") << tr("State"); + const QStringList labels({tr("Image"), tr("Mode"), tr("State")}); imagesTable->horizontalHeader()->setDefaultSectionSize(90); imagesTable->setColumnCount(3); @@ -361,18 +352,17 @@ QWidget *MainWindow::createImagesGroupBox() imagesTable->horizontalHeader()->setSectionResizeMode(1, QHeaderView::Fixed); imagesTable->horizontalHeader()->setSectionResizeMode(2, QHeaderView::Fixed); imagesTable->verticalHeader()->hide(); -//! [23] +//! [22] //! [24] connect(imagesTable, &QTableWidget::itemChanged, -//! [24] //! [25] this, &MainWindow::changeIcon); QVBoxLayout *layout = new QVBoxLayout(imagesGroupBox); layout->addWidget(imagesTable); return imagesGroupBox; -} //! [25] +} //! [26] QWidget *MainWindow::createIconSizeGroupBox() @@ -428,8 +418,8 @@ QWidget *MainWindow::createIconSizeGroupBox() layout->addLayout(otherSizeLayout, 3, 0, 1, 2); layout->setRowStretch(4, 1); return iconSizeGroupBox; -} //! [27] +} void MainWindow::screenChanged() { -- cgit v1.2.3 From c33d8bfc996cb52d3b9d5b821e51860347c74fa2 Mon Sep 17 00:00:00 2001 From: Ville Voutilainen Date: Thu, 17 Oct 2019 17:38:53 +0300 Subject: Add a timeout for transfers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If a transfer timeout is set for QNetworkRequest, downloads and uploads are aborted if the timeout expires and bytes haven't been transmitted in either direction. Task-number: QTBUG-3443 Change-Id: I702d223d673f0c6612343dc9d053815acfcb61b8 Reviewed-by: Mårten Nordheim --- src/network/access/qnetworkreplyhttpimpl.cpp | 36 +++++++++++-- src/network/access/qnetworkreplyhttpimpl_p.h | 7 +++ src/network/access/qnetworkrequest.cpp | 50 ++++++++++++++++++ src/network/access/qnetworkrequest.h | 6 +++ .../access/qnetworkreply/tst_qnetworkreply.cpp | 60 +++++++++++++++++++++- 5 files changed, 154 insertions(+), 5 deletions(-) diff --git a/src/network/access/qnetworkreplyhttpimpl.cpp b/src/network/access/qnetworkreplyhttpimpl.cpp index 44c1d3e422..a13c2b144c 100644 --- a/src/network/access/qnetworkreplyhttpimpl.cpp +++ b/src/network/access/qnetworkreplyhttpimpl.cpp @@ -461,6 +461,7 @@ QNetworkReplyHttpImplPrivate::QNetworkReplyHttpImplPrivate() , preMigrationDownloaded(-1) , bytesDownloaded(0) , bytesBuffered(0) + , transferTimeout(nullptr) , downloadBufferReadPosition(0) , downloadBufferCurrentSize(0) , downloadZerocopyBuffer(nullptr) @@ -1067,6 +1068,7 @@ void QNetworkReplyHttpImplPrivate::replyDownloadData(QByteArray d) if (!isHttpRedirectResponse()) { buffer.append(d); bytesDownloaded += d.size(); + setupTransferTimeout(); } bytesBuffered += d.size(); @@ -1401,6 +1403,7 @@ void QNetworkReplyHttpImplPrivate::replyDownloadProgressSlot(qint64 bytesReceive return; bytesDownloaded = bytesReceived; + setupTransferTimeout(); downloadBufferCurrentSize = bytesReceived; @@ -1857,7 +1860,6 @@ bool QNetworkReplyHttpImplPrivate::startWaitForSession(QSharedPointersetFinished(true); @@ -2033,6 +2036,31 @@ void QNetworkReplyHttpImplPrivate::_q_bufferOutgoingData() } } +void QNetworkReplyHttpImplPrivate::_q_transferTimedOut() +{ + Q_Q(QNetworkReplyHttpImpl); + q->abort(); +} + +void QNetworkReplyHttpImplPrivate::setupTransferTimeout() +{ + Q_Q(QNetworkReplyHttpImpl); + if (!transferTimeout) { + transferTimeout = new QTimer(q); + QObject::connect(transferTimeout, SIGNAL(timeout()), + q, SLOT(_q_transferTimedOut()), + Qt::QueuedConnection); + } + transferTimeout->stop(); + if (request.transferTimeout()) { + transferTimeout->setSingleShot(true); + transferTimeout->setInterval(request.transferTimeout()); + QMetaObject::invokeMethod(transferTimeout, "start", + Qt::QueuedConnection); + + } +} + #ifndef QT_NO_BEARERMANAGEMENT void QNetworkReplyHttpImplPrivate::_q_networkSessionConnected() { @@ -2115,6 +2143,8 @@ void QNetworkReplyHttpImplPrivate::emitReplyUploadProgress(qint64 bytesSent, qin if (isFinished) return; + setupTransferTimeout(); + if (!emitAllUploadProgressSignals) { //choke signal emissions, except the first and last signals which are unconditional if (uploadProgressSignalChoke.isValid()) { @@ -2126,7 +2156,6 @@ void QNetworkReplyHttpImplPrivate::emitReplyUploadProgress(qint64 bytesSent, qin uploadProgressSignalChoke.start(); } } - emit q->uploadProgress(bytesSent, bytesTotal); } @@ -2159,7 +2188,8 @@ void QNetworkReplyHttpImplPrivate::_q_finished() void QNetworkReplyHttpImplPrivate::finished() { Q_Q(QNetworkReplyHttpImpl); - + if (transferTimeout) + transferTimeout->stop(); if (state == Finished || state == Aborted || state == WaitingForSession) return; diff --git a/src/network/access/qnetworkreplyhttpimpl_p.h b/src/network/access/qnetworkreplyhttpimpl_p.h index ef69ce0653..dec0c4c589 100644 --- a/src/network/access/qnetworkreplyhttpimpl_p.h +++ b/src/network/access/qnetworkreplyhttpimpl_p.h @@ -59,6 +59,7 @@ #include "QtCore/qdatetime.h" #include "QtCore/qsharedpointer.h" #include "QtCore/qscopedpointer.h" +#include "QtCore/qtimer.h" #include "qatomic.h" #include @@ -100,6 +101,7 @@ public: Q_PRIVATE_SLOT(d_func(), void _q_cacheLoadReadyRead()) Q_PRIVATE_SLOT(d_func(), void _q_bufferOutgoingData()) Q_PRIVATE_SLOT(d_func(), void _q_bufferOutgoingDataFinished()) + Q_PRIVATE_SLOT(d_func(), void _q_transferTimedOut()) #ifndef QT_NO_BEARERMANAGEMENT Q_PRIVATE_SLOT(d_func(), void _q_networkSessionConnected()) Q_PRIVATE_SLOT(d_func(), void _q_networkSessionFailed()) @@ -181,6 +183,9 @@ public: void _q_cacheSaveDeviceAboutToClose(); + void _q_transferTimedOut(); + void setupTransferTimeout(); + #ifndef QT_NO_BEARERMANAGEMENT void _q_networkSessionConnected(); void _q_networkSessionFailed(); @@ -250,6 +255,8 @@ public: qint64 bytesDownloaded; qint64 bytesBuffered; + QTimer *transferTimeout; + // Only used when the "zero copy" style is used. // Please note that the whole "zero copy" download buffer API is private right now. Do not use it. qint64 downloadBufferReadPosition; diff --git a/src/network/access/qnetworkrequest.cpp b/src/network/access/qnetworkrequest.cpp index 7899bce32b..e03d844af9 100644 --- a/src/network/access/qnetworkrequest.cpp +++ b/src/network/access/qnetworkrequest.cpp @@ -425,6 +425,18 @@ QT_BEGIN_NAMESPACE based on some app-specific configuration. */ +/*! + \enum QNetworkRequest::TransferTimeoutConstant + \since 5.15 + + A constant that can be used for enabling transfer + timeouts with a preset value. + + \value TransferTimeoutPreset The transfer timeout in milliseconds. + Used if setTimeout() is called + without an argument. + */ + class QNetworkRequestPrivate: public QSharedData, public QNetworkHeadersPrivate { public: @@ -435,6 +447,7 @@ public: , sslConfiguration(0) #endif , maxRedirectsAllowed(maxRedirectCount) + , transferTimeout(0) { qRegisterMetaType(); } ~QNetworkRequestPrivate() { @@ -459,6 +472,7 @@ public: #if QT_CONFIG(http) h2Configuration = other.h2Configuration; #endif + transferTimeout = other.transferTimeout; } inline bool operator==(const QNetworkRequestPrivate &other) const @@ -472,6 +486,7 @@ public: #if QT_CONFIG(http) && h2Configuration == other.h2Configuration #endif + && transferTimeout == other.transferTimeout ; // don't compare cookedHeaders } @@ -486,6 +501,7 @@ public: #if QT_CONFIG(http) QHttp2Configuration h2Configuration; #endif + int transferTimeout; }; /*! @@ -909,6 +925,40 @@ void QNetworkRequest::setHttp2Configuration(const QHttp2Configuration &configura { d->h2Configuration = configuration; } + +/*! + \since 5.15 + + Returns the timeout used for transfers, in milliseconds. + + This timeout is zero if setTransferTimeout hasn't been + called, which means that the timeout is not used. + + \sa setTransferTimeout +*/ +int QNetworkRequest::transferTimeout() +{ + return d->transferTimeout; +} + +/*! + \since 5.15 + + Sets \a timeout as the transfer timeout in milliseconds. + + Transfers are aborted if no bytes are transferred before + the timeout expires. Zero means no timer is set. If no + argument is provided, the timeout is + QNetworkRequest::TransferTimeoutPreset. If this function + is not called, the timeout is disabled and has the + value zero. + + \sa transferTimeout +*/ +void QNetworkRequest::setTransferTimeout(int timeout) +{ + d->transferTimeout = timeout; +} #endif // QT_CONFIG(http) || defined(Q_CLANG_QDOC) static QByteArray headerName(QNetworkRequest::KnownHeaders header) diff --git a/src/network/access/qnetworkrequest.h b/src/network/access/qnetworkrequest.h index 72a6555d91..5d9969bd9b 100644 --- a/src/network/access/qnetworkrequest.h +++ b/src/network/access/qnetworkrequest.h @@ -134,6 +134,9 @@ public: UserVerifiedRedirectPolicy }; + enum TransferTimeoutConstant { + TransferTimeoutPreset = 30000 + }; QNetworkRequest(); explicit QNetworkRequest(const QUrl &url); @@ -185,6 +188,9 @@ public: #if QT_CONFIG(http) || defined(Q_CLANG_QDOC) QHttp2Configuration http2Configuration() const; void setHttp2Configuration(const QHttp2Configuration &configuration); + + int transferTimeout(); + void setTransferTimeout(int timeout = TransferTimeoutPreset); #endif // QT_CONFIG(http) || defined(Q_CLANG_QDOC) private: QSharedDataPointer d; diff --git a/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp b/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp index 418e1caf68..3bc6717d58 100644 --- a/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp +++ b/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp @@ -511,6 +511,8 @@ private Q_SLOTS: void autoDeleteReplies_data(); void autoDeleteReplies(); + void getWithTimeout(); + void postWithTimeout(); // NOTE: This test must be last! void parentingRepliesToTheApp(); private: @@ -589,6 +591,7 @@ public: bool multiple; int totalConnections; + bool stopTransfer = false; bool hasContent = false; int contentRead = 0; int contentLength = 0; @@ -655,7 +658,7 @@ protected: // we need to emulate the bytesWrittenSlot call if the data is empty. if (dataToTransmit.size() == 0) { emit client->bytesWritten(0); - } else { + } else if (!stopTransfer) { client->write(dataToTransmit); // FIXME: For SSL connections, if we don't flush the socket, the // client never receives the data and since we're doing a disconnect @@ -711,7 +714,8 @@ public slots: Q_ASSERT(currentClient); if (currentClient != client) client = currentClient; - + if (stopTransfer) + return; receivedData += client->readAll(); const int doubleEndlPos = receivedData.indexOf("\r\n\r\n"); @@ -9342,6 +9346,58 @@ void tst_QNetworkReply::autoDeleteReplies() } } +void tst_QNetworkReply::getWithTimeout() +{ + MiniHttpServer server(tst_QNetworkReply::httpEmpty200Response, false); + + QNetworkRequest request(QUrl("http://localhost:" + QString::number(server.serverPort()))); + QNetworkReplyPtr reply(manager.get(request)); + QSignalSpy spy(reply.data(), SIGNAL(error(QNetworkReply::NetworkError))); + + QCOMPARE(waitForFinish(reply), int(Success)); + + QCOMPARE(spy.count(), 0); + QVERIFY(reply->error() == QNetworkReply::NoError); + + request.setTransferTimeout(1000); + server.stopTransfer = true; + + QNetworkReplyPtr reply2(manager.get(request)); + QSignalSpy spy2(reply2.data(), SIGNAL(error(QNetworkReply::NetworkError))); + + QCOMPARE(waitForFinish(reply2), int(Failure)); + + QCOMPARE(spy2.count(), 1); + QVERIFY(reply2->error() == QNetworkReply::OperationCanceledError); +} + +void tst_QNetworkReply::postWithTimeout() +{ + MiniHttpServer server(tst_QNetworkReply::httpEmpty200Response, false); + + QNetworkRequest request(QUrl("http://localhost:" + QString::number(server.serverPort()))); + request.setRawHeader("Content-Type", "application/octet-stream"); + QByteArray postData("Just some nonsense"); + QNetworkReplyPtr reply(manager.post(request, postData)); + QSignalSpy spy(reply.data(), SIGNAL(error(QNetworkReply::NetworkError))); + + QCOMPARE(waitForFinish(reply), int(Success)); + + QCOMPARE(spy.count(), 0); + QVERIFY(reply->error() == QNetworkReply::NoError); + + request.setTransferTimeout(1000); + server.stopTransfer = true; + + QNetworkReplyPtr reply2(manager.post(request, postData)); + QSignalSpy spy2(reply2.data(), SIGNAL(error(QNetworkReply::NetworkError))); + + QCOMPARE(waitForFinish(reply2), int(Failure)); + + QCOMPARE(spy2.count(), 1); + QVERIFY(reply2->error() == QNetworkReply::OperationCanceledError); +} + // NOTE: This test must be last testcase in tst_qnetworkreply! void tst_QNetworkReply::parentingRepliesToTheApp() { -- cgit v1.2.3 From 538d5bdf662f4198a2d6c38a6002abb81433fb29 Mon Sep 17 00:00:00 2001 From: Giuseppe D'Angelo Date: Fri, 18 Oct 2019 13:26:02 +0200 Subject: Add a getter for QObjectPrivate::threadData Strictly a temporary measure to deal with cross-module merges. Change-Id: I344bb3f20f68f04367041834e608669122ff70b1 Reviewed-by: Thiago Macieira --- src/corelib/kernel/qobject_p.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/corelib/kernel/qobject_p.h b/src/corelib/kernel/qobject_p.h index feafcaf323..45fc27917d 100644 --- a/src/corelib/kernel/qobject_p.h +++ b/src/corelib/kernel/qobject_p.h @@ -374,6 +374,7 @@ public: } public: ExtraData *extraData; // extra data set by the user + QThreadData *getThreadData() const { return threadData; } QThreadData *threadData; // id of the thread that owns the object using ConnectionDataPointer = QExplicitlySharedDataPointer; -- cgit v1.2.3 From c87e2c37de439996895fc2e2e4c79376b6ec1f09 Mon Sep 17 00:00:00 2001 From: Christian Ehrlicher Date: Sun, 8 Sep 2019 20:21:41 +0200 Subject: tst_QDataWidgetMapper/QFileIconProvider/ItemEditorFactory: cleanup Cleanup QDataWidgetMapper/QFileIconProvider/ItemEditorFactory autotests: - use range-based for loops - use nullptr - use member initialization - use new signal/slot syntax - use static invocations - use override Change-Id: I1a36ea2da7de1cfa5d5d4e305ef508fda3a6c460 Reviewed-by: Friedemann Kleint --- .../qdatawidgetmapper/tst_qdatawidgetmapper.cpp | 26 ++++++++++--------- .../qfileiconprovider/tst_qfileiconprovider.cpp | 29 +++++++--------------- .../qitemeditorfactory/tst_qitemeditorfactory.cpp | 8 +++--- 3 files changed, 28 insertions(+), 35 deletions(-) diff --git a/tests/auto/widgets/itemviews/qdatawidgetmapper/tst_qdatawidgetmapper.cpp b/tests/auto/widgets/itemviews/qdatawidgetmapper/tst_qdatawidgetmapper.cpp index 5717afab51..856672b957 100644 --- a/tests/auto/widgets/itemviews/qdatawidgetmapper/tst_qdatawidgetmapper.cpp +++ b/tests/auto/widgets/itemviews/qdatawidgetmapper/tst_qdatawidgetmapper.cpp @@ -25,15 +25,16 @@ ** $QT_END_LICENSE$ ** ****************************************************************************/ + +#include #include -#include #include -#include +#include +#include +#include +#include #include #include -#include -#include -#include class tst_QDataWidgetMapper: public QObject { @@ -56,7 +57,7 @@ private slots: Q_DECLARE_METATYPE(QAbstractItemDelegate::EndEditHint) -static QStandardItemModel *testModel(QObject *parent = 0) +static QStandardItemModel *testModel(QObject *parent) { QStandardItemModel *model = new QStandardItemModel(10, 10, parent); @@ -84,7 +85,7 @@ void tst_QDataWidgetMapper::setModel() { // let the model go out of scope firstma QStandardItemModel model; mapper.setModel(&model); - QCOMPARE(mapper.model(), static_cast(&model)); + QCOMPARE(mapper.model(), &model); } QCOMPARE(mapper.model(), nullptr); @@ -273,7 +274,7 @@ void tst_QDataWidgetMapper::currentIndexChanged() QAbstractItemModel *model = testModel(&mapper); mapper.setModel(model); - QSignalSpy spy(&mapper, SIGNAL(currentIndexChanged(int))); + QSignalSpy spy(&mapper, &QDataWidgetMapper::currentIndexChanged); mapper.toFirst(); QCOMPARE(spy.count(), 1); @@ -405,13 +406,13 @@ void tst_QDataWidgetMapper::mappedWidgetAt() mapper.addMapping(&lineEdit1, 1); mapper.addMapping(&lineEdit2, 2); - QCOMPARE(mapper.mappedWidgetAt(1), static_cast(&lineEdit1)); - QCOMPARE(mapper.mappedWidgetAt(2), static_cast(&lineEdit2)); + QCOMPARE(mapper.mappedWidgetAt(1), &lineEdit1); + QCOMPARE(mapper.mappedWidgetAt(2), &lineEdit2); mapper.addMapping(&lineEdit2, 4242); QCOMPARE(mapper.mappedWidgetAt(2), nullptr); - QCOMPARE(mapper.mappedWidgetAt(4242), static_cast(&lineEdit2)); + QCOMPARE(mapper.mappedWidgetAt(4242), &lineEdit2); } void tst_QDataWidgetMapper::textEditDoesntChangeFocusOnTab_qtbug3305() @@ -423,7 +424,8 @@ void tst_QDataWidgetMapper::textEditDoesntChangeFocusOnTab_qtbug3305() QAbstractItemModel *model = testModel(&mapper); mapper.setModel(model); - QSignalSpy closeEditorSpy(mapper.itemDelegate(), SIGNAL(closeEditor(QWidget*,QAbstractItemDelegate::EndEditHint))); + QSignalSpy closeEditorSpy(mapper.itemDelegate(), + &QAbstractItemDelegate::closeEditor); QVERIFY(closeEditorSpy.isValid()); QWidget container; diff --git a/tests/auto/widgets/itemviews/qfileiconprovider/tst_qfileiconprovider.cpp b/tests/auto/widgets/itemviews/qfileiconprovider/tst_qfileiconprovider.cpp index 6db1f78312..4824973576 100644 --- a/tests/auto/widgets/itemviews/qfileiconprovider/tst_qfileiconprovider.cpp +++ b/tests/auto/widgets/itemviews/qfileiconprovider/tst_qfileiconprovider.cpp @@ -27,16 +27,15 @@ ****************************************************************************/ -#include -#include -#include +#include +#include +#include class tst_QFileIconProvider : public QObject { Q_OBJECT private slots: - void qfileiconprovider_data(); void qfileiconprovider(); void iconType_data(); @@ -51,21 +50,10 @@ private slots: void taskQTBUG_46755_QFileIconEngine_crash(); }; -// Subclass that exposes the protected functions. -class SubQFileIconProvider : public QFileIconProvider -{ -public: - -}; - -void tst_QFileIconProvider::qfileiconprovider_data() -{ -} - void tst_QFileIconProvider::qfileiconprovider() { // don't crash - SubQFileIconProvider provider; + QFileIconProvider provider; } Q_DECLARE_METATYPE(QFileIconProvider::IconType) @@ -86,7 +74,7 @@ void tst_QFileIconProvider::iconType_data() void tst_QFileIconProvider::iconType() { QFETCH(QFileIconProvider::IconType, type); - SubQFileIconProvider provider; + QFileIconProvider provider; QVERIFY(!provider.icon(type).isNull()); } @@ -109,7 +97,7 @@ void tst_QFileIconProvider::iconInfo() if (setPath) QVERIFY(info.exists()); - SubQFileIconProvider provider; + QFileIconProvider provider; // we should always get an icon QVERIFY(!provider.icon(info).isNull()); } @@ -131,7 +119,7 @@ void tst_QFileIconProvider::type_data() void tst_QFileIconProvider::type() { QFETCH(QFileInfo, info); - SubQFileIconProvider provider; + QFileIconProvider provider; QVERIFY(!provider.type(info).isEmpty()); } @@ -144,7 +132,8 @@ static QIcon getIcon() void tst_QFileIconProvider::taskQTBUG_46755_QFileIconEngine_crash() { const QIcon &icon = getIcon(); - foreach (const QSize &size, icon.availableSizes()) + const auto sizes = icon.availableSizes(); + for (const QSize &size : sizes) icon.pixmap(size); // No crash, all good. diff --git a/tests/auto/widgets/itemviews/qitemeditorfactory/tst_qitemeditorfactory.cpp b/tests/auto/widgets/itemviews/qitemeditorfactory/tst_qitemeditorfactory.cpp index ae587279b2..ed4c543d0a 100644 --- a/tests/auto/widgets/itemviews/qitemeditorfactory/tst_qitemeditorfactory.cpp +++ b/tests/auto/widgets/itemviews/qitemeditorfactory/tst_qitemeditorfactory.cpp @@ -25,9 +25,11 @@ ** $QT_END_LICENSE$ ** ****************************************************************************/ -#include -#include -#include + + +#include +#include +#include class tst_QItemEditorFactory: public QObject { -- cgit v1.2.3 From 58a67e4e0a88e52d71eef5d08df1465f7ac610ae Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Mon, 28 Oct 2019 16:37:57 +0100 Subject: rhi: Move to CBOR in QShader and expand the autotest Binary JSON is said to become deprecated. Therefore, add support for CBOR. Binary JSON is still supported for deserialization, so all existing .qsb files will continue to work, as long as the binaryjson feature is enabled in the Qt build. Also makes QShaderDescription comparable. This is important for tests in particular. A nice side effect of using CBOR is that .qsb files become smaller. For a typical Qt Quick material shader this can mean a reduction of 300 bytes or more. Task-number: QTBUG-79576 Change-Id: I5547c0266e3e8128c9653e954e47487352267f71 Reviewed-by: Paul Olav Tvete --- src/gui/rhi/qshader.cpp | 23 +-- src/gui/rhi/qshader_p_p.h | 6 + src/gui/rhi/qshaderdescription.cpp | 152 +++++++++++++++++- src/gui/rhi/qshaderdescription_p.h | 40 +++++ tests/auto/gui/rhi/qshader/data/README | 15 ++ tests/auto/gui/rhi/qshader/data/color.vert.qsb | Bin 1847 -> 0 bytes .../gui/rhi/qshader/data/color_all_v1.vert.qsb | Bin 0 -> 1847 bytes .../gui/rhi/qshader/data/color_simple.vert.qsb | Bin 813 -> 0 bytes .../gui/rhi/qshader/data/color_spirv_v1.vert.qsb | Bin 0 -> 813 bytes tests/auto/gui/rhi/qshader/data/texture.frag | 16 ++ .../gui/rhi/qshader/data/texture_all_v2.frag.qsb | Bin 0 -> 1691 bytes .../gui/rhi/qshader/data/texture_all_v3.frag.qsb | Bin 0 -> 1432 bytes tests/auto/gui/rhi/qshader/tst_qshader.cpp | 174 ++++++++++++++++++++- 13 files changed, 410 insertions(+), 16 deletions(-) create mode 100644 tests/auto/gui/rhi/qshader/data/README delete mode 100644 tests/auto/gui/rhi/qshader/data/color.vert.qsb create mode 100644 tests/auto/gui/rhi/qshader/data/color_all_v1.vert.qsb delete mode 100644 tests/auto/gui/rhi/qshader/data/color_simple.vert.qsb create mode 100644 tests/auto/gui/rhi/qshader/data/color_spirv_v1.vert.qsb create mode 100644 tests/auto/gui/rhi/qshader/data/texture.frag create mode 100644 tests/auto/gui/rhi/qshader/data/texture_all_v2.frag.qsb create mode 100644 tests/auto/gui/rhi/qshader/data/texture_all_v3.frag.qsb diff --git a/src/gui/rhi/qshader.cpp b/src/gui/rhi/qshader.cpp index c22b029dc8..14d780b4e4 100644 --- a/src/gui/rhi/qshader.cpp +++ b/src/gui/rhi/qshader.cpp @@ -214,9 +214,6 @@ QT_BEGIN_NAMESPACE QShader, it indicates no shader code was found for the requested key. */ -static const int QSB_VERSION = 2; -static const int QSB_VERSION_WITHOUT_BINDINGS = 1; - /*! Constructs a new, empty (and thus invalid) QShader instance. */ @@ -368,9 +365,9 @@ QByteArray QShader::serialized() const if (!buf.open(QIODevice::WriteOnly)) return QByteArray(); - ds << QSB_VERSION; + ds << QShaderPrivate::QSB_VERSION; ds << d->stage; - ds << d->desc.toBinaryJson(); + ds << d->desc.toCbor(); ds << d->shaders.count(); for (auto it = d->shaders.cbegin(), itEnd = d->shaders.cend(); it != itEnd; ++it) { const QShaderKey &k(it.key()); @@ -429,9 +426,12 @@ QShader QShader::fromSerialized(const QByteArray &data) Q_ASSERT(d->ref.loadRelaxed() == 1); // must be detached int intVal; ds >> intVal; - const int qsbVersion = intVal; - if (qsbVersion != QSB_VERSION && qsbVersion != QSB_VERSION_WITHOUT_BINDINGS) { - qWarning("Attempted to deserialize QShader with unknown version %d.", qsbVersion); + d->qsbVersion = intVal; + if (d->qsbVersion != QShaderPrivate::QSB_VERSION + && d->qsbVersion != QShaderPrivate::QSB_VERSION_WITH_BINARY_JSON + && d->qsbVersion != QShaderPrivate::QSB_VERSION_WITHOUT_BINDINGS) + { + qWarning("Attempted to deserialize QShader with unknown version %d.", d->qsbVersion); return QShader(); } @@ -439,7 +439,10 @@ QShader QShader::fromSerialized(const QByteArray &data) d->stage = Stage(intVal); QByteArray descBin; ds >> descBin; - d->desc = QShaderDescription::fromBinaryJson(descBin); + if (d->qsbVersion > QShaderPrivate::QSB_VERSION_WITH_BINARY_JSON) + d->desc = QShaderDescription::fromCbor(descBin); + else + d->desc = QShaderDescription::fromBinaryJson(descBin); int count; ds >> count; for (int i = 0; i < count; ++i) { @@ -454,7 +457,7 @@ QShader QShader::fromSerialized(const QByteArray &data) d->shaders[k] = shader; } - if (qsbVersion != QSB_VERSION_WITHOUT_BINDINGS) { + if (d->qsbVersion > QShaderPrivate::QSB_VERSION_WITHOUT_BINDINGS) { ds >> count; for (int i = 0; i < count; ++i) { QShaderKey k; diff --git a/src/gui/rhi/qshader_p_p.h b/src/gui/rhi/qshader_p_p.h index 4535e01491..8c89f2b45f 100644 --- a/src/gui/rhi/qshader_p_p.h +++ b/src/gui/rhi/qshader_p_p.h @@ -57,6 +57,10 @@ QT_BEGIN_NAMESPACE struct Q_GUI_EXPORT QShaderPrivate { + static const int QSB_VERSION = 3; + static const int QSB_VERSION_WITH_BINARY_JSON = 2; + static const int QSB_VERSION_WITHOUT_BINDINGS = 1; + QShaderPrivate() : ref(1) { @@ -64,6 +68,7 @@ struct Q_GUI_EXPORT QShaderPrivate QShaderPrivate(const QShaderPrivate *other) : ref(1), + qsbVersion(other->qsbVersion), stage(other->stage), desc(other->desc), shaders(other->shaders), @@ -75,6 +80,7 @@ struct Q_GUI_EXPORT QShaderPrivate static const QShaderPrivate *get(const QShader *s) { return s->d; } QAtomicInt ref; + int qsbVersion = QSB_VERSION; QShader::Stage stage = QShader::VertexStage; QShaderDescription desc; QHash shaders; diff --git a/src/gui/rhi/qshaderdescription.cpp b/src/gui/rhi/qshaderdescription.cpp index 179d5f3a07..d0f73f6aa7 100644 --- a/src/gui/rhi/qshaderdescription.cpp +++ b/src/gui/rhi/qshaderdescription.cpp @@ -38,6 +38,9 @@ #include #include #include +#include +#include +#include QT_BEGIN_NAMESPACE @@ -335,11 +338,27 @@ bool QShaderDescription::isValid() const /*! \return a serialized binary version of the data. - \sa toJson() + \sa toJson(), toCbor() */ QByteArray QShaderDescription::toBinaryJson() const { +#if QT_CONFIG(binaryjson) return d->makeDoc().toBinaryData(); +#else + qWarning("Cannot generate binary JSON from QShaderDescription due to disabled binaryjson feature"); + return QByteArray(); +#endif +} + +/*! + \return a serialized binary version of the data in CBOR (Concise Binary + Object Representation) format. + + \sa QCborValue, toBinaryJson(), toJson() + */ +QByteArray QShaderDescription::toCbor() const +{ + return QCborValue::fromJsonValue(d->makeDoc().object()).toCbor(); } /*! @@ -347,7 +366,7 @@ QByteArray QShaderDescription::toBinaryJson() const \note There is no deserialization method provided for JSON text. - \sa toBinaryJson() + \sa toBinaryJson(), toCbor() */ QByteArray QShaderDescription::toJson() const { @@ -357,11 +376,38 @@ QByteArray QShaderDescription::toJson() const /*! Deserializes the given binary JSON \a data and returns a new QShaderDescription. + + \sa fromCbor() */ QShaderDescription QShaderDescription::fromBinaryJson(const QByteArray &data) { QShaderDescription desc; +#if QT_CONFIG(binaryjson) QShaderDescriptionPrivate::get(&desc)->loadDoc(QJsonDocument::fromBinaryData(data)); +#else + Q_UNUSED(data); + qWarning("Cannot load QShaderDescription from binary JSON due to disabled binaryjson feature"); +#endif + return desc; +} + +/*! + Deserializes the given CBOR \a data and returns a new QShaderDescription. + + \sa fromBinaryJson() + */ +QShaderDescription QShaderDescription::fromCbor(const QByteArray &data) +{ + QShaderDescription desc; + const QCborValue cbor = QCborValue::fromCbor(data); + if (cbor.isMap()) { + const QJsonDocument doc(cbor.toMap().toJsonObject()); + QShaderDescriptionPrivate::get(&desc)->loadDoc(doc); + } + if (cbor.isArray()) { + const QJsonDocument doc(cbor.toArray().toJsonArray()); + QShaderDescriptionPrivate::get(&desc)->loadDoc(doc); + } return desc; } @@ -1119,4 +1165,106 @@ void QShaderDescriptionPrivate::loadDoc(const QJsonDocument &doc) } } +/*! + Returns \c true if the two QShaderDescription objects \a lhs and \a rhs are + equal. + + \relates QShaderDescription + */ +bool operator==(const QShaderDescription &lhs, const QShaderDescription &rhs) Q_DECL_NOTHROW +{ + if (lhs.d == rhs.d) + return true; + + return lhs.d->inVars == rhs.d->inVars + && lhs.d->outVars == rhs.d->outVars + && lhs.d->uniformBlocks == rhs.d->uniformBlocks + && lhs.d->pushConstantBlocks == rhs.d->pushConstantBlocks + && lhs.d->storageBlocks == rhs.d->storageBlocks + && lhs.d->combinedImageSamplers == rhs.d->combinedImageSamplers + && lhs.d->storageImages == rhs.d->storageImages + && lhs.d->localSize == rhs.d->localSize; +} + +/*! + Returns \c true if the two InOutVariable objects \a lhs and \a rhs are + equal. + + \relates QShaderDescription::InOutVariable + */ +bool operator==(const QShaderDescription::InOutVariable &lhs, const QShaderDescription::InOutVariable &rhs) Q_DECL_NOTHROW +{ + return lhs.name == rhs.name + && lhs.type == rhs.type + && lhs.location == rhs.location + && lhs.binding == rhs.binding + && lhs.descriptorSet == rhs.descriptorSet + && lhs.imageFormat == rhs.imageFormat + && lhs.imageFlags == rhs.imageFlags; +} + +/*! + Returns \c true if the two BlockVariable objects \a lhs and \a rhs are + equal. + + \relates QShaderDescription::BlockVariable + */ +bool operator==(const QShaderDescription::BlockVariable &lhs, const QShaderDescription::BlockVariable &rhs) Q_DECL_NOTHROW +{ + return lhs.name == rhs.name + && lhs.type == rhs.type + && lhs.offset == rhs.offset + && lhs.size == rhs.size + && lhs.arrayDims == rhs.arrayDims + && lhs.arrayStride == rhs.arrayStride + && lhs.matrixStride == rhs.matrixStride + && lhs.matrixIsRowMajor == rhs.matrixIsRowMajor + && lhs.structMembers == rhs.structMembers; +} + +/*! + Returns \c true if the two UniformBlock objects \a lhs and \a rhs are + equal. + + \relates QShaderDescription::UniformBlock + */ +bool operator==(const QShaderDescription::UniformBlock &lhs, const QShaderDescription::UniformBlock &rhs) Q_DECL_NOTHROW +{ + return lhs.blockName == rhs.blockName + && lhs.structName == rhs.structName + && lhs.size == rhs.size + && lhs.binding == rhs.binding + && lhs.descriptorSet == rhs.descriptorSet + && lhs.members == rhs.members; +} + +/*! + Returns \c true if the two PushConstantBlock objects \a lhs and \a rhs are + equal. + + \relates QShaderDescription::PushConstantBlock + */ +bool operator==(const QShaderDescription::PushConstantBlock &lhs, const QShaderDescription::PushConstantBlock &rhs) Q_DECL_NOTHROW +{ + return lhs.name == rhs.name + && lhs.size == rhs.size + && lhs.members == rhs.members; +} + +/*! + Returns \c true if the two StorageBlock objects \a lhs and \a rhs are + equal. + + \relates QShaderDescription::StorageBlock + */ +bool operator==(const QShaderDescription::StorageBlock &lhs, const QShaderDescription::StorageBlock &rhs) Q_DECL_NOTHROW +{ + return lhs.blockName == rhs.blockName + && lhs.instanceName == rhs.instanceName + && lhs.knownSize == rhs.knownSize + && lhs.binding == rhs.binding + && lhs.descriptorSet == rhs.descriptorSet + && lhs.members == rhs.members; +} + QT_END_NAMESPACE diff --git a/src/gui/rhi/qshaderdescription_p.h b/src/gui/rhi/qshaderdescription_p.h index 5a63b998cd..e02a53dcb5 100644 --- a/src/gui/rhi/qshaderdescription_p.h +++ b/src/gui/rhi/qshaderdescription_p.h @@ -69,9 +69,11 @@ public: bool isValid() const; QByteArray toBinaryJson() const; + QByteArray toCbor() const; QByteArray toJson() const; static QShaderDescription fromBinaryJson(const QByteArray &data); + static QShaderDescription fromCbor(const QByteArray &data); enum VariableType { Unknown = 0, @@ -263,6 +265,7 @@ private: #ifndef QT_NO_DEBUG_STREAM friend Q_GUI_EXPORT QDebug operator<<(QDebug, const QShaderDescription &); #endif + friend Q_GUI_EXPORT bool operator==(const QShaderDescription &lhs, const QShaderDescription &rhs) Q_DECL_NOTHROW; }; Q_DECLARE_OPERATORS_FOR_FLAGS(QShaderDescription::ImageFlags) @@ -276,6 +279,43 @@ Q_GUI_EXPORT QDebug operator<<(QDebug, const QShaderDescription::PushConstantBlo Q_GUI_EXPORT QDebug operator<<(QDebug, const QShaderDescription::StorageBlock &); #endif +Q_GUI_EXPORT bool operator==(const QShaderDescription &lhs, const QShaderDescription &rhs) Q_DECL_NOTHROW; +Q_GUI_EXPORT bool operator==(const QShaderDescription::InOutVariable &lhs, const QShaderDescription::InOutVariable &rhs) Q_DECL_NOTHROW; +Q_GUI_EXPORT bool operator==(const QShaderDescription::BlockVariable &lhs, const QShaderDescription::BlockVariable &rhs) Q_DECL_NOTHROW; +Q_GUI_EXPORT bool operator==(const QShaderDescription::UniformBlock &lhs, const QShaderDescription::UniformBlock &rhs) Q_DECL_NOTHROW; +Q_GUI_EXPORT bool operator==(const QShaderDescription::PushConstantBlock &lhs, const QShaderDescription::PushConstantBlock &rhs) Q_DECL_NOTHROW; +Q_GUI_EXPORT bool operator==(const QShaderDescription::StorageBlock &lhs, const QShaderDescription::StorageBlock &rhs) Q_DECL_NOTHROW; + +inline bool operator!=(const QShaderDescription &lhs, const QShaderDescription &rhs) Q_DECL_NOTHROW +{ + return !(lhs == rhs); +} + +inline bool operator!=(const QShaderDescription::InOutVariable &lhs, const QShaderDescription::InOutVariable &rhs) Q_DECL_NOTHROW +{ + return !(lhs == rhs); +} + +inline bool operator!=(const QShaderDescription::BlockVariable &lhs, const QShaderDescription::BlockVariable &rhs) Q_DECL_NOTHROW +{ + return !(lhs == rhs); +} + +inline bool operator!=(const QShaderDescription::UniformBlock &lhs, const QShaderDescription::UniformBlock &rhs) Q_DECL_NOTHROW +{ + return !(lhs == rhs); +} + +inline bool operator!=(const QShaderDescription::PushConstantBlock &lhs, const QShaderDescription::PushConstantBlock &rhs) Q_DECL_NOTHROW +{ + return !(lhs == rhs); +} + +inline bool operator!=(const QShaderDescription::StorageBlock &lhs, const QShaderDescription::StorageBlock &rhs) Q_DECL_NOTHROW +{ + return !(lhs == rhs); +} + QT_END_NAMESPACE #endif diff --git a/tests/auto/gui/rhi/qshader/data/README b/tests/auto/gui/rhi/qshader/data/README new file mode 100644 index 0000000000..3d89f2a0c5 --- /dev/null +++ b/tests/auto/gui/rhi/qshader/data/README @@ -0,0 +1,15 @@ +Warning: Do NOT regenerate the .qsb files without proper planning and understanding +the following. + +Among other things, we are also testing backwards compatibility for QShader +deserialization. + +.qsb files with _v1 in the name were produced with an older qtshadertools +and have a QSB_VERSION of 1. + +Files with _v2 are generated with a newer qsb, those have QSB_VERSION 2. +The difference is the support for nativeResourceBindingMap() which is only +present in v2. + +Files with _v3 come from an even newer qsb, and have QSB_VERSION 3. The +difference to 2 is the use of CBOR instead of binary JSON for QShaderDescription. diff --git a/tests/auto/gui/rhi/qshader/data/color.vert.qsb b/tests/auto/gui/rhi/qshader/data/color.vert.qsb deleted file mode 100644 index 7d02d823d2..0000000000 Binary files a/tests/auto/gui/rhi/qshader/data/color.vert.qsb and /dev/null differ diff --git a/tests/auto/gui/rhi/qshader/data/color_all_v1.vert.qsb b/tests/auto/gui/rhi/qshader/data/color_all_v1.vert.qsb new file mode 100644 index 0000000000..7d02d823d2 Binary files /dev/null and b/tests/auto/gui/rhi/qshader/data/color_all_v1.vert.qsb differ diff --git a/tests/auto/gui/rhi/qshader/data/color_simple.vert.qsb b/tests/auto/gui/rhi/qshader/data/color_simple.vert.qsb deleted file mode 100644 index c82ba7e8e7..0000000000 Binary files a/tests/auto/gui/rhi/qshader/data/color_simple.vert.qsb and /dev/null differ diff --git a/tests/auto/gui/rhi/qshader/data/color_spirv_v1.vert.qsb b/tests/auto/gui/rhi/qshader/data/color_spirv_v1.vert.qsb new file mode 100644 index 0000000000..c82ba7e8e7 Binary files /dev/null and b/tests/auto/gui/rhi/qshader/data/color_spirv_v1.vert.qsb differ diff --git a/tests/auto/gui/rhi/qshader/data/texture.frag b/tests/auto/gui/rhi/qshader/data/texture.frag new file mode 100644 index 0000000000..bd22f817e0 --- /dev/null +++ b/tests/auto/gui/rhi/qshader/data/texture.frag @@ -0,0 +1,16 @@ +#version 440 + +layout(location = 0) in vec2 qt_TexCoord; +layout(location = 0) out vec4 fragColor; + +layout(std140, binding = 0) uniform buf { + mat4 qt_Matrix; + float opacity; +} ubuf; + +layout(binding = 1) uniform sampler2D qt_Texture; + +void main() +{ + fragColor = texture(qt_Texture, qt_TexCoord) * ubuf.opacity; +} diff --git a/tests/auto/gui/rhi/qshader/data/texture_all_v2.frag.qsb b/tests/auto/gui/rhi/qshader/data/texture_all_v2.frag.qsb new file mode 100644 index 0000000000..79f5486945 Binary files /dev/null and b/tests/auto/gui/rhi/qshader/data/texture_all_v2.frag.qsb differ diff --git a/tests/auto/gui/rhi/qshader/data/texture_all_v3.frag.qsb b/tests/auto/gui/rhi/qshader/data/texture_all_v3.frag.qsb new file mode 100644 index 0000000000..b6e49aa03d Binary files /dev/null and b/tests/auto/gui/rhi/qshader/data/texture_all_v3.frag.qsb differ diff --git a/tests/auto/gui/rhi/qshader/tst_qshader.cpp b/tests/auto/gui/rhi/qshader/tst_qshader.cpp index 21f0cc7895..a0082f1e3b 100644 --- a/tests/auto/gui/rhi/qshader/tst_qshader.cpp +++ b/tests/auto/gui/rhi/qshader/tst_qshader.cpp @@ -40,6 +40,9 @@ private slots: void genVariants(); void shaderDescImplicitSharing(); void bakedShaderImplicitSharing(); + void mslResourceMapping(); + void loadV3(); + void serializeShaderDesc(); }; static QShader getShader(const QString &name) @@ -53,8 +56,9 @@ static QShader getShader(const QString &name) void tst_QShader::simpleCompileCheckResults() { - QShader s = getShader(QLatin1String(":/data/color_simple.vert.qsb")); + QShader s = getShader(QLatin1String(":/data/color_spirv_v1.vert.qsb")); QVERIFY(s.isValid()); + QCOMPARE(QShaderPrivate::get(&s)->qsbVersion, 1); QCOMPARE(s.availableShaders().count(), 1); const QShaderCode shader = s.shader(QShaderKey(QShader::SpirvShader, @@ -125,10 +129,11 @@ void tst_QShader::simpleCompileCheckResults() void tst_QShader::genVariants() { - QShader s = getShader(QLatin1String(":/data/color.vert.qsb")); + QShader s = getShader(QLatin1String(":/data/color_all_v1.vert.qsb")); // spirv, glsl 100, glsl 330, glsl 120, hlsl 50, msl 12 // + batchable variants QVERIFY(s.isValid()); + QCOMPARE(QShaderPrivate::get(&s)->qsbVersion, 1); QCOMPARE(s.availableShaders().count(), 2 * 6); int batchableVariantCount = 0; @@ -149,8 +154,9 @@ void tst_QShader::genVariants() void tst_QShader::shaderDescImplicitSharing() { - QShader s = getShader(QLatin1String(":/data/color_simple.vert.qsb")); + QShader s = getShader(QLatin1String(":/data/color_spirv_v1.vert.qsb")); QVERIFY(s.isValid()); + QCOMPARE(QShaderPrivate::get(&s)->qsbVersion, 1); QCOMPARE(s.availableShaders().count(), 1); QVERIFY(s.availableShaders().contains(QShaderKey(QShader::SpirvShader, QShaderVersion(100)))); @@ -168,6 +174,7 @@ void tst_QShader::shaderDescImplicitSharing() QCOMPARE(d1.inputVariables().count(), 2); QCOMPARE(d1.outputVariables().count(), 1); QCOMPARE(d1.uniformBlocks().count(), 1); + QCOMPARE(d0, d1); d1.detach(); QVERIFY(QShaderDescriptionPrivate::get(&d0) != QShaderDescriptionPrivate::get(&d1)); @@ -177,12 +184,17 @@ void tst_QShader::shaderDescImplicitSharing() QCOMPARE(d1.inputVariables().count(), 2); QCOMPARE(d1.outputVariables().count(), 1); QCOMPARE(d1.uniformBlocks().count(), 1); + QCOMPARE(d0, d1); + + d1 = QShaderDescription(); + QVERIFY(d0 != d1); } void tst_QShader::bakedShaderImplicitSharing() { - QShader s0 = getShader(QLatin1String(":/data/color_simple.vert.qsb")); + QShader s0 = getShader(QLatin1String(":/data/color_spirv_v1.vert.qsb")); QVERIFY(s0.isValid()); + QCOMPARE(QShaderPrivate::get(&s0)->qsbVersion, 1); QCOMPARE(s0.availableShaders().count(), 1); QVERIFY(s0.availableShaders().contains(QShaderKey(QShader::SpirvShader, QShaderVersion(100)))); @@ -229,5 +241,159 @@ void tst_QShader::bakedShaderImplicitSharing() } } +void tst_QShader::mslResourceMapping() +{ + QShader s = getShader(QLatin1String(":/data/texture_all_v2.frag.qsb")); + QVERIFY(s.isValid()); + QCOMPARE(QShaderPrivate::get(&s)->qsbVersion, 2); + + const QVector availableShaders = s.availableShaders(); + QCOMPARE(availableShaders.count(), 7); + QVERIFY(availableShaders.contains(QShaderKey(QShader::SpirvShader, QShaderVersion(100)))); + QVERIFY(availableShaders.contains(QShaderKey(QShader::MslShader, QShaderVersion(12)))); + QVERIFY(availableShaders.contains(QShaderKey(QShader::HlslShader, QShaderVersion(50)))); + QVERIFY(availableShaders.contains(QShaderKey(QShader::GlslShader, QShaderVersion(100, QShaderVersion::GlslEs)))); + QVERIFY(availableShaders.contains(QShaderKey(QShader::GlslShader, QShaderVersion(120)))); + QVERIFY(availableShaders.contains(QShaderKey(QShader::GlslShader, QShaderVersion(150)))); + QVERIFY(availableShaders.contains(QShaderKey(QShader::GlslShader, QShaderVersion(330)))); + + const QShader::NativeResourceBindingMap *resMap = + s.nativeResourceBindingMap(QShaderKey(QShader::GlslShader, QShaderVersion(330))); + QVERIFY(!resMap); + + // The Metal shader must come with a mapping table for binding points 0 + // (uniform buffer) and 1 (combined image sampler mapped to a texture and + // sampler in the shader). + resMap = s.nativeResourceBindingMap(QShaderKey(QShader::MslShader, QShaderVersion(12))); + QVERIFY(resMap); + + QCOMPARE(resMap->count(), 2); + QCOMPARE(resMap->value(0).first, 0); // mapped to native buffer index 0 + QCOMPARE(resMap->value(1), qMakePair(0, 0)); // mapped to native texture index 0 and sampler index 0 +} + +void tst_QShader::loadV3() +{ + // qsb version 3: QShaderDescription is serialized as CBOR. Ensure the deserialized data is as expected. + QShader s = getShader(QLatin1String(":/data/texture_all_v3.frag.qsb")); + QVERIFY(s.isValid()); + QCOMPARE(QShaderPrivate::get(&s)->qsbVersion, 3); + + const QVector availableShaders = s.availableShaders(); + QCOMPARE(availableShaders.count(), 7); + QVERIFY(availableShaders.contains(QShaderKey(QShader::SpirvShader, QShaderVersion(100)))); + QVERIFY(availableShaders.contains(QShaderKey(QShader::MslShader, QShaderVersion(12)))); + QVERIFY(availableShaders.contains(QShaderKey(QShader::HlslShader, QShaderVersion(50)))); + QVERIFY(availableShaders.contains(QShaderKey(QShader::GlslShader, QShaderVersion(100, QShaderVersion::GlslEs)))); + QVERIFY(availableShaders.contains(QShaderKey(QShader::GlslShader, QShaderVersion(120)))); + QVERIFY(availableShaders.contains(QShaderKey(QShader::GlslShader, QShaderVersion(150)))); + QVERIFY(availableShaders.contains(QShaderKey(QShader::GlslShader, QShaderVersion(330)))); + + const QShaderDescription desc = s.description(); + QVERIFY(desc.isValid()); + QCOMPARE(desc.inputVariables().count(), 1); + for (const QShaderDescription::InOutVariable &v : desc.inputVariables()) { + switch (v.location) { + case 0: + QCOMPARE(v.name, QLatin1String("qt_TexCoord")); + QCOMPARE(v.type, QShaderDescription::Vec2); + break; + default: + QVERIFY(false); + break; + } + } + QCOMPARE(desc.outputVariables().count(), 1); + for (const QShaderDescription::InOutVariable &v : desc.outputVariables()) { + switch (v.location) { + case 0: + QCOMPARE(v.name, QLatin1String("fragColor")); + QCOMPARE(v.type, QShaderDescription::Vec4); + break; + default: + QVERIFY(false); + break; + } + } + QCOMPARE(desc.uniformBlocks().count(), 1); + const QShaderDescription::UniformBlock blk = desc.uniformBlocks().first(); + QCOMPARE(blk.blockName, QLatin1String("buf")); + QCOMPARE(blk.structName, QLatin1String("ubuf")); + QCOMPARE(blk.size, 68); + QCOMPARE(blk.binding, 0); + QCOMPARE(blk.descriptorSet, 0); + QCOMPARE(blk.members.count(), 2); + for (int i = 0; i < blk.members.count(); ++i) { + const QShaderDescription::BlockVariable v = blk.members[i]; + switch (i) { + case 0: + QCOMPARE(v.offset, 0); + QCOMPARE(v.size, 64); + QCOMPARE(v.name, QLatin1String("qt_Matrix")); + QCOMPARE(v.type, QShaderDescription::Mat4); + QCOMPARE(v.matrixStride, 16); + break; + case 1: + QCOMPARE(v.offset, 64); + QCOMPARE(v.size, 4); + QCOMPARE(v.name, QLatin1String("opacity")); + QCOMPARE(v.type, QShaderDescription::Float); + break; + default: + QVERIFY(false); + break; + } + } +} + +void tst_QShader::serializeShaderDesc() +{ + // default constructed QShaderDescription + { + QShaderDescription desc; + QVERIFY(!desc.isValid()); + + const QByteArray data = desc.toCbor(); + QVERIFY(!data.isEmpty()); + + QShaderDescription desc2 = QShaderDescription::fromCbor(data); + QVERIFY(!desc2.isValid()); + } + + // a QShaderDescription with inputs, outputs, uniform block and combined image sampler + { + QShader s = getShader(QLatin1String(":/data/texture_all_v3.frag.qsb")); + QVERIFY(s.isValid()); + const QShaderDescription desc = s.description(); + QVERIFY(desc.isValid()); + + const QByteArray data = desc.toCbor(); + QVERIFY(!data.isEmpty()); + + QShaderDescription desc2; + QVERIFY(!desc2.isValid()); + QVERIFY(!(desc == desc2)); + QVERIFY(desc != desc2); + + desc2 = QShaderDescription::fromCbor(data); + QVERIFY(desc2.isValid()); + QCOMPARE(desc, desc2); + } + + // exercise QShader and QShaderDescription comparisons + { + QShader s1 = getShader(QLatin1String(":/data/texture_all_v3.frag.qsb")); + QVERIFY(s1.isValid()); + QShader s2 = getShader(QLatin1String(":/data/color_all_v1.vert.qsb")); + QVERIFY(s2.isValid()); + + QVERIFY(s1.description().isValid()); + QVERIFY(s2.description().isValid()); + + QVERIFY(s1 != s2); + QVERIFY(s1.description() != s2.description()); + } +} + #include QTEST_MAIN(tst_QShader) -- cgit v1.2.3 From b56e856d218976bf39d981468267337d8d6223f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Morten=20Johan=20S=C3=B8rvig?= Date: Mon, 18 Mar 2019 12:25:14 +0100 Subject: Cocoa: rename IsMouseOrKeyEvent -> isUserInputEvent This matches the intended use of this function. Reformat to modern Qt style. Change-Id: I076d2bdb3ac14b346f0dc6934f7a47765badc6b0 Reviewed-by: Timur Pocheptsov --- .../platforms/cocoa/qcocoaeventdispatcher.mm | 68 ++++++++++------------ 1 file changed, 32 insertions(+), 36 deletions(-) diff --git a/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm b/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm index e87fc39c42..b3ce9e45dc 100644 --- a/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm +++ b/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm @@ -293,46 +293,42 @@ bool QCocoaEventDispatcher::hasPendingEvents() return qGlobalPostedEventsCount() || (qt_is_gui_used && !CFRunLoopIsWaiting(CFRunLoopGetMain())); } -static bool IsMouseOrKeyEvent( NSEvent* event ) +static bool isUserInputEvent(NSEvent* event) { - bool result = false; - - switch( [event type] ) - { - case NSEventTypeLeftMouseDown: - case NSEventTypeLeftMouseUp: - case NSEventTypeRightMouseDown: - case NSEventTypeRightMouseUp: - case NSEventTypeMouseMoved: // ?? - case NSEventTypeLeftMouseDragged: - case NSEventTypeRightMouseDragged: - case NSEventTypeMouseEntered: - case NSEventTypeMouseExited: - case NSEventTypeKeyDown: - case NSEventTypeKeyUp: - case NSEventTypeFlagsChanged: // key modifiers changed? - case NSEventTypeCursorUpdate: // ?? - case NSEventTypeScrollWheel: - case NSEventTypeTabletPoint: - case NSEventTypeTabletProximity: - case NSEventTypeOtherMouseDown: - case NSEventTypeOtherMouseUp: - case NSEventTypeOtherMouseDragged: + switch ([event type]) { + case NSEventTypeLeftMouseDown: + case NSEventTypeLeftMouseUp: + case NSEventTypeRightMouseDown: + case NSEventTypeRightMouseUp: + case NSEventTypeMouseMoved: // ?? + case NSEventTypeLeftMouseDragged: + case NSEventTypeRightMouseDragged: + case NSEventTypeMouseEntered: + case NSEventTypeMouseExited: + case NSEventTypeKeyDown: + case NSEventTypeKeyUp: + case NSEventTypeFlagsChanged: // key modifiers changed? + case NSEventTypeCursorUpdate: // ?? + case NSEventTypeScrollWheel: + case NSEventTypeTabletPoint: + case NSEventTypeTabletProximity: + case NSEventTypeOtherMouseDown: + case NSEventTypeOtherMouseUp: + case NSEventTypeOtherMouseDragged: #ifndef QT_NO_GESTURES - case NSEventTypeGesture: // touch events - case NSEventTypeMagnify: - case NSEventTypeSwipe: - case NSEventTypeRotate: - case NSEventTypeBeginGesture: - case NSEventTypeEndGesture: + case NSEventTypeGesture: // touch events + case NSEventTypeMagnify: + case NSEventTypeSwipe: + case NSEventTypeRotate: + case NSEventTypeBeginGesture: + case NSEventTypeEndGesture: #endif // QT_NO_GESTURES - result = true; + return true; break; - - default: + default: break; } - return result; + return false; } static inline void qt_mac_waitForMoreEvents(NSString *runLoopMode = NSDefaultRunLoopMode) @@ -465,7 +461,7 @@ bool QCocoaEventDispatcher::processEvents(QEventLoop::ProcessEventsFlags flags) dequeue: YES]; if (event) { - if (IsMouseOrKeyEvent(event)) { + if (isUserInputEvent(event)) { [event retain]; d->queuedUserInputEvents.append(event); continue; @@ -485,7 +481,7 @@ bool QCocoaEventDispatcher::processEvents(QEventLoop::ProcessEventsFlags flags) if (event) { if (flags & QEventLoop::ExcludeUserInputEvents) { - if (IsMouseOrKeyEvent(event)) { + if (isUserInputEvent(event)) { [event retain]; d->queuedUserInputEvents.append(event); continue; -- cgit v1.2.3