Skip to content

fix: upload shm textures via Qt in vulkan mode - #1131

Closed
Groveer wants to merge 1 commit into
linuxdeepin:masterfrom
Groveer:fix/vulkan-shm-qt-upload
Closed

fix: upload shm textures via Qt in vulkan mode#1131
Groveer wants to merge 1 commit into
linuxdeepin:masterfrom
Groveer:fix/vulkan-shm-qt-upload

Conversation

@Groveer

@Groveer Groveer commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Description

In vulkan rendering mode, shm client window textures currently borrow wlroots' device-local VkImage via qw_texture::from_buffer / makeTextureupdateVKTexture. That VkImage shares the VkDevice with Qt but runs on a separate command stream without cross-stream synchronization (shm buffers have no dma-buf sync fences), so Qt may sample the image before wlroots finishes uploading, conflict with wlroots' layout transitions, or reference a texture wlroots has already reclaimed.

This PR makes shm buffers in vulkan mode bypass wlroots' texture path and instead be uploaded by Qt itself, closing the sync gap. dmabuf client textures are unaffected (dma-buf sync provides cross-stream synchronization) and keep their existing makeTextureupdateVKTexture path.

Changes

  • waylib/src/server/qtquick/wrenderhelper.h / wrenderhelper.cpp — New WRenderHelper::makeTextureFromShm: reads shm pixels via qw_buffer::begin_data_ptr_access, converts DRM format to QImage format via WTools::toImageFormat, deep-copies the pixels before end_data_ptr_access, and sets the image on a Qt-owned QSGPlainTexture.
  • waylib/src/server/qtquick/wsgtextureprovider.cppsetBuffer intercepts shm buffers under vulkan mode (guarded by ENABLE_VULKAN_RENDER + get_shm check) before the wlroots texture path; content-update (buffer == qwBuffer()) re-reads shm pixels; shmTexture lifecycle managed with deferred cleanup matching the existing rhiTexture pattern; texture(), setSmooth(), invalidate(), cleanTexture() all handle the shm path.

Testing

  • shm client windows (XWayland, software-rendered apps) under vulkan rendering mode
  • dmabuf client windows under vulkan mode (should be unaffected)
  • content updates on the same shm buffer
  • texture lifecycle: switching shm buffers, destroying surfaces
  • gles2/software rendering paths (no regression)

Multica Issue WM-78

Summary by Sourcery

Handle shm client window textures in Vulkan rendering mode via Qt-owned textures uploaded from shared memory instead of borrowing wlroots VkImages, improving synchronization and lifecycle management while keeping dmabuf paths unchanged.

New Features:

  • Introduce WRenderHelper::makeTextureFromShm to create QSGPlainTexture instances from shm buffer pixel data.
  • Add a dedicated shm texture path in WSGTextureProvider that uploads shm buffers via Qt when using the Vulkan renderer.

Bug Fixes:

  • Prevent synchronization issues in Vulkan mode where Qt sampled wlroots-managed VkImages for shm buffers without proper cross-stream synchronization, risking stale or partially uploaded content.

Enhancements:

  • Align texture cleanup and ownership handling for shm-backed textures with existing deferred QRhi texture cleanup in WSGTextureProvider.
  • Ensure texture smoothing and filtering settings are consistently applied to both standard and shm-backed textures in WSGTextureProvider.

1. Add WRenderHelper::makeTextureFromShm to read shm pixels via
   begin_data_ptr_access and upload to a Qt-owned QSGPlainTexture
2. Intercept shm buffers in WSGTextureProvider::setBuffer under vulkan
   mode so Qt uploads textures itself instead of borrowing wlroots'
   device-local VkImage which lacks cross-stream sync
3. Deep-copy shm pixels before end_data_ptr_access since QSGPlainTexture
   uploads lazily during the render cycle
4. Manage shmTexture lifecycle with deferred cleanup matching the
   existing rhiTexture pattern; dmabuf path is unchanged

Log: Fixed shm client window rendering glitches in vulkan mode by
letting Qt upload shm textures directly instead of borrowing wlroots'
vulkan texture

Influence:
1. Test shm client windows (XWayland, software-rendered apps) under
   vulkan rendering mode for correct display
2. Verify dmabuf client windows are unaffected under vulkan mode
3. Test content updates on the same shm buffer to ensure texture
   refreshes correctly
4. Verify no texture leaks when switching shm buffers or destroying
   surfaces
5. Test gles2/software rendering to confirm no regression

fix: vulkan 模式下 shm 纹理改由 Qt 上传

1. 新增 WRenderHelper::makeTextureFromShm,通过 begin_data_ptr_access
   读取 shm 像素并上传到 Qt 自有的 QSGPlainTexture
