virgl: add support for hardware video acceleration
authorFeng Jiang <jiangfeng@kylinos.cn>
Mon, 20 Jun 2022 07:10:58 +0000 (15:10 +0800)
committerMarge Bot <emma+marge@anholt.net>
Thu, 11 Aug 2022 10:21:03 +0000 (10:21 +0000)
Currently H.264 and H.265 decoding is supported, and other profiles
and encodings will be added in the future.

This function relies on the virglrenderer commit:
a92fed821f5ac173ca82c011a5d7e5b25e7bd0eb

Signed-off-by: Feng Jiang <jiangfeng@kylinos.cn>
Signed-off-by: Ming Xie <xieming@kylinos.cn>
Signed-off-by: Liming Sun <sunliming@kylinos.cn>
Reviewed-by: Gert Wollny <gert.wollny@collabora.com>
Reviewed-by: Emil Velikov <emil.velikov@collabora.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/17157>

12 files changed:
src/gallium/drivers/virgl/meson.build
src/gallium/drivers/virgl/virgl_context.c
src/gallium/drivers/virgl/virgl_encode.c
src/gallium/drivers/virgl/virgl_encode.h
src/gallium/drivers/virgl/virgl_screen.c
src/gallium/drivers/virgl/virgl_screen.h
src/gallium/drivers/virgl/virgl_video.c [new file with mode: 0644]
src/gallium/drivers/virgl/virgl_video.h [new file with mode: 0644]
src/gallium/targets/va/meson.build
src/gallium/targets/vdpau/meson.build
src/virtio/virtio-gpu/virgl_hw.h
src/virtio/virtio-gpu/virgl_protocol.h

index b3adb12..0bb26c6 100644 (file)
@@ -30,6 +30,7 @@ files_libvirgl = files(
   'virgl_transfer_queue.c',
   'virgl_texture.c',
   'virgl_tgsi.c',
+  'virgl_video.c',
 )
 
 libvirgl = static_library(
index 771c204..ad1ed87 100644 (file)
@@ -49,6 +49,7 @@
 #include "virgl_resource.h"
 #include "virgl_screen.h"
 #include "virgl_staging_mgr.h"
+#include "virgl_video.h"
 
 struct virgl_vertex_elements_state {
    uint32_t handle;
@@ -859,6 +860,17 @@ static void virgl_clear(struct pipe_context *ctx,
    virgl_encode_clear(vctx, buffers, color, depth, stencil);
 }
 
+static void virgl_clear_render_target(struct pipe_context *ctx,
+                                      struct pipe_surface *dst,
+                                      const union pipe_color_union *color,
+                                      unsigned dstx, unsigned dsty,
+                                      unsigned width, unsigned height,
+                                      bool render_condition_enabled)
+{
+   if (virgl_debug & VIRGL_DEBUG_VERBOSE)
+      debug_printf("VIRGL: clear render target unsupported.\n");
+}
+
 static void virgl_clear_texture(struct pipe_context *ctx,
                                 struct pipe_resource *res,
                                 unsigned int level,
@@ -1624,6 +1636,7 @@ struct pipe_context *virgl_context_create(struct pipe_screen *pscreen,
    vctx->base.launch_grid = virgl_launch_grid;
 
    vctx->base.clear = virgl_clear;
+   vctx->base.clear_render_target = virgl_clear_render_target;
    vctx->base.clear_texture = virgl_clear_texture;
    vctx->base.draw_vbo = virgl_draw_vbo;
    vctx->base.flush = virgl_flush_from_st;
@@ -1660,6 +1673,9 @@ struct pipe_context *virgl_context_create(struct pipe_screen *pscreen,
    vctx->base.memory_barrier = virgl_memory_barrier;
    vctx->base.emit_string_marker = virgl_emit_string_marker;
 
+   vctx->base.create_video_codec = virgl_video_create_codec;
+   vctx->base.create_video_buffer = virgl_video_create_buffer;
+
    if (rs->caps.caps.v2.host_feature_check_version >= 7)
       vctx->base.link_shader = virgl_link_shader;
 
index b09bce5..3a3e212 100644 (file)
@@ -36,6 +36,7 @@
 #include "virtio-gpu/virgl_protocol.h"
 #include "virgl_resource.h"
 #include "virgl_screen.h"
+#include "virgl_video.h"
 
 #define VIRGL_ENCODE_MAX_DWORDS MIN2(VIRGL_MAX_CMDBUF_DWORDS, VIRGL_CMD0_MAX_DWORDS)
 
@@ -308,6 +309,18 @@ enum virgl_formats pipe_to_virgl_format(enum pipe_format format)
    return vformat;
 }
 
+enum pipe_format virgl_to_pipe_format(enum virgl_formats format)
+{
+   enum pipe_format pformat;
+
+   for (pformat = PIPE_FORMAT_NONE; pformat < PIPE_FORMAT_COUNT; pformat++)
+      if (virgl_formats_conv_table[pformat] == format)
+          return pformat;
+
+   debug_printf("VIRGL: virgl format %u not in the format table\n", format);
+   return PIPE_FORMAT_NONE;
+}
+
 static int virgl_encoder_write_cmd_dword(struct virgl_context *ctx,
                                         uint32_t dword)
 {
@@ -1613,3 +1626,76 @@ void virgl_encode_emit_string_marker(struct virgl_context *ctx,
    virgl_encoder_write_dword(ctx->cbuf, len);
    virgl_encoder_write_block(ctx->cbuf, (const uint8_t *)message, len);
 }
+
+void virgl_encode_create_video_codec(struct virgl_context *ctx,
+                                     struct virgl_video_codec *cdc)
+{
+   virgl_encoder_write_cmd_dword(ctx, VIRGL_CMD0(VIRGL_CCMD_CREATE_VIDEO_CODEC, 0, 7));
+   virgl_encoder_write_dword(ctx->cbuf, cdc->handle);
+   virgl_encoder_write_dword(ctx->cbuf, cdc->base.profile);
+   virgl_encoder_write_dword(ctx->cbuf, cdc->base.entrypoint);
+   virgl_encoder_write_dword(ctx->cbuf, cdc->base.chroma_format);
+   virgl_encoder_write_dword(ctx->cbuf, cdc->base.level);
+   virgl_encoder_write_dword(ctx->cbuf, cdc->base.width);
+   virgl_encoder_write_dword(ctx->cbuf, cdc->base.height);
+}
+
+void virgl_encode_destroy_video_codec(struct virgl_context *ctx,
+                                      struct virgl_video_codec *cdc)
+{
+   virgl_encoder_write_cmd_dword(ctx, VIRGL_CMD0(VIRGL_CCMD_DESTROY_VIDEO_CODEC, 0, 1));
+   virgl_encoder_write_dword(ctx->cbuf, cdc->handle);
+}
+
+void virgl_encode_create_video_buffer(struct virgl_context *ctx,
+                                      struct virgl_video_buffer *vbuf)
+{
+   unsigned i;
+
+   virgl_encoder_write_cmd_dword(ctx, VIRGL_CMD0(VIRGL_CCMD_CREATE_VIDEO_BUFFER, 0,
+                                                 4 + vbuf->num_planes));
+   virgl_encoder_write_dword(ctx->cbuf, vbuf->handle);
+   virgl_encoder_write_dword(ctx->cbuf, pipe_to_virgl_format(vbuf->buf->buffer_format));
+   virgl_encoder_write_dword(ctx->cbuf, vbuf->buf->width);
+   virgl_encoder_write_dword(ctx->cbuf, vbuf->buf->height);
+   for (i = 0; i < vbuf->num_planes; i++)
+       virgl_encoder_write_res(ctx, virgl_resource(vbuf->plane_views[i]->texture));
+}
+
+void virgl_encode_destroy_video_buffer(struct virgl_context *ctx,
+                                       struct virgl_video_buffer *buf)
+{
+   virgl_encoder_write_cmd_dword(ctx, VIRGL_CMD0(VIRGL_CCMD_DESTROY_VIDEO_BUFFER, 0, 1));
+   virgl_encoder_write_dword(ctx->cbuf, buf->handle);
+}
+
+void virgl_encode_begin_frame(struct virgl_context *ctx,
+                              struct virgl_video_codec *cdc,
+                              struct virgl_video_buffer *buf)
+{
+   virgl_encoder_write_cmd_dword(ctx, VIRGL_CMD0(VIRGL_CCMD_BEGIN_FRAME, 0, 2));
+   virgl_encoder_write_dword(ctx->cbuf, cdc->handle);
+   virgl_encoder_write_dword(ctx->cbuf, buf->handle);
+}
+
+void virgl_encode_decode_bitstream(struct virgl_context *ctx,
+                                   struct virgl_video_codec *cdc,
+                                   struct virgl_video_buffer *buf,
+                                   void *desc, uint32_t desc_size)
+{
+   virgl_encoder_write_cmd_dword(ctx, VIRGL_CMD0(VIRGL_CCMD_DECODE_BITSTREAM, 0, 5));
+   virgl_encoder_write_dword(ctx->cbuf, cdc->handle);
+   virgl_encoder_write_dword(ctx->cbuf, buf->handle);
+   virgl_encoder_write_res(ctx, virgl_resource(cdc->desc_buffers[cdc->cur_buffer]));
+   virgl_encoder_write_res(ctx, virgl_resource(cdc->bs_buffers[cdc->cur_buffer]));
+   virgl_encoder_write_dword(ctx->cbuf, cdc->bs_size);
+}
+
+void virgl_encode_end_frame(struct virgl_context *ctx,
+                            struct virgl_video_codec *cdc,
+                            struct virgl_video_buffer *buf)
+{
+   virgl_encoder_write_cmd_dword(ctx, VIRGL_CMD0(VIRGL_CCMD_END_FRAME, 0, 2));
+   virgl_encoder_write_dword(ctx->cbuf, cdc->handle);
+   virgl_encoder_write_dword(ctx->cbuf, buf->handle);
+}
index dd11690..90fe8d1 100644 (file)
@@ -36,6 +36,8 @@ struct virgl_resource;
 struct virgl_screen;
 struct virgl_transfer;
 struct virgl_sampler_view;
+struct virgl_video_codec;
+struct virgl_video_buffer;
 
 struct virgl_surface {
    struct pipe_surface base;
@@ -317,5 +319,31 @@ void virgl_encode_get_memory_info(struct virgl_context *ctx, struct virgl_resour
 void virgl_encode_emit_string_marker(struct virgl_context *ctx, const char *message,
                                        int len);
 
+void virgl_encode_create_video_codec(struct virgl_context *ctx,
+                                     struct virgl_video_codec *cdc);
+
+void virgl_encode_destroy_video_codec(struct virgl_context *ctx,
+                                      struct virgl_video_codec *cdc);
+
+void virgl_encode_create_video_buffer(struct virgl_context *ctx,
+                                      struct virgl_video_buffer *buf);
+
+void virgl_encode_destroy_video_buffer(struct virgl_context *ctx,
+                                       struct virgl_video_buffer *buf);
+
+void virgl_encode_begin_frame(struct virgl_context *ctx,
+                              struct virgl_video_codec *cdc,
+                              struct virgl_video_buffer *buf);
+
+void virgl_encode_decode_bitstream(struct virgl_context *ctx,
+                                   struct virgl_video_codec *cdc,
+                                   struct virgl_video_buffer *buf,
+                                   void *desc, uint32_t desc_size);
+
+void virgl_encode_end_frame(struct virgl_context *ctx,
+                            struct virgl_video_codec *cdc,
+                            struct virgl_video_buffer *buf);
+
 enum virgl_formats pipe_to_virgl_format(enum pipe_format format);
+enum pipe_format virgl_to_pipe_format(enum virgl_formats format);
 #endif
index 63026cc..6e80580 100644 (file)
@@ -32,6 +32,8 @@
 #include "pipe/p_defines.h"
 #include "pipe/p_screen.h"
 #include "nir/nir_to_tgsi.h"
+#include "vl/vl_decoder.h"
+#include "vl/vl_video_buffer.h"
 
 #include "tgsi/tgsi_exec.h"
 
@@ -53,6 +55,7 @@ static const struct debug_named_value virgl_debug_options[] = {
    { "xfer",      VIRGL_DEBUG_XFER,                "Do not optimize for transfers" },
    { "r8srgb-readback",   VIRGL_DEBUG_L8_SRGB_ENABLE_READBACK, "Enable redaback for L8 sRGB textures" },
    { "nocoherent", VIRGL_DEBUG_NO_COHERENT,        "Disable coherent memory"},
+   { "video",     VIRGL_DEBUG_VIDEO,               "Video codec"},
    DEBUG_NAMED_VALUE_END
 };
 DEBUG_GET_ONCE_FLAGS_OPTION(virgl_debug, "VIRGL_DEBUG", virgl_debug_options, 0)
@@ -457,6 +460,72 @@ virgl_get_shader_param(struct pipe_screen *screen,
    }
 }
 
+static int
+virgl_get_video_param(struct pipe_screen *screen,
+                      enum pipe_video_profile profile,
+                      enum pipe_video_entrypoint entrypoint,
+                      enum pipe_video_cap param)
+{
+   unsigned i;
+   struct virgl_video_caps *vcaps = NULL;
+   struct virgl_screen *vscreen;
+
+   if (!screen)
+       return 0;
+
+   vscreen = virgl_screen(screen);
+   if (vscreen->caps.caps.v2.num_video_caps > ARRAY_SIZE(vscreen->caps.caps.v2.video_caps))
+       return 0;
+
+   for (i = 0;  i < vscreen->caps.caps.v2.num_video_caps; i++) {
+       if (vscreen->caps.caps.v2.video_caps[i].profile == profile &&
+           vscreen->caps.caps.v2.video_caps[i].entrypoint == entrypoint) {
+           vcaps = &vscreen->caps.caps.v2.video_caps[i];
+           break;
+       }
+   }
+
+   /*
+    * Since there are calls like this:
+    *   pot_buffers = !pipe->screen->get_video_param
+    *   (
+    *      pipe->screen,
+    *      PIPE_VIDEO_PROFILE_UNKNOWN,
+    *      PIPE_VIDEO_ENTRYPOINT_UNKNOWN,
+    *      PIPE_VIDEO_CAP_NPOT_TEXTURES
+    *   );
+    * All parameters need to check the vcaps.
+    */
+   switch (param) {
+      case PIPE_VIDEO_CAP_SUPPORTED:
+         return vcaps != NULL;
+      case PIPE_VIDEO_CAP_NPOT_TEXTURES:
+         return vcaps ? vcaps->npot_texture : true;
+      case PIPE_VIDEO_CAP_MAX_WIDTH:
+         return vcaps ? vcaps->max_width : 0;
+      case PIPE_VIDEO_CAP_MAX_HEIGHT:
+         return vcaps ? vcaps->max_height : 0;
+      case PIPE_VIDEO_CAP_PREFERED_FORMAT:
+         return vcaps ? virgl_to_pipe_format(vcaps->prefered_format) : PIPE_FORMAT_NV12;
+      case PIPE_VIDEO_CAP_PREFERS_INTERLACED:
+         return vcaps ? vcaps->prefers_interlaced : false;
+      case PIPE_VIDEO_CAP_SUPPORTS_INTERLACED:
+         return vcaps ? vcaps->supports_interlaced : false;
+      case PIPE_VIDEO_CAP_SUPPORTS_PROGRESSIVE:
+         return vcaps ? vcaps->supports_progressive : true;
+      case PIPE_VIDEO_CAP_MAX_LEVEL:
+         return vcaps ? vcaps->max_level : 0;
+      case PIPE_VIDEO_CAP_STACKED_FRAMES:
+         return vcaps ? vcaps->stacked_frames : 0;
+      case PIPE_VIDEO_CAP_MAX_MACROBLOCKS:
+         return vcaps ? vcaps->max_macroblocks : 0;
+      case PIPE_VIDEO_CAP_MAX_TEMPORAL_LAYERS:
+         return vcaps ? vcaps->max_temporal_layers : 0;
+      default:
+         return 0;
+   }
+}
+
 static float
 virgl_get_paramf(struct pipe_screen *screen, enum pipe_capf param)
 {
@@ -804,6 +873,15 @@ virgl_is_format_supported( struct pipe_screen *screen,
                                      may_emulate_bgra);
 }
 
+static bool virgl_is_video_format_supported(struct pipe_screen *screen,
+                                            enum pipe_format format,
+                                            enum pipe_video_profile profile,
+                                            enum pipe_video_entrypoint entrypoint)
+{
+    return vl_video_buffer_is_format_supported(screen, format, profile, entrypoint);
+}
+
+
 static void virgl_flush_frontbuffer(struct pipe_screen *screen,
                                     struct pipe_context *ctx,
                                       struct pipe_resource *res,
@@ -1055,10 +1133,12 @@ virgl_create_screen(struct virgl_winsys *vws, const struct pipe_screen_config *c
    screen->base.get_vendor = virgl_get_vendor;
    screen->base.get_param = virgl_get_param;
    screen->base.get_shader_param = virgl_get_shader_param;
+   screen->base.get_video_param = virgl_get_video_param;
    screen->base.get_compute_param = virgl_get_compute_param;
    screen->base.get_paramf = virgl_get_paramf;
    screen->base.get_compiler_options = virgl_get_compiler_options;
    screen->base.is_format_supported = virgl_is_format_supported;
+   screen->base.is_video_format_supported = virgl_is_video_format_supported;
    screen->base.destroy = virgl_destroy_screen;
    screen->base.context_create = virgl_context_create;
    screen->base.flush_frontbuffer = virgl_flush_frontbuffer;
index cbebb8d..89d8361 100644 (file)
@@ -39,6 +39,7 @@ enum virgl_debug_flags {
    VIRGL_DEBUG_NO_COHERENT          = 1 << 6,
    VIRGL_DEBUG_USE_TGSI             = 1 << 7,
    VIRGL_DEBUG_L8_SRGB_ENABLE_READBACK = 1 << 8,
+   VIRGL_DEBUG_VIDEO                = 1 << 9,
 };
 
 extern int virgl_debug;
diff --git a/src/gallium/drivers/virgl/virgl_video.c b/src/gallium/drivers/virgl/virgl_video.c
new file mode 100644 (file)
index 0000000..dc668db
--- /dev/null
@@ -0,0 +1,567 @@
+/*
+ * Copyright 2022 Kylin Software Co., Ltd.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * on the rights to use, copy, modify, merge, publish, distribute, sub
+ * license, and/or sell copies of the Software, and to permit persons to whom
+ * the Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include <string.h>
+#include <sys/param.h>
+
+#include "vl/vl_decoder.h"
+#include "vl/vl_video_buffer.h"
+#include "util/u_video.h"
+#include "util/u_memory.h"
+
+#include "virgl_screen.h"
+#include "virgl_resource.h"
+#include "virgl_encode.h"
+#include "virgl_video.h"
+
+/*
+ * The max size of bs buffer is approximately:
+ *   num_of_macroblocks * max_size_of_per_macroblock + size_of_some_headers
+ * Now, we only support YUV420 formats, this means that we have a limit of
+ * 3200 bits(400 Bytes) per macroblock. To simplify the calculation, we
+ * directly use 512 instead of 400.
+ */
+#define BS_BUF_DEFAULT_SIZE(width, height) \
+    ((width) * (height) / (VL_MACROBLOCK_WIDTH * VL_MACROBLOCK_HEIGHT) * 512)
+
+static void switch_buffer(struct virgl_video_codec *vcdc)
+{
+    vcdc->cur_buffer++;
+    vcdc->cur_buffer %= VIRGL_VIDEO_CODEC_BUF_NUM;
+}
+
+#define ITEM_SET(dest, src, item)   (dest)->item = (src)->item
+#define ITEM_CPY(dest, src, item)   memcpy(&(dest)->item, &(src)->item, sizeof((dest)->item))
+
+static int fill_base_picture_desc(const struct pipe_picture_desc *desc,
+                                  struct virgl_base_picture_desc *vbase)
+{
+    ITEM_SET(vbase, desc, profile);
+    ITEM_SET(vbase, desc, entry_point);
+    ITEM_SET(vbase, desc, protected_playback);
+    ITEM_SET(vbase, desc, key_size);
+    memcpy(vbase->decrypt_key, desc->decrypt_key,
+           MIN(desc->key_size, sizeof(vbase->decrypt_key)));
+
+    return 0;
+}
+
+static int fill_h264_picture_desc(const struct pipe_picture_desc *desc,
+                                  union virgl_picture_desc *vdsc)
+{
+    unsigned i;
+    struct virgl_video_buffer *vbuf;
+
+    struct virgl_h264_picture_desc *vh264 = &vdsc->h264;
+    struct virgl_h264_pps *vpps = &vh264->pps;
+    struct virgl_h264_sps *vsps = &vh264->pps.sps;
+
+    struct pipe_h264_picture_desc *h264 = (struct pipe_h264_picture_desc *)desc;
+    struct pipe_h264_pps *pps = h264->pps;
+    struct pipe_h264_sps *sps = h264->pps->sps;
+
+    fill_base_picture_desc(desc, &vh264->base);
+
+    ITEM_SET(vsps, sps, level_idc);
+    ITEM_SET(vsps, sps, chroma_format_idc);
+    ITEM_SET(vsps, sps, separate_colour_plane_flag);
+    ITEM_SET(vsps, sps, bit_depth_luma_minus8);
+    ITEM_SET(vsps, sps, bit_depth_chroma_minus8);
+    ITEM_SET(vsps, sps, seq_scaling_matrix_present_flag);
+    ITEM_CPY(vsps, sps, ScalingList4x4);
+    ITEM_CPY(vsps, sps, ScalingList8x8);
+    ITEM_SET(vsps, sps, log2_max_frame_num_minus4);
+    ITEM_SET(vsps, sps, pic_order_cnt_type);
+    ITEM_SET(vsps, sps, log2_max_pic_order_cnt_lsb_minus4);
+    ITEM_SET(vsps, sps, delta_pic_order_always_zero_flag);
+    ITEM_SET(vsps, sps, offset_for_non_ref_pic);
+    ITEM_SET(vsps, sps, offset_for_top_to_bottom_field);
+    ITEM_CPY(vsps, sps, offset_for_ref_frame);
+    ITEM_SET(vsps, sps, num_ref_frames_in_pic_order_cnt_cycle);
+    ITEM_SET(vsps, sps, max_num_ref_frames);
+    ITEM_SET(vsps, sps, frame_mbs_only_flag);
+    ITEM_SET(vsps, sps, mb_adaptive_frame_field_flag);
+    ITEM_SET(vsps, sps, direct_8x8_inference_flag);
+    ITEM_SET(vsps, sps, MinLumaBiPredSize8x8);
+
+    ITEM_SET(vpps, pps, entropy_coding_mode_flag);
+    ITEM_SET(vpps, pps, bottom_field_pic_order_in_frame_present_flag);
+    ITEM_SET(vpps, pps, num_slice_groups_minus1);
+    ITEM_SET(vpps, pps, slice_group_map_type);
+    ITEM_SET(vpps, pps, slice_group_change_rate_minus1);
+    ITEM_SET(vpps, pps, num_ref_idx_l0_default_active_minus1);
+    ITEM_SET(vpps, pps, num_ref_idx_l1_default_active_minus1);
+    ITEM_SET(vpps, pps, weighted_pred_flag);
+    ITEM_SET(vpps, pps, weighted_bipred_idc);
+    ITEM_SET(vpps, pps, pic_init_qp_minus26);
+    ITEM_SET(vpps, pps, pic_init_qs_minus26);
+    ITEM_SET(vpps, pps, chroma_qp_index_offset);
+    ITEM_SET(vpps, pps, deblocking_filter_control_present_flag);
+    ITEM_SET(vpps, pps, constrained_intra_pred_flag);
+    ITEM_SET(vpps, pps, redundant_pic_cnt_present_flag);
+    ITEM_CPY(vpps, pps, ScalingList4x4);
+    ITEM_CPY(vpps, pps, ScalingList8x8);
+    ITEM_SET(vpps, pps, transform_8x8_mode_flag);
+    ITEM_SET(vpps, pps, second_chroma_qp_index_offset);
+
+    ITEM_SET(vh264, h264, frame_num);
+    ITEM_SET(vh264, h264, field_pic_flag);
+    ITEM_SET(vh264, h264, bottom_field_flag);
+    ITEM_SET(vh264, h264, num_ref_idx_l0_active_minus1);
+    ITEM_SET(vh264, h264, num_ref_idx_l1_active_minus1);
+    ITEM_SET(vh264, h264, slice_count);
+    ITEM_CPY(vh264, h264, field_order_cnt);
+    ITEM_SET(vh264, h264, is_reference);
+    ITEM_SET(vh264, h264, num_ref_frames);
+    ITEM_CPY(vh264, h264, field_order_cnt_list);
+    ITEM_CPY(vh264, h264, frame_num_list);
+
+    for (i = 0; i < 16; i++) {
+        ITEM_SET(vh264, h264, is_long_term[i]);
+        ITEM_SET(vh264, h264, top_is_reference[i]);
+        ITEM_SET(vh264, h264, bottom_is_reference[i]);
+
+        vbuf = virgl_video_buffer(h264->ref[i]);
+        vh264->buffer_id[i] = vbuf ? vbuf->handle : 0;
+    }
+
+    return 0;
+}
+
+static int fill_h265_picture_desc(const struct pipe_picture_desc *desc,
+                                  union virgl_picture_desc *vdsc)
+{
+    unsigned i;
+    struct virgl_video_buffer *vbuf;
+
+    struct virgl_h265_picture_desc *vh265 = &vdsc->h265;
+    struct pipe_h265_picture_desc *h265 = (struct pipe_h265_picture_desc *)desc;
+
+    fill_base_picture_desc(desc, &vh265->base);
+
+    ITEM_SET(&vh265->pps.sps, h265->pps->sps, chroma_format_idc);
+    ITEM_SET(&vh265->pps.sps, h265->pps->sps, separate_colour_plane_flag);
+    ITEM_SET(&vh265->pps.sps, h265->pps->sps, pic_width_in_luma_samples);
+    ITEM_SET(&vh265->pps.sps, h265->pps->sps, pic_height_in_luma_samples);
+    ITEM_SET(&vh265->pps.sps, h265->pps->sps, bit_depth_luma_minus8);
+    ITEM_SET(&vh265->pps.sps, h265->pps->sps, bit_depth_chroma_minus8);
+    ITEM_SET(&vh265->pps.sps, h265->pps->sps, log2_max_pic_order_cnt_lsb_minus4);
+    ITEM_SET(&vh265->pps.sps, h265->pps->sps, sps_max_dec_pic_buffering_minus1);
+    ITEM_SET(&vh265->pps.sps, h265->pps->sps, log2_min_luma_coding_block_size_minus3);
+    ITEM_SET(&vh265->pps.sps, h265->pps->sps, log2_diff_max_min_luma_coding_block_size);
+    ITEM_SET(&vh265->pps.sps, h265->pps->sps, log2_min_transform_block_size_minus2);
+    ITEM_SET(&vh265->pps.sps, h265->pps->sps, log2_diff_max_min_transform_block_size);
+    ITEM_SET(&vh265->pps.sps, h265->pps->sps, max_transform_hierarchy_depth_inter);
+    ITEM_SET(&vh265->pps.sps, h265->pps->sps, max_transform_hierarchy_depth_intra);
+    ITEM_SET(&vh265->pps.sps, h265->pps->sps, scaling_list_enabled_flag);
+    ITEM_CPY(&vh265->pps.sps, h265->pps->sps, ScalingList4x4);
+    ITEM_CPY(&vh265->pps.sps, h265->pps->sps, ScalingList8x8);
+    ITEM_CPY(&vh265->pps.sps, h265->pps->sps, ScalingList16x16);
+    ITEM_CPY(&vh265->pps.sps, h265->pps->sps, ScalingList32x32);
+    ITEM_CPY(&vh265->pps.sps, h265->pps->sps, ScalingListDCCoeff16x16);
+    ITEM_CPY(&vh265->pps.sps, h265->pps->sps, ScalingListDCCoeff32x32);
+    ITEM_SET(&vh265->pps.sps, h265->pps->sps, amp_enabled_flag);
+    ITEM_SET(&vh265->pps.sps, h265->pps->sps, sample_adaptive_offset_enabled_flag);
+    ITEM_SET(&vh265->pps.sps, h265->pps->sps, pcm_enabled_flag);
+    ITEM_SET(&vh265->pps.sps, h265->pps->sps, pcm_sample_bit_depth_luma_minus1);
+    ITEM_SET(&vh265->pps.sps, h265->pps->sps, pcm_sample_bit_depth_chroma_minus1);
+    ITEM_SET(&vh265->pps.sps, h265->pps->sps, log2_min_pcm_luma_coding_block_size_minus3);
+    ITEM_SET(&vh265->pps.sps, h265->pps->sps, log2_diff_max_min_pcm_luma_coding_block_size);
+    ITEM_SET(&vh265->pps.sps, h265->pps->sps, pcm_loop_filter_disabled_flag);
+    ITEM_SET(&vh265->pps.sps, h265->pps->sps, num_short_term_ref_pic_sets);
+    ITEM_SET(&vh265->pps.sps, h265->pps->sps, long_term_ref_pics_present_flag);
+    ITEM_SET(&vh265->pps.sps, h265->pps->sps, num_long_term_ref_pics_sps);
+    ITEM_SET(&vh265->pps.sps, h265->pps->sps, sps_temporal_mvp_enabled_flag);
+    ITEM_SET(&vh265->pps.sps, h265->pps->sps, strong_intra_smoothing_enabled_flag);
+
+    ITEM_SET(&vh265->pps, h265->pps, dependent_slice_segments_enabled_flag);
+    ITEM_SET(&vh265->pps, h265->pps, output_flag_present_flag);
+    ITEM_SET(&vh265->pps, h265->pps, num_extra_slice_header_bits);
+    ITEM_SET(&vh265->pps, h265->pps, sign_data_hiding_enabled_flag);
+    ITEM_SET(&vh265->pps, h265->pps, cabac_init_present_flag);
+    ITEM_SET(&vh265->pps, h265->pps, num_ref_idx_l0_default_active_minus1);
+    ITEM_SET(&vh265->pps, h265->pps, num_ref_idx_l1_default_active_minus1);
+    ITEM_SET(&vh265->pps, h265->pps, init_qp_minus26);
+    ITEM_SET(&vh265->pps, h265->pps, constrained_intra_pred_flag);
+    ITEM_SET(&vh265->pps, h265->pps, transform_skip_enabled_flag);
+    ITEM_SET(&vh265->pps, h265->pps, cu_qp_delta_enabled_flag);
+    ITEM_SET(&vh265->pps, h265->pps, diff_cu_qp_delta_depth);
+    ITEM_SET(&vh265->pps, h265->pps, pps_cb_qp_offset);
+    ITEM_SET(&vh265->pps, h265->pps, pps_cr_qp_offset);
+    ITEM_SET(&vh265->pps, h265->pps, pps_slice_chroma_qp_offsets_present_flag);
+    ITEM_SET(&vh265->pps, h265->pps, weighted_pred_flag);
+    ITEM_SET(&vh265->pps, h265->pps, weighted_bipred_flag);
+    ITEM_SET(&vh265->pps, h265->pps, transquant_bypass_enabled_flag);
+    ITEM_SET(&vh265->pps, h265->pps, tiles_enabled_flag);
+    ITEM_SET(&vh265->pps, h265->pps, entropy_coding_sync_enabled_flag);
+    ITEM_SET(&vh265->pps, h265->pps, num_tile_columns_minus1);
+    ITEM_SET(&vh265->pps, h265->pps, num_tile_rows_minus1);
+    ITEM_SET(&vh265->pps, h265->pps, uniform_spacing_flag);
+    ITEM_CPY(&vh265->pps, h265->pps, column_width_minus1);
+    ITEM_CPY(&vh265->pps, h265->pps, row_height_minus1);
+    ITEM_SET(&vh265->pps, h265->pps, loop_filter_across_tiles_enabled_flag);
+    ITEM_SET(&vh265->pps, h265->pps, pps_loop_filter_across_slices_enabled_flag);
+    ITEM_SET(&vh265->pps, h265->pps, deblocking_filter_control_present_flag);
+    ITEM_SET(&vh265->pps, h265->pps, deblocking_filter_override_enabled_flag);
+    ITEM_SET(&vh265->pps, h265->pps, pps_deblocking_filter_disabled_flag);
+    ITEM_SET(&vh265->pps, h265->pps, pps_beta_offset_div2);
+    ITEM_SET(&vh265->pps, h265->pps, pps_tc_offset_div2);
+    ITEM_SET(&vh265->pps, h265->pps, lists_modification_present_flag);
+    ITEM_SET(&vh265->pps, h265->pps, log2_parallel_merge_level_minus2);
+    ITEM_SET(&vh265->pps, h265->pps, slice_segment_header_extension_present_flag);
+    ITEM_SET(&vh265->pps, h265->pps, st_rps_bits);
+
+    ITEM_SET(vh265, h265, IDRPicFlag);
+    ITEM_SET(vh265, h265, RAPPicFlag);
+    ITEM_SET(vh265, h265, CurrRpsIdx);
+    ITEM_SET(vh265, h265, NumPocTotalCurr);
+    ITEM_SET(vh265, h265, NumDeltaPocsOfRefRpsIdx);
+    ITEM_SET(vh265, h265, NumShortTermPictureSliceHeaderBits);
+    ITEM_SET(vh265, h265, NumLongTermPictureSliceHeaderBits);
+
+    ITEM_SET(vh265, h265, CurrPicOrderCntVal);
+    for (i = 0; i < 16; i++) {
+        vbuf = virgl_video_buffer(h265->ref[i]);
+        vh265->ref[i] = vbuf ? vbuf->handle : 0;
+    }
+    ITEM_CPY(vh265, h265, PicOrderCntVal);
+    ITEM_CPY(vh265, h265, IsLongTerm);
+    ITEM_SET(vh265, h265, NumPocStCurrBefore);
+    ITEM_SET(vh265, h265, NumPocStCurrAfter);
+    ITEM_SET(vh265, h265, NumPocLtCurr);
+    ITEM_CPY(vh265, h265, RefPicSetStCurrBefore);
+    ITEM_CPY(vh265, h265, RefPicSetStCurrAfter);
+    ITEM_CPY(vh265, h265, RefPicSetLtCurr);
+    ITEM_CPY(vh265, h265, RefPicList);
+    ITEM_SET(vh265, h265, UseRefPicList);
+    ITEM_SET(vh265, h265, UseStRpsBits);
+
+    return 0;
+}
+
+static int fill_mpeg4_picture_desc(const struct pipe_picture_desc *desc,
+                                   union virgl_picture_desc *vdsc)
+{
+    unsigned i;
+    struct virgl_video_buffer *vbuf;
+    struct virgl_mpeg4_picture_desc *vmpeg4 = &vdsc->mpeg4;
+    struct pipe_mpeg4_picture_desc *mpeg4 = (struct pipe_mpeg4_picture_desc *)desc;
+
+    fill_base_picture_desc(desc, &vmpeg4->base);
+
+    ITEM_CPY(vmpeg4, mpeg4, trd);
+    ITEM_CPY(vmpeg4, mpeg4, trb);
+    ITEM_SET(vmpeg4, mpeg4, vop_time_increment_resolution);
+    ITEM_SET(vmpeg4, mpeg4, vop_coding_type);
+    ITEM_SET(vmpeg4, mpeg4, vop_fcode_forward);
+    ITEM_SET(vmpeg4, mpeg4, vop_fcode_backward);
+    ITEM_SET(vmpeg4, mpeg4, resync_marker_disable);
+    ITEM_SET(vmpeg4, mpeg4, interlaced);
+    ITEM_SET(vmpeg4, mpeg4, quant_type);
+    ITEM_SET(vmpeg4, mpeg4, quarter_sample);
+    ITEM_SET(vmpeg4, mpeg4, short_video_header);
+    ITEM_SET(vmpeg4, mpeg4, rounding_control);
+    ITEM_SET(vmpeg4, mpeg4, alternate_vertical_scan_flag);
+    ITEM_SET(vmpeg4, mpeg4, top_field_first);
+    ITEM_CPY(vmpeg4, mpeg4, intra_matrix);
+    ITEM_CPY(vmpeg4, mpeg4, non_intra_matrix);
+    for (i = 0; i < 16; i++) {
+        vbuf = virgl_video_buffer(mpeg4->ref[i]);
+        vmpeg4->ref[i] = vbuf ? vbuf->handle : 0;
+    }
+
+    return 0;
+}
+
+#undef ITEM_SET
+#undef ITEM_CPY
+
+static int fill_picture_desc(const struct pipe_picture_desc *desc,
+                             union virgl_picture_desc *vdsc)
+{
+    switch (u_reduce_video_profile(desc->profile)) {
+    case PIPE_VIDEO_FORMAT_MPEG4:
+        return fill_mpeg4_picture_desc(desc, vdsc);
+    case PIPE_VIDEO_FORMAT_MPEG4_AVC:
+        return fill_h264_picture_desc(desc, vdsc);
+    case PIPE_VIDEO_FORMAT_HEVC:
+        return fill_h265_picture_desc(desc, vdsc);
+    default:
+        return -1;
+    }
+}
+
+static void virgl_video_begin_frame(struct pipe_video_codec *codec,
+                                    struct pipe_video_buffer *target,
+                                    struct pipe_picture_desc *picture)
+{
+    struct virgl_video_codec *vcdc = virgl_video_codec(codec);
+    struct virgl_video_buffer *vbuf = virgl_video_buffer(target);
+
+    virgl_encode_begin_frame(vcdc->vctx, vcdc, vbuf);
+}
+
+static void virgl_video_decode_macroblock(struct pipe_video_codec *codec,
+                                          struct pipe_video_buffer *target,
+                                          struct pipe_picture_desc *picture,
+                                          const struct pipe_macroblock *macroblocks,
+                                          unsigned num_macroblocks)
+{
+    (void)codec;
+    (void)target;
+    (void)picture;
+    (void)macroblocks;
+    (void)num_macroblocks;
+}
+
+static void virgl_video_decode_bitstream(struct pipe_video_codec *codec,
+                                         struct pipe_video_buffer *target,
+                                         struct pipe_picture_desc *picture,
+                                         unsigned num_buffers,
+                                         const void * const *buffers,
+                                         const unsigned *sizes)
+{
+    struct virgl_video_codec *vcdc = virgl_video_codec(codec);
+    struct virgl_video_buffer *vbuf = virgl_video_buffer(target);
+    struct virgl_context *vctx = vcdc->vctx;
+    struct virgl_screen *vs = virgl_screen(vctx->base.screen);
+    struct virgl_resource *vres;
+    union  virgl_picture_desc vdsc;
+    struct pipe_transfer *xfer = NULL;
+    void *ptr;
+    unsigned i, total_size;
+
+    /* transfer bitstream data */
+    for (i = 0, total_size = 0; i < num_buffers; i++)
+        total_size += sizes[i];
+
+    if (total_size > pipe_buffer_size(vcdc->bs_buffers[vcdc->cur_buffer])) {
+        pipe_resource_reference(&vcdc->bs_buffers[vcdc->cur_buffer], NULL);
+        vcdc->bs_buffers[vcdc->cur_buffer] = pipe_buffer_create(vctx->base.screen,
+                        PIPE_BIND_CUSTOM, PIPE_USAGE_STAGING, total_size);
+    }
+
+    vctx->base.flush(&vctx->base, NULL, 0);
+
+    vres = virgl_resource(vcdc->bs_buffers[vcdc->cur_buffer]);
+    vs->vws->resource_wait(vs->vws, vres->hw_res);
+    ptr = pipe_buffer_map(&vctx->base, vcdc->bs_buffers[vcdc->cur_buffer],
+                          PIPE_MAP_WRITE, &xfer);
+    if (!ptr)
+        return;
+    for (i = 0, vcdc->bs_size = 0; i < num_buffers; i++) {
+        memcpy(ptr + vcdc->bs_size, buffers[i], sizes[i]);
+        vcdc->bs_size += sizes[i];
+    }
+    pipe_buffer_unmap(&vctx->base, xfer);
+
+    /* transfer picture description */
+    fill_picture_desc(picture, &vdsc);
+    vres = virgl_resource(vcdc->desc_buffers[vcdc->cur_buffer]);
+    vs->vws->resource_wait(vs->vws, vres->hw_res);
+    ptr = pipe_buffer_map(&vctx->base, vcdc->desc_buffers[vcdc->cur_buffer],
+                          PIPE_MAP_WRITE, &xfer);
+    if (!ptr)
+        return;
+    memcpy(ptr, &vdsc, sizeof(vdsc));
+    pipe_buffer_unmap(&vctx->base, xfer);
+
+    virgl_encode_decode_bitstream(vctx, vcdc, vbuf, &vdsc, sizeof(vdsc));
+}
+
+static void virgl_video_end_frame(struct pipe_video_codec *codec,
+                                  struct pipe_video_buffer *target,
+                                  struct pipe_picture_desc *picture)
+{
+    struct virgl_video_codec *vcdc = virgl_video_codec(codec);
+    struct virgl_context *vctx = virgl_context(vcdc->base.context);
+    struct virgl_video_buffer *vbuf = virgl_video_buffer(target);
+
+    virgl_encode_end_frame(vctx, vcdc, vbuf);
+    virgl_flush_eq(vctx, vctx, NULL);
+
+    switch_buffer(vcdc);
+}
+
+static void virgl_video_flush(struct pipe_video_codec *codec)
+{
+    (void)codec;
+}
+
+static void virgl_video_get_feedback(struct pipe_video_codec *codec,
+                                     void *feedback,
+                                     unsigned *size)
+{
+    (void)codec;
+    (void)feedback;
+    (void)size;
+}
+
+static void virgl_video_destroy_codec(struct pipe_video_codec *codec)
+{
+    unsigned i;
+    struct virgl_video_codec *vcdc = virgl_video_codec(codec);
+    struct virgl_context *vctx = virgl_context(vcdc->base.context);
+
+    for (i = 0; i < VIRGL_VIDEO_CODEC_BUF_NUM; i++) {
+        pipe_resource_reference(&vcdc->bs_buffers[i], NULL);
+        pipe_resource_reference(&vcdc->desc_buffers[i], NULL);
+    }
+
+    virgl_encode_destroy_video_codec(vctx, vcdc);
+
+    free(vcdc);
+}
+
+
+struct pipe_video_codec *
+virgl_video_create_codec(struct pipe_context *ctx,
+                         const struct pipe_video_codec *templ)
+{
+    unsigned i;
+    struct virgl_video_codec *vcdc;
+    struct virgl_context *vctx = virgl_context(ctx);
+    unsigned width = templ->width, height = templ->height;
+
+    if (virgl_debug & VIRGL_DEBUG_VIDEO)
+        debug_printf("VIDEO: create codec. profile=%d, level=%u, entryp=%d, "
+                     "chroma_fmt=%d, size=%ux%u, max_ref=%u, expect=%d\n",
+                     templ->profile, templ->level, templ->entrypoint,
+                     templ->chroma_format, templ->width, templ->height,
+                     templ->max_references, templ->expect_chunked_decode);
+
+    /* encode: not supported now */
+    if (templ->entrypoint == PIPE_VIDEO_ENTRYPOINT_ENCODE)
+        return NULL;
+
+    /* decode: */
+    switch (u_reduce_video_profile(templ->profile)) {
+    case PIPE_VIDEO_FORMAT_MPEG4: /* fall through */
+    case PIPE_VIDEO_FORMAT_MPEG4_AVC:
+        width = align(width, VL_MACROBLOCK_WIDTH);
+        height = align(height, VL_MACROBLOCK_HEIGHT);
+        break;
+    case PIPE_VIDEO_FORMAT_HEVC: /* fall through */
+    default:
+        break;
+    }
+
+    vcdc = CALLOC_STRUCT(virgl_video_codec);
+    if (!vcdc)
+        return NULL;
+
+    vcdc->base = *templ;
+    vcdc->base.width = width;
+    vcdc->base.height = height;
+    vcdc->base.context = ctx;
+
+    vcdc->base.destroy = virgl_video_destroy_codec;
+    vcdc->base.begin_frame = virgl_video_begin_frame;
+    vcdc->base.decode_macroblock = virgl_video_decode_macroblock;
+    vcdc->base.decode_bitstream = virgl_video_decode_bitstream;
+    vcdc->base.end_frame = virgl_video_end_frame;
+    vcdc->base.flush = virgl_video_flush;
+    vcdc->base.get_feedback = virgl_video_get_feedback;
+
+    vcdc->bs_size = 0;
+    vcdc->cur_buffer = 0;
+    for (i = 0; i < VIRGL_VIDEO_CODEC_BUF_NUM; i++) {
+        vcdc->bs_buffers[i] = pipe_buffer_create(ctx->screen,
+                          PIPE_BIND_CUSTOM, PIPE_USAGE_STAGING,
+                          BS_BUF_DEFAULT_SIZE(width, height));
+
+        vcdc->desc_buffers[i] = pipe_buffer_create(ctx->screen,
+                            PIPE_BIND_CUSTOM, PIPE_USAGE_STAGING,
+                            sizeof(union virgl_picture_desc));
+    }
+
+    vcdc->handle = virgl_object_assign_handle();
+    vcdc->vctx = vctx;
+
+    virgl_encode_create_video_codec(vctx, vcdc);
+
+    return &vcdc->base;
+}
+
+
+static void virgl_video_destroy_buffer(struct pipe_video_buffer *buffer)
+{
+    struct virgl_video_buffer *vbuf = virgl_video_buffer(buffer);
+
+    virgl_encode_destroy_video_buffer(vbuf->vctx, vbuf);
+
+    vl_video_buffer_destroy(buffer);
+
+    free(vbuf);
+}
+
+static void virgl_video_destroy_buffer_associated_data(void *data)
+{
+    (void)data;
+}
+
+struct pipe_video_buffer *
+virgl_video_create_buffer(struct pipe_context *ctx,
+                          const struct pipe_video_buffer *tmpl)
+{
+    struct virgl_context *vctx = virgl_context(ctx);
+    struct virgl_video_buffer *vbuf;
+
+    vbuf = CALLOC_STRUCT(virgl_video_buffer);
+    if (!vbuf)
+        return NULL;
+
+    vbuf->buf = vl_video_buffer_create(ctx, tmpl);
+    if (!vbuf->buf) {
+        free(vbuf);
+        return NULL;
+    }
+    vbuf->buf->destroy = virgl_video_destroy_buffer;
+    vl_video_buffer_set_associated_data(vbuf->buf,
+            NULL, vbuf, virgl_video_destroy_buffer_associated_data);
+
+    vbuf->num_planes = util_format_get_num_planes(vbuf->buf->buffer_format);
+    vbuf->plane_views = vbuf->buf->get_sampler_view_planes(vbuf->buf);
+    vbuf->handle = virgl_object_assign_handle();
+    vbuf->buffer_format = tmpl->buffer_format;
+    vbuf->width = tmpl->width;
+    vbuf->height = tmpl->height;
+    vbuf->vctx = vctx;
+
+    virgl_encode_create_video_buffer(vctx, vbuf);
+
+    if (virgl_debug & VIRGL_DEBUG_VIDEO) {
+        debug_printf("VIDEO: create buffer. fmt=%s, %ux%u, num_planes=%u\n",
+                     util_format_name(tmpl->buffer_format),
+                     tmpl->width, tmpl->height, vbuf->num_planes);
+
+        for (unsigned i = 0; i < vbuf->num_planes; i++)
+            if (vbuf->plane_views[i])
+                debug_printf("VIDEO: plane[%d]: fmt=%s, target=%u\n", i,
+                             util_format_name(vbuf->plane_views[i]->format),
+                             vbuf->plane_views[i]->target);
+    }
+
+    return vbuf->buf;
+}
+
diff --git a/src/gallium/drivers/virgl/virgl_video.h b/src/gallium/drivers/virgl/virgl_video.h
new file mode 100644 (file)
index 0000000..7d88b77
--- /dev/null
@@ -0,0 +1,347 @@
+/*
+ * Copyright 2022 Kylin Software Co., Ltd.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * on the rights to use, copy, modify, merge, publish, distribute, sub
+ * license, and/or sell copies of the Software, and to permit persons to whom
+ * the Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef VIRGL_VIDEO_H
+#define VIRGL_VIDEO_H
+
+/*
+ * This file contains parts of the HW ABI, such as union virgl_picture_desc
+ * and other related structures.
+ */
+
+#include "virgl_context.h"
+#include "vl/vl_video_buffer.h"
+#include "pipe/p_video_codec.h"
+
+#define VIRGL_VIDEO_CODEC_BUF_NUM    10
+
+struct virgl_video_codec {
+    struct pipe_video_codec base;       /* must be first */
+
+    uint32_t handle;
+    struct virgl_context *vctx;
+
+    uint32_t bs_size;                   /* size of data in bs_buffer */
+    uint32_t cur_buffer;                /* index of current bs/desc buffer */
+    struct pipe_resource *bs_buffers[VIRGL_VIDEO_CODEC_BUF_NUM];
+    struct pipe_resource *desc_buffers[VIRGL_VIDEO_CODEC_BUF_NUM];
+};
+
+struct virgl_video_buffer {
+    uint32_t handle;
+    enum pipe_format buffer_format;
+    unsigned width;
+    unsigned height;
+    struct virgl_context *vctx;
+    struct pipe_video_buffer *buf;
+    unsigned num_planes;
+    struct pipe_sampler_view **plane_views;
+};
+
+struct virgl_base_picture_desc {
+    uint16_t profile;       /* enum pipe_video_profile */
+    uint8_t entry_point;    /* enum pipe_video_entrypoint */
+    uint8_t protected_playback;
+    uint8_t decrypt_key[256];
+    uint32_t key_size;
+
+};
+
+/* H.264 sequence parameter set */
+struct virgl_h264_sps {
+    uint8_t  level_idc;
+    uint8_t  chroma_format_idc;
+    uint8_t  separate_colour_plane_flag;
+    uint8_t  bit_depth_luma_minus8;
+
+    uint8_t  bit_depth_chroma_minus8;
+    uint8_t  seq_scaling_matrix_present_flag;
+    uint8_t  ScalingList4x4[6][16];
+    uint8_t  ScalingList8x8[6][64];
+
+    uint8_t  log2_max_frame_num_minus4;
+    uint8_t  pic_order_cnt_type;
+    uint8_t  log2_max_pic_order_cnt_lsb_minus4;
+    uint8_t  delta_pic_order_always_zero_flag;
+
+    int32_t  offset_for_non_ref_pic;
+    int32_t  offset_for_top_to_bottom_field;
+    int32_t  offset_for_ref_frame[256];
+
+    uint8_t  num_ref_frames_in_pic_order_cnt_cycle;
+    uint8_t  max_num_ref_frames;
+    uint8_t  frame_mbs_only_flag;
+    uint8_t  mb_adaptive_frame_field_flag;
+
+    uint8_t  direct_8x8_inference_flag;
+    uint8_t  MinLumaBiPredSize8x8;
+    uint8_t  reserved[2];
+};
+
+/* H.264 picture parameter set */
+struct virgl_h264_pps {
+    struct virgl_h264_sps sps; /* Seq Param Set */
+
+    uint8_t  entropy_coding_mode_flag;
+    uint8_t  bottom_field_pic_order_in_frame_present_flag;
+    uint8_t  num_slice_groups_minus1;
+    uint8_t  slice_group_map_type;
+
+    uint8_t  slice_group_change_rate_minus1;
+    uint8_t  num_ref_idx_l0_default_active_minus1;
+    uint8_t  num_ref_idx_l1_default_active_minus1;
+    uint8_t  weighted_pred_flag;
+
+    uint8_t  weighted_bipred_idc;
+    int8_t   pic_init_qp_minus26;
+    int8_t   pic_init_qs_minus26;
+    int8_t   chroma_qp_index_offset;
+
+    uint8_t  deblocking_filter_control_present_flag;
+    uint8_t  constrained_intra_pred_flag;
+    uint8_t  redundant_pic_cnt_present_flag;
+    uint8_t  transform_8x8_mode_flag;
+
+    uint8_t  ScalingList4x4[6][16];
+    uint8_t  ScalingList8x8[6][64];
+
+    int8_t   second_chroma_qp_index_offset;
+    uint8_t  reserved[3];
+};
+
+struct virgl_h264_picture_desc {
+    struct virgl_base_picture_desc base;
+
+    struct virgl_h264_pps pps;  /* Picture Param Set */
+
+    uint32_t frame_num;
+
+    uint8_t  field_pic_flag;
+    uint8_t  bottom_field_flag;
+    uint8_t  num_ref_idx_l0_active_minus1;
+    uint8_t  num_ref_idx_l1_active_minus1;
+
+    uint32_t slice_count;
+    int32_t  field_order_cnt[2];
+
+    uint8_t  is_long_term[16];
+    uint8_t  top_is_reference[16];
+    uint8_t  bottom_is_reference[16];
+    uint32_t field_order_cnt_list[16][2];
+    uint32_t frame_num_list[16];
+    uint32_t buffer_id[16];
+
+    uint8_t  is_reference;
+    uint8_t  num_ref_frames;
+    uint8_t  reserved[2];
+};
+
+struct virgl_h265_sps
+{
+   uint32_t pic_width_in_luma_samples;
+   uint32_t pic_height_in_luma_samples;
+
+   uint8_t chroma_format_idc;
+   uint8_t separate_colour_plane_flag;
+   uint8_t bit_depth_luma_minus8;
+   uint8_t bit_depth_chroma_minus8;
+
+   uint8_t log2_max_pic_order_cnt_lsb_minus4;
+   uint8_t sps_max_dec_pic_buffering_minus1;
+   uint8_t log2_min_luma_coding_block_size_minus3;
+   uint8_t log2_diff_max_min_luma_coding_block_size;
+
+   uint8_t log2_min_transform_block_size_minus2;
+   uint8_t log2_diff_max_min_transform_block_size;
+   uint8_t max_transform_hierarchy_depth_inter;
+   uint8_t max_transform_hierarchy_depth_intra;
+
+   uint8_t ScalingList4x4[6][16];
+   uint8_t ScalingList8x8[6][64];
+   uint8_t ScalingList16x16[6][64];
+   uint8_t ScalingList32x32[2][64];
+
+   uint8_t ScalingListDCCoeff16x16[6];
+   uint8_t ScalingListDCCoeff32x32[2];
+
+   uint8_t scaling_list_enabled_flag;
+   uint8_t amp_enabled_flag;
+   uint8_t sample_adaptive_offset_enabled_flag;
+   uint8_t pcm_enabled_flag;
+
+   uint8_t pcm_sample_bit_depth_luma_minus1;
+   uint8_t pcm_sample_bit_depth_chroma_minus1;
+   uint8_t log2_min_pcm_luma_coding_block_size_minus3;
+   uint8_t log2_diff_max_min_pcm_luma_coding_block_size;
+
+   uint8_t pcm_loop_filter_disabled_flag;
+   uint8_t num_short_term_ref_pic_sets;
+   uint8_t long_term_ref_pics_present_flag;
+   uint8_t num_long_term_ref_pics_sps;
+
+   uint8_t sps_temporal_mvp_enabled_flag;
+   uint8_t strong_intra_smoothing_enabled_flag;
+   uint8_t reserved[2];
+};
+
+struct virgl_h265_pps
+{
+   struct virgl_h265_sps sps;
+
+   uint8_t dependent_slice_segments_enabled_flag;
+   uint8_t output_flag_present_flag;
+   uint8_t num_extra_slice_header_bits;
+   uint8_t sign_data_hiding_enabled_flag;
+
+   uint8_t cabac_init_present_flag;
+   uint8_t num_ref_idx_l0_default_active_minus1;
+   uint8_t num_ref_idx_l1_default_active_minus1;
+   int8_t init_qp_minus26;
+
+   uint8_t constrained_intra_pred_flag;
+   uint8_t transform_skip_enabled_flag;
+   uint8_t cu_qp_delta_enabled_flag;
+   uint8_t diff_cu_qp_delta_depth;
+
+   int8_t pps_cb_qp_offset;
+   int8_t pps_cr_qp_offset;
+   uint8_t pps_slice_chroma_qp_offsets_present_flag;
+   uint8_t weighted_pred_flag;
+
+   uint8_t weighted_bipred_flag;
+   uint8_t transquant_bypass_enabled_flag;
+   uint8_t tiles_enabled_flag;
+   uint8_t entropy_coding_sync_enabled_flag;
+
+   uint16_t column_width_minus1[20];
+   uint16_t row_height_minus1[22];
+
+   uint8_t num_tile_columns_minus1;
+   uint8_t num_tile_rows_minus1;
+   uint8_t uniform_spacing_flag;
+   uint8_t loop_filter_across_tiles_enabled_flag;
+
+   uint8_t pps_loop_filter_across_slices_enabled_flag;
+   uint8_t deblocking_filter_control_present_flag;
+   uint8_t deblocking_filter_override_enabled_flag;
+   uint8_t pps_deblocking_filter_disabled_flag;
+
+   int8_t pps_beta_offset_div2;
+   int8_t pps_tc_offset_div2;
+   uint8_t lists_modification_present_flag;
+   uint8_t log2_parallel_merge_level_minus2;
+
+   uint16_t st_rps_bits;
+   uint8_t slice_segment_header_extension_present_flag;
+   uint8_t reserved;
+};
+
+struct virgl_h265_picture_desc
+{
+   struct virgl_base_picture_desc base;
+
+   struct virgl_h265_pps pps;
+
+   int32_t CurrPicOrderCntVal;
+   uint32_t ref[16];
+   int32_t PicOrderCntVal[16];
+
+   uint32_t NumPocTotalCurr;
+   uint32_t NumDeltaPocsOfRefRpsIdx;
+   uint32_t NumShortTermPictureSliceHeaderBits;
+   uint32_t NumLongTermPictureSliceHeaderBits;
+
+   uint8_t IsLongTerm[16];
+
+   uint8_t IDRPicFlag;
+   uint8_t RAPPicFlag;
+   uint8_t CurrRpsIdx;
+   uint8_t NumPocStCurrBefore;
+
+   uint8_t NumPocStCurrAfter;
+   uint8_t NumPocLtCurr;
+   uint8_t UseRefPicList;
+   uint8_t UseStRpsBits;
+
+   uint8_t RefPicSetStCurrBefore[8];
+   uint8_t RefPicSetStCurrAfter[8];
+   uint8_t RefPicSetLtCurr[8];
+
+   uint8_t RefPicList[2][15];
+   uint8_t reserved[2];
+};
+
+struct virgl_mpeg4_picture_desc
+{
+   struct virgl_base_picture_desc base;
+
+   int32_t trd[2];
+   int32_t trb[2];
+   uint16_t vop_time_increment_resolution;
+   uint8_t vop_coding_type;
+   uint8_t vop_fcode_forward;
+   uint8_t vop_fcode_backward;
+   uint8_t resync_marker_disable;
+   uint8_t interlaced;
+   uint8_t quant_type;
+   uint8_t quarter_sample;
+   uint8_t short_video_header;
+   uint8_t rounding_control;
+   uint8_t alternate_vertical_scan_flag;
+   uint8_t top_field_first;
+
+   uint8_t intra_matrix[64];
+   uint8_t non_intra_matrix[64];
+
+   uint32_t ref[2];
+};
+
+union virgl_picture_desc {
+    struct virgl_base_picture_desc base;
+    struct virgl_h264_picture_desc h264;
+    struct virgl_h265_picture_desc h265;
+    struct virgl_mpeg4_picture_desc mpeg4;
+};
+
+static inline struct virgl_video_codec *
+virgl_video_codec(struct pipe_video_codec *codec)
+{
+    return (struct virgl_video_codec *)codec;
+}
+
+static inline struct virgl_video_buffer *
+virgl_video_buffer(struct pipe_video_buffer *buffer)
+{
+    return buffer ? vl_video_buffer_get_associated_data(buffer, NULL) : NULL;
+}
+
+struct pipe_video_codec *
+virgl_video_create_codec(struct pipe_context *ctx,
+                         const struct pipe_video_codec *templ);
+
+struct pipe_video_buffer *
+virgl_video_create_buffer(struct pipe_context *ctx,
+                          const struct pipe_video_buffer *tmpl);
+
+#endif /* VIRGL_VIDEO_H */
+
index 10f9495..44addd4 100644 (file)
@@ -51,7 +51,7 @@ libva_gallium = shared_library(
   link_whole : [libva_st],
   link_with : link_with_libva_gallium,
   dependencies : [
-    dep_libdrm, driver_r600, driver_radeonsi, driver_nouveau, driver_d3d12,
+    dep_libdrm, driver_r600, driver_radeonsi, driver_nouveau, driver_d3d12, driver_virgl,
     idep_mesautil,
   ],
   link_depends : va_link_depends,
@@ -64,6 +64,7 @@ libva_gallium = shared_library(
 foreach d : [[with_gallium_r600, 'r600'],
              [with_gallium_radeonsi, 'radeonsi'],
              [with_gallium_nouveau, 'nouveau'],
+             [with_gallium_virgl, 'virtio_gpu'],
              [with_gallium_d3d12_video, 'd3d12']]
   if d[0]
     va_drivers += '@0@_drv_video.so'.format(d[1])
index 1e793eb..d51af5c 100644 (file)
@@ -56,7 +56,7 @@ libvdpau_gallium = shared_library(
   link_with : link_with_libvdpau_gallium,
   dependencies : [
     idep_mesautil,
-    driver_r300, driver_r600, driver_radeonsi, driver_nouveau, driver_d3d12,
+    driver_r300, driver_r600, driver_radeonsi, driver_nouveau, driver_d3d12, driver_virgl,
   ],
   link_depends : vdpau_link_depends,
   soversion : '@0@.@1@.0'.format(VDPAU_MAJOR, VDPAU_MINOR),
@@ -69,6 +69,7 @@ foreach d : [[with_gallium_r300, 'r300'],
              [with_gallium_r600, 'r600'],
              [with_gallium_radeonsi, 'radeonsi'],
              [with_gallium_nouveau, 'nouveau'],
+             [with_gallium_virgl, 'virtio_gpu'],
              [with_gallium_d3d12_video, 'd3d12']]
   if d[0]
     vdpau_drivers += 'libvdpau_@0@.so.@1@.@2@.0'.format(d[1], VDPAU_MAJOR, VDPAU_MINOR)
index eafc0b4..b713ef6 100644 (file)
@@ -546,6 +546,26 @@ struct virgl_caps_v1 {
         uint32_t max_texture_gather_components;
 };
 
+struct virgl_video_caps {
+        uint32_t profile:8;
+        uint32_t entrypoint:8;
+        uint32_t max_level:8;
+        uint32_t stacked_frames:8;
+
+        uint32_t max_width:16;
+        uint32_t max_height:16;
+
+        uint32_t prefered_format:16;
+        uint32_t max_macroblocks:16;
+
+        uint32_t npot_texture:1;
+        uint32_t supports_progressive:1;
+        uint32_t supports_interlaced:1;
+        uint32_t prefers_interlaced:1;
+        uint32_t max_temporal_layers:8;
+        uint32_t reserved:20;
+};
+
 /*
  * This struct should be growable when used in capset 2,
  * so we shouldn't have to add a v3 ever.
@@ -603,6 +623,8 @@ struct virgl_caps_v2 {
         uint32_t max_shader_sampler_views;
         struct virgl_supported_format_mask supported_multisample_formats;
         uint32_t max_const_buffer_size[6]; // PIPE_SHADER_TYPES
+        uint32_t num_video_caps;
+        struct virgl_video_caps video_caps[32];
 };
 
 union virgl_caps {
index 7162953..31ce72e 100644 (file)
@@ -117,6 +117,18 @@ enum virgl_context_cmd {
    VIRGL_CCMD_GET_MEMORY_INFO,
    VIRGL_CCMD_EMIT_STRING_MARKER,
    VIRGL_CCMD_LINK_SHADER,
+
+   /* video codec */
+   VIRGL_CCMD_CREATE_VIDEO_CODEC,
+   VIRGL_CCMD_DESTROY_VIDEO_CODEC,
+   VIRGL_CCMD_CREATE_VIDEO_BUFFER,
+   VIRGL_CCMD_DESTROY_VIDEO_BUFFER,
+   VIRGL_CCMD_BEGIN_FRAME,
+   VIRGL_CCMD_DECODE_MACROBLOCK,
+   VIRGL_CCMD_DECODE_BITSTREAM,
+   VIRGL_CCMD_ENCODE_BITSTREAM,
+   VIRGL_CCMD_END_FRAME,
+
    VIRGL_MAX_COMMANDS
 };
 
@@ -696,4 +708,52 @@ enum vrend_tweak_type {
 #define VIRGL_LINK_SHADER_TESS_EVAL_HANDLE 5
 #define VIRGL_LINK_SHADER_COMPUTE_HANDLE 6
 
+/* VIRGL_CCMD_CREATE_VIDEO_CODEC */
+#define VIRGL_CREATE_VIDEO_CODEC_SIZE       7
+#define VIRGL_CREATE_VIDEO_CODEC_HANDLE     1
+#define VIRGL_CREATE_VIDEO_CODEC_PROFILE    2
+#define VIRGL_CREATE_VIDEO_CODEC_ENTRYPOINT 3
+#define VIRGL_CREATE_VIDEO_CODEC_CHROMA_FMT 4
+#define VIRGL_CREATE_VIDEO_CODEC_LEVEL      5
+#define VIRGL_CREATE_VIDEO_CODEC_WIDTH      6
+#define VIRGL_CREATE_VIDEO_CODEC_HEIGHT     7
+
+/* VIRGL_CCMD_DESTROY_VIDEO_CODEC */
+#define VIRGL_DESTROY_VIDEO_CODEC_SIZE      1
+#define VIRGL_DESTROY_VIDEO_CODEC_HANDLE    1
+
+/* VIRGL_CCMD_CREATE_VIDEO_BUFFER */
+#define VIRGL_CREATE_VIDEO_BUFFER_MIN_SIZE  5
+#define VIRGL_CREATE_VIDEO_BUFFER_HANDLE    1
+#define VIRGL_CREATE_VIDEO_BUFFER_FORMAT    2
+#define VIRGL_CREATE_VIDEO_BUFFER_WIDTH     3
+#define VIRGL_CREATE_VIDEO_BUFFER_HEIGHT    4
+#define VIRGL_CREATE_VIDEO_BUFFER_RES_BASE  5
+
+/* VIRGL_CCMD_DESTROY_VIDEO_BUFFER */
+#define VIRGL_DESTROY_VIDEO_BUFFER_SIZE     1
+#define VIRGL_DESTROY_VIDEO_BUFFER_HANDLE   1
+
+/* VIRGL_CCMD_BEGIN_FRAME */
+#define VIRGL_BEGIN_FRAME_SIZE              2
+#define VIRGL_BEGIN_FRAME_CDC_HANDLE        1
+#define VIRGL_BEGIN_FRAME_TGT_HANDLE        2
+
+/* VIRGL_CCMD_DECODE_MACROBLOCK */
+
+/* VIRGL_CCMD_DECODE_BITSTREAM */
+#define VIRGL_DECODE_BS_SIZE                5
+#define VIRGL_DECODE_BS_CDC_HANDLE          1
+#define VIRGL_DECODE_BS_TGT_HANDLE          2
+#define VIRGL_DECODE_BS_DSC_HANDLE          3
+#define VIRGL_DECODE_BS_BUF_HANDLE          4
+#define VIRGL_DECODE_BS_BUF_SIZE            5
+
+/* VIRGL_CCMD_ENCODE_BITSTREAM */
+
+/* VIRGL_CCMD_END_FRAME */
+#define VIRGL_END_FRAME_SIZE                2
+#define VIRGL_END_FRAME_CDC_HANDLE          1
+#define VIRGL_END_FRAME_TGT_HANDLE          2
+
 #endif