Browse code

simd and unroll png_filter_row cycles per 1000 pixels on core2: left: 9211->5170 top: 9283->2138 avg: 12215->7611 paeth: 64024->17360 overall rgb png decoding speed: +45% overall greyscale png decoding speed: +6%

Originally committed as revision 12164 to svn://svn.ffmpeg.org/ffmpeg/trunk

Loren Merritt authored on 2008/02/21 16:10:46
Showing 4 changed files
... ...
@@ -44,6 +44,9 @@ void vorbis_inverse_coupling(float *mag, float *ang, int blocksize);
44 44
 /* flacenc.c */
45 45
 void ff_flac_compute_autocorr(const int32_t *data, int len, int lag, double *autoc);
46 46
 
47
+/* pngdec.c */
48
+void ff_add_png_paeth_prediction(uint8_t *dst, uint8_t *src, uint8_t *top, int w, int bpp);
49
+
47 50
 uint8_t ff_cropTbl[256 + 2 * MAX_NEG_CROP] = {0, };
48 51
 uint32_t ff_squareTbl[512] = {0, };
49 52
 
... ...
@@ -3288,6 +3291,17 @@ static void add_bytes_c(uint8_t *dst, uint8_t *src, int w){
3288 3288
         dst[i+0] += src[i+0];
3289 3289
 }
3290 3290
 
