版本記錄
版本號 | 時間 |
---|---|
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
蛉威。
MTKView
是MetalKit
框架的一部分日丹。 要訪問此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,感興趣的給個贊或者關注~~~