3048 Commits
Author SHA1 Message Date
Stefan MaierandClaude Opus 5.5 91bdb7e67c Refresh rate: step down at one late frame in 32, show the rate it runs at
The first try stepped 90 to 80 Hz a few seconds into a 30-player game,
and no doubled picture was seen, but 2-5 stale frames a second were left
at 80: one late frame in 32 (was 16) takes it on to 72. The VR options
show the rate chosen and the one it runs at while lowered.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 17:44:18 +02:00
Stefan MaierandClaude Opus 5.5 bf778b6d60 Step the refresh rate down while frames come late (vr.refresh_rate_auto)
In big online games a client's ticks cost 3 to 5 ms (twice what bots or
smaller games do), and the frames with a tick missed 90 Hz's 11.1 ms:
the headset showed them twice, a doubled picture when turning, with the
GPU at 60-80 %. While more than one frame in sixteen comes late, two
two-second windows running, the rate now steps down to the next the
headset offers (90 to 72 Hz); after ten windows in which hardly any
would be late at the rate above, it steps back up, never above
vr.refresh_rate. A load's long frames count for neither. On by default,
"Lower it when frames are late" on the VR options' MORE page; nothing
where the headset offers one rate (SteamVR).

Also the BUTTONS page's notes below its ten rows (the push to talk row
pushed the last one onto them).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 17:38:59 +02:00
Stefan MaierandClaude Opus 5.5 87fe9fc1ad Merge OpenCE upstream: network version 29, Custom Edition maps keep their names, map sharing hardened
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 13:04:54 +02:00
MrBruh 3e0ba55111 Keep Custom Edition maps' own names: the version goes in the game record
A game's level name is the map's own (custom_maps\hugeass), so no screen or
server browser listing shows a hash, and the game record's map version is
the map's hash's first 32 bits, which a client finds its file by. Maps are
no longer renamed; a download beside another version of its name is named
<name>_<hash>.map and listed as "(2)"; maps the last build renamed get
their names back. Network version 29: the level name and the map version
mean something else.
2026-10-14 23:08:39 +13:00
Stefan MaierandClaude Opus 5.5 e18c2b37d6 Push to talk on any button, never sent by a grip reaching for the weapon
Push to talk becomes an action of the BUTTONS page (vr.button_talk),
on an eighth button there, the free hand's grip (its default): it can
trade places with any other action (the move stick's click, say, so
that a player holding a weapon in both hands can talk).

The free hand's grip as a button is pressed once held 0.15 s, at the
threshold halo_vr_two_hand takes the foregrip at, and not at all while
it holds the weapon's foregrip or the Warthog's wheel: taken in that
time, the press is theirs until the grip opens. Before, the runtime's
grip button (which closes before the foregrip's threshold) could open
the microphone for a frame or two as the hand took the weapon.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 21:45:40 +02:00
Stefan MaierandClaude Opus 5.5 0f6aa62eda VR: graphics "medium" by default; the PTC Quest's doubled pictures noted
On the developer's Quest 3 the picture doubles when turning with every
build back to 2026-10-06's, at 90 and 72 Hz, even when the compositor
reports no stale frames; that Quest runs Horizon OS from the Public Test
Channel. "medium" is smooth there, so it is the default until a Quest
outside PTC has tried "high" (roadmap). An unknown vr.graphics is
"medium" too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 23:04:03 +02:00
Stefan MaierandClaude Opus 5.5 1c7adac6e5 VR: graphics "auto", effects left out while the GPU falls behind and brought back after
On the Quest 3 the shader worker took away the long frames, but a 15-bot
game on Blood Gulch still had seconds at a time of 9-13 ms of GPU a frame
(GPU% 0.91-0.94, 4-37 stale frames a second, the picture doubled), between
spells of 5-7 ms; "medium" held 90 Hz.

- vr.graphics "auto", the new default (the VR options' Graphics row has
  Auto): "high", less what the GPU's time asks to leave out. host_xr.c
  reads XR_META_performance_metrics' app/gpu_frametime each frame (the
  metrics are now on wherever the runtime has them; logged only with
  vr.log_performance), handed to the guest by host_xr_gpu_time.
