diff --git a/3rdparty/wlroots/include/wlr/render/vulkan.h b/3rdparty/wlroots/include/wlr/render/vulkan.h index 50f8c558d3..5ee84e05fb 100644 --- a/3rdparty/wlroots/include/wlr/render/vulkan.h +++ b/3rdparty/wlroots/include/wlr/render/vulkan.h @@ -2,6 +2,7 @@ * This an unstable interface of wlroots. No guarantees are made regarding the * future consistency of this API. */ + #ifndef WLR_USE_UNSTABLE #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif @@ -9,9 +10,14 @@ #ifndef WLR_RENDER_VULKAN_H #define WLR_RENDER_VULKAN_H +#include +#include + #include #include +struct wlr_buffer; + struct wlr_vk_image_attribs { VkImage image; VkImageLayout layout; @@ -32,5 +38,15 @@ void wlr_vk_texture_get_image_attribs(struct wlr_texture *texture, struct wlr_vk_image_attribs *attribs); bool wlr_vk_texture_has_alpha(struct wlr_texture *texture); -#endif +/* waylib extensions (not upstream): Qt Quick RHI bridge helpers. + * Reuse wlroots' own wlr_vk_render_buffer (same path as tinywl/scene), + * instead of creating a parallel VkImage import of the scanout dmabuf. */ +bool waylib_vk_renderer_get_render_buffer_attribs(struct wlr_renderer *renderer, + struct wlr_buffer *buffer, struct wlr_vk_image_attribs *attribs); +bool waylib_vk_renderer_record_render_buffer_acquire(struct wlr_renderer *renderer, + struct wlr_buffer *buffer, VkCommandBuffer cb); +bool waylib_vk_renderer_record_render_buffer_release(struct wlr_renderer *renderer, + struct wlr_buffer *buffer, VkCommandBuffer cb, VkImageLayout old_layout); +bool waylib_vk_renderer_flush_stage(struct wlr_renderer *renderer); +#endif diff --git a/3rdparty/wlroots/render/vulkan/renderer.c b/3rdparty/wlroots/render/vulkan/renderer.c index e87a00e096..802642f3c8 100644 --- a/3rdparty/wlroots/render/vulkan/renderer.c +++ b/3rdparty/wlroots/render/vulkan/renderer.c @@ -413,6 +413,17 @@ bool vulkan_submit_stage_wait(struct wlr_vk_renderer *renderer) { return vulkan_wait_command_buffer(cb, renderer); } +bool waylib_vk_renderer_flush_stage(struct wlr_renderer *wlr_renderer) { + if (wlr_renderer == NULL || !wlr_renderer_is_vk(wlr_renderer)) { + return false; + } + struct wlr_vk_renderer *renderer = vulkan_get_renderer(wlr_renderer); + if (renderer->stage.cb == NULL) { + return true; + } + return vulkan_submit_stage_wait(renderer); +} + struct wlr_vk_format_props *vulkan_format_props_from_drm( struct wlr_vk_device *dev, uint32_t drm_fmt) { for (size_t i = 0u; i < dev->format_prop_count; ++i) { @@ -947,6 +958,137 @@ static struct wlr_vk_render_buffer *get_render_buffer( return buffer; } +static struct wlr_vk_render_buffer *get_or_create_render_buffer( + struct wlr_vk_renderer *renderer, struct wlr_buffer *wlr_buffer) { + struct wlr_vk_render_buffer *render_buffer = + get_render_buffer(renderer, wlr_buffer); + if (render_buffer != NULL) { + return render_buffer; + } + return create_render_buffer(renderer, wlr_buffer); +} + +bool waylib_vk_renderer_get_render_buffer_attribs(struct wlr_renderer *wlr_renderer, + struct wlr_buffer *wlr_buffer, struct wlr_vk_image_attribs *attribs) { + if (wlr_renderer == NULL || wlr_buffer == NULL || attribs == NULL) { + return false; + } + if (!wlr_renderer_is_vk(wlr_renderer)) { + return false; + } + + struct wlr_vk_renderer *renderer = vulkan_get_renderer(wlr_renderer); + struct wlr_vk_render_buffer *render_buffer = + get_or_create_render_buffer(renderer, wlr_buffer); + if (render_buffer == NULL) { + return false; + } + + struct wlr_dmabuf_attributes dmabuf = {0}; + if (!wlr_buffer_get_dmabuf(wlr_buffer, &dmabuf)) { + wlr_log(WLR_ERROR, "wlr_buffer_get_dmabuf() failed"); + return false; + } + + const struct wlr_vk_format *format = vulkan_get_format_from_drm(dmabuf.format); + if (format == NULL) { + wlr_log(WLR_ERROR, "Unsupported pixel format %"PRIx32 " (%.4s)", + dmabuf.format, (const char *)&dmabuf.format); + return false; + } + + attribs->image = render_buffer->image; + attribs->layout = VK_IMAGE_LAYOUT_GENERAL; + attribs->format = format->vk; + return true; +} + +bool waylib_vk_renderer_record_render_buffer_acquire(struct wlr_renderer *wlr_renderer, + struct wlr_buffer *wlr_buffer, VkCommandBuffer cb) { + if (wlr_renderer == NULL || wlr_buffer == NULL || cb == VK_NULL_HANDLE) { + return false; + } + if (!wlr_renderer_is_vk(wlr_renderer)) { + return false; + } + + struct wlr_vk_renderer *renderer = vulkan_get_renderer(wlr_renderer); + struct wlr_vk_render_buffer *render_buffer = + get_or_create_render_buffer(renderer, wlr_buffer); + if (render_buffer == NULL) { + return false; + } + + /* Match wlroots pass.c / tinywl: foreign ownership transfer into the + * graphics queue before writing the scanout image. */ + VkImageLayout src_layout = VK_IMAGE_LAYOUT_GENERAL; + if (!render_buffer->srgb.transitioned && !render_buffer->plain.transitioned) { + src_layout = VK_IMAGE_LAYOUT_PREINITIALIZED; + } + // The sRGB and plain pathways share the imported VkImage. Once this + // acquire barrier runs, both pathways observe GENERAL on subsequent use. + render_buffer->srgb.transitioned = true; + render_buffer->plain.transitioned = true; + + VkImageMemoryBarrier barrier = { + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_FOREIGN_EXT, + .dstQueueFamilyIndex = renderer->dev->queue_family, + .image = render_buffer->image, + .oldLayout = src_layout, + .newLayout = VK_IMAGE_LAYOUT_GENERAL, + .srcAccessMask = 0, + .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | + VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, + .subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .subresourceRange.layerCount = 1, + .subresourceRange.levelCount = 1, + }; + + vkCmdPipelineBarrier(cb, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + 0, 0, NULL, 0, NULL, 1, &barrier); + return true; +} + +bool waylib_vk_renderer_record_render_buffer_release(struct wlr_renderer *wlr_renderer, + struct wlr_buffer *wlr_buffer, VkCommandBuffer cb, + VkImageLayout old_layout) { + if (wlr_renderer == NULL || wlr_buffer == NULL || cb == VK_NULL_HANDLE) { + return false; + } + if (!wlr_renderer_is_vk(wlr_renderer)) { + return false; + } + + struct wlr_vk_renderer *renderer = vulkan_get_renderer(wlr_renderer); + struct wlr_vk_render_buffer *render_buffer = + get_render_buffer(renderer, wlr_buffer); + if (render_buffer == NULL) { + return false; + } + + VkImageMemoryBarrier barrier = { + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .srcQueueFamilyIndex = renderer->dev->queue_family, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_FOREIGN_EXT, + .image = render_buffer->image, + .oldLayout = old_layout, + .newLayout = VK_IMAGE_LAYOUT_GENERAL, + .srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | + VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, + .dstAccessMask = 0, + .subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .subresourceRange.layerCount = 1, + .subresourceRange.levelCount = 1, + }; + + vkCmdPipelineBarrier(cb, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, + 0, 0, NULL, 0, NULL, 1, &barrier); + return true; +} + static bool buffer_export_sync_file(struct wlr_vk_renderer *renderer, struct wlr_buffer *buffer, uint32_t flags, int sync_file_fds[static WLR_DMABUF_MAX_PLANES]) { struct wlr_dmabuf_attributes dmabuf = {0}; diff --git a/3rdparty/wlroots/wlroots.syms b/3rdparty/wlroots/wlroots.syms index 7127121eeb..4b65b12a12 100644 --- a/3rdparty/wlroots/wlroots.syms +++ b/3rdparty/wlroots/wlroots.syms @@ -2,6 +2,7 @@ global: wlr_*; _wlr_*; + waylib_*; local: *; }; diff --git a/CMakeLists.txt b/CMakeLists.txt index 7baa2e7522..fe3a1d1a14 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -108,6 +108,7 @@ add_compile_definitions("QT_FORCE_ASSERTS") add_compile_definitions("TREELAND_PLUGINS_INSTALL_PATH=\"${TREELAND_FULL_PLUGINS_INSTALL_PATH}\"") add_compile_definitions("TREELAND_PLUGINS_OUTPUT_PATH=\"${TREELAND_PLUGINS_OUTPUT_PATH}\"") +add_compile_definitions("TREELAND_WALLPAPER_FACTORY_OUTPUT_PATH=\"${CMAKE_BINARY_DIR}/wallpaper-factory/treeland-wallpaper-factory\"") add_compile_definitions("TREELAND_COMPONENTS_TRANSLATION_DIR=\"${TREELAND_FULL_COMPONENTS_TRANSLATION_DIR}\"") diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6224549162..c1d4615e0d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -253,6 +253,7 @@ qt_add_qml_module(libtreeland core/qml/Animations/LaunchpadAnimation.qml core/qml/Animations/LayerShellAnimation.qml core/qml/Effects/Blur.qml + core/qml/Effects/+vulkan/Blur.qml core/qml/Effects/GlassEffect.qml core/qml/Effects/LaunchpadCover.qml core/qml/TaskSwitcher.qml @@ -307,6 +308,16 @@ qt_add_shaders(libtreeland "treeland_liquidglass_shaders" ${PROJECT_RESOURCES_DIR}/shaders/liquidglass.frag ) +set_source_files_properties(core/qml/overridable/InWindowBlur.qml + PROPERTIES + QT_RESOURCE_ALIAS "+treeland/InWindowBlur.qml" +) +qt_add_resources(libtreeland "override_dtkdeclarative_qml" + PREFIX "/dtk/declarative/qml/overridable" + FILES + core/qml/overridable/InWindowBlur.qml +) + qt_add_resources(libtreeland "treeland_assets" PREFIX "/dsg/icons" BASE ${PROJECT_RESOURCES_DIR}/icons @@ -402,6 +413,7 @@ target_include_directories(${BIN_NAME} PRIVATE target_link_libraries(${BIN_NAME} PRIVATE libtreeland Qt6::GuiPrivate + Qt6::QuickControls2 ) install(TARGETS ${BIN_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}") diff --git a/src/core/qml/Effects/+vulkan/Blur.qml b/src/core/qml/Effects/+vulkan/Blur.qml new file mode 100644 index 0000000000..acd34851da --- /dev/null +++ b/src/core/qml/Effects/+vulkan/Blur.qml @@ -0,0 +1,46 @@ +// Copyright (C) 2026 UnionTech Software Technology Co., Ltd. +// SPDX-License-Identifier: Apache-2.0 OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +import QtQuick +import Treeland + +Rectangle { + id: root + + color: Qt.rgba(0.0, 0.0, 0.0, 0.0) + + property bool radiusEnabled: radius > 0 + property int blurMax: Helper.config.blurStrength + property bool blurEnabled: blurMax > 0 && blurAmount > 0 + property real blurAmount: Helper.config.blurAmount + property real multiplier: Helper.config.blurMultiplier + property real brightness: 0.0 + property real contrast: 0.0 + property real saturation: Helper.config.glassSaturation + property real glassSpecular: Helper.config.glassSpecular + property real glassTint: 0.0 + property bool glassEnabled: Helper.config.glassEnabled + property Item effectContent: effectContentItem + property bool contentOffscreen: false + property bool effectEnabled: true + readonly property Item content: contentContainer + property alias offscreen: root.contentOffscreen + default property alias data: contentContainer.data + + Item { + id: contentContainer + anchors.fill: parent + } + + Rectangle { + id: effectContentItem + anchors.fill: parent + radius: root.radius + color: Qt.rgba(1.0, 1.0, 1.0, 0.15) + visible: root.effectEnabled && !root.contentOffscreen + z: -1 + } + + z: parent.z ? parent.z - 1 : -1 + anchors.fill: parent +} diff --git a/src/core/qml/Effects/Blur.qml b/src/core/qml/Effects/Blur.qml index a716ce7b27..0b7f369563 100644 --- a/src/core/qml/Effects/Blur.qml +++ b/src/core/qml/Effects/Blur.qml @@ -23,6 +23,9 @@ RenderBufferBlitter { property real glassSpecular: Helper.config.glassSpecular property real glassTint: 0.0 property bool glassEnabled: Helper.config.glassEnabled + property Item effectContent: contentLoader + property bool contentOffscreen: false + property bool effectEnabled: true z: parent.z ? parent.z - 1 : -1 anchors.fill: parent @@ -31,8 +34,10 @@ RenderBufferBlitter { // the active branch is instantiated. Toggling the DConfig key unloads one // Component and loads the other. Loader { + id: contentLoader anchors.fill: parent sourceComponent: blitter.glassEnabled ? glassComponent : blurComponent + visible: blitter.effectEnabled && !blitter.contentOffscreen } Component { diff --git a/src/core/qml/overridable/InWindowBlur.qml b/src/core/qml/overridable/InWindowBlur.qml new file mode 100644 index 0000000000..69f36671cd --- /dev/null +++ b/src/core/qml/overridable/InWindowBlur.qml @@ -0,0 +1,22 @@ +// Copyright (C) 2026 UnionTech Software Technology Co., Ltd. +// SPDX-License-Identifier: Apache-2.0 OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +import QtQuick +import Treeland + +Item { + id: control + + property alias offscreen: blur.contentOffscreen + property alias radius: blur.blurMax + property alias multiplier: blur.multiplier + property alias content: blur.effectContent + default property alias data: blur.data + property alias valid: blur.effectEnabled + + Blur { + id: blur + anchors.fill: parent + glassEnabled: false + } +} diff --git a/src/core/qmlengine.cpp b/src/core/qmlengine.cpp index 7a15cca9f0..37bc16b0db 100644 --- a/src/core/qmlengine.cpp +++ b/src/core/qmlengine.cpp @@ -13,10 +13,28 @@ #include #include +#include #include +#include + +namespace { + +QObject *installQmlFileSelector(QQmlEngine *engine) +{ + auto selector = new QQmlFileSelector(engine); + const auto api = QQuickWindow::graphicsApi(); + selector->setExtraSelectors({ + QStringLiteral("treeland"), + api == QSGRendererInterface::Vulkan ? QStringLiteral("vulkan") : QStringLiteral("gles"), + }); + return selector; +} + +} // namespace QmlEngine::QmlEngine(QObject *parent) : QQmlApplicationEngine(parent) + , dtkInWindowBlurFileSelector(installQmlFileSelector(this)) , titleBarComponent(this, "Treeland", "TitleBar") , decorationComponent(this, "Treeland", "Decoration") , windowMenuComponent(this, "Treeland", "WindowMenu") diff --git a/src/core/qmlengine.h b/src/core/qmlengine.h index a5937e646a..b6ae16b601 100644 --- a/src/core/qmlengine.h +++ b/src/core/qmlengine.h @@ -84,6 +84,7 @@ class QmlEngine : public QQmlApplicationEngine } private: + QObject *const dtkInWindowBlurFileSelector; QQmlComponent titleBarComponent; QQmlComponent decorationComponent; QQmlComponent windowMenuComponent; diff --git a/src/main.cpp b/src/main.cpp index 9d39099785..64662f458c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #if QT_VERSION >= QT_VERSION_CHECK(6, 10, 0) # include @@ -52,7 +53,7 @@ int main(int argc, char *argv[]) WServer::initializeQPA({}, [](const QString &) { return static_cast(new QDeepinTheme()); }); - // QQuickStyle::setStyle("Material"); + QQuickStyle::setStyle("Chameleon"); QGuiApplication::setAttribute(Qt::AA_UseOpenGLES); QGuiApplication::setHighDpiScaleFactorRoundingPolicy( diff --git a/src/plugins/lockscreen/qml/SessionList.qml b/src/plugins/lockscreen/qml/SessionList.qml index 18c58949dd..4efc871463 100644 --- a/src/plugins/lockscreen/qml/SessionList.qml +++ b/src/plugins/lockscreen/qml/SessionList.qml @@ -13,9 +13,7 @@ Popup { modal: true width: 220 height: 280 - background: RoundBlur { - radius: 12 - } + objectName: "LoginScreenSessionListPopup" ListView { id: list diff --git a/src/plugins/lockscreen/qml/UserList.qml b/src/plugins/lockscreen/qml/UserList.qml index c7c6b1eb0f..1daccdd894 100644 --- a/src/plugins/lockscreen/qml/UserList.qml +++ b/src/plugins/lockscreen/qml/UserList.qml @@ -7,7 +7,7 @@ import LockScreen import org.deepin.dtk 1.0 as D import QtQuick.Controls -D.Popup { +Popup { id: users property var count: UserModel.count @@ -18,16 +18,13 @@ D.Popup { signal otherUserRequested() + objectName: "LoginScreenUserListPopup" focus: true padding: 6 modal: true width: 232 height: useScrollBar ? maxListHeight : listv.contentHeight + padding * 2 - background: D.FloatingPanel { - radius: 12 - } - function selectCurrentUser(userName, index) { UserModel.currentUserName = userName users.lastCheckedIndex = index diff --git a/src/wallpaper/wallpaperlauncher.cpp b/src/wallpaper/wallpaperlauncher.cpp index 54dfa6c137..ff4a886661 100644 --- a/src/wallpaper/wallpaperlauncher.cpp +++ b/src/wallpaper/wallpaperlauncher.cpp @@ -27,6 +27,14 @@ WallpaperLauncher::~WallpaperLauncher() m_launcherThread->deleteLater(); } } +QString WallpaperLauncher::wallpaperProgram() +{ +#ifdef QT_DEBUG + return QStringLiteral(TREELAND_WALLPAPER_FACTORY_OUTPUT_PATH); +#else + return QStringLiteral("treeland-wallpaper-factory"); +#endif +} void WallpaperLauncher::setDisplayName(const QString &displayName) { @@ -68,7 +76,7 @@ void WallpaperLauncher::onStartRequested() m_crashCount = 0; m_wallpaperProcess = new QProcess(this); - m_wallpaperProcess->setProgram(QStringLiteral("treeland-wallpaper-factory")); + m_wallpaperProcess->setProgram(wallpaperProgram()); m_wallpaperProcess->setProcessChannelMode(QProcess::MergedChannels); QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); env.insert("WAYLAND_DISPLAY", m_socket->fullServerName()); diff --git a/src/wallpaper/wallpaperlauncher.h b/src/wallpaper/wallpaperlauncher.h index 08dd1c9d64..062c904257 100644 --- a/src/wallpaper/wallpaperlauncher.h +++ b/src/wallpaper/wallpaperlauncher.h @@ -17,6 +17,7 @@ class WallpaperLauncher : public QObject public: explicit WallpaperLauncher(QPointer socket); ~WallpaperLauncher() override; + static QString wallpaperProgram(); void setDisplayName(const QString &displayName); void start(); diff --git a/waylib/src/server/kernel/wglobal.h b/waylib/src/server/kernel/wglobal.h index 946cb4262b..48dfbb6cd4 100644 --- a/waylib/src/server/kernel/wglobal.h +++ b/waylib/src/server/kernel/wglobal.h @@ -1,4 +1,4 @@ -// Copyright (C) 2023 JiDe Zhang . +// Copyright (C) 2023-2026 UnionTech Software Technology Co., Ltd. // SPDX-License-Identifier: Apache-2.0 OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #pragma once @@ -192,6 +192,13 @@ class WAYLIB_SERVER_EXPORT WGlobal { QML_UNCREATABLE("Use for enums") public: + enum class ColorContentsMode { + DontCare, + Clear, + Preserve, + }; + Q_ENUM(ColorContentsMode) + enum class CursorShape { Default = Qt::CustomCursor + 1, Invalid, diff --git a/waylib/src/server/qtquick/private/wbufferrenderer.cpp b/waylib/src/server/qtquick/private/wbufferrenderer.cpp index f12f4bf072..7d4dd151c0 100644 --- a/waylib/src/server/qtquick/private/wbufferrenderer.cpp +++ b/waylib/src/server/qtquick/private/wbufferrenderer.cpp @@ -1,4 +1,4 @@ -// Copyright (C) 2023-2026 JiDe Zhang . +// Copyright (C) 2023-2026 UnionTech Software Technology Co., Ltd. // SPDX-License-Identifier: Apache-2.0 OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "wbufferrenderer_p.h" @@ -267,6 +267,11 @@ QRhiTexture *WBufferRenderer::currentRenderTarget() const return colorAttachment->texture(); } +bool WBufferRenderer::isColorPreserved() const +{ + return state.renderTarget.colorPreserved(); +} + const qw_damage_ring *WBufferRenderer::damageRing() const { return &m_damageRing; @@ -331,7 +336,8 @@ QTransform WBufferRenderer::inputMapToOutput(const QRectF &sourceRect, const QRe } qw_buffer *WBufferRenderer::beginRender(const QSize &pixelSize, qreal devicePixelRatio, - uint32_t format, RenderFlags flags) + uint32_t format, RenderFlags flags, + WGlobal::ColorContentsMode mode) { Q_ASSERT(!state.buffer); Q_ASSERT(m_output); @@ -378,8 +384,7 @@ qw_buffer *WBufferRenderer::beginRender(const QSize &pixelSize, qreal devicePixe auto wd = QQuickWindowPrivate::get(window()); Q_ASSERT(wd->renderControl); - auto lastRT = m_renderHelper->lastRenderTarget(); - auto rt = m_renderHelper->acquireRenderTarget(wd->renderControl, buffer); + auto rt = m_renderHelper->acquireRenderTarget(wd->renderControl, buffer, mode); if (rt.isNull()) { buffer->unlock(); return nullptr; @@ -390,7 +395,8 @@ qw_buffer *WBufferRenderer::beginRender(const QSize &pixelSize, qreal devicePixe m_damageRing.rotate_buffer(wbuffer, damage); state.dirty = WTools::fromPixmanRegion(damage); - auto rtd = QQuickRenderTargetPrivate::get(&rt); + auto rtValue = rt.rt(); + auto rtd = QQuickRenderTargetPrivate::get(&rtValue); QSGRenderTarget sgRT; if (rtd->type == QQuickRenderTargetPrivate::Type::PaintDevice) { @@ -420,6 +426,7 @@ qw_buffer *WBufferRenderer::beginRender(const QSize &pixelSize, qreal devicePixe } state.flags = flags; + state.colorContentsMode = mode; state.context = wd->context; state.pixelSize = pixelSize; state.devicePixelRatio = devicePixelRatio; @@ -436,8 +443,7 @@ inline static QRect scaleToRect(const QRectF &s, qreal scale) { } void WBufferRenderer::render(int sourceIndex, const QMatrix4x4 &renderMatrix, - const QRectF &sourceRect, const QRectF &targetRect, - bool preserveColorContents) + const QRectF &sourceRect, const QRectF &targetRect) { Q_ASSERT(state.buffer); @@ -475,18 +481,18 @@ void WBufferRenderer::render(int sourceIndex, const QMatrix4x4 &renderMatrix, const auto viewportRect = scaleToRect(targetRect, devicePixelRatio); auto softwareRenderer = dynamic_cast(renderer); + const bool isVulkanRhi = wd->rhi && wd->rhi->backend() == QRhi::Vulkan; { // before render if (softwareRenderer) { // Avoid do clear before paint, for the software renderer this // work is expensive. - if (m_clearColor.alpha() == 0) - preserveColorContents = true; + const bool clearColor = !state.renderTarget.colorPreserved(); #if QT_VERSION >= QT_VERSION_CHECK(6, 9, 0) - softwareRenderer->setClearColorEnabled(!preserveColorContents); + softwareRenderer->setClearColorEnabled(clearColor); #else auto bn = softwareRenderer->renderableNode(W_PRIVATE_MEMBER(*softwareRenderer, QSGAbsSoftRenderer_m_background_tag{})); if (bn) { - W_PRIVATE_MEMBER(*bn, QSGSoftRenderableNode_m_opacity_tag{}) = preserveColorContents ? 0 : 1; + W_PRIVATE_MEMBER(*bn, QSGSoftRenderableNode_m_opacity_tag{}) = clearColor ? 1 : 0; } #endif if (!state.dirty.isEmpty()) { @@ -501,7 +507,7 @@ void WBufferRenderer::render(int sourceIndex, const QMatrix4x4 &renderMatrix, if (!mapTransform.isIdentity()) state.worldTransform = mapTransform * state.worldTransform; state.worldTransform.optimize(); - auto image = getImageFrom(state.renderTarget); + auto image = getImageFrom(state.renderTarget.rt()); image->setDevicePixelRatio(devicePixelRatio); // TODO: Should set to QSGSoftwareRenderer, but it's not support specify matrix. @@ -523,7 +529,7 @@ void WBufferRenderer::render(int sourceIndex, const QMatrix4x4 &renderMatrix, state.worldTransform.optimize(); bool flipY = wd->rhi ? !wd->rhi->isYUpInNDC() : false; - if (state.renderTarget.mirrorVertically()) + if (state.renderTarget.rt().mirrorVertically()) flipY = !flipY; if (viewportRect.isValid()) { @@ -564,16 +570,18 @@ void WBufferRenderer::render(int sourceIndex, const QMatrix4x4 &renderMatrix, renderer->setProjectionMatrix(projectionMatrix); renderer->setProjectionMatrixWithNativeNDC(projectionMatrixWithNativeNDC); - auto textureRT = static_cast(state.sgRenderTarget.rt); - if (preserveColorContents) { - textureRT->setFlags(textureRT->flags() | QRhiTextureRenderTarget::PreserveColorContents); - } else { - textureRT->setFlags(textureRT->flags() & ~QRhiTextureRenderTarget::PreserveColorContents); - } } } +#ifdef ENABLE_VULKAN_RENDER + if (m_renderHelper) + m_renderHelper->prepareVulkanRenderTarget(state.sgRenderTarget.cb, state.renderTarget); +#endif state.context->renderNextFrame(renderer); +#ifdef ENABLE_VULKAN_RENDER + if (m_renderHelper) + m_renderHelper->finishVulkanRenderTarget(state.sgRenderTarget.cb, state.renderTarget); +#endif { // after render if (!softwareRenderer) { @@ -587,11 +595,12 @@ void WBufferRenderer::render(int sourceIndex, const QMatrix4x4 &renderMatrix, // sourceIndex, we should let the RHI (Rendering Hardware Interface) // complete the results of this drawing here to ensure the current // drawing result is available for use. - wd->rhi->finish(); + if (!isVulkanRhi) + wd->rhi->finish(); } else { state.dirty = softwareRenderer->flushRegion(); - auto currentImage = getImageFrom(state.renderTarget); + auto currentImage = getImageFrom(state.renderTarget.rt()); Q_ASSERT(currentImage && currentImage == softwareRenderer->renderTarget().paintDevice); currentImage->setDevicePixelRatio(1.0); const auto scaleTF = QTransform::fromScale(devicePixelRatio, devicePixelRatio); diff --git a/waylib/src/server/qtquick/private/wbufferrenderer_p.h b/waylib/src/server/qtquick/private/wbufferrenderer_p.h index 628684c714..94b035f77f 100644 --- a/waylib/src/server/qtquick/private/wbufferrenderer_p.h +++ b/waylib/src/server/qtquick/private/wbufferrenderer_p.h @@ -1,10 +1,11 @@ -// Copyright (C) 2023-2026 JiDe Zhang . +// Copyright (C) 2023-2026 UnionTech Software Technology Co., Ltd. // SPDX-License-Identifier: Apache-2.0 OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #pragma once #include #include +#include #include #include @@ -32,7 +33,6 @@ struct pixman_region32; struct wlr_swapchain; WAYLIB_SERVER_BEGIN_NAMESPACE -class WRenderHelper; class WSGTextureProvider; class WAYLIB_SERVER_EXPORT WBufferRenderer : public QQuickItem { @@ -76,6 +76,7 @@ class WAYLIB_SERVER_EXPORT WBufferRenderer : public QQuickItem QW_NAMESPACE::qw_buffer *currentBuffer() const; QW_NAMESPACE::qw_buffer *lastBuffer() const; QRhiTexture *currentRenderTarget() const; + bool isColorPreserved() const; const QW_NAMESPACE::qw_damage_ring *damageRing() const; QW_NAMESPACE::qw_damage_ring *damageRing(); @@ -95,10 +96,10 @@ class WAYLIB_SERVER_EXPORT WBufferRenderer : public QQuickItem protected: QW_NAMESPACE::qw_buffer *beginRender(const QSize &pixelSize, qreal devicePixelRatio, - uint32_t format, RenderFlags flags = {}); + uint32_t format, RenderFlags flags = {}, + WGlobal::ColorContentsMode mode = WGlobal::ColorContentsMode::DontCare); void render(int sourceIndex, const QMatrix4x4 &renderMatrix, - const QRectF &sourceRect = {}, const QRectF &targetRect = {}, - bool preserveColorContents = false); + const QRectF &sourceRect = {}, const QRectF &targetRect = {}); void endRender(); void componentComplete() override; @@ -133,6 +134,7 @@ class WAYLIB_SERVER_EXPORT WBufferRenderer : public QQuickItem struct RenderState { RenderFlags flags; + WGlobal::ColorContentsMode colorContentsMode = WGlobal::ColorContentsMode::DontCare; QSGRenderContext *context; QSGRenderer *renderer; QSGBatchRenderer::Renderer *batchRenderer; @@ -140,7 +142,7 @@ class WAYLIB_SERVER_EXPORT WBufferRenderer : public QQuickItem QSize pixelSize; qreal devicePixelRatio; std::unique_ptr buffer; - QQuickRenderTarget renderTarget; + WRenderHelper::RenderTarget renderTarget; QSGRenderTarget sgRenderTarget; QRegion dirty; } state; diff --git a/waylib/src/server/qtquick/private/woutputviewport_p.h b/waylib/src/server/qtquick/private/woutputviewport_p.h index 9cd0cc1368..2b9b04c654 100644 --- a/waylib/src/server/qtquick/private/woutputviewport_p.h +++ b/waylib/src/server/qtquick/private/woutputviewport_p.h @@ -1,4 +1,4 @@ -// Copyright (C) 2023 JiDe Zhang . +// Copyright (C) 2023-2026 UnionTech Software Technology Co., Ltd. // SPDX-License-Identifier: Apache-2.0 OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #pragma once @@ -23,7 +23,7 @@ class Q_DECL_HIDDEN WOutputViewportPrivate : public QQuickItemPrivate WOutputViewportPrivate() : attached(false) , offscreen(false) - , preserveColorContents(false) + , colorContentsMode(WGlobal::ColorContentsMode::DontCare) , live(true) , forceRender(false) , ignoreViewport(false) @@ -84,7 +84,7 @@ class Q_DECL_HIDDEN WOutputViewportPrivate : public QQuickItemPrivate uint attached:1; uint offscreen:1; - uint preserveColorContents:1; + WGlobal::ColorContentsMode colorContentsMode; uint live:1; uint forceRender:1; uint ignoreViewport:1; diff --git a/waylib/src/server/qtquick/woutputhelper.cpp b/waylib/src/server/qtquick/woutputhelper.cpp index a71a58f396..c3e629c1be 100644 --- a/waylib/src/server/qtquick/woutputhelper.cpp +++ b/waylib/src/server/qtquick/woutputhelper.cpp @@ -1,4 +1,4 @@ -// Copyright (C) 2023 JiDe Zhang . +// Copyright (C) 2023-2026 UnionTech Software Technology Co., Ltd. // SPDX-License-Identifier: Apache-2.0 OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "woutputhelper.h" @@ -138,8 +138,8 @@ QWindow *WOutputHelper::outputWindow() const return d->outputWindow; } -std::pair WOutputHelper::acquireRenderTarget(QQuickRenderControl *rc, - wlr_swapchain **swapchain) +WRenderHelper::RenderTarget WOutputHelper::acquireRenderTarget(QQuickRenderControl *rc, + wlr_swapchain **swapchain) { W_D(WOutputHelper); @@ -157,14 +157,14 @@ std::pair WOutputHelper::acquireRenderTarget(QQ return {}; } - return {buffer, rt}; + return rt; } -std::pair WOutputHelper::lastRenderTarget() +WRenderHelper::RenderTarget WOutputHelper::lastRenderTarget() { W_DC(WOutputHelper); if (!d->renderHelper) - return {nullptr, {}}; + return {}; return d->renderHelper->lastRenderTarget(); } diff --git a/waylib/src/server/qtquick/woutputhelper.h b/waylib/src/server/qtquick/woutputhelper.h index 3a4d3fb250..c9024dd967 100644 --- a/waylib/src/server/qtquick/woutputhelper.h +++ b/waylib/src/server/qtquick/woutputhelper.h @@ -1,10 +1,11 @@ -// Copyright (C) 2023 JiDe Zhang . +// Copyright (C) 2023-2026 UnionTech Software Technology Co., Ltd. // SPDX-License-Identifier: Apache-2.0 OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #pragma once #include #include +#include #include #include @@ -44,9 +45,9 @@ class WAYLIB_SERVER_EXPORT WOutputHelper : public QObject, public WObject WOutput *output() const; QWindow *outputWindow() const; - std::pair acquireRenderTarget(QQuickRenderControl *rc, - wlr_swapchain **swapchain = nullptr); - std::pair lastRenderTarget(); + WRenderHelper::RenderTarget acquireRenderTarget(QQuickRenderControl *rc, + wlr_swapchain **swapchain = nullptr); + WRenderHelper::RenderTarget lastRenderTarget(); void setBuffer(QW_NAMESPACE::qw_buffer *buffer); QW_NAMESPACE::qw_buffer *buffer() const; diff --git a/waylib/src/server/qtquick/woutputlayer.cpp b/waylib/src/server/qtquick/woutputlayer.cpp index 3de7a165f5..e925cc075e 100644 --- a/waylib/src/server/qtquick/woutputlayer.cpp +++ b/waylib/src/server/qtquick/woutputlayer.cpp @@ -1,4 +1,4 @@ -// Copyright (C) 2023 JiDe Zhang . +// Copyright (C) 2023-2026 UnionTech Software Technology Co., Ltd. // SPDX-License-Identifier: Apache-2.0 OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "woutputlayer.h" @@ -41,6 +41,7 @@ class Q_DECL_HIDDEN WOutputLayerPrivate : public QObjectPrivate uint actualEnabled:1; uint refItem:1; WOutputLayer::Flags flags = {0}; + WGlobal::ColorContentsMode colorContentsMode = WGlobal::ColorContentsMode::DontCare; int z = 0; QPointF cursorHotSpot; QList outputs; @@ -177,6 +178,21 @@ void WOutputLayer::setFlags(const Flags &newFlags) Q_EMIT flagsChanged(); } +WGlobal::ColorContentsMode WOutputLayer::colorContentsMode() const +{ + W_DC(WOutputLayer); + return d->colorContentsMode; +} + +void WOutputLayer::setColorContentsMode(WGlobal::ColorContentsMode mode) +{ + W_D(WOutputLayer); + if (d->colorContentsMode == mode) + return; + d->colorContentsMode = mode; + Q_EMIT colorContentsModeChanged(); +} + const QList &WOutputLayer::outputs() const { W_DC(WOutputLayer); diff --git a/waylib/src/server/qtquick/woutputlayer.h b/waylib/src/server/qtquick/woutputlayer.h index fc61164489..9857389707 100644 --- a/waylib/src/server/qtquick/woutputlayer.h +++ b/waylib/src/server/qtquick/woutputlayer.h @@ -1,4 +1,4 @@ -// Copyright (C) 2023 JiDe Zhang . +// Copyright (C) 2023-2026 UnionTech Software Technology Co., Ltd. // SPDX-License-Identifier: Apache-2.0 OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #pragma once @@ -23,6 +23,7 @@ class WAYLIB_SERVER_EXPORT WOutputLayer : public QObject Q_PROPERTY(bool keepLayer READ keepLayer WRITE setKeepLayer NOTIFY keepLayerChanged FINAL) Q_PROPERTY(bool force READ force WRITE setForce NOTIFY forceChanged FINAL) Q_PROPERTY(Flags flags READ flags WRITE setFlags NOTIFY flagsChanged FINAL) + Q_PROPERTY(WGlobal::ColorContentsMode colorContentsMode READ colorContentsMode WRITE setColorContentsMode NOTIFY colorContentsModeChanged FINAL) Q_PROPERTY(QList outputs READ outputs WRITE setOutputs NOTIFY outputsChanged FINAL) Q_PROPERTY(QList inOutputsByHardware READ inOutputsByHardware NOTIFY inOutputsByHardwareChanged FINAL) Q_PROPERTY(int z READ z WRITE setZ NOTIFY zChanged FINAL) @@ -35,7 +36,6 @@ class WAYLIB_SERVER_EXPORT WOutputLayer : public QObject enum Flag { SizeSensitive = 1 << 0, DontClip = 1 << 1, - PreserveColorContents = 1 << 2, NoAlpha = 1 << 3, Cursor = 1 << 4, }; @@ -54,6 +54,9 @@ class WAYLIB_SERVER_EXPORT WOutputLayer : public QObject Flags flags() const; void setFlags(const Flags &newFlags); + WGlobal::ColorContentsMode colorContentsMode() const; + void setColorContentsMode(WGlobal::ColorContentsMode mode); + const QList &outputs() const; void setOutputs(const QList &newOutputList); @@ -74,6 +77,7 @@ class WAYLIB_SERVER_EXPORT WOutputLayer : public QObject Q_SIGNALS: void enabledChanged(); void flagsChanged(); + void colorContentsModeChanged(); void outputsChanged(); void inOutputsByHardwareChanged(); void zChanged(); diff --git a/waylib/src/server/qtquick/woutputrenderwindow.cpp b/waylib/src/server/qtquick/woutputrenderwindow.cpp index 454167897d..7921c403f8 100644 --- a/waylib/src/server/qtquick/woutputrenderwindow.cpp +++ b/waylib/src/server/qtquick/woutputrenderwindow.cpp @@ -238,9 +238,10 @@ class Q_DECL_HIDDEN OutputHelper : public WOutputHelper inline qw_buffer *beginRender(WBufferRenderer *renderer, const QSize &pixelSize, uint32_t format, - WBufferRenderer::RenderFlags flags); + WBufferRenderer::RenderFlags flags, + WGlobal::ColorContentsMode mode = WGlobal::ColorContentsMode::DontCare); inline void render(WBufferRenderer *renderer, int sourceIndex, const QMatrix4x4 &renderMatrix, - const QRectF &sourceRect, const QRectF &viewportRect, bool preserveColorContents); + const QRectF &sourceRect, const QRectF &viewportRect); static bool visualizeLayers() { static bool on = qEnvironmentVariableIsSet("WAYLIB_VISUALIZE_LAYERS"); @@ -249,7 +250,7 @@ class Q_DECL_HIDDEN OutputHelper : public WOutputHelper qw_buffer *renderLayer(LayerData *layer, bool *dontEndRenderAndReturnNeedsEndRender); WBufferRenderer *afterRender(); - WBufferRenderer *compositeLayers(const QVector layers, bool forceShadowRenderer); + WBufferRenderer *compositeLayers(const QList layers, bool forceShadowRenderer); bool commit(WBufferRenderer *buffer); bool tryToHardwareCursor(const LayerData *layer); @@ -590,16 +591,17 @@ void OutputHelper::cleanCursorRender() qw_buffer *OutputHelper::beginRender(WBufferRenderer *renderer, const QSize &pixelSize, uint32_t format, - WBufferRenderer::RenderFlags flags) + WBufferRenderer::RenderFlags flags, + WGlobal::ColorContentsMode mode) { - return renderer->beginRender(pixelSize, devicePixelRatio(), format, flags); + return renderer->beginRender(pixelSize, devicePixelRatio(), format, flags, mode); } void OutputHelper::render(WBufferRenderer *renderer, int sourceIndex, const QMatrix4x4 &renderMatrix, - const QRectF &sourceRect, const QRectF &targetRect, bool preserveColorContents) + const QRectF &sourceRect, const QRectF &targetRect) { renderWindowD()->pushRenderer(renderer); - renderer->render(sourceIndex, renderMatrix, sourceRect, targetRect, preserveColorContents); + renderer->render(sourceIndex, renderMatrix, sourceRect, targetRect); } static QQuickItem *createVisualRectangle(QQuickItem *target, const QColor &color) { @@ -753,20 +755,28 @@ qw_buffer *OutputHelper::renderLayer(LayerData *layer, bool *dontEndRenderAndRet layer->renderer->setSize(layer->pixelSize / dpr); const bool alpha = !layer->layer->layer->flags().testFlag(WOutputLayer::NoAlpha); + auto mode = layer->layer->layer->colorContentsMode(); + if (visualizeLayers()) { + if (mode == WGlobal::ColorContentsMode::Clear) { + qCWarning(lcWlRenderer) << "Layer" << layer->layer->layer + << "requires Clear but visualizeLayers forces Preserve"; + } + mode = WGlobal::ColorContentsMode::Preserve; + } // Don't use OutputHelper::beginRender, because the dpr maybe is from LayerData::mapFrom buffer = layer->renderer->beginRender(layer->pixelSize, dpr, // TODO: Allows control format by WOutputLayer alpha ? DRM_FORMAT_ARGB8888 : DRM_FORMAT_XRGB8888, - WBufferRenderer::DontConfigureSwapchain); + WBufferRenderer::DontConfigureSwapchain, + mode); if (buffer) { const QRectF sr = QRectF(layer->mapRect.topLeft() - layer->noClipMapRect.topLeft(), layer->mapRect.size()); const QRectF tr(QPointF(0, 0), layer->mapRect.size()); - render(layer->renderer, 0, layer->renderMatrix, sr, tr, - layer->layer->layer->flags().testFlag(WOutputLayer::PreserveColorContents)); + render(layer->renderer, 0, layer->renderMatrix, sr, tr); if (visualizeLayers()) - render(layer->renderer, 1, layer->renderMatrix, sr, tr, true); + render(layer->renderer, 1, layer->renderMatrix, sr, tr); if (dontEndRenderAndReturnNeedsEndRender) { *dontEndRenderAndReturnNeedsEndRender = true; @@ -965,7 +975,8 @@ WBufferRenderer *OutputHelper::compositeLayers(const QList layers, b { Q_ASSERT(!layers.isEmpty()); - const bool usingShadowRenderer = forceShadowRenderer; + const bool usingShadowRenderer = forceShadowRenderer + || !bufferRenderer()->isColorPreserved(); if (!m_layerPorxyContainer) { m_layerPorxyContainer = new QQuickItem(renderWindow()->contentItem()); @@ -1039,19 +1050,20 @@ WBufferRenderer *OutputHelper::compositeLayers(const QList layers, b if (usingShadowRenderer) { const bool ok = beginRender(bufferRenderer2(), m_output->output()->size(), qwoutput()->handle()->render_format, - WBufferRenderer::RedirectOpenGLContextDefaultFrameBufferObject); + WBufferRenderer::RedirectOpenGLContextDefaultFrameBufferObject, + WGlobal::ColorContentsMode::Preserve); if (ok) { // stop primary render if (bufferRenderer()->currentBuffer()) bufferRenderer()->endRender(); - render(bufferRenderer2(), 0, {}, m_output->effectiveSourceRect(), m_output->targetRect(), true); + render(bufferRenderer2(), 0, {}, m_output->effectiveSourceRect(), m_output->targetRect()); return bufferRenderer2(); } } else { if (bufferRenderer()->currentBuffer()) { - render(bufferRenderer(), 1, {}, m_output->effectiveSourceRect(), m_output->targetRect(), true); + render(bufferRenderer(), 1, {}, m_output->effectiveSourceRect(), m_output->targetRect()); } else { // ###(zccrs): Maybe because contents is not dirty, so not do render // in WOutputRenderWindowPrivate::doRenderOutputs, force mark the @@ -1485,13 +1497,13 @@ WOutputRenderWindowPrivate::doRenderOutputs(qw_output *needsFrameOutput, const Q updateDirtyNodes(); qw_buffer *buffer = helper->beginRender(helper->bufferRenderer(), helper->output()->output()->size(), format, - WBufferRenderer::RedirectOpenGLContextDefaultFrameBufferObject); + WBufferRenderer::RedirectOpenGLContextDefaultFrameBufferObject, + helper->output()->colorContentsMode()); Q_ASSERT(buffer == helper->bufferRenderer()->currentBuffer()); if (buffer) { helper->render(helper->bufferRenderer(), 0, renderMatrix, helper->output()->effectiveSourceRect(), - helper->output()->targetRect(), - helper->output()->preserveColorContents()); + helper->output()->targetRect()); } renderResults.append(helper); } @@ -1739,6 +1751,8 @@ void WOutputRenderWindow::attach(WOutputLayer *layer, WOutputViewport *output) connect(layer, &WOutputLayer::flagsChanged, outputHelper, &WOutputHelper::scheduleFrame); + connect(layer, &WOutputLayer::colorContentsModeChanged, + outputHelper, &WOutputHelper::scheduleFrame); connect(layer, &WOutputLayer::zChanged, outputHelper, &WOutputHelper::scheduleFrame); diff --git a/waylib/src/server/qtquick/woutputviewport.cpp b/waylib/src/server/qtquick/woutputviewport.cpp index da6e4c321f..849df7a520 100644 --- a/waylib/src/server/qtquick/woutputviewport.cpp +++ b/waylib/src/server/qtquick/woutputviewport.cpp @@ -1,4 +1,4 @@ -// Copyright (C) 2023 JiDe Zhang . +// Copyright (C) 2023-2026 UnionTech Software Technology Co., Ltd. // SPDX-License-Identifier: Apache-2.0 OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "woutputviewport.h" @@ -269,19 +269,19 @@ void WOutputViewport::setCacheBuffer(bool newCacheBuffer) d->bufferRenderer->setCacheBuffer(newCacheBuffer); } -bool WOutputViewport::preserveColorContents() const +WGlobal::ColorContentsMode WOutputViewport::colorContentsMode() const { W_DC(WOutputViewport); - return d->preserveColorContents; + return d->colorContentsMode; } -void WOutputViewport::setPreserveColorContents(bool newPreserveColorContents) +void WOutputViewport::setColorContentsMode(WGlobal::ColorContentsMode mode) { W_D(WOutputViewport); - if (d->preserveColorContents == newPreserveColorContents) + if (d->colorContentsMode == mode) return; - d->preserveColorContents = newPreserveColorContents; - Q_EMIT preserveColorContentsChanged(); + d->colorContentsMode = mode; + Q_EMIT colorContentsModeChanged(); } bool WOutputViewport::live() const diff --git a/waylib/src/server/qtquick/woutputviewport.h b/waylib/src/server/qtquick/woutputviewport.h index cc3589ef37..987911d79b 100644 --- a/waylib/src/server/qtquick/woutputviewport.h +++ b/waylib/src/server/qtquick/woutputviewport.h @@ -1,4 +1,4 @@ -// Copyright (C) 2023 JiDe Zhang . +// Copyright (C) 2023-2026 UnionTech Software Technology Co., Ltd. // SPDX-License-Identifier: Apache-2.0 OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #pragma once @@ -25,7 +25,7 @@ class WAYLIB_SERVER_EXPORT WOutputViewport : public QQuickItem, public virtual W Q_PROPERTY(qreal devicePixelRatio READ devicePixelRatio WRITE setDevicePixelRatio NOTIFY devicePixelRatioChanged) Q_PROPERTY(bool offscreen READ offscreen WRITE setOffscreen NOTIFY offscreenChanged) Q_PROPERTY(bool cacheBuffer READ cacheBuffer WRITE setCacheBuffer NOTIFY cacheBufferChanged FINAL) - Q_PROPERTY(bool preserveColorContents READ preserveColorContents WRITE setPreserveColorContents NOTIFY preserveColorContentsChanged FINAL) + Q_PROPERTY(WGlobal::ColorContentsMode colorContentsMode READ colorContentsMode WRITE setColorContentsMode NOTIFY colorContentsModeChanged FINAL) Q_PROPERTY(bool live READ live WRITE setLive NOTIFY liveChanged FINAL) Q_PROPERTY(QRectF sourceRect READ sourceRect WRITE setSourceRect RESET resetSourceRect NOTIFY sourceRectChanged FINAL) Q_PROPERTY(QRectF targetRect READ targetRect WRITE setTargetRect RESET resetTargetRect NOTIFY targetRectChanged FINAL) @@ -67,8 +67,8 @@ class WAYLIB_SERVER_EXPORT WOutputViewport : public QQuickItem, public virtual W bool cacheBuffer() const; void setCacheBuffer(bool newCacheBuffer); - bool preserveColorContents() const; - void setPreserveColorContents(bool newPreserveColorContents); + WGlobal::ColorContentsMode colorContentsMode() const; + void setColorContentsMode(WGlobal::ColorContentsMode mode); bool live() const; void setLive(bool newLive); @@ -112,7 +112,7 @@ public Q_SLOTS: void devicePixelRatioChanged(); void offscreenChanged(); void cacheBufferChanged(); - void preserveColorContentsChanged(); + void colorContentsModeChanged(); void outputRenderInitialized(); void inputChanged(); void liveChanged(); diff --git a/waylib/src/server/qtquick/wrenderhelper.cpp b/waylib/src/server/qtquick/wrenderhelper.cpp index 0f00631593..b52f574b2b 100644 --- a/waylib/src/server/qtquick/wrenderhelper.cpp +++ b/waylib/src/server/qtquick/wrenderhelper.cpp @@ -1,4 +1,4 @@ -// Copyright (C) 2023 JiDe Zhang . +// Copyright (C) 2023-2026 UnionTech Software Technology Co., Ltd. // SPDX-License-Identifier: Apache-2.0 OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "wrenderhelper.h" @@ -6,6 +6,7 @@ #include "wayliblogging.h" #include "private/wqmlhelper_p.h" #include "private/wglobal_p.h" +#include #include #include @@ -27,6 +28,9 @@ #include #include #include +#ifdef ENABLE_VULKAN_RENDER +#include +#endif extern "C" { #define static @@ -52,9 +56,7 @@ struct Q_DECL_HIDDEN RhiRenderEntry { Q_GLOBAL_STATIC(QVector, s_rhiRenderBuffers) struct Q_DECL_HIDDEN BufferData { - BufferData() { - - } + BufferData() = default; ~BufferData() { resetWindowRenderTarget(); @@ -65,6 +67,7 @@ struct Q_DECL_HIDDEN BufferData { WImageRenderTarget paintDevice; QQuickRenderTarget renderTarget; QQuickWindowRenderTarget windowRenderTarget; + bool colorPreserved = false; inline void resetWindowRenderTarget() { #if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0) @@ -131,12 +134,103 @@ struct Q_DECL_HIDDEN BufferData { } }; +class WRenderHelper::RenderTarget::Private { +public: + std::weak_ptr data; +}; + +WRenderHelper::RenderTarget::RenderTarget() : d(new Private) {} +WRenderHelper::RenderTarget::RenderTarget(const RenderTarget &other) + : d(other.d ? new Private(*other.d) : nullptr) {} +WRenderHelper::RenderTarget &WRenderHelper::RenderTarget::operator=(const RenderTarget &other) +{ + if (this != &other) { + delete d; + d = other.d ? new Private(*other.d) : nullptr; + } + return *this; +} +WRenderHelper::RenderTarget::~RenderTarget() { delete d; } + +bool WRenderHelper::RenderTarget::isNull() const +{ + return !d || d->data.expired(); +} + +QQuickRenderTarget WRenderHelper::RenderTarget::rt() const +{ + if (!d) + return {}; + auto data = d->data.lock(); + return data ? data->renderTarget : QQuickRenderTarget(); +} + +qw_buffer *WRenderHelper::RenderTarget::buffer() const +{ + if (!d) + return nullptr; + auto data = d->data.lock(); + return data ? data->buffer : nullptr; +} + +bool WRenderHelper::RenderTarget::colorPreserved() const +{ + if (!d) + return false; + auto data = d->data.lock(); + return data ? data->colorPreserved : false; +} + +static constexpr WGlobal::ColorContentsMode resolveColorContentsMode( + WGlobal::ColorContentsMode requested, bool softwareRenderer) noexcept +{ + if (requested != WGlobal::ColorContentsMode::DontCare) + return requested; + // Software clear is expensive; default to preserve. + return softwareRenderer ? WGlobal::ColorContentsMode::Preserve + : WGlobal::ColorContentsMode::Clear; +} + +static QRhiTextureRenderTarget::Flags rhiRenderTargetFlags(WGlobal::ColorContentsMode mode) +{ + Q_ASSERT(mode != WGlobal::ColorContentsMode::DontCare); + return mode == WGlobal::ColorContentsMode::Preserve + ? QRhiTextureRenderTarget::PreserveColorContents + : QRhiTextureRenderTarget::Flags{}; +} + +static bool recreateRhiRenderTarget(BufferData *data, QRhiTextureRenderTarget::Flags flags) +{ +#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0) + auto renderTarget = static_cast( + data->windowRenderTarget.rt.renderTarget); + auto &rpDesc = data->windowRenderTarget.res.rpDesc; +#else + auto renderTarget = static_cast( + data->windowRenderTarget.renderTarget); + auto &rpDesc = data->windowRenderTarget.rpDesc; +#endif + Q_ASSERT(renderTarget); + renderTarget->destroy(); + renderTarget->setFlags(flags); + + auto newRpDesc = renderTarget->newCompatibleRenderPassDescriptor(); + if (!newRpDesc) + return false; + delete rpDesc; + rpDesc = newRpDesc; + renderTarget->setRenderPassDescriptor(rpDesc); + return renderTarget->create(); +} + + // Copy from qquickrendertarget.cpp static bool createRhiRenderTarget(const QRhiColorAttachment &colorAttachment, const QSize &pixelSize, int sampleCount, QRhi *rhi, - QQuickWindowRenderTarget &dst) + QQuickWindowRenderTarget &dst, + QRhiTextureRenderTarget::Flags flags) { std::unique_ptr depthStencil(rhi->newRenderBuffer(QRhiRenderBuffer::DepthStencil, pixelSize, sampleCount)); if (!depthStencil->create()) { @@ -146,7 +240,7 @@ static bool createRhiRenderTarget(const QRhiColorAttachment &colorAttachment, QRhiTextureRenderTargetDescription rtDesc(colorAttachment); rtDesc.setDepthStencilBuffer(depthStencil.get()); - std::unique_ptr rt(rhi->newTextureRenderTarget(rtDesc)); + std::unique_ptr rt(rhi->newTextureRenderTarget(rtDesc, flags)); std::unique_ptr rp(rt->newCompatibleRenderPassDescriptor()); rt->setRenderPassDescriptor(rp.get()); @@ -170,7 +264,8 @@ static bool createRhiRenderTarget(const QRhiColorAttachment &colorAttachment, return true; } -bool createRhiRenderTarget(QRhi *rhi, const QQuickRenderTarget &source, QQuickWindowRenderTarget &dst) +bool createRhiRenderTarget(QRhi *rhi, const QQuickRenderTarget &source, QQuickWindowRenderTarget &dst, + QRhiTextureRenderTarget::Flags flags) { auto rtd = QQuickRenderTargetPrivate::get(&source); @@ -178,14 +273,14 @@ bool createRhiRenderTarget(QRhi *rhi, const QQuickRenderTarget &source, QQuickWi case QQuickRenderTargetPrivate::Type::NativeTexture: { const auto format = rtd->u.nativeTexture.rhiFormat == QRhiTexture::UnknownFormat ? QRhiTexture::RGBA8 : QRhiTexture::Format(rtd->u.nativeTexture.rhiFormat); - const auto flags = QRhiTexture::RenderTarget | QRhiTexture::Flags( + const auto textureFlags = QRhiTexture::RenderTarget | QRhiTexture::Flags( #if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0) rtd->u.nativeTexture.rhiFormatFlags #else rtd->u.nativeTexture.rhiFlags #endif ); - std::unique_ptr texture(rhi->newTexture(format, rtd->pixelSize, rtd->sampleCount, flags)); + std::unique_ptr texture(rhi->newTexture(format, rtd->pixelSize, rtd->sampleCount, textureFlags)); texture->setName(QByteArrayLiteral("WaylibTexture")); #if QT_VERSION < QT_VERSION_CHECK(6, 6, 0) if (!texture->createFrom({ rtd->u.nativeTexture.object, rtd->u.nativeTexture.layout })) @@ -194,7 +289,7 @@ bool createRhiRenderTarget(QRhi *rhi, const QQuickRenderTarget &source, QQuickWi #endif return false; QRhiColorAttachment att(texture.get()); - if (!createRhiRenderTarget(att, rtd->pixelSize, rtd->sampleCount, rhi, dst)) + if (!createRhiRenderTarget(att, rtd->pixelSize, rtd->sampleCount, rhi, dst, flags)) return false; #if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0) dst.res.texture = texture.release(); @@ -210,7 +305,7 @@ bool createRhiRenderTarget(QRhi *rhi, const QQuickRenderTarget &source, QQuickWi return false; } QRhiColorAttachment att(renderbuffer.get()); - if (!createRhiRenderTarget(att, rtd->pixelSize, rtd->sampleCount, rhi, dst)) + if (!createRhiRenderTarget(att, rtd->pixelSize, rtd->sampleCount, rhi, dst, flags)) return false; renderbuffer->setName(QByteArrayLiteral("WaylibRenderBuffer")); #if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0) @@ -242,21 +337,21 @@ class Q_DECL_HIDDEN WRenderHelperPrivate : public WObjectPrivate void resetRenderBuffer(); void onBufferDestroy(); - static bool ensureRhiRenderTarget(QQuickRenderControl *rc, BufferData *data); + static bool ensureRhiRenderTarget(QQuickRenderControl *rc, BufferData *data, + QRhiTextureRenderTarget::Flags flags); W_DECLARE_PUBLIC(WRenderHelper) qw_renderer *renderer; - QList buffers; - BufferData *lastBuffer = nullptr; + QList> buffers; + std::weak_ptr lastBuffer; QSize size; }; void WRenderHelperPrivate::resetRenderBuffer() { - qDeleteAll(buffers); - lastBuffer = nullptr; buffers.clear(); + lastBuffer.reset(); } void WRenderHelperPrivate::onBufferDestroy() @@ -266,15 +361,17 @@ void WRenderHelperPrivate::onBufferDestroy() for (int i = 0; i < buffers.count(); ++i) { auto data = buffers[i]; if (data->buffer == buffer) { - if (lastBuffer == data) - lastBuffer = nullptr; + auto locked = lastBuffer.lock(); + if (locked && locked == data) + lastBuffer.reset(); buffers.removeAt(i); break; } } } -bool WRenderHelperPrivate::ensureRhiRenderTarget(QQuickRenderControl *rc, BufferData *data) +bool WRenderHelperPrivate::ensureRhiRenderTarget(QQuickRenderControl *rc, BufferData *data, + QRhiTextureRenderTarget::Flags flags) { data->resetWindowRenderTarget(); #if QT_VERSION < QT_VERSION_CHECK(6, 6, 0) @@ -283,7 +380,7 @@ bool WRenderHelperPrivate::ensureRhiRenderTarget(QQuickRenderControl *rc, Buffer auto rhi = rc->rhi(); #endif auto tmp = data->renderTarget; - bool ok = createRhiRenderTarget(rhi, tmp, data->windowRenderTarget); + bool ok = createRhiRenderTarget(rhi, tmp, data->windowRenderTarget, flags); if (!ok) return false; #if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0) @@ -535,7 +632,8 @@ qw_buffer *WRenderHelper::toBuffer(qw_renderer *renderer, QSGTexture *texture, Q return nullptr; } -QQuickRenderTarget WRenderHelper::acquireRenderTarget(QQuickRenderControl *rc, qw_buffer *buffer) +WRenderHelper::RenderTarget WRenderHelper::acquireRenderTarget(QQuickRenderControl *rc, qw_buffer *buffer, + WGlobal::ColorContentsMode mode) { W_D(WRenderHelper); Q_ASSERT(buffer); @@ -543,21 +641,53 @@ QQuickRenderTarget WRenderHelper::acquireRenderTarget(QQuickRenderControl *rc, q if (d->size.isEmpty()) return {}; + const bool isSoftware = wlr_renderer_is_pixman(d->renderer->handle()); + const auto resolvedMode = resolveColorContentsMode(mode, isSoftware); + const bool needPreserve = resolvedMode == WGlobal::ColorContentsMode::Preserve; + const auto flags = rhiRenderTargetFlags(resolvedMode); + for (int i = 0; i < d->buffers.count(); ++i) { auto data = d->buffers[i]; if (data->buffer == buffer) { + if (needPreserve != data->colorPreserved) { +#ifdef ENABLE_VULKAN_RENDER + if (wlr_renderer_is_vk(d->renderer->handle())) { + qCWarning(lcWlRenderHelper) + << "Recreating Vulkan render target for buffer" << buffer + << "to change color preserved from" << data->colorPreserved + << "to" << needPreserve; + if (!recreateRhiRenderTarget(data.get(), flags)) + return {}; + } else +#endif + { +#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0) + auto renderTarget = data->windowRenderTarget.rt.renderTarget; +#else + auto renderTarget = data->windowRenderTarget.renderTarget; +#endif + if (renderTarget) + static_cast(renderTarget)->setFlags(flags); + } + } + data->colorPreserved = needPreserve; d->lastBuffer = data; - return data->renderTarget; + RenderTarget result; + result.d->data = data; + return result; } } std::unique_ptr bufferData(new BufferData); bufferData->buffer = buffer; - auto texture = qw_texture::from_buffer(*d->renderer, *buffer); + bufferData->colorPreserved = needPreserve; QQuickRenderTarget rt; - if (wlr_renderer_is_pixman(d->renderer->handle())) { + if (isSoftware) { + std::unique_ptr texture(qw_texture::from_buffer(*d->renderer, *buffer)); + if (!texture) + return {}; pixman_image_t *image = wlr_pixman_texture_get_image(texture->handle()); void *data = pixman_image_get_data(image); if (bufferData->paintDevice.constBits() != data) @@ -567,12 +697,23 @@ QQuickRenderTarget WRenderHelper::acquireRenderTarget(QQuickRenderControl *rc, q } #ifdef ENABLE_VULKAN_RENDER else if (wlr_renderer_is_vk(d->renderer->handle())) { - wlr_vk_image_attribs attribs; - wlr_vk_texture_get_image_attribs(texture->handle(), &attribs); - rt = QQuickRenderTarget::fromVulkanImage(attribs.image, attribs.layout, attribs.format, d->size); + // Reuse wlroots' wlr_vk_render_buffer (same VkImage as tinywl/scene), + // not a parallel dmabuf import owned by waylib. + wlr_vk_image_attribs attribs = {}; + if (!waylib_vk_renderer_get_render_buffer_attribs(d->renderer->handle(), + buffer->handle(), &attribs)) { + return {}; + } + rt = QQuickRenderTarget::fromVulkanImage(attribs.image, + attribs.layout, + attribs.format, + d->size); } #endif else if (wlr_renderer_is_gles2(d->renderer->handle())) { + std::unique_ptr texture(qw_texture::from_buffer(*d->renderer, *buffer)); + if (!texture) + return {}; wlr_gles2_texture_attribs attribs; wlr_gles2_texture_get_attribs(texture->handle(), &attribs); @@ -580,13 +721,12 @@ QQuickRenderTarget WRenderHelper::acquireRenderTarget(QQuickRenderControl *rc, q rt.setMirrorVertically(true); } - delete texture; bufferData->renderTarget = rt; if (QSGRendererInterface::isApiRhiBased(getGraphicsApi(rc))) { if (!rt.isNull()) { // Force convert to Rhi render target - if (!d->ensureRhiRenderTarget(rc, bufferData.get())) + if (!d->ensureRhiRenderTarget(rc, bufferData.get(), flags)) bufferData->renderTarget = {}; } @@ -602,20 +742,106 @@ QQuickRenderTarget WRenderHelper::acquireRenderTarget(QQuickRenderControl *rc, q connect(buffer, SIGNAL(before_destroy()), this, SLOT(onBufferDestroy()), Qt::UniqueConnection); - d->buffers.append(bufferData.release()); + d->buffers.append(std::shared_ptr(bufferData.release())); d->lastBuffer = d->buffers.last(); - return d->buffers.last()->renderTarget; + RenderTarget result; + result.d->data = d->buffers.last(); + return result; } -std::pair WRenderHelper::lastRenderTarget() const +WRenderHelper::RenderTarget WRenderHelper::lastRenderTarget() const { W_DC(WRenderHelper); - if (!d->lastBuffer) - return {nullptr, {}}; + auto data = d->lastBuffer.lock(); + if (!data) + return {}; + + RenderTarget result; + result.d->data = data; + return result; +} + +#ifdef ENABLE_VULKAN_RENDER +void WRenderHelper::prepareVulkanRenderTarget(QRhiCommandBuffer *cb, const RenderTarget &rt) +{ + W_D(WRenderHelper); + if (!rt.d || !cb) + return; + auto data = rt.d->data.lock(); + if (!data || !data->buffer || !d->renderer || !wlr_renderer_is_vk(d->renderer->handle())) + return; + + cb->beginExternal(); + auto handles = static_cast(cb->nativeHandles()); + if (!handles || handles->commandBuffer == VK_NULL_HANDLE) { + cb->endExternal(); + qCWarning(lcWlRenderHelper, "Vulkan render buffer acquire: missing native command buffer"); + return; + } + + // FOREIGN_EXT -> graphics queue, same as wlroots pass.c / PR #1171. + if (!waylib_vk_renderer_record_render_buffer_acquire(d->renderer->handle(), + data->buffer->handle(), + handles->commandBuffer)) { + qCWarning(lcWlRenderHelper) << "Vulkan render buffer acquire failed for" << data->buffer; + } + cb->endExternal(); +} + +void WRenderHelper::finishVulkanRenderTarget(QRhiCommandBuffer *cb, const RenderTarget &rt) +{ + W_D(WRenderHelper); + if (!rt.d || !cb) + return; + auto data = rt.d->data.lock(); + if (!data || !data->buffer || !d->renderer || !wlr_renderer_is_vk(d->renderer->handle())) + return; + +#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0) + QRhiTexture *qtTexture = data->windowRenderTarget.res.texture; +#else + QRhiTexture *qtTexture = data->windowRenderTarget.texture; +#endif + if (!qtTexture) { + qCWarning(lcWlRenderHelper, "Vulkan render buffer release: missing Qt QRhiTexture"); + return; + } + + const auto native = qtTexture->nativeTexture(); +#if QT_VERSION < QT_VERSION_CHECK(6, 6, 0) + const auto oldLayout = VkImageLayout(native.layout); +#else + const auto oldLayout = VkImageLayout(native.layout); +#endif + if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED) { + qCWarning(lcWlRenderHelper, "Vulkan render buffer release: Qt texture layout is UNDEFINED"); + return; + } + + cb->beginExternal(); + auto handles = static_cast(cb->nativeHandles()); + if (!handles || handles->commandBuffer == VK_NULL_HANDLE) { + cb->endExternal(); + qCWarning(lcWlRenderHelper, "Vulkan render buffer release: missing native command buffer"); + return; + } + + // graphics queue -> FOREIGN_EXT + GENERAL for KMS, using Qt's real old layout. + if (!waylib_vk_renderer_record_render_buffer_release(d->renderer->handle(), + data->buffer->handle(), + handles->commandBuffer, + oldLayout)) { + qCWarning(lcWlRenderHelper) << "Vulkan render buffer release failed for" << data->buffer; + cb->endExternal(); + return; + } + cb->endExternal(); - return {d->lastBuffer->buffer, d->lastBuffer->renderTarget}; + // Keep QRhi's external-image tracker in sync with the post-release layout. + qtTexture->setNativeLayout(VK_IMAGE_LAYOUT_GENERAL); } +#endif // ENABLE_VULKAN_RENDER static qw_renderer *createRendererWithType(const char *type, qw_backend *backend) { diff --git a/waylib/src/server/qtquick/wrenderhelper.h b/waylib/src/server/qtquick/wrenderhelper.h index 789edb5ca4..3c39beb159 100644 --- a/waylib/src/server/qtquick/wrenderhelper.h +++ b/waylib/src/server/qtquick/wrenderhelper.h @@ -1,4 +1,4 @@ -// Copyright (C) 2023 JiDe Zhang . +// Copyright (C) 2023-2026 UnionTech Software Technology Co., Ltd. // SPDX-License-Identifier: Apache-2.0 OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #pragma once @@ -15,6 +15,7 @@ class QQuickRenderControl; class QSGTexture; class QSGPlainTexture; class QRhi; +class QRhiCommandBuffer; QT_END_NAMESPACE QW_BEGIN_NAMESPACE @@ -47,8 +48,35 @@ class WAYLIB_SERVER_EXPORT WRenderHelper : public QObject, public WObject static QW_NAMESPACE::qw_buffer *toBuffer(QW_NAMESPACE::qw_renderer *renderer, QSGTexture *texture, QSGRendererInterface::GraphicsApi api); - QQuickRenderTarget acquireRenderTarget(QQuickRenderControl *rc, QW_NAMESPACE::qw_buffer *buffer); - std::pair lastRenderTarget() const; + // Opaque handle to an internal BufferData. Becomes null when the buffer + // is destroyed (similar to QPointer). + class WAYLIB_SERVER_EXPORT RenderTarget { + public: + RenderTarget(); + RenderTarget(const RenderTarget &); + RenderTarget &operator=(const RenderTarget &); + ~RenderTarget(); + + bool isNull() const; + QQuickRenderTarget rt() const; + QW_NAMESPACE::qw_buffer *buffer() const; + bool colorPreserved() const; + + private: + friend class WRenderHelper; + class Private; + Private *d = nullptr; + }; + + RenderTarget acquireRenderTarget(QQuickRenderControl *rc, QW_NAMESPACE::qw_buffer *buffer, + WGlobal::ColorContentsMode mode = WGlobal::ColorContentsMode::DontCare); + RenderTarget lastRenderTarget() const; + +#ifdef ENABLE_VULKAN_RENDER + // Record wlroots render_buffer FOREIGN acquire/release around Qt RHI pass. + void prepareVulkanRenderTarget(QRhiCommandBuffer *cb, const RenderTarget &rt); + void finishVulkanRenderTarget(QRhiCommandBuffer *cb, const RenderTarget &rt); +#endif static QW_NAMESPACE::qw_renderer *createRenderer(QW_NAMESPACE::qw_backend *backend); static QW_NAMESPACE::qw_renderer *createRenderer(QW_NAMESPACE::qw_backend *backend, QSGRendererInterface::GraphicsApi api); diff --git a/waylib/src/server/qtquick/wsgtextureprovider.cpp b/waylib/src/server/qtquick/wsgtextureprovider.cpp index 362c841602..c69c2155d9 100644 --- a/waylib/src/server/qtquick/wsgtextureprovider.cpp +++ b/waylib/src/server/qtquick/wsgtextureprovider.cpp @@ -14,6 +14,12 @@ #include #include +extern "C" { +#ifdef ENABLE_VULKAN_RENDER +#include +#endif +} + WAYLIB_SERVER_BEGIN_NAMESPACE class Q_DECL_HIDDEN WSGTextureProviderPrivate : public WObjectPrivate @@ -26,8 +32,6 @@ class Q_DECL_HIDDEN WSGTextureProviderPrivate : public WObjectPrivate qtTexture.setOwnsTexture(false); qtTexture.setFiltering(smooth ? QSGTexture::Linear : QSGTexture::Nearest); - qtTexture.setMipmapFiltering(smooth ? QSGTexture::Linear - : QSGTexture::Nearest); } ~WSGTextureProviderPrivate() { @@ -35,7 +39,12 @@ class Q_DECL_HIDDEN WSGTextureProviderPrivate : public WObjectPrivate } void cleanTexture() { - if (rhiTexture) { + auto *oldRhiTexture = qtTexture.rhiTexture(); + if (oldRhiTexture) { + // QSGPlainTexture does not own the QRhiTexture in this provider. + qtTexture.setOwnsTexture(false); + qtTexture.setTexture(nullptr); + Q_ASSERT(window); class TextureCleanupJob : public QRunnable { @@ -49,9 +58,8 @@ class Q_DECL_HIDDEN WSGTextureProviderPrivate : public WObjectPrivate }; // Delay clean the qt rhi textures. - window->scheduleRenderJob(new TextureCleanupJob(rhiTexture), + window->scheduleRenderJob(new TextureCleanupJob(oldRhiTexture), QQuickWindow::AfterSynchronizingStage); - rhiTexture = nullptr; } if (ownsTexture && texture) @@ -59,6 +67,21 @@ class Q_DECL_HIDDEN WSGTextureProviderPrivate : public WObjectPrivate texture = nullptr; } + void flushVulkanStageIfNeeded() { +#ifdef ENABLE_VULKAN_RENDER + if (!window || !window->renderer()) + return; + auto *renderer = window->renderer()->handle(); + if (!wlr_renderer_is_vk(renderer)) + return; + // SHM uploads are recorded on wlroots' stage CB and only submitted by + // a wlroots render pass. Flush before Qt samples the image. + if (!waylib_vk_renderer_flush_stage(renderer)) { + qCWarning(lcWlQtQuickTexture) << "Failed to flush Vulkan stage CB for SHM upload"; + } +#endif + } + void updateRhiTexture() { Q_ASSERT(texture); bool ok = WRenderHelper::makeTexture(window->rhi(), texture, &qtTexture); @@ -69,7 +92,7 @@ class Q_DECL_HIDDEN WSGTextureProviderPrivate : public WObjectPrivate return; } - rhiTexture = qtTexture.rhiTexture(); + flushVulkanStageIfNeeded(); } W_DECLARE_PUBLIC(WSGTextureProvider) @@ -83,7 +106,6 @@ class Q_DECL_HIDDEN WSGTextureProviderPrivate : public WObjectPrivate // qt resources QSGPlainTexture qtTexture; - QRhiTexture *rhiTexture = nullptr; bool smooth = true; }; @@ -101,15 +123,17 @@ WOutputRenderWindow *WSGTextureProvider::window() const void WSGTextureProvider::setBuffer(qw_buffer *buffer) { - if (buffer == qwBuffer()) { + W_D(WSGTextureProvider); + if (buffer == d->buffer) { // The buffer object is not changed, but maybe the buffer's content is changed. // So should emit textureChanged() signal too. - if (buffer) + if (buffer && d->texture) { + d->flushVulkanStageIfNeeded(); Q_EMIT textureChanged(); + } return; } - W_D(WSGTextureProvider); d->cleanTexture(); d->buffer = buffer; @@ -193,8 +217,6 @@ void WSGTextureProvider::setSmooth(bool newSmooth) d->smooth = newSmooth; d->qtTexture.setFiltering(newSmooth ? QSGTexture::Linear : QSGTexture::Nearest); - d->qtTexture.setMipmapFiltering(newSmooth ? QSGTexture::Linear - : QSGTexture::Nearest); Q_EMIT smoothChanged(); }