用于图形处理器单元上的图形和非图形计算的语言、函数库和编译器的制作方法

文档序号:11142222阅读:621来源:国知局
用于图形处理器单元上的图形和非图形计算的语言、函数库和编译器的制造方法与工艺

本专利文档的公开内容的一部分包含受到(版权或掩模作品权)保护的材料。由于本专利文档或本专利公开内容出现在专利与商标局专利文件或记录中,因此(版权或掩模作品权)所有人不反对任何人对本专利文档或本专利公开内容的拓制,但会以其他方式保留任何形式的所有(版权或掩模作品权)权利。



背景技术:

本公开整体涉及计算机编程领域。更具体地讲,但并非作为限制,本公开涉及一种用于对在图形处理器单元上执行的内核进行编程的编程语言的编程语言和编译系统。

图形处理器单元(GPU)已经变得对于处理数据并行图形任务而言越来越重要。开发者们也已经认识到,非图形数据并行任务也可以由GPU利用其海量并行能力来处理。供应商和标准组织已经创建了应用编程接口(API),使得图形数据并行任务更容易编程。类似地,供应商和标准组织已经创建了不同的API,使得计算或非图形数据并行任务更容易编程。然而,这些高级API已经造成性能下降,还使得组合不同的图形和计算数据并行任务不那么方便,因为需要为每种类型的任务使用不同的API或者在与编程语言(例如开发者非常常用于在CPU上编写代码的C++)相当不同的语言中编写代码。



技术实现要素:

一种编译器和库提供了根据定义的语言模型将编程语言编译成独立于编程语言、独立于机器的中间表示的能力,用于转换成目标可编程设备上的可执行文件。该语言模型允许编写执行数据并行图形和非图形任务的程序。

附图说明

图1是示出了根据一个实施方案的程序的编译、链接和执行的框图。

图2是示出了根据一个实施方案用于在图形处理器单元上执行程序的计算机系统的框图。

图3是示出了根据一个实施方案用于编译和链接程序的计算机系统的框图。

图4是示出了根据一个实施方案的联网系统的框图。

图5-图36是示出了根据一个实施方案用于编程语言的语言模型的特征的表格。

具体实施方式

在以下描述中,为了解释的目的,阐述了很多具体细节以便提供对发明的彻底理解。然而,对本领域的技术人员而言显而易见的是,可以在不存在这些具体细节的情况下实践本发明。在其他实例中,结构和设备被以框图的形式示出,以便避免不必要地模糊本发明。没有下标或后缀的数字引用被理解为引用与所引用数字对应的下标和后缀的所有实例。此外,本公开中所使用的语言已主要被选择用于可读性和指导性目的,并且可能没有被选择为划定或限定本发明的主题,从而诉诸于所必需的权利要求以确定此类发明主题。在说明书中提到“一个实施方案”或“实施方案”意指在本发明的至少一个实施方案中包括的结合该实施方案描述的特定特征、结构或特性,并且多次提到“一个实施方案”或“实施方案”不应被理解为全部必然地参考相同的实施方案。

如本文所用,术语“计算机系统”可以指单个计算机或多个计算机,它们一起工作以执行被描述为在计算机系统上或由计算机系统执行的功能。类似地,机器可读介质可以指可以一起包含其上存储的所指示信息的单个物理介质或多个介质。处理器可以指在单个芯片或在多个处理芯片上实现的单个处理元件或多个处理元件。

图形处理器单元(GPU)是一种专用电子电路,被设计成快速操控和改变存储器,以加快在帧缓冲区中旨在用于输出到显示器的图形的创建。GPU在操控计算机图像方面高效,具有高度并行的结构,使其比通用计算机处理器(CPU)更有效,其中大数据块的处理是并行完成的。除了图形处理之外,GPU还用于非图形并行处理,有时称为“计算处理”。

下文更详细描述的实施方案允许软件开发者利用编程语言准备应用,该编程语言符合被设计成有助于开发者编写有效的多线程程序的语言模型,多线程程序能够在GPU上既执行图形处理又执行计算(非图形)处理。用于编程语言的编译器生成与独立于机器的编程语言无关的中间表示。开发者不需要担心所用的特定GPU的架构,允许改变最终执行程序所在的硬件而不需要开发者重写程序。然后可以分布中间表示而无需用于程序的源代码。为了允许程序在目标机器上执行,可以在目标机器上使用用于中间表示的嵌入式编译器对中间表示进行编译,产生可以被存储用于重复执行的最终可执行文件。或者,可以使用中间系统以将中间表示编译成机器特定的可执行文件。

下文更详细描述的编程语言使用对于计算和图形功能而言是统一的且被设计成用于提前编译的语言模型。开发者可以使用该语言编写在GPU上执行以进行图形和通用数据并行计算的代码。在一个实施方案中,该语言模型和编程语言基于C++编程语言,其添加了用于并行任务GPU编程的特征。在一个实施方案中,该编程语言基于具有特定扩展和限制的C++11规范(也称为ISO/IEC JTC1/SC22/WG21N3290语言规范)。其他实施方案可以基于其他编程语言和语言模型,例如Objective C。

传统的编译器将编译过程分解成两个步骤:编译和链接。编译器将源代码编译成包含机器代码的目标文件,链接程序将目标文件和库组合以形成可执行程序。在简单系统中,链接程序通常比级联目标文件和解析符号参考做的稍多。

然而,近年来,编译过程被分成更多阶段。编译器现在对源代码进行语法分析,并生成目标代码文件,但并非包含特定于目标机器的机器代码,目标代码的形式是中间表示,这是虚拟指令集的一种形式。通常,中间表示与编程语言无关且与机器无关,被设计成用于假想或虚拟机器。优化链接程序然后组合目标文件,优化它们,并将它们集成为针对目标机器的本机可执行文件。优化链接程序可以自身被分成独立的阶段,从而对中间表示进行优化,可以在与优化和链接不同的计算机系统上执行针对目标机器的最终代码生成。链接程序可以由多个编译器使用,允许将单个链接程序用于多种编程语言。

通过让链接程序发出优化形式的中间表示,可以将本机可执行文件的形成推送到目标系统。目标系统上的嵌入式编译器可以将中间表示编译成用于在目标系统上直接执行的本机代码。这种编译器结构允许开发者在独立于机器的编程语言中编写源代码,将源代码编译成优化的中间表示代码,然后将中间表示代码分布到目标或最终用户系统,以转换成可执行形式。因此,不需要分布源代码,从而允许保护源代码免受最终用户系统的影响。此外,如果需要,目标机器并不提前编译中间表示,然后稍晚执行目标特定的可执行文件,即时编译器或解释程序可以用于允许在执行时对中间表示进行工作中编译或解释。

现在转向图1,根据一个实施方案,以框图的形式示出了开发者计算机系统100和目标计算机系统150。开发者将下述编程语言中的源代码提供到编译器115,编译器对源代码进行解析,并生成与机器无关、与编程语言无关的中间表示。然后向链接程序/归档程序120提供中间表示,在此可以将其与开发者编写的程序使用的库125的函数组合。链接程序120可以将中间表示优化成优化的中间表示。结果是可分布应用130。

可以将应用130以任何期望的方式传递到目标机器150,包括通过网络的电子传输和机器可读介质的物理传输。这通常涉及向服务器(图1中未示出)传递应用130,目标系统150可以从服务器获得应用130。应用130可以与其他数据捆绑在一起,例如运行时库、安装程序等,它们可用于在目标系统150上安装应用130。在一些情况下,应用130可以被提供作为更大软件包的一部分。

在将应用130作为包含状态信息142、片段着色器144和顶点着色器146的管道对象140的集合来安装时,由嵌入式GPU编译器170编译该应用,该GPU编译器利用高速缓存160将中间表示编译成用于GPU 180的本机二进制代码。编译的本机代码可以被高速缓存在高速缓存160中或存储于目标系统150的别处。最后,GPU 180可以执行本机二进制代码,以执行用于数据并行操作的图形和计算内核。

现在参考图2,框图示出了根据一个实施方案,能够用作开发者系统100的计算机系统200。虽然图2示出了计算机系统的各种部件,但是其并不旨在表示使这些部件互连的任何特定架构或方式,因为此类细节与本公开并无密切关系。也可以使用具有更少部件或可能更多部件的网络计算机和其他数据处理系统(例如,手持式计算机、个人数字助理(PDA)、蜂窝电话、娱乐系统、消费电子设备等)实施一个或多个实施方案。

如图2中所示,作为一种形式的数据处理系统的计算机系统200包括耦接到微处理器216(可以是CPU和/或GPU)的总线222,存储器212(可以包括易失性读/写随机存取存储器(RAM)和只读存储器(ROM)之一或两者)和非易失性存储设备214。微处理器216可从存储器212和存储设备214检索指令并使用高速缓存218执行该指令以执行上述操作。链路222与这些各种部件互连在一起,并且还将这些部件216、218、212和214互连至显示控制器206和显示设备220,以及互连至外围设备诸如输入/输出(I/O)设备204,该输入/输出(I/O)设备可以是鼠标、键盘、调制解调器、网络接口、打印机和本领域熟知的其他设备。通常,输入/输出设备204通过输入/输出控制器202耦接到系统。其中易失性RAM包括在存储器212中,RAM通常被实现为需要连续供电以刷新或保持存储器中的数据的动态RAM(DRAM)。显示器控制器206和显示设备220可以任选地包括一个或多个GPU以处理显示数据。任选地,可以提供GPU存储器208以支持显示器控制器206或显示设备220中包括的GPU。

存储设备214通常为磁性硬盘驱动器、光驱、非易失性固态存储器设备,或者即使在系统断电后也保持数据(例如,大量数据)的其他类型的存储器系统。虽然图2示出存储装置214为直接耦接到数据处理系统中的其余部件的本地设备,但实施方案可利用远离系统的非易失性存储器,诸如通过可能为有线或无线的网络接口210耦接到数据处理系统的网络存储设备。链路222可包括通过本领域熟知的各种桥接器、控制器和/或适配器相互连接的一个或多个链路。尽管图2中为了清晰起见仅示出了每种类型单个元件,但可以根据需要使用任意或所有各种元件类型的多个元件。

现在参考图3,框图示出了根据一个实施方案,能够用作目标计算机系统150的计算系统300。虽然图3示出了计算机系统的各种部件,但是其并不旨在表示使这些部件互连的任何特定架构或方式,因为此类细节与本公开并无密切关系。也可以使用具有更少部件或可能更多部件的网络计算机和其他数据处理系统(例如,手持式计算机、个人数字助理(PDA)、蜂窝电话、娱乐系统、消费电子设备等)实施一个或多个实施方案。

计算系统300包括CPU 310、GPU 330。在图3所示的实施方案中,CPU 310和GPU 330包括在独立的集成电路(IC)或封装上。然而,在其他实施方案中,CPU 310和GPU 330或它们的功能性集合可以被包括在单个IC或封装中。

