arm_jit_swscale.c
上传用户:kjfoods
上传日期:2020-07-06
资源大小:29949k
文件大小:15k
源码类别:

midi

开发平台:

Unix_Linux

  1. /*
  2.  * Fast JIT powered scaler for ARM
  3.  *
  4.  * Copyright (C) 2007 Siarhei Siamashka <ssvb@users.sourceforge.net>
  5.  *
  6.  * This library is free software; you can redistribute it and/or
  7.  * modify it under the terms of the GNU Lesser General Public License
  8.  * version 2.1 as published by the Free Software Foundation.
  9.  *
  10.  * This library is distributed in the hope that it will be useful, but
  11.  * WITHOUT ANY WARRANTY; without even the implied warranty of
  12.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  13.  * Lesser General Public License for more details.
  14.  *
  15.  * You should have received a copy of the GNU Lesser General Public
  16.  * License along with this library; if not, write to the Free Software
  17.  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
  18.  * 02110-1301 USA
  19.  */
  20. #include <stdio.h>
  21. #include <math.h>
  22. #include <stdint.h>
  23. #include <stdlib.h>
  24. #include <sys/mman.h>
  25. #include <string.h>
  26. #include "arm_jit_swscale.h"
  27. #include "arm_colorconv.h"
  28. /* Size of cpu instructions cache, we should never exceed it in generated code */
  29. #define INSTRUCTIONS_CACHE_SIZE 32768
  30. /* Supported output formats */
  31. #define FMT_OMAPFB_YUV422 1
  32. #define FMT_OMAPFB_YUV420 2
  33. extern void __clear_cache (char *beg, char *end);
  34. /*
  35.  * API is similar to API from ffmpeg libswscale
  36.  */
  37. typedef struct SwsContextArmJit {
  38.     int fmt;
  39.     int source_w;
  40.     int source_h;
  41.     int target_w;
  42.     int target_h;
  43.     uint32_t *codebuffer;
  44.     int *linebuffer;
  45.     int armv6_is_supported;
  46. } SwsContextArmJit;
  47. //#define JIT_DEBUG
  48. #define INTERPOLATE_COPY_FIRST   0
  49. #define INTERPOLATE_AVERAGE_1_3  1
  50. #define INTERPOLATE_AVERAGE_2_2  2
  51. #define INTERPOLATE_AVERAGE_3_1  3
  52. /**
  53.  * Get two nearest pixels from the source image
  54.  *
  55.  * @todo get rid of the floating point math
  56.  */
  57. static inline int get_pix(int quality, int orig_w, int dest_w, int x, int *p1, int *p2)
  58. {
  59.     double offs = ((double)x + 0.5) / (double)dest_w * (double)orig_w;
  60.     double dist;
  61.     int pix1 = floor(offs - 0.5);
  62.     int pix2 = ceil(offs - 0.5);
  63.     // Special boundary cases
  64.     if (pix1 < 0) {
  65.         *p1 = *p2 = 0;
  66.         return INTERPOLATE_COPY_FIRST;
  67.     }
  68.     if (pix2 >= orig_w) {
  69.         *p1 = *p2 = orig_w - 1;
  70.         return INTERPOLATE_COPY_FIRST;
  71.     }
  72.     dist = offs - ((double)pix1 + 0.5);
  73. #if 0
  74.     if (quality >= 3) {
  75.         if (dist > 0.125 && dist < 0.375) {
  76.             *p1 = pix1;
  77.             *p2 = pix2;
  78.             return INTERPOLATE_AVERAGE_3_1;
  79.         }
  80.         if (dist > 0.625 && dist < 0.875) {
  81.             *p1 = pix1;
  82.             *p2 = pix2;
  83.             return INTERPOLATE_AVERAGE_1_3;
  84.         }
  85.     }
  86. #endif
  87.     if (quality >= 2) {
  88.         if (dist > 0.25 && dist < 0.75) {
  89.             *p1 = pix1;
  90.             *p2 = pix2;
  91.             return INTERPOLATE_AVERAGE_2_2;
  92.         }
  93.     }
  94.     if (dist < 0.5) {
  95.         *p1 = *p2 = pix1;
  96.         return INTERPOLATE_COPY_FIRST;
  97.     } else {
  98.         *p1 = *p2 = pix2;
  99.         return INTERPOLATE_COPY_FIRST;
  100.     }
  101. }
  102. static uint32_t *generate_arm_cmd_ldrb_r_r_offs(uint32_t *cmdbuffer, int dstreg, int basereg, int offset)
  103. {
  104. #ifdef JIT_DEBUG
  105.     printf("ldrb     r%d, [r%d, #%d]n", dstreg, basereg, offset);
  106. #endif
  107.     *cmdbuffer++ = 0xE5D00000 | (basereg << 16) | (dstreg << 12) | (offset);
  108.     return cmdbuffer;
  109. }
  110. static uint32_t *generate_arm_cmd_add_r_r_r_lsl(uint32_t *cmdbuffer, int dstreg, int r1, int r2, int r2_shift)
  111. {
  112. #ifdef JIT_DEBUG
  113.     printf("add      r%d, r%d, r%d, lsl #%dn", dstreg, r1, r2, r2_shift);
  114. #endif
  115.     *cmdbuffer++ = 0xE0800000 | (r1 << 16) | (dstreg << 12) | (r2_shift << 7) | (r2);
  116.     return cmdbuffer;
  117. }
  118. static uint32_t *generate_arm_cmd_mov_r_r_lsr(uint32_t *cmdbuffer, int dstreg, int r, int shift)
  119. {
  120. #ifdef JIT_DEBUG
  121.     printf("mov      r%d, r%d, lsr #%dn", dstreg, r, shift);
  122. #endif
  123.     *cmdbuffer++ = 0xE1A00020 | (dstreg << 12) | (shift << 7) | (r);
  124.     return cmdbuffer;
  125. }
  126. /**
  127.  * Generation of 32-bit output scaled data
  128.  * @param quality - scaling quality level
  129.  * @param buf1reg - register that holds a pointer to the buffer with data for the first output byte
  130.  * @param buf2reg - register that holds a pointer to the buffer with data for the second output byte
  131.  * @param buf3reg - register that holds a pointer to the buffer with data for the third output byte
  132.  * @param buf4reg - register that holds a pointer to the buffer with data for the fourth output byte
  133.  */
  134. static uint32_t *generate_32bit_scaled_data_write(
  135.     uint32_t *p,
  136.     int quality, int orig_w, int dest_w,
  137.     int buf1reg, int size1, int offs1,
  138.     int buf2reg, int size2, int offs2,
  139.     int buf3reg, int size3, int offs3,
  140.     int buf4reg, int size4, int offs4)
  141. {
  142.     int p1, p2;
  143.     int type_y1, type_y2, type_u, type_v;
  144.     // First stage: perform data loading
  145.     type_y1 = get_pix(quality, orig_w / size1, dest_w / size1, offs1 / size1, &p1, &p2);
  146.     if (type_y1 == INTERPOLATE_COPY_FIRST) {
  147.         // Special case, no interpolation is needed, so load this data
  148.         // directly into destination register
  149.         p = generate_arm_cmd_ldrb_r_r_offs(p, 4, buf1reg, p1);
  150.     } else {
  151.         p = generate_arm_cmd_ldrb_r_r_offs(p, 5, buf1reg, p1);
  152.         p = generate_arm_cmd_ldrb_r_r_offs(p, 6, buf1reg, p2);
  153.     }
  154.     // u
  155.     type_u = get_pix(quality, orig_w / size2, dest_w / size2, offs2 / size2, &p1, &p2);
  156.     p = generate_arm_cmd_ldrb_r_r_offs(p, 7, buf2reg, p1);
  157.     if (type_u != INTERPOLATE_COPY_FIRST) p = generate_arm_cmd_ldrb_r_r_offs(p, 8, buf2reg, p2);
  158.     // y2
  159.     type_y2 = get_pix(quality, orig_w / size3, dest_w / size3, offs3 / size3, &p1, &p2);
  160.     p = generate_arm_cmd_ldrb_r_r_offs(p, 9, buf3reg, p1);
  161.     if (type_y2 != INTERPOLATE_COPY_FIRST) p = generate_arm_cmd_ldrb_r_r_offs(p, 10, buf3reg, p2);
  162.     // v
  163.     type_v = get_pix(quality, orig_w / size4, dest_w / size4, offs4 / size4, &p1, &p2);
  164.     p = generate_arm_cmd_ldrb_r_r_offs(p, 11, buf4reg, p1);
  165.     if (type_v != INTERPOLATE_COPY_FIRST) p = generate_arm_cmd_ldrb_r_r_offs(p, 12, buf4reg, p2);
  166.     // Second stage: perform data shuffling
  167.     if (type_y1 == INTERPOLATE_AVERAGE_2_2) {
  168.         p = generate_arm_cmd_add_r_r_r_lsl(p, 14, 5, 6, 0);
  169.         p = generate_arm_cmd_mov_r_r_lsr(p, 4, 14, 1);
  170.     }
  171.     if (type_u == INTERPOLATE_COPY_FIRST) {
  172.         p = generate_arm_cmd_add_r_r_r_lsl(p, 4, 4, 7, 8);
  173.     } else if (type_u == INTERPOLATE_AVERAGE_2_2) {
  174.         p = generate_arm_cmd_add_r_r_r_lsl(p, 14, 7, 8, 0);
  175.         p = generate_arm_cmd_mov_r_r_lsr(p, 14, 14, 1);
  176.         p = generate_arm_cmd_add_r_r_r_lsl(p, 4, 4, 14, 8);
  177.     }
  178.     if (type_y2 == INTERPOLATE_COPY_FIRST) {
  179.         p = generate_arm_cmd_add_r_r_r_lsl(p, 4, 4, 9, 16);
  180.     } else if (type_y2 == INTERPOLATE_AVERAGE_2_2) {
  181.         p = generate_arm_cmd_add_r_r_r_lsl(p, 14, 9, 10, 0);
  182.         p = generate_arm_cmd_mov_r_r_lsr(p, 14, 14, 1);
  183.         p = generate_arm_cmd_add_r_r_r_lsl(p, 4, 4, 14, 16);
  184.     }
  185.     if (type_v == INTERPOLATE_COPY_FIRST) {
  186.         p = generate_arm_cmd_add_r_r_r_lsl(p, 4, 4, 11, 24);
  187.     } else if (type_v == INTERPOLATE_AVERAGE_2_2) {
  188.         p = generate_arm_cmd_add_r_r_r_lsl(p, 14, 11, 12, 0);
  189.         p = generate_arm_cmd_mov_r_r_lsr(p, 14, 14, 1);
  190.         p = generate_arm_cmd_add_r_r_r_lsl(p, 4, 4, 14, 24);
  191.     }
  192.     // Third stage: store data and advance output buffer pointer
  193.     *p++ = 0xE4834004; // str r4, [r3], #4
  194.     return p;
  195. }
  196. /**
  197.  * Scaler code should assume:
  198.  * r0 - y plane
  199.  * r1 - u plane
  200.  * r2 - v plane
  201.  * r3 - destination buffer
  202.  * r4 - result for storage into output buffer
  203.  * r5, r6 - source data for y1 calculation
  204.  * r7, r8 - source data for u calculation
  205.  * r9, r10 - source data for y2 calculation
  206.  * r11, r12 - source data for v calculation
  207.  * r14 (lr) - accumulator
  208.  *
  209.  * @param cmdbuffer - bugger for dynamically generated code
  210.  * @return - number of instructions generated
  211.  */
  212. static int generate_yuv420p_to_yuyv422_line_scaler(uint32_t *cmdbuffer, int maxcmdcount, int orig_w, int dest_w, int quality)
  213. {
  214.     int i, p1, p2, cmdcount;
  215.     int type_y1, type_y2, type_u, type_v;
  216.     uint32_t *p = cmdbuffer;
  217.     *p++ = 0xE92D4FF0; // stmfd  sp!, {r4-r11, lr} @ save all registers
  218.     // Process a pair of destination pixels per loop iteration (it should result in 32-bit value write)
  219.     for (i = 0; i < dest_w; i += 2) {
  220.         p = generate_32bit_scaled_data_write(
  221.             p, quality, orig_w, dest_w,
  222.             0, 1, i + 0,
  223.             1, 2, i,
  224.             0, 1, i + 1,
  225.             2, 2, i);
  226.     }
  227.     *p++ = 0xE8BD8FF0; // ldmfd  sp!, {r4-r11, pc} @ restore all registers and return
  228.     cmdcount = p - cmdbuffer;
  229. #ifdef JIT_DEBUG
  230.     printf("@ number of instructions = %dn", cmdcount);
  231.     FILE *f = fopen("cmdbuf.bin", "w+");
  232.     fwrite(cmdbuffer, 1, INSTRUCTIONS_CACHE_SIZE, f);
  233.     fclose(f);
  234. #endif
  235.     return cmdcount;
  236. }
  237. static int generate_yuv420p_to_yuv420_line_scaler(uint32_t *cmdbuffer, int maxcmdcount, int orig_w, int dest_w, int quality)
  238. {
  239.     int i = 0, p1, p2, cmdcount;
  240.     int type_y1, type_y2, type_u, type_v;
  241.     uint32_t *p = cmdbuffer;
  242.     #define SRC_Y 0
  243.     #define SRC_U 1
  244.     *p++ = 0xE92D4FF0; // stmfd  sp!, {r4-r11, lr} @ save all registers
  245.     while (i + 8 <= dest_w) {
  246.         p = generate_32bit_scaled_data_write(
  247.             p, quality, orig_w, dest_w,
  248.             SRC_Y, 1, i + 0 * 1,
  249.             SRC_U, 2, i + 0 * 2,
  250.             SRC_U, 2, i + 1 * 2,
  251.             SRC_Y, 1, i + 1 * 1);
  252.         p = generate_32bit_scaled_data_write(
  253.             p, quality, orig_w, dest_w,
  254.             SRC_Y, 1, i + 3 * 1,
  255.             SRC_Y, 1, i + 2 * 1,
  256.             SRC_Y, 1, i + 4 * 1,
  257.             SRC_U, 2, i + 2 * 2);
  258.         p = generate_32bit_scaled_data_write(
  259.             p, quality, orig_w, dest_w,
  260.             SRC_U, 2, i + 3 * 2,
  261.             SRC_Y, 1, i + 5 * 1,
  262.             SRC_Y, 1, i + 7 * 1,
  263.             SRC_Y, 1, i + 6 * 1);
  264.         i += 8;
  265.     }
  266.     *p++ = 0xE8BD8FF0; // ldmfd  sp!, {r4-r11, pc} @ restore all registers and return
  267.     cmdcount = p - cmdbuffer;
  268. #ifdef JIT_DEBUG
  269.     printf("@ number of instructions = %dn", cmdcount);
  270.     FILE *f = fopen("cmdbuf.bin", "w+");
  271.     fwrite(cmdbuffer, 1, INSTRUCTIONS_CACHE_SIZE, f);
  272.     fclose(f);
  273. #endif
  274.     return cmdcount;
  275. }
  276. /******************************************************************************/
  277. static struct SwsContextArmJit *sws_arm_jit_create_scaler_internal(int source_w, int source_h, int target_w, int target_h, int quality, int fmt)
  278. {
  279.     int i, p1, p2;
  280.     uint32_t *p = mmap(0, INSTRUCTIONS_CACHE_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
  281.     if (fmt == FMT_OMAPFB_YUV422) {
  282.         generate_yuv420p_to_yuyv422_line_scaler(p, INSTRUCTIONS_CACHE_SIZE / 4, source_w, target_w, quality);
  283.     } else if (fmt == FMT_OMAPFB_YUV420) {
  284.         generate_yuv420p_to_yuv420_line_scaler(p, INSTRUCTIONS_CACHE_SIZE / 4, source_w, target_w, quality);
  285.     } else {
  286.         return NULL;
  287.     }
  288.     
  289.     int *linebuffer = (int *)malloc(target_h * sizeof(int));
  290.     for (i = 0; i < target_h; i ++) {
  291.         get_pix(1, source_h, target_h, i, &p1, &p2);
  292.         linebuffer[i] = p1;
  293.     }
  294.     __clear_cache((char *)p, (char *)p + INSTRUCTIONS_CACHE_SIZE);
  295.     SwsContextArmJit *context = (SwsContextArmJit *)malloc(sizeof(SwsContextArmJit));
  296.     memset(context, 0, sizeof(SwsContextArmJit));
  297.     context->source_w = source_w;
  298.     context->source_h = source_h;
  299.     context->target_w = target_w;
  300.     context->target_h = target_h;
  301.     context->codebuffer = p;
  302.     context->linebuffer = linebuffer;
  303.     context->fmt = fmt;
  304.     context->armv6_is_supported = 0;
  305.     return context;
  306. }
  307. struct SwsContextArmJit *sws_arm_jit_create_omapfb_yuv422_scaler(int source_w, int source_h, int target_w, int target_h, int quality)
  308. {
  309.     return sws_arm_jit_create_scaler_internal(source_w, source_h, target_w, target_h, quality, FMT_OMAPFB_YUV422);
  310. }
  311. struct SwsContextArmJit *sws_arm_jit_create_omapfb_yuv420_scaler(int source_w, int source_h, int target_w, int target_h, int quality)
  312. {
  313.     return sws_arm_jit_create_scaler_internal(source_w, source_h, target_w, target_h, quality, FMT_OMAPFB_YUV420);
  314. }
  315. struct SwsContextArmJit *sws_arm_jit_create_omapfb_yuv420_scaler_armv6(int source_w, int source_h, int target_w, int target_h, int quality)
  316. {
  317.     struct SwsContextArmJit *s = sws_arm_jit_create_scaler_internal(source_w, source_h, target_w, target_h, quality, FMT_OMAPFB_YUV420);
  318.     if (s) s->armv6_is_supported = 1;
  319.     return s;
  320. }
  321. void sws_arm_jit_free(SwsContextArmJit *context)
  322. {
  323.     if (!context) return;
  324.     munmap(context->codebuffer, INSTRUCTIONS_CACHE_SIZE);
  325.     free(context->linebuffer);
  326.     free(context);
  327. }
  328. static int sws_arm_jit_vscaleonly_internal(SwsContextArmJit *context, uint8_t* src[], int srcStride[], uint8_t* dst[], int dstStride[])
  329. {
  330.     int i, j;
  331.     if (context->fmt == FMT_OMAPFB_YUV420) {
  332.         void (*yv12_to_yuv420_line)(uint16_t *dst, const uint16_t *src_y, const uint8_t *src_c, int w) =
  333.             yv12_to_yuv420_line_arm;
  334.         if (context->armv6_is_supported) yv12_to_yuv420_line = yv12_to_yuv420_line_armv6;
  335.         for (i = 0; i < context->target_h; i++) {
  336.             j = context->linebuffer[i];
  337.             if (i & 1) {
  338.                 yv12_to_yuv420_line((uint16_t *)(dst[0] + i * dstStride[0]),
  339.                     src[0] + j * srcStride[0], src[2] + (j / 2) * srcStride[2], context->target_w);
  340.             } else {
  341.                 yv12_to_yuv420_line((uint16_t *)(dst[0] + i * dstStride[0]),
  342.                     src[0] + j * srcStride[0], src[1] + (j / 2) * srcStride[1], context->target_w);
  343.             }
  344.         }
  345.         return 1;
  346.     } else if (context->fmt == FMT_OMAPFB_YUV422) {
  347.         void (*yv12_to_yuy2_line)(uint16_t *dst, const uint16_t *src_y, const uint8_t *src_u, const uint8_t *src_v, int w) =
  348.             yv12_to_yuy2_line_arm;
  349.         for (i = 0; i < context->target_h; i++) {
  350.             j = context->linebuffer[i];
  351.             yv12_to_yuy2_line(
  352.                 dst[0] + i * dstStride[0],
  353.                 src[0] + j * srcStride[0],
  354.                 src[1] + (j / 2) * srcStride[1],
  355.                 src[2] + (j / 2) * srcStride[2],
  356.                 context->target_w);
  357.         }
  358.         return 1;
  359.     }
  360.     return 0;
  361. }
  362. static int sws_arm_jit_scale_internal(SwsContextArmJit *context, uint8_t* src[], int srcStride[], uint8_t* dst[], int dstStride[])
  363. {
  364.     int i, j;
  365.     void (*scale_line)(uint8_t *y, uint8_t *u, uint8_t *v, uint8_t *out) =
  366.         (void (*)(uint8_t *, uint8_t *, uint8_t *, uint8_t *))context->codebuffer;
  367.     if (context->source_w == context->target_w)
  368.         return sws_arm_jit_vscaleonly_internal(context, src, srcStride, dst, dstStride);
  369.     if (context->fmt == FMT_OMAPFB_YUV422) {
  370.         for (i = 0; i < context->target_h; i++) {
  371.             j = context->linebuffer[i];
  372.             scale_line(
  373.                 src[0] + j * srcStride[0],
  374.                 src[1] + (j / 2) * srcStride[1],
  375.                 src[2] + (j / 2) * srcStride[2],
  376.                 dst[0] + i * dstStride[0]);
  377.         }
  378.         return 1;
  379.     } else if (context->fmt == FMT_OMAPFB_YUV420) {
  380.         for (i = 0; i < context->target_h; i++) {
  381.             j = context->linebuffer[i];
  382.             scale_line(
  383.                 src[0] + j * srcStride[0],
  384.                 (i & 1) ? (src[2] + (j / 2) * srcStride[2]) : (src[1] + (j / 2) * srcStride[1]),
  385.                 0,
  386.                 dst[0] + i * dstStride[0]);
  387.         }
  388.         return 1;
  389.     }
  390.     return 0;
  391. }
  392. int sws_arm_jit_scale(SwsContextArmJit *context, uint8_t* src[], int srcStride[], int y, int h, uint8_t* dst[], int dstStride[])
  393. {
  394.     if (y != 0 || h != context->source_h) return 0; // Slices are not supported yet
  395.     return sws_arm_jit_scale_internal(context, src, srcStride, dst, dstStride);
  396. }