Saturday, May 23, 2015

Android StudioのGUIを使わずにビルド→インストール→起動

以下のようなバッチファイルをgradlew.batと同じ位置に作っておくことで、Android Studioを起動せずともコマンドライン又はエクスプローラからビルド、インストール、起動ができるようになります。NDKを使用している場合のビルドまで問題なく出来ました。

※ パッケージ名は、com.pinotnoir.adamasを、アクティビティ名はcommon.pinotnoir.glactivity.PinotGLActivityを置き換えてください。

ところで、"ADB server didn't ACK" というエラーに悩まされていたのですが、原因はNsight Tegra Visual Studio Editionでした。これが内部でADBをつかっているため衝突するようです。Visual Stuidio起動中に上のバッチファイルが使えない場合、Visual Studioを終了すると使えるようになりました。



Nsight Tegra Visual Studio Editionを一時停止することはできないようです。Visual StudioとAndroid Studioを同時に使いつつ、Nsight Tegraも使いたい場合、面倒なことになりそうです。Nsight Tegraをアンインストールすると衝突は起こらなくなりました。

Thursday, May 14, 2015

GitHubのDirectXTKをgit submoduleとして組み込む

MicrosoftのGitHubアカウントでDirectXTKが公開されています。gitのsubmoduleとして組み込めるので格段に管理しやすくなりそうです。早速組み込んでみました。

組み込み方


git bashからDirectXTKを組み込みたいプロジェクトのディレクトリに移動し、以下のように入力すればDirectXTKフォルダ以下にDirectXTKのプロジェクトファイルが生成され、DirectXTKを参照するコミットを生成できます。

$ git submodule init
$ git submodule add https://github.com/Microsoft/DirectXTK.git DirectXTK
$ git commit -m "DirectXTK added as a submodule"

submoduleの何が良いのか


自分のレポジトリに外部のgitレポジトリの参照を加えることで、(半自動的に)一緒にチェックアウトできるようになります。自分のレポジトリを肥大化せずに外部モジュールを取り込めるわけです。また、参照にはバージョンも含まれているのでバージョンを取り違えてビルドに失敗することもありません。何より外部モジュールの組み込み方をgitが標準化したという側面が大きいです。

当然ながら、取り込みたいモジュールがgitレポジトリとして公開されているのが前提条件になるのですが、MicrosoftもGitHubでプロジェクトを公開してくれるようになりました。ありがたいですね。

submoduleは以下が詳しいです。

→ Git のさまざまなツール - サブモジュール

レポジトリについて


→ https://github.com/yorung/dx11x

D3DXやDirectX SDKを使わないモダンなDirectX11で.xファイルを読み込んで表示するプログラムです。スキニングも含めてアニメーションもひととおり対応しています。




Saturday, May 2, 2015

OpenGL ES 2.0のvec4配列でUBOもどきを作る際の注意点

OpenGL ES 3.0ではUBOやVAOのような現状のハードウェアで効率的に動作するAPIが使えるようになります。しかし、開発者はモダンなAPIへの対応作業を迫られる一方、ES 2.0もサポートし続けなければいけません。https://developer.android.com/about/dashboards/index.html にもあるように未だに65%のデバイスはOpenGL ES 2.0であり、この状況は当分続きそうです。
「じゃあES2.0だけでいいや」では時代遅れになるので、ES2.0でもモダンなフリをしながら両バージョンでコードを共有する方法を模索してみます。

複数のuniform変数を配列化してひとつに


http://on-demand.gputechconf.com/gtc/2013/presentations/S3032-Advanced-Scenegraph-Rendering-Pipeline.pdf の24ページ目でuniform block(UBO)を使えない場合に、複数のuniformを配列にまとめて使う方法が提案されています。これは効率的であるのみならず、C側では構造体で転送するデータを管理できるので、UBOを使ったES 3.0のコードとのアーキテクチャの差を吸収できそうです。
https://devtalk.nvidia.com/default/topic/777618/scenix/announcing-nvpro-pipeline-a-research-rendering-pipeline/ この掲示板に動画もありますね。