此外,计算系统300还包括可以由CPU 310和GPU 330访问的系统存储器340。在各种实施方案中,计算系统300可以包括超级计算机、台式计算机、膝上型电脑、视频游戏控制台、嵌入式设备、手持式设备(例如,移动电话、智能电话、MP3播放器、相机、GPS设备或其他移动设备)或包括或被配置为包括GPU的任何其他设备。尽管图3中未示出,计算系统300还可以包括计算系统的常规元件,包括用于显示计算系统300的内容(例如,图形、视频等)的显示设备(例如,阴极射线管、液晶显示器、等离子体显示器等),以及输入设备(例如,键盘、触控板、鼠标等)、存储设备(例如,硬盘、光盘等)和通信设备(例如,网络接口)。根据需要可以包括任何其他元件。尽管被图示为由公共通信链路350耦接,但可以根据需要为连接到独立但互连的链路350的CPU 310和GPU 330采用多个链路350。

GPU 330通过执行某些特定功能来辅助CPU 310,例如,图形处理任务和数据并行、通用计算任务,通常比CPU 310在软件中能够执行它们更快。

GPU 330通过链路350与CPU 310和系统存储器340耦接。链路350可以是计算机系统中使用的任何类型的总线或通信结构,包括外围部件接口(PCI)总线、加速图形端口(AGP)总线、PCI快速(PCIE)总线或另一种链路,包括非总线链路。如果采用多个链路350,它们可以是不同类型的。

除了系统存储器340之外,计算系统300可以包括耦接到GPU 330以及链路350的本地存储器320。本地存储器320可以由GPU 330用于提供对特定数据(例如,频繁使用的数据)的访问,这种访问比数据存储在系统存储器340中可能的速度更快。本地存储器320还可以由CPU 310用于提供对诸如本地存储器320中存储的二进制代码的数据的访问。在一些实施方案中,可以为CPU 310和GPU 330使用独立的本地存储器,而不是共享公共本地存储器320。

尽管图3中示出了单个CPU 310和GPU 330,但各实施方案可以根据需要采用任意数量的CPU 310和GPU 330。在采用多个CPU 310和GPU 330时,CPU 310和GPU 330的每个都可以是不同类型和架构的。可以根据需要在不同的GPU 330上执行应用130的各部分。此外,计算机系统300可以采用一个或多个专用协处理器设备(图3中未示出),例如密码协处理器,其可以根据需要利用链路350或其他链路耦接到CPU 310和GPU 330中的一者或多者。

现在转向图4,框图示出了互连可编程设备400的网络,包括服务器430和关联的数据存储440,以及台式计算机410、膝上型电脑412、平板电脑414和移动电话416。这些可编程设备的任一种都可以是图1的开发者系统100或目标系统150。互连可编程设备的网络420可以是任何类型的网络,有线的或无线的,局域网或广域网、公共的或私有的,使用任何期望的网络通信协议从一个系统向另一个系统传输数据。尽管被示为单个网络420,但可以使用任意数量的互连网络连接各个可编程设备,其可以采用不同的网络技术。在一个实施例中,台式工作站410可以是图1的开发者系统100,向服务器430分布应用130,服务器继而可以向多个设备412、414和416分布应用130,多个设备的每者都可以采用不同的GPU以及其他不同的部件。

现在转向编程语言和语言模型。下文针对编程语言示出的特定语法仅仅是示例和作为例示,可以根据需要使用不同的语法。编程语言符合语言模型,该语言模型允许开发者使用低级数据结构在GPU上编程图形和计算(非图形)数据并行任务或内核,无需担心最终将执行程序的特定GPU。编程语言和语言模型的以下描述是Apple Inc.2014年的版权。

简介

本文档描述根据一个实施方案用于统一图形和计算语言的语言模型。该语言是基于C++的编程语言,开发者可以使用该语言编写在GPU上执行以进行图形和通用数据并行计算的代码。由于该语言基于C++,因此开发者将发现其熟悉且易用。利用该语言,可以利用单一的、统一的语言编程图形和计算程序,这样允许在两者之间进行更紧密的集成。

该语言被设计成与框架一起工作,框架管理着语言代码的执行并且任选地管理语言代码的编译。在一个实施方案中,该框架和开发环境使用clang和LLVM,因此开发者得到的编译器提供接近在GPU上执行的代码的金属性能(metal performance)。

本说明书的组织

该语言模型的描述被组织成以下章节:

这一章“简介”是对本文档的介绍,涵盖该语言和C++11之间的相似性和差异。

“数据类型”列出了语言数据类型,包括代表矢量、矩阵、缓冲区、纹理和采样器的类型。本文还论述了类型分配和类型转换。

“运算符”列出了语言运算符。

“函数、变量和限定词”详细描述了如何声明函数和变量,有时具有限制如何使用它们的限定词。

“标准库”定义了内置语言函数的集合。

“编译器选项”详细描述了用于语言编译器的选项,包括预处理器指令、用于数学本征函数的选项和控制优化的选项。

“数值兼容性”描述表示浮点数字的要求,包括数学运算中的精确度。

语言和C++11

该编程语言基于具有特定扩展和限制的C++11规范(也称为ISO/IEC JTC1/SC22/WG21N3290语言规范),在此通过引用将其全文并入本文。请参考C++11规范以获得语言语法的详细描述。

这一节及其子节描述了对该语言中支持的C++11语言的修改和限制。

要获得关于该语言预处理指令和编译器选项的更多信息,请参见本文档的编译器选项一节。

重载

该语言支持由C++11规范第13节定义的重载。函数重载规则被扩展以包括实参的地址空间限定词。语言图形和内核函数不能被重载。(对于图形和内核函数的定义,请参见本文档的函数限定词一节。)

模板

该语言支持由C++11规范第14节定义的模板。

预处理指令

该语言支持由C++11规范第16节定义的预处理指令。

限制

在根据一个实施方案的语言中没有以下C++11特征(本列表中的章节数字指C++11规范):λ表达(5.1.2节);dynamic_cast运算符(5.2.7节);类型标识(5.2.8节);新的和删除运算符(5.3.4和5.3.5节);noexcept运算符(5.3.7节);导出类(10节);成员访问控制(11节);特殊成员函数(12节)和异常处理(15节)。

在语言代码中一定不能使用C++标准库。替代C++标准库,该语言具有其自己的标准库,在以下标准库一节中论述了该标准库。

在一个实施方案中,该语言限制指针的使用:必须要利用global、local或constant地址空间限定词来声明程序中作为指针声明的语言图形和内核函数的实参。(参见本文档中地址空间限定词一节获得关于语言地址空间限定词的更多信息。)不支持函数指针。

不能将语言图像和内核函数的实参声明为指向指针的指针。

结构体或类的成员必须要属于同一地址空间。不支持位字段结构体成员。

不支持goto语句。

不能将图形和内核函数的实参声明为size_t、ptrdiff_t类型,或者包含被声明为这些内置标量类型之一的成员的结构体和/或联合。

main是该语言中的保留关键词。函数不能被称为main。

像素和纹理坐标系

在该语言中,在左上角处定义了帧缓冲区附件的像素坐标系的原点。类似地,帧缓冲区附件的纹理坐标系的原点在左上角。

数据类型

这一章详述了语言数据类型,包括表示矢量和矩阵的类型。还论述了原子数据类型、缓冲区、纹理、采样器、数组和用户定义的结构体。还描述了类型对准和类型转换。

标量数据类型

该语言支持表1(图5)中列出的标量类型。除了C++11标准数据类型之外,该语言还支持:

半精确度浮点,half

对double数据类型的支持是任选的。该语言不支持C++11标准long、unsigned long、long、unsigned long和long double数据类型。

该语言支持C++11标准f或F后缀,以指定单一精确度浮点字面值(例如0.5f或0.5F)。此外,该语言支持h或H后缀,以指定半精确度浮点字面值(例如0.5h或0.5H)。该语言还支持针对未赋值整数文字的u或U后缀。

矢量和矩阵数据类型

该语言针对布尔值、整数和浮点值的2、3和4分量矢量定义其自己的内置数据类型。矢量可以用于表示图形构造,例如颜色、顶点、表面法线或纹理坐标,但它们不限于表示那些。支持的矢量类型名称是:

bool n、

char n、short n、int n、uchar n、ushort n、uint n、half n、float n和double n

其中n是表示2、3或4分量矢量类型的2、3或4。

double n为任选的。如果支持double,则支持它。

也可以使用vec<T,n>模板化类型来定义矢量。T是bool、char、short、int、uchar、ushort、uint、half、float或double之一。n是2、3或4。

该语言具有用于具有2、3或4列和行的浮点值矩阵的内置数据类型。支持的矩阵类型为:

half nxm、float nxm和double nxm

其中n和m是列数和行数。n和m可以是2、3或4。double nxm为任选的。如果支持double,则支持它。

也可以使用以下模板类型来声明矩阵:matrix<T,n>,其中n既是列数又是行数,以及matrix<T,n,m>,其中n和m是列数和行数。n和m可以是2、3或4之一。T是half、float或double之一。

访问矢量分量

可以利用数组索引访问矢量分量。数组索引0是指矢量的第一分量,索引1是指第二分量,等等。以下示例示出了访问数组分量的各种方式:

pos=float4(1.0f,2.0f,3.0f,4.0f);

float x=pos[0];//x=1.0

float z=pos[2];//z=3.0

float4vA=float4(1.0f,2.0f,3.0f,4.0f);

float4vB;

for(int i=0;i<4;i++)

vB[i]=vA[i]*2.0f//vB=(2.0,4.0,6.0,8.0);

该语言支持使用句点(.)作为选择运算符以访问矢量分量,使用可以指示坐标或色彩数据的字母:

<vector_data_type>.xyzw或

<vector_data_type>.rgba。

在以下代码中,对矢量测试进行初始化,然后利用.xyzw或.rgba选择语法访问分量:

int4test=int4(0,1,2,3);

int a=test.x;//a=0

int b=test.y;//b=1

int c=test.z;//c=2

int d=test.w;//d=3

int e=test.r;//e=0

int f=test.g;//f=1

int g=test.b;//g=2

int h=test.a;//h=3

分量选择语法允许选择多个分量。

float4c;

c.xyzw=float4(1.0f,2.0f,3.0f,4.0f);

c.z=1.0f;c.xy=float2(3.0f,4.0f);

c.xyz=float3(3.0f,4.0f,5.0f);

分量选择语法还允许排列或复制分量。

float4pos=float4(1.0f,2.0f,3.0f,4.0f);

float4swiz=pos.wzyx;//swiz=(4.0f,3.0f,2.0f,1.0f)

float4dup=pos.xxyy;//dup=(1.0f,1.0f,2.0f,2.0f)

该分量组表示可以发生于表达式的左侧。为了形成左值,可以应用调配。根据指定分量的数量,所得的左值可以是标量或矢量类型。每个分量必须是受到支持的标量或矢量类型。所得的矢量类型的左值必须不能包含重复分量。

float4pos=float4(1.0f,2.0f,3.0f,4.0f);

//pos=(5.0,2.0,3.0,6.0)

pos.xw=float2(5.0f,6.0f);

//pos=(8.0,2.0,3.0,7.0)

pos.wx=float2(7.0f,8.0f);

//pos=(3.0,5.0,9.0,4.0)

pos.xyz=float3(3.0f,5.0f,9.0f);

在一个实施方案中以下矢量分量访问的方法不被许可,并导致编译时错误:

访问针对矢量类型声明的那些之外的分量是一种错误。2分量矢量数据类型仅可以访问.xy或.rg元素。3分量矢量数据类型仅可以访问.xyz或.rgb元素。例如:

float2pos;

pos.x=1.0f;//is legal;so is y

pos.z=1.0f;//is illegal;so is w

