NEON scalers


M-HT

Very Active Member
Joined
Nov 30, 2007
Messages
688
Location
Bratislava
Website
github.com
Hi,

I implemented various scalers with following properties:
- written in pure assembler
- uses ARMv7 and NEON instructions
- uses instruction scheduling for Cortex-A8 processors
- no checking for corectness of parameters
- no clipping
- MIT license

Implemented scalers: normal1x, normal2x, normal3x, normal4x, scale2x, scale3x, scale4x, eagle2x

The library and source code is available in the archive.
The source code is available on GitHub.

Edit (2012-11-19):
Known use:
GPFCE - NES emulator (uses: scale2x, eagle2x)
gpSP - GBA emulator (uses: scale2x, scale3x, eagle2x)
PCSX ReARMed - PlayStation emulator (uses: scale2x, eagle2x)
 
Last edited:
Wooooaa!


Scale4x and the likes with NEON and assembler?


Awesome work, thanks a lot :D
 
Is this as exciting as it sounds? (thanks M-HT)


can these now be incorporated in all the emulators/games that currently don't scale to the Pandora's full screen?


If so.. happy days!!
 
can these now be incorporated in all the emulators/games that currently don't scale to the Pandora's full screen?
This library is meant as faster versions of the scalers. Any program can use it, but these scalers only scale to integer factors (2x, 3x, 4x) so the result probably won't fill Pandora's full screen.
 
surly it could be converted to use floats? instead of Integers? im assuming that will slow it down somewhat? but I though that what neon was good at ? floating point calculations?
 
Should be very useful for The Collector Plus. Thanks :)
 
surly it could be converted to use floats? instead of Integers? im assuming that will slow it down somewhat? but I though that what neon was good at ? floating point calculations?
These scalers only compare pixels for equality, they don't perform any calculations (ie. interpolation) upon them, so there's no difference between floats and integers - only thing that matters is the number of bits per pixel.


Neon is also good for processing multiple data with single instruction.


With 8 bits per pixels 16 pixels are processed at once.


With 16 bits per pixels 8 pixels are processed at once - two times slower than 8 bits per pixel.


With floats (32 bits per pixel) 4 pixels would be processed at once - two times slower than 16 bits per pixel.
 
Well, but using Scale2x or Eagle2x and then using the hardware scaler to make it fit the Pandora screen might look pretty awesome with some emulators :)
 
Obvious question is why the emulators aren't they doing it already. :)


If it's because it slows down the emulation, then I hope that my implementation is fast enough.
 
These scalers only compare pixels for equality, they don't perform any calculations (ie. interpolation) upon them, so there's no difference between floats and integers - only thing that matters is the number of bits per pixel.
Neon is also good for processing multiple data with single instruction.


With 8 bits per pixels 16 pixels are processed at once.


With 16 bits per pixels 8 pixels are processed at once - two times slower than 8 bits per pixel.


With floats (32 bits per pixel) 4 pixels would be processed at once - two times slower than 16 bits per pixel.

Even worse, 32x4 FP32 compare issues in two cycles, while 32x4 integer compare issues in one. Although you can sometimes compare floats using integer compare.
 
Is this meant as a drop in replacement for other scale2x/3x filters?


I'd like to integrate those faster implementations into panmame, if I can. Would you help me a bit, if I run into troubles there?


Edit: And of course thank you for taking the time to write those, definately no easy job.
 
Last edited by a moderator:
Is this meant as a drop in replacement for other scale2x/3x filters?
There are (probably) different input parameters and the input conditions must be met, but yes.


If you need different input parameters, parameter checking, cliping, etc. you can write wrappers for my functions, but that shouldn't be a problem.

I'd like to integrate those faster implementations into panmame, if I can. Would you help me a bit, if I run into troubles there?
Yes.
 
Integrated these into gpfce today, a few comments:


some older versions of GCC want comma before :alignment, or else:



