s/16/32/ (Josh Vanderhoof)
[mesa.git] / src / mesa / x86 / mmx_blend.S
1 /*
2 * Written by José Fonseca <j_r_fonseca@yahoo.co.uk>
3 */
4
5 #include "matypes.h"
6
7
8 /* integer multiplication - alpha plus one
9 *
10 * makes the following approximation to the division (Sree)
11 *
12 * rgb*a/255 ~= (rgb*(a+1)) >> 256
13 *
14 * which is the fastest method that satisfies the following OpenGL criteria
15 *
16 * 0*0 = 0 and 255*255 = 255
17 *
18 * note that MX1 is a register with 0xffffffffffffffff constant which can be easily obtained making
19 *
20 * PCMPEQW ( MX1, MX1 )
21 */
22 #define GMB_MULT_AP1( MP1, MA1, MP2, MA2, MX1 ) \
23 PSUBW ( MX1, MA1 ) /* a1 + 1 | a1 + 1 | a1 + 1 | a1 + 1 */ ;\
24 PMULLW ( MP1, MA1 ) /* t1 = p1*a1 */ ;\
25 ;\
26 TWO(PSUBW ( MX1, MA2 )) /* a2 + 1 | a2 + 1 | a2 + 1 | a2 + 1 */ ;\
27 TWO(PMULLW ( MP2, MA2 )) /* t2 = p2*a2 */ ;\
28 ;\
29 PSRLW ( CONST(8), MA1 ) /* t1 >> 8 ~= t1/255 */ ;\
30 TWO(PSRLW ( CONST(8), MA2 )) /* t2 >> 8 ~= t2/255 */
31
32
33 /* integer multiplication - geometric series
34 *
35 * takes the geometric series approximation to the division
36 *
37 * t/255 = (t >> 8) + (t >> 16) + (t >> 24) ..
38 *
39 * in this case just the first two terms to fit in 16bit arithmetic
40 *
41 * t/255 ~= (t + (t >> 8)) >> 8
42 *
43 * note that just by itself it doesn't satisfies the OpenGL criteria, as 255*255 = 254,
44 * so the special case a = 255 must be accounted or roundoff must be used
45 */
46 #define GMB_MULT_GS( MP1, MA1, MP2, MA2 ) \
47 PMULLW ( MP1, MA1 ) /* t1 = p1*a1 */ ;\
48 TWO(PMULLW ( MP2, MA2 )) /* t2 = p2*a2 */ ;\
49 ;\
50 MOVQ ( MA1, MP1 ) ;\
51 PSRLW ( CONST(8), MA1 ) /* t1 >> 8 */ ;\
52 ;\
53 TWO(MOVQ ( MA2, MP2 )) ;\
54 TWO(PSRLW ( CONST(8), MA2 )) /* t2 >> 8 */ ;\
55 ;\
56 PADDW ( MP1, MA1 ) /* t1 + (t1 >> 8) ~= (t1/255) << 8 */ ;\
57 PSRLW ( CONST(8), MA1 ) /* sa1 | sb1 | sg1 | sr1 */ ;\
58 ;\
59 TWO(PADDW ( MP2, MA2 )) /* t2 + (t2 >> 8) ~= (t2/255) << 8 */ ;\
60 TWO(PSRLW ( CONST(8), MA2 )) /* sa2 | sb2 | sg2 | sr2 */
61
62
63 /* integer multiplication - geometric series plus rounding
64 *
65 * when using a geometric series division instead of truncating the result
66 * use roundoff in the approximation (Jim Blinn)
67 *
68 * t = rgb*a + 0x80
69 *
70 * achieving the exact results
71 *
72 * note that M80 is register with the 0x0080008000800080 constant
73 */
74 #define GMB_MULT_GSR( MP1, MA1, MP2, MA2, M80 ) \
75 PMULLW ( MP1, MA1 ) /* t1 = p1*a1 */ ;\
76 PADDW ( M80, MA1 ) /* t1 += 0x80 */ ;\
77 ;\
78 TWO(PMULLW ( MP2, MA2 )) /* t2 = p2*a2 */ ;\
79 TWO(PADDW ( M80, MA2 )) /* t2 += 0x80 */ ;\
80 ;\
81 MOVQ ( MA1, MP1 ) ;\
82 PSRLW ( CONST(8), MA1 ) /* t1 >> 8 */ ;\
83 ;\
84 TWO(MOVQ ( MA2, MP2 )) ;\
85 TWO(PSRLW ( CONST(8), MA2 )) /* t2 >> 8 */ ;\
86 ;\
87 PADDW ( MP1, MA1 ) /* t1 + (t1 >> 8) ~= (t1/255) << 8 */ ;\
88 PSRLW ( CONST(8), MA1 ) /* sa1 | sb1 | sg1 | sr1 */ ;\
89 ;\
90 TWO(PADDW ( MP2, MA2 )) /* t2 + (t2 >> 8) ~= (t2/255) << 8 */ ;\
91 TWO(PSRLW ( CONST(8), MA2 )) /* sa2 | sb2 | sg2 | sr2 */
92
93
94 /* linear interpolation - geometric series
95 */
96 #define GMB_LERP_GS( MP1, MQ1, MA1, MP2, MQ2, MA2) \
97 PSUBW ( MQ1, MP1 ) /* pa1 - qa1 | pb1 - qb1 | pg1 - qg1 | pr1 - qr1 */ ;\
98 PSLLW ( CONST(8), MQ1 ) /* q1 << 8 */ ;\
99 PMULLW ( MP1, MA1 ) /* t1 = (q1 - p1)*pa1 */ ;\
100 ;\
101 TWO(PSUBW ( MQ2, MP2 )) /* pa2 - qa2 | pb2 - qb2 | pg2 - qg2 | pr2 - qr2 */ ;\
102 TWO(PSLLW ( CONST(8), MQ2 )) /* q2 << 8 */ ;\
103 TWO(PMULLW ( MP2, MA2 )) /* t2 = (q2 - p2)*pa2 */ ;\
104 ;\
105 MOVQ ( MA1, MP1 ) ;\
106 PSRLW ( CONST(8), MA1 ) /* t1 >> 8 */ ;\
107 ;\
108 TWO(MOVQ ( MA2, MP2 )) ;\
109 TWO(PSRLW ( CONST(8), MA2 )) /* t2 >> 8 */ ;\
110 ;\
111 PADDW ( MP1, MA1 ) /* t1 + (t1 >> 8) ~= (t1/255) << 8 */ ;\
112 TWO(PADDW ( MP2, MA2 )) /* t2 + (t2 >> 8) ~= (t2/255) << 8 */ ;\
113 ;\
114 PADDW ( MQ1, MA1 ) /* (t1/255 + q1) << 8 */ ;\
115 TWO(PADDW ( MQ2, MA2 )) /* (t2/255 + q2) << 8 */ ;\
116 ;\
117 PSRLW ( CONST(8), MA1 ) /* sa1 | sb1 | sg1 | sr1 */ ;\
118 TWO(PSRLW ( CONST(8), MA2 )) /* sa2 | sb2 | sg2 | sr2 */
119
120
121 /* linear interpolation - geometric series with roundoff
122 *
123 * this is a generalization of Blinn's formula to signed arithmetic
124 *
125 * note that M80 is a register with the 0x0080008000800080 constant
126 */
127 #define GMB_LERP_GSR( MP1, MQ1, MA1, MP2, MQ2, MA2, M80) \
128 PSUBW ( MQ1, MP1 ) /* pa1 - qa1 | pb1 - qb1 | pg1 - qg1 | pr1 - qr1 */ ;\
129 PSLLW ( CONST(8), MQ1 ) /* q1 << 8 */ ;\
130 PMULLW ( MP1, MA1 ) /* t1 = (q1 - p1)*pa1 */ ;\
131 ;\
132 TWO(PSUBW ( MQ2, MP2 )) /* pa2 - qa2 | pb2 - qb2 | pg2 - qg2 | pr2 - qr2 */ ;\
133 TWO(PSLLW ( CONST(8), MQ2 )) /* q2 << 8 */ ;\
134 TWO(PMULLW ( MP2, MA2 )) /* t2 = (q2 - p2)*pa2 */ ;\
135 ;\
136 PSRLW ( CONST(15), MP1 ) /* q1 > p1 ? 1 : 0 */ ;\
137 TWO(PSRLW ( CONST(15), MP2 )) /* q2 > q2 ? 1 : 0 */ ;\
138 ;\
139 PSLLW ( CONST(8), MP1 ) /* q1 > p1 ? 0x100 : 0 */ ;\
140 TWO(PSLLW ( CONST(8), MP2 )) /* q2 > q2 ? 0x100 : 0 */ ;\
141 ;\
142 PSUBW ( MP1, MA1 ) /* t1 -=? 0x100 */ ;\
143 TWO(PSUBW ( MP2, MA2 )) /* t2 -=? 0x100 */ ;\
144 ;\
145 PADDW ( M80, MA1 ) /* t1 += 0x80 */ ;\
146 TWO(PADDW ( M80, MA2 )) /* t2 += 0x80 */ ;\
147 ;\
148 MOVQ ( MA1, MP1 ) ;\
149 PSRLW ( CONST(8), MA1 ) /* t1 >> 8 */ ;\
150 ;\
151 TWO(MOVQ ( MA2, MP2 )) ;\
152 TWO(PSRLW ( CONST(8), MA2 )) /* t2 >> 8 */ ;\
153 ;\
154 PADDW ( MP1, MA1 ) /* t1 + (t1 >> 8) ~= (t1/255) << 8 */ ;\
155 TWO(PADDW ( MP2, MA2 )) /* t2 + (t2 >> 8) ~= (t2/255) << 8 */ ;\
156 ;\
157 PADDW ( MQ1, MA1 ) /* (t1/255 + q1) << 8 */ ;\
158 TWO(PADDW ( MQ2, MA2 )) /* (t2/255 + q2) << 8 */ ;\
159 ;\
160 PSRLW ( CONST(8), MA1 ) /* sa1 | sb1 | sg1 | sr1 */ ;\
161 TWO(PSRLW ( CONST(8), MA2 )) /* sa2 | sb2 | sg2 | sr2 */
162
163
164 /* linear interpolation - geometric series with correction
165 *
166 * instead of the roundoff this adds a small correction to satisfy the OpenGL criteria
167 *
168 * t/255 ~= (t + (t >> 8) + (t >> 15)) >> 8
169 *
170 * note that although is faster than rounding off it doesn't give always the exact results
171 */
172 #define GMB_LERP_GSC( MP1, MQ1, MA1, MP2, MQ2, MA2) \
173 PSUBW ( MQ1, MP1 ) /* pa1 - qa1 | pb1 - qb1 | pg1 - qg1 | pr1 - qr1 */ ;\
174 PSLLW ( CONST(8), MQ1 ) /* q1 << 8 */ ;\
175 PMULLW ( MP1, MA1 ) /* t1 = (q1 - p1)*pa1 */ ;\
176 ;\
177 TWO(PSUBW ( MQ2, MP2 )) /* pa2 - qa2 | pb2 - qb2 | pg2 - qg2 | pr2 - qr2 */ ;\
178 TWO(PSLLW ( CONST(8), MQ2 )) /* q2 << 8 */ ;\
179 TWO(PMULLW ( MP2, MA2 )) /* t2 = (q2 - p2)*pa2 */ ;\
180 ;\
181 MOVQ ( MA1, MP1 ) ;\
182 PSRLW ( CONST(8), MA1 ) /* t1 >> 8 */ ;\
183 ;\
184 TWO(MOVQ ( MA2, MP2 )) ;\
185 TWO(PSRLW ( CONST(8), MA2 )) /* t2 >> 8 */ ;\
186 ;\
187 PADDW ( MA1, MP1 ) /* t1 + (t1 >> 8) ~= (t1/255) << 8 */ ;\
188 PSRLW ( CONST(7), MA1 ) /* t1 >> 15 */ ;\
189 ;\
190 TWO(PADDW ( MA2, MP2 )) /* t2 + (t2 >> 8) ~= (t2/255) << 8 */ ;\
191 TWO(PSRLW ( CONST(7), MA2 )) /* t2 >> 15 */ ;\
192 ;\
193 PADDW ( MP1, MA1 ) /* t1 + (t1 >> 8) + (t1 >>15) ~= (t1/255) << 8 */ ;\
194 TWO(PADDW ( MP2, MA2 )) /* t2 + (t2 >> 8) + (t2 >>15) ~= (t2/255) << 8 */ ;\
195 ;\
196 PADDW ( MQ1, MA1 ) /* (t1/255 + q1) << 8 */ ;\
197 TWO(PADDW ( MQ2, MA2 )) /* (t2/255 + q2) << 8 */ ;\
198 ;\
199 PSRLW ( CONST(8), MA1 ) /* sa1 | sb1 | sg1 | sr1 */ ;\
200 TWO(PSRLW ( CONST(8), MA2 )) /* sa2 | sb2 | sg2 | sr2 */
201
202
203 /* common blending setup code
204 *
205 * note that M00 is a register with 0x0000000000000000 constant which can be easily obtained making
206 *
207 * PXOR ( M00, M00 )
208 */
209 #define GMB_LOAD(rgba, dest, MPP, MQQ) \
210 ONE(MOVD ( REGIND(rgba), MPP )) /* | | | | qa1 | qb1 | qg1 | qr1 */ ;\
211 ONE(MOVD ( REGIND(dest), MQQ )) /* | | | | pa1 | pb1 | pg1 | pr1 */ ;\
212 ;\
213 TWO(MOVQ ( REGIND(rgba), MPP )) /* qa2 | qb2 | qg2 | qr2 | qa1 | qb1 | qg1 | qr1 */ ;\
214 TWO(MOVQ ( REGIND(dest), MQQ )) /* pa2 | pb2 | pg2 | pr2 | pa1 | pb1 | pg1 | pr1 */
215
216 #define GMB_UNPACK(MP1, MQ1, MP2, MQ2, M00) \
217 TWO(MOVQ ( MP1, MP2 )) ;\
218 TWO(MOVQ ( MQ1, MQ2 )) ;\
219 ;\
220 PUNPCKLBW ( M00, MQ1 ) /* qa1 | qb1 | qg1 | qr1 */ ;\
221 TWO(PUNPCKHBW ( M00, MQ2 )) /* qa2 | qb2 | qg2 | qr2 */ ;\
222 PUNPCKLBW ( M00, MP1 ) /* pa1 | pb1 | pg1 | pr1 */ ;\
223 TWO(PUNPCKHBW ( M00, MP2 )) /* pa2 | pb2 | pg2 | pr2 */
224
225 #define GMB_ALPHA(MP1, MA1, MP2, MA2) \
226 MOVQ ( MP1, MA1 ) ;\
227 TWO(MOVQ ( MP2, MA2 )) ;\
228 ;\
229 PUNPCKHWD ( MA1, MA1 ) /* pa1 | pa1 | | */ ;\
230 TWO(PUNPCKHWD ( MA2, MA2 )) /* pa2 | pa2 | | */ ;\
231 PUNPCKHDQ ( MA1, MA1 ) /* pa1 | pa1 | pa1 | pa1 */ ;\
232 TWO(PUNPCKHDQ ( MA2, MA2 )) /* pa2 | pa2 | pa2 | pa2 */
233
234 #define GMB_PACK( MS1, MS2 ) \
235 PACKUSWB ( MS2, MS1 ) /* sa2 | sb2 | sg2 | sr2 | sa1 | sb1 | sg1 | sr1 */ ;\
236
237 #define GMB_STORE(rgba, MSS ) \
238 ONE(MOVD ( MSS, REGIND(rgba) )) /* | | | | sa1 | sb1 | sg1 | sr1 */ ;\
239 TWO(MOVQ ( MSS, REGIND(rgba) )) /* sa2 | sb2 | sg2 | sr2 | sa1 | sb1 | sg1 | sr1 */
240
241
242 SEG_DATA
243
244 ALIGNDATA8
245 const_0080:
246 D_LONG 0x00800080, 0x00800080
247
248 const_80:
249 D_LONG 0x80808080, 0x80808080
250
251 SEG_TEXT
252
253
254 /* Blend transparency function
255 */
256
257 #define TAG(x) x##_transparency
258
259 #define INIT \
260 PXOR ( MM0, MM0 ) /* 0x0000 | 0x0000 | 0x0000 | 0x0000 */
261
262 #define MAIN( rgba, dest ) \
263 GMB_LOAD( rgba, dest, MM1, MM2 ) ;\
264 GMB_UNPACK( MM1, MM2, MM4, MM5, MM0 ) ;\
265 GMB_ALPHA( MM1, MM3, MM4, MM6 ) ;\
266 GMB_LERP_GSC( MM1, MM2, MM3, MM4, MM5, MM6 ) ;\
267 GMB_PACK( MM3, MM6 ) ;\
268 GMB_STORE( rgba, MM3 )
269
270 #include "mmx_blendtmp.h"
271
272
273 /* Blend add function
274 *
275 * FIXME: Add some loop unrolling here...
276 */
277
278 #define TAG(x) x##_add
279
280 #define INIT
281
282 #define MAIN( rgba, dest ) \
283 ONE(MOVD ( REGIND(rgba), MM1 )) /* | | | | qa1 | qb1 | qg1 | qr1 */ ;\
284 ONE(MOVD ( REGIND(dest), MM2 )) /* | | | | pa1 | pb1 | pg1 | pr1 */ ;\
285 ONE(PADDUSB ( MM2, MM1 )) ;\
286 ONE(MOVD ( MM1, REGIND(rgba) )) /* | | | | sa1 | sb1 | sg1 | sr1 */ ;\
287 ;\
288 TWO(MOVQ ( REGIND(rgba), MM1 )) /* qa2 | qb2 | qg2 | qr2 | qa1 | qb1 | qg1 | qr1 */ ;\
289 TWO(PADDUSB ( REGIND(dest), MM1 )) /* sa2 | sb2 | sg2 | sr2 | sa1 | sb1 | sg1 | sr1 */ ;\
290 TWO(MOVQ ( MM1, REGIND(rgba) ))
291
292 #include "mmx_blendtmp.h"
293
294
295 /* Blend min function
296 */
297
298 #define TAG(x) x##_min
299
300 #define INIT \
301 MOVQ ( CONTENT(const_80), MM7 ) /* 0x80| 0x80| 0x80| 0x80| 0x80| 0x80| 0x80| 0x80*/
302
303 #define MAIN( rgba, dest ) \
304 GMB_LOAD( rgba, dest, MM1, MM2 ) ;\
305 MOVQ ( MM1, MM3 ) ;\
306 MOVQ ( MM2, MM4 ) ;\
307 PXOR ( MM7, MM3 ) /* unsigned -> signed */ ;\
308 PXOR ( MM7, MM4 ) /* unsigned -> signed */ ;\
309 PCMPGTB ( MM3, MM4 ) /* q > p ? 0xff : 0x00 */ ;\
310 PAND ( MM4, MM1 ) /* q > p ? p : 0 */ ;\
311 PANDN ( MM2, MM4 ) /* q > p ? 0 : q */ ;\
312 POR ( MM1, MM4 ) /* q > p ? p : q */ ;\
313 GMB_STORE( rgba, MM4 )
314
315 #include "mmx_blendtmp.h"
316
317
318 /* Blend max function
319 */
320
321 #define TAG(x) x##_max
322
323 #define INIT \
324 MOVQ ( CONTENT(const_80), MM7 ) /* 0x80| 0x80| 0x80| 0x80| 0x80| 0x80| 0x80| 0x80*/
325
326 #define MAIN( rgba, dest ) \
327 GMB_LOAD( rgba, dest, MM1, MM2 ) ;\
328 MOVQ ( MM1, MM3 ) ;\
329 MOVQ ( MM2, MM4 ) ;\
330 PXOR ( MM7, MM3 ) /* unsigned -> signed */ ;\
331 PXOR ( MM7, MM4 ) /* unsigned -> signed */ ;\
332 PCMPGTB ( MM3, MM4 ) /* q > p ? 0xff : 0x00 */ ;\
333 PAND ( MM4, MM2 ) /* q > p ? q : 0 */ ;\
334 PANDN ( MM1, MM4 ) /* q > p ? 0 : p */ ;\
335 POR ( MM2, MM4 ) /* q > p ? p : q */ ;\
336 GMB_STORE( rgba, MM4 )
337
338 #include "mmx_blendtmp.h"
339
340
341 /* Blend modulate function
342 */
343
344 #define TAG(x) x##_modulate
345
346 #define INIT \
347 PXOR ( MM0, MM0 ) /* 0x0000 | 0x0000 | 0x0000 | 0x0000 */ ;\
348 MOVQ ( CONTENT(const_0080), MM7 ) /* 0x0080 | 0x0080 | 0x0080 | 0x0080 */
349
350 #define MAIN( rgba, dest ) \
351 GMB_LOAD( rgba, dest, MM1, MM2 ) ;\
352 GMB_UNPACK( MM1, MM2, MM4, MM5, MM0 ) ;\
353 GMB_MULT_GSR( MM1, MM2, MM4, MM5, MM7 ) ;\
354 GMB_PACK( MM2, MM5 ) ;\
355 GMB_STORE( rgba, MM2 )
356
357 #include "mmx_blendtmp.h"
358