Swift Metal渲染视频
一、基本Metal概念
Metal是iOS推出的图像渲染工具,类似于OpenGL,Metal为图形和数据并行计算工作负载提供单一,统一的编程接口和语言。 Metal使您能够更有效地集成图形和计算任务,而无需使用单独的API和着色器语言。
Metal框架提供以下内容:
- Low-overhead interface - 低开销接口。 Metal旨在消除“隐藏”性能瓶颈,例如隐式状态验证。您可以控制GPU的异步行为,以实现用于并行创建和提交命令缓冲区的高效多线程。
有关Metal命令提交的详细信息,请参阅Command Organization and Execution Model。
- Memory and resource management - 内存和资源管理。 Metal框架描述了表示GPU内存分配的缓冲区和纹理对象。纹理对象具有特定的像素格式,可用于纹理图像或附件。
有关Metal内存对象的详细信息,请参阅Resource Objects: Buffers and Textures。
- Integrated support for both graphics and compute operations - 集成了对图形和计算操作的支持。 Metal为图形和计算操作使用相同的数据结构和资源(如缓冲区,纹理和命令队列)。此外,Metal着色语言支持图形和计算函数。 Metal框架允许在运行时接口,图形着色器和计算函数之间共享资源。
有关编写使用Metal进行图形渲染或数据并行计算操作的应用程序的详细信息,请参阅Graphics Rendering: Render Command Encoder或Data-Parallel Compute Processing: Compute Command Encoder。
- Precompiled shaders - 预编译着色器。可以在构建时编译Metal着色器以及应用程序代码,然后在运行时加载。此工作流程提供了更好的代码生成以及更简单的着色器代码调试。 (Metal还支持着色器代码的运行时编译。)
有关使用Metal框架代码中的Metal着色器的详细信息,请参阅Functions and Libraries。有关Metal Shading Language Guide本身的详细信息,请参见Metal Shading Language Guide。
二、创建连接Metal文件
1、创建metal文件第一步要获取Metal设备就是手机等设备、获取命令队列
init(){
guard let device = MTLCreateSystemDefaultDevice() else{
fatalError("Could not create Metal Device")
}
self.device = device
guard let queue = self.device.makeCommandQueue() else{
fatalError("Could not create command queue")
}
self.commandQueue = queue
// let frameworkBundle = Bundle.main
// guard let metalLibraryPath = frameworkBundle.path(forResource: "default", ofType: "metallib")else{
// fatalError("Could not load library")
// }
do {
self.shaderLibrary = try device.makeDefaultLibrary(bundle: Bundle.main)
} catch {
fatalError("Could not load library")
}
}
2、创建metal文件、编写着色器代码

编写顶点着色器和片元着色器代码
//
// BlendModeConstants.metal
// DQVideoEditor
//
// Created by zhaoquan.du on 2022/9/26.
//
#include <metal_stdlib>
#include "OperationShaderTypes.h"
#include "BlendModeConstants.h"
using namespace metal;
vertex SingleInputVertexIO blendOperationVertex(const device packed_float2 *position [[ buffer(0) ]],
const device packed_float2 *texturecoord [[ buffer(1) ]],
constant float4x4& modelView [[ buffer(2) ]],
constant float4x4& projection [[ buffer(3) ]],
uint vid [[vertex_id]])
{
SingleInputVertexIO outputVertices;
outputVertices.position = projection * modelView * float4(position[vid], 0, 1.0);
outputVertices.textureCoordinate = texturecoord[vid];
return outputVertices;
}
half4 normalBlend(half3 Sca, half3 Dca, half Sa, half Da) {
half4 blendColor;
blendColor.rgb = Sca + Dca * (1.0 - Sa);
blendColor.a = Sa + Da - Sa * Da;
return blendColor;
}
half4 darken(half3 Sca, half3 Dca, half Sa, half Da) {
half4 blendColor;
blendColor.rgb = min(Sca * Da, Dca * Sa) + Sca * (1.0 - Da) + Dca * (1.0 - Sa);
blendColor.a = Sa + Da - Sa * Da;
return blendColor;
}
half4 multiply(half3 Sca, half3 Dca, half Sa, half Da) {
half4 blendColor;
blendColor.rgb = Sca * Dca + Sca * (1.0 - Da) + Dca * (1.0 - Sa);
blendColor.a = Sa + Da - Sa * Da;
return blendColor;
}
fragment half4 blendOperationFragment(SingleInputVertexIO fragmentInput [[stage_in]],
texture2d<half> inputTexture [[texture(0)]],
half4 backColor [[color(0)]],
constant int& blendMode [[ buffer(1) ]],
constant float& blendOpacity [[ buffer(2) ]])
{
constexpr sampler quadSampler;
half4 sourceColor = inputTexture.sample(quadSampler, fragmentInput.textureCoordinate);
half3 Sca = sourceColor.rgb;
half3 Dca = backColor.rgb;
half Sa = sourceColor.a;
half Da = backColor.a;
half4 blendColor;
if (blendMode == BlendModeNormal) {
blendColor = normalBlend(Sca, Dca, Sa, Da);
} else if (blendMode == BlendModeDarken) {
blendColor = darken(Sca, Dca, Sa, Da);
} else if (blendMode == BlendModeMultiply) {
blendColor = multiply(Sca, Dca, Sa, Da);
} else {
blendColor = half4(0.0, 0.0, 0.0, 1.0);
}
return mix(backColor, blendColor, blendOpacity);
}
3.创建渲染管道,保存着色器参数类型
func generateRenderPipelineState(vertexFunctionName:String, fragmentFunctionName:String, oprationName:String) -> (MTLRenderPipelineState, [String: UniformInfor], [String: UniformInfor]){
//获取顶点着色器函数
guard let vertexFunction = sharedMetalRenderingDevice.shaderLibrary.makeFunction(name: vertexFunctionName) else{
fatalError("\(oprationName):could not compile vertex function \(vertexFunctionName)")
}
//获取片元着色器函数
guard let fragmentFuntion = sharedMetalRenderingDevice.shaderLibrary.makeFunction(name: fragmentFunctionName) else{
fatalError("\(oprationName): could not compile fragment function \(fragmentFunctionName)")
}
//配置渲染管道描述符
let descriptor = MTLRenderPipelineDescriptor()
descriptor.colorAttachments[0].pixelFormat = MTLPixelFormat.bgra8Unorm
descriptor.rasterSampleCount = 1
descriptor.vertexFunction = vertexFunction
descriptor.fragmentFunction = fragmentFuntion
do {
//创建渲染管道
var reflection: MTLAutoreleasedRenderPipelineReflection?
let pipLinState = try sharedMetalRenderingDevice.device.makeRenderPipelineState(descriptor: descriptor,options: [.bufferTypeInfo, .argumentInfo],reflection: &reflection)
//获取着色器函数 参数类型,以便之后传惨
var vertexUniforms: [String: UniformInfor] = [:]
var fragmentUniforms = [String : UniformInfor]()
if #available(iOS 16.0, *) {
if let vertexBudings = reflection?.vertexBindings as? [MTLBufferBinding]{
for bufferBuding in vertexBudings {
let uniformInfor = UniformInfor(locationIndex: bufferBuding.index, dataSize: bufferBuding.bufferDataSize)
vertexUniforms[bufferBuding.name] = uniformInfor
}
}
//片元着色器中参数 banging中有 MTLTextureBinding 和 MTLBufferBinding,需要过滤一下
if let fragmentBudings = reflection?.fragmentBindings.compactMap({$0 as? MTLBufferBinding}) as? [MTLBufferBinding]{
for bufferBuding in fragmentBudings {
let uniformInfor = UniformInfor(locationIndex: bufferBuding.index, dataSize: bufferBuding.bufferDataSize)
fragmentUniforms[bufferBuding.name] = uniformInfor
}
}
} else {
if let vertexArguments = reflection?.vertexArguments {
for vertexArgument in vertexArguments where vertexArgument.type == .buffer {
let uniformInfor = UniformInfor(locationIndex: vertexArgument.index, dataSize: vertexArgument.bufferDataSize)
vertexUniforms[vertexArgument.name] = uniformInfor
}
}
if let fragmentArguments = reflection?.fragmentArguments {
for fragmentArgument in fragmentArguments where fragmentArgument.type == .buffer {
let uniformInfor = UniformInfor(locationIndex: fragmentArgument.index, dataSize: fragmentArgument.bufferDataSize)
fragmentUniforms[fragmentArgument.name] = uniformInfor
}
}
}
return (pipLinState, vertexUniforms, fragmentUniforms)
} catch {
fatalError("Could not create render pipeline state for vertex:\(vertexFunctionName), fragment:\(fragmentFunctionName), error:\(error)")
}
}

浙公网安备 33010602011771号