Skip to content

refactor: split RenderFrame into per-section helpers#7

Merged
kryuchenko merged 1 commit into
mainfrom
refactor/split-render-frame
Apr 18, 2026
Merged

refactor: split RenderFrame into per-section helpers#7
kryuchenko merged 1 commit into
mainfrom
refactor/split-render-frame

Conversation

@kryuchenko
Copy link
Copy Markdown
Owner

Summary

Step 4 (final) of the cli_args_debugger.cpp decomposition. RenderFrame was the last function above the static-analysis threshold — CCN 21, 203 NLOC, 289 lines, touching 37 other class members.

Split into a ~20-line orchestrator plus ten private section methods:

helper responsibility
UpdateFrameTiming tick, FPS, rotation
RenderCube D3D11: clear / viewport / MVP / draw
RenderTextHud description + CLI args + command status
RenderLoadedDataPanel logs/saved-data panel (gated on show_logs_)
RenderPathsPanel path items (gated on show_paths_)
RenderInputPrompt exit prompt + user input
RenderQrBitmap QR code bitmap blit
RenderVolumeMeter L/R mic bars + device name (or no-mic fallback)
EndOverlay D2D EndDraw + device-lost recreate; returns false if recreated
PresentFrame FPS log + Present + device-removed recreate

No behaviour change intended

  • Every helper receives the same D2D1_SIZE_F and writes to the same render target, in the same order as before.
  • EndOverlay's bool return lets the orchestrator skip Present on device loss, mirroring the original early return in that path.
  • PresentFrame keeps the Wine/Proton VSync-off branch (blocking Present on Wine starves the message loop).

Numbers

  • Warnings in cli_args_debugger.cpp (CCN > 15 or length > 1000): was 2 (PollMicrophone=50, RenderFrame=21) → now 0. lizard reports clean.
  • Highest-CCN function in the file is now WindowProc at CCN 7.
  • File is ~a wash on line count — this is a structural split, not a net-line reduction.

Full series recap

step PR before after main file
1 #3 LogManager 2101 1964
2 #4 PathInfo 1964 1846
3 #6 AudioCapture 1846 1382
4 this PR 1382 ~1270

Test plan

  • build / windows-build — raw cl link with the split.
  • test — no existing unit test targets RenderFrame directly; the refactor is internal.
  • Memory Safety Tests (ASan Debug) — confirms nothing I moved introduces leaks.
  • Manual on a Windows host: run with a few args, toggle logs / path, Ctrl+C — frame still renders the cube, QR code, text panels, and mic meter in the same positions.

