版本记录
版本号 | 时间 |
---|---|
V1.0 | 2018.10.08 星期一 |
前言
很多做视频和图像的,相信对这个框架都不是很陌生,它渲染高级3D图形,并使用GPU执行数据并行计算。接下来的几篇我们就详细的解析这个框架。感兴趣的看下面几篇文章。
1. Metal框架详细解析(一)—— 基本概览
2. Metal框架详细解析(二) —— 器件和命令(一)
3. Metal框架详细解析(三) —— 渲染简单的2D三角形(一)
4. Metal框架详细解析(四) —— 关于GPU Family 4(一)
5. Metal框架详细解析(五) —— 关于GPU Family 4之关于Imageblocks(二)
6. Metal框架详细解析(六) —— 关于GPU Family 4之关于Tile Shading(三)
7. Metal框架详细解析(七) —— 关于GPU Family 4之关于光栅顺序组(四)
8. Metal框架详细解析(八) —— 关于GPU Family 4之关于增强的MSAA和Imageblock采样覆盖控制(五)
9. Metal框架详细解析(九) —— 关于GPU Family 4之关于线程组共享(六)
10. Metal框架详细解析(十) —— 基本组件(一)
11. Metal框架详细解析(十一) —— 基本组件之器件选择 - 图形渲染的器件选择(二)
12. Metal框架详细解析(十二) —— 基本组件之器件选择 - 计算处理的设备选择(三)
13. Metal框架详细解析(十三) —— 计算处理(一)
Hello Compute - 你好,计算
演示如何使用GPU执行数据并行计算。
Overview - 概览
在Basic Texturing示例中,您学习了如何通过将纹理应用于单个四边形来渲染2D图像。
在此示例中,您将学习如何在Metal中执行计算处理工作负载以进行图像处理。 特别是,您将学习如何使用计算处理管道和编写内核函数。
General-Purpose GPU Programming - 通用GPU编程
图形处理单元(GPU)最初设计用于以非常快速和有效的方式处理大量图形数据,例如顶点或片段。这种设计在GPU硬件架构本身中很明显,GPU架构本身具有许多处理核心,可以并行执行工作负载。
纵观GPU设计的历史,并行处理架构保持相当一致,但处理核心变得越来越可编程。这一变化使GPU能够从固定功能管道转向可编程管道,这一变化也支持通用GPU(GPGPU)编程。
在GPGPU
模型中,GPU可用于任何类型的处理任务,并且不限于图形数据。例如,GPU可用于加密,机器学习,物理或财务。在Metal中,GPGPU
工作负载称为计算处理工作负载或计算。
图形和计算工作负载不是互斥的;Metal提供统一的框架和语言,可实现图形和计算工作负载的无缝集成。实际上,此示例通过以下方式演示了此集成:
- 1) 使用将彩色图像转换为灰度图像的计算管道
- 2) 使用图形管道将灰度图像渲染为四边形表面
Create a Compute Processing Pipeline - 创建计算处理管道
计算处理流水线仅由一个阶段组成,即可编程内核函数,它执行计算传递。 内核函数直接读取和写入资源,而不通过各种管道阶段传递资源数据。
MTLComputePipelineState
对象表示计算处理管道。 与图形渲染管道不同,您可以使用单个内核函数创建MTLComputePipelineState
对象,而无需使用管道描述符。
// Load the kernel function from the library
id<MTLFunction> kernelFunction = [defaultLibrary newFunctionWithName:@"grayscaleKernel"];
// Create a compute pipeline state
_computePipelineState = [_device newComputePipelineStateWithFunction:kernelFunction
error:&error];
Write a Kernel Function - 编写内核函数
此示例将图像数据加载到纹理中,然后使用内核函数将纹理的像素从颜色转换为灰度。 内核函数独立并发处理像素。
注意:可以为CPU编写和执行等效算法。 但是,GPU解决方案更快,因为纹理的像素不需要按顺序处理。
此示例中的内核函数称为grayscaleKernel
,其签名如下所示:
kernel void
grayscaleKernel(texture2d<half, access::read> inTexture [[texture(AAPLTextureIndexInput)]],
texture2d<half, access::write> outTexture [[texture(AAPLTextureIndexOutput)]],
uint2 gid [[thread_position_in_grid]])
该函数采用以下资源参数:
-
inTexture
:包含输入颜色像素的只读2D纹理。 -
outTexture
:一种只写的2D纹理,用于存储输出灰度像素。
可以使用read()
函数读取指定读取访问限定符的纹理。可以使用write()
函数写入指定写访问限定符的纹理。
内核函数每个线程执行一次,这类似于顶点函数每个顶点执行一次的方式。线程被组织成3D网格;编码计算传递通过声明网格的大小来指定要处理的线程数。因为此示例处理2D纹理,所以线程排列在2D网格中,其中每个线程对应于唯一的纹理元素。
内核函数的gid
参数使用[[thread_position_in_grid]]
属性限定符,该限定符定位计算网格中的线程。内核函数的每次执行都有一个唯一的gid
值,使每个线程能够清晰地工作。
灰度像素对于其每个RGB分量具有相同的值。 可以通过简单地平均颜色像素的RGB分量,或者通过将某些权重应用于每个分量来计算该值。 此示例使用Rec. 709 c
亮度系数用于颜色到灰度转换。
half4 inColor = inTexture.read(gid);
half gray = dot(inColor.rgb, kRec709Luma);
outTexture.write(half4(gray, gray, gray, 1.0), gid);
Execute a Compute Pass - 执行计算传递
MTLComputeCommandEncoder
对象包含用于执行计算传递的命令,包括对内核函数及其资源的引用。 与渲染命令编码器不同,您可以在不使用传递描述符的情况下创建MTLComputeCommandEncoder
。
id<MTLComputeCommandEncoder> computeEncoder = [commandBuffer computeCommandEncoder];
[computeEncoder setComputePipelineState:_computePipelineState];
[computeEncoder setTexture:_inputTexture
atIndex:AAPLTextureIndexInput];
[computeEncoder setTexture:_outputTexture
atIndex:AAPLTextureIndexOutput];
计算传递必须指定执行内核函数的次数。 此数字对应于网格大小,该大小根据线程和线程组定义。 线程组是由内核函数并发执行的3D线程组。 在此示例中,每个线程对应一个唯一的纹理元素,网格大小必须至少为2D图像的大小。 为简单起见,此示例使用16 x 16
线程组大小,该大小足以供任何GPU使用。 但实际上,选择有效的线程组大小取决于数据的大小和特定设备的功能。
// Set the compute kernel's threadgroup size of 16x16
_threadgroupSize = MTLSizeMake(16, 16, 1);
// Calculate the number of rows and columns of threadgroups given the width of the input image
// Ensure that you cover the entire image (or more) so you process every pixel
_threadgroupCount.width = (_inputTexture.width + _threadgroupSize.width - 1) / _threadgroupSize.width;
_threadgroupCount.height = (_inputTexture.height + _threadgroupSize.height - 1) / _threadgroupSize.height;
该示例通过发出调度调用并结束计算命令的编码来最终确定计算传递。
[computeEncoder dispatchThreadgroups:_threadgroupCount
threadsPerThreadgroup:_threadgroupSize];
[computeEncoder endEncoding];
然后,该示例继续编码首先在Basic Texturing示例中引入的渲染命令。 计算传递和渲染过程的命令使用相同的灰度纹理,附加到同一命令缓冲区,并同时提交给GPU。 但是,计算过程中的灰度转换始终在渲染过程中的四重渲染之前执行。
后记
本篇主要讲述了你好,计算,感兴趣的给个赞或者关注~~~