Metal框架詳細解析(四十八) —— 將項目從OpenGL轉(zhuǎn)化到Metal(一)

版本記錄

版本號 時間
V1.0 2019.01.18 星期五

前言

很多做視頻和圖像的,相信對這個框架都不是很陌生耘斩,它渲染高級3D圖形鲸沮,并使用GPU執(zhí)行數(shù)據(jù)并行計算。接下來的幾篇我們就詳細的解析這個框架澳盐。感興趣的看下面幾篇文章。
1. Metal框架詳細解析(一)—— 基本概覽
2. Metal框架詳細解析(二) —— 器件和命令(一)
3. Metal框架詳細解析(三) —— 渲染簡單的2D三角形(一)
4. Metal框架詳細解析(四) —— 關于GPU Family 4(一)
5. Metal框架詳細解析(五) —— 關于GPU Family 4之關于Imageblocks(二)
6. Metal框架詳細解析(六) —— 關于GPU Family 4之關于Tile Shading(三)
7. Metal框架詳細解析(七) —— 關于GPU Family 4之關于光柵順序組(四)
8. Metal框架詳細解析(八) —— 關于GPU Family 4之關于增強的MSAA和Imageblock采樣覆蓋控制(五)
9. Metal框架詳細解析(九) —— 關于GPU Family 4之關于線程組共享(六)
10. Metal框架詳細解析(十) —— 基本組件(一)
11. Metal框架詳細解析(十一) —— 基本組件之器件選擇 - 圖形渲染的器件選擇(二)
12. Metal框架詳細解析(十二) —— 基本組件之器件選擇 - 計算處理的設備選擇(三)
13. Metal框架詳細解析(十三) —— 計算處理(一)
14. Metal框架詳細解析(十四) —— 計算處理之你好令宿,計算(二)
15. Metal框架詳細解析(十五) —— 計算處理之關于線程和線程組(三)
16. Metal框架詳細解析(十六) —— 計算處理之計算線程組和網(wǎng)格大械鸢摇(四)
17. Metal框架詳細解析(十七) —— 工具、分析和調(diào)試(一)
18. Metal框架詳細解析(十八) —— 工具粒没、分析和調(diào)試之Metal GPU Capture(二)
19. Metal框架詳細解析(十九) —— 工具筛婉、分析和調(diào)試之GPU活動監(jiān)視器(三)
20. Metal框架詳細解析(二十) —— 工具、分析和調(diào)試之關于Metal著色語言文件名擴展名癞松、使用Metal的命令行工具構(gòu)建庫和標記Metal對象和命令(四)
21. Metal框架詳細解析(二十一) —— 基本課程之基本緩沖區(qū)(一)
22. Metal框架詳細解析(二十二) —— 基本課程之基本紋理(二)
23. Metal框架詳細解析(二十三) —— 基本課程之CPU和GPU同步(三)
24. Metal框架詳細解析(二十四) —— 基本課程之參數(shù)緩沖 - 基本參數(shù)緩沖(四)
25. Metal框架詳細解析(二十五) —— 基本課程之參數(shù)緩沖 - 帶有數(shù)組和資源堆的參數(shù)緩沖區(qū)(五)
26. Metal框架詳細解析(二十六) —— 基本課程之參數(shù)緩沖 - 具有GPU編碼的參數(shù)緩沖區(qū)(六)
27. Metal框架詳細解析(二十七) —— 高級技術(shù)之圖層選擇的反射(一)
28. Metal框架詳細解析(二十八) —— 高級技術(shù)之使用專用函數(shù)的LOD(一)
29. Metal框架詳細解析(二十九) —— 高級技術(shù)之具有參數(shù)緩沖區(qū)的動態(tài)地形(一)
30. Metal框架詳細解析(三十) —— 延遲照明(一)
31. Metal框架詳細解析(三十一) —— 在視圖中混合Metal和OpenGL渲染(一)
32. Metal框架詳細解析(三十二) —— Metal渲染管道教程(一)
33. Metal框架詳細解析(三十三) —— Metal渲染管道教程(二)
34. Metal框架詳細解析(三十四) —— Hello Metal爽撒! 一個簡單的三角形的實現(xiàn)(一)
35. Metal框架詳細解析(三十五) —— Hello Metal! 一個簡單的三角形的實現(xiàn)(二)
36. Metal框架詳細解析(三十六) —— Metal編程指南之概覽(一)
37. Metal框架詳細解析(三十七) —— Metal編程指南之基本Metal概念(二)
38. Metal框架詳細解析(三十八) —— Metal編程指南之命令組織和執(zhí)行模型(三)
39. Metal框架詳細解析(三十九) —— Metal編程指南之資源對象:緩沖區(qū)和紋理(四)
40. Metal框架詳細解析(四十) —— Metal編程指南之函數(shù)和庫(五)
41. Metal框架詳細解析(四十一) —— Metal編程指南之圖形渲染:渲染命令編碼器之Part 1(六)
42. Metal框架詳細解析(四十二) —— Metal編程指南之圖形渲染:渲染命令編碼器之Part 2(七)
43. Metal框架詳細解析(四十三) —— Metal編程指南之數(shù)據(jù)并行計算處理:計算命令編碼器(八)
44. Metal框架詳細解析(四十四) —— Metal編程指南之緩沖和紋理操作:Blit命令編碼器(九)
45. Metal框架詳細解析(四十五) —— Metal編程指南之Metal工具(十)
46. Metal框架詳細解析(四十六) —— Metal編程指南之Tessellation(十一)
47. Metal框架詳細解析(四十七) —— Metal編程指南之資源堆(十二)

開始

首先看下寫作環(huán)境

Swift 4.2, iOS 12, Xcode 10

在這篇Metal教程中响蓉,您將學習如何將項目從OpenGL移動到Apple的3D圖形API:Metal硕勿。

Metal于2014年推出,作為基于GPU計算的通用API枫甲。在2018年源武,蘋果公司在iOS 12中的iOS和MacOS中都棄用了的OpenGL。

在本教程中想幻,您將學習如何將應用程序從使用OpenGL轉(zhuǎn)換為Metal粱栖。要完成本教程,您需要一個可用的OpenGL應用程序脏毯。

在開始之前闹究,您可能想要查看Metal和OpenGL上的這些優(yōu)秀資源。

如果您沒有3D圖形經(jīng)驗食店,請不要擔心渣淤!你仍然可以看懂。如果您對3D編程或OpenGL有一些經(jīng)驗吉嫩,您可能會發(fā)現(xiàn)本教程很簡單砂代。許多相同的概念適用于Metal。

注意:Metal應用程序無法在iOS模擬器上運行率挣。它們需要帶有Apple A7 device或更高版本的設備刻伊。要完成本教程,您需要A7及其以上的設備。


OpenGL ES vs. Metal

OpenGL ES旨在成為一個跨平臺的框架捶箱。 這意味著智什,通過一些小的修改,您可以在其他平臺上運行C ++ OpenGL ES代碼丁屎,例如Android荠锭。

OpenGL ES的跨平臺支持很不錯,但Apple意識到它缺少所有優(yōu)質(zhì)Apple產(chǎn)品所具有的操作系統(tǒng)晨川,硬件和軟件的優(yōu)點集成证九。 因此,它專門為Apple硬件設計了圖形API共虑。 我們的目標是在支持最新和最強大功能的同時實現(xiàn)低開銷和高性能愧怜。

答案就是Metal,與OpenGL ES相比妈拌,它可以為您的應用提供高達10?的繪制調(diào)用次數(shù)拥坛。 效果令人驚嘆 - 您可能會在WWDC 2014 keynote主題演講中的Zen Garden示例中記住它們。

1. Understanding Conceptual Differences

從開發(fā)的角度來看尘分,OpenGL和Metal是相似的猜惋。在這兩種情況下,您都可以使用數(shù)據(jù)設置緩沖區(qū)以傳遞給GPU并指定頂點和片段著色器培愁。在OpenGL項目中著摔,有一個GLKBaseEffect,它是著色器之上的抽象定续。 Metal沒有這樣的API梨撞,所以你需要自己編寫著色器。但不要擔心 - 它不是太復雜香罐!

OpenGL和Metal之間的最大區(qū)別在于卧波,在Metal中,您通常使用兩種類型的對象:

  • 描述符對象(Descriptor objects)庇茫。
  • 編譯狀態(tài)對象(Compiled-state objects)港粱。

這個想法很簡單。您創(chuàng)建一個描述符對象并進行編譯旦签。編譯狀態(tài)對象是GPU優(yōu)化的資源查坪。創(chuàng)建和編譯都是昂貴的操作,因此我們的想法是盡可能少地執(zhí)行它們宁炫,然后再使用編譯狀態(tài)對象進行操作偿曙。

這種方法意味著在使用Metal時,您不需要在渲染循環(huán)上進行大量的設置操作羔巢。這使得它比OpenGL更有效望忆,由于體系結(jié)構(gòu)的限制罩阵,OpenGL無法做到這一點。

是時候自己探索這種差異吧启摄!


Integrating Metal

打開已有的項目OpenGLKit.xcodeproj稿壁,你會看到一個實用OpenGL的項目,構(gòu)建并運行如下所示:

你應該看到一個彩色方形旋轉(zhuǎn)歉备。 此方塊使用OpenGL渲染傅是。 但是,由于此項目的部署目標是iOS 12蕾羊,因此有幾個OpenGL棄用警告喧笔。 您可以在Issue navigator中的Xcode中查看這些內(nèi)容。

現(xiàn)在你要用Metal畫同一個方格龟再。 并擺脫所有那些討厭的警告书闸!

打開ViewController.swift,將ViewController更改為UIViewController的子類吸申,而不是GLKViewController梗劫。 在Metal中享甸,沒有MetalViewController這樣的東西截碴。 相反,你必須在UIViewController中使用MTKView蛉威。

MTKViewMetalKit框架的一部分日丹。 要訪問此API,
在文件頂部添加以下內(nèi)容:

import MetalKit

1. Switching From OpenGL

現(xiàn)在是時候做一些OpenGL清理了蚯嫌。 跟著這些步驟:

  • 1) 將setupGL()的兩次出現(xiàn)重命名為setupMetal()哲虾。
  • 2) 刪除tearDownGL()deinit()方法。 使用Metal择示,不需要像這樣明確清理束凑。
  • 3) 查找并刪除整個擴展GLKViewControllerDelegate,因為此視圖控制器不再是GLKViewController栅盲。 請注意汪诉,glkViewControllerUpdate包含用于旋轉(zhuǎn)的邏輯。 這很有用谈秫,但是暫時刪除它扒寄。
  • 4) 從setupMetal()的頂部刪除以下代碼:
context = EAGLContext(api: .openGLES3)
EAGLContext.setCurrent(context)
    
if let view = self.view as? GLKView, let context = context {
  view.context = context
  delegate = self
}
  • 5) 從ViewController頂部刪除以下屬性:
private var context: EAGLContext?
private var effect = GLKBaseEffect()
private var ebo = GLuint()
private var vbo = GLuint()
private var vao = GLuint()

最后,在ViewController類聲明的頂部拟烫,向MTKView添加一個IBOutlet

@IBOutlet weak var metalView: MTKView!

2. Setting Up the Storyboard

ViewController不再是GLKViewController该编,因此您需要在故事板中進行一些更改。

打開Main.storyboard硕淑。 在此示例中课竣,故事板包含兩個場景嘉赎,均命名為View Controller Scene。 一個有GLKView稠氮,另一個包含MTKView和你剛剛添加到源代碼的outlet的連接曹阔。

您需要做的就是使用MTKView作為初始視圖控制器來設置場景。 找到當前沒有箭頭指向它的場景隔披。 單擊頂部的欄以選擇視圖控制器赃份。 或者,您可以在文檔大綱窗格中選擇它奢米。 然后打開屬性檢查器并選中Is Initial View Controller抓韩。

完成后,您可以刪除第一個場景鬓长。 干得好谒拴!


Setting Up Metal

你準備好了嗎? 是時候使用一些Metal了涉波!

在Metal中英上,您將用于訪問GPU的主要對象是MTLDevice。 下一個最重要的對象是MTLCommandQueue啤覆。 此對象是您將傳遞編碼幀的隊列苍日。

打開ViewController.swift并添加以下屬性:

private var metalDevice: MTLDevice!
private var metalCommandQueue: MTLCommandQueue!

現(xiàn)在,轉(zhuǎn)到setupMetal()窗声。 用以下內(nèi)容替換它的內(nèi)容:

metalDevice = MTLCreateSystemDefaultDevice() 
metalCommandQueue = metalDevice.makeCommandQueue()  
metalView.device = metalDevice 
metalView.delegate = self

這比以前更短了相恃!

這將抓取系統(tǒng)默認的Metal設備,然后從設備創(chuàng)建命令隊列笨觅。 然后它將設備分配給Metal視圖拦耐。 最后,它將視圖控制器設置為視圖的委托见剩,以便在繪制和調(diào)整大小時接收回調(diào)杀糯。

現(xiàn)在您需要實現(xiàn)MTKViewDelegate協(xié)議。

ViewController.swift的底部苍苞,添加以下擴展:

extension ViewController: MTKViewDelegate {
  // 1
  func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) {
  }
  
  // 2
  func draw(in view: MTKView) { 
  }
}

