Archives

July 2011 の記事

OpenGL と DirectX は色の並び順が異なります。

       GL Type   OpenGL          DirectX9
------------------------------------------------------------
8888   BYTE      R8G8B8A8        B8G8R8A8 (A8R8G8B8)
888    BYTE      R8G8B8          B8G8R8   (R8G8B8)
565    SHORT     R5G6B5          R5G6B5
5551   SHORT     R5G5B5A1        A1R5G5B5
4444   SHORT     R4G4B4A4        A4R4G4B4
HALF   HALF      R16G16B16A16F   R16G16B16A16F (A16B16G16R16F)
FLOAT  FLOAT     R32G32B32A32F   R32G32B32A32F (A32B32G32R32F)


      OpenGL(LE)           OpenGL(BE)           DirectX9(LE)
-----------------------------------------------------------------
      1byte目  2byte目     1byte目  2byte目     1byte目  2byte目
      H______L H______L    H______L H______L    H______L H______L
565   gggbbbbb rrrrrggg    rrrrrggg gggbbbbb    gggbbbbb rrrrrggg
5551  ggbbbbba rrrrrggg    rrrrrggg ggbbbbba    gggbbbbb arrrrrgg
4444  bbbbaaaa rrrrgggg    rrrrgggg bbbbaaaa    ggggbbbb aaaarrrr

Alpha 位置の違いなので 565 は OpenGL/DirectX どちらも同じ。


●OpenGL の場合

OpenGL はメモリのアクセス単位が TYPE で決まっています。
例えば GL_UNSIGNED_BYTE の場合バイトオーダーに関係なくメモリの並びは
一定となります。

・OpenGL はメモリ上の配列は R G B A の順。
・short 等パックされた場合は上位 bit から R G B A。アルファが最下位。

メモリの並びとパックされた値は Big endian (BE) の場合に一致します。
R G B A の順番は X Y Z W の対応と等しく、float (fp16/32) 等のフォーマット
時の並びと一致します。


●DirectX の場合

アクセス単位という概念が特に無く 32bit/16bit 共に違いを意識しません。

・int/short 等パックされた場合、最上位がアルファ。上位から A R G B の順。
・メモリ上の配列は 32/24bit を LE で格納するため B G R A の順になる。

Little endian (LE) で格納した場合 24bit color と 32bit color の構造に
互換性があります。(B G R X → B G R A)
GL と D3D の違いはフォーマット作成時の GE と LE の違いかもしれません。

ARGB の順番 (D3DCOLOR) は XYZW と一致しません。
常に XYZW 順である float (fp16/32) フォーマットとも異なっています。
Direct3D10 では RGBA 並びをもとに大きく変更されました。
Direct3D9 以前と Direct3D10 (DXGI) 以降では FORMAT シンボルの表記順も
逆になっています。

Direct3D9              ABGR順    D3DFMT_A16B16G16R16F
Direct3D10/11(DXGI)    RGBA順    DXGI_FORMAT_R16G16B16A16_FLOAT


●相互変換

OpenGL で dds を用いたり、モバイルの OpenGL ES と PC の DirectX で
エンジンを共通化する場合はこれらの画素変換が必要になります。

具体例
OpenGLES2.0 DDS テクスチャを読み込む

モバイル GPU ではバス帯域がボトルネックになりがちです。
出来る限り圧縮テクスチャを用いますが、未対応 GPU の場合は
16bit テクスチャを活用することになります。


関連エントリ
Android OpenGL ES 2.0 の圧縮テクスチャ
OpenGLES2.0 DDS テクスチャを読み込む


起動すると名前とレベルと HP を表示します。
自分にクリップで取り付けて状態をさりげなくアピールしてください。

hpmeter01.jpg

[←]      HP を 10% 減らします
[→]      HP を 10% 増やします
[SELECT]  ダメージを受けます。ランダムに HP が減ります。

名前とレベルは設定で変更できます。

「LiveView アプリケーション」→ タイルをカスタマイズ → プラグインの管理 → hpmeter

