21 Commits
Author SHA1 Message Date
Ludovic Marie 52d38ca94c Merge remote-tracking branch 'origin/main' into HEAD
# Conflicts:
#	tools/stress_scenes.py
2026-11-02 23:28:18 +01:00
Ludovic MarieandClaude Opus 5.5 ca4435ef27 Cosmetics: the game draws them on players, behind a flag
The skin path poses each item on its bone and draws it with the player,
textures in the skin atlas's slots; Extras gets 'My cosmetics' and
'Other players' cosmetics'. Off by default (horizon_cosmetics::ON_BY_DEFAULT).
The perf bench gains stress_avatars, 300 mannequins, to measure the cost.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-02 21:12:52 +01:00
Ludovic MarieandClaude Opus 5.5 d821e95b87 Pose horse markings and slime outer cubes on the GPU
With improved transparency off, EntityBatch ignores the OIT_ENTITY tag:
an entityTranslucent layer (a horse's markings, a slime's outer cube) is a
plain draw with a tag, as the eyes render type's layers are (#7287). Such
mobs now take the GPU path then too, their jobs tagged as the CPU tags the
vertices. SkinModels::eyes becomes SkinModels::tagged (Renderer::skins_tagged).

The every-mob test gives the horse markings and expects the horse and the
slime posed with the tagged layers; new stress scene stress_markings (1000
marked horses and slimes).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-02 17:39:02 +01:00
Ludovic MarieandClaude Opus 5.5 fd594cb6a0 Pose glowing entities' silhouettes on the GPU skin path
A glowing mob (and a glowing dropped item) went to the CPU path whole:
the entity outline (`OutlineBufferSource`) copied its CPU vertices in
the team colour with no normal (`mark_outline`). Now a glowing entity
that is not invisible is posed on the GPU like any other, and its
silhouette too: `SkinFrame::outline_since` copies the entity's skin
jobs as FLAT jobs (no normal) in the team colour. The same dispatch
writes them past every entity material in the entity buffer, and the
outline pass draws them from there after the CPU silhouettes. An
invisible glowing mob (only its outline shows) keeps the CPU path.

The shader test now dispatches outline jobs into a second region and
checks each silhouette is its draw's vertex with the colour set and the
normal cleared, as `mark_outline` makes it. Adds stress_glowing (1000
glowing mobs).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-02 15:41:01 +01:00
Ludovic MarieandClaude Opus 5.5 7e00efefc3 Pose mobs with glowing eyes on the GPU skin path
Spiders, cave spiders, endermen, phantoms, the copper golem and a
creaking with lit eyes went to the CPU path whole, body included: their
eyes layer (the eyes render type) is translucent, and the GPU path took
only plain opaque and cutout draws. With improved transparency off (the
default), those layers are ordinary entity quads, so they now join the
mob's skinned draw:

- a skin job's mode (was `item`) gains FLAT: the eyes layer's normal is
  all zero, as the CPU path writes it;
- the OIT tag and the eyes' HDR tier ride in the job's overlay word,
  the same bits the CPU path ORs into the vertex.

With improved transparency on, EntityBatch sorts those quads into its
stages by the CPU vertices' tag, so the mobs stay on the CPU path then
(`Renderer::skins_eyes`).

The every-mob test runs twice, eyes off and on: with eyes on, the five
mobs above are posed, byte-equal to the CPU path. The shader test
covers a FLAT job. Adds stress_eyes (1000 spiders, cave spiders and
endermen).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-02 15:14:23 +01:00
Ludovic MarieandClaude Opus 5.5 9598faaba1 Draw mobs' held items on the GPU skin path
A mob's main- and off-hand items were posed and lit vertex by vertex on
the CPU every frame, even when the mob's own body went through the GPU
skin path. Items whose baked model can take that path (opaque or cutout,
no glint, emission or translucency) now join the mob's skinned draw:
one job per item with the hand pose, culled and truncated together with
the body. Outlined or glowing mobs and first-person hands keep the CPU
path, which outlines need.

The byte-for-byte test now poses every item in the ground, both
third-person hands, fixed and head contexts: 15514 states take the GPU
path and match the CPU vertices exactly; 1066 stay on the CPU.

