Skip to content

SDL3 Migration - #572

Open
yesid-bocanegra wants to merge 193 commits into
sven-n:mainfrom
yesid-bocanegra:main
Open

SDL3 Migration#572
yesid-bocanegra wants to merge 193 commits into
sven-n:mainfrom
yesid-bocanegra:main

Conversation

@yesid-bocanegra

@yesid-bocanegra yesid-bocanegra commented Aug 21, 2026

Copy link
Copy Markdown

Summary

Migrates the MuMain client toward a cross-platform SDL3 runtime while preserving existing Windows behavior and upstream gameplay changes.

The migration makes SDL3 and SDL GPU the primary platform/rendering path, replaces legacy audio with miniaudio, expands Linux/macOS support, strengthens the network bridge, and adds
broad regression coverage.

Key changes

  • Migrates windowing, input, text, lifecycle, and frame handling to SDL3.
  • Replaces the active OpenGL renderer path with SDL GPU compatibility and native shader pipelines.
  • Adds GPU skinning, frame statistics, screenshot readback, growable buffers, and rendering parity fixes.
  • Replaces DirectSound/SDL_mixer usage with miniaudio for music and sound effects.
  • Adds native Linux x64 and macOS arm64 build support while retaining Windows x86/x64.
  • Improves ClientLibrary packet binding, reconnection, stale-packet handling, and asynchronous outbound sends.
  • Ports upstream gameplay, UI, rendering, performance, and stability changes using preservation-first conflict resolution.
  • Adds CMake presets, CI workflows, platform checks, developer tooling, and 121 test files.
  • Removes tracked local IDE/agent state such as .idea and .superpowers.

Review scope

This is intentionally a repository-scale migration:

  • 792 changed files
  • 114 commits
  • 580 runtime/game source files
  • 121 test files

A large portion of the added lines comes from vendored miniaudio sources and generated shader artifacts.

Recommended review order:

  1. Build configuration and platform entry points
  2. SDL GPU renderer, shaders, and resource lifecycle
  3. Input, audio, networking, and shutdown behavior
  4. Gameplay and UI parity
  5. Tests, migration ledgers, and documented follow-ups

Detailed source-to-target accounting is available in:

  • docs/porting/cross-platform-sdl-ledger.md
  • docs/porting/upstream-sync-2026-08-20-ledger.md
  • docs/upstream-merge-features.md
  • docs/sdl-migration-runtime-checklist.md

Validation recorded

  • macOS arm64 Debug configure/build completed.
  • Repeated macOS launch and shutdown smoke tests completed without crash reports.
  • Pre-upstream-sync test build and CTest completed with 85/85 passing.
  • Focused regression coverage was added for rendering, input, audio, networking, platform portability, gameplay, UI, and stability.
  • Migration checkpoints include targeted CTest, shader validation, formatting, static-analysis, and Win32 guard audits.

Validation still required

  • Full current-head CTest run
  • Linux x64 build and runtime smoke test
  • Windows MSVC x86/x64 build and runtime smoke test
  • Windows MinGW validation, if still supported
  • Login, character selection, and main-world visual parity
  • Text input, IME, audio, fullscreen, and dense-item-drop testing

Known gaps

  • Item-glow parity still needs runtime verification.
  • Ground-item labels use an uncached fallback on SDL3; dense drop piles may reduce FPS.
  • Some legacy OpenGL link/include remnants and compatibility no-ops remain.
  • Print Screen JPEG capture and depth-pixel readback are not fully runtime-validated.
  • Several imported gameplay features remain marked for manual smoke testing.

Related work

Upstream capabilities and source PRs are mapped individually in the preservation and feature ledgers listed above.

## [1.1.4](v1.1.3...v1.1.4) (2026-08-27)

### Bug Fixes

* **d3d12:** read skinned bones from raw buffer ([07d0595](07d0595))
* **dotnet:** defer managed symbol resolution ([8167839](8167839))
@yesid-bocanegra

Copy link
Copy Markdown
Author

just FYI, the build now works, everything renders and the pipelines pass https://github.com/yesid-bocanegra/MuMain/actions/runs/33085798738

