使用Xcode External Build System實現Rust 專案 Capture GPU Frame 線上除錯 Metal

熊皮皮發表於2018-11-29

文件列表見:Rust 移動端跨平臺複雜圖形渲染專案開發系列總結(目錄)

上次更新:2018.12.18

根據kvark指導,Xcode建立External Build System專案可通過Capture GPU Frame檢視gfx-hal的examples繪製過程,略出乎意料,我沒想過可以這麼幹(浪),雖然2015年我用External Build System單步除錯過FFmpeg原始碼,但是我不知道Xcode也支援這種形式的Capture GPU Frame,可以說很無知且沒有探索精神了。

雖然gfx-hal有完美的日誌輸出,但是,作為剛接觸gfx專案和Rust的初學者,有時我想確認程式流程是否符合預期(我一直在忙其他事,很少看Rust語法,這是個人失誤),即便Rust寫日誌輸出比C++方便很多,不過圖形專案開發過程中還是幀回放定位問題的效率最高 。但是,折騰一番,我在3臺mac(MacBook Pro + iMac)上一執行就崩潰。劉子殊在他本地測試也崩潰。我覺得可能是工程配置出錯了,原因是,終端直接執行gfx/examples/quad等可執行檔案是正常的。

客觀地說,隔了一段時間沒用Xcode,我不熟悉它的配置了,然後考慮並驗證了下列幾個替代方案:

  • Rust暴露C介面接入macOS專案。給gfx/examples填寫FFI介面,讓C/C++呼叫Rust實現的功能。在繪製過程中正常使用Capture GPU Frame。
  • 利用Metal MTLCommandBuffer addCompletedHandler介面新增執行完成的日誌。缺點是,只能驗證MTLCommandBuffer確實被GPU執行完,沒法檢視渲染結果。用Compute Shader做數值計算場合用這個方案倒是可以接受。
  • 類似上一方案,不輸出日誌,改為讀取MTLCommandBuffer目標紋理的畫素值,通過CIImage生成影象,通過Xcode外掛進行檢視。缺點,線上除錯Shader沒Capture GPU Frame方便。

然而,我還是沒法放棄Capture GPU Frame by External Build System方案,因為它配置和二次開發成本最低,最合適我們當前的團隊狀態。花了好長時間,在Josh的幫助下,終於解決了。

問題現象: examples/三個專案以External Build System專案形式在Xcode中執行後崩潰,提示資訊:

thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Os { code: 20, kind: Other, message: "Not a directory" }', libcore/result.rs:1009:5
note: Run with `RUST_BACKTRACE=1` for a backtrace.
ERROR 2018-11-30T07:12:36Z: gfx_backend_metal::command: Command buffer not released properly!
thread 'main' panicked at 'assertion failed: !self.active', src/backend/metal/src/command.rs:55:9
複製程式碼

結論: External Build Tool Configuration的Directory配置項只用於build過程,對於Rust專案,src/target/是兩個不同的目錄,examples/那些專案要求的編譯和執行路徑是examples/,根本原因是,examples程式碼用fs::read_to_string()通過相對路徑載入glsl原始碼檔案!此時相對路徑前面的完整路徑就隱式限制為examples/ 。然而,Xcode的播放按鈕功能是Build and then run the current scheme,Build階段配置正常了,問題出在Run階段,需要傳遞正確的路徑,示例如下圖所示。

Working Directory的正確配置

正常的Capture GPU Frame截圖。

正常的Capture GPU Frame截圖

下面覆盤當時的嘗試過程,流下了無知的淚水。

  • 同一個電腦(10.14 + Xcode 9.4.1 (9F2000))上依次嘗試examples/三個demo專案。執行失敗,下面簡寫成失敗。
  • 換兩臺電腦(10.13.5 + Xcode 9.4.1 (9F2000)、10.13.5 + Xcode 10 )嘗試上一步驟 ——> 失敗。
  • 重置程式碼庫,獲取最新程式碼,重新用Xcode執行 ——> 失敗。
  • 去掉Xcode Main Thread Checker,重新用Xcode執行 ——> 失敗。
  • 修改Rust編譯工具鏈,依次用nightly、stable,清掉編譯快取,重新編譯,重新用Xcode執行 ——> 失敗。
  • 升級Rust編譯工具鏈,重複上一步驟 ——> 失敗。
  • 反覆修改External Build Tool Configuration的三個配置項,確認它們的值都正確 ——> 失敗。
  • 修改Xcode工程位置,重複前面所有步驟 ——> 失敗。
  • 修改當前Scheme/Run/Debug/Info的所有配置項,重複前面所有步驟 ——> 失敗。
  • 搜尋更多Xcode執行Rust專案資料並逐一修改,重複前面所有步驟 ——> 失敗。
  • 和別人討論解決方案 ——> 失敗,開始接近問題本質原因。感謝齒輪哥。
  • 在Terminal切換到target/debug/執行quad ——> 失敗,報錯和Xcode幾乎一致。
  • 檢視原始碼,找到fs::read_to_string() ——> 問題差不多定位。
  • 修改原始碼加上完整檔案路徑 ——> Xcode執行不崩潰,但是Capture GPU Frame不可用,問題完全定位。
  • 換成MacBook Pro重複上一步驟 ——> 正常。推斷:iMac (Retina 5K, 27-inch, Late 2015)在10.14下驅動有問題。
  • 最終Josh給了更好的解決方案(見結論部分),無需修改原始碼。
  • 齒輪哥建議使用concat!(env!())補全gfx-hal/examples的shader原始碼路徑,我實現後提了PR,老外不同意合併。

相關文章