* USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
+/**
+ * @file
+ * Virgl video driver implementation.
+ *
+ * The virgl video driver acts as the frontend, and the virglrenderer acts as
+ * the backend. Currently, the backend is implemented via VA-API, but it is
+ * not limited to this.
+ *
+ * The relationship between vaSurface and video buffer objects:
+ *
+ * GUEST (Mesa) | HOST (Virglrenderer)
+ * |
+ * +------------+ | +------------+
+ * | vaSurface | | | vaSurface | <------+
+ * +------------+ | +------------+ |
+ * | | |
+ * +---------------------------+ | +-------------------------+ |
+ * | virgl_video_buffer | | | vrend_video_buffer | |
+ * | +-----------------------+ | | | +-------------------+ | |
+ * | | vl_video_buffer | | | | | vrend_resource(s) | | |
+ * | | +-------------------+ | |<--+-->| +-------------------+ | |
+ * | | | virgl_resource(s) | | | | | +--------------------+ | |
+ * | | +-------------------+ | | | | | virgl_video_buffer |-+--+
+ * | +-----------------------+ | | | +--------------------+ |
+ * +---------------------------+ | +-------------------------+
+ *
+ * The relationship between vaContext and video codec objects:
+ *
+ * GUEST (Mesa) | HOST (Virglrenderer)
+ * |
+ * +------------+ | +------------+
+ * | vaContext | | | vaContext | <-------+
+ * +------------+ | +------------+ |
+ * | | |
+ * +------------------------+ | +--------------------------+ |
+ * | virgl_video_codec | <--+--> | vrend_video_codec | |
+ * +------------------------+ | | +--------------------+ | |
+ * | | | virgl_video_codec | -+--+
+ * | | +--------------------+ |
+ * | +--------------------------+
+ *
+ * @author Feng Jiang <jiangfeng@kylinos.cn>
+ */
+
#include <string.h>
#include <sys/param.h>
return 0;
}
+static int fill_h264_enc_picture_desc(const struct pipe_picture_desc *desc,
+ union virgl_picture_desc *vdsc)
+{
+ unsigned i;
+ struct virgl_h264_enc_picture_desc *vh264 = &vdsc->h264_enc;
+ struct pipe_h264_enc_picture_desc *h264 = (struct pipe_h264_enc_picture_desc *)desc;
+
+ fill_base_picture_desc(desc, &vh264->base);
+
+ /* seq param */
+ ITEM_SET(vh264, h264, seq.enc_constraint_set_flags);
+ ITEM_SET(vh264, h264, seq.enc_frame_cropping_flag);
+ ITEM_SET(vh264, h264, seq.enc_frame_crop_left_offset);
+ ITEM_SET(vh264, h264, seq.enc_frame_crop_right_offset);
+ ITEM_SET(vh264, h264, seq.enc_frame_crop_top_offset);
+ ITEM_SET(vh264, h264, seq.enc_frame_crop_bottom_offset);
+ ITEM_SET(vh264, h264, seq.pic_order_cnt_type);
+ ITEM_SET(vh264, h264, seq.num_temporal_layers);
+ ITEM_SET(vh264, h264, seq.vui_parameters_present_flag);
+ ITEM_SET(vh264, h264, seq.vui_flags.aspect_ratio_info_present_flag);
+ ITEM_SET(vh264, h264, seq.vui_flags.timing_info_present_flag);
+ ITEM_SET(vh264, h264, seq.aspect_ratio_idc);
+ ITEM_SET(vh264, h264, seq.sar_width);
+ ITEM_SET(vh264, h264, seq.sar_height);
+ ITEM_SET(vh264, h264, seq.num_units_in_tick);
+ ITEM_SET(vh264, h264, seq.time_scale);
+
+ /* rate_ctrl */
+ for (i = 0; i < 4; i++) {
+ ITEM_SET(vh264, h264, rate_ctrl[i].rate_ctrl_method);
+ ITEM_SET(vh264, h264, rate_ctrl[i].target_bitrate);
+ ITEM_SET(vh264, h264, rate_ctrl[i].peak_bitrate);
+ ITEM_SET(vh264, h264, rate_ctrl[i].frame_rate_num);
+ ITEM_SET(vh264, h264, rate_ctrl[i].frame_rate_den);
+ ITEM_SET(vh264, h264, rate_ctrl[i].vbv_buffer_size);
+ ITEM_SET(vh264, h264, rate_ctrl[i].vbv_buf_lv);
+ ITEM_SET(vh264, h264, rate_ctrl[i].target_bits_picture);
+ ITEM_SET(vh264, h264, rate_ctrl[i].peak_bits_picture_integer);
+ ITEM_SET(vh264, h264, rate_ctrl[i].peak_bits_picture_fraction);
+ ITEM_SET(vh264, h264, rate_ctrl[i].fill_data_enable);
+ ITEM_SET(vh264, h264, rate_ctrl[i].skip_frame_enable);
+ ITEM_SET(vh264, h264, rate_ctrl[i].enforce_hrd);
+ ITEM_SET(vh264, h264, rate_ctrl[i].max_au_size);
+ ITEM_SET(vh264, h264, rate_ctrl[i].max_qp);
+ ITEM_SET(vh264, h264, rate_ctrl[i].min_qp);
+ }
+
+ /* motion_est */
+ ITEM_SET(vh264, h264, motion_est.motion_est_quarter_pixel);
+ ITEM_SET(vh264, h264, motion_est.enc_disable_sub_mode);
+ ITEM_SET(vh264, h264, motion_est.lsmvert);
+ ITEM_SET(vh264, h264, motion_est.enc_en_ime_overw_dis_subm);
+ ITEM_SET(vh264, h264, motion_est.enc_ime_overw_dis_subm_no);
+ ITEM_SET(vh264, h264, motion_est.enc_ime2_search_range_x);
+ ITEM_SET(vh264, h264, motion_est.enc_ime2_search_range_y);
+
+ /* pic_ctrl */
+ ITEM_SET(vh264, h264, pic_ctrl.enc_cabac_enable);
+ ITEM_SET(vh264, h264, pic_ctrl.enc_cabac_init_idc);
+
+ ITEM_SET(vh264, h264, intra_idr_period);
+
+ ITEM_SET(vh264, h264, quant_i_frames);
+ ITEM_SET(vh264, h264, quant_p_frames);
+ ITEM_SET(vh264, h264, quant_b_frames);
+
+ ITEM_SET(vh264, h264, picture_type);
+ ITEM_SET(vh264, h264, frame_num);
+ ITEM_SET(vh264, h264, frame_num_cnt);
+ ITEM_SET(vh264, h264, p_remain);
+ ITEM_SET(vh264, h264, i_remain);
+ ITEM_SET(vh264, h264, idr_pic_id);
+ ITEM_SET(vh264, h264, gop_cnt);
+ ITEM_SET(vh264, h264, pic_order_cnt);
+ ITEM_SET(vh264, h264, num_ref_idx_l0_active_minus1);
+ ITEM_SET(vh264, h264, num_ref_idx_l1_active_minus1);
+
+ for (i = 0; i < 32; i++) {
+ ITEM_SET(vh264, h264, ref_idx_l0_list[i]);
+ ITEM_SET(vh264, h264, ref_idx_l1_list[i]);
+ ITEM_SET(vh264, h264, l0_is_long_term[i]);
+ ITEM_SET(vh264, h264, l1_is_long_term[i]);
+ }
+
+ ITEM_SET(vh264, h264, gop_size);
+
+ ITEM_SET(vh264, h264, quality_modes.level);
+ ITEM_SET(vh264, h264, quality_modes.preset_mode);
+ ITEM_SET(vh264, h264, quality_modes.pre_encode_mode);
+ ITEM_SET(vh264, h264, quality_modes.vbaq_mode);
+
+ ITEM_SET(vh264, h264, not_referenced);
+ ITEM_SET(vh264, h264, is_ltr);
+ ITEM_SET(vh264, h264, ltr_index);
+ ITEM_SET(vh264, h264, enable_vui);
+
+ ITEM_SET(vh264, h264, num_slice_descriptors);
+ for (i = 0; i < vh264->num_slice_descriptors; i++) {
+ ITEM_SET(vh264, h264, slices_descriptors[i].macroblock_address);
+ ITEM_SET(vh264, h264, slices_descriptors[i].num_macroblocks);
+ ITEM_SET(vh264, h264, slices_descriptors[i].slice_type);
+ }
+
+ return 0;
+}
+
static int fill_h265_picture_desc(const struct pipe_picture_desc *desc,
union virgl_picture_desc *vdsc)
{
}
}
+static int fill_enc_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_AVC:
+ return fill_h264_enc_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);
+ if (codec->entrypoint == PIPE_VIDEO_ENTRYPOINT_ENCODE)
+ fill_enc_picture_desc(picture, &vcdc->desc);
+
virgl_encode_begin_frame(vcdc->vctx, vcdc, vbuf);
}
virgl_encode_decode_bitstream(vctx, vcdc, vbuf, &vdsc, sizeof(vdsc));
}
+static void virgl_video_encode_bitstream(struct pipe_video_codec *codec,
+ struct pipe_video_buffer *source,
+ struct pipe_resource *target,
+ void **feedback)
+{
+ struct virgl_video_codec *vcdc = virgl_video_codec(codec);
+ struct virgl_context *vctx = vcdc->vctx;
+ struct virgl_screen *vs = virgl_screen(vctx->base.screen);
+ struct virgl_resource *vres;
+ struct virgl_video_encode_feedback *fb;
+ struct pipe_transfer *xfer = NULL;
+ void *ptr;
+
+ /* Transfer picture desc */
+ 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, &vcdc->desc, sizeof(vcdc->desc));
+ pipe_buffer_unmap(&vctx->base, xfer);
+
+ /* Init feedback */
+ vres = virgl_resource(vcdc->feed_buffers[vcdc->cur_buffer]);
+ vs->vws->resource_wait(vs->vws, vres->hw_res);
+ fb = pipe_buffer_map(&vctx->base, vcdc->feed_buffers[vcdc->cur_buffer],
+ PIPE_MAP_WRITE, &xfer);
+ if (!fb)
+ return;
+ fb->stat = VIRGL_VIDEO_ENCODE_STAT_NOT_STARTED;
+ fb->coded_size = 0;
+ pipe_buffer_unmap(&vctx->base, xfer);
+ *feedback = vres;
+
+ /*
+ * These objects do not need to be transferred manually:
+ * source - corresponds to VASurface in VA-API
+ * target - corresponds to VACodedBuffer in VA-API
+ */
+
+ virgl_encode_encode_bitstream(vctx, vcdc, virgl_video_buffer(source),
+ virgl_resource(target));
+}
+
static void virgl_video_end_frame(struct pipe_video_codec *codec,
struct pipe_video_buffer *target,
struct pipe_picture_desc *picture)
static void virgl_video_flush(struct pipe_video_codec *codec)
{
- (void)codec;
+ struct pipe_context *ctx = codec->context;
+ struct pipe_fence_handle *fence = NULL;
+
+ ctx->flush(ctx, &fence, 0);
+ if (fence) {
+ ctx->screen->fence_finish(ctx->screen, NULL, fence, PIPE_TIMEOUT_INFINITE);
+ ctx->screen->fence_reference(ctx->screen, &fence, NULL);
+ }
}
static void virgl_video_get_feedback(struct pipe_video_codec *codec,
void *feedback,
unsigned *size)
{
- (void)codec;
- (void)feedback;
- (void)size;
+ struct virgl_video_codec *vcdc = virgl_video_codec(codec);
+ struct virgl_context *vctx = vcdc->vctx;
+ struct virgl_screen *vs = virgl_screen(vctx->base.screen);
+ struct virgl_resource *vres = feedback;
+ struct virgl_video_encode_feedback *fb;
+ struct pipe_transfer *xfer;
+
+ if (!feedback || !size)
+ return;
+
+ vs->vws->resource_wait(vs->vws, vres->hw_res);
+ fb = pipe_buffer_map(&vctx->base, &vres->b, PIPE_MAP_READ, &xfer);
+ if (!fb)
+ return;
+ if (fb->stat == VIRGL_VIDEO_ENCODE_STAT_SUCCESS) {
+ *size = fb->coded_size;
+ } else {
+ *size = 0;
+ if (virgl_debug & VIRGL_DEBUG_VIDEO) {
+ debug_printf("unexpected encode feedback: %u\n", fb->stat);
+ }
+ }
+ pipe_buffer_unmap(&vctx->base, xfer);
}
static void virgl_video_destroy_codec(struct pipe_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);
+ if (codec->entrypoint != PIPE_VIDEO_ENTRYPOINT_ENCODE) {
+ pipe_resource_reference(&vcdc->bs_buffers[i], NULL);
+ } else {
+ pipe_resource_reference(&vcdc->feed_buffers[i], NULL);
+ }
pipe_resource_reference(&vcdc->desc_buffers[i], NULL);
}
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:
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.encode_bitstream = virgl_video_encode_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));
+ if (templ->entrypoint != PIPE_VIDEO_ENTRYPOINT_ENCODE) {
+ vcdc->bs_buffers[i] = pipe_buffer_create(ctx->screen,
+ PIPE_BIND_CUSTOM, PIPE_USAGE_STAGING,
+ BS_BUF_DEFAULT_SIZE(width, height));
+ } else {
+ vcdc->feed_buffers[i] = pipe_buffer_create(ctx->screen,
+ PIPE_BIND_CUSTOM, PIPE_USAGE_STAGING,
+ sizeof(struct virgl_video_encode_feedback));
+ }
vcdc->desc_buffers[i] = pipe_buffer_create(ctx->screen,
PIPE_BIND_CUSTOM, PIPE_USAGE_STAGING,
};
+struct virgl_enc_quality_modes {
+ uint32_t level;
+ uint32_t preset_mode;
+ uint32_t pre_encode_mode;
+ uint32_t vbaq_mode;
+};
+
/* H.264 sequence parameter set */
struct virgl_h264_sps {
uint8_t level_idc;
uint8_t reserved[2];
};
+struct virgl_h264_enc_seq_param
+{
+ uint32_t enc_constraint_set_flags;
+ uint32_t enc_frame_cropping_flag;
+ uint32_t enc_frame_crop_left_offset;
+ uint32_t enc_frame_crop_right_offset;
+ uint32_t enc_frame_crop_top_offset;
+ uint32_t enc_frame_crop_bottom_offset;
+ uint32_t pic_order_cnt_type;
+ uint32_t num_temporal_layers;
+ uint32_t vui_parameters_present_flag;
+ struct {
+ uint32_t aspect_ratio_info_present_flag: 1;
+ uint32_t timing_info_present_flag: 1;
+ uint32_t reserved:30;
+ } vui_flags;
+ uint32_t aspect_ratio_idc;
+ uint32_t sar_width;
+ uint32_t sar_height;
+ uint32_t num_units_in_tick;
+ uint32_t time_scale;
+};
+
+struct virgl_h264_enc_rate_control
+{
+ uint32_t target_bitrate;
+ uint32_t peak_bitrate;
+ uint32_t frame_rate_num;
+ uint32_t frame_rate_den;
+ uint32_t vbv_buffer_size;
+ uint32_t vbv_buf_lv;
+ uint32_t target_bits_picture;
+ uint32_t peak_bits_picture_integer;
+ uint32_t peak_bits_picture_fraction;
+ uint32_t fill_data_enable;
+ uint32_t skip_frame_enable;
+ uint32_t enforce_hrd;
+ uint32_t max_au_size;
+ uint32_t max_qp;
+ uint32_t min_qp;
+
+ uint8_t rate_ctrl_method; /* see enum pipe_h2645_enc_rate_control_method */
+ uint8_t reserved[3];
+};
+
+struct virgl_h264_enc_motion_estimation
+{
+ uint32_t motion_est_quarter_pixel;
+ uint32_t enc_disable_sub_mode;
+ uint32_t lsmvert;
+ uint32_t enc_en_ime_overw_dis_subm;
+ uint32_t enc_ime_overw_dis_subm_no;
+ uint32_t enc_ime2_search_range_x;
+ uint32_t enc_ime2_search_range_y;
+};
+
+struct virgl_h264_enc_pic_control
+{
+ uint32_t enc_cabac_enable;
+ uint32_t enc_cabac_init_idc;
+};
+
+struct virgl_h264_slice_descriptor
+{
+ uint32_t macroblock_address;
+ uint32_t num_macroblocks;
+
+ uint8_t slice_type; /* see enum pipe_h264_slice_type */
+ uint8_t reserved[3];
+};
+
+struct virgl_h264_enc_picture_desc
+{
+ struct virgl_base_picture_desc base;
+
+ struct virgl_h264_enc_seq_param seq;
+ struct virgl_h264_enc_rate_control rate_ctrl[4];
+ struct virgl_h264_enc_motion_estimation motion_est;
+ struct virgl_h264_enc_pic_control pic_ctrl;
+
+ uint32_t intra_idr_period;
+
+ uint32_t quant_i_frames;
+ uint32_t quant_p_frames;
+ uint32_t quant_b_frames;
+
+ uint32_t frame_num;
+ uint32_t frame_num_cnt;
+ uint32_t p_remain;
+ uint32_t i_remain;
+ uint32_t idr_pic_id;
+ uint32_t gop_cnt;
+ uint32_t pic_order_cnt;
+ uint32_t num_ref_idx_l0_active_minus1;
+ uint32_t num_ref_idx_l1_active_minus1;
+ uint32_t ref_idx_l0_list[32];
+ uint8_t l0_is_long_term[32];
+ uint32_t ref_idx_l1_list[32];
+ uint8_t l1_is_long_term[32];
+ uint32_t gop_size;
+ struct virgl_enc_quality_modes quality_modes;
+
+ uint32_t num_slice_descriptors;
+ struct virgl_h264_slice_descriptor slices_descriptors[128];
+
+ uint8_t picture_type; /* see enum pipe_h2645_enc_picture_type */
+ uint8_t not_referenced;
+ uint8_t is_ltr;
+ uint8_t enable_vui;
+
+ uint32_t ltr_index;
+};
+
+
struct virgl_h265_sps
{
uint32_t pic_width_in_luma_samples;
struct virgl_h264_picture_desc h264;
struct virgl_h265_picture_desc h265;
struct virgl_mpeg4_picture_desc mpeg4;
+ struct virgl_h264_enc_picture_desc h264_enc;
+};
+
+enum virgl_video_encode_stat {
+ VIRGL_VIDEO_ENCODE_STAT_NOT_STARTED = 0,
+ VIRGL_VIDEO_ENCODE_STAT_IN_PROGRESS,
+ VIRGL_VIDEO_ENCODE_STAT_SUCCESS,
+ VIRGL_VIDEO_ENCODE_STAT_FAILURE,
+};
+
+struct virgl_video_encode_feedback {
+ uint8_t stat; /* see enum virgl_video_encode_stat */
+ uint8_t reserved[3];
+
+ uint32_t coded_size; /* size of encoded data in bytes */
};
#endif /* VIRGL_VIDEO_HW_H */