2017年1月5日木曜日

OpenGLで pixel buffer を60fps描画する方法

たまには技術的なことを書こう。

かなり以前、Android版VGSの描画をSurfaceViewからOpenGLに変更する試みをしたものの、60fpsのパフォーマンスが確保できず断念したことがあります。

VGSの描画処理は、

  1. 描画API(vgs_putSP等)を発行するとメモリ(システムメモリ)上に確保された8bit仮想VRAMに出力(OS非依存)
  2. VSYNC(垂直同期)の間隔で8bit仮想VRAMの内容を画面に出力(OS依存)
という流れで映像を画面に出力してます。

Android版VGSの上記2(OS依存部分)の実装は、RGB565のBitmapに8bit仮想VRAMの内容を(パレット変換をしながら)書き込み、それをSurfaceViewへCanvas#drawBitmapで描画する形になっています。

当時、これをOpenGL化するに当たって、毎フレームテクスチャを生成して描画するという方法を試しました。

しかし、テクスチャ画像の生成には結構時間がかかります。
また、システムメモリからGPUのメモリへの転送速度は物凄く遅い。
色々工夫して、50fps台ぐらいまで出せるようになったものの、それでは実用上問題がある(フレームレートだけでなく発熱量や消費電力も凄いことになる)ので、当時は結局OpenGL化を断念しました。

今回また、映像処理をOpenGL化することに再挑戦してみようと思います。

今回は、映像をテクスチャで表示するのではなく、1pixelを2ポリゴン(1つの四角形)で表現し、ポリゴンの色を変更することで映像を表示する仕組みを試してみることにします。

また、今回はVGSではなく、先日作ったLaiNESベースのファミコンエミュレータを使います。VGSのアーキテクチャはゲーム機エミュレータと同じ仕組みで作っているので、やっていることはファミコンと大差はありません。

以下、実際にOpenGL対応したAndroid版LaiNESを作成時の Pull Request です。
https://github.com/suzukiplan/nes-view-android/pull/4

ファミコンの場合、画素は256x240なので、pixel数=61,440 になります。
ポリゴン数 = pixel数 x 2 なので, 1フレーム辺り122,880ポリゴン。
60frame で描画するポリゴン数は, 7,372,800(737万2,800ポリゴン/秒)。

初代PlayStationの最大ポリゴン数のSCE公表値は、

  • 演算能力 = 150万ポリゴン/秒
  • 表示能力 = 36万ポリゴン/秒

とのことなので、この方式だと少々キツイ。
まぁ、初代PlayStationで動かす訳ではないですが。
ただ、もう少しポリゴン数を抑えた方が良い感じかなと。

そこで、上記のPull Requestでは、色情報の変化があったpixelのポリゴンのみ色を変更するようにしています。

これで適当なゲーム(某横スクロールアクションゲーム)を動かしてみて、1フレームあたりに更新されたピクセル数を測定した結果が下図です。
画面切り替わり直後などに最大更新が走りますが、ゲームプレイ中は多くても1万pixel未満の更新(これはスクロール時)で、スクロールしていない状態であれば100pixel未満の更新しか走らないようです。

常にスクロールしている状態(1万pixel更新)であれば、120万ポリゴン/秒ということになるので、これなら演算能力的には初代PlayStationでも耐えられそうです。(表示能力は追いつかなそうですが)

ナッツ&ミルクやドンキーコングみたいな固定画面アクションゲームなら余裕です。
まぁ、最近のスマホならフル更新(737万ポリゴン)であっても60fps保てそうな感じかと思います。(Android実機が手元に無くてエミュレータでしか動作確認してません)

0 件のコメント:

コメントを投稿

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

合理的ではないものを作りたい

ここ最近、実機版の東方VGSの開発が忙しくて、東方VGSの曲追加が滞っています。 東方VGS(実機版)のデザインを作りながら検討中。基本レトロUIベースですがシークバーはモダンに倣おうかな…とか pic.twitter.com/YOYprlDsYD — SUZUKI PLAN (...