Dark Bit Factory & Gravity

PROGRAMMING => Freebasic => Topic started by: MrP on September 07, 2006

Title: Full screen 640 x 480 alpha fade between two images
Post by: MrP on September 07, 2006
Well I've almost finished converting my gfx drawing functions into assembly, and by god have I been up and down with it. But in the end its been worth it, the difference in performance is quite staggering really. so here at the moment is my pride and joy a fast full screen 640x480 alpha blend between two images. at the moment this clocks in at a tad over 2.6 millisecs to draw each image on my rig, so in theory at a refresh of 75hz you should be able to do at least four images at once without a hit in framerate. Not sure why coz it'd look stupid but anyways.... Have a look see what you think, and if you could please post back with framerate and some system specs.....

Cheers in advance......



[oversized attachment deleted by admin]
Title: Re: Full screen 640 x 480 alpha fade between two images
Post by: Shockwave on September 07, 2006
Had you restricted the refresh rate to 75hz? That's what I get and it hardly ever drops to 74.

Looks wicked! Well done.

P4 3Ghz Radeon X300 se.
Title: Re: Full screen 640 x 480 alpha fade between two images
Post by: DrewPee on September 07, 2006
I get around 73fps and I agree with Sockwave - Wicked - very very smooth.

This was done on my rig at work - Pentium  (Dual Core) 2.66Mhz (x2) with 1GB ram and a GF6500 (512Mb) Graphics Card.

DrewPee
Title: Re: Full screen 640 x 480 alpha fade between two images
Post by: Rbz on September 07, 2006
Very nice, I got 42 FPS  (my gfx card doesn't like software rendered pixels)

P4 2.8GHZ    512MB Ram
GeForce 6600GT  128MB VRAM

Title: Re: Full screen 640 x 480 alpha fade between two images
Post by: MrP on September 07, 2006
Cheers folks thanks for the replies. Getting the feedback i'd hoped for..... Took a lot of time to get that drawing routine working properly.....

@Shockwave - its only limited by initing the screen with a 75hz refresh and using screensync to sync to it. So yes it is limited to 75hz

@Rbraz - Not sure why the performance should be hit like that on your rig mate.... I assume you've had poor pixel performance before due to the content of your post, but still curios as to why its like that...... Cheers for the info.....
Title: Re: Full screen 640 x 480 alpha fade between two images
Post by: Blitz Amateur on September 07, 2006
I get 29 on my laptop, will run on my good PC soon.

Laptop specs:

1066Mhz PIIIMobile 384Mb SDRAM Intel82830m integrated graphics (48mb)

I am very surprised by that speed, very well done!
Title: Re: Full screen 640 x 480 alpha fade between two images
Post by: Clyde on September 07, 2006
Welldone 38 fps.
Title: Re: Full screen 640 x 480 alpha fade between two images
Post by: Blitz Amateur on September 07, 2006
75 FPS on my good PC

2.2GHz AMD Athlon64 1GB DDR SDRAM RADEON X800Pro (PCI-Ex)

Very nice
Title: Re: Full screen 640 x 480 alpha fade between two images
Post by: zawran on September 07, 2006
Good work, looks like its rather fast, but I cannot see the corner where the fps was shown.

One thing though on a more general note. It would be nice if people had windowed as an option or as a seperate download. Fullscreen in most low resolutions either doesn't work at all, or looks really strange on my widescreen 1900x1200 res monitor. I would actually prefer to watch anything windowed even if it is smaller.


Title: Re: Full screen 640 x 480 alpha fade between two images
Post by: MrP on September 08, 2006
Thanks all for the replies and details of your rig and stuff. Your feedback is very much appreciated, and thank you also for the kind words. It kinda makes it all worth while if you know what i mean

@zawran - I'll keep in mind about including a windowed version and i'll see if I can update this one. By the way just how close have you got to sit to that screen to read it lol.........
Title: Re: Full screen 640 x 480 alpha fade between two images
Post by: zawran on September 08, 2006
Not close at all, its a 24 inch monitor :)
Title: Re: Full screen 640 x 480 alpha fade between two images
Post by: ninogenio on September 08, 2006
nice one MrP,

41 fps on a geforce4 ti4600 128m , athlon xp2200 256m ddr ram rig.

although i must say ive found out through genarally messing around that freebasic is slow compared too say dev c i remember doing a program that done exactly the same thing as yours in dev c gdi and i was getting about 90fps on this comp.
Title: Re: Full screen 640 x 480 alpha fade between two images
Post by: Shockwave on September 10, 2006
Mmm. Well as Mr.P is really still getting to grips with ASM, I think we should sit tight and wait for him to discover the optimisations that will no doubt accelerate his stuff even more.
For me it looks cool and seems fast, although how fast it really is is unknown with the refresh rate stuck on 75fps.
Title: Re: Full screen 640 x 480 alpha fade between two images
Post by: Jim on September 10, 2006
72fps here, P4-3.0, Radeon X1600 AGP.   MMX is absolutely designed for this kind of thing, if you're going to look at asm MrP.

Jim
Title: Re: Full screen 640 x 480 alpha fade between two images
Post by: psygate on September 10, 2006
25 pics on my comp. 1.4 GHz AMD Athlon XP. Dont now the graphic card or anything else, im just happy that the thing keeps running  :||
Title: Re: Full screen 640 x 480 alpha fade between two images
Post by: MrP on September 11, 2006
OK Results look reasonable all round I think.... Thanks to you all for responding.....

@Shockwave - Yep just about getting to grips but as you say once i get to optimising this stuff a bit better it should be faster still... The routine i wrote to draw images has not been optimised at all, it is as it was when i first got it working, and as you know once you have something working theres generally a way to get it working faster. Also on the frame limiting note, when I get home tonight I'll post a version without the frame limiter so we can see what its up to.....

@Jim - The pixel drawing bit does already use mmx, the routine calculates the alpha for the red green and blue element of each pixel at the same time. However I am still very new to assembly on the pc so there may be a way to implement the mmx stuff more efficiently. I'm sure as I go on i'll pick up more tricks and stuff......
Title: Re: Full screen 640 x 480 alpha fade between two images
Post by: Shockwave on September 11, 2006
I'll look forward to the de-limited version :) I'd love to see how fast it really goes!
Title: Re: Full screen 640 x 480 alpha fade between two images
Post by: MrP on September 12, 2006
OK Sorry I couldn't post yesterday. Friends Birthday and things got a bit hectic shall we say......

Anyway here as promised is the version with a toggleable frame limiter. Enjoy!!!!!!!

[oversized attachment deleted by admin]
Title: Re: Full screen 640 x 480 alpha fade between two images
Post by: Shockwave on September 12, 2006
118 FPS :)
Title: Re: Full screen 640 x 480 alpha fade between two images
Post by: Clyde on September 13, 2006
69 FPS.
Title: Re: Full screen 640 x 480 alpha fade between two images
Post by: Jim on September 14, 2006
About 103fps.  Â 3.8GHz P4, Quadro FX1400.

I did a naughty thing and ran your code through my disassembler (IDA Pro).  It shows me you're doing something like this
Code: [Select]
for y=0 to 479
for x=0 to 639
pixel=screen(x+y*640)
'some good mmx code to mix pixels
screen(x+y*640)=new pixel
next
next
The MMX stuff looks fine, but the code to do x+y*640 is almost as big!  Try using a pointer, so it would go something like this
Code: [Select]
dim screen_ptr as uinteger ptr
screen_ptr = @screen(0)
for y=0 to 479
for x=0 to 639
pixel=*screen_ptr
'some good mmx code to mix pixels
*screen_ptr=new pixel
screen_ptr = screen_ptr+1
next
next
Then you can take it one step further and roll the two loops into one
Code: [Select]
dim screen_ptr as uinteger ptr
screen_ptr = @screen(0)
for p=0 to 640*480-1
pixel=*screen_ptr
'some good mmx code to mix pixels
*screen_ptr=new pixel
screen_ptr = screen_ptr+1
next
And finally, now you've got something far simpler, why not re-write the entire loop in asm? :D

Jim
Title: Re: Full screen 640 x 480 alpha fade between two images
Post by: MrP on September 14, 2006
Er... bit confused now... the entire loop is in asm. Granted its a bit messy and needs a good looking over to simplify it a bit, but it is entirely in asm. The image drawing loop isn't just for drawing full screen pics. you can give it an image of any size and an x,y co-ordinate on screen and it will draw it there. I know that for the purpose of this full screen fade you could just blit two images starting from the top left and going down to the bottom right, then you wouldn't actually need to do the y*640+x thing at all. A simple increment for each pixel is all that would be required til you got to the end of each image, Just like in your last example. The code as it stands lets you pass an x,y, source buffer, and alpha value to it. it then calculates where on screen the pixels should be drawn using the y*640+x formula as it steps through each pixel in the source buffer. This is what is expensive because that is calculated for every pixel being drawn. A quicker approach (which i'm working on at the minute) would be to calculate where on the screen the top left pixel should be, then just increment both the source and destination buffers by one pixel then at the end of each line of the source buffer you could then increment the screen position by 640 - width of the source buffer and then continue line by line through the source buffer until all pixels are drawn. This i suppose would be more like the blitting approach used in your last example. I hope this explains why the code is a lot more complicated than it should be for the simple task of drawing two full screen images. Anyways i'm at work at the mo, but when i get home ill post the whole loop. Hopefully you can give me some pointers on where I might be able to improve it......
Title: Re: Full screen 640 x 480 alpha fade between two images
Post by: ninogenio on September 14, 2006
i think what jim was saying mate is exactly the same as your now trying to incorporate and he was just showing you in basic for clarity.

ive done sprite blitting with the two methods and the later works far quicker.
Title: Re: Full screen 640 x 480 alpha fade between two images
Post by: MrP on September 14, 2006
OK Heres my routine to draw an image of any size anywhere on screen, although I've not incorporated any bounds checking yet. This is probably very primitive and as i've said above i'm working on a quicker way to do it, but this is my first bash at it and what you see here is exactly what i had when it worked for the first time, so I'm damn sure this is messy to say the least.... Any comments on this would be most welcome, even though it's not going to stay like this any suggestions on what I could have done better would be appreciated....

Code: [Select]
    sub pgl_draw_image(byval src_buffer as integer ptr, byval x as integer, byval y as integer, byval alpha as ubyte)
    dim x_pos as integer = 0
    dim y_pos as integer = 0
    dim p_alpha as integer = rgb(alpha, alpha, alpha)
    asm
        mov eax, [src_buffer]       'get image width and height into edi and esi
        mov edi, [eax]               '
        add eax, 4                  '
        mov esi, [eax]               '
        add eax, 4
       
        dloop:
        mov edx, [x]                'edx has x position
        mov ecx, [y]                'ecx has y position
       
        mov ebx, [pgl_screen_ptr]   'ebx has screen pointer
        imul ecx, [pgl_screen_x]
        add ecx, edx
        lea ebx, [ebx + ecx * 4]

        pxor mm6, mm6
   
        movd mm0, [eax]       'image pixel col in mm0
        movd mm1, [ebx]       'screen pixel col in mm2
        movd mm2, [p_alpha]   'mm2 has alpha value
       
        punpcklbw mm0, mm6    'unpack mm0 using blank mm6 register as interleave
        punpcklbw mm1, mm6    'same for screen color
        punpcklbw mm2, mm6    'same for Alpha
   
        psubw mm0, mm1        'subtract image color from screen color
        pmullw mm0, mm2       'multiply resulting color by alpha
        psrlw mm0, 8          'divide that by 255
        paddb mm0, mm1        'and then add screen color back in

        packuswb mm0, mm0     'repack image pixel back to lower 32 bits of mm0
        movd [ebx], mm0       'move result back to screen
           
        push ecx
        mov ecx, [x_pos]
        inc ecx
        cmp ecx, edi
        ja inc_y
        back:
        mov [x_pos], ecx
        pop ecx
        add eax, 4
        add edx, 1
        mov [x], edx
        jmp dloop
       
        inc_y:
        push edx
        mov edx, [y_pos]
        inc edx
        cmp edx, esi
        ja finished
        mov [y_pos], edx
        pop edx
        sub [x], edi
        mov ecx, 0
        jmp back
       
        finished:
        emms
    end asm
end sub
Title: Re: Full screen 640 x 480 alpha fade between two images
Post by: Jim on September 15, 2006
Don't be confused MrP, it's just how my brain works ;) It's hard to tell from my original disassembly whether it was all asm or just the pixel bit, which, without meaning to insult you in any way, means it's not terribly good assembler. :-\  You need to get those xpos/x ypos/y variables into registers, which with some thought is definitely possible.

I didn't realise until you posted again that this is supposed to allow you to blit an arbitrary rectangle with alpha over the top of an existing screen.  That makes it a lot more than just a demo that blends two screens together.  Sorry if I misunderstood.

To speed it up, then you need to fix that per-pixel calculation x+y*screen_width.  That can be done by working with one scanline of the blitted image at a time.  You can calculate the screen address for the destination by x+y*w and then at the end of each line, having added on 1 for each pixel of the image you are blitting on top, you need to add on screen_width-width of blitted image.
Also one really simple optimisation is that mm2 which contains your unpacked alpha value is constant for the whole blit, so you can easily move that outside your loop.

I think you should be aiming for 15 instructions or fewer in the inner loop. :D

Jim
Title: Re: Full screen 640 x 480 alpha fade between two images
Post by: MrP on September 15, 2006
Thanks for the pointers Jim. I'm in the middle of changing the routine, just been a bit busy this week and I've not had much time to comit to it. I will aim for 15 instructions and I'll post back with what I come up with. I've got the alpha bit inside the loop, because eventually the drawing bit wil calculate per pixel alpha information from the source image and not just one constant alpha value for the whole thing. However i'm struggling extracting the alpha information from the integer and putting it back together in assembly, eg my source pixel value has the format aarrggbb, so I need to extract the first byte and then put that into a register with the format 00aaaaaa so i can load it into mm2 and sort the alpha stuff out in the mmx bit. Thats why its done before the asm bit with the freebasic rgb() command. I've tried moving a byte value into a register and shifting it left 8 then or'ing it with the byte value then shifting again then or'ing again but it doesn't seem to work. But anyways before that really becomes an issue I really need to do some basic housekeeping on the rest according to your excellent suggestions..... Once again thanks very much for your comments and suggestions, now lets see what I can do with them......
Title: Re: Full screen 640 x 480 alpha fade between two images
Post by: Shockwave on September 15, 2006
That's a fair point Mr.P TGA files having that per pixel alpha info make them huge though and although I'm bound to say that they are great, there's no way I'd ever include one in a program of mine unless it was calculated mathematically at runtime. Just too big.

It would be very cool to see how fast you could make this go without that provision and hust having one alpha value per bilt.
Title: Re: Full screen 640 x 480 alpha fade between two images
Post by: MrP on September 15, 2006
ok i'll post up a version later tonight (Friday) that just blits two images with an alpha value.
Title: Re: Full screen 640 x 480 alpha fade between two images
Post by: MrP on September 15, 2006
OK Here it is alpha blitting rather than the other method, christ this is a lot quicker than I thought it would be, im maxing out at about 238 fps  :) :) :)

I'll include two files one is just the exe if you've still got the two images, the other has all the lot... Enjoy....

[oversized attachment deleted by admin]
Title: Re: Full screen 640 x 480 alpha fade between two images
Post by: Shockwave on September 15, 2006
Wahey! Thant's more like it, it's fairly belting along at 223 fps now :) Nice work.
Title: Re: Full screen 640 x 480 alpha fade between two images
Post by: MrP on September 15, 2006
do you know when you do something no matter how simple it is and you just can't stop f!#*ing looking at it, needless to say im a bit chuffed with that....
Title: Re: Full screen 640 x 480 alpha fade between two images
Post by: Clyde on September 15, 2006
I get my full monitor refresh rate of 75/76 welldone mate.
Title: Re: Full screen 640 x 480 alpha fade between two images
Post by: Shockwave on September 15, 2006
I had the self same feeling when I finished my first dbf intro mate :) Enjoy it, there's more to come.
Title: Re: Full screen 640 x 480 alpha fade between two images
Post by: Rbz on September 15, 2006
I get 64 fps now, welldone!
Title: Re: Full screen 640 x 480 alpha fade between two images
Post by: MrP on September 16, 2006
Heres my latest routine to blit any size image to screen, rather than the drawing method i used before. This is tons faster and on full screen images theres hardly any drop in frame rate from the full screen blit i put up in my last post.......

