macOS、iOS的Metal 2開發(fā)爬坑記錄:攝像頭美旧、Capture GPU Frame渤滞、Shader調(diào)試與GPUImage存在的問題

本文檔記錄Metal 2配合Xcode 9在macOS High Serria、iOS 8+開發(fā)過程遇到的攝像頭榴嗅、Capture GPU Frame與Shader編譯調(diào)試問題及解決辦法妄呕。另外,修正了GPUImage源碼中對Mac攝像頭不支持yuv輸出的“不恰當(dāng)”地說法(至少在macOS High Serria是不恰當(dāng)?shù)模?/p>

1. 調(diào)用iMac攝像頭

1.1 攝像頭的position屬性為AVCaptureDevicePositionUnspecified

在iOS開發(fā)中嗽测,一般通過AVCaptureDevicePosition(Front或Back)確認(rèn)訪問前后置攝像頭绪励。雖然iMac有前置攝像頭,然而唠粥,它的position屬性為nil疏魏。因此,當(dāng)macOS和iOS共享一份代碼晤愧,遍歷[AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo]返回的AVCaptureDevice列表時大莫,AVCaptureDevice的position屬性并不等于AVCaptureDevicePositionFront,而是AVCaptureDevicePositionUnspecified官份。

NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
for (AVCaptureDevice *device in devices)
{
    if ([device position] == cameraPosition)
    {
        _inputCamera = device;
    }
}
if (!_inputCamera)
{
    return nil;
}

或者只厘,如果做平臺條件編譯,直接用默認(rèn)攝像頭即可舅巷,示例代碼如下所示羔味。

#if TARGET_OS_MAC
[AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
#endif

正常情況下,iMac 5k攝像頭的輸出信息如下所示钠右。

<AVCaptureDALDevice: 0x10071e6f0 [FaceTime HD Camera (Built-in)][0x1440000005ac8511]>

1.2 輸出yuv像素格式并兼容Metal 2

因?yàn)锳VFoundation和Metal 2支持macOS和iOS赋元,所以我將音視頻輸入部分寫成同一份源碼并加上適當(dāng)?shù)臈l件編譯。出于性能考慮,iOS一般讓攝像頭輸出yuv像素數(shù)據(jù)搁凸,并且在yuv空間上做些圖像處理操作媚值,這就得測試yuv轉(zhuǎn)rgb的shader是否正常工作的。因此护糖,令macOS輸出yuv格式數(shù)據(jù)在此場合是合理的杂腰。閱讀GPUImage源碼,可發(fā)現(xiàn)如下注釋:

// Despite returning a longer list of supported pixel formats, only RGB, RGBA, BGRA, and the YUV 4:2:2 variants seem to return cleanly

打印AVCaptureVideoDataOutput.availableVideoCVPixelFormatTypes屬性椅文,發(fā)現(xiàn)其支持yuv420sp、yuv422sp和RGBA及BGRA惜颇。

因此皆刺,GPUImage在macOS上直接輸出32BGRA數(shù)據(jù),繞過這個坑凌摄。實(shí)際上羡蛾,上述說法對于macOS High Sierra是不成立的,其他版本的Mac沒測試锨亏。

下面痴怨,以iMac 5k為例調(diào)用攝像頭,設(shè)置AVCaptureVideoDataOutput.videoSettings = nil; // receives samples in device format后器予,返回kCVPixelFormatType_422YpCbCr8 浪藻,即yuv422sp('2vuy')∏瑁可知爱葵,和iOS一樣,iMac攝像頭原始格式為yuv422p反浓。也證明了GPUImage的說法不那么正確萌丈,也有了接下來的折騰。

好了雷则,問題來了辆雾,macOS輸出yuv格式數(shù)據(jù)有點(diǎn)小坑。下面描述輸出為yuv420p時月劈,通過CVMetalTextureCacheCreateTextureFromImage創(chuàng)建Metal紋理時返回kCVReturnFirst(-6660)的解決過程度迂。

指定videoSettings的輸出格式為yuv420p后,在CVMetalTextureCacheCreateTextureFromImage創(chuàng)建Metal紋理時返回kCVReturnFirst(-6660)艺栈。顯然英岭,沒創(chuàng)建出可用的紋理。

