Skip to content

DIS: warp-based interpolation with source-point flow lookup - #775

Merged
maxjivi05 merged 26 commits into
WinNative-Emu:mainfrom
qwertypower:main
Sep 26, 2026
Merged

maxjivi05 merged 26 commits into
WinNative-Emu:mainfrom
qwertypower:main

Conversation

@qwertypower

Copy link
Copy Markdown
Contributor

No description provided.

qwertypower and others added 26 commits September 14, 2026 23:03
…ts on vsync

- Cap dialog widths to the window instead of fixed minimums, weight labels against value chips

- Stack footers, storage info, glasses settings and HUD toggles at compact widths

- Align store grid columns with libraryColumns for controller focus

- Add IME padding to dialogs with text input

- Coalesce guest presents onto the Choreographer callback in VulkanRenderer

- Give Fast-tier DIS flow the Balance refinement budget
dis_interpolate.comp

- Replace the photometric-only occlusion test. It compared full-resolution
  colour against a flow solved at 180-360 px on the short side, so on textured
  motion it saturated and snapped whole regions to the nearest source frame,
  which reads as motion stalling for a frame and then jumping.

- Occlusion now requires both a discontinuous flow field and a genuine mismatch
  between the warped samples. The discontinuity is measured over a 4 flow-texel
  window: around a screen-locked object the field ramps over a band tens of
  texels wide rather than stepping, so a per-texel slope never clears a useful
  threshold. The mismatch is measured at the flow scale and normalised by local
  contrast, so a slightly misaligned texture edge does not count as occlusion.

- Pick the valid side from the sign of the flow divergence rather than the
  nearest frame in time: a diverging field is area opening up, which exists
  only in the next frame; a converging one is area being covered.

- Flow debug view: log magnitude scale in source pixels. The old scale
  saturated at 16 px, which pegged the whole frame during a camera orbit and
  hid both the magnitude and any parallax.

- OCCLUSION_STRENGTH and DEBUG_OCCLUSION are compile-time switches for A/B.

dis_vr_add.comp

- Bound the absolute flow magnitude. Nothing upstream did: the block search only
  limits displacement from the coarse estimate, the refinement only limits its
  own update, and the existing guard allowed four frame widths and then fell
  back to a value that was itself already huge. On a scene cut the flow reached
  hundreds of pixels and the warp smeared the frame edge across the picture.
  Clamp the magnitude to a quarter of the frame, and clamp rather than revert.

The variational solver was ported to NumPy and exercised on a synthetic cut to
locate this: it does not diverge, it faithfully smooths whatever it is given,
and the runaway comes from the missing absolute bound. With the clamp the
output pins at the limit for any input, and correct small motion is unchanged.

Both shaders verified to compile to SPIR-V. Not yet run on device.
DIS: divergence-gated occlusion mask; bound the absolute flow magnitude
- dis_interpolate.comp: pre-occlusion blending (photometric occlusion only)

- dis_vr_add.comp: loose component guard, no absolute magnitude clamp

- vkr_dis.c: original refinement ladder, no Fast-tier VR budget lift

Brings DIS in line with WinNative-Emu/WinNative 68f678c.
- dis_hist.comp: half-resolution pass collecting the pass-through evidence (frame difference, plus edge agreement for translucent overlays) and keeping the maximum with a decay, so a per-frame decision hovering around the thresholds cannot flicker

- dis_interpolate.comp: apply the stabilized mask on top of the per-frame test, suppressed on cuts by the current frame difference; pick the blend side from the flow divergence instead of t so the side stays the same for every generated frame of a pair

- vkr_dis.c: history resources, pipeline, descriptor sets, dispatch and reset

UI_HIST_ENABLE=0 falls back to the per-frame mask, DIS_OCCL_SIDED=0 to the t-based side.
- the variational refinement runs on the finest three levels only; levels without it skip prep/add entirely and the search reads the densified flow as its coarse estimate

- dis_propagate ranks candidates on a 4x4 subsample of the patch instead of all 64 texels, with the score rescaled to the 64-sample units the densification compares against

- dis_side.comp computes the occlusion side (flow divergence sign) once per flow texel; the interpolate stage reads one tap instead of four flow fetches

- the inverse search caps at six Gauss-Newton iterations (the SSD break already ends most patches earlier)

- VR descriptor sets and pool are sized to the refined levels; UI dilation taps 4 -> 2
The 8x8 luma/gradient patch is loaded as 4x4 quads, three gathers per quad instead of one fetch per texel - about three times fewer sample instructions in the setup, same texels. The gathered component order is documented in the shader; at clamped borders the edge texel repeats, as the per-texel clamp did before.
Under the same threshold the overlay pass-through already uses to skip its own fetches, the shifted samples cannot change an output pixel visibly, so the warped pair, the side map and the edge dilation are all dropped and the real pair is blended directly. Static scenery and HUD-heavy screens are mostly such pixels, and this pass is the most expensive one per output pixel.
- the variational refinement runs on all levels again: at 4x the fast motion needed the coarse levels, and the three-level scope showed up as objects that looked uninterpolated

- source-rate estimate: longer smoothing window and sub-2ms bursts rejected, so a single startup burst no longer spikes it to hundreds of fps

- ratio hysteresis widened so the planner stops oscillating between 2 and 3 generated frames while the source rate drifts
The two-tap dilation saved a few fetches but left antialiased overlay edges uncovered under scene motion - the edge pixel is a blend of glyph and moving background, so only its neighbours carry the pair agreement. Four taps over 1.5px is the point the original analysis measured as the useful limit, and refining every level stays as well: the alternatives either save a couple of dispatches or give back the fast-motion smoothness.
- blend pick: thresholds raised (0.02..0.12 -> 0.03..0.18) so a moderately wrong flow keeps the warped blend instead of snapping to one real frame, which is what read as fast objects not being interpolated

- 4x generation gets a third fixed-point pass on the finest level, so the warped pair lines up more often and fewer pixels reach the pick at all
SPDX headers crediting qwertypower (DEVAR Entertainment LLC), same as the C sources.
- dis_temporal.comp: EMA over the level-0 field with an advection gate, ping-pong RGBA32F where xy is this pair's chord and zw the previous pair's for the same content; jitter on long runs roughly halves

- dis_interpolate.comp: follow a parabola through three consecutive real frames instead of the straight chord, falling back to the chord by construction where the gate found no usable history

- dis_vr_add.comp: clamp the absolute flow magnitude to a quarter of the frame instead of the never-firing four-width component guard

- vkr_dis.c / CMakeLists: pipeline, ping-pong images, descriptor sets and dispatch for the temporal pass
Replace the freeze/crossfade fallbacks with a motion-preserving warp: flow read at the source point, static overlays kept by static-vs-motion residual, divergence-sided occlusion, Catmull-Rom fetch. Drop temporal EMA and the parabolic path.
… hint

The generator now lives in app/src/main/cpp/dis as the wndis static library (public header and GLSL shaders under include/), linked by libwinlator and libwnwayland instead of being compiled into each. On Adreno the GLES motion estimator can seed the level-2 search as a second candidate; it is off by default (debug.winnative.dis.hwme=1 enables it) since it measured quality-neutral at ~2.5 ms per frame.
@maxjivi05
maxjivi05 merged commit 5f913e3 into WinNative-Emu:main Sep 26, 2026
5 checks passed
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.

2 participants