Skip to content

Repair PC build and test failures across platforms - #136

Merged
Adubbz merged 12 commits into
TheMoonPeople:masterfrom
carrdylan52:pr/ci-fixes
Oct 21, 2026
Merged

Adubbz merged 12 commits into
TheMoonPeople:masterfrom
carrdylan52:pr/ci-fixes

Conversation

@carrdylan52

Copy link
Copy Markdown
Contributor

Depends on TheMoonPeople/Kazarov#27, "Fix Tracy calibration and CI portability" (into Kazarov master). Merge that first.
The last commit, Kazarov: follow master with the CI fixes (dependency integration), pins tools/kazarov to the
fork SHA b93a533. Before this merges, it must be re-pointed at the SHA the Kazarov PR lands as, and the workflow
re-run. Without those Kazarov commits, Linux keeps two validation errors per GPU case and fails the arena death test,
and macOS fails to link.

Ready for review, but not for merge until Kazarov#27 lands: its current gitlink is a temporary fork commit. After Kazarov#27 merges into master, I will replace it with the actual landed SHA and rerun all four jobs. The five macOS rendering failures below remain open and blocking.

What the fork runs do and don't show. CI ran on carrdylan52/ChronicleTwo Actions, on hosted runners:

  • Run 39521736875 tested candidate 685a4c1 plus one URL-only verification commit, 457f049, so CI could fetch Kazarov b93a533 from the fork. Linux, Windows and Flatpak were green. Linux reported 1860 passed and 55 skipped (CTest total 1915); Windows reported 1608 passed and 45 skipped (CTest total 1653), with GPU tests excluded. macOS was red: 1836 passed, 59 skipped and exactly five failed (CTest total 1900). One disabled test per platform is outside those totals. Arena, both split catalog cases and all eight Crash.* cases passed. After the blocking test failure, macOS skipped the report-only group, Rust tests, build/symbol packaging and uploads, and the dSYM check. None of those skipped steps is validated for this dependency pair.
  • The earlier run 39514684237 had green job conclusions, but used an older version of the fixes on Kazarov de810e2, a relaxed arena matcher, and five additional macOS exclusions. Its blocking macOS step reported zero failures out of 1891; its nonblocking group had 9 failures out of 19. Its later Rust, packaging and dSYM steps passed. It supports the individual fixes, not the current dependency pair or a fully passing GPU suite.
  • Windows CI still excludes GPU cases (-LE gpu).
  • The macOS GPU results come from the runner's Apple Paravirtual device; nothing here was run on Apple hardware.

All 70 recorded master runs of PC builds failed. Each job failed for several separate reasons stacked on each other,
and fixing one exposed the next. Each commit below names its failure.

Windows (flaky, a different case each run)

  • Give each debug and monster memory test process its own config.json. config.json sits beside the save root.
    DebugFixture and MonsterMemory used %TEMP%\dc2_*_<pid> as the save root, so every process wrote
    %TEMP%\config.json (and its .tmp). Two replacing it under ctest -j fail on Windows, which caused the
    GamePadSingleDebug, GamePadDebugChord and MonsterMemory/Off failures. Locally it reproduced in 10 of 10 -j16
    rounds before the fix and 0 of 30 after; the new SetUp assertion fails 11 of 11 against the old layout.
  • Zero the test town's part extents. Town::Define left area3_box uninitialised and the fence test set only x,
    so GeoramaParts.PaintedFencesMatchRetailRecursion read heap garbage. It failed in about 1 of 5 fresh processes
    before the fix and 0 of 300 after.
  • Give the test discovery a minute. gtest_discover_tests once timed out at its 5 s default before any case ran.

Linux (254–257 failures)

  • Kazarov dependency (see the top): the Tracy calibration pool removes VUID-vkBeginCommandBuffer-commandBuffer-00050
    from every renderer start, the cause of 246 failures.
  • Run every death test in a re-run child. A forked child of the multithreaded test binary hangs in exit()
    joining Tracy's worker threads, which caused 5 timeouts. This needs the Kazarov arena test fix.
  • Keep the menu hand's clamp valid before the screen size is set. std::clamp(lo > hi) in MovePointer
    aborted 2 cases under libstdc++ assertions. It reproduces on Windows with libc++ _LIBCPP_HARDENING_MODE_EXTENSIVE.
  • Give lavapipe's slow GPU cases three minutes. The fog cases took 67.93 s and 127.84 s on the runner.
    CT_GPU_SLOW_TIMEOUT is now settable; ordinary cases keep 60 s.
  • Regenerate docs/port/truncations.md: after Port: PC settings, skip intro, perf tools, and expanded dungeon cap Performance Boost #59, CEditMap::SaveData and LoadData are replaced.

Flatpak

  • Hand the build Luau's source. flatpak-builder configures offline, and luau.cmake's FetchContent failed to
    resolve codeload.github.com. Luau is now a manifest archive source, like the other FetchContent dependencies.

