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 テクスチャを読み込む


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 と浮動小数演算最適化