makeRenderPipelineStateで失敗する件
macOS上で、MetalのmakeRenderPipelineState
(Swiftの場合)あるいはnewRenderPipelineStateWithDescriptor
(Objective-Cの場合)を実行したタイミングで、
というエラーがデバッグコンソールに出力されて、ウィンドウが空で表示される、という現象に2日間悩まされました(と言っても、まだ解決はしてません)。
具体的には、AppleのMetalのサンプルコード、HelloTriangleでもこの現象が起こります。
もうちょっと厳密に言うと、上記のサンプルそのものは動きます。
最初、サンプルの処理を流用したものをSwiftで書いたのですが、この件で動きませんでした。
次に、まったく同じ処理(のSwift版)にして、ファイルの構成もサンプルと同じにしましたが、やはり動きませんでした。何かSwiftは特別なおまじないのようなものが要るのかとか、Storyboardに何か設定もれがあるのかとか、色々調べました。ネットの情報を探してもそれっぽいのがありません。
最後に、Objective-Cのプロジェクトを新規に作って、ファイルをサンプルから差し替えました。それでもやはり動きませんでした。
ここに至って、プロジェクトの設定かなと思い始め、色々試したところ、
ということが分かりました(ちなみに上記のHelloTriangleの設定は10.12になっています)。
11.0以降で生じるのがなぜなのかは、これから調べます…
よくよく調べると、10.15以前に設定してても、Xcodeからデバッグ実行(Run)はできるものの、Archiveして実行ファイル形式にすると実行できない(空のウインドウが表示される)、という状態です。
(もっと言うと、~/Library/Developer/Xcode/DerivedData/以下にできる中間ファイルとしての.appでも空のウインドウが表示されます。Debug版でも)
なんだろ。
よくよく調べると、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の参照がありませんでした。
あと、たぶん忘れるので未来の自分に向けてメモ。
XcodeのDebug→Debug Executable...から、実行ファイル形式の.appをデバッグ
ですが、実行ファイルを選ぶとScheme編集画面?のようなものが表示されます。
ここで、「Debug Executable」のチェックを外さないと、リリース版をデバッグ実行できません。
外さない場合で、かつ、自分のプロジェクトだと、なのか、.dSYMがあると、なのか、その場合はRunボタンを押すと、デバッグ版が実行されます。
チェックを外してCloseして、Runボタンを押すと、そのまま選んだ実行ファイルが実行されるようです。
なんとなくMTKViewを使うのが良くない気がしてます。
良くないというか、ハマりポイントが多いというか。
あと未来の自分のために、URLを。
Using an MTKView object is the preferred way to interact with drawables.
とか書いてあるんだけども…
Metal-by-Exampleの人のチュートリアルコードも、Downgraded targets for first two weeks to be macOS 10.15 compatible
とかつって、何かを解決するのではなく、単にターゲットを10.15に下げてますね。
これは闇深い。
そして、「最初の二週間分だけ」かと思っていたら「Downgraded targets for remaining days to be macOS 10.15 compatible」もあってさらに闇深いw
MTLRenderPipelineDescriptor.rasterSampleCount
に値を設定すると、
は発生せずに、
が発生します。
空のウインドウが表示されるのはそのままだけども、前者はいきなりどっかにすっ飛んでいくのに対して、後者はちゃんとthrowされてきます(Objective-Cの場合は、引数の&errorに入ってきます)。
追記:
3だと上記の「not supported by device」でしたが、4だと前者になりました。
元の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でどうするのか調べないと。
怪しいのは、コールスタック上、いくつか前から現れる_MTLCompilePerformanceStatisticsEnabled
や_MTLAddCompilePipelinePerformanceStatistics
というシンボル名です。
macOS 11.0以降をターゲットにすると、パフォーマンス統計?が勝手に有効になって要らん処理をしようとして失敗している、とかではないかなと思います。
ここに「View Remarks and Recommendations」という節があるのが、なんか関係ありそうななさそうな…
結局。
macOS 11.0以降にターゲットを設定すると、デバッグ版はエラーで実行できないけども、リリース版(実行ファイル形式)は動く、という感じに見えます。
デバッグ版ドライバ?Xcode?の問題なのかなという気がします。
これが最近のバージョン、例えば13.2の問題、とかいうなら直るまで待つかという気にもなりますが、macOS11って2世代前なのでもう直らん気がしてしまいます。
(もしくは誰も言ってないから直らないだけで、言えば直るのか)
Intel Macだと、11.0でデバッグ実行してもエラーになりません。
M1だからなのか、特定のドライバの問題なのか。
なので。
この件は、いったんここまでにして、M1 Macでいじるときは10.15にしてやることにします。
どっちみち、まずMac用にMetalをいじりたいわけではなくて、iOSでMetal使うための勉強、と思っているところなので。
この件は、いったんここまでにして、M1 Macでいじるときは10.15にしてやることにします。
どっちみち、まずMac用にMetalをいじりたいわけではなくて、iOSでMetal使うための勉強、と思っているところなので。
これ嘘でした。非常に困りますね。SwiftUIのビューにMetalで何か書くとかできない…。
無理矢理10.15で動くSwiftUIにしようとすると、だいぶ最新から離れたものになってしまいます。
M1 MacでmacOS 11.0以降をターゲットにして、Metalをデバッグ実行することができました!やった!
Edit Scheme...で、以下の設定を変更します。
- Options→GPU Frame CaptureをDisabledにします。
- Diagnostics→Metal→API Validation/Shader Validationをオフにします。
もう少し進むとまた何か出てくるのかもしれませんが、いったんクローズにしたいと思います。