etnaviv: add support for sharing the TS buffer
authorLucas Stach <l.stach@pengutronix.de>
Wed, 28 Oct 2020 19:13:45 +0000 (20:13 +0100)
committerMarge Bot <emma+marge@anholt.net>
Thu, 3 Nov 2022 20:24:41 +0000 (20:24 +0000)
This adds support for sharing the TS buffer, which up until now has been
an internal implementation detail, with the outside world. This mainly
improves performance with a GPU compositor present, but on i.MX8M also
direct to display use-cases benefit.

The impact of this change depends on the GPU generation:
- old GPUs with a single pipe won't see any difference
- GC2000 can skip the TS resolve in the client and will benefit from a
  more efficient blit into the sampler compatible format when the client
  buffer contains cleared tiles
- GC3000 can directly sample with TS support, so saves both write and read
  memory bandwidth when the client buffer contains cleared tiles
- GC7000 with compression support can keep the client buffer in compressed
  format, thus saving both read and write bandwidth even for fully filled
  client buffers
- GC7000 coupled to a display unit supporting the compression format (DCSS
  on i.MX8M) does not even need to uncompress the render buffer for display
  so will see significant bandwidth saving even when GPU compositing is
  bypassed

There is a slight complication in that the tile clear color isn't part of
the TS buffer, but is programmed into state registers in the GPU. To handle
this externally shared TS buffers now contain a software metadata area,
where the clear color is stored by the driver, so the receiving end of the
TS buffer can retrieve the clear color from this area.

The compression format is handled in the same way by storing it in the SW
meta area. While we can derive the compression format from the color buffer
format in most cases, some users, like weston, expect that they can "upgrade"
ARGB to XRGB color formats. While this works with plain color formats, as
it's just masking a channel, the compression format differs when alpha is in
use. Receivers of the TS buffer should thus not try to infer the compression
format from the color buffer format, but instead fetch it from the SW meta.

The import/export handling of the TS buffer is modelled after the Intel iris
driver: we add a separate plane for the TS buffer and fold it into the base
resource after the import.

Signed-off-by: Lucas Stach <l.stach@pengutronix.de>
Tested-by: Guido Günther <agx@sigxcpu.org>
Reviewed-by: Christian Gmeiner <christian.gmeiner@gmail.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/9780>

src/gallium/drivers/etnaviv/etnaviv_blt.c
src/gallium/drivers/etnaviv/etnaviv_clear_blit.c
src/gallium/drivers/etnaviv/etnaviv_resource.c
src/gallium/drivers/etnaviv/etnaviv_resource.h
src/gallium/drivers/etnaviv/etnaviv_rs.c
src/gallium/drivers/etnaviv/etnaviv_screen.c
src/gallium/drivers/etnaviv/etnaviv_surface.c
src/gallium/drivers/etnaviv/etnaviv_texture.c
src/gallium/drivers/etnaviv/etnaviv_translate.h

index 8ec41e1..125d625 100644 (file)
@@ -258,6 +258,12 @@ etna_blit_clear_color_blt(struct pipe_context *pctx, struct pipe_surface *dst,
    if (surf->surf.ts_size) {
       ctx->framebuffer.TS_COLOR_CLEAR_VALUE = new_clear_value;
       ctx->framebuffer.TS_COLOR_CLEAR_VALUE_EXT = new_clear_value >> 32;
+
+      /* update clear color in SW meta area of the buffer if TS is exported */
+      if (unlikely(new_clear_value != surf->level->clear_value &&
+          etna_resource_ext_ts(etna_resource(dst->texture))))
+         etna_resource(dst->texture)->ts_meta->v0.clear_value = new_clear_value;
+
       surf->level->ts_valid = true;
       ctx->dirty |= ETNA_DIRTY_TS | ETNA_DIRTY_DERIVE_TS;
    }
index 113f9cf..bef5a9d 100644 (file)
@@ -177,7 +177,7 @@ etna_flush_resource(struct pipe_context *pctx, struct pipe_resource *prsc)
          etna_copy_resource(pctx, prsc, rsc->render, 0, 0);
          rsc->seqno = etna_resource(rsc->render)->seqno;
       }
-   } else if (etna_resource_needs_flush(rsc)) {
+   } else if (!etna_resource_ext_ts(rsc) && etna_resource_needs_flush(rsc)) {
       etna_copy_resource(pctx, prsc, prsc, 0, 0);
       rsc->flush_seqno = rsc->seqno;
    }
index b807e58..7e730a3 100644 (file)
 #include "util/u_inlines.h"
 #include "util/u_memory.h"
 