@Jim if you happen to take a look at this, I would very much appreciate your thoughts on it, I've tried to take into account what you said on your last reply and hopefully i'm somewhere closer to where I should be. Many thanks in advance MrP.....

Code: [Select]
sub pgl_blit_image(byval src_buffer as integer ptr, byval x as integer, byval y as integer, byval alpha as ubyte)
    dim p_alpha as integer = rgb(alpha, alpha, alpha)
    asm
        mov ebx, [pgl_screen_ptr]   'ebx has screen pointer

        mov esi, [x]                        'x location in esi
        mov edi, [y]                       'y location in edi
       
        imul edi, [pgl_screen_x]    'calculate screen y position
        add edi, esi                'then the x position
        lea ebx, [ebx + edi * 4]    'add the result to our screen pointer

        mov eax, [src_buffer]       'start of source buffer in eax
        mov edi, [eax]              'image width into edi
        mov edx, [pgl_blit_inc]     'move screen line increment into edx
        sub edx, edi                'subtract the width of our image
                                    'edx now has the amount we need to add to screen pointer
                                    'at the end of each line of source image
        add eax, 8                  'move eax to byte 8 so we can get image length
        mov ecx, [eax]              'length of image for drawing completion check into ecx
        add eax, 4                  'move eax to beginning of image data
        add ecx, eax                'add start of image to end of image so we have true pointer to end
       
        pxor mm6, mm6               'blank register mm6
        movd mm2, [p_alpha]         'mm2 has alpha value
        punpcklbw mm2, mm6          'unpack it for processing
       
        mov esi, 0                  'esi will be counter for source image x location
       
        idloop:
            movd mm0, [eax]         'image pixel col in mm0
            movd mm1, [ebx]         'screen pixel col in mm2
       
            punpcklbw mm0, mm6      'unpack mm0 using blank mm6 register as interleave
            punpcklbw mm1, mm6      'same for screen color
   
            psubw mm0, mm1          'subtract image color from screen color
            pmullw mm0, mm2         'multiply resulting color by alpha
            psrlw mm0, 8            'divide that by 255
            paddb mm0, mm1          'and then add screen color back in

            packuswb mm0, mm0       'repack image pixel back to lower 32 bits of mm0
            movd [ebx], mm0         'move result back to screen

            add ebx, 4              'increment our screen pointer
            add eax, 4              'increment our source pointer
            add esi, 4              'increment our source x loxation
           
            cmp eax, ecx            'have we got to the end of image
            jz enddraw              'if we have jump out of drawing loop
           
            cmp esi, edi            'are we at the end of a line in our image
            ja incline              'if we are jump to add line bit
           
        jmp idloop                  'if none of the above are met jump back to beginning of draw loop
           
            incline:
            add ebx, edx            'add increment to screen pointer to get to right position on screen for next line
            mov esi, 0              'reset our image x counter
        jmp idloop                  'jump back to beginning of draw loop
           
            enddraw:
            emms                    'restore floating point registers
    end asm
end sub
Title: Re: Full screen 640 x 480 alpha fade between two images
Post by: Jim on September 17, 2006
That looks great now, good effort!  Another 30fps faster, now 135fps!

There's one way to make this a fraction faster still, and that is to mix the ordinary CPU instructions in with the MMX ones.  So instead of going
O=ordinary
M=mmx
O,O,M,M,M,M,M,M,O,O,O,O,
try to get it to go
O,M,O,M,O,M,O,M,O,M,O,M,

