OpenGL 3.2 の GeometryShader

OpenGL 3.2 では core 機能に Geometry Shader が含まれています。
試してみました。

OpenGL Registry

GeometryShader はポリゴンの面単位で実行可能なシェーダープログラムです。
面単位なので、プリミティブタイプが Triangle List (GL_TRIANGLES) なら一度に
3頂点を入力として受け取ります。主な用途は下記の通りです。

・頂点演算
・面単位のマテリアル演算
・形状の操作
・出力先の変更
・StreamOutput (Transform Feedback)

わかりやすいところでは面法線の生成など面単位のマテリアル設定が考えられます。
ユニークなのは入力と関係なく出力プリミティブを組み立てられることです。
出力頂点数は任意なので、エッジだけ Line として書き出したり、面を分割したり、
面を消すことも出来ます。

例えば入力を PointList (GL_POINTS) で 1頂点だけ受け取り、Triangle で
2 プリミティブ出力すればポイントスプライトの代わりになるわけです。

出力先の変更は Direct3D だと RenderTarget Array, OpenGL では
Layered Rendering と呼ばれているようです。
Geometry Shader で書き込むフレームバッファの選択が可能で、Cube Map への
レンダリングも一回で出来る仕様になっています。

入力可能なプリミティブタイプも Direct3D 10 同様に増えています。
下記は増えた分のみ。使えるのは GeometryShader が有効な場合だけに限られます。

GL_LINES_ADJACENCY
GL_LINE_STRIP_ADJACENCY
GL_TRIANGLES_ADJACENCY
GL_TRIANGLE_STRIP_ADJACENCY

あまり馴染みがない名称ですが、極端な言い方をすれば 4,6 頂点のプリミティブが
使えるようになったということです。
GL_TRIANGLES だと GeometryShader は 3 頂点単位で受け取りますが、
GL_TRIANGLES_ADJACENCY では一度に 6頂点受け取れることになります。
GL_LINES_ADJACENCY の 4頂点は、QUAD プリミティブの代わりとしても使えます。

ちなみに Direct3D 11 の ShaderModel 5.0 はさらに増えていて、テセレータ用に
32頂点まで任意の数の頂点を入力できるようになっています。

今回使用したビデオカードは GeForce GTX280。ステートを調べると下記の通りです。
頂点の生成が最大 1024 float なのも Direct3D 10 の ShaderModel 4.0 と同じです。

GL_MAX_GEOMETRY_OUTPUT_VERTICES = 1024
GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS = 32
GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS = 1024

● GeometryShader のコンパイル

Shader の読み込みとコンパイル手順はこれまでの VertexShader, FragmentShader
と同じです。もともとシェーダーしか使えない OpenGL ES 2.0 を使っていたため、
ちょうど GeometryShader の部分が追加された形になります。

GLuint vshader= glCreateShader( GL_VERTEX_SHADER );
glShaderSource( vshader, 1, vshader_source_text, NULL );
glCompileShader( vshader );

GLuint gshader= glCreateShader( GL_GEOMETRY_SHADER );
glShaderSource( gshader, 1, gshader_source_text, NULL );
glCompileShader( gshader );

GLuint pshader= glCreateShader( GL_FRAGMENT_SHADER );
glShaderSource( pshader, 1, pshader_source_text, NULL );
glCompileShader( pshader );

エラー判定は省いてあります。

次はコンパイルしたシェーダーをリンクして Program の作成です。
これまで VertexShader, FragmentShader だけ attach していたところに
GeometryShader が加わります。

GLuint shaderProgram= glCreateProgram();
glAttachShader( shaderProgram, vshader );
glAttachShader( shaderProgram, gshader );
glAttachShader( shaderProgram, pshader );
glLinkProgram( shaderProgram );

●ドライバの補足

GLSL の GeometryShader の書き方がわからなかったため、ドキュメント
OpenGL Shadeing Language 1.50.09 と Direct3D の知識を頼りに進めました。

最初はコンパイルすらうまく通らず、EmitVertex を用いると
「#extension GL_EXT_geometry_shader4 : enable」
を宣言しなさいとエラーが出てしまいます。

「#version 150」で GLSL 1.5 を指定すると未対応のバージョンだといわれます。
140 なら通ります。ドライバを調べてみると、最新版 190.62 は OpenGL 3.1 しか
対応していませんでした。
ドライバが OpenGL 3.2 / GLSL 1.5 に対応していなかったことが原因です。

