[Mesa-dev] [PATCH 6/8] etnaviv: use filter blit for 2D YUV import on old GC320

Philipp Zabel p.zabel at pengutronix.de
Mon Apr 15 10:57:29 UTC 2019


On Fri, 2019-04-12 at 19:38 +0200, Lucas Stach wrote:
> The GC320 without the 2D tiling feature doesn't support regular blits
> with YUV input, as well as the tiled output. So on those cores we need
> need to do a filter blit for the YUV->RGB conversion to a temporary
> linear buffer and then do a tiling blit into the texture buffer using
> the RS engine on the 3D core.
> 
> Not the most efficient path, but at least gives us the same level of
> functionality as on the newer GC320 cores and looks the same to the
> application.
> 
> Signed-off-by: Lucas Stach <l.stach at pengutronix.de>
> ---
>  src/gallium/drivers/etnaviv/etnaviv_2d.c | 198 ++++++++++++++++++++---
>  1 file changed, 180 insertions(+), 18 deletions(-)
> 
> diff --git a/src/gallium/drivers/etnaviv/etnaviv_2d.c b/src/gallium/drivers/etnaviv/etnaviv_2d.c
> index 457fa4e0cbd0..31b6bf4313dd 100644
> --- a/src/gallium/drivers/etnaviv/etnaviv_2d.c
> +++ b/src/gallium/drivers/etnaviv/etnaviv_2d.c
> @@ -25,13 +25,16 @@
>  #include "etnaviv_context.h"
>  #include "etnaviv_emit.h"
>  #include "etnaviv_screen.h"
> +#include "etnaviv_rs.h"
>  
>  #include "pipe/p_state.h"
>  #include "util/u_format.h"
>  
>  #include "hw/state_2d.xml.h"
> +#include "hw/common.xml.h"
>  
>  #include <assert.h>
> +#include <math.h>
>  
>  #define EMIT_STATE(state_name, src_value) \
>     etna_coalsence_emit(stream, &coalesce, VIVS_##state_name, src_value)
> @@ -39,15 +42,85 @@
>  #define EMIT_STATE_RELOC(state_name, src_value) \
>     etna_coalsence_emit_reloc(stream, &coalesce, VIVS_##state_name, src_value)
>  
> +/* stolen from xf86-video-armada */
> +#define KERNEL_ROWS     17
> +#define KERNEL_INDICES  9
> +#define KERNEL_SIZE     (KERNEL_ROWS * KERNEL_INDICES)
> +#define KERNEL_STATE_SZ ((KERNEL_SIZE + 1) / 2)
> +
> +static bool filter_kernel_initialized;
> +static uint32_t filter_kernel[KERNEL_STATE_SZ];
> +
> +static inline float
> +sinc (float x)
       ^
> +{
> +  return x != 0.0 ? sinf (x) / x : 1.0;
                           ^
> +}
> +
> +static void
> +etnaviv_init_filter_kernel(void)
> +{
> +   unsigned row, idx, i;
> +   int16_t kernel_val[KERNEL_STATE_SZ * 2];
> +   float row_ofs = 0.5;
> +   float radius = 4.0;
> +
> +   /* Compute lanczos filter kernel */
> +   for (row = i = 0; row < KERNEL_ROWS; row++) {
> +      float kernel[KERNEL_INDICES] = { 0.0 };
> +      float sum = 0.0;
> +
> +      for (idx = 0; idx < KERNEL_INDICES; idx++) {
> +         float x = idx - 4.0 + row_ofs;
> +
> +         if (fabs (x) <= radius)
> +            kernel[idx] = sinc (M_PI * x) * sinc (M_PI * x / radius);
                                 ^                 ^

Why the whitespace?

> +
> +         sum += kernel[idx];
> +       }
> +
> +       /* normalise the row */
> +       if (sum)
> +          for (idx = 0; idx < KERNEL_INDICES; idx++)
> +             kernel[idx] /= sum;
> +
> +       /* convert to 1.14 format */
> +       for (idx = 0; idx < KERNEL_INDICES; idx++) {
> +          int val = kernel[idx] * (float) (1 << 14);
> +
> +          if (val < -0x8000)
> +             val = -0x8000;
> +          else if (val > 0x7fff)
> +             val = 0x7fff;
> +
> +          kernel_val[i++] = val;
> +       }
> +
> +       row_ofs -= 1.0 / ((KERNEL_ROWS - 1) * 2);
> +   }
> +
> +   kernel_val[KERNEL_SIZE] = 0;
> +
> +   /* Now convert the kernel values into state values */
> +   for (i = 0; i < KERNEL_STATE_SZ * 2; i += 2)
> +      filter_kernel[i / 2] =
> +         VIVS_DE_FILTER_KERNEL_COEFFICIENT0 (kernel_val[i]) |
> +         VIVS_DE_FILTER_KERNEL_COEFFICIENT1 (kernel_val[i + 1]);
> +}
> +
>  bool etna_try_2d_blit(struct pipe_context *pctx,
>                        const struct pipe_blit_info *blit_info)
>  {
>     struct etna_context *ctx = etna_context(pctx);
> +   struct etna_screen *screen = ctx->screen;
>     struct etna_cmd_stream *stream = ctx->stream2d;
>     struct etna_coalesce coalesce;
>     struct etna_reloc ry, ru, rv, rdst;
>     struct pipe_resource *res_y, *res_u, *res_v, *res_dst;
> +   struct etna_bo *temp_bo = NULL;
>     uint32_t src_format;
> +   bool ext_blt = VIV_2D_FEATURE(screen, chipMinorFeatures2, 2D_TILING);
> +   uint32_t dst_config;
>  
>     assert(util_format_is_yuv(blit_info->src.format));
>     assert(blit_info->dst.format == PIPE_FORMAT_R8G8B8A8_UNORM);
> @@ -55,6 +128,11 @@ bool etna_try_2d_blit(struct pipe_context *pctx,
>     if (!stream)
>        return FALSE;
>  
> +  if (unlikely(!ext_blt && !filter_kernel_initialized)) {
> +      etnaviv_init_filter_kernel();
> +      filter_kernel_initialized = true;
> +  }
> +
>     switch (blit_info->src.format) {
>     case PIPE_FORMAT_NV12:
>        src_format = DE_FORMAT_NV12;
> @@ -66,6 +144,18 @@ bool etna_try_2d_blit(struct pipe_context *pctx,
>        return FALSE;
>     }
>  
> +   res_dst = blit_info->dst.resource;
> +
> +   if (!ext_blt && etna_resource(res_dst)->layout != ETNA_LAYOUT_LINEAR) {
> +      struct etna_resource *dst = etna_resource(blit_info->dst.resource);
> +      unsigned int bo_size = dst->levels[blit_info->dst.level].stride *
> +                             dst->levels[blit_info->dst.level].padded_height;
> +
> +      temp_bo = etna_bo_new(screen->dev, bo_size, DRM_ETNA_GEM_CACHE_WC);
> +      if (!temp_bo)
> +         return FALSE;
> +   }
> +
>     res_y = blit_info->src.resource;
>     res_u = res_y->next ? res_y->next : res_y;
>     res_v = res_u->next ? res_u->next : res_u;
> @@ -79,8 +169,7 @@ bool etna_try_2d_blit(struct pipe_context *pctx,
>  
>     ry.flags = ru.flags = rv.flags = ETNA_RELOC_READ;
>  
> -   res_dst = blit_info->dst.resource;
> -   rdst.bo = etna_resource(res_dst)->bo;
> +   rdst.bo = temp_bo ? temp_bo : etna_resource(res_dst)->bo;
>     rdst.flags = ETNA_RELOC_WRITE;
>     rdst.offset = 0;
>  
> @@ -114,11 +203,15 @@ bool etna_try_2d_blit(struct pipe_context *pctx,
>  
>     /* Drawing engine configuration */
>     EMIT_STATE(DE_DEST_ROTATION_CONFIG, 0);
> -   EMIT_STATE(DE_DEST_CONFIG,
> -              VIVS_DE_DEST_CONFIG_FORMAT(DE_FORMAT_A8R8G8B8) |
> -              VIVS_DE_DEST_CONFIG_COMMAND_BIT_BLT |
> -              VIVS_DE_DEST_CONFIG_SWIZZLE(DE_SWIZZLE_ABGR) |
> -              VIVS_DE_DEST_CONFIG_TILED_ENABLE);
> +   dst_config = VIVS_DE_DEST_CONFIG_FORMAT(DE_FORMAT_A8R8G8B8) |
> +                VIVS_DE_DEST_CONFIG_SWIZZLE(DE_SWIZZLE_ABGR);
> +   if (ext_blt) {
> +      dst_config |= VIVS_DE_DEST_CONFIG_COMMAND_BIT_BLT |
> +                    VIVS_DE_DEST_CONFIG_TILED_ENABLE;
> +   } else {
> +      dst_config |= VIVS_DE_DEST_CONFIG_COMMAND_HOR_FILTER_BLT;
> +   }
> +   EMIT_STATE(DE_DEST_CONFIG, dst_config);
>     EMIT_STATE(DE_ROP,
>                VIVS_DE_ROP_ROP_FG(0xcc) | VIVS_DE_ROP_ROP_BG(0xcc) |
>                VIVS_DE_ROP_TYPE_ROP4);
> @@ -136,21 +229,46 @@ bool etna_try_2d_blit(struct pipe_context *pctx,
>     EMIT_STATE(DE_SRC_ROTATION_HEIGHT, 0);
>     EMIT_STATE(DE_ROT_ANGLE, 0);
>  
> +   if (!ext_blt) {
> +      EMIT_STATE(DE_VR_SOURCE_IMAGE_LOW,
> +                 VIVS_DE_VR_SOURCE_IMAGE_LOW_LEFT (0) |
> +                 VIVS_DE_VR_SOURCE_IMAGE_LOW_TOP (0));
> +      EMIT_STATE(DE_VR_SOURCE_IMAGE_HIGH,
> +                 VIVS_DE_VR_SOURCE_IMAGE_HIGH_RIGHT(blit_info->src.box.width) |
> +                 VIVS_DE_VR_SOURCE_IMAGE_HIGH_BOTTOM(blit_info->src.box.height));
> +      EMIT_STATE(DE_VR_SOURCE_ORIGIN_LOW, VIVS_DE_VR_SOURCE_ORIGIN_LOW_X(1));
> +      EMIT_STATE(DE_VR_SOURCE_ORIGIN_HIGH, VIVS_DE_VR_SOURCE_ORIGIN_HIGH_Y(1));
> +      EMIT_STATE(DE_VR_TARGET_WINDOW_LOW,
> +                 VIVS_DE_VR_TARGET_WINDOW_LOW_LEFT(0) |
> +                 VIVS_DE_VR_TARGET_WINDOW_LOW_TOP(0));
> +      EMIT_STATE(DE_VR_TARGET_WINDOW_HIGH,
> +                 VIVS_DE_VR_TARGET_WINDOW_HIGH_RIGHT(blit_info->dst.box.width) |
> +                 VIVS_DE_VR_TARGET_WINDOW_HIGH_BOTTOM(blit_info->dst.box.height));
> +   }
> +
>     etna_coalesce_end(stream, &coalesce);
>  
> -   etna_cmd_stream_emit(stream, VIV_FE_DRAW_2D_HEADER_OP_DRAW_2D |
> -                        VIV_FE_DRAW_2D_HEADER_COUNT(1));
> -   etna_cmd_stream_emit(stream, 0xdeadbeef);
> +   if (ext_blt) {
> +      etna_cmd_stream_emit(stream, VIV_FE_DRAW_2D_HEADER_OP_DRAW_2D |
> +                           VIV_FE_DRAW_2D_HEADER_COUNT(1));
> +      etna_cmd_stream_emit(stream, 0xdeadbeef);
>  
> -   etna_cmd_stream_emit(stream, VIV_FE_DRAW_2D_TOP_LEFT_X(0) |
> -                        VIV_FE_DRAW_2D_TOP_LEFT_Y(0));
> -   etna_cmd_stream_emit(stream,
> -                        VIV_FE_DRAW_2D_BOTTOM_RIGHT_X(blit_info->dst.box.width) |
> -                        VIV_FE_DRAW_2D_BOTTOM_RIGHT_Y(blit_info->dst.box.height));
> +      etna_cmd_stream_emit(stream, VIV_FE_DRAW_2D_TOP_LEFT_X(0) |
> +                           VIV_FE_DRAW_2D_TOP_LEFT_Y(0));
> +      etna_cmd_stream_emit(stream,
> +                           VIV_FE_DRAW_2D_BOTTOM_RIGHT_X(blit_info->dst.box.width) |
> +                           VIV_FE_DRAW_2D_BOTTOM_RIGHT_Y(blit_info->dst.box.height));
>  
> -   etna_set_state(stream, 1, 0);
> -   etna_set_state(stream, 1, 0);
> -   etna_set_state(stream, 1, 0);
> +      etna_set_state(stream, 1, 0);
> +      etna_set_state(stream, 1, 0);
> +      etna_set_state(stream, 1, 0);
> +   } else {
> +      etna_set_state_multi(stream, VIVS_DE_FILTER_KERNEL(0), KERNEL_STATE_SZ,
> +                           filter_kernel);
> +
> +      etna_set_state(stream, VIVS_DE_VR_CONFIG,
> +                     VIVS_DE_VR_CONFIG_START_HORIZONTAL_BLIT);
> +   }
>  
>     etna_set_state(stream, VIVS_GL_FLUSH_CACHE, VIVS_GL_FLUSH_CACHE_PE2D);
>  
> @@ -160,5 +278,49 @@ bool etna_try_2d_blit(struct pipe_context *pctx,
>      */
>     etna_cmd_stream_flush(ctx->stream2d);
>  
> +   if (temp_bo) {
> +      struct etna_resource *dst = etna_resource(res_dst);
> +      struct compiled_rs_state tile_blit;
> +
> +      etna_compile_rs_state(ctx, &tile_blit, &(struct rs_state) {
> +            .source_format = RS_FORMAT_X8R8G8B8,
> +            .source_tiling = ETNA_LAYOUT_LINEAR,
> +            .source = temp_bo,
> +            .source_offset = 0,
> +            .source_stride = dst->levels[0].stride,
> +            .source_padded_width = dst->levels[0].padded_width,
> +            .source_padded_height = dst->levels[0].padded_height,
> +            .source_ts_valid = 0,
> +            .dest_format = RS_FORMAT_X8R8G8B8,
> +            .dest_tiling = ETNA_LAYOUT_TILED,
> +            .dest = dst->bo,
> +            .dest_offset = 0,
> +            .dest_stride = dst->levels[0].stride,
> +            .dest_padded_height = dst->levels[0].padded_height,
> +            .downsample_x = 0,
> +            .downsample_y = 0,
> +            .swap_rb = 0,
> +            .dither = {0xffffffff, 0xffffffff},
> +            .clear_mode = VIVS_RS_CLEAR_CONTROL_MODE_DISABLED,
> +            .width = blit_info->dst.box.width,
> +            .height = blit_info->dst.box.height,
> +         });
> +
> +      /* The combined color/depth cache flush is required to avoid pixels stuck
> +       * in the caches being flushed to the RS target. This seems to be some bug
> +       * found on at least GC2000, with no other known workaround.
> +       */
> +      etna_set_state(ctx->stream, VIVS_GL_FLUSH_CACHE,
> +                     VIVS_GL_FLUSH_CACHE_COLOR | VIVS_GL_FLUSH_CACHE_DEPTH);
> +      etna_stall(ctx->stream, SYNC_RECIPIENT_RA, SYNC_RECIPIENT_PE);
> +      etna_set_state(ctx->stream, VIVS_TS_MEM_CONFIG, 0);
> +      ctx->dirty |= ETNA_DIRTY_TS;
> +      etna_submit_rs_state(ctx, &tile_blit);
> +
> +      /* flush now, so we can get rid of the temp BO right here */
> +      etna_cmd_stream_flush(ctx->stream);
> +      etna_bo_del(temp_bo);
> +   }
> +
>     return TRUE;
>  }

Reviewed-by: Philipp Zabel <p.zabel at pengutronix.de>

regards
Philipp


More information about the mesa-dev mailing list