Commit graph

1327 commits

Author SHA1 Message Date
Erik
8b57ba7167 docs(render): correct Campaign V slice V2 texture-table ownership
The device flushes its texture table before draws it records, but at V2 the draws still go through raw GL in WbDrawDispatcher, so the device would never flush and the table would be stale on the GPU. V2 therefore keeps the handle table inside the existing texture caches; V4c deletes it once the dispatcher moves onto the encoder and the device table becomes reachable.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-27 15:20:34 +02:00
Erik
86afbe2ecd test(render): add the Campaign V offline pixel gate
Seven of the campaign slices (V2, V4a-V4g) are renderer ports whose entire
acceptance criterion is that no pixel changed, and the existing connected
lifecycle route needs both a live ACE server and the user watching. That would
have made the campaign advance only when someone is at the keyboard.

The client renders the world from the DATs without ACDREAM_LIVE, so the existing
UI automation probe can capture a settled frame with no session created and no
ACE state to disturb. The gate wraps that: capture at the parent commit, capture
at slice HEAD, compare through the existing compare-screenshots CLI at the
project's pinned tolerance 2 / 0.001.

Determinism was measured rather than assumed, and the first measurement failed:
two captures at the same commit differed in 0.29% of pixels. The differences
were confined to the top ~180 rows, which is correct behavior rather than a bug
- the sky animates and the Dereth clock advances with wall time, so two launches
cannot agree there. Below the horizon everything was stable. Masking the top 280
rows brings two independent same-commit pairs to 15 and 17 differing pixels out
of 563,200 compared, a ~33x margin under the threshold. Masking the animated
band keeps the rest a strict identity check; relaxing the tolerance instead
would have hidden real regressions everywhere else.

Covers terrain and blending, scenery, static meshes, water, fog, and the whole
retained UI. Does not cover sky (masked), EnvCell interiors, particles, or the
paperdoll viewports, since the offline scene is a fixed outdoor view - so V4e,
V4f and V4g keep a user visual gate on top of this one.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-27 15:19:43 +02:00
Erik
4f94ad7ddd feat(render): Campaign V slice V1 - OpenGL RHI backend (dark)
Implements GlGpuDevice and the rest of AcDream.App.Rendering.Gpu.Gl,
filling the V0-pinned IGpuDevice contract on OpenGL 4.3. This is the
first of the port slices described in
docs/plans/2026-07-27-vulkan-campaign.md: every later renderer port
(V2 onward) needs a real, driver-proven GL implementation of the RHI
to port onto, and the GL backend is deliberately built to be
behaviour-preserving rather than optimal, because that is what turns
each subsequent slice's pixel gate into a strict identity check
instead of a moving target. The Vulkan backend (V5+) is where the
actual efficiency gains land.

GlGpuDevice is a fresh root, not derived from Chorizite's
BaseGraphicsDevice/OpenGLGraphicsDevice - shedding that inheritance is
one of the things this campaign explicitly does. It owns its own
BindlessSupport instance rather than sharing the legacy WB render
path's, which is what lets it be constructed the moment a GL context
and a GpuFrameFlightController exist, with no dependency on when
WorldRenderCompositionPhase happens to detect bindless support later
in startup. The ring buffer keeps a managed staging array plus a real
GL buffer per flight slot and flushes with one BufferSubData
immediately before each Draw/DrawIndexed/MultiDrawIndexedIndirect
(never at bind time, since a renderer may still write after binding);
V1 throws on an over-capacity ring request rather than growing it,
since nothing consumes the device yet and a silent grow would hide a
future renderer's real working set. The texture table is a bump/free-
list allocator over a managed uvec2 handle array, gated through the
frame-flight retirement queue so a released slot cannot be reused
while a submitted frame might still read it. Push constants are
applied by uniform name on the currently-bound program, cached per
program, and explicitly re-applied whenever BindPipeline switches
programs - GL uniforms are per-program state, so the "survives
pipeline changes within a pass" guarantee the interface documents (a
freebie on Vulkan's shared pipeline layout) has to be emulated here.

