Posted at

過去に開発した、高難易度案件の技術概要

More than 1 year has passed since last update.

自分語りすみません。自己紹介ついでです


自己紹介

学生の頃ふと図書館においてあった古いゲーム雑誌に


「ゲーム業界では服装も髪も自由だ。黒髪でリクルートスーツ着ている人より、金髪でラフな服装をしている人のほうが きっとエンタテイメントな人生をおくってるに違いない。ゲーム業界では独創的な人は大歓迎だ」

といったリクルート記事をみて 一瞬でゲームプログラマに決めました


学生時代は勉強は出来たけど、集団生活や 授業を聞くことが出来ず常に授業中走り回ってました

大学も3ヶ月で教師と喧嘩して辞めて 東京に出て フリーでビジネスプログラミングをしました

高校時代に ゲームを逆アセンブルして、グラフィックローダや音楽ドライバ等を高速化して

会社に送りつけたりしてたので、すんなり仕事はできました

そこからは お金がほしい時はビジネスプログラム、夢がほしい時はゲームプログラムと

業界を行ったり来たりし、ソーシャルゲームバブルで ゲーム業界がビジネスと同じ金額になってからは

だいたい ゲーム・エンタメ系で仕事しています

5年前に チームを組んで仕事を請けはじめ、会社を作ったほうが便利だったので起業

就職経験は結局ゼロです

今は 社員1名と外注が6名ぐらいで仕事まわしています


最初のゲームの仕事

ネット掲示板で、コンシュマー会社がDirectXやCOM、通信、Linuxの経験がなく

色々と回答していたら 採用したいと言われ、バイトでならOKと入った

大手ゲーム会社の R&Dで、ゲームセンター(アーケード基盤)と 家庭PC(DirectX)をインターネットでつなぎ

対戦ゲームを行う 研究開発

当時ゲーム会社ではノウハウがなかった ネットワーク、DBの知識が ビジネスで鍛えた私にマッチした

それに比べ趣味でDirectXを触っていたのが役に立った

しかし その会社の副社長のデザイナーが酒乱で酒を飲んだら暴れて迷惑かけるので

酒の席について口論となり 辞めた

私はお酒を1滴も飲めない体だけど、お酒は迷惑かけないように みんなが楽しくなるように飲んでください


PCと特殊ハードを使った独自3Dエンジンゲーム メイン

特殊ハードをつかった アーケード端末

なぜ DirectXを使わずに独自3Dエンジンなのか・・・というと

元々はDirectDraw(2D)とDirect3D のコンポーネントが別れていて

Direct3Dが出た当初はパフォーマンスが悪く、3D部分は自分でアセンブラ入れて自作して

DirectDrawで描画するのが速かったのと

Direct3Dはデバイスごとに使用できる機能が異なりビデオカード対応が面倒だし動かないマシンも多く

それなら 自分で3D作ったほうが、DirectDrawの互換性だけ保てばいいので楽だったから

ところが nVidiaのハードウェアT&Lにより、ジオメトリ演算がGPU上でハードウェアパイプラインになり

Direct3Dも高速化し

自作3Dでは既に速度が絶対勝てなかったけど

互換性のために 自社で3Dエンジンを作る といわれ

メインプログラマとして 殆どを作った

3DCGの知識もその時に作りながら全部覚えた


PS2

悪名高いPS2

会社は大手だったけど、ゲーム会社としては知名度がなく、まともなライブラリももらえず

自社でライブラリを作る事になり、ライブラリを作れる人がいないので私が駆り出された

3Dエンジンぐらいなら・・と 気楽に請けたんだけど

DMAの調停から行わなければならない、サウンド鳴らすには命令をDSUに送って・・・

メモリアロケータもなければ なんにもなく

カーネルから作るとは思ってもみなかった・・

そして、VU0とVU1である

当時はまだ プログラマブルシェーダがなかったので画期的で

今で言う 頂点シェーダのようなものである。

しかし アセンブラで記述する必要があり、しかも SIMDとMIPSの2コアあり

