hrydgard/ppsspp — pull request #6699 by ZironZ (2014-08-08 07:19:10+00:00)
Title: NNEDI3 and Spline36 OpenCL texture scaling

This code adds NNEDI3 and spline36 texture scaling using OpenCL.

Example of bicubic at 3x vs. NNEDI3 (default settings): http://screenshotcomparison.com/comparison/86097/

Example of bicubic at 3x vs. spline36: http://screenshotcomparison.com/comparison/86102/

Spline36 is pretty much a direct translation of the shader version to OpenCL. Compared to bicubic, it has a bit more ringing, but is a bit sharper. It scales 2-6 times faster than the CPU-based bicubic on my GTX 570, with the bigger gains being at higher scaling factors and texture sizes. I am able to run it at 5x with no hitching in Wipeout, and only a small amount of hitching in Type-0.

NNEDI3 is a neural network image doubler. It does a really nice job on text and sprites. NNEDI3 tends to cause almost no aliasing or ringing, and doesn't smudge text as much as xBRZ. Since it can only scale to powers of two, spline36 is used to scale the image the rest of the way for any non-power of 2 factor. The biggest downside to NNEDI3 is that it is a rather slow algorithm. Even at its lowest settings I doubt most people will be able to run it at anything more than 3x in a game like Type-0 without a lot of hitching. Although, high-end AMD GPUs might be able to manage at higher settings.

The main NNEDI3 OpenCL kernel is taken from MadVR. Like MadVR, this code offers the ability to scale the luma and chroma channels separately in YUVA. Compared to scaling all the channels in RGBA, it is around 50% faster to scale only the luma using NNEDI3, and around 10% faster to scale only the luma and alpha. There are some options to tweak the quality of NNEDI3 on a per channel basis in the graphics setting menu. Higher neuron settings look slightly better, but are far slower. The code defaults to using the lowest neuron setting on the luma and alpha channels, while using spline36 to scale the chroma.

All the OpenCL code conforms to OpenCL 1.1 since Nvidia doesn't support anything newer. This does add a dependency on an OpenCL library when compiling. I used the one included in the AMD APP SDK. I've only compiled and tested this on Windows since that is all I have access to. I have both an AMD GPU and a Nvidia GPU, and the OpenCL code works on both. It also works on my Intel CPU (and runs like garbage). In cases where the OpenCL scaling fails for whatever reason it outputs the relevant error to the ppsspp error log.

I wasn't really sure how to handle systems that don't support OpenCL. Mobile devices almost never support it so those can be handled with ifdef, but some older desktop devices may also have no support for it. It is relatively simple to check if OpenCL is supported (see IsSupported() in the OpenCL scaling class). I just wasn't really sure where to put the check and where to store the result in order to disable the menu options in cases where OpenCL isn't supported.