65.9K
CodeProject 正在变化。 阅读更多。
Home

ARM 上的 Windows:Qt 框架的原生支持

starIconstarIconstarIconstarIconstarIcon

5.00/5 (1投票)

2023年4月26日

CPOL

7分钟阅读

viewsIcon

6389

本文将向您展示如何通过简单的更改,将 x64 架构的计算时间减少 40%。

Qt 框架 (Qt) 提供了模块化的 C++ 库类和应用程序编程接口 (API),以加速跨平台应用程序开发。Qt 使您能够开发可维护、高性能和可重用的代码。凭借其小部件工具包,所有使用 Qt 创建的应用程序都具有原生外观的用户界面 (UI)。Qt 还提供了开发工具,包括 Qt Creator,一个跨平台集成开发环境 (IDE)。

Qt v6.2 现在支持 Arm 上的 Windows (WoA) 的原生开发。为了演示原生开发的优势,本文将向您展示如何对图形图像执行仿射变换。这些变换演示了 AArch64 架构提供的性能提升。它们包括图像旋转、缩放和剪切,这些在人工智能驱动的机器视觉系统中常用。然后,本教程将向您展示如何通过简单的更改,将 x64 架构的计算时间减少 40%。

Qt 现在包含原生 WoA 支持

Qt 是一个用于桌面、嵌入式和移动平台的应用程序开发框架。

Qt v6.2 现在支持 WoA 的原生开发,因此您可以从官方网站下载安装程序。Qt 使您能够为 x64 或 AArch64 处理器架构构建您的软件。因此,您可以充分利用设备硬件来加速您的图形用户界面 (GUI) 应用程序。

Qt 的优势

Qt 提供了跨平台应用程序开发,因此您可以使用相同的高级 API 来创建可在桌面、移动和物联网 (IoT) 设备上运行的应用程序,而无需或只需极少的平台特定代码。这意味着您可以使用它同时针对多个平台。

您还可以使用 Qt 创建智能边缘设备。随着更复杂的 AI 模型转移到边缘设备,对这些设备效率的需求持续增加。AArch64 提供了高性能和低能耗要求,使其成为边缘设备上应用程序的理想选择。此外,AArch64 现在通过 Arm64EC 在 Windows 11 上得到全面支持。

设置开发环境

本教程使用 Windows Dev Kit 2023(也称为 Project Volterra)进行开发。但您也可以在运行 Windows 11 的 Apple 芯片 Mac 电脑上获得类似的性能提升(例如,通过使用 Parallels Desktop)。

要开始开发,请访问 Qt Group 下载页面,注册 Qt 试用版,然后下载 Qt。或者,如果您不想注册,请访问Qt for Open Source Development页面并从那里下载。

Qt Setup 窗口中,选择至少 Qt 6.2 版本。本教程使用 Qt 6.4。

请注意,Windows Dev Kit 2023 运行完整版 Windows 11,因此安装和开发过程与典型桌面相同。这意味着您无需学习任何新技能即可为 WoA 开发应用程序。

安装后,打开 Qt Creator,点击 Examples,选择 Qt6,然后在搜索框中输入“affine”。

如果您是 Qt Creator 的新手,请查阅文档以快速入门。项目文件在左侧,双击它们会在编辑器中打开。RunDebug 按钮在左下角。或者您可以按 F5 运行和调试您的项目,按 Ctrl + R 在没有调试器的情况下运行它。

示例列表应包含一个项目:Affine Transformations

点击它,使源代码在 Qt Creator 中可用。现在构建并运行应用程序。

执行后,应用程序应如下所示:

应用程序通过旋转、缩放和剪切来连续变换 Tux(Linux 的企鹅角色)的图像。您可以通过点击 Animate 来停止动画。当应用程序在 XFormWidget 的构造函数中运行时,它是激活的。

现在您可以修改此默认行为。

开发应用程序

要修改仿射变换应用程序,请添加一个带 Run 标签的按钮。当您点击此按钮时,它会调用自定义的 runAnimation 方法。此方法负责多次执行仿射变换。这使您可以在为 x64 和 AArch64 架构构建应用程序时测量这些操作的性能。

