diff options
| author | salmonik <salmonik@tuta.io> | 2026-11-04 14:11:40 +0100 |
|---|---|---|
| committer | salmonik <salmonik@tuta.io> | 2026-11-04 14:11:40 +0100 |
| commit | 592b865ca5c98d1df2153ea5b77f67aa8a76db60 (patch) | |
| tree | 4fb957603dba3bc57d316168f074410bdef3e70f | |
| parent | Gamma: clamp slider to the engine's 2.6 cap, defer readback (diff) | |
| download | hl2h-592b865ca5c98d1df2153ea5b77f67aa8a76db60.tar.gz hl2h-592b865ca5c98d1df2153ea5b77f67aa8a76db60.zip | |
Add HDR tonemap brightness slider
The engine applies mat_monitorgamma (SDL receives changing ramp midpoints),
but Wayland/XWayland compositors discard the window gamma ramp, so the gamma
slider has no visible effect there. Add an in-engine brightness control that
does not depend on the window system: mat_force_tonemap_scale, exposed as
"HDR brightness" (x10, 0 = automatic). Drop the temporary SDL gamma ramp
diagnostic.
| -rw-r--r-- | CONTEXT.md | 17 | ||||
| -rw-r--r-- | src/gui.cpp | 12 | ||||
| -rw-r--r-- | src/hl2h.cpp | 28 | ||||
| -rw-r--r-- | src/hl2h.h | 1 | ||||
| -rw-r--r-- | src/menu.cpp | 11 |
5 files changed, 52 insertions, 17 deletions
| @@ -241,14 +241,15 @@ vocabulary / decisions. | |||
| 241 | `ClientCmd` when their slider changes. Gamma is stored as x10 | 241 | `ClientCmd` when their slider changes. Gamma is stored as x10 |
| 242 | (`[view] gamma=22` = 2.2) and the slider stops at 26 because the engine | 242 | (`[view] gamma=22` = 2.2) and the slider stops at 26 because the engine |
| 243 | clamps `mat_monitorgamma` at 2.6. The value is read back through | 243 | clamps `mat_monitorgamma` at 2.6. The value is read back through |
| 244 | `ICvar::FindVar` on the next tick (ClientCmd is queued) and logged | 244 | `ICvar::FindVar` on the next tick (ClientCmd is queued) and logged. The |
| 245 | (`view: gamma request ..., cvar reports ...`) to distinguish a rejected | 245 | engine does apply the ramp (`SDL_SetWindowGammaRamp` was called with |
| 246 | command from an OS gamma ramp the compositor ignores. `SDL_SetWindowGammaRamp` | 246 | changing midpoints), but a Wayland/XWayland compositor discards it, so on |
| 247 | is also interposed (first 12 calls log `gamma: SDL_SetWindowGammaRamp | 247 | this setup the slider has no visible effect. |
| 248 | mid=...`) to see whether the engine ever pushes a ramp after the slider | 248 | - `HDR brightness` (`[view] tonemap`, x10, 0 = automatic) sets |
| 249 | moves; if it does and the screen does not change, the compositor is | 249 | `mat_force_tonemap_scale`. This is in-engine tonemapping for HDR maps and |
| 250 | discarding it. Both live in the always-on | 250 | does not depend on the window system applying a gamma ramp; 10 = 1.0. |
| 251 | `Camera / display` card (`[view] fov/vmfov/gamma`). | 251 | Both live in the always-on `Camera / display` card |
| 252 | (`[view] fov/vmfov/gamma/tonemap`). | ||
| 252 | 253 | ||
| 253 | ## Map transitions | 254 | ## Map transitions |
| 254 | 255 | ||
diff --git a/src/gui.cpp b/src/gui.cpp index e8bcac7..83dc0a5 100644 --- a/src/gui.cpp +++ b/src/gui.cpp | |||
| @@ -420,8 +420,12 @@ static void build_cards() { | |||
| 420 | &g_fov_viewmodel); | 420 | &g_fov_viewmodel); |
| 421 | add_slider(view, "Gamma x10", | 421 | add_slider(view, "Gamma x10", |
| 422 | "mat_monitorgamma x10: 16 = 1.6, 26 = 2.6 (engine max), stock " | 422 | "mat_monitorgamma x10: 16 = 1.6, 26 = 2.6 (engine max), stock " |
| 423 | "is 22.", | 423 | "is 22. Uses the OS gamma ramp; some compositors ignore it.", |
| 424 | 16, 26, &g_gamma); | 424 | 16, 26, &g_gamma); |
| 425 | add_slider(view, "HDR brightness", | ||
| 426 | "mat_force_tonemap_scale x10 (0 = automatic). In-engine " | ||
| 427 | "brightness for HDR maps; 10 = 1.0.", | ||
| 428 | 0, 30, &g_tonemap); | ||
| 425 | 429 | ||
| 426 | add_card(2, &g_fullbright, "Fullbright", | 430 | add_card(2, &g_fullbright, "Fullbright", |
| 427 | "Ignore world lighting so dark areas are visible.", true); | 431 | "Ignore world lighting so dark areas are visible.", true); |
| @@ -2254,12 +2258,6 @@ SDL_SetWindowGammaRamp(void *window, const unsigned short *r, | |||
| 2254 | const unsigned short *, | 2258 | const unsigned short *, |
| 2255 | const unsigned short *))dlsym( | 2259 | const unsigned short *))dlsym( |
| 2256 | RTLD_NEXT, "SDL_SetWindowGammaRamp"); | 2260 | RTLD_NEXT, "SDL_SetWindowGammaRamp"); |
| 2257 | static int logs = 0; | ||
| 2258 | if (logs < 12) { | ||
| 2259 | logs++; | ||
| 2260 | logf("gamma: SDL_SetWindowGammaRamp mid=%u/%u/%u", r ? r[128] : 0, | ||
| 2261 | g ? g[128] : 0, b ? b[128] : 0); | ||
| 2262 | } | ||
| 2263 | return real_set_gamma_ramp ? real_set_gamma_ramp(window, r, g, b) : -1; | 2261 | return real_set_gamma_ramp ? real_set_gamma_ramp(window, r, g, b) : -1; |
| 2264 | } | 2262 | } |
| 2265 | 2263 | ||
diff --git a/src/hl2h.cpp b/src/hl2h.cpp index 1fc7096..a037bac 100644 --- a/src/hl2h.cpp +++ b/src/hl2h.cpp | |||
| @@ -698,6 +698,34 @@ static void view_settings_apply(void *ent) { | |||
| 698 | pending_gamma % 10, got); | 698 | pending_gamma % 10, got); |
| 699 | pending_gamma = -1; | 699 | pending_gamma = -1; |
| 700 | } | 700 | } |
| 701 | // In-engine brightness for HDR maps. Unlike mat_monitorgamma this does not | ||
| 702 | // depend on the window system applying a gamma ramp. | ||
| 703 | static int last_tonemap = 0; | ||
| 704 | static int pending_tonemap = -1; | ||
| 705 | if (g_tonemap != last_tonemap) { | ||
| 706 | char cmd[64]; | ||
| 707 | if (g_tonemap == 0) | ||
| 708 | snprintf(cmd, sizeof(cmd), "mat_force_tonemap_scale 0"); | ||
| 709 | else | ||
| 710 | snprintf(cmd, sizeof(cmd), "mat_force_tonemap_scale %d.%d", | ||
| 711 | g_tonemap / 10, g_tonemap % 10); | ||
| 712 | console_cmd(cmd); | ||
| 713 | pending_tonemap = g_tonemap; | ||
| 714 | last_tonemap = g_tonemap; | ||
| 715 | } | ||
| 716 | if (pending_tonemap >= 0) { | ||
| 717 | float got = -1.0f; | ||
| 718 | if (g_iface.cvar) { | ||
| 719 | using FindFn = void *(*)(void *, const char *); | ||
| 720 | void *v = vfunc<FindFn>(g_iface.cvar, 12)(g_iface.cvar, | ||
| 721 | "mat_force_tonemap_scale"); | ||
| 722 | if (v) | ||
| 723 | got = *(float *)((uintptr_t)v + 0x2c); | ||
| 724 | } | ||
| 725 | logf("view: tonemap request %d.%d, cvar reports %.2f", | ||
| 726 | pending_tonemap / 10, pending_tonemap % 10, got); | ||
| 727 | pending_tonemap = -1; | ||
| 728 | } | ||
| 701 | } | 729 | } |
| 702 | 730 | ||
| 703 | // --------------------------------------------------------------------------- | 731 | // --------------------------------------------------------------------------- |
| @@ -249,6 +249,7 @@ extern bool g_edge_jump; | |||
| 249 | extern int g_fov_camera; | 249 | extern int g_fov_camera; |
| 250 | extern int g_fov_viewmodel; | 250 | extern int g_fov_viewmodel; |
| 251 | extern int g_gamma; | 251 | extern int g_gamma; |
| 252 | extern int g_tonemap; | ||
| 252 | extern int g_esp_col_hostile[3]; | 253 | extern int g_esp_col_hostile[3]; |
| 253 | extern int g_esp_col_neutral[3]; | 254 | extern int g_esp_col_neutral[3]; |
| 254 | extern int g_esp_col_friendly[3]; | 255 | extern int g_esp_col_friendly[3]; |
diff --git a/src/menu.cpp b/src/menu.cpp index 248aa48..033c0f9 100644 --- a/src/menu.cpp +++ b/src/menu.cpp | |||
| @@ -82,6 +82,7 @@ bool g_edge_jump = false; | |||
| 82 | int g_fov_camera = 75; // HL2's stock camera FOV | 82 | int g_fov_camera = 75; // HL2's stock camera FOV |
| 83 | int g_fov_viewmodel = 54; // HL2's stock viewmodel FOV | 83 | int g_fov_viewmodel = 54; // HL2's stock viewmodel FOV |
| 84 | int g_gamma = 22; // mat_monitorgamma x10, stock 2.2 | 84 | int g_gamma = 22; // mat_monitorgamma x10, stock 2.2 |
| 85 | int g_tonemap = 0; // mat_force_tonemap_scale x10, 0 = automatic | ||
| 85 | int g_esp_col_hostile[3] = {255, 60, 60}; | 86 | int g_esp_col_hostile[3] = {255, 60, 60}; |
| 86 | int g_esp_col_neutral[3] = {220, 220, 220}; | 87 | int g_esp_col_neutral[3] = {220, 220, 220}; |
| 87 | int g_esp_col_friendly[3] = {60, 255, 60}; | 88 | int g_esp_col_friendly[3] = {60, 255, 60}; |
| @@ -248,8 +249,8 @@ void config_save() { | |||
| 248 | g_esp_col_neutral[0], g_esp_col_neutral[1], g_esp_col_neutral[2], | 249 | g_esp_col_neutral[0], g_esp_col_neutral[1], g_esp_col_neutral[2], |
| 249 | g_esp_col_friendly[0], g_esp_col_friendly[1], | 250 | g_esp_col_friendly[0], g_esp_col_friendly[1], |
| 250 | g_esp_col_friendly[2]); | 251 | g_esp_col_friendly[2]); |
| 251 | fprintf(f, "[view]\nfov=%d\nvmfov=%d\ngamma=%d\n", g_fov_camera, | 252 | fprintf(f, "[view]\nfov=%d\nvmfov=%d\ngamma=%d\ntonemap=%d\n", |
| 252 | g_fov_viewmodel, g_gamma); | 253 | g_fov_camera, g_fov_viewmodel, g_gamma, g_tonemap); |
| 253 | fprintf(f, "[movement]\ncrouchjump=%d\nedgejump=%d\n", | 254 | fprintf(f, "[movement]\ncrouchjump=%d\nedgejump=%d\n", |
| 254 | (int)g_auto_crouchjump, (int)g_edge_jump); | 255 | (int)g_auto_crouchjump, (int)g_edge_jump); |
| 255 | gui_config_write(f); | 256 | gui_config_write(f); |
| @@ -409,6 +410,11 @@ static bool config_load() { | |||
| 409 | lo = 16; | 410 | lo = 16; |
| 410 | hi = 26; // the engine clamps mat_monitorgamma at 2.6 | 411 | hi = 26; // the engine clamps mat_monitorgamma at 2.6 |
| 411 | stock = 22; | 412 | stock = 22; |
| 413 | } else if (strcmp(key, "tonemap") == 0) { | ||
| 414 | dst = &g_tonemap; | ||
| 415 | lo = 0; | ||
| 416 | hi = 30; | ||
| 417 | stock = 0; | ||
| 412 | } | 418 | } |
| 413 | if (dst) { | 419 | if (dst) { |
| 414 | int v = atoi(val); | 420 | int v = atoi(val); |
| @@ -605,6 +611,7 @@ void config_reset_defaults() { | |||
| 605 | g_fov_camera = 75; | 611 | g_fov_camera = 75; |
| 606 | g_fov_viewmodel = 54; | 612 | g_fov_viewmodel = 54; |
| 607 | g_gamma = 22; | 613 | g_gamma = 22; |
| 614 | g_tonemap = 0; | ||
| 608 | g_esp_col_hostile[0] = 255; | 615 | g_esp_col_hostile[0] = 255; |
| 609 | g_esp_col_hostile[1] = 60; | 616 | g_esp_col_hostile[1] = 60; |
| 610 | g_esp_col_hostile[2] = 60; | 617 | g_esp_col_hostile[2] = 60; |
