Skip to content

[Feature Request]: Option to filter 2D images (uploads, native frames, 2D textures) with bilinear or xBR when upscaling #14947

Description

@LuismaSP89

Description

When running games above native resolution, a lot of 2D stuff in PCSX2 still comes out as blocks of replicated pixels: FMVs, pre-rendered backgrounds, HUD icons, menus, anything the game draws as flat sprites. I've been digging into why, and it comes down to three separate paths in the hardware renderer, none of which gets any filtering by default:

  1. Images the CPU writes straight into memory that the texture cache tracks as a render target (IPU decoded video frames, pre-rendered backgrounds, software rendered 2D). GSTextureCache::Target::Update stretches them into the upscaled target with nearest. This used to be bilinear until 2b49614 in 2023, when it was moved behind the "Bilinear Dirty Upscale" hardware fix, so today you only get it back by enabling manual hardware fixes.
  2. Frames that Native Scaling keeps at native resolution (Dragon Quest VIII, Shadow of the Colossus and everything else with nativeScaling in the GameDB). They either get bilinear'd back up or are presented as they are.
  3. The most common one: textures used by flat 2D draws. A 512x448 FMV frame drawn as one full screen sprite, a 64x64 HUD icon, etc. are native resolution textures sampled with whatever the game asked for, which is usually nearest. "Bilinear (Forced)" helps a bit but it's all or nothing and it also touches 3D.

What I'd like is a single option in Graphics > Rendering, "Filter 2D Images", with three values: Off (default, exactly today's behaviour), Bilinear and xBR. It should work on its own, not tied to the Bilinear Dirty Upscale hack, and it should only touch 2D:

  • Bilinear: the uploads in (1) are stretched bilinearly (only 16/24/32bpp rects, 4/8bpp uploads are usually indexed data being moved around and are left alone), and flat 2D draws (sprite class, or triangles with constant Z) are sampled bilinearly even when the game asked for nearest.
  • xBR: same, but with Hyllian's xBR. The upload staging texture is xBR upscaled once and then copied per rect so write masks and the RTA alpha correction stay as they are, native resolution targets going back to the upscaled size and native frames reaching the display get the same treatment, and the hash cache textures used by flat 2D draws get a lazily created xBR upscaled copy (up to 6x, bounded per texture) that is sampled bilinearly.
  • Never touched: 3D geometry, render target sources, GPU palette (indexed) textures, depth, texture shuffles, HD texture replacements. Non-integer upscale factors fall back to bilinear for the upload part.

I have all of this working in a branch of my fork, as a single commit on top of current master: https://github.com/Forgottenshadow89/pcsx2/tree/filter2d . There's a FILTER2D_NOTES.md in there that explains every file that's touched and why. The shader is Hyllian's xBR (MIT), added as a new ShaderConvert entry for D3D11/12, OpenGL, Vulkan and Metal; it needs no constant buffer because it derives the scale from the texture coordinate derivatives.

About why this is an issue and not a PR: I'm not a PCSX2 contributor and the code in that branch was written with the help of an AI assistant (Claude Fable 5.1), with me directing it and testing everything on real games and GS dumps. Your contribution guidelines don't allow that for new contributors, so I'm not going to submit it as a PR. I'm leaving it here in case the idea itself is useful to the team; anyone is welcome to take the branch, rewrite it properly or just use it as a reference for where the problem is.

Reason

Right now the only way to get smooth 2D at high internal resolutions is Bilinear (Forced) plus the Bilinear Dirty Upscale hack behind manual hardware fixes, and even then FMVs drawn as sprites and HUDs in native scaling games stay blocky, while 3D gets blurred too. Other emulators (Vba-M, PPSSPP, Flycast, Project64, SNES9x, etc) offer this kind of 2D filtering as a normal option and it makes a big difference in games with pre-rendered backgrounds, videos and pixel art HUDs, without any accuracy cost when it's off.

Concretely, in my testing: Dragon Quest VIII's intro video (uploaded as 16x16 macroblocks and drawn as a 1:1 sprite) goes from hard 6x blocks to clean edges, Shadow of the Colossus' HUD icons get smooth outlines while the 3D stays pixel identical between Off and xBR, and games that stream pre-rendered backgrounds into the framebuffer stop looking like they're running at native resolution.

Examples

DuckStation's "Texture Filtering" and "Sprite Texture Filtering" options (xBR, JINC2 and so on, applied to 2D/sprites separately from 3D), and PPSSPP's texture upscaling (xBRZ 2x-5x) are the closest equivalents. My fork branch above is a working implementation for PCSX2 itself.

Activity

  1. LuismaSP89 commented on Sep 12, 2026

    @LuismaSP89
    Author

    Some screenshot comparisons between my fork and PCSX2 normal behaviour:

    Image Image Image Image Image Image Image Image Image Image Image Image
  2. RedPanda4552 commented on Sep 15, 2026

    @RedPanda4552
    SponsorContributor

    The ask here sounds more in line with having a separate post processing shader that only affects 2D assets. The thing I disagree with here is the analysis that there are things that don't get filtering at all. While this can be true, as games themselves get to choose what does and does not receive texture filtering, the examples shown do not seem to be cases of that. Furthermore texture filtering can already be forced, by switching to the Bilinear (Forced) mode to always apply it rather than following what the game asks for.

    I have suspicions about the quality of the findings here given there was AI use. But there may still be validity in the addition of a post processing shader that targets 2D assets. Would need consideration from other team members if that's reasonable to pull off, though.

  3. LuismaSP89 commented on Sep 15, 2026

    @LuismaSP89
    Author

    The ask here sounds more in line with having a separate post processing shader that only affects 2D assets. The thing I disagree with here is the analysis that there are things that don't get filtering at all. While this can be true, as games themselves get to choose what does and does not receive texture filtering, the examples shown do not seem to be cases of that. Furthermore texture filtering can already be forced, by switching to the Bilinear (Forced) mode to always apply it rather than following what the game asks for.

    I have suspicions about the quality of the findings here given there was AI use. But there may still be validity in the addition of a post processing shader that targets 2D assets. Would need consideration from other team members if that's reasonable to pull off, though.

    Of course, my approach with this issue/request, was to expose the problem that currently many games had with 2D textures not being filtered in any way, using these examples (Sotc map, icons on the map, & weapons, dragon quest VIII intro splashes and characters images at the menu, etc) I choosed that examples because even having bilinear forced always enabled by default they're not being currently filtered at least on my side. (But there are many others)

    Regarding the code itself, it was done with AI help as said. That's the reason to avoid opening a PR because PCSX2 contribution guidelines are against that. But I wanted to find a way to fix this by myself, and show how the xbr filter is also a good option already present in many emulators when it's used only for 2D textures.

    In any case, I'm open to modify anything, or close the issue if the devs are offended by this, (It wasn't my intention though)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions