Add graphics backend selection

This commit is contained in:
2026-07-13 10:19:29 +08:00
parent c74cc92879
commit 341283b1cd
8 changed files with 176 additions and 3 deletions
+2
View File
@@ -144,6 +144,8 @@ void Config::SetDefaults()
SetEntryInternal(QStringLiteral("PreviewNonFloatDontAskAgain"),
NodeValue::kBoolean, false);
SetEntryInternal(QStringLiteral("UseGLFinish"), NodeValue::kBoolean, false);
SetEntryInternal(QStringLiteral("GraphicsBackend"), NodeValue::kText,
QStringLiteral("opengl"));
SetEntryInternal(QStringLiteral("RenderProcessIsolationEnabled"),
NodeValue::kBoolean, false);
@@ -22,6 +22,7 @@
#include "preferencesbehaviortab.h"
#include <QCheckBox>
#include <QComboBox>
#include <QVBoxLayout>
#include "config/config.h"
@@ -36,7 +37,8 @@ PreferencesBehaviorTab::PreferencesBehaviorTab()
behavior_tree_ = new QTreeWidget();
layout->addWidget(behavior_tree_);
behavior_tree_->setHeaderLabel(tr("Behavior"));
behavior_tree_->setColumnCount(2);
behavior_tree_->setHeaderLabels({ tr("Behavior"), tr("Setting") });
QTreeWidgetItem *general_group = AddParent(tr("General"));
AddItem(
@@ -97,7 +99,30 @@ PreferencesBehaviorTab::PreferencesBehaviorTab()
"dependencies among clips when copying or splitting them."),
node_group);
QTreeWidgetItem *opengl_group = AddParent(tr("OpenGL"));
QTreeWidgetItem *rendering_group = AddParent(tr("Rendering"));
QTreeWidgetItem *graphics_backend_item =
new QTreeWidgetItem({ tr("Graphics Backend") });
graphics_backend_item->setToolTip(
0, tr("Selects the graphics API Oak should request on next launch. "
"Vulkan is experimental and currently falls back to the OpenGL "
"renderer for timeline/viewer rendering."));
rendering_group->addChild(graphics_backend_item);
graphics_backend_combobox_ = new QComboBox();
graphics_backend_combobox_->addItem(tr("OpenGL"), QStringLiteral("opengl"));
graphics_backend_combobox_->addItem(tr("Vulkan (experimental)"),
QStringLiteral("vulkan"));
const QString current_backend =
OLIVE_CONFIG("GraphicsBackend").toString().toLower();
const int backend_index = graphics_backend_combobox_->findData(
current_backend.isEmpty() ? QStringLiteral("opengl")
: current_backend);
graphics_backend_combobox_->setCurrentIndex(backend_index >= 0
? backend_index
: 0);
behavior_tree_->setItemWidget(graphics_backend_item, 1,
graphics_backend_combobox_);
QTreeWidgetItem *opengl_group = AddParent(tr("OpenGL"), rendering_group);
AddItem(tr("Use glFinish"), QStringLiteral("UseGLFinish"), opengl_group);
}
@@ -110,6 +135,9 @@ void PreferencesBehaviorTab::Accept(MultiUndoCommand *command)
OLIVE_CONFIG_STR(iterator.value()) =
(iterator.key()->checkState(0) == Qt::Checked);
}
OLIVE_CONFIG("GraphicsBackend") =
graphics_backend_combobox_->currentData().toString();
}
QTreeWidgetItem *PreferencesBehaviorTab::AddItem(const QString &text,
@@ -22,6 +22,7 @@
#ifndef PREFERENCESBEHAVIORTAB_H
#define PREFERENCESBEHAVIORTAB_H
#include <QComboBox>
#include <QTreeWidget>
#include "dialog/configbase/configdialogbase.h"
@@ -49,6 +50,8 @@ private:
QMap<QTreeWidgetItem *, QString> config_map_;
QComboBox *graphics_backend_combobox_;
QTreeWidget *behavior_tree_;
};
+13 -1
View File
@@ -41,6 +41,7 @@ extern "C" {
#include <QIcon>
#include <QSurfaceFormat>
#include "config/config.h"
#include "core.h"
#include "common/commandlineparser.h"
#include "common/debug.h"
@@ -300,7 +301,8 @@ int main(int argc, char *argv[])
startup_params.set_startup_project(project_argument->GetSetting());
// Set OpenGL display profile
// Set OpenGL display profile. Oak's render pipeline still uses OpenGL
// internally even when Vulkan is requested as the Qt graphics backend.
QSurfaceFormat format;
// Tries to cover all bases. If drivers don't support 3.2, they should fallback to the closest
@@ -338,6 +340,16 @@ int main(int argc, char *argv[])
a.reset(new QCoreApplication(argc, argv));
}
olive::Config::Load();
const QString graphics_backend =
olive::Config::Current()[QStringLiteral("GraphicsBackend")]
.toString()
.toLower();
qputenv("QSG_RHI_BACKEND",
graphics_backend == QStringLiteral("vulkan")
? QByteArrayLiteral("vulkan")
: QByteArrayLiteral("opengl"));
if (auto *gui_app = qobject_cast<QGuiApplication *>(a.get())) {
gui_app->setWindowIcon(QIcon(QStringLiteral(":/graphics/oak-logo.png")));
}
+42
View File
@@ -40,11 +40,53 @@ namespace olive
RenderManager *RenderManager::instance_ = nullptr;
const rational RenderManager::kDryRunInterval = rational(10);
RenderManager::Backend RenderManager::BackendFromString(const QString &backend)
{
const QString lower = backend.toLower();
if (lower == QStringLiteral("vulkan")) {
return kVulkan;
}
if (lower == QStringLiteral("multiprocess")) {
return kMultiProcess;
}
if (lower == QStringLiteral("dummy")) {
return kDummy;
}
return kOpenGL;
}
QString RenderManager::BackendToString(Backend backend)
{
switch (backend) {
case kOpenGL:
return QStringLiteral("opengl");
case kVulkan:
return QStringLiteral("vulkan");
case kMultiProcess:
return QStringLiteral("multiprocess");
case kDummy:
return QStringLiteral("dummy");
}
return QStringLiteral("opengl");
}
RenderManager::RenderManager(QObject *parent)
: backend_(kOpenGL)
, requested_backend_(BackendFromString(
OLIVE_CONFIG("GraphicsBackend").toString()))
, aggressive_gc_(0)
, worker_pool_(nullptr)
{
if (requested_backend_ == kVulkan) {
qWarning()
<< "Vulkan graphics backend was requested, but the current render "
"pipeline still uses OpenGL. Falling back to OpenGL renderer.";
}
if (backend_ == kOpenGL) {
context_ = new OpenGLRenderer();
decoder_cache_ = new DecoderCache();
+12
View File
@@ -78,6 +78,9 @@ public:
/// Graphics acceleration provided by OpenGL
kOpenGL,
/// Vulkan requested by the user. Falls back to OpenGL until VulkanRenderer is implemented.
kVulkan,
/// Video frames are rendered by an external olive-render-worker process.
kMultiProcess,
@@ -201,6 +204,14 @@ public:
return backend_;
}
Backend requested_backend() const
{
return requested_backend_;
}
static Backend BackendFromString(const QString &backend);
static QString BackendToString(Backend backend);
PreviewAutoCacher *GetCacher() const
{
return auto_cacher_;
@@ -228,6 +239,7 @@ private:
Renderer *context_;
Backend backend_;
Backend requested_backend_;
DecoderCache *decoder_cache_;
@@ -18,8 +18,10 @@
|:--|:--|:--|
| Linux + Mesa/AMD 或 Intel | 软件导出、代理、示波器、音频同步 | 当前开发主环境优先 |
| Linux + NVIDIA | NVENC、代理、4K/8K 预览 | 需要 NVIDIA 驱动和 ffmpeg 编码器支持 |
| Linux + Vulkan 驱动 | 图形后端选择、Vulkan 请求、OpenGL 回退 | Vulkan 当前为实验入口,需确认不会破坏 OpenGL 渲染 |
| macOS Apple Silicon | VideoToolbox、ColorSync/显示路径、代理 | 重点看硬件导出和 UI 响应 |
| Windows + NVIDIA/Intel | NVENC/QSV 可用性、路径编码、文件管理器 reveal | 重点看中文路径和空格路径 |
| Windows + Vulkan Runtime | 图形后端选择、Vulkan 请求、驱动缺失回退 | 重点看设置持久化和启动稳定性 |
## 测试素材准备
@@ -357,6 +359,58 @@
通过标准:内存没有持续不可控增长;播放停止后应用仍可操作和保存。
## 10. 图形后端选择测试
当前版本允许用户在 Preferences 中选择 OpenGL 或 Vulkan。注意:Vulkan 入口目前是实验性图形 API 请求和后续 VulkanRenderer 的接入点;时间线/viewer 渲染仍应安全回退到现有 OpenGL renderer。因此测试重点是“用户可选择、设置可持久化、Vulkan 请求不破坏现有渲染、失败可回退”。
### 10.1 默认 OpenGL 后端
1. 删除或备份现有用户配置。
2. 启动 Oak。
3. 打开 Preferences > Behavior > Rendering。
4. 检查 Graphics Backend 的默认值。
5. 导入 `color_chart.mov` 并播放。
通过标准:默认值为 OpenGL;viewer、Scope、调色和播放行为与原 OpenGL 路径一致。
### 10.2 切换到 Vulkan 并重启
1. 在 Preferences > Behavior > Rendering 中选择 `Vulkan (experimental)`
2. 确认设置保存。
3. 关闭 Oak 并重新启动。
4. 再次打开 Preferences,确认仍显示 Vulkan。
5. 导入并播放 `4k_camera_a.mov`
通过标准:Vulkan 选择可持久化;重启后应用不崩溃;当前未完成 VulkanRenderer 时应明确回退 OpenGL 渲染,播放仍可用。
### 10.3 Vulkan 驱动缺失或不可用
1. 在没有 Vulkan Runtime 或驱动不可用的机器上选择 Vulkan。
2. 重启 Oak。
3. 打开项目并播放一段视频。
4. 检查日志或控制台输出。
通过标准:应用可以启动;日志应说明 Vulkan 请求不可完全满足或当前回退 OpenGL;用户能回到 Preferences 改回 OpenGL。
### 10.4 从 Vulkan 切回 OpenGL
1. 在 Vulkan 已选中状态下打开 Preferences。
2. 将 Graphics Backend 改为 OpenGL。
3. 保存、退出并重启。
4. 播放同一项目并执行一次软件导出。
通过标准:重启后显示 OpenGL;播放和导出正常;不会保留错误的 Vulkan 状态。
### 10.5 代理、Scope 与调色组合
1. 选择 Vulkan 并重启。
2.`8k_or_heavy_camera.mov` 生成并启用代理。
3. 添加 LUT 和三向调色。
4. 打开 Waveform、Vectorscope、Histogram 依次观察。
5. 切回 OpenGL 后重复同一段播放。
通过标准:Vulkan 请求状态下代理、Scope、调色不崩溃;切回 OpenGL 后项目状态一致;两种选择下导出默认仍使用原片。
## 缺陷记录模板
每个失败项记录以下信息:
@@ -373,6 +427,7 @@
- 预检、LUT、三向色轮、三类 Scope、波形同步、BWF 时间码、音频表、代理生成/启用/删除、软件导出全部通过。
- 至少一个硬件编码环境通过 NVENC 或 VideoToolbox。
- OpenGL/Vulkan 图形后端选择、持久化和 Vulkan 请求回退测试通过。
- 批量队列至少通过多任务执行和取消测试。
- 组合回归测试中的完整剪辑链路通过。
- 所有失败项有明确 issue 或文档化限制,不存在“无提示崩溃”级别问题。
+19
View File
@@ -1,6 +1,7 @@
#include <gtest/gtest.h>
#include "config/config.h"
#include "render/rendermanager.h"
TEST(Config, DefaultsPresent)
{
@@ -10,6 +11,8 @@ TEST(Config, DefaultsPresent)
EXPECT_TRUE(cfg[QStringLiteral("Style")].isValid());
EXPECT_TRUE(cfg[QStringLiteral("TimecodeDisplay")].isValid());
EXPECT_TRUE(cfg[QStringLiteral("DefaultStillLength")].isValid());
EXPECT_EQ(cfg[QStringLiteral("GraphicsBackend")].toString(),
QStringLiteral("opengl"));
}
TEST(Config, SetAndGetValues)
@@ -18,3 +21,19 @@ TEST(Config, SetAndGetValues)
cfg[QStringLiteral("UnitTestValue")] = 42;
EXPECT_EQ(cfg[QStringLiteral("UnitTestValue")].toInt(), 42);
}
TEST(Config, GraphicsBackendStringConversion)
{
EXPECT_EQ(olive::RenderManager::BackendFromString(QStringLiteral("opengl")),
olive::RenderManager::kOpenGL);
EXPECT_EQ(olive::RenderManager::BackendFromString(QStringLiteral("vulkan")),
olive::RenderManager::kVulkan);
EXPECT_EQ(olive::RenderManager::BackendToString(
olive::RenderManager::kOpenGL),
QStringLiteral("opengl"));
EXPECT_EQ(olive::RenderManager::BackendToString(
olive::RenderManager::kVulkan),
QStringLiteral("vulkan"));
EXPECT_EQ(olive::RenderManager::BackendFromString(QStringLiteral("bad")),
olive::RenderManager::kOpenGL);
}