BindlessSupport gained one additive method,
GetResidentHandle(texture, sampler), calling the same
ArbBindlessTexture.GetTextureSamplerHandle entry point
ManagedGLTextureArray already uses through a different path. The
existing GetResidentHandle(texture) cannot express
IGpuDevice.RegisterTexture's documented pair semantics ("the same
texture registered with two samplers occupies two slots"), so this
was the minimal change needed rather than a workaround.

The pure bookkeeping - ring watermark/alignment arithmetic, the
texture-slot allocator, render-state diffing, the push-constant field-
to-uniform-name table, and GL format mapping - lives in small GL-free
classes so it is unit-testable without a live context, following the
same seam pattern GpuFrameFlightController already uses for its fence
API. GlGpuTimerPool follows suit with an injectable timer-query API.

The device is constructed in HostInputCameraCompositionPhase
immediately after the frame-flight controller (the same phase that
already builds GpuFrameFlightController), rather than in
WorldRenderCompositionPhase as first considered: GlGpuDevice's self-
contained bindless detection means it has no ordering dependency on
the legacy WB path's BindlessSupport, so it can be proven against the
real driver as early as possible while keeping the composition change
to one phase. Composition, publication, and shutdown wiring follow
the existing acquire/publish/fault-injection pattern exactly, and GPU
device disposal is scheduled through the frame-flight retirement queue
before that queue itself is torn down. Nothing consumes the device
yet - that starts at V4a - so this slice's pixel gate is trivially a
tripwire.

App tests: 3834 passed / 3 skipped (V0 baseline 3785 + 49 new: ring,
texture-slot, render-state, push-constant, format-mapping, enum-
mapping, and timer-pool tests, plus one new fault-injection point in
the existing composition theory).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-27 15:11:04 +02:00
Erik
90f7c6f2f4 docs(render): drop the ambient-encoder relaxation from Campaign V
The transitional path for slices V4a-V4g needs no special API after all.
On GL, BeginPass binds the target framebuffer and applies load ops but
deliberately leaves viewport and scissor to the encoder, so a renderer
being ported mid-campaign opens a Load/Store pass against the backbuffer
and gets exactly today's behavior while the frame spine still owns clears.
One less contract concept, and one less thing for V4h to unwind.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-27 14:28:06 +02:00
Erik
621b16364b feat(render): Campaign V slice V0 — pin the Vulkan-shaped RHI contract
Campaign V migrates the renderer from OpenGL 4.3+extensions to a single
Vulkan 1.3 backend on Windows x64 and Linux x64, then deletes the GL path.
Motivation is compatibility and efficiency, not rescue: mandatory
GL_ARB_bindless_texture is the exact floor that parked Slice L (Mesa
D3D12/llvmpipe lack it) while Vulkan descriptor indexing is core, and
per-frame data can be written straight into mapped memory rather than
copied through BufferSubData.

V0 pins the contract every later slice codes against. Nothing consumes it
yet, so this commit changes no runtime behavior.

The seam is a minimal Vulkan-shaped RHI implemented FIRST on GL. That
ordering is the point: the twelve renderers then port one at a time under a
strict pixel gate on the still-shipping backend, so a divergence is
attributed to one slice instead of surfacing at a big-bang integration.
Duplicating renderers per backend was rejected because WbDrawDispatcher is
4,449 lines holding only ~62 GL call sites — the API surface is small and
the retail-fidelity CPU logic is large, and forking the latter is how subtle
regressions enter.

Contract highlights:
  - GpuBindingModel pins set/binding numbers dual-legal for GL and Vulkan
    GLSL. Storage bindings 0-8 keep today's shader numbering; UBOs move to
    their own set, which resolves the binding=1 collision GL only tolerates
    because it keeps SSBO and UBO tables separate.
  - GpuRingAllocation is a ref struct replacing every per-frame
    BufferSubData; the compiler forbids outliving the owning frame.
  - GpuTextureSlot replaces bindless handles. Unassigned is a loud
    uint.MaxValue sentinel rather than a silent resolve to slot 0 — the
    failure mode behind the magenta 1x1 UI placeholder bug. Renderers
    needing a fallback take the device's really-registered default slot.
  - Renderers always speak GL winding/viewport conventions; the Vulkan
    backend compensates with a negative viewport height in exactly one
    mapping function.

