diff options
Diffstat (limited to 'src/gui')
28 files changed, 531 insertions, 307 deletions
diff --git a/src/gui/configure.json b/src/gui/configure.json index 19312d245d..134a2e0a15 100644 --- a/src/gui/configure.json +++ b/src/gui/configure.json @@ -41,11 +41,10 @@ "sm": { "type": "boolean", "name": "sessionmanager" }, "tslib": "boolean", "vulkan": "boolean", - "xcb": { "type": "enum", "values": [ "no", "yes", "qt", "system" ] }, + "xcb": "boolean", + "bundled-xcb-xinput": "boolean", "xcb-native-painting": "boolean", "xcb-xlib": "boolean", - "xcb-xinput": "boolean", - "xkb": "boolean", "xkbcommon": "boolean" } }, @@ -573,18 +572,22 @@ ] }, "xcb": { - "label": "XCB >= 1.9", + "label": "XCB >= 1.11", "test": { "main": [ "int primaryScreen = 0;", "(void)xcb_connect(\"\", &primaryScreen);", - "// This won't compile unless libxcb >= 1.9 which defines XCB_CONN_CLOSED_INVALID_SCREEN.", - "int xcbScreenError = XCB_CONN_CLOSED_INVALID_SCREEN;" + "/* XCB_PACKED define was added in libxcb 1.11 */", + "#ifdef XCB_PACKED", + " return 0;", + "#else", + " return -1;", + "#endif" ] }, "headers": "xcb/xcb.h", "sources": [ - { "type": "pkgConfig", "args": "xcb >= 1.9" }, + { "type": "pkgConfig", "args": "xcb >= 1.11" }, "-lxcb" ] }, @@ -691,21 +694,10 @@ "use": "xcb xlib" }, "xcb_xkb": { - "label": "XCB XKB >= 1.10", - "test": { - "head": [ - "// xkb.h is using a variable called 'explicit', which is a reserved keyword in C++", - "#define explicit dont_use_cxx_explicit" - ], - "tail": "#undef explicit", - "main": [ - "// This takes more arguments in xcb-xkb < 1.10.", - "xcb_xkb_get_kbd_by_name_unchecked(NULL, 0, 0, 0, 0);" - ] - }, + "label": "XCB XKB", "headers": "xcb/xkb.h", "sources": [ - { "type": "pkgConfig", "args": "xcb-xkb >= 1.10" }, + { "type": "pkgConfig", "args": "xcb-xkb" }, "-lxcb-xkb" ], "use": "xcb" @@ -769,7 +761,7 @@ "xkbcommon_x11": { "label": "xkbcommon-x11", "test": { - "main": "xkb_x11_get_core_keyboard_device_id(nullptr);" + "main": "xkb_x11_setup_xkb_extension_flags flag = XKB_X11_SETUP_XKB_EXTENSION_NO_FLAGS;" }, "headers": [ "xkbcommon/xkbcommon-x11.h" ], "sources": [ @@ -1056,6 +1048,11 @@ "label": "XCB (extensions)", "type": "compile", "test": { + "head": [ + "// xkb.h is using a variable called 'explicit', which is a reserved keyword in C++", + "#define explicit dont_use_cxx_explicit" + ], + "tail": "#undef explicit", "include": [ "xcb/xcb.h", "xcb/xcb_image.h", @@ -1068,7 +1065,8 @@ "xcb/xfixes.h", "xcb/xinerama.h", "xcb/xcb_icccm.h", - "xcb/xcb_renderutil.h" + "xcb/xcb_renderutil.h", + "xcb/xkb.h" ], "main": [ "int primaryScreen = 0;", @@ -1082,10 +1080,13 @@ " xcb_render_query_pict_formats_reply(c, formatsCookie, &error);", "/* RENDERUTIL: xcb_renderutil.h include won't compile unless version >= 0.3.9 */", - "xcb_render_util_find_standard_format(nullptr, XCB_PICT_STANDARD_ARGB_32);" + "xcb_render_util_find_standard_format(nullptr, XCB_PICT_STANDARD_ARGB_32);", + + "/* XKB: This takes more arguments in xcb-xkb < 1.11 */", + "xcb_xkb_get_kbd_by_name_replies_key_names_value_list_sizeof(nullptr, 0, 0, 0, 0, 0, 0, 0, 0);" ] }, - "use": "xcb_icccm xcb_image xcb_keysyms xcb_randr xcb_render xcb_renderutil xcb_shape xcb_shm xcb_sync xcb_xfixes xcb_xinerama xcb" + "use": "xcb_icccm xcb_image xcb_keysyms xcb_randr xcb_render xcb_renderutil xcb_shape xcb_shm xcb_sync xcb_xfixes xcb_xinerama xcb_xkb xcb" }, "x11prefix": { "label": "X11 prefix", @@ -1514,16 +1515,7 @@ "label": "XCB", "section": "Platform plugins", "autoDetect": "!config.darwin", - "enable": "input.xcb == 'system' || input.xcb == 'qt' || input.xcb == 'yes'", - "condition": "features.thread && features.xkbcommon && libs.xcb", - "output": [ "privateFeature" ] - }, - "system-xcb": { - "label": "Using system-provided XCB libraries", - "enable": "input.xcb == 'system'", - "disable": "input.xcb == 'qt'", - "autoDetect": "!config.darwin", - "condition": "features.xcb && tests.xcb_syslibs", + "condition": "features.thread && libs.xcb && tests.xcb_syslibs && features.xkbcommon-x11", "output": [ "privateFeature" ] }, "x11-prefix": { @@ -1562,12 +1554,6 @@ "condition": "features.xcb-native-painting", "output": [ "privateFeature" ] }, - "xkb": { - "label": "XCB XKB", - "emitIf": "features.xcb", - "condition": "(!features.system-xcb || libs.xcb_xkb) && libs.xkbcommon_x11", - "output": [ "privateFeature" ] - }, "xcb-xlib": { "label": "XCB Xlib", "condition": "features.xlib && libs.xcb_xlib", @@ -1579,10 +1565,12 @@ "condition": "features.sessionmanager && libs.x11sm", "output": [ "privateFeature" ] }, - "xcb-xinput": { - "label": "XCB XInput", + "system-xcb-xinput": { + "label": "Using system-provided xcb-xinput", "emitIf": "features.xcb", - "condition": "!features.system-xcb || libs.xcb_xinput", + "disable": "input.bundled-xcb-xinput == 'yes'", + "enable": "input.bundled-xcb-xinput == 'no'", + "condition": "libs.xcb_xinput", "output": [ "privateFeature" ] }, "xkbcommon": { @@ -1590,6 +1578,11 @@ "condition": "libs.xkbcommon", "output": [ "privateFeature" ] }, + "xkbcommon-x11": { + "label": "xkbcommon-x11", + "condition": "features.xkbcommon && libs.xkbcommon_x11", + "output": [ "privateFeature" ] + }, "xlib": { "label": "XLib", "autoDetect": "!config.darwin || features.xcb", @@ -1831,7 +1824,7 @@ { "type": "error", "condition": "input.xcb != '' && input.xcb != 'no' && input.xkbcommon == 'no'", - "message": "XCB plugin requires xkbcommon, but -no-xkbcommon was provided." + "message": "XCB plugin requires xkbcommon and xkbcommon-x11, but -no-xkbcommon was provided." } ], @@ -1953,7 +1946,8 @@ QMAKE_LIBDIR_OPENGL[_ES2] and QMAKE_LIBS_OPENGL[_ES2] in the mkspec for your pla "entries": [ "xlib", "xcb-xlib", - "egl_x11" + "egl_x11", + "xkbcommon-x11" ] } ] @@ -1986,7 +1980,7 @@ QMAKE_LIBDIR_OPENGL[_ES2] and QMAKE_LIBS_OPENGL[_ES2] in the mkspec for your pla "section": "XCB", "condition": "features.xcb", "entries": [ - "system-xcb", "xkb", "xcb-xinput", "xcb-native-painting", + "system-xcb-xinput", "xcb-native-painting", { "section": "GL integrations", "entries": [ diff --git a/src/gui/gui.pro b/src/gui/gui.pro index 45c8c05162..350d4c5ee3 100644 --- a/src/gui/gui.pro +++ b/src/gui/gui.pro @@ -99,4 +99,4 @@ qtConfig(egl): CMAKE_EGL_INCDIRS = $$cmakePortablePaths($$QMAKE_INCDIR_EGL) QMAKE_DYNAMIC_LIST_FILE = $$PWD/QtGui.dynlist TRACEPOINT_PROVIDER = $$PWD/qtgui.tracepoints -CONFIG += qt_tracepoints +CONFIG += qt_tracepoints metatypes install_metatypes diff --git a/src/gui/image/qimage_conversions.cpp b/src/gui/image/qimage_conversions.cpp index 97a5f89e68..4f3821a7cc 100644 --- a/src/gui/image/qimage_conversions.cpp +++ b/src/gui/image/qimage_conversions.cpp @@ -832,216 +832,6 @@ static bool convert_A2RGB30_PM_to_ARGB_inplace(QImageData *data, Qt::ImageConver return true; } -static bool convert_indexed8_to_ARGB_PM_inplace(QImageData *data, Qt::ImageConversionFlags) -{ - Q_ASSERT(data->format == QImage::Format_Indexed8); - Q_ASSERT(data->own_data); - - const int depth = 32; - auto params = QImageData::calculateImageParameters(data->width, data->height, depth); - if (params.bytesPerLine < 0) - return false; - uchar *const newData = (uchar *)realloc(data->data, params.totalSize); - if (!newData) - return false; - - data->data = newData; - - // start converting from the end because the end image is bigger than the source - uchar *src_data = newData + data->nbytes; // end of src - quint32 *dest_data = (quint32 *) (newData + params.totalSize); // end of dest > end of src - const int width = data->width; - const int src_pad = data->bytes_per_line - width; - const int dest_pad = (params.bytesPerLine >> 2) - width; - if (data->colortable.size() == 0) { - data->colortable.resize(256); - for (int i = 0; i < 256; ++i) - data->colortable[i] = qRgb(i, i, i); - } else { - for (int i = 0; i < data->colortable.size(); ++i) - data->colortable[i] = qPremultiply(data->colortable.at(i)); - - // Fill the rest of the table in case src_data > colortable.size() - const int oldSize = data->colortable.size(); - const QRgb lastColor = data->colortable.at(oldSize - 1); - data->colortable.insert(oldSize, 256 - oldSize, lastColor); - } - - for (int i = 0; i < data->height; ++i) { - src_data -= src_pad; - dest_data -= dest_pad; - for (int pixI = 0; pixI < width; ++pixI) { - --src_data; - --dest_data; - *dest_data = data->colortable.at(*src_data); - } - } - - data->colortable = QVector<QRgb>(); - data->format = QImage::Format_ARGB32_Premultiplied; - data->bytes_per_line = params.bytesPerLine; - data->depth = depth; - data->nbytes = params.totalSize; - - return true; -} - -static bool convert_indexed8_to_ARGB_inplace(QImageData *data, Qt::ImageConversionFlags) -{ - Q_ASSERT(data->format == QImage::Format_Indexed8); - Q_ASSERT(data->own_data); - - const int depth = 32; - auto params = QImageData::calculateImageParameters(data->width, data->height, depth); - if (params.bytesPerLine < 0) - return false; - uchar *const newData = (uchar *)realloc(data->data, params.totalSize); - if (!newData) - return false; - - data->data = newData; - - // start converting from the end because the end image is bigger than the source - uchar *src_data = newData + data->nbytes; - quint32 *dest_data = (quint32 *) (newData + params.totalSize); - const int width = data->width; - const int src_pad = data->bytes_per_line - width; - const int dest_pad = (params.bytesPerLine >> 2) - width; - if (data->colortable.size() == 0) { - data->colortable.resize(256); - for (int i = 0; i < 256; ++i) - data->colortable[i] = qRgb(i, i, i); - } else { - // Fill the rest of the table in case src_data > colortable.size() - const int oldSize = data->colortable.size(); - const QRgb lastColor = data->colortable.at(oldSize - 1); - data->colortable.insert(oldSize, 256 - oldSize, lastColor); - } - - for (int i = 0; i < data->height; ++i) { - src_data -= src_pad; - dest_data -= dest_pad; - for (int pixI = 0; pixI < width; ++pixI) { - --src_data; - --dest_data; - *dest_data = (quint32) data->colortable.at(*src_data); - } - } - - data->colortable = QVector<QRgb>(); - data->format = QImage::Format_ARGB32; - data->bytes_per_line = params.bytesPerLine; - data->depth = depth; - data->nbytes = params.totalSize; - - return true; -} - -static bool convert_indexed8_to_RGB_inplace(QImageData *data, Qt::ImageConversionFlags flags) -{ - Q_ASSERT(data->format == QImage::Format_Indexed8); - Q_ASSERT(data->own_data); - - if (data->has_alpha_clut) { - for (int i = 0; i < data->colortable.size(); ++i) - data->colortable[i] |= 0xff000000; - } - - if (!convert_indexed8_to_ARGB_inplace(data, flags)) - return false; - - data->format = QImage::Format_RGB32; - return true; -} - -static bool convert_indexed8_to_RGB16_inplace(QImageData *data, Qt::ImageConversionFlags) -{ - Q_ASSERT(data->format == QImage::Format_Indexed8); - Q_ASSERT(data->own_data); - - const int depth = 16; - auto params = QImageData::calculateImageParameters(data->width, data->height, depth); - if (params.bytesPerLine < 0) - return false; - uchar *const newData = (uchar *)realloc(data->data, params.totalSize); - if (!newData) - return false; - - data->data = newData; - - // start converting from the end because the end image is bigger than the source - uchar *src_data = newData + data->nbytes; - quint16 *dest_data = (quint16 *) (newData + params.totalSize); - const int width = data->width; - const int src_pad = data->bytes_per_line - width; - const int dest_pad = (params.bytesPerLine >> 1) - width; - - quint16 colorTableRGB16[256]; - const int tableSize = data->colortable.size(); - if (tableSize == 0) { - for (int i = 0; i < 256; ++i) - colorTableRGB16[i] = qConvertRgb32To16(qRgb(i, i, i)); - } else { - // 1) convert the existing colors to RGB16 - for (int i = 0; i < tableSize; ++i) - colorTableRGB16[i] = qConvertRgb32To16(data->colortable.at(i)); - data->colortable = QVector<QRgb>(); - - // 2) fill the rest of the table in case src_data > colortable.size() - const quint16 lastColor = colorTableRGB16[tableSize - 1]; - for (int i = tableSize; i < 256; ++i) - colorTableRGB16[i] = lastColor; - } - - for (int i = 0; i < data->height; ++i) { - src_data -= src_pad; - dest_data -= dest_pad; - for (int pixI = 0; pixI < width; ++pixI) { - --src_data; - --dest_data; - *dest_data = colorTableRGB16[*src_data]; - } - } - - data->format = QImage::Format_RGB16; - data->bytes_per_line = params.bytesPerLine; - data->depth = depth; - data->nbytes = params.totalSize; - - return true; -} - -static bool convert_RGB_to_RGB16_inplace(QImageData *data, Qt::ImageConversionFlags) -{ - Q_ASSERT(data->format == QImage::Format_RGB32); - Q_ASSERT(data->own_data); - - const int depth = 16; - - // cannot overflow, since we're shrinking the buffer - const qsizetype dst_bytes_per_line = ((data->width * depth + 31) >> 5) << 2; - const qsizetype src_bytes_per_line = data->bytes_per_line; - quint32 *src_data = (quint32 *) data->data; - quint16 *dst_data = (quint16 *) data->data; - - for (int i = 0; i < data->height; ++i) { - for (int j = 0; j < data->width; ++j) - dst_data[j] = qConvertRgb32To16(src_data[j]); - src_data = (quint32 *) (((char*)src_data) + src_bytes_per_line); - dst_data = (quint16 *) (((char*)dst_data) + dst_bytes_per_line); - } - data->format = QImage::Format_RGB16; - data->bytes_per_line = dst_bytes_per_line; - data->depth = depth; - data->nbytes = dst_bytes_per_line * data->height; - uchar *const newData = (uchar *)realloc(data->data, data->nbytes); - if (newData) - data->data = newData; - - // can't fail, since we're shrinking - return true; -} - static void convert_ARGB_PM_to_ARGB(QImageData *dest, const QImageData *src) { Q_ASSERT(src->format == QImage::Format_ARGB32_Premultiplied || src->format == QImage::Format_RGBA8888_Premultiplied); @@ -2991,10 +2781,10 @@ InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QIma 0, 0, 0, - convert_indexed8_to_RGB_inplace, - convert_indexed8_to_ARGB_inplace, - convert_indexed8_to_ARGB_PM_inplace, - convert_indexed8_to_RGB16_inplace, + 0, + 0, + 0, + 0, 0, 0, 0, @@ -3018,7 +2808,7 @@ InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QIma 0, mask_alpha_converter_inplace<QImage::Format_ARGB32>, mask_alpha_converter_inplace<QImage::Format_ARGB32_Premultiplied>, - convert_RGB_to_RGB16_inplace, + 0, 0, 0, 0, diff --git a/src/gui/image/qpnghandler.cpp b/src/gui/image/qpnghandler.cpp index 4ab45337b0..d6caf6773a 100644 --- a/src/gui/image/qpnghandler.cpp +++ b/src/gui/image/qpnghandler.cpp @@ -174,7 +174,7 @@ public: void setGamma(float); bool writeImage(const QImage& img, int x, int y); - bool writeImage(const QImage& img, volatile int compression_in, const QString &description, int x, int y); + bool writeImage(const QImage& img, int compression_in, const QString &description, int x, int y); bool writeImage(const QImage& img) { return writeImage(img, 0, 0); } bool writeImage(const QImage& img, int compression, const QString &description) @@ -900,7 +900,7 @@ bool QPNGImageWriter::writeImage(const QImage& image, int off_x, int off_y) return writeImage(image, -1, QString(), off_x, off_y); } -bool QPNGImageWriter::writeImage(const QImage& image, volatile int compression_in, const QString &description, +bool QPNGImageWriter::writeImage(const QImage& image, int compression_in, const QString &description, int off_x_in, int off_y_in) { QPoint offset = image.offset(); diff --git a/src/gui/kernel/qguiapplication.cpp b/src/gui/kernel/qguiapplication.cpp index 54f3996b6e..455de52319 100644 --- a/src/gui/kernel/qguiapplication.cpp +++ b/src/gui/kernel/qguiapplication.cpp @@ -1040,7 +1040,9 @@ QList<QScreen *> QGuiApplication::screens() The \a point is in relation to the virtualGeometry() of each set of virtual siblings. If the point maps to more than one set of virtual siblings the first - match is returned. + match is returned. If you wish to search only the virtual desktop siblings + of a known screen (for example siblings of the screen of your application + window \c QWidget::windowHandle()->screen()), use QScreen::virtualSiblingAt(). \since 5.10 */ @@ -1694,13 +1696,7 @@ QGuiApplicationPrivate::~QGuiApplicationPrivate() qt_gl_set_global_share_context(0); } #endif -#ifdef Q_OS_WASM - EM_ASM( - // unmount persistent directory as IDBFS - // see QTBUG-70002 - FS.unmount('/home/web_user'); - ); -#endif + platform_integration->destroy(); delete platform_theme; diff --git a/src/gui/kernel/qguiapplication_p.h b/src/gui/kernel/qguiapplication_p.h index 26f65b2f16..ee493faa5d 100644 --- a/src/gui/kernel/qguiapplication_p.h +++ b/src/gui/kernel/qguiapplication_p.h @@ -116,7 +116,7 @@ public: static QAbstractEventDispatcher *qt_qpa_core_dispatcher() { if (QCoreApplication::instance()) - return QCoreApplication::instance()->d_func()->threadData->eventDispatcher.loadRelaxed(); + return QCoreApplication::instance()->d_func()->threadData.loadRelaxed()->eventDispatcher.loadRelaxed(); else return nullptr; } diff --git a/src/gui/kernel/qhighdpiscaling_p.h b/src/gui/kernel/qhighdpiscaling_p.h index f58944a7d2..3c85481495 100644 --- a/src/gui/kernel/qhighdpiscaling_p.h +++ b/src/gui/kernel/qhighdpiscaling_p.h @@ -313,7 +313,7 @@ public: static inline QPoint mapPositionToNative(const QPoint &pos, const QPlatformScreen *) { return pos; } static inline QPoint mapPositionToGlobal(const QPoint &pos, const QPoint &windowGlobalPosition, const QWindow *window) { return pos; } static inline QPoint mapPositionFromGlobal(const QPoint &pos, const QPoint &windowGlobalPosition, const QWindow *window) { return pos; } - static inline QDpi logicalDpi() { return QDpi(-1,-1); } + static inline QDpi logicalDpi(const QScreen *screen) { return QDpi(-1,-1); } }; namespace QHighDpi { diff --git a/src/gui/kernel/qplatformintegration.h b/src/gui/kernel/qplatformintegration.h index d9f349555a..01406958e2 100644 --- a/src/gui/kernel/qplatformintegration.h +++ b/src/gui/kernel/qplatformintegration.h @@ -106,7 +106,8 @@ public: ApplicationIcon, SwitchableWidgetComposition, TopStackedNativeChildWindows, - OpenGLOnRasterSurface + OpenGLOnRasterSurface, + MaximizeUsingFullscreenGeometry }; virtual ~QPlatformIntegration() { } diff --git a/src/gui/kernel/qplatformscreen.cpp b/src/gui/kernel/qplatformscreen.cpp index f3213bf5ea..e511a6f5c4 100644 --- a/src/gui/kernel/qplatformscreen.cpp +++ b/src/gui/kernel/qplatformscreen.cpp @@ -410,15 +410,22 @@ void QPlatformScreen::resizeMaximizedWindows() const QRect newGeometry = deviceIndependentGeometry(); const QRect newAvailableGeometry = QHighDpi::fromNative(availableGeometry(), QHighDpiScaling::factor(this), newGeometry.topLeft()); + const bool supportsMaximizeUsingFullscreen = QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::MaximizeUsingFullscreenGeometry); + for (QWindow *w : windows()) { // Skip non-platform windows, e.g., offscreen windows. if (!w->handle()) continue; - if (w->windowState() & Qt::WindowMaximized || w->geometry() == oldAvailableGeometry) + if (supportsMaximizeUsingFullscreen + && w->windowState() & Qt::WindowMaximized + && w->flags() & Qt::MaximizeUsingFullscreenGeometryHint) { + w->setGeometry(newGeometry); + } else if (w->windowState() & Qt::WindowMaximized || w->geometry() == oldAvailableGeometry) { w->setGeometry(newAvailableGeometry); - else if (w->windowState() & Qt::WindowFullScreen || w->geometry() == oldGeometry) + } else if (w->windowState() & Qt::WindowFullScreen || w->geometry() == oldGeometry) { w->setGeometry(newGeometry); + } } } @@ -609,4 +616,18 @@ int QPlatformScreen::preferredMode() const return 0; } +QList<QPlatformScreen *> QPlatformPlaceholderScreen::virtualSiblings() const +{ + QList<QPlatformScreen *> siblings; + + if (!m_virtualSibling) + return siblings; + + for (QScreen *screen : QGuiApplication::screens()) { + if (screen->handle() && screen->handle() != this) + siblings << screen->handle(); + } + return siblings; +} + QT_END_NAMESPACE diff --git a/src/gui/kernel/qplatformscreen.h b/src/gui/kernel/qplatformscreen.h index d7378aed51..0be7646032 100644 --- a/src/gui/kernel/qplatformscreen.h +++ b/src/gui/kernel/qplatformscreen.h @@ -105,6 +105,8 @@ public: QPlatformScreen(); virtual ~QPlatformScreen(); + virtual bool isPlaceholder() const { return false; } + virtual QPixmap grabWindow(WId window, int x, int y, int width, int height) const; virtual QRect geometry() const = 0; @@ -172,6 +174,27 @@ private: friend class QScreenPrivate; }; +// Qt doesn't currently support running with no platform screen +// QPA plugins can use this class to create a fake screen +class Q_GUI_EXPORT QPlatformPlaceholderScreen : public QPlatformScreen { +public: + // virtualSibling can be passed in to make the placeholder a sibling with other screens during + // the transitioning phase when the real screen is about to be removed, or the first real screen + // is about to be added. This is useful because Qt will currently recreate (but now show!) + // windows when they are moved from one virtual desktop to another, so if the last monitor is + // unplugged, then plugged in again, windows will be hidden unless the placeholder belongs to + // the same virtual desktop as the other screens. + QPlatformPlaceholderScreen(bool virtualSibling = true) : m_virtualSibling(virtualSibling) {} + bool isPlaceholder() const override { return true; } + QRect geometry() const override { return QRect(); } + QRect availableGeometry() const override { return QRect(); } + int depth() const override { return 32; } + QImage::Format format() const override { return QImage::Format::Format_RGB32; } + QList<QPlatformScreen *> virtualSiblings() const override; +private: + bool m_virtualSibling = true; +}; + QT_END_NAMESPACE #endif // QPLATFORMSCREEN_H diff --git a/src/gui/kernel/qscreen.cpp b/src/gui/kernel/qscreen.cpp index 7adf3db1b8..80de561297 100644 --- a/src/gui/kernel/qscreen.cpp +++ b/src/gui/kernel/qscreen.cpp @@ -700,6 +700,25 @@ void QScreenPrivate::updatePrimaryOrientation() } /*! + Returns the screen at \a point within the set of \l QScreen::virtualSiblings(), + or \c nullptr if outside of any screen. + + The \a point is in relation to the virtualGeometry() of each set of virtual + siblings. + + \since 5.15 +*/ +QScreen *QScreen::virtualSiblingAt(const QPoint &point) +{ + const auto &siblings = virtualSiblings(); + for (QScreen *sibling : siblings) { + if (sibling->geometry().contains(point)) + return sibling; + } + return nullptr; +} + +/*! Creates and returns a pixmap constructed by grabbing the contents of the given \a window restricted by QRect(\a x, \a y, \a width, \a height). diff --git a/src/gui/kernel/qscreen.h b/src/gui/kernel/qscreen.h index 14392d3036..88925ab731 100644 --- a/src/gui/kernel/qscreen.h +++ b/src/gui/kernel/qscreen.h @@ -125,6 +125,7 @@ public: QRect availableGeometry() const; QList<QScreen *> virtualSiblings() const; + QScreen *virtualSiblingAt(const QPoint &point); QSize virtualSize() const; QRect virtualGeometry() const; diff --git a/src/gui/kernel/qtestsupport_gui.cpp b/src/gui/kernel/qtestsupport_gui.cpp index 7aad4d8c7d..79da26f2ca 100644 --- a/src/gui/kernel/qtestsupport_gui.cpp +++ b/src/gui/kernel/qtestsupport_gui.cpp @@ -37,10 +37,15 @@ ** ****************************************************************************/ +#include <private/qguiapplication_p.h> + +#include <qpa/qplatformintegration.h> + #include "qtestsupport_gui.h" #include "qwindow.h" #include <QtCore/qtestsupport_core.h> +#include <QtCore/QDebug> QT_BEGIN_NAMESPACE @@ -55,6 +60,14 @@ QT_BEGIN_NAMESPACE */ Q_GUI_EXPORT bool QTest::qWaitForWindowActive(QWindow *window, int timeout) { + if (Q_UNLIKELY(!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation))) { + qWarning() << "qWaitForWindowActive was called on a platform that doesn't support window" + << "activation. This means there is an error in the test and it should either" + << "check for the WindowActivation platform capability before calling" + << "qWaitForWindowActivate, use qWaitForWindowExposed instead, or skip the test." + << "Falling back to qWaitForWindowExposed."; + return qWaitForWindowExposed(window, timeout); + } return QTest::qWaitFor([&]() { return window->isActive(); }, timeout); } diff --git a/src/gui/rhi/qrhivulkan.cpp b/src/gui/rhi/qrhivulkan.cpp index 103fea627a..2d69abb36b 100644 --- a/src/gui/rhi/qrhivulkan.cpp +++ b/src/gui/rhi/qrhivulkan.cpp @@ -1710,6 +1710,10 @@ QRhi::FrameOpResult QRhiVulkan::endFrame(QRhiSwapChain *swapChain, QRhi::EndFram presInfo.waitSemaphoreCount = 1; presInfo.pWaitSemaphores = &frame.drawSem; // gfxQueueFamilyIdx == presQueueFamilyIdx ? &frame.drawSem : &frame.presTransSem; + // Do platform-specific WM notification. F.ex. essential on Wayland in + // order to circumvent driver frame callbacks + inst->presentAboutToBeQueued(swapChainD->window); + VkResult err = vkQueuePresentKHR(gfxQueue, &presInfo); if (err != VK_SUCCESS) { if (err == VK_ERROR_OUT_OF_DATE_KHR) { diff --git a/src/gui/rhi/qshader.cpp b/src/gui/rhi/qshader.cpp index 9d35d83336..dc6060f882 100644 --- a/src/gui/rhi/qshader.cpp +++ b/src/gui/rhi/qshader.cpp @@ -214,9 +214,6 @@ QT_BEGIN_NAMESPACE QShader, it indicates no shader code was found for the requested key. */ -static const int QSB_VERSION = 2; -static const int QSB_VERSION_WITHOUT_BINDINGS = 1; - /*! Constructs a new, empty (and thus invalid) QShader instance. */ @@ -368,9 +365,9 @@ QByteArray QShader::serialized() const if (!buf.open(QIODevice::WriteOnly)) return QByteArray(); - ds << QSB_VERSION; + ds << QShaderPrivate::QSB_VERSION; ds << int(d->stage); - ds << d->desc.toBinaryJson(); + ds << d->desc.toCbor(); ds << d->shaders.count(); for (auto it = d->shaders.cbegin(), itEnd = d->shaders.cend(); it != itEnd; ++it) { const QShaderKey &k(it.key()); @@ -429,9 +426,12 @@ QShader QShader::fromSerialized(const QByteArray &data) Q_ASSERT(d->ref.loadRelaxed() == 1); // must be detached int intVal; ds >> intVal; - const int qsbVersion = intVal; - if (qsbVersion != QSB_VERSION && qsbVersion != QSB_VERSION_WITHOUT_BINDINGS) { - qWarning("Attempted to deserialize QShader with unknown version %d.", qsbVersion); + d->qsbVersion = intVal; + if (d->qsbVersion != QShaderPrivate::QSB_VERSION + && d->qsbVersion != QShaderPrivate::QSB_VERSION_WITH_BINARY_JSON + && d->qsbVersion != QShaderPrivate::QSB_VERSION_WITHOUT_BINDINGS) + { + qWarning("Attempted to deserialize QShader with unknown version %d.", d->qsbVersion); return QShader(); } @@ -439,7 +439,10 @@ QShader QShader::fromSerialized(const QByteArray &data) d->stage = Stage(intVal); QByteArray descBin; ds >> descBin; - d->desc = QShaderDescription::fromBinaryJson(descBin); + if (d->qsbVersion > QShaderPrivate::QSB_VERSION_WITH_BINARY_JSON) + d->desc = QShaderDescription::fromCbor(descBin); + else + d->desc = QShaderDescription::fromBinaryJson(descBin); int count; ds >> count; for (int i = 0; i < count; ++i) { @@ -454,7 +457,7 @@ QShader QShader::fromSerialized(const QByteArray &data) d->shaders[k] = shader; } - if (qsbVersion != QSB_VERSION_WITHOUT_BINDINGS) { + if (d->qsbVersion > QShaderPrivate::QSB_VERSION_WITHOUT_BINDINGS) { ds >> count; for (int i = 0; i < count; ++i) { QShaderKey k; diff --git a/src/gui/rhi/qshader_p_p.h b/src/gui/rhi/qshader_p_p.h index 4535e01491..8c89f2b45f 100644 --- a/src/gui/rhi/qshader_p_p.h +++ b/src/gui/rhi/qshader_p_p.h @@ -57,6 +57,10 @@ QT_BEGIN_NAMESPACE struct Q_GUI_EXPORT QShaderPrivate { + static const int QSB_VERSION = 3; + static const int QSB_VERSION_WITH_BINARY_JSON = 2; + static const int QSB_VERSION_WITHOUT_BINDINGS = 1; + QShaderPrivate() : ref(1) { @@ -64,6 +68,7 @@ struct Q_GUI_EXPORT QShaderPrivate QShaderPrivate(const QShaderPrivate *other) : ref(1), + qsbVersion(other->qsbVersion), stage(other->stage), desc(other->desc), shaders(other->shaders), @@ -75,6 +80,7 @@ struct Q_GUI_EXPORT QShaderPrivate static const QShaderPrivate *get(const QShader *s) { return s->d; } QAtomicInt ref; + int qsbVersion = QSB_VERSION; QShader::Stage stage = QShader::VertexStage; QShaderDescription desc; QHash<QShaderKey, QShaderCode> shaders; diff --git a/src/gui/rhi/qshaderdescription.cpp b/src/gui/rhi/qshaderdescription.cpp index 179d5f3a07..d0f73f6aa7 100644 --- a/src/gui/rhi/qshaderdescription.cpp +++ b/src/gui/rhi/qshaderdescription.cpp @@ -38,6 +38,9 @@ #include <QDebug> #include <QJsonObject> #include <QJsonArray> +#include <QCborValue> +#include <QCborMap> +#include <QCborArray> QT_BEGIN_NAMESPACE @@ -335,11 +338,27 @@ bool QShaderDescription::isValid() const /*! \return a serialized binary version of the data. - \sa toJson() + \sa toJson(), toCbor() */ QByteArray QShaderDescription::toBinaryJson() const { +#if QT_CONFIG(binaryjson) return d->makeDoc().toBinaryData(); +#else + qWarning("Cannot generate binary JSON from QShaderDescription due to disabled binaryjson feature"); + return QByteArray(); +#endif +} + +/*! + \return a serialized binary version of the data in CBOR (Concise Binary + Object Representation) format. + + \sa QCborValue, toBinaryJson(), toJson() + */ +QByteArray QShaderDescription::toCbor() const +{ + return QCborValue::fromJsonValue(d->makeDoc().object()).toCbor(); } /*! @@ -347,7 +366,7 @@ QByteArray QShaderDescription::toBinaryJson() const \note There is no deserialization method provided for JSON text. - \sa toBinaryJson() + \sa toBinaryJson(), toCbor() */ QByteArray QShaderDescription::toJson() const { @@ -357,11 +376,38 @@ QByteArray QShaderDescription::toJson() const /*! Deserializes the given binary JSON \a data and returns a new QShaderDescription. + + \sa fromCbor() */ QShaderDescription QShaderDescription::fromBinaryJson(const QByteArray &data) { QShaderDescription desc; +#if QT_CONFIG(binaryjson) QShaderDescriptionPrivate::get(&desc)->loadDoc(QJsonDocument::fromBinaryData(data)); +#else + Q_UNUSED(data); + qWarning("Cannot load QShaderDescription from binary JSON due to disabled binaryjson feature"); +#endif + return desc; +} + +/*! + Deserializes the given CBOR \a data and returns a new QShaderDescription. + + \sa fromBinaryJson() + */ +QShaderDescription QShaderDescription::fromCbor(const QByteArray &data) +{ + QShaderDescription desc; + const QCborValue cbor = QCborValue::fromCbor(data); + if (cbor.isMap()) { + const QJsonDocument doc(cbor.toMap().toJsonObject()); + QShaderDescriptionPrivate::get(&desc)->loadDoc(doc); + } + if (cbor.isArray()) { + const QJsonDocument doc(cbor.toArray().toJsonArray()); + QShaderDescriptionPrivate::get(&desc)->loadDoc(doc); + } return desc; } @@ -1119,4 +1165,106 @@ void QShaderDescriptionPrivate::loadDoc(const QJsonDocument &doc) } } +/*! + Returns \c true if the two QShaderDescription objects \a lhs and \a rhs are + equal. + + \relates QShaderDescription + */ +bool operator==(const QShaderDescription &lhs, const QShaderDescription &rhs) Q_DECL_NOTHROW +{ + if (lhs.d == rhs.d) + return true; + + return lhs.d->inVars == rhs.d->inVars + && lhs.d->outVars == rhs.d->outVars + && lhs.d->uniformBlocks == rhs.d->uniformBlocks + && lhs.d->pushConstantBlocks == rhs.d->pushConstantBlocks + && lhs.d->storageBlocks == rhs.d->storageBlocks + && lhs.d->combinedImageSamplers == rhs.d->combinedImageSamplers + && lhs.d->storageImages == rhs.d->storageImages + && lhs.d->localSize == rhs.d->localSize; +} + +/*! + Returns \c true if the two InOutVariable objects \a lhs and \a rhs are + equal. + + \relates QShaderDescription::InOutVariable + */ +bool operator==(const QShaderDescription::InOutVariable &lhs, const QShaderDescription::InOutVariable &rhs) Q_DECL_NOTHROW +{ + return lhs.name == rhs.name + && lhs.type == rhs.type + && lhs.location == rhs.location + && lhs.binding == rhs.binding + && lhs.descriptorSet == rhs.descriptorSet + && lhs.imageFormat == rhs.imageFormat + && lhs.imageFlags == rhs.imageFlags; +} + +/*! + Returns \c true if the two BlockVariable objects \a lhs and \a rhs are + equal. + + \relates QShaderDescription::BlockVariable + */ +bool operator==(const QShaderDescription::BlockVariable &lhs, const QShaderDescription::BlockVariable &rhs) Q_DECL_NOTHROW +{ + return lhs.name == rhs.name + && lhs.type == rhs.type + && lhs.offset == rhs.offset + && lhs.size == rhs.size + && lhs.arrayDims == rhs.arrayDims + && lhs.arrayStride == rhs.arrayStride + && lhs.matrixStride == rhs.matrixStride + && lhs.matrixIsRowMajor == rhs.matrixIsRowMajor + && lhs.structMembers == rhs.structMembers; +} + +/*! + Returns \c true if the two UniformBlock objects \a lhs and \a rhs are + equal. + + \relates QShaderDescription::UniformBlock + */ +bool operator==(const QShaderDescription::UniformBlock &lhs, const QShaderDescription::UniformBlock &rhs) Q_DECL_NOTHROW +{ + return lhs.blockName == rhs.blockName + && lhs.structName == rhs.structName + && lhs.size == rhs.size + && lhs.binding == rhs.binding + && lhs.descriptorSet == rhs.descriptorSet + && lhs.members == rhs.members; +} + +/*! + Returns \c true if the two PushConstantBlock objects \a lhs and \a rhs are + equal. + + \relates QShaderDescription::PushConstantBlock + */ +bool operator==(const QShaderDescription::PushConstantBlock &lhs, const QShaderDescription::PushConstantBlock &rhs) Q_DECL_NOTHROW +{ + return lhs.name == rhs.name + && lhs.size == rhs.size + && lhs.members == rhs.members; +} + +/*! + Returns \c true if the two StorageBlock objects \a lhs and \a rhs are + equal. + + \relates QShaderDescription::StorageBlock + */ +bool operator==(const QShaderDescription::StorageBlock &lhs, const QShaderDescription::StorageBlock &rhs) Q_DECL_NOTHROW +{ + return lhs.blockName == rhs.blockName + && lhs.instanceName == rhs.instanceName + && lhs.knownSize == rhs.knownSize + && lhs.binding == rhs.binding + && lhs.descriptorSet == rhs.descriptorSet + && lhs.members == rhs.members; +} + QT_END_NAMESPACE diff --git a/src/gui/rhi/qshaderdescription_p.h b/src/gui/rhi/qshaderdescription_p.h index 5a63b998cd..e02a53dcb5 100644 --- a/src/gui/rhi/qshaderdescription_p.h +++ b/src/gui/rhi/qshaderdescription_p.h @@ -69,9 +69,11 @@ public: bool isValid() const; QByteArray toBinaryJson() const; + QByteArray toCbor() const; QByteArray toJson() const; static QShaderDescription fromBinaryJson(const QByteArray &data); + static QShaderDescription fromCbor(const QByteArray &data); enum VariableType { Unknown = 0, @@ -263,6 +265,7 @@ private: #ifndef QT_NO_DEBUG_STREAM friend Q_GUI_EXPORT QDebug operator<<(QDebug, const QShaderDescription &); #endif + friend Q_GUI_EXPORT bool operator==(const QShaderDescription &lhs, const QShaderDescription &rhs) Q_DECL_NOTHROW; }; Q_DECLARE_OPERATORS_FOR_FLAGS(QShaderDescription::ImageFlags) @@ -276,6 +279,43 @@ Q_GUI_EXPORT QDebug operator<<(QDebug, const QShaderDescription::PushConstantBlo Q_GUI_EXPORT QDebug operator<<(QDebug, const QShaderDescription::StorageBlock &); #endif +Q_GUI_EXPORT bool operator==(const QShaderDescription &lhs, const QShaderDescription &rhs) Q_DECL_NOTHROW; +Q_GUI_EXPORT bool operator==(const QShaderDescription::InOutVariable &lhs, const QShaderDescription::InOutVariable &rhs) Q_DECL_NOTHROW; +Q_GUI_EXPORT bool operator==(const QShaderDescription::BlockVariable &lhs, const QShaderDescription::BlockVariable &rhs) Q_DECL_NOTHROW; +Q_GUI_EXPORT bool operator==(const QShaderDescription::UniformBlock &lhs, const QShaderDescription::UniformBlock &rhs) Q_DECL_NOTHROW; +Q_GUI_EXPORT bool operator==(const QShaderDescription::PushConstantBlock &lhs, const QShaderDescription::PushConstantBlock &rhs) Q_DECL_NOTHROW; +Q_GUI_EXPORT bool operator==(const QShaderDescription::StorageBlock &lhs, const QShaderDescription::StorageBlock &rhs) Q_DECL_NOTHROW; + +inline bool operator!=(const QShaderDescription &lhs, const QShaderDescription &rhs) Q_DECL_NOTHROW +{ + return !(lhs == rhs); +} + +inline bool operator!=(const QShaderDescription::InOutVariable &lhs, const QShaderDescription::InOutVariable &rhs) Q_DECL_NOTHROW +{ + return !(lhs == rhs); +} + +inline bool operator!=(const QShaderDescription::BlockVariable &lhs, const QShaderDescription::BlockVariable &rhs) Q_DECL_NOTHROW +{ + return !(lhs == rhs); +} + +inline bool operator!=(const QShaderDescription::UniformBlock &lhs, const QShaderDescription::UniformBlock &rhs) Q_DECL_NOTHROW +{ + return !(lhs == rhs); +} + +inline bool operator!=(const QShaderDescription::PushConstantBlock &lhs, const QShaderDescription::PushConstantBlock &rhs) Q_DECL_NOTHROW +{ + return !(lhs == rhs); +} + +inline bool operator!=(const QShaderDescription::StorageBlock &lhs, const QShaderDescription::StorageBlock &rhs) Q_DECL_NOTHROW +{ + return !(lhs == rhs); +} + QT_END_NAMESPACE #endif diff --git a/src/gui/text/qcssparser.cpp b/src/gui/text/qcssparser.cpp index ce7c7610c1..793ef5774b 100644 --- a/src/gui/text/qcssparser.cpp +++ b/src/gui/text/qcssparser.cpp @@ -123,6 +123,7 @@ static const QCssKnownValue properties[NumProperties - 1] = { { "font-variant", FontVariant }, { "font-weight", FontWeight }, { "height", Height }, + { "icon", QtIcon }, { "image", QtImage }, { "image-position", QtImageAlignment }, { "left", Left }, @@ -1379,6 +1380,37 @@ bool ValueExtractor::extractImage(QIcon *icon, Qt::Alignment *a, QSize *size) return hit; } +bool ValueExtractor::extractIcon(QIcon *icon, QSize *size) +{ + // Find last declaration that specifies an icon + const auto declaration = std::find_if( + declarations.rbegin(), declarations.rend(), + [](const Declaration &decl) { return decl.d->propertyId == QtIcon; }); + if (declaration == declarations.rend()) + return false; + + *icon = declaration->iconValue(); + + // If the value contains a URI, try to get the size of the icon + if (declaration->d->values.isEmpty()) + return true; + + const auto &propertyValue = declaration->d->values.constFirst(); + if (propertyValue.type != Value::Uri) + return true; + + // First try to read just the size from the image without loading it + const QString url(propertyValue.variant.toString()); + QImageReader imageReader(url); + *size = imageReader.size(); + if (!size->isNull()) + return true; + + // Get the size by loading the image instead + *size = imageReader.read().size(); + return true; +} + /////////////////////////////////////////////////////////////////////////////// // Declaration QColor Declaration::colorValue(const QPalette &pal) const diff --git a/src/gui/text/qcssparser_p.h b/src/gui/text/qcssparser_p.h index ab85e76cf3..b8bf259dda 100644 --- a/src/gui/text/qcssparser_p.h +++ b/src/gui/text/qcssparser_p.h @@ -198,6 +198,7 @@ enum Property { QtLineHeightType, FontKerning, QtForegroundTextureCacheKey, + QtIcon, NumProperties }; @@ -855,6 +856,7 @@ struct Q_GUI_EXPORT ValueExtractor bool extractPalette(QBrush *fg, QBrush *sfg, QBrush *sbg, QBrush *abg); int extractStyleFeatures(); bool extractImage(QIcon *icon, Qt::Alignment *a, QSize *size); + bool extractIcon(QIcon *icon, QSize *size); int lengthValue(const Declaration &decl); diff --git a/src/gui/text/qharfbuzzng.cpp b/src/gui/text/qharfbuzzng.cpp index 2f25aea92b..397e6cc49f 100644 --- a/src/gui/text/qharfbuzzng.cpp +++ b/src/gui/text/qharfbuzzng.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2019 The Qt Company Ltd. ** Copyright (C) 2013 Konstantin Ritt ** Contact: https://www.qt.io/licensing/ ** @@ -216,7 +216,20 @@ static const hb_script_t _qtscript_to_hbscript[] = { HB_SCRIPT_MASARAM_GONDI, HB_SCRIPT_NUSHU, HB_SCRIPT_SOYOMBO, - HB_SCRIPT_ZANABAZAR_SQUARE + HB_SCRIPT_ZANABAZAR_SQUARE, + + // Unicode 12.1 additions (not present in harfbuzz-ng 1.7.4) + hb_script_t(HB_TAG('D', 'o', 'g', 'r')), // Script_Dogra + hb_script_t(HB_TAG('G', 'o', 'n', 'g')), // Script_GunjalaGondi + hb_script_t(HB_TAG('R', 'o', 'h', 'g')), // Script_HanifiRohingya + hb_script_t(HB_TAG('M', 'a', 'k', 'a')), // Script_Makasar + hb_script_t(HB_TAG('M', 'e', 'd', 'f')), // Script_Medefaidrin + hb_script_t(HB_TAG('S', 'o', 'g', 'o')), // Script_OldSogdian + hb_script_t(HB_TAG('S', 'o', 'g', 'd')), // Script_Sogdian + hb_script_t(HB_TAG('E', 'l', 'y', 'm')), // Script_Elymaic + hb_script_t(HB_TAG('N', 'a', 'n', 'd')), // Script_Nandinagari + hb_script_t(HB_TAG('H', 'm', 'n', 'p')), // Script_NyiakengPuachueHmong + hb_script_t(HB_TAG('W', 'c', 'h', 'o')), // Script_Wancho }; Q_STATIC_ASSERT(QChar::ScriptCount == sizeof(_qtscript_to_hbscript) / sizeof(_qtscript_to_hbscript[0])); @@ -682,12 +695,12 @@ _hb_qt_font_create(QFontEngine *fe) return NULL; } - const int y_ppem = fe->fontDef.pixelSize; - const int x_ppem = (fe->fontDef.pixelSize * fe->fontDef.stretch) / 100; + const qreal y_ppem = fe->fontDef.pixelSize; + const qreal x_ppem = (fe->fontDef.pixelSize * fe->fontDef.stretch) / 100.0; hb_font_set_funcs(font, hb_qt_get_font_funcs(), (void *)fe, NULL); - hb_font_set_scale(font, QFixed(x_ppem).value(), -QFixed(y_ppem).value()); - hb_font_set_ppem(font, x_ppem, y_ppem); + hb_font_set_scale(font, QFixed::fromReal(x_ppem).value(), -QFixed::fromReal(y_ppem).value()); + hb_font_set_ppem(font, int(x_ppem), int(y_ppem)); hb_font_set_ptem(font, fe->fontDef.pointSize); diff --git a/src/gui/vulkan/qplatformvulkaninstance.cpp b/src/gui/vulkan/qplatformvulkaninstance.cpp index 9d044bfd58..1b5d3370f0 100644 --- a/src/gui/vulkan/qplatformvulkaninstance.cpp +++ b/src/gui/vulkan/qplatformvulkaninstance.cpp @@ -80,6 +80,11 @@ QPlatformVulkanInstance::~QPlatformVulkanInstance() { } +void QPlatformVulkanInstance::presentAboutToBeQueued(QWindow *window) +{ + Q_UNUSED(window); +} + void QPlatformVulkanInstance::presentQueued(QWindow *window) { Q_UNUSED(window); diff --git a/src/gui/vulkan/qplatformvulkaninstance.h b/src/gui/vulkan/qplatformvulkaninstance.h index d47c59b5db..f96f1720fb 100644 --- a/src/gui/vulkan/qplatformvulkaninstance.h +++ b/src/gui/vulkan/qplatformvulkaninstance.h @@ -77,6 +77,7 @@ public: virtual QByteArrayList enabledExtensions() const = 0; virtual PFN_vkVoidFunction getInstanceProcAddr(const char *name) = 0; virtual bool supportsPresent(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex, QWindow *window) = 0; + virtual void presentAboutToBeQueued(QWindow *window); virtual void presentQueued(QWindow *window); virtual void setDebugFilters(const QVector<QVulkanInstance::DebugFilter> &filters); diff --git a/src/gui/vulkan/qvulkaninstance.cpp b/src/gui/vulkan/qvulkaninstance.cpp index 0605d88cca..daf37e3dc8 100644 --- a/src/gui/vulkan/qvulkaninstance.cpp +++ b/src/gui/vulkan/qvulkaninstance.cpp @@ -775,6 +775,20 @@ bool QVulkanInstance::supportsPresent(VkPhysicalDevice physicalDevice, uint32_t } /*! + This function should be called by the application's renderer before queuing + a present operation for \a window. + + While on some platforms this will be a no-op, some may perform windowing + system dependent synchronization. For example, on Wayland this will + add send a wl_surface.frame request in order to prevent the driver from + blocking for minimized windows. + */ +void QVulkanInstance::presentAboutToBeQueued(QWindow *window) +{ + d_ptr->platformInst->presentAboutToBeQueued(window); +} + +/*! This function should be called by the application's renderer after queuing a present operation for \a window. diff --git a/src/gui/vulkan/qvulkaninstance.h b/src/gui/vulkan/qvulkaninstance.h index 70f2fd5102..5b3db9a4c8 100644 --- a/src/gui/vulkan/qvulkaninstance.h +++ b/src/gui/vulkan/qvulkaninstance.h @@ -186,6 +186,7 @@ public: bool supportsPresent(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex, QWindow *window); + void presentAboutToBeQueued(QWindow *window); void presentQueued(QWindow *window); typedef bool (*DebugFilter)(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objectType, uint64_t object, diff --git a/src/gui/vulkan/qvulkanwindow.cpp b/src/gui/vulkan/qvulkanwindow.cpp index 4b5c2b56ee..790bef9e14 100644 --- a/src/gui/vulkan/qvulkanwindow.cpp +++ b/src/gui/vulkan/qvulkanwindow.cpp @@ -647,18 +647,40 @@ void QVulkanWindowPrivate::init() #endif qCDebug(lcGuiVk, "Using queue families: graphics = %u present = %u", gfxQueueFamilyIdx, presQueueFamilyIdx); - VkDeviceQueueCreateInfo queueInfo[2]; + QVector<VkDeviceQueueCreateInfo> queueInfo; + queueInfo.reserve(2); const float prio[] = { 0 }; - memset(queueInfo, 0, sizeof(queueInfo)); - queueInfo[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; - queueInfo[0].queueFamilyIndex = gfxQueueFamilyIdx; - queueInfo[0].queueCount = 1; - queueInfo[0].pQueuePriorities = prio; + VkDeviceQueueCreateInfo addQueueInfo; + memset(&addQueueInfo, 0, sizeof(addQueueInfo)); + addQueueInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + addQueueInfo.queueFamilyIndex = gfxQueueFamilyIdx; + addQueueInfo.queueCount = 1; + addQueueInfo.pQueuePriorities = prio; + queueInfo.append(addQueueInfo); if (gfxQueueFamilyIdx != presQueueFamilyIdx) { - queueInfo[1].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; - queueInfo[1].queueFamilyIndex = presQueueFamilyIdx; - queueInfo[1].queueCount = 1; - queueInfo[1].pQueuePriorities = prio; + addQueueInfo.queueFamilyIndex = presQueueFamilyIdx; + addQueueInfo.queueCount = 1; + addQueueInfo.pQueuePriorities = prio; + queueInfo.append(addQueueInfo); + } + if (queueCreateInfoModifier) { + queueCreateInfoModifier(queueFamilyProps.constData(), queueCount, queueInfo); + bool foundGfxQueue = false; + bool foundPresQueue = false; + for (const VkDeviceQueueCreateInfo& createInfo : qAsConst(queueInfo)) { + foundGfxQueue |= createInfo.queueFamilyIndex == gfxQueueFamilyIdx; + foundPresQueue |= createInfo.queueFamilyIndex == presQueueFamilyIdx; + } + if (!foundGfxQueue) { + qWarning("QVulkanWindow: Graphics queue missing after call to queueCreateInfoModifier"); + status = StatusFail; + return; + } + if (!foundPresQueue) { + qWarning("QVulkanWindow: Present queue missing after call to queueCreateInfoModifier"); + status = StatusFail; + return; + } } // Filter out unsupported extensions in order to keep symmetry @@ -676,8 +698,8 @@ void QVulkanWindowPrivate::init() VkDeviceCreateInfo devInfo; memset(&devInfo, 0, sizeof(devInfo)); devInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; - devInfo.queueCreateInfoCount = gfxQueueFamilyIdx == presQueueFamilyIdx ? 1 : 2; - devInfo.pQueueCreateInfos = queueInfo; + devInfo.queueCreateInfoCount = queueInfo.size(); + devInfo.pQueueCreateInfos = queueInfo.constData(); devInfo.enabledExtensionCount = devExts.count(); devInfo.ppEnabledExtensionNames = devExts.constData(); @@ -1546,6 +1568,52 @@ bool QVulkanWindow::event(QEvent *e) } /*! + \typedef QVulkanWindow::QueueCreateInfoModifier + + A function function that is called during graphics initialization to add + additAional queues that should be created. + + Set if the renderer needs additional queues besides the default graphics + queue (e.g. a transfer queue). + The provided queue family properties can be used to select the indices for + the additional queues. + The renderer can subsequently request the actual queue in initResources(). + + Note when requesting additional graphics queues: Qt itself always requests + a graphics queue, you'll need to search queueCreateInfo for the appropriate + entry and manipulate it to obtain the additional queue. + + \sa setQueueCreateInfoModifier() + */ + +/*! + Return a previously set queue create info modification function. + + \sa setQueueCreateInfoModifier() + + \since 5.15 + */ +QVulkanWindow::QueueCreateInfoModifier QVulkanWindow::queueCreateInfoModifier() const +{ + Q_D(const QVulkanWindow); + return d->queueCreateInfoModifier; +} + +/*! + Set a queue create info modification function. + + \sa queueCreateInfoModifier() + + \since 5.15 + */ +void QVulkanWindow::setQueueCreateInfoModifier(QueueCreateInfoModifier modifier) +{ + Q_D(QVulkanWindow); + d->queueCreateInfoModifier = modifier; +} + + +/*! Returns true if this window has successfully initialized all Vulkan resources, including the swapchain. @@ -1950,6 +2018,10 @@ void QVulkanWindowPrivate::endFrame() presInfo.waitSemaphoreCount = 1; presInfo.pWaitSemaphores = gfxQueueFamilyIdx == presQueueFamilyIdx ? &frame.drawSem : &frame.presTransSem; + // Do platform-specific WM notification. F.ex. essential on Wayland in + // order to circumvent driver frame callbacks + inst->presentAboutToBeQueued(q); + err = vkQueuePresentKHR(gfxQueue, &presInfo); if (err != VK_SUCCESS) { if (err == VK_ERROR_OUT_OF_DATE_KHR) { @@ -2211,6 +2283,23 @@ VkQueue QVulkanWindow::graphicsQueue() const } /*! + Returns the family index of the active graphics queue. + + \note Calling this function is only valid from the invocation of + QVulkanWindowRenderer::initResources() up until + QVulkanWindowRenderer::releaseResources(). Implementations of + QVulkanWindowRenderer::updateQueueCreateInfo() can also call this + function. + + \since 5.15 + */ +uint32_t QVulkanWindow::graphicsQueueFamilyIndex() const +{ + Q_D(const QVulkanWindow); + return d->gfxQueueFamilyIdx; +} + +/*! Returns the active graphics command pool. \note Calling this function is only valid from the invocation of diff --git a/src/gui/vulkan/qvulkanwindow.h b/src/gui/vulkan/qvulkanwindow.h index 927c81042f..530b6c0744 100644 --- a/src/gui/vulkan/qvulkanwindow.h +++ b/src/gui/vulkan/qvulkanwindow.h @@ -103,6 +103,12 @@ public: QVector<int> supportedSampleCounts(); void setSampleCount(int sampleCount); + typedef std::function<void(const VkQueueFamilyProperties *, + uint32_t, + QVector<VkDeviceQueueCreateInfo> &)> QueueCreateInfoModifier; + QueueCreateInfoModifier queueCreateInfoModifier() const; + void setQueueCreateInfoModifier(QueueCreateInfoModifier modifier); + bool isValid() const; virtual QVulkanWindowRenderer *createRenderer(); @@ -112,6 +118,7 @@ public: const VkPhysicalDeviceProperties *physicalDeviceProperties() const; VkDevice device() const; VkQueue graphicsQueue() const; + uint32_t graphicsQueueFamilyIndex() const; VkCommandPool graphicsCommandPool() const; uint32_t hostVisibleMemoryIndex() const; uint32_t deviceLocalMemoryIndex() const; diff --git a/src/gui/vulkan/qvulkanwindow_p.h b/src/gui/vulkan/qvulkanwindow_p.h index fb374a5564..777be237a8 100644 --- a/src/gui/vulkan/qvulkanwindow_p.h +++ b/src/gui/vulkan/qvulkanwindow_p.h @@ -102,6 +102,7 @@ public: QHash<VkPhysicalDevice, QVulkanInfoVector<QVulkanExtension> > supportedDevExtensions; QVector<VkFormat> requestedColorFormats; VkSampleCountFlagBits sampleCount = VK_SAMPLE_COUNT_1_BIT; + QVulkanWindow::QueueCreateInfoModifier queueCreateInfoModifier; VkDevice dev = VK_NULL_HANDLE; QVulkanDeviceFunctions *devFuncs; |