macOS (never reached its tests since 14 Oct)

  • Build against one SDK (CMAKE_OSX_SYSROOT=$(xcrun --show-sdk-path)). Iconv was found in Xcode's SDK while
    Homebrew clang used the Command Line Tools SDK, and the resulting -isystem broke libc++'s <cstddef>.
  • Compare GetChrFileSize's estimate as a size_t. On macOS, size_t is not uint64_t.
  • Kazarov dependency: the Rust link-directory fix, for the CLT 27.0 .tbd error.
  • Start a macOS crash trace at the faulting function. The unwinder leaves _sigtramp through lr, so a faulting
    leaf function was missing from every report, which failed Crash.segv and Crash.segv-frame.

Dependency integration (last commit)

  • tools/kazarov moves from de810e2, PR Make menu on dungeons can lead to a game crash #26's head, which is on no Kazarov branch, to Kazarov master plus the PR.
    That also brings master's Luau-only script publication and its audio and model changes. ChronicleTwo still calls the
    Retain-policy entry point, so publication behaviour is unchanged.
  • .gitmodules branch changes from main to master.
  • Kazarov's PNG loader now decodes RGB (colour type 2) PNGs to RGBA. MgTexture.CatalogRejectsRgbPngAndLegacyPaletteMetadata
    is split into CatalogDecodesRgbPngAsOpaqueRgba, which checks exact bytes, dimensions and alpha 255 for nonuniform
    RGB, and CatalogRejectsLegacyPaletteMetadata.

Open follow-up: five macOS GPU cases (not quarantined)

These fail on the hosted runner's Apple Paravirtual device and pass on lavapipe (Linux CI) and on a local NVIDIA GPU.
Whether the defect is in our Vulkan use, MoltenVK or the virtual device is not established, and they have not been run
on Apple hardware:

  • MglibAspect.MenuFormsKeepMessageCursorAndTailWhole: mglib_aspect_test.cpp:736, green 38, expected > 200.
  • MglibAspect.MenuFormsParkPastHalfScaleUi and MenuFormsParkPastOneAndAHalfScaleUi: mglib_aspect_test.cpp:906-907
    "form clip leaked to a following draw" (R 200, B 100, expected > 240; form at 470, 296).
  • MglibAspect.MenuFormsParkVerticallyOnTallWindow: the same check (R 20, B 30; form at 100, 400).
  • TargetPresentation.WorldLifeGaugeInterpolatesPositionAndHealthAndReuseCuts: mglib_present_test.cpp:1132, the
    display frame differs from the canonical frame at interpolation 0.25, 0.5 and 0.75.

In run 39521736875 these five are the only failures. Merging as-is leaves macOS red on them, by design: making them report-only needs a maintainer decision, and Apple-hardware evidence or a found cause.

…json

config.json is written beside the save root. DebugFixture and the
MonsterMemory fixture set their save root to %TEMP%\dc2_*_<pid>, so every
test process using them wrote the same %TEMP%\config.json (and its .tmp).
Under ctest -j two of them replacing it at once fail on Windows, where an
open file cannot be renamed over, and ConfigChange returns false: the
GamePadSingleDebug, GamePadDebugChord and MonsterMemory/Off failures on the
Windows CI job, a different case each run.

The save root moves a level down, so config.json lands in the process's
own directory, and SetUp asserts that it did.
CEditPartsInfo::Initialize leaves area3_box for the part's collision data
to fill, as retail does. Town::Define never filled it, and
PaintedFencesMatchRetailRecursion sets only the x of each corner, so
GetFenceSide's y and z came from whatever the vector's heap memory held.
When that was large the fence ends lay further apart than
kFenceChainDistance and no chain formed ("expected.size() > 2, actual 1"):
about one fresh process in five locally, and Windows CI runs at random.
ctest lists darkchronicle_tests' cases before running them, and the listing
runs the binary's static initialisers, audio device probing included. On a
Windows runner it once took over gtest_discover_tests' 5 second default,
which fails the whole Test step before any case runs.
MovePointer clamps the hand to [left, right - 1] of the visible UI extent.
Without a renderer the extent falls back to mgScreenWidth x mgScreenHeight,
which is 0 x 0 until mglib sets the screen, so the upper bound fell below
the lower one. That is undefined for std::clamp, and the Linux Debug build's
libstdc++ assertion aborted MenuCursor.YesNoMouseTints... and
MgTextureGpu.MenuPointerUses... on every CI run; Windows and macOS build
Release libc++, which does not check it. A libc++ build with
_LIBCPP_HARDENING_MODE_EXTENSIVE reproduces both aborts on Windows.

An extent under one unit wide now pins the hand to its left or top edge.
Since Tracy joined the test binary, a static tracy::Profiler starts worker
threads before main. A fork-style death test's child has none of them, so
when the child leaves through exit() the profiler's destructor joins a
thread that does not exist and waits until ctest's 60 s limit:
DataReadDeathTest.LoadFileExitsWhenTheFileIsMissing, both
MgFramePositionDeathTest cases, PlatformFirstRun.MissingDataExits... and
SaveSlotTest.RecoveryUsesOnlyPublishedFilesInFreshProcess on Linux, after
gtest's "Death tests use fork() ... detected 5 threads" warning.

