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 EncoderData-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)")
    }
    
}

 

posted @ 2022-10-09 15:52  不停奔跑的蜗牛  阅读(360)  评论(0编辑  收藏  举报