- vr_graphics.c: past four fifths of the frame (8.9 ms at 90 Hz, scaled
  with the rate) for three quarters of a quarter second's frames, the next
  steps are left out, as many as their known costs say the excess takes:
  the shield's glow, object shadows, reflections, specular light, lens
  flares, bump maps, the rest of "low". Steps that leave out nothing
  drawn (the glow without display.shield_glow) are passed over. A step's
  cost is learnt from the GPU time before it and the second after it
  settled. An effect comes back once the GPU time plus its cost stays
  under seven tenths of the frame for 3 s; an effect that had to go again
  within 10 s of coming back makes the wait twice as long, up to a minute,
  back to 3 s after a minute without leaving anything out. Each change is
  logged with the GPU time.
- Where the headset does not tell the GPU's time (SteamVR: the Steam
  Frame) "auto" draws everything, as "high" did.
- shield_glow.c draws no glow while "auto" leaves it out.
- debug.vr_graphics_measure leaves each effect out in turn (1.5 s on, 1.5 s
  off) and logs its cost by the GPU time, with means every round.

Checked: the APK builds, android_abi_check finds nothing, the desktop and
web paths compile; a host simulation of vr_graphics.c with a lagging
synthetic GPU time (heavy spells of 12 ms, effects of 0.3-2 ms) left out
two or three steps within 0.6 s of a spell and brought them back 3 s
after it, longer after a second spell; the measure mode recovers the
simulated costs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 22:17:41 +02:00
Stefan MaierandClaude Opus 5.5 d46e3fcb38 VR: foveation off by default, its "high" level taken out
On the Quest 3 (graphics "high", 90 Hz, multiview, a 15-bot game on Blood
Gulch) the app's GPU time was 5.5-6.4 ms with foveation off, low, medium
and high alike: the GPU is not what makes the frames late. "high" also
drew a grey rectangle below the player. vr.foveation is now "off" by
default with "low" and "medium" left to try; an unknown value is off.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 21:34:57 +02:00
MrBruh 6d64abdb42 Fix map sharing restart overflow and harden map validation 2026-10-13 07:43:41 +13:00
Stefan MaierandClaude Opus 5.5 a79dab53d1 Android: a program binary cache, and a map's programs made ready as it loads
The Quest's driver compiles and links a shader program the first time it
is drawn with, 10 to 30 ms each, which the frame waits for: a long frame
at a map's start (14 programs, 256 ms) and near its end (61 in 780 ms),
seen in VR as stale frames.

- port/linux/src/xgpu_program_cache.c: a linked program is written to
  shader_cache in the data folder, named by its sources' hash, with its
  binary (glGetProgramBinary), the driver that made it (GL_RENDERER and
  GL_VERSION) and both sources. Asked for again it is loaded with
  glProgramBinary when the driver, the sizes, the checksum and the sources
  agree; otherwise compiled as before and written again. A refused binary
  only falls back to compiling.
- d3d8_gl.c makes its shaders with their sources and compiles them only
  when a program of them must be linked (shader_make, program_make), so a
  program from the cache costs no compile. Each program entry keeps its
  cache key and lists itself once per map.
- Each map keeps the programs it drew with (shader_cache/maps/<map>.txt,
  written every 20 s while it grows and as the map goes). As a map loads
  (game_initialize_for_new_map, a port: hook) its programs are loaded from
  their binaries; after a driver update compiled again from the kept
  sources, in the background with GL_KHR_parallel_shader_compile where the
  driver has it (polled each frame, binaries written two a frame), else
  for at most two seconds of the load.
- display.shader_cache (true) turns it off; the folder is emptied past
  three quarters of 192 MB or 12000 files at the start, and may be deleted.