Verified while writing the plan: acdream's cameras already build
[0,1]-NDC projections (PortalProjection.cs:12-13), which is Vulkan's
convention. No projection rework is needed and depth precision improves,
at the cost of shifted z-fight patterns — the one pre-approved divergence
class, registered per instance at V7.

Gate: Release build green; App suite 3,785 passed / 3 skipped (3,763
baseline plus 22 new contract tests). Note for later slices, recorded in
the plan: run the suite in Release. LandblockBuildOriginTests'
far-strip test asserts behavior that LandblockStreamer.cs:505 deliberately
turns into a loud Debug.Assert in Debug builds, so a Debug run shows one
pre-existing failure that is not a regression.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-27 14:26:26 +02:00
Erik
f6275f4501 docs: reconcile project state and refresh README
Some checks failed
Headless portability / portable-headless (ubuntu-latest) (push) Has been cancelled
Headless portability / portable-headless (windows-latest) (push) Has been cancelled
Headless portability / linux-graphical (push) Has been cancelled
2026-07-27 12:53:31 +02:00
Erik
97a095d628 docs(linux): park Slice L at L1 checkpoint 2026-07-27 12:35:47 +02:00
Erik
07bb1c5a74 docs(linux): close Slice L0 2026-07-27 12:00:10 +02:00
Erik
1628d9f587 docs(headless): close modern runtime slice K 2026-07-27 11:31:19 +02:00
Erik
b2ab0956f2 docs(headless): record K4 resource envelope 2026-07-27 11:21:44 +02:00
Erik
93c6c54220 docs(headless): record K4 telemetry checkpoint 2026-07-27 10:50:32 +02:00
Erik
827a039760 docs(headless): close K3 connected gates 2026-07-27 10:28:53 +02:00
Erik
fd9559a063 docs(headless): record K3 isolation checkpoint 2026-07-27 09:28:47 +02:00
Erik
fbdb58a962 docs(headless): close modern runtime slice K2 2026-07-27 08:26:23 +02:00
Erik
b299e3738e docs: close modern runtime slice K1 2026-07-27 07:39:56 +02:00
Erik
fbebb91848 docs(headless): close K0 and activate K1
Pin the tested Windows/Linux portability boundary, exact rollback, dependency audit, and synchronized architecture and roadmap state before starting the production single-session host.

Co-authored-by: Codex <noreply@openai.com>
2026-07-27 07:05:45 +02:00
Erik
953c469cac docs(runtime): close Slice J and activate Slice K
Record the shared graphical/no-window reset architecture, deterministic lifecycle evidence, exact rollback point, and synchronized project guidance before beginning the Linux headless host.

Co-authored-by: Codex <noreply@openai.com>
2026-07-27 01:01:41 +02:00
Erik
7818494116 docs(runtime): close the accepted J7 visual gate 2026-07-27 00:12:26 +02:00
Erik
0843a41b85 docs(runtime): define the canonical J8 generation reset 2026-07-27 00:08:52 +02:00
Erik
456c72233e docs(reference): preserve local ACE command catalog 2026-07-27 00:03:58 +02:00
Erik
5b3fb17775 fix(vfx): classify hardwareless particle emitters once 2026-07-27 00:03:44 +02:00
Erik
921712f412 fix(interaction): restore retail loot placement and world-drop projection 2026-07-27 00:03:15 +02:00
Erik
4d095be286 fix(physics): traverse prepared indoor portal topology 2026-07-27 00:02:44 +02:00
Erik
b12d94047c docs(runtime): detail Linux graphical closeout
Pin the mandatory-driver matrix, portable platform services, Linux packaging and connected gates, and the evidence threshold for any GPU migration.

