Skip to content

【Experimental】Enable Vulkan Renderer#1032

Draft
LFRon wants to merge 2 commits into
linuxdeepin:masterfrom
LFRon:feat-and-refactor/enable-vulkan-renderer
Draft

【Experimental】Enable Vulkan Renderer#1032
LFRon wants to merge 2 commits into
linuxdeepin:masterfrom
LFRon:feat-and-refactor/enable-vulkan-renderer

Conversation

@LFRon

@LFRon LFRon commented Jun 21, 2026

Copy link
Copy Markdown
Contributor

这个PR让treeland内调用的Qt RHI Backend切换到Vulkan, 然后通过wlroots Vulkan后端进行上屏

@deepin-ci-robot

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: LFRon

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

@deepin-ci-robot

Copy link
Copy Markdown

Hi @LFRon. Thanks for your PR.

I'm waiting for a linuxdeepin member to verify that this patch is reasonable to test. If it is, they should reply with /ok-to-test on its own line. Until that is done, I will not automatically test new commits in this PR, but the usual testing commands by org members will still work. Regular contributors should join the org to skip this step.

Once the patch is verified, the new status will be reflected by the ok-to-test label.

I understand the commands that are listed here.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository.

@sourcery-ai

sourcery-ai Bot commented Jun 21, 2026

Copy link
Copy Markdown

Reviewer's Guide

Integrates wlroots' Vulkan renderer with Qt Quick by allowing a Vulkan wlroots backend to coexist with a GL-based Qt RHI via dmabuf/EGL interop, adds Vulkan-specific image/sync handling, and refactors texture/cleanup paths to support mixed backends and stable animations.

File-Level Changes

Change Details Files
Add EGL/dmabuf import path to let Qt GL render into wlroots Vulkan-backed buffers and textures, with proper cleanup.
  • Introduce EGL and QOpenGLContext includes and per-buffer EGLImage/GL texture tracking in BufferData, with destroyEglDmabufTexture to free imports.
  • Add runtime resolution helpers for eglCreateImageKHR/eglDestroyImageKHR/glEGLImageTargetTexture2DOES and a helper eglImportDmabufToGLTexture to import wlr_dmabuf_attributes into GL textures.
  • Extend WRenderHelper::releaseNativeTexture and NativeTextureCleanup to delete imported GL textures and EGLImages in a current GL context.
  • In acquireRenderTarget and makeTexture, when wlroots uses Vulkan but Qt RHI is GL, import buffer dmabuf into a GL texture via EGL and wrap it as a QQuickRenderTarget or QSGPlainTexture/QRhiTexture, with a pixel-readback fallback for non-dmabuf textures.
  • Adjust getUpdateTextFunction and WSGTextureProvider to cooperate with the new makeTexture signature and handle native texture cleanup correctly.
waylib/src/server/qtquick/wrenderhelper.cpp
waylib/src/server/qtquick/wrenderhelper.h
waylib/src/server/qtquick/wsgtextureprovider.cpp
waylib/src/server/qtquick/private/wrenderbuffernode.cpp
Adopt wlroots' Vulkan device into Qt RHI where appropriate and create an independent EGL context for GL RHI when wlroots renderer is Vulkan.
  • Expose wlroots Vulkan renderer native handles (VkInstance, VkPhysicalDevice, VkDevice, queue family) via qw_renderer API wrappers.
  • In WOutputRenderWindowPrivate::initRCWithRhi, when using QRhi Vulkan backend, adopt wlroots VkInstance/device into QVulkanInstance/QRhi and add robust error logging; when using QRhi OpenGLES2 backend with a Vulkan wlroots renderer, create an independent wlr_egl with DRM fd and wrap it in a QW::OpenGLContext.
  • Track and destroy the independent wlr_egl in WOutputRenderWindowPrivate destructor.
  • Adjust renderer creation to respect WLR_RENDERER=vulkan while keeping Qt RHI using OpenGL, and log mismatches or Vulkan backend selection.
  • Guard QApplication GL attribute setup so AA_UseOpenGLES is not forced when WLR_RENDERER=vulkan, matching the mixed Vulkan+GL design.
qwlroots/src/render/qwrenderer.h
waylib/src/server/qtquick/woutputrenderwindow.cpp
waylib/src/server/qtquick/wrenderhelper.cpp
waylib/src/server/qtquick/wrenderhelper.h
src/main.cpp
Implement Vulkan-specific image transition and implicit sync wiring for dmabuf-backed Vulkan images.
  • Add resolveVkGetDeviceProcAddr helper and WRenderHelper::transitionVkImageToGeneral to build a temporary Vulkan command buffer using device-level function pointers, submit it with an exportable semaphore, and signal dmabuf implicit fences via DMA_BUF_IOCTL_IMPORT_SYNC_FILE.
  • Use QRhiVulkanNativeHandles from Qt RHI to access wlroots-adopted VkDevice/queue and perform the explicit sync when ENABLE_VULKAN_RENDER is defined.
  • Ensure proper error logging and resource cleanup around command pool, command buffers, semaphores, and sync_file fds.
  • Document that GL paths rely on glFinish and implicit sync while Vulkan requires this explicit path (comment in WBufferRenderer).
waylib/src/server/qtquick/wrenderhelper.cpp
waylib/src/server/qtquick/wrenderhelper.h
waylib/src/server/qtquick/private/wbufferrenderer.cpp
Refine surface/texture lifetime and "live" behavior for animations and surface items under Vulkan backend.
  • Add isVulkanRendererBackend helper and, for splash screens on Vulkan, disable liveSource in NewAnimation to avoid live texture issues.
  • Introduce a liveSource property in NewAnimation.qml and use it to control ShaderEffectSource.live.
  • Track a textureDirty flag in WSurfaceItemContentPrivate, set it on buffer/surface changes, and use it in updatePaintNode/wTextureProvider/setLive to control when textures are refreshed.
  • Ensure WSurfaceItemContent resets textureDirty on surface destruction and buffer swaps to avoid stale textures.
src/surface/surfacewrapper.cpp
src/core/qml/Animations/NewAnimation.qml
waylib/src/server/qtquick/wsurfaceitem.cpp
Tighten renderer probing/selection and logging for Vulkan vs GLES paths.
  • In WRenderHelper::createRenderer, use WLR_RENDERER to select Vulkan renderer even when Qt RHI is GL, and warn if a non-Vulkan renderer is returned when Vulkan was requested.
  • In setupRendererBackend, when WLR_RENDERER=vulkan and Vulkan support is enabled, keep Qt GraphicsApi as OpenGL but log that Vulkan is used only for the composite backend.
  • Add minor logging and guard checks (e.g., null VkDevice/VkPhysicalDevice in initRCWithRhi) to aid debugging of Vulkan integration.