- VR's long frame line now also says how many programs came from the cache
  and what textures were uploaded (and decoded from DXT: the Quest has no
  S3TC), menu pictures decoded and render targets made, and in how long,
  for the menus' long frames that compile nothing.

Checked: the APK builds; the desktop and web paths compile; a host harness
with a mock driver went through the first run, a second run (all loaded),
a driver change (compiled from the kept sources, with and without
background compiling), a corrupt and a truncated file (compiled again) and
a missing list. On the emulator (no binary formats) the cache logs itself
off and a 4-bot game on Blood Gulch runs as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 20:34:37 +02:00
Stefan MaierandClaude Opus 5.5 52ca15dcf0 VR: fixed foveated rendering of the eyes on the Quest (vr.foveation)
The Quest 3 at 90 Hz with graphics "high" spends 5-9 ms of GPU a frame
on the eyes and shows stale frames (ASW, doubled pictures as the head
turns); "medium" holds, so the frame is short of GPU time. The eyes are
now drawn at full detail about each lens's centre and coarser towards
their edges with GL_QCOM_texture_foveated (the Quest's Adreno 740), where
the lenses blur them anyway.

- vr.foveation "off", "low", "medium" (the default) or "high", a row
  on the VR options' MORE page (dim where the GPU has no foveation, as the
  Steam Frame's zink); read as it changes.
- The focal point of each eye is its lens centre, tangent 0, in the
  picture's coordinates: off the middle, the Quest's views being
  asymmetric. Full detail within sqrt(1 + area) / gain of it (0.69, 0.58,
  0.43 of the half width), down to a least density of 1/2, 1/4, 1/8.
- Only the eyes' targets: the multiview layers, which nothing else draws
  into; one eye at a time, the back buffer only from the eye's camera to
  its copy into the headset's image, so the HUD, menus, scope, crosshair
  and names drawn into it after stay at full detail. A texture is first
  foveated when the setting is not off (it cannot be undone); "off"
  afterwards sets the gain to 0, full detail everywhere.
- The GL ES log line names foveation, GL_KHR_parallel_shader_compile and
  the program binary formats the driver has; the first foveated target
  logs the extension's features and focal points, vr.c the level and the
  lens centres.

Checked: the APK builds; the desktop and web paths compile (no foveation
code outside HALO_ANDROID). Not tried on a headset.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 20:34:22 +02:00
Stefan MaierandClaude Opus 5.5 7c6fa07cb5 Merge upstream 2026-10-12: network version 28, map sharing, pregame lobby, body and BSP fixes
Conflicts:
- rasterizer.h: upstream raised the transparent geometry groups to 2048
  as we had (halo_port_capacity.h); theirs.
- d3d8_gl.c: the ES capabilities' log with upstream's base vertex and our
  multiview.
- p2p.c, p2p_internal.h, p2p_signal.c: upstream's several joins and
  reconnect, with our join codes' state and the codes' and public list's
  updates each pass.
- network_server_manager.c: a machine downloading the map is not refused,
  and the bots' room logic stays.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 11:23:08 +02:00
MrBruh 5930a46f23 Merge pull request #294 from hkmodd/fix/titles-under-menus 2026-10-12 20:46:49 +13:00
MrBruh bbe00a4049 Merge pull request #286 from hkmodd/fix/hires-text-fit 2026-10-12 20:46:49 +13:00
MrBruh 3cd1a6622e Merge pull request #305 from hkmodd/perf/debug-model-vertices-static 2026-10-12 20:46:49 +13:00
MrBruh e71e6514f3 Merge pull request #304 from hkmodd/fix/transparent-geometry-groups 2026-10-12 20:46:49 +13:00
MrBruh 065d7f2b1d Merge pull request #263 from addisonbair/pregame-lobby 2026-10-12 20:34:15 +13:00
MrBruh 5a7c655a60 Testing branch review fixes
Network version 28: the host now holds a downloading machine's players out
of the game, and keeps a multiplayer map's first scripted BSP. Dynamic lights
and the flashlight stay on past the Xbox limits, and a second weapon picked
up while firing is readied. Machines of version 27 would disagree on these.

- A large co-op game's respawn fallback no longer picks a teammate who is in
  the air, outside the BSP, or riding an AI vehicle.
- A machine still downloading the map times out when it goes silent.
- Held players join through the late-join queue, so they get the same checks.
  A machine with no held players is started instead of being refused.
- The co-op teleport uses world positions, which are right for seated players.
- Overshield drain no longer cuts off a hit's flash, and hits over 0.02 flash.
- The menu XML is the generator's (Custom Edition's layout) again.
- port: markers on two new declarations.

