st/va: implement vlVa(Query|Create|Get|Put|Destroy)Image
authorLeo Liu <leo.liu@amd.com>
Thu, 18 Sep 2014 16:21:58 +0000 (12:21 -0400)
committerLeo Liu <leo.liu@amd.com>
Wed, 1 Oct 2014 17:21:36 +0000 (13:21 -0400)
This patch implements functions for images support,
which basically supports copy data between video
surface and user buffers, in this case supports
SW decode, and other video output

v2: fix buffer size for odd-sized image case
    expose I420 format as well
v3: fix YUV 4:2:2 format data buffer size
    cleanup I420 format  exposure

Signed-off-by: Leo Liu <leo.liu@amd.com>
src/gallium/state_trackers/va/context.c
src/gallium/state_trackers/va/image.c
src/gallium/state_trackers/va/va_private.h

index 1819ec562891ccd138e1dd4957a9b663a3a08b1f..ae87d3b2b153d6cc7e3ece26f4f628996758d94d 100644 (file)
@@ -121,7 +121,7 @@ VA_DRIVER_INIT_FUNC(VADriverContextP ctx)
    ctx->max_profiles = PIPE_VIDEO_PROFILE_MPEG4_AVC_HIGH - PIPE_VIDEO_PROFILE_UNKNOWN;
    ctx->max_entrypoints = 1;
    ctx->max_attributes = 1;
-   ctx->max_image_formats = 1;
+   ctx->max_image_formats = VL_VA_MAX_IMAGE_FORMATS;
    ctx->max_subpic_formats = 1;
    ctx->max_display_attributes = 1;
    ctx->str_vendor = "mesa gallium vaapi";
index 8aaa29cee2d56bb0e2b35ec4beb04684485c3fbf..cd4044a1a7620010875b0089b49cb4ee4725af38 100644 (file)
  *
  **************************************************************************/
 
+#include "pipe/p_screen.h"
+
+#include "util/u_memory.h"
+#include "util/u_handle_table.h"
+#include "util/u_surface.h"
+#include "util/u_video.h"
+
+#include "vl/vl_winsys.h"
+
 #include "va_private.h"
 
+static const VAImageFormat formats[VL_VA_MAX_IMAGE_FORMATS] =
+{
+   {VA_FOURCC('N','V','1','2')},
+   {VA_FOURCC('I','4','2','0')},
+   {VA_FOURCC('Y','V','1','2')},
+   {VA_FOURCC('Y','U','Y','V')},
+   {VA_FOURCC('U','Y','V','Y')},
+};
+
+static void
+vlVaVideoSurfaceSize(vlVaSurface *p_surf, int component,
+                     unsigned *width, unsigned *height)
+{
+   *width = p_surf->templat.width;
+   *height = p_surf->templat.height;
+
+   if (component > 0) {
+      if (p_surf->templat.chroma_format == PIPE_VIDEO_CHROMA_FORMAT_420) {
+         *width /= 2;
+         *height /= 2;
+      } else if (p_surf->templat.chroma_format == PIPE_VIDEO_CHROMA_FORMAT_422)
+         *width /= 2;
+   }
+   if (p_surf->templat.interlaced)
+      *height /= 2;
+}
+
 VAStatus
 vlVaQueryImageFormats(VADriverContextP ctx, VAImageFormat *format_list, int *num_formats)
 {
+   struct pipe_screen *pscreen;
+   enum pipe_format format;
+   int i;
+
    if (!ctx)
       return VA_STATUS_ERROR_INVALID_CONTEXT;
 
    if (!(format_list && num_formats))
-      return VA_STATUS_ERROR_UNKNOWN;
+      return VA_STATUS_ERROR_INVALID_PARAMETER;
 
    *num_formats = 0;
+   pscreen = VL_VA_PSCREEN(ctx);
+   for (i = 0; i < VL_VA_MAX_IMAGE_FORMATS; ++i) {
+      format = YCbCrToPipe(formats[i].fourcc);
+      if (pscreen->is_video_format_supported(pscreen, format,
+          PIPE_VIDEO_PROFILE_UNKNOWN,
+          PIPE_VIDEO_ENTRYPOINT_BITSTREAM))
+         format_list[(*num_formats)++] = formats[i];
+   }
 
    return VA_STATUS_SUCCESS;
 }
@@ -45,16 +93,61 @@ vlVaQueryImageFormats(VADriverContextP ctx, VAImageFormat *format_list, int *num
 VAStatus
 vlVaCreateImage(VADriverContextP ctx, VAImageFormat *format, int width, int height, VAImage *image)
 {
+   vlVaDriver *drv;
+   int w, h;
+
    if (!ctx)
       return VA_STATUS_ERROR_INVALID_CONTEXT;
 
-   if(!format)
-      return VA_STATUS_ERROR_UNKNOWN;
+   if (!(format && image && width && height))
+      return VA_STATUS_ERROR_INVALID_PARAMETER;
+
+   drv = VL_VA_DRIVER(ctx);
 
-   if (!(width && height))
+   image->image_id = handle_table_add(drv->htab, image);
+   image->format = *format;
+   image->width = width;
+   image->height = height;
+   w = align(width, 2);
+   h = align(width, 2);
+
+   switch (format->fourcc) {
+   case VA_FOURCC('N','V','1','2'):
+      image->num_planes = 2;
+      image->pitches[0] = w;
+      image->offsets[0] = 0;
+      image->pitches[1] = w;
+      image->offsets[1] = w * h;
+      image->data_size  = w * h * 3 / 2;
+      break;
+
+   case VA_FOURCC('I','4','2','0'):
+   case VA_FOURCC('Y','V','1','2'):
+      image->num_planes = 3;
+      image->pitches[0] = w;
+      image->offsets[0] = 0;
+      image->pitches[1] = w / 2;
+      image->offsets[1] = w * h;
+      image->pitches[2] = w / 2;
+      image->offsets[2] = w * h * 5 / 4;
+      image->data_size  = w * h * 3 / 2;
+      break;
+
+   case VA_FOURCC('U','Y','V','Y'):
+   case VA_FOURCC('Y','U','Y','V'):
+      image->num_planes = 1;
+      image->pitches[0] = w * 2;
+      image->offsets[0] = 0;
+      image->data_size  = w * h * 2;
+      break;
+
+   default:
       return VA_STATUS_ERROR_INVALID_IMAGE_FORMAT;
+   }
 
-   return VA_STATUS_ERROR_UNIMPLEMENTED;
+   return vlVaCreateBuffer(ctx, 0, VAImageBufferType,
+                           align(image->data_size, 16),
+                           1, NULL, &image->buf);
 }
 
 VAStatus
@@ -69,10 +162,16 @@ vlVaDeriveImage(VADriverContextP ctx, VASurfaceID surface, VAImage *image)
 VAStatus
 vlVaDestroyImage(VADriverContextP ctx, VAImageID image)
 {
+   VAImage  *vaimage;
+
    if (!ctx)
       return VA_STATUS_ERROR_INVALID_CONTEXT;
 
-   return VA_STATUS_ERROR_UNIMPLEMENTED;
+   vaimage = handle_table_get(VL_VA_DRIVER(ctx)->htab, image);
+   if (!vaimage)
+      return VA_STATUS_ERROR_INVALID_IMAGE;
+
+   return vlVaDestroyBuffer(ctx, vaimage->buf);
 }
 
 VAStatus
@@ -88,10 +187,93 @@ VAStatus
 vlVaGetImage(VADriverContextP ctx, VASurfaceID surface, int x, int y,
              unsigned int width, unsigned int height, VAImageID image)
 {
+   vlVaDriver *drv;
+   vlVaSurface *surf;
+   vlVaBuffer *img_buf;
+   VAImage *vaimage;
+   struct pipe_sampler_view **views;
+   enum pipe_format format;
+   bool convert = false;
+   void *data[3];
+   unsigned pitches[3], i, j;
+
    if (!ctx)
       return VA_STATUS_ERROR_INVALID_CONTEXT;
 
-   return VA_STATUS_ERROR_UNIMPLEMENTED;
+   drv = VL_VA_DRIVER(ctx);
+
+   surf = handle_table_get(drv->htab, surface);
+   if (!surf || !surf->buffer)
+      return VA_STATUS_ERROR_INVALID_SURFACE;
+
+   vaimage = handle_table_get(drv->htab, image);
+   if (!vaimage)
+      return VA_STATUS_ERROR_INVALID_IMAGE;
+
+   img_buf = handle_table_get(drv->htab, vaimage->buf);
+   if (!img_buf)
+      return VA_STATUS_ERROR_INVALID_BUFFER;
+
+   format = YCbCrToPipe(vaimage->format.fourcc);
+   if (format == PIPE_FORMAT_NONE)
+      return VA_STATUS_ERROR_OPERATION_FAILED;
+
+   if (format != surf->buffer->buffer_format) {
+      /* support NV12 to YV12 conversion now only */
+      if (format == PIPE_FORMAT_YV12 &&
+          surf->buffer->buffer_format == PIPE_FORMAT_NV12)
+         convert = true;
+      else
+         return VA_STATUS_ERROR_OPERATION_FAILED;
+   }
+
+   views = surf->buffer->get_sampler_view_planes(surf->buffer);
+   if (!views)
+      return VA_STATUS_ERROR_OPERATION_FAILED;
+
+   for (i = 0; i < vaimage->num_planes; i++) {
+      data[i] = img_buf->data + vaimage->offsets[i];
+      pitches[i] = vaimage->pitches[i];
+   }
+   if (vaimage->format.fourcc == VA_FOURCC('I','4','2','0')) {
+      void *tmp_d;
+      unsigned tmp_p;
+      tmp_d  = data[1];
+      data[1] = data[2];
+      data[2] = tmp_d;
+      tmp_p = pitches[1];
+      pitches[1] = pitches[2];
+      pitches[2] = tmp_p;
+   }
+
+   for (i = 0; i < vaimage->num_planes; i++) {
+      unsigned width, height;
+      if (!views[i]) continue;
+      vlVaVideoSurfaceSize(surf, i, &width, &height);
+      for (j = 0; j < views[i]->texture->array_size; ++j) {
+         struct pipe_box box = {0, 0, j, width, height, 1};
+         struct pipe_transfer *transfer;
+         uint8_t *map;
+         map = drv->pipe->transfer_map(drv->pipe, views[i]->texture, 0,
+                  PIPE_TRANSFER_READ, &box, &transfer);
+         if (!map)
+            return VA_STATUS_ERROR_OPERATION_FAILED;
+
+         if (i == 1 && convert) {
+            u_copy_nv12_to_yv12(data, pitches, i, j,
+               transfer->stride, views[i]->texture->array_size,
+               map, box.width, box.height);
+         } else {
+            util_copy_rect(data[i] + pitches[i] * j,
+               views[i]->texture->format,
+               pitches[i] * views[i]->texture->array_size, 0, 0,
+               box.width, box.height, map, transfer->stride, 0, 0);
+         }
+         pipe_transfer_unmap(drv->pipe, transfer);
+      }
+   }
+
+   return VA_STATUS_SUCCESS;
 }
 
 VAStatus
@@ -99,8 +281,76 @@ vlVaPutImage(VADriverContextP ctx, VASurfaceID surface, VAImageID image,
              int src_x, int src_y, unsigned int src_width, unsigned int src_height,
              int dest_x, int dest_y, unsigned int dest_width, unsigned int dest_height)
 {
+   vlVaDriver *drv;
+   vlVaSurface *surf;
+   vlVaBuffer *img_buf;
+   VAImage *vaimage;
+   struct pipe_sampler_view **views;
+   enum pipe_format format;
+   void *data[3];
+   unsigned pitches[3], i, j;
+
    if (!ctx)
       return VA_STATUS_ERROR_INVALID_CONTEXT;
 
-   return VA_STATUS_ERROR_UNIMPLEMENTED;
+   drv = VL_VA_DRIVER(ctx);
+
+   surf = handle_table_get(drv->htab, surface);
+   if (!surf || !surf->buffer)
+      return VA_STATUS_ERROR_INVALID_SURFACE;
+
+   vaimage = handle_table_get(drv->htab, image);
+   if (!vaimage)
+      return VA_STATUS_ERROR_INVALID_IMAGE;
+
+   img_buf = handle_table_get(drv->htab, vaimage->buf);
+   if (!img_buf)
+      return VA_STATUS_ERROR_INVALID_BUFFER;
+
+   format = YCbCrToPipe(vaimage->format.fourcc);
+   if (format == PIPE_FORMAT_NONE)
+      return VA_STATUS_ERROR_OPERATION_FAILED;
+
+   if (surf->buffer == NULL || format != surf->buffer->buffer_format) {
+      if (surf->buffer)
+         surf->buffer->destroy(surf->buffer);
+      surf->templat.buffer_format = format;
+      surf->buffer = drv->pipe->create_video_buffer(drv->pipe, &surf->templat);
+      if (!surf->buffer)
+         return VA_STATUS_ERROR_ALLOCATION_FAILED;
+   }
+
+   views = surf->buffer->get_sampler_view_planes(surf->buffer);
+   if (!views)
+      return VA_STATUS_ERROR_OPERATION_FAILED;
+
+   for (i = 0; i < vaimage->num_planes; i++) {
+      data[i] = img_buf->data + vaimage->offsets[i];
+      pitches[i] = vaimage->pitches[i];
+   }
+   if (vaimage->format.fourcc == VA_FOURCC('I','4','2','0')) {
+      void *tmp_d;
+      unsigned tmp_p;
+      tmp_d  = data[1];
+      data[1] = data[2];
+      data[2] = tmp_d;
+      tmp_p = pitches[1];
+      pitches[1] = pitches[2];
+      pitches[2] = tmp_p;
+   }
+
+   for (i = 0; i < vaimage->num_planes; ++i) {
+      unsigned width, height;
+      if (!views[i]) continue;
+      vlVaVideoSurfaceSize(surf, i, &width, &height);
+      for (j = 0; j < views[i]->texture->array_size; ++j) {
+         struct pipe_box dst_box = {0, 0, j, width, height, 1};
+         drv->pipe->transfer_inline_write(drv->pipe, views[i]->texture, 0,
+            PIPE_TRANSFER_WRITE, &dst_box,
+            data[i] + pitches[i] * j,
+            pitches[i] * views[i]->texture->array_size, 0);
+      }
+   }
+
+   return VA_STATUS_SUCCESS;
 }
index 34e1f3ed661c2b4386d5e595ac5c5eeb9d078302..060a1faeb9cdff8a661b828ba42302a3ad8054b2 100644 (file)
@@ -44,6 +44,8 @@
 #define VL_VA_DRIVER(ctx) ((vlVaDriver *)ctx->pDriverData)
 #define VL_VA_PSCREEN(ctx) (VL_VA_DRIVER(ctx)->vscreen->pscreen)
 
+#define VL_VA_MAX_IMAGE_FORMATS 5
+
 static inline enum pipe_video_chroma_format
 ChromaToPipe(int format)
 {
@@ -60,6 +62,26 @@ ChromaToPipe(int format)
    }
 }
 
+static inline enum pipe_format
+YCbCrToPipe(unsigned format)
+{
+   switch(format) {
+   case VA_FOURCC('N','V','1','2'):
+      return PIPE_FORMAT_NV12;
+   case VA_FOURCC('I','4','2','0'):
+      return PIPE_FORMAT_IYUV;
+   case VA_FOURCC('Y','V','1','2'):
+      return PIPE_FORMAT_YV12;
+   case VA_FOURCC('Y','U','Y','V'):
+      return PIPE_FORMAT_YUYV;
+   case VA_FOURCC('U','Y','V','Y'):
+      return PIPE_FORMAT_UYVY;
+   default:
+      assert(0);
+      return PIPE_FORMAT_NONE;
+   }
+}
+
 static inline VAProfile
 PipeToProfile(enum pipe_video_profile profile)
 {