On Vulkan the VMU screens and lightgun crosshairs are drawn in the
frame's render pass (its last subpass), so the write-out pass, recorded
after the render pass, filtered and dithered them too. On the console
they are not part of the picture at all; Direct3D and OpenGL already
draw them after the pass.
Now, when the frame has a write-out and there is something to draw over
it, the screen drawer ends the frame's render pass, records the pass,
and the VMUs and crosshairs go in a render pass of their own that
loads the picture (VulkanWriteOut::BeginOverlay()), with a quad pipeline
made for it. The render pass, its framebuffers (one per picture) and
the pipeline are made with the pictures, when the renderer starts or
changes size. Nothing is copied, and nothing is made in a frame. A frame
without a write-out, or with nothing over the picture, records what it
did before. With the option off, nothing changes.
Gates (lavapipe; not a real GPU): RetroArch, VF3tb booted to frame 900
with VMU screen 1 shown (opaque), for the per-triangle and per-pixel
renderers:
- option off: the same picture as before, byte for byte;
- option on: outside the VMU screen (and the line under it, whose
filter taps reach it), the option-off picture through the model of the
filter and dither; on the VMU screen, the VMU as drawn, unfiltered.
No validation errors.
The core option is now "PowerVR2 scaler, flicker filter and dither"
(reicast_pvr2_scaler, still off by default). It does what the
PowerVR2 does to the picture as it writes the tiles to video memory,
in the chip's order: the X scaler, the Y filter, then the packing to a
framebuffer of 16 bits a pixel and its dither (FB_W_CTRL). The picture
shown is that framebuffer as the video output reads it back, with
FB_R_CTRL's fb_concat in the low bits.
- rend/tile/pvr_scaler.h: pvr_fb_word() and pvr_fb_write(), the
packing to 0555/1555 or 565 with rend/rtt_watch.cpp's 4x4 pattern
(the same arithmetic as the renders to a texture). At internal
resolution N the pattern goes by the chip's pixel (N picture pixels
to one). 4444 and the 24/32-bit formats are left as they are.
Checked against an independent copy of rtt_watch.cpp's packing plus
TexCache.cpp's read-back: no difference in 195072 cases.
- The compute renderer's in-place pass (PVRC_K_YFILTER) does the
packing and dither after the Y filter, and runs on any frame with
either (ComputeFrame::WriteOut()). tile_ref and refsw do the same.
refsw did not dither the picture before: it does now.
- The raster renderers run the same pass in place over their picture
after the frame (before the VMUs and crosshairs on Direct3D and
OpenGL; on Vulkan they are in the frame's render pass, so the pass
runs over them too):
- Vulkan, both renderers: the compute renderer's own SPIR-V
(vulkan/writeout.cpp).
- Direct3D 12, both renderers: the compute renderer's own DXIL
(dx12/dx12_writeout.cpp). Needs typed UAV loads of R8G8B8A8_UNORM.
- Direct3D 11, both renderers: the same pass in HLSL, compiled when
the renderer starts (dx11/dx11_writeout.cpp). Needs typed UAV
loads of R8G8B8A8_UNORM (TypedUAVLoadAdditionalFormats).
- OpenGL 4.3 and later, both renderers: the same pass in GLSL,
compiled (and cached) when the renderer starts
(gles/gl_writeout.cpp). The frontend's framebuffer is not a
texture of the core's, so the picture is blitted to the pass's
texture and back.
Not on OpenGL ES, OpenGL 3 or Direct3D 9.
The pictures are made writable by a compute shader only when the
option is on. Turning the option on or off counts as a change of size:
the renderer makes its pictures again between frames, never during
one. Off, the renderers do what they did before, at no cost.
- The rasterizers' X scaler is not done here: they still squash the
game's double-width drawing in the transform, as before. No dumped
game sets SCALER_CTL's horizontal scaling.
The old "PowerVR2 Post-processing Filter" (reicast_pvr2_filtering,
OpenGL only) is removed: PostProcessor, gles/postprocess.cpp/.h, the
PowerVR2Filter setting, the option in every language, and its
Makefile entry. It guessed at a 3-tap vertical blur, which the exact Y
filter now replaces. It also dithered by its own rules and added a made-up
"VGA signal loss" smear, which the hardware does not do. It compiled its
shaders mid-game and drew through an extra framebuffer.
RetroArch ignores old values of the option left in users' configs.
Gates (lavapipe/llvmpipe and vkd3d, not a real GPU):
- tile_test VF3tb 1x on Vulkan, at 1 and 4 samples, and on Direct3D 12,
at 1 and 4 samples: off, filter + 565 dither (VF3tb's own
registers), 555 dither alone, and taps 3 lines apart with fb_concat 5.
16/16 runs are the same as tile_ref on each.
- RetroArch (Vulkan, then glcore) with VF3tb booted, frame 900, for each
of Vulkan per-triangle, Vulkan per-pixel, OpenGL per-triangle and
OpenGL 4 per-pixel. With the option off, the picture is the same as
master's, byte for byte. With it on, the picture is exactly the
option-off picture through an independent model of the filter and
dither (Y_COEFF 0x8040, 565, dither on). No validation errors.
- Direct3D 11 and 12 rasterizers: syntax-checked with mingw, and the
Direct3D 11 pass compiled with d3dcompiler_47 (cs_5_0). Not run.