為處理-6660湿右,加上[videoOutput setVideoSettings:@{(id) kCVPixelBufferMetalCompatibilityKey: @(TRUE)}];反而導(dǎo)致CVPixelBufferGetPlaneCount(cameraFrame)返回值為0诅妹。具體原因是,每次調(diào)用setVideoSettings會覆蓋上一次設(shè)置的結(jié)果。解決辦法是吭狡,先構(gòu)造完整videoSettings字典尖殃,再設(shè)置。比如划煮,

#if TARGET_OS_MAC
NSDictionary<NSString *, id> *videoSettings = @{
    (id) kCVPixelBufferMetalCompatibilityKey: @(TRUE),
    (id) kCVPixelBufferPixelFormatTypeKey: @(kCVPixelFormatType_420YpCbCr8BiPlanarFullRange)
};
videoOutput.videoSettings = videoSettings;
#else
[videoOutput setVideoSettings:@{(id) kCVPixelBufferPixelFormatTypeKey: @(kCVPixelFormatType_420YpCbCr8BiPlanarFullRange)}];
#endif

同理送丰,單獨(dú)指定為kCVPixelFormatType_32BGRA在CVMetalTextureCacheCreateTextureFromImage創(chuàng)建紋理時也是-6660,解決辦法同上弛秋。

2. Capture GPU Frame

按照GPUImage的做法器躏,在macOS High Serria且Scheme為默認(rèn)值情況下,Capture GPU Frame功能不可能用蟹略。具體表現(xiàn)為登失,Xcode 9的Capture GPU Frame功能變灰、快捷圖標(biāo)消失挖炬。啟動app也不打印Metal API Extended Validation信息揽浙。正常情況下,打印信息示例如下意敛。

[DYMTLInitPlatform] platform initialization successful
Metal GPU Frame Capture Enabled
Metal API Validation Enabled

通常我們第一反應(yīng)是馅巷,可以強(qiáng)制修改Scheme的GPU Frame Capture為Metal(默認(rèn)為Automatically Enable)。實(shí)際上草姻,這個改動之后钓猬,Capture GPU Frame快捷圖標(biāo)是出現(xiàn)了,然而撩独,問題并沒解決逗噩。

其實(shí),這不是Xcode的bug跌榔,是代碼邏輯不對异雁。解決起來很簡單,而且不需要強(qiáng)制修改Scheme的GPU Frame Capture為Metal僧须,默認(rèn)的Automatically Enable就夠用了纲刀。只要代碼邏輯合理,Capture GPU Frame會自動設(shè)置為可用狀態(tài)担平。

3. Shader編譯與調(diào)試

3.1 Metal shader文件不可放入Bundle

.metal文件放入Bundle中示绊,Xcode編譯時并不檢查shader代碼是否正確。相應(yīng)地暂论,運(yùn)行后使用defaultLibrary得不到預(yù)編譯的著色器函數(shù)面褐。

3.2 在線調(diào)試看不到Shader源碼

在線調(diào)試Metal shader時,發(fā)現(xiàn)找不到源碼取胎,Xcode提示如下所示:

Cannot show the function source
Xcode could not find the library source. Make sure debugging information is enabled for library compilation under target build settings.

具體原因是展哭,在Metal出現(xiàn)前的老Xcode創(chuàng)建的項(xiàng)目一般會出現(xiàn)此問題湃窍。項(xiàng)目太古老,用Xcode 9打開后匪傍,它不會自動設(shè)置此項(xiàng)您市。

解決辦法:Produce debugging information將debug設(shè)置Yes。使用Xcode 9等新版本創(chuàng)建Metal項(xiàng)目役衡,默認(rèn)將此項(xiàng)設(shè)置為Yes∫鹦荩現(xiàn)在,在線調(diào)試時Shader源碼可正常修改手蝎、編譯榕莺。

題外話,之前嘗試修改Bitcode設(shè)置YES或NO棵介,并不能解決此問題帽撑。

3.3 Metal文件不支持平臺相關(guān)的條件編譯

舉例:

#if TARGET_OS_MAC
    XXX
#else
    YYY
#endif

在macOS上運(yùn)行Metal應(yīng)用,實(shí)際編譯結(jié)果得到Y(jié)YY鞍时。

3.4 Metal文件包含Metal文件或自定義頭文件

Metal文件可包含另一個Metal文件,在Xcode 9扣蜻,這是可行的逆巍。也可包含自定義頭文件。缺點(diǎn)是Xcode無法自動跳轉(zhuǎn)到相應(yīng)的文件莽使,相當(dāng)不方便锐极,希望官方后續(xù)能解決此缺陷。