Checked: ninja linux, android and web build; tools/harness,
test_touch_menu and test_web_build pass.
2026-10-12 20:01:49 +13:00
hkmoddandClaude Opus 5.5 eea1c3641a Keep the debug model vertices off the stack
rasterizer_debug_model_vertices declares 2048 debug vertices of 64 bytes
on the stack. render_model calls it for each part it draws, and the
release build inlines it there, so render_model's frame was 136,304
bytes, and the Windows build probed it a page at a time (_chkstk, 33
pages) for every model drawn, although the debug display is off. Its
vertices are static now: only the vertices below debug_vertex_count are
read, each written when it is added, and the function runs on the
render thread only, so nothing it draws changes.

Measured on Windows (7800X3D, RTX 4080, release build, vsync off), b30
co-op with 32 times the enemies and a bot, 75 s a run, five runs of each
build alternated: render_model's frame is 5,360 bytes (the PDB's
S_FRAMEPROC); _chkstk fell from 2.0% to 0.5% of the game thread's
samples; fps over windows with 2000 draws a frame or more, origin/main
128, 141, 139, 134, 107 (median 134), this change 140, 139, 144, 141,
132 (median 140). Linux, Android and the web build do not probe the
stack, so the change only shortens their frame. They were not built.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 03:18:12 +02:00
hkmoddandClaude Opus 5.5 2c6c85cf38 Raise the transparent geometry groups a frame from 384 to 2048
Every transparent thing a frame draws takes a group: each transparent
part of a model, each widget (light volumes, antennas, flags, glows),
each batch of dynamic geometry (particles, contrails), each transparent
surface of the structure. The Xbox's 384 were a limit the native builds'
larger pools of objects, widgets, effects and particles could pass: a
frame past it was drawn without the rest, and debug.txt said "too many
transparent geometry groups". A part refused a group has no sort record
(models.c checks the group's index), so nothing else changes.

Checked on Windows in b30 co-op with eight times the enemies and a bot,
75 s a run: origin/main overflowed in 3 runs of 4; a build counting the
groups asked for showed up to 464 in a frame, while the dynamic triangle
and vertex buffers peaked at 123 and 328 of their 1024 and the dynamic
triangles at 10272 of 49152, so they are left as they are. With this
change, the same count showed 391 in a frame and no overflow; two more
runs had none. The pool is 2048 * 160 bytes, malloc'd; the model order
pass keeps 14 bytes a group on the stack (28 KB). Harness: the
transparent_plane_order tests pass at the new size (natively on Windows;
the other 44 failures there are the same on origin/main, POSIX headers).
Android and web builds not built.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 02:55:40 +02:00
bnunu 173248fd8e Merge pull request #299 from innerhat-dev/overshield-drain-testing
An overshield's drain is not a shield hit, and a second weapon is readied
2026-10-11 20:02:20 -04:00
bnunu a33bf2d537 Merge pull request #297 from innerhat-dev/coop-respawn-fallback-testing
Respawn a large co-op fight beside the group after ten seconds
2026-10-11 20:01:58 -04:00
bnunu caad6e772a Merge pull request #296 from innerhat-dev/multiplayer-sky-bsp-testing
A multiplayer sky script no longer reloads the level
2026-10-11 20:01:43 -04:00
bnunu 4f59a20a01 Merge pull request #295 from innerhat-dev/multiplayer-flashlight-testing
A multiplayer match keeps the flashlight on
2026-10-11 20:01:21 -04:00
justinfilip 2598dbe8b6 An overshield's drain is not a shield hit, and a second weapon is readied. 2026-10-11 18:34:17 -05:00
justinfilip 0b4a03561b A large co-op fight respawns beside the group after ten seconds.
The wait only counted teammates the map called grounded, so jumping, riding, or standing on a beach left it with nobody to use. A late joiner after a checkpoint uses the same wait when nobody is safe to spawn beside.
2026-10-11 18:34:15 -05:00
justinfilip b71bd6a0fa Keep a multiplayer map's first scripted BSP switch so the host's game type chooses the sky. 2026-10-11 18:34:14 -05:00
justinfilip c3365b1c69 A multiplayer script that switches BSP to cycle the sky no longer reloads the level. 2026-10-11 18:34:14 -05:00
justinfilip 417de7e2ad A multiplayer match keeps the flashlight past the Xbox player and vehicle limits. 2026-10-11 18:34:13 -05:00
justinfilip d6df944886 Bring a co-op team inside with a scripted teleport.
Campaign scripts only name two players, so a large group was left on the beach when Truth and Reconciliation's grav lift switched into the ship.
2026-10-11 18:27:24 -05:00
justinfilip baab0780f6 Adding a player after a map download uses the same checks as any other late join. 2026-10-11 17:25:11 -05:00
justinfilip 05dd9ccdea Create the custom_maps folder on startup when it is missing. 2026-10-11 17:03:37 -05:00
justinfilip 67f2214219 A player still downloading the map no longer spawns until that player joins. 2026-10-11 17:03:37 -05:00
hkmoddandClaude Opus 5.5 13ddcf802a Keep a scenario's titles under the menus
A scenario's titles (cutscene titles, their text from the scenario's
ingame_help_text) are drawn with the screen's overlays, which come after
the players' windows and so after their menus. Some Custom Edition maps
show their name with one for the whole game: Extinction's "Extinction",
at the bottom right, was drawn over the pause menu and over the profile
screen's CANCEL button. cinematic_render already leaves out the letterbox
while a menu is up; it now leaves out the titles too. Their time goes on
as before, so a title still fades out when it would have.

