场景图 - QML 下的 Vulkan

展示如何在Qt Quick 场景下直接使用 Vulkan 渲染。

注: 编译此示例需要 SDK。有关安装信息,请参阅Vulkan Integration

概述

本示例利用QQuickWindow::beforeRendering() 和QQuickWindow::beforeRenderPassRecording() 信号在Qt Quick 场景下绘制自定义 Vulkan 内容。QML 用于在顶部渲染文本标签。

该示例在大多数方面与OpenGL Under QMLDirect3D 11 Under QMLMetal Under QML示例相同,它们都是通过不同的本地 API 来呈现相同的自定义内容。

本文档将介绍使用 QML 的具体细节,而不会深入探讨定制 Vulkan 渲染的细节。

从 QML 影响 Vulkan 渲染

该示例展示了如何使用 QML 暴露的值来控制 Vulkan 渲染。

为了将阈值t 暴露给 QML,在VulkanSquircle 的定义中,我们使用了Q_OBJECTQ_PROPERTYQML_ELEMENT 宏:

class VulkanSquircle : public QQuickItem
{
    Q_OBJECT
    Q_PROPERTY(qreal t READ t WRITE setT NOTIFY tChanged)
    QML_ELEMENT

然后,我们继续声明公共项和私有项:

public:
    VulkanSquircle();

    qreal t() const { return m_t; }
    void setT(qreal t);

signals:
    void tChanged();

public slots:
    void sync();
    void cleanup();

private slots:
    void handleWindowChanged(QQuickWindow *win);

private:
    void releaseResources() override;

    qreal m_t = 0;
    SquircleRenderer *m_renderer = nullptr;

然后,在main.qml 中,我们使用NumberAnimation 将阈值动画化。

    VulkanSquircle {
        SequentialAnimation on t {
            NumberAnimation { to: 1; duration: 2500; easing.type: Easing.InQuad }
            NumberAnimation { to: 0; duration: 2500; easing.type: Easing.OutQuad }
            loops: Animation.Infinite

t 变量最终会被绘制松鼠的 SPIR-V 着色程序使用。

使用信号渲染自定义 Vulkan 内容

QQuickWindow::beforeRendering() 和QQuickWindow::beforeRenderPassRecording() 信号是使用的信号。

QQuickWindow::beforeRendering() 信号在场景图开始渲染之前的每一帧开始时发出。这意味着响应该信号的任何 Vulkan 绘制调用都会堆叠到Qt Quick 项下。之所以有两个信号,是因为自定义 Vulkan 命令被记录在场景图使用的同一个命令缓冲区中。

beforeRendering()函数本身不足以实现这一点,因为它会在帧开始时发出,在使用vkCmdBeginRenderPass 记录renderpass 实例的开始之前。

解决办法是:通过连接 beforeRenderPassRecording(),应用程序自己的命令和场景图的脚手架将以正确的顺序结束。

连接信号由sync() 函数完成:

void VulkanSquircle::sync()
{
    if (!m_renderer) {
        m_renderer = new SquircleRenderer;
        // Initializing resources is done before starting to record the
        // renderpass, regardless of wanting an underlay or overlay.
        connect(window(), &QQuickWindow::beforeRendering, m_renderer, &SquircleRenderer::frameStart, Qt::DirectConnection);
        // Here we want an underlay and therefore connect to
        // beforeRenderPassRecording. Changing to afterRenderPassRecording
        // would render the squircle on top (overlay).
        connect(window(), &QQuickWindow::beforeRenderPassRecording, m_renderer, &SquircleRenderer::mainPassRecordingStart, Qt::DirectConnection);
    }
    m_renderer->setViewportSize(window()->size() * window()->devicePixelRatio());
    m_renderer->setT(m_t);

Qt Quick 场景之上渲染 Vulkan 内容的另一种方法是连接QQuickWindow::afterRendering() 和QQuickWindow::afterRenderPassRecording() 信号。

示例项目 @ code.qt.io

© 2025 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.