mirror of
				https://github.com/webmproject/libwebp.git
				synced 2025-10-31 10:25:46 +01:00 
			
		
		
		
	MIPS: dspr2: added optimization for (un)filters
HorizontalFilter VerticalFilter GradientFilter HorizontalUnfilter VerticalUnfilter GradientUnfilter Change-Id: I54055b4767c37719691811072e95bf79c1f627b1
This commit is contained in:
		
				
					committed by
					
						 Gerrit Code Review
						Gerrit Code Review
					
				
			
			
				
	
			
			
			
						parent
						
							137e609016
						
					
				
				
					commit
					b4dc4069a2
				
			| @@ -44,6 +44,7 @@ LOCAL_SRC_FILES := \ | ||||
|     src/dsp/enc_mips32.c \ | ||||
|     src/dsp/enc_neon.$(NEON) \ | ||||
|     src/dsp/enc_sse2.c \ | ||||
|     src/dsp/filters_mips_dsp_r2.c \ | ||||
|     src/dsp/lossless.c \ | ||||
|     src/dsp/lossless_mips32.c \ | ||||
|     src/dsp/lossless_neon.$(NEON) \ | ||||
|   | ||||
| @@ -178,6 +178,7 @@ DSP_DEC_OBJS = \ | ||||
|     $(DIROBJ)\dsp\dec_mips32.obj \ | ||||
|     $(DIROBJ)\dsp\dec_neon.obj \ | ||||
|     $(DIROBJ)\dsp\dec_sse2.obj \ | ||||
|     $(DIROBJ)\dsp\filters_mips_dsp_r2.obj \ | ||||
|     $(DIROBJ)\dsp\lossless.obj \ | ||||
|     $(DIROBJ)\dsp\lossless_mips32.obj \ | ||||
|     $(DIROBJ)\dsp\lossless_neon.obj \ | ||||
|   | ||||
| @@ -114,6 +114,7 @@ DSP_DEC_OBJS = \ | ||||
|     src/dsp/dec_mips32.o \ | ||||
|     src/dsp/dec_neon.o \ | ||||
|     src/dsp/dec_sse2.o \ | ||||
|     src/dsp/filters_mips_dsp_r2.o \ | ||||
|     src/dsp/lossless.o \ | ||||
|     src/dsp/lossless_mips32.o \ | ||||
|     src/dsp/lossless_neon.o \ | ||||
|   | ||||
| @@ -16,6 +16,7 @@ COMMON_SOURCES += dec_clip_tables.c | ||||
| COMMON_SOURCES += dec_mips32.c | ||||
| COMMON_SOURCES += dec_neon.c | ||||
| COMMON_SOURCES += dsp.h | ||||
| COMMON_SOURCES += filters_mips_dsp_r2.c | ||||
| COMMON_SOURCES += lossless.c | ||||
| COMMON_SOURCES += lossless.h | ||||
| COMMON_SOURCES += lossless_mips32.c | ||||
|   | ||||
| @@ -290,9 +290,20 @@ void (*WebPApplyAlphaMultiply4444)(uint8_t*, int, int, int); | ||||
| //------------------------------------------------------------------------------ | ||||
| // Init function | ||||
|  | ||||
| extern void VP8FiltersInitMIPSdspR2(void); | ||||
|  | ||||
| void WebPInitAlphaProcessing(void) { | ||||
|   WebPMultARGBRow = MultARGBRow; | ||||
|   WebPMultRow = MultRow; | ||||
|   WebPApplyAlphaMultiply = ApplyAlphaMultiply; | ||||
|   WebPApplyAlphaMultiply4444 = ApplyAlphaMultiply_16b; | ||||
|  | ||||
|   // If defined, use CPUInfo() to overwrite some pointers with faster versions. | ||||
|   if (VP8GetCPUInfo != NULL) { | ||||
| #if defined(WEBP_USE_MIPS_DSP_R2) | ||||
|     if (VP8GetCPUInfo(kMIPSdspR2)) { | ||||
|       VP8FiltersInitMIPSdspR2(); | ||||
|     } | ||||
| #endif | ||||
|   } | ||||
| } | ||||
|   | ||||
							
								
								
									
										331
									
								
								src/dsp/filters_mips_dsp_r2.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										331
									
								
								src/dsp/filters_mips_dsp_r2.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,331 @@ | ||||
| // Copyright 2014 Google Inc. All Rights Reserved. | ||||
| // | ||||
| // Use of this source code is governed by a BSD-style license | ||||
| // that can be found in the COPYING 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. | ||||
| // ----------------------------------------------------------------------------- | ||||
| // | ||||
| // Spatial prediction using various filters | ||||
| // | ||||
| // Author(s): Branimir Vasic (branimir.vasic@imgtec.com) | ||||
| //            Djordje Pesut (djordje.pesut@imgtec.com) | ||||
|  | ||||
| #include "./dsp.h" | ||||
|  | ||||
| #if defined(WEBP_USE_MIPS_DSP_R2) | ||||
|  | ||||
| #include "../utils/filters.h" | ||||
| #include <assert.h> | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
|  | ||||
| //------------------------------------------------------------------------------ | ||||
| // Helpful macro. | ||||
|  | ||||
| # define SANITY_CHECK(in, out)                                                 \ | ||||
|   assert(in != NULL);                                                          \ | ||||
|   assert(out != NULL);                                                         \ | ||||
|   assert(width > 0);                                                           \ | ||||
|   assert(height > 0);                                                          \ | ||||
|   assert(stride >= width);                                                     \ | ||||
|   assert(row >= 0 && num_rows > 0 && row + num_rows <= height);                \ | ||||
|   (void)height;  // Silence unused warning. | ||||
|  | ||||
| #define DO_PREDICT_LINE(SRC, PRED, DST, LENGTH, INVERSE) do {                  \ | ||||
|     const uint8_t* psrc = (uint8_t*)(SRC);                                     \ | ||||
|     const uint8_t* ppred = (uint8_t*)(PRED);                                   \ | ||||
|     uint8_t* pdst = (uint8_t*)(DST);                                           \ | ||||
|     const int ilength = (int)(LENGTH);                                         \ | ||||
|     int temp0, temp1, temp2, temp3, temp4, temp5, temp6, temp7;                \ | ||||
|     __asm__ volatile (                                                         \ | ||||
|       ".set      push                                   \n\t"                  \ | ||||
|       ".set      noreorder                              \n\t"                  \ | ||||
|       "srl       %[temp0],    %[length],    0x3         \n\t"                  \ | ||||
|       "beqz      %[temp0],    2f                        \n\t"                  \ | ||||
|       " andi     %[temp7],    %[length],    0x7         \n\t"                  \ | ||||
|     "1:                                                 \n\t"                  \ | ||||
|       "ulw       %[temp1],    0(%[src])                 \n\t"                  \ | ||||
|       "ulw       %[temp2],    0(%[pred])                \n\t"                  \ | ||||
|       "ulw       %[temp3],    4(%[src])                 \n\t"                  \ | ||||
|       "ulw       %[temp4],    4(%[pred])                \n\t"                  \ | ||||
|       "addiu     %[src],      %[src],       8           \n\t"                  \ | ||||
|     ".if "#INVERSE"                                     \n\t"                  \ | ||||
|       "addu.qb   %[temp5],    %[temp1],     %[temp2]    \n\t"                  \ | ||||
|       "addu.qb   %[temp6],    %[temp3],     %[temp4]    \n\t"                  \ | ||||
|     ".else                                              \n\t"                  \ | ||||
|       "subu.qb   %[temp5],    %[temp1],     %[temp2]    \n\t"                  \ | ||||
|       "subu.qb   %[temp6],    %[temp3],     %[temp4]    \n\t"                  \ | ||||
|     ".endif                                             \n\t"                  \ | ||||
|       "addiu     %[pred],     %[pred],      8           \n\t"                  \ | ||||
|       "usw       %[temp5],    0(%[dst])                 \n\t"                  \ | ||||
|       "usw       %[temp6],    4(%[dst])                 \n\t"                  \ | ||||
|       "addiu     %[temp0],    %[temp0],     -1          \n\t"                  \ | ||||
|       "bnez      %[temp0],    1b                        \n\t"                  \ | ||||
|       " addiu    %[dst],      %[dst],       8           \n\t"                  \ | ||||
|       "beqz      %[temp7],    3f                        \n\t"                  \ | ||||
|       " nop                                             \n\t"                  \ | ||||
|     "2:                                                 \n\t"                  \ | ||||
|       "lbu       %[temp1],    0(%[src])                 \n\t"                  \ | ||||
|       "lbu       %[temp2],    0(%[pred])                \n\t"                  \ | ||||
|       "addiu     %[src],      %[src],       1           \n\t"                  \ | ||||
|       "addiu     %[pred],     %[pred],      1           \n\t"                  \ | ||||
|     ".if "#INVERSE"                                     \n\t"                  \ | ||||
|       "addu      %[temp3],    %[temp1],     %[temp2]    \n\t"                  \ | ||||
|     ".else                                              \n\t"                  \ | ||||
|       "subu      %[temp3],    %[temp1],     %[temp2]    \n\t"                  \ | ||||
|     ".endif                                             \n\t"                  \ | ||||
|       "sb        %[temp3],    0(%[dst])                 \n\t"                  \ | ||||
|       "addiu     %[temp7],    %[temp7],     -1          \n\t"                  \ | ||||
|       "bnez      %[temp7],    2b                        \n\t"                  \ | ||||
|       " addiu    %[dst],      %[dst],       1           \n\t"                  \ | ||||
|     "3:                                                 \n\t"                  \ | ||||
|       ".set      pop                                    \n\t"                  \ | ||||
|       : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2),         \ | ||||
|         [temp3]"=&r"(temp3), [temp4]"=&r"(temp4), [temp5]"=&r"(temp5),         \ | ||||
|         [temp6]"=&r"(temp6), [temp7]"=&r"(temp7), [pred]"+r"(ppred),           \ | ||||
|         [dst]"+r"(pdst), [src]"+r"(psrc)                                       \ | ||||
|       : [length]"r"(ilength)                                                   \ | ||||
|       : "memory"                                                               \ | ||||
|     );                                                                         \ | ||||
|   } while (0) | ||||
|  | ||||
| static WEBP_INLINE void PredictLine(const uint8_t* src, const uint8_t* pred, | ||||
|                                     uint8_t* dst, int length, int inverse) { | ||||
|   if (inverse) { | ||||
|     DO_PREDICT_LINE(src, pred, dst, length, 1); | ||||
|   } else { | ||||
|     DO_PREDICT_LINE(src, pred, dst, length, 0); | ||||
|   } | ||||
| } | ||||
|  | ||||
| #define PREDICT_LINE_ONE_PASS(SRC, PRED, DST, INVERSE) do {                    \ | ||||
|     int temp1, temp2, temp3;                                                   \ | ||||
|     __asm__ volatile (                                                         \ | ||||
|       "lbu       %[temp1],   0(%[src])               \n\t"                     \ | ||||
|       "lbu       %[temp2],   0(%[pred])              \n\t"                     \ | ||||
|     ".if "#INVERSE"                                  \n\t"                     \ | ||||
|       "addu      %[temp3],   %[temp1],   %[temp2]    \n\t"                     \ | ||||
|     ".else                                           \n\t"                     \ | ||||
|       "subu      %[temp3],   %[temp1],   %[temp2]    \n\t"                     \ | ||||
|     ".endif                                          \n\t"                     \ | ||||
|       "sb        %[temp3],   0(%[dst])               \n\t"                     \ | ||||
|       : [temp1]"=&r"(temp1), [temp2]"=&r"(temp2), [temp3]"=&r"(temp3)          \ | ||||
|       : [pred]"r"((PRED)), [dst]"r"((DST)), [src]"r"((SRC))                    \ | ||||
|       : "memory"                                                               \ | ||||
|     );                                                                         \ | ||||
|   } while (0) | ||||
|  | ||||
| //------------------------------------------------------------------------------ | ||||
| // Horizontal filter. | ||||
|  | ||||
| #define FILTER_LINE_BY_LINE(INVERSE) do {                                      \ | ||||
|     while (row < last_row) {                                                   \ | ||||
|       PREDICT_LINE_ONE_PASS(in, preds - stride, out, INVERSE);                 \ | ||||
|       DO_PREDICT_LINE(in + 1, preds, out + 1, width - 1, INVERSE);             \ | ||||
|       ++row;                                                                   \ | ||||
|       preds += stride;                                                         \ | ||||
|       in += stride;                                                            \ | ||||
|       out += stride;                                                           \ | ||||
|     }                                                                          \ | ||||
|   } while (0) | ||||
|  | ||||
| static WEBP_INLINE void DoHorizontalFilter(const uint8_t* in, | ||||
|                                            int width, int height, int stride, | ||||
|                                            int row, int num_rows, | ||||
|                                            int inverse, uint8_t* out) { | ||||
|   const uint8_t* preds; | ||||
|   const size_t start_offset = row * stride; | ||||
|   const int last_row = row + num_rows; | ||||
|   SANITY_CHECK(in, out); | ||||
|   in += start_offset; | ||||
|   out += start_offset; | ||||
|   preds = inverse ? out : in; | ||||
|  | ||||
|   if (row == 0) { | ||||
|     // Leftmost pixel is the same as input for topmost scanline. | ||||
|     out[0] = in[0]; | ||||
|     PredictLine(in + 1, preds, out + 1, width - 1, inverse); | ||||
|     row = 1; | ||||
|     preds += stride; | ||||
|     in += stride; | ||||
|     out += stride; | ||||
|   } | ||||
|  | ||||
|   // Filter line-by-line. | ||||
|   if (inverse) { | ||||
|     FILTER_LINE_BY_LINE(1); | ||||
|   } else { | ||||
|     FILTER_LINE_BY_LINE(0); | ||||
|   } | ||||
| } | ||||
|  | ||||
| #undef FILTER_LINE_BY_LINE | ||||
|  | ||||
| static void HorizontalFilter(const uint8_t* data, int width, int height, | ||||
|                              int stride, uint8_t* filtered_data) { | ||||
|   DoHorizontalFilter(data, width, height, stride, 0, height, 0, filtered_data); | ||||
| } | ||||
|  | ||||
| static void HorizontalUnfilter(int width, int height, int stride, int row, | ||||
|                                int num_rows, uint8_t* data) { | ||||
|   DoHorizontalFilter(data, width, height, stride, row, num_rows, 1, data); | ||||
| } | ||||
|  | ||||
| //------------------------------------------------------------------------------ | ||||
| // Vertical filter. | ||||
|  | ||||
| #define FILTER_LINE_BY_LINE(INVERSE) do {                                      \ | ||||
|     while (row < last_row) {                                                   \ | ||||
|       DO_PREDICT_LINE(in, preds, out, width, INVERSE);                         \ | ||||
|       ++row;                                                                   \ | ||||
|       preds += stride;                                                         \ | ||||
|       in += stride;                                                            \ | ||||
|       out += stride;                                                           \ | ||||
|     }                                                                          \ | ||||
|   } while (0) | ||||
|  | ||||
| static WEBP_INLINE void DoVerticalFilter(const uint8_t* in, | ||||
|                                          int width, int height, int stride, | ||||
|                                          int row, int num_rows, | ||||
|                                          int inverse, uint8_t* out) { | ||||
|   const uint8_t* preds; | ||||
|   const size_t start_offset = row * stride; | ||||
|   const int last_row = row + num_rows; | ||||
|   SANITY_CHECK(in, out); | ||||
|   in += start_offset; | ||||
|   out += start_offset; | ||||
|   preds = inverse ? out : in; | ||||
|  | ||||
|   if (row == 0) { | ||||
|     // Very first top-left pixel is copied. | ||||
|     out[0] = in[0]; | ||||
|     // Rest of top scan-line is left-predicted. | ||||
|     PredictLine(in + 1, preds, out + 1, width - 1, inverse); | ||||
|     row = 1; | ||||
|     in += stride; | ||||
|     out += stride; | ||||
|   } else { | ||||
|     // We are starting from in-between. Make sure 'preds' points to prev row. | ||||
|     preds -= stride; | ||||
|   } | ||||
|  | ||||
|   // Filter line-by-line. | ||||
|   if (inverse) { | ||||
|     FILTER_LINE_BY_LINE(1); | ||||
|   } else { | ||||
|     FILTER_LINE_BY_LINE(0); | ||||
|   } | ||||
| } | ||||
|  | ||||
| #undef FILTER_LINE_BY_LINE | ||||
|  | ||||
| static void VerticalFilter(const uint8_t* data, int width, int height, | ||||
|                            int stride, uint8_t* filtered_data) { | ||||
|   DoVerticalFilter(data, width, height, stride, 0, height, 0, filtered_data); | ||||
| } | ||||
|  | ||||
| static void VerticalUnfilter(int width, int height, int stride, int row, | ||||
|                              int num_rows, uint8_t* data) { | ||||
|   DoVerticalFilter(data, width, height, stride, row, num_rows, 1, data); | ||||
| } | ||||
|  | ||||
| //------------------------------------------------------------------------------ | ||||
| // Gradient filter. | ||||
|  | ||||
| static WEBP_INLINE int GradientPredictor(uint8_t a, uint8_t b, uint8_t c) { | ||||
|   int temp0; | ||||
|   __asm__ volatile ( | ||||
|     "addu             %[temp0],   %[a],       %[b]        \n\t" | ||||
|     "subu             %[temp0],   %[temp0],   %[c]        \n\t" | ||||
|     "shll_s.w         %[temp0],   %[temp0],   23          \n\t" | ||||
|     "precrqu_s.qb.ph  %[temp0],   %[temp0],   $zero       \n\t" | ||||
|     "srl              %[temp0],   %[temp0],   24          \n\t" | ||||
|     : [temp0]"=&r"(temp0) | ||||
|     : [a]"r"(a),[b]"r"(b),[c]"r"(c) | ||||
|   ); | ||||
|   return temp0; | ||||
| } | ||||
|  | ||||
| #define FILTER_LINE_BY_LINE(INVERSE, PREDS, OPERATION) do {                    \ | ||||
|     while (row < last_row) {                                                   \ | ||||
|       int w;                                                                   \ | ||||
|       PREDICT_LINE_ONE_PASS(in, PREDS - stride, out, INVERSE);                 \ | ||||
|       for (w = 1; w < width; ++w) {                                            \ | ||||
|         const int pred = GradientPredictor(PREDS[w - 1],                       \ | ||||
|                                            PREDS[w - stride],                  \ | ||||
|                                            PREDS[w - stride - 1]);             \ | ||||
|         out[w] = in[w] OPERATION pred;                                         \ | ||||
|       }                                                                        \ | ||||
|       ++row;                                                                   \ | ||||
|       in += stride;                                                            \ | ||||
|       out += stride;                                                           \ | ||||
|     }                                                                          \ | ||||
|   } while (0) | ||||
|  | ||||
| static WEBP_INLINE void DoGradientFilter(const uint8_t* in, | ||||
|                                          int width, int height, int stride, | ||||
|                                          int row, int num_rows, | ||||
|                                          int inverse, uint8_t* out) { | ||||
|   const uint8_t* preds; | ||||
|   const size_t start_offset = row * stride; | ||||
|   const int last_row = row + num_rows; | ||||
|   SANITY_CHECK(in, out); | ||||
|   in += start_offset; | ||||
|   out += start_offset; | ||||
|   preds = inverse ? out : in; | ||||
|  | ||||
|   // left prediction for top scan-line | ||||
|   if (row == 0) { | ||||
|     out[0] = in[0]; | ||||
|     PredictLine(in + 1, preds, out + 1, width - 1, inverse); | ||||
|     row = 1; | ||||
|     preds += stride; | ||||
|     in += stride; | ||||
|     out += stride; | ||||
|   } | ||||
|  | ||||
|   // Filter line-by-line. | ||||
|   if (inverse) { | ||||
|     FILTER_LINE_BY_LINE(1, out, +); | ||||
|   } else { | ||||
|     FILTER_LINE_BY_LINE(0, in, -); | ||||
|   } | ||||
| } | ||||
|  | ||||
| #undef FILTER_LINE_BY_LINE | ||||
|  | ||||
| static void GradientFilter(const uint8_t* data, int width, int height, | ||||
|                            int stride, uint8_t* filtered_data) { | ||||
|   DoGradientFilter(data, width, height, stride, 0, height, 0, filtered_data); | ||||
| } | ||||
|  | ||||
| static void GradientUnfilter(int width, int height, int stride, int row, | ||||
|                              int num_rows, uint8_t* data) { | ||||
|   DoGradientFilter(data, width, height, stride, row, num_rows, 1, data); | ||||
| } | ||||
|  | ||||
| #undef PREDICT_LINE_ONE_PASS | ||||
| #undef DO_PREDICT_LINE | ||||
| #undef SANITY_CHECK | ||||
|  | ||||
| #endif  // WEBP_USE_MIPS_DSP_R2 | ||||
|  | ||||
| //------------------------------------------------------------------------------ | ||||
|  | ||||
| extern void VP8FiltersInitMIPSdspR2(void); | ||||
|  | ||||
| void VP8FiltersInitMIPSdspR2(void) { | ||||
| #if defined(WEBP_USE_MIPS_DSP_R2) | ||||
|   WebPFilters[WEBP_FILTER_HORIZONTAL] = HorizontalFilter; | ||||
|   WebPFilters[WEBP_FILTER_VERTICAL] = VerticalFilter; | ||||
|   WebPFilters[WEBP_FILTER_GRADIENT] = GradientFilter; | ||||
|  | ||||
|   WebPUnfilters[WEBP_FILTER_HORIZONTAL] = HorizontalUnfilter; | ||||
|   WebPUnfilters[WEBP_FILTER_VERTICAL] = VerticalUnfilter; | ||||
|   WebPUnfilters[WEBP_FILTER_GRADIENT] = GradientUnfilter; | ||||
| #endif  // WEBP_USE_MIPS_DSP_R2 | ||||
| } | ||||
|  | ||||
| //------------------------------------------------------------------------------ | ||||
| @@ -248,14 +248,14 @@ WEBP_FILTER_TYPE EstimateBestFilter(const uint8_t* data, | ||||
|  | ||||
| //------------------------------------------------------------------------------ | ||||
|  | ||||
| const WebPFilterFunc WebPFilters[WEBP_FILTER_LAST] = { | ||||
| WebPFilterFunc WebPFilters[WEBP_FILTER_LAST] = { | ||||
|   NULL,              // WEBP_FILTER_NONE | ||||
|   HorizontalFilter,  // WEBP_FILTER_HORIZONTAL | ||||
|   VerticalFilter,    // WEBP_FILTER_VERTICAL | ||||
|   GradientFilter     // WEBP_FILTER_GRADIENT | ||||
| }; | ||||
|  | ||||
| const WebPUnfilterFunc WebPUnfilters[WEBP_FILTER_LAST] = { | ||||
| WebPUnfilterFunc WebPUnfilters[WEBP_FILTER_LAST] = { | ||||
|   NULL,                // WEBP_FILTER_NONE | ||||
|   HorizontalUnfilter,  // WEBP_FILTER_HORIZONTAL | ||||
|   VerticalUnfilter,    // WEBP_FILTER_VERTICAL | ||||
|   | ||||
| @@ -41,12 +41,12 @@ typedef void (*WebPUnfilterFunc)(int width, int height, int stride, | ||||
| // in raster order. | ||||
| // 'stride' is number of bytes per scan line (with possible padding). | ||||
| // 'out' should be pre-allocated. | ||||
| extern const WebPFilterFunc WebPFilters[WEBP_FILTER_LAST]; | ||||
| extern WebPFilterFunc WebPFilters[WEBP_FILTER_LAST]; | ||||
|  | ||||
| // In-place reconstruct the original data from the given filtered data. | ||||
| // The reconstruction will be done for 'num_rows' rows starting from 'row' | ||||
| // (assuming rows upto 'row - 1' are already reconstructed). | ||||
| extern const WebPUnfilterFunc WebPUnfilters[WEBP_FILTER_LAST]; | ||||
| extern WebPUnfilterFunc WebPUnfilters[WEBP_FILTER_LAST]; | ||||
|  | ||||
| // Fast estimate of a potentially good filter. | ||||
| WEBP_FILTER_TYPE EstimateBestFilter(const uint8_t* data, | ||||
|   | ||||
		Reference in New Issue
	
	Block a user