yesid-bocanegra and others added 4 commits August 27, 2026 16:59
Preserve translated NPC names and selected-monster health bars across platforms. Scale text, panels, scene viewports, overlays, and picked-item cursor coordinates consistently.
Archive POSIX runtimes on source runners to preserve executable bits. Link each client release to its exact content-addressed data bundle.
A four-entry statue array used the six-entry gate count, overwriting adjacent globals after the UI state changed binary layout.
## [1.1.5](v1.1.4...v1.1.5) (2026-08-27)

### Bug Fixes

* **release:** publish all runtime assets ([9808288](9808288))
* **ui:** bound Senatus array initialization ([12e53c8](12e53c8))
* **ui:** restore labels and responsive layouts ([c336a27](c336a27))
@nitoygo

nitoygo commented Aug 28, 2026

Copy link
Copy Markdown
Collaborator

Pulled this branch today and tried a native Windows MSVC build (windows-x64 preset, x64 Release) to check the D3D12 path end-to-end. Four issues found, plus a request. (I verified locally for 1 and 2)

1. Windows configure fails out of the box — vcpkg isn't wired in

vcpkg.json declares openssl, curl[ssl], directx-dxc, glslang[tools], and spirv-cross as required Windows MSVC dependencies, but nothing in the build actually uses vcpkg:

  • toolchain-x64.cmake has no VCPKG_ROOT/CMAKE_TOOLCHAIN_FILE reference.
  • CMakePresets.json's windows-base/windows-x64 presets only set toolchainFile to the project's own toolchain-x64.cmake.
  • docs/build/windows/console.md and docs/build/README.md list VS C++ tools + .NET 10 SDK as the only prerequisites — no vcpkg step at all.

Following those docs exactly, cmake --preset windows-x64 fails at src/CMakeLists.txt:153 with Could NOT find OpenSSL. It only configures once vcpkg is chain-loaded manually:

