[Pixman] [PATCH 26/37] armv6: Add fetcher for a8r8g8b8 nearest-neighbour transformed images

Ben Avison bavison at riscosopen.org
Sun Sep 14 13:10:12 PDT 2014


This is constrained to support X increments in the positive X direction only,
so this means scaled images (except those reflected in the Y axis) plus
parallelogram transformations which preserve the direction of the X axis.
It also doesn't attempt to support any form of image repeat.

With this optimisation, some operations constructed from fetcher and combiner
calls using general_composite_rect() now outperform the versions consructed
from FAST_NEAREST macros in pixman-fast-path.c, but unfortunately the
FAST_NEAREST ones have higher priority in fast path lookup. Here are some
benchmarks for the in_reverse_8888_8888 operation, which is not affected:

lowlevel-blt-bench -n :

     Before          After
     Mean   StdDev   Mean   StdDev  Confidence  Change
L1   10.2   0.0      27.1   0.2     100.0%      +164.8%
L2   8.2    0.1      23.0   0.4     100.0%      +179.2%
M    8.3    0.0      24.8   0.0     100.0%      +200.3%
HT   5.5    0.0      12.7   0.0     100.0%      +129.9%
VT   5.4    0.0      12.1   0.0     100.0%      +123.2%
R    5.4    0.0      11.9   0.1     100.0%      +122.7%
RT   2.8    0.0      5.4    0.1     100.0%      +91.9%

affine-bench for 5 different scaling factors:

     Before          After
     Mean   StdDev   Mean   StdDev  Confidence  Change
0.5  11.1   0.0      28.3   0.0     100.0%      +155.1%
0.75 10.5   0.0      26.4   0.0     100.0%      +152.2%
1.0  9.9    0.0      24.6   0.0     100.0%      +147.5%
1.5  9.0    0.0      21.8   0.0     100.0%      +141.4%
2.0  8.3    0.0      19.7   0.0     100.0%      +138.4%
---
 pixman/pixman-arm-common.h          |   14 ++
 pixman/pixman-arm-simd-asm-scaled.S |    9 +
 pixman/pixman-arm-simd-asm-scaled.h |  367 +++++++++++++++++++++++++++++++++++
 pixman/pixman-arm-simd.c            |   70 +++++++
 4 files changed, 460 insertions(+), 0 deletions(-)
 create mode 100644 pixman/pixman-arm-simd-asm-scaled.h

diff --git a/pixman/pixman-arm-common.h b/pixman/pixman-arm-common.h
index f4632b2..a4d4ea4 100644
--- a/pixman/pixman-arm-common.h
+++ b/pixman/pixman-arm-common.h
@@ -455,6 +455,20 @@ cputype##_combine_##name##_u (pixman_implementation_t *imp,                   \
 
 /*****************************************************************************/
 
+#define PIXMAN_ARM_NEAREST_AFFINE_FLAGS                                 \
+    (FAST_PATH_NO_ALPHA_MAP             |                               \
+     FAST_PATH_NO_ACCESSORS             |                               \
+     FAST_PATH_NARROW_FORMAT            |                               \
+     FAST_PATH_NEAREST_FILTER           |                               \
+     FAST_PATH_HAS_TRANSFORM            |                               \
+     FAST_PATH_AFFINE_TRANSFORM)
+
+#define PIXMAN_ARM_NEAREST_SCALED_COVER_FLAGS                           \
+    (PIXMAN_ARM_NEAREST_AFFINE_FLAGS      |                             \
+     FAST_PATH_SAMPLES_COVER_CLIP_NEAREST |                             \
+     FAST_PATH_X_UNIT_POSITIVE            |                             \
+     FAST_PATH_Y_UNIT_ZERO)
+
 #define PIXMAN_ARM_BIND_GET_SCANLINE(cputype, name)                         \
 void                                                                        \
 pixman_get_scanline_##name##_asm_##cputype (int32_t        w,               \
