tl;dr
go-gl は Go の OpenGL バインディングです。 Windows などのデスクトップ環境に対応しています。実装は単純に C の関数を Cgo を使って呼ぶだけです。が、 Cgo には後述するような問題があり、現在のところ必要悪とみなされています。
ところで Windows では、 DLL からの関数ポインタ取得および syscall.Syscall
などの関数を使うことで、一切 Cgo を使わずに C 関数を利用することができます。自分はこの修正を提案し、無事アクセプトされました。現在 PR がレビュー中です。
提案の詳細は Design Doc にまとめています。本記事はこの提案のざっくりとした和訳および補足説明です。
背景
OpenGL は C の関数セットであるため、 Go と OpenGL のバインドに Cgo が使われています。しかしながら Cgo には以下のような問題があります:
これらは特に Windows ユーザーにとっては負担です。ほかの POSIX な環境とは異なり、 C コンパイラに馴染みがないかもしれないからです。幸運なことに、 Windows では syscall.Syscall
関数を使って C 関数を Cgo なしで呼ぶことができます。残念なことにこの手法は他のプラットフォームでは困難です。
筆者は glow (バインディングのジェネレータ) を修正し、 Windows では syscall.Syscall
を使って Cgo に依存しないようにする提案をしました。
コードの変更
glow は 4 つのファイルを出力するので、説明も 4 パートに分けます。
conversions.go
Go と C の値を相互変換する関数など。筆者の見る限り Cgo を一切使わなくても同等のことができるので、そのように修正します。
debug.go
Go の関数を C から呼び出す必要があるため、 syscall.NewCallback
を使うという修正をしました。
というか現在そもそもこの関数まわり、全く動いていない気がするんですよね。誰も使ってないのでは??
procaddr.go
関数名から関数ポインタを取得する実装は Cgo を使わずに syscall.NewLazyDLL
などを使えばいけるので、そのように修正します。
package.go
一番作業量が多い部分。基本的に syscall.Syscall
を呼ぶように修正するだけなのですが、引数の数によって syscall.Syscall6
だったり syscall.Syscall9
だったり、呼ぶ関数が変わってしまいます。その修正を行います。
syscall.Syscall
系の関数は引数をすべて uintptr
で受け取ります。整数型など、たいていそのままコンバートすればいいのですが、一部例外があります。 bool はそのための if
文が必要だったり、浮動小数点型は math.Float64bits
なで変換してあげる必要があります。
Proof of concept
実際提案だけだと絵に描いた餅なので実際に example を動かしてみました。詳細な手順は Design Docs を参照してください。
後方互換性
ほとんどの関数はそのまま動くことが期待されますが、現時点でどうしても動かない関数が 2 つあります:
func LGPUCopyImageSubDataNVX(sourceGpu uint32, destinationGpuMask uint32, srcName uint32, srcTarget uint32, srcLevel int32, srcX int32, srxY int32, srcZ int32, dstName uint32, dstTarget uint32, dstLevel int32, dstX int32, dstY int32, dstZ int32, width int32, height int32, depth int32)
func MulticastCopyImageSubDataNV(srcGpu uint32, dstGpuMask uint32, srcName uint32, srcTarget uint32, srcLevel int32, srcX int32, srcY int32, srcZ int32, dstName uint32, dstTarget uint32, dstLevel int32, dstX int32, dstY int32, dstZ int32, srcWidth int32, srcHeight int32, srcDepth int32)
syscall.Syscall
系の関数は引数の数ごとに関数名が異なりますが、最大が syscall.Syscall15
であり、 15 個が最大値です。しかしながらこれらの関数は 16 個以上引数を取ります。よって syscall.Syscall
の手法ではこれらの関数は呼べない、ということになります。
この手法の唯一の懸念点だったのですが、「まあこの関数、誰も使ってないでしょ」ということで、未実装状態で置いとくということで、一件落着しました。
ついでながら syscall.Syscall18
を提案し、実装してしまいました。 Go 1.12 から使えるようになります。よかったですね!
現在の状況
PR を提出しましたが、現在レビュー中です。結構中の人が忙しいようです。早ければ年内にもマージされるでしょう。
あとは「これを入れるとしてメンテする人いるの?」という問題があって、「じゃあ自分がやります」と立候補したところ、 go-gl のメンバーに入れていただきました。今後とも宜しくお願いします。
ちなみに拙作の Ebiten では修正が適用された go-gl を先行して独自に使用しています。よって少なくとも go-gl 部分については Cgo 依存がなくなりました。