Closed14

makeRenderPipelineStateで失敗する件

kabeyakabeya

macOS上で、MetalのmakeRenderPipelineState (Swiftの場合)あるいはnewRenderPipelineStateWithDescriptor (Objective-Cの場合)を実行したタイミングで、

というエラーがデバッグコンソールに出力されて、ウィンドウが空で表示される、という現象に2日間悩まされました(と言っても、まだ解決はしてません)。

具体的には、AppleのMetalのサンプルコード、HelloTriangleでもこの現象が起こります。

もうちょっと厳密に言うと、上記のサンプルそのものは動きます。

最初、サンプルの処理を流用したものをSwiftで書いたのですが、この件で動きませんでした。
次に、まったく同じ処理(のSwift版)にして、ファイルの構成もサンプルと同じにしましたが、やはり動きませんでした。何かSwiftは特別なおまじないのようなものが要るのかとか、Storyboardに何か設定もれがあるのかとか、色々調べました。ネットの情報を探してもそれっぽいのがありません。

最後に、Objective-Cのプロジェクトを新規に作って、ファイルをサンプルから差し替えました。それでもやはり動きませんでした。
ここに至って、プロジェクトの設定かなと思い始め、色々試したところ、

ということが分かりました(ちなみに上記のHelloTriangleの設定は10.12になっています)。

11.0以降で生じるのがなぜなのかは、これから調べます…

kabeyakabeya

よくよく調べると、10.15以前に設定してても、Xcodeからデバッグ実行(Run)はできるものの、Archiveして実行ファイル形式にすると実行できない(空のウインドウが表示される)、という状態です。
(もっと言うと、~/Library/Developer/Xcode/DerivedData/以下にできる中間ファイルとしての.appでも空のウインドウが表示されます。Debug版でも)

なんだろ。

kabeyakabeya

よくよく調べると、10.15以前に設定してても、Xcodeからデバッグ実行(Run)はできるものの、Archiveして実行ファイル形式にすると実行できない(空のウインドウが表示される)、という状態です。

これに関しては、XcodeのDebug→Debug Executable...から、実行ファイル形式の.appをデバッグしたところ、

というメッセージが出ていたので、プロジェクトのTARGETS→General→Frameworks, Libraries, and Embedded Contentで+ボタンを押して、MetalKit.frameworkを追加することで解消しました。
元のHelloTrianglesプロジェクトにはiPhoneOS/tvOS用のMetalKit.frameworkへの参照だけ存在し、macOS用のMetalKit.frameworkの参照がありませんでした。

kabeyakabeya

あと、たぶん忘れるので未来の自分に向けてメモ。

XcodeのDebug→Debug Executable...から、実行ファイル形式の.appをデバッグ

ですが、実行ファイルを選ぶとScheme編集画面?のようなものが表示されます。

ここで、「Debug Executable」のチェックを外さないと、リリース版をデバッグ実行できません。
外さない場合で、かつ、自分のプロジェクトだと、なのか、.dSYMがあると、なのか、その場合はRunボタンを押すと、デバッグ版が実行されます。
チェックを外してCloseして、Runボタンを押すと、そのまま選んだ実行ファイルが実行されるようです。

kabeyakabeya

そして、「最初の二週間分だけ」かと思っていたら「Downgraded targets for remaining days to be macOS 10.15 compatible」もあってさらに闇深いw

kabeyakabeya

MTLRenderPipelineDescriptor.rasterSampleCountに値を設定すると、

は発生せずに、

が発生します。
空のウインドウが表示されるのはそのままだけども、前者はいきなりどっかにすっ飛んでいくのに対して、後者はちゃんとthrowされてきます(Objective-Cの場合は、引数の&errorに入ってきます)。

追記:
3だと上記の「not supported by device」でしたが、4だと前者になりました。

kabeyakabeya

元のHelloTriangleのObjective-Cコードには@try/catchがなかったので、付けて実行するとthrowしてくる場所が(なんとなく)分かりました。

@try {
   _pipelineState = [_device newRenderPipelineStateWithDescriptor:pipelineStateDescriptor options:MTLPipelineOptionNone reflection:nil error:&error];
}
@catch (NSException* exception) {
  NSLog(@"exception: %@", exception.debugDescription);
  for (NSString* stack in exception.callStackSymbols) {
    NSLog(@"stack: %@", stack);
  }
  NSLog(@"error: %@", error);
}

コールスタックは以下のような感じ。

0x00000001a1f343e8 __exceptionPreprocess + 176
0x00000001a1a7eea8 objc_exception_throw + 60
0x00000001a2019c94 -[__NSCFString characterAtIndex:].cold.1 + 0
0x00000001a2026dd0 -[__NSDictionaryM setObject:forKey:].cold.3 + 0
0x00000001a1e75114 -[__NSDictionaryM setObject:forKey:] + 840
0x00000001ecba5398 AGXMetalG13X + 3281816
0x00000001ecbadd18 AGXMetalG13X + 3317016
0x00000001ecbad730 AGXMetalG13X + 3315504
0x00000001ecbab474 AGXMetalG13X + 3306612
0x00000001eca67b54 AGXMetalG13X + 1981268
0x00000001eca614cc AGXMetalG13X + 1955020
0x00000001ecba8b84 AGXMetalG13X + 3296132
0x00000001ecba91fc AGXMetalG13X + 3297788
0x00000001eca63ed8 AGXMetalG13X + 1965784
0x00000001eca61ffc AGXMetalG13X + 1957884
0x00000001ecba847c AGXMetalG13X + 3294332
0x00000001ecbb2854 AGXMetalG13X + 3336276
0x00000001a260ddf8 -[MTLDebugDevice _newRenderPipelineStateWithDescriptor:options:reflection:error:] + 476
0x000000010333505c -[CaptureMTLDevice newRenderPipelineStateWithDescriptor:options:reflection:error:] + 252
0x000000010282aae4 -[AAPLRenderer initWithMetalLayer:] + 488

-[NSDictionary setObject:forKey:]の直前の2個ぐらいのアドレス近辺でブレークポイントを置いてみると、以下のような感じでした。

直前の呼び出しは以下。確かに@"Remarks"があります。

その1個前はこんな感じ。@"promoted"@"VertexBufferPrefetch"といったキーワードがあります。後者が怪しいのかも。

それとは別に、Objective-Cの@try/catchで捕まえるべき NSExceptionをSwiftでどうするのか調べないと。

kabeyakabeya

怪しいのは、コールスタック上、いくつか前から現れる_MTLCompilePerformanceStatisticsEnabled_MTLAddCompilePipelinePerformanceStatisticsというシンボル名です。

macOS 11.0以降をターゲットにすると、パフォーマンス統計?が勝手に有効になって要らん処理をしようとして失敗している、とかではないかなと思います。

Optimizing Performance with Pipeline Statistics (https://developer.apple.com/documentation/metal/performance_tuning/optimizing_performance_with_pipeline_statistics)

ここに「View Remarks and Recommendations」という節があるのが、なんか関係ありそうななさそうな…

kabeyakabeya

結局。

macOS 11.0以降にターゲットを設定すると、デバッグ版はエラーで実行できないけども、リリース版(実行ファイル形式)は動く、という感じに見えます。

デバッグ版ドライバ?Xcode?の問題なのかなという気がします。

これが最近のバージョン、例えば13.2の問題、とかいうなら直るまで待つかという気にもなりますが、macOS11って2世代前なのでもう直らん気がしてしまいます。
(もしくは誰も言ってないから直らないだけで、言えば直るのか)

kabeyakabeya

Intel Macだと、11.0でデバッグ実行してもエラーになりません。
M1だからなのか、特定のドライバの問題なのか。

なので。
この件は、いったんここまでにして、M1 Macでいじるときは10.15にしてやることにします。
どっちみち、まずMac用にMetalをいじりたいわけではなくて、iOSでMetal使うための勉強、と思っているところなので。

kabeyakabeya

この件は、いったんここまでにして、M1 Macでいじるときは10.15にしてやることにします。
どっちみち、まずMac用にMetalをいじりたいわけではなくて、iOSでMetal使うための勉強、と思っているところなので。

これ嘘でした。非常に困りますね。SwiftUIのビューにMetalで何か書くとかできない…。
無理矢理10.15で動くSwiftUIにしようとすると、だいぶ最新から離れたものになってしまいます。

kabeyakabeya

M1 MacでmacOS 11.0以降をターゲットにして、Metalをデバッグ実行することができました!やった!

Edit Scheme...で、以下の設定を変更します。

  1. Options→GPU Frame CaptureをDisabledにします。
  2. Diagnostics→Metal→API Validation/Shader Validationをオフにします。


もう少し進むとまた何か出てくるのかもしれませんが、いったんクローズにしたいと思います。

このスクラップは2023/03/01にクローズされました