此擴展實現(xiàn)了兩種方法固翰。

  • 1) 當可繪制尺寸改變時,例如當屏幕旋轉(zhuǎn)時柒啤,調(diào)用此方法倦挂。
  • 2) 調(diào)用此方法以執(zhí)行實際繪圖。

1. Basic Drawing

你們都準備好了担巩! 為了簡單起見方援,您將首先繪制灰色背景。

對于要在Metal中繪制的每個幀涛癌,必須創(chuàng)建一個命令緩沖區(qū)犯戏,您可以在其中指定要繪制的內(nèi)容和方式送火。 然后,此緩沖區(qū)在CPU上編碼先匪,并通過命令隊列發(fā)送到GPU种吸。

draw(in :)中添加以下代碼:

// 1
guard let drawable = view.currentDrawable else {
  return
}
    
let renderPassDescriptor = MTLRenderPassDescriptor() // 2
renderPassDescriptor.colorAttachments[0].texture = drawable.texture // 3
renderPassDescriptor.colorAttachments[0].loadAction = .clear // 4
renderPassDescriptor.colorAttachments[0]
  .clearColor = MTLClearColor(red: 0.85, green: 0.85, blue: 0.85, alpha: 1.0) // 5
    
// 6
guard let commandBuffer = metalCommandQueue.makeCommandBuffer() else {
  return
}
    
// 7
guard let renderEncoder = commandBuffer
  .makeRenderCommandEncoder(descriptor: renderPassDescriptor) else {
    return
}
    
// Frame drawing goes here
    
renderEncoder.endEncoding() // 8

commandBuffer.present(drawable) // 9
commandBuffer.commit() // 10

這里面的東西有點多,這是上面代碼中發(fā)生的事情:

  • 1) 確保有一個有效的drawable用于當前幀呀非。
  • 2) MTLRenderPassDescriptor包含一組附件坚俗,這些附件是渲染過程生成的像素的渲染目標。
  • 3) 將視圖中的紋理設置為繪圖目標岸裙。
  • 4) 清除渲染開始時的每個像素猖败。
  • 5) 指定清除顏色附件時要使用的顏色。在這種情況下降允,它是一個可愛的85%灰色恩闻。
  • 6) 請求命令隊列創(chuàng)建新的命令緩沖區(qū)。
  • 7) 創(chuàng)建一個編碼器對象剧董,可以將圖形渲染命令編碼到命令緩沖區(qū)中幢尚。稍后您將在此聲明后添加實際繪圖代碼。
  • 8) 聲明此編碼器生成的所有命令都已完成翅楼。
  • 9) 盡快注冊可繪制的drawable presentation尉剩。
  • 10) 提交此命令緩沖區(qū)以在命令隊列中執(zhí)行。

總之犁嗅,您可以創(chuàng)建命令緩沖區(qū)和命令編碼器边涕。然后晤碘,在命令編碼器中進行繪制褂微,并通過命令隊列將命令緩沖區(qū)提交給GPU。然后每次繪制幀時重復這些步驟园爷。

注意:如果您正在使用模擬器宠蚂,此時您將收到構(gòu)建錯誤。您需要一個兼容的設備來構(gòu)建和運行該應用程序童社。

構(gòu)建并運行應用程序求厕。

一個灰色面板完成了。


Drawing Primitives

繪制更有意義的東西需要一些時間扰楼。 所以呀癣,下面我們就開始!

要繪制內(nèi)容弦赖,必須將表示對象的數(shù)據(jù)傳遞給GPU项栏。 您已經(jīng)有頂點結(jié)構(gòu)來表示頂點數(shù)據(jù),但是您需要進行一些小的更改才能將它與Metal一起使用蹬竖。

打開ViewController.swift并從中更改Indices屬性:

var Indices: [GLubyte]

改成下面這種:

var Indices: [UInt32]

你將會很快看到為什么在繪制基元(primitives)的時候需要這么更改沼沈。

1. Data Buffers

要將頂點數(shù)據(jù)傳遞給GPU流酬,您需要創(chuàng)建兩個緩沖區(qū):一個用于頂點峰尝,一個用于索引(indices)峡眶。 這類似于OpenGL的元素緩沖對象(EBO)和頂點緩沖對象(VBO)

將這些屬性添加到ViewController

private var vertexBuffer: MTLBuffer!
private var indicesBuffer: MTLBuffer!

現(xiàn)在疏唾,在setupMetal()中页衙,在底部添加以下內(nèi)容:

