334 lines
9.5 KiB
Markdown
334 lines
9.5 KiB
Markdown
|
|
# 统一渲染架构快速入门
|
|||
|
|
|
|||
|
|
## 概述
|
|||
|
|
|
|||
|
|
本项目采用统一的渲染架构设计,通过抽象基类 `RenderWidgetBase` 实现对 Vulkan 和 QPainter 两种渲染方式的统一管理。
|
|||
|
|
|
|||
|
|
## 核心设计
|
|||
|
|
|
|||
|
|
### 三层架构
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
┌─────────────────────────────────────┐
|
|||
|
|
│ MainWindow │ ← 使用统一接口
|
|||
|
|
│ (RenderWidgetBase* m_renderWidget) │
|
|||
|
|
└──────────────┬──────────────────────┘
|
|||
|
|
│
|
|||
|
|
↓
|
|||
|
|
┌─────────────────────────────────────┐
|
|||
|
|
│ RenderWidgetBase (基类) │ ← 定义统一接口
|
|||
|
|
│ - initializeRenderer() │
|
|||
|
|
│ - setRenderingEnabled() │
|
|||
|
|
│ - getRenderFrameCount() │
|
|||
|
|
│ - getRendererType() │
|
|||
|
|
└──────────────┬──────────────────────┘
|
|||
|
|
│
|
|||
|
|
┌───────┴────────┐
|
|||
|
|
↓ ↓
|
|||
|
|
┌─────────────┐ ┌─────────────┐
|
|||
|
|
│VulkanWidget │ │CustomWidget │ ← 具体实现
|
|||
|
|
│ (Vulkan) │ │ (QPainter) │
|
|||
|
|
└─────────────┘ └─────────────┘
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 编译选项
|
|||
|
|
|
|||
|
|
### 使用 Vulkan(默认,如果可用)
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
cd ScreenLockDetector
|
|||
|
|
mkdir -p build && cd build
|
|||
|
|
cmake .. -DENABLE_VULKAN=ON
|
|||
|
|
make -j4
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**结果**:应用将使用 VulkanWidget 进行硬件加速渲染
|
|||
|
|
|
|||
|
|
### 使用 QPainter(后备方案)
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
cd ScreenLockDetector
|
|||
|
|
mkdir -p build && cd build
|
|||
|
|
cmake .. -DENABLE_VULKAN=OFF
|
|||
|
|
make -j4
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**结果**:应用将使用 CustomWidget 进行 CPU 绘制
|
|||
|
|
|
|||
|
|
### 自动回退
|
|||
|
|
|
|||
|
|
如果 Vulkan 头文件不可用,CMake 会自动回退到 QPainter:
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
cmake ..
|
|||
|
|
# 输出:Vulkan headers not found, disabling Vulkan support
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 主要改进
|
|||
|
|
|
|||
|
|
### 之前的设计问题
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
// ❌ 旧设计:需要维护两个独立的组件
|
|||
|
|
private:
|
|||
|
|
CustomWidget *m_customWidget;
|
|||
|
|
#ifdef ENABLE_VULKAN_WIDGET
|
|||
|
|
VulkanWidget *m_vulkanWidget;
|
|||
|
|
#endif
|
|||
|
|
|
|||
|
|
QTabWidget *m_tabWidget; // 使用 Tab 切换
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 新的统一设计
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
// ✅ 新设计:单一组件指针
|
|||
|
|
private:
|
|||
|
|
RenderWidgetBase *m_renderWidget; // 统一接口
|
|||
|
|
|
|||
|
|
// 构造函数中根据编译选项创建
|
|||
|
|
#ifdef ENABLE_VULKAN_WIDGET
|
|||
|
|
m_renderWidget = new VulkanWidget(this);
|
|||
|
|
#else
|
|||
|
|
m_renderWidget = new CustomWidget(this);
|
|||
|
|
#endif
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 统一接口使用示例
|
|||
|
|
|
|||
|
|
### 1. 初始化渲染器
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
// 对于 VulkanWidget:内部调用 initializeVulkan()
|
|||
|
|
// 对于 CustomWidget:直接返回 true(无需初始化)
|
|||
|
|
bool success = m_renderWidget->initializeRenderer();
|
|||
|
|
if (!success) {
|
|||
|
|
qDebug() << "Error:" << m_renderWidget->getLastError();
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 2. 控制渲染
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
// 启用渲染(适用于任何渲染器)
|
|||
|
|
m_renderWidget->setRenderingEnabled(true);
|
|||
|
|
|
|||
|
|
// 禁用渲染(适用于任何渲染器)
|
|||
|
|
m_renderWidget->setRenderingEnabled(false);
|
|||
|
|
|
|||
|
|
// 查询状态
|
|||
|
|
bool isRendering = m_renderWidget->isRenderingEnabled();
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 3. 获取状态信息
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
// 获取渲染器类型
|
|||
|
|
QString type = m_renderWidget->getRendererType();
|
|||
|
|
// 返回 "Vulkan" 或 "QPainter"
|
|||
|
|
|
|||
|
|
// 获取帧数统计
|
|||
|
|
int frames = m_renderWidget->getRenderFrameCount();
|
|||
|
|
|
|||
|
|
// 检查初始化状态
|
|||
|
|
bool initialized = m_renderWidget->isInitialized();
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 4. 错误处理
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
if (!m_renderWidget->isInitialized()) {
|
|||
|
|
if (!m_renderWidget->initializeRenderer()) {
|
|||
|
|
QMessageBox::warning(
|
|||
|
|
this,
|
|||
|
|
"初始化失败",
|
|||
|
|
m_renderWidget->getLastError()
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 锁屏检测集成
|
|||
|
|
|
|||
|
|
无论使用哪种渲染器,锁屏检测的处理逻辑完全一致:
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
void MainWindow::onLockStateChanged(bool locked)
|
|||
|
|
{
|
|||
|
|
// 统一的处理逻辑
|
|||
|
|
if (m_renderWidget) {
|
|||
|
|
m_renderWidget->setRenderingEnabled(!locked);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
updateStatusDisplay();
|
|||
|
|
updateButtonStates();
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 添加新渲染器
|
|||
|
|
|
|||
|
|
假设要添加 OpenGL 支持:
|
|||
|
|
|
|||
|
|
### 1. 创建新类
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
// src/openglwidget.h
|
|||
|
|
class OpenGLWidget : public RenderWidgetBase
|
|||
|
|
{
|
|||
|
|
Q_OBJECT
|
|||
|
|
public:
|
|||
|
|
explicit OpenGLWidget(QWidget *parent = nullptr);
|
|||
|
|
|
|||
|
|
// 实现所有虚函数
|
|||
|
|
bool initializeRenderer() override;
|
|||
|
|
void setRenderingEnabled(bool enabled) override;
|
|||
|
|
bool isRenderingEnabled() const override;
|
|||
|
|
int getRenderFrameCount() const override;
|
|||
|
|
void resetFrameCount() override;
|
|||
|
|
bool isInitialized() const override;
|
|||
|
|
QString getLastError() const override;
|
|||
|
|
QString getRendererType() const override { return "OpenGL"; }
|
|||
|
|
};
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 2. 修改 CMakeLists.txt
|
|||
|
|
|
|||
|
|
```cmake
|
|||
|
|
option(ENABLE_OPENGL "Enable OpenGL support" OFF)
|
|||
|
|
|
|||
|
|
if(ENABLE_OPENGL)
|
|||
|
|
list(APPEND SOURCES src/openglwidget.cpp)
|
|||
|
|
list(APPEND HEADERS src/openglwidget.h)
|
|||
|
|
add_definitions(-DENABLE_OPENGL_WIDGET)
|
|||
|
|
endif()
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 3. 修改 MainWindow 构造函数
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
#ifdef ENABLE_VULKAN_WIDGET
|
|||
|
|
m_renderWidget = new VulkanWidget(this);
|
|||
|
|
#elif defined(ENABLE_OPENGL_WIDGET)
|
|||
|
|
m_renderWidget = new OpenGLWidget(this);
|
|||
|
|
#else
|
|||
|
|
m_renderWidget = new CustomWidget(this);
|
|||
|
|
#endif
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 运行效果
|
|||
|
|
|
|||
|
|
### Vulkan 模式
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
┌──────────────────────────────────────┐
|
|||
|
|
│ Qt Screen Lock Detection Demo │
|
|||
|
|
├──────────────────────────────────────┤
|
|||
|
|
│ │
|
|||
|
|
│ [Vulkan 硬件加速渲染窗口] │
|
|||
|
|
│ (旋转的彩色圆圈动画) │
|
|||
|
|
│ │
|
|||
|
|
├──────────────────────────────────────┤
|
|||
|
|
│ [ Enable Rendering ] │
|
|||
|
|
│ [ Disable Rendering ] │
|
|||
|
|
│ [ Reset Frame Count ] │
|
|||
|
|
├──────────────────────────────────────┤
|
|||
|
|
│ Renderer Type: Vulkan │
|
|||
|
|
│ Detector Status: Active │
|
|||
|
|
│ Renderer Status: ✓ Initialized │
|
|||
|
|
│ Screen Lock Status: 🔓 UNLOCKED │
|
|||
|
|
│ Rendering Status: ✓ ENABLED │
|
|||
|
|
│ Frame Count: 1234 frames │
|
|||
|
|
└──────────────────────────────────────┘
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### QPainter 模式
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
┌──────────────────────────────────────┐
|
|||
|
|
│ Qt Screen Lock Detection Demo │
|
|||
|
|
├──────────────────────────────────────┤
|
|||
|
|
│ │
|
|||
|
|
│ [QPainter 2D 渲染窗口] │
|
|||
|
|
│ (旋转的彩色圆圈动画) │
|
|||
|
|
│ │
|
|||
|
|
├──────────────────────────────────────┤
|
|||
|
|
│ [ Enable Rendering ] │
|
|||
|
|
│ [ Disable Rendering ] │
|
|||
|
|
│ [ Reset Frame Count ] │
|
|||
|
|
├──────────────────────────────────────┤
|
|||
|
|
│ Renderer Type: QPainter │
|
|||
|
|
│ Detector Status: Active │
|
|||
|
|
│ Renderer Status: ✓ Initialized │
|
|||
|
|
│ Screen Lock Status: 🔓 UNLOCKED │
|
|||
|
|
│ Rendering Status: ✓ ENABLED │
|
|||
|
|
│ Frame Count: 1234 frames │
|
|||
|
|
└──────────────────────────────────────┘
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 优势总结
|
|||
|
|
|
|||
|
|
### ✅ 代码简化
|
|||
|
|
- MainWindow 代码减少约 40%
|
|||
|
|
- 消除了大量重复的控制逻辑
|
|||
|
|
- 减少了条件编译代码
|
|||
|
|
|
|||
|
|
### ✅ 统一界面
|
|||
|
|
- 不再使用 Tab 切换
|
|||
|
|
- 单一、简洁的用户界面
|
|||
|
|
- 自动选择最佳渲染方式
|
|||
|
|
|
|||
|
|
### ✅ 易于维护
|
|||
|
|
- 添加新功能只需修改基类接口
|
|||
|
|
- 所有实现类自动继承新接口
|
|||
|
|
- 更容易追踪和修复 bug
|
|||
|
|
|
|||
|
|
### ✅ 高可扩展性
|
|||
|
|
- 可以轻松添加新的渲染后端
|
|||
|
|
- 可以在运行时切换渲染器(未来功能)
|
|||
|
|
- 支持插件化架构(未来功能)
|
|||
|
|
|
|||
|
|
## 常见问题
|
|||
|
|
|
|||
|
|
### Q: 如何知道当前使用的是哪种渲染器?
|
|||
|
|
|
|||
|
|
A: 查看应用程序窗口的 "Renderer Type" 标签,或在日志中查看:
|
|||
|
|
```
|
|||
|
|
MainWindow initialized with Vulkan renderer
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Q: Vulkan 初始化失败怎么办?
|
|||
|
|
|
|||
|
|
A: 应用会自动显示错误信息。可以重新编译使用 QPainter:
|
|||
|
|
```bash
|
|||
|
|
cmake .. -DENABLE_VULKAN=OFF
|
|||
|
|
make -j4
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Q: 两种渲染器性能差异大吗?
|
|||
|
|
|
|||
|
|
A:
|
|||
|
|
- **Vulkan**: 硬件加速,GPU 渲染,CPU 占用低,适合复杂场景
|
|||
|
|
- **QPainter**: CPU 渲染,CPU 占用较高,但兼容性最好
|
|||
|
|
|
|||
|
|
### Q: 可以在运行时切换渲染器吗?
|
|||
|
|
|
|||
|
|
A: 当前版本不支持,但架构设计已经为此功能预留了空间。
|
|||
|
|
|
|||
|
|
## 相关文档
|
|||
|
|
|
|||
|
|
- [详细设计文档](./unified_rendering_design.md) - 完整的架构设计说明
|
|||
|
|
- [API 参考](../src/renderwidgetbase.h) - RenderWidgetBase 接口定义
|
|||
|
|
- [主 README](../README.md) - 项目总体说明
|
|||
|
|
|
|||
|
|
## 贡献指南
|
|||
|
|
|
|||
|
|
如果您想添加新的渲染后端:
|
|||
|
|
|
|||
|
|
1. 创建新类继承 `RenderWidgetBase`
|
|||
|
|
2. 实现所有虚函数
|
|||
|
|
3. 在 CMakeLists.txt 中添加编译选项
|
|||
|
|
4. 在 MainWindow 中添加创建逻辑
|
|||
|
|
5. 编写测试用例
|
|||
|
|
6. 更新文档
|
|||
|
|
|
|||
|
|
欢迎提交 Pull Request!
|