HPメーター (hpmeter) hpmeter.apk
HPメーター (hpmeter) ソースコード LiveView_hpmeter_v100.zip



関連エントリ

Android LiveView MN800 プラグインの作り方
LiveView MN800 Android のマイクロディスプレイ


プラグインを作ってみました。

liveview_watch2.jpg

3地域同時表示の手抜き世界時計です。

世界時計 watch2.apk

前回 書いたように LiveView MN800 のアプリケーションは親機の Android で
動いています。画面出力とボタン入力は Bluetooth を介して行われます。

プラグインは大きく分けて 2種類あります。

 (1) 通知 プラグイン
 (2) Sandbox プラグイン

(1) メールのように着信をテキストで通知する場合は指定の API でテキストを
送るだけです。標準的なユーザーインターフェースが予め用意されています。

(2) Sandbox プラグインは描画やボタン操作を全て自前で処理します。
使える描画手段は bitmap の転送だけです。
イメージデータを用意すれば、API を呼ぶだけで Bluetooth 経由で LiveView の
画面に送られます。


● Android SDK

Plug-in は Android アプリそのままなので、Android の開発環境が必要です。

SDK install 手順

LiveView Plug-in の場合、上記手順のうち Cygwin と NDK は不要となります。


● LiveView SDK

Create Android plug-ins for LiveView micro display

上記の場所から LiveView の SDK とドキュメントをダウンロードすることが
できます。


● LiveView SDK のサンプルを動かす

ダウンロードした LiveView_SDK_v1.01.zip を適当なフォルダに展開しておきます。

1. Eclipse を起動し File → Import
2. General を開いて「Existing Projects into Workspace」を選択 → Next
3. Select root directory の右の [Browse...] をクリック
4. 展開しておいた LiveView_SDK 内の sandboxPlugin を選択して OK
5. sandboxPlugin にチェックが自動で入るので Finish

Package Explorer で sandboxPlugin を選択してメニューの Run → Run 。
実機 (親機) が adb 接続してあるならこれで sandboxPlugin がインストール
されます。

「LiveView アプリケーション」を起動し タイルをカスタマイズ→プラグインの管理
を開き、プラグインが読み込まれていることを確認します。
設定で Enabled にチェックを入れたら LiveView 本体側で確認します。


● LiveView の API

SDK 付属のサンプルやテンプレートにはソースコードも多数含まれています。
一見難しそうに見えますが、プラグインのプログラムは
SandboxPluginService.java だけです。

API そのものは下記のファイルで定義されています。

IPluginServiceV1.aidl
IPluginServiceCallbackV1.aidl

中を見ると API 自体は非常にシンプルで数も少ないことがわかります。

その他の java コードはプラグインの雛形として使えるライブラリです。
必ずしも必要ではありませんがよく使う使われる処理がまとまっています。

基本的にはサンプルか Template をコピーし、必要な部分を置き換えていく
形になるかと思います。


●世界時計を作ってみた

サンプルの sandboxPlugin 自体にすでに簡単な時計機能が含まれています。
これを参考にして作ってみました。
ただし出来る限り機能を単純化(手抜き化)します。

(A) 表示は分単位にします
 sandboxPlugin は 1秒単位で画像を更新していますが、遅くなるし
 バッテリーを消耗しそうなのでやめておきます。

(B) 自動更新しません
 LiveView は操作しなければ 20秒くらいですぐ画面が消えます。
 1分未満ですぐ画面が消えるだろうとの予測の元、リアルタイムに表示を
 更新しないことにします。

時計として使う場合どうせボタンを押さなければ画面が表示されないので、
割りきってしまうことで画面の送信タイミングが下の 2つだけになります。

  ・プラグイン起動時
  ・画面が ON になった瞬間

自動更新しないので Timer 処理も不要となり、終了手順は何もしなくて
よくなりました。

もちろん表示時刻は厳密には正確ではありません。
1分程度の誤差が出るため、表示時刻の方を 30秒進めておきます。

世界時計 (watch2) ソースコード LiveView_watch2_v100.zip

セレクトボタンを押した場合も表示を更新するようになっています。
押すたびに下部の 3連アナログがランダムなサイコロに切り替わります。