The threadsafe style, which MglibFrameDeathTest already used, re-runs the
binary for the child, as Windows always does, so it becomes the default.

Needs Kazarov's "Tests: give the unnamed arena overflow case a fixed
address": PlatformArenaBlock.OverflowOfAnUnnamedArenaGivesItsAddress
expected the parent's address in the child's message.
Every case in CT_GPU_SLOW_TESTS (GfxGpu*.* among them) compiles the
renderer's 840 pipelines, which takes llvmpipe about 17 s, and then renders
on the CPU. GfxGpuMeshDc2.FogIsContinuousAcrossSharedClippedEdge and
FogChangesContinuouslyThroughZeroDepthAndW intermittently passed the 60 s
limit on the hosted runner; with this change they passed there in 67.93 s
and 127.84 s. The limit was CT_TEST_TIMEOUT because CT_GPU_SLOW_TIMEOUT
could not be set on its own. It is now a cache variable, and the Linux job
sets it to 180; ordinary cases keep 60.
The JSON saves (TheMoonPeople#59) replaced both functions with PORT_OVERRIDE definitions,
so their two low-bits casts are replaced rather than open, and
Truncations.Check failed on the stale inventory. Regenerated with
scripts/port/truncations.py from the Linux CI build.
flatpak-builder configures chronicletwo without network access, so
luau.cmake's FetchContent download failed ("Could not resolve host:
codeload.github.com") on every Flatpak build since Luau landed. Luau is
now an archive source of the module, passed in through
FETCHCONTENT_SOURCE_DIR_LUAU like fastgltf, simdjson, cpptrace, Tracy and
libdwarf, with the hash luau.cmake already checks.
Homebrew's clang compiles against the Command Line Tools SDK, while
CMake's find modules search the SDK xcrun names (Xcode's). Iconv was found
there, and its include directory is not one of the compiler's implicit
ones, so every ct_port compile got
-isystem .../Xcode_26.6.app/.../MacOSX.sdk/usr/include ahead of libc++'s
wrapper headers and stopped at "<cstddef> tried including <stddef.h> but
didn't find libc++'s <stddef.h> header". CMAKE_OSX_SYSROOT names xcrun's
SDK for the compiler too.
EstimateCharacterMemory returns a std::size_t, which std::min was asked
to compare with a std::uint64_t. They are one type on Linux and Windows
but not on macOS (unsigned long and unsigned long long), where the call
matched no overload and ct_port stopped building.
The signal handler's unwind is trimmed to the frame at the faulting
instruction. macOS's unwinder leaves _sigtramp through the link register,
so that instruction is never among the frames: nothing was trimmed, and a
leaf function that faulted was missing from the report, which went from
__sigtramp straight to its caller. Crash.segv and Crash.segv-frame failed
on the first macOS CI run to reach them ("the symbolised trace has no
CrashTestSegv frame", with CrashTestFire after __sigtramp).

When the faulting instruction is not found, the trace now starts with it
in front of the link register's frame. Linux's unwinder steps through the
signal frame to the faulting instruction, so it is unaffected.
DEPENDENCY: tools/kazarov points at b93a533 on carrdylan52/Kazarov
pr/gfx-tracy-calibration-pool, Kazarov master 0781d5b plus the three
commits this series needs (the Tracy calibration pool, the fixed-address
arena overflow case for re-run death tests, the macOS Rust link
directory). Replace it with the SHA those commits land as on
TheMoonPeople/Kazarov master once that PR merges.

The pin moves from de810e2, PR TheMoonPeople#26's head, which is on no Kazarov branch,
to master, which carries PR TheMoonPeople#26 as 28281ce plus Luau-only script
publication, audio and model compatibility changes. ChronicleTwo keeps the
Retain publication policy (firstrun.cpp still calls the compatibility
entry point). .gitmodules now names master, the branch the pin follows.

Kazarov's PNG loader decodes colour type 2 (RGB) PNGs to RGBA as well as
type 6, for other games' converted data. MgTexture's catalog case that
required an RGB PNG to be rejected now checks that it decodes: nonuniform
RGB pixels come back with their exact bytes, 3 x 2, alpha 255. The legacy
palette metadata rejection is its own case.
@Adubbz
Adubbz merged commit 4249549 into TheMoonPeople:master Oct 21, 2026
6 of 7 checks passed
Adubbz pushed a commit that referenced this pull request Oct 21, 2026
#137)

#136 pinned b93a533, the head of Kazarov #27's branch. #27 was squash-merged
into Kazarov master as 96c2d95, which has the same tree, so nothing in the
build changes. The pin now names a commit on the branch .gitmodules follows,
rather than one only kept by the pull request.

Co-authored-by: carrdylan52 <17089587+carrdylan52@users.noreply.github.com>
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.

2 participants