3291
+static void add_bytes_l2_c(uint8_t *dst, uint8_t *src1, uint8_t *src2, int w){
3292
+    int i;
3293
+    for(i=0; i<=w-sizeof(long); i+=sizeof(long)){
3294
+        long a = *(long*)(src1+i);
3295
+        long b = *(long*)(src2+i);
3296
+        *(long*)(dst+i) = ((a&0x7f7f7f7f7f7f7f7fL) + (b&0x7f7f7f7f7f7f7f7fL)) ^ ((a^b)&0x8080808080808080L);
3297
+    }
3298
+    for(; i<w; i++)
3299
+        dst[i] = src1[i]+src2[i];
3300
+}
3301
+
3291 3302
 static void diff_bytes_c(uint8_t *dst, uint8_t *src1, uint8_t *src2, int w){
3292 3303
     int i;
3293 3304
     for(i=0; i+7<w; i+=8){
... ...
@@ -4232,9 +4246,13 @@ void dsputil_init(DSPContext* c, AVCodecContext *avctx)
4232 4232
     c->ssd_int8_vs_int16 = ssd_int8_vs_int16_c;
4233 4233
 
4234 4234
     c->add_bytes= add_bytes_c;
4235
+    c->add_bytes_l2= add_bytes_l2_c;
4235 4236
     c->diff_bytes= diff_bytes_c;
4236 4237
     c->sub_hfyu_median_prediction= sub_hfyu_median_prediction_c;
4237 4238
     c->bswap_buf= bswap_buf;
4239
+#ifdef CONFIG_PNG_DECODER
4240
+    c->add_png_paeth_prediction= ff_add_png_paeth_prediction;
4241
+#endif
4238 4242
 
4239 4243
     c->h264_v_loop_filter_luma= h264_v_loop_filter_luma_c;
4240 4244
     c->h264_h_loop_filter_luma= h264_h_loop_filter_luma_c;
... ...
@@ -304,12 +304,15 @@ typedef struct DSPContext {
304 304
 
305 305
     /* huffyuv specific */
306 306
     void (*add_bytes)(uint8_t *dst/*align 16*/, uint8_t *src/*align 16*/, int w);
307
+    void (*add_bytes_l2)(uint8_t *dst/*align 16*/, uint8_t *src1/*align 16*/, uint8_t *src2/*align 16*/, int w);
307 308
     void (*diff_bytes)(uint8_t *dst/*align 16*/, uint8_t *src1/*align 16*/, uint8_t *src2/*align 1*/,int w);
308 309
     /**
309 310
      * subtract huffyuv's variant of median prediction
310 311
      * note, this might read from src1[-1], src2[-1]
311 312
      */
312 313
     void (*sub_hfyu_median_prediction)(uint8_t *dst, uint8_t *src1, uint8_t *src2, int w, int *left, int *left_top);
314
+    /* this might write to dst[w] */
315
+    void (*add_png_paeth_prediction)(uint8_t *dst, uint8_t *src, uint8_t *top, int w, int bpp);
313 316
     void (*bswap_buf)(uint32_t *dst, const uint32_t *src, int w);
314 317
 
315 318
     void (*h264_v_loop_filter_luma)(uint8_t *pix, int stride, int alpha, int beta, int8_t *tc0);
... ...
@@ -59,6 +59,7 @@ DECLARE_ALIGNED_8 (const uint64_t, ff_pw_42 ) = 0x002A002A002A002AULL;
59 59
 DECLARE_ALIGNED_8 (const uint64_t, ff_pw_64 ) = 0x0040004000400040ULL;
60 60
 DECLARE_ALIGNED_8 (const uint64_t, ff_pw_96 ) = 0x0060006000600060ULL;
61 61
 DECLARE_ALIGNED_8 (const uint64_t, ff_pw_128) = 0x0080008000800080ULL;
62
+DECLARE_ALIGNED_8 (const uint64_t, ff_pw_255) = 0x00ff00ff00ff00ffULL;
62 63
 
63 64
 DECLARE_ALIGNED_8 (const uint64_t, ff_pb_1  ) = 0x0101010101010101ULL;
64 65
 DECLARE_ALIGNED_8 (const uint64_t, ff_pb_3  ) = 0x0303030303030303ULL;
... ...
@@ -605,6 +606,26 @@ static void add_bytes_mmx(uint8_t *dst, uint8_t *src, int w){
605 605
         dst[i+0] += src[i+0];
606 606
 }
607 607
 
608
+static void add_bytes_l2_mmx(uint8_t *dst, uint8_t *src1, uint8_t *src2, int w){
609
+    long i=0;
610
+    asm volatile(
611
+        "1:                             \n\t"
612
+        "movq   (%2, %0), %%mm0         \n\t"
613
+        "movq  8(%2, %0), %%mm1         \n\t"
614
+        "paddb  (%3, %0), %%mm0         \n\t"
615
+        "paddb 8(%3, %0), %%mm1         \n\t"
616
+        "movq %%mm0,  (%1, %0)          \n\t"
617
+        "movq %%mm1, 8(%1, %0)          \n\t"
618
+        "add $16, %0                    \n\t"
619
+        "cmp %4, %0                     \n\t"
620
+        " jb 1b                         \n\t"
621
+        : "+r" (i)
622
+        : "r"(dst), "r"(src1), "r"(src2), "r"((long)w-15)
623
+    );
624
+    for(; i<w; i++)
625
+        dst[i] = src1[i] + src2[i];
626
+}
627
+
608 628
 #define H263_LOOP_FILTER \
609 629
         "pxor %%mm7, %%mm7              \n\t"\
610 630
         "movq  %0, %%mm0                \n\t"\
... ...
@@ -1564,6 +1585,80 @@ static void sub_hfyu_median_prediction_mmx2(uint8_t *dst, uint8_t *src1, uint8_t
1564 1564
     *left    = src2[w-1];
1565 1565
 }
1566 1566
 
1567
+#define PAETH(cpu, abs3)\
1568
+void add_png_paeth_prediction_##cpu(uint8_t *dst, uint8_t *src, uint8_t *top, int w, int bpp)\
1569
+{\
1570
+    long i = -bpp;\
1571
+    long end = w-3;\
1572
+    asm volatile(\
1573
+        "pxor      %%mm7, %%mm7 \n"\
1574
+        "movd    (%1,%0), %%mm0 \n"\
1575
+        "movd    (%2,%0), %%mm1 \n"\
1576
+        "punpcklbw %%mm7, %%mm0 \n"\
1577
+        "punpcklbw %%mm7, %%mm1 \n"\
1578
+        "add       %4, %0 \n"\
1579
+        "1: \n"\
1580
+        "movq      %%mm1, %%mm2 \n"\
1581
+        "movd    (%2,%0), %%mm1 \n"\
1582
+        "movq      %%mm2, %%mm3 \n"\
1583
+        "punpcklbw %%mm7, %%mm1 \n"\
1584
+        "movq      %%mm2, %%mm4 \n"\
1585
+        "psubw     %%mm1, %%mm3 \n"\
1586
+        "psubw     %%mm0, %%mm4 \n"\
1587
+        "movq      %%mm3, %%mm5 \n"\
1588
+        "paddw     %%mm4, %%mm5 \n"\
1589
+        abs3\
1590
+        "movq      %%mm4, %%mm6 \n"\
1591
+        "pminsw    %%mm5, %%mm6 \n"\
1592
+        "pcmpgtw   %%mm6, %%mm3 \n"\
1593
+        "pcmpgtw   %%mm5, %%mm4 \n"\
1594
+        "movq      %%mm4, %%mm6 \n"\
1595
+        "pand      %%mm3, %%mm4 \n"\
1596
+        "pandn     %%mm3, %%mm6 \n"\
1597
+        "pandn     %%mm0, %%mm3 \n"\
1598
+        "movd    (%3,%0), %%mm0 \n"\
1599
+        "pand      %%mm1, %%mm6 \n"\
1600
+        "pand      %%mm4, %%mm2 \n"\
1601
+        "punpcklbw %%mm7, %%mm0 \n"\
1602
+        "movq      %6,    %%mm5 \n"\
1603
+        "paddw     %%mm6, %%mm0 \n"\
1604
+        "paddw     %%mm2, %%mm3 \n"\
1605
+        "paddw     %%mm3, %%mm0 \n"\
1606
+        "pand      %%mm5, %%mm0 \n"\
1607
+        "movq      %%mm0, %%mm3 \n"\
1608
+        "packuswb  %%mm3, %%mm3 \n"\
1609
+        "movd      %%mm3, (%1,%0) \n"\
1610
+        "add       %4, %0 \n"\
1611
+        "cmp       %5, %0 \n"\
1612
+        "jle 1b \n"\
1613
+        :"+r"(i)\
1614
+        :"r"(dst), "r"(top), "r"(src), "r"((long)bpp), "g"(end),\
1615
+         "m"(ff_pw_255)\
1616
+        :"memory"\
1617
+    );\
1618
+}
1619
+
1620
+#define ABS3_MMX2\
1621
+        "psubw     %%mm5, %%mm7 \n"\
1622
+        "pmaxsw    %%mm7, %%mm5 \n"\
1623
+        "pxor      %%mm6, %%mm6 \n"\
1624
+        "pxor      %%mm7, %%mm7 \n"\
1625
+        "psubw     %%mm3, %%mm6 \n"\
1626
+        "psubw     %%mm4, %%mm7 \n"\
1627
+        "pmaxsw    %%mm6, %%mm3 \n"\
1628
+        "pmaxsw    %%mm7, %%mm4 \n"\
1629
+        "pxor      %%mm7, %%mm7 \n"
1630
+
1631
+#define ABS3_SSSE3\
1632
+        "pabsw     %%mm3, %%mm3 \n"\
1633
+        "pabsw     %%mm4, %%mm4 \n"\
1634
+        "pabsw     %%mm5, %%mm5 \n"
1635
+
1636
+PAETH(mmx2, ABS3_MMX2)
1637
+#ifdef HAVE_SSSE3
1638
+PAETH(ssse3, ABS3_SSSE3)
1639
+#endif
1640
+
1567 1641
 #define DIFF_PIXELS_1(m,a,t,p1,p2)\
1568 1642
     "mov"#m" "#p1", "#a"              \n\t"\
1569 1643
     "mov"#m" "#p2", "#t"              \n\t"\
... ...
@@ -3317,6 +3412,7 @@ void dsputil_init_mmx(DSPContext* c, AVCodecContext *avctx)
3317 3317
         c->gmc= gmc_mmx;
3318 3318
 
3319 3319
         c->add_bytes= add_bytes_mmx;
3320
+        c->add_bytes_l2= add_bytes_l2_mmx;
3320 3321
 #ifdef CONFIG_ENCODERS
3321 3322
         c->diff_bytes= diff_bytes_mmx;
3322 3323
         c->sum_abs_dctelem= sum_abs_dctelem_mmx;
... ...
@@ -3471,6 +3567,7 @@ void dsputil_init_mmx(DSPContext* c, AVCodecContext *avctx)
3471 3471
             if (ENABLE_VC1_DECODER || ENABLE_WMV3_DECODER)
3472 3472
                 ff_vc1dsp_init_mmx(c, avctx);
3473 3473
 
3474
+            c->add_png_paeth_prediction= add_png_paeth_prediction_mmx2;
3474 3475
 #ifdef CONFIG_ENCODERS
3475 3476
             c->sub_hfyu_median_prediction= sub_hfyu_median_prediction_mmx2;
3476 3477
 #endif //CONFIG_ENCODERS
... ...
@@ -3565,6 +3662,7 @@ void dsputil_init_mmx(DSPContext* c, AVCodecContext *avctx)
3565 3565
             H264_QPEL_FUNCS(3, 1, ssse3);
3566 3566
             H264_QPEL_FUNCS(3, 2, ssse3);
3567 3567
             H264_QPEL_FUNCS(3, 3, ssse3);
3568
+            c->add_png_paeth_prediction= add_png_paeth_prediction_ssse3;
3568 3569
         }
3569 3570
 #endif
3570 3571
 
... ...
@@ -21,6 +21,7 @@
21 21
 #include "avcodec.h"
22 22
 #include "bytestream.h"
23 23
 #include "png.h"
24
+#include "dsputil.h"
24 25
 
25 26
 /* TODO:
26 27
  * - add 2, 4 and 16 bit depth support
... ...
@@ -31,6 +32,8 @@
31 31
 //#define DEBUG
32 32
 
33 33
 typedef struct PNGDecContext {
34
+    DSPContext dsp;
35
+
34 36
     const uint8_t *bytestream;
35 37
     const uint8_t *bytestream_start;
36 38
     const uint8_t *bytestream_end;
... ...
@@ -129,12 +132,60 @@ static void png_put_interlaced_row(uint8_t *dst, int width,
129 129
     }
130 130
 }
131 131
 
132
-/* XXX: optimize */
132
+void ff_add_png_paeth_prediction(uint8_t *dst, uint8_t *src, uint8_t *top, int w, int bpp)
133
+{
134
+    int i;
135
+    for(i = 0; i < w; i++) {
136
+        int a, b, c, p, pa, pb, pc;
137
+
138
+        a = dst[i - bpp];
139
+        b = top[i];
140
+        c = top[i - bpp];
141
+
142
+        p = b - c;
143
+        pc = a - c;
144
+
145
+        pa = abs(p);
146
+        pb = abs(pc);
147
+        pc = abs(p + pc);
148
+
149
+        if (pa <= pb && pa <= pc)
150
+            p = a;
151
+        else if (pb <= pc)
152
+            p = b;
153
+        else
154
+            p = c;
155
+        dst[i] = p + src[i];
156
+    }
157
+}
158
+
159
+#define UNROLL1(bpp, op) {\
160
+                 r = dst[0];\
161
+    if(bpp >= 2) g = dst[1];\
162
+    if(bpp >= 3) b = dst[2];\
163
+    if(bpp >= 4) a = dst[3];\
164
+    for(; i < size; i+=bpp) {\
165
+        dst[i+0] = r = op(r, src[i+0], last[i+0]);\
166
+        if(bpp == 1) continue;\
167
+        dst[i+1] = g = op(g, src[i+1], last[i+1]);\
168
+        if(bpp == 2) continue;\
169
+        dst[i+2] = b = op(b, src[i+2], last[i+2]);\
170
+        if(bpp == 3) continue;\
171
+        dst[i+3] = a = op(a, src[i+3], last[i+3]);\
172
+    }\
173
+}
174
+
175
+#define UNROLL_FILTER(op)\
176
+         if(bpp == 1) UNROLL1(1, op)\
177
+    else if(bpp == 2) UNROLL1(2, op)\
178
+    else if(bpp == 3) UNROLL1(3, op)\
179
+    else if(bpp == 4) UNROLL1(4, op)\
180
+
133 181
 /* NOTE: 'dst' can be equal to 'last' */
134
-static void png_filter_row(uint8_t *dst, int filter_type,
182
+static void png_filter_row(DSPContext *dsp, uint8_t *dst, int filter_type,
135 183
                            uint8_t *src, uint8_t *last, int size, int bpp)
136 184
 {
137
-    int i, p;
185
+    int i, p, r, g, b, a;
138 186
 
139 187
     switch(filter_type) {
140 188
     case PNG_FILTER_VALUE_NONE:
... ...
@@ -144,54 +195,41 @@ static void png_filter_row(uint8_t *dst, int filter_type,
144 144
         for(i = 0; i < bpp; i++) {
145 145
             dst[i] = src[i];
146 146
         }
147
-        for(i = bpp; i < size; i++) {
148
-            p = dst[i - bpp];
149
-            dst[i] = p + src[i];
147
+        if(bpp == 4) {
148
+            p = *(int*)dst;
149
+            for(; i < size; i+=bpp) {
150
+                int s = *(int*)(src+i);
151
+                p = ((s&0x7f7f7f7f) + (p&0x7f7f7f7f)) ^ ((s^p)&0x80808080);
152
+                *(int*)(dst+i) = p;
153
+            }
154
+        } else {
155
+#define OP_SUB(x,s,l) x+s
156
+            UNROLL_FILTER(OP_SUB);
150 157
         }
151 158
         break;
152 159
     case PNG_FILTER_VALUE_UP:
153
-        for(i = 0; i < size; i++) {
154
-            p = last[i];
155
-            dst[i] = p + src[i];
156
-        }
160
+        dsp->add_bytes_l2(dst, src, last, size);
157 161
         break;
158 162
     case PNG_FILTER_VALUE_AVG:
159 163
         for(i = 0; i < bpp; i++) {
160 164
             p = (last[i] >> 1);
161 165
             dst[i] = p + src[i];
162 166
         }
163
-        for(i = bpp; i < size; i++) {
164
-            p = ((dst[i - bpp] + last[i]) >> 1);
165
-            dst[i] = p + src[i];
166
-        }
167
+#define OP_AVG(x,s,l) (((x + l) >> 1) + s) & 0xff
168
+        UNROLL_FILTER(OP_AVG);
167 169
         break;
168 170
     case PNG_FILTER_VALUE_PAETH:
169 171
         for(i = 0; i < bpp; i++) {
170 172
             p = last[i];
171 173
             dst[i] = p + src[i];
172 174
         }
173
-        for(i = bpp; i < size; i++) {
174
-            int a, b, c, pa, pb, pc;
175
-
176
-            a = dst[i - bpp];
177
-            b = last[i];
178
-            c = last[i - bpp];
179
-
180
-            p = b - c;
181
-            pc = a - c;
182
-
183
-            pa = abs(p);
184
-            pb = abs(pc);
185
-            pc = abs(p + pc);
186
-
187
-            if (pa <= pb && pa <= pc)
188
-                p = a;
189
-            else if (pb <= pc)
190
-                p = b;
191
-            else
192
-                p = c;
193
-            dst[i] = p + src[i];
175
+        if(bpp > 1 && size > 4) {
176
+            // would write off the end of the array if we let it process the last pixel with bpp=3
177
+            int w = bpp==4 ? size : size-3;
178
+            dsp->add_png_paeth_prediction(dst+i, src+i, last+i, w-i, bpp);
179
+            i = w;
194 180
         }
181
+        ff_add_png_paeth_prediction(dst+i, src+i, last+i, size-i, bpp);
195 182
         break;
196 183
     }
197 184
 }
... ...
@@ -222,7 +260,7 @@ static void png_handle_row(PNGDecContext *s)
222 222
         ptr = s->image_buf + s->image_linesize * s->y;
223 223
         /* need to swap bytes correctly for RGB_ALPHA */
224 224
         if (s->color_type == PNG_COLOR_TYPE_RGB_ALPHA) {
225
-            png_filter_row(s->tmp_row, s->crow_buf[0], s->crow_buf + 1,
225
+            png_filter_row(&s->dsp, s->tmp_row, s->crow_buf[0], s->crow_buf + 1,
226 226
                            s->last_row, s->row_size, s->bpp);
227 227
             memcpy(s->last_row, s->tmp_row, s->row_size);
228 228
             convert_to_rgb32(ptr, s->tmp_row, s->width);
... ...
@@ -233,7 +271,7 @@ static void png_handle_row(PNGDecContext *s)
233 233
             else
234 234
                 last_row = ptr - s->image_linesize;
235 235
 
236
-            png_filter_row(ptr, s->crow_buf[0], s->crow_buf + 1,
236
+            png_filter_row(&s->dsp, ptr, s->crow_buf[0], s->crow_buf + 1,
237 237
                            last_row, s->row_size, s->bpp);
238 238
         }
239 239
         s->y++;
... ...
@@ -249,7 +287,7 @@ static void png_handle_row(PNGDecContext *s)
249 249
                    wait for the next one */
250 250
                 if (got_line)
251 251
                     break;
252
-                png_filter_row(s->tmp_row, s->crow_buf[0], s->crow_buf + 1,
252
+                png_filter_row(&s->dsp, s->tmp_row, s->crow_buf[0], s->crow_buf + 1,
253 253
                                s->last_row, s->pass_row_size, s->bpp);
254 254
                 memcpy(s->last_row, s->tmp_row, s->pass_row_size);
255 255
                 got_line = 1;
... ...
@@ -534,6 +572,7 @@ static int png_dec_init(AVCodecContext *avctx){
534 534
 
535 535
     avcodec_get_frame_defaults((AVFrame*)&s->picture);
536 536
     avctx->coded_frame= (AVFrame*)&s->picture;
537
+    dsputil_init(&s->dsp, avctx);
537 538
 
538 539
     return 0;
539 540
 }