Repository navigation
Conversation
A mod or a plugin can now give the game's own screens a look of their own, chosen in Options › Interface › Interface theme (docs/THEMES.md). A theme is a folder with a theme.json (Themes/<id>/ of a content folder, or a plugin's own folder): - the palette: omsi_ui::theme's constants are now a Palette in force (theme::th(), set by themes::sync from the setting ui_theme), read by the widget kit, the pause menu, its settings windows and lists, the photo mode, the developer tools, the permission dialog, the headset's menu and the plugins' panels and notifications; colours a theme leaves out follow the ones they go with; - glass: Ui::surface draws a panel as frosted glass - the game's picture behind the pause menu, rendered small when the menu opens (and every 1.5 s while a LAN game goes on), blurred on the CPU, under the panel's colour, with a light edge and a sheen; the rail becomes a floating pane; - the loading screen: the theme's colours and logo, which can be the loading bar itself - its path id="progress" drawn along (icons::path_length, icons::progress_svg) or the logo filled from the left as far as the loading is; - the pause menu's logo and name. openOMSI's own look is unchanged and stays the default. Plugins see the themes with ui.themes and ui.theme and can switch with ui.set_theme (omsi.ui.version 6; the API docs regenerated).
…eaches them An element of an SVG loading logo with an id and data-at (0 to 1 along the progress) stays hidden until the loading is that far, then pops up from its middle with a little overshoot; with data-blink it flashes twice (a light), data-delay waits a moment after (icons::marks, icons::staged_svg, Stage::of). The loading screen times each mark by its own clock and draws a new picture of the logo only when a mark's stage changes (in twentieths of its size).
"Interface theme" and its description in app.yml, in the 29 languages the Interface page's other rows have, with a test that the row is drawn in the player's language.
|
Thanks @Luc-nbr. This now conflicts with main: the pause menu was reworked (rounder corners, |
isaacsa2
left a comment
There was a problem hiding this comment.
The theme format and the default-look fallback are a useful start. I can't approve 4782a89 yet.
I agree with turbo-devv's merge-main/API-version follow-up. Beyond that, the description still says the feature has not been tried in game, screenshots are pending and performance is unmeasured, although the PR is ready for review. Please add before/after captures of the default look and a themed pause/settings/loading screen, and fill the performance comparison on the same map and machine.
Please include an unpaused LAN session with the glass menu open: frame_glass_backdrop() calls render_to_image, reads the picture back and blurs it on the CPU every 1.5 seconds. Average FPS alone won't show the stalls that path may add; include frame-time spikes/p99 and memory, alongside the default theme. Also check switching back to openOMSI and restarting with the saved selection. I haven't measured a stall myself; this is the unvalidated execution path in the diff. Fresh platform CI is still needed after the conflicts are resolved.
… the glass on the GPU - The reworked pause menu (rounder corners, pause_hidden, the veil on category pages) takes the theme: its corners through theme::corner(r), which is r with openOMSI's look and follows a theme's radius; with no category chosen the glass theme dims the picture only a little behind the rail, a category page, a list or a settings window has the picture blurred under the background. - openOMSI-org#3277 took omsi.ui.version 6: the themes are 7 (code, docs/PLUGINS.md, the changelog), the API docs regenerated. - The glass's picture is no longer rendered a second time and read back: the frame the scene has just drawn is copied on the graphics card (SurfaceState::picture: the window's texture, or the sRGB stand-in, where it allows COPY_SRC) and blurred there in three small passes (Shell::capture_backdrop), every frame the menu is open. Nothing waits for the GPU and nothing runs on the CPU per pixel; the CPU blur and frame_glass_backdrop are gone.
…heme's wordmark on the rail Tried in the game (Berlin-Spandau, DX12): - The window is made before the renderer (SurfaceState::early), so its picture could not be copied from and the glass showed the picture unblurred: adopt() now asks for COPY_SRC once the renderer's adapter says the window allows it. - The glass's picture is taken when the menu opens, then at most 30 times a second while the game goes on behind it (Shell::glass_due), and not again while it is paused: with it taken every frame the glass cost 1.1 ms a frame in a LAN session, now 0.24 ms (p50; p99 and the slow frames as with openOMSI's own look). - What is clicked stays solid on the glass - the session's buttons (End the session with a hairline of the red), the driver's chip, the action cards on the home and category pages - so that it reads as a control; the glass is the rail's and the panels'. - A theme's logo that is a wordmark (1.6 times as wide as high or more) is drawn across the top of the rail in place of the icon and the name, openOMSI's version under it.
…view box does not start at 0 usvg puts the view box on a group under the root, so the box of a mark is the canvas's: its middle is taken back through the element's parent's transform (its own undone), and a stop of the Omsi-Hub logo popped in a little above its line. Tested with a moved view box: the middle in the SVG's units, and a mark half grown has its ink centred where the whole one has.
# Conflicts: # crates/omsi-app/src/settings.rs
|
Thanks both, it's updated. @turbo-devv: @isaacsa2: I've now run it in the game, and the description has before/after screenshots ( Running it in the game also turned up three bugs, now fixed: the window could not be copied from (no blur), the glass every frame cost 1.1 ms, and a loading logo with a moved view box popped its marks in slightly off their place. It merges cleanly now, so CI can run again. |
Test builds for d2d51be
|
isaacsa2
left a comment
There was a problem hiding this comment.
Rechecked 22b7b50. Thanks for the in-game captures, restart checks and unpaused LAN measurements. The GPU-copy/blur path replaces the readback I flagged earlier, so that evidence request is addressed.
There are still concrete blockers. PR checks / files fails on three file sizes: omsi-app/src/ui.rs is 1,852 lines against 1,810, omsi-plugin-ui/src/lib.rs is 1,510 against 1,500, and omsi-plugin/src/ui.rs is 1,507 against 1,500. Please extract cohesive helpers/modules rather than raise the limits. Log: https://github.com/openOMSI-org/openOMSI/actions/runs/40224043363/job/121261539726
The current Release also fails workspace tests: process_permissions::once_waits_without_blocking_frames_then_prompts_again_and_deny_stays_denied on Windows, and process_wait::timer_can_wait_and_be_cancelled_by_unloading on Linux. I have not established whether those are environmental; please diagnose them or provide the matching base comparison, and get the required checks green. The branch also conflicts with current main again. Coordinate the ui_theme setting with #3309 during that update, since both PRs introduce that key with different meanings. I reviewed the follow-up and CI; I have not run the themed renderer locally.
…three files under their size - main merged again (settings.rs: nav_classic_2d beside the theme pack). - openOMSI-org#3309 (open) makes `ui_theme` the light or dark interface theme, with the row "Interface theme". This PR's setting is now `ui_theme_pack`, its row "Theme pack" (in the 29 languages of the Interface page): a theme pack is a look a mod or plugin brings, the light or dark theme is openOMSI's own; the two keys no longer clash, whichever is merged first. - Under the size budget by moving cohesive parts into modules, not by raising limits: - omsi-app: the loading screen's drawing (the wordmark, the striped bar, an interface theme's logo and marks) is `ui/loading.rs` (ui.rs 1852 -> 1671 lines, its budget 1810); - omsi-plugin-ui: the panels' colours (the card, the accent, the menus' greys) are `look.rs` (lib.rs 1510 -> 1486); - omsi-plugin: the navigator's board (`Dock`, `set_dock`, `dock`) is `ui/dock.rs`, as the pause menu's lines (`ui/menu.rs`) and the themes (`ui/theme.rs`) are (ui.rs 1507 -> 1482). scripts/size-budget.sh and scripts/arch-guard.sh pass.
…erface › Theme pack (openOMSI-org/openOMSI#3278 keeps its key apart from the light and dark interface theme of #3309)
|
Thanks @isaacsa2, all three are addressed in 93914b6. File sizes: I moved cohesive parts out instead of raising limits:
#3309: this PR's key is now The two test failures: neither test touches anything this PR changes. Both pass 5/5 locally (Windows) on 93914b6, and the same workflow is green on
CI is running again on the new commit. |
Signed-off-by: Isaac S. <107325320+isaacsa2@users.noreply.github.com>
Adapt square.rs from main to the theme branch’s runtime accent function. This fixes E0432 after merging main without changing the tile layout or behaviour. Signed-off-by: Isaac S. <107325320+isaacsa2@users.noreply.github.com>
isaacsa2
left a comment
There was a problem hiding this comment.
Rechecked the follow-up and updated this branch with main. The settings conflict keeps both ui_theme_pack and cab_wheel_rotation=0. The fresh build exposed E0432 in omsi-plugin-ui/src/square.rs: main's new quick-menu tiles still imported ACCENT, while this branch uses accent(). I fixed those two references in 99f12f6; the tile layout and behaviour are unchanged. The module extraction and separate ui_theme_pack key address my earlier size/key objections. Theme pack is selectable in Options > Interface and players can return to openOMSI's own look, so its optional glass cost is not an FPS blocker under the current requirement. Approval is still pending the platform/workspace checks on 99f12f6. The supplied DX12 captures and measurements do not establish Vulkan/Metal or VR behaviour, and I have not run the game locally.
isaacsa2
left a comment
There was a problem hiding this comment.
Rechecked 99f12f6. The current Release, PR checks, CodeQL and secret scan now pass; size budget, crate layers and whitespace checks also pass locally. The module extractions, separate ui_theme_pack setting and quick-menu accent fix resolve my earlier build/integration objections.
The expensive CPU readback path is gone. Glass uses the existing frame on the GPU, is refreshed at a bounded rate and is optional through Options > Interface > Theme pack, with openOMSI's own look available again. The supplied DX12 captures and measurements address the validation I requested. Approving this head.
#3309 touches the same palette paths, so whichever lands second needs to merge the updated main and rerun its tests; keeping both setting names alone is not enough. I reviewed the code and CI, not a new local gameplay run. Vulkan, Metal and VR behavior is not established by the DX12 measurements.
thibautgnt
left a comment
There was a problem hiding this comment.
Thanks a lot @Luc-nbr, this is a carefully built feature: the theme file is validated field by field with clear log lines, openOMSI's own look stays the default and pixel-identical, and the glass moved to a bounded-rate GPU copy with real p99 numbers behind it. Two things before it can go in:
-
An SVG logo can still read files outside the theme's folder (
crates/omsi-ui/src/icons.rs:30,:46,:55,:182).read_logochecks the logo's own path (crates/omsi-app/src/themes.rs:232), but the SVG is then parsed withusvg::Options::default(). With noresources_dir, usvg's default image resolver opens any<image href="...">as a plain path, absolute or relative to the working directory, and reads it whole withstd::fs::read(no 4 MB cap). So a theme with<image href="C:/Users/me/Pictures/x.png"/>orhref="/some/other/file.svg"reads files outside its folder, and on Linuxhref="/dev/zero"makes the game read forever and run out of memory every time it starts, because the setting is saved. The file is read again on every parse: four parses at load, then one for each new picture of the loading logo. Fix: one sharedOptionsfor theme logos withimage_href_resolver.resolve_stringreturningNone(keepresolve_dataif you wantdata:images, which stay inside the 4 MB file), used byrasterize_rgba,svg_size,path_lengthandmarks. Please add a test with an<image href>pointing outside the folder. -
Conflict with #3309 (unify game theme), merged on main just now.
git merge-treeagainst current main conflicts in 14 files:omsi-ui/src/theme.rs,omsi-ui/src/kit.rs,omsi-plugin-ui/src/{lib,nav,square,widgets}.rs,omsi-app/src/shell/pause.rs,shell/pause/navigation_ui.rs,shell/pause/quick_actions_ui.rs,devtools/panel.rs,photo/panel.rs,game_lists/dropdowns.rs,settings.rsandomsi-launcher-core/src/lib.rs. It is not only text: the two PRs give the same colours two different mechanisms. On main,Uicarriestheme: Theme(the UPPERCASE fields) picked byset_theme(light), andlight: boolis passed down to the plugin panels (ThemeColors::new(light)), the quick menu and the shell. Here, everything reads a globalth()Palettebuilt on the dark palette. If the merge just keeps both, a theme pack shows its colours only whereth()is read, and the light theme only whereself.themeis read. Please settle on one source of colours, and decide what a pack does on top of Light: the simplest iswith_colorsstarting frompalette(light)instead ofPalette::OPENOMSI, with radius and glass on top. Then rerun the omsi-ui, omsi-plugin-ui and omsi-app tests and the size budget on the merged result.
Non-blocking notes:
crates/omsi-render/src/surface.rs:87and:140addCOPY_SRCto the window's swapchain for every player who can have it, with or without a glass theme. On DX12 your numbers show no cost, but on some mobile GPUs a copy-source swapchain can turn off framebuffer compression, so "without a theme nothing changes" may not hold on Android. Asking forCOPY_SRConly while a glass theme is in force, and reconfiguring when that changes, would rule this out.themes.rs:249: a picture logo is fully decoded before the 4096 px check (decode_fileallows up to 16384 px, so roughly 1 GB from a small, very compressible PNG). Mod textures already go through the same path, so this is no new hole, but reading the header size first would make the "at most 4096 px" in the description true.- The loading screen re-parses and rasterizes the logo for each new progress hundredth and mark stage (
ui/loading.rs:116). That's fine for a normal logo. A 4 MB SVG will make the loading screen stutter, but that only hurts the theme's own author.
The game told the plugins its themes only from their first frame, after it had loaded them: `ui.themes()` was empty and `ui.set_theme` refused every theme while a plugin's main chunk ran, which is where docs/PLUGINS.md offers a plugin's theme (Omsi-Hub's plugin logged "no theme \"omsihub\"" and never offered it again). The game now tells the themes and the one in use before it loads the plugins (`omsi_plugin::ui::themes_for_load`, read by a new `UiState`), and each frame as before; a test for it.
usvg's own options read any file an `<image href>` names, a path absolute or relative to the working directory, and whole, so a theme's logo could read files outside its folder (and on Linux `/dev/zero` forever, at every start, the setting being saved). Every SVG is now read with one set of options (`icons::tree`) whose `<image>` is only a `data:` URL, which stays inside the 4 MB file: `rasterize`, `rasterize_rgba`, `svg_size`, `path_length` and `marks`. A test with an `<image href>` to a file outside it, which usvg's own options do read.
…s over Light or Dark Main put the light look in a `Theme` of its own, read from `Ui::theme` (`set_theme(light)`) and passed to the plugin panels as `ThemeColors::new(light)`; this branch reads the palette in force, `omsi_ui::theme::th()`. Kept both, a pack's colours showed only where `th()` is read and Light only where `self.theme` is. Now there is one source of colours: - `omsi_ui::theme::Palette::LIGHT` is main's light look and `theme::palette(light)` gives openOMSI's dark or light palette; `Palette::with_colors` lays a theme's colours over the one given, and the theme's radius and glass go on top (`themes::Theme::palette(light)`). - `themes::sync(pack, light)` puts that in force (the theme read anew only when it is another); the pause menu, developer tools and photo mode call it where main called `set_theme`. `Ui::theme`, `Ui::set_theme`, `Shell::set_theme` and `theme::Theme` are gone; main's lines read `th()`, with its light-look fixes kept (accent ink on buttons, the toggle, slider, segment and progress tracks, tooltips and popups from the palette). - The plugin panels' `ThemeColors::of(palette)` keeps openOMSI's dark greys exactly and takes any other palette's surfaces and inks; a card is laid out again when the palette changes (`PanelsFrame::palette` for main's `light`). The game's own panels take the palette (`themes::palette(light)`) rather than `light`. - The settings keep both: `ui_theme` (Light or Dark) and `ui_theme_pack`. - docs/THEMES.md: a theme goes over the look the player chose. Main's tests for the light look run on the palette (`with_palette`); a test for a theme over the light look.
…n force The swapchain asked for COPY_SRC for every player wherever the window allowed it, though only a glass theme reads the window's picture (the blurred glass behind the game's screens). On DX12 that costs nothing measurable, but a mobile GPU may give up framebuffer compression for a swapchain that can be copied from. `SurfaceState::set_glass` asks for COPY_SRC only while a glass theme is in force and reconfigures the window when that changes; the frame calls it after the theme is synced. Without a glass theme the window is configured as before the themes.
…is decoded A picture logo was decoded whole before its 4096 px limit was checked, and the decoder allows up to 16384 px: a 4 MB file of one colour could make the game decode about 1 GB. Its size is now read from its header first (`image::ImageReader::into_dimensions`, the format guessed from its content as OMSI's misnamed files need), and one larger than 4096 px is refused undecoded; the decoded size is still checked after. A test with a 5000 px PNG.
|
Thanks @thibautgnt, both addressed:
Non-blocking: |
thibautgnt
left a comment
There was a problem hiding this comment.
Thanks @Luc-nbr, that was a thorough round. The SVG fix is the right one: a single icons::tree that keeps data: images, plus a test that proves usvg's own options would have read the file. The merge with #3309 now has one source of colours, with a pack laid over palette(light). You also took care of both non-blocking notes (COPY_SRC only while glass is on, and the picture size read from the header). CI is green, and the branch merges cleanly with current main, where size budget and arch-guard pass.
The merge left one place where Light breaks:
- The game menu drawn by
ui/menu.rsturns white on white in the Light look.own()(crates/omsi-app/src/ui/menu.rs:9) keeps openOMSI's dark colours only whenth() == Palette::OPENOMSI. Before the merge, that was true for anyone without a pack. Nowthemes::syncputsPalette::LIGHTin force for every player withui_theme=light, sopanel()/panel_alt()(:23, :26) return the light panel (252 grey) while the text colours stay fixed light greys:WHITE(236),SOFT,MUTED,DIM(:57, :66-68), and alsoLIT,SELECTED,CHIP,BORDER,THUMB. The title (:244), the lines and the preview card (:581-589) end up near-white on near-white. On main this menu stays dark in Light. The same thing happens with any pack over Light that doesn't setpanelitself.
Fix: when the palette isn't openOMSI's own, take the inks and line fills from it as well, e.g.WHITEfromp.text,SOFTfromp.text_soft,MUTED/DIMfromp.text_dim,OFF_INKfromp.text_faint,LIT/SELECTEDfromp.hover/p.selected,CHIPfromp.fieldandBORDERfromp.edge(the sameown(...)wrapper you already use forpanel()). Or, if you'd rather keep this menu dark in Light as on main, compare againstpalette(false)/palette(true)there. A small test underwith_palette(Palette::LIGHT, ...)checking that the title ink differs from the panel would keep this from coming back.
Non-blocking:
read_logo(crates/omsi-app/src/themes.rs:272): whenimagecan't read the header (a format it doesn't know), the file still goes todecode_filebefore the size check. That's fine, since those formats aren't the compressible kind, but it is the one path left where "at most 4096 px" is checked only after decoding.
isaacsa2
left a comment
There was a problem hiding this comment.
Rechecked d2d51be after the main merge. The shared palette integration and the SVG resolver fix address the earlier conflicts and external-file concern; I am not carrying those forward as blockers. Current CI passes.
The remaining blocker is the light palette in crates/omsi-app/src/ui/menu.rs. panel() and panel_alt() now follow th(), but WHITE, SOFT and the fixed border/selection/chip colours still assume a dark background. With Palette::LIGHT, the panel becomes nearly white while titles and preview text remain [236, 236, 236], making them difficult to read. A theme pack inheriting those light backgrounds has the same mismatch.
Please make the foreground/background pairs follow the active palette consistently, including selection states, or keep this menu consistently dark. Add a light-palette regression for the title/text and selected-row colours, alongside the existing default/theme tests. This is a source-level finding, not a new local screenshot or gameplay test.
What changes for the player?
Options › Interface has a new row, "Theme pack". A mod or a plugin can bring a theme that gives the game's own screens a look of their own:
The choice is kept for the next start. openOMSI's own look is the first entry and the default; without a theme, nothing changes (see the before/after screenshots).
How to test
Make a folder
Themes/Night/in the game folder or the content folder with thistheme.json:{ "name": "Night", "colors": { "accent": "#2a75f7", "background": "#0a142e", "surface": "#141a26", "ink": "#e8ebf2" }, "radius": 12, "glass": true, "loading": { "logo": "logo.svg", "progress": "stroke" }, "logo": "logo.svg" }and this
logo.svgbeside it:theme.json(e.g."radius": 80) and start the game:game.logsaysinterface theme "Night": radius: 0 to 24 is expected - openOMSI's own look, and the game looks as before.For a plugin: a
theme.jsoninplugins/<name>/makes a theme of that name, andomsi.ui.set_theme("<name>")switches to it (omsi.ui.themes()lists them,omsi.ui.theme()says which one is in force).Tried in the game on Windows 11 with an RTX 4070 SUPER (DX12): the release build of this branch and of
main(8119ca6), Berlin-Spandau with the MAN SD200 and 30 AI vehicles, 1600 × 900. Each run had a freshHOMEandOMSI_CONTENT. The Omsi-Hub theme was the test theme (glass, the logo as the loading bar, a wordmark), and the screens were driven byOMSI_INPUT(key Escape,key Right,menu options). Switching and restarting, checked in three runs:ui.set_theme("omsihub")savesui_theme=omsihub;ui.set_theme("")savesui_theme=openomsi;(These runs were made before the key was renamed for #3309: the setting was then
ui_theme, nowui_theme_pack; nothing else in that path changed.)Doing that found and fixed three things: the window, made before the renderer, could not be copied from, so the glass was not blurred; the glass cost too much when it was taken every frame (see Performance); and a loading logo whose view box does not start at 0 popped its marks in a little off their place.
Automated:
cargo test -p omsi-ui: palette fallbacks, hex colours, per-thread palettes; a path's length, a logo drawn along it, its marks popping up and flashing, also with a moved view box.cargo test -p omsi-pluginandcargo test -p omsi-plugin-ui: a plugin asks for a theme the game has, and the API manifest.cargo test -p omsi-app: a theme file read whole, a wrong one saying why and nothing read outside its folder, themes found inThemesandpluginswith the first id winning, the setting's values, the translated row.cargo test -p omsi-renderandcargo test -p omsi-launcher-core settings.Screenshots
In the game, Berlin-Spandau, 1600 × 900. Left
main, middle this branch with openOMSI's own look (the same asmain), right this branch with the Omsi-Hub theme.mainAfter switching back to openOMSI's look and restarting:
What does this change, and why?
Mods have always changed how OMSI looks, but the game's own screens could not be changed: their colours are constants in the code. With interface themes, a mod or a plugin can give the menus and the loading screen a look that fits it, without touching the code, and the player chooses it. Omsi-Hub, an add-on that runs on openOMSI through the plugin API, is one user: its plugin brings a theme in its own colours, with its logo as the loading bar. Nothing in the change is specific to it. The full format is in
docs/THEMES.md.The loading screen comes before any plugin runs, so a theme is a file that the game reads itself (
Themes/<id>/theme.json, ortheme.jsonin a plugin's folder), not a call a plugin makes.API (
omsi.ui.versionis now 7; #3277 took 6):omsi-ui:themebecame aPalettein force (theme::th(),theme::set), withPalette::OPENOMSIas before andwith_palettefor the tests. A theme's colours left out follow the one they go with (sidebarthe background,textthe ink and so on).theme::corner(r)isrwith openOMSI's look and follows a theme'sradius. The reworked pause menu's corners (12, 14, 18 ...) go through it.Glassdescribes the glass.kit::Ui::surfacedraws a surface solid, or as glass: the backdrop (Ui::backdrop) behind it throughimage_part, the panel's colour at the glass's opacity, a sheen and a light edge.paneluses it.iconshaspath_length,progress_svg(a path drawn along by a dash, over a faint track),marks,staged_svgandStage(elements withdata-atpopping up or flashing), andsvg_size.omsi-render:SurfaceState::picturegives the texture the frame is drawn into, where the window allowsCOPY_SRC;new_withandadoptask for it when the adapter offers it. The sRGB stand-in allows it too.omsi-app:themesfinds, reads and validates themes and keeps the one in force (sync, called every frame, which only reads anew when the setting changes).Settings::ui_theme_packis the setting, and Options › Interface has its row "Theme pack" (in the 29 languages of that page).ui/loading.rs, out ofui.rs.Shell::capture_backdropcopies the frame the scene has just drawn and blurs it on the graphics card in three small passes (a quarter of the window, then a 16th, then an 8th). It is done when the menu opens, then at most 30 times a second while the game goes on behind it (glass_due), and not again while the game is paused. Nothing is read back and nothing waits for the GPU.surface; the buttons, the driver's chip and the action cards stay solid. A wide theme logo goes across the top of the rail.ui/menu.rs) takes the theme's accent and panel colours, but no glass.omsi-plugin:UiStatekeeps the themes and a plugin's wish (ui/theme.rs), withui.themes,ui.themeandui.set_theme.omsi-apphands the wish to the settings after the plugins' frame. The navigator's board (Dock,set_dock,dock) moved toui/dock.rs, besideui/menu.rs, to keepui.rsunder its size.omsi-plugin-ui: the panels' accent and the notifications' card follow the theme; the panels' colours arelook.rs.omsi-launcher-core:ui_theme_packis read and written with the settings file.With #3309 (light and dark, open): #3309 makes
ui_themethe light or dark interface theme, with the row "Interface theme". This PR's setting isui_theme_packand its row "Theme pack", so the two keys and rows no longer clash, whichever is merged first. A theme pack is a look a mod or plugin brings; the light or dark theme is openOMSI's own look. Whichever PR lands second will need to fit its palette into the other's (both replace the same colour constants): a theme pack replaces openOMSI's colours, light or dark.docs/THEMES.mdis new.docs/PLUGINS.mdhas a section "An interface theme", and its reference andplugin-api.jsonare regenerated.What could break?
With openOMSI's own look, every colour is the constant it was, and the screenshots of
mainand this branch are the same. The screens now read the colours throughth()instead of constants: a lock and a copy a call.A theme is data only: nothing outside its folder is read, a file is at most 4 MB, a picture at most 4096 px, and a content folder another player's game sent in a LAN session brings no theme. A broken theme leaves openOMSI's look with a line in
game.log.The glass needs the window's picture to allow a copy. Where it does not, the panels are see-through without the blur. I tried DX12 only; Vulkan and Metal report
COPY_SRCfor their windows too, but I have not run them. I could not try the triple screens, the cockpit display or a phone. In VR, the menu keeps its own drawing with the theme's colours and no glass. Plugin panels already drawn keep their old colours until their content changes, after a theme is switched while driving.Unrelated to this change, but found while testing: a local release build of
maincrashes at start-up with the renderer pipelines' threads overflowing their stack whendxcompiler.dllis not next to the program (wgpu then compiles with FXC). The released packages are not affected; with DXC beside the program, both builds run.Performance
The same scene for every run: Berlin-Spandau, the MAN SD200 in the driver's view, 30 AI vehicles, a LAN session hosted (so the menu does not pause the game), the pause menu opened at 40 s and left open until the end at 180 s. Vsync off, no frame limit, 1600 × 900, the summary of
OMSI_PROFILE_JSON(after its 15 s warm-up), and the process's private memory read at 2 min.main(8119ca6)Machine: Windows 11, NVIDIA GeForce RTX 4070 SUPER (DX12), 1600 × 900, the default settings of a fresh home (Vanilla+, 4× MSAA, SSAO, 2048 shadows). One run each; the frame times move a few per cent from run to run on this machine (openOMSI's own look: 139.6 and 145.1 FPS in two runs).
With the glass taken every frame, it cost 1.1 ms a frame (121 FPS, p50 7.76 ms). Taken at most 30 times a second, it costs about 0.24 ms (p50), and its slowest 1 % and its slow frames are as with openOMSI's own look: no stall from the glass.
Checklist
cargo test --workspaceandcargo build --releasepass: omsi-ui (46), omsi-render, omsi-plugin, omsi-plugin-ui, omsi-app (398) and the launcher's settings tests pass locally,cargo build --release -p omsi-appbuilds, andscripts/size-budget.shandscripts/arch-guard.shpass. The full workspace is left to CI.crates/omsi-app/locales/app.yml("Interface theme" and its description, in the 29 languages of the Interface page)