Entry 340
The frame-mode deflicker: read circuit 1 a line lower, merged at ALP 127
Documented in How the game is put together.
Filescrates/piney-gs/src/lib.rscrates/piney-draw/src/lib.rscrates/piney-mpeg/src/movie.rscrates/piney-game/src/main.rsdocs/engine/overview.mdREADME.md
While working out SetDisplayOffset's units (339), a second pair of
DISPLAY values turned up at ccSystem +0xe10 / +0xe20. It is the game's
deflicker. In frame mode, the mode the game runs in outside the movies, the
GS shows the frame buffer through both read circuits, one a line lower,
merged half and half.
The game
SetScreenModeMain (INF SLUS_202.67:0x0010ad40), when the field flag
(+0xbd6) is 0, after sceGsSetDefDBuff:
0x0010b4e8 both buffers' PMODE (+0xbe0, +0xc08): bit 0 (EN1) set
0x0010b51c their ALP byte (+0xbe1, +0xc09) = 127
0x0010b524 +0xe10 = buffer 0's DISPLAY (+0xbf8), +0xe20 = buffer 1's (+0xc20)
0x0010b534 each copy's DY + 1
libgraph's PMODE is 0x66 (sceGsSetDefDispEnv 0x0010dea0): EN2, CRTMD 1,
MMOD 1 (the merge's alpha is ALP), AMOD 1, SLBG 0 (circuit 2 is the
background). Each frame SwapDoubleBuffer (0x0010ac40) writes, in frame
mode, the shown buffer's DISPFB (+0xbf0 + 40 n) to DISPFB1 (0x12000070) and
its copy (+0xe10 + 16 n) to DISPLAY1 (0x12000080). sceGsSwapDBuff then puts
the buffer's own environment, with DISPFB2 and DISPLAY2. SetDisplayOffset
keeps the copies a line lower (339). So both circuits read the same
buffer, circuit 1 a line lower, and a screen line comes out as
ALP / 255 x the buffer's line above + (1 - ALP / 255) x its own, about
half each. On an interlaced TV that softens one-line detail that would
flicker between the fields. Field mode (the movies' SetScreenMode(640, 448, 2)) skips all of it and sets sceGsSetHalfOffset instead.
PCSX2's "Anti-Blur" option, on by default, is meant to undo blurs of this kind, so PCSX2 players see the sharp picture. That is from the option's description; it was not tried here.
The port
Frame::field_mode (piney-draw) says how a frame is shown. It is false for
the game's frames and true for piney_mpeg::movie::draw. Gs::render
remembers it. piney_gs::Pcrtc carries the display offset and
deflicker to Presenter::present. The shader's merged() mixes the
moved picture with itself one row (1/448) up, at 127/255, before the sRGB
conversion: the GS merges the stored values. Above the top row the line
above is black, as circuit 1 there has nothing to show.
The port's picture is progressive, so there is no flicker to fight, and
the present is kept sharp (its sharp bilinear). So the merge is off by
default, like PCSX2's anti-blur. --deflicker and
the console's deflicker on|off switch it (README "Play").
present_merges_the_line_above_in_frame_mode draws a one-row white line at
row 200. Sharp, rows 199-201 read 0, 255, 0. With the deflicker they read
0, 128, 127, 0. A field-mode frame keeps 255, 0.
the merge circuit's arithmetic on the console (ALP / 255 as PCSX2 takes it, or a shift); whether the user wants the deflicker on by default.