On Sun, 19 Feb 2017 18:46:42 +0000
Mark Thompson <[email protected]> wrote:
> Uses the cl_intel_va_api_media_sharing extension, which supports only
> NV12 surfaces and only mapping from QSV to OpenCL.
No P010?
> ---
> configure | 5 +
> libavutil/hwcontext_opencl.c | 246
> +++++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 251 insertions(+)
>
> diff --git a/configure b/configure
> index a6933288b..10f2fdaf7 100755
> --- a/configure
> +++ b/configure
> @@ -1706,6 +1706,7 @@ HAVE_LIST="
> $TYPES_LIST
> dos_paths
> dxva2_lib
> + intel_opencl_qsv
> intel_opencl_vaapi
> libc_msvcrt
> MMAL_PARAMETER_VIDEO_MAX_NUM_CALLBACKS
> @@ -4826,6 +4827,10 @@ enabled vaapi &&
> if enabled opencl && enabled vaapi ; then
> check_type "CL/cl_intel.h" "clCreateImageFromFdINTEL_fn" &&
> enable intel_opencl_vaapi
> + if enabled libmfx ; then
> + check_type "CL/cl.h CL/va_ext.h"
> "clCreateFromVA_APIMediaSurfaceINTEL_fn" &&
> + enable intel_opencl_qsv
> + fi
> fi
>
> enabled vdpau &&
> diff --git a/libavutil/hwcontext_opencl.c b/libavutil/hwcontext_opencl.c
> index 7d14052b8..f380b35af 100644
> --- a/libavutil/hwcontext_opencl.c
> +++ b/libavutil/hwcontext_opencl.c
> @@ -37,6 +37,13 @@
> #include "hwcontext_vaapi.h"
> #endif
>
> +#if HAVE_INTEL_OPENCL_QSV
> +#include <mfx/mfxstructures.h>
> +#include <va/va.h>
> +#include <CL/va_ext.h>
> +#include "hwcontext_vaapi.h"
> +#endif
> +
> // The maximum number of planes in an image. This must be structly
> // less than AV_NUM_DATA_POINTERS because we place the whole-frame
> // reference in a buffer entry after the final plane. For now, four
> @@ -57,6 +64,16 @@ typedef struct OpenCLDeviceContext {
> clCreateImageFromFdINTEL_fn clCreateImageFromFdINTEL;
> clCreateBufferFromFdINTEL_fn clCreateBufferFromFdINTEL;
> #endif
> +
> +#if HAVE_INTEL_OPENCL_QSV
> + int qsv_enabled;
> + clCreateFromVA_APIMediaSurfaceINTEL_fn
> + clCreateFromVA_APIMediaSurfaceINTEL;
> + clEnqueueAcquireVA_APIMediaSurfacesINTEL_fn
> + clEnqueueAcquireVA_APIMediaSurfacesINTEL;
> + clEnqueueReleaseVA_APIMediaSurfacesINTEL_fn
> + clEnqueueReleaseVA_APIMediaSurfacesINTEL;
> +#endif
> } OpenCLDeviceContext;
>
> static void opencl_error_callback(const char *errinfo,
> @@ -325,6 +342,71 @@ static int opencl_device_init(AVHWDeviceContext *hwdev)
> }
> #endif
>
> +#if HAVE_INTEL_OPENCL_QSV
> + {
> + size_t props_size;
> + cl_context_properties *props = NULL;
> + VADisplay va_display;
> + int i;
> +
> + cle = clGetContextInfo(hwctx->context, CL_CONTEXT_PROPERTIES,
> + 0, NULL, &props_size);
> + if (cle != CL_SUCCESS) {
> + av_log(hwdev, AV_LOG_ERROR, "Failed to get context "
> + "properties: %d.\n", cle);
> + goto no_qsv;
> + }
> +
> + props = av_malloc(props_size);
> + if (!props)
> + return AVERROR(ENOMEM);
> +
> + cle = clGetContextInfo(hwctx->context, CL_CONTEXT_PROPERTIES,
> + props_size, props, NULL);
> + if (cle != CL_SUCCESS) {
> + av_log(hwdev, AV_LOG_ERROR, "Failed to get context "
> + "properties: %d.\n", cle);
> + goto no_qsv;
> + }
> +
> + va_display = NULL;
> + for (i = 0; i < (props_size / sizeof(*props) - 1); i++) {
> + if (props[i] == CL_CONTEXT_VA_API_DISPLAY_INTEL) {
> + va_display = (VADisplay)(intptr_t)props[i+1];
> + break;
> + }
> + }
> + if (!va_display) {
> + av_log(hwdev, AV_LOG_ERROR, "Media sharing must be "
> + "enabled on context creation to use QSV to "
> + "OpenCL mapping.\n");
> + goto no_qsv;
> + }
> + if (!vaDisplayIsValid(va_display)) {
> + av_log(hwdev, AV_LOG_ERROR, "A valid VADisplay is "
> + "required on context creation to use QSV to "
> + "OpenCL mapping.\n");
> + goto no_qsv;
> + }
(Why can the display be invalid?)
> +
> + CL_FUNC(clCreateFromVA_APIMediaSurfaceINTEL,
> + "Intel QSV to OpenCL mapping", no_qsv);
> + CL_FUNC(clEnqueueAcquireVA_APIMediaSurfacesINTEL,
> + "Intel QSV in OpenCL acquire", no_qsv);
> + CL_FUNC(clEnqueueReleaseVA_APIMediaSurfacesINTEL,
> + "Intel QSV in OpenCL release", no_qsv);
> +
> + ctx->qsv_enabled = 1;
> + if (0) {
> + no_qsv:
> + av_log(hwdev, AV_LOG_ERROR, "QSV to OpenCL mapping "
> + "not usable.\n");
> + ctx->qsv_enabled = 0;
> + }
> + av_free(props);
> + }
> +#endif
> +
> #undef CL_FUNC
>
> return 0;
> @@ -360,6 +442,30 @@ static int opencl_device_derive(AVHWDeviceContext *ctx,
> err = opencl_device_init(ctx);
> break;
> #endif
> +#if HAVE_INTEL_OPENCL_QSV
> + // The generic code automatically attempts to derive from all
> + // ancestors of the given device, so we can ignore QSV devices
> + // here and just consider the inner device it was derived from.
> + case AV_HWDEVICE_TYPE_VAAPI:
> + {
> + AVVAAPIDeviceContext *src_hwctx = src_ctx->hwctx;
> + cl_context_properties props[5] = {
> + CL_CONTEXT_VA_API_DISPLAY_INTEL,
> + (intptr_t)src_hwctx->display,
> + CL_CONTEXT_INTEROP_USER_SYNC,
> + CL_FALSE,
> + 0,
> + };
> + err = av_dict_set(&opts, "device_extensions",
> + "cl_intel_va_api_media_sharing", 0);
> + if (err >= 0)
> + err = opencl_device_create_internal(ctx, NULL, opts,
> + 0, props);
> + if (err >= 0)
> + err = opencl_device_init(ctx);
> + }
> + break;
> +#endif
> default:
> err = AVERROR(ENOSYS);
> break;
> @@ -1362,6 +1468,141 @@ fail:
>
> #endif
>
> +#if HAVE_INTEL_OPENCL_QSV
> +
> +typedef struct QSVtoOpenCLMapping {
> + int nb_planes;
> + cl_mem plane[MAX_PLANES];
> +} QSVtoOpenCLMapping;
> +
> +static void opencl_unmap_from_qsv(AVHWFramesContext *dst_fc,
> + HWMapDescriptor *hwmap)
> +{
> + QSVtoOpenCLMapping *mapping = hwmap->priv;
> + OpenCLDeviceContext *ctx = dst_fc->device_ctx->internal->priv;
> + cl_event event;
> + cl_int cle;
> + int i;
> +
> + av_log(dst_fc, AV_LOG_DEBUG, "Unmap QSV/VAAPI surface from OpenCL.\n");
> +
> + cle = ctx->clEnqueueReleaseVA_APIMediaSurfacesINTEL(
> + ctx->command_queue, mapping->nb_planes, mapping->plane,
> + 0, NULL, &event);
> + if (cle != CL_SUCCESS) {
> + av_log(dst_fc, AV_LOG_ERROR, "Failed to release surface "
> + "handle: %d.\n", cle);
> + }
> +
> + opencl_wait_events(dst_fc, &event, 1);
> +
> + for (i = 0; i < mapping->nb_planes; i++) {
> + cle = clReleaseMemObject(mapping->plane[i]);
> + if (cle != CL_SUCCESS) {
> + av_log(dst_fc, AV_LOG_ERROR, "Failed to release CL "
> + "image of plane %d of QSV/VAAPI surface.\n", cle);
> + }
> + }
> +
> + av_free(mapping);
> +}
> +
> +static int opencl_map_from_qsv(AVHWFramesContext *dst_fc, AVFrame *dst,
> + const AVFrame *src, int flags)
> +{
> + AVHWFramesContext *src_fc =
> + (AVHWFramesContext*)src->hw_frames_ctx->data;
> + AVOpenCLDeviceContext *dst_dev = dst_fc->device_ctx->hwctx;
> + OpenCLDeviceContext *ctx = dst_fc->device_ctx->internal->priv;
> + QSVtoOpenCLMapping *mapping = NULL;
> + VASurfaceID va_surface;
> + cl_mem_flags cl_flags;
> + cl_event event;
> + cl_int cle;
> + int err, i;
> +
> + if (src->format == AV_PIX_FMT_QSV) {
> + mfxFrameSurface1 *mfx_surface = (mfxFrameSurface1*)src->data[3];
> + va_surface = *(VASurfaceID*)mfx_surface->Data.MemId;
Again the somewhat duplicated and "unportable" MemId access.
> + } else if (src->format == AV_PIX_FMT_VAAPI) {
> + va_surface = (VASurfaceID)(uintptr_t)src->data[3];
> + } else {
> + return AVERROR(ENOSYS);
> + }
> +
> + if (flags & (AV_HWFRAME_MAP_READ | AV_HWFRAME_MAP_WRITE))
> + cl_flags = CL_MEM_READ_WRITE;
> + else if (flags & AV_HWFRAME_MAP_READ)
> + cl_flags = CL_MEM_READ_ONLY;
> + else if (flags & AV_HWFRAME_MAP_WRITE)
> + cl_flags = CL_MEM_WRITE_ONLY;
> + else
> + return AVERROR(EINVAL);
We don't have this flag-mapping somewhere else already?
> +
> + av_log(src_fc, AV_LOG_DEBUG, "Map QSV/VAAPI surface %#x to "
> + "OpenCL.\n", va_surface);
> +
> + mapping = av_mallocz(sizeof(*mapping));
> + if (!mapping) {
> + err = AVERROR(ENOMEM);
> + goto fail;
> + }
> +
> + // The cl_intel_va_api_media_sharing extension only supports NV12
> + // surfaces, so for now there are always exactly two planes.
> + mapping->nb_planes = 2;
> +
> + for (i = 0; i < mapping->nb_planes; i++) {
> + mapping->plane[i] = ctx->clCreateFromVA_APIMediaSurfaceINTEL(
> + dst_dev->context, cl_flags, &va_surface, i, &cle);
> + if (!mapping->plane[i]) {
> + av_log(dst_fc, AV_LOG_ERROR, "Failed to create CL "
> + "image from plane %d of QSV/VAAPI surface "
> + "%#x: %d.\n", i, va_surface, cle);
> + err = AVERROR(EIO);
> + goto fail;
> + }
> +
> + dst->data[i] = (uint8_t*)mapping->plane[i];
> + dst->linesize[i] = src_fc->width;
> + }
> +
> + cle = ctx->clEnqueueAcquireVA_APIMediaSurfacesINTEL(
> + ctx->command_queue, mapping->nb_planes, mapping->plane,
> + 0, NULL, &event);
> + if (cle != CL_SUCCESS) {
> + av_log(dst_fc, AV_LOG_ERROR, "Failed to acquire surface "
> + "handle: %d.\n", cle);
> + err = AVERROR(EIO);
> + goto fail;
> + }
> +
> + err = opencl_wait_events(dst_fc, &event, 1);
> + if (err < 0)
> + goto fail;
> +
> + err = ff_hwframe_map_create(dst->hw_frames_ctx, dst, src,
> + &opencl_unmap_from_qsv, mapping);
> + if (err < 0)
> + goto fail;
> +
> + dst->width = src->width;
> + dst->height = src->height;
> +
> + return 0;
> +
> +fail:
> + if (mapping) {
> + for (i = 0; i < mapping->nb_planes; i++)
> + if (mapping->plane[i])
> + clReleaseMemObject(mapping->plane[i]);
> + }
> + av_freep(&mapping);
> + return err;
> +}
> +
> +#endif
> +
>
> static int opencl_map_to(AVHWFramesContext *hwfc, AVFrame *dst,
> const AVFrame *src, int flags)
> @@ -1372,6 +1613,11 @@ static int opencl_map_to(AVHWFramesContext *hwfc,
> AVFrame *dst,
> case AV_PIX_FMT_VAAPI:
> return opencl_map_from_vaapi(hwfc, dst, src, flags);
> #endif
> +#if HAVE_INTEL_OPENCL_QSV
> + case AV_PIX_FMT_QSV:
> + case AV_PIX_FMT_VAAPI:
> + return opencl_map_from_qsv(hwfc, dst, src, flags);
> +#endif
> default:
> return AVERROR(ENOSYS);
> }
Can't comment much. The ifdeffery and complexity of hwcontext_opencl
seems a bit worrying, maybe calling for a more structured approach.
_______________________________________________
libav-devel mailing list
[email protected]
https://lists.libav.org/mailman/listinfo/libav-devel