let vertexBufferSize = Vertices.size()
vertexBuffer = metalDevice
  .makeBuffer(bytes: &Vertices, length: vertexBufferSize, options: .storageModeShared)
    
let indicesBufferSize = Indices.size()
indicesBuffer = metalDevice
  .makeBuffer(bytes: &Indices, length: indicesBufferSize, options: .storageModeShared)

這要求metalDevice創(chuàng)建用數(shù)據(jù)初始化的頂點和索引緩沖區(qū)摊滔。

現(xiàn)在,去draw(in:)中店乐,然后在之前:

renderEncoder.endEncoding()

添加下面代碼

renderEncoder.setVertexBuffer(vertexBuffer, offset: 0, index: 0)  
renderEncoder.drawIndexedPrimitives(
  type: .triangle, 
  indexCount: Indices.count, 
  indexType: .uint32, 
  indexBuffer: indicesBuffer, 
  indexBufferOffset: 0) 

首先惭载,它將頂點緩沖區(qū)傳遞給GPU,將其設置為索引0响巢。然后描滔,它使用indicesBuffer繪制三角形。 請注意踪古,您需要指定索引類型uint32含长。 這就是你之前更改索引類型的原因。

構(gòu)建并運行應用程序伏穆。

崩潰拘泞! 這不好。 您將數(shù)據(jù)從CPU傳遞到GPU枕扫。 它崩潰是因為您沒有指定GPU應該如何使用這些數(shù)據(jù)陪腌。 你需要添加一些著色器! 幸運的是烟瞧,這是下一步诗鸭。

后記

本篇主要講述了將項目從OpenGL轉(zhuǎn)化到Metal,感興趣的給個贊或者關注~~~

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末参滴,一起剝皮案震驚了整個濱河市强岸,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌砾赔,老刑警劉巖蝌箍,帶你破解...
    沈念sama閱讀 221,430評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異暴心,居然都是意外死亡妓盲,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,406評論 3 398
  • 文/潘曉璐 我一進店門专普,熙熙樓的掌柜王于貴愁眉苦臉地迎上來悯衬,“玉大人,你說我怎么就攤上這事脆诉∩跬ぃ” “怎么了贷币?”我有些...
    開封第一講書人閱讀 167,834評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長亏狰。 經(jīng)常有香客問我役纹,道長,這世上最難降的妖魔是什么暇唾? 我笑而不...
    開封第一講書人閱讀 59,543評論 1 296
  • 正文 為了忘掉前任促脉,我火速辦了婚禮,結(jié)果婚禮上策州,老公的妹妹穿的比我還像新娘瘸味。我一直安慰自己,他們只是感情好够挂,可當我...
    茶點故事閱讀 68,547評論 6 397
  • 文/花漫 我一把揭開白布旁仿。 她就那樣靜靜地躺著,像睡著了一般孽糖。 火紅的嫁衣襯著肌膚如雪枯冈。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,196評論 1 308
  • 那天办悟,我揣著相機與錄音尘奏,去河邊找鬼。 笑死病蛉,一個胖子當著我的面吹牛炫加,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播铺然,決...
    沈念sama閱讀 40,776評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼俗孝,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了探熔?” 一聲冷哼從身側(cè)響起驹针,我...
    開封第一講書人閱讀 39,671評論 0 276
  • 序言:老撾萬榮一對情侶失蹤烘挫,失蹤者是張志新(化名)和其女友劉穎诀艰,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體饮六,經(jīng)...
    沈念sama閱讀 46,221評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡其垄,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,303評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了卤橄。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片绿满。...
    茶點故事閱讀 40,444評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖窟扑,靈堂內(nèi)的尸體忽然破棺而出喇颁,到底是詐尸還是另有隱情漏健,我是刑警寧澤,帶...
    沈念sama閱讀 36,134評論 5 350
  • 正文 年R本政府宣布橘霎,位于F島的核電站蔫浆,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏姐叁。R本人自食惡果不足惜瓦盛,卻給世界環(huán)境...
    茶點故事閱讀 41,810評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望外潜。 院中可真熱鬧原环,春花似錦、人聲如沸处窥。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,285評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽滔驾。三九已至柜与,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間嵌灰,已是汗流浹背弄匕。 一陣腳步聲響...
    開封第一講書人閱讀 33,399評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留沽瞭,地道東北人迁匠。 一個月前我還...
    沈念sama閱讀 48,837評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像驹溃,于是被迫代替她去往敵國和親城丧。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,455評論 2 359

推薦閱讀更多精彩內(nèi)容