mat4かvec4かfloatか


複数のuniformをまとめる際、どのような型の配列として渡すか決めなければなりません。行列だけのバッファならmat4の配列、色やベクトルだけならvec4配列にします。

mat4とvec4の混合のバッファを作りたい場合もあるかもしれません。そういうときはvec4の配列とするとよさそうです。mat4配列でもよいのですがバッファサイズがfloat16個単位になってしまうので柔軟性に欠けるからです。次のようにしてGLSL側でvec4を4つまとめてmat4を復元します。


ならば、vec4配列よりfloat配列のほうが柔軟そうですが、float4つを1単位とする癖をつけたほうが良いかもしれません。以下のポストはDirectX11のもので、NVIDIAによるものなので他のGPU全てに当てはまるかはわかりませんが、float4つ単位にしなかった場合の速度低下の可能性を示唆しています。
https://developer.nvidia.com/content/understanding-structured-buffer-performance

UBOとuniform vec4配列の違い


上のように、ES3.0のUBOのようなものをES 2.0で実現してみましたが、同一ではありません。UBOはシェーダから独立したバッファで、複数シェーダで共有可能です。uniformはシェーダの一部であってC側から書き換え可能な変数です。あるuniform vec4配列を複数のシェーダ間で共有したい場合、シェーダを切り替える度にglUniform4fvでバッファを転送してあげなければなりません。

OpenGL ES特有のprecisionの問題


ES特有のつまづきそうなポイントです。uniformはシェーダ間で共有できませんが、同じprogramに属するvertex shaderとfragment shaderに限って同じ名前のuniform変数を宣言すると共有されます。しかし、precisionが違うと共有できません(glLinkProgramでエラーになります)。vertex shaderではhighp、fragment shaderではmediump、のような使い方をすることが多いと思うので注意します。(fragment shaderでhighpがエラーになる機種もある)

Monday, April 27, 2015

언리얼엔진 4, 소스코드로부터 APK 생성 후기

무료화된 언리얼 엔진4로 APK를 만들어 보았습니다. GitHub로 제공되는데 PRIVATE리포지토리라서 언리얼 엔진 공식 사이트에서 회원등록해야 리포지토리에 접근이 됩니다.

소스코드 빌드와 에디터 실행


빌드 방법은 GitHub의 Readme.md에 다 나와 있어서, 지시대로 VisualStudio 2013로 빌드하고 실행하면 에디터가 뜹니다.


샘플 실행중의 모습.


안드로이드 바이너리 만들기


공식  https://docs.unrealengine.com/latest/KOR/Platforms/Android/GettingStarted/index.html

안드로이드 APK로 패키지합니다. ETC1텍스쳐를 지정하면 단말기를 가리지 않고 실행 가능할 겁니다...



missing UE4Game binary이란 에러가 떴습니다. 사전에 Visual Studio로 안드로이드 바이너리를 빌드하지 않으면 이런 에러가 뜹니다.
(참고: 옛날 버전 얘기입니다. 세상이 좋아져서 최근 버전은 에디터로부터 바이너리 생성까지 다 해줍니다! 따라서 아래 Visual Studio작업도 필요 없습니다.)



UE4.sln에서 Solution Configurations에 "Development", Solution Platforms에 "Android"를 지정해서 빌드하면 안드로이드 바이너리가 생성됩니다. 다시 에디터에서 패키지해 보니 APK가 생성되었습니다. 단말기에 APK를 전송해 주는 배치파일까지 만들어 주는 센스!



APK가 25MB, Obb파일이 1GB나 되네요. 깔아 보면 단말기에서는 100MB정도가 됩니다.

결론


언리얼엔진은 자료가 아주 잘 정리되어 있어서 APK까지 쉽게 만들 수가 있었습니다. 단점은 엔진 자체 크기 때문에 빌드 시간이 아주 깁니다. 소스코드 빌드 시간이 걸리는 것은 어쩔 수 없는데, 간단한 APK 생성이 30분 정도 소용이 됩니다. 언리얼 개발을 위해서는 하이엔드PC부터 맞추어야 될 것 같습니다.

그리고 어떤 단말기에서 실행이 안 되었습니다. Acer Iconia B1-730HD에서는 돌아갔는데, 팬택Vega Racer에서 안 돌아갔습니다. 하지만 TAPPY CHICKEN는 Vega Racer에서 실행되었으니 조정을 잘하면 될 수도 있겠네요...

Tuesday, April 7, 2015

OpenGL ESでのUniform Bufferのバインドの仕方(DX11比較付き)

Unifrom BufferはDirectX11のConstant Bufferとほぼ同じものです。OpenGL ES 3.1では使い方もよく似ていて、以下のようにGLSLの"binding"で指定するbinding pointがほぼHLSLの"register"と同じ概念です。



C側からバッファを"register"に指定するID3D11DeviceContext::VSSetConstantBuffers に相当するのは、glBindBufferBaseです。



OpenGL ES 3.1はここまでで動作します。

GLSLで"binding"が使えないOpenGL ES 3.0では以下のそれ相当の処理をC側から行う必要があります。これはOpenGL ES 3.1でも有効で、"binding"で指定するのと同じ結果になります。更には、既に"binding"指定があってもC側から上書きすることもできます。

Saturday, March 28, 2015

Androidで動くモダンなインスタンシングを考える

このNVIDIAのスライドは基本的にPC対象です。しかし、昨今のゲーム業界はモバイル無しでは語れません。OpenGL ESで対応可能か機能を吟味してみます。

一発のDrawCallでたくさんのオブジェクトを表示するには以下の様な機能が必要です。

  1. インスタンシングのためのAPI
  2. シェーダーからバッファにランダムアクセスする方法
  3. シェーダーから複数のテクスチャにランダムアクセスする方法

サンプルプログラム程度の簡素なインスタンシングならglDrawElementsInstancedのようなAPIと、glVertexAttribDivisorでper instanceデータとして個別の座標等を渡せば済むので、2,3は無くてもいいかもしれません。しかし、マテリアルやボーンを変えてインスタンシングしたいなど、実用性を考えだすと2や3が必要となってきます。

まずは1のAPIですが、glMultiDrawElementsIndirectを使うと形の違うメッシュを一回のDrawCallで同時に描画できそうでした。しかし、残念ながらOpenGL ESでは使えなさそうです。glDrawElementsInstancedという、DirectX9時代からあった伝統的なインスタンシングで我慢します。

2と3は、マテリアルやボーンなどが異なるオブジェクトを一回のDrawCallで描くためには描画と描画の間にテクスチャの切り替えやバッファの更新を入れることができないため、描画対象の複数のオブジェクトが必要なデータを全部VRAMに上げておいて、シェーダーからランダムアクセスする必要があるということです。特にボーンが入っていて個別にアニメーションするとなると大きなバッファが必要です。

2. は嬉しいことに、OpenGL ES 3.1 ではSSBOという、ランダムアクセス可能なサイズの大きいバッファに対応しています。(資料https://www.khronos.org/assets/uploads/developers/library/2014-gdc/Khronos-OpenGL-ES-GDC-Mar14.pdf)しかし、Lollipopより前、KitKat以前のOpenGL ES 3.0の場合、uniformバッファを使う必要がありそうです。当然uniformの最大サイズに制限があるので、それより大きなバッファにランダムアクセスしたい場合はテクスチャにデータを格納してvertex texture fetchするなど更なる工夫が要求されます。

3.はbindless texture(ARB_bindless_texture)に対応していると理想的です。シェーダーからどのテクスチャにアクセスするかに関してほぼ制限がありません。しかし、OpenGL ESでは使えません。しかも、PCであっても比較的最近のものしか対応していないようです。

スライドではテクスチャをタイル化して大きなテクスチャを構成するARB_sparse_textureにも言及されていますが、同様にESでは使えません。

OpenGL ES 3.0 であればTexture Arraysが使えます。ただし、テクスチャのサイズやフォーマットを一致させる必要があるなど、アーティストに制限を課してしまうかもしれません。

古典的な手法ではTexture Atlasesや、あるいはインスタンシング対象はテクスチャを変えない、インスタンシングはしない、なども視野に入れて悩む必要がありそうです。


まとめ


PCなら以下のセットで異種混合インスタンシング
  1. glMultiDrawElementsIndirect(ARB_shader_draw_parameters)
  2. SSBO(ARB_shader_storage_buffer_object)
  3. bindless texture(ARB_bindless_texture)

Lollipop(OpenGL ES 3.1)なら以下のセットでインスタンシング
  1. glDrawElementsInstanced
  2. SSBO(ARB_shader_storage_buffer_object)
  3. Texture Arrays

OpenGL ES 3.0なら以下のセットでインスタンシング
  1. glDrawElementsInstanced
  2. uniform か vertex texture fetch
  3. Texture Arrays

OpenGL ES 2.0はインスタンシングのAPIが無いのであきらめる。

Sunday, March 22, 2015

OpenGLで異種混合インスタンシングを考える

レンダリングの高速化のためにはGPUとCPUの平行動作が重要で、一発のDrawCallで大量のモデルを描画するためにOpenGLの拡張が続けられています。2014年にはNVIDIA、AMD、INTEL各社がインスタンシングやbindless texture等の技術講義を行っていたようですね。

オーバーヘッドを減らすといえば、先日GDC2015で発表されたVulkanや、DX12の登場が待ち遠しいところですが、現状で使える最強のDrawCallはglMultiDrawElementsIndirectかと思われます。

伝統的なインスタンシングは同じモデルを大量に描画できるのに対し、glMultiDrawElementsIndirectは異なるモデルを一回のDrawCallに含められる点が頭一つ抜きん出ています。

GDC2014のスライド、
http://www.slideshare.net/CassEveritt/approaching-zero-driver-overhead
が網羅的に解説しています。

また、NVIDIAのスライド
http://www.slideshare.net/CassEveritt/beyond-porting?related=1
はモダンな描画方法の何がいいのかわかりやすく解説しています。NVIDIAのサンプルプログラムMultiDrawIndirectは異種混合インスタンシングを実現したサンプルです。

原理は簡単で、同一の頂点バッファに複数のモデルを含めておくだけです。頂点バッファのどの部分を描画に使うかは、DrawElementsIndirectCommand構造体の変数が指し示します。glMultiDrawElementsIndirectにはDrawElementsIndirectCommandを配列で渡せるので異種混合インスタンシングができます。ただし、同じ頂点フォーマット、同じシェーダー、同じレンダーステートどうしでないと一緒に描画できません。

また、残念なのはAndroidでは現状で最も機能豊富なAndroid Extension Packに対応していてもglMultiDrawElementsIndirectは使えなさそうなことです。

AndroidではglDrawElementsIndirectがES 3.1から、glDrawElementsInstancedがES 3.0から使えます。これらは基本的に一回のDrawCallで同じモデルを複数描画することができます。OpenGL ESでどうしても異種混合インスタンシングをしたいなら、ボーンを操作したりモーフィングしたりで形を変えながら別のモデルに見せることは出来るかもしれません。しかし、基本的にインスタンスごとにインデックスバッファを変えたり頂点数を変えたりできないです。

現状では異種混合インスタンシングはPC、あるいはフルスペックのOpenGLをサポートしたSHIELD tablet等でしか使えなさそうです。