描画処理のスレッド分割について

今のご時世、すでに普通にタスクマネージャではCPUの負荷グラフが複数表示される様な時代ですからADVシステムでもマルチCPU(マルチコアCPU含む)に対応した方がいい時代です。

が、実はこの「描画処理の分割」というのが意外と難しいわけです。特に2Dのみで構成されているような場合だと

描画を完了した絵に対してさらに描画するというシーケンスを繰り返さないと結果が正しくなくなるためにシングルスレッド的に処理する場合がほとんどで、

そもそも「描画処理の分割」を考えること自体が難しいです。

たとえば、以下のような方法を使います。

描画領域を分割し、各スレッドに割り振る

たとえばデュアルコアCPUなら、画面を上下二つに仮想的に分割して

スレッド1 => 上半分の描画担当 (クリッピング領域を上半分にして描画処理を行う)

スレッド2 => 下半分の描画担当 (クリッピング領域を下半分にして描画処理を行う)

メインスレッド => 各スレッドの描画終了待ち

のように分割します。わかりやすい分割方法です。この方法の利点は

  • 普通の2D描画でも確実に処理の分割ができることが保証される
  • 負荷分散の分割方法は任意なので、上下分割がよくないなら左右分割といった変更が行いやすい

問題点としては、たとえば

  • 描画ルーチンにクリッピング処理がないときはその部分の追加処理が必要であり、追加のデバッグが問題になることがある
  • 描画状態を正しく分割できないとき(たとえば回転した絵を上下にかかって描画するとき)に「そのタイミングですべての描画を待つ」必要があり、負荷分散の度合いが下がる
  • 対象のサーフェイスが「書き込んで読み込まれる」のようなタイプの時に正しく描画待ちを行う必要がある

という感じです。

描画オブジェクトごとにスレッドを割り振る

ちょっとわかりにくいかもしれませんが、ADVシステムが3Dベースで組まれているときに有効な分割方法です。

2Dのみの場合でもマルチレイヤーサーフェイス(サーフェイス内にレイヤー状態の絵が存在している)ときに使えます。

以下のような動作を行います。

スレッド1 => マルチレイヤー1の内部描画処理 (たとえば、立ち絵+顔を一枚に合成する処理)

スレッド2 => マルチレイヤー2の内部描画処理 (たとえば、テキストウィンドウと各種ボタンを一枚に合成する処理)

メインスレッド => 各スレッドの描画が完了した後、マルチレイヤー1とマルチレイヤー2をスクリーンに描画 (たとえば、顔つき立ち絵を描画してテキストウィンドウを上に描画する)

のように分割します。この方法の利点は

  • 描画の開始点での分割なのでコードの変更量が少なくてすむ
  • 描画オブジェクトを独立して扱うのでマルチスレッド処理の問題点である同期をほとんど考える必要がない

問題点はたくさんありますが・・・たとえば

  • 最後の描画処理の負荷が高い場合はシングルスレッド的な処理となるため、分割の効力が少ない(3Dベースの場合は最後の処理がグラフィックカードに移るのであまり問題ない)
  • 内部描画処理が階層化していた場合(上記の例ではマルチレイヤー1内にさらにマルチレイヤーが存在するとき)には分割処理をうまく処理しなければならい
  • 描画状態により負荷分散の度合いにばらつきが出る。マルチレイヤーが存在していないときに負荷分散にならない

という感じです。

これらを複合させれば強力ですが

そこまでする必要があるのはせいぜい

  • かなり高解像度の描画が必要となるとき(1280×720とかまで上がると2D描画の部分で負荷がかなり大きくなる)
  • 2D描画部にかなりの数の描画オブジェクトが存在し、ハードウェア側で処理できない描画エフェクトを行うとき

くらいです。それが想定できるシステムであれば対応させる必要はあると思います。

この頃のゲームはワイドモニタが一般化してきた結果として解像度がワイド化+高解像度化しているようなので、それなりに必要かもしれません。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

この記事のトラックバック用URL