それらがレジスタを共有して並列で動くので 並列アセンブラ!!

という 謎な技術でびっくりした

後にも先にも あんな変態なハードウェアは出てこないと思う。。。

ほんと びっくりしたけど ゲームエンジンと タイトル2本作り上げた


DirectX11のアバターレンダリングサーバ

レンダリングサーバを作るだけなら簡単だが、パフォーマンス要求が恐ろしかった

元々2Dレンダリングで秒間50だったのを 秒間100は必要と言われる

しょっぱなの目標から絶望的だったが、結果的に 秒間800オーバーの数字を叩き出した

2GPUの案件


Deferredコンテキスト

まず、当時出たばかりのDirectX11を利用した理由は Deferredコンテキストのためである

(ライティングを遅らせる Deferred レンダリングとは全く違うもの)

通常は 1つのレンダリングコンテキストが終了してから次のレンダリング命令をGPUに送るのだが

その部分を非同期にし、レンダリング処理中にテクスチャ転送したり、次のレンダリングのための設定を送ったり出来る

技術的には はじめての機能で多少むずかしかった


MultiThread Socket

マルチスレッドによるSocketにしたのは

非同期も試したが、1接続の処理が重く、20接続以上に増やしても GPUがボトルネックになりさばけない

また、CPUのコンテキストスイッチコストもあるため、同じコアにスレッドを固定するほうが速く

けっか マルチスレッド型にした


コンカレントリスト

アバターのアイテム数が何十万とあり、全部がGPUメモリには乗らないので

よく使われるアイテムはGPUに、そうでないものはメインメモリに、あまり使われないものはHDDに

と 3段階のキャッシュシステムを作った

そして メモリ効率のため、それらはすべてのスレッドからアクセス出来るようにした

そのため、スレッドセーフなコンテナが必要であった

その時の候補として IntelTBB、Silk などがあった

結局 IntelTBBを使ったが、Silk++ のほうが速くなったかもしれない


64ビットアセンブラ

最近のC++コンパイラは人間がアセンブラ書くより速く最適化する

まー 実際は熟練者がアセンブラ書けばコンパイラに勝てるのだが、コンパイラで十分である

ただし、SIMDが使える大量データの処理においては 未だに人間が書けば大きくパフォーマンスアップ

出来る場所が多い

ので その部分をアセンブラにした


CUDA

減色処理やデータの変換、Jpeg化などは SIMDアセンブラ使うより GPGPUのほうが速い

DirectComputeやOpenCLを使ってみたが、CUDAでかくのが最も速かったのでCUDAにした

ただし、システム自体がGPUがボトルネックになっているので、スレッドのうちのいくつかはCPUで処理するようにしたり。。


アニメパース

3DCGだと中心からずれると歪む。

例えば二人横に並んでいると 2D絵では二人が並んでまっすぐ前に向くが

3Dだと中心に向かって少し斜めに見える(リアルでは正しい)

ところが アニメに慣れていると それでは違和感を感じるため

透視変換行列を二人かえることで 正面を向くようにしたり

勇者ポーズ等で極端に手前のものが大きくなるよう ジオメトリを変更する機能をいれた


OIT

知っての通り半透明は重ねる順番で色が変わる

そのため、半透明の順番を制御するためのアルゴリズムが必要だが

ちゃんと処理すると非常に重い・・・

が、一応対応した


ジオメトリシェーダ、マルチレンダーターゲット

レンダリングエンジンでは アニメーションをコマ送りでレンダリングし動画にしている

同じモデルを数コマレンダリングするので、レンダーターゲットを変え ジオメトリシェーダで異なるボーンを適用させることで

複数コマを1回のレンダリングパスで処理可能である


ドライバの不具合

秒間200程度なら普通に動くが、それを超えるとブルースクリーンになり落ちる

明らかにドライバに不具合があるので メーカーに相談し、しばらくしたらドライバの不具合が改善した


サーバでの工夫

上記本気で対応して 秒間800を超えて、企画的にはそれ以上高速化不要だったが

