多媒体技术图形编程框架之Metal介绍与基本概念
所属分类:ios | 发布于 2024-12-18
你敢不敢相信,就想搞一个相机App,居然研究到了Metal,也终于接触了苹果发布会上次次都要说的GPU。
背景
图形编程是多媒体技术中最重要的一块。
图标边框框架用于解决什么问题:
1、在游戏开发中,对游戏场景/游戏任务的渲染
2、在音视频开发中,对于视频解码后的数据渲染
3、在地图引擎中,对于地图数据的渲染
4、在动画中,实现动画的绘制
5、在视频处理中,视频添加滤镜效果
6、...
目前几个主流的图形处理框架:
1、DirectX:由很多API组成,是Windows上的一个多媒体处理框架,只支持Windows系统。
2、OpenGL(Open Graphics Library):一种跨平台的图像处理框架,用于开发2D和3D图形应用程序。
3、OpenGL ES(OpenGL for Embedded Systems):是OpenGL三维图形API的子集,针对手机、PDA和游戏主机等嵌入式设备而设计的,去除了许多不必要和性能较低的API接口。
4、Metal:Apple推出的新的平台技术,该技术能为3D图像提高10倍的渲染性能。在Metal之前,苹果使用OpenGL ES,在iOS12以后,弃用了OpenGL ES,转为对Metal的支持。
Metal介绍
Metal是苹果平台上图形和计算机编程的一个强大而低级的框架。它允许您充分利用GPU(图形处理单元)的性能来执行复杂的计算和渲染任务。
官方文档:https://developer.apple.com/cn/metal/
Metal的主要用例之一是图像处理,因为它允许您以高性能和低开销实时对图像和视频执行操作。
我们将介绍Metal编程的基础知识,包括主要概念和术语,以及设置开发环境的步骤。
Metal Shader(着色器)的使用
在Metal中,Shader(着色器)是一个在GPU上运行的小程序,负责执行特定任务,例如渲染3D模型或应用图像过滤器。
Metal中有三种主要类型的着色器:Vertex shaders(顶点着色器)、Feagment shaders(片段着色器)和Compute shaders(计算着色器)
1、Vertex shaders
Vertex shaders负责将模型的3D坐标转换为2D屏幕空间坐标。它们将一组顶点数据(如位置、法线和纹理坐标)作为输入,并将一组变换后的顶点数据作为输出。
2、Fragment shaders
Fragment shaders负责确定屏幕上每个像素的最终颜色。它们从顶点着色器中获取插值顶点数据作为输入,并为每个像素返回最终颜色作为输出。
3、Compute shaders
Compute shaders是一种更通用的着色器类型,可以在GPU上执行任何类型的计算。它们通常用于图像处理和模拟等任务。
利用GPU进行计算
进行GPU计算是每个Metal应用都要使用的基本任务,整个流程大致如下:
1、编写MSL函数
2、找到一个可用的GPU
3、创建pipeline
4、创建GPU可访问的数据对象
5、创建命令缓冲区,向其写入命令
6、将缓冲区提交到命令队列
1、编写一个GPU函数
如果需要在GPU上执行计算,需要使用MSL(Metal Shading Language)编写的函数。MSL是C++的一个变体,专为GPU编程而设计。在Metal中,运行在GPU上的代码被称为shader着色器。
新建一个add.metal文件,并写入如下代码:
kernel void add_arrays(device const float* inA,
device const float* inB,
device float* result,
uint index [[thread_position_in_grid]])
{
// the for-loop is replaced with a collection of threads, each of which
// calls this function.
result[index] = inA[index] + inB[index];
}
Xcode在应用程序目标中构建所有的 .Metal 文件,并创建一个默认的Metal库,并打包到应用程序中。
为了更好的解释代码含义,写一个C语言版本的来做对比:
void add_arrays(const float* inA,
const float* inB,
float* result,
int length)
{
for (int index = 0; index < length ; index++)
{
result[index] = inA[index] + inB[index];
}
}
代码解释:
1、函数添加kernel关键字,kernel关键字将声明函数为公开GPU函数。公开函数只有你自己的应用能访问到。公开函数也不能被其他shader函数调用。同时也表明这是一个计算函数(也称为计算内核),它使用线程网格执行并行计算。
2、add_arrays函数用device关键字声明了它的三个参数,表示这些指针位于设备地址空间中。MSL为内存定义了几个不相交的地址空间。在MSL中声明指针时,必须提供一个关键字来声明其地址空间。本例使用设备地址空间来声明只有内存,以保证GPU可以读取和写入。
3、MSL版相对于C版本,删除了for循环,因为该函数现在将有计算网格中的多个线程调用。这个示例创建一个与数组尺寸完全匹配的一堆线程网格,这样数组中的每个元素都由不同的线程计算。
为了替换先前由for循环提供的索引,该函数接受一个新的索引参数,该参数使用另一个MSL关键字thread_position_in_grid,该关键字使用C++属性语法指定。这个关键字声明Metal应该为每个线程计算一个唯一的索引,并在这个参数中传递该索引。因为add_arrays使用一维网格,所以索引被定义为一个标量整数。
2、找到一个可用的GPU
在Metal中,MTLDevice对象是GPU的一个抽象,你可以使用它来和GPU通信,Metal为每个GPU创建一个MTLDevice。通过调用MTLCreateSystemDefaultDevice()来获取默认对象。在 macOS中,Mac可以有多个GPU,Metal旋转其中一个GPU作为默认值并返回该GPU的设备对象。
let device: MTLDevice = MTLCreateSystemDefaultDevice();
3、获取Metal函数引用
4、创建Metal Pipeline
5、创建命令队列
6、创建数据缓冲区和加载数据
7、创建命令缓冲区
8、创建命令编码器
9、设置Pipeline State和参数数据
10、指定线程组数量
11、执行线程组大小
12、编码计算命令提交到执行线程
13、结束计算编码
14、提交到命令缓冲区并执行
15、等待计算完成
16、从缓冲区读取结果