Metal 框架支持 GPU 加速高级 3D 图像渲染,以及数据并行计算工作。Metal 提供了先进合理的 API,它不仅为图形的组织、处理和呈现,也为计算命令以及为这些命令相关的数据和资源的管理,提供了细粒度和底层的控制。Metal 的主要目的是最小化 GPU 工作时 CPU 所要的消耗。
Metal的特点
Metal特点.png
- 低CPU开销
- 最大程度发挥GPU性能
- 最大限度的提高CPU/GPU 的并发性
- 有效的资源管理
图形管道 graphics pipelines
Metal图形管道.png
- 通过CPU将顶点数据传入定点着色器
- 进行图元装配
- 进行光栅化
- 将颜色值传入片元着色器
- 将数据传入帧缓冲区,显示到屏幕上
Metal的使用建议
Apple对于Metal的使用提出了几点建议
- Separate Your Rendering Loop 分开渲染循环。Apple建议开发者将渲染循环单独封装在一个类中。
- Respond to View Events 响应视图的事件,即MTKViewDelegate协议,也需要放在自定义的渲染循环中。
- Metal Command Objects 创建一个命令对象,即创建执行命令的GPU、与GPU交互的MTLCommandQueue对象以及MTCommandBuffer渲染缓存区湘。
Metal命令对象之间的关系
Metal命令对象之间的关系.png
- 命令缓存区(command buffer) 是从命令队列(command queue) 创建的
- 命令编码器(command encoders) 将命令编码到命令缓存区中
- 提交命令缓存区并将其发送到GPU
- GPU执⾏命令并将结果呈现为可绘制.
Metal API
顶点函数声明
vertex RasterizerData
vertexShader(uint vertexID [[vertex_id]],
constant CCVertex *vertices [[buffer(CCVertexInputIndexVertices)]],
constant vector_uint2 *viewportSizePointer [[buffer(CCVertexInputIndexViewportSize)]])
//vertexID : 顶点索引 [[vertex_id]] 属性修饰符
//vertices : 顶点数组 [[buffer(CCVertexInputIndexVertices)]] 属性修饰符
//viewportSizePointer : 视⼝⼤⼩ [[buffer(CCVertexInputIndexViewportSize)]] 属性修饰符
顶点函数实现
1. 执⾏坐标系转换,将⽣成的顶点剪辑空间位置写⼊`out.clipSpacePosition`返回值。
2. 将顶点颜⾊传递给`out.color`返回值。
RasterizerData out;
out.clipSpacePosition = vector_float4(0.0, 0.0, 0.0, 1.0);
float2 pixelSpacePosition = vertices[vertexID].position.xy;
vector_float2 viewportSize = vector_float2(*viewportSizePointer);
out.clipSpacePosition.xy = pixelSpacePosition / (viewportSize / 2.0);
out.color = vertices[vertexID].color;
return out;
⽚元函数实现
主要任务是处理传⼊的⽚段数据并计算可绘制像素的颜⾊值。
fragment float4 fragmentShader(RasterizerData in [[stage_in]])
{
return in.color;
}
获取函数库并创建管道
//在项目中加载所有的(.metal)着色器文件,从bundle中获取.metal文件
id<MTLLibrary> defaultLibrary = [_device newDefaultLibrary];
//从库中加载顶点函数
id<MTLFunction> vertexFunction = [defaultLibrary newFunctionWithName:@"vertexShader"];
//从库中加载片元函数
id<MTLFunction> fragmentFunction = [defaultLibrary newFunctionWithName:@"fragmentShader"];
//配置用于创建管道状态的管道
MTLRenderPipelineDescriptor *pipelineStateDescriptor = [[MTLRenderPipelineDescriptor alloc] init];
//管道名称
pipelineStateDescriptor.label = @"Simple Pipeline";
//可编程函数,用于处理渲染过程中的各个顶点
pipelineStateDescriptor.vertexFunction = vertexFunction;
//可编程函数,用于处理渲染过程中各个片段/片元
pipelineStateDescriptor.fragmentFunction = fragmentFunction;
//一组存储颜色数据的组件
pipelineStateDescriptor.colorAttachments[0].pixelFormat = mtkView.colorPixelFormat;
//同步创建并返回渲染管线状态对象
_pipelineState = [_device newRenderPipelineStateWithDescriptor:pipelineStateDescriptor error:&error];
//判断是否返回了管线状态对象
if (!_pipelineState)
{
//如果我们没有正确设置管道描述符,则管道状态创建可能失败
NSLog(@"Failed to created pipeline state, error %@", error);
return nil;
}
//创建命令队列
_commandQueue = [_device newCommandQueue];