3.5 cannot have global constructors (llvm.global_ctors) in FragmentFunctionXXX

從3.4節(jié)可知芳肌,Metal文件允許包含另一個Metal文件灵再。那么,你會想亿笤,能否定義一些常用顏色轉(zhuǎn)換矩陣在公共metal文件翎迁,然后在其它metal中直接使用,從而避免每次繪制都上傳這些數(shù)據(jù)呢净薛?

你問我支不支持汪榔,我當(dāng)然支持你的想法∷喟荩可是痴腌,也得看看編譯器怎么看。實(shí)際上燃领,如果定義的是行或列向量士聪,這是可行的。然而猛蔽,如果定義全局矩陣(比如剥悟,half3x3),而且全局矩陣參與計算,最終結(jié)果為Fragment Function的返回值懦胞,Xcode編譯期間會報錯:

cannot have global constructors (llvm.global_ctors) in FragmentFunctionXXX

怎么解決呢替久?目前來看,只能打消定義全局矩陣的念頭躏尉。定義幾個常用的采樣器就夠了蚯根,要啥自行車。

3.6 空CommandBuffer導(dǎo)致Capture GPU Frame無法結(jié)束

每次渲染提交不帶MTLRenderCommandEncoder的MTLCommandBuffer胀糜,進(jìn)行Capture GPU Frame颅拦,Xcode狀態(tài)欄會瘋狂讀取MTLCommandBuffer數(shù)據(jù),無法自拔教藻。比如距帅,定時執(zhí)行如下代碼。

id <MTLCommandBuffer> commandBuffer = [_commandQueue commandBuffer];
[commandBuffer commit];

為什么呢括堤?因?yàn)榇a存在邏輯錯誤碌秸,才會出現(xiàn)這種現(xiàn)象,上述代碼只是示意悄窃。那讥电,哪里錯了呢?咱們還是用圖說話吧轧抗。

小結(jié)

使用Metal 2遇到的問題不止這些恩敌。之后會整理成文檔,逐步發(fā)布横媚。不得不說纠炮,現(xiàn)在的Metal比2015年那會兒在工具的支持上強(qiáng)太多了。比如灯蝴,Xcode支持?jǐn)帱c(diǎn)預(yù)覽紋理恢口,從此不再頻繁Capture GPU Frame。
另外穷躁,Capture GPU Frame在macOS App上的速度非郴∮快,比iOS爽太多折砸。放兩個截圖示意下看疗。

GPU Stack
斷點(diǎn)查看紋理內(nèi)容
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市睦授,隨后出現(xiàn)的幾起案子两芳,更是在濱河造成了極大的恐慌,老刑警劉巖去枷,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件怖辆,死亡現(xiàn)場離奇詭異是复,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)竖螃,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進(jìn)店門淑廊,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人特咆,你說我怎么就攤上這事季惩。” “怎么了腻格?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵画拾,是天一觀的道長。 經(jīng)常有香客問我菜职,道長青抛,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任酬核,我火速辦了婚禮蜜另,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘嫡意。我一直安慰自己举瑰,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布鹅很。 她就那樣靜靜地躺著,像睡著了一般罪帖。 火紅的嫁衣襯著肌膚如雪促煮。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天整袁,我揣著相機(jī)與錄音菠齿,去河邊找鬼。 笑死坐昙,一個胖子當(dāng)著我的面吹牛绳匀,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播炸客,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼疾棵,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了痹仙?” 一聲冷哼從身側(cè)響起是尔,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎开仰,沒想到半個月后拟枚,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體薪铜,經(jīng)...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年恩溅,在試婚紗的時候發(fā)現(xiàn)自己被綠了隔箍。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,059評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡脚乡,死狀恐怖蜒滩,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情每窖,我是刑警寧澤帮掉,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站窒典,受9級特大地震影響蟆炊,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜瀑志,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一涩搓、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧劈猪,春花似錦昧甘、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至常侦,卻和暖如春浇冰,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背聋亡。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工肘习, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人坡倔。 一個月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓漂佩,卻偏偏與公主長得像,于是被迫代替她去往敵國和親罪塔。 傳聞我的和親對象是個殘疾皇子投蝉,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評論 2 345

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