float3pos;pos.z=1.0f;//is legal

pos.w=1.0f;//is illegal

在左侧访问相同分量两次是不明确的;例如,

//illegal-'x'used twice

pos.xx=float2(3.0f,4.0f);

//illegal-mismatch between float2and float4

pos.xy=float4(1.0f,2.0f,3.0f,4.0f);

.rgba和.xyzw限定词不能混和在单个访问中;例如,

float4pos=float4(1.0f,2.0f,3.0f,4.0f);

pos.x=1.0f;//OK

pos.g=2.0f;//OK

pos.xg=float2(3.0f,4.0f);//非法-使用了混合限定词

float3coord=pos.ryz;//非法-使用了混合限定词

指针或对具有调配的矢量的引用;例如

float4pos=float4(1.0f,2.0f,3.0f,4.0f);

my_func(&pos.xy);//非法

矢量类型上的sizeof运算符返回矢量的大小,这是由分量的数量*每个分量的大小给定的。例如,sizeof(float4)返回16,sizeof(half3)返回6。

访问矩阵分量

可以访问floatnxm、halfnxm和doublenxm矩阵作为n floatm、n halfm或n doublem条目的数组。

可以使用数组下标语法访问矩阵的分量。向矩阵应用单个下标将矩阵作为列矢量的数组来处理。顶列为列0。第二下标然后会操作于所得矢量,如先前针对矢量所定义的那样。因此,两个下标选择列,然后选择行。

float4x4m;

//sets the 2nd column to all 2.0

m[1]=float4(2.0f);

//sets the 1st element of the 1st column to 1.0

m[0][0]=1.0f;

//sets the 4th element of the 3rd column to 3.0

m[2][3]=3.0f;

访问具有非常数表达式的矩阵边界之外的分量导致未定义行为。访问具有常数表达式的矩阵边界之外的矩阵分量生成编译时错误。

矢量构造函数

构造函数可以用于从一组标量或矢量形成矢量。在对矢量进行初始化时,其参数签名决定了如何构造它。例如,如果仅利用单个标量参数对矢量进行初始化,那么将构造矢量的所有分量设置成该标量值。

如果矢量是从多个标量、一个或多个矢量或这些的混合构造的,则按照从实参的分量开始的次序构造矢量的分量。实参是从左到右消耗的。每个实参都在消耗来自下一实参的任何分量之前按次序消耗其所有分量。

这是float4可用的构造函数的完整列表:

float4(float x);

float4(float x,float y,float z,float w);

float4(float2a,float2b);

float4(float2a,float b,float c);

float4(float a,float b,float2c);

float4(float a,float2b,float c);

float4(float3a,float b);

float4(float a,float3b);

float4(float4x);

这是float3可用的构造函数的完整列表:

float3(float x);

float3(float x,float y,float z);

float3(float a,float2b);

float3(float2a,float b);

float3(float3x);

这是float2可用的构造函数的完整列表:

float2(float x);

float2(float x,float y);

float2(float2x);

以下示例示出了构造函数的使用:

float x=1.0f,y=2.0f,z=3.0f,w=4.0f;

float4a=float4(0.0f);

float4b=float4(x,y,z,w);

float2c=float2(5.0f,6.0f);

float2a=float2(j,k);

float2b=float2(l,m);

float4x=float4(a,b);

对矢量构造函数初始化不足是编译时错误。

矩阵构造函数

构造函数可以用于从一组标量、矢量或矩阵形成矩阵。在对矩阵进行初始化时,其参数签名确定了如何构造它。例如,如果仅利用单个标量参数对矩阵进行初始化,结果是包含针对矩阵对角的所有分量的标量的矩阵,其余分量被初始化为0.0。例如,调用

float4x4(fval);

利用这些初始内容构造矩阵:

还可以从另一个相同大小(即,具有相同数量的行和列)的矩阵构造矩阵。例如,

float3x4(float3x4);

按照列优先次序构造和消耗矩阵分量。矩阵构造函数必须要具有在其实参中指定的恰好足够多值,以对所构造矩阵对象中的每个分量进行初始化。提供比所需更多的实参导致错误。对矩阵构造函数初始化不足也导致编译时错误。

具有n列和m行的类型T的矩阵也可以从n vec<T,m>矢量构造。以下示例是合法的构造函数:

float2x2(float2,float2);

float3x3(float3,float3,float3);

float3x2(float2,float2,float2);

以下是不支持的矩阵构造函数的示例。矩阵不能从多个标量值构造,也不能从矢量和标量的组合构造。

//both are bad

float2x2(float a00,float a01,float a10,float a11);float2x3(float2a,float b,float2c,float d);

原子数据类型

语言原子数据类型被限制成供编程语言实施的原子函数使用,如原子函数一节中所述。这些原子函数是C++11原子和同步函数的子集。语言原子函数必须要对语言原子数据操作,且必须无锁。

原子类型被定义为:

atomic_int和atomic_uint

缓冲区

该语言将缓冲区实现为指向全局或常量地址空间中描述的内置或用户定义的数据类型的指针。(参考地址空间限定词一节,获得对这些地址限定词的完整描述。)这些缓冲区可以在程序范围中声明为函数的实参或作为函数的实参被传递。

示例

纹理

纹理数据类型是对应于单一位图等级的纹理所有或部分的一维、二维或三维纹理数据的句柄。以下模板定义了具体的纹理数据类型:

T指定在从纹理读取时返回的色彩类型,或者在向纹理写入时指定的色彩类型。对于纹理类型(除了depth纹理类型之外),T可以是half、float、int或uint。对于depth纹理类型,T必须是float。

注意:如果T为int,与纹理相关联的数据必须使用有符号整数格式。如果T为uint,与纹理相关联的数据必须使用无符号整数格式。如果T为half,与纹理相关联的数据必须是归一化的(有符号或无符号整数)或半精确度格式。如果T为float,与纹理相关联的数据必须是归一化的(有符号或无符号整数)、半或单一精确度格式。

access限定词描述如何能够访问纹理。支持的访问限定词为:sample–可以对纹理对象采样。样本意味着利用或不利用采样器从纹理读取的能力;read–没有采样器,图形或内核函数仅能够读取纹理对象。(对于多次采样的纹理,仅支持读取限定词。);或者write-图形或内核函数能够向纹理对象写入。

depth_format限定词描述深度纹理格式。仅支持的值为depth_float。以下示例使用具有纹理对象实参的这些访问限定词。

(参见定位资源的属性限定词一节,获得对texture_index属性限定词的描述。)

采样器

sampler类型标识如何对纹理采样,包括以下采样器状态:

·寻址模式,包括针对纹理坐标s、t和r的独立寻址模式。

·过滤器模式,包括对mag过滤器、min过滤器和mip过滤器的支持。

·表示坐标是否归一化的布尔值

·最大各向异性值-0和16之间的整数值

·最小和最大细节等级(lod)和mip lod偏置,全都是浮点值

·比较函数(仅在用于深度纹理时适用)

在一个实施方案中,API允许开发者创建sampler对象,并将这些作为实参传递到图形和内核函数。sampler对象也可以在程序源而非API中描述。对于这些情况,仅允许指定采样器状态的子集:寻址模式、过滤器模式和归一化坐标。

表2(图6)描述被支持采样器状态枚举的列表及其关联值(和默认值)。可以在语言程序源中对采样器进行初始化时指定这些状态。

表3(图7)描述lod采样器状态,可以利用采样器及其关联值(和默认值)指定它们。不能在语言程序源中对采样器进行初始化时指定这些值。为了指定用于lod采样器状态的值(而不是默认值),必须要利用API创建采样器并作为实参传递到图形或内核函数。

表2(图6)中描述的采样器数据类型使用的枚举类型如下指定:

enum class coord{normalized,pixel};

enum class filter{nearest,linear};

enum class min_filter{nearest,linear};

enum class mag_filter{nearest,linear};

enum class s_address{clamp_to_zero,clamp_to_edge,

repeat,mirrored_repeat};

enum class t_address{clamp_to_zero,clamp_to_edge,

repeat,mirrored_repeat};

enum class r_address{clamp_to_zero,clamp_to_edge,

repeat,mirrored_repeat};

enum class address{clamp_to_zero,clamp_to_edge,

repeat,mirrored_repeat};

enum class mip_filter{none,nearest,linear};

//can only be used with depth_sampler

enum class compare_func{none,less,less_equal,greater,

greater_equal,equal,not_equal,

always,never};

该语言如下实现采样器对象:

Ts必须是上文列出的枚举类型,其可以由采样器数据类型使用。如果在给定采样器构造函数中声明同一枚举类型多次,最后列出的值将生效。

以下语言程序源例示了声明采样器的几种方式。(以下代码中出现的属性限定词(sampler_index(n)、buffer_index(n)和texture_index(n))在属性限定词一节中解释。)需注意,程序源中声明的采样器或常数缓冲区不需要这些属性限定词。

注:必须要利用constexpr限定词声明程序源中初始化的采样器。

数组和结构体

完全支持数组和结构体,包括矢量、矩阵、纹理和采样器的数组。texture和sampler类型不能在结构体中声明。

可以将采样器的数组作为函数的实参传递或在程序范围中声明。采样器数组必须要是设定大小的数组。在编译时必须要知道采样器索引值;否则,行为则未定义。在编译时不知道采样器索引值时,编译器可以抛出编译错误。

采样器代码的一些示例如下:

纹理类型的数组仅可以作为函数的实参被传递。纹理数组必须是设定大小的数组。在编译时必须要知道纹理索引值;否则,行为未定义。在编译时不知道纹理索引值时,语言编译器可以抛出编译错误。

类型的对准

表4(图8)列出了标量和矢量数据类型的对准。

可以使用alignas对准指定符指定类型或对象的对准要求。alignas指定符可以应用于声明结构体或类的变量或数据成员。还可以将其应用于声明结构体、类或枚举类型。

语言编译器负责将数据项对准到数据类型要求的适当对准。对于声明为数据类型的指针的图形或内核函数的实参,该语言编译器可以假设,该指针始终如数据类型所需那样适当对准。未对准负载或存储的行为未定义。

封装矢量数据类型

矢量和矩阵数据类型一节中描述的矢量数据类型对准到矢量的大小。有若干使用情况中开发者需要紧密封装其矢量数据。例如,可能包含位置、法线、切向矢量和纹理坐标的顶点结构体被紧密封装并作为缓冲区传递到顶点或顶点获取函数。

支持的封装矢量类型名称是:

packed_charn,packed_shortn,

packed_intn,packed_ucharn,packed_ushortn,packed_uintn,

packed_halfn,packed_floatn and packed_doublen

n是表示2、3或4分量矢量类型的2、3或4。表5(图9)列出了封装矢量数据类型的对准。

封装矢量数据类型仅可以被用作数据存储格式。从封装的矢量数据类型到对准矢量数据类型的负载和存储以及反之亦然,支持拷贝构造函数和分配运算符。对于封装的矢量数据类型,不支持算术、逻辑和关系运算符。

示例:

global float4*buffer;

global packed_float4*packed_buffer;

int i;

packed_float4f(buffer[i]);

pack_buffer[i]=buffer[i];

//operator to convert from packed_float4to float4.

buffer[i]=float4(packed_buffer[i]);

隐式类型转换

支持标量内置类型(空除外)之间的隐式转换。在进行隐式转换时,不仅仅是重新解释表达式的值,而是将该值转换成新类型中的等价值。例如,将整数值5转换成浮点值5.0。

所有矢量类型都被视为具有比标量类型更高的转换等级。不许可从矢量类型到另一矢量或标量类型的隐式转换,会导致编译错误。例如,从4分量整数矢量转换到4分量浮点矢量的以下尝试会失败。

int4i;float4f=i;//compile error.

支持从标量到矢量类型和从标量到矩阵类型的隐式转换。在矢量的每个元素中都复制标量值。在矩阵对角的所有分量中复制标量值,其中剩余的分量被初始化为0。也可以对标量进行通常的算术转换,转换成矢量或矩阵使用的元素类型。

例如:

float4f=2.0f;//f=(2.0f,2.0f,2.0f,2.0f)

不许可从矩阵类型到另一矩阵、矢量或标量类型的隐式转换,会导致编译错误。

指针类型的隐式转换遵循C++11规范中描述的规则。

类型转换和重新解释数据

static_cast运算符用于从标量或矢量类型转换为不具有饱和度的默认舍入模式的另一标量或矢量类型(即,在转换到浮点时,舍入到最近的偶数;在转换到整数时,向零舍入)。

该语言增加了as_type<type-id>运算符,以允许将任何标量或矢量数据类型(并非指针)重新解释为相同大小的另一标量或矢量数据类型。操作数中的位在不被修改为新类型的情况下被直接返回。不执行函数实参的通常类型升级。

例如,as_type<float>(0x3f800000)返回1.0f,如果视为IEEE-754单精确度值,这是位模式0x3f800000的值。使用as_type<type-id>运算符将数据重新解释成不同字节数的类型是错误的。

示例:

float f=1.0f;

//Legal.Contains:0x3f800000

uint u=as_type<uint>(f);

//Legal.Contains:

//(int4)(0x3f800000,0x40000000,

//0x40400000,0x40800000)

float4f=float4(1.0f,2.0f,3.0f,4.0f);

int4i=as_type<int4>(f);int i;

//Legal.

short2j=as_type<short2>(i);

float4f;

//Error.Result and operand have different sizes

double4g=as_type<double4>(f);

float4f;

//Legal.g.xyz will have same values as f.xyz.

//g.w is undefined

float3g=as_type<float3>(f);

运算符

这一章列出和描述了语言运算符。

标量和矢量运算符

算术运算符加(+)、减(-)、乘(*)和除(/)对标量和矢量、整数和浮点数据类型进行操作。所有的算术运算符在操作数类型转换之后都返回与操作数类型相同的内置类型(整数或浮点)的结果。在转换之后,以下情况是有效的:

两个操作数是标量。在这种情况下,应用操作,结果是标量。

一个操作数是标量,另一个是矢量。在这种情况下,可以对标量进行通常的算术转换,转换成矢量操作数使用的元素类型。然后将标量类型拓宽到与矢量操作数具有相同数量的分量的矢量。逐个分量执行运算,结果是相同大小的矢量。

两个操作数是相同大小的矢量。在这种情况下,逐个分量执行运算,结果是相同大小的矢量。

得到位于整数类型的最大可表示值和最小可表示值界定的范围之外的值的整数类型上的除法不会导致异常,但导致未指定的值。整数类型除以零不会导致异常,但导致未指定的值。浮点类型除以零导致±无穷大或NaN,如IEEE-754标准所规定的那样。(关于浮点运算的数值精确度的详情,请参见数值兼容性一章。)

运算符模量(%)对标量和矢量整数数据类型进行操作。所有的算术运算符在操作数类型转换之后都返回与操作数类型相同的内置类型(整数或浮点)的结果。以下情况是有效的:

两个操作数是标量。在这种情况下,应用操作,结果是标量。

一个操作数是标量,另一个是矢量。在这种情况下,可以对标量进行通常的算术转换,转换成矢量操作数使用的元素类型。然后将标量类型拓宽到与矢量操作数具有相同数量的分量的矢量。逐个分量执行运算,结果是相同大小的矢量。

两个操作数是相同大小的矢量。在这种情况下,逐个分量执行运算,结果是相同大小的矢量。

所得的值对于利用第二操作数为零进行计算的任意分量都是未定义的,而对于具有非零操作数的其他分量的结果保持为定义的。

如果两个操作数都是非负,余数为非负。如果操作数之一或两者为负,结果未定义。

算术一元运算符(+和-)对标量和矢量、整数和浮点类型进行操作。

算术后和前递增和递减运算符(--和++)对标量和矢量整数类型进行操作。所有的一元运算符都对其操作数逐个分量工作。该结果是它们操作的相同类型。对于后和前递增和递减,表达式必须是能够分配的表达式(1值)。前递增和前递减向它们操作的表达式的内容加1或减1,前递增或前递减表达式的值是该修改的所得值。后递增和后递减表达式对它们操作的表达式内容加1或减1,但所得表达式在执行后递增或后递减之前具有该表达式的值。

关系运算符大于(>)、小于(<)、大于等于(>=)和小于等于(<=)对标量和矢量、整数和浮点类型操作,以测试矢量关系运算符结果中的任意或所有元素是否测试为真。例如,为了在if(…)语句的语境中使用,请参见关系函数一节中定义的任意和所有内置函数。该结果是布尔(bool型)标量或矢量。在操作数类型转换之后,以下情况是有效的:

两个操作数是标量。在这种情况下,应用操作,得到bool。

一个操作数是标量,另一个是矢量。在这种情况下,可以对标量进行通常的算术转换,转换成矢量操作数使用的元素类型。然后将标量类型拓宽到与矢量操作数具有相同数量分量的矢量。逐个分量执行运算,结果是布尔矢量。

两个操作数是相同类型的矢量。在这种情况下,逐个分量执行运算,获得布尔矢量。

如果任一实参为NaN,关系运算符始终返回假。

等于运算符等于(==)和不等于(!=)对标量和矢量、整数和浮点类型进行操作。所有等于运算符都获得布尔(bool型)标量或矢量。在操作数类型转换之后,以下情况是有效的:

两个操作数是标量。在这种情况下,应用操作,得到bool。

一个操作数是标量,另一个是矢量。在这种情况下,可以对标量进行通常的算术转换,转换成矢量操作数使用的元素类型。然后将标量类型拓宽到与矢量操作数具有相同数量分量的矢量。逐个分量执行运算,结果是布尔矢量。

两个操作数是相同类型的矢量。在这种情况下,逐个分量执行运算,获得相同大小的布尔矢量。

隐式转换的所有其他情况都是非法的。如果实参之一或两者是“非数字”(NaN),等于运算符equal(==)返回假。如果实参之一或两者是“非数字”(NaN),等于运算符not equal(!=)返回真。

位操作运算符与(&)、或(|)、异或(^)、非(~)运算符对所有标量和矢量内置类型操作,除内置标量和矢量浮点类型之外。对于内置矢量类型而言,逐个分量地应用该运算符。如果一个操作数是标量,另一个是矢量,可以对标量进行通常的算术转换,转换成矢量操作数使用的元素类型。然后将标量类型拓宽到与矢量操作数具有相同数量分量的矢量。逐个分量执行运算,得到相同大小的矢量。

逻辑运算符与(&&)、或(||)对两个布尔表达式进行运算。结果是标量或矢量布尔值。

逻辑一元运算符非(!)对布尔型表达式进行运算。结果是标量或矢量布尔值。

三元选择运算符(?:)对三个表达式(exp1?exp2:exp3)进行运算。该运算符对第一表达式exp1求值,这必然获得标量布尔值。如果结果为真,它选择对第二表达式求值;否则,它对第三表达式求值。仅对第二和第三表达式之一求值。第二和第三表达式可以是任何类型,只要它们的类型匹配即可,或者有第0节中的转换,其可以应用于表达式之一以使其类型匹配,或者一者为矢量,另一个为标量,在这种情况下,将标量拓宽到与矢量类型相同的类型。这一所得匹配类型是整个表达式的类型。

一的补数运算符(~)。该运算符必须是标量或矢量整数类型,结果是其操作数的一的补数。

运算符右移(>>)、左移(<<)对所有标量和矢量整数类型操作。对于内置矢量类型而言,逐个分量地应用该运算符。对于右移(>>)、左移(<<)运算符,如果第一个操作数是标量,最右的操作数必须是标量。如果第一个操作数是矢量,最右操作数可以是矢量或标量。

E1<<E2的结果是E1被左移位E2中log2(N)个最低有效位,E2被视为无符号的整数值,其中N是用于表示E1数据类型的位数(如果E1是标量),或者用于表示E1元素类型的位数(如果E1是矢量)。用零填充空出的位。

E1>>E2的结果是E1被右移位E2中log2(N)个最低有效位,E2被视为无符号的整数值,其中N是用于表示E1数据类型的位数(如果E1是标量),或者用于表示E1元素类型的位数(如果E1是矢量)。如果E1具有无符号类型,或者如果E1具有有符号类型和非负值,则利用零填充空出的位。如果E1具有有符号类型和负值,利用一填充空出的位。

赋值运算符的行为如C++11规范所述。对于lvalue=表达式的赋值运算,如果表达式是标量类型,且lvalue是矢量类型,则将标量转换成矢量操作数使用的元素类型。然后将标量类型拓宽到与矢量操作数具有相同数量分量的矢量。逐个分量执行运算,结果是相同大小的矢量。

注:上文未描述的受到C++11支持的运算符(例如,sizeof(T)、一元(&)运算符和逗号(,)运算符)的行为如C++11规范中所述那样。

矩阵运算符

算术运算符加(+)、减(-)对矩阵进行操作。两个矩阵都必须具有相同数量的行和列。逐个分量执行运算,得到相同大小的矩阵。算术运算符乘(*)操作于:标量和矩阵、矩阵和标量、矢量和矩阵、矩阵和矢量或矩阵和矩阵。

如果一个操作数是标量,标量值被乘以矩阵的每个分量,得到相同大小的矩阵。右矢量操作数被作为列矢量对待,左矢量操作数被作为行矢量。对于矢量-矩阵、矩阵-矢量和矩阵-矩阵乘法,左操作数的列数需要等于右操作数的行数。乘法操作进行线性代数乘法,获得与左操作数具有相同行数且与右操作数具有相同列数的矢量或矩阵。

以下示例假设这些矢量、矩阵和标量变量被初始化:

float3v;

float3x3m;

float a=3.0f;

以下矩阵到标量乘法

float3x3m1=m*a;

等价于:

m1[0][0]=m[0][0]*a;

m1[0][1]=m[0][1]*a;

m1[0][2]=m[0][2]*a;

m1[1][0]=m[1][0]*a;

m1[1][1]=m[1][1]*a;

m1[1][2]=m[1][2]*a;

m1[2][0]=m[2][0]*a;

m1[2][1]=m[2][1]*a;

m1[2][2]=m[2][2]*a;

以下顶点到矩阵乘法

float3u=v*m;

等价于:

u.x=dot(v,m[0]);

u.y=dot(v,m[1]);

u.z=dot(v,m[2]);

以下矩阵到顶点乘法

float3u=m*v;

等价于:

u=v.x*m[0];

u+=v.y*m[1];

u+=v.z*m[2];

以下矩阵到矩阵乘法

float3x3m,n,r;

r=m*n;

等价于:

r[0]=m[0]*n[0].x;

r[0]+=m[1]*n[0].y;

r[0]+=m[2]*n[0].z;

r[1]=m[0]*n[1].x;

r[1]+=m[1]*n[1].y;

r[1]+=m[2]*n[1].z;

r[2]=m[0]*n[2].x;

r[2]+=m[1]*n[2].y;

函数、变量和限定词

这一章描述如何声明函数、实参和变量。这一章还详述了如何将限定词常常用于函数、实参和变量以指定限制。

函数限定词

该语言支持以下限制可以如何使用函数的限定词:

kernel-数据并行计算内核。

vertex_fetch-获取着色器,其从资源读取并向顶点着色器返回每个顶点的输入。

vertex-针对顶点流中的每个顶点执行并生成每个顶点的输出的顶点着色器。

fragment–针对片段流中每个片段及其关联数据执行并生成每个片段的输出的片段着色器。

顶点着色器也可以从类似于顶点获取着色器的资源读取(缓冲区和纹理)。顶点获取着色器允许开发者将用于在顶点着色器中声明每个顶点输入的数据类型与用于声明从资源(例如顶点缓冲区)读取的顶点数据的数据类型解耦。通过解耦这些数据类型,可以将顶点着色器与一个或多个获取着色器配对,反之亦然。在利用合适的API创建管道对象时,顶点获取着色器是任选的。

在函数开始时,在其返回类型之前,使用函数限定词。以下示例示出了用于计算函数的语法。

kernel void

foo(...)

{

...

}

对于利用内核限定词声明的函数,返回类型必须为空。

仅有图形函数可以被声明具有vertex_fetch、vertex或fragment限定词之一。对于图形函数而言,返回类型标识函数生成的输出是针对每个顶点还是每个片段。用于图形函数(vertex_fetch除外)的返回类型可以是void,表示该函数不生成输出。

使用kernel、vertex_fetch、vertex或fragment函数限定词的函数不能调用也使用这些限定词的函数,或者编译错误结果。

用于变量和实参的地址空间限定词

该语言实现地址空间限定词以指定分配函数变量或实参的存储器的区域。这些限定词描述针对如下变量的不相交地址空间:

global

local

constant

private