-#include "drm-uapi/drm_fourcc.h"
-
 static enum etna_surface_layout modifier_to_layout(uint64_t modifier)
 {
-   switch (modifier) {
+   switch (modifier & ~VIVANTE_MOD_EXT_MASK) {
    case DRM_FORMAT_MOD_VIVANTE_TILED:
       return ETNA_LAYOUT_TILED;
    case DRM_FORMAT_MOD_VIVANTE_SUPER_TILED:
@@ -75,19 +73,27 @@ static uint64_t layout_to_modifier(enum etna_surface_layout layout)
    }
 }
 
+static uint64_t etna_resource_modifier(struct etna_resource *rsc)
+{
+   if (etna_resource_ext_ts(rsc))
+      return rsc->modifier;
+
+   return layout_to_modifier(rsc->layout);
+}
+
 /* A tile is either 64 bytes or, when the GPU has the CACHE128B256BPERLINE
  * feature, 128/256 bytes of color/depth data, tracked by
  * 'screen->specs.bits_per_tile' bits of tile status.
  */
 bool
 etna_screen_resource_alloc_ts(struct pipe_screen *pscreen,
-                              struct etna_resource *rsc)
+                              struct etna_resource *rsc, uint64_t modifier)
 {
    struct etna_screen *screen = etna_screen(pscreen);
    struct pipe_resource *prsc = &rsc->base;
-   size_t tile_size, rt_ts_size, ts_layer_stride;
+   size_t tile_size, ts_size, ts_bo_size, ts_layer_stride, ts_data_offset = 0;
    uint8_t ts_mode = TS_MODE_128B;
-   int8_t ts_compress_fmt;
+   int8_t ts_compress_fmt = -1;
    unsigned layers;
 
    assert(!rsc->ts_bo);
@@ -96,36 +102,64 @@ etna_screen_resource_alloc_ts(struct pipe_screen *pscreen,
     * v4 compression can be enabled everywhere without any known drawback,
     * except that in-place resolve must go through a slower path
     */
-   ts_compress_fmt = (screen->specs.v4_compression || prsc->nr_samples > 1) ?
-                      translate_ts_format(prsc->format) : -1;
+   if ((screen->specs.v4_compression &&
+        (!modifier || (modifier & VIVANTE_MOD_COMP_DEC400))) ||
+       (!modifier && prsc->nr_samples > 1))
+      ts_compress_fmt = translate_ts_format(prsc->format);
 
    /* enable 256B ts mode with compression, as it improves performance
     * the size of the resource might also determine if we want to use it or not
     */
-   if (VIV_FEATURE(screen, chipMinorFeatures6, CACHE128B256BPERLINE) &&
-       ts_compress_fmt >= 0 &&
-       (rsc->layout != ETNA_LAYOUT_LINEAR ||
-        rsc->levels[0].stride % 256 == 0) )
+   if (VIV_FEATURE(screen, chipMinorFeatures6, CACHE128B256BPERLINE)) {
+      if ((modifier & VIVANTE_MOD_TS_MASK) == VIVANTE_MOD_TS_128_4)
+         ts_mode = TS_MODE_128B;
+      else if ((modifier & VIVANTE_MOD_TS_MASK) == VIVANTE_MOD_TS_256_4)
          ts_mode = TS_MODE_256B;
+      else {
+         /* Without a TS modifier TS is only internal, so we can choose the
+          * mode to use freely. Enable 256B ts mode with compression, as it
+          * improves performance. The size of the resource might also determine
+          * if we want to use it or not.
+          */
+         if (ts_compress_fmt >= 0 &&
+            (rsc->layout != ETNA_LAYOUT_LINEAR ||
+             rsc->levels[0].stride % 256 == 0))
+            ts_mode = TS_MODE_256B;
+         else
+            ts_mode = TS_MODE_128B;
+      }
+   }
 
    tile_size = etna_screen_get_tile_size(screen, ts_mode, prsc->nr_samples > 1);
    layers = prsc->target == PIPE_TEXTURE_3D ? prsc->depth0 : prsc->array_size;
    ts_layer_stride = align(DIV_ROUND_UP(rsc->levels[0].layer_stride,
                                         tile_size * 8 / screen->specs.bits_per_tile),
                            0x100 * screen->specs.pixel_pipes);
-   rt_ts_size = ts_layer_stride * layers;
-   if (rt_ts_size == 0)
+   ts_size = ts_bo_size = ts_layer_stride * layers;
+   if (ts_size == 0)
       return true;
 
+   /* add space for the software meta */
+   if (modifier & VIVANTE_MOD_TS_MASK) {
+      /* The offset is a educated guess for a safe value based on the experience
+       * that various object pointers on the GPU need to be 64B aligned. Maybe
+       * some GPU needs even more alignment, or we could drop this to 32B. 64B
+       * has worked well across various GPU generations so far.
+       */
+      ts_data_offset = 64;
+      assert(ts_data_offset >= sizeof(struct etna_ts_sw_meta));
+      ts_bo_size += ts_data_offset;
+   }
+
    DBG_F(ETNA_DBG_RESOURCE_MSGS, "%p: Allocating tile status of size %zu",
-         rsc, rt_ts_size);
+         rsc, ts_bo_size);
 
    if ((rsc->base.bind & PIPE_BIND_SCANOUT) && screen->ro->kms_fd >= 0) {
       struct pipe_resource scanout_templat;
       struct winsys_handle handle;
 
       scanout_templat.format = PIPE_FORMAT_R8_UNORM;
-      scanout_templat.width0 = align(rt_ts_size, 4096);
+      scanout_templat.width0 = align(ts_bo_size, 4096);
       scanout_templat.height0 = 1;
 
       rsc->ts_scanout = renderonly_scanout_for_resource(&scanout_templat,
@@ -147,12 +181,21 @@ etna_screen_resource_alloc_ts(struct pipe_screen *pscreen,
       return false;
    }
 
-   rsc->levels[0].ts_offset = 0;
+   rsc->levels[0].ts_offset = ts_data_offset;
    rsc->levels[0].ts_layer_stride = ts_layer_stride;
-   rsc->levels[0].ts_size = rt_ts_size;
+   rsc->levels[0].ts_size = ts_size;
    rsc->levels[0].ts_mode = ts_mode;
    rsc->levels[0].ts_compress_fmt = ts_compress_fmt;
 
+   /* fill software meta */
+   if (modifier & VIVANTE_MOD_TS_MASK) {
+      rsc->ts_meta = etna_bo_map(rsc->ts_bo);
+      rsc->ts_meta->version = 0;
+      rsc->ts_meta->v0.data_size = ts_size;
+      rsc->ts_meta->v0.data_offset = ts_data_offset;
+      rsc->ts_meta->v0.layer_stride = ts_layer_stride;
+      rsc->ts_meta->v0.comp_format = ts_format_to_drmfourcc(rsc->levels[0].ts_compress_fmt);
+   }
    return true;
 }
 
@@ -305,6 +348,7 @@ etna_resource_alloc(struct pipe_screen *pscreen, unsigned layout,
    rsc->base.screen = pscreen;
    rsc->base.nr_samples = templat->nr_samples;
    rsc->layout = layout;
+   rsc->modifier = modifier;
    rsc->halign = halign;
    rsc->explicit_flush = true;
 
@@ -346,6 +390,12 @@ etna_resource_alloc(struct pipe_screen *pscreen, unsigned layout,
       }
    }
 
+   /* If TS is externally visible set it up now, so it can be exported before
+    * the first rendering to a surface.
+    */
+   if (etna_resource_ext_ts(rsc))
+      etna_screen_resource_alloc_ts(pscreen, rsc, modifier);
+
    if (DBG_ENABLED(ETNA_DBG_ZERO)) {
       void *map = etna_bo_map(rsc->bo);
       etna_bo_cpu_prep(rsc->bo, DRM_ETNA_PREP_WRITE);
@@ -433,9 +483,10 @@ select_best_modifier(const struct etna_screen * screen,
                      const uint64_t *modifiers, const unsigned count)
 {
    enum modifier_priority prio = MODIFIER_PRIORITY_INVALID;
+   uint64_t best_modifier, base_modifier;
 
    for (int i = 0; i < count; i++) {
-      switch (modifiers[i]) {
+      switch (modifiers[i] & ~VIVANTE_MOD_EXT_MASK) {
       case DRM_FORMAT_MOD_VIVANTE_SUPER_TILED:
          if ((screen->specs.pixel_pipes > 1 && !screen->specs.single_buffer) ||
              !screen->specs.can_supertile)
@@ -466,7 +517,32 @@ select_best_modifier(const struct etna_screen * screen,
       }
    }
 
-   return priority_to_modifier[prio];
+   best_modifier = base_modifier = priority_to_modifier[prio];
+
+   /* Make a second pass to try and find the best TS modifier if any. */
+   for (int i = 0; i < count; i++) {
+      if ((modifiers[i] & ~VIVANTE_MOD_EXT_MASK) == base_modifier)
+         if ((modifiers[i] & VIVANTE_MOD_TS_MASK) >
+             (best_modifier & VIVANTE_MOD_TS_MASK))
+            best_modifier = modifiers[i];
+   }
+
+   /* If no modifier with TS was found, there is no point in looking further,
+    * as compression depends on TS.
+    */
+   if (best_modifier == base_modifier)
+      return best_modifier;
+
+   /* Make a third pass to find a modifier allowing compression. */
+   base_modifier = best_modifier;
+   for (int i = 0; i < count; i++) {
+      if ((modifiers[i] & ~VIVANTE_MOD_COMP_MASK) == base_modifier)
+         if ((modifiers[i] & VIVANTE_MOD_COMP_MASK) >
+             (best_modifier & VIVANTE_MOD_COMP_MASK))
+            best_modifier = modifiers[i];
+   }
+
+   return best_modifier;
 }
 
 static struct pipe_resource *
@@ -504,6 +580,9 @@ etna_resource_destroy(struct pipe_screen *pscreen, struct pipe_resource *prsc)
    if (rsc->scanout)
       renderonly_scanout_destroy(rsc->scanout, etna_screen(pscreen)->ro);
 
+   if (rsc->ts_scanout)
+      renderonly_scanout_destroy(rsc->ts_scanout, etna_screen(pscreen)->ro);
+
    util_range_destroy(&rsc->valid_buffer_range);
 
    pipe_resource_reference(&rsc->texture, NULL);
@@ -515,6 +594,38 @@ etna_resource_destroy(struct pipe_screen *pscreen, struct pipe_resource *prsc)
    FREE(rsc);
 }
 
+static void etna_resource_finish_ts_import(struct etna_screen *screen,
+                                           struct etna_resource *rsc)
+{
+   struct etna_resource *ts_rsc = etna_resource(rsc->base.next);
+   uint64_t ts_modifier = rsc->modifier & VIVANTE_MOD_TS_MASK;
+   uint8_t ts_mode = 0;
+
+   if (ts_modifier == VIVANTE_MOD_TS_256_4)
+      ts_mode = TS_MODE_256B;
+
+   rsc->ts_bo = etna_bo_ref(ts_rsc->bo);
+   rsc->ts_meta = etna_bo_map(rsc->ts_bo) + ts_rsc->levels[0].offset;
+
+   rsc->ts_scanout = ts_rsc->scanout;
+   ts_rsc->scanout = NULL;
+
+   /* Get TS layout/usage information from the SW meta. FIXME: clear color may
+    * change over the lifetime of the resource, so might need to look this up
+    * at a few other places. For now it's not an issue, as buffers with shared
+    * TS get re-imported all the time. */
+   rsc->levels[0].ts_compress_fmt = drmfourcc_to_ts_format(rsc->ts_meta->v0.comp_format);
+   rsc->levels[0].ts_offset = ts_rsc->levels[0].offset + rsc->ts_meta->v0.data_offset;
+   rsc->levels[0].ts_layer_stride = rsc->ts_meta->v0.layer_stride;
+   rsc->levels[0].clear_value = rsc->ts_meta->v0.clear_value;
+   rsc->levels[0].ts_size = rsc->ts_meta->v0.data_size;
+   rsc->levels[0].ts_mode = ts_mode;
+   rsc->levels[0].ts_valid = true;
+
+   etna_resource_destroy(&screen->base, rsc->base.next);
+   rsc->base.next = NULL;
+}
+
 static struct pipe_resource *
 etna_resource_from_handle(struct pipe_screen *pscreen,
                           const struct pipe_resource *tmpl,
@@ -554,6 +665,7 @@ etna_resource_from_handle(struct pipe_screen *pscreen,
 
    rsc->seqno = 1;
    rsc->layout = modifier_to_layout(modifier);
+   rsc->modifier = modifier;
 
    if (usage & PIPE_HANDLE_USAGE_EXPLICIT_FLUSH)
       rsc->explicit_flush = true;
@@ -576,6 +688,20 @@ etna_resource_from_handle(struct pipe_screen *pscreen,
                                                                   level->padded_height);
    level->size = level->layer_stride;
 
+   if (screen->ro) {
+      struct pipe_resource *imp_prsc = prsc;
+      do {
+         etna_resource(imp_prsc)->scanout =
+               renderonly_create_gpu_import_for_resource(imp_prsc, screen->ro,
+                                                         NULL);
+         /* failure is expected for scanout incompatible buffers */
+      } while ((imp_prsc = imp_prsc->next));
+   }
+
+   /* If the buffer is for a TS plane, skip the RS compatible checks */
+   if (handle->plane >= util_format_get_num_planes(prsc->format))
+      return prsc;
+
    /* The DDX must give us a BO which conforms to our padding size.
     * The stride of the BO must be greater or equal to our padded
     * stride. The size of the BO must accomodate the padded height. */
@@ -592,15 +718,8 @@ etna_resource_from_handle(struct pipe_screen *pscreen,
       goto fail;
    }
 
-   if (screen->ro) {
-      struct pipe_resource *imp_prsc = prsc;
-      do {
-         etna_resource(imp_prsc)->scanout =
-               renderonly_create_gpu_import_for_resource(imp_prsc, screen->ro,
-                                                         NULL);
-         /* failure is expected for scanout incompatible buffers */
-      } while ((imp_prsc = imp_prsc->next));
-   }
+   if (handle->plane == 0 && etna_resource_ext_ts(rsc))
+      etna_resource_finish_ts_import(screen, rsc);
 
    return prsc;
 
@@ -618,9 +737,12 @@ etna_resource_get_handle(struct pipe_screen *pscreen,
 {
    struct etna_screen *screen = etna_screen(pscreen);
    struct etna_resource *rsc = etna_resource(prsc);
+   bool wants_ts = etna_resource_ext_ts(rsc) &&
+                      handle->plane >= util_format_get_num_planes(prsc->format);
    struct renderonly_scanout *scanout;
+   struct etna_bo *bo;
 
-   if (handle->plane) {
+   if (handle->plane && !wants_ts) {
       struct pipe_resource *cur = prsc;
 
       for (int i = 0; i < handle->plane; i++) {
@@ -634,24 +756,37 @@ etna_resource_get_handle(struct pipe_screen *pscreen,
    /* Scanout is always attached to the base resource */
    scanout = rsc->scanout;
 
-   handle->stride = rsc->levels[0].stride;
-   handle->offset = rsc->levels[0].offset;
-   handle->modifier = layout_to_modifier(rsc->layout);
+   if (wants_ts) {
+      handle->stride = DIV_ROUND_UP(rsc->levels[0].stride,
+                          etna_screen_get_tile_size(screen,
+                                                     rsc->levels[0].ts_mode,
+                                                     false)
+                          * 8 /screen->specs.bits_per_tile);
+      handle->offset = rsc->levels[0].ts_offset - rsc->ts_meta->v0.data_offset;
+      scanout = rsc->ts_scanout;
+      bo = rsc->ts_bo;
+   } else {
+      handle->stride = rsc->levels[0].stride;
+      handle->offset = rsc->levels[0].offset;
+      scanout = rsc->scanout;
+      bo = rsc->bo;
+   }
+   handle->modifier = etna_resource_modifier(rsc);
 
    if (!(usage & PIPE_HANDLE_USAGE_EXPLICIT_FLUSH))
       rsc->explicit_flush = false;
 
    if (handle->type == WINSYS_HANDLE_TYPE_SHARED) {
-      return etna_bo_get_name(rsc->bo, &handle->handle) == 0;
+      return etna_bo_get_name(bo, &handle->handle) == 0;
    } else if (handle->type == WINSYS_HANDLE_TYPE_KMS) {
       if (screen->ro) {
          return renderonly_get_handle(scanout, handle);
       } else {
-         handle->handle = etna_bo_handle(rsc->bo);
+         handle->handle = etna_bo_handle(bo);
          return true;
       }
    } else if (handle->type == WINSYS_HANDLE_TYPE_FD) {
-      handle->handle = etna_bo_dmabuf(rsc->bo);
+      handle->handle = etna_bo_dmabuf(bo);
       return true;
    } else {
       return false;
@@ -665,32 +800,54 @@ etna_resource_get_param(struct pipe_screen *pscreen,
                         enum pipe_resource_param param,
                         unsigned usage, uint64_t *value)
 {
+   struct etna_screen *screen = etna_screen(pscreen);
+   struct etna_resource *rsc = etna_resource(prsc);
+   bool wants_ts = etna_resource_ext_ts(rsc) &&
+                      plane >= util_format_get_num_planes(prsc->format);
+
    if (param == PIPE_RESOURCE_PARAM_NPLANES) {
-      unsigned count = 0;
+      if (etna_resource_ext_ts(rsc)) {
+               *value = 2;
+      } else {
+         unsigned count = 0;
 
-      for (struct pipe_resource *cur = prsc; cur; cur = cur->next)
-         count++;
-      *value = count;
+         for (struct pipe_resource *cur = prsc; cur; cur = cur->next)
+            count++;
+         *value = count;
+      }
       return true;
    }
 
-   struct pipe_resource *cur = prsc;
-   for (int i = 0; i < plane; i++) {
-      cur = cur->next;
-      if (!cur)
-         return false;
+   if (!wants_ts) {
+      struct pipe_resource *cur = prsc;
+      for (int i = 0; i < plane; i++) {
+         cur = cur->next;
+         if (!cur)
+            return false;
+      }
+      rsc = etna_resource(cur);
    }
-   struct etna_resource *rsc = etna_resource(cur);
 
    switch (param) {
    case PIPE_RESOURCE_PARAM_STRIDE:
-      *value = rsc->levels[level].stride;
+      if (wants_ts) {
+         *value = DIV_ROUND_UP(rsc->levels[0].stride,
+                               etna_screen_get_tile_size(screen,
+                                                         rsc->levels[0].ts_mode,
+                                                         prsc->nr_samples > 1)
+                               * 8 / screen->specs.bits_per_tile);
+      } else {
+         *value = rsc->levels[0].stride;
+      }
       return true;
    case PIPE_RESOURCE_PARAM_OFFSET:
-      *value = rsc->levels[level].offset;
+      if (wants_ts)
+         *value = rsc->levels[0].ts_offset - rsc->ts_meta->v0.data_offset;
+      else
+         *value = rsc->levels[0].offset;
       return true;
    case PIPE_RESOURCE_PARAM_MODIFIER:
-      *value = layout_to_modifier(rsc->layout);
+      *value = etna_resource_modifier(rsc);
       return true;
    default:
       return false;
index 14a02bf..ffd46dd 100644 (file)
 #include "util/u_helpers.h"
 #include "util/u_range.h"
 
+#include "drm-uapi/drm_fourcc.h"
+
 struct etna_context;
 struct pipe_screen;
 struct util_dynarray;
 
+struct etna_ts_sw_meta {
+   uint16_t version;
+   struct {
+      uint16_t data_offset;
+      uint32_t data_size;
+      uint32_t layer_stride;
+      uint32_t comp_format;
+      uint64_t clear_value;
+   } v0;
+};
+
 struct etna_resource_level {
    unsigned width, padded_width; /* in pixels */
    unsigned height, padded_height; /* in samples */
@@ -79,10 +92,13 @@ struct etna_resource {
    /* only lod 0 used for non-texture buffers */
    /* Layout for surface (tiled, multitiled, split tiled, ...) */
    enum etna_surface_layout layout;
+   uint64_t modifier;
    /* Horizontal alignment for texture unit (TEXTURE_HALIGN_*) */
    unsigned halign;
    struct etna_bo *bo; /* Surface video memory */
    struct etna_bo *ts_bo; /* Tile status video memory */
+   struct renderonly_scanout *ts_scanout; /* display compatible TS */
+   struct etna_ts_sw_meta *ts_meta; /* metadata for shared TS */
 
    struct etna_resource_level levels[ETNA_NUM_LOD];
 
@@ -142,6 +158,13 @@ etna_resource_hw_tileable(bool use_blt, const struct pipe_resource *pres)
           util_format_get_blocksize(pres->format) == 4;
 }
 
+/* returns TRUE if resource TS buffer is exposed externally */
+static inline bool
+etna_resource_ext_ts(const struct etna_resource *res)
+{
+   return res->modifier & VIVANTE_MOD_TS_MASK;
+}
+
 static inline struct etna_resource *
 etna_resource(struct pipe_resource *p)
 {
@@ -173,7 +196,8 @@ etna_resource_status(struct etna_context *ctx, struct etna_resource *res);
  * This is also called "fast clear". */
 bool
 etna_screen_resource_alloc_ts(struct pipe_screen *pscreen,
-                              struct etna_resource *prsc);
+                              struct etna_resource *prsc,
+                              uint64_t modifier);
 
 struct pipe_resource *
 etna_resource_alloc(struct pipe_screen *pscreen, unsigned layout,
index 52c7b36..e49279c 100644 (file)
@@ -340,6 +340,11 @@ etna_blit_clear_color_rs(struct pipe_context *pctx, struct pipe_surface *dst,
          ctx->framebuffer.TS_MEM_CONFIG |= VIVS_TS_MEM_CONFIG_COLOR_AUTO_DISABLE;
       }
 
+      /* update clear color in SW meta area of the buffer if TS is exported */
+      if (unlikely(new_clear_value != surf->level->clear_value &&
+          etna_resource_ext_ts(etna_resource(dst->texture))))
+         etna_resource(dst->texture)->ts_meta->v0.clear_value = new_clear_value;
+
       surf->level->ts_valid = true;
       ctx->dirty |= ETNA_DIRTY_TS | ETNA_DIRTY_DERIVE_TS;
    } else if (unlikely(new_clear_value != surf->level->clear_value)) { /* Queue normal RS clear for non-TS surfaces */
index 4eaed98..dadd57d 100644 (file)
@@ -643,22 +643,66 @@ etna_screen_query_dmabuf_modifiers(struct pipe_screen *pscreen,
                                    unsigned int *external_only, int *count)
 {
    struct etna_screen *screen = etna_screen(pscreen);
-   int num_modifiers = etna_get_num_modifiers(screen);
-   int i;
+   int num_base_mods = etna_get_num_modifiers(screen);
+   int mods_multiplier = 1;
+   int i, j;
+
+   if (VIV_FEATURE(screen, chipFeatures, FAST_CLEAR)) {
+      /* If TS is supported expose the TS modifiers. GPUs with feature
+       * CACHE128B256BPERLINE have both 128B and 256B color tile TS modes,
+       * older cores support exactly one TS layout.
+       */
+      if (VIV_FEATURE(screen, chipMinorFeatures6, CACHE128B256BPERLINE))
+         if (screen->specs.v4_compression &&
+             translate_ts_format(format) != ETNA_NO_MATCH)
+            mods_multiplier += 4;
+         else
+            mods_multiplier += 2;
+      else
+         mods_multiplier += 1;
+   }
 
-   if (max > num_modifiers)
-      max = num_modifiers;
+   if (max > num_base_mods * mods_multiplier)
+      max = num_base_mods * mods_multiplier;
 
    if (!max) {
       modifiers = NULL;
-      max = num_modifiers;
+      max = num_base_mods * mods_multiplier;
    }
 
-   for (i = 0, *count = 0; *count < max && i < num_modifiers; i++, (*count)++) {
-      if (modifiers)
-         modifiers[*count] = supported_modifiers[i];
-      if (external_only)
-         external_only[*count] = util_format_is_yuv(format) ? 1 : 0;
+   for (i = 0, *count = 0; *count < max && i < num_base_mods; i++) {
+      for (j = 0; *count < max && j < mods_multiplier; j++, (*count)++) {
+         uint64_t ts_mod;
+
+         if (j == 0) {
+            ts_mod = 0;
+         } else if (VIV_FEATURE(screen, chipMinorFeatures6,
+                                CACHE128B256BPERLINE)) {
+            switch (j) {
+            case 1:
+               ts_mod = VIVANTE_MOD_TS_128_4;
+               break;
+            case 2:
+               ts_mod = VIVANTE_MOD_TS_256_4;
+               break;
+            case 3:
+               ts_mod = VIVANTE_MOD_TS_128_4 | VIVANTE_MOD_COMP_DEC400;
+               break;
+            case 4:
+               ts_mod = VIVANTE_MOD_TS_256_4 | VIVANTE_MOD_COMP_DEC400;
+            }
+         } else {
+            if (screen->specs.bits_per_tile == 2)
+               ts_mod = VIVANTE_MOD_TS_64_2;
+            else
+               ts_mod = VIVANTE_MOD_TS_64_4;
+         }
+
+         if (modifiers)
+            modifiers[*count] = supported_modifiers[i] | ts_mod;
+         if (external_only)
+            external_only[*count] = util_format_is_yuv(format) ? 1 : 0;
+      }
    }
 }
 
@@ -669,13 +713,36 @@ etna_screen_is_dmabuf_modifier_supported(struct pipe_screen *pscreen,
                                          bool *external_only)
 {
    struct etna_screen *screen = etna_screen(pscreen);
-   int num_modifiers = etna_get_num_modifiers(screen);
+   int num_base_mods = etna_get_num_modifiers(screen);
+   uint64_t base_mod = modifier & ~VIVANTE_MOD_EXT_MASK;
+   uint64_t ts_mod = modifier & VIVANTE_MOD_TS_MASK;
    int i;
 
-   for (i = 0; i < num_modifiers; i++) {
-      if (modifier != supported_modifiers[i])
+   for (i = 0; i < num_base_mods; i++) {
+      if (base_mod != supported_modifiers[i])
          continue;
 
+      if ((modifier & VIVANTE_MOD_COMP_DEC400) &&
+          (!screen->specs.v4_compression || translate_ts_format(format) == ETNA_NO_MATCH))
+         return false;
+
+      if (ts_mod) {
+         if (!VIV_FEATURE(screen, chipFeatures, FAST_CLEAR))
+            return false;
+
+         if (VIV_FEATURE(screen, chipMinorFeatures6, CACHE128B256BPERLINE)) {
+            if (ts_mod != VIVANTE_MOD_TS_128_4 &&
+                ts_mod != VIVANTE_MOD_TS_256_4)
+               return false;
+         } else {
+            if ((screen->specs.bits_per_tile == 2 &&
+                 ts_mod != VIVANTE_MOD_TS_64_2) ||
+                (screen->specs.bits_per_tile == 4 &&
+                 ts_mod != VIVANTE_MOD_TS_64_4))
+               return false;
+         }
+      }
+
       if (external_only)
          *external_only = util_format_is_yuv(format) ? 1 : 0;
 
@@ -685,6 +752,19 @@ etna_screen_is_dmabuf_modifier_supported(struct pipe_screen *pscreen,
    return false;
 }
 
+static unsigned int
+etna_screen_get_dmabuf_modifier_planes(struct pipe_screen *pscreen,
+                                       uint64_t modifier,
+                                       enum pipe_format format)
+{
+   unsigned planes = util_format_get_num_planes(format);
+
+   if (modifier & VIVANTE_MOD_TS_MASK)
+      return planes * 2;
+
+   return planes;
+}
+
 static void
 etna_determine_uniform_limits(struct etna_screen *screen)
 {
@@ -1141,6 +1221,7 @@ etna_screen_create(struct etna_device *dev, struct etna_gpu *gpu,
    pscreen->is_format_supported = etna_screen_is_format_supported;
    pscreen->query_dmabuf_modifiers = etna_screen_query_dmabuf_modifiers;
    pscreen->is_dmabuf_modifier_supported = etna_screen_is_dmabuf_modifier_supported;
+   pscreen->get_dmabuf_modifier_planes = etna_screen_get_dmabuf_modifier_planes;
 
    if (!etna_shader_screen_init(pscreen))
       goto fail;
index a705ef3..89cae6a 100644 (file)
@@ -119,7 +119,7 @@ etna_create_surface(struct pipe_context *pctx, struct pipe_resource *prsc,
        /* Multi-layer resources would need to keep much more state (TS valid and
         * clear color per layer) and are unlikely to profit from TS usage. */
        prsc->depth0 == 1 && prsc->array_size == 1) {
-      etna_screen_resource_alloc_ts(pctx->screen, rsc);
+      etna_screen_resource_alloc_ts(pctx->screen, rsc, 0);
    }
 
    surf->base.format = templat->format;
index 3120364..27557bc 100644 (file)
@@ -231,6 +231,7 @@ struct etna_resource *
 etna_texture_handle_incompatible(struct pipe_context *pctx, struct pipe_resource *prsc)
 {
    struct etna_resource *res = etna_resource(prsc);
+
    if (!etna_resource_sampler_compatible(res)) {
       /* The original resource is not compatible with the sampler.
        * Allocate an appropriately tiled texture. */
index 0aa19e5..aea4070 100644 (file)
@@ -333,6 +333,44 @@ translate_blt_format(enum pipe_format fmt)
    }
 }
 
+static inline uint32_t
+drmfourcc_to_ts_format(uint32_t fourcc)
+{
+   switch (fourcc) {
+   case DRM_FORMAT_ARGB8888:
+      return COMPRESSION_FORMAT_A8R8G8B8;
+   case DRM_FORMAT_XRGB8888:
+      return COMPRESSION_FORMAT_X8R8G8B8;
+   case DRM_FORMAT_RGB565:
+      return COMPRESSION_FORMAT_R5G6B5;
+   case DRM_FORMAT_ARGB4444:
+      return COMPRESSION_FORMAT_A4R4G4B4;
+   case DRM_FORMAT_ARGB1555:
+      return COMPRESSION_FORMAT_A1R5G5B5;
+   default:
+      return ~0;
+   }
+}
+
+static inline uint32_t
+ts_format_to_drmfourcc(uint32_t comp_format)
+{
+   switch (comp_format) {
+   case COMPRESSION_FORMAT_A8R8G8B8:
+      return DRM_FORMAT_ARGB8888;
+   case COMPRESSION_FORMAT_X8R8G8B8:
+      return DRM_FORMAT_XRGB8888;
+   case COMPRESSION_FORMAT_R5G6B5:
+      return DRM_FORMAT_RGB565;
+   case COMPRESSION_FORMAT_A4R4G4B4:
+      return DRM_FORMAT_ARGB4444;
+   case COMPRESSION_FORMAT_A1R5G5B5:
+      return DRM_FORMAT_ARGB1555;
+   default:
+      return 0;
+   }
+}
+
 /* Return normalization flag for vertex element format */
 static inline uint32_t
 translate_vertex_format_normalize(enum pipe_format fmt)