●プログラムの説明

プラグインは WatchService.java です。

画面を更新したいタイミングで upgradeTime() を呼びだしています。
Handler に渡していますが、描画する内容自体は sendImage() で作られます。

128x128 dot の画像を生成し、その中に表示すべき情報を構築しています。
最後の mLiveViewAdapter.sendImageAsBitmap() でデータの送出です。

画面の更新速度は Bluetooth の通信状況に依存します。
プラグイン起動直後など、Bluetooth の通信が始まるまではしばらく待たされる
ことがあります。

watch2 の場合は特に画面 ON の通知が来てから表示の更新を行うので、
データが届くまでの間 LiveView の画面には古い時刻が表示され続けてしまいます。
時計としては致命的なので、画面 OFF のタイミングで「更新中」の文字を
転送しておきます。

 1. 表示が消える
 2. 画面 OFF のイベントが Plug-in に届く
 3. 「更新中」の文字を転送しておく (clearImage())
 4. 表示は消えているが LiveView の画面には「更新中」の文字がブレンドされる

 5. 電源ボタンを押すなどして画面が点灯する
 6. 4. の状態の画面が表示される。(つまり更新中マーク入り)
 7. 画面 ON のイベントが Plug-in に届く
 8. 現在時刻をもとに新しい画面を作って送信する
 9. LiveView の画面に最新の時刻が反映される


●startWork()/stopWork() とは何か

プラグインでは startWork()/stopWork() の定義が必須となっています。
これは AbstractPluginService から呼ばれるもので API には特にありません。

LiveView Plug-in の設定画面によく出てくる "Enabled" を実現するための
代物のようです。プリファレンスで Enabled のチェックを切り替える度に
この startWork()/stopWork() が呼ばれています。


●LiveView plug-in

プラグインの構造自体は画像を作って送信するだけなので、非常にわかりやすい
ものでした。Android のアプリそのままなので、いろいろ応用もできるし用途は
工夫次第だと思います。

ただやはりボタン反応は思うように動かないことがあり、操作性が悪く感じる
場合があります。一度動き出せばスムーズに反応するので、Bluetooth の
ネゴシエーションか省電力モードからの立ち上がりの遅延かもしれません。


関連エントリ

LiveView MN800 Android のマイクロディスプレイ


SonyEricsson の LiveView MN800 を使ってみました。
音楽プレイヤーや腕時計のような小型の Bluetooth 端末です。
任意のアプリを追加可能で、スマートフォン等の Android 端末と連動して
情報閲覧が可能となります。

LiveView MN800

何が出来るのかイメージしにくいのですが、基本的にワイヤレスで動く小型の
ディスプレイ&リモコンとなっているようです。

アプリ自体は Android 端末側(親機)で動作します。
画面の表示とボタン入力だけを Bluetooth 経由で通信する仕組みとなっている
ようです。

メールなどの着信通知だけでなく、親機側にアプリ(LiveView plug-in)を追加
すればさまざまな動作が可能となります。
自由度が高い反面、親機からある程度離れて通信出来ない状態になるとこれらの
アプリは機能しなくなります。


● Xperia 以外でも使える

必要なアプリを入れて Bluetooth でペアリングすれば他の Android 端末でも
動くようです。今のところ HTC Desire X06HT (Android 2.2) で使えています。


●親機側に必要なソフト

(1) LiveView アプリケーション
(2) LiveWare マネージャ

最低限必要なのは (1) です。
利用時はペアリングして接続し、通信可能な状態でかつ (1) が起動してる必要が
あります。(バックグラウンド可)

(2) は、LiveView が Bluetooth 接続された場合に (1) を立ち上げてくれる
サポートツールのようです。(2) がある方が取り扱いが楽になりました。


●接続の 3状態

1. Bluetooth のペアリング
親機となる Android 端末と LiveView を Bluetooth でペアリングする

2. Bluetooth の接続
親機と LiveView を接続する (ペアリングしていれば自動接続可能)

3. 通信状態
LiveView アプリケーションが起動して通信可能状態となる

LiveWare マネージャはおそらく 2. の状態を検出して 3. にする働きをします。
マネージャがなくても手動で LiveView アプリケーションを起動しても通信状態
にできます。


●操作は 5 way 方式

画面枠の上下左右がタッチボタンとなっています。
方向キーに相当し、右上の角のボタン (コントロールボタン) が決定キーです。

マニュアルでは左側が「電源ボタン」右側が「コントロールボタン」という名称に
なっています。

電源ボタン  コントロールボタン
   +----------------+
   |       ↑       |
   |                |
   |←    画面    →|
   |                |
   |       ↓       |
   +----------------+


                        短く押した場合        長押し
---------------------------------------------------------------
電源ボタン              画面 On/Off           電源 On/Off
コントロールボタン      決定                  戻る
方向ボタン              選択など

方向キーで選択して右上のコントロールボタンで決定、コントロールボタンの長押し
で戻るのが基本操作です。


●時計機能

通信出来ない状態になっても時計画面は表示できます。

時刻設定などはなく、また日時のバッテリーバックアップ自体が無いようです。
電源を完全に切ってしまうと忘れるので、電源投入後に親機から現在時刻を
受け取っています。

そのため時計として使う場合も電源を入れたら最低一回は親機との通信が必要です。
親機と同期してしまえば、以後接続が切れても時計自体は動いています。

時計画面でコントロールボタンを押すと機能メニューになります。


●再接続

接続が切れている時にコントロールボタンを押すと再接続を試みます。

親機側の接続状態と LiveView 側の接続状態が食い違っていると再接続に
失敗し続けることがあるようです。
このあたりはまだいろいろ試行錯誤している段階です。


●音楽プレイヤー

メニュー画面でコントロールボタンを長押しすると、音楽プレイヤーのリモコン
画面になります。
Android 内蔵の音楽プレイヤーの再生や音量調節が出来ます。
若干タイムラグがあるので、ボタンを押したあとワンテンポ待つイメージでうまく
いきます。


●プラグイン

プラグインで機能を追加することが出来ます。
直接 Android Market で検索してもいいし、LiveWare マネージャから探すことも
できます。

追加したプラグインは、LiveView アプリケーション」を開いて

「タイルをカスタマイズ」→「プラグインの管理」

で設定します。自分でプラグイン開発もできるようです。


●使い道

・画面が小さく表示内容が限られる
・常に親機と通信して動作するため操作性は若干重く感じる
・親機を一緒に持ち歩かなければならない
・バッテリーの持ちと接続安定性

いろいろ面白い使い方ができそうなのですが、やはり上の制限があります。
親機を持ち歩くなら直接 Android 端末を取り出したほうが早い・・
と言われたらそれまでなので、使う側にもちょっとした努力と工夫が必要です。

例えば LiveVew を腕時計のように身につけるなら端末をとり出さなくても
情報を見れるし、通知もすぐわかる。
(逆に身につけないと振動通知は気がつかない可能性が高い)
他にも Tablet を親機にして電車の中で使う、など。

100% いつでもあった方が便利、とは言えませんが、今までできなかった
ことのいくつかを埋めてくれることは確かです。

プラグインを入れたり作ったり、使い方を工夫するところも含めて
楽しんでいくデバイスだと思います。


ARMv7 の fpu、vfp には大きく分けて NEON 命令のありなしで 2種類あります。
細かい違いを含めると更に増えます。
gcc のオプションを見るとこんな感じ。

vfp
vfpv3
vfpv3-d16
vfpv3-fp16
vfpv3-d16-fp16
neon
neon-fp16
vfpv4
vfpv4-d16
neon-vfpv4

使える機能はハードウエアレジスタ MVFR0/MVFR1 を見ることで判断できる
ようです。分かる範囲でまとめてみました。

ARM vfp の種類

                     D32 VSP VDP DIV SQR SV  NLS NI  NSP NHP VHP FMA