Code:
drivers/arm/neon_scale2x.S:253: Error: ']' expected -- `vld1.16 {q9},[ip:128]!'



.include doesn't work well if source files are not in working directory:



Code:
arm-none-linux-gnueabi-gcc -mcpu=cortex-a8 -mfpu=neon   -c -o drivers/arm/neon_scale2x.o drivers/arm/neon_scale2x.S

/tmp/cc27EDXa.s: Assembler messages:

/tmp/cc27EDXa.s:29: Error: can't open neon_scale2x.Sinc for reading: No such file or directory

while #include does.


Finally, that 32bit palette with 16bit values is rather inconvenient to use, I assume you wanted to avoid address generation complexity and larger load latency?
 
Integrated these into gpfce today, a few comments:
You're the first one to use them (that I know of). It's good to know that I didn't waste my time creating them :) .

some older versions of GCC want comma before :alignment, or else:



Code:
drivers/arm/neon_scale2x.S:253: Error: ']' expected -- `vld1.16 {q9},[ip:128]!'
The GCC I'm using (4.6.1) doesn't accept comma before :alignment and newer versions are probably the same.



.include doesn't work well if source files are not in working directory:



Code:
arm-none-linux-gnueabi-gcc -mcpu=cortex-a8 -mfpu=neon -c -o drivers/arm/neon_scale2x.o drivers/arm/neon_scale2x.S

/tmp/cc27EDXa.s: Assembler messages:

/tmp/cc27EDXa.s:29: Error: can't open neon_scale2x.Sinc for reading: No such file or directory

while #include does.
I didn't realize that. When deciding between #include and .include, I decided to use .include, because that allows the file to be compiled without C preprocessor.



Finally, that 32bit palette with 16bit values is rather inconvenient to use, I assume you wanted to avoid address generation complexity and larger load latency?
I wanted to use only one instruction for loading value from palette:



Code:
ldr value, [palette, pixel, lsl #2]

With 16bit palette, more instructions are needed.



You can write a wrapper if you like, such as this:



Code:
void neon_scale2x_8_16_wrapper(const uint8_t *src, uint16_t *dst, const uint16_t *palette, unsigned int width, unsigned int srcstride, unsigned int dststride, unsigned int height)

{

uint32_t palette2[256];

int i;

for (i = 0; i < 256; i++) palette2[i] = palette[i];


neon_scale2x_8_16(src, dst, palette2, width, srcstride, dststride, height);

}

If you're calling the scaler once per blit, the overhead is negligible.
 
The GCC I'm using (4.6.1) doesn't accept comma before :alignment and newer versions are probably the same.
Are you sure? I took latest linaro release and it works here:



Code:
$ arm-linux-gnueabihf-gcc -v

...

gcc version 4.7.2 20120731 (prerelease) (crosstool-NG linaro-1.13.1-2012.08-20120827 - Linaro GCC 2012.08)

$ echo 'vld1.16 {q9},[ip, :128]!' > test.s

$ arm-linux-gnueabihf-gcc -c test.s -o test.o

$ arm-linux-gnueabihf-objdump test.o

00000000 <.text>:

   0:    f46c2a6d     vld1.16    {d18-d19}, [ip :128]!
 
You're right.


I was putting the register with alignment in a macro call - that didn't work because the macro was taking it as two parameters instead of one.
 
I've also noticed you don't use preload (pld instruction) at all, this gave a good improvement to my SDL blitters because Cortex-A8 doesn't have streaming detection (A9 does). Preloading around 2 cachelines ahead of time did a good job there. I tried to illustrate this on your code but strangely it had no positive effect even on test buffers way larger than L2 cache size. Strange.
 
Last edited by a moderator:
I did some tests with using pld instruction in some scalers, but it was slower than the version without using them, so I didn't use the instruction in any scaler. Either I didn't use the instruction properly or it has no effect in my implementation.
 
Back
Top