WWDC Quick Look 💓 By SwiftGGTeam
Debug GPU-side errors in Metal

Debug GPU-side errors in Metal

元の動画を見る

ハイライト

Xcode 12 では、拡張されたコマンド バッファー エラーとシェーダー検証が Metal に追加されます。開発者は、一部のシナリオで、エンコーダ、描画呼び出し、メタル関数、およびシェーダの行番号に対する GPU 側のエラーを特定できます。

主要内容

Metal の問題点は、GPU で多くのバグが発生することです。 CPU 側 API が正しく使用されていない場合、Metal API 検証は関数エントリ、エラー タイプ、コール スタック、ソース コードの場所を提供します。 GPU 側のシェーダー エラーは、多くの場合、単にコマンド バッファーの障害、グラフィックスの破損、シャドウの欠落、または時折のデータ汚染として現れます。

このようなエラーには、境界外のグローバル メモリまたはスレッドグループ メモリ、nil テクスチャへのアクセス、無効な引数バッファ リソースの常駐、長時間実行または無限ループが含まれます。以前は一般的なコマンドバッファエラーがよく見られました。コマンド バッファーには数百のエンコーダーが存在する可能性があり、開発者は範囲を手動で絞り込むことしかできません。

Xcode 12 では、Metal デバッグ プロセスに 2 つの情報レイヤーが追加されます。最初のレイヤーは拡張コマンド バッファー エラーで、コマンド バッファー エラーをエンコーダー レベルまで分解し、どのエンコーダーが完了したか、保留中、影響を受けているか、障害が発生しているか、不明であるかを示します。 2 番目のレイヤーはシェーダー検証で、GPU に Metal シェーダーを挿入し、未定義の動作を検出し、描画呼び出し、Metal 関数、および GPU バックトレースのエラーを特定します。条件が満たされると、ファイルと行番号も表示されます。

これら 2 つのツール層は、明確なトラブルシューティング パスに対応しています。まずエラーを検出し、次に範囲を特定し、次に分類し、最後にコードを修復します。拡張コマンド バッファ エラーは、開発および QA での通常の使用に適しています。オーバーヘッドが低いシナリオでは、テレメトリを収集するためにオンラインにすることもできます。シェーダー検証はより高価であり、Xcode のデバッグ、自動テスト、およびリリース前の検証に適しています。

詳細

コマンドバッファにエンコーダレベルのステータスを報告させます

(03:40) 拡張コマンド バッファ エラーは、新しい記述子 API を通じて有効になります。キースイッチは、errorOptions = .encoderExecutionStatus

let desc = MTLCommandBufferDescriptor()
desc.errorOptions = .encoderExecutionStatus
let commandBuffer = commandQueue.makeCommandBuffer(descriptor: desc)

キーポイント:

  • MTLCommandBufferDescriptor()コマンドバッファ記述オブジェクトを作成します。
  • desc.errorOptions = .encoderExecutionStatusエラーが発生したときに、Metal がエンコーダの実行ステータスをログに記録する必要があります。
  • makeCommandBuffer(descriptor:)この説明オブジェクトを使用してコマンド バッファを作成すると、後続のエラーによってエンコーダ情報が取得されます。

(03:55) エラーが発生した後、エンコーダ情報はNSError.userInfoMTLCommandBufferEncoderInfoErrorKey内部。それぞれMTLCommandBufferEncoderInfoすべてにラベル、デバッグ標識、エラー状態が付属しています。

if let error = commandBuffer.error as NSError? {

    if let encoderInfos =
        error.userInfo[MTLCommandBufferEncoderInfoErrorKey]
        as? [MTLCommandBufferEncoderInfo] {

        for info in encoderInfos {
            print(info.label + info.debugSignposts.joined())
            if info.errorState == .faulted {
                print(info.label + " faulted!")
            }
        }
    }
}

キーポイント:

  • commandBuffer.error as NSError?コマンドバッファが完了したら、エラーオブジェクトを取得します。
  • MTLCommandBufferEncoderInfoErrorKey記録された順序で配置された一連のエンコーダー情報に対応します。
  • info.labelそしてinfo.debugSignposts開発者が通常エンコーダーに追加するデバッグ フラグを利用します。
  • info.errorState == .faultedこのエンコーダがコマンド バッファ フォールトを直接引き起こすことを示します。
  • affected代表者らはこの不具合の影響を受けた可能性があるが、メタル社はそれが原因であることを確認できていない。

この仕組みにより、まず「どこから見ればいいのか」という問題が解決されます。このビデオでは、開発および QA フェーズですべてのコマンド バッファーに対してこれを有効にすることを明確に推奨しています。 Metal に直接統合されているため、オーバーヘッドが低く、パフォーマンス評価後にテレメトリを使用して問題の範囲を徐々に絞り込むことでユーザー デバイスに取り込むこともできます。

シェーダー検証を使用してシェーダー内の未定義の動作を見つける

(07:08) シェーダー検証は、GPU 上で実行される検証レイヤーです。 Metal シェーダをインストルメント化し、未定義の動作を引き起こす操作を検出します。問題が検出されると、ツールは操作を阻止し、一部のシナリオで描画呼び出し、メタル関数、および特定のシェーダー ラインを特定するのに役立つログを生成します。

検出できる範囲は次のとおりです。

  • デバイスメモリと定数メモリが範囲外にアクセスされます。
  • スレッドグループのメモリ境界外アクセス。
  • Null テクスチャ オブジェクトに対してテクスチャリング関数を呼び出します。

ビデオ内の例は典型的なものです。境界外の読み取りが未割り当ての仮想メモリに落ち、コマンド バッファ フォールトが発生することがあります。場合によっては、隣接するバッファに落ちて、間違ったデータを読み取ったり、別のバッファを破損したりすることがあります。後者の場合、コマンド バッファ エラーはなく、断続的なレンダリング エラーのみが発生する可能性があります。これがシェーダー検証の価値です。障害がなくても、いくつかの未定義の動作が発見される可能性があります。

(09:47) Xcode で使用する場合は、スキーム設定の診断を開き、メタル領域のシェーダー検証にチェックを入れます。 Xcode は、すべてのコマンド バッファーの拡張コマンド バッファー エラーを同時にオンにします。次に、Metal 診断ブレークポイントを追加します。プログラムがシェーダー検証エラーに遭遇すると、プログラムは停止し、GPU と CPU のバックトレースを表示します。

デモでは、レイ トレーシングのサンプルに影とスクリーン ラインが欠けています。シェーダー検証が有効になっている場合、Xcode は Metal シェーダー内の境界外のメモリ読み取りにフラグを立てます。開発者は、呼び出し元までの GPU バックトレースをたどったところ、2 次元座標を 1 次元配列インデックスに変換するときに、gridX幅を掛けて修正すると、gridY * width + gridXレンダリング後の再開。

Xcode 外部での検証を有効にしてログを読み取る

(15:05) 自動テストまたは非 Xcode ランタイム環境でも検証レイヤーを有効にすることができます。MTL_DEBUG_LAYERAPI 検証をオンにするには、ゼロ以外の値に設定します。MTL_SHADER_VALIDATIONシェーダー検証をオンにするには、ゼロ以外の値に設定します。 2 つの変数は同時に設定することも、別々に使用することもできますが、Metal デバイスよりも前に作成する必要があります。デバイスの作成後、これらの値はロックされ、このプロセスでの後続の変更は有効になりません。

(15:39) コマンドバッファには、logs財産。これはコマンド バッファーが完了した後にのみ有効であるため、読み取りロジックを完了ハンドラーに組み込む必要があります。

commandBuffer.addCompletedHandler { (commandBuffer) in
    for log in commandBuffer.logs {
        let encoderLabel = log.encoderLabel ?? "Unknown Label"
        print("Faulting encoder \"\(encoderLabel)\"")
        guard let debugLocation = log.debugLocation,
              let functionName = debugLocation.functionName
        else {
            return
        }
        print("Faulting function \(functionName):\(debugLocation.line):\(debugLocation.column)")
    }
}

キーポイント:

  • addCompletedHandlerコマンド バッファーが完了した後にコードが実行されるようにしてください。
  • commandBuffer.logs複数のシェーダー検証エラーが含まれる可能性があります。
  • log.encoderLabelエラーをエンコーダに関連付けます。
  • log.debugLocationライブラリがソースから、またはデバッグ シンボルを使用してコンパイルされるときに、ソース コードの場所を提供します。
  • functionNamelinecolumn自動テスト ログがシェーダの機能と場所を直接指すようにします。

(15:40) 同じ種類の情報がシステム ログにも記録されます。使用できますlog streamMetal GPU デバッグ ログをフィルタリングします。

log stream --predicate "subsystem = 'com.apple.Metal' and category = 'GPUDebug'"

キーポイント:

  • subsystem = 'com.apple.Metal'Metal サブシステムを見てください。
  • category = 'GPUDebug'GPU デバッグ関連のログをフィルタリングします。
  • ログには、プロセス名、エラーの種類、エラーの詳細、メタル ファイル名と行番号などの情報が含まれます。

シェーダー検証を有効にする前に知っておくべきコスト

(17:17) シェーダー検証により、パイプラインのコンパイル時間が増加します。このビデオでは、非同期コンパイル方法を使用して複数のスレッドにコンパイルを分散し、開発中の待機を減らすことを推奨しています。

(17:34) Metal ライブラリにはデバッグ シンボルが必要です。 Xcode デバッグ スキームはそれを自動的に処理します。 Metal フロントエンドを手動で呼び出す場合は、以下を追加する必要があります-g。実行時にソースからライブラリをコンパイルする場合は、次の使用をお勧めします。#lineディレクティブを使用して、バックトレーサーに認識可能なファイル名を与えます。

(18:26) シェーダー検証はプロセスレベルのスイッチです。オンにすると、UI レンダリングを含むプロセス内のすべての Metal コマンドが検証レイヤーを通過します。これにより、高いパフォーマンスとメモリのオーバーヘッドがもたらされます。ビデオは開発と QA には推奨されますが、ユーザーには推奨されません。

(18:57) オンにすると、一部のクエリ結果が変わる場合があります。コンピューティングパイプラインmaxTotalThreadsPerThreadgroupそしてthreadExecutionWidthすべてもう一度読む必要があります。一部のインストルメンテーションは、環境変数を使用してオフにできます。たとえば、Null テクスチャを自分でチェックした場合、次のように設定できます。MTL_SHADER_VALIDATION_TEXTURE_USAGE = 0テクスチャ使用計測をオフにしますが、対応する検出機能は放棄されます。

重要ポイント

  • メタル エラー テレメトリ チャネルを作成します: QA ビルドで使用されますMTLCommandBufferDescriptorエンコーダの実行ステータスを有効にし、障害が発生したエンコーダのラベル、サインポスト、デバイス情報を一緒にアップロードします。これにより、「時折発生する GPU クラッシュ」が集約された問題リストに変わります。

  • シェーダー検証をレンダリング回帰テストに統合: CI のグラフィックス テスト タスクに設定MTL_DEBUG_LAYER=1そしてMTL_SHADER_VALIDATION=1、クリティカル レンダリング パスの実行後に読み取ります。commandBuffer.logs。テスト障害情報は、障害のあるエンコーダ、関数、行、列を直接出力します。

  • 各レンダー パスの命名規則を確立します: コマンド エンコーダーに安定したラベルを設定し、複雑なパスにデバッグ サインポストを挿入します。拡張コマンド バッファ エラーと Xcode Metal Debugger はこれらの識別子を再利用し、障害レポートをビジネス モジュールに送信できます。

  • シェーダー コンパイル パイプラインのデバッグ シンボル モードを保持: デバッグおよび QA ビルドに対して Metal デバッグ シンボルを均一に有効にします。ランタイムコンパイル済みシェーダーソースコードと#lineディレクティブを使用すると、シェーダー検証のバックトレースが実際のファイルまたはジェネレーターの出力場所を指すようになります。

  • リリース前の GPU 未定義動作チェックリストを作成します: 範囲外のデバイス メモリ、スレッドグループ メモリ、ヌル テクスチャ、引数バッファ リソースの永続性、およびタイムアウトをカバーします。拡張コマンド バッファー エラーはエンコーダー レベルの位置決めを担当し、シェーダー検証はインストルメンテーションによって検出できる未定義の動作を担当します。

関連セッション

コメント

GitHub Issues · utterances