スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

マルチスレッドローディングの話

(SharpDX.Toolkit前提)

ゲームである以上グラフィックスやモデルデータなど読み込みが必要であって、そして読み込みには時間が必要なのである。

プログラムで時間がかかる処理をして、その間ユーザーの操作を受け付けない状態になると、ユーザーさんは困ってしまうわけだ。
windowsではゲームループに加えてウィンドウのメッセージループも処理しないといけないんだけども、ゲームループ内で時間のかかる処理をして次の処理に到達しないでいるとウィンドウが固まってしまっているように見えるわけだ。この間はウィンドウを動かすこともキー入力も受け付けなくなる。

なので大抵のゲームで用意されているローディング画面。ローディング画面では大抵何かが動いていて、「プログラムは固まってないヨ」的な表示をする。
つまり、読み込みと描画を同時にしている。

んなこたーみんな知ってる。


マルチスレッドで読み込みを行うには、たとえば読み込みのための入り口のメソッドを新しいスレッドにぶち込んで実行すればOKだ。

そして壁にぶちあたる。
壁とはDirectX様である。
モデルデータの読み込みではDirectX様にお願いして頂点バッファやシェーダーやテクスチャ等を作ってもらう必要がある。こうゆう処理と描画処理がぶつかるとDirectX様がお怒りになる。
ぶつからないようにするにはクリティカルセクションをつくってどうのこうのというものがあるのだけれど、デッドロックやクリティカルセクションの範囲によってはマルチスレッドする意味がない感があるのと、多用すると通常の処理速度にも影響が出る(はず)。そして死ぬほどややこしくなる。自分の環境で動いたとしても、ほかの環境ではタイミングが大きく異なる可能性があるので他の環境では落ちるかもしれない。しっかり設計しないとまるで歯が立たない。マルチスレッド嫌い。
マルチスレッドは目的ではなく便利な道具として使いたい。なのでその制御のために苦労はしたくない。
なので、いろいろ試行錯誤したけど難しいわこれ俺のポンコツ脳みそでは完璧な実装は無理だわ死のうと思った。
ファイルIOやデータ構築までを非同期にして、DirectX様にバッファ作ってもらうとこは同期に、と処理を分けようとしたけれどソースのカオス度がかなり上がってしまって一朝一夕にできるもんじゃないな死のうと思った。

一朝一夕にやりたい。


整理すると
ここで問題になっているのはマルチスレッドであり、つまり描画と読み込み等が同時に実行されることが問題なのだ。
→同時に実行されなければいいのではないか。
→ロジックループと描画ループは同時には動かない。
→ロジックループ内であれば読み込みをしても描画ループとはぶつからない。
→ロジックループ内でいっぺんに読み込み処理をするとそれが完了するまで描画できないしウィンドウが固まった状態になる。(根本に戻ってきた)

んでまずこう考えたり調べたりした。
ロジックループで読み込みスレッドを開始、描画ループではスレッドを止める。ロジックループに入ったらスレッドを再開。
→C#のThreadではResumeやSuspendは非推奨メソッドになってる。スレッドに「止まって」って言っても即時に止まるわけではなさそう(未検証)なので、たぶんスレッドの停止・再生を制御する方法は無理そう。

次に考えたのは、同一のスレッドで読み込み処理を細切れにちょっとずつ実行できないかということ。いっぺんにやったら問題になるならば、ちょっとずつやればいい。

検索しまくってると出てくるのはマイクロスレッド、コルーチンという考え。

めんどくさいので省くけど(本題なのに・・・)、IEnumeratorで検索すると詳しいことがわかります是非検索してくださいボクはよくわかりませんでした。

で、このローディング目的のためにという限定的な見方をすると、読み込みメソッドの中である一定の位置まで実行したらメソッドを抜けて、次に呼ばれたときに前回抜けたところから再開するという使い方ができる。
IEnumerator LoadSomething()
{
LoadA();
yield return null;
LoadB();
yield return null;
}
これを呼ぶときは、
IEnumerator load = LoadSomething();
load.MoveNext(); -> LoadA()が呼ばれる
load.MoveNext(); ->LoadB()が呼ばれる
というふうにできる。(たぶん)(たぶんて・・・)
while(load.MoveNext()) { }
で最後まで実行できる。終わりまで行ったらMoveNext()はfalseを返す。
こうして処理をちょくちょく抜けて細切れにして、いっぺんに処理時間を食わないようにすればプログラムが固まったように見えない上に同一スレッドなので描画処理ともぶつからない超安全な読み込み処理ができる。
yield returnの嵐になるし、いちいちEnumerator取得してMoveNext()していかないといけないのでよく考えないとわりとソースがぐじゃぐじゃになるのでご利用は計画的に。
ロジックループの中で一回ないしは複数回MoveNext()しつつ、描画ループでは動いてるローディング画面を描画していけば、見た目にはマルチスレッドで読み込んでるのとそれほど違わない感のあるローディング画面が出来上がる。
つーか出来た。
ロジックループに乗せる以上、ロジックループの周波数で読み込み完了までにちょっと時間がかかる可能性があるんだけれども固まるよりははるかにマシ。一回のロジックループ内で複数回MoveNext()することで速度は稼げる。
マルチコア時代なのにそれを活かせないのはアレですが。


つーか
実際はこれforeachで使えるような、コレクション等を列挙するための仕組みのはずなのでこういう使い方は想定されてるかは知らない。少なくともローディングに使うような話は聞いたことがない。
「そういう機能じゃねえからこれ!」って言われると怖いのでここで読んだ話は人に話さないでネ。

ともかく処理を途中で抜けてまたそこから再開する、という点を利用したローディングを作れたのだった。


こういうのもちっと再利用可能な形にまとめつつ、進める。

っていうかマルチスレッドローディングの話のはずだったのに結局採用したのはこれ非同期処理じゃないじゃん的な。






背中いたい。。
スポンサーサイト

コメントの投稿

非公開コメント

プロフィール

zerobyteorbit

Author:zerobyteorbit
deathpiyoがgameをdevelopしたり、musicをcomposeしたり迷走したりする。

現在は迷走中。

under the lotusはリビルドのために考え中。

deathpiyo twitter

UnderTheLotus test3h(download)
I'm thinking about rebuilding UTL.


同人音楽アルバム
[Lovers Immortality]
-Japanease-
Lovers Immortality -works until worldend- DLsite.com直リンク
Melonbooks DL
-English-
Lovers Immortality -works until world end- Link to DLsite.com

18+
【東の森の魔女2 VS 魔王 -終宴する世界と肛虐(逆)の魔女たち-】
東の森の魔女2 VS 魔王 -終宴する世界と肛虐(逆)の魔女たち- DLsite.com直リンク
DMM.同人

紹介ページ


【地下迷宮の機械姦自壊オナニー生活。】
地下迷宮の機械姦自壊オナニー生活。 DLsite.com直リンク
DMM同人

紹介ページ




DLSite
Link to DLsite.com

最新記事
最新コメント
最新トラックバック
月別アーカイブ
カテゴリ
FC2カウンター
検索フォーム
RSSリンクの表示
リンク
QRコード
QR
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。