NVIDIA OpenGL 3.0/3.1/3.2 Support for Windows, Linux, FreeBSD, and Solaris

上のページにある、OpenGL 3.2 対応ドライバ 190.57 を入れたら、#version 150 指定
による GLSL 1.5 も、GeometryShader 専用命令も通るようになりました。
「#version 150」とだけ書いておけばよく、extension 指定は不要です。

● GLSL v1.50

「#version 150」を指定したら VertexShader でもエラーが出るようになりました。
attribute, varying 宣言は古いので、in, out を使えと言われます。

もともと attribute は頂点からの入力、varying はラスタライザの補間パラメータを
意味しています。出力先が GeometryShader かもしれないし、シェーダーパイプラインが
多層化&汎用化されたので名称が変わったものと思われます。

OpenGL ES 2.0 との互換性も残したいのでとりあえず define でごまかしました。
attribute を in に、varying を out に置き換えます。

// sysdef2.vsh (Vertex Shader)

#define attribute   in
#define varying     out

#version 150

uniform vec4	World[3];
uniform mat4	PView;

attribute vec3	POSITION;
attribute vec3	NORMAL;
attribute vec2	TEXCOORD;

varying vec3	onormal;
varying vec2	otexcoord;

void main()
{
    gl_Position= vec4( POSITION.xyz, 1 ) * PView;
    vec3    nvec;
    nvec.x= dot( NORMAL.xyz, World[0].xyz );
    nvec.y= dot( NORMAL.xyz, World[1].xyz );
    nvec.z= dot( NORMAL.xyz, World[2].xyz );
    onormal= nvec;
    otexcoord= TEXCOORD;
}

Matrix 周りが少々不自然なのは、OpenGL ES 2.0 に mat4x3 が無かったことと頂点
アニメーションを削ったためです。もともと Skinning の処理が入っていました。

同じように Fragment Shader も書き換えます。

// sysdef2.fsh (Fragment Shader)

#define varying     in

#version 150

varying	vec3    onormal;
varying	vec2    otexcoord;
uniform sampler2D   ColorMap;

void main()
{
    vec3  lightdir= vec3( 0.0, 0.0, -1.0 );
    float lv= clamp( dot( normalize( onormal ), lightdir ), 0.0, 1.0 ) + 0.5;
    vec4  color= vec4( 1.0, 1.0, 1.0, 1.0 ) * lv;
    vec4  tcol= texture2D( ColorMap, otexcoord );
    gl_FragColor= color * tcol;
}

FragmentShader の場合 VertexShader と逆で varying が in になります。

GeometryShader は最初から in/out で記述します。

// sysdef2.gsh (Geometry Shader)

#version 150

layout(triangles) in;
layout(triangle_strip, max_vertices= 3) out;

in Inputs {
    vec3    onormal;
    vec2    otexcoord;
} gin[3];

out vec3    onormal;
out vec2    otexcoord;

void main()
{
    gl_Position= gl_in[0].gl_Position;
    onormal= gin[0].onormal;
    otexcoord= gin[0].otexcoord;
    EmitVertex();

    gl_Position= gl_in[1].gl_Position;
    onormal= gin[1].onormal;	// (1)
    otexcoord= gin[1].otexcoord;
    EmitVertex();

    gl_Position= gl_in[2].gl_Position;
    onormal= gin[2].onormal;	// (1)
    otexcoord= gin[2].otexcoord;
    EmitVertex();

    EndPrimitive();
}

プリミティブのタイプは layout() で宣言しています。
入力は block 宣言を使っており、Triangle なので 3頂点分のデータを受け取ります。
VertexShader が書き込んだシステム変数 gl_Position の値は、こちらも builtin
の gl_in[] という配列で受け取れます。

出力はこれまで通り out (varying) 宣言した変数に書き込みます。
必要なパラメータを書き込んだ後に EmitVertex() 命令で頂点が確定します。
プリミティブの区切りは EndPrimitive() 命令です。

このジオメトリシェーダーは何もせずに、頂点の出力をそのままスルーしています。
上の (1) の行を削除すると面に同じ法線が適用されるため、フラットシェーディング
風の見た目になるはずです。

同じシェーダーを DirectX の HLSL で書くとこんな感じです。

// HLSL Geometry Shader

struct GS_INPUT {
    float4   Position  : POSITION;
    float3   Normal    : NORMAL;
    float2   Texcoord  : TEXCOORD;
};

