Skip to content

Experimental (PoC): xBR/EPX brick filtering for the iso HD view#316

Draft
noctonca wants to merge 3 commits into
feat/iso-brick-scalefrom
feat/iso-xbr-bricks
Draft

Experimental (PoC): xBR/EPX brick filtering for the iso HD view#316
noctonca wants to merge 3 commits into
feat/iso-brick-scalefrom
feat/iso-xbr-bricks

Conversation

@noctonca

@noctonca noctonca commented Jun 8, 2026

Copy link
Copy Markdown
Collaborator

Status: experimental PoC, Draft, not for merge. Stacked on #314 (the iso view-scale foundation); the base will retarget to main if/when that lands. Sharing it so the approach and one engine finding are on record.

What it is

At an iso zoom (iso scale > 1, from #314) the brick background is nearest-stretched and blocky. This filters that background with an edge-directed upscaler and composites it over the unscaled, crisp 3D models, so the floor and walls smooth out while characters and objects stay sharp.

Console toggle (interior, zoom > 1):

iso scale 2     # zoom first
iso epx 0       # off (nearest bricks)
iso epx 1       # EPX-blend (cheap, mild)
iso epx 2       # xBR (edge-directed)
iso epx 3       # xBR supersampled (SSAA, smoothest)

How it works

  • Recover the native bricks by downscaling the zoomed Screen by the zoom factor (no second AffGrille pass, which corrupts shared render state and makes moving objects draw black).
  • Filter to 2x native, then bilinear-resample to the display, so it works at any zoom > 1 (zoom 2 stays bit-identical to the direct path).
  • Composite at present time, via a new scene-level present callback that runs under the console/touch overlays: brick pixels (where Log == Screen) take the filtered RGB layer; model pixels are re-converted from Log through PtrPal.

xBR and its YUV edge metric here are written from the published algorithm, not ported.

One finding worth recording

Going RGB-at-present surfaced a pre-existing engine quirk: the SDL present palette misses the 216-255 range in iso scenes (those entries are black, while PtrPal holds the real colours, e.g. the translucent ghost's blue). Anything using that range presents black even though it is correct in Log, and --screenshot hides it entirely because it saves PtrPal-converted Log. The composite works around it by re-converting model pixels through PtrPal. Full write-up in docs/ISO_XBR_FILTERING.md.

Scope and status

  • Experimental, off by default, interior + zoom > 1 only.
  • This branch is intentionally unoptimised (per-camera-cut rebuild is slow). The perf work (skip-when-unchanged gate, luma-only metric, threaded filter, LRU layer cache: xBR 311 -> 61 ms, SSAA 1470 -> 189 ms at 1280x960) is preserved on feat/iso-xbr-optimized and documented for re-applying once the design settles.
  • The camera snaps between fixed views with its own transition delay, so the rebuild lands inside an existing pause; steady-state is just the composite.

Not proposing this for merge.

noctonca added 3 commits June 8, 2026 19:28
…ent)

At iso scale 2, filter the brick background with an edge-directed upscaler and
composite it over the crisp scaled models at present time. Filter ladder on the
`iso epx` console toggle: 0 off, 1 EPX-blend, 2 xBR, 3 xBR supersampled (SSAA).

The brick layer is recovered by 2x-downscaling the cached Screen (the
nearest-doubled native bricks) and refiltering, so there is no second AffGrille
pass to corrupt render state. The composite runs as a scene-level present
callback (new SetScenePresentCallback slot, under the console/touch overlays):
brick pixels (Log==Screen) take the filtered RGB layer; model pixels are
re-converted from Log through PtrPal, since the SDL paletteLUT misses the
216-255 range some objects use and they would otherwise present black.

xBR and its YUV edge metric are written from the published algorithm, not
ported. 2x interior only; experimental, off by default.
…xperiment

Recover the native bricks by downscaling the zoomed Screen by the actual zoom
factor (not a hard 2x), filter to 2x native, then bilinear-resample to the
display. Zoom == 2 keeps the original direct path (bit-identical), and 3x/4x/etc
now filter correctly too. Gate is IsoScale > ISO_SCALE_ONE in both the build and
the present composite.

Add docs/ISO_XBR_FILTERING.md recording the pipeline, the SDL paletteLUT 216-255
gap and the PtrPal fix, the headless-diagnosis notes, and the optimisation work
(preserved on feat/iso-xbr-optimized) with its measured numbers, so it can be
resumed later.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant