diff options
| author | Gabriel Schneider <[email protected]> | 2026-09-16 13:20:29 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-10-01 00:12:14 -0300 |
| commit | 7cbd44dd1cfe97126e7fc996214e458525d2980c (patch) | |
| tree | eeb02eabaf39231dece68647129040e546aa80a2 /docs/rendering-parity-design.md | |
| parent | 3042f33df2b16b61c08d6535fba268103728dd1e (diff) | |
| download | pardes-7cbd44dd1cfe97126e7fc996214e458525d2980c.tar.gz pardes-7cbd44dd1cfe97126e7fc996214e458525d2980c.zip | |
Align macOS rendering with Linux and establish parity regressions
Diffstat (limited to 'docs/rendering-parity-design.md')
| -rw-r--r-- | docs/rendering-parity-design.md | 132 |
1 files changed, 132 insertions, 0 deletions
diff --git a/docs/rendering-parity-design.md b/docs/rendering-parity-design.md new file mode 100644 index 00000000..b6036543 --- /dev/null +++ b/docs/rendering-parity-design.md @@ -0,0 +1,132 @@ +# Shared rendering contract: design groundwork + +Status: proposed direction, not an implemented renderer abstraction. The current +change fixes the native macOS backend and adds regression coverage. It does not +move layout or drawing into a new shared module. + +## Why the backends diverged + +The core shares editor state, cells, tag layers, body layers, and PDF placements. +It does not yet specify every visual operation needed to present them. SDL and +AppKit therefore independently reconstruct tag backgrounds, compact text bands, +context separators, overlay order, and opacity. A correct cell snapshot can +coexist with an incorrect rendered frame. + +The macOS/Linux comparison exposed concrete gaps in this contract: + +- Reduced tag glyph bands were also used as background bounds on macOS; SDL + fills the entire row. Column and pane rules were missing or painted before + an overlapping layer. +- Tree-sitter context rows had matching compact geometry, but the separator + used the scroll-track color instead of the border color. +- Repeated source-over background fills compounded window opacity. At 71%, + two overlapping fills yield about 92%, and three about 98%. The window's own + colored background added another layer behind the view. +- CoreText fallback runs did not inherit the selected foreground color. +- PDF/image composition ignored window opacity on macOS, while SDL applies it + to raster content. Theme colors also need an explicit sRGB interpretation. + +These are duplicated policy decisions, rather than a reason to replace CoreText +or require identical fonts on every host. + +## Proposed ownership boundary + +A shared scene builder should translate the existing surface and its layers +into an ordered list of positioned drawing operations. Platform code should +consume that list without deciding which rows are compact, which edge has a +separator, or which objects participate in window opacity. + +The builder would own: + +- Tag and context geometry, background extents, separator positions and colors, + clipping, and the order of backgrounds, text, images, cursors, and overlays. +- Coordinate conversion rules: logical layout units, supplied font metrics, + device scale, and the rounding policy for one-device-pixel rules. +- Color and compositing semantics, including clear backgrounds, replacement + background fills, image coverage, selection, and opaque native text/cursors. +- Stable image references and a defined frame lifetime, including snapshots + retained for transitions and postprocessing. + +Backends would continue to own native font selection, shaping/rasterization, +image upload/cache management, and presentation. Native event handling, window +management, and the text-only terminal frontend are outside this proposal. + +A small 2D operation vocabulary could include solid rectangles, positioned text +runs, images with source/destination rectangles, and explicit clips. Operations +must carry semantic blend behavior and a documented color space. Wrapping +`fillRect` and `drawText` while leaving each backend to build the scene would +preserve most of the duplication that caused these bugs. + +Do not freeze a new ABI before extracting one small path and checking its needs. +Font metrics must enter the shared layout explicitly; changing fonts can still +change line capacity and glyph appearance. Pixel equality across different +fonts, rasterizers, and display scales is not the acceptance criterion. + +## Compositing contract to preserve + +Let p be WindowOpacity in [0, 1]. An ordinary background region has alpha p, +regardless of how many logical layers cover it. Clear background regions remain +clear. Native text and cursors retain their own opacity. + +For an image sample with coverage a and straight RGB c, the existing SDL +background-layer behavior produces premultiplied output: + +``` +out.rgb = p * a * c + (1 - a) * dst.rgb +out.a = p * a + (1 - a) * dst.a +``` + +Thus PDF text is part of a raster page and fades with the page; native text does +not. Transparent image pixels preserve the destination. Ordinary source-over +with source alpha p*a is not equivalent. Any future operation API needs to +express this distinction directly and specify sampling and premultiplication. + +## Tests available before extraction + +`test/macos-snapshots/tag-ink.snap` checks actual rendered tag foreground pixels, +covering failures that a cell-grid golden cannot detect. + +`test/macos-snapshots/rendering-parity.snap` uses native backing-scale metrics +and checks rendered tag backgrounds and rules, top/bottom tags, opacity 100/71/0, +invalid opacity rejection, and nested multiline Tree-sitter context separators. +It also checks PDF placement and sampled page alpha at those opacity values, +height fit, tint modes, and scrolling. `context.zig` is the nested context fixture; +`docs/9p.pdf` is the existing PDF fixture. Optional PNG output is enabled with +`PARDES_TEST_CAPTURE_DIR`. + +The Zig test named `mac tag layer ABI preserves logical capacity and physical +grip` covers tag-bottom and border-color metadata as well as existing capacity +and grip behavior. The existing Linux `test/window_opacity.py` checks background +changes, native glyph preservation, zero opacity, invalid input, and restoration. + +Run on macOS: + +```sh +zig build unit-test -Dplatform=macos '-Dtest-filter=mac tag layer ABI' +zig build macos-e2e -Dplatform=macos +zig build -Dplatform=macos +``` + +These are baseline regressions, not a comprehensive cross-backend image oracle. +PDF alpha samples do not establish exact raster color or interpolation equality; +offscreen AppKit captures do not test the desktop compositor. Live comparison +still matters for window transparency and display color management. + +## Suggested later extraction and acceptance gates + +1. Add shared geometry/scene assertions for tags and context rows using supplied + metrics at several scales. Include clipping, partial rows, bottom tags, and + final separator order; expected values should come from the contract rather + than a copy of either backend's implementation. +2. Extract that scene construction once and adapt both native renderers. Keep + current backend pixel checks running without regenerating goldens merely to + accept differences. Compare matching fixtures and settings on both systems. +3. Add synthetic RGBA image coverage cases before extracting image composition: + opaque, transparent, and partial-alpha pixels over a known destination at + p=0, intermediate p, and p=1. Include fit, crop, and clip boundaries. +4. Extend the contract to the remaining overlays and retained transition frames. + Measure frame time and allocations before broadening the abstraction. + +The desired result is one definition of visual policy with small native drawing +adapters. A new GPU engine, shared font rasterizer, or wholesale backend rewrite +is not required to reach that boundary. |