struct GS_OUTPUT {
    float4   Position  : SV_POSITION;
    float3   Normal    : NORMAL;
    float2   Texcoord  : TEXCOORD;
};

[maxvertexcount(3)]
void GS_Main( triangle GS_INPUT In[3], inout TriangleStream gsstream )
{
    GS_OUTPUT   Out;

    Out.Position= In[0].Position;
    Out.Texcoord= In[0].Texcoord;
    Out.Normal  = In[0].Normal;
    gsstream.Append( Out );

    Out.Position= In[1].Position;
    Out.Texcoord= In[1].Texcoord;
    Out.Normal  = In[1].Normal;
    gsstream.Append( Out );

    Out.Position= In[2].Position;
    Out.Texcoord= In[2].Texcoord;
    Out.Normal  = In[2].Normal;
    gsstream.Append( Out );

    gsstream.RestartStrip();
}

書式が違うだけでほとんど同じです。
Direct3D でも、アセンブラ出力すると頂点の生成は emit 命令なのです。

関連エントリ
OpenGL のはじめ方
OpenGL ES 2.0 Emulator
OpenGL ES 2.0
OpenGLES2.0 DDS テクスチャを読み込む
OpenGLES2.0 Direct3D とのフォーマット変換
OpenGLES 2.0 頂点フォーマットの管理
OpenGLES2.0 の頂点
OpenGLES2.0 D3D座標系
OpenGLES2.0 シェーダー管理

OpenGL のはじめ方

Windows 上で DirectX ばかり使ってきたので、いざ OpenGL に手をつけてみようと思っても
何を使えばいいのか良くわかりませんでした。DirectX10/11 はすでにシェーダーのみとなって
いるため、OpenGL 入門によく載っているようなレガシーな命令もちょっと違います。
WindowsSDK に付属している gl.h ヘッダは OpenGL v1.1 ですが、すでに OpenGL は
3.2 まで進化しているようです。

Windows の場合、OS との間を取り持つ wgl~ という API 群があります。
wgl と gl は、Direct3D でいえば DXGI と D3D の関係に似ています。

MSDN Win32 Extensions to OpenGL

以前 Maya plug-in を作ったときも GL_ARB_vertex_program など OpenGL の
Extension 関数を呼び出すために wglGetProcAddress() を使いました。
Core API の場合も全く同じ方法でした。

(1) 最新のヘッダファイルを入手する
(2) wgl 命令で描画の初期化
(3) ヘッダの定義を元に wglGetProcAddress() でアドレスを取り出す

OpenGL Registry

上のページに gl3.h や glext.h, wglext.h 等が登録されています。
関数インターフェースの定義はこの中で行われており、必要ならプロトタイプ宣言もしてくれます。
wglGetProcAddress() を用いて関数名から実際のエントリアドレスを取得すれば
API を呼び出せるようになります。

たとえば gl3.h の中では、OpenGL 2.0 の命令として glCreateShader() が定義されています。

#define APIENTRY
#define APIENTRYP APIENTRY *
#define GLAPI extern

GLAPI GLuint APIENTRY glCreateShader (GLenum);
~
typedef GLuint (APIENTRYP PFNGLCREATESHADERPROC) (GLenum type);

上がプロトタイプ宣言、下が関数ポインタの型宣言です。APIENTRYP はただの ‘*’。
今回はエントリアドレスを直接取り出すため、下の関数ポインタ型の宣言しか使いません。
プロトタイプ宣言はデフォルトで無効になっています。

この glCreateShader() を呼び出すには下記のようにすればよいわけです。

// 取り出し
PFNGLCREATESHADERPROC glCreateShader=
        (PFNGLCREATESHADERPROC)wglGetProcAddress( "glCreateShader" );

// 呼び出し
glCreateShader( shader );

もちろん使えるのはディスプレイドライバが対応している場合だけです。
ドライバが対応していなければ wglGetProcAddress() はエラーを返します。

このように関数名からエントリアドレスを取得するやりかたは DLL と同じです。
DirectX の場合は COM なので、関数単独ではなく class 単位で API を取り出しますが
基本は同じです。

上の例のように、OpenGL 1.2~3.2 の膨大な関数を手作業で追加するのは酷です。
自動化します。

ヘッダのプロトタイプ宣言から、必要な関数名だけ抜き出すスクリプトを作っておきます。

