From a7469a9ffd3d5b8dc3d59ed9a370ffd79122f2df Mon Sep 17 00:00:00 2001 From: David Rosca Date: Tue, 25 Jun 2024 16:18:34 +0200 Subject: [PATCH] frontends/va: Rework EFC logic Currently EFC would be used for every RGB->NV12 conversion, even if the target surface wasn't going to be used as encode input. Also another issue is that there may be multiple conversions from the same source surface before the encode operation (this is the case with B-frames when using one source surface). EFC is now used only if the postproc conversion is the last postproc operation immediately before encoding. Until it's been observed that this is the case, the shader conversion is also applied as a fallback in case EFC could not be used. Tested with (fixes EFC + H264 with B-frames on radeonsi): ffmpeg -vaapi_device /dev/dri/renderD128 -f lavfi -i testsrc \ -vf hwupload,scale_vaapi=format=nv12 -c:v h264_vaapi -bf 3 \ -vframes 200 out.mp4 Reviewed-by: Thong Thai Part-of: --- src/gallium/frontends/va/picture.c | 13 +++++++-- src/gallium/frontends/va/postproc.c | 38 ++++++++++++++------------- src/gallium/frontends/va/surface.c | 2 ++ src/gallium/frontends/va/va_private.h | 7 +++-- 4 files changed, 38 insertions(+), 22 deletions(-) diff --git a/src/gallium/frontends/va/picture.c b/src/gallium/frontends/va/picture.c index e7d5cbb86df..ded801fd881 100644 --- a/src/gallium/frontends/va/picture.c +++ b/src/gallium/frontends/va/picture.c @@ -1291,9 +1291,18 @@ vlVaEndPicture(VADriverContextP ctx, VAContextID context_id) getEncParamPresetH265(context); } - context->desc.base.input_format = surf->buffer->buffer_format; + if (surf->efc_surface) { + assert(surf == drv->last_efc_surface); + context->target = surf->efc_surface->buffer; + context->desc.base.input_format = surf->efc_surface->buffer->buffer_format; + context->desc.base.output_format = surf->buffer->buffer_format; + surf->efc_surface = NULL; + drv->last_efc_surface = NULL; + } else { + context->desc.base.input_format = surf->buffer->buffer_format; + context->desc.base.output_format = surf->buffer->buffer_format; + } context->desc.base.input_full_range = surf->full_range; - context->desc.base.output_format = surf->encoder_format; int driver_metadata_support = drv->pipe->screen->get_video_param(drv->pipe->screen, context->decoder->profile, diff --git a/src/gallium/frontends/va/postproc.c b/src/gallium/frontends/va/postproc.c index b3c92218c69..47efbad8e70 100644 --- a/src/gallium/frontends/va/postproc.c +++ b/src/gallium/frontends/va/postproc.c @@ -560,7 +560,17 @@ vlVaHandleVAProcPipelineParameterBufferType(vlVaDriver *drv, vlVaContext *contex src_region = vlVaRegionDefault(param->surface_region, src_surface, &def_src_region); dst_region = vlVaRegionDefault(param->output_region, dst_surface, &def_dst_region); - if (!param->num_filters && + /* EFC can only do one conversion, and it must be the last postproc + * operation immediately before encoding. + * Disable EFC completely if this is not the case. */ + if (drv->last_efc_surface) { + vlVaSurface *surf = drv->last_efc_surface; + surf->efc_surface = NULL; + drv->last_efc_surface = NULL; + drv->efc_count = -1; + } + + if (drv->efc_count >= 0 && !param->num_filters && src_region->width == dst_region->width && src_region->height == dst_region->height && src_region->x == dst_region->x && @@ -572,24 +582,16 @@ vlVaHandleVAProcPipelineParameterBufferType(vlVaDriver *drv, vlVaContext *contex PIPE_VIDEO_PROFILE_UNKNOWN, PIPE_VIDEO_ENTRYPOINT_ENCODE)) { - vlVaSurface *surf = dst_surface; + dst_surface->efc_surface = src_surface; + drv->last_efc_surface = dst_surface; - // EFC will convert the buffer to a format the encoder accepts - if (src_surface->buffer->buffer_format != surf->buffer->buffer_format) { - surf->encoder_format = surf->buffer->buffer_format; - - surf->templat.interlaced = src_surface->templat.interlaced; - surf->templat.buffer_format = src_surface->templat.buffer_format; - surf->buffer->destroy(surf->buffer); - - if (vlVaHandleSurfaceAllocate(drv, surf, &surf->templat, NULL, 0) != VA_STATUS_SUCCESS) - return VA_STATUS_ERROR_ALLOCATION_FAILED; - } - - pipe_resource_reference(&(((struct vl_video_buffer *)(surf->buffer))->resources[0]), ((struct vl_video_buffer *)(src_surface->buffer))->resources[0]); - context->target = surf->buffer; - - return VA_STATUS_SUCCESS; + /* Do the blit for first few conversions as a fallback in case EFC + * could not be used (see above), after that assume EFC can always + * be used and skip the blit. */ + if (drv->efc_count < 16) + drv->efc_count++; + else + return VA_STATUS_SUCCESS; } src = src_surface->buffer; diff --git a/src/gallium/frontends/va/surface.c b/src/gallium/frontends/va/surface.c index 1e78cc874cd..a46c264db72 100644 --- a/src/gallium/frontends/va/surface.c +++ b/src/gallium/frontends/va/surface.c @@ -90,6 +90,8 @@ vlVaDestroySurfaces(VADriverContextP ctx, VASurfaceID *surface_list, int num_sur if (surf->fence && surf->ctx->decoder && surf->ctx->decoder->destroy_fence) surf->ctx->decoder->destroy_fence(surf->ctx->decoder, surf->fence); } + if (drv->last_efc_surface == surf) + drv->last_efc_surface = NULL; util_dynarray_fini(&surf->subpics); FREE(surf); handle_table_remove(drv->htab, surface_list[i]); diff --git a/src/gallium/frontends/va/va_private.h b/src/gallium/frontends/va/va_private.h index 3486fd92767..17ec33439d6 100644 --- a/src/gallium/frontends/va/va_private.h +++ b/src/gallium/frontends/va/va_private.h @@ -303,6 +303,9 @@ typedef struct { char vendor_string[256]; bool has_external_handles; + + int efc_count; + void *last_efc_surface; } vlVaDriver; typedef struct { @@ -399,7 +402,7 @@ typedef struct { unsigned int rt_format; } vlVaConfig; -typedef struct { +typedef struct vlVaSurface { struct pipe_video_buffer templat, *buffer; struct util_dynarray subpics; /* vlVaSubpicture */ vlVaContext *ctx; @@ -408,9 +411,9 @@ typedef struct { unsigned int frame_num_cnt; bool force_flushed; struct pipe_video_buffer *obsolete_buf; - enum pipe_format encoder_format; bool full_range; struct pipe_fence_handle *fence; + struct vlVaSurface *efc_surface; /* input surface for EFC */ } vlVaSurface; typedef struct {