From 7efdbdb39a2e3b90ec8188cdaa0e9b4f1c31a9e2 Mon Sep 17 00:00:00 2001 From: Ludovic Marie <136255825+ludovic111@users.noreply.github.com> Date: Sat, 24 Oct 2026 15:42:44 +0200 Subject: [PATCH 01/41] docs: remove the surveys of other mods; mod code is never read Removes docs/performance/java-perf-mods.md (a survey of eight Java performance mods' techniques) and docs/research/java-mod-compat.md (notes on how Fabric, NeoForge and Mixin work, with a list of mods to port). Links to them now point at mods.md, which says in two lines why Java jars don't run in Horizon. docs/modpacks.md no longer lists a mod's decompiled code as an input of a port: a mod's code is never decompiled, read, quoted or paraphrased, and ports are written from the mod's observed behaviour (the oracle and the harness), its own data files and its public documentation only. Co-Authored-By: Claude Opus 5.5 --- docs/README.md | 2 +- docs/mod-browser.md | 2 +- docs/modpacks.md | 24 ++-- docs/mods.md | 7 +- docs/performance/java-perf-mods.md | 130 -------------------- docs/research/java-mod-compat.md | 183 ----------------------------- 6 files changed, 18 insertions(+), 330 deletions(-) delete mode 100644 docs/performance/java-perf-mods.md delete mode 100644 docs/research/java-mod-compat.md diff --git a/docs/README.md b/docs/README.md index 2e06b6e89..71e2a9ced 100644 --- a/docs/README.md +++ b/docs/README.md @@ -14,7 +14,7 @@ To play, use [the launcher](../launcher/README.md); the project site is [playhor - [Benchmark](benchmark.md): Horizon against vanilla 26.3 and vanilla with performance mods, on the same machine, world, path and settings: what is measured, how it is kept fair, how to reproduce it. - [Shader packs](shader-packs.md): how the renderer draws a frame, the Horizon WGSL shader pack format (stages, uniforms, `pack.toml`, hot reload), and the plan to translate OptiFine/Iris packs. - [Extras](extras.md): optional zoom, minimap, FPS and keystroke overlays and auto-reconnect, all off by default. -- [Mods](mods.md): Horizon's own mod system (Rust, sandboxed WebAssembly), how to make and install mods, and [what carries over from Java mods](research/java-mod-compat.md). +- [Mods](mods.md): Horizon's own mod system (Rust, sandboxed WebAssembly), how to make and install mods. - [Modpacks](modpacks.md): porting whole packs (ATM10 and smaller): import, inventory, carry-over, an agent per mod with a harness against the Java original, coverage, and how "10x" is measured. - [Launcher](../launcher/README.md): installs, updates and starts builds; its CLI and MCP server; releasing it. - [Versioning](versioning.md): how releases are numbered from 0.2 (`v0.2.x`, `launcher@0.2.x`), how installs from before 0.2 get there, and the steps for the day 0.2 ships. diff --git a/docs/mod-browser.md b/docs/mod-browser.md index 745f51d9b..3146fca67 100644 --- a/docs/mod-browser.md +++ b/docs/mod-browser.md @@ -6,7 +6,7 @@ packs, see how well each one runs before installing it, and install it with its ## What it offers -Horizon doesn't run Java code ([research/java-mod-compat.md](research/java-mod-compat.md)). It ports what a Java mod +Horizon doesn't run Java code ([mods.md](mods.md)). It ports what a Java mod carries as data (blocks, items, recipes, loot tables, tags, worldgen, textures, names) with `horizon-port-pack`, and says before install which of the mod's features that leaves out. Horizon `.hmod` mods aren't on Modrinth or CurseForge yet (Modrinth has no `horizon` loader). diff --git a/docs/modpacks.md b/docs/modpacks.md index c15d0767e..591c5a567 100644 --- a/docs/modpacks.md +++ b/docs/modpacks.md @@ -7,9 +7,8 @@ pipeline, not a list of hand ports. It takes a pack as players download it and p Horizon mods. The pipeline is AI agents in dependency order, each port checked against the original mod running in a dev-only Java instance. -This page is the design. It builds on [mods.md](mods.md) (the mod API, WASM in wasmtime) and -[research/java-mod-compat.md](research/java-mod-compat.md) (why Java jars and Mixins cannot -run in Horizon, so we port instead). The ATM10 inventory behind the numbers below is +This page is the design. It builds on [mods.md](mods.md) (the mod API, WASM in wasmtime, and +why Java jars cannot run in Horizon, so we port instead). The ATM10 inventory behind the numbers below is [modpacks/atm10-mods.csv](modpacks/atm10-mods.csv). ## In one picture @@ -141,12 +140,13 @@ derivative work. These rules are part of the pipeline, not advice to users: has given written permission, recorded in the index entry. The tooling refuses to package anything else. No-derivatives and non-commercial licences (14 in ATM10) are never published. For them, even a local port gets a warning that the licence may not allow it. -4. **Mod code never enters this repository.** Mod jars, decompiled sources, Java oracle builds +4. **Mod code never enters this repository, and nobody reads it.** Mod jars, Java oracle builds with mod code, and ports of mods we have no permission to publish all live outside the repo: in the user's pipeline work directory, or for our own development in - `~/code/horizon-parity/modports/` (dev-only, never pushed). Ports are written from - observed behaviour and the harness, with decompiled code as a local reference only. The - repository holds the pipeline, the API, metadata (ids, names, versions, licences, counts) + `~/code/horizon-parity/modports/` (dev-only, never pushed). A mod's code is never + decompiled, read, quoted or paraphrased, by people or agents. Ports are written only from + the mod's observed behaviour (the oracle and the harness), its own data files and its + public documentation. The repository holds the pipeline, the API, metadata (ids, names, versions, licences, counts) and test fixtures we write ourselves. 5. **Contacting authors or publishing any port is Ludovic's call.** Agents ask first. @@ -177,7 +177,7 @@ frame, chat and command hooks. Server mods do not run yet. What a content pack n | Entities: types, attributes, AI goals and brains, spawning | — | deep (87) | 0.5 | `entity` interface, AI in the mod with vanilla goals reusable | | Custom renderers and animated models (block-entity, entity, item; GeckoLib's format) | — | deep | 0.5 | data models (Bedrock geometry + animations), plus a budgeted draw-list interface for the rest | | Dimensions: dimension types, levels, chunk generators | — | about 10 (Twilight Forest, Aether, Ad Astra, Undergarden, ...) | 0.5 | JSON part with worldgen; custom generators as mod code | -| New hooks into vanilla behaviour (what Mixins do) | — | deep | as asked | each becomes a named hook, added for everyone ([java-mod-compat.md](research/java-mod-compat.md#3-a-porting-guide-outline)) | +| New hooks into vanilla behaviour (what Mixins do) | — | deep | as asked | each becomes a named hook, added for everyone | | Client interfaces: `keys`, `target`, `inventory-read`, `ui`, `render`, `world-read` | planned | client tier (38) | 0.2–0.4 | as in [mods.md](mods.md#planned-interfaces) | Two rules from mods.md stay: **the sandbox** (no files, sockets or processes; memory cap; fuel @@ -711,16 +711,16 @@ For tiers logic, machines and deep, in dependency order, as many agents at once budget allows: - **Input:** the inventory record, the carried-over data, the oracle's exports and traces, the - harness, the Horizon mod API docs, the ports of its dependencies, and the mod's decompiled - code as a local reference (dev-only, in the work directory). + harness, the Horizon mod API docs, the ports of its dependencies, and the mod's public + documentation. Never its code: the agent doesn't decompile, read or quote it. - **Output:** a Rust crate in `mods//port/` built with `cargo horizon-mod build`, and its `.hmod`. - **Loop:** write → build → run the harness → read the differences → fix, until the harness passes or the agent reports what blocks it. Usually that is an API gap, which becomes an issue against the mod API ("needs a scheduled-tick hook"), the way version ports turn differences into work items. -- **Rules:** the agent writes the port from observed behaviour and the harness, not by - transliterating decompiled Java; ports follow the mod's licence ([Licences](#licences)). +- **Rules:** the agent writes the port from observed behaviour, the harness and the mod's + own data files only; ports follow the mod's licence ([Licences](#licences)). Players without an AI agent get the carry-over tiers. A port the licence lets us publish can be downloaded instead of regenerated. diff --git a/docs/mods.md b/docs/mods.md index 4521d312d..bc91c043a 100644 --- a/docs/mods.md +++ b/docs/mods.md @@ -3,8 +3,9 @@ Horizon gets its own mod system, written in Rust and run in a sandbox, plus two ways to bring over what Java players use: native Horizon ports of popular mods and the data-driven parts of packs and mods. Fabric and NeoForge jars do -**not** run as they are: [research/java-mod-compat.md](research/java-mod-compat.md) -explains why and what we do instead. +**not** run as they are: their code calls and patches Minecraft's Java classes, and +Horizon has none. What carries over is the data and assets inside a jar +([modpacks.md](modpacks.md)). Mods are **off and absent by default**. A fresh install has no `horizon-mods` folder, loads no runtime and plays exactly like vanilla 26.3. @@ -102,7 +103,7 @@ name (the mod reads it as `args`; API 0.1 has no way to declare argument types). ### Planned interfaces Each is a new capability, added in a 0.x minor version so older mods keep -loading. Ordered by what the first native ports need ([research](research/java-mod-compat.md#port-first)): +loading. Ordered by what the first native ports need: | Interface | For | Notes | |---|---|---| diff --git a/docs/performance/java-perf-mods.md b/docs/performance/java-perf-mods.md deleted file mode 100644 index 62aab88a1..000000000 --- a/docs/performance/java-perf-mods.md +++ /dev/null @@ -1,130 +0,0 @@ -# Java performance mods: which of their techniques apply to Horizon - -A survey of the techniques used by eight Java Edition performance mods. Asked for in #991, from famescare on Discord. For each technique it gives the vanilla cost it targets, whether Horizon already avoids that cost, and whether it is worth measuring here. - -**Rules.** Techniques only, taken from each mod's public description, documentation and changelogs (linked below). No mod code was read or copied. Several of these mods are LGPL-3.0, and Horizon's code stays its own. Anything Horizon adopts is reimplemented against vanilla 26.3's behaviour. It keeps parity, so the harnesses stay unchanged, and it needs before/after numbers like any other perf PR (`docs/benchmark.md`, `docs/profiling.md`). - -**Why many don't apply.** A large share of these techniques work around costs of the JVM, OpenGL or GLFW rather than costs of the game's rules. Examples are object headers and duplicate objects, boxing, lambdas and Streams, garbage collection, JNI upcalls, immediate-mode GL draws and GL error checks. Horizon is Rust on wgpu and winit, so it never pays those costs. The techniques that do carry over remove work the game's rules don't need: sleeping block entities, skipped searches, and cheaper geometry tests. - -Verdicts: -- **has**: Horizon already avoids the cost, by its design or with a merged change. -- **n/a**: the cost comes from the JVM, OpenGL or GLFW, and Horizon doesn't have it. -- **later**: missing, and worth measuring. These are listed in "Worth measuring" below and in the LATER list. -- **no**: missing, but it would change vanilla's behaviour or look, or the gain here is too small to pay for itself. - -## Lithium (server and shared game logic) - -Source: [Lithium's mixin configuration](https://github.com/CaffeineMC/lithium/blob/develop/lithium-fabric-mixin-config.md). Lithium keeps vanilla's behaviour, which is the same rule Horizon follows. - -| Technique | Vanilla cost | Horizon | Verdict | -| --- | --- | --- | --- | -| Hopper caching, inventory change listeners and hopper sleeping | Every hopper looks up its source and target containers and tries a transfer every tick, even when nothing can move | `game/machines.rs` ticks every hopper on each tick. It checks the block's name and cooldown, then runs the transfer attempt. Nothing is cached or put to sleep | **later** | -| Block entity sleeping (furnaces, brewing stands, campfires, crafters, shulker boxes, sculk) | Idle block entities still tick | `game/block_entity_ticks.rs` ticks every ticker each tick, as vanilla does | **later** | -| Entity collisions: faster block access, chunk caching, the supporting block first | Collision scans read blocks through generic level lookups | Collision scans already run over Horizon's own section arrays (`horizon-world` `block_collision_scan`). A counting cursor that does fewer lookups is open as #659 | **has** (more in #659) | -| Specialised cuboid and empty `VoxelShape`s, precomputed shape arrays | Generic voxel shape intersection and merging | `horizon-core::geometry` has its own immutable shapes and box lists (`docs/collision-geometry.md`) | **has** | -| POI search and portal search | POI lookups go through vanilla's stream-heavy search | Section-indexed POIs (`poi_sections.rs`), searched in vanilla's order | **has** | -| Pathfinding: cached node types per block | Node evaluation reads the same blocks over and over | Not measured. Mob pathfinding costs haven't been profiled against vanilla yet | **later** (profile first) | -| Explosions: one access per block on a ray, skip air, skip exposure work | Each ray step reads the block and the fluid again | `game/explosion.rs` follows vanilla's loop. It hasn't been profiled, and TNT chains are the case to measure | **later** (profile first) | -| Random ticks: roll the chance early, fast block search | Sections with nothing that ticks randomly are still walked | Sections with nothing random-tickable are skipped (`docs/random-tick-eligibility-benchmark.md`) | **has** | -| Tick scheduler: faster collections, packed scheduled ticks | Boxed `ScheduledTick` objects in hash sets | Horizon's own tick heap and index (`horizon-core::ticks`, `docs/scheduled-ticks.md`) | **has** | -| Collections and allocation (fastutil maps, cached `values()`, fewer `BlockPos`, no Streams) | JVM allocation, boxing, GC | Rust value types, no GC | **n/a** | -| Math inlining, a smaller sine table | JIT inlining limits | Compiled and inlined by rustc. Horizon keeps vanilla's `Mth.sin` table where results depend on it | **n/a** | -| Chunk access without debug locks or bounds validation | `PalettedContainer`'s thread-check lock | Horizon doesn't have that lock (`docs/performance/paletted-container-access-results.md`) | **n/a** | -| Palette replacement and compaction | Hash palette resizing on the JVM | Horizon's own paletted containers (`horizon-core::paletted`) | **has** | -| Game event dispatchers made only for sections with a listener | Every section holds a dispatcher | Not checked. This is small next to the items above | **no** (until a profile shows it) | -| Item entity merging grouped by item type (experimental) | Each item entity checks every nearby item to merge with | Not present. It matters only with thousands of dropped items (see #996: fresh worlds drop about twice as many items as vanilla) | **later**, after #996 | -| World border cache, redstone wire double reads, fluid flow reads, framed-map scans | Small repeated reads | Each is minor. Fix them where a profile points at them | **no** | - -## FerriteCore (memory) - -Source: [FerriteCore's summary of its changes](https://github.com/malte0811/FerriteCore/blob/main/summary.md). - -| Technique | Vanilla cost | Horizon | Verdict | -| --- | --- | --- | --- | -| One `FastMap` of neighbour states per block instead of a table per state | About 600 MB of per-state neighbour tables in large packs | Block states are numeric ids with flat tables, with no object per state | **n/a** | -| Compact property storage per state | A per-state `ImmutableMap` | Same as above | **n/a** | -| Cached multipart predicates, shared multipart models | Hundreds of thousands of model and predicate objects | Models are baked once into a shared library (`docs/shared-model-library-benchmark.md`) | **has** | -| Deduplicated baked-quad vertex arrays | Identical `int[]` per quad | Section meshes are built per section from the shared baked models, and no per-quad arrays are kept | **has** | -| Deduplicated shape caches | Duplicate cached `VoxelShape`s per state | One table of distinct shapes, indexed per state (`horizon-world` `CachedCollisionRegistry`) | **has** | -| String deduplication in model locations | Duplicate `String`s from the constructor's round-trips | No `ModelResourceLocation` objects, so the round-trip doesn't exist | **n/a** | -| Partly loaded chunk NBT kept between loading steps | The whole tag is kept | Chunks are decoded into Horizon's own structures | **n/a** | - -Horizon's process memory is tracked against vanilla in `docs/process-memory.md`. - -## ImmediatelyFast (client draw submission) - -Source: [ImmediatelyFast on Modrinth](https://modrinth.com/mod/immediatelyfast). Its gains show only when the CPU is the bottleneck. - -| Technique | Vanilla cost | Horizon | Verdict | -| --- | --- | --- | --- | -| A batching buffer for immediate-mode draws (entities, block entities, particles, text, GUI) | Many small draw calls and uploads | The HUD is one atlas and one batch of quads (`hud.rs`). Entities are grouped by material (`gfx/entity_batch.rs`). Terrain uses indirect multi-draws (`gfx.rs`, `gfx/flat.rs`) | **has** | -| HUD batching, fewer GUI draw calls | One draw per GUI element | Same batch (`hud.rs`) | **has** | -| Fast text lookup, a bigger font atlas | Per-character lookups, atlas thrash with large fonts | Glyphs live in maps in `font.rs`, drawn into the same HUD atlas | **has** | -| One texture for all map item textures | One texture per visible map | Not measured. It matters only with walls of maps in item frames | **later** (with a map-wall scene) | -| Cached closed-chest geometry | Re-tessellating chests each frame | Merged in #728 (Online5277) | **has** | -| Buffered sign text instead of redrawing it each frame | Text redrawn every frame | Sign text is recorded as text calls in the batch. Not measured with sign-heavy scenes | **later** (with a sign-heavy scene) | -| Fewer framebuffer switches | GL state changes between render types | wgpu render passes are fixed by Horizon's pipeline | **n/a** | -| GL error checking off | `glGetError` overhead | No GL. Release builds run without wgpu's backend validation and, outside DirectX 12, without its indirect-draw validation (`gfx.rs` `instance_descriptor`) | **n/a** | - -## Gnetum (HUD rendering) - -Source: [Gnetum on Modrinth](https://modrinth.com/mod/gnetum). - -| Technique | Vanilla cost | Horizon | Verdict | -| --- | --- | --- | --- | -| Draw the HUD into an offscreen buffer in several passes, one pass per frame, and show the last complete buffer | The HUD is redrawn every frame | Horizon redraws its one HUD batch every frame | **no**. The HUD would then update at a lower rate than vanilla's, which changes what players see. Horizon's HUD is already one batch. | -| Adaptive distribution and a separate HUD FPS cap | Same | Same | **no**, for the same reason | - -## ScalableLux (lighting, from Starlight) - -Source: [ScalableLux on Modrinth](https://modrinth.com/mod/scalablelux). - -| Technique | Vanilla cost | Horizon | Verdict | -| --- | --- | --- | --- | -| Starlight's light engine: per-section light arrays and direct propagation queues | Vanilla's light engine is a bottleneck in fast chunk generation | Horizon has its own engine with vanilla's rules (`light.rs`). New chunks are lit on the streaming worker pool, and edits are relit incrementally (`relight_block`). See `docs/performance/lighting-worker-results.md` | **has** | -| Parallel light updates | One light thread | Chunk lighting already runs on several workers during streaming | **has**. Measure the light share of fresh-chunk generation before doing more. | - -## Structure Layout Optimizer (jigsaw structures) - -Source: [Structure Layout Optimizer on Modrinth](https://modrinth.com/mod/structure-layout-optimizer). - -| Technique | Vanilla cost | Horizon | Verdict | -| --- | --- | --- | --- | -| An octree of piece boxes instead of a `VoxelShape` for free space | Voxel shape boolean ops slow down as pieces add up | Free space is a container box minus a list of placed boxes (`worldgen/structure/jigsaw.rs` `Free`). That avoids the voxel shapes, but the scan is linear per test | **has**. An octree pays only for structures with hundreds of pieces. | -| Skip jigsaw blocks that face the structure boundary or another piece | Every parent jigsaw is matched against every child | Not present | **later** (time trial chambers and ancient cities first) | -| Skip pool elements already found not to fit at this spot | Weighted pools repeat an element once per weight point, and it is re-tested each time | Not present. Only the variant that keeps the result identical applies (no deduplication of the list, which changes layouts) | **later** | -| Cull positions outside the chunk before processors run | Whole templates are processed and then mostly discarded | Not checked | **later** (same measurement) | -| Palette-based template storage | Templates keep every block and NBT object | Templates are Horizon's own structures. Memory isn't measured | **no** (until memory shows it) | - -## AsyncParticles (particles) - -Source: [AsyncParticles on Modrinth](https://modrinth.com/mod/asyncparticles). - -| Technique | Vanilla cost | Horizon | Verdict | -| --- | --- | --- | --- | -| Particle culling | Off-screen particles are built anyway | Vanilla already skips quad particles whose position is outside the frustum (`QuadParticleGroup.extractRenderState`, `Frustum.pointInFrustum`). Horizon builds every particle's quad (`particles.rs` `render`). This is a parity gap as well as a cost | **later** (vanilla's frustum point test) | -| Vertex transformation on the GPU | Every particle's quad is built on the CPU and uploaded every frame | Same in Horizon (`particles.rs` `render`). Instancing would upload one record per particle | **later** (measure with 10k+ particles) | -| Particle ticks on another thread | Particle ticks on the client thread | Horizon ticks particles on the game thread | **no**. Vanilla's particle ticks read the level, and racing them against block changes risks parity. Revisit only if a profile shows particle ticks are big. | -| Asynchronous light cache for particles | A light lookup per particle per frame | `particles.rs` looks light up per particle | **later**, with the item above | -| Texture animation one frame late | Animated textures tick on the client tick | Not adopted | **no**, because it changes timing | - -## Ixeris (input and the render thread) - -Source: [Ixeris on Modrinth](https://modrinth.com/project/p8RJPJIC). - -| Technique | Vanilla cost | Horizon | Verdict | -| --- | --- | --- | --- | -| Event polling on the main thread, rendering on its own thread | GLFW event polling and JNI upcalls take time from rendering | winit, without JNI. Input and rendering share the event loop (`game.rs`, `ApplicationHandler`) | **n/a** for the JNI part | -| Buffered raw input (`GetRawInputBuffer`) | One `GetRawInputData` call per event. An 8000 Hz mouse costs a lot on Windows | winit reads `WM_INPUT` one message at a time | **later**. Measure on Windows with a high-polling-rate mouse before changing it. | -| Cached GLFW state | Round trips to the main thread | No GLFW | **n/a** | -| A frame limiter that sleeps, then spins | Imprecise sleeps | Horizon has its own limiter | **no** (no reported problem) | - -## Worth measuring - -Each of these needs a profile or a benchmark first. They are ordered by the expected gain. They are in the LATER list in `~/code/horizon-parity/FEATURES.md`, not filed as issues: - -1. Hopper and block-entity sleeping, with vanilla-identical results (Lithium). Benchmark: a hopper and furnace array. -2. Particles: vanilla's frustum point test (a parity fix too), then GPU-built quads (AsyncParticles). Benchmark: 1,000+ campfires. -3. Jigsaw placement: skip blocked connectors and already-failed elements without changing the layout (Structure Layout Optimizer). Benchmark: trial chambers and ancient cities in fresh worlds, `docs/generation-scan-benchmark.md`. -4. Windows raw input in batches for high-polling-rate mice (Ixeris). Benchmark: an 8000 Hz mouse on Windows. -5. Profile before choosing a technique: mob pathfinding, TNT explosions, item merging after #996, map walls and sign-heavy scenes. diff --git a/docs/research/java-mod-compat.md b/docs/research/java-mod-compat.md deleted file mode 100644 index 8a36b7c48..000000000 --- a/docs/research/java-mod-compat.md +++ /dev/null @@ -1,183 +0,0 @@ -# Java mods and Horizon: what can carry over - -Players ask "will my mods work?". Short answer: **Fabric and NeoForge jars -will not run in Horizon as they are**, and no bridge we could build would -change that for more than a handful of mods. What carries over is the -*content* (data packs, resource packs, the data and assets inside mod jars) -and the *ideas* (native Horizon ports of the mods people use most, written -with [Horizon's own mod system](../mods.md)). This page is the reasoning. - -## How Java mods work - -### Fabric - -- **Fabric Loader** finds jars in `mods/`, reads `fabric.mod.json` - (entrypoints, dependencies, mixin configs, access wideners), and starts the - game through a class loader that can rewrite classes as they load. -- **Mixin** (SpongePowered) is how nearly every mod changes the game: a mixin - class names a Minecraft class (`net.minecraft.client.gui.hud.InGameHud`) and - injects code at points inside its methods (`@Inject(method = "render", at = - @At("TAIL"))`, `@Redirect`, `@ModifyVariable`, `@Overwrite`...). The bytecode - of the vanilla class is rewritten at load time. Access wideners make private - fields and methods public for the same purpose. -- **Fabric API** is a set of modules that give mods events and registries - instead of raw mixins: `fabric-lifecycle-events` (`ClientTickEvents`, - `ServerTickEvents`), `fabric-rendering` (`HudRenderCallback`, - `WorldRenderEvents`), `fabric-command-api`, `fabric-key-binding-api`, - `fabric-networking-api` (custom payloads), `fabric-registry-sync`, - `fabric-resource-loader`, `fabric-screen-api`, `fabric-item-group`, - `fabric-events-interaction` and about forty more. **Fabric API itself is - implemented with mixins** into the same vanilla classes. -- Mods mix the two freely: a mod using Fabric API events still calls - `MinecraftClient.getInstance().player.getBlockPos()`, - `world.getBlockState(pos)`, `DrawContext.drawText(...)`: direct calls into - vanilla's classes, by their mapped names. - -### NeoForge (and Forge) - -- An event bus (`NeoForge.EVENT_BUS`, mod buses) with hundreds of events, - `DeferredRegister` for registries, data attachments and capabilities, - networking payloads, config specs. NeoForge patches vanilla's source - directly (binary patches) to fire its events. -- Mods also use mixins and access transformers, and call vanilla classes - directly, as on Fabric. - -### Data and assets in a jar - -Besides code, a mod jar carries the same folders as packs: -`assets//` (models, blockstates, textures, sounds, language files, -shaders) and `data//` (recipes, loot tables, tags, advancements, -predicates, functions, worldgen: biomes, features, structures). These are -**vanilla formats**, read by vanilla's own code. - -## Why a Java bridge cannot run them - -Horizon is a reimplementation in Rust: there are no `net.minecraft` classes -at runtime, no `InGameHud`, no `ClientPlayerEntity`, no bytecode to rewrite. - -1. **Mixins have nothing to bind to.** A mixin is a patch against a specific - method's bytecode. Without vanilla's classes there is no method, no - injection point and no local variable to modify. This is not a missing - feature we could add: it is the mechanism itself. -2. **Fabric API is mixins too.** A shim Fabric API would have to be - re-implemented from scratch as Java classes calling into Horizon. That part - alone is possible for the event modules. -3. **Mods call vanilla directly.** Even "pure Fabric API" mods use vanilla's - classes for everything else: the player, the world, the block states, the - renderer, the text drawing. Running them means providing Java classes with - vanilla's names and behaviour, backed by Horizon's state: in effect a - second Minecraft, in Java, on top of Horizon. Thousands of classes, in the - mapped names of every mapping set (Yarn, Mojang, intermediary), version by - version. -4. **The JVM itself** would ship with every install (a trimmed JRE is still - 40-60 MB, the full one 150+ MB), start in a second or more, hold a few - hundred MB of heap before any mod loads, and bring garbage-collector pauses - back into the frame: the costs Horizon exists to remove. JNI calls cost - tens to hundreds of nanoseconds each and every one crosses with copies. -5. **No sandbox.** Java mods are fully trusted native-speed code with file - and network access. Horizon's mods are sandboxed and their permissions are - shown; a Java bridge would be the one hole in that. - -**What a bridge could support**, if we ever built one: a stable subset of -Fabric API events (client and server tick, HUD render callback, client -commands, key bindings, custom payloads), with a tiny "vanilla view" API -(player position, held item, chat), for mods written only against that -subset. Surveying the most-downloaded Fabric mods, nearly none qualify: the -popular ones are popular precisely because they reach into rendering, the -world or the UI. The cost (a JVM in the game, a second API surface to keep -stable) is far above the benefit. **We do not build it.** If that changes, -the bridge would sit on top of the WIT API like any other mod runtime, so the -sandbox would still hold. - -## What we do instead - -### 1. Data-driven content (packs and the data in mod jars) - -Horizon already reads vanilla resource packs and data packs (functions, -function tags, predicates today). Extending data-pack coverage to recipes, -loot tables, tags, advancements and worldgen JSON brings most "content" -packs over with no mod code at all, and it is vanilla behaviour, so the parity -harnesses check it. - -For **Fabric/NeoForge jars**, the launcher can offer "import the data": read -`data/` and `assets/` from the jar and load them as a data pack plus a -resource pack. What works: new recipes, loot changes, tags, advancements, -functions, worldgen tweaks that only use vanilla blocks, retextures, language -files. What does not: anything referring to a block, item or entity the jar -registers in Java code (the ids do not exist); the import skips those files and -lists them. With Horizon's planned `registries` interface, a native port can -register those ids and reuse the jar's JSON models, textures, recipes and loot -as they are. - -### 2. Native Horizon ports of popular mods - -Horizon itself covers the performance mods: Sodium, Lithium, FerriteCore, -ModernFix, EntityCulling and ImmediatelyFast exist because vanilla Java is -slow, and Horizon's renderer and simulation are the replacement (see -[benchmark](../benchmark.md)). Zoom (OK Zoomer, Zoomify), FPS and keystroke -overlays, a minimap and auto-reconnect are already built in as -[extras](../extras.md). - -#### Port first - -Ranked by players reached per unit of API work. Each needs the planned -interfaces listed in [mods.md](../mods.md#planned-interfaces). - -| Mod (Java) | What it does | Horizon port | Needs | -|---|---|---|---| -| **Mod Menu** | list mods, open their settings | built in: Options > Mods (done), settings pages next | `ui` | -| **AppleSkin** | food saturation and exhaustion on the HUD and tooltips | SDK mod | `inventory-read`, `ui` | -| **Jade / WTHIT** | what the crosshair points at: block, entity, harvest tool, container contents | SDK mod | `target`, `ui` | -| **JEI / EMI / REI** | browse items, recipes and uses | SDK mod (or built in: Horizon already has the recipe book's data) | `recipes`, `inventory-read`, `ui` | -| **Xaero's Minimap / JourneyMap** | minimap, world map, waypoints | the minimap is an extra; a world map and waypoints as an SDK mod | `world-read`, `render`, `storage` | -| **Litematica** | schematic overlay to build from | SDK mod | `world-read`, `render`, `storage`, `ui` | -| **Mouse Tweaks / Inventory Profiles Next** | drag and sort inventory | built in or SDK mod | `inventory-read` + a sanctioned click API | -| **Continuity / CIT** | connected and custom item textures | built into the renderer (pack formats) | none: resource-pack features | -| **Distant Horizons** | low-detail far terrain | a Horizon renderer feature, not a mod | none | -| **Iris + shader packs** | shaders | a Horizon renderer feature (wgpu shader pipeline), later | none | -| **Simple Voice Chat** | proximity voice | server + client mod | `server`, `net`, audio (later) | - -### 3. A porting guide (outline) - -To be written as `docs/porting-from-fabric.md` once API 0.2 lands: - -1. **Separate logic from glue.** Most of a mod is plain logic (maths, - formatting, data structures): port it to Rust once, unit-test it on the - host. -2. **Map events.** - - | Fabric | Horizon | - |---|---| - | `ClientModInitializer.onInitializeClient` | `Mod::init`, returning a `Registration` | - | `ClientTickEvents.END_CLIENT_TICK` | `Registration::on_tick`, `Mod::tick` | - | `HudRenderCallback` | `hud::set_panel` when the content changes (retained), or `on_frame` | - | `ClientCommandRegistrationCallback` | `Registration::command`, `Mod::command` | - | `ClientReceiveMessageEvents` | `Registration::on_chat`, `Mod::chat` | - | `KeyBindingHelper.registerKeyBinding` | `keys` (planned) | - | `Registry.register(Registries.ITEM, ...)` | `registries` (planned) with JSON in `data/` | - | `ServerPlayNetworking` payloads | `net` (planned) | - | config libraries (Cloth Config, YACL) | `storage` + a `ui` settings page (planned) | - -3. **Replace mixins with hooks.** For each mixin, write down what it observes - or changes. If a WIT hook covers it, use the hook. If not, open an issue - asking for the hook ("I need X before/after Y"): hooks are added to the API - for everyone instead of each mod patching the game. -4. **Declare capabilities** in `horizon-mod.toml`, the least the mod needs. -5. **Reuse assets**: textures, models, language files and recipes move as - they are into the `.hmod`'s `assets/` and `data/` (with `registries`). -6. **Test** with `cargo horizon-mod run` and hot reload; add unit tests for the - logic; publish with a signed `.hmod`. - -## Sources - -- Fabric documentation (`docs.fabricmc.net`): mod structure, Mixin - introduction, Fabric API modules and events. -- SpongePowered Mixin wiki: injection points and how class transformation - works. -- NeoForge documentation (`docs.neoforged.net`): events, registries, - networking. -- Modrinth's most-downloaded Fabric mods, for the porting list. -- Horizon's own Java test mod (`mods/horizon`, built by `tools/build_mod.py` - and the CI `portable-mod` job) is a Fabric mod used by the benchmarks and - parity oracles: it shows the Mixin pattern (`FrameHookMixin`) this page - describes, and stays a developer tool, not a player-facing mod. From 2a9e0ab28dd3684556120e8935f6e8018fa8757f Mon Sep 17 00:00:00 2001 From: rdbtCVS <87152661+rdbtCVS@users.noreply.github.com> Date: Sat, 24 Oct 2026 14:49:46 +0100 Subject: [PATCH 02/41] Remove unused voxel-kernels research experiment Co-Authored-By: OpenAI Codex gpt-6.1-sol --- AGENTS.md | 2 +- Cargo.lock | 4 - Cargo.toml | 2 +- crates/voxel-kernels/Cargo.toml | 13 --- crates/voxel-kernels/src/lib.rs | 136 ------------------------------- crates/voxel-kernels/src/main.rs | 92 --------------------- docs/research-lab.md | 3 - tests/test_lab.py | 2 +- 8 files changed, 3 insertions(+), 251 deletions(-) delete mode 100644 crates/voxel-kernels/Cargo.toml delete mode 100644 crates/voxel-kernels/src/lib.rs delete mode 100644 crates/voxel-kernels/src/main.rs diff --git a/AGENTS.md b/AGENTS.md index 943f2d0e4..a2af25cb4 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -48,7 +48,7 @@ Game workspace (root `Cargo.toml`); `launcher/` and `crates/horizon-telemetry-se | `horizon-port-pack` | Porting Java modpacks ([docs/modpacks.md](docs/modpacks.md)) | | `horizon-theme` | Design tokens and fonts for the launcher, the game's own menus and the website | | `horizon-telemetry`, `horizon-telemetry-server`, `horizon-bench` | Opt-in data collection, its ingest server, the benchmark report format | -| `horizon-dualsense`, `horizon-threads`, `horizon-release-sign`, `horizon-bots`, `voxel-kernels`, `xtask` | DualSense output, thread priorities, signed release checksums, load-test bots, occupancy kernels, repo tasks | +| `horizon-dualsense`, `horizon-threads`, `horizon-release-sign`, `horizon-bots`, `xtask` | DualSense output, thread priorities, signed release checksums, load-test bots, repo tasks | | `launcher/` | GPUI launcher: `main.rs` (UI), `backend.rs` (installs, updates), `cli.rs`, `mcp.rs`, `*_page.rs` | Other top-level folders: `tools/` (harnesses and dev scripts), `tests/` (Python unit tests: `python3 -m diff --git a/Cargo.lock b/Cargo.lock index 1a1b288c9..550d3eb4b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3968,10 +3968,6 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" -[[package]] -name = "voxel-kernels" -version = "0.1.0" - [[package]] name = "walkdir" version = "2.5.0" diff --git a/Cargo.toml b/Cargo.toml index bc67ec748..7ffc68471 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [workspace] exclude = ["launcher", "crates/horizon-telemetry-server", "third_party/wgpu-hal"] -members = ["crates/horizon-core", "crates/horizon-dualsense", "crates/horizon-threads", "crates/horizon-world", "crates/voxel-kernels", "crates/horizon-client", "crates/horizon-server-files", "crates/horizon-server", "crates/horizon-bots", "crates/horizon-assets", "crates/horizon-protocol", "crates/horizon-theme", "crates/horizon-shaderpack", "crates/horizon-prepare", "crates/horizon-telemetry", "crates/horizon-bench", "crates/horizon-friends", "crates/horizon-relay", "crates/horizon-mod-sdk", "crates/horizon-mods", "crates/horizon-mods/tests/guest", "crates/cargo-horizon-mod", "crates/horizon-port-pack", "crates/horizon-release-sign", "crates/horizon-mod-sdk/examples/coords-hud", "xtask"] +members = ["crates/horizon-core", "crates/horizon-dualsense", "crates/horizon-threads", "crates/horizon-world", "crates/horizon-client", "crates/horizon-server-files", "crates/horizon-server", "crates/horizon-bots", "crates/horizon-assets", "crates/horizon-protocol", "crates/horizon-theme", "crates/horizon-shaderpack", "crates/horizon-prepare", "crates/horizon-telemetry", "crates/horizon-bench", "crates/horizon-friends", "crates/horizon-relay", "crates/horizon-mod-sdk", "crates/horizon-mods", "crates/horizon-mods/tests/guest", "crates/cargo-horizon-mod", "crates/horizon-port-pack", "crates/horizon-release-sign", "crates/horizon-mod-sdk/examples/coords-hud", "xtask"] resolver = "2" [workspace.package] diff --git a/crates/voxel-kernels/Cargo.toml b/crates/voxel-kernels/Cargo.toml deleted file mode 100644 index f4582e0d1..000000000 --- a/crates/voxel-kernels/Cargo.toml +++ /dev/null @@ -1,13 +0,0 @@ -[package] -name = "voxel-kernels" -version = "0.1.0" -edition.workspace = true -license.workspace = true -rust-version.workspace = true - -[[bin]] -name = "voxel-bench" -path = "src/main.rs" - -[lints] -workspace = true diff --git a/crates/voxel-kernels/src/lib.rs b/crates/voxel-kernels/src/lib.rs deleted file mode 100644 index caf72879f..000000000 --- a/crates/voxel-kernels/src/lib.rs +++ /dev/null @@ -1,136 +0,0 @@ -//! Original occupancy kernels for synthetic 16³ voxel sections. -//! A set bit means an opaque full cube; all neighbors outside the section are air. -//! This is not a Minecraft renderer or a model of partial/transparent blocks. - -pub const SIDE: usize = 16; - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct Section { - rows: [u16; SIDE * SIDE], -} - -impl Default for Section { - fn default() -> Self { - Self { - rows: [0; SIDE * SIDE], - } - } -} - -impl Section { - pub fn from_fn(mut occupied: impl FnMut(usize, usize, usize) -> bool) -> Self { - let mut section = Self::default(); - for z in 0..SIDE { - for y in 0..SIDE { - for x in 0..SIDE { - if occupied(x, y, z) { - section.rows[z * SIDE + y] |= 1 << x; - } - } - } - } - section - } - - pub fn occupied(&self, x: usize, y: usize, z: usize) -> bool { - assert!(x < SIDE && y < SIDE && z < SIDE); - self.rows[z * SIDE + y] & (1 << x) != 0 - } - - /// Simple per-voxel reference. Count each opaque cube's exposed unit faces. - pub fn exposed_faces_scalar(&self) -> u32 { - let mut faces = 0; - for z in 0..SIDE { - for y in 0..SIDE { - for x in 0..SIDE { - if !self.occupied(x, y, z) { - continue; - } - faces += u32::from(x == 0 || !self.occupied(x - 1, y, z)); - faces += u32::from(x + 1 == SIDE || !self.occupied(x + 1, y, z)); - faces += u32::from(y == 0 || !self.occupied(x, y - 1, z)); - faces += u32::from(y + 1 == SIDE || !self.occupied(x, y + 1, z)); - faces += u32::from(z == 0 || !self.occupied(x, y, z - 1)); - faces += u32::from(z + 1 == SIDE || !self.occupied(x, y, z + 1)); - } - } - } - faces - } - - /// Evaluate sixteen voxels at once with row masks and population counts. - pub fn exposed_faces_bitset(&self) -> u32 { - let mut faces = 0; - for z in 0..SIDE { - for y in 0..SIDE { - let i = z * SIDE + y; - let row = self.rows[i]; - let below = if y == 0 { 0 } else { self.rows[i - 1] }; - let above = if y + 1 == SIDE { 0 } else { self.rows[i + 1] }; - let back = if z == 0 { 0 } else { self.rows[i - SIDE] }; - let front = if z + 1 == SIDE { - 0 - } else { - self.rows[i + SIDE] - }; - faces += (row & !(row << 1)).count_ones(); - faces += (row & !(row >> 1)).count_ones(); - faces += (row & !below).count_ones(); - faces += (row & !above).count_ones(); - faces += (row & !back).count_ones(); - faces += (row & !front).count_ones(); - } - } - faces - } -} - -#[cfg(test)] -mod tests { - use super::*; - - fn check(section: &Section, expected: u32) { - assert_eq!(section.exposed_faces_scalar(), expected); - assert_eq!(section.exposed_faces_bitset(), expected); - } - - #[test] - fn known_geometry() { - check(&Section::default(), 0); - check(&Section::from_fn(|_, _, _| true), 6 * 16 * 16); - check(&Section::from_fn(|x, y, z| x == 3 && y == 4 && z == 5), 6); - check(&Section::from_fn(|x, y, z| x < 2 && y == 4 && z == 5), 10); - check( - &Section::from_fn(|x, y, z| (x + y + z) % 2 == 0), - 4096 / 2 * 6, - ); - } - - #[test] - fn every_single_voxel_including_boundaries() { - for i in 0..SIDE.pow(3) { - let mut section = Section::default(); - section.rows[i / SIDE] = 1 << (i % SIDE); - check(§ion, 6); - } - } - - #[test] - fn random_occupancy_matches_reference_at_multiple_densities() { - let mut state = 0x1234_5678_9abc_def0_u64; - for threshold in [0, 1, 16, 64, 128, 192, 254, 255, 256] { - for _ in 0..32 { - let section = Section::from_fn(|_, _, _| { - state ^= state << 13; - state ^= state >> 7; - state ^= state << 17; - (state & 255) < threshold - }); - assert_eq!( - section.exposed_faces_scalar(), - section.exposed_faces_bitset() - ); - } - } - } -} diff --git a/crates/voxel-kernels/src/main.rs b/crates/voxel-kernels/src/main.rs deleted file mode 100644 index e2443958b..000000000 --- a/crates/voxel-kernels/src/main.rs +++ /dev/null @@ -1,92 +0,0 @@ -use std::{hint::black_box, time::Instant}; -use voxel_kernels::Section; - -fn sample(corpus: &[Section], kernel: fn(&Section) -> u32, repeats: usize) -> f64 { - let start = Instant::now(); - let mut checksum = 0_u64; - for _ in 0..repeats { - for section in corpus { - checksum = checksum.wrapping_add(u64::from(kernel(black_box(section)))); - } - } - black_box(checksum); - start.elapsed().as_nanos() as f64 / (corpus.len() * repeats) as f64 -} - -fn median(samples: &mut [f64]) -> f64 { - samples.sort_by(f64::total_cmp); - samples[samples.len() / 2] -} - -fn main() { - let repeats: usize = match std::env::args().nth(1) { - None => 100, - Some(value) => match value.parse() { - Ok(n) if n > 0 => n, - _ => { - eprintln!("usage: voxel-bench [positive repeat count]"); - std::process::exit(2); - } - }, - }; - let mut rng = 0x7065_7273_6f6e_616c_u64; - let random = |rng: &mut u64| { - *rng ^= *rng << 13; - *rng ^= *rng >> 7; - *rng ^= *rng << 17; - *rng - }; - println!( - "workload,sections,samples,repeats,scalar_ns_per_section,bitset_ns_per_section,speedup" - ); - for workload in [ - "empty", - "solid", - "flat", - "checkerboard", - "random_10", - "random_50", - "random_90", - ] { - let corpus: Vec<_> = (0..32) - .map(|i| { - Section::from_fn(|x, y, z| match workload { - "empty" => false, - "solid" => true, - "flat" => y < 1 + i % 15, - "checkerboard" => (x + y + z + i) % 2 == 0, - "random_10" => random(&mut rng) % 100 < 10, - "random_50" => random(&mut rng) % 100 < 50, - "random_90" => random(&mut rng) % 100 < 90, - _ => unreachable!(), - }) - }) - .collect(); - for section in &corpus { - assert_eq!( - section.exposed_faces_scalar(), - section.exposed_faces_bitset() - ); - } - for kernel in [Section::exposed_faces_scalar, Section::exposed_faces_bitset] { - sample(&corpus, kernel, 10); - } - let mut scalar = Vec::new(); - let mut bitset = Vec::new(); - for trial in 0..21 { - if trial % 2 == 0 { - scalar.push(sample(&corpus, Section::exposed_faces_scalar, repeats)); - bitset.push(sample(&corpus, Section::exposed_faces_bitset, repeats)); - } else { - bitset.push(sample(&corpus, Section::exposed_faces_bitset, repeats)); - scalar.push(sample(&corpus, Section::exposed_faces_scalar, repeats)); - } - } - let scalar = median(&mut scalar); - let bitset = median(&mut bitset); - println!( - "{workload},32,21,{repeats},{scalar:.2},{bitset:.2},{:.2}", - scalar / bitset - ); - } -} diff --git a/docs/research-lab.md b/docs/research-lab.md index ae147364e..f7aa969d5 100644 --- a/docs/research-lab.md +++ b/docs/research-lab.md @@ -75,11 +75,8 @@ separate implementation. [Geometry and collision scope](collision-geometry.md). ```sh cargo test --workspace -cargo run --release --bin voxel-bench -- 100 > .local/voxel-benchmark.csv ``` -The dependency-free occupancy experiment compares scalar and bitset exposed-face counting for synthetic 16³ sections. It has no real mesh, material/AO/light work, neighboring-section context, JNI bridge or game integration. Speedups are kernel-only, not Minecraft FPS. - Public branding and new mod downloads use **Horizon**. The existing Fabric mod ID and `dev.horizonlab` namespace remain stable for installed instances and measurement compatibility. The installer backs up both old `horizon-lab-*.jar` diff --git a/tests/test_lab.py b/tests/test_lab.py index 648160ce1..f10a5fd55 100644 --- a/tests/test_lab.py +++ b/tests/test_lab.py @@ -172,7 +172,7 @@ class PublicationTests(unittest.TestCase): def test_private_paths_rejected(self): for path in [".local/minecraft/source.java", "decompiled/Foo.txt", "game.JAR", "capture.jfr", "source/Foo.java"]: self.assertTrue(check_public_tree.prohibited(path), path) - for path in ["tools/lab.py", "crates/voxel-kernels/src/lib.rs", "Cargo.lock", "docs/analysis.md", + for path in ["tools/lab.py", "crates/horizon-core/src/lib.rs", "Cargo.lock", "docs/analysis.md", "mods/horizon/src/main/java/dev/horizonlab/HorizonLab.java", "mods/horizon-lab/src/main/java/dev/horizonlab/HorizonLab.java"]: self.assertFalse(check_public_tree.prohibited(path), path) From 06ec15f3a00ca5135874eb5cc1bc79ba12f84905 Mon Sep 17 00:00:00 2001 From: rdbtCVS <87152661+rdbtCVS@users.noreply.github.com> Date: Sat, 24 Oct 2026 14:50:55 +0100 Subject: [PATCH 03/41] Retire the legacy Java optimization mod and performance packs Preserve Java parity oracles and comparison benchmark instrumentation. Move their remaining dependency locks under tools/benchmark-deps and remove legacy product packaging, fixtures, and CI checks. Co-Authored-By: OpenAI Codex gpt-6.1-sol --- .github/workflows/ci.yml | 96 +--- .../server-four-way-2026-10-19/README.md | 4 +- docs/README.md | 2 +- docs/architecture.md | 2 +- docs/benchmark.md | 2 +- docs/collectors.md | 48 -- docs/java-packs.md | 118 +---- docs/performance/java-perf-mods.md | 2 +- docs/process-memory.md | 53 --- docs/profiling.md | 16 +- docs/research/java-mod-compat.md | 7 +- docs/server.md | 2 +- docs/tick-state.md | 26 - docs/vulkan.md | 32 -- .../main/java/dev/horizonlab/HorizonLab.java | 78 --- .../dev/horizonlab/budget/DebounceWait.java | 14 - .../dev/horizonlab/budget/DispatchRetry.java | 14 - .../dev/horizonlab/budget/TerrainBudget.java | 38 -- .../horizonlab/budget/TerrainReadWait.java | 27 -- .../horizonlab/budget/WakeableIdleWait.java | 15 - .../dev/horizonlab/budget/WorkerPacer.java | 36 -- .../client/DistantTerrainBudget.java | 74 --- .../client/DistantTerrainSettings.java | 46 -- .../dev/horizonlab/client/HorizonClient.java | 212 --------- .../dev/horizonlab/client/ShaderSettings.java | 28 -- .../geometry/RelativeTerrainPosition.java | 30 -- .../metrics/ProcessMemoryMonitor.java | 53 --- .../dev/horizonlab/metrics/Recording.java | 159 ------- .../java/dev/horizonlab/metrics/Reports.java | 43 -- .../horizonlab/metrics/ResidentMemory.java | 20 - .../dev/horizonlab/metrics/SampleStats.java | 36 -- .../dev/horizonlab/metrics/ServerContext.java | 27 -- .../horizonlab/metrics/SoftwareContext.java | 21 - .../dev/horizonlab/metrics/TickState.java | 76 --- .../mixin/CullingEmptyWhitelistMixin.java | 19 - .../mixin/CullingRayScratchMixin.java | 28 -- .../horizonlab/mixin/DhDebounceWaitMixin.java | 77 --- .../mixin/DhPriorityDispatchMixin.java | 76 --- .../horizonlab/mixin/DhQuadReuseMixin.java | 31 -- .../mixin/DhRelativePositionMixin.java | 16 - .../mixin/DhTerrainReadWaitMixin.java | 18 - .../horizonlab/mixin/DhTickDedupMixin.java | 30 -- .../horizonlab/mixin/DhWorkerPacingMixin.java | 42 -- .../java/dev/horizonlab/mixin/FrameMixin.java | 16 - .../horizonlab/mixin/HorizonMixinPlugin.java | 193 -------- .../dev/horizonlab/mixin/ServerTickMixin.java | 25 - .../assets/horizonlab/lang/en_us.json | 3 - .../src/main/resources/fabric.mod.json | 29 -- .../src/main/resources/horizonlab.mixins.json | 24 - .../budget/DebounceQueueBenchmark.java | 50 -- .../horizonlab/budget/DebounceWaitTest.java | 30 -- .../horizonlab/budget/DispatchRetryTest.java | 23 - .../horizonlab/budget/IdleQueueBenchmark.java | 63 --- .../horizonlab/budget/TerrainBudgetTest.java | 30 -- .../budget/WakeableIdleWaitTest.java | 70 --- .../horizonlab/budget/WorkerPacerTest.java | 48 -- .../budget/WorkerPacingBenchmark.java | 64 --- .../client/DistantTerrainBudgetTest.java | 56 --- .../client/DistantTerrainSettingsTest.java | 59 --- .../dispatch/CullingEmptyIntegration.java | 100 ---- .../dispatch/CullingRayIntegration.java | 148 ------ .../dispatch/DispatchIntegration.java | 108 ----- .../dispatch/PositionIntegration.java | 121 ----- .../dispatch/QuadReuseIntegration.java | 99 ---- .../dispatch/QuadSizeIntegration.java | 31 -- .../dispatch/TerrainReadIntegration.java | 155 ------ .../dispatch/TickDedupIntegration.java | 87 ---- .../dispatch/fixture/CullingRayAccess.java | 16 - .../fixture/GraphicsBackendTraceMixin.java | 58 --- .../geometry/RelativeTerrainPositionTest.java | 52 -- .../metrics/ClientGraphicsIntegration.java | 155 ------ .../dev/horizonlab/metrics/MetricsTest.java | 102 ---- .../metrics/ObjectHeaderBenchmark.java | 59 --- .../horizonlab/metrics/ObjectSizeAgent.java | 7 - .../metrics/ResidentMemoryTest.java | 59 --- .../metrics/ServerGraphicsIntegration.java | 69 --- .../metrics/TickStateIntegration.java | 70 --- packs/Mac-Start.command | 32 -- packs/START-HERE.txt | 195 -------- packs/config/cinematic.toml | 18 - packs/config/far-distance.toml | 17 - packs/config/m2-balanced.toml | 18 - packs/config/vulkan.toml | 18 - packs/mods.lock.json | 323 ------------- tests/test_fixtures.py | 119 ----- tests/test_packs.py | 447 ------------------ tests/test_runtime.py | 48 ++ tools/Compare-Benchmarks.html | 299 ------------ tools/benchmark-deps/README.md | 10 + .../benchmark-deps}/fabric.lock.json | 0 tools/benchmark-deps/mods.lock.json | 134 ++++++ tools/build_mod.py | 268 ----------- tools/build_pack.py | 192 -------- tools/build_release.py | 42 -- tools/compare_benchmarks.py | 195 -------- tools/perf_bench.py | 8 +- tools/runtime.py | 103 +--- tools/server_bench.py | 8 +- tools/test_client_graphics.py | 198 -------- tools/test_culling_empty.py | 69 --- tools/test_culling_rays.py | 105 ---- tools/test_dispatch.py | 87 ---- tools/test_quad_reuse.py | 77 --- tools/test_quad_sizes.py | 99 ---- tools/test_server_graphics.py | 75 --- tools/test_terrain_position.py | 77 --- tools/test_terrain_read.py | 71 --- tools/test_tick_dedup.py | 68 --- 108 files changed, 232 insertions(+), 7261 deletions(-) delete mode 100644 docs/collectors.md delete mode 100644 docs/process-memory.md delete mode 100644 docs/tick-state.md delete mode 100644 docs/vulkan.md delete mode 100644 mods/horizon/src/main/java/dev/horizonlab/HorizonLab.java delete mode 100644 mods/horizon/src/main/java/dev/horizonlab/budget/DebounceWait.java delete mode 100644 mods/horizon/src/main/java/dev/horizonlab/budget/DispatchRetry.java delete mode 100644 mods/horizon/src/main/java/dev/horizonlab/budget/TerrainBudget.java delete mode 100644 mods/horizon/src/main/java/dev/horizonlab/budget/TerrainReadWait.java delete mode 100644 mods/horizon/src/main/java/dev/horizonlab/budget/WakeableIdleWait.java delete mode 100644 mods/horizon/src/main/java/dev/horizonlab/budget/WorkerPacer.java delete mode 100644 mods/horizon/src/main/java/dev/horizonlab/client/DistantTerrainBudget.java delete mode 100644 mods/horizon/src/main/java/dev/horizonlab/client/DistantTerrainSettings.java delete mode 100644 mods/horizon/src/main/java/dev/horizonlab/client/HorizonClient.java delete mode 100644 mods/horizon/src/main/java/dev/horizonlab/client/ShaderSettings.java delete mode 100644 mods/horizon/src/main/java/dev/horizonlab/geometry/RelativeTerrainPosition.java delete mode 100644 mods/horizon/src/main/java/dev/horizonlab/metrics/ProcessMemoryMonitor.java delete mode 100644 mods/horizon/src/main/java/dev/horizonlab/metrics/Recording.java delete mode 100644 mods/horizon/src/main/java/dev/horizonlab/metrics/Reports.java delete mode 100644 mods/horizon/src/main/java/dev/horizonlab/metrics/ResidentMemory.java delete mode 100644 mods/horizon/src/main/java/dev/horizonlab/metrics/SampleStats.java delete mode 100644 mods/horizon/src/main/java/dev/horizonlab/metrics/ServerContext.java delete mode 100644 mods/horizon/src/main/java/dev/horizonlab/metrics/SoftwareContext.java delete mode 100644 mods/horizon/src/main/java/dev/horizonlab/metrics/TickState.java delete mode 100644 mods/horizon/src/main/java/dev/horizonlab/mixin/CullingEmptyWhitelistMixin.java delete mode 100644 mods/horizon/src/main/java/dev/horizonlab/mixin/CullingRayScratchMixin.java delete mode 100644 mods/horizon/src/main/java/dev/horizonlab/mixin/DhDebounceWaitMixin.java delete mode 100644 mods/horizon/src/main/java/dev/horizonlab/mixin/DhPriorityDispatchMixin.java delete mode 100644 mods/horizon/src/main/java/dev/horizonlab/mixin/DhQuadReuseMixin.java delete mode 100644 mods/horizon/src/main/java/dev/horizonlab/mixin/DhRelativePositionMixin.java delete mode 100644 mods/horizon/src/main/java/dev/horizonlab/mixin/DhTerrainReadWaitMixin.java delete mode 100644 mods/horizon/src/main/java/dev/horizonlab/mixin/DhTickDedupMixin.java delete mode 100644 mods/horizon/src/main/java/dev/horizonlab/mixin/DhWorkerPacingMixin.java delete mode 100644 mods/horizon/src/main/java/dev/horizonlab/mixin/FrameMixin.java delete mode 100644 mods/horizon/src/main/java/dev/horizonlab/mixin/HorizonMixinPlugin.java delete mode 100644 mods/horizon/src/main/java/dev/horizonlab/mixin/ServerTickMixin.java delete mode 100644 mods/horizon/src/main/resources/assets/horizonlab/lang/en_us.json delete mode 100644 mods/horizon/src/main/resources/fabric.mod.json delete mode 100644 mods/horizon/src/main/resources/horizonlab.mixins.json delete mode 100644 mods/horizon/src/test/java/dev/horizonlab/budget/DebounceQueueBenchmark.java delete mode 100644 mods/horizon/src/test/java/dev/horizonlab/budget/DebounceWaitTest.java delete mode 100644 mods/horizon/src/test/java/dev/horizonlab/budget/DispatchRetryTest.java delete mode 100644 mods/horizon/src/test/java/dev/horizonlab/budget/IdleQueueBenchmark.java delete mode 100644 mods/horizon/src/test/java/dev/horizonlab/budget/TerrainBudgetTest.java delete mode 100644 mods/horizon/src/test/java/dev/horizonlab/budget/WakeableIdleWaitTest.java delete mode 100644 mods/horizon/src/test/java/dev/horizonlab/budget/WorkerPacerTest.java delete mode 100644 mods/horizon/src/test/java/dev/horizonlab/budget/WorkerPacingBenchmark.java delete mode 100644 mods/horizon/src/test/java/dev/horizonlab/client/DistantTerrainBudgetTest.java delete mode 100644 mods/horizon/src/test/java/dev/horizonlab/client/DistantTerrainSettingsTest.java delete mode 100644 mods/horizon/src/test/java/dev/horizonlab/dispatch/CullingEmptyIntegration.java delete mode 100644 mods/horizon/src/test/java/dev/horizonlab/dispatch/CullingRayIntegration.java delete mode 100644 mods/horizon/src/test/java/dev/horizonlab/dispatch/DispatchIntegration.java delete mode 100644 mods/horizon/src/test/java/dev/horizonlab/dispatch/PositionIntegration.java delete mode 100644 mods/horizon/src/test/java/dev/horizonlab/dispatch/QuadReuseIntegration.java delete mode 100644 mods/horizon/src/test/java/dev/horizonlab/dispatch/QuadSizeIntegration.java delete mode 100644 mods/horizon/src/test/java/dev/horizonlab/dispatch/TerrainReadIntegration.java delete mode 100644 mods/horizon/src/test/java/dev/horizonlab/dispatch/TickDedupIntegration.java delete mode 100644 mods/horizon/src/test/java/dev/horizonlab/dispatch/fixture/CullingRayAccess.java delete mode 100644 mods/horizon/src/test/java/dev/horizonlab/dispatch/fixture/GraphicsBackendTraceMixin.java delete mode 100644 mods/horizon/src/test/java/dev/horizonlab/geometry/RelativeTerrainPositionTest.java delete mode 100644 mods/horizon/src/test/java/dev/horizonlab/metrics/ClientGraphicsIntegration.java delete mode 100644 mods/horizon/src/test/java/dev/horizonlab/metrics/MetricsTest.java delete mode 100644 mods/horizon/src/test/java/dev/horizonlab/metrics/ObjectHeaderBenchmark.java delete mode 100644 mods/horizon/src/test/java/dev/horizonlab/metrics/ObjectSizeAgent.java delete mode 100644 mods/horizon/src/test/java/dev/horizonlab/metrics/ResidentMemoryTest.java delete mode 100644 mods/horizon/src/test/java/dev/horizonlab/metrics/ServerGraphicsIntegration.java delete mode 100644 mods/horizon/src/test/java/dev/horizonlab/metrics/TickStateIntegration.java delete mode 100644 packs/Mac-Start.command delete mode 100644 packs/START-HERE.txt delete mode 100644 packs/config/cinematic.toml delete mode 100644 packs/config/far-distance.toml delete mode 100644 packs/config/m2-balanced.toml delete mode 100644 packs/config/vulkan.toml delete mode 100644 packs/mods.lock.json delete mode 100644 tests/test_fixtures.py delete mode 100644 tests/test_packs.py create mode 100644 tests/test_runtime.py delete mode 100644 tools/Compare-Benchmarks.html create mode 100644 tools/benchmark-deps/README.md rename {packs => tools/benchmark-deps}/fabric.lock.json (100%) create mode 100644 tools/benchmark-deps/mods.lock.json delete mode 100644 tools/build_mod.py delete mode 100644 tools/build_pack.py delete mode 100644 tools/build_release.py delete mode 100644 tools/compare_benchmarks.py delete mode 100644 tools/test_client_graphics.py delete mode 100644 tools/test_culling_empty.py delete mode 100644 tools/test_culling_rays.py delete mode 100644 tools/test_dispatch.py delete mode 100644 tools/test_quad_reuse.py delete mode 100644 tools/test_quad_sizes.py delete mode 100644 tools/test_server_graphics.py delete mode 100644 tools/test_terrain_position.py delete mode 100644 tools/test_terrain_read.py delete mode 100644 tools/test_tick_dedup.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1dfd43300..98d5b9093 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -430,7 +430,7 @@ jobs: env: RUSTUP_TOOLCHAIN: stable run: python tools/test_float_log_parity.py - portable-mod: + jfr-analysis: if: github.event_name == 'workflow_dispatch' runs-on: [self-hosted, linux, horizon-game] steps: @@ -442,101 +442,7 @@ jobs: with: distribution: temurin java-version: '25' - - name: Compile original mod and test metrics - run: python tools/build_mod.py - name: Verify streaming JFR analysis against the JDK exporter run: python tools/test_jfr.py - name: Verify ZGC pause and allocation event interpretation run: python tools/test_jfr.py --gc zgc - - name: Build pinned portable packs - run: python tools/build_pack.py - - name: Measure isolated worker pacing on this platform - run: python -c "from tools.build_mod import benchmark_workers; benchmark_workers()" - - name: Measure isolated debounce queue polling - run: python -c "from tools.build_mod import benchmark_debounce; benchmark_debounce()" - - name: Measure wakeable idle queue latency - run: python -c "from tools.build_mod import benchmark_idle; benchmark_idle()" - - name: Measure object allocations with compact headers off and on - run: python -c "from tools.build_mod import benchmark_headers; benchmark_headers()" - - name: Test pack integrity and comparisons - run: python -m unittest discover -s tests -v - - name: Start recommended OpenGL macOS clients and verify framebuffer readback - if: runner.os == 'macOS' - run: | - python tools/test_client_graphics.py --native-display - cp dist/client-graphics-smoke.json dist/client-graphics-macos-title.json - python tools/test_client_graphics.py --native-display --world - cp dist/client-graphics-smoke.json dist/client-graphics-macos-world.json - - name: Compile the private dispatch fixture - run: python -c "from tools.build_mod import build_dispatch_fixture; build_dispatch_fixture()" - - name: Compile the private integrated-server graphics fixture - run: python -c "from tools.build_mod import build_integration_fixture; build_integration_fixture('server-graphics', 'dev.horizonlab.metrics.ServerGraphicsIntegration', fixture_dependencies={})" - - name: Compile the private tick-manager state fixture - run: python -c "from tools.build_mod import build_integration_fixture; build_integration_fixture('tick-state', 'dev.horizonlab.metrics.TickStateIntegration')" - - name: Compile the private terrain tick membership fixture - run: python -c "from tools.build_mod import build_tick_dedup_fixture; build_tick_dedup_fixture()" - - name: Compile the private culling whitelist fixture - run: python -c "from tools.build_mod import build_culling_empty_fixture; build_culling_empty_fixture()" - - name: Compile the private culling ray geometry fixture - run: python -c "from tools.build_mod import build_integration_fixture; build_integration_fixture('culling-rays', 'dev.horizonlab.dispatch.CullingRayIntegration', ['entityculling'], ['CullingRayAccess'])" - - name: Compile the private terrain position lookup fixture - run: python -c "from tools.build_mod import build_integration_fixture; build_integration_fixture('terrain-position', 'dev.horizonlab.dispatch.PositionIntegration')" - - name: Compile the private terrain quad reuse fixture - run: python -c "from tools.build_mod import build_integration_fixture; build_integration_fixture('quad-reuse', 'dev.horizonlab.dispatch.QuadReuseIntegration')" - - name: Compile the private object-size agent and terrain quad fixture - run: python -c "from tools.test_quad_sizes import build_agent; from tools.build_mod import build_integration_fixture; build_agent(); build_integration_fixture('quad-size', 'dev.horizonlab.dispatch.QuadSizeIntegration')" - - name: Compile the private terrain read shutdown fixture - run: python -c "from tools.build_mod import build_integration_fixture; build_integration_fixture('terrain-read', 'dev.horizonlab.dispatch.TerrainReadIntegration')" - - name: Install headless graphics libraries for applied-hook checks - if: runner.os == 'Linux' - run: sudo apt-get update && sudo apt-get install -y xvfb libgl1 libegl1 libvulkan1 mesa-vulkan-drivers libasound2t64 libxkbcommon0 - - name: Verify integrated-server graphics report against the actual device - run: python tools/test_server_graphics.py ${{ runner.os == 'macOS' && '--native-display' || '' }} - - name: Exercise the applied dispatcher hooks and upstream rejection path - if: runner.os == 'Linux' - run: python tools/test_dispatch.py - - name: Verify applied tick membership transitions and allocation - if: runner.os == 'Linux' - run: python tools/test_tick_dedup.py - - name: Verify live culling whitelist registration and empty iteration - if: runner.os == 'Linux' - run: python tools/test_culling_empty.py - - name: Compare applied culling ray geometry and provider queries - if: runner.os == 'Linux' - run: python tools/test_culling_rays.py - - name: Verify actual full-data position lookup equivalence - if: runner.os == 'Linux' - run: python tools/test_terrain_position.py - - name: Verify actual terrain quad geometry and allocation - if: runner.os == 'Linux' - run: python tools/test_quad_reuse.py - - name: Measure actual builder and quad shallow sizes in both header modes - if: runner.os == 'Linux' - run: python tools/test_quad_sizes.py - - name: Verify interrupted DH reads and preserved shared futures - if: runner.os == 'Linux' - run: python tools/test_terrain_read.py - - name: Upload original mod and pack manifests only - uses: actions/upload-artifact@v4 - with: - name: horizon-linux - path: | - dist/*.mrpack - dist/horizon-*.jar - dist/worker-pacing-benchmark.txt - dist/debounce-queue-benchmark.txt - dist/idle-queue-benchmark.txt - dist/object-header-benchmark.txt - dist/dispatch-integration.json - dist/tick-dedup-integration.json - dist/culling-empty-integration.json - dist/terrain-read-integration.json - dist/culling-rays-integration.json - dist/terrain-position-integration.json - dist/quad-reuse-integration.json - dist/quad-size-integration.json - dist/server-graphics-smoke.json - dist/client-graphics-smoke.json - dist/client-graphics-macos-title.json - dist/client-graphics-macos-world.json - retention-days: 3 diff --git a/benchmarks/server-four-way-2026-10-19/README.md b/benchmarks/server-four-way-2026-10-19/README.md index 570f5bb24..7048c2743 100644 --- a/benchmarks/server-four-way-2026-10-19/README.md +++ b/benchmarks/server-four-way-2026-10-19/README.md @@ -131,8 +131,8 @@ average) and reached the ThinkPad over the tailnet. It ran with Paper's defaults except `connection-throttle: -1` in `bukkit.yml`. Every bot connects from one address, and the default throttle (4 s) refused all but the first. Paperclip patched the pinned server jar from its cache. Same Java and heap. -- Fabric + Lithium: the pinned server jar on Fabric loader 0.19.5 (`packs/fabric.lock.json`) with Lithium - `mc26.3-0.26.2-fabric` (`packs/mods.lock.json`, checked against its SHA-512). Lithium needs no Fabric API. +- Fabric + Lithium: the pinned server jar on Fabric loader 0.19.5 (`tools/benchmark-deps/fabric.lock.json`) with Lithium + `mc26.3-0.26.2-fabric` (`tools/benchmark-deps/mods.lock.json`, checked against its SHA-512). Lithium needs no Fabric API. Same Java and heap. The Paper and Fabric jars are dev-only downloads into `.local/server-bench/jars` and are never committed or diff --git a/docs/README.md b/docs/README.md index 2e06b6e89..b93177528 100644 --- a/docs/README.md +++ b/docs/README.md @@ -48,4 +48,4 @@ To play, use [the launcher](../launcher/README.md); the project site is [playhor ## The earlier Java track (no longer developed) -- [Java performance packs](java-packs.md), with [Vulkan on your Mac](vulkan.md), [collector comparisons](collectors.md), [profiling](profiling.md), [process memory](process-memory.md) and [tick state](tick-state.md). The Fabric server starter is retired: [host a server](hosting.md) with horizon-server. +- [Retired Java performance packs](java-packs.md); [JFR analysis for Java comparisons](profiling.md). diff --git a/docs/architecture.md b/docs/architecture.md index 99e9b630e..29e98f2e6 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -27,7 +27,7 @@ file selects ~103. | 10 | Launcher (124) | `launcher/src/` (`main.rs`, `backend.rs`, `cli.rs`, `mcp.rs`, `servers_page.rs`) | `cd launcher && cargo test`; `python3 tools/launcher_mcp_e2e.py` | | 11 | Mob types and species (117) | `L/mobs.rs`, `L/mobs/species.rs`, `L/mobs/data.rs`, `L/mobs/living.rs`, `L/mobs/drops.rs`, `L/mobs/gear.rs` | mob_attributes, entity_attribute, entity_death_loot, mob_ai | | 12 | Guests' actions on a server (98) | `G/lan_actions.rs` (`fn as_guest`, `fn tick_guests`), `G/lan_{attributes,pvp,chat,crafting,stations,trading,mount_menu,waypoints}.rs` | server_player_combat, server_player_damage, server_player_targets, lan_e2e (all) | -| 13 | Modpack porting, mod content (97) | `crates/horizon-port-pack/`, `L/mod_content.rs`, `tools/port_*.py`, `packs/` | `cargo test -p horizon-port-pack`; `python3 tools/mod_port_harness.py` | +| 13 | Modpack porting, mod content (97) | `crates/horizon-port-pack/`, `L/mod_content.rs`, `tools/port_*.py` | `cargo test -p horizon-port-pack`; `python3 tools/mod_port_harness.py` | | 14 | Mob AI (96) | `L/mobs/ai.rs`, `goals.rs`, `brain.rs`, `*_brain.rs`, `nav.rs`, `path.rs`, `control.rs` | mob_ai (probe `examples/mob_ai_probe.rs`; known failure on main: drowned_*, #290) | | 15 | Items and item use (96) | `L/items.rs`, `L/item_data.rs`, `G/hand.rs`, `G/consume.rs`, `G/potions.rs`, `L/effects.rs`; game.rs `fn use_hand`, `fn use_item` | use_item_on, item_use, item_use_lifecycle, consumable, consume_effect, status_effect | | 16 | Options and settings screens (93) | `L/options_ui.rs`, `G/options.rs`, `L/settings.rs`, `L/options_file.rs`, `L/graphics_ui.rs`, `L/graphics.rs` | options, toggle_keys | diff --git a/docs/benchmark.md b/docs/benchmark.md index 8bcc432e0..ca6c12e11 100644 --- a/docs/benchmark.md +++ b/docs/benchmark.md @@ -21,7 +21,7 @@ One game is started per window size (1280x720 and 1920x1080 by default) and rend - **Warm-up excluded.** Each scene's flight starts only after its chunks are loaded and drawn and 2 seconds have passed; the load itself is reported separately. JIT warm-up of the JVM during the first scene is part of vanilla's real behaviour and is not removed. - **The same statistics code.** Horizon returns its raw frame times (`horizon-ctl fly ... raw`) and the vanilla mod writes its own; `stats()` in the driver computes every figure for both. - **Vanilla as players run it.** Your own 26.3 client jar and libraries (as the official launcher installs them for your OS), the official launcher's default JVM arguments (`-Xmx2G` and its G1 options), Java 25. The game's assets index is not used, so neither game plays sound (Horizon runs with `HORIZON_NO_AUDIO=1`, vanilla with OpenAL's null device); textures, models and shaders come from the client jar as usual. -- **Performance mods.** `--game vanilla-mods` adds the Fabric builds for 26.3 of Sodium, Lithium, FerriteCore and ImmediatelyFast, the versions pinned with their hashes in `packs/mods.lock.json` (downloaded from Modrinth, never committed), plus Fabric API if one of them needs it. +- **Performance mods.** `--game vanilla-mods` adds the Fabric builds for 26.3 of Sodium, Lithium, FerriteCore and ImmediatelyFast, the versions pinned with their hashes in `tools/benchmark-deps/mods.lock.json` (downloaded from Modrinth, never committed), plus Fabric API if one of them needs it. ## Known caveats diff --git a/docs/collectors.md b/docs/collectors.md deleted file mode 100644 index 98b497328..000000000 --- a/docs/collectors.md +++ /dev/null @@ -1,48 +0,0 @@ -# Compare Java collectors - -Use the balanced pack first with the launcher's existing JVM policy. Java 25 -HotSpot supports explicit G1 (`-XX:+UseG1GC`) and ZGC (`-XX:+UseZGC`) selections; -the [Java command reference](https://docs.oracle.com/en/java/javase/25/docs/specs/man/java.html) -documents both. A collector choice can change CPU, heap headroom, collection -behavior and frame pacing. Lower pauses alone do not establish lower RAM or -higher FPS. The packs do not automatically select a collector. - -For a Mac comparison, duplicate one instance. In Edit > Settings > Java, enable -the Java Arguments override checkbox. Preserve existing non-collector arguments, -but use exactly one collector selector in each instance. Choose G1 in one and -ZGC in the other. Use the same native Java 25 ARM64, maximum/minimum heap, compact -header setting, world copy, resolution, shaders, distances and warmed camera route. -Alternate at least three 120-second runs rather than comparing unrelated captures. -Remove the explicit selector to restore the launcher's policy, then restart. - -The viewer shows the actual collector names captured by the game. Check them: -setting a launcher's text field does not establish which flags reached the VM. -Prism's per-instance Java Arguments and Memory settings have separate override -checkboxes. A global collector selection may need removal or replacement when -creating an explicit per-instance experiment. - -Record FPS, slowest-1% rate, frame spikes, heap and process RSS together. RSS does -not represent all shared GPU memory on Apple Silicon. `gc_time_ms` sums collector -MXBean counters; concurrent cycles and overlapping phases can contribute, so it -is not a measured pause total. Use JFR root pause events for pause analysis. -The streaming reader is checked against the JDK's event exporter with both -default-GC and ZGC fixtures in CI on Windows, Linux and ARM macOS. - -The developer demo launcher accepts `--gc default`, `--gc g1` or `--gc zgc`. -For example: - -```sh -python3 tools/runtime.py demo --instance .local/runtime/collector-test --heap 6G --gc zgc --compact-headers -python3 tools/test_jfr.py --gc zgc -``` - -Prepare demo mods/worlds before comparing. -Options affect only the launched process; they do not change global Java settings. -The collector and compact-header experiments can be tested independently. - -A local 3 GiB, compact-header ZGC client completed a 120-second game recording. -That recording also encountered a distant-terrain rejection storm. Its workload -and cache history differed from G1 captures, so the larger memory footprint and -different frame rate cannot be attributed to the collector alone. The dispatcher -experiment addresses the identified retry loop separately. No M2 collector -performance winner is established. diff --git a/docs/java-packs.md b/docs/java-packs.md index 66ee402d3..36e76faf9 100644 --- a/docs/java-packs.md +++ b/docs/java-packs.md @@ -1,109 +1,15 @@ -# Java performance packs (no longer developed) +# Retired Java performance packs -Before the Rust rewrite, Horizon was a lab for Minecraft Java client performance: pinned Fabric performance packs, distant terrain, an original frame and tick recorder, and client and server starter downloads (the server starter is gone: [horizon-server](hosting.md) replaces it). This track is **no longer developed**. No release downloads are published; build the packs and starters locally with `python3 tools/build_release.py` (see [Build and run](#build-and-run)). This page keeps its documentation. +Before the Rust rewrite, Horizon shipped a Fabric optimization mod, performance pack profiles +and a Prism Launcher Mac starter. That product is retired. Its patches, profiles, builders, +starter, benchmark viewer and product-specific checks have been removed; use Git history +for the former implementation. Horizon's Rust game does not use those Java patches. -## Java packs: Mac starter (out of scope) - -Build **horizon-mac-starter-0.21.0.zip** with `python3 tools/build_release.py` (it lands in `dist/`), extract it and open `Mac-Start.command`. It verifies and installs official universal Prism Launcher in `~/Applications` if needed, then opens the balanced pack import. Sign into Microsoft **inside Prism yourself**. This project needs no credentials. - -Use native **Java 25 ARM64**, maximum heap **6144 MiB**, minimum **512 MiB** on a 16 GB M2 Pro Mac. Prism can download Java automatically. Start at 1280×800 or 1920×1200; full Retina resolution is a larger GPU workload. [START-HERE.txt](../packs/START-HERE.txt) has details. The same `.mrpack` files import into Prism on Windows and Linux. - -Core and balanced passed macOS title and demo-world rendering probes on the hosted -ARM64 software renderer, including readback, saves and clean exits. Physical M2 -GPU performance and macOS 27 were never tested. - -| Profile | Full-detail chunks | Distant LOD radius | Suggested maximum heap | -| --- | ---: | ---: | ---: | -| baseline | 12 | none | 6144 MiB | -| core-performance | 12 | none | 6144 MiB | -| m2-balanced | 12 | 128 | 6144 MiB | -| adaptive (experiment) | 12 | 128 | 6144 MiB | -| worker-pacing (experiment) | 12 | 128 | 6144 MiB | -| adaptive-pacing (experiment) | 12 | 128 | 6144 MiB | -| queue-wait (experimental) | 12 | 128 | 6144 MiB | -| adaptive-queue (experimental) | 12 | 128 | 6144 MiB | -| dispatch-retry (experimental) | 12 | 128 | 6144 MiB | -| tick-dedup (experimental) | 12 | 128 | 6144 MiB | -| culling-empty (experimental) | 12 | 128 | 6144 MiB | -| quad-reuse (experimental) | 12 | 128 | 6144 MiB | -| terrain-position (experimental) | 12 | 128 | 6144 MiB | -| culling-rays (experimental) | 12 | 128 | 6144 MiB | -| foreground-first (combined experiment) | 12 | 128 | 6144 MiB | -| foreground-vulkan (combined experiment) | 12 | 128 | 6144 MiB | -| low-memory | 8 | none | 3072 MiB | -| far-distance | 12 | 256 | 8192 MiB, with sufficient physical RAM | -| cinematic | 12 | 128 | 6144 MiB | -| vulkan | 12 | 128 | 6144 MiB | - -Balanced combines [Sodium](https://modrinth.com/mod/sodium), [Lithium](https://modrinth.com/mod/lithium), [FerriteCore](https://modrinth.com/mod/ferrite-core), [ImmediatelyFast](https://modrinth.com/mod/immediatelyfast), [EntityCulling](https://modrinth.com/mod/entityculling), and [Distant Horizons](https://modrinth.com/mod/distanthorizons). Cinematic adds [Iris](https://modrinth.com/mod/iris) and [Complementary Reimagined](https://modrinth.com/shader/complementary-reimagined), initially disabled. Vulkan independently experiments with the game's own backend and fallback, without Iris. - -Distant LOD is simplified terrain. Nearby blocks retain full detail. Initial generation costs CPU/disk/memory and needs warm-up. DH adjusts clouds, chunk fade and transparency to blend its renderer; assess appearance as well as speed. Shaders cost GPU time and have [Mac-specific limits](https://shaders.properties/current/reference/miscellaneous/macos/). Universal CPU/GPU support and exact vanilla equivalence remain validation goals. Simulation distance stays 12 in every profile; normal 20 TPS rules are retained. - -For the first comparison at the same view distance, use `baseline` versus -`core-performance`: both select 12 full-detail chunks and no distant LOD or shaders. -The latter adds only the pinned core performance mods and leaves every original -controller disabled. Then test balanced separately for its additional distant -terrain. Matching captured settings does not prove pixel-identical rendering. - -The profiles marked *experiment* each isolate one change to Distant Horizons, -EntityCulling or the JVM (adaptive terrain throttling, precise worker pacing, -debounce-queue waits, rejected-dispatch retries, empty culling whitelists, -culling ray scratch vectors, terrain quad reuse, terrain position arithmetic, -tick membership storage, a smaller heap, compact object headers). Each kept the -upstream behaviour it replaced and was measured only on Linux software rendering, -where none showed a gain large enough to enable by default; balanced keeps them -all off. Their write-ups were removed once the Rust rewrite replaced this track; -they remain in Git history. Matched software-rendered pairs showed `core-performance` -14–21% faster than `baseline`, and Vulkan about 1.5% more FPS for about 12% more -CPU; neither is evidence for a Mac GPU. - -To try the game's own Vulkan backend on a Mac, see [Vulkan on your Mac](vulkan.md); -to compare garbage collectors, see [collector comparisons](collectors.md). - -## Measure on your hardware - -Press **F8** during gameplay to start a 120-second recording, or again to stop early. Rebind in Controls if needed. This also works with restricted chat accounts. Alternatively: `/horizon bench