--------------------------------------------------------------------
Cortex-A8 vfpv3+NEON  ◎  ◎  ◎  ◎  ◎  ◎  ◎  ◎  ◎  --  --  --
Cortex-A9 vfpv3-D16   --  ◎  ◎  ◎  ◎  ◎  --  --  --  --  ◎  --
Cortex-A9 vfpv3+NEON  ◎  ◎  ◎  ◎  ◎  --  ◎  ◎  ◎  ◎  ◎  --
Cortex-A5 vfpv4-D16   --  ◎  ◎  ◎  ◎  --  -   --  --  -   ◎  ◎
Cortex-A5 vfpv4+NEON  ◎  ◎  ◎  ◎  ◎  --  ◎  ◎  ◎  ◎  ◎  ◎
--------------------------------------------------------------------

D32 = double 32個
VSP = VFP 単精度演算
VDP = VFP 倍精度演算
DIV = HW 除算
SQR = HW 平方根
SV  = VFP Short Vector 対応
NLS = NEON Load/Store
NI  = NEON Int演算
NSP = NEON 単精度演算
NHP = NEON half(fp16) 演算
VHP = VFP half(fp16) 演算
FMA = IEEE 積和 Fused Multiply Add

Cortex-A9 以降は fp16 (16bit 浮動小数点命令) に対応しています。

vfpv3 と vfpv4 の違いは FMA (Fused Multiply Add) にあるようです。

また VFP の Vector Mode (SV) が Cortex-A9 の NEON 版から無くなっている
こともわかります。この場合 FPSCR の Len が 0 以外の場合例外が発生し、
ソフトウエアでエミュレーションすることになるようです。

Cortex-A9 で fp16 命令を試してみました。
下記の命令で 16bit, 32bit float の相互変換ができます。

vcvtb.f16.f32  sd, ss
vcvtt.f16.f32  sd, ss
vcvtb.f32.f16  sd, ss
vcvtt.f32.f16  sd, ss

Android NDK ではオプション指定してもコンパイルできませんでしたが、
iOS ではコンパイルが通りました。

unsigned int SingleToHalf( float a, float b )
{
    float    fparam[4];
    fparam[0]= a;
    fparam[1]= b;
    __asm__ __volatile__(
        "vldmia %0, {d0-d1}\n"
        "vcvtb.f16.f32  s2, s0\n"
        "vcvtt.f16.f32  s2, s1\n"
        "vstmia %0, {d0-d1}\n"
         : "=&r"( fparam )
         : "0"( fparam )
         : "d0","d1","cc" );
    unsigned int*   iparam= (unsigned int*)fparam;
    return  iparam[2];
}

void HalfToSingle( unsigned int param_in )
{
    unsigned int    iparam[4];
    iparam[0]= param_in;
    __asm__ __volatile__(
        "vldmia %0, {d0-d1}\n"
        "vcvtb.f32.f16  s1, s0\n"
        "vcvtt.f32.f16  s2, s0\n"
        "vstmia %0, {d0-d1}\n"
         : "=&r"( iparam )
         : "0"( iparam )
         : "d0","d1","cc" );
    float*   fparam= (float*)iparam;
    printf( "%f %f\n", fparam[0], fparam[1] );
}

iPad2 (Cortex-A9) で動作し、相互変換できました。
fp16 未対応の iPod touch 3 (Cortex-A8) ではエラーで停止します。

Mobile GPU の比較

PowerVR SGX, Adreno, Tegra2 など 16bit (half) float 対応の GPU は
意外に多いので、頂点フォーマットの変換に使いたい機能です。


関連エントリ
Snapdragon の本当の浮動小数点演算能力
iPad2 A5 と浮動小数演算 VFP/NEON
Tegra2 Cortex-A9 と浮動小数演算
Snapdragon と浮動小数演算速度
ARM Cortex-A8 の NEON と浮動小数演算最適化


Android NDK r6 がリリースされています。

Download the Android NDK

x86 に対応しており、NDK で ARM 以外のハードウエアサポートが
始まりました。
従来の armeabi, armeabi-v7a と同じように x86 を指定することで
3種類のアーキテクチャに対応した fat binary を作ることができます。
命令セットは SSE3 まで。


関連エントリ

Android NDK と NativeActivity