2. 在 WSGTextureProvider::setBuffer 中于 vulkan 模式下拦截 shm buffer,
   由 Qt 自行上传纹理而非借用 wlroots 的 device-local VkImage(缺少
   跨 command stream 同步)
3. 在 end_data_ptr_access 之前深拷贝 shm 像素,因 QSGPlainTexture 在
   渲染周期惰性上传
4. shmTexture 生命周期管理与现有 rhiTexture 延迟清理模式一致;dmabuf
   路径保持不变

Log: 修复 vulkan 模式下 shm 客户端窗口渲染异常,由 Qt 直接上传
shm 纹理而非借用 wlroots 的 vulkan 纹理

Influence:
1. 在 vulkan 渲染模式下测试 shm 客户端窗口(XWayland、软件渲染应用)
   显示是否正常
2. 验证 vulkan 模式下 dmabuf 客户端窗口未受影响
3. 测试同一 shm buffer 的内容更新确保纹理正确刷新
4. 验证切换 shm buffer 或销毁 surface 时无纹理泄漏
5. 测试 gles2/软件渲染路径确认无回归
@deepin-ci-robot

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: Groveer

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@Groveer
Groveer marked this pull request as draft July 13, 2026 07:43
@sourcery-ai

sourcery-ai Bot commented Jul 13, 2026

Copy link
Copy Markdown

Reviewer's Guide

In Vulkan mode, shm client window buffers are now uploaded into Qt-owned textures directly from shared memory instead of borrowing wlroots VkImages, with supporting helpers and lifecycle management added to WRenderHelper and WSGTextureProvider.

Sequence diagram for shm texture upload in Vulkan mode

sequenceDiagram
    actor ShmClient
    participant WSGTextureProvider
    participant WRenderHelper
    participant QSGPlainTexture as shmTexture

    ShmClient->>WSGTextureProvider: setBuffer(qw_buffer)
    WSGTextureProvider->>WSGTextureProvider: WRenderHelper::getGraphicsApi()
    alt Vulkan_api_and_shm_buffer
        WSGTextureProvider->>WSGTextureProvider: createPlainTexture()
        WSGTextureProvider->>WRenderHelper: makeTextureFromShm(qw_buffer, shmTexture)
        WRenderHelper->>WRenderHelper: begin_data_ptr_access(...)
        WRenderHelper->>WRenderHelper: WTools::toImageFormat(format)
        WRenderHelper->>QSGPlainTexture: setImage(QImage)
        WRenderHelper->>QSGPlainTexture: setTextureSize(QSize)
        WRenderHelper->>QSGPlainTexture: setHasAlphaChannel(bool)
        WRenderHelper->>WRenderHelper: end_data_ptr_access()
        WSGTextureProvider->>WSGTextureProvider: cleanTexture()
        WSGTextureProvider->>WSGTextureProvider: store shmTexture, hasShmImage = true
        WSGTextureProvider-->>ShmClient: textureChanged()
    else Non_Vulkan_or_non_shm
        WSGTextureProvider->>WSGTextureProvider: cleanTexture()
        WSGTextureProvider->>WSGTextureProvider: use existing makeTexture path
        WSGTextureProvider-->>ShmClient: textureChanged()
    end

    ShmClient->>WSGTextureProvider: texture()
    WSGTextureProvider-->>ShmClient: shmTexture (if hasShmImage)
Loading

File-Level Changes

Change Details Files
Add a helper to build QSGPlainTexture images directly from shm buffer pixel data and DRM formats.
  • Introduce WRenderHelper::makeTextureFromShm that reads shm data via begin_data_ptr_access/end_data_ptr_access.
  • Convert DRM formats to QImage formats via WTools::toImageFormat and deep-copy pixels into a QImage.
  • Configure the target QSGPlainTexture with the new image, size, and alpha channel metadata.
waylib/src/server/qtquick/wrenderhelper.h
waylib/src/server/qtquick/wrenderhelper.cpp
Extend WSGTextureProvider to support a Vulkan-specific shm upload path using Qt-owned textures, with proper cleanup and smoothness handling.
  • Add a unique_ptr-managed QSGPlainTexture (shmTexture) plus hasShmImage flag and a helper to create configured QSGPlainTexture instances.
  • Modify setBuffer to detect Vulkan + shm buffers, build a Qt-owned texture via makeTextureFromShm, and fall back to the wlroots path if shm upload fails.
  • Handle content updates on the same buffer by re-uploading shm pixels when using the shm path.
  • Update cleanTexture to defer QRhiTexture deletion for shm textures and prevent double-delete by disabling QSGPlainTexture ownership.
  • Update texture() and setSmooth() to route through shmTexture when present and to keep filtering in sync.
