# Metal 学习(一)

1.MTLDevice

一个MTLDevice对象可以代表一个执行指令的GPU
MTLDevice 协议提供了查询设备,创建Metal其他对象的方法
(MTLCommandQueue, MTLLibrary, MTLRenderPipelineState, MTLTexture, MTLBuffer)等

通过调用 MTLCreateSystemDefaultDevice() 方法,可以获取系统首选的设备对象。

注:MTLDevice 的创建很昂贵、耗时,并且它可以一直使用。所以只需要创建一次即可。

2.CAMetalLayer & CAMetalDrawable

负责渲染的是CALayer,UIView只要做内容的管理和事件的响应。想让UIView支持渲染Metal内容,关键在于CALayer。
CoreAnimation定义了一个CAMetakLayer类,他的content使用Metal渲染
CAMetalDrawable 协议也是 Core Animation 中定义的,它表示某个对象是可被显示的资源。
它继承自 MTLDrawable,并扩展了一个实现 MTLTexture 协议的 texture 对象,这个 texture用来表示渲染指令执行的目标。即之后的渲染操作,会画在这个 texture 上。
所以这个,大家要明白一点,显示的图像内容,可以抽象描述为 texture

3.Render

首先我们要大致了解一下渲染的流程:


image.png

GPU下发指令,渲染管线进行工作。
我们通过 MTLRenderPassDescriptor 告诉Metal下一个渲染过程中有执行什么操作
texture:关联的纹理,即渲染目标。必须设置,不然内容不知道要渲染到哪里。
loadAction:决定前一次 texture 的内容需要清除、还是保留
storeAction:决定这次渲染的内容需要存储、还是丢弃
clearColor:当 loadAction 是 MTLLoadActionClear 时,则会使用对应的颜色来覆盖当前 texture(用某一色值逐像素写入)

let renderPassDescripor = MTLRenderPassDescriptor()
renderPassDescripor.colorAttachments[0].clearColor = MTLClearColorMake(0.48, 0.74, 0.92, 1)
renderPassDescripor.colorAttachments[0].texture = drawable.texture
renderPassDescripor.colorAttachments[0].loadAction = .clear
renderPassDescripor.colorAttachments[0].storeAction = .store

4.MTLCommandQueue,MTLCommandBuffer,MTLRenderCommandEncoder

image.png

指令的提交流程:
Device 创建 Command Queue
Command Queue 创建 Command Buffer
Command Buffer 创建 Command Encoder
Command Encoder 将对应的指令编码写入 Command Encoder
Command Buffer 被提交到 GPU 中执行
GPU 执行指令后,将渲染结果写入 texture 中
展示 drawable

Command Encoder 由 Command Buffer 创建,MTLCommandBuffer 协议支持以下几种 Encoder 类型,它们被用于编码不同的 GPU 任务:

1.MTLRenderCommandEncoder ,该类型的 Encoder 为一个 render pass 编码3D图形渲染指令。
2.MTLComputeCommandEncoder ,该类型的 Encoder 编码并行数据计算任务。
3.MTLBlitCommandEncoder ,该类型的 Encoder 支持在 buffer 和 texture 之间进行简单的拷贝操作,以及类似 mipmap 生成操作。
4.MTLParallelRenderCommandEncoder ,该类型的 Encoder 为并行图形渲染任务编码指令。

1.当前 Command Encoder 配置完毕,调用 endEncoding():只有当上一个Encoder调用endEncoding()后才能创建下一个Encoder)
2.一旦所有的编码工作结束, Command Buffer 执行 commit() 操作,标记该 Command Buffer 已经准备好被 GPU 执行。
3.然后 Command Queue 会控制什么时候执行已经提交到 Command Buffer 上的指令,执行后,会把渲染结果写入我们之前设置的渲染目的(texture)中。
4.最后,我们需要把渲染结果,显示到屏幕上。这时候需要调用 drawable 对象的 present() 方法,才会将对应的显示操作提交到 Core Animation 上去。为了确保 drawable 上已经渲染完毕,我们一般是使用 Command Buffer 的 present(_ drawable: MTLDrawable) 方法,它会在 Command Buffer 确保已经被 GPU 执行后,再自动调用 drawable 对象的 present() 方法。

5.MTLRenderPipelineState & MTLRenderPipelineDescriptor

渲染管线的大致流程:


image.png

我们上面所有的准备工作都做好了,然而GPU并不知道我们要用到什么东西,需要我们按照它能识别的方式告诉它。

我们渲染管线对象在不同阶段需要:顶点数据,顶点颜色,片段着色器进行描述

我们通过MTLRenderPipelineDescriptor来描述创建MTLRenderPipelineState

    let library = device?.makeDefaultLibrary()
    let vertexFunction = library?.makeFunction(name: "vertexShader")
    let fragmentFunction = library?.makeFunction(name: "fragmentShader")
    
    
    let pipelineDescriptor = MTLRenderPipelineDescriptor()
    pipelineDescriptor.colorAttachments[0].pixelFormat = metalLayer.pixelFormat
    pipelineDescriptor.vertexFunction = vertexFunction
    pipelineDescriptor.fragmentFunction = fragmentFunction
    
    pipelineState = try! device?.makeRenderPipelineState(descriptor: pipelineDescriptor)

