Merge remote-tracking branch 'origin/5.15' into dev

Conflicts:
	tests/benchmarks/network/access/qnetworkreply/tst_qnetworkreply.cpp
	tests/auto/network/access/spdy/tst_spdy.cpp

Change-Id: I3196c5f7b34f2ffc9ef1e690d02d5b9bb3270a74
This commit is contained in:
Qt Forward Merge Bot 2020-01-15 01:00:59 +01:00 committed by Edward Welbourne
commit d14fd32d40
76 changed files with 2545 additions and 841 deletions

View File

@ -144,7 +144,7 @@ public:
private slots:
void handleNetworkData(QNetworkReply *networkReply) {
if (!networkReply->error()) {
if (!networkReply->networkError()) {
if (!mapReplies.contains(networkReply)) {
// Assume UTF-8 encoded
QByteArray data = networkReply->readAll();

View File

@ -162,7 +162,7 @@ void SlippyMap::handleNetworkData(QNetworkReply *reply)
{
QImage img;
QPoint tp = reply->request().attribute(QNetworkRequest::User).toPoint();
if (!reply->error())
if (!reply->networkError())
if (!img.load(reply, 0))
img = QImage();
reply->deleteLater();

View File

@ -175,7 +175,7 @@ void DownloadManager::sslErrors(const QList<QSslError> &sslErrors)
void DownloadManager::downloadFinished(QNetworkReply *reply)
{
QUrl url = reply->url();
if (reply->error()) {
if (reply->networkError()) {
fprintf(stderr, "Download of %s failed: %s\n",
url.toEncoded().constData(),
qPrintable(reply->errorString()));

View File

@ -162,7 +162,7 @@ void DownloadManager::downloadFinished()
progressBar.clear();
output.close();
if (currentDownload->error()) {
if (currentDownload->networkError()) {
// download failed
fprintf(stderr, "Failed: %s\n", qPrintable(currentDownload->errorString()));
output.remove();

View File

@ -209,7 +209,7 @@ void GSuggestCompletion::preventSuggest()
void GSuggestCompletion::handleNetworkData(QNetworkReply *networkReply)
{
QUrl url = networkReply->url();
if (networkReply->error() == QNetworkReply::NoError) {
if (networkReply->networkError() == QNetworkReply::NoError) {
QVector<QString> choices;
QByteArray response(networkReply->readAll());

View File

@ -249,7 +249,7 @@ void HttpWindow::httpFinished()
return;
}
if (reply->error()) {
if (reply->networkError()) {
QFile::remove(fi.absoluteFilePath());
statusLabel->setText(tr("Download failed:\n%1.").arg(reply->errorString()));
downloadButton->setEnabled(true);

View File

@ -29,7 +29,7 @@ qtHaveModule(widgets) {
}
qtConfig(openssl): SUBDIRS += securesocketclient
qtConfig(ssl): SUBDIRS += securesocketclient
qtConfig(dtls): SUBDIRS += secureudpserver secureudpclient
qtConfig(sctp): SUBDIRS += multistreamserver multistreamclient
}

View File

@ -165,8 +165,8 @@ void TrackerClient::httpRequestDone(QNetworkReply *reply)
return;
}
if (reply->error() != QNetworkReply::NoError) {
emit connectionError(reply->error());
if (reply->networkError() != QNetworkReply::NoError) {
emit connectionError(reply->networkError());
return;
}

View File

@ -0,0 +1,36 @@
/****************************************************************************
**
** Copyright (C) 2020 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the documentation of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:FDL$
** 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 Free Documentation License Usage
** Alternatively, this file may be used under the terms of the GNU Free
** Documentation License version 1.3 as published by the Free Software
** Foundation and appearing in the file included in the packaging of
** this file. Please review the following information to ensure
** the GNU Free Documentation License version 1.3 requirements
** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
** $QT_END_LICENSE$
**
****************************************************************************/
/*!
\example widgets/gallery
\title Widgets Gallery Example
\ingroup examples-widgets
\brief The Widgets Gallery example shows widgets relevant for designing UIs.
This example demonstrates widgets typically used in dialogs and forms.
It also allows for changing the style.
*/

View File

@ -75,8 +75,6 @@
WidgetGallery::WidgetGallery(QWidget *parent)
: QDialog(parent)
{
originalPalette = QApplication::palette();
styleComboBox = new QComboBox;
const QString defaultStyleName = QApplication::style()->objectName();
QStringList styleNames = QStyleFactory::keys();
@ -162,10 +160,8 @@ void WidgetGallery::changeStyle(const QString &styleName)
void WidgetGallery::changePalette()
//! [7] //! [8]
{
if (useStylePaletteCheckBox->isChecked())
QApplication::setPalette(QApplication::style()->standardPalette());
else
QApplication::setPalette(originalPalette);
QApplication::setPalette(useStylePaletteCheckBox->isChecked() ?
QApplication::style()->standardPalette() : QPalette());
}
//! [8]

View File

@ -96,8 +96,6 @@ private:
void createBottomRightGroupBox();
void createProgressBar();
QPalette originalPalette;
QLabel *styleLabel;
QComboBox *styleComboBox;
QCheckBox *useStylePaletteCheckBox;

View File

@ -139,10 +139,12 @@ Q_CORE_EXPORT void *qMemSet(void *dest, int c, size_t n);
// in. The idea here is to error or warn if otherwise implicit Qt
// assumptions are not fulfilled on new hardware or compilers
// (if this list becomes too long, consider factoring into a separate file)
Q_STATIC_ASSERT_X(sizeof(int) == 4, "Qt assumes that int is 32 bits");
Q_STATIC_ASSERT_X(UCHAR_MAX == 255, "Qt assumes that char is 8 bits");
Q_STATIC_ASSERT_X(sizeof(int) == 4, "Qt assumes that int is 32 bits");
Q_STATIC_ASSERT_X(QT_POINTER_SIZE == sizeof(void *), "QT_POINTER_SIZE defined incorrectly");
Q_STATIC_ASSERT_X(sizeof(float) == 4, "Qt assumes that float is 32 bits");
Q_STATIC_ASSERT_X(sizeof(char16_t) == 2, "Qt assumes that char16_t is 16 bits");
Q_STATIC_ASSERT_X(sizeof(char32_t) == 4, "Qt assumes that char32_t is 32 bits");
// While we'd like to check for __STDC_IEC_559__, as per ISO/IEC 9899:2011
// Annex F (C11, normative for C++11), there are a few corner cases regarding

View File

@ -403,6 +403,7 @@ QStringList QWindowsFileSystemWatcherEngine::addPaths(const QStringList &paths,
const QString absolutePath = isDir ? fileInfo.absoluteFilePath() : fileInfo.absolutePath();
const uint flags = isDir
? (FILE_NOTIFY_CHANGE_DIR_NAME
| FILE_NOTIFY_CHANGE_ATTRIBUTES
| FILE_NOTIFY_CHANGE_FILE_NAME)
: (FILE_NOTIFY_CHANGE_DIR_NAME
| FILE_NOTIFY_CHANGE_FILE_NAME

View File

@ -118,7 +118,7 @@ public:
int remainingTime(int timerId) final;
void wakeUp() final;
void wakeUp() override;
void interrupt() final;
void flush() override;

View File

@ -463,10 +463,10 @@ QT_BEGIN_NAMESPACE
\c{\xHHHH} with more than 2 digits. A pattern like \c{\x2022} neeeds to
be ported to \c{\x{2022}}, or it will match a space (\c{0x20}) followed
by the string \c{"22"}. In general, it is highly recommended to always use
curly braces with the \c{\\x} escape, no matter the amount of digits
curly braces with the \c{\x} escape, no matter the amount of digits
specified.
\li A 0-to-n quantification like \c{{,n}} needs to be ported to c{{0,n}} to
\li A 0-to-n quantification like \c{{,n}} needs to be ported to \c{{0,n}} to
preserve semantics. Otherwise, a pattern such as \c{\d{,3}} would
actually match a digit followed by the exact string \c{"{,3}"}.

View File

@ -5036,21 +5036,25 @@ bool QString::endsWith(QChar c, Qt::CaseSensitivity cs) const
}
/*!
Returns \c true if the string only contains uppercase letters,
otherwise returns \c false.
Returns \c true if the string is uppercase, that is, it's identical
to its toUpper() folding.
Note that this does \e not mean that the string does not contain
lowercase letters (some lowercase letters do not have a uppercase
folding; they are left unchanged by toUpper()).
For more information, refer to the Unicode standard, section 3.13.
\since 5.12
\sa QChar::isUpper(), isLower()
\sa QChar::toUpper(), isLower()
*/
bool QString::isUpper() const
{
if (isEmpty())
return false;
QStringIterator it(*this);
const QChar *d = data();
for (int i = 0, max = size(); i < max; ++i) {
if (!d[i].isUpper())
while (it.hasNext()) {
uint uc = it.nextUnchecked();
if (qGetProp(uc)->cases[QUnicodeTables::UpperCase].diff)
return false;
}
@ -5058,21 +5062,25 @@ bool QString::isUpper() const
}
/*!
Returns \c true if the string only contains lowercase letters,
otherwise returns \c false.
Returns \c true if the string is lowercase, that is, it's identical
to its toLower() folding.
Note that this does \e not mean that the string does not contain
uppercase letters (some uppercase letters do not have a lowercase
folding; they are left unchanged by toLower()).
For more information, refer to the Unicode standard, section 3.13.
\since 5.12
\sa QChar::isLower(), isUpper()
\sa QChar::toLower(), isUpper()
*/
bool QString::isLower() const
{
if (isEmpty())
return false;
QStringIterator it(*this);
const QChar *d = data();
for (int i = 0, max = size(); i < max; ++i) {
if (!d[i].isLower())
while (it.hasNext()) {
uint uc = it.nextUnchecked();
if (qGetProp(uc)->cases[QUnicodeTables::LowerCase].diff)
return false;
}

View File

@ -64,6 +64,7 @@ public:
{
*this = reply;
}
inline QDBusReply(const QDBusReply &) = default;
inline QDBusReply& operator=(const QDBusMessage &reply)
{
QVariant data(qMetaTypeId<Type>(), nullptr);

View File

@ -236,21 +236,6 @@ static bool qt_detectRTLLanguage()
" and Arabic) to get proper widget layout.") == QLatin1String("RTL"));
}
static void initPalette()
{
if (!QGuiApplicationPrivate::app_pal)
if (const QPalette *themePalette = QGuiApplicationPrivate::platformTheme()->palette())
QGuiApplicationPrivate::app_pal = new QPalette(*themePalette);
if (!QGuiApplicationPrivate::app_pal)
QGuiApplicationPrivate::app_pal = new QPalette(Qt::gray);
}
static inline void clearPalette()
{
delete QGuiApplicationPrivate::app_pal;
QGuiApplicationPrivate::app_pal = nullptr;
}
static void initFontUnlocked()
{
if (!QGuiApplicationPrivate::app_font) {
@ -605,8 +590,13 @@ static QWindowGeometrySpecification windowGeometrySpecification = Q_WINDOW_GEOME
The following parameters are available for \c {-platform windows}:
\list
\li \c {altgr}, detect the key \c {AltGr} found on some keyboards as
Qt::GroupSwitchModifier (since Qt 5.12).
\li \c {dialogs=[xp|none]}, \c xp uses XP-style native dialogs and
\c none disables them.
\li \c {dpiawareness=[0|1|2} Sets the DPI awareness of the process
(see \l{High DPI Displays}, since Qt 5.4).
\li \c {fontengine=freetype}, uses the FreeType font engine.
\li \c {menus=[native|none]}, controls the use of native menus.
@ -616,10 +606,23 @@ static QWindowGeometrySpecification windowGeometrySpecification = Q_WINDOW_GEOME
provide hover signals. They are mainly intended for Qt Quick.
By default, they will be used if the application is not an
instance of QApplication or for Qt Quick Controls 2
applications.
applications (since Qt 5.10).
\li \c {altgr}, detect the key \c {AltGr} found on some keyboards as
Qt::GroupSwitchModifier.
\li \c {nocolorfonts} Turn off DirectWrite Color fonts
(since Qt 5.8).
\li \c {nodirectwrite} Turn off DirectWrite fonts (since Qt 5.8).
\li \c {nomousefromtouch} Ignores mouse events synthesized
from touch events by the operating system.
\li \c {nowmpointer} Switches from Pointer Input Messages handling
to legacy mouse handling (since Qt 5.12).
\li \c {reverse} Activates Right-to-left mode (experimental).
Windows title bars will be shown accordingly in Right-to-left locales
(since Qt 5.13).
\li \c {tabletabsoluterange=<value>} Sets a value for mouse mode detection
of WinTab tablets (Legacy, since Qt 5.3).
\endlist
The following parameter is available for \c {-platform cocoa} (on macOS):
@ -673,7 +676,7 @@ QGuiApplication::~QGuiApplication()
d->session_manager = nullptr;
#endif //QT_NO_SESSIONMANAGER
clearPalette();
QGuiApplicationPrivate::clearPalette();
QFontDatabase::removeAllApplicationFonts();
#ifndef QT_NO_CURSOR
@ -1597,7 +1600,7 @@ void QGuiApplicationPrivate::init()
if (platform_integration == nullptr)
createPlatformIntegration();
initPalette();
updatePalette();
QFont::initialize();
initThemeHints();
@ -3275,46 +3278,97 @@ QClipboard * QGuiApplication::clipboard()
*/
/*!
Returns the default application palette.
Returns the current application palette.
Roles that have not been explicitly set will reflect the system's platform theme.
\sa setPalette()
*/
QPalette QGuiApplication::palette()
{
initPalette();
if (!QGuiApplicationPrivate::app_pal)
QGuiApplicationPrivate::updatePalette();
return *QGuiApplicationPrivate::app_pal;
}
void QGuiApplicationPrivate::updatePalette()
{
if (app_pal) {
if (setPalette(*app_pal) && qGuiApp)
qGuiApp->d_func()->handlePaletteChanged();
} else {
setPalette(QPalette());
}
}
void QGuiApplicationPrivate::clearPalette()
{
delete app_pal;
app_pal = nullptr;
}
/*!
Changes the default application palette to \a pal.
Changes the application palette to \a pal.
The color roles from this palette are combined with the system's platform
theme to form the application's final palette.
\sa palette()
*/
void QGuiApplication::setPalette(const QPalette &pal)
{
if (!QGuiApplicationPrivate::setPalette(pal))
return;
QCoreApplication::setAttribute(Qt::AA_SetPalette);
if (qGuiApp)
qGuiApp->d_func()->sendApplicationPaletteChange();
if (QGuiApplicationPrivate::setPalette(pal) && qGuiApp)
qGuiApp->d_func()->handlePaletteChanged();
}
bool QGuiApplicationPrivate::setPalette(const QPalette &palette)
{
if (app_pal && palette.isCopyOf(*app_pal))
// Resolve the palette against the theme palette, filling in
// any missing roles, while keeping the original resolve mask.
QPalette basePalette = qGuiApp ? qGuiApp->d_func()->basePalette() : Qt::gray;
basePalette.resolve(0); // The base palette only contributes missing colors roles
QPalette resolvedPalette = palette.resolve(basePalette);
if (app_pal && resolvedPalette == *app_pal && resolvedPalette.resolve() == app_pal->resolve())
return false;
if (!app_pal)
app_pal = new QPalette(palette);
app_pal = new QPalette(resolvedPalette);
else
*app_pal = palette;
*app_pal = resolvedPalette;
QCoreApplication::setAttribute(Qt::AA_SetPalette, app_pal->resolve() != 0);
return true;
}
/*
Returns the base palette used to fill in missing roles in
the current application palette.
Normally this is the theme palette, but QApplication
overrides this for compatibility reasons.
*/
QPalette QGuiApplicationPrivate::basePalette() const
{
return platformTheme() ? *platformTheme()->palette() : Qt::gray;
}
void QGuiApplicationPrivate::handlePaletteChanged(const char *className)
{
if (!className) {
Q_ASSERT(app_pal);
emit qGuiApp->paletteChanged(*QGuiApplicationPrivate::app_pal);
}
if (is_app_running && !is_app_closing) {
QEvent event(QEvent::ApplicationPaletteChange);
QGuiApplication::sendEvent(qGuiApp, &event);
}
}
void QGuiApplicationPrivate::applyWindowGeometrySpecificationTo(QWindow *window)
{
windowGeometrySpecification.applyTo(window);
@ -4113,11 +4167,8 @@ QPixmap QGuiApplicationPrivate::getPixmapCursor(Qt::CursorShape cshape)
void QGuiApplicationPrivate::notifyThemeChanged()
{
if (!testAttribute(Qt::AA_SetPalette)) {
clearPalette();
initPalette();
sendApplicationPaletteChange();
}
updatePalette();
if (!(applicationResourceFlags & ApplicationFontExplicitlySet)) {
const auto locker = qt_scoped_lock(applicationFontMutex);
clearFontUnlocked();
@ -4126,20 +4177,6 @@ void QGuiApplicationPrivate::notifyThemeChanged()
initThemeHints();
}
void QGuiApplicationPrivate::sendApplicationPaletteChange(bool toAllWidgets, const char *className)
{
Q_UNUSED(toAllWidgets)
if (!className)
emit qGuiApp->paletteChanged(*QGuiApplicationPrivate::app_pal);
if (!is_app_running || is_app_closing)
return;
QEvent event(QEvent::ApplicationPaletteChange);
QGuiApplication::sendEvent(QGuiApplication::instance(), &event);
}
#if QT_CONFIG(draganddrop)
void QGuiApplicationPrivate::notifyDragStarted(const QDrag *drag)
{

View File

@ -326,17 +326,22 @@ public:
static void resetCachedDevicePixelRatio();
static bool setPalette(const QPalette &palette);
protected:
virtual void notifyThemeChanged();
virtual void sendApplicationPaletteChange(bool toAllWidgets = false, const char *className = nullptr);
static bool setPalette(const QPalette &palette);
virtual QPalette basePalette() const;
virtual void handlePaletteChanged(const char *className = nullptr);
bool tryCloseRemainingWindows(QWindowList processedWindows);
#if QT_CONFIG(draganddrop)
virtual void notifyDragStarted(const QDrag *);
#endif // QT_CONFIG(draganddrop)
private:
static void clearPalette();
static void updatePalette();
friend class QDragManager;
static QGuiApplicationPrivate *self;

View File

@ -269,6 +269,14 @@ QT_BEGIN_NAMESPACE
#define GL_ALL_BARRIER_BITS 0xFFFFFFFF
#endif
#ifndef GL_SHADER_IMAGE_ACCESS_BARRIER_BIT
#define GL_SHADER_IMAGE_ACCESS_BARRIER_BIT 0x00000020
#endif
#ifndef GL_SHADER_STORAGE_BARRIER_BIT
#define GL_SHADER_STORAGE_BARRIER_BIT 0x00002000
#endif
#ifndef GL_VERTEX_PROGRAM_POINT_SIZE
#define GL_VERTEX_PROGRAM_POINT_SIZE 0x8642
#endif
@ -1307,6 +1315,21 @@ QRhi::FrameOpResult QRhiGles2::finish()
return QRhi::FrameOpSuccess;
}
static bool bufferAccessIsWrite(QGles2Buffer::Access access)
{
return access == QGles2Buffer::AccessStorageWrite
|| access == QGles2Buffer::AccessStorageReadWrite
|| access == QGles2Buffer::AccessUpdate;
}
static bool textureAccessIsWrite(QGles2Texture::Access access)
{
return access == QGles2Texture::AccessStorageWrite
|| access == QGles2Texture::AccessStorageReadWrite
|| access == QGles2Texture::AccessUpdate
|| access == QGles2Texture::AccessFramebuffer;
}
void QRhiGles2::trackedBufferBarrier(QGles2CommandBuffer *cbD, QGles2Buffer *bufD, QGles2Buffer::Access access)
{
Q_ASSERT(cbD->recordingPass == QGles2CommandBuffer::NoPass); // this is for resource updates only
@ -1314,7 +1337,7 @@ void QRhiGles2::trackedBufferBarrier(QGles2CommandBuffer *cbD, QGles2Buffer *buf
if (access == prevAccess)
return;
if (prevAccess == QGles2Buffer::AccessStorageWrite || prevAccess == QGles2Buffer::AccessStorageReadWrite) {
if (bufferAccessIsWrite(prevAccess)) {
// Generating the minimal barrier set is way too complicated to do
// correctly (prevAccess is overwritten so we won't have proper
// tracking across multiple passes) so setting all barrier bits will do
@ -1335,7 +1358,7 @@ void QRhiGles2::trackedImageBarrier(QGles2CommandBuffer *cbD, QGles2Texture *tex
if (access == prevAccess)
return;
if (prevAccess == QGles2Texture::AccessStorageWrite || prevAccess == QGles2Texture::AccessStorageReadWrite) {
if (textureAccessIsWrite(prevAccess)) {
QGles2CommandBuffer::Command cmd;
cmd.cmd = QGles2CommandBuffer::Command::Barrier;
cmd.args.barrier.barriers = GL_ALL_BARRIER_BITS;
@ -2273,26 +2296,21 @@ void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb)
// subsequent pass.
for (auto it = tracker.cbeginBuffers(), itEnd = tracker.cendBuffers(); it != itEnd; ++it) {
QGles2Buffer::Access accessBeforePass = QGles2Buffer::Access(it->stateAtPassBegin.access);
if (accessBeforePass == QGles2Buffer::AccessStorageWrite
|| accessBeforePass == QGles2Buffer::AccessStorageReadWrite)
{
if (bufferAccessIsWrite(accessBeforePass))
barriers |= GL_ALL_BARRIER_BITS;
}
}
for (auto it = tracker.cbeginTextures(), itEnd = tracker.cendTextures(); it != itEnd; ++it) {
QGles2Texture::Access accessBeforePass = QGles2Texture::Access(it->stateAtPassBegin.access);
if (accessBeforePass == QGles2Texture::AccessStorageWrite
|| accessBeforePass == QGles2Texture::AccessStorageReadWrite)
{
if (textureAccessIsWrite(accessBeforePass))
barriers |= GL_ALL_BARRIER_BITS;
}
}
if (barriers)
if (barriers && caps.compute)
f->glMemoryBarrier(barriers);
}
break;
case QGles2CommandBuffer::Command::Barrier:
f->glMemoryBarrier(cmd.args.barrier.barriers);
if (caps.compute)
f->glMemoryBarrier(cmd.args.barrier.barriers);
break;
default:
break;
@ -2791,6 +2809,8 @@ void QRhiGles2::beginComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch
cbD->recordingPass = QGles2CommandBuffer::ComputePass;
cbD->resetCachedState();
cbD->computePassState.reset();
}
void QRhiGles2::endComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
@ -2823,11 +2843,96 @@ void QRhiGles2::setComputePipeline(QRhiCommandBuffer *cb, QRhiComputePipeline *p
}
}
template<typename T>
inline void qrhigl_accumulateComputeResource(T *writtenResources, QRhiResource *resource,
QRhiShaderResourceBinding::Type bindingType,
int loadTypeVal, int storeTypeVal, int loadStoreTypeVal)
{
int access = 0;
if (bindingType == loadTypeVal) {
access = QGles2CommandBuffer::ComputePassState::Read;
} else {
access = QGles2CommandBuffer::ComputePassState::Write;
if (bindingType == loadStoreTypeVal)
access |= QGles2CommandBuffer::ComputePassState::Read;
}
auto it = writtenResources->find(resource);
if (it != writtenResources->end())
it->first |= access;
else if (bindingType == storeTypeVal || bindingType == loadStoreTypeVal)
writtenResources->insert(resource, { access, true });
}
void QRhiGles2::dispatch(QRhiCommandBuffer *cb, int x, int y, int z)
{
QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
Q_ASSERT(cbD->recordingPass == QGles2CommandBuffer::ComputePass);
if (cbD->currentComputeSrb) {
GLbitfield barriers = 0;
// The key in the writtenResources map indicates that the resource was
// written in a previous dispatch, whereas the value accumulates the
// access mask in the current one.
for (auto &accessAndIsNewFlag : cbD->computePassState.writtenResources)
accessAndIsNewFlag = { 0, false };
QGles2ShaderResourceBindings *srbD = QRHI_RES(QGles2ShaderResourceBindings, cbD->currentComputeSrb);
const int bindingCount = srbD->m_bindings.count();
for (int i = 0; i < bindingCount; ++i) {
const QRhiShaderResourceBinding::Data *b = srbD->m_bindings.at(i).data();
switch (b->type) {
case QRhiShaderResourceBinding::ImageLoad:
case QRhiShaderResourceBinding::ImageStore:
case QRhiShaderResourceBinding::ImageLoadStore:
qrhigl_accumulateComputeResource(&cbD->computePassState.writtenResources,
b->u.simage.tex,
b->type,
QRhiShaderResourceBinding::ImageLoad,
QRhiShaderResourceBinding::ImageStore,
QRhiShaderResourceBinding::ImageLoadStore);
break;
case QRhiShaderResourceBinding::BufferLoad:
case QRhiShaderResourceBinding::BufferStore:
case QRhiShaderResourceBinding::BufferLoadStore:
qrhigl_accumulateComputeResource(&cbD->computePassState.writtenResources,
b->u.sbuf.buf,
b->type,
QRhiShaderResourceBinding::BufferLoad,
QRhiShaderResourceBinding::BufferStore,
QRhiShaderResourceBinding::BufferLoadStore);
break;
default:
break;
}
}
for (auto it = cbD->computePassState.writtenResources.begin(); it != cbD->computePassState.writtenResources.end(); ) {
const int accessInThisDispatch = it->first;
const bool isNewInThisDispatch = it->second;
if (accessInThisDispatch && !isNewInThisDispatch) {
if (it.key()->resourceType() == QRhiResource::Texture)
barriers |= GL_SHADER_IMAGE_ACCESS_BARRIER_BIT;
else
barriers |= GL_SHADER_STORAGE_BARRIER_BIT;
}
// Anything that was previously written, but is only read now, can be
// removed from the written list (because that previous write got a
// corresponding barrier now).
if (accessInThisDispatch == QGles2CommandBuffer::ComputePassState::Read)
it = cbD->computePassState.writtenResources.erase(it);
else
++it;
}
if (barriers) {
QGles2CommandBuffer::Command cmd;
cmd.cmd = QGles2CommandBuffer::Command::Barrier;
cmd.args.barrier.barriers = barriers;
cbD->commands.append(cmd);
}
}
QGles2CommandBuffer::Command cmd;
cmd.cmd = QGles2CommandBuffer::Command::Dispatch;
cmd.args.dispatch.x = GLuint(x);

View File

@ -521,6 +521,17 @@ struct QGles2CommandBuffer : public QRhiCommandBuffer
QRhiShaderResourceBindings *currentComputeSrb;
uint currentSrbGeneration;
struct ComputePassState {
enum Access {
Read = 0x01,
Write = 0x02
};
QHash<QRhiResource *, QPair<int, bool> > writtenResources;
void reset() {
writtenResources.clear();
}
} computePassState;
QVector<QByteArray> dataRetainPool;
QVector<QImage> imageRetainPool;

View File

@ -2219,6 +2219,8 @@ void QRhiVulkan::beginComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch
cbD->recordingPass = QVkCommandBuffer::ComputePass;
cbD->computePassState.reset();
if (cbD->useSecondaryCb)
cbD->secondaryCbs.append(startSecondaryCommandBuffer());
}
@ -2267,15 +2269,152 @@ void QRhiVulkan::setComputePipeline(QRhiCommandBuffer *cb, QRhiComputePipeline *
psD->lastActiveFrameSlot = currentFrameSlot;
}
template<typename T>
inline void qrhivk_accumulateComputeResource(T *writtenResources, QRhiResource *resource,
QRhiShaderResourceBinding::Type bindingType,
int loadTypeVal, int storeTypeVal, int loadStoreTypeVal)
{
VkAccessFlags access = 0;
if (bindingType == loadTypeVal) {
access = VK_ACCESS_SHADER_READ_BIT;
} else {
access = VK_ACCESS_SHADER_WRITE_BIT;
if (bindingType == loadStoreTypeVal)
access |= VK_ACCESS_SHADER_READ_BIT;
}
auto it = writtenResources->find(resource);
if (it != writtenResources->end())
it->first |= access;
else if (bindingType == storeTypeVal || bindingType == loadStoreTypeVal)
writtenResources->insert(resource, { access, true });
}
void QRhiVulkan::dispatch(QRhiCommandBuffer *cb, int x, int y, int z)
{
QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::ComputePass);
// When there are multiple dispatches, read-after-write and
// write-after-write need a barrier.
QVarLengthArray<VkImageMemoryBarrier, 8> imageBarriers;
QVarLengthArray<VkBufferMemoryBarrier, 8> bufferBarriers;
if (cbD->currentComputeSrb) {
// The key in the writtenResources map indicates that the resource was
// written in a previous dispatch, whereas the value accumulates the
// access mask in the current one.
for (auto &accessAndIsNewFlag : cbD->computePassState.writtenResources)
accessAndIsNewFlag = { 0, false };
QVkShaderResourceBindings *srbD = QRHI_RES(QVkShaderResourceBindings, cbD->currentComputeSrb);
const int bindingCount = srbD->m_bindings.count();
for (int i = 0; i < bindingCount; ++i) {
const QRhiShaderResourceBinding::Data *b = srbD->m_bindings.at(i).data();
switch (b->type) {
case QRhiShaderResourceBinding::ImageLoad:
case QRhiShaderResourceBinding::ImageStore:
case QRhiShaderResourceBinding::ImageLoadStore:
qrhivk_accumulateComputeResource(&cbD->computePassState.writtenResources,
b->u.simage.tex,
b->type,
QRhiShaderResourceBinding::ImageLoad,
QRhiShaderResourceBinding::ImageStore,
QRhiShaderResourceBinding::ImageLoadStore);
break;
case QRhiShaderResourceBinding::BufferLoad:
case QRhiShaderResourceBinding::BufferStore:
case QRhiShaderResourceBinding::BufferLoadStore:
qrhivk_accumulateComputeResource(&cbD->computePassState.writtenResources,
b->u.sbuf.buf,
b->type,
QRhiShaderResourceBinding::BufferLoad,
QRhiShaderResourceBinding::BufferStore,
QRhiShaderResourceBinding::BufferLoadStore);
break;
default:
break;
}
}
for (auto it = cbD->computePassState.writtenResources.begin(); it != cbD->computePassState.writtenResources.end(); ) {
const int accessInThisDispatch = it->first;
const bool isNewInThisDispatch = it->second;
if (accessInThisDispatch && !isNewInThisDispatch) {
if (it.key()->resourceType() == QRhiResource::Texture) {
QVkTexture *texD = QRHI_RES(QVkTexture, it.key());
VkImageMemoryBarrier barrier;
memset(&barrier, 0, sizeof(barrier));
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
// won't care about subresources, pretend the whole resource was written
barrier.subresourceRange.baseMipLevel = 0;
barrier.subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS;
barrier.subresourceRange.baseArrayLayer = 0;
barrier.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS;
barrier.oldLayout = texD->usageState.layout;
barrier.newLayout = texD->usageState.layout;
barrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT;
barrier.dstAccessMask = accessInThisDispatch;
barrier.image = texD->image;
imageBarriers.append(barrier);
} else {
QVkBuffer *bufD = QRHI_RES(QVkBuffer, it.key());
VkBufferMemoryBarrier barrier;
memset(&barrier, 0, sizeof(barrier));
barrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER;
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT;
barrier.dstAccessMask = accessInThisDispatch;
barrier.buffer = bufD->buffers[bufD->m_type == QRhiBuffer::Dynamic ? currentFrameSlot : 0];
barrier.size = VK_WHOLE_SIZE;
bufferBarriers.append(barrier);
}
}
// Anything that was previously written, but is only read now, can be
// removed from the written list (because that previous write got a
// corresponding barrier now).
if (accessInThisDispatch == VK_ACCESS_SHADER_READ_BIT)
it = cbD->computePassState.writtenResources.erase(it);
else
++it;
}
}
if (cbD->useSecondaryCb) {
df->vkCmdDispatch(cbD->secondaryCbs.last(), uint32_t(x), uint32_t(y), uint32_t(z));
VkCommandBuffer secondaryCb = cbD->secondaryCbs.last();
if (!imageBarriers.isEmpty()) {
df->vkCmdPipelineBarrier(secondaryCb, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
0, 0, nullptr,
0, nullptr,
imageBarriers.count(), imageBarriers.constData());
}
if (!bufferBarriers.isEmpty()) {
df->vkCmdPipelineBarrier(secondaryCb, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
0, 0, nullptr,
bufferBarriers.count(), bufferBarriers.constData(),
0, nullptr);
}
df->vkCmdDispatch(secondaryCb, uint32_t(x), uint32_t(y), uint32_t(z));
} else {
QVkCommandBuffer::Command cmd;
if (!imageBarriers.isEmpty()) {
cmd.cmd = QVkCommandBuffer::Command::ImageBarrier;
cmd.args.imageBarrier.srcStageMask = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
cmd.args.imageBarrier.dstStageMask = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
cmd.args.imageBarrier.count = imageBarriers.count();
cmd.args.imageBarrier.index = cbD->pools.imageBarrier.count();
cbD->pools.imageBarrier.append(imageBarriers.constData(), imageBarriers.count());
cbD->commands.append(cmd);
}
if (!bufferBarriers.isEmpty()) {
cmd.cmd = QVkCommandBuffer::Command::BufferBarrier;
cmd.args.bufferBarrier.srcStageMask = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
cmd.args.bufferBarrier.dstStageMask = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
cmd.args.bufferBarrier.count = bufferBarriers.count();
cmd.args.bufferBarrier.index = cbD->pools.bufferBarrier.count();
cbD->pools.bufferBarrier.append(bufferBarriers.constData(), bufferBarriers.count());
cbD->commands.append(cmd);
}
cmd.cmd = QVkCommandBuffer::Command::Dispatch;
cmd.args.dispatch.x = x;
cmd.args.dispatch.y = y;
@ -2465,7 +2604,9 @@ void QRhiVulkan::trackedBufferBarrier(QVkCommandBuffer *cbD, QVkBuffer *bufD, in
cmd.cmd = QVkCommandBuffer::Command::BufferBarrier;
cmd.args.bufferBarrier.srcStageMask = s.stage;
cmd.args.bufferBarrier.dstStageMask = stage;
cmd.args.bufferBarrier.desc = bufMemBarrier;
cmd.args.bufferBarrier.count = 1;
cmd.args.bufferBarrier.index = cbD->pools.bufferBarrier.count();
cbD->pools.bufferBarrier.append(bufMemBarrier);
cbD->commands.append(cmd);
s.access = access;
@ -2507,7 +2648,9 @@ void QRhiVulkan::trackedImageBarrier(QVkCommandBuffer *cbD, QVkTexture *texD,
cmd.cmd = QVkCommandBuffer::Command::ImageBarrier;
cmd.args.imageBarrier.srcStageMask = srcStage;
cmd.args.imageBarrier.dstStageMask = stage;
cmd.args.imageBarrier.desc = barrier;
cmd.args.imageBarrier.count = 1;
cmd.args.imageBarrier.index = cbD->pools.imageBarrier.count();
cbD->pools.imageBarrier.append(barrier);
cbD->commands.append(cmd);
s.layout = layout;
@ -2541,7 +2684,9 @@ void QRhiVulkan::subresourceBarrier(QVkCommandBuffer *cbD, VkImage image,
cmd.cmd = QVkCommandBuffer::Command::ImageBarrier;
cmd.args.imageBarrier.srcStageMask = srcStage;
cmd.args.imageBarrier.dstStageMask = dstStage;
cmd.args.imageBarrier.desc = barrier;
cmd.args.imageBarrier.count = 1;
cmd.args.imageBarrier.index = cbD->pools.imageBarrier.count();
cbD->pools.imageBarrier.append(barrier);
cbD->commands.append(cmd);
}
@ -3409,12 +3554,12 @@ void QRhiVulkan::recordPrimaryCommandBuffer(QVkCommandBuffer *cbD)
case QVkCommandBuffer::Command::ImageBarrier:
df->vkCmdPipelineBarrier(cbD->cb, cmd.args.imageBarrier.srcStageMask, cmd.args.imageBarrier.dstStageMask,
0, 0, nullptr, 0, nullptr,
1, &cmd.args.imageBarrier.desc);
cmd.args.imageBarrier.count, cbD->pools.imageBarrier.constData() + cmd.args.imageBarrier.index);
break;
case QVkCommandBuffer::Command::BufferBarrier:
df->vkCmdPipelineBarrier(cbD->cb, cmd.args.bufferBarrier.srcStageMask, cmd.args.bufferBarrier.dstStageMask,
0, 0, nullptr,
1, &cmd.args.bufferBarrier.desc,
cmd.args.bufferBarrier.count, cbD->pools.bufferBarrier.constData() + cmd.args.bufferBarrier.index,
0, nullptr);
break;
case QVkCommandBuffer::Command::BlitImage:

View File

@ -370,6 +370,13 @@ struct QVkCommandBuffer : public QRhiCommandBuffer
QVarLengthArray<VkCommandBuffer, 4> secondaryCbs;
bool inExternal;
struct {
QHash<QRhiResource *, QPair<VkAccessFlags, bool> > writtenResources;
void reset() {
writtenResources.clear();
}
} computePassState;
struct Command {
enum Cmd {
CopyBuffer,
@ -429,12 +436,14 @@ struct QVkCommandBuffer : public QRhiCommandBuffer
struct {
VkPipelineStageFlags srcStageMask;
VkPipelineStageFlags dstStageMask;
VkImageMemoryBarrier desc;
int count;
int index;
} imageBarrier;
struct {
VkPipelineStageFlags srcStageMask;
VkPipelineStageFlags dstStageMask;
VkBufferMemoryBarrier desc;
int count;
int index;
} bufferBarrier;
struct {
VkImage src;
@ -537,6 +546,8 @@ struct QVkCommandBuffer : public QRhiCommandBuffer
pools.vertexBuffer.clear();
pools.vertexBufferOffset.clear();
pools.debugMarkerData.clear();
pools.imageBarrier.clear();
pools.bufferBarrier.clear();
}
struct {
@ -546,6 +557,8 @@ struct QVkCommandBuffer : public QRhiCommandBuffer
QVarLengthArray<VkBuffer, 4> vertexBuffer;
QVarLengthArray<VkDeviceSize, 4> vertexBufferOffset;
QVarLengthArray<QByteArray, 4> debugMarkerData;
QVarLengthArray<VkImageMemoryBarrier, 8> imageBarrier;
QVarLengthArray<VkBufferMemoryBarrier, 8> bufferBarrier;
} pools;
friend class QRhiVulkan;

View File

@ -367,7 +367,7 @@ QByteArray QShader::serialized() const
ds << QShaderPrivate::QSB_VERSION;
ds << int(d->stage);
ds << d->desc.toCbor();
d->desc.serialize(&ds);
ds << d->shaders.count();
for (auto it = d->shaders.cbegin(), itEnd = d->shaders.cend(); it != itEnd; ++it) {
const QShaderKey &k(it.key());
@ -428,6 +428,7 @@ QShader QShader::fromSerialized(const QByteArray &data)
ds >> intVal;
d->qsbVersion = intVal;
if (d->qsbVersion != QShaderPrivate::QSB_VERSION
&& d->qsbVersion != QShaderPrivate::QSB_VERSION_WITH_CBOR
&& d->qsbVersion != QShaderPrivate::QSB_VERSION_WITH_BINARY_JSON
&& d->qsbVersion != QShaderPrivate::QSB_VERSION_WITHOUT_BINDINGS)
{
@ -437,14 +438,18 @@ QShader QShader::fromSerialized(const QByteArray &data)
ds >> intVal;
d->stage = Stage(intVal);
QByteArray descBin;
ds >> descBin;
if (d->qsbVersion > QShaderPrivate::QSB_VERSION_WITH_BINARY_JSON) {
if (d->qsbVersion > QShaderPrivate::QSB_VERSION_WITH_CBOR) {
d->desc = QShaderDescription::deserialize(&ds);
} else if (d->qsbVersion > QShaderPrivate::QSB_VERSION_WITH_BINARY_JSON) {
QByteArray descBin;
ds >> descBin;
d->desc = QShaderDescription::fromCbor(descBin);
} else {
#if QT_CONFIG(binaryjson) && QT_DEPRECATED_SINCE(5, 15)
QT_WARNING_PUSH
QT_WARNING_DISABLE_DEPRECATED
QByteArray descBin;
ds >> descBin;
d->desc = QShaderDescription::fromBinaryJson(descBin);
QT_WARNING_POP
#else

View File

@ -57,7 +57,8 @@ QT_BEGIN_NAMESPACE
struct Q_GUI_EXPORT QShaderPrivate
{
static const int QSB_VERSION = 3;
static const int QSB_VERSION = 4;
static const int QSB_VERSION_WITH_CBOR = 3;
static const int QSB_VERSION_WITH_BINARY_JSON = 2;
static const int QSB_VERSION_WITHOUT_BINDINGS = 1;

View File

@ -36,6 +36,7 @@
#include "qshaderdescription_p_p.h"
#include <QDebug>
#include <QDataStream>
#include <QJsonObject>
#include <QJsonArray>
#include <QCborValue>
@ -358,6 +359,11 @@ QByteArray QShaderDescription::toJson() const
return d->makeDoc().toJson();
}
void QShaderDescription::serialize(QDataStream *stream) const
{
d->writeToStream(stream);
}
#if QT_CONFIG(binaryjson) && QT_DEPRECATED_SINCE(5, 15)
/*!
\deprecated
@ -396,6 +402,13 @@ QShaderDescription QShaderDescription::fromCbor(const QByteArray &data)
return desc;
}
QShaderDescription QShaderDescription::deserialize(QDataStream *stream)
{
QShaderDescription desc;
QShaderDescriptionPrivate::get(&desc)->loadFromStream(stream);
return desc;
}
/*!
\return the list of input variables. This includes vertex inputs (sometimes
called attributes) for the vertex stage, and inputs for other stages
@ -867,6 +880,15 @@ static void addDeco(QJsonObject *obj, const QShaderDescription::InOutVariable &v
(*obj)[imageFlagsKey] = int(v.imageFlags);
}
static void serializeDecorations(QDataStream *stream, const QShaderDescription::InOutVariable &v)
{
(*stream) << v.location;
(*stream) << v.binding;
(*stream) << v.descriptorSet;
(*stream) << int(v.imageFormat);
(*stream) << int(v.imageFlags);
}
static QJsonObject inOutObject(const QShaderDescription::InOutVariable &v)
{
QJsonObject obj;
@ -876,6 +898,13 @@ static QJsonObject inOutObject(const QShaderDescription::InOutVariable &v)
return obj;
}
static void serializeInOutVar(QDataStream *stream, const QShaderDescription::InOutVariable &v)
{
(*stream) << v.name;
(*stream) << int(v.type);
serializeDecorations(stream, v);
}
static QJsonObject blockMemberObject(const QShaderDescription::BlockVariable &v)
{
QJsonObject obj;
@ -904,6 +933,23 @@ static QJsonObject blockMemberObject(const QShaderDescription::BlockVariable &v)
return obj;
}
static void serializeBlockMemberVar(QDataStream *stream, const QShaderDescription::BlockVariable &v)
{
(*stream) << v.name;
(*stream) << int(v.type);
(*stream) << v.offset;
(*stream) << v.size;
(*stream) << v.arrayDims.count();
for (int dim : v.arrayDims)
(*stream) << dim;
(*stream) << v.arrayStride;
(*stream) << v.matrixStride;
(*stream) << v.matrixIsRowMajor;
(*stream) << v.structMembers.count();
for (const QShaderDescription::BlockVariable &sv : v.structMembers)
serializeBlockMemberVar(stream, sv);
}
QJsonDocument QShaderDescriptionPrivate::makeDoc()
{
QJsonObject root;
@ -1002,6 +1048,67 @@ QJsonDocument QShaderDescriptionPrivate::makeDoc()
return QJsonDocument(root);
}
void QShaderDescriptionPrivate::writeToStream(QDataStream *stream)
{
(*stream) << inVars.count();
for (const QShaderDescription::InOutVariable &v : qAsConst(inVars))
serializeInOutVar(stream, v);
(*stream) << outVars.count();
for (const QShaderDescription::InOutVariable &v : qAsConst(outVars))
serializeInOutVar(stream, v);
(*stream) << uniformBlocks.count();
for (const QShaderDescription::UniformBlock &b : uniformBlocks) {
(*stream) << b.blockName;
(*stream) << b.structName;
(*stream) << b.size;
(*stream) << b.binding;
(*stream) << b.descriptorSet;
(*stream) << b.members.count();
for (const QShaderDescription::BlockVariable &v : b.members)
serializeBlockMemberVar(stream, v);
}
(*stream) << pushConstantBlocks.count();
for (const QShaderDescription::PushConstantBlock &b : pushConstantBlocks) {
(*stream) << b.name;
(*stream) << b.size;
(*stream) << b.members.count();
for (const QShaderDescription::BlockVariable &v : b.members)
serializeBlockMemberVar(stream, v);
}
(*stream) << storageBlocks.count();
for (const QShaderDescription::StorageBlock &b : storageBlocks) {
(*stream) << b.blockName;
(*stream) << b.instanceName;
(*stream) << b.knownSize;
(*stream) << b.binding;
(*stream) << b.descriptorSet;
(*stream) << b.members.count();
for (const QShaderDescription::BlockVariable &v : b.members)
serializeBlockMemberVar(stream, v);
}
(*stream) << combinedImageSamplers.count();
for (const QShaderDescription::InOutVariable &v : qAsConst(combinedImageSamplers)) {
(*stream) << v.name;
(*stream) << int(v.type);
serializeDecorations(stream, v);
}
(*stream) << storageImages.count();
for (const QShaderDescription::InOutVariable &v : qAsConst(storageImages)) {
(*stream) << v.name;
(*stream) << int(v.type);
serializeDecorations(stream, v);
}
for (size_t i = 0; i < 3; ++i)
(*stream) << localSize[i];
}
static QShaderDescription::InOutVariable inOutVar(const QJsonObject &obj)
{
QShaderDescription::InOutVariable var;
@ -1020,6 +1127,29 @@ static QShaderDescription::InOutVariable inOutVar(const QJsonObject &obj)
return var;
}
static void deserializeDecorations(QDataStream *stream, QShaderDescription::InOutVariable *v)
{
(*stream) >> v->location;
(*stream) >> v->binding;
(*stream) >> v->descriptorSet;
int f;
(*stream) >> f;
v->imageFormat = QShaderDescription::ImageFormat(f);
(*stream) >> f;
v->imageFlags = QShaderDescription::ImageFlags(f);
}
static QShaderDescription::InOutVariable deserializeInOutVar(QDataStream *stream)
{
QShaderDescription::InOutVariable var;
(*stream) >> var.name;
int t;
(*stream) >> t;
var.type = QShaderDescription::VariableType(t);
deserializeDecorations(stream, &var);
return var;
}
static QShaderDescription::BlockVariable blockVar(const QJsonObject &obj)
{
QShaderDescription::BlockVariable var;
@ -1046,6 +1176,30 @@ static QShaderDescription::BlockVariable blockVar(const QJsonObject &obj)
return var;
}
static QShaderDescription::BlockVariable deserializeBlockMemberVar(QDataStream *stream)
{
QShaderDescription::BlockVariable var;
(*stream) >> var.name;
int t;
(*stream) >> t;
var.type = QShaderDescription::VariableType(t);
(*stream) >> var.offset;
(*stream) >> var.size;
int count;
(*stream) >> count;
var.arrayDims.resize(count);
for (int i = 0; i < count; ++i)
(*stream) >> var.arrayDims[i];
(*stream) >> var.arrayStride;
(*stream) >> var.matrixStride;
(*stream) >> var.matrixIsRowMajor;
(*stream) >> count;
var.structMembers.resize(count);
for (int i = 0; i < count; ++i)
var.structMembers[i] = deserializeBlockMemberVar(stream);
return var;
}
void QShaderDescriptionPrivate::loadDoc(const QJsonDocument &doc)
{
if (doc.isNull()) {
@ -1150,6 +1304,87 @@ void QShaderDescriptionPrivate::loadDoc(const QJsonDocument &doc)
}
}
void QShaderDescriptionPrivate::loadFromStream(QDataStream *stream)
{
Q_ASSERT(ref.loadRelaxed() == 1); // must be detached
int count;
(*stream) >> count;
inVars.resize(count);
for (int i = 0; i < count; ++i)
inVars[i] = deserializeInOutVar(stream);
(*stream) >> count;
outVars.resize(count);
for (int i = 0; i < count; ++i)
outVars[i] = deserializeInOutVar(stream);
(*stream) >> count;
uniformBlocks.resize(count);
for (int i = 0; i < count; ++i) {
(*stream) >> uniformBlocks[i].blockName;
(*stream) >> uniformBlocks[i].structName;
(*stream) >> uniformBlocks[i].size;
(*stream) >> uniformBlocks[i].binding;
(*stream) >> uniformBlocks[i].descriptorSet;
int memberCount;
(*stream) >> memberCount;
uniformBlocks[i].members.resize(memberCount);
for (int memberIdx = 0; memberIdx < memberCount; ++memberIdx)
uniformBlocks[i].members[memberIdx] = deserializeBlockMemberVar(stream);
}
(*stream) >> count;
pushConstantBlocks.resize(count);
for (int i = 0; i < count; ++i) {
(*stream) >> pushConstantBlocks[i].name;
(*stream) >> pushConstantBlocks[i].size;
int memberCount;
(*stream) >> memberCount;
pushConstantBlocks[i].members.resize(memberCount);
for (int memberIdx = 0; memberIdx < memberCount; ++memberIdx)
pushConstantBlocks[i].members[memberIdx] = deserializeBlockMemberVar(stream);
}
(*stream) >> count;
storageBlocks.resize(count);
for (int i = 0; i < count; ++i) {
(*stream) >> storageBlocks[i].blockName;
(*stream) >> storageBlocks[i].instanceName;
(*stream) >> storageBlocks[i].knownSize;
(*stream) >> storageBlocks[i].binding;
(*stream) >> storageBlocks[i].descriptorSet;
int memberCount;
(*stream) >> memberCount;
storageBlocks[i].members.resize(memberCount);
for (int memberIdx = 0; memberIdx < memberCount; ++memberIdx)
storageBlocks[i].members[memberIdx] = deserializeBlockMemberVar(stream);
}
(*stream) >> count;
combinedImageSamplers.resize(count);
for (int i = 0; i < count; ++i) {
(*stream) >> combinedImageSamplers[i].name;
int t;
(*stream) >> t;
combinedImageSamplers[i].type = QShaderDescription::VariableType(t);
deserializeDecorations(stream, &combinedImageSamplers[i]);
}
(*stream) >> count;
storageImages.resize(count);
for (int i = 0; i < count; ++i) {
(*stream) >> storageImages[i].name;
int t;
(*stream) >> t;
storageImages[i].type = QShaderDescription::VariableType(t);
deserializeDecorations(stream, &storageImages[i]);
}
for (size_t i = 0; i < 3; ++i)
(*stream) >> localSize[i];
}
/*!
Returns \c true if the two QShaderDescription objects \a lhs and \a rhs are
equal.

View File

@ -56,6 +56,7 @@
QT_BEGIN_NAMESPACE
struct QShaderDescriptionPrivate;
class QDataStream;
class Q_GUI_EXPORT QShaderDescription
{
@ -69,6 +70,7 @@ public:
bool isValid() const;
QByteArray toCbor() const;
void serialize(QDataStream *stream) const;
QByteArray toJson() const;
#if QT_CONFIG(binaryjson) && QT_DEPRECATED_SINCE(5, 15)
@ -76,6 +78,7 @@ public:
static QShaderDescription fromBinaryJson(const QByteArray &data);
#endif
static QShaderDescription fromCbor(const QByteArray &data);
static QShaderDescription deserialize(QDataStream *stream);
enum VariableType {
Unknown = 0,

View File

@ -80,7 +80,9 @@ struct Q_GUI_EXPORT QShaderDescriptionPrivate
static const QShaderDescriptionPrivate *get(const QShaderDescription *desc) { return desc->d; }
QJsonDocument makeDoc();
void writeToStream(QDataStream *stream);
void loadDoc(const QJsonDocument &doc);
void loadFromStream(QDataStream *stream);
QAtomicInt ref;
QVector<QShaderDescription::InOutVariable> inVars;

View File

@ -554,13 +554,32 @@ QNetworkAccessManager::Operation QNetworkReply::operation() const
return d_func()->operation;
}
#if QT_DEPRECATED_SINCE(5, 15)
/*!
\deprecated
Use networkError() instead.
Returns the error that was found during the processing of this
request. If no error was found, returns NoError.
\sa setError(), networkError()
*/
QNetworkReply::NetworkError QNetworkReply::error() const
{
return networkError();
}
#endif // QT_DEPRECATED_SINCE(5, 15)
/*!
\since 5.15
Returns the error that was found during the processing of this
request. If no error was found, returns NoError.
\sa setError()
*/
QNetworkReply::NetworkError QNetworkReply::error() const
QNetworkReply::NetworkError QNetworkReply::networkError() const
{
return d_func()->errorCode;
}
@ -858,7 +877,7 @@ void QNetworkReply::setRequest(const QNetworkRequest &request)
Calling setError() does not emit the error(QNetworkReply::NetworkError)
signal.
\sa error(), errorString()
\sa error(), errorString(), networkError()
*/
void QNetworkReply::setError(NetworkError errorCode, const QString &errorString)
{

View File

@ -124,7 +124,12 @@ public:
QNetworkAccessManager *manager() const;
QNetworkAccessManager::Operation operation() const;
QNetworkRequest request() const;
NetworkError error() const;
#if QT_DEPRECATED_SINCE(5, 15)
QT_DEPRECATED_X("Use networkError()") NetworkError error() const;
#endif // QT_DEPRECATED_SINCE(5, 15)
NetworkError networkError() const;
bool isFinished() const;
bool isRunning() const;
QUrl url() const;

View File

@ -62,6 +62,9 @@
QT_BEGIN_NAMESPACE
// Clang does not consider __declspec(nothrow) as nothrow
QT_WARNING_DISABLE_CLANG("-Wmicrosoft-exception-spec")
// Convert from design units to logical pixels
#define DESIGN_TO_LOGICAL(DESIGN_UNIT_VALUE) \
QFixed::fromReal((qreal(DESIGN_UNIT_VALUE) / qreal(m_unitsPerEm)) * fontDef.pixelSize)

View File

@ -33,6 +33,14 @@
#include <emscripten.h>
#if (__EMSCRIPTEN_major__ > 1 || __EMSCRIPTEN_minor__ > 38 || __EMSCRIPTEN_minor__ == 38 && __EMSCRIPTEN_tiny__ >= 22)
# define EMSCRIPTEN_HAS_ASYNC_RUN_IN_MAIN_RUNTIME_THREAD
#endif
#ifdef EMSCRIPTEN_HAS_ASYNC_RUN_IN_MAIN_RUNTIME_THREAD
#include <emscripten/threading.h>
#endif
class QWasmEventDispatcherPrivate : public QEventDispatcherUNIXPrivate
{
@ -179,3 +187,18 @@ void QWasmEventDispatcher::doMaintainTimers()
emscripten_async_call(callback, this, toWaitDuration);
m_currentTargetTime = newTargetTime;
}
void QWasmEventDispatcher::wakeUp()
{
#ifdef EMSCRIPTEN_HAS_ASYNC_RUN_IN_MAIN_RUNTIME_THREAD
if (!emscripten_is_main_runtime_thread())
emscripten_async_run_in_main_runtime_thread_(EM_FUNC_SIG_VI, (void*)(&QWasmEventDispatcher::mainThreadWakeUp), this);
#endif
QEventDispatcherUNIX::wakeUp();
}
void QWasmEventDispatcher::mainThreadWakeUp(void *eventDispatcher)
{
emscripten_resume_main_loop(); // Service possible requestUpdate Calls
static_cast<QWasmEventDispatcher *>(eventDispatcher)->processEvents(QEventLoop::AllEvents);
}

View File

@ -51,6 +51,8 @@ public:
protected:
bool processEvents(QEventLoop::ProcessEventsFlags flags) override;
void doMaintainTimers();
void wakeUp() override;
static void mainThreadWakeUp(void *eventDispatcher);
private:
bool m_hasMainLoop = false;

View File

@ -107,6 +107,9 @@ private:
ULONG m_ref;
};
// Clang does not consider __declspec(nothrow) as nothrow
QT_WARNING_DISABLE_CLANG("-Wmicrosoft-exception-spec")
QT_END_NAMESPACE
#endif // QWINDOWSCOMBASE_H

View File

@ -2585,7 +2585,7 @@ QPalette QMacStyle::standardPalette() const
auto platformTheme = QGuiApplicationPrivate::platformTheme();
auto styleNames = platformTheme->themeHint(QPlatformTheme::StyleNames);
if (styleNames.toStringList().contains("macintosh"))
return *platformTheme->palette();
return QPalette(); // Inherit everything from theme
else
return QStyle::standardPalette();
}

View File

@ -3774,8 +3774,7 @@ int QWindowsXPStyle::styleHint(StyleHint hint, const QStyleOption *option, const
/*! \reimp */
QPalette QWindowsXPStyle::standardPalette() const
{
return QWindowsXPStylePrivate::useXP() && QApplicationPrivate::sys_pal
? *QApplicationPrivate::sys_pal : QWindowsStyle::standardPalette();
return QWindowsXPStylePrivate::useXP() ? QPalette() : QWindowsStyle::standardPalette();
}
/*!

View File

@ -140,30 +140,6 @@ Q_GUI_EXPORT bool qt_sendShortcutOverrideEvent(QObject *o, ulong timestamp, int
QApplicationPrivate *QApplicationPrivate::self = nullptr;
static void initSystemPalette()
{
if (QApplicationPrivate::sys_pal)
return; // Already initialized
QPalette defaultPalette;
if (QApplicationPrivate::app_style)
defaultPalette = QApplicationPrivate::app_style->standardPalette();
auto *platformTheme = QGuiApplicationPrivate::platformTheme();
if (const QPalette *themePalette = platformTheme ? platformTheme->palette() : nullptr) {
QApplicationPrivate::setSystemPalette(themePalette->resolve(defaultPalette));
QApplicationPrivate::initializeWidgetPaletteHash();
} else {
QApplicationPrivate::setSystemPalette(defaultPalette);
}
}
static void clearSystemPalette()
{
delete QApplicationPrivate::sys_pal;
QApplicationPrivate::sys_pal = nullptr;
}
bool QApplicationPrivate::autoSipEnabled = true;
QApplicationPrivate::QApplicationPrivate(int &argc, char **argv, int flags)
@ -381,8 +357,6 @@ QString QApplicationPrivate::styleSheet; // default application styles
#endif
QPointer<QWidget> QApplicationPrivate::leaveAfterRelease = nullptr;
QPalette *QApplicationPrivate::sys_pal = nullptr; // default system palette
QFont *QApplicationPrivate::sys_font = nullptr; // default system font
QFont *QApplicationPrivate::set_font = nullptr; // default font set by programmer
@ -545,12 +519,7 @@ void QApplicationPrivate::init()
// Must be called before initialize()
QColormap::initialize();
if (sys_pal) {
// Now that we have a platform theme we need to reset
// the system palette to pick up the theme colors.
clearSystemPalette();
initSystemPalette();
}
initializeWidgetPalettesFromTheme();
qt_init_tooltip_palette();
QApplicationPrivate::initializeWidgetFontHash();
@ -629,38 +598,6 @@ void QApplicationPrivate::initialize()
is_app_running = true; // no longer starting up
}
static void setPossiblePalette(const QPalette *palette, const char *className)
{
if (palette == nullptr)
return;
QApplicationPrivate::setPalette_helper(*palette, className);
}
void QApplicationPrivate::initializeWidgetPaletteHash()
{
QPlatformTheme *platformTheme = QGuiApplicationPrivate::platformTheme();
if (!platformTheme)
return;
widgetPalettes.clear();
setPossiblePalette(platformTheme->palette(QPlatformTheme::ToolButtonPalette), "QToolButton");
setPossiblePalette(platformTheme->palette(QPlatformTheme::ButtonPalette), "QAbstractButton");
setPossiblePalette(platformTheme->palette(QPlatformTheme::CheckBoxPalette), "QCheckBox");
setPossiblePalette(platformTheme->palette(QPlatformTheme::RadioButtonPalette), "QRadioButton");
setPossiblePalette(platformTheme->palette(QPlatformTheme::HeaderPalette), "QHeaderView");
setPossiblePalette(platformTheme->palette(QPlatformTheme::ItemViewPalette), "QAbstractItemView");
setPossiblePalette(platformTheme->palette(QPlatformTheme::MessageBoxLabelPalette), "QMessageBoxLabel");
setPossiblePalette(platformTheme->palette(QPlatformTheme::TabBarPalette), "QTabBar");
setPossiblePalette(platformTheme->palette(QPlatformTheme::LabelPalette), "QLabel");
setPossiblePalette(platformTheme->palette(QPlatformTheme::GroupBoxPalette), "QGroupBox");
setPossiblePalette(platformTheme->palette(QPlatformTheme::MenuPalette), "QMenu");
setPossiblePalette(platformTheme->palette(QPlatformTheme::MenuBarPalette), "QMenuBar");
setPossiblePalette(platformTheme->palette(QPlatformTheme::TextEditPalette), "QTextEdit");
setPossiblePalette(platformTheme->palette(QPlatformTheme::TextEditPalette), "QTextControl");
setPossiblePalette(platformTheme->palette(QPlatformTheme::TextLineEditPalette), "QLineEdit");
}
void QApplicationPrivate::initializeWidgetFontHash()
{
const QPlatformTheme *theme = QGuiApplicationPrivate::platformTheme();
@ -798,9 +735,6 @@ QApplication::~QApplication()
delete qt_desktopWidget;
qt_desktopWidget = nullptr;
delete QApplicationPrivate::app_pal;
QApplicationPrivate::app_pal = nullptr;
clearSystemPalette();
QApplicationPrivate::widgetPalettes.clear();
delete QApplicationPrivate::sys_font;
@ -1051,10 +985,10 @@ QStyle *QApplication::style()
// Take ownership of the style
defaultStyle->setParent(qApp);
initSystemPalette();
if (testAttribute(Qt::AA_SetPalette))
defaultStyle->polish(*QGuiApplicationPrivate::app_pal);
else
QApplicationPrivate::initializeWidgetPalettesFromTheme();
#ifndef QT_NO_STYLE_STYLESHEET
if (!QApplicationPrivate::styleSheet.isEmpty()) {
@ -1128,13 +1062,10 @@ void QApplication::setStyle(QStyle *style)
// take care of possible palette requirements of certain gui
// styles. Do it before polishing the application since the style
// might call QApplication::setPalette() itself
if (testAttribute(Qt::AA_SetPalette)) {
if (testAttribute(Qt::AA_SetPalette))
QApplicationPrivate::app_style->polish(*QGuiApplicationPrivate::app_pal);
} else {
if (QApplicationPrivate::sys_pal)
clearSystemPalette();
initSystemPalette();
}
else
QApplicationPrivate::initializeWidgetPalettesFromTheme();
// The default widget font hash is based on the platform theme,
// not the style, but the widget fonts could in theory have been
@ -1317,6 +1248,22 @@ void QApplication::setGlobalStrut(const QSize& strut)
// Widget specific palettes
QApplicationPrivate::PaletteHash QApplicationPrivate::widgetPalettes;
QPalette QApplicationPrivate::basePalette() const
{
// Start out with a palette based on the style, in case there's no theme
// available, or so that we can fill in missing roles in the theme.
QPalette palette = app_style ? app_style->standardPalette() : Qt::gray;
// Prefer theme palette if available, but fill in missing roles from style
// for compatibility. Note that the style's standard palette is not prioritized
// over the theme palette, as the documented way of applying the style's palette
// is to set it explicitly using QApplication::setPalette().
if (const QPalette *themePalette = platformTheme() ? platformTheme()->palette() : nullptr)
palette = themePalette->resolve(palette);
return palette;
}
/*!
\fn QPalette QApplication::palette(const QWidget* widget)
@ -1363,35 +1310,8 @@ QPalette QApplication::palette(const char *className)
return QGuiApplication::palette();
}
void QApplicationPrivate::setPalette_helper(const QPalette &palette, const char* className)
{
QPalette pal = palette;
if (QApplicationPrivate::app_style)
QApplicationPrivate::app_style->polish(pal); // NB: non-const reference
bool all = false;
if (!className) {
if (!QGuiApplicationPrivate::setPalette(pal))
return;
if (!QApplicationPrivate::sys_pal || !palette.isCopyOf(*QApplicationPrivate::sys_pal))
QCoreApplication::setAttribute(Qt::AA_SetPalette);
if (!widgetPalettes.isEmpty()) {
all = true;
widgetPalettes.clear();
}
} else {
widgetPalettes.insert(className, pal);
}
if (qApp)
qApp->d_func()->sendApplicationPaletteChange(all, className);
}
/*!
Changes the default application palette to \a palette.
Changes the application palette to \a palette.
If \a className is passed, the change applies only to widgets that inherit
\a className (as reported by QObject::inherits()). If \a className is left
@ -1412,23 +1332,87 @@ void QApplicationPrivate::setPalette_helper(const QPalette &palette, const char*
\sa QWidget::setPalette(), palette(), QStyle::polish()
*/
void QApplication::setPalette(const QPalette &palette, const char* className)
{
QApplicationPrivate::setPalette_helper(palette, className);
QPalette polishedPalette = palette;
if (QApplicationPrivate::app_style)
QApplicationPrivate::app_style->polish(polishedPalette);
if (className) {
QApplicationPrivate::widgetPalettes.insert(className, polishedPalette);
if (qApp)
qApp->d_func()->handlePaletteChanged(className);
} else {
QGuiApplication::setPalette(polishedPalette);
}
}
void QApplicationPrivate::setSystemPalette(const QPalette &pal)
void QApplicationPrivate::handlePaletteChanged(const char *className)
{
if (!sys_pal)
sys_pal = new QPalette(pal);
else
*sys_pal = pal;
if (!is_app_running || is_app_closing)
return;
if (!testAttribute(Qt::AA_SetPalette))
QApplication::setPalette(*sys_pal);
// Setting the global application palette is documented to
// reset any previously set class specific widget palettes.
bool sendPaletteChangeToAllWidgets = false;
if (!className && !widgetPalettes.isEmpty()) {
sendPaletteChangeToAllWidgets = true;
widgetPalettes.clear();
}
QGuiApplicationPrivate::handlePaletteChanged(className);
QEvent event(QEvent::ApplicationPaletteChange);
const QWidgetList widgets = QApplication::allWidgets();
for (auto widget : widgets) {
if (sendPaletteChangeToAllWidgets || (!className && widget->isWindow()) || (className && widget->inherits(className)))
QCoreApplication::sendEvent(widget, &event);
}
#if QT_CONFIG(graphicsview)
for (auto scene : qAsConst(scene_list))
QCoreApplication::sendEvent(scene, &event);
#endif
// Palette has been reset back to the default application palette,
// so we need to reinitialize the widget palettes from the theme.
if (!className && !testAttribute(Qt::AA_SetPalette))
initializeWidgetPalettesFromTheme();
}
void QApplicationPrivate::initializeWidgetPalettesFromTheme()
{
QPlatformTheme *platformTheme = QGuiApplicationPrivate::platformTheme();
if (!platformTheme)
return;
widgetPalettes.clear();
struct ThemedWidget { const char *className; QPlatformTheme::Palette palette; };
static const ThemedWidget themedWidgets[] = {
{ "QToolButton", QPlatformTheme::ToolButtonPalette },
{ "QAbstractButton", QPlatformTheme::ButtonPalette },
{ "QCheckBox", QPlatformTheme::CheckBoxPalette },
{ "QRadioButton", QPlatformTheme::RadioButtonPalette },
{ "QHeaderView", QPlatformTheme::HeaderPalette },
{ "QAbstractItemView", QPlatformTheme::ItemViewPalette },
{ "QMessageBoxLabel", QPlatformTheme::MessageBoxLabelPalette },
{ "QTabBar", QPlatformTheme::TabBarPalette },
{ "QLabel", QPlatformTheme::LabelPalette },
{ "QGroupBox", QPlatformTheme::GroupBoxPalette },
{ "QMenu", QPlatformTheme::MenuPalette },
{ "QMenuBar", QPlatformTheme::MenuBarPalette },
{ "QTextEdit", QPlatformTheme::TextEditPalette },
{ "QTextControl", QPlatformTheme::TextEditPalette },
{ "QLineEdit", QPlatformTheme::TextLineEditPalette },
};
for (const auto themedWidget : themedWidgets) {
if (auto *palette = platformTheme->palette(themedWidget.palette))
QApplication::setPalette(*palette, themedWidget.className);
}
}
/*!
@ -4413,31 +4397,10 @@ void QApplicationPrivate::translateTouchCancel(QTouchDevice *device, ulong times
void QApplicationPrivate::notifyThemeChanged()
{
QGuiApplicationPrivate::notifyThemeChanged();
clearSystemPalette();
initSystemPalette();
qt_init_tooltip_palette();
}
void QApplicationPrivate::sendApplicationPaletteChange(bool toAllWidgets, const char *className)
{
if (!is_app_running || is_app_closing)
return;
QGuiApplicationPrivate::sendApplicationPaletteChange(toAllWidgets, className);
QEvent event(QEvent::ApplicationPaletteChange);
const QWidgetList widgets = QApplication::allWidgets();
for (auto widget : widgets) {
if (toAllWidgets || (!className && widget->isWindow()) || (className && widget->inherits(className)))
QCoreApplication::sendEvent(widget, &event);
}
#if QT_CONFIG(graphicsview)
for (auto scene : qAsConst(scene_list))
QCoreApplication::sendEvent(scene, &event);
#endif // QT_CONFIG(graphicsview)
}
#if QT_CONFIG(draganddrop)
void QApplicationPrivate::notifyDragStarted(const QDrag *drag)
{

View File

@ -158,12 +158,12 @@ public:
static QSize app_strut;
static QWidgetList *popupWidgets;
static QStyle *app_style;
static QPalette *sys_pal;
protected:
void notifyThemeChanged() override;
void sendApplicationPaletteChange(bool toAllWidgets = false,
const char *className = nullptr) override;
QPalette basePalette() const override;
void handlePaletteChanged(const char *className = nullptr) override;
#if QT_CONFIG(draganddrop)
void notifyDragStarted(const QDrag *) override;
@ -184,9 +184,7 @@ public:
static int enabledAnimations; // Combination of QPlatformTheme::UiEffect
static bool widgetCount; // Coupled with -widgetcount switch
static void setSystemPalette(const QPalette &pal);
static void setPalette_helper(const QPalette &palette, const char* className);
static void initializeWidgetPaletteHash();
static void initializeWidgetPalettesFromTheme();
static void initializeWidgetFontHash();
static void setSystemFont(const QFont &font);

View File

@ -34,6 +34,9 @@
#include <QElapsedTimer>
#include <QTextStream>
#include <QDir>
#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
#include <windows.h>
#endif
/* All tests need to run in temporary directories not used
* by the application to avoid non-deterministic failures on Windows
@ -79,6 +82,9 @@ private slots:
void signalsEmittedAfterFileMoved();
void watchUnicodeCharacters();
#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
void watchDirectoryAttributeChanges();
#endif
private:
QString m_tempDirPattern;
@ -813,5 +819,27 @@ void tst_QFileSystemWatcher::watchUnicodeCharacters()
QTRY_COMPARE(changedSpy.count(), 1);
}
#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
void tst_QFileSystemWatcher::watchDirectoryAttributeChanges()
{
QTemporaryDir temporaryDirectory(m_tempDirPattern);
QVERIFY2(temporaryDirectory.isValid(), qPrintable(temporaryDirectory.errorString()));
QDir testDir(temporaryDirectory.path());
const QString subDir(QString::fromLatin1("attrib_test"));
QVERIFY(testDir.mkdir(subDir));
testDir = QDir(temporaryDirectory.path() + QDir::separator() + subDir);
QFileSystemWatcher watcher;
QVERIFY(watcher.addPath(temporaryDirectory.path()));
FileSystemWatcherSpy changedSpy(&watcher, FileSystemWatcherSpy::SpyOnDirectoryChanged);
QCOMPARE(changedSpy.count(), 0);
QVERIFY(SetFileAttributes(reinterpret_cast<LPCWSTR>(testDir.absolutePath().utf16()), FILE_ATTRIBUTE_HIDDEN) != 0);
QTRY_COMPARE(changedSpy.count(), 1);
QVERIFY(SetFileAttributes(reinterpret_cast<LPCWSTR>(testDir.absolutePath().utf16()), FILE_ATTRIBUTE_NORMAL) != 0);
QTRY_COMPARE(changedSpy.count(), 2);
}
#endif
QTEST_MAIN(tst_QFileSystemWatcher)
#include "tst_qfilesystemwatcher.moc"

View File

@ -459,8 +459,8 @@ private slots:
void trimmed();
void toUpper();
void toLower();
void isUpper();
void isLower();
void isLower_isUpper_data();
void isLower_isUpper();
void toCaseFolded();
void rightJustified();
void leftJustified();
@ -2290,44 +2290,83 @@ void tst_QString::toLower()
#endif // icu
}
void tst_QString::isUpper()
void tst_QString::isLower_isUpper_data()
{
QVERIFY(!QString().isUpper());
QVERIFY(!QString("").isUpper());
QVERIFY(QString("TEXT").isUpper());
QVERIFY(!QString("text").isUpper());
QVERIFY(!QString("Text").isUpper());
QVERIFY(!QString("tExt").isUpper());
QVERIFY(!QString("teXt").isUpper());
QVERIFY(!QString("texT").isUpper());
QVERIFY(!QString("TExt").isUpper());
QVERIFY(!QString("teXT").isUpper());
QVERIFY(!QString("tEXt").isUpper());
QVERIFY(!QString("tExT").isUpper());
QVERIFY(!QString("@ABYZ[").isUpper());
QVERIFY(!QString("@abyz[").isUpper());
QVERIFY(!QString("`ABYZ{").isUpper());
QVERIFY(!QString("`abyz{").isUpper());
QTest::addColumn<QString>("string");
QTest::addColumn<bool>("isLower");
QTest::addColumn<bool>("isUpper");
int row = 0;
QTest::addRow("lower-and-upper-%02d", row++) << QString() << true << true;
QTest::addRow("lower-and-upper-%02d", row++) << QString("") << true << true;
QTest::addRow("lower-and-upper-%02d", row++) << QString(" ") << true << true;
QTest::addRow("lower-and-upper-%02d", row++) << QString("123") << true << true;
QTest::addRow("lower-and-upper-%02d", row++) << QString("@123$#") << true << true;
QTest::addRow("lower-and-upper-%02d", row++) << QString("𝄞𝄴𝆏♫") << true << true; // Unicode Block 'Musical Symbols'
// not foldable
QTest::addRow("lower-and-upper-%02d", row++) << QString("𝚊𝚋𝚌𝚍𝚎") << true << true; // MATHEMATICAL MONOSPACE SMALL A, ... E
QTest::addRow("lower-and-upper-%02d", row++) << QString("𝙖,𝙗,𝙘,𝙙,𝙚") << true << true; // MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL A, ... E
QTest::addRow("lower-and-upper-%02d", row++) << QString("𝗔𝗕𝗖𝗗𝗘") << true << true; // MATHEMATICAL SANS-SERIF BOLD CAPITAL A, ... E
QTest::addRow("lower-and-upper-%02d", row++) << QString("𝐀,𝐁,𝐂,𝐃,𝐄") << true << true; // MATHEMATICAL BOLD CAPITAL A, ... E
row = 0;
QTest::addRow("only-lower-%02d", row++) << QString("text") << true << false;
QTest::addRow("only-lower-%02d", row++) << QString("àaa") << true << false;
QTest::addRow("only-lower-%02d", row++) << QString("øæß") << true << false;
QTest::addRow("only-lower-%02d", row++) << QString("text ") << true << false;
QTest::addRow("only-lower-%02d", row++) << QString(" text") << true << false;
QTest::addRow("only-lower-%02d", row++) << QString("hello, world!") << true << false;
QTest::addRow("only-lower-%02d", row++) << QString("123@abyz[") << true << false;
QTest::addRow("only-lower-%02d", row++) << QString("`abyz{") << true << false;
QTest::addRow("only-lower-%02d", row++) << QString("a𝙖a|b𝙗b|c𝙘c|d𝙙d|e𝙚e") << true << false; // MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL A, ... E
QTest::addRow("only-lower-%02d", row++) << QString("𐐨") << true << false; // DESERET SMALL LETTER LONG I
// uppercase letters, not foldable
QTest::addRow("only-lower-%02d", row++) << QString("text𝗔text") << true << false; // MATHEMATICAL SANS-SERIF BOLD CAPITAL A
row = 0;
QTest::addRow("only-upper-%02d", row++) << QString("TEXT") << false << true;
QTest::addRow("only-upper-%02d", row++) << QString("ÀAA") << false << true;
QTest::addRow("only-upper-%02d", row++) << QString("ØÆẞ") << false << true;
QTest::addRow("only-upper-%02d", row++) << QString("TEXT ") << false << true;
QTest::addRow("only-upper-%02d", row++) << QString(" TEXT") << false << true;
QTest::addRow("only-upper-%02d", row++) << QString("HELLO, WORLD!") << false << true;
QTest::addRow("only-upper-%02d", row++) << QString("123@ABYZ[") << false << true;
QTest::addRow("only-upper-%02d", row++) << QString("`ABYZ{") << false << true;
QTest::addRow("only-upper-%02d", row++) << QString("A𝐀A|B𝐁B|C𝐂C|D𝐃D|E𝐄E") << false << true; // MATHEMATICAL BOLD CAPITAL A, ... E
QTest::addRow("only-upper-%02d", row++) << QString("𐐀") << false << true; // DESERET CAPITAL LETTER LONG I
// lowercase letters, not foldable
QTest::addRow("only-upper-%02d", row++) << QString("TEXT𝚊TEXT") << false << true; // MATHEMATICAL MONOSPACE SMALL A
row = 0;
QTest::addRow("not-lower-nor-upper-%02d", row++) << QString("Text") << false << false;
QTest::addRow("not-lower-nor-upper-%02d", row++) << QString("tExt") << false << false;
QTest::addRow("not-lower-nor-upper-%02d", row++) << QString("teXt") << false << false;
QTest::addRow("not-lower-nor-upper-%02d", row++) << QString("texT") << false << false;
QTest::addRow("not-lower-nor-upper-%02d", row++) << QString("TExt") << false << false;
QTest::addRow("not-lower-nor-upper-%02d", row++) << QString("teXT") << false << false;
QTest::addRow("not-lower-nor-upper-%02d", row++) << QString("tEXt") << false << false;
QTest::addRow("not-lower-nor-upper-%02d", row++) << QString("tExT") << false << false;
// not foldable
QTest::addRow("not-lower-nor-upper-%02d", row++) << QString("TEXT𝚊text") << false << false; // MATHEMATICAL MONOSPACE SMALL A
QTest::addRow("not-lower-nor-upper-%02d", row++) << QString("text𝗔TEXT") << false << false; // MATHEMATICAL SANS-SERIF BOLD CAPITAL A
// titlecase, foldable
QTest::addRow("not-lower-nor-upper-%02d", row++) << QString("abcLjdef") << false << false; // LATIN CAPITAL LETTER L WITH SMALL LETTER J
QTest::addRow("not-lower-nor-upper-%02d", row++) << QString("ABCLjDEF") << false << false; // LATIN CAPITAL LETTER L WITH SMALL LETTER J
}
void tst_QString::isLower()
void tst_QString::isLower_isUpper()
{
QVERIFY(!QString().isLower());
QVERIFY(!QString("").isLower());
QVERIFY(QString("text").isLower());
QVERIFY(!QString("Text").isLower());
QVERIFY(!QString("tExt").isLower());
QVERIFY(!QString("teXt").isLower());
QVERIFY(!QString("texT").isLower());
QVERIFY(!QString("TExt").isLower());
QVERIFY(!QString("teXT").isLower());
QVERIFY(!QString("tEXt").isLower());
QVERIFY(!QString("tExT").isLower());
QVERIFY(!QString("TEXT").isLower());
QVERIFY(!QString("@ABYZ[").isLower());
QVERIFY(!QString("@abyz[").isLower());
QVERIFY(!QString("`ABYZ{").isLower());
QVERIFY(!QString("`abyz{").isLower());
QFETCH(QString, string);
QFETCH(bool, isLower);
QFETCH(bool, isUpper);
QCOMPARE(string.isLower(), isLower);
QCOMPARE(string.toLower() == string, isLower);
QVERIFY(string.toLower().isLower());
QCOMPARE(string.isUpper(), isUpper);
QCOMPARE(string.toUpper() == string, isUpper);
QVERIFY(string.toUpper().isUpper());
}
void tst_QString::toCaseFolded()

View File

@ -29,7 +29,7 @@
#include <QtTest/QtTest>
#include <qstandarditemmodel.h>
#include <QStandardItem>
class tst_QStandardItem : public QObject
{
@ -48,8 +48,6 @@ private slots:
void parent();
void insertColumn_data();
void insertColumn();
void insertColumns_data();
void insertColumns();
void insertRow_data();
void insertRow();
void insertRows_data();
@ -312,12 +310,15 @@ void tst_QStandardItem::getSetFlags()
QCOMPARE(item.checkState(), Qt::Checked);
#if QT_DEPRECATED_SINCE(5, 6)
QT_WARNING_PUSH
QT_WARNING_DISABLE_DEPRECATED
// deprecated API
item.setTristate(true);
QVERIFY(item.isTristate());
QVERIFY(item.flags() & Qt::ItemIsTristate);
item.setTristate(false);
QVERIFY(!(item.flags() & Qt::ItemIsTristate));
QT_WARNING_POP
#endif
}
@ -382,7 +383,7 @@ void tst_QStandardItem::getSetChild()
QStandardItem item(rows, columns);
bool shouldHaveChildren = (rows > 0) && (columns > 0);
QCOMPARE(item.hasChildren(), shouldHaveChildren);
QCOMPARE(item.child(row, column), static_cast<QStandardItem*>(0));
QCOMPARE(item.child(row, column), nullptr);
QStandardItem *child = new QStandardItem;
item.setChild(row, column, child);
@ -399,11 +400,11 @@ void tst_QStandardItem::getSetChild()
QCOMPARE(item.child(row, column), anotherChild);
QCOMPARE(anotherChild->row(), row);
QCOMPARE(anotherChild->column(), column);
item.setChild(row, column, 0);
item.setChild(row, column, nullptr);
} else {
delete child;
}
QCOMPARE(item.child(row, column), static_cast<QStandardItem*>(0));
QCOMPARE(item.child(row, column), nullptr);
}
void tst_QStandardItem::parent()
@ -411,7 +412,7 @@ void tst_QStandardItem::parent()
{
QStandardItem item;
QStandardItem *child = new QStandardItem;
QCOMPARE(child->parent(), static_cast<QStandardItem*>(0));
QCOMPARE(child->parent(), nullptr);
item.setChild(0, 0, child);
QCOMPARE(child->parent(), &item);
@ -425,7 +426,7 @@ void tst_QStandardItem::parent()
QStandardItem *item = new QStandardItem;
model.appendRow(item);
// parent of a top-level item should be 0
QCOMPARE(item->parent(), static_cast<QStandardItem*>(0));
QCOMPARE(item->parent(), nullptr);
}
}
@ -485,7 +486,7 @@ void tst_QStandardItem::insertColumn()
for (int i = 0; i < count; ++i)
QCOMPARE(item.child(i, column), columnItems.at(i));
for (int i = count; i < item.rowCount(); ++i)
QCOMPARE(item.child(i, column), static_cast<QStandardItem*>(0));
QCOMPARE(item.child(i, column), nullptr);
} else {
QCOMPARE(item.columnCount(), columns);
QCOMPARE(item.rowCount(), rows);
@ -493,14 +494,6 @@ void tst_QStandardItem::insertColumn()
}
}
void tst_QStandardItem::insertColumns_data()
{
}
void tst_QStandardItem::insertColumns()
{
}
void tst_QStandardItem::insertRow_data()
{
QTest::addColumn<int>("rows");
@ -557,7 +550,7 @@ void tst_QStandardItem::insertRow()
for (int i = 0; i < count; ++i)
QCOMPARE(item.child(row, i), rowItems.at(i));
for (int i = count; i < item.columnCount(); ++i)
QCOMPARE(item.child(row, i), static_cast<QStandardItem*>(0));
QCOMPARE(item.child(row, i), nullptr);
} else {
QCOMPARE(item.columnCount(), columns);
QCOMPARE(item.rowCount(), rows);
@ -585,9 +578,8 @@ void tst_QStandardItem::insertRows()
QStandardItem item(rows, columns);
QList<QStandardItem*> items;
for (int i = 0; i < insertCount; ++i) {
for (int i = 0; i < insertCount; ++i)
items.append(new QStandardItem());
}
item.insertRows(insertAt, items);
QCOMPARE(item.rowCount(), rows + insertCount);
@ -659,7 +651,7 @@ void tst_QStandardItem::appendColumn()
for (int i = 0; i < count; ++i)
QCOMPARE(item.child(i, columns), columnItems.at(i));
for (int i = count; i < item.rowCount(); ++i)
QCOMPARE(item.child(i, columns), static_cast<QStandardItem*>(0));
QCOMPARE(item.child(i, columns), nullptr);
// make sure original children remained unchanged
for (int i = 0; i < rows; ++i) {
@ -734,7 +726,7 @@ void tst_QStandardItem::appendRow()
for (int i = 0; i < count; ++i)
QCOMPARE(item.child(rows, i), rowItems.at(i));
for (int i = count; i < item.columnCount(); ++i)
QCOMPARE(item.child(rows, i), static_cast<QStandardItem*>(0));
QCOMPARE(item.child(rows, i), nullptr);
// make sure original children remained unchanged
for (int i = 0; i < rows; ++i) {
@ -753,7 +745,7 @@ void tst_QStandardItem::takeChild()
for (int i = 0; i < item.rowCount(); ++i) {
QCOMPARE(item.takeChild(i), itemList.at(i));
QCOMPARE(item.takeChild(0, 0), static_cast<QStandardItem*>(0));
QCOMPARE(item.takeChild(0, 0), nullptr);
for (int j = i + 1; j < item.rowCount(); ++j)
QCOMPARE(item.child(j), itemList.at(j));
}
@ -938,7 +930,7 @@ void tst_QStandardItem::deleteItem()
for (int j = 0; j < model.columnCount(); ++j) {
QStandardItem *item = model.item(i, j);
delete item;
QCOMPARE(model.item(i, j), static_cast<QStandardItem*>(0));
QCOMPARE(model.item(i, j), nullptr);
}
}
}
@ -995,9 +987,9 @@ void tst_QStandardItem::sortChildren()
item->appendRow(two);
QSignalSpy layoutAboutToBeChangedSpy(
model, SIGNAL(layoutAboutToBeChanged()));
model, &QAbstractItemModel::layoutAboutToBeChanged);
QSignalSpy layoutChangedSpy(
model, SIGNAL(layoutChanged()));
model, &QAbstractItemModel::layoutChanged);
one->sortChildren(0, Qt::DescendingOrder);
// verify sorted
@ -1040,19 +1032,16 @@ void tst_QStandardItem::sortChildren()
class CustomItem : public QStandardItem
{
public:
CustomItem(const QString &text) : QStandardItem(text) { }
CustomItem() { }
virtual ~CustomItem() { }
using QStandardItem::QStandardItem;
virtual int type() const { return QStandardItem::UserType + 1; }
int type() const override { return QStandardItem::UserType + 1; }
virtual QStandardItem *clone() const { return QStandardItem::clone(); }
void emitDataChanged() { QStandardItem::emitDataChanged(); }
virtual bool operator<(const QStandardItem &other) const {
bool operator<(const QStandardItem &other) const override {
return text().length() < other.text().length();
}
using QStandardItem::clone;
using QStandardItem::emitDataChanged;
};
Q_DECLARE_METATYPE(QStandardItem*)
@ -1072,11 +1061,11 @@ void tst_QStandardItem::subclassing()
QStandardItemModel model;
model.appendRow(item);
QSignalSpy itemChangedSpy(&model, SIGNAL(itemChanged(QStandardItem*)));
QSignalSpy itemChangedSpy(&model, &QStandardItemModel::itemChanged);
item->emitDataChanged();
QCOMPARE(itemChangedSpy.count(), 1);
QCOMPARE(itemChangedSpy.at(0).count(), 1);
QCOMPARE(qvariant_cast<QStandardItem*>(itemChangedSpy.at(0).at(0)), (QStandardItem*)item);
QCOMPARE(qvariant_cast<QStandardItem*>(itemChangedSpy.at(0).at(0)), item);
CustomItem *child0 = new CustomItem("cc");
CustomItem *child1 = new CustomItem("bbb");
@ -1085,9 +1074,9 @@ void tst_QStandardItem::subclassing()
item->appendRow(child1);
item->appendRow(child2);
item->sortChildren(0);
QCOMPARE(item->child(0), (QStandardItem*)child2);
QCOMPARE(item->child(1), (QStandardItem*)child0);
QCOMPARE(item->child(2), (QStandardItem*)child1);
QCOMPARE(item->child(0), child2);
QCOMPARE(item->child(1), child0);
QCOMPARE(item->child(2), child1);
}
void tst_QStandardItem::lessThan()

View File

@ -29,7 +29,7 @@
#include <QtTest/QtTest>
#include <qstandarditemmodel.h>
#include <QStandardItemModel>
#include <QTreeView>
#include <private/qtreeview_p.h>
@ -134,28 +134,30 @@ private slots:
void setItemPersistentIndex();
private:
QStandardItemModel *m_model;
QStandardItemModel *m_model = nullptr;
QPersistentModelIndex persistent;
QVector<QModelIndex> rcParent;
QVector<int> rcFirst;
QVector<int> rcLast;
QVector<QModelIndex> rcParent = QVector<QModelIndex>(8);
QVector<int> rcFirst = QVector<int>(8, 0);
QVector<int> rcLast = QVector<int>(8, 0);
QVector<int> currentRoles;
//return true if models have the same structure, and all child have the same text
bool compareModels(QStandardItemModel *model1, QStandardItemModel *model2);
static bool compareModels(QStandardItemModel *model1, QStandardItemModel *model2);
//return true if models have the same structure, and all child have the same text
bool compareItems(QStandardItem *item1, QStandardItem *item2);
static bool compareItems(QStandardItem *item1, QStandardItem *item2);
};
static const int defaultSize = 3;
static constexpr int defaultSize = 3;
Q_DECLARE_METATYPE(QStandardItem*)
Q_DECLARE_METATYPE(Qt::Orientation)
tst_QStandardItemModel::tst_QStandardItemModel() : m_model(0), rcParent(8), rcFirst(8,0), rcLast(8,0)
tst_QStandardItemModel::tst_QStandardItemModel()
{
qRegisterMetaType<QStandardItem*>("QStandardItem*");
qRegisterMetaType<Qt::Orientation>("Qt::Orientation");
qRegisterMetaType<QAbstractItemModel::LayoutChangeHint>("QAbstractItemModel::LayoutChangeHint");
qRegisterMetaType<QList<QPersistentModelIndex>>("QList<QPersistentModelIndex>");
}
/*
@ -171,23 +173,23 @@ tst_QStandardItemModel::tst_QStandardItemModel() : m_model(0), rcParent(8), rcFi
void tst_QStandardItemModel::init()
{
m_model = new QStandardItemModel(defaultSize, defaultSize);
connect(m_model, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)),
this, SLOT(rowsAboutToBeInserted(QModelIndex,int,int)));
connect(m_model, SIGNAL(rowsInserted(QModelIndex,int,int)),
this, SLOT(rowsInserted(QModelIndex,int,int)));
connect(m_model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
this, SLOT(rowsAboutToBeRemoved(QModelIndex,int,int)));
connect(m_model, SIGNAL(rowsRemoved(QModelIndex,int,int)),
this, SLOT(rowsRemoved(QModelIndex,int,int)));
connect(m_model, &QStandardItemModel::rowsAboutToBeInserted,
this, &tst_QStandardItemModel::rowsAboutToBeInserted);
connect(m_model, &QStandardItemModel::rowsInserted,
this, &tst_QStandardItemModel::rowsInserted);
connect(m_model, &QStandardItemModel::rowsAboutToBeRemoved,
this, &tst_QStandardItemModel::rowsAboutToBeRemoved);
connect(m_model, &QStandardItemModel::rowsRemoved,
this, &tst_QStandardItemModel::rowsRemoved);
connect(m_model, SIGNAL(columnsAboutToBeInserted(QModelIndex,int,int)),
this, SLOT(columnsAboutToBeInserted(QModelIndex,int,int)));
connect(m_model, SIGNAL(columnsInserted(QModelIndex,int,int)),
this, SLOT(columnsInserted(QModelIndex,int,int)));
connect(m_model, SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)),
this, SLOT(columnsAboutToBeRemoved(QModelIndex,int,int)));
connect(m_model, SIGNAL(columnsRemoved(QModelIndex,int,int)),
this, SLOT(columnsRemoved(QModelIndex,int,int)));
connect(m_model, &QStandardItemModel::columnsAboutToBeInserted,
this, &tst_QStandardItemModel::columnsAboutToBeInserted);
connect(m_model, &QStandardItemModel::columnsInserted,
this, &tst_QStandardItemModel::columnsInserted);
connect(m_model, &QStandardItemModel::columnsAboutToBeRemoved,
this, &tst_QStandardItemModel::columnsAboutToBeRemoved);
connect(m_model, &QStandardItemModel::columnsRemoved,
this, &tst_QStandardItemModel::columnsRemoved);
connect(m_model, &QAbstractItemModel::dataChanged,
this, [this](const QModelIndex &, const QModelIndex &, const QVector<int> &roles)
@ -201,25 +203,9 @@ void tst_QStandardItemModel::init()
void tst_QStandardItemModel::cleanup()
{
disconnect(m_model, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)),
this, SLOT(rowsAboutToBeInserted(QModelIndex,int,int)));
disconnect(m_model, SIGNAL(rowsInserted(QModelIndex,int,int)),
this, SLOT(rowsInserted(QModelIndex,int,int)));
disconnect(m_model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
this, SLOT(rowsAboutToBeRemoved(QModelIndex,int,int)));
disconnect(m_model, SIGNAL(rowsRemoved(QModelIndex,int,int)),
this, SLOT(rowsRemoved(QModelIndex,int,int)));
disconnect(m_model, SIGNAL(columnsAboutToBeInserted(QModelIndex,int,int)),
this, SLOT(columnsAboutToBeInserted(QModelIndex,int,int)));
disconnect(m_model, SIGNAL(columnsInserted(QModelIndex,int,int)),
this, SLOT(columnsInserted(QModelIndex,int,int)));
disconnect(m_model, SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)),
this, SLOT(columnsAboutToBeRemoved(QModelIndex,int,int)));
disconnect(m_model, SIGNAL(columnsRemoved(QModelIndex,int,int)),
this, SLOT(columnsRemoved(QModelIndex,int,int)));
m_model->disconnect(this);
delete m_model;
m_model = 0;
m_model = nullptr;
}
void tst_QStandardItemModel::insertRow_data()
@ -241,9 +227,9 @@ void tst_QStandardItemModel::insertRow()
QIcon icon;
// default all initial items to DisplayRole: "initalitem"
for (int r=0; r < m_model->rowCount(); ++r) {
for (int c=0; c < m_model->columnCount(); ++c) {
m_model->setData(m_model->index(r,c), "initialitem", Qt::DisplayRole);
for (int r = 0; r < m_model->rowCount(); ++r) {
for (int c = 0; c < m_model->columnCount(); ++c) {
m_model->setData(m_model->index(r, c), "initialitem", Qt::DisplayRole);
}
}
@ -307,7 +293,7 @@ void tst_QStandardItemModel::insertRowsItems()
int rowCount = m_model->rowCount();
QList<QStandardItem *> items;
QStandardItemModel *m = qobject_cast<QStandardItemModel*>(m_model);
QStandardItemModel *m = m_model;
QStandardItem *hiddenRoot = m->invisibleRootItem();
for (int i = 0; i < 3; ++i)
items.append(new QStandardItem(QString::number(i + 10)));
@ -318,7 +304,7 @@ void tst_QStandardItemModel::insertRowsItems()
QCOMPARE(m_model->index(rowCount + 2, 0).data().toInt(), 12);
for (int i = rowCount; i < rowCount + 3; ++i) {
QVERIFY(m->item(i));
QCOMPARE(static_cast<QAbstractItemModel *>(m->item(i)->model()), m_model);
QCOMPARE(m->item(i)->model(), m_model);
}
}
@ -357,9 +343,9 @@ void tst_QStandardItemModel::insertColumn()
QFETCH(int, expectedColumn);
// default all initial items to DisplayRole: "initalitem"
for (int r=0; r < m_model->rowCount(); ++r) {
for (int c=0; c < m_model->columnCount(); ++c) {
m_model->setData(m_model->index(r,c), "initialitem", Qt::DisplayRole);
for (int r = 0; r < m_model->rowCount(); ++r) {
for (int c = 0; c < m_model->columnCount(); ++c) {
m_model->setData(m_model->index(r, c), "initialitem", Qt::DisplayRole);
}
}
@ -475,9 +461,9 @@ void tst_QStandardItemModel::setHeaderData()
QCOMPARE(m_model->headerData(i, orient).toString(), QString::number(i + 1));
QSignalSpy headerDataChangedSpy(
m_model, SIGNAL(headerDataChanged(Qt::Orientation,int,int)));
m_model, &QAbstractItemModel::headerDataChanged);
QSignalSpy dataChangedSpy(
m_model, SIGNAL(dataChanged(QModelIndex,QModelIndex)));
m_model, &QAbstractItemModel::dataChanged);
// insert custom values and check
for (int i = 0; i < count; ++i) {
QString customString = QString("custom") + QString::number(i);
@ -593,14 +579,14 @@ void tst_QStandardItemModel::removingPersistentIndexes()
QVERIFY(m_model->insertRows(0, 10));
QVERIFY(m_model->insertColumns(0, 10));
QObject::connect(m_model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
this, SLOT(checkAboutToBeRemoved()));
QObject::connect(m_model, SIGNAL(rowsRemoved(QModelIndex,int,int)),
this, SLOT(checkRemoved()));
QObject::connect(m_model, SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)),
this, SLOT(checkAboutToBeRemoved()));
QObject::connect(m_model, SIGNAL(columnsRemoved(QModelIndex,int,int)),
this, SLOT(checkRemoved()));
connect(m_model, &QAbstractItemModel::rowsAboutToBeRemoved,
this, &tst_QStandardItemModel::checkAboutToBeRemoved);
connect(m_model, &QAbstractItemModel::rowsRemoved,
this, &tst_QStandardItemModel::checkRemoved);
connect(m_model, &QAbstractItemModel::columnsAboutToBeRemoved,
this, &tst_QStandardItemModel::checkAboutToBeRemoved);
connect(m_model, &QAbstractItemModel::columnsRemoved,
this, &tst_QStandardItemModel::checkRemoved);
// test removeRow
@ -635,14 +621,14 @@ void tst_QStandardItemModel::removingPersistentIndexes()
QVERIFY(m_model->removeColumn(0));
QObject::disconnect(m_model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
this, SLOT(checkAboutToBeRemoved()));
QObject::disconnect(m_model, SIGNAL(rowsRemoved(QModelIndex,int,int)),
this, SLOT(checkRemoved()));
QObject::disconnect(m_model, SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)),
this, SLOT(checkAboutToBeRemoved()));
QObject::disconnect(m_model, SIGNAL(columnsRemoved(QModelIndex,int,int)),
this, SLOT(checkRemoved()));
disconnect(m_model, &QAbstractItemModel::rowsAboutToBeRemoved,
this, &tst_QStandardItemModel::checkAboutToBeRemoved);
disconnect(m_model, &QAbstractItemModel::rowsRemoved,
this, &tst_QStandardItemModel::checkRemoved);
disconnect(m_model, &QAbstractItemModel::columnsAboutToBeRemoved,
this, &tst_QStandardItemModel::checkAboutToBeRemoved);
disconnect(m_model, &QAbstractItemModel::columnsRemoved,
this, &tst_QStandardItemModel::checkRemoved);
}
void tst_QStandardItemModel::updateRowAboutToBeRemoved()
@ -654,8 +640,8 @@ void tst_QStandardItemModel::updateRowAboutToBeRemoved()
void tst_QStandardItemModel::updatingPersistentIndexes()
{
QObject::connect(m_model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
this, SLOT(updateRowAboutToBeRemoved()));
connect(m_model, &QAbstractItemModel::rowsAboutToBeRemoved,
this, &tst_QStandardItemModel::updateRowAboutToBeRemoved);
persistent = m_model->index(1, 0);
QVERIFY(persistent.isValid());
@ -664,8 +650,8 @@ void tst_QStandardItemModel::updatingPersistentIndexes()
QPersistentModelIndex tmp = m_model->index(0, 0);
QCOMPARE(persistent, tmp);
QObject::disconnect(m_model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
this, SLOT(updateRowAboutToBeRemoved()));
disconnect(m_model, &QAbstractItemModel::rowsAboutToBeRemoved,
this, &tst_QStandardItemModel::updateRowAboutToBeRemoved);
}
void tst_QStandardItemModel::modelChanged(ModelChanged change, const QModelIndex &parent,
@ -738,8 +724,8 @@ void tst_QStandardItemModel::data()
QCOMPARE(currentRoles, QVector<int>{});
QIcon icon;
for (int r=0; r < m_model->rowCount(); ++r) {
for (int c=0; c < m_model->columnCount(); ++c) {
for (int r = 0; r < m_model->rowCount(); ++r) {
for (int c = 0; c < m_model->columnCount(); ++c) {
m_model->setData(m_model->index(r,c), "initialitem", Qt::DisplayRole);
QCOMPARE(currentRoles, QVector<int>({Qt::DisplayRole, Qt::EditRole}));
m_model->setData(m_model->index(r,c), "tooltip", Qt::ToolTipRole);
@ -787,9 +773,9 @@ void tst_QStandardItemModel::clear()
QCOMPARE(model.columnCount(), 10);
QCOMPARE(model.rowCount(), 10);
QSignalSpy modelResetSpy(&model, SIGNAL(modelReset()));
QSignalSpy layoutChangedSpy(&model, SIGNAL(layoutChanged()));
QSignalSpy rowsRemovedSpy(&model, SIGNAL(rowsRemoved(QModelIndex,int,int)));
QSignalSpy modelResetSpy(&model, &QStandardItemModel::modelReset);
QSignalSpy layoutChangedSpy(&model, &QStandardItemModel::layoutChanged);
QSignalSpy rowsRemovedSpy(&model, &QStandardItemModel::rowsRemoved);
QAbstractItemModelTester mt(&model);
@ -806,129 +792,35 @@ void tst_QStandardItemModel::clear()
void tst_QStandardItemModel::sort_data()
{
QTest::addColumn<int>("sortOrder");
QTest::addColumn<Qt::SortOrder>("sortOrder");
QTest::addColumn<QStringList>("initial");
QTest::addColumn<QStringList>("expected");
QTest::newRow("flat descending") << static_cast<int>(Qt::DescendingOrder)
<< (QStringList()
<< "delta"
<< "yankee"
<< "bravo"
<< "lima"
<< "charlie"
<< "juliet"
<< "tango"
<< "hotel"
<< "uniform"
<< "alpha"
<< "echo"
<< "golf"
<< "quebec"
<< "foxtrot"
<< "india"
<< "romeo"
<< "november"
<< "oskar"
<< "zulu"
<< "kilo"
<< "whiskey"
<< "mike"
<< "papa"
<< "sierra"
<< "xray"
<< "viktor")
<< (QStringList()
<< "zulu"
<< "yankee"
<< "xray"
<< "whiskey"
<< "viktor"
<< "uniform"
<< "tango"
<< "sierra"
<< "romeo"
<< "quebec"
<< "papa"
<< "oskar"
<< "november"
<< "mike"
<< "lima"
<< "kilo"
<< "juliet"
<< "india"
<< "hotel"
<< "golf"
<< "foxtrot"
<< "echo"
<< "delta"
<< "charlie"
<< "bravo"
<< "alpha");
QTest::newRow("flat ascending") << static_cast<int>(Qt::AscendingOrder)
<< (QStringList()
<< "delta"
<< "yankee"
<< "bravo"
<< "lima"
<< "charlie"
<< "juliet"
<< "tango"
<< "hotel"
<< "uniform"
<< "alpha"
<< "echo"
<< "golf"
<< "quebec"
<< "foxtrot"
<< "india"
<< "romeo"
<< "november"
<< "oskar"
<< "zulu"
<< "kilo"
<< "whiskey"
<< "mike"
<< "papa"
<< "sierra"
<< "xray"
<< "viktor")
<< (QStringList()
<< "alpha"
<< "bravo"
<< "charlie"
<< "delta"
<< "echo"
<< "foxtrot"
<< "golf"
<< "hotel"
<< "india"
<< "juliet"
<< "kilo"
<< "lima"
<< "mike"
<< "november"
<< "oskar"
<< "papa"
<< "quebec"
<< "romeo"
<< "sierra"
<< "tango"
<< "uniform"
<< "viktor"
<< "whiskey"
<< "xray"
<< "yankee"
<< "zulu");
const QStringList unsorted(
{"delta", "yankee", "bravo", "lima", "charlie", "juliet",
"tango", "hotel", "uniform", "alpha", "echo", "golf",
"quebec", "foxtrot", "india", "romeo", "november",
"oskar", "zulu", "kilo", "whiskey", "mike", "papa",
"sierra", "xray" , "viktor"});
QStringList sorted = unsorted;
std::sort(sorted.begin(), sorted.end());
QTest::newRow("flat ascending") << Qt::AscendingOrder
<< unsorted
<< sorted;
std::reverse(sorted.begin(), sorted.end());
QTest::newRow("flat descending") << Qt::DescendingOrder
<< unsorted
<< sorted;
QStringList list;
for (int i=1000; i < 2000; ++i)
for (int i = 1000; i < 2000; ++i)
list.append(QStringLiteral("Number: ") + QString::number(i));
QTest::newRow("large set ascending") << static_cast<int>(Qt::AscendingOrder) << list << list;
QTest::newRow("large set ascending") << Qt::AscendingOrder << list << list;
}
void tst_QStandardItemModel::sort()
{
QFETCH(int, sortOrder);
QFETCH(Qt::SortOrder, sortOrder);
QFETCH(QStringList, initial);
QFETCH(QStringList, expected);
// prepare model
@ -943,12 +835,12 @@ void tst_QStandardItemModel::sort()
}
QSignalSpy layoutAboutToBeChangedSpy(
&model, SIGNAL(layoutAboutToBeChanged()));
&model, &QStandardItemModel::layoutAboutToBeChanged);
QSignalSpy layoutChangedSpy(
&model, SIGNAL(layoutChanged()));
&model, &QStandardItemModel::layoutChanged);
// sort
model.sort(0, static_cast<Qt::SortOrder>(sortOrder));
model.sort(0, sortOrder);
QCOMPARE(layoutAboutToBeChangedSpy.count(), 1);
QCOMPARE(layoutChangedSpy.count(), 1);
@ -964,23 +856,23 @@ void tst_QStandardItemModel::sortRole_data()
{
QTest::addColumn<QStringList>("initialText");
QTest::addColumn<QVariantList>("initialData");
QTest::addColumn<int>("sortRole");
QTest::addColumn<int>("sortOrder");
QTest::addColumn<Qt::ItemDataRole>("sortRole");
QTest::addColumn<Qt::SortOrder>("sortOrder");
QTest::addColumn<QStringList>("expectedText");
QTest::addColumn<QVariantList>("expectedData");
QTest::newRow("sort ascending with Qt::DisplayRole")
<< (QStringList() << "b" << "a" << "c")
<< (QVariantList() << 2 << 3 << 1)
<< static_cast<int>(Qt::DisplayRole)
<< static_cast<int>(Qt::AscendingOrder)
<< Qt::DisplayRole
<< Qt::AscendingOrder
<< (QStringList() << "a" << "b" << "c")
<< (QVariantList() << 3 << 2 << 1);
QTest::newRow("sort ascending with Qt::UserRole")
<< (QStringList() << "a" << "b" << "c")
<< (QVariantList() << 3 << 2 << 1)
<< static_cast<int>(Qt::UserRole)
<< static_cast<int>(Qt::AscendingOrder)
<< Qt::UserRole
<< Qt::AscendingOrder
<< (QStringList() << "c" << "b" << "a")
<< (QVariantList() << 1 << 2 << 3);
}
@ -989,8 +881,8 @@ void tst_QStandardItemModel::sortRole()
{
QFETCH(QStringList, initialText);
QFETCH(QVariantList, initialData);
QFETCH(int, sortRole);
QFETCH(int, sortOrder);
QFETCH(Qt::ItemDataRole, sortRole);
QFETCH(Qt::SortOrder, sortOrder);
QFETCH(QStringList, expectedText);
QFETCH(QVariantList, expectedData);
@ -1002,7 +894,7 @@ void tst_QStandardItemModel::sortRole()
model.appendRow(item);
}
model.setSortRole(sortRole);
model.sort(0, static_cast<Qt::SortOrder>(sortOrder));
model.sort(0, sortOrder);
for (int i = 0; i < expectedText.count(); ++i) {
QStandardItem *item = model.item(i);
QCOMPARE(item->text(), expectedText.at(i));
@ -1033,23 +925,23 @@ void tst_QStandardItemModel::getSetHeaderItem()
{
QStandardItemModel model;
QCOMPARE(model.horizontalHeaderItem(0), static_cast<QStandardItem*>(0));
QCOMPARE(model.horizontalHeaderItem(0), nullptr);
QStandardItem *hheader = new QStandardItem();
model.setHorizontalHeaderItem(0, hheader);
QCOMPARE(model.columnCount(), 1);
QCOMPARE(model.horizontalHeaderItem(0), hheader);
QCOMPARE(hheader->model(), &model);
model.setHorizontalHeaderItem(0, 0);
QCOMPARE(model.horizontalHeaderItem(0), static_cast<QStandardItem*>(0));
model.setHorizontalHeaderItem(0, nullptr);
QCOMPARE(model.horizontalHeaderItem(0), nullptr);
QCOMPARE(model.verticalHeaderItem(0), static_cast<QStandardItem*>(0));
QCOMPARE(model.verticalHeaderItem(0), nullptr);
QStandardItem *vheader = new QStandardItem();
model.setVerticalHeaderItem(0, vheader);
QCOMPARE(model.rowCount(), 1);
QCOMPARE(model.verticalHeaderItem(0), vheader);
QCOMPARE(vheader->model(), &model);
model.setVerticalHeaderItem(0, 0);
QCOMPARE(model.verticalHeaderItem(0), static_cast<QStandardItem*>(0));
model.setVerticalHeaderItem(0, nullptr);
QCOMPARE(model.verticalHeaderItem(0), nullptr);
}
void tst_QStandardItemModel::indexFromItem()
@ -1066,7 +958,7 @@ void tst_QStandardItemModel::indexFromItem()
QCOMPARE(itemIndex.row(), 10);
QCOMPARE(itemIndex.column(), 20);
QCOMPARE(itemIndex.parent(), QModelIndex());
QCOMPARE(itemIndex.model(), (const QAbstractItemModel*)(&model));
QCOMPARE(itemIndex.model(), &model);
QStandardItem *child = new QStandardItem;
item->setChild(4, 2, child);
@ -1081,7 +973,7 @@ void tst_QStandardItemModel::indexFromItem()
QVERIFY(!noSuchIndex.isValid());
delete dummy;
noSuchIndex = model.indexFromItem(0);
noSuchIndex = model.indexFromItem(nullptr);
QVERIFY(!noSuchIndex.isValid());
}
@ -1089,7 +981,7 @@ void tst_QStandardItemModel::itemFromIndex()
{
QStandardItemModel model;
QCOMPARE(model.itemFromIndex(QModelIndex()), (QStandardItem*)0);
QCOMPARE(model.itemFromIndex(QModelIndex()), nullptr);
QStandardItem *item = new QStandardItem;
model.setItem(10, 20, item);
@ -1110,35 +1002,31 @@ void tst_QStandardItemModel::itemFromIndex()
class CustomItem : public QStandardItem
{
public:
CustomItem() : QStandardItem() { }
~CustomItem() { }
int type() const {
return UserType;
}
QStandardItem *clone() const {
return new CustomItem;
}
using QStandardItem::QStandardItem;
int type() const override { return UserType; }
QStandardItem *clone() const override { return new CustomItem; }
};
void tst_QStandardItemModel::getSetItemPrototype()
{
QStandardItemModel model;
QCOMPARE(model.itemPrototype(), static_cast<const QStandardItem*>(0));
QCOMPARE(model.itemPrototype(), nullptr);
const CustomItem *proto = new CustomItem;
model.setItemPrototype(proto);
QCOMPARE(model.itemPrototype(), (const QStandardItem*)proto);
QCOMPARE(model.itemPrototype(), proto);
model.setRowCount(1);
model.setColumnCount(1);
QModelIndex index = model.index(0, 0, QModelIndex());
model.setData(index, "foo");
QStandardItem *item = model.itemFromIndex(index);
QVERIFY(item != 0);
QVERIFY(item != nullptr);
QCOMPARE(item->type(), static_cast<int>(QStandardItem::UserType));
model.setItemPrototype(0);
QCOMPARE(model.itemPrototype(), static_cast<const QStandardItem*>(0));
model.setItemPrototype(nullptr);
QCOMPARE(model.itemPrototype(), nullptr);
}
void tst_QStandardItemModel::getSetItemData()
@ -1175,7 +1063,7 @@ void tst_QStandardItemModel::getSetItemData()
QModelIndex idx = model.index(0, 0, QModelIndex());
QSignalSpy modelDataChangedSpy(
&model, SIGNAL(dataChanged(QModelIndex,QModelIndex)));
&model, &QStandardItemModel::dataChanged);
QVERIFY(model.setItemData(idx, roles));
QCOMPARE(modelDataChangedSpy.count(), 1);
QVERIFY(model.setItemData(idx, roles));
@ -1187,44 +1075,44 @@ void tst_QStandardItemModel::setHeaderLabels_data()
{
QTest::addColumn<int>("rows");
QTest::addColumn<int>("columns");
QTest::addColumn<int>("orientation");
QTest::addColumn<Qt::Orientation>("orientation");
QTest::addColumn<QStringList>("labels");
QTest::addColumn<QStringList>("expectedLabels");
QTest::newRow("horizontal labels")
<< 1
<< 4
<< int(Qt::Horizontal)
<< Qt::Horizontal
<< (QStringList() << "a" << "b" << "c" << "d")
<< (QStringList() << "a" << "b" << "c" << "d");
QTest::newRow("vertical labels")
<< 4
<< 1
<< int(Qt::Vertical)
<< Qt::Vertical
<< (QStringList() << "a" << "b" << "c" << "d")
<< (QStringList() << "a" << "b" << "c" << "d");
QTest::newRow("too few (horizontal)")
<< 1
<< 4
<< int(Qt::Horizontal)
<< Qt::Horizontal
<< (QStringList() << "a" << "b")
<< (QStringList() << "a" << "b" << "3" << "4");
QTest::newRow("too few (vertical)")
<< 4
<< 1
<< int(Qt::Vertical)
<< Qt::Vertical
<< (QStringList() << "a" << "b")
<< (QStringList() << "a" << "b" << "3" << "4");
QTest::newRow("too many (horizontal)")
<< 1
<< 2
<< int(Qt::Horizontal)
<< Qt::Horizontal
<< (QStringList() << "a" << "b" << "c" << "d")
<< (QStringList() << "a" << "b" << "c" << "d");
QTest::newRow("too many (vertical)")
<< 2
<< 1
<< int(Qt::Vertical)
<< Qt::Vertical
<< (QStringList() << "a" << "b" << "c" << "d")
<< (QStringList() << "a" << "b" << "c" << "d");
}
@ -1233,20 +1121,18 @@ void tst_QStandardItemModel::setHeaderLabels()
{
QFETCH(int, rows);
QFETCH(int, columns);
QFETCH(int, orientation);
QFETCH(Qt::Orientation, orientation);
QFETCH(QStringList, labels);
QFETCH(QStringList, expectedLabels);
QStandardItemModel model(rows, columns);
QSignalSpy columnsInsertedSpy(
&model, SIGNAL(columnsInserted(QModelIndex,int,int)));
QSignalSpy rowsInsertedSpy(
&model, SIGNAL(rowsInserted(QModelIndex,int,int)));
QSignalSpy columnsInsertedSpy(&model, &QAbstractItemModel::columnsInserted);
QSignalSpy rowsInsertedSpy(&model, &QAbstractItemModel::rowsInserted);
if (orientation == Qt::Horizontal)
model.setHorizontalHeaderLabels(labels);
else
model.setVerticalHeaderLabels(labels);
for (int i = 0; i < expectedLabels.count(); ++i)
QCOMPARE(model.headerData(i, Qt::Orientation(orientation)).toString(), expectedLabels.at(i));
QCOMPARE(model.headerData(i, orientation).toString(), expectedLabels.at(i));
QCOMPARE(columnsInsertedSpy.count(),
(orientation == Qt::Vertical) ? 0 : labels.count() > columns);
QCOMPARE(rowsInsertedSpy.count(),
@ -1257,10 +1143,8 @@ void tst_QStandardItemModel::itemDataChanged()
{
QStandardItemModel model(6, 4);
QStandardItem item;
QSignalSpy dataChangedSpy(
&model, SIGNAL(dataChanged(QModelIndex,QModelIndex)));
QSignalSpy itemChangedSpy(
&model, SIGNAL(itemChanged(QStandardItem*)));
QSignalSpy dataChangedSpy(&model, &QStandardItemModel::dataChanged);
QSignalSpy itemChangedSpy(&model, &QStandardItemModel::itemChanged);
model.setItem(0, &item);
QCOMPARE(dataChangedSpy.count(), 1);
@ -1304,19 +1188,17 @@ void tst_QStandardItemModel::takeHeaderItem()
{
QStandardItemModel model;
// set header items
QStandardItem *hheader = new QStandardItem();
model.setHorizontalHeaderItem(0, hheader);
QStandardItem *vheader = new QStandardItem();
model.setVerticalHeaderItem(0, vheader);
QScopedPointer<QStandardItem> hheader(new QStandardItem());
model.setHorizontalHeaderItem(0, hheader.get());
QScopedPointer<QStandardItem> vheader(new QStandardItem());
model.setVerticalHeaderItem(0, vheader.get());
// take header items
QCOMPARE(model.takeHorizontalHeaderItem(0), hheader);
QCOMPARE(model.takeVerticalHeaderItem(0), vheader);
QCOMPARE(hheader->model(), static_cast<QStandardItemModel*>(0));
QCOMPARE(vheader->model(), static_cast<QStandardItemModel*>(0));
QCOMPARE(model.takeHorizontalHeaderItem(0), static_cast<QStandardItem*>(0));
QCOMPARE(model.takeVerticalHeaderItem(0), static_cast<QStandardItem*>(0));
delete hheader;
delete vheader;
QCOMPARE(model.takeHorizontalHeaderItem(0), hheader.get());
QCOMPARE(model.takeVerticalHeaderItem(0), vheader.get());
QCOMPARE(hheader->model(), nullptr);
QCOMPARE(vheader->model(), nullptr);
QCOMPARE(model.takeHorizontalHeaderItem(0), nullptr);
QCOMPARE(model.takeVerticalHeaderItem(0), nullptr);
}
void tst_QStandardItemModel::useCase1()
@ -1326,7 +1208,7 @@ void tst_QStandardItemModel::useCase1()
QStandardItemModel model(rows, columns);
for (int i = 0; i < model.rowCount(); ++i) {
for (int j = 0; j < model.columnCount(); ++j) {
QCOMPARE(model.item(i, j), static_cast<QStandardItem*>(0));
QCOMPARE(model.item(i, j), nullptr);
QStandardItem *item = new QStandardItem();
model.setItem(i, j, item);
@ -1361,7 +1243,7 @@ static void createChildren(QStandardItemModel *model, QStandardItem *parent, int
QStandardItem *theItem = model->itemFromIndex(index);
QCOMPARE(theItem, item);
QStandardItem *theParent = model->itemFromIndex(parentIndex);
QCOMPARE(theParent, (level == 0) ? (QStandardItem*)0 : parent);
QCOMPARE(theParent, (level == 0) ? static_cast<QStandardItem *>(nullptr) : parent);
}
{
@ -1382,7 +1264,7 @@ void tst_QStandardItemModel::useCase2()
void tst_QStandardItemModel::useCase3()
{
// create the tree structure first
QStandardItem *childItem = 0;
QStandardItem *childItem = nullptr;
for (int i = 0; i < 100; ++i) {
QStandardItem *item = new QStandardItem(QStringLiteral("item ") + QString::number(i));
if (childItem)
@ -1395,7 +1277,7 @@ void tst_QStandardItemModel::useCase3()
model.appendRow(childItem);
// make sure each item has the correct model and parent
QStandardItem *parentItem = 0;
QStandardItem *parentItem = nullptr;
while (childItem) {
QCOMPARE(childItem->model(), &model);
QCOMPARE(childItem->parent(), parentItem);
@ -1406,10 +1288,10 @@ void tst_QStandardItemModel::useCase3()
// take the item, make sure model is set to 0, but that parents are the same
childItem = model.takeItem(0);
{
parentItem = 0;
parentItem = nullptr;
QStandardItem *item = childItem;
while (item) {
QCOMPARE(item->model(), static_cast<QStandardItemModel*>(0));
QCOMPARE(item->model(), nullptr);
QCOMPARE(item->parent(), parentItem);
parentItem = item;
item = item->child(0);
@ -1424,7 +1306,7 @@ void tst_QStandardItemModel::setNullChild()
model.setColumnCount(2);
createChildren(&model, model.invisibleRootItem(), 0);
QStandardItem *item = model.item(0);
QSignalSpy spy(&model, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector<int>)));
QSignalSpy spy(&model, &QAbstractItemModel::dataChanged);
item->setChild(0, nullptr);
QCOMPARE(item->child(0), nullptr);
QCOMPARE(spy.count(), 1);
@ -1436,7 +1318,7 @@ void tst_QStandardItemModel::deleteChild()
model.setColumnCount(2);
createChildren(&model, model.invisibleRootItem(), 0);
QStandardItem *item = model.item(0);
QSignalSpy spy(&model, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector<int>)));
QSignalSpy spy(&model, &QAbstractItemModel::dataChanged);
delete item->child(0);
QCOMPARE(item->child(0), nullptr);
QCOMPARE(spy.count(), 1);
@ -1471,7 +1353,7 @@ bool tst_QStandardItemModel::compareItems(QStandardItem *item1, QStandardItem *i
return true;
if (!item1 || !item2)
return false;
if (item1->text() != item2->text()){
if (item1->text() != item2->text()) {
qDebug() << item1->text() << item2->text();
return false;
}
@ -1483,39 +1365,36 @@ bool tst_QStandardItemModel::compareItems(QStandardItem *item1, QStandardItem *i
// qDebug() << "ColumnCount" << item1->text() << item1->columnCount() << item2->columnCount();
return false;
}
for (int row = 0; row < item1->columnCount(); row++)
for (int row = 0; row < item1->columnCount(); row++) {
for (int col = 0; col < item1->columnCount(); col++) {
if (!compareItems(item1->child(row, col), item2->child(row, col)))
return false;
if (!compareItems(item1->child(row, col), item2->child(row, col)))
return false;
}
}
return true;
}
static QStandardItem *itemFromText(QStandardItem *parent, const QString &text)
{
QStandardItem *item = 0;
for(int i = 0; i < parent->columnCount(); i++)
for(int j = 0; j < parent->rowCount(); j++) {
QStandardItem *item = nullptr;
for (int i = 0; i < parent->columnCount(); i++) {
for (int j = 0; j < parent->rowCount(); j++) {
QStandardItem *child = parent->child(j, i);
if (!child)
continue;
QStandardItem *child = parent->child(j, i);
if(!child)
continue;
if (child->text() == text) {
if (item) {
return 0;
if (child->text() == text) {
if (item)
return nullptr;
item = child;
}
item = child;
}
QStandardItem *candidate = itemFromText(child, text);
if(candidate) {
if (item) {
return 0;
QStandardItem *candidate = itemFromText(child, text);
if (candidate) {
if (item)
return nullptr;
item = candidate;
}
item = candidate;
}
}
return item;

View File

@ -506,29 +506,67 @@ void tst_QGuiApplication::keyboardModifiers()
window->close();
}
/*
Compare actual against expected but ignore unset roles.
Comparing palettes via operator== will compare all roles.
*/
static bool palettesMatch(const QPalette &actual, const QPalette &expected)
{
if (actual.resolve() != expected.resolve())
return false;
for (int i = 0; i < QPalette::NColorGroups; i++) {
for (int j = 0; j < QPalette::NColorRoles; j++) {
const auto g = QPalette::ColorGroup(i);
const auto r = QPalette::ColorRole(j);
if (expected.isBrushSet(g, r)) {
if (actual.brush(g, r) != expected.brush(g, r))
return false;
}
}
}
return true;
}
void tst_QGuiApplication::palette()
{
// Getting the palette before application construction should work
QPalette paletteBeforeAppConstruction = QGuiApplication::palette();
// And should be reflected in the default constructed palette
QCOMPARE(paletteBeforeAppConstruction, QPalette());
int argc = 1;
char *argv[] = { const_cast<char*>("tst_qguiapplication") };
QGuiApplication app(argc, argv);
// The same should be true after application construction
QCOMPARE(QGuiApplication::palette(), QPalette());
// The default application palette is not resolved
QVERIFY(!QGuiApplication::palette().resolve());
QSignalSpy signalSpy(&app, SIGNAL(paletteChanged(QPalette)));
QPalette oldPalette = QGuiApplication::palette();
QPalette newPalette = QPalette(Qt::red);
QGuiApplication::setPalette(newPalette);
QCOMPARE(QGuiApplication::palette(), newPalette);
QVERIFY(palettesMatch(QGuiApplication::palette(), newPalette));
QCOMPARE(signalSpy.count(), 1);
QCOMPARE(signalSpy.at(0).at(0), QVariant(newPalette));
QVERIFY(palettesMatch(signalSpy.at(0).at(0).value<QPalette>(), newPalette));
QCOMPARE(QGuiApplication::palette(), QPalette());
QGuiApplication::setPalette(oldPalette);
QCOMPARE(QGuiApplication::palette(), oldPalette);
QVERIFY(palettesMatch(QGuiApplication::palette(), oldPalette));
QCOMPARE(signalSpy.count(), 2);
QCOMPARE(signalSpy.at(1).at(0), QVariant(oldPalette));
QVERIFY(palettesMatch(signalSpy.at(1).at(0).value<QPalette>(), oldPalette));
QCOMPARE(QGuiApplication::palette(), QPalette());
QGuiApplication::setPalette(oldPalette);
QCOMPARE(QGuiApplication::palette(), oldPalette);
QVERIFY(palettesMatch(QGuiApplication::palette(), oldPalette));
QCOMPARE(signalSpy.count(), 2);
QCOMPARE(QGuiApplication::palette(), QPalette());
}
void tst_QGuiApplication::font()
@ -1073,6 +1111,9 @@ void tst_QGuiApplication::testSetPaletteAttribute()
QGuiApplication::setPalette(palette);
QVERIFY(QCoreApplication::testAttribute(Qt::AA_SetPalette));
QGuiApplication::setPalette(QPalette());
QVERIFY(!QCoreApplication::testAttribute(Qt::AA_SetPalette));
}
// Test that static functions do not crash if there is no application instance.

View File

@ -43,6 +43,8 @@ private slots:
void mslResourceMapping();
void loadV3();
void serializeShaderDesc();
void comparison();
void loadV4();
};
static QShader getShader(const QString &name)
@ -353,36 +355,63 @@ void tst_QShader::serializeShaderDesc()
QShaderDescription desc;
QVERIFY(!desc.isValid());
const QByteArray data = desc.toCbor();
QByteArray data;
{
QBuffer buf(&data);
QDataStream ds(&buf);
QVERIFY(buf.open(QIODevice::WriteOnly));
desc.serialize(&ds);
}
QVERIFY(!data.isEmpty());
QShaderDescription desc2 = QShaderDescription::fromCbor(data);
QVERIFY(!desc2.isValid());
{
QBuffer buf(&data);
QDataStream ds(&buf);
QVERIFY(buf.open(QIODevice::ReadOnly));
QShaderDescription desc2 = QShaderDescription::deserialize(&ds);
QVERIFY(!desc2.isValid());
}
}
// a QShaderDescription with inputs, outputs, uniform block and combined image sampler
{
QShader s = getShader(QLatin1String(":/data/texture_all_v3.frag.qsb"));
QShader s = getShader(QLatin1String(":/data/texture_all_v4.frag.qsb"));
QVERIFY(s.isValid());
const QShaderDescription desc = s.description();
QVERIFY(desc.isValid());
const QByteArray data = desc.toCbor();
QByteArray data;
{
QBuffer buf(&data);
QDataStream ds(&buf);
QVERIFY(buf.open(QIODevice::WriteOnly));
desc.serialize(&ds);
}
QVERIFY(!data.isEmpty());
QShaderDescription desc2;
QVERIFY(!desc2.isValid());
QVERIFY(!(desc == desc2));
QVERIFY(desc != desc2);
{
QShaderDescription desc2;
QVERIFY(!desc2.isValid());
QVERIFY(!(desc == desc2));
QVERIFY(desc != desc2);
}
desc2 = QShaderDescription::fromCbor(data);
QVERIFY(desc2.isValid());
QCOMPARE(desc, desc2);
{
QBuffer buf(&data);
QDataStream ds(&buf);
QVERIFY(buf.open(QIODevice::ReadOnly));
QShaderDescription desc2 = QShaderDescription::deserialize(&ds);
QVERIFY(desc2.isValid());
QCOMPARE(desc, desc2);
}
}
}
void tst_QShader::comparison()
{
// exercise QShader and QShaderDescription comparisons
{
QShader s1 = getShader(QLatin1String(":/data/texture_all_v3.frag.qsb"));
QShader s1 = getShader(QLatin1String(":/data/texture_all_v4.frag.qsb"));
QVERIFY(s1.isValid());
QShader s2 = getShader(QLatin1String(":/data/color_all_v1.vert.qsb"));
QVERIFY(s2.isValid());
@ -393,6 +422,93 @@ void tst_QShader::serializeShaderDesc()
QVERIFY(s1 != s2);
QVERIFY(s1.description() != s2.description());
}
{
QShader s1 = getShader(QLatin1String(":/data/texture_all_v4.frag.qsb"));
QVERIFY(s1.isValid());
QShader s2 = getShader(QLatin1String(":/data/texture_all_v4.frag.qsb"));
QVERIFY(s2.isValid());
QVERIFY(s1.description().isValid());
QVERIFY(s2.description().isValid());
QVERIFY(s1 == s2);
QVERIFY(s1.description() == s2.description());
}
}
void tst_QShader::loadV4()
{
// qsb version 4: QShaderDescription is serialized via QDataStream. Ensure the deserialized data is as expected.
QShader s = getShader(QLatin1String(":/data/texture_all_v4.frag.qsb"));
QVERIFY(s.isValid());
QCOMPARE(QShaderPrivate::get(&s)->qsbVersion, 4);
const QVector<QShaderKey> 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;
}
}
}
#include <tst_qshader.moc>

View File

@ -279,7 +279,7 @@ void tst_Http2::singleRequest()
QVERIFY(prefaceOK);
QVERIFY(serverGotSettingsACK);
QCOMPARE(reply->error(), QNetworkReply::NoError);
QCOMPARE(reply->networkError(), QNetworkReply::NoError);
QVERIFY(reply->isFinished());
}
@ -444,7 +444,7 @@ void tst_Http2::pushPromise()
QVERIFY(prefaceOK);
QVERIFY(serverGotSettingsACK);
QCOMPARE(reply->error(), QNetworkReply::NoError);
QCOMPARE(reply->networkError(), QNetworkReply::NoError);
QVERIFY(reply->isFinished());
// Now, the most interesting part!
@ -466,7 +466,7 @@ void tst_Http2::pushPromise()
QCOMPARE(nSentRequests, 0);
// Decreased by replyFinished():
QCOMPARE(nRequests, 0);
QCOMPARE(reply->error(), QNetworkReply::NoError);
QCOMPARE(reply->networkError(), QNetworkReply::NoError);
QVERIFY(reply->isFinished());
}
@ -511,7 +511,7 @@ void tst_Http2::goaway()
QNetworkRequest request(url);
request.setAttribute(QNetworkRequest::Http2AllowedAttribute, QVariant(true));
replies[i] = manager->get(request);
QCOMPARE(replies[i]->error(), QNetworkReply::NoError);
QCOMPARE(replies[i]->networkError(), QNetworkReply::NoError);
void (QNetworkReply::*errorSignal)(QNetworkReply::NetworkError) =
&QNetworkReply::error;
connect(replies[i], errorSignal, this, &tst_Http2::replyFinishedWithError);
@ -671,7 +671,7 @@ void tst_Http2::connectToHost()
connect(reply, &QNetworkReply::finished, [this, reply]() {
--nRequests;
eventLoop.exitLoop();
QCOMPARE(reply->error(), QNetworkReply::NoError);
QCOMPARE(reply->networkError(), QNetworkReply::NoError);
QVERIFY(reply->isFinished());
// Nothing received back:
QVERIFY(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).isNull());
@ -698,7 +698,7 @@ void tst_Http2::connectToHost()
QVERIFY(prefaceOK);
QVERIFY(serverGotSettingsACK);
QCOMPARE(reply->error(), QNetworkReply::NoError);
QCOMPARE(reply->networkError(), QNetworkReply::NoError);
QVERIFY(reply->isFinished());
}
@ -927,10 +927,10 @@ void tst_Http2::replyFinished()
QVERIFY(nRequests);
if (const auto reply = qobject_cast<QNetworkReply *>(sender())) {
if (reply->error() != QNetworkReply::NoError)
if (reply->networkError() != QNetworkReply::NoError)
stopEventLoop();
QCOMPARE(reply->error(), QNetworkReply::NoError);
QCOMPARE(reply->networkError(), QNetworkReply::NoError);
const QVariant http2Used(reply->attribute(QNetworkRequest::Http2WasUsedAttribute));
if (!http2Used.isValid() || !http2Used.toBool())
@ -960,9 +960,9 @@ void tst_Http2::replyFinishedWithError()
if (const auto reply = qobject_cast<QNetworkReply *>(sender())) {
// For now this is a 'generic' code, it just verifies some error was
// reported without testing its type.
if (reply->error() == QNetworkReply::NoError)
if (reply->networkError() == QNetworkReply::NoError)
stopEventLoop();
QVERIFY(reply->error() != QNetworkReply::NoError);
QVERIFY(reply->networkError() != QNetworkReply::NoError);
}
--nRequests;

View File

@ -322,10 +322,10 @@ void tst_QAbstractNetworkCache::runTest()
QByteArray secondData = reply2->readAll();
if (!fetchFromCache && cacheLoadControl == QNetworkRequest::AlwaysCache) {
QCOMPARE(reply2->error(), QNetworkReply::ContentNotFoundError);
QCOMPARE(reply2->networkError(), QNetworkReply::ContentNotFoundError);
QCOMPARE(secondData, QByteArray());
} else {
QCOMPARE(reply2->error(), QNetworkReply::NoError);
QCOMPARE(reply2->networkError(), QNetworkReply::NoError);
QCOMPARE(QString(secondData), QString(goodData));
QCOMPARE(secondData, goodData);
QCOMPARE(reply2->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
@ -375,12 +375,12 @@ void tst_QAbstractNetworkCache::checkSynchronous()
QByteArray secondData = reply2->readAll();
if (!fetchFromCache && cacheLoadControl == QNetworkRequest::AlwaysCache) {
QCOMPARE(reply2->error(), QNetworkReply::ContentNotFoundError);
QCOMPARE(reply2->networkError(), QNetworkReply::ContentNotFoundError);
QCOMPARE(secondData, QByteArray());
} else {
if (reply2->error() != QNetworkReply::NoError)
if (reply2->networkError() != QNetworkReply::NoError)
qDebug() << reply2->errorString();
QCOMPARE(reply2->error(), QNetworkReply::NoError);
QCOMPARE(reply2->networkError(), QNetworkReply::NoError);
QCOMPARE(QString(secondData), QString(goodData));
QCOMPARE(secondData, goodData);
QCOMPARE(reply2->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);

File diff suppressed because it is too large Load Diff

View File

@ -149,7 +149,7 @@ public slots:
void requestFinished(QNetworkReply *reply)
{
QCOMPARE(reply->error(), QNetworkReply::NoError);
QCOMPARE(reply->networkError(), QNetworkReply::NoError);
reply->deleteLater();
}

View File

@ -536,7 +536,7 @@ void tst_qnetworkreply::echoPerformance()
connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()), Qt::QueuedConnection);
QTestEventLoop::instance().enterLoop(5);
QVERIFY(!QTestEventLoop::instance().timeout());
QVERIFY(reply->error() == QNetworkReply::NoError);
QVERIFY(reply->networkError() == QNetworkReply::NoError);
delete reply;
}
}
@ -561,7 +561,7 @@ void tst_qnetworkreply::preConnectEncrypted()
QPair<QNetworkReply *, qint64> normalResult = runGetRequest(&manager, request);
QNetworkReply *normalReply = normalResult.first;
QVERIFY(!QTestEventLoop::instance().timeout());
QVERIFY(normalReply->error() == QNetworkReply::NoError);
QVERIFY(normalReply->networkError() == QNetworkReply::NoError);
qint64 normalElapsed = normalResult.second;
// clear all caches again
@ -580,7 +580,7 @@ void tst_qnetworkreply::preConnectEncrypted()
QPair<QNetworkReply *, qint64> preConnectResult = runGetRequest(&manager, request);
QNetworkReply *preConnectReply = normalResult.first;
QVERIFY(!QTestEventLoop::instance().timeout());
QVERIFY(preConnectReply->error() == QNetworkReply::NoError);
QVERIFY(preConnectReply->networkError() == QNetworkReply::NoError);
qint64 preConnectElapsed = preConnectResult.second;
qDebug() << request.url().toString() << "full request:" << normalElapsed
<< "ms, pre-connect request:" << preConnectElapsed << "ms, difference:"
@ -635,7 +635,7 @@ void tst_qnetworkreply::uploadPerformance()
QTimer::singleShot(5000, &generator, SLOT(stop()));
QTestEventLoop::instance().enterLoop(30);
QCOMPARE(reply->error(), QNetworkReply::NoError);
QCOMPARE(reply->networkError(), QNetworkReply::NoError);
QVERIFY(!QTestEventLoop::instance().timeout());
}
@ -661,7 +661,7 @@ void tst_qnetworkreply::httpUploadPerformance()
reader.exit();
reader.wait();
QVERIFY(reply->isFinished());
QCOMPARE(reply->error(), QNetworkReply::NoError);
QCOMPARE(reply->networkError(), QNetworkReply::NoError);
QVERIFY(!QTestEventLoop::instance().timeout());
qDebug() << "tst_QNetworkReply::httpUploadPerformance" << elapsed << "msec, "
@ -722,7 +722,7 @@ void tst_qnetworkreply::httpDownloadPerformance()
QTime time;
time.start();
QTestEventLoop::instance().enterLoop(40);
QCOMPARE(reply->error(), QNetworkReply::NoError);
QCOMPARE(reply->networkError(), QNetworkReply::NoError);
QVERIFY(!QTestEventLoop::instance().timeout());
qint64 elapsed = time.elapsed();
@ -804,7 +804,7 @@ void tst_qnetworkreply::httpDownloadPerformanceDownloadBuffer()
QBENCHMARK_ONCE {
QTestEventLoop::instance().enterLoop(40);
QCOMPARE(reply->error(), QNetworkReply::NoError);
QCOMPARE(reply->networkError(), QNetworkReply::NoError);
QVERIFY(reply->isFinished());
QVERIFY(!QTestEventLoop::instance().timeout());
}
@ -839,7 +839,7 @@ public slots:
}
QNetworkReply *reply = qobject_cast<QNetworkReply*>(sender());
if (reply) {
QVERIFY(reply->error() == QNetworkReply::NoError);
QVERIFY(reply->networkError() == QNetworkReply::NoError);
qDebug() << "time =" << timeOneRequest.elapsed() << "ms";
timeList.append(timeOneRequest.elapsed());
}
@ -894,7 +894,7 @@ void tst_qnetworkreply::runHttpsUploadRequest(const QByteArray &data, const QNet
connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
QTestEventLoop::instance().enterLoop(15);
QVERIFY(!QTestEventLoop::instance().timeout());
QCOMPARE(reply->error(), QNetworkReply::NoError);
QCOMPARE(reply->networkError(), QNetworkReply::NoError);
reply->deleteLater();
}
@ -934,7 +934,7 @@ void tst_qnetworkreply::preConnect()
QPair<QNetworkReply *, qint64> normalResult = runGetRequest(&manager, request);
QNetworkReply *normalReply = normalResult.first;
QVERIFY(!QTestEventLoop::instance().timeout());
QVERIFY(normalReply->error() == QNetworkReply::NoError);
QVERIFY(normalReply->networkError() == QNetworkReply::NoError);
qint64 normalElapsed = normalResult.second;
// clear all caches again
@ -954,7 +954,7 @@ void tst_qnetworkreply::preConnect()
QPair<QNetworkReply *, qint64> preConnectResult = runGetRequest(&manager, request);
QNetworkReply *preConnectReply = normalResult.first;
QVERIFY(!QTestEventLoop::instance().timeout());
QVERIFY(preConnectReply->error() == QNetworkReply::NoError);
QVERIFY(preConnectReply->networkError() == QNetworkReply::NoError);
qint64 preConnectElapsed = preConnectResult.second;
qDebug() << request.url().toString() << "full request:" << normalElapsed
<< "ms, pre-connect request:" << preConnectElapsed << "ms, difference:"

View File

@ -0,0 +1,6 @@
qsb --glsl "300 es,120" --hlsl 50 --msl 12 cubemap_oneface.vert -o cubemap_oneface.vert.qsb
qsb --glsl "300 es,120" --hlsl 50 --msl 12 cubemap_oneface.frag -o cubemap_oneface.frag.qsb
qsb --glsl "300 es,120" --hlsl 50 --msl 12 cubemap_mrt.vert -o cubemap_mrt.vert.qsb
qsb --glsl "300 es,120" --hlsl 50 --msl 12 cubemap_mrt.frag -o cubemap_mrt.frag.qsb
qsb --glsl "300 es,120" --hlsl 50 --msl 12 cubemap_sample.vert -o cubemap_sample.vert.qsb
qsb --glsl "300 es,120" --hlsl 50 --msl 12 cubemap_sample.frag -o cubemap_sample.frag.qsb

View File

@ -0,0 +1,28 @@
#version 440
layout(location = 0) out vec4 c0;
layout(location = 1) out vec4 c1;
layout(location = 2) out vec4 c2;
layout(location = 3) out vec4 c3;
layout(location = 4) out vec4 c4;
layout(location = 5) out vec4 c5;
layout(std140, binding = 0) uniform buf {
mat4 mvp;
vec3 color0;
vec3 color1;
vec3 color2;
vec3 color3;
vec3 color4;
vec3 color5;
} ubuf;
void main()
{
c0 = vec4(ubuf.color0, 1.0);
c1 = vec4(ubuf.color1, 1.0);
c2 = vec4(ubuf.color2, 1.0);
c3 = vec4(ubuf.color3, 1.0);
c4 = vec4(ubuf.color4, 1.0);
c5 = vec4(ubuf.color5, 1.0);
}

Binary file not shown.

View File

@ -0,0 +1,20 @@
#version 440
layout(location = 0) in vec4 position;
layout(std140, binding = 0) uniform buf {
mat4 mvp;
vec3 color0;
vec3 color1;
vec3 color2;
vec3 color3;
vec3 color4;
vec3 color5;
} ubuf;
out gl_PerVertex { vec4 gl_Position; };
void main()
{
gl_Position = ubuf.mvp * position;
}

Binary file not shown.

View File

@ -0,0 +1,13 @@
#version 440
layout(location = 0) out vec4 fragColor;
layout(std140, binding = 0) uniform buf {
mat4 mvp;
vec3 color;
} ubuf;
void main()
{
fragColor = vec4(ubuf.color, 1.0);
}

View File

@ -0,0 +1,15 @@
#version 440
layout(location = 0) in vec4 position;
layout(std140, binding = 0) uniform buf {
mat4 mvp;
vec3 color;
} ubuf;
out gl_PerVertex { vec4 gl_Position; };
void main()
{
gl_Position = ubuf.mvp * position;
}

View File

@ -0,0 +1,466 @@
/****************************************************************************
**
** Copyright (C) 2020 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the examples of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** 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.
**
** BSD License Usage
** Alternatively, you may use this file under the terms of the BSD license
** as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of The Qt Company Ltd nor the names of its
** contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
** $QT_END_LICENSE$
**
****************************************************************************/
// Demonstrates rendering to two cubemaps in two different ways:
// - one by one, to each face,
// - if the supported max number of color attachments is greater than 4: in
// one go with all 6 faces attached as render targets.
//
// Finally, show what we got in a skybox-ish thing. Press the arrow keys to
// switch between the two cubemaps. (the only difference should be their
// background clear color)
#define EXAMPLEFW_KEYPRESS_EVENTS
#include "../shared/examplefw.h"
#include "../shared/cube.h"
// each face is 512x512
static const QSize cubemapSize(512, 512);
// each cubemap face gets a 256x256 quad in the center
static float halfQuadVertexData[] =
{ // Y up, CCW
-0.5f, 0.5f,
-0.5f, -0.5f,
0.5f, -0.5f,
0.5f, 0.5f,
};
static quint16 halfQuadIndexData[] =
{
0, 1, 2, 0, 2, 3
};
struct {
QVector<QRhiResource *> releasePool;
QRhiTexture *cubemap1 = nullptr;
QRhiTexture *cubemap2 = nullptr;
bool canDoMrt = false;
QRhiBuffer *half_quad_vbuf = nullptr;
QRhiBuffer *half_quad_ibuf = nullptr;
QRhiBuffer *oneface_ubuf = nullptr;
int ubufSizePerFace;
QRhiTextureRenderTarget *oneface_rt[6];
QRhiRenderPassDescriptor *oneface_rp = nullptr;
QRhiShaderResourceBindings *oneface_srb = nullptr;
QRhiGraphicsPipeline *oneface_ps = nullptr;
QRhiBuffer *mrt_ubuf = nullptr;
QRhiTextureRenderTarget *mrt_rt = nullptr;
QRhiRenderPassDescriptor *mrt_rp = nullptr;
QRhiShaderResourceBindings *mrt_srb = nullptr;
QRhiGraphicsPipeline *mrt_ps = nullptr;
QRhiBuffer *vbuf = nullptr;
QRhiBuffer *ubuf = nullptr;
QRhiSampler *sampler = nullptr;
QRhiShaderResourceBindings *srb = nullptr;
QRhiGraphicsPipeline *ps = nullptr;
QRhiResourceUpdateBatch *initialUpdates = nullptr;
QMatrix4x4 winProj;
float rx = 0;
} d;
void initializePerFaceRendering(QRhi *rhi)
{
d.cubemap1 = rhi->newTexture(QRhiTexture::RGBA8, cubemapSize, 1, QRhiTexture::CubeMap | QRhiTexture::RenderTarget);
d.cubemap1->build();
d.releasePool << d.cubemap1;
d.ubufSizePerFace = rhi->ubufAligned(64 + 12);
d.oneface_ubuf = rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, d.ubufSizePerFace * 6);
d.oneface_ubuf->build();
d.releasePool << d.oneface_ubuf;
for (int face = 0; face < 6; ++face) {
QRhiColorAttachment att(d.cubemap1);
att.setLayer(face);
QRhiTextureRenderTargetDescription rtDesc(att);
d.oneface_rt[face] = rhi->newTextureRenderTarget(rtDesc);
if (face == 0) {
d.oneface_rp = d.oneface_rt[0]->newCompatibleRenderPassDescriptor();
d.releasePool << d.oneface_rp;
}
d.oneface_rt[face]->setRenderPassDescriptor(d.oneface_rp);
d.oneface_rt[face]->build();
d.releasePool << d.oneface_rt[face];
}
d.oneface_srb = rhi->newShaderResourceBindings();
const QRhiShaderResourceBinding::StageFlags visibility =
QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage;
d.oneface_srb->setBindings({
QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(0, visibility, d.oneface_ubuf, 64 + 12)
});
d.oneface_srb->build();
d.releasePool << d.oneface_srb;
d.oneface_ps = rhi->newGraphicsPipeline();
d.oneface_ps->setShaderStages({
{ QRhiShaderStage::Vertex, getShader(QLatin1String(":/cubemap_oneface.vert.qsb")) },
{ QRhiShaderStage::Fragment, getShader(QLatin1String(":/cubemap_oneface.frag.qsb")) }
});
QRhiVertexInputLayout inputLayout;
inputLayout.setBindings({
{ 2 * sizeof(float) }
});
inputLayout.setAttributes({
{ 0, 0, QRhiVertexInputAttribute::Float2, 0 },
});
d.oneface_ps->setVertexInputLayout(inputLayout);
d.oneface_ps->setShaderResourceBindings(d.oneface_srb);
d.oneface_ps->setRenderPassDescriptor(d.oneface_rp);
d.oneface_ps->build();
d.releasePool << d.oneface_ps;
// wasteful to duplicate the mvp as well but will do for now
for (int face = 0; face < 6; ++face) {
const int offset = d.ubufSizePerFace * face;
QMatrix4x4 identity;
d.initialUpdates->updateDynamicBuffer(d.oneface_ubuf, offset, 64, identity.constData());
// will use a different color for each face
QColor c;
switch (face) {
case 0:
c = Qt::red;
break;
case 1:
c = Qt::green;
break;
case 2:
c = Qt::blue;
break;
case 3:
c = Qt::yellow;
break;
case 4:
c = Qt::lightGray;
break;
case 5:
c = Qt::cyan;
break;
}
float color[] = { float(c.redF()), float(c.greenF()), float(c.blueF()) };
d.initialUpdates->updateDynamicBuffer(d.oneface_ubuf, offset + 64, 12, color);
}
}
// 6 render passes, 1 draw call each, targeting one cubemap face at a time
void renderPerFace(QRhiCommandBuffer *cb)
{
for (int face = 0; face < 6; ++face) {
cb->beginPass(d.oneface_rt[face], Qt::black, { 1.0f, 0 });
cb->setGraphicsPipeline(d.oneface_ps);
cb->setViewport({ 0, 0,
float(d.oneface_rt[face]->pixelSize().width()),
float(d.oneface_rt[face]->pixelSize().height()) });
const QRhiCommandBuffer::DynamicOffset dynamicOffset(0, face * d.ubufSizePerFace);
cb->setShaderResources(nullptr, 1, &dynamicOffset);
QRhiCommandBuffer::VertexInput vbufBinding(d.half_quad_vbuf, 0);
cb->setVertexInput(0, 1, &vbufBinding, d.half_quad_ibuf, 0, QRhiCommandBuffer::IndexUInt16);
cb->drawIndexed(6);
cb->endPass();
}
}
void initializeMrtRendering(QRhi *rhi)
{
d.cubemap2 = rhi->newTexture(QRhiTexture::RGBA8, cubemapSize, 1, QRhiTexture::CubeMap | QRhiTexture::RenderTarget);
d.cubemap2->build();
d.releasePool << d.cubemap2;
d.mrt_ubuf = rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 64 + 6 * 16); // note that vec3 is aligned to 16 bytes
d.mrt_ubuf->build();
d.releasePool << d.mrt_ubuf;
QVarLengthArray<QRhiColorAttachment, 6> attachments;
for (int face = 0; face < 6; ++face) {
QRhiColorAttachment att(d.cubemap2);
att.setLayer(face);
attachments.append(att);
}
QRhiTextureRenderTargetDescription rtDesc;
rtDesc.setColorAttachments(attachments.cbegin(), attachments.cend());
d.mrt_rt = rhi->newTextureRenderTarget(rtDesc);
d.mrt_rp = d.mrt_rt->newCompatibleRenderPassDescriptor();
d.releasePool << d.mrt_rp;
d.mrt_rt->setRenderPassDescriptor(d.mrt_rp);
d.mrt_rt->build();
d.releasePool << d.mrt_rt;
d.mrt_srb = rhi->newShaderResourceBindings();
const QRhiShaderResourceBinding::StageFlags visibility =
QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage;
d.mrt_srb->setBindings({
QRhiShaderResourceBinding::uniformBuffer(0, visibility, d.mrt_ubuf)
});
d.mrt_srb->build();
d.releasePool << d.mrt_srb;
d.mrt_ps = rhi->newGraphicsPipeline();
d.mrt_ps->setShaderStages({
{ QRhiShaderStage::Vertex, getShader(QLatin1String(":/cubemap_mrt.vert.qsb")) },
{ QRhiShaderStage::Fragment, getShader(QLatin1String(":/cubemap_mrt.frag.qsb")) }
});
QVarLengthArray<QRhiGraphicsPipeline::TargetBlend, 6> targetBlends;
for (int face = 0; face < 6; ++face)
targetBlends.append({}); // default to blend = false, color write = all, which is good
d.mrt_ps->setTargetBlends(targetBlends.cbegin(), targetBlends.cend());
QRhiVertexInputLayout inputLayout;
inputLayout.setBindings({
{ 2 * sizeof(float) }
});
inputLayout.setAttributes({
{ 0, 0, QRhiVertexInputAttribute::Float2, 0 },
});
d.mrt_ps->setVertexInputLayout(inputLayout);
d.mrt_ps->setShaderResourceBindings(d.mrt_srb);
d.mrt_ps->setRenderPassDescriptor(d.mrt_rp);
d.mrt_ps->build();
d.releasePool << d.mrt_ps;
QMatrix4x4 identity;
d.initialUpdates->updateDynamicBuffer(d.mrt_ubuf, 0, 64, identity.constData());
for (int face = 0; face < 6; ++face) {
const int offset = 64 + face * 16;
// will use a different color for each face
QColor c;
switch (face) {
case 0:
c = Qt::red;
break;
case 1:
c = Qt::green;
break;
case 2:
c = Qt::blue;
break;
case 3:
c = Qt::yellow;
break;
case 4:
c = Qt::lightGray;
break;
case 5:
c = Qt::cyan;
break;
}
float color[] = { float(c.redF()), float(c.greenF()), float(c.blueF()) };
d.initialUpdates->updateDynamicBuffer(d.mrt_ubuf, offset, 12, color);
}
}
// 1 render pass, 1 draw call, with all 6 faces attached and written to
void renderWithMrt(QRhiCommandBuffer *cb)
{
// use a different clear color to differentiate from cubemap1 (because the
// results are expected to be identical otherwise)
cb->beginPass(d.mrt_rt, Qt::magenta, { 1.0f, 0 });
cb->setGraphicsPipeline(d.mrt_ps);
cb->setViewport({ 0, 0,
float(d.mrt_rt->pixelSize().width()),
float(d.mrt_rt->pixelSize().height()) });
cb->setShaderResources();
QRhiCommandBuffer::VertexInput vbufBinding(d.half_quad_vbuf, 0);
cb->setVertexInput(0, 1, &vbufBinding, d.half_quad_ibuf, 0, QRhiCommandBuffer::IndexUInt16);
cb->drawIndexed(6);
cb->endPass();
}
void Window::customInit()
{
d.half_quad_vbuf = m_r->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(halfQuadVertexData));
d.half_quad_vbuf->build();
d.releasePool << d.half_quad_vbuf;
d.half_quad_ibuf = m_r->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::IndexBuffer, sizeof(halfQuadIndexData));
d.half_quad_ibuf->build();
d.releasePool << d.half_quad_ibuf;
d.initialUpdates = m_r->nextResourceUpdateBatch();
d.initialUpdates->uploadStaticBuffer(d.half_quad_vbuf, 0, sizeof(halfQuadVertexData), halfQuadVertexData);
d.initialUpdates->uploadStaticBuffer(d.half_quad_ibuf, halfQuadIndexData);
initializePerFaceRendering(m_r);
d.canDoMrt = m_r->resourceLimit(QRhi::MaxColorAttachments) >= 6;
if (d.canDoMrt)
initializeMrtRendering(m_r);
else
qWarning("Not enough color attachments (need 6, supports %d)", m_r->resourceLimit(QRhi::MaxColorAttachments));
// onscreen stuff
d.vbuf = m_r->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(cube));
d.vbuf->build();
d.releasePool << d.vbuf;
d.initialUpdates->uploadStaticBuffer(d.vbuf, cube);
d.ubuf = m_r->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 64);
d.ubuf->build();
d.releasePool << d.ubuf;
d.sampler = m_r->newSampler(QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
QRhiSampler::Repeat, QRhiSampler::Repeat);
d.sampler->build();
d.releasePool << d.sampler;
d.srb = m_r->newShaderResourceBindings();
d.srb->setBindings({
QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, d.ubuf),
QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, d.cubemap1, d.sampler)
});
d.srb->build();
d.releasePool << d.srb;
d.ps = m_r->newGraphicsPipeline();
d.ps->setDepthTest(true);
d.ps->setDepthWrite(true);
d.ps->setDepthOp(QRhiGraphicsPipeline::LessOrEqual);
d.ps->setCullMode(QRhiGraphicsPipeline::Front); // we are inside the cube so cull front, not back
d.ps->setFrontFace(QRhiGraphicsPipeline::CCW); // front is ccw in the cube data
QShader vs = getShader(QLatin1String(":/cubemap_sample.vert.qsb"));
Q_ASSERT(vs.isValid());
QShader fs = getShader(QLatin1String(":/cubemap_sample.frag.qsb"));
Q_ASSERT(fs.isValid());
d.ps->setShaderStages({
{ QRhiShaderStage::Vertex, vs },
{ QRhiShaderStage::Fragment, fs }
});
QRhiVertexInputLayout inputLayout;
inputLayout.setBindings({
{ 3 * sizeof(float) }
});
inputLayout.setAttributes({
{ 0, 0, QRhiVertexInputAttribute::Float3, 0 }
});
d.ps->setVertexInputLayout(inputLayout);
d.ps->setShaderResourceBindings(d.srb);
d.ps->setRenderPassDescriptor(m_rp);
d.ps->build();
d.releasePool << d.ps;
if (d.canDoMrt)
qDebug("Use the arrow keys to switch between the two generated cubemaps");
}
void Window::customRelease()
{
qDeleteAll(d.releasePool);
d.releasePool.clear();
}
void Window::customRender()
{
const QSize outputSizeInPixels = m_sc->currentPixelSize();
QRhiCommandBuffer *cb = m_sc->currentFrameCommandBuffer();
QRhiResourceUpdateBatch *u = m_r->nextResourceUpdateBatch();
if (d.initialUpdates) {
u->merge(d.initialUpdates);
d.initialUpdates->release();
d.initialUpdates = nullptr;
}
QMatrix4x4 mvp = m_r->clipSpaceCorrMatrix();
mvp.perspective(90.0f, outputSizeInPixels.width() / (float) outputSizeInPixels.height(), 0.01f, 1000.0f);
mvp.scale(10);
mvp.rotate(d.rx, 1, 0, 0);
d.rx += 0.5f;
u->updateDynamicBuffer(d.ubuf, 0, 64, mvp.constData());
cb->resourceUpdate(u);
renderPerFace(cb);
if (d.canDoMrt)
renderWithMrt(cb);
cb->beginPass(m_sc->currentFrameRenderTarget(), m_clearColor, { 1.0f, 0 });
cb->setGraphicsPipeline(d.ps);
cb->setViewport(QRhiViewport(0, 0, outputSizeInPixels.width(), outputSizeInPixels.height()));
cb->setShaderResources();
const QRhiCommandBuffer::VertexInput vbufBinding(d.vbuf, 0);
cb->setVertexInput(0, 1, &vbufBinding);
cb->draw(36);
cb->endPass();
}
void Window::keyPressEvent(QKeyEvent *e)
{
switch (e->key()) {
case Qt::Key_Left:
case Qt::Key_Up:
qDebug("Showing first cubemap (generated by rendering to the faces one by one; black background)");
d.srb->setBindings({
QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, d.ubuf),
QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, d.cubemap1, d.sampler)
});
d.srb->build();
break;
case Qt::Key_Right:
case Qt::Key_Down:
if (d.canDoMrt) {
qDebug("Showing second cubemap (generated with multiple render targets; magenta background)");
d.srb->setBindings({
QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, d.ubuf),
QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, d.cubemap2, d.sampler)
});
d.srb->build();
}
break;
default:
e->ignore();
break;
}
}

View File

@ -0,0 +1,8 @@
TEMPLATE = app
QT += gui-private
SOURCES = \
cubemap_render.cpp
RESOURCES = cubemap_render.qrc

View File

@ -0,0 +1,10 @@
<!DOCTYPE RCC><RCC version="1.0">
<qresource>
<file>cubemap_oneface.vert.qsb</file>
<file>cubemap_oneface.frag.qsb</file>
<file>cubemap_mrt.vert.qsb</file>
<file>cubemap_mrt.frag.qsb</file>
<file>cubemap_sample.vert.qsb</file>
<file>cubemap_sample.frag.qsb</file>
</qresource>
</RCC>

View File

@ -0,0 +1,10 @@
#version 440
layout(location = 0) in vec3 v_coord;
layout(location = 0) out vec4 fragColor;
layout(binding = 1) uniform samplerCube tex;
void main()
{
fragColor = vec4(texture(tex, v_coord).rgb, 1.0);
}

View File

@ -0,0 +1,16 @@
#version 440
layout(location = 0) in vec4 position;
layout(location = 0) out vec3 v_coord;
layout(std140, binding = 0) uniform buf {
mat4 mvp;
} ubuf;
out gl_PerVertex { vec4 gl_Position; };
void main()
{
v_coord = position.xyz;
gl_Position = ubuf.mvp * position;
}

View File

@ -0,0 +1,3 @@
#!/bin/sh
qsb --glsl "430,310 es" --hlsl 50 --msl 12 load.comp -o load.comp.qsb
qsb --glsl "430,310 es" --hlsl 50 --msl 12 prefilter.comp -o prefilter.comp.qsb

View File

@ -0,0 +1,312 @@
/****************************************************************************
**
** Copyright (C) 2020 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the examples of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** 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.
**
** BSD License Usage
** Alternatively, you may use this file under the terms of the BSD license
** as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of The Qt Company Ltd nor the names of its
** contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
** $QT_END_LICENSE$
**
****************************************************************************/
// An advanced version of floattexture. Instead of RGBA32F, we use RGBA16F, and
// also generate the floating point data from rgba with compute. Then there's a
// compute pass using the BSDF prefiltering taken from Qt Quick 3D, which
// generates all the mip levels.
// Why do we animate the scale of the quad rendered to the window? To have
// different mip levels used, to prove that all of them are generated
// correctly, without artifacts (which would occur if memory barriers were not
// correctly generated by QRhi). For full verification use RenderDoc or similar.
#include "../shared/examplefw.h"
#include <qmath.h>
static float vertexData[] =
{ // Y up, CCW
-0.5f, 0.5f, 0.0f, 0.0f,
-0.5f, -0.5f, 0.0f, 1.0f,
0.5f, -0.5f, 1.0f, 1.0f,
0.5f, 0.5f, 1.0f, 0.0f
};
static quint16 indexData[] =
{
0, 1, 2, 0, 2, 3
};
static const int MAX_MIP_LEVELS = 20;
struct {
QVector<QRhiResource *> releasePool;
QRhiBuffer *vbuf = nullptr;
QRhiBuffer *ibuf = nullptr;
QRhiBuffer *ubuf = nullptr;
QRhiTexture *texRgba = nullptr;
QRhiTexture *texFloat16 = nullptr;
QRhiSampler *sampler = nullptr;
QRhiShaderResourceBindings *srb = nullptr;
QRhiGraphicsPipeline *ps = nullptr;
QRhiBuffer *computeUBuf_load = nullptr;
QRhiShaderResourceBindings *computeBindings_load = nullptr;
QRhiComputePipeline *computePipeline_load = nullptr;
QRhiBuffer *computeUBuf_prefilter = nullptr;
QRhiShaderResourceBindings *computeBindings_prefilter[MAX_MIP_LEVELS];
QRhiComputePipeline *computePipeline_prefilter = nullptr;
QRhiResourceUpdateBatch *initialUpdates = nullptr;
bool computeDone = false;
int mipCount;
int prefilterUBufElemSize;
quint32 prefilterNumWorkGroups[MAX_MIP_LEVELS][3];
float scale = 2.5f;
int scale_dir = -1;
} d;
void recordUploadThenFilterFloat16TextureWithCompute(QRhiCommandBuffer *cb)
{
const int w = d.texRgba->pixelSize().width() / 16;
const int h = d.texRgba->pixelSize().height() / 16;
cb->beginComputePass();
cb->setComputePipeline(d.computePipeline_load);
cb->setShaderResources();
cb->dispatch(w, h, 1);
cb->setComputePipeline(d.computePipeline_prefilter);
for (int level = 1; level < d.mipCount; ++level) {
const int i = level - 1;
const int mipW = d.prefilterNumWorkGroups[i][0];
const int mipH = d.prefilterNumWorkGroups[i][1];
QPair<int, quint32> dynamicOffset = { 0, quint32(d.prefilterUBufElemSize * i) };
cb->setShaderResources(d.computeBindings_prefilter[i], 1, &dynamicOffset);
cb->dispatch(mipW, mipH, 1);
}
cb->endComputePass();
}
void Window::customInit()
{
if (!m_r->isFeatureSupported(QRhi::Compute))
qFatal("Compute is not supported");
if (!m_r->isTextureFormatSupported(QRhiTexture::RGBA16F))
qFatal("RGBA16F texture format is not supported");
d.initialUpdates = m_r->nextResourceUpdateBatch();
// load rgba8 image data
QImage image;
image.load(QLatin1String(":/qt256.png"));
image = image.convertToFormat(QImage::Format_RGBA8888);
Q_ASSERT(!image.isNull());
d.texRgba = m_r->newTexture(QRhiTexture::RGBA8, image.size(), 1, QRhiTexture::UsedWithLoadStore);
d.texRgba->build();
d.releasePool << d.texRgba;
d.initialUpdates->uploadTexture(d.texRgba, image);
d.mipCount = m_r->mipLevelsForSize(image.size());
Q_ASSERT(d.mipCount <= MAX_MIP_LEVELS);
d.texFloat16 = m_r->newTexture(QRhiTexture::RGBA16F, image.size(), 1, QRhiTexture::UsedWithLoadStore | QRhiTexture::MipMapped);
d.releasePool << d.texFloat16;
d.texFloat16->build();
// compute
d.computeUBuf_load = m_r->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 12);
d.computeUBuf_load->build();
d.releasePool << d.computeUBuf_load;
quint32 numWorkGroups[3] = { quint32(image.width()), quint32(image.height()), 0 };
d.initialUpdates->updateDynamicBuffer(d.computeUBuf_load, 0, 12, numWorkGroups);
d.computeBindings_load = m_r->newShaderResourceBindings();
d.computeBindings_load->setBindings({
QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::ComputeStage, d.computeUBuf_load),
QRhiShaderResourceBinding::imageLoad(1, QRhiShaderResourceBinding::ComputeStage, d.texRgba, 0),
QRhiShaderResourceBinding::imageStore(2, QRhiShaderResourceBinding::ComputeStage, d.texFloat16, 0)
});
d.computeBindings_load->build();
d.releasePool << d.computeBindings_load;
d.computePipeline_load = m_r->newComputePipeline();
d.computePipeline_load->setShaderResourceBindings(d.computeBindings_load);
d.computePipeline_load->setShaderStage({ QRhiShaderStage::Compute, getShader(QLatin1String(":/load.comp.qsb")) });
d.computePipeline_load->build();
d.releasePool << d.computePipeline_load;
d.prefilterUBufElemSize = m_r->ubufAligned(12);
d.computeUBuf_prefilter = m_r->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, d.prefilterUBufElemSize * d.mipCount);
d.computeUBuf_prefilter->build();
d.releasePool << d.computeUBuf_prefilter;
int mipW = image.width() >> 1;
int mipH = image.height() >> 1;
for (int level = 1; level < d.mipCount; ++level) {
const int i = level - 1;
d.prefilterNumWorkGroups[i][0] = quint32(mipW);
d.prefilterNumWorkGroups[i][1] = quint32(mipH);
d.prefilterNumWorkGroups[i][2] = 0;
d.initialUpdates->updateDynamicBuffer(d.computeUBuf_prefilter, d.prefilterUBufElemSize * i, 12, d.prefilterNumWorkGroups[i]);
mipW = mipW > 2 ? mipW >> 1 : 1;
mipH = mipH > 2 ? mipH >> 1 : 1;
d.computeBindings_prefilter[i] = m_r->newShaderResourceBindings();
d.computeBindings_prefilter[i]->setBindings({
QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(0, QRhiShaderResourceBinding::ComputeStage, d.computeUBuf_prefilter, 12),
QRhiShaderResourceBinding::imageLoad(1, QRhiShaderResourceBinding::ComputeStage, d.texFloat16, level - 1),
QRhiShaderResourceBinding::imageStore(2, QRhiShaderResourceBinding::ComputeStage, d.texFloat16, level)
});
d.computeBindings_prefilter[i]->build();
d.releasePool << d.computeBindings_prefilter[i];
}
d.computePipeline_prefilter = m_r->newComputePipeline();
d.computePipeline_prefilter->setShaderResourceBindings(d.computeBindings_prefilter[0]); // just need a layout compatible one
d.computePipeline_prefilter->setShaderStage({ QRhiShaderStage::Compute, getShader(QLatin1String(":/prefilter.comp.qsb")) });
d.computePipeline_prefilter->build();
d.releasePool << d.computePipeline_prefilter;
// graphics
d.vbuf = m_r->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(vertexData));
d.vbuf->build();
d.releasePool << d.vbuf;
d.ibuf = m_r->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::IndexBuffer, sizeof(indexData));
d.ibuf->build();
d.releasePool << d.ibuf;
d.ubuf = m_r->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 68);
d.ubuf->build();
d.releasePool << d.ubuf;
// enable mipmaps
d.sampler = m_r->newSampler(QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::Linear,
QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge);
d.releasePool << d.sampler;
d.sampler->build();
d.srb = m_r->newShaderResourceBindings();
d.releasePool << d.srb;
d.srb->setBindings({
QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, d.ubuf),
QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, d.texFloat16, d.sampler)
});
d.srb->build();
d.ps = m_r->newGraphicsPipeline();
d.releasePool << d.ps;
d.ps->setShaderStages({
{ QRhiShaderStage::Vertex, getShader(QLatin1String(":/texture.vert.qsb")) },
{ QRhiShaderStage::Fragment, getShader(QLatin1String(":/texture.frag.qsb")) }
});
QRhiVertexInputLayout inputLayout;
inputLayout.setBindings({
{ 4 * sizeof(float) }
});
inputLayout.setAttributes({
{ 0, 0, QRhiVertexInputAttribute::Float2, 0 },
{ 0, 1, QRhiVertexInputAttribute::Float2, 2 * sizeof(float) }
});
d.ps->setVertexInputLayout(inputLayout);
d.ps->setShaderResourceBindings(d.srb);
d.ps->setRenderPassDescriptor(m_rp);
d.ps->build();
d.initialUpdates->uploadStaticBuffer(d.vbuf, vertexData);
d.initialUpdates->uploadStaticBuffer(d.ibuf, indexData);
qint32 flip = 0;
d.initialUpdates->updateDynamicBuffer(d.ubuf, 64, 4, &flip);
}
void Window::customRelease()
{
qDeleteAll(d.releasePool);
d.releasePool.clear();
}
void Window::customRender()
{
QRhiCommandBuffer *cb = m_sc->currentFrameCommandBuffer();
QRhiResourceUpdateBatch *u = m_r->nextResourceUpdateBatch();
if (d.initialUpdates) {
u->merge(d.initialUpdates);
d.initialUpdates->release();
d.initialUpdates = nullptr;
}
QMatrix4x4 mvp = m_proj;
mvp.scale(d.scale);
d.scale += d.scale_dir * 0.01f;
if (qFuzzyIsNull(d.scale) || d.scale >= 2.5f)
d.scale_dir *= -1;
u->updateDynamicBuffer(d.ubuf, 0, 64, mvp.constData());
cb->resourceUpdate(u);
// If not yet done, then do a compute pass that uploads level 0, doing an
// rgba8 -> float16 conversion. Follow that with another compute pass to do
// the filtering and generate all the mip levels.
if (!d.computeDone) {
recordUploadThenFilterFloat16TextureWithCompute(cb);
d.computeDone = true;
}
const QSize outputSizeInPixels = m_sc->currentPixelSize();
cb->beginPass(m_sc->currentFrameRenderTarget(), m_clearColor, { 1.0f, 0 });
cb->setGraphicsPipeline(d.ps);
cb->setViewport({ 0, 0, float(outputSizeInPixels.width()), float(outputSizeInPixels.height()) });
cb->setShaderResources();
const QRhiCommandBuffer::VertexInput vbufBinding(d.vbuf, 0);
cb->setVertexInput(0, 1, &vbufBinding, d.ibuf, 0, QRhiCommandBuffer::IndexUInt16);
cb->drawIndexed(6);
cb->endPass();
}

View File

@ -0,0 +1,8 @@
TEMPLATE = app
QT += gui-private
SOURCES = \
float16texture_with_compute.cpp
RESOURCES = float16texture_with_compute.qrc

View File

@ -0,0 +1,9 @@
<!DOCTYPE RCC><RCC version="1.0">
<qresource>
<file>load.comp.qsb</file>
<file>prefilter.comp.qsb</file>
<file alias="texture.vert.qsb">../shared/texture.vert.qsb</file>
<file alias="texture.frag.qsb">../shared/texture.frag.qsb</file>
<file alias="qt256.png">../shared/qt256.png</file>
</qresource>
</RCC>

View File

@ -0,0 +1,19 @@
#version 440
layout(local_size_x = 16, local_size_y = 16) in;
layout(rgba8, binding = 1) readonly uniform image2D inputImage;
layout(rgba16f, binding = 2) writeonly uniform image2D outputImage;
// There is no equivalent of gl_NumWorkGroups in HLSL. So instead pass the
// values in in a uniform buffer.
layout(std140, binding = 0) uniform numWorkGroupsBuf {
uvec3 numWorkGroups;
};
void main()
{
if (gl_GlobalInvocationID.x >= numWorkGroups.x || gl_GlobalInvocationID.y >= numWorkGroups.y)
return;
vec4 value = imageLoad(inputImage, ivec2(gl_GlobalInvocationID.xy));
imageStore(outputImage, ivec2(gl_GlobalInvocationID.xy), value);
}

View File

@ -0,0 +1,50 @@
#version 440
layout(local_size_x = 16, local_size_y = 16) in;
layout(rgba16f, binding = 1) readonly uniform image2D inputImage;
layout(rgba16f, binding = 2) writeonly uniform image2D outputImage;
// There is no equivalent of gl_NumWorkGroups in HLSL. So instead pass the
// values in in a uniform buffer.
layout(std140, binding = 0) uniform numWorkGroupsBuf {
uvec3 numWorkGroups;
};
int wrapMod( in int a, in int base )
{
return ( a >= 0 ) ? a % base : -(a % base) + base;
}
void getWrappedCoords( inout int sX, inout int sY, in int width, in int height )
{
if (sY < 0) { sX -= width >> 1; sY = -sY; }
if (sY >= height) { sX += width >> 1; sY = height - sY; }
sX = wrapMod( sX, width );
}
void main()
{
int prevWidth = int(numWorkGroups.x) << 1;
int prevHeight = int(numWorkGroups.y) << 1;
if (gl_GlobalInvocationID.x >= numWorkGroups.x || gl_GlobalInvocationID.y >= numWorkGroups.y)
return;
vec4 accumVal = vec4(0.0);
for (int sy = -2; sy <= 2; ++sy) {
for (int sx = -2; sx <= 2; ++sx) {
int sampleX = sx + (int(gl_GlobalInvocationID.x) << 1);
int sampleY = sy + (int(gl_GlobalInvocationID.y) << 1);
getWrappedCoords(sampleX, sampleY, prevWidth, prevHeight);
if ((sampleY * prevWidth + sampleX) < 0 )
sampleY = prevHeight + sampleY;
ivec2 pos = ivec2(sampleX, sampleY);
vec4 value = imageLoad(inputImage, pos);
float filterPdf = 1.0 / ( 1.0 + float(sx*sx + sy*sy)*2.0 );
filterPdf /= 4.71238898;
accumVal[0] += filterPdf * value.r;
accumVal[1] += filterPdf * value.g;
accumVal[2] += filterPdf * value.b;
accumVal[3] += filterPdf * value.a;
}
}
imageStore(outputImage, ivec2(gl_GlobalInvocationID.xy), accumVal);
}

View File

@ -9,11 +9,13 @@ SUBDIRS += \
msaarenderbuffer \
cubemap \
cubemap_scissor \
cubemap_render \
multiwindow \
multiwindow_threaded \
triquadcube \
offscreen \
floattexture \
float16texture_with_compute \
mrt \
shadowmap \
computebuffer \

View File

@ -148,6 +148,9 @@ protected:
void exposeEvent(QExposeEvent *) override;
bool event(QEvent *) override;
#ifdef EXAMPLEFW_KEYPRESS_EVENTS
void keyPressEvent(QKeyEvent *e) override;
#endif
bool m_running = false;
bool m_notExposed = false;