detached f0ca3ee

docs(cef): clarify adaptive osr frame rate

commit
f0ca3ee219bf9ca8764150a16499a34b363c43e0
author
brice · · 2026-05-20 06:01 UTC
parent
45b07b3
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
4 files · +29 -10
M README.md +3 -1
@@ -101,7 +101,9 @@ CEF_DIR=/custom/path dumber browse
101101
102102 Then set `engine.type = "cef"` in your config.
103103
104-CEF uses Dumber's GPU-first Wayland render stack by default: GDK DMABUF presentation with ANGLE/GSK Vulkan. For driver compatibility, switch to the EGL/OpenGL stack with `engine.cef.render_stack = "egl"`; the default is `"vulkan"`. CEF OSR frame rate adapts to the active Wayland monitor refresh rate by default, capped by `engine.cef.windowless_frame_rate_max = 240`; set `engine.cef.windowless_frame_rate` to a positive value for an explicit fixed cap.
104+CEF uses Dumber's GPU-first Wayland render stack by default: GDK DMABUF presentation with ANGLE/GSK Vulkan. For driver compatibility, switch to the EGL/OpenGL stack with `engine.cef.render_stack = "egl"`; the default is `"vulkan"`.
105+
106+CEF OSR frame rate adapts to the active Wayland monitor refresh rate by default when `engine.cef.adaptive_windowless_frame_rate = true`. Adaptive mode is capped by `engine.cef.windowless_frame_rate_max = 240`. To use a fixed frame rate instead, set `engine.cef.windowless_frame_rate` to a positive value.
105107
106108 ## Keyboard-Driven
107109
@@ -101,7 +101,9 @@ CEF_DIR=/custom/path dumber browse
101 101
102 Then set `engine.type = "cef"` in your config.102 Then set `engine.type = "cef"` in your config.
103 103
104-CEF uses Dumber's GPU-first Wayland render stack by default: GDK DMABUF presentation with ANGLE/GSK Vulkan. For driver compatibility, switch to the EGL/OpenGL stack with `engine.cef.render_stack = "egl"`; the default is `"vulkan"`. CEF OSR frame rate adapts to the active Wayland monitor refresh rate by default, capped by `engine.cef.windowless_frame_rate_max = 240`; set `engine.cef.windowless_frame_rate` to a positive value for an explicit fixed cap.104+CEF uses Dumber's GPU-first Wayland render stack by default: GDK DMABUF presentation with ANGLE/GSK Vulkan. For driver compatibility, switch to the EGL/OpenGL stack with `engine.cef.render_stack = "egl"`; the default is `"vulkan"`.
105+
106+CEF OSR frame rate adapts to the active Wayland monitor refresh rate by default when `engine.cef.adaptive_windowless_frame_rate = true`. Adaptive mode is capped by `engine.cef.windowless_frame_rate_max = 240`. To use a fixed frame rate instead, set `engine.cef.windowless_frame_rate` to a positive value.
105 107
106 ## Keyboard-Driven108 ## Keyboard-Driven
107 109
M docs/config/index.md +3 -3
@@ -190,9 +190,9 @@ border = "#363636"
190190 | `rendering.sample_memory` | bool | `false` | - | Enable WebKit memory sampling (`WEBKIT_SAMPLE_MEMORY`) |
191191 | `rendering.debug_frames` | bool | `false` | - | Enable GTK frame timing debug (`GDK_DEBUG=frames`) |
192192 | `engine.cef.render_stack` | string | `"vulkan"` | `vulkan`, `egl` | CEF GPU render stack |
193-| `engine.cef.adaptive_windowless_frame_rate` | bool | `true` | - | Adapt CEF OSR FPS to the active Wayland monitor when `windowless_frame_rate = 0` |
194-| `engine.cef.windowless_frame_rate` | int32 | `0` | >= 0 | Explicit CEF OSR FPS cap; 0 lets adaptive mode choose |
195-| `engine.cef.windowless_frame_rate_max` | int32 | `240` | > 0 | Hard cap for adaptive CEF OSR FPS |
193+| `engine.cef.adaptive_windowless_frame_rate` | bool | `true` | - | Enable adaptive CEF OSR FPS polling (only applies when `windowless_frame_rate = 0`) |
194+| `engine.cef.windowless_frame_rate` | int32 | `0` | >= 0 | Explicit CEF OSR FPS cap; 0 uses adaptive mode if enabled, otherwise CEF default |
195+| `engine.cef.windowless_frame_rate_max` | int32 | `240` | >= 0 | Hard cap for adaptive CEF OSR FPS; 0 uses the built-in default cap |
196196 | `default_ui_scale` | float | `1.0` | > 0 | GTK widget UI scale (1.0=100%, 2.0=200%) |
197197 | `default_webpage_zoom` | float | `1.2` | > 0 | Default page zoom (1.0=100%, 1.2=120%) |
198198
@@ -190,9 +190,9 @@ border = "#363636"
190 | `rendering.sample_memory` | bool | `false` | - | Enable WebKit memory sampling (`WEBKIT_SAMPLE_MEMORY`) |190 | `rendering.sample_memory` | bool | `false` | - | Enable WebKit memory sampling (`WEBKIT_SAMPLE_MEMORY`) |
191 | `rendering.debug_frames` | bool | `false` | - | Enable GTK frame timing debug (`GDK_DEBUG=frames`) |191 | `rendering.debug_frames` | bool | `false` | - | Enable GTK frame timing debug (`GDK_DEBUG=frames`) |
192 | `engine.cef.render_stack` | string | `"vulkan"` | `vulkan`, `egl` | CEF GPU render stack |192 | `engine.cef.render_stack` | string | `"vulkan"` | `vulkan`, `egl` | CEF GPU render stack |
193-| `engine.cef.adaptive_windowless_frame_rate` | bool | `true` | - | Adapt CEF OSR FPS to the active Wayland monitor when `windowless_frame_rate = 0` |193+| `engine.cef.adaptive_windowless_frame_rate` | bool | `true` | - | Enable adaptive CEF OSR FPS polling (only applies when `windowless_frame_rate = 0`) |
194-| `engine.cef.windowless_frame_rate` | int32 | `0` | >= 0 | Explicit CEF OSR FPS cap; 0 lets adaptive mode choose |194+| `engine.cef.windowless_frame_rate` | int32 | `0` | >= 0 | Explicit CEF OSR FPS cap; 0 uses adaptive mode if enabled, otherwise CEF default |
195-| `engine.cef.windowless_frame_rate_max` | int32 | `240` | > 0 | Hard cap for adaptive CEF OSR FPS |195+| `engine.cef.windowless_frame_rate_max` | int32 | `240` | >= 0 | Hard cap for adaptive CEF OSR FPS; 0 uses the built-in default cap |
196 | `default_ui_scale` | float | `1.0` | > 0 | GTK widget UI scale (1.0=100%, 2.0=200%) |196 | `default_ui_scale` | float | `1.0` | > 0 | GTK widget UI scale (1.0=100%, 2.0=200%) |
197 | `default_webpage_zoom` | float | `1.2` | > 0 | Default page zoom (1.0=100%, 1.2=120%) |197 | `default_webpage_zoom` | float | `1.2` | > 0 | Default page zoom (1.0=100%, 1.2=120%) |
198 198
M internal/infrastructure/cef/adaptive_frame_rate_test.go +21 -4
@@ -26,10 +26,27 @@ func TestAdaptiveFrameRateForRefresh(t *testing.T) {
2626 }
2727
2828 func TestIsWaylandDisplayName(t *testing.T) {
29- if !isWaylandDisplayName("Wayland") || !isWaylandDisplayName("wayland-0") {
30- t.Fatal("expected wayland display names to match")
29+ tests := []struct {
30+ name string
31+ input string
32+ want bool
33+ }{
34+ {name: "exact lowercase", input: "wayland", want: true},
35+ {name: "exact uppercase", input: "WAYLAND", want: true},
36+ {name: "mixed case", input: "WayLand", want: true},
37+ {name: "numeric suffix", input: "wayland-1", want: true},
38+ {name: "punctuation suffix", input: "wayland:0", want: true},
39+ {name: "leading whitespace", input: " wayland ", want: true},
40+ {name: "substring", input: "mywayland-display", want: true},
41+ {name: "x11", input: "x11", want: false},
42+ {name: "empty", input: "", want: false},
43+ {name: "whitespace", input: " ", want: false},
3144 }
32- if isWaylandDisplayName("x11") || isWaylandDisplayName("") {
33- t.Fatal("unexpected non-wayland display match")
45+ for _, tt := range tests {
46+ t.Run(tt.name, func(t *testing.T) {
47+ if got := isWaylandDisplayName(tt.input); got != tt.want {
48+ t.Fatalf("isWaylandDisplayName(%q) = %v, want %v", tt.input, got, tt.want)
49+ }
50+ })
3451 }
3552 }
@@ -26,10 +26,27 @@ func TestAdaptiveFrameRateForRefresh(t *testing.T) {
26 }26 }
27 27
28 func TestIsWaylandDisplayName(t *testing.T) {28 func TestIsWaylandDisplayName(t *testing.T) {
29- if !isWaylandDisplayName("Wayland") || !isWaylandDisplayName("wayland-0") {29+ tests := []struct {
30- t.Fatal("expected wayland display names to match")30+ name string
31+ input string
32+ want bool
33+ }{
34+ {name: "exact lowercase", input: "wayland", want: true},
35+ {name: "exact uppercase", input: "WAYLAND", want: true},
36+ {name: "mixed case", input: "WayLand", want: true},
37+ {name: "numeric suffix", input: "wayland-1", want: true},
38+ {name: "punctuation suffix", input: "wayland:0", want: true},
39+ {name: "leading whitespace", input: " wayland ", want: true},
40+ {name: "substring", input: "mywayland-display", want: true},
41+ {name: "x11", input: "x11", want: false},
42+ {name: "empty", input: "", want: false},
43+ {name: "whitespace", input: " ", want: false},
31 }44 }
32- if isWaylandDisplayName("x11") || isWaylandDisplayName("") {45+ for _, tt := range tests {
33- t.Fatal("unexpected non-wayland display match")46+ t.Run(tt.name, func(t *testing.T) {
47+ if got := isWaylandDisplayName(tt.input); got != tt.want {
48+ t.Fatalf("isWaylandDisplayName(%q) = %v, want %v", tt.input, got, tt.want)
49+ }
50+ })
34 }51 }
35 }52 }
M internal/infrastructure/config/schema_provider.go +2 -2
@@ -958,8 +958,8 @@ func (*SchemaProvider) getPerformanceKeys(defaults *Config) []entity.ConfigKeyIn
958958 Key: "engine.cef.windowless_frame_rate_max",
959959 Type: "int32",
960960 Default: fmt.Sprintf("%d", defaults.Engine.CEF.CEFWindowlessFrameRateMax()),
961- Description: "Hard cap for adaptive CEF OSR frame rate",
962- Range: ">0",
961+ Description: "Hard cap for adaptive CEF OSR frame rate; 0 uses the built-in default cap",
962+ Range: ">=0",
963963 Section: SectionPerformance,
964964 },
965965 {
@@ -958,8 +958,8 @@ func (*SchemaProvider) getPerformanceKeys(defaults *Config) []entity.ConfigKeyIn
958 Key: "engine.cef.windowless_frame_rate_max",958 Key: "engine.cef.windowless_frame_rate_max",
959 Type: "int32",959 Type: "int32",
960 Default: fmt.Sprintf("%d", defaults.Engine.CEF.CEFWindowlessFrameRateMax()),960 Default: fmt.Sprintf("%d", defaults.Engine.CEF.CEFWindowlessFrameRateMax()),
961- Description: "Hard cap for adaptive CEF OSR frame rate",961+ Description: "Hard cap for adaptive CEF OSR frame rate; 0 uses the built-in default cap",
962- Range: ">0",962+ Range: ">=0",
963 Section: SectionPerformance,963 Section: SectionPerformance,
964 },964 },
965 {965 {

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 8

Tags 42

↑↓ moveenter openesc close