From 6deeade1d498ea0c515caa8f57816e3f77c191d0 Mon Sep 17 00:00:00 2001 From: JiDe Zhang Date: Wed, 29 Jul 2026 11:48:09 +0800 Subject: [PATCH 1/3] fix(waylib): fix Vulkan scanout RT via wlroots render_buffer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Reuse wlroots wlr_vk_render_buffer for Qt Quick RHI. 2. Record FOREIGN_EXT ownership transfer around Qt rendering. 3. Preserve SHM partial updates and avoid QRhi::finish on Vulkan. Log: Fix Vulkan scanout and SHM upload stability Influence: 1. Run Vulkan output rendering with SHM and DMA-BUF clients. 2. Verify repeated frames and buffer reuse do not crash. 3. Verify GLES and software paths keep their existing flush behavior. Acknowledgements: Thanks to GitHub user @LFRon for identifying the Qt RHI/wlroots Vulkan integration issue and suggesting in PR #1132 that Treeland reuse wlroots APIs, which helped guide this implementation. fix(waylib): 修复 Vulkan 扫描输出渲染目标 1. 复用 wlroots 的 wlr_vk_render_buffer 供 Qt Quick RHI 使用。 2. 在 Qt 绘制前后记录 FOREIGN_EXT 所有权转移。 3. 保留 SHM 局部更新,并避免 Vulkan 调用 QRhi::finish。 Log: 修复 Vulkan 扫描输出与 SHM 上传稳定性 Influence: 1. 验证 Vulkan 下 SHM 和 DMA-BUF 客户端输出。 2. 验证多帧绘制和缓冲复用不再触发崩溃。 3. 验证 GLES 与软件渲染路径保持原有刷新行为。 致谢: 感谢 GitHub 用户 @LFRon 发现 Qt RHI 与 wlroots Vulkan 集成问题, 并在 PR #1132 中建议 Treeland 直接复用 wlroots 暴露的 API, 为本次实现提供了重要参考。 --- 3rdparty/wlroots/include/wlr/render/vulkan.h | 18 +- 3rdparty/wlroots/render/vulkan/renderer.c | 140 ++++++++ 3rdparty/wlroots/wlroots.syms | 1 + waylib/src/server/kernel/wglobal.h | 9 +- .../qtquick/private/wbufferrenderer.cpp | 51 +-- .../qtquick/private/wbufferrenderer_p.h | 14 +- .../qtquick/private/woutputviewport_p.h | 6 +- waylib/src/server/qtquick/woutputhelper.cpp | 12 +- waylib/src/server/qtquick/woutputhelper.h | 9 +- waylib/src/server/qtquick/woutputlayer.cpp | 18 +- waylib/src/server/qtquick/woutputlayer.h | 8 +- .../server/qtquick/woutputrenderwindow.cpp | 48 ++- waylib/src/server/qtquick/woutputviewport.cpp | 14 +- waylib/src/server/qtquick/woutputviewport.h | 10 +- waylib/src/server/qtquick/wrenderhelper.cpp | 301 ++++++++++++++++-- waylib/src/server/qtquick/wrenderhelper.h | 34 +- .../src/server/qtquick/wsgtextureprovider.cpp | 46 ++- 17 files changed, 615 insertions(+), 124 deletions(-) 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..b0e52e73fa 100644 --- a/3rdparty/wlroots/render/vulkan/renderer.c +++ b/3rdparty/wlroots/render/vulkan/renderer.c @@ -413,6 +413,15 @@ 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) { + assert(wlr_renderer_is_vk(wlr_renderer)); + 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 +956,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/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..6ce4dc204c 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"); @@ -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..f726ffb9ee 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,19 +56,22 @@ struct Q_DECL_HIDDEN RhiRenderEntry { Q_GLOBAL_STATIC(QVector, s_rhiRenderBuffers) struct Q_DECL_HIDDEN BufferData { - BufferData() { - - } + BufferData() = default; ~BufferData() { resetWindowRenderTarget(); } qw_buffer *buffer = nullptr; +#ifdef ENABLE_VULKAN_RENDER + // Non-owning; used to talk to wlroots' wlr_vk_render_buffer for this buffer. + wlr_renderer *renderer = nullptr; +#endif // for software renderer WImageRenderTarget paintDevice; QQuickRenderTarget renderTarget; QQuickWindowRenderTarget windowRenderTarget; + bool colorPreserved = false; inline void resetWindowRenderTarget() { #if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0) @@ -131,12 +138,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 +244,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 +268,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 +277,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 +293,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 +309,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 +341,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 +365,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 +384,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 +636,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 +645,56 @@ 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 (data->renderer && wlr_renderer_is_vk(data->renderer)) { + 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; +#ifdef ENABLE_VULKAN_RENDER + bufferData->renderer = d->renderer->handle(); +#endif 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 +704,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 +728,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 +749,104 @@ 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) +{ + if (!rt.d || !cb) + return; + auto data = rt.d->data.lock(); + if (!data || !data->buffer || !data->renderer || !wlr_renderer_is_vk(data->renderer)) + 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(data->renderer, + 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) +{ + if (!rt.d || !cb) + return; + auto data = rt.d->data.lock(); + if (!data || !data->buffer || !data->renderer || !wlr_renderer_is_vk(data->renderer)) + 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(data->renderer, + 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(); } From cb5baa8abdbefa00628d2076bd31717f4dac7aca Mon Sep 17 00:00:00 2001 From: JiDe Zhang Date: Tue, 4 Aug 2026 13:30:27 +0800 Subject: [PATCH 2/3] fix(lockscreen): add Vulkan-specific Blur placeholder MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Use a white placeholder Rectangle for Blur in Vulkan mode. 2. Preserve InWindowBlur content and offscreen semantics. 3. Stabilize lockscreen user switching popup lifecycle and visibility. 4. Keep the popup regression test independent of QtQuick.Window. Log: Restore lockscreen blur and user popup rendering Influence: 1. Verify the white Blur placeholder on the Vulkan backend. 2. Verify InWindowBlur offscreen and valid transitions. 3. Run DTK override tests with software, OpenGL, and Vulkan backends. fix(lockscreen): 增加 Vulkan 专用 Blur 占位处理 1. Vulkan 模式下使用白色 Rectangle 作为 Blur 占位实现。 2. 保持 InWindowBlur 内容和 offscreen 语义兼容。 3. 稳定锁屏切换用户弹窗的生命周期和显示行为。 4. 保持弹窗回归测试不依赖 QtQuick.Window QML 模块。 Log: 恢复锁屏模糊与用户弹窗渲染行为 Influence: 1. 验证 Vulkan 后端下的白色 Blur 占位实现。 2. 验证 InWindowBlur 的 offscreen 和 valid 状态切换。 3. 使用软件、OpenGL 和 Vulkan 后端运行 DTK 覆盖测试。 --- 3rdparty/wlroots/render/vulkan/renderer.c | 6 ++- src/CMakeLists.txt | 12 +++++ src/core/qml/Effects/+vulkan/Blur.qml | 46 +++++++++++++++++++ src/core/qml/Effects/Blur.qml | 5 ++ src/core/qml/overridable/InWindowBlur.qml | 22 +++++++++ src/core/qmlengine.cpp | 18 ++++++++ src/core/qmlengine.h | 1 + src/main.cpp | 3 +- src/plugins/lockscreen/qml/SessionList.qml | 4 +- src/plugins/lockscreen/qml/UserList.qml | 7 +-- .../server/qtquick/woutputrenderwindow.cpp | 2 +- waylib/src/server/qtquick/wrenderhelper.cpp | 19 +++----- 12 files changed, 121 insertions(+), 24 deletions(-) create mode 100644 src/core/qml/Effects/+vulkan/Blur.qml create mode 100644 src/core/qml/overridable/InWindowBlur.qml diff --git a/3rdparty/wlroots/render/vulkan/renderer.c b/3rdparty/wlroots/render/vulkan/renderer.c index b0e52e73fa..802642f3c8 100644 --- a/3rdparty/wlroots/render/vulkan/renderer.c +++ b/3rdparty/wlroots/render/vulkan/renderer.c @@ -414,7 +414,9 @@ bool vulkan_submit_stage_wait(struct wlr_vk_renderer *renderer) { } bool waylib_vk_renderer_flush_stage(struct wlr_renderer *wlr_renderer) { - assert(wlr_renderer_is_vk(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; @@ -984,7 +986,7 @@ bool waylib_vk_renderer_get_render_buffer_attribs(struct wlr_renderer *wlr_rende struct wlr_dmabuf_attributes dmabuf = {0}; if (!wlr_buffer_get_dmabuf(wlr_buffer, &dmabuf)) { - wlr_log(WLR_ERROR, "wlr_buffer_get_dmabuf failed"); + wlr_log(WLR_ERROR, "wlr_buffer_get_dmabuf() failed"); return false; } 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/waylib/src/server/qtquick/woutputrenderwindow.cpp b/waylib/src/server/qtquick/woutputrenderwindow.cpp index 6ce4dc204c..7921c403f8 100644 --- a/waylib/src/server/qtquick/woutputrenderwindow.cpp +++ b/waylib/src/server/qtquick/woutputrenderwindow.cpp @@ -250,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); diff --git a/waylib/src/server/qtquick/wrenderhelper.cpp b/waylib/src/server/qtquick/wrenderhelper.cpp index f726ffb9ee..b52f574b2b 100644 --- a/waylib/src/server/qtquick/wrenderhelper.cpp +++ b/waylib/src/server/qtquick/wrenderhelper.cpp @@ -63,10 +63,6 @@ struct Q_DECL_HIDDEN BufferData { } qw_buffer *buffer = nullptr; -#ifdef ENABLE_VULKAN_RENDER - // Non-owning; used to talk to wlroots' wlr_vk_render_buffer for this buffer. - wlr_renderer *renderer = nullptr; -#endif // for software renderer WImageRenderTarget paintDevice; QQuickRenderTarget renderTarget; @@ -655,7 +651,7 @@ WRenderHelper::RenderTarget WRenderHelper::acquireRenderTarget(QQuickRenderContr if (data->buffer == buffer) { if (needPreserve != data->colorPreserved) { #ifdef ENABLE_VULKAN_RENDER - if (data->renderer && wlr_renderer_is_vk(data->renderer)) { + if (wlr_renderer_is_vk(d->renderer->handle())) { qCWarning(lcWlRenderHelper) << "Recreating Vulkan render target for buffer" << buffer << "to change color preserved from" << data->colorPreserved @@ -685,9 +681,6 @@ WRenderHelper::RenderTarget WRenderHelper::acquireRenderTarget(QQuickRenderContr std::unique_ptr bufferData(new BufferData); bufferData->buffer = buffer; bufferData->colorPreserved = needPreserve; -#ifdef ENABLE_VULKAN_RENDER - bufferData->renderer = d->renderer->handle(); -#endif QQuickRenderTarget rt; @@ -772,10 +765,11 @@ WRenderHelper::RenderTarget WRenderHelper::lastRenderTarget() const #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 || !data->renderer || !wlr_renderer_is_vk(data->renderer)) + if (!data || !data->buffer || !d->renderer || !wlr_renderer_is_vk(d->renderer->handle())) return; cb->beginExternal(); @@ -787,7 +781,7 @@ void WRenderHelper::prepareVulkanRenderTarget(QRhiCommandBuffer *cb, const Rende } // FOREIGN_EXT -> graphics queue, same as wlroots pass.c / PR #1171. - if (!waylib_vk_renderer_record_render_buffer_acquire(data->renderer, + 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; @@ -797,10 +791,11 @@ void WRenderHelper::prepareVulkanRenderTarget(QRhiCommandBuffer *cb, const Rende 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 || !data->renderer || !wlr_renderer_is_vk(data->renderer)) + if (!data || !data->buffer || !d->renderer || !wlr_renderer_is_vk(d->renderer->handle())) return; #if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0) @@ -833,7 +828,7 @@ void WRenderHelper::finishVulkanRenderTarget(QRhiCommandBuffer *cb, const Render } // graphics queue -> FOREIGN_EXT + GENERAL for KMS, using Qt's real old layout. - if (!waylib_vk_renderer_record_render_buffer_release(data->renderer, + if (!waylib_vk_renderer_record_render_buffer_release(d->renderer->handle(), data->buffer->handle(), handles->commandBuffer, oldLayout)) { From 077469e57ec804307634008f3b3dd39dc87e5b0f Mon Sep 17 00:00:00 2001 From: JiDe Zhang Date: Thu, 6 Aug 2026 11:33:30 +0800 Subject: [PATCH 3/3] fix(wallpaper): use build factory in Debug MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Launch the build-tree wallpaper factory in Qt Debug builds. 2. Keep Release builds on the installed factory executable. 3. Avoid requiring a separately installed Debug factory binary. Log: Debug wallpaper sessions now use the build-tree factory. Influence: 1. Verify Debug Treeland starts the build-tree factory. 2. Verify wallpaper updates work in Debug sessions. 3. Verify Release still starts installed treeland-wallpaper-factory. fix(wallpaper):Debug 使用构建目录中的 factory 1. Qt Debug 构建启动构建目录中的 wallpaper factory。 2. Release 构建继续使用系统安装的 factory。 3. 避免 Debug 环境依赖额外安装的 factory。 Log: Debug wallpaper 会话现在使用构建目录中的 factory。 Influence: 1. 验证 Debug Treeland 启动构建目录中的 factory。 2. 验证 Debug 会话中的 wallpaper 更新正常。 3. 验证 Release 仍启动系统安装的 treeland-wallpaper-factory。 --- CMakeLists.txt | 1 + src/wallpaper/wallpaperlauncher.cpp | 10 +++++++++- src/wallpaper/wallpaperlauncher.h | 1 + 3 files changed, 11 insertions(+), 1 deletion(-) 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/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();