import  re
import  sys
mp_api= re.compile( "^GLAPI\s+[a-zA-Z0-9_*]+\s+APIENTRY\s+(.*)\s+\(" )
def ProcList( filename ):
    f= open( filename, 'r' )
    for line in f:
        pat_api= mp_api.search( line )
        if pat_api != None:
            func= pat_api.group(1)
            print "GLDEFPROC( PFN"+ func.upper() + "PROC, " + func + " )"
    f.close()
ProcList( sys.argv[1] )

関数名を全部大文字にして PFN ~ PROC で囲めばインターフェースの型宣言になります。
上のスクリプトは gl3.h ヘッダから関数名だけ抜き出して下記のような一覧を生成します。

GLDEFPROC( PFNGLATTACHSHADERPROC, glAttachShader )
GLDEFPROC( PFNGLBINDATTRIBLOCATIONPROC, glBindAttribLocation )
GLDEFPROC( PFNGLCOMPILESHADERPROC, glCompileShader )
GLDEFPROC( PFNGLCREATEPROGRAMPROC, glCreateProgram )
GLDEFPROC( PFNGLCREATESHADERPROC, glCreateShader )
GLDEFPROC( PFNGLDELETEPROGRAMPROC, glDeleteProgram )
GLDEFPROC( PFNGLDELETESHADERPROC, glDeleteShader )
GLDEFPROC( PFNGLDETACHSHADERPROC, glDetachShader )
~

そのまま gl3.h を渡すと OpenGL 1.1 など余計な関数まで再定義してしまうので、
必要な 1.2~3.2 部分だけ切り出してから与えることにします。
Core API だけでなく拡張命令も同じように追加できるでしょう。

出力ファイル名を GLExtensionList.inc としておきます。
これを C ソースで include し、GLDEFPROC() を用途に応じて再定義してして使い分けます。

ヘッダ側

// GLFunction.h
#include  
#include  
#include  

#define GLDEFPROC(type,name)     extern type name;
# include  "GLExtensionList.inc"
#undef  GLDEFPROC

extern void InitOpenGL();

ソース側

// GLFunction.cpp

#define GLDEFPROC(type,name)      type name;
# include    "GLExtensionList.inc"
#undef  GLDEFPROC

struct T_ProcList {
    const char* Proc;
    void**      Addr;
};

static T_ProcList   ProcList[]= {

#define GLDEFPROC(type,name)      { #name, (void**)&name },
# include    "GLExtensionList.inc"
#undef  GLDEFPROC

    {   NULL,   NULL,   },
};

void InitOpenGL()
{
    const T_ProcList* tp= ProcList;
    for(; tp->Proc ; tp++ ){
        *tp->Addr= wglGetProcAddress( tp->Proc );
        if( !*tp->Addr ){
            int errcode= GetLastError();
            ERROR_UTF8( "'%s' not found %d/%x\n", tp->Proc, errcode, errcode );
            //*tp->Addr= _UnknownAPI_Func;
        }
    }
}

これで GLFunction.h を include すれば、OpenGL 3.2 までの命令をそのまま
使えるようになります。追加 lib は WindowsSDK の OpenGL32.lib です。
使用可能なのはドライバが対応している場合だけ。
また起動時は最初に一度 InitOpenGL() を呼び出しておく必要があります。

OpenGL ES 2.0 用に作成した描画コードが、一カ所直しただけでそのままコンパイルできました。
GLSL のシェーダーコードも共有しており、きちんど動作します。
ドライバと環境は GeForce 9800GT + 190.62 + Windows7 x64 。
モデルデータもテクスチャも GL ES 2.0 と全く同じ物です。
修正したのは下記の点

glClearDepthf( depth ); // GL ES のみ
    ↓
glClearDepth( depth );

OpenGL 3.2 に厳密に従うには、ピクセルフォーマットなどテクスチャの読み込み部も
手を入れる必要がありそうです。

関連エントリ
OpenGL ES 2.0 Emulator
OpenGL ES 2.0
OpenGLES2.0 DDS テクスチャを読み込む
OpenGLES2.0 Direct3D とのフォーマット変換
OpenGLES 2.0 頂点フォーマットの管理
OpenGLES2.0 の頂点
OpenGLES2.0 D3D座標系
OpenGLES2.0 シェーダー管理

OpenGL ES 2.0 Emulator