waylib/src/server/qtquick/wrenderhelper.cpp
waylib/src/server/qtquick/woutputrenderwindow.cpp

Possibly linked issues

  • #Feature: Vulkan Renderer: PR implements Vulkan renderer/backend integration with wlroots and Qt, directly addressing the Vulkan Renderer feature request.

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 5 issues, and left some high level feedback:

  • The new Vulkan/GL interop path allocates and destroys a command pool, command buffer and semaphore on every call to transitionVkImageToGeneral(), and uses vkQueueWaitIdle(), which can severely stall the GPU; consider caching a per-device command pool and reusing a command buffer plus using fences or a lighter-weight wait to reduce overhead and avoid full-queue stalls.
  • The code relies on non-public wlroots EGL symbols (wlr_egl_create_with_drm_fd / wlr_egl_destroy) declared manually; to make this more robust, consider guarding these calls with runtime checks and providing a clear fallback/log path when they are not available, or centralizing the ABI-dependent logic behind a helper abstraction so it is easier to adjust for different wlroots versions.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The new Vulkan/GL interop path allocates and destroys a command pool, command buffer and semaphore on every call to transitionVkImageToGeneral(), and uses vkQueueWaitIdle(), which can severely stall the GPU; consider caching a per-device command pool and reusing a command buffer plus using fences or a lighter-weight wait to reduce overhead and avoid full-queue stalls.
- The code relies on non-public wlroots EGL symbols (wlr_egl_create_with_drm_fd / wlr_egl_destroy) declared manually; to make this more robust, consider guarding these calls with runtime checks and providing a clear fallback/log path when they are not available, or centralizing the ABI-dependent logic behind a helper abstraction so it is easier to adjust for different wlroots versions.

## Individual Comments

