blob: cd8638abbb9cecf0253b2d70ec2aef078d78b173 [file] [log] [blame]
/*
* 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
},
};