OpenGL ES 2.0 は必要な機能のみ用意されているため非常にわかりやすくなっています。
レガシーだけど互換性のために残っている API が、ほとんど無いからです。
定期的に仕様をゼロから作り直す DirectX に似ているといえるかもしれません。

複数の描画方法が残っていると、どれを使えばよいのかわからなくなることがあります。
OpenGLES 2.0 では悩むことが少なくて済みました。
モバイル向けサブセットであり仕様を割り切っていること、ShaderModel 3.0 相当の
シェーダーが利用可能なことから、OpenGL の入門としても参考になりました。

PC 上でも OpenGL ES 2.0 Emulator を使えば全く同じ API を使うことが出来ます。

AMD Developer Central GPU Tools
Imagination PowerVR Insider

この両者、初期化がわずかに異なるだけで dll 名も一緒。全く同じように使えるようです。
コードを共有化する場合、NativeWindowType でどちらを使っているか判断できます。

#include  
#include  
#include  

#ifdef	NativeWindowType
# define  USE_GLES20_AMD   1
#else
# define  USE_GLES20_PVR   1
# include 
#endif

初期化の違いもこれだけです。

#if USE_GLES20_AMD
static const EGLint attrib[]= {
    EGL_RED_SIZE,       5,
    EGL_GREEN_SIZE,     6,
    EGL_BLUE_SIZE,      5,
    EGL_ALPHA_SIZE,     0,
    EGL_DEPTH_SIZE,     16,
    EGL_STENCIL_SIZE,   0,
    EGL_SAMPLES,        4,
    EGL_NONE
};
egl_Display= eglGetDisplay( EGL_DEFAULT_DISPLAY );
#else // PVR
static const EGLint attrib[]= {
    EGL_LEVEL,                  0,
    EGL_SURFACE_TYPE,           EGL_WINDOW_BIT,
    EGL_RENDERABLE_TYPE,        EGL_OPENGL_ES2_BIT,
    EGL_NATIVE_RENDERABLE,      EGL_FALSE,
    EGL_DEPTH_SIZE,             EGL_DONT_CARE,
    EGL_NONE
};
hDC= GetDC( hWnd );
egl_Display= eglGetDisplay( hDC );
#endif

EGLint  majorv= 0;
EGLint  minorv= 0;
eglInitialize( egl_Display, &majorv, &minorv );

EGLint	configs= 0;
eglGetConfigs( egl_Display, NULL, 0, &configs );
eglChooseConfig( egl_Display, attrib, &egl_Config, 1, &configs );
egl_Surface= eglCreateWindowSurface( egl_Display, egl_Config, hWnd, NULL );
static const EGLint   attrlist[]= {
    EGL_CONTEXT_CLIENT_VERSION,
    2,
    EGL_NONE
};
egl_Context= eglCreateContext( egl_Display, egl_Config, EGL_NO_CONTEXT, attrlist );
eglMakeCurrent( egl_Display, egl_Surface, egl_Surface, egl_Context );

PC では本来の OpenGL が使えるので、3D を描画する目的でわざわざ Emulator を使う
必要はないです。元々 GL ES 自体がサブセットであり OpenGL でほぼ同じ API が使えます。
今回いろいろ試しているのも手持ちの描画エンジンを様々なプラットフォームで
そのまま動くようにしているから。

GLSL に mat4x3 が無いとかバイナリ形式の扱いとか多少機能不足もありますが
その辺は何とでもなります。Direct3D Mobile がなかなか更新されないので、
Mobile 向け API は GL ES 2.0 で統一してくれた方が良いですね。

Direct3D11 (DirectX11) に慣れすぎたせいで、ついリソース生成をサブスレッドで
実行してしまいます。リソースの生成は描画と同じメインスレッドで無いと失敗します。

関連エントリ
OpenGL ES 2.0
OpenGLES2.0 DDS テクスチャを読み込む
OpenGLES2.0 Direct3D とのフォーマット変換
OpenGLES 2.0 頂点フォーマットの管理
OpenGLES2.0 の頂点
OpenGLES2.0 D3D座標系
OpenGLES2.0 シェーダー管理

OpenGL ES 2.0

MID SmartQ5 が気になったので、3D 描画性能などを調べていたら下記のページに
いくつかヒントがあることに気がつきました。

Khronos Conformant Products

Khronos のトップより右上の View all Conformatn Products。
OpenGL ES 2.0 に対応しているグラフィックスコアがだいたいわかります。
2.0 に対応しているのはこのあたり。