Checked: the Windows build (clang 23.1.3, full LTO) without warnings;
Extinction from the Italian Custom Edition in a hidden window, START
pressed by a local test input after 14 seconds: the title is drawn in
the game and not over the pause menu.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 23:37:59 +02:00
hkmoddandClaude Opus 5.5 7a6fc70eb9 Fit the high-res text's fonts to the widths of a Custom Edition loc.map's letters
The high-res text sizes Overpass by the font tag's capitals and centres
each glyph on the tag's character width, and Overpass was chosen for the
Xbox maps' widths. A Custom Edition loc.map's fonts are narrower: the
Italian one's large_ui and small_ui are the same font, its capitals 10
units tall and its letters 87% as wide as Overpass's at that height
(against 95 to 98% for the Xbox maps' interstate, large_ui and small_ui),
so the menus' and HUD's letters ran into each other. Each font is now
narrowed until its letters A to Z and a to z are, summed, as wide as the
tag's; the Xbox maps' fonts are narrowed by 2 to 5 hundredths.

The fonts were also remembered by the tag's name, and the Xbox maps and a
loc.map have tags of the same names with other measures, and by the font
tag's address, which the next map's tags may reuse: they are remembered
by their measures too, and forgotten with the map's tags.

Checked: the fonts' widths measured from the Italian loc.map and the Xbox
ui.map against Overpass's advances; the Windows build (clang 23.1.3, full
LTO) without warnings; Extinction from the Italian Custom Edition, whose
large_ui is now drawn 87% wide, its letters apart, and the main menu's
small_ui 98% wide.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 23:16:04 +02:00
justinfilip 71ceb363f1 Players who already have the map no longer wait for a machine that is still downloading it. 2026-10-11 13:37:40 -05:00
justinfilip 2059b6cef5 A player joining or leaving the lobby no longer restarts the countdown. 2026-10-11 13:37:40 -05:00
MrBruh a2aad18f65 Custom Edition map sharing
Hosts send players who join without the game's Custom Edition map the map
and the resource maps it needs; the other players help, torrent-style.
Network version 27: new map sharing messages, and players of an internet
game now reach each other.
2026-10-12 08:07:14 +13:00
Addison Bair 59a74b0c4b The pregame screen opened through the port's menus everywhere
Fast setup and the network test's join opened ui.map's pregame screen by name, where ui_widget.c opens the port's lobby; a ui.map of only the menus' tags hosts and joins with both.
2026-10-11 14:08:51 +00:00
Stefan MaierandClaude Opus 5.5 74bdffb957 Merge upstream 2026-10-11: network version 26, first-person legs and shield flare, web port, fixes
Conflicts:
- render_objects.c: upstream's first-person legs and our VR body share one
  node_matrices (ours no longer shadowed it); upstream's legs never in VR
  (first_person_legs_wanted: set back behind a flat view's eye, they would
  stand behind the player; the VR body is vr.body "full").