与任何其他 C++ 应用程序一样,应用程序的入口点是 main 函数,如 main.cpp 中所定义。main 函数初始化并显示 xform.hxform.cpp 中的 XFormWidget。

首先更改 xform.h 文件(位于项目树左侧的 Header 文件夹中)。首先,添加一个 include 指令来导入 QElapsedTimer 类,您将使用它来测量代码的执行时间。

#ifndef XFORM_H
#define XFORM_H
 
#include "arthurwidgets.h"
 
#include <QBasicTimer>
#include <QPolygonF>
#include <QElapsedTimer>

然后,通过在 public slots 部分声明 runAnimation 方法来修改 XFormView 类的声明。您将把此方法与 Run 按钮的点击信号关联起来。

class XFormView : public ArthurFrame
{
// …
public slots:
    void setAnimation(bool animate);
    void runAnimation();
    void updateControlPoints(const QPolygonF &);
    void changeRotation(int rotation);
    void changeScale(int scale);
    void changeShear(int shear);
// …

最后,在 xform.h 头文件的 private 部分声明一个 QElapsedTimer 对象的实例。

private:
    QPolygonF m_controlPoints{{250, 250}, {350, 250}};
    HoverPoints *m_hoverPoints;
    qreal m_rotation = 0;
    qreal m_scale = 1;
    qreal m_shear = 0;
    XFormType m_type = VectorType;
    QPixmap m_pixmap;
    QString m_text;
    QBasicTimer timer;
    QElapsedTimer elapsedTimer;

现在,您需要修改源文件 (xform.cpp)。通过添加一个 Run 按钮,将 runAnimation 方法与该按钮的点击信号绑定,并禁用 animateButtonanimateClick 方法的调用,可以防止在启动应用程序时运行默认动画。

然后,使用以下代码中加粗的语句修改 XFormWidget 构造函数(此类的声明在 XFormView 之后)。

XFormWidget::XFormWidget(QWidget *parent)
    : QWidget(parent), textEditor(new QLineEdit)
{
    setWindowTitle(tr("Affine Transformations"));
 
    //…
 
    QPushButton *whatsThisButton = new QPushButton(mainGroup);
    whatsThisButton->setText(tr("What's This?"));
    whatsThisButton->setCheckable(true);
 
    QPushButton *runAnimationButton = new QPushButton(mainGroup);
    runAnimationButton->setText(tr("Run"));
 
    // …    
 
    QVBoxLayout *mainGroupLayout = new QVBoxLayout(mainGroup);
    mainGroupLayout->addWidget(rotateGroup);
    mainGroupLayout->addWidget(scaleGroup);
    mainGroupLayout->addWidget(shearGroup);
    mainGroupLayout->addWidget(typeGroup);
    mainGroupLayout->addStretch(1);
    mainGroupLayout->addWidget(resetButton);
    mainGroupLayout->addWidget(animateButton);
    mainGroupLayout->addWidget(showSourceButton);
#if QT_CONFIG(opengl)
    mainGroupLayout->addWidget(enableOpenGLButton);
#endif
    mainGroupLayout->addWidget(whatsThisButton);
    mainGroupLayout->addWidget(runAnimationButton);
 
    // …
 
    connect(resetButton, &QPushButton::clicked, view, &XFormView::reset);
    connect(animateButton, &QPushButton::clicked, view, &XFormView::setAnimation);
    connect(whatsThisButton, &QPushButton::clicked, view, 
        &ArthurFrame::setDescriptionEnabled);
    connect(runAnimationButton, &QPushButton::clicked, view, &XFormView::runAnimation);
    connect(whatsThisButton, &QPushButton::clicked, view->hoverPoints(), 
        &HoverPoints::setDisabled);
    connect(view, &XFormView::descriptionEnabledChanged, view->hoverPoints(), 
        &HoverPoints::setDisabled);
    connect(view, &XFormView::descriptionEnabledChanged, whatsThisButton,
        &QPushButton::setChecked);
    connect(showSourceButton, &QPushButton::clicked, view, &XFormView::showSource);
#if QT_CONFIG(opengl)
    connect(enableOpenGLButton, &QPushButton::clicked, view, &XFormView::enableOpenGL);
#endif
    view->loadSourceFile(":res/affine/xform.cpp");
    view->loadDescription(":res/affine/xform.html");
 
    // defaults
    view->reset();
    vectorType->setChecked(true);   
    textEditor->setText("Qt Affine Transformation Example");
    textEditor->setEnabled(false);
 
    //animateButton->animateClick(); // Prevent running the default animation
}

接下来,实现 runAnimation 方法。

void XFormView::runAnimation()
{
    // Restart timer
    elapsedTimer.restart();
    // Run affine transformations 10,000 times
    for(int i = 0; i < 10000; i++) {
        QPointF center(m_hoverPoints->points().at(0));
        QTransform m;
        m.translate(center.x(), center.y());
        m.rotate(0.2);
        m.translate(-center.x(), -center.y());
        m_hoverPoints->setPoints(m_hoverPoints->points() * m);
 
        setUpdatesEnabled(false);
        static qreal scale_inc = 0.003;
        static qreal shear_inc = -0.001;
        emit scaleChanged(int((m_scale + scale_inc) * 1000));
        emit shearChanged(int((m_shear + shear_inc) * 1000));
        if (m_scale >= 4.0 || m_scale <= 0.1)
            scale_inc = -scale_inc;
        if (m_shear >= 1.0 || m_shear <= -1.0)
            shear_inc = -shear_inc;
        setUpdatesEnabled(true);
 
        m_hoverPoints->firePointChange();
    }
    // Measure performance
    qDebug() << "Animation took" << elapsedTimer.elapsed() << "milliseconds";
}

该方法的工作方式类似于 Animate 按钮的原始事件处理程序。它平移 (QTransform.translate)、旋转 (QTransform.rotate)、缩放 (QTransform.scale) 和剪切 (QTransform.shear) 图像。它使用 for 循环下的语句实现所有这些操作。在底层,QTransform 使用变换矩阵执行仿射变换。因此,您还可以使用 QTransform.setMatrix 方法手动设置该矩阵的元素。

请注意,用户界面通过信号(通过 emit 关键字)进行更新。此更新执行缩放和剪切更改。

然后,您只添加了三样东西:

