創(chuàng)建MetalKit視圖和渲染過(guò)程以繪制視圖的內(nèi)容
概觀
在本示例中,您將學(xué)習(xí)使用Metal渲染圖形內(nèi)容的基礎(chǔ)知識(shí)。您將使用MetalKit框架創(chuàng)建一個(gè)使用Metal繪制視圖內(nèi)容的視圖。然后,您將編碼用于將修改視圖背景顏色的渲染過(guò)程的命名。
注意
MetalKit自動(dòng)化窗口任務(wù)系統(tǒng),加載紋理和處理3D模型數(shù)據(jù)。有關(guān)更多信息,請(qǐng)參閱MetalKit。
準(zhǔn)備MetalKit視圖繪制
MetalKit提供一個(gè)名為MTKView的類,他是NSView(macOS中)或者UIView(iOS和tvOS中)的子類。MetalKit處理許多與將您使用Metal繪制內(nèi)容放到屏幕上相關(guān)的細(xì)節(jié)。
一個(gè)MTKView需要一個(gè)Metaldevice對(duì)象的引用,以便在其內(nèi)部創(chuàng)建資源,所以你的第一步是以現(xiàn)有的MTLDevice設(shè)置視圖的device屬性。
mtkView?.device = MTLCreateSystemDefaultDevice()
MTKView允許您控制其他屬性的行為。要將視圖的內(nèi)容改為純色背景,請(qǐng)?jiān)O(shè)置其clearColor屬性。您可以使用MTLClearColorMake(_: _: _: _:)函數(shù)創(chuàng)建color,指定rgba。
mtkView?.clearColor = MTLClearColorMake(0.0, 0.5, 1.0, 1.0)
因?yàn)槟恍枰诖耸纠欣L制動(dòng)畫內(nèi)容,所以請(qǐng)配置視圖,使其僅在需要更新內(nèi)容時(shí)繪制,例如視圖改變形狀時(shí):
mtkView?.enableSetNeedsDisplay = true
代理繪圖責(zé)任
MTKView依賴于您的應(yīng)用程序向Metal發(fā)出命令以生成可視內(nèi)容。MTKView使用代理模式通知您的應(yīng)用程序何時(shí)可以繪制。要接收代理回調(diào),請(qǐng)將視圖的delegate設(shè)置為符合MTKViewDelegate協(xié)議的對(duì)象。
mtkView?.delegate = renderer
代理實(shí)現(xiàn)的兩種方法:
- 只要內(nèi)容的大小發(fā)生變化,視圖就會(huì)調(diào)用mtkView(_:drawableSizeWillChange:)方法。當(dāng)包含視圖的窗口調(diào)整大小或設(shè)備方向更改時(shí)(在iOS上),會(huì)發(fā)生這種情況。這允許您的應(yīng)用根據(jù)根據(jù)其呈現(xiàn)的分辨率調(diào)整視圖大小。
- 只要有時(shí)間更新視圖內(nèi)容,試圖就會(huì)調(diào)用draw(in:)方法。在此方法中,您將創(chuàng)建一個(gè)命令緩沖區(qū),編碼命令,告訴GPU繪制什么及何時(shí)在屏幕上顯示它,并將該命令緩沖區(qū)排入隊(duì)列以供GPU執(zhí)行。這有時(shí)被稱為繪制框架。您可以將框架視為生成在屏幕上顯示的單個(gè)圖像的所有工作。在交互式應(yīng)用程序中,如游戲,您可以每秒繪制許多幀。
在此示例中,調(diào)用類APPLRenderer實(shí)現(xiàn)代理方法并承擔(dān)繪制責(zé)任。視圖控制器視圖控制器創(chuàng)建此類的實(shí)例并將其設(shè)置為視圖的代理。
創(chuàng)建渲染通道描述符(Render Pass Descriptor)
繪制時(shí),GPU會(huì)將結(jié)果存儲(chǔ)到紋理中,紋理是包含圖像數(shù)據(jù)并可供GPU訪問(wèn)的內(nèi)存塊。在此示例中,將MTKView創(chuàng)建繪制到視圖中所需的所有紋理。他創(chuàng)建多個(gè)紋理,以便在渲染到另一個(gè)紋理的過(guò)程中可以顯示一個(gè)紋理的內(nèi)容。
要繪制,您需要?jiǎng)?chuàng)建一個(gè)渲染過(guò)程,它是一系列渲染命令,可以繪制成一組紋理。在渲染過(guò)程中使用時(shí),紋理也稱為渲染目標(biāo)。要?jiǎng)?chuàng)建渲染過(guò)程,需要一個(gè)渲染過(guò)程描述符,一個(gè)MTLRenderPassDescriptor實(shí)例。在此示例中,不要配置自己的渲染過(guò)程描述符,而是要求MetalKit視圖為您創(chuàng)建一個(gè)。
guard let renderPassDescriptor = view.currentRenderPassDescriptor else { return }
渲染過(guò)程描述符渲染目標(biāo)集,以及如何在渲染過(guò)程的開始和結(jié)束時(shí)處理它們。渲染過(guò)程還定義了渲染的其他一些方面,這些方面不屬于此示例的一部分。視圖返回一個(gè)渲染過(guò)程描述符,其中包含指向視圖紋理之一的單一顏色附件,否則根據(jù)視圖的屬性配置渲染過(guò)程。默認(rèn)情況下,這意味著渲染過(guò)程開始,渲染目標(biāo)將被刪除為與視圖clearColor屬性匹配的純色,并且在渲染過(guò)程結(jié)束時(shí),所有更改都將存儲(chǔ)會(huì)紋理。
因?yàn)橐晥D渲染過(guò)程描述符可能nil,所以您應(yīng)該測(cè)試以確保在創(chuàng)建渲染過(guò)程之前渲染過(guò)程描述符(Render Pass Descriptor)對(duì)象是non-nil。
創(chuàng)建渲染通道
您可以使用MTLRenderCommand對(duì)象將其編碼帶命令緩沖區(qū)中來(lái)創(chuàng)建渲染過(guò)程。調(diào)用命令緩沖區(qū)的makeRenderCommandEncoder(descriptor:)方法并傳入渲染過(guò)程描述符。
let commandEncoder = commandBuffer?.makeRenderCommandEncoder(descriptor: renderPassDescriptor)
在此示例中,您不對(duì)繪圖命令進(jìn)行任何編碼,因此渲染過(guò)程唯一要做的就是擦除紋理。調(diào)用編碼器的endEncoding方法以指示傳遞完成。
commandEncoder?.endEncoding()
向屏幕呈現(xiàn)可繪制對(duì)象(Drawable)
繪制到紋理不會(huì)自動(dòng)在屏幕呈現(xiàn)新內(nèi)容。實(shí)際上,屏幕上只能顯示一些紋理。在Metal中,可以在屏幕上顯示的紋理由可繪制對(duì)象管理,要顯示內(nèi)容,您將呈現(xiàn)可繪制對(duì)象(drawable)。
MTKView會(huì)自動(dòng)創(chuàng)建可繪制對(duì)象(drawable)來(lái)管理紋理,讀取currentDrawable屬性以獲取擁有作為渲染目標(biāo)的紋理的可繪制對(duì)象。這個(gè)視圖返回一個(gè)CAMetalDrawable對(duì)象,這個(gè)對(duì)象用來(lái)連接到Core Animation。
guard let drawable = view.currentDrawable else { return }
在命令緩沖區(qū)上調(diào)用present(_:)方法,傳入drawable。
commandBuffer?.present(drawable)
此方法告訴Metal,當(dāng)命令緩沖區(qū)被安排執(zhí)行時(shí),Metal應(yīng)與Core Animation協(xié)調(diào)以在渲染完成后顯示紋理。當(dāng)Core Animation呈現(xiàn)紋理時(shí),它將成為視圖的新內(nèi)容。在此示例中,這意味著已擦除的紋理將成為視圖的新背景。此更改與Core Animation為屏幕用戶界面元素進(jìn)行的其他可視更新一起發(fā)生。
提交命令緩沖區(qū)
現(xiàn)在您已經(jīng)為該幀發(fā)出了所有命令,請(qǐng)?zhí)峤幻罹彌_區(qū)。
commandBuffer?.present(drawable)