Adds stress_held (1000 mobs holding two items each) to the stress
scenes.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-02 14:05:32 +01:00
Ludovic Marie 76aa871c1e Merge remote-tracking branch 'origin/main' into perf/entities-stress
# Conflicts:
#	mods/horizon/src/bench/java/dev/horizonlab/bench/VanillaBench.java
#	tools/perf_bench.py
2026-11-02 07:10:57 +01:00
Ludovic MarieandClaude Opus 5.5 50fdee8614 perf_bench report: runs without --server (tests' Namespace) have no server condition
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-02 01:48:06 +01:00
Ludovic MarieandClaude Opus 5.5 7a52fde28f perf_bench: many-entity stress scenes, per-scene client tick and memory
tools/stress_scenes.py builds nine scenes above the spawn with vanilla
commands (1000 mobs, 2000 dropped items, 512 item frames, 500 armor
stands, 800 containers, 687 signs, 300 banners, steady particles from
redstone-powered command blocks, and an empty baseline) and removes each
afterwards. The same lines run in Horizon (horizon-ctl command) and in
vanilla (the driver mod's new per-scene setup and cleanup).
perf_bench --scenes stress holds each scene still and reports frame CPU,
client tick time (vanilla: Minecraft.tick through a new mixin) and
resident memory per scene.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-01 22:48:34 +01:00
Ludovic MarieandClaude Opus 5.5 72fabfede5 tests: main's Python suite passes again (perf_bench fixture, trusted reviewers)
- test_table_distinguishes_missing_scene_load_from_zero: #4407's later
  commit makes table() refuse reports without matching conditions, so its
  fixtures now carry the same full conditions as the contract tests.
- test_the_repos_list_is_the_nightlys: the list names Qwerner88880 since
  f35210d2d; 10c71a65d updated test_nightly_set but not this test.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-31 19:20:13 +01:00
Ludovic Marie 685cf4a18c Merge pull request #4407 from SlaVKsVolks/contribute/horizon-benchmark-report-20261030
🤖 Agent: perf_bench --table prints the missing-value marker when a report has no scene-load measurement, keeps a measured 0.0 and the mean of measured scenes; regression test covers all three. Tooling only; rdbtCVS approved; CI green. Marked ready from draft.
2026-10-31 18:27:48 +01:00
iseaprojectsandDeepSeek V4.1 Flash a257a4221e tools: read cmd /C ver in the console's code page on Windows
`perf_bench.windows_version` asked subprocess for text, which decodes with
the locale encoding: `pr_ready` runs the tests with PYTHONUTF8=1, so the
reader thread decoded `cmd /C ver`'s OEM output as UTF-8, failed, and left
`stdout` None. `test_report_without_a_load_average` then raised, so every
Windows contributor's self-check showed `Python tests: fail`.

Decode the command's output as OEM, with replacement for stray bytes, and
keep the `platform.version()` fallback for an empty result. The regression
test runs the import in a child process with PYTHONUTF8=1 and checks it still
prints a Windows version; it fails on main with a CalledProcessError.

Fixes #5201

Co-Authored-By: DeepSeek V4.1 Flash <[email protected]>
2026-10-31 11:28:02 -04:00
SlaVKsVolksandOpenAI Codex GPT-6.1 Sol 320778a3d2 fix(bench): reject incomparable benchmark reports
Check the documented comparison conditions before printing tables, and report missing or conflicting settings without producing a misleading comparison.

Co-Authored-By: OpenAI Codex GPT-6.1 Sol <[email protected]>
2026-10-30 11:12:46 -03:00
SlaVKsVolksandOpenAI Codex GPT-6.1 Sol e0b4037a39 fix(bench): distinguish absent scene load measurements
Show a dash when no scene-load measurements exist; preserve measured zero and the mean of available scenes.

Co-Authored-By: OpenAI Codex GPT-6.1 Sol <[email protected]>
2026-10-30 10:42:54 -03:00
d3grandoandClaude Opus 5.5 cc243fabd8 perf_bench: pass vanilla the manifest's -XX flags
The 26.3 manifest gives every OS -XX:StackShadowPages=32, and the official launcher passes it.
perf_bench only took the manifest's OS-conditional arguments, so vanilla started without it. On
Windows about half of those starts crash before the title screen: an access violation under
shaderc.dll while the client compiles its shaders, with no hs_err file. Measured on Windows 11
(Ryzen 7 9800X3D, RTX 5080, Java 25.0.5), 8 starts each through run_vanilla: 4 crashed without the
flag, none with it.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-29 08:29:52 +07:00
d3grandoandClaude Opus 5.5 f44af70080 perf_bench: run on Windows
tools/perf_bench.py stopped on Windows before or right after the first game:

- the control socket is AF_UNIX at /tmp, which Python on Windows doesn't have; the game
  there listens on a localhost TCP port named in a .json endpoint file (control.rs), so
  the driver now uses that transport when there are no Unix sockets;
- memory was read with `ps`; Windows uses the process's working set (GetProcessMemoryInfo);
- os.getloadavg doesn't exist; the report leaves load_avg_1m out there, as horizon-bench does;
- hardware() read /etc/os-release and /proc; Windows gets `ver`, the CPU brand string
  and the total RAM;
- nvidia-smi reports per-process memory as [N/A] under WDDM, which raised ValueError;
- the binary is horizon-client.exe.

A release binary passed with --build reports its own version (from its first log line)
instead of this checkout's version and commit.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-29 08:29:52 +07:00
Ludovic MarieandClaude Opus 5.5 5c9f11b954 Benchmark: a 1080p set next to 720p
perf_bench ran every game in a 1280x720 window only. It now runs the
standard set at 1280x720 and 1920x1080 (--sizes picks others), each size
its own reports: <label>-rd<N>.json at 720p as before, <label>-1080p-rd<N>.json
at 1080p.

Horizon takes vanilla's --width and --height for its window's starting size
(1280x720 by default); perf_bench passes them to every game, and to Horizon
only off 720p, so builds before this one still run the 720p set.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-23 07:58:53 +02:00
Ludovic MarieandClaude Opus 5.5 9e1c3ec860 perf_bench: the readiness gate only with a chunk target; a test for the quiet start
Without min_chunks (the old call) wait_loaded behaves as before, which tests/test_perf_bench.py's
old-builds case relies on.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-22 21:51:16 +02:00
Ludovic MarieandClaude Opus 5.5 49b6367d2e perf_bench: a load ends when every streaming stage is idle
Builds that report status.streaming are loaded once chunk counts are
stable and no stage (generation, decoration, light, meshing) has held work
for half a second. Meshing going quiet also waited through the remeshing
that follows a load (fluids settling, random ticks: 1500 falling to 60
meshes/s for ~150 s at RD16), which is simulation, not loading. Older
builds keep the meshing-rate rule.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 07:01:03 +02:00
Ludovic MarieandClaude Opus 5.5 2a91612928 perf_bench.py: repeats, and the benchmark report format v1
Each game runs --repeats times (3) per render distance and writes one v1
report per distance (benchmarks/perf/<label>-rd<N>.json): figures as means
with standard deviation and 95% CI, per-flight frame entries with the fixed
histogram, the hardware without the host name. frames() and summary() match
crates/horizon-bench value for value (tested on the same inputs).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 07:01:03 +02:00
Ludovic MarieandClaude Opus 5.5 b691b5ebaa Benchmark Horizon against vanilla 26.3 and vanilla with performance mods
tools/perf_bench.py runs Horizon, the vanilla 26.3 client and vanilla with
Sodium, Lithium, FerriteCore and ImmediatelyFast through the same steps:
fresh world (seed 1234, creative) per render distance, start to title, start
to in-world, then per scene teleport, chunk load, settle and a fixed 192-block
flight. Every frame time is kept and one function computes average FPS, 1% and
0.1% lows, p50/p95/p99/p99.9 and a frame-time histogram for every game; RSS is
sampled from outside the process for every game. One options.txt (vanilla's
keys, Fancy preset values with a Custom preset so the render distance holds)
is written for all of them.

Vanilla is driven by a dev-only Fabric mod (mods/horizon/src/bench) compiled
against the user's own client jar: it creates the world with
WorldOpenFlows.createFreshLevel, teleports with /tp and places the player on
the path every frame as horizon-ctl fly does. Nothing of the game is
redistributed and the performance mods are fetched from Modrinth with the
hashes pinned in packs/mods.lock.json.

Horizon side: `fly ... PITCH raw` also replies with every frame time, and
--offscreen drives frames from the event loop, so a window nobody sees (a
locked macOS session) still renders.

docs/benchmark.md describes what is measured, how fairness is kept, the
caveats and how to reproduce it.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 03:40:12 +02:00