Intel MediaSDK sample_decode 官方GPU解码流程学习(二) - 在双显卡机器上实现DirectX11 D3D11和OpenCL共享资源

2023-10-27

很久以前写过有关D3D11和OCL直接共享显存的代码,

Intel MediaSDK sample_decode 官方GPU解码流程学习 - DirectX11 D3D11和OpenCL共享资源

这段代码一直运行的很好,被我用来做验证显卡驱动里的D3D11和OCL兼容性的一个测试项目。

在双显卡机器上发现新问题

最近发现这个测试程序在Intel双显卡的笔记本上运行不正常,当命令行指定使用集显UHD630或者独显Intel Iris(R) Xe MAX Graphics上做硬件解码的时候

sample_decode h264 -i test.h264 -hw -d3d11 -r -iGfx[-dGfx]

总会有一块显卡在运行时会失败,调试了一下代码,发现是在初始化OpenCL的时候,在调用clGetDeviceIDsFromD3D11KHR()时返回-1 (CL_DEVICE_NOT_FOUND),因此后面创建OCL的clContext和clCommandQueue之类的操作就都失败了。

		status = ptrToFunction_clGetDeviceIDsFromD3D11KHR(g_platformToUse, CL_D3D11_DEVICE_KHR, (void *)g_pd3dDevice, CL_PREFERRED_DEVICES_FOR_D3D11_KHR, 0, NULL, &numDevs);
		printf("clGetDeviceIDsFromD3D11KHR returns status = %d\n", status);

运行输出
clGetDeviceIDsFromD3D11KHR returns status = -1

查了下这个函数的作用 Ubuntu Manpage: clGetDeviceIDsFromD3D11KHR - Querying OpenCL Devices Corresponding to Direct3D 11 Devices.

clGetDeviceIDsFromD3D11KHR - Querying OpenCL Devices Corresponding to Direct3D 11 Devices.

cl_int clGetDeviceIDsFromD3D11KHR(cl_platform_id platform,
                                         cl_d3d11_device_source_khr d3d_device_source,
                                         void *d3d_object,
                                         cl_d3d11_device_set_khr d3d_device_set,
                                         cl_uint num_entries, cl_device_id *devices,
                                         cl_uint *num_devices);

ERRORS

Returns CL_SUCCESS if the function is executed successfully. Otherwise it may return:

       ·   CL_INVALID_PLATFORM if platform is not a valid platform.

       ·   CL_INVALID_VALUE if d3d_device_source is not a valid value, d3d_device_set is not a
           valid value, num_entries is equal to zero and devices is not NULL, or if both
           num_devices and devices are NULL.

       ·   CL_DEVICE_NOT_FOUND if no OpenCL devices that correspond to d3d_object were found.

返回CL_DEVICE_NOT_FOUND说明在传进去的参数 d3d_object platform 之间不匹配。

跟踪了一下代码, d3d_object是通过D3D11CreateDevice()创建的,由传进去的-iGfx/-dGfx控制创建在独显或者集显硬件设备上

platform这个参数就比较有趣了,现在的代码流程是这样

	//[1] get the platform
	g_platformToUse = NULL;
	status = clGetPlatformIDs(0, NULL, &numPlatforms);
	testStatus(status, "clGetPlatformIDs error");
	
	cl_platform_id *platforms = (cl_platform_id *)malloc(sizeof(cl_platform_id) * numPlatforms);
	if(platforms == NULL)
	{
		printf("Error when allocating space for the platforms\n");
		exit(EXIT_FAILURE);
	}

	status = clGetPlatformIDs(numPlatforms, platforms, NULL);
	testStatus(status, "clGetPlatformIDs error");

	//[2] get device ids for the platform i have obtained
	cl_uint num_devices = -1; //yeah yeah, i know uint 
	cl_device_info devTypeToUse = CL_DEVICE_CPU_OR_GPU;  //set as CPU or GPU from host_common.h	
	for(unsigned int i=0;i<numPlatforms;i++)
	{
		char pbuf[256];
		status = clGetPlatformInfo(platforms[i], CL_PLATFORM_VENDOR, sizeof(pbuf), pbuf, NULL);
		testStatus(status, "clGetPlatformInfo error");

		//leaving this strcmp for AMD, used when debugging and is the precise AMD platform name
		//if(!strcmp(pbuf, "Advanced Micro Devices, Inc.")) 
		//We found the Intel Platform, this is the one we wantto use
		if(!strcmp(pbuf, "Intel(R) Corporation"))
		{
			printf("Great! We found an Intel OpenCL platform.\n");
			g_platformToUse = platforms[i];
			status = clGetDeviceIDs(g_platformToUse, devTypeToUse, 0, g_clDevices, &num_devices);
			if (status == CL_DEVICE_NOT_FOUND)
				continue; 
			else
				break;
		}
	}

 对于单显卡或者只有1块intel显卡的时候,这个流程是没问题的。但是在有2块Intel显卡的机器上,枚举ocl platformd的数量会返回2;而且2块ocl platform的vendor名字都包含"Intel(R) Corporation",但是根据这个逻辑,代码永远只会用搜到的第一个包含"Intel"字串的设备作为后面的platform 参数。(而且我发现根据安装驱动的方法和流程不同,这个默认的第一个OCL platform有时候是UHD630,有时候是Iris Xe MAX...)。如果d3d11设备是创建在UHD630上的,而opencl platform是对应的Iris Xe MAX, 调用clGetDeviceIDsFromD3D11KHR()就一定会返回-1, 这也很好理解,2个框架创建在不同的硬件设备上,显存当然不能共享访问了。

解决方法1 - 使用厂家定义的OCL extension

要解决这个问题,只能在OCL框架里把每个platform下面的device打印出来,看看怎么和d3d11设备匹配... 这段代码要改

            //获取每个platform下面的设备数量
			status = clGetDeviceIDs(platforms[i], CL_DEVICE_TYPE_ALL, num_devices, clDevices, 0);
            ...

			for(cl_uint iDevNum=0;iDevNum<num_devices;iDevNum++)
			{
                //获取每个设备的一些OCL属性
				clGetDeviceInfo(clDevices[iDevNum], CL_DEVICE_TYPE, sizeof(cl_device_type), (void *)&device_type, NULL);
				clGetDeviceInfo(clDevices[iDevNum], CL_DEVICE_VENDOR, (sizeof(char)*256), &vendorName, NULL);
				clGetDeviceInfo(clDevices[iDevNum], CL_DEVICE_EXTENSIONS, (sizeof(char)*2048), &devExtString, NULL);

                ...
                ...
                ...
            }

接着发现了一个问题,OpenCL里面只能获取CL_DEVICE_VENDOR_ID,  就是0x8086, 获取不了device id.

D3D11 API这边可以获取vendor id和device id

		DXGI_ADAPTER_DESC adapterDesc;
		adapter->GetDesc(&adapterDesc);
		if (wcsstr(adapterDesc.Description, L"Intel"))
		{
			intel = adapterIdx;
			printf("Found %d: %ls VendorId:%08X DeviceId:%08X\n", adapterIdx,adapterDesc.Description, adapterDesc.VendorId, adapterDesc.DeviceId);
		}


运行输出:
Found 0: Intel(R) UHD Graphics 630 VendorId:00008086 DeviceId:00003E98

 为了找到OpenCL下面获取device id的方法,再度和谷哥讨论了一下,发现OpenCL标准里确实没有这个功能,但是对于双显卡的场景,这又是个刚需。所以A家N家都是通过OCL Extension的方式增加了一个接口Associating OpenCL device ids with GPUs |Anteru's Blog

Intel显卡这边同样也是增加了一个接口,在OpenCL 3.0的Extension里 cl_intel_device_attribute_query

所以用之前需要查询OpenCL是否支持cl_intel_device_attribute_query这个extension. 这个需要驱动支持

			status = clGetDeviceIDs(platforms[i], CL_DEVICE_TYPE_ALL, num_devices, clDevices, 0);
			testStatus(status, "clGetDeviceIDs error");

			for(cl_uint iDevNum=0;iDevNum<num_devices;iDevNum++)
			{
				//query each for their extension string
				//print out the device type just to make sure we got it right
				cl_device_type device_type;
				char vendorName[256];
				memset(vendorName, '\0', 256);
				char devExtString[2048];
				memset(devExtString, '\0', 2048);

				clGetDeviceInfo(clDevices[iDevNum], CL_DEVICE_TYPE, sizeof(cl_device_type), (void *)&device_type, NULL);
				clGetDeviceInfo(clDevices[iDevNum], CL_DEVICE_VENDOR, (sizeof(char)*256), &vendorName, NULL);
				clGetDeviceInfo(clDevices[iDevNum], CL_DEVICE_EXTENSIONS, (sizeof(char)*2048), &devExtString, NULL);

//查询当前显卡驱动是否支持cl_khr_d3d11_sharing扩展
				char *extStringStart1 = NULL;
				extStringStart1 = strstr(devExtString, "cl_khr_d3d11_sharing");
                ...
//查询当前显卡驱动是否支持cl_intel_device_attribute_query扩展
                char *extStringStart2 = NULL;
				extStringStart2 = strstr(devExtString, "cl_intel_device_attribute_query");

				if (extStringStart2 != 0)
				{
					if (device_type == CL_DEVICE_TYPE_CPU)
					{
						strcpy_s(devTypeString, "CPU");
					}
					else if (device_type == CL_DEVICE_TYPE_GPU)
					{
						strcpy_s(devTypeString, "GPU");
					}
					else
					{
						strcpy_s(devTypeString, "Not a CPU or GPU"); //for sample code, not product
					}
					printf("Device %s in %s platform supports cl_intel_device_attribute_query extension\n", devTypeString, vendorName);
				}
				else
				{
					printf("Device %s in %s platform does not support cl_intel_device_attribute_query extension\n", devTypeString, vendorName);
				}
 			} //end for(...)

如果支持这个extension, 接下来就可以获取device id了

					//print out the device type just to make sure we got it right
					cl_device_type device_type;
					cl_uint device_vendor_id;
					cl_uint device_device_id;
					char vendorName[256];
					char DeviceVersion[1024];
					memset(vendorName, '\0', 256);
					memset(DeviceVersion, '\0', 1024);

#define CL_DEVICE_ID_INTEL                        0x4251

					clGetDeviceInfo(dummy_g_clDevices[dummy_device_id], CL_DEVICE_TYPE, sizeof(cl_device_type), (void *)&device_type, NULL);
					clGetDeviceInfo(dummy_g_clDevices[dummy_device_id], CL_DEVICE_VENDOR_ID, sizeof(cl_uint), (void *)&device_vendor_id, NULL);
					clGetDeviceInfo(dummy_g_clDevices[dummy_device_id], CL_DEVICE_ID_INTEL, sizeof(cl_uint), (void *)&device_device_id, NULL);

					clGetDeviceInfo(dummy_g_clDevices[dummy_device_id], CL_DEVICE_VENDOR, (sizeof(char) * 256), vendorName, NULL);
					clGetDeviceInfo(dummy_g_clDevices[dummy_device_id], CL_DEVICE_VERSION, (sizeof(char) * 1024), DeviceVersion, NULL);



					if (device_type == CL_DEVICE_TYPE_CPU)
					{
						printf("		[%d] Device type is CPU, Vendor is %s\n", dummy_device_id, vendorName);
					}
					else if (device_type == CL_DEVICE_TYPE_GPU)
					{
						printf("		[%d] Device type is GPU, Vendor is %s\n", dummy_device_id, vendorName);
					}
					else
					{
						printf("		[%d] device type is unknown\n",dummy_device_id );
					}

					clGetDeviceInfo(dummy_g_clDevices[0], CL_DEVICE_NAME, (sizeof(char) * 256), vendorName, NULL);
					printf("		[%d] Device name is %s\n", dummy_device_id, vendorName);
					printf("		[%d] Device_vendor_id is 0x%08X\n", dummy_device_id, device_vendor_id);
					printf("		[%d] device_device_id is 0x%08X\n", dummy_device_id, device_device_id);
					printf("		[%d] CL_DEVICE_VERSION is %s\n", dummy_device_id, DeviceVersion);

运行输出:
Great! We found an Intel OpenCL platform. - platform index[0]
        platform index[0] : num_devices = 1
                [0] Device type is GPU, Vendor is Intel(R) Corporation
                [0] Device name is Intel(R) UHD Graphics 630
                [0] Device_vendor_id is 0x00008086
                [0] device_device_id is 0x00003E98
                [0] CL_DEVICE_VERSION is OpenCL 3.0 NEO

Great! We found an Intel OpenCL platform. - platform index[1]
        platform index[1] : num_devices = 1
                [0] Device type is GPU, Vendor is Intel(R) Corporation
                [0] Device name is Intel(R) Iris(R) Xe MAX Graphics
                [0] Device_vendor_id is 0x00008086
                [0] device_device_id is 0x00004905
                [0] CL_DEVICE_VERSION is OpenCL 3.0 NEO

从2个platform 里分别获取了2个显卡的device id, 有了这些信息,我们就知道该把哪个platform作为参数传给clGetDeviceIDsFromD3D11KHR()了

解决方法2 - 一种更通用的解决方式

上面这种模式算是基本解决了问题,但是也发现了一个新的问题:这个OCL获取device id的扩展是在opencl 3.0的标准里加进去的,也就是说只支持OpenCL 2.0的老的显卡驱动里,如果查询显卡属性不支持"cl_intel_device_attribute_query"的时候是无法获取到这个device id的。比如下面这个输出是在一个非常老的显卡驱动上跑的,此时驱动只支持到opencl 2.1。

Great! We found an Intel OpenCL platform. - platform index[0]
        platform index[0] : num_devices = 1
                [0] Device type is GPU, Vendor is Intel(R) Corporation
                [0] Device name is Intel(R) UHD Graphics 630
                [0] Device_vendor_id is 0x00008086
                [0] device_device_id is 0x00000000
                [0] CL_DEVICE_VERSION is OpenCL 2.1 NEO

对于这种情况,如果要求每一台机器都升级到最新的显卡驱动,显然是不现实的。所以就想到了另一个方法,既然一般家用电脑最多也就是双显卡的配置,那么前面枚举OpenCL platform的时候,最多也就会获得2个platform, 那么索性就把这2个platform依次传给clGetDeviceIDsFromD3D11KHR()匹配,如果不匹配就会返回CL_DEVICE_NOT_FOUND, 如果返回CL_SUCCESS就继续进行下面的OpenCL的初始化。

    //获取当前平台下面的cl platform数量和信息
	std::vector < cl_platform_id> intel_platforms;

	intel_platforms.empty();

	//[1] get the platform
	g_platformToUse = NULL;
	status = clGetPlatformIDs(0, NULL, &numPlatforms);
	testStatus(status, "clGetPlatformIDs error");
	
	cl_platform_id *platforms = (cl_platform_id *)malloc(sizeof(cl_platform_id) * numPlatforms);
	if(platforms == NULL)
	{
		printf("Error when allocating space for the platforms\n");
		exit(EXIT_FAILURE);
	}

	status = clGetPlatformIDs(numPlatforms, platforms, NULL);
	testStatus(status, "clGetPlatformIDs error");
	printf("Total numPlatforms found = %d\n", numPlatforms);


	for(unsigned int i=0;i<numPlatforms;i++)
	{
		char pbuf[256];
        //获取这个platform的vendro描述字符串
		status = clGetPlatformInfo(platforms[i], CL_PLATFORM_VENDOR, sizeof(pbuf), pbuf, NULL);
		testStatus(status, "clGetPlatformInfo error");

        ...

		if(!strcmp(pbuf, "Intel(R) Corporation"))
		{
            //找到一个intel platform
			printf("Great! We found an Intel OpenCL platform. - platform index[%d]\n",i);

            ...
            //将符合条件的platform加入到intel_platforms这个vector容器里
			intel_platforms.push_back(platforms[i]);
            
            ...
        }
    }


    ...
    ...
    ...



	cl_int platform_index;
    //用遍历的方式依次匹配d3d11设备和opencl platform
	for (cl_platform_id intel_platform : intel_platforms)
	{
		g_platformToUse = intel_platform;

		cps[1] = (cl_context_properties)g_platformToUse;

		ptrToFunction_clGetDeviceIDsFromD3D11KHR = NULL;
		ptrToFunction_clGetDeviceIDsFromD3D11KHR = (clGetDeviceIDsFromD3D11KHR_fn)clGetExtensionFunctionAddressForPlatform(g_platformToUse, "clGetDeviceIDsFromD3D11KHR");

		numDevs = 0;
		//size_t bytes = 0;
		//careful with the g_pd3DDevice
		status = ptrToFunction_clGetDeviceIDsFromD3D11KHR(g_platformToUse, CL_D3D11_DEVICE_KHR, (void *)g_pd3dDevice, CL_PREFERRED_DEVICES_FOR_D3D11_KHR, 0, NULL, &numDevs);
		printf("clGetDeviceIDsFromD3D11KHR returns status = %d\n", status);

		std::vector <cl_platform_id> ::iterator itr;
		itr = find(intel_platforms.begin(), intel_platforms.end(), intel_platform);
		cl_int platform_index = distance(intel_platforms.begin(), itr);

		if (status != CL_SUCCESS)
		{
            //不匹配,继续匹配下一个platform
			printf("Failed: the [%d/%d] OCL platform unmatch\n", platform_index + 1, intel_platforms.size());
		}
		else
		{
            //匹配成功,跳出循环,进行后面的初始化
			printf("Good: Found the matched OCL platform [%d/%d]\n", platform_index + 1, intel_platforms.size());
			break;
		}

	}

    //检查是否匹配到正确的cl platform
	testStatus(status, "Failed on clGetDeviceIDsFromD3D11KHR");

最后测试一下遍历匹配的代码

 没啥大问题,搞定收工 ^_^

最后还是老规矩,源码附上,仅供参考 :)

https://gitee.com/tisandman/d3d11_ocl_sharing

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Intel MediaSDK sample_decode 官方GPU解码流程学习(二) - 在双显卡机器上实现DirectX11 D3D11和OpenCL共享资源 的相关文章

随机推荐

  • 目标检测(Object Detection)原理与实现(五)

    基于Cascade分类器的目标检测 从今天开始进入基于机器学习的目标检测 前几节虽然也接触了一些机器学习的方法 但它主要是做辅助工作 机器学习的方法和非机器学习的方法结合在一起使用 说到这想起来前几天看到一位博士师兄发的笑话 说的是百度实验
  • 算法练习-NOJ-1005-装载问题

    时限 1000ms 内存限制 10000K 总时限 3000ms 描述 有两艘船 载重量分别是c1 c2 n个集装箱 重量是wi i 1 n 且所有集装箱的总重量不超过c1 c2 确定是否有可能将所有集装箱全部装入两艘船 输入 多个测例 每
  • outlook搜索栏跑到上面去了_工具方法

    我想这点文字主要对于研究生 或经常收发邮件的人比较有意义 可以帮助您 不会错过重要人物的信件 不会错过重要的日程安排 不会错过你关心领域的第一手资讯 如果你平时办公学习基本不使用邮件 那这篇文字对你没什么作用 就不用浪费时间阅读 当然 你也
  • Singleton单例模式-【懒汉式-加双重校验锁&防止指令重排序的懒汉式】实现方案中为什么需要加volatile关键字

    前提知识点 volatile可以保证可见性 防止指令重排序 synchronized可以保证可见性 防止指令重排序 原子性 也即是说volatile是synchronized的功能子集 我们知道在 懒汉式 加双重校验锁 的单例模式实现中已经
  • SSM项目打包到本地MAVEN仓库

  • redis哨兵模式原理

    阅读目录 redis哨兵模式架构 哨兵模式工作原理 哨兵如何判断master宕机 故障转移过程 主节点写压力过大 集群脑裂 主从数据不一致 总结 概述 为了实现redis集群的高可用 redis经历了好几次迭代 从最开始的主从模式 到哨兵模
  • 服务器发送了一个意外的数据包。received:3,expected:20“问题的解决方法

    XShell连接ssh服务器时提示这个 同事的没有问题 经过比对 我的是xshell5 0版本 同事的是6 0版本 升级xshell解决问题
  • C++友元函数,友元类

    友元函数 友元函数是在C 中用来访问另一个类的私有成员的一种机制 通过将函数声明为友元函数 可以使该函数能够访问类中的私有成员 即使它不是类的成员函数或者成员 在类的声明中声明友元函数 将友元函数的原型放在类的声明中 并在函数原型前加上fr
  • 静态代理

    一 静态代理概念 代理模式 目标对象类型的变量指向代理对象 然后调用方法的时候会执行代理对象的方法 代理对象的方法里面重写或者调用了目标对象的方法 并且在方法执行前后添加了一些功能 静态代理 写好对应的代理类源文件 在编译期就已经确定了目标
  • 微搭低代码学习之数据收集

    低代码和开发之间的关系 低代码平台是一种快速构建应用程序的工具 旨在提高开发效率 它们提供了一种基于图形用户界面的方式来创建应用程序 而无需编写大量的代码 使用低代码平台 开发人员可以更快速地构建和交付应用程序 从而缩短开发周期 然而 低代
  • 反向读取文件的每一行

    反向读取文件的每一行 作者 大矩阵作坊 加入时间 2004 02 19 浏览次数 100 有的初学者可能会尝试写一些文本数据的程序 并把每一条记录存为一行 如留言本 写入数据时 可能会把新添加的数据加入文件未尾 但是读文件并输出时 会发现也
  • Python中常用内置函数学习

    Python中的各个函数集合 时刻补充中 一 range函数 函数原型 range start end scan 参数含义 start 计数从start开始 默认是从0开始 例如range 5 等价于range 0 5 end 计数到end
  • 解决element-ui/element-plus中el-pagination分页组件显示英文

    解决element ui element plus中el pagination分页组件显示英文 解决方法 在main js或main ts中引入中文语言 import locale from element plus lib locale
  • Mybatis-Plus复杂语句多级嵌套分组带分页查询

    如 SELECT dbname FROM SELECT CONCAT db type table name as dbname FROM mdn table permission WHERE db type MYSQL ORDER BY c
  • 使用nginx搭建helm私有仓库

    使用nginx 起一个helm的http服务 root master1 docker run d name nginx p 81 80 v root charts usr share nginx html charts nginx 把本地的
  • Python--统计学检验

    1 导入相关库 import numpy as np import pandas as pd import matplotlib pyplot as plt from scipy import stats from scipy stats
  • python execute() 使用%s 拼接sql 避免sql注入攻击 好于.format

    1 execute 参数一 sql 语句 锁定当前查询结果行 cursor execute SELECT high low vol FROM table name WHERE symbol s FOR UPDATE symbol 2 for
  • 互联网程序员行话(黑话)合集

    一 招聘行话大全 能听懂证明你是历经磨难的老司机 刚开始投简历时 你总以为是这样的 其实大部分情况下是那样的 面试之后 HR让回去等消息 傻傻的等待 半个月以上没有回音 各种焦虑 明面上的意思就是实际意思的公司 貌似都是说的是别人的公司 下
  • 安全多方计算从入门到精通:MPC简介&JUGO平台

    简介 今天我们来介绍一下基于安全多方计算所设计出来的产品JUGO 从安全性角度来看 数据泄露 隐私安全问题严重 facebook的数据泄露事件闹得很大 原因就是facebook单方面将用户的个人数据提供给了第三方机构 这为个人数据的拥有权敲
  • Intel MediaSDK sample_decode 官方GPU解码流程学习(二) - 在双显卡机器上实现DirectX11 D3D11和OpenCL共享资源

    很久以前写过有关D3D11和OCL直接共享显存的代码 Intel MediaSDK sample decode 官方GPU解码流程学习 DirectX11 D3D11和OpenCL共享资源 这段代码一直运行的很好 被我用来做验证显卡驱动里的