better hadamard8_diff16 in AltiVec, and more patch by (Romain Dolbeau <dolbeau at...
[ffmpeg.git] / libavcodec / ppc / dsputil_ppc.c
1 /*
2  * Copyright (c) 2002 Brian Foley
3  * Copyright (c) 2002 Dieter Shirley
4  * Copyright (c) 2003-2004 Romain Dolbeau <romain@dolbeau.org>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include "../dsputil.h"
22
23 #include "dsputil_ppc.h"
24
25 #ifdef HAVE_ALTIVEC
26 #include "dsputil_altivec.h"
27 #endif
28
29 extern void fdct_altivec(int16_t *block);
30 extern void idct_put_altivec(uint8_t *dest, int line_size, int16_t *block);
31 extern void idct_add_altivec(uint8_t *dest, int line_size, int16_t *block);
32
33 int mm_flags = 0;
34
35 int mm_support(void)
36 {
37     int result = 0;
38 #ifdef HAVE_ALTIVEC
39     if (has_altivec()) {
40         result |= MM_ALTIVEC;
41     }
42 #endif /* result */
43     return result;
44 }
45
46 #ifdef POWERPC_PERFORMANCE_REPORT
47 unsigned long long perfdata[POWERPC_NUM_PMC_ENABLED][powerpc_perf_total][powerpc_data_total];
48 /* list below must match enum in dsputil_ppc.h */
49 static unsigned char* perfname[] = {
50   "ff_fft_calc_altivec",
51   "gmc1_altivec",
52   "dct_unquantize_h263_altivec",
53   "fdct_altivec",
54   "idct_add_altivec",
55   "idct_put_altivec",
56   "put_pixels16_altivec",
57   "avg_pixels16_altivec",
58   "avg_pixels8_altivec",
59   "put_pixels8_xy2_altivec",
60   "put_no_rnd_pixels8_xy2_altivec",
61   "put_pixels16_xy2_altivec",
62   "put_no_rnd_pixels16_xy2_altivec",
63   "hadamard8_diff8x8_altivec",
64   "hadamard8_diff16_altivec",
65   "clear_blocks_dcbz32_ppc",
66   "clear_blocks_dcbz128_ppc"
67 };
68 #include <stdio.h>
69 #endif
70
71 #ifdef POWERPC_PERFORMANCE_REPORT
72 void powerpc_display_perf_report(void)
73 {
74   int i, j;
75   av_log(NULL, AV_LOG_INFO, "PowerPC performance report\n Values are from the PMC registers, and represent whatever the registers are set to record.\n");
76   for(i = 0 ; i < powerpc_perf_total ; i++)
77   {
78     for (j = 0; j < POWERPC_NUM_PMC_ENABLED ; j++)
79       {
80         if (perfdata[j][i][powerpc_data_num] != (unsigned long long)0)
81           av_log(NULL, AV_LOG_INFO,
82                   " Function \"%s\" (pmc%d):\n\tmin: %llu\n\tmax: %llu\n\tavg: %1.2lf (%llu)\n",
83                   perfname[i],
84                   j+1,
85                   perfdata[j][i][powerpc_data_min],
86                   perfdata[j][i][powerpc_data_max],
87                   (double)perfdata[j][i][powerpc_data_sum] /
88                   (double)perfdata[j][i][powerpc_data_num],
89                   perfdata[j][i][powerpc_data_num]);
90       }
91   }
92 }
93 #endif /* POWERPC_PERFORMANCE_REPORT */
94
95 /* ***** WARNING ***** WARNING ***** WARNING ***** */
96 /*
97   clear_blocks_dcbz32_ppc will not work properly
98   on PowerPC processors with a cache line size
99   not equal to 32 bytes.
100   Fortunately all processor used by Apple up to
101   at least the 7450 (aka second generation G4)
102   use 32 bytes cache line.
103   This is due to the use of the 'dcbz' instruction.
104   It simply clear to zero a single cache line,
105   so you need to know the cache line size to use it !
106   It's absurd, but it's fast...
107
108   update 24/06/2003 : Apple released yesterday the G5,
109   with a PPC970. cache line size : 128 bytes. Oups.
110   The semantic of dcbz was changed, it always clear
111   32 bytes. so the function below will work, but will
112   be slow. So I fixed check_dcbz_effect to use dcbzl,
113   which is defined to clear a cache line (as dcbz before).
114   So we still can distinguish, and use dcbz (32 bytes)
115   or dcbzl (one cache line) as required.
116
117   see <http://developer.apple.com/technotes/tn/tn2087.html>
118   and <http://developer.apple.com/technotes/tn/tn2086.html>
119 */
120 void clear_blocks_dcbz32_ppc(DCTELEM *blocks)
121 {
122 POWERPC_PERF_DECLARE(powerpc_clear_blocks_dcbz32, 1);
123     register int misal = ((unsigned long)blocks & 0x00000010);
124     register int i = 0;
125 POWERPC_PERF_START_COUNT(powerpc_clear_blocks_dcbz32, 1);
126 #if 1
127     if (misal) {
128       ((unsigned long*)blocks)[0] = 0L;
129       ((unsigned long*)blocks)[1] = 0L;
130       ((unsigned long*)blocks)[2] = 0L;
131       ((unsigned long*)blocks)[3] = 0L;
132       i += 16;
133     }
134     for ( ; i < sizeof(DCTELEM)*6*64 ; i += 32) {
135 #ifndef __MWERKS__
136       asm volatile("dcbz %0,%1" : : "b" (blocks), "r" (i) : "memory");
137 #else
138       __dcbz( blocks, i );
139 #endif
140     }
141     if (misal) {
142       ((unsigned long*)blocks)[188] = 0L;
143       ((unsigned long*)blocks)[189] = 0L;
144       ((unsigned long*)blocks)[190] = 0L;
145       ((unsigned long*)blocks)[191] = 0L;
146       i += 16;
147     }
148 #else
149     memset(blocks, 0, sizeof(DCTELEM)*6*64);
150 #endif
151 POWERPC_PERF_STOP_COUNT(powerpc_clear_blocks_dcbz32, 1);
152 }
153
154 /* same as above, when dcbzl clear a whole 128B cache line
155    i.e. the PPC970 aka G5 */
156 #ifndef NO_DCBZL
157 void clear_blocks_dcbz128_ppc(DCTELEM *blocks)
158 {
159 POWERPC_PERF_DECLARE(powerpc_clear_blocks_dcbz128, 1);
160     register int misal = ((unsigned long)blocks & 0x0000007f);
161     register int i = 0;
162 POWERPC_PERF_START_COUNT(powerpc_clear_blocks_dcbz128, 1);
163 #if 1
164  if (misal) {
165    // we could probably also optimize this case,
166    // but there's not much point as the machines
167    // aren't available yet (2003-06-26)
168       memset(blocks, 0, sizeof(DCTELEM)*6*64);
169     }
170     else
171       for ( ; i < sizeof(DCTELEM)*6*64 ; i += 128) {
172         asm volatile("dcbzl %0,%1" : : "b" (blocks), "r" (i) : "memory");
173       }
174 #else
175     memset(blocks, 0, sizeof(DCTELEM)*6*64);
176 #endif
177 POWERPC_PERF_STOP_COUNT(powerpc_clear_blocks_dcbz128, 1);
178 }
179 #else
180 void clear_blocks_dcbz128_ppc(DCTELEM *blocks)
181 {
182   memset(blocks, 0, sizeof(DCTELEM)*6*64);
183 }
184 #endif
185
186 #ifndef NO_DCBZL
187 /* check dcbz report how many bytes are set to 0 by dcbz */
188 /* update 24/06/2003 : replace dcbz by dcbzl to get
189    the intended effect (Apple "fixed" dcbz)
190    unfortunately this cannot be used unless the assembler
191    knows about dcbzl ... */
192 long check_dcbzl_effect(void)
193 {
194   register char *fakedata = (char*)av_malloc(1024);
195   register char *fakedata_middle;
196   register long zero = 0;
197   register long i = 0;
198   long count = 0;
199
200   if (!fakedata)
201   {
202     return 0L;
203   }
204
205   fakedata_middle = (fakedata + 512);
206
207   memset(fakedata, 0xFF, 1024);
208
209   /* below the constraint "b" seems to mean "Address base register"
210      in gcc-3.3 / RS/6000 speaks. seems to avoid using r0, so.... */
211   asm volatile("dcbzl %0, %1" : : "b" (fakedata_middle), "r" (zero));
212
213   for (i = 0; i < 1024 ; i ++)
214   {
215     if (fakedata[i] == (char)0)
216       count++;
217   }
218
219   av_free(fakedata);
220   
221   return count;
222 }
223 #else
224 long check_dcbzl_effect(void)
225 {
226   return 0;
227 }
228 #endif
229
230 void dsputil_init_ppc(DSPContext* c, AVCodecContext *avctx)
231 {
232     // Common optimizations whether Altivec is available or not
233
234   switch (check_dcbzl_effect()) {
235   case 32:
236     c->clear_blocks = clear_blocks_dcbz32_ppc;
237     break;
238   case 128:
239     c->clear_blocks = clear_blocks_dcbz128_ppc;
240     break;
241   default:
242     break;
243   }
244   
245 #ifdef HAVE_ALTIVEC
246     if (has_altivec()) {
247         mm_flags |= MM_ALTIVEC;
248         
249         // Altivec specific optimisations
250         c->pix_abs[0][1] = sad16_x2_altivec;
251         c->pix_abs[0][2] = sad16_y2_altivec;
252         c->pix_abs[0][3] = sad16_xy2_altivec;
253         c->pix_abs[0][0] = sad16_altivec;
254         c->pix_abs[1][0] = sad8_altivec;
255         c->sad[0]= sad16_altivec;
256         c->sad[1]= sad8_altivec;
257         c->pix_norm1 = pix_norm1_altivec;
258         c->sse[1]= sse8_altivec;
259         c->sse[0]= sse16_altivec;
260         c->pix_sum = pix_sum_altivec;
261         c->diff_pixels = diff_pixels_altivec;
262         c->get_pixels = get_pixels_altivec;
263 // next one disabled as it's untested.
264 #if 0
265         c->add_bytes= add_bytes_altivec;
266 #endif /* 0 */
267         c->put_pixels_tab[0][0] = put_pixels16_altivec;
268         /* the two functions do the same thing, so use the same code */
269         c->put_no_rnd_pixels_tab[0][0] = put_pixels16_altivec;
270         c->avg_pixels_tab[0][0] = avg_pixels16_altivec;
271 // next one disabled as it's untested.
272 #if 0
273         c->avg_pixels_tab[1][0] = avg_pixels8_altivec;
274 #endif /* 0 */
275         c->put_pixels_tab[1][3] = put_pixels8_xy2_altivec;
276         c->put_no_rnd_pixels_tab[1][3] = put_no_rnd_pixels8_xy2_altivec;
277         c->put_pixels_tab[0][3] = put_pixels16_xy2_altivec;
278         c->put_no_rnd_pixels_tab[0][3] = put_no_rnd_pixels16_xy2_altivec;
279         
280         c->gmc1 = gmc1_altivec;
281
282         c->hadamard8_diff[0] = hadamard8_diff16_altivec;
283         c->hadamard8_diff[1] = hadamard8_diff8x8_altivec;
284
285 #ifdef CONFIG_ENCODERS
286         if (avctx->dct_algo == FF_DCT_AUTO ||
287             avctx->dct_algo == FF_DCT_ALTIVEC)
288         {
289             c->fdct = fdct_altivec;
290         }
291 #endif //CONFIG_ENCODERS
292
293         if ((avctx->idct_algo == FF_IDCT_AUTO) ||
294                 (avctx->idct_algo == FF_IDCT_ALTIVEC))
295         {
296             c->idct_put = idct_put_altivec;
297             c->idct_add = idct_add_altivec;
298 #ifndef ALTIVEC_USE_REFERENCE_C_CODE
299             c->idct_permutation_type = FF_TRANSPOSE_IDCT_PERM;
300 #else /* ALTIVEC_USE_REFERENCE_C_CODE */
301             c->idct_permutation_type = FF_NO_IDCT_PERM;
302 #endif /* ALTIVEC_USE_REFERENCE_C_CODE */
303         }
304         
305 #ifdef POWERPC_PERFORMANCE_REPORT
306         {
307           int i, j;
308           for (i = 0 ; i < powerpc_perf_total ; i++)
309           {
310             for (j = 0; j < POWERPC_NUM_PMC_ENABLED ; j++)
311               {
312                 perfdata[j][i][powerpc_data_min] = (unsigned long long)0xFFFFFFFFFFFFFFFF;
313                 perfdata[j][i][powerpc_data_max] = (unsigned long long)0x0000000000000000;
314                 perfdata[j][i][powerpc_data_sum] = (unsigned long long)0x0000000000000000;
315                 perfdata[j][i][powerpc_data_num] = (unsigned long long)0x0000000000000000;
316               }
317           }
318         }
319 #endif /* POWERPC_PERFORMANCE_REPORT */
320     } else
321 #endif /* HAVE_ALTIVEC */
322     {
323         // Non-AltiVec PPC optimisations
324
325         // ... pending ...
326     }
327 }