### Comment 1
<location path="waylib/src/server/qtquick/wrenderhelper.cpp" line_range="1004-1013" />
<code_context>
+                for (int i = 0; i < dmabuf.n_planes; ++i) {
</code_context>
<issue_to_address>
**issue (bug_risk):** Avoid reusing and double-closing the same sync_file FD across planes

DMA_BUF_IOCTL_IMPORT_SYNC_FILE closes the given sync_file FD. In this loop, the same syncFileFd is passed to every plane, then close(syncFileFd) is called again. After the first ioctl the FD is already closed, so later iterations see EBADF and the final close() is a double-close. Please either import once (e.g., plane 0 only) and use that for the buffer, or dup() per plane and drop the final close(), only closing in the error path before any ioctl calls.
</issue_to_address>

### Comment 2
<location path="waylib/src/server/qtquick/wrenderhelper.cpp" line_range="69-72" />
<code_context>

     ~BufferData() {
         resetWindowRenderTarget();
+#ifdef ENABLE_VULKAN_RENDER
+        destroyEglDmabufTexture();
+#endif
     }
</code_context>
<issue_to_address>
**issue (bug_risk):** Guard GL resource destruction on a current context in BufferData destructor

`destroyEglDmabufTexture()` calls `glDeleteTextures`, but `BufferData` can be destroyed when no OpenGL context is current (e.g. outside a render pass). `releaseNativeTexture()` avoids this by checking `QOpenGLContext::currentContext()` first.

Please either call `releaseNativeTexture` (or a similar helper) from `~BufferData` or add a `QOpenGLContext::currentContext()` guard inside `destroyEglDmabufTexture()` to prevent GL calls without a current context and the resulting undefined behaviour.
</issue_to_address>

### Comment 3
<location path="waylib/src/server/qtquick/wrenderhelper.cpp" line_range="925-934" />
<code_context>
+    beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
+    vkBeginCommandBuffer(cb, &beginInfo);
+
+    // NOTE: No layout barrier is recorded here. The previous approach
+    // (transition COLOR_ATTACHMENT_OPTIMAL -> GENERAL + setNativeLayout) caused
+    // InvalidImageLayout validation errors (13.txt) because Qt RHI does not
+    // update usageState.layout after its render pass (finalLayout stays
+    // COLOR_ATTACHMENT_OPTIMAL but the tracked value goes stale), and
+    // preserveColorContents mode expects COLOR_ATTACHMENT_OPTIMAL as
+    // initialLayout. KMS scanout reads the dmabuf's physical memory directly
+    // and does not care about the Vulkan image layout, so leaving the image in
+    // COLOR_ATTACHMENT_OPTIMAL is acceptable. This command buffer is empty and
+    // serves only as a submit载体 to signal the semaphore for sync_file export.
+
+    vkEndCommandBuffer(cb);
+
+    // Create a binary semaphore exportable as a sync_file (VK_KHR_external_
</code_context>
<issue_to_address>
**nitpick:** Align function naming and comments with the fact that no layout transition is performed

Given this function now records no barriers and uses the command buffer only to signal a semaphore, its name and header comment are misleading. Please either rename it to reflect its role as a render completion/sync signalling helper, or update the existing comments to explicitly state that no layout transition or ownership transfer occurs and why this is valid in this pipeline.
</issue_to_address>

### Comment 4
<location path="waylib/src/server/qtquick/wrenderhelper.h" line_range="59" />
<code_context>
     static QSGRendererInterface::GraphicsApi probe(QW_NAMESPACE::qw_backend *testBackend, const QList<QSGRendererInterface::GraphicsApi> &apiList);

-    static bool makeTexture(QRhi *rhi, QW_NAMESPACE::qw_texture *handle, QSGPlainTexture *texture);
+    struct NativeTextureCleanup {
+        enum class Type {
+            None,
</code_context>
<issue_to_address>
**issue (complexity):** Consider hiding the new low‑level EGL/Vulkan structures and functions behind private or detail helpers while keeping a simple public `makeTexture`/scanout API on WRenderHelper.

You can keep the new functionality while hiding most of the low‑level surface area from `WRenderHelper`’s public API.

### 1. Hide `NativeTextureCleanup` behind a private/detail type

Right now `NativeTextureCleanup` is a public nested struct, so every user of `WRenderHelper` sees EGL/VK details. You can keep it, but make it an implementation detail:

```cpp
// in the header
class WRenderHelper : public QObject
{
    Q_OBJECT
public:
    // keep the simple API
    static bool makeTexture(QRhi *rhi,
                            QW_NAMESPACE::qw_texture *handle,
                            QSGPlainTexture *texture);

    // optional: a focused, higher-level API if callers really need cleanup
    static void releaseImportedTexture(QRhiTexture *texture);

    struct TextureEntry {
        wlr_buffer *buffer;
        QW_NAMESPACE::qw_texture *texture;
        QRhiTexture *rhiTexture;
    };

    // ...
private:
    struct NativeTextureCleanup {
        enum class Type {
            None,
            OpenGLTexture,
        };

        Type type = Type::None;
        quint64 texture = 0;
        void *eglImage = nullptr;
        void *eglDisplay = nullptr;
    };

    static void releaseNativeTexture(NativeTextureCleanup *cleanup);
};
```

```cpp
// in the cpp
bool WRenderHelper::makeTexture(QRhi *rhi,
                                qw_texture *handle,
                                QSGPlainTexture *texture)
{
    NativeTextureCleanup cleanup;
    return makeTextureImpl(rhi, handle, texture, nullptr, &cleanup);
}

// internal implementation, not declared in the header
static bool makeTextureImpl(QRhi *rhi,
                            qw_texture *handle,
                            QSGPlainTexture *texture,
                            qw_buffer *buffer,
                            WRenderHelper::NativeTextureCleanup *nativeCleanup)
{
    // existing logic...
}
```

This keeps the low‑level `NativeTextureCleanup` visible only inside the implementation, while preserving the behavior. If some special caller really needs to hook into cleanup, you can give them a higher‑level API (`releaseImportedTexture`) instead of the raw struct.

### 2. Split `makeTexture` into a public high‑level API + internal low‑level variant

The expanded `makeTexture` signature can be kept internal. Publicly, keep or reintroduce the simple form:

```cpp
// header
class WRenderHelper : public QObject
{
public:
    // high-level API
    static bool makeTexture(QRhi *rhi,
                            QW_NAMESPACE::qw_texture *handle,
                            QSGPlainTexture *texture);

private:
    static bool makeTextureImpl(QRhi *rhi,
                                QW_NAMESPACE::qw_texture *handle,
                                QSGPlainTexture *texture,
                                QW_NAMESPACE::qw_buffer *buffer,
                                NativeTextureCleanup *nativeCleanup);
};
```

```cpp
// cpp
bool WRenderHelper::makeTexture(QRhi *rhi,
                                qw_texture *handle,
                                QSGPlainTexture *texture)
{
    return makeTextureImpl(rhi, handle, texture, /*buffer*/ nullptr,
                           /*nativeCleanup*/ nullptr);
}
```

Internally (or in a private helper), you can still call `makeTextureImpl` with `buffer` and `nativeCleanup` when you need the advanced path, without exposing those extra responsibilities to all users.

### 3. Move `transitionVkImageToGeneral` to a backend/detail helper

`transitionVkImageToGeneral` is very backend‑specific. You can keep the logic but tuck it into a Vulkan‑specific helper and only expose higher‑level operations on `WRenderHelper`:

```cpp
// header
namespace detail {
class WVulkanInterop
{
public:
    static void transitionImageToGeneral(QRhi *rhi,
                                         QRhiTexture *texture,
                                         QW_NAMESPACE::qw_buffer *buffer);
};
}

class WRenderHelper : public QObject
{
public:
    // high-level API if you need a public entry point
    static void prepareExternalScanout(QRhi *rhi,
                                       QRhiTexture *texture,
                                       QW_NAMESPACE::qw_buffer *buffer);
};
```

```cpp
// cpp
void detail::WVulkanInterop::transitionImageToGeneral(QRhi *rhi,
                                                      QRhiTexture *texture,
                                                      qw_buffer *buffer)
{
    // current implementation of transitionVkImageToGeneral
}

void WRenderHelper::prepareExternalScanout(QRhi *rhi,
                                           QRhiTexture *texture,
                                           qw_buffer *buffer)
{
    detail::WVulkanInterop::transitionImageToGeneral(rhi, texture, buffer);
}
```

Callers who don’t care about Vulkan specifics can stick to `prepareExternalScanout`, while the heavy Vulkan semantics live in a clearly named backend utility class. This also avoids forcing every `WRenderHelper` user to read Vulkan‑specific comments in the main header.

These small restructurings keep all current behavior, but reduce what the main public header exposes, so most callers don’t have to understand EGL/Vulkan/implicit sync details.
</issue_to_address>

### Comment 5
<location path="waylib/src/server/qtquick/woutputrenderwindow.cpp" line_range="1399" />
<code_context>
     } else
 #endif
     if (rhiSupport->rhiBackend() == QRhi::OpenGLES2) {
-        Q_ASSERT(wlr_renderer_is_gles2(m_renderer->handle()));
-        auto egl = wlr_gles2_renderer_get_egl(m_renderer->handle());
</code_context>
<issue_to_address>
**issue (complexity):** Consider extracting backend-specific initialization into helper functions and wrapping the independent EGL pointer in an RAII helper to simplify initRCWithRhi and resource management.

You can reduce the added complexity by:

1. **Isolating backend‑specific paths into helpers**  
2. **Moving `m_independentEgl` lifetime management into an RAII helper**

Below are focused examples that keep all functionality intact.

---

### 1. Extract GLES2 / Vulkan+GL logic into helpers

Right now, `initRCWithRhi()` branches inline between:

- GLES2 wlroots renderer → adopt EGL from wlroots
- Vulkan wlroots renderer + GL RHI → create independent EGL

You can hide this distinction in a helper, so the top‑level reads linearly:

```cpp
bool WOutputRenderWindowPrivate::initRCWithRhi()
{
    // ...
#ifdef ENABLE_VULKAN_RENDER
    if (rhiSupport->rhiBackend() == QRhi::Vulkan) {
        if (!initVulkanRhiFromWlrootsRenderer())
            return false;
    } else
#endif
    if (rhiSupport->rhiBackend() == QRhi::OpenGLES2) {
        if (!initGlesRhiFromWlrootsRenderer())
            return false;
    } else {
        return false;
    }

    // offscreen surface + QRhi creation remains unchanged
}
```

Then move your detailed logic into these two helpers:

```cpp
bool WOutputRenderWindowPrivate::initGlesRhiFromWlrootsRenderer()
{
    auto r = m_renderer->handle();
    if (wlr_renderer_is_gles2(r)) {
        // current "GL wlroots renderer" path
        auto egl = wlr_gles2_renderer_get_egl(r);
        auto display = wlr_egl_get_display(egl);
        auto context = wlr_egl_get_context(egl);

        glContext = new QW::OpenGLContext(display, context, rc());
        if (!glContext->create())
            return false;

        q->setGraphicsDevice(QQuickGraphicsDevice::fromOpenGLContext(glContext));
        return true;
    }

    // current "Vulkan wlroots renderer with GL Qt RHI" path
    return initIndependentEglForVulkanRenderer();
}
```

```cpp
bool WOutputRenderWindowPrivate::initIndependentEglForVulkanRenderer()
{
    int drm_fd = wlr_renderer_get_drm_fd(m_renderer->handle());
    if (drm_fd < 0) {
        qCWarning(lcWlRenderer) << "Vulkan+GL: wlr_renderer_get_drm_fd failed";
        return false;
    }

    IndependentEglGuard eglGuard(wlr_egl_create_with_drm_fd(drm_fd));
    if (!eglGuard.egl) {
        qCWarning(lcWlRenderer) << "Vulkan+GL: wlr_egl_create_with_drm_fd failed";
        return false;
    }

    EGLDisplay display = wlr_egl_get_display(eglGuard.egl);
    EGLContext context = wlr_egl_get_context(eglGuard.egl);
    glContext = new QW::OpenGLContext(display, context, rc());
    if (!glContext->create()) {
        qCWarning(lcWlRenderer) << "Vulkan+GL: failed to create QW::OpenGLContext";
        return false;
    }

    q->setGraphicsDevice(QQuickGraphicsDevice::fromOpenGLContext(glContext));
    m_independentEgl = eglGuard.release(); // hand off ownership
    qCInfo(lcWlRenderer) << "Vulkan wlroots renderer with GL Qt RHI: using independent EGL context for dmabuf import";
    return true;
}
```

You can similarly extract the Vulkan path if desired:

```cpp
bool WOutputRenderWindowPrivate::initVulkanRhiFromWlrootsRenderer()
{
    // move the existing Vulkan logic here (including phdev/dev checks & logging)
}
```

This keeps `initRCWithRhi()` as a short dispatcher, which matches the reviewer’s suggestion and makes future backend additions easier.

---

### 2. Wrap `m_independentEgl` in a small RAII helper

Instead of manually destroying `m_independentEgl` in the destructor, you can encapsulate the lifetime in a tiny helper. This removes stateful cleanup logic from the window class and makes ownership clearer:

```cpp
struct IndependentEglContext
{
    wlr_egl *egl = nullptr;

    ~IndependentEglContext()
    {
        if (egl)
            wlr_egl_destroy(egl);
    }

    void reset(wlr_egl *newEgl = nullptr)
    {
        if (egl && egl != newEgl)
            wlr_egl_destroy(egl);
        egl = newEgl;
    }

    wlr_egl *release()
    {
        wlr_egl *tmp = egl;
        egl = nullptr;
        return tmp;
    }
};
```

Member:

```cpp
IndependentEglContext m_independentEgl;
```

Usage in your Vulkan+GL path becomes:

```cpp
wlr_egl *egl = wlr_egl_create_with_drm_fd(drm_fd);
if (!egl) {
    qCWarning(lcWlRenderer) << "Vulkan+GL: wlr_egl_create_with_drm_fd failed";
    return false;
}

m_independentEgl.reset(egl);
// ... create QW::OpenGLContext from egl ...
```

And the class destructor no longer needs to mention `wlr_egl_destroy` explicitly:

```cpp
~WOutputRenderWindowPrivate() {
    qDeleteAll(layers);
    // m_independentEgl cleans itself up
}
```

These two small refactorings keep your new feature exactly as‑is but localize backend‑specific complexity and make the lifecycle of the independent EGL context easier to reason about.
</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 +1004 to +1013
for (int i = 0; i < dmabuf.n_planes; ++i) {
struct dma_buf_import_sync_file data = {};
data.flags = DMA_BUF_SYNC_WRITE;
data.fd = syncFileFd;
if (ioctl(dmabuf.fd[i], DMA_BUF_IOCTL_IMPORT_SYNC_FILE, &data) != 0) {
qCWarning(lcWlRenderHelper) << "Vulkan: DMA_BUF_IOCTL_IMPORT_SYNC_FILE failed on plane" << i
<< "errno=" << errno << "- KMS implicit sync may not wait";
break;
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

issue (bug_risk): Avoid reusing and double-closing the same sync_file FD across planes

DMA_BUF_IOCTL_IMPORT_SYNC_FILE closes the given sync_file FD. In this loop, the same syncFileFd is passed to every plane, then close(syncFileFd) is called again. After the first ioctl the FD is already closed, so later iterations see EBADF and the final close() is a double-close. Please either import once (e.g., plane 0 only) and use that for the buffer, or dup() per plane and drop the final close(), only closing in the error path before any ioctl calls.

Comment thread waylib/src/server/qtquick/wrenderhelper.cpp
Comment thread waylib/src/server/qtquick/wrenderhelper.cpp
static QSGRendererInterface::GraphicsApi probe(QW_NAMESPACE::qw_backend *testBackend, const QList<QSGRendererInterface::GraphicsApi> &apiList);

static bool makeTexture(QRhi *rhi, QW_NAMESPACE::qw_texture *handle, QSGPlainTexture *texture);
struct NativeTextureCleanup {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

issue (complexity): Consider hiding the new low‑level EGL/Vulkan structures and functions behind private or detail helpers while keeping a simple public makeTexture/scanout API on WRenderHelper.

You can keep the new functionality while hiding most of the low‑level surface area from WRenderHelper’s public API.

1. Hide NativeTextureCleanup behind a private/detail type

Right now NativeTextureCleanup is a public nested struct, so every user of WRenderHelper sees EGL/VK details. You can keep it, but make it an implementation detail:

// in the header
class WRenderHelper : public QObject
{
    Q_OBJECT
public:
    // keep the simple API
    static bool makeTexture(QRhi *rhi,
                            QW_NAMESPACE::qw_texture *handle,
                            QSGPlainTexture *texture);

    // optional: a focused, higher-level API if callers really need cleanup
    static void releaseImportedTexture(QRhiTexture *texture);

    struct TextureEntry {
        wlr_buffer *buffer;
        QW_NAMESPACE::qw_texture *texture;
        QRhiTexture *rhiTexture;
    };

    // ...
private:
    struct NativeTextureCleanup {
        enum class Type {
            None,
            OpenGLTexture,
        };

        Type type = Type::None;
        quint64 texture = 0;
        void *eglImage = nullptr;
        void *eglDisplay = nullptr;
    };

    static void releaseNativeTexture(NativeTextureCleanup *cleanup);
};
// in the cpp
bool WRenderHelper::makeTexture(QRhi *rhi,
                                qw_texture *handle,
                                QSGPlainTexture *texture)
{
    NativeTextureCleanup cleanup;
    return makeTextureImpl(rhi, handle, texture, nullptr, &cleanup);
}

// internal implementation, not declared in the header
static bool makeTextureImpl(QRhi *rhi,
                            qw_texture *handle,
                            QSGPlainTexture *texture,
                            qw_buffer *buffer,
                            WRenderHelper::NativeTextureCleanup *nativeCleanup)
{
    // existing logic...
}

This keeps the low‑level NativeTextureCleanup visible only inside the implementation, while preserving the behavior. If some special caller really needs to hook into cleanup, you can give them a higher‑level API (releaseImportedTexture) instead of the raw struct.

2. Split makeTexture into a public high‑level API + internal low‑level variant

The expanded makeTexture signature can be kept internal. Publicly, keep or reintroduce the simple form:

// header
class WRenderHelper : public QObject
{
public:
    // high-level API
    static bool makeTexture(QRhi *rhi,
                            QW_NAMESPACE::qw_texture *handle,
                            QSGPlainTexture *texture);

private:
    static bool makeTextureImpl(QRhi *rhi,
                                QW_NAMESPACE::qw_texture *handle,
                                QSGPlainTexture *texture,
                                QW_NAMESPACE::qw_buffer *buffer,
                                NativeTextureCleanup *nativeCleanup);
};
// cpp
bool WRenderHelper::makeTexture(QRhi *rhi,
                                qw_texture *handle,
                                QSGPlainTexture *texture)
{
    return makeTextureImpl(rhi, handle, texture, /*buffer*/ nullptr,
                           /*nativeCleanup*/ nullptr);
}

Internally (or in a private helper), you can still call makeTextureImpl with buffer and nativeCleanup when you need the advanced path, without exposing those extra responsibilities to all users.

3. Move transitionVkImageToGeneral to a backend/detail helper

transitionVkImageToGeneral is very backend‑specific. You can keep the logic but tuck it into a Vulkan‑specific helper and only expose higher‑level operations on WRenderHelper:

// header
namespace detail {
class WVulkanInterop
{
public:
    static void transitionImageToGeneral(QRhi *rhi,
                                         QRhiTexture *texture,
                                         QW_NAMESPACE::qw_buffer *buffer);
};
}

class WRenderHelper : public QObject
{
public:
    // high-level API if you need a public entry point
    static void prepareExternalScanout(QRhi *rhi,
                                       QRhiTexture *texture,
                                       QW_NAMESPACE::qw_buffer *buffer);
};
// cpp
void detail::WVulkanInterop::transitionImageToGeneral(QRhi *rhi,
                                                      QRhiTexture *texture,
                                                      qw_buffer *buffer)
{
    // current implementation of transitionVkImageToGeneral
}

void WRenderHelper::prepareExternalScanout(QRhi *rhi,
                                           QRhiTexture *texture,
                                           qw_buffer *buffer)
{
    detail::WVulkanInterop::transitionImageToGeneral(rhi, texture, buffer);
}

Callers who don’t care about Vulkan specifics can stick to prepareExternalScanout, while the heavy Vulkan semantics live in a clearly named backend utility class. This also avoids forcing every WRenderHelper user to read Vulkan‑specific comments in the main header.

These small restructurings keep all current behavior, but reduce what the main public header exposes, so most callers don’t have to understand EGL/Vulkan/implicit sync details.

q->setGraphicsDevice(gd);
} else
#endif
if (rhiSupport->rhiBackend() == QRhi::OpenGLES2) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

issue (complexity): Consider extracting backend-specific initialization into helper functions and wrapping the independent EGL pointer in an RAII helper to simplify initRCWithRhi and resource management.

You can reduce the added complexity by:

  1. Isolating backend‑specific paths into helpers
  2. Moving m_independentEgl lifetime management into an RAII helper

Below are focused examples that keep all functionality intact.


1. Extract GLES2 / Vulkan+GL logic into helpers

Right now, initRCWithRhi() branches inline between:

  • GLES2 wlroots renderer → adopt EGL from wlroots
  • Vulkan wlroots renderer + GL RHI → create independent EGL

You can hide this distinction in a helper, so the top‑level reads linearly:

bool WOutputRenderWindowPrivate::initRCWithRhi()
{
    // ...
#ifdef ENABLE_VULKAN_RENDER
    if (rhiSupport->rhiBackend() == QRhi::Vulkan) {
        if (!initVulkanRhiFromWlrootsRenderer())
            return false;
    } else
#endif
    if (rhiSupport->rhiBackend() == QRhi::OpenGLES2) {
        if (!initGlesRhiFromWlrootsRenderer())
            return false;
    } else {
        return false;
    }

    // offscreen surface + QRhi creation remains unchanged
}

Then move your detailed logic into these two helpers:

bool WOutputRenderWindowPrivate::initGlesRhiFromWlrootsRenderer()
{
    auto r = m_renderer->handle();
    if (wlr_renderer_is_gles2(r)) {
        // current "GL wlroots renderer" path
        auto egl = wlr_gles2_renderer_get_egl(r);
        auto display = wlr_egl_get_display(egl);
        auto context = wlr_egl_get_context(egl);

        glContext = new QW::OpenGLContext(display, context, rc());
        if (!glContext->create())
            return false;

        q->setGraphicsDevice(QQuickGraphicsDevice::fromOpenGLContext(glContext));
        return true;
    }

    // current "Vulkan wlroots renderer with GL Qt RHI" path
    return initIndependentEglForVulkanRenderer();
}
bool WOutputRenderWindowPrivate::initIndependentEglForVulkanRenderer()
{
    int drm_fd = wlr_renderer_get_drm_fd(m_renderer->handle());
    if (drm_fd < 0) {
        qCWarning(lcWlRenderer) << "Vulkan+GL: wlr_renderer_get_drm_fd failed";
        return false;
    }

    IndependentEglGuard eglGuard(wlr_egl_create_with_drm_fd(drm_fd));
    if (!eglGuard.egl) {
        qCWarning(lcWlRenderer) << "Vulkan+GL: wlr_egl_create_with_drm_fd failed";
        return false;
    }

    EGLDisplay display = wlr_egl_get_display(eglGuard.egl);
    EGLContext context = wlr_egl_get_context(eglGuard.egl);
    glContext = new QW::OpenGLContext(display, context, rc());
    if (!glContext->create()) {
        qCWarning(lcWlRenderer) << "Vulkan+GL: failed to create QW::OpenGLContext";
        return false;
    }

    q->setGraphicsDevice(QQuickGraphicsDevice::fromOpenGLContext(glContext));
    m_independentEgl = eglGuard.release(); // hand off ownership
    qCInfo(lcWlRenderer) << "Vulkan wlroots renderer with GL Qt RHI: using independent EGL context for dmabuf import";
    return true;
}

You can similarly extract the Vulkan path if desired:

bool WOutputRenderWindowPrivate::initVulkanRhiFromWlrootsRenderer()
{
    // move the existing Vulkan logic here (including phdev/dev checks & logging)
}

This keeps initRCWithRhi() as a short dispatcher, which matches the reviewer’s suggestion and makes future backend additions easier.


2. Wrap m_independentEgl in a small RAII helper

Instead of manually destroying m_independentEgl in the destructor, you can encapsulate the lifetime in a tiny helper. This removes stateful cleanup logic from the window class and makes ownership clearer:

struct IndependentEglContext
{
    wlr_egl *egl = nullptr;

    ~IndependentEglContext()
    {
        if (egl)
            wlr_egl_destroy(egl);
    }

    void reset(wlr_egl *newEgl = nullptr)
    {
        if (egl && egl != newEgl)
            wlr_egl_destroy(egl);
        egl = newEgl;
    }

    wlr_egl *release()
    {
        wlr_egl *tmp = egl;
        egl = nullptr;
        return tmp;
    }
};

Member:

IndependentEglContext m_independentEgl;

Usage in your Vulkan+GL path becomes:

wlr_egl *egl = wlr_egl_create_with_drm_fd(drm_fd);
if (!egl) {
    qCWarning(lcWlRenderer) << "Vulkan+GL: wlr_egl_create_with_drm_fd failed";
    return false;
}

m_independentEgl.reset(egl);
// ... create QW::OpenGLContext from egl ...

And the class destructor no longer needs to mention wlr_egl_destroy explicitly:

~WOutputRenderWindowPrivate() {
    qDeleteAll(layers);
    // m_independentEgl cleans itself up
}

These two small refactorings keep your new feature exactly as‑is but localize backend‑specific complexity and make the lifecycle of the independent EGL context easier to reason about.

@deepin-bot

deepin-bot Bot commented Jun 22, 2026

Copy link
Copy Markdown

TAG Bot

New tag: 0.8.12
DISTRIBUTION: unstable
Suggest: synchronizing this PR through rebase #1036

@LFRon LFRon force-pushed the feat-and-refactor/enable-vulkan-renderer branch 2 times, most recently from f33c0ac to 974a92a Compare June 23, 2026 04:22

@zccrs zccrs left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

这个改动的核心思想是啥?不至于有这么多地方需要对vulkan、gles2做判断区分呀,原本不就支持vulkan渲染吗,核心链路应该是通的(最近没验证过),不应该出现这么多改动。

#if WLR_HAVE_VULKAN_RENDERER
// Access the wlroots-adopted Vulkan device handles. Used by compositors
// (e.g. waylib) that adopt the wlroots VkDevice into Qt RHI.
QW_FUNC_MEMBER(vk_renderer, get_instance, VkInstance)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

不应该加到这个class里,要有应该是个新的 vk_renderer 类型

QW_FUNC_STATIC(texture, from_dmabuf, qw_texture *, wlr_renderer *renderer, wlr_dmabuf_attributes *attribs)
QW_FUNC_STATIC(texture, from_buffer, qw_texture *, wlr_renderer *renderer, wlr_buffer *buffer)
#if WLR_HAVE_VULKAN_RENDERER
QW_FUNC_MEMBER(vk_texture, get_image_attribs, void, wlr_vk_image_attribs *attribs)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

同上

m_windowAnimation = m_engine->createNewAnimation(this, container(), direction);
m_windowAnimation->setProperty("enableBlur", m_blur);
if (m_type == Type::SplashScreen && isVulkanRendererBackend())
m_windowAnimation->setProperty("liveSource", false);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

这是为啥?为什么vulkan backend时liveSource需要是false?

Comment thread src/main.cpp
});
// QQuickStyle::setStyle("Material");

QGuiApplication::setAttribute(Qt::AA_UseOpenGLES);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

这行应该可以直接删掉,WRenderHelper::setupRendererBackend(); 会找到合适的backend

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

这里的设置不要紧,只是告诉Qt如果用opengl,应该用gles版本,不会影响treeland和QtQuick的渲染引擎。

auto dev = wlr_vk_renderer_get_device(m_renderer->handle());
auto queue_family = wlr_vk_renderer_get_queue_family(m_renderer->handle());
if (Q_UNLIKELY(!phdev || !dev)) {
qCWarning(lcWlRenderer) << "Vulkan: wlroots renderer exposed null VkPhysicalDevice/VkDevice, cannot adopt into Qt RHI";

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

什么情况下会走到这里?

bool ok = this->glContext->create();
if (!ok)
return false;
if (wlr_renderer_is_gles2(m_renderer->handle())) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

为什么把这里的断言改成了if判断?不应该出现 rhiSupport->rhiBackend() == QRhi::OpenGLES2 时,wlroots的backend不是gles2的情况,如果存在,那就是其他地方的代码出现了逻辑性错误。

return false;

q->setGraphicsDevice(QQuickGraphicsDevice::fromOpenGLContext(this->glContext));
} else {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

不要单独处理这种else的情况,项目的设计要求就是wlroots的render backend和QRhi的backend必须一致,不应该出现不一致的情况,不支持这种用法。没看出来做这种情况的兼容有啥意义。

}

#ifdef ENABLE_VULKAN_RENDER
if (isVulkanRenderer()) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

为什么vulkan模式下需要做这样的特殊处理?

@LFRon

LFRon commented Jun 23, 2026

Copy link
Copy Markdown
Contributor Author

这个改动的核心思想是啥?不至于有这么多地方需要对vulkan、gles2做判断区分呀,原本不就支持vulkan渲染吗,核心链路应该是通的(最近没验证过),不应该出现这么多改动。

我让GPT跟GLM都试过, Qt的RHI Vulkan的生命周期管理等一系列行为与wlroots的Vulkan有区别, 要改动的幅度实在是太大了必须要人力介入改, 所以我才刻意避开了, 并把这个PR标注为了实验性仅供参考
image

@LFRon LFRon force-pushed the feat-and-refactor/enable-vulkan-renderer branch 2 times, most recently from bd7354e to a3652d2 Compare June 24, 2026 04:41
@zccrs

zccrs commented Jun 24, 2026

Copy link
Copy Markdown
Member

这个改动的核心思想是啥?不至于有这么多地方需要对vulkan、gles2做判断区分呀,原本不就支持vulkan渲染吗,核心链路应该是通的(最近没验证过),不应该出现这么多改动。

我让GPT跟GLM都试过, Qt的RHI Vulkan的生命周期管理等一系列行为与wlroots的Vulkan有区别, 要改动的幅度实在是太大了必须要人力介入改, 所以我才刻意避开了, 并把这个PR标注为了实验性仅供参考 image

直接WLR_RENDERER=vulkan就是用vulkan渲染了,无需做改动,你那边有啥问题呀

@LFRon

LFRon commented Jun 24, 2026

Copy link
Copy Markdown
Contributor Author

这个改动的核心思想是啥?不至于有这么多地方需要对vulkan、gles2做判断区分呀,原本不就支持vulkan渲染吗,核心链路应该是通的(最近没验证过),不应该出现这么多改动。

我让GPT跟GLM都试过, Qt的RHI Vulkan的生命周期管理等一系列行为与wlroots的Vulkan有区别, 要改动的幅度实在是太大了必须要人力介入改, 所以我才刻意避开了, 并把这个PR标注为了实验性仅供参考 image

直接WLR_RENDERER=vulkan就是用vulkan渲染了,无需做改动,你那边有啥问题呀

  1. Qt QML RHI仍然保持OpenGL渲染,多次dmabuf来回拷贝容易导致内核驱动崩溃(就是我当前这个更改)
  2. Qt QML RHI如果走Vulkan, 在生命周期等上有诸多问题,我还在试着改

@LFRon LFRon force-pushed the feat-and-refactor/enable-vulkan-renderer branch from a3652d2 to 0b5e38a Compare June 24, 2026 12:18
@deepin-bot

deepin-bot Bot commented Jun 26, 2026

Copy link
Copy Markdown

TAG Bot

New tag: 0.8.13
DISTRIBUTION: unstable
Suggest: synchronizing this PR through rebase #1063

@LFRon LFRon force-pushed the feat-and-refactor/enable-vulkan-renderer branch from 0b5e38a to e91ea02 Compare June 29, 2026 16:34
@zccrs

zccrs commented Jun 30, 2026

Copy link
Copy Markdown
Member

这个改动的核心思想是啥?不至于有这么多地方需要对vulkan、gles2做判断区分呀,原本不就支持vulkan渲染吗,核心链路应该是通的(最近没验证过),不应该出现这么多改动。

我让GPT跟GLM都试过, Qt的RHI Vulkan的生命周期管理等一系列行为与wlroots的Vulkan有区别, 要改动的幅度实在是太大了必须要人力介入改, 所以我才刻意避开了, 并把这个PR标注为了实验性仅供参考 image

直接WLR_RENDERER=vulkan就是用vulkan渲染了,无需做改动,你那边有啥问题呀

  1. Qt QML RHI仍然保持OpenGL渲染,多次dmabuf来回拷贝容易导致内核驱动崩溃(就是我当前这个更改)
  2. Qt QML RHI如果走Vulkan, 在生命周期等上有诸多问题,我还在试着改
  1. WLR_RENDERER=vulkan 时 “QML RHI 仍然保持OpenGL渲染” 这个是需求还是说现状呀?现状qml应该是走到了vulkan才对。
  2. 这些问题可以具体提一下。

@LFRon

LFRon commented Jun 30, 2026

Copy link
Copy Markdown
Contributor Author
  1. WLR_RENDERER=vulkan 时 “QML RHI 仍然保持OpenGL渲染” 这个是需求还是说现状呀

WLR_RENDERER=vulkan 时 “QML RHI 仍然保持OpenGL渲染” 这个是需求还是说现状呀

这个我说的是现状, 因为QML RHI走Vulkan需要自己手动管理生命周期显式同步等太麻烦了, 而且wlroots用Vulkan合成/渲染并不影响对应的应用该走OpenGL走OpenGL, wlroots的Vk渲染只是走最后一步, 这个类似于安卓HWUI Vulkan

@LFRon LFRon force-pushed the feat-and-refactor/enable-vulkan-renderer branch from e91ea02 to 684b9c6 Compare June 30, 2026 04:53
@zccrs

zccrs commented Jul 1, 2026

Copy link
Copy Markdown
Member
  1. WLR_RENDERER=vulkan 时 “QML RHI 仍然保持OpenGL渲染” 这个是需求还是说现状呀

WLR_RENDERER=vulkan 时 “QML RHI 仍然保持OpenGL渲染” 这个是需求还是说现状呀

这个我说的是现状, 因为QML RHI走Vulkan需要自己手动管理生命周期显式同步等太麻烦了, 而且wlroots用Vulkan合成/渲染并不影响对应的应用该走OpenGL走OpenGL, wlroots的Vk渲染只是走最后一步, 这个类似于安卓HWUI Vulkan

  1. 跟我的测试情况不太一样,指定了 WLR_RENDERER=vulkan 后,treeland里的QtQuick和QML都会使用vulkan进行渲染,可以通过给 vkGetInstanceProcAddr 加断点做验证。

这里的设置只对wlroots和Treeland生效,不会影响其他程序,wayland client程序该是opengl还是opengl。

@LFRon

LFRon commented Jul 1, 2026

Copy link
Copy Markdown
Contributor Author
  1. 指定了 WLR_RENDERER=vulkan 后,treeland里的QtQuick和QML都会使用vulkan进行渲染,可以通过给 vkGetInstanceProcAddr 加断点做验证。

指定了 WLR_RENDERER=vulkan 后,treeland里的QtQuick和QML都会使用vulkan进行渲染,可以通过给 vkGetInstanceProcAddr 加断点做验证。

对, 我这么试着做了, 然后出了一大堆问题, 包括但不局限于光标丢失/圆角丢失/只有图标, 我把新的半成品代码放上来了
还有不清楚我的理解对不对: treeland调用的QML其实是通过QML的能力将整个画面合成为一块画布, 将最终"成品"递给qwlroots包裹好的wlroots能力进行最终上屏? 如果对, 那我这个Vulkan renderer实现太多余了, 相当于把已经准备上屏的OpenGL内容又扒拉了出来塞进dmabuf弄一遍再通过Vulkan上屏, 效率太低了

@LFRon LFRon force-pushed the feat-and-refactor/enable-vulkan-renderer branch from f4e5ab5 to 809d783 Compare July 1, 2026 13:09
@LFRon LFRon marked this pull request as draft July 1, 2026 13:09
@LFRon

LFRon commented Jul 1, 2026

Copy link
Copy Markdown
Contributor Author

我的对话记录:
chatgpt-5.5.md

@LFRon LFRon force-pushed the feat-and-refactor/enable-vulkan-renderer branch from 809d783 to fa23670 Compare July 1, 2026 13:12
@zccrs

zccrs commented Jul 2, 2026

Copy link
Copy Markdown
Member
  1. 指定了 WLR_RENDERER=vulkan 后,treeland里的QtQuick和QML都会使用vulkan进行渲染,可以通过给 vkGetInstanceProcAddr 加断点做验证。

指定了 WLR_RENDERER=vulkan 后,treeland里的QtQuick和QML都会使用vulkan进行渲染,可以通过给 vkGetInstanceProcAddr 加断点做验证。

对, 我这么试着做了, 然后出了一大堆问题, 包括但不局限于光标丢失/圆角丢失/只有图标, 我把新的半成品代码放上来了 还有不清楚我的理解对不对: treeland调用的QML其实是通过QML的能力将整个画面合成为一块画布, 将最终"成品"递给qwlroots包裹好的wlroots能力进行最终上屏? 如果对, 那我这个Vulkan renderer实现太多余了, 相当于把已经准备上屏的OpenGL内容又扒拉了出来塞进dmabuf弄一遍再通过Vulkan上屏, 效率太低了

对于上屏的理解是对的,上屏用的buffer无论在什么渲染接口下,都是由wlroots提供的。treeland的工作是把这个buffer作为render target给QtQuick使用,QtQuick会把那些qml元素都合成到这个buffer上,合成完成后由Treeland通过wlroots的接口上屏。

vulkan模式下我这里的已知问题只有一个:没有支持高斯模糊效果。其他的应该都ok,包括窗口阴影、圆角之类的。

你遇到的问题可以截个图看看。

@LFRon

LFRon commented Jul 2, 2026

Copy link
Copy Markdown
Contributor Author
  1. 指定了 WLR_RENDERER=vulkan 后,treeland里的QtQuick和QML都会使用vulkan进行渲染,可以通过给 vkGetInstanceProcAddr 加断点做验证。

指定了 WLR_RENDERER=vulkan 后,treeland里的QtQuick和QML都会使用vulkan进行渲染,可以通过给 vkGetInstanceProcAddr 加断点做验证。

对, 我这么试着做了, 然后出了一大堆问题, 包括但不局限于光标丢失/圆角丢失/只有图标, 我把新的半成品代码放上来了 还有不清楚我的理解对不对: treeland调用的QML其实是通过QML的能力将整个画面合成为一块画布, 将最终"成品"递给qwlroots包裹好的wlroots能力进行最终上屏? 如果对, 那我这个Vulkan renderer实现太多余了, 相当于把已经准备上屏的OpenGL内容又扒拉了出来塞进dmabuf弄一遍再通过Vulkan上屏, 效率太低了

对于上屏的理解是对的,上屏用的buffer无论在什么渲染接口下,都是由wlroots提供的。treeland的工作是把这个buffer作为render target给QtQuick使用,QtQuick会把那些qml元素都合成到这个buffer上,合成完成后由Treeland通过wlroots的接口上屏。

vulkan模式下我这里的已知问题只有一个:没有支持高斯模糊效果。其他的应该都ok,包括窗口阴影、圆角之类的。

你遇到的问题可以截个图看看。

就是这个分支的代码, 有严重的闪屏等一系列问题
CRnall_20260701_153143155

顺带一句,treeland本体跑在OpenGL(GLES2)下, 切QML RHI到vulkan一样会导致dde-shell起不来, 这个好像是Qt自己的问题, 我之前翻到过有人反馈Wayland下vulkan后端的QML速度巨慢

@LFRon

LFRon commented Jul 2, 2026

Copy link
Copy Markdown
Contributor Author
image

- add Vulkan RHI output-layer compositor plumbing for treeland canvas
- experiment with client dmabuf import paths for Qt Quick Vulkan RHI
- add diagnostics for output buffers, surface textures, alpha, and modifiers
- track surface opaque regions through texture provider/render helper paths
- prefer texture-compatible modifiers for intermediate Vulkan output buffers
- document current investigation state in chatgpt-5.5.md

This is still a work in progress. The remaining blocker is stabilizing
client dmabuf import/sampling semantics inside the Qt Quick Vulkan scene graph.
Drop the experimental Vulkan direct-surface rendering path that tried to
bypass the Qt Quick scene graph for client surfaces.

The removed path included:
- WVulkanSurfaceRenderNode and its custom shaders
- per-surface vulkanDirectSurfaceAllowed QML/property plumbing
- output-layer direct client surface collection/composition logic
- the unsafe wlroots native VkImage wrapper fallback for dmabuf textures

Keep client contents represented through the normal Qt Quick SurfaceItemContent
path, and let the Vulkan RHI path use QRhiTexture import plus synchronized
fallbacks instead of injecting separate client-surface render nodes.

This intentionally keeps the Vulkan compositor architecture centered around a
single Qt Quick scene render target. The remaining Vulkan runtime issue is in
the output intermediate dmabuf image usage contract: the Qt render target is
later sampled by the wlroots Vulkan output-layer compositor, so its imported
VkImage usage needs to include the real roles used by both sides.
@LFRon LFRon force-pushed the feat-and-refactor/enable-vulkan-renderer branch from db2626d to e258631 Compare July 2, 2026 07:19
@LFRon

LFRon commented Jul 2, 2026

Copy link
Copy Markdown
Contributor Author

@zccrs 大佬, 我现在把这个分支的AI写的代码已经改成了几乎贴近treeland实现方式的方案, 但是有大量问题比如Vulkan显式同步等都没解决, 我把我能做到更改的放这了

@LFRon

LFRon commented Jul 2, 2026

Copy link
Copy Markdown
Contributor Author

我现在有考虑一个方案: client dmabuf -> wlroots Vulkan renderer 合成
不进 Qt RHI,让OpenGL应用渲染时直接走wlroots, 最接近 sway/SurfaceFlinger,稳定性和职责边界更对。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants