pea posted on Sep 4 2005 at 11:35 PM said:
Here is my final solution. I used una-i's solution of masking out the bits higher. yet to test it:
Code:
unsigned short addColorChannel( const unsigned short dest, const unsigned short source, unsigned long shift){
unsigned dest_a = (dest>>shift) & 0x1F; // Destination colour channel in lower 5 bits
unsigned source_a = (source>>shift) & 0x1F; // Source colour channel in lower 5 bits
dest_a = source_a = dest_a + source_a; // Sum of colours including overflow
source_a &= 0x20; // Mask out overflow bit
source_a -= (source_a>>5); // Create channel saturation mask from overflow bit
dest_a |= source_a; // Mask overflow
return (dest & ~(0x1F<<shift)) | (dest_a & 0x1F)<<shift; // Mask out channel and add back to dest
}
Where:
dest is colour at destination
source is colour at source
shift is dependant on channel to mix (11=R, 6=G, 1=B )
What it should do:
Blend a single channel of the source colour with the whole destination colour
Doubts:
Need to benchmark it because I have my doubts that:
5 shifts, an addition, a subtraction and 7 binary operations
is faster than:
1 comparison/branch, 3 shifts, an addition and 2 binary operations
But then, what do I know
I may sound a bit dickish, but this board is for discussing code aswell, so I wonder if my thoughts are correct AND I am still of the opinion that my code is the fastest
My proposal has:
2 shifts ( needed for aligning the R G B values, could be leaved out for B value!)
2 bitwise-operations ( needed to mask out all unneeded bits.
1 add ( obviusoly needed for ADDitive-blending
1 compare ( which is the same computation as arithmetik ( SUB)
and 1 simple move which is just executed when saturation should be done!
BTW, the "mov r4, #$1f" isn't needed, cause immeadite is as fast as register addressing, so just replace all "r4" to "#$1f", like: "and r0, r0, #$1f".
So we have 6 or 7 instructions compared to the 12 ( without branch) and the 7 ( with branch).
One of the key ideas is, that the saturated value and the AND-mask are always identical! The only thing I dont know by heart is, if the CPU issues a real NOP instruction when the condition for the "movGT" instruction isn't met and if then a pipeline flush occures as well. I see no need for that, but if it is implemented that way in the ARM 9 core, then my proposal hjas to be overthougth
You can further use a specilized route for every channel, then you can get rid of the shifting, dont forget, you have to shift back your result, so you can save always a shift more for R and G channles.
looks like this:
Code:
MASK_R equ $1f << 11
MASK_G equ $1f << 6
MASK_B equ $1f
; for each channel do like the following
and r0, r2, #MASK_R; mask off
and r1, r3, #MASK_R;
add r0, r0, r1 ; mix colors
cmp r0, #MASK_R ; check for saturation
mov r0, #MASK_R ; saturated value ( if needed)
Further optimization is ofcourse possible when you caluclate two (16 bit) pixels in one (32-bit) word, then you have to use the carry bit for the overflow of the highest Red bit. But this approache is not so generic but ( obviuosly) delivers double the speed
I would recommend to compile your C code to assembnler src ( -S option in GCC if I am not mistaken) and count the instructions. Because it is not guaranteed that the C compiler translates every C instruction with one ARM 9 instruction
If your problem is to use the asm code, I can try to make a C macro for this ( asm statement). I never done it before, so I can't add it now.
Any corrections are highly welcomed, cause I have no time to test it and therefore can't say it is 100% correct. Sorry for this
salut,
creature