Replies: 2 comments 1 reply
|
Platform Abstracting Layer: sounds like a great idea to me. I personally prefer using SDL as PAL in my projects (SDL2 covers most platforms, even Dreamcast AFAIK), but I understand (and have experienced it) that some platforms might need even lower level access to hardware/OS stuff. About renaming and restructuring files: I think I prefer keeping the original structure so it's easier to look back at the original code at any time, or older commits. But not sure how useful is that at this point 🤔 In summary… no strong opinion about this, but those are my 2c 😅 |
|
Update: the architecture docs from #293 feed directly into this roadmap. A batch of engine-axis docs just merged (#293) that overlap a few of the open questions here:
None of this changes the roadmap; just connective tissue for whoever picks up Phase 0. |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
This RFC proposes a phased roadmap toward hardware rendering and broader platform portability. Each phase is independently mergeable and has a payoff on its own — this is not a big-bang rewrite.
The immediate motivation is hardware rendering. Getting there cleanly requires two abstraction layers and a directory structure that don't exist yet. This RFC is about agreeing on the shape of those layers and the order of work before anyone moves files around.
Roadmap at a glance
Testing is a first-class concern from Phase 0, not a separate phase.
Phase 0 — Restructure
Move from the historical Adeline layout (
SOURCES/+LIB386/) to a modern tree, and normalise filename case while we're touching everything.Target tree
Host-only vs ASM-/render-equivalence tests are distinguished by CTest labels (
host_quickvsequiv), not directory layout —LBA2_BUILD_ASM_EQUIV_TESTS=OFFruns the host suite without Docker.Mapping table
SOURCES/*.cpp,*.h,*.asm(flat)src/engine/(still flat — subdivide later once coupling is mapped)SOURCES/3DEXT/src/engine/3dext/SOURCES/CONFIG/src/engine/config/SOURCES/CONSOLE/src/engine/console/SOURCES/WIN/packaging/(existing dir — Windows resources join the macOS/Linux packaging artefacts)LIB386/3D/src/lib/math/LIB386/AIL/src/lib/audio/LIB386/ANIM/src/lib/anim/LIB386/FILEIO/src/lib/fileio/LIB386/OBJECT/src/lib/object/LIB386/SYSTEM/src/lib/system/LIB386/MENU/src/lib/menu/(currently unbuilt — see note)LIB386/SMACKER/src/lib/video/(currently unbuilt — see note)LIB386/SNAPSHOT/src/lib/snapshot/LIB386/VIDEO_AUDIO_RESAMPLE/src/lib/audio_resample/LIB386/SVGA/src/render/software/svga/LIB386/pol_work/src/render/software/pol_work/LIB386/H/src/lib/include/(subdirs match code dir names)LIB386/libsmacker/src/lib/third_party/libsmacker/LIB386/AIL/SDL/stb_vorbis.{c,h}src/lib/third_party/stb_vorbis/(lifted out of the SDL backend — it's third-party, not SDL-specific)Dead-code discovery during dry run:
LIB386/MENU/andLIB386/SMACKER/aren't currently wired into the main build (no parentadd_subdirectory). MENU has tests, SMACKER doesn't. Decision needed: preserve, delete, or revive. Conservative default = preserve.SVGA + pol_work is a merge, not a move. Both have separate CMake targets (
libsvg.a,libpoly.a). Proposing to keep as two subdirs undersrc/render/software/{svga,pol_work}/rather than flatten into one — the concerns are distinct (raster blits vs polygon fillers).Case rule
Everything lowercase, with three carve-outs:
SDL3/system headers stay uppercase (<SDL3/SDL.h>). Upstream's install path.LBA2.CFG,LBA2.RC,LBA2.ICO,PALETTE.PAL,CLAVIER.TXT). Referenced by exact name in shipped game data + packaging.INITADEL.C→initadel.cpp(semantic upgrade). Unix convention says uppercase.Cis C++; the engine was already C++ by content. Becoming.cppis the right modernisation.Why lowercase: modern C++ convention (Boost, fmt, abseil, spdlog, Godot, bgfx — all lowercase). Uppercase is Adeline's 1996 MS-DOS / Watcom convention; we keep it in git history but move the live tree forward.
After 0c,
CONTRIBUTING.mdstates the rule for new files: lowercase basename, lowercase extensions, except for retail data filenames and external library paths (e.g.SDL3/).Staging — 3 commits
git mvdirectories only. Code dirs, header dirs, test dirs all lowercase. Update CMakeListsadd_subdirectory()paths.#includedirectives to match. Pure mechanical churn.INITADEL.C→.cpp. Lowercase generated-header templates (build_info.h.in,version_generated.h). Updatecmake/embed_lba2_cfg.cmakeinclude line. SDL3/ carve-out exceptions documented in CONTRIBUTING.All three SHAs added to
.git-blame-ignore-revssogit blameskips through the move and surfaces the original Adeline / community authorship.Dry-run status — verified
The dry run is on a public branch for inspection:
noctonca/lba2-classic-community @ dryrun/phase0-restructure (commit
7e19e73, not for merge)End-to-end verification on that branch:
The commit is the all-three-stages end state — when actually staged as three commits in the implementation PR, all three SHAs go into
.git-blame-ignore-revs.Testing infrastructure
The existing
tests/tree gets renamed alongsidesrc/in 0a. New unit tests forlib/math/andlib/fileio/are easy first additions — they have no platform or render dependencies. Render-equivalence scaffolding gets populated in Phase 2.CMake dependency rule (established here)
CMake enforces this at link time. Accidental upward dependencies fail in CI.
Phase 1 — Platform abstraction layer
Decouple the engine from direct SDL calls behind a thin interface. Builds directly on the scancode abstraction from #58 (virtual scancode mapping is essentially a micro-PAL for input).
Evidence the PAL boundary is real
lba2-dreamcastis a KallistiOS port at V0.9 alpha — boots, plays intro FMV, full first-island gameplay on Flycast. Files they had to replace to make a non-SDL port work:LIB386/SVGA/SDL.CPP→LIB386/SVGA/KOS.CPPLIB386/SYSTEM/*files → oneLIB386/SYSTEM/KOS_BACKEND.CPPLIB386/AIL/SDL/*→LIB386/AIL/KOS/*dc_sdl_compat.hshim fakingSDL_GetTicks,SDL_Delay,SDL_GetBasePathetc. so engine call sites inPERSO.CPP/PLAYACF.CPP/RES_DISCOVERY.CPP/DIRECTORIES.CPPcould keep their syntax.That shim is the Phase 1 PAL-shaped hole, made literal. A clean PAL reduces it to zero lines.
Proposed interface
The interface lives in
src/platform/platform.h. Each backend implements it in its own subdir (sdl/,headless/, …).Audio is not in the PAL. AIL owns the audio device directly (validated by the DC port replacing the AIL backend cleanly without engine changes). Keeping AIL as
src/lib/audio/and a separate audio interface is the right factoring.Backends
src/platform/sdl/— the existing SDL3 implementation, reorganised.src/platform/headless/— all stubs, monotonic clock. Enables headless CI and integration tests.src/platform/console/switch/— natural second target via devkitPro's SDL3 port.Runtime backend switching is supported by the struct pointer pattern. Useful for debug toggling and silent SDL_GPU → software fallback.
Testing — integration tier lands here
Headless boot + N ticks + assert-no-crash. Surfaces any engine code that bypasses the PAL. Scriptable input injection for deterministic replays:
Phase 2 — Render interface
The engine stops touching pixels or draw calls directly. The interface between engine and renderer needs to be picked, not just sketched.
The decision
The current engine isn't triangle submission. Exterior is a sort tree driving per-poly filler dispatch with material flags; interior is iso + z-buffer; output is paletted pixels. So "engine stops touching pixels" has three plausible boundaries:
Proposed: (a). Concrete evidence: the DC port runs the existing software renderer producing paletted output, then does a 565 blit at present time. They never touched
pol_workor the sort tree. The boundary that already works in shipping code is (a).The interface must remain bimodal
There is no GL on SH4, PSP, or most retro targets. The software renderer is a permanent backend, not a stepping stone. The RI accommodates two valid output modes:
A backend implements one or the other, not both.
Testing
First render-equivalence scenes captured here against the software backend. These become the golden reference all future hardware backends are validated against.
Phase 3 — SDL_GPU + Equivalence CI
Implement the RI's GPU mode against SDL3's SDL_GPU API. SDL_GPU translates to Vulkan on Linux, D3D12 on Windows, Metal on macOS — no per-platform graphics code required.
Equivalence CI
Lavapipe (software Vulkan, CPU) renders the GPU output in CI. Software renderer renders the reference. Perceptual-diff with tolerance:
Prerequisite — software-renderer determinism
The software renderer is not deterministic across platforms today.
LPROJ3DF.CPP/LROT3DF.CPPuselong double+lrintlto match x87fistprounding.long doubleis 80-bit on Linux x86_64, 64-bit on Windows MSYS2, 64-bit on macOS ARM64. There are platform-specific differences in screen coords and Z values today, and an open exterior draw-order bug on Linux tied to this."Software goldens" begs the question: captured on which platform? Perceptual-diff tolerance won't paper over a polygon drawn in the wrong place.
Decision needed before Phase 3 starts:
Testing pyramid
Forward-looking — shared library across LBA engines
The PAL contract proposed in Phase 1 is shape-compatible with
twin-e'splatform.handtimeco'ssystem.h. With small choices early — a C header (extern "C"linkage), stdint types, audio as a sibling contract, lowercase convention — the same interface could later live in a sharedadeline-commonconsumed by all three engines.This RFC does not commit to that extraction. It does suggest designing Phase 1 as if extraction were possible — the cost is near-zero and the option is valuable. A separate cross-repo discussion will work out scope and governance.
Open questions
.C→.cpp) acceptable?LIB386/MENU/andLIB386/SMACKER/: preserve, delete, or revive?All reactions