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 <thong.thai@amd.com> Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/29932>
This commit is contained in:
@@ -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,
|
||||
|
@@ -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;
|
||||
|
@@ -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]);
|
||||
|
@@ -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 {
|
||||
|
Reference in New Issue
Block a user