最も簡単に高速化できるサーバの問題を見過ごすわけにはいかず

アバターの生成において ハッシュを使いリバースプロキシを頑張った

ハッシュもMemcachedを使い、複数のサーバからも高速アクセス可能に

また、レンダリングエンジン内部のキャッシュヒットを高めるため、サーバが複数たっていたら

アバターのベースごとに、優先するレンダリングサーバを自動的に分散させるシステムも作った

ただし 上記のキャッシュとあわせて、爆速すぎて 10台可動必要な予定が 1台の可動で十分だったため

永遠に分散されることはなかった


上記レンダリングエンジンのスマホ化

時はスマホ。上記レンダリングにつかったモデルをつかってゲームエンジン作成をした


C++ JNI

AndroidはJava、iPhoneはObjective-Cであるが

Javaで作ると遅いし、2つのエンジンをメンテしたくないので

C++でエンジンをつくり、JNIなどを使い AndroidやiPhoneから呼び出す事にした

結果は大成功だと思う

フロント側の要望で 例外入れてほしいとか言われたが、当時のNDKは例外非対応

だったが、無理やり exceptionやrttiライブラリを組み込み 例外に成功


ボーン数とウェイト

モデルは255本ボーン4ウェイトだった

頂点ウェイトをモデルロード時に2ウェイトに削る処理は簡単に出来る

が、スマホのGLES2.0では uniformレジスタが128本であり 2ウェイトでもせいぜい30ボーンしか扱えない

グラフィック的にボーンの数をそこまで減らすことが不可能なたえ しかたなく

CPU上でスキンメッシュをすることにした

高速化のために全部アセンブラ(NEON)で書いた

コマ数が比較的少なかったため、一度にコマ数ぶん スキンメッシュ適用後の頂点データを保存しそこからレンダリングできた

ただ、それだと カメラを動かした時に スキンメッシュ処理を待っていると1秒ラグるので

1コマずつマルチスレッドでなる早で計算し

1コマ目が計算終えたらはじめて 新しいモーションでレンダリングして・・

と 遅延がなるべく見えないように 工夫をした


右手左手座標問題

DirectXでは左手座標、OpenGLでは右手座標のため

DirectXのモデルをそのまま使う必要があったため、扱いに苦労した


機種問題

Galaxyシリーズ、Aquosシリーズは ハードや実装が特殊で

対応していない命令があったり、FrameBufferを読めなかったり

その機種だけ遅かったり 色々対応に苦労して

OSのコードまで読むことになった


C++のMMOサーバ

スマホゲームでのMMO的なゲームのリアルタイムサーバ開発

本来は簡単だったが 私がこだわりすぎて 苦労してしまった ごめんなさい


要件

要件は1サーバあたり1000人もさばけば十分

会社の実績的に1サーバ500ぐらいさばけるのでそこから改良してくれ

C++が最も速いのでC++で


会社の実績コードをみて

マルチスレッドじゃC10K問題で壁が見える。ここは非同期にすべきだ

メッセージ関連でMutex使ってスレッド間で同期をとっているがこれは明らかなボトルネック

これもシングルスレッド非同期にして 同期を防げば早くなる

あなたの資産は使わず 1からBoost.Asioで非同期で作ります


Boost.Asio

まず お客さんに対して、資産をつかわず 非同期で作り直す説明をするための

非同期はすごいぞ 資料を作るのが大変だった

経営者としてバカなことに、それでも もらうギャラは同じなの。

ただ 技術者として、もっと良い技術を知っていて 使わない事はできなかった

ただ 私自身 非同期プログラミングもやったことなく、当然Boost.Asioなんて知らず

STLだってまともに使ったことなかったので

結果的に 泣きそうなほど苦しんだ2週間がある

そのあたりは Qiita記事や SlideShareにいろいろかいてある・・


今現在

C++のサーバいこうは ゲーム作ったり、サーバレスでのゲームサーバや、VirtualYoutuberのシステム開発

あるいは 仮想通貨の取引所の開発

と 色々な仕事をしているが

まだ 苦しむようなものには あたってない