Metal 1

作者: f8d1cf28626a | 来源:发表于2022-07-20 05:18 被阅读0次

    Metal 1

    MetalKit

    • MTKView

    Metal 渲染管道

    • 1.VertexData 通过CPU也就是我们的程序提供顶点数据
      1. VertexProcessing(VertexShader) -> PrimitiveAssembly -> Rasterization -> FragmentProcessing(FragmentShader) -> Framebuffer
    VertexData(CPU)
    Vertex Processing(VertexShader)
    Primitive Assembly [triangles,quads,lines,points]
    Rasterization
    Fragment Processing(FragmentShader) [textures,stencil,alpha,depth]
    Framebuffer (Screen Pixels)

    MTKView 内部有一个定时器,去执行重绘的动作(可以自定义关闭或暂停)

    苹果官方建议:Saparate Your Rendering Loop (分离或者抽离你的渲染模块,这样的解释大家比较容易理解)

    MTKViewDelegate 两个必须实现的代理方法

    • 1.当窗口发生改变的时候会调用一个代理方法
    -(void)mtkView:(nonnull MTKView *)view drawableSizeWillChange:(CGSize)size
    
    • 2.当帧率确定之后,比方帧率为60,每个60帧会调用一次代理方法
    -(void)drawInMTKView:(nonnull MTKView *)view
    

    Metal Command Objects

    • MTLDevice ->GPU

    创建device

    mtkview.device = MTLCreateSystemDefaultDevice();
    
    • MTLCommandQueue 所有应用程序需要与GPU交互的第一对象(也就是它先睡,睡完之后其他人才能睡)
    _commandQueue = [_device newCommandQueue];
    
    • MTLCommandBuffer 为当前的每一个渲染传递创建一个新的命令缓冲区
    id<MTLCommandBuffer> commandBuffer = [_commandQueue commandBuffer];
    commandBuffer.label = @"command_1";
    
    • 命令缓冲区(commandBuffer)是由命令队列(_commandQueue)创建的
    • 命令编码器(commoand encoders)将命令编码到命令缓冲区中
    • commit命令缓存区并将其提交到GPU
    • GPU执行命令并将结果呈现为可绘制

    理解:
    1.命令时通过 command encoders 编译之后(编译这个字眼在OpenGLES就很熟悉了,方便理解)存储在 命令缓冲区中(commandBuffer)
    2.然后通过命令缓冲区 驱动GPU完成绘制和显示的动作
    3.Metal 不需要像OpenGLES一样需要自己编译和链接(毕竟是苹果爸爸的亲儿子,所以需要有神秘感)

    【metal 1】

    Metal 实践流程

    • 1.如何向GPU 发送渲染命令
    • 2.如何获取Metal设备
    • 3.如何配置MetalKit
    • 4.如何创建执行GPU指令
    • 5.如何显示渲染内容

    案例1:实现背景颜色的更迭

    步骤一 创建RCRendering类继承自NSObject 并添加MTKViewDelegate

    #import <Foundation/Foundation.h>
    @import MetalKit;
    
    NS_ASSUME_NONNULL_BEGIN
    
    @interface RCRendering : NSObject<MTKViewDelegate>
    
    -(id)initWithMetalkitview:(MTKView *)mtkview;
    
    @end
    
    NS_ASSUME_NONNULL_END
    

    步骤二 RCRendering 的完整步骤(当然了作为一个完美的男人,关键的时刻是隐藏)

    1.定义私有变量

    @implementation RCRendering
    {
        id<MTLDevice> _device;
        id<MTLCommandQueue> _commandQueue;
    }
    

    2.定义一个颜色结构体

    typedef struct {
        float r,g,b,a;
    }CustomColor;
    

    3.设置想要的颜色

    -(CustomColor)makeFancycolor{
        // 用来判断是否增加颜色
        static BOOL growing = YES;
        // 创建一个颜色通道
        static NSUInteger primaryChannel = 0;
        static float colorChannels[] = {1.0,0.2,0.0,1.0};
        const float dynamicColorrate = 0.015;
        
        if (growing){
            NSUInteger dynamicChannelIndex = (primaryChannel +1)%3;
            colorChannels[dynamicChannelIndex] += dynamicColorrate;
            
            if (colorChannels[dynamicChannelIndex]>=1.0){
                growing = NO;
                primaryChannel = dynamicChannelIndex;
            }
        }
        else{
            NSUInteger dynamicChannelIndex = (primaryChannel + 2)%3;
            colorChannels[dynamicChannelIndex] -= dynamicColorrate;
            
            if (colorChannels[dynamicChannelIndex]<=0.0){
                growing = NO;
            }
        }
        CustomColor color;
        color.r = *colorChannels + 0;
        color.g = *colorChannels + 1;
        color.b = *colorChannels + 2;
        color.a = *colorChannels + 3;
        return color;
    }
    

    4.初始化方法

    -(id)initWithMetalkitview:(MTKView *)mtkview{
        self = [super init];
        if (self){
            _device = mtkview.device;
            _commandQueue = [_device newCommandQueue];
        }
        return self;
    }
    

    5.MTKViewDelegate 方法

    • -(void)drawInMTKView
    -(void)drawInMTKView:(nonnull MTKView *)view {
        // 拿到颜色值
        CustomColor color = [self makeFancycolor];
        
        // 清理背景颜色
        view.clearColor = MTLClearColorMake(color.r, color.g, color.b, color.a);
        
        // 创建commandbuffer
        id<MTLCommandBuffer> commandBuffer = [_commandQueue commandBuffer];
        commandBuffer.label = @"buffer_1";
        
        // 创建渲染描述对象(会自动打印描述信息)
        MTLRenderPassDescriptor *renderPD = view.currentRenderPassDescriptor;
        
        if (!renderPD) return;
        
        // 创建 command encoders
        id<MTLCommandEncoder> commandEncoders = [commandBuffer parallelRenderCommandEncoderWithDescriptor:renderPD];
        commandEncoders.label= @"command encoders";
        
        /** 省略一些内容 主要是桥接metal文件的内容,关键部分隐藏就不告诉你*/
        
        [commandEncoders endEncoding];
        
        // 把帧缓冲区的内容渲染到屏幕上
        [commandBuffer presentDrawable:view.currentDrawable];
        
        // 提交GPU
        [commandBuffer commit];
    }
    
    • -(void)mtkView:(nonnull MTKView *)view drawableSizeWillChange
    -(void)mtkView:(nonnull MTKView *)view drawableSizeWillChange:(CGSize)size {
        NSLog(@"%ld",(long)view.preferredFramesPerSecond);
        NSLog(@"%ld",(long)view.currentRenderPassDescriptor);
    }
    

    6.viewcontroller 里面的实现

    // MTL MTK
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        _mtkView = (MTKView*)self.view;
        
        _mtkView.device = MTLCreateSystemDefaultDevice();
        
        if (!_mtkView.device) return;
        
        _rcrender = [[RCRendering alloc] initWithMetalkitview:_mtkView];
        
        if (!_rcrender) return;
        
        _mtkView.delegate = _rcrender;
        
        // 设置帧率
        _mtkView.preferredFramesPerSecond = 60;
        
    }
    

    总结:MTL MTK

    相关文章

      网友评论

          本文标题:Metal 1

          本文链接:https://www.haomeiwen.com/subject/rdheirtx.html