waylib/src/server/qtquick/wsgtextureprovider.cpp

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 1 issue, and left some high level feedback:

  • TextureCleanupJob is now used for both rhiTexture and shmTexture but still defined locally inside cleanTexture(); consider hoisting it to a reusable helper (e.g., a small private class or lambda) to avoid repeated class definitions and make future extensions easier.
  • The hasShmImage flag duplicates shmTexture’s null/non-null state; you could simplify the state machine by deriving this from shmTexture instead of maintaining a separate boolean, reducing the risk of subtle desynchronization bugs.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- TextureCleanupJob is now used for both rhiTexture and shmTexture but still defined locally inside cleanTexture(); consider hoisting it to a reusable helper (e.g., a small private class or lambda) to avoid repeated class definitions and make future extensions easier.
- The hasShmImage flag duplicates shmTexture’s null/non-null state; you could simplify the state machine by deriving this from shmTexture instead of maintaining a separate boolean, reducing the risk of subtle desynchronization bugs.

## Individual Comments

### Comment 1
<location path="waylib/src/server/qtquick/wrenderhelper.cpp" line_range="865-866" />
<code_context>
+        // QSGPlainTexture uploads the QImage lazily during the render cycle, so
+        // the image must own its data instead of referencing the shm mapping
+        // (which is only safely accessible between begin/end_data_ptr_access).
+        image = QImage(static_cast<const uchar *>(data), size.width(), size.height(),
+                       static_cast<int>(stride), imageFormat).copy();
+    }
+    buffer->end_data_ptr_access();
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Guard against potential `stride` truncation when casting from `size_t` to `int`.

Here `stride` comes from wlroots as `size_t` and is cast to `int` for the `QImage` constructor without validation. If `stride` ever exceeds `INT_MAX`, this could overflow and produce a malformed image or undefined behavior. Please guard the cast (e.g., `stride <= std::numeric_limits<int>::max()` with an early return) or convert to `qsizetype`, which matches Qt’s internal size type, before passing it to `QImage`.

Suggested implementation:

```cpp
    const QSize size(buffer->handle()->width, buffer->handle()->height);
    const QImage::Format imageFormat = WTools::toImageFormat(format);
    QImage image;
    if (imageFormat != QImage::Format_Invalid && data && size.isValid()) {
        // Deep-copy the shm pixels while the data pointer is accessible. The
        // QSGPlainTexture uploads the QImage lazily during the render cycle, so
        // the image must own its data instead of referencing the shm mapping
        // (which is only safely accessible between begin/end_data_ptr_access).

        // Guard against stride exceeding the range of int expected by QImage.
        if (stride > static_cast<size_t>(std::numeric_limits<int>::max())) {
            buffer->end_data_ptr_access();
            return false;
        }

        const int bytesPerLine = static_cast<int>(stride);
        image = QImage(static_cast<const uchar *>(data),
                       size.width(),
                       size.height(),
                       bytesPerLine,
                       imageFormat)
                    .copy();
    }
    buffer->end_data_ptr_access();

```

If `std::numeric_limits<int>` is not already available in this file, add `#include <limits>` to the include list at the top of `wrenderhelper.cpp`.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +865 to +866
image = QImage(static_cast<const uchar *>(data), size.width(), size.height(),
static_cast<int>(stride), imageFormat).copy();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (bug_risk): Guard against potential stride truncation when casting from size_t to int.

Here stride comes from wlroots as size_t and is cast to int for the QImage constructor without validation. If stride ever exceeds INT_MAX, this could overflow and produce a malformed image or undefined behavior. Please guard the cast (e.g., stride <= std::numeric_limits<int>::max() with an early return) or convert to qsizetype, which matches Qt’s internal size type, before passing it to QImage.

Suggested implementation:

    const QSize size(buffer->handle()->width, buffer->handle()->height);
    const QImage::Format imageFormat = WTools::toImageFormat(format);
    QImage image;
    if (imageFormat != QImage::Format_Invalid && data && size.isValid()) {
        // Deep-copy the shm pixels while the data pointer is accessible. The
        // QSGPlainTexture uploads the QImage lazily during the render cycle, so
        // the image must own its data instead of referencing the shm mapping
        // (which is only safely accessible between begin/end_data_ptr_access).

        // Guard against stride exceeding the range of int expected by QImage.
        if (stride > static_cast<size_t>(std::numeric_limits<int>::max())) {
            buffer->end_data_ptr_access();
            return false;
        }

        const int bytesPerLine = static_cast<int>(stride);
        image = QImage(static_cast<const uchar *>(data),
                       size.width(),
                       size.height(),
                       bytesPerLine,
                       imageFormat)
                    .copy();
    }
    buffer->end_data_ptr_access();

If std::numeric_limits<int> is not already available in this file, add #include <limits> to the include list at the top of wrenderhelper.cpp.

@zccrs

zccrs commented Jul 13, 2026

Copy link
Copy Markdown
Member

通过 #1132 实现了

@zccrs zccrs closed this Jul 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants