ADL SDK V10.2 中文文档

译者前言

AMD显示库(AMD Display Library)可以在以下网址下载
http://developer.amd.com/tools-and-sdks/graphics-development/display-library-adl-sdk/
目前页面上可供下载的有7.0、8.0、9.0、10.0四个版本
译者选择下载的是10.0版,实际跳转到10.2版(ADL_SDK_V10.2.zip)
我已经将ADL_SDK_V10.2.zip上传到了csdn
http://download.csdn.net/detail/xbj1108_25/9830564
如果希望快速上手,可以跳到第四部分应用示例。

官网简介:
ADL SDK旨在访问AMD Radeon™和AMD FirePro™显卡的显示驱动程序的功能。SDK支持Radeon家族的产品,包括集成显卡、独立显卡及FirePro系列的产品。它也支持32位或64位的Windows® XP、Windows® Vista、Windows 7、Windows 8、Windows 10及 Linux®。SDK可以被非托管(C/C++)和托管(C#)应用程序使用。在下载包中提供了文档及示例。
注:需要催化剂16.30及以上版本

兼容性

所支持的AMD产品

  • AMD Radeon Graphics
  • AMD FirePro Graphics
  • AMD FX
  • AMD A Series

所支持的操作系统

  • Windows 7
  • Windows 8.1
  • Windows 10
  • Linux
  • Ubuntu
  • Red Hat

如何使用AMD显示库(ADL)

1. 介绍

在AMD显示库(ADL)诞生之前,AMD提供了其他几个供外部程序调用的SDK(PDL,DSP,CCC,COM),这些SDK只能在windows上工作,在Linux下是没有的。ADL的目标是为两款操作系统提供单个SDK,并最终取代其他SDK。

大部分ADL的API是封装AMD私有API的,这使得库的使用者可以访问与系统相关的图形信息。ADL被严格地用于暴露“图形硬件”支持。其他硬件(如CPU、北桥等)或软件子系统(如催化剂控制中心或HydraVision多桌面显示包)将通过他们自己的二进制文件暴露一个接口,如果他们有的话。

ADL 二进制文件是催化剂显示驱动程序包的一部分,而文档、定义和示例代码则通过网络下载的单个zip压缩包中分发。

2. ADL二进制

在32位Windows XP和32位Windows Vista操作系统中,ADL库的二进制文件在如下位置:

%WINDOWS%\System32\atiadlxx.dll

在64位Windows XP和64位Windows Vista操作系统中,有两个库

  • %WINDOWS%\System32\atiadlxx.dll(原生系统驱动)
  • %WINDOWS%\SysWOW64\atiadlxy.dll(32位系统驱动)

在Linux操作系统中,二进制文件的路径及名称是:
Linux 32位运行时(32位和64位操作系统):

/usr/X11R6/lib/Libatiadlxx.so

Linux 64位运行时(64位操作系统):

/usr/X11R6/lib64/Libatiadlxx.so

重点:在64位操作系统中提供32位库以确保对非托管的32位应用程序的支持。这些应用程序无法加载原生的64位ADL库,因此在加载失败后应该尝试加载32位库。注意两个库有不同的名称(不仅仅是路径)。这将在本文档稍后详细讨论。

3. 关于ADL的更多信息

3.1 API命名约定

ADL提供了一种类C风格的导出,这是典型的Windows DLL和Linux so库的风格。

一般来说,ADL对调用者的环境没有作任何的假设。ADL创建并销毁它自己的内存分配。客户程序可以选择性地要求ADL确保线程安全(通过在同一时刻只允许一个线程访问ADL的API)。可以在本文档中找到关于如何在线程安全模式下实例化ADL的详细信息。

返回数据的API要么通过堆栈返回简单的数据,要么需要调用者预先分配内存并且通过API调用说明提供预分配内存的引用。如果ADL调用返回到了它的调用者那里,它将在同一个线程中,并且所有的数据将由ADL管理;调用者将不会尝试对ADL提供的数据进行内存管理。

公开的API的名称以”ADL”或”ADL2”开头,并通常以”Get”、”Set”或”Caps”结尾,如下是命名风格的一个示例:

ADL_Xxx_Yyy_Get
ADL_Xxx_Yyy_Set
ADL2_Xxx_Yyy_Get
ADL2_Xxx_Yyy_Set

Xxx 可以是“Main”、“Adapter”、“Display”等,而Yyy 则详细描述了这个函数。

例如:

ADL_Display_NumberOfDisplays_Get 被用来检索适配器支持的显示器的个数。

返回值是一个描述ADL返回级别的整数。一般来说,负数表示输出参数无效,零值表示正常成功,正数表示成功,但需要更改模式或者重启系统。

“0:成功”的返回值表示了驱动程序以正常方式接收和处理请求, 并且可以根据每个 api 的规范处理输出参数。
以下是完整的返回值的集合及说明:

#define     ADL_OK_WAIT   4
//  All OK, but need to wait. 
#define     ADL_OK_RESTART   3
//  All OK, but need restart. 
#define     ADL_OK_MODE_CHANGE   2
//  All OK but need mode change. 
#define     ADL_OK_WARNING   1
//  All OK, but with warning. 
#define     ADL_OK   0
//  ADL function completed successfully. 
#define     ADL_ERR   -1
//  Generic Error. Most likely one or more of the Escape calls to the driver failed! 
#define     ADL_ERR_NOT_INIT   -2
//  ADL not initialized. 
#define     ADL_ERR_INVALID_PARAM   -3
//  One of the parameter passed is invalid. 
#define     ADL_ERR_INVALID_PARAM_SIZE   -4
//  One of the parameter size is invalid. 
#define     ADL_ERR_INVALID_ADL_IDX   -5
//  Invalid ADL index passed. 
#define     ADL_ERR_INVALID_CONTROLLER_IDX   -6
//  Invalid controller index passed. 
#define     ADL_ERR_INVALID_DIPLAY_IDX   -7
//  Invalid display index passed. 
#define     ADL_ERR_NOT_SUPPORTED   -8
//  Function not supported by the driver. 
#define     ADL_ERR_NULL_POINTER   -9
//  Null Pointer error. 
#define     ADL_ERR_DISABLED_ADAPTER   -10
//  Call can't be made due to disabled adapter. 
#define     ADL_ERR_INVALID_CALLBACK   -11
//  Invalid Callback. 
#define     ADL_ERR_RESOURCE_CONFLICT   -12
//  Display Resource conflict. 
#define     ADL_ERR_SET_INCOMPLETE   -20
#define     ADL_ERR_NO_XDISPLAY   -21
//  There's no Linux XDisplay in Linux Console environment.

3.2 线程处理及并发访问

ADL可以工作在两种模式下
1. 未锁定模式。在这种模式下,ADL允许多个客户端线程并发执行ADL的API。注意ADL不是线程安全的库。在未锁定模式下实例化ADL的客户程序需要提供自己的同步上下文。
2. 锁定模式。在这种模式下,ADL通过在所有暴露的ADLAPI的周围布置临界区来防止被多线程并发地访问。它确保在某个给定时间,只有一个客户线程可以进入ADL。

在多个进程使用他们自己的ADL实例访问驱动或者操作系统的情况下,驱动和操作系统必须为信道或操作系统的入口点保证多进程安全。在Windows中,GDI层使用全局的互斥体来确保只有一个调用(无论是否是一个进程中的)进入内核进行转义处理。同样地,在Linux中,X-Server也确保在任何进程中调用X-Server跨越所有进程,是这样单线程的。对于系统调用,系统在必要时自身提供了线程及进程安全,ADL不会在这一点上画蛇添足。

3.3 用例

  • 面向过程类应用程序:应用程序调用一个或多个API(在初始化/关闭API调用之外)执行特定的工作,这涉及到一系列的步骤:检查是否支持所需的接口;检查功能是否受支持;检索功能;检索特征的属性;更改可改变的属性的值;调用自定义操作。这些通常是执行特定任务的命令行驱动程序(如命令行类的配置工具或者自动测试工具)。
  • 事件驱动类应用程序:应用程序的工作方式是通过在程序中检测到的预先编程的事件进行响应,调用一组ADL API。应用程序本身可能是单线程或多线程的。事件驱动和面向过程程序的主要区别是对AMD Display的访问的总体顺序。在事件驱动的程序中,对ADL的访问往往涉及到一组少量的API,但总体上有大量的API,这些API根据外部事件确定分组顺序。

从ADL的角度来说,这两类应用程序没有区别。

3.4 常规工作流程

应用程序加载ADL,然后获取逻辑适配器的集合(对应于GPU控制器),每个条目元素包含每个GPU上每个控制器的基本信息。最重要的是适配器索引(AdapterIndex),它标识了特定GPU上的特定控制器。该索引提供了所有已知的GPU控制器的入口表。

使用适配器索引(AdapterIndex)后,应用程序可以调用其它API来:
1. 获取驱动程序组件的功能。
2. 获取与组件相关的数据。
3. 设置与组件相关的数据。
4. 请求特定操作。

每个ADL API方法的使用说明中都定义了适当的输入输出参数。调用方将数据作为输入参数的一部分提供,API将数据作为输出参数的一部分返回。此外,API返回ADL调用状态(ADL概念的值,而不是驱动组件提供的值)。

大多数ADL API在内部打包解包库中调用相应的 “打包” 例程来创建消息, 然后将消息发送到驱动程序。 然后, 驱动程序根据已发送的消息进行操作, 并返回一个返回消息。

在收到与已发送消息对应的驱动程序的返回消息后, API在内部打包解包库中调用相应的 “解包” 例程, 将返回的消息解码为相应的输出参数。 然后, API返回与 ADL 活动级别相关的结果代码。 在某些情况下, API 可以执行多个驱动程序调用或系统调用。 系统调用通常不使用打包/解包机制, 而是使用某些特定于系统的调用约定。

驱动程序组件错误、操作系统错误等, 可以按照每个 API方法定义的参数返回。

一般来说, ADL API将作为参数的各种数据, 定义在AD暴露的头文件中。 它还需要一个特定控制器的适配器索引(AdapterIndex),用于标识在哪个GPU控制器上采取操作。管理类API将调用打包库的打包例程,使用驱动程序头文件创建适当的消息(对ADL的调用者永远无法看到也不需要知道驱动程序头文件)。 打包库例程返回相应驱动程序打包的消息数据。 然后,API把适配器索引(AdapterIndex)该消息放在一起,调用所谓的 “通道库” 。 通道传递消息并接收(同步内联)驱动程序的响应,这是一个”waiting“调用,并将阻塞调用的线程。然后,API通过调用适当的解包库的解包例程,提取数据并通过服务类API方法中的输出参数将数据返回到应用程序。API返回值为零,表示成功。

每个操作系统都有不同的方案用于识别个人GPU、获取和管理可访问GPU集合。在某些操作系统上,由于用户权限,某些 gpu 无法被特定的用户访问。在ADL设计背后的主要原则之一是对每个API分别进行编程,以便它仍然酌情为所有的操作系统提供最小条件的编码服务。

尽管ADL用户不需要了解所有内部实现的细节,但这两个内部库具有特别重要的意义:打包/解包库和通道库。

3.5 打包解包库

在ADL库之前,应用程序需要编写自己的打包解包库(创建C语言类型的结构体,使用局部变量来填充他们,然后验证这些值是否正确),或者直接连接到驱动程序,亦或使用私有的入口点(比如PDL)来执行这种接入。

在大多数情况下,应用程序数据保存在整个程序中的各种位置/变量中(如嵌入到UI窗体或某些数据存储区中),并在I/O时间内合并,这通常出现在一些内部的每个调用都需要退出的应用程序方法中。ADL定义了单一的代码实现,而不是让每个应用程序都自己实现。

此外,ADL还处理操作系统无关的接入请求(PDL无法做到,因为他只能运行在Windows上)。这给了调用ADL的应用程序(包括测试应用程序)提供了更多的机会,以减少操作系统相关的编码。诚然,调用ADL的应用程序仍然需要一些操作系统相关的代码来处理特定操作系统的API,但大多数调用ADL的应用程序都可能成为操作系统无关的,从而减少开发和测试的负担,这是ADL背后的主要原则之一。

3.6 通道库

通道库是一个ADL内部库。他提供了操作系统相关的特定实现,以便将打包的消息从ADL转发到驱动,并返回一个解包的消息。

目前,有三条规定的通道:
1. Windows XP通道——使用GDI来向驱动发送ESC调用
2. Windows Visata通道——使用DirectX向驱动发送ESC调用
3. Linux通道——使用AMD专有的X-Server扩展来与DDX驱动进行通信

这里通道的概念可以扩展到未来的操作系统,比如Windows7上,如果有必要的话。例如,我们考虑存在一些在将来的Windows操作系统上可能会删除一些接入到图形驱动程序的传统ESC调用,可能将他移动到IOCTL或者类似的功能上。ADL通道库旨在以无缝透明的方式允许这样的新通道。

在Linux上,常用的打包消息格式遵循Vista格式。某些Linux特有的函数是单独定义的,用来适应现有的S-extension的实现。大多数函数通常遵循CWDDE消息机制。

4 应用示例

与ADL SDK一起提供的是一个非托管的C代码示例应用程序,它演示了有关如何使用ADL的一些原则。本章不会在细节上讨论这些应用,而是强调一些重点。

4.1 包含所用到的SDK中的头文件

在include文件夹中的三个文件是您的项目需要的唯一文件:
- adl_defines.h 包含所有定义的常量
- adl_structures.h 包含所有定义的结构体
- adl_sdk.h 包含内存分配回调函数的定义

您的项目只包含adl_sdk.h 就够了,因为它包含了 adl_structures.hadl_structures.h 又包含了 adl_defines.h 。这三个文件应该在同一个文件夹中。

4.2 加载AMD Display库

下面几行从代码示例中截取的代码是自解释的。每个操作系统都使用相应的函数来加载库(Windows操作系统的atiadlxx.dll和Linux的libatiadlxx.so)

#if defined (LINUX)
    hDLL = dlopen( "libatiadlxx.so", RTLD_LAZY|RTLD_GLOBAL);
#else
    hDLL = LoadLibrary("atiadlxx.dll");
    if (hDLL == NULL)
        // A 32 bit calling application on 64 bit OS will fail to LoadLIbrary.
        // Try to load the 32 bit library (atiadlxy.dll) instead
        hDLL = LoadLibrary("atiadlxy.dll");
#endif

在Linux中, 32 位和64位库的名称相同, 但放置在不同的位置。
在Windows中, 和操作系统相对应的本机库始终被命名为atiadlxx.dll。 因此, 在32位操作系统上的32位应用程序将成功加载atiadlxx.dll,并且在64位 操作系统上64 位应用程序将成功加载atiadlxx.dll。但是,32 位应用程序在64位操作系统上加载本机64位 atiadlxx.dll的尝试将会失败。因此, 应用程序应立即尝试加载32位版本的adl(atiadlxy.dll)。请参见上文中的代码和第二章中的二进制库的位置。

4.3 在使用ADL的API之前

以下提供的示例展示了调用ADL API所需的步骤:

  1. 对于每一个你计划使用的API,用typedef定义一个函数类型的指针,这些typedef可以组织为一个单独的包含文件,以下是示例中的一些typedef:
typedef int ( *ADL_MAIN_CONTROL_CREATE )(ADL_MAIN_MALLOC_CALLBACK, int );
typedef int ( *ADL_MAIN_CONTROL_DESTROY )();
typedef int ( *ADL_ADAPTER_NUMBEROFADAPTERS_GET ) ( int* );
typedef int ( *ADL_ADAPTER_ADAPTERINFO_GET ) ( LPAdapterInfo, int );
  1. 在代码的某个位置,实例化这些类型的指针。下面是示例中的代码:
ADL_MAIN_CONTROL_CREATE          ADL_Main_Control_Create;
ADL_MAIN_CONTROL_DESTROY         ADL_Main_Control_Destroy;
ADL_ADAPTER_NUMBEROFADAPTERS_GET ADL_Adapter_NumberOfAdapters_Get;
ADL_ADAPTER_ADAPTERINFO_GET      ADL_Adapter_AdapterInfo_Get;

4.4 初始化ADL并提供内存分配函数

ADL的主初始化函数,也是唯一初始化函数是:

int ADL_Main_Control_Create ( ADL_MAIN_MALLOC_CALLBACK callback, int iEnumConnectedAdapters );

在调用任何其他ADL API之前, 应该先调用此函数(确切的步骤在下一小节中)。它初始化库并提供内存分配函数作为第一个参数。注意 ADL_Main_Control_Create 在解锁模式下实例化ADL。使用 ADL_Main_ControlX2_Create 可以显式指定ADL的线程模式。

ADL的内存分配函数是干什么的呢?某些API,比如ADL_Display_DisplayInfo_Get() ,将一个未初始化的指针作为输入参数。然后他调用(已经在Create函数中创建的)内存分配函数,它从用户的堆中分配内存。用户也负责内存的释放。下面的示例说明了这项技术:

// Memory allocation function
void* __stdcall ADL_Main_Memory_Alloc ( int iSize )
{
    void* lpBuffer = malloc ( iSize );
    return lpBuffer;
}

// Optional Memory de-allocation function
void __stdcall ADL_Main_Memory_Free ( void** lpBuffer )
{
    if ( NULL != *lpBuffer )
    {
        free ( *lpBuffer );
        *lpBuffer = NULL;
    }
}

ADL_Main_Control_Create 的第二个参数是 iEnumConnectedAdapters 。如果这个值是零,那么将仅仅采集系统中实际存在并启用的适配器信息。如果该值非零,那么将手机系统中所有(甚至当前已经不存在)的适配器信息。

4.5 调用ADL API

请求的API的函数地址可以通过使用GetProcAddress和ADL库加载后的句柄得到。ADL_Main_Control_Create() 必须是第一个被调用的API,这里是获得它的地址并调用该API的方法:

ADL_Main_Control_Create = ADL_MAIN_CONTROL_CREATE)GetProcAddress(hDLL,"ADL_Main_Control_Create");
            if ( NULL != ADL_Main_Control_Create)
                ADL_Err = ADL_Main_Control_Create (ADL_Main_Memory_Alloc, 1);

返回值ADL_Err 是一个整数,应该返回ADL_OK表示ADL初始化成功。
调用其它API应遵循相同的模式:

  • 获取函数地址并检查是否为NULL
  • 使用适当的参数调用函数
  • 检查返回代码是否有错误

在提供的示例中那个,一开始就获得了所有的函数指针并检查是否为NULL。哪怕只有一个API不可用,应用程序就会执行失败。这完全由用户决定是一次获取所有的函数指针还是在需要时逐个获取。

下面是一个说明如何获取每个适配器相关信息的示例。
在本例中,AdapterInfo结构的内存分配通过malloc完成。大多数ADL API都以这种方式分配内存,而一些API(请参见最后一个示例)使用的内存分配函数是传入ADL_Main_Control_Create 函数的第一个参数。文档指出了这些API。
现在让我们获取系统中适配器的数量。

if ( ADL_OK != ADL_Adapter_NumberOfAdapters_Get ( &iNumberAdapters ) )
{
    printf("Cannot get the number of adapters!\n");
    return 0;
}

然后我们检查这是否是一个非零值,并分配AdapterInfo结构体大小*适配器数量的内存。我们将这个内存的指针传递给 ADL_AdapterInfoGet() 。在函数返回时,结构体中将填充与系统中图形适配器相对应的数据。以下是代码片段:

if ( 0 < iNumberAdapters )
{
    lpAdapterInfo = malloc ( sizeof (AdapterInfo) * iNumberAdapters );
    memset ( lpAdapterInfo,'\0', sizeof (AdapterInfo) * iNumberAdapters );

    // Get the AdapterInfo structure for all adapters in the system
    ADL_Adapter_AdapterInfo_Get (lpAdapterInfo, sizeof (AdapterInfo) * iNumberAdapters);
}

下面w

5 ADL2 API

5.1 调用ADL2 API

5.2 ADL和ADL2 API在同一个应用程序中混合使用

阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页