6.MTLBuffer

MTLBuffer,可以理解成一个 CPU 和 GPU 都可以访问的内存块,它里面存储的数据,是没有格式、类型限制的,即可以存储任意类型的数据。

我们一般使用MTLBuffer来存储顶点数据

我们通过device来创建维护MTLBuffer对象,管理我们的数据

    /**
     @method newBufferWithBytes:length:options:
     @brief Create a buffer by allocating new memory and specifing the initial contents to be copied into it.
     */
    func makeBuffer(bytes pointer: UnsafeRawPointer, length: Int, options: MTLResourceOptions = []) -> MTLBuffer?
    
    ----------------------------------------------------------
    
let vertices = [
            XTVertex(position: [-1, -1], textureCoordinates: [0.0, 1.0]),
            XTVertex(position: [-1, 1], textureCoordinates: [0.0, 0.0]),
            XTVertex(position: [1, -1], textureCoordinates: [1.0, 1.0]),
            XTVertex(position: [1, 1], textureCoordinates: [1.0, 0.0]),
        ]
        vertexBuffer = device?.makeBuffer(bytes: vertices, length: MemoryLayout<XTVertex>.size * 4, options: .cpuCacheModeWriteCombined)
    

了解一下 MTLResourceOptions,表示资源的管理方式

public struct MTLResourceOptions : OptionSet {

    public init(rawValue: UInt)

    
    public static var cpuCacheModeWriteCombined: MTLResourceOptions { get }

    
    @available(iOS 9.0, *)
    public static var storageModeShared: MTLResourceOptions { get }

    
    @available(iOS 9.0, *)
    public static var storageModePrivate: MTLResourceOptions { get }

    @available(iOS 10.0, *)
    public static var storageModeMemoryless: MTLResourceOptions { get }

    
    @available(iOS 10.0, *)
    public static var hazardTrackingModeUntracked: MTLResourceOptions { get }

    @available(iOS 13.0, *)
    public static var hazardTrackingModeTracked: MTLResourceOptions { get }

    
    public static var optionCPUCacheModeWriteCombined: MTLResourceOptions { get }
}

我们使用的cpuCacheModeWriteCombined,会优化资源,表示CPU只能写入。
一般情况,也可以不设置,不设置就是用默认MTLResourceCPUCacheModeDefaultCache,表示CPU,GPU都能正常读写操作

7.MTLTexture

纹理是我们经常用到的。我们现实纹理一般都需要这些操作
· 图片转纹理
· 纹理映射,采样,从纹理上获取具体的色值

代码如下:

    func newTexture(_ image: UIImage) -> MTLTexture {
        let imageRef = image.cgImage!
        let width = imageRef.width
        let height = imageRef.height
        let colorSpace = CGColorSpaceCreateDeviceRGB() //色域
        let rawData = calloc(height * width * 4, MemoryLayout<UInt8>.size) //图片存储数据的指针
        let bitsPerComponent = 8 //指定每一个像素中组件的位数(bits,二进制位)。例如:对于32位格式的RGB色域,你需要为每一个部分指定8位
        let bytesPerPixel = 4
        let bytesPerRow = width * bytesPerPixel
        let context = CGContext(data: rawData,
                  width: width,
                  height: height,
                  bitsPerComponent: bitsPerComponent,
                  bytesPerRow: bytesPerRow,
                  space: colorSpace,
                  bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue | CGBitmapInfo.byteOrder32Big.rawValue)
        context?.draw(imageRef, in: CGRect(x: 0, y: 0, width: width, height: height))
        let textureDescriptor = MTLTextureDescriptor.texture2DDescriptor(pixelFormat: .rgba8Unorm, width: width, height: height, mipmapped: false)
        let texture = device?.makeTexture(descriptor: textureDescriptor)
        let region = MTLRegionMake2D(0, 0, width, height)
        texture?.replace(region: region, mipmapLevel: 0, withBytes: rawData!, bytesPerRow: bytesPerRow)
        free(rawData)
        return texture!
    }
/*!

MTLRegion 结构体,定义了 texture 中对应的图像区域,我们一般和图像的实际大小保持一致即可。

 @struct MTLRegion
 @abstract Identify a region in an image or texture.
 */
public struct MTLRegion {

    public var origin: MTLOrigin
    public var size: MTLSize

    public init()
    public init(origin: MTLOrigin, size: MTLSize)
}

针对2D纹理图像的纹理坐标如下:


image.png

来自:小专栏-iOS图像处理

此文章只为自己做记录,勉励自己学习。

热爱生活,记录生活!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,904评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,581评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,527评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,463评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,546评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,572评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,582评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,330评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,776评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,087评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,257评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,923评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,571评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,192评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,436评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,145评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,127评论 2 352

推荐阅读更多精彩内容