・Imagination Technologies PowerVR SGX 535
・Samsung Electronics FIMG-3DSE v1.5
・ARM Mali-400 MP
・Imagination Technologies PowerVR SGX 520
・NVIDIA Corporation Tegra
・Vivante Corporation
・ARM Mali 200
・Imagination Technologies PowerVR SGX530
・Advanced Micro Devices AMD Z430?

iPhone が PowerVR SGX 535 であることもわかります。
下の方には Cell の記載も。JETSTREAM-A は Cell を使ったレンダラでしょうか。

PowerVR SGX は Unified Shader で GPGPU や ShaderModel4.0 (DirectX10)
も見据えた設計になってますが、Mali 400 等は Vertex/Pixel が独立した
ShaderModel 3.0 系のデザインとなってるようです。

ARM社がグラフィックス描画処理コア「Mali-400 MP」を発表,画素処理部の数を可変に

Wikipedia NVIDIA Tegra によると Tegra も GeForce6 系らしいのでおそらく
ShaderModel 3.0 でしょう。
GLES 2.0 自体 3.0 世代 (DirectX9) なので十分ですが、ハード機能的には世代が
混在している状態です。実際の速度は機能よりシェーダーユニットの個数で決まるため
判断は難しくなっています。

GLES 2.0 世代はおおざっぱに 14M~35M triangle/sec, 275M~1G pixel/sec あたりと
仮定すると 275MHz x Pixel Units または 20cycle/vertex くらいの計算でしょうか。

Freescale i.MX31 は PowerVR MBX Lite (Wikipedia PowerVR) が使われていました。
i.MX51 は GLES 2.0 対応なので別物です。どうやら AMD Z430 らしい。
(Google i.mx51+z430)

Direct3D Mobile と T-01A の Snapdragon

Toshiba T-01A の d3dmcaps を調べてもらいました。
wm_gamer さん協力ありがとうございました。

Direct3D Mobile DeviceCaps 一覧

Driver="YAMATO_D3DM", Description="AMD Yamato D3D Mobile Driver"
   yes D3DMDEVCAPS_HWTRANSFORMANDLIGHT
   yes D3DMDEVCAPS_HWRASTERIZATION

上記の通り 3D アクセラレータが有効となっています。AMD (ATI) core です。
ドライバはこれまで見たことがない YAMATO_D3DM というもの。

T-01A は Qualcomm の Snapdragon を搭載しています。
Snapdragon は Cortex-A8 同様、新しい ARMv7 世代の ARM core を採用しており、
さらに 1GHz で動作するのが特徴です。

Impress Qualcommの携帯電話向けプロセッサ「Scorpion」~独自実装で1GHz駆動を実現

上の記事によると、かつての StrongARM のように独自の実装であることがわかります。
Cortex-A8 と同じくアウトオブオーダーのスーパースカラで、レジスタリネーミングなど
より積極的な改良が行われているようです。
嬉しいのは VFP 対応なこと。WindowsMobile も、iPhone のように当たり前に
浮動小数演算を使えるようになって欲しいところです。

3D 性能も強化されており、上の d3dmcaps を見ても機能の対応度がこれまでの
MSM7201A (Touch Diamond 他) とは全然違います。

OpenGLES2.0 対応とのことなので、ハードウエア的にはシェーダー世代のはず。
Direct3D8 相当で固定パイプしかない Direct3D Mobile (D3DM) は手狭なのでしょう。
現状で性能を出し切るにはおそらく OpenGLES 2.0 が必要です。

Mobile の世界では Direct3D , OpenGL の立場が逆転しており、Direct3D Mobile の
仕様はかなり遅れているといわざるを得ません。

d3dmcpas のリストを見て特筆すべき点は、出力対応フレームバッファとして 32bit
D3DMFMT_A8R8G8B8 が列挙されていることです。
WindowsMobile は 16bit カラーしか対応しておらず、これまでも最大発色数は 65536色 の
ままでした。当たり前のようにフルカラー対応機種が出ている携帯電話と比べると、だいぶ見劣り
していたことになります。

T-01A / Snapdragon がフルスクリーン時のフルカラー出力に対応しているのだとしたら
私が知る限りでは初だと思います。

関連エントリ
HTC Touch Diamond で Direct3DMobile その(10) d3dmclock v1.10 3Dクロック 更新
HTC S11HT の 3dmcaps