The reason for that is that each MMX instrution takes more than 1 clock tick, so if the next instruction is waiting for the result of the previous one, eg.
            psubw mm0, mm1          'subtract image color from screen color
            pmullw mm0, mm2         'multiply resulting color by alpha
here, the mul can't begin until the sub has taken place - it needs mm0 to be ready.  Internally then to the cpu, there's a gap where nothing is happening between the two instructions which is being wasted.  The solution to this is to move one of your other instructions into that gap and take advantage of the overlap.
So, you might move one of the adds for your pointers in there.
eg.
            psubw mm0, mm1          'subtract image color from screen color
            add eax, 4                     'increment our source pointer
            pmullw mm0, mm2         'multiply resulting color by alpha
effectively that gives you the add for free.

It'll be interesting to see whether that makes any difference.

Some other possibilities
        ja incline              'if we are jump to add line bit   
        jmp idloop                  'if none of the above are met jump back to beginning of draw loop
incline:
I'm sure you've done this for clarity, but you can replace that with
        jbe idloop
incline:

You seem to have 3 counters, but only 2 sources, which means you've got an extra
add reg,4
in your loop.  Can you move that outside the scanline loop so you're only adding/checking once per scanline instead of once per pixel?

Some very useful information here http://www.agner.org/optimize/.  Agner Fog is a total expert on how to wring the last cycle out of Intel chips!
If you look in the instruction_tables.pdf for P4 timings in there, you will see the latency entry for psub and pmullw.  There's a gap of 1 after a psub, and 5 after a pmul.

Jim
Title: Re: Full screen 640 x 480 alpha fade between two images
Post by: MrP on September 17, 2006
Thanks very much for that detailed explanation Jim..... Would I be right in assuming that while your waiting for the other instructions to finish your effectively using the other pipe. I've read about the U and V pipe and the fact you can pair instructions so that your not wasting clock cycles. I've only really touched on that and i'm not really sure what you can pair and what you cant.... Anyways Thanks once again for your input to this, it is very much appreciated.
Title: Re: Full screen 640 x 480 alpha fade between two images
Post by: Jim on September 17, 2006
u and v pipes are only found on the original Pentium.  Basically all integer instructions (mov, add, cmp etc) could go down either pipe, in pairs, except shifts, muls and divides.  So every instruction took 1/2 cycle.  All the pairing had to be worked out by hand or by the compiler.  With newer CPUs it's not so cut-and-dried.  Each integer instruction is broken down into u-ops (micro-ops) and then there are a number of excution units that can process these ops, either in or out of order.  So the CPU is kind of recompiling the code dynamically.  MMX, XMM and FPU instructions are broken up the same way.  The idea is, to get maximum speed, to get all these excution units firing all the time.  So effectively you have far more than just uv pipes.  Hyperthreading was invented as an automatic way of making sure these units are all working at once - if there's not enough instructions coming in from one thread, find another thread to start executing too!

You're nearly going as fast as you can with this routine, by the way.  You'll end up being limited entirely by how slow it is to read the two screens, write them out, and blit the result using ptc.  Just it would be great to see if the pipelining squeezes a little bit more out first. :D

Jim
Title: Re: Full screen 640 x 480 alpha fade between two images
Post by: MrP on September 18, 2006
OK jim moved the increment to eax to after the psrlw mm0, and got rid of the extra add to esi per pixel you mentioned and squezed another 5 fps or so out of it. Tried the add to eax in various places within the mmx code, but where it sits now is definately faster then the rest.... Cheers again for your help on this. I'm rather pleased with this now......

Oh and if clicking the applaud thingy gives you some karma, which I'm assuming it does, then you can have some of that along with my thanks.....
Title: Re: Full screen 640 x 480 alpha fade between two images
Post by: Shockwave on September 18, 2006
Applaud does indeed give positive Karma :)

Squeezing an extra 5fps out of this when it was already fast is not small beans either. Well done Mr. P :) Whoops I just pressed your applaud button!
Title: Re: Full screen 640 x 480 alpha fade between two images
Post by: Jim on September 19, 2006
Indeedy!  Squeezing more fps out by reordering the instructions is pretty significant. Good job!

Jim