Compare commits

...
51 changed files with 12434 additions and 32 deletions

No files matched your search

+22 -2
View File
@@ -2,7 +2,25 @@
Client-first performance research for Minecraft Java **26.3**, preserving vanilla gameplay rules. Any language or renderer may be used when measured gains justify it. Not affiliated with Mojang or Microsoft.
**First playable release:** pinned Java performance packs, distant terrain, an original frame/tick recorder, and Mac/client/server starter downloads. The M2 Pro/16 GB Mac is the first testing target. A Rust rewrite or custom renderer has not been completed; no M2 FPS improvement has been measured yet.
**Current focus: the physical M2 Pro / 16 GB Mac.** Initial matched renderer tests show 13–15% higher average FPS at 2560×1600 and 70 → 89 FPS in one full Retina terrain-view pair through Vulkan/MoltenVK. Repeated warmed-cache, movement and gameplay validation continue. [Measurements and limits](docs/m2-pro-performance.md). Work on NVIDIA, AMD, Windows and Linux is deferred.
The playable foundation includes pinned Java performance packs, distant terrain,
an original frame/tick recorder and starter downloads. The local `m2-metal`
profile preserves the user's 32 detailed chunks and 128-chunk LOD through the
game's Vulkan/MoltenVK backend. `m2-metal-batched` adds an original, opt-in cloud
rendering change for this M2 Pro. Two prototype pairs improve roughly 84 → 96
FPS at full Retina resolution, with identical frozen sky/cloud pixels. The
production artifact confirms +14.1% in a fixed view and +24.7–25.3% in two
matched flyover pairs. Isolated stalls and broader gameplay testing remain.
The published 0.21.0 starter
predates these local 0.22.0 profiles.
Local Prism imports also carry the tested heap and collector settings:
`python3 tools/build_mac_candidate.py --gc zgc` produces a separate ZGC candidate;
use `--gc g1` for its control. Two route pairs improve 1% lows by 6.5–12.8%
with ZGC and reduce frames over 16 ms, while average FPS stays within 0.7%.
Worst frames remain worse than G1 in these pairs, so this is an optional
smoother-play configuration. [Collector measurements and import instructions](docs/m2-pro-performance.md).
## Play on your Mac
@@ -14,13 +32,15 @@ If Prism is already installed, follow the [Mac import and first comparison guide
Core and balanced also pass [macOS title/demo-world rendering probes](docs/client-graphics-smoke.md)
on the hosted ARM64 software renderer, including readback, saves and clean exits.
Physical M2 GPU performance and macOS 27 remain to be tested.
Physical M2 GPU testing on macOS 27 is now underway; see the linked observations.
| 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 |
| m2-metal (local Mac test) | 32 | 128 | 6144 MiB |
| m2-metal-batched (M2 Pro experiment) | 32 | 128 | 6144 MiB |
| adaptive (experiment) | 12 | 128 | 6144 MiB |
| worker-pacing (experiment) | 12 | 128 | 6144 MiB |
| adaptive-pacing (experiment) | 12 | 128 | 6144 MiB |
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,117 @@
{
"schema": 1,
"performance_measurement": false,
"protocol": "Freeze world ticks and DH wall-clock cloud movement after 120 seconds warmup; hide HUD; render control, batched, control with five seconds settling per image.",
"gpu": "Apple M2 Pro",
"graphics_backend": "Vulkan",
"framebuffer": [
3456,
2234
],
"fixture_sha256": "4385a40953ed82d88077ed3d5d63e904b4193e0bf44ea58f0ed37220b07c23ab",
"original_mod_sha256": "6828ab3385bde5a97cc46fa98a5122211f5ac4e2197da8ed9555c29393ebbba6",
"batch_draws_exercised": 18500,
"image_sha256": {
"cloud-image-control-a.png": "9a645c433f5dc0ed2795ad2f81690659dc1437110c780a2b3f5481186a09ace1",
"cloud-image-batched.png": "7bc48a6f9b115b84eea95d1f9a47fd72859e34eef5fe73db3c28a73a3394a84a",
"cloud-image-control-b.png": "f82b9cb66e081d1d35cd820b64d2ecdcc6f51ca7dbac1961ff77af24f951188f"
},
"rgb_difference_regions": {
"full": {
"y_pixels": [
0,
2234
]
},
"sky": {
"y_pixels": [
80,
893
]
},
"near_terrain": {
"y_pixels": [
1340,
2234
]
}
},
"rgb_differences": {
"full": {
"control_a_vs_batched": {
"mean_abs_channel_difference": 0.00039249443228665847,
"root_mean_square_channel_difference": 0.09972327790283458,
"pixels_any_rgb_change_percent": 0.007486363937796346,
"pixels_over_8_levels_percent": 0.0017874017706157367,
"max_channel_difference": 69
},
"batched_vs_control_b": {
"mean_abs_channel_difference": 0.008503067423212529,
"root_mean_square_channel_difference": 0.2330295622442773,
"pixels_any_rgb_change_percent": 0.4726123420869392,
"pixels_over_8_levels_percent": 0.02376726267449186,
"max_channel_difference": 63
},
"control_a_vs_control_b": {
"mean_abs_channel_difference": 0.008845480066757297,
"root_mean_square_channel_difference": 0.2529589023079416,
"pixels_any_rgb_change_percent": 0.4744256482310421,
"pixels_over_8_levels_percent": 0.025593521005338377,
"max_channel_difference": 70
}
},
"sky": {
"control_a_vs_batched": {
"mean_abs_channel_difference": 0.0,
"root_mean_square_channel_difference": 0.0,
"pixels_any_rgb_change_percent": 0.0,
"pixels_over_8_levels_percent": 0.0,
"max_channel_difference": 0
},
"batched_vs_control_b": {
"mean_abs_channel_difference": 0.0,
"root_mean_square_channel_difference": 0.0,
"pixels_any_rgb_change_percent": 0.0,
"pixels_over_8_levels_percent": 0.0,
"max_channel_difference": 0
},
"control_a_vs_control_b": {
"mean_abs_channel_difference": 0.0,
"root_mean_square_channel_difference": 0.0,
"pixels_any_rgb_change_percent": 0.0,
"pixels_over_8_levels_percent": 0.0,
"max_channel_difference": 0
}
},
"near_terrain": {
"control_a_vs_batched": {
"mean_abs_channel_difference": 1.0788659651448615e-07,
"root_mean_square_channel_difference": 0.000328460951278057,
"pixels_any_rgb_change_percent": 3.236597895434584e-05,
"pixels_over_8_levels_percent": 0.0,
"max_channel_difference": 1
},
"batched_vs_control_b": {
"mean_abs_channel_difference": 4.315463860579446e-07,
"root_mean_square_channel_difference": 0.000656921902556114,
"pixels_any_rgb_change_percent": 0.00012946391581738337,
"pixels_over_8_levels_percent": 0.0,
"max_channel_difference": 1
},
"control_a_vs_control_b": {
"mean_abs_channel_difference": 5.394329825724308e-07,
"root_mean_square_channel_difference": 0.000734461015011982,
"pixels_any_rgb_change_percent": 0.00016182989477172923,
"pixels_over_8_levels_percent": 0.0,
"max_channel_difference": 1
}
}
},
"limits": [
"One daytime fixed scene on the pinned M2 Pro Vulkan/DH build.",
"Sky-region RGB pixels are identical across all three images.",
"Far-terrain cache generation continues: whole-image differences must be compared with the control/control variation.",
"Near-terrain RGB differs by at most one channel level, including control/control variation.",
"Other weather, heights, moving camera, callbacks and shader stacks are not covered."
]
}
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,98 @@
{
"gpu": "Apple M2 Pro",
"graphics_backend": "Vulkan",
"framebuffer_width": 3456,
"framebuffer_height": 2234,
"original_mod_sha256": "804fd1777770e15d8d39911a708e828a028da36d3cb43d1bc2e1c7cddc318e64",
"fixture_sha256": "8e9711a36f5ebd99bd81c65385aa58813f324b8642bbf75bc1222c30d3fb0dbb",
"production_cloud_batch": true,
"cloud_batch": {
"draws": 18463,
"passes": 998
},
"schema": 1,
"performance_measurement": false,
"protocol": "Frozen world ticks and DH wall clock after 120 seconds warmup; hidden HUD; control, production batch, control with five seconds settling per image.",
"rgb_regions_y_end_exclusive": {
"sky": [
80,
893
],
"near_terrain": [
1340,
2234
]
},
"readback_sha256": {
"cloud-image-control-a.png": "ba3caf0704a3d0a1c8914e3dc44f965e17c95ba36bb9fdd4ec4221151c3bbaf1",
"cloud-image-batched.png": "0c7ffc6c14313d2fef649817d037c95d1bf38f2884aa063716741551b0713eb4",
"cloud-image-control-b.png": "3a06c42a107a3916ad26f9965a4602b3fed79b9b8ff53510d1946a0c80f269e4"
},
"pairs": {
"control_a_vs_batched": {
"full": {
"maximum_channel_difference": 252,
"mean_absolute_channel_difference": 0.014238338887231009,
"changed_pixels": 36459,
"total_pixels": 7720704
},
"sky": {
"maximum_channel_difference": 0,
"mean_absolute_channel_difference": 0.0,
"changed_pixels": 0,
"total_pixels": 2809728
},
"near_terrain": {
"maximum_channel_difference": 0,
"mean_absolute_channel_difference": 0.0,
"changed_pixels": 0,
"total_pixels": 3089664
}
},
"control_a_vs_control_b": {
"full": {
"maximum_channel_difference": 253,
"mean_absolute_channel_difference": 0.026262371928777478,
"changed_pixels": 36131,
"total_pixels": 7720704
},
"sky": {
"maximum_channel_difference": 0,
"mean_absolute_channel_difference": 0.0,
"changed_pixels": 0,
"total_pixels": 2809728
},
"near_terrain": {
"maximum_channel_difference": 1,
"mean_absolute_channel_difference": 3.2365978954345846e-07,
"changed_pixels": 3,
"total_pixels": 3089664
}
},
"batched_vs_control_b": {
"full": {
"maximum_channel_difference": 229,
"mean_absolute_channel_difference": 0.0222054879969495,
"changed_pixels": 38818,
"total_pixels": 7720704
},
"sky": {
"maximum_channel_difference": 0,
"mean_absolute_channel_difference": 0.0,
"changed_pixels": 0,
"total_pixels": 2809728
},
"near_terrain": {
"maximum_channel_difference": 1,
"mean_absolute_channel_difference": 3.2365978954345846e-07,
"changed_pixels": 3,
"total_pixels": 3089664
}
}
},
"limits": [
"One fixed daytime view; no proof for other weather, camera height, movement or shaders.",
"Distant-cache generation continues and changes far terrain in both A/B and A/A comparisons.",
"Only private validation freezes simulation and clouds; playable renderer preserves normal simulation and time."
]
}
@@ -0,0 +1,867 @@
{
"schema": 1,
"profiling": false,
"experiment": "Original 0.22.0 M2 Pro DH cloud renderer grouping, explicit opt-in profile",
"common_seed": "m2-vk-retina-01 saved demo world including DH cache",
"runs": {
"m2-metal-production-control02": {
"scene": "overlook",
"camera_yaw": 141.5,
"camera_pitch": 16.95,
"player_x": -41.5,
"player_y": 100.0,
"player_z": 72.5,
"warmup_seconds": 120,
"gpu": "Apple M2 Pro",
"graphics_backend": "Vulkan",
"fixture_sha256": "a71cb439bb27c6dbb894b10154ee89f85444e7225a7122bce9acd3632d2e1140",
"original_mod_sha256": "804fd1777770e15d8d39911a708e828a028da36d3cb43d1bc2e1c7cddc318e64",
"profiling_enabled": false,
"frames": {
"minecraft": "26.3",
"profile": "m2-metal",
"adaptive_budget_enabled": false,
"precise_worker_pacing": false,
"debounce_queue_wait": false,
"wakeable_idle_wait": false,
"dispatch_retry_backoff": false,
"tick_membership_dedup": false,
"terrain_quad_reuse": false,
"terrain_position_arithmetic": false,
"culling_ray_scratch": false,
"empty_culling_whitelist": false,
"interruptible_terrain_read": true,
"adaptive_target_fps": 60,
"world_difficulty": "NORMAL",
"render_distance": 32,
"simulation_distance": 12,
"vsync": false,
"fps_limit": 260,
"fps_limit_unlimited": true,
"inactivity_fps_limit": "minimized",
"framebuffer_width": 3456,
"framebuffer_height": 2234,
"graphics_preset": "CUSTOM",
"ambient_occlusion": true,
"improved_transparency": false,
"mipmap_levels": 4,
"entity_distance_scaling": 1.0,
"entity_shadows": true,
"particles": "ALL",
"clouds": "OFF",
"cloud_range": 128,
"fov": 110,
"dh_rendering_enabled": true,
"dh_renderer_mode": "DEFAULT",
"dh_lod_radius_chunks": 128,
"dh_horizontal_quality": "MEDIUM",
"dh_vertical_quality": "MEDIUM",
"dh_max_horizontal_resolution": "BLOCK",
"dh_transparency": "COMPLETE",
"dh_lod_shading": "AUTO",
"dh_lod_only_mode": false,
"dh_camera_quality_dropoff": true,
"dh_ambient_occlusion": true,
"dh_thread_count": 2,
"dh_runtime_ratio": 0.5,
"dh_capture_status": "available",
"iris_capture_status": "not_installed",
"iris_shaders_enabled": false,
"gpu": "Apple M2 Pro",
"graphics_backend": "Vulkan",
"graphics_driver": "1.2.350 MoltenVK 1.4.2",
"frame_limiter_effective_fps_start": 260,
"frame_limiter_reason_start": "NONE",
"java_vendor": "Microsoft",
"java_vm_name": "OpenJDK 64-Bit Server VM",
"java_runtime_version": "25.0.1+8-LTS",
"mods": [
"[email protected]",
"[email protected]",
"[email protected]+38c7a2d55d",
"[email protected]+fcdff87f02",
"[email protected]+3434d6d902",
"[email protected]+26.3",
"[email protected]+74ed1ea502",
"[email protected]+0ae6f91102",
"[email protected]+3434d6d902",
"[email protected]+4be74c3f02",
"[email protected]+fcdff87f02",
"[email protected]+74ed1ea55d",
"[email protected]+6b5b47fd5d",
"[email protected]+fcdff87f02",
"[email protected]+fcdff87f02",
"[email protected]+3434d6d902",
"[email protected]+427eab975d",
"[email protected]+3434d6d902",
"[email protected]+47f74c9802",
"[email protected]+3434d6d902",
"[email protected]+3434d6d902",
"[email protected]+3434d6d902",
"[email protected]+cf6bc2db5d",
"[email protected]+3434d6d902",
"[email protected]+ffef5f6702",
"[email protected]+4068fd645d",
"[email protected]+ed66f0a35d",
"[email protected]+3434d6d902",
"[email protected]+fcdff87f5d",
"[email protected]+fcdff87f02",
"[email protected]+3434d6d902",
"[email protected]+3434d6d902",
"[email protected]+fcdff87f02",
"[email protected]+fcdff87f02",
"[email protected]+fcdff87f02",
"[email protected]+79385d0b5d",
"[email protected]+b9d63e335d",
"[email protected]+3434d6d902",
"[email protected]+901a437c02",
"[email protected]+3434d6d902",
"[email protected]+4fc5413f02",
"[email protected]+fcdff87f02",
"[email protected]+48607d035d",
"[email protected]+74ed1ea502",
"[email protected]+3434d6d902",
"[email protected]+fcdff87f02",
"[email protected]+fcdff87f02",
"[email protected]+52268a0e02",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]+26.3",
"java@25",
"[email protected]+mc26.3",
"[email protected]",
"[email protected]",
"[email protected]+mc26.3",
"[email protected]",
"[email protected]"
],
"frame_limiter_effective_fps_end": 260,
"frame_limiter_reason_end": "NONE",
"frame_limiter_observations": 9951,
"frame_limiter_throttled_observations": 0,
"frame_limiter_afk_observations": 0,
"visual_settings_end": {
"world_difficulty": "NORMAL",
"render_distance": 32,
"simulation_distance": 12,
"vsync": false,
"fps_limit": 260,
"fps_limit_unlimited": true,
"inactivity_fps_limit": "minimized",
"framebuffer_width": 3456,
"framebuffer_height": 2234,
"graphics_preset": "CUSTOM",
"ambient_occlusion": true,
"improved_transparency": false,
"mipmap_levels": 4,
"entity_distance_scaling": 1.0,
"entity_shadows": true,
"particles": "ALL",
"clouds": "OFF",
"cloud_range": 128,
"fov": 110,
"dh_rendering_enabled": true,
"dh_renderer_mode": "DEFAULT",
"dh_lod_radius_chunks": 128,
"dh_horizontal_quality": "MEDIUM",
"dh_vertical_quality": "MEDIUM",
"dh_max_horizontal_resolution": "BLOCK",
"dh_transparency": "COMPLETE",
"dh_lod_shading": "AUTO",
"dh_lod_only_mode": false,
"dh_camera_quality_dropoff": true,
"dh_ambient_occlusion": true,
"dh_thread_count": 2,
"dh_runtime_ratio": 0.5,
"dh_capture_status": "available",
"iris_capture_status": "not_installed",
"iris_shaders_enabled": false,
"dh_cloud_batch_requested": false,
"dh_cloud_batch_enabled": false
},
"visual_settings_changed": [],
"label": "m2-metal-production-control02",
"kind": "frames",
"schema": 1,
"elapsed_seconds": 120.009546791,
"dropped_samples": 0,
"excluded_frames": 0,
"peak_players": 0,
"java_version": "25.0.1",
"os": "Mac OS X",
"os_version": "27.0.1",
"architecture": "aarch64",
"logical_processors": 12,
"garbage_collectors": [
"G1 Concurrent GC",
"G1 Old Generation",
"G1 Young Generation"
],
"compact_object_headers": false,
"heap_used_end_bytes": 1952922832,
"heap_committed_end_bytes": 2801795072,
"heap_max_bytes": 6442450944,
"heap_used_peak_sampled_bytes": 2624148856,
"nonheap_used_peak_sampled_bytes": 252569024,
"resource_samples": 121,
"resource_sample_interval_ms": 1000,
"process_rss_last_sampled_bytes": 3533422592,
"process_rss_peak_sampled_bytes": 3582558208,
"process_rss_samples": 120,
"process_rss_sample_interval_ms": 1000,
"process_rss_last_sample_age_ms": 137.019084,
"process_cpu_seconds": 211.445484,
"process_cpu_mean_cores": 1.7619055288012897,
"gc_collections": 50,
"gc_time_ms": 271,
"stats": {
"count": 9950,
"meanMs": 12.058937282211057,
"p50Ms": 12.07575,
"p95Ms": 12.558958,
"p99Ms": 13.02825,
"maxMs": 52.341917,
"meanRate": 82.92604701370881,
"lowOnePercentRate": 69.04374876082869,
"over16Ms": 14,
"over33Ms": 1,
"over50Ms": 1,
"over100Ms": 0
},
"samples_file": "m2-metal-production-control02-frames-1791138458891.csv",
"note": "Frame start intervals include frame limiting and presentation waits; menus, pauses and unfocused frames are excluded. Not GPU timings. Process RSS, when available, is sampled separately.",
"dh_cloud_batch_requested": false,
"dh_cloud_batch_enabled": false,
"dh_cloud_batch_draws_recorded": 0,
"dh_cloud_batch_passes_recorded": 0,
"samples_sha256": "d304bc1caece306b2fda1c24435710ca907e97bf1ae53041c76909ec394bcabc"
},
"ticks": {
"java_vendor": "Microsoft",
"java_vm_name": "OpenJDK 64-Bit Server VM",
"java_runtime_version": "25.0.1+8-LTS",
"mods": [
"[email protected]",
"[email protected]",
"[email protected]+38c7a2d55d",
"[email protected]+fcdff87f02",
"[email protected]+3434d6d902",
"[email protected]+26.3",
"[email protected]+74ed1ea502",
"[email protected]+0ae6f91102",
"[email protected]+3434d6d902",
"[email protected]+4be74c3f02",
"[email protected]+fcdff87f02",
"[email protected]+74ed1ea55d",
"[email protected]+6b5b47fd5d",
"[email protected]+fcdff87f02",
"[email protected]+fcdff87f02",
"[email protected]+3434d6d902",
"[email protected]+427eab975d",
"[email protected]+3434d6d902",
"[email protected]+47f74c9802",
"[email protected]+3434d6d902",
"[email protected]+3434d6d902",
"[email protected]+3434d6d902",
"[email protected]+cf6bc2db5d",
"[email protected]+3434d6d902",
"[email protected]+ffef5f6702",
"[email protected]+4068fd645d",
"[email protected]+ed66f0a35d",
"[email protected]+3434d6d902",
"[email protected]+fcdff87f5d",
"[email protected]+fcdff87f02",
"[email protected]+3434d6d902",
"[email protected]+3434d6d902",
"[email protected]+fcdff87f02",
"[email protected]+fcdff87f02",
"[email protected]+fcdff87f02",
"[email protected]+79385d0b5d",
"[email protected]+b9d63e335d",
"[email protected]+3434d6d902",
"[email protected]+901a437c02",
"[email protected]+3434d6d902",
"[email protected]+4fc5413f02",
"[email protected]+fcdff87f02",
"[email protected]+48607d035d",
"[email protected]+74ed1ea502",
"[email protected]+3434d6d902",
"[email protected]+fcdff87f02",
"[email protected]+fcdff87f02",
"[email protected]+52268a0e02",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]+26.3",
"java@25",
"[email protected]+mc26.3",
"[email protected]",
"[email protected]",
"[email protected]+mc26.3",
"[email protected]",
"[email protected]"
],
"profile": "m2-metal",
"minecraft": "26.3",
"graphics_backend": "Vulkan",
"server_runtime_kind": "integrated",
"graphics_driver": "1.2.350 MoltenVK 1.4.2",
"gpu": "Apple M2 Pro",
"client_graphics_capture_status": "captured_at_recording_start",
"label": "m2-metal-production-control02",
"kind": "ticks",
"schema": 1,
"elapsed_seconds": 120.002829792,
"observed_ticks_per_second": 19.999528379121575,
"tick_state_capture_status": "sampled_server_ticks",
"tick_state_samples": 2400,
"tick_state_invalid_samples": 0,
"tick_state_first": {
"runs_game_elements": true,
"stepping": false,
"sprinting": false,
"configured_tick_rate": 20.0,
"frozen": false
},
"tick_state_last": {
"runs_game_elements": true,
"stepping": false,
"sprinting": false,
"configured_tick_rate": 20.0,
"frozen": false
},
"tick_state_changed": [],
"tick_state_frozen_samples": 0,
"tick_state_stepping_samples": 0,
"tick_state_sprinting_samples": 0,
"tick_state_suppressed_samples": 0,
"tick_state_nonstandard_rate_samples": 0,
"dropped_samples": 0,
"excluded_frames": 0,
"peak_players": 1,
"java_version": "25.0.1",
"os": "Mac OS X",
"os_version": "27.0.1",
"architecture": "aarch64",
"logical_processors": 12,
"garbage_collectors": [
"G1 Concurrent GC",
"G1 Old Generation",
"G1 Young Generation"
],
"compact_object_headers": false,
"heap_used_end_bytes": 1952922832,
"heap_committed_end_bytes": 2801795072,
"heap_max_bytes": 6442450944,
"heap_used_peak_sampled_bytes": 2631028672,
"nonheap_used_peak_sampled_bytes": 252569024,
"resource_samples": 118,
"resource_sample_interval_ms": 1000,
"process_rss_last_sampled_bytes": 3533422592,
"process_rss_peak_sampled_bytes": 3582558208,
"process_rss_samples": 119,
"process_rss_sample_interval_ms": 1000,
"process_rss_last_sample_age_ms": 135.860667,
"process_cpu_seconds": 211.439715,
"process_cpu_mean_cores": 1.7619560752566157,
"gc_collections": 50,
"gc_time_ms": 271,
"stats": {
"count": 2400,
"meanMs": 5.11966473375,
"p50Ms": 5.12125,
"p95Ms": 5.81475,
"p99Ms": 6.266792,
"maxMs": 15.523875,
"meanRate": 195.3252902299972,
"lowOnePercentRate": 114.53856252586394,
"over16Ms": 0,
"over33Ms": 0,
"over50Ms": 0,
"over100Ms": 0
},
"samples_file": "m2-metal-production-control02-ticks-1791138458891.csv",
"note": "Tick execution durations exclude scheduler sleep. meanRate is execution capacity, not observed TPS.",
"samples_sha256": "55e2d86888534b363d1a20134d5b3f7891cc2721afc3344ad745f4e66b882230"
}
},
"m2-metal-production-batch02": {
"scene": "overlook",
"camera_yaw": 141.5,
"camera_pitch": 16.95,
"player_x": -41.5,
"player_y": 100.0,
"player_z": 72.5,
"warmup_seconds": 120,
"gpu": "Apple M2 Pro",
"graphics_backend": "Vulkan",
"fixture_sha256": "a71cb439bb27c6dbb894b10154ee89f85444e7225a7122bce9acd3632d2e1140",
"original_mod_sha256": "804fd1777770e15d8d39911a708e828a028da36d3cb43d1bc2e1c7cddc318e64",
"profiling_enabled": false,
"frames": {
"minecraft": "26.3",
"profile": "m2-metal-batched",
"adaptive_budget_enabled": false,
"precise_worker_pacing": false,
"debounce_queue_wait": false,
"wakeable_idle_wait": false,
"dispatch_retry_backoff": false,
"tick_membership_dedup": false,
"terrain_quad_reuse": false,
"terrain_position_arithmetic": false,
"culling_ray_scratch": false,
"empty_culling_whitelist": false,
"interruptible_terrain_read": true,
"adaptive_target_fps": 60,
"world_difficulty": "NORMAL",
"render_distance": 32,
"simulation_distance": 12,
"vsync": false,
"fps_limit": 260,
"fps_limit_unlimited": true,
"inactivity_fps_limit": "minimized",
"framebuffer_width": 3456,
"framebuffer_height": 2234,
"graphics_preset": "CUSTOM",
"ambient_occlusion": true,
"improved_transparency": false,
"mipmap_levels": 4,
"entity_distance_scaling": 1.0,
"entity_shadows": true,
"particles": "ALL",
"clouds": "OFF",
"cloud_range": 128,
"fov": 110,
"dh_rendering_enabled": true,
"dh_renderer_mode": "DEFAULT",
"dh_lod_radius_chunks": 128,
"dh_horizontal_quality": "MEDIUM",
"dh_vertical_quality": "MEDIUM",
"dh_max_horizontal_resolution": "BLOCK",
"dh_transparency": "COMPLETE",
"dh_lod_shading": "AUTO",
"dh_lod_only_mode": false,
"dh_camera_quality_dropoff": true,
"dh_ambient_occlusion": true,
"dh_thread_count": 2,
"dh_runtime_ratio": 0.5,
"dh_capture_status": "available",
"iris_capture_status": "not_installed",
"iris_shaders_enabled": false,
"gpu": "Apple M2 Pro",
"graphics_backend": "Vulkan",
"graphics_driver": "1.2.350 MoltenVK 1.4.2",
"frame_limiter_effective_fps_start": 260,
"frame_limiter_reason_start": "NONE",
"java_vendor": "Microsoft",
"java_vm_name": "OpenJDK 64-Bit Server VM",
"java_runtime_version": "25.0.1+8-LTS",
"mods": [
"[email protected]",
"[email protected]",
"[email protected]+38c7a2d55d",
"[email protected]+fcdff87f02",
"[email protected]+3434d6d902",
"[email protected]+26.3",
"[email protected]+74ed1ea502",
"[email protected]+0ae6f91102",
"[email protected]+3434d6d902",
"[email protected]+4be74c3f02",
"[email protected]+fcdff87f02",
"[email protected]+74ed1ea55d",
"[email protected]+6b5b47fd5d",
"[email protected]+fcdff87f02",
"[email protected]+fcdff87f02",
"[email protected]+3434d6d902",
"[email protected]+427eab975d",
"[email protected]+3434d6d902",
"[email protected]+47f74c9802",
"[email protected]+3434d6d902",
"[email protected]+3434d6d902",
"[email protected]+3434d6d902",
"[email protected]+cf6bc2db5d",
"[email protected]+3434d6d902",
"[email protected]+ffef5f6702",
"[email protected]+4068fd645d",
"[email protected]+ed66f0a35d",
"[email protected]+3434d6d902",
"[email protected]+fcdff87f5d",
"[email protected]+fcdff87f02",
"[email protected]+3434d6d902",
"[email protected]+3434d6d902",
"[email protected]+fcdff87f02",
"[email protected]+fcdff87f02",
"[email protected]+fcdff87f02",
"[email protected]+79385d0b5d",
"[email protected]+b9d63e335d",
"[email protected]+3434d6d902",
"[email protected]+901a437c02",
"[email protected]+3434d6d902",
"[email protected]+4fc5413f02",
"[email protected]+fcdff87f02",
"[email protected]+48607d035d",
"[email protected]+74ed1ea502",
"[email protected]+3434d6d902",
"[email protected]+fcdff87f02",
"[email protected]+fcdff87f02",
"[email protected]+52268a0e02",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]+26.3",
"java@25",
"[email protected]+mc26.3",
"[email protected]",
"[email protected]",
"[email protected]+mc26.3",
"[email protected]",
"[email protected]"
],
"frame_limiter_effective_fps_end": 260,
"frame_limiter_reason_end": "NONE",
"frame_limiter_observations": 11356,
"frame_limiter_throttled_observations": 0,
"frame_limiter_afk_observations": 0,
"visual_settings_end": {
"world_difficulty": "NORMAL",
"render_distance": 32,
"simulation_distance": 12,
"vsync": false,
"fps_limit": 260,
"fps_limit_unlimited": true,
"inactivity_fps_limit": "minimized",
"framebuffer_width": 3456,
"framebuffer_height": 2234,
"graphics_preset": "CUSTOM",
"ambient_occlusion": true,
"improved_transparency": false,
"mipmap_levels": 4,
"entity_distance_scaling": 1.0,
"entity_shadows": true,
"particles": "ALL",
"clouds": "OFF",
"cloud_range": 128,
"fov": 110,
"dh_rendering_enabled": true,
"dh_renderer_mode": "DEFAULT",
"dh_lod_radius_chunks": 128,
"dh_horizontal_quality": "MEDIUM",
"dh_vertical_quality": "MEDIUM",
"dh_max_horizontal_resolution": "BLOCK",
"dh_transparency": "COMPLETE",
"dh_lod_shading": "AUTO",
"dh_lod_only_mode": false,
"dh_camera_quality_dropoff": true,
"dh_ambient_occlusion": true,
"dh_thread_count": 2,
"dh_runtime_ratio": 0.5,
"dh_capture_status": "available",
"iris_shaders_enabled": false,
"iris_capture_status": "not_installed",
"dh_cloud_batch_requested": true,
"dh_cloud_batch_enabled": true
},
"visual_settings_changed": [],
"label": "m2-metal-production-batch02",
"kind": "frames",
"schema": 1,
"elapsed_seconds": 120.013808792,
"dropped_samples": 0,
"excluded_frames": 0,
"peak_players": 0,
"java_version": "25.0.1",
"os": "Mac OS X",
"os_version": "27.0.1",
"architecture": "aarch64",
"logical_processors": 12,
"garbage_collectors": [
"G1 Concurrent GC",
"G1 Old Generation",
"G1 Young Generation"
],
"compact_object_headers": false,
"heap_used_end_bytes": 2362702304,
"heap_committed_end_bytes": 2629828608,
"heap_max_bytes": 6442450944,
"heap_used_peak_sampled_bytes": 2411913496,
"nonheap_used_peak_sampled_bytes": 252099304,
"resource_samples": 121,
"resource_sample_interval_ms": 1000,
"process_rss_last_sampled_bytes": 3365011456,
"process_rss_peak_sampled_bytes": 3365011456,
"process_rss_samples": 120,
"process_rss_sample_interval_ms": 1000,
"process_rss_last_sample_age_ms": 124.224792,
"process_cpu_seconds": 200.945491,
"process_cpu_mean_cores": 1.6743530850542827,
"gc_collections": 55,
"gc_time_ms": 264,
"stats": {
"count": 11355,
"meanMs": 10.567351849405547,
"p50Ms": 10.566083,
"p95Ms": 10.803542,
"p99Ms": 10.929875,
"maxMs": 18.12125,
"meanRate": 94.63108773616293,
"lowOnePercentRate": 84.62637566863958,
"over16Ms": 3,
"over33Ms": 0,
"over50Ms": 0,
"over100Ms": 0
},
"samples_file": "m2-metal-production-batch02-frames-1791138162792.csv",
"note": "Frame start intervals include frame limiting and presentation waits; menus, pauses and unfocused frames are excluded. Not GPU timings. Process RSS, when available, is sampled separately.",
"dh_cloud_batch_requested": true,
"dh_cloud_batch_enabled": true,
"dh_cloud_batch_draws_recorded": 431310,
"dh_cloud_batch_passes_recorded": 22718,
"samples_sha256": "fa71172923ef6f0c362db90e5c203d82f10c606f6e1477c1086c52698197b880"
},
"ticks": {
"java_vendor": "Microsoft",
"java_vm_name": "OpenJDK 64-Bit Server VM",
"java_runtime_version": "25.0.1+8-LTS",
"mods": [
"[email protected]",
"[email protected]",
"[email protected]+38c7a2d55d",
"[email protected]+fcdff87f02",
"[email protected]+3434d6d902",
"[email protected]+26.3",
"[email protected]+74ed1ea502",
"[email protected]+0ae6f91102",
"[email protected]+3434d6d902",
"[email protected]+4be74c3f02",
"[email protected]+fcdff87f02",
"[email protected]+74ed1ea55d",
"[email protected]+6b5b47fd5d",
"[email protected]+fcdff87f02",
"[email protected]+fcdff87f02",
"[email protected]+3434d6d902",
"[email protected]+427eab975d",
"[email protected]+3434d6d902",
"[email protected]+47f74c9802",
"[email protected]+3434d6d902",
"[email protected]+3434d6d902",
"[email protected]+3434d6d902",
"[email protected]+cf6bc2db5d",
"[email protected]+3434d6d902",
"[email protected]+ffef5f6702",
"[email protected]+4068fd645d",
"[email protected]+ed66f0a35d",
"[email protected]+3434d6d902",
"[email protected]+fcdff87f5d",
"[email protected]+fcdff87f02",
"[email protected]+3434d6d902",
"[email protected]+3434d6d902",
"[email protected]+fcdff87f02",
"[email protected]+fcdff87f02",
"[email protected]+fcdff87f02",
"[email protected]+79385d0b5d",
"[email protected]+b9d63e335d",
"[email protected]+3434d6d902",
"[email protected]+901a437c02",
"[email protected]+3434d6d902",
"[email protected]+4fc5413f02",
"[email protected]+fcdff87f02",
"[email protected]+48607d035d",
"[email protected]+74ed1ea502",
"[email protected]+3434d6d902",
"[email protected]+fcdff87f02",
"[email protected]+fcdff87f02",
"[email protected]+52268a0e02",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]+26.3",
"java@25",
"[email protected]+mc26.3",
"[email protected]",
"[email protected]",
"[email protected]+mc26.3",
"[email protected]",
"[email protected]"
],
"profile": "m2-metal-batched",
"minecraft": "26.3",
"graphics_backend": "Vulkan",
"server_runtime_kind": "integrated",
"graphics_driver": "1.2.350 MoltenVK 1.4.2",
"gpu": "Apple M2 Pro",
"client_graphics_capture_status": "captured_at_recording_start",
"label": "m2-metal-production-batch02",
"kind": "ticks",
"schema": 1,
"elapsed_seconds": 120.007075208,
"observed_ticks_per_second": 19.998820868188357,
"tick_state_capture_status": "sampled_server_ticks",
"tick_state_samples": 2400,
"tick_state_invalid_samples": 0,
"tick_state_first": {
"sprinting": false,
"stepping": false,
"runs_game_elements": true,
"frozen": false,
"configured_tick_rate": 20.0
},
"tick_state_last": {
"sprinting": false,
"stepping": false,
"runs_game_elements": true,
"frozen": false,
"configured_tick_rate": 20.0
},
"tick_state_changed": [],
"tick_state_frozen_samples": 0,
"tick_state_stepping_samples": 0,
"tick_state_sprinting_samples": 0,
"tick_state_suppressed_samples": 0,
"tick_state_nonstandard_rate_samples": 0,
"dropped_samples": 0,
"excluded_frames": 0,
"peak_players": 1,
"java_version": "25.0.1",
"os": "Mac OS X",
"os_version": "27.0.1",
"architecture": "aarch64",
"logical_processors": 12,
"garbage_collectors": [
"G1 Concurrent GC",
"G1 Old Generation",
"G1 Young Generation"
],
"compact_object_headers": false,
"heap_used_end_bytes": 2362702304,
"heap_committed_end_bytes": 2629828608,
"heap_max_bytes": 6442450944,
"heap_used_peak_sampled_bytes": 2420113408,
"nonheap_used_peak_sampled_bytes": 252091760,
"resource_samples": 119,
"resource_sample_interval_ms": 1000,
"process_rss_last_sampled_bytes": 3365011456,
"process_rss_peak_sampled_bytes": 3365011456,
"process_rss_samples": 119,
"process_rss_sample_interval_ms": 1000,
"process_rss_last_sample_age_ms": 122.8665,
"process_cpu_seconds": 200.937019,
"process_cpu_mean_cores": 1.6743764369869834,
"gc_collections": 55,
"gc_time_ms": 264,
"stats": {
"count": 2400,
"meanMs": 5.300246929583333,
"p50Ms": 5.254125,
"p95Ms": 5.920583,
"p99Ms": 6.653792,
"maxMs": 14.089208,
"meanRate": 188.6704550345568,
"lowOnePercentRate": 102.21677433315672,
"over16Ms": 0,
"over33Ms": 0,
"over50Ms": 0,
"over100Ms": 0
},
"samples_file": "m2-metal-production-batch02-ticks-1791138162792.csv",
"note": "Tick execution durations exclude scheduler sleep. meanRate is execution capacity, not observed TPS.",
"samples_sha256": "423cfd784899704629a0d0a08edc3b4325195c282de75202320fe3b6349adb47"
}
}
},
"comparison": {
"kind": "frames",
"mismatches": [],
"warnings": [
"World, route, warm-up, shader state and LOD cache must also match; these are not verified by this tool.",
"Experiment dh_cloud_batch_requested differs: False vs True.",
"Experiment dh_cloud_batch_enabled differs: False vs True."
],
"metrics": [
{
"metric": "meanRate",
"baseline": 82.92604701370881,
"candidate": 94.63108773616293,
"change_percent": 14.115035195780056
},
{
"metric": "lowOnePercentRate",
"baseline": 69.04374876082869,
"candidate": 84.62637566863958,
"change_percent": 22.569207477117104
},
{
"metric": "p95Ms",
"baseline": 12.558958,
"candidate": 10.803542,
"change_percent": -13.9774016283835
},
{
"metric": "p99Ms",
"baseline": 13.02825,
"candidate": 10.929875,
"change_percent": -16.106345825417844
},
{
"metric": "maxMs",
"baseline": 52.341917,
"candidate": 18.12125,
"change_percent": -65.37908613473213
},
{
"metric": "heap_used_end_bytes",
"baseline": 1952922832,
"candidate": 2362702304,
"change_percent": 20.982880904738167
},
{
"metric": "heap_used_peak_sampled_bytes",
"baseline": 2624148856,
"candidate": 2411913496,
"change_percent": -8.087778996025063
},
{
"metric": "nonheap_used_peak_sampled_bytes",
"baseline": 252569024,
"candidate": 252099304,
"change_percent": -0.18597688368943155
},
{
"metric": "process_rss_last_sampled_bytes",
"baseline": 3533422592,
"candidate": 3365011456,
"change_percent": -4.766232501634493
},
{
"metric": "process_rss_peak_sampled_bytes",
"baseline": 3582558208,
"candidate": 3365011456,
"change_percent": -6.072385691158044
},
{
"metric": "process_cpu_mean_cores",
"baseline": 1.7619055288012897,
"candidate": 1.6743530850542827,
"change_percent": -4.969190590290795
},
{
"metric": "gc_time_ms",
"baseline": 271,
"candidate": 264,
"change_percent": -2.583025830258301
}
]
},
"limits": [
"One production-artifact pair, candidate then control; two earlier private prototype pairs ran in opposite orders.",
"Same original JAR, fixture binary, seed/cache, 120-second warmup and 120-second recording.",
"Simulation, wall-clock clouds and further LOD generation remain active.",
"No compilation, JFR, GPU timing or CPU-heavy analysis overlaps these recording intervals.",
"This candidate has no frame over 33 ms; earlier prototype runs include isolated 51.69/65.59 ms stalls.",
"Movement smoke is a separate 30-second functional check, not a matched throughput comparison."
]
}
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,851 @@
{
"schema": 1,
"profiling": false,
"common_seed": "m2-vk-retina-01 saved demo world, including terrain and Distant Horizons cache",
"runs": {
"m2-gl-retina-cache-02": {
"scene": "overlook",
"camera_yaw": 141.5,
"camera_pitch": 16.95,
"player_x": -41.5,
"player_y": 100.0,
"player_z": 72.5,
"warmup_seconds": 120,
"gpu": "Apple M2 Pro",
"graphics_backend": "OpenGL",
"fixture_sha256": "a71cb439bb27c6dbb894b10154ee89f85444e7225a7122bce9acd3632d2e1140",
"original_mod_sha256": "6828ab3385bde5a97cc46fa98a5122211f5ac4e2197da8ed9555c29393ebbba6",
"frames": {
"minecraft": "26.3",
"profile": "m2-balanced",
"adaptive_budget_enabled": false,
"precise_worker_pacing": false,
"debounce_queue_wait": false,
"wakeable_idle_wait": false,
"dispatch_retry_backoff": false,
"tick_membership_dedup": false,
"terrain_quad_reuse": false,
"terrain_position_arithmetic": false,
"culling_ray_scratch": false,
"empty_culling_whitelist": false,
"interruptible_terrain_read": true,
"adaptive_target_fps": 60,
"world_difficulty": "NORMAL",
"render_distance": 32,
"simulation_distance": 12,
"vsync": false,
"fps_limit": 260,
"fps_limit_unlimited": true,
"inactivity_fps_limit": "minimized",
"framebuffer_width": 3456,
"framebuffer_height": 2234,
"graphics_preset": "CUSTOM",
"ambient_occlusion": true,
"improved_transparency": false,
"mipmap_levels": 4,
"entity_distance_scaling": 1.0,
"entity_shadows": true,
"particles": "ALL",
"clouds": "OFF",
"cloud_range": 128,
"fov": 110,
"dh_rendering_enabled": true,
"dh_renderer_mode": "DEFAULT",
"dh_lod_radius_chunks": 128,
"dh_horizontal_quality": "MEDIUM",
"dh_vertical_quality": "MEDIUM",
"dh_max_horizontal_resolution": "BLOCK",
"dh_transparency": "COMPLETE",
"dh_lod_shading": "AUTO",
"dh_lod_only_mode": false,
"dh_camera_quality_dropoff": true,
"dh_ambient_occlusion": true,
"dh_thread_count": 2,
"dh_runtime_ratio": 0.5,
"dh_capture_status": "available",
"iris_capture_status": "not_installed",
"iris_shaders_enabled": false,
"gpu": "Apple M2 Pro",
"graphics_backend": "OpenGL",
"graphics_driver": "4.1 Metal - 91.7",
"frame_limiter_effective_fps_start": 260,
"frame_limiter_reason_start": "NONE",
"java_vendor": "Microsoft",
"java_vm_name": "OpenJDK 64-Bit Server VM",
"java_runtime_version": "25.0.1+8-LTS",
"mods": [
"[email protected]",
"[email protected]",
"[email protected]+38c7a2d55d",
"[email protected]+fcdff87f02",
"[email protected]+3434d6d902",
"[email protected]+26.3",
"[email protected]+74ed1ea502",
"[email protected]+0ae6f91102",
"[email protected]+3434d6d902",
"[email protected]+4be74c3f02",
"[email protected]+fcdff87f02",
"[email protected]+74ed1ea55d",
"[email protected]+6b5b47fd5d",
"[email protected]+fcdff87f02",
"[email protected]+fcdff87f02",
"[email protected]+3434d6d902",
"[email protected]+427eab975d",
"[email protected]+3434d6d902",
"[email protected]+47f74c9802",
"[email protected]+3434d6d902",
"[email protected]+3434d6d902",
"[email protected]+3434d6d902",
"[email protected]+cf6bc2db5d",
"[email protected]+3434d6d902",
"[email protected]+ffef5f6702",
"[email protected]+4068fd645d",
"[email protected]+ed66f0a35d",
"[email protected]+3434d6d902",
"[email protected]+fcdff87f5d",
"[email protected]+fcdff87f02",
"[email protected]+3434d6d902",
"[email protected]+3434d6d902",
"[email protected]+fcdff87f02",
"[email protected]+fcdff87f02",
"[email protected]+fcdff87f02",
"[email protected]+79385d0b5d",
"[email protected]+b9d63e335d",
"[email protected]+3434d6d902",
"[email protected]+901a437c02",
"[email protected]+3434d6d902",
"[email protected]+4fc5413f02",
"[email protected]+fcdff87f02",
"[email protected]+48607d035d",
"[email protected]+74ed1ea502",
"[email protected]+3434d6d902",
"[email protected]+fcdff87f02",
"[email protected]+fcdff87f02",
"[email protected]+52268a0e02",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]+26.3",
"java@25",
"[email protected]+mc26.3",
"[email protected]",
"[email protected]",
"[email protected]+mc26.3",
"[email protected]",
"[email protected]"
],
"frame_limiter_effective_fps_end": 260,
"frame_limiter_reason_end": "NONE",
"frame_limiter_observations": 8023,
"frame_limiter_throttled_observations": 0,
"frame_limiter_afk_observations": 0,
"visual_settings_end": {
"world_difficulty": "NORMAL",
"render_distance": 32,
"simulation_distance": 12,
"vsync": false,
"fps_limit": 260,
"fps_limit_unlimited": true,
"inactivity_fps_limit": "minimized",
"framebuffer_width": 3456,
"framebuffer_height": 2234,
"graphics_preset": "CUSTOM",
"ambient_occlusion": true,
"improved_transparency": false,
"mipmap_levels": 4,
"entity_distance_scaling": 1.0,
"entity_shadows": true,
"particles": "ALL",
"clouds": "OFF",
"cloud_range": 128,
"fov": 110,
"dh_rendering_enabled": true,
"dh_renderer_mode": "DEFAULT",
"dh_lod_radius_chunks": 128,
"dh_horizontal_quality": "MEDIUM",
"dh_vertical_quality": "MEDIUM",
"dh_max_horizontal_resolution": "BLOCK",
"dh_transparency": "COMPLETE",
"dh_lod_shading": "AUTO",
"dh_lod_only_mode": false,
"dh_camera_quality_dropoff": true,
"dh_ambient_occlusion": true,
"dh_thread_count": 2,
"dh_runtime_ratio": 0.5,
"dh_capture_status": "available",
"iris_capture_status": "not_installed",
"iris_shaders_enabled": false
},
"visual_settings_changed": [],
"label": "m2-gl-retina-cache-02",
"kind": "frames",
"schema": 1,
"elapsed_seconds": 120.017216333,
"dropped_samples": 0,
"excluded_frames": 0,
"peak_players": 0,
"java_version": "25.0.1",
"os": "Mac OS X",
"os_version": "27.0.1",
"architecture": "aarch64",
"logical_processors": 12,
"garbage_collectors": [
"G1 Concurrent GC",
"G1 Old Generation",
"G1 Young Generation"
],
"compact_object_headers": false,
"heap_used_end_bytes": 2223416320,
"heap_committed_end_bytes": 2600468480,
"heap_max_bytes": 6442450944,
"heap_used_peak_sampled_bytes": 2375691312,
"nonheap_used_peak_sampled_bytes": 252126832,
"resource_samples": 121,
"resource_sample_interval_ms": 1000,
"process_rss_last_sampled_bytes": 4128243712,
"process_rss_peak_sampled_bytes": 4128243712,
"process_rss_samples": 120,
"process_rss_sample_interval_ms": 1000,
"process_rss_last_sample_age_ms": 142.62575,
"process_cpu_seconds": 196.751059,
"process_cpu_mean_cores": 1.6393569607054885,
"gc_collections": 45,
"gc_time_ms": 241,
"stats": {
"count": 8022,
"meanMs": 14.956412173896783,
"p50Ms": 14.811708,
"p95Ms": 15.797583,
"p99Ms": 16.476458,
"maxMs": 25.481334,
"meanRate": 66.86095491171913,
"lowOnePercentRate": 56.973365580408306,
"over16Ms": 58,
"over33Ms": 0,
"over50Ms": 0,
"over100Ms": 0
},
"samples_file": "m2-gl-retina-cache-02-frames-1791123561936.csv",
"note": "Frame start intervals include frame limiting and presentation waits; menus, pauses and unfocused frames are excluded. Not GPU timings. Process RSS, when available, is sampled separately.",
"samples_sha256": "1082fa0ea56ef9da4b4908c607118805fa2e9af51dc7bcded6859dc44f8e15fd"
},
"ticks": {
"java_vendor": "Microsoft",
"java_vm_name": "OpenJDK 64-Bit Server VM",
"java_runtime_version": "25.0.1+8-LTS",
"mods": [
"[email protected]",
"[email protected]",
"[email protected]+38c7a2d55d",
"[email protected]+fcdff87f02",
"[email protected]+3434d6d902",
"[email protected]+26.3",
"[email protected]+74ed1ea502",
"[email protected]+0ae6f91102",
"[email protected]+3434d6d902",
"[email protected]+4be74c3f02",
"[email protected]+fcdff87f02",
"[email protected]+74ed1ea55d",
"[email protected]+6b5b47fd5d",
"[email protected]+fcdff87f02",
"[email protected]+fcdff87f02",
"[email protected]+3434d6d902",
"[email protected]+427eab975d",
"[email protected]+3434d6d902",
"[email protected]+47f74c9802",
"[email protected]+3434d6d902",
"[email protected]+3434d6d902",
"[email protected]+3434d6d902",
"[email protected]+cf6bc2db5d",
"[email protected]+3434d6d902",
"[email protected]+ffef5f6702",
"[email protected]+4068fd645d",
"[email protected]+ed66f0a35d",
"[email protected]+3434d6d902",
"[email protected]+fcdff87f5d",
"[email protected]+fcdff87f02",
"[email protected]+3434d6d902",
"[email protected]+3434d6d902",
"[email protected]+fcdff87f02",
"[email protected]+fcdff87f02",
"[email protected]+fcdff87f02",
"[email protected]+79385d0b5d",
"[email protected]+b9d63e335d",
"[email protected]+3434d6d902",
"[email protected]+901a437c02",
"[email protected]+3434d6d902",
"[email protected]+4fc5413f02",
"[email protected]+fcdff87f02",
"[email protected]+48607d035d",
"[email protected]+74ed1ea502",
"[email protected]+3434d6d902",
"[email protected]+fcdff87f02",
"[email protected]+fcdff87f02",
"[email protected]+52268a0e02",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]+26.3",
"java@25",
"[email protected]+mc26.3",
"[email protected]",
"[email protected]",
"[email protected]+mc26.3",
"[email protected]",
"[email protected]"
],
"profile": "m2-balanced",
"minecraft": "26.3",
"graphics_backend": "OpenGL",
"server_runtime_kind": "integrated",
"graphics_driver": "4.1 Metal - 91.7",
"gpu": "Apple M2 Pro",
"client_graphics_capture_status": "captured_at_recording_start",
"label": "m2-gl-retina-cache-02",
"kind": "ticks",
"schema": 1,
"elapsed_seconds": 120.010891792,
"observed_ticks_per_second": 20.006517443111377,
"tick_state_capture_status": "sampled_server_ticks",
"tick_state_samples": 2401,
"tick_state_invalid_samples": 0,
"tick_state_first": {
"stepping": false,
"runs_game_elements": true,
"frozen": false,
"configured_tick_rate": 20.0,
"sprinting": false
},
"tick_state_last": {
"stepping": false,
"runs_game_elements": true,
"frozen": false,
"configured_tick_rate": 20.0,
"sprinting": false
},
"tick_state_changed": [],
"tick_state_frozen_samples": 0,
"tick_state_stepping_samples": 0,
"tick_state_sprinting_samples": 0,
"tick_state_suppressed_samples": 0,
"tick_state_nonstandard_rate_samples": 0,
"dropped_samples": 0,
"excluded_frames": 0,
"peak_players": 1,
"java_version": "25.0.1",
"os": "Mac OS X",
"os_version": "27.0.1",
"architecture": "aarch64",
"logical_processors": 12,
"garbage_collectors": [
"G1 Concurrent GC",
"G1 Old Generation",
"G1 Young Generation"
],
"compact_object_headers": false,
"heap_used_end_bytes": 2223416320,
"heap_committed_end_bytes": 2600468480,
"heap_max_bytes": 6442450944,
"heap_used_peak_sampled_bytes": 2330627552,
"nonheap_used_peak_sampled_bytes": 252126832,
"resource_samples": 118,
"resource_sample_interval_ms": 1000,
"process_rss_last_sampled_bytes": 4128243712,
"process_rss_peak_sampled_bytes": 4128243712,
"process_rss_samples": 119,
"process_rss_sample_interval_ms": 1000,
"process_rss_last_sample_age_ms": 141.511041,
"process_cpu_seconds": 196.745797,
"process_cpu_mean_cores": 1.6393995083462518,
"gc_collections": 45,
"gc_time_ms": 241,
"stats": {
"count": 2401,
"meanMs": 5.977001070803832,
"p50Ms": 6.014167,
"p95Ms": 6.926959,
"p99Ms": 7.41225,
"maxMs": 13.996875,
"meanRate": 167.30798407996815,
"lowOnePercentRate": 112.11489369272508,
"over16Ms": 0,
"over33Ms": 0,
"over50Ms": 0,
"over100Ms": 0
},
"samples_file": "m2-gl-retina-cache-02-ticks-1791123561936.csv",
"note": "Tick execution durations exclude scheduler sleep. meanRate is execution capacity, not observed TPS.",
"samples_sha256": "25f8cec870cb44cb6c3a0548914858ae752511c582462ac23b16f6df2d64abdf"
}
},
"m2-vk-retina-cache-02": {
"scene": "overlook",
"camera_yaw": 141.5,
"camera_pitch": 16.95,
"player_x": -41.5,
"player_y": 100.0,
"player_z": 72.5,
"warmup_seconds": 120,
"gpu": "Apple M2 Pro",
"graphics_backend": "Vulkan",
"fixture_sha256": "a71cb439bb27c6dbb894b10154ee89f85444e7225a7122bce9acd3632d2e1140",
"original_mod_sha256": "6828ab3385bde5a97cc46fa98a5122211f5ac4e2197da8ed9555c29393ebbba6",
"frames": {
"minecraft": "26.3",
"profile": "m2-balanced",
"adaptive_budget_enabled": false,
"precise_worker_pacing": false,
"debounce_queue_wait": false,
"wakeable_idle_wait": false,
"dispatch_retry_backoff": false,
"tick_membership_dedup": false,
"terrain_quad_reuse": false,
"terrain_position_arithmetic": false,
"culling_ray_scratch": false,
"empty_culling_whitelist": false,
"interruptible_terrain_read": true,
"adaptive_target_fps": 60,
"world_difficulty": "NORMAL",
"render_distance": 32,
"simulation_distance": 12,
"vsync": false,
"fps_limit": 260,
"fps_limit_unlimited": true,
"inactivity_fps_limit": "minimized",
"framebuffer_width": 3456,
"framebuffer_height": 2234,
"graphics_preset": "CUSTOM",
"ambient_occlusion": true,
"improved_transparency": false,
"mipmap_levels": 4,
"entity_distance_scaling": 1.0,
"entity_shadows": true,
"particles": "ALL",
"clouds": "OFF",
"cloud_range": 128,
"fov": 110,
"dh_rendering_enabled": true,
"dh_renderer_mode": "DEFAULT",
"dh_lod_radius_chunks": 128,
"dh_horizontal_quality": "MEDIUM",
"dh_vertical_quality": "MEDIUM",
"dh_max_horizontal_resolution": "BLOCK",
"dh_transparency": "COMPLETE",
"dh_lod_shading": "AUTO",
"dh_lod_only_mode": false,
"dh_camera_quality_dropoff": true,
"dh_ambient_occlusion": true,
"dh_thread_count": 2,
"dh_runtime_ratio": 0.5,
"dh_capture_status": "available",
"iris_capture_status": "not_installed",
"iris_shaders_enabled": false,
"gpu": "Apple M2 Pro",
"graphics_backend": "Vulkan",
"graphics_driver": "1.2.350 MoltenVK 1.4.2",
"frame_limiter_effective_fps_start": 260,
"frame_limiter_reason_start": "NONE",
"java_vendor": "Microsoft",
"java_vm_name": "OpenJDK 64-Bit Server VM",
"java_runtime_version": "25.0.1+8-LTS",
"mods": [
"[email protected]",
"[email protected]",
"[email protected]+38c7a2d55d",
"[email protected]+fcdff87f02",
"[email protected]+3434d6d902",
"[email protected]+26.3",
"[email protected]+74ed1ea502",
"[email protected]+0ae6f91102",
"[email protected]+3434d6d902",
"[email protected]+4be74c3f02",
"[email protected]+fcdff87f02",
"[email protected]+74ed1ea55d",
"[email protected]+6b5b47fd5d",
"[email protected]+fcdff87f02",
"[email protected]+fcdff87f02",
"[email protected]+3434d6d902",
"[email protected]+427eab975d",
"[email protected]+3434d6d902",
"[email protected]+47f74c9802",
"[email protected]+3434d6d902",
"[email protected]+3434d6d902",
"[email protected]+3434d6d902",
"[email protected]+cf6bc2db5d",
"[email protected]+3434d6d902",
"[email protected]+ffef5f6702",
"[email protected]+4068fd645d",
"[email protected]+ed66f0a35d",
"[email protected]+3434d6d902",
"[email protected]+fcdff87f5d",
"[email protected]+fcdff87f02",
"[email protected]+3434d6d902",
"[email protected]+3434d6d902",
"[email protected]+fcdff87f02",
"[email protected]+fcdff87f02",
"[email protected]+fcdff87f02",
"[email protected]+79385d0b5d",
"[email protected]+b9d63e335d",
"[email protected]+3434d6d902",
"[email protected]+901a437c02",
"[email protected]+3434d6d902",
"[email protected]+4fc5413f02",
"[email protected]+fcdff87f02",
"[email protected]+48607d035d",
"[email protected]+74ed1ea502",
"[email protected]+3434d6d902",
"[email protected]+fcdff87f02",
"[email protected]+fcdff87f02",
"[email protected]+52268a0e02",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]+26.3",
"java@25",
"[email protected]+mc26.3",
"[email protected]",
"[email protected]",
"[email protected]+mc26.3",
"[email protected]",
"[email protected]"
],
"frame_limiter_effective_fps_end": 260,
"frame_limiter_reason_end": "NONE",
"frame_limiter_observations": 9756,
"frame_limiter_throttled_observations": 0,
"frame_limiter_afk_observations": 0,
"visual_settings_end": {
"world_difficulty": "NORMAL",
"render_distance": 32,
"simulation_distance": 12,
"vsync": false,
"fps_limit": 260,
"fps_limit_unlimited": true,
"inactivity_fps_limit": "minimized",
"framebuffer_width": 3456,
"framebuffer_height": 2234,
"graphics_preset": "CUSTOM",
"ambient_occlusion": true,
"improved_transparency": false,
"mipmap_levels": 4,
"entity_distance_scaling": 1.0,
"entity_shadows": true,
"particles": "ALL",
"clouds": "OFF",
"cloud_range": 128,
"fov": 110,
"dh_rendering_enabled": true,
"dh_renderer_mode": "DEFAULT",
"dh_lod_radius_chunks": 128,
"dh_horizontal_quality": "MEDIUM",
"dh_vertical_quality": "MEDIUM",
"dh_max_horizontal_resolution": "BLOCK",
"dh_transparency": "COMPLETE",
"dh_lod_shading": "AUTO",
"dh_lod_only_mode": false,
"dh_camera_quality_dropoff": true,
"dh_ambient_occlusion": true,
"dh_thread_count": 2,
"dh_runtime_ratio": 0.5,
"dh_capture_status": "available",
"iris_capture_status": "not_installed",
"iris_shaders_enabled": false
},
"visual_settings_changed": [],
"label": "m2-vk-retina-cache-02",
"kind": "frames",
"schema": 1,
"elapsed_seconds": 120.00796975,
"dropped_samples": 0,
"excluded_frames": 0,
"peak_players": 0,
"java_version": "25.0.1",
"os": "Mac OS X",
"os_version": "27.0.1",
"architecture": "aarch64",
"logical_processors": 12,
"garbage_collectors": [
"G1 Concurrent GC",
"G1 Old Generation",
"G1 Young Generation"
],
"compact_object_headers": false,
"heap_used_end_bytes": 1898423224,
"heap_committed_end_bytes": 3028287488,
"heap_max_bytes": 6442450944,
"heap_used_peak_sampled_bytes": 2853250848,
"nonheap_used_peak_sampled_bytes": 255258208,
"resource_samples": 121,
"resource_sample_interval_ms": 1000,
"process_rss_last_sampled_bytes": 3610017792,
"process_rss_peak_sampled_bytes": 3755999232,
"process_rss_samples": 120,
"process_rss_sample_interval_ms": 1000,
"process_rss_last_sample_age_ms": 26.511833,
"process_cpu_seconds": 196.25092,
"process_cpu_mean_cores": 1.635315724520871,
"gc_collections": 33,
"gc_time_ms": 181,
"stats": {
"count": 9755,
"meanMs": 12.29954284551512,
"p50Ms": 12.233542,
"p95Ms": 13.203208,
"p99Ms": 13.592833,
"maxMs": 53.125791,
"meanRate": 81.3038348303033,
"lowOnePercentRate": 66.85283320659684,
"over16Ms": 6,
"over33Ms": 3,
"over50Ms": 1,
"over100Ms": 0
},
"samples_file": "m2-vk-retina-cache-02-frames-1791122829137.csv",
"note": "Frame start intervals include frame limiting and presentation waits; menus, pauses and unfocused frames are excluded. Not GPU timings. Process RSS, when available, is sampled separately.",
"samples_sha256": "83234c8d2571d6e2c64f043bfbe8a3a0f065ded7091fe06330cc8b50485d238b"
},
"ticks": {
"java_vendor": "Microsoft",
"java_vm_name": "OpenJDK 64-Bit Server VM",
"java_runtime_version": "25.0.1+8-LTS",
"mods": [
"[email protected]",
"[email protected]",
"[email protected]+38c7a2d55d",
"[email protected]+fcdff87f02",
"[email protected]+3434d6d902",
"[email protected]+26.3",
"[email protected]+74ed1ea502",
"[email protected]+0ae6f91102",
"[email protected]+3434d6d902",
"[email protected]+4be74c3f02",
"[email protected]+fcdff87f02",
"[email protected]+74ed1ea55d",
"[email protected]+6b5b47fd5d",
"[email protected]+fcdff87f02",
"[email protected]+fcdff87f02",
"[email protected]+3434d6d902",
"[email protected]+427eab975d",
"[email protected]+3434d6d902",
"[email protected]+47f74c9802",
"[email protected]+3434d6d902",
"[email protected]+3434d6d902",
"[email protected]+3434d6d902",
"[email protected]+cf6bc2db5d",
"[email protected]+3434d6d902",
"[email protected]+ffef5f6702",
"[email protected]+4068fd645d",
"[email protected]+ed66f0a35d",
"[email protected]+3434d6d902",
"[email protected]+fcdff87f5d",
"[email protected]+fcdff87f02",
"[email protected]+3434d6d902",
"[email protected]+3434d6d902",
"[email protected]+fcdff87f02",
"[email protected]+fcdff87f02",
"[email protected]+fcdff87f02",
"[email protected]+79385d0b5d",
"[email protected]+b9d63e335d",
"[email protected]+3434d6d902",
"[email protected]+901a437c02",
"[email protected]+3434d6d902",
"[email protected]+4fc5413f02",
"[email protected]+fcdff87f02",
"[email protected]+48607d035d",
"[email protected]+74ed1ea502",
"[email protected]+3434d6d902",
"[email protected]+fcdff87f02",
"[email protected]+fcdff87f02",
"[email protected]+52268a0e02",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]+26.3",
"java@25",
"[email protected]+mc26.3",
"[email protected]",
"[email protected]",
"[email protected]+mc26.3",
"[email protected]",
"[email protected]"
],
"profile": "m2-balanced",
"minecraft": "26.3",
"graphics_backend": "Vulkan",
"server_runtime_kind": "integrated",
"graphics_driver": "1.2.350 MoltenVK 1.4.2",
"gpu": "Apple M2 Pro",
"client_graphics_capture_status": "captured_at_recording_start",
"label": "m2-vk-retina-cache-02",
"kind": "ticks",
"schema": 1,
"elapsed_seconds": 120.000482416,
"observed_ticks_per_second": 20.00825289748892,
"tick_state_capture_status": "sampled_server_ticks",
"tick_state_samples": 2401,
"tick_state_invalid_samples": 0,
"tick_state_first": {
"runs_game_elements": true,
"stepping": false,
"sprinting": false,
"configured_tick_rate": 20.0,
"frozen": false
},
"tick_state_last": {
"runs_game_elements": true,
"stepping": false,
"sprinting": false,
"configured_tick_rate": 20.0,
"frozen": false
},
"tick_state_changed": [],
"tick_state_frozen_samples": 0,
"tick_state_stepping_samples": 0,
"tick_state_sprinting_samples": 0,
"tick_state_suppressed_samples": 0,
"tick_state_nonstandard_rate_samples": 0,
"dropped_samples": 0,
"excluded_frames": 0,
"peak_players": 1,
"java_version": "25.0.1",
"os": "Mac OS X",
"os_version": "27.0.1",
"architecture": "aarch64",
"logical_processors": 12,
"garbage_collectors": [
"G1 Concurrent GC",
"G1 Old Generation",
"G1 Young Generation"
],
"compact_object_headers": false,
"heap_used_end_bytes": 1898423224,
"heap_committed_end_bytes": 3028287488,
"heap_max_bytes": 6442450944,
"heap_used_peak_sampled_bytes": 2850402968,
"nonheap_used_peak_sampled_bytes": 255178424,
"resource_samples": 118,
"resource_sample_interval_ms": 1000,
"process_rss_last_sampled_bytes": 3610017792,
"process_rss_peak_sampled_bytes": 3755999232,
"process_rss_samples": 119,
"process_rss_sample_interval_ms": 1000,
"process_rss_last_sample_age_ms": 25.188583,
"process_cpu_seconds": 196.232982,
"process_cpu_mean_cores": 1.6352682760034947,
"gc_collections": 33,
"gc_time_ms": 181,
"stats": {
"count": 2401,
"meanMs": 5.043994845064557,
"p50Ms": 5.053042,
"p95Ms": 5.693458,
"p99Ms": 6.132666,
"maxMs": 18.295667,
"meanRate": 198.25555551042623,
"lowOnePercentRate": 124.6633839307103,
"over16Ms": 1,
"over33Ms": 0,
"over50Ms": 0,
"over100Ms": 0
},
"samples_file": "m2-vk-retina-cache-02-ticks-1791122829137.csv",
"note": "Tick execution durations exclude scheduler sleep. meanRate is execution capacity, not observed TPS.",
"samples_sha256": "c943b4155ed5c17a4f35cfc627cbd1a90e56c4531e7e468890e050c9fb92ee64"
}
}
},
"comparison": {
"kind": "frames",
"mismatches": [
"graphics_backend: 'OpenGL' vs 'Vulkan'",
"graphics_driver: '4.1 Metal - 91.7' vs '1.2.350 MoltenVK 1.4.2'"
],
"warnings": [
"World, route, warm-up, shader state and LOD cache must also match; these are not verified by this tool."
],
"metrics": [
{
"metric": "meanRate",
"baseline": 66.86095491171913,
"candidate": 81.3038348303033,
"change_percent": 21.60136650404416
},
{
"metric": "lowOnePercentRate",
"baseline": 56.973365580408306,
"candidate": 66.85283320659684,
"change_percent": 17.34050204958548
},
{
"metric": "p95Ms",
"baseline": 15.797583,
"candidate": 13.203208,
"change_percent": -16.422607179845173
},
{
"metric": "p99Ms",
"baseline": 16.476458,
"candidate": 13.592833,
"change_percent": -17.501486059685888
},
{
"metric": "maxMs",
"baseline": 25.481334,
"candidate": 53.125791,
"change_percent": 108.48904927818927
},
{
"metric": "heap_used_end_bytes",
"baseline": 2223416320,
"candidate": 1898423224,
"change_percent": -14.616835051386145
},
{
"metric": "heap_used_peak_sampled_bytes",
"baseline": 2375691312,
"candidate": 2853250848,
"change_percent": 20.10191869574005
},
{
"metric": "nonheap_used_peak_sampled_bytes",
"baseline": 252126832,
"candidate": 255258208,
"change_percent": 1.2419844310739503
},
{
"metric": "process_rss_last_sampled_bytes",
"baseline": 4128243712,
"candidate": 3610017792,
"change_percent": -12.553181356362709
},
{
"metric": "process_rss_peak_sampled_bytes",
"baseline": 4128243712,
"candidate": 3755999232,
"change_percent": -9.017018034036074
},
{
"metric": "process_cpu_mean_cores",
"baseline": 1.6393569607054885,
"candidate": 1.635315724520871,
"change_percent": -0.24651349776063647
},
{
"metric": "gc_time_ms",
"baseline": 241,
"candidate": 181,
"change_percent": -24.896265560165975
}
]
},
"limits": [
"One matched pair, 120 seconds warmup and 120 seconds recording from independent copies of the same generated world and LOD cache.",
"Stationary private spectator scene; moving gameplay and thermal-repeat tests remain necessary.",
"Weather, simulation and additional LOD generation continue normally.",
"Vulkan has one 53.13 ms stall versus OpenGL maximum 25.48 ms."
]
}
@@ -0,0 +1,847 @@
{
"schema": 1,
"profiling": false,
"runs": {
"m2-gl-retina-01": {
"scene": "overlook",
"camera_yaw": 141.5,
"camera_pitch": 16.95,
"player_x": -41.5,
"player_y": 100.0,
"player_z": 72.5,
"warmup_seconds": 60,
"gpu": "Apple M2 Pro",
"graphics_backend": "OpenGL",
"frames": {
"minecraft": "26.3",
"profile": "m2-balanced",
"adaptive_budget_enabled": false,
"precise_worker_pacing": false,
"debounce_queue_wait": false,
"wakeable_idle_wait": false,
"dispatch_retry_backoff": false,
"tick_membership_dedup": false,
"terrain_quad_reuse": false,
"terrain_position_arithmetic": false,
"culling_ray_scratch": false,
"empty_culling_whitelist": false,
"interruptible_terrain_read": true,
"adaptive_target_fps": 60,
"world_difficulty": "NORMAL",
"render_distance": 32,
"simulation_distance": 12,
"vsync": false,
"fps_limit": 260,
"fps_limit_unlimited": true,
"inactivity_fps_limit": "minimized",
"framebuffer_width": 3456,
"framebuffer_height": 2234,
"graphics_preset": "CUSTOM",
"ambient_occlusion": true,
"improved_transparency": false,
"mipmap_levels": 4,
"entity_distance_scaling": 1.0,
"entity_shadows": true,
"particles": "ALL",
"clouds": "OFF",
"cloud_range": 128,
"fov": 110,
"dh_rendering_enabled": true,
"dh_renderer_mode": "DEFAULT",
"dh_lod_radius_chunks": 128,
"dh_horizontal_quality": "MEDIUM",
"dh_vertical_quality": "MEDIUM",
"dh_max_horizontal_resolution": "BLOCK",
"dh_transparency": "COMPLETE",
"dh_lod_shading": "AUTO",
"dh_lod_only_mode": false,
"dh_camera_quality_dropoff": true,
"dh_ambient_occlusion": true,
"dh_thread_count": 2,
"dh_runtime_ratio": 0.5,
"dh_capture_status": "available",
"iris_capture_status": "not_installed",
"iris_shaders_enabled": false,
"gpu": "Apple M2 Pro",
"graphics_backend": "OpenGL",
"graphics_driver": "4.1 Metal - 91.7",
"frame_limiter_effective_fps_start": 260,
"frame_limiter_reason_start": "NONE",
"java_vendor": "Microsoft",
"java_vm_name": "OpenJDK 64-Bit Server VM",
"java_runtime_version": "25.0.1+8-LTS",
"mods": [
"[email protected]",
"[email protected]",
"[email protected]+38c7a2d55d",
"[email protected]+fcdff87f02",
"[email protected]+3434d6d902",
"[email protected]+26.3",
"[email protected]+74ed1ea502",
"[email protected]+0ae6f91102",
"[email protected]+3434d6d902",
"[email protected]+4be74c3f02",
"[email protected]+fcdff87f02",
"[email protected]+74ed1ea55d",
"[email protected]+6b5b47fd5d",
"[email protected]+fcdff87f02",
"[email protected]+fcdff87f02",
"[email protected]+3434d6d902",
"[email protected]+427eab975d",
"[email protected]+3434d6d902",
"[email protected]+47f74c9802",
"[email protected]+3434d6d902",
"[email protected]+3434d6d902",
"[email protected]+3434d6d902",
"[email protected]+cf6bc2db5d",
"[email protected]+3434d6d902",
"[email protected]+ffef5f6702",
"[email protected]+4068fd645d",
"[email protected]+ed66f0a35d",
"[email protected]+3434d6d902",
"[email protected]+fcdff87f5d",
"[email protected]+fcdff87f02",
"[email protected]+3434d6d902",
"[email protected]+3434d6d902",
"[email protected]+fcdff87f02",
"[email protected]+fcdff87f02",
"[email protected]+fcdff87f02",
"[email protected]+79385d0b5d",
"[email protected]+b9d63e335d",
"[email protected]+3434d6d902",
"[email protected]+901a437c02",
"[email protected]+3434d6d902",
"[email protected]+4fc5413f02",
"[email protected]+fcdff87f02",
"[email protected]+48607d035d",
"[email protected]+74ed1ea502",
"[email protected]+3434d6d902",
"[email protected]+fcdff87f02",
"[email protected]+fcdff87f02",
"[email protected]+52268a0e02",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]+26.3",
"java@25",
"[email protected]+mc26.3",
"[email protected]",
"[email protected]",
"[email protected]+mc26.3",
"[email protected]",
"[email protected]"
],
"frame_limiter_effective_fps_end": 260,
"frame_limiter_reason_end": "NONE",
"frame_limiter_observations": 8432,
"frame_limiter_throttled_observations": 0,
"frame_limiter_afk_observations": 0,
"visual_settings_end": {
"world_difficulty": "NORMAL",
"render_distance": 32,
"simulation_distance": 12,
"vsync": false,
"fps_limit": 260,
"fps_limit_unlimited": true,
"inactivity_fps_limit": "minimized",
"framebuffer_width": 3456,
"framebuffer_height": 2234,
"graphics_preset": "CUSTOM",
"ambient_occlusion": true,
"improved_transparency": false,
"mipmap_levels": 4,
"entity_distance_scaling": 1.0,
"entity_shadows": true,
"particles": "ALL",
"clouds": "OFF",
"cloud_range": 128,
"fov": 110,
"dh_rendering_enabled": true,
"dh_renderer_mode": "DEFAULT",
"dh_lod_radius_chunks": 128,
"dh_horizontal_quality": "MEDIUM",
"dh_vertical_quality": "MEDIUM",
"dh_max_horizontal_resolution": "BLOCK",
"dh_transparency": "COMPLETE",
"dh_lod_shading": "AUTO",
"dh_lod_only_mode": false,
"dh_camera_quality_dropoff": true,
"dh_ambient_occlusion": true,
"dh_thread_count": 2,
"dh_runtime_ratio": 0.5,
"dh_capture_status": "available",
"iris_capture_status": "not_installed",
"iris_shaders_enabled": false
},
"visual_settings_changed": [],
"label": "m2-gl-retina-01",
"kind": "frames",
"schema": 1,
"elapsed_seconds": 120.003805666,
"dropped_samples": 0,
"excluded_frames": 0,
"peak_players": 0,
"java_version": "25.0.1",
"os": "Mac OS X",
"os_version": "27.0.1",
"architecture": "aarch64",
"logical_processors": 12,
"garbage_collectors": [
"G1 Concurrent GC",
"G1 Old Generation",
"G1 Young Generation"
],
"compact_object_headers": false,
"heap_used_end_bytes": 2950894968,
"heap_committed_end_bytes": 3208642560,
"heap_max_bytes": 6442450944,
"heap_used_peak_sampled_bytes": 3018115440,
"nonheap_used_peak_sampled_bytes": 281112144,
"resource_samples": 121,
"resource_sample_interval_ms": 1000,
"process_rss_last_sampled_bytes": 4540743680,
"process_rss_peak_sampled_bytes": 4557029376,
"process_rss_samples": 120,
"process_rss_sample_interval_ms": 1000,
"process_rss_last_sample_age_ms": 129.569166,
"process_cpu_seconds": 230.873908,
"process_cpu_mean_cores": 1.9238882193667979,
"gc_collections": 58,
"gc_time_ms": 358,
"stats": {
"count": 8431,
"meanMs": 14.229055637884,
"p50Ms": 14.047,
"p95Ms": 15.58675,
"p99Ms": 16.267291,
"maxMs": 22.121167,
"meanRate": 70.27873285824819,
"lowOnePercentRate": 56.463349822969725,
"over16Ms": 48,
"over33Ms": 0,
"over50Ms": 0,
"over100Ms": 0
},
"samples_file": "m2-gl-retina-01-frames-1791121841107.csv",
"note": "Frame start intervals include frame limiting and presentation waits; menus, pauses and unfocused frames are excluded. Not GPU timings. Process RSS, when available, is sampled separately."
},
"ticks": {
"java_vendor": "Microsoft",
"java_vm_name": "OpenJDK 64-Bit Server VM",
"java_runtime_version": "25.0.1+8-LTS",
"mods": [
"[email protected]",
"[email protected]",
"[email protected]+38c7a2d55d",
"[email protected]+fcdff87f02",
"[email protected]+3434d6d902",
"[email protected]+26.3",
"[email protected]+74ed1ea502",
"[email protected]+0ae6f91102",
"[email protected]+3434d6d902",
"[email protected]+4be74c3f02",
"[email protected]+fcdff87f02",
"[email protected]+74ed1ea55d",
"[email protected]+6b5b47fd5d",
"[email protected]+fcdff87f02",
"[email protected]+fcdff87f02",
"[email protected]+3434d6d902",
"[email protected]+427eab975d",
"[email protected]+3434d6d902",
"[email protected]+47f74c9802",
"[email protected]+3434d6d902",
"[email protected]+3434d6d902",
"[email protected]+3434d6d902",
"[email protected]+cf6bc2db5d",
"[email protected]+3434d6d902",
"[email protected]+ffef5f6702",
"[email protected]+4068fd645d",
"[email protected]+ed66f0a35d",
"[email protected]+3434d6d902",
"[email protected]+fcdff87f5d",
"[email protected]+fcdff87f02",
"[email protected]+3434d6d902",
"[email protected]+3434d6d902",
"[email protected]+fcdff87f02",
"[email protected]+fcdff87f02",
"[email protected]+fcdff87f02",
"[email protected]+79385d0b5d",
"[email protected]+b9d63e335d",
"[email protected]+3434d6d902",
"[email protected]+901a437c02",
"[email protected]+3434d6d902",
"[email protected]+4fc5413f02",
"[email protected]+fcdff87f02",
"[email protected]+48607d035d",
"[email protected]+74ed1ea502",
"[email protected]+3434d6d902",
"[email protected]+fcdff87f02",
"[email protected]+fcdff87f02",
"[email protected]+52268a0e02",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]+26.3",
"java@25",
"[email protected]+mc26.3",
"[email protected]",
"[email protected]",
"[email protected]+mc26.3",
"[email protected]",
"[email protected]"
],
"profile": "m2-balanced",
"minecraft": "26.3",
"graphics_backend": "OpenGL",
"server_runtime_kind": "integrated",
"graphics_driver": "4.1 Metal - 91.7",
"gpu": "Apple M2 Pro",
"client_graphics_capture_status": "captured_at_recording_start",
"label": "m2-gl-retina-01",
"kind": "ticks",
"schema": 1,
"elapsed_seconds": 119.99747575,
"observed_ticks_per_second": 20.000420717183292,
"tick_state_capture_status": "sampled_server_ticks",
"tick_state_samples": 2400,
"tick_state_invalid_samples": 0,
"tick_state_first": {
"stepping": false,
"runs_game_elements": true,
"frozen": false,
"configured_tick_rate": 20.0,
"sprinting": false
},
"tick_state_last": {
"stepping": false,
"runs_game_elements": true,
"frozen": false,
"configured_tick_rate": 20.0,
"sprinting": false
},
"tick_state_changed": [],
"tick_state_frozen_samples": 0,
"tick_state_stepping_samples": 0,
"tick_state_sprinting_samples": 0,
"tick_state_suppressed_samples": 0,
"tick_state_nonstandard_rate_samples": 0,
"dropped_samples": 0,
"excluded_frames": 0,
"peak_players": 1,
"java_version": "25.0.1",
"os": "Mac OS X",
"os_version": "27.0.1",
"architecture": "aarch64",
"logical_processors": 12,
"garbage_collectors": [
"G1 Concurrent GC",
"G1 Old Generation",
"G1 Young Generation"
],
"compact_object_headers": false,
"heap_used_end_bytes": 2950894968,
"heap_committed_end_bytes": 3208642560,
"heap_max_bytes": 6442450944,
"heap_used_peak_sampled_bytes": 3025083096,
"nonheap_used_peak_sampled_bytes": 281091216,
"resource_samples": 118,
"resource_sample_interval_ms": 1000,
"process_rss_last_sampled_bytes": 4540743680,
"process_rss_peak_sampled_bytes": 4557029376,
"process_rss_samples": 119,
"process_rss_sample_interval_ms": 1000,
"process_rss_last_sample_age_ms": 128.609959,
"process_cpu_seconds": 230.865902,
"process_cpu_mean_cores": 1.9239229871883368,
"gc_collections": 58,
"gc_time_ms": 358,
"stats": {
"count": 2400,
"meanMs": 6.497739260833333,
"p50Ms": 6.537167,
"p95Ms": 7.496959,
"p99Ms": 7.97775,
"maxMs": 14.175542,
"meanRate": 153.8996810825786,
"lowOnePercentRate": 108.63386371191973,
"over16Ms": 0,
"over33Ms": 0,
"over50Ms": 0,
"over100Ms": 0
},
"samples_file": "m2-gl-retina-01-ticks-1791121841107.csv",
"note": "Tick execution durations exclude scheduler sleep. meanRate is execution capacity, not observed TPS."
},
"fixture_sha256": "a71cb439bb27c6dbb894b10154ee89f85444e7225a7122bce9acd3632d2e1140",
"original_mod_sha256": "6828ab3385bde5a97cc46fa98a5122211f5ac4e2197da8ed9555c29393ebbba6"
},
"m2-vk-retina-01": {
"scene": "overlook",
"camera_yaw": 141.5,
"camera_pitch": 16.95,
"player_x": -41.5,
"player_y": 100.0,
"player_z": 72.5,
"warmup_seconds": 60,
"gpu": "Apple M2 Pro",
"graphics_backend": "Vulkan",
"frames": {
"minecraft": "26.3",
"profile": "m2-balanced",
"adaptive_budget_enabled": false,
"precise_worker_pacing": false,
"debounce_queue_wait": false,
"wakeable_idle_wait": false,
"dispatch_retry_backoff": false,
"tick_membership_dedup": false,
"terrain_quad_reuse": false,
"terrain_position_arithmetic": false,
"culling_ray_scratch": false,
"empty_culling_whitelist": false,
"interruptible_terrain_read": true,
"adaptive_target_fps": 60,
"world_difficulty": "NORMAL",
"render_distance": 32,
"simulation_distance": 12,
"vsync": false,
"fps_limit": 260,
"fps_limit_unlimited": true,
"inactivity_fps_limit": "minimized",
"framebuffer_width": 3456,
"framebuffer_height": 2234,
"graphics_preset": "CUSTOM",
"ambient_occlusion": true,
"improved_transparency": false,
"mipmap_levels": 4,
"entity_distance_scaling": 1.0,
"entity_shadows": true,
"particles": "ALL",
"clouds": "OFF",
"cloud_range": 128,
"fov": 110,
"dh_rendering_enabled": true,
"dh_renderer_mode": "DEFAULT",
"dh_lod_radius_chunks": 128,
"dh_horizontal_quality": "MEDIUM",
"dh_vertical_quality": "MEDIUM",
"dh_max_horizontal_resolution": "BLOCK",
"dh_transparency": "COMPLETE",
"dh_lod_shading": "AUTO",
"dh_lod_only_mode": false,
"dh_camera_quality_dropoff": true,
"dh_ambient_occlusion": true,
"dh_thread_count": 2,
"dh_runtime_ratio": 0.5,
"dh_capture_status": "available",
"iris_shaders_enabled": false,
"iris_capture_status": "not_installed",
"gpu": "Apple M2 Pro",
"graphics_backend": "Vulkan",
"graphics_driver": "1.2.350 MoltenVK 1.4.2",
"frame_limiter_effective_fps_start": 260,
"frame_limiter_reason_start": "NONE",
"java_vendor": "Microsoft",
"java_vm_name": "OpenJDK 64-Bit Server VM",
"java_runtime_version": "25.0.1+8-LTS",
"mods": [
"[email protected]",
"[email protected]",
"[email protected]+38c7a2d55d",
"[email protected]+fcdff87f02",
"[email protected]+3434d6d902",
"[email protected]+26.3",
"[email protected]+74ed1ea502",
"[email protected]+0ae6f91102",
"[email protected]+3434d6d902",
"[email protected]+4be74c3f02",
"[email protected]+fcdff87f02",
"[email protected]+74ed1ea55d",
"[email protected]+6b5b47fd5d",
"[email protected]+fcdff87f02",
"[email protected]+fcdff87f02",
"[email protected]+3434d6d902",
"[email protected]+427eab975d",
"[email protected]+3434d6d902",
"[email protected]+47f74c9802",
"[email protected]+3434d6d902",
"[email protected]+3434d6d902",
"[email protected]+3434d6d902",
"[email protected]+cf6bc2db5d",
"[email protected]+3434d6d902",
"[email protected]+ffef5f6702",
"[email protected]+4068fd645d",
"[email protected]+ed66f0a35d",
"[email protected]+3434d6d902",
"[email protected]+fcdff87f5d",
"[email protected]+fcdff87f02",
"[email protected]+3434d6d902",
"[email protected]+3434d6d902",
"[email protected]+fcdff87f02",
"[email protected]+fcdff87f02",
"[email protected]+fcdff87f02",
"[email protected]+79385d0b5d",
"[email protected]+b9d63e335d",
"[email protected]+3434d6d902",
"[email protected]+901a437c02",
"[email protected]+3434d6d902",
"[email protected]+4fc5413f02",
"[email protected]+fcdff87f02",
"[email protected]+48607d035d",
"[email protected]+74ed1ea502",
"[email protected]+3434d6d902",
"[email protected]+fcdff87f02",
"[email protected]+fcdff87f02",
"[email protected]+52268a0e02",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]+26.3",
"java@25",
"[email protected]+mc26.3",
"[email protected]",
"[email protected]",
"[email protected]+mc26.3",
"[email protected]",
"[email protected]"
],
"frame_limiter_effective_fps_end": 260,
"frame_limiter_reason_end": "NONE",
"frame_limiter_observations": 10674,
"frame_limiter_throttled_observations": 0,
"frame_limiter_afk_observations": 0,
"visual_settings_end": {
"world_difficulty": "NORMAL",
"render_distance": 32,
"simulation_distance": 12,
"vsync": false,
"fps_limit": 260,
"fps_limit_unlimited": true,
"inactivity_fps_limit": "minimized",
"framebuffer_width": 3456,
"framebuffer_height": 2234,
"graphics_preset": "CUSTOM",
"ambient_occlusion": true,
"improved_transparency": false,
"mipmap_levels": 4,
"entity_distance_scaling": 1.0,
"entity_shadows": true,
"particles": "ALL",
"clouds": "OFF",
"cloud_range": 128,
"fov": 110,
"dh_rendering_enabled": true,
"dh_renderer_mode": "DEFAULT",
"dh_lod_radius_chunks": 128,
"dh_horizontal_quality": "MEDIUM",
"dh_vertical_quality": "MEDIUM",
"dh_max_horizontal_resolution": "BLOCK",
"dh_transparency": "COMPLETE",
"dh_lod_shading": "AUTO",
"dh_lod_only_mode": false,
"dh_camera_quality_dropoff": true,
"dh_ambient_occlusion": true,
"dh_thread_count": 2,
"dh_runtime_ratio": 0.5,
"dh_capture_status": "available",
"iris_shaders_enabled": false,
"iris_capture_status": "not_installed"
},
"visual_settings_changed": [],
"label": "m2-vk-retina-01",
"kind": "frames",
"schema": 1,
"elapsed_seconds": 120.010259334,
"dropped_samples": 0,
"excluded_frames": 0,
"peak_players": 0,
"java_version": "25.0.1",
"os": "Mac OS X",
"os_version": "27.0.1",
"architecture": "aarch64",
"logical_processors": 12,
"garbage_collectors": [
"G1 Concurrent GC",
"G1 Old Generation",
"G1 Young Generation"
],
"compact_object_headers": false,
"heap_used_end_bytes": 2720985624,
"heap_committed_end_bytes": 3279945728,
"heap_max_bytes": 6442450944,
"heap_used_peak_sampled_bytes": 3092108504,
"nonheap_used_peak_sampled_bytes": 281973344,
"resource_samples": 121,
"resource_sample_interval_ms": 1000,
"process_rss_last_sampled_bytes": 3836805120,
"process_rss_peak_sampled_bytes": 3938910208,
"process_rss_samples": 120,
"process_rss_sample_interval_ms": 1000,
"process_rss_last_sample_age_ms": 123.285709,
"process_cpu_seconds": 238.795316,
"process_cpu_mean_cores": 1.9897908505922803,
"gc_collections": 55,
"gc_time_ms": 371,
"stats": {
"count": 10673,
"meanMs": 11.242102072144665,
"p50Ms": 11.04175,
"p95Ms": 12.467041,
"p99Ms": 12.960375,
"maxMs": 51.66175,
"meanRate": 88.9513361097983,
"lowOnePercentRate": 68.73329439688561,
"over16Ms": 15,
"over33Ms": 1,
"over50Ms": 1,
"over100Ms": 0
},
"samples_file": "m2-vk-retina-01-frames-1791122160740.csv",
"note": "Frame start intervals include frame limiting and presentation waits; menus, pauses and unfocused frames are excluded. Not GPU timings. Process RSS, when available, is sampled separately."
},
"ticks": {
"java_vendor": "Microsoft",
"java_vm_name": "OpenJDK 64-Bit Server VM",
"java_runtime_version": "25.0.1+8-LTS",
"mods": [
"[email protected]",
"[email protected]",
"[email protected]+38c7a2d55d",
"[email protected]+fcdff87f02",
"[email protected]+3434d6d902",
"[email protected]+26.3",
"[email protected]+74ed1ea502",
"[email protected]+0ae6f91102",
"[email protected]+3434d6d902",
"[email protected]+4be74c3f02",
"[email protected]+fcdff87f02",
"[email protected]+74ed1ea55d",
"[email protected]+6b5b47fd5d",
"[email protected]+fcdff87f02",
"[email protected]+fcdff87f02",
"[email protected]+3434d6d902",
"[email protected]+427eab975d",
"[email protected]+3434d6d902",
"[email protected]+47f74c9802",
"[email protected]+3434d6d902",
"[email protected]+3434d6d902",
"[email protected]+3434d6d902",
"[email protected]+cf6bc2db5d",
"[email protected]+3434d6d902",
"[email protected]+ffef5f6702",
"[email protected]+4068fd645d",
"[email protected]+ed66f0a35d",
"[email protected]+3434d6d902",
"[email protected]+fcdff87f5d",
"[email protected]+fcdff87f02",
"[email protected]+3434d6d902",
"[email protected]+3434d6d902",
"[email protected]+fcdff87f02",
"[email protected]+fcdff87f02",
"[email protected]+fcdff87f02",
"[email protected]+79385d0b5d",
"[email protected]+b9d63e335d",
"[email protected]+3434d6d902",
"[email protected]+901a437c02",
"[email protected]+3434d6d902",
"[email protected]+4fc5413f02",
"[email protected]+fcdff87f02",
"[email protected]+48607d035d",
"[email protected]+74ed1ea502",
"[email protected]+3434d6d902",
"[email protected]+fcdff87f02",
"[email protected]+fcdff87f02",
"[email protected]+52268a0e02",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]+26.3",
"java@25",
"[email protected]+mc26.3",
"[email protected]",
"[email protected]",
"[email protected]+mc26.3",
"[email protected]",
"[email protected]"
],
"profile": "m2-balanced",
"minecraft": "26.3",
"graphics_backend": "Vulkan",
"client_graphics_capture_status": "captured_at_recording_start",
"gpu": "Apple M2 Pro",
"graphics_driver": "1.2.350 MoltenVK 1.4.2",
"server_runtime_kind": "integrated",
"label": "m2-vk-retina-01",
"kind": "ticks",
"schema": 1,
"elapsed_seconds": 120.003517459,
"observed_ticks_per_second": 19.999413774016883,
"tick_state_capture_status": "sampled_server_ticks",
"tick_state_samples": 2400,
"tick_state_invalid_samples": 0,
"tick_state_first": {
"sprinting": false,
"configured_tick_rate": 20.0,
"frozen": false,
"runs_game_elements": true,
"stepping": false
},
"tick_state_last": {
"sprinting": false,
"configured_tick_rate": 20.0,
"frozen": false,
"runs_game_elements": true,
"stepping": false
},
"tick_state_changed": [],
"tick_state_frozen_samples": 0,
"tick_state_stepping_samples": 0,
"tick_state_sprinting_samples": 0,
"tick_state_suppressed_samples": 0,
"tick_state_nonstandard_rate_samples": 0,
"dropped_samples": 0,
"excluded_frames": 0,
"peak_players": 1,
"java_version": "25.0.1",
"os": "Mac OS X",
"os_version": "27.0.1",
"architecture": "aarch64",
"logical_processors": 12,
"garbage_collectors": [
"G1 Concurrent GC",
"G1 Old Generation",
"G1 Young Generation"
],
"compact_object_headers": false,
"heap_used_end_bytes": 2720985624,
"heap_committed_end_bytes": 3279945728,
"heap_max_bytes": 6442450944,
"heap_used_peak_sampled_bytes": 3100333200,
"nonheap_used_peak_sampled_bytes": 281989632,
"resource_samples": 118,
"resource_sample_interval_ms": 1000,
"process_rss_last_sampled_bytes": 3836805120,
"process_rss_peak_sampled_bytes": 3938910208,
"process_rss_samples": 119,
"process_rss_sample_interval_ms": 1000,
"process_rss_last_sample_age_ms": 122.220292,
"process_cpu_seconds": 238.792129,
"process_cpu_mean_cores": 1.98987608077059,
"gc_collections": 55,
"gc_time_ms": 371,
"stats": {
"count": 2400,
"meanMs": 5.4780099325,
"p50Ms": 5.4815,
"p95Ms": 6.318041,
"p99Ms": 6.849375,
"maxMs": 14.798334,
"meanRate": 182.54804433033036,
"lowOnePercentRate": 121.38208617176281,
"over16Ms": 0,
"over33Ms": 0,
"over50Ms": 0,
"over100Ms": 0
},
"samples_file": "m2-vk-retina-01-ticks-1791122160740.csv",
"note": "Tick execution durations exclude scheduler sleep. meanRate is execution capacity, not observed TPS."
},
"fixture_sha256": "a71cb439bb27c6dbb894b10154ee89f85444e7225a7122bce9acd3632d2e1140",
"original_mod_sha256": "6828ab3385bde5a97cc46fa98a5122211f5ac4e2197da8ed9555c29393ebbba6"
}
},
"comparison": {
"kind": "frames",
"mismatches": [
"graphics_backend: 'OpenGL' vs 'Vulkan'",
"graphics_driver: '4.1 Metal - 91.7' vs '1.2.350 MoltenVK 1.4.2'"
],
"warnings": [
"World, route, warm-up, shader state and LOD cache must also match; these are not verified by this tool."
],
"metrics": [
{
"metric": "meanRate",
"baseline": 70.27873285824819,
"candidate": 88.9513361097983,
"change_percent": 26.56935105704403
},
{
"metric": "lowOnePercentRate",
"baseline": 56.463349822969725,
"candidate": 68.73329439688561,
"change_percent": 21.730812309907233
},
{
"metric": "p95Ms",
"baseline": 15.58675,
"candidate": 12.467041,
"change_percent": -20.01513464962228
},
{
"metric": "p99Ms",
"baseline": 16.267291,
"candidate": 12.960375,
"change_percent": -20.32862140352687
},
{
"metric": "maxMs",
"baseline": 22.121167,
"candidate": 51.66175,
"change_percent": 133.53989416561976
},
{
"metric": "heap_used_end_bytes",
"baseline": 2950894968,
"candidate": 2720985624,
"change_percent": -7.791173406480933
},
{
"metric": "heap_used_peak_sampled_bytes",
"baseline": 3018115440,
"candidate": 3092108504,
"change_percent": 2.451631339853577
},
{
"metric": "nonheap_used_peak_sampled_bytes",
"baseline": 281112144,
"candidate": 281973344,
"change_percent": 0.30635460558403604
},
{
"metric": "process_rss_last_sampled_bytes",
"baseline": 4540743680,
"candidate": 3836805120,
"change_percent": -15.502715185191862
},
{
"metric": "process_rss_peak_sampled_bytes",
"baseline": 4557029376,
"candidate": 3938910208,
"change_percent": -13.564081268718164
},
{
"metric": "process_cpu_mean_cores",
"baseline": 1.9238882193667979,
"candidate": 1.9897908505922803,
"change_percent": 3.4254916975983596
},
{
"metric": "gc_time_ms",
"baseline": 358,
"candidate": 371,
"change_percent": 3.6312849162011274
}
]
},
"limits": [
"Single matched-code pair from equal starting world copies; weather and LOD generation continue normally.",
"Fixed private spectator camera; not a movement or survival gameplay equivalence test.",
"Near terrain and water visually inspected; cloud appearance and distant-cache coverage are not pixel identical.",
"Repeated copies of a common generated world and LOD cache are the next validation stage.",
"Vulkan has one 51.66 ms stall; repeat before claiming consistently better worst-case pacing."
]
}
@@ -0,0 +1,90 @@
{
"note": "Diagnostic overlap is not causation. GC and thread waits can overlap each other; sampled stacks can miss short work. Profiling intervals are not ordinary FPS measurements.",
"slow_frames": [
{
"render_thread_wait_overlap_ms": 0.0,
"render_thread_sample_leaf_methods": [],
"start": "2026-10-04T18:30:25.104841333Z",
"render_thread_sample_inclusive_methods": [],
"gc_pause_overlap_ms": 0.0,
"interval_ms": 43.896459
},
{
"render_thread_wait_overlap_ms": 0.0,
"render_thread_sample_leaf_methods": [
{
"samples": 3,
"method": "org.lwjgl.util.vma.Vma.nvmaCreateBuffer"
}
],
"start": "2026-10-04T18:31:21.695948833Z",
"render_thread_sample_inclusive_methods": [
{
"samples": 3,
"method": "com.mojang.blaze3d.framegraph.FrameGraphBuilder.execute"
},
{
"samples": 3,
"method": "com.mojang.renderpearl.backend.vulkan.VulkanDevice.createBuffer"
},
{
"samples": 3,
"method": "com.mojang.renderpearl.backend.vulkan.VulkanGpuBuffer$Direct.<init>"
},
{
"samples": 3,
"method": "com.mojang.renderpearl.frontend.FrontendGpuDevice.createBuffer"
},
{
"samples": 3,
"method": "com.seibel.distanthorizons.common.render.blaze.wrappers.buffer.BlazeVertexBufferWrapper.uploadVertexBuffer"
},
{
"samples": 3,
"method": "com.seibel.distanthorizons.core.api.internal.ClientApi.renderLodLayer"
},
{
"samples": 3,
"method": "com.seibel.distanthorizons.core.api.internal.ClientApi.renderLods"
},
{
"samples": 3,
"method": "com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding.LodBufferContainer.lambda$uploadBuffersAsync$0"
},
{
"samples": 3,
"method": "com.seibel.distanthorizons.core.render.RenderThreadTaskHandler$QueuedRunnable.run"
},
{
"samples": 3,
"method": "com.seibel.distanthorizons.core.render.RenderThreadTaskHandler.runRenderThreadTasks"
},
{
"samples": 3,
"method": "net.fabricmc.loader.impl.game.minecraft.MinecraftGameProvider.launch"
},
{
"samples": 3,
"method": "net.fabricmc.loader.impl.launch.knot.Knot.launch"
}
],
"gc_pause_overlap_ms": 0.0,
"interval_ms": 63.974708
}
],
"frame_events": 11408,
"slow_frame_threshold_ms": 25,
"schema": 1,
"profiling": true,
"original_mod_sha256": "804fd1777770e15d8d39911a708e828a028da36d3cb43d1bc2e1c7cddc318e64",
"fixture_sha256": "c4e31f8e0571b27821d65c2231f3e9c06cebf4820cb912af9c608b2f3e994739",
"gpu": "Apple M2 Pro",
"graphics_backend": "Vulkan",
"frame_csv_samples": 11409,
"limits": [
"One profiling capture with normal simulation; not an ordinary throughput comparison.",
"JFR frame events cover one fewer interval than the recorder CSV.",
"Samples identify DH terrain vertex-buffer upload inside Vulkan allocation during the 63.97 ms frame; this is a candidate, not proof of a general cause.",
"The first 43.90 ms diagnostic interval has no sampled stack and must not be attributed to GC or allocation."
]
}
@@ -0,0 +1,144 @@
{
"label": "m2-metal-upload-jfr01",
"diagnostic_only": true,
"device": "Apple M2 Pro",
"backend": "Vulkan",
"scene": "flyover",
"warmup_seconds": 120,
"recording_seconds": 120.010957,
"resolution": [
3456,
2234
],
"original_mod_sha256": "804fd1777770e15d8d39911a708e828a028da36d3cb43d1bc2e1c7cddc318e64",
"fixture_sha256": "f39f6bbe2d88d9d2a8daec42e6d235610c1af0e6fb3d36b3a0ec5b8706e08a47",
"profiling_enabled": true,
"allocation_and_upload": {
"instrumented_calls_total": 6836,
"bytes_allocated_during_recording": 849158016,
"capacity_reuse_candidates": 0,
"dropped_samples": 0,
"dropped_slow_events": 0,
"allocation_duration": {
"count": 2336,
"meanMs": 0.00452784375,
"p50Ms": 0.003125,
"p95Ms": 0.010416,
"p99Ms": 0.014209,
"maxMs": 0.097875,
"meanRate": 220855.67771635228,
"lowOnePercentRate": 48281.383905400675,
"over16Ms": 0,
"over33Ms": 0,
"over50Ms": 0,
"over100Ms": 0
},
"slow_allocations": [],
"whole_upload": {
"bytes": 849158016,
"dropped_samples": 0,
"dropped_slow_events": 0,
"duration": {
"count": 2336,
"meanMs": 0.0395248642979452,
"p50Ms": 0.0145,
"p95Ms": 0.052208,
"p99Ms": 0.074666,
"maxMs": 46.492708,
"meanRate": 25300.529622614984,
"lowOnePercentRate": 494.69449430380934,
"over16Ms": 1,
"over33Ms": 1,
"over50Ms": 0,
"over100Ms": 0
},
"slow_events": [
{
"recording_offset_ns": 10745292,
"duration_ns": 46492708,
"bytes": 542720
}
]
},
"staging_copy_and_barrier": {
"bytes": 849158016,
"dropped_samples": 0,
"dropped_slow_events": 0,
"duration": {
"count": 2336,
"meanMs": 0.013740746575342466,
"p50Ms": 0.007583,
"p95Ms": 0.042125,
"p99Ms": 0.059959,
"maxMs": 0.116333,
"meanRate": 72776.24942115466,
"lowOnePercentRate": 14014.271199504828,
"over16Ms": 0,
"over33Ms": 0,
"over50Ms": 0,
"over100Ms": 0
},
"slow_events": []
},
"note": "Original DH vertex allocations only. Durations include Java and driver work; profiling is separate from ordinary FPS measurements."
},
"frame_events": 13345,
"slow_frame_threshold_ms": 25,
"slow_frames": [
{
"interval_ms": 51.230167,
"gc_pause_overlap_ms": 0.0,
"render_thread_wait_overlap_ms": 0.0,
"terrain_upload_overlap_ms": 0.0
},
{
"interval_ms": 40.096958,
"gc_pause_overlap_ms": 0.0,
"render_thread_wait_overlap_ms": 0.0,
"terrain_upload_overlap_ms": 0.0
},
{
"interval_ms": 27.714625,
"gc_pause_overlap_ms": 24.492167,
"render_thread_wait_overlap_ms": 0.0,
"terrain_upload_overlap_ms": 0.0
},
{
"interval_ms": 26.068541,
"gc_pause_overlap_ms": 22.856917,
"render_thread_wait_overlap_ms": 0.0,
"terrain_upload_overlap_ms": 0.0
},
{
"interval_ms": 31.879791,
"gc_pause_overlap_ms": 29.20725,
"render_thread_wait_overlap_ms": 0.0,
"terrain_upload_overlap_ms": 0.0
},
{
"interval_ms": 28.467666,
"gc_pause_overlap_ms": 23.655583,
"render_thread_wait_overlap_ms": 0.0,
"terrain_upload_overlap_ms": 0.0
},
{
"interval_ms": 29.939875,
"gc_pause_overlap_ms": 20.903792,
"render_thread_wait_overlap_ms": 0.0,
"terrain_upload_overlap_ms": 0.0
},
{
"interval_ms": 28.263583,
"gc_pause_overlap_ms": 20.792166,
"render_thread_wait_overlap_ms": 0.0,
"terrain_upload_overlap_ms": 0.0
}
],
"limits": [
"Profiling is excluded from ordinary FPS comparisons.",
"Six of eight captured intervals over 25 ms overlap GC pauses; this does not explain every hitch.",
"The first whole-upload interval includes private event setup outside its JFR duration and is unattributed. Destination allocation and staging timers do not explain it.",
"Zero in-place capacity-reuse candidates were observed; this does not evaluate a cross-wrapper pool.",
"GC and upload overlap are not exclusive and do not prove causation."
]
}
@@ -0,0 +1,123 @@
{
"label": "m2-metal-zgc-upload-jfr01",
"diagnostic_only": true,
"device": "Apple M2 Pro",
"backend": "Vulkan",
"scene": "flyover",
"warmup_seconds": 120,
"recording_seconds": 120.01719675,
"original_mod_sha256": "804fd1777770e15d8d39911a708e828a028da36d3cb43d1bc2e1c7cddc318e64",
"fixture_sha256": "70c35d7804c87a834a9aede18c774749adf4283f07f5ed973c78f5cb97893f00",
"profiling_enabled": true,
"garbage_collectors": [
"ZGC Major Cycles",
"ZGC Major Pauses",
"ZGC Minor Cycles",
"ZGC Minor Pauses"
],
"allocation_and_upload": {
"instrumented_calls_total": 6963,
"bytes_allocated_during_recording": 787634112,
"capacity_reuse_candidates": 0,
"dropped_samples": 0,
"dropped_slow_events": 0,
"allocation_duration": {
"count": 2269,
"meanMs": 0.0044483962097840455,
"p50Ms": 0.002542,
"p95Ms": 0.010292,
"p99Ms": 0.016208,
"maxMs": 0.048875,
"meanRate": 224800.1196027785,
"lowOnePercentRate": 45552.221661071664,
"over16Ms": 0,
"over33Ms": 0,
"over50Ms": 0,
"over100Ms": 0
},
"slow_allocations": [],
"whole_upload": {
"bytes": 787634112,
"dropped_samples": 0,
"dropped_slow_events": 0,
"duration": {
"count": 2269,
"meanMs": 0.021492267078007932,
"p50Ms": 0.014875,
"p95Ms": 0.060458,
"p99Ms": 0.101667,
"maxMs": 0.291,
"meanRate": 46528.36279999772,
"lowOnePercentRate": 7846.821849509556,
"over16Ms": 0,
"over33Ms": 0,
"over50Ms": 0,
"over100Ms": 0
},
"slow_events": []
},
"staging_copy_and_barrier": {
"bytes": 787634112,
"dropped_samples": 0,
"dropped_slow_events": 0,
"duration": {
"count": 2269,
"meanMs": 0.015113673865138828,
"p50Ms": 0.007125,
"p95Ms": 0.049916,
"p99Ms": 0.074375,
"maxMs": 0.282083,
"meanRate": 66165.24935784133,
"lowOnePercentRate": 9546.218431589516,
"over16Ms": 0,
"over33Ms": 0,
"over50Ms": 0,
"over100Ms": 0
},
"slow_events": []
},
"note": "Original DH destination allocation, complete upload, and staging/copy/barrier timings. Nested durations are not additive. Profiling is separate from ordinary FPS measurements."
},
"jfr_summary": {
"gc_cycles": 61,
"gc_pause_events": 193,
"gc_pause_total_ms": 2.163793,
"gc_pause_longest_ms": 0.030584,
"heap_used_peak_at_gc_bytes": 6092226560
},
"frame_events": 13295,
"slow_frame_threshold_ms": 25,
"slow_frames": [
{
"interval_ms": 89.86475,
"gc_pause_overlap_ms": 0.0,
"render_thread_wait_overlap_ms": 0.0,
"terrain_upload_overlap_ms": 0.0
},
{
"interval_ms": 25.813,
"gc_pause_overlap_ms": 0.0,
"render_thread_wait_overlap_ms": 0.0,
"terrain_upload_overlap_ms": 0.0
},
{
"interval_ms": 39.459208,
"gc_pause_overlap_ms": 0.012042,
"render_thread_wait_overlap_ms": 0.0,
"terrain_upload_overlap_ms": 0.0
},
{
"interval_ms": 25.424875,
"gc_pause_overlap_ms": 0.0,
"render_thread_wait_overlap_ms": 0.0,
"terrain_upload_overlap_ms": 0.0
}
],
"limits": [
"JFR covers 15 seconds of late warmup plus the timed route. Pause totals include both.",
"Profiling results are excluded from ordinary FPS comparisons.",
"The first 89.86 ms diagnostic interval has no samples or recorded overlaps and remains unattributed.",
"One later 39.46 ms interval samples class-loader/mixin processing; event-level class loading was not yet enabled.",
"Native allocation and complete uploads were fast in this capture; that does not explain all previous stalls."
]
}
+251
View File
@@ -0,0 +1,251 @@
# Physical M2 Pro work
The current target is the user's M2 Pro MacBook Pro: 8 performance cores,
4 efficiency cores, 19 GPU cores and 16 GB unified memory. Work on other GPU
vendors and operating systems is deferred while this device is being optimized.
Gameplay rules, save formats, simulation distance 12 and normal 20 TPS remain
the compatibility target.
## First renderer observations
These are early measurements on the actual Apple GPU, not software rendering.
Vulkan successfully uses the bundled MoltenVK 1.4.2 Metal path; OpenGL uses
Apple's 4.1 driver. Both use the same core mods and Distant Horizons 3.3.4,
32 detailed chunks, 128-chunk medium LOD, FOV 110, 6 GiB maximum heap, G1,
VSync off and uncapped rendering. Original scheduling experiments remain off.
| Scene and framebuffer | OpenGL FPS | Vulkan FPS | FPS change | p99, OpenGL → Vulkan |
| --- | ---: | ---: | ---: | ---: |
| Survival spawn, 2560×1600, pair 1 | 102.65 | 115.71 | +12.72% | 13.24 → 11.29 ms |
| Survival spawn, 2560×1600, pair 2 | 100.73 | 115.55 | +14.72% | 14.58 → 12.04 ms |
| Fixed elevated view, 3456×2234, fresh-cache pair | 70.28 | 88.95 | +26.57% | 16.27 → 12.96 ms |
| Same view, common generated cache | 66.86 | 81.30 | +21.60% | 16.48 → 13.59 ms |
Initial runs warm for 60 seconds; the common-cache pair warms for 120 seconds.
All record 120 active seconds. CSV counts and
means are checked against JSON, with no lost/excluded samples, no captured
visual-setting changes and no abnormal sampled tick-manager state. Original
mod and fixture code match within each pair. Captured-setting differences are
the intended backend and driver. Spawn order is Vulkan/OpenGL/OpenGL/Vulkan;
the fresh Retina pair is OpenGL/Vulkan and the cached pair Vulkan/OpenGL.
The elevated scene uses a private spectator camera at a fixed position to
measure visible terrain without collisions. The spawn scene runs ordinary
survival and has slight position drift. Neither fixture is in a playable pack.
World simulation, weather and distant-cache generation still vary. Near terrain
and water were visually inspected, but skies and distant-cache coverage are not
pixel identical. The cached pair starts from independent copies of the same
generated world and Distant Horizons database; it still allows further LOD
generation and weather changes. These pairs do not establish a general gain.
Vulkan has a single 51.66/53.13 ms stall in the fresh/cached pair; OpenGL's
maximum is 22.12/25.48 ms respectively.
Movement, fresh terrain, reloads and longer thermal behavior remain necessary.
The supplied user recording is a separate route/world workload at 3456×2234;
these rows must not be treated as a matched comparison with its 84.27 FPS.
[Initial selected observations](../benchmarks/m2-pro-2026-10-04/initial-observations.json)
retain the first spawn pairs. [Fresh Retina](../benchmarks/m2-pro-2026-10-04/retina-initial-observations.json)
and [common-cache Retina observations](../benchmarks/m2-pro-2026-10-04/retina-cached-observations.json)
retain the two larger-framebuffer pairs. Raw files, logs, images and worlds remain in
gitignored `.local/`.
## Mac-specific playable profile
`m2-metal` requests the game's existing Vulkan/MoltenVK backend and preserves
the supplied 32 detailed chunks, 128-chunk medium LOD, simulation distance 12
and FOV 110. Vanilla clouds are off, matching the supplied capture; DH clouds
remain enabled. It shares the balanced terrain configuration and keeps original
controllers disabled. This is a separate local test profile; it does not
overwrite the existing balanced instance or implement a new Metal renderer.
The game retains its OpenGL fallback, so inspect the recorded actual backend.
```sh
python3 tools/build_mod.py
python3 tools/build_pack.py --profile m2-metal
python3 tools/build_pack.py --profile m2-metal-batched
```
Import the resulting `.mrpack` from `dist/` into Prism as a new instance. Keep
native Java 25 and maximum heap 6144 MiB. Use a copy of your world and compare
the actual framebuffer size and visual settings before judging the difference.
`m2-metal-batched` adds the cloud-rendering experiment to otherwise identical
quality settings. It is explicitly opt-in and requires Minecraft 26.3, DH 3.3.4,
Mac ARM, the actual Apple M2 Pro Vulkan device and no Iris. Unsupported devices
or backends use the original renderer. All other profiles leave the experiment
off. These 0.22.0 artifacts are local builds, not a published starter update.
Two ordinary full-Retina flyover pairs compare G1 and ZGC on that same batched
renderer and exact fixture, in opposite order. ZGC changes average FPS by
-0.33%/-0.64%, improves 1% lows from 60.01 → 67.69 and 61.40 → 65.38 FPS,
and reduces frames over 16 ms from 45 to 21/26 per 120-second run. p99 changes
12.65 → 12.40 and 12.43 → 12.48 ms; it does not consistently improve. ZGC's
worst frames are 52.67/43.17 ms versus G1's 42.72/38.80 ms. Sampled peak RSS
falls 17.45%/14.92%, while peak JVM heap grows 38.41%/39.49%. RSS is not a
complete unified-memory measure. Collector MXBean time includes concurrent
work and must not be interpreted as pause time. Both pairs have matching end
positions and quality settings, normal sampled 20 TPS, and no profiling.
[Collector observations](../benchmarks/m2-pro-2026-10-04/collector-observations.json)
retain both pairs. ZGC is an optional smoother-play candidate; G1 remains
available and isolated worst-frame hitches remain unresolved.
The local Prism ZIPs carry the selected collector and 512–6144 MiB memory
settings, alongside the same pinned mods and game configuration:
```sh
python3 tools/build_mac_candidate.py --gc zgc
python3 tools/build_mac_candidate.py --gc g1
```
Import `dist/horizon-m2-metal-batched-zgc-0.22.0-prism.zip` into Prism as a new
instance, or use the G1 counterpart for comparison. `dist/Mac-Import-zgc.command`
opens its import in an installed Prism Launcher. Enable automatic native Java
25 selection in Prism. Select fullscreen when comparing with these full-Retina
runs. The archives are generated from explicit configuration and pinned mod
inputs, with no world, account, game binary, logs or private benchmark fixture.
Archive hashes/settings are checked locally; Prism UI import is a separate
verification step.
The verified production-artifact pair at 3456×2234 averages 82.93 → 94.63 FPS
(+14.12%), with 1% lows 69.04 → 84.63 (+22.57%) and p99 13.03 → 10.93 ms
(-16.11%). Both warm and record for 120 seconds from the common copied cache;
candidate runs first, control second, with identical original JAR and fixture
hashes. Captured quality settings match, with no lost/excluded frames or abnormal
sampled simulation state. The candidate's maximum is 18.12 ms versus 52.34 ms
for this control; earlier prototype stalls remain part of the evidence.
[Production timing observations](../benchmarks/m2-pro-2026-10-04/cloud-batch-production-observations.json)
retain this pair. It is one final-artifact pair, separate from the two prototype
pairs and from the user's supplied route.
A separate 30-second flyover smoke travels about 326 horizontal blocks using
vanilla forward input at full Retina resolution, with batching active, normal
sampled simulation and no captured graphics changes. Its short run is a
functional check. Two clean 120-second flyover pairs measure 96.32 → 120.13
FPS (+24.72%) and, in reverse order, 95.43 → 119.62 FPS (+25.34%). p99
improves 14.29 → 12.68 ms and 14.40 → 12.74 ms. Each pair reaches exactly
matching end positions, runs at sampled normal 20 TPS and uses about 6.2 CPU
cores. Candidate worst frames are 52.20/59.15 ms versus control 47.46/64.62 ms;
isolated stalls persist. Asynchronous terrain completion, weather and hardware
clocks can still vary; these results do not establish a general-world speedup.
[Flyover observations](../benchmarks/m2-pro-2026-10-04/cloud-batch-flyover-observations.json)
retain both clean pairs. An earlier flyover attempt was discarded after
memory-heavy diagnostic analysis reached its recording interval.
## Profiling the next change
A separate 60-second Metal/JFR capture at 3456×2234 measures approximately
10.48 ms per GPU render interval and 0.62 ms per CPU `GameRenderer.render()`
call. Render-thread stack samples frequently include Vulkan submission waits.
These intervals cover different portions of the frame and sampling is not an
exact CPU/GPU utilization measurement. Profiling results are excluded from
ordinary FPS comparisons. OpenGL timestamps returned invalid zero intervals
on this driver; that profiling run was rejected rather than reporting a GPU win.
A later JFR diagnostic captures a 63.97 ms frame with no overlapping top-level
GC pause or recorded render-thread park/monitor wait. Three stack samples inside
that interval are in Vulkan native buffer creation, reached from DH terrain
vertex-buffer upload. This identifies a buffer-allocation candidate; one capture
does not establish a general cause or a fix. The first 43.90 ms diagnostic
interval has no stack samples and remains unattributed.
[Selected stall observations](../benchmarks/m2-pro-2026-10-04/stall-diagnostic-observations.json)
retain the correlation. `java tools/JfrSummary.java --frame-stalls recording.jfr`
uses two bounded streaming passes; overlap coverage avoids double counting
within each event category. GC and thread waits can overlap each other.
A separate full-route upload/JFR diagnostic measures 2,336 original terrain
uploads and 849 MB of destination allocations in 120 seconds. Destination
allocation peaks at 0.098 ms and staging/copy/barrier work at 0.116 ms, with
zero existing-capacity reuse candidates. Six of eight frame intervals over
25 ms overlap 20.79–29.21 ms top-level GC pauses. Two intervals remain
unattributed; the first whole-upload timing also includes private event setup
outside its JFR duration. These measurements weaken allocation as a general
explanation and motivate clean G1/ZGC comparisons before changing the collector.
They do not prove that garbage collection explains every hitch or that pooling
would help. [Upload diagnostic observations](../benchmarks/m2-pro-2026-10-04/upload-diagnostic-observations.json)
retain bounded counters and overlap measurements. The private `--vbo-profile`
option measures complete uploads as well as their nested allocation and copy
work; those timings must not be added together. Combined JFR events allow
streaming overlap analysis without expanding the full recording to JSON.
A later ZGC upload/JFR diagnostic captures 193 top-level pauses totaling
2.16 ms, with a longest pause of 0.031 ms, across 15 seconds of late warmup
plus the timed route. Complete uploads peak at 0.291 ms. Four frame intervals
over 25 ms have no meaningful GC or upload overlap; one 39.46 ms interval
samples class-loader/mixin processing. The initial 89.86 ms diagnostic interval
has no samples and remains unattributed. [ZGC diagnostic observations](../benchmarks/m2-pro-2026-10-04/zgc-diagnostic-observations.json)
retain the bounded data. This supports separating GC-related frame tails from
other hitches, not claiming that ZGC fixes every stall. Subsequent private JFR
captures enable individual class-load durations, and Apple Metal System Trace
is available for native driver and thread-wait diagnosis. Neither profiler is
part of the playable candidate.
The optional private pass profiler samples every twentieth frame and brackets
the actual Vulkan render-pass operations with device timestamps. Pass timing
can disturb scheduling; it selects candidates rather than establishing a
performance gain. The initial profile points to the main world pass, distant
terrain geometry, TAA/sharpen and repeated generic rendering. SSAO is a smaller
cost in this view. Larger renderer or native-kernel changes should follow these
measurements and preserve the visible scene.
The common-cache pass capture has no missing or invalid queries across 254
sampled frames. The main pass averages 4.12 ms, the two distant-terrain passes
2.29 ms combined, and 38.25 generic-object passes per frame total 1.69 ms.
TAA/sharpen total 1.06 ms. Per-label frame totals include frames where that
label occurs; they are diagnostic intervals affected by timestamp insertion,
not percentages of total GPU utilization. The cloud batching prototype uses
separate transient uniform snapshots for each draw. Its matched Retina pairs
improve 83.71 → 96.46 FPS (+15.23%) and 84.11 → 96.62 FPS (+14.87%), with
opposite run order. p99 improves 12.98 → 10.72 ms and 12.90 → 10.65 ms.
Candidate worst frames are 51.69/65.59 ms versus control 37.10/43.90 ms;
the experiment has not solved isolated stalls.
[Cloud batching observations](../benchmarks/m2-pro-2026-10-04/cloud-batch-initial-observations.json)
retain both prototype pairs. A frozen daytime A/B/A check produces identical
RGB pixels across the sky/cloud region (rows 80–892), and at most one channel
level of near-terrain variation, including control/control variation.
[Image observations](../benchmarks/m2-pro-2026-10-04/cloud-batch-image-observations.json)
retain the readback hashes and differences. The original 0.22.0 production
artifact also passes a frozen A/B/A check: sky/cloud pixels are identical,
near terrain is identical in A/B, and three near-terrain pixels vary by one
channel level between the control images. Its 18,463 recorded batch draws
confirm that the production code was exercised.
[Production image observations](../benchmarks/m2-pro-2026-10-04/cloud-batch-production-image-observations.json)
retain that artifact's hash and differences. Distant-cache generation still
changes far pixels in both A/B and A/A; other weather, camera heights and
movement need tests. The batch closes before geometry uploads, callback
GPU commands and incompatible draws, preserving draw order. Each draw gets
its own transient uniform slice; no shared uniform is overwritten in flight.
```sh
python3 tools/mac_benchmark.py --profile m2-metal --backend vulkan \
--label m2-local-test --seed-world /path/to/disposable/Demo_World \
--scene overlook --fullscreen --warmup 120 --seconds 120
```
Use the same fixture JAR with `--fixture` for a matched pair. `--gpu-profile`
adds whole-render timestamps; `--pass-profile` adds sampled Vulkan pass timing.
`--jfr-profile` captures private JVM events and aligned frame intervals for
stall diagnosis. Its recording begins before the timed interval to warm the
profiler, and profiling results remain separate from ordinary FPS comparisons.
Keep profiling and ordinary timing labels separate. `--cloud-batch` tests the
private renderer prototype; `--cloud-image-check --scene overlook` with
`m2-metal-batched` validates the production renderer in a separate frozen A/B/A
image check without recording FPS. `--scene spawn` uses normal
survival; `overlook` changes only the private copied world to a fixed spectator
view. `--scene flyover` holds vanilla forward input during recording, exercising
normal spectator movement and terrain loading in that private copy. Existing
labels cannot be overwritten. No account data is needed.
## AFK measurement correction
Minecraft's default `afk` inactivity option caps stationary gameplay at 30 FPS
after one minute and 10 FPS after ten minutes, even when the configured cap is
unlimited. A focused stationary run measured 29.96 FPS and was rejected as a
renderer comparison. Private benchmarks select the vanilla `minimized` option;
playable profiles retain idle power saving.
The recorder now captures the inactivity option, effective limiter values and
reasons at the boundaries, and counts limiter/AFK observations during active
gameplay. Both comparison tools warn about these observations and mark different
inactivity options as mismatched. Legacy reports remain usable with an unknown
limiter warning. Disabling the idle cap is a measurement correction, not a game
performance optimization.
+12 -2
View File
@@ -2,8 +2,11 @@
For the 16 GB M2 Pro, start with the published 0.21.0 packs. Core and balanced
pass title/demo-world rendering and save/exit probes on the hosted macOS ARM64
software renderer. Gameplay and FPS on this physical Mac remain unmeasured.
The Linux software-rendering observations do not predict an M2 speedup.
software renderer. [Physical M2 measurements](m2-pro-performance.md) now compare
OpenGL with the bundled Metal-backed Vulkan path, including full Retina.
Repeated gameplay validation continues; Linux software-rendering observations
do not predict an M2 speedup. The new local `m2-metal` profile is built separately
from the published starter below.
## Import after installing Prism
@@ -55,6 +58,13 @@ Warm each run for at least three minutes after entering the world and dismissing
menus. Longer generation workloads may need more time. Press **F8** (possibly
**Fn+F8**) in focused gameplay for a 120-second recording. Keep the view or route
consistent and avoid menus during recording. After it finishes, save and quit.
For a stationary comparison, select the game's **Minimized** inactivity FPS
option on both sides; the default **AFK** option imposes a 30 FPS idle cap after
one minute. Restore your preferred idle behavior after testing. New recordings
capture effective limiter observations; older recordings do not establish that
a configured unlimited cap stayed unlimited throughout a stationary run.
Repeat three pairs in this order:
| Pair | First run | Second run |
+35 -2
View File
@@ -2,7 +2,40 @@
## Starting point
The first playable Java foundation is in the root README. Client optimization has priority. Vanilla gameplay, world formats and multiplayer behavior must be preserved; existing modpack compatibility is not required. Any language, renderer or architecture is in scope when measured gains justify it. Windows/Linux and Apple Silicon/Intel macOS are targets, with the M2 Pro/16 GB Mac prioritized.
The first playable Java foundation is in the root README. Current implementation
and performance work targets the physical **M2 Pro / 16 GB MacBook Pro**: 8
performance cores, 4 efficiency cores and a 19-core GPU. Windows/Linux,
NVIDIA/AMD and other Macs are deferred until this device has a convincing,
repeatable gameplay improvement. Existing portable packs remain available.
Vanilla gameplay, world formats and multiplayer behavior must be preserved;
existing modpack compatibility is not required. Any language, renderer or
architecture is in scope when measured gains justify it. Full Retina resolution,
32 detailed chunks and 128-chunk distant terrain are the user's supplied workload;
reducing those settings is a separate quality tradeoff, not proof of a faster
renderer.
The user's 4 October capture reports 84.27 average FPS, 48.18 slowest-1%-average
FPS and 18.14 ms p99 at 3456×2234, OpenGL, FOV 110 and normal simulation distance
12. About 81.59 seconds of frame intervals were accepted within 120.09 elapsed
seconds; menu, paused or unfocused frames were excluded. Tick execution averages
7.89 ms with no execution above 50 ms. Its 1,646 tick invocations correspond to
82.3 seconds at 20 TPS, so the 13.71 observed invocation rate across the whole
recording does not demonstrate an overloaded server. GPU time and movement route
are unknown. Original captures remain private under `.local/user-baseline/`.
Initial physical-Mac inspection verifies the existing Vulkan backend creates a
MoltenVK 1.4.2 device on this GPU and renders a nonuniform demo-world framebuffer.
This establishes an available Metal-backed path, not an FPS gain or complete
visual equivalence. Matched private OpenGL/Vulkan measurements come first.
A stationary 60-second warm-up exposed Minecraft's default AFK limiter: an
otherwise unlimited recording settled at 29.96 FPS. That run is rejected as a
renderer baseline. Private stationary instances now select the vanilla
`minimized` inactivity option; playable defaults retain their normal idle power
saving. Recorder metadata captures the option, effective limiter boundaries and
throttled observations. The private benchmark checks active duration, sample
files, unchanged settings and normal tick-manager state before accepting a run.
Inspect both pinned game JARs locally, retain bytecode as the reference, and measure actual Minecraft workloads before selecting a subsystem to replace. The Rust face-count experiment establishes a small correctness/benchmark workflow; it does not identify a Minecraft bottleneck.
@@ -46,7 +79,7 @@ jfr summary .local/baseline.jfr
jfr print --events jdk.ExecutionSample,jdk.GarbageCollection .local/baseline.jfr > .local/baseline-events.txt
```
Collect CPU profiles, allocation/GC information, frame-time percentiles, server tick-time percentiles, and relevant GPU timings. JFR samples alone do not measure FPS or GPU cost. JVM compilation/warm-up, profiling overhead, world generation, and background activity must be controlled when comparing runs. The [core comparison](core-performance.md) now contains stationary Linux software-rendering observations; Mac GPU, movement/generation and populated dedicated-server measurements remain open.
Collect CPU profiles, allocation/GC information, frame-time percentiles, server tick-time percentiles, and relevant GPU timings. JFR samples alone do not measure FPS or GPU cost. JVM compilation/warm-up, profiling overhead, world generation, and background activity must be controlled when comparing runs. The [core comparison](core-performance.md) contains stationary Linux software-rendering observations; [physical M2 Pro observations](m2-pro-performance.md) now include repeated renderer measurements and GPU pass timings. Movement/generation and populated dedicated-server measurements remain open.
## First native integration experiment
@@ -4,6 +4,7 @@ import com.mojang.brigadier.arguments.IntegerArgumentType;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.platform.InputConstants;
import com.mojang.blaze3d.platform.FramerateLimitTracker.FramerateThrottleReason;
import dev.horizonlab.HorizonLab;
import dev.horizonlab.metrics.Recording;
import dev.horizonlab.metrics.Reports;
@@ -30,6 +31,11 @@ public final class HorizonClient implements ClientModInitializer {
private static DistantTerrainBudget terrainBudget;
private static long previousBudgetFrame;
private static int targetFps = 60;
private static long limiterObservations;
private static long throttledObservations;
private static long afkObservations;
private static long cloudDrawsStart, cloudPassesStart;
private static Map<String, Object> visualSettingsStart = Map.of();
@Override public void onInitializeClient() {
KeyMapping benchmarkKey = KeyMappingHelper.registerKeyMapping(new KeyMapping(
@@ -103,14 +109,22 @@ public final class HorizonClient implements ClientModInitializer {
context.put("empty_culling_whitelist", dev.horizonlab.mixin.HorizonMixinPlugin.cullingEmptyEnabled());
context.put("interruptible_terrain_read", dev.horizonlab.mixin.HorizonMixinPlugin.terrainReadWaitEnabled());
context.put("adaptive_target_fps", targetFps);
context.putAll(visualSettings(client));
visualSettingsStart = visualSettings(client);
context.putAll(visualSettingsStart);
var device = RenderSystem.getDevice().getDeviceInfo();
context.put("gpu", device.name());
context.put("graphics_backend", device.backendName());
context.put("graphics_driver", device.driverInfo());
context.put("frame_limiter_effective_fps_start", client.getFramerateLimitTracker().getFramerateLimit());
context.put("frame_limiter_reason_start", client.getFramerateLimitTracker().getThrottleReason().name());
cloudDrawsStart = dev.horizonlab.render.CloudBatchStatus.draws;
cloudPassesStart = dev.horizonlab.render.CloudBatchStatus.passes;
context.putAll(dev.horizonlab.metrics.SoftwareContext.capture());
frames = new Recording(label, "frames", output, seconds, context);
previousStart = 0;
limiterObservations = 0;
throttledObservations = 0;
afkObservations = 0;
if (client.getSingleplayerServer() != null) HorizonLab.startTicks(label, seconds, output, profile, "integrated", context);
message("Recording " + seconds + " seconds. Close chat, keep the game focused, and run your route.");
return 1;
@@ -124,6 +138,7 @@ public final class HorizonClient implements ClientModInitializer {
context.put("vsync", client.options.enableVsync().get());
context.put("fps_limit", client.options.framerateLimit().get());
context.put("fps_limit_unlimited", client.options.framerateLimit().get() >= Options.UNLIMITED_FRAMERATE_CUTOFF);
context.put("inactivity_fps_limit", client.options.inactivityFpsLimit().get().getSerializedName());
context.put("framebuffer_width", client.getWindow().getWidth());
context.put("framebuffer_height", client.getWindow().getHeight());
context.put("graphics_preset", client.options.graphicsPreset().get().toString());
@@ -142,6 +157,8 @@ public final class HorizonClient implements ClientModInitializer {
catch (RuntimeException | LinkageError unavailable) { context.put("dh_capture_status", "unavailable"); }
} else context.put("dh_capture_status", "not_installed");
context.putAll(ShaderSettings.read(loader.isModLoaded("iris")));
context.put("dh_cloud_batch_requested", dev.horizonlab.render.CloudBatchStatus.requested);
context.put("dh_cloud_batch_enabled", dev.horizonlab.render.CloudBatchStatus.active);
return context;
}
@@ -165,6 +182,10 @@ public final class HorizonClient implements ClientModInitializer {
previousBudgetFrame = now;
}
if (recording != null) {
var reason = client.getFramerateLimitTracker().getThrottleReason();
limiterObservations++;
if (reason != FramerateThrottleReason.NONE) throttledObservations++;
if (reason == FramerateThrottleReason.SHORT_AFK || reason == FramerateThrottleReason.LONG_AFK) afkObservations++;
if (previousStart != 0) recording.add(now - previousStart);
previousStart = now;
}
@@ -182,18 +203,17 @@ public final class HorizonClient implements ClientModInitializer {
frames = null;
previousStart = 0;
HorizonLab.finishTicks();
var limiter = Minecraft.getInstance().getFramerateLimitTracker();
recording.context.put("frame_limiter_effective_fps_end", limiter.getFramerateLimit());
recording.context.put("frame_limiter_reason_end", limiter.getThrottleReason().name());
recording.context.put("frame_limiter_observations", limiterObservations);
recording.context.put("frame_limiter_throttled_observations", throttledObservations);
recording.context.put("frame_limiter_afk_observations", afkObservations);
recording.context.put("dh_cloud_batch_draws_recorded", dev.horizonlab.render.CloudBatchStatus.draws - cloudDrawsStart);
recording.context.put("dh_cloud_batch_passes_recorded", dev.horizonlab.render.CloudBatchStatus.passes - cloudPassesStart);
Map<String, Object> end = visualSettings(Minecraft.getInstance());
java.util.List<String> changed = new java.util.ArrayList<>();
for (var setting : end.entrySet()) {
// Adaptive duty cycle is expected to change; visual quality changes are separate.
if (!setting.getKey().equals("dh_runtime_ratio")
&& !java.util.Objects.equals(recording.context.get(setting.getKey()), setting.getValue()))
changed.add(setting.getKey());
}
for (String setting : recording.context.keySet()) {
if ((setting.startsWith("dh_") || setting.startsWith("iris_")) && !end.containsKey(setting))
changed.add(setting);
}
var changed = dev.horizonlab.metrics.VisualSettingsChanges.between(visualSettingsStart, end);
visualSettingsStart = Map.of();
recording.context.put("visual_settings_end", end);
recording.context.put("visual_settings_changed", changed);
Reports.write(recording.finish()).whenComplete((path, error) -> {
@@ -0,0 +1,25 @@
package dev.horizonlab.metrics;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/** Compare visual snapshots independently of changing measurement metadata. */
public final class VisualSettingsChanges {
private VisualSettingsChanges() {}
public static List<String> between(Map<String, ?> before, Map<String, ?> after) {
var changed = new ArrayList<String>();
for (var setting : after.entrySet()) {
if (!setting.getKey().equals("dh_runtime_ratio")
&& !Objects.equals(before.get(setting.getKey()), setting.getValue()))
changed.add(setting.getKey());
}
for (String setting : before.keySet()) {
if ((setting.startsWith("dh_") || setting.startsWith("iris_")) && !after.containsKey(setting))
changed.add(setting);
}
return changed;
}
}
@@ -0,0 +1,27 @@
package dev.horizonlab.mixin;
import com.mojang.renderpearl.frontend.FrontendCommandEncoder;
import dev.horizonlab.render.DhCloudBatch;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
/** Preserve command order when uploads or callbacks interrupt a batch. */
@Mixin(FrontendCommandEncoder.class)
public abstract class DhCloudBarrierMixin {
@Inject(method = {"submit", "clearColorTexture", "clearColorAndDepthTextures", "clearDepthTexture",
"writeToBuffer", "copyToBuffer", "writeToTexture", "copyBufferToTexture", "copyTextureToBuffer",
"copyTextureToTexture", "writeTimestamp"}, at = @At("HEAD"))
private void horizon$beforeCommand(CallbackInfo callback) {
var batch = DhCloudBatch.current;
if (batch != null) batch.closePass();
}
@Inject(method = {"createRenderPass", "createFence"}, at = @At("HEAD"))
private void horizon$beforeResource(CallbackInfoReturnable<?> callback) {
var batch = DhCloudBatch.current;
if (batch != null) batch.closePass();
}
}
@@ -0,0 +1,62 @@
package dev.horizonlab.mixin;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.renderpearl.api.pipeline.RenderPipeline;
import com.seibel.distanthorizons.common.render.blaze.BlazeDhGenericObjectRenderer;
import com.seibel.distanthorizons.common.render.blaze.wrappers.uniform.BlazeUniformBufferWrapper;
import com.seibel.distanthorizons.core.render.RenderParams;
import com.seibel.distanthorizons.core.render.renderer.RenderableBoxGroup;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IProfilerWrapper;
import dev.horizonlab.render.DhCloudBatch;
import dev.horizonlab.render.CloudBatchStatus;
import com.llamalad7.mixinextras.injector.wrapmethod.WrapMethod;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
/** Opt-in M2 Pro cloud batching on the inspected DH renderer. */
@Mixin(BlazeDhGenericObjectRenderer.class)
public abstract class DhCloudBatchMixin {
@Shadow @Final private BlazeUniformBufferWrapper vertUniformBufferWrapper;
@Shadow private RenderPipeline pipeline;
@WrapMethod(method = "render")
private void horizon$cloudBatch(RenderParams params, IProfilerWrapper profiler, boolean ssao,
Operation<Void> original) {
var device = RenderSystem.getDevice().getDeviceInfo();
boolean active = HorizonMixinPlugin.cloudBatchEnabled() && device.backendName().equals("Vulkan")
&& device.name().equals("Apple M2 Pro");
CloudBatchStatus.active = active;
if (!active) {
original.call(params, profiler, ssao);
return;
}
var parent = DhCloudBatch.current;
if (parent != null) {
parent.closePass();
DhCloudBatch.current = null;
try { original.call(params, profiler, ssao); }
finally { DhCloudBatch.current = parent; }
return;
}
var batch = new DhCloudBatch(vertUniformBufferWrapper);
DhCloudBatch.current = batch;
try (batch) { original.call(params, profiler, ssao); }
finally { DhCloudBatch.current = null; }
}
@Inject(method = "renderBoxGroupInstanced", at = @At("HEAD"), cancellable = true)
private void horizon$drawCloud(RenderParams params, RenderableBoxGroup group,
IProfilerWrapper profiler, CallbackInfo callback) {
var batch = DhCloudBatch.current;
if (batch != null) {
batch.draw(params, group, pipeline);
callback.cancel();
}
}
}
@@ -0,0 +1,21 @@
package dev.horizonlab.mixin;
import com.mojang.renderpearl.api.buffers.GpuBufferSlice;
import com.mojang.renderpearl.api.commands.CommandEncoder;
import com.seibel.distanthorizons.common.render.blaze.wrappers.uniform.BlazeUniformBufferWrapper;
import java.nio.ByteBuffer;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
/** Give each draw its own transient uniform snapshot. */
@Mixin(BlazeUniformBufferWrapper.class)
public abstract class DhCloudUniformMixin {
@Redirect(method = "finishAndUpload", at = @At(value = "INVOKE", target =
"Lcom/mojang/renderpearl/api/commands/CommandEncoder;writeToBuffer(Lcom/mojang/renderpearl/api/buffers/GpuBufferSlice;Ljava/nio/ByteBuffer;)V"))
private void horizon$snapshot(CommandEncoder encoder, GpuBufferSlice destination, ByteBuffer data) {
var batch = dev.horizonlab.render.DhCloudBatch.current;
if (batch != null && batch.uniformWrapper == (Object) this) batch.captureUniform(data);
else encoder.writeToBuffer(destination, data);
}
}
@@ -21,9 +21,32 @@ public final class HorizonMixinPlugin implements IMixinConfigPlugin {
private static boolean cullingEmpty;
private static boolean terrainReadWait;
private static boolean cullingRayScratch;
private static boolean cloudBatch;
@Override public void onLoad(String mixinPackage) {
var loader = FabricLoader.getInstance();
try {
var properties = new Properties();
var cloudFile = loader.getConfigDir().resolve("horizon-cloud-batch.properties");
if (Files.exists(cloudFile)) {
try (var reader = Files.newBufferedReader(cloudFile)) { properties.load(reader); }
}
boolean requested = Boolean.parseBoolean(properties.getProperty("enabled", "false"));
dev.horizonlab.render.CloudBatchStatus.requested = requested;
boolean supported = loader.getModContainer("distanthorizons")
.map(mod -> mod.getMetadata().getVersion().getFriendlyString().equals("3.3.4")).orElse(false)
&& loader.getModContainer("minecraft")
.map(mod -> mod.getMetadata().getVersion().getFriendlyString().equals("26.3")).orElse(false)
&& System.getProperty("os.name", "").startsWith("Mac")
&& Set.of("aarch64", "arm64").contains(System.getProperty("os.arch", ""))
&& !loader.isModLoaded("iris");
cloudBatch = requested && supported;
if (requested) LoggerFactory.getLogger("horizonlab").info(
"DH cloud batching: {} (requires Mac ARM, Minecraft 26.3, DH 3.3.4, no Iris; runtime requires M2 Pro Vulkan)",
cloudBatch ? "hooks enabled" : "unavailable");
} catch (Exception error) {
LoggerFactory.getLogger("horizonlab").warn("Cloud batching disabled: invalid configuration", error);
}
var file = loader.getConfigDir().resolve("horizon-worker-pacing.properties");
try {
Properties properties = new Properties();
@@ -173,7 +196,10 @@ public final class HorizonMixinPlugin implements IMixinConfigPlugin {
public static boolean cullingEmptyEnabled() { return cullingEmpty; }
public static boolean cullingRayScratchEnabled() { return cullingRayScratch; }
public static boolean terrainReadWaitEnabled() { return terrainReadWait; }
public static boolean cloudBatchEnabled() { return cloudBatch; }
@Override public boolean shouldApplyMixin(String targetClassName, String mixinClassName) {
if (mixinClassName.endsWith("DhCloudBatchMixin") || mixinClassName.endsWith("DhCloudUniformMixin")
|| mixinClassName.endsWith("DhCloudBarrierMixin")) return cloudBatch;
if (mixinClassName.endsWith("DhWorkerPacingMixin")) return workerPacing;
if (mixinClassName.endsWith("DhDebounceWaitMixin")) return debounceWait;
if (mixinClassName.endsWith("DhPriorityDispatchMixin")) return dispatchRetry;
@@ -0,0 +1,11 @@
package dev.horizonlab.render;
/** Render-thread counters kept independent of optional DH classes. */
public final class CloudBatchStatus {
public static boolean requested;
public static boolean active;
public static long draws;
public static long passes;
private CloudBatchStatus() {}
}
@@ -0,0 +1,79 @@
package dev.horizonlab.render;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.renderpearl.api.buffers.GpuBuffer;
import com.mojang.renderpearl.api.buffers.GpuBufferSlice;
import com.mojang.renderpearl.api.commands.RenderPass;
import com.mojang.renderpearl.api.pipeline.IndexType;
import com.mojang.renderpearl.api.pipeline.RenderPipeline;
import com.seibel.distanthorizons.common.render.blaze.BlazeDhMetaRenderer;
import com.seibel.distanthorizons.common.render.blaze.objects.BlazeGenericObjectVertexContainer;
import com.seibel.distanthorizons.common.render.blaze.wrappers.uniform.BlazeUniformBufferWrapper;
import com.seibel.distanthorizons.common.wrappers.misc.LightMapWrapper;
import com.seibel.distanthorizons.core.render.RenderParams;
import com.seibel.distanthorizons.core.render.renderer.RenderableBoxGroup;
import java.nio.ByteBuffer;
import java.util.Optional;
import java.util.OptionalDouble;
/** Render-thread-only state, scoped to one original generic-render invocation. */
public final class DhCloudBatch implements AutoCloseable {
public static DhCloudBatch current;
public final BlazeUniformBufferWrapper uniformWrapper;
private GpuBufferSlice uniform;
private RenderPass pass;
private Object lightmap;
public DhCloudBatch(BlazeUniformBufferWrapper wrapper) {
uniformWrapper = wrapper;
}
public void captureUniform(ByteBuffer data) {
var copy = data.duplicate();
copy.position(0);
var encoder = RenderSystem.getDevice().createCommandEncoder();
try (var mapped = encoder.transientMemory().allocateGpuMapped(copy.remaining(), 256, GpuBuffer.USAGE_UNIFORM)) {
mapped.data().put(copy);
uniform = mapped.slice();
}
}
public void draw(RenderParams params, RenderableBoxGroup group, RenderPipeline pipeline) {
if (uniform == null) throw new AssertionError("Missing DH uniform snapshot");
var vertices = (BlazeGenericObjectVertexContainer) group.vertexBufferContainer;
boolean cloud = group.getResourceLocationNamespace().equals("DistantHorizons")
&& group.getResourceLocationPath().equals("Clouds");
if (!cloud || lightmap != params.lightmap) closePass();
if (pass == null) {
var targets = BlazeDhMetaRenderer.INSTANCE;
pass = RenderSystem.getDevice().createCommandEncoder().createRenderPass(
() -> "horizon:DH cloud batch", targets.dhColorTextureWrapper.getTextureView(),
Optional.empty(), targets.dhDepthTextureWrapper.getTextureView(), OptionalDouble.empty());
pass.setPipeline(RenderSystem.getCompiledPipeline(pipeline));
var light = ((LightMapWrapper) params.lightmap).getTextureViewWrapper();
pass.setUniform("uLightMap", light.getTextureView(), light.getTextureSampler());
lightmap = params.lightmap;
CloudBatchStatus.passes++;
}
pass.setUniform("vertUniformBlock", uniform);
pass.setIndexBuffer(vertices.indexGpuBuffer, IndexType.INT);
pass.setVertexBuffer(0, vertices.vboGpuBuffer.slice());
if (vertices.uploadedBoxCount > 0) {
pass.drawIndexed(Math.multiplyExact(vertices.uploadedBoxCount, 36), 1, 0, 0, 0);
CloudBatchStatus.draws++;
}
uniform = null;
if (!cloud) closePass();
}
public void closePass() {
var closing = pass;
pass = null;
lightmap = null;
if (closing != null) closing.close();
}
@Override public void close() {
closePass();
}
}
@@ -1,7 +1,7 @@
{
"schemaVersion": 1,
"id": "horizonlab",
"version": "0.21.0",
"version": "0.22.0",
"name": "Horizon Lab",
"description": "Client frame pacing and server tick measurements for reproducible optimization experiments.",
"authors": [
@@ -16,7 +16,10 @@
"DhTerrainReadWaitMixin",
"CullingRayScratchMixin",
"DhRelativePositionMixin",
"DhQuadReuseMixin"
"DhQuadReuseMixin",
"DhCloudBatchMixin",
"DhCloudUniformMixin",
"DhCloudBarrierMixin"
],
"injectors": {
"defaultRequire": 1
@@ -0,0 +1,27 @@
package dev.horizonlab.dispatch.fixture;
import com.mojang.renderpearl.frontend.FrontendCommandEncoder;
import dev.horizonlab.metrics.MacBenchmarkIntegration.MacCloudBatchState;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
/** Preserve command order when uploads or callbacks interrupt a private batch. */
@Mixin(FrontendCommandEncoder.class)
public abstract class MacDhCloudBarrierMixin {
@Inject(method = {"submit", "clearColorTexture", "clearColorAndDepthTextures", "clearDepthTexture",
"writeToBuffer", "copyToBuffer", "writeToTexture", "copyBufferToTexture", "copyTextureToBuffer",
"copyTextureToTexture", "writeTimestamp"}, at = @At("HEAD"))
private void horizon$beforeCommand(CallbackInfo callback) {
var batch = MacCloudBatchState.current;
if (batch != null) batch.closePass();
}
@Inject(method = {"createRenderPass", "createFence"}, at = @At("HEAD"))
private void horizon$beforeResource(CallbackInfoReturnable<?> callback) {
var batch = MacCloudBatchState.current;
if (batch != null) batch.closePass();
}
}
@@ -0,0 +1,52 @@
package dev.horizonlab.dispatch.fixture;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.renderpearl.api.pipeline.RenderPipeline;
import com.seibel.distanthorizons.common.render.blaze.BlazeDhGenericObjectRenderer;
import com.seibel.distanthorizons.common.render.blaze.wrappers.uniform.BlazeUniformBufferWrapper;
import com.seibel.distanthorizons.core.render.RenderParams;
import com.seibel.distanthorizons.core.render.renderer.RenderableBoxGroup;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IProfilerWrapper;
import dev.horizonlab.metrics.MacBenchmarkIntegration;
import dev.horizonlab.metrics.MacBenchmarkIntegration.MacCloudBatchState;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
/** Private experiment on the pinned DH renderer; not part of playable packs. */
@Mixin(BlazeDhGenericObjectRenderer.class)
public abstract class MacDhCloudBatchMixin {
@Shadow @Final private BlazeUniformBufferWrapper vertUniformBufferWrapper;
@Shadow private RenderPipeline pipeline;
@Inject(method = "render", at = @At("HEAD"))
private void horizon$cloudBatch(RenderParams params, IProfilerWrapper profiler, boolean ssao, CallbackInfo callback) {
var device = RenderSystem.getDevice().getDeviceInfo();
if (!MacBenchmarkIntegration.cloudBatchEnabled || !device.backendName().equals("Vulkan") || !device.name().contains("Apple M2 Pro")) return;
if (MacCloudBatchState.current != null) throw new AssertionError("Nested private DH batch");
MacCloudBatchState.current = new MacCloudBatchState(vertUniformBufferWrapper);
}
@Inject(method = "render", at = @At("RETURN"))
private void horizon$endBatch(RenderParams params, IProfilerWrapper profiler, boolean ssao, CallbackInfo callback) {
var batch = MacCloudBatchState.current;
if (batch != null) {
try { batch.close(); }
finally { MacCloudBatchState.current = null; }
}
}
@Inject(method = "renderBoxGroupInstanced", at = @At("HEAD"), cancellable = true)
private void horizon$drawCloud(RenderParams params, RenderableBoxGroup group,
IProfilerWrapper profiler, CallbackInfo callback) {
var batch = MacCloudBatchState.current;
if (batch != null) {
batch.draw(params, group, pipeline);
callback.cancel();
}
}
}
@@ -0,0 +1,17 @@
package dev.horizonlab.dispatch.fixture;
import com.seibel.distanthorizons.core.render.renderer.CloudRenderHandler;
import dev.horizonlab.metrics.MacBenchmarkIntegration;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
/** Freeze wall-clock cloud movement only during private image validation. */
@Mixin(CloudRenderHandler.class)
public abstract class MacDhCloudClockMixin {
@Redirect(method = "preRender", at = @At(value = "INVOKE", target = "Ljava/lang/System;currentTimeMillis()J"))
private long horizon$validationClock() {
long frozen = MacBenchmarkIntegration.cloudValidationClock;
return frozen == 0 ? System.currentTimeMillis() : frozen;
}
}
@@ -0,0 +1,21 @@
package dev.horizonlab.dispatch.fixture;
import com.mojang.renderpearl.api.buffers.GpuBufferSlice;
import com.mojang.renderpearl.api.commands.CommandEncoder;
import com.seibel.distanthorizons.common.render.blaze.wrappers.uniform.BlazeUniformBufferWrapper;
import java.nio.ByteBuffer;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
/** Give each experimental draw its own transient uniform snapshot. */
@Mixin(BlazeUniformBufferWrapper.class)
public abstract class MacDhCloudUniformMixin {
@Redirect(method = "finishAndUpload", at = @At(value = "INVOKE", target =
"Lcom/mojang/renderpearl/api/commands/CommandEncoder;writeToBuffer(Lcom/mojang/renderpearl/api/buffers/GpuBufferSlice;Ljava/nio/ByteBuffer;)V"))
private void horizon$snapshot(CommandEncoder encoder, GpuBufferSlice destination, ByteBuffer data) {
var batch = dev.horizonlab.metrics.MacBenchmarkIntegration.MacCloudBatchState.current;
if (batch != null && batch.uniformWrapper == (Object) this) batch.captureUniform(data);
else encoder.writeToBuffer(destination, data);
}
}
@@ -0,0 +1,53 @@
package dev.horizonlab.dispatch.fixture;
import com.mojang.renderpearl.api.buffers.GpuBuffer;
import com.mojang.renderpearl.api.buffers.GpuBufferSlice;
import com.mojang.renderpearl.api.commands.CommandEncoder;
import com.mojang.renderpearl.api.device.GpuDevice;
import com.llamalad7.mixinextras.injector.wrapmethod.WrapMethod;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.seibel.distanthorizons.common.render.blaze.wrappers.buffer.BlazeVertexBufferWrapper;
import dev.horizonlab.metrics.MacBenchmarkIntegration;
import java.util.function.Supplier;
import java.nio.ByteBuffer;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
/** Private allocation timings; the original allocation and upload remain intact. */
@Mixin(BlazeVertexBufferWrapper.class)
public abstract class MacDhVertexAllocationMixin {
@Shadow public GpuBuffer vertexGpuBuffer;
@WrapMethod(method = "uploadVertexBuffer")
private void horizon$upload(ByteBuffer data, int vertices, Operation<Void> original) {
long bytes = data.remaining();
long started = System.nanoTime();
var event = MacBenchmarkIntegration.beginTerrainUpload(bytes);
try { original.call(data, vertices); }
finally {
MacBenchmarkIntegration.profileVboUpload(bytes, started, System.nanoTime() - started, false);
if (event != null) { event.end(); event.commit(); }
}
}
@Redirect(method = "uploadVertexBuffer", at = @At(value = "INVOKE", target =
"Lcom/mojang/renderpearl/api/commands/CommandEncoder;writeToBuffer(Lcom/mojang/renderpearl/api/buffers/GpuBufferSlice;Ljava/nio/ByteBuffer;)V"))
private void horizon$copy(CommandEncoder encoder, GpuBufferSlice destination, ByteBuffer data) {
long bytes = data.remaining();
long started = System.nanoTime();
try { encoder.writeToBuffer(destination, data); }
finally { MacBenchmarkIntegration.profileVboUpload(bytes, started, System.nanoTime() - started, true); }
}
@Redirect(method = "uploadVertexBuffer", at = @At(value = "INVOKE", target =
"Lcom/mojang/renderpearl/api/device/GpuDevice;createBuffer(Ljava/util/function/Supplier;IJ)Lcom/mojang/renderpearl/api/buffers/GpuBuffer;"))
private GpuBuffer horizon$allocation(GpuDevice device, Supplier<String> label, int usage, long bytes) {
boolean capacityFits = vertexGpuBuffer != null && vertexGpuBuffer.size() >= bytes;
long started = System.nanoTime();
var buffer = device.createBuffer(label, usage, bytes);
MacBenchmarkIntegration.profileVboAllocation(bytes, started, System.nanoTime() - started, capacityFits);
return buffer;
}
}
@@ -0,0 +1,17 @@
package dev.horizonlab.dispatch.fixture;
import dev.horizonlab.metrics.MacBenchmarkIntegration;
import net.minecraft.client.Minecraft;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
/** Private diagnostic intervals on the same boundary as the frame recorder. */
@Mixin(Minecraft.class)
public abstract class MacFrameIntervalMixin {
@Inject(method = "runTick", at = @At("HEAD"))
private void horizon$frameInterval(boolean advanceGameTime, CallbackInfo callback) {
MacBenchmarkIntegration.profileFrameInterval();
}
}
@@ -0,0 +1,21 @@
package dev.horizonlab.dispatch.fixture;
import dev.horizonlab.metrics.MacBenchmarkIntegration;
import net.minecraft.client.Minecraft;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(Minecraft.class)
public abstract class MacGpuPassFrameMixin {
@Inject(method = "runTick", at = @At("HEAD"), require = 1)
private void horizon$beginPassFrame(boolean advanceGameTime, CallbackInfo callback) {
MacBenchmarkIntegration.profileFrameStart();
}
@Inject(method = "runTick", at = @At("RETURN"), require = 1)
private void horizon$endPassFrame(boolean advanceGameTime, CallbackInfo callback) {
MacBenchmarkIntegration.profileFrameEnd();
}
}
@@ -0,0 +1,22 @@
package dev.horizonlab.dispatch.fixture;
import dev.horizonlab.metrics.MacBenchmarkIntegration;
import net.minecraft.client.renderer.GameRenderer;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
/** Optional private asynchronous device timestamps; absent from timing-only runs. */
@Mixin(GameRenderer.class)
public abstract class MacGpuTimingMixin {
@Inject(method = "render", at = @At("HEAD"), require = 1)
private void horizon$beginGpuProfile(CallbackInfo callback) {
MacBenchmarkIntegration.profileRenderStart();
}
@Inject(method = "render", at = @At("RETURN"), require = 1)
private void horizon$endGpuProfile(CallbackInfo callback) {
MacBenchmarkIntegration.profileRenderEnd();
}
}
@@ -0,0 +1,26 @@
package dev.horizonlab.dispatch.fixture;
import com.mojang.renderpearl.api.commands.RenderPassDescriptor;
import com.mojang.renderpearl.backend.api.CommandEncoderBackend;
import com.mojang.renderpearl.backend.api.RenderPassBackend;
import com.mojang.renderpearl.backend.vulkan.VulkanCommandEncoder;
import dev.horizonlab.metrics.MacBenchmarkIntegration;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
/** Private pass-boundary queries around the original operations. */
@Mixin(VulkanCommandEncoder.class)
public abstract class MacVulkanPassTimingMixin {
@Inject(method = "createRenderPass", at = @At("HEAD"), require = 1)
private void horizon$beginPass(RenderPassDescriptor descriptor, CallbackInfoReturnable<RenderPassBackend> callback) {
MacBenchmarkIntegration.profilePassStart((CommandEncoderBackend) (Object) this, descriptor);
}
@Inject(method = "submitRenderPass", at = @At("RETURN"), require = 1)
private void horizon$endPass(CallbackInfo callback) {
MacBenchmarkIntegration.profilePassEnd((CommandEncoderBackend) (Object) this);
}
}
@@ -0,0 +1,702 @@
package dev.horizonlab.metrics;
import com.mojang.renderpearl.api.buffers.GpuBuffer;
import com.mojang.renderpearl.api.buffers.GpuBufferSlice;
import com.mojang.renderpearl.api.commands.RenderPass;
import com.mojang.renderpearl.api.pipeline.IndexType;
import com.mojang.renderpearl.api.pipeline.RenderPipeline;
import com.seibel.distanthorizons.common.render.blaze.BlazeDhMetaRenderer;
import com.seibel.distanthorizons.common.render.blaze.objects.BlazeGenericObjectVertexContainer;
import com.seibel.distanthorizons.common.render.blaze.wrappers.uniform.BlazeUniformBufferWrapper;
import com.seibel.distanthorizons.common.wrappers.misc.LightMapWrapper;
import com.seibel.distanthorizons.core.render.RenderParams;
import com.seibel.distanthorizons.core.render.renderer.RenderableBoxGroup;
import java.nio.ByteBuffer;
import java.util.Optional;
import java.util.OptionalDouble;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.renderpearl.api.commands.GpuQueryPool;
import com.mojang.renderpearl.api.commands.RenderPassDescriptor;
import com.mojang.renderpearl.backend.api.CommandEncoderBackend;
import java.nio.file.Files;
import java.nio.file.Path;
import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.client.Screenshot;
import net.minecraft.client.gui.screens.TitleScreen;
import net.minecraft.network.chat.contents.TranslatableContents;
import net.minecraft.server.MinecraftServer;
import net.minecraft.world.level.levelgen.WorldOptions;
import net.minecraft.world.level.levelgen.presets.WorldPresets;
import net.minecraft.world.level.GameType;
/** Private physical-Mac measurement fixture; never included in playable packs. */
public final class MacBenchmarkIntegration implements ClientModInitializer {
private boolean opening, finished, readbackRequested;
private long ready, started;
private int warmupSeconds, seconds, postRecordSeconds;
private float yaw, pitch;
private String label;
private boolean gpuProfile;
private boolean jfrProfile;
private boolean vboProfile;
private boolean renderClassPrewarm;
private static final String[] PREWARM_RENDER_CLASSES = {
"net.minecraft.client.renderer.extract.TransientBlock",
"net.minecraft.client.renderer.block.MovingBlockRenderState",
"net.caffeinemc.mods.sodium.client.render.frapi.render.NonTerrainBlockRenderContext",
"net.caffeinemc.mods.sodium.client.model.light.data.SingleBlockLightDataCache",
"it.unimi.dsi.fastutil.longs.Long2IntLinkedOpenHashMap"
};
private static VboTiming vboTiming;
private jdk.jfr.Recording flightRecording;
private static long flightDeadline, previousFlightFrame;
private static FrameIntervalEvent flightFrame;
private boolean passProfile;
private boolean sceneRequested;
private String scene;
public static boolean cloudBatchEnabled;
public static long cloudValidationClock;
private boolean cloudImageCheck, imagePending, productionCloudBatch;
private long imageDrawsStart, imagePassesStart;
private int imageStage;
private long imageReady;
private static GpuTiming gpuTiming;
private static PassTiming passTiming;
public static void profileVboAllocation(long bytes, long start, long duration, boolean capacityFits) {
if (vboTiming != null) vboTiming.add(bytes, start, duration, capacityFits);
}
public static void profileVboUpload(long bytes, long start, long duration, boolean copy) {
if (vboTiming != null && vboTiming.started != 0 && start >= vboTiming.started && start < vboTiming.deadline) {
(copy ? vboTiming.copy : vboTiming.upload).add(bytes, start - vboTiming.started, duration);
}
}
@jdk.jfr.Name("dev.horizonlab.TerrainUpload")
@jdk.jfr.Label("Private DH terrain upload")
@jdk.jfr.StackTrace(false)
public static final class TerrainUploadEvent extends jdk.jfr.Event {
public long bytes;
}
public static TerrainUploadEvent beginTerrainUpload(long bytes) {
if (flightDeadline == 0 || System.nanoTime() >= flightDeadline) return null;
var event = new TerrainUploadEvent();
event.bytes = bytes;
event.begin();
return event;
}
private static final class UploadTiming {
private final long[] durations = new long[300_000];
private final com.google.gson.JsonArray slow = new com.google.gson.JsonArray();
private int count, dropped, slowDropped;
private long bytes;
void add(long size, long offset, long duration) {
bytes += size;
if (count == durations.length) { dropped++; return; }
durations[count++] = duration;
if (duration < 1_000_000) return;
if (slow.size() >= 128) { slowDropped++; return; }
var event = new JsonObject();
event.addProperty("recording_offset_ns", offset);
event.addProperty("duration_ns", duration);
event.addProperty("bytes", size);
slow.add(event);
}
JsonObject summary() {
var result = new JsonObject();
result.addProperty("bytes", bytes);
result.addProperty("dropped_samples", dropped);
result.addProperty("dropped_slow_events", slowDropped);
result.add("duration", new com.google.gson.Gson().toJsonTree(
SampleStats.of(java.util.Arrays.copyOf(durations, count), count)));
result.add("slow_events", slow);
return result;
}
}
private static final class VboTiming {
private final UploadTiming upload = new UploadTiming(), copy = new UploadTiming();
private final long[] durations = new long[300_000];
private final com.google.gson.JsonArray slow = new com.google.gson.JsonArray();
private long started, deadline, totalCalls, bytesAllocated, capacityReuseCandidates;
private int count, dropped, slowDropped;
void add(long bytes, long start, long duration, boolean capacityFits) {
totalCalls++;
if (started == 0 || start < started || start >= deadline) return;
bytesAllocated += bytes;
if (capacityFits) capacityReuseCandidates++;
if (count == durations.length) { dropped++; return; }
durations[count++] = duration;
if (duration < 5_000_000) return;
if (slow.size() >= 128) { slowDropped++; return; }
var event = new JsonObject();
event.addProperty("recording_offset_ns", start - started);
event.addProperty("duration_ns", duration);
event.addProperty("bytes", bytes);
event.addProperty("existing_capacity_fits", capacityFits);
slow.add(event);
}
JsonObject summary() {
var result = new JsonObject();
result.addProperty("instrumented_calls_total", totalCalls);
result.addProperty("bytes_allocated_during_recording", bytesAllocated);
result.addProperty("capacity_reuse_candidates", capacityReuseCandidates);
result.addProperty("dropped_samples", dropped);
result.addProperty("dropped_slow_events", slowDropped);
result.add("allocation_duration", new com.google.gson.Gson().toJsonTree(
SampleStats.of(java.util.Arrays.copyOf(durations, count), count)));
result.add("slow_allocations", slow);
result.add("whole_upload", upload.summary());
result.add("staging_copy_and_barrier", copy.summary());
result.addProperty("note", "Original DH destination allocation, complete upload, and staging/copy/barrier timings. Nested durations are not additive. Profiling is separate from ordinary FPS measurements.");
return result;
}
}
@jdk.jfr.Name("dev.horizonlab.FrameInterval")
@jdk.jfr.Label("Private Minecraft frame interval")
@jdk.jfr.StackTrace(false)
public static final class FrameIntervalEvent extends jdk.jfr.Event {
@jdk.jfr.Timespan(jdk.jfr.Timespan.NANOSECONDS)
public long intervalNs;
}
public static void profileFrameInterval() {
long now = System.nanoTime();
if (flightDeadline == 0 || now >= flightDeadline) { flightFrame = null; return; }
if (flightFrame != null) {
flightFrame.end();
flightFrame.intervalNs = now - previousFlightFrame;
flightFrame.commit();
}
flightFrame = new FrameIntervalEvent();
flightFrame.begin();
previousFlightFrame = now;
}
public static void profileFrameStart() { if (passTiming != null) passTiming.beginFrame(); }
public static void profileFrameEnd() { if (passTiming != null) passTiming.endFrame(); }
public static void profilePassStart(CommandEncoderBackend encoder, RenderPassDescriptor descriptor) {
if (passTiming != null) passTiming.begin(encoder, descriptor.label().get());
}
public static void profilePassEnd(CommandEncoderBackend encoder) {
if (passTiming != null) passTiming.end(encoder);
}
private static final class PassTiming implements AutoCloseable {
private final GpuQueryPool pool = RenderSystem.getDevice().createTimestampQueryPool(512);
private final String[] pending = new String[256];
private final int[] pendingFrames = new int[256];
private final java.util.Map<String, java.util.ArrayList<Long>> durations = new java.util.LinkedHashMap<>();
private final java.util.Map<String, java.util.Map<Integer, Long>> frameDurations = new java.util.LinkedHashMap<>();
private final java.util.Map<String, Long> passCounts = new java.util.LinkedHashMap<>();
private final long deadlineNs;
private final double period = RenderSystem.getDevice().getDeviceInfo().timestampPeriod();
private int frame, active = -1, skipped, invalid;
private boolean sampling;
private String activeLabel;
PassTiming(long deadlineNs) { this.deadlineNs = deadlineNs; }
private void collect() {
for (int slot = 0; slot < pending.length; slot++) {
String label = pending[slot];
if (label == null) continue;
var values = pool.getValues(slot * 2, 2);
if (values[0].isEmpty() || values[1].isEmpty()) continue;
pending[slot] = null;
long duration = (long) ((values[1].getAsLong() - values[0].getAsLong()) * period);
if (duration <= 0) { invalid++; continue; }
if (!durations.containsKey(label) && durations.size() >= 64) { skipped++; continue; }
var samples = durations.computeIfAbsent(label, key -> new java.util.ArrayList<>());
if (samples.size() < 4096) samples.add(duration);
var frames = frameDurations.computeIfAbsent(label, key -> new java.util.LinkedHashMap<>());
int sampleFrame = pendingFrames[slot];
if (frames.containsKey(sampleFrame) || frames.size() < 2048) {
frames.merge(sampleFrame, duration, Long::sum);
passCounts.merge(label, 1L, Long::sum);
} else skipped++;
}
}
void beginFrame() {
collect();
sampling = System.nanoTime() < deadlineNs && frame++ % 20 == 0;
}
void endFrame() {
sampling = false;
if (active >= 0) throw new AssertionError("Unclosed private GPU pass timing");
}
void begin(CommandEncoderBackend encoder, String label) {
if (!sampling) return;
if (active >= 0) throw new AssertionError("Overlapping private GPU pass timing");
for (int slot = 0; slot < pending.length; slot++) {
if (pending[slot] != null) continue;
encoder.writeTimestamp(pool, slot * 2);
activeLabel = label;
pendingFrames[slot] = frame;
active = slot;
return;
}
skipped++;
}
void end(CommandEncoderBackend encoder) {
if (active < 0) return;
int slot = active;
active = -1;
encoder.writeTimestamp(pool, slot * 2 + 1);
pending[slot] = activeLabel;
}
JsonObject summary() {
collect();
var result = new JsonObject();
var passes = new JsonObject();
var gson = new com.google.gson.Gson();
durations.forEach((label, values) -> {
long[] samples = values.stream().mapToLong(Long::longValue).toArray();
passes.add(label, gson.toJsonTree(SampleStats.of(samples, samples.length)));
});
result.add("passes", passes);
var totals = new JsonObject();
frameDurations.forEach((label, frames) -> {
long[] samples = frames.values().stream().mapToLong(Long::longValue).toArray();
var total = gson.toJsonTree(SampleStats.of(samples, samples.length)).getAsJsonObject();
total.addProperty("pass_invocations", passCounts.get(label));
totals.add(label, total);
});
result.add("per_sampled_frame", totals);
result.addProperty("sampling_frame_stride", 20);
result.addProperty("skipped_queries", skipped);
result.addProperty("invalid_queries", invalid);
return result;
}
@Override public void close() { pool.close(); }
}
/** Called only by the optional private GPU timing mixin. */
public static void profileRenderStart() {
if (gpuTiming != null) gpuTiming.begin();
}
public static void profileRenderEnd() {
if (gpuTiming != null) gpuTiming.end();
}
private static final class GpuTiming implements AutoCloseable {
private final GpuQueryPool pool = RenderSystem.getDevice().createTimestampQueryPool(16);
private final boolean[] pending = new boolean[8];
private final long[] gpuSamples = new long[120_000];
private final long[] cpuSamples = new long[120_000];
private final float period = RenderSystem.getDevice().getDeviceInfo().timestampPeriod();
private final long deadlineNs;
private int active = -1, gpuCount, cpuCount, skipped, invalid;
private long cpuStart;
private boolean recordingFrame;
GpuTiming(long deadlineNs) { this.deadlineNs = deadlineNs; }
private void collect() {
for (int slot = 0; slot < pending.length; slot++) {
if (!pending[slot]) continue;
var values = pool.getValues(slot * 2, 2);
if (values[0].isEmpty() || values[1].isEmpty()) continue;
pending[slot] = false;
long duration = (long) ((values[1].getAsLong() - values[0].getAsLong()) * (double) period);
if (duration > 0 && gpuCount < gpuSamples.length) gpuSamples[gpuCount++] = duration;
else invalid++;
}
}
void begin() {
recordingFrame = System.nanoTime() < deadlineNs;
if (!recordingFrame) return;
collect();
cpuStart = System.nanoTime();
active = -1;
for (int slot = 0; slot < pending.length; slot++) {
if (pending[slot]) continue;
active = slot;
RenderSystem.getDevice().createCommandEncoder().writeTimestamp(pool, slot * 2);
return;
}
skipped++;
}
void end() {
if (!recordingFrame) return;
long duration = System.nanoTime() - cpuStart;
if (duration > 0 && cpuCount < cpuSamples.length) cpuSamples[cpuCount++] = duration;
if (active < 0) return;
RenderSystem.getDevice().createCommandEncoder().writeTimestamp(pool, active * 2 + 1);
pending[active] = true;
active = -1;
}
JsonObject summary() {
collect();
var gson = new com.google.gson.Gson();
var result = new JsonObject();
result.addProperty("status", gpuCount > 0 ? "available" : "unavailable");
result.add("gpu_render_duration", gson.toJsonTree(SampleStats.of(gpuSamples, gpuCount)));
result.add("cpu_render_call_duration", gson.toJsonTree(SampleStats.of(cpuSamples, cpuCount)));
result.addProperty("skipped_queries", skipped);
result.addProperty("invalid_or_overflowed_queries", invalid);
int pendingCount = 0;
for (boolean value : pending) if (value) pendingCount++;
result.addProperty("pending_queries_at_end", pendingCount);
result.addProperty("timestamp_period_ns", period);
return result;
}
@Override public void close() { pool.close(); }
}
@Override public void onInitializeClient() {
try {
var config = new java.util.Properties();
try (var reader = Files.newBufferedReader(FabricLoader.getInstance().getConfigDir()
.resolve("mac-benchmark.properties"))) { config.load(reader); }
warmupSeconds = Integer.parseInt(config.getProperty("warmupSeconds", "60"));
seconds = Integer.parseInt(config.getProperty("seconds", "120"));
yaw = Float.parseFloat(config.getProperty("yaw", "141.5"));
pitch = Float.parseFloat(config.getProperty("pitch", "16.95"));
label = config.getProperty("label", "mac-stationary");
productionCloudBatch = Boolean.parseBoolean(config.getProperty("productionCloudBatch", "false"));
cloudImageCheck = Boolean.parseBoolean(config.getProperty("cloudImageCheck", "false"));
cloudBatchEnabled = !cloudImageCheck && Boolean.parseBoolean(config.getProperty("cloudBatch", "false"));
gpuProfile = Boolean.parseBoolean(config.getProperty("gpuProfile", "false"));
jfrProfile = Boolean.parseBoolean(config.getProperty("jfrProfile", "false"));
vboProfile = Boolean.parseBoolean(config.getProperty("vboProfile", "false"));
if (vboProfile) vboTiming = new VboTiming();
renderClassPrewarm = Boolean.parseBoolean(config.getProperty("renderClassPrewarm", "false"));
postRecordSeconds = Integer.parseInt(config.getProperty("postRecordSeconds", "3"));
if (renderClassPrewarm) {
for (String type : PREWARM_RENDER_CLASSES) {
Class.forName(type, false, MacBenchmarkIntegration.class.getClassLoader());
}
System.out.println("HORIZON_MAC_RENDER_CLASSES_PREWARMED " + PREWARM_RENDER_CLASSES.length);
}
passProfile = Boolean.parseBoolean(config.getProperty("passProfile", "false"));
scene = config.getProperty("scene", "spawn");
if (warmupSeconds < 0 || warmupSeconds > 300 || seconds < 10 || seconds > 120
|| !Float.isFinite(yaw) || !Float.isFinite(pitch)
|| !label.matches("[a-zA-Z0-9_-]{1,64}")
|| !(scene.equals("spawn") || scene.equals("overlook") || scene.equals("flyover"))) throw new IllegalArgumentException("Invalid fixture settings");
} catch (Exception error) { throw new IllegalStateException("Private Mac benchmark configuration failed", error); }
ClientTickEvents.END_CLIENT_TICK.register(client -> {
if (finished || client.gui.overlay() != null) return;
if (!opening && client.gui.screen() instanceof TitleScreen screen) {
opening = true;
if (Files.isRegularFile(client.gameDirectory.toPath().resolve("saves/Demo_World/level.dat"))) {
client.createWorldOpenFlows().openWorld("Demo_World", () -> {
throw new AssertionError("Private benchmark world did not open");
});
} else {
client.createWorldOpenFlows().createFreshLevel("Demo_World", MinecraftServer.DEMO_SETTINGS,
WorldOptions.DEMO_OPTIONS, WorldPresets::createNormalWorldDimensions, screen);
}
return;
}
if (client.gui.screen() != null && client.gui.screen().getTitle().getContents() instanceof TranslatableContents title
&& title.getKey().equals("demo.help.title")) { client.gui.setScreen(null); return; }
if (client.level == null || client.player == null || client.gui.screen() != null
|| client.isPaused() || !client.isWindowActive()) {
if (started == 0) ready = 0;
return;
}
if (!scene.equals("spawn")) {
if (!sceneRequested) {
sceneRequested = true;
var server = client.getSingleplayerServer();
var playerId = client.player.getUUID();
server.execute(() -> {
var player = server.getPlayerList().getPlayer(playerId);
player.setGameMode(GameType.SPECTATOR);
player.teleportTo(-41.5, 100.0, 72.5);
player.setDeltaMovement(0, 0, 0);
});
return;
}
if (!client.player.isSpectator()
|| (started == 0 && Math.abs(client.player.getY() - 100.0) > 0.01)) return;
if (scene.equals("overlook")) {
client.player.setPos(-41.5, 100.0, 72.5);
client.player.setDeltaMovement(0, 0, 0);
}
}
// Control only this private workload's camera; simulation runs normally.
client.player.setYRot(yaw);
client.player.setXRot(pitch);
if (scene.equals("overlook")) client.player.setOldPosAndRot();
long now = System.nanoTime();
if (scene.equals("flyover")) client.options.keyUp.setDown(
started != 0 && now - started < seconds * 1_000_000_000L);
if (ready == 0) {
ready = now;
System.out.println("HORIZON_MAC_BENCHMARK_READY");
}
if (jfrProfile && started == 0 && flightRecording == null
&& now - ready >= Math.max(0, warmupSeconds - 15L) * 1_000_000_000L) {
try {
flightRecording = new jdk.jfr.Recording(jdk.jfr.Configuration.getConfiguration("profile"));
flightRecording.setName("Private M2 Pro diagnostics");
flightRecording.setMaxSize(128L * 1024 * 1024);
flightRecording.enable(FrameIntervalEvent.class).withThreshold(java.time.Duration.ZERO);
if (vboProfile) flightRecording.enable(TerrainUploadEvent.class).withThreshold(java.time.Duration.ofMillis(1));
flightRecording.enable("jdk.ThreadPark").withThreshold(java.time.Duration.ofMillis(1));
flightRecording.enable("jdk.JavaMonitorEnter").withThreshold(java.time.Duration.ofMillis(1));
flightRecording.enable("jdk.ClassLoad").withThreshold(java.time.Duration.ofMillis(1));
flightRecording.start();
var frameEvent = new FrameIntervalEvent();
frameEvent.begin(); frameEvent.end();
if (vboProfile) {
// Initialize the private event machinery during warmup, before timing uploads.
var event = new TerrainUploadEvent();
event.begin(); event.end(); event.commit();
}
} catch (Exception error) { throw new AssertionError("Private JFR start failed", error); }
}
if (cloudImageCheck) {
if (now - ready >= warmupSeconds * 1_000_000_000L) checkCloudImages(client, now);
return;
}
if (started == 0 && now - ready >= warmupSeconds * 1_000_000_000L) {
try {
var method = Class.forName("dev.horizonlab.client.HorizonClient")
.getDeclaredMethod("start", String.class, int.class);
method.setAccessible(true);
if (!method.invoke(null, label, seconds).equals(1)) throw new AssertionError("Recording failed to start");
for (String[] target : new String[][]{{"dev.horizonlab.client.HorizonClient", "frames"},
{"dev.horizonlab.HorizonLab", "ticks"}}) {
var field = Class.forName(target[0]).getDeclaredField(target[1]);
field.setAccessible(true);
var recording = (Recording) field.get(null);
if (recording != null) synchronized (recording) {
recording.context.put("private_render_class_prewarm", renderClassPrewarm);
if (gpuProfile || passProfile || jfrProfile || vboProfile) {
recording.context.put("private_profiling_enabled", true);
}
}
}
started = now;
if (vboTiming != null) {
vboTiming.started = now;
vboTiming.deadline = now + seconds * 1_000_000_000L;
}
if (jfrProfile) flightDeadline = now + seconds * 1_000_000_000L;
if (gpuProfile) gpuTiming = new GpuTiming(now + seconds * 1_000_000_000L);
if (passProfile) passTiming = new PassTiming(now + seconds * 1_000_000_000L);
System.out.println("HORIZON_MAC_BENCHMARK_STARTED");
} catch (ReflectiveOperationException error) { throw new AssertionError("Private recorder start failed", error); }
}
if (started == 0 || now - started < (seconds + (long) postRecordSeconds) * 1_000_000_000L || readbackRequested) return;
Path output = client.gameDirectory.toPath().resolve("horizon-benchmarks");
try (var paths = Files.list(output)) {
var reports = paths.filter(p -> p.getFileName().toString().startsWith(label + "-")
&& p.toString().endsWith(".json")).toList();
if (reports.size() != 2) throw new AssertionError("Expected completed frame and tick reports");
var result = new JsonObject();
result.add("cloud_batch", MacCloudBatchState.summary());
result.addProperty("warmup_seconds", warmupSeconds);
result.addProperty("post_record_seconds", postRecordSeconds);
result.addProperty("scene", scene);
result.addProperty("render_class_prewarm", renderClassPrewarm);
if (renderClassPrewarm) result.add("prewarmed_render_classes",
new com.google.gson.Gson().toJsonTree(PREWARM_RENDER_CLASSES));
result.addProperty("camera_yaw", yaw);
result.addProperty("camera_pitch", pitch);
result.addProperty("player_x", client.player.getX());
result.addProperty("player_y", client.player.getY());
result.addProperty("player_z", client.player.getZ());
var device = RenderSystem.getDevice().getDeviceInfo();
result.addProperty("graphics_backend", device.backendName());
result.addProperty("gpu", device.name());
if (vboTiming != null) {
result.add("vbo_allocation_profile", vboTiming.summary());
vboTiming = null;
}
if (flightRecording != null) {
flightDeadline = 0;
flightFrame = null;
flightRecording.stop();
flightRecording.dump(client.gameDirectory.toPath().resolve("mac-benchmark.jfr"));
var jfr = new JsonObject();
jfr.addProperty("file", "mac-benchmark.jfr");
jfr.addProperty("frame_event", jdk.jfr.EventType.getEventType(FrameIntervalEvent.class).getName());
jfr.addProperty("max_recording_bytes", 128L * 1024 * 1024);
result.add("jfr_profile", jfr);
flightRecording.close();
flightRecording = null;
}
if (gpuTiming != null) {
result.add("gpu_profile", gpuTiming.summary());
gpuTiming.close();
gpuTiming = null;
}
if (passTiming != null) {
result.add("gpu_pass_profile", passTiming.summary());
passTiming.close();
passTiming = null;
}
for (Path path : reports) {
var report = JsonParser.parseString(Files.readString(path)).getAsJsonObject();
if (report.getAsJsonObject("stats").get("count").getAsInt() < 100)
throw new AssertionError("Too few active measurement samples");
result.add(report.get("kind").getAsString(), report);
}
readbackRequested = true;
Screenshot.takeScreenshot(client.gameRenderer.mainRenderTarget(), image -> {
try (image) {
image.writeToFile(client.gameDirectory.toPath().resolve("mac-benchmark-private.png"));
result.addProperty("framebuffer_width", image.getWidth());
result.addProperty("framebuffer_height", image.getHeight());
Files.writeString(client.gameDirectory.toPath().resolve("mac-benchmark-result.json"), result.toString());
System.out.println("HORIZON_MAC_BENCHMARK_RESULT " + result);
finished = true;
client.execute(client::stop);
} catch (Exception error) { throw new AssertionError("Private Mac readback failed", error); }
});
} catch (Exception error) { throw new AssertionError("Private Mac benchmark failed", error); }
});
}
private void setImageBatchEnabled(boolean enabled) {
if (!productionCloudBatch) { cloudBatchEnabled = enabled; return; }
try {
var flag = Class.forName("dev.horizonlab.mixin.HorizonMixinPlugin").getDeclaredField("cloudBatch");
flag.setAccessible(true);
flag.setBoolean(null, enabled);
} catch (ReflectiveOperationException error) { throw new AssertionError("Private production batch toggle failed", error); }
}
/** Frozen-scene A/B/A readbacks are separate from ordinary performance runs. */
private void checkCloudImages(net.minecraft.client.Minecraft client, long now) {
if (imageReady == 0) {
setImageBatchEnabled(false);
imageDrawsStart = productionCloudBatch ? dev.horizonlab.render.CloudBatchStatus.draws : MacCloudBatchState.totalDraws;
imagePassesStart = productionCloudBatch ? dev.horizonlab.render.CloudBatchStatus.passes : MacCloudBatchState.totalPasses;
cloudValidationClock = System.currentTimeMillis();
if (!client.gui.hud.isHidden()) client.gui.hud.toggle();
client.getSingleplayerServer().execute(() -> client.getSingleplayerServer().tickRateManager().setFrozen(true));
imageReady = now;
return;
}
if (!client.level.tickRateManager().isFrozen() || imagePending || now - imageReady < 5_000_000_000L) return;
String name = new String[]{"cloud-image-control-a.png", "cloud-image-batched.png", "cloud-image-control-b.png"}[imageStage];
imagePending = true;
Screenshot.takeScreenshot(client.gameRenderer.mainRenderTarget(), image -> {
try (image) {
image.writeToFile(client.gameDirectory.toPath().resolve(name));
imageStage++;
setImageBatchEnabled(imageStage == 1);
imageReady = System.nanoTime();
imagePending = false;
if (imageStage < 3) return;
var result = new JsonObject();
var device = RenderSystem.getDevice().getDeviceInfo();
result.addProperty("image_check_only", true);
result.addProperty("graphics_backend", device.backendName());
result.addProperty("gpu", device.name());
result.addProperty("frozen_scene", client.level.tickRateManager().isFrozen());
result.addProperty("framebuffer_width", image.getWidth());
result.addProperty("framebuffer_height", image.getHeight());
result.addProperty("production_cloud_batch", productionCloudBatch);
var batch = new JsonObject();
batch.addProperty("draws", (productionCloudBatch ? dev.horizonlab.render.CloudBatchStatus.draws : MacCloudBatchState.totalDraws) - imageDrawsStart);
batch.addProperty("passes", (productionCloudBatch ? dev.horizonlab.render.CloudBatchStatus.passes : MacCloudBatchState.totalPasses) - imagePassesStart);
result.add("cloud_batch", batch);
if (productionCloudBatch) setImageBatchEnabled(true);
Files.writeString(client.gameDirectory.toPath().resolve("mac-benchmark-result.json"), result.toString());
finished = true;
client.execute(client::stop);
} catch (Exception error) { throw new AssertionError("Private cloud image validation failed", error); }
});
}
/** Render-thread-only state, scoped to one original generic-render invocation. */
public static final class MacCloudBatchState implements AutoCloseable {
public static MacCloudBatchState current;
public final BlazeUniformBufferWrapper uniformWrapper;
private GpuBufferSlice uniform;
private RenderPass pass;
private Object lightmap;
private static long totalDraws, totalPasses;
public MacCloudBatchState(BlazeUniformBufferWrapper wrapper) {
uniformWrapper = wrapper;
}
public void captureUniform(ByteBuffer data) {
var copy = data.duplicate();
copy.position(0);
var encoder = RenderSystem.getDevice().createCommandEncoder();
try (var mapped = encoder.transientMemory().allocateGpuMapped(copy.remaining(), 256, GpuBuffer.USAGE_UNIFORM)) {
mapped.data().put(copy);
uniform = mapped.slice();
}
}
public void draw(RenderParams params, RenderableBoxGroup group, RenderPipeline pipeline) {
if (uniform == null) throw new AssertionError("Missing private DH uniform snapshot");
var vertices = (BlazeGenericObjectVertexContainer) group.vertexBufferContainer;
boolean cloud = group.getResourceLocationNamespace().equals("DistantHorizons")
&& group.getResourceLocationPath().equals("Clouds");
if (!cloud || lightmap != params.lightmap) closePass();
if (pass == null) {
var targets = BlazeDhMetaRenderer.INSTANCE;
pass = RenderSystem.getDevice().createCommandEncoder().createRenderPass(
() -> "horizon-private:DH cloud batch", targets.dhColorTextureWrapper.getTextureView(),
Optional.empty(), targets.dhDepthTextureWrapper.getTextureView(), OptionalDouble.empty());
pass.setPipeline(RenderSystem.getCompiledPipeline(pipeline));
var light = ((LightMapWrapper) params.lightmap).getTextureViewWrapper();
pass.setUniform("uLightMap", light.getTextureView(), light.getTextureSampler());
lightmap = params.lightmap;
totalPasses++;
}
pass.setUniform("vertUniformBlock", uniform);
pass.setIndexBuffer(vertices.indexGpuBuffer, IndexType.INT);
pass.setVertexBuffer(0, vertices.vboGpuBuffer.slice());
if (vertices.uploadedBoxCount > 0) {
pass.drawIndexed(Math.multiplyExact(vertices.uploadedBoxCount, 36), 1, 0, 0, 0);
totalDraws++;
}
uniform = null;
if (!cloud) closePass();
}
public void closePass() {
var closing = pass;
pass = null;
lightmap = null;
if (closing != null) closing.close();
}
public static JsonObject summary() {
var result = new JsonObject();
result.addProperty("requested", cloudBatchEnabled);
result.addProperty("draws", totalDraws);
result.addProperty("passes", totalPasses);
return result;
}
@Override public void close() {
closePass();
}
}
}
@@ -0,0 +1,25 @@
package dev.horizonlab.metrics;
import java.util.LinkedHashMap;
import java.util.Map;
public final class VisualSettingsChangesTest {
public static void main(String[] args) {
var visual = Map.<String, Object>of("render_distance", 32, "dh_runtime_ratio", 0.5,
"dh_cloud_batch_enabled", true, "iris_capture_status", "not_installed");
var snapshot = new LinkedHashMap<String, Object>(visual);
if (!VisualSettingsChanges.between(snapshot, visual).isEmpty())
throw new AssertionError("Identical visual settings were treated as changed");
var end = new LinkedHashMap<String, Object>(visual);
end.put("dh_runtime_ratio", 0.25);
if (!VisualSettingsChanges.between(snapshot, end).isEmpty())
throw new AssertionError("Expected adaptive duty-cycle change was treated as visual quality");
end.put("dh_cloud_batch_enabled", false);
end.put("render_distance", 12);
end.remove("iris_capture_status");
var changed = VisualSettingsChanges.between(snapshot, end);
if (!changed.containsAll(java.util.List.of("dh_cloud_batch_enabled", "render_distance", "iris_capture_status"))
|| changed.size() != 3) throw new AssertionError("Actual visual changes were missed: " + changed);
System.out.println("Visual snapshot equality, adaptive duty cycle and actual changes checked.");
}
}
+1 -1
View File
@@ -3,7 +3,7 @@
"minecraft": "26.3",
"java_major": 25,
"fabric_loader": "0.19.5",
"pack_version": "0.21.0",
"pack_version": "0.22.0",
"options_data_version": 5023,
"projects": {
"sodium": {
+63 -1
View File
@@ -5,7 +5,37 @@ import jdk.jfr.Recording;
/** Produces real JDK counter/GC events for the reader's independent integration check. */
public final class JfrFixture {
private static volatile Object[] retained;
@jdk.jfr.Name("dev.horizonlab.FrameInterval")
@jdk.jfr.StackTrace(false)
public static final class FrameInterval extends jdk.jfr.Event {
public long intervalNs;
}
@jdk.jfr.Name("dev.horizonlab.TerrainUpload")
@jdk.jfr.StackTrace(false)
public static final class TerrainUpload extends jdk.jfr.Event {}
public static final class LoadPayload {}
static void checkOverlapCoverage() {
var random = new java.util.Random(0x4d3250524fL);
for (int test = 0; test < 10_000; test++) {
var spans = new java.util.ArrayList<JfrSummary.Span>();
var occupied = new boolean[128];
for (int i = random.nextInt(16); i > 0; i--) {
int start = random.nextInt(128), end = start + random.nextInt(129 - start);
spans.add(new JfrSummary.Span(start, end));
java.util.Arrays.fill(occupied, start, end, true);
}
long expected = 0;
for (boolean present : occupied) if (present) expected++;
if (JfrSummary.coveredNs(spans) != expected)
throw new AssertionError("Overlapping event intervals were double-counted");
java.util.Collections.shuffle(spans, random);
if (JfrSummary.coveredNs(spans) != expected)
throw new AssertionError("Coverage depends on event order");
}
}
public static void main(String[] args) throws Exception {
checkOverlapCoverage();
Thread.currentThread().setName("Horizon JFR fixture \"quoted\"\tthread");
try (var recording = new Recording()) {
recording.enable("jdk.ThreadAllocationStatistics").withPeriod(Duration.ofMillis(100));
@@ -14,6 +44,9 @@ public final class JfrFixture {
recording.enable("jdk.GarbageCollection");
recording.enable("jdk.GCPhasePause");
recording.enable("jdk.GCHeapSummary");
recording.enable(FrameInterval.class).withThreshold(Duration.ZERO);
recording.enable(TerrainUpload.class).withThreshold(Duration.ZERO);
recording.enable("jdk.ClassLoad").withThreshold(Duration.ZERO);
recording.start();
for (int batch = 0; batch < 12; batch++) {
Object[] objects = new Object[100_000];
@@ -21,7 +54,36 @@ public final class JfrFixture {
retained = objects;
Thread.sleep(120);
}
System.gc();
var renderer = new Thread(() -> {
var frame = new FrameInterval();
long started = System.nanoTime();
frame.begin();
System.gc();
try (var input = JfrFixture.class.getResourceAsStream("JfrFixture$LoadPayload.class")) {
byte[] bytes = input.readAllBytes();
var slowLoader = new ClassLoader(null) {
@Override protected Class<?> findClass(String name) throws ClassNotFoundException {
if (!name.equals("JfrFixture$LoadPayload")) throw new ClassNotFoundException(name);
java.util.concurrent.locks.LockSupport.parkNanos(10_000_000);
return defineClass(name, bytes, 0, bytes.length);
}
};
Class.forName("JfrFixture$LoadPayload", false, slowLoader);
} catch (Exception failure) { throw new AssertionError(failure); }
var outer = new TerrainUpload();
outer.begin();
var inner = new TerrainUpload();
inner.begin();
java.util.concurrent.locks.LockSupport.parkNanos(20_000_000);
inner.end(); inner.commit();
java.util.concurrent.locks.LockSupport.parkNanos(20_000_000);
outer.end(); outer.commit();
frame.end();
frame.intervalNs = System.nanoTime() - started;
frame.commit();
}, "Render thread");
renderer.start();
renderer.join();
Thread.sleep(250);
recording.stop();
recording.dump(Path.of(args[0]));
+60
View File
@@ -0,0 +1,60 @@
import copy
import csv
from pathlib import Path
import tempfile
import unittest
from tools.mac_benchmark import validate_capture
class MacCaptureTests(unittest.TestCase):
def setUp(self):
self.directory = tempfile.TemporaryDirectory()
self.addCleanup(self.directory.cleanup)
self.instance = Path(self.directory.name)
output = self.instance / "horizon-benchmarks"
output.mkdir()
self.result = {"gpu": "Apple M2 Pro", "graphics_backend": "Vulkan"}
for kind, duration in [("frames", 120_000_000), ("ticks", 5_000_000)]:
filename = kind + ".csv"
with (output / filename).open("w") as stream:
writer = csv.writer(stream)
writer.writerow(["sample", "duration_ns"])
writer.writerows((index, duration) for index in range(1000))
self.result[kind] = {"kind": kind, "gpu": "Apple M2 Pro", "graphics_backend": "Vulkan",
"elapsed_seconds": 120, "dropped_samples": 0, "excluded_frames": 0,
"samples_file": filename, "stats": {"count": 1000, "meanMs": duration / 1e6}}
self.result["frames"].update(visual_settings_changed=[], render_distance=32, simulation_distance=12,
inactivity_fps_limit="minimized", frame_limiter_throttled_observations=0)
self.result["ticks"].update(tick_state_changed=[])
for field in ["frozen", "stepping", "sprinting", "suppressed", "nonstandard_rate"]:
self.result["ticks"]["tick_state_" + field + "_samples"] = 0
def test_complete_active_capture_is_accepted(self):
validate_capture(self.result, self.instance, 32, 120)
def test_idle_focused_capture_and_missing_limiter_measurements_are_rejected(self):
for value in [None, 1, 3600]:
report = copy.deepcopy(self.result)
report["frames"]["frame_limiter_throttled_observations"] = value
with self.subTest(value=value), self.assertRaises(AssertionError):
validate_capture(report, self.instance, 32, 120)
def test_incomplete_files_changed_graphics_and_nonstandard_simulation_are_rejected(self):
changes = [("frames", "visual_settings_changed", ["render_distance"]),
("frames", "excluded_frames", 1), ("frames", "samples_file", "../private.csv"),
("frames", "graphics_backend", "OpenGL"), ("ticks", "tick_state_frozen_samples", 1),
("ticks", "dropped_samples", 1), ("ticks", "elapsed_seconds", 60)]
for kind, key, value in changes:
report = copy.deepcopy(self.result)
report[kind][key] = value
with self.subTest(kind=kind, key=key), self.assertRaises(AssertionError):
validate_capture(report, self.instance, 32, 120)
report = copy.deepcopy(self.result)
report["frames"]["stats"]["count"] = 999
with self.assertRaises(AssertionError):
validate_capture(report, self.instance, 32, 120)
if __name__ == "__main__":
unittest.main()
+55 -1
View File
@@ -62,6 +62,30 @@ class PackTests(unittest.TestCase):
self.assertNotIn("distanthorizons", candidate["mods"])
self.assertNotIn("iris", candidate["mods"])
def test_m2_metal_retains_supplied_detail_and_balanced_terrain_quality(self):
files = build_pack.overrides("m2-metal", b"fixture")
balanced = build_pack.overrides("m2-balanced", b"fixture")
options = dict(line.split(":", 1) for line in files["options.txt"].decode().splitlines())
self.assertEqual(options["renderDistance"], "32")
self.assertEqual(options["simulationDistance"], "12")
self.assertEqual(float(options["fov"]), 1.0)
self.assertEqual(json.loads(options["renderClouds"]), "false")
self.assertEqual(json.loads(options["preferredGraphicsBackend"]), "vulkan")
self.assertEqual(files["config/DistantHorizons.toml"], balanced["config/DistantHorizons.toml"])
for name in balanced:
if name.startswith("config/horizon-") and name != "config/horizon-profile.txt":
self.assertEqual(files[name], balanced[name])
def test_cloud_batch_is_opt_in_and_keeps_metal_visual_settings(self):
metal = build_pack.overrides("m2-metal", b"fixture")
batch = build_pack.overrides("m2-metal-batched", b"fixture")
self.assertEqual(batch["options.txt"], metal["options.txt"])
self.assertEqual(batch["config/DistantHorizons.toml"], metal["config/DistantHorizons.toml"])
self.assertEqual(batch["config/horizon-cloud-batch.properties"], b"enabled=true\n")
for profile in build_pack.PROFILES:
if profile != "m2-metal-batched":
self.assertEqual(build_pack.overrides(profile, b"fixture")["config/horizon-cloud-batch.properties"], b"enabled=false\n")
def test_missing_exact_iris_sodium_dependency_is_rejected(self):
with self.assertRaisesRegex(ValueError, "Missing pinned dependency"):
build_pack.validate_selection(build_pack.read_lock(), ["iris"])
@@ -232,13 +256,17 @@ class PackTests(unittest.TestCase):
with mock.patch.object(runtime, "prepare_client", return_value=(manifest, [root / "game.jar"], root / "natives")), \
mock.patch.object(runtime.platform, "system", return_value="Darwin"), \
mock.patch.object(runtime.subprocess, "run") as run, \
mock.patch("sys.argv", ["runtime.py", "demo", "--instance", str(root / "instance")]):
mock.patch("sys.argv", ["runtime.py", "demo", "--instance", str(root / "instance"),
"--width", "1728", "--height", "1117", "--fullscreen"]):
runtime.main()
command = run.call_args.args[0]
self.assertEqual(command.count("-XstartOnFirstThread"), 1)
self.assertLess(command.index("-XstartOnFirstThread"),
command.index("net.fabricmc.loader.impl.launch.knot.KnotClient"))
self.assertEqual(command.count("-cp"), 1)
self.assertEqual(command[command.index("--width") + 1], "1728")
self.assertEqual(command[command.index("--height") + 1], "1117")
self.assertEqual(command.count("--fullscreen"), 1)
self.assertTrue(run.call_args.kwargs["check"])
def test_native_architecture_is_selected_even_with_os_only_rules(self):
@@ -251,6 +279,32 @@ class PackTests(unittest.TestCase):
class ComparisonTests(unittest.TestCase):
def test_private_profiling_is_visible_in_frame_and_tick_comparisons(self):
for kind in ["frames", "ticks"]:
baseline = self.report(kind)
candidate = {**baseline, "private_profiling_enabled": True}
warnings = compare_benchmarks.compare(baseline, candidate)["warnings"]
self.assertTrue(any("candidate includes private profiling overhead" in item for item in warnings))
def test_afk_cap_is_explicit_even_with_unlimited_configured_fps(self):
baseline = self.report()
baseline.update(fps_limit=260, fps_limit_unlimited=True, inactivity_fps_limit="afk",
frame_limiter_throttled_observations=3600, frame_limiter_afk_observations=3600)
candidate = {**baseline, "inactivity_fps_limit": "minimized",
"frame_limiter_throttled_observations": 0, "frame_limiter_afk_observations": 0}
result = compare_benchmarks.compare(baseline, candidate)
self.assertTrue(any("inactivity_fps_limit" in item for item in result["mismatches"]))
self.assertTrue(any("3600 AFK" in item for item in result["warnings"]))
self.assertFalse(any("candidate includes" in item for item in result["warnings"]))
legacy = self.report()
self.assertTrue(any("AFK throttling is unknown" in item for item in compare_benchmarks.compare(legacy, legacy)["warnings"]))
def test_cloud_batch_experiment_is_visible_in_comparison(self):
baseline = {**self.report(), "dh_cloud_batch_requested": False, "dh_cloud_batch_enabled": False}
candidate = {**baseline, "dh_cloud_batch_requested": True, "dh_cloud_batch_enabled": True}
result = compare_benchmarks.compare(baseline, candidate)
self.assertTrue(any("dh_cloud_batch_enabled" in warning for warning in result["warnings"]))
def report(self, kind="frames"):
return {"schema": 1, "kind": kind, "elapsed_seconds": 120, "framebuffer_width": 1920,
"stats": {"count": 1000, "meanRate": 100, "lowOnePercentRate": 50,
+6 -2
View File
@@ -30,7 +30,7 @@
</main>
<script>
'use strict';
const SETTINGS=['minecraft','kind','os','os_version','architecture','gpu','graphics_backend','graphics_driver','server_runtime_kind','client_graphics_capture_status','java_version','heap_max_bytes','framebuffer_width','framebuffer_height','render_distance','simulation_distance','vsync','fps_limit','graphics_preset','ambient_occlusion','improved_transparency','mipmap_levels','entity_distance_scaling','entity_shadows','particles','clouds','cloud_range','fov','world_difficulty','dh_capture_status','dh_rendering_enabled','dh_renderer_mode','dh_lod_radius_chunks','dh_horizontal_quality','dh_vertical_quality','dh_max_horizontal_resolution','dh_transparency','dh_lod_shading','dh_lod_only_mode','dh_camera_quality_dropoff','dh_ambient_occlusion','iris_capture_status','iris_shaders_enabled','iris_shader_pack'];
const SETTINGS=['minecraft','kind','os','os_version','architecture','gpu','graphics_backend','graphics_driver','server_runtime_kind','client_graphics_capture_status','java_version','heap_max_bytes','framebuffer_width','framebuffer_height','render_distance','simulation_distance','vsync','fps_limit','inactivity_fps_limit','graphics_preset','ambient_occlusion','improved_transparency','mipmap_levels','entity_distance_scaling','entity_shadows','particles','clouds','cloud_range','fov','world_difficulty','dh_capture_status','dh_rendering_enabled','dh_renderer_mode','dh_lod_radius_chunks','dh_horizontal_quality','dh_vertical_quality','dh_max_horizontal_resolution','dh_transparency','dh_lod_shading','dh_lod_only_mode','dh_camera_quality_dropoff','dh_ambient_occlusion','iris_capture_status','iris_shaders_enabled','iris_shader_pack'];
const state={baseline:null,candidate:null},generation={baseline:0,candidate:0};
let comparison=null,chartData=null;
const $=id=>document.getElementById(id);
@@ -117,6 +117,7 @@ function compareReports(a,b){
if(a.kind==='ticks'&&a.peak_players!==b.peak_players)differences.push({setting:'peak_players',baseline:a.peak_players??null,candidate:b.peak_players??null});
const warnings=['World, camera route, shaders and terrain-cache warm-up still need to match. These are not verified by the captured settings.'];
warnings.push(...softwareWarnings(a,b));
for(const [name,report]of [['Baseline',a],['Candidate',b]])if(report.private_profiling_enabled)warnings.push(name+' includes private profiling overhead; keep separate from ordinary performance comparisons.');
if(a.kind==='ticks'){
warnings.push(...serverContextWarnings(a,b));
const tick=compareTickStates(a,b);differences.push(...tick.differences);warnings.push(...tick.warnings);
@@ -126,10 +127,12 @@ function compareReports(a,b){
else if(JSON.stringify(a[key])!==JSON.stringify(b[key]))warnings.push('JVM '+key+' differs. Treat it as an explicit experiment variable.');
}
if(a.kind==='frames'){
for(const key of ['adaptive_budget_enabled','adaptive_target_fps','precise_worker_pacing','debounce_queue_wait','wakeable_idle_wait','dispatch_retry_backoff','tick_membership_dedup','empty_culling_whitelist','culling_ray_scratch','terrain_position_arithmetic','terrain_quad_reuse','interruptible_terrain_read','dh_thread_count','dh_runtime_ratio']){
for(const key of ['adaptive_budget_enabled','adaptive_target_fps','precise_worker_pacing','debounce_queue_wait','wakeable_idle_wait','dispatch_retry_backoff','tick_membership_dedup','empty_culling_whitelist','culling_ray_scratch','terrain_position_arithmetic','dh_cloud_batch_requested','dh_cloud_batch_enabled','private_render_class_prewarm','terrain_quad_reuse','interruptible_terrain_read','dh_thread_count','dh_runtime_ratio']){
if(key in a&&key in b&&JSON.stringify(a[key])!==JSON.stringify(b[key]))warnings.push('Experiment '+key+' differs.');
}
for(const [label,r] of [['Baseline',a],['Candidate',b]]){
if(!('frame_limiter_throttled_observations' in r))warnings.push(label+' effective frame-limiter state was not captured; AFK throttling is unknown.');
else if(r.frame_limiter_throttled_observations)warnings.push(label+' includes '+r.frame_limiter_throttled_observations+' frame-limiter throttled observations, including '+(r.frame_limiter_afk_observations??'unknown')+' AFK observations. Configured unlimited FPS does not override this limiter.');
if(!['available','not_installed'].includes(r.dh_capture_status))warnings.push(label+' distant-terrain configuration was not captured; quality equivalence is unknown.');
if(!['available','not_installed'].includes(r.iris_capture_status))warnings.push(label+' Iris state was not captured; shader equivalence is unknown.');
if(Array.isArray(r.visual_settings_changed)&&r.visual_settings_changed.length){
@@ -197,6 +200,7 @@ function renderDetails(side){
fields.push(['Rejected dispatch retry', r.dispatch_retry_backoff===true?'on':r.dispatch_retry_backoff===false?'off':'not captured']);
fields.push(['Wakeable queue idle', r.wakeable_idle_wait===true?'on':r.wakeable_idle_wait===false?'off':'not captured']);
if(r.kind==='frames')fields.push(['Tick membership deduplication',r.tick_membership_dedup===true?'on':r.tick_membership_dedup===false?'off':'not captured']);
if(r.kind==='frames')fields.push(['DH cloud batching',r.dh_cloud_batch_enabled===true?'on':r.dh_cloud_batch_enabled===false?'off':'not captured']);
if(r.kind==='frames')fields.push(['Terrain quad reuse',r.terrain_quad_reuse===true?'on':r.terrain_quad_reuse===false?'off':'not captured']);
if(r.kind==='frames')fields.push(['Terrain position arithmetic',r.terrain_position_arithmetic===true?'on':r.terrain_position_arithmetic===false?'off':'not captured']);
if(r.kind==='frames')fields.push(['Culling ray scratch',r.culling_ray_scratch===true?'on':r.culling_ray_scratch===false?'off':'not captured']);
+121
View File
@@ -150,6 +150,123 @@ public final class JfrSummary {
.reversed().thenComparingLong(Map.Entry::getKey)).map(e -> e.getValue().result(e.getKey())).toList();
}
record Span(long start, long end) {}
static long coveredNs(List<Span> spans) {
var ordered = spans.stream().filter(s -> s.end > s.start)
.sorted(Comparator.comparingLong(Span::start)).toList();
long total = 0, start = 0, end = 0;
for (Span span : ordered) {
if (span.start > end) {
total += end - start;
start = span.start;
}
end = Math.max(end, span.end);
}
return total + end - start;
}
static final class Stall {
final Instant start, end;
final long intervalNs;
final List<Span> pauses = new ArrayList<>(), waits = new ArrayList<>(), uploads = new ArrayList<>(), classLoads = new ArrayList<>();
final List<Map<String, Object>> loadedClasses = new ArrayList<>();
final Map<String, Long> samples = new HashMap<>();
final Map<String, Long> inclusiveSamples = new HashMap<>();
Stall(RecordedEvent event) {
start = event.getStartTime(); end = event.getEndTime();
intervalNs = event.getLong("intervalNs");
}
void overlap(RecordedEvent event, List<Span> spans) {
Instant first = event.getStartTime().isAfter(start) ? event.getStartTime() : start;
Instant last = event.getEndTime().isBefore(end) ? event.getEndTime() : end;
if (!first.isBefore(last)) return;
if (spans.size() >= 4096) throw new IllegalArgumentException("Too many overlapping diagnostic events");
spans.add(new Span(Duration.between(start, first).toNanos(), Duration.between(start, last).toNanos()));
}
Map<String, Object> result() {
return Map.of("start", start.toString(), "interval_ms", intervalNs / 1e6,
"gc_pause_overlap_ms", coveredNs(pauses) / 1e6,
"render_thread_wait_overlap_ms", coveredNs(waits) / 1e6,
"terrain_upload_overlap_ms", coveredNs(uploads) / 1e6,
"render_thread_class_load_overlap_ms", coveredNs(classLoads) / 1e6,
"render_thread_class_loads", loadedClasses,
"render_thread_sample_leaf_methods", top(samples),
"render_thread_sample_inclusive_methods", top(inclusiveSamples));
}
}
/** Two bounded streaming passes, without expanding JFR class-loader graphs. */
static Map<String, Object> frameStalls(Path path) throws Exception {
var stalls = new ArrayList<Stall>();
long frames = 0;
try (var file = new RecordingFile(path)) {
while (file.hasMoreEvents()) {
var event = file.readEvent();
String type = event.getEventType().getName();
if (!type.equals("dev.horizonlab.FrameInterval")
&& !type.equals("dev.horizonlab.metrics.MacBenchmarkIntegration$FrameIntervalEvent")) continue;
frames++;
if (event.getLong("intervalNs") < 25_000_000) continue;
if (stalls.size() >= 4096) throw new IllegalArgumentException("Too many slow frames for bounded analysis");
stalls.add(new Stall(event));
}
}
if (frames == 0) throw new IllegalArgumentException("No private frame-interval events captured");
try (var file = new RecordingFile(path)) {
while (file.hasMoreEvents()) {
var event = file.readEvent();
String type = event.getEventType().getName();
boolean pause = type.equals("jdk.GCPhasePause");
boolean wait = type.equals("jdk.ThreadPark") || type.equals("jdk.JavaMonitorEnter")
|| type.equals("jdk.JavaMonitorWait");
boolean sample = type.equals("jdk.ExecutionSample") || type.equals("jdk.NativeMethodSample");
boolean upload = type.equals("dev.horizonlab.TerrainUpload");
boolean classLoad = type.equals("jdk.ClassLoad");
if (!pause && !wait && !sample && !upload && !classLoad) continue;
if (!pause) {
var thread = event.getThread(sample ? "sampledThread" : "eventThread");
if (thread == null || !threadName(thread).equals("Render thread")) continue;
}
for (Stall stall : stalls) {
if (pause) stall.overlap(event, stall.pauses);
else if (wait) stall.overlap(event, stall.waits);
else if (upload) stall.overlap(event, stall.uploads);
else if (classLoad) {
int previous = stall.classLoads.size();
stall.overlap(event, stall.classLoads);
if (stall.classLoads.size() == previous) continue;
if (stall.loadedClasses.size() >= 128)
throw new IllegalArgumentException("Too many overlapping class loads");
var span = stall.classLoads.getLast();
stall.loadedClasses.add(Map.of("class_name", event.getClass("loadedClass").getName(),
"duration_ms", event.getDuration().toNanos() / 1e6,
"overlap_ms", (span.end - span.start) / 1e6));
}
else if (!event.getStartTime().isBefore(stall.start) && event.getStartTime().isBefore(stall.end)) {
var stack = event.getStackTrace();
if (stack != null && !stack.getFrames().isEmpty()) {
var method = stack.getFrames().getFirst().getMethod();
stall.samples.merge(method.getType().getName() + "." + method.getName(), 1L, Long::sum);
var methods = new java.util.HashSet<String>();
for (var frame : stack.getFrames()) {
var inclusive = frame.getMethod();
methods.add(inclusive.getType().getName() + "." + inclusive.getName());
}
for (String inclusive : methods) stall.inclusiveSamples.merge(inclusive, 1L, Long::sum);
}
}
}
}
}
return Map.of("frame_events", frames, "slow_frame_threshold_ms", 25,
"slow_frames", stalls.stream().sorted(Comparator.comparing(s -> s.start)).map(Stall::result).toList(),
"note", "Diagnostic overlap is not causation. GC and thread waits can overlap each other; sampled stacks can miss short work. Profiling intervals are not ordinary FPS measurements.");
}
static String json(Object value) {
if (value == null) return "null";
if (value instanceof String string) {
@@ -172,6 +289,10 @@ public final class JfrSummary {
}
public static void main(String[] args) throws Exception {
if (args.length == 2 && args[0].equals("--frame-stalls")) {
System.out.println(json(frameStalls(Path.of(args[1]))));
return;
}
if (args.length != 1) throw new IllegalArgumentException("Usage: java tools/JfrSummary.java private-recording.jfr");
System.out.println(json(summarize(Path.of(args[0]))));
}
+66
View File
@@ -0,0 +1,66 @@
#!/usr/bin/env python3
"""Build a local Prism import containing the measured M2 Pro launch settings."""
import argparse
import hashlib
import json
from pathlib import Path
try:
from . import build_pack, runtime
except ImportError:
import build_pack
import runtime
def build(collector, mod_jar):
lock = build_pack.read_lock()
if collector not in {"g1", "zgc"}:
raise ValueError("Choose the measured G1 or ZGC launch configuration")
if lock["minecraft"] != "26.3" or lock["fabric_loader"] != "0.19.5":
raise ValueError("Revalidate the Prism components before exporting a new game version")
profile = "m2-metal-batched"
selected = build_pack.PROFILES[profile]["mods"]
build_pack.validate_selection(lock, selected)
mod_bytes = mod_jar.read_bytes()
name = f"horizon-m2-metal-batched-{collector}-{lock['pack_version']}"
entries = {"minecraft/" + path: data for path, data in build_pack.overrides(profile, mod_bytes).items()}
for project in selected:
pinned = lock["projects"][project]
source = runtime.fetch(pinned["url"], build_pack.ROOT / ".local/pack-cache" / pinned["filename"],
pinned["hashes"]["sha512"], "sha512")
entries["minecraft/" + build_pack.mod_path(pinned)] = source.read_bytes()
entries["mmc-pack.json"] = json.dumps({"formatVersion": 1, "components": [
{"uid": "org.lwjgl3", "version": "3.4.3", "dependencyOnly": True},
{"uid": "net.minecraft", "version": lock["minecraft"], "important": True},
{"uid": "net.fabricmc.intermediary", "version": lock["minecraft"], "dependencyOnly": True},
{"uid": "net.fabricmc.fabric-loader", "version": lock["fabric_loader"]},
]}, indent=2).encode()
flag = {"g1": "-XX:+UseG1GC", "zgc": "-XX:+UseZGC"}[collector]
entries["instance.cfg"] = (f"InstanceType=OneSix\nname={name}\niconKey=default\n"
"AutomaticJava=true\nOverrideJavaLocation=false\nOverrideMemory=true\n"
f"MinMemAlloc=512\nMaxMemAlloc=6144\nOverrideJavaArgs=true\nJvmArgs={flag}\n").encode()
entries["HORIZON-BUILD.json"] = json.dumps({"profile": profile, "collector": collector,
"pack_version": lock["pack_version"], "original_mod_sha256": hashlib.sha256(mod_bytes).hexdigest(),
"mods": {project: lock["projects"][project]["hashes"] for project in selected}}, indent=2).encode()
output = build_pack.ROOT / "dist" / f"{name}-prism.zip"
build_pack.write_zip(output, entries)
script = output.with_name(f"Mac-Import-{collector}.command")
script.write_text('#!/bin/bash\nset -euo pipefail\ncd "$(dirname "$0")"\n'
f'open -a "Prism Launcher" --args --import "$PWD/{output.name}"\n')
script.chmod(0o755)
print(f"Built local Prism import: {output}\nCollector: {flag}; heap: 512–6144 MiB")
return output
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--gc", choices=["g1", "zgc"], required=True)
parser.add_argument("--mod", type=Path,
default=build_pack.ROOT / "dist" / f"horizon-lab-{build_pack.read_lock()['pack_version']}.jar")
args = parser.parse_args()
build(args.gc, args.mod)
if __name__ == "__main__":
main()
+12
View File
@@ -33,6 +33,10 @@ def test_metrics():
"dev.horizonlab.metrics.MetricsTest"], check=True)
subprocess.run(["java", "-Xms32M", "-Xmx256M", "-XX:+UseZGC", "-XX:+UseCompactObjectHeaders", "-cp", str(destination),
"dev.horizonlab.metrics.MetricsTest"], check=True)
subprocess.run(["javac", "--release", "25", "-d", str(destination),
str(SOURCE / "main/java/dev/horizonlab/metrics/VisualSettingsChanges.java"),
str(SOURCE / "test/java/dev/horizonlab/metrics/VisualSettingsChangesTest.java")], check=True)
subprocess.run(["java", "-cp", str(destination), "dev.horizonlab.metrics.VisualSettingsChangesTest"], check=True)
budget_sources = [SOURCE / "main/java/dev/horizonlab/budget/TerrainBudget.java",
SOURCE / "test/java/dev/horizonlab/budget/TerrainBudgetTest.java"]
subprocess.run(["javac", "--release", "25", "-d", str(destination), *map(str, budget_sources)], check=True)
@@ -65,6 +69,14 @@ def build():
dh = lock["projects"]["distanthorizons"]
jars.append(runtime.fetch(dh["url"], ROOT / ".local/pack-cache" / dh["filename"],
dh["hashes"]["sha512"], "sha512"))
# Fabric Loader provides MixinExtras at runtime; compile against that exact embedded JAR.
loader = next(jar for jar in jars if jar.name == f"fabric-loader-{lock['fabric_loader']}.jar")
with zipfile.ZipFile(loader) as archive:
embedded = "META-INF/jars/mixinextras-fabric-0.5.5.jar"
extras = BUILD / Path(embedded).name
extras.parent.mkdir(parents=True, exist_ok=True)
extras.write_bytes(archive.read(embedded))
jars.append(extras)
culling = lock["projects"]["entityculling"]
jars.append(runtime.fetch(culling["url"], ROOT / ".local/pack-cache" / culling["filename"],
culling["hashes"]["sha512"], "sha512"))
+8 -1
View File
@@ -19,6 +19,8 @@ PROFILES = {
"baseline": {"title": "Vanilla baseline", "mods": ["fabric-api"], "render": 12, "lod": 0, "heap": 6144},
"core-performance": {"title": "Full-detail performance comparison", "mods": CORE, "render": 12, "lod": 0, "heap": 6144},
"m2-balanced": {"title": "M2 Pro balanced", "mods": CORE + ["distanthorizons"], "render": 12, "lod": 128, "heap": 6144},
"m2-metal": {"title": "M2 Pro Metal-backed renderer", "mods": CORE + ["distanthorizons"], "render": 32, "lod": 128, "heap": 6144, "backend": "vulkan", "fov": 110, "clouds": "false"},
"m2-metal-batched": {"title": "M2 Pro cloud batching experiment", "mods": CORE + ["distanthorizons"], "render": 32, "lod": 128, "heap": 6144, "backend": "vulkan", "fov": 110, "clouds": "false", "cloud_batch": True},
"adaptive": {"title": "Adaptive terrain experiment", "mods": CORE + ["distanthorizons"], "render": 12, "lod": 128, "heap": 6144, "adaptive": True},
"worker-pacing": {"title": "Precise worker pacing experiment", "mods": CORE + ["distanthorizons"], "render": 12, "lod": 128, "heap": 6144, "pacing": True},
"adaptive-pacing": {"title": "Adaptive precise pacing experiment", "mods": CORE + ["distanthorizons"], "render": 12, "lod": 128, "heap": 6144, "adaptive": True, "pacing": True},
@@ -77,6 +79,10 @@ def options(profile):
"renderDistance": profile["render"], "simulationDistance": 12, "enableVsync": "false",
"maxFps": 260, "guiScale": 2, "onboardAccessibility": "false",
"preferredGraphicsBackend": json.dumps(profile.get("backend", "opengl"))}
if "fov" in profile:
values["fov"] = (profile["fov"] - 70) / 40
if "clouds" in profile:
values["renderClouds"] = json.dumps(profile["clouds"])
return "".join(f"{key}:{value}\n" for key, value in values.items())
@@ -86,7 +92,8 @@ def overrides(profile_name, mod_bytes):
"config/horizon-profile.txt": (profile_name + "\n").encode(),
"options.txt": options(profile).encode()}
experiment = any(profile.get(key, False) for key in ["adaptive", "pacing", "queue_wait", "dispatch_retry", "tick_dedup", "culling_empty", "culling_rays", "terrain_position", "quad_reuse"])
config = ROOT / "packs/config" / f"{'m2-balanced' if experiment else profile_name}.toml"
config = ROOT / "packs/config" / f"{'m2-balanced' if experiment or profile_name in {'m2-metal', 'm2-metal-batched'} else profile_name}.toml"
files["config/horizon-cloud-batch.properties"] = f"enabled={str(profile.get('cloud_batch', False)).lower()}\n".encode()
files["config/horizon-adaptive.properties"] = (
f"enabled={str(profile.get('adaptive', False)).lower()}\ntargetFps={profile.get('target_fps', 60)}\n").encode()
files["config/horizon-worker-pacing.properties"] = (
+11 -2
View File
@@ -8,7 +8,7 @@ from pathlib import Path
MATCH_FIELDS = ("minecraft", "kind", "os", "os_version", "architecture", "gpu", "graphics_backend", "graphics_driver",
"server_runtime_kind", "client_graphics_capture_status", "java_version", "heap_max_bytes", "framebuffer_width", "framebuffer_height", "render_distance",
"simulation_distance", "vsync", "fps_limit", "graphics_preset", "ambient_occlusion",
"simulation_distance", "vsync", "fps_limit", "inactivity_fps_limit", "graphics_preset", "ambient_occlusion",
"improved_transparency", "mipmap_levels", "entity_distance_scaling", "entity_shadows", "particles",
"clouds", "cloud_range", "fov", "world_difficulty", "dh_capture_status", "dh_rendering_enabled", "dh_renderer_mode",
"dh_lod_radius_chunks", "dh_horizontal_quality", "dh_vertical_quality", "dh_max_horizontal_resolution",
@@ -118,7 +118,12 @@ def compare(baseline, candidate):
mismatches = [f"{key}: {baseline.get(key)!r} vs {candidate.get(key)!r}" for key in MATCH_FIELDS
if baseline.get(key) != candidate.get(key)]
warnings = ["World, route, warm-up, shader state and LOD cache must also match; these are not verified by this tool."]
warnings.append("Memory peaks are sampled and can miss short spikes. RSS includes resident JVM and native pages, but is not a complete GPU/unified-memory measurement. Heap/nonheap measure JVM pools only. CPU includes the whole process and integrated server.")
warnings.append("GC collection time comes from collector MXBean counters. It may include concurrent cycles and overlapping collector phases; it is not a measured stop-the-world pause total. Use JFR pause events for that comparison.")
warnings.extend(software_warnings(baseline, candidate))
for name, report in [("baseline", baseline), ("candidate", candidate)]:
if report.get("private_profiling_enabled"):
warnings.append(f"{name} includes private profiling overhead; keep separate from ordinary performance comparisons.")
if baseline["kind"] == "ticks":
warnings.extend(server_context_warnings(baseline, candidate))
tick_mismatches, tick_warnings = compare_tick_states(baseline, candidate)
@@ -127,10 +132,14 @@ def compare(baseline, candidate):
warnings.append("Tick durations exclude scheduler sleep. Observed TPS counts measured invocations, including frozen ticks, and includes recording time spent paused; tick execution capacity is not TPS.")
if baseline["kind"] == "frames":
for field in ["adaptive_budget_enabled", "adaptive_target_fps", "precise_worker_pacing", "debounce_queue_wait",
"wakeable_idle_wait", "dispatch_retry_backoff", "tick_membership_dedup", "empty_culling_whitelist", "culling_ray_scratch", "terrain_position_arithmetic", "terrain_quad_reuse", "interruptible_terrain_read", "dh_thread_count", "dh_runtime_ratio"]:
"wakeable_idle_wait", "dispatch_retry_backoff", "tick_membership_dedup", "empty_culling_whitelist", "culling_ray_scratch", "terrain_position_arithmetic", "dh_cloud_batch_requested", "dh_cloud_batch_enabled", "private_render_class_prewarm", "terrain_quad_reuse", "interruptible_terrain_read", "dh_thread_count", "dh_runtime_ratio"]:
if field in baseline and field in candidate and baseline[field] != candidate[field]:
warnings.append(f"Experiment {field} differs: {baseline[field]!r} vs {candidate[field]!r}.")
for name, report in [("baseline", baseline), ("candidate", candidate)]:
if "frame_limiter_throttled_observations" not in report:
warnings.append(f"{name} effective frame-limiter state was not captured; AFK throttling is unknown.")
elif report["frame_limiter_throttled_observations"]:
warnings.append(f"{name} includes {report['frame_limiter_throttled_observations']} frame-limiter throttled observations, including {report.get('frame_limiter_afk_observations', 'unknown')} AFK observations. Configured unlimited FPS does not override this limiter.")
if report.get("dh_capture_status") not in ["available", "not_installed"]:
warnings.append(f"{name} distant-terrain configuration was not captured; quality equivalence is unknown.")
if report.get("iris_capture_status") not in ["available", "not_installed"]:
+226
View File
@@ -0,0 +1,226 @@
#!/usr/bin/env python3
"""Run an isolated physical-Mac benchmark using copied demo-world data."""
import argparse
import csv
import hashlib
import json
import math
import os
from pathlib import Path
import platform
import re
import shutil
import sys
try:
from . import build_mod, build_pack
from .test_culling_rays import run_fixture
except ImportError:
import build_mod
import build_pack
from test_culling_rays import run_fixture
def validate_capture(result, instance, render, seconds):
"""Reject changed workloads and truncated files before interpreting throughput."""
frames, ticks = result["frames"], result["ticks"]
if frames.get("inactivity_fps_limit") != "minimized" or frames.get("frame_limiter_throttled_observations") != 0:
raise AssertionError("The stationary workload was frame-throttled or limiter state was not captured")
if frames["visual_settings_changed"]:
raise AssertionError("Visual settings changed during measurement")
if frames["render_distance"] != render or frames["simulation_distance"] != 12:
raise AssertionError("Effective view or simulation settings differ")
for kind, report in [("frames", frames), ("ticks", ticks)]:
if report["kind"] != kind or report["dropped_samples"] or report["excluded_frames"]:
raise AssertionError("Lost, excluded or misidentified measurement samples")
if abs(report["elapsed_seconds"] - seconds) > 1:
raise AssertionError("Measurement duration differs")
if any(report[field] != result[field] for field in ["gpu", "graphics_backend"]):
raise AssertionError("Captured device differs from the actual runtime device")
sample_name = report["samples_file"]
if Path(sample_name).name != sample_name:
raise AssertionError("Invalid sample filename")
with (instance / "horizon-benchmarks" / sample_name).open() as source:
durations = [int(row["duration_ns"]) for row in csv.DictReader(source)]
stats = report["stats"]
if len(durations) != stats["count"] or len(durations) < 100 or min(durations) <= 0:
raise AssertionError("CSV sample counts or durations are invalid")
if not math.isclose(sum(durations) / len(durations) / 1e6, stats["meanMs"], rel_tol=1e-10):
raise AssertionError("CSV timings differ from the summary")
if kind == "frames" and abs(sum(durations) / 1e9 - report["elapsed_seconds"]) > 1:
raise AssertionError("Active frame intervals do not cover the recording")
if ticks["tick_state_changed"] or any(ticks["tick_state_" + field + "_samples"] for field in
["frozen", "stepping", "sprinting", "suppressed", "nonstandard_rate"]):
raise AssertionError("Simulation tick-manager state changed or is nonstandard")
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--profile", choices=build_pack.PROFILES, default="m2-balanced")
parser.add_argument("--backend", choices=["opengl", "vulkan"], required=True)
parser.add_argument("--label", required=True)
parser.add_argument("--seed-world", type=Path, required=True)
parser.add_argument("--warmup", type=int, default=60)
parser.add_argument("--seconds", type=int, default=120)
parser.add_argument("--post-record-seconds", type=int, default=3, help="Keep the private world alive after capture for external profiler shutdown")
parser.add_argument("--render", type=int, default=32)
parser.add_argument("--scene", choices=["spawn", "overlook", "flyover"], default="spawn",
help="Use survival spawn, a fixed spectator view, or a vanilla-input flyover")
parser.add_argument("--width", type=int, default=1280)
parser.add_argument("--height", type=int, default=800)
parser.add_argument("--fullscreen", action="store_true")
parser.add_argument("--heap", default="6G")
parser.add_argument("--compact-headers", action="store_true")
parser.add_argument("--cloud-image-check", action="store_true", help="Frozen-scene image validation only; no FPS measurement")
parser.add_argument("--cloud-batch", action="store_true", help="Test private M2 Pro cloud batching; not part of playable packs")
parser.add_argument("--gpu-profile", action="store_true", help="Add asynchronous GPU timings; keep separate from ordinary benchmarks")
parser.add_argument("--jfr-profile", action="store_true", help="Capture JVM events and frame intervals separately from ordinary benchmarks")
parser.add_argument("--vbo-profile", action="store_true", help="Measure original DH allocation, staging/copy and complete terrain uploads")
parser.add_argument("--render-prewarm", action="store_true", help="Privately preload five measured rendering classes without initializing them")
parser.add_argument("--pass-profile", action="store_true", help="Sample native Vulkan render-pass times separately from normal benchmarks")
parser.add_argument("--fixture", type=Path, help="Reuse an earlier private fixture JAR for an identical-code comparison")
parser.add_argument("--gc", choices=["default", "g1", "zgc"], default="default")
args = parser.parse_args()
if platform.system() != "Darwin" or platform.machine() not in {"arm64", "aarch64"}:
parser.error("This benchmark targets the physical Apple Silicon Mac")
if not re.fullmatch(r"[a-zA-Z0-9_-]{1,64}", args.label):
parser.error("--label must contain 1 to 64 letters, digits, underscores or hyphens")
if not (0 <= args.warmup <= 300 and 10 <= args.seconds <= 120 and 2 <= args.render <= 32):
parser.error("Invalid warmup, measurement duration or view distance")
if not 3 <= args.post_record_seconds <= 120:
parser.error("Post-recording delay must be between 3 and 120 seconds")
production_cloud_batch = bool(build_pack.PROFILES[args.profile].get("cloud_batch"))
if args.cloud_batch and production_cloud_batch:
parser.error("Do not combine private and production cloud batch mixins")
if args.cloud_image_check and (not (args.cloud_batch or production_cloud_batch) or args.scene != "overlook" or args.gpu_profile or args.pass_profile or args.jfr_profile or args.vbo_profile):
parser.error("--cloud-image-check requires cloud batching and --scene overlook, without profiling")
if args.cloud_batch and (args.backend != "vulkan" or "distanthorizons" not in build_pack.PROFILES[args.profile]["mods"]):
parser.error("--cloud-batch requires Vulkan and a Distant Horizons profile")
if args.pass_profile and args.backend != "vulkan":
parser.error("--pass-profile requires the Vulkan backend")
if args.vbo_profile and (args.backend != "vulkan" or "distanthorizons" not in build_pack.PROFILES[args.profile]["mods"]):
parser.error("--vbo-profile requires Vulkan and Distant Horizons")
if args.render_prewarm and (args.cloud_image_check or args.backend != "vulkan" or "sodium" not in build_pack.PROFILES[args.profile]["mods"]):
parser.error("--render-prewarm requires Vulkan and Sodium, without cloud image checks")
if not (args.seed_world / "level.dat").is_file():
parser.error("--seed-world must be an existing disposable demo-world directory")
root = build_pack.ROOT
instance = root / ".local/runtime/mac-benchmarks" / args.label
if instance.exists():
parser.error("This label already exists; use a new label to retain prior evidence")
mixins = ["MacGpuTimingMixin"] if args.gpu_profile else []
if args.jfr_profile:
mixins.append("MacFrameIntervalMixin")
if args.vbo_profile:
mixins.append("MacDhVertexAllocationMixin")
if args.cloud_image_check and production_cloud_batch:
mixins.append("MacDhCloudClockMixin")
if args.cloud_batch:
mixins.extend(["MacDhCloudBatchMixin", "MacDhCloudUniformMixin", "MacDhCloudBarrierMixin", "MacDhCloudClockMixin"])
if args.pass_profile:
mixins.extend(["MacGpuPassFrameMixin", "MacVulkanPassTimingMixin"])
fixture = args.fixture or build_mod.build_integration_fixture("mac-benchmark",
"dev.horizonlab.metrics.MacBenchmarkIntegration", fixture_dependencies={},
fixture_mixins=mixins)
mod = root / "dist" / f"horizon-lab-{build_pack.read_lock()['pack_version']}.jar"
hashes = {"original_mod_sha256": hashlib.sha256(mod.read_bytes()).hexdigest(),
"fixture_sha256": hashlib.sha256(fixture.read_bytes()).hexdigest()}
build_pack.prepare_instance(args.profile, instance, mod)
shutil.copytree(args.seed_world, instance / "saves/Demo_World")
shutil.copy2(fixture, instance / "mods/mac-benchmark-fixture.jar")
options = instance / "options.txt"
text = options.read_text()
text = re.sub(r"^renderDistance:.*$", f"renderDistance:{args.render}", text, flags=re.MULTILINE)
text = re.sub(r"^preferredGraphicsBackend:.*$", "preferredGraphicsBackend:" + json.dumps(args.backend), text, flags=re.MULTILINE)
# Match the supplied capture's expensive visual workload. Simulation remains 12.
options.write_text(text + 'fov:1.0\nrenderClouds:"false"\ninactivityFpsLimit:"minimized"\n')
(instance / "config/mac-benchmark.properties").write_text(
f"label={args.label}\nwarmupSeconds={args.warmup}\nseconds={args.seconds}\n"
f"gpuProfile={str(args.gpu_profile).lower()}\nscene={args.scene}\n"
f"jfrProfile={str(args.jfr_profile).lower()}\n"
f"vboProfile={str(args.vbo_profile).lower()}\n"
f"renderClassPrewarm={str(args.render_prewarm).lower()}\n"
f"postRecordSeconds={args.post_record_seconds}\n"
f"passProfile={str(args.pass_profile).lower()}\ncloudBatch={str(args.cloud_batch).lower()}\n"
f"cloudImageCheck={str(args.cloud_image_check).lower()}\n"
f"productionCloudBatch={str(production_cloud_batch).lower()}\n")
command = [sys.executable, str(root / "tools/runtime.py"), "demo", "--instance", str(instance),
"--heap", args.heap, "--width", str(args.width), "--height", str(args.height), "--gc", args.gc]
if args.compact_headers:
command.append("--compact-headers")
if args.fullscreen:
command.append("--fullscreen")
run = run_fixture(command, instance, os.environ.copy(), timeout=args.warmup + args.seconds + args.post_record_seconds + 180)
result_file = instance / "mac-benchmark-result.json"
if run.returncode or not result_file.is_file():
raise RuntimeError(f"Mac benchmark failed; inspect the private log in {instance}")
result = json.loads(result_file.read_text())
if args.cloud_image_check:
if not result.get("image_check_only") or not result.get("frozen_scene") or result.get("cloud_batch", {}).get("draws", 0) < 100:
raise AssertionError("Frozen cloud image check was not exercised")
if not all((instance / name).is_file() for name in ["cloud-image-control-a.png", "cloud-image-batched.png", "cloud-image-control-b.png"]):
raise AssertionError("Cloud image readbacks are missing")
result.update(hashes)
result_file.write_text(json.dumps(result, indent=2) + "\n")
print(json.dumps(result), flush=True)
return
frames = result["frames"]
validate_capture(result, instance, args.render, args.seconds)
if result.get("post_record_seconds", 3) != args.post_record_seconds:
raise AssertionError("This fixture does not provide the requested profiler shutdown delay")
if bool(result.get("render_class_prewarm")) != args.render_prewarm:
raise AssertionError("Requested rendering-class prewarm differs from the fixture")
if args.render_prewarm and len(result.get("prewarmed_render_classes", [])) != 5:
raise AssertionError("Measured rendering classes were not all preloaded")
if production_cloud_batch and (not frames.get("dh_cloud_batch_enabled") or frames.get("dh_cloud_batch_draws_recorded", 0) < 100):
raise AssertionError("Production cloud batching was not active in the recording")
if args.scene == "flyover" and math.hypot(result["player_x"] + 41.5, result["player_z"] - 72.5) < args.seconds:
raise AssertionError("Private flyover did not move far enough to exercise terrain")
if result.get("scene", "spawn") != args.scene:
raise AssertionError("Fixture did not render the requested scene")
batch = result.get("cloud_batch", {})
if args.cloud_batch and (not batch.get("requested") or batch.get("draws", 0) < 100 or batch.get("passes", 0) < 1):
raise AssertionError("Requested private cloud batch was not exercised")
if not args.cloud_batch and batch.get("requested"):
raise AssertionError("Unexpected private cloud batch")
if args.gpu_profile:
if result.get("gpu_profile", {}).get("gpu_render_duration", {}).get("count", 0) < 100:
raise AssertionError("Requested GPU timings were not captured")
elif "gpu_profile" in result:
raise AssertionError("Unexpected GPU profiling in a timing-only run")
if args.pass_profile:
passes = result.get("gpu_pass_profile", {}).get("passes", {})
if not passes or result["graphics_backend"] != "Vulkan":
raise AssertionError("Requested native Vulkan pass timings were not captured")
elif "gpu_pass_profile" in result:
raise AssertionError("Unexpected GPU pass profiling in a timing-only run")
if args.jfr_profile:
if "jfr_profile" not in result or (instance / "mac-benchmark.jfr").stat().st_size < 1024:
raise AssertionError("Requested JFR diagnostics were not captured")
elif "jfr_profile" in result:
raise AssertionError("Unexpected JFR profiling in a timing-only run")
if args.vbo_profile:
if result.get("vbo_allocation_profile", {}).get("instrumented_calls_total", 0) < 1:
raise AssertionError("Requested terrain allocation profiling was not exercised")
timings = result["vbo_allocation_profile"]
if any(key not in timings for key in ["whole_upload", "staging_copy_and_barrier"]):
raise AssertionError("This fixture does not capture the complete terrain upload path")
if any(timing["dropped_samples"] or timing["dropped_slow_events"] for timing in
[timings, timings["whole_upload"], timings["staging_copy_and_barrier"]]):
raise AssertionError("Terrain allocation profiling lost samples")
elif "vbo_allocation_profile" in result:
raise AssertionError("Unexpected allocation profiling in a timing-only run")
result["requested_backend"] = args.backend
result["backend_fallback"] = result["graphics_backend"].lower() != args.backend
result.update(hashes)
result["profiling_enabled"] = args.gpu_profile or args.pass_profile or args.jfr_profile or args.vbo_profile
result_file.write_text(json.dumps(result, indent=2) + "\n")
print(json.dumps({"label": args.label, "gpu": result["gpu"], "backend": result["graphics_backend"],
"fallback": result["backend_fallback"], "frames": frames["stats"],
"process_cpu_mean_cores": frames["process_cpu_mean_cores"],
"resolution": [frames["framebuffer_width"], frames["framebuffer_height"]]}), flush=True)
if __name__ == "__main__":
main()
+8 -1
View File
@@ -160,11 +160,16 @@ def main():
parser.add_argument("command", choices=["prepare", "demo"])
parser.add_argument("--instance", type=Path, default=CACHE / "demo")
parser.add_argument("--heap", default="4G")
parser.add_argument("--width", type=int, default=1280, help="Logical window width; Retina framebuffer can be larger")
parser.add_argument("--height", type=int, default=800, help="Logical window height; Retina framebuffer can be larger")
parser.add_argument("--fullscreen", action="store_true", help="Use the game's native fullscreen mode")
parser.add_argument("--compact-headers", action="store_true", help="Enable Java 25 compact object headers for this demo process")
parser.add_argument("--gc", choices=["default", "g1", "zgc"], default="default", help="Select a collector for this process; default leaves JVM policy in use")
args = parser.parse_args()
if not re.fullmatch(r"[1-9][0-9]*[MGmg]", args.heap):
parser.error("--heap must look like 4G")
if not (64 <= args.width <= 8192 and 64 <= args.height <= 8192):
parser.error("--width and --height must be between 64 and 8192")
manifest, jars, natives = prepare_client()
if args.command == "prepare":
return
@@ -179,7 +184,9 @@ def main():
"--username", "HorizonDemo", "--version", manifest["id"], "--gameDir", str(instance),
"--assetsDir", str(CACHE / "assets"), "--assetIndex", manifest["assetIndex"]["id"],
"--accessToken", "0", "--uuid", "00000000000000000000000000000000", "--demo",
"--width", "1280", "--height", "800"]
"--width", str(args.width), "--height", str(args.height)]
if args.fullscreen:
command.append("--fullscreen")
subprocess.run(command, cwd=instance, check=True)
+2 -2
View File
@@ -25,7 +25,7 @@ class FixtureTimeout(RuntimeError):
self.run = run
def run_fixture(command, instance, environment):
def run_fixture(command, instance, environment, timeout=240):
# Keep all children of this private fixture in a controllable process group.
process = subprocess.Popen(command, cwd=build_pack.ROOT, env=environment, text=True,
stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
@@ -37,7 +37,7 @@ def run_fixture(command, instance, environment):
try: os.killpg(process.pid, signal.SIGKILL if force else signal.SIGTERM)
except ProcessLookupError: pass
try:
output, _ = process.communicate(timeout=240)
output, _ = process.communicate(timeout=timeout)
except subprocess.TimeoutExpired:
stop_tree()
try: output, _ = process.communicate(timeout=10)
+24 -1
View File
@@ -3,7 +3,9 @@
import argparse
import json
import math
from pathlib import Path
import re
import subprocess
ROOT = Path(__file__).resolve().parents[1]
@@ -14,7 +16,11 @@ def main(collector="default"):
BUILD.mkdir(parents=True, exist_ok=True)
fixture = BUILD / "fixture.jfr"
gc_flags = ["-XX:+UseZGC"] if collector == "zgc" else []
subprocess.run(["java", "-Xmx128M", *gc_flags, str(ROOT / "tests/java/JfrFixture.java"), str(fixture)], check=True)
classes = BUILD / "classes"
classes.mkdir(exist_ok=True)
subprocess.run(["javac", "--release", "25", "-d", str(classes),
str(ROOT / "tools/JfrSummary.java"), str(ROOT / "tests/java/JfrFixture.java")], check=True)
subprocess.run(["java", "-Xmx128M", *gc_flags, "-cp", str(classes), "JfrFixture", str(fixture)], check=True)
summary = json.loads(subprocess.check_output(
["java", str(ROOT / "tools/JfrSummary.java"), str(fixture)], text=True))
events = json.loads(subprocess.check_output(
@@ -63,7 +69,24 @@ def main(collector="default"):
for child in value:
no_weight_keys(child)
no_weight_keys(summary)
frames = json.loads(subprocess.check_output(
["java", "-cp", str(classes), "JfrSummary", "--frame-stalls", str(fixture)], text=True))
assert frames["frame_events"] == 1 and len(frames["slow_frames"]) == 1
assert 0 < frames["slow_frames"][0]["gc_pause_overlap_ms"] < frames["slow_frames"][0]["interval_ms"]
uploads = json.loads(subprocess.check_output(
["jfr", "print", "--json", "--events", "dev.horizonlab.TerrainUpload", str(fixture)],
text=True))["recording"]["events"]
assert len(uploads) == 2
durations = [float(re.fullmatch(r"PT([0-9.]+)S", event["values"]["duration"]).group(1)) * 1000
for event in uploads]
assert math.isclose(frames["slow_frames"][0]["terrain_upload_overlap_ms"], max(durations), abs_tol=1e-6)
loads = frames["slow_frames"][0]["render_thread_class_loads"]
payload = next(row for row in loads if row["class_name"] == "JfrFixture$LoadPayload")
assert payload["duration_ms"] >= 5
coverage = frames["slow_frames"][0]["render_thread_class_load_overlap_ms"]
assert payload["overlap_ms"] <= coverage <= frames["slow_frames"][0]["interval_ms"]
print("Streaming JFR summary matches real JDK allocation counters, stack-sample counts, GC event counts, heap observations and escaped thread names.")
print("Frame/GC overlap, nested upload coverage, real slow class loading and 10,000 seeded interval-union cases checked.")
if __name__ == "__main__":