2 Commits
Author SHA1 Message Date
LibretroAdmin 70304fcd02 vulkan: the chip's write-out before the VMUs and crosshairs, as Direct3D and OpenGL have it
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.
2026-10-24 22:13:48 +02:00
LibretroAdmin ca3fee8eaa rend: the chip's write-out on the raster renderers, with the 16-bit dither; the old PowerVR2 filter goes
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.
2026-10-24 22:13:36 +02:00