  • elapsedTimer.restart 方法,用于重新启动 QElapsedTimer 实例。
  • for 循环中嵌入的变换。
  • qDebug 用于在调试器控制台中打印代码执行时间的信息。

以上所有内容都与架构无关。这意味着您可以在为 AArch64 开发时使用与 x64 相同的特性,包括调试。您还可以使用提供的 CMakeLists.txt 来配置 CMake 并向项目添加单元测试

运行应用程序

现在是时候使用 AArch64 构建运行应用程序了。为此,请使用 QtCreator 左下角的图标。选择 Desktop Qt 6.4.2 MSVC2019 ARM64

点击绿色播放图标开始调试。

点击 Run 触发动画。Tux 图像被变换 10,000 次。变换所需的时间显示在 Qt Creator 窗口底部的 Application Output 窗口中。请注意,您必须点击 Application Output 或按 Alt + 3 才能查看它。

平均而言,当应用程序使用 AArch64 架构时,计算时间约为 270 毫秒。

现在,切换到 x64,重新构建并启动应用程序。点击 Run,您将看到 x64 架构的计算时间。

计算速度较慢,平均需要约 438 毫秒。这意味着您的应用程序在 AArch64 上的性能比 x64 快 1.6 倍。

结论

Qt 框架从 6.2 版本开始,支持原生 WoA 开发。本教程演示了在为 AArch64 处理器构建时使用 Qt 的好处。Qt 使您能够开发可维护、高性能和可重用的代码,它对 WoA 的支持使您的应用程序性能大大提高。在本教程中,AArch64 上的构建比 x64 快 1.6 倍。

您可以通过在 Qt 界面中切换构建配置来利用这种性能提升。其他一切保持不变。您可以快速加速您现有的 Qt 应用程序,并以近两倍的速度处理应用程序命令。这提升了您 AI 驱动的边缘设备的性能并提高了其准确性,使您能够部署更严苛的 AI 模型。

亲自尝试 WoA 上的 Qt 框架,并使用 Windows Dev Kit 2023 编译和测试您的应用程序。

© . All rights reserved.