$env:VCPKG_ROOT = "<path-to-vcpkg>"
cmake --preset windows-x64 `
  "-DCMAKE_TOOLCHAIN_FILE=$env:VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake" `
  "-DVCPKG_CHAINLOAD_TOOLCHAIN_FILE=<repo>/toolchain-x64.cmake" `
  "-DVCPKG_TARGET_TRIPLET=x64-windows-static-md"

Suggest documenting this as a required prerequisite, or baking the chain-load into CMakePresets.json so cmake --preset windows-x64 works standalone like the docs imply.

2. Main fails to link with a static vcpkg triplet — the guard mentioned in the code comment doesn't exist

src/CMakeLists.txt:528-538:

if (MSVC)
  # ponytail: native CI uses dynamic vcpkg triplets; guard an empty runtime
  # DLL list if static triplets are added.
  add_custom_command(TARGET Main POST_BUILD
    COMMAND "${CMAKE_COMMAND}" -E copy_if_different
      $<TARGET_RUNTIME_DLLS:Main>
      "$<TARGET_FILE_DIR:Main>"
    COMMAND_EXPAND_LISTS
    COMMENT "Staging linked-target runtime DLLs"
    VERBATIM
  )
endif()

The comment names the exact risk but the guard was never written. With x64-windows-static-md (all-static, so $<TARGET_RUNTIME_DLLS:Main> is empty), COMMAND_EXPAND_LISTS drops the empty list entirely, leaving cmake -E copy_if_different "<dir>" with zero source arguments — invalid usage, CMake dumps its -E help text, and the link step fails every time with a static triplet. Fix that worked locally:

COMMAND "${CMAKE_COMMAND}" -E "$<IF:$<BOOL:$<TARGET_RUNTIME_DLLS:Main>>,copy_if_different,true>"
  $<TARGET_RUNTIME_DLLS:Main>
  "$<TARGET_FILE_DIR:Main>"

3. Widespread invalid-UTF-8 bytes across many headers

MSVC's /utf-8 flag throws hundreds of C4828: illegal character in codepage 65001 warnings spread across many unrelated headers — at least GameShop/ShopListManager/ShopPackageList.h, UI/NewUI/Quests/NewUIQuestProgress.h, UI/NewUI/Combat/NewUIDuelWatchMainFrameWindow.h, World/GameMaps/GM_Kanturu_2nd.h, World/GameMaps/GMAida.h, and World/GameMaps/GMCrywolf1st.h (likely more — stopped cataloging once the pattern was clear). All on line 1, pointing at mojibake carried through as raw bytes rather than a hand-written string — probably worth a systematic pass rather than a one-off fix.

D3D12 runtime: works cleanly once 1 and 2 are fixed

With $env:SDL_GPU_DRIVER = "direct3d12" and both fixes above applied, the client starts clean:

SDL_gpu -- device driver: direct3d12
SDL_gpu -- shaders loaded for driver: direct3d12
SDL_gpu -- Init complete

Process stays responsive, window opens, and there are zero rendering/GPU errors in the log afterward.

4. $vsync off console command crashes the app

Not sure if expected - flagging in case it's not already known. The code path: $vsync off (muConsoleDebug.cpp:140) → DisableVSync()MuRendererSDLGpu::SetVSyncEnabled(false)SDL_SetGPUSwapchainParameters(s_device, s_window, SDL_GPU_SWAPCHAINCOMPOSITION_SDR, SDL_GPU_PRESENTMODE_IMMEDIATE) (MuRendererSDLGpu.cpp:2374-2375). A plain SDL-level failure there is already handled gracefully (logged as a warning, function returns false without crashing), so if this is a real crash it's likely either an SDL/D3D12-backend issue with immediate present mode specifically, or something downstream once frame submission goes uncapped

5. Could you re-run preservation-first conflict resolution against current main?

main has moved, doing a fresh preservation-first pass now might save pain later. @yesid-bocanegra

(co-written by Claude)

@nitoygo

nitoygo commented Aug 29, 2026

Copy link
Copy Markdown
Collaborator

I tried testing it on my machine (Windows 11, Ryzen 5700X3D, AMD 5700XT):

  • d3d12 driver seems broken
    • the FPS does not follow cap when vsync is on
    • in release build, since the FPS is too high (not following vsync), some animation are broken
  • vulkan is more stable
    • FPS is capped when vsync is on in release build
    • framerate is still low on debug build (as expected)
      • maybe the best way to dev/test is using release with debug info build (unless there's a good reason why someone needs to test it on the slow debug build like tracing the code or stack)
  • general (not sure if present in both, but observed in vulkan)
    • shadows not rendered

Dug into why the debug build is slow even in vulkan (via Claude), and it's explainable from the implementation alone, not really a bug.

================

Details **The old GL/RHI renderer and this one aren't structurally comparable in Debug.** `RHI_GL.cpp` was a thin, mostly-direct wrapper: a draw call went through a small state-cache check and straight into a GL call. `MuRendererSDLGpu.cpp` does something different by design — it **records** every draw into a per-frame `std::vector s_renderCmds`, classifies whether each command can merge with the previous one, and only actually submits to the GPU in `EndFrame()`'s replay pass. That's intentional, not accidental — it's what makes cross-call batching (the SDL GPU equivalent of the old GLP-19 work) possible at all.

The catch is what's being pushed into that vector, every single draw call:

struct RenderCmd
{
    RenderCmdType type;
    SDL_GPUGraphicsPipeline* pipeline;
    SDL_GPUTexture* texture;
    SDL_GPUSampler* sampler;
    Uint32 vtxOffset, vtxCount, idxCount, stripIdxOffset;
    VertexUniforms vu;
    SkinningVertexUniforms skinningVu{};   // 192 bytes
    FogUniform fogUniform;                  // 48 bytes
    SDL_GPUViewport viewport;
    SDL_Rect scissor;
    BlendMode blendMode{};
    bool blendEnabled{}, depthTestEnabled{}, depthMaskEnabled{}, cullFaceEnabled{};
};

300+ bytes, value-copied into s_renderCmds at every one of its 14 call sites (sprites, particles, terrain tiles, BMD meshes, UI) — potentially thousands of times a frame in a busy scene. Every push, the merge-comparison pass afterward (ClassifyBatchBreak, CanMergeTriangleDraws/CanMergeQuadDraws), and the replay pass all run through std::vector's checked-iterator machinery under MSVC Debug (_ITERATOR_DEBUG_LEVEL=2) with none of /Ob0's inlining. The old renderer never built a struct this large per draw or kept a running history to compare against, so it never paid this cost to begin with.

The asymmetry that makes this specifically worse than the old renderer's own Debug penalty: Debug flags only punish our compiled code, never the driver's — the old renderer put more of its per-frame cost into driver-side calls (already-optimized, unaffected by our build config). This renderer deliberately moved more work into application-owned C++ specifically so it could cut redundant GPU-side calls in Release. Good trade once optimized; in Debug it means a larger fraction of total frame cost now runs at Debug speed instead of driver speed. And it scales with scene complexity — more draws means more pushes/comparisons/replay, all at Debug speed, which lines up with a frame-rate problem rather than a flat offset.

Not proposing a fix — the batching architecture is the right call for Release, and trimming RenderCmd or the merge logic to help Debug would either hurt Release's design or just chase a number nobody should be judging performance from anyway. Only suggesting a doc note, since docs/GPU Skinning/glperf/README.md already carries this kind of caveat for the GLP-29 entry (Debug-only win, no Release win): worth a similar line here so Debug captures on this renderer don't get mistaken for a real regression against the old one.

================

Add'l request:

  • Can we include the backend/driver used in the $details?

@yesid-bocanegra

Copy link
Copy Markdown
Author

thanks for testing this so thoroughly, I'll try to address these issues tomorrow or monday. I also made some changes that surfaced from patching a rendering issue for monster and npc names which led to reviewing the UI and font scaling.

semantic-release-bot and others added 9 commits August 30, 2026 11:17
## [1.2.0](v1.1.5...v1.2.0) (2026-08-30)

### Features

* **ui:** add responsive HUD layouts ([fe407e1](fe407e1))

### Bug Fixes

* **ui:** align scaled item interactions ([2bdd366](2bdd366))
* **ui:** block world hover behind panels ([1e0099b](1e0099b))
* **ui:** scale auxiliary game windows ([a1ef8f8](a1ef8f8))
* **ui:** stabilize character and NPC labels ([307a4a2](307a4a2))
…ion/upstream-2026-08-30

# Conflicts:
#	docs/GPU Skinning/README.md
#	src/source/Data/GameConfig/GameConfigConstants.h
#	src/source/Render/Core/RenderConfig.cpp
#	src/source/Render/Core/RenderConfig.h
#	src/source/Render/Effects/ZzzEffectParticle.cpp
#	src/source/Render/RHI/RHI_GL.cpp
#	src/source/Render/Terrain/ZzzLodTerrain.cpp
#	tests/CMakeLists.txt
## [1.2.1](v1.2.0...v1.2.1) (2026-08-30)

### Performance

* **render:** group particle draws by texture behind [Render] SortParticleDraws ([dccda7f](dccda7f))
* **render:** upload only changed terrain light rows, not the whole array ([58742b8](58742b8))

### Refactoring

* **render:** extract RenderParticle from RenderParticles' loop body ([0f243f8](0f243f8))
## [1.2.2](v1.2.1...v1.2.2) (2026-08-30)

### Bug Fixes

* **build:** enforce UTF-8 sources ([4dd34bf](4dd34bf))
* **build:** repair Windows presets ([427c88c](427c88c))
* **render:** restore projected shadows ([68e1975](68e1975))
@yesid-bocanegra

Copy link
Copy Markdown
Author

Review feedback update:

  • 1af2981d simplifies outbound flushing: integrates the flush loop, removes the extra sender abstraction, updates focused self-checks.
  • 427c88c1 repairs Windows presets: VCPKG_ROOT toolchain selection, architecture chainloading/triplets, empty runtime-DLL guard, docs/tests.
  • 4dd34bf1 enforces UTF-8 sources: 40 CP949 C/C++ files converted with 40/40 semantic round-trip verification, /utf-8 /we4828, strict regression coverage. Runtime shop-data files remain unchanged.
  • 68e19754 restores projected shadows by removing unsupported stencil state from BMD::RenderBodyShadow; stencil-volume paths remain separate.

Verification:

Caveat: Windows/D3D12 hardware runtime behavior was not manually exercised; runtime retesting remains required.

Deferred item 3 needs a dedicated immediate follow-up: safe frame-boundary VSync changes, MAILBOX then IMMEDIATE fallback, D3D12 software pacing, $details driver display, Debug-performance documentation.

@nitoygo

nitoygo commented Aug 31, 2026

Copy link
Copy Markdown
Collaborator

retesting the "broken fps cap via vsync when using direct3d12 GpuDriver" after the recent fixes:

image

The vsync is still not followed: expected is 144 only (my monitor is 144Hz).

@nitoygo
nitoygo marked this pull request as ready for review August 31, 2026 17:11
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.

4 participants