☠️
Cubism Coreでモデルを読み込む際の落とし穴
Live2D Cubism Coreの共有ライブラリをebitengine/puregoで叩いて描画しようと試行錯誤している中でかなりハマったポイントがあるので紹介。
モデルを読み込むにはcsmReviveMocInPlace
とcsmInitializeModelInPlace
を使うことになっているので、素直に書くと以下のようなコードになる。エラー処理は割愛。
type Core struct {
csmReviveMocInPlace func(uintptr, uint) uintptr
csmGetSizeofModel func(uintptr) uint
csmInitializeModelInPlace func(uintptr, uintptr, uint) uintptr
}
type Model struct {
csmMoc uintptr
csmModel uintptr
}
func LoadModel(core *Core) (m Model) {
moc := "Resources/Haru/Haru.moc3"
mocBuffer, _ := os.ReadFile(moc)
m.csmMoc = core.csmReviveMocInPlace(uintptr(unsafe.Pointer(&mocBuffer[0])), uint(len(mocBuffer)))
size := core.csmGetSizeofModel(m.csmMoc)
modelBuffer := make([]byte, size)
m.csmModel = core.csmInitializeModelInPlace(m.csmMoc, uintptr(unsafe.Pointer(&modelBuffer[0])), size)
return
}
いい感じに見えるし、実行しても処理は成功する。が、その後の関数呼び出しにおいてfound bad pointer in Go heap
だのsegmentation violation
だの致命的なエラーが発生しまくる。
読み込み自体は成功しているので、問題はその後の処理なのかと思って見当違いの箇所を調べたりしてしまい随分ハマったのだが、結論から言うとmocBuffer
とmodelBuffer
が破棄されているのが原因だった。
別に破棄されようが関係ないだろうと思っていたが、APIリファレンスをよく読むと
csmReviveMocInPlace、csmInitializeModelInPlaceで⼊⼒に使ったメモリ空間にcsmMoc、
csmModelが存在するので、⼊⼒したメモリ空間は保持しつける必要があります。
また、csmMocは対応するcsmModelすべてが破棄されるまで保持する必要があります。
これはcsmModelがcsmMocを参照しているためです。
と書いてある。たしかによく考えれば当たり前だった...。
なので、破棄されないように構造体のメンバとして保持したりすると解決する。
type Model struct {
mocBuffer []byte
csmMoc uintptr
modelBuffer []byte
csmModel uintptr
}
func LoadModel(core *Core) (m Model) {
moc := "Resources/Haru/Haru.moc3"
m.mocBuffer, _ = os.ReadFile(moc)
m.csmMoc = csmReviveMocInPlace(uintptr(unsafe.Pointer(&m.mocBuffer[0])), uint(len(m.mocBuffer)))
size := csmGetSizeofModel(m.csmMoc)
m.modelBuffer = make([]byte, size)
m.csmModel = csmInitializeModelInPlace(m.csmMoc, uintptr(unsafe.Pointer(&m.modelBuffer[0])), size)
return
}
ドキュメントやリファレンスはちゃんと読みましょうってハナシ
Discussion