ffmpeg/libavutil/hwcontext_opencl.c
Lynne 2e08b39444
hwcontext: add av_hwdevice_ctx_create_derived_opts
This allows for users who derive devices to set options for the
new device context they derive.
The main use case of this is to allow users to enable extensions
(such as surface drawing extensions) in Vulkan while deriving from
the device their frames are on. That way, users don't need to write
any initialization code themselves, since the Vulkan spec invalidates
mixing instances, physical devices and active devices.
Apart from Vulkan, other hwcontexts ignore the opts argument since they
don't support options at all (or in VAAPI and OpenCL's case, options are
currently only used for device selection, which device_derive overrides).
2020-05-23 19:07:26 +01:00

2943 lines
95 KiB
C

/*
* This file is part of FFmpeg.
*
* FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#define CL_USE_DEPRECATED_OPENCL_1_2_APIS
#include <string.h>
#include "config.h"
#include "avassert.h"
#include "avstring.h"
#include "common.h"
#include "hwcontext.h"
#include "hwcontext_internal.h"
#include "hwcontext_opencl.h"
#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
#if HAVE_OPENCL_DRM_BEIGNET
#include <unistd.h>
#include <CL/cl_intel.h>
#include "hwcontext_drm.h"
#endif
#if HAVE_OPENCL_VAAPI_INTEL_MEDIA
#if CONFIG_LIBMFX
#include <mfx/mfxstructures.h>
#endif
#include <va/va.h>
#include <CL/cl_va_api_media_sharing_intel.h>
#include "hwcontext_vaapi.h"
#endif
#if HAVE_OPENCL_DXVA2
#define COBJMACROS
#include <CL/cl_dx9_media_sharing.h>
#include <dxva2api.h>
#include "hwcontext_dxva2.h"
#endif
#if HAVE_OPENCL_D3D11
#include <CL/cl_d3d11.h>
#include "hwcontext_d3d11va.h"
#endif
#if HAVE_OPENCL_DRM_ARM
#include <CL/cl_ext.h>
#include <drm_fourcc.h>
#include "hwcontext_drm.h"
#endif
typedef struct OpenCLDeviceContext {
// Default command queue to use for transfer/mapping operations on
// the device. If the user supplies one, this is a reference to it.
// Otherwise, it is newly-created.
cl_command_queue command_queue;
// The platform the context exists on. This is needed to query and
// retrieve extension functions.
cl_platform_id platform_id;
// Platform/device-specific functions.
#if HAVE_OPENCL_DRM_BEIGNET
int beignet_drm_mapping_usable;
clCreateImageFromFdINTEL_fn clCreateImageFromFdINTEL;
#endif
#if HAVE_OPENCL_VAAPI_INTEL_MEDIA
int qsv_mapping_usable;
clCreateFromVA_APIMediaSurfaceINTEL_fn
clCreateFromVA_APIMediaSurfaceINTEL;
clEnqueueAcquireVA_APIMediaSurfacesINTEL_fn
clEnqueueAcquireVA_APIMediaSurfacesINTEL;
clEnqueueReleaseVA_APIMediaSurfacesINTEL_fn
clEnqueueReleaseVA_APIMediaSurfacesINTEL;
#endif
#if HAVE_OPENCL_DXVA2
int dxva2_mapping_usable;
cl_dx9_media_adapter_type_khr dx9_media_adapter_type;
clCreateFromDX9MediaSurfaceKHR_fn
clCreateFromDX9MediaSurfaceKHR;
clEnqueueAcquireDX9MediaSurfacesKHR_fn
clEnqueueAcquireDX9MediaSurfacesKHR;
clEnqueueReleaseDX9MediaSurfacesKHR_fn
clEnqueueReleaseDX9MediaSurfacesKHR;
#endif
#if HAVE_OPENCL_D3D11
int d3d11_mapping_usable;
clCreateFromD3D11Texture2DKHR_fn
clCreateFromD3D11Texture2DKHR;
clEnqueueAcquireD3D11ObjectsKHR_fn
clEnqueueAcquireD3D11ObjectsKHR;
clEnqueueReleaseD3D11ObjectsKHR_fn
clEnqueueReleaseD3D11ObjectsKHR;
#endif
#if HAVE_OPENCL_DRM_ARM
int drm_arm_mapping_usable;
#endif
} OpenCLDeviceContext;
typedef struct OpenCLFramesContext {
// Command queue used for transfer/mapping operations on this frames
// context. If the user supplies one, this is a reference to it.
// Otherwise, it is a reference to the default command queue for the
// device.
cl_command_queue command_queue;
#if HAVE_OPENCL_DXVA2 || HAVE_OPENCL_D3D11
// For mapping APIs which have separate creation and acquire/release
// steps, this stores the OpenCL memory objects corresponding to each
// frame.
int nb_mapped_frames;
AVOpenCLFrameDescriptor *mapped_frames;
#endif
} OpenCLFramesContext;
static void CL_CALLBACK opencl_error_callback(const char *errinfo,
const void *private_info,
size_t cb,
void *user_data)
{
AVHWDeviceContext *ctx = user_data;
av_log(ctx, AV_LOG_ERROR, "OpenCL error: %s\n", errinfo);
}
static void opencl_device_free(AVHWDeviceContext *hwdev)
{
AVOpenCLDeviceContext *hwctx = hwdev->hwctx;
cl_int cle;
cle = clReleaseContext(hwctx->context);
if (cle != CL_SUCCESS) {
av_log(hwdev, AV_LOG_ERROR, "Failed to release OpenCL "
"context: %d.\n", cle);
}
}
static struct {
const char *key;
cl_platform_info name;
} opencl_platform_params[] = {
{ "platform_profile", CL_PLATFORM_PROFILE },
{ "platform_version", CL_PLATFORM_VERSION },
{ "platform_name", CL_PLATFORM_NAME },
{ "platform_vendor", CL_PLATFORM_VENDOR },
{ "platform_extensions", CL_PLATFORM_EXTENSIONS },
};
static struct {
const char *key;
cl_device_info name;
} opencl_device_params[] = {
{ "device_name", CL_DEVICE_NAME },
{ "device_vendor", CL_DEVICE_VENDOR },
{ "driver_version", CL_DRIVER_VERSION },
{ "device_version", CL_DEVICE_VERSION },
{ "device_profile", CL_DEVICE_PROFILE },
{ "device_extensions", CL_DEVICE_EXTENSIONS },
};
static struct {
const char *key;
cl_device_type type;
} opencl_device_types[] = {
{ "cpu", CL_DEVICE_TYPE_CPU },
{ "gpu", CL_DEVICE_TYPE_GPU },
{ "accelerator", CL_DEVICE_TYPE_ACCELERATOR },
{ "custom", CL_DEVICE_TYPE_CUSTOM },
{ "default", CL_DEVICE_TYPE_DEFAULT },
{ "all", CL_DEVICE_TYPE_ALL },
};
static char *opencl_get_platform_string(cl_platform_id platform_id,
cl_platform_info key)
{
char *str;
size_t size;
cl_int cle;
cle = clGetPlatformInfo(platform_id, key, 0, NULL, &size);
if (cle != CL_SUCCESS)
return NULL;
str = av_malloc(size);
if (!str)
return NULL;
cle = clGetPlatformInfo(platform_id, key, size, str, &size);
if (cle != CL_SUCCESS) {
av_free(str);
return NULL;
}
av_assert0(strlen(str) + 1 == size);
return str;
}
static char *opencl_get_device_string(cl_device_id device_id,
cl_device_info key)
{
char *str;
size_t size;
cl_int cle;
cle = clGetDeviceInfo(device_id, key, 0, NULL, &size);
if (cle != CL_SUCCESS)
return NULL;
str = av_malloc(size);
if (!str)
return NULL;
cle = clGetDeviceInfo(device_id, key, size, str, &size);
if (cle != CL_SUCCESS) {
av_free(str);
return NULL;
}
av_assert0(strlen(str) + 1== size);
return str;
}
static int opencl_check_platform_extension(cl_platform_id platform_id,
const char *name)
{
char *str;
int found = 0;
str = opencl_get_platform_string(platform_id,
CL_PLATFORM_EXTENSIONS);
if (str && strstr(str, name))
found = 1;
av_free(str);
return found;
}
static int opencl_check_device_extension(cl_device_id device_id,
const char *name)
{
char *str;
int found = 0;
str = opencl_get_device_string(device_id,
CL_DEVICE_EXTENSIONS);
if (str && strstr(str, name))
found = 1;
av_free(str);
return found;
}
static av_unused int opencl_check_extension(AVHWDeviceContext *hwdev,
const char *name)
{
AVOpenCLDeviceContext *hwctx = hwdev->hwctx;
OpenCLDeviceContext *priv = hwdev->internal->priv;
if (opencl_check_platform_extension(priv->platform_id, name)) {
av_log(hwdev, AV_LOG_DEBUG,
"%s found as platform extension.\n", name);
return 1;
}
if (opencl_check_device_extension(hwctx->device_id, name)) {
av_log(hwdev, AV_LOG_DEBUG,
"%s found as device extension.\n", name);
return 1;
}
return 0;
}
static int opencl_enumerate_platforms(AVHWDeviceContext *hwdev,
cl_uint *nb_platforms,
cl_platform_id **platforms,
void *context)
{
cl_int cle;
cle = clGetPlatformIDs(0, NULL, nb_platforms);
if (cle != CL_SUCCESS) {
av_log(hwdev, AV_LOG_ERROR, "Failed to get number of "
"OpenCL platforms: %d.\n", cle);
return AVERROR(ENODEV);
}
av_log(hwdev, AV_LOG_DEBUG, "%u OpenCL platforms found.\n",
*nb_platforms);
*platforms = av_malloc_array(*nb_platforms, sizeof(**platforms));
if (!*platforms)
return AVERROR(ENOMEM);
cle = clGetPlatformIDs(*nb_platforms, *platforms, NULL);
if (cle != CL_SUCCESS) {
av_log(hwdev, AV_LOG_ERROR, "Failed to get list of OpenCL "
"platforms: %d.\n", cle);
av_freep(platforms);
return AVERROR(ENODEV);
}
return 0;
}
static int opencl_filter_platform(AVHWDeviceContext *hwdev,
cl_platform_id platform_id,
const char *platform_name,
void *context)
{
AVDictionary *opts = context;
const AVDictionaryEntry *param;
char *str;
int i, ret = 0;
for (i = 0; i < FF_ARRAY_ELEMS(opencl_platform_params); i++) {
param = av_dict_get(opts, opencl_platform_params[i].key,
NULL, 0);
if (!param)
continue;
str = opencl_get_platform_string(platform_id,
opencl_platform_params[i].name);
if (!str) {
av_log(hwdev, AV_LOG_ERROR, "Failed to query %s "
"of platform \"%s\".\n",
opencl_platform_params[i].key, platform_name);
return AVERROR_UNKNOWN;
}
if (!av_stristr(str, param->value)) {
av_log(hwdev, AV_LOG_DEBUG, "%s does not match (\"%s\").\n",
param->key, str);
ret = 1;
}
av_free(str);
}
return ret;
}
static int opencl_enumerate_devices(AVHWDeviceContext *hwdev,
cl_platform_id platform_id,
const char *platform_name,
cl_uint *nb_devices,
cl_device_id **devices,
void *context)
{
cl_int cle;
cle = clGetDeviceIDs(platform_id, CL_DEVICE_TYPE_ALL,
0, NULL, nb_devices);
if (cle == CL_DEVICE_NOT_FOUND) {
av_log(hwdev, AV_LOG_DEBUG, "No devices found "
"on platform \"%s\".\n", platform_name);
*nb_devices = 0;
return 0;
} else if (cle != CL_SUCCESS) {
av_log(hwdev, AV_LOG_ERROR, "Failed to get number of devices "
"on platform \"%s\": %d.\n", platform_name, cle);
return AVERROR(ENODEV);
}
av_log(hwdev, AV_LOG_DEBUG, "%u OpenCL devices found on "
"platform \"%s\".\n", *nb_devices, platform_name);
*devices = av_malloc_array(*nb_devices, sizeof(**devices));
if (!*devices)
return AVERROR(ENOMEM);
cle = clGetDeviceIDs(platform_id, CL_DEVICE_TYPE_ALL,
*nb_devices, *devices, NULL);
if (cle != CL_SUCCESS) {
av_log(hwdev, AV_LOG_ERROR, "Failed to get list of devices "
"on platform \"%s\": %d.\n", platform_name, cle);
av_freep(devices);
return AVERROR(ENODEV);
}
return 0;
}
static int opencl_filter_device(AVHWDeviceContext *hwdev,
cl_device_id device_id,
const char *device_name,
void *context)
{
AVDictionary *opts = context;
const AVDictionaryEntry *param;
char *str;
int i, ret = 0;
param = av_dict_get(opts, "device_type", NULL, 0);
if (param) {
cl_device_type match_type = 0, device_type;
cl_int cle;
for (i = 0; i < FF_ARRAY_ELEMS(opencl_device_types); i++) {
if (!strcmp(opencl_device_types[i].key, param->value)) {
match_type = opencl_device_types[i].type;
break;
}
}
if (!match_type) {
av_log(hwdev, AV_LOG_ERROR, "Unknown device type %s.\n",
param->value);
return AVERROR(EINVAL);
}
cle = clGetDeviceInfo(device_id, CL_DEVICE_TYPE,
sizeof(device_type), &device_type, NULL);
if (cle != CL_SUCCESS) {
av_log(hwdev, AV_LOG_ERROR, "Failed to query device type "
"of device \"%s\".\n", device_name);
return AVERROR_UNKNOWN;
}
if (!(device_type & match_type)) {
av_log(hwdev, AV_LOG_DEBUG, "device_type does not match.\n");
return 1;
}
}
for (i = 0; i < FF_ARRAY_ELEMS(opencl_device_params); i++) {
param = av_dict_get(opts, opencl_device_params[i].key,
NULL, 0);
if (!param)
continue;
str = opencl_get_device_string(device_id,
opencl_device_params[i].name);
if (!str) {
av_log(hwdev, AV_LOG_ERROR, "Failed to query %s "
"of device \"%s\".\n",
opencl_device_params[i].key, device_name);
return AVERROR_UNKNOWN;
}
if (!av_stristr(str, param->value)) {
av_log(hwdev, AV_LOG_DEBUG, "%s does not match (\"%s\").\n",
param->key, str);
ret = 1;
}
av_free(str);
}
return ret;
}
typedef struct OpenCLDeviceSelector {
int platform_index;
int device_index;
void *context;
int (*enumerate_platforms)(AVHWDeviceContext *hwdev,
cl_uint *nb_platforms,
cl_platform_id **platforms,
void *context);
int (*filter_platform) (AVHWDeviceContext *hwdev,
cl_platform_id platform_id,
const char *platform_name,
void *context);
int (*enumerate_devices) (AVHWDeviceContext *hwdev,
cl_platform_id platform_id,
const char *platform_name,
cl_uint *nb_devices,
cl_device_id **devices,
void *context);
int (*filter_device) (AVHWDeviceContext *hwdev,
cl_device_id device_id,
const char *device_name,
void *context);
} OpenCLDeviceSelector;
static int opencl_device_create_internal(AVHWDeviceContext *hwdev,
const OpenCLDeviceSelector *selector,
cl_context_properties *props)
{
cl_uint nb_platforms;
cl_platform_id *platforms = NULL;
cl_platform_id platform_id;
cl_uint nb_devices;
cl_device_id *devices = NULL;
AVOpenCLDeviceContext *hwctx = hwdev->hwctx;
cl_int cle;
cl_context_properties default_props[3];
char *platform_name_src = NULL,
*device_name_src = NULL;
int err, found, p, d;
av_assert0(selector->enumerate_platforms &&
selector->enumerate_devices);
err = selector->enumerate_platforms(hwdev, &nb_platforms, &platforms,
selector->context);
if (err)
return err;
found = 0;
for (p = 0; p < nb_platforms; p++) {
const char *platform_name;
if (selector->platform_index >= 0 &&
selector->platform_index != p)
continue;
av_freep(&platform_name_src);
platform_name_src = opencl_get_platform_string(platforms[p],
CL_PLATFORM_NAME);
if (platform_name_src)
platform_name = platform_name_src;
else
platform_name = "Unknown Platform";
if (selector->filter_platform) {
err = selector->filter_platform(hwdev, platforms[p],
platform_name,
selector->context);
if (err < 0)
goto fail;
if (err > 0)
continue;
}
err = selector->enumerate_devices(hwdev, platforms[p], platform_name,
&nb_devices, &devices,
selector->context);
if (err < 0)
continue;
for (d = 0; d < nb_devices; d++) {
const char *device_name;
if (selector->device_index >= 0 &&
selector->device_index != d)
continue;
av_freep(&device_name_src);
device_name_src = opencl_get_device_string(devices[d],
CL_DEVICE_NAME);
if (device_name_src)
device_name = device_name_src;
else
device_name = "Unknown Device";
if (selector->filter_device) {
err = selector->filter_device(hwdev, devices[d],
device_name,
selector->context);
if (err < 0)
goto fail;
if (err > 0)
continue;
}
av_log(hwdev, AV_LOG_VERBOSE, "%d.%d: %s / %s\n", p, d,
platform_name, device_name);
++found;
platform_id = platforms[p];
hwctx->device_id = devices[d];
}
av_freep(&devices);
}
if (found == 0) {
av_log(hwdev, AV_LOG_ERROR, "No matching devices found.\n");
err = AVERROR(ENODEV);
goto fail;
}
if (found > 1) {
av_log(hwdev, AV_LOG_ERROR, "More than one matching device found.\n");
err = AVERROR(ENODEV);
goto fail;
}
if (!props) {
props = default_props;
default_props[0] = CL_CONTEXT_PLATFORM;
default_props[1] = (intptr_t)platform_id;
default_props[2] = 0;
} else {
if (props[0] == CL_CONTEXT_PLATFORM && props[1] == 0)
props[1] = (intptr_t)platform_id;
}
hwctx->context = clCreateContext(props, 1, &hwctx->device_id,
&opencl_error_callback, hwdev, &cle);
if (!hwctx->context) {
av_log(hwdev, AV_LOG_ERROR, "Failed to create OpenCL context: "
"%d.\n", cle);
err = AVERROR(ENODEV);
goto fail;
}
hwdev->free = &opencl_device_free;
err = 0;
fail:
av_freep(&platform_name_src);
av_freep(&device_name_src);
av_freep(&platforms);
av_freep(&devices);
return err;
}
static int opencl_device_create(AVHWDeviceContext *hwdev, const char *device,
AVDictionary *opts, int flags)
{
OpenCLDeviceSelector selector = {
.context = opts,
.enumerate_platforms = &opencl_enumerate_platforms,
.filter_platform = &opencl_filter_platform,
.enumerate_devices = &opencl_enumerate_devices,
.filter_device = &opencl_filter_device,
};
if (device && device[0]) {
// Match one or both indices for platform and device.
int d = -1, p = -1, ret;
if (device[0] == '.')
ret = sscanf(device, ".%d", &d);
else
ret = sscanf(device, "%d.%d", &p, &d);
if (ret < 1) {
av_log(hwdev, AV_LOG_ERROR, "Invalid OpenCL platform/device "
"index specification \"%s\".\n", device);
return AVERROR(EINVAL);
}
selector.platform_index = p;
selector.device_index = d;
} else {
selector.platform_index = -1;
selector.device_index = -1;
}
return opencl_device_create_internal(hwdev, &selector, NULL);
}
static int opencl_device_init(AVHWDeviceContext *hwdev)
{
AVOpenCLDeviceContext *hwctx = hwdev->hwctx;
OpenCLDeviceContext *priv = hwdev->internal->priv;
cl_int cle;
if (hwctx->command_queue) {
cle = clRetainCommandQueue(hwctx->command_queue);
if (cle != CL_SUCCESS) {
av_log(hwdev, AV_LOG_ERROR, "Failed to retain external "
"command queue: %d.\n", cle);
return AVERROR(EIO);
}
priv->command_queue = hwctx->command_queue;
} else {
priv->command_queue = clCreateCommandQueue(hwctx->context,
hwctx->device_id,
0, &cle);
if (!priv->command_queue) {
av_log(hwdev, AV_LOG_ERROR, "Failed to create internal "
"command queue: %d.\n", cle);
return AVERROR(EIO);
}
}
cle = clGetDeviceInfo(hwctx->device_id, CL_DEVICE_PLATFORM,
sizeof(priv->platform_id), &priv->platform_id,
NULL);
if (cle != CL_SUCCESS) {
av_log(hwdev, AV_LOG_ERROR, "Failed to determine the OpenCL "
"platform containing the device.\n");
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_DRM_BEIGNET
{
int fail = 0;
CL_FUNC(clCreateImageFromFdINTEL,
"Beignet DRM to OpenCL image mapping");
if (fail) {
av_log(hwdev, AV_LOG_WARNING, "Beignet DRM to OpenCL "
"mapping not usable.\n");
priv->beignet_drm_mapping_usable = 0;
} else {
priv->beignet_drm_mapping_usable = 1;
}
}
#endif
#if HAVE_OPENCL_VAAPI_INTEL_MEDIA
{
size_t props_size;
cl_context_properties *props = NULL;
VADisplay va_display;
const char *va_ext = "cl_intel_va_api_media_sharing";
int i, fail = 0;
if (!opencl_check_extension(hwdev, va_ext)) {
av_log(hwdev, AV_LOG_VERBOSE, "The %s extension is "
"required for QSV to OpenCL mapping.\n", va_ext);
goto no_qsv;
}
cle = clGetContextInfo(hwctx->context, CL_CONTEXT_PROPERTIES,
0, NULL, &props_size);
if (cle != CL_SUCCESS) {
av_log(hwdev, AV_LOG_VERBOSE, "Failed to get context "
"properties: %d.\n", cle);
goto no_qsv;
}
if (props_size == 0) {
av_log(hwdev, AV_LOG_VERBOSE, "Media sharing must be "
"enabled on context creation to use QSV to "
"OpenCL mapping.\n");
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_VERBOSE, "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_VERBOSE, "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_VERBOSE, "A valid VADisplay is "
"required on context creation to use QSV to "
"OpenCL mapping.\n");
goto no_qsv;
}
CL_FUNC(clCreateFromVA_APIMediaSurfaceINTEL,
"Intel QSV to OpenCL mapping");
CL_FUNC(clEnqueueAcquireVA_APIMediaSurfacesINTEL,
"Intel QSV in OpenCL acquire");
CL_FUNC(clEnqueueReleaseVA_APIMediaSurfacesINTEL,
"Intel QSV in OpenCL release");
if (fail) {
no_qsv:
av_log(hwdev, AV_LOG_WARNING, "QSV to OpenCL mapping "
"not usable.\n");
priv->qsv_mapping_usable = 0;
} else {
priv->qsv_mapping_usable = 1;
}
av_free(props);
}
#endif
#if HAVE_OPENCL_DXVA2
{
int fail = 0;
CL_FUNC(clCreateFromDX9MediaSurfaceKHR,
"DXVA2 to OpenCL mapping");
CL_FUNC(clEnqueueAcquireDX9MediaSurfacesKHR,
"DXVA2 in OpenCL acquire");
CL_FUNC(clEnqueueReleaseDX9MediaSurfacesKHR,
"DXVA2 in OpenCL release");
if (fail) {
av_log(hwdev, AV_LOG_WARNING, "DXVA2 to OpenCL mapping "
"not usable.\n");
priv->dxva2_mapping_usable = 0;
} else {
priv->dx9_media_adapter_type = CL_ADAPTER_D3D9EX_KHR;
priv->dxva2_mapping_usable = 1;
}
}
#endif
#if HAVE_OPENCL_D3D11
{
const char *d3d11_ext = "cl_khr_d3d11_sharing";
const char *nv12_ext = "cl_intel_d3d11_nv12_media_sharing";
int fail = 0;
if (!opencl_check_extension(hwdev, d3d11_ext)) {
av_log(hwdev, AV_LOG_VERBOSE, "The %s extension is "
"required for D3D11 to OpenCL mapping.\n", d3d11_ext);
fail = 1;
} else if (!opencl_check_extension(hwdev, nv12_ext)) {
av_log(hwdev, AV_LOG_VERBOSE, "The %s extension may be "
"required for D3D11 to OpenCL mapping.\n", nv12_ext);
// Not fatal.
}
CL_FUNC(clCreateFromD3D11Texture2DKHR,
"D3D11 to OpenCL mapping");
CL_FUNC(clEnqueueAcquireD3D11ObjectsKHR,
"D3D11 in OpenCL acquire");
CL_FUNC(clEnqueueReleaseD3D11ObjectsKHR,
"D3D11 in OpenCL release");
if (fail) {
av_log(hwdev, AV_LOG_WARNING, "D3D11 to OpenCL mapping "
"not usable.\n");
priv->d3d11_mapping_usable = 0;
} else {
priv->d3d11_mapping_usable = 1;
}
}
#endif
#if HAVE_OPENCL_DRM_ARM
{
const char *drm_arm_ext = "cl_arm_import_memory";
const char *image_ext = "cl_khr_image2d_from_buffer";
int fail = 0;
if (!opencl_check_extension(hwdev, drm_arm_ext)) {
av_log(hwdev, AV_LOG_VERBOSE, "The %s extension is "
"required for DRM to OpenCL mapping on ARM.\n",
drm_arm_ext);
fail = 1;
}
if (!opencl_check_extension(hwdev, image_ext)) {
av_log(hwdev, AV_LOG_VERBOSE, "The %s extension is "
"required for DRM to OpenCL mapping on ARM.\n",
image_ext);
fail = 1;
}
// clImportMemoryARM() is linked statically.
if (fail) {
av_log(hwdev, AV_LOG_WARNING, "DRM to OpenCL mapping on ARM "
"not usable.\n");
priv->drm_arm_mapping_usable = 0;
} else {
priv->drm_arm_mapping_usable = 1;
}
}
#endif
#undef CL_FUNC
return 0;
}
static void opencl_device_uninit(AVHWDeviceContext *hwdev)
{
OpenCLDeviceContext *priv = hwdev->internal->priv;
cl_int cle;
if (priv->command_queue) {
cle = clReleaseCommandQueue(priv->command_queue);
if (cle != CL_SUCCESS) {
av_log(hwdev, AV_LOG_ERROR, "Failed to release internal "
"command queue reference: %d.\n", cle);
}
priv->command_queue = NULL;
}
}
#if HAVE_OPENCL_VAAPI_INTEL_MEDIA
static int opencl_filter_intel_media_vaapi_platform(AVHWDeviceContext *hwdev,
cl_platform_id platform_id,
const char *platform_name,
void *context)
{
// This doesn't exist as a platform extension, so just test whether
// the function we will use for device enumeration exists.
if (!clGetExtensionFunctionAddressForPlatform(platform_id,
"clGetDeviceIDsFromVA_APIMediaAdapterINTEL")) {
av_log(hwdev, AV_LOG_DEBUG, "Platform %s does not export the "
"VAAPI device enumeration function.\n", platform_name);
return 1;
} else {
return 0;
}
}
static int opencl_enumerate_intel_media_vaapi_devices(AVHWDeviceContext *hwdev,
cl_platform_id platform_id,
const char *platform_name,
cl_uint *nb_devices,
cl_device_id **devices,
void *context)
{
VADisplay va_display = context;
clGetDeviceIDsFromVA_APIMediaAdapterINTEL_fn
clGetDeviceIDsFromVA_APIMediaAdapterINTEL;
cl_int cle;
clGetDeviceIDsFromVA_APIMediaAdapterINTEL =
clGetExtensionFunctionAddressForPlatform(platform_id,
"clGetDeviceIDsFromVA_APIMediaAdapterINTEL");
if (!clGetDeviceIDsFromVA_APIMediaAdapterINTEL) {
av_log(hwdev, AV_LOG_ERROR, "Failed to get address of "
"clGetDeviceIDsFromVA_APIMediaAdapterINTEL().\n");
return AVERROR_UNKNOWN;
}
cle = clGetDeviceIDsFromVA_APIMediaAdapterINTEL(
platform_id, CL_VA_API_DISPLAY_INTEL, va_display,
CL_PREFERRED_DEVICES_FOR_VA_API_INTEL, 0, NULL, nb_devices);
if (cle == CL_DEVICE_NOT_FOUND) {
av_log(hwdev, AV_LOG_DEBUG, "No VAAPI-supporting devices found "
"on platform \"%s\".\n", platform_name);
*nb_devices = 0;
return 0;
} else if (cle != CL_SUCCESS) {
av_log(hwdev, AV_LOG_ERROR, "Failed to get number of devices "
"on platform \"%s\": %d.\n", platform_name, cle);
return AVERROR_UNKNOWN;
}
*devices = av_malloc_array(*nb_devices, sizeof(**devices));
if (!*devices)
return AVERROR(ENOMEM);
cle = clGetDeviceIDsFromVA_APIMediaAdapterINTEL(
platform_id, CL_VA_API_DISPLAY_INTEL, va_display,
CL_PREFERRED_DEVICES_FOR_VA_API_INTEL, *nb_devices, *devices, NULL);
if (cle != CL_SUCCESS) {
av_log(hwdev, AV_LOG_ERROR, "Failed to get list of VAAPI-supporting "
"devices on platform \"%s\": %d.\n", platform_name, cle);
av_freep(devices);
return AVERROR_UNKNOWN;
}
return 0;
}
static int opencl_filter_intel_media_vaapi_device(AVHWDeviceContext *hwdev,
cl_device_id device_id,
const char *device_name,
void *context)
{
const char *va_ext = "cl_intel_va_api_media_sharing";
if (opencl_check_device_extension(device_id, va_ext)) {
return 0;
} else {
av_log(hwdev, AV_LOG_DEBUG, "Device %s does not support the "
"%s extension.\n", device_name, va_ext);
return 1;
}
}
#endif
#if HAVE_OPENCL_DXVA2
static int opencl_filter_dxva2_platform(AVHWDeviceContext *hwdev,
cl_platform_id platform_id,
const char *platform_name,
void *context)
{
const char *dx9_ext = "cl_khr_dx9_media_sharing";
if (opencl_check_platform_extension(platform_id, dx9_ext)) {
return 0;
} else {
av_log(hwdev, AV_LOG_DEBUG, "Platform %s does not support the "
"%s extension.\n", platform_name, dx9_ext);
return 1;
}
}
static int opencl_enumerate_dxva2_devices(AVHWDeviceContext *hwdev,
cl_platform_id platform_id,
const char *platform_name,
cl_uint *nb_devices,
cl_device_id **devices,
void *context)
{
IDirect3DDevice9 *device = context;
clGetDeviceIDsFromDX9MediaAdapterKHR_fn
clGetDeviceIDsFromDX9MediaAdapterKHR;
cl_dx9_media_adapter_type_khr media_adapter_type = CL_ADAPTER_D3D9EX_KHR;
cl_int cle;
clGetDeviceIDsFromDX9MediaAdapterKHR =
clGetExtensionFunctionAddressForPlatform(platform_id,
"clGetDeviceIDsFromDX9MediaAdapterKHR");
if (!clGetDeviceIDsFromDX9MediaAdapterKHR) {
av_log(hwdev, AV_LOG_ERROR, "Failed to get address of "
"clGetDeviceIDsFromDX9MediaAdapterKHR().\n");
return AVERROR_UNKNOWN;
}
cle = clGetDeviceIDsFromDX9MediaAdapterKHR(
platform_id, 1, &media_adapter_type, (void**)&device,
CL_PREFERRED_DEVICES_FOR_DX9_MEDIA_ADAPTER_KHR,
0, NULL, nb_devices);
if (cle == CL_DEVICE_NOT_FOUND) {
av_log(hwdev, AV_LOG_DEBUG, "No DXVA2-supporting devices found "
"on platform \"%s\".\n", platform_name);
*nb_devices = 0;
return 0;
} else if (cle != CL_SUCCESS) {
av_log(hwdev, AV_LOG_ERROR, "Failed to get number of devices "
"on platform \"%s\": %d.\n", platform_name, cle);
return AVERROR_UNKNOWN;
}
*devices = av_malloc_array(*nb_devices, sizeof(**devices));
if (!*devices)
return AVERROR(ENOMEM);
cle = clGetDeviceIDsFromDX9MediaAdapterKHR(
platform_id, 1, &media_adapter_type, (void**)&device,
CL_PREFERRED_DEVICES_FOR_DX9_MEDIA_ADAPTER_KHR,
*nb_devices, *devices, NULL);
if (cle != CL_SUCCESS) {
av_log(hwdev, AV_LOG_ERROR, "Failed to get list of DXVA2-supporting "
"devices on platform \"%s\": %d.\n", platform_name, cle);
av_freep(devices);
return AVERROR_UNKNOWN;
}
return 0;
}
#endif
#if HAVE_OPENCL_D3D11
static int opencl_filter_d3d11_platform(AVHWDeviceContext *hwdev,
cl_platform_id platform_id,
const char *platform_name,
void *context)
{
const char *d3d11_ext = "cl_khr_d3d11_sharing";
if (opencl_check_platform_extension(platform_id, d3d11_ext)) {
return 0;
} else {
av_log(hwdev, AV_LOG_DEBUG, "Platform %s does not support the "
"%s extension.\n", platform_name, d3d11_ext);
return 1;
}
}
static int opencl_enumerate_d3d11_devices(AVHWDeviceContext *hwdev,
cl_platform_id platform_id,
const char *platform_name,
cl_uint *nb_devices,
cl_device_id **devices,
void *context)
{
ID3D11Device *device = context;
clGetDeviceIDsFromD3D11KHR_fn clGetDeviceIDsFromD3D11KHR;
cl_int cle;
clGetDeviceIDsFromD3D11KHR =
clGetExtensionFunctionAddressForPlatform(platform_id,
"clGetDeviceIDsFromD3D11KHR");
if (!clGetDeviceIDsFromD3D11KHR) {
av_log(hwdev, AV_LOG_ERROR, "Failed to get address of "
"clGetDeviceIDsFromD3D11KHR().\n");
return AVERROR_UNKNOWN;
}
cle = clGetDeviceIDsFromD3D11KHR(platform_id,
CL_D3D11_DEVICE_KHR, device,
CL_PREFERRED_DEVICES_FOR_D3D11_KHR,
0, NULL, nb_devices);
if (cle == CL_DEVICE_NOT_FOUND) {
av_log(hwdev, AV_LOG_DEBUG, "No D3D11-supporting devices found "
"on platform \"%s\".\n", platform_name);
*nb_devices = 0;
return 0;
} else if (cle != CL_SUCCESS) {
av_log(hwdev, AV_LOG_ERROR, "Failed to get number of devices "
"on platform \"%s\": %d.\n", platform_name, cle);
return AVERROR_UNKNOWN;
}
*devices = av_malloc_array(*nb_devices, sizeof(**devices));
if (!*devices)
return AVERROR(ENOMEM);
cle = clGetDeviceIDsFromD3D11KHR(platform_id,
CL_D3D11_DEVICE_KHR, device,
CL_PREFERRED_DEVICES_FOR_D3D11_KHR,
*nb_devices, *devices, NULL);
if (cle != CL_SUCCESS) {
av_log(hwdev, AV_LOG_ERROR, "Failed to get list of D3D11-supporting "
"devices on platform \"%s\": %d.\n", platform_name, cle);
av_freep(devices);
return AVERROR_UNKNOWN;
}
return 0;
}
#endif
#if HAVE_OPENCL_DXVA2 || HAVE_OPENCL_D3D11
static int opencl_filter_gpu_device(AVHWDeviceContext *hwdev,
cl_device_id device_id,
const char *device_name,
void *context)
{
cl_device_type device_type;
cl_int cle;
cle = clGetDeviceInfo(device_id, CL_DEVICE_TYPE,
sizeof(device_type), &device_type, NULL);
if (cle != CL_SUCCESS) {
av_log(hwdev, AV_LOG_ERROR, "Failed to query device type "
"of device \"%s\".\n", device_name);
return AVERROR_UNKNOWN;
}
if (!(device_type & CL_DEVICE_TYPE_GPU)) {
av_log(hwdev, AV_LOG_DEBUG, "Device %s skipped (not GPU).\n",
device_name);
return 1;
}
return 0;
}
#endif
#if HAVE_OPENCL_DRM_ARM
static int opencl_filter_drm_arm_platform(AVHWDeviceContext *hwdev,
cl_platform_id platform_id,
const char *platform_name,
void *context)
{
const char *drm_arm_ext = "cl_arm_import_memory";
if (opencl_check_platform_extension(platform_id, drm_arm_ext)) {
return 0;
} else {
av_log(hwdev, AV_LOG_DEBUG, "Platform %s does not support the "
"%s extension.\n", platform_name, drm_arm_ext);
return 1;
}
}
static int opencl_filter_drm_arm_device(AVHWDeviceContext *hwdev,
cl_device_id device_id,
const char *device_name,
void *context)
{
const char *drm_arm_ext = "cl_arm_import_memory";
if (opencl_check_device_extension(device_id, drm_arm_ext)) {
return 0;
} else {
av_log(hwdev, AV_LOG_DEBUG, "Device %s does not support the "
"%s extension.\n", device_name, drm_arm_ext);
return 1;
}
}
#endif
static int opencl_device_derive(AVHWDeviceContext *hwdev,
AVHWDeviceContext *src_ctx, AVDictionary *opts,
int flags)
{
int err;
switch (src_ctx->type) {
#if HAVE_OPENCL_DRM_BEIGNET
case AV_HWDEVICE_TYPE_DRM:
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 *selector_opts = NULL;
err = av_dict_set(&selector_opts, "platform_vendor", "Intel", 0);
if (err >= 0)
err = av_dict_set(&selector_opts, "platform_version", "beignet", 0);
if (err >= 0) {
OpenCLDeviceSelector selector = {
.platform_index = -1,
.device_index = 0,
.context = selector_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(&selector_opts);
}
break;
#endif
#if HAVE_OPENCL_VAAPI_INTEL_MEDIA
// 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 VAAPI device it was derived from.
case AV_HWDEVICE_TYPE_VAAPI:
{
AVVAAPIDeviceContext *src_hwctx = src_ctx->hwctx;
cl_context_properties props[7] = {
CL_CONTEXT_PLATFORM,
0,
CL_CONTEXT_VA_API_DISPLAY_INTEL,
(intptr_t)src_hwctx->display,
CL_CONTEXT_INTEROP_USER_SYNC,
CL_FALSE,
0,
};
OpenCLDeviceSelector selector = {
.platform_index = -1,
.device_index = -1,
.context = src_hwctx->display,
.enumerate_platforms = &opencl_enumerate_platforms,
.filter_platform = &opencl_filter_intel_media_vaapi_platform,
.enumerate_devices = &opencl_enumerate_intel_media_vaapi_devices,
.filter_device = &opencl_filter_intel_media_vaapi_device,
};
err = opencl_device_create_internal(hwdev, &selector, props);
}
break;
#endif
#if HAVE_OPENCL_DXVA2
case AV_HWDEVICE_TYPE_DXVA2:
{
AVDXVA2DeviceContext *src_hwctx = src_ctx->hwctx;
IDirect3DDevice9 *device;
HANDLE device_handle;
HRESULT hr;
hr = IDirect3DDeviceManager9_OpenDeviceHandle(src_hwctx->devmgr,
&device_handle);
if (FAILED(hr)) {
av_log(hwdev, AV_LOG_ERROR, "Failed to open device handle "
"for Direct3D9 device: %lx.\n", (unsigned long)hr);
err = AVERROR_UNKNOWN;
break;
}
hr = IDirect3DDeviceManager9_LockDevice(src_hwctx->devmgr,
device_handle,
&device, FALSE);
if (SUCCEEDED(hr)) {
cl_context_properties props[5] = {
CL_CONTEXT_PLATFORM,
0,
CL_CONTEXT_ADAPTER_D3D9EX_KHR,
(intptr_t)device,
0,
};
OpenCLDeviceSelector selector = {
.platform_index = -1,
.device_index = -1,
.context = device,
.enumerate_platforms = &opencl_enumerate_platforms,
.filter_platform = &opencl_filter_dxva2_platform,
.enumerate_devices = &opencl_enumerate_dxva2_devices,
.filter_device = &opencl_filter_gpu_device,
};
err = opencl_device_create_internal(hwdev, &selector, props);
IDirect3DDeviceManager9_UnlockDevice(src_hwctx->devmgr,
device_handle, FALSE);
} else {
av_log(hwdev, AV_LOG_ERROR, "Failed to lock device handle "
"for Direct3D9 device: %lx.\n", (unsigned long)hr);
err = AVERROR_UNKNOWN;
}
IDirect3DDeviceManager9_CloseDeviceHandle(src_hwctx->devmgr,
device_handle);
}
break;
#endif
#if HAVE_OPENCL_D3D11
case AV_HWDEVICE_TYPE_D3D11VA:
{
AVD3D11VADeviceContext *src_hwctx = src_ctx->hwctx;
cl_context_properties props[5] = {
CL_CONTEXT_PLATFORM,
0,
CL_CONTEXT_D3D11_DEVICE_KHR,
(intptr_t)src_hwctx->device,
0,
};
OpenCLDeviceSelector selector = {
.platform_index = -1,
.device_index = -1,
.context = src_hwctx->device,
.enumerate_platforms = &opencl_enumerate_platforms,
.filter_platform = &opencl_filter_d3d11_platform,
.enumerate_devices = &opencl_enumerate_d3d11_devices,
.filter_device = &opencl_filter_gpu_device,
};
err = opencl_device_create_internal(hwdev, &selector, props);
}
break;
#endif
#if HAVE_OPENCL_DRM_ARM
case AV_HWDEVICE_TYPE_DRM:
{
OpenCLDeviceSelector selector = {
.platform_index = -1,
.device_index = -1,
.context = NULL,
.enumerate_platforms = &opencl_enumerate_platforms,
.filter_platform = &opencl_filter_drm_arm_platform,
.enumerate_devices = &opencl_enumerate_devices,
.filter_device = &opencl_filter_drm_arm_device,
};
err = opencl_device_create_internal(hwdev, &selector, NULL);
}
break;
#endif
default:
err = AVERROR(ENOSYS);
break;
}
return err;
}
static int opencl_get_plane_format(enum AVPixelFormat pixfmt,
int plane, int width, int height,
cl_image_format *image_format,
cl_image_desc *image_desc)
{
const AVPixFmtDescriptor *desc;
const AVComponentDescriptor *comp;
int channels = 0, order = 0, depth = 0, step = 0;
int wsub, hsub, alpha;
int c;
if (plane >= AV_NUM_DATA_POINTERS)
return AVERROR(ENOENT);
desc = av_pix_fmt_desc_get(pixfmt);
// Only normal images are allowed.
if (desc->flags & (AV_PIX_FMT_FLAG_BITSTREAM |
AV_PIX_FMT_FLAG_HWACCEL |
AV_PIX_FMT_FLAG_PAL))
return AVERROR(EINVAL);
wsub = 1 << desc->log2_chroma_w;
hsub = 1 << desc->log2_chroma_h;
// Subsampled components must be exact.
if (width & wsub - 1 || height & hsub - 1)
return AVERROR(EINVAL);
for (c = 0; c < desc->nb_components; c++) {
comp = &desc->comp[c];
if (comp->plane != plane)
continue;
// The step size must be a power of two.
if (comp->step != 1 && comp->step != 2 &&
comp->step != 4 && comp->step != 8)
return AVERROR(EINVAL);
// The bits in each component must be packed in the
// most-significant-bits of the relevant bytes.
if (comp->shift + comp->depth != 8 &&
comp->shift + comp->depth != 16)
return AVERROR(EINVAL);
// The depth must not vary between components.
if (depth && comp->depth != depth)
return AVERROR(EINVAL);
// If a single data element crosses multiple bytes then
// it must match the native endianness.
if (comp->depth > 8 &&
HAVE_BIGENDIAN == !(desc->flags & AV_PIX_FMT_FLAG_BE))
return AVERROR(EINVAL);
// A single data element must not contain multiple samples
// from the same component.
if (step && comp->step != step)
return AVERROR(EINVAL);
depth = comp->depth;
order = order * 10 + comp->offset / ((depth + 7) / 8) + 1;
step = comp->step;
alpha = (desc->flags & AV_PIX_FMT_FLAG_ALPHA &&
c == desc->nb_components - 1);
++channels;
}
if (channels == 0)
return AVERROR(ENOENT);
memset(image_format, 0, sizeof(*image_format));
memset(image_desc, 0, sizeof(*image_desc));
image_desc->image_type = CL_MEM_OBJECT_IMAGE2D;
if (plane == 0 || alpha) {
image_desc->image_width = width;
image_desc->image_height = height;
image_desc->image_row_pitch = step * width;
} else {
image_desc->image_width = width / wsub;
image_desc->image_height = height / hsub;
image_desc->image_row_pitch = step * width / wsub;
}
if (depth <= 8) {
image_format->image_channel_data_type = CL_UNORM_INT8;
} else {
if (depth <= 16)
image_format->image_channel_data_type = CL_UNORM_INT16;
else
return AVERROR(EINVAL);
}
#define CHANNEL_ORDER(order, type) \
case order: image_format->image_channel_order = type; break;
switch (order) {
CHANNEL_ORDER(1, CL_R);
CHANNEL_ORDER(12, CL_RG);
CHANNEL_ORDER(1234, CL_RGBA);
CHANNEL_ORDER(2341, CL_ARGB);
CHANNEL_ORDER(3214, CL_BGRA);
#ifdef CL_ABGR
CHANNEL_ORDER(4321, CL_ABGR);
#endif
default:
return AVERROR(EINVAL);
}
#undef CHANNEL_ORDER
return 0;
}
static int opencl_frames_get_constraints(AVHWDeviceContext *hwdev,
const void *hwconfig,
AVHWFramesConstraints *constraints)
{
AVOpenCLDeviceContext *hwctx = hwdev->hwctx;
cl_uint nb_image_formats;
cl_image_format *image_formats = NULL;
cl_int cle;
enum AVPixelFormat pix_fmt;
int err, pix_fmts_found;
size_t max_width, max_height;
cle = clGetDeviceInfo(hwctx->device_id, CL_DEVICE_IMAGE2D_MAX_WIDTH,
sizeof(max_width), &max_width, NULL);
if (cle != CL_SUCCESS) {
av_log(hwdev, AV_LOG_ERROR, "Failed to query maximum "
"supported image width: %d.\n", cle);
} else {
constraints->max_width = max_width;
}
cle = clGetDeviceInfo(hwctx->device_id, CL_DEVICE_IMAGE2D_MAX_HEIGHT,
sizeof(max_height), &max_height, NULL);
if (cle != CL_SUCCESS) {
av_log(hwdev, AV_LOG_ERROR, "Failed to query maximum "
"supported image height: %d.\n", cle);
} else {
constraints->max_height = max_height;
}
av_log(hwdev, AV_LOG_DEBUG, "Maximum supported image size %dx%d.\n",
constraints->max_width, constraints->max_height);
cle = clGetSupportedImageFormats(hwctx->context,
CL_MEM_READ_WRITE,
CL_MEM_OBJECT_IMAGE2D,
0, NULL, &nb_image_formats);
if (cle != CL_SUCCESS) {
av_log(hwdev, AV_LOG_ERROR, "Failed to query supported "
"image formats: %d.\n", cle);
err = AVERROR(ENOSYS);
goto fail;
}
if (nb_image_formats == 0) {
av_log(hwdev, AV_LOG_ERROR, "No image support in OpenCL "
"driver (zero supported image formats).\n");
err = AVERROR(ENOSYS);
goto fail;
}
image_formats =
av_malloc_array(nb_image_formats, sizeof(*image_formats));
if (!image_formats) {
err = AVERROR(ENOMEM);
goto fail;
}
cle = clGetSupportedImageFormats(hwctx->context,
CL_MEM_READ_WRITE,
CL_MEM_OBJECT_IMAGE2D,
nb_image_formats,
image_formats, NULL);
if (cle != CL_SUCCESS) {
av_log(hwdev, AV_LOG_ERROR, "Failed to query supported "
"image formats: %d.\n", cle);
err = AVERROR(ENOSYS);
goto fail;
}
pix_fmts_found = 0;
for (pix_fmt = 0; pix_fmt < AV_PIX_FMT_NB; pix_fmt++) {
cl_image_format image_format;
cl_image_desc image_desc;
int plane, i;
for (plane = 0;; plane++) {
err = opencl_get_plane_format(pix_fmt, plane, 0, 0,
&image_format,
&image_desc);
if (err < 0)
break;
for (i = 0; i < nb_image_formats; i++) {
if (image_formats[i].image_channel_order ==
image_format.image_channel_order &&
image_formats[i].image_channel_data_type ==
image_format.image_channel_data_type)
break;
}
if (i == nb_image_formats) {
err = AVERROR(EINVAL);
break;
}
}
if (err != AVERROR(ENOENT))
continue;
av_log(hwdev, AV_LOG_DEBUG, "Format %s supported.\n",
av_get_pix_fmt_name(pix_fmt));
err = av_reallocp_array(&constraints->valid_sw_formats,
pix_fmts_found + 2,
sizeof(*constraints->valid_sw_formats));
if (err < 0)
goto fail;
constraints->valid_sw_formats[pix_fmts_found] = pix_fmt;
constraints->valid_sw_formats[pix_fmts_found + 1] =
AV_PIX_FMT_NONE;
++pix_fmts_found;
}
av_freep(&image_formats);
constraints->valid_hw_formats =
av_malloc_array(2, sizeof(*constraints->valid_hw_formats));
if (!constraints->valid_hw_formats) {
err = AVERROR(ENOMEM);
goto fail;
}
constraints->valid_hw_formats[0] = AV_PIX_FMT_OPENCL;
constraints->valid_hw_formats[1] = AV_PIX_FMT_NONE;
return 0;
fail:
av_freep(&image_formats);
return err;
}
static void opencl_pool_free(void *opaque, uint8_t *data)
{
AVHWFramesContext *hwfc = opaque;
AVOpenCLFrameDescriptor *desc = (AVOpenCLFrameDescriptor*)data;
cl_int cle;
int p;
for (p = 0; p < desc->nb_planes; p++) {
cle = clReleaseMemObject(desc->planes[p]);
if (cle != CL_SUCCESS) {
av_log(hwfc, AV_LOG_ERROR, "Failed to release plane %d: "
"%d.\n", p, cle);
}
}
av_free(desc);
}
static AVBufferRef *opencl_pool_alloc(void *opaque, int size)
{
AVHWFramesContext *hwfc = opaque;
AVOpenCLDeviceContext *hwctx = hwfc->device_ctx->hwctx;
AVOpenCLFrameDescriptor *desc;
cl_int cle;
cl_mem image;
cl_image_format image_format;
cl_image_desc image_desc;
int err, p;
AVBufferRef *ref;
desc = av_mallocz(sizeof(*desc));
if (!desc)
return NULL;
for (p = 0;; p++) {
err = opencl_get_plane_format(hwfc->sw_format, p,
hwfc->width, hwfc->height,
&image_format, &image_desc);
if (err == AVERROR(ENOENT))
break;
if (err < 0)
goto fail;
// For generic image objects, the pitch is determined by the
// implementation.
image_desc.image_row_pitch = 0;
image = clCreateImage(hwctx->context, CL_MEM_READ_WRITE,
&image_format, &image_desc, NULL, &cle);
if (!image) {
av_log(hwfc, AV_LOG_ERROR, "Failed to create image for "
"plane %d: %d.\n", p, cle);
goto fail;
}
desc->planes[p] = image;
}
desc->nb_planes = p;
ref = av_buffer_create((uint8_t*)desc, sizeof(*desc),
&opencl_pool_free, hwfc, 0);
if (!ref)
goto fail;
return ref;
fail:
for (p = 0; desc->planes[p]; p++)
clReleaseMemObject(desc->planes[p]);
av_free(desc);
return NULL;
}
static int opencl_frames_init_command_queue(AVHWFramesContext *hwfc)
{
AVOpenCLFramesContext *hwctx = hwfc->hwctx;
OpenCLDeviceContext *devpriv = hwfc->device_ctx->internal->priv;
OpenCLFramesContext *priv = hwfc->internal->priv;
cl_int cle;
priv->command_queue = hwctx->command_queue ? hwctx->command_queue
: devpriv->command_queue;
cle = clRetainCommandQueue(priv->command_queue);
if (cle != CL_SUCCESS) {
av_log(hwfc, AV_LOG_ERROR, "Failed to retain frame "
"command queue: %d.\n", cle);
return AVERROR(EIO);
}
return 0;
}
static int opencl_frames_init(AVHWFramesContext *hwfc)
{
if (!hwfc->pool) {
hwfc->internal->pool_internal =
av_buffer_pool_init2(sizeof(cl_mem), hwfc,
&opencl_pool_alloc, NULL);
if (!hwfc->internal->pool_internal)
return AVERROR(ENOMEM);
}
return opencl_frames_init_command_queue(hwfc);
}
static void opencl_frames_uninit(AVHWFramesContext *hwfc)
{
OpenCLFramesContext *priv = hwfc->internal->priv;
cl_int cle;
#if HAVE_OPENCL_DXVA2 || HAVE_OPENCL_D3D11
int i, p;
for (i = 0; i < priv->nb_mapped_frames; i++) {
AVOpenCLFrameDescriptor *desc = &priv->mapped_frames[i];
for (p = 0; p < desc->nb_planes; p++) {
cle = clReleaseMemObject(desc->planes[p]);
if (cle != CL_SUCCESS) {
av_log(hwfc, AV_LOG_ERROR, "Failed to release mapped "
"frame object (frame %d plane %d): %d.\n",
i, p, cle);
}
}
}
av_freep(&priv->mapped_frames);
#endif
if (priv->command_queue) {
cle = clReleaseCommandQueue(priv->command_queue);
if (cle != CL_SUCCESS) {
av_log(hwfc, AV_LOG_ERROR, "Failed to release frame "
"command queue: %d.\n", cle);
}
priv->command_queue = NULL;
}
}
static int opencl_get_buffer(AVHWFramesContext *hwfc, AVFrame *frame)
{
AVOpenCLFrameDescriptor *desc;
int p;
frame->buf[0] = av_buffer_pool_get(hwfc->pool);
if (!frame->buf[0])
return AVERROR(ENOMEM);
desc = (AVOpenCLFrameDescriptor*)frame->buf[0]->data;
for (p = 0; p < desc->nb_planes; p++)
frame->data[p] = (uint8_t*)desc->planes[p];
frame->format = AV_PIX_FMT_OPENCL;
frame->width = hwfc->width;
frame->height = hwfc->height;
return 0;
}
static int opencl_transfer_get_formats(AVHWFramesContext *hwfc,
enum AVHWFrameTransferDirection dir,
enum AVPixelFormat **formats)
{
enum AVPixelFormat *fmts;
fmts = av_malloc_array(2, sizeof(*fmts));
if (!fmts)
return AVERROR(ENOMEM);
fmts[0] = hwfc->sw_format;
fmts[1] = AV_PIX_FMT_NONE;
*formats = fmts;
return 0;
}
static int opencl_wait_events(AVHWFramesContext *hwfc,
cl_event *events, int nb_events)
{
cl_int cle;
int i;
cle = clWaitForEvents(nb_events, events);
if (cle != CL_SUCCESS) {
av_log(hwfc, AV_LOG_ERROR, "Failed to wait for event "
"completion: %d.\n", cle);
return AVERROR(EIO);
}
for (i = 0; i < nb_events; i++) {
cle = clReleaseEvent(events[i]);
if (cle != CL_SUCCESS) {
av_log(hwfc, AV_LOG_ERROR, "Failed to release "
"event: %d.\n", cle);
}
}
return 0;
}
static int opencl_transfer_data_from(AVHWFramesContext *hwfc,
AVFrame *dst, const AVFrame *src)
{
OpenCLFramesContext *priv = hwfc->internal->priv;
cl_image_format image_format;
cl_image_desc image_desc;
cl_int cle;
size_t origin[3] = { 0, 0, 0 };
size_t region[3];
cl_event events[AV_NUM_DATA_POINTERS];
int err, p;
if (dst->format != hwfc->sw_format)
return AVERROR(EINVAL);
for (p = 0;; p++) {
err = opencl_get_plane_format(hwfc->sw_format, p,
src->width, src->height,
&image_format, &image_desc);
if (err < 0) {
if (err == AVERROR(ENOENT))
err = 0;
break;
}
if (!dst->data[p]) {
av_log(hwfc, AV_LOG_ERROR, "Plane %d missing on "
"destination frame for transfer.\n", p);
err = AVERROR(EINVAL);
break;
}
region[0] = image_desc.image_width;
region[1] = image_desc.image_height;
region[2] = 1;
cle = clEnqueueReadImage(priv->command_queue,
(cl_mem)src->data[p],
CL_FALSE, origin, region,
dst->linesize[p], 0,
dst->data[p],
0, NULL, &events[p]);
if (cle != CL_SUCCESS) {
av_log(hwfc, AV_LOG_ERROR, "Failed to enqueue read of "
"OpenCL image plane %d: %d.\n", p, cle);
err = AVERROR(EIO);
break;
}
}
opencl_wait_events(hwfc, events, p);
return err;
}
static int opencl_transfer_data_to(AVHWFramesContext *hwfc,
AVFrame *dst, const AVFrame *src)
{
OpenCLFramesContext *priv = hwfc->internal->priv;
cl_image_format image_format;
cl_image_desc image_desc;
cl_int cle;
size_t origin[3] = { 0, 0, 0 };
size_t region[3];
cl_event events[AV_NUM_DATA_POINTERS];
int err, p;
if (src->format != hwfc->sw_format)
return AVERROR(EINVAL);
for (p = 0;; p++) {
err = opencl_get_plane_format(hwfc->sw_format, p,
src->width, src->height,
&image_format, &image_desc);
if (err < 0) {
if (err == AVERROR(ENOENT))
err = 0;
break;
}
if (!src->data[p]) {
av_log(hwfc, AV_LOG_ERROR, "Plane %d missing on "
"source frame for transfer.\n", p);
err = AVERROR(EINVAL);
break;
}
region[0] = image_desc.image_width;
region[1] = image_desc.image_height;
region[2] = 1;
cle = clEnqueueWriteImage(priv->command_queue,
(cl_mem)dst->data[p],
CL_FALSE, origin, region,
src->linesize[p], 0,
src->data[p],
0, NULL, &events[p]);
if (cle != CL_SUCCESS) {
av_log(hwfc, AV_LOG_ERROR, "Failed to enqueue write of "
"OpenCL image plane %d: %d.\n", p, cle);
err = AVERROR(EIO);
break;
}
}
opencl_wait_events(hwfc, events, p);
return err;
}
typedef struct OpenCLMapping {
// The mapped addresses for each plane.
// The destination frame is not available when we unmap, so these
// need to be stored separately.
void *address[AV_NUM_DATA_POINTERS];
} OpenCLMapping;
static void opencl_unmap_frame(AVHWFramesContext *hwfc,
HWMapDescriptor *hwmap)
{
OpenCLFramesContext *priv = hwfc->internal->priv;
OpenCLMapping *map = hwmap->priv;
cl_event events[AV_NUM_DATA_POINTERS];
int p, e;
cl_int cle;
for (p = e = 0; p < FF_ARRAY_ELEMS(map->address); p++) {
if (!map->address[p])
break;
cle = clEnqueueUnmapMemObject(priv->command_queue,
(cl_mem)hwmap->source->data[p],
map->address[p],
0, NULL, &events[e]);
if (cle != CL_SUCCESS) {
av_log(hwfc, AV_LOG_ERROR, "Failed to unmap OpenCL "
"image plane %d: %d.\n", p, cle);
}
++e;
}
opencl_wait_events(hwfc, events, e);
av_free(map);
}
static int opencl_map_frame(AVHWFramesContext *hwfc, AVFrame *dst,
const AVFrame *src, int flags)
{
OpenCLFramesContext *priv = hwfc->internal->priv;
cl_map_flags map_flags;
cl_image_format image_format;
cl_image_desc image_desc;
cl_int cle;
OpenCLMapping *map;
size_t origin[3] = { 0, 0, 0 };
size_t region[3];
size_t row_pitch;
cl_event events[AV_NUM_DATA_POINTERS];
int err, p;
av_assert0(hwfc->sw_format == dst->format);
if (flags & AV_HWFRAME_MAP_OVERWRITE &&
!(flags & AV_HWFRAME_MAP_READ)) {
// This is mutually exclusive with the read/write flags, so
// there is no way to map with read here.
map_flags = CL_MAP_WRITE_INVALIDATE_REGION;
} else {
map_flags = 0;
if (flags & AV_HWFRAME_MAP_READ)
map_flags |= CL_MAP_READ;
if (flags & AV_HWFRAME_MAP_WRITE)
map_flags |= CL_MAP_WRITE;
}
map = av_mallocz(sizeof(*map));
if (!map)
return AVERROR(ENOMEM);
for (p = 0;; p++) {
err = opencl_get_plane_format(hwfc->sw_format, p,
src->width, src->height,
&image_format, &image_desc);
if (err == AVERROR(ENOENT))
break;
if (err < 0)
goto fail;
region[0] = image_desc.image_width;
region[1] = image_desc.image_height;
region[2] = 1;
map->address[p] =
clEnqueueMapImage(priv->command_queue,
(cl_mem)src->data[p],
CL_FALSE, map_flags, origin, region,
&row_pitch, NULL, 0, NULL,
&events[p], &cle);
if (!map->address[p]) {
av_log(hwfc, AV_LOG_ERROR, "Failed to map OpenCL "
"image plane %d: %d.\n", p, cle);
err = AVERROR(EIO);
goto fail;
}
dst->data[p] = map->address[p];
av_log(hwfc, AV_LOG_DEBUG, "Map plane %d (%p -> %p).\n",
p, src->data[p], dst->data[p]);
}
err = opencl_wait_events(hwfc, events, p);
if (err < 0)
goto fail;
err = ff_hwframe_map_create(src->hw_frames_ctx, dst, src,
&opencl_unmap_frame, map);
if (err < 0)
goto fail;
dst->width = src->width;
dst->height = src->height;
return 0;
fail:
for (p = 0; p < AV_NUM_DATA_POINTERS; p++) {
if (!map->address[p])
break;
clEnqueueUnmapMemObject(priv->command_queue,
(cl_mem)src->data[p],
map->address[p],
0, NULL, &events[p]);
}
if (p > 0)
opencl_wait_events(hwfc, events, p);
av_freep(&map);
return err;
}
#if HAVE_OPENCL_DRM_BEIGNET
typedef struct DRMBeignetToOpenCLMapping {
AVFrame *drm_frame;
AVDRMFrameDescriptor *drm_desc;
AVOpenCLFrameDescriptor frame;
} DRMBeignetToOpenCLMapping;
static void opencl_unmap_from_drm_beignet(AVHWFramesContext *dst_fc,
HWMapDescriptor *hwmap)
{
DRMBeignetToOpenCLMapping *mapping = hwmap->priv;
cl_int cle;
int i;
for (i = 0; i < mapping->frame.nb_planes; i++) {
cle = clReleaseMemObject(mapping->frame.planes[i]);
if (cle != CL_SUCCESS) {
av_log(dst_fc, AV_LOG_ERROR, "Failed to release CL image "
"of plane %d of DRM frame: %d.\n", i, cle);
}
}
av_free(mapping);
}
static int opencl_map_from_drm_beignet(AVHWFramesContext *dst_fc,
AVFrame *dst, const AVFrame *src,
int flags)
{
AVOpenCLDeviceContext *hwctx = dst_fc->device_ctx->hwctx;
OpenCLDeviceContext *priv = dst_fc->device_ctx->internal->priv;
DRMBeignetToOpenCLMapping *mapping;
const AVDRMFrameDescriptor *desc;
cl_int cle;
int err, i, j, p;
desc = (const AVDRMFrameDescriptor*)src->data[0];
mapping = av_mallocz(sizeof(*mapping));
if (!mapping)
return AVERROR(ENOMEM);
p = 0;
for (i = 0; i < desc->nb_layers; i++) {
const AVDRMLayerDescriptor *layer = &desc->layers[i];
for (j = 0; j < layer->nb_planes; j++) {
const AVDRMPlaneDescriptor *plane = &layer->planes[j];
const AVDRMObjectDescriptor *object =
&desc->objects[plane->object_index];
cl_import_image_info_intel image_info = {
.fd = object->fd,
.size = object->size,
.type = CL_MEM_OBJECT_IMAGE2D,
.offset = plane->offset,
.row_pitch = plane->pitch,
};
cl_image_desc image_desc;
err = opencl_get_plane_format(dst_fc->sw_format, p,
src->width, src->height,
&image_info.fmt,
&image_desc);
if (err < 0) {
av_log(dst_fc, AV_LOG_ERROR, "DRM frame layer %d "
"plane %d is not representable in OpenCL: %d.\n",
i, j, err);
goto fail;
}
image_info.width = image_desc.image_width;
image_info.height = image_desc.image_height;
mapping->frame.planes[p] =
priv->clCreateImageFromFdINTEL(hwctx->context,
&image_info, &cle);
if (!mapping->frame.planes[p]) {
av_log(dst_fc, AV_LOG_ERROR, "Failed to create CL image "
"from layer %d plane %d of DRM frame: %d.\n",
i, j, cle);
err = AVERROR(EIO);
goto fail;
}
dst->data[p] = (uint8_t*)mapping->frame.planes[p];
mapping->frame.nb_planes = ++p;
}
}
err = ff_hwframe_map_create(dst->hw_frames_ctx, dst, src,
&opencl_unmap_from_drm_beignet,
mapping);
if (err < 0)
goto fail;
dst->width = src->width;
dst->height = src->height;
return 0;
fail:
for (p = 0; p < mapping->frame.nb_planes; p++) {
if (mapping->frame.planes[p])
clReleaseMemObject(mapping->frame.planes[p]);
}
av_free(mapping);
return err;
}
#if HAVE_OPENCL_VAAPI_BEIGNET
static int opencl_map_from_vaapi(AVHWFramesContext *dst_fc,
AVFrame *dst, const AVFrame *src,
int flags)
{
AVFrame *tmp;
int err;
tmp = av_frame_alloc();
if (!tmp)
return AVERROR(ENOMEM);
tmp->format = AV_PIX_FMT_DRM_PRIME;
err = av_hwframe_map(tmp, src, flags);
if (err < 0)
goto fail;
err = opencl_map_from_drm_beignet(dst_fc, dst, tmp, flags);
if (err < 0)
goto fail;
err = ff_hwframe_map_replace(dst, src);
fail:
av_frame_free(&tmp);
return err;
}
#endif /* HAVE_OPENCL_VAAPI_BEIGNET */
#endif /* HAVE_OPENCL_DRM_BEIGNET */
static inline cl_mem_flags opencl_mem_flags_for_mapping(int map_flags)
{
if ((map_flags & AV_HWFRAME_MAP_READ) &&
(map_flags & AV_HWFRAME_MAP_WRITE))
return CL_MEM_READ_WRITE;
else if (map_flags & AV_HWFRAME_MAP_READ)
return CL_MEM_READ_ONLY;
else if (map_flags & AV_HWFRAME_MAP_WRITE)
return CL_MEM_WRITE_ONLY;
else
return 0;
}
#if HAVE_OPENCL_VAAPI_INTEL_MEDIA
static void opencl_unmap_from_qsv(AVHWFramesContext *dst_fc,
HWMapDescriptor *hwmap)
{
AVOpenCLFrameDescriptor *desc = hwmap->priv;
OpenCLDeviceContext *device_priv = dst_fc->device_ctx->internal->priv;
OpenCLFramesContext *frames_priv = dst_fc->internal->priv;
cl_event event;
cl_int cle;
int p;
av_log(dst_fc, AV_LOG_DEBUG, "Unmap QSV/VAAPI surface from OpenCL.\n");
cle = device_priv->clEnqueueReleaseVA_APIMediaSurfacesINTEL(
frames_priv->command_queue, desc->nb_planes, desc->planes,
0, NULL, &event);
if (cle != CL_SUCCESS) {
av_log(dst_fc, AV_LOG_ERROR, "Failed to release surface "
"handles: %d.\n", cle);
}
opencl_wait_events(dst_fc, &event, 1);
for (p = 0; p < desc->nb_planes; p++) {
cle = clReleaseMemObject(desc->planes[p]);
if (cle != CL_SUCCESS) {
av_log(dst_fc, AV_LOG_ERROR, "Failed to release CL "
"image of plane %d of QSV/VAAPI surface: %d\n",
p, cle);
}
}
av_free(desc);
}
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 *device_priv = dst_fc->device_ctx->internal->priv;
OpenCLFramesContext *frames_priv = dst_fc->internal->priv;
AVOpenCLFrameDescriptor *desc;
VASurfaceID va_surface;
cl_mem_flags cl_flags;
cl_event event;
cl_int cle;
int err, p;
#if CONFIG_LIBMFX
if (src->format == AV_PIX_FMT_QSV) {
mfxFrameSurface1 *mfx_surface = (mfxFrameSurface1*)src->data[3];
va_surface = *(VASurfaceID*)mfx_surface->Data.MemId;
} else
#endif
if (src->format == AV_PIX_FMT_VAAPI) {
va_surface = (VASurfaceID)(uintptr_t)src->data[3];
} else {
return AVERROR(ENOSYS);
}
cl_flags = opencl_mem_flags_for_mapping(flags);
if (!cl_flags)
return AVERROR(EINVAL);
av_log(src_fc, AV_LOG_DEBUG, "Map QSV/VAAPI surface %#x to "
"OpenCL.\n", va_surface);
desc = av_mallocz(sizeof(*desc));
if (!desc)
return AVERROR(ENOMEM);
// The cl_intel_va_api_media_sharing extension only supports NV12
// surfaces, so for now there are always exactly two planes.
desc->nb_planes = 2;
for (p = 0; p < desc->nb_planes; p++) {
desc->planes[p] =
device_priv->clCreateFromVA_APIMediaSurfaceINTEL(
dst_dev->context, cl_flags, &va_surface, p, &cle);
if (!desc->planes[p]) {
av_log(dst_fc, AV_LOG_ERROR, "Failed to create CL "
"image from plane %d of QSV/VAAPI surface "
"%#x: %d.\n", p, va_surface, cle);
err = AVERROR(EIO);
goto fail;
}
dst->data[p] = (uint8_t*)desc->planes[p];
}
cle = device_priv->clEnqueueAcquireVA_APIMediaSurfacesINTEL(
frames_priv->command_queue, desc->nb_planes, desc->planes,
0, NULL, &event);
if (cle != CL_SUCCESS) {
av_log(dst_fc, AV_LOG_ERROR, "Failed to acquire surface "
"handles: %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, desc);
if (err < 0)
goto fail;
dst->width = src->width;
dst->height = src->height;
return 0;
fail:
for (p = 0; p < desc->nb_planes; p++)
if (desc->planes[p])
clReleaseMemObject(desc->planes[p]);
av_freep(&desc);
return err;
}
#endif
#if HAVE_OPENCL_DXVA2
static void opencl_unmap_from_dxva2(AVHWFramesContext *dst_fc,
HWMapDescriptor *hwmap)
{
AVOpenCLFrameDescriptor *desc = hwmap->priv;
OpenCLDeviceContext *device_priv = dst_fc->device_ctx->internal->priv;
OpenCLFramesContext *frames_priv = dst_fc->device_ctx->internal->priv;
cl_event event;
cl_int cle;
av_log(dst_fc, AV_LOG_DEBUG, "Unmap DXVA2 surface from OpenCL.\n");
cle = device_priv->clEnqueueReleaseDX9MediaSurfacesKHR(
frames_priv->command_queue, desc->nb_planes, desc->planes,
0, NULL, &event);
if (cle != CL_SUCCESS) {
av_log(dst_fc, AV_LOG_ERROR, "Failed to release surface "
"handle: %d.\n", cle);
return;
}
opencl_wait_events(dst_fc, &event, 1);
}
static int opencl_map_from_dxva2(AVHWFramesContext *dst_fc, AVFrame *dst,
const AVFrame *src, int flags)
{
AVHWFramesContext *src_fc =
(AVHWFramesContext*)src->hw_frames_ctx->data;
AVDXVA2FramesContext *src_hwctx = src_fc->hwctx;
OpenCLDeviceContext *device_priv = dst_fc->device_ctx->internal->priv;
OpenCLFramesContext *frames_priv = dst_fc->internal->priv;
AVOpenCLFrameDescriptor *desc;
cl_event event;
cl_int cle;
int err, i;
av_log(dst_fc, AV_LOG_DEBUG, "Map DXVA2 surface %p to "
"OpenCL.\n", src->data[3]);
for (i = 0; i < src_hwctx->nb_surfaces; i++) {
if (src_hwctx->surfaces[i] == (IDirect3DSurface9*)src->data[3])
break;
}
if (i >= src_hwctx->nb_surfaces) {
av_log(dst_fc, AV_LOG_ERROR, "Trying to map from a surface which "
"is not in the mapped frames context.\n");
return AVERROR(EINVAL);
}
desc = &frames_priv->mapped_frames[i];
cle = device_priv->clEnqueueAcquireDX9MediaSurfacesKHR(
frames_priv->command_queue, desc->nb_planes, desc->planes,
0, NULL, &event);
if (cle != CL_SUCCESS) {
av_log(dst_fc, AV_LOG_ERROR, "Failed to acquire surface "
"handle: %d.\n", cle);
return AVERROR(EIO);
}
err = opencl_wait_events(dst_fc, &event, 1);
if (err < 0)
goto fail;
for (i = 0; i < desc->nb_planes; i++)
dst->data[i] = (uint8_t*)desc->planes[i];
err = ff_hwframe_map_create(dst->hw_frames_ctx, dst, src,
&opencl_unmap_from_dxva2, desc);
if (err < 0)
goto fail;
dst->width = src->width;
dst->height = src->height;
return 0;
fail:
cle = device_priv->clEnqueueReleaseDX9MediaSurfacesKHR(
frames_priv->command_queue, desc->nb_planes, desc->planes,
0, NULL, &event);
if (cle == CL_SUCCESS)
opencl_wait_events(dst_fc, &event, 1);
return err;
}
static int opencl_frames_derive_from_dxva2(AVHWFramesContext *dst_fc,
AVHWFramesContext *src_fc, int flags)
{
AVOpenCLDeviceContext *dst_dev = dst_fc->device_ctx->hwctx;
AVDXVA2FramesContext *src_hwctx = src_fc->hwctx;
OpenCLDeviceContext *device_priv = dst_fc->device_ctx->internal->priv;
OpenCLFramesContext *frames_priv = dst_fc->internal->priv;
cl_mem_flags cl_flags;
cl_int cle;
int err, i, p, nb_planes;
if (src_fc->sw_format != AV_PIX_FMT_NV12) {
av_log(dst_fc, AV_LOG_ERROR, "Only NV12 textures are supported "
"for DXVA2 to OpenCL mapping.\n");
return AVERROR(EINVAL);
}
nb_planes = 2;
if (src_fc->initial_pool_size == 0) {
av_log(dst_fc, AV_LOG_ERROR, "Only fixed-size pools are supported "
"for DXVA2 to OpenCL mapping.\n");
return AVERROR(EINVAL);
}
cl_flags = opencl_mem_flags_for_mapping(flags);
if (!cl_flags)
return AVERROR(EINVAL);
frames_priv->nb_mapped_frames = src_hwctx->nb_surfaces;
frames_priv->mapped_frames =
av_mallocz_array(frames_priv->nb_mapped_frames,
sizeof(*frames_priv->mapped_frames));
if (!frames_priv->mapped_frames)
return AVERROR(ENOMEM);
for (i = 0; i < frames_priv->nb_mapped_frames; i++) {
AVOpenCLFrameDescriptor *desc = &frames_priv->mapped_frames[i];
cl_dx9_surface_info_khr surface_info = {
.resource = src_hwctx->surfaces[i],
.shared_handle = NULL,
};
desc->nb_planes = nb_planes;
for (p = 0; p < nb_planes; p++) {
desc->planes[p] =
device_priv->clCreateFromDX9MediaSurfaceKHR(
dst_dev->context, cl_flags,
device_priv->dx9_media_adapter_type,
&surface_info, p, &cle);
if (!desc->planes[p]) {
av_log(dst_fc, AV_LOG_ERROR, "Failed to create CL "
"image from plane %d of DXVA2 surface %d: %d.\n",
p, i, cle);
err = AVERROR(EIO);
goto fail;
}
}
}
return 0;
fail:
for (i = 0; i < frames_priv->nb_mapped_frames; i++) {
AVOpenCLFrameDescriptor *desc = &frames_priv->mapped_frames[i];
for (p = 0; p < desc->nb_planes; p++) {
if (desc->planes[p])
clReleaseMemObject(desc->planes[p]);
}
}
av_freep(&frames_priv->mapped_frames);
frames_priv->nb_mapped_frames = 0;
return err;
}
#endif
#if HAVE_OPENCL_D3D11
static void opencl_unmap_from_d3d11(AVHWFramesContext *dst_fc,
HWMapDescriptor *hwmap)
{
AVOpenCLFrameDescriptor *desc = hwmap->priv;
OpenCLDeviceContext *device_priv = dst_fc->device_ctx->internal->priv;
OpenCLFramesContext *frames_priv = dst_fc->device_ctx->internal->priv;
cl_event event;
cl_int cle;
cle = device_priv->clEnqueueReleaseD3D11ObjectsKHR(
frames_priv->command_queue, desc->nb_planes, desc->planes,
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);
}
static int opencl_map_from_d3d11(AVHWFramesContext *dst_fc, AVFrame *dst,
const AVFrame *src, int flags)
{
OpenCLDeviceContext *device_priv = dst_fc->device_ctx->internal->priv;
OpenCLFramesContext *frames_priv = dst_fc->internal->priv;
AVOpenCLFrameDescriptor *desc;
cl_event event;
cl_int cle;
int err, index, i;
index = (intptr_t)src->data[1];
if (index >= frames_priv->nb_mapped_frames) {
av_log(dst_fc, AV_LOG_ERROR, "Texture array index out of range for "
"mapping: %d >= %d.\n", index, frames_priv->nb_mapped_frames);
return AVERROR(EINVAL);
}
av_log(dst_fc, AV_LOG_DEBUG, "Map D3D11 texture %d to OpenCL.\n",
index);
desc = &frames_priv->mapped_frames[index];
cle = device_priv->clEnqueueAcquireD3D11ObjectsKHR(
frames_priv->command_queue, desc->nb_planes, desc->planes,
0, NULL, &event);
if (cle != CL_SUCCESS) {
av_log(dst_fc, AV_LOG_ERROR, "Failed to acquire surface "
"handle: %d.\n", cle);
return AVERROR(EIO);
}
err = opencl_wait_events(dst_fc, &event, 1);
if (err < 0)
goto fail;
for (i = 0; i < desc->nb_planes; i++)
dst->data[i] = (uint8_t*)desc->planes[i];
err = ff_hwframe_map_create(dst->hw_frames_ctx, dst, src,
&opencl_unmap_from_d3d11, desc);
if (err < 0)
goto fail;
dst->width = src->width;
dst->height = src->height;
return 0;
fail:
cle = device_priv->clEnqueueReleaseD3D11ObjectsKHR(
frames_priv->command_queue, desc->nb_planes, desc->planes,
0, NULL, &event);
if (cle == CL_SUCCESS)
opencl_wait_events(dst_fc, &event, 1);
return err;
}
static int opencl_frames_derive_from_d3d11(AVHWFramesContext *dst_fc,
AVHWFramesContext *src_fc, int flags)
{
AVOpenCLDeviceContext *dst_dev = dst_fc->device_ctx->hwctx;
AVD3D11VAFramesContext *src_hwctx = src_fc->hwctx;
OpenCLDeviceContext *device_priv = dst_fc->device_ctx->internal->priv;
OpenCLFramesContext *frames_priv = dst_fc->internal->priv;
cl_mem_flags cl_flags;
cl_int cle;
int err, i, p, nb_planes;
if (src_fc->sw_format != AV_PIX_FMT_NV12) {
av_log(dst_fc, AV_LOG_ERROR, "Only NV12 textures are supported "
"for D3D11 to OpenCL mapping.\n");
return AVERROR(EINVAL);
}
nb_planes = 2;
if (src_fc->initial_pool_size == 0) {
av_log(dst_fc, AV_LOG_ERROR, "Only fixed-size pools are supported "
"for D3D11 to OpenCL mapping.\n");
return AVERROR(EINVAL);
}
cl_flags = opencl_mem_flags_for_mapping(flags);
if (!cl_flags)
return AVERROR(EINVAL);
frames_priv->nb_mapped_frames = src_fc->initial_pool_size;
frames_priv->mapped_frames =
av_mallocz_array(frames_priv->nb_mapped_frames,
sizeof(*frames_priv->mapped_frames));
if (!frames_priv->mapped_frames)
return AVERROR(ENOMEM);
for (i = 0; i < frames_priv->nb_mapped_frames; i++) {
AVOpenCLFrameDescriptor *desc = &frames_priv->mapped_frames[i];
desc->nb_planes = nb_planes;
for (p = 0; p < nb_planes; p++) {
UINT subresource = 2 * i + p;
desc->planes[p] =
device_priv->clCreateFromD3D11Texture2DKHR(
dst_dev->context, cl_flags, src_hwctx->texture,
subresource, &cle);
if (!desc->planes[p]) {
av_log(dst_fc, AV_LOG_ERROR, "Failed to create CL "
"image from plane %d of D3D texture "
"index %d (subresource %u): %d.\n",
p, i, (unsigned int)subresource, cle);
err = AVERROR(EIO);
goto fail;
}
}
}
return 0;
fail:
for (i = 0; i < frames_priv->nb_mapped_frames; i++) {
AVOpenCLFrameDescriptor *desc = &frames_priv->mapped_frames[i];
for (p = 0; p < desc->nb_planes; p++) {
if (desc->planes[p])
clReleaseMemObject(desc->planes[p]);
}
}
av_freep(&frames_priv->mapped_frames);
frames_priv->nb_mapped_frames = 0;
return err;
}
#endif
#if HAVE_OPENCL_DRM_ARM
typedef struct DRMARMtoOpenCLMapping {
int nb_objects;
cl_mem object_buffers[AV_DRM_MAX_PLANES];
int nb_planes;
cl_mem plane_images[AV_DRM_MAX_PLANES];
} DRMARMtoOpenCLMapping;
static void opencl_unmap_from_drm_arm(AVHWFramesContext *dst_fc,
HWMapDescriptor *hwmap)
{
DRMARMtoOpenCLMapping *mapping = hwmap->priv;
int i;
for (i = 0; i < mapping->nb_planes; i++)
clReleaseMemObject(mapping->plane_images[i]);
for (i = 0; i < mapping->nb_objects; i++)
clReleaseMemObject(mapping->object_buffers[i]);
av_free(mapping);
}
static int opencl_map_from_drm_arm(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;
const AVDRMFrameDescriptor *desc;
DRMARMtoOpenCLMapping *mapping = NULL;
cl_mem_flags cl_flags;
const cl_import_properties_arm props[3] = {
CL_IMPORT_TYPE_ARM, CL_IMPORT_TYPE_DMA_BUF_ARM, 0,
};
cl_int cle;
int err, i, j;
desc = (const AVDRMFrameDescriptor*)src->data[0];
cl_flags = opencl_mem_flags_for_mapping(flags);
if (!cl_flags)
return AVERROR(EINVAL);
mapping = av_mallocz(sizeof(*mapping));
if (!mapping)
return AVERROR(ENOMEM);
mapping->nb_objects = desc->nb_objects;
for (i = 0; i < desc->nb_objects; i++) {
int fd = desc->objects[i].fd;
av_log(dst_fc, AV_LOG_DEBUG, "Map DRM PRIME fd %d to OpenCL.\n", fd);
if (desc->objects[i].format_modifier) {
av_log(dst_fc, AV_LOG_DEBUG, "Warning: object %d fd %d has "
"nonzero format modifier %"PRId64", result may not "
"be as expected.\n", i, fd,
desc->objects[i].format_modifier);
}
mapping->object_buffers[i] =
clImportMemoryARM(dst_dev->context, cl_flags, props,
&fd, desc->objects[i].size, &cle);
if (!mapping->object_buffers[i]) {
av_log(dst_fc, AV_LOG_ERROR, "Failed to create CL buffer "
"from object %d (fd %d, size %"SIZE_SPECIFIER") of DRM frame: %d.\n",
i, fd, desc->objects[i].size, cle);
err = AVERROR(EIO);
goto fail;
}
}
mapping->nb_planes = 0;
for (i = 0; i < desc->nb_layers; i++) {
const AVDRMLayerDescriptor *layer = &desc->layers[i];
for (j = 0; j < layer->nb_planes; j++) {
const AVDRMPlaneDescriptor *plane = &layer->planes[j];
cl_mem plane_buffer;
cl_image_format image_format;
cl_image_desc image_desc;
cl_buffer_region region;
int p = mapping->nb_planes;
err = opencl_get_plane_format(src_fc->sw_format, p,
src_fc->width, src_fc->height,
&image_format, &image_desc);
if (err < 0) {
av_log(dst_fc, AV_LOG_ERROR, "Invalid plane %d (DRM "
"layer %d plane %d): %d.\n", p, i, j, err);
goto fail;
}
region.origin = plane->offset;
region.size = image_desc.image_row_pitch *
image_desc.image_height;
plane_buffer =
clCreateSubBuffer(mapping->object_buffers[plane->object_index],
cl_flags,
CL_BUFFER_CREATE_TYPE_REGION,
&region, &cle);
if (!plane_buffer) {
av_log(dst_fc, AV_LOG_ERROR, "Failed to create sub-buffer "
"for plane %d: %d.\n", p, cle);
err = AVERROR(EIO);
goto fail;
}
image_desc.buffer = plane_buffer;
mapping->plane_images[p] =
clCreateImage(dst_dev->context, cl_flags,
&image_format, &image_desc, NULL, &cle);
// Unreference the sub-buffer immediately - we don't need it
// directly and a reference is held by the image.
clReleaseMemObject(plane_buffer);
if (!mapping->plane_images[p]) {
av_log(dst_fc, AV_LOG_ERROR, "Failed to create image "
"for plane %d: %d.\n", p, cle);
err = AVERROR(EIO);
goto fail;
}
++mapping->nb_planes;
}
}
for (i = 0; i < mapping->nb_planes; i++)
dst->data[i] = (uint8_t*)mapping->plane_images[i];
err = ff_hwframe_map_create(dst->hw_frames_ctx, dst, src,
&opencl_unmap_from_drm_arm, mapping);
if (err < 0)
goto fail;
dst->width = src->width;
dst->height = src->height;
return 0;
fail:
for (i = 0; i < mapping->nb_planes; i++) {
clReleaseMemObject(mapping->plane_images[i]);
}
for (i = 0; i < mapping->nb_objects; i++) {
if (mapping->object_buffers[i])
clReleaseMemObject(mapping->object_buffers[i]);
}
av_free(mapping);
return err;
}
#endif
static int opencl_map_from(AVHWFramesContext *hwfc, AVFrame *dst,
const AVFrame *src, int flags)
{
av_assert0(src->format == AV_PIX_FMT_OPENCL);
if (hwfc->sw_format != dst->format)
return AVERROR(ENOSYS);
return opencl_map_frame(hwfc, dst, src, flags);
}
static int opencl_map_to(AVHWFramesContext *hwfc, AVFrame *dst,
const AVFrame *src, int flags)
{
av_unused OpenCLDeviceContext *priv = hwfc->device_ctx->internal->priv;
av_assert0(dst->format == AV_PIX_FMT_OPENCL);
switch (src->format) {
#if HAVE_OPENCL_DRM_BEIGNET
case AV_PIX_FMT_DRM_PRIME:
if (priv->beignet_drm_mapping_usable)
return opencl_map_from_drm_beignet(hwfc, dst, src, flags);
#endif
#if HAVE_OPENCL_VAAPI_BEIGNET
case AV_PIX_FMT_VAAPI:
if (priv->beignet_drm_mapping_usable)
return opencl_map_from_vaapi(hwfc, dst, src, flags);
#endif
#if HAVE_OPENCL_VAAPI_INTEL_MEDIA
case AV_PIX_FMT_QSV:
case AV_PIX_FMT_VAAPI:
if (priv->qsv_mapping_usable)
return opencl_map_from_qsv(hwfc, dst, src, flags);
#endif
#if HAVE_OPENCL_DXVA2
case AV_PIX_FMT_DXVA2_VLD:
if (priv->dxva2_mapping_usable)
return opencl_map_from_dxva2(hwfc, dst, src, flags);
#endif
#if HAVE_OPENCL_D3D11
case AV_PIX_FMT_D3D11:
if (priv->d3d11_mapping_usable)
return opencl_map_from_d3d11(hwfc, dst, src, flags);
#endif
#if HAVE_OPENCL_DRM_ARM
case AV_PIX_FMT_DRM_PRIME:
if (priv->drm_arm_mapping_usable)
return opencl_map_from_drm_arm(hwfc, dst, src, flags);
#endif
}
return AVERROR(ENOSYS);
}
static int opencl_frames_derive_to(AVHWFramesContext *dst_fc,
AVHWFramesContext *src_fc, int flags)
{
av_unused OpenCLDeviceContext *priv = dst_fc->device_ctx->internal->priv;
switch (src_fc->device_ctx->type) {
#if HAVE_OPENCL_DRM_BEIGNET
case AV_HWDEVICE_TYPE_DRM:
if (!priv->beignet_drm_mapping_usable)
return AVERROR(ENOSYS);
break;
#endif
#if HAVE_OPENCL_VAAPI_BEIGNET
case AV_HWDEVICE_TYPE_VAAPI:
if (!priv->beignet_drm_mapping_usable)
return AVERROR(ENOSYS);
break;
#endif
#if HAVE_OPENCL_VAAPI_INTEL_MEDIA
case AV_HWDEVICE_TYPE_QSV:
case AV_HWDEVICE_TYPE_VAAPI:
if (!priv->qsv_mapping_usable)
return AVERROR(ENOSYS);
break;
#endif
#if HAVE_OPENCL_DXVA2
case AV_HWDEVICE_TYPE_DXVA2:
if (!priv->dxva2_mapping_usable)
return AVERROR(ENOSYS);
{
int err;
err = opencl_frames_derive_from_dxva2(dst_fc, src_fc, flags);
if (err < 0)
return err;
}
break;
#endif
#if HAVE_OPENCL_D3D11
case AV_HWDEVICE_TYPE_D3D11VA:
if (!priv->d3d11_mapping_usable)
return AVERROR(ENOSYS);
{
int err;
err = opencl_frames_derive_from_d3d11(dst_fc, src_fc, flags);
if (err < 0)
return err;
}
break;
#endif
#if HAVE_OPENCL_DRM_ARM
case AV_HWDEVICE_TYPE_DRM:
if (!priv->drm_arm_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",
.device_hwctx_size = sizeof(AVOpenCLDeviceContext),
.device_priv_size = sizeof(OpenCLDeviceContext),
.frames_hwctx_size = sizeof(AVOpenCLFramesContext),
.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,
.frames_get_constraints = &opencl_frames_get_constraints,
.frames_init = &opencl_frames_init,
.frames_uninit = &opencl_frames_uninit,
.frames_get_buffer = &opencl_get_buffer,
.transfer_get_formats = &opencl_transfer_get_formats,
.transfer_data_to = &opencl_transfer_data_to,
.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,
AV_PIX_FMT_NONE
},
};