- AndroidManifest.xml: upstream's ':game' process left out (the Steam
  Frame's Lepton waits for the app's process to end; the headsets have no
  Java heap over the Xbox window); HaloApplication kept, idle without it.
- xinput_sdl.c: the touch controls and the web port's, never in VR; the
  controllers' rumble in VR before the phone's.
- d3d8_gl.c: multiview's uniforms and upstream's blend constant, both.
- build.yml: Android only; upstream's web and pages jobs off.
- README.md: ours; upstream's in docs/OPENCE_README.md.
- menu_tags.c: our VR pause-list fit dropped: upstream now spaces the rows
  30 apart and moves the button hints below them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 14:04:08 +02:00
MrBruh 17a57832d8 Windows: tag block accessor builds again
msvc_comdat.c defines __inline as weak on Windows, which a static function cannot be; the accessor is __inline__. Checked by compiling msvc_comdat.c for i686-pc-windows-msvc.
2026-10-11 22:45:33 +13:00
MrBruh a3277d94dc Fewer draws and GL calls
Decals and light volumes batched, web index and vertex array caches, no FULL_ES3. Prisoner, 15 players: 115 to 172 FPS (A/B).
2026-10-11 22:28:34 +13:00
MrBruh 6399ed9d4a Text drawn a string at a time, web immediate draws packed
Scoreboard text on the web: 1.41 to 0.41 ms a frame. The network test's browse joins the fullest game that is not co-op.
2026-10-11 16:16:35 +13:00
MrBruh 01afcb654a First-person shield flare kept close only with display.viewmodel_shield (#254) 2026-10-11 15:13:49 +13:00
MrBruh 3fa1f348a9 Merge pull request #254 from chestahh/codex/opence-viewmodel-shield-fit 2026-10-11 15:05:51 +13:00
MrBruh 0bfe211289 Web: stable pages hashed less often, fewer clock reads
18-player Blood Gulch in Chrome at 1080p: 135 to 225 FPS.
2026-10-11 14:59:19 +13:00
chestahh 248210cedb Keep first-person shield flares close to the arms 2026-10-11 01:52:48 +01:00
MrBruh a55a182086 Web deploy, tests, docs 2026-10-11 12:46:28 +13:00
MrBruh acfa9a2a4d Web build 2026-10-11 12:46:28 +13:00
MrBruh 095973133e Shield color on the first-person arms too (#244) 2026-10-11 12:25:51 +13:00