Co-authored-by: OpenAI Codex <codex@openai.com>
2026-07-26 19:37:07 +02:00
Erik
7d1f88da61 docs(runtime): detail Linux multi-session host
Fix the portable host, scheduler, credential, shared-content, parity, and two-hour 30-session stress contracts before Slice K implementation begins.

Co-authored-by: OpenAI Codex <codex@openai.com>
2026-07-26 19:35:33 +02:00
Erik
0a5bc68e70 docs(runtime): detail Slice J no-window closeout
Pin the single-root fixture host, full deterministic lifecycle, fault and isolation matrix, and J7 visual entry gate before implementation begins.

Co-authored-by: OpenAI Codex <codex@openai.com>
2026-07-26 19:33:21 +02:00
Erik
b8c90c1959 docs(runtime): record J7 root cutover gates
Capture the exact production rollback, complete automated evidence, connected route results, and the still-active physical-display acceptance without declaring J7 closed early.

Co-authored-by: OpenAI Codex <codex@openai.com>
2026-07-26 19:29:18 +02:00
Erik
ecb9f79444 docs(runtime): record game runtime root gate 2026-07-26 18:51:41 +02:00
Erik
96ddd16539 refactor(runtime): compose canonical game runtime root 2026-07-26 18:51:17 +02:00
Erik
75f9510e10 docs(runtime): close exact world host ownership 2026-07-26 18:41:24 +02:00
Erik
73d0b54e38 docs(runtime): record teleport correlation gate 2026-07-26 18:04:50 +02:00
Erik
6a063a27d4 refactor(runtime): own teleport destination correlation 2026-07-26 17:52:34 +02:00
Erik
38b3773cb9 docs(runtime): record reveal ownership gate 2026-07-26 17:27:15 +02:00
Erik
4ab98b080e docs(runtime): record world environment ownership 2026-07-26 16:52:54 +02:00
Erik
902076c0a4 refactor(runtime): own world environment state 2026-07-26 16:45:04 +02:00
Erik
b972f539f7 docs(runtime): plan world transit ownership 2026-07-26 16:35:46 +02:00
Erik
4743f7c409 docs(runtime): close simulation ownership 2026-07-26 16:29:28 +02:00
Erik
c30a3efeb0 docs(runtime): close projectile ownership 2026-07-26 15:27:50 +02:00
Erik
fce8e7b18e docs(runtime): close per-session physics ownership
Record J5.5 production SHA, complete Release baseline, exact-binary lifecycle/reconnect and nine-stop collision/movement evidence, and rollback. Synchronize architecture, roadmap, milestones, AGENTS/CLAUDE, and advance the active program to J5.6 projectile runtime.

Co-authored-by: Codex <noreply@openai.com>
2026-07-26 13:59:06 +02:00
Erik
7e6033d0ad refactor(runtime): own per-session physics simulation
Move the sole PhysicsEngine, production cache, collision admissions, canonical bodies and hosts, remote components, ordinary/remote worksets, simulation, cell commits, and shadow synchronization under RuntimeEntityObjectLifetime. Keep App as the prepared-asset, animation-input, and render-projection adapter while preserving the named-retail update and collision order.

Add exact-incarnation, object-clock, callback-reentrancy, GUID-reuse, two-runtime isolation, source ownership, collision publication, and graphical projection coverage. Release build and the complete 8,588-test solution pass.

Co-authored-by: Codex <noreply@openai.com>
2026-07-26 13:39:57 +02:00
Erik
0dc3bfdeff docs(runtime): close local movement ownership
Record the exact J5.4 automated, lifecycle/reconnect, and nine-stop movement evidence; advance the Modern Runtime program to J5.5; and synchronize architecture, roadmap, milestones, AGENTS/CLAUDE, rollback, and durable research pointers.