diff --git a/pixman/pixman-arm-simd-asm-scaled.S b/pixman/pixman-arm-simd-asm-scaled.S
index e050292..2c7e091 100644
--- a/pixman/pixman-arm-simd-asm-scaled.S
+++ b/pixman/pixman-arm-simd-asm-scaled.S
@@ -38,6 +38,7 @@
 	.p2align 2
 
 #include "pixman-arm-asm.h"
+#include "pixman-arm-simd-asm-scaled.h"
 
 /*
  * Note: This code is only using armv5te instructions (not even armv6),
@@ -154,3 +155,11 @@ generate_nearest_scanline_func \
 
 generate_nearest_scanline_func \
     pixman_scaled_nearest_scanline_8888_8888_SRC_asm_armv6, 2,  , 48, 32
+
+/******************************************************************************/
+
+generate_nearest_scaled_cover_function \
+    pixman_get_scanline_nearest_scaled_cover_a8r8g8b8_asm_armv6, 32, \
+    3, 3 /* prefetch distances */, nop_macro, nop_macro
+
+/******************************************************************************/
diff --git a/pixman/pixman-arm-simd-asm-scaled.h b/pixman/pixman-arm-simd-asm-scaled.h
new file mode 100644
index 0000000..fb6eb44
--- /dev/null
+++ b/pixman/pixman-arm-simd-asm-scaled.h
@@ -0,0 +1,367 @@
+/*
+ * Copyright © 2014 RISC OS Open Ltd
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of the copyright holders not be used in
+ * advertising or publicity pertaining to distribution of the software without
+ * specific, written prior permission.  The copyright holders make no
+ * representations about the suitability of this software for any purpose.  It
+ * is provided "as is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ *
+ * Author:  Ben Avison (bavison at riscosopen.org)
+ */
+
+.set log2_32, 5
+.set log2_16, 4
+.set log2_8,  3
+.set log2_4,  2
+.set log2_2,  1
+.set log2_1,  0
+
+.macro ldrx  bpp, cond, tail
+ .if \bpp == 32
+        ldr\cond \tail
+ .elseif \bpp == 16
+        ldr\cond\()h \tail
+ .elseif \bpp == 8
+        ldr\cond\()b \tail
+ .else
+        .error  "Input bits per pixel not supported"
+ .endif
+.endm
+
+.macro branch  cond, label1, label2
+ .ifnc "\label1", ""
+        b\cond  \label1
+ .else
+        b\cond  \label2
+ .endif
+.endm
+
+.macro nearest_scaled_cover_enlarge_mask_innerloop  bpp, reg, convert, mask_hint, may_be_final, exit_label, store
+ .ifnc \mask_hint, mask_is_0
+        teq     VALID, #1
+  .ifc \convert, nop_macro
+   .ifnc \mask_hint, mask_is_non_0
+        ittt    ne
+        teqne   \reg, #0
+   .else
+        itt     ne
+   .endif
+        ldrx    \bpp, ne, <PIXEL, [SRC]>
+        movne   VALID, #1
+  .else
+   .ifnc \mask_hint, mask_is_non_0
+        it      ne
+        teqne   \reg, #0
+   .endif
+        beq     1101f
+        ldrx    \bpp,, <PIXEL, [SRC]>
+        mov     VALID, #1
+        \convert PIXEL, TMP
+1101:
+  .endif
+ .endif
+        adds    ACCUM, ACCUM, UX
+ .ifnc \mask_hint, mask_is_0
+        mov     \reg, PIXEL
+ .endif
+        \store
+        branch  cc, \exit_label, 1103f
+        add     SRC, SRC, #\bpp/8
+        mov     VALID, #0
+        tst     SRC, #31
+        branch  ne, \exit_label, 1103f
+        subs    PLDS, PLDS, #32
+        branch  lt, \exit_label, 1103f
+        pld     [SRC, #prefetch_distance_src*32]
+1103:
+.endm
+
+.macro nearest_scaled_cover_enlarge_nomask_innerloop  bpp, reg, convert, mask_hint, may_be_final, exit_label, store
+        adds    ACCUM, ACCUM, UX
+        mov     \reg, PIXEL
+        \store
+        branch  cc, \exit_label, 1203f
+ .ifnc "\may_be_final",""
+        teq     COUNT, #0
+        ldrx    \bpp, ne, <PIXEL, [SRC, #\bpp/8]!!>
+ .else
+        ldrx    \bpp,, <PIXEL, [SRC, #\bpp/8]!!>
+ .endif
+        tst     SRC, #31
+        \convert PIXEL, TMP
+        branch  ne, \exit_label, 1203f
+        subs    PLDS, PLDS, #32
+        branch  lt, \exit_label, 1203f
+        pld     [SRC, #prefetch_distance_src*32]
+1203:
+.endm
+
+.macro nearest_scaled_cover_reduce_mask_innerloop  bpp, reg, convert, mask_hint, may_be_final, exit_label, store
+        add     PTR, SRC, XHI, lsl #log2_\bpp - 3
+        mov     TMP, XHI
+        adds    XLO, XLO, UX, lsl #16
+        adc     XHI, XHI, UX, lsr #16
+ .ifc "\mask_hint",""
+        teq     \reg, #0
+  .ifnc \convert, nop_macro
+        beq     1301f
+        ldrx    \bpp,, <\reg, [PTR]>
+  .else
+        ldrx    \bpp, ne, <\reg, [PTR]>
+  .endif
+        eor     TMP, TMP, XHI
+        bics    TMP, TMP, #255/\bpp
+        \convert \reg, TMP
+  .ifnc \convert, nop_macro
+        b       1302f
+1301:   eor     TMP, TMP, XHI
+        bics    TMP, TMP, #255/\bpp
+1302:
+  .endif
+ .else
+  .ifc \mask_hint, mask_is_non_0
+        ldrx    \bpp,, <\reg, [PTR]>
+  .endif
+        eor     TMP, TMP, XHI
+        bics    TMP, TMP, #255/\bpp
+  .ifc \mask_hint, mask_is_non_0
+        \convert \reg, TMP
+  .endif
+ .endif
+        \store
+        branch  eq, \exit_label, 1303f
+        subs    PLDS, PLDS, #32
+        branch  lt, \exit_label, 1303f
+        bic     PTR, PTR, #31   @ base of *previous* cacheline
+        pld     [PTR, #(prefetch_distance_src+1)*32]
+1303:
+.endm
+
+.macro nearest_scaled_cover_reduce_nomask_innerloop  bpp, reg, convert, mask_hint, may_be_final, exit_label, store
+        add     PTR, SRC, XHI, lsl #log2_\bpp - 3
+        mov     TMP, XHI
+        adds    XLO, XLO, UX, lsl #16
+        adc     XHI, XHI, UX, lsr #16
+        ldrx    \bpp,, <\reg, [PTR]>
+        eor     TMP, TMP, XHI
+        bics    TMP, TMP, #255/\bpp
+        \convert \reg, TMP
+        \store
+        branch  eq, \exit_label, 1403f
+        subs    PLDS, PLDS, #32
+        branch  lt, \exit_label, 1403f
+        bic     PTR, PTR, #31   @ base of *previous* cacheline
+        pld     [PTR, #(prefetch_distance_src+1)*32]
+1403:
+.endm
+
+.macro process1  bpp, has_mask, disable_prefetch, inner_loop, convert
+ .if \has_mask
+        ldr     WK0, [MASK], #4
+  .if !\disable_prefetch
+        tst     MASK, #31
+        bne     1501f
+        pld     [MASK, #prefetch_distance_mask*32]
+1501:
+  .endif
+        teq     WK0, #0
+        bne     1502f
+        \inner_loop  \bpp, WK0, \convert, mask_is_0, 1, 1503f, <add DST, DST, #4>
+        b       1503f
+ .endif
+1502:   \inner_loop  \bpp, WK0, \convert, mask_is_non_0, 1,, <str WK0, [DST], #4>
+1503:
+.endm
+
+.macro process4  bpp, has_mask, disable_mask_prefetch, inner_loop, convert
+ .if \has_mask
+        ldmia   MASK!, {WK0-WK3}
+  .if !\disable_mask_prefetch
+        bic     TMP, MASK, #31
+        pld     [TMP, #prefetch_distance_mask*32]
+  .endif
+        orr     WK0, WK0, WK1
+        orr     WK2, WK2, WK3
+        orrs    WK0, WK0, WK2
+        bne     1601f
+        \inner_loop  \bpp, WK0, \convert, mask_is_0
+        \inner_loop  \bpp, WK1, \convert, mask_is_0
+        \inner_loop  \bpp, WK2, \convert, mask_is_0
+        \inner_loop  \bpp, WK3, \convert, mask_is_0, 1, 1602f, <add DST, DST, #4*4>
+        b       1602f
+ .endif
+1601:   \inner_loop  \bpp, WK0, \convert
+        \inner_loop  \bpp, WK1, \convert
+        \inner_loop  \bpp, WK2, \convert
+        \inner_loop  \bpp, WK3, \convert,, 1,, <stmia DST!!, {WK0,WK1,WK2,WK3}>
+1602:
+.endm
+
+.macro process  bpp, has_mask, inner_loop, convert
+        cmp     COUNT, #2 * 4 - 1 - 1   @ guaranteed at least one aligned half-cacheline output?
+        blo     1706f
+        tst     DST, #15
+        beq     1702f
+1701:   process1 \bpp, \has_mask, 0, \inner_loop, \convert
+        sub     COUNT, COUNT, #1
+        tst     DST, #15
+        bne     1701b
+1702:   sub     COUNT, COUNT, #4 - 1
+        tst     MASK, #16
+        beq     1704f
+1703:   process4  \bpp, \has_mask, 0, \inner_loop, \convert
+        subs    COUNT, COUNT, #4
+        bcc     1705f
+1704:   process4  \bpp, \has_mask, 1, \inner_loop, \convert
+        subs    COUNT, COUNT, #4
+        bcs     1703b
+1705:   adds    COUNT, COUNT, #4 - 1
+        bcc     1707f
+        @ drop through...
+1706:   process1 \bpp, \has_mask, 1, \inner_loop, \convert
+        subs    COUNT, COUNT, #1
+        bcs     1706b
+1707:   pop     {r4-r11, pc}
+.endm
+
+.macro generate_nearest_scaled_cover_function fname, \
+                                              bpp, \
+                                              prefetch_distance_src_, \
+                                              prefetch_distance_mask_, \
+                                              init, \
+                                              convert
+
+/* void fname(uint32_t width,
+ *            pixman_fixed_t x,
+ *            pixman_fixed_t ux,
+ *            uint32_t *dest,
+ *            const uint32_t *source,
+ *            const uint32_t *mask);
+ */
+pixman_asm_function fname
+
+/*
+ * Make some macro arguments globally visible and accessible
+ * from other macros
+ */
+ .set prefetch_distance_src,  prefetch_distance_src_
+ .set prefetch_distance_mask, prefetch_distance_mask_
+
+/*
+ * Assign symbolic names to registers
+ */
+COUNT   .req    a1
+X       .req    a2
+ACCUM   .req    a2  @ enlarge only
+XLO     .req    a2  @ reduce only
+UX      .req    a3
+DST     .req    a4
+SRC     .req    v1
+MASK    .req    v2
+PLDS    .req    v3
+PIXEL   .req    v4  @ enlarge only
+XHI     .req    v4  @ reduce only
+WK0     .req    v5
+WK1     .req    v6
+WK2     .req    sl
+WK3     .req    fp
+VALID   .req    ip  @ enlarge-with-mask only
+PTR     .req    ip  @ reduce only
+TMP     .req    lr
+
+        mov     ip, sp
+        push    {r4-r11, lr}        /* save all registers */
+        ldmia   ip, {SRC, MASK}
+        subs    COUNT, COUNT, #1
+        blo     1807f-4
+        \init
+        mla     WK2, COUNT, UX, X
+        bics    WK0, MASK, #31
+        beq     1801f
+        @ Use a simplified preload process for the mask,
+        @ without a braking distance.
+  .set OFFSET, 0
+  .rept prefetch_distance_mask + 1
+        pld     [WK0, #OFFSET]
+   .set OFFSET, OFFSET + 32
+  .endr
+1801:
+        add     WK0, SRC, X, lsr #16 - (log2_\bpp - 3)
+        bic     WK0, WK0, #31
+        pld     [WK0]
+        add     WK2, SRC, WK2, lsr #16 - (log2_\bpp - 3)
+        bic     WK2, WK2, #31
+        add     WK1, WK0, #prefetch_distance_src*32
+        subs    PLDS, WK2, WK1
+        movcc   WK1, WK2
+1802:   add     WK0, WK0, #32
+        cmp     WK0, WK1
+        bhi     1803f
+        pld     [WK0]
+        b       1802b
+1803:
+        cmp     UX, #0x10000
+        bhs     1805f
+        @ Enlarge
+        add     SRC, X, lsr #16 - (log2_\bpp - 3)
+        mov     ACCUM, X, lsl #16
+        mov     UX, UX, lsl #16
+        bic     SRC, SRC, #(\bpp-1)/8
+        teq     MASK, #0
+        beq     1804f
+        mov     VALID, #0
+        process \bpp, 1, nearest_scaled_cover_enlarge_mask_innerloop, \convert
+1804:
+        ldrx    \bpp,, <PIXEL, [SRC]>
+        \convert PIXEL, TMP
+        process \bpp, 0, nearest_scaled_cover_enlarge_nomask_innerloop, \convert
+
+1805:   @ Reduce
+        and     TMP, SRC, #31
+        bic     SRC, SRC, #31
+        mov     XHI, X, lsr #16
+        mov     XLO, X, lsl #16
+        add     XHI, XHI, TMP, lsr #log2_\bpp - 3
+        teq     MASK, #0
+        beq     1806f
+        process \bpp, 1, nearest_scaled_cover_reduce_mask_innerloop, \convert
+1806:   process \bpp, 0, nearest_scaled_cover_reduce_nomask_innerloop, \convert
+1807:
+
+        .unreq  COUNT
+        .unreq  X
+        .unreq  ACCUM
+        .unreq  XLO
+        .unreq  UX
+        .unreq  DST
+        .unreq  SRC
+        .unreq  MASK
+        .unreq  PLDS
+        .unreq  PIXEL
+        .unreq  XHI
+        .unreq  WK0
+        .unreq  WK1
+        .unreq  WK2
+        .unreq  WK3
+        .unreq  VALID
+        .unreq  PTR
+        .unreq  TMP
+.endfunc
+.endm
+
+.macro nop_macro x:vararg
+.endm
diff --git a/pixman/pixman-arm-simd.c b/pixman/pixman-arm-simd.c
index f028794..0a4daa7 100644
--- a/pixman/pixman-arm-simd.c
+++ b/pixman/pixman-arm-simd.c
@@ -116,6 +116,68 @@ PIXMAN_ARM_BIND_WRITE_BACK   (armv6, r5g6b5)
 PIXMAN_ARM_BIND_GET_SCANLINE (armv6, a1r5g5b5)
 PIXMAN_ARM_BIND_GET_SCANLINE (armv6, a8)
 
+#define PIXMAN_IMAGE_GET_SCALED(image, unscaled_x, unscaled_y, type, stride, out_bits, scaled_x, scaled_y, uxx, uxy, uyy) \
+    do                                                                                           \
+    {                                                                                            \
+        pixman_image_t  *__image__      = (image);                                               \
+        pixman_fixed_t   __offset__     = pixman_int_to_fixed (unscaled_x) + pixman_fixed_1 / 2; \
+        pixman_fixed_t   __line__       = pixman_int_to_fixed (unscaled_y) + pixman_fixed_1 / 2; \
+        pixman_fixed_t   __x__, __y__;                                                           \
+        int64_t          __x64__, __y64__;                                                       \
+        pixman_fixed_t (*__matrix__)[3] = __image__->common.transform->matrix;                   \
+                                                                                                 \
+        __x64__  = (int64_t) __matrix__[0][0] * (__offset__ & 0xFFFF);                           \
+        __x64__ += (int64_t) __matrix__[0][1] * (__line__ & 0xFFFF);                             \
+        __x__    = (__x64__ + 0x8000) >> 16;                                                     \
+        __x__   += __matrix__[0][0] * (__offset__ >> 16);                                        \
+        __x__   += __matrix__[0][1] * (__line__ >> 16);                                          \
+        __x__   += __matrix__[0][2];                                                             \
+        __y64__  = (int64_t) __matrix__[1][1] * (__line__ & 0xFFFF);                             \
+        __y__    = (__y64__ + 0x8000) >> 16;                                                     \
+        __y__   += __matrix__[1][1] * (__line__ >> 16);                                          \
+        __y__   += __matrix__[1][2];                                                             \
+                                                                                                 \
+        (stride)   = __image__->bits.rowstride * (int) sizeof (uint32_t) / (int) sizeof (type);  \
+        (out_bits) = (type *)__image__->bits.bits;                                               \
+        (scaled_x) = __x__;                                                                      \
+        (scaled_y) = __y__;                                                                      \
+        (uxx)      = __matrix__[0][0];                                                           \
+        (uxy)      = __matrix__[0][1];                                                           \
+        (uyy)      = __matrix__[1][1];                                                           \
+    } while (0)
+
+#define BIND_GET_SCANLINE_NEAREST_SCALED_COVER(cputype, name, type)         \
+void                                                                        \
+pixman_get_scanline_nearest_scaled_cover_##name##_asm_##cputype (           \
+                                                  uint32_t        width,    \
+                                                  pixman_fixed_t  x,        \
+                                                  pixman_fixed_t  ux,       \
+                                                  uint32_t       *dest,     \
+                                                  const type     *source,   \
+                                                  const uint32_t *mask);    \
+                                                                            \
+static uint32_t *                                                           \
+cputype##_get_scanline_nearest_scaled_cover_##name (pixman_iter_t  *iter,   \
+                                                    const uint32_t *mask)   \
+{                                                                           \
+    int            stride;                                                  \
+    type          *bits, *src;                                              \
+    pixman_fixed_t x, y, uxx, uxy, uyy;                                     \
+                                                                            \
+    PIXMAN_IMAGE_GET_SCALED (iter->image, iter->x, iter->y++, type,         \
+                             stride, bits, x, y, uxx, uxy, uyy);            \
+                                                                            \
+    (void) uxy;                                                             \
+    (void) uyy;                                                             \
+    src = bits + stride * pixman_fixed_to_int (y - pixman_fixed_e);         \
+    pixman_get_scanline_nearest_scaled_cover_##name##_asm_##cputype (       \
+            iter->width, x - pixman_fixed_e, uxx, iter->buffer, src, mask); \
+                                                                            \
+    return iter->buffer;                                                    \
+}
+
+BIND_GET_SCANLINE_NEAREST_SCALED_COVER (armv6, a8r8g8b8, uint32_t)
+
 void
 pixman_composite_src_n_8888_asm_armv6 (int32_t   w,
                                        int32_t   h,
@@ -340,6 +402,14 @@ static const pixman_fast_path_t arm_simd_fast_paths[] =
 
 static const pixman_iter_info_t arm_simd_iters[] =
 {
+    { PIXMAN_a8r8g8b8,
+      PIXMAN_ARM_NEAREST_SCALED_COVER_FLAGS,
+      ITER_NARROW | ITER_SRC,
+      NULL,
+      armv6_get_scanline_nearest_scaled_cover_a8r8g8b8,
+      NULL
+    },
+
     { PIXMAN_r5g6b5,
       (FAST_PATH_STANDARD_FLAGS             |
        FAST_PATH_ID_TRANSFORM               |
-- 
1.7.5.4



More information about the Pixman mailing list