docs: plan retained glyph text renderer
This commit is contained in:
@@ -2763,6 +2763,14 @@ record with `gfx_object_bind_draw`. Native SC0000 records `ctx+0x14e9c = 50 ms`:
|
||||
at `0x834`, then publishes them one at a time before reaching wait `0x83c`. A click during reveal completes
|
||||
the remaining records and is consumed; the next click releases the stable wait.
|
||||
|
||||
The record layout is `{publication-chain flag, x, y, width, height}` as five dwords. The publisher binds
|
||||
handle `layout+0x68 + reveal_index` from source surface `layout_slot+0x14`, uses `(x,y,width,height)` as
|
||||
the source rectangle, and places it at `(layout_origin_x+x, layout_origin_y+y)`. A set first dword makes the
|
||||
service publish the following record in the same tick; otherwise the configured glyph delay controls the
|
||||
next publication. This gives the port a direct path to ordinary `GfxState` objects: rasterize the complete
|
||||
layout surface first, then materialize these exact bounded records in handle order rather than adding a
|
||||
second overlay compositor.
|
||||
|
||||
The port retains the SC0000-visible contract without exposing thousands of individual host glyph objects:
|
||||
surface strings remain associated with blank surface slots for later retained-object binding, while ADV
|
||||
lines retain cursor/origin, start time, visible-glyph count, and completion state. The compositor renders
|
||||
|
||||
@@ -33,7 +33,7 @@ or replaced before claiming portable exports.
|
||||
| AGE movie decode (`0x236` scene movies; `0x20f` modal LOGO/OP/ED; `0x24d` movie masks) | `FfmpegMovieDecoder` is the sole factory over the project-owned `native/age_movie_ffmpeg` ABI | Windows-x64 passes the complete 213-payload installed video/audio corpus gate plus audible LOGO/OP/CHAPTER playback and real TEST.AGF green-mask decode | Add target-specific native builds and export packaging |
|
||||
| Movie integration | Each surface owns a unique playback-instance id; `MovieRuntime` owns `IMovieDecoder` from an injected factory; video-only streams use monotonic pacing while audio-bearing streams use the Godot output clock. Opcode `0x24d` redirects decoded green bytes through the platform-neutral retained rasterizer and exact managed packed-alpha mask helper | Concurrent/restarted uses of one asset have independent frame/audio/completion/teardown state. Ordinary and mask movies share VFS/FFmpeg ownership; the mask result is a backend-neutral dynamic RGBA surface consumed by either Godot renderer. Managed code is no longer Windows-annotated, while only the win-x64 native bundle exists today | Add Linux/macOS native builds and smoke gates |
|
||||
| Movie audio | ABI v2 returns timestamped stereo float PCM; bounded managed buffering feeds a per-playback Godot `AudioStreamGenerator` and routes native movie flags to engine buses | All 29 installed audio-bearing streams decode with signal; synchronized LOGO/OP/CHAPTER playback is audibly accepted | Treat absent, distorted, or unsynchronized audio from an audio-bearing movie as a runtime bug |
|
||||
| ADV font discovery/raster fidelity | Opcode `0x1a5` reaches presentation; `godot/Main.cs` loads Windows `MS 明朝`/`MS ゴシック` from their known TTC files when available, otherwise uses the existing Japanese-font/default fallback. AGE actually uses a display information DC, weight-700 `LOGFONTA`, `GGO_GRAY4_BITMAP`, and its own integer glyph/outline compositor; the current `Label` backend substitutes FreeType embolden and a Godot outline | On the reference Windows install, regular advances and direct SC0000 placement/bounds agree, but the behaviorally different provisional bold path has 45% fewer bright pixels and much softer edge coverage. Other platforms normally lack the proprietary faces, so substitute metrics remain profile-dependent | Deferred until gameplay settles: implement the Phase-E decoded mask/metrics/compositor backlog in `docs/remake-architecture-and-roadmap.md`, with a shipped GDI reference backend on Windows and an explicitly defined portable rasterizer/substitution policy |
|
||||
| ADV font discovery/raster fidelity | Opcode `0x1a5` reaches presentation; `godot/Main.cs` loads Windows `MS 明朝`/`MS ゴシック` from their known TTC files when available, otherwise uses the existing Japanese-font/default fallback. AGE actually uses a display information DC, weight-700 `LOGFONTA`, `GGO_GRAY4_BITMAP`, and its own integer glyph/outline compositor; the current `Label` backend substitutes FreeType embolden and a Godot outline | On the reference Windows install, regular advances and direct SC0000 placement/bounds agree, but the behaviorally different provisional bold path has 45% fewer bright pixels and much softer edge coverage. Other platforms normally lack the proprietary faces, so substitute metrics remain profile-dependent | The Phase-E retained glyph-mask polish slice is now planned in `docs/remake-architecture-and-roadmap.md`: keep the GDI reference adapter outside the platform-neutral VM project, select it explicitly on Windows, and complete an explicitly non-identical portable rasterizer/substitution policy before removing the temporary Label fallback |
|
||||
| Filesystem semantics | Several filename and containment comparisons use `OrdinalIgnoreCase`; installed assets are conventionally uppercase | Needs validation on case-sensitive filesystems; may hide casing or containment mistakes | Add Linux/macOS tests with mixed-case synthetic roots and use filesystem-appropriate containment rules |
|
||||
| Save/profile/settings storage | `Sys4PersistencePaths` models AGE's independent `SAVEPATH` and `REGFILEPATH` resolutions. Godot replaces Himegari's related profile directory with `user://`, yielding `user://SAVE` for native S3SD/S4SD/S3RT files and thumbnails plus `user://SYS4REG.INI` for the BOM-less CP932 options file; the preserving writer changes only its nine `[sound]` keys | Save payloads and engine options are isolated together without changing either native format. Native/drop-in resolution remains available through `USEAPPDATAFOLDER` plus both SYS4INI paths. A single-root override is rejected when `SAVEPATH` is not beneath `REGFILEPATH`, preventing cross-profile guesses | Expose explicit profile/native selection through the future launcher and allow independent overrides for profiles whose two native paths are unrelated. Validate CP932 availability, replace/flush, case, permissions, and interrupted-write behavior on each export target |
|
||||
| Game-install and repository discovery | `GameRootSelection` accepts `--game-root`, then probes the executable directory and current working directory for `SYS4INI.BIN`; on Unix the frontend prefers inherited shell `PWD` because Godot may change the process directory during project startup. Godot injects the selected root into its catalog and loose-first ALF store. `Paths.cs` remains the development/test locator for generated opcode data, diagnostics, and CLI conveniences | Installed game data no longer depends on the workspace sibling layout, enabling an executable beside `AGE.EXE`, a terminal launch from the install, or a launcher-supplied absolute profile root. A packaged export still needs its generated runtime metadata bundled independently of repository discovery | Add export packaging for the opcode/profile artifacts, let the future profile launcher pass `--game-root`, and run executable-directory/CWD plus case/permission smoke gates on Linux and macOS |
|
||||
|
||||
@@ -522,14 +522,17 @@ requirements.
|
||||
- Enhancements the VM unlocks: higher/wide resolution, faster text, QoL, save-anywhere, new-content
|
||||
mods. Modding docs + tools. Save/UX polish.
|
||||
|
||||
#### Deferred backlog — AGE-exact glyph-mask text renderer
|
||||
#### Planned polish slice — AGE-exact retained glyph-mask text renderer
|
||||
|
||||
**Scheduling:** defer this effort until the natural gameplay spine, first dungeon loop, and combat/system
|
||||
presentation are settled. The current Godot `Label` backend is readable, respects the script-requested
|
||||
Mincho/Gothic family when available, and matches SC0000's placement and effective line width. Its remaining
|
||||
weight, hinting, and outline-pixel differences do not block gameplay work. Resume this backlog early only if
|
||||
the approximation causes a legibility, layout, input, or gameplay-presentation defect rather than a cosmetic
|
||||
native-parity difference.
|
||||
**Scheduling (2026-07-30):** resume this as the next bounded presentation-polish subsystem, while still
|
||||
allowing urgent playthrough blockers to preempt it. The original deferral gate has been met: the natural
|
||||
gameplay spine and first-dungeon loop are playable, numbered save/load is provisionally accepted, the shipped
|
||||
corpus has no remaining decoded effectful-opcode gaps, and representative system/menu presentation is live.
|
||||
More importantly, the detached `Label` representation now causes behavioral defects rather than only
|
||||
cosmetic native-parity differences: labels sit above the complete retained canvas, surface-drawn text does
|
||||
not inherit its object's alpha/tint, and modal/publication paths need explicit visibility and replacement
|
||||
exceptions. The current backend remains the migration fallback, not a target for further screenshot-driven
|
||||
font tuning.
|
||||
|
||||
**Behavioral source:** implement the native contract decoded in `docs/engine-re.md`; screenshots are
|
||||
integration/regression evidence, not tuning inputs. AGE selects an exact `LOGFONTA` into a
|
||||
@@ -557,17 +560,66 @@ primitive, which cannot be made equivalent by choosing another embolden constant
|
||||
through one cached glyph service. Retain the existing `Label` path as a temporary portable fallback until
|
||||
that integration is complete, then remove synthetic embolden/glyph-spacing constants from the exact path.
|
||||
|
||||
**Execution sequence:**
|
||||
|
||||
1. **Close the retained-record contract and pin baselines.** Before changing presentation, record the
|
||||
current semantic coordinates and native-backed checks for SC0000 voiced/unvoiced pages, STUDY/MAMES,
|
||||
HISTORY, BUNKI, and save/load restoration. Confirm the remaining implementation-sensitive native details:
|
||||
overflow/kinsoku behavior, object-range exhaustion, layout reset/republication, and restoration of live
|
||||
layout surfaces. The known 20-byte glyph record is `{publication-chain flag, x, y, width, height}`;
|
||||
publication binds `layout_first_handle + reveal_index` from surface `layout_slot + 0x14`, using `(x,y,w,h)`
|
||||
as the source rectangle and `layout_origin + (x,y)` as the destination.
|
||||
2. **Land the backend-neutral mask and compositor core.** Add request/result records and an
|
||||
`IGlyphMaskRasterizer`-style seam outside the VM. Keep CP932 byte identity available for native-compatible
|
||||
requests while permitting an explicit Unicode-capable portable policy for mods. Implement AGE's coverage
|
||||
conversion, modes 0–3, clipping, alpha/RGB integer blend, metrics, wrapping, final-cursor result, and
|
||||
bounded font/glyph caches in managed code. Synthetic masks, not OS fonts, are the unit-test oracle.
|
||||
3. **Add the independent Windows GDI reference backend.** Keep the P/Invoke adapter outside the
|
||||
platform-neutral `Age.Engine` project so the VM continues to call no OS APIs. Reproduce the decoded display
|
||||
IC, LOGFONT, measurement, and `GGO_GRAY4_BITMAP` request, and compare masks/`GLYPHMETRICS`/advances directly
|
||||
on Windows. Backend selection must be explicit and diagnostic; an unavailable exact backend falls back
|
||||
rather than silently claiming parity.
|
||||
4. **Move immediate surface strings first.** Make ops `0x204`/`0x205` rasterize directly into the numbered
|
||||
RGBA surface instead of appending `SurfaceTextDraw` metadata. Remove the separate surface-label projection
|
||||
only after GPU and software paths prove that ordinary handle order, object alpha/tint, affine transforms,
|
||||
source clipping, offscreen `0x222` captures, fills/copies, and backbuffer preservation all consume the same
|
||||
pixels. This is the smallest integration slice and directly fixes the reported surface-fade defect.
|
||||
5. **Materialize live ADV glyphs as ordinary retained objects.** Extend the transient layout-presentation
|
||||
binding with its source surface, first handle, and capacity without changing the persisted History record
|
||||
ABI. Build the complete line before reveal, update the canonical live cursor from measured metrics, then
|
||||
bind one normal `GfxState` object per revealed record. Click/Skip publishes the remaining records and keeps
|
||||
the existing consumed-click boundary. Layout reset erases its range and clears its surface; partial
|
||||
`0x1f7` erases work per handle naturally; `0x20a` republishes from retained records. Consecutive
|
||||
literal/dynamic runs advance by returned metrics rather than relying on Label concatenation.
|
||||
6. **Migrate History and the ADV wait indicator through the same retained path.** Op `0x1d1` rasterizes and
|
||||
binds into its target layout instead of creating a layout-keyed Label. Bind the configured wait atlas at
|
||||
the op-`0x212` handle so it follows native z-order and source-cell animation. Once SC0000, HISTORY,
|
||||
STUDY/MAMES, HIDEWIN, and BUNKI pass, delete the game-text Label pools and the modal/raw-callback visibility
|
||||
exceptions whose only purpose was compensating for detached overlays.
|
||||
7. **Complete and select the portable backend.** Spike the smallest maintainable way to obtain portable
|
||||
font masks/metrics (Godot/TextServer offscreen extraction versus a bounded FreeType-compatible adapter)
|
||||
before choosing a dependency. Define face substitution and raster policy as profile/configuration data,
|
||||
keep its output explicitly non-identical to GDI, and run the same layout/compositor tests. Retain a
|
||||
development-only backend switch through acceptance; remove the old Label fallback only when both exact
|
||||
Windows and portable startup/legibility gates pass.
|
||||
|
||||
**Acceptance gates:**
|
||||
|
||||
- Fixed synthetic-mask tests byte-match AGE's decoded 16-/32-bit compositor, including overlapping mode-3
|
||||
neighbors, clipping, alpha, and RGB integer rounding.
|
||||
- On Windows, representative CP932 glyph masks, `GLYPHMETRICS`, and advances match a direct invocation of
|
||||
the decoded GDI request; tests compare returned data, not screenshot histograms.
|
||||
- SC0000 and menu/history checks retain current placement, wrapping, line advance, reveal timing, colors,
|
||||
effects, and retained-object lifetime after the backend swap.
|
||||
- Surface-string tests prove text is occluded by later handles and inherits the bound object's alpha, tint,
|
||||
affine transform, source clipping, offscreen capture, and transition behavior in both render backends.
|
||||
- Retained-layout tests prove per-glyph handle order, capacity bounds, partial/full erase, reset/republication,
|
||||
final cursor, reveal/Skip click consumption, and save/load reconstruction without Label visibility shims.
|
||||
- SC0000, STUDY/MAMES, HISTORY, HIDEWIN, BUNKI, and save/load checks retain placement, wrapping, line advance,
|
||||
reveal timing, colors, effects, publication behavior, and native-backed lifetime after the backend swap.
|
||||
- The portable backend starts without Windows fonts or GDI, reports/uses its selected substitution policy,
|
||||
and passes layout/legibility tests without being labeled pixel-identical to native AGE.
|
||||
- Glyph/font caches remain bounded and reveal-time rendering does not introduce visible frame stalls.
|
||||
- Glyph/font caches and retained node/surface pools remain bounded. A complete line is rasterized once before
|
||||
reveal, so revealing another glyph does not rerasterize or upload the whole text surface and introduces no
|
||||
visible frame stalls.
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user