[FFmpeg-devel] [PATCH 03/15] hwcontext_opencl: VAAPI to OpenCL mapping for Intel i965+beignet
Mark Thompson
sw at jkqxz.net
Tue Nov 14 21:47:18 EET 2017
Supports all surface formats in common between the two.
---
configure | 6 +
libavutil/hwcontext_opencl.c | 298 +++++++++++++++++++++++++++++++++++++++++++
2 files changed, 304 insertions(+)
diff --git a/configure b/configure
index 167db274f1..dcdb5fee1f 100755
--- a/configure
+++ b/configure
@@ -2121,6 +2121,7 @@ HAVE_LIST="
$TYPES_LIST
makeinfo
makeinfo_html
+ opencl_vaapi_beignet
perl
pod2man
texi2html
@@ -6150,6 +6151,11 @@ enabled vaapi &&
check_cpp_condition "va/va.h" "VA_CHECK_VERSION(1, 0, 0)" &&
enable vaapi_1
+if enabled_all opencl vaapi ; then
+ check_type "CL/cl_intel.h" "clCreateImageFromFdINTEL_fn" &&
+ enable opencl_vaapi_beignet
+fi
+
enabled vdpau &&
check_cpp_condition vdpau/vdpau.h "defined VDP_DECODER_PROFILE_MPEG4_PART2_ASP" ||
disable vdpau
diff --git a/libavutil/hwcontext_opencl.c b/libavutil/hwcontext_opencl.c
index 0fe25d9500..7f99f5af3f 100644
--- a/libavutil/hwcontext_opencl.c
+++ b/libavutil/hwcontext_opencl.c
@@ -29,6 +29,14 @@
#include "mem.h"
#include "pixdesc.h"
+#if HAVE_OPENCL_VAAPI_BEIGNET
+#include <unistd.h>
+#include <va/va.h>
+#include <va/va_drmcommon.h>
+#include <CL/cl_intel.h>
+#include "hwcontext_vaapi.h"
+#endif
+
typedef struct OpenCLDeviceContext {
// Default command queue to use for transfer/mapping operations on
@@ -41,6 +49,10 @@ typedef struct OpenCLDeviceContext {
cl_platform_id platform_id;
// Platform/device-specific functions.
+#if HAVE_OPENCL_VAAPI_BEIGNET
+ int vaapi_mapping_usable;
+ clCreateImageFromFdINTEL_fn clCreateImageFromFdINTEL;
+#endif
} OpenCLDeviceContext;
typedef struct OpenCLFramesContext {
@@ -589,6 +601,40 @@ static int opencl_device_init(AVHWDeviceContext *hwdev)
return AVERROR(EIO);
}
+#define CL_FUNC(name, desc) do { \
+ if (fail) \
+ break; \
+ priv->name = clGetExtensionFunctionAddressForPlatform( \
+ priv->platform_id, #name); \
+ if (!priv->name) { \
+ av_log(hwdev, AV_LOG_VERBOSE, \
+ desc " function not found (%s).\n", #name); \
+ fail = 1; \
+ } else { \
+ av_log(hwdev, AV_LOG_VERBOSE, \
+ desc " function found (%s).\n", #name); \
+ } \
+ } while (0)
+
+#if HAVE_OPENCL_VAAPI_BEIGNET
+ {
+ int fail = 0;
+
+ CL_FUNC(clCreateImageFromFdINTEL,
+ "Intel DRM to OpenCL image mapping");
+
+ if (fail) {
+ av_log(hwdev, AV_LOG_WARNING, "VAAPI to OpenCL mapping "
+ "not usable.\n");
+ priv->vaapi_mapping_usable = 0;
+ } else {
+ priv->vaapi_mapping_usable = 1;
+ }
+ }
+#endif
+
+#undef CL_FUNC
+
return 0;
}
@@ -606,6 +652,52 @@ static void opencl_device_uninit(AVHWDeviceContext *hwdev)
}
}
+static int opencl_device_derive(AVHWDeviceContext *hwdev,
+ AVHWDeviceContext *src_ctx,
+ int flags)
+{
+ int err;
+ switch (src_ctx->type) {
+
+#if HAVE_OPENCL_VAAPI_BEIGNET
+ case AV_HWDEVICE_TYPE_VAAPI:
+ {
+ // Surface mapping works via DRM PRIME fds with no special
+ // initialisation required in advance. This just finds the
+ // Beignet ICD by name.
+ AVDictionary *opts = NULL;
+
+ err = av_dict_set(&opts, "platform_vendor", "Intel", 0);
+ if (err >= 0)
+ err = av_dict_set(&opts, "platform_version", "beignet", 0);
+ if (err >= 0) {
+ OpenCLDeviceSelector selector = {
+ .platform_index = -1,
+ .device_index = 0,
+ .context = opts,
+ .enumerate_platforms = &opencl_enumerate_platforms,
+ .filter_platform = &opencl_filter_platform,
+ .enumerate_devices = &opencl_enumerate_devices,
+ .filter_device = NULL,
+ };
+ err = opencl_device_create_internal(hwdev, &selector, NULL);
+ }
+ av_dict_free(&opts);
+ }
+ break;
+#endif
+
+ default:
+ err = AVERROR(ENOSYS);
+ break;
+ }
+
+ if (err < 0)
+ return err;
+
+ return opencl_device_init(hwdev);
+}
+
static int opencl_get_plane_format(enum AVPixelFormat pixfmt,
int plane, int width, int height,
cl_image_format *image_format,
@@ -1263,6 +1355,177 @@ fail:
return err;
}
+#if HAVE_OPENCL_VAAPI_BEIGNET
+
+typedef struct VAAPItoOpenCLMapping {
+ VAImage va_image;
+ VABufferInfo va_buffer_info;
+
+ AVOpenCLFrameDescriptor frame;
+} VAAPItoOpenCLMapping;
+
+static void opencl_unmap_from_vaapi(AVHWFramesContext *src_fc,
+ HWMapDescriptor *hwmap)
+{
+ VAAPItoOpenCLMapping *mapping = hwmap->priv;
+ AVVAAPIDeviceContext *src_dev = src_fc->device_ctx->hwctx;
+ VASurfaceID surface_id;
+ VAStatus vas;
+ cl_int cle;
+ int i;
+
+ surface_id = (VASurfaceID)(uintptr_t)hwmap->source->data[3];
+ av_log(src_fc, AV_LOG_DEBUG, "Unmap VAAPI surface %#x from OpenCL.\n",
+ surface_id);
+
+ for (i = 0; i < mapping->frame.nb_planes; i++) {
+ cle = clReleaseMemObject(mapping->frame.planes[i]);
+ if (cle != CL_SUCCESS) {
+ av_log(src_fc, AV_LOG_ERROR, "Failed to release CL "
+ "buffer of plane %d of VA image %#x (derived "
+ "from surface %#x): %d.\n", i,
+ mapping->va_image.buf, surface_id, cle);
+ }
+ }
+
+ vas = vaReleaseBufferHandle(src_dev->display,
+ mapping->va_image.buf);
+ if (vas != VA_STATUS_SUCCESS) {
+ av_log(src_fc, AV_LOG_ERROR, "Failed to release buffer "
+ "handle of image %#x (derived from surface %#x): "
+ "%d (%s).\n", mapping->va_image.buf, surface_id,
+ vas, vaErrorStr(vas));
+ }
+
+ vas = vaDestroyImage(src_dev->display,
+ mapping->va_image.image_id);
+ if (vas != VA_STATUS_SUCCESS) {
+ av_log(src_fc, AV_LOG_ERROR, "Failed to destroy image "
+ "derived from surface %#x: %d (%s).\n",
+ surface_id, vas, vaErrorStr(vas));
+ }
+
+ av_free(mapping);
+}
+
+static int opencl_map_from_vaapi(AVHWFramesContext *dst_fc, AVFrame *dst,
+ const AVFrame *src, int flags)
+{
+ AVHWFramesContext *src_fc =
+ (AVHWFramesContext*)src->hw_frames_ctx->data;
+ AVVAAPIDeviceContext *src_dev = src_fc->device_ctx->hwctx;
+ AVOpenCLDeviceContext *dst_dev = dst_fc->device_ctx->hwctx;
+ OpenCLDeviceContext *priv = dst_fc->device_ctx->internal->priv;
+ VAAPItoOpenCLMapping *mapping = NULL;
+ VASurfaceID surface_id;
+ VAStatus vas;
+ cl_int cle;
+ int err, p;
+
+ surface_id = (VASurfaceID)(uintptr_t)src->data[3];
+ av_log(src_fc, AV_LOG_DEBUG, "Map VAAPI surface %#x to OpenCL.\n",
+ surface_id);
+
+ mapping = av_mallocz(sizeof(*mapping));
+ if (!mapping)
+ return AVERROR(ENOMEM);
+
+ vas = vaDeriveImage(src_dev->display, surface_id,
+ &mapping->va_image);
+ if (vas != VA_STATUS_SUCCESS) {
+ av_log(src_fc, AV_LOG_ERROR, "Failed to derive image from "
+ "surface %#x: %d (%s).\n",
+ surface_id, vas, vaErrorStr(vas));
+ err = AVERROR(EIO);
+ goto fail;
+ }
+
+ mapping->va_buffer_info.mem_type =
+ VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME;
+
+ vas = vaAcquireBufferHandle(src_dev->display,
+ mapping->va_image.buf,
+ &mapping->va_buffer_info);
+ if (vas != VA_STATUS_SUCCESS) {
+ av_log(src_fc, AV_LOG_ERROR, "Failed to get buffer "
+ "handle from image %#x (derived from surface %#x): "
+ "%d (%s).\n", mapping->va_image.buf, surface_id,
+ vas, vaErrorStr(vas));
+ vaDestroyImage(src_dev->display, mapping->va_image.buf);
+ err = AVERROR(EIO);
+ goto fail_derived;
+ }
+
+ av_log(dst_fc, AV_LOG_DEBUG, "DRM PRIME fd is %ld.\n",
+ mapping->va_buffer_info.handle);
+
+ mapping->frame.nb_planes = mapping->va_image.num_planes;
+ for (p = 0; p < mapping->frame.nb_planes; p++) {
+ cl_import_image_info_intel image_info = {
+ .fd = mapping->va_buffer_info.handle,
+ .size = mapping->va_buffer_info.mem_size,
+ .type = CL_MEM_OBJECT_IMAGE2D,
+ .offset = mapping->va_image.offsets[p],
+ .row_pitch = mapping->va_image.pitches[p],
+ };
+ cl_image_desc image_desc;
+
+ err = opencl_get_plane_format(src_fc->sw_format, p,
+ mapping->va_image.width,
+ mapping->va_image.height,
+ &image_info.fmt,
+ &image_desc);
+ if (err < 0) {
+ av_log(dst_fc, AV_LOG_ERROR, "VA %#x (derived from "
+ "surface %#x) has invalid parameters: %d.\n",
+ mapping->va_image.buf, surface_id, err);
+ goto fail_mapped;
+ }
+ image_info.width = image_desc.image_width;
+ image_info.height = image_desc.image_height;
+
+ mapping->frame.planes[p] =
+ priv->clCreateImageFromFdINTEL(dst_dev->context,
+ &image_info, &cle);
+ if (!mapping->frame.planes[p]) {
+ av_log(dst_fc, AV_LOG_ERROR, "Failed to create CL image "
+ "from plane %d of VA image %#x (derived from "
+ "surface %#x): %d.\n", p,
+ mapping->va_image.buf, surface_id, cle);
+ err = AVERROR(EIO);
+ goto fail_mapped;
+ }
+
+ dst->data[p] = (uint8_t*)mapping->frame.planes[p];
+ }
+
+ err = ff_hwframe_map_create(src->hw_frames_ctx,
+ dst, src, &opencl_unmap_from_vaapi,
+ mapping);
+ if (err < 0)
+ goto fail_mapped;
+
+ dst->width = src->width;
+ dst->height = src->height;
+
+ return 0;
+
+fail_mapped:
+ for (p = 0; p < mapping->frame.nb_planes; p++) {
+ if (mapping->frame.planes[p])
+ clReleaseMemObject(mapping->frame.planes[p]);
+ }
+ vaReleaseBufferHandle(src_dev->display, mapping->va_image.buf);
+fail_derived:
+ vaDestroyImage(src_dev->display, mapping->va_image.image_id);
+fail:
+ av_freep(&mapping);
+ return err;
+}
+
+#endif
+
+
static int opencl_map_from(AVHWFramesContext *hwfc, AVFrame *dst,
const AVFrame *src, int flags)
{
@@ -1272,6 +1535,38 @@ static int opencl_map_from(AVHWFramesContext *hwfc, AVFrame *dst,
return opencl_map_frame(hwfc, dst, src, flags);
}
+static int opencl_map_to(AVHWFramesContext *hwfc, AVFrame *dst,
+ const AVFrame *src, int flags)
+{
+ OpenCLDeviceContext *priv = hwfc->device_ctx->internal->priv;
+ av_assert0(dst->format == AV_PIX_FMT_OPENCL);
+ switch (src->format) {
+#if HAVE_OPENCL_VAAPI_BEIGNET
+ case AV_PIX_FMT_VAAPI:
+ if (priv->vaapi_mapping_usable)
+ return opencl_map_from_vaapi(hwfc, dst, src, flags);
+#endif
+ }
+ return AVERROR(ENOSYS);
+}
+
+static int opencl_frames_derive_to(AVHWFramesContext *dst_fc,
+ AVHWFramesContext *src_fc, int flags)
+{
+ OpenCLDeviceContext *priv = dst_fc->device_ctx->internal->priv;
+ switch (src_fc->device_ctx->type) {
+#if HAVE_OPENCL_VAAPI_BEIGNET
+ case AV_HWDEVICE_TYPE_VAAPI:
+ if (!priv->vaapi_mapping_usable)
+ return AVERROR(ENOSYS);
+ break;
+#endif
+ default:
+ return AVERROR(ENOSYS);
+ }
+ return opencl_frames_init_command_queue(dst_fc);
+}
+
const HWContextType ff_hwcontext_type_opencl = {
.type = AV_HWDEVICE_TYPE_OPENCL,
.name = "OpenCL",
@@ -1282,6 +1577,7 @@ const HWContextType ff_hwcontext_type_opencl = {
.frames_priv_size = sizeof(OpenCLFramesContext),
.device_create = &opencl_device_create,
+ .device_derive = &opencl_device_derive,
.device_init = &opencl_device_init,
.device_uninit = &opencl_device_uninit,
@@ -1295,6 +1591,8 @@ const HWContextType ff_hwcontext_type_opencl = {
.transfer_data_from = &opencl_transfer_data_from,
.map_from = &opencl_map_from,
+ .map_to = &opencl_map_to,
+ .frames_derive_to = &opencl_frames_derive_to,
.pix_fmts = (const enum AVPixelFormat[]) {
AV_PIX_FMT_OPENCL,
--
2.11.0
More information about the ffmpeg-devel
mailing list