Step 4 (final) of the cli_args_debugger.cpp decomposition (steps 1-3
were #3, #4, #6). RenderFrame was the last function above the static
analysis threshold at CCN 21 / 203 NLOC / 289 lines and touching 37
other class members.

Split into an orchestrator plus ten private section methods:

  UpdateFrameTiming    - tick + FPS + rotation
  RenderCube           - D3D11: clear, viewport, MVP, draw
  RenderTextHud        - description, cli-args header, args, status
  RenderLoadedDataPanel - logs/saved-data panel (show_logs_)
  RenderPathsPanel     - path items (show_paths_)
  RenderInputPrompt    - exit prompt + user_input_
  RenderQrBitmap       - QR code blit
  RenderVolumeMeter    - mic level bars + device name (or no-mic text)
  EndOverlay           - D2D EndDraw + device-lost recreate
  PresentFrame         - FPS log + Present + device-removed recreate

RenderFrame itself is now ~20 lines of sequencing. No behaviour change
intended — each helper receives the same D2D1_SIZE_F and draws into the
same render target, in the same order as before. EndOverlay's bool
return lets the orchestrator skip Present if the D2D device was lost,
mirroring the original early `return`.

Numbers:
- Warnings (CCN > 15 or length > 1000) in cli_args_debugger.cpp:
  was 2 (PollMicrophone=50 already extracted, RenderFrame=21) → now 0.
- cli_args_debugger.cpp stays within a line of the previous total —
  structural split, not a net-line reduction.
- Highest-CCN function in the file drops to WindowProc at CCN 7.
@kryuchenko kryuchenko merged commit defab10 into main Apr 18, 2026
4 checks passed
@kryuchenko kryuchenko deleted the refactor/split-render-frame branch April 18, 2026 19:19
kryuchenko pushed a commit that referenced this pull request Apr 18, 2026
…ests

Splits the 2101-line monolithic cli_args_debugger.cpp into a thin window
class plus four composable modules, and fills the test gap along the way.
End state: cli_args_debugger.cpp down to ~1270 lines, zero functions over
the static-analysis CCN threshold, and 33 new unit tests covering the
extracted pure logic.

Modules extracted
-----------------

log_manager.{hpp,cpp}
  Owns g_log_file / g_logPath, InitLogger / Log / LogSEH / CloseLogger.
  Collapses the duplicated cleanup that was inlined in both branches of
  wWinMain. Keeps the extern globals so tests/logging_tests.cpp links
  unchanged.

path_info.{hpp,cpp}  (namespace path_info)
  Hosts ExecutablePath / CurrentWorkingDirectory / TempDirectory /
  WindowsDirectory / SystemDirectory / OsVersionString /
  WineOrProtonVersion / SaveFilePath / Collect. While moving the code:
    - MAX_PATH truncation fixed in five Win32 helpers via growing-retry
      / queried-size patterns.
    - C-style casts on GetProcAddress → reinterpret_cast.
    - exeDir = exeDir.substr(0, slash) → in-place erase.
    - Wrapped under a namespace so Windows.h macros like
      GetWindowsDirectory don't rewrite the function names.

audio_capture.{hpp,cpp}  (class AudioCapture)
  Pulls the entire WASAPI pipeline out of ArgumentDebuggerWindow: the
  11 audio data members, InitializeMicrophone, PollMicrophone (CCN 50,
  the worst function in the codebase), the capture thread entry, and
  the audio slice of OnDestroy/Cleanup. The seh_wrapper TU now casts
  its LPVOID straight to AudioCapture* and calls ThreadMain, so the
  main class no longer leaks into it. PollMicrophone is split into
  ResolveFormat + four per-format peak helpers + a dispatch; highest
  resulting CCN is 16.

RenderFrame split in place
  The CCN-21, 289-line RenderFrame is replaced by a ~20-line orchestrator
  plus ten private section methods (UpdateFrameTiming, RenderCube,
  RenderTextHud, RenderLoadedDataPanel, RenderPathsPanel,
  RenderInputPrompt, RenderQrBitmap, RenderVolumeMeter, EndOverlay,
  PresentFrame). No behaviour change — each helper draws the same
  region in the same order; EndOverlay's bool return mirrors the
  original device-lost early return.

Build system
------------
CMAKE_CXX_STANDARD 20 (required, extensions off) is set in both
CMakeLists.txt files, aligning the CMake/CTest builds with the
/std:c++20 flag already used by the raw-cl path in build.yml. Prior to
this, CMakeLists.txt left the standard unset (MSVC default ≈ C++14)
and tests/CMakeLists.txt pinned C++17, which is why
std::wstring::data() appeared to work for the main exe but broke
CTest compilation.

Tests
-----
tests/audio_peak_tests.cpp — 22 GoogleTest cases covering PeakFloat32,
PeakPcm16, PeakPcm24, PeakPcm32, PeakForFormat dispatch, ResolveFormat
for plain WAVEFORMATEX and WAVEFORMATEXTENSIBLE unwrapping. Includes
the 24-bit sign-extension edge case that used to be buried in
PollMicrophone, the "unsupported format degrades to 0" contract, and
the zero-channel-count fallback.

tests/path_info_tests.cpp — 11 smoke tests covering every path_info::
helper plus Collect(). Validates the growing-buffer / queried-size
patterns don't truncate, and pins Collect()'s label order so a future
accidental reorder surfaces as a test failure.

To keep these helpers testable without a live microphone, the
anonymous-namespace peak helpers in audio_capture.cpp are promoted
into audio_capture::detail.

Numbers
-------
                         before   after
cli_args_debugger.cpp    2101    ~1270   (-40 %)
functions with CCN > 15     2       0
average CCN (main file)   6.4     3.4
highest CCN (main file)    50       7   (WindowProc)
test suites                12      14

Squash of PRs #3, #4, #5, #6, #7, #8.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant