2019年3月22日金曜日

SwiftからMetalへFloatの値を渡す(仮)

縦横比は無視(こういう演出なので)

(仮)がついているのは、「とりあえず動いたけど正しいかわからない」というツッコミ待ちフラグを示します。堤修一さんの「Metal入門」を読んでいることが前提です。

最初に結果を書きます。そのあとはほぼ雑談です。

まず、swift側のコード。drawの中です。valueが渡したい値で最初にバッファを定義して
        // バッファを作成
        var valueBuffer: MTLBuffer!
        let value:[Float] = [0.5]
        let size = MemoryLayout.size
        valueBuffer = device.makeBuffer(bytes: value, length: size)
次に生成したエンコーダーにセットします。
        renderEncoder.setFragmentBuffer(valueBuffer, offset: 0, index: 0)
.metal側
fragment float4 fragmentShader(ColorInOut       in       [[ stage_in ]],
                               texture2d texture  [[ texture(0) ]],
                               device float     *fBuffer [[ buffer(0) ]])
{
    float f = fBuffer[0];

fに値が入ります。めでたしめでたし。

[[ ]] の中の修飾子がどういう働きをしているのか理解できていなかったんですが…これ、GPUとCPUの共有メモリにtexure[0], [1], [2]....、vertex_id、buffer[0], buffer[1], buffer[2]....のような領域があって、
device float     *fBuffer [[ buffer(0) ]]
って書いた場合、「0番目のbuffer領域のアドレスをfloatのポインタfBufferにセットする」って意味なんですね。で、Swift側ではサイズとx番目かを示してデータをセットすれば、metal側ではそれをポインタ渡しみたいな形で受け取れる。

わかってしまえば簡単なんですが…いやー、苦労したたたた。

■背景■

「Metal入門」という本を読みつつ、Metalのお勉強をしています。お勉強中ですが、同僚のM氏から「作って」と言われている課題をこなさなければいけないので必死です。

その課題は撮影した画像をRGBとグレイに分割して、それらを重ねつつ動かすというやつです。CIFilterなどを探してみたのですが、どうにも上手く行かず、Metal様におすがりしました。

で、「Metal入門」を拾い読みして(それがいかんのだ)、fragment shaderを使えば画像の任意の点の色情報を取得し、それを最終的な描画位置の色として加工して返す、という処理が記述できることがわかった…のは良いですが、アニメーションとして処理したいのでCPUからパラメータを渡す必要があり、その方法を探していた次第です。

この記事が誰かの役に立ちますよーに。

■余談■

MSLでもif文書けるんですね。やってみたらエラーでないで意図した通りに動いたので思わずユーレカーと叫びながら表を走ってしまうところでした。ベクトルプロセッサだから無理だろと思って無駄な苦労してました。もちろん、GPUを使う上ではもっと効率のいい方法なんかもあるんでしょうけども、不勉強な凡夫として助かっております。

とりあえずfragmentShaderの実装については「今度、ここの座標の色を教えろや」って上位からドットの数だけ呼ばれるので、textureとして受け取った画像からsamplerを使って元画像の色の値を取り出し、そのカラー値(r, g, b, a)を返せば元画像がそのまま描画されるし、color = float4(red, 0, 0, 1)を返せば赤プレーンだけの値が返される…ということがわかって、そこから何とか書けるようになった感じです。

いやー…奥が深い。私基本的に業務系アプリ屋なんで、Metalのお勉強してもあんまり役に立たないんですけどねw でも、何かあったらお仕事いただければ幸いですm(_ _)m

0 件のコメント:

コメントを投稿

注: コメントを投稿できるのは、このブログのメンバーだけです。