版本記錄
| 版本號 | 時間 |
|---|---|
| V1.0 | 2018.10.08 星期一 |
前言
很多做視頻和圖像的,相信對這個框架都不是很陌生,它渲染高級3D圖形,并使用GPU執(zhí)行數(shù)據(jù)并行計算。接下來的幾篇我們就詳細(xì)的解析這個框架。感興趣的看下面幾篇文章。
1. Metal框架詳細(xì)解析(一)—— 基本概覽
2. Metal框架詳細(xì)解析(二) —— 器件和命令(一)
3. Metal框架詳細(xì)解析(三) —— 渲染簡單的2D三角形(一)
4. Metal框架詳細(xì)解析(四) —— 關(guān)于GPU Family 4(一)
5. Metal框架詳細(xì)解析(五) —— 關(guān)于GPU Family 4之關(guān)于Imageblocks(二)
6. Metal框架詳細(xì)解析(六) —— 關(guān)于GPU Family 4之關(guān)于Tile Shading(三)
7. Metal框架詳細(xì)解析(七) —— 關(guān)于GPU Family 4之關(guān)于光柵順序組(四)
8. Metal框架詳細(xì)解析(八) —— 關(guān)于GPU Family 4之關(guān)于增強的MSAA和Imageblock采樣覆蓋控制(五)
9. Metal框架詳細(xì)解析(九) —— 關(guān)于GPU Family 4之關(guān)于線程組共享(六)
10. Metal框架詳細(xì)解析(十) —— 基本組件(一)
11. Metal框架詳細(xì)解析(十一) —— 基本組件之器件選擇 - 圖形渲染的器件選擇(二)
12. Metal框架詳細(xì)解析(十二) —— 基本組件之器件選擇 - 計算處理的設(shè)備選擇(三)
13. Metal框架詳細(xì)解析(十三) —— 計算處理(一)
Hello Compute - 你好,計算
演示如何使用GPU執(zhí)行數(shù)據(jù)并行計算。
Overview - 概覽
在Basic Texturing示例中,您學(xué)習(xí)了如何通過將紋理應(yīng)用于單個四邊形來渲染2D圖像。
在此示例中,您將學(xué)習(xí)如何在Metal中執(zhí)行計算處理工作負(fù)載以進(jìn)行圖像處理。 特別是,您將學(xué)習(xí)如何使用計算處理管道和編寫內(nèi)核函數(shù)。
General-Purpose GPU Programming - 通用GPU編程
圖形處理單元(GPU)最初設(shè)計用于以非常快速和有效的方式處理大量圖形數(shù)據(jù),例如頂點或片段。這種設(shè)計在GPU硬件架構(gòu)本身中很明顯,GPU架構(gòu)本身具有許多處理核心,可以并行執(zhí)行工作負(fù)載。
縱觀GPU設(shè)計的歷史,并行處理架構(gòu)保持相當(dāng)一致,但處理核心變得越來越可編程。這一變化使GPU能夠從固定功能管道轉(zhuǎn)向可編程管道,這一變化也支持通用GPU(GPGPU)編程。
在GPGPU模型中,GPU可用于任何類型的處理任務(wù),并且不限于圖形數(shù)據(jù)。例如,GPU可用于加密,機器學(xué)習(xí),物理或財務(wù)。在Metal中,GPGPU工作負(fù)載稱為計算處理工作負(fù)載或計算。
圖形和計算工作負(fù)載不是互斥的;Metal提供統(tǒng)一的框架和語言,可實現(xiàn)圖形和計算工作負(fù)載的無縫集成。實際上,此示例通過以下方式演示了此集成:
- 1) 使用將彩色圖像轉(zhuǎn)換為灰度圖像的計算管道
- 2) 使用圖形管道將灰度圖像渲染為四邊形表面
Create a Compute Processing Pipeline - 創(chuàng)建計算處理管道
計算處理流水線僅由一個階段組成,即可編程內(nèi)核函數(shù),它執(zhí)行計算傳遞。 內(nèi)核函數(shù)直接讀取和寫入資源,而不通過各種管道階段傳遞資源數(shù)據(jù)。
MTLComputePipelineState對象表示計算處理管道。 與圖形渲染管道不同,您可以使用單個內(nèi)核函數(shù)創(chuàng)建MTLComputePipelineState對象,而無需使用管道描述符。
// Load the kernel function from the library
id<MTLFunction> kernelFunction = [defaultLibrary newFunctionWithName:@"grayscaleKernel"];
// Create a compute pipeline state
_computePipelineState = [_device newComputePipelineStateWithFunction:kernelFunction
error:&error];
Write a Kernel Function - 編寫內(nèi)核函數(shù)
此示例將圖像數(shù)據(jù)加載到紋理中,然后使用內(nèi)核函數(shù)將紋理的像素從顏色轉(zhuǎn)換為灰度。 內(nèi)核函數(shù)獨立并發(fā)處理像素。
注意:可以為CPU編寫和執(zhí)行等效算法。 但是,GPU解決方案更快,因為紋理的像素不需要按順序處理。
此示例中的內(nèi)核函數(shù)稱為grayscaleKernel,其簽名如下所示:
kernel void
grayscaleKernel(texture2d<half, access::read> inTexture [[texture(AAPLTextureIndexInput)]],
texture2d<half, access::write> outTexture [[texture(AAPLTextureIndexOutput)]],
uint2 gid [[thread_position_in_grid]])
該函數(shù)采用以下資源參數(shù):
-
inTexture:包含輸入顏色像素的只讀2D紋理。 -
outTexture:一種只寫的2D紋理,用于存儲輸出灰度像素。
可以使用read()函數(shù)讀取指定讀取訪問限定符的紋理。可以使用write()函數(shù)寫入指定寫訪問限定符的紋理。
內(nèi)核函數(shù)每個線程執(zhí)行一次,這類似于頂點函數(shù)每個頂點執(zhí)行一次的方式。線程被組織成3D網(wǎng)格;編碼計算傳遞通過聲明網(wǎng)格的大小來指定要處理的線程數(shù)。因為此示例處理2D紋理,所以線程排列在2D網(wǎng)格中,其中每個線程對應(yīng)于唯一的紋理元素。
內(nèi)核函數(shù)的gid參數(shù)使用[[thread_position_in_grid]]屬性限定符,該限定符定位計算網(wǎng)格中的線程。內(nèi)核函數(shù)的每次執(zhí)行都有一個唯一的gid值,使每個線程能夠清晰地工作。
灰度像素對于其每個RGB分量具有相同的值。 可以通過簡單地平均顏色像素的RGB分量,或者通過將某些權(quán)重應(yīng)用于每個分量來計算該值。 此示例使用Rec. 709 c亮度系數(shù)用于顏色到灰度轉(zhuǎn)換。
half4 inColor = inTexture.read(gid);
half gray = dot(inColor.rgb, kRec709Luma);
outTexture.write(half4(gray, gray, gray, 1.0), gid);
Execute a Compute Pass - 執(zhí)行計算傳遞
MTLComputeCommandEncoder對象包含用于執(zhí)行計算傳遞的命令,包括對內(nèi)核函數(shù)及其資源的引用。 與渲染命令編碼器不同,您可以在不使用傳遞描述符的情況下創(chuàng)建MTLComputeCommandEncoder。
id<MTLComputeCommandEncoder> computeEncoder = [commandBuffer computeCommandEncoder];
[computeEncoder setComputePipelineState:_computePipelineState];
[computeEncoder setTexture:_inputTexture
atIndex:AAPLTextureIndexInput];
[computeEncoder setTexture:_outputTexture
atIndex:AAPLTextureIndexOutput];
計算傳遞必須指定執(zhí)行內(nèi)核函數(shù)的次數(shù)。 此數(shù)字對應(yīng)于網(wǎng)格大小,該大小根據(jù)線程和線程組定義。 線程組是由內(nèi)核函數(shù)并發(fā)執(zhí)行的3D線程組。 在此示例中,每個線程對應(yīng)一個唯一的紋理元素,網(wǎng)格大小必須至少為2D圖像的大小。 為簡單起見,此示例使用16 x 16線程組大小,該大小足以供任何GPU使用。 但實際上,選擇有效的線程組大小取決于數(shù)據(jù)的大小和特定設(shè)備的功能。
// Set the compute kernel's threadgroup size of 16x16
_threadgroupSize = MTLSizeMake(16, 16, 1);
// Calculate the number of rows and columns of threadgroups given the width of the input image
// Ensure that you cover the entire image (or more) so you process every pixel
_threadgroupCount.width = (_inputTexture.width + _threadgroupSize.width - 1) / _threadgroupSize.width;
_threadgroupCount.height = (_inputTexture.height + _threadgroupSize.height - 1) / _threadgroupSize.height;
該示例通過發(fā)出調(diào)度調(diào)用并結(jié)束計算命令的編碼來最終確定計算傳遞。
[computeEncoder dispatchThreadgroups:_threadgroupCount
threadsPerThreadgroup:_threadgroupSize];
[computeEncoder endEncoding];
然后,該示例繼續(xù)編碼首先在Basic Texturing示例中引入的渲染命令。 計算傳遞和渲染過程的命令使用相同的灰度紋理,附加到同一命令緩沖區(qū),并同時提交給GPU。 但是,計算過程中的灰度轉(zhuǎn)換始終在渲染過程中的四重渲染之前執(zhí)行。
后記
本篇主要講述了你好,計算,感興趣的給個贊或者關(guān)注~~~
