Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ set ( deepin-screen-recorder_HDRS
)

set ( deepin-screen-recorder_SRCS
utils/cpuvendor.cpp
utils/log.cpp
utils/calculaterect.cpp
utils/shapesutils.cpp
Expand Down
7 changes: 7 additions & 0 deletions src/src.pro
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,13 @@ lessThan(QT_MAJOR_VERSION, 6) {
}

# Sources
HEADERS += utils/cpuvendor.h
SOURCES += utils/cpuvendor.cpp
equals(QT_MAJOR_VERSION, 6) {
HEADERS += utils/ximageconverter.h
SOURCES += utils/ximageconverter.cpp
}

SOURCES += main.cpp \
capture.cpp \
main_window.cpp \
Expand Down
27 changes: 8 additions & 19 deletions src/utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// SPDX-License-Identifier: GPL-3.0-or-later

#include "utils.h"
#include "utils/cpuvendor.h"
#include "constant.h"
#include "dbus_name.h"

Expand All @@ -11,12 +12,13 @@
#endif

#include <mutex>
#include <dlfcn.h>

Check warning on line 15 in src/utils.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <dlfcn.h> not found. Please note: Cppcheck does not need standard library headers to get proper results.

#include <QString>

Check warning on line 17 in src/utils.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QString> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QFile>

Check warning on line 18 in src/utils.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QFile> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QDir>

Check warning on line 19 in src/utils.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QDir> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QApplication>

Check warning on line 20 in src/utils.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QApplication> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QDebug>

Check warning on line 21 in src/utils.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QDebug> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QFontMetrics>
#include <QPainter>
#include <QPainterPath>
Expand Down Expand Up @@ -698,27 +700,14 @@
bool Utils::checkCpuIsZhaoxin()
{
qCInfo(dsrApp) << "checkCpuIsZhaoxin() called.";
QProcess process;
process.start("lscpu");
process.waitForFinished();
process.waitForReadyRead();
QString comStr = process.readAllStandardOutput();
process.close();
QStringList lines = comStr.split('\n');
QStringList filteredLines;

// 过滤包含指定关键字的行
for (const QString &line : lines) {
if (line.contains("CentaurHauls", Qt::CaseInsensitive)) {
filteredLines.append(line);
}
}
if (filteredLines.isEmpty()) {
qCInfo(dsrApp) << "CPU is not zhaoxin.";
QFile cpuInfo(QStringLiteral("/proc/cpuinfo"));
if (!cpuInfo.open(QIODevice::ReadOnly)) {
qCWarning(dsrApp) << "Cannot read CPU vendor:" << cpuInfo.errorString();
return false;
}
qCInfo(dsrApp) << "CPU is zhaoxin.";
return true;
const bool isZhaoxin = CpuVendor::isZhaoxin(cpuInfo);
qCInfo(dsrApp) << "CPU is zhaoxin:" << isZhaoxin;
return isZhaoxin;
}

// 获取处理器名称
Expand Down
22 changes: 22 additions & 0 deletions src/utils/cpuvendor.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd.
// SPDX-License-Identifier: GPL-3.0-or-later
#include "cpuvendor.h"
#include <QIODevice>

Check warning on line 4 in src/utils/cpuvendor.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QIODevice> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QByteArray>

Check warning on line 5 in src/utils/cpuvendor.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QByteArray> not found. Please note: Cppcheck does not need standard library headers to get proper results.

bool CpuVendor::isZhaoxin(QIODevice &cpuInfo)
{
if (!cpuInfo.isReadable())
return false;
// /proc files can report size zero even while they contain readable data.
for (QByteArray line = cpuInfo.readLine(); !line.isEmpty(); line = cpuInfo.readLine()) {
const int separator = line.indexOf(':');
if (separator < 0)
continue;
if (line.left(separator).trimmed().compare("vendor_id", Qt::CaseInsensitive) == 0
&& line.mid(separator + 1).trimmed().compare("CentaurHauls", Qt::CaseInsensitive) == 0) {
return true;
}
}
return false;
}
9 changes: 9 additions & 0 deletions src/utils/cpuvendor.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd.
// SPDX-License-Identifier: GPL-3.0-or-later
#ifndef CPUVENDOR_H
#define CPUVENDOR_H
class QIODevice;
namespace CpuVendor {
bool isZhaoxin(QIODevice &cpuInfo);
}
#endif
18 changes: 8 additions & 10 deletions src/utils/screengrabber.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

// X11 headers for XGetImage workaround
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
#include "ximageconverter.h"
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#endif
Expand Down Expand Up @@ -650,20 +651,17 @@ QPixmap ScreenGrabber::grabWithXGetImage(bool &ok, const QRect &rect)
<< "depth:" << ximg->depth << "bits_per_pixel:" << ximg->bits_per_pixel;

// 将 XImage 转换为 QImage
QImage qimg(ximg->width, ximg->height, QImage::Format_RGB32);
for (int row = 0; row < ximg->height; ++row) {
for (int col = 0; col < ximg->width; ++col) {
unsigned long pixel = XGetPixel(ximg, col, row);
int r = (pixel >> 16) & 0xFF;
int g = (pixel >> 8) & 0xFF;
int b = pixel & 0xFF;
qimg.setPixel(col, row, qRgb(r, g, b));
}
}
QImage qimg = XImageConverter::toImage(ximg);

XDestroyImage(ximg);
XCloseDisplay(dpy);

if (qimg.isNull()) {
qCWarning(dsrApp) << "[XGetImage] Failed to convert XImage";
ok = false;
return QPixmap();
}

// 转换为 QPixmap
QPixmap fullPixmap = QPixmap::fromImage(qimg);
qCDebug(dsrApp) << "[XGetImage] Full pixmap size:" << fullPixmap.size();
Expand Down
60 changes: 60 additions & 0 deletions src/utils/ximageconverter.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd.
// SPDX-License-Identifier: GPL-3.0-or-later
#include "ximageconverter.h"
#include <QSysInfo>

Check warning on line 4 in src/utils/ximageconverter.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QSysInfo> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <X11/Xlib.h>

Check warning on line 5 in src/utils/ximageconverter.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <X11/Xlib.h> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <X11/Xutil.h>

namespace {
// True when the XImage buffer is already laid out as native-endian 0x??RRGGBB words.
bool isNativeRgb32(const XImage *image)
{
const int nativeOrder = QSysInfo::ByteOrder == QSysInfo::LittleEndian ? LSBFirst : MSBFirst;
return image->format == ZPixmap && image->bits_per_pixel == 32
&& (image->depth == 24 || image->depth == 32)
&& image->red_mask == 0xff0000 && image->green_mask == 0xff00 && image->blue_mask == 0xff
&& image->byte_order == nativeOrder && image->xoffset == 0
&& image->bytes_per_line >= image->width * 4
&& image->bytes_per_line % alignof(quint32) == 0
&& reinterpret_cast<quintptr>(image->data) % alignof(quint32) == 0;
}

void releaseXImageData(void *data)
{
XFree(data);
}
}

QImage XImageConverter::toImage(XImage *image)
{
if (!image || !image->data || image->width <= 0 || image->height <= 0 || !image->f.get_pixel)
return {};

if (isNativeRgb32(image)) {
// Force the unused high byte opaque in place, then hand the buffer to QImage.
for (int y = 0; y < image->height; ++y) {
quint32 *row = reinterpret_cast<quint32 *>(image->data + qsizetype(y) * image->bytes_per_line);
for (int x = 0; x < image->width; ++x)
row[x] |= 0xff000000u;
}
uchar *data = reinterpret_cast<uchar *>(image->data);
QImage result(data, image->width, image->height, image->bytes_per_line,
QImage::Format_RGB32, releaseXImageData, data);
if (!result.isNull())
image->data = nullptr;
return result;
}

// Preserve the existing conversion for uncommon XImage layouts.
QImage result(image->width, image->height, QImage::Format_RGB32);
if (result.isNull())
return result;
for (int y = 0; y < image->height; ++y) {
QRgb *row = reinterpret_cast<QRgb *>(result.scanLine(y));
for (int x = 0; x < image->width; ++x) {
const unsigned long pixel = XGetPixel(image, x, y);
row[x] = qRgb((pixel >> 16) & 0xff, (pixel >> 8) & 0xff, pixel & 0xff);
}
}
return result;
}
14 changes: 14 additions & 0 deletions src/utils/ximageconverter.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd.
// SPDX-License-Identifier: GPL-3.0-or-later
#ifndef XIMAGECONVERTER_H
#define XIMAGECONVERTER_H
#include <QImage>
struct _XImage;

namespace XImageConverter {
// Returns an opaque RGB32 image. When the XImage already stores native RGB32
// pixels, its buffer is reused: alpha is forced in place and image->data is set
// to nullptr. Callers must still release the XImage with XDestroyImage.
QImage toImage(_XImage *image);
}
#endif
26 changes: 26 additions & 0 deletions tests/startup/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd.
# SPDX-License-Identifier: GPL-3.0-or-later
cmake_minimum_required(VERSION 3.16)
project(screenshot_startup_tests LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
find_package(Qt6 REQUIRED COMPONENTS Core Gui)
find_package(X11 REQUIRED)
option(STARTUP_TEST_LEGACY "Run the fast-path regression against the original loop" OFF)
if(STARTUP_TEST_LEGACY)
add_executable(test_startup test_startup.cpp)
target_compile_definitions(test_startup PRIVATE STARTUP_TEST_LEGACY)
else()
add_executable(test_startup test_startup.cpp ../../src/utils/ximageconverter.cpp)
endif()
target_link_libraries(test_startup PRIVATE Qt6::Core Qt6::Gui X11::X11)
enable_testing()
add_test(NAME startup_regressions COMMAND test_startup)

add_executable(test_cpuvendor test_cpuvendor.cpp ../../src/utils/cpuvendor.cpp)
target_link_libraries(test_cpuvendor PRIVATE Qt6::Core)
add_test(NAME cpuvendor_regressions COMMAND test_cpuvendor)
if(NOT STARTUP_TEST_LEGACY)
add_executable(benchmark_startup benchmark_startup.cpp
../../src/utils/ximageconverter.cpp ../../src/utils/cpuvendor.cpp)
target_link_libraries(benchmark_startup PRIVATE Qt6::Core Qt6::Gui X11::X11)
endif()
44 changes: 44 additions & 0 deletions tests/startup/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Screenshot startup tests

These focused tests need Qt6 Core/Gui development files, Xlib, CMake and a C++17 compiler. They do not require recording or camera dependencies and do not open a window.

```sh
cmake -S tests/startup -B build/startup-tests -DCMAKE_BUILD_TYPE=Release
cmake --build build/startup-tests -j4
ctest --test-dir build/startup-tests --output-on-failure
build/startup-tests/benchmark_startup
```

The pixel tests cover row padding, alpha, buffer adoption, byte order, 24-bit storage, source alignment, nonstandard depth/masks and invalid input. CPU tests use in-memory metadata, including empty, unreadable and misleading input.

`benchmark_startup` alternates the old and new conversion order for 31 samples and compares every pixel. Add `--desktop` to also compare a real X11 desktop image in memory; it does not save the image. CPU timing compares `lscpu` with the procfs detector.

The image regression can be run against the original conversion, where it must fail because native RGB32 still calls XGetPixel per pixel:

```sh
cmake -S tests/startup -B build/startup-legacy -DSTARTUP_TEST_LEGACY=ON
cmake --build build/startup-legacy -j4
ctest --test-dir build/startup-legacy --output-on-failure
```

Compare two full app builds through the screenshot D-Bus method:

```sh
python3 tests/startup/benchmark_launch.py /path/to/before /path/to/after --samples 11
```

This temporarily displays and closes each overlay, refuses to interfere with an existing screenshot/recording service, and only terminates its own subprocesses. It reports a new-process measurement with warm filesystem cache. The endpoint is showFullScreen return, not the first frame presented by the compositor. Results and per-run logs default to `/tmp/dsr-launch-benchmark`.

## Local measurements

Measured on X11 with Qt6, a 2880x1800 physical screen and DPR 1.75. Both variants used the same source baseline and Release build options. Eleven runs per variant were interleaved, each using a new process with warm filesystem cache:

| Boundary | Before median | After median |
| --- | ---: | ---: |
| Process launch to showFullScreen return | 240.30 ms | 184.63 ms |
| StartScreenshot method entry to showFullScreen return | 152.15 ms | 95.57 ms |
| Process launch to service ready | 83.29 ms | 83.43 ms |

For 31 alternating samples, conversion of a real 2880x1800 desktop XImage fell from 55.00 ms to 2.22 ms with identical pixels; CPU detection fell from 6.60 ms to 0.67 ms. Native RGB32 images are converted in place and the Xlib buffer is adopted by the QImage, so there is no second full-screen allocation or copy; `XGetImage` itself (about 23 ms on the same machine) is now the dominant capture cost. These component timings exclude capture, QPixmap creation and display. Application measurements use log receipt times and include logging/reader overhead; they do not measure keyboard dispatch, cold D-Bus activation, cold filesystem cache or compositor presentation.

Both before/after builds covered only the screenshot main program (`src/src.pro`). The top-level subdirectory project, Dock plugins, pin-screenshots program and Debian package were not built. In particular, the missing `libdtk6tablerecognizer-dev` dependency still prevents validating the pin-screenshots build. Qt5, multiscreen, Wayland/Treeland and the full existing unit-test suite were not exercised. The optimized main program also saved a valid opaque 2880x1800 PNG in the local fullscreen-save smoke test.
Loading
Loading