Merge "remove unused VP8 com/dec asm offsets"
diff --git a/test/test.mk b/test/test.mk
index 52a54fb..c2a3aec 100644
--- a/test/test.mk
+++ b/test/test.mk
@@ -26,6 +26,7 @@
LIBVPX_TEST_SRCS-$(CONFIG_VP9_ENCODER) += borders_test.cc
LIBVPX_TEST_SRCS-$(CONFIG_VP8_ENCODER) += resize_test.cc
LIBVPX_TEST_SRCS-$(CONFIG_VP9_ENCODER) += cpu_speed_test.cc
+LIBVPX_TEST_SRCS-$(CONFIG_VP9_ENCODER) += vp9_lossless_test.cc
LIBVPX_TEST_SRCS-$(CONFIG_DECODERS) += ../md5_utils.h ../md5_utils.c
LIBVPX_TEST_SRCS-yes += decode_test_driver.cc
diff --git a/test/vp9_lossless_test.cc b/test/vp9_lossless_test.cc
new file mode 100644
index 0000000..441cc44
--- /dev/null
+++ b/test/vp9_lossless_test.cc
@@ -0,0 +1,75 @@
+/*
+ Copyright (c) 2012 The WebM project authors. All Rights Reserved.
+
+ Use of this source code is governed by a BSD-style license
+ that can be found in the LICENSE file in the root of the source
+ tree. An additional intellectual property rights grant can be found
+ in the file PATENTS. All contributing project authors may
+ be found in the AUTHORS file in the root of the source tree.
+*/
+
+#include "third_party/googletest/src/include/gtest/gtest.h"
+#include "test/codec_factory.h"
+#include "test/encode_test_driver.h"
+#include "test/i420_video_source.h"
+#include "test/util.h"
+
+namespace {
+
+const int kMaxPsnr = 100;
+
+class LossLessTest : public ::libvpx_test::EncoderTest,
+ public ::libvpx_test::CodecTestWithParam<libvpx_test::TestMode> {
+ protected:
+ LossLessTest() : EncoderTest(GET_PARAM(0)),
+ psnr_(kMaxPsnr),
+ nframes_(0),
+ encoding_mode_(GET_PARAM(1)) {
+ }
+
+ virtual ~LossLessTest() {}
+
+ virtual void SetUp() {
+ InitializeConfig();
+ SetMode(encoding_mode_);
+ }
+
+ virtual void BeginPassHook(unsigned int /*pass*/) {
+ psnr_ = 0.0;
+ nframes_ = 0;
+ }
+
+ virtual void PSNRPktHook(const vpx_codec_cx_pkt_t *pkt) {
+ if (pkt->data.psnr.psnr[0] < psnr_)
+ psnr_= pkt->data.psnr.psnr[0];
+ }
+
+ double GetMinPsnr() const {
+ return psnr_;
+ }
+
+ private:
+ double psnr_;
+ unsigned int nframes_;
+ libvpx_test::TestMode encoding_mode_;
+};
+
+TEST_P(LossLessTest, TestLossLessEncoding) {
+ const vpx_rational timebase = { 33333333, 1000000000 };
+ cfg_.g_timebase = timebase;
+ cfg_.rc_target_bitrate = 2000;
+ cfg_.g_lag_in_frames = 25;
+ cfg_.rc_min_quantizer = 0;
+ cfg_.rc_max_quantizer = 0;
+
+ init_flags_ = VPX_CODEC_USE_PSNR;
+
+ // intentionally changed the dimension for better testing coverage
+ libvpx_test::I420VideoSource video("hantro_collage_w352h288.yuv", 356, 284,
+ timebase.den, timebase.num, 0, 30);
+
+ const double psnr_lossless = GetMinPsnr();
+ EXPECT_GE(psnr_lossless, kMaxPsnr);
+}
+VP9_INSTANTIATE_TEST_CASE(LossLessTest, ALL_TEST_MODES);
+} // namespace
diff --git a/vp9/common/arm/neon/vp9_loopfilter_neon.asm b/vp9/common/arm/neon/vp9_loopfilter_neon.asm
index 5011315..02cf2f5 100644
--- a/vp9/common/arm/neon/vp9_loopfilter_neon.asm
+++ b/vp9/common/arm/neon/vp9_loopfilter_neon.asm
@@ -10,6 +10,8 @@
EXPORT |vp9_loop_filter_horizontal_edge_neon|
EXPORT |vp9_loop_filter_vertical_edge_neon|
+ EXPORT |vp9_mbloop_filter_horizontal_edge_neon|
+ EXPORT |vp9_mbloop_filter_vertical_edge_neon|
ARM
AREA ||.text||, CODE, READONLY, ALIGN=2
@@ -33,50 +35,47 @@
; sp const uint8_t *thresh,
; sp+4 int count
|vp9_loop_filter_horizontal_edge_neon| PROC
- push {r4-r6, lr}
+ push {lr}
- ldr r12, [sp,#20] ; load count
- ldrb r4, [r2] ; load *blimit
- ldrb r5, [r3] ; load *limit
+ ldr r12, [sp,#8] ; load count
cmp r12, #0
beq end_vp9_lf_h_edge
- ldr r3, [sp, #16] ; load thresh
- vdup.u8 d0, r4 ; duplicate blimit
- ldrb r6, [r3] ; load *thresh
- vdup.u8 d1, r5 ; duplicate limit
- vdup.u8 d2, r6 ; duplicate thresh
+ vld1.8 {d0[]}, [r2] ; duplicate *blimit
+ ldr r2, [sp, #4] ; load thresh
+ vld1.8 {d1[]}, [r3] ; duplicate *limit
+ vld1.8 {d2[]}, [r2] ; duplicate *thresh
count_lf_h_loop
sub r2, r0, r1, lsl #2 ; move src pointer down by 4 lines
- add r6, r2, r1
+ add r3, r2, r1
add r1, r1, r1
vld1.u8 {d3}, [r2@64], r1 ; p3
- vld1.u8 {d4}, [r6@64], r1 ; p2
+ vld1.u8 {d4}, [r3@64], r1 ; p2
vld1.u8 {d5}, [r2@64], r1 ; p1
- vld1.u8 {d6}, [r6@64], r1 ; p0
+ vld1.u8 {d6}, [r3@64], r1 ; p0
vld1.u8 {d7}, [r2@64], r1 ; q0
- vld1.u8 {d16}, [r6@64], r1 ; q1
+ vld1.u8 {d16}, [r3@64], r1 ; q1
vld1.u8 {d17}, [r2@64] ; q2
- vld1.u8 {d18}, [r6@64] ; q3
+ vld1.u8 {d18}, [r3@64] ; q3
sub r2, r2, r1, lsl #1
- sub r6, r6, r1, lsl #1
+ sub r3, r3, r1, lsl #1
bl vp9_loop_filter_neon
vst1.u8 {d4}, [r2@64], r1 ; store op1
- vst1.u8 {d5}, [r6@64], r1 ; store op0
+ vst1.u8 {d5}, [r3@64], r1 ; store op0
vst1.u8 {d6}, [r2@64], r1 ; store oq0
- vst1.u8 {d7}, [r6@64], r1 ; store oq1
+ vst1.u8 {d7}, [r3@64], r1 ; store oq1
add r0, r0, #8
subs r12, r12, #1
bne count_lf_h_loop
end_vp9_lf_h_edge
- pop {r4-r6, pc}
+ pop {pc}
ENDP ; |vp9_loop_filter_horizontal_edge_neon|
; Currently vp9 only works on iterations 8 at a time. The vp8 loop filter
@@ -98,31 +97,28 @@
; sp const uint8_t *thresh,
; sp+4 int count
|vp9_loop_filter_vertical_edge_neon| PROC
- push {r4-r6, lr}
+ push {lr}
- ldr r12, [sp,#20] ; load count
- ldrb r4, [r2] ; load *blimit
- ldrb r5, [r3] ; load *limit
+ ldr r12, [sp,#8] ; load count
cmp r12, #0
beq end_vp9_lf_v_edge
- ldr r3, [sp, #16] ; load thresh
- vdup.u8 d0, r4 ; duplicate blimit
- ldrb r6, [r3] ; load *thresh
- vdup.u8 d1, r5 ; duplicate limit
- vdup.u8 d2, r6 ; duplicate thresh
+ vld1.8 {d0[]}, [r2] ; duplicate *blimit
+ ldr r2, [sp, #4] ; load thresh
+ vld1.8 {d1[]}, [r3] ; duplicate *limit
+ vld1.8 {d2[]}, [r2] ; duplicate *thresh
count_lf_v_loop
- sub r6, r0, #4 ; move s pointer down by 4 columns
+ sub r2, r0, #4 ; move s pointer down by 4 columns
- vld1.u8 {d3}, [r6], r1 ; load s data
- vld1.u8 {d4}, [r6], r1
- vld1.u8 {d5}, [r6], r1
- vld1.u8 {d6}, [r6], r1
- vld1.u8 {d7}, [r6], r1
- vld1.u8 {d16}, [r6], r1
- vld1.u8 {d17}, [r6], r1
- vld1.u8 {d18}, [r6]
+ vld1.u8 {d3}, [r2], r1 ; load s data
+ vld1.u8 {d4}, [r2], r1
+ vld1.u8 {d5}, [r2], r1
+ vld1.u8 {d6}, [r2], r1
+ vld1.u8 {d7}, [r2], r1
+ vld1.u8 {d16}, [r2], r1
+ vld1.u8 {d17}, [r2], r1
+ vld1.u8 {d18}, [r2]
;transpose to 8x16 matrix
vtrn.32 d3, d7
@@ -159,7 +155,7 @@
bne count_lf_v_loop
end_vp9_lf_v_edge
- pop {r4-r6, pc}
+ pop {pc}
ENDP ; |vp9_loop_filter_vertical_edge_neon|
; void vp9_loop_filter_neon();
@@ -224,7 +220,7 @@
vsub.s8 d28, d7, d6 ; ( qs0 - ps0)
- vcge.u8 d17, d0, d17 ; (a > blimit * 2 + limit) * -1
+ vcge.u8 d17, d0, d17 ; a > blimit
vqsub.s8 d27, d5, d16 ; filter = clamp(ps1-qs1)
vorr d22, d21, d22 ; hevmask
@@ -267,4 +263,331 @@
bx lr
ENDP ; |vp9_loop_filter_neon|
+; void vp9_mbloop_filter_horizontal_edge_neon(uint8_t *s, int p,
+; const uint8_t *blimit,
+; const uint8_t *limit,
+; const uint8_t *thresh,
+; int count)
+; r0 uint8_t *s,
+; r1 int p, /* pitch */
+; r2 const uint8_t *blimit,
+; r3 const uint8_t *limit,
+; sp const uint8_t *thresh,
+; sp+4 int count
+|vp9_mbloop_filter_horizontal_edge_neon| PROC
+ push {lr}
+
+ ldr r12, [sp,#8] ; load count
+ cmp r12, #0
+ beq end_vp9_mblf_h_edge
+
+ vld1.8 {d0[]}, [r2] ; duplicate *blimit
+ ldr r2, [sp, #4] ; load thresh
+ vld1.8 {d1[]}, [r3] ; duplicate *limit
+ vld1.8 {d2[]}, [r2] ; duplicate *thresh
+
+count_mblf_h_loop
+ sub r3, r0, r1, lsl #2 ; move src pointer down by 4 lines
+ add r2, r3, r1
+ add r1, r1, r1
+
+ vld1.u8 {d3}, [r3@64], r1 ; p3
+ vld1.u8 {d4}, [r2@64], r1 ; p2
+ vld1.u8 {d5}, [r3@64], r1 ; p1
+ vld1.u8 {d6}, [r2@64], r1 ; p0
+ vld1.u8 {d7}, [r3@64], r1 ; q0
+ vld1.u8 {d16}, [r2@64], r1 ; q1
+ vld1.u8 {d17}, [r3@64] ; q2
+ vld1.u8 {d18}, [r2@64], r1 ; q3
+
+ sub r3, r3, r1, lsl #1
+ sub r2, r2, r1, lsl #2
+
+ bl vp9_mbloop_filter_neon
+
+ vst1.u8 {d2}, [r2@64], r1 ; store op2
+ vst1.u8 {d3}, [r3@64], r1 ; store op1
+ vst1.u8 {d4}, [r2@64], r1 ; store op0
+ vst1.u8 {d5}, [r3@64], r1 ; store oq0
+ vst1.u8 {d6}, [r2@64], r1 ; store oq1
+ vst1.u8 {d7}, [r3@64], r1 ; store oq2
+
+ add r0, r0, #8
+ subs r12, r12, #1
+ bne count_mblf_h_loop
+
+end_vp9_mblf_h_edge
+ pop {pc}
+
+ ENDP ; |vp9_mbloop_filter_horizontal_edge_neon|
+
+; void vp9_mbloop_filter_vertical_edge_neon(uint8_t *s,
+; int pitch,
+; const uint8_t *blimit,
+; const uint8_t *limit,
+; const uint8_t *thresh,
+; int count)
+;
+; r0 uint8_t *s,
+; r1 int pitch,
+; r2 const uint8_t *blimit,
+; r3 const uint8_t *limit,
+; sp const uint8_t *thresh,
+; sp+4 int count
+|vp9_mbloop_filter_vertical_edge_neon| PROC
+ push {lr}
+
+ ldr r12, [sp,#8] ; load count
+ cmp r12, #0
+ beq end_vp9_mblf_v_edge
+
+ vld1.8 {d0[]}, [r2] ; duplicate *blimit
+ ldr r2, [sp, #4] ; load thresh
+ vld1.8 {d1[]}, [r3] ; duplicate *limit
+ vld1.8 {d2[]}, [r2] ; duplicate *thresh
+
+count_mblf_v_loop
+ sub r2, r0, #4 ; move s pointer down by 4 columns
+
+ vld1.u8 {d3}, [r2], r1 ; load s data
+ vld1.u8 {d4}, [r2], r1
+ vld1.u8 {d5}, [r2], r1
+ vld1.u8 {d6}, [r2], r1
+ vld1.u8 {d7}, [r2], r1
+ vld1.u8 {d16}, [r2], r1
+ vld1.u8 {d17}, [r2], r1
+ vld1.u8 {d18}, [r2]
+
+ ;transpose to 8x16 matrix
+ vtrn.32 d3, d7
+ vtrn.32 d4, d16
+ vtrn.32 d5, d17
+ vtrn.32 d6, d18
+
+ vtrn.16 d3, d5
+ vtrn.16 d4, d6
+ vtrn.16 d7, d17
+ vtrn.16 d16, d18
+
+ vtrn.8 d3, d4
+ vtrn.8 d5, d6
+ vtrn.8 d7, d16
+ vtrn.8 d17, d18
+
+ sub r2, r0, #3
+ add r3, r0, #1
+
+ bl vp9_mbloop_filter_neon
+
+ ;store op2, op1, op0, oq0
+ vst4.8 {d2[0], d3[0], d4[0], d5[0]}, [r2], r1
+ vst4.8 {d2[1], d3[1], d4[1], d5[1]}, [r2], r1
+ vst4.8 {d2[2], d3[2], d4[2], d5[2]}, [r2], r1
+ vst4.8 {d2[3], d3[3], d4[3], d5[3]}, [r2], r1
+ vst4.8 {d2[4], d3[4], d4[4], d5[4]}, [r2], r1
+ vst4.8 {d2[5], d3[5], d4[5], d5[5]}, [r2], r1
+ vst4.8 {d2[6], d3[6], d4[6], d5[6]}, [r2], r1
+ vst4.8 {d2[7], d3[7], d4[7], d5[7]}, [r2]
+
+ ;store oq1, oq2
+ vst2.8 {d6[0], d7[0]}, [r3], r1
+ vst2.8 {d6[1], d7[1]}, [r3], r1
+ vst2.8 {d6[2], d7[2]}, [r3], r1
+ vst2.8 {d6[3], d7[3]}, [r3], r1
+ vst2.8 {d6[4], d7[4]}, [r3], r1
+ vst2.8 {d6[5], d7[5]}, [r3], r1
+ vst2.8 {d6[6], d7[6]}, [r3], r1
+ vst2.8 {d6[7], d7[7]}, [r3]
+
+ add r0, r0, r1, lsl #3 ; s += pitch * 8
+ subs r12, r12, #1
+ bne count_mblf_v_loop
+
+end_vp9_mblf_v_edge
+ pop {pc}
+ ENDP ; |vp9_mbloop_filter_vertical_edge_neon|
+
+; void vp9_mbloop_filter_neon();
+; This is a helper function for the loopfilters. The invidual functions do the
+; necessary load, transpose (if necessary) and store. The function does not use
+; registers d8-d15.
+;
+; r0-r3 PRESERVE
+; d0 blimit
+; d1 limit
+; d2 thresh
+; d3 p3
+; d4 p2
+; d5 p1
+; d6 p0
+; d7 q0
+; d16 q1
+; d17 q2
+; d18 q3
+|vp9_mbloop_filter_neon| PROC
+ ; filter_mask
+ vabd.u8 d19, d3, d4 ; abs(p3 - p2)
+ vabd.u8 d20, d4, d5 ; abs(p2 - p1)
+ vabd.u8 d21, d5, d6 ; abs(p1 - p0)
+ vabd.u8 d22, d16, d7 ; abs(q1 - q0)
+ vabd.u8 d23, d17, d16 ; abs(q2 - q1)
+ vabd.u8 d24, d18, d17 ; abs(q3 - q2)
+
+ ; only compare the largest value to limit
+ vmax.u8 d19, d19, d20 ; max(abs(p3 - p2), abs(p2 - p1))
+ vmax.u8 d20, d21, d22 ; max(abs(p1 - p0), abs(q1 - q0))
+ vmax.u8 d23, d23, d24 ; max(abs(q2 - q1), abs(q3 - q2))
+ vmax.u8 d19, d19, d20
+
+ vabd.u8 d24, d6, d7 ; abs(p0 - q0)
+
+ vmax.u8 d19, d19, d23
+
+ vabd.u8 d23, d5, d16 ; a = abs(p1 - q1)
+ vqadd.u8 d24, d24, d24 ; b = abs(p0 - q0) * 2
+
+ ; abs () > limit
+ vcge.u8 d19, d1, d19
+
+ ; flatmask4
+ vabd.u8 d25, d6, d4 ; abs(p0 - p2)
+ vabd.u8 d26, d7, d17 ; abs(q0 - q2)
+ vabd.u8 d27, d3, d6 ; abs(p3 - p0)
+ vabd.u8 d28, d18, d7 ; abs(q3 - q0)
+
+ ; only compare the largest value to thresh
+ vmax.u8 d25, d25, d26 ; max(abs(p0 - p2), abs(q0 - q2))
+ vmax.u8 d26, d27, d28 ; max(abs(p3 - p0), abs(q3 - q0))
+ vmax.u8 d25, d25, d26
+ vmax.u8 d20, d20, d25
+
+ vshr.u8 d23, d23, #1 ; a = a / 2
+ vqadd.u8 d24, d24, d23 ; a = b + a
+
+ vmov.u8 d23, #1
+ vcge.u8 d24, d0, d24 ; a > blimit
+
+ vcge.u8 d20, d23, d20 ; flat
+
+ vand d19, d19, d24 ; mask
+
+ ; hevmask
+ vcgt.u8 d21, d21, d2 ; (abs(p1 - p0) > thresh)*-1
+ vcgt.u8 d22, d22, d2 ; (abs(q1 - q0) > thresh)*-1
+ vorr d21, d21, d22 ; hev
+
+ vmov.u8 d22, #0x80
+
+ ; mbfilter() function
+
+ ; filter() function
+ ; convert to signed
+ veor d23, d7, d22 ; qs0
+ veor d24, d6, d22 ; ps0
+ veor d25, d5, d22 ; ps1
+ veor d26, d16, d22 ; qs1
+
+ vmov.u8 d27, #3
+
+ vsub.s8 d28, d23, d24 ; ( qs0 - ps0)
+
+ vqsub.s8 d29, d25, d26 ; filter = clamp(ps1-qs1)
+
+ vmull.s8 q15, d28, d27 ; 3 * ( qs0 - ps0)
+
+ vand d29, d29, d21 ; filter &= hev
+
+ vaddw.s8 q15, q15, d29 ; filter + 3 * (qs0 - ps0)
+
+ vmov.u8 d29, #4
+
+ ; filter = clamp(filter + 3 * ( qs0 - ps0))
+ vqmovn.s16 d28, q15
+
+ vand d28, d28, d19 ; filter &= mask
+
+ vqadd.s8 d30, d28, d27 ; filter2 = clamp(filter+3)
+ vqadd.s8 d29, d28, d29 ; filter1 = clamp(filter+4)
+ vshr.s8 d30, d30, #3 ; filter2 >>= 3
+ vshr.s8 d29, d29, #3 ; filter1 >>= 3
+
+ vand d20, d20, d19 ; flat & mask
+
+ vqadd.s8 d24, d24, d30 ; op0 = clamp(ps0 + filter2)
+ vqsub.s8 d23, d23, d29 ; oq0 = clamp(qs0 - filter1)
+
+ ; outer tap adjustments: ++filter1 >> 1
+ vrshr.s8 d29, d29, #1
+ vbic d29, d29, d21 ; filter &= ~hev
+
+ vqadd.s8 d25, d25, d29 ; op1 = clamp(ps1 + filter)
+ vqsub.s8 d26, d26, d29 ; oq1 = clamp(qs1 - filter)
+
+ veor d24, d24, d22 ; *f_op0 = u^0x80
+ veor d23, d23, d22 ; *f_oq0 = u^0x80
+ veor d25, d25, d22 ; *f_op1 = u^0x80
+ veor d26, d26, d22 ; *f_oq1 = u^0x80
+
+ ; mbfilter flat && mask branch
+ ; TODO(fgalligan): Can I decrease the cycles shifting to consective d's
+ ; and using vibt on the q's?
+ vmov.u8 d21, #2
+ vaddl.u8 q14, d6, d7 ; op2 = p0 + q0
+ vmlal.u8 q14, d3, d27 ; op2 += p3 * 3
+ vmlal.u8 q14, d4, d21 ; op2 += p2 * 2
+ vaddw.u8 q14, d5 ; op2 += p1
+ vqrshrn.u16 d30, q14, #3 ; r_op2
+
+ vsubw.u8 q14, d3 ; op1 = op2 - p3
+ vsubw.u8 q14, d4 ; op1 -= p2
+ vaddw.u8 q14, d5 ; op1 += p1
+ vaddw.u8 q14, d16 ; op1 += q1
+ vqrshrn.u16 d31, q14, #3 ; r_op1
+
+ vsubw.u8 q14, d3 ; op0 = op1 - p3
+ vsubw.u8 q14, d5 ; op0 -= p1
+ vaddw.u8 q14, d6 ; op0 += p0
+ vaddw.u8 q14, d17 ; op0 += q2
+ vqrshrn.u16 d21, q14, #3 ; r_op0
+
+ vsubw.u8 q14, d3 ; oq0 = op0 - p3
+ vsubw.u8 q14, d6 ; oq0 -= p0
+ vaddw.u8 q14, d7 ; oq0 += q0
+ vaddw.u8 q14, d18 ; oq0 += q3
+ vqrshrn.u16 d22, q14, #3 ; r_oq0
+
+ vsubw.u8 q14, d4 ; oq1 = oq0 - p2
+ vsubw.u8 q14, d7 ; oq1 -= q0
+ vaddw.u8 q14, d16 ; oq1 += q1
+ vaddw.u8 q14, d18 ; oq1 += q3
+ vqrshrn.u16 d0, q14, #3 ; r_oq1
+
+ vsubw.u8 q14, d5 ; oq2 = oq0 - p1
+ vsubw.u8 q14, d16 ; oq2 -= q1
+ vaddw.u8 q14, d17 ; oq2 += q2
+ vaddw.u8 q14, d18 ; oq2 += q3
+ vqrshrn.u16 d1, q14, #3 ; r_oq2
+
+ ; Filter does not set op2 or oq2, so use p2 and q2.
+ vbit d2, d30, d20 ; op2 |= r_op2 & (flat & mask)
+ vbif d2, d4, d20 ; op2 |= op2 & ~(flat & mask)
+
+ vbit d3, d31, d20 ; op1 |= r_op1 & (flat & mask)
+ vbif d3, d25, d20 ; op1 |= f_op1 & ~(flat & mask)
+
+ vbit d4, d21, d20 ; op0 |= r_op0 & (flat & mask)
+ vbif d4, d24, d20 ; op0 |= f_op0 & ~(flat & mask)
+
+ vbit d5, d22, d20 ; oq0 |= r_oq0 & (flat & mask)
+ vbif d5, d23, d20 ; oq0 |= f_oq0 & ~(flat & mask)
+
+ vbit d6, d0, d20 ; oq1 |= r_oq1 & (flat & mask)
+ vbif d6, d26, d20 ; oq1 |= f_oq1 & ~(flat & mask)
+
+ vbit d7, d1, d20 ; oq2 |= r_oq2 & (flat & mask)
+ vbif d7, d17, d20 ; oq2 |= oq2 & ~(flat & mask)
+
+ bx lr
+ ENDP ; |vp9_mbloop_filter_neon|
+
END
diff --git a/vp9/common/vp9_loopfilter.c b/vp9/common/vp9_loopfilter.c
index ef8ed2b..9d2e2ea 100644
--- a/vp9/common/vp9_loopfilter.c
+++ b/vp9/common/vp9_loopfilter.c
@@ -35,16 +35,12 @@
void vp9_loop_filter_update_sharpness(loop_filter_info_n *lfi,
int sharpness_lvl) {
- int i;
+ int lvl;
- /* For each possible value for the loop filter fill out limits */
- for (i = 0; i <= MAX_LOOP_FILTER; i++) {
- int filt_lvl = i;
- int block_inside_limit = 0;
-
- /* Set loop filter paramaeters that control sharpness. */
- block_inside_limit = filt_lvl >> (sharpness_lvl > 0);
- block_inside_limit = block_inside_limit >> (sharpness_lvl > 4);
+ // For each possible value for the loop filter fill out limits
+ for (lvl = 0; lvl <= MAX_LOOP_FILTER; lvl++) {
+ // Set loop filter paramaeters that control sharpness.
+ int block_inside_limit = lvl >> ((sharpness_lvl > 0) + (sharpness_lvl > 4));
if (sharpness_lvl > 0) {
if (block_inside_limit > (9 - sharpness_lvl))
@@ -54,10 +50,9 @@
if (block_inside_limit < 1)
block_inside_limit = 1;
- vpx_memset(lfi->lim[i], block_inside_limit, SIMD_WIDTH);
- vpx_memset(lfi->blim[i], (2 * filt_lvl + block_inside_limit),
- SIMD_WIDTH);
- vpx_memset(lfi->mblim[i], (2 * (filt_lvl + 2) + block_inside_limit),
+ vpx_memset(lfi->lim[lvl], block_inside_limit, SIMD_WIDTH);
+ vpx_memset(lfi->blim[lvl], (2 * lvl + block_inside_limit), SIMD_WIDTH);
+ vpx_memset(lfi->mblim[lvl], (2 * (lvl + 2) + block_inside_limit),
SIMD_WIDTH);
}
}
@@ -78,98 +73,68 @@
vpx_memset(lfi->hev_thr[i], i, SIMD_WIDTH);
}
-void vp9_loop_filter_frame_init(VP9_COMMON *cm,
- MACROBLOCKD *xd,
+void vp9_loop_filter_frame_init(VP9_COMMON *cm, MACROBLOCKD *xd,
int default_filt_lvl) {
- int seg, // segment number
- ref, // index in ref_lf_deltas
- mode; // index in mode_lf_deltas
+ int seg;
// n_shift is the a multiplier for lf_deltas
// the multiplier is 1 for when filter_lvl is between 0 and 31;
// 2 when filter_lvl is between 32 and 63
- int n_shift = default_filt_lvl >> 5;
+ const int n_shift = default_filt_lvl >> 5;
+ loop_filter_info_n *const lfi = &cm->lf_info;
- loop_filter_info_n *lfi = &cm->lf_info;
-
- /* update limits if sharpness has changed */
- // printf("vp9_loop_filter_frame_init %d\n", default_filt_lvl);
- // printf("sharpness level: %d [%d]\n",
- // cm->sharpness_level, cm->last_sharpness_level);
+ // update limits if sharpness has changed
if (cm->last_sharpness_level != cm->sharpness_level) {
vp9_loop_filter_update_sharpness(lfi, cm->sharpness_level);
cm->last_sharpness_level = cm->sharpness_level;
}
for (seg = 0; seg < MAX_MB_SEGMENTS; seg++) {
- int lvl_seg = default_filt_lvl;
- int lvl_ref, lvl_mode;
-
+ int lvl_seg = default_filt_lvl, ref, mode, intra_lvl;
// Set the baseline filter values for each segment
if (vp9_segfeature_active(xd, seg, SEG_LVL_ALT_LF)) {
- /* Abs value */
- if (xd->mb_segment_abs_delta == SEGMENT_ABSDATA) {
- lvl_seg = vp9_get_segdata(xd, seg, SEG_LVL_ALT_LF);
- } else { /* Delta Value */
- lvl_seg += vp9_get_segdata(xd, seg, SEG_LVL_ALT_LF);
- lvl_seg = clamp(lvl_seg, 0, 63);
- }
+ const int data = vp9_get_segdata(xd, seg, SEG_LVL_ALT_LF);
+ lvl_seg = xd->mb_segment_abs_delta == SEGMENT_ABSDATA
+ ? data
+ : clamp(default_filt_lvl + data, 0, MAX_LOOP_FILTER);
}
if (!xd->mode_ref_lf_delta_enabled) {
- /* we could get rid of this if we assume that deltas are set to
- * zero when not in use; encoder always uses deltas
- */
+ // we could get rid of this if we assume that deltas are set to
+ // zero when not in use; encoder always uses deltas
vpx_memset(lfi->lvl[seg][0], lvl_seg, 4 * 4);
continue;
}
- lvl_ref = lvl_seg;
+ intra_lvl = lvl_seg + (xd->ref_lf_deltas[INTRA_FRAME] << n_shift);
+ lfi->lvl[seg][INTRA_FRAME][0] = clamp(intra_lvl, 0, MAX_LOOP_FILTER);
- /* INTRA_FRAME */
- ref = INTRA_FRAME;
-
- /* Apply delta for reference frame */
- lvl_ref += xd->ref_lf_deltas[ref] << n_shift;
-
- mode = 0; /* all the rest of Intra modes */
- lvl_mode = lvl_ref;
- lfi->lvl[seg][ref][mode] = clamp(lvl_mode, 0, 63);
-
- /* LAST, GOLDEN, ALT */
- for (ref = 1; ref < MAX_REF_FRAMES; ref++) {
- int lvl_ref = lvl_seg;
-
- /* Apply delta for reference frame */
- lvl_ref += xd->ref_lf_deltas[ref] << n_shift;
-
- /* Apply delta for Inter modes */
- for (mode = 0; mode < MAX_MODE_LF_DELTAS; mode++) {
- lvl_mode = lvl_ref + (xd->mode_lf_deltas[mode] << n_shift);
- lfi->lvl[seg][ref][mode] = clamp(lvl_mode, 0, 63);
+ for (ref = LAST_FRAME; ref < MAX_REF_FRAMES; ++ref)
+ for (mode = 0; mode < MAX_MODE_LF_DELTAS; ++mode) {
+ const int inter_lvl = lvl_seg + (xd->ref_lf_deltas[ref] << n_shift)
+ + (xd->mode_lf_deltas[mode] << n_shift);
+ lfi->lvl[seg][ref][mode] = clamp(inter_lvl, 0, MAX_LOOP_FILTER);
}
- }
}
}
static int build_lfi(const VP9_COMMON *cm, const MB_MODE_INFO *mbmi,
struct loop_filter_info *lfi) {
- const loop_filter_info_n *lfi_n = &cm->lf_info;
- int mode = mbmi->mode;
- int mode_index = lfi_n->mode_lf_lut[mode];
- int seg = mbmi->segment_id;
- int ref_frame = mbmi->ref_frame[0];
- int filter_level = lfi_n->lvl[seg][ref_frame][mode_index];
+ const loop_filter_info_n *const lfi_n = &cm->lf_info;
+ const int seg = mbmi->segment_id;
+ const int ref = mbmi->ref_frame[0];
+ const int mode = lfi_n->mode_lf_lut[mbmi->mode];
+ const int filter_level = lfi_n->lvl[seg][ref][mode];
- if (filter_level) {
- const int hev_index = filter_level >> 4;
+ if (filter_level > 0) {
lfi->mblim = lfi_n->mblim[filter_level];
lfi->blim = lfi_n->blim[filter_level];
lfi->lim = lfi_n->lim[filter_level];
- lfi->hev_thr = lfi_n->hev_thr[hev_index];
+ lfi->hev_thr = lfi_n->hev_thr[filter_level >> 4];
return 1;
+ } else {
+ return 0;
}
- return 0;
}
static void filter_selectively_vert(uint8_t *s, int pitch,
diff --git a/vp9/common/vp9_loopfilter.h b/vp9/common/vp9_loopfilter.h
index ce954c0..2582979 100644
--- a/vp9/common/vp9_loopfilter.h
+++ b/vp9/common/vp9_loopfilter.h
@@ -16,34 +16,35 @@
#include "vp9/common/vp9_blockd.h"
#define MAX_LOOP_FILTER 63
+#define MAX_SHARPNESS 7
+
#define SIMD_WIDTH 16
-/* Need to align this structure so when it is declared and
- * passed it can be loaded into vector registers.
- */
+// Need to align this structure so when it is declared and
+// passed it can be loaded into vector registers.
typedef struct {
- DECLARE_ALIGNED(SIMD_WIDTH, unsigned char,
+ DECLARE_ALIGNED(SIMD_WIDTH, uint8_t,
mblim[MAX_LOOP_FILTER + 1][SIMD_WIDTH]);
- DECLARE_ALIGNED(SIMD_WIDTH, unsigned char,
+ DECLARE_ALIGNED(SIMD_WIDTH, uint8_t,
blim[MAX_LOOP_FILTER + 1][SIMD_WIDTH]);
- DECLARE_ALIGNED(SIMD_WIDTH, unsigned char,
+ DECLARE_ALIGNED(SIMD_WIDTH, uint8_t,
lim[MAX_LOOP_FILTER + 1][SIMD_WIDTH]);
- DECLARE_ALIGNED(SIMD_WIDTH, unsigned char,
+ DECLARE_ALIGNED(SIMD_WIDTH, uint8_t,
hev_thr[4][SIMD_WIDTH]);
- unsigned char lvl[MAX_MB_SEGMENTS][4][4];
- unsigned char mode_lf_lut[MB_MODE_COUNT];
+ uint8_t lvl[MAX_MB_SEGMENTS][MAX_REF_FRAMES][MAX_MODE_LF_DELTAS];
+ uint8_t mode_lf_lut[MB_MODE_COUNT];
} loop_filter_info_n;
struct loop_filter_info {
- const unsigned char *mblim;
- const unsigned char *blim;
- const unsigned char *lim;
- const unsigned char *hev_thr;
+ const uint8_t *mblim;
+ const uint8_t *blim;
+ const uint8_t *lim;
+ const uint8_t *hev_thr;
};
#define prototype_loopfilter(sym) \
- void sym(uint8_t *src, int pitch, const unsigned char *blimit, \
- const unsigned char *limit, const unsigned char *thresh, int count)
+ void sym(uint8_t *src, int pitch, const uint8_t *blimit, \
+ const uint8_t *limit, const uint8_t *thresh, int count)
#define prototype_loopfilter_block(sym) \
void sym(uint8_t *y, uint8_t *u, uint8_t *v, \
@@ -53,11 +54,10 @@
#include "x86/vp9_loopfilter_x86.h"
#endif
-typedef void loop_filter_uvfunction(uint8_t *u, /* source pointer */
- int p, /* pitch */
- const unsigned char *blimit,
- const unsigned char *limit,
- const unsigned char *thresh,
+typedef void loop_filter_uvfunction(uint8_t *src, int pitch,
+ const uint8_t *blimit,
+ const uint8_t *limit,
+ const uint8_t *thresh,
uint8_t *v);
/* assorted loopfilter functions which get used elsewhere */
diff --git a/vp9/common/vp9_rtcd_defs.sh b/vp9/common/vp9_rtcd_defs.sh
index 7e2cc6d..c6167d2 100644
--- a/vp9/common/vp9_rtcd_defs.sh
+++ b/vp9/common/vp9_rtcd_defs.sh
@@ -109,7 +109,7 @@
specialize vp9_mb_lpf_vertical_edge_w sse2
prototype void vp9_mbloop_filter_vertical_edge "uint8_t *s, int pitch, const uint8_t *blimit, const uint8_t *limit, const uint8_t *thresh, int count"
-specialize vp9_mbloop_filter_vertical_edge sse2
+specialize vp9_mbloop_filter_vertical_edge sse2 neon
prototype void vp9_loop_filter_vertical_edge "uint8_t *s, int pitch, const uint8_t *blimit, const uint8_t *limit, const uint8_t *thresh, int count"
specialize vp9_loop_filter_vertical_edge mmx neon
@@ -118,7 +118,7 @@
specialize vp9_mb_lpf_horizontal_edge_w sse2
prototype void vp9_mbloop_filter_horizontal_edge "uint8_t *s, int pitch, const uint8_t *blimit, const uint8_t *limit, const uint8_t *thresh, int count"
-specialize vp9_mbloop_filter_horizontal_edge sse2
+specialize vp9_mbloop_filter_horizontal_edge sse2 neon
prototype void vp9_loop_filter_horizontal_edge "uint8_t *s, int pitch, const uint8_t *blimit, const uint8_t *limit, const uint8_t *thresh, int count"
specialize vp9_loop_filter_horizontal_edge mmx neon
diff --git a/vp9/common/vp9_seg_common.c b/vp9/common/vp9_seg_common.c
index df7747c..9bf2072 100644
--- a/vp9/common/vp9_seg_common.c
+++ b/vp9/common/vp9_seg_common.c
@@ -13,7 +13,9 @@
#include "vp9/common/vp9_seg_common.h"
static const int seg_feature_data_signed[SEG_LVL_MAX] = { 1, 1, 0, 0 };
-static const int seg_feature_data_max[SEG_LVL_MAX] = { MAXQ, 63, 3, 0 };
+
+static const int seg_feature_data_max[SEG_LVL_MAX] = {
+ MAXQ, MAX_LOOP_FILTER, 3, 0 };
// These functions provide access to new segment level features.
// Eventually these function may be "optimized out" but for the moment,