├── README.md
├── ch01.md
├── ch02.md
├── ch03.md
├── ch04.md
├── ch05.md
├── ch06.md
└── ch07.md
/README.md:
--------------------------------------------------------------------------------
1 | # Metal 着色语言指南 V2.1
2 |
3 | [第一章 介绍](ch01.md)
4 |
5 | [第二章 数据类型](ch02.md)
6 |
7 | [第三章 运算符](ch03.md)
8 |
9 | [第四章 函数和变量申明](ch04.md)
10 |
11 | [第五章 Metal标准库](ch05.md)
12 |
13 | [第六章 编译器和预处理器](ch06.md)
14 |
15 | [第七章 数值合规性](ch07.md)
16 |
17 | [Metal Shading Language Specification](https://developer.apple.com/metal/Metal-Shading-Language-Specification.pdf)
--------------------------------------------------------------------------------
/ch01.md:
--------------------------------------------------------------------------------
1 | # 1 介绍
2 |
3 | 该文档描述了Metal标准图像计算语言。Metal是一门基于C++的编程语言,开发者可以用它来编写可以运行在GPU上进行图像处理和一般用途的数据并行计算。由于Metal基于C++,开发者会发现Metal熟悉易用。通过Metal,图像处理和计算程序可以用单一统一的语言进行编写,使得图像处理和计算无缝集成。
4 |
5 | Metal旨在与Metal框架协同工作,该框架管理Metal代码的执行以及可选的编译。Metal使用 clang和LVVM,因此开发者可以获得一个编译器,可以为运行在GPU上的代码提供接近metal性能的编译器。
6 |
7 | ## 1.1 读者
8 |
9 | 使用Metal框架编写代码的开发人员希望阅读本文档,因为他们需要使用Metal着色语言来编写要在GPU上执行的图形和计算程序。
10 |
11 | ## 1.2 目录组织
12 |
13 | 本文档分为以下章节:
14 |
15 | * 本章“简介”是对本文档的介绍,介绍了Metal和C++ 14之间的异同。
16 | * “数据类型”列出了Metal的数据类型,包括代表向量(Vectors)、矩阵(Matrices)、缓冲区(Buffers)、纹理(Textures)和采样器(Samplers)。它还讨论了类型对齐和类型转换。
17 | * “运算符”列出了Metal中的运算符。
18 | * “函数和变量声明”详细说明了如何声明函数和变量,有时会介绍限制他们使用方式的属性。
19 | * “Metal标准库”定义了一系列内置的Metal函数。
20 | * “编译器”详细介绍了Metal编译器的选项,包括预处理指令,数学内在函数选项和控制优化的选项。
21 | * “数值符合性”描述了表示浮点数的要求,包括数学运算的准确性。
22 |
23 | 除非另有说明,否则从Metal 1.0开始,本文档中描述的特征(例如:函数、枚举、类型或操作)可在所有OS上获得。
24 |
25 | ## 1.3 引用
26 |
27 | C++14
28 | Stroustrup, Bjarne. The C++ Programming Language. Harlow: Addison-Wesley, 2013.
29 |
30 | Metal
31 | 官方Metal文档的概述位于:[https://developer.apple.com/documentation/metal](https://developer.apple.com/documentation/metal)
32 |
33 | ## 1.4 Metal 与 C++ 14
34 | Metal 编程语言基于C++ 14规范(a.k.a., ISO/IEC JTC1/SC22/WG21 N4431 语言规范),具有特定的扩展和限制。有关语言语法的详细说明,请参与C++ 14的规范。
35 |
36 | 本节及其小节描述了对Metal支持的C++ 14语言的修改和限制。除非另有说明,否则所有OS(即iOS和macOS)都支持类型,运算符,属性和函数。
37 |
38 | 对于本文的的剩余部分,缩写vX.Y代表Metal版本X.Y;例如,V2.1表示Metal版本2.1。
39 |
40 | 有关Metal预处理指令和编译选项的更多信息,请参阅本文档的第6部分。
41 |
42 | ### 1.4.1 重载
43 |
44 | Metal支持C++ 14规范第13节定义的重载。扩展函数重载规则伊包含参数的地址空间属性。Metal图形和内核函数不能重载。(有关图形和内核函数的定义,请参阅本文档的第4.1节。)
45 |
46 | ### 1.4.2 模板
47 |
48 | Metal支持C++ 14规范第14节中定义的模板。
49 |
50 | ### 1.4.3 预处理指令
51 |
52 | Metal支持C++ 14规范地16节中定义的预处理指令。
53 |
54 | ### 1.4.4 限制
55 |
56 | 以下C++ 14特征在Metal中不可用(此列表中的部分编号参考C++ 14规范):
57 |
58 | * lambda 表达式 (第5.1.2节)
59 | * `dynamic_cast` 操作符 (第5.2.7节)
60 | * 类型识别(type identification) (第5.2.8节)
61 | * 递归函数调用 (第5.2.2节,第9项)
62 | * `new` 和 `delete` 操作符 (第5.3.4节 和 第5.3.5节)
63 | * `noexcept` 操作符 (第5.3.7节)
64 | * `goto` 语句 (第6.6节)
65 | * register thread_local 存储属性 (第7.1.1节)
66 | * 虚函数特性 (第7.1.2节)
67 | * 派生类 (第10章, 第11章)
68 | * 异常处理 (第15章)
69 |
70 | 不得在Metal代码中使用C++标准库。Metal拥有自己的标准库,而不是C++标准库,本文档的第5张对此进行了讨论。
71 |
72 | Metal限制指针的使用:
73 |
74 | * 必须使用Metal `device`、`threadgroup`、`threadgroup_imageblock` 或 `constant`地址空间属性来声明在Metal图像和内核函数中作为参数使用的指针(有关Metal地址空间属性的更多信息,请参阅本文档的第4.2节。)
75 | * 不支持函数指针
76 |
77 | Metal函数不能成为main函数。
78 |
79 | ## 1.5 Metal 像素坐标系统
80 |
81 | 在 Metal中,帧缓冲(framebuffer) 附件的像素坐标系的原点定义在左上角。
--------------------------------------------------------------------------------
/ch02.md:
--------------------------------------------------------------------------------
1 | # 2 数据类型
2 |
3 | 本章详细介绍Metal数据类型,包括代表向量(vectors)和矩阵(matrices)的类型。还讨论了原子数据类型、缓冲区(buffers)、纹理、采样器、数组和用户自定义的结构。还描述了类型对齐(Type alignment)和类型转换。
4 |
5 | ## 2.1 标量数据类型
6 | Metal 支持`表1`中罗列的标量类型,Metal 不支持 `double`, `long`, `unsigned long`, `long long`, `unsigned longlong`和 `long double` 数据类型。
7 |
8 | `表1 - Metal 标量数据类型`
9 |
10 | | 类型 | 描述 |
11 | | --- | --- |
12 | | bool | 条件数据类型,其值为true或falsee。 值 true 扩展为整数常量1,值 false 扩展为整数常量0。 |
13 | | char
int8_t | 带符号的二进制补码8位整数。 |
14 | | unsigned char
uchar
uint8_t | 无符号的8位整数 |
15 | | short
int16_t| 带符号的二进制补码16位整数 |
16 | | unsigned short
ushort
uint16_t | 无符号的16位整数 |
17 | | int
int32_t | 带符号的二进制补码32位整数 |
18 | | unsigned int
uint
uint32_t | 无符号的32位整数 |
19 | | half | 一个16位浮点。 半数据类型必须符合IEEE 754 二进制16 存储格式 |
20 | | float | 一个32位浮点。浮点数据类型必须符合IEEE 754 单精度存储格式 |
21 | | size_t| `sizeof`运算符的结果的无符号整数类型。这是一个64无符号整数 |
22 | | ptrdiff_t | 有符号整数类型,它是两个指针相减的结果。这是一个64带符号整数 |
23 | | void | void类型包含一组空值;它是一种不完整的类型 |
24 |
25 | > 注意: Metal 支持标准的 `f`或`F`后缀,以指定单精度浮点字面值(例如,0.5f或0.5F)。此外,Metal支持`h`或`H`后缀以指定半精度浮点字面值(例如,0.5h或0.5H)。Metal还支持无符号整数文字的`u`或`U`后缀。
26 |
27 | 表2列出了大多数标量数据类型的大小和对齐方式。
28 |
29 | `表2 - 标量数据类型的大小和对齐`
30 |
31 | | 类型 | 大小(字节) | 对齐方式(字节) |
32 | | --- | --- | --- |
33 | | bool | 1 | 1 |
34 | | char
int8_t
unsigned char
uchar
uint8_t | 1 | 1 |
35 | | short
int16_t
unsigned short
ushort
uint16_t | 2 | 2 |
36 | | int
int32_t
unsigned int
uint
uint32_t | 4 | 4 |
37 | | half | 2 | 2 |
38 | | float | 4 | 4 |
39 |
40 |
41 | ## 2.2 矢量和矩阵
42 |
43 | Metal支持由系统向量数学库实现的向量数据类型的子集。
44 |
45 | 支持的向量类型名称是:
46 | `booln`、`charn`、`shortn`、`intn`、`ucharn`、`ushort`、`uintn`、`halfn`和`floatn`。
47 | 其中`n`是2、3或4,表示2-、3-、4-组向量类型。表3列出了向量数据类型的大小和对齐方式。
48 |
49 | `表3 - 向量数据类型的大小和对齐`
50 |
51 | | 类型 | 大小(字节) | 对齐方式(字节) |
52 | | --- | --- | --- |
53 | | bool2 | 2 | 2 |
54 | | bool3 | 4 | 4 |
55 | | bool4 | 4 | 4 |
56 | | char2
uchar2 | 2 | 2 |
57 | | char3
uchar3 | 4 | 4 |
58 | | char4
uchar4 | 4 | 4 |
59 | | short2
ushort2 | 4 | 4 |
60 | | short3
ushort3 | 8 | 8 |
61 | | short4
ushort4 | 8 | 8 |
62 | | int2
uint2 | 8 | 8 |
63 | | int3
uint3 | 16 | 16 |
64 | | int4
uint4 | 16 | 16 |
65 | | half2 | 4 | 4 |
66 | | half3 | 8 | 8 |
67 | | half4 | 8 | 8 |
68 | | float2 | 8 | 8 |
69 | | float3 | 16 | 16 |
70 | | float4 | 16 | 16 |
71 |
72 | ### 2.2.1 访问向量组件
73 |
74 | 可以使用数组索引访问向量组件。数组索引0表示向量的第一个组件,索引1表示第二个组件,以此类推。以下示例显示了访问阵列组件的各种方法:
75 |
76 | ```metal
77 | pos = float4(1.0f, 2.0f, 3.0f, 4.0f);
78 |
79 | float x = pos[0]; // x = 1.0
80 | float x = pos[2]; // z = 3.0
81 |
82 | float4 vA = float4(1.0f, 2.0f, 3.0f, 4.0f);
83 | float3 vB;
84 |
85 | for (int i=0; i<4; i++)
86 | vB[i] = vA[i] * 2.0f // vB = (2.0, 4.0, 6.0, 8.0);
87 | ```
88 |
89 | Metal 支持使用点(.)作为选择运算符来访问向量组件,使用可以指示坐标或者颜色数据的字母:
90 |
91 | ```metal
92 | .xyzw
93 | .rgba
94 | ```
95 |
96 | 在以下的代码中,初始化向量test,然后使用.xyzw或.rgba选择语法访问组件:
97 |
98 | ```metal
99 | int4 test = int4(0, 1, 2, 3);
100 | int a = test.x; // a = 0
101 | int b = test.y; // b = 1
102 | int c = test.z; // c = 2
103 | int d = test.w; // d = 3
104 | int e = test.r; // e = 0
105 | int f = test.g; // f = 1
106 | int g = test.b; // g = 2
107 | int h = test.a; // h = 3
108 | ```
109 |
110 | 组件选择语法允许选择多个组件。
111 |
112 | ```metal
113 | float4 c;
114 | c.xyzw = float4(1.0f, 2.0f, 3.0f, 4.0f);
115 | c.z = 1.0f;
116 | c.xy = float2(3.0f, 4.0f);
117 | c.xyz = float3(3.0f, 4.0f, 5.0f);
118 | ```
119 |
120 | 组件选择语法还允许组件被置换或复制。
121 |
122 | ```metal
123 | float4 pos = float4(1.0f, 2.0f, 3.0f, 4.0f);
124 | float4 swiz = pos.wzyx; // swiz = (4.0f, 3.0f, 2.0f, 1.0f)
125 | float4 dup = pos.xxyy; // dup = (1.0f, 1.0f, 2.0f, 2.0f)
126 | ```
127 |
128 | 组件组表示可以出现在表达式的左侧。为了形成左值,可以应用混合。得到的左值可以是标量或向量类型,具体取决于指定的组件数量。生成的向量类型的左值不得包含重复的组件。
129 |
130 | ```metal
131 | float4 pos = float4(1.0f, 2.0f, 3.0f, 4.0f);
132 | // pos = (5.0, 2.0, 3.0, 6.0)
133 | pos.xw = float2(5.0f, 6.0f);
134 | // pos = (8.0, 2.0, 3.0, 7.0)
135 | pos.wx = float2(7.0f, 8.0f);
136 | // pos = (3.0, 5.0, 9.0, 7.0)
137 | pos.xyz = float3(3.0f, 5.0f, 9.0f);
138 | ```
139 |
140 | 不允许使用以下的向量组件访问方法,会导致编译时错误:
141 |
142 | * 访问超出向量类型声明的组件是错误的。2-组件向量数据类型只能访问.xy或.rg元素。3-组件向量数据类型只能访问 .xyz或.rgb元素。
143 |
144 | ```metal
145 | float2 pos;
146 | pos.x = 1.0f; // is legal; so is y
147 | pos.z = 1.0f; // is illegal; so is w
148 | float3 pos;
149 | pos.z = 1.0f; // is legal
150 | pos.w = 1.0f; // is illegal
151 | ```
152 |
153 | * 在左侧访问相同的组件两次是不明确的,是一个错误。
154 |
155 | ```metal
156 | // illegal - 'x' used twice
157 | pos.xx = float2(3.0f, 4.0f);
158 | ```
159 |
160 | * 访问不同数量的组件是错误的。
161 |
162 | ```metal
163 | // illegal - mismatch between float2 and float4
164 | pos.xy = float4(1.0f, 2.0f, 3.0f, 4.0f);
165 | ```
166 |
167 | * 在单次访问中混合.rgba和.xyzw语法是错误的。
168 |
169 | ```metal
170 | float4 pos = float4(1.0f, 2.0f, 3.0f, 4.0f);
171 | pos.x = 1.0f; // OK
172 | pos.g = 2.0f; // OK
173 | pos.xg = float2(3.0f, 4.0f); // illegal - mixed attributes used
174 | float3 coord = pos.ryz; // illegal - mixed attributes used
175 | ```
176 |
177 | * 带有 swizzles的向量的指针或引用是错误的。
178 |
179 | ```metal
180 | float4 pos = float4(1.0f, 2.0f, 3.0f, 4.0f);
181 | my_func(&pos.xy); // illegal
182 | ```
183 |
184 | 向量类型的`sizeof`运算符返回向量的大小,该大小以组件数*每个组件的大小给出。例如,`sizeof(float4)`返回16,`sizeof(half4)`返回8。
185 |
186 | ### 2.2.2 矢量构造函数
187 |
188 | 构造函数可用于从一组标量或向量创建向量。初始化向量时,其参数签名确定其构造方式。例如,如果仅使用单个标量初始化向量,则构造向量的所有组件都将设置为该标量值。
189 |
190 | 如果向量是由多个标量,一个或多个向量或这些向量的混合构成的,则从参数的组成部分开始按顺序构造向量的组件。参数从左到右消耗。在消耗下一个参数的任何组件之前,每个参数都按顺序使用其所有组件。
191 |
192 | 这是一个可用于`float4`的构造函数的完整列表:
193 |
194 | ```metal
195 | float4(float x);
196 | float4(float x, float y, float z, float w);
197 | float4(float2 a, float2, b);
198 | float4(float a, float b, float2 c);
199 | float4(float a, float2 b, float c);
200 | float4(float3 a, float b);
201 | float4(float a, float3 b);
202 | float4(float4 x);
203 | ```
204 |
205 | 这是一个可用于`float3`的构造函数的完整列表:
206 |
207 | ```metal
208 | float3(float x);
209 | float3(float x, float y, float z);
210 | float3(float a, float2 b);
211 | float3(float2 a, float b);
212 | float3(float3 x);
213 | ```
214 |
215 | 这是一个可用于`float2`的构造函数的完整列表:
216 |
217 | ```metal
218 | float2(float x);
219 | float2(float x, float y);
220 | float2(float2 x);
221 | ```
222 |
223 | 以下示例说明了构造函数的用法:
224 |
225 | ```metal
226 | float x = 1.0f, y = 2.0f, z = 3.0f, w = 4.0f;
227 | float4 a = float4(0.0f);
228 | float4 b = float4(x, y, z, w);
229 | float2 c = float2(5.0f, 6.0f);
230 |
231 | float2 a = float2(x, y);
232 | float2 b = float2(z, w);
233 | float4 x = float4(a.xy, b.xy);
234 | ```
235 |
236 | 初始化向量构造函数是一个编译时错误。
237 |
238 |
239 | ### 2.2.3 打包向量类型
240 |
241 | 2.2节中描述的向量数据类型和向量大小对齐方式。开发人员还可以要求他们的向量数据紧密包装。例如,顶点结构可以包含紧密包装的位置、法线、切线向量和纹理坐标,并作为缓冲区传递给顶点函数。
242 |
243 | 支持的打包向量类型名称是:
244 | `packed_charn`、`packed_shortn`、`packed_intn`、`packed_ucharn`、`packed_ushortn`、`packed_uintn`、`packed_halfn`和`packed_floatn`。
245 | 其中`n`是2、3或4,分别代表2-、3-或4-组分向量类型。(`packed_booln`向量类型名称是保留的。)
246 |
247 | 表4列出了打包向量数据类型的大小和对齐方式。
248 |
249 | | 类型 | 大小(字节) | 对齐方式(字节) |
250 | | --- | --- | --- |
251 | | packed_char2,
packed_uchar2 | 2 | 1 |
252 | | packed_char3,
packed_uchar3 | 3 | 1 |
253 | | packed_char4,
packed_uchar4 | 4 | 1 |
254 | | packed_short2,
packed_ushort2 | 4 | 2 |
255 | | packed_short3,
packed_ushort3 | 6 | 2 |
256 | | packed_short4,
packed_ushort4 | 8 | 2 |
257 | | packed_int2,
packed_uint2 | 8 | 4 |
258 | | packed_int3,
packed_uint3 | 12 | 4 |
259 | | packed_int4,
packed_uint4 | 16 | 4 |
260 | | packed_half2 | 4 | 2 |
261 | | packed_half3 | 6 | 2 |
262 | | packed_half4 | 8 | 2 |
263 | | packed_float2 | 8 | 4 |
264 | | packed_float3 | 12 | 4 |
265 | | packed_float4 | 16 | 4 |
266 |
267 | 打包的向量数据类型通常用作数据存储格式。从打包的向量数据类型加载和存储到对齐的向量数据类型,反之亦然。支持复制构造函数和赋值运算符。打包向量数据类型也支持算术、逻辑和关系运算符。
268 |
269 | 例如:
270 |
271 | ```metal
272 | device float4 *buffer;
273 | device packed_float4 *packed_buffer;
274 | int i;
275 | packed_float4 f ( buffer[i] );
276 | pack_buffer[i] = buffer[i];
277 | // operator to convert from packed_float4 to float4
278 | buffer[i] = float4( packed_buffer[i] );
279 | ```
280 |
281 | 可以使用数组索引访问打包向量数据类型的组件。但是无法使用`.xyzw`或`.rgba`选择语法访问打包向量数据类型的组件。
282 |
283 | 例如:
284 |
285 | ```metal
286 | packed_float4 f;
287 | f[0] = 1.0f; // OK
288 | f.x = 1.0f; // Illegal - compilation error
289 | ```
290 |
291 | ## 2.3 矩阵数据类型
292 |
293 | Metal支持由系统数学库实现的矩阵数据类型的子集。支持的矩阵类型名称是:
294 | `halfnxm` 和 `floatnxm`
295 | 其中`n`和`m`是列数和行数。`n`和`m`必须是2、3或4。`floatnxm`类型的矩阵由`n`个`floatm`向量组成。类似的,`halfnxm`类型的矩阵由`n`个半向量组成。
296 |
297 | 表5列出了矩阵数据类型的大小和对齐方式。
298 |
299 | | 类型 | 大小(字节) | 对齐方式(字节) |
300 | | --- | --- | --- |
301 | | half2x2 | 8 | 4 |
302 | | half2x3 | 16 | 8 |
303 | | half2x4 | 16 | 8 |
304 | | half3x2 | 12 | 4 |
305 | | half3x3 | 24 | 8 |
306 | | half3x4 | 24 | 8 |
307 | | half4x2 | 16 | 4 |
308 | | half4x3 | 32 | 8 |
309 | | half4x4 | 32 | 8 |
310 | | float2x2 | 16 | 8 |
311 | | float2x3 | 32 | 16 |
312 | | float2x4 | 32 | 16 |
313 | | float3x2 | 24 | 8 |
314 | | float3x3 | 48 | 16 |
315 | | float3x4 | 48 | 16 |
316 | | float4x2 | 32 | 8 |
317 | | float4x3 | 64 | 16 |
318 | | float4x4 | 64 | 16 |
319 |
320 | ### 2.3.1 访问矩阵组件
321 |
322 | 可以使用数组下标语法访问矩阵的组件。将单个下表应用于矩阵将矩阵视为列向量的数组。两个下标选择一列,然后选择一行。顶部列是第0列,然后第2个下标对结果向量进行操作,如前面对向量所定义的那样。
323 |
324 | ```metal
325 | float4x4 m;
326 | // sets the 2nd column to all 2.0
327 | m[1] = float4(2.0f);
328 | // sets the 1st element of the 1st column to 1.0
329 | m[0][0] = 1.0f;
330 | // sets the 4th element of the 3rd column to 3.0
331 | m[2][3] = 3.0f;
332 | ```
333 |
334 | `floatnxm`和`halfnxm`矩阵可以作为n个`floatm`或n个`halfm`条目的数组进行访问。
335 |
336 | 使用非常量表达式访问矩阵边界之外的组件会导致未定义的行为。使用常量表达式访问矩阵边界之外的矩阵组件会导致编译时错误。
337 |
338 | ### 2.3.2 矩阵构造函数
339 |
340 | 构造函数可用于从一组标量、向量或者矩阵创建矩阵。初始化矩阵时,其参数签名决定了它的构造函数。例如,如果仅使用单个标量初始化矩阵,则结果是包含矩阵对角线的所有分量的标量的矩阵,其余分量初始化为0.0。例如调用
341 | `float4x4(fval);`
342 | 其中`fval`是标量浮点值,用这些初始内容构造一个矩阵:
343 |
344 | ```metal
345 | fval 0.0 0.0 0.0
346 | 0.0 fval 0.0 0.0
347 | 0.0 0.0 fval 0.0
348 | 0.0 0.0 0.0 fval
349 | ```
350 |
351 | 矩阵也可以由具有相同大小的另一个矩阵构成;即,具有相同数量的行和列。例如,
352 |
353 | ```metal
354 | float3x4(float3x4);
355 | float3x4(half3x4);
356 | ```
357 |
358 | 矩阵组件按列主要顺序构建和使用。矩阵构造函数必须在其参数中指定足够的值,以初始化构造的矩阵对象中的每个组件。提供多余必要的参数会导致错误。初始化矩阵构造函数会导致编译时错误。
359 |
360 | 具有n列和m行的类型T的矩阵也可以由具有m个分量的类型T的n个向量构成。以下示例是合法的构造函数:
361 |
362 | ```metal
363 | float2x2(float2, float2);
364 | float3x3(float3, float3, float3);
365 | float3x2(float2, float2, float2);
366 | ```
367 |
368 | 从Metal v2.0开始,具有n列和m行的类型T的矩阵也可以由类型为T的n*m个标量构成。以下示例是合法的构造函数:
369 |
370 | ```metal
371 | float2x2(float, float, float, float);
372 | float3x2(float, float, float, float, float, float);
373 | ```
374 |
375 | 以下是不受支持的矩阵构造函数的示例。不能从向量和标量的组合构造矩阵。
376 |
377 | ```metal
378 | // not supported
379 | float2x3(float2 a, float b, float2 c, float d);
380 | ```
381 |
382 | ## 2.4 数据类型对齐方式
383 |
384 | `alignas`对齐说明符可用于指定类型或对象的对齐要求。`alignas`说明符可以应用于变量的声明结构或类的数据成员。它也可以应用于struct、class、或 enumeration 类型的声明。
385 |
386 | Metal编译器负责根据数据类型的要求将数据项对齐到适当的对齐方式。对于声明为数据类型指针的图形或内核函数的参数,Metal编译器可以假定指针对象始终根据数据类型的要求进行适当的对齐。
387 |
388 | ## 2.5 原子数据类型
389 |
390 | 原子类型的对象是唯一没有数据竞争的Metal着色语言对象。如果一个线程写入原子对象而另一个线程从中读取,则行为是明确定义的。
391 |
392 | 支持如下这些原子类型:
393 |
394 | * `atomic_int` 所有的系统,Metal 1.0 以上版本
395 | * `atomic_uint` 所有的系统,Metal 1.0 以上版本
396 | * `atomic_bool` iOS系统 Metal 2.0 以上版本;macOS 不支持
397 | * `atomic` iOS系统 Metal 2.0 以上版本;macOS 不支持
398 |
399 | `atomic` 表示模板化类型,其中T可以是`int`、`uint`或`bool`。
400 |
401 | Metal原子数据类型仅限于Metal原子功能使用,如第5.13节所述。这些原子函数是C++ 14原子和同步函数的子集。
402 |
403 | ## 2.6 像素数据类型(Pixel Data Types)
404 |
405 | > 所有操作系统:自v2.0后支持像素数据类型。
406 |
407 | Metal像素数据类型是模板化类型,其描述像素格式类型及其对应的ALU类型。ALU类型表示加载操作返回的类型以及为存储操作指定的输入类型。像素数据类型通常在所有地址空间中可用(有关地址空间的详细信息,请参阅第4.2节)。
408 |
409 | 表6列出了Metal着色语言中支持的像素数据类型,以及它们的大小和对齐方式。
410 |
411 | | 像素数据类型 | T的可选类型 | 大小(字节) | 大小(字节) |
412 | | --- | --- | --- | --- |
413 | | r8unorm | half 或 float | 1 | 1 |
414 | | r8snorm | half 或 float | 1 | 1 |
415 | | r16unorm | float | 2 | 2 |
416 | | r16snorm | float | 2 | 2 |
417 | | rg8unorm | half2 或 float2 | 2 | 1 |
418 | | rg8snorm | half2 或 float2 | 2 | 1 |
419 | | rg16unorm | float2 | 4 | 2 |
420 | | rg16snorm | float2 | 4 | 2 |
421 | | rgba8unorm | half4 或 float4 | 4 | 1 |
422 | | srgba8unorm | half4 或 float4 | 4 | 1 |
423 | | rgba8snorm | half4 或 float4 | 4 | 1 |
424 | | rgba16unorm | float4 | 8 | 2 |
425 | | rgba16snorm | float4 | 8 | 2 |
426 | | rgb10a2 | half4 或 float4 | 4 | 4 |
427 | | rg11b10f | half3 或 float3 | 4 | 4 |
428 | | rgb9e5 | half3 或 float3 | 4 | 4 |
429 |
430 | 仅允许像素数据类型与其对应的ALU类型之间的分配和等式/不等式比较。(下面的代码中出现的 `buffer(n)`属性将在4.3.1节中解释)
431 |
432 | 例子:
433 |
434 | ```metal
435 | kernel void
436 | my_kernel(device rgba8unorm *p [[buffer(0)]],
437 | uint gid [[thread_position_in_grid]], …)
438 | {
439 | rgba8unorm x = p[index]; half4 val = p[gid];
440 | …
441 | p[gid] = val;
442 | p[index] = x;
443 | }
444 | ```
445 |
446 | 例子:
447 |
448 | ```metal
449 | struct Foo {
450 | rgba8unorm a;
451 | };
452 |
453 | kernel void
454 | my_kernel(device Foo *p [[buffer(0)]],
455 | uint gid [[thread_position_in_grid]], …)
456 | {
457 | half4 a = p[gid].a;
458 | …
459 | p[gid].a = a;
460 | }
461 |
462 | ```
463 |
464 | ## 2.7 缓冲区(Buffers)
465 |
466 | Metal 将缓存区实现为指向 `device`、`threadgroup`或`constant`地址空间中描述的内置或者用户定义数据类型的指针(有关这些地址属性的完成说明,请参阅第4.2节)。这些缓存区可以在程序范围内声明,也可以作为参数传递给函数。
467 |
468 | 例如:
469 |
470 | ```metal
471 | device float4 *device_buffer;
472 |
473 | struct my_user_data {
474 | float4 a;
475 | float b;
476 | int2 c;
477 | };
478 |
479 | constant my_user_data *user_data;
480 | ```
481 |
482 | 普通Metal缓冲区可以包含:
483 |
484 | * 基本类型,如`float`和`int`
485 | * 向量和矩阵类型
486 | * 缓冲区类型的数组
487 | * 缓冲区类型的结构
488 | * 缓冲区类型的联合
489 |
490 | 有关把缓冲区作为参数,请参阅第2.12节。
491 |
492 | ## 2.8 纹理(Texture)
493 |
494 | 纹理数据类型是一维、二维、三维纹理数据的句柄,其对应纹理的单个 mipmap级别的全部或一部分。以下模板定义特定的纹理数据类型:
495 |
496 | ```metal
497 | enum class access { sample, read, write, read_write };
498 | texture1d
499 | texture1d_array
500 | texture2d
501 | texture2d_array
502 | texture3d
503 | texturecube
504 | texturecube_array
505 | texture2d_ms
506 | ```
507 |
508 | 必须将具有深度格式的纹理声明为以下纹理数据类型之一:
509 |
510 | ```metal
511 | depth2d
512 | depth2d_array
513 | depthcube
514 | depthcube_array
515 | depth2d_ms
516 | ```
517 |
518 | `T`指定了从纹理中读取时返回的组件之一的颜色类型或写入纹理时指定的组件之一的颜色类型。对于纹理类型(深度类型除外),`T`可以是`half`、`float`、`short`、`ushort`、`int` 或者 `uint`。对于深度纹理类型,`T`必须是`float`。
519 |
520 | > 注意:如果`T`是 `int`或者`short`,则与纹理关联的数据必须使用带符号的整数格式。如果`T`是`uint`或者是`ushort`,则与纹理关联的数据必须使用无符号的整数格式。如果`T`是`half`,则与纹理关联的数据必须使用归一化(normalized)的(有符号或者无符号的整数)或者半精度格式。如果`T`是`float`,与纹理关联的数据必须使用归一化(normalized)的(带符号或者无符号的整数)或者单精度格式。
521 |
522 | `access`属性描述了如何访问纹理。支持的访问属性是:
523 |
524 | * `sample` - 可以对纹理对象进行采样。`sample`表示使用和不使用采样器从纹理读取的能力。
525 | * `read` - 没有采样器,图形或内核函数只能读取纹理对象。
526 | * `write` - 图形或内核函数可以写入纹理对象。
527 | * `read_write` - 图形或内核函数可以读写纹理对象。
528 |
529 | > 所有的系统: `read_write` 访问权限从Metal 1.2 开始支持。而其他的访问权限从Metal 1.0就开始支持。
530 |
531 | > 注意:对于多重采样纹理,仅支持读取属性。对于深度纹理,只能使用 `sample`和`read`修饰符。
532 |
533 | 以下示例将访问限定符与纹理对象参数一起使用。
534 |
535 | ```metal
536 | void foo (texture2d imgA [[texture(0)]],
537 | texture2d imgB [[texture(1)]],
538 | texture2d imgC [[texture(2)]])
539 | {…}
540 | ```
541 | (有关纹理属性的说明,请参与第4.3.1节。)
542 |
543 | 纹理类型也可以用作函数内声明任何变量的变量类型。在函数内声明的纹理类型变量的限定符只能使用`acess::read`或者 `access::sample`。在函数方法体中如果不用`acess::read`或`access::sample`修饰符来定义纹理类型会报编译错误。
544 |
545 | 例子:
546 |
547 | ```metal
548 | void foo (texture2d imgA [[ texture(0) ]],
549 | texture2d imgB [[ texture(1) ]],
550 | texture2d imgC [[ texture(2) ]])
551 | {
552 | texture2d x = imgA; // legal
553 | texture2d y = imgB; // legal
554 | texture2d z; // illegal
555 | ...
556 | }
557 | ```
558 |
559 | ### 2.8.1 纹理Buffers
560 |
561 | 所有的系统:纹理Buffers 在Metal2.1开始支持。
562 |
563 | 纹理缓冲区是一种纹理类型,可以访问大型像素数据的一维数组,并在具有优化性能的数据上执行像素格式之间的动态类型转换。纹理缓冲区比其他技术更有效地处理类型转换,允许访问更大的元素,并处理越界读访问。也可以在没有纹理缓冲区的情况下实现类似的类型转换:
564 |
565 | * 从纹理对象读取像素数据(就像任何其数组一样)并执行像素转换为所需格式,或者
566 | * 将纹理对象包裹在缓冲区对象的数据周围,然后通过纹理访问共享缓冲区数据。(这种包装技术提供了像素转换,但需要额外的处理步骤,并且纹理的大小是有限制的。)
567 |
568 | 如下模板定义了不透明类型`texture_buffer`,它与任何纹理类型一样使用:
569 |
570 | ```metal
571 | texture_buffer
572 | ```
573 |
574 | `access`可以是`read`、 `write`或者 `read_write`。
575 | `T`指定从纹理缓冲区读取时返回的组件类型或写入纹理缓冲区时指定的组件类型。对于纹理缓冲区,`T`可以是`half`、`float`、`short`、`ushort`、`int`或`uint`之一。
576 |
577 | 对于没有alpha同道的格式(例如,R、RG或RGB),越界读取返回 (0, 0, 0, 1)。对于具有alpha通道(例如,RGBA)的格式,越界读取返回(0, 0, 0, 0)。对于某些设备,越界读取可能会降低性能。
578 |
579 | 越界写入将被忽略。
580 |
581 | 纹理缓冲区可以支持比通用一维纹理更多的纹理数据,通用一维纹理限制为16384的宽度。但是,无法对纹理缓冲区进行采样。
582 |
583 | 纹理缓冲区还可以转换数据,以请求的纹理格式传送数据,而不管源的格式如何。创建纹理缓冲区市,可以指定缓冲区中的数据格式(例如,RGBA8Unorm),之后着色器函数可以将其作为转换类型(例如float64)读取。因此,单个管道状态对象可以访问以不同像素格式存储的数据,而无需重新编译。
584 |
585 | 纹理缓冲区(如纹理类型)可以声明为着色器函数的局部变量类型。
586 |
587 | 有关纹理缓冲区数组,请参阅第2.11.1节。有关纹理缓冲函数的详细信息,请参见第5.10.14节。
588 |
589 |
590 | ## 2.9 采样器
591 |
592 | `sampler`类型标识了如何对纹理进行采样。Metal API允许您创建一个采样器对象,并将其作为参数传给图形或者内核函数。采样器对象也可以在程序源中而不是在API中描述。对于这些情况,我们只允许指定采样器状态的子集:寻址模式、滤波器模式、归一化坐标系和比较函数。
593 |
594 | 表7描述了支持的采样器状态枚举以及关联(和默认值)的列表。在Metal程序源中初始化采样器时,可以指定这些状态。
595 |
596 | | 枚举名称 | 可取值 | 描述 |
597 | | --- | --- | --- |
598 | | coord | normalized(默认值)
pixel | 指定从纹理采样时纹理坐标是否为归一化值。 |
599 | | address | repeat
mirrored_repeat
clamp_to_edge(默认值)
clamp_to_zero
clamp_to_border | 设置所有纹理坐标的寻址模式 |
600 | | s_address
t_address
r_address | repeat
mirrored_repeat
clamp_to_edge(默认值)
clamp_to_zero
clamp_to_border | 设置各个纹理坐标的寻址模式 |
601 | | border_color | transparent_black (默认值)
opaque_black
opaque_white | 指定与`clamp_to_border`寻址模式一起使用的边框颜色 |
602 | | filter | nearest(默认值)
linear | 设置纹理采样时放大和缩小过滤模式 |
603 | | mag_filter | nearest(默认值)
linear | 设置纹理采样时的放大滤波模式 |
604 | | min_filter | nearest(默认值)
linear | 设置纹理采样时的缩小滤波模式 |
605 | | mip_filter | none(默认值)
nearest
linear | 设置纹理采样时的mipmap过滤模式。如果为`none`,那么只有一个细节级别是活动的 |
606 | | compare_func | never(默认值)
less
less_equal
greater
greater_equal
equal
not_equal
always | 设置`smaple_compare`和`gather_compare`纹理函数使用的比较测试 |
607 |
608 | > macOS: `clamp_to_border`地址模式和`border_color`从v1.2开始支持。
609 | > iOS: 不支持 `clamp_to_border` 地址模式和`border_color`。
610 |
611 | 使用 `clamp_to_border`,纹理外部的采样仅使用纹理坐标的边框颜色(并且不使用纹理边缘的任何颜色)。如果地址模式为 `clamp_to_border`,则`border_color`有效。
612 |
613 | `clamp_to_zero`相当于`clamp_to_border`,边框颜色为 `transparent_black` (0.0, 0.0, 0.0),有纹理alpha分量值。如果将`clamp_to_zero`指定为一个或多个纹理坐标的地址模式,则当且仅当边框颜色为`transparent_black`时,其他纹理坐标可以使用`clamp_to_border`的地址模式。否则行为未定义。
614 |
615 | 如果`coord`设置为`pixel`,则`min_filter`和`mag_filter`值必须相同,`mip_filter`值必须为`none`,地址模式必须为`clamp_to_zero`、`clamp_to_border`或`clamp_to_edge`。
616 |
617 | 除了枚举类型之外,还可以使用采样器指定以下类型:
618 |
619 | ```metal
620 | max_anisotropy(int value)
621 | lod_clamp(float min, float max)
622 | ```
623 |
624 |
625 | 以下Metal程序源说明了几种声明采样器的方法。(以下代码中出现的 `sampler(n)`属性在4.3.1节中进行了解释。)请注意,在程序源中声明的采样器或常量缓冲区不需要这些属性限定符。在Metal着色语言源中初始化的采样器使用`constexpr`声明。
626 |
627 | ```metal
628 | constexpr sampler s(coord::pixel,
629 | address::clamp_to_zero,
630 | filter::linear);
631 |
632 | constexpr sampler a(coord::normalized);
633 |
634 | constexpr sampler b(address::repeat);
635 |
636 | constexpr sampler s(address::clamp_to_zero,
637 | filter::linear,
638 | compare_func::less);
639 |
640 | constexpr sampler s(address::clamp_to_zero,
641 | filter::linear,
642 | compare_func::less,
643 | max_anisotropy(10),
644 | lod_clamp(0.0f, MAXFLOAT));
645 |
646 | kernel void
647 | my_kernel(device float4 *p [[buffer(0)]],
648 | texture2d img [[texture(0)]],
649 | sampler smp [[sampler(3)]],
650 | …)
651 | {
652 | …
653 | }
654 | ```
655 |
656 |
657 | ## 2.10 图像块(Imageblocks)
658 |
659 | > iOS: imageblock Metal 2.0 以上支持
660 | > macOS: 不支持 imageblocks
661 |
662 | 图像块(Imageblocks)是在线程存储器中分配的二维数据结构(由宽度、高度和样本数表示),被设计为用于处理二维图像数据的有效机制。图像块的数据布局是不透明的。可以使用(x, y)坐标和可选的样本索引来访问图像块中的元素。与特别的 (x, y)相关联的图像块中的元素被称为每线程图像块数据或仅称为图像块数据。
663 |
664 | 图像块仅用于片段(fragment)和内核函数。
665 |
666 |
667 |
668 | | 像素存储类型 | 兼容的纹理格式 |
669 | | --- | --- |
670 | | `float`, `half`| R32Float, R16Float, A8Unorm, R8Unorm, R8Snorm, R16Unorm, R16Snorm |
671 | | float2, half2 | RG32Float, RG16Float, RG8Unorm, RG8Snorm, RG16Unorm, RG16Snorm |
672 | | float4, half4 | RGBA32Float, RGBA16Float, RGBA8Unorm, RGBA8Snorm, RGBA16Unorm, RGBA16Snorm, RGB10A2Unorm, RG11B10Float, RGB9E5Float |
673 | | int, short | R32Sint, R16Sint, R8Sint |
674 | | int2, short2 | RG32Sint, RG16Sint, RG8Sint |
675 | | int4, short4 | RGBA32Sint, RGBA16Sint, RGBA8Sint |
676 | | uint, ushort | R32Uint, R16Uint, R8Uint |
677 | | uint2, ushort2 | RG32Uint, RG16Uint, RG8Uint |
678 | | uint4, ushort4 | RGBA32Uint, RGBA16Uint, RGBA8Uint |
679 | | r8unorm | A8Unorm, R8Unorm |
680 | | r8snorm | R8Snorm |
681 | | r16unorm | R16Unorm |
682 | | r16snorm | R16Snorm |
683 | | rg8unorm | RG8Unorm |
684 | | rg8snorm | RG8Snorm |
685 | | rg16unorm | RG16Unorm |
686 | | rg16snorm | RG16Snorm |
687 | | rgba8unorm | RGBA8Unorm, BGRA8Unorm |
688 | | srgba8unorm | RGBA8Unorm_sRGB, BGRA8Unorm_sRGB |
689 | | rgba8snorm | RGBA8Snorm, BGRA8Unorm |
690 | | rgba16unorm | RGBA16Unorm |
691 | | rgba16snorm | RGBA16Snorm |
692 | | rgb10a2 | RGB10A2Unorm |
693 | | rg11b10f | RG11B10Float |
694 | | rgb9e5 | RGB9E5Float |
695 |
696 | 第 2.10.1节 和 2.10.2 节分别描述了如何在片段和内核函数访问图像块。
697 |
698 | ### 2.10.1 片段函数中的图像块
699 |
700 | 在片段函数中,可以通过两种方式访问图像块:
701 |
702 | * 作为颜色附件,其中图像块的存储布局在片段函数(隐式图像块布局)中是未知的。隐式图像块布局使用现有的颜色附件属性。见 2.10.1.1节
703 | * 或者当在片段函数(显式图像块布局)中明确指定图像块的存储布局时,用作声明图像块数据的结构。见 2.10.1.2节。
704 |
705 | #### 2.10.1.1 片段函数的隐式图像块布局
706 |
707 | 可以在片段函数中访问图像块数据(即,与像素相关联的图像块中的所有数据成员)。Metal创建一个隐式图像块,它匹配颜色附件(用于输入和从片段函数输出)的行为。在该模式中,与片段函数中描述的颜色附件相关联的类型是ALU类型(即用于在片段函数中执行计算的类型)。Metal运行时定义要使用的实际存储(即像素)格式。
708 |
709 | 当作为颜色附件访问图像块数据中,无法在图像块切片结构中声明2.6节描述的像素存储类型。
710 |
711 | 对于类型`T`类型的图像块数据隐式布局,`T`是一个结构,其中每个成员满足以下之一:
712 |
713 | * 有颜色附件(参见4.3.4节 表12 中的 `[[color(m)]]`属性)。对于`T`的每个成员(和子成员),颜色索引`m`必须是唯一的。
714 | * 是一个结构类型,其成员满足列表中的约束。
715 |
716 | #### 2.10.1.2 片段函数的显式图像块布局
717 |
718 | 具有显式布局的图像块数据(即,图像块布局在着色函数中声明,而不是通过运行时对颜色附件进行声明)被声明为结构。
719 |
720 | 每个片段图像块数据的每个成员可以是标量或向量整数或浮点数据类型、2.6节中描述的像素数据类型之一、这些类型的数组或者用这些类型构建的结构。图像块结构的数据成员对结构中声明的每个数据成员类型使用适当的对齐规则,以确定实际的结构布局和大小。
721 |
722 | 片段函数可以读取每片图像块数据中的一个或多个数据成员,并写入每个片端图像块数据中的一个或多个数据成员。可以将片段函数的输入和输出的图像块声明为结构。输入和输出图像块结构可以是完全显式的图像块结构(称为主显式图像块结构)或者是主显式图像块结构的子集(称为图像块视图结构)。对于后一种情况,`[[imageblock_data(type)]]`属性必须与片段函数上指定的输入和输出图像块数据结构一起使用,其中`type`指定完全显式的图像块数据结构。
723 |
724 | 如果在没有类型的输入参数或输出结构元素上指定`[[imageblock_data(type)]]`属性,则假定片段函数在输入或输出上使用主显式图像块数据结构。
725 |
726 | 例如:
727 |
728 | ```metal
729 | struct I {
730 | float a [[raster_order_group(0)]];
731 | };
732 |
733 | struct FragOut {
734 | float c [[color(0)]];
735 | I i [[imageblock_data]];
736 | };
737 |
738 | fragment FragOut
739 | my_fragment(I i [[imageblock_data]])
740 | {
741 | FragOut fragOut;
742 | ...
743 | return fragOut;
744 | }
745 | ```
746 |
747 |
748 | 例如:
749 |
750 | ```metal
751 | struct I {
752 | float a [[raster_order_group(0)]];
753 | };
754 |
755 | struct FragOut {
756 | float c [[color(0)]];
757 | I i [[imageblock_data]];
758 | };
759 |
760 | fragment FragOut
761 | my_fragment(I i [[imageblock_data]],
762 | float c [[color(0)]])
763 | {
764 | FragOut fragOut;
765 | ...
766 | return fragOut;
767 | }
768 |
769 | ```
770 |
771 | 默认情况下,显式图像块存储和隐式图像块的存储分开。要在显式图像块和隐式图像块之间共享存储,请参加第4.7.3节。
772 |
773 | ### 2.10.2 内核函数的图像块
774 |
775 | `imageblock`类型(在头文件中定义)只能用于在内核函数或内核函数调用的用户函数中声明的参数。只有内核函数可以将参数声明为`imageblock`类型。图像块中的数据仅对线程组中的线程可见。
776 |
777 | 内核函数的图像块参数声明为一下模板化类型:
778 |
779 | ```metal
780 | class imageblock_layout_explicit;
781 | class imageblock_layout_implicit;
782 | template struct imageblock;
783 | ```
784 |
785 | 有如下的限制:
786 |
787 | - `L`是`imageblock_layout_explicit`或`imageblock_layout_implicit`之一
788 | - `T`是一个结构
789 | - `T`的每个成员可以是一下的任何一个:
790 | - 标量
791 | - 向量或打包向量
792 | - 像素数据类型
793 | - 一个数组,其元素是此列表中的一种类型
794 | - 一个结构,结构的成员是此列表中的类型之一
795 |
796 | 对于具有隐式布局的图像块(imageblock_layout_implicit),结构的每个成员可以具有颜色附件(参见4.3.4节的表12中的`[[color(m)]]`属性)。对于`T`的每个成员(和子成员),颜色索引`m`必须是唯一的。
797 |
798 | 如果用户未指定图像块布局,则编译器基于`T`推断布局。如果`T`与隐式或显式图像块不兼容,则导致编译器错误。
799 |
800 | 显式和隐式图像块都可以作为参数传递给内核函数。这也使得在片段函数和内核函数之间共享显式和隐式图像块结构变得容易。默认情况下,显式图像块存储与隐式图像块的存储分开。要在显式图像块和隐式图像块之间共享存储,请参加第4.7.3节。
801 |
802 | ## 2.11 聚合类型
803 |
804 | Metal支持多种聚合类型:数组(arrays)、结构(structs)、类(classes)和联合(unions)。
805 |
806 | 除非成员是指针类型,否则不要指定具有地址空间属性的结构成员。聚合类型的所有成员必须属于同一地址空间。(有关地址空间的详细信息,请参阅第4.2节。)
807 |
808 | ### 2.11.1 纹理、纹理缓冲区和采样器阵列
809 |
810 | > iOS: 自v1.2支持纹理数组;自v2.0支持采样器阵列;自v2.1支持纹理缓冲区数组。
811 | > macOS: 自v2.0支持纹理数组;自v2.0支持采样器阵列;自v2.1支持纹理缓冲区数组。
812 |
813 | 纹理数据可以声明为:
814 |
815 | ```metal
816 | array
817 | const array
818 | ```
819 |
820 | `typename`应该使用 `access::red`或`access::sample`属性的纹理类型(参见第2.8节)。
821 |
822 | 一组带有 `access::read`限定符的纹理缓冲区数组(参见第2.8.1节)可以这样构造:
823 |
824 | ```metal
825 | array, size t N>
826 | ```
827 |
828 | 一组采样器可以声明为:
829 |
830 | ```metal
831 | array
832 | const array
833 | ```
834 |
835 | 纹理数组或采样器数组可以作为参数传递给函数(图形、内核或用户函数),或者在函数内声明为局部变量。也可以在程序范围内声明一组采样器。除非在参数缓冲区(参见第2.12节)中使用,否则不能在结构中声明用于纹理、纹理缓冲区或采样器的 `array`类型。
836 |
837 | Metal着色语言还增加了对 `array_ref`的支持。`array_ref`表示类型为`T`的`size()`元素的不可变数组。`T`必须是采样器类型或支持的纹理类型,包括纹理缓冲区。数组的存储不归`array_ref`对象所有。隐式转换是从具有连续迭代器(如`metal::array`)的类型提供的。`array_ref`的一个常见用途是将纹理数组作为参数传给函数,以便它们可以接受各种数组类型。
838 |
839 | `array_ref`类型不能作为参数传递给图形和内核函数。但是`array_ref`类型可以作为参数传递给用户函数。`array_ref`类型不能在函数内声明为局部变量。
840 |
841 | 第2.11.1.1节到第2.11.1.3节中列出的成员函数可用于纹理数组、采样器数组和`array_ref`类型:
842 |
843 | #### 2.11.1.1 使用[]运算符访问数组元素
844 |
845 | 可以使用`[]`运算符访问纹理、纹理缓冲区和采样器数组的元素:
846 |
847 | ```metal
848 | reference operator[] (size_t pos);
849 | ```
850 |
851 | 可以使用一下`[]`运算符的变体访问纹理、纹理缓冲区和采样器数组或模板化类型 `array_ref`的元素:
852 |
853 | ```metal
854 | constexpr const_reference operator[] (size_t pos) const;
855 | ```
856 |
857 | #### 2.11.1.2 数组容量
858 |
859 | `size()`返回纹理、纹理缓冲区或采样器数组的元素个数。
860 |
861 | ```metal
862 | constexpr size_t size();
863 | constexpr size_t size() const;
864 | ```
865 |
866 | 例如
867 |
868 | ```metal
869 | kernel void
870 | my_kernel(const array, 10> src [[texture(0)]],
871 | texture2d dst [[texture(10)]],
872 | ...)
873 | {
874 | for (int i=0; i
892 | constexpr array_ref(const T(&a)[N]);
893 |
894 | template
895 | constexpr array_ref make_array_ref(const T * array, size_t length)
896 |
897 | template
898 | constexpr array_ref make_array_ref(const T(&a)[N])
899 | ```
900 |
901 | 构造数组的示例:
902 |
903 | ```metal
904 | float4 foo(array_ref> src)
905 | {
906 | float4 clr(0.0f);
907 | for (int i=0; i, 10> src [[texture(0)]],
916 | texture2d dst [[texture(10)]],
917 | ...)
918 | {
919 | float4 clr = foo(src);
920 | ...
921 | }
922 |
923 | kernel void
924 | my_kernel_B(const array, 20> src [[texture(0)]],
925 | texture2d dst [[texture(10)]],
926 | ...)
927 | {
928 | float4 clr = foo(src);
929 | ...
930 | }
931 |
932 | ```
933 |
934 | 下面是在程序范围内声明的采样器数组的示例:
935 |
936 | ```metal
937 | constexpr array = { sampler(address::clamp_to_zero), sampler(coord::pixel) };
938 | ```
939 |
940 | ## 2.12 参数缓冲区
941 |
942 | > 所有操作系统: 自v2.0支持参数缓冲区
943 |
944 | ```metal
945 | struct Foo {
946 | texture2d a;
947 | depth2d b;
948 | sampler c;
949 | texture2d d;
950 | device float4* e;
951 | texture2d f;
952 | texture_buffer g;
953 | int h;
954 | };
955 |
956 | kernel void
957 | my_kernel(constant Foo & f [[buffer(0)]])
958 | {...}
959 | ```
960 |
961 | 可以使用现有的`array`模板化类型声明纹理和采样器数组。所有其他合法缓冲区类型的数组也可以使用C风格的数组语法声明。
962 |
963 | 可以为参数缓冲区的成员分配通用`[[id(n)]]`属性,其中`n`是32位无符号整数,可用于从Metal API中标识缓冲区元素。如果参数缓冲区包含缓冲区、纹理、采样器或者具有`[[id]]`属性的任何元素,则可以将它与常规缓冲区区分开来。
964 |
965 | 可能不会将相同的索引分配给参数缓冲区的多个成员。手动分配的指数不需要是连续的,但它们必须是单调递增。在下面的示例中,索引0自动分配给`foo1`。`[[id(n)]]`属性指定`t1`和`t2`结构成员的索引偏移量。由于没有为`foo2`指定索引,因此会自动为其分配下一个索引4,该索引是通过将1加到前一个结构成员使用的最大的ID来确定的。
966 |
967 | ```metal
968 | struct Foo {
969 | texture2d t1 [[id(1)]];
970 | texture2d t2 [[id(3)]];
971 | };
972 |
973 | struct Bar {
974 | Foo foo1; // foo1 assigned idx 0, t1 and t2 assigned idx 1 and 3
975 | Foo foo2; // foo2 assigned idx 4, t1 and t2 assigned idx 5 and 7
976 | };
977 | ```
978 |
979 | 如果省略`[[id]]`属性,则会根据以下的规则自动分配ID:
980 |
981 | * 1. 通过将1加到前一个结构成员使用的最大ID,将ID按顺序分配给结果成员。在下面的示例中,未提供索引,因此会自从分配索引0和1。
982 |
983 | ```metal
984 | struct MaterialTexture {
985 | texture2d tex; // Assigned index 0
986 | float4 uvScaleOffset; // Assigned index 1
987 | };
988 | ```
989 |
990 | * 2. 通过将1加到前一个数组元素使用的最大ID,按顺序将ID分配给数组元素。在下面的示例中,索引1-3自动分配给`texs1`的三个元素。索引4-5自动分配给`materials[0]`,索引6-7分配给`materials[1]`,索引8-9分给`materials[2]`。`[[id(20)]]`属性从索引20赋值给常量。
991 |
992 | ```metal
993 | struct Material {
994 | float4 diffuse; // Assigned index 0
995 | array, 3> texs1; // Assigned indices 1-3
996 | MaterialTexture materials[3]; // Assigned indices 4-9
997 | int constants [[id(20)]] [4]; // Assigned indices 20-23
998 | };
999 | ```
1000 |
1001 | * 3. 如果结构成员或数组元素`E`本身是结构或数组,则从分配给`E`的ID开始,根据规则1和2递归地为其结果成员或数组元素分配索引。在下面的示例中,对名为`normal`的结构显示提供了索引4,因此其元素(以前定义为`tex`和`uvScaleOffset`)分别分配了ID 4和5。通过向前一个成员使用的最大ID(5)加1,名为`specular`的结构的元素分配ID6和7。
1002 |
1003 | ```metal
1004 | struct Material {
1005 | MaterialTexture diffuse; // Assigned indices 0, 1
1006 | MaterialTexture normal [[id(4)]]; // Assigned indices 4, 5
1007 | MaterialTexture specular; // Assigned indices 6, 7
1008 | }
1009 | ```
1010 |
1011 | * 4. 根据规则1-3,为顶级参数缓冲区参数分配从0开始的ID。
1012 |
1013 | ### 2.12.1 参数缓冲区的第2层硬件支持
1014 |
1015 | 使用第2层硬件时,参数缓冲区具有以下附加功能,这些功能在第1层硬件中不可用。
1016 |
1017 | 可以通过指针索引访问参数缓冲区。下面显示的语法是指连续的、独立编码的参数缓冲区的数组:
1018 |
1019 | ```metal
1020 | kernel void
1021 | kern(constant Resources *resArray [[buffer(0)]])
1022 | {
1023 | constant Resources &resources = resArray[3];
1024 | }
1025 |
1026 | kernel void
1027 | kern(constant texture2d *textures [[buffer(0)]]);
1028 | ```
1029 |
1030 | 为了支持GPU驱动的管道和间接绘制调用和调度,可以在函数中的结构和数组直接复制资源,如下所示:
1031 |
1032 | ```metal
1033 | kernel void
1034 | copy(constant Foo & src [[buffer(0)]],
1035 | device Foo & dst [[buffer(1)]])
1036 | {
1037 | dst.a = src.d;
1038 | ...
1039 | }
1040 | ```
1041 |
1042 | 无法将采样器从线程地址空间复制到设备地址空间。因此,只能将采样器直接从另一个参数缓冲区复制到参数缓冲区中。以下示例显示合法和非法复制:
1043 |
1044 | ```metal
1045 | struct Resources {
1046 | sampler sam;
1047 | };
1048 | kernel void
1049 | copy(device Resources *src,
1050 | device Resources *dst,
1051 | sampler sam1)
1052 | {
1053 | constexpr sampler sam2;
1054 | dst->sam = src->sam; // Legal: device -> device
1055 | dst->sam = sam1; // Illegal: thread -> device
1056 | dst->sam = sam2; // Illegal: thread -> device
1057 | }
1058 | ```
1059 |
1060 | 参数缓冲区可以包含指向其他参数缓冲区的指针:
1061 |
1062 | ```metal
1063 | struct Textures {
1064 | texture2d diffuse;
1065 | texture2d specular;
1066 | };
1067 | struct Material {
1068 | device Textures *textures;
1069 | };
1070 | fragment float4
1071 | fragFunc(device Material & material);
1072 | ```
1073 |
1074 | ## 2.13 Uniform类型
1075 |
1076 | > 所有操作系统:自 v2.0支持uniform类型
1077 |
1078 | ### 2.13.1 Uniform类型的需求
1079 |
1080 | 在下面的函数示例中,变量`i`用于索引由`texInput`给出的纹理数组。变量`i`是不uniform的,即对于执行绘图或调度调用的图形或内核函数的线程,它可以具有不同的值,如下面的示例所示。因此,纹理采样硬件必须处理样本请求,该请求可以为执行绘图或调度的图形或内核函数的线程引用不同的纹理。
1081 |
1082 | ```metal
1083 | kernel void
1084 | my_kernel(array, 10> texInput,
1085 | array, 10> texOutput,
1086 | sampler s,
1087 | ...,
1088 | uint2 gid [[thread_position_in_grid]])
1089 | {
1090 | int i = ...;
1091 | float4 color = texInput[i].sample(s, float2(gid));
1092 | ...;
1093 | texOutput[i].write(color, float2(gid));
1094 | }
1095 | ```
1096 |
1097 | 如果变量`i`对于执行绘制或调度调用的图形或内核函数的所有线程(即,统一)具有相同的值,并且如果该信息被传送到硬件,则纹理采样硬件可以应用适当的优化。可以为纹理写入进行类似的参数,其中在运行时计算的变量用作纹理数组的索引或索引到一个或多个缓冲区。
1098 |
1099 | 为了表明这个变量对于执行绘制或调度的图形或内核函数的所有线程是统一的,Metal着色语言添加了一个名为 `uniform`的新模板(在头文件`metal_uniform`中可用),可用于在图形或内核函数内部声明变量。此模板类智能使用算术类型(即布尔、整数和浮点类型)和向量类型进行实例化。
1100 |
1101 | 下面的代码是前一个示例的修改版本,其中变量`i`被声明为`uniform`类型。
1102 |
1103 | ```metal
1104 | kernel void
1105 | my_kernel(array, 10> texInput,
1106 | array, 10> texOutput,
1107 | sampler s,
1108 | ...,
1109 | uint2 gid [[thread_position_in_grid]])
1110 | {
1111 | uniform i = ...;
1112 | float4 color = texInput[i].sample(s, float2(gid));
1113 | ...;
1114 | texOutput[i].write(color, float2(gid));
1115 | }
1116 | ```
1117 |
1118 | ### 2.13.2 Uniform类型的行为
1119 |
1120 | 如果变量属于Uniform类型,并且变量对于内核或图形函数的所有线程没有相同的值,则行为未定义。
1121 |
1122 | 统一变量隐式转换为非统一类型。将使用统一变量计算的表达式的结果赋值给统一变量是合法的,但是将非统一的变量赋值给统一变量会导致编译时错误。在下面的示例中,乘法合法地将统一变量x转换为非统一乘积z。但是,将非统一变量z赋值给统一变量b会导致编译时错误。
1123 |
1124 | ```metal
1125 | uniform x = ...;
1126 | int y = ...;
1127 | int z = x*y; // x is converted to a non-uniform for a multiply
1128 | uniform b = z; // illegal; compile-time error
1129 | ```
1130 |
1131 | 声明一个uniform元素的数组:
1132 |
1133 | ```metal
1134 | uniform bar[10]; // elements stored in bar array are uniform
1135 | ```
1136 |
1137 | `uniform`类型对于参数和函数的返回类型都是合法的。例如:
1138 |
1139 | ```metal
1140 | uniform foo(...); // foo returns a uniform integer value
1141 | int bar(uniform a, ...);
1142 | ```
1143 |
1144 | 声明指向统一类型的指针是合法的,但声明统一指针是不合法的。例如:
1145 |
1146 | ```metal
1147 | device uniform *ptr; // values pointed to by ptr are uniform
1148 | uniform ptr; // illegal; compile-time error
1149 | ```
1150 |
1151 | 将 uniform变量与非uniform变量组合的表达式结构是非uniform。如果将非uniform的结果分配给uniform变量,如下例所示,则行为未定义。(前端可能会产生编译时错误,但不保证会这样做。)
1152 |
1153 | ```metal
1154 | uniform i = ...;
1155 | int j = ...;
1156 | if (i < j) { // non-uniform result for expression (i < j)
1157 | ...
1158 | i++; //compile-timeerror,undefinedbehavior
1159 | }
1160 | ```
1161 |
1162 | 以下示例类似:
1163 |
1164 | ```metal
1165 | bool p = ... // non-uniform condition.
1166 | uniform a = ..., b = ...;
1167 | uniform c = p ? a : b; // compile-time error, undefined behavior
1168 | ```
1169 |
1170 | ### 2.13.3 统一控制流程
1171 |
1172 | 当控制流条件测试基于统一数量时,所有程序实例在函数中的条件测试中遵循相同的路径。基于uniform数量的控制流程代码应该比基于非uniform数量的控制流程代码更有效。
1173 |
1174 | ## 2.14 类型转换和重新解释数据
1175 |
1176 | `static_cast`运算符用于从标量或向量类型转换为另一标量或向量类型,没有饱和度并具有默认的舍入模式(即,当转换为浮点数时,舍入到最接近的偶数;当转换为整数时,舍入为零)。如果源类型是标量或向量布尔值,则将值`false`转换为零,将值`true`转换为1。
1177 |
1178 | Metal添加了一个 `as_type` 运算符,允许将任何标量或向量数据类型(不是指针)重新解释为另一个相同大小的标量或向量数据类型。操作输中的位直接返回不是作为新类型进行修改。不执行函数参数的常规类型提升。
1179 |
1180 | 例如,`as_type (0x3f800000)`返回1.0f,如果将其视为IEEE-754单精度值,则为位模式`0x3f800000`的值。
1181 |
1182 | 使用 `as_type`运算符将数据重新解释为具有不同字节数的类型会导致错误。
1183 |
1184 | 合法和非法类型转换的示例:
1185 |
1186 | ```metal
1187 | float f = 1.0f;
1188 | // Legal. Contains: 0x3f800000
1189 | uint u = as_type(f);
1190 |
1191 | // Legal. Contains:
1192 | // (int4)(0x3f800000, 0x40000000, 0x40400000, 0x40800000)
1193 | float4 f = float4(1.0f, 2.0f, 3.0f, 4.0f);
1194 | int4 i = as_type(f);
1195 |
1196 | int i;
1197 | // Legal.
1198 | short2 j = as_type(i);
1199 |
1200 | half4 f;
1201 | // Error. Result and operand have different sizes
1202 | float4 g = as_type(f);
1203 |
1204 | float4 f;
1205 | // Legal. g.xyz has same values as f.xyz.
1206 | // g.w is undefined
1207 | float3 g = as_type(f);
1208 | ```
1209 |
1210 | ## 2.15 隐式类型转换
1211 |
1212 | 支持标量内置类型(void除外)之间的隐式转换。当完成隐式转换时,它不仅仅是对表达式值的重新解释,而是将该值转换为新类型中的等效值。例如,整数值被转换为浮点值5.0。
1213 |
1214 | 所有向量类型都被认为具有比标量类型更高的转换等级。不允许从向量类型到另一个向量或标量类型的隐式转换,并且会生成编译错误。例如,以下尝试将4分量整数向量转换为4分量浮点向量会失败。
1215 |
1216 | ```metal
1217 | int4 i;
1218 | float4 f = i; // compile error.
1219 | ```
1220 |
1221 | 支持从标量到向量类型的隐式转换。标量值在向量的每个元素复制。标量也可以用常用算术转换为向量或矩阵使用的元素类型。
1222 |
1223 | 例如:
1224 |
1225 | ```metal
1226 | float4 f = 2.0; // f = (2.0f, 2.0f, 2.0f, 2.0f)
1227 | ```
1228 |
1229 | 不支持从标量到矩阵类型和向量到矩阵类型的隐式转换,并且会产生编译时错误。不允许从矩阵类型到另一个矩阵、向量或者标量类型的隐式转换,并且会产生编译时错误。
1230 |
1231 | 指针类型的隐式转换遵循C++ 14规范中描述的规则。
--------------------------------------------------------------------------------
/ch03.md:
--------------------------------------------------------------------------------
1 | # 3 运算符
2 |
3 | > 所有操作系统:从v1.0开始支持标量、向量和矩阵的运算符。
4 |
5 | 对于间接命令缓冲器,赋值运算符(=)不会复制命令的内容。有关在间接命令缓冲区复制命令的更多详细信息,请参阅第5.16.1节。
6 |
7 | ## 3.1 标量和向量运算符
8 |
9 | 1. 算术运算符,加(+)、减(-)、乘(*)、除(/),可以用于标量、向量、整数和浮点数这类数据类型上。在操作数类型转换之后,所有算术运算符都返回与操作数类型相同的内置类型(整数或浮点)的结果。转换后,以下情况有效:
10 |
11 | - 两个操作数都是标量。在这种情况下,结果是一个标量。
12 | - 一个操作数是标量,另一个是向量。在这种情况下,标量将转换为向量操作数使用的元素类型。然后将标量类型扩展为具有与向量操作数相同数量组件的向量。该操作以分量方式执行,结果是相同大小的向量。
13 | - 两个操作数是相同大小的向量。在这种情况下,操作是分量执行的,结果会是同样大小的向量。
14 |
15 | 对整数类型进行除法运算,导致值超出整数类型的最大和最小值可表示的限定范围,例如对于有符号证书类型的 TYPE_MIN/-1或除以零不会导致异常,但会导致一个未指定的值。根据`IEEE-754`标准的规则,浮点类型除以零的结果是正负无穷大或NaN。(有关浮点运算的数值精度的详细信息,请参阅第7章。)
16 |
17 | 2. 模运算符(%)可以对标量和向量整数数据类型进行操作。在操作数类型转换之后,所有算术运算符都返回与操作数类型相同的内置类型的结果。以下情况有效:
18 |
19 | - 两个操作数是标量。在这种情况下,当运用运算符,结果也是标量。
20 | - 一个操作数是标量,一个操作数是向量。在这种情况下,标量转换为向量操作数使用的数据类型。然后将标量类型扩展为具有与向量操作数相同数量组件的向量。该操作以分量方式执行,结果是相同大小的向量。
21 | - 两个操作数是相同大小的向量。在这种情况下,操作是分量执行的,结果会是同样大小的向量。
22 |
23 | 对于使用第二个操作数为零计算的任何组件,结果值是未定义的,而具有非零操作数的其他组件的结果仍保持其定义。如果两个操作数都是非负数,则余数为非负数。如果操作数中有一个为负数,则结果是未定义的。
24 |
25 | 3. 一元运算符(+ 和 -)可以对标量和向量进行操作,操作数的类型为整型或浮点型。
26 |
27 | 4. 算术前置与后置的递增与递减运算符(`--`和`++`)可以对整数类型的标量与向量进行操作。所有一元运算符都是操作数组件分量进行运算。结果值与操作数类型相同。
28 |
29 |
30 | 5. 关系运算符,大于(>),小于(<),大于等于(>=)以及小于等于(<=),可以用于标量和矢量,整型和浮点型上。结果只是一个布尔(bool)标量或者矢量。
31 |
32 | - 两个操作数都是标量。在这种情况下,当运用运算符操作,结果是一个`bool`值
33 | - 一个操作数是标量,另一个是向量。在这种情况下,标量将转换为向量操作数使用的元素类型。然后将标量类型扩展为具有与向量操作数相同数量的组件的向量。操作以分量方式执行,结果是布尔向量。
34 | - 两个操作数是相同类型的向量。在这种情况下,以分量方式执行操作,结果是布尔向量。
35 |
36 | 如果任一参数是`NaN`,则关系运算符总返回`false`。要测试向量的任何或所有元素的关系运算,请在`if(...)`语句的上下文中使用`any`和`all`内置函数(在第5.4节中定义)。
37 |
38 | 6. 判相当运算符,等于(==)和不等于(!=), 可以运用在标量和矩阵、整型和浮点型上。所有判相当的
39 |
40 | - 两个操作数都是标量。在这种情况下,当运用运算符操作,结果是一个`bool`值
41 | - 一个操作数是标量,另一个是向量。在这种情况下,标量将转换为向量操作数使用的元素类型。然后将标量类型扩展为具有与向量操作数相同数量的组件的向量。操作以分量方式执行,结果是布尔向量。
42 | - 两个操作数是相同类型的向量。在这种情况下,以分量方式执行操作,结果是布尔向量。
43 |
44 | 所有其他隐式转换的情况都是非法的。如果一个参数或参数都是“非数字”(NaN),则等于运算符(`==`)返回`false`。如果一个参数或参数都是“非数字”(NaN),则等于运算符(`!=`)返回`true`。
45 |
46 | 7. 除内置浮点型标量和向量之外,按位运算符 与(`&`)、或(`|`)、异或(`^`)、非(`~`)对所有其他内置类型标量和向量进行操作。
47 |
48 | 8. 逻辑运算符 与(`&`)、或(`||`)对两个布尔表达式进行操作。结果是标量或向量布尔值。
49 |
50 | 9. 逻辑一元运算符 非(`!`)对布尔表达式进行操作。结果是标量或向量布尔值。
51 |
52 | 10. 三元选择运算符(`?:`)对三个表达式(`exp1 ? exp2 : exp3`)进行操作。此运算符计算第一个表达式`exp1`,该表达式的结果必须是标量布尔值。如果结果为`true`,则选择计算第二个表达式;否则计算第三个表达式。仅计算第二个和第三个表达式其中的一个。第二个和第三个表达式可以是任何类型,只要他们的类型匹配,或者可以对他们其中的一个表达式应用第2.11节的转换使其类型匹配,或者一个是向量而另一个是标量。这种情况下,标量被加宽到与向量类型相同的类型。生成的匹配类型是整个表达式的类型。
53 |
54 | 11. 补码(`~`)运算符的操作数必须是整型的标量或向量,结果是其操作数的补码。
55 |
56 | 右移运算符(`>>`)、左移运算符(`<<`)对所有整数类型的标量和向量进行操作。对于内置向量类型,运算符对组件进行操作。对于右移(`>>`)、左移(`<<`)运算符,如果第一个操作数是标量,则最右边的操作数必须是标量。如果第一个操作数是向量,则最右边的操作数可以是向量或标量。
57 |
58 | `E1<> E2`的结果是`E1`右移由`E2`的`log2(N)`最低有效位的无符号整数值位数,其中`N`用于表示`E1`数据类型的位数,如果`E1`是标量则用于表示`E1`元素类型的比特数。如果`E1`是向量,腾出的位用零填充。
61 |
62 | 12. `lvalue = expression`
63 |
64 | 上面未描述而C++ 13支持的运算符(例如,`sizeof()`、一元`&`和逗号`,`运算符)的行为与C++ 14规范中描述的相同。
65 |
66 | 无符号整数应该遵守算术模2n的定律,其中`n`是该特定整数大小的值表示中的位数。有符号整数溢出的结果是未定义的。
67 |
68 | 对于积分操作数,除(`/`)运算符产生代数商,丢弃任何小数部分。(这通常被称为截断为零)。如果商`a/b`在结果的类型中是可以表示的,则`(a/b)*6 + a%b` 与 `a`是相等的。
69 |
70 | ## 3.2 矩阵运算
71 |
72 | 算术运算符加(+)和减(-)可以对矩阵进行操作。两个矩阵必须具有相同数量的行和列。操作以分量方式完成,产生相同大小的矩阵。算术运算符乘法(*)操作:
73 |
74 | * 一个标量和一个矩阵
75 | * 一个矩阵和一个标量
76 | * 一个矢量和一个矩阵
77 | * 一个矩阵和一个矢量
78 | * 或者 一个矩阵和另一个矩阵
79 |
80 | 如果一个操作数是标量,则标量值乘以矩阵的每个分量,得到相同大小的矩阵。右向量操作数被视为列向量,左向量操作数被视为行向量。对于向量-矩阵,矩阵-向量和矩阵-矩阵乘法,左操作数的列数必须等于右操作数的行数。乘法运算执行线性代数乘法,产生一个向量或矩阵,其行数与左操作数相同,列数与右操作数相同。
81 |
82 | 以下示例假定这些向量、矩阵和标量变量已初始化。下面描述的向量到矩阵、矩阵到向量和矩阵到矩阵乘法运算的部分和的顺序是不确定的。
83 |
84 | ```metal
85 | float3 v;
86 | float3x3 m;
87 | float a = 3.0f;
88 | ```
89 |
90 | 如下的矩阵到标量的乘法
91 |
92 | ```metal
93 | float3x3 m1 = m * a;
94 | ```
95 |
96 | 等同于:
97 |
98 | ```metal
99 | m1[0][0] = m[0][0] * a;
100 | m1[0][1] = m[0][1] * a;
101 | m1[0][2] = m[0][2] * a;
102 | m1[1][0] = m[1][0] * a;
103 | m1[1][1] = m[1][1] * a;
104 | m1[1][2] = m[1][2] * a;
105 | m1[2][0] = m[2][0] * a;
106 | m1[2][1] = m[2][1] * a;
107 | m1[2][2] = m[2][2] * a;
108 | ```
109 |
110 | 向量到矩阵的乘法
111 |
112 | ```metal
113 | float3 u = v * m;
114 | ```
115 | 等同于:
116 |
117 | ```metal
118 | u.x = dot(v, m[0]);
119 | u.y = dot(v, m[1]);
120 | u.z = dot(v, m[2]);
121 | ```
122 |
123 | 如下的矩阵到向量的乘法
124 |
125 | ```metal
126 | float3 u = m * v;
127 | ```
128 | 等同于:
129 |
130 | ```metal
131 | u.x = m[0].x * v.x + m[1].x * v.y + m[2].x * v.z;
132 | u.y = m[0].y * v.x + m[1].y * v.y + m[2].y * v.z;
133 | u.z = m[0].z * v.x + m[1].z * v.y + m[2].z * v.z;
134 | ```
135 |
136 | 矩阵到矩阵的乘法:
137 |
138 | ```metal
139 | float3x3 r = m * n; // m,n are float3x3
140 | ```
141 |
142 | 等同于:
143 |
144 | ```metal
145 | r[0].x = m[0].x * n[0].x + m[1].x * n[0].y + m[2].x * n[0].z;
146 | r[0].y = m[0].y * n[0].x + m[1].y * n[0].y + m[2].y * n[0].z;
147 | r[0].z = m[0].z * n[0].x + m[1].z * n[0].y + m[2].z * n[0].z;
148 | r[1].x = m[0].x * n[1].x + m[1].x * n[1].y + m[2].x * n[1].z;
149 | r[1].y = m[0].y * n[1].x + m[1].y * n[1].y + m[2].y * n[1].z;
150 | r[1].z = m[0].z * n[1].x + m[1].z * n[1].y + m[2].z * n[1].z;
151 | r[2].x = m[0].x * n[2].x + m[1].x * n[2].y + m[2].x * n[2].z;
152 | r[2].y = m[0].y * n[2].x + m[1].y * n[2].y + m[2].y * n[2].z;
153 | r[2].x = m[0].z * n[2].x + m[1].z * n[2].y + m[2].z * n[2].z;
154 | ```
--------------------------------------------------------------------------------
/ch04.md:
--------------------------------------------------------------------------------
1 | # 4 函数和变量声明
2 |
3 | 本章描述了如何声明函数、参数和变量。还详细说明了特性如何与函数、参数和变量一起使用来指定一些限制。
4 |
5 | ## 4.1 函数
6 |
7 | > 所有操作系统:kernel、vetex和fragment属性从v1.0开始就支持。
8 |
9 | Metal支持以下函数属性,这些属性限定了函数的使用方式:`vertex`、`fragment`和`kernel`,它们分别在4.1.1、4.1.2和4.1.3节中详述。这些函数属性在函数的开头使用,在返回值之前。
10 |
11 | 使用了`vertex`、`fragment`和`kernel`函数属性的函数不得调用也使用这些属性的函数,否则会导致编译错误。
12 |
13 | 可以在命名空间内声明使用`vertex`、`fragment`和`kernel`函数属性的函数。
14 |
15 | ### 4.1.1 顶点函数(Vertex Functions)
16 |
17 | 以下示例演示了使用`vertex`属性声明顶点函数的语法。
18 |
19 | ```metal
20 | vertex void
21 | my_vertex_func(...)
22 | {...}
23 | ```
24 |
25 | ### 4.1.2 片段函数(Fragment Functions)
26 |
27 | ### 4.1.3 内核函数(Kernel Functions)
28 |
29 | ```metal
30 | kernel void
31 | my_kernel(...)
32 | {...}
33 | ```
34 |
35 | 使用`kernel`属性声明的函数必须返回`void`。
36 |
37 | ` [[max_total_threads_per_threadgroup]]` 函数属性可以与内核函数一起使用,来指定每个线程组的最大线程数。
38 |
39 | 下面是使用此属性的内核函数示例:
40 |
41 | ```metal
42 | [[max_total_threads_per_threadgroup(x)]]
43 | kernel void
44 | my_kernel(...)
45 | {...}
46 | ```
47 |
48 | 如果`[[max_total_threads_per_threadgroup]]`的值大于`[MTLDevice maxThreadsPerThreadgroup]`属性,则管道状态会创建失败。
49 |
50 | ### 4.1.4 Tile函数
51 |
52 |
53 |
54 | ## 4.2 Address Space Attributes for Variables and Arguments
55 |
56 | `device`、`threadgroup`、`constant` 和 `thread`
57 |
58 | `threadgroup_imageblock`
59 |
60 |
61 |
62 | * `device`
63 | * `threadgroup`
64 | * `threadgroup_imageblock` (section 4.2.3)
65 | * `constant` (section 4.2.2)
66 | * `thread` (section 4.2.5)
67 |
68 | ### 4.2.1 device Address Space
69 |
70 | ### 4.2.2 threadgroup Address Space
71 |
72 | ### 4.2.3 threadgroup_imageblock Address Space
73 |
74 | ### 4.2.4 constant Address Space
75 |
76 | ### 4.2.5 thread Address Space
77 |
78 | ## 4.3 函数参数与变量
79 |
80 | 大部分图像处理器和内核方法的输入和输出是通过参数来传递的。
81 |
82 |
83 |
84 | ### 4.3.2 Buffers 和 纹理的结构
85 |
86 |
87 | ## 4.4 存储类说明符
88 |
89 | ```metal
90 | extern constant float4 noise_table[256];
91 | static constant float4 color_table[256] = { ... }; // static is okay
92 |
93 | extern void my_foo(texture2d img);
94 | extern void my_bar(device float *a);
95 |
96 | kernel void
97 | my_kernel(texture2d img [[texture(0)]],
98 | device float *ptr [[buffer(0)]])
99 | {
100 | extern constant float4 a;
101 | static constant float4 b; // static is an error.
102 | static float c; // static is an error.
103 |
104 | ...
105 | my_foo(img);
106 | ...
107 | my_bar(ptr);
108 | ...
109 | }
110 | ```
111 |
112 | ## 4.5 采样和差值属性
113 |
114 | ```metal
115 | center_perspective
116 | center_no_perspective
117 | centroid_perspective
118 | centroid_no_perspective
119 | sample_perspective
120 | sample_no_perspective
121 | flat
122 | ```
123 |
124 | `center_perspective`是默认的采样和差值属性,以下情况例外:
125 |
126 | ```metal
127 | struct FragmentInput {
128 | float4 pos [[center_no_perspective]];
129 | float4 color [[center_perspective]];
130 | float2 texcoord;
131 | int index [[flat]];
132 | float f [[sample_perspective]];
133 | };
134 | ```
135 |
136 | ## 4.6 片段函数 vs. 采样函数
137 |
138 |
139 |
140 | ## 4.7 图像块属性
141 |
142 | > macOS: 不支持图像块属性
143 | > iOS: 从v2.0开始支持图像块属性
144 |
145 | 本节介绍几个与图像块(imageblocks)一起使用的属性。在第2.1节中描述了`[[imageblock_data(type)]]`属性,该属性指定在片段函数上具有显式图像块布局的输入和输出图像块。
146 |
147 | ### 4.7.1 用于匹配和查看图像块数据的成员的用户属性
148 |
149 | `[[user(name)]]`属性可用于为片段函数指定图像块数据类型的数据成员的属性名称。如果片段函数中指定的图像块结果是主显式图像块结构的子集,则使用以下规则将与使用片段函数的图像块与在主显式图像块结构中声明的相应数据成员进行匹配:
150 |
151 | * 由`[[user(name)]]`给出的每个属性名称对于图像块中的每个数据成员必须是唯一的。
152 | * 如果为数据成员指定了`[[user(name)]]`属性名称,则`name`指定的属性名称必须与主显式图像块结构中声明的数据成员匹配。此外,关联的数据类型也必须匹配。
153 | * 如果未指定`[[user(name)]]`属性,则片段函数的图像块数据类型和主图像块结构的数据成员名称和类型必须匹配。此外,数据成员不能位于视图图像块结构内或主图像块结构的嵌套结构中。
154 |
155 | 下面的示例显示了使用`[[user(name)]]`属性声明的图像块结构:
156 |
157 | 示例:
158 |
159 | ```metal
160 |
161 | // The explicit layout imageblock data master struct.
162 | struct IM {
163 | rgba8unorm a [[user(my_a), raster_order_group(0)]];
164 | rgb9e5 b [[user(my_b), raster_order_group(0)]];
165 | int c [[user(my_c), raster_order_group(0)]];
166 | float d [[user(my_d), raster_order_group(0)]];
167 | };
168 |
169 | // The explicit layout imageblock data view struct for input.
170 | struct IVIn {
171 | rgb9e5 x [[user(my_b)]]; // Maps to IM::b
172 | float y [[user(my_d)]]; // Maps to IM::d
173 | };
174 |
175 |
176 | // The explicit layout imageblock data view struct for output.
177 | struct IVOut {
178 | int z [[ user(my_c) ]]; // Maps to IM::c
179 | };
180 |
181 | // The fragment return struct.
182 | struct FragOut {
183 | // IVOut is a view of the master IM.
184 | IVOut i [[ imageblock_data(IM) ]];
185 | };
186 |
187 | // IVIn is a view of the master IM.
188 | fragment FragOut
189 | my_fragment(IVIn i [[imageblock_data(IM)]], ...) {
190 | FragOut fragOut;
191 | ... = i.x;
192 | ... = i.y;
193 | fragOut.i.z = ...;
194 | return fragOut;
195 | }
196 | ```
197 |
198 | 下面的示例显示了没有使用`[[user(name)]]`属性声明的图像块结构:
199 |
200 | ```metal
201 | struct IM {
202 | rgba8unorm a [[raster_order_group(0)]];
203 | rgb9e5 b [[raster_order_group(0)]];
204 | int c [[raster_order_group(0)]];
205 | float d [[raster_order_group(0)]];
206 | };
207 |
208 | struct IVIn {
209 | rgb9e5 b; // Maps to IM::b
210 | float d; // Maps to IM::d
211 | };
212 |
213 | struct IVOut {
214 | int c; // Maps to IM::c
215 | };
216 |
217 | struct FragOut {
218 | IVOut i [[imageblock_data(IM)]];
219 | };
220 |
221 | fragment FragOut
222 | my_fragment(IVIn i [[imageblock_data(IM)]], ...) {
223 | FragOut fragOut;
224 | ... = i.b;
225 | ... = i.d;
226 | fragOut.i.c = ...;
227 | return fragOut;
228 | }
229 | ```
230 |
231 | 嵌套结构可以在主显式图像块中声明并查看图像块结构。下面的示例显示了图像块中嵌套结构如何与使用`[[user(name)]]`属性声明的数据成员一起使用:
232 |
233 | ```metal
234 | struct A {
235 | rgba8unorm a [[user(A_a)]];
236 | rgb9e5 b [[user(A_b)]];
237 | };
238 |
239 | struct B {
240 | int a [[user(B_a), raster_order_group(1)]];
241 | float b [[user(B_b), raster_order_group(2)]];
242 | };
243 |
244 | struct IM {
245 | A a [[user(A), raster_order_group(0)]];
246 | B b [[user(B)]];
247 | };
248 |
249 | struct IVIn {
250 | A x [[user(A)]]; // Maps to IM::a
251 | };
252 |
253 | struct IVOut {
254 | B y [[user(B)]]; // Maps to IM::b
255 | rgb9e5 z [[user(A_b)]]; // Maps to IM::A::b
256 | };
257 |
258 | struct FragOut {
259 | IVOut i [[imageblock_data(IM)]];
260 | };
261 |
262 | fragment FragOut
263 | my_fragment(IVIn i [[imageblock_data(IM)]], ...) {
264 | FragOut fragOut;
265 | ... = i.x;
266 | fragOut.i.y.a = ...;
267 | fragOut.i.y.b = ...;
268 | fragOut.i.z = ...;
269 | return fragOut;
270 | }
271 | ```
272 |
273 | 视图结构的每个字段必须只对应一个主结构字段。主结构字段可以引用顶级结构字段以及嵌套结构中的字段。两个或多个视图结构字段对同一主结构字段进行别名是非法的。
274 |
275 | 非法使用示例:
276 |
277 | ```metal
278 | struct M {
279 | struct A {
280 | int a [[user(x)]];
281 | }
282 | b [[user(y), raster_order_group(0)]];
283 | };
284 |
285 | struct V {
286 | int a [[user(x)]];
287 | M::A b [[user(y)]]; // illegal: b aliases with a
288 | };
289 |
290 | fragment void
291 | f(V i [[imageblock_data(M)]])
292 | {...}
293 | ```
294 |
295 | 显式图像块类型不能具有使用`[[color(n)]]`属性声明的数据成员。
296 |
297 |
298 | ### 4.7.2 图像块和栅格组
299 |
300 | 在内核函数中,忽略在图像块的数据成员成员上指定的`[[raster_order_group(index)]]`属性。
301 |
302 | 在片段函数中,必须使用主显式图像块数据结构的数据成员指定`[[raster_order_group(index)]]`属性。
303 |
304 | 如果主显式图像块结构包含作为结构的数据成员,则可以为嵌套结构中的所有数据成员或仅嵌套结构体指定`[[raster_order_group(index)]]`属性。如果在嵌套结构体上指定了`[[raster_order_group(index)]]`属性,那么它将应用于嵌套结构体所有的数据成员,并且嵌套结构体中的任何数据成员都不能具有`[[raster_order_group(index)]] `的属性声明。
305 |
306 | `[[raster_order_group(index)]]`可以选择使用图像块视图结构的数据成员指定,但`[[raster_order_group(index)]]`必须匹配在数据成员上指定的相同`[[raster_order_group(index)]]`主显式图像块结构。
307 |
308 | 以下示例显示了如何为主图像块的数据成员指定`[[raster_order_group(index)]]`属性。由于`[[raster_order_group(index)]]`属性用于`gBufferData`结构的`S`结构成员,因此该属性不能用于`S`结构的任何成员。
309 |
310 | ```metal
311 | struct S {
312 | rgb9e5 normal;
313 | float factor;
314 | };
315 |
316 | struct gBufferData {
317 | half3 color [[raster_order_group(0)]];
318 | S s [[raster_order_group(1)]];
319 | rgb11b10f lighting [[raster_order_group(2)]];
320 | };
321 | ```
322 |
323 | 声明为数组的数据成员具有与该数据所有成员关联的单个栅格组。以下示例显示如何为声明为结构体类型的数组的主图像块的数据成员指定`[[raster_order_group(index)]]`属性。
324 |
325 | ```metal
326 | struct S {
327 | rgb9e5 normal;
328 | float factor;
329 | };
330 |
331 | struct IM {
332 | half3 color [[raster_order_group(0)]];
333 | S s [[raster_order_group(1)]][2];
334 | rgb11b10f lighting [[raster_order_group(2)]];
335 | };
336 | ```
337 |
338 | 以下示例显示`[[raster_order_group(index)]]`属性的错误用法,其中数据成员`s`被声明为`S`类结构的数组,其成员指定了会导致编译错误的栅格组。
339 |
340 | ```metal
341 | struct S {
342 | rgb9e5 normal [[raster_order_group(0)]];
343 | float factor [[raster_order_group(1)]];
344 | };
345 |
346 | struct IM {
347 | half3 color [[raster_order_group(0)]];
348 | S s[2]; // compilation error
349 | rgb11b10f lighting [[raster_order_group(2)]];
350 | };
351 | ```
352 |
353 | ### 4.7.3 给显式和隐式图像块增加别名
354 |
355 |
356 |
357 | ```metal
358 | [[alias_implicit_imageblock]]
359 | [[alias_implicit_imageblock_color(n)]]
360 | ```
361 |
362 | ```metal
363 | struct I {
364 | rgba8unorm a;
365 | rgb9e5 b;
366 | int c;
367 | float d;
368 | };
369 |
370 | struct FragOut {
371 | float4 finalColor [[color(0)]];
372 | I i [[imagelock_data, alias_implicit_imageblock_color(1)]];
373 | };
374 |
375 | fragment FragOut
376 | my_fragment(I i [[imageblock_data]], ...)
377 | {
378 | FragOut fragOut;
379 | ...
380 | return fragOut;
381 | }
382 | ```
383 |
384 | ### 4.7.4 图像块和函数常量
385 |
386 | 片段函数或内核函数的输入参数或输出结果,不能是含有用`[[function_constant(name)]]`属性修饰的成员变量的图像块结构体。
387 |
388 | ## 4.8 可编程混合
389 |
390 | 片段函数可用于执行每片段或每采样可编程混合。由`[[color(m)]]`属性标识的颜色附加索引可以指定为片段函数的参数。
391 |
392 | 下面是一个OpenGL ES可编程混合示例,它描述了如何在下面的内容上绘制灰度。
393 |
394 | GLSL版本是:
395 |
396 | ```metal
397 | #extension GL_APPLE_shader_framebuffer_fetch : require
398 | void main()
399 | {
400 | // RGB to grayscale
401 | mediump float lum = dot(gl_LastFragData[0].rgb, vec3(0.30,0.59,0.11));
402 | gl_FragColor = vec4(lum, lum, lum, 1.0);
403 | }
404 | ```
405 |
406 | Metal等效版本是:
407 |
408 | ```metal
409 | fragment half4
410 | paint_grayscale(half4 dst_color [[color(0)]])
411 | {
412 | // RGB to grayscale
413 | half lum = dot(dst_color.rgb, half3(0.30h, 0.59h, 0.11h));
414 | return half4(lum, lum, lum, 1.0h);
415 | }
416 | ```
417 |
418 | ## 4.9 图形函数 - 签名匹配
419 |
420 | 图形函数的方法签名是输入到图形函数或从图形函数输出的参数列表。
421 |
422 | ### 4.9.1 顶点-片段 签名匹配
423 |
424 | 可以在顶点和片段函数之间传递两种数据:用户自定义的变量和内置变量。
425 |
426 | 使用`[[stage_in]]`属性声明片段函数的每个实例输入。这些由关联的顶点函数输出。
427 |
428 | 使用第4.3.3节中定义的属性之一来声明内置变量。这些由顶点函数(例如`[[position]]`, `[[point_size]]`, `[[clip_distance]]`)生成,由光栅化生成(例如`[[point_coord]]`, `[[front_facing]]`, `[[sample_id]]`, `[[sample_mask]]`) 或引用作为片段函数输入传递的帧缓冲颜色值(如`[[color]]`)。
429 |
430 | 必须始终返回内置变量`[[position]]`。如果需要,由顶点函数生成的其他内置变量(如 `[[point_size]]`, `[[clip_distance]]`)必须在顶点函数的返回类型中声明,但不能由片段函数访问。
431 |
432 | ```metal
433 | struct VertexOutput {
434 | float4 position [[position]];
435 | float3 normal;
436 | float2 texcoord;
437 | };
438 |
439 | vertex VertexOutput
440 | my_vertex(...)
441 | {
442 | VertexOutput v;
443 | ...
444 | return v;
445 | }
446 |
447 | fragment float4
448 | my_fragment(VertexOutput f [[stage_in]], ...) {
449 | float4 clr;
450 | ...
451 | return clr;
452 | }
453 |
454 | fragment float4
455 | my_fragment2(VertexOutput f [[stage_in]],
456 | bool is_front_face [[front_facing]], ...)
457 | {
458 | float4 clr;
459 | ...
460 | return clr;
461 | }
462 | ```
463 |
464 | 以下是兼容签名的另一个示例:
465 |
466 | ```metal
467 | struct VertexOutput {
468 | float4 position [[position]];
469 | float3 vertex_normal [[user(normal)]];
470 | float2 texcoord [[user(texturecoord)]];
471 | };
472 |
473 | struct FragInput {
474 | float3 frag_normal [[user(normal)]];
475 | float4 position [[position]];
476 | float4 framebuffer_color [[color(0)]];
477 | bool is_front_face [[front_facing]];
478 | };
479 |
480 | vertex VertexOutput
481 | my_vertex(...)
482 | {
483 | VertexOutput v;
484 | ...
485 | return v;
486 | }
487 |
488 | fragment float4
489 | my_fragment(FragInput f [[stage_in]], ...) {
490 | float4 clr;
491 | ...
492 | return clr;
493 | }
494 | ```
495 |
496 | 以下是兼容签名的另一个示例:
497 |
498 | ```metal
499 | struct VertexOutput {
500 | float4 position [[position]];
501 | float3 normal;
502 | float2 texcoord;
503 | };
504 |
505 | vertex VertexOutput
506 | my_vertex(...)
507 | {
508 | VertexOutput v;
509 | ...
510 | return v;
511 | }
512 |
513 | fragment float4
514 | my_fragment(float4 p [[position]], ...) {
515 | float4 clr;
516 | ...
517 | return clr;
518 | }
519 | ```
520 |
521 | 以下是不兼容签名的一个示例。`VertexOutput`中的法线数据类型(`float3`)与 `FragInput`(`half3`)中的`normal`类型不匹配:
522 |
523 | ```metal
524 | struct VertexOutput {
525 | float4 position [[position]];
526 | float3 normal;
527 | float2 texcoord;
528 | };
529 |
530 | struct FragInput {
531 | float4 position [[position]];
532 | half3 normal;
533 | };
534 |
535 | vertex VertexOutput
536 | my_vertex(...)
537 | {
538 | VertexOutput v;
539 | ...
540 | return v;
541 | }
542 |
543 | fragment float4
544 | my_fragment(FragInput f [[stage_in]], ...) {
545 | float4 clr;
546 | ...
547 | return clr;
548 | }
549 | ```
550 |
551 | 以下是不兼容签名的另一个示例。`VertexOutput`(`normal`)中`normal`属性索引与`FragInput`(`foo`)中的`normal`属性索引不匹配。
552 |
553 | ```metal
554 | struct VertexOutput {
555 | float4 position [[position]];
556 | float3 normal [[user(normal)]];
557 | float2 texcoord [[user(texturecoord)]];
558 | };
559 |
560 | struct FragInput {
561 | float3 normal [[user(foo)]];
562 | float4 position [[position]];
563 | };
564 |
565 | vertex VertexOutput
566 | my_vertex_shader(...)
567 | {
568 | VertexOutput v;
569 | ...
570 | return v;
571 | }
572 |
573 | fragment float4
574 | my_fragment_shader(FragInput f [[stage_in]], ...) {
575 | float4 clr;
576 | ...
577 | return clr;
578 | }
579 | ```
580 |
581 | ## 4.10 程序范围内的函数常量
582 |
583 | ### 4.10.1
584 |
585 | ```metal
586 | [[function_constant(index)]]
587 | ```
588 |
589 | ```metal
590 | constant int a [[function_constant(0)]];
591 | constant bool b [[function_constant(2)]];
592 | ```
593 |
594 | ```metal
595 | constant int a [[function_constant(0)]];
596 | constant bool b [[function_constant(2)]];
597 | constant bool c = ((a == 1) && b);
598 | constant int d = (a * 4);
599 | ```
600 |
601 | #### 4.10.1.1 用于控制编译路径的函数常量
602 |
603 | 考虑如下的函数,该函数使用预处理器宏作为函数常量:
604 |
605 | ```metal
606 | struct VertexOutput {
607 | float4 position [[position]];
608 | float4 color;
609 | };
610 |
611 | struct VertexInput {
612 | float4 position [[attribute(0)]];
613 | float4 offset [[attribute(1)]];
614 | float4 color [[attribute(2)]];
615 | };
616 |
617 | vertex VertexOutput
618 | myVertex(VertexInput vIn [[stage_in]])
619 | {
620 | VertexOutput vOut;
621 |
622 | vOut.position = vIn.position;
623 | #ifdef OFFSET_DEFINED
624 | vOut.position += vIn.offset;
625 | #endif
626 |
627 | #ifdef COLOR_DEFINED
628 | vOut.color = vIn.color;
629 | #else
630 | vOut.color = float4(0.0f);
631 | #endif
632 |
633 | return vOut;
634 | }
635 | ```
636 |
637 | 使用函数常量变量编写的相应函数是:
638 |
639 | ```metal
640 | constant bool offset_defined [[function_constant(0)]];
641 | constant bool color_defined [[function_constant(1)]];
642 |
643 | vertex VertexOutput
644 | myVertex(VertexInput vIn [[stage_in]])
645 | {
646 | VertexOutput vOut;
647 |
648 | vOut.position = vIn.position;
649 | if (offset_defined)
650 | vOut.position += vIn.offset;
651 |
652 | if (color_defined)
653 | vOut.color = vIn.color;
654 | else
655 | vOut.color = float4(0.0f);
656 |
657 | return vOut;
658 | }
659 | ```
660 |
661 | 函数常量只能是标量或向量类型。对函数常量使用用户自定义的类型、或标量或向量数组、或函数常量会导致编译错误。
662 |
663 | #### 4.10.1.2
664 |
665 |
666 | ## 4.11
667 |
668 | > macOS: `viewport_array_index`属性自v2.0开始支持。
669 | > iOS: `viewport_array_index`属性自v2.1开始支持。
670 |
671 | 将`[[viewport_array_index]]`指定为
672 |
673 | 您必须为基元中的每个顶点返回相同的`[[viewport_array_index]]`值。如果值不同,则传递给片段函数的行为和值未定义。相同的行为适用于细分生成的基元。
674 |
675 | ## 4.12 附加限制
676 |
677 | * 从顶点函数写入缓冲区不保证对来自给定图元的相关片段函数的读取可见。
678 | * 如果顶点函数写入缓冲区,则其返回类型必须是`void`。
679 | * 顶点或片段函数的返回类型不能包含打包向量类型、矩阵类型、结构体类型、引用或指定类型的指针的元素。
680 | * 使用`stage_in`属性声明的片段函数的输入数量是有限制的。不同功能集的输入限制不同,具体限制在[https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf](https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf) 中的Metal功能列表中列出。(输入向量计为`n`个输入标量,其中`n`是向量中的维数。)
681 | * 图形或内核函数的参数的类型不能是派生类。此外,使用`stage_in`属性声明的图形函数的参数类型不能是派生类。
--------------------------------------------------------------------------------
/ch05.md:
--------------------------------------------------------------------------------
1 | # 5. Metal 标准库
2 |
3 | 本章节介绍Metal标准库中支持的函数。
4 |
5 | ## 5.1 命名空间与头文件
6 |
7 | Metal标准库函数和枚举被定义在`metal`命名空间下。除了在Metal标准库函数中的头文件之外,引用头文件``也可以访问所有Metal标准库支持的函数。
8 |
9 | ## 5.2 通用函数
10 |
11 | 下表的函数定义在Metal标准库头文件 ``。其中`T`是标量、向量或者浮点类型。
12 |
13 | | 内置通用函数 | 描述 |
14 | | -- | -- |
15 | | T clamp(T x, T minval, T maxval) | 返回 fmin(fmax(x, minval), maxval). 如果 minval > maxval,返回结果未定义。|
16 | | T mix(T x, T y, T a) | 返回 |
17 | | T saturate(T x) | |
18 | | T sign(T x) | 如果 x>0, 返回1.0;如果 x= -0.0,返回-0.0;如果 x= +0.0,返回 +0.0;如果 x<0,返回-1.0. |
19 | | T smoothstep(T edge0, T edge1, T
20 | x) | 如果 x <= edge0 返回0.0, 如果 |
21 | | T step(T edge, T x) | 如果 x < edge,返回0.0,否则返回 1.0 |
22 |
23 | 对于单精度的浮点类型,Metal同样支持
24 |
25 | ## 5.3 整型函数
26 |
27 |
28 | | 内置整型函数 | 描述 |
29 | | ------- | ------- |
30 | | T abs(T x) | 返回 |x| |
31 | | Tu absdiff(T x, T y) | 返回 |x-y| without modulo overflow |
32 | | T addsat(T x, T y) | 返回 x + y 并且对结果 |
33 | | `T clamp(T x, T minval, T maxval)` | Returns min(max(x, minval), maxval).
Results are undefined if minval > maxval. |
34 | | `T clz(T x)` | Returns the number of leading 0-bits in `x`, starting at the most significant bit position. If `x` is 0, returns the size in bits of the type of x or component type of `x`, if `x` is a vector |
35 | | `T ctz(T x)` | Returns the count of trailing 0-bits in x. If x is 0, returns the size in bits of the type ofxor ifxis a vector, the component type of x. |
36 | | `T extract_bits(T x, uint offset, uint bits)`
All OS: since v1.2 | Extract bits [offset, offset+bits-1] from x, returning them in the least significant bits of the result.
For unsigned data types, the most significant bits of the result are set to zero. For signed data types, the most significant bits are set to the value of bit offset+bits-1.
If bits is zero, the result is zero. If the sum of offset and bits is greater than the number of bits used to store the operand, the result is undefined. |
37 | | `T hadd(T x, T y)` | Returns `(x + y) >> 1`. The intermediate sum does not modulo overflow. |
38 |
39 |
40 | mul24 shall only be used if x and y are signed integers and x and y are in the range [-2^23, 2^23 -1], or if x and y are unsigned integers and x and y are in the range [0, 2^24 -1]. If x and y are not in this range, the multiplication result is implementation-defined.
41 |
42 |
43 | ## 5.4 关系函数
44 |
45 | 表18中的关系函数位于Metal标准库,并在头文件``中定义。`T`是浮点类型的标量或向量。Ti是整型或布尔类型的标量或向量。Tb只代表布尔类型的标量或向量。
46 |
47 | ##### 表18 Metal标准库中的关系函数
48 |
49 | | 内置关系型函数 | 描述 |
50 | | ------ | ------ |
51 | | bool all(Tb x) | 仅当`x`的所有组件都为true时才返回true |
52 | | bool any(Tb x) | 仅当`x`的任一组件都为true时才返回true |
53 | | Tb isfinite(T x) | 检查是否是有限值 |
54 | | Tb isinf(T x) | 检查是否为正无穷大或负无穷大 |
55 | | Tb isnan(T x) | 检查是否是NaN |
56 | | Tb isnormal(T x) | 检查是否为归一化 |
57 | | Tb isordered(T x, T y) | 检查参数是否有序.。`isordered()` 接受参数`x`和`y`并返回结果 `(x == x) && (y == y)` |
58 | | Tb isunordered(T x, T y) | 检查参数是否无序。 `isunordered()` 接受参数`x`和`y`并返回结果,如果`x`或`y`是NaN,则返回`true`;否则返回`false` |
59 | | Tb not(Tb x) | 返回`x`的分类逻辑补码 |
60 | | T select(T a, T b, Tb c)
Ti select(Ti a, Ti b, Tb c) | 对于向量类型的每个组件,`result[i] = c[i] ? b[i] : a[i]`
对于标量类型,
`result = c ? b : a` |
61 | | Tb signbit(T x) | 检查符号位。 如果为`x`中的浮点值设置符号位,则返回`true`; 否则返回`false`。|
62 |
63 | ## 5.5 数学函数
64 |
65 | 表19中的数学函数位于Metal标准库中,并在头文件``中定义。`T`是浮点型标量货向量。Ti仅指整型标量或向量。
66 |
67 | | 内置数学函数 | 描述 |
68 | | ------ | ------ |
69 | | `T acos(T x)` | 计算x的反余弦 |
70 | | `T acosh(T x)`| 计算x的反双曲余弦值 |
71 | | `T asin(T x)` |计算x的反正弦值 |
72 | | `T asinh(T x)` | 计算x的反双曲正弦 |
73 | | `T atan(T y_over_x)` | 计算x的反正切 |
74 | | `T atan2(T y, T x)` | 计算y在x上的反正切 |
75 | | `T atanh(T x)` | 计算x的双曲正切 |
76 | | `T ceil(T x)` | 使用舍入到正无限舍入模式将x舍入为整数值 |
77 | | `T copysign(T x, T y)` | Return x with its sign changed to match the sign of y. |
78 | | `T cos(T x)` | 计算x的余弦值 |
79 | | `T cosh(T x)` | 计算x的双曲余弦值 |
80 | | `T cospi(T x)` | 计算 `cos(πx)` |
81 | | `T divide(T x, T y)` | 计算 x / y |
82 | | `T exp(T x)` | Exponential base e function. |
83 | | `T exp2(T x)`| Exponential base 2 function. |
84 | | `T exp10(T x)` | Exponential base 10 function. |
85 | | `T fabs(T x)`
`T abs(T x)` | Compute absolute value of a floating-point number. |
86 | | `T fdim(T x, T y)`| x – y if x > y; +0 if x <= y. |
87 | | `T floor(T x)` | Round x to integral value using the round to negative infinity rounding mode. |
88 | | `T fma(T a, T b, T c)` | Returns the correctly rounded floating-point representation of the sum of c with the infinitely precise product of a and b. Rounding of intermediate products shall not occur. Edge case behavior is per the IEEE 754-2008 standard. |
89 |
90 |
91 | ## 5.6 矩阵函数
92 |
93 | 表22中的函数位于Metal标准库中,并在头文件``中定义。`T`是`float`或`half`。
94 |
95 | | 内置矩阵函数 | 描述 |
96 | | ------ | ------ |
97 | | `float determinant(floatnxn)`
`half determinant(halfnxn)` | 计算矩阵的行列式。矩阵必须是方阵。 |
98 | | `floatmxn transpose(floatnxm)`
`halfmxn transpose(halfnxm)` | 转置矩阵。 |
99 |
100 | 例如:
101 |
102 | ```metal
103 | float4x4 mA;
104 | float det = determinant(mA);
105 | ```
106 |
107 | ## 5.7 几何函数
108 |
109 | | 内置几何函数 | 描述 |
110 | | ---------- | ---------- |
111 | | T cross(T x, T y) | Return the cross product of x and y.
T must be a 3-component vector type. |
112 | | Ts distance(T x, T y) | Return the distance between x and y, i.e., length(x-y) |
113 | | Ts distance_squared(T x, T y) | Return the square of the distance between x and y. |
114 | | Ts dot(T x, T y) | Return the dot product of x and y, i.e., x[0] * y[0] + x[1] * y[1] + ... |
115 | | T faceforward(T N, T I, T Nref) | If dot(Nref, I) < 0.0 return N, otherwise return –N. |
116 | | Ts length(T x) | Return the length of vector x, i.e., sqrt(x[0]2 + x[1]2 + ...) |
117 | | Ts length_squared(T x) | Return the square of the length of vector x, i.e., (x[0]2 + x[1]2 + ...) |
118 | | T normalize(T x) | Returns a vector in the same direction as x but with a length of 1. |
119 | | T reflect(T I, T N) | For the incident vector I and surface orientation N, returns the reflection direction: I – 2 * dot(N, I) * N
In order to achieve the desired result, N must be normalized. |
120 | | T refract(T I, T N, Ts eta) | For the incident vector I and surface normal N, and the ratio of indices of refraction eta, return the refraction vector.
The input parameters for the incident vector I and the surface normal N must already be normalized to get the desired results. |
121 |
122 | xx
123 |
124 | ## 5.8 计算函数
125 |
126 | 第5.8节及其子章节中的函数只能从内核函数调用,并在头文件``中定义。
127 |
128 | | mem_flags | 描述 |
129 | | ----- | ----- |
130 | | `mem_none` | No memory fence is applied, and threadgroup_barrier acts only as an execution barrier. |
131 | | `mem_device` | Ensure correct ordering of memory operations to device memory. |
132 | | `mem_threadgroup` | Ensure correct ordering of memory operations to threadgroup memory for threads in a threadgroup. |
133 | | `mem_texture`
macOS: 从v1.2开始
iOS: 从 v2.0开始 | Ensure correct ordering of memory operations to texture memory for threads in a threadgroup. |
134 |
135 | ## 5.9 图形函数
136 |
137 |
138 | 本节及其小节列出了可由片段和顶点函数调用的图形函数集。这些在头文件``中定义。
139 |
140 | ### 5.9.1 片段函数
141 |
142 | #### 5.9.1.1
143 |
144 | ## 5.10 纹理函数
145 |
146 | ### 5.10.1 一维纹理
147 |
148 |
149 | Tv sample(sampler s, float coord) const
150 |
151 |
152 |
153 | ### 5.10.9 2D深度纹理
154 |
155 | 如下的数据类型以及对应的构造函数可以用来指定多样的采样选项:
156 |
157 | ```metal
158 | bias(float value)
159 | level(float lod)
160 | gradient2d(float2 dPdx, float2 dPdy)
161 | ```
162 |
163 | 如下的成员函数可以用来对2D深度纹理进行采样。
164 |
165 | ```metal
166 | T sample(sampler s, float2 coord, int2 offset = int2(0)) const
167 | T sample(sampler s, float2 coord, lod_options options, int2 offset =
168 | int2(0)) const
169 | ```
170 |
171 | `lod_options`必须是 `bias`、`level` 或者 `gradient2d`中的一种类型。
172 |
173 | 如下的成员函数可以用来对2D深度纹理进行采样,以及和单维组进行数值比较。
174 |
175 | ```metal
176 | T sample_compare(sampler s, float2 coord, float compare_value, int2 offset
177 | = int2(0)) const
178 | T sample_compare(sampler s, float2 coord, float compare_value, lod_options
179 | options, int2 offset = int2(0)) const
180 | ```
181 |
182 | `lod_options`必须是 `bias`、`level` 或者 `gradient2d`中的一种类型。`T`必须是`float`类型。
183 |
184 | `sample_compare`
185 |
186 |
187 | ### 5.10.10 2D深度纹理数组
188 |
189 | ```metal
190 | T sample(sampler s, float2 coord, uint array, int2 offset = int2(0)) const
191 | T sample(sampler s, float2 coord, uint array, lod_options options, int2
192 | offset = int2(0)) const
193 | ```
194 |
195 | `lod_options`必须是 `bias`、`level` 或者 `gradient2d`中的一种类型。
196 |
197 |
198 | ### 5.10.11 立方深度纹理(Cube Depth Texture)
199 |
200 | ```metal
201 | bias(float value)
202 | level(float lod)
203 | gradientcube(float3 dPdx, float3 dPdy)
204 | ```
205 |
206 | 如下的成员函数可以用来对立方深度纹理进行采样。
207 |
208 | ```metal
209 | T sample(sampler s, float3 coord) const
210 | T sample(sampler s, float3 coord, lod_options options) const
211 | ```
212 |
213 | `load_options` 必须是 `bias`、`level` 或者 `gradientcube`中的一种类型。
214 |
215 |
216 | 如下的
217 |
218 | ```metal
219 | T sample_compare(sampler s, float3 coord, float compare_value) const
220 | T sample_compare(sampler s, float3 coord, float compare_value, lod_options
221 | options) const
222 | ```
223 |
224 | `load_options` 必须是 `bias`、`level` 或者 `gradientcube`中的一种类型。`T`必须是`float`。
225 |
226 |
227 | ```metal
228 | T read(uint2 coord, uint face, uint lod = 0) const
229 | T read(ushort2 coord, ushort face, ushort lod = 0) const
230 | ```
231 |
232 |
233 |
--------------------------------------------------------------------------------
/ch06.md:
--------------------------------------------------------------------------------
1 | # 6 编译器和预处理器
2 |
3 | Metal编译器可以在线使用(比如用合适的API来编译Metal源文件),也可以离线使用。Metal源文件离线编译
4 |
5 | 本章介绍了Metal编译器支持的编译器选项,他们为预处理器选项、数学内置函数选项、控制优化选项和其他选项。在线和离线Metal编译器都支持这些选项。
6 |
7 | ## 6.1 预处理编译器选项
8 |
9 | 这些选项控制每个程序源实际运行前的Metal预处理器。
10 |
11 | ```
12 | -D name
13 | ```
14 |
15 | 预定义 *name* 为宏。
16 |
17 | ```metal
18 | -D name=definition
19 | ```
20 | `#define`指令。此选项允许开发人员编译Metal代码以更改启用或禁用的功能。
21 |
22 | ```metal
23 | -I dir
24 | ```
25 |
26 | 将目录 *dir* 添加到要搜索头文件的目录列表中。 此选项仅适用于离线编译器。
27 |
28 |
29 | ## 6.2 预处理定义
30 |
31 | Metal编译器默认内置许多预处理器定义,包括:
32 |
33 | ```
34 | __METAL_VERSION__ // Metal SDK 版本
35 | __METAL_MACOS__ // 如果用MacOS 编译需要设置
36 | __METAL_IOS__ // 如果用iOS编译需要设置
37 | ```
38 | 你可以使用定义有条件的使用一些仅在之后的SDK(通过比较版本号)或特定平台(例如,macOS或iOS)上可用的着色语言功能。
39 |
40 | 版本号实现为 主要次要补丁 版本号。例如,对于v1.2,补丁0,`__METAL_VERSION__`为120;对于v2.1,补丁1,`__METAL_VERSION__`211。
41 |
42 | 如果要有条件地包含使用v2.0中引入的功能代码,可以在代码中使用预处理器定义,如下所示:
43 |
44 | ```metal
45 | #if __METAL_VERSION__ >= 200
46 | // code that requires features introduced in v2.0 6
47 | #endif
48 | ```
49 |
50 | ## 6.3 数学内在编译选项
51 |
52 | 以下选项可以控制有关浮点运算的编译器行为,在速度和正确性之间进行权衡。
53 |
54 | ```metal
55 | -ffast-math (default)
56 | -fno-fast-math
57 | ```
58 |
59 | 这些选项启用(默认)或禁用可能违反IEEE 754标准的浮点运算优化。它们还为单精度浮点标量和矢量类型启用或禁用数学函数的高精度变量。
60 |
61 | 浮点运算的优化包括:
62 |
63 | * 无NaNs - 允许优化假设参数和结果不是NaN。
64 | * 无无穷大 - 允许优化假设参数和结果不是正的或负的无穷大。
65 | * 无签名零 - 允许优化将零参数和结果的符号视为无关紧要。
66 | * 允许倒数 - 允许优化使用参数的倒数不是执行除法。
67 | * 快速 - 允许代数等效的转换,即重新关联可能会显著改变结果的浮点运算。
68 |
69 |
70 | ## 6.4 编译器选项控制语言版本
71 |
72 | 以下选项控制编译器接受的统一图像/计算语言的版本。
73 |
74 | ```metal
75 | -std=
76 | ```
77 |
78 | 确定要使用的语言版本。必须提供选项的值,该值必须是以下值之一:
79 |
80 | * `ios-metal1.0` – 支持 iOS 8.0 Metal 版本1.0
81 | * `ios-metal1.1` – 支持 iOS 9.0 Metal 版本1.1
82 | * `ios-metal1.2` – 支持 iOS 10.0 Metal 版本1.2
83 | * `ios-metal2.0` – 支持 iOS 11.0 Metal 版本2.0
84 | * `ios-metal2.1` – 支持 iOS 12.0 Metal 版本2.1
85 | * `osx-metal1.1` – 支持 macOS 10.11 Metal 版本1.1
86 | * `osx-metal1.2` – 支持 macOS 10.12 Metal 版本1.2
87 | * `osx-metal2.0` – 支持 macOS 10.13 Metal 版本2.0
88 | * `osx-metal2.1` – 支持 macOS 10.14 Metal 版本2.1
89 |
90 | ## 6.5 显示或隐藏警告的编译选项
91 |
92 | 下列的选项可以用来处理警告。
93 |
94 | ```
95 | -Werror
96 | ```
97 |
98 | 将所有的警告转成错误。
99 |
100 | ```metal
101 | -W
102 | ```
103 |
104 | 屏蔽所有的警告消息。
105 |
106 |
--------------------------------------------------------------------------------
/ch07.md:
--------------------------------------------------------------------------------
1 | # 7 数字合规性
2 |
3 | 本章介绍Metal如何在数学运算中表示浮点数。Metal符合IEEE 754标准的子集。
4 |
5 | ## 7.1 INF、NaN 与 非归一化数字
6 |
7 | 单精度和半精度的浮点类型数字必须支持INF。
8 |
9 | 对于单精度和半精度浮点数,必须支持NaNs
10 | 快速数学禁用)。如果启用快速数学处理NaN或INF的行为(作为输入或
11 | 输出)未定义。不支持信令NaN。
12 |
13 |
14 | ## 7.2 取整方式
15 |
16 | ## 7.3 浮点异常
17 |
18 | Metal中禁用浮点异常。
19 |
20 | ## 7.4 ULP的相对误差
21 |
22 | 表36描述了作为ULP值给出的单精度浮点基本算术运算和数学函数的最小精度。用于计算算术运算的ULP值的参考值是无限精确的结果。
23 |
24 | | 数学函数 | 描述 |
25 | | -- | -- |
26 | | x + y | 正确取整 |
27 | | x - y | 正确取整 |
28 | | x * y | 正确取整 |
29 | | 1.0 / x | 正确取整 |
30 | | x / y | 正确取整 |
31 | | acos | <=4 ulp |
32 | | acosh | <= 4 ulp |
33 | | asin | <= 4 ulp |
34 | | asinh | <= 4 ulp |
35 | | atan | <= 5 ulp |
36 | | atan2 | <= 6 ulp |
37 | | atanh | <= 5 ulp |
38 | | ceil | Correctly rounded |
39 | | copysign | 0 ulp |
40 | | cos | <= 4 ulp |
41 | | cosh | <= 4 ulp |
42 | | cospi | <= 4 ulp |
43 | | exp | <= 4 ulp |
44 | | exp2 | <= 4 ulp |
45 | | exp10 | <= 4 ulp |
46 | | fabs | 0 ulp |
47 | | fdim | Correctly rounded |
48 | | floor | Correctly rounded |
49 | | fma | Correctly rounded |
50 | | fmax | 0 ulp |
51 | | fmin | 0 ulp |
52 | | fmod | 0 ulp |
53 | | fract | Correctly rounded |
54 | | frexp | 0 ulp |
55 | | ilogb | 0 ulp |
56 | | ldexp | Correctly rounded |
57 | | log | <= 4 ulp |
58 | | log2 | <= 4 ulp |
59 | | log10 | <= 4 ulp |
60 | | modf | 0 ulp |
61 | | pow | <= 16 ulp |
62 | | powr | <= 16 ulp |
63 | | rint | Correctly rounded |
64 | | round | Correctly rounded |
65 | | rsqrt | Correctly rounded |
66 | | sin | <= 4 ulp |
67 | | sincos | <= 4 ulp |
68 | | sinh | <= 4 ulp |
69 | | sinpi | <= 4 ulp |
70 | | sqrt | Correctly rounded |
71 | | tan | <= 6 ulp |
72 | | tanpi | <= 6 ulp |
73 | | tanh | <= 5 ulp |
74 | | trunc | Correctly rounded |
75 |
76 | 表37
77 |
78 | | 数学函数 | 值 |
79 | | --- | --- |
80 | | x+y | Correctly rounded |
81 | | x-y | Correctly rounded |
82 | | x*y | Correctly rounded |
83 | | 1.0 / x | <= 1 ulp for x in the domain of 2-126 to 2126 |
84 | | x/y | <= 2.5 ulp for y in the domain of 2-126 to 2126 |
85 | | acos(x) | <= 5 ulp for x in the domain [-1, 1] |
86 | | acosh(x) | Implemented as log(x + sqrt(x * x – 1.0)) |
87 | | asin(x) | <= 5 ulp for for x in the domain [-1, 1] and |x| >= 2-125 |
88 | | asinh(x) | Implemented as log(x + sqrt(x * x + 1.0)) |
89 | | atan(x) | <= 5 ulp |
90 | | atanh(x) | Implemented as 0.5 * (log(1.0 + x) / log(1.0 – x)) |
91 | | atan2(y, x) | Implemented as atan(y / x) for x > 0, atan(y / x) + M_PI_F for x < 0 and y > 0, atan(y / x) – M_PI_F for x < 0 and y < 0 and is undefined if y = 0 and x = 0. |
92 | | cos(x) | For x in the domain [-pi, pi], the maximum absolute error is <= 2-13 and larger otherwise. |
93 | | cosh(x) | Implemented as 0.5 * (exp(x) + exp(-x)) |
94 | | cospi(x) | For x in the domain [-1, 1], the maximum absolute error is <= 2-13 and larger otherwise. |
95 | | exp(x) | <= 3 + floor(fabs(2 * x)) ulp |
96 | | exp2(x) | <= 3 + floor(fabs(2 * x)) ulp |
97 | | exp10(x) | Implemented as exp2(x * log2(10)) |
98 | | fabs | 0 ulp |
99 | | fdim | Correctly rounded |
100 | | floor | Correctly rounded |
101 | | fma | Correctly rounded |
102 | | fmax | 0 ulp |
103 | | fmin | 0 ulp |
104 | | fmod | 0 ulp |
105 | | fract | Correctly rounded |
106 | | frexp | 0 ulp |
107 | | ilogb | 0 ulp |
108 | | ldexp | Correctly rounded |
109 | | log(x) | For x in the domain [0.5, 2], the maximum absolute error is <= 2-21; otherwise the maximum error is <= 3 ulp if x > 0; otherwise the results are undefined. |
110 | | log2(x) | For x in the domain [0.5, 2], the maximum absolute error is <= 2-22; otherwise the maximum error is <= 2 ulp if x > 0; otherwise the results are undefined. |
111 | | log10(x) | Implemented as log2(x) * log10(2) |
112 | | modf | 0 ulp |
113 | | pow(x, y) | Implemented as exp2(y * log2(x)). Undefined for x = 0 and y = 0. |
114 | | powr(x, y) | Implemented as exp2(y * log2(x)). Undefined for x = 0 and y = 0. |
115 | | rint | Correctly rounded |
116 | | round(x) | Correctly rounded |
117 | | rsqrt | <= 2 ulp |
118 | | sin(x) | For x in the domain [-pi, pi], the maximum absolute error is <= 2-13 and larger otherwise. |
119 | | sinh(x) | Implemented as 0.5 * (exp(x) – exp(-x)) |
120 | | sincos(x) | ULP values as defined for sin(x) and cos(x) |
121 | | sinpi(x) | For x in the domain [-1, 1], the maximum absolute error is <= 2-13 and larger otherwise. |
122 | | sqrt(x) | Implemented as x * rsqrt(x) with special cases handled correctly. |
123 | | tan(x) | Implemented as sin(x) * (1.0 / cos(x)) |
124 | | tanh(x) | Implemented as (t – 1.0)/(t + 1.0) where t = exp(2.0 * x) |
125 | | tanpi(x) | Implemented as tan(x * pi) |
126 | | trunc | Correctly rounded |
127 |
128 | 表38
129 |
130 | | 数学函数 | 值 |
131 | | --- | --- |
132 | | x+y | Correctly rounded |
133 | | x-y | Correctly rounded |
134 | | x*y | Correctly rounded |
135 | | 1.0 / x | Correctly rounded |
136 | | x/y | Correctly rounded |
137 | | acos(x) | <= 1 ulp |
138 | | acosh(x) | <= 1 ulp |
139 | | asin(x) | <= 1 ulp |
140 | | asinh(x) | <= 1 ulp |
141 | | atan(x) | <= 1 ulp |
142 | | atanh(x) | <= 1 ulp |
143 | | atan2(y, x) | <= 1 ulp |
144 | | cos(x) | <= 1 ulp |
145 | | cosh(x) | <= 1 ulp |
146 | | cospi(x) | <= 1 ulp |
147 | | exp(x) | <= 1 ulp |
148 | | exp2(x) | <= 1 ulp |
149 | | exp10(x) | <= 1 ulp |
150 | | fabs | 0 ulp |
151 | | fdim | Correctly rounded |
152 | | floor | Correctly rounded |
153 | | fma | Correctly rounded |
154 | | fmax | 0 ulp |
155 | | fmin | 0 ulp |
156 | | fmod | 0 ulp |
157 | | fract | Correctly rounded |
158 | | frexp | 0 ulp |
159 | | ilogb | 0 ulp |
160 | | ldexp | Correctly rounded |
161 | | log(x) | <= 1 ulp |
162 | | log2(x) | <= 1 ulp |
163 | | log10(x) | <= 1 ulp |
164 | | modf | 0 ulp |
165 | | pow(x, y) | <= 2 ulp |
166 | | powr(x, y) | <= 2 ulp |
167 | | rint | Correctly rounded |
168 | | round(x) | Correctly rounded |
169 | | rsqrt | Correctly rounded |
170 | | sin(x) | <= 1 ulp |
171 | | sinh(x) | <= 1 ulp |
172 | | sincos(x) | ULP values as defined for sin(x) and cos(x) |
173 | | sinpi(x) | <= 1 ulp |
174 | | sqrt(x) | Correctly rounded |
175 | | tan(x) | <= 1 ulp |
176 | | tanh(x) | <= 1 ulp |
177 | | tanpi(x) | <= 1 ulp |
178 | | trunc | Correctly rounded |
179 |
180 | ## 7.5
181 |
182 |
183 | ## 7.6 浮点型和整型的转换规则
184 |
185 | 舍入到零模式用于从浮点类型到整数类型的转换。舍入到最接近的偶数或舍入到零的模式用于浮点或整数类型到浮点类型的转换。
186 |
187 | 从半精度到浮点数的转换是无损的。使用
188 |
189 | ## 7.7 纹理寻址和转换规则
190 |
191 | ### 7.7.1
192 |
193 | #### 7.7.1.1
194 |
195 | | 转换自 | 转换到归一化浮点数的规则 | 临界情况 |
196 | | --- | --- | --- |
197 | | 1位归一化无符号整数 | `float(c)` | 0必须转换为0.0
1必须转换为1.0 |
198 | | 2位归一化无符号整数 | `float(c) / 3.0` | 0必须转换为0.0
3必须转换为1.0 |
199 | | 4位归一化无符号整数 | `float(c) / 15.0` | 0必须转换为0.0
15必须转换为1.0 |
200 | | 5位归一化无符号整数 | `float(c) / 31.0` | 0必须转换为0.0
31必须转换为1.0 |
201 | | 6位归一化无符号整数 | `float(c) / 63.0` | 0必须转换为0.0
63必须转换为1.0 |
202 | | 8位归一化无符号整数 | `float(c) / 255.0` | 0必须转换为0.0
255必须转换为1.0 |
203 | | 10位归一化无符号整数 | `float(c) / 1023.0` | 0必须转换为0.0
1023必须转换为1.0 |
204 | | 16位归一化无符号整数 | `float(c) / 65535.0` | 0必须转换为0.0
65535必须转换为1.0 |
205 | | 8位归一化有符号整数 | `max(-1.0, float(c)/127.0)` | -128和-127必须转换为-1.0
0必须转换为0.0
127必须转换为1.0 |
206 | | 16位归一化有符号整数 | `max(-1.0, float(c)/32767.0)` | -32768和-32767 必须转换为-1.0
0必须转换为0.0
32767必须转换为1.0 |
207 |
208 |
209 | #### 7.7.1.2
210 |
211 |
212 |
213 | ### 7.7.6 有符号与无符号的整型像素数据类型的转换规则
214 |
215 |
216 | | 转换自 | 转换为 | 转换规则 |
217 | | --- | --- | --- |
218 | | 32位有符号整数 | 8位有符号整数 | `result = convert_char_saturate(val)` |
219 | | 32位有符号整数 | 16位有符号整数 | `result = convert_short_saturate(val)` |
220 | | 32位无符号整数 | 8位无符号整数 | `result = convert_uchar_saturate(val)` |
221 | | 32位无符号整数 | 16位无符号整数 | `result = convert_ushort_saturate(val)` |
222 |
223 |
224 | ### 7.7.7 sRGBA 与 sBGRA 纹理的转换规则
225 |
226 | 当对一个SRGB纹理进行采样时,
227 |
228 | ```metal
229 | if (c <= 0.04045)
230 | result = c / 12.92;
231 | else
232 | result = powr((c + 0.055) / 1.055, 2.4);
233 | ```
234 |
235 |
236 | 以下是转换线性RGB浮点颜色值(称为c)归一化的8位无符号整数sRGB值。
237 |
238 | ```metal
239 | if (isnan(c)) c = 0.0;
240 | if (c > 1.0)
241 | c = 1.0;
242 | else if (c < 0.0)
243 | c = 0.0;
244 | else if (c < 0.0031308)
245 | c = 12.92 * c;
246 | else
247 | c = 1.055 * powr(c, 1.0/2.4) - 0.055;
248 |
249 | convert to integer scale i.e. c = c * 255.0
250 | convert to integer:
251 |
252 | c = c + 0.5
253 | drop the decimal fraction, and the remaining
254 | floating-point(integral) value is converted
255 | directly to an integer.
256 | ```
257 |
258 | 上述转换的精度应为:
259 |
260 | ```metal
261 | fabs(r – rorig) <= 0.5
262 | ```
--------------------------------------------------------------------------------