Co-authored-by: Codex <noreply@openai.com>
2026-07-26 12:53:41 +02:00
Erik
aa3f4a60f8 refactor(runtime): own local movement and outbound cadence
Move the canonical local movement controller, body/motion managers, object clock, movement wire data, and MTS/jump/AP sender into AcDream.Runtime. Replace process skill defaults with typed Runtime character options, make graphical and direct commands borrow one autorun owner, retain the construction-time PartArray seam, and include movement in terminal ownership convergence.

Preserve the accepted pre-inbound movement/jump and post-inbound autonomous-position order while moving the exact packet/cadence fixtures into Runtime tests. Add graphical/direct parity, two-instance isolation, teardown, allocation, architecture, and divergence-path coverage.

Co-authored-by: Codex <noreply@openai.com>
2026-07-26 12:33:53 +02:00
Erik
75b761e658 docs(runtime): close combat and magic intent
Record the exact J5.3 owner boundary, retail ordering, Release and connected evidence, rollback, and J5.4 handoff across architecture, roadmap, milestones, agent instructions, and durable memory.

Co-authored-by: Codex <noreply@openai.com>
2026-07-26 12:09:19 +02:00
Erik
20df9d155d refactor(runtime): own combat and magic intent
Move attack build/repeat state, combat-mode policy, authoritative auto-target transitions, and spell-cast intent beneath RuntimeActionState. Keep App as the input, world-query, DAT-policy, transport, and presentation adapter while preserving retail request and busy ordering. Add direct/graphical parity, reset, failure, and instance-isolation coverage.

Co-authored-by: Codex <noreply@openai.com>
2026-07-26 11:56:40 +02:00
Erik
81b31857c6 docs(runtime): close interaction transaction ownership
Record the exact J5.2 owner boundary, retail ordering, Release and connected evidence, rollback, and J5.3 handoff across architecture, roadmap, milestones, agent instructions, and durable memory.

Co-authored-by: Codex <noreply@openai.com>
2026-07-26 11:22:07 +02:00
Erik
be73bccf5a docs(runtime): close canonical action ownership
Record J5.1's exact Runtime selection, combat, and target-mode owner, its automated and connected acceptance evidence, rollback, and the J5.2 interaction-transaction boundary. Synchronize architecture, roadmap, milestones, issues, and agent handoff state.

Co-authored-by: Codex <codex@openai.com>
2026-07-26 10:54:12 +02:00
Erik
bb45afef33 docs(runtime): plan simulation ownership
Inventory the present selection, interaction, combat, movement, physics, and projectile owners; pin named-retail ordering; and define independently reversible J5.1-J5.7 gates without moving presentation into Runtime.

Co-authored-by: Codex <noreply@openai.com>
2026-07-26 10:25:46 +02:00
Erik
2c67a2c3ba docs(runtime): close gameplay-state ownership
Record J4.5 canonical shortcut and spellbook command ownership, combined failure and graphical/headless parity evidence, the 8,544-test Release gate, exact-binary lifecycle and nine-stop acceptance, rollback instructions, and the J5 boundary.

Co-authored-by: Codex <noreply@openai.com>
2026-07-26 10:17:39 +02:00
Erik
89e6b207f8 refactor(runtime): close canonical gameplay ownership
Unify the toolbar shortcut manager with Runtime inventory state, route retail-ordered shortcut and spellbook command effects through the canonical owners, and make retained controllers borrow those exact instances. Remove the item-interaction transaction fallback and add graphical/no-window parity plus failure-safe terminal ownership-ledger coverage.

Co-authored-by: Codex <codex@openai.com>
2026-07-26 09:48:51 +02:00
Erik
ce6fae7b38 docs(runtime): close character projections
Record J4.4's canonical character settings, borrowed gameplay views, typed state-command seam, complete Release evidence, exact-binary lifecycle/reconnect acceptance, rollback, and the J4.5 combined closeout boundary. Synchronize architecture, roadmap, milestones, agent guidance, and durable memory.

Co-authored-by: Codex <codex@openai.com>
2026-07-26 09:21:26 +02:00