standalone: VoxelForgeCore link-safe next to Unreal -- internals in vfcore::detail, symbol check

- The copied generator, mesher and Compat stand-ins compile inside vfcore::detail.
- CheckCoreSymbols.ps1 (run by Build.ps1) fails on any unqualified Unreal-style symbol in the
  library: 16,255 on 557f6aa, 0 now. A UE 5.7 CoreMinimal.h + VoxelForgeCore.h smoke compile passes.
- Output identical: 11 hash rows at W1/W8, routes 5642/5642, look/passage/far audits pass,
  bad-shell control fails, six captures byte-identical.
Cost, measured by me interleaved (8 workers, cold, 4 runs each, first run excluded): 0.963 s vs
0.937 s for 557f6aa, ~27 ms in shape compilation; same math, so codegen/inlining. To be found.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
This commit is contained in:
Fr0zkaandClaude Opus 5.5 committed 2026-10-05 15:20:29 +02:00
1 parent 557f6aac30
commit aeeedaa73d
11 files changed
+461 -87

No files matched your search

+103 -26
View File
@@ -23,9 +23,44 @@ changed.
tile to `World::generateTile`. Shape-world `--stream-benchmark`, `--stream-pipeline`, edit,
audit, and `--benchmark-level` runs use the Core world. Explicit evaluator and owner-comparison
modes retain the compatibility generator only as the comparison side of those diagnostics.
- The build manifest compiles the copied CPU generator and mesher with the private compatibility
headers under `Compat`. Those headers and their Unreal-shaped stand-ins are implementation
details. They are absent from the public include tree and public API.
- The build compiles the copied CPU generator and mesher with the private compatibility headers
under `Compat`. Those headers and their Unreal-shaped stand-ins are implementation details. They
are absent from the public include tree and public API.
## Implementation namespace and Unreal link safety
The public `VoxelForgeCore.h` surface contains only standard C++ declarations. The copied
generator, mesher, and compatibility implementation compile under `vfcore::detail`, so engine
symbols such as `FVector`, `TArray`, `FMath`, and `UObject`-shaped stand-ins do not acquire the same
linker names as Unreal's definitions. `Core/src/VoxelForgeCore.cpp` owns the public API boundary;
it opens `vfcore::detail` only around its private implementation includes, then defines the
`VoxelForgeCore` API outside that namespace.
For each non-boundary source in `Core/core_sources.txt`, `Build.ps1` writes a one-source shim under
`Build/VoxelForgeCore/wrappers`. The shim includes `CoreNamespacePreamble.h` before opening
`vfcore::detail`, then includes the canonical source path from the manifest. The preamble loads
the standard/platform headers and stateless JSON parser before namespace entry, preventing system
declarations from being accidentally nested in `vfcore::detail`. The `VoxelForgeCore.cpp` row is
compiled directly because it sets up its own public/private namespace boundary. Keep one
implementation source per shim.
`Compat` still defines Unreal-shaped types and a few Unreal-named macros for the copied code, but
those declarations live in private implementation translation units only. The UBT module must
disable unity builds and PCHs for these shims, and must not force-include Unreal headers into
them. This keeps compatibility macros local to one translation unit. Normal plugin adapter
translation units may include `CoreMinimal.h` and the public `VoxelForgeCore.h` together; the
public header does not include any compatibility header or Unreal declaration.
`Build.ps1` runs `Core/CheckCoreSymbols.ps1` on the completed static library. The check invokes
`dumpbin /SYMBOLS`, derives copied Unreal-style type names from `Compat`, `Source`, and `Core/src`,
and fails on an unqualified global occurrence. It also fails closed if `dumpbin` returns too little
symbol data to inspect. Rebuilding the `557f6aa` manifest directly and running the same checker
found 16,255 unqualified Unreal-style type symbols; the namespaced build checks 177,569
symbol-table lines against 370 type names with no violations. The baseline and current logs are
retained in `Core/verification/link_safety_557f6aa_final.log` and
`Core/verification/link_safety_final.log`. `Core/verification/UnrealHeaderCoexistence.cpp` was
also compiled against the installed Unreal 5.7 `CoreMinimal.h` and the public header; see
`Core/verification/unreal_header_smoke_final.log`.
## API and ownership
@@ -53,22 +88,46 @@ cancellation/version policy. Every client must apply the same sequence and brush
## Unreal integration: UBT module
Add a `VoxelForgeCore` runtime module to the plugin. Its `Build.cs` sets
`CppStandardVersion.Cpp20`, exports only `Standalone/Core/include`, and keeps these private include
roots available to the core implementation:
Add a `VoxelForgeCore` runtime module to the plugin and make the existing `VoxelForge` module depend
on it. Its `Build.cs` sets C++20, exports only `Standalone/Core/include`, and keeps the Standalone
root plus these private include roots available to the core implementation:
- `Standalone`
- `Standalone/Core/src`
- `Standalone/Compat`
- `Standalone/Source`
- `Standalone/Source/VoxelForge/Public`
- `Standalone/Source/VoxelForge/Private`
Set `PCHUsage = PCHUsageMode.NoPCHs` and `bUseUnity = false` for the Core module. For example:
```csharp
string PluginRoot = Path.GetFullPath(Path.Combine(ModuleDirectory, "../.."));
string Standalone = Path.Combine(PluginRoot, "Standalone");
PublicIncludePaths.Add(Path.Combine(Standalone, "Core", "include"));
PrivateIncludePaths.AddRange(new[] {
Standalone,
Path.Combine(Standalone, "Core", "src"),
Path.Combine(Standalone, "Compat"),
Path.Combine(Standalone, "Source"),
Path.Combine(Standalone, "Source", "VoxelForge", "Public"),
Path.Combine(Standalone, "Source", "VoxelForge", "Private")
});
CppStandard = CppStandardVersion.Cpp20;
PCHUsage = PCHUsageMode.NoPCHs;
bUseUnity = false;
```
UBT discovers translation units under a module's `Source` tree. Add one thin `.cpp` include shim per
row of `Core/core_sources.txt`, with each shim including exactly one canonical source file by a
relative path into `Standalone`. Keeping one included source per shim preserves the translation
unit boundaries used by `Build.ps1`. A manifest check in the UBT module should fail when a manifest
row lacks a shim or a shim names a different file. Do not copy implementation files into the
module. The standalone and UBT builds then compile the same source bytes.
path rooted at the private `Standalone` include path. For every implementation row except
`Core/src/VoxelForgeCore.cpp`, the shim includes `CoreNamespacePreamble.h`, opens
`namespace vfcore::detail`, then includes that one source. The special API row gets a shim that
includes `Core/src/VoxelForgeCore.cpp` directly; that source establishes its own namespace
boundary. Keeping one included source per shim preserves the translation-unit boundaries used by
`Build.ps1`. A manifest check in the UBT module should fail when a row lacks a shim, a shim names a
different file, or an extra shim exists. Do not copy implementation files into the module. The
standalone and UBT builds then compile the same canonical source bytes.
The existing `VoxelForge` module owns the adapter and calls the new module. Its adapter runs on the
game thread, resolves soft references and asset defaults, preserves array order, and serializes
@@ -113,22 +172,40 @@ creation, or rendering into the library.
## Acceptance record
The retained Round 11 executable is the reference at checkout `631dd57`. The final Core build
matched its 11 hash rows (full detail, whole view and LOD0–8) at both W1 and W8. It retained the
5,642/5,642 route audit, passed the shape/look and far-survival audits, and the damaged-shell control
still reports 512 removed triangles, 16 missing boundary surfaces, 16 holes, and exit 14. Six
fixed 1280×800 captures compare byte-for-byte with the Round 11 references. The final single-tile
The `557f6aa` commit is the strict output baseline. The rebuilt namespaced Core matches all 11
full-detail, whole-view and LOD0–8 hash rows at W1 and W8, with W1/W8 parity. The fresh records are
`Core/verification/last_standard_hash_w1.stdout.log`,
`Core/verification/last_standard_hash_w8.stdout.log`, and
`Core/verification/final_rebuilt_hash_parity.log`.
The look and shape audits pass, and walkability remains 5,642/5,642. The dedicated passage-camera
detail audit passes at 13,271 passage samples with a detail ratio of 1.152. Far survival passes
with 7,200 surfaces, zero missing surfaces or holes, and 10,976 far connectors. The damaged-shell
control removes 512 triangles, reports 16 missing boundary surfaces and 16 holes, and exits 14.
The final rebuilt logs are `Core/verification/final_rebuilt_shape_audit.stdout.log`,
`Core/verification/final_rebuilt_passage_camera_audit.stdout.log`,
`Core/verification/final_rebuilt_far_survival.stdout.log`, and
`Core/verification/final_rebuilt_bad_shell_control.stdout.log`.
The six fixed 1280×800 views were recaptured from the final rebuilt viewer and compare byte-for-byte
with the prior Core captures. Their images are `Core/verification/core_final_rebuild_*.png`; the SHA-256
results are in `Core/verification/final_rebuilt_capture_parity.log`. The single-tile
`--shape-world --benchmark-level=0` path reports `generator=VoxelForgeCore`.
The final no-hash, cold W8 direct full-payload benchmark was interleaved
reference/core/core/reference with `--stream-benchmark --shape-world --force-generate --workers=8
--clip-radius=3`. Process wall means were 1.041 s for the reference (1.035, 1.047) and 1.0245 s
for Core (1.037, 1.012), 16.5 ms faster. Each run generated 2,226 tiles with the full 110.90 MiB
payload and zero cache hits.
The link-safety check demonstrates the failure before the change and the pass after it. Running it
on the `557f6aa` library finds 16,255 unqualified Unreal-style symbols; the rebuilt Core library
passes after checking 177,569 `dumpbin /SYMBOLS` lines against 370 copied Unreal-style type names.
`Core/verification/link_safety_557f6aa_final.log` and
`Core/verification/link_safety_final.log` retain the outputs. A smoke translation unit that
includes the installed Unreal 5.7 `CoreMinimal.h` and the public `VoxelForgeCore.h` also compiles;
see `Core/verification/unreal_header_smoke_final.log`.
The separate neighborhood-first `--stream-pipeline` benchmark was repeated in eight interleaved
processes per build. Whole-view-ready means were 0.887 s for the reference (0.878–0.899 s) and
0.894 s for Core (0.880–0.932 s). The 7 ms mean difference is smaller than the 54 ms combined
run-to-run range; Core reached the 27-tile neighborhood and 960-tile full-detail milestones at
comparable times. Direct benchmark, pipeline, audit, and capture logs are retained in
`Core/verification`.
**Speed gate: not met.** The no-hash, cold W8 `--stream-pipeline --shape-world --force-generate`
check was run in four interleaved baseline/Core pairs, each generating 2,226 tiles and the full
110.90 MiB mesh payload with no cache hits. `whole_view_ready` averaged 0.89175 s for the exact
`557f6aa` executable and 0.92050 s for the namespaced build, a 28.8 ms (3.2%) regression. Stream
wall time was effectively unchanged at 0.46000 s and 0.46025 s; the difference was in shape
compilation, including `intervals+signs` averaging 95.7 ms on baseline and 114.9 ms on Core. The
raw cold runs are in `Core/verification/final_rebuilt_pipeline_*.stdout.log`. API-only `/Ob3` and
`/Ob1` trials also retained hash parity but were slower, so the build is restored to `/O2 /MP8`.
The required “no slower” acceptance condition remains outstanding.