From 73bce53a2b7578621ef150cb563622bc8560535d Mon Sep 17 00:00:00 2001 From: Zwitschi Date: Mon, 9 Nov 2026 12:40:48 +0100 Subject: [PATCH] =?UTF-8?q?feat(hud):=20F3=20debug=20overlay=20=E2=80=94?= =?UTF-8?q?=20FPS,=20frame=20time,=20worst=20frame,=20state?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A top-left readout for performance work: smoothed FPS and frame time, the worst frame in a rolling two-second window, the active view, player speed, a frame counter and the player's position in metres from the project origin. - **F3** toggles it, through the console variable so a packaged build can be checked without a rebuild (). - **On automatically** for and , so the performance capture never depends on pressing a key first; **off in the built demo**, because a player should not see an FPS counter over Hollywood Boulevard. - The first two seconds are a **warm-up window** excluded from the worst-frame figure. Without it the readout showed 'worst 400 ms' for a healthy run: the startup shader and streaming hitches dominated the rolling maximum. Measured cost on the self-test route at 1440p: 2.64 -> 2.77 ms average, about 5 %, still 6x the floor. The release-check number stays the overlay-off run; the overlay-on series is committed beside it as Docs/perf/0.2.0-walk-frametimes-overlay.csv. Docs: controls (F3 row, overlay section, switch table), release checks (cost row), architecture building block view (HUD and input list). --- Docs/Architecture/05_building_block_view.md | 4 +- Docs/Architecture/architecture.md | 4 +- Docs/controls.md | 12 +- Docs/perf/0.2.0-walk-frametimes-overlay.csv | 723 ++++++++++++++++++ Docs/release-checks.md | 1 + .../Content/Input/IA_ToggleDebug.uasset | 3 + Game/GigLine/Content/Input/IMC_Default.uasset | 4 +- .../Source/GigLine/GigLineCharacter.cpp | 30 + .../GigLine/Source/GigLine/GigLineCharacter.h | 10 + Game/GigLine/Source/GigLine/GigLineHUD.cpp | 81 ++ Game/GigLine/Source/GigLine/GigLineHUD.h | 34 + Tools/ue_create_input_assets.py | 6 +- 12 files changed, 904 insertions(+), 8 deletions(-) create mode 100644 Docs/perf/0.2.0-walk-frametimes-overlay.csv create mode 100644 Game/GigLine/Content/Input/IA_ToggleDebug.uasset diff --git a/Docs/Architecture/05_building_block_view.md b/Docs/Architecture/05_building_block_view.md index 45bc8fb..1d35105 100644 --- a/Docs/Architecture/05_building_block_view.md +++ b/Docs/Architecture/05_building_block_view.md @@ -20,12 +20,12 @@ C++ classes in `Game/GigLine/Source/GigLine/`: | --- | --- | --- | | `AGigLineCharacter` | First-person player pawn | 1.75 m capsule, 4.5 m/s walk, Enhanced Input bindings, reset, pause, `-qaselftest` harness | | `AGigLineGameModeBase` | Sets game mode and HUD class | `HUDClass = AGigLineHUD` | -| `AGigLineHUD` | HUD text overlay | `DrawText` every frame, bottom-left | +| `AGigLineHUD` | HUD text overlay | `DrawText` every frame: ODbL attribution bottom-left, controls bottom-right, and an F3 debug readout top-left (`gigline.Debug`, on for `-perf`/`-qaselftest`) | | `AGigLineNPCPerson` | NPC prefab (future) | Capsule + visible MPFB body, feet on Z 0 | | `AGigLineShotActor` | Screenshot camera walker | Drives cameras in `-game` mode, `FScreenshotRequest`, quits | | `UGigLineAssetTools` | C++ collision helpers | `SetComplexCollisionAsSimple` on `StaticMesh` assets | -Input assets in `/Game/Input/`: `IA_Move`, `IA_MoveBack`, `IA_Strafe`, `IA_StrafeLeft`, `IA_Look`, `IA_Jump`, `IA_Reset`, `IA_Pause`, `IA_ToggleCamera`, bound in `IMC_Default`. +Input assets in `/Game/Input/`: `IA_Move`, `IA_MoveBack`, `IA_Strafe`, `IA_StrafeLeft`, `IA_Look`, `IA_Jump`, `IA_Reset`, `IA_Pause`, `IA_ToggleCamera`, `IA_ToggleDebug`, bound in `IMC_Default`. Player-facing bindings: **WASD** move, **mouse** look, **Space** jump, **R** reset, **Escape** pause, **V** swap first/third person. The camera and body work landed with `0.2.0` (see ADR-9): the shipped default view stays first person, and `-thirdperson` starts the pawn in the third-person rig — used by the screenshot run so the review is reproducible. diff --git a/Docs/Architecture/architecture.md b/Docs/Architecture/architecture.md index 1b2b491..e50df9a 100644 --- a/Docs/Architecture/architecture.md +++ b/Docs/Architecture/architecture.md @@ -164,12 +164,12 @@ C++ classes in `Game/GigLine/Source/GigLine/`: | --- | --- | --- | | `AGigLineCharacter` | First-person player pawn | 1.75 m capsule, 4.5 m/s walk, Enhanced Input bindings, reset, pause, `-qaselftest` harness | | `AGigLineGameModeBase` | Sets game mode and HUD class | `HUDClass = AGigLineHUD` | -| `AGigLineHUD` | HUD text overlay | `DrawText` every frame, bottom-left | +| `AGigLineHUD` | HUD text overlay | `DrawText` every frame: ODbL attribution bottom-left, controls bottom-right, and an F3 debug readout top-left (`gigline.Debug`, on for `-perf`/`-qaselftest`) | | `AGigLineNPCPerson` | NPC prefab (future) | Capsule + visible MPFB body, feet on Z 0 | | `AGigLineShotActor` | Screenshot camera walker | Drives cameras in `-game` mode, `FScreenshotRequest`, quits | | `UGigLineAssetTools` | C++ collision helpers | `SetComplexCollisionAsSimple` on `StaticMesh` assets | -Input assets in `/Game/Input/`: `IA_Move`, `IA_MoveBack`, `IA_Strafe`, `IA_StrafeLeft`, `IA_Look`, `IA_Jump`, `IA_Reset`, `IA_Pause`, `IA_ToggleCamera`, bound in `IMC_Default`. +Input assets in `/Game/Input/`: `IA_Move`, `IA_MoveBack`, `IA_Strafe`, `IA_StrafeLeft`, `IA_Look`, `IA_Jump`, `IA_Reset`, `IA_Pause`, `IA_ToggleCamera`, `IA_ToggleDebug`, bound in `IMC_Default`. Player-facing bindings: **WASD** move, **mouse** look, **Space** jump, **R** reset, **Escape** pause, **V** swap first/third person. The camera and body work landed with `0.2.0` (see ADR-9): the shipped default view stays first person, and `-thirdperson` starts the pawn in the third-person rig — used by the screenshot run so the review is reproducible. diff --git a/Docs/controls.md b/Docs/controls.md index 287cef4..d17b4e8 100644 --- a/Docs/controls.md +++ b/Docs/controls.md @@ -13,6 +13,7 @@ Keyboard and mouse. The same list is shown in-game, bottom-right of the frame (` | Reset to start | `R` | Returns to the grounded spawn point | | Pause / release mouse | `Escape` | Second press resumes and recaptures the mouse | | Swap first / third person | `V` | First person is the default; the body is hidden in that view | +| Show / hide debug overlay | `F3` | FPS, frame time, worst frame, view mode, speed and player position | ## Notes @@ -24,6 +25,14 @@ Keyboard and mouse. The same list is shown in-game, bottom-right of the frame (` - **`V` swaps first and third person** (`0.2.0`, `IA_ToggleCamera`). The shipped default view is still first person, as the owner decided for `0.1.0`; third person adds a collision-tested spring-arm camera and a visible, animated body. `Escape` still pauses and releases the mouse in either view. +## Debug overlay + +`F3` toggles a top-left readout: smoothed **FPS** and frame time, the **worst** frame in a rolling two-second window, the active view (first or third person), player speed, a frame counter, and the player's position in metres from the project origin. The first two seconds of a session are a warm-up window and are excluded from the worst-frame figure, because shader and streaming hitches there made a healthy run read as a 400 ms stall. + +It is **off in the built demo** — a player should not see an FPS counter over Hollywood Boulevard — and **on automatically** when the process is started with `-perf` or `-qaselftest`, so a performance capture never depends on someone pressing a key first. In a running build it can also be forced with the console variable `gigline.Debug 1`. + +Cost: about **5 %** of frame time at 1440p in the demo (2.64 → 2.77 ms average), measured in the same self-test route. The performance release check is recorded without the overlay for that reason; the overlay run is kept beside it as `Docs/perf/0.2.0-walk-frametimes-overlay.csv`. + ## Review and harness switches These are command-line switches for the screenshot pipeline and QA runs, not player controls. They exist so a capture is reproducible rather than dependent on a human pressing keys at the right moment. @@ -32,7 +41,8 @@ These are command-line switches for the screenshot pipeline and QA runs, not pla | --- | --- | | `-thirdperson` | Starts the pawn in the third-person rig, so a capture shows the body without anyone pressing `V` | | `-walk` | Holds forward input from the first frame — what makes the stride shot possible | -| `-qaselftest` | Runs the scripted walk / reset / pause / transect checks and quits | +| `-qaselftest` | Runs the scripted walk / reset / pause / transect checks and quits; also enables the debug overlay | +| `-perf` | Enables the debug overlay for a manual frame-time check | The walking shot is driven per shot rather than by the switch: the shot actor enables the walk only while a tracking (`ShotFollow`) camera is being captured, and returns the pawn to spawn afterwards, so the fixed-camera shots still find it in place. diff --git a/Docs/perf/0.2.0-walk-frametimes-overlay.csv b/Docs/perf/0.2.0-walk-frametimes-overlay.csv new file mode 100644 index 0000000..0de40fd --- /dev/null +++ b/Docs/perf/0.2.0-walk-frametimes-overlay.csv @@ -0,0 +1,723 @@ +frame,ms +0,2.607 +1,2.532 +2,2.324 +3,3.432 +4,2.808 +5,2.870 +6,2.367 +7,2.311 +8,2.756 +9,3.278 +10,2.418 +11,2.665 +12,2.438 +13,2.646 +14,2.461 +15,2.941 +16,2.975 +17,3.164 +18,2.766 +19,2.341 +20,2.543 +21,3.395 +22,2.772 +23,2.575 +24,2.737 +25,2.427 +26,2.538 +27,2.708 +28,3.183 +29,2.734 +30,2.425 +31,2.546 +32,2.600 +33,2.545 +34,3.705 +35,2.563 +36,2.907 +37,2.368 +38,2.470 +39,2.411 +40,3.778 +41,3.735 +42,2.292 +43,2.458 +44,2.566 +45,3.044 +46,3.449 +47,2.914 +48,3.243 +49,2.084 +50,3.872 +51,1.633 +52,3.389 +53,2.448 +54,2.474 +55,2.509 +56,2.346 +57,3.192 +58,2.762 +59,2.788 +60,2.538 +61,2.520 +62,2.517 +63,3.036 +64,3.205 +65,3.080 +66,2.514 +67,2.429 +68,2.515 +69,2.963 +70,3.555 +71,2.430 +72,2.742 +73,2.435 +74,2.526 +75,3.304 +76,3.511 +77,2.477 +78,2.472 +79,2.452 +80,2.421 +81,3.334 +82,3.286 +83,2.725 +84,2.411 +85,2.597 +86,2.440 +87,3.213 +88,3.411 +89,3.047 +90,2.365 +91,2.530 +92,2.877 +93,3.249 +94,3.186 +95,2.684 +96,2.304 +97,2.406 +98,2.948 +99,3.652 +100,2.822 +101,2.552 +102,2.575 +103,2.354 +104,2.676 +105,2.775 +106,3.865 +107,2.480 +108,2.501 +109,2.675 +110,2.724 +111,3.671 +112,2.828 +113,3.064 +114,2.153 +115,2.752 +116,2.936 +117,3.008 +118,2.932 +119,2.648 +120,2.441 +121,3.471 +122,1.885 +123,4.435 +124,2.504 +125,3.241 +126,2.813 +127,2.445 +128,3.321 +129,3.055 +130,3.222 +131,2.766 +132,2.582 +133,2.597 +134,3.637 +135,3.150 +136,3.163 +137,2.675 +138,2.708 +139,2.581 +140,3.377 +141,3.518 +142,2.565 +143,2.385 +144,2.360 +145,2.768 +146,3.172 +147,2.754 +148,2.758 +149,2.474 +150,2.995 +151,2.903 +152,3.398 +153,2.484 +154,2.894 +155,3.022 +156,2.413 +157,3.517 +158,3.370 +159,2.854 +160,2.485 +161,3.058 +162,2.473 +163,3.390 +164,3.023 +165,2.543 +166,2.629 +167,2.929 +168,2.928 +169,3.268 +170,2.622 +171,2.764 +172,2.920 +173,2.681 +174,2.551 +175,3.492 +176,2.534 +177,2.741 +178,2.949 +179,2.554 +180,2.596 +181,3.522 +182,3.207 +183,2.722 +184,2.945 +185,2.474 +186,2.934 +187,3.447 +188,2.515 +189,2.317 +190,3.059 +191,2.542 +192,3.417 +193,2.737 +194,2.456 +195,3.025 +196,2.640 +197,2.601 +198,3.567 +199,2.350 +200,2.581 +201,3.079 +202,2.514 +203,2.462 +204,3.725 +205,3.730 +206,2.670 +207,2.308 +208,2.689 +209,2.565 +210,3.479 +211,2.604 +212,3.339 +213,2.373 +214,2.678 +215,3.340 +216,2.867 +217,2.751 +218,2.747 +219,2.410 +220,2.485 +221,2.594 +222,3.602 +223,2.689 +224,2.565 +225,2.397 +226,2.562 +227,2.598 +228,3.403 +229,3.303 +230,2.775 +231,2.101 +232,2.499 +233,2.991 +234,3.059 +235,3.210 +236,2.568 +237,2.066 +238,2.498 +239,2.572 +240,3.281 +241,2.676 +242,2.640 +243,2.867 +244,2.418 +245,2.493 +246,3.126 +247,2.845 +248,2.522 +249,2.459 +250,2.524 +251,2.466 +252,3.060 +253,3.848 +254,2.516 +255,2.460 +256,2.499 +257,2.427 +258,3.463 +259,2.761 +260,2.674 +261,2.450 +262,2.491 +263,2.543 +264,2.732 +265,3.679 +266,2.325 +267,2.529 +268,2.344 +269,2.534 +270,2.691 +271,3.176 +272,2.492 +273,2.721 +274,2.587 +275,2.594 +276,2.326 +277,3.434 +278,3.514 +279,2.415 +280,2.481 +281,2.478 +282,2.555 +283,3.303 +284,3.098 +285,2.451 +286,2.552 +287,2.413 +288,2.613 +289,3.194 +290,3.191 +291,2.338 +292,2.561 +293,2.785 +294,2.240 +295,3.324 +296,2.767 +297,2.589 +298,2.608 +299,2.432 +300,2.444 +301,2.968 +302,3.792 +303,2.590 +304,2.335 +305,2.520 +306,2.435 +307,3.613 +308,2.681 +309,2.692 +310,2.329 +311,2.580 +312,2.355 +313,3.084 +314,3.026 +315,2.551 +316,2.490 +317,2.481 +318,2.553 +319,2.619 +320,3.350 +321,2.885 +322,2.576 +323,2.456 +324,2.441 +325,3.223 +326,3.031 +327,3.055 +328,2.592 +329,2.499 +330,2.354 +331,2.573 +332,3.323 +333,3.357 +334,2.241 +335,2.319 +336,2.472 +337,2.609 +338,3.565 +339,2.607 +340,2.539 +341,2.440 +342,2.483 +343,2.626 +344,3.407 +345,2.772 +346,2.491 +347,2.456 +348,2.553 +349,2.773 +350,3.239 +351,4.079 +352,2.346 +353,2.177 +354,2.562 +355,2.350 +356,3.329 +357,3.383 +358,2.441 +359,2.399 +360,2.530 +361,2.455 +362,3.219 +363,2.879 +364,2.726 +365,2.418 +366,2.511 +367,2.544 +368,3.077 +369,2.926 +370,2.571 +371,2.595 +372,2.534 +373,2.367 +374,2.671 +375,4.222 +376,2.411 +377,2.569 +378,2.493 +379,2.464 +380,2.691 +381,3.474 +382,2.697 +383,2.541 +384,2.494 +385,2.476 +386,2.473 +387,3.464 +388,3.084 +389,2.144 +390,2.503 +391,2.501 +392,2.525 +393,3.335 +394,2.919 +395,2.334 +396,2.557 +397,2.579 +398,2.567 +399,4.120 +400,3.297 +401,2.561 +402,2.428 +403,2.495 +404,2.729 +405,3.752 +406,2.783 +407,2.509 +408,2.337 +409,2.589 +410,2.556 +411,3.449 +412,2.702 +413,2.742 +414,2.384 +415,2.759 +416,2.290 +417,3.374 +418,2.934 +419,2.546 +420,2.228 +421,2.672 +422,2.369 +423,3.442 +424,3.484 +425,2.621 +426,2.279 +427,2.529 +428,2.889 +429,2.910 +430,2.981 +431,2.957 +432,2.420 +433,2.398 +434,2.310 +435,3.662 +436,2.422 +437,2.676 +438,2.557 +439,2.527 +440,2.564 +441,2.649 +442,3.168 +443,2.499 +444,2.737 +445,2.637 +446,2.509 +447,2.716 +448,3.845 +449,2.654 +450,2.536 +451,2.366 +452,2.526 +453,2.906 +454,3.101 +455,2.809 +456,2.466 +457,2.502 +458,2.767 +459,2.486 +460,3.137 +461,3.402 +462,2.187 +463,2.398 +464,2.392 +465,2.614 +466,3.371 +467,2.847 +468,2.463 +469,2.639 +470,2.288 +471,2.562 +472,3.381 +473,3.491 +474,2.521 +475,2.397 +476,2.431 +477,2.759 +478,3.222 +479,2.957 +480,2.470 +481,2.403 +482,2.560 +483,2.470 +484,3.451 +485,2.905 +486,2.583 +487,2.329 +488,2.798 +489,2.289 +490,2.882 +491,2.903 +492,2.627 +493,2.897 +494,2.321 +495,2.472 +496,2.882 +497,3.844 +498,3.096 +499,2.525 +500,2.250 +501,2.477 +502,2.728 +503,3.391 +504,2.539 +505,2.635 +506,2.542 +507,2.435 +508,3.011 +509,3.042 +510,2.965 +511,2.273 +512,2.433 +513,3.020 +514,2.776 +515,3.646 +516,2.856 +517,2.886 +518,2.522 +519,2.528 +520,4.396 +521,3.397 +522,2.218 +523,2.433 +524,2.605 +525,3.504 +526,3.087 +527,3.186 +528,2.343 +529,2.433 +530,2.900 +531,3.000 +532,3.511 +533,2.663 +534,2.699 +535,2.423 +536,3.136 +537,2.802 +538,3.151 +539,2.507 +540,2.973 +541,2.325 +542,3.322 +543,3.260 +544,3.525 +545,2.846 +546,2.125 +547,2.697 +548,2.829 +549,3.389 +550,2.758 +551,2.866 +552,2.338 +553,3.115 +554,2.500 +555,3.387 +556,2.851 +557,2.511 +558,2.459 +559,3.107 +560,2.521 +561,3.308 +562,2.782 +563,2.810 +564,2.323 +565,3.259 +566,2.366 +567,3.542 +568,2.929 +569,2.299 +570,2.336 +571,3.027 +572,2.540 +573,3.409 +574,2.784 +575,2.444 +576,2.437 +577,2.734 +578,2.591 +579,3.845 +580,3.212 +581,2.802 +582,2.370 +583,2.704 +584,2.624 +585,4.023 +586,2.360 +587,2.759 +588,2.511 +589,2.608 +590,2.922 +591,3.081 +592,3.271 +593,2.471 +594,2.516 +595,2.474 +596,2.814 +597,3.127 +598,2.537 +599,3.080 +600,2.341 +601,3.027 +602,2.766 +603,2.984 +604,2.450 +605,2.922 +606,2.481 +607,2.616 +608,2.541 +609,3.462 +610,2.971 +611,2.955 +612,2.258 +613,2.682 +614,2.770 +615,3.119 +616,2.792 +617,2.598 +618,2.260 +619,2.441 +620,2.536 +621,3.425 +622,2.958 +623,2.722 +624,2.099 +625,2.400 +626,2.570 +627,3.295 +628,2.919 +629,2.737 +630,2.311 +631,2.408 +632,2.514 +633,3.325 +634,2.767 +635,2.724 +636,2.288 +637,2.680 +638,2.347 +639,2.902 +640,3.058 +641,2.770 +642,2.522 +643,2.406 +644,2.539 +645,2.753 +646,3.215 +647,2.636 +648,2.639 +649,2.666 +650,2.392 +651,2.408 +652,3.515 +653,2.687 +654,2.589 +655,2.428 +656,2.375 +657,2.619 +658,3.308 +659,2.664 +660,2.752 +661,2.456 +662,2.365 +663,2.533 +664,3.354 +665,2.812 +666,2.684 +667,2.349 +668,2.441 +669,2.714 +670,2.769 +671,3.368 +672,2.624 +673,2.308 +674,2.365 +675,2.634 +676,2.617 +677,3.350 +678,2.733 +679,2.623 +680,2.462 +681,2.494 +682,2.460 +683,3.463 +684,2.537 +685,2.628 +686,2.674 +687,2.381 +688,2.516 +689,3.586 +690,2.681 +691,2.676 +692,2.289 +693,2.726 +694,2.303 +695,2.934 +696,3.339 +697,2.606 +698,2.187 +699,2.687 +700,2.328 +701,3.062 +702,2.930 +703,2.871 +704,2.313 +705,2.582 +706,2.464 +707,2.521 +708,3.304 +709,2.492 +710,2.723 +711,2.587 +712,2.608 +713,2.574 +714,4.002 +715,3.011 +716,2.227 +717,2.374 +718,2.343 +719,2.541 +720,3.580 +721,2.854 diff --git a/Docs/release-checks.md b/Docs/release-checks.md index 33df923..3da43d4 100644 --- a/Docs/release-checks.md +++ b/Docs/release-checks.md @@ -43,6 +43,7 @@ Measured at 1440p on the development workstation: Bazzite, Ryzen 7 7700X, 64 GB | `0.1.0` | Collision and packaging | PASS — no blocking collision, no invisible walls, no seam gaps (see audit note) | `-qaselftest` on the packaged build, 2026-11-08 | 2026-11-08 | | `0.1.0` | 60 FPS at 1440p | PASS — 2.42 ms avg (414 fps), p99 3.01 ms at 2560×1440, TSR on, VSync off, no FG | `-qaselftest` perf capture, `Docs/perf/0.1.0-walk-frametimes.csv` | 2026-11-08 | | `0.2.0` | 60 FPS at 1440p | PASS — 2.64 ms avg (379 fps), p50 2.54, p95 3.39, p99 3.76, max 4.05 ms at 2560×1440, TSR on, VSync off, no FG | `-qaselftest` perf capture, `Docs/perf/0.2.0-walk-frametimes.csv` | 2026-11-09 | +| `0.2.0` | Debug overlay cost | PASS — same route with the F3 overlay drawn: 2.77 ms avg (361 fps), p50 2.64, p95 3.52, p99 3.87, max 4.43 ms; about **5 %** heavier, still 6× the floor. The overlay is on for `-perf`/`-qaselftest`, off in the built demo | `Docs/perf/0.2.0-walk-frametimes-overlay.csv` | 2026-11-09 | | `0.1.0` | Fresh checkout build | PASS — clean clone of `main` (`4bc41e9`), `git lfs pull` + `check-lfs.sh` OK (130 LFS files), both `GigLineEditor` and `GigLine` targets built with the documented engine | `/tmp/fresh-check`, 2026-11-08 | 2026-11-08 | | `0.1.0` | Packaged Linux build | PASS — packaged Development build launches straight into DemoMap, no missing-asset/linker errors; controls shown in-game and in `Docs/controls.md` | measurement recorded 2026-11-08; the capture was removed from the repository 2026-11-09 | 2026-11-08 | diff --git a/Game/GigLine/Content/Input/IA_ToggleDebug.uasset b/Game/GigLine/Content/Input/IA_ToggleDebug.uasset new file mode 100644 index 0000000..9434686 --- /dev/null +++ b/Game/GigLine/Content/Input/IA_ToggleDebug.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5d91ef606471fb0343933205b261299b7350bff7986fa8492907716bb4f1f6c6 +size 1138 diff --git a/Game/GigLine/Content/Input/IMC_Default.uasset b/Game/GigLine/Content/Input/IMC_Default.uasset index 75081d9..828e913 100644 --- a/Game/GigLine/Content/Input/IMC_Default.uasset +++ b/Game/GigLine/Content/Input/IMC_Default.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:adcaeacf4d6d603999b517b89433a8b3186ec5b1bb0af9f18ee866d12eb9a983 -size 11853 +oid sha256:8a8c0dd390c6ea6da03770f9ef2e58ba685c0a6d4444ff658e3419da78802363 +size 12616 diff --git a/Game/GigLine/Source/GigLine/GigLineCharacter.cpp b/Game/GigLine/Source/GigLine/GigLineCharacter.cpp index e21a47e..fa4e008 100644 --- a/Game/GigLine/Source/GigLine/GigLineCharacter.cpp +++ b/Game/GigLine/Source/GigLine/GigLineCharacter.cpp @@ -15,6 +15,7 @@ #include "Engine/World.h" #include "TimerManager.h" #include "Containers/Ticker.h" +#include "HAL/IConsoleManager.h" #include "Kismet/GameplayStatics.h" #include "Misc/CommandLine.h" #include "Misc/Parse.h" @@ -132,6 +133,8 @@ AGigLineCharacter::AGigLineCharacter() TEXT("/Game/Input/IA_Jump.IA_Jump")); static ConstructorHelpers::FObjectFinder ResetF( TEXT("/Game/Input/IA_Reset.IA_Reset")); + static ConstructorHelpers::FObjectFinder DebugF( + TEXT("/Game/Input/IA_ToggleDebug.IA_ToggleDebug")); static ConstructorHelpers::FObjectFinder CamF( TEXT("/Game/Input/IA_ToggleCamera.IA_ToggleCamera")); static ConstructorHelpers::FObjectFinder PauseF( @@ -146,6 +149,7 @@ AGigLineCharacter::AGigLineCharacter() JumpAction = JumpF.Object; ResetAction = ResetF.Object; ToggleCameraAction = CamF.Object; + ToggleDebugAction = DebugF.Object; PauseAction = PauseF.Object; if (!MappingContext || !MoveAction || !MoveBackAction || !StrafeAction || !StrafeLeftAction @@ -257,6 +261,28 @@ void AGigLineCharacter::BeginPlay() // with `-thirdperson` so a capture can show the walk clip mid-stride: an idle // shot cannot prove the animation set, and a still cannot show a stride. bWalkForever = FParse::Param(FCommandLine::Get(), TEXT("walk")); + + // `-perf` starts the overlay on: the performance capture and any manual frame + // check should not depend on someone pressing F3 first. The built demo has + // neither switch, so the counter stays hidden for players. + if (FParse::Param(FCommandLine::Get(), TEXT("perf")) || FParse::Param(FCommandLine::Get(), TEXT("qaselftest"))) + { + if (IConsoleVariable* Var = IConsoleManager::Get().FindConsoleVariable(TEXT("gigline.Debug"))) + { + Var->Set(1, ECVF_SetByConsole); + } + } +} + +void AGigLineCharacter::ToggleDebugOverlay() +{ + static IConsoleVariable* Var = IConsoleManager::Get().FindConsoleVariable(TEXT("gigline.Debug")); + if (Var) + { + const bool bNowOn = Var->GetInt() == 0; + Var->Set(bNowOn ? 1 : 0, ECVF_SetByConsole); + UE_LOG(LogTemp, Log, TEXT("GigLine: debug overlay %s"), bNowOn ? TEXT("on") : TEXT("off")); + } } void AGigLineCharacter::SetReviewWalk(bool bEnable) @@ -658,6 +684,10 @@ void AGigLineCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputCo { Input->BindAction(ToggleCameraAction, ETriggerEvent::Started, this, &AGigLineCharacter::ToggleCamera); } + if (ToggleDebugAction) + { + Input->BindAction(ToggleDebugAction, ETriggerEvent::Started, this, &AGigLineCharacter::ToggleDebugOverlay); + } if (JumpAction) { Input->BindAction(JumpAction, ETriggerEvent::Started, this, &AGigLineCharacter::StartJump); diff --git a/Game/GigLine/Source/GigLine/GigLineCharacter.h b/Game/GigLine/Source/GigLine/GigLineCharacter.h index ffb5be7..f084b4f 100644 --- a/Game/GigLine/Source/GigLine/GigLineCharacter.h +++ b/Game/GigLine/Source/GigLine/GigLineCharacter.h @@ -42,6 +42,9 @@ protected: void TogglePause(); public: + /** True while the third-person rig is the active view (for the debug overlay). */ + bool IsThirdPerson() const { return bThirdPerson; } + /** Review harness: hold or release forward input from the screenshot actor. */ void SetReviewWalk(bool bEnable); @@ -172,6 +175,13 @@ protected: /** Applies the `-walk` forward input when the flag is set and the pawn is grounded. */ void WalkForever(); + /** Debug-overlay action (F3). */ + UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Input") + TObjectPtr ToggleDebugAction; + + /** Flips the gigline.Debug console variable that the HUD reads. */ + void ToggleDebugOverlay(); + /** Toggle-camera action (V). */ UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Input") TObjectPtr ToggleCameraAction; diff --git a/Game/GigLine/Source/GigLine/GigLineHUD.cpp b/Game/GigLine/Source/GigLine/GigLineHUD.cpp index f1c7593..b2d7c2b 100644 --- a/Game/GigLine/Source/GigLine/GigLineHUD.cpp +++ b/Game/GigLine/Source/GigLine/GigLineHUD.cpp @@ -2,11 +2,61 @@ #include "Engine/Canvas.h" #include "Engine/Engine.h" +#include "GameFramework/PlayerController.h" +#include "GigLineCharacter.h" +#include "HAL/IConsoleManager.h" +#include "Kismet/GameplayStatics.h" + +// Console variable so a packaged build can be checked without a rebuild: +// gigline.Debug 1 +// The HUD reads it every frame; the F3 binding toggles it. +static TAutoConsoleVariable CVarGigLineDebug( + TEXT("gigline.Debug"), + 0, + TEXT("Draw the performance and state overlay (0=off, 1=on). F3 toggles it."), + ECVF_Cheat); + +void AGigLineHUD::UpdateFrameStats(float DeltaSeconds) +{ + if (DeltaSeconds <= 0.0f) + { + return; + } + ++FramesDrawn; + const float Ms = DeltaSeconds * 1000.0f; + if (WarmupSeconds > 0.0f) + { + WarmupSeconds -= DeltaSeconds; + SmoothedFrameMs = FMath::Lerp(SmoothedFrameMs, Ms, 0.1f); + WorstFrameMs = Ms; + WorstWindowSeconds = 0.0f; + return; + } + // A 0.9/0.1 EMA settles in a few frames and still reacts to a stall. + SmoothedFrameMs = FMath::Lerp(SmoothedFrameMs, Ms, 0.1f); + WorstFrameMs = FMath::Max(WorstFrameMs, Ms); + // Rolling worst-frame window: a spike stays visible for two seconds, so a + // hitch is not missed between glances (and not averaged away). + WorstWindowSeconds += DeltaSeconds; + if (WorstWindowSeconds >= 2.0f) + { + WorstWindowSeconds = 0.0f; + WorstFrameMs = Ms; + } +} void AGigLineHUD::DrawHUD() { Super::DrawHUD(); + // Console variable is authoritative; the UPROPERTY is the per-instance default + // for tests that set it before BeginPlay. + if (IConsoleVariable* Var = IConsoleManager::Get().FindConsoleVariable(TEXT("gigline.Debug"))) + { + bShowDebug = Var->GetInt() != 0; + } + UpdateFrameStats(GetWorld() ? GetWorld()->GetDeltaSeconds() : 0.0f); + if (!Canvas || AttributionLine.IsEmpty()) { return; @@ -37,4 +87,35 @@ void AGigLineHUD::DrawHUD() DrawText(ControlsLine, Shadow, CX + 1.0f, CY + 1.0f, nullptr, TextScale * 0.82f); DrawText(ControlsLine, Ink, CX, CY, nullptr, TextScale * 0.82f); } + + if (!bShowDebug) + { + return; + } + + // Performance and state readout, top-left. Everything here is cheap enough to + // leave on during a capture: four formatted lines and no additional passes. + const APawn* Pawn = UGameplayStatics::GetPlayerPawn(this, 0); + const AGigLineCharacter* Character = Cast(Pawn); + const float Fps = SmoothedFrameMs > 0.0f ? 1000.0f / SmoothedFrameMs : 0.0f; + const FVector Where = Pawn ? Pawn->GetActorLocation() / 100.0f : FVector::ZeroVector; + const float Speed = Pawn ? Pawn->GetVelocity().Size2D() / 100.0f : 0.0f; + + const FString Header = FString::Printf(TEXT("%.0f FPS %.2f ms worst %.2f ms"), Fps, SmoothedFrameMs, WorstFrameMs); + const FString State = FString::Printf(TEXT("%s %.2f m/s frames %lld"), + Character && Character->IsThirdPerson() ? TEXT("third person") : TEXT("first person"), + Speed, FramesDrawn); + const FString Place = FString::Printf(TEXT("x %.1f y %.1f z %.1f m"), Where.X, Where.Y, Where.Z); + const FString Hint = TEXT("F3 hide"); + + const FLinearColor DebugInk(0.85f, 1.0f, 0.55f, 0.95f); + const FLinearColor DebugShadow(0.0f, 0.0f, 0.0f, 0.6f); + const float DX = MarginPx; + float DY = MarginPx; + for (const FString& Line : { Header, State, Place, Hint }) + { + DrawText(Line, DebugShadow, DX + 1.0f, DY + 1.0f, nullptr, TextScale * 0.9f); + DrawText(Line, DebugInk, DX, DY, nullptr, TextScale * 0.9f); + DY += 22.0f * TextScale * 0.9f; + } } diff --git a/Game/GigLine/Source/GigLine/GigLineHUD.h b/Game/GigLine/Source/GigLine/GigLineHUD.h index 491ac5e..a9c87ae 100644 --- a/Game/GigLine/Source/GigLine/GigLineHUD.h +++ b/Game/GigLine/Source/GigLine/GigLineHUD.h @@ -40,6 +40,19 @@ public: UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Attribution") FString LicenceLine = TEXT("Data \u00A9 OpenStreetMap contributors, ODbL 1.0 \u2014 openstreetmap.org/copyright"); + /** + * Performance and state readout, top-left, off by default. + * + * Toggle in game with **F3**, or start with it on by passing `-perf` (the + * performance capture does). The built demo ships with it hidden so a player + * does not see an FPS counter over Hollywood Boulevard. + */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Debug") + bool bShowDebug = false; + + /** Smoothed FPS and a rolling frame-time window, refreshed every frame. */ + void UpdateFrameStats(float DeltaSeconds); + /** Screen-space margin in pixels. */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Attribution") float MarginPx = 18.0f; @@ -47,4 +60,25 @@ public: /** Text scale relative to the default canvas font. */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Attribution") float TextScale = 1.45f; + +private: + /** Exponential moving average of frame time, in milliseconds. */ + float SmoothedFrameMs = 16.7f; + + /** Worst frame in the rolling window, so a hitch is visible without a log. */ + float WorstFrameMs = 0.0f; + + /** Seconds until the rolling worst-frame window resets. */ + float WorstWindowSeconds = 0.0f; + + /** Frames drawn, so the overlay can show that the counter is live. */ + int64 FramesDrawn = 0; + + /** + * Seconds left in the warm-up window. The first frames of a session include + * shader and streaming hitches of several hundred milliseconds; without this + * the "worst" figure read 400 ms for the whole first two seconds and made a + * healthy run look broken. + */ + float WarmupSeconds = 2.0f; }; diff --git a/Tools/ue_create_input_assets.py b/Tools/ue_create_input_assets.py index 03eb6fe..6b22eb0 100644 --- a/Tools/ue_create_input_assets.py +++ b/Tools/ue_create_input_assets.py @@ -127,6 +127,10 @@ def build(): ia_reset.set_editor_property("value_type", unreal.InputActionValueType.BOOLEAN) save(ia_reset) + ia_debug = fresh("IA_ToggleDebug", unreal.InputAction, unreal.InputAction_Factory) + ia_debug.set_editor_property("value_type", unreal.InputActionValueType.BOOLEAN) + save(ia_debug) + ia_cam = fresh("IA_ToggleCamera", unreal.InputAction, unreal.InputAction_Factory) ia_cam.set_editor_property("value_type", unreal.InputActionValueType.BOOLEAN) save(ia_cam) @@ -140,7 +144,7 @@ def build(): (ia_move, "Gamepad_LeftY"), (ia_strafe, "Gamepad_LeftX"), (ia_look, "MouseX"), (ia_look, "MouseY"), (ia_look, "Gamepad_Right2D"), (ia_jump, "SpaceBar"), (ia_jump, "Gamepad_FaceButton_Bottom"), - (ia_reset, "R"), (ia_pause, "Escape"), (ia_cam, "V"), + (ia_reset, "R"), (ia_pause, "Escape"), (ia_cam, "V"), (ia_debug, "F3"), ] mappings = []