render/app: display bit depth option (10-bit default, 8-bit optional)
Preferences gains a display-bit-depth combo stored in the config store; at startup the app forwards it to gpui_wgpu via OAK_DISPLAY_BIT_DEPTH, which prefers Rgb10a2Unorm for 10-bit presentation. Takes effect after restart (noted in the dialog); i18n in all eight packs.
This commit is contained in:
@@ -285,6 +285,10 @@
|
||||
"preferences.backend": "Renderer-Backend"
|
||||
"preferences.hwdecode.enable": "Hardwarebeschleunigte Videodekodierung (VideoToolbox / VA-API / NVDEC / D3D11VA)"
|
||||
"preferences.backend.placeholder": "Backend auswählen…"
|
||||
"preferences.bit_depth.label": "Anzeige-Bittiefe"
|
||||
"preferences.bit_depth.10bit": "10 Bit"
|
||||
"preferences.bit_depth.8bit": "8 Bit"
|
||||
"preferences.bit_depth.restart_hint": "Eine Änderung wird nach einem Neustart wirksam."
|
||||
"preferences.language": "Sprache"
|
||||
"preferences.language.placeholder": "Sprache auswählen…"
|
||||
"preferences.theme": "Design"
|
||||
|
||||
@@ -285,6 +285,10 @@
|
||||
"preferences.backend": "Renderer backend"
|
||||
"preferences.hwdecode.enable": "Hardware-accelerated video decoding (VideoToolbox / VA-API / NVDEC / D3D11VA)"
|
||||
"preferences.backend.placeholder": "Select a backend…"
|
||||
"preferences.bit_depth.label": "Display bit depth"
|
||||
"preferences.bit_depth.10bit": "10-bit"
|
||||
"preferences.bit_depth.8bit": "8-bit"
|
||||
"preferences.bit_depth.restart_hint": "A change takes effect after a restart."
|
||||
"preferences.language": "Language"
|
||||
"preferences.language.placeholder": "Select a language…"
|
||||
"preferences.theme": "Theme"
|
||||
|
||||
@@ -285,6 +285,10 @@
|
||||
"preferences.backend": "Motor de renderizado"
|
||||
"preferences.hwdecode.enable": "Decodificación de vídeo acelerada por hardware (VideoToolbox / VA-API / NVDEC / D3D11VA)"
|
||||
"preferences.backend.placeholder": "Seleccionar un motor…"
|
||||
"preferences.bit_depth.label": "Profundidad de color de pantalla"
|
||||
"preferences.bit_depth.10bit": "10 bits"
|
||||
"preferences.bit_depth.8bit": "8 bits"
|
||||
"preferences.bit_depth.restart_hint": "El cambio surte efecto tras reiniciar."
|
||||
"preferences.language": "Idioma"
|
||||
"preferences.language.placeholder": "Seleccionar un idioma…"
|
||||
"preferences.theme": "Tema"
|
||||
|
||||
@@ -285,6 +285,10 @@
|
||||
"preferences.backend": "Moteur de rendu"
|
||||
"preferences.hwdecode.enable": "Décodage vidéo accéléré par le matériel (VideoToolbox / VA-API / NVDEC / D3D11VA)"
|
||||
"preferences.backend.placeholder": "Sélectionner un moteur…"
|
||||
"preferences.bit_depth.label": "Profondeur de couleur de l'affichage"
|
||||
"preferences.bit_depth.10bit": "10 bits"
|
||||
"preferences.bit_depth.8bit": "8 bits"
|
||||
"preferences.bit_depth.restart_hint": "Le changement prend effet après un redémarrage."
|
||||
"preferences.language": "Langue"
|
||||
"preferences.language.placeholder": "Sélectionner une langue…"
|
||||
"preferences.theme": "Thème"
|
||||
|
||||
@@ -285,6 +285,10 @@
|
||||
"preferences.backend": "レンダラーバックエンド"
|
||||
"preferences.hwdecode.enable": "ハードウェアアクセラレーションによるビデオデコード(VideoToolbox / VA-API / NVDEC / D3D11VA)"
|
||||
"preferences.backend.placeholder": "バックエンドを選択…"
|
||||
"preferences.bit_depth.label": "表示ビット深度"
|
||||
"preferences.bit_depth.10bit": "10 ビット"
|
||||
"preferences.bit_depth.8bit": "8 ビット"
|
||||
"preferences.bit_depth.restart_hint": "変更は再起動後に反映されます。"
|
||||
"preferences.language": "言語"
|
||||
"preferences.language.placeholder": "言語を選択…"
|
||||
"preferences.theme": "テーマ"
|
||||
|
||||
@@ -285,6 +285,10 @@
|
||||
"preferences.backend": "Backend do renderizador"
|
||||
"preferences.hwdecode.enable": "Decodificação de vídeo acelerada por hardware (VideoToolbox / VA-API / NVDEC / D3D11VA)"
|
||||
"preferences.backend.placeholder": "Selecionar um backend…"
|
||||
"preferences.bit_depth.label": "Profundidade de bits da tela"
|
||||
"preferences.bit_depth.10bit": "10 bits"
|
||||
"preferences.bit_depth.8bit": "8 bits"
|
||||
"preferences.bit_depth.restart_hint": "A alteração entra em vigor após reiniciar."
|
||||
"preferences.language": "Idioma"
|
||||
"preferences.language.placeholder": "Selecionar um idioma…"
|
||||
"preferences.theme": "Tema"
|
||||
|
||||
@@ -285,6 +285,10 @@
|
||||
"preferences.backend": "Бэкенд рендеринга"
|
||||
"preferences.hwdecode.enable": "Аппаратное декодирование видео (VideoToolbox / VA-API / NVDEC / D3D11VA)"
|
||||
"preferences.backend.placeholder": "Выберите бэкенд…"
|
||||
"preferences.bit_depth.label": "Глубина цвета экрана"
|
||||
"preferences.bit_depth.10bit": "10 бит"
|
||||
"preferences.bit_depth.8bit": "8 бит"
|
||||
"preferences.bit_depth.restart_hint": "Изменение вступит в силу после перезапуска."
|
||||
"preferences.language": "Язык"
|
||||
"preferences.language.placeholder": "Выберите язык…"
|
||||
"preferences.theme": "Тема"
|
||||
|
||||
@@ -285,6 +285,10 @@
|
||||
"preferences.backend": "渲染后端"
|
||||
"preferences.hwdecode.enable": "硬件加速视频解码(VideoToolbox / VA-API / NVDEC / D3D11VA)"
|
||||
"preferences.backend.placeholder": "选择一个后端…"
|
||||
"preferences.bit_depth.label": "渲染色深"
|
||||
"preferences.bit_depth.10bit": "10 位"
|
||||
"preferences.bit_depth.8bit": "8 位"
|
||||
"preferences.bit_depth.restart_hint": "更改将在重启后生效。"
|
||||
"preferences.language": "语言"
|
||||
"preferences.language.placeholder": "选择语言…"
|
||||
"preferences.theme": "主题"
|
||||
|
||||
@@ -3010,6 +3010,18 @@ fn run_with<E: AppEngine>(args: AppArgs) {
|
||||
// SAFETY: single-threaded startup, before any window exists.
|
||||
unsafe { std::env::set_var("OAK_MACOS_LAYER_COLORSPACE", "display") };
|
||||
}
|
||||
// Display bit depth: the wgpu window layer must pick the swapchain
|
||||
// format before the surface is created, so surface the persisted
|
||||
// choice the same way the colorspace tag is passed to the platform
|
||||
// layer above. Read by gpui_wgpu as OAK_DISPLAY_BIT_DEPTH ("8" opts
|
||||
// into the 8-bit pair; anything else requests 10-bit).
|
||||
let bit_depth = oak_render::backend::DisplayBitDepth::from_config_string(
|
||||
&crate::oakui::real::config_get_string(
|
||||
oak_render::backend::CONFIG_KEY_DISPLAY_BIT_DEPTH,
|
||||
),
|
||||
);
|
||||
// SAFETY: single-threaded startup, before any window exists.
|
||||
unsafe { std::env::set_var("OAK_DISPLAY_BIT_DEPTH", bit_depth.to_config_string()) };
|
||||
cx.init_colors();
|
||||
let bounds = Bounds::centered(None, size(px(1600.0), px(900.0)), cx);
|
||||
let initial = initial.clone();
|
||||
|
||||
@@ -54,6 +54,10 @@ use crate::oakui::real::{
|
||||
DEFAULT_PREVIEW_WINDOW_FORWARD, DEFAULT_SNAPSHOT_INTERVAL_SEC, DEFAULT_TRANSITION_SEC,
|
||||
EXPORT_FORMAT_MP4,
|
||||
};
|
||||
// The `DisplayBitDepth` config key lives with the format mapping it
|
||||
// drives (oak-render's backend); the preferences dropdown and the
|
||||
// window-layer consumer share the same key.
|
||||
use oak_render::backend::CONFIG_KEY_DISPLAY_BIT_DEPTH;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Preferences
|
||||
@@ -100,6 +104,10 @@ impl gpui::EventEmitter<PreferencesEvent> for PreferencesContent {}
|
||||
/// exit).
|
||||
pub struct PreferencesContent {
|
||||
backend: Entity<ComboBox>,
|
||||
/// The on-screen display bit depth (10-bit default, 8-bit fallback).
|
||||
/// The swapchain format is fixed at surface creation, so a change
|
||||
/// takes effect after a restart.
|
||||
display_bit_depth: Entity<ComboBox>,
|
||||
language: Entity<ComboBox>,
|
||||
theme: Entity<ComboBox>,
|
||||
cache_dir: Entity<PathField>,
|
||||
@@ -162,6 +170,37 @@ impl PreferencesContent {
|
||||
combo.set_selected(Some(backend_selected), cx)
|
||||
});
|
||||
|
||||
// --- 渲染 Rendering: on-screen display bit depth ---------------------
|
||||
// 10-bit is the default; 8-bit is the compatibility fallback. The
|
||||
// swapchain format is chosen once at window/surface creation, so a
|
||||
// change takes effect after a restart (see oak-render's
|
||||
// `DisplayBitDepth::present_formats`).
|
||||
let display_bit_depth = cx.new(|cx| {
|
||||
let options = vec![
|
||||
ComboBoxOption::new(0, i18n::tr("preferences.bit_depth.10bit")),
|
||||
ComboBoxOption::new(1, i18n::tr("preferences.bit_depth.8bit")),
|
||||
];
|
||||
ComboBox::new(12, options, window, cx)
|
||||
});
|
||||
cx.subscribe(&display_bit_depth, |_this, _combo, event: &ComboBoxEvent, cx| {
|
||||
if let ComboBoxEvent::Selected { value, .. } = event {
|
||||
let depth = if *value == 1 { "8" } else { "10" };
|
||||
config_set_string(CONFIG_KEY_DISPLAY_BIT_DEPTH, depth);
|
||||
println!("[preferences] display bit depth → {depth}");
|
||||
}
|
||||
let _ = cx;
|
||||
})
|
||||
.detach();
|
||||
let bit_depth_selected =
|
||||
if config_get_string(CONFIG_KEY_DISPLAY_BIT_DEPTH) == "8" {
|
||||
1
|
||||
} else {
|
||||
0
|
||||
};
|
||||
display_bit_depth.update(cx, |combo, cx| {
|
||||
combo.set_selected(Some(bit_depth_selected), cx)
|
||||
});
|
||||
|
||||
// --- 常规 General: language + theme --------------------------------
|
||||
// Data-driven: one option per discovered language pack, labelled
|
||||
// with the pack's own `language.name` — a newly dropped-in pack
|
||||
@@ -494,6 +533,7 @@ impl PreferencesContent {
|
||||
|
||||
Self {
|
||||
backend,
|
||||
display_bit_depth,
|
||||
language,
|
||||
theme,
|
||||
cache_dir,
|
||||
@@ -855,6 +895,19 @@ impl Render for PreferencesContent {
|
||||
i18n::tr("preferences.audio.input").into(),
|
||||
self.audio_input.clone(),
|
||||
))
|
||||
// 渲染 Rendering: display bit depth (kept at the bottom of the list
|
||||
// so the rows above stay inside the card's scroll viewport).
|
||||
.child(form_row(
|
||||
&colors,
|
||||
i18n::tr("preferences.bit_depth.label").into(),
|
||||
self.display_bit_depth.clone(),
|
||||
))
|
||||
.child(
|
||||
div()
|
||||
.text_color(colors.disabled)
|
||||
.text_xs()
|
||||
.child(i18n::tr("preferences.bit_depth.restart_hint")),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.text_color(colors.disabled)
|
||||
|
||||
@@ -144,6 +144,74 @@ impl BackendKind {
|
||||
}
|
||||
}
|
||||
|
||||
/// The config-store key for the on-screen display bit depth ("10" or
|
||||
/// "8"). The window swapchain format is chosen once at surface creation,
|
||||
/// so a change takes effect after a restart.
|
||||
pub const CONFIG_KEY_DISPLAY_BIT_DEPTH: &str = "DisplayBitDepth";
|
||||
|
||||
/// On-screen presentation bit depth, persisted under the
|
||||
/// `DisplayBitDepth` config key. The choice is user-visible: the
|
||||
/// preferences dialog exposes a 10-bit/8-bit dropdown, and it decides
|
||||
/// the formats the window layer presents with (see
|
||||
/// [`DisplayBitDepth::present_formats`]).
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum DisplayBitDepth {
|
||||
/// 8-bit per channel (Bgra8/Rgba8 swapchain).
|
||||
Bit8,
|
||||
/// 10-bit per channel (RGB10A2 — the default; RGBA16F for HDR).
|
||||
Bit10,
|
||||
}
|
||||
|
||||
impl DisplayBitDepth {
|
||||
/// Parse the settings string; unknown values yield the default 10-bit.
|
||||
pub fn from_config_string(s: &str) -> DisplayBitDepth {
|
||||
match s.trim() {
|
||||
"8" => DisplayBitDepth::Bit8,
|
||||
_ => DisplayBitDepth::Bit10,
|
||||
}
|
||||
}
|
||||
|
||||
/// The config-store value persisted for this depth.
|
||||
pub fn to_config_string(self) -> &'static str {
|
||||
match self {
|
||||
DisplayBitDepth::Bit8 => "8",
|
||||
DisplayBitDepth::Bit10 => "10",
|
||||
}
|
||||
}
|
||||
|
||||
/// Read the user's persisted choice through the oakcommon config C ABI
|
||||
/// ("DisplayBitDepth").
|
||||
pub fn from_user_config() -> DisplayBitDepth {
|
||||
let configured =
|
||||
crate::commonutil::config_get_string(None, CONFIG_KEY_DISPLAY_BIT_DEPTH)
|
||||
.unwrap_or_default();
|
||||
DisplayBitDepth::from_config_string(&configured)
|
||||
}
|
||||
|
||||
/// The wgpu surface formats the window layer should present with, in
|
||||
/// preference order (the platform picks the first the surface
|
||||
/// supports). 10-bit prefers RGB10A2 with an RGBA16F (HDR) fallback;
|
||||
/// 8-bit mirrors the engine's current default preference list.
|
||||
///
|
||||
/// The actual swapchain is configured by the window layer
|
||||
/// (gpui_wgpu's surface setup intersects these with the surface
|
||||
/// capabilities); this crate is headless and never creates a surface
|
||||
/// itself, so the mapping is the format-selection contract the window
|
||||
/// layer consumes at surface creation.
|
||||
pub fn present_formats(self) -> &'static [wgpu::TextureFormat] {
|
||||
match self {
|
||||
DisplayBitDepth::Bit10 => &[
|
||||
wgpu::TextureFormat::Rgb10a2Unorm,
|
||||
wgpu::TextureFormat::Rgba16Float,
|
||||
],
|
||||
DisplayBitDepth::Bit8 => &[
|
||||
wgpu::TextureFormat::Bgra8Unorm,
|
||||
wgpu::TextureFormat::Rgba8Unorm,
|
||||
],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A GPU-resident texture in the context registry.
|
||||
#[derive(Clone)]
|
||||
struct GpuTexture {
|
||||
@@ -1312,6 +1380,43 @@ mod tests {
|
||||
std::env::remove_var("OAK_RENDER_BACKEND");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn display_bit_depth_string_roundtrip() {
|
||||
assert_eq!(DisplayBitDepth::from_config_string("8"), DisplayBitDepth::Bit8);
|
||||
assert_eq!(
|
||||
DisplayBitDepth::from_config_string("10"),
|
||||
DisplayBitDepth::Bit10
|
||||
);
|
||||
// Unknown / missing → the 10-bit default.
|
||||
assert_eq!(DisplayBitDepth::from_config_string(""), DisplayBitDepth::Bit10);
|
||||
assert_eq!(
|
||||
DisplayBitDepth::from_config_string("bogus"),
|
||||
DisplayBitDepth::Bit10
|
||||
);
|
||||
assert_eq!(
|
||||
DisplayBitDepth::from_config_string(DisplayBitDepth::Bit8.to_config_string()),
|
||||
DisplayBitDepth::Bit8
|
||||
);
|
||||
assert_eq!(
|
||||
DisplayBitDepth::from_config_string(DisplayBitDepth::Bit10.to_config_string()),
|
||||
DisplayBitDepth::Bit10
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn display_bit_depth_present_formats() {
|
||||
// 10-bit → the 10-bit (RGB10A2) swapchain format, HDR float fallback.
|
||||
assert_eq!(
|
||||
DisplayBitDepth::Bit10.present_formats(),
|
||||
&[wgpu::TextureFormat::Rgb10a2Unorm, wgpu::TextureFormat::Rgba16Float]
|
||||
);
|
||||
// 8-bit → the engine's current default preference list.
|
||||
assert_eq!(
|
||||
DisplayBitDepth::Bit8.present_formats(),
|
||||
&[wgpu::TextureFormat::Bgra8Unorm, wgpu::TextureFormat::Rgba8Unorm]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cpu_backend_has_no_adapter() {
|
||||
assert!(GpuContext::create(BackendKind::Cpu).is_none());
|
||||
|
||||
+1
-1
Submodule gpui updated: 0b4329e50e...4022d1db5e
Reference in New Issue
Block a user