要获得每者的更多细节,请参见如下相关章节。必须要利用地址空间限定词声明作为类型指针的图形或内核函数的所有实参。对于图形函数,必须要在global或constant地址空间中声明作为指向类型的指针或引用的实参。对于内核函数,必须要在global、local或constant地址空间中声明作为指向类型的指针或引用的实参。以下示例介绍了几个地址空间限定词的使用。(这里仅在如下所述由内核函数调用foo时,针对指针l_data支持local限定词。

针对程序范围处变量的地址空间必须是常数。

必须要利用本节中论述的地址空间限定词之一声明作为指针或引用的任何变量。如果在指针或引用类型声明上缺失了地址空间限定词,则发生编译错误。

global地址空间

global地址空间名称是指从既可读又可写的全局存储器池分配的缓冲区存储器对象。

可以将缓冲区存储器对象声明为指向标量、矢量或用户定义的结构体的指针或引用。在经由主机代码中的合适的API调用分配存储器对象时,确定缓冲区存储器对象的实际大小。

一些示例为:

由于纹理对象始终是从全局地址空间分配的,因此纹理类型不需要global地址限定词。纹理对象的元素不能被直接访问。提供了从纹理对象读取和向其写入的函数。

local地址空间

local地址空间名称用于需要在本地存储器中分配并由工作组的所有工作项目共享的内核函数内部的变量。本地地址空间中声明的变量不能用于图形函数中。

内核函数内部的local地址空间中分配的变量为执行内核的每个工作组而分配,并仅在执行内核的工作组的寿命期间存在。

内核函数内部的local地址空间中声明的变量必须要发生于函数范围处。在以下函数示例中,在本地地址空间中适当分配浮点变量a和数组b。然而,浮点变量c不在函数范围处声明,因此这里不允许其声明。(下文更详细地解释以下代码中的限定词[[local_index(0)]]。)

内核函数内部的本地地址空间中分配的变量不能同时被声明和初始化。在以下示例中,在其声明期间对变量a进行适当初始化,但变量b的值之后进行适当设置。

constant地址空间

constant地址空间的名称必须要用于程序范围中的变量,它们是在全局存储器中分配的并在函数内部作为只读变量被访问。

程序范围中的变量与程序具有相同寿命,它们的值在对程序中任何计算或图形函数的调用之间持续。在计算内核函数中,可以在其执行期间由内核的所有(全局)工作项目访问只读变量。

程序范围中的变量必须要在constant地址空间中声明并在声明语句期间初始化。用于对它们进行初始化的值必须是编译时常数。

constant float samples[]={1.0f,2.0f,3.0f,4.0f};

允许对constant地址空间的指针或引用作为函数的实参。

向constant地址空间中声明的变量写入是编译时错误。声明这样的变量而不进行初始化也是编译时错误。

private地址空间

函数内部声明的变量处于private地址中。

函数实参和变量

图形和内核函数的所有输入,除了针对constant地址空间中的初始化变量和程序范围中声明的采样器之外,以及输出,都作为实参被传递。图形(顶点和片段)和内核函数的实参可以是以下之一:

全局缓冲区-对全局地址空间中任何数据类型的指针或引用(参见以上缓冲区一节)

常数缓冲区-对常数地址空间中任何数据类型的指针或引用(参见以上缓冲区一节)

纹理对象(参见以上纹理一节)

采样器对象(参见以上采样器一节)

本地缓冲区(仅可以用作内核函数的实参)-指向local地址空间中类型的指针

常数缓冲区、全局缓冲区、纹理或采样器的数组。

指定为图形或内核函数的实参值的缓冲区(全局和常数)不能是别名,即,被作为实参值传递的缓冲区不能与传递到同一图形或内核函数的独立实参的另一缓冲区交叠。

这些函数的实参常常被指定具有属性限定词,以对其使用提供进一步指导。属性限定词被用于指定:

用于实参的资源位置(参见以下定位资源的属性限定词一节),

支持在固定函数和可编程管线级之间通信数据的内置变量(参见以下的用于内置变量的属性限定词一节),

沿管线从顶点函数向片段函数下行发送哪些数据(参见以下章节中的stage_in限定词)。

定位资源的属性限定词

对于每个实参,必须要指定属性限定词以标识要用于这种实参类型的资源的位置。在一个实施方案中,框架API使用这种属性标识资源的位置。

全局和常数缓冲区–[[buffer_index(slot)]]

纹理–[[texture_index(slot)]]

采样器–[[sampler_index(slot)]]

本地缓冲区–[[local_index(slot)]]

数组–[[buffer_index(slot,count)]]、[[texture_index(slot,count)]]或[[sampler_index(slot,count)]]。

slot值是无符号的整数,标识被分配的资源的位置。适当的语法是让属性限定词追随实参/变量名称。

注意:资源位置在顶点函数和关联的顶点获取函数之间共享。

以下示例是简单内核函数,add_vectors,在全局地址空间中添加两个缓冲区的数组inA和inB,并返回缓冲区out中的结果。属性限定词(buffer_index(slot))指定函数实参的资源位置。

以下示例示出了用于几种不同类型(缓冲区、纹理和采样器)的函数实参的属性限定词。

以下示例示出了用于数组类型(缓冲区数组、纹理数组和采样器数组)的函数实参的属性限定词。

指定资源并向全局存储器输出的顶点函数示例

以下示例是顶点函数,render_vertex,其向数组xform_pos_output中的全局存储器输出,这是利用global限定词指定的函数实参(这是在以上global地址空间一节中介绍的)。如以上定位资源的属性限定词一节中介绍的那样,所有render_vertex函数实参都是利用资源限定词(buffer_index(0)、buffer_index(1)、buffer_index(2)和buffer_index(3))指定的。(本实施例中示出的position限定词在以下用于内置变量属性限定词一节中论述。)

用于内置变量的属性限定词

一些图形操作发生于固定函数管线级中,需要向图形函数提供值或从其接收值。内置输入和输出变量用于在图形(顶点和片段)函数和固定函数图形管线级之间通信值。属性限定词用于图形函数的实参和返回类型以识别这些内置变量。

用于顶点或顶点获取函数输入的属性限定词

表6(图10)列出了可以被指定用于顶点或顶点获取函数的实参以及它们可以使用的对应数据类型的内置属性限定词。

用于顶点函数输出的属性限定词

表7(图11)列出了可以被指定用于顶点函数的返回类型或顶点函数返回的结构体的成员(以及它们可以使用的对应数据类型)的内置属性限定词。

以下示例描述称为process_vertex的vertex函数。该函数返回称为VertexOutput的用户定义的结构体,其包含表示顶点位置的内置变量,因此其需要[[position]]限定词。

用于片段函数输入的属性限定词

表8(图12)列出了可以被指定用于片段函数的实参(以及它们对应的数据类型)的内置属性限定词。

注意:如果有关联的片段函数,顶点函数必须输出利用位置限定词声明的返回类型。

利用[[position]]属性作为片段函数的输入而声明的变量可以使用以下采样和内插限定词之一:center_no_perspective或centroid_no_perspective。对于[[color(m)]],使用m指定访问(读取或写入)片段函数中多个彩色附件时的彩色附件索引。m是任选的,可以是0到7的值。如果未指定m,彩色附件索引从0开始。如果片段函数中仅有单个彩色附件,那么不能使用m。(参见以下每个片段的函数和可编程混合各节中指定彩色附件的实施例。)

用于片段函数输出的属性限定词

片段函数的返回类型描述每个片段的输出。片段函数可以输出一个或多个渲染目标色彩值、深度值和覆盖掩模,这必须要使用表9(图13)中列出的属性限定词标识。如果片段函数未输出深度值,则向深度附件输出光栅化程序生成的深度值。

与针对片段输入的[[color(m)]]以相同方式指定针对片段输出的色彩附件索引m(参见表8(图12)的论述)。

如果片段函数写入深度值,则必须要利用如下值之一指定depth_qualifier。

any

greater

less

unchanged

以下实施例示出了可以如何指定色彩附件索引。clr_f中写入的色彩值向色彩附件索引0写入,clr_i向色彩附件索引1写入,clr_ui向色彩附件索引2写入。

用于内核函数输入的属性限定词

表10(图14)列出了可以被指定用于内核函数的实参以及它们可以使用的对应数据类型的内置属性限定词。

关于内核函数属性限定词的备注:

用于声明[[global_id]]、[[global_size]]、[[local_id]]、[[local_size]]和[[thread_group_id]]的任何类型都必须是标量类型或矢量类型。如果是矢量类型,用于声明这些实参的矢量类型的分量数量必须匹配。用于声明[[global_id]]和[[global_size]]的数据类型必须匹配。用于声明[[local_id]]和[[local_size]]的数据类型必须匹配。如果声明[[local_id]]或[[local_size]]为uint、uint2或uint3类型,必须要声明[[linear_local_id]]为uint类型。

stage_in限定词

利用来自顶点函数的输出和光栅化程序生成的片段生成片段函数的每片段输入。类似地,可以利用来自顶点获取函数的输出生成顶点函数的每个顶点的输入。每个片段或每个顶点的输入:必须是片段或顶点函数的第一实参,必须利用[[stage_in]]属性限定词标识。

片段或顶点函数中仅一个实参可以利用stage_in限定词声明。对于利用stage_in限定词声明的用户定义的结构体,该结构体的成员可以是:标量整数或标量浮点值、整数或浮点值矢量、整数或浮点值矩阵,或者标量、矢量或矩阵数组(它们是或包含整数或浮点值)。

对于使用stage_in限定词的完整实施例,请参见下文。

使用stage_in限定词的顶点和顶点获取函数示例

以下实施例定义读取色彩和位置数据的顶点获取函数fetch_vertex。fetch_vertex首先将色彩数据打开并转换成半精确度浮点值的4分量矢量。fetch_vertex最终返回输出(每个顶点的VertexInput结构体),该输出被管线化处理为使用[[stage_in]]限定词的顶点函数render_vertex的第一实参。

使用stage_in限定词的片段函数示例

上一节中的示例在前面介绍了process_vertex顶点函数,其返回每个顶点的VertexOutput结构体。在以下示例中,来自process_vertex的输出被管线化输送以变为称为render_pixel的片段函数的输入,因此片段函数的第一实参使用[[stage_in]]限定词,且必须还是传入的VertexOutput类型。(在render_pixel中,imgA和imgB 2D纹理调用内置函数sample,在下面的2D纹理函数一节中介绍该函数。)

存储类指定符

该语言支持静态和外部存储类指定符。该语言不支持thread_local存储类指定符。extern存储类指定符仅仅能够用于在程序范围中声明的函数和变量或在函数内部声明的变量。static存储类指定符仅仅用于在程序范围中声明的全局变量(参见constant地址空间一节),不针对图形或内核函数中的本地变量。在以下示例中,static指示符不正确地被内核函数中的本地变量b和c使用。

采样和内插限定词

采样和内插限定词仅用于顶点函数的返回类型和片段函数的实参。限定词确定了片段函数使用什么采样方法以及如何进行内插,包括是否使用视角正确内插、线性内插或无内插。

采样和内插限定词可以在利用stage_in限定词声明的任何结构成员上指定。支持的采样和内插限定词为:

center_perspective(默认除了[[position]]变量)

center_no_perspective

centroid_perspective

centroid_no_perspective

sample_perspective

sample_no_perspective

flat

以下示例是指定如何内插特定成员中的数据的用户定义的结构体:

对于整数和double类型,仅有的有效内插限定词是flat。

采样限定词变体(sample_perspective和sample_no_perspective)在样本位置而非在像素中心内插。利用这些限定词之一,使用这些变量的片段函数中的片段函数或代码块针对每个样本而非每个片段来执行。

每个片段的函数和每个样本的函数

片段函数通常是针对每个片段执行的。采样限定词标识是否要在每个样本还是每个片段处内插任何片段输入。类似地,[[sample_id]]属性用于标识当前样本索引,[[color(m)]]属性用于标识目的地片段色彩或样本色彩(对于多次采样的色彩附件)值。如果这些限定词的任何一个与片段函数的实参一起使用,该片段函数可以逐个样本而非逐个像素执行。该具体实施可以决定仅执行取决于每个样本的值的代码,以逐个样本执行,片段函数的其余部分可以逐个片段执行。应当逐个样本执行。

仅具有指定了样本的输入(或利用[[sample_id]]或[[color(m)]]限定词声明的)在逐个片段或逐个样本调用之间有差别,而其他输入仍然在像素中心处内插。

以下样本使用[[color]]属性指定,应当逐个样本地执行这一片段函数。

可编程混合

片段函数可以用于执行逐个片段或逐个样本的可编程混合。[[color(m)]]属性限定词标识的色彩附件索引可以被指定为片段函数的实参。

以下是根据一个实施方案的编程混合实施例:

以下是根据一个实施方案的片段函数的编程混合实施例:

图形函数-签名匹配

图形函数签名是来自图形函数的输入或输出的参数列表。

顶点-片段签名匹配

在顶点和片段函数之间可以传递两类数据:用户定义的和内置变量。

片段函数的每个实例的输入是利用[[stage_in]]限定词

声明的。这些是由关联的顶点函数输出的。

内置变量是利用4.3.2节中定义的属性限定词之一声明的。这些是由顶点函数(例如[[position]]、[[point_size]]、[[clip_distance]])生成的,由光栅化程序(例如,[[point_coord]]、[[front_facing]]、[[sample_id]]、[[sample_mask]])生成的,或引用作为片段函数的输入传递的帧缓冲色彩值(例如[[color]])。

必须要始终返回内置变量[[position]]。如果需要,由顶点函数生成的其他内置变量([[point_size]]、[[clip_distance]])必须要在顶点函数的返回类型中声明,但不能被片段函数访问。

光栅化程序生成或引用帧缓冲色彩值的内置变量还可以利用合适的属性限定词被声明为片段函数的实参。

属性[[user(name)]]语法也可以用于为任何用户定义的变量指定属性名称。

如果出现以下情况,顶点和片段函数被视为具有匹配签名:

没有在片段函数中利用[[stage_in]]限定词声明的输入实参。

对于利用[[stage_in]]声明的片段函数实参,与这个实参相关联的类型中的每个元素都可以是以下之一:由光栅化程序生成的内置变量、作为片段函数的输入传递的帧缓冲色彩值或用户生成的顶点函数输出。对于由光栅化程序或帧缓冲色彩值生成的内置变量,对于要和顶点返回类型的元素相关联的匹配类型没有要求。对于作为用户生成的输出的元素,以下规则适用:

如果针对元素指定了由[[user(name)]]给出的属性名称,那么这个属性名称必须匹配顶点函数返回类型中的元素,它们的对应数据类型也必须匹配。

如果未指定[[user(name)]]属性名称,那么实参名称和类型必须匹配。

以下是兼容签名的实施例:

my_vertex_shader和my_fragment_shader或my_vertex_shader和my_fragment_shader2可以一起用于渲染基元。以下是兼容签名的另一实施例:

以下是兼容签名的另一实施例:

以下是兼容签名的另一实施例:

以下是不兼容签名的实施例:

以下是不兼容签名的另一实施例:

顶点获取-顶点签名匹配

如果利用[[stage_in]]限定词声明的顶点函数的实参类型匹配顶点获取函数的返回类型,那么顶点获取和顶点函数被视为具有匹配签名。以下限制适用于:

顶点获取函数的返回类型不能指定任何内置顶点变量(例如[[position]]、[[point_size]]、[[clip_distance]])。顶点获取函数的任何输入实参都不能利用[[stage_in]]限定词声明。

另外的属性限定词

该语言支持以下另外的属性(除了更早章节中所述那些之外)。

[[early_fragment_tests]]限定词允许片段函数实现早期片段测试。如果利用片段函数指定这个属性,则在片段函数执行之前执行逐个片段的测试。否则,在片段函数执行之后执行它们。利用[[early_fragment_tests]]限定词声明的片段函数不能输出深度值。片段函数的返回类型不能包含利用[[depth(depth_qualifier)]]限定词声明的元素,否则会导致编译错误。

如果在对内核排队时指定了工作组大小,那么使用该工作组大小。如果在对给定内核排队时未指定工作组大小,则[[thread_group_size(x,y,z)]]限定词指定要用于内核的工作组大小。

另外的备注

由已经调用来处理未被光栅化基元覆盖的片段或样本的片段着色器向缓冲区或纹理写入是无效的。这可发生以有助于为例如纹理查找计算导数。

标准库

本章描述根据一个实施方案用于编程语言的标准库支持的函数。

名称空间和标头文件

标准库函数和枚举是在语言特有的名称空间中声明的。除了标准库函数中描述的标头文件之外,<language_stdlib>标头也是可用的,并可以访问标准库支持的所有函数。(在一个实施方案中,“language_stdlib”中的术语“language”可以由用于编译器的名称替代。)

公共函数

表11(图15)中的函数在标准库中,且是在标头<language_common>中定义的。(在一个实施方案中,“language_common”中的术语“language”可以由用于编译器的名称替代。)T是标量或矢量浮点类型之一。

整数函数

表12(图16)中的整数函数在标准库中,且是在标头<language_integer>中定义的。(在一个实施方案中,“language_integer”中的术语“language”可以由用于编译器的名称替代。)T是标量或矢量整数类型之一。

关系函数

表13(图17)中的关系函数在标准库中,且是在标头<language_relational>中定义的。(在一个实施方案中,“language_relational”中的术语“language”可以由用于编译器的名称替代。)T是标量或矢量浮点类型之一。Ti是标量或矢量整数或布尔类型之一。Tb仅仅指标量或矢量布尔类型之一。

数学函数

表14(图18-图19)中的数学函数在标准库中,且是在标头<language_math>中定义的。(在一个实施方案中,“language_math”中的术语“language”可以由用于编译器的名称替代。)T是标量或矢量浮点类型之一。Ti仅仅指标量或矢量整数类型。

有两种数学函数的变体可用:精确的和快速的变体。

-ffast-math编译器选项(指以下编译器选项一节)可以用于指定在编译编程语言中的源时要使用哪种变体。precise和fast嵌套名称空间也可用于允许开发者明确选择这些数学函数的快速或精确变体。

实施例

#include<language_stdlib>

using namespace language;

float x;

float a=fast::sin(x);//use fast version of sin()

float b=precise::cos(x);//use precise version of cos()

矩阵函数

表15(图20)中的函数在标准库中,且是在标头<language_matrix>中定义的。(在一个实施方案中,“language_matrix”中的术语“language”可以由用于编译器的名称替代。)T为浮点、half或double。

示例

float4x4mA;

float det=determinant(mA);

几何函数

表16(图21)中的函数在标准库中,且是在标头<language_geometric>中定义的。(在一个实施方案中,“language_geometric”中的术语“language”可以由用于编译器的名称替代。)T为浮点、half或double。

计算函数

本节及其子节中的计算函数仅能够从内核函数被调用,且是在标头<language_compute>中定义的。(在一个实施方案中,“language_compute”中的术语“language”可以由用于编译器的名称替代。)

线程组同步函数

支持表17(图22)中的工作组函数。

执行内核的工作组中的所有工作项目必然会遇到thread_group_barrier函数。

如果thread_group_barrier在条件语句内部,且如果任何工作项目进入条件语句中并执行该屏障,那么所有工作项目都必须进入条件式并执行该屏障。

如果thread_group_barrier在循环内部,对于循环的每次迭代,所有工作项目都必须在允许任何工作项目在越过thread_group_barrier继续执行之前执行thread_group_barrier。thread_group_barrier函数还对存储器栅栏(读取和写入)排队,以确保存储器操作正确排序到本地或全局存储器。

如表18(图23)中所述,thread_group_barrier中的mem_flags实参是可以设置成一个或多个以下标记的位字段。

图形函数

本节及其子节列出了可以由片段和顶点函数调用的一组图形函数。这些是在标头<language_graphics>中定义的。(在一个实施方案中,“language_graphics”中的术语“language”可以由用于编译器的名称替代。)

片段函数

本节中的函数(表19(图24)、表20(图25)和表21(图26)中列出的)仅可以在片段函数(利用片段限定词声明的函数)内部或从片段函数调用的函数内部被调用。否则,该行为未定义并可以导致编译时错误。

片段函数-导数

该语言包括表19(图24)中的函数以计算导数。T是float、float2、float3、float4、half、half2、half3或half4之一。注意:导数在非均匀控制流中未定义。

片段函数-样本

该语言包括表20(图25)中的以下每个样本的函数。

get_num_samples和get_sample_position返回针对色彩附件的样本数量和针对给定样本索引的样本偏移。例如,这可以用于逐个片段进行着色,但不对每个样本进行alpha测试用于透明超采样。

片段函数-流控制

表21(图26)中的语言函数用于终结片段。

纹理函数

纹理函数被分类成:来自纹理的样本、从纹理读取(无采样器读取)、从纹理收集、向纹理写入和纹理查询函数。

这些是在标头<language_texture>中定义的。(在一个实施方案中,“language_texture”中的术语“language”可以由用于编译器的名称替代。)纹理sample、sample_compare、gather和gather_compare函数为2D纹理、2D纹理数组和3D纹理采取偏移实参。该偏移是在查找每个纹理之前应用于纹理坐标的整数值。这个整数值可以在-8到+7范围中。默认值为0。

用于2D纹理、2D纹理数组、3D纹理、立方图和立方图数组的纹理sample和sample_compare函数的重载变体可用,并允许在采样之前利用应用于mip水平的偏置或利用x和y方向上用户提供的梯度对纹理采样。

注意:纹理sample、sample_compare、gather和gather_compare函数需要利用sample访问限定词声明纹理。纹理读取函数需要利用sample或read访问限定词声明纹理。纹理写入函数需要利用write访问限定词声明纹理。

1D纹理

可以使用以下内置函数从1D纹理采样。

vec<T,4>sample(sampler s,float coord)const

可以使用以下内置函数从1D纹理进行无采样器读取:

vec<T,4>read(uint coord,uint lod=0)const

可以使用以下内置函数向1D纹理写入特定mip水平。

void write(vec<T,4>color,

uint coord,

uint lod=0)

提供了以下内置1D纹理查询函数。

uint get_width(uint lod=0)const

uint get_num_mip_levels()const

1D纹理数组

可以使用以下内置函数从1D纹理数组采样。

vec<T,4>sample(sampler s,

float coord,

uint array)const

可以使用以下内置函数从1D纹理数组进行无采样器读取:

vec<T,4>read(uint coord,

uint array,

uint lod=0)const

可以使用以下内置函数向1D纹理数组写入特定mip水平。

提供了以下内置1D纹理数组查询函数。

uint get_width(uint lod=0)const

uint get_array_size()const

uint get_num_mip_levels()const

2D纹理

可以使用以下数据类型和对应的构造函数指定各种采样选项:

bias(float value)

level(float lod)

gradient2d(float2dPdx,float2dPdy)

可以使用以下内置函数从2D纹理采样。

lod_options必须是以下类型之一:bias、level或gradient2d。

可以使用以下内置函数从2D纹理进行无采样器读取:

vec<T,4>read(uint2coord,

uint lod=0)const

可以使用以下内置函数向2D纹理写入。

void write(vec<T,4>color,

uint2coord,

uint lod=0)

可以使用以下内置函数收集四个样本,四个样本会在对2D纹理采样时用于进行线性内插。

提供了以下内置2D纹理查询函数。

uint get_width(uint lod=0)const

uint get_height(uint lod=0)const

uint get_num_mip_levels()const

2D纹理采样示例

以下代码示出了根据其实参,2D纹理样本函数的几种用途。

texture2d<float>tex;

sampler s;

float2coord;

int2offset;

float lod;

//no optional arguments

float4clr=tex.sample(s,coord);

//sample using a mip-level

clr=tex.sample(s,coord,level(lod));

//sample with an offset

clr=tex.sample(s,coord,offset);

//sample using a mip-level and an offset

clr=tex.sample(s,coord,level(lod),offset);

2D纹理数组

可以使用以下内置函数从2D纹理数组采样。

lod_options必须是以下类型之一:bias、level或gradient2d。可以使用以下内置函数从2D纹理数组执行无采样器读取:

vec<T,4>read(uint2coord,

uint array,uint lod=0)const

可以使用以下内置函数向2D纹理数组写入。

可以使用以下内置函数收集四个样本,四个样本会在对2D纹理数组采样时进行线性内插。

提供了以下内置2D纹理数组查询函数。

uint get_width(uint lod=0)const

uint get_height(uint lod=0)const

uint get_array_size()const

uint get_num_mip_levels()const

3D纹理

可以使用以下数据类型和对应的构造函数指定各种采样选项:

bias(float value)

level(float lod)

gradient3d(float3dPdx,float3dPdy)

可以使用以下内置函数从3D纹理采样。

lod_options必须是以下类型之一:bias、level或gradient3d。可以使用以下内置函数从3D纹理执行无采样器读取:

vec<T,4>read(uint3coord,

uint lod=0)const

可以使用以下内置函数向3D纹理写入。

void write(vec<T,4>color,

uint3coord,

uint lod=0)

提供了以下内置3D纹理查询函数。

uint get_width(uint lod=0)const

uint get_height(uint lod=0)const

uint get_depth(uint lod=0)const

uint get_num_mip_levels()const

立方图纹理

可以使用以下数据类型和对应的构造函数指定各种采样选项:

bias(float value)

level(float lod)

gradientcube(float3dPdx,float3dPdy)

可以使用以下内置函数从立方图纹理采样。

lod_options必须是以下类型之一:bias、level或gradientcube。可以使用以下内置函数向立方图纹理写入。

注意:表22(图27)描述了立方图的面和用于标识面的数字。

可以使用以下内置函数收集四个样本,四个样本会在对立方图纹理采样时进行线性内插。

vec<T,4>gather(sampler s,

float3coord,

component c=component::x)const

提供了以下内置立方图纹理查询函数。

uint get_width(uint lod=0)const

uint get_height(uint lod=0)const

uint get_num_mip_levels()const

立方图纹理数组[任选]

可以使用以下内置函数从立方图纹理数组采样。

lod_options必须是以下类型之一:bias、level或gradientcube。可以使用以下内置函数向立方图纹理数组写入。

可以使用以下内置函数收集四个样本,四个样本会在对立方图纹理数组采样时进行线性内插。

提供了以下内置立方图纹理数组查询函数。

uint get_width(uint lod=0)const

uint get_height(uint lod=0)const

uint get_array_size()const

uint get_num_mip_levels()const

2D多采样纹理

可以使用以下内置函数从2D多采样纹理执行无采样器读取:

vec<T,4>read(uint2coord,

uint sample)const

提供了以下内置2D多采样纹理查询函数。

uint get_width()const

uint get_height()const

uint get_num_samples()const

2D深度纹理

可以使用以下数据类型和对应的构造函数指定各种采样选项:

bias(float value)

level(float lod)

gradient2d(float2dPdx,float2dPdy)

可以使用以下内置函数从2D深度纹理采样。

lod_options必须是以下类型之一:bias、level或gradient2d。可以使用以下内置函数从2D深度纹理采样并将单个分量与指定的比较值比较

lod_options必须是以下类型之一:bias、level或gradient2d。T必须是float类型。可以使用以下内置函数从2D深度纹理执行无采样器读取:

T read(uint2coord,

uint lod=0)const

可以使用以下内置函数向2D深度纹理写入。

void write(T depth,

uint2coord,

uint lod=0)

可以使用以下内置函数收集四个样本,四个样本会在对2D深度纹理采样时用于进行线性内插。

vec<T,4>gather(sampler s,

float2coord,

int2offset=int2(0))const

可以使用以下内置函数收集四个样本,四个样本会在对2D深度纹理采样时用于进行线性内插并将这些样本与指定的比较值比较。

T必须是float类型。提供了以下内置2D深度纹理查询函数。

uint get_width(uint lod=0)const

uint get_height(uint lod=0)const

uint get_num_mip_levels()const

2D深度纹理数组

可以使用以下内置函数从2D深度纹理数组采样。

lod_options必须是以下类型之一:bias、level或gradient2d。可以使用以下内置函数从2D深度纹理数组采样并将单个分量与指定的比较值比较

lod_options必须是以下类型之一:bias、level或gradient2d。T必须是float类型。可以使用以下内置函数从2D深度纹理数组执行无采样器读取:

T read(uint2coord,

uint array,

uint lod=0)const

可以使用以下内置函数向2D深度纹理数组写入。

可以使用以下内置函数收集四个样本,四个样本会在对2D深度纹理数组采样时用于进行线性内插。

可以使用以下内置函数收集四个样本,四个样本会在对2D深度纹理数组采样时用于进行线性内插并将这些样本与指定的比较值比较。

T必须是float类型。提供了以下内置2D深度纹理数组查询函数。

uint get_width(uint lod=0)const

uint get_height(uint lod=0)const

uint get_array_size()const

uint get_num_mip_levels()const

立方图深度纹理

可以使用以下数据类型和对应的构造函数指定各种采样选项:

bias(float value)

level(float lod)

gradientcube(float3dPdx,float3dPdy)

可以使用以下内置函数从立方图深度纹理采样。

lod_options必须是以下类型之一:bias、level或gradientcube。可以使用以下内置函数从立方图深度纹理采样并将单个分量与指定的比较值比较

lod_options必须是以下类型之一:bias、level或gradientcube。T必须是float类型。可以使用以下内置函数向立方图深度纹理写入。

可以使用以下内置函数收集四个样本,四个样本会在对立方图深度纹理采样时用于进行线性内插。

vec<T,4>gather(sampler s,

float3coord)const

可以使用以下内置函数收集四个样本,四个样本会在对立方图深度纹理采样时用于进行线性内插并将这些样本与指定的比较值比较。

vec<T,4>gather_compare(sampler s,

float3coord,

float compare_value)const

T必须是float类型。提供了以下内置立方图深度纹理查询函数。

uint get_width(uint lod=0)const

uint get_height(uint lod=0)const

uint get_num_mip_levels()const

立方图深度纹理数组[任选]

可以使用以下内置函数从立方图深度纹理数组采样。

lod_options必须是以下类型之一:bias、level或gradientcube。可以使用以下内置函数从立方图深度纹理数组采样并将单个分量与指定的比较值比较

lod_options必须是以下类型之一:bias、level或gradientcube。T必须是float类型。可以使用以下内置函数向立方图深度纹理数组写入。

可以使用以下内置函数收集四个样本,四个样本会在对立方图深度纹理数组采样时用于进行线性内插。

vec<T,4>gather(sampler s,

float3coord,

uint array)const

可以使用以下内置函数收集四个样本,四个样本会在对立方图深度纹理数组采样时用于进行线性内插并将这些样本与指定的比较值比较。

T必须是float类型。提供了以下内置立方图深度纹理数组查询函数。

uint get_width(uint lod=0)const

uint get_height(uint lod=0)const

uint get_array_size()const

uint get_num_mip_levels()const

2D多采样深度纹理

可以使用以下内置函数从2D多采样深度纹理执行无采样器读取:

T read(uint2coord,

uint sample)const

提供了以下内置2D多采样深度纹理查询函数。

uint get_width()const

uint get_height()const

uint get_num_samples()const

封装和解封功能

本节列出了用于将矢量浮点数据转换成封装整数值以及从封装整数值转换成矢量浮点数据的语言函数。这些函数是在标头<language_pack>中定义的。(在一个实施方案中,“language_pack”中的术语“language”可以由用于编译器的名称替代。)参考以下转换各节获得如何从8位、10位或16位有符号或无符号整数值转换成归一化单精确度或半精确度浮点值,反之亦然。

解封整数;转换成浮点矢量

表23(图28)列出了从单一无符号整数解封多个值然后将它们转换成存储于矢量中的浮点值的函数。

将浮点矢量转换成整数,然后封装整数

表24(图29)列出了开始于浮点矢量,将分量转换成整数值,然后将多个值封装成单个无符号整数的函数。unpack_unorm2x16_to_half和unpack_snorm2x16_to_half函数在从16位unorm或snorm值转换成半精确度浮点时可能会导致精确度损失。

原子函数

该编程语言实施C++11原子和同步运算的子集。对于原子操作而言,在一个实施方案中仅支持memory_order_relaxed的memory_order。如果原子运算指向本地存储器,这些原子运算的存储器范围是工作组,如果原子运算指向全局存储器,则这些原子运算的存储器范围是设备。

原子类型仅有几种运算,尽管那些种类有很多实例。这一节指定每个一般的种类。

这些是在标头<language_atomic>中定义的。(在一个实施方案中,“language_atomic”中的术语“language”可以由用于编译器的名称替代。)注意:对于iOS而言,对于全局存储器的原子运算仅可以在内核函数(利用内核限定词声明的函数)内部或在从内核函数调用的函数内部执行。对于OS X,对全局存储器的原子运算可以由图形和内核函数执行。

memory_order enum被定义为:

enum memory_order{memory_order_relaxed};

在该语言的一个实施方案中仅支持memory_order_relaxed。

原子存储函数

这些函数以原子方式利用期望值替换由obj(*obj)指向的值。

原子负载函数

这些函数以原子方式获取由obj指向的值。

原子交换函数

这些函数以原子方式利用期望值替换obj指向的值,并返回先前保持的值obj。

原子比较和交换函数

这些函数以原子方式比较obj指向的值和期望的值。如果那些值相等,该函数利用期望值(通过执行读取-修改-写入操作)替换*obj。该函数返回先前保持的值obj。

原子获取和修改函数

以下运算执行算术和位计算。所有这些运算都适用于任何原子类型的对象。在表25(图30)中给出了关键字、运算符和计算的对应关系。

利用由key和arg指定的值的计算结果以原子方式替换由obj指向的值。这些操作是原子读取-修改-写入操作。对于有符号整数类型,算术被定义为使用二进制补码表示,在溢出时使用无声环绕。没有未定义的结果。返回先前保持的值obj。

编译器选项

该语言编译器可以在线(即,使用适当的API编译语言源)或离线使用。离线编译的该语言源可以作为二进制文件,使用适当的API被加载。

这一章介绍了根据一个实施方案的语言编译器支持的编译器选项,其被分类为预处理器选项、用于数学本征函数的选项、控制优化的选项和混杂选项。在线和离线编译器支持这些选项。

预处理器选项

这些选项在实际编译之前控制运行于每个程序源上的预处理器。

-D名称

将name利用定义1预定义为宏。

-D name=definition

definition的内容被符号化和处理,如同它们出现于#define指令中那样。这种选项可以接收多个选项,它们按照它们出现的次序被处理。这种选项允许开发者编译语言代码以改变启用或禁用哪些特征。

-I dir-向要搜索标头文件的目录列表添加目录dir。这个选项仅可用于离线编译器。

数学本征选项

这些选项控制编译器关于浮点算术的行为。这些选项在速度和正确度之间进行折中。

–fdenorms

-fno-denorms(默认)

这些选项控制如何处理单精确度、半精确度和双精确度未归一化数字。默认地,denorm被刷新到零。这些编译器选项启用或禁用(默认)denorm支持。

如果该设备不支持单一精确度非归一化数字,则针对单一精确度数字忽略fdenorms选项。如果该设备不支持半精确度非归一化数字,则针对半精确度数字忽略这个选项。

如果该设备不支持双精确度,则针对双精确度数字忽略这个选项。

这些选项仅适用于标量和矢量浮点变量和对程序之内这些浮点变量的计算。它们不适用于采样,从纹理对象读取或向纹理对象写入。

-ffast-math(默认)

-fno-fast-math

这些选项启用(默认)或禁用对可能违反IEEE 754标准的浮点算术的优化。它们还启用或禁用针对单精确度浮点标量和矢量类型的数学函数的高精确度变体。

控制语言版本的选项

以下选项控制编译器接受的统一图形/计算语言的版本。

-std=

确定要使用的语言修订。必须要提供这个选项的值。可能的值为:

ios-language10–支持用于iOS的统一图形/计算语言修订1.0程序。

osx-language10–支持用于Mac OS X的统一图形/计算语言修订1.0程序。

(在一个实施方案中,以上术语“language”可以由用于编译器的名称替代。)

任选特征

在为设备编译语言源时,使用以下编译器选项标识要启用的任选特征。这些选项默认是禁用的。

-fnative-double

-fno-native-double(默认)

启用或禁用(默认)对双精确度类型和浮点算术运算的支持。

如果–std=ios-language10,则有以下任选的特征:

帧缓冲区获取允许您从片段着色器中的色彩附件读取色彩。

如果–std=osx-language10,则有以下任选的特征:

立方图数组

数值兼容性

这一章涵盖语言如何表示有关数学运算中的精确度的浮点数字。一个实施方案中的语言符合IEEE 754标准的子集。

INF、NaN和非归一化数字

INF和NaN必须要针对单精确度和双精确度浮点数字得到支持,对于半精确度浮点数字是任选的。不需要支持信令NaN。

对具有单精确度和半精确度浮点的非归一化数字的支持是任选的。作为输入传递或作为单精确度或半精确度浮点运算的输出产生的非归一化单精确度或半精确度浮点数字可以被清零。

双精确度浮点支持是任选的。如果支持双精确度,需要支持具有双精确度浮点的非归一化数字。

舍入模式

对于单精确度和半精确度浮点运算,可以支持舍入到零或舍入到最接近值的舍入模式。对于完全支持计算的设备,对于单精确度浮点运算需要舍入到最接近值的舍入模式。

对于双精确度浮点,需要舍入到最接近值的舍入模式。

浮点异常

在该语言中禁用浮点异常。浮点异常的结果必须匹配针对未启用异常的情况的IEEE 754规范。

作为ULP的相对错误

表26(图31–图32)描述了单精确度和双精确度浮点基本算术运算和给定为ULP值的数学函数的最低精确度。用于计算算术运算的ULP值的参考值是无限精确的结果。

表27(图33)描述了给定为启用了快速数学的ULP值的常用单精确度浮点算术运算的最低精确度(除非将-ffast-math-disable指定为编译器选项,否则这是默认的)。对于其他数学函数而言,精确度如表26(图31–图32)中定义的那样。

清零模式中的边缘情况行为

如果将denormal清零,那么函数可以返回四种结果之一:

(1)针对非清零模式的任何相配结果。

(2)如果(1)给出的结果在舍入之前欠正常,可以将其清零。

(3)如果其欠正常操作数中的一者或多者被清零,针对该函数的任何非清零的相配结果。

(4)如果(3)的结果在舍入之前欠正常,可以将该结果清零。

在以上情况的每种中,如果将操作数或结果清零,零的符号未定义。

纹理寻址和转换规则

指定到sample、sample_compare、gather、gather_compare、read和write函数的纹理坐标不能是INF或NaN。此外,对于纹理read和write函数,纹理坐标必须参照纹理内部的区域。在后面的小节中,我们论述在图形或内核函数中读取和写入纹理时应用的转换规则。

针对归一化整数像素数据类型的转换规则

在这一节中,我们论述将归一化的整数像素数据类型转换到浮点值,反之亦然。

将归一化整数像素数据类型转换成浮点值

对于具有8位、10位或16位归一化无符号整数像素值的纹理,该纹理样本和读取函数将像素值从8位或16位无符号整数转换成在[0.0…1.0]范围中的归一化单精确度或半精确度浮点值。

对于具有8位或16位归一化有符号整数像素值的纹理,该纹理样本和读取函数将像素值从8位或16位无符号整数转换成在[-1.0…1.0]范围中的归一化单精确度或半精确度浮点值。

如表28(图34)的第二列中所列那样进行这些转换。转换规则的精确度保证为<=1.5ulp,除了第三列中所述的情况。

将浮点值转换成归一化整数像素数据类型

对于具有8位、10位或16位归一化无符号整数像素值的纹理,该纹理写入函数将单精确度或半精确度浮点像素值转换成8位或16位无符号整数。

对于具有8位或16位归一化有符号整数像素值的纹理,该纹理写入函数将单精确度或半精确度浮点像素值转换成8位或16位有符号整数。

从浮点值转换成归一化整数值的优选方法列在表29(图35)中。

GPU可以选择近似上述转换中使用的舍入模式。如果使用除舍入到最接近偶数值之外的舍入模式,该具体实施的绝对误差取决于舍入模式,舍入到最接近偶数值舍入模式产生的结果必须<=0.6。

用于半精确度浮点像素数据类型的转换规则

对于具有半精确度浮点像素色彩值的纹理而言,从half到float的转换是无损的。从float到half的转换利用舍入到最接近偶数或舍入到零的舍入模式来舍入尾数。在将float转换到half时可以生成的针对half数据类型的非归一化数字可以被清零。float NaN必须被转换到half类型中的适当NaN。float INF必须被转换到half类型中的适当INF。

用于浮点信道数据类型的转换规则

以下规则适用于读取和写入具有单精确度浮点像素色彩值的纹理。

NaN可以被转换成该设备支持的NaN值。

Denorm可以被清零。

所有其他值必须要被保留。

针对有符号和无符号整数像素数据类型的转换规则

对于具有8位或16位的有符号或无符号整数像素值的纹理,该纹理采样和读取函数返回有符号或无符号的32位整数像素值。本节中描述的转换必须要正确饱和。

向这些整数纹理写入执行表30(图36)中列出的转换之一。

用于sRGBA和sBGRA纹理的转换规则

从sRGB空间转换到线性空间是在从sRGB纹理采样时自动进行的。从sRGB转换到线性RGB是在应用对纹理采样时指定的采样器中指定的过滤器之前执行的。如果纹理具有α信道,则在线性色彩空间中存储α数据。

从线性到sRGB空间的转换是在从sRGB纹理写入时自动进行的。如果纹理具有α信道,则在线性色彩空间中存储α数据。

以下是用于根据上述规则将归一化8位无符号整数sRGB色彩值(称为c)转换成浮点线性RGB色彩值的转换规则。

所得的浮点值,如果被转换回sRGB值而未舍入到8位无符号整数值,则必须在初始sRGB值的0.5ulp之内。

以下是用于将线性RGB浮点色彩值(称为c)转换成归一化8位无符号整数sRGB值的转换规则。

以上转换的精确度应当使得

fabs(reference result–integer result)<=0.6.

应当理解,以上描述旨在是例示性的而非限制性的。例如,可彼此结合地使用上述实施方案。在回顾以上描述时,很多其他的实施方案对于本领域的技术人员而言将是显而易见的。因此应当参考所附权利要求以及命名此类权利要求的等同形式的完整范围来确定本发明的范围。

当前第1页1 2 3 
网友询问留言 已有0条留言
  • 还没有人留言评论。精彩留言会获得点赞!
1