Ufh!! 8-O I've finally (& hopefully) finished the rewrite of Mesa's MMX blend code.
The code is configurable - allowing to choose several methods for the blending equation. Here are the benchmarks that I made (on a Pentium III 700Mhz): C code: 8.142382 sec Old MMX code: 4.363946 sec exact (single multiply w/o rounding): 3.637088 sec <= fastest that will satisfy glean exact (two multiplies w/o rounding): 3.817152 sec approx (single multiply w/o rouding): 3.476336 sec approx (two multiplies w/o rounding: 3.629378 sec approx (single multiply with alpha+1): 3.250325 sec Attached is the file and the testsuite I used (note that to use the testsuite it's needed to remove the static and ASSERTs from _mesa_mmx_blend_transparency since this function is called directly). I'll eventually make something very similar to this on the lines of the "double blend trick" in the C code, but it will stay on hold because I feel I have to dedicate myself to mach64 again. Regards, Jos� Fonseca PS: I've CC'd to dri-devel because this subject was first raised on the DRI meeting, but I'm not sure if there is a real interest since most of its subscribers eventually subscribe mesa3d-dev as well... or am I wrong? PPS: I didn't name this thread "Mesa software blending" for the obvious reasons... :-)
/*
* Written by Jos� Fonseca <[EMAIL PROTECTED]>
*/
#include "matypes.h"
/*
* make the following approximation to the division (Sree)
*
* rgb*a/255 ~= (rgb*(a+1)) >> 256
*
* which is the fastest method that satisfies the following OpenGL criteria
*
* 0*0 = 0 and 255*255 = 255
*
* note this one should be used alone
*/
#define GMBT_ALPHA_PLUS_ONE 0
/*
* take the geometric series approximation to the division
*
* t/255 = (t >> 8) + (t >> 16) + (t >> 24) ..
*
* in this case just the first two terms to fit in 16bit arithmetic
*
* t/255 ~= (t + (t >> 8)) >> 8
*
* note that just by itself it doesn't satisfies the OpenGL criteria, as 255*255 = 254,
* so the special case a = 255 must be accounted or roundoff must be used
*/
#define GMBT_GEOMETRIC_SERIES 1
/*
* when using a geometric series division instead of truncating the result
* use roundoff in the approximation (Jim Blinn)
*
* t = rgb*a + 0x80
*
* achieving the exact results
*/
#define GMBT_ROUNDOFF 1
/*
* do
*
* s = (q - p)*a + q
*
* instead of
*
* s = p*a + q*(1-a)
*
* this eliminates a multiply at the expense of
* complicating the roundoff but is generally worth it
*/
#define GMBT_SIGNED_ARITHMETIC 1
#if GMBT_ROUNDOFF
SEG_DATA
ALIGNDATA8
const_80:
D_LONG 0x00800080, 0x00800080
#endif
SEG_TEXT
ALIGNTEXT16
GLOBL GLNAME(_mesa_mmx_blend_transparency)
/*
* void blend_transparency( GLcontext *ctx,
* GLuint n,
* const GLubyte mask[],
* GLchan rgba[][4],
* CONST GLchan dest[][4] )
*
* Common transparency blending mode.
*/
GLNAME( _mesa_mmx_blend_transparency ):
PUSH_L ( EBP )
MOV_L ( ESP, EBP )
PUSH_L ( ESI )
PUSH_L ( EDI )
PUSH_L ( EBX )
MOV_L ( REGOFF(12, EBP), ECX ) /* n */
CMP_L ( CONST(0), ECX)
JE ( LLBL (GMBT_return) )
MOV_L ( REGOFF(16, EBP), EBX ) /* mask */
MOV_L ( REGOFF(20, EBP), EDI ) /* rgba */
MOV_L ( REGOFF(24, EBP), ESI ) /* dest */
TEST_L ( CONST(4), EDI ) /* align rgba on an 8-byte boundary */
JZ ( LLBL (GMBT_align_end) )
CMP_B ( CONST(0), REGIND(EBX) ) /* *mask == 0 */
JE ( LLBL (GMBT_align_continue) )
PXOR ( MM0, MM0 ) /* 0x0000 | 0x0000 | 0x0000 | 0x0000 */
MOVD ( REGIND(ESI), MM1 ) /* | | | | qa1 | qb1 | qg1 | qr1 */
MOVD ( REGIND(EDI), MM2 ) /* | | | | pa1 | pb1 | pg1 | pr1 */
PUNPCKLBW ( MM0, MM1 ) /* qa1 | qb1 | qg1 | qr1 */
PUNPCKLBW ( MM0, MM2 ) /* pa1 | pb1 | pg1 | pr1 */
MOVQ ( MM2, MM3 )
PUNPCKHWD ( MM3, MM3 ) /* pa1 | pa1 | | */
PUNPCKHDQ ( MM3, MM3 ) /* pa1 | pa1 | pa1 | pa1 */
#if GMBT_ALPHA_PLUS_ONE
PCMPEQW ( MM4, MM4 ) /* 0xffff | 0xffff | 0xffff | 0xffff */
PSUBW ( MM4, MM3 ) /* pa1 + 1 | pa1 + 1 | pa1 + 1 | pa1 + 1 */
#endif
#if GMBT_SIGNED_ARITHMETIC
PSUBW ( MM1, MM2 ) /* pa1 - qa1 | pb1 - qb1 | pg1 - qg1 | pr1 - qr1 */
PSLLW ( CONST(8), MM1 ) /* q1 << 8 */
#if GMBT_ROUNDOFF
MOVQ ( MM2, MM4 )
#endif
PMULLW ( MM3, MM2 ) /* t1 = (q1 - p1)*pa1 */
#if GMBT_ROUNDOFF
PSRLW ( CONST(15), MM4 ) /* q1 > p1 ? 1 : 0 */
PSLLW ( CONST(8), MM4 ) /* q1 > p1 ? 0x100 : 0 */
PSUBW ( MM4, MM2 ) /* t1 -=? 0x100 */
#endif
#else
PCMPEQW ( MM4, MM4 ) /* 0xffff | 0xffff | 0xffff | 0xffff */
PUNPCKLBW ( MM0, MM4 ) /* 0x00ff | 0x00ff | 0x00ff | 0x00ff */
MOVQ ( MM4, MM0 )
PMULLW ( MM3, MM2 ) /* p1*pa1 */
PSUBW ( MM3, MM0 ) /* 255 - pa1 | 255 - pa1 | 255 - pa1 | 255 - pa1 */
PMULLW ( MM0, MM1 ) /* q1*(255 - pa1) */
PADDW ( MM1, MM2 ) /* t1 = p1*pa1 + q1*(255 - pa1) */
#endif
#if GMBT_ROUNDOFF
MOVQ ( CONTENT(const_80), MM4 )
PADDW ( MM4, MM2 ) /* t1 += 0x80 */
#endif
#if GMBT_GEOMETRIC_SERIES
MOVQ ( MM2, MM3 )
PSRLW ( CONST(8), MM3 ) /* t1 >> 8 */
PADDW ( MM3, MM2 ) /* t1 + (t1 >> 8) ~= (t1/255) << 8 */
#endif
#if GMBT_SIGNED_ARITHMETIC
PADDW ( MM1, MM2 ) /* (t1/255 + q1) << 8 */
#endif
PSRLW ( CONST(8), MM2 ) /* sa1 | sb1 | sg1 | sr1 */
PACKUSWB ( MM0, MM2 ) /* | | | | sa1 | sb1 | sg1 | sr1 */
MOVD ( MM2, REGIND(EDI) )
LLBL (GMBT_align_continue):
DEC_L ( ECX ) /* n -= 1 */
INC_L ( EBX ) /* mask += 1 */
ADD_L ( CONST(4), EDI ) /* rgba += 1 */
ADD_L ( CONST(4), ESI ) /* dest += 1 */
LLBL (GMBT_align_end):
CMP_L ( CONST(2), ECX)
JB ( LLBL (GMBT_loop_end) )
ALIGNTEXT16
LLBL (GMBT_loop_begin):
CMP_W ( CONST(0), REGIND(EBX) ) /* *mask == 0 && *(mask + 1) == 0 */
JE ( LLBL (GMBT_loop_continue) )
/* NOTE: the instruction pairing when multiple pipelines are available must be checked */
PXOR ( MM0, MM0 ) /* 0x0000 | 0x0000 | 0x0000 | 0x0000 */
MOVQ ( REGIND(ESI), MM7 ) /* qa2 | qb2 | qg2 | qr2 | qa1 | qb1 | qg1 | qr1 */
MOVQ ( REGIND(EDI), MM6 ) /* pa2 | pb2 | pg2 | pr2 | pa1 | pb1 | pg1 | pr1 */
MOVQ ( MM7, MM1 )
MOVQ ( MM6, MM2 )
PUNPCKLBW ( MM0, MM1 ) /* qa1 | qb1 | qg1 | qr1 */
PUNPCKHBW ( MM0, MM7 ) /* qa2 | qb2 | qg2 | qr2 */
PUNPCKLBW ( MM0, MM2 ) /* pa1 | pb1 | pg1 | pr1 */
PUNPCKHBW ( MM0, MM6 ) /* pa2 | pb2 | pg2 | pr2 */
MOVQ ( MM2, MM3 )
MOVQ ( MM6, MM5 )
PUNPCKHWD ( MM3, MM3 ) /* pa1 | pa1 | | */
PUNPCKHWD ( MM5, MM5 ) /* pa2 | pa2 | | */
PUNPCKHDQ ( MM3, MM3 ) /* pa1 | pa1 | pa1 | pa1 */
PUNPCKHDQ ( MM5, MM5 ) /* pa2 | pa2 | pa2 | pa2 */
#if GMBT_ALPHA_PLUS_ONE
PCMPEQW ( MM4, MM4 ) /* 0xffff | 0xffff | 0xffff | 0xffff */
PSUBW ( MM4, MM3 ) /* pa1 + 1 | pa1 + 1 | pa1 + 1 | pa1 + 1 */
PSUBW ( MM4, MM5 ) /* pa2 + 1 | pa2 + 1 | pa2 + 1 | pa2 + 1 */
#endif
#if GMBT_SIGNED_ARITHMETIC
PSUBW ( MM1, MM2 ) /* pa1 - qa1 | pb1 - qb1 | pg1 - qg1 | pr1 - qr1 */
PSUBW ( MM7, MM6 ) /* pa2 - qa2 | pb2 - qb2 | pg2 - qg2 | pr2 - qr2 */
PSLLW ( CONST(8), MM1 ) /* q1 << 8 */
PSLLW ( CONST(8), MM7 ) /* q2 << 8 */
#if GMBT_ROUNDOFF
MOVQ ( MM2, MM0 )
MOVQ ( MM6, MM4 )
#endif
PMULLW ( MM3, MM2 ) /* t1 = (q1 - p1)*pa1 */
PMULLW ( MM5, MM6 ) /* t2 = (q2 - p2)*pa2 */
#if GMBT_ROUNDOFF
PSRLW ( CONST(15), MM0 ) /* q1 > p1 ? 1 : 0 */
PSRLW ( CONST(15), MM4 ) /* q2 > q2 ? 1 : 0 */
PSLLW ( CONST(8), MM0 ) /* q1 > p1 ? 0x100 : 0 */
PSLLW ( CONST(8), MM4 ) /* q2 > q2 ? 0x100 : 0 */
PSUBW ( MM0, MM2 ) /* t1 -=? 0x100 */
PSUBW ( MM4, MM7 ) /* t2 -=? 0x100 */
#endif
#else
PCMPEQW ( MM4, MM4 ) /* 0xffff | 0xffff | 0xffff | 0xffff */
PUNPCKLBW ( MM0, MM4 ) /* 0x00ff | 0x00ff | 0x00ff | 0x00ff */
MOVQ ( MM4, MM0 )
PMULLW ( MM3, MM2 ) /* p1*pa1 */
PMULLW ( MM5, MM6 ) /* p2*pa2 */
PSUBW ( MM3, MM0 ) /* 255 - pa1 | 255 - pa1 | 255 - pa1 | 255 - pa1 */
PSUBW ( MM5, MM4 ) /* 255 - pa2 | 255 - pa2 | 255 - pa2 | 255 - pa2 */
PMULLW ( MM0, MM1 ) /* q1*(255 - pa1) */
PMULLW ( MM4, MM7 ) /* q2*(255 - pa2) */
PADDW ( MM1, MM2 ) /* t1 = p1*pa1 + q1*(255 - pa1) */
PADDW ( MM7, MM6 ) /* t2 = p2*pa2 + q2*(255 - pa2) */
#endif
#if GMBT_ROUNDOFF
MOVQ ( CONTENT(const_80), MM4 )
PADDW ( MM4, MM2 ) /* t1 += 0x80 */
PADDW ( MM4, MM6 ) /* t2 += 0x80 */
#endif
#if GMBT_GEOMETRIC_SERIES
MOVQ ( MM2, MM3 )
MOVQ ( MM6, MM5 )
PSRLW ( CONST(8), MM3 ) /* t1 >> 8 */
PSRLW ( CONST(8), MM5 ) /* t2 >> 8 */
PADDW ( MM3, MM2 ) /* t1 + (t1 >> 8) ~= (t1/255) << 8 */
PADDW ( MM5, MM6 ) /* t2 + (t2 >> 8) ~= (t2/255) << 8 */
#endif
#if GMBT_SIGNED_ARITHMETIC
PADDW ( MM1, MM2 ) /* (t1/255 + q1) << 8 */
PADDW ( MM7, MM6 ) /* (t2/255 + q2) << 8 */
#endif
PSRLW ( CONST(8), MM2 ) /* sa1 | sb1 | sg1 | sr1 */
PSRLW ( CONST(8), MM6 ) /* sa2 | sb2 | sg2 | sr2 */
PACKUSWB ( MM6, MM2 ) /* sa2 | sb2 | sg2 | sr2 | sa1 | sb1 | sg1 | sr1 */
MOVQ ( MM2, REGIND(EDI) )
LLBL (GMBT_loop_continue):
DEC_L ( ECX )
DEC_L ( ECX ) /* n -= 2 */
ADD_L ( CONST(2), EBX ) /* mask += 2 */
ADD_L ( CONST(8), EDI ) /* rgba += 2 */
ADD_L ( CONST(8), ESI ) /* dest += 2 */
CMP_L ( CONST(2), ECX )
JAE ( LLBL (GMBT_loop_begin) )
LLBL (GMBT_loop_end):
CMP_L ( CONST(1), ECX )
JB ( LLBL (GMBT_done) )
CMP_B ( CONST(0), REGIND(EBX) ) /* *mask == 0 */
JE ( LLBL (GMBT_done) )
PXOR ( MM0, MM0 ) /* 0x0000 | 0x0000 | 0x0000 | 0x0000 */
MOVD ( REGIND(ESI), MM1 ) /* | | | | qa1 | qb1 | qg1 | qr1 */
MOVD ( REGIND(EDI), MM2 ) /* | | | | pa1 | pb1 | pg1 | pr1 */
PUNPCKLBW ( MM0, MM1 ) /* qa1 | qb1 | qg1 | qr1 */
PUNPCKLBW ( MM0, MM2 ) /* pa1 | pb1 | pg1 | pr1 */
MOVQ ( MM2, MM3 )
PUNPCKHWD ( MM3, MM3 ) /* pa1 | pa1 | | */
PUNPCKHDQ ( MM3, MM3 ) /* pa1 | pa1 | pa1 | pa1 */
#if GMBT_ALPHA_PLUS_ONE
PCMPEQW ( MM4, MM4 ) /* 0xffff | 0xffff | 0xffff | 0xffff */
PSUBW ( MM4, MM3 ) /* pa1 + 1 | pa1 + 1 | pa1 + 1 | pa1 + 1 */
#endif
#if GMBT_SIGNED_ARITHMETIC
PSUBW ( MM1, MM2 ) /* pa1 - qa1 | pb1 - qb1 | pg1 - qg1 | pr1 - qr1 */
PSLLW ( CONST(8), MM1 ) /* q1 << 8 */
#if GMBT_ROUNDOFF
MOVQ ( MM2, MM4 )
#endif
PMULLW ( MM3, MM2 ) /* t1 = (q1 - p1)*pa1 */
#if GMBT_ROUNDOFF
PSRLW ( CONST(15), MM4 ) /* q1 > p1 ? 1 : 0 */
PSLLW ( CONST(8), MM4 ) /* q1 > p1 ? 0x100 : 0 */
PSUBW ( MM4, MM2 ) /* t1 -=? 0x100 */
#endif
#else
PCMPEQW ( MM4, MM4 ) /* 0xffff | 0xffff | 0xffff | 0xffff */
PUNPCKLBW ( MM0, MM4 ) /* 0x00ff | 0x00ff | 0x00ff | 0x00ff */
MOVQ ( MM4, MM0 )
PMULLW ( MM3, MM2 ) /* p1*pa1 */
PSUBW ( MM3, MM0 ) /* 255 - pa1 | 255 - pa1 | 255 - pa1 | 255 - pa1 */
PMULLW ( MM0, MM1 ) /* q1*(255 - pa1) */
PADDW ( MM1, MM2 ) /* t1 = p1*pa1 + q1*(255 - pa1) */
#endif
#if GMBT_ROUNDOFF
MOVQ ( CONTENT(const_80), MM4 )
PADDW ( MM4, MM2 ) /* t1 += 0x80 */
#endif
#if GMBT_GEOMETRIC_SERIES
MOVQ ( MM2, MM3 )
PSRLW ( CONST(8), MM3 ) /* t1 >> 8 */
PADDW ( MM3, MM2 ) /* t1 + (t1 >> 8) ~= (t1/255) << 8 */
#endif
#if GMBT_SIGNED_ARITHMETIC
PADDW ( MM1, MM2 ) /* (t1/255 + q1) << 8 */
#endif
PSRLW ( CONST(8), MM2 ) /* sa1 | sb1 | sg1 | sr1 */
PACKUSWB ( MM0, MM2 ) /* | | | | sa1 | sb1 | sg1 | sr1 */
MOVD ( MM2, REGIND(EDI) )
LLBL (GMBT_done):
EMMS
LLBL (GMBT_return):
POP_L ( EBX )
POP_L ( EDI )
POP_L ( ESI )
MOV_L ( EBP, ESP )
POP_L ( EBP )
RET
testsuite.tar.gz
Description: GNU Zip compressed data
