Much faster VP8 mv and mode prediction
[ffmpeg.git] / libavcodec / vp8.c
index f7163f4..bc95fb0 100644 (file)
@@ -45,6 +45,7 @@ typedef struct {
     DSPContext dsp;
     VP8DSPContext vp8dsp;
     H264PredContext hpc;
+    vp8_mc_func put_pixels_tab[3][3][3];
     AVFrame frames[4];
     AVFrame *framep[4];
     uint8_t *edge_emu_buffer;
@@ -61,6 +62,7 @@ typedef struct {
     int update_last;    ///< update VP56_FRAME_PREVIOUS with the current one
     int update_golden;  ///< VP56_FRAME_NONE if not updated, or which frame to copy if so
     int update_altref;
+    int deblock_filter;
 
     /**
      * If this flag is not set, all the probability updates
@@ -83,6 +85,12 @@ typedef struct {
     uint8_t *intra4x4_pred_mode_base;
     int b4_stride;
 
+    /**
+     * Cache of the top row needed for intra prediction
+     * 16 for luma, 8 for each chroma plane
+     */
+    uint8_t (*top_border)[16+8+8];
+
     /**
      * For coeff decode, we need to know whether the above block had non-zero
      * coefficients. This means for each macroblock, we need data for 4 luma
@@ -196,6 +204,7 @@ static void vp8_decode_flush(AVCodecContext *avctx)
     av_freep(&s->intra4x4_pred_mode_base);
     av_freep(&s->top_nnz);
     av_freep(&s->edge_emu_buffer);
+    av_freep(&s->top_border);
 
     s->macroblocks        = NULL;
     s->intra4x4_pred_mode = NULL;
@@ -223,6 +232,10 @@ static int update_dimensions(VP8Context *s, int width, int height)
     s->macroblocks_base        = av_mallocz(s->mb_stride*(s->mb_height+1)*sizeof(*s->macroblocks));
     s->intra4x4_pred_mode_base = av_mallocz(s->b4_stride*(4*s->mb_height+1));
     s->top_nnz                 = av_mallocz(s->mb_width*sizeof(*s->top_nnz));
+    s->top_border              = av_mallocz((s->mb_width+1)*sizeof(*s->top_border));
+
+    if (!s->macroblocks_base || !s->intra4x4_pred_mode_base || !s->top_nnz || !s->top_border)
+        return AVERROR(ENOMEM);
 
     s->macroblocks        = s->macroblocks_base        + 1 + s->mb_stride;
     s->intra4x4_pred_mode = s->intra4x4_pred_mode_base + 4 + s->b4_stride;
@@ -379,8 +392,13 @@ static int decode_frame_header(VP8Context *s, const uint8_t *buf, int buf_size)
     buf      += 3;
     buf_size -= 3;
 
-    if (s->profile)
-        av_log(s->avctx, AV_LOG_WARNING, "Profile %d not fully handled\n", s->profile);
+    if (s->profile > 3)
+        av_log(s->avctx, AV_LOG_WARNING, "Unknown profile %d\n", s->profile);
+
+    if (!s->profile)
+        memcpy(s->put_pixels_tab, s->vp8dsp.put_vp8_epel_pixels_tab, sizeof(s->put_pixels_tab));
+    else    // profile 1-3 use bilinear, 4+ aren't defined so whatever
+        memcpy(s->put_pixels_tab, s->vp8dsp.put_vp8_bilinear_pixels_tab, sizeof(s->put_pixels_tab));
 
     if (header_size > buf_size - 7*s->keyframe) {
         av_log(s->avctx, AV_LOG_ERROR, "Header size larger than data provided\n");
@@ -502,39 +520,45 @@ static inline void clamp_mv(VP8Context *s, VP56mv *dst, const VP56mv *src,
 }
 
 static void find_near_mvs(VP8Context *s, VP8Macroblock *mb, int mb_x, int mb_y,
-                          VP56mv near[2], VP56mv *best, int cnt[4])
+                          VP56mv near[2], VP56mv *best, uint8_t cnt[4])
 {
-    VP8Macroblock *mb_edge[3] = { mb - s->mb_stride     /* top */,
-                                  mb - 1                /* left */,
-                                  mb - s->mb_stride - 1 /* top-left */ };
+    int mb_stride = s->mb_stride;
+    VP8Macroblock *mb_edge[3] = { mb - mb_stride     /* top */,
+                                  mb - 1             /* left */,
+                                  mb - mb_stride - 1 /* top-left */ };
     enum { EDGE_TOP, EDGE_LEFT, EDGE_TOPLEFT };
     VP56mv near_mv[4]  = {{ 0 }};
     enum { CNT_ZERO, CNT_NEAREST, CNT_NEAR, CNT_SPLITMV };
-    int idx = CNT_ZERO, n;
+    int idx = CNT_ZERO;
     int best_idx = CNT_ZERO;
+    int cur_sign_bias = s->sign_bias[mb->ref_frame];
+    int *sign_bias = s->sign_bias;
 
     /* Process MB on top, left and top-left */
-    for (n = 0; n < 3; n++) {
-        VP8Macroblock *edge = mb_edge[n];
-        if (edge->ref_frame != VP56_FRAME_CURRENT) {
-            if (edge->mv.x | edge->mv.y) {
-                VP56mv tmp = edge->mv;
-                if (s->sign_bias[mb->ref_frame] != s->sign_bias[edge->ref_frame]) {
-                    tmp.x *= -1;
-                    tmp.y *= -1;
-                }
-                if ((tmp.x ^ near_mv[idx].x) | (tmp.y ^ near_mv[idx].y))
-                    near_mv[++idx] = tmp;
-                cnt[idx]       += 1 + (n != 2);
-            } else
-                cnt[CNT_ZERO] += 1 + (n != 2);
-        }
+    #define MV_EDGE_CHECK(n)\
+    {\
+        VP8Macroblock *edge = mb_edge[n];\
+        int edge_ref = edge->ref_frame;\
+        if (edge_ref != VP56_FRAME_CURRENT) {\
+            uint32_t mv = AV_RN32A(&edge->mv);\
+            if (mv) {\
+                if (cur_sign_bias != sign_bias[edge_ref]) {\
+                    /* SWAR negate of the values in mv. */\
+                    mv = ((mv&0x80008000) + 0x00010001) ^ (mv&0x7fff7fff);\
+                }\
+                if (!n || mv != AV_RN32A(&near_mv[idx]))\
+                    AV_WN32A(&near_mv[++idx], mv);\
+                cnt[idx]      += 1 + (n != 2);\
+            } else\
+                cnt[CNT_ZERO] += 1 + (n != 2);\
+        }\
     }
+    MV_EDGE_CHECK(0)
+    MV_EDGE_CHECK(1)
+    MV_EDGE_CHECK(2)
 
-    /* If we have three distinct MV's, merge first and last if they're the same */
-    if (cnt[CNT_SPLITMV] &&
-        !((near_mv[1+EDGE_TOP].x ^ near_mv[1+EDGE_TOPLEFT].x) |
-          (near_mv[1+EDGE_TOP].y ^ near_mv[1+EDGE_TOPLEFT].y)))
+    /* If we have three distinct MVs, merge first and last if they're the same */
+    if (cnt[CNT_SPLITMV] && AV_RN32A(&near_mv[1+EDGE_TOP]) == AV_RN32A(&near_mv[1+EDGE_TOPLEFT]))
         cnt[CNT_NEAREST] += 1;
 
     cnt[CNT_SPLITMV] = ((mb_edge[EDGE_LEFT]->mode   == VP8_MVMODE_SPLIT) +
@@ -543,8 +567,8 @@ static void find_near_mvs(VP8Context *s, VP8Macroblock *mb, int mb_x, int mb_y,
 
     /* Swap near and nearest if necessary */
     if (cnt[CNT_NEAR] > cnt[CNT_NEAREST]) {
-        FFSWAP(int,    cnt[CNT_NEAREST],     cnt[CNT_NEAR]);
-        FFSWAP(VP56mv, near_mv[CNT_NEAREST], near_mv[CNT_NEAR]);
+        FFSWAP(uint8_t,     cnt[CNT_NEAREST],     cnt[CNT_NEAR]);
+        FFSWAP( VP56mv, near_mv[CNT_NEAREST], near_mv[CNT_NEAR]);
     }
 
     /* Choose the best mv out of 0,0 and the nearest mv */
@@ -593,44 +617,54 @@ static const uint8_t *get_submv_prob(const VP56mv *left, const VP56mv *top)
 
 /**
  * Split motion vector prediction, 16.4.
+ * @returns the number of motion vectors parsed (2, 4 or 16)
  */
-static void decode_splitmvs(VP8Context    *s,  VP56RangeCoder *c,
+static int decode_splitmvs(VP8Context    *s,  VP56RangeCoder *c,
                             VP8Macroblock *mb, VP56mv         *base_mv)
 {
     int part_idx = mb->partitioning =
         vp8_rac_get_tree(c, vp8_mbsplit_tree, vp8_mbsplit_prob);
     int n, num = vp8_mbsplit_count[part_idx];
-    VP56mv part_mv[16];
+    const uint8_t *mbsplits = vp8_mbsplits[part_idx],
+                  *firstidx = vp8_mbfirstidx[part_idx];
 
     for (n = 0; n < num; n++) {
-        int k = vp8_mbfirstidx[part_idx][n];
-        const VP56mv *left  = (k & 3) ? &mb->bmv[k - 1] : &mb[-1].bmv[k + 3],
-                     *above = (k > 3) ? &mb->bmv[k - 4] : &mb[-s->mb_stride].bmv[k + 12];
-        const uint8_t *submv_prob = get_submv_prob(left, above);
+        int k = firstidx[n];
+        const VP56mv *left, *above;
+        const uint8_t *submv_prob;
+
+        if (!(k & 3)) {
+            VP8Macroblock *left_mb = &mb[-1];
+            left = &left_mb->bmv[vp8_mbsplits[left_mb->partitioning][k + 3]];
+        } else
+            left  = &mb->bmv[mbsplits[k - 1]];
+        if (k <= 3) {
+            VP8Macroblock *above_mb = &mb[-s->mb_stride];
+            above = &above_mb->bmv[vp8_mbsplits[above_mb->partitioning][k + 12]];
+        } else
+            above = &mb->bmv[mbsplits[k - 4]];
+
+        submv_prob = get_submv_prob(left, above);
 
         switch (vp8_rac_get_tree(c, vp8_submv_ref_tree, submv_prob)) {
         case VP8_SUBMVMODE_NEW4X4:
-            part_mv[n].y = base_mv->y + read_mv_component(c, s->prob->mvc[0]);
-            part_mv[n].x = base_mv->x + read_mv_component(c, s->prob->mvc[1]);
+            mb->bmv[n].y = base_mv->y + read_mv_component(c, s->prob->mvc[0]);
+            mb->bmv[n].x = base_mv->x + read_mv_component(c, s->prob->mvc[1]);
             break;
         case VP8_SUBMVMODE_ZERO4X4:
-            part_mv[n].x = 0;
-            part_mv[n].y = 0;
+            mb->bmv[n].x = 0;
+            mb->bmv[n].y = 0;
             break;
         case VP8_SUBMVMODE_LEFT4X4:
-            part_mv[n] = *left;
+            mb->bmv[n] = *left;
             break;
         case VP8_SUBMVMODE_TOP4X4:
-            part_mv[n] = *above;
+            mb->bmv[n] = *above;
             break;
         }
-
-        /* fill out over the 4x4 blocks in MB */
-        for (k = 0; k < 16; k++)
-            if (vp8_mbsplits[part_idx][k] == n) {
-                mb->bmv[k]      = part_mv[n];
-            }
     }
+
+    return num;
 }
 
 static inline void decode_intra4x4_modes(VP56RangeCoder *c, uint8_t *intra4x4,
@@ -656,7 +690,6 @@ static void decode_mb_mode(VP8Context *s, VP8Macroblock *mb, int mb_x, int mb_y,
                            uint8_t *intra4x4)
 {
     VP56RangeCoder *c = &s->c;
-    int n;
 
     if (s->segmentation.update_map)
         mb->segment = vp8_rac_get_tree(c, vp8_segmentid_tree, s->prob->segmentid);
@@ -675,7 +708,7 @@ static void decode_mb_mode(VP8Context *s, VP8Macroblock *mb, int mb_x, int mb_y,
         mb->ref_frame = VP56_FRAME_CURRENT;
     } else if (vp56_rac_get_prob(c, s->prob->intra)) {
         VP56mv near[2], best;
-        int cnt[4] = { 0 };
+        uint8_t cnt[4] = { 0 };
         uint8_t p[4];
 
         // inter MB, 16.2
@@ -687,13 +720,14 @@ static void decode_mb_mode(VP8Context *s, VP8Macroblock *mb, int mb_x, int mb_y,
 
         // motion vectors, 16.3
         find_near_mvs(s, mb, mb_x, mb_y, near, &best, cnt);
-        for (n = 0; n < 4; n++)
-            p[n] = vp8_mode_contexts[cnt[n]][n];
+        p[0] = vp8_mode_contexts[cnt[0]][0];
+        p[1] = vp8_mode_contexts[cnt[1]][1];
+        p[2] = vp8_mode_contexts[cnt[2]][2];
+        p[3] = vp8_mode_contexts[cnt[3]][3];
         mb->mode = vp8_rac_get_tree(c, vp8_pred16x16_tree_mvinter, p);
         switch (mb->mode) {
         case VP8_MVMODE_SPLIT:
-            decode_splitmvs(s, c, mb, &best);
-            mb->mv = mb->bmv[15];
+            mb->mv = mb->bmv[decode_splitmvs(s, c, mb, &best) - 1];
             break;
         case VP8_MVMODE_ZERO:
             mb->mv.x = 0;
@@ -711,8 +745,8 @@ static void decode_mb_mode(VP8Context *s, VP8Macroblock *mb, int mb_x, int mb_y,
             break;
         }
         if (mb->mode != VP8_MVMODE_SPLIT) {
-            for (n = 0; n < 16; n++)
-                mb->bmv[n] = mb->mv;
+            mb->partitioning = VP8_SPLITMVMODE_NONE;
+            mb->bmv[0] = mb->mv;
         }
     } else {
         // intra MB, 16.1
@@ -729,11 +763,13 @@ static void decode_mb_mode(VP8Context *s, VP8Macroblock *mb, int mb_x, int mb_y,
 }
 
 /**
+ * @param c arithmetic bitstream reader context
+ * @param block destination for block coefficients
+ * @param probs probabilities to use when reading trees from the bitstream
  * @param i initial coeff index, 0 unless a separate DC block is coded
  * @param zero_nhood the initial prediction context for number of surrounding
  *                   all-zero blocks (only left/top, so 0-2)
- * @param qmul[0] dc dequant factor
- * @param qmul[1] ac dequant factor
+ * @param qmul array holding the dc/ac dequant factor at position 0/1
  * @return 0 if no coeffs were decoded
  *         otherwise, the index of the last coeff decoded plus one
  */
@@ -832,6 +868,48 @@ static void decode_mb_coeffs(VP8Context *s, VP56RangeCoder *c, VP8Macroblock *mb
         mb->skip = 1;
 }
 
+static av_always_inline
+void backup_mb_border(uint8_t *top_border, uint8_t *src_y, uint8_t *src_cb, uint8_t *src_cr,
+                      int linesize, int uvlinesize, int simple)
+{
+    AV_COPY128(top_border, src_y + 15*linesize);
+    if (!simple) {
+        AV_COPY64(top_border+16, src_cb + 7*uvlinesize);
+        AV_COPY64(top_border+24, src_cr + 7*uvlinesize);
+    }
+}
+
+static av_always_inline
+void xchg_mb_border(uint8_t *top_border, uint8_t *src_y, uint8_t *src_cb, uint8_t *src_cr,
+                    int linesize, int uvlinesize, int mb_x, int mb_y, int mb_width,
+                    int simple, int xchg)
+{
+    uint8_t *top_border_m1 = top_border-32;     // for TL prediction
+    src_y  -=   linesize;
+    src_cb -= uvlinesize;
+    src_cr -= uvlinesize;
+
+#define XCHG(a,b,xchg) do {                     \
+        if (xchg) AV_SWAP64(b,a);               \
+        else      AV_COPY64(b,a);               \
+    } while (0)
+
+    XCHG(top_border_m1+8, src_y-8, xchg);
+    XCHG(top_border,      src_y,   xchg);
+    XCHG(top_border+8,    src_y+8, 1);
+    if (mb_x < mb_width-1)
+        XCHG(top_border+32, src_y+16, 1);
+
+    // only copy chroma for normal loop filter
+    // or to initialize the top row to 127
+    if (!simple || !mb_y) {
+        XCHG(top_border_m1+16, src_cb-8, xchg);
+        XCHG(top_border_m1+24, src_cr-8, xchg);
+        XCHG(top_border+16,    src_cb, 1);
+        XCHG(top_border+24,    src_cr, 1);
+    }
+}
+
 static int check_intra_pred_mode(int mode, int mb_x, int mb_y)
 {
     if (mode == DC_PRED8x8) {
@@ -850,6 +928,13 @@ static void intra_predict(VP8Context *s, uint8_t *dst[3], VP8Macroblock *mb,
 {
     int x, y, mode, nnz, tr;
 
+    // for the first row, we need to run xchg_mb_border to init the top edge to 127
+    // otherwise, skip it if we aren't going to deblock
+    if (s->deblock_filter || !mb_y)
+        xchg_mb_border(s->top_border[mb_x+1], dst[0], dst[1], dst[2],
+                       s->linesize, s->uvlinesize, mb_x, mb_y, s->mb_width,
+                       s->filter.simple, 1);
+
     if (mb->mode < MODE_I4x4) {
         mode = check_intra_pred_mode(mb->mode, mb_x, mb_y);
         s->hpc.pred16x16[mode](dst[0], s->linesize);
@@ -893,6 +978,11 @@ static void intra_predict(VP8Context *s, uint8_t *dst[3], VP8Macroblock *mb,
     mode = check_intra_pred_mode(s->chroma_pred_mode, mb_x, mb_y);
     s->hpc.pred8x8[mode](dst[1], s->uvlinesize);
     s->hpc.pred8x8[mode](dst[2], s->uvlinesize);
+
+    if (s->deblock_filter || !mb_y)
+        xchg_mb_border(s->top_border[mb_x+1], dst[0], dst[1], dst[2],
+                       s->linesize, s->uvlinesize, mb_x, mb_y, s->mb_width,
+                       s->filter.simple, 0);
 }
 
 /**
@@ -910,6 +1000,7 @@ static void intra_predict(VP8Context *s, uint8_t *dst[3], VP8Macroblock *mb,
  * @param width width of src/dst plane data
  * @param height height of src/dst plane data
  * @param linesize size of a single line of plane data, including padding
+ * @param mc_func motion compensation function pointers (bilinear or sixtap MC)
  */
 static inline void vp8_mc(VP8Context *s, int luma,
                           uint8_t *dst, uint8_t *src, const VP56mv *mv,
@@ -937,6 +1028,54 @@ static inline void vp8_mc(VP8Context *s, int luma,
     mc_func[my_idx][mx_idx](dst, linesize, src, linesize, block_h, mx, my);
 }
 
+static inline void vp8_mc_part(VP8Context *s, uint8_t *dst[3],
+                               AVFrame *ref_frame, int x_off, int y_off,
+                               int bx_off, int by_off,
+                               int block_w, int block_h,
+                               int width, int height, VP56mv *mv)
+{
+    VP56mv uvmv = *mv;
+
+    /* Y */
+    vp8_mc(s, 1, dst[0] + by_off * s->linesize + bx_off,
+           ref_frame->data[0], mv, x_off + bx_off, y_off + by_off,
+           block_w, block_h, width, height, s->linesize,
+           s->put_pixels_tab[block_w == 8]);
+
+    /* U/V */
+    if (s->profile == 3) {
+        uvmv.x &= ~7;
+        uvmv.y &= ~7;
+    }
+    x_off   >>= 1; y_off   >>= 1;
+    bx_off  >>= 1; by_off  >>= 1;
+    width   >>= 1; height  >>= 1;
+    block_w >>= 1; block_h >>= 1;
+    vp8_mc(s, 0, dst[1] + by_off * s->uvlinesize + bx_off,
+           ref_frame->data[1], &uvmv, x_off + bx_off, y_off + by_off,
+           block_w, block_h, width, height, s->uvlinesize,
+           s->put_pixels_tab[1 + (block_w == 4)]);
+    vp8_mc(s, 0, dst[2] + by_off * s->uvlinesize + bx_off,
+           ref_frame->data[2], &uvmv, x_off + bx_off, y_off + by_off,
+           block_w, block_h, width, height, s->uvlinesize,
+           s->put_pixels_tab[1 + (block_w == 4)]);
+}
+
+/* Fetch pixels for estimated mv 4 macroblocks ahead.
+ * Optimized for 64-byte cache lines.  Inspired by ffh264 prefetch_motion. */
+static inline void prefetch_motion(VP8Context *s, VP8Macroblock *mb, int mb_x, int mb_y, int x_off, int y_off, int ref)
+{
+    if (mb->ref_frame != VP56_FRAME_CURRENT) {
+        int mx = mb->mv.x + x_off + 8;
+        int my = mb->mv.y + y_off;
+        uint8_t **src= s->framep[mb->ref_frame]->data;
+        int off= mx + (my + (mb_x&3)*4)*s->linesize + 64;
+        s->dsp.prefetch(src[0]+off, s->linesize, 4);
+        off= (mx>>1) + ((my>>1) + (mb_x&7))*s->uvlinesize + 64;
+        s->dsp.prefetch(src[1]+off, src[2]-src[1], 2);
+    }
+}
+
 /**
  * Apply motion vectors to prediction buffer, chapter 18.
  */
@@ -945,29 +1084,16 @@ static void inter_predict(VP8Context *s, uint8_t *dst[3], VP8Macroblock *mb,
 {
     int x_off = mb_x << 4, y_off = mb_y << 4;
     int width = 16*s->mb_width, height = 16*s->mb_height;
-    VP56mv uvmv;
 
-    if (mb->mode < VP8_MVMODE_SPLIT) {
-        /* Y */
-        vp8_mc(s, 1, dst[0], s->framep[mb->ref_frame]->data[0], &mb->mv,
-               x_off, y_off, 16, 16, width, height, s->linesize,
-               s->vp8dsp.put_vp8_epel_pixels_tab[0]);
+    prefetch_motion(s, mb, mb_x, mb_y, x_off, y_off, VP56_FRAME_PREVIOUS);
 
-        /* U/V */
-        uvmv = mb->mv;
-        if (s->profile == 3) {
-            uvmv.x &= ~7;
-            uvmv.y &= ~7;
-        }
-        x_off >>= 1; y_off >>= 1; width >>= 1; height >>= 1;
-        vp8_mc(s, 0, dst[1], s->framep[mb->ref_frame]->data[1], &uvmv,
-               x_off, y_off, 8, 8, width, height, s->uvlinesize,
-               s->vp8dsp.put_vp8_epel_pixels_tab[1]);
-        vp8_mc(s, 0, dst[2], s->framep[mb->ref_frame]->data[2], &uvmv,
-               x_off, y_off, 8, 8, width, height, s->uvlinesize,
-               s->vp8dsp.put_vp8_epel_pixels_tab[1]);
-    } else {
+    if (mb->mode < VP8_MVMODE_SPLIT) {
+        vp8_mc_part(s, dst, s->framep[mb->ref_frame], x_off, y_off,
+                    0, 0, 16, 16, width, height, &mb->mv);
+    } else switch (mb->partitioning) {
+    case VP8_SPLITMVMODE_4x4: {
         int x, y;
+        VP56mv uvmv;
 
         /* Y */
         for (y = 0; y < 4; y++) {
@@ -976,7 +1102,7 @@ static void inter_predict(VP8Context *s, uint8_t *dst[3], VP8Macroblock *mb,
                        s->framep[mb->ref_frame]->data[0], &mb->bmv[4*y + x],
                        4*x + x_off, 4*y + y_off, 4, 4,
                        width, height, s->linesize,
-                       s->vp8dsp.put_vp8_epel_pixels_tab[2]);
+                       s->put_pixels_tab[2]);
             }
         }
 
@@ -1002,15 +1128,41 @@ static void inter_predict(VP8Context *s, uint8_t *dst[3], VP8Macroblock *mb,
                        s->framep[mb->ref_frame]->data[1], &uvmv,
                        4*x + x_off, 4*y + y_off, 4, 4,
                        width, height, s->uvlinesize,
-                       s->vp8dsp.put_vp8_epel_pixels_tab[2]);
+                       s->put_pixels_tab[2]);
                 vp8_mc(s, 0, dst[2] + 4*y*s->uvlinesize + x*4,
                        s->framep[mb->ref_frame]->data[2], &uvmv,
                        4*x + x_off, 4*y + y_off, 4, 4,
                        width, height, s->uvlinesize,
-                       s->vp8dsp.put_vp8_epel_pixels_tab[2]);
+                       s->put_pixels_tab[2]);
             }
         }
+        break;
     }
+    case VP8_SPLITMVMODE_16x8:
+        vp8_mc_part(s, dst, s->framep[mb->ref_frame], x_off, y_off,
+                    0, 0, 16, 8, width, height, &mb->bmv[0]);
+        vp8_mc_part(s, dst, s->framep[mb->ref_frame], x_off, y_off,
+                    0, 8, 16, 8, width, height, &mb->bmv[1]);
+        break;
+    case VP8_SPLITMVMODE_8x16:
+        vp8_mc_part(s, dst, s->framep[mb->ref_frame], x_off, y_off,
+                    0, 0, 8, 16, width, height, &mb->bmv[0]);
+        vp8_mc_part(s, dst, s->framep[mb->ref_frame], x_off, y_off,
+                    8, 0, 8, 16, width, height, &mb->bmv[1]);
+        break;
+    case VP8_SPLITMVMODE_8x8:
+        vp8_mc_part(s, dst, s->framep[mb->ref_frame], x_off, y_off,
+                    0, 0, 8, 8, width, height, &mb->bmv[0]);
+        vp8_mc_part(s, dst, s->framep[mb->ref_frame], x_off, y_off,
+                    8, 0, 8, 8, width, height, &mb->bmv[1]);
+        vp8_mc_part(s, dst, s->framep[mb->ref_frame], x_off, y_off,
+                    0, 8, 8, 8, width, height, &mb->bmv[2]);
+        vp8_mc_part(s, dst, s->framep[mb->ref_frame], x_off, y_off,
+                    8, 8, 8, 8, width, height, &mb->bmv[3]);
+        break;
+    }
+
+    prefetch_motion(s, mb, mb_x, mb_y, x_off, y_off, VP56_FRAME_GOLDEN);
 }
 
 static void idct_mb(VP8Context *s, uint8_t *y_dst, uint8_t *u_dst, uint8_t *v_dst,
@@ -1108,41 +1260,57 @@ static void filter_level_for_mb(VP8Context *s, VP8Macroblock *mb, int *level, in
     }
 }
 
-// TODO: look at backup_mb_border / xchg_mb_border in h264.c
 static void filter_mb(VP8Context *s, uint8_t *dst[3], VP8Macroblock *mb, int mb_x, int mb_y)
 {
-    int filter_level, inner_limit, hev_thresh;
+    int filter_level, inner_limit, hev_thresh, mbedge_lim, bedge_lim;
 
     filter_level_for_mb(s, mb, &filter_level, &inner_limit, &hev_thresh);
     if (!filter_level)
         return;
 
+    mbedge_lim = 2*(filter_level+2) + inner_limit;
+     bedge_lim = 2* filter_level    + inner_limit;
+
     if (mb_x) {
-        s->vp8dsp.vp8_h_loop_filter16(dst[0], s->linesize,   filter_level+2, inner_limit, hev_thresh);
-        s->vp8dsp.vp8_h_loop_filter8 (dst[1], s->uvlinesize, filter_level+2, inner_limit, hev_thresh);
-        s->vp8dsp.vp8_h_loop_filter8 (dst[2], s->uvlinesize, filter_level+2, inner_limit, hev_thresh);
+        s->vp8dsp.vp8_h_loop_filter16y(dst[0],     s->linesize,
+                                       mbedge_lim, inner_limit, hev_thresh);
+        s->vp8dsp.vp8_h_loop_filter8uv(dst[1],     dst[2],      s->uvlinesize,
+                                       mbedge_lim, inner_limit, hev_thresh);
     }
 
     if (!mb->skip || mb->mode == MODE_I4x4 || mb->mode == VP8_MVMODE_SPLIT) {
-        s->vp8dsp.vp8_h_loop_filter16_inner(dst[0]+ 4, s->linesize,   filter_level, inner_limit, hev_thresh);
-        s->vp8dsp.vp8_h_loop_filter16_inner(dst[0]+ 8, s->linesize,   filter_level, inner_limit, hev_thresh);
-        s->vp8dsp.vp8_h_loop_filter16_inner(dst[0]+12, s->linesize,   filter_level, inner_limit, hev_thresh);
-        s->vp8dsp.vp8_h_loop_filter8_inner (dst[1]+ 4, s->uvlinesize, filter_level, inner_limit, hev_thresh);
-        s->vp8dsp.vp8_h_loop_filter8_inner (dst[2]+ 4, s->uvlinesize, filter_level, inner_limit, hev_thresh);
+        s->vp8dsp.vp8_h_loop_filter16y_inner(dst[0]+ 4, s->linesize, bedge_lim,
+                                             inner_limit,   hev_thresh);
+        s->vp8dsp.vp8_h_loop_filter16y_inner(dst[0]+ 8, s->linesize, bedge_lim,
+                                             inner_limit,   hev_thresh);
+        s->vp8dsp.vp8_h_loop_filter16y_inner(dst[0]+12, s->linesize, bedge_lim,
+                                             inner_limit,   hev_thresh);
+        s->vp8dsp.vp8_h_loop_filter8uv_inner(dst[1] + 4,    dst[2] + 4,
+                                             s->uvlinesize, bedge_lim,
+                                             inner_limit,   hev_thresh);
     }
 
     if (mb_y) {
-        s->vp8dsp.vp8_v_loop_filter16(dst[0], s->linesize,   filter_level+2, inner_limit, hev_thresh);
-        s->vp8dsp.vp8_v_loop_filter8 (dst[1], s->uvlinesize, filter_level+2, inner_limit, hev_thresh);
-        s->vp8dsp.vp8_v_loop_filter8 (dst[2], s->uvlinesize, filter_level+2, inner_limit, hev_thresh);
+        s->vp8dsp.vp8_v_loop_filter16y(dst[0],     s->linesize,
+                                       mbedge_lim, inner_limit, hev_thresh);
+        s->vp8dsp.vp8_v_loop_filter8uv(dst[1],     dst[2],      s->uvlinesize,
+                                       mbedge_lim, inner_limit, hev_thresh);
     }
 
     if (!mb->skip || mb->mode == MODE_I4x4 || mb->mode == VP8_MVMODE_SPLIT) {
-        s->vp8dsp.vp8_v_loop_filter16_inner(dst[0]+ 4*s->linesize,   s->linesize,   filter_level, inner_limit, hev_thresh);
-        s->vp8dsp.vp8_v_loop_filter16_inner(dst[0]+ 8*s->linesize,   s->linesize,   filter_level, inner_limit, hev_thresh);
-        s->vp8dsp.vp8_v_loop_filter16_inner(dst[0]+12*s->linesize,   s->linesize,   filter_level, inner_limit, hev_thresh);
-        s->vp8dsp.vp8_v_loop_filter8_inner (dst[1]+ 4*s->uvlinesize, s->uvlinesize, filter_level, inner_limit, hev_thresh);
-        s->vp8dsp.vp8_v_loop_filter8_inner (dst[2]+ 4*s->uvlinesize, s->uvlinesize, filter_level, inner_limit, hev_thresh);
+        s->vp8dsp.vp8_v_loop_filter16y_inner(dst[0]+ 4*s->linesize,
+                                             s->linesize,   bedge_lim,
+                                             inner_limit,   hev_thresh);
+        s->vp8dsp.vp8_v_loop_filter16y_inner(dst[0]+ 8*s->linesize,
+                                             s->linesize,   bedge_lim,
+                                             inner_limit,   hev_thresh);
+        s->vp8dsp.vp8_v_loop_filter16y_inner(dst[0]+12*s->linesize,
+                                             s->linesize,   bedge_lim,
+                                             inner_limit, hev_thresh);
+        s->vp8dsp.vp8_v_loop_filter8uv_inner(dst[1] + 4 * s->uvlinesize,
+                                             dst[2] + 4 * s->uvlinesize,
+                                             s->uvlinesize, bedge_lim,
+                                             inner_limit,   hev_thresh);
     }
 }
 
@@ -1185,6 +1353,7 @@ static void filter_mb_row(VP8Context *s, int mb_y)
     int mb_x;
 
     for (mb_x = 0; mb_x < s->mb_width; mb_x++) {
+        backup_mb_border(s->top_border[mb_x+1], dst[0], dst[1], dst[2], s->linesize, s->uvlinesize, 0);
         filter_mb(s, dst, mb++, mb_x, mb_y);
         dst[0] += 16;
         dst[1] += 8;
@@ -1199,6 +1368,7 @@ static void filter_mb_row_simple(VP8Context *s, int mb_y)
     int mb_x;
 
     for (mb_x = 0; mb_x < s->mb_width; mb_x++) {
+        backup_mb_border(s->top_border[mb_x+1], dst, NULL, NULL, s->linesize, 0, 1);
         filter_mb_simple(s, dst, mb++, mb_x, mb_y);
         dst += 16;
     }
@@ -1225,6 +1395,7 @@ static int vp8_decode_frame(AVCodecContext *avctx, void *data, int *data_size,
         s->invisible = 1;
         goto skip_decode;
     }
+    s->deblock_filter = s->filter.level && avctx->skip_loop_filter < skip_thresh;
 
     for (i = 0; i < 4; i++)
         if (&s->frames[i] != s->framep[VP56_FRAME_PREVIOUS] &&
@@ -1263,11 +1434,7 @@ static int vp8_decode_frame(AVCodecContext *avctx, void *data, int *data_size,
     memset(s->top_nnz, 0, s->mb_width*sizeof(*s->top_nnz));
 
     // top edge of 127 for intra prediction
-    if (!(avctx->flags & CODEC_FLAG_EMU_EDGE)) {
-        memset(curframe->data[0] - s->linesize  -1, 127, s->linesize  +1);
-        memset(curframe->data[1] - s->uvlinesize-1, 127, s->uvlinesize+1);
-        memset(curframe->data[2] - s->uvlinesize-1, 127, s->uvlinesize+1);
-    }
+    memset(s->top_border, 127, (s->mb_width+1)*sizeof(*s->top_border));
 
     for (mb_y = 0; mb_y < s->mb_height; mb_y++) {
         VP56RangeCoder *c = &s->coeff_partition[mb_y & (s->num_coeff_partitions-1)];
@@ -1286,8 +1453,14 @@ static int vp8_decode_frame(AVCodecContext *avctx, void *data, int *data_size,
             for (i = 0; i < 3; i++)
                 for (y = 0; y < 16>>!!i; y++)
                     dst[i][y*curframe->linesize[i]-1] = 129;
+        if (mb_y)
+            memset(s->top_border, 129, sizeof(*s->top_border));
 
         for (mb_x = 0; mb_x < s->mb_width; mb_x++) {
+            /* Prefetch the current frame, 4 MBs ahead */
+            s->dsp.prefetch(dst[0] + (mb_x&3)*4*s->linesize + 64, s->linesize, 4);
+            s->dsp.prefetch(dst[1] + (mb_x&7)*s->uvlinesize + 64, dst[2] - dst[1], 2);
+
             decode_mb_mode(s, mb, mb_x, mb_y, intra4x4 + 4*mb_x);
 
             if (!mb->skip)
@@ -1322,19 +1495,13 @@ static int vp8_decode_frame(AVCodecContext *avctx, void *data, int *data_size,
             dst[2] += 8;
             mb++;
         }
-        if (mb_y && s->filter.level && avctx->skip_loop_filter < skip_thresh) {
+        if (s->deblock_filter) {
             if (s->filter.simple)
-                filter_mb_row_simple(s, mb_y-1);
+                filter_mb_row_simple(s, mb_y);
             else
-                filter_mb_row(s, mb_y-1);
+                filter_mb_row(s, mb_y);
         }
     }
-    if (s->filter.level && avctx->skip_loop_filter < skip_thresh) {
-        if (s->filter.simple)
-            filter_mb_row_simple(s, mb_y-1);
-        else
-            filter_mb_row(s, mb_y-1);
-    }
 
 skip_decode:
     // if future frames don't use the updated probabilities,