- Add Recordingtest.EngineBridge library (IEngineSnapshot, HmEgSnapshot skeleton, MetadataLoader, CandidateFinder, CatalogWriter). - Add Recordingtest.EngineBridge.Probe console exe that dumps hmeg-types.json and hmeg-candidates.json to docs/engine-catalog. - Add Recordingtest.EngineBridge.Tests (xUnit, 6 tests). - Add probe design doc with plugin-masquerade recommendation. - Static analysis only; SUT is never executed. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
98 lines
5.4 KiB
Markdown
98 lines
5.4 KiB
Markdown
# engine-bridge Probe Design (PoC v2 blueprint)
|
|
|
|
Related: issue #9, contract `docs/contracts/engine-bridge.md`.
|
|
|
|
## Goal
|
|
|
|
Provide recordingtest with a reliable, low-latency read path into HmEG's live runtime state
|
|
(selected object IDs, camera state, scene summary, render-complete flag) while the SUT
|
|
(`EG-BIM Modeler`) is running, so golden-file scenarios can checkpoint internal state alongside
|
|
saved `.hmeg` files.
|
|
|
|
PoC v1 (this sprint) delivers static catalogs and a skeleton API. PoC v2 must actually get
|
|
into the process. The rest of this document surveys the options and picks one.
|
|
|
|
## Options
|
|
|
|
### 1. `AssemblyLoadContext` side-load in the recordingtest process
|
|
Load `HmEG.dll` into a secondary `AssemblyLoadContext` inside the recordingtest test runner.
|
|
|
|
- Pros: no native code, single process, trivial lifetime.
|
|
- Cons: **we would be reading our own copy of HmEG, not the SUT's**. No live camera, no live
|
|
selection. Any singleton the SUT maintains is unreachable.
|
|
- Verdict: **not viable** for read-live-state; listed for completeness so reviewers don't
|
|
propose it. Static metadata scanning (what PoC v1 already does) is the legitimate use of
|
|
this family of APIs.
|
|
|
|
### 2. CLR profiling API (ICorProfilerCallback)
|
|
Ship a native profiler DLL registered via `CORECLR_PROFILER*` env vars. The profiler attaches
|
|
at CLR startup, can `SetILFunctionBody` on HmEG methods, or hand managed code to a bootstrap
|
|
assembly that installs hooks.
|
|
|
|
- Pros: deepest reach, survives obfuscation, hooks JIT. Can intercept private methods.
|
|
- Cons: native DLL (C/C++), must match CLR bitness and version, requires SUT restart with
|
|
env vars set, complex failure modes, AV-sensitive.
|
|
- Verdict: **fallback** for a zero-cooperation scenario. Too heavy for first attempt.
|
|
|
|
### 3. In-process managed injector (e.g. `Ezez.CLRInjection`, `SharpMonoInjector` style)
|
|
`CreateRemoteThread` + `LoadLibrary` on a small native bootstrap that starts a CLR host and
|
|
runs our managed bridge DLL inside the SUT's AppDomain. Bridge uses ordinary reflection to
|
|
reach `HmEGAppManager` and exposes state via named pipe or loopback HTTP.
|
|
|
|
- Pros: attach at runtime, no SUT restart if the SUT is already running. Pure managed once
|
|
bootstrapped. Symmetrical with how profilers like dotTrace attach on demand.
|
|
- Cons: Windows-only, bitness must match (SUT is x64 WPF), AV alarms, fragile across .NET
|
|
runtime versions, process isolation privileges required. CoreCLR hosting from a foreign
|
|
thread is tricky compared to classic Framework.
|
|
- Verdict: viable but operationally painful. Use only if option 4 is blocked.
|
|
|
|
### 4. Plugin masquerade via SUT's existing MEF pipeline (RECOMMENDED)
|
|
The SUT already loads plugins from `EG-BIM Modeler/Plugins/` using MEF (per `CLAUDE.md` §1
|
|
and `docs/sut-catalog/plugins.md`). Drop `Recordingtest.EngineBridge.SutPlugin.dll` into that
|
|
folder. It exports the plugin contract the SUT expects, grabs a reference to
|
|
`HmEGAppManager` (or whatever the static catalog shows is the canonical accessor), and
|
|
publishes `IEngineSnapshot` over a localhost endpoint (named pipe `recordingtest-bridge` or
|
|
HTTP on 127.0.0.1).
|
|
|
|
- Pros: legitimate extension point, no native code, no process injection, no AV noise, CI
|
|
friendly, single cooperating restart. Honors CLAUDE.md §5.7 ("SUT 침습 최소화 — 별도
|
|
어셈블리로 격리"). Integrates with dispatcher marshaling by design — the plugin runs on
|
|
the WPF UI thread when called back.
|
|
- Cons: requires SUT restart once to load the plugin; depends on MEF contract remaining
|
|
stable; small IPC surface to keep deterministic.
|
|
- Verdict: **chosen path for PoC v2**.
|
|
|
|
### 5. AutomationPeer (comparison baseline)
|
|
Add a custom `UIA` AutomationPeer on the 3D viewport that exposes selection via the standard
|
|
`SelectionPattern` and camera via custom properties.
|
|
|
|
- Pros: standard, debuggable with `inspect.exe`.
|
|
- Cons: requires SUT source changes; cannot easily expose full scene graph or render-complete
|
|
without contortions; limited to what UIA patterns can model.
|
|
- Verdict: parallel track for exposing UI-shaped state. Not a replacement for the bridge.
|
|
|
|
## Render-complete signal
|
|
|
|
| Approach | Latency (expected) | Notes |
|
|
|---|---|---|
|
|
| Poll `IsDirty`-like property at ~16 ms | ~1 frame | simple, robust; costs a reflection call per tick |
|
|
| Subscribe to a `FrameRendered`/`Invalidated` event | event-driven, ~0 ms | best case; depends on such an event existing (the static catalog will tell us — look for `render` candidates with `MemberKind=Event`) |
|
|
| WPF `CompositionTarget.Rendering` from inside the plugin | ~1 frame | works even if HmEG has no event; UI-thread bound |
|
|
|
|
Recommendation: try event subscription first via the candidates catalog; fall back to
|
|
`CompositionTarget.Rendering` inside the MEF plugin because the plugin already runs on the
|
|
UI thread.
|
|
|
|
## Recommendation
|
|
|
|
PoC v2: **implement option 4 (plugin masquerade)** with a named-pipe JSON protocol that
|
|
produces the `IEngineSnapshot` DTOs defined in this library. Option 2 (CLR profiler) is the
|
|
documented fallback for a future "zero cooperation" scenario. Option 5 (AutomationPeer) is a
|
|
parallel concern that belongs to `sut-prober` and `recorder`, not to engine-bridge.
|
|
|
|
## Open questions (tracked, not blocking v1)
|
|
|
|
- Exact MEF contract the SUT expects (see `docs/sut-catalog/plugins.md` — resolved before v2)
|
|
- Whether HmEG exposes a `FrameRendered`-style event (answer will come from v1 catalog)
|
|
- Selection identity: object GUID vs. transient index — needs a stable key for golden diffs
|