Archives

2009 の記事

Google IME が 64bit に対応したので入れてみました。
Windows 7 x64

きちんとキー操作のカスタマイズに対応しているし、Export/Immport 可能だし、
Export したカスタマイズファイルもテキストなのでそのまま書き換えられます。
今使っている ATOK2008 も結局サポート機能をほとんど全部無効化していたので
これで十分でした。数字による候補選択のショートカットを無効設定できる点も
ATOK2008 より好印象です。

API は TSF に対応しているらしく、em1key も ATOK オプション無しでそのまま
起動できるようです。MS-IME 同様 em1keypc をそのまま実行するだけで使えます。


AMD Z430 は PowerVR と同じようにタイルベースのレンダリング (TBR) を行うことで
知られています。TBR も Unified Shader の仕様も AMD は Xbox360 の GPU がベース
だと説明していますが、その両者の性能差は桁違いです。
一方は強力な GPU が要求するバス帯域に応えるため、もう一方はモバイル向けでタイトな
メモリ速度でもそれなりの性能を維持するため。同じ技術でも応用先の GPU が真逆なのは
面白いところです。

AMD Next-Gen Tile-Based GPUs (pdf)
AMD Next-Generation OpenGLR ES 2.0 Graphics Technology Achieves Industry Conformance

NetWalker に用いられている i.MX515 は GPU core として AMD (ATI) imageon Z430
を搭載しています。
ここ最近 NetWalker で OpenGL ES 2.0 を触っていますが、まだ Z430 の特性がうまく
つかめておらずあまり速度が出ていません。調べている最中です。
偶然 TBR のタイル領域の大きさを知ることが出来ました。


●タイルサイズ

NetWalker_OpenGLES03.jpg

フレームバッファをクリアしないで描画した場合の画面がこれです。GPU のタイルバッファも
クリアせずに上書きされているようです。
1024x600 の画面なので、上の写真のケースだと 1タイルは 256x128 ドット。
以下バックバッファの組み合わせ毎にタイルサイズをまとめてみました。

Color   Depth/Stencil   Tile
-----------------------------------
16bit   none            512x128
16bit   16bit           256x128
32bit   16bit           128x128
16bit   24bit/8bit      128x128
32bit   24bit/8bit      128x128

◎タイルバッファの容量

 少なくても 128KByte 存在しています。予想より大容量です。

◎Depth/Stencil との組み合わせ

 使用可能な depth の幅はカラーの影響を受けることが多いのですが、こちら
 書いたとおり Z430 では任意の組み合わせを選べます。PowerVR もそうだったので
 やはり TBR 専用のバッファを持っているおかげだと思われます。

◎32bit Color or 24bit Depth 利用時の速度低下

 Color か Depth どちらかを 16bit より増やすと速度に影響が出ます。
 その原因はバス帯域だと思っていましたが、この結果を見るとタイル分割数の増加が
 負担になっているのかもしれません。


● Extension

まだ試していませんが TBR を制御する API があります。

GL_VERSION: OpenGL ES 2.0
GL_RENDERER: AMD Z430
GL_VENDOR: Advanced Micro Devices, Inc.
GL_SHADING_LANGUAGE_VERSION: OpenGL ES GLSL ES 1.00
GL_EXTENSIONS:
    GL_AMD_compressed_3DC_texture
    GL_AMD_compressed_ATC_texture
    GL_AMD_performance_monitor
    GL_AMD_program_binary_Z400
    GL_AMD_tiled_rendering
    GL_EXT_texture_filter_anisotropic
    GL_EXT_texture_type_2_10_10_10_REV
    GL_EXT_bgra
    GL_OES_compressed_ETC1_RGB8_texture
    GL_OES_compressed_paletted_texture
    GL_OES_depth_texture
    GL_OES_depth24
    GL_OES_EGL_image
    GL_OES_EGL_image_external
    GL_OES_element_index_uint
    GL_OES_fbo_render_mipmap
    GL_OES_fragment_precision_high
    GL_OES_get_program_binary
    GL_OES_packed_depth_stencil
    GL_OES_rgb8_rgba8
    GL_OES_standard_derivatives
    GL_OES_texture_3D
    GL_OES_texture_float
    GL_OES_texture_half_float
    GL_OES_texture_half_float_linear
    GL_OES_texture_npot
    GL_OES_vertex_half_float
    GL_OES_vertex_type_10_10_10_2
    GL_NV_fence


● NetWalker の速度比較

描画面積を出来るだけ小さくして頂点速度を測定しようとしたもの。

AMD Z430          48000x1 =  48000 Tris/s 30fps = 約 1.44M Tris/s  (1024x600)
PowerVR SGX 535   48000x4 = 192000 Tris/s 45fps = 約 8.64M Tris/s  ( 480x320)

フレームバッファのサイズが極端に違うし、どちらも Unified Shader なので頂点に
ピクセルの影響が全くないとは言い切れません。Clear → Swap だけでもあまり速度が
出ないので、広い画面が仇になっている可能性があります。
タイル+転送だけである程度の負荷がかかり、頂点も圧迫しているのでしょうか。
1024x600 は 480x320 のちょうど 4倍の面積です。

1024x600 = 614400 pixels  8 倍  NetWalker
 640x480 = 307200 pixels  4 倍  VGA
 480x320 = 153600 pixels  2 倍  iPhone
 320x240 =  76800 pixels  1 倍  QVGA


● VRAM 容量

PC のチップセット内蔵 GPU と同じように、VRAM はメインメモリから確保していると
考えられます。NetWalker の RAM は 512MB ですが 480MB しか見えないので、残りの
32MB が VRAM の取り分かもしれません。

1024x600 x16bit は 1.2MB (fb は 2.5MB) なので、32MB もあると少々無駄に感じる
かもしれません。ところが OpenGL ES 2.0 を使っていたらあっという間に VRAM が
溢れました。(GL_OUT_OF_MEMORY を返す)

その原因は自分のローダーでした。Z430 が DXT に対応していないため、圧縮された
テクスチャを 8888 に展開していたからです。あらかじめ ATC/3Dc/ETC へ変換して
おけば解決するはずです。計算したらテクスチャを 23MB も読み込んでいました。

もう一つ考えられる理由は TBR による遅延レンダリングです。
システムメモリからの逐次転送できず、シーンに必要なリソースを全部 VRAM に乗せて
おかないと描画できないのかもしれません。何らかの確証があるわけではなくあくまで
想像です。

どちらにせよ、設定を変えられるなら VRAM 容量はもうちょっと増やしたいところです。


●立ったまま OpenGL ES 2.0 プログラミング

帰りの電車の中では立ったまま、EGLConfig のパラメータを変えて make したりタイルの
データ取りをしてました。この大きさで開発+実行できるのは良いですね。


関連エントリ
NetWalker PC-Z1 i.MX515 OpenGL ES 2.0 (3)
NetWalker PC-Z1 i.MX515 OpenGL ES 2.0 (2)
NetWalker PC-Z1 i.MX515 OpenGL ES 2.0
OpenGL ES 2.0 Emulator


●移植された!

「おくやくん」 は SHARP のポケットコンピュータ、PC-1245/50/51/55 で動くゲームです。
工学社の雑誌、PiO 1985年の 11月号に掲載されたので、もう 24年も前になります。
その古いゲームを、なんと 24年の時を経て他の機種に移植してくれた方が現れました。
下記ページで公開されています。

「ポケットコンピューター」について

こちらには新たに移植された PC-1450 版、PC-1260/61/62 版(*1)がありますし、
16ステージ分オンメモリに改良された PC-1251/55 向けも掲載されています。
BASIC による PC-G801/E200 版もあります。

ページに掲載されているプログラムは音声形式 WAVE ファイルで、これをカセット
インターフェースから読み込ませるとポケコン本体に転送できるという仕組みです。

このカセットインターフェース用のセーブ音を聞くだけでもう懐かしくて涙が出そうです。


●電卓みたいなコンピュータ

SHARP のポケコン PC-1245/50/51/55 や PC-1260/61/62 は非常に小さくて薄くて
ただの電卓にしか見えません。

今更こんな写真を見せられても、コンピュータとして使い物になるのかと疑問に思う方が
きっと多いことでしょう。
いや当時もそう思いました。

でもそこが魅力の一つでもあります。
見かけは薄っぺらな関数電卓なのに、BASIC だけでなくマシン語プログラムが走り、
高速なゲームが動いてサウンドの演奏もできます。当時としてはどれも驚くべきもの。
見た目のギャップと実力の差、中に詰まった感じが良かったのです。


●ゲーム内容

横画面のアスレチックぽい仕様となっていますが、実際は迷路ゲームです。
画面が 1行しかないことを逆手に取り、先が読めず決断を迫られることと、若干の
暗記力が必要でした。

一番の特徴はドット単位の上下スクロールです。
VRAM 構造上の制約もあったため、掲載時はまだ珍しかったように思います。
2D 風のフィールドを移動できてマシン語で比較的高速に動作するし、
内容もかなり緩いゲーム性だったこと、コンストラクションモードがあって自由に
面データを作れたのが良かったのかもしれません。


●おくやくん作成の話と機械語プログラミング

このゲームを作った目的はやっぱりスムーズな上下スクロールでした。
ただ最初はそこまで深く考えていなかったため、あまりきれいな作りではありません。
結局はライン単位でパターンを展開しているし、スクロールにキャラが付いてこない
ところも不完全です。
もっときちんと作っておけば良かったと、ここだけがずっと心残りでした。
完全に 2D 空間を滑らかにスクロール出来たのは、その後の別のゲーム
トロピカルアイランドだったように思います。

おくやくん はほぼ全部マシン語で書かれています。
アセンブラもパソコンも無かったので、紙に手でプログラムを書いていました。
ニモニックは使わずに 16進数で命令を並べ、自分で相対ジャンプを数えて、ある程度
出来たらマシン語モニタで直接 RAM に命令を書き込んでいきます。

これだとあとから大きな修正ができないので、プログラミングは完全にボトムアップです。
キー入力ルーチンやサウンド部分から始めて、パターン転送ルーチンなど小さい部品を
作り、テストしながらメモリ上に配置していきます。
ゲームとして全体が繋がるのは最後でした。
RAM が 2~10KByte しかなかったおかげか、こんな方法でも十分だったのです。


記憶メディアはカセットテープしかなかったものの、ポケコンは今で言うスリープ相当。
RAM の内容はバッテリーで保持されています。
ファイルやディスクといった概念もなく、メモリを書き換えたらそれがすべてです。
プログラムもエディットしたステージデータもずっと RAM 上に保存されています。

そのためプログラム実行時は最初に必ず初期化ルーチンが必要で、ワークエリアを
クリアしたり、パラメータの初期値を転送しなければなりません。
いつも最後に作るのがこの部分でした。

その頃には RAM エリアはすでに後ろまでいっぱい使い切っており、たいてい入る場所
が無くなっています。そこで逆にアドレスの前方に伸ばしていきます。ぎりぎりまで
BASIC エリアを浸食するようになるわけです。
プログラムの一番先頭にワーク初期化用のデータテーブルがあったり、プログラムの
スタートアドレスが途中の中途半端な番地になっているのもそのため。

ちなみに 「おくやくん」 というのは当時の友人、奥山君のこと。


●ポケコンユーザー

移植してくれた柳田さんありがとうございました。
今後他の機種へも移植を行っていくとのことです。

今年の 2月にもLC-3 コンパイラが欲しいとの問い合わせもあって、意外にもまだまだ
ポケコンを使ってる方が結構いらっしゃるようです。
このように問い合わせのメールをいただく度にいつも驚いています。
そして謝ります。すみません、コンパイラはまだ用意できてませんでした。


*1: PC-1260 には過去にも移植版があったようです。森岡氏 PC-1260


関連エントリ
BASICコンパイラと手書き原稿の時代
28年前のモバイル PC-1211 他
20年前のモバイル PC-1417G


わかる方、自力で何とか出来る方のみ参考にしてください。
すべて自己責任でお願いします。

(1) カーネルをコンパイルできる環境を作る。( 参考1, 参考2 )
(2) module を読み込めるようにカーネルを作る。
 (CONFIG_MODVERSIONS を無効化するなど> 例 Module versioning support を外す)
(3) 作成したカーネルを使って起動出来る環境を作る。
  ・SD カード上に作成
  ・本体フラッシュ (/dev/mtd3) を書き換え
  など

SD 上の debian で起動して insmod だけ確認した後、面倒なので /dev/mtd3 を
置き換えました。壊れても気にしない人のみ。それ以外はお勧めしません。
3Gモデム対応ツールを適用しており、かつ互換性あるカーネルを作っている前提です。
L2.6.28_4.5.1_SDK_Aug2009_source.tar.gz (こちら)を展開して install を実行。
以後メッセージに従う。
入力した展開先フォルダの中の ltib/pkgs の中からアーカイブを参照。

$ tar -zxvf amd-gpu-bin-mx51-4.5.1.tar.gz
$ cd amd-gpu-bin-mx51-4.5.1
$ sudo cp -r lib/module/2.6.28-419-g12a78a3/extra  /lib/modules/2.6.28-15-araneo/kernel/drivers/
$ sudo depmod

ファイルを手でコピー。ここでも気にしないでシステムを置き換えています。
分けるなら /usr/local 以下に配置。

$ sudo cp -r  usr/include/*  /usr/include/
$ sudo cp  usr/lib/*  /usr/lib/
$ sudo mkdir  /usr/local/bin
$ sudo cp  usr/bin/*  /usr/local/bin/
$ sudo /usr/local/bin/gpu-install install

最後の gpu-install でドライバを読み込んでいます。
「sudo /usr/local/bin/gpu-install install 」は再起動の度に必要。
添付のサンプルで動作確認。

$ sudo /usr/local/bin/tiger
$ sudo /usr/local/bin/es11ex

(実行結果の画面) フルスクリーンで起動するので VRAM を破壊します。実害はありません。
最初は ssh などネットワーク経由での実行をお勧めします。

サンプルが動けば動作はうまくいったように見えますが、ドキュメントやサンプル
ソースがあるわけでもないので、実際の使い方はまだまだ手探り状態です。

Display インターフェースは egl なので、初期化は以前 OpenGL ES 2.0 Emulator
を取り扱ったときのコードがほぼそのまま使えます。
AMD 版 GL ES 2.0 Emulator のターゲットがこの Z430 なので当たり前といえば
当たり前です。

OpenGL ES 2.0 Emulator

eglGetDisplay() は EGL_DEFAULT_DISPLAY を使用。

eglChooseConfig() では 4444, 565, 5551, 8888 が使えるようです。
depth は 0, 16, 24 (24+ stencil8) のどれかと組み合わせ。
カラーバッファが 16bit でも 24bit depth を使えます。
バックバッファのフォーマットなので、8888 を指定してもフロントバッファが
32bit になるわけではありません。

eglCreateWindowSurface() で描画と API をバインドしますが、この場合
EGLNativeWindowType に何を指定すればよいのか悩みました。
いろいろ試した結果、/dev/fb0 を渡したら動きました。

EGLNativeWindowType eglwin= open( "/dev/fb0", O_RDWR, 0 );
egl_Surface= eglCreateWindowSurface( egl_Display, egl_Config, eglwin, NULL );

描画のフラッシュと転送は普通に eglSwapBuffers() で動きます。

デバイスファイルにアクセスするため root で実行する必要があります。

フレームバッファは 16bit 1024x600 ですが、倍の容量存在しているのは 32bit
への切り替えを考慮してのことでしょうか。
この辺いろいろとステートを読み出したりしつつ調べている最中です。
例えば EGL_HORIZONTAL_RESOLUTION が 2048 になっている点など、まだ良く
わかっていない部分が多数あります。

描画の印象としてあまり速くありません。eglSwapInterval() に 1 しか設定
できないため、常にモニタのリフレッシュレートと同期している可能性があります。
ただ動作時間が半分ほどで終わってしまい、計算と一致していないのも謎です。
性能はまだ未知数。

取りあえず GLSL のシェーダーを読み込んで、テクスチャの描画まで成功。
シェーダーのコンパイルエラー発生時に、ドライバが詳細なエラーメッセージ(log)
を返してこないのも はまったところです。デバッグは Emulator で行った方が
良いかもしれません。

ヘッダ (GL ES 2.0 の場合)

#include  <EGL/egl.h>
#include  <GLES2/gl2.h>
#include  <GLES2/gl2ext.h>

ライブラリの指定など (Makefile)

CC      = gcc
LIBS    = -lm -lstdc++  -legl13 -lgles20
INCS	=
CFLAGS  = -O4 -Wall \
	-march=armv7-a  \
	-mtune=cortex-a8        \
	-mfloat-abi=softfp      \
	-mfpu=neon      \

TARGET  = main
OBJS    = main.o

$(TARGET): $(OBJS)
	$(CC) -o $(TARGET) $(OBJS) $(LIBS)

%.o:%.cpp
	$(CC) -c -o $@ $< $(CFLAGS) $(INCS)

ARM Cortex-A8 + OpenGL ES 2.0 は iPhone 3GS などハイエンドスマートフォントにも
搭載されています。
比べると NetWalker の良いところはクロス開発しなくてもいいところでしょう。
修正したり設定を変えてテストを繰り返していると、自分でコンパイルできるのは非常に
便利だと実感します。
micro SD 上で作業しているので、開発環境もこの小さい本体だけです。


関連エントリ
NetWalker PC-Z1 i.MX515 OpenGL ES 2.0 (2)
NetWalker PC-Z1 i.MX515 OpenGL ES 2.0
OpenGL ES 2.0 Emulator


NetWalker_OpenGLES01.jpg

NetWalker_OpenGLES02.jpg
いろいろ手間取ったけど、何とか動かすことに成功

追記:
Version: 1 3
EGL_CLIENT_APIS: OpenGL_ES OpenVG
EGL_EXTENSIONS: eglCreatePbufferFromClientBuffer EGL_KHR_image EGL_AMD_create_image
EGL_VENDOR: Advanced Micro Devices, Inc
EGL_VERSION: 1.3 Internal version 1.4.1

GL_VERSION: OpenGL ES 2.0
GL_RENDERER: AMD Z430
GL_VENDOR: Advanced Micro Devices, Inc.
GL_SHADING_LANGUAGE_VERSION: OpenGL ES GLSL ES 1.00



関連エントリ
NetWalker PC-Z1 i.MX515 OpenGL ES 2.0


見つけたのは自分ではありませんが、このあたりに Linux のソースコードとか
i.MX515 用の OpenGL ES SDK とかいろいろとファイルが追加されているそうです。
Freescale i.MX515 Software & Tools


関連エントリ
NetWalker PC-Z1 ubuntu で ctrla を動かす ([A] キーで Control と両立)


久しぶりに NetWalker の情報を探したら、モジュールを作れるようになっているし
キーカスタマイズソフト「窓使いの憂鬱」も動くようになっているとのことです。
かなり便利になってきました。ありがたいことです。

Android Zaurusの日記
 ・NetWalkerのカーネルとローダブルモジュールのビルド

ひとりぶろぐ
 ・[NetWalker]窓使いの憂鬱をNetWalkerで動かす

しかも「窓使いの憂鬱」では [A]キー を Control にする設定まで出来るようです。
これで ctrla を作らなくて済むし、楽が出来る!!
と思って試したら ctrla の動作とはちょっと違いました。少々甘かったようです。

そこで上のページを参考にして uinput.ko を作り、ctrla を NetWalker 本体の Ubuntu
で動くようにしてみました。
Android Zaurusの日記 さん、ひとりぶろぐ さんの記事を参考にさせていただきました。
ありがとうございました。

以下
 ・ctrla の動作の違い
 ・新しい ctrla v1.10
 ・ctrla を NetWalker の Ubuntu で動かすまで

ctrla は、[A] キーを Control キー兼用として用いるためのソフトです。


● ctrla の動作の違い

「窓使いの憂鬱」の "mod control += !!A" の設定では、[A]キー を押している間に
他のキーが押されたタイミングで Control キーと見なしています。

これだけだと、素早く文字入力を行った場合に意図しない Control キーが挿入されて
しまうことがあります。

例えば「会議」と打とうとして "kai" と素早くタイプすると、[A] を離す前に [I]
が押されて Control-I が入ってしまうことになります。

ctrla では連続タイプについていろいろ検証しており、この状態を区別できるように
二つのクロスストローク判定を導入しています。

もしかしたら「窓使いの憂鬱」でも設定次第で実現出来るのかもしれません。
そのような機能がすでにありましたらごめんなさい。


●クロスストローク判定1、遅延判定

"kai" を素早く打つと、下記のように前のキーを離す前に次のキーを押してしまう
ことがあります。

→時間
                 (1)  (2)
---------------------------------------
[K]dw ------ [K]up
         [A]dw ------ [A]up
                 [I]dw ------ [I]up

「窓使いの憂鬱」+ "mod control += !!A" はおそらく (1) のタイミングで判定
しています。これだと Control-I が入ってしまいます。

ctrla では、[A] キーを先に離すと 'A' キーを、[I] を先に離すと Control-I が
入るように判定を遅らせています。
つまり上の例だと (2) のタイミングで 'A' キーを送信します。

下記の例では先に [I] を離しているので (3) のタイミングで Control-I になります。

                   (3)
---------------------------------------
 [A]dw ------------------- [A]up
         [I]dw --- [I]up


●クロスストローク判定2、id による識別

実際に使ってみると、上の「遅延判定」だけでは不十分なことがわかります。
例えば「朝」と高速にタイプしようとすると下記のキーストローク "asa" になります。

→時間
         (4)          (5)
---------------------------------------
[A]dw ------ [A]up
         [S]dw ------ [S]up
                 [A]dw ------ [A]up
---------------------------------------
ID1→            ID2→

[S] を押したタイミング (4) でも、[S] を離したタイミング (5) でも、どちらも
[A] キーが押されているため、単純な遅延判定だと (5) で Control-S が入ってしまいます。

そこで ctrla では、[A] キーを押す度に毎回異なる id 値を割り振るようにしています。
キーを押した時点の id と離した時の id 値が異なる場合は、別の [A] キーとみなして
Control に変換しません。

よって上の例の場合、(5) のタイミングで id が異なるのでそのまま 'S' キーを送信します。


●新しい ctrla v1.10

プログラム ctrla110.tgz

下記の修正を行いました。

 1. NetWalker の Ubuntu (uinput.ko) で動作しない問題を修正
 2. 連続して 1秒以上 [A] キーだけ押し続けた場合、Control とみなす

1. は hook_main.c で構造体がクリアされていないところがあったために、module
として組み込んだ場合に誤動作していました。カーネルに組み込んだ場合はメモリが
0 初期化されているらしく動いていました。(/dev/input/event* で動くのもそのせい)

テキストエディタ上で考え事をしているとき、無意識に Ctrl キーを押しっぱなしに
していることがあります。カーソル移動とか編集操作を一切せずにキーを離すと
'A' キーが入ってしまうことがありました。
そこで [A] を 1秒以上押し続けた場合は Control と見なすようにしました。


● ctrla を NetWalker の Ubuntu で動かすまで

(1) 3Gモデム対応ツールを入れる

NetWalker(PC-Z1) 「3Gモデム対応ツール」

 この更新を行うと無線 LAN 接続でトラブルが起こる可能性が高いので、先に無線LAN
 を off にしておいてください。以前自分でカーネルを build したときに無線 LAN
 が動かなかった症状と良く似ています。

(2) uinput.ko を作る

NetWalkerのカーネルとローダブルモジュールのビルド」の手順をそのまま実行します。

Makefile の修正は「EXTRAVERSION = .10」の行を「EXTRAVERSION = -15-araneo」
に置き換えます。

1時間くらいしてコンパイルが通ったらモジュールを作ります。

$ sudo aptitude install libncurses5-dev
$ make menuconfig

メニューで下記の設定を変更します。
(space キーを押して User level driver support を '<M>' にする )

Input device support --->
  Miscellaneous devices --->
    <M> User level driver support


$ make modules
$ sudo cp  drivers/input/misc/uinput.ko  /lib/modules/2.6.28-15-araneo/kernel/drivers/input/
$ sudo insmod /lib/modules/2.6.28-15-araneo/kernel/drivers/input/uinput.ko

(3) ctrla v1.10 を動かす

$ mkdir $HOME/ctrla
$ cd $HOME/ctrla
$ wget http://dench.flatlib.jp/arfiles/ctrla110.tgz
$ tar -zxvf ctrla110.tgz
$ sleep 1; sudo ctrla/ctrla /dev/input/event5

まずはこの状態で、他のターミナルやソフトでキーの動作を確認してみてください。
万が一キー入力できなくなった場合も、慌てずに USB キーボードつないでください。
外付けキーボードの動作には影響を与えません。


関連エントリ
NetWalker PC-Z1 カーネルのコンパイル
NetWalker PC-Z1 Debian (2)
NetWalker PC-Z1 Debian
NetWalker PC-Z1 [A] キーを Control キーと兼用する実験
NetWalker PC-Z1 Bluetooth とキーカスタムその他


小さくなったおかげで持ち歩きやすくなり、結構便利に使ってます。


● 例えばミュージックプレイヤー

Bluetooth が AVRCP に対応しているおかげで、ヘッドホン用のレシーバー DRC-BT15P
がワイヤレスリモコンも兼ねるようになりました。
曲の選曲や再生停止も手元でできるわけです。
この機能が使えるのは、クロスメディアバーでカーソルがミュージックにある場合に
限られるようです。
プレイヤーに移動したり、画面を消したり HOLD したりと少々手順が多いのが難点。


● HOLD は重要です

使っていて気になったのは、少しスライドしただけですぐスリープが解除されて
しまうこと。

一度鞄から取り出したらバッテリーが無くなっていて、電源が全く入らなくなっていた
ことがありました。たぶん移動中にどこかでスライドがずれて電源が入ったのだと思います。

ゲーム中のスリープだったので、オートスリープせずにバッテリーを使い切って
しまったのでしょう。スライドも HOLD にしておけば反応しないので、普段から
HOLD に切り替えておいた方が良さそうです。

その代わり嬉しい発見もありました。


●自動で中断セーブしてくれます

スリープ中バッテリーが空になったので、直前のゲームが消えてるだろうなとあきらめて
いたのですが、なんと中断セーブデータが残っていました。バッテリーが切れる前の状態
から再開しています。
バッテリーが切れそうになると、自ら中断セーブを行ってくれるようです。

PSP go はゲームの中断機能があって、プレイ中に別の機能を呼び出すことが出来ます。
仕組みはノート PC のサスペンド/ハイバネーションと同じなので、このような活用も
出来るんだと感心しました。

これ実際のゲームプレイ中にも一度確認してみたいと思ってます。
本当に自動で中断してくれるならもうバッテリー切れが怖くないですね。


関連エントリ
PSP-N1000 PSP go の Bluetooth で出来ること
PSP-N1000 PSP go 購入、Store が無くても出来ること


ComputeShader はかなり便利なことがわかってきました。
ポストフィルタなどのピクセル処理だけでなく、VertexShader の前段に頂点演算として
挿入したり、頂点シェーダーの代わりに使ったりも出来ます。
標準描画パイプラインへのちょっとした機能追加が簡単にできるようになった印象です。

これまでは Stream Output や PixelShader を使っていた処理が ComputeShader
だけで済むわけです。


●ここが簡単

・読み込み位置、書き込み位置が固定されない

  与えるリソースはどれも任意アドレスに対してアクセス可能で、特別扱いする
  ものがありません。

・設定が項目が少ない

  ConstantBuffer, ShaderResourceView, UnorderedAccessView のわずか 3つだけ。
  サンプラーを使う場合は + 1。

・設定が独立している

  描画用のステートをいじる必要がないので、パラメータを保存したり復帰させなくて
  済みます。たとえば Viewport とか、RenderTarget とか、
  DepthStencilState とか!! 描画に影響与えないし、戻さなくてもいいんです。
 
特に 3番目。いちいち DepthStencilState を作ったり、Depth を disable にしたり
しなくても良いだけで CS ありがとう、といった感じ。


● CS の制限

今までの Shader から見れば制限無しに扱いやすい ComputeShader ですが、
VertexShader 代わりに使おうとするといくつか制限も生じます。

 ・グループ内の実行スレッド数はシェーダー内に記述し、実行時に書き換えできない。
 ・Dispatch() に与えられる実行回数は x,y,z それぞれ 65535 まで。
 ・グループ内のスレッド数は 1024 まで。

よって VertexShader のように、1~100万回 など任意の実行回数が与えられた場合に
どのように Compute Shader を呼び出せばよいのか悩みます。

1. 65535 回を超える場合

x, y, z に分けるにしろ、CS 内のアトリビュートで Group 内スレッド数を増やすにしろ、
実行回数が常に定数で割り切れるとは限らない。

2. 速度

ある程度グループ内のスレッド数を大きめの値にしなければ ComputeShader の実行
速度が落ちます。65535 回未満だからと言って、Dispatch() だけで回数を指定して
下記のようなシェーダーを走らせると非常に低速になります。

[numthreads(1,1,1)]
void cmain_loop1( uint3 threadid : SV_DispatchThreadID )
{
    lmain( threadid.x );
}


●解決案

二通りの手段を考えてみました。

(1) 複数のシェーダーに分ける

numthreads = 32 などグループスレッド数を増やしたものと、numthreads = 1 の
端数を処理するスレッドに分けて実行します。
下のプログラムは 32 で割り切れる回数分 cmain_loop32 を実行し、端数を
cmain_loop1 で処理しています。

例えば 75 個のデータを処理するなら、cmain_loop32 を 2回、cmain_loop1 を 11回分
実行します。

// Compute Shader 5.0
cbuffer offset_T : register( b0 ) {
    uint    threadid_offset;
    uint    thread_total;
    uint    r0;
    uint    r1;
};

[numthreads(32,1,1)]
void cmain_loop32( uint3 threadid : SV_DispatchThreadID)
{
    lmain( threadid.x );
}

[numthreads(1,1,1)]
void cmain_loop1( uint3 threadid : SV_DispatchThreadID )
{
    lmain( threadid.x + threadid_offset );
}


// C++
    const int ThreadGroup= 32;
    int dcount1= data_count/ThreadGroup;
    int offset= dcount1*ThreadGroup;
    int dcount2= dcount - offset;

    offset_T cparam;
    cparam.threadid_offset= offset;
    cparam.thread_total= data_count;
    context.UpdateSubresource( CB_Offset.iBuffer, 0, NULL, &cparam, 0, 0 );
    context.CSSetConstantBuffers( 0, 1, &CB_Offset.iBuffer );

    if( dcount1 > 0 ){
        context.CSSetShader( LoopShader_1.iCS, NULL, 0 );
        context.Dispatch( dcount1, 1, 1 );
    }
    if( dcount2 > 0 ){
        context.CSSetShader( LoopShader_32.iCS, NULL, 0 );
        context.Dispatch( dcount2, 1, 1 );
    }


(2) 動的分岐を用いる

端数込みで 32 の倍数分実行します。スレッド番号が実行したい回数より多ければ、
動的分岐で処理を省きます。

例えば 75 個のデータを処理するなら、cmain_loop_dis を 3 回 (96回分) 実行し、
id が 75 以上なら何もしないで終了します。

// Compute Shader 5.0
cbuffer offset_T : register( b0 ) {
    uint    threadid_offset;
    uint    thread_total;
    uint    r0;
    uint    r1;
};

[numthreads(32,1,1)]
void cmain_loop_dis( uint3 threadid : SV_DispatchThreadID )
{
    if( threadid.x < thread_total ){
        lmain( threadid.x );
    }
}


// C++
    const int ThreadGroup= 32;
    int dcount1= (data_count+ThreadGroup-1)/ThreadGroup;

    offset_T cparam;
    cparam.threadid_offset= 0;
    cparam.thread_total= data_count;
    context.UpdateSubresource( CB_Offset.iBuffer, 0, NULL, &cparam, 0, 0 );
    context.CSSetConstantBuffers( 0, 1, &CB_Offset.iBuffer );

    context.CSSetShader( CSSubDShaderDis.iCS, NULL, 0 );
    context.Dispatch( dcount1, 1, 1 );


●実行結果

RADEON HD 5870 で試してみました。
1 フレームあたり 7セット Compute Shader の実行を繰り返しています。
それ以外の描画は fps などのフォントのみ。

上のプログラムはグループ内スレッド数 32 固定でしたが、16~320 まで変更して
試しています。

GroupThread= 16
(1)   476 fps
(2)   482 fps

GroupThread= 32
(1)   840 fps
(2)   852 fps

GroupThread= 48
(1)   973 fps
(2)  1006 fps

GroupThread= 64
(1)  1102 fps
(2)  1145 fps

GroupThread= 96
(1)   984 fps
(2)  1026 fps

GroupThread= 128
(1)  1090 fps
(2)  1140 fps

GroupThread= 256
(1)  1083 fps
(2)  1128 fps

GroupThread= 320
(1)  1009 fps
(2)  1065 fps

GroupThread が小さいと低速です。あまり小さいと Dispatch() の 65535 制限にも
ひっかかります。

どのケースでも、分岐を用いた (2) の方が高速でした。
端数分とはいえ GroupThread=1 で実行しているスレッドがあるため効率が悪い、
7セットの実行中に毎回シェーダー切り替えが発生しているから、切り替えないで済む
(2) の方が条件的に有利、等の理由が考えられます。


関連エントリ
DirectX 11 / Direct3D 11 と RADEON HD 5870 の caps
Direct3D11/DirectX11 ComputeShader 4.0 を使う
Direct3D11/DirectX11 (6) D3D11 の ComputeShader を使ってみる


URoad-5000 は持ち歩きには便利だし電源を入れるだけで即使える点も魅力です。
でもバッテリー駆動できるモバイル向けルータなので、パフォーマンスよりバッテリー
寿命や軽量化の方が優先されているようです。自宅など据え置き用途で使うには少々
パワー不足な感じがします。UPnP に対応していないといった制限もあります。

そこで、使い道に困っている(余っている) NetBook を自宅用ルータにしました。
Windows 標準の ICS を使っているだけですが、たぶんこちらの方が高速でしょう。
実際に試したのは Vista Home Basic です。ここでは Windows 7 で説明しています。

(1) NetBook に WiMAX の設定を行います。普通に WiMAX が繋がれば OK。

  USB タイプ UD01SS (UD03SS) を使ったので、USB ポートにさしてドライバとユーティリティ
  ソフトをインストールし、更新をかけました。

(2) 「ネットワークと共有センター」を開きます。
  左側の「アダプターの設定の変更」をクリック。(Vista だと「ネットワーク接続の管理」)

(3) 「ローカルエリア接続」の中から WiMAX 相当を探します。
  右ボタンでプロパティを開きます。

(4) "共有" タブを開いて共有の設定をします。
   「ネットワークのほかのユーザーに ~ 接続を許可する」
   「ネットワークのほかのユーザーに ~ 制御や無効化を許可する」
  の両方にチェックを入れます。

(5) 「ホーム ネットワーク接続」のプルダウンメニューで、他の PC やゲーム機など
  の端末をどこにつなぐか選択します。

  たいていの NetBook は、有線 LAN ポートと無線 LAN の両方を備えています。
  おそらくアダプタとしてこの段階で下記の 3つが存在しているはずです。

   ・ローカルエリア接続
   ・ローカルエリア接続 2 (UD01SS WiMAX)
   ・ワイヤレスネットワーク

  ※ ローカルエリア接続 の番号は環境によって変わる可能性あり
  今回は次のようにつなぐので、WiMAX じゃない方のローカルエリア接続を選びます。

     WAN (UQ WiMAX)
       |
 [ローカルエリア接続2 (UD01SS)] 
       |
     [NetBook]
       |
 [ローカルエリア接続 (有線LAN)]]
       |
[ハブ or 無線LAN アクセスポイント]
       |
   他の PC やゲーム機

共有設定することで NetBook がルータ相当になり、DHCP サーバーとして IP アドレスも
割り振ってくれます。

無線 LAN アクセスポイントの設定が済んでいるなら、NetBook とアクセスポイント間を
無線でつなぐことも可能です。アクセスポイントに DHCP クライアント機能があるならそれを
有効にするだけですが、固定 IP アドレスなら割り振られるアドレスと確認してから登録します。


●速度

時間帯や条件が一定と限らないので、速度はおおよその目安です。
UQ WiMAX 自体の速度はエリアや電波状況で変わります。

(1) 1.5Mbps

UD01SS + URoad-5000 --無線-- LAN-PWG/APR(子機モード) --有線-- Desktop PC

モバイルルータを 2 個経由していたせいか、かなり速度が落ちていました。
LAN-PWG/APR は子機モードでコンバータとして使っています。

(2) 2~3Mbps

UD01SS + URoad-5000 --無線-- GW-USMicroN (USB無線LANアダプタ) + Desktop PC

無線 LAN 直結 (11n) になったので速くなりました。

(3) 5~Mbps

UD01SS + NetBook

直差しが一番速いです。5Mbps くらい。

(4) 5Mbps

UD01SS + NetBook --有線-- CG-WLBARGNH(ルータ,Hubとして) --有線-- Desktop PC

NetBook に直接有線でぶら下がっている形です。オーバーヘッドが減っています。

(5) 4.5~Mbps

UD01SS + NetBook --有線-- CG-WLBARGNH --無線-- GW-USMicroN + Desktop PC

CG-WLBARGNH (無線LAN ルータ) を、アクセスポイントモードで設定しています。
これで無線 LAN ルータ相当となりました。モバイルルータをそのまま使うよりも
速度が出ています。


関連エントリ
WiMAX ルータ URoad-5000 (4) 充電の仕方
Windows7 beta で UQ WiMAX


Bluetooth を使ってみました。
本体に触らないでゲームできます。

pspgo_00.jpg

PS3 コントローラで PhantasyStar Portable 2 体験版 + Bluetooth でヘッドホン


●PSP go の特徴

PSP go の最大の違いは UMD ドライブが無いことです。他にも

 ・フラッシュメモリ 16GB 内蔵
 ・スライド式のボディ
 ・Bluetooth 機能内蔵
 ・ゲームの中断機能に対応
 ・時計/カレンダー表示

といった新要素があります。また従来の PSP から変更されたのは下記の点です。

 ・充電+通信コネクタが専用形状に変更
 ・画面サイズが 4.3 → 3.8 inch に縮小
 ・メモリカードが MS Duo から M2 に変更された

新要素の中でも Bluetooth に対応したのは大きな特徴です。


● PSP go の方向キー

PSP go の方向キー(十字キー)はスライド機構のため、周囲との段差が少なくストロークも
かなり浅くなっています。倒すと言うよりボタンをクリックして押す感じに近いです。
方向の押し分けはしっかりしており操作性は問題ありませんが、PSP-2000/3000 の操作感に
慣れてしまうと、若干斜め方向が入りにくく感じるかもしれません。

とはいえ PSP-1000 のように全く反応しないと言うこともなく、比べものにならないほど
きちんと斜め方向が入ります。DS Lite のように斜めに入り損ねることもなく、PSP go の方が
反応良く確実に押せます。ボタンの薄さは DSi に近いかもしれません。

さらに PSP go は外付けのコントローラを使うことが出来ます。
操作性に関しては、個人の好みの点も含めても携帯機で最高の環境を備える可能性が出てきました。


● PS3 コントローラが使える

Playstation 3 / Wii 等のワイヤレスコントローラは Bluetooth を使っています。
PSP go も Bluetooth を内蔵したことにより、携帯ゲーム機ながら外付けのゲーム
コントローラが使えるようになりました。

現在対応しているのは PS3 用のゲームコントローラ SIXAXIS / DUALSHOCK3 です。
Bluetooth は汎用プロトコルなので今後の更新で対応デバイスが増える可能性があります。

注意点はペアリングのために PS3 本体が必要となること。
SIXAXIS / DUALSHOCK3 は USB ケーブルによるワイヤード接続でペアリングを行う仕様と
なっており、Bluetooth のペアリングモードを備えていません。PSP go もいったん PS3 を
経由してコントローラの登録を行います。一度ペアリングを行えば PS3 が無くても使えます。
PS3 持ってる人に頼んでペアリングだけしてもらうのもありでしょう。


● ペアリング

用意する物

 ・PS3 用コントローラ (SIXAXIS/DUALSHOCK 3)
 ・PS3 本体 (最初だけ)
 ・PSP go
 ・それぞれをつなぐ USB ケーブル

(1) PS3 の電源を入れておく
(2) PSP go 側で操作
   「設定」→「Bluetooth機器設定」→「Bluetooth 機器管理」
    →「新しい機器の登録」→「PS3 コントローラを登録する」
(3) 以後 PSP go の画面の指示に従います。

 1. PSP go と PS3 を USB ケーブルでつなぐ。
 2. ペアリングしたいコントローラを PS3 本体と USB ケーブルでつなぐ。
 3. 画面の指示があったら両方とも USB ケーブルを抜く。
 4. PS3 コントローラの (PS) ボタンを押す

操作は PSP go 側で行うので、この間 PS3 の画面を見る必要がありません。
TV をつけずに設定出来ました。


●操作

PS3 コントローラを使うとスライドを閉じた状態でもゲームが出来ます。
(PS) ボタンも反応するし、PSP go の電源を切るとコントローラの電源も落ちます。
go の電源を入れてコントローラの (PS) ボタンを押せばすぐに再リンクします。

ボタンの対応は下記の通り。

・右アナログスティック, L3, R3 = 未使用
・L1, L2 = どちらも PSP の L
・R1, R2 = どちらも PSP の R

基本的に PSP に存在していないボタンは使えないようです。

ゲームアーカイブスの PS1 タイトルで右アナログが使えるかどうか、DUALSHOCK 対応ソフトを
持っていなかったので検証できませんでした。設定画面を見つけていないので、おそらく
振動機能も使えないのではないかと思います。
PS1 のゲームで同時に複数のコントローラが使えるかどうかも未確認です。


●その他の入力デバイス

キーボードとマウスは「非対応のデバイスです」といわれて接続できませんでした。

PS3 ワイヤレスキーパッド
・RBK-2000BT2 (Bluetooth キーボード)
・VGP-BMS33 (Bluetooth マウス)


●オーディオデバイス

Bluetooth オーディオデバイス (A2DP) に対応しています。
下記のワイヤレスオーディオレシーバーを使ってみました。

SONY DRC-BT15P
SANWA MM-BTSH3

ペアリングは直接行います。

「設定」→「Bluetooth 機器設定」→「Bluetooth 機器管理」→「新しい機器の登録」
 →「Bluetooth 機器を検索する」

あとはそれぞれのレシーバーのマニュアルに書かれているパスキーを入れるだけ。
音声出力切り替えは、PSP go 上面にある [♪] 音符マークのサウンドボタンを使います。

 ・[♪] ボタンを長押しするとミュート (1秒)
 ・[♪] ボタンをさらに長押し (5秒) すると音声の出力先が Bluetooth に切り替わる

A2DP 対応は PS3 にも欲しい機能です。


● Bluetooth 音声の注意点

Bluetooth のリンクが切れると音声は強制的に本体から流れます。
例えば左側面のワイヤレススイッチを切った場合、無線 LAN だけでなく Bluetooth も
切れます。直後、本体スピーカーから音声が流れることになります。


●ダイヤルアップネットワーク

PSP は無線 LAN のアドホックでインターネット接続できないので、Bluetooth の
ダイヤルアップ対応は嬉しい機能の一つです。

EMOBILE TouchDiamond S21HT を使ってみましたが、ペアリングできるものの接続は
まだ成功していません。PC からの接続では Bluetooth 経由で DUN で繋がっているのに、
PSP go からは 8030000B というエラーで失敗します。まだ原因はわかってません。


●警告画面が出てしまう

Bluetooth を ON にすると、USB モードに切り替わるたびに Bluetooth を一時的に無効するとの
警告画面が出てきます。いちいちボタンを押さなければならないので少々面倒かもしれません。


●PSP go 全般

PSP-1000 時代に夢だった外部コントローラが、念願かなって使えるようになって
しまいました。当時 PSP-1000 の方向キーに問題があったせいなのですが…。
PS3 のように、今後はキーボードとか様々なデバイスに繋がるようになればゲームの
幅も広がります。特にアーカイブスタイトルとか。

画面は密度が上がったことと、コントラストが高く色もはっきりしており非常にきれいに
見えます。以前使っていた PSP が PSP-2000 なので特にそう感じるのかもしれません。

本体は非常に小さくスリムです。これに携帯電話が付いていてもおかしくないと思うくらい。
本体底面に滑り止めのゴムあり。

上面は隙間無くボタンが並んでいるのでつかむ場所がありません。
上下を挟んで持つのはやめた方がよさそう。

Bluetooth 対応した携帯ゲーム機は多くありません。
iPhone や iPod touch も Bluetooth を内蔵していますが A2DP に対応しているものの
外部コントローラやキーボードをつなぐことが出来ません。
iPhone で Bluetooth コントローラが使えるようになる日は来るのでしょうか。


関連エントリ
PSP-N1000 PSP go 購入、Store が無くても出来ること
Media Go で PSP のデータ管理
新型 PSP (PSP-2000) とゲームアーカイブス


PSP go (PSP-N1000) 買いました。
ほぼゲームアーカイブス専用機と化していた自分にとっては理想の端末です。
欲しいダウンロードソフトが出るまでは、小型で持ち歩ける PS1 になりそうです。


●体験版

最初からある程度充電してあったらしく電源が入りました。
本体メモリにもゲームの体験版が 3本インストール済みなので、箱から出してすぐ
ゲームで遊べます。

完全に充電しておかないとファーム更新で怒られるのでまずは充電します。


●とりあえず充電

付属の AC アダプタはメガネケーブルタイプですが、VAIO type P のように直付けの
プラグアダプタがついていました。充電ケーブルも USB ケーブル兼用なので
AC アダプタを使わずとも USB で充電できます。

 ・付属の AC アダプタを使う (付属の専用 USB ケーブルが必要)
 ・USB から充電する (付属の専用 USB ケーブルが必要)

パソコンなどの USB コネクタから充電を行う場合、PSP 側を USB モードに切り替えて
おく必要があります。USB 端子から充電しながらゲームするだけの電力がとれないための
配慮だと思われます。この辺も PSP-2000/3000 と同じです。
USB ケーブルが堅めで取り回ししづらいのは残念。


●とりあえずネットワーク

ネットワークに接続します。ワイヤレス LAN の設定もこれまでの PSP と全く同じ。
NetWalker に続いて WiMAX ルータ URoad-5000 が活躍してくれそうです。

設定 → ネットワーク設定 → インフラストラクチャモード
[新しい接続の作成] → 検索する → 一覧から対象のルータを選んで

SSID : デフォルトのまま
セキュリティー設定 : そのまま
キー: 入力する

アドレス設定 : かんたん → 接続名: 任意 → 完了

これでインターネットブラウザーが使えます。


●とりあえず更新

購入時のシステムソフトウエアのバージョンは 5.70 でした。

設定 → システムアップデート

更新後 6.10 になります。


●ここで少々失敗

ネットにさえつながれば、ソフトなんてどこでもダウンロードできるだろうと甘く
考えいたものの、出先なもので肝心のアカウントを失念。
PlayStation Store からダウンロード出来ませんでした。

新規でアカウントを作るのも手間だし、せっかくなので以下 Store に接続しないで
どこまで遊べるかやってみます。


●ソフトウエアの転送/ダウンロード手段のまとめ

Media Go の CD-ROM が入っていましたが、もともと PS3 で購入管理していたのと
PC にもすでにインストール済みなのでたぶん不要です。
Media Go はこちらからダウンロードできます。

SONY Media Go

ソフトの入手と転送方法は下記の 3種類あります。

(1) PSP go 本体から PlayStation Store に直接つないでダウンロード
(2) Playstation3 上の PlayStation Store からダウンロード+USB 転送
(3) Windows PC + Media Go で PlayStation Store からダウンロード+USB 転送


●アカウントが無くても体験版ダウンロード

体験版だけならもう一つダウンロード方法があります。
PlayStation Store のアカウントも不要です。

(4) 本体内蔵ブラウザ+ PSP Home ページからダウンロード

ネットワーク → インターネットブラウザー 

ブラウザが起動したら (△) ボタンで表示されるメニューを開いて「ブックマーク」
(ハート型アイコン) の "PSP" を選択します。
プリセットされている PSP の公式ホームページです。

ここから体験版やムービー、カスタムテーマ、壁紙などをダウンロードすることが
できます。PlayStation Store に並んでいるものとはラインナップが異なります。

PSP 本体から PlayStation Store に直接接続できるようになったのはほんの 1年前
システムバージョン 5.0 からです。それまでは、ここから体験版をダウンロード
していました。


●インターネットラジオ

ネットワーク → インターネットラジオ → インターネットラジオについて

説明を読んで、各種ネットラジオプレイヤーをダウンロードします。
多数あるのでとりあえず片っ端から落としてみます。

ダウンロードしたプレイヤーを起動すると、ネットワークに接続した後
「このページに埋め込まれたプラグインを実行しますか」ときかれるので「はい」。

しばらくして音楽のストリーミング再生が始まります。これらのプレイヤーはブラウザ
のプラグインで作られています。基本的なカーソルの操作はブラウザと同じ。
負荷が重いとなかなかアイコンが表示されないのもそのためです。
ストリーミング先が重いとなかなか再生が始まらないので、そのときはあきらめて
放送局を切り替えた方がよいです。

プレイヤーとして使うならこんな感じの設定で。

1. 本体のスライドを閉じる
2. 上部の□ボタン (画面輝度切り替えボタン) を長押ししてバックライトを消す
3. 電源スイッチを HOLD にする


●ポッドキャスト

ネットワーク → RSS チャンネル → RSSチャンネルガイド

音声や動画による配信を視聴できます。いわゆるポッドキャストプレイヤーです。
チャンネルガイドでブラウザが起動するので、好きな番組を登録して使います。

例えば TOKYO FM Podcasts など、ラジオ局のものも多く提供されています。
「インターネットラジオ」よりも、こちらの方が日本語でイメージする「ラジオ」に
近いかもしれません。


●メディアプレイヤー

USB ケーブルで PC につなぐと USB モードに切り替わります。
PC からはリムーバブルディスクとして認識されました。
Media Go を使ってデータを転送すればメディアプレイヤーとして使えます。
エクスプローラから直接コピーも可能です。

ここまで説明した機能はどれも従来の PSP で出来ることばかりでした。
あらためて考えると PSP で出来ることは多いです。これ以外にも skype とか周辺機器
などいろいろあるので。


●容量と整理方法

アーカイブスのゲームはどれも容量が大きいですが、本体だけで 16GB あるので
しばらく持ちそうです。

むしろ問題になるのはゲームの整理方法。PS3 もそうですが、ダウンロードしたゲーム
が増えるとメニューから探し出すのが大変になります。
タグをつけたりフォルダを作ったり、何らかの整理方法が欲しくなります。


関連エントリ
Media Go で PSP のデータ管理
新型 PSP (PSP-2000) とゲームアーカイブス


たまたま SubD を何ステップまで実行できるか、という話になりました。
せいぜい 10 くらい?とか考えていたら、ツール上では 128 までいけたとか!
そんなの想像も付かない、と思ってよくよくきいたら、やはりツール内部で上限値が
設定してあったそうです。

128 ステップだと 1ポリゴンが 10 の 77 乗個に増えると言うこと。
1 辺の長さは 10 の 38 乗 分の 1 。

想像できないので計算してみた。

例えば 1 ポリゴンを 1m 四方とみなして作ったとして、
34 ステップもやればすでに原子レベル、
50 ステップ目では陽子中間子のサイズをも下回ります。

10km 四方の範囲もわずか 60 ステップで原子核に突入です。
この段階ですでに地球丸ごと作っても分子レベルでレンダリングできます。

全然追いつきません。
いっきに 1光年の範囲に広げても桁が 16個減るだけです。
1 光年も 87 ステップでは原子のサイズ。

宇宙の端から端をおおざっぱに半径 500 億光年としても
123 ステップで原子の領域となりました。

よって 1 ポリゴンを再帰的に Subdivision Surface で細分割していったとすると、
128 ステップ実行すれば宇宙全体を原子よりずっと小さいサイズまで砕くことが出来る。

という、そんなどうでもいい話でした。

よく考えると 2^128 って 32bit 浮動小数の指数範囲を超えています。


光学式タッチパネルは些細なことでも反応します。
そばに置いたコンビニの袋の取っ手が画面に触れただけで、スリープが解除されたりもします。

マルチタッチ対応 HP IQ800 を長いこと机の上に置いてますが、
袖が触れたり、書類の端が触れたりすると敏感に反応。
電源が入ったり、気がつくとクリック音が鳴り続けていることがあるのです。

今日の犯人は虫でした。
突然スリープから復帰したと思ったら、よく見たらマウスカーソルが勝手に動いている!
小さな蜘蛛くらいの虫で、ぴったりトレースしてマウスカーソルが付いていく様は面白い。

でもタッチパネルだからクリックされていることに…
場所によっては勝手にダイアログのボタンが押されたり、
勝手にプログラムを起動したり、
勝手にブラウザのリンクに飛んだりする可能性もあるわけですね。
慎重な作業中は 虫 にも注意。

取りあえず今度 ペイント の上を歩かせてみます。


また今更な古い話なので興味ない方は無視してください。(前回)

当時 CG WORLD の 2004 年 9 月号を見た人から、キャラに object space (OS) の
ノーマルマップを使っているのはなぜかと良く聞かれることがありました。
それを思い出したのでちょっと書いてみます。


●理由

Phantom Dust は、シーン全部にほぼノーマルマップまたは同等の重いシェーダーを
適用しています。

Xbox は Shader Model 1.x 世代なので、Pixel Shader は 9~12bit の低精度な
固定小数だし 8 + 4 stage という命令数制限もありました。
この制限の中に必要な機能を詰め込むにはかなり工夫が必要です。

逆に頂点シェーダー側は浮動小数演算だし命令スロット数も比較的余裕はあります。
でも頂点演算はタイトです。シェーダーが 1命令 (0.5 cycle) 増えただけで描画速度に
大きく響きます。

PixelShader
・命令数制限、演算精度が厳しいので、必要な機能を入れるだけでぎりぎり。

VertexShader
・描画速度にかなり影響が生じる。機能的には十分だが速度的には余裕がない。

キャラクタ用シェーダーの頂点は、スキニング処理が入るためただでさえ負荷が高く、
頂点データも index と weight の分だけ多くなります。

 ・頂点のデータ量を減らしたい
 ・演算量を減らして高速化したい。

よって最適化のために、キャラクタ用シェーダーではオブジェクトスペースのノーマル
マップを使っています。
速度だけでなくメモリを大量に消費することもノーマルマップでは大問題でした。


●計算

オブジェクトスペースで作られたノーマルマップは、シェイプの変形に柔軟に追従する
ことが出来ません。
そのため、法線をテクスチャに焼き込んだ形状が保存されない Maya 等のツール上では、
オブジェクトスペースのノーマルマップではアニメーションすることが不可能となります。

しかしながら焼き込んだ形状が保存されていて、かつ変形したジオメトリとの差分が求まるならば、
オブジェクトスペースのノーマルマップから変形後の法線を導き出すことが出来ます。

ボーンによるアニメーションでも全く同じことです。基準となるバインドポーズを持っていて、
ツールから出力されるメッシュはこの形状を保ったスタティックなものです。

スキニングの演算自体が、骨の移動に従った基準形状からの各頂点の変化量を求めていることに
なります。このマトリクスがそのまま使えます。

Blend された Local to World matrix をそのまま使うだけ

Phantom Dust では光源ベクトルを逆変換しているので、頂点で求めた Local To World
Matrix の逆行列 (3x3) を光源に掛けます。
これが各頂点毎の Local 空間 (OS) なので、サンプリングされたオブジェクトスペースの
ノーマルマップを使ったライティングを行えます。
基本姿勢の OS を Os0 とすると、光源を World → Os → Os0 と変換していることに
なります。


頂点に Tangent Space の Matrix を埋め込まなくて済むので、こちらの方がデータ量として
かなりお得です。Tanget Space への変換分だけ演算負荷が減るし、頂点サイズの縮小も
そのまま高速化に繋がります。

ただしオブジェクトスペースでノーマルマップを作った場合は、以後絶対に基本となる
ポーズを変えてはいけません。Export 時に、ツール上でうっかり触ってずれてしまった、
とか許されなくなるわけです。


●ムービーレンダリング

プロジェクト終盤で困ったのがムービーのレンダリングです。
ゲーム内のキャラデータを使ってムービーシーンを作る必要があったのですが、次の理由に
よりそれが困難でした。

 (1) 法線マップのレンダリング方法
 (2) アニメーション変形

(1) 当時の Maya (4.5 あたり) には法線マップ用のレンダリング機能がなかったので、
そのままではレンダリングすることが出来ません。シェーディングノードを複雑に組み合わせて
Matrix 演算を行うことも出来ますが、いろいろ無理があります。

(2) 上でも書いたとおり、オブジェクトスペースのノーマルマップはツール上だと
元の形状からの変化量が求まらないのでアニメーション変形に対応することができません。

結局ムービーレンダリングのために専用のプラグインを作成しました。
プロジェクト終盤ではすでに RADEON 9700 が出ていたので、RADEON + ShaderModel 2.0
相当の OpenGL 機能を使っています。
OpenGL + ARB_vertex_program + ARB_fragment_program を使い、
Maya ビューポートへのリアルタイムプレビュー +
Maya ハードウエアレンダーバッファへのレンダリングを行っています。

確か条件分けは 3通り。

1. TS Normal map ではそのまま何もせずレンダリング可能。アニメーションも OK。

2. OS Normal map でも変形しない物ならそのままレンダリングします。

3. OS かつアニメーションを行う場合、プラグインに対して事前に基本形状を登録する
 必要あり。


3. では登録したメッシュを内部で記録して、Object Space のレンダリング時に参照しています。
これは Transform node (Matrix) ではなくメッシュ自体を丸ごと保存しています。
そのため実機と違い、ボーンアニメーションだけでなくシェイプアニメーションにも対応
出来るようになっています。

これが可能なのは OS だけど各頂点に Tangent Space 用の Matrix を埋め込んでいるから。
変形後の頂点から Tangent Space の Matrix が求まるので、そこから逆変換すれば
OS のノーマルマップから得た法線を TS に持って行くことが出来ます。
つまり頂点が持っている Tangent Space 変換 Matrix は 2つです。

Matrix1:  Ts → Os0
Matrix2:  Ts → Os

Os0 は基本形状の Object Space 、Os はアニメーション後の Object Space とすると
サンプリングした法線は Matrix1 で逆変換後 Matrix2 に適用すればよいことになります。


●背景

背景は Tangent Space (TS) です。地面や壁などバンプマップとしてタイリングするし
同じノーマルマップテクスチャを何度も使い回すからです。
貼られる面も任意となります。


●その後

あくまで昔の話です。キャラデータに OS のノーマルマップを使用したのはこのときだけ。
変形があるとデータの扱いが難しいし、その後のシェーダーでは PixelShader もかなり
高機能化されたので、ここまで考える必要がなくなりました。

Shader Model 2.0~3.0 の描画パイプラインの設計では、全く区別のない両対応をしたものの
結局キャラにも Tangent Space を使っています。

ただしツール内で生成された Tangent Space には互換性が無いので、最終的な
レンダリングプログラムと条件を一致させなければなりません。
結局自前のツールでデータを生成することになります。

Shader Model 4.0 以降は TS のみの対応としました。
シェーダーの基本機能として当たり前に使えるけど、その比重はさらに下がっています。
すでに Direct3D 11 の時代、Shader Model 5.0 ならなおさらでしょう。


関連エントリ
Direct3D ファントムダストと破壊の穴
3D一般 ノーマルマップの互換性問題(2) UVに潜む罠


SD ブートで好きなだけ実験できる環境が出来たので次はカーネルをカスタマイズします。

●仮想マシン

Windows に VirtualBox をインストール。
あらかじめ Ubuntu 9.04 のインストール iso をダウンロードしておく。

VirtualBox で仮想マシンを [新規] 作成

OS タイプ: Linux
バージョン: Ubuntu または Ubuntu (64bit)
(64bit 仮想マシンに 32bit OS を入れてもかまわない)

RAM 適量 (HOST PC の RAM 容量と相談)
可変容量で新規ディスク作成 20GB くらい

仮想マシンが出来たらいったん [設定] を開く

[設定]
→ ネットワーク
  アダプタの割り当てを「NAT」から「ブリッジアダプタ」に変更

→ CD/DVD-ROM
 CD/DVD ドライブのマウント→iso イメージファイル
 仮想メディアマネージャーからダウンロードしておいた ubuntu の iso を選択
  ubuntu-9.04-desktop-i386.iso や ubuntu-9.04-desktop-amd64.iso 等

VirtualBox の「ファイル」→「環境設定」を開いてホストキーの確認
 入力 の中の ホストキー
 必要なら変更しておく。特にキーボードに右 ctrl キーがない場合は要変更。

作成した仮想マシンを起動して ubuntu をインストール。
右 Control キー、または設定したホストキーでマウスが HOST 側に戻る。
パーティショニングは「ディスク全体を使用する」にしておく。


●クロスコンパイル

下記ページを参考にさせていただきました。

ひまじめ 組み込みLInux

「ARMクロス開発環境構築」の手順に従い ARM EABI (armel) 用ツールをインストール。
設定したら gcc-4.3-arm-linux-gnueabi binutils-multiarch を入れます。
クロス開発用ライブラリはここでは不要です。

$ sudo apt-get install gcc-4.3-arm-linux-gnueabi binutils-multiarch libncurses5-dev

次は「ARM用Linuxカーネルをソースからビルドする」を参照します。
libncurses5-dev も install 。

NetWalker のカーネルソースとビルド方法は下記ページを参考にさせていただきました。

Android Zaurusの日記 NetWalkerのカーネルをリビルド

手順に従い上記ページ "*2" の場所から tar.gz をダウンロード。
解説の通りコンパイルしてみます。
CROSS_COMPILE は arm-linux-gnueabi- を指定。
config.gz はあらかじめ NetWalker の /proc からコピーしておく。

$ tar -zxvf linux-fsl-imx51_2.6.28-15.50fsl1araneo7.tar.gz
$ zcat config.gz > jaunty-arm/arch/arm/configs/netwalker_defconfig
$ cd jaunty-arm
$ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi-  netwalker_defconfig
$ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi-  menuconfig

menuconfig でオプション確認と変更
以前作った 「[Ctrl] と [A] の併用化」 を動かすなら下記を enable に
( space キーを押して '<*>' にする)

Input device support --->
  Miscellaneous devices --->
    <*> User level driver support


$ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi-  zImage modules

できあがった arch/arm/boot/zImage を 起動確認用 micro SD の /boot に
ファイル名 zImage-10 としてコピー
/boot/boot.conf の最初のパスを zImage-10 に変更
microSD からの起動を確認。

あとはカーネルの設定を変更しつついろいろテストできます。

[Ctrl] と [A] の併用化 を試すなら

# wget http://dench.flatlib.jp/arfiles/ctrla.tgz
# tar -zxvf ctrla.tgz
# cd ctrla
# mknod /dev/uinput c 10 223
# ./ctrla /dev/input/event5

動きました。左手小指を使って Ctrl-H とか Ctrl-U とか使えています。
長かったけど、この設定を常用化できれば自分としては目的達成です。


関連エントリ
NetWalker PC-Z1 Debian (2)
NetWalker PC-Z1 Debian


容量増やして NetWalker の Debian に gdm, gnome を入れてみました。
入れてみただけでまだいろいろ問題があります。
取りあえず SD からの起動で環境を切り替えを行うテストなので。

netwalker_d04.jpg

オプティカルポイントの左右ボタンでクリックがきかないのに、ドラッグでウィンドウの移動はできる。
クリックしつつカーソルをわずかに動かすとボタンも反応する。
タッチパネルは補正されてないので位置がばらばら。
USB の外付けマウスだと普通に使えます。

netwalker_d03.jpg

起動直後の gdm でキー入力できない場合あり。
ネット経由で gdm を restart するか、しばらくして gdm が勝手に起動し直せば直るようです。


関連エントリ
NetWalker PC-Z1 Debian


ATI Stream SDK v2.0 beta4 がリリースされ、OpenCL も GPU で動作するようになりました。

ATI Stream Software Development Kit (SDK) v2.0 Beta Program

以前 こちら で書いたとおり、これまでは OpenCL の対象デバイスは CPU のみ列挙され、
GPU が使われることはありませんでした。

今回のアップデートでは GPU に対応しており、やっと本来の姿になったといえます。
サポート対象は基本的に Radeon 4000/5000 シリーズ。
Windows7 にもインストールできます。


OpenCL はすでに MacOSX や GeForce で動いており、GPU 対応もあまり目新しいものでは
無いかもしれません。でも HD 5000 があれば Direct3D 11, Compute Shader 5.0,
OpenCL 1.0 と新しい API を全部試すことが出来るので、ボードの抜き差し回数が減りそうです。
唯一 OpenGL 3.2 対応はまだのようです。

OpenCL 対応 ATI Stream SDK を使うにはドライバのアップデートも必要です。
RADEON HD 5870/5850 のドライバは 8.66 から 8.67 に更新されました。

下記は 以前 と同じプログラムで表示したもの。
DEVICE の列挙は 2つあり、上は CPU です。下が RADEON HD 5850 です。
COMPUTE UNIT が 18 個とありますが、5870 ではここが 20 でした。

CL_PLATFORM_PROFILE            = FULL_PROFILE
CL_PLATFORM_VERSION            = OpenCL 1.0 ATI-Stream-v2.0-beta4
CL_PLATFORM_NAME               = ATI Stream
CL_PLATFORM_VENDOR             = Advanced Micro Devices, Inc.
CL_PLATFORM_EXTENSIONS         = 

// Core i7 920
CL_DEVICE_TYPE = 2      // CL_DEVICE_TYPE_CPU
CL_DEVICE_VENDOR_ID = 4098
CL_DEVICE_MAX_COMPUTE_UNITS = 8
CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS = 3
CL_DEVICE_MAX_WORK_ITEM_SIZES = 1024 1024 1024
CL_DEVICE_MAX_WORK_GROUP_SIZE = 1024
CL_DEVICE_EXECUTION_CAPABILITIES = 1

// RADEON HD 5850
CL_DEVICE_TYPE = 4      // CL_DEVICE_TYPE_GPU
CL_DEVICE_VENDOR_ID = 4098
CL_DEVICE_MAX_COMPUTE_UNITS = 18  // 5870 では 20
CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS = 3
CL_DEVICE_MAX_WORK_ITEM_SIZES = 256 256 256
CL_DEVICE_MAX_WORK_GROUP_SIZE = 256
CL_DEVICE_EXECUTION_CAPABILITIES = 1


関連エントリ
ATI Stream SDK v2.0 beta4 OpenCL
DirectX 11 / Direct3D 11 と RADEON HD 5870 の caps
OpenCL の vector 型
ATI Stream SDK 2.0 beta と OpenCL


GPU に対応したようです。
RADEON 58 だけでなく 4000 系も含まれています。

ATI Stream Software Development Kit (SDK) v2.0 Beta Program


カーネルの再構築をテストするために SD からの boot を少々試しました。

取りあえずリカバリ microSD の中身を書き換えるだけで起動します。
/boot だけ残して本体フラッシュの中身をまるごとコピーするだけでも ubuntu が
SD から立ち上がるようです。カーネルのテストだけならこれで十分かもしれません。

手持ちが 2GB SD しかなく、ただのコピーだと入りきらなかったので Debian 入れてみました。
下記ページを参考にしました。ほぼこの手順通りに作業しています。

D.3. Unix/Linux システムからの Debian GNU/Linux のインストール

最初にリカバリ用 micro SD を作成しており、そこから /boot 以外全部削除しています。
NetWalker がネットに繋がった状態です。

$ sudo -s
# apt-get update 
# apt-get install debootstrap 

/media/disk の microSD を /mnt/sd にマウントし直す

# umount /media/disk
# mkdir /mnt/sd
# mount /dev/mmcblk0p1 /mnt/sd

install CD-1 の iso をあらかじめ download してあるなら

# mkdir /mnt/cd
# cd
# mount -t iso9660 -o loop debian-503-armel-CD-1.iso /mnt/cd
# debootstrap --arch armel lenny /mnt/sd file:/mnt/cd

またはネットから実行する場合 ( http://www.debian.org/mirror/list)

# debootstrap --arch armel lenny /mnt/sd  http:/ftp.~.org/debian/

モジュールをコピーしておく

# cp -r /lib/modules/* /mnt/sd/lib/modules

手順通りです

# LANG=C chroot /mnt/sd /bin/bash
# mount -t proc proc /proc
# cd /dev
# MAKEDEV generic
# dpkg-reconfigure tzdata

# vi /etc/hosts

127.0.0.1 localhost netwalker

# vi /etc/network/interfaces

auto lo
iface lo inet loopback
auto eth0
iface eth0 inet dhcp
auto eth1
iface eth1 inet dhcp

# aptitude update 
# aptitude install locales
# dpkg-reconfigure locales

起動してみます。

# reboot

SHARP ロゴがでる前から、オプティカルポイントの左右ボタン (本体左側にあるマウスボタン)
の両方を押しっぱなしにします。そのまましばらく耐え続けて、うまく起動すると SHARP ロゴを
突き破ってコンソールが出現。root でログイン。

netwalker_d01.jpg

無線 LAN で繋がらないので、手持ちの USB LAN アダプタを試したら両方とも使えました。
USB1.1 のかなり古いものです。

corega FEther USB-TX
IO DATA Lan-Egg USB-ET/T

# /etc/init.d/networking restart

これで ssh を入れればリモートから作業できるようになりました。
コンソールだとカーソルが表示されないのでエディタの編集で少々困ります。

X を入れてみます。

# aptitude install xserver-xorg-video-fbdev
# aptitude install xinit

xorg.conf を書き換えて Section "Device" のところに Driver "fbdev" を追加。

# vi /etc/X11/xorg.conf

~
Section "Device"
        Identifier      "Configured Video Device"
        Driver       "fbdev"
        Option          "UseFBDev"      "true"
EndSection
~

startx で取りあえず起動することがわかったので

# aptitude install xterm fvwm

# vi /root/.xinitrc

xterm &
exec fvwm

起動します。

# startx

何も入っていません。

netwalker_d02.jpg

マウスボタンの同時押しを失敗しやすいのが難点ですが、micro SD から割と簡単に
起動できることがわかりました。


以前 キーボードのカスタマイズ でやりたかったのはこれです。
横に長い NetWalker の [A] キーを Ctrl キーと兼用にしています。

そのままキータイプした場合は文字 'A' が入力され、[A] を押したまま他のキーを押すと
Ctrl キー操作になります。

欠点は
 ・Ctrl-A の入力ができない
 ・文字 'A' キーのリピートができない
 ・[A] キーを離したときに文字 'A' が入力されるので表示が遅れる

Ctrl-A の入力には本物の [Ctrl] キーを使う必要があります。
[A] + [F] (Ctrl-F) など、コントロールキーとして機能する場合はリピートできます。
[A] キーは遅延判定になるので表示が遅れますが連続したキー入力には影響は出ません。


NetWalker の場合そのままでは uinput が使えないので、次のどちらかの手段が必要です。

  (1) カーネルを入れ替えて uinput が使えるようにする。
  (2) uinput の代わりに別のキーボードをつないで代用する。

(1) はまだ試していないので、本当に使えるかどうか確認していません。
他の PC の Linux 上では一応 uinput を使ってテストしています。

(2) の場合 USB の外付けキーボードをつないで代わりの出力先として流用します。
常に USB のキーボードがぶら下がっていることになるので全く実用的ではないです。


取りあえず標準状態の NetWalker で走らせるには (2) の方法を使います。
USB キーボードをつないだ状態で下記のように実行します。

 $ sudo ./ctrla -u /dev/input/event7 /dev/input/event5

プログラム ctrla.tgz v1.00
プログラム ctrla110.tgz v1.10

●アルゴリズム

[A] を押し続けている間に他のキーを押して離すと Control キーと見なします。
いったん Control キーと認識されると [A] キーを離すまで状態が続きます。
それ以外のキーストロークはバッファリングした後、判断可能な状態になったら文字
として出力します。

他にも一定時間 [A] を押し続けると Control と判定するとか、短い時間で先に [A]
より他のキーを離したときは文字と見なすとかいろいろ試しましたが、結局シンプルな
この方法が自然に扱えました。


関連エントリ
NetWalker PC-Z1 設定とキーの打ち方
NetWalker PC-Z1 と親指シフト入力
NetWalker PC-Z1 Bluetooth とキーカスタムその他
NetWalker PC-Z1 意外にいける
Netwalker PC-Z1 買いました


前回説明したとおり、Direct3D 11 の Pixel Shader 5.0 は UAV (Unordered Access View)
を使った書き込みが出来ます。
UAV を RTV (Render Target View) の代わりに用いる利点は次の通り。

(1) 書き込み座標を任意に指定できる
(2) 読み込める

本来 Render Target に書き込む座標はラスタライズ時に決定します。
このスクリーン座標値は Shader Model 5.0 (ps_5_0) の場合 SV_Position で受け取る
ことができます。

UAV は書き込むアドレスを直接指定できるため、ラスタライズ座標以外の場所に点を打てるし
一度に複数の点を書き込むことも可能です。
Geometry Shader がプリミティブの単位で座標をいじれたり増やしたり出来るのに似ています。

例えばスクリーン座標で左右反転したり、画像を複製したり。

dx11_uav_blend01.jpg

↑これは実際に 1枚のプリミティブを書き込んでいますが、Pixel Shader 内で複製して
100 ピクセル離れた場所にも同時に書き込んでいます。
わかりにくいけど、半透明の背景が同じものになっています。

(2) は現在書き込んでいるフレームバッファの値を事前に読み取って演算出来ることを
意味しています。うまく活用すれば Alpha Blend を自由にシェーダーでプログラム
出来るかもしれません。

結果だけ述べると、さすがに少々無理があったようです。

dx11_uav_blend03.jpg

↑ちらつき

この 4枚のポリゴンは一度の Draw() で描画しています。
Reference Device では期待通り動きますが、RADEON HD 5870 の場合は自分自身との
重なりなど、直前に書き込んだピクセルの値が反映される場合とされない場合があります。
おそらく書き込みバッファがフラッシュされる前に読み込んでいるのだと思います。
プリミティブが大きくて一度の描画面積が大きい場合はおそらくバッファ容量を超えているため
うまく動いているように見えます。

dx11_uav_blend02.jpg

↑自分自身との合成でもうまくいくシーン


以下実際に試したプログラムと解説。

フレームバッファ用のテクスチャを DXGI_FORMAT_R32_FLOAT で作っておきます。
SRV, RTV, UAV 全部作ります。

R8G8B8 のカラー値を変換して 32bit 整数値にして R32_FLOAT に書き込んでいます。
asfloat()/asint() を使っているため実質 R32_UINT でも同じです。

モデルデータを描画する場合

1. フレームバッファの値を読み込む
2. 自前でブレンド計算する
3. UAV に書き込む

最後に R32_FLOAT で書き込まれたフレームバッファを RGB に戻して描画します。


モデルを描画するシェーダー。四角形 1枚のみ。
// draw.hlsl
struct VS_INPUT {
    uint    vIndex    : SV_VertexID;
};

struct VS_OUTPUT {
    float4  vPos      : SV_Position;
    float2  vTexcoord : TEXCOORD;
};

struct PS_INPUT : VS_OUTPUT {
    float2  uv  : TEXCOORD;
};

cbuffer g_buf : register( c0 ) {
    float4x4    ViewProjection;
};

// 頂点は VS で生成
VS_OUTPUT vmain_Plane( VS_INPUT vin )
{
    float4  vlist[4]= {
        {   -1.0f,  1.0f, 1.0f,  1.0f    },
        {    1.0f,  1.0f, 1.0f,  1.0f    },
        {   -1.0f, -1.0f, 1.0f,  1.0f    },
        {    1.0f, -1.0f, 1.0f,  1.0f    },
    };
    float2  texlist[4]= {
        {    0.0f,  0.0f,    },
        {    1.0f,  0.0f,    },
        {    0.0f,  1.0f,    },
        {    1.0f,  1.0f,    },
    };
    VS_OUTPUT   vout;
    float4  pos= vlist[ vin.vIndex ];
    vout.vPos= mul( pos, ViewProjection );
    vout.vTexcoord= texlist[ vin.vIndex ];
    return  vout;
}

// パックされたカラーを展開する
float3 pf_to_float3( float pf )
{
    uint    data= asint( pf );
    float3  rcol;
    const float tof= 1.0f/255.0f;
    rcol.x= ( data      & 255) * tof;
    rcol.y= ((data>> 8) & 255) * tof;
    rcol.z= ((data>>16) & 255) * tof;
    return  rcol;
}

// カラーを float1 に圧縮する
float float3_to_pf( float3 color )
{
    uint3   bcol= (uint3)( color * 255.0f ) & 255;
    return  asfloat( (bcol.z << 16) + (bcol.y << 8) + bcol.x );
}

SamplerState    sample0 : register( s0 );
Texture2D<float>    tex0  : register( t0 );
RWTexture2D<float>  tex1  : register( u1 );
Texture2D<float4>   tex2  : register( t1 );

// UAV へレンダリング
float4 pmain_Plane( PS_INPUT pin, float4 fscrpos : SV_POSITION ) : SV_TARGET
{
    // 書き込むデータのテクスチャ
    float3  data= tex2[ pin.uv ];

    // フレームバッファの座標
    int2    scrpos= (int2)( fscrpos.xy );

    // フレームバッファの内容を読み出す
    float3  scrdata= pf_to_float3( tex1[ scrpos ] );

    // 適当にブレンドしてみる
    data= saturate( data * 0.7f + scrdata * 0.3f );

    // UAV へ書き込んでいる
    tex1[ scrpos.xy + int2( 100,  0 ) ]= float3_to_pf( data );
    tex1[ scrpos.xy + int2(   0,  0 ) ]= float3_to_pf( data );

    // RTV へは出力しない
    return  float4( 0,0,0,0 );
}

draw.hlsl の続き

VS_OUTPUT vmain_Render( VS_INPUT vin )
{
    float4  vlist[4]= {
        {   -1.0f,  1.0f, 0.0f,  1.0f    },
        {    1.0f,  1.0f, 0.0f,  1.0f    },
        {   -1.0f, -1.0f, 0.0f,  1.0f    },
        {    1.0f, -1.0f, 0.0f,  1.0f    },
    };
    float2  texlist[4]= {
        {    0.0f,  0.0f,    },
        {    1.0f,  0.0f,    },
        {    0.0f,  1.0f,    },
        {    1.0f,  1.0f,    },
    };

    VS_OUTPUT   vout;
    vout.vPos= vlist[ vin.vIndex ];
    vout.vTexcoord= texlist[ vin.vIndex ];
    return  vout;
}


// フレームバッファの値は圧縮されているので展開する
float4 pmain_Render( PS_INPUT pin ) : SV_TARGET
{
    float2  size;
    tex0.GetDimensions( size.x, size.y );
    int2    loc= (int2)( pin.uv * size );
    float3  data= pf_to_float3( tex0[ loc ] );
    return  float4( data, 1.0f );
}

C言語側。リソースの解放、BG の描画は省略しています。
CreateShader(), LoadTexture() の中身も略。

ID3D11Device* iDevice= NULL;

struct BufferSet {
    ID3D11Texture2D*            iTexture;
    ID3D11ShaderResourceView*   iSRV;
    ID3D11RenderTargetView*     iRTV;
    ID3D11UnorderedAccessView*  iUAV;
public:
    BufferSet() :
        iTexture( NULL ),
        iSRV( NULL ),
        iRTV( NULL ),
        iUAV( NULL )
    {
    }
    void Create( int width, int height, DXGI_FORMAT buffer_format );
    void Release();
};

void BufferSet::Create( int width, int height, DXGI_FORMAT buffer_format )
{
    // Texture   CD3D11_ ~ は D3D11.h に定義されている
    CD3D11_TEXTURE2D_DESC   tdesc( buffer_format, width, height, 1, 1,
            D3D11_BIND_SHADER_RESOURCE
                |D3D11_BIND_RENDER_TARGET
                |D3D11_BIND_UNORDERED_ACCESS,
            D3D11_USAGE_DEFAULT, 0, 1, 0, 0  );
    iDevice->CreateTexture2D( &tdesc, NULL, &iTexture );

    // ShaderResourceView も作る
    CD3D11_SHADER_RESOURCE_VIEW_DESC    vdesc(
                D3D11_SRV_DIMENSION_TEXTURE2D, buffer_format, 0, 1, 0, 1, 0 );
    iDevice->CreateShaderResourceView( iTexture, &vdesc, &iSRV );

    // RenderTargetView も作る
    CD3D11_RENDER_TARGET_VIEW_DESC   rdesc(
                D3D11_RTV_DIMENSION_TEXTURE2D, buffer_format, 0, 0, 1 );
    iDevice->CreateRenderTargetView( iTexture, &rdesc, &iRTV );

    // UnorderedAccessVieww も作る
    CD3D11_UNORDERED_ACCESS_VIEW_DESC  uodesc( iTexture,
                D3D11_UAV_DIMENSION_TEXTURE2D, DXGI_FORMAT_UNKNOWN, 0, 0, 1 );
    iDevice->CreateUnorderedAccessView( iTexture, &uodesc, &iUAV );
}

BufferSet Screen;
ID3D11DeviceContext* iContext= NULL;
ID3D11VertexShader* iVS_Render= NULL;
ID3D11PixelShader*  iPS_Render= NULL;
ID3D11VertexShader* iVS_Plane= NULL;
ID3D11PixelShader*  iPS_Plane= NULL;
ID3D11ShaderResourceView*  iInputImage= NULL;
ID3D11ShaderResourceView*  iBGImage= NULL;

void Initialize()
{
    Screen.Create( ScreenWidth, ScreenHeight, DXGI_FORMAT_R32_FLOAT );

    iVS_Render= CreateShader( "draw.hlsl", "vs_5_0", "vmain_Render" );
    iPS_Render= CreateShader( "draw.hlsl", "ps_5_0", "pmain_Render" );

    iVS_Plane= CreateShader( "draw.hlsl", "vs_5_0", "vmain_Plane" );
    iPS_Plane= CreateShader( "draw.hlsl", "ps_5_0", "pmain_Plane" );

    iInputImage= LoadTexture( "plane.bmp" );
    iBGImage= LoadTexture( "bgimage.bmp" );
}

// モデル描画
void Draw()
{
    CD3D11_VIEWPORT viewp( 0.0f, 0.0f, ScreenWidth, ScreenHeight );
    iContext->RSSetViewports( 1, &viewp );

    // ブレンドするので Zバッファは使わない
    iContext->OMSetDepthStensilState( iZDisable, 0 );

    // Unordered Access View を設定する。RTV は NULL
    ID3D11RenderTargetView* ZERO_RTV= NULL;
    iContext->OMSetRenderTargetsAndUnorderedAccessViews(
            1,      // NumViews
            &ZERO_RTV,
            NULL,   // depth buffer
            1,      // UAVStartSlot
            1,      // NumUAVs
            &Screen.iUAV,
            NULL
        );

    iContext->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP );
    iContext->VSSetShader( iVS_Plane, NULL, 0 );
    iContext->PSSetShader( iPS_Plane, NULL, 0 );
    iContext->PSSetShaderResources( 0, 1, &iInputImage );
    iContext->PSSetSamplers( 0, 1, &iSampler );
    iContext->Draw( 4, 0 );
}

// フレームバッファ (Screen) の内容を実際に描画する。
// 本来のフレームバッファに転送
void Flush()
{
    CD3D11_VIEWPORT viewp( 0.0f, 0.0f, ScreenWidth, ScreenHeight );
    iContext->RSSetViewports( 1, &viewp );

    iContext->OMSetDepthStensilState( iZDisable, 0 );
    iContext->OMSetRenderTargets( 1, &iDefaultRenderTarget, NULL );
    iContext->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP );
    iContext->VSSetShader( iVS_Render, NULL, 0 );
    iContext->PSSetShader( iPS_Render, NULL, 0 );
    iContext->PSSetShaderResources( 0, 1, &Screen.iSRV );
    iContext->Draw( 4, 0 );
}


関連エントリ
Direct3D 11 / DirectX 11 UnorderedAccessView と RenderTarget の関係
DirectX 11 / Direct3D 11 と RADEON HD 5870 の caps
Direct3D11/DirectX11 (7) テセレータの流れの基本部分
その他 Direct3D 11 関連


DirectX10 で大きく仕様が変わったのは、より汎用化を進めるためでした。
つまり専用の機能が無くなったということ。
ライティングのための機能、マテリアルの設定などといった目的別の API は
完全に姿を消しています。

では CPU のように完全に何にでも使える、単純でフラットな空間が得られたのかと言えば必ずしも
そうではなく、特にリソースとバッファの扱いはかえって複雑になった印象を受けます。

汎用化や何にでも使えることが、そのまま単純化に繋がるとは限らないためです。
複数の機能の要素をマージしたこと、目的別の機能の代わりにより抽象的な用語や仕様が
増えたことがその要因といえるかもしれません。

DirectX11 はさらに CompteShader が使えるようになり、書き込み可能なバッファが
追加されています。

以下 FEATURE_LEVEL 11.0、 ShaderModel 5.0 を対象としています。


●リソース

Direct3D 11 では View も ShaderObject も種類が増えており何を見て良いのか
わからなくなります。
でもリソースを作る手段は二つだけ。CreateBuffer() か CreateTexture~() です。
Constant も Buffer だし、Vertex や Index もただの Buffer です。


● View

リソースをどう扱うか、それを決めるのが View です。
同じリソースを複数の View を通して見ることが可能で、テクスチャとして参照したり
レンダリングしたり、データとして読み書きするなど流用が可能となります。

View は次の 4種類だけです。

・読み込みのための View、テクスチャやバッファなど
  ShaderResourceView (SRV)

・Merger による書き込みのための View
  RenderTargetView (RTV)
  DepthStencilView (DSV)

・読み書きのための View
  UnorderedAccessView (UAV)

VertexBuffer/IndexBuffer/ConstantBuffer は View を用いずに設定します。


●タイプとシェーダーオブジェクト

データをアクセスする方法を決定するのが、バッファ作成時の細かい指定と
DXGI フォーマットタイプです。

Texture
Buffer
ByteAdddressBuffer
StructuredBuffer
AppendSturctutedBuffer
ConsumeStructuredBuffer
RWTexture
RWBuffer
RWSturcturedBuffer
RWByteAddressBuffer

これらのシェーダーオブジェクトは、シェーダーがアクセスする方法を決定しています。
シェーダー側の宣言だけではだめで、リソース生成時にもいろいろとフラグを指定
しておく必要があります。


● UnorderedAccessView

RW がついているものと AppendSturctutedBuffer/ConsumeStructuredBuffer は
書き込み可能なシェーダーオブジェクトです。
これらは UnorderedAccessView (UAV) を使います。

UAV が使えるのは PixelShader と CompteShader だけ。
Compte Shader は Output Merger が無いので、出力のために必ず何らかの UAV を
用いることになります。

実際に使ってみると、出力はすべて UnorderedAccessView に相当することがわかってきます。
つまり RenderTarget も出力先アドレスが固定された UnorderedAccessView 相当だということです。

UAV の設定 API は次の通り

CompteShader → CSSetUnorderedAccessViews()
PixelShader  → OMSetRenderTargetsAndUnorderedAccessViews()

OMSetRenderTargetsAndUnorderedAccessViews() で一見何のために存在する
パラメータなのかわかりにくいのが UAVStartSlot です。
これは RenderTarget との衝突を避けるためのものでした。

例えば RenderTarget を 2 枚設定した場合は、UnorderedAccessView は必ず
番号 2 から始めなければなりません。

下記のようなリソースを登録する場合

RenderTarget x2
UnorderedAccessView x3

OMSetRenderTargetsAndUnorderedAccessViews() には次のようなパラメータを
渡します。

NumViews = 2          // RTV (RenderTargetView) の個数
UAVStartSlot = 2      // UAV (UnorderedAccessView) の開始番号 == NumViews
NumUAVs = 3           // UAV の個数

※ August 2009 のマニュアル OMSetRenderTargetsAndUnorderedAccessViews()
の引数は若干間違いがあります。ヘッダファイルを見た方が正しい宣言です。

シェーダーレジスタはこのように割り当てられます。

o0 = RenderTarget
o1 = RenderTarget
u2 = UnorderedAccessView
u3 = UnorderedAccessView
u4 = UnorderedAccessView

つまり o レジスタと u レジスタは同時に同じ番号を使うことが出来ず、
使用可能な View はあわせて 8 個まで。この両者ほぼ同じ扱いです。

UnorderedAccessView は読み書き可能なバッファです。
よく考えると RenderTarget や DepthBuffer と同じこと。
UAV は読み込んだあとにブレンドしたり、判定した結果をまた書き戻すことが出来るわけです。
そのパス内で更新できる OM をプログラムできるなら、いろいろ面白いことが出来そうです。

実際試してみたらエラーで、RW で読み込めるのは sngle コンポーネントに限ると言われます。
時間がなかったので、このあたりは後ほどまた詳しく調べてみます。

昔わからないと書いた CSSetUnorderedAccessViews() の最後のパラメータは
Append/Consume buffer に関するものでした。
やはり当時のサンプルの使い方が間違っていたのだと思われます。


関連エントリ
DirectX 11 / Direct3D 11 と RADEON HD 5870 の caps
Direct3D11/DirectX11 (7) テセレータの流れの基本部分
その他 Direct3D 11 関連


Ubuntu のデスクトップ環境はよくできています。
設定とかウィンドウ上のダイアログだけで済んでしまうし、普通に使っている分には
Windows とあまり変わらない印象です。

もちろんコマンドシェルを使った方がやれることが多いだろうし、以前から Linux や Unix 系
OS を使っている方ならコマンドを打ち込んだ方が早いかもしれません。
そのため古くから使っている人だけでなく、ネット上に蓄積されている情報もコマンドで作業する
ものが多くなっています。
今後誰でも使える環境を目指すなら、コマンドに頼らない GUI だけで活用するノウハウも
必要になってくるのではないでしょうか。


● VPN の設定

通知スペースにある 無線LAN のアイコンをクリックすると「VPN 接続」という項目があります。
そのまま設定できそうですができませんでした。何らかのマネージャーを登録する必要があるようです。

ここでは PPTP を使っています。

・アプリケーションの追加と削除から PPTP で検索
・PPTP VPN Connection Manager が見つかったのでインストール
 (チェックボックスにチェックを入れて、変更の適用)

これで通知スペースのネットワークアイコンから「VPN 接続」を選べるようになりました。

・VPN の設定から [追加]
・VPN 接続タイプで PPTP を選択して [作成]
・必要な項目を書き込む

「VPN 接続」のメニューに書き込んだ項目が追加されました。
これでうまくいきそうに見えますがつながりません。

エラーが出ているようですが、右上の通知も一瞬で消えるので原因もよくわからないまま。
ログアウトして再ログインしてもだめでした。

しばらく悩んだものの、結局再起動したらあっさりつながりました。
ネットワークアイコンにもログイン状況を示すアニメーションや鍵マークが表示されて
VPN で繋がっています。


●クイックスタートボタンを無効化する

本体支える場所が少ないのでクイックスタートボタンをよく触ってしまいます。
下記の設定で無効化できることがわかりました。

設定 → キーボード・ショートカット → デスクトップ →「Run a defined commnd」

XF86HomePage, XF86WWW, XF86Mail, 0xca と書かれているのがそれぞれ
HOME, Web, Mail, ☆ ボタンに相当するようです。
個別に選択して [BS] キーを押すと「無効」になります。
設定を変更したら、いったんログアウトして再ログインし直すと反映されます。
元に戻すには、個別に選択して対応するキーを押せば OK です。


● Flash プラグイン

ブラウザが落ちやすくなったので結局無効にしてしまいました。
ツール → アドオン → プラグイン → Shockwave Flash 無効化


●片手だけタッチタイプで使う

(1) 両手とも5本指

テーブルに置いた状態では、両手でキーボードをたたいています。
パソコンと同じようにホームポジションに手を置きながら。
オプティカルポイントは人差し指で操作です。

(2) 親指のみ

電車の中など立って使う場合は、両手で持って親指でオプティカルポイントを操作しています。
この場合キーボードも親指打ちになります。

でもこれだと中央のキーを打ちづらいしあまり速く打てないので、文章を打つ場合は
右手だけ 5本指を使うようになりました。

(3) 親指+5本指

左手は本体のホールド兼用で、左側のキーを親指で打ちます。
右手はホームポジションに乗せて、そのまま (1) と同じようにタッチタイプします。
オプティカルポイントは人差し指や中指で。

この打ち方のメリットは右手側のキーだけ見なくても速く打てることと、左手の親指が届かない
真ん中のキーを右手でカバーできることです。
デメリットは本体を支える左手が疲れること。
左右逆でもよいかもしれません。


●NetWalker

個人的にはタッチタイプできる絶妙なサイズのキーボードで、予想に反して手放せないものと
なりつつあります。

親指打ちに特化されたキーボードだと、上のような組み合わせた持ち方&打ち方は出来ないし、
これ以上キーボードが大きくなるとおそらく本体も重くて片手で支えられなくなります。
複数の打ち方を使い分けられる別のジャンルと割り切ってよいのではないでしょうか。
Netbook が買える価格帯であることを考えると決して万人には勧められませんし、それぞれ
個別に見ていくと中途半端に見えるかもしれません。
だけど今まで無かったこの中途半端なサイズを求める人にはぴったりかもしれません。

今後キーの質が改良されるのは望むべきことですが、サイズとか他のところはあまり大きく
変えないで欲しいというのが今の感想です。


関連エントリ
NetWalker PC-Z1 と親指シフト入力
NetWalker PC-Z1 Bluetooth とキーカスタムその他
NetWalker PC-Z1 意外にいける
Netwalker PC-Z1 買いました


RADEON HD 5870 手に入れました。
とりあえず各機能の対応状況は以下の通り。(caps viewer より)

Direct3D 11
   Feature Level                   D3D_FEATURE_LEVEL_11_0
   Driver Concurrent Creates       No
   Driver Command Lists            No
   Double-precision Shaders        Yes
   Compute Shader 4.x              Yes
D3D_FEATURE_LEVEL_11_0
   Shader Model                    5.0
   Geometry Shader                 Yes
   Stream Out                      Yes
   Compute Shader                  Yes
   Hull & Domain Shaders           Yes
   Texture Resource Arrays         Yes
   Cubemap Resource Arrays         Yes
   BC4/BC5 Compression             Yes
   BC6H/BC7 Compression            Yes
   Alpha-to-coverage               Yes
   Extended Formats (BGRA, etc.)   Yes
   10-bit XR High Color Format     Yes

ShaderModel 5.0 も、ComputeShader も、BC6H/BC7 も、Yes です。
去年末くらいに実験していたテセレータ周りのプログラムもそのまま動きました。
Compute Shader 4~5 も動いてます。OpenCL の方は不明。

唯一 Thread 周りが未対応で、Concurrent Creates と Command Lists が No に
なっています。ドライバがまだ完全ではないのかもしれません。
使用したドライバは Radeon_HD5800_8.66RC6_Vista_Win7_Sep21 。

倍精度演算のコンパイルを試しました。hlsl の出力は下記の通り。

cs_5_0
dcl_globalFlags refactoringAllowed | enableDoublePrecisionFloatOps
dcl_uav_structured u0, 4
dcl_input vThreadIDInGroupFlattened
dcl_input vThreadGroupID.xy
dcl_input vThreadIDInGroup.xy
dcl_input vThreadID.xy
dcl_temps 2
~
add r0.y, r0.z, r0.y
add r0.y, r0.w, r0.y
ftod r0.zw, r0.y
dmul r0.zw, d(0.000000, 100000.000000), r0.zwzw
dadd r0.zw, d(0.000000, 0.000000), r0.zwzw
imul null, r1.xy, l(10000, 1000, 0, 0), vThreadIDInGroup.xyxx
utof r1.xy, r1.xyxx
ftod r1.xyzw, r1.xyxx
dadd r0.zw, r0.zwzw, r1.xyxy

d がついているのが double 命令です。
倍精度演算はレジスタを 2 個使って表現していることがわかります。
レジスタのペアを {} で表現すると

ftod r0.zw, r0.y
  ↓
r0.{zw} = FloatToDouble( r0.y )


utof r1.xy, r1.xyxx
ftod r1.xyzw, r1.xyxx
dadd r0.zw, r0.zwzw, r1.xyxy
  ↓
r0.x = UIntToFloat( r1.x )
r0.y = UIntToFloat( r1.y )
r1.{xy} = FloatToDouble( r0.x )
r1.{zw} = FloatToDouble( r0.y )
r0.{zw} = r0.{zw} + r1.{xy}

でも実際に走らせると落ちました。
単純な加算や型変換だけなら動くのですが、まだ何らかの問題があるようです。


関連エントリ
RADEON HD 5870 と DirectX 11
Direct3D11/DirectX11 (18) GPU を使ったアウトラインフォントの描画の(6)


ARM も x86 と同じように、これまで様々な命令セットの拡張が行われてきました。
例えば乗算一つとっても多数の命令が存在しており、どれを使えばよいかわからなくなる
ことがあります。
x86 に FPU と SSE 命令が共存しているように、ARM にも VFP や NEON 命令があります。
どちらの命令を使っても、単精度の浮動小数演算が可能です。

Cortex-A8 (ARM v7) の場合: 浮動小数演算
VFP    低速  倍精度演算対応  IEEE754 準拠
NEON   高速  単精度のみ      SIMD

NEON 命令は VFP 命令も混在しており区別が付きにくいことから、それぞれ演算速度を
調べてみました。

下記の表は気になる命令のみピックアップして調べたものです。
4 G 個 (40億個) の命令を実行した場合の時間の実測値です。

01:  vld1.32  {d[0]},[r]     20.16 sec *1
02:  vld1.32  {d[0]},[r]     15.14 sec *2
03:  vldr.32  s,[r]          15.06 sec *1
04:  vldr.32  s,[r]          10.04 sec *2
05:  vldr.64  d,[r]          10.18 sec
06:  vdup.32  d,r             5.90 sec
07:  vdup.32  q,r             5.90 sec
08:  vdup.32  d,d[0]          5.02 sec
09:  vdup.32  q,d[0]          5.02 sec
10:  vmov.32  d[0],r         15.10 sec *1
11:  vmov.32  d[0],r         10.08 sec *2
12:  vmov.32  r,d[0]          7.03 sec
13:  vmov  d,r,r             11.79 sec
14:  vmov  r,r,d             12.05 sec
15:  vcvt.s32.f32  d,d        5.01 sec
16:  vcvt.s32.f32  q,q       10.03 sec
17:  vcvt.f32.s32  s,s       45.30 sec VFP
18:  vcvt.f32.s32  d,d        3.76 sec
19:  vcvt.f32.s32  q,q       10.04 sec
20:  vadd.f32  d,d,d          5.02 sec
21:  vadd.f32  q,q,q         10.04 sec
22:  vmla.f32  d,d,d          5.02 sec
23:  vmla.f32  q,q,q         10.04 sec
24:  vrecpe.f32  d,d          5.02 sec
25:  vrecpe.f32  q,q         10.10 sec
26:  vrecps.f32  d,d,d        5.08 sec
27:  vrecps.f32  q,q,q       10.09 sec

r = ARM レジスタ r0~
s = VFP レジスタ s0~s31
d = NEON/VFP レジスタ d0~d31
q = NEON レジスタ q0~q15

*1 = インターリーブ無し
*2 = インターリーブあり

ARM レジスタから NEON/VFP レジスタへのデータ転送はいくつかの手段が考えられます。

13:  vmov  d,r,r             11.79 sec
14:  vmov  r,r,d             12.05 sec

この命令は NEON の 64bit d0~d31 レジスタと、ARM レジスタ r0~ 2個を相互に
転送するものです。ARM 側は必ず 2個セットの 64bit でなければならず、動作時間も
比較的かかっています。

10:  vmov.32  d[0],r         15.10 sec *1
11:  vmov.32  d[0],r         10.08 sec *2
12:  vmov.32  r,d[0]          7.03 sec

上の vmov.32 は NEON の d0~d31 レジスタのうち半分、32bit 分のみ転送する命令です。
NEON レジスタは下記のように割り当てられています。

NEON ビュー
+-------------------------------+
| q0                            |  128bit   ( ~ q15 , 16個 )
+---------------+---------------+
| d0            | d1            |   64bit   ( ~ d31 , 32個 )
+-------+-------+-------+-------+
| d0[0] | d0[1] | d1[0] | d1[1] |   32bit   ( ~ d31[1] , 64個 )
+-------+-------+-------+-------+

全く同じ領域を VFP レジスタとしてもアクセスすることが出来ます。

VFP ビュー
+---------------+---------------+
| d0            | d1            |   64bit   ( ~ d31 , 32個 )
+-------+-------+-------+-------+
| s0    | s1    | s2    | s3    |   32bit   ( ~ s31 , 32個 )
+-------+-------+-------+-------+

VFP ビューでは 32bit のデータを sレジスタとして個別にアクセスできます。
64bit dレジスタの扱いはほぼ同じです。
命令フィールドの制限から、レジスタ番号は 0~31 の範囲でなければなりません。
つまり 32bit レジスタが 64 個あるにもかかわらず、s レジスタとしてアクセス出来る
のは半分の s0~s31 だけです。

NEON ビューの 32bit スカラ要素では、このようなアクセス制限が無いことがわかります。

NEON ビューと VFP ビューの重要かつ大きな違いがもう一つあります。
NEON では dレジスタの 64bit 単位でデータを扱うということ。
VFP は sレジスタ単位、つまり 32bit 単位でデータを扱います。

よって上の命令

10:  vmov.32  d[0],r         15.10 sec *1

は NEON d レジスタの 32bit 分、半分の領域にしか書き込みを行いません。
このとき d レジスタは、残り半分のデータを保存しなければならなくなるため
デステネーションレジスタにも依存が発生します。
いわゆるパーシャルレジスタストールです。
SSE で SS 命令よりも、完全に置き換える PS 命令の方が速いのと同じです。

実際に測定してみると、下記のようにデスティネーション側レジスタに同じ d レジスタ
を指定するとパイプラインストールが発生します。これが 10: の vmov.32 の値です。

; 同じ d0 に部分書き込みを行うためストールする。10: vmov.32 ~ 15.06 sec
vmov.32  d0[0],r2
vmov.32  d0[1],r3
vmov.32  d0[0],r2
vmov.32  d0[0],r3
 ~

; 異なるレジスタへ交互に書き込む場合。11: vmov.32 ~ 10.04 sec
vmov.32  d0[0],r2
vmov.32  d1[0],r2
vmov.32  d2[0],r2
vmov.32  d3[0],r2
 ~

レジスタを置き換えた 11: の方では 15 sec → 10 sec と速くなるため、1cycle 分の
遅延が発生していることがわかります。

全く同じことが 03: の vldr.32 s,[r] でも発生していることがわかりました。
03: vldr.32 s,[r] は VFP 命令のはずですが、実行時間を見ると NEON の演算ユニットで
実行しているようです。
NEON には即値アドレスのメモリから 32bit スカラを読み込む命令がないので
この命令を多用しても大丈夫そうです。

s レジスタへの書き込みは上の d[x] と同じように 32bit の部分書き込みに相当します。
実際に下記の通りストールが発生しました。

; ストールする ( s0 も s1 も同じ d0 レジスタに相当するため)
vldr.32   s0,[r1]
vldr.32   s1,[r1]
vldr.32   s0,[r1]
vldr.32   s1,[r1]

; ストールしない
vldr.32   s0,[r1]
vldr.32   s2,[r1]
vldr.32   s4,[r1]
vldr.32   s6,[r1]

また sレジスタなので、後半 d16~d31 エリアへ直接 32bit の値をロードすることができません。

VFP 命令が遅いのは、このようなアーキテクチャの違いも一つの要因かもしれません。
レジスタへのアクセス単位が異なるので、パイプラインが矛盾しないように実行が終わるのを
待っている可能性があります。


最初の測定結果から、ARM レジスタから NEON レジスタへのスカラ転送は vdup を
使うのが最も効率がよいことがわかります。
実行速度も速く d レジスタを完全に置き換えるために不要な依存が発生しません。

メモリからの読み込みは vldr.32 s を用います。後半 d16~d31 エリアへの代入が
必要なら vld1.32 を使うことが出来ますが、この場合利用できるアドレッシングモードに
制限があります。

これらの測定データを元に、VFP 命令を NEON 命令に置換する簡単なスクリプト
作ってみました。命令置換は下記の方針で処理しています。

・s0~s31 レジスタは d0~d31 レジスタのスカラにマップする。

・ARM レジスタとの相互転送は下記の命令を使う。
     R ← D    vmov.32  r,d[0]
     D ← R    vdup.32  d,r

・一般の演算や浮動小数と整数の変換などは NEON の 64bit (float x2) 演算を用いる。

・メモリからのロードは vldr.32 s,addr

・d レジスタの奇数要素 d0[1]~d31[1] は一時的なテンポラリに使える。

・どうしても s レジスタを使わなければならない命令ではレジスタ番号を 2倍する。
  例えば double への変換や vldr 命令時。
  s1 は d1 = (s2,s3) にマッピングされるため。

・s レジスタの番号が 32 を超える場合は、テンポラリを経由した複数命令に展開する。やむなし

・倍精度演算命令は置き換えない。

gcc は VFP でコンパイルし、出力したアセンブラコード (*.s) をいったんスクリプトに通して
可能な部分を NEON 命令に置き換えます。例えば Makefile は下記のような感じで、
opt_neon.pl を通しています。

%.o: %.cpp
	$(CC) $(CFLAGS) $< -S -o $*._temp.s
	$(PERL) opt_neon.pl $*._temp.s > $*.s
	$(CC) $(CFLAGS) $*.s -c -o $@

void Loop_main( float dd )
{
    TimerClass  timer;
    timer.Begin();
    float   sum= 0.0f;
    for( int i= 0 ; i< 100000000 ; i++ ){
        sum= sum * dd + dd;
    }
    timer.End( "end" );
    printf( "%f\n", sum );
}

上のプログラムを実行した結果は次の通り (NetWalker Cortex-A8 800MHz)

そのまま VFP を使用した場合    2.39 秒
オプティマイザを通した場合     1.01 秒

スクリプト opt_neon.pl は上のプログラムを正しく変換できるように必要な命令の分しか
作っていません。つまりまだ未完成です。対応してない命令があるのでどんなプログラムでも
変換できるわけではありません。
それでもきちんと効果があるので、このまま対応命令を増やせば浮動小数演算を多用した
アプリケーションも大幅な高速化が期待できそうです。

opt_neon.pl

将来的にはおそらく gcc の方に、スカラ演算でも NEON コードだけを生成するオプションが
追加されるのではないでしょうか。
試していませんが iPhone 3GS や iPod Touch 3G でももしかしたら NEON 最適化が
有効かもしれません。

NetWalker    i.MX515  Cortex-A8 (ARM v7-A) 800MHz   AMD Z430
iPhone 3GS   S5PC100  Cortex-A8 (ARM v7-A) 600MHz   PowerVR SGX 535


関連エントリ
NetWalker PC-Z1 Cortex-A8 の NEON 命令とメモリ速度
SSE の浮動小数演算速度
NetWalker PC-Z1 Cortex-A8 浮動小数演算の実行速度
NetWalker PC-Z1 Atom と速度比較


最善ケースばかりでなく、実際に使えるプログラムで試してみます。
使っているのは i.MX51 系 i.MX515 を搭載した NetWalker PC-Z1 ですが、
おそらく同じ Cortex-A8 (ARM v7-A) の CPU core を使った iPhone 3GS や
iPod touch 3G でも全く同様ではないかと思います。

NEON のプログラミングは比較的簡単です。
例えば 4x4 matrix の乗算だとこんな感じで書けます。

inline void Mul_NEON( Float4x4* p3, const Float4x4* p1, const Float4x4* p2 )
{
    __asm__ __volatile__ ( "\
        vldmia  %0, {d0-d7}     \n\
        vldmia  %1, {d8-d15}    \n\
\n\
        vmul.f32    q8,q0,d8[0]     \n\
        vmla.f32    q8,q1,d8[1]     \n\
        vmla.f32    q8,q2,d9[0]     \n\
        vmla.f32    q8,q3,d9[1]     \n\
        vstmia  %2!, {d16,d17}      \n\
\n\
        vmul.f32    q8,q0,d10[0]    \n\
        vmla.f32    q8,q1,d10[1]    \n\
        vmla.f32    q8,q2,d11[0]    \n\
        vmla.f32    q8,q3,d11[1]    \n\
        vstmia  %2!, {d16,d17}      \n\
\n\
        vmul.f32    q8,q0,d12[0]    \n\
        vmla.f32    q8,q1,d12[1]    \n\
        vmla.f32    q8,q2,d13[0]    \n\
        vmla.f32    q8,q3,d13[1]    \n\
        vstmia  %2!, {d16,d17}      \n\
\n\
        vmul.f32    q8,q0,d14[0]    \n\
        vmla.f32    q8,q1,d14[1]    \n\
        vmla.f32    q8,q2,d15[0]    \n\
        vmla.f32    q8,q3,d15[1]    \n\
        vstmia  %2!, {d16,d17}      \n\
    "
    : "=&r"( p1 ), "=&r"( p2 ), "=&r"( p3 )
    : "0"( p1 ), "1"( p2 ), "2"( p3 )
    : "q0", "q1", "q2", "q3", "q4", "q5", "q6", "q7", "q8", "cc", "memory"
    );
}

かなり少ない命令で記述できます。

・3オペランドの命令フォーマットでレジスタの転送が不要
・積和命令がある
・ベクタへのスカラ乗算機能が用意されているため、スカラ要素の複製が不要
・レジスタの数が多い

d0~d31 : 64bit レジスタ (float x2) 32本
q1~q16 : 128bit レジスタ (float x4) 16本 (d0~d31 と共有)

上のプログラムで配列のような記述をしている d8[0], d8[1] は、d0レジスタに含まれる
2つの float に個別にアクセスしています。

s0~s31 は 32bit の single float レジスタですが、この形式で記述する命令は
VFP 命令なので要注意です。NEON View では d または q レジスタのみ扱い、
single 要素は d レジスタの一部として扱います。
なお VFP 命令も倍精度演算で d レジスタを扱うことがあります。

vmla は積和命令で vmla.f32 q8,q0,d8[1] はシェーダー風の記述を用いるなら

q8.xyzw = q0.xyzw * d8.xxxx + q8.xyzw

となります。ベクタへのスカラ乗算を簡単に記述できるのは便利です。
SSE だと 3~4命令くらいかかります。


これが SSE なら

void __fastcall Mul_SSE( Float4x4* p3, const Float4x4* p1, const Float4x4* p2 )
{
    __asm {
    mov     eax, dword ptr[esp+4]	; p2

    movaps  xmm4, xmmword ptr[edx]	; p1->_11_12_13_14
    movss   xmm0, xmmword ptr[eax]	; p2->_11
    shufps  xmm0, xmm0, 00000000b
    mulps   xmm0, xmm4
    movaps  xmm1, xmm0

    movaps  xmm5, xmmword ptr[edx+16]	; p1->_21_22_23_24
    movss   xmm0, xmmword ptr[eax+4]	; p2->_12
    shufps  xmm0, xmm0, 00000000b
    mulps   xmm0, xmm5
    addps   xmm1, xmm0

    movaps  xmm6, xmmword ptr[edx+32]	; p1->_31_32_33_34
    movss   xmm0, xmmword ptr[eax+8]	; p2->_13
    shufps  xmm0, xmm0, 00000000b
    mulps   xmm0, xmm6
    addps   xmm1, xmm0

    movaps  xmm7, xmmword ptr[edx+48]	; p1->_41_42_43_44
    movss   xmm0, xmmword ptr[eax+12]	; p2->_14
    shufps  xmm0, xmm0, 00000000b
    mulps   xmm0, xmm7
    addps   xmm1, xmm0

    movaps  xmmword ptr[ecx], xmm1	; p3->_11_12_13_14

    movss   xmm0, xmmword ptr[eax+16]	; p2->_21
    shufps  xmm0, xmm0, 00000000b
    mulps   xmm0, xmm4
    movaps  xmm1, xmm0

    movss   xmm0, xmmword ptr[eax+20]	; p2->_22
    shufps  xmm0, xmm0, 00000000b
    mulps   xmm0, xmm5
    addps   xmm1, xmm0

    movss   xmm0, xmmword ptr[eax+24]	; p2->_23
    shufps  xmm0, xmm0, 00000000b
    mulps   xmm0, xmm6
    addps   xmm1, xmm0

    movss   xmm0, xmmword ptr[eax+28]	; p2->_24
    shufps  xmm0, xmm0, 00000000b
    mulps   xmm0, xmm7
    addps   xmm1, xmm0

    movaps  xmmword ptr[ecx+16], xmm1	; p3->_21_22_23_24

    movss   xmm0, xmmword ptr[eax+32]	; p2->_31
    shufps  xmm0, xmm0, 00000000b
    mulps   xmm0, xmm4
    movaps  xmm1, xmm0

    movss   xmm0, xmmword ptr[eax+36]	; p2->_32
    shufps  xmm0, xmm0, 00000000b
    mulps   xmm0, xmm5
    addps   xmm1, xmm0

    movss   xmm0, xmmword ptr[eax+40]	; p2->_33
    shufps  xmm0, xmm0, 00000000b
    mulps   xmm0, xmm6
    addps   xmm1, xmm0

    movss   xmm0, xmmword ptr[eax+44]	; p2->_34
    shufps  xmm0, xmm0, 00000000b
    mulps   xmm0, xmm7
    addps   xmm1, xmm0

    movaps  xmmword ptr[ecx+32], xmm1	; p3->_31_32_33_34

    movss   xmm0, xmmword ptr[eax+48]	; p2->_41
    shufps  xmm0, xmm0, 00000000b
    mulps   xmm0, xmm4
    movaps  xmm1, xmm0

    movss   xmm0, xmmword ptr[eax+52]	; p2->_42
    shufps  xmm0, xmm0, 00000000b
    mulps   xmm0, xmm5
    addps   xmm1, xmm0

    movss   xmm0, xmmword ptr[eax+56]	; p2->_43
    shufps  xmm0, xmm0, 00000000b
    mulps   xmm0, xmm6
    addps   xmm1, xmm0

    movss   xmm0, xmmword ptr[eax+60]	; p2->_44
    shufps  xmm0, xmm0, 00000000b
    mulps   xmm0, xmm7
    addps   xmm1, xmm0

    movaps  xmmword ptr[ecx+48], xmm1	; p3->_41_42_43_44
    };
}

長いです。

その代わり Cortex-A8 / NEON はインオーダー実行 HT 無しなので、最適化を考えて
書く必要があります。
最初の NEON の例も、レジスタが多いことを利用すれば次のようにできます。

    vldmia  %0, {d0-d7}
    vldmia  %1, {d8-d15}

    vmul.f32    q8,q0,d8[0]
    vmul.f32    q9,q0,d10[0]
    vmul.f32    q10,q0,d12[0]
    vmul.f32    q11,q0,d14[0]

    vmla.f32    q8,q1,d8[1]
    vmla.f32    q9,q1,d10[1]
    vmla.f32    q10,q1,d12[1]
    vmla.f32    q11,q1,d14[1]

    vmla.f32    q8,q2,d9[0]
    vmla.f32    q9,q2,d11[0]
    vmla.f32    q10,q2,d13[0]
    vmla.f32    q11,q2,d15[0]

    vmla.f32    q8,q3,d9[1]
    vmla.f32    q9,q3,d11[1]
    vmla.f32    q10,q3,d13[1]
    vmla.f32    q11,q3,d15[1]
    vstmia  %2, {d16-d23}

完全にキャッシュがヒットする前提なら、こちらのコードの方が 1.7倍くらい速くなります。
ただし、実際のアプリケーションで使うとここまで差が出ません。
メモリアクセスの方がずっと低速だからです。

キャッシュがほとんど利かない条件でテストすると、Cortex-A8 + NEON は Atom よりも
かなり低速でした。Matrix 演算ではなく、ただのメモリ転送だけテストしてみたのが下の表です。

Atom Z540 1.86GHz  2.98GB/sec
Atom Z540  800MHz  1.91GB/sec (省電力機能で制限をかけたもの)
Cortex-A8  800MHz   255MB/sec

Atom Z540 は FSB 533MHz で 64bit 幅あるため、DDR2-533 とするとメモリの転送速度は
最大 4.2GB/sec (PC2-4200) と考えられます。
i.MX515 は mDDR1 or DDR2 200MHz の 16/32bit らしいですが、この辺の具体的な値は
明らかになっていません。ただ考えられる数値よりも測定結果はかなり低いです。
省電力との兼ね合いでしょうか。L2 cache 容量も Atom の半分です。

プロセッサ自体の演算能力はそれなりにあるものの、メモリ上のデータを大量にストリーム処理
するようなケースでは、ほとんど生かし切れていない可能性があります。

同様の core を持つ iPhone 3GS でどの程度動くか試してみたいところです。


関連エントリ
SSE の浮動小数演算速度
NetWalker PC-Z1 Cortex-A8 浮動小数演算の実行速度
NetWalker PC-Z1 Atom と速度比較


コメントをいただきましたのでこちらにも書いておきます。
NetWalker は購入後、何のソフトを入れなくても最初から親指シフト入力が可能です。
もしかして富士通以外のメーカー PC で親指シフト入力に最初から対応しているのは、
これが初だったりするんでしょうか。
実は Anthy が親指シフト入力に対応しているおかげです。

設定から
 システム → 設定 → SCIM入力メソッドの設定 → IMエンジン → Anthy

入力方式: 親指シフト入力方式

に切り替えれば OK です。

以後デフォルトが「親指シフト入力」になりますし、この状態でも [Alt] + [ローマ字] を押せば

ローマ字入力 → かな入力 → 親指シフト入力

と順番に切り替わります。細かいカスタマイズも出来ます。

ただし難点もいくつかあります。

・NetWalker のキーの出来(つくり)が悪くてタッチタイプするにはかなり練習が必要
・キーボードの周囲がせり上がっていて、下段のキーを親指で押しづらい
・[ね,][ほ.] のキーが小さい
・カスタマイズしないと [ん;] のキーが遠い

幸いなことにキーの入れ替えは標準の機能で出来ます。親指シフト入力向け (?) の
キー入れ替えはこちら をどうぞ。
USB や Bluetooth の外付けキーボードを使えば問題ないかもしれません。

キーボードの周囲の縁はいつか削ろうと思ってます。

↓削りました
netwalker_01.jpg


関連エントリ
NetWalker PC-Z1 Bluetooth とキーカスタムその他
NetWalker PC-Z1 意外にいける
Netwalker PC-Z1 買いました


Cortex-A8 の NEON のように SSE も試してみました。
やりかたは前回と全く同じように、ひたすら命令を並べてスループットの測定。
今回はスレッドにも対応しておきます。

static void Thread_SSE_MULPS( Thread::Sync* barrier )
{
    __asm {
        xorps   xmm0, xmm0
        ~
    };
    barrier->Barrier( THREAD_COUNT );
    for( int i= 0 ; i< VECTOR_LOOP ; i++ ){
        __asm {
            mulps   xmm0, xmm1
            mulps   xmm1, xmm2
            mulps   xmm2, xmm3
            mulps   xmm3, xmm4
            mulps   xmm4, xmm5
            mulps   xmm5, xmm6
            mulps   xmm6, xmm7
            mulps   xmm7, xmm0
		~
	};
    }
    barrier->Barrier( THREAD_COUNT );
}

void Start_SSE( void (*func)( Thread::Sync* ), const char* msg )
{
    MultiThreadClass<THREAD_COUNT-1>    thread;
    Thread::Sync barrier;
    barrier.Init();

    thread.Start( func, &barrier );

    TimerClass  timer;
    timer.Begin();

    func( &barrier );

    timer.End( msg );

    barrier.Quit();
    thread.Join();
}

void SSE_main()
{
    Start_SSE( Thread_SSE_MULPS, "SSE_MULPS" );
}

上記のようにメモリアクセスの無い命令を前回同様に 40 個 (8x5) 並べます。

スレッドの開始と終了待ちはシグナルで同期します。一番遅いスレッドの終了に合わせているため、
速く終了したスレッドがあると若干無駄が生じる可能性があります。
メインスレッドが実行している間に、サブスレッドで何回命令を実行出来たか数えた方が
より正確な結果になるかもしれません。

Core i7 920 2.67GHz を使っています。1 スレッドあたり 1G 回ループします。

SSE_MULPS  14.32 sec  Thread x1

40命令 × 1G 回ループなので 40/14.32 = 2.79 。
CPU は 2.67GHz なので 1命令 / 1cycle に近い値だけど少々オーバーしています。
ターボブーストのおかげでしょうか。スレッドを増やしてみます。

SSE_MULPS  16.19 sec  Thread x4
SSE_MULPS  29.05 sec  Thread x8

core 数 (4) までは大きく増えませんが、8スレッド時は、1スレッドのほぼ 2倍の時間が
かかっています。HT を含めるとハードウエアで 8スレッドですが、パイプラインに空きが
ないためか HT の効果が全く無いことがわかります。
8スレッド時の値を元に計算してみると Core i7 920 2.67GHz は mulps で

40命令 × 1G回ループ × 4 SIMD × 8 Thread / 29.05 秒 = およそ 44.06 G FLOPS

SSE には積和がありませんが SSE4.1 には DPPS があることを思い出しました。
Mul x4 + Add x3 なので試してみます。

        __asm {
            dpps    xmm0, xmm1, 255
            dpps    xmm1, xmm2, 255
            dpps    xmm2, xmm3, 255
                ~


SSE_DPPS  28.74 sec  Thread x1
SSE_DPPS  31.02 sec  Thread x4
SSE_DPPS  57.73 sec  Thread x8

mul のちょうど 2倍、スループット 2 で実行しているようです。
2 cycle で 7 float なので演算量自体は mulps に劣ります。

最初の乗算を敢えてストールするように組み替えてみます。

        __asm {
            mulps   xmm0, xmm0
            mulps   xmm0, xmm0
            mulps   xmm0, xmm0
                ~


SSE_MULPS_ST  57.36 sec  Thread x1
SSE_MULPS_ST  57.51 sec  Thread x4
SSE_MULPS_ST  58.63 sec  Thread x8

インターリーブ時は 14.32秒だったので、4倍の実行時間がかかるようになりました。
mulps のレイテンシが 4 cycle あることがわかります。パイプラインはがら空きで、
4スレッド時と 8スレッド時の実行速度に差がありません。HT がめいっぱい働いており、
それでもまだ半分ほど無駄が生じていることになります。

SSE_DPPS_ST  157.57 sec  Thread x1
SSE_DPPS_ST  157.68 sec  Thread x4
SSE_DPPS_ST  157.74 sec  Thread x8

同じように dpps もストールさせてみると、こちらはさらに時間がかかります。
5.5 倍かかっているためスループット 2 、レイテンシ 11 で実行しているようです。

Atom でも試してみました。遅いのでループ回数を 1/10 (100M) に減らしています。

Atom 1.8GHz  SSE_MULPS      4.36 sec  Thread x1
Atom 1.8GHz  SSE_MULPS      8.70 sec  Thread x2
Atom 1.8GHz  SSE_ADDPS      2.25 sec  Thread x1
Atom 1.8GHz  SSE_ADDPS      4.46 sec  Thread x1
Atom 800MHz  SSE_MULPS     10.15 sec  Thread x1
Atom 800MHz  SSE_MULPS     20.29 sec  Thread x2

乗算は 2 float/cycle、加算で 4 float/cycle 実行しています。
やはりパイプラインを埋めると HT の効果が無いことがわかります。

Atom 1.8GHz  SSE_MULPS_ST  10.88 sec  Thread x1
Atom 1.8GHz  SSE_MULPS_ST  10.92 sec  Thread x2
Atom 1.8GHz  SSE_MULPS_ST  21.92 sec  Thread x4

乗算はストール発生時 2.5 倍遅くなっています。スループット 2/ レイテンシ 5

結果として Atom も Cortex-A8 + NEON も、1 cycle あたり 2乗算 or 4加算で同等です で同等、加算は Atom の方が高速です。
ただし積和の場合は Atom は 3cycle かかるところ NEON は 2cycle で済むため、
この組み合わせでは Cortex-A8 + NEON の方が速いことになります。
浮動小数演算も同クロック数の Atom より高速に実行できそうです。

2009/10/02 追記>: 加算が多いと Atom の方が速く、最適化が弱い場合は HT が有効な分 Atom が有利なこともあります。

もちろん他の命令の実行速度にもよるし、メモリアクセスや演算以外の転送命令等も絡んで
くるので一概には言えません。レイテンシも大きめなので最適化をどれだけ出来るかに
かかってきます。

厳密なデータではありませんが、以前の結果を見ても浮動小数演算以外は同サイクルの
Atom より速い速度で実行できているし、浮動小数演算も潜在能力は高そうです。
問題は VFP でしょうか。


関連エントリ
NetWalker PC-Z1 Cortex-A8 浮動小数演算の実行速度
NetWalker PC-Z1 Atom と速度比較


先日のテストでは Atom に匹敵する実行速度を出していながら、浮動小数演算では
大きく差をつけられる結果となりました。
もう少し詳しく調べたところ、いろいろわかってきました。

結論は
・VFP が遅い
・NEON の SIMD 2/4 を使えばかなり速い
・NEON の単精度スカラー同士の演算は VFP なので遅い

今回は直接命令毎の実行速度を測ってみます。例えば整数乗算なら下記のような感じで。
省略していますが実際には mul 命令を 40 個並べています。
これを 100M 回 (1億) ループします。

static void Start_ASMINT_MUL()
{
    TimerClass  timer;
    timer.Begin();

    __asm__ __volatile__( "\
        mov  r2, #123  \n\
    " : : : "r2","cc" );

    for( int i= 0 ; i< VECTOR_LOOP ; i++ ){
        __asm__ __volatile__(
        "\
            mul	r3, r2, r2 \n\
            mul	r3, r2, r2 \n\
              ~
        " : : : "r3","cc" );
    }

    timer.End( "ASMINT_MUL" );
}


各種演算命令を並べてテストした結果が下記の通りです。
4000M 命令の実行時間の実測値 (秒) です。

i.MX515 Cortex-A8 (ARM) 800MHz
命令                 実行時間        演算ユニットと演算単位
---------------------------------------------------------------
mul  r,r,r           10.21 sec       ALU   32bit
add  r,r,r            5.16 sec       ALU   32bit
fmuls  s,s,s         50.45 sec       VFP   32bit
fadds  s,s,s         45.45 sec       VFP   32bit
fmuld  d,d,d         55.49 sec       VFP   64bit
vmul.f32  s,s,s      50.45 sec       VFP   32bit
vmul.f32  q,q,q      10.07 sec       NEON 128bit (32bit x4)
vadd.f32  q,q,q      10.08 sec       NEON 128bit (32bit x4)
vmla.f32  q,q,q      10.09 sec       NEON 128bit (32bit x4) 積和
vmul.f32  d,d,d       5.04 sec       NEON  64bit (32bit x2)

命令はパイプラインがストールしないよう、意味がないけど依存が発生しない組み合わせで
並べています。

整数演算は mul でおよそ 392M命令/sec です。add の場合は 775M 命令なので、
ほぼ 1命令/cycle に近い数値で実行出来ていることがわかります。

問題の VFP による浮動小数演算は乗算で 5倍、add 比で 9倍も遅くなっています。
倍精度の乗算でも 10% ほどしか増えていないので、オーバーヘッドは別のところに
あるのかもしれません。
丸めモードや Flush-to-Zero モードなど、FPSCR も書き換えてみましたが結果は同じでした。
なお乗算する値が 0 の場合のみ、fmuls は 3割ほど速い速度で完了します。

対照的に NEON がかなり高速です。4要素(128bit) の SIMD 演算であるにもかかわらず
整数演算と同速度で実行しており、2要素(64bit) の SIMD の場合はむしろ整数演算よりも
高速です。ほぼ 1cycle 毎に 2 float の演算をしています。
さらに積和演算が可能なので、SIMD で vmla を用いた場合

40命令 × 100Mループ × 4 (SIMD) × 2 (積和) / 10sec = およそ 3.2G FLOPS

となります。

ではなぜ普通にコンパイルしたプログラムの浮動小数演算が遅いのかと言えば、NEON の
スカラー演算命令は VFP と共有されているからです。
sレジスタ を用いた演算は、NEON ではなく VFP で実行されます。
実際に vmul.f32 s,s,s は fmuls s,s,s と全く同じバイトコードでした。
コンパイラは一般の浮動小数演算では VFP 命令を出力しています。

結局最初に書いたとおり VFP が非常に遅いという結論になります。
そのためスカラー演算であっても 2/4 ベクタとみなして NEON 命令を使った方が
高速になるわけです。
例えば fmuls s,s,s → vmul.f32 d,d,d なら 10 倍です。

実際のアプリケーションで使ったわけではありませんが、周辺を考えなければ Atom 比でも
結構良い結果になるのではないでしょうか。
これが iPod touch 3G/iPhone 3GS の大きさにも収まっていると考えればなおさらです。

vmla は破壊代入するため、他の命令のように同じオペランドのまま並べると依存が発生して
ストールします。5倍ほど遅くなるので、このテストでは代入側レジスタを 5個用いて
インターリーブしています。quad 時スループット 2 のレイテンシ 10 くらいでしょうか。


関連エントリ
NetWalker PC-Z1 Atom と速度比較
NetWalker PC-Z1 Bluetooth とキーカスタムその他
NetWalker PC-Z1 意外にいける
Netwalker PC-Z1 買いました
タッチタイプの境界
NetWalker PC-Z1


Atom と比べてみました。
あくまで参考程度でお願いします。

  ALU    FPU    MEM1   MEM4
  9.32   5.17   4.24   3.20     (1)  Core i7 920 2.67GHz
 29.41  29.36  43.83  27.43     (2)  Atom Z540 1.86GHz
 68.68  68.73  46.70  36.45     (3)  Atom Z540 800MHz
 42.88  44.09  39.45  20.03     (4)  Atom N270 1.60GHz
 37.82 209.76  56.35  30.68     (5)  Cortex-A8 800MHz NEON
 37.84 209.79  58.17  30.69     (6)  Cortex-A8 800MHz VFP
 37.83 252.80  56.34  30.75     (7)  Cortex-A8 800MHz soft
 42.96 211.02  55.86  30.52     (8)  Cortex-A8 800MHz NEON + Thumb-2
 42.85 212.18  55.87  30.53     (9)  Cortex-A8 800MHz VFP + Thumb-2
 42.92 267.23  55.84  30.54     (10) Cortex-A8 800MHz soft + Thumb-2

単位は秒、値が小さい方が高速

テストは基本的にただのループかつシングルスレッドのみです。
数値は Real Time なので厳密なコードのみの実行速度ではないです。

ALU 全部キャッシュに収まる整数演算とループ
FPU スカラーの浮動小数演算を含んだループ
MEM1 8bit 単位のメモリアクセス
MEM4 32bit 単位のメモリアクセス

(5)~(10) が NetWalker です。
浮動小数演算以外はほぼ Atom と近い範囲で、クロック差があるにもかかわらずかなり
善戦しています。おそらく整数演算に関してはシングルスレッドかつキャッシュに入る場合、
同クロックの Atom よりも高速です。

ただし実際の動作速度は CPU core の速度だけでなく、メモリや SSD 等他のデバイスの
影響が大きいので、これだけで判断することは出来ません。

また Atom は HT 前提のパイプライン設計なので、シングルスレッドではその性能を
出し切れていないともいえます。
同じように (1) の Core i7 も 8 スレッドのうち 1つだです。ちなみに (1) は
同時に VirtualBox の仮想 PC が動いていたりとかなりの悪条件で走らせました。

計測も Real 時間なので、バックグラウンドで動いているタスクの影響を少なからず
受けているといえます。この場合 HT を持っている Atom の方が若干有利に働くかも
しれません。

(3) は Z540 の電源設定を変更して、上限 800MHz で動かしたものです。
ALU FPU 時間はクロック数に比例して遅くなりました。MEM1/MEM4 はそれほど変化が
ないようです。この点から MEM1/4 の実行速度は CPU core の速度度よりも、メモリ
アクセス時間の割合の方が大きいと考えられます。

(2)/(3) と (4) はクロック比と速度が一致していません。これはおそらくコンパイラ
の差です。最適化の傾向が異なっており、ループ内の演算に無駄があり畳み込める
条件では gcc の方が高速でした。また VC は SSE 命令を使っており、gcc は
-msse4 をつけても FPU 命令に展開されています。

MEM1 は 1byte 単位で 32MByte を読み書きしています。
MEM4 は 4byte 単位で 64MByte アクセスしています。
よってループ回数は MEM4 が半分になります。
(4) の結果を見ると MEM4 は MEM1 のちょうど半分の時間で完了しているので、
このテストではデータサイズが速度に影響を与えていないようです。

浮動小数演算はただのスカラー演算です。遅すぎて、設定ミスとか例外が発生してるの
ではないかと疑いました。一応 vfp,neon,soft と 3通り試しています。
取りあえずエミュレーションよりは速いことがわかります。
きちんと SIMD を活用すれば、もう少しそれなりの速度が出るのかもしれません。

Thumb-2 の効果は確認できました。16/32bit 命令が混在しており、FP 命令呼び出しも
特に速度が落ちていないようです。

浮動小数演算以外は思ったよりも速いです。浮動小数演算が著しく遅いのは、
プログラムのミスなのかまだ良くわかっていません。

ARM の浮動小数演算に関しては下記ページを参考にさせていただきました。
ひまじめ 組み込みLInux


(2009/09/30 追記) 続き>> NetWalker PC-Z1 Cortex-A8 浮動小数演算の実行速度


(1) PC
Core i7 920 2.67GHz
Windows7 RTM x64, VC2008 x86 -arch:SSE2

(2) VAIO type P
Atom Z540 1.86GHz
Windows7 RC x86, VC2008 x86 -arch:SSE2

(3) VAIO type P
Atom Z540 1.86GHz (800MHz)
Windows7 RC x86, VC2008 x86 -arch:SSE2

(4) EeePC 901
Atom N270 1.6GHz
Ubuntu 9.04 x86, gcc-4.3.3 -mssse3

(5) NetWalker PC-Z1
i.MX515 Cortex-A8 800MHz NEON
Ubuntu 9.04 armel, gcc-4.3.3 -mfloat-abi=softfp -mfpu=neon

(6) NetWalker PC-Z1
i.MX515 Cortex-A8 800MHz VFP
Ubuntu 9.04 armel, gcc-4.3.3 -mfloat-abi=softfp -mfpu=vfp

(7) NetWalker PC-Z1
i.MX515 Cortex-A8 800MHz soft
Ubuntu 9.04 armel, gcc-4.3.3 -mfloat-abi=soft

(8) NetWalker PC-Z1
i.MX515 Cortex-A8 800MHz NEON + Thumb-2
Ubuntu 9.04 armel, gcc-4.3.3 -mfloat-abi=softfp -mfpu=neon -mthumb

(9) NetWalker PC-Z1
i.MX515 Cortex-A8 800MHz VFP + Thumb-2
Ubuntu 9.04 armel, gcc-4.3.3 -mfloat-abi=softfp -mfpu=vfp -mthumb

(10) NetWalker PC-Z1
i.MX515 Cortex-A8 800MHz soft + Thumb-2
Ubuntu 9.04 armel, gcc-4.3.3 -mfloat-abi=soft -mthumb

(2009/09/30 追記) 続き>> NetWalker PC-Z1 Cortex-A8 浮動小数演算の実行速度


関連エントリ
NetWalker PC-Z1 Bluetooth とキーカスタムその他
NetWalker PC-Z1 意外にいける
Netwalker PC-Z1 買いました
タッチタイプの境界
NetWalker PC-Z1


GeForce のドライバ 191.03 (beta) が出ています。
やっと OpenGL 3.2 と Compute Shader 4.0 の共存が出来るようになりました。
どちらも動いています。

GEFORCE/ION DRIVER RELEASE 191

この辺 RADEON はまだ対応していませんが、すでに DirectX11 が動く 5870 が発売されました。
ShaderModel とか Compte Shader の対応とか非常に興味あるけど
週末 RADEON HD 5870 はすでに売り切れでした。


関連エントリ
OpenGL や GLSL の互換性
Direct3D11/DirectX11 GeForce の ComputeShader とドライバ


●Bluetooth

Bluetooth アダプタ Princeton PTM-UBT3S, PLANEX BT-Mini2EDR を使ってみました。

・システム → システム管理 → Synaptic パッケージマネージャー
・gnome-bluetooth をインストール指定 → 適用
・いったんログアウトして再ログイン

アダプタを接続すると通知スペースに Bluetooth のアイコンが表示されるので
そこからペアリング等を行います。または システム → 設定 → Bluetooth 。

ペアリングしたもの。
マウス SONY VGP-BMS33, キーボード REUDO RBK-2100BTJ

二回ほどサスペンド後にアダプタを認識してないことがありましたが、
電源入れ直し等で復活。以降は今のところ動いています。


●キーボード

購入時からキーボードの配列のカスタマイズを考えていました。
本当は uinput を使いたいのですが module uinput.ko が無かったので取りあえず
xmodmap を使います。

$ xmodmap -pke > $HOME/.Xmodmap

以降 .Xmodmap の書き換えでキー配列の入れ替えができます。
書き換えた内容はログインし直しで適用されるはずです。
その場で確認するなら

$ xmdomap $HOME/.Xmodmap

上のコマンド実行だけだとサスペンド後に配列が戻ってしまいます。
再ログインしておけばサスペンド後も有効となります。
$HOME に .Xmodmap で始まるファイルが複数ある場合、ログイン時にどれを用いるか
選択画面が出ます。

実際に設定してみます。
例えば [/?] (61) と [;+] (47) を入れ替えるなら 61 と 47 を交換します。

!keycode  47 = semicolon plus semicolon plus semicolon plus
keycode  61 = semicolon plus semicolon plus semicolon plus

!keycode  61 = slash question slash question slash question
keycode  47 = slash question slash question slash question

Shift や Control キーを入れ替える場合はモディファイヤの再登録が必要です。

[ J ][ K ][ L ][/? ][Ent]
 [ M ][<,][>.][ ↑ ][Sft]

  ↓

[ J ][ K ][ L ][;+ ][Ent]
 [ M ][<,][>.][ /? ][ ↑]

例えば内蔵キーボードの右下部分を上記のように並べ替えるには

! ファイル先頭で remove
remove shift = Shift_R

!keycode  47 = semicolon plus semicolon plus semicolon plus
keycode  61 = semicolon plus semicolon plus semicolon plus

!keycode  61 = slash question slash question slash question
keycode  111 = slash question slash question slash question

!keycode  62 = Shift_R NoSymbol Shift_R NoSymbol Shift_R
keycode  47 = Shift_R NoSymbol Shift_R NoSymbol Shift_R

!keycode 111 = Up NoSymbol Up NoSymbol Up
keycode 62 = Up NoSymbol Up NoSymbol Up

! ファイル最後で add し直し
add shift = Shift_R


●キーボードカスタマイズ uinput

User Input を利用するとプログラムでキーの挙動をカスタマイズすることが可能です。
下記のページを参考にさせていただきました。

Linux Input Subsystemの使い方

x86 PC の Ubuntu 上で試してみました。
入力を遅延判定させた場合のバッファリングとかそのフラッシュなど、Windows /
WindowsMobile 上で ctrlswapminiem1key を作っていたときとほとんど同じ感覚です。
おそらく同等のプログラムの移植も可能だと思います。
日本語入力の状態が取得できれば親指シフト入力も対応できるでしょう。

(2009/10/03 追記: 親指シフトについてはこちらをどうぞ)

NetWalker 向けのプログラムも作ったのですが uinput が使えずまだ試しておりません。
実際のプログラム例は下記の通りです。
これは [A] と [B] を交換するだけです。サンプルにあった hook_main.c/.h をそのまま
使わせていただきました。hook_main.c をリンクするだけで動作します。

// swap_AB.c
// swap_AB /dev/input/event*
#include  <stdio.h>
#include  <linux/input.h>
#include  <linux/uinput.h>
#include  <sys/ioctl.h>
#include  "hook_main.h"

const char*  name= "swap_AB";
const char*  version_information= "swap_AB";
const char*  description= "swap_AB";

static int   keyboard_fd= 0;

void* init_event_handler( int number_of_event_device_files, int* event_fds )
{
    ioctl( keyboard_fd= *event_fds, EVIOCGRAB, 1 );
    return  NULL;
}

void finalize_event_hander( void* user_data )
{
    ioctl( keyboard_fd, EVIOCGRAB, 0 );
}

void handle_event( int fd, struct input_event event, void* user_data )
{
    int	vk= event.code;
    if( event.type == EV_KEY ){
        switch( vk ){
        case KEY_A:
            vk= KEY_B;
            break;
        case KEY_B:
            vk= KEY_A;
            break;
        }
    }
    send_event( event.type, vk, event.value );
}

void set_event_bits( int ui_fd )
{
    int	i;
    ioctl( ui_fd, UI_SET_EVBIT, EV_KEY );
    for( i= 0 ; i< 256 ; i++ ){
        ioctl( ui_fd, UI_SET_KEYBIT, i );
    }
}


●その他気がついたことなど

最初すぐにバッテリーが減ったように見えるけど、ステータスでは「バッテリー容量:中」。
容量とアイコンの見た目が合っていません。

フラッシュメモリの容量は 4GB ですが、初期状態の空きはおよそ 2.7GB でした。
カタログには「ユーザーエリア 2GB」と書かれているので若干余裕があります。

RAM 容量は 512MB、利用可能な空間は 480MB。
Firefox、ドキュメントビューア、コンソールを起動して消費メモリは 200MB くらい。
ページを複数開いてもまだまだ大丈夫です。

「パネルへ追加」から「CPU 周波数の計測モニタ」を追加してみました。
CPU クロックはピーク時 800MHz で、アイドル時は 160MHz まで落ちているようです。

上パネルの時計の設定から都市を登録しておくと天気や温度も表示されます。

[Fn]+[8] で画面のバックライトだけ消すことが可能です。

黒い色を買ったせいか汚れは結構目立ちます。
ストラップをつけられるのは便利です。手でもって使う場合も安心。

仕様一覧によるとフレームバッファは 16bit 65536色です。


関連エントリ
NetWalker PC-Z1 意外にいける
Netwalker PC-Z1 買いました
タッチタイプの境界
NetWalker PC-Z1


二日ほど使った感想は「結構良い」です。
意外といったら失礼かもしれませんが、いろいろな面で見直しました。


●両手で持つ

前回書いたとおりネット接続はあまり考えてなかったのですが、使ってみたらこれが快適でした。
無線 LAN さえあれば立っていても横になった状態でも楽にブラウズできます。

・両手持ちしたときちょうど良い位置にあるオプティカルポイント。ほぼこれだけで操作できる。
・ちょっとした検索文字列の入力なら十分役に立つキーボード。
・小さいけど十分な解像度の液晶画面もあるしフォントも見やすい。
・NetBook より軽量で持ち上げられる。
・熱も持たずにバッテリーの持ちも良い。
・中断も DS のようにふたを閉じるだけ。電源を入れるとすぐに復帰する。

電車内でもモバイルルータ併用で立ったまま使ってみました。これも思いのほか使いやすい。
Firefox でネットを見たり、電波の届かない地下鉄区間もメモ打ちしたりと便利に使えます。

以前使っていたスマートフォンで、親指タイプには苦手意識がありました。
W-ZERO3[es] や EM・ONE は、クリック感はあるものの小さくて堅い QWERTY キーで、
爪で押す必要があったり、ボタンが浅いので隣のキーを押してしまうことも多かったからです。
逆に W-ZERO3[es] のテンキー部分は指で押せる大きさで打ちやすいものでした。

同じように NetWalker のキーも指の腹で押せるし、親指ならぐらつくキートップでも確実に
打てるため、それなりに使いやすいことがわかりました。
キーを押し下げた場合の感触はいまいちで引っかかりがあるけれど、親指打ちも結構いけるんだと、
今更ながら気がついたというのが本音です。

ただ両手持ちの親指打ちだと、中央付近のキーは少々遠く感じます。
端ぎりぎりまでキーがあるし Fn や Ctrl を併用する操作は慣れがいるかもしれません。
親指タイプとタッチタイプの両用を欲張ったための弊害でしょう。

この辺の操作は今後のカスタマイズで何とでもなると思います。
むしろ変にメーカー固有のカスタマイズが入ってない素のパソコンキーボード状態なのは
好ましいことです。


●オプティカルポイント

マウス代わりに用いるデバイスです。
タッチパッドのように指でスライドしてカーソルをコントロールします。
反応が良くてきびきび動いてくれます。

PC-NJ70A の光センサー液晶パッドは反応が悪くて使いづらかったので、なおさらそう感じる
のかもしれません。また比較対象があれですが、PS3 ワイヤレスキーパッドのタッチパッド
モードより思い通りに動かせます。

デフォルト状態では少々カーソルの動きが速すぎる感じがします。ウィンドウの切り替えや
ダイアログのボタン、ブラウザの操作は快適なのですが、小さいアイコンやスクロールバーに
正確にあわせるのは苦手です。
使うアプリケーションや用途次第だと思いますが、個人的にはブラウザの操作だけなら
これで十分でした。

設定でマウスの速度や感度を多少いじっています。間違って「☆」マークに触れてしまうと
誤動作するので、スクロールモードに切り替わらないようこちらも封印しています。


●アプリケーション

開発環境を用意したり、アプリストアを設けたり、最近そういう試みが増えた気がします。
携帯 OS に限らずありとあらゆるプラットフォームで。
開発側、使う側、両方の敷居を下げる意味があるのでしょう。
iPhone のような盛り上がりをどこか期待している反面、専用アプリを用意しなければいけない
と言う現実問題がそこにあるのかもしれません。
結局開発者に投げてしまっているわけで、実際にアプリケーションがどれだけ充実するのか未知数です。

NetWalker は普通の Linux を採用したおかげで最初から豊富なアプリケーションが揃っています。
インストールも一覧から選択するだけで簡単です。
以前 WindowsCE の Handheld PC を使っていた頃も使えるアプリケーションソフトが限られて
いました。Desktop 向け Windows がそのまま動くなら何の心配もないのですが。

もう1つの利点は削除された機能がないことです。
モバイル向けの移植ではないので、操作や機能が簡略化されていたり、削られているような
ことがありません。

MID としてみたら上記 2点はメリットですが、UMPC としてみたらこれらはごく当たり前の
ことかもしれません。
そういう意味では NetWalker は MID のカテゴリとして売られているけど、
実質 ARM を使った UMPC の一種だと言えます。


●キーボード

個性だと割り切っています。


●最後に

NetBook が流行し多くの機種が販売されていますが、どれも EeePC に倣ったかのような
画一的なスペックで意外に個性がないように見えます。おそらくコストの事情もあるのでしょう。
本当なら用途を特定したような、もっとユニークなデバイスが登場しても良さそうです。
ASUS のキーボード一体型みたいな。

MID / UMPC にはまだお手本となる大ヒット機種がないせいか、比較的個性を出しやすいようです。

NetWalker を店頭で熱心に見ていると、店員が「おすすめしづらい」製品だと漏らしていました。
過去の経験に当てはまらず具体的な用途を想像しづらいのかもしれません。
「今まで無かった新しいもの」 かまたは 「本当に用途がわからないもの」 のどちらかでしょう。
うまく説明できないものこそ新しいジャンルを切り開く可能性もありますが
果たして NetWalker はどちらになるでしょうか。

個人的にはスマートフォンの機動性を確保しつつ十分使えるパソコンだと認識しました。


関連エントリ
Netwalker PC-Z1 買いました
タッチタイプの境界
NetWalker PC-Z1


NetWalker は Linux 搭載ミニ PC です。
ARM 系 CPU に Ubuntu がそのまま入っています。
本体は小さいながらキーボードを搭載し、約 400g と軽量でバッテリーも長持ちします。

逆に欠点も多く、価格が高めであること、限られたフラッシュ容量や Bluetooth が無いなど
スペックも低め、なかなか言うことをきかないキーボードなど、思ったより使う人を選びます。

はじめて触ったのは2日前。
ホームポジションにぎりぎり指が乗るだけのキーピッチがあるのに、あまりにも思うように
タイプできなくて店頭で 30分くらい奮闘。力を入れて意識して打てば
何とか文字抜け無しに打てるといったところ。
一時は諦めたけど、ずっと気になっていて結局買ってしまいました。


●外観

実測で 401g ありました。見た目は電子辞書。
Netbook と比べても PC らしからぬ印象なのは、通風口がまったく無いせいかもしれません。
ファンレスは当然として側面も裏面もスリット無しです。


●電源

スマートフォンやかつての Handhed PC のように、電源切断 = サスペンド (スリープ) 推奨
と書かれています。シャットダウンとの違いは同梱のパンフレットにも説明があります。
3秒起動なのはサスペンドからの復帰のこと。スリープを使えば Windows PC でも同じ
ですが、サスペンドを常用できるくらい省電力だということなのでしょう。

シャットダウン状態からの起動はそれなりに時間がかかります。
バッテリー動作で実測 1分45秒 かかりました。
この数値は Windows7 + Atom Z540 + 高速 SSD の Netbook のおよそ 2倍です。
OS の起動自体はそれほど速くありません。

初起動後は若干遅めで、アプリ起動だけでなくメニューやホットキーの動作なども
ワンテンポ遅れる印象がありました。最初だけだったようで、しばらく使って落ち着いたら
それなりのレスポンス速度で動いています。


●画面

高密度できれいに見えます。
最初から太めで大きなフォントが使われているため見やすい画面です。

5インチ で 1024x600 なので この の計算式に当てはめると 約 237.4 ppi 。
LOOX U に負けるけど VAIO type P よりは高密度です。
KOHJINSHA PM は 4.8 インチなのでさらに細かく 247.3 ppi 。


● Ubuntu

本当にそのまま Ubuntu です。起動画面も desktop も見慣れたものです。
あくまで個人的な話ですが、vim も入っているしキーバインドも普段通りなので、買って
何のソフト導入もカスタマイズもせずにすぐ使用できたのは初めてかもしれません。


●ポインティングデバイス

キーボードの右上、ヒンジとの間に OPTICALPOINT が埋め込まれています。
きちんと設定して使ったら結構反応も良く使いやすそうです。

店頭でたまにカーソルが動かない端末がありましたがその原因も判明しました。
オプティカルポイントには「カーソルモード」と「ホイールモード」の2種類があって、
ホイールモードだとカーソルが動かずスクロール動作になる仕組みです。
「☆」マークまたは Fn +[9] で切り替わります。
おそらく店頭の端末もホイールモードに切り替わっていたため。
カーソルが出っぱなしだと固まったように見えるので少々分かりにくいところです。

PC-NJ70A で苦労させれた光センサー液晶パッドよりはレスポンスも良好です。

そういえば画面がタッチパネルであることをすっかり忘れていました。


●クイックスタートボタン

キーボードとヒンジの間、電源ボタンの隣に描かれているアイコンはタッチ式のボタンに
なっています。デフォルトで起動の遅いアプリが登録されているので、間違って触れてしまうと
ちょっとしたダメージが。
設定で無効にすることが出来なかったので「デスクトップの表示」にしておきました。


●通信

内蔵している通信手段は無線 LAN のみ。
Bluetooth が入ってないのは寂しいですが、おかげでまだ Bluetooth キーボードやマウスに
逃げることなく使っています。

UQ WiMAX の USB アダプタは、Windows でも x64 未対応だったりと期待できないので
必要な時はモバイルルータ URoad-5000 に頼るつもりです。


●キーボード

タッチタイピングによる入力は相変わらず敷居が高いです。
キーを打った後に定位置に吸い込むような感触が無く、むしろ球面を押し込むような反発
とぐらつきがあります。
キーを押したあとの指の位置が安定しないので、隣のキーを押してしまったりホーム
ポジションが徐々にずれたりします。指を離したときもわかりにくい。
まだあまり速く打てないけどだいぶ慣れてきました。


●用途

現状お勧めしづらいし、キーボードも配列以前のところでかなり使いづらい印象です。
それでもぎりぎり打てるサイズなので敢えて使いこなしてみたくなります。
正直割と気に入っています。
ネット接続よりも今のところは無謀にも文字入力用の端末として考えています。

CPU は Cortex-A8 だし、もし AMD Z430 の OpenGL ES 2.0 が使えるなら
また違う使い方が出来るでしょう。


関連エントリ
タッチタイプの境界
NetWalker PC-Z1
OpenGL ES 2.0
WiMAX ルータ URoad-5000 (4) 充電の仕方


Direct3D 11 対応 GPU RADEON HD 5870/5850 が発表されました。
Windows7 発売に間に合うし、Direct3D 11 SDK も直前に RTM しています。
足並みが揃った良いタイミングです。
あとは安定して入手できるかどうか、ドライバが間に合うかどうかでしょう。

AMD、業界初DirectX 11対応GPU「Radeon HD 5800」シリーズ ~上位モデルは1,600SP搭載の2.7TFLOPS

最近は GPU の世代が代わり 機能が増えても一時的なパフォーマンスの低下は見られなく
なりました。GeForce 6800 や 8800 の時もそうでしたがむしろ倍のスコアを出しています。
速度を求める場合も機能が目的でも、安心して手を出せるのは嬉しいところです。

Direct3D 10 → Direct3D 11 の更新は API 的なデザインの変更は少ないものの、
Direct3D 8 → Direct3D 9 と同じように増えた機能はかなりあります。

・汎用性の向上 Compute Shader
・グラフィックス機能の進化 Tessellation, BC6H/BC7
・管理機能の強化 Dynamic Shader Linkage
・スレッド対応 Deferred Context


一見地味な Dynamic Shader Linkage や Deferred Context も、開発者にとっては
大変嬉しい機能です。
Dynamic Shader Linkage は描画発行時にシェーダー内の飛び先を決定することが出来ます。
いわば関数ポインタのようなもので、シェーダーの種類を増やさずに機能の切り替えを
容易にしてくれます。
Deferred Context は他のスレッドが発行した描画命令を Display List としてバッファリングし、
コンテキストに矛盾が生じないようにします。

シェーダーの機能切り替えも効率よいスレッドの活用も、これまではアプリケーション側で工夫して
実現する必要がありました。
つまり DirectX 11 では従来 CPU 側で行ってきたいくつかの分野もアクセラレーションの
対象となっているといえます。GPGPU に限らず GPU の役割は徐々に広がっているようです。

今後 CPU/GPU もお互いに相手の機能を取り込むだけではなく、より連携しやすいような歩み寄りも
必要ではないでしょうか。例えば CPU にも積極的に GPU 連携のための命令が追加されるとか。
SSE5 の half 型変換命令はその一つの好例かもしれません。

DirectX/GPU 年表 も更新しました。


関連エントリ
DirectX SDK August 2009 の解説と Direct3D 11 RTM
Direct3D11/DirectX11 (18) GPU を使ったアウトラインフォントの描画の(6)
Direct3D11/DirectX11 (13) TessFactor とシェーダーリンクの補足など
Direct3D11/DirectX11 (12) テセレータのレンダーステート他


昨日の表に仕様もいくつか追加してみました。

                 key pitch CPU                  RAM
KOHJINSHA PM       12.0mm  Atom Z510   1.1GHz 512MB  345g   7H  59800円
WILLCOM D4         12.2mm  Atom Z520   1.3GHz   1GB  460g 1.5H
HTC Shift          13.0mm  x86 A110    800MHz   1GB  800g   2H
Jornada680         13.2mm  SH3         133MHz  16MB  510g      138000円
Jornada720         13.2mm  StrongARM   206MHz  32MB  510g  10H  94800円
Libretto 20        13.5mm  DX4 75MHz    75MHz  20MB  840g   3H 178000円
NetWalker PC-Z1    14.0mm  Cortex-A8   800MHz 512MB  409g  10H  44800円
Loox U50X/V        14.0mm  x86 A110    800MHz   1GB  599g 3.8H 
Sigmarion II       14.1mm  MIPS VR4131 200MHz  32MB  500g  10H  
Sigmarion III      14.1mm  ARM PXA255  400MHz  64MB  455g  10H  
KOHJINSHA SK       14.2mm  Atom Z530   1.3GHz   1GB  720g 3.1H
Libretto 70        14.5mm  MMX Pentium 120MHz  32MB  850g   3H
Libretto 100       14.5mm  MMX Pentium 166MHz  64MB  950g   2H  
Loox U/C40         14.8mm  Atom Z530   1.6GHz   1GB  565g 4.2H
Libretto SS1000    15.0mm  MMX Pentium 166MHz  96MB  820g   3H
Libretto ff1100    15.0mm  MMX Pentium 266MHz 128MB  980g   3H
EeePC 901-X        15.5mm  Atom N270   1.6GHz   2GB 1100g 8.3H  59800円
VAIO type P 90     16.5mm  Atom Z540  1.86GHz   2GB  588g   4H
MobileGear MC/R530 16.5mm  MIPS VR4121 168MHz  32MB  770g  10H 110000円
MobileGear MC/R700 16.5mm  MIPS VR4121 131MHz  32MB 1100g   8H 155000円

RAM は搭載可能最大値、バッテリーは小または標準時。


関連エントリ
タッチタイプの境界


店頭で NetWalker PC-Z1 と KOHJINSHA PM (PM1WX16SA) を触ってきました。
タッチタイピング前提でどこまでキーを打てるか興味あったからです。

SHARP NetWalker
KOHJINSHA PM series

NetWalker はホームポジションに指を載せることが可能で、サイズの割にそこそこ
打てそうな印象でしたが予想以上に苦戦しました。なかなかタイピングのこつがつかめず
最初はキーの取りこぼしが多くてさんざんな結果に。
見かねた店員が 「両手でもって親指で打てばいいですよ」 とかアドバイスしてくれたほど。

キートップが不安定で、指先の感触だけでは入力出来たかどうか判断しづらいのが原因のようです。
文字を確実に打つには、指先の感触に頼らないで完全に底打ちするくらい押し込まなければ
なりません。

店頭の不安定な飾り台ではだめで、安定したテーブルの上に置いて指先に力を入れて強く
つぶし込む気持ちで打てば割と打てるようになりました。
ただこれだけ力を込めてタイプするなら、今まで敬遠しがちだった他のどのキーボードでも
使いこなす自信が付きそうです。

PM (mbook) はキーの感触が良く、NetWalker よりも軽く確実に打つことが出来ます。
問題はキーピッチの方で、ホームポジションにぎりぎり指を乗せられるか若干はみ出る感じです。
キー配列は PM の方が好みなのですが、やっぱり指が窮屈なのか速く打とうとすると
うまく文字を探せないことがありました。

キーピッチだけがすべてではないと思いますが、過去に使ったことがある端末および同系統の
機種を調べてみました。"*" は実際に所有して長時間使ったことがあるものです。

mbook M1/PM            12.0mm
Jornada680             13.2mm
Jornada720             13.2mm *
Libretto 20            13.5mm
NetWalker              14.0mm
Loox U50               14.0mm *
Sigmarion              14.1mm
Libretto 70            14.5mm
Libretto 100           14.5mm *
Loox U                 14.8mm
EeePC 901-X            15.5mm *
VAIO type P            16.5mm *
Mobile Gear II MC/R530 16.5mm *
Mobile Gear II MC/R700 16.5mm *
Let's Note R4          17.0mm *
PC Keyboard            19.0mm *

個人的に気に入っており、タッチタイプで打ちやすかったのが MC/R530 と Jornada 720。
当時はあまり意識しなかったけど Libretto 100 も普通に使っていた気がします。
今のところ 13mm 前後が境界線です。NetWalker のキーボードを確認するため、
長時間練習したことを考えれば 12mm も慣れたら何とかなるんでしょうか。

以下はキーピッチを調べる上で参考にしたページです。

PC watch Libretto 新機種
TOSHIBA Libretto 100 の発売について
製品マニュアル HP Jornada 720 ハンドヘルド PC
VAIO type P スペック
NECインフロンティア Mobile Gear Age
Wikipedia モバイルギア
Eee PC 1000H-X速攻レビュー
Panasonic レッツノートラインナップ比較表
工人舎「PM」~ワイシャツのポケットに入る超小型Windows PC
LOOX U カタログモデル仕様
Atom搭載「LOOX U」の進化ぶりを写真でじっくり解説する


2009/09/16
Mobile GPU

HYPERでんちの GPU 年表更新しました。
ほとんどが Mobile デバイスの追加です。
2009年になって一気に Shader 対応端末が普及したことがわかります。
この流れを推し進めているは OpenGL ES 2.0 。
OpenGL ES 2.0 は Direct3D 10 以降のように Shader パイプラインしか定義しておらず
GL ES 2.0 対応であればシェーダーベースの GPU であることがわかります。
デバイスの機能的にもシェーダーに進むそれなりの理由があるのでしょう。

例えば一部 GPU はすでにユニファイドシェーダーを採用しています。
ShaderModel 4.0 にかなり近いか、最初から 4.0 に対応し得る設計です。
シェーダーユニットの再利用は実行効率的にも優れており、ユニットの個数で
スケーラビリティを保ちやすいなどのメリットが考えられます。
また CPU の補助として GPGPU 的な使い方も視野に入れているようです。

他にも先行しているデスクトップ向け 3D ゲームの移植しやすさや同等のテクニックが
使えること、デスクトップ向け GPU の設計を流用できる、バス帯域の制約から演算の
比重を高めるなど、考えられる理由があります。

表を見ているとかつてのデスクトップ向け 3D アクセラレータが多数登場し、
各社多種のビデオカードがしのぎを削っていた頃を思い出します。
ほぼ 2強で占められたハイエンドと違い、様々な GPU が使われているようです。

電力も面積も限られているモバイル向け GPU にとってバス帯域は大きな問題です。
タイルベースで深度判定のみ先行し、最小限のフラグメントのみレンダリングする
PowerVR と同じように、AMD もチップ内の少量かつ高速な RAM を用いたタイルベース
レンダリングを行っているようです。この辺の工夫も非常に興味深いところです。

WindowsVista/7 のウィンドウが GPU 描画になったのと同じように、Mobile デバイス
でも 3D アクセラレータを用いた表現が重要となっています。
一般のアプリケーションも当たり前のように GPU を使っているなんて
3D アクセラレータが乗っていなかったり、乗っていてもドライバが無くて使えなかった
昔からは考えられないことです。

とはいえ今時の CPU は速いので、MI-Zaurus + CPU だけでレンダリングしていた頃を
考えれば CPU でも十分描画できるのかもしれません。

GPU 年表
Direct3D Mobile DeviceCaps 一覧
Zaurus GA 3D Engine


簡単なテストです。
出来るだけ描画面積を小さくしてほぼ頂点演算のみ。
単一マテリアルかつ共有頂点、48000ポリゴンの最適化していない素のモデルデータを
描画しています。Indexed + Triangle List で Strip 化や頂点キャッシュ用ソートを
していません。

VAIO Type P で描画してみました。
当初 OpenGL を使おうとしましたがドライバが未対応でした(↓)。

GL_VERSION: 1.1.0
GL_RENDERER: GDI Generic
GL_VENDOR: Microsoft Corporation
GL_SHADING_LANGUAGE_VERSION: (null)

D3D11 の CapsViewer で調べると Direct3D 11 の FEATURE_LEVEL_9 も未対応。
Direct3D 10 の FeatureLevel 9_1 には対応しています。
今回は昔作った Direct3D 9 のツール&シェーダーを使っています。
シェーダーも複数の機能を盛り込んで汎用化したもので、あまり最適化されていません。

・Atom Z540 + GMA500
・Windows7 RC で Aero off

この条件で上記モデルデータを 4個描画しておよそ 38fps。
結果だけ見ると頂点演算は 7.3M triangles/sec くらい。

Wikipedia PowerVR によると GMA500 は PowerVR SGX535 で 28M poly/s 。
記載されているピーク値の 1/4 くらいですが最初はこんなもんでしょう。

モデルを拡大して描画面積を広げるとあっという間に処理落ちします。
Unified シェーダーということもあって、おそらくピーク値は極端な値を示す傾向があると
考えられます。つまり実際のアプリケーションで使う場合、シェーダーユニットをピクセルにも
割り振る必要があるので、その分だけ数値は落ちます。
こんなにいい加減なテストでも PowerVR MBX Lite の性能値として記載されている頂点
演算速度よりはおそらく高速です。頂点だけに絞ればかなりの差が付きそうです。

また iPhone 3GS + GL ES 2.0 で同じモデルデータを描画したところほとんど同じ
結果になりました。4個描画時に 40fps、7.68M tri/s くらいです。
こちらの方が多少簡略化したフラグメントシェーダーを用いており、厳密に同一条件では
ありませんが、ピクセルの影響は少ないのでほぼ同じとみて良さそうです。

どちらも同じ SGX 535 相当であることが結果からも明らかになりました。
つまり Type P は、iPhone 3GS や iPod touch 3G とほとんど同じ能力の GPU で
8倍の面積を描画していることになります。

ちなみに GeForce/RADEON などデスクトップ PC 向け GPU だと、上記モデルは
100~150個 * 60fps 出ます。GPU グレード間の差が付かないので、ハイエンドだと
このテストは低負荷すぎるようです。


関連エントリ
VAIO type P + Windows7 RC で Direct3D 11
Intel GMA500 のスペックについて考える。続き (2)


Direct3D の HLSL コンパイラは Microsoft が用意しており、共通のバイトコードを
生成しています。
実行時はさらにドライバが GPU 毎のネイティブコードに最適化&変換を行っています。

一見二度手間ですが、最初の最適化は共通バイトコードへの変換時に済んでいます。
時間のかかる巨大なシェーダーでも事前に変換しておくことが可能だし、コンパイラ部の
最適化の恩恵はすべての GPU で受けられます。
ドライバも共通コードからの変換だけ行えばよいので、互換性も比較的維持しやすいのでは
ないかと考えられます。

その代わり一度バイナリにしてしまうと、コンパイルした時点のコンパイラの能力である程度
固定されます。あとからコンパイラが強化される可能性もあるし、GPU が ShaderModel 5.0
対応になっても、3.0 向けにコンパイルしたシェーダーで能力を出し切れるとは限りません。


OpenGL の GLSL の場合 API が受け取るのは生のソースコードです。
コンパイルそのものが GPU ドライバの役目となっていて、中間の共通コードがありません。
動的にコンパイルするメリットは常にハードに最適なプロファイルを選べることです。

逆にデメリットとしては、コンパイル時間がかかるのではないかとの懸念もありますが
もっと別の問題もあるようです。
今まで GeForce で組み立ててきたコードを RADEON で走らせてみました。
環境は下記の通り。

GeForce 9800GT 190.62 (GL 3.1 GLSL 1.4) WHQL
GeForce GTX280 190.57 (GL 3.2 GLSL 1.5)
RADEON HD 4760 Catalyst 9.9 (GL 3.1 GLSL 1.4)


● Version 指定

RADEON の GLSL では「#version」はソースコードの先頭にないとエラーになります。

・#version の前にプリプロセッサ命令があるだけでもだめ。
・glShaderSource() に複数のソースコードを渡している場合、最初のコードの先頭でないとだめ。2番目以降のソースコードには記述できない。

この挙動は OpenGLES 2.0 の GLSL と同じです。また GLSLangSpec.Full.1.40.05.pdf
を見ても、#version の前に許されるのはコメントとホワイトスぺースのみと記載されています。
こちらの動作の方が正解でした。

ただプリプロセッサ命令も許されていないので、複数のターゲット間で互換性ととる場合に
version 指定を分岐できないのは不便です。C 言語側の Shader Loader 部分に手を加えて、
独自のプリプロセッサを追加するなどの対策が必要かもしれません。


● precision 指定

OpenGL ES の GLSL から取り入れた仕様として precision 指定があります。
宣言時に変数が必要とする精度のヒントを与えます。
highp, middlep, lowp の 3段階ありますが

・GeForce は in/out 宣言の前に必要
・RADEON は in/out 宣言の後ろ書かないとエラー

RADEON の場合必ず何らかの精度指定が必須で、個別指定かまたはデフォルトの
precision 行が必要です。GeForce は無くても通ります。
とりあえず最初にデフォルト宣言をしておけばどちらでも通ります。
細かく個別に宣言をしている場合は注意。

precision highp float;

ドキュメントを見る限り RADEON の方が正しいようです。
全体的に GeForce の方が判定が緩く RADEON の方が厳密になっています。


GPU のドライバ毎にコンパイラの仕様が異なっている可能性を考えると、
Direct3D + HLSL のように共通化されている方が楽だと思います。
慣れるまではこれらの互換性の維持に苦労しそうです。


●OpenGL 3.1

現段階で OpenGL 3.2/GLSL 1.5 に対応しているのは GeForce 190.57 だけです。
RADEON で試すにあたって OpenGL 3.1/GLSL 1.4 で動作するように修正しました。

GeForce の場合、最初に wglCreateContext() で作った Context がすでに
OpenGL 3.x に対応していました。
wglCreateContextAttribsARB() で作り直さなくても動作します。
RADEON の場合 OpenGL 2.1 だったので、wglCreateContextAttribsARB() が必要です。
でもシェーダーバージョンは同じ。

// GeForce 190.57
// wglCreateContext()
GL VERSION: 3.2.0
GL RENDERER: GeForce GTX 280/PCI/SSE2
GL VENDOR: NVIDIA Corporation
GL SHADING_LANGUAGE_VERSION: 1.50 NVIDIA via Cg compiler
 ↓
// wglCreateContextAttribsARB()
GL VERSION: 3.1.0
GL RENDERER: GeForce GTX 280/PCI/SSE2
GL VENDOR: NVIDIA Corporation
GL SHADING_LANGUAGE_VERSION: 1.40 NVIDIA via Cg compiler

// GeForce 190.62
// wglCreateContext()
GL_VERSION: 3.1.0
GL_RENDERER: GeForce 9800 GT/PCI/SSE2
GL_VENDOR: NVIDIA Corporation
GL_SHADING_LANGUAGE_VERSION: 1.40 NVIDIA via Cg compiler
 ↓
// wglCreateContextAttribsARB()
GL_VERSION: 3.1.0
GL_RENDERER: GeForce 9800 GT/PCI/SSE2
GL_VENDOR: NVIDIA Corporation
GL_SHADING_LANGUAGE_VERSION: 1.40 NVIDIA via Cg compiler


// RADEON 9.9
// wglCreateContext()
GL_VERSION: 2.1.8918
GL_RENDERER: ATI Radeon HD 4600 Series
GL_VENDOR: ATI Technologies Inc.
GL_SHADING_LANGUAGE_VERSION: 1.40
 ↓
// wglCreateContextAttribsARB()
GL_VERSION: 3.1.8918
GL_RENDERER: ATI Radeon HD 4600 Series
GL_VENDOR: ATI Technologies Inc.
GL_SHADING_LANGUAGE_VERSION: 1.40


● Extension の判定

GeForce 190.57 を Version 3.1 で作り直した場合のはまりです。
Extension の判定で glGetString( GL_EXTENSIONS ) を参照していましたが、
Context を作り直すとエラーになります。
ドキュメントをよく見ると glGetString( GL_EXTENSIONS ) は古い仕様で、
OpenGL 3.x の場合は

glGetIntegerv( GL_NUM_EXTENSIONS, .. )
glGetStringi( GL_EXTENSIONS, .. )

を用いるのが正しいやり方のようです。
WGL_CONTEXT_FLAGS_ARB を 0 にしても
WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB がセットされてしまうことが
原因のようです。
190.62 では WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB を指定しない限り
大丈夫でした。


互換性周りはまだ慣れてないせいか、または出来るだけ共通で動かそうと欲張っている
せいか苦労しています。
Direct3D の場合問題になる互換性はハードウエア能力の差でした。
その後 Direct3D 10 では完全に足並みが揃って、GeForce も RADEON もほとんど
区別せずに同じシェーダーが動くようになっています。
OpenGL ではハードウエア能力の違いよりも、まだまだドライバの差が表面化している感じです。
API 仕様が決まっても、安定するまでしばらく時間がかかるのかもしれません。


関連エントリ
OpenGL の同期と描画速度の測定
OpenGL のはじめ方 (2) wgl
OpenGL 3.2 GeometryShader をもう少し使ってみる
OpenGL GLSL のバージョン
OpenGL 3.2 の GeometryShader
OpenGL のはじめ方


前回書いた BC6H/BC7 のデータ化けですが BC1/DXT1 でも同様の問題が発生する
ことがわかりました。texconvex.exe に何らかの問題があるようです。

DirectX SDK August 2009 付属 texconv.exe と texconvex.exe を、それぞれ
x86 と x64 で比較してみました。

変換スクリプト(バッチ)
set DXSDK_UTILBIN=${DXSDK_DIR}Utilities/bin
set BIN86=$DXSDK_UTILBIN/x86
set BIN64=$DXSDK_UTILBIN/x64

set CONV86=$BIN86/texconv.exe
set CONVEX86=$BIN86/texconvex.exe
set CONV86=$BIN64/texconv.exe
set CONVEX86=$BIN64/texconvex.exe

$CONV86       -ft dds -f DXT1      -m 1 -px dx9_dxt1_86_ t000.bmp
$CONV64       -ft dds -f DXT1      -m 1 -px dx9_dxt1_64_ t000.bmp
$CONVEX86 -10 -ft dds -f BC1_UNORM -m 1 -px dx10_bc1_86_ t000.bmp
$CONVEX64 -10 -ft dds -f BC1_UNORM -m 1 -px dx10_bc1_64_ t000.bmp
$CONVEX86 -11 -ft dds -f BC1_UNORM -m 1 -px dx11_bc1_86_ t000.bmp
$CONVEX64 -11 -ft dds -f BC1_UNORM -m 1 -px dx11_bc1_64_ t000.bmp

hexdump dx9_dxt1_86_t000.dds > dx9_dxt1_86_t000.txt 
hexdump dx9_dxt1_64_t000.dds > dx9_dxt1_64_t000.txt 
hexdump dx10_bc1_86_t000.dds > dx10_bc1_86_t000.txt 
hexdump dx10_bc1_64_t000.dds > dx10_bc1_64_t000.txt 
hexdump dx11_bc1_86_t000.dds > dx11_bc1_86_t000.txt 
hexdump dx11_bc1_64_t000.dds > dx11_bc1_64_t000.txt 

入力は 512x512 サイズの bmp。mipmap 無し。
texconv と texconvex で出力拡張子の大文字小文字がばらばらなのも気になるところ。

 131200  dx9_dxt1_64_t000.dds
 131200  dx9_dxt1_86_t000.dds
 131200  dx10_bc1_64_t000.DDS
 131200  dx10_bc1_86_t000.DDS
 131200  dx11_bc1_64_t000.DDS
 131200  dx11_bc1_86_t000.DDS

出力ファイルサイズはすべて同一です。つまり 512*512*2 = 131072 、
131072 + 128 = 131200 なので、128byte の DDS ヘッダのみ追加されている状態です。
DXT10 ヘッダ (Direct3D 10 拡張ヘッダ) は存在していないことになります。


バイナリを比較すると、texconv を用いた下記 2ファイルは完全に同一です。
使用したコンバータが x86 か x64 だけの違いなので、一致していないと困ります。

・dx9_dxt1_64_t000.dds
・dx9_dxt1_86_t000.dds

texconvex を用いた dx10~, dx11~ の各ファイルはお互いにどれとも一致しませんでした。
それどころかコンバータを実行するたびにバイト単位で相違が生じています。

↓正常なファイル (dx9_dxt1_86_t000.dds)

00000000 :  44 44 53 20 7c 00 00 00 07 10 00 00 00 02 00 00  DDS |...........
00000010 :  00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
00000020 :  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
00000030 :  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
00000040 :  00 00 00 00 00 00 00 00 00 00 00 00 20 00 00 00  ............ ...
00000050 :  04 00 00 00 44 58 54 31 00 00 00 00 00 00 00 00  ....DXT1........
00000060 :  00 00 00 00 00 00 00 00 00 00 00 00 00 10 00 00  ................
00000070 :  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
00000080 :  a7 5b 23 11 d7 3f 0a 8a 49 6c 84 19 5e de a0 ca ここからデータ
00000090 :  74 9d e6 31 fd 3b 2b ed 91 9d e5 29 fa 58 16 d7
000000a0 :  f6 ce c8 42 75 ff a8 af cc 84 e2 21 b7 aa f0 5e
000000b0 :  eb 8c 61 2a c2 ca 7b ea 57 df c2 21 2f ad f5 d5
000000c0 :  92 be e2 29 82 02 a9 55 93 be a0 19 5f fe 0a a2
000000d0 :  57 d7 e2 3a 77 ea a0 a2 32 ae e6 4a 25 95 b8 c2
000000e0 :  79 e7 e8 4a 2a 28 b7 a5 36 d7 26 5b d6 b8 00 e0
000000f0 :  d2 be e3 42 fc e2 e0 b7 4f be 82 32 ff d7 3a f8
00000100 :  cd b5 80 21 a5 ea 5c f7 6d 7c 81 21 aa 22 d5 df

↓問題のファイル (dx11_bc1_86_t000.dds)

00000000 :  44 44 53 20 7c 00 00 00 06 00 00 00 00 02 00 00  DDS |...........
00000010 :  00 02 00 00 00 04 00 00 00 00 00 00 01 00 00 00  ................
00000020 :  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
00000030 :  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
00000040 :  00 00 00 00 00 00 00 00 00 00 00 00 20 00 00 00  ............ ...
00000050 :  04 00 00 00 44 58 54 31 04 00 00 00 00 00 00 00  ....DXT1........
00000060 :  00 00 00 00 00 00 00 00 00 00 00 00 00 10 00 00  ................
00000070 :  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
00000080 :  00 00 00 00 00 00 00 00 24 2a 00 0e 24 86 08 00
00000090 :  80 00 4d 02 00 00 00 00 20 da 0d 00 00 00 00 00
000000a0 :  74 9d e6 31 fd 3b 2b ed 91 9d e5 29 fa 58 16 d7
000000b0 :  f6 ce c8 42 75 ff a8 af cc 84 e2 21 b7 aa f0 5e
000000c0 :  eb 8c 61 2a c2 ca 7b ea 57 df c2 21 2f ad f5 d5
000000d0 :  92 be e2 29 82 02 a9 55 93 be a0 19 5f fe 0a a2
000000e0 :  57 d7 e2 3a 77 ea a0 a2 32 ae e6 4a 25 95 b8 c2
000000f0 :  79 e7 e8 4a 2a 28 b7 a5 36 d7 26 5b d6 b8 00 e0
00000100 :  d2 be e3 42 fc e2 e0 b7 4f be 82 32 ff d7 3a f8

・データ本体の先頭 16byte が欠けている
・データ本体の先頭に 32byte のゴミデータが挿入されている
・データ本体が 16byte 下がっている

これはまだましな方です。
dx10_bc1_64_t000.dds の場合 224byte の不明なデータが挿入されていました。
この症状は、以前下記エントリで試した texconv10.exe の頃と変わっていないような気がします。

Direct3D10 と DDS テクスチャフォーマット

ヘッダ部分の相違は下記の通り。こちらは特に問題は無いです。

・texconv (dx9)
dwFlags= 0x00001007 = DDSD_PIXELFORMAT|DDSD_WIDTH|DDSD_HEIGHT|DDSD_CAPS
dwPitchOrLinearSize= 0       (DDSD_LINEARSIZE が無いので無視できる)
dwDepth= 0          (DDSD_DEPTH が無いので無視できる)
dwMipMapCount= 0    (DDSD_MIPMAP が無いので無視できる)

・texconvex (dx11)
dwFlags= 0x00000006 = DDSD_WIDTH|DDSD_HEIGHT
dwPitchOrLinearSize= 0x400   (DDSD_LINEARSIZE が無いので無視できる)
dwDepth= 1          (DDSD_DEPTH が無いので無視できる)
dwMipMapCount= 1    (DDSD_MIPMAP が無いので無視できる)

・共通
dwSize   = 0x007c = 124 = ヘッダサイズ
dwWidth  = 0x0200 = 512
dwHeight = 0x0200 = 512
dwPfSize = 0x0020 = 32  = PIXELFORMAT サイズ
dwPfFlags= 0x0004 = DDPF_FOURCC
dwFourCC = "DXT1"
dwCaps   = 0x1000 = DDSCAPS_TEXTURE

texconvex で出力フォーマットの指定を R8G8B8A8_UNORM にするとデータが全部ゼロで埋められてしまいます。


関連エントリ
DirectX SDK August 2009 の解説と Direct3D 11 RTM
Direct3D10 と DDS テクスチャフォーマット


DirectX SDK August 2009 が出ています。
ついに Direct3D 11 の RTM 対応となりました。

DirectX SDK (August 2009)

D3D11 自体は Windows 7 や Windows SDK for Windows7 で RTM 版が出ています。
デバッグ向けライブラリや D3DX、ツールを含んだ SDK としてはこれが最初です。
これで安心して Windows7 RTM 環境へ移行できそうです。


●大きなトピック

予想通りベータ版で欠けていた機能が追加されました。
例えば

・Effect (fx)
・BC6/7 対応コンバータ

特に Effect はベータに入っていてもおかしくない大きなコンポーネントです。
もしかして SDK のリリースが遅れた原因もこのあたりでしょうか。

マニュアルの完成度は高く、大幅に更新されているようです。
ドキュメントも March SDK で足りなかった各項目が埋まっており、説明も増えて結構
力が入っています。かなり好印象です。

また今回新たに明らかになった点もいくつかあります。特に次の 2つ


● FeatureLevel 9_3 は ShaderModel 2.0 だった

最初マニュアルの表記ミスかと思いました。
CapsViewer でも確かに 2.0 と表示されるので間違いではないようです。

11_0   sm5.0
10_1   sm4.1
10_0   sm4.0
 9_3   sm2.0   (4_0_level_9_3) << ここ
 9_2   sm2.0   (4_0_level_9_1)
 9_1   sm2.0   (4_0_level_9_1)


● CapsViewer が復活した!!

FeatureLevel 登場時から結局 caps と同じことではないかと心配しましたが、
今回のリリースではそれを正式に認めたようなもの。
本当に CapsViewer に組み込まれてしまいました。

DXCapsViewer.exe を起動すると DXGI 1.1 Devices の欄が追加されています。
それぞれの FeatureLevel 毎に対応機能やフォーマットなど、各ドライバの対応状況を
確認することが出来ます。

d3d11_capsviewer.png


●D3DCSX が追加された

D3DX の ComputeShader 版です。prefix は D3DX のまま。
D3DX*.dll, D3DCompiler*.dll のように、番号付きの dll が今後追加されていく
ことになるようです。


●バージョンと互換性

SDK のメジャー番号は 9 のままです。
厳密には DirectX 9 SDK の中に Direct3D 10 や Direct3D 11 が含まれている
扱いになります。とはいえ、便宜上 DirectX 11 と表記することも多いです。

DirectX だけでなく Direct2D や DirectWrite も含まれています。
Vista でも動作します。


●ID3D11DeviceContext の更新

・追加
ID3D11DeviceContext::CopyStructureCount()

・削除
ID3D11DeviceContext::GetTextFilterSize()
ID3D11DeviceContext::SetTextFilterSize()

以前 DebugLayer が動かなかった原因はこの辺にありそうです。


● D3D11_PRIMITIVE の追加

enum D3D11_PRIMITIVE が追加されています。
似たようなシンボルとしてすでに D3D11_PRIMITIVE_TOPOLOGY が存在しています。

定義を見ると STRIP 形式が外されているようです。D3D11_PRIMITIVE は唯一
D3D11_SHADER_DESC の中で用いられています。考えられる用途は下記の通りです。

・D3D11_PRIMITIVE_TOPOLOGY ( strip あり )
  IA や SO など strip 形式を含めたプリミティブの指定。

・D3D11_PRIMITIVE ( strip 無し )
  GeometryShader / HullShader など、頂点ストリームやキャッシュを通過し、
  Triangle (Primitive) 単位で入力が必要なシェーダーの指定で用いる。


● BC6H/BC7 texture

新しい圧縮テクスチャフォーマットです。繰り返しになりますが、従来 DXT と
呼ばれていたフォーマットは Direct3D 10 より BC に名称が変わっています。

BC1 4bpp ← DXT1
BC2 8bpp ← DXT2/3
BC3 8bpp ← DXT4/5
BC4 4bpp ← 1チャンネル圧縮
BC5 8bpp ← 2チャンネル圧縮, 法線圧縮 (3Dc/ATI2)
BC6 8bpp ← (BC6H_UF16, BC6H_SF16)
BC7 8bpp ← (BC7_UNORM, BC7_UNORM_SRGB)

付属のコマンドラインツール texconvex.exe を使うと実際に BC6H/BC7 で圧縮できる
ようになりました。DxTex.exe などそれ以外のツールは Direct3D 9 のテクスチャしか
扱えないので要注意です。

実際に試しましたが、圧縮は非常に低速です。
最初 1600x1200 のデータを渡してしまってかなり後悔しました。
結局中断して 640x480 でやり直したくらい。それでも結構待たされています。
Core i7 920 なのに。

リファレンスデバイスを指定したアプリを作れば一応読み込むことが出来ました。
ただデータの一部が化けており正しく出ていない可能性があります。
DXT10 形式の dds データ化けは以前もあったので、使い方のミスかツール側の
問題かもしれません。

DXT10 ヘッダを追加した dds テクスチャは D3D10 で登場したものの、対応したツール
はまだあまり出ていません。128byte + 20byte と中途半端なヘッダサイズになって
しまうため、一括ロードだと SSE 対応も難しいという欠点があります。


●残るは GPU (とドライバ)

とりあえず 9_3 の Shader Model 2.0 と、DXCapsViewer には驚きました。

Direct3D 8 の改革が Direct3D 9 で完成したように、
Direct3D 10 で目指したものの完成形が Direct3D 11 だといえるでしょう。

手持ちのコードは March 2009 から Direct3D 11 に移行しましたが、互換性も取れる
ようになったし、かなり使いやすい印象です。
あとは対応 GPU が発売されるのを待つばかり。


関連エントリ
Direct3D11 Windows7 RTM と DebugLayer
Direct3D11/DirectX11 ComputeShader 4.0 を使う
DirectX SDK March 2009
Gamefest2008 と Direct3D 11
Direct3D10 と DDS テクスチャフォーマット


そろそろパフォーマンスの測定もしようと思い同期周りを調べてみます。
ゲームでは CPU と GPU の同期の設計が重要です。描画パフォーマンス優先なら CPU と
GPU ができるだけ並列動作するようにスケジューリングしておきます。
動的な頂点生成で CPU からデータを転送する場合、またはクエリによって GPU の結果を
CPU で受け取る場合など、お互いに待っている時間をなくすようにします。
ここで間違うとパフォーマンスに大きく影響を与えることがあります。


●インターバルの設定

Direct3D 同様に描画時間の管理を自前で行っていたのですが、ウエイト無しにしても
必ず 60fps 前後で止まってしまうことに気がつきました。
ヘッダを見ていたら wglSwapIntervalEXT() を見つけたので使えるか確認。
GeForce GTX280 + 190.57 、"WGL_EXT_swap_control" が含まれています。

wglSwapIntervalEXT( 0 );

これで Display と同期せずに最大速度で描画するようになりました。
モニタのリフレッシュレート依存だと思いますが、 1 を与えると 60fps 固定、
2でその半分となりました。


●フェンス

D3D では ID3D11Query を用いて GPU の状態を受け取ることが出来ます。
レンダリングしたピクセル数、演算頂点数といったレンダリング結果の問い合わせだけでなく、
同期のための Event (Fence) や TimeStamp も含まれています。

OpenGL にも glBeginQuery() ~ glEndQuery() があります。
こちらはもともと実際にレンダリングした pixel 数を返す Occlusion Query の機能だった
ようです。Direct3D にも昔からあるもので、この手のオクルージョン判定は高速化のためと
言うよりレンズフレアの描画などに用いられていました。

同期用の機能は別の API になっていて、glSyncFence() を使います。
fence は描画コマンドと同じように PushBuffer (CommandBuffer) に記録されます。
レンダリングが fence に到達すると、対応する同期オブジェクトを Signal 状態にします。
CPU では同期オブジェクトの状態を見ることで、描画の進行具合と同期を取ることが出来ます。

// 挿入
GLsync  SyncObject;
SyncObject= glFenceSync( GL_SYNC_GPU_COMMANDS_COMPLETE, 0 );

// 同期待ち
glClientWaitSync( SyncObject, 0, 0 ); // no wait
glWaitSync( SyncObject, 0, GL_TIMEOUT_IGNORED );

// 削除
glDeleteSync( SyncObject );

glClientWaitSync() は Timeout 時間を指定可能で、状態を返すことが出来ます。
glWaitSync() は Signal 状態になるまで、つまり完了するまでブロックします。
glSynciv() を用いて、現在の状態を参照することも出来るようです。

SwapBuffers() の動作をまだ厳密につかんでいないので、これで正しいかどうか
必要なのかどうかもわかりませんが、同期の例としてはこんな感じで。

static GLsync  SyncObject[2];
static int     SyncCurrent= 0;
static int     FenceCount= 0;

void Render()
{
    ~

    SyncObject[SyncCurrent]= glFenceSync( GL_SYNC_GPU_COMMANDS_COMPLETE, 0 );

    SwapBuffers( hDC );

    Current^= 1;
    if( FenceCount ){
        glWaitSync( SyncObject[SyncCurrent], 0, GL_TIMEOUT_IGNORED );
        glDeleteSync( SyncObject[SyncCurrent] );
    }
    FenceCount= 1;
}


●速度の測定

当初 TimeStamp が見つからなかったのですが Extension の方にありました。
GL_EXT_timer_query です。GeForce は対応していました。

glBeginQuery() ~ glEndQuery() が拡張されていて、GPU 処理の経過時間を
計測することが出来ます。

// 作成
const int maxid= 8;
GLuint  idbuffer[maxid];
glGenQueries( maxid, idbuffer );

// 削除
glDeleteQueries( maxid, idbuffer );

// 計測
glBeginQuery( GL_TIME_ELAPSED_EXT, idbuffer[0] );
~ // 計測期間
glEndQuery( GL_TIME_ELAPSED_EXT );

// 結果が準備できているか調べる&待つ
GLuint   qret= FALSE;
do{
    glGetQueryObjectuiv( id, GL_QUERY_RESULT_AVAILABLE, &qret );
}while( qret != TRUE );

// 計測結果の受け取り
GLuint64EXT    data= 0;
glGetQueryObjectui64vEXT( id, GL_QUERY_RESULT, &data );

Begin ~ End 区間に発行した GPU 命令の実行時間が返ります。値は nano 秒です。

Direct3D とは少々使い方が異なります。D3D11_QUERY_TIMESTAMP の場合は絶対時間、
つまり GPU クロックの参照に相当し、かつ単位は GPU/ドライバ によって異なります。
また計測期間が連続しているとは限らないため、その結果も受け取る必要があります。
D3D11_QUERY_TIMESTAMP_DISJOINT はこれらの追加情報を返します。

実際に用いる場合は query を 2重化して、1~2フレーム前の結果を受け取るようにします。
これで数値がとれるようになったので、あとは実際に試すだけです。

glquery00.jpg
↑計測の例

OpenGL ES にはこの手の API が無いので、出来れば一通り追加して欲しいところです。


関連エントリ
OpenGL のはじめ方 (2) wgl
OpenGL 3.2 GeometryShader をもう少し使ってみる
OpenGL GLSL のバージョン
OpenGL 3.2 の GeometryShader
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 シェーダー管理


iPhone 3GS 使っていて久しぶりに Safari が落ちました。
iPhone の偉いところは、一切ためらわずにアプリが即時落ちること。
この潔さは心地よいとすらいえます。

これが WindowsMobile や PocketPC だと…

・なんだかアプリが重くなってくる
・反応が鈍いなと思いつつ粘ろうとする
・しばらく円グラフがぐるぐる回って操作できなくなる
・さらにしばらく待って完全に固まったことを確認
・諦めてリセットボタンを押す
・起動するまでじっと待つ。1分以上かかる。

この間、実に数分間を無駄にすることになります。

iPhone では問題が発生したアプリが即時終了してくれるおかげで

・待たせない

  ユーザーが操作を選択できる状態に戻ります。
  すぐに同じアプリを立ち上げてページを開き直すことも出来ます。
  また落ちる可能性はありますが、不安定な状態で我慢して使い続けるよりは
  起動し直した方がましなはずです。
  もしくは、別の同等のアプリに切り替えるという選択も出来ます。
  何らかの対策ができるということ。

・システムを巻き込まない

  とりあえずすぐに電話受けられる状態に戻ってくれます。
  無限ループのようなリセットしなければならない状態は回避されます。

もちろん落ちないのが一番良いわけで根本的な解決はなっていませんが、
リセットするくらいの困った状況はほとんど無くなりました。

回復不可能なのに下手にエラーメッセージが出て待たせたり、固まったりするのは
一番避けるべきことだったのだと思い知らされました。


2009/09/08
NetWalker PC-Z1

SHARP から新しい端末 NetWalker PC-Z1 が出ます。
小型のノート PC の外観とスマートフォン向け CPU を組み合わせたもの。
かつて似たようなデバイスがあったことを思い出します。

ARM や MIPS など組み込み向けの CPU を搭載し、小型でバッテリー寿命長くて
キーボードを搭載した Handheld PC。WindowsCE H/PC Pro や H/PC 2000 のことです。
ポケットに入る小型なものから 9インチ 800x600 の液晶を持ったノート PC クラスまで、
結構いろんなタイプのハードが揃っていました。
普通の PC との違いは HDD が無く軽量の専用 OS を載せていること。
タッチを用いるなど操作性も違うし、動くソフトウエアも限られるけど、
普段からサスペンド状態ですぐ電源が入るなどそれなりに使う意味がありました。

今回の NetWalker が昔の WindowsCE を搭載した H/PC と違うのは
一般の Desktop / Notebook PC 向けにリリースされている Linux 環境を乗せていることです。
Linux ディストリビューションの一つ Ubuntu をそのまま採用しています。

実際ハード的にも ARM Cortex-A8 800MHz に RAM 512MB + SSD。
最初の Netbook もこれくらいの中身だったし、スペック的にも本当に Netbook に
近いことになります。

・初期の Netbook と同等のスペックを持つ
・デスクトップ向けの汎用 OS を使っている

これらが昔のハンドヘルド PC との大きな違いといえるでしょうか。
そこで今回はあえて NetWalker を ARM を使ったノート PC であると定義することにします。

ここで問題となるのは、Atom を使った 小型の PC とほとんど変わらなくなってくるということ。
例えば同じ Linux を動かすにしても、x86 の方が選択肢が多いし情報も多いし、ハードや
ソフトの対応状況を考えても有利ではないかと言うことです。x86 の小型の UMPC とか
Netbook とかに Linux を入れてもほぼ同じように使えるのではないかと考えられます。

それじゃ NetWalker の立場はどうなるのかといえば、それはおそらく
日本のメーカーがはじめて Linux (Ubuntu) だけ に対応した PC を売ろうとしている事実。
(すでに過去に存在していたらごめんなさい)

選択肢として Linux も選べるのではなく最初から Ubuntu だけ。
家電や携帯電話のように内部で利用しているわけではなく、デスクトップとして載せています。
x86 でないからこそできた決断だし x86 だったらメーカーもユーザーもここまで
割り切れなかったのではないでしょうか。

ARM シリーズの性能向上と Atom の省電力化により使われ方も近づいています。
そんな中、ARM を使った PC の特徴となり得るのは Windows を入れたくならないこと
ではないかと、NetWalker を見て思いました。

今後どのような展開になるか興味あるところです。


いくつか複数の問い合わせが重なったので更新してみました。
使用期間が短いため問題ある場合は引き続き旧バージョンをお使いください。

touchkeysip v1.11 ソフトウエア入力パネル

●複数の sip 登録

オプション画面を拡張しました。オプション画面の開き方は次の通り。

  スタートメニューから
    設定 → 入力 → 入力方法: touchkeysip → オプション

右上のプルダウンメニューで「Sip 0」~「Sip 2」の切り替えが出来ます。
3 種類それぞれに対して個別に

 ・Script Path / 割り当てるスクリプトファイル名
 ・Enable / 有効・無効 切り替え

の設定が出来ます。有効にすると Sip 一覧に touchkeysip 1, 2 のエントリが表示
され、それぞれが独立した Sip のように振る舞います。

touchkeysip111_01.jpg
↑EM・ONE S01SH (WM6) の場合

touchkeysip111_02.jpg
↑Touch Diamond (S21HT) の場合

複数有効にしてもインスタンスは共有されるので、追加のメモリ消費はほとんど
無いはずです。現在選択している SIP 以外のりソースは解放されるので、
メモリに読み込まれているスクリプトも常に 1つだけです。

これで複数のキーボードデータを使い分けることが出来ると思います。
レジストリへ登録する CLSID を増やすだけなので、原理的にはいくらでも増やすことが出来ます。


●回転時の動作

T-01A で画面回転時に位置が不定になるとの報告をいただきました。
SIP を閉じた状態で画面を回転すると、センタリング位置が不定になる問題は修正
しました。

ただし、T-01A で発生している症状は他の機種では再現しないため、根本的に直って
いるかどうかはわかりません。現物がないと原因を特定できないかもしれません。
WindowsMobile 6.x ではタッチ用に UI が変更またはカスタマイズされており、
細かい動作が端末によって異なっている可能性があります。


●消費メモリの軽減

画像読み込み時のみ必要な dll はすぐに解放するようにしました。
bmp 指定時は dll を使わずに古いローダーを用いるように変更しました。


●パネル

スクリプトでパネルを切り替える方法について質問をいただきました。
以下その説明です。

画面(パネル)切り替えという概念は、script の機能を用いて仮想的に実現しています。
各パネルの機能を作るには下記のデータが必要となります。

・ディスプレイリスト
・イベントテーブル

スクリプトでは、この 2つデータを切り替えることでパネルを変更しています。


◎画像データ

パネルの絵は LoadBitmap コマンドで読み込み、必要なタイミングで描画しています。
描画は自分で行う場合と、ウィンドウシステムが必要なときに勝手に描画する場合が
あります。

例えば上に重なった別のウィンドウを閉じたとき、下のウィンドウの絵を復元しなければ
なりません。
このように再描画が必要になった場合に、どのデータを画面に描画するのかあらかじめ
登録しておくのが「ディスプレイリスト」です。


◎ディスプレイリストの登録

SetDisplayList 命令を使います。
画像の転送元や転送範囲、転送先の座標を登録するだけです。
複数登録しておくと順番に描画します。

SetWindowDisplayList 命令を使って、ウィンドウ毎にディスプレイリストを割り付ける
ことが出来ます。


◎直接描画

ディスプレイリストを登録してもすぐには画面が書き換わりません。
再描画が必要なタイミングにならないと呼び出されないからです。

アニメーションの表現や、パネル切り替えなどで即座に画面を書き換えたい場合は
直接描画する命令を使います。
DrawDisplayList は、指定したディスプレイリストをその場で直接描画する命令です。


◎描画のまとめ

ディスプレリストとは描画手順のこと。

SetDisplayList 命令で登録しておくことができます。

描画手順は二通り。この二つを状況に応じて使い分けることになります。

・ディスプレイリストをウィンドウに割り当てる SetWindowDisplayList
  再描画が必要になったら勝手に呼び出して描画してくれます。
  元の描画に戻す手順です。登録しておかないと再描画で元に戻らなくなります。

・その場で画面に書き込みたいなら DrawDisplayList
  すぐ描画します。一度限りなので、他のウィンドウで消されても戻りません。
  アニメーションや、パネル切り替えなどその場で更新したい場合に使います。


◎イベントテーブル

どの座標をタッチしたらどの関数を呼び出すのか、このようなアクションを登録して
おけるのがイベントテーブルです。

talbe <名前>
dataw <ボタンの個数>
dataw EVENT_DOWN  0 16  32  32  FuncPushA  0  0
~
endtable

テーブルは座標範囲と呼び出す関数名、関数に渡す引数で構成されています。
これをボタンの数だけ並べておけば、タッチしたときに任意のスクリプトが走ります。

実際の動作は関数依存なので、任意の文字を送信してもいいし、パネル切り替えなどの
機能を作ることも出来ます。

イベントテーブルは複数作っておくことが出来るので、パネル毎に設定し直すだけで
動作を好きなように変えられるわけです。

テーブルはウィンドウ毎に指定可能で SetEventTable 命令を使います。
SetEventTable には一度に複数のテーブルを与えることが出来ます。
複数のパネルで同じように使うボタンがあれば、イベントテーブルを共通化しておくことが出来ます。


◎ウィンドウ

CommandManual.txt にも説明がありますが、ウィンドウという概念を持っています。
メインパネルはウィンドウの 0 番 (= WIN_MAIN) に相当します。

サブウィンドウを開くことが出来るので、例えばポップアップ小さい画面を重ねて
情報を出すような表現を作ることが出来ます。

WindowsMobile に最初から入っている SIP はメインパネルだけで出来ています。
touchkeysip も複雑なことをしなければ WIN_MAIN だけで sip として機能します。


◎パネルを切り替える方法

ほぼ下記の命令をセットで用いています。
サンプルスクリプトなどで探してみてください。

SetDisplayList ~       # 描画手順の変更、再登録や書き換えなど
SetWindowDisplayList ~ # ウィンドウに DisplayList を割り当て(変更無ければ不要)
DrawDisplayList ~      # その場で画面を書き換えて表示を更新
SetEventTable ~        # ボタンを押したときの動作を変更する

現在選択しているパネルの状態は、適当なグローバル変数を使って管理しています。


関連エントリ
WindowsMobile touchkeysip v1.10 加速センサーで文字入力


Windows で OpenGL を使う方法がだんだんわかってきました。
DirectX ならとりあえず新しい SDK を入れれば終わりですが、OpenGL の場合は少々違います。
特別な SDK や lib が無くても、ドライバさえ対応していれば使うことができます。

敢えて OpenGL の各種ライブラリは使用していません。便利で容易に任せられる反面、中で何を
やっているのか、最新バージョンを使うにはどうしたらいいのかがわかりにくかったからです。

(1) OS と OpenGL のやりとりは WGL が行う。
(2) Windows SDK には OpenGL 1.1 のヘッダ&lib のみ。最小限。
(3) 新しい OpenGL に対応している GPU ライバをインストールする。
(4) ヘッダファイルは OpenGL Registry からダウンロードできる。
(5) wglGetProcAddress() を使って自分で最新の API を取得する。

API の取得は以前行ったとおり。ここ数日、実際に OpenGL 3.2 で追加された機能を
試すことが出来ました。今度は初期化部分、wgl まわりをもう少し詳しく調べてみます。

MSDN Using OpenGL on Windows NT/2000 and Windows 95/98


●ピクセルフォーマットと初期化

Direct3D と同じように、とりあえず Win32 API でウィンドウを作っておきます。
OpenGL の Context (HGLRC) を作るには HDC が必要です。

そのまま wglCreateContext() を呼び出してもエラーになります。
OpenGL が対応しているピクセルフォーマットになっていないことが原因でした。
あらかじめフレームバッファのフォーマットを指定しておきます。

現在対応しているフォーマットは下記の方法で取得可能です。

HDC hDC= GetDC( hWnd );

// 現在対応しているフォーマットの数を参照する
int format_count= DescribePixelFormat( hDC, 0, 0, NULL );

// 列挙する
for( int fi= 1 ; fi<= format_count ; fi++ ){
    PIXELFORMATDESCRIPTOR   pformat;
    DescribePixelFormat( hDC, fi, sizeof(PIXELFORMATDESCRIPTOR), &pformat );
}

GeForce 190.57 で実際に列挙してみました。126個あります。

GeForce 190.57 PIXELFORMATDESCRIPTOR 一覧

最後の方に W と GL 両方付いているものが Window 描画対応かつ OpenGL で使える
フォーマットです。

MSDN Choosing and Setting a Best-Match Pixel Format の例では
Color=24, Depth=32 を指定していますが、Direct3D を考えても、上の列挙の結果を見ても、
あまり一般的とはいえません。

今回は下記のピクセルフォーマット指定を行いました。
ChoosePixelFormat() を使うとフォーマットのマッチングを自動で行ってくれるようです。

static PIXELFORMATDESCRIPTOR   pformat= {
    sizeof(PIXELFORMATDESCRIPTOR),
    1,
    0
    |PFD_DRAW_TO_WINDOW
    |PFD_SUPPORT_OPENGL
    |PFD_DOUBLEBUFFER
    ,
    PFD_TYPE_RGBA,
    32,     // color
    0, 0,   // R
    0, 0,   // G
    0, 0,   // B
    0, 0,   // A
    0, 0, 0, 0, 0,      // AC R G B A
    24,     // depth
    8,      // stencil
    0,      // aux
    0,      // layertype
    0,  // reserved
    0,  // layermask
    0,  // visiblemask
    0   // damagemask
};

HDC hDC= GetDC( hWnd );

// ピクセルフォーマットの選択
int pfmt= ChoosePixelFormat( hDC, &pformat );
SetPixelFormat( hDC, pfmt, &pformat );

// OpenGL コンテキストの作成
HGLRC hGLRC= wglCreateContext( hDC );
wglMakeCurrent( hDC, hGLRC );

エラー判定を省いていますが、初期化手順は上記の通りです。
hDC, hGLRC は保存しておきます。終了時のコードは下記の通りです。

if( hGLRC ){
    wglMakeCurrent( NULL, NULL );
    wglDeleteContext( hGLRC );
    hGLRC= NULL;
}
if( hDC ){
    ReleaseDC( hWnd, hDC );
    hDC= NULL;
}


●描画ループ

wglMakeCurrent() で hGLRC を指定すれば、以後描画のために gl ~ API を
用いることが出来ます。Double Buffer を指定したので毎フレームの描画の最後に
SwapBuffers() を入れます。Direct3D の Present() に相当します。

    // フレームバッファクリア
    glClearColor( 0.0f, 0.0f, 0.3, 0.0f );
    glClearDepth( 1.0f );
    glClearStencil( 0 );
    glClear( GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT );

    // 描画など
    ~

    // flip
    SwapBuffers( hDC );


●新しい Context の作成

基本的な WGL と OpenGL の関係は上記の通りです。

さらに新しい API を使う場合、いくつか追加の手順が必要となります。
まず拡張 API にはいくつか種類があります。

・新しいバージョンの OpenGL core API
・OpenGL 拡張 API
・WGL 拡張 API

これら拡張 API を使う方法はどれも同じです。ヘッダファイルと対応ドライバを手に入れたなら、
あとは wglGetProcAddress() を用いるだけ。
wglGetProcAddress() 関数は、wglCreateContext() のあと wglMakeCurrent() で
hGLRC を設定した後でないと呼び出すことが出来ません。

(1) OpenGL Registry から wglext.h をダウンロードしておきます。
(2) 通常の手段で Context を作成し wglMakeCurrent() しておきます。
(3) WGL の拡張 API 、wglCreateContextAttribsARB() を取り出します。
(4) wglCreateContextAttribsARB() を使って HGLRC を作り直します。

HDC hDC= GetDC( hWnd );

// ピクセルフォーマットの選択
int pfmt= ChoosePixelFormat( hDC, &pformat );
SetPixelFormat( hDC, pfmt, &pformat );

// OpenGL コンテキストの作成
HGLRC hGLRC= wglCreateContext( hDC );
wglMakeCurrent( hDC, hGLRC );

// API 取り出し --- (A)
PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB= (PFNWGLCREATECONTEXTATTRIBSARBPROC)wglGetProcAddress( "wglCreateContextAttribsARB" );

// 使用する OpenGL のバージョンとプロファイルの指定
static const int  att[]= {
   WGL_CONTEXT_MAJOR_VERSION_ARB,   3,
   WGL_CONTEXT_MINOR_VERSION_ARB,   2,
   WGL_CONTEXT_FLAGS_ARB,           0,
   WGL_CONTEXT_PROFILE_MASK_ARB,    WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
   0,
};

// 新しい HGLRC の作成
HGLRC   hglrc= wglCreateContextAttribsARB( hDC, NULL, att );
wglMakeCurrent( hDC, hglrc );

// 古い HGLRC の削除と置き換え
wglDeleteContext( hGLRC );
hGLRC= hglrc;

// 新しい Core API の取り出し
InitOpenGL();

InitOpenGL() については こちら を参照してください。

なお GeForce で試している分には、特にこの手順を踏まなくても OpenGL 3.2 の命令が
使えるし描画もできています。

上の例では wglCreateContextAttribsARB() だけ個別に取り出していますが、
実際は InitOpenGL() (GLFunction) のテーブルに、gl ~ と同じように wgl 関数
を組み込みました。よって上の (A) のタイミングで InitOpenGL() を用いて
wglCreateContextAttribsARB を含めた一度にすべての拡張関数を取り出しています。


●プロファイルと互換性

OpenGL 3.1~ は DirectX のように、徐々に古い固定機能を無くし新しい機能への
置き換えが進んでいます。それでもアプリケーションが困らないように、従来と互換性を保った
API を使う手段も残されています。

・OpenGL 3.1 + GL_ARB_compatibility
・OpenGL 3.2 + WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB

OpenGL 3.2 では Profile の選択が出来るようになりました。
wglCreateContextAttribsARB() 呼び出し時の WGL_CONTEXT_PROFILE_MASK_ARB
で指定できる Profile は次の 2つ。

・WGL_CONTEXT_CORE_PROFILE_BIT_ARB
・WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB

Core profile はシェーダーを用いた新しい API セット、Compatibility profile は
互換性を持った API です。
テストで GeForce の 190.57 を使っている分には、どちらもほとんど差がありませんでした。


関連エントリ
OpenGL 3.2 GeometryShader をもう少し使ってみる
OpenGL GLSL のバージョン
OpenGL 3.2 の GeometryShader
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 シェーダー管理


前々回は VertexShaded から GeometryShader へのパラメータ渡しで
built-in のシステム変数 gl_Position を使いました。

VertexShader:
  gl_Position= vec4( POSITION.xyz, 1.0 ) * PView;

GeomteryShader:
  position= gl_in[0].gl_Position;

gl_Position を使うメリットは下記の通りです。

・宣言なしに使うことが出来る
・VertexShader の段階で書き込んでいるので GeomteryShader が無くても描画できる

前々回のような単なるスルー出力では GeomteryShader が無くても描画可能です。
VertexShader で gl_Position に値を書き込んでいたので、GeomteryShader を attach
せずにそのまま描画することが出来ました。

ただし、常に VertexShader の段階で座標が確定するとは限りません。
頂点からの座標値も、普通の変数を介して GeomteryShader に渡すことが出来ます。

Geometry Shader
↑基本の状態

// GLSL 1.5 VertexShader gl_Position を使わない
#version 150

uniform vec4   World[3];
uniform mat4   PView;

in vec3	    POSITION;
in vec3	    NORMAL;
in vec2	    TEXCOORD;

out vec4    vPosition;
out vec3    vNormal;
out vec2    vTexcoord;

void main()
{
    vPosition= vec4( POSITION.xyz, 1 ) * PView;
    vNormal.x= dot( NORMAL.xyz, World[0].xyz );
    vNormal.y= dot( NORMAL.xyz, World[1].xyz );
    vNormal.z= dot( NORMAL.xyz, World[2].xyz );
    vTexcoord= TEXCOORD;
}


// GLSL 1.5 GeometryShader
#version 150

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

in Inputs {
   vec4    vPosition;
   vec3    vNormal;
   vec2    vTexcoord;
} gin[3];

out vec3    vNormal;
out vec2    vTexcoord;

void main()
{
    for( int i= 0 ; i< 3 ; i++ ){
        gl_Position= gin[i].vPosition;
        vNormal= gin[i].vNormal;
        vTexcoord= gin[i].vTexcoord;
        EmitVertex();
    }
    EndPrimitive();
}

gl_in を使用していません。

面毎に法線と texcoord をまとめてフラットシェーディング風にしてみます。(↓)

Geometry Shader

// GeometryShader フラット化
void main()
{
    vNormal= normalize( gin[0].vNormal + gin[1].vNormal + gin[2].vNormal );
    vTexcoord= (gin[0].vTexcoord + gin[1].vTexcoord + gin[2].vTexcoord)/3;
    for( int i= 0 ; i< 3 ; i++ ){
        gl_Position= gin[i].vPosition;
        EmitVertex();
    }
    EndPrimitive();
}

きちんと法線を算出している訳じゃないので、本来平面のはずの Quad が Triangle に
分かれて見えてしまいます。今回は気にしないでいきます。

さらに座標もずらしてみます。(↓)

Geometry Shader

// GeometryShader 座標もずらす
void main()
{
    vec3 vnormal= normalize(gin[0].vNormal + gin[1].vNormal + gin[2].vNormal);
    vNormal= vnormal;
    vTexcoord= ( gin[0].vTexcoord + gin[1].vTexcoord + gin[2].vTexcoord )/3;
    vec3    offset= vnormal.xyz*2.0;
    for( int i= 0 ; i< 3 ; i++ ){
        gl_Position= gin[i].vPosition + vec4( offset.xy, 0.0, 0.0 );
        EmitVertex();
    }
    EndPrimitive();
}

上のスクリーンショットではわからないかもしれませんが、投影後の 2D 座標がずれる
だけなので、形状として厳密に正しい物ではないです。
3D で変形を行うには、Geometry Shader で頂点演算を行う必要があります。
Vertex Shader の方がスルー出力になります。

座標値が vec3 になっている点に注意。任意の型やデータをやりとりできるのが
gl_Position を使わない場合の利点です。

なお正しく面法線を求めていれば、三角形ではなく四角形に押し出されていたはず。

3D 座標(local)でずらした結果(↓)
Geometry Shader

// GLSL 1.5 VertexShader
#version 150

in vec3	    POSITION;
in vec3	    NORMAL;
in vec2	    TEXCOORD;

out vec3    vPosition;   // vec4 → vec3
out vec3    vNormal;
out vec2    vTexcoord;

void main()
{
    vPosition= POSITION;
    vNormal= NORMAL;
    vTexcoord= TEXCOORD;
}


// GLSL 1.5 GeometryShader
#version 150

uniform vec4	World[3];
uniform mat4	PView;

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

in Inputs {
   vec3    vPosition;   // vec4 → vec3
   vec3    vNormal;
   vec2    vTexcoord;
} gin[3];

out vec3    vNormal;
out vec2    vTexcoord;

vec4 transform( vec3 pos )
{
    return  vec4( pos, 1 ) * PView;
}

void main()
{
    vec3 vnormal= normalize(gin[0].vNormal + gin[1].vNormal + gin[2].vNormal);
    vNormal.x= dot( vnormal.xyz, World[0].xyz );
    vNormal.y= dot( vnormal.xyz, World[1].xyz );
    vNormal.z= dot( vnormal.xyz, World[2].xyz );
    vTexcoord= ( gin[0].vTexcoord + gin[1].vTexcoord + gin[2].vTexcoord )/3;
    for( int i= 0 ; i< 3 ; i++ ){
        gl_Position= transform( gin[i].vPosition + vnormal * 1.5 );
        EmitVertex();
    }
    EndPrimitive();
}

以前は VertexShader で受け取っていた uniform 変数 World, PView が
GeometryShader に移動しています。
OpenGL + GLSL の場合、Link したあとに uniform の設定を行うので C++ 側のコード
は何も修正しなくて済みます。結構便利です。

Direct3D だと各シェーダー毎に ConstantBuffer の設定が必要なので、
VSSetConstantBuffers() → GSSetConstantBuffers() と C++ の呼び出し側も
変更しなければなりませんでした。

ここまできたら VertexShader は不要な気もします。
一応試してみましたが VertexShader の省略は出来ませんでした。
Direct3D の Effect (FX) だと VertexShader と GeometryShader に同じ内容を
割り当てることで省略できます。


GeometryShader でポリゴンを増やしてみます。中に元の形状が内部に置いてある状態です。
共有頂点分の演算が重複するので、こういうケースでは VertexShader を併用
した方が効率がよいかもしれません。
out の layout で max_vertices= 6 に注意。

Geometry Shader

// GLSL 1.5 GeometryShader
#version 150

uniform vec4	World[3];
uniform mat4	PView;

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

in Inputs {
   vec3    vPosition;
   vec3    vNormal;
   vec2    vTexcoord;
} gin[3];

out vec3    vNormal;
out vec2    vTexcoord;

vec4 transform( vec3 pos )
{
    return  vec4( pos, 1 ) * PView;
}

void setnormal( vec3 normal )
{
    vNormal.x= dot( normal.xyz, World[0].xyz );
    vNormal.y= dot( normal.xyz, World[1].xyz );
    vNormal.z= dot( normal.xyz, World[2].xyz );
}

void main()
{
    for( int i= 0 ; i< 3 ; i++ ){
        setnormal( gin[i].vNormal );
        vTexcoord= gin[i].vTexcoord;
        gl_Position= transform( gin[i].vPosition );
        EmitVertex();
    }
    EndPrimitive();

    vec3 vnormal= normalize( gin[0].vNormal + gin[1].vNormal + gin[2].vNormal );
    setnormal( vnormal );
    vTexcoord= ( gin[0].vTexcoord + gin[1].vTexcoord + gin[2].vTexcoord )/3;
    for( int i= 0 ; i< 3 ; i++ ){
        gl_Position= transform( gin[i].vPosition + vnormal * 2.0 );
        EmitVertex();
    }
    EndPrimitive();
}


関連エントリ
OpenGL GLSL のバージョン
OpenGL 3.2 の GeometryShader
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 シェーダー管理


GeometryShader 利用時に GLSL のバージョンの違いが問題になったのでまとめてみました。

OpenGL 1.3  ----           OpenGL ES 1.0  ----
OpenGL 1.5  *1             OpenGL ES 1.1  ----
OpenGL 2.0  GLSL 1.1       OpenGL ES 2.0  GLSL ES 1.0 (ESSL)
OpenGL 2.1  GLSL 1.2
OpenGL 3.0  GLSL 1.3
OpenGL 3.1  GLSL 1.4
OpenGL 3.2  GLSL 1.5

*1: Shader 対応。Extension で GLSL1.0 も利用可能。Core API になったのは OpenGL 2.0 から。

Wikipedia GLSL

OpenGL ES 2.0 の GLSL は、GLSL ES または ESSL と呼ばれています。

上の対応表では OpenGL 2.0 / GLSL 1.1 の列にありますが、GLSL ES 1.0 自体は
GLSL 1.2 の機能も含まれています。実際は OpenGL 2.0~2.1 の中間になります。

OpenGL 2.0 で GLSL が core に組み込まれました。
OpenGL 3.0 / GLSL 1.3 で大きな仕様変更があり、シェーダーも固定機能の排除が
進められています。attribute/varying が in/out に変更になったのもここです。

Direct3D との対応付けは難しいですが、おそらく OpenGL 2.0 が Direct3D 9、
OpenGL 3.0 が ShaderModel 4.0 の Direct3D 10 に近い位置づけです。
実際はここまで明確な区別が無く、もっと入り組んでいるし互換性も保たれています。
機能拡張も段階的に進められています。

例えば OpenGL 3.1 で Uniform Block / Uniform Buffer が追加されていて、
これはちょうど Direct3D 10 の ConstantBuffer に相当するものです。
OpenGL 3.2 では Geometry Shader が入りました。

GLES と GLSL ES (ESSL) は「 #ifdef GL_ES 」で区別できます。
昨日はむりやり define して動かしましたが、下記のような定義でシェーダーを共有
することにしました。

// Vertex Shader
#ifdef GL_ES
# define IN         attribute
# define OUT        varying
# define LOWP       lowp
# define MEDIUMP    mediump
precision MEDIUMP float;
precision MEDIUMP int;
#else
# define IN         in
# define OUT        out
# version 150
# define LOWP    
# define MEDIUMP   
#endif


// Fragment Shader
#ifdef GL_ES
# define IN         varying
# define LOWP       lowp
# define MEDIUMP    mediump
precision MEDIUMP   float;
precision MEDIUMP   int;
precision LOWP      sampler2D;
#else
# define IN         in
# version 150
# define LOWP    
# define MEDIUMP   
#endif

OpenGL ES のコンパイラには少々問題があって、ソースの先頭に #version が無いと
エラーになるようです。コンパイルオプションの切り替えのために複数のソースを
渡しているせいか、#version をどこに書いてもエラーとなってしまいました。
上の定義では OpenGL ES 側に #version を書いていないのはそのためです。


関連エントリ
OpenGL 3.2 の GeometryShader
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 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<GS_OUTPUT> 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 シェーダー管理


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  <WinGDI.h>
#include  <gl/gl.h>
#include  <GL3/gl3.h>

#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 は必要な機能のみ用意されているため非常にわかりやすくなっています。
レガシーだけど互換性のために残っている 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  <EGL/egl.h>
#include  <GLES2/gl2.h>
#include  <GLES2/gl2ext.h>

#ifdef	NativeWindowType
# define  USE_GLES20_AMD   1
#else
# define  USE_GLES20_PVR   1
# include <GLES2/gl2extimg.h>
#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 シェーダー管理


2009/08/29
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)


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


NVIDIA の新しいドライバ 190.62 がコンピュートシェーダに対応したらしいので試しました。
Windows7 RC/RTM どちらでも Compute Shader 4.0 動いてます。

(Direct3D11)
*HARDWARE  = 10.0 (a000)
 feature threading  DriverConcurrentCreates=1
 feature threading  DriverCommandLists=0
 feature d3d10_x  ComputeShaders_Plus_RawAndStructuredBuffers_Via_Shader_4_x=1
 feature doubles  DoublePrecisionFloatShaderOps=0


関連エントリ
Direct3D11/DirectX11 GeForce の ComputeShader とドライバ
Direct3D11/DirectX11 ComputeShader 4.0 を使う
Direct3D11/DirectX11 (6) D3D11 の ComputeShader を使ってみる


ファントムダスト Phantomdust というゲームがありました。
今更ですが…忘れないうちにいくつか書いておきます。

マップの破壊表現は複数の手法の組み合わせで出来ています。
オブジェクト、パーティクル、破裂シェーダー、破壊のへこみ穴 等。
今回説明するのは「穴」の表現です。

攻撃やダメージ等によって、地形モデルのどの位置にでも削れたような穴があきます。
地面だけでなく、壁でも天井でも同様に破壊の痕跡が残ります。
このへこんだ形状は別モデルとして用意してあり、ステンシルマスクを用いて背景の
描画時に合成しています。
戦っているうちに地形の至る所が穴だらけになるわけです。

例えば下記リンク先のイレースシェルの地面がそうです。
いくつも穴がありますが、もともとは平らな地面のモデルデータです。

famitsu.com
Impress GAME Watch


●特徴

メリット

・デカールと違い立体的に見えます。
・背景モデルデータを書き換えることなく形状を変化出来ます。
・立体的なデータなのでヒット位置を厳密に割り出さなくても描画できます。
・ノーマルマップの背景に対して低コストで適用できます。

デメリット

実装時に様々な制限が生じます。問題をどのように解決したのかは後述します。
欠点というか実装できなかったのはコリジョンの追従です。コリジョンの変更はコストが
高く、ゲーム内容に関わるため通信同期も必要でした。残念ながら見送りました。


●この方法を用いた理由

破壊表現は当初からの課題でした。
プロトタイプの段階では通常のマテリアルを使っていたので、レイヤテクスチャ+
メッシュを分割+動的に頂点を書き換えていました。
問題となったのはノーマルマップです。
Xbox 1 の当時はまだ珍しかったのですが、最終的に背景もキャラも全面にノーマルマップを
使っています。これはハイポリのディテールを少ないポリゴンで再現可能な手法なので、
結果として頂点数を減らすように方針転換しました。よって

・頂点を動かすだけのポリゴンの細分化が出来ない
  頂点を減らせるのがメリットだし、TS の容量も計算も減らしたい
・変形した形状の Tangent Space の再計算はコストがかかる
・PixelShader の 8+4 stage をノーマルマップで使い切っている
  レイヤを増やすとマルチパスになる


●破壊穴モデルの形状

実際は平べったい形状ですが便宜上球と思って構いません。
穴の中のへこみを表現するモデルなので、面が裏返った内側を向いたデータです。

破壊の穴と地面の交差を判定するボリュームモデルと、実際に穴の中を描画する
描画モデルの 2つが必要となります。

・交差判定のためのボリュームモデル
・穴の中を描画するモデルデータ

描画モデルのマテリアルが単純かつ軽量なものだったので、この両者を兼用しています。
つまり描画モデルをそのまま断面の判定にも使用しています。


●断面の生成

シャドウボリュームと同じです。
テクスチャをフェッチしない専用の軽量シェーダーを割り当てて、ボリューム形状を
ステンシルバッファに 2回書きます。

(1) depth test かつ 表面、stencil +1
(2) depth test かつ 裏面、stencil -1

裏面と面面の差分が地面との交差面となります。上記の設定だと stencil が 0以外で
交差面です。
この条件を元に、同じ位置に描画モデルをレンダリングすれば合成ができます。

余談ですが、両面ステンシル機能がある GPU なら一度の描画で差分が求まります。
両面ステンシルが無くても両面マテリアルで代用可能で、例えばフレームバッファの
Alpha (DestAlpha) に加算で書き込むと一回で生成できます。
このとき描画頂点数は減りますがピクセル負荷は変わりません。

多くの GPU は Depth + Stencil の書き込みに特化したモードがあり、フィルレート
が倍になるように最適化されています。なので DestAlpha を使ってもあまりメリット
はありませんでした。
それにこのゲームは DestAlpha をすでに別の用途で利用していました。


●形状の変更

このゲームは表現上ステンシルバッファを多用しています。
8bit しかない stencil を bitmask して複数使い分けているところもあります。

・影
・オーラ
・地面や建物の破壊穴

もしゲーム画面か古い CGWORLD を見ることが出来るなら、穴の中にも形状に沿って影が
落ちていることがわかると思います。
でも描画しているモデルデータは平らなままです。

背景の影もシャドウボリュームを使っているのは、このように破壊によって動的に
地形が変わる可能性があるため。テクスチャや頂点への焼き込みだと、地形の変化に
対応できなくなります。


●負荷の相殺

破壊が進むと描画の負荷が増えて重くなりそうに見えますが実はそうでもありません。

このゲームは背景もキャラもすべてノーマルマップを使った重いシェーダーを適用
しています。最初から背景のピクセルが一番重い前提で負荷を計算しているので、
地形の描画面積を減らす処理は描画負荷の軽減につながります。

ノーマルマップを適用していない破壊の穴モデルは、数が増えれば増えるほど地形の
描画負荷を軽くすることになります。
ステンシルバッファへのレンダリングが増えますが相殺して軽くなります。
(でも頂点は軽くなりません。この判断は今思うと微妙だったかもしれません。)

同じようにキャラクタの描画負荷も相殺するのでアップになっても速度は変わりません。
半透明のエフェクトはまずいです。


●矛盾の解決

実際に組み込んでみると単純な表現でもさまざまな問題が生じます。
他の描画や表現と共存が必要だからで、1つ 1つ対策を施していきます。

・穴が開くと困る場所

破壊モデル合成時に、特殊な形状や半透明など矛盾が生じる場所があります。
あらかじめマテリアルの設定で描画しないように除外しています。
これは描画パスを分けることで実現しています。

・穴同士の重なり

Z test は別の判定に用いるので、描画の重なりの解決に Z バッファが使えません。
同じ位置にポリゴンが重なると矛盾が生じるので、描画位置を決める際に当たり判定を
行っています。必ず他の穴と重ならない位置に配置しています。

・地面の下だけ描画

破壊の穴形状は多少ずれても問題がないように球形をしています。
上半分が見えてしまわないように、地面の下に潜り込んだ部分だけ切り取るため
depth test を併用しています。Stencil がパスしてかつ、Z Fail が条件です。
地面の下にめり込んだ部分だけが描画されるようになります。

・depth 強制更新

ただ描画するだけでは不十分であることがわかります。
見た目と Z buffer に食い違いが生じるからです。
たとえば穴の上に置いた物や影が、地面の位置でまっすぐに切れてしまいます。

よって描画モデルを描き込むときに同時に depth を強制的に上書きします。
depth (Z buffer) が更新できれば、その後に描画する Shadow Volume も穴の形に
沿って落ちてくれます。

・遠くの穴が見えてしまう

地面が階層化している場所など、上の段の破壊穴と下の段の穴が重なって描画される
ことがあります。depth test の条件に Z Fail を設定しているということは、常に
遠くのオブジェクトが優先されることに等しいからです。
穴の中にさらに遠くの穴が見えてしまいます。

そこで穴モデル自体を Zソートして手前から描画し、同時にステンシルに 0 を書き
込んで消していきます。遠くの穴を描画する段階では depth test の Z Fail を
通っても、stencil テストに失敗するので描画されなくなります。

・地形との合成

上の問題を対処するためステンシルバッファを 0 で消してしまうと、今度はその
位置だけ地形を除外できなくなります。つまり穴の位置に上書きしてしまいます。
ステンシルをクリアする場合、別の条件で区別できる値を書き込まなければなりません。
元々ステンシル値の 0 または 0 以外で判定していたので思ったより簡単ではないです。
もしくは描画順番で何とかします。交差面を求めた後、地形の穴の中を描画する前に
先に地面を描画し、その後破壊穴モデルを描画すれば 0 クリアでも問題ありません。

・穴の上に重なるモデルの表示順番

地面に穴が開くはずの場所に他のモデルが置いてある場合も描画順番が問題となります。
例えばキャラが地面に立っていて、わずかに地面にめり込んでいる場合。(本当は
それが良くないのですが)
穴を開ける前の Z バッファで描画してしまうとめり込んだ部分が切れてしまいます。
だから地形に乗っているものは、穴を開けて Z バッファを更新したあとに描画する
ことになります。

・個数制限と消去

破壊の穴は同時に 30個描画しています。上限はただの定数値で、何らかの制限があった
わけではないです。これを超えた場合、視野に入っていないものから消しています。


●描画手順のまとめ

(1) 破壊可能部分の depth のみ
(2) 破壊穴モデルの交差判定
(3) 破壊穴の描画と depth 更新+識別可能な stencil で書き直す(要確認)
(4) 地形の上に乗っているモデル描画、不透明物一般
(5) 破壊不可能部分描画
(6) 破壊可能部分のカラー描画
(7) シャドウボリューム
(8) 影の合成
(9) 半透明地形の描画
(10) 半透明エフェクト

記憶だけで書いているので、おそらく異なっている部分や足りない部分があります。
これ以外にも特殊なエフェクトや表現があったので、実際はもっと描画手順が複雑です。
具体的に設定したステートの組み合わせなどは、おそらくもう一度実際に作ってみないと
わからないと思います。
それに今のハードならもっと簡単でしょう。ShaderModel 1.x の当時とは違うので、
別の表現方法も使えると思います。


昨日の続きです。ミップマップと 24bit テクスチャに対応します。
非圧縮形式はほぼ読み込めるようになりました。

DDS ファイルのミップマップの判定です。

    int	mipLevel= 1;
    if( (dp->dwCaps & DDSCAPS_MIPMAP)
            && (dp->dwFlags & DDSD_MIPMAPCOUNT)
            && dp->dwMipMapCount ){
        mipLevel= dp->dwMipMapCount;
    }

DDS テクスチャに含まれている Mipmap Level 数は dwMipMapCount に入ります。
本来最小値は 1 ですが、ツールによっては dwMipMapCount を設定していないものがあります。
dwMipMapCount が有効かどうかは dwFlags の DDSD_MIPMAPCOUNT で判別します。
DDSD_MIPMAPCOUNT が設定されていない場合、また念のため dwMipMapCount が 0 の
場合も 1 と見なした方が良いでしょう。

前回調べたように DDS と OpenGL では一部ピクセル形式に違いがあります。
MipLevel ごとに毎回変換するのは避けたいので、フォーマット判別時に全部書き換えてしまいます。
この変換処理を一度で済ませるために、まずはデータに含まれる全ピクセルの総数を求めます。

MipMap が無ければ width * height です。
何も考えずにファイルサイズ分全部変換してしまうのが一番楽かもしれません。
でも敢えて計算で求めてみました。

  unsigned int  bdif= IntLog2( IntMin( width, height ) ) << 1;
  unsigned long long  mbit= 0xaaaaaaaaLL| ((0x100000000LL >> bdif) - 1);
  mbit &= ~((0x100000000LL >> (mipLevel<<1))-1);
  pixcount= static_cast<unsigned int>( (width * height * mbit) >> 31 );

これで任意レベル数の mipmap の合計容量がわかります。
テクスチャが正方形なら (width * height * 0xaaaaaaaaLL)>>31 だけで済みます。
縦横のサイズが異なっている場合の補正が最初の 2行の bdif です。
1pixel を半分にした場合に 0.5pixel にならないよう最小値を clamp しています。

3行目はテクスチャ画像に含まれる MipLevel を制限するためのものです。
たとえば実際のゲームでも、ミップマップでテクスチャがぼけすぎるのを避けるために
MipLevel 数を制限することがあります。1024x1024 サイズでも 5 level 分しか格納
されていないことがあり得るわけです。

IntMin() は最小値を返しているだけ。
問題は IntLog2 の部分ですが x86/x64 では下記のように 1命令で記述することができます。

inline unsigned long IntLog2( unsigned int param )
{
    unsigned long  ret;
    _BitScanReverse( &ret, static_cast<unsigned long>( param ) );
    return  ret;
}

_BitScanReverse() は intrinsic で x86 の bsr 命令のことです。

他の CPU 対応を考えると、_BitScanReverse を置き換えるよりも最初からループ等で
MipMap の容量を求めた方が良いかもしれません。

#include  "TextureCache.h"
#include  "IntLog2.h"

#define	DDSCAPS_MIPMAP	    0x00400000
#define	DDSD_MIPMAPCOUNT    0x00020000

struct T_DDSHEADER {
    unsigned int    dwMagic;
    unsigned int    dwSize;
    unsigned int    dwFlags;
    unsigned int    dwHeight;
    unsigned int    dwWidth;
    unsigned int    dwPitchOrLinearSize;
    unsigned int    dwDepth;
    unsigned int    dwMipMapCount;
    unsigned int    dwReserved1[11];
    unsigned int    dwPfSize;
    unsigned int    dwPfFlags;
    unsigned int    dwFourCC;
    unsigned int    dwRGBBitCount;
    unsigned int    dwRBitMask;
    unsigned int    dwGBitMask;
    unsigned int    dwBBitMask;
    unsigned int    dwRGBAlphaBitMask;
    unsigned int    dwCaps;
    unsigned int    dwCaps2;
    unsigned int    dwReservedCaps[2];
    unsigned int    dwReserved2;
};
#define	__DDS__MAGIC	0x20534444	// ' SDD'

static inline unsigned int IntMin( unsigned int a, unsigned int b )
{
    return  a < b ? a : b;
}

template<unsigned int AShift, unsigned int AMask>
static void DDStoGL16( unsigned int count, void* data )
{
    unsigned short*	ptr= reinterpret_cast<unsigned short*>( data );
    for(; count-- ;){
        unsigned int	color= *ptr;
        unsigned int	alpha= color & AMask;
        *ptr++= (color << (16-AShift)) | (alpha >> AShift);
    }
}

static void DDStoGLByte( unsigned int count, void* data, unsigned int stride )
{
    unsigned char*	ptr= reinterpret_cast<unsigned char*>( data );
    for(; count-- ;){
        unsigned char   r= ptr[0];
        unsigned char   b= ptr[2];
        ptr[0]= b;
        ptr[2]= r;
        ptr+= stride;
    }
}

GLuint _flFASTCALL
TextureCache::DDSLoader( void* mem )
{
    const T_DDSHEADER*	dp= reinterpret_cast<T_DDSHEADER*>( mem );
    int     width= dp->dwWidth;
    int     height= dp->dwHeight;
    void*   data= reinterpret_cast<void*>( reinterpret_cast<intptr_t>( mem ) + sizeof(T_DDSHEADER) );

    GLenum  format= GL_NONE;
    GLenum  ftype= GL_UNSIGNED_BYTE;

    int	mipLevel= 1;
    unsigned int  pixcount= width * height;

    if( (dp->dwCaps & DDSCAPS_MIPMAP)
            && (dp->dwFlags & DDSD_MIPMAPCOUNT)
            && dp->dwMipMapCount ){
        mipLevel= dp->dwMipMapCount;

        unsigned int    bdif= IntLog2( IntMin( width, height ) ) << 1;
        unsigned long long  mbit= 0xaaaaaaaaLL| ((0x100000000LL >> bdif) - 1);
        mbit &= ~((0x200000000LL >> (mipLevel<<1))-1);
        pixcount= static_cast<unsigned int>( (pixcount * mbit) >> 31 );
    }

    switch( dp->dwRGBBitCount ){
    case 32: // 8888
        switch( dp->dwRBitMask ){
        case 0x000000ff: // R G B A = GL
            format= GL_RGBA;
            ftype= GL_UNSIGNED_BYTE;
            break;
        case 0x00ff0000: // B G R A = DX
            format= GL_RGBA;
            ftype= GL_UNSIGNED_BYTE;
            DDStoGLByte( pixcount, data, 4 );
            break;
        }
        break;
    case 24: // 888
        switch( dp->dwRBitMask ){
        case 0x0000ff: // R G B = GL
            format= GL_RGB;
            ftype= GL_UNSIGNED_BYTE;
            break;
        case 0xff0000: // B G R = DX
            format= GL_RGB;
            ftype= GL_UNSIGNED_BYTE;
            DDStoGLByte( pixcount, data, 3 );
            break;
        }
        break;
    case 16:
        switch( dp->dwGBitMask ){
        case 0x00f0: // 4444
            format= GL_RGBA;
            ftype= GL_UNSIGNED_SHORT_4_4_4_4;
            DDStoGL16<12,0xf000>( pixcount, data );
            break;
        case 0x07e0: // 565
            format= GL_RGB;
            ftype= GL_UNSIGNED_SHORT_5_6_5;
            break;
        case 0x03e0: // 1555
            format= GL_RGBA;
            ftype= GL_UNSIGNED_SHORT_5_5_5_1;
            DDStoGL16<15,0x8000>( pixcount, data );
            break;
        case 0x0000: // L A 88
            format= GL_LUMINANCE_ALPHA;
            ftype= GL_UNSIGNED_BYTE;
            break;
        }
        break;
    case 8:
        if( dp->dwRGBAlphaBitMask ){
            format= GL_ALPHA;
            ftype= GL_UNSIGNED_BYTE;
        }else{
            format= GL_LUMINANCE;
            ftype= GL_UNSIGNED_BYTE;
        }
        break;
    }

    if( format == GL_NONE ){
        return	0;
    }

    GLuint  texid= 0;

    glGenTextures( 1, &texid );
    glBindTexture( GL_TEXTURE_2D, texid );

    for( int mi= 0 ; mi < mipLevel ; mi++ ){
        glTexImage2D(
                GL_TEXTURE_2D,
                mi,     // level
                format, // internal format
                width,
                height,
                0,      // border
                format,
                ftype,
                data
                );
        data= reinterpret_cast<void*>( reinterpret_cast<intptr_t>( data ) + ((width * height * dp->dwRGBBitCount) >> 3) );
        width>>= 1;
        height>>= 1;
        if( width <= 0 ){
            width= 1;
        }
        if( height <= 0 ){
            height= 1;
        }
    }

    if( mipLevel > 1 ){
        glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR );
    }else{
        glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
    }
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );

    return  texid;
}

TextureCache は多重にテクスチャが読み込まれないよう参照管理を行っています。
リソースマネージャーは統合されており、テクスチャやシェーダー、スクリプトファイル
等が同じ仕組みで管理下に置かれています。


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


DXT/BC 等の圧縮や Mipmap/Cubemap, HDR 対応などを考えると DDS は便利な
テクスチャフォーマットです。OpenGLES でも読めるようにしておきます。
まずは非圧縮の 2D 形式のみ考えます。

とりあえず適当に…
これ を書いた当人と思えないくらい いい加減 な判定ですが、DDS (Direct3D) と OpenGL
のフォーマットの対応はこんな感じになります。
後で説明しますがこの段階ではまだ不完全です。

GLuint _flFASTCALL
TextureCache::DDSLoader( void* mem )
{
    const T_DDSHEADER*	dp= reinterpret_cast<T_DDSHEADER*>( mem );
    int     width= dp->dwWidth;
    int     height= dp->dwHeight;
    void*   data= reinterpret_cast<void*>( reinterpret_cast<intptr_t>( mem ) + sizeof(T_DDSHEADER) );

    GLenum  format= GL_NONE;
    GLenum  ftype= GL_UNSIGNED_BYTE;

    switch( dp->dwRGBBitCount ){
    case 32: // 8888
        switch( dp->dwRBitMask ){
        case 0x000000ff: // R G B A = GL
            format= GL_RGBA;
            ftype= GL_UNSIGNED_BYTE;
            break;
        case 0x00ff0000: // B G R A = DX
            format= GL_RGBA;
            ftype= GL_UNSIGNED_BYTE;
            break;
        }
        break;
    case 24: // 888
        switch( dp->dwRBitMask ){
        case 0x0000ff: // R G B = GL
            format= GL_RGB;
            ftype= GL_UNSIGNED_BYTE;
            break;
        case 0xff0000: // B G R = DX
            format= GL_RGB;
            ftype= GL_UNSIGNED_BYTE;
            break;
        }
        break;
    case 16:
        switch( dp->dwGBitMask ){
        case 0x00f0: // 4444
            format= GL_RGBA;
            ftype= GL_UNSIGNED_SHORT_4_4_4_4;
            break;
        case 0x07e0: // 565
            format= GL_RGB;
            ftype= GL_UNSIGNED_SHORT_5_6_5;
            break;
        case 0x03e0: // 1555
            format= GL_RGBA;
            ftype= GL_UNSIGNED_SHORT_5_5_5_1;
            break;
        case 0x0000: // L A 88
            format= GL_LUMINANCE_ALPHA;
            ftype= GL_UNSIGNED_BYTE;
            break;
        }
        break;
    case 8:
        if( dp->dwRGBAlphaBitMask ){
            format= GL_ALPHA;
            ftype= GL_UNSIGNED_BYTE;
        }else{
            format= GL_LUMINANCE;
            ftype= GL_UNSIGNED_BYTE;
        }
        break;
    }

    GLuint  texid= 0;
    glGenTextures( 1, &texid );
    glBindTexture( GL_TEXTURE_2D, texid );

    glTexImage2D(
            GL_TEXTURE_2D,
            0,      // level
            format, // internal format
            width,
            height,
            0,      // border
            format,
            ftype,
            data
            );

    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );

    return  texid;
}

OpenGLES で対応できそうなのは下記の各フォーマットです。
A8R8G8B8, A8B8G8R8, R8G8B8, A4R4G4B4, R5G6B5, A1R5G5B5, A8L8, A8, L8
でも実際に表示してみると正しく出るのは一部だけ。
A8R8G8B8, R8G8B8, A4R4G4B4, A1R5G5B5 は色ずれがあり失敗します。

Direct3D と OpenGL ではピクセル内の配置が異なっているようです。
調べてみました。


● OpenGL の pixel 配列

原則として R G B A の順番となります。

各コンポーネント(RGB)が独立している場合は、メモリ上に R G B A の順で配置
されます。たとえば 32bit 8888 がそうです。Byte 単位で書き込まれています。
64bit, 128bit といった Float 形式も同様です。
この順番は x y z w の並びに一致するため、シェーダーから見ても自然なものです。

ただし 8888 を DWORD (32bit) で読み込んだ場合、リトルエンディアンでは下記の
bit 並びになります。最下位が R で最上位が Alpha です。

31 H<---------------->L 0
|  A  |  B  |  G  |  R  |    GL_RGBA + GL_UNSIGNED_BYTE  (R8 G8 B8 A8)

コンポーネントが UNSIGNED_SHORT にパックされている場合は下記の内容になります。
リトルエンディアンで読み込んだ 32bit 8888 と逆順です。

15 H<-------->L 0
| R | G | B | A |          GL_RGBA + GL_UNSIGNED_SHORT_4_4_4_4  (R4 G4 B4 A4)

15 H<-------->L 0
| R  | G  | B |A|          GL_RGBA + GL_UNSIGNED_SHORT_5_5_5_1  (R5 G5 B5 A1)

15 H<-------->L 0
| R  |  G  | B  |          GL_RGB + GL_UNSIGNED_SHORT_5_6_5  (R5 G6 B5)

OpenGL の 32bit 8888 形式は、GL_UNSIGNED_BYTE というシンボル通り Byte
アクセスを想定しています。


● Direct3D の pixel 配列 (32bit 8888 の場合)

・ Direct3D9 以前

Direct3D9 まではフォーマット名称の表記が OpenGL と逆順でした。Direct3D 上は
ARGB と表記されますが、OpenGL の呼び方にあわせると BGRA 相当です。
これは下記の bit 並びを見るとよくわかります。OpenGL と比べてみてください。

31 H<---------------->L 0
|  A  |  R  |  G  |  B  |       D3DFMT_A8R8G8B8

リトルエンディアンなので D3DFMT_A8R8G8B8 をメモリに格納すると B G R A の順番
になります。

なお Direct3D は A8B8G8R8 という逆順フォーマットも持っており、両方使うことが
出来ます。こちらは OpenGL の 8888 と全く同じ並びです。

31 H<---------------->L 0
|  A  |  B  |  G  |  R  |       D3DFMT_A8B8G8R8

ピクセルだけでなく、頂点形式でも D3DDECLTYPE_D3DCOLOR, D3DDECLTYPE_UBYTE4
と 2種類のフォーマットを選択できました。これも上記 2種類の並び順に対応します。

2種類の並び順を持っているのは基本的に 32bit 8888 だけです。
A32B32G32R32F 等の HDR/浮動小数系のフォーマットはすべて A B G R で
OpenGL と同じ順番で配置されています。


◎ Direct3D10 以降

Direct3D は上記のように 32bit 8888 の場合だけ配列が 2種類あります。
Direct3D10 以降は、HDR/浮動小数や OpenGL 同様の順番でほぼ統一されました。

まずフォーマットの表記が逆順になります。

 D3D9     D3D10/11/OpenGL
 ------------------------
 ARGB  →   BGRA
 ABGR  →   RGBA

そして 32bit 8888 のデフォルトの配列が RGBA (R8G8B8A8) になりました。
もちろんこれまで通り B8G8R8A8 も使えます。

31 H<---------------->L 0
|  A  |  B  |  G  |  R  |     DXGI_FORMAT_R8G8B8A8_UNORM  (== D3DFMT_A8B8G8R8)

31 H<---------------->L 0
|  A  |  R  |  G  |  B  |     DXGI_FORMAT_B8G8R8A8_UNORM   == D3DFMT_A8R8G8B8)

D3D9 以前と表記が逆になっているのでかなり ややこしい ことになります。
DDS テクスチャを作る場合、ほとんどのツールは Direct3D9 以前のフォーマット
表記になっているからです。

さらに Direct3D11 (DXGI1.1) では一見廃れるかと思われた B8G8R8A8 系が復活し
バリエーションが大幅に追加されています。


● Direct3D の pixel 配列 (16bit)

標準形式だった D3DFMT_A8R8G8B8 が ARGB の並びであることを思い出してください。
32bit で Alpha は最上位になります。
16bit にパックされた 565, 1555, 4444 も同様です。

31 H<---------------->L 0
|  A  |  R  |  G  |  B  |     D3DFMT_A8R8G8B8

15 H<-------->L 0
| A | R | G | B |             D3DFMT_A4R4G4B4

15 H<-------->L 0
|A| R  | G  | B |             D3DFMT_A1R5G5B5

15 H<-------->L 0
| R  |  G  | B  |             D3DFMT_R5G6B5

OpenGLES の形式と Alpha の位置が異なっています。
これで正しく表示できなかった原因が判明しました。
Alpha が存在しない 565 だけ正しく表示できたのも納得です。

独立したコンポーネントを Byte アクセスする OpenGL と違い、Direct3D は
リトルエンディアンの 32bit (DWORD) で読み込んだときに、互換のある形式に
なっているわけです。


● DDS の読み込み続き

原因が判明したのでローダーの続きです。
読み込みと同時に互換性のないピクセル並びを変換します。
まずは上で省略してしまった DDS ヘッダの定義。(詳しくはこちらをどうぞ)

#define	DDSCAPS_MIPMAP	0x00400000
struct T_DDSHEADER {
    unsigned int    dwMagic;
    unsigned int    dwSize;
    unsigned int    dwFlags;
    unsigned int    dwHeight;
    unsigned int    dwWidth;
    unsigned int    dwPitchOrLinearSize;
    unsigned int    dwDepth;
    unsigned int    dwMipMapCount;
    unsigned int    dwReserved1[11];
    unsigned int    dwPfSize;
    unsigned int    dwPfFlags;
    unsigned int    dwFourCC;
    unsigned int    dwRGBBitCount;
    unsigned int    dwRBitMask;
    unsigned int    dwGBitMask;
    unsigned int    dwBBitMask;
    unsigned int    dwRGBAlphaBitMask;
    unsigned int    dwCaps;
    unsigned int    dwCaps2;
    unsigned int    dwReservedCaps[2];
    unsigned int    dwReserved2;
};
#define	__DDS__MAGIC	0x20534444	// ' SDD'

ピクセルの入れ替えです。

template<unsigned int AShift, unsigned int AMask>
static void _flFASTCALL
DDStoGL16( unsigned int count, void* data )
{
    unsigned short*	ptr= reinterpret_cast<unsigned short*>( data );
    for(; count-- ;){
        unsigned int	color= *ptr;
        unsigned int	alpha= color & AMask;
        *ptr++= (color << (16-AShift)) | (alpha >> AShift);
    }
}

static void _flFASTCALL
DDStoGL32( unsigned int count, void* data )
{
    unsigned int*	ptr= reinterpret_cast<unsigned int*>( data );
    for(; count-- ;){
        unsigned int	color= *ptr;
        *ptr++= (color & 0xff00ff00)
                |((color >> 16) & 0xff)
                |((color << 16) & 0xff0000);
    }
}

変換が必要な場所で呼び出します。

  case 32: // 8888
        switch( dp->dwRBitMask ){
        ~
        case 0x00ff0000: // B G R A = DX
            format= GL_RGBA;
            ftype= GL_UNSIGNED_BYTE;
            DDStoGL32( width*height, data );      // ← ここ
            break;
        }
        break;


    case 16:
        switch( dp->dwGBitMask ){
        case 0x00f0: // 4444
            format= GL_RGBA;
            ftype= GL_UNSIGNED_SHORT_4_4_4_4;
            DDStoGL16<12,0xf000>( width*height, data );     // ← ここ
            break;
        case 0x07e0: // 565
            ~
        case 0x03e0: // 1555
            format= GL_RGBA;
            ftype= GL_UNSIGNED_SHORT_5_5_5_1;
            DDStoGL16<15,0x8000>( width*height, data );     // ← ここ
            break;
        case 0x0000: // L A 88
            ~

このコードは入力したメモリを直接書き換えている点に注意です。

DDSLoader() は、読み込んだ DDS ファイルのメモリイメージをそのまま受け取ります。
メモリイメージはテンポラリ相当なので、この仕様で問題になることはあまりないかもしれません。
もし DDSLoader() 終了後もすぐにメモリを解放せずに、同じデータで何度も
DDSLoader() を呼び出すような再利用があれば問題が生じます。
安全のためには バッファを const で受け取り、変換が必要な場合のみバッファを複製するよう
書き換えが必要かもしれません。

今回のローダーは Mipmap に対応していないので DDSLoader() 最後の 2行は重要です。

    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );

デフォルトで MIPMAP 有効になっていることがあったためです。
明示的に MIPMAP を切っておかないと真っ黒なテクスチャが表示されることがあります。

長くなったので Mipmap 対応は次回にします。
書き込んでから気がつきました。24bit 888 の対応も忘れていました。


関連エントリ
OpenGLES2.0 Direct3D とのフォーマット変換
OpenGLES 2.0 頂点フォーマットの管理
OpenGLES2.0 の頂点
OpenGLES2.0 D3D座標系
OpenGLES2.0 シェーダー管理
Direct3D11/DirectX11 (4) FeatureLevel と旧 GPU の互換性、テクスチャ形式など
D3D関連 DDSテクスチャの取り扱い
Direct3D もうひとつのユニファイド


Direct3D10 以降、Pixel フォーマットと頂点の各 Element のフォーマット形式は
統一されています。DXGI_FORMAT はまた Direct3D からも独立しており、
バージョンに依存せず同じものが使えるようになりました。
つまり Direct3D10 と Direct3D11 のフォーマットは同一です。

●Direct3D9

・ピクセル、インデックス用フォーマット D3DFORMAT
     D3DFMT_A8R8G8B8
     D3DFMT_R5G6B5
     D3DFMT_A16B16G16R16F
     D3DFMT_INDEX16    (IndexBuffer 用)
     等

・頂点フォーマット D3DDECLTYPE
     D3DDECLTYPE_FLOAT3
     D3DDECLTYPE_UBYTE4
     他


●Direct3D10/Direct3D11

・ピクセル、頂点、インデックス兼用  DXGI_FORMAT
     DXGI_FORMAT_R8G8B8A8_UNORM
     DXGI_FORMAT_B5G6R5_UNORM
     DXGI_FORMAT_R32G32B32_FLOAT  (従来の D3DDECLTYPE_FLOAT3 も兼ねる)
     DXGI_FORMAT_R16_UINT         (従来の D3DFMT_INDEX16 も兼ねる)
     ~

Direct3D11 ではライブラリ独自の頂点形式から ID3D11InputLayout を作るために
下記の関数を用意していました。
これをそのまま OpenGLES.20 用に置き換えたのが、先日の GLInputLayout の関数です。

// DXInputLayout.cpp
static struct _FormatType {
    DXGI_FORMAT   dxgi_format;
} formatTable[]= {

{	DXGI_FORMAT_UNKNOWN,			},
{	DXGI_FORMAT_UNKNOWN,			},
{	DXGI_FORMAT_UNKNOWN,			},
{	DXGI_FORMAT_UNKNOWN,			},

{	DXGI_FORMAT_R32_FLOAT,			},
{	DXGI_FORMAT_R32G32_FLOAT,		},
{	DXGI_FORMAT_R32G32B32_FLOAT,		},
{	DXGI_FORMAT_R32G32B32A32_FLOAT,		},

{	DXGI_FORMAT_R16_FLOAT,			},
{	DXGI_FORMAT_R16G16_FLOAT,		},
{	DXGI_FORMAT_UNKNOWN,			},
{	DXGI_FORMAT_R16G16B16A16_FLOAT,		},

{	DXGI_FORMAT_R32_SINT,			},
{	DXGI_FORMAT_R32G32_SINT,		},
{	DXGI_FORMAT_R32G32B32_SINT,		},
{	DXGI_FORMAT_R32G32B32A32_SINT,		},

{	DXGI_FORMAT_R16_SINT,			},
{	DXGI_FORMAT_R16G16_SINT,		},
{	DXGI_FORMAT_UNKNOWN,			},
{	DXGI_FORMAT_R16G16B16A16_SINT,		},

{	DXGI_FORMAT_R8_SINT,			},
{	DXGI_FORMAT_R8G8_SINT,			},
{	DXGI_FORMAT_UNKNOWN,			},
{	DXGI_FORMAT_R8G8B8A8_SINT,		},

{	DXGI_FORMAT_R32_UINT,			},
{	DXGI_FORMAT_R32G32_UINT,		},
{	DXGI_FORMAT_R32G32B32_UINT,		},
{	DXGI_FORMAT_R32G32B32A32_UINT,		},

{	DXGI_FORMAT_R16_UINT,			},
{	DXGI_FORMAT_R16G16_UINT,		},
{	DXGI_FORMAT_UNKNOWN,			},
{	DXGI_FORMAT_R16G16B16A16_UINT,		},

{	DXGI_FORMAT_R8_UINT,			},
{	DXGI_FORMAT_R8G8_UINT,			},
{	DXGI_FORMAT_UNKNOWN,			},
{	DXGI_FORMAT_R8G8B8A8_UINT,		},

{	DXGI_FORMAT_R16_SNORM,			},
{	DXGI_FORMAT_R16G16_SNORM,		},
{	DXGI_FORMAT_UNKNOWN,			},
{	DXGI_FORMAT_R16G16B16A16_SNORM,		},

{	DXGI_FORMAT_R8_SNORM,			},
{	DXGI_FORMAT_R8G8_SNORM,			},
{	DXGI_FORMAT_UNKNOWN,			},
{	DXGI_FORMAT_R8G8B8A8_SNORM,		},

{	DXGI_FORMAT_R16_UNORM,			},
{	DXGI_FORMAT_R16G16_UNORM,		},
{	DXGI_FORMAT_UNKNOWN,			},
{	DXGI_FORMAT_R16G16B16A16_UNORM,		},

{	DXGI_FORMAT_R8_UNORM,			},
{	DXGI_FORMAT_R8G8_UNORM,			},
{	DXGI_FORMAT_UNKNOWN,			},
{	DXGI_FORMAT_R8G8B8A8_UNORM,		},
};


DXGI_FORMAT _flFASTCALL
PixelToFormat( unsigned int func )
{
    assert( func < Image::FUNC_END );
    return  formatTable[ func ].dxgi_format;
}

void _flFASTCALL
SetElementDesc(
	D3D11_INPUT_ELEMENT_DESC* desc,
	unsigned int desccount,
	const a5::InputLayout& layout
	)
{
    unsigned int    ecount= layout.GetElementCount();
    assert( ecount < desccount );
    for( unsigned int ei= 0 ; ei< ecount ; ei++, desc++ ){
        const a5::InputElement*  ep= layout.GetElement( ei );
        desc->SemanticName= ep->SemanticName;
        desc->SemanticIndex= ep->SemanticIndex;
        desc->Format= PixelToFormat( ep->pixf.Func );
        desc->InputSlot= 0;
        desc->AlignedByteOffset= ep->ByteOffset;
        desc->InputSlotClass= D3D11_INPUT_PER_VERTEX_DATA;
        desc->InstanceDataStepRate= 0;
    }
}

このように、独自の頂点形式をテーブルで DXGI_FORMAT に変換しています。
DXGI そのままでないのが幸いして、OpenGLES2.0 への変換も比較的うまく
いっています。OpenGLES2.0 で用意したのが下記のコードです。
このテーブルはそのまま上の DXGI_FORMAT に対応しています。

static struct _FormatType {
    GLenum    dxgi_format;
    unsigned int   component;
    unsigned int   normalize;
} formatTable[]= {

{    0,                  0,    FALSE,    },
{    0,                  0,    FALSE,    },
{    0,                  0,    FALSE,    },
{    0,                  0,    FALSE,    },

{    GL_FLOAT,           1,    FALSE,	},
{    GL_FLOAT,           2,    FALSE,	},
{    GL_FLOAT,           3,    FALSE,	},
{    GL_FLOAT,           4,    FALSE,	},

{    0,                  0,    FALSE,	},  // half float
{    0,                  0,    FALSE,	},  // half float
{    0,                  0,    FALSE,	},  // half float
{    0,                  0,    FALSE,	},  // half float

{    0,                  0,    FALSE,	},  // signed int 32
{    0,                  0,    FALSE,	},  // signed int 32
{    0,                  0,    FALSE,	},  // signed int 32
{    0,                  0,    FALSE,	},  // signed int 32

{    GL_SHORT,	         1,    FALSE,	},  // signed int 16
{    GL_SHORT,	         2,    FALSE,	},  // signed int 16
{    GL_SHORT,	         3,    FALSE,	},  // signed int 16
{    GL_SHORT,	         4,    FALSE,	},  // signed int 16

{    GL_BYTE,	         1,    FALSE,	},  // signed byte 8
{    GL_BYTE,	         2,    FALSE,	},  // signed byte 8
{    GL_BYTE,	         3,    FALSE,	},  // signed byte 8
{    GL_BYTE,	         4,    FALSE,	},  // signed byte 8

{    0,		         0,    FALSE,	},  // unsigned int 32
{    0,		         0,    FALSE,	},  // unsigned int 32
{    0,		         0,    FALSE,	},  // unsigned int 32
{    0,		         0,    FALSE,	},  // unsigned int 32

{    GL_UNSIGNED_SHORT,  1,    FALSE,	},
{    GL_UNSIGNED_SHORT,  2,    FALSE,	},
{    GL_UNSIGNED_SHORT,  3,    FALSE,	},
{    GL_UNSIGNED_SHORT,  4,    FALSE,	},

{    GL_UNSIGNED_BYTE,   1,    FALSE,	},
{    GL_UNSIGNED_BYTE,   2,    FALSE,	},
{    GL_UNSIGNED_BYTE,   3,    FALSE,	},
{    GL_UNSIGNED_BYTE,   4,    FALSE,	},

{    GL_SHORT,           1,    TRUE,	},
{    GL_SHORT,           2,    TRUE,	},
{    GL_SHORT,           3,    TRUE,	},
{    GL_SHORT,           4,    TRUE,	},

{    GL_BYTE,            1,    TRUE,	},
{    GL_BYTE,            2,    TRUE,	},
{    GL_BYTE,            3,    TRUE,	},
{    GL_BYTE,            4,    TRUE,	},

{    GL_UNSIGNED_SHORT,  1,    TRUE,	},
{    GL_UNSIGNED_SHORT,  2,    TRUE,	},
{    GL_UNSIGNED_SHORT,  3,    TRUE,	},
{    GL_UNSIGNED_SHORT,  4,    TRUE,	},

{    GL_UNSIGNED_BYTE,   1,    TRUE,	},
{    GL_UNSIGNED_BYTE,   2,    TRUE,	},
{    GL_UNSIGNED_BYTE,   3,    TRUE,	},
{    GL_UNSIGNED_BYTE,   4,    TRUE,	},
};


GLenum _flFASTCALL
PixelToFormat( unsigned int func, unsigned int* component, unsigned int* normal )
{
    assert( func < Image::FUNC_END );
    const _FormatType*   fp= &formatTable[ func ];
    if( component ){
        *component= fp->component;
    }
    if( normal ){
        *normal= fp->normalize;
    }
    return  fp->dxgi_format;
}

前回 GLInputLayout の CreateInputLayout() の中で呼び出していた
PixelToFormat() がこれです。
まだ FLOAT しか使っていないので本当に互換性があるかどうかは検証してません。
BYTE の並びは D3D9 の D3DDECLTYPE_UBYTE4 と D3DDECLTYPE_D3DCOLOR
のように順番が異なっている可能性があります。

同じく前回「ELEMENTFLAG_NORMALIZE の定義値が 1 なのは念のため。」と書いた
のはコンパイラの最適化で三項演算子が完全に消えることを期待していたからです。

(ep->Flag & ELEMENTFLAG_NORMALIZE) ? GL_TRUE : GL_FALSE

GL_TRUE / GL_FALSE の定義は 1 と 0 なので、最終的には
「ep->Flag & ELEMENTFLAG_NORMALIZE」だけが残ります。
予想通り分岐は消えますが、シンボルが 1 以外でも shr が一つ追加されるだけでした。

; ELEMENTFLAG_NORMALIZE == 4 の場合 (x64 + VC)
000000013F4D6697  shr         ebx,2 
000000013F4D66A1  and         ebx,1 
000000013F4D66A4  mov         edx,ebx 

シフトを命令に埋め込める ARM だったら全く意識する必要ないのかもしれません。


関連エントリ
OpenGLES 2.0 頂点フォーマットの管理
OpenGLES2.0 の頂点
OpenGLES2.0 D3D座標系
OpenGLES2.0 シェーダー管理


OpenGLES2.0 上にほぼ Direct3D 互換の環境を作っています。
頂点形式も同じものです。頂点バッファを作ることも出来ました。

GLuint _flFASTCALL
CreateBuffer(
    unsigned int bytesize,
    GLenum buffer,
    GLenum btype,
    const void* initdata
    )
{
    GLuint   bid;
    glGenBuffers( 1, &bid );
    glBindBuffer( buffer, bid );

    if( initdata ){
        glBufferData( buffer, bytesize, initdata, btype );
        ErrorHandle( "CreateBuffer" );
    }
    return  bid;
}

API 的に VertexBuffer / IndexBuffer の区別が無いのは Direct3D10 以降と同じです。

// VertexBuffer
vbuf= CreateBuffer( bytesize, GL_ARRAY_BUFFER, GL_STATIC_DRAW, vdata );

// IndexBuffer
ibuf= CreateBuffer( bytesize, GL_ELEMENT_ARRAY_BUFFER, GL_STATIC_DRAW, idata );

GL_STATIC_DRAW は Direct3D の D3D10_USAGE_/D3D11_USAGE_ や AccessFlag
に相当するものです。

glBIndBuffer( GL_ARRAY_BUFFER, vbuf );   // IASetVertexBuffers()
glBIndBuffer( GL_ELEMENT_ARRAY_BUFFER, ibuf );   // IASetIndexBuffer()

与えられるバッファの区別がないので、描画に使えるバッファは 1つだけ。
頂点データを同じバッファに格納することが前提となっているようです。

シェーダーとのバインドは glVertexAttribPointer() そのままです。

GLint loc_position= glGetAttribLocation( ShaderProgram, "POSITION" );
GLint loc_normal=   glGetAttribLocation( ShaderProgram, "NORMAL" );
GLint loc_texcoord= glGetAttribLocation( ShaderProgram, "TEXCOORD" );
glVertexAttribPointer( loc_position, 3, GL_FLOAT, GL_FALSE, 32, (void*)0 );
glVertexAttribPointer( loc_normal,   3, GL_FLOAT, GL_FALSE, 32, (void*)(sizeof(float)*3) );
glVertexAttribPointer( loc_texcoord, 2, GL_FLOAT, GL_FALSE, 32, (void*)(sizeof(float)*6) );

必要なパラメータもやってることも、D3D10_INPUT_ELEMENT_DESC,
D3D11_INPUT_ELEMENT_DESC によく似ています。
違うのは、バインドシンボルが SEMANTIC ではなく変数名そのままになること。

上の例だとシェーダー側はこんな感じです。
SEMANTIC を直接変数名にすることで D3D と同じような記述になります。
// GLSL
attribute vec3 POSITION;
attribute vec3 NORMAL;
attribute vec2 TEXCOORD;

void main()
{
   gl_Position= vec4( POSITION.xyz, 1.0 ) * Matrix;
}

Direct3D で書くと下記の通りです。

// HLSL
struct VS_INPUT {
   float3  vposition : POSITION;
   float3  vnormal   : NORMAL;
   float2  vtexcoord : TEXCOORD0;
};

float4 main( VS_INPUT vin ) : SV_Position
{
   return  mul( float4( vin.vposition.xyz, 1.0f ), Matrix );
}

デモやサンプルプログラムならこのままでいいのですが、汎用的な仕組みとして
シェーダーとのバインドを考えると、D3D の InputLayout 相当の機能が必要に
なることがわかります。(ID3D10InputLayout / ID3D11InputLayout)

なので実際に作っておきます。

// GLInputLayout.h
enum {
    ELEMENTFLAG_NORMALIZE  =  (1<<0),
    ELEMENTFLAG_ACTIVE     =  (1<<1),
    ELEMENTFLAG_END        =  (1<<2),
};

struct ShaderInputElement {
    unsigned int    Location;
    unsigned int    Type;       // GLenum GL_FLOAT
    unsigned char   Stride;
    unsigned char   ByteOffset;
    unsigned char   Components;
    unsigned char   Flag;
};


// GLInputLayout.cpp
void _flFASTCALL
CreateInputLayout( 
      ShaderInputElement* edesc,
      unsigned int desccount,
      GLuint shader,
      const a5::InputLayout& layout
        )
{
    unsigned int   ecount= layout.GetElementCount();
    assert( ecount <= desccount );
    for( unsigned int ei= 0 ; ei< ecount ; ei++, edesc++ ){
        const a5::InputElement*	ep= layout.GetElement( ei );
        unsigned int	component= 0;
        unsigned int	normal= 0;
        edesc->Flag= 0;
        if( ei == ecount-1 ){
            edesc->Flag|= ELEMENTFLAG_END;
        }
        GLint  loc= glGetAttribLocation( shader, ep->SemanticName );
        ErrorHandle( "Layout" );
        if( loc < 0 ){
            continue;
        }
        edesc->Location= loc;
        edesc->Type= PixelToFormat( ep->pixf, &component, &normal );
        edesc->Stride= static_cast<unsigned char>( layout.GetStrideSize() );
        edesc->Components= static_cast<unsigned char>( component );
        edesc->ByteOffset= static_cast<unsigned char>( ep->ByteOffset );
        if( normal ){
            edesc->Flag|= ELEMENTFLAG_NORMALIZE;
        }
        edesc->Flag|= ELEMENTFLAG_ACTIVE;
    }
}

void _flFASTCALL
IASetInputLayout( const ShaderInputElement* edesc )
{
    const ShaderInputElement*   ep= edesc;
    for(;; ep++ ){
        if( ep->Flag & ELEMENTFLAG_ACTIVE ){ 
            glVertexAttribPointer(
                ep->Location,
                ep->Components,
                ep->Type,
                (ep->Flag & ELEMENTFLAG_NORMALIZE) ? GL_TRUE : GL_FALSE,
                ep->Stride,
                (const void*)ep->ByteOffset
                );
            glEnableVertexAttribArray( ep->Location );
        }
        if( ep->Flag & ELEMENTFLAG_END ){
            break;
        }
    }
}

void _flFASTCALL
IAResetInputLayout( const ShaderInputElement* edesc )
{
    const ShaderInputElement*	ep= edesc;
    for(;; ep++ ){
        if( ep->Flag & ELEMENTFLAG_ACTIVE ){ 
            glDisableVertexAttribArray( ep->Location );
        }
        if( ep->Flag & ELEMENTFLAG_END ){
            break;
        }
    }
}

CreateInputLayout() はオブジェクトを生成しないで固定バッファを使用します。
ShaderInputElement がそのまま描画時に使える InputLayout 情報になります。

複雑な Flag 管理を行っているのは、エンドマークやバッファ数を別に持たなくて
済むようにです。必要 Element 分、固定で渡されたバッファで完結させるため。
メモリアロケートに抵抗がなければオブジェクトにした方がずっとシンプルでしょう。
ShaderInputElement の Type が 32bit なのは GLenum の互換性を考えたためです。
実際のシンボル値は 16bit なので、問題なければ圧縮して total 8byte に収まります。
シェーダーに対応するシンボルが無ければ頂点のセットアップを行いません。

a5::InputLayout は、個人的に Direct3D で使用している頂点情報管理 object です。
本来はこの object を元に D3D11_INPUT_ELEMENT_DESC を生成して、
ID3D11InputLayout を作っていました。

ELEMENTFLAG_NORMALIZE の定義値が 1 なのは念のため。

PixelToFormat() は頂点フォーマットに含まれている Direct3D の
DXGI_FORMAT を OpenGL 形式に変換しています。詳細は次回以降で。

// 初期化
a5::InputLayout layout( ~ );
ShaderInputElement vLayout[3];
CreateInputLayout( vLayout, 3, ShaderProgram, layout );

// 描画時
glBIndBuffer( GL_ARRAY_BUFFER, vbuf );   // IASetVertexBuffers()
IASetInputLayout( vLayout );
glBIndBuffer( GL_ELEMENT_ARRAY_BUFFER, ibuf );   // IASetIndexBuffer()

glDrawElements( GL_TRIANGLES, Indices, GL_UNSIGNED_SHORT, NULL );

頂点バッファ (頂点バッファオブジェクト)を使った描画命令も、頂点の設定同様
これまでと同じものでした。
Index の配列はすでに GL_ELEMENT_ARRAY_BUFFER で渡しているため
glDrawElements() の最後は NULL になります。
Indices は Index の個数なので GL_TRIANGLES だと常に 3 の倍数となります。


描画の仕組みを理解して API を使うだけなら難しいことは特にないです。
でも実際にゲーム開発を行っていると、サンプルだけではわからない事項がいくつも
出てきます。3D の描画エンジンを作っていて常に悩まされるのは、
汎用的に使える仕組みとその設計部分です。

例えば CG ツールからどうやって export してどんなフォーマットで保存するか、
数多く存在する頂点フォーマットやピクセルフォーマットに対応するにはどうするか、
データとシェーダーの対応付けやシェーダーの管理をどうやって行うか、など。

3D 系プログラムの開発はこのような地味で面倒なことの方が多いです。
blog の冒頭説明でも書いてますが、ゲーム開発のノウハウの差というのは目新しい
技術の実装だけでなく、データ制作やデータの管理もかなり含まれているはずです。

とりあえず export したモデルが表示できて、任意のシェーダーを適用して
アニメーション出来るところまでは作成済み。


関連エントリ
OpenGLES2.0 の頂点
OpenGLES2.0 D3D座標系
OpenGLES2.0 シェーダー管理


URoad-5000 の使い方でわからない点があったのでいくつか問い合わせてみました。
以下その結果をまとめたものです。


●バッテリー LED の意味

モードは 2種類あります。Battery モード と Charge モード

「Batteryモード」(バッテリー利用時のインジケータの色の意味)

 緑: 60%~100%
 橙: 60%以下
 赤: 残量小
 赤点滅: 残量ほぼゼロ


「Chargeモード」 (AC アダプタ接続時のインジケータの色の意味)

 緑: 90%以上充電済み
 橙: 充電中

※ モードの切り替わり時は LED が 3回点滅する

AC アダプタを接続すると LED の色の意味が変わります。
たとえば残量 70% だったら、AC アダプタをつなぐと緑から橙へ。
モードの切り替わりを示しているのが LED の 3回点滅だそうです。


●バッテリー充電時間と稼働時間

  バッテリーLED          充電時間          稼働時間の目安
  ---------------------------------------------------------
 赤点滅→緑 (0%→95%)   2時間半~3時間    約3時間
 緑→緑 (95%→100%)     +30分             +15分

Chargeモードで 緑色まで充電すると、スペック通りおよそ 3時間利用可能。
緑まで充電してからさらに 30分充電すると 100% です。


●ACアダプタをつなぐと勝手に電源が入る点について

・AC アダプタをつなぐと電源が入るのは仕様。
 このときルーターの動作が不要なら、電源を切っておいても問題無いそうです。
 電源を入れなくても充電できます。

・AC アダプタ接続中はバッテリーを消費しません。
 本体の動作は、バッテリーを経由しないで直接 AC アダプタの電源を使っている
 そうです。電源を切っても消費電力は減るけど充電時間は変わりません。


●その他 URoad-5000 利用状況など

電車内など移動中の利用はあきらめて外出時に使える回線として活用しています。
昼食時に DS を Wi-Fi につないだりとか。
ポケットに入れられるくらい小さくて薄くて軽いことと、バッテリーで動作出来るのが
良いです。ほとんど負担にならずに持ち歩けます。

つるつるな上面部は結構指紋が目立つ感じです。専用ケースも付属しているのですが
サイズが増すので今は使っていません。

本体の LED はそうでもないのですが、USB アダプタ側 UD01SS の青色 LED は非常に
まぶしく感じます。電波を探している間は点灯したままなので特に。
夜間とか外で使ってると目立つかもしれません。暗くするか消せると良いのですが。


関連エントリ
WiMAX ルータ URoad-5000 (3) 三日目
WiMAX ルータ URoad-5000 (2) 二日目
WiMAX ルータ URoad-5000
Windows7 beta で UQ WiMAX


モニターももうすぐ三ヶ月です。
このクラスの同スペックな PC はたくさんあります。
基本的な PC 部分の性能はほぼ横並びでそれほど大きな違いはありません。
PC 部分はおそらくあまり他と変わらない感想になってしまうことでしょう。

だからこそ差別化が必要で、その点メビウスは強烈な個性を持っていました。
光センサー液晶パッドを搭載していることです。

タッチパッド部分が液晶画面なのです。
この液晶パネルがセンサーも兼ねているとのこと。
この不思議なデバイスは、各画素が光に反応し2次元のイメージとして取り込むことが
できるようです。
処理次第では全く制限のない入力装置になるかもしれません。

惜しむらくはこの個性とテクノロジーが、メビウス自体の魅力に思ったより
つながっていなかったということです。
この点過去の書き込みでいくつか考察してきました。

それでもレポート開始当初はかなり楽観視していました。
6月に開発キットが公開されるとの記事があったからです。

ASCII.jp 謎のMebiusは光センサー液晶パッド搭載のNetbook!
 >光センサー液晶パッドのアプリケーション開発に必要な情報や開発キットは、
 >同社から6月くらいに提供される予定。ユーザーにより開発された独自アプリ
 >ケーションの登場も期待される。

初物で使いづらい点があったとしても、もしかしたら自力で何とか出来るのでは無かろうかと。
レポートに書くネタにもなるし仕組みや出来ることに興味もあったからです。

でも結局開発キットは無し。
アプリケーションが一つ追加されたのみで、光センサー液晶パッドで出来ることは
残念ながら発売直後とあまり変わりませんでした。


●おすすめできる点

・手書き文字入力

パソコンで手書文字認識できる機能はよくあります。その扱いやすさを考えると
電子辞書のように手前に液晶パッドが付いているスタイルはたいへん理にかなっています。

タッチパネル付きの PC は少なくないですし、タブレット PC だって各種あります。
でもキーボードの奥の液晶画面まで手を伸ばして、そのままペンで書くのはたいへんです。
逆に液晶を折りたたんでタブレット形状にしてしまうとキーボード操作に難儀します。

メビウス PC-NJ70A は普段の PC スタイルながら、すぐ手が届く手前にタッチパッド
があり、いつでもペンを扱えます。キーボード操作も妨げません。
メイン液晶+キーボード+液晶パッド、といったスタイルは今のところメビウス
独自のものです。

もう少し動作が速くて気持ちよく操作できたなら、手書き以外の他の機能も
お勧めできたかもしれません。


●改良希望点

通常のタッチパッドと比べてまだ使いづらいところがあります。
独自の専用アプリケーションもまだまだ不足しているように思います。
このように 1つ 1つの細かい点は気になりますし、実際にレポートやアンケートで
報告させていただきました。
でも本当は、ソフトウエアで継続して更新していけるのが PC の良いところです。

結論として、今最も感じている足りないところは、発売&モニターレポート開始から
3ヶ月経過してもほとんど変わってないところだと思います。

光センサー液晶パッドを使ったアプリが次々と出ていたり、アイデアをすぐに実現
出来る環境があったなら、今は多少不満があっても
「今後なんだか良くなっていきそうだ」 とか 「今後面白くなっていきそうだ」 といった
勢いや流れが見えていたかもしれません。
自分で何とか出来そうだ、と思えるだけでも十分だったと思います。

おそらく今一番危惧しなければならないのは、このまま変わらないのではないかと
思ってしまうことでしょう。


関連エントリ
メビウス PC-NJ70A (8) タッチアプリとイルミスキャン
メビウス PC-NJ70A (7) 一ヶ月後
メビウス PC-NJ70A (6) ユーザーインターフェース
メビウス PC-NJ70A (5) サブモニタ
メビウス PC-NJ70A (4) 使用感
メビウス PC-NJ70A (3) Windows7 RC を入れる
メビウス PC-NJ70A (2) 初期セットアップとメモリ増設
メビウス PC-NJ70A 液晶センサータッチパッド
メビウス PC-NJ70A 画面解像度とタッチ


今まで全く使っていなかったので、やはりいろいろな面でつまずいたりします。
特に操作に慣れておらず、初歩的なキーボードやマウスの設定が落ち着くまでも
結構かかりました。以下そのメモです。


●キーボードの配列

Windows 用キーボードを USB 接続で使っています。
システム環境設定のキーボードショートカットから Ctrl キーの配置変更ができます。
でも ESC キーの入れ替えがありません。
ソフトウエアでの入れ替えだとアプリケーションによってキーボードの認識に差が
生じる可能性もあったのであきらめました。

結局ハード的にキーの入れ替え機能を持ったキーボード (DHARMAPOINT DRTCKB91UBK)
に交換して、キーボード側で Ctrl/ESC の位置を置き換えています。
Mac 標準の日本語キーボードだと最初から Control キーが A の左に配置されて
いるので、これで良かったのかもしれません。


●日本語変換のキーカスタマイズ

標準の日本語変換だと設定項目が少なくキー操作の変更ができませんでした。
Windows の場合、MS-IME でも変換操作のキーを自由に変更することができます。
たとえば日本語入力中の文節移動や文節伸縮の操作を全部ホームポジションから
操作できるように設定できます。

ATOK2009 を導入しました。
web にはこの辺の情報が無くカスタマイズができるかどうかわからなかったのですが、
Windows 版では出来ているので入れてみました。
結果当たりでした。Windows 版同様にキー操作を変更できます。
カーソル移動や文節伸縮などを任意のキーに割り当て使っています。


●マウスの挙動

最初は気にならなかったのですが、使っていてどうもマウス挙動に違和感を感じる
ようになりました。カーソルが移動が遅いので、速度設定をあげると微調整が
スムーズにできなくなります。小さいボタンがうまく押せない感じ。
当初マウスの問題かと思って、新しいマウスに取り替えたり電池を替えたり
マウスパッドを用意してみたり、ワイヤードマウスにしたりといろいろ試しました。
下記のページによると OS 側の問題だったようです。

MacOS Xのマウス挙動 Pert2 (absurd-wings)
Mac OS X のマウス加速問題

Microsoft 製のマウス (Explorer mini) と、マウス付属のユーティリティソフト
Microsoft IntelliPoint を導入することで使いやすくなりました。



OpenCL で扱える SIMD の vector 型は、float4 や int4 といった型名表記に
なっています。この点は HLSL と同じです。
GLSL では vec4 や ivec4 等の独自の型が用いられていました。

また GLSL の場合 1コンポーネントの vec1 型は無く vec2~vec4 のみ。
OpenCL の仕様をよく見ると、最大 16要素まで扱えるものの使える組み合わせは
2, 4, 8, 16 だけです。つまり float1 も float3 もありません。

GLSL       (float)  vec2     vec3     vec4
OpenCL     (float)  float2   -        float4   float8   float16
HLSL       float1   float2   float3   float4


Compute Shader で用いられる HLSL では float1~float4 すべて使えます。
もともと vector<> の別名にすぎず、1~4 まで任意の数値を与えることができます。
つまり下記の定義と同じです。

typedef vector<float,4> float4;

また float v; と宣言した場合の v.x が許されるのも HLSL 独自かもしれません。


OpenCL は要素数が増えているため、swizzle も xyzw だけでなく s 表記が
追加されています。(rgba は無し)

float16  v;
  v.s0 // == v.x
  v.s1 // == v.y
  v.s2 // == v.z
  v.s3 // == v.w
  v.s4
  ~
  v.se
  v.sf

例えば v.xy は v.s01 と記述可能。float16 の全要素代入を明示的に書くなら

float16  d, s;
d.s0123456789abcdef = s.s0123456789abcdef;

逆順にするなら

d.s0123456789abcdef = s.sfedcba9876543210;

float16 を 4x4 matrix とみなすなら Transpose (転置) は

d.s0123456789abcdef = s.s048c159d26ae37bf;

と書けます。
値数は括弧表記で、コンストラクタではなくキャストが付きます。

float16  d= (float16)(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);

すべて同じ値ならスカラーからの変換が使えて d= (float16)(0); と書けます。
スカラー以外は個数が一致する必要があります。

float4	 a= (float4)( 1.0f );
float16  d= (float16)( a, a, a, a );
float16  e= (float16)( (float8)(1.0f), (float8)(2.0f) );

一つ下のサイズの vector を取り出す suffix もあります。odd, even, lo, hi

float16  a= (float16)(1);
float8   b= a.odd; //== a.s13579bdf
float4   c= b.odd; //== b.s1357
float2   d= c.odd; //== c.s13 == c.yw
float8   l= a.lo;  //== a.s01234567
float8   h= a.hi;  //== a.s89abcdef
float4   ll= a.lo.lo; //== (a.s01234567).s0123 == a.s0123

おそらく特定の swizzle の別名です。
s 表記でも書けます。a.s01234567.s1357.s01 (== a.lo.odd.lo)

opencl-1.0.43 には 4x4 Transpose の応用例が載っています。

KHRONOS GROUP OpenCL
Khronos OpenCL API Registry

opencl-1.0.43.pdf page 134
//transpose
t.even = x.lo;
t.odd = x.hi;
x.even = t.lo;
x.odd = t.hi;

x t はどちらも float16。
展開してみます。

t.s02468ace = x.s01234567;
t.s13579bdf = x.s89abcdef;
// t: 0 1 2 3 4 5 6 7    <= x.lo
//     8 9 a b c d e f   <= x.hi
// t= 08192a3b4c5d6e7f

x.s02468ace = t.s01234567;
x.s13579bdf = t.s89abcdef;
// x: 0 8 1 9 2 a 3 b    <= t.lo
//     4 c 5 d 6 e 7 f   <= t.hi
// x= 048c159d26ae37bf

なんだか SSE のプログラムを見ているようです。
1行で記述できるのにこのような例が載っているということは、
Larrabee のプログラミングはこんな感じなのかもしれません。

GPU べったりのシェーダーとはまた違った印象です。


関連エントリ
ATI Stream SDK 2.0 beta と OpenCL


Windows 版 vim を使っています。
といってもちょっと便利な vi 程度にしか使っておらず、カスタマイズは最小限で
済ませています。コマンドシェルから gvim を起動してファイル毎に別のエディタを
開くスタイルです。
shell + gvim の組み合わせは X68000 の Ko-Window 上で vi (stevie) を使う
場合とよく似ています。shell と別ウィンドウでいくつでも開けるからです。
ほとんど同じ感覚で使用しています。

vim は vi 系のテキストエディタです。
コマンドシェルからヒストリを使ってエディタを呼び出していると、すでに開いている
ファイルなのに、つい多重で開いてしまうことがあります。
同じファイルを複数のエディタで同時に開くのはまずいです。
最後に保存した方の更新しか残らないからです。
stevie の時はよくミスしがちでした。

幸い vim の場合はあまり困ったことにはなりません。
ファイルを開くと .swp ファイルが作られるので、そのファイルがすでに編集中なのか
わかります。すでに .swp ファイルがあるなら、このまま開くか ReadOnly
で続けるか、起動を中止するか確認してくれます。

便利な反面、衝突するたびに動作を選択して中止し、起動中のウィンドウから
目的のものを探し出すのはだんだん面倒になってきます。
そこで、下記のようなプログラムを作って使っていました。

・指定したファイルがすでに gvim で開いているかどうか調べる
  ・もしすでに存在していたら、そのウィンドウをアクティブにする
  ・無ければ新たに gvim でそのファイルを開く

もしかしたら vim 自体に何らかの設定があったり、最初から似たような機能が付いて
いたり、または同等のものが存在しているのかもしれません。
必要あるかどうかわかりませんが置いておきます。

runvim100.zip

使い方
runvim [option] [<ファイル名>]

 -e<起動するgvimのパス>

たとえば alias や shell の関数定義で下記のように定義しておきます。
(gvim のパスは任意)

alias vi='runvim.exe -eC:/app/Vim71/gvim.exe'

上記定義した後「vi ファイル名」で起動すると、すでに開いていれば対応する
gvim を最前面に持ってきて入力フォーカスします。
無ければ gvim を通常起動します。


以下 vim (7.1) の個人的な設定です。
Vim 自体は本家 vim.org からダウンロードしたパッケージをそのままインストール
しています。

" $HOME/_vimrc
se ai columns=80 lines=46 hls nobackup ruler
se guifont=MS_ゴシック:h8:cSHIFTJIS
se linespace=0
se guioptions=grLt
se mouse=a
se iminsert=0 imsearch=0
se enc=cp932
se guioptions-=a
se statusline=%<%f\ %m%r%h%w%{'['.(&fenc!=''?&fenc:&enc).']['.&ff.']'}%=%b\ %B\ %l,%c%V%8P
lan en
lan cty en
lan mes en
lan tim ja
syntax enable
hi Constant	guifg=#a03800
hi PreProc	guifg=#008010
hi Folded	guifg=#808000 guibg=#fffffe
hi FoldColumn	guifg=#808000 guibg=#fffffe
so $HOME/format.vim
let format_join_spaces=2
se foldmethod=syntax

実はフォントは難ありで、この設定だと小文字のエル=l と大文字のアイ=I の区別が
付かないけど慣れました。このままプログラム書いてます。

format.vim は日本語で join したときに空白が入らないようにするため使用しています。

doxygen syntax を追加しています。
Vim を install したフォルダ内の filetype.vim を書き換えます。
"setf cpp" や "setf c" と定義されているところを全部 "setf cpp.doxygen"
のように変更します。".doxygen" を追加するわけです。
これで syntax として cpp が選択されると同時に doxygen のキーワードハイライト
が有効になります。

折りたたみ機能も利用しています。
そのまま folding を有効にすると C ソースコードのブロックも折りたたまれて
しまうので、その設定は削除。その代わり doxygen のコメントを部を折りたためる
ようにしています。

(1) syntax/c.vim の中の syntax ~ fold ~ と書かれた行の "fold" を全部削除
  たとえば下記のように (syn ~ で始まる場合もあり)
syntax region	cBlock		start="{" end="}" transparent fold
 ↓
syntax region	cBlock		start="{" end="}" transparent

(2) syntax/doxygen.vim に下記の 2行を追加
syn region myDoxFold start="/\*!" end="\*/" transparent fold keepend
syn sync fromstart

折りたたみ操作は vim 上で zr または zm です。


.hlsl や .fx 等、自分が使うファイルの syntax も追加しています。
専用の syntax ファイルが無くても、C言語系なら c や cpp を割り当てるだけで
見やすくなります。
たとえば filetype.vim を書き換えてこんな風に。(fsc は自前の C言語スクリプト)
" filetype.vim
au BufNewFile,BufRead *.fsc			setf cpp.doxygen


Vim の syntax や script ファイルは下記ページで検索できます。

vim.org Scripts Browse all


関連エントリ
vi の話


まだ未対応らしく Windows7 にはうまくインストールできませんでした。

ATI Stream Software Development Kit (SDK) v2.0 Beta Program

インストーラが正常終了しても Program Files の ATI Stream フォルダが
空のままです。
とりあえず Vista で展開したのちパスを通せば使えなくはないようです。
よく見ると OpenCL は GPU 未対応で CAL も無くなっています。
Intel CPU (+GeForce) でも動きました。

CL_PLATFORM_PROFILE    = FULL_PROFILE
CL_PLATFORM_VERSION    = OpenCL 1.0 ATI-Stream-v2.0-beta2
CL_PLATFORM_NAME       = ATI Stream
CL_PLATFORM_VENDOR     = Advanced Micro Devices
CL_PLATFORM_EXTENSIONS = 

CL_DEVICE_TYPE = 2
CL_DEVICE_VENDOR_ID = 4098
CL_DEVICE_MAX_COMPUTE_UNITS = 8
CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS = 3
CL_DEVICE_MAX_WORK_ITEM_SIZES = 1024 1024 1024
CL_DEVICE_MAX_WORK_GROUP_SIZE = 1024
CL_DEVICE_EXECUTION_CAPABILITIES = 1

Core i7 の結果です。
CL_DEVICE_TYPE の 2 は CPU (CL_DEVICE_TYPE_CPU) を表しています。
CL_DEVICE_MAX_COMPUTE_UNITS は 8個。
Core2 Duo で走らせると CL_DEVICE_MAX_COMPUTE_UNITS = 2 となります。

Device Type は下記の通り 3種類定義されています。

2 CL_DEVICE_TYPE_CPU
4 CL_DEVICE_TYPE_GPU
8 CL_DEVICE_TYPE_ACCELERATOR

Compute Shader は GPU 上で走るプログラムを共通化するのが目的でした。
OpenCL は CPU/GPU やその他を含めた、より幅広いプロセッサを対象として
作られているようです。

Direct3D の Compute Shader も CPU でエミュレーションできれば同じですが
現状だと WARP (10.1) も対応しておらず、Reference でしか動作しません。
エミュレーションできても GPU, CPU 混在しての利用は想定されていないといって
良いでしょう。

CL_DEVICE_EXECUTION_CAPABILITIES は CL_EXEC_KERNEL のみセットされています。
CL_EXEC_NATIVE_KERNEL が無いので、clEnqueueNativeKernel()
は実行出来ないようです。

バッファの転送や実行は Command Queue を生成して非同期に監視します。
GPU の描画操作に似ています。
異なるのは Out of order で実行可能なこと。
そのままでは実行順の保証が出来ませんが、その代わり各コマンドに依存関係を
設定できます。特定のコマンドの完了を待ってから実行できるわけです。

Direct3D11 でも DeviceContext が分離され、Command List (Queue) に
蓄えられる GPU 命令と、そうでないものを区別できるようになっています。
ただこちらは、複数のスレッドで作成したコマンドを単一のコンテキストで実行する
ための仕組みです。コマンドバッファ内の実行順は決まっているため、
スケジューリングは呼び出す側に委ねられています。

OpenCL の kernel の記述言語は glsl とは別もので、より C言語に近いものです。
long は 64bit。vector も 16個まで扱えるようです。
4コンポーネントを超えた場合、各要素は16進数指定で s0, s1,~ sf といった表記
になっています。

D3D の Compute Shader はデバイスやリソース管理等を D3D のコンポーネントに
委ねています。Direct3D の一部であって言語も hlsl そのまま同じものでした。

OpenCL の場合完全に独立しており、リソース管理も言語も OpenCL の世界で作られています。
コマンドをいくつか拡張してレンダリング向け補助機能を追加すれば、新しい
3D API セットができるのではないかと思うくらい。
その分 ATI CAL とか、独自 SDK と比べると複雑になった印象を受けるかもしれません。
リソースは描画と共有可能で、D3D や OpenGL からも OpenCL のバッファを作成
出来るようになっています。

const int THREAD_SIZE= 32;
cl_int	  status= 0;
cl_uint	  nums;
cl_platform_id	pid= 0;
cl_device_id	device= 0;

status= clGetPlatformIDs( 1, &pid, &nums );
status= clGetDeviceIDs( pid, CL_DEVICE_TYPE_CPU, 1, &device, &nums );

cl_context  context= clCreateContextFromType( NULL, CL_DEVICE_TYPE_CPU, NULL, NULL, &status );
cl_command_queue   command= clCreateCommandQueue( context, device, CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE, &status );

cl_mem	mem= clCreateBuffer( context, CL_MEM_READ_WRITE, THREAD_SIZE * sizeof(cl_float), NULL, &status );

const char*   src=
    "__kernel void main( __global float* op )\n"
    "{\n"
    "   unsigned int   tid= get_global_id(0);\n"
    "   op[tid]= tid * tid;\n"
    "}";
size_t	srcsize= strlen( src );
cl_program  prog= clCreateProgramWithSource( context, 1, &src, &srcsize, &status );

status= clBuildProgram( prog, 0, NULL, NULL, NULL, NULL );

cl_kernel  kernel= clCreateKernel( prog, "main", &status );

status= clSetKernelArg( kernel, 0, sizeof(cl_mem), &mem );

size_t	gthread= THREAD_SIZE;
size_t	lthread= 1;
cl_event   event;
status= clEnqueueNDRangeKernel( command, kernel, 1, NULL, >hread, <hread, 0, NULL, &event );

cl_event   event2;
cl_float   memptr2[THREAD_SIZE];
status= clEnqueueReadBuffer( command, mem, CL_TRUE, 0, THREAD_SIZE * sizeof(cl_float), memptr2, 1, &event, &event2 );

status= clWaitForEvents( 1, &event2 );
clReleaseEvent( event );
clReleaseEvent( event2 );

for( int i= 0 ; i< THREAD_SIZE ; i++ ){
    printf( "%d: %f\n", i, memptr2[i] );
}

status= clReleaseKernel( kernel );
status= clReleaseProgram( prog );
status= clReleaseMemObject( mem );
status= clReleaseCommandQueue( command );
status= clReleaseContext( context );



HP TouchSmart IQ800 で Multi touch を試すには Windows7 が必要ですが
ノート用 GPU を使っているせいかグラフィックドライバがうまく入らない
ことがあります。

HP TouchSmart PC IQ800

GeForce 9600M GS に対応しているはずの 186.03 もそのままでは install できません。
Vista 用ドライバも使えるので、もともと付属している純正ドライバは入れられます。
プリインストールされている Vista のドライブパーティションを残してあるなら
\hp\drivers\nVidia_Graphics がそれです。

IQ827jp の場合付属しているのは WindowsVista x86 (32bit) なので、上記ドライバも
x86 のみです。Windows7 x64 を入れる場合は使えませんでした。
HP のサイトから IQ817jp 用の Vista x64 ドライバを落とせますがこれもだめです。

Windows7 beta のときはこんな感じでドライバを入れました。

一応 186.03 も inf を書き換えれば何とかなりそうです。
もともと GeForce 9600M GS に対応していると書かれているので
inf ファイルからそれらしき記述を探します。
まず nvac.inf を調べてみます。

NVIDIA_DEV.0648.** というシンボルが 9600M GS のようです。

デバイスの ID が一致しているかどうか確認します。

デバイスマネージャー → Display adapters
   → Standard VGA Graphics Adapter → プロパティ
details タブ Hardware Ids を見ると ID がわかります。& が区切り。

VEN = 10DE
DEV= 0648
SUBSYS= 900F1043

DEV が 0648 なので上で調べたシンボルと一致しています。
対応する SUBSYS が無いためそのままインストールできなかったのだと思われます。
9600M GS 相当の定義エントリ「%NVIDIA_DEV.0648.**% = ~」の行を複製し、
「**」の番号を別の重複しない値 (今回は18) に書き換えます。
SUBSYS_ の後ろを 900F1043 に置き換えることで一応インストールできる
ようになりました。


%NVIDIA_DEV.0648.18% = Section005, PCI\VEN_10DE&DEV_0648&SUBSYS_900F1043

行を複製した部分は 3カ所です。そのうち SUBSYS を書き換えたのは 2カ所。
nvac.inf 以外にも inf ファイルは多数存在し、他にも 9600M GS の記述がみられます。
本当に nvac で良いのか根拠は全くないです。
何らかの問題が生じる可能性もあるので、もし試す場合は必ず自己責任でお願いします。
早く公式な Windows7 ドライバが出てくれると良いのですが。

Aero にするにはドライバインストール後にシステム評価の再計測が必要です。

WDDM 1.1

CPU: 6.0
RAM: 6.0
AERO: 6.3
GAME: 6.3
HDD: 5.9


関連エントリ
Windows7 Multitouch API (3)
Windows7 とマルチタッチ / HP TouchSmart PC IQ800


Windows7 を RTM 版に入れ替えたら D3D11(beta) のプログラムが動かなくなって
いました。DirectX SDK March2009 + D3D11 を使用しており、RC まではきちんと
動いていたものです。

止まっているのが CommandList まわりの呼び出しで関係ない関数に飛んでいます。
lpVtbl がずれているような感じ。
単に beta と互換性がなくなっているだけかもしれません。
新しい Windows7 SDK には D3D 関連のヘッダや lib も含まれているため、
こちらを使えば RTM 版 dll が用いられます。
d3dx11 や d3dcompiler は無いので、この辺を使っている場合は DirectX SDK も
併用することになります。core ではないためおそらく大丈夫だと思います。

でも結局 Windows7 SDK の d3d11.lib に切り替えてもだめ。
コンソールをみると d3d11.dll だけでなく最後に d3d11_beta.dll も読み込まれて
いました。この両者が混在しているのはあまりよい感じではありません。

DirectX SDK サンプルは動くしサンプルをビルドしても大丈夫です。
サンプルとの違いは DXGI1.1 を使っていることくらいでしょうか。
試しに DXGI1 に戻してもやっぱり動きません。
DXGI1.1 は Windows SDK 側の dxgi.lib で使用できるはずなので、問題ないと
思います。

lib の検索順番や、リンクする lib の組み合わせをいろいろテスト。
気になるのは Windows SDK 側の d3d11.lib のみ使うようにしても、必ず最後に
d3d11_beta.dll が読み込まれてしまうことです。
誰が読み込んでいるのだろうと調べているうちに DebugLayer が原因ではないかと
思いつきました。

D3D11Create~() で D3D11_CREATE_DEVICE_DEBUG を指定していると、
Debug でも Release build でも最後に d3d11_beta.dll が読み込まれています。
とりあえずこのフラグ指定を消すと Windows7 RTM で動作しました。
やはり d3d11_beta.dll との互換性が問題だと思われます。

DirectX SDK がリリースされるまで待った方が良さそうです。


OpenGLES の頂点データは各頂点要素毎の配列を登録します。
頂点座標や法線など、頂点の構成要素は Direct3D では element、OpenGL では
attribute と呼ばれているようです。

glVertexAttribPointer( vloc, 3, GL_FLOAT, GL_FALSE, sizeof(vec3), v_data );
glVertexAttribPointer( nloc, 3, GL_FLOAT, GL_FALSE, sizeof(vec3), n_data );
glVertexAttribPointer( tloc, 2, GL_FLOAT, GL_FALSE, sizeof(vec2), t_data );

Direct3D の場合は基本的にパックされた頂点データを用います。
いわゆる AOS で、対する OpenGL はベクター単位の SOA になります。
とはいえ実際には Direct3D でも複数の頂点ストリームを与えることが出来るので、
OpenGL のように要素毎に配列を分離することが可能です。

また OpenGL の場合も index は共有なので頂点の位置や配列の大きさも同数です。
stride を指定すれば、Direct3D のようなパックした頂点データのポインタを登録
することができます。
どちらも出来ることや使い方にほとんど差が無くなっています。

struct vtype {
    float x, y, z;
    float nx, ny, nz;
    float tu, tv;
};
glVertexAttribPointer( vloc, 3, GL_FLOAT, GL_FALSE, sizeof(vtype), &vp->x );
glVertexAttribPointer( nloc, 3, GL_FLOAT, GL_FALSE, sizeof(vtype), &vp->nx );
glVertexAttribPointer( cloc, 2, GL_FLOAT, GL_FALSE, sizeof(vtype), &vp->tu );

普段 Direct3D 向けにデータを出力している関係上、座標系と同じように D3D と同じ
データが使えれば楽出来ます。
問題はどちらが効率がよいのかと言うこと。
D3D 形式の頂点の持ち方で速度が落ちないかどうかが心配な点です。

キャッシュの利用効率を考えると、一見インターリーブされた D3D タイプの方が
良さそうに見えます。
一度にアクセスするであろうデータが適切な局所性を持つからです。
頂点は index によるランダムアクセスになる可能性があるので、SOA の配列の効率が
必ずしも最善とは限りません。でも GPU は 1頂点分まとめてデータを読み込む
ことがわかっているので、一カ所に集まっていた方がキャッシュを活用できます。

以前そう考えて最適化のつもりで試したものの、逆に遅くなってしまうハードウエア
がありました。インデックスも個別に持てたので、頂点ストリーム毎にキャッシュを
持つなどの特殊な仕組みだったのかもしれません。もしそうなら D3D タイプの
インターリーブは同じデータが重複してキャッシュに乗ることになってしまいます。

ただこれは特殊なケースかもしれません。いろいろ見てみると、やはり最適化手法
としてインターリーブを推奨している場合もあるようです。
普通は D3D と同形式でデータを扱って問題ないと思われます。
これでデータもライブラリコードも D3D と全く同じように扱う準備が出来ました。


関連エントリ
OpenGLES2.0 D3D座標系
OpenGLES2.0 シェーダー管理


先日の SSE 命令の実行速度を試してみました。
あまり厳密なものではなくて、掲載したコード 3種類をそのままループで回しただけです。

● Core2 Duo P8600 2.4GHz Windows7 x86

x86  SSE1       17581 (msec)  mulps+addps
x86  SSE3       20623         haddps
x86  SSE4.1     17799         dpps

● Core i7 940 2.93GHz Windows7 RC x64

x86  SSE1       14320         mulps+addps
x86  SSE3       11716         haddps
x86  SSE4.1     13682         dpps

x64  SSE1       17269         mulps+addps
x64  SSE3       11076         haddps
x64  SSE4.1     13681         dpps

単位は時間で値が小さい方が高速です。

Core2 実行時、SSE3 hadd で遅くなってしまい少々焦りました。
昨日これで良いって書いたばかりなのに。結局元のコードの方が速いという結果に。
Core i7 の場合は正反対で hadd が最速になっています。

おかしなことに x64 のみ SSE1 で極端に速度が落ちています。
実際に x64+SSE1 の展開されたコードを見てみると、x64 で増えたレジスタを最大限
使おうとしてループの先頭で全部レジスタにロードしていました。
その分転送などの命令が他のケースよりも増えています。

このテストは局所的な小さいループかつ、使用したデータもストリーム入出力ではなく
同じ領域へのたたみ込みでした。
それゆえすべてのデータがキャッシュに乗った状態だと考えられます。
レジスタ間の転送命令が増えるよりもおそらくキャッシュに乗ったメモリから
読み直した方が速いのでしょう。

実際に SSE 命令が使われるケースだとストリーム処理が多いため、必ずしも
このような結果にはならないと思われます。

haddps , dpps の実行性能は CPU によって逆転することがわかりました。
この両者は比較的似たようなコードに展開されています。
ループ内の命令数も同数でした。
プロセッサによって異なるため、やはり指針としては命令数を減らすことを考えて
書くのが良さそうです。

関数は intrinsics 命令で記述しためコンパイラは inline 展開しており、
複数の関数に渡って並べ替えなどの最適化が行われています。
intrinsic 命令がどのように展開されるかだけ把握しておけば asm を使わない方が
良いかもしれません。x64 だと使えないし。
_mm_setr_ps とかは結構命令数食います。


関連エントリ
Direct3D Matrix の並びと SSE 命令
Intel AVX その3 命令
D3D10 row_major column_major
SSE についてのメモ(2) SSE4など


Matrix の並びには row major と column major の2種類あります。
たまにどっちがどっちかわからなくなりますが、row major が「三」で
column major が「川」。
演算上はどちらも違いは無くデータ配列が異なっているだけです。
特にシェーダーのコード上は大差ないです。演算もデータ構造も使う側に委ねられて
いてどちらでも使えます。

シェーダーコンパイラ fxc は /Zpr, /Zpc のオプションで並びの方向を指定する
ことが出来ます。
このオプションによって、たとえば vector * matrix の乗算が dp (dot product)
に展開されるか mad に展開されるか切り替わります。
どっちの設定だろうが乗算の前後を入れ替えれば良いだけなので、正直自分の記述と
デフォルトが異なっている場合にオプションで入れ替えればよい、とそのくらいの
認識でした。

fxc のデフォルトは /Zpc で column-major になっています。
上の演算であれば mad に展開されます。

シェーダーの場合 dp, mad どちらでも基本的に演算命令数は変わらず、効率の差は
生じません。ただ mad の方が vector の w=1 の代入が簡略化されるケースがある
ため小さくなることがあります。
これがそのまま GPU ネイティブコード上でも影響があるかどうかはまた別です。
AMD stream のように swizzle 時に直接 w に 1 を書き込めるなら差は無くなると
思います。

; opos= mul( mat, float4(v.xyz,1) );

; /Zpr
mov r0.xyz, cb0[4].xyzx
mov r0.w, l(1.000000)
dp4 o0.x, cb0[0].xyzw, r0.xyzw
dp4 o0.y, cb0[1].xyzw, r0.xyzw
dp4 o0.z, cb0[2].xyzw, r0.xyzw
dp4 o0.w, cb0[3].xyzw, r0.xyzw

; /Zpc
mul r0.xyzw, cb0[1].xyzw, cb0[4].yyyy
mad r0.xyzw, cb0[0].xyzw, cb0[4].xxxx, r0.xyzw
mad r0.xyzw, cb0[2].xyzw, cb0[4].zzzz, r0.xyzw
add o0.xyzw, r0.xyzw, cb0[3].xyzw

Direct3D ではもともと column major の形式で D3DMATIRX が定義されていました。
D3D9 以前、固定機能パイプラインでは API に渡すデータ並びを共通化する必要が
あったためと思われます。
そのため自前のライブラリでもほぼ D3DMATRIX 互換で使っていました。

typedef struct _D3DMATRIX {
    union {
        struct {
            float   _11, _12, _13, _14;
            float   _21, _22, _23, _24;
            float   _31, _32, _33, _34;
            float   _41, _42, _43, _44;

        };
        float m[4][4];
    };
} D3DMATRIX;

ただし実際はターゲットハードウエアやライブラリの仕様に合わせたり、最適化の
ために row major 形式に変換することが多かった思います。
シェーダーの演算上は大差なくても contant buffer の利用効率に影響があるからです。
昔は Constant Buffer が非常に小さかったため、index で参照する matrix は
4x3 でないとほとんど入りませんでした。
そのため Bone として使う geometry 用の matrix 乗算ルーチンをライブラリに
加えています。これは乗算結果を転置の 4x3 で返す専用命令です。

そろそろ D3DMATRIX 互換性もいらないかなと思い、最初から全部 row major で
持つようにライブラリを書き換えてみました。

基本的には自前の Matrix を下記のように定義し直すだけで、メンバにアクセスして
いるコードはほぼそのまま動きます。

struct {
    float  _11, _21, _31, _41;
    float  _12, _22, _32, _42;
    float  _13, _23, _33, _43;
    float  _14, _24, _34, _44;
};

修正が必要なのは、部分的に vector みなしてアクセスしていた処理と、SSE 命令で
記述していた各種関数です。
4x4 の matrix 同士の乗算では修正は不要ですが、それ以外はいろいろ見直す必要が
ありそうです。
たとえば vector との乗算だと、mad ではなく dp 相当の水平演算になります。

SSE4.1 の dpps を使ってみました。SSE4 のヘッダは smmintrin.h。
実命令数がわかるように dest 側のレジスタを共通にしています。

// SSE4.1
__m128 xmm0;
xmm0= _mm_load_ps( &v.x );

xmm1= _mm_load_ps( &_11 );
xmm1= _mm_dp_ps( xmm1, xmm0, 0xf1 );

xmm2= _mm_load_ps( &_12 );
xmm2= _mm_dp_ps( xmm2, xmm0, 0xf2 );
xmm1= _mm_add_ps( xmm1, xmm2 );

xmm2= _mm_load_ps( &_13 );
xmm2= _mm_dp_ps( xmm2, xmm0, 0xf4 );
xmm1= _mm_add_ps( xmm1, xmm2 );

xmm2= _mm_load_ps( &_14 );
xmm2= _mm_dp_ps( xmm2, xmm0, 0xf8 );
xmm1= _mm_add_ps( xmm1, xmm2 );

Shader のように簡単に記述できますが SSE4.1 対応 CPU (Penryn 以降) でないと
動きません。
試しに SSE3 の haddps を使ってみました。

// SSE3
xmm0= _mm_load_ps( &v.x );

xmm1= _mm_load_ps( &_11 );
xmm1= _mm_mul_ps( xmm1, xmm0 );

xmm2= _mm_load_ps( &_12 );
xmm2= _mm_mul_ps( xmm2, xmm0 );

xmm1= _mm_hadd_ps( xmm1, xmm2 );

xmm3= _mm_load_ps( &_13 );
xmm3= _mm_mul_ps( xmm3, xmm0 );

xmm2= _mm_load_ps( &_14 );
xmm2= _mm_mul_ps( xmm2, xmm0 );

xmm3= _mm_hadd_ps( xmm3, xmm2 );
xmm1= _mm_hadd_ps( xmm1, xmm3 );

全く同じ命令数でした。
SSE3 対応 PC の方が多いしこっちの方が良さそうです。
SSE4 命令は命令長が長いので生成バイナリも小さくなります。
ただもっと複雑なケースだと、入力を自由に選択できたり出力マスク可能な dpps
の方が融通が利いて少ない命令で書けるようです。
以前はこんな感じ。

// SSE1 (column major)
xmm0= _mm_load_ps( &v.x );

xmm1= xmm0;
xmm1= _mm_shuffle_ps( xmm1, xmm1, _MM_SHUFFLE(0,0,0,0) );
xmm2= _mm_load_ps( &_11 );
xmm1= _mm_mul_ps( xmm1, xmm2 );

xmm3= xmm0;
xmm3= _mm_shuffle_ps( xmm3, xmm3, _MM_SHUFFLE(1,1,1,1) );
xmm2= _mm_load_ps( &_21 );
xmm3= _mm_mul_ps( xmm3, xmm2 );
xmm1= _mm_add_ps( xmm1, xmm3 );

xmm3= xmm0;
xmm3= _mm_shuffle_ps( xmm3, xmm3, _MM_SHUFFLE(2,2,2,2) );
xmm2= _mm_load_ps( &_31 );
xmm3= _mm_mul_ps( xmm3, xmm2 );
xmm1= _mm_add_ps( xmm1, xmm3 );

xmm0= _mm_shuffle_ps( xmm0, xmm0, _MM_SHUFFLE(3,3,3,3) );
xmm2= _mm_load_ps( &_41 );
xmm0= _mm_mul_ps( xmm0, xmm2 );
xmm1= _mm_add_ps( xmm1, xmm0 );

matrix 同士の乗算も水平演算命令で書けるかと思い少々考えてみましたがレジスタ
が足りなくてうまくいっていません。
x64 だとレジスタ数が倍あるため intrinsic で書いたとおりのコードに展開されます。
x86 では待避のためのスタックが使われていました。


関連エントリ
Intel AVX その3 命令
D3D10 row_major column_major
SSE についてのメモ(2) SSE4など


今日は不調でした。

●繊細な WiMAX

接続が切れる場所は結構あります。
移動中安定して使えないのは、ハンドオーバーが出来ないからではなく
圏外になる区間がまだあちこちあるからです。
エリア以外でも、たとえば停車中でも併走する路線に電車が横付けされると
その間通信が止まったりすることがあります。
同じ電車でも車両や場所、混み具合、上り下りの違いでも影響があるようです。

ただ一時的に通信が止まっても、圏内になって電波をつかめばまた通信は始まります。
スムーズに復帰さえできれば十分で、全く使えないってことはなく使えます。
というのも、つながる場所も切れる場所も毎日通っているとほぼ把握できるから。
つながればそれなりに速いので、いつ更新しておけばいいのかタイミングがわかります。


●問題再発

URoad-5000 の問題は WiMAX とはまた別のところにあるようです。
何らかのタイミングでアダプタが反応しなくなることがあります。

・UD01SS の LED が点灯したまま
・URoad-5000 の WiMAX 電波受信 LED は直前の状態で固定 (変化しなくなる)

一時的に通信が途切れて停止し、その後復帰のための初期化待ちで失敗している
ように見えます。
無線 LAN は生きており、WiMAX のアンテナランプも点灯したままなので一見
きちんとつながっているように見えます。でも通信しても反応はなし。
新宿駅でホームに別の電車が入ってきたときに通信が止まり、その後目的地に
着くまで復活しませんでした。

一度 UD01SS を抜いて差し直したのち、しばらく待つと復活しました。
UD01SS を差し直した直後、しばらく UD01SS の LED が点灯したままになる点に
注意です。またハングアップ状態かと思いがちですが根気よく待ちます。

初日にほとんど使えなかったのも、これが原因だったと思われます。
VAIO type P で UD01SS を使用していたときはこのような症状は出ていませんでした。


●LED の読み方

URoad-5000 の本体 LED と、UD01SS の LED 両方から状態を判断する必要があります。

WiMAX アダプタ UD01SS 本体の青色 LED

(1) URoad-5000 の電源が入っていない状態 - 消灯
(2) URoad-5000 の電源を入れてもまだ初期化されていない状態 - 消灯
(3) URoad-5000 の無線も起動し、WiMAX の初期化が始まった状態 - 常時点灯
(4) 初期化が終わり、WiMAX を検索している状態 - 消灯
(5) WiMAX に接続できた場合 - 通信があると点滅、無ければ消灯

初期化中 (4) のようにしばらく常時点灯する期間があります。
URoad-5000 本体の WiMAX 電波ランプは、電波をつかむ (5) の段階になるまで消灯
したままです。

現時点でわかってる状態をまとめてみます。

                UD01SS LED        本体WiMAX LED
---------------------------------------------------------------------
本体電源off        消灯             消灯
本体電源on         消灯             一瞬点灯
本体初期化中       消灯             点灯
無線LAN起動後      消灯             消灯
WiMAX初期化中      常時点灯         消灯
WiMAX検索中        消灯             消灯
WiMAX接続後     通信に応じて点滅  電波強度=色,通信=点滅(圏外=消灯)
圏外から脱出等     常時点灯         消灯
ハングアップ時     常時点灯         直前の状態で固定

これらの挙動はファームアップなどで変わる可能性があります。

ハングアップかどうかの判断は、UD01SS の LED が点灯したままで長時間通信
できない状態が続いた場合です。通常はしばらくたつと LED が消えます。
このとき高い確率で本体の WiMAX LED が点灯しており、色が変化しません。

やはり瞬間的な電波の遮断で UD01SS の初期化がかかったものの、その状態を
URoad-5000 側が正しく認識していないのが原因だと思われます。

移動時以外は長時間稼働しても特に問題は出ていません。
一カ所にとどまった状態だと安定して利用できています。


●まとめ

状況の把握が出来れば対策可能なので、移動中でも十分使えると思います。
小さくて軽量な本体もバッテリー駆動出来る点も良いです。
ただ鞄に入れたまま手放しで待機しておくには、まだ不安かもしれません。
もし本体の方に何らかの問題があるならファームウエアの更新で改善できるでしょうか。



関連エントリ
WiMAX ルータ URoad-5000 (2) 二日目
WiMAX ルータ URoad-5000
Windows7 beta で UQ WiMAX



今日は非常に好調でした。
昨日の不安定さとは大違いで、電車の車内でもきちんとつながるし使えています。
WiMAX の電波状況は右端の LED。消灯→赤→橙→緑 と変化します。
この変化や強さが、普段 VAIO type P + UD01SS で使っていたときとの記憶と
完全に一致。
切れる場所、つながって使える場所も同じで、切れたあとの再接続も問題なく
普通にブラウズできました。
試した場所は新宿近辺から中央総武各駅停車。

つながらない区間はところどころありますが、全く使えないほどではありません。
先日の報告は訂正させていただきます。申し訳ありません。
移動時につながらなくなった症状の条件はまだ絞り込めていないので、もう少し
使ってからレポートしたいと思います。

uroad5000

電波状況や通信時の点滅を凝視しながらだったので、こんな感じ(↑)の手持ちで
使用していました。
一体型ケースがあってもいい良いんじゃないかと思うくらい軽くて薄い本体です。
ポケットに余裕で入ります。iPhone 3GS と両方合わせて 260g くらい。


充電の仕方はまだよくわかっていません。
AC アダプタをつなぐと電源が入ってしまい、いちいち電源を切らないと通信待機
状態のままになります。無線 LAN も見えます。
また充電中のバッテリーマークはずっと緑色で一晩充電しても変化はなし。
充電完了かどうか区別できないようです。
何時間充電すればよいかも記載されていないので、この辺もあとで問い合わせてみます。

バッテリーマークも電波感度マークも一般的なアイコン形状(↓)だけど
バーの本数は変わりません。色が変わるだけ。

uroad5000

ドラクエの Wi-Fi ショッピングも、PSP まいにちいっしょポータブルのデータ更新も
どこでも出来るようになりました。
時間がかかる更新処理は移動中ではなく電波の安定したところでやった方が良いです。
やはりつながるとそこそこ速くて快適です。
Eye-Fi を試すのを忘れました。バッテリーの持ちなどもまた後日。

いらないケース
・ノート PC で使う場合は直接 USB アダプタを差した方が使いやすいし高速

必要なケース
・ノート PC が 64bit OS の場合
・iPod touch, PSP, DS 等、PC 無しでモバイル通信したい場合

※次回に続く(2009/08/06)

関連エントリ
Windows7 beta で UQ WiMAX
WiMAX ルータ URoad-5000


シンセイコーポレーションのモバイルルータ URoad-5000 購入しました。

URoad-5000

WiMAX の回線をどこでも使えるようにするための無線LAN ルーターです。
同じシンセイコーポレーション製の USB WiMAX アダプタを使うことが出来ます。
ちょうど UQ WiMAX 用のUD01SS を持っていたので、ビックカメラで URoad-5000
のみ購入してきました。


●使えるアダプタ

URoad-5000 は USB タイプの WiMAX アダプタに対応したモバイルルーターなので
単体では何も出来ません。添付の「かんたん説明マニュアル」を見ると使える
アダプタは MW-U2510 だけだそうです。

web サイトの FAQ (よくある質問) を読むと UD01SS, UD03SS, BDSS01,
MW-U2510/DM が使えるとのこと。これら全部 MW-U2510 と同じものだそうです。
実際 UD01SS の USB コネクタの裏側には「モデル名 MW-U2510」と小さい字で
書かれています。


●使い道

今のところ iPhone 等の携帯端末から WiMAX 回線を使うには PC か WiMAX 対応
ルーターが必要になります。ルーターの場合アドホックに対応していない
DS や PSP でも使用できます。
箱にも PSP, NINTENDO DS, iPod touch 対応と書かれていました。

また WiMAX の USB アダプタには 64bit OS 向けドライバがまだありません。
ルータ経由なら Windows7 x64 からも WiMAX つなげられるようになるはずです。


●説明書がない

最初に少々戸惑ったのは本体の取扱説明書がないことです。
無線LAN 設定方法までの手順が書かれた「かんたん設定マニュアル」は入って
いましたが、他に説明書も CD-ROM 等もありません。
バッテリーの入れ方や充電方法(ACアダプタをつなぐと電源が入ってしまう)など、
難しくはないけど、しばらく使ってみないとわからないことがあります。
購入時に店員が念入りに付属品の確認をしていたのはこのためかもしれません。
説明書がない等の問い合わせがあるのではないでしょうか。

製品情報ページには FAQ の pdf が掲載されており、補足の説明はこちらを見る
ことになるようです。


●大きさ

本体はバッテリー内蔵ながら非常に小さく手に持った感じも軽量です。
カタログ値でバッテリー込み 110g。
実測でバッテリーパックが 49g、本体が 57g の合計 106g でした。
重量の半分近くがバッテリーです。
UD01SS は実測 19g だったのであわせて 125g くらい。

uroad5000
↑左端が UD01SS、真ん中が URoad-5000

大きさも iPhone くらいのサイズで予想よりも薄型でした。
WindowsMobile のスマートフォンをもう1つ持ち歩くくらいの感覚です。

ただ USB アダプタ UD01SS を接続すると持ち歩きに不安な形ででっぱります。
ノート PC に直接差した場合と可搬性はあまり変わらないかもしれません。

uroad5000
uroad5000


●利用は簡単

USB WiMAX アダプタ UD01SS は、もともと UQ から購入したもので PC につないで
使用していました。そのまま URoad-5000 に差し込むだけで使えるようです。

サイドの小さいボタンを長押しして電源を入れると、しばらくして接続ランプが
点灯します。

最初から WEP と WPA-PSK(TKIP) 対応の 2種類の SSID が登録されていました。
キーはどちらも同じで本体裏に貼ってあるシールに書かれています。
SSID, KEY, PIN と 3つ番号があるけど使うのは「KEY:~」の方です。

WEP しか使えない DS は URoad-****** の方につなぎます。それ以外は
URoadWPS-****** を使えば大丈夫なはずです。(もしだめなら URoad- の方へ)
つながったら「かんたん設定マニュアル」に従って、ブラウザからセキュリティ
等の設定変更ができるようになります。


●電源投入

まだほとんど使っていないので実際のバッテリー寿命などはまだわかりません。
スペック上は連続使用 3時間だそうです。
AC アダプタのコネクタはサイズは細くて特殊な形状でしたが、アダプタ本体も
割と小さい方でしょう。AC アダプタの電圧は 5V。
バッテリーの電源容量は 3.7V 2200mAh でした。


電源を入れてから WiMAX が利用可能になるまで少々時間がかかります。

(1) Wireless LAN のランプが点灯するまで 25秒
(2) USBアダプタ(UD01SS) の LED がつくまで 40秒
(3) WiMAX の電波状況ランプが点灯して使えるようになるまで 60~70秒

電源を入れてから 1分少々。特に UD01SS が電波をつかんで安定するまで意外に
時間がかかるようです。たまに初期化に失敗するのか UD01SS の再初期化が
何度か行われるようで、その場合は 2分近くかかることもありました。

つながらないときは何度も電源入れ直したりアダプタを抜き差しして
しまいましたが、しばらく待つのが正解のようです。
ちなみに電源入れたあとに UD01SS を差し込んでも大丈夫でした。


●速度

VAIO type P に UD01SS 直差しで 3Mbps 出る場所でテストしました。
URoad-5000 経由では 2Mbps。思ったよりも速度が落ちています。

周囲に無線 LAN 機器が多数あるので、無線 LAN 側が混信している可能性もあります。
チャンネルを手動で変更すると多少上がって 2.3Mbps 程に。
でも主に移動時に使うので自動選択に戻しました。

URoad-5000 は 11b/g/n に対応しています。
VAIO type P (Windows7 RC) のダイアログでは 300Mbps と表示されており、
11n で接続されていたようです。


●接続

とりあえず VAIO type P 、DS lite 、iPhone でつないでみました。
それなりに快適で、これなら iPod touch のままでも良かったんじゃないかと
一瞬思いました。

電源を入れてから鞄にいれて都内の電車内で使ってみました。結果はほぼ全滅です。
駅で停車中たまにつながりますが駅間では切れます。
圏外からの再接続が間に合わないらしく、停車中圏内になってもほとんど安定して
使える状態にはなりませんでした。


また VAIO type P に直差しした場合よりも感度が落ちるのか、以前ノート PC で
ぎりぎり使えていた場所でもなかなか安定しません。
とりあえず通勤時は使えないことが判明。
iPod touch + UQ で十分かも、と思ったのは間違いでした。


●まとめ

優先 LAN 端子はなく無線 LAN のみです。
WiMAX 以外使えない専用機なので WiMAX をしっかり使おう、という人向けです。
小型軽量でバッテリー駆動できるのはメリットですが、移動中は安定しません
外出時どこかに腰を据えて使うことになります。

PC に直接アダプタをさして使うよりも速度は少々落ちます。
通信速度も、圏外からの立ち上がりも若干時間がかかるようになった印象です。
ルーターのオーバーヘッドなのか電波の入りが悪くなるのが原因なのかはわかりません。

利点は PC なしで WiMAX の回線が使えることです。
iPod touch や iPhone で高速なネット接続出来ますし、外出時に DS や PSP を
ネットにつなぐこともできるでしょう。

自分の用途だと使える場所が限られてしまうので、まだ活躍できるかわかりませんが
これからいろいろ使ってみます。

※ 次回に続く (2009/08/05)


関連エントリ
Windows7 beta で UQ WiMAX


こちらで触れたように Multitouch 周りは Windows7 ベータの時と仕様が若干
変わっています。なかなか試せず時間がたってしまいました。
ベータの WM_TOUCH

Multitouch API には 2系統あります。WM_TOUCH と WM_GESTURE です。
WM_TOUCH は複数の点の座標をそのまま送ってくるメッセージ、WM_GESTURE は
ある程度の動作を判断したあとに送られてきます。
両者は共存できないので、あらかじめどちらを使うか切り替えておくことになります。

ベータの時は WM_TOUCHDOWN, WM_TOUCHUP, WM_TOUCHMOVE と 3種類
メッセージがありました。RC 以降は WM_TOUCH 一つにまとめられています。
WM_TOUCH 系は複数のタッチ点の座標を一度に送信してくるため、それぞれ個別に
DOWN/UP する可能性があります。受け取った構造体の TOUCHINPUT には個別に
UP/DOWN/MOVE フラグが立っているので情報としては十分です。
複数の点をまとめて WM_TOUCHDOWN/TOUCHUP/TOUCHMOVE とひとくくりに
なっていたのは GESTURE のような操作を想定していたからかもしれません。

少々不思議なのは個別の UP/DOWN/MOVE は本当に区別できるのかということ。
座標はその瞬間サンプリングされたタッチ点なので厳密には個々のポイントを区別
することができず、連続性を持った値になるとは限りません。
例えば移動中のマルチタッチ点が増えたとして、どの点が新規にタッチされたものか
区別しなければならないということです。
サンプリングレートが十分速くて移動が限られている UI 操作ならそれほど問題
ないのかもしれません。
WM_TOUCH のイベントでは Windows 側で同一点の処理が行われており、
TOUCHINPUT の dwID に識別番号が入っています。
dwID が同じものを見ていくと DOWN ~ MOVE ~ UP の流れを見ることが
できるわけです。

lparam がハンドルなので、GetTouchInputInfo() で詳細を受け取ります。
これもベータ時の HANDLE から HTOUCHINPUT に変更されているようです。

void WM_Touch( UINT mes, WPARAM wparam, LPARAM lparam )
{
    int inputs= LOWORD( wparam );
    TOUCHINPUT  tbuf[ 32 ]; // inputs
    HTOUCHINPUT	hinput= reinterpret_cast( lparam );
    if( GetTouchInputInfo( hinput, inputs, tbuf, sizeof(TOUCHINPUT) ) ){
        TOUCHINPUT*  tp= tbuf;
        for( int i= 0 ; i< inputs && i < 32 ; i++, tp++ ){
            ~
	    tp->dwID // 識別
	    tp->x, tp->y // 座標
	    tp->dwFlags // UP/DOWN/MOVE
	}
    }
    CloseTouchInputHandle( hinput );
}

実際の 2点タッチだとこんな感じです。

0:(34054,41033) ID=3 flag=3a mask=4 ex=0 cx=4881 cy=5874 DOWN INRANGE PRIMARY NOCOALESCE 

0:(34054,41033) ID=3 flag=39 mask=4 ex=0 cx=4881 cy=5874 MVOE INRANGE PRIMARY NOCOALESCE 
1:(46230,31629) ID=4 flag=2a mask=4 ex=0 cx=5162 cy=5057 DOWN INRANGE NOCOALESCE 

0:(34007,40957) ID=3 flag=19 mask=4 ex=0 cx=5179 cy=5057 MVOE INRANGE PRIMARY 
1:(46224,31614) ID=4 flag=09 mask=4 ex=0 cx=4974 cy=5874 MVOE INRANGE 

0:(33966,40880) ID=3 flag=39 mask=4 ex=0 cx=5121 cy=5061 MVOE INRANGE PRIMARY NOCOALESCE 
1:(46212,31600) ID=4 flag=29 mask=4 ex=0 cx=4951 cy=5877 MVOE INRANGE NOCOALESCE 

0:(33796,40627) ID=3 flag=39 mask=4 ex=0 cx=5121 cy=5061 MVOE INRANGE PRIMARY NOCOALESCE 
1:(46330,31582) ID=4 flag=29 mask=4 ex=0 cx=4951 cy=5877 MVOE INRANGE NOCOALESCE 

0:(33632,40378) ID=3 flag=39 mask=4 ex=0 cx=4916 cy=5068 MVOE INRANGE PRIMARY NOCOALESCE 
1:(46447,31563) ID=4 flag=29 mask=4 ex=0 cx=4904 cy=5870 MVOE INRANGE NOCOALESCE 

0:(33082,39298) ID=3 flag=39 mask=4 ex=0 cx=4916 cy=5068 MVOE INRANGE PRIMARY NOCOALESCE 
1:(46757,31680) ID=4 flag=29 mask=4 ex=0 cx=4904 cy=5870 MVOE INRANGE NOCOALESCE 

0:(32531,38217) ID=3 flag=19 mask=4 ex=0 cx=3779 cy=5108 MVOE INRANGE PRIMARY 
1:(47074,31801) ID=4 flag=09 mask=4 ex=0 cx=4464 cy=5870 MVOE INRANGE 

0:(32531,38217) ID=3 flag=34 mask=4 ex=0 cx=3779 cy=5108 UP PRIMARY NOCOALESCE 
1:(47074,31801) ID=4 flag=29 mask=4 ex=0 cx=4464 cy=5870 MVOE INRANGE NOCOALESCE 

0:(47074,31801) ID=4 flag=24 mask=4 ex=0 cx=4464 cy=5870 UP NOCOALESCE 

座標値をピクセルに変換するには TOUCH_COORD_TO_PIXEL()。

HP TouchSmart IQ800 を使っていますが、たまに multi touch が無効になっている
ことがあります。何らかの更新のタイミングで、別のドライバに上書きされて
しまっているようです。ドライバを入れ直すと再び使えるようになります。

nextwindow Windows 7 Touch Screen Driver


関連エントリ
Windows SDK for Windows7 RC と Multitouch / Direct3D 11
Windows7 Multitouch API その(2) WM_GESTURE 系
Windows7 Multitouch API


Direct3D と OpenGL は座標系が違います。
そのためデータの Exporter を作るときは、どのプラットフォームにも対応できるよう
いつも両対応で作っています。

Direct3D から見ると両対応は簡単です。
というのも、Maya や XSI など多くのツールが OpenGL の右手座標系なので、
D3D の左手系に合わせるにはもともと座標系の変換処理が必要だからです。
OpenGL に対応するには反転しなければ良いだけ。

ただ OpenGLES 2.0 の場合ジオメトリもライティングもシェーダーだけで行いますし
演算式も任意です。汎用化された GPU では実装に任されているので、座標系の
違いをあまり意識しなくても良いのかもしれません。

普段使っているライブラリやシェーダーを流用することを考えて、Direct3D の
左手座標系でそのまま使ってみました。いつもの感覚でシェーダーやマトリクスの
セットを行っていたら自然にそうなりました。

OpenGL の場合も Depth Buffer の初期値はデフォルトで 1.0 を設定するようです。
比較関数も初期値は GL_LESS なので D3D と違いはありません。
Matrix の設定もモデルデータもシェーダーも D3D と同じパラメータで良いようです。

唯一変更したのは下記の設定のみ。
デフォルトは GL_CCW なので GL から見るとカリングが逆になります。

glFrontFace( GL_CW );

あとはデータもシェーダーも matrix も D3D 互換で使えます。

シェーダーはまだ GLSL のテストをしている状態です。
アニメーションはまだ未確認。

// vsh
uniform mat4	pmatrix;

attribute vec3	vposition;
attribute vec3	vnormal;
attribute vec2	vtexcoord;

varying vec4	ocolor;
varying vec2	otexcoord;

void main()
{
    gl_Position= pmatrix * vec4( vposition.xyz, 1 );

    vec3  lightdir= vec3( 0.0, 0.0, -1.0 );
    ocolor= vec4( 1.0, 1.0, 1.0, 1.0 ) * clamp( dot( vnormal, lightdir ), 0.0, 1.0 ) + 0.2;
    otexcoord= vtexcoord;
}


// fsh
precision mediump float;
varying	vec4   ocolor;
varying	vec2   otexcoord;

uniform sampler2D   texture0;

void main()
{
    vec4   tcol= texture2D( texture0, otexcoord );
    gl_FragColor= ocolor * tcol;
}


// cpp
struct v_type3 {
    float x, y, z;
};
struct v_type2 {
    float x, y;
};

static const v_type3	vdata_p[]= {
    {	-1.0f,	 1.0f,	0.0f,	},
    {	 1.0f,	 1.0f,	0.0f,	},
    {	-1.0f,	-1.0f,	0.0f,	},
    {	 1.0f,	-1.0f,	0.0f,	},
};

static const v_type3	vdata_n[]= {
    {	0.0f,	0.0f,	-1.0f,	},
    {	0.0f,	0.0f,	-1.0f,	},
    {	0.0f,	0.0f,	-1.0f,	},
    {	0.0f,	0.0f,	-1.0f,	},
};

static const v_type2	vdata_t[]= {
    {	0.0f,	0.0f,	},
    {	1.0f,	0.0f,	},
    {	0.0f,	1.0f,	},
    {	1.0f,	1.0f,	},
};

glVertexAttribPointer( attr_vposition, 3, GL_FLOAT, GL_FALSE, sizeof(v_type3), vdata_p );
glVertexAttribPointer( attr_vnormal, 3, GL_FLOAT, GL_FALSE, sizeof(v_type3), vdata_n );
glVertexAttribPointer( attr_vtexcoord, 2, GL_FLOAT, GL_FALSE, sizeof(v_type2), vdata_t );

glEnableVertexAttribArray( attr_vposition );
glEnableVertexAttribArray( attr_vnormal );
glEnableVertexAttribArray( attr_vtexcoord );

static unsigned short	idata[]= {
    0, 1, 2,
    2, 1, 3,
};

MatrixF	projection;
MatrixF	view;
MatrixF	world;

projection.SetProjection( ScreenWidth, ScreenHeight, 1.0f, 5000.0f, DEGtoRAD( 30.0f ) );
view.SetIdentity();
view.Translation( 0.0f, 0.0f, 20.0f );
world.SetIdentity();
world.RotationY( rotang );

MatrixF	mat;
mat.Mul( projection, view );
mat.Mul( world );

glUniformMatrix4fv( attr_pmatrix, 1, GL_FALSE, &mat._11 );
glDrawElements( GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, idata );


関連エントリ
OpenGLES2.0 シェーダー管理


OpenGL 系はほとんど使っていないので、余りよくわかっていないことが多いです。
とりあえずシェーダーのバリエーション管理をどうするか。

Direct3D11 では D3DCompile() 命令にプリプロセッサ用のデータを渡すことが
できます。シェーダーコンパイラは Direct3D11 より中立化しました。
D3DCompiler.h / d3dcompiler.lib と独立したモジュールになっており、
FeatureLevel や D3D のナンバーと関係なく共通で使用します。


include に対応するためには ID3DInclude を定義します。
define シンボルを渡す場合は、D3D_SHADER_MACRO 構造体の配列を作っておきます。
(Direct3D 10 Shader4.0 シェーダーのコンパイル)

Shader の機能わけのために、これまでプリプロセッサ命令を活用してきました。
同じシェーダープログラムでもシンボル定義に応じて複数の機能に対応させます。

D3D_SHADER_MACRO  deflist[]= {
{  "SF_ANIMATION",  "1", },
{  NULL, NULL },
};

例えば上記の配列を D3DCompiler() に渡すと、hlsl 上では SF_ANIMATION の定義が
"1" になります。

シェーダー (hlsl) 上では

#if SF_ANIMATION
  ~
#endif

といった感じでコンパイル時の機能指定に応じてコードを分けることができます。

OpenGLES の GLSL コンパイラの場合、特にプリプロセッサ用の定義データを
そのまま渡すわけではなさそうです。
glShaderSource() では、一度のコンパイルでも複数のソースを同時に渡すことができます。
このうち 1つをプログラム内で生成したテキストに割り当てます。
例えばシェーダーコンパイル時に

#define SF_ANIMATION  1

といったテキストを生成してソースの 1つとして与えることで、D3D_SHADER_MACRO
と同じような使い方ができました。


OpenGLES2.0 はシェーダーのみと割り切ったおかげで無駄のないすっきりした
構成になっているようです。
シェーダーの世代的には ShaderModel3.0 相当と思われます。

Direct3D9 + ShaderModel3.0 の場合も、多くの機能がシェーダーで汎用化された
ために従来固定機能だったパイプラインの機能がいくつか使えなくなっています。
それでも API 的には重複する固定機能の設定が残っていたため、どちらでも動くのか
どうか、使えるのかどうかなどわかりにくい点がありました。

この問題は Direct3D10 でいったんリセットすることで解消しています。
Direct3D 10 以降はシェーダーのみと割り切って、重複する固定機能の多くを
排除しました。

そういう意味では OpenGLES2.0 は、Direct3D9 + ShaderModel3.0 というよりも、
Direct3D10 + ShaderModel4_0_Level_9_3 の方により近いといえるかもしれません。


関連エントリ
Direct3D11/DirectX11 (4) FeatureLevel と旧 GPU の互換性、テクスチャ形式など
Direct3D 10 Shader4.0 シェーダーのコンパイル


WindowsMobile 用のフリーソフトウエアは CAB 形式で配布しています。
CAB ファイルは explorer から実行するだけでインストールできるので扱いも簡単です。

今なら別の方法があるのかもしれませんが、昔から VisualStudio に付属している
cabwiz を使用しています。というか WindowsCE / PocketPC の頃に作った設定
ファイルをほとんどテンプレートとして、ずっとそのまま使ってきました。

d3dmclock の場合 (d3dmclock.inf)

[Version]
Signature	= "$Windows NT$"
Provider	= "HYP"
CESignature	= "$Windows CE$"

[CEStrings]
AppName		= d3dmclock
InstallDir	= %CE1%\%AppName%

[CEDevice.ARMWM6]
VersionMin	= 5.0
VersionMax	= 32767.0
ProcessorType	= 2577

[DestinationDirs]
Shortcuts.All = 0,%CE11%
Files.Common = 0,%InstallDir%
DefaultDestDir = 0,%InstallDir%

[Shortcuts.All]
d3dmclock,0,d3dmclock.exe,%CE11%

[SourceDisksNames]
1 = , "Common files",,C:\usr\ce\gclock

[SourceDisksNames.ARMWM6]
2 = , "ARMfiles",,C:\usr\ce\gclock\ARMV4I\Release

[SourceDisksFiles]
d3dmclock.txt = 1
font.dds = 1
bgimage0.bmp = 1
bgimage1.bmp = 1
bgimage2.bmp = 1
textimage0.dds = 1
textimage1.dds = 1
textimage2.dds = 1
textimage3.dds = 1
textimage4.dds = 1
textimage5.dds = 1
textimage6.dds = 1
textimage7.dds = 1

[SourceDisksFiles.ARMWM6]
d3dmclock.exe = 2

[DefaultInstall]
CEShortcuts	= Shortcuts.All

[DefaultInstall.ARMWM6]
CopyFiles	= Files.Common, Files.ARMWM6

[Files.Common]
d3dmclock.txt,,,0
font.dds,,,0
bgimage0.bmp,,,0
bgimage1.bmp,,,0
bgimage2.bmp,,,0
textimage0.dds,,,0
textimage1.dds,,,0
textimage2.dds,,,0
textimage3.dds,,,0
textimage4.dds,,,0
textimage5.dds,,,0
textimage6.dds,,,0
textimage7.dds,,,0

[Files.ARMWM6]
d3dmclock.exe,,,0x80000000

CEString は他で参照する定数の定義です。%~% で参照しています。
Install 先フォルダをここに記述しています。%CE1% は \Program Files

SourceDisksNames はインストールするファイルが置かれている実在のパスを指定します。

SourceDisksFiles はアーカイブするファイルがどこにあるか指定します。
後ろの番号は SourceDisksNames の番号に対応しています。

Files が実際にインストールコピーされるファイルリスト。

".ARMWM6" がついた定義名は、もともと WindowsCE が複数の CPU に対応しており
対象 CPU 毎に CAB を用意していた頃の名残です。CPU 毎に別ファイルを指定する
場合と、共通に含めるファイルを分けて記述できるようになっています。

(1) DestinationDirs でインストール先フォルダを指定。
  %InstallDir% の定義は CEStrings にあります。
(2) SourceDisksNames.* にアーカイブするファイルの場所を記述
(3) SourceDisksFiles.* に必要なファイルを記述
  (数値は SourceDisksNames の番号に対応している)
(4) Files.* にアーカイブするファイルを記述

詳しくはこちら
MSDN スマート デバイスの .inf ファイルの概要


touchkeysip の場合 (touchkeysip.inf)

[Version]
Signature	= "$Windows NT$"
Provider	= "HYP"
CESignature	= "$Windows CE$"

[CEStrings]
AppName		= touchkeysip
InstallDir	= %CE1%\%AppName%

[CEDevice.ARM]
VersionMin	= 5.0
VersionMax	= 1000.0
ProcessorType	= 2577

[CEDevice.ARMGS]
VersionMin	= 5.0
VersionMax	= 1000.0
ProcessorType	= 2577

[DestinationDirs]
Files.Common = 0,%InstallDir%
DefaultDestDir = 0,%CE2%

[SourceDisksNames]
1 = , "Common files",,C:\usr\ce\minisip2

[SourceDisksNames.ARM]
2 = , "ARMfiles",,C:\usr\ce\minisip2

[SourceDisksNames.ARMGS]
3 = , "ARMfiles",,C:\usr\ce\minisip2\gs

[SourceDisksFiles]
touchkeysip.txt = 1
defaultscript.txt = 1
defaultkeyboard.bmp = 1

[SourceDisksFiles.ARM]
touchkeysip.dll = 2

[SourceDisksFiles.ARMGS]
touchkeysip.dll = 3

[DefaultInstall]

[DefaultInstall.ARM]
CopyFiles	= Files.Common, Files.ARM
CESelfRegister	= touchkeysip.dll

[DefaultInstall.ARMGS]
CopyFiles	= Files.Common, Files.ARMGS
CESelfRegister	= touchkeysip.dll

[Files.Common]
touchkeysip.txt,,,0
defaultscript.txt,,,0
defaultkeyboard.bmp,,,0

[Files.ARM]
touchkeysip.dll,,,0x80000000

[Files.ARMGS]
touchkeysip.dll,,,0x80000000

コマンドの実行手順は Makefile に記述しています。(install.mak)

ZIPFILEARM	= touchkeysip***.zip
CABFILEARM	= touchkeysip.ARM.CAB
EXEARM		= touchkeysip.dll

all: \
	$(EXEARM) \
	$(CABFILEARM) \
	$(ZIPFILEARM) \


zip: $(ZIPFILEARM)


_MANUALFILE	= touchkeysip.txt	\
		CommandManual.txt	\


_CABSRCFILE	= touchkeysip.txt	\
		defaultscript.txt	\
		defaultkeyboard.bmp	\


_SRCFILEARM	= $(EXEARM) \
		$(_CABSRCFILE) \


ZIPSRCFILEARM	= $(CABFILEARM)   $(_MANUALFILE)


$(ZIPFILEARM):	$(ZIPSRCFILEARM)
	7z a -tzip $(ZIPFILEARM) $(ZIPSRCFILEARM)


CABWIZE	= C:\Program Files (x86)\Microsoft Visual Studio 9.0\SmartDevices\SDK\SDKTools\Cabwiz.exe

$(CABFILEARM): touchkeysip.inf $(_SRCFILEARM)
	"$(CABWIZE)" touchkeysip.inf  /cpu ARM

VisualStudio で作成&動作確認が終わったら、nmake の実行だけで CAB の生成と
zip の作成もいっぺんに行われます。上のファイルは install.mak という名前で
各プロジェクトのフォルダに置いてあります。実行はコマンドラインから

nmake -f install.mak

プログラムの数も非常に多いし、開発中はそれぞれが頻繁に更新されるので
公開までの手順は自動化されています。
ドキュメントを修正した場合も nmake だけでアーカイブが作られます。

普段使っているスクリプトファイルを入れて

touchkeysip.dll + スクリプト

といった自分専用の CAB を作っておけば、デバイスの再インストール時に
ちょっとだけ手間が減るかもしれません。


キーボードに結構こだわっている人はいます。
プログラマだと少々高くても良いものを探したり、好みのものを選んで使ってる
方も多いことと思います。
全く同じように、緻密ながら素早い動作が必要になるデザイナーの作業でも
マウスの性能差は作業効率に大きく影響があるのだそうでです。

知り合いのデザイナーにきいたところ、仕事がらマウス選びにはいつも
苦労しているのだそうです。

マウスの場合、性能の良い高級タイプはゲーマー向けとして売られているものが
ほとんどです。
センサーの性能や確実性、反応速度などはやはりこれらゲーマー向けマウスが
一番だとのこと。

ただどうしてもサイズが大きいものが多くて、手になじまないのが悩みだと
訴えていました。
ゲームプレイ時と作業時では、実際にマウスを動かす稼働範囲やマウス自体の
持ち方が違っているのかもしれません。
例えば手首を固定して指先でマウスを持つような持ち方の場合、
マウスが大きすぎると手のひらが密着してあまり動かせられなくなります。

持ち方だけでなく、純粋に手の大きさが違う場合もあります。
マウスのサイズにはもっとバリエーションが多くても良いのではないでしょうか。

日本のブランドである DHARMAPOINT のゲーマーマウスは、比較的小振りで
よく使っているそうです。

なんだかんだでいろいろ試しているうちに、DHARAMAPOINT ブランドの製品は
マウス全世代、マウスパッド数タイプ、交換ソール、キーボードなど
ヘッドセット以外は全部買いそろえてしまったらしい。

DHARMAPOINT

ゲーマーマウス並の高性能で、さらに作業用で持ち方にあわせて
大きさを各種選べるような、そんなマウスは無いでしょうか。
毎日ほとんどの時間をマウスとともに費やしているわけです。
ストレスを抱えながら作業するよりは少々高くても良いマウスを選びます。


PC 上で PSP のデータを管理できる Media Go というソフトウエアが少し前に
リリースされていたようです。PlayStation Store に接続することも可能で、
PSP のネット環境がなくてもソフトウエアのダウンロードや購入ができます。

SONY Media Go

以前から PC 上で PSP ソフトウエアの購入はできましたが、当初はもっとシンプルな
ダウンロード専用ツールだったように覚えています。
Media Go ではさらにゲームの入れ替えやセーブデータのバックアップも可能。
PSP に転送するメディアファイルの管理もできて、統合ツールに進化しつつ
あるようです。

Media Go は PSP をつながなくてもインストール&起動することができました。
左側の「PlayStation Store」をクリックすると PSP 版のストアにアクセスできます。
ゲームタイトルやビデオの一覧はログインしなくても閲覧可能。
実際にダウンロードする場合にはアカウント情報が必要になります。


PC と PSP の接続は USB ケーブルで行います。
PSP を「USB 接続」モードにすれば空き容量やファームのバージョンなど本体の
情報が表示され、同時にメモリースティック内のデータもブラウズできます。

アイコン付きで、現在どんなゲームが入っているのか一目瞭然です。
セーブデータもわかりやすく、タイトルごとにまとまっているので扱いも容易です。

これまでもメモリースティックを直接参照すれば同じような管理はできました。
でもフォルダ名がタイトル固有 ID なので、どのファイルがどのゲームなのか
判断するのは困難だったのです。

Media Go 上では、ゲームやセーブデータのアイコン上でマウスの右ボタンを押して
「転送→ライブラリ」を選ぶと PC のライブラリに保存できます。
PSP に書き戻せるので、容量の限られたメモリースティックでも複数のゲームを
入れ替えながら使えるようです。

ライブラリの保存先はマイドキュメントの "Media Go" でした。
中をのぞいてみると、ライセンスファイルは特に指定しなくても自動で
バックアップされていることがわかります。


操作はシンプルでそれほど多機能ではありませんが、位置づけやできることは
iTunes に近いようです。時間のかかる転送もバックグラウンドで行われるので
やはり意識しているように見えます。
Sony 製のメディア管理ソフトというと、どうしても過去の重いソフトウエアたちを
思い浮かべてしまいます。Media Go は比較的動作も軽い印象でした。


ストアへの初回接続時に、言語を選択する画面が現れます。
ここで間違ったリンクをクリックしてしまうと海外のストアにつながりますが、
言語を選択し直す方法がわからなくて焦りました。
ユーザー設定から変更できるのは、メニューなど UI の言語だけでした。

レジストリの下記エントリを消して起動し直すと、再びストアの言語選択に戻れる
ことがわかりました。または Region の値を "JP" にすれば良いようです。
(レジストリを書き換える場合は自己責任でお願いします)

HKEY_CURRENT_USER\Software\Sony Corporation\PS Store\Region


すでに PS3 上で PSP のゲームのダウンロード&管理を行っているため特に移行
する必要はないのですが、データをまとめて転送できる Media Go は便利そうです。


関連エントリ
新型 PSP (PSP-2000) とゲームアーカイブス


少し前から Compute Shader が使えなくなっていました。

Direct3D11/DirectX11 ComputeShader 4.0 を使う

上のエントリを書いたあと、ドライバを 186.18 に更新したら
CheckFeatureSupport() の D3D11_FEATURE_D3D10_X_HARDWARE_OPTIONS で
ComputeShaders_Plus_RawAndStructuredBuffers_Via_Shader_4_x が 0 を返す
ようになっており、実際に CreateComputeShader() が失敗します。
今回 190.38 に更新してもやっぱり同じで、186.08 に戻したら動きました。

なお CheckFeatureSupport() の D3D11_FEATURE_THREADING で
DriverConcurrentCreates は 1 を返すので、ドライバがきちんと D3D11 に
対応しているのは本当です。
DriverCommandLists が 0 なので Deferred Context はドライバの
エミュレーションになりますが、リソース生成はスレッドに対応している
ことになります。

DriverConcurrentCreates
 リソース生成のスレッド対応かどうか。

DriverCommandLists
 ドライバが CommandList の記録と実行に直接対応しているかどうか。
 0 でもコマンドバッファをソフトウエアで記録するため実行できます。

以前描画周りを Direct3D11 対応で作り直していて積極的にスレッド化したの
ですが、ロード後のリソース生成でまれに失敗することがありました。
よく考えたら当時まだドライバが DriverConcurrentCreates に
対応していなかったので当たり前でした。


関連エントリ
Direct3D11/DirectX11 ComputeShader 4.0 を使う
Direct3D11/DirectX11 (6) D3D11 の ComputeShader を使ってみる


モニターレポートで少々当てが外れたのは、2回目くらいまでには SDK など
何らかの開発情報が出てるだろうと予想していたからです。

最初の使用感などから徐々に詳細に踏み込んだり、実際に応用を試したりと
そんな流れを想定していました。まだ動きがないので、もしかしたら Windows7
のリリース待ちかもしれません。
開発関連の情報があれば、この興味深いデバイスで何が出来て何が出来ないのか
明らかになってくると思います。


●アプリの追加

売りである光センサー液晶パッドには最初からいくつかのアプリケーションが
組み込まれています。手書き文字対応辞書やゲーム、電卓や手描きツールまで
一通りそろっています。

ここにはさらに専用のアプリを追加することができます。

まだ SDK などの配布が行われておらず、対応ソフトを自由に作れる状態では
ないのですがメーカーのサイトで新しいソフトが公開されていました。

PC・モバイル ユーザーズ倶楽部

上記サイトの「ダウンロードサービス」に「タッチソフトダウンロード」という
アイコンがあります。

 PC-NJ70/NJ80用 タッチソフト
   ゲームソフト「イルミスキャン」

早速試してみました。


●ゲーム内容

インストール手順は一般的な Windows ソフトと同じです。
ダウンロードした IllumiScanInstaller.exe を実行します。

完了するとタッチパッド部のホームメニュー「エンターテインメント」の中に
アイコンが追加されており、イルミスキャンを遊べるようになっています。

Windows7 RC に入れましたが問題なく組み込み&実行できました。

ゲーム内容は、表示された丸いマーカーを指で隠していくというもの。
一度に複数のマーカーが現れるので、そのすべてを指で覆います。
マルチタッチでないとできない操作だし、いくつでも検出可能な光センサーだから
こそ実現できる内容といえます。

ゲーム内容の方は反応がいまいちなのか自分の操作が下手なのか、音で反応は
するけどうまく進めなくて時間切れです。遊び方のヘルプを何度も見返したものの
どうもうまく遊べませんでした。


でもこのアプリ、真に見るべきところはそこではないようです。
ゲーム中は余裕がなくて気がつかないのですが、上のメイン画面の方に
タッチパッドに触れている指の影がうっすらと映っています。

mebius pc-nj70a
↑三本指

mebius pc-nj70a
↑タッチペンを置いてみたところ

おそらくタッチパッド部分で取り込んでいる映像だと思われます。
タッチペンなど、きちんと形が取れているのがおもしろいところです。

今までのアプリでは、一般的なタッチパッドとしての使い方からあまり大きく
外れたものではありませんでした。
イルミスキャンでは本当に画像として実データを見ることが出来るので、
初めて光センサーらしさを実感できた気がします。


●インストールまで

イルミスキャンでゲームより手間取ったのはダウンロードまでの道のりです。
無料ダウンロードだけどユーザーズ倶楽部への会員登録が必要です。

ユーザー登録には名前住所など個人プロフィールが必要になるので書き込みます。
その途中で所有している SHARP 製品の登録番号を入れます。
製品をユーザー登録すると得られる番号らしく、仕方なく先にこちらも登録。

するとまた同じように名前住所などを書き込む欄があって製造番号も入れます。
これで登録番号が得られたので、あらためて ユーザーズ倶楽部の ID を取得します。

ちょっと試したいだけなのに 2度手間のような登録作業や、何度も書かされる
個人情報など、義務感がなかったら途中であきらめていたかもしれません。
本体を所有していれば勝手につながってダウンロードできるなど、何らかの
工夫が欲しいと感じました。
もっと簡単になればダウンロード数も増えるのではないでしょうか。


●タッチアプリ

比較的気に入っているのは手書き文字認識です。
ペン操作は指より操作しやすく候補も即座に出てきて気持ちよく使えます。

逆にいつも残念に思うのはメビウスの全画面乗っ取ってしまうアプリケーションです。
ゲームもタッチパッド部だけで遊べるものが多いのに、関係ないはずの上の
ウィンドウズ画面も奪われます。

マウスモードとタッチメニューはいつでもワンボタンで切り替えできます。
電卓等は他の作業中でもいつでも呼び出せるのが良いところ。
ゲームも同じように呼び出せたら便利だと思うのです。

たとえばブラウザを開いた状態で、ファイルのダウンロード中だけちょっとだけ
パズルを進めておきたい、と思ってもできない。
ゲーム中マウスモードには切り替えられても、上のフルスクリーン画面が残るので
結局アプリを終了しなければならないからです。

PC に統合された光センサー液晶パッドは、1つのことに専念するような使い方より
気軽に呼び出せる方があっていると思います。

イルミスキャンでは、タッチパッドに乗せた物体の形を判別できることがわかりました。
タッチパッドとして無理に使うより、光センサーにしかできないことを考えた方が
良さそうですね。


関連エントリ
メビウス PC-NJ70A (7) 一ヶ月後
メビウス PC-NJ70A (6) ユーザーインターフェース
メビウス PC-NJ70A (5) サブモニタ
メビウス PC-NJ70A (4) 使用感
メビウス PC-NJ70A (3) Windows7 RC を入れる
メビウス PC-NJ70A (2) 初期セットアップとメモリ増設
メビウス PC-NJ70A 液晶センサータッチパッド
メビウス PC-NJ70A 画面解像度とタッチ


2009/07/18
UP300 アプリ

いつの間にか、UP300x/UP300 がアプリ対応になってるようです。

UPLAB UPアプリとは
UPLAB UPアプリ開発ガイド

ネイティブではなく Flash でした。
もともと WindowsCE が使われており、ブラウザが動いていたのでその上で
動作させるようです。



関連エントリ
ニコン UP300x/UP300 を体験してきた。ヘッドマウントディスプレイ


モニター開始から一ヶ月。とはいえ SDK 等の動きもないので、特に何も変わって
ないというのが正直なところです。
HDD なのでアプリケーションのインストールで困ることもなく、容量も書き込み
速度も気にせずに使っています。


主な用途は 32bit OS での動作テストや、テキストメモ取り PC として。
基本的にはこれまで使ってきたノート PC と全く一緒です。
売りの光センサー液晶パッドはあまり活用できていないので、SDK が公開されるか
追加アプリが登場するまで眠らせておくかもしれません。
サイズも重さも決して悪いスペックではないのですが、今だと他にもっと小型軽量な
良い選択肢があるだけに悩ましいところです。


● Windows7 RC

Windows7 RC で使用しています。詳しいインストール手順はこちら。
メビウス PC-NJ70A (3) Windows7 RC を入れる

Windows7 RC で液晶パッドへのアプリの追加
メビウス PC-NJ70A (5) サブモニタ

Windows7 なのは主に動作テストのためと、標準の Home Basic ではリモートデスク
トップなどいくつか機能制限が生じるためです。


個人的に行っている設定

ネットブック系 PC は横長のワイド画面が多く、縦方向は狭くなっています。
WindowsVista までは、設定次第でタスクバーの上にアプリケーションウィンドウを
重ねることが出来ました。
Windows7 はできないので、タスクバーを画面の左端に配置しています。
アプリケーションで上下幅をめいっぱいまで使えるように。
タスクバーをわずかに横にのばすと、時刻だけでなく日付も表示できます。

pc-nj70a
pc-nj70a


●マウス

Bluetooth の外付けマウスを使用しています。

SONY VGP-BMS33

光センサー液晶パッドはマウスモードで表示を消す設定にしていますが、
何らかのタイミングで液晶パッドのバックライトが点灯してしまいます。

 SHARP 液晶パッド設定→マウスモード時に、液晶パッドの表示を消す

サスペンドから復帰したタイミングや、省電力設定でメインディスプレイの電源が
切れた場合、その復帰と同時に液晶パッドも点灯してしまうようです。
真ん中ボタンでいったんパッドのモードを切り替えるとバックライトが消えます。

持ち歩いてて一度、光センサー液晶パッドの反応が悪くほとんど操作出来なくなる
ことがありました。室内だったので、汗などによるパッド部の汚れが原因では
ないかと思います。外付けのマウスは必須となりました。


●アドオンジャケット

アドオンジャケットは天板の保護にもなるし、好きなメモとか挟んでおけるので
おもしろい仕組みです。欠点は 100g ほど重くなること。
持ち歩く用途を考えた場合どちらを優先するか悩みます。
結局軽量化を考えて取り外しました。


●通信

外では UQ WiMAX を使用しています。
こちらはモニターではなく購入品。
まだエリアが限られていますが、一度つながれば外でも高速な通信が可能です。
7月から正式サービスが始まることもあり、以前使えなかった場所でもつながるように
なってきました。


●インストールソフトウエアメモ

インストールしている主なソフト。
Google Chrome
ATOK2008
DirectX SDK March 2009
UD01SS Utility
Vim 7.2


関連エントリ
メビウス PC-NJ70A (6) ユーザーインターフェース
メビウス PC-NJ70A (5) サブモニタ
メビウス PC-NJ70A (4) 使用感
メビウス PC-NJ70A (3) Windows7 RC を入れる
メビウス PC-NJ70A (2) 初期セットアップとメモリ増設
メビウス PC-NJ70A 液晶センサータッチパッド
メビウス PC-NJ70A 画面解像度とタッチ
Windows7 beta で UQ WiMAX


2009/06/27
iPhone 3GS 入手

昨日の朝に入手。比較的手に入れやすそうな店を選んだけれどさすがに混雑していて
しばらく待たされました。iPhone 3GS

手に入れたあと周りからきかれるのは「速い?」「速くなった?」ばかり。
正直あまり速いとか実感していないのですが、事実上目立った違いがそれしかない
ということでしょうか。
おそらく前使っていたのが比較的速い iPod touch 2G で、通信も無線LAN しか
使っていなかったせいでしょう。3G の通信で遅いと感じるなんて贅沢になりました。

中身は大幅に入れ替わっているようで、iPhone 3G のおよそ 2倍だそうです。

iPhone 3G    : ARM11         412MHz   8stage  128MB
iPod touch 2 : ARM11         533MHz   8stage  128MB
iPhone 3GS   : ARM Cortex-A8 600MHz  13stage  256MB

core が違うので単純な比較は出来ませんが、クロック周波数で 約1.46倍。
さらに Cortex-A8 はスーパースカラで 2命令同時発行らしいので、2パイプ動作して
実質 1.4倍程度と考えると (1.4*1.46= 2倍) ちょうど良い感じです。
その代わりインオーダー発行なので命令並びに依存するし、パイプラインの
ステージ数もだいぶ深くなっています。

wikipedia ARM architecture

メインプロセッサもそうですが、PowerVR SGX は ShaderModel 3.0 が動作する
はずなので、本当に違いが出るのは今後対応アプリが出揃ってからになるでしょう。


表面上変わってない、違いが少ないというのはむしろすごいことです。
過去にずっと長いこと日本製 PDA を使っていましたが、アプリ開発上の難関は
ユーザーインターフェースの互換性でした。
新製品が出るたびにボタン配列も全部変わってしまうし、そのたびにいちいち
プログラムを修正して、新しい操作方法を考えて、対応アプリを出さないと
いけなかったからです。
対する Palm は頑なにボタン配列を守り続けていました。

当時メーカーの方に話を聞く機会がありましたが、曰く商品コンセプトありきだとのこと。
毎回どこで売るのかコンセプトを考えて、ハードもそれに合わせてデザインする。
ソフトウエアもあわせて必要になれば調達すれば良い、とのお話でした。
商売としては正しいし全く異論はないのですが、ソフトウエア開発者としては
そこに何の未来も見いだせなかったのは事実です。
ZAURUS から手を引いた理由の1つはここにあります。

今の携帯電話もそうかもしれません。
ZERO3 も非常に使いやすくて気に入っていたのに、なかなか良い点が新型に引き継がれ
ません。良い感じにカスタマイズできても、新型に乗り換えるとまた同じことを最初から
やり直しです。
新たに発生した使いづらいところを穴埋めしていく作業なのです。

iPhone の魅力の一つは操作方法がずっと変わらないだろうと安心できること。
ビジョンがあって貫かれているからこそ、目先の商品コンセプトに振り回される
ことがないんでしょう。
速度とか OS3.0 の細かな変更点とかいろいろありますが、性能が上がって機能が
増えても、重要なところが大きく変わらないからこそずっと使い続けていけるはず。


これまで iPod touch 2G を使っていました。音楽プレイヤー扱いながら、
過去に使ってきた PDA の中では一番使いやすくて実用的だったという印象です。
OS3.0 への更新も、ソフトウエアをダウンロードしただけなのに Bluetooth が
使えるようになりました。
1200円でハードウエアも一緒に付いてきたような得した感じです。

HID はありませんでした。PS3 も Wii もコントローラは Bluetooh なので、もしか
したらキーボードだけでなくゲームコントローラもつながっていたかもしれない
と思うと少々残念です。


iPhone 3GS は iPod touch と比べると厚みもあって大きく感じます。
その代わりどこでも通信できて GPS とコンパスも使えるし、持ち歩くものを1つ
減らせます。乗り換える決め手となったのは MMS で、メールアドレスも変わらずに
従来の携帯電話を置き換えられるようになりました。

SMS/MMS はメーラーというよりチャットのような画面レイアウトで、フォルダではなく
送信相手毎に分類されます。
最初悩んだのは MMS の送信が出来ないこと。上の「メール」というボタンを押すと
ただの E-Mail になってしまいます。
下にある1行の入力欄が SMS/MMS 送信用の本文でした。検索だと思ってました。
いろいろ慣れない部分も多いので、これから慣れます。



DirectX11 の ComputeShader が実際に動くようになりました。

Direct3D11 GeForce driver 186.08 beta

ComputeShader 4.0 は FeatureLevel 10.0。つまり 1世代前の GPU で動作します。
cs4.0/4.1 の存在自体は November 2008 SDK でも確認できました。
しかしながら実際に使うためにはドライバの対応を待つ必要があったわけです。

本来の ComputeShader (cs5.0) の仕様に対して cs4.0 はサブセットとなります。
機能制限のある cs4.0 は、ぱっと見 PixelShader とほとんど違わないし
どこにメリットがあるのかあまりわからないかもしれません。
とりあえず目に付いた ComputeShader4.0 の利点は次の通り。

・シェーダーがバッファに書き込みできる
・共有メモリへアクセスできる

描画パイプラインに組み込まれたシェーダーの場合、ストリームとして動作するため
出力位置は固定でした。
ComputeShader は戻り値を持たず、代わりに UnorderedAccessView を通して
リソースの任意の位置にデータを書き込むことができます。

共有メモリはスレッド間でデータの受け渡しができるプロセッサ内部のローカルメモリです。
高速アクセス可能で他のスレッドが書き込んだ値を参照することもできます。

VertexShader 等を通さずに直接シェーダーのみ実行できるのも ComputeShader
の特徴となります。


●実行とデータの受け渡し

ComputeShader を実行する命令は Dispatch() です。

iDeviceContext->CSSetShader( iCS, NULL, 0 );
UINT	v= 0;
iDeviceContext->CSSetUnorderedAccessViews( 0, 1, &iOutputUAView, &v );
iDeviceContext->CSSetShaderResources( 0, 1, &iInputView );
iDeviceContext->Dispatch( 1, 1, 1 );

この例では入力が ShaderResourceView (SRV)、出力は
UnroderedAccessView (UAV) です。

入力データは何らかの形で SRV のリソースに書き込んでおきます。
ConstantBuffer や Texture へ書き込む手順と変わらないので難しくありません。

実行結果は UAV で受け取ります。
UAV のリソースは RenderTarget 同様 GPU が書き込むため D3D11_USAGE_DEFAULT
を指定しておきます。
デバッグ時など、結果を何らかの形で CPU で受け取りたい場合は
D3D11_USAGE_STAGING のバッファにコピーしなければなりません。

STAGING リソースから読み込む場合 Map() を使いますが、STAGING への Map は
ChildContext で実行することができませんでした。
STAGING は直接メモリアクセスしているのに、コマンドをバッファにためるだけでは
意味がないので当然かもしれません。
スレッド対応のためにすべて Deferred Context 化している場合は注意が必要です。
Immediate Context を使います。

(1) SRV リソースへ書き込み
  ・UpdateSubresource() → USAGE_DEFAULT
  ・Map() → USAGE_STAGING → CopyResource() → USAGE_DEFAULT
  ・Map() → USAGE_DYNAMIC
(2) 実行 Dispatch()
(3) UAV から結果受け取り
  ・USAGE_DEFAULT → CopyResource() → USAGE_STAGING → Map()

このように、ComputeShader の場合 CPU とのデータやりとりも一般のシェーダーと
同じ手順が必要になります。
CUDA とか ATI Stream (CAL) より若干手間がいるかもしれません。

ComputeShader 4.x の場合、UAV は RAW または Structured Buffer のみ使用できます。
同時に与えられる UAV も 1つだけです。


● ComputeShader の実行回数と ID

PixelShader なら 2次元の面積で描画するピクセル数が決定します。
このピクセルの数だけ Shader を実行することになります。

ComputeShader の場合 3次元の体積で実行するスレッドの数を指定できます。

例えば XYZ= ( 2, 3, 1 ) の場合、2 x 3 x 1 = 6 スレッド走ります。
各スレッドは自分がどの座標に対応しているのか 3次元の id 番号を
受け取ることができるわけです。

実行するスレッドの個数の指定は二カ所で行います。

1. Dispatch() の引数
2. ComputeShader のアトリビュート numthreads 指定

[numthreads(3,2,1)]  // ←これ
void cmain1(
		uint3 did : SV_DispatchThreadID,
		uint3 gid: SV_GroupThreadID,
		uint3 group: SV_GroupID,
		uint gindex : SV_GroupIndex
				)
{
	int	index= did.x + did.y * 10;
~

両者の指定は似ていますが別物です。
6次元の id 値を持っているといえるかもしれません。


・1グループ内のスレッド実行回数の定義

[numthreads()] で指定するのは 1グループ内で実行するスレッドの数です。
シェーダーのコンパイル時に決定するので static な値となります。

  uint3 SV_GroupThreadID // グループ内のスレッド番号

semantic SV_GroupThreadID はグループ内の id 番号を受け取ることができます。
[numthreads(3,2,1)] は次の 6 通り。

(0,0,0) (1,0,0) (2,0,0) (0,1,0), (1,1,0), (2,1,0)

  int SV_GroupIndex // グループ内の通し番号

SV_GroupIndex はグループ内の連番です。
[numthreads(3,2,1)] の場合 0~5 の連番を受け取ります。


・グループ自体を何回実行するか指定する

Dispatch() の引数は、シェーダーで定義したスレッド Group をさらに何回実行するか
指定します。例えば [numthreads(3,2,1)] を Dispatch( 2, 1, 1 ) で呼び出すと
合計 12スレッドプログラムが走ることになります。

  uint3 SV_GroupID // グループの番号

SV_GroupID はグループにつけられた番号です。
例えば Dispatch( 10, 2, 1 ) で呼び出すと (0,0,0)~(9,1,0) まで。
同じグループの中のスレッドはすべて同じ値です。

  uint3 SV_DispatchThreadID // 3次元の通し番号

SV_DispatchThreadID は numthreads も Dispatch も区別無く、3次元に展開した
スレッドの通し番号になります。
例えば [numthreads(3,2,1)] を Dispatch( 4, 9, 1 ) で呼び出すと 216スレッド。
(3*4, 2*9, 1*1) の範囲なので (0,0,0)~(11,17,0) までの値を取ります。

[numthreads(3,2,1)] を Dispatch( 2, 1, 1 ) で呼び出した場合の ID 一覧。

SV_GroupID  SV_GroupThreadID  SV_GroupIndex  SV_DispatchThreadID
(0,0,0)     (0,0,0)           0              (0,0,0)
(0,0,0)     (1,0,0)           1              (1,0,0)
(0,0,0)     (2,0,0)           2              (2,0,0)
(0,0,0)     (0,1,0)           3              (0,1,0)
(0,0,0)     (1,1,0)           4              (1,1,0)
(0,0,0)     (2,1,0)           5              (2,1,0)
(1,0,0)     (0,0,0)           0              (3,0,0)
(1,0,0)     (1,0,0)           1              (4,0,0)
(1,0,0)     (2,0,0)           2              (5,0,0)
(1,0,0)     (0,1,0)           3              (3,1,0)
(1,0,0)     (1,1,0)           4              (4,1,0)
(1,0,0)     (2,1,0)           5              (5,1,0)

ComputeShader4.x の場合スレッド数の 3番目の値は必ず 1 でなければなりません。
つまり PixelShader 同様 2次元の id 指定に制限されます。


● Group

上記のようにスレッドの実行にはグループという概念があります。
プログラムの実行はこのグループ単位に行われているようです。

・スレッド共有メモリは group 毎に確保される
・共有メモリへのアクセスは group 単位で同期できる


●共有メモリ

ComputeShader はローカルな共有メモリにアクセスすることができます。
メモリ空間にマップされるわけではないので Buffer より高速です。
ShaderResource と違い読み書きも可能です。
同一グループ内の他のスレッドと共有できます。他のスレッドでも書き込んだ値を参照可能です。

・レジスタ = スレッド固有
・共有メモリ = グループ内のみ共有
・リソース(Buffer)

Shader プログラム内で groupshared 宣言すると共有メモリになります。

ComputeShader 4.x ではいくつか制限があります。
容量は 16KB までです。
また共有メモリの宣言はグループ内のスレッド数と同数でなければならず、
書き込めるのは自分の SV_GroupIndex に対応するメモリだけ。
宣言できる共有メモリも1つだけです。
その代わり Structured のように構造体を与えることが可能です。

struct SharedMem {
	float2	fl;
	uint	id;
};

groupshared SharedMem	buf[6]; // 3x2

[numthreads(3,2,1)]
void cmain(
		uint3 did : SV_DispatchThreadID,
		uint3 gid: SV_GroupThreadID,
		uint3 group: SV_GroupID,
		uint gindex : SV_GroupIndex
				)
{
	buf[  gindex ].fl.x= gindex;
~

groupshared 指定は、アセンブラコードだと下記の宣言になります。
g0 が共有メモリです。

dcl_tgsm_structured g0, 32, 6


●同期

ComputeShader 4.x の場合 Interlock 系の Atomic なオペレーション命令は
ありませんが、メモリアクセスに対する Barrier 同期は使えるようです。

GroupMemoryBarrierWithGroupSync();
GroupMemoryBarrier();
AllMemoryBarrierWithGroupSync();
AllMemoryBarrier();

GeForce 用ドライバ 186.08 beta ではこれらの命令が動作しませんでした。
命令のコンパイル自体は可能だし DebugLayer も無言だったので、ドライバがまだ
対応していないだけかもしれません。
Reference Driver を使うと Shader4.0 でも動作しました。

アセンブラでは sync 命令が挿入されています。

sync_g              GroupMemoryBarrier()
sync_g_t            GroupMemoryBarrierWithGroupSync()
sync_uglobal_g      AllMemoryBarrier()
sync_uglobal_g_t    AllMemoryBarrierWithGroupSync()



関連エントリ
Direct3D11/DirectX11 (6) D3D11 の ComputeShader を使ってみる
Direct3D11/DirectX11 (4) FeatureLevel と旧 GPU の互換性、テクスチャ形式など
Direct3D11 (DirectX11) シェーダーの書き込み RWBuffer 他



masafumi さんのところで DirectX 11 対応ドライバが
動作したとの報告があがってました。

新 masafumi's Diary Direct3D11対応ドライバ

CheckFeatureSupport() の結果を調べてみました。

ドライバ 186.08
[NVIDIA GeForce 9800 GT ]
   vmem:497MB sysmem:0MB shmem:2811MB
   flag=0 
  (Direct3D11)
   *HARDWARE  = 10.0 (a000)
     feature threading  DriverConcurrentCreates=1
     feature threading  DriverCommandLists=0
     feature d3d10_x ComputeShaders_Plus_RawAndStructuredBuffers_Via_Shader_4_x=1
     feature doubles  DoublePrecisionFloatShaderOps=0
[ATI Radeon HD 4670]
   vmem:504MB sysmem:0MB shmem:2811MB
   flag=0 
  (Direct3D11)
   *HARDWARE  = 10.1 (a100)
     feature threading  DriverConcurrentCreates=0
     feature threading  DriverCommandLists=0
     feature d3d10_x ComputeShaders_Plus_RawAndStructuredBuffers_Via_Shader_4_x=0
     feature doubles  DoublePrecisionFloatShaderOps=0

動いています。threading DriverConcurrentCreates と
d3d10_x ComputeShaders_Plus_RawAndStructuredBuffers_Via_Shader_4_x
が 1 になりました。

以前の結果はこちら


関連エントリ
Direct3D10/11 DXGI1.1 とリモート GPU (アダプタ)


タッチパッドとか操作性とか、多少使いづらかったとしてもそれはそれ。
「これでしかできないことがある」のは確かです。
光センサー液晶パッドはまだまだ初物。

今後ソフトウエアの改良によって他の PC と同じ操作性へ近づけていくことは可能でしょう。
でもメビウス PC-NJ70A と同じことが他のパソコンで出来るわけではありません。

これまで書いてきたように、それ以外を割り切ったのが一番の特徴です。
普段持ち歩いているとついついただの普通のパソコンとして見てしまいがち。
あらためて光センサー液晶パッドについて考えてみます。


使っていてあまりわくわくしないのはなぜでしょう。
多少欠点があっても、何かしら今後良くなりそうな可能性の片鱗が見えれば
たいした問題ではないはずです。

従来のパソコンにない新しい部分が、現状だと他のデバイスでも出来ることの
寄せ集めでしかなく、しかも劣っていることが原因かもしれません。

他に良いものがあることをユーザーは知ってますし、自分も常時 iPod touch を
持ち歩いています。
ようするに今の光センサー液晶パッドでは、マルチタッチを使った操作は全く快適では
ないということ。

ではどうすればよいか考えてみました。


●苦手なこと

使っていると光センサー液晶パッドと比較的相性の良い操作、苦手な操作がわかってきます。

良い

 ・ペン操作
 ・ボタン

だめ

 ・ドラッグ系
 ・ジェスチャ入力
    二本指スクロール・拡大縮小・回転

手描きのペン入力は割と素直に使えます。
手書き文字入力も想像より快適でした。線を加える毎に即座に文字が絞り込まれていきます。
レスポンスも速く、自分が行った操作とフィードバックが直結しています。
でも純粋なタブレット PC と違い、キーボードがあるから使わなくても困らないのも事実。

電卓やピアノといったシンプルなボタンを活用するユーザーインターフェースも
不満が少なく相性が良いといえます。
感圧式と違って触った時点で反応するので、押した感触がわかりやすいようです。

逆にドラッグ操作でオブジェクトを動かすような操作は苦手です。
タッチ位置にオブジェクトがついてこないし、遅延が目立つし、見た目とずれが
あるため慣れるまで思うように操作出来ません。

ジェスチャ系も同様です。
たとえばズーム操作では、読み込みが間に合わなかったとしても画像を拡大表示して
ごまかし、見た目の反応速度を上げるテクニックが良く用いられます。
液晶パッドの場合はそれ以前の問題で、ただの拡大ですら更新エリアが大きいため
描画の負担となり現実的ではないのです。


●描画更新が遅いならただのボタンの方が良い

結局 描画の更新を減らす工夫が何より大切だといえそうです。

・できるだけボタンなどスタティックなユーザーインターフェースにする。
・ジェスチャ系は操作中ワイヤーフレームで表示してあとから更新する。

といったことを考え、ハードウエアの性能を考慮したユーザーインターフェースを
採用します。以下具体例。


・2本指のスクロール

初めてリスト一覧を見たとき、最初にスクロールバーを探してしまいました。
2本指のスクロールは いわれないと わからないし、描画が遅くて操作できているのか
出来ていないのか判断しづらくなっています。
なぜかスクロールバーも常時表示されていません。
そのせいでスライドしすぎて止まらなくなり、ますます戸惑うことに。

液晶パッドは画面が広いので、いっそ専用のスライド領域を設けてしまった方が
良いのではないでしょうか。見た目でわかりやすいし、スライドした量を描画すれば
操作出来ていると安心できます。


・ページ切り替えとボタン

広い面積のスクロールは苦手なので、直接ページを切り替えられるボタンがあった
方が迅速だしわかりやすいです。タブ切り替えのように。
フォトモードの秒数指定ボタンは最初ページ切り替えだと勘違いしました。


・ハードウエアボタン

触れただけで反応するタッチ面に対して、クリックボタンは少々力がいります。
ボタンの説明として「次ページ」「マウス操作へ」「前ページ」とわかりやすく表示
してありますが、むしろここの文字をタッチしたくなります。


・マッチ棒のパズル

マッチ棒をスライドで動かすのはたいへんです。
描画が遅くて思わぬところに落としてしまうことも。
しかも関係ない位置においてしまうと操作回数がカウントされて失敗扱いです。

ゲーム内容を考えると、動かしたいマッチ棒をタッチしてその後異動先をタッチ
するだけで十分だと思います。
ハードウエアスペックに応じた操作仕様にするだけで大幅に遊びやすくなるはずです。


・拡大縮小回転

個別にボタンを用意するか、タッチに合わせたワイヤーフレーム等の演出が
合っているように思います。

液晶パッド部の解像度はかなり高いです。
メインの液晶画面が 1024x600 ドットなのに対して 854x480 ドットもあります。
縦横ともにおよそ 20% 小さいだけ。
手書きイラストのキャプチャ選択も全体を 80% の縮小表示にして、ワイヤーで選択
範囲が表示されているだけで十分な気がします。


●まとめ

書き出してみたらあまりたいしたことではありませんでした。
操作ではなく描画の問題で、結論はハードウエアの能力を考慮したユーザー
インターフェースにするという一点のみです。

あとは画像取り込みとかスキャナ機能とか、何かしら光センサー液晶パッドでないと
出来ない使い方があると良いですね。


関連エントリ
メビウス PC-NJ70A (5) サブモニタ
メビウス PC-NJ70A (4) 使用感
メビウス PC-NJ70A (3) Windows7 RC を入れる
メビウス PC-NJ70A (2) 初期セットアップとメモリ増設
メビウス PC-NJ70A 液晶センサータッチパッド
メビウス PC-NJ70A 画面解像度とタッチ


前回 も書きましたが mebius は結構消費電力が大きいようです。
起動直後はともかく使用中はすぐにファンが回りだして左サイド手前の排気口
から熱が吹き出しています。最近の Atom ノートとしては珍しいでしょう。

バッテリーパックも 7.4V 4800mAh 36Wh と、決して小さいわけでは無いものの
カタログの持続時間は 3時間。同じ 10インチクラスの同等機種と比較すると
大食いなのは確かです。
やはり光センサー液晶パッド分の消費電力が加わっているためでしょう。

実際に持ち歩いてみるとサイズより少々重たいのが気になります。
カタログ値で 1.46kg。直前まで使っていた VAIO type P のおよそ 2倍。
説明書を見るとアドオンジャケットは 102g あるので、ジャケット付きだと 1.56kg。
実測で 1558g (アドオンジャケット含む) だったので一致しています。

増えているのは「増加分のバッテリー+センサーユニット」と考えられます。
すべては光センサー液晶パッドを搭載するためです。
光センサー液晶パッドのために作られた PC です。


●サブモニタの描画

液晶パッド部分のサブモニタは、残念ながらデスクトップの拡張画面としては
使えませんでした。将来ドライバの更新で対応できるかどうかは不明です。

今のところ専用アプリのみが描画アクセスしている印象です。
マルチタッチという独自の拡張があるので、おそらく Windows SideShow 等とも
別物だと考えられます。

描画とタッチ入力は独立しているようで、SLMgr/SLMgr2 が起動していなくても
マウス互換の普通のタッチパッドとして機能します。
マルチタッチの情報を受け取るには、何らかの専用 API を介する必要が
あるのでしょう。つまりマルチタッチ操作も専用アプリのみ。

サブモニタ部分の描画更新は遅く、特にフォントを含む画面が低速に見えます。
一般的なアプリとしての実用性はそれほど損なわれていませんが、タッチ操作への
追従性を考えるともの足りません。
メインメモリからの転送が原因なのか、それ以外にボトルネックが生じているのかは
わかりません。854x480 dot と非常に高精細なので、転送データ量が多く負担に
なっているのかもしれません。

液晶パッド部分で動く専用ゲームでもレスポンスの遅さは目立ちます。
ミニボウリングというゲームは比較的タッチへの追従性が良く、ほぼタッチ位置に
すぐ反応します。その代わり操作中は表示が点滅しています。

このことから、普段は内部で仮想的な VRAM を持っていて全転送しているのでは
ないかと考えられます。
仮想画面の転送はフレームレートを稼ぐことが出来ず、部分的な更新だと高速。
ミニボウリングは部分転送した重ね合わせを消すために、いったん背景を転送し
直しているため点滅しているのだと予想できます。

マウスモード時のエフェクト(光、猫、花)はきちんと重ね合わせが出来ているので、
あらかじめ合成したデータを部分更新しているのか、または別の合成機能が用意
されているのかもしれません。

画面の大きさはほぼ名刺大。


●サブモニタの使い道

追加削除可能な専用アプリ以外に、いつでも呼び出せる標準機能が用意されています。

・マウスモードの背景
  ・時計
  ・カレンダー
  ・スライドショー

・タッチモードの一時アプリ
  ・電卓
  ・手描きイラスト
  ・手書き文字入力パネル

マウスモードは常時何らかの追加情報を表示するもの、タッチモードは入力手段の拡張です。
どれも便利なものですが、一番欲しいメールや RSS などの通知系がまだありません。
SDK 公開以降何らかのアプリに期待、といったところでしょうか。
完全に PC-NJ70A 専用になりそうなので、わざわざ手を出すかどうかが問題。
デスクトップの拡張だったらすぐにでも実現可能なので惜しいです。


●Windows7 でアプリ追加

SHARP 液晶パッド設定を使ってアプリの追加削除できました。
D: ドライブにプリインストール時の Vista が残っていれば D:\Program Files
等の中にある個別のアプリ "Piano" "Bowling" "Fusen" "Macchi" の中の exe を
登録するだけ。辞書および電子ブックはパスが異なるためか動いていません。
レジストリにそれらしいエントリがあるので、そのあたりも設定すれば動くかも
しれません。

Windows7 RC を入れる方法はこちら。
メビウス PC-NJ70A (3) Windows7 RC を入れる


●専用アプリ

手描きイラストにキャプチャ機能があったり、フォトには BGM がついていたり、
ピアノにも自動演奏機能が入っていたりと意外に良く作り込んであります。
プリセットデータも入っていますね。

ただピアノとか上画面は特に使用していないのですが全画面乗っ取られてしまいます。
電卓のように液晶パッド部分だけに納めた方が良い気がします。
ネットを見ながらピアノを弾いたりできるし、作業中でも気軽に切り替えられるし。
上画面が残ってしまうので毎回終了しなければなりません。


●誤動作防止方法

前回タッチパッド部が右側に広いため、キータイプ中に右手が触れて誤動作すると
書きました。その後、液晶パッドの右側 1/3 を紙などで隠してもタッチパッドとして
機能することがわかりました。

SHARP 液晶パッド設定 → 省電力 →
 「ディスプレイがオフのときに、液晶パッドの表示を消す」
にチェックを入れて、さらに輝度を一番暗くしておきます。
この状態で右側 1/3 を紙などで覆っておけば誤動作を防げます。

mebius pcnj70a

とりあえずの対処ですが、これで安心してホームポジションに手を置けます。

「ディスプレイがオフのときに液晶パッドの表示を消す」にチェックを入れても、
輝度を最小に設定しておかないとバックライトだけ点灯していることがあるようです。
具体的な条件は未調査。


●インターフェース

Windows7 RC の場合、デバイスの取り外しメニューに LCD-Pad が出てきます。
このメニューを見る限り USB デバイスの一種として扱われているようです。
SLMgr, SLMgr2, SLLaunch のプロセスを停止しておくと取り外し可能になります。


● UQ

持ち歩きのために UQ WiMAX の UD01SS を使っています。普通に利用できています。
やはり電波強度表示等のモニタウィンドウは、他のウィンドウに隠れないよう
液晶パッド部分に表示したい。


● Bluetooth

Vista では東芝 stack が使われていました。
Windows7 では A2DP も標準でサポートしているので、Windows 標準の stack の
ままで十分です。実際に Bluetooth マウスとステレオヘッドセット MM-BTSH3 を
ペアリングして使っています。
Music Player も液晶パッド部のアプリケーションとして相性がよいかもしれません。


●タッチと描画とまとめ

新しいセンサーでメリットも多いし、実際に見た目のインパクトもあります。
直接タッチしたりペンで描けるわかりやすさは他には無いものです。
パネルも手前に配置されているためペンで字が書きやすいし、キーボードとの連携も
容易です。
タブレット PC のようにタッチ画面が遠かったり、近づけるには表示部を反転して
たたまないといけない、といった煩わしさもありません。

惜しいのは、上で述べたようにレスポンスが遅くてタッチ操作を活かしきれないこと。
本来直感的でわかりやすいはずなのに、その良さを阻害してしまっていることです。
この操作は出来ないのかな、と思った頃に何テンポも遅れて画面が切り替わったり、
操作ミスかと思って何度もスライドさせると、バッファがたまって目的のところで
止められなかったりします。

最低限操作に直結したフィードバックがあり、成功失敗がすぐに区別できれば
操作を自力で学ぶことが出来ます。また触りたいと思うでしょう。
機能として出来るかどうかよりも、ずっと大切なことだと思います。


関連エントリ
メビウス PC-NJ70A (4) 使用感
メビウス PC-NJ70A (3) Windows7 RC を入れる
メビウス PC-NJ70A (2) 初期セットアップとメモリ増設
メビウス PC-NJ70A 液晶センサータッチパッド
メビウス PC-NJ70A 画面解像度とタッチ


Atom PC は低発熱および低消費電力が特徴です。
同時にチップが小さい故の低価格も魅力。
Netbook が盛り上がっているのもそのためで、低価格化路線と
今まで作れなかったような低発熱な小型軽量 PC の実現に期待が持てます。

SHARP といえば常に一歩進んだ小さなモバイルデバイスも得意とするメーカーでした。
そのため新メビウスも超小型な PC の再来か、等と期待したのが今回の正直な本音。
実際に現れたその「新メビウス」はいろいろな意味で予想外のパソコンでした。

SHARP Mebius PC-NJ70A

その特徴はサイズでも価格でも無く、新機軸の液晶画面がついたタッチパッドです。
ユーザーインターフェースとして有望視されている画面を直接触れるタッチパネルが、
メイン画面ではなくもう一つの画面として「タッチパッド」部分に内蔵されています。

タッチパネル PC とノート PC のちょうど中間に位置するポジションで、
使い方も実に様々です。

・タッチパッドの拡張として。マルチタッチ対応版
・ペンが使えるタブレットの代わり
・独自のタッチパネル用アプリが動くプラットフォーム

おそらく SHARP が開発した「光センサー液晶パッド」が先にあって、それを
どのように活用するか試行錯誤した結果だと思われます。
過去に存在していないものなので、まだ本当の使い方が絞られていません。

可能性は大きいため、従来にないわかりやすい操作性を実現した新しいユーザー
インターフェースに化けるかもしれません。
今回幅広くモニターを募集した意図もここにあるのではないでしょうか。

そしてもう 1つ PC-NJ70A が示したのは Atom PC の新しい使い道です。

しばらく使用してみてわかるのは液晶センサータッチパッドは結構消費電力が大きい
のではないかということ。
メイン画面としても使えそうな高解像度の画面がもう 1つついているわけだし、
光センサーを使ったポインティング処理はそれなりの演算能力を必要としている
のかもしれません。

このような負担となるデバイスを内蔵できたのは Atom PC だからこそ。
すなわち 3つ目の路線。

(1) 低価格路線
(2) 低消費電力&低発熱ゆえの小型化路線
(3) 低消費電力&低発熱なので、他のデバイスを取り込む余裕ができる

このように考えていくと Netbook プラットフォームを採用した理由もわかる気が
します。


以下普通のレビュー


●外観

上面と内面は光沢のある仕上げです。
特に液晶タッチパッドはパームレスト部と一体化しており、継ぎ目のない
アクリルカバーで覆われています。
汚れがつくとすぐに気になってしまうのは他の光沢仕上げの PC 等と同じ。

対して裏面はざらつきのある EeePC 701 のようなプラスチックで、滑り止めの波状の
加工が施されています。透明感や高級感はありませんがこちらの方が汚れが目立ちません。

インターフェース類は左右のサイドのみ。後ろはバッテリー、前はステレオスピーカー。
この PC のバランスを考える上で、かなり割り切って作った部分といえます。
特筆点は左手前側面にある排気口とその隣のペンホルダ。


●キーボード

比較的固めながらしっかりしたキーボードは個人的には好印象です。
変形キーもありますが右一列のみで済んでいるし、
[1] の左に [全/半] キーも配置されています。
[変換][ひらカタ] 周りの配列も標準的でキートップも滑りません。


●キータイプとタッチパネル

実際にキータイプしていて問題となるのはむしろタッチパッドの方です。
液晶画面があるせいか想像以上に横に大きな面積を取っています。
ホームポジションでキータイプしているとどうしても右手親指が触れてしまうのです。

・タッピング (タッチで左ボタン相当) を禁止する
・キー入力中はマウスモードからタッチモードに切り替えておく
・外付けマウス接続中はマウスモードにならない設定にしておく

等の対策は取れるかもしれません。
幸いフラットで継ぎ目がないのでソフトウエアで対処できそうです。
将来何らかのタッチパッドアプリを作ることが出来れば、有効面積をエリア指定で
制限したり、スクロール領域を設定することも可能でしょう。
まずは SDK 次第。


●キータイプとマウスボタン

もう一つの難点はボタンクリックが遠いことです。特に右クリック。

液晶パッドの面積が大きいので、ホームポジションに手を置いたままだと
少々がんばって指を伸ばすことになります。
左クリックは何とか。右クリックは完全に手をずらす必要あり。

タッチパッドは本体の中央に置かれているので、ホームポジションからは右寄りに
なります。さらに本来右ボタンがあって欲しい場所にはモード切り替えボタンが!
右ボタンはますます遠くなっています。

キータイプを中心に考えるとこのボタン配置は使いづらい代物です。
せっかくのマルチタッチ&液晶パネルなので、マウスボタンもパネル内に作れたら
良いのではないでしょうか。マルチタッチ対応なら同時押しも出来るし、位置も好みで
並べられます。真ん中ボタンだって作れるでしょう。
触って区別がつくように透明なシールを周囲に貼って段差つけて、、といった工夫も
必要かもしれません。


●タッチパッド・マウス操作

マウスモードでのカーソルの追従性は若干遅延を感じます。
初期の頃の低速なワイヤレスマウスを使用しているような印象です。

コントロールパネル → 電源オプション → プラン設定の変更→ 詳細な電源設定の変更

 電源オプションのダイアログが開くので

   シャープノートパソコンの設定
    → サブ画面センサー設定
     → バッテリ駆動: 処理優先
       電源に接続: 処理優先

上記のように サブ画面センサー設定 を「処理優先」にすると多少改善するように
見えます。

原因をいろいろ考えてみましたが、どうも一般的な静電容量式タッチパッドと
比較すると初動作の遊びが大きいようです。
数ミリ動かしただけでは反応せず、大きめに動かすとついてきます。

ペンの手描き入力でも、慎重にゆっくりペンを動かすと即座には点が打たれず
短い折れ線の集まりになることがわかります。
ペンを動かさずに点を打つとその場所に現れるので、ピクセル分の分解能は持っている
ようです。この辺の挙動は誤動作対策かもしれません。
今後ドライバの更新等で改善されることを期待します。


●タッチパッド・手描き入力

タッチ操作では「手描きイラスト」機能によってペンで直接絵を描くことが出来ます。
いわゆるタブレット代わりです。

ユニークなのは単なるお絵かきソフトにとどまっていないこと。
右上にあるキャプチャボタンを使えば、メイン画面の任意の部分を取り込んで
その上に重ねて描けるようになっています。
デスクトップだろうとアプリだろうと落書き自由。

キャプチャ自体はボタンを押した瞬間に行われるようで、その後の範囲指定は取り
込んだ画像の切り出しに相当します。タッチ操作で拡縮&スクロール可能。
画面の追従性が悪いので最初は操作がわかりにくいかもしれませんが、
サブ画面の解像度が高いことは十分実感できます。

mebius pcnj70a
↑デスクトップの取り込み

取り込んだ&描いた絵はピクチャフォルダに保存可能。
編集画面が少々狭いけど、PC 操作中にいつでも呼び出せるのはおもしろい仕組みです。

ペン操作の PDA や電子手帳を長年使ってきましたがどれもこれも感圧式でした。
ペンの先端が触れるだけで反応する軽さは今までにない感覚です。
これも光学式ならでは。

ペン座標の判定は残念ながら同時に一点のみでした。
Windows7 付属の「ペイント」だと、マルチタッチ環境なら複数の指で同時に線が
描けたりします。
せっかくのマルチタッチセンサーなのだから、マルチペン対応もぜひ希望します。
マルチだと、一人が描いている隣で別の人が落書きできたりするのです。


関連エントリ
メビウス PC-NJ70A (3) Windows7 RC を入れる
メビウス PC-NJ70A (2) 初期セットアップとメモリ増設
メビウス PC-NJ70A 液晶センサータッチパッド
メビウス PC-NJ70A 画面解像度とタッチ


SHARP Mebius PC-NJ70A に Windows7 RC x86 入れました。
気になるタッチパッド部分のアプリも動作しています。
Aero Glass も半透明。

mebius pcnj70a

Windows 7 RC の エクスペリエンスインデックス
CPU  2.2
RAM  4.3
AERO 2.0
GAME 3.0
HDD  5.3


以下手順の詳細です。
リカバリディスクの作成はすでに終わっているものとします。


(1) インストール領域の作成

デュアルブートの場合 Vista で起動し、
120GB の "MEB_V7710T (C:)" をパーティション分割します。

スタートメニュー → コンピュータの上で右ボタンメニューから 「管理」
  ディスクの管理→ MEB_V7710T の右ボタンメニューでボリュームの圧縮

さらに 18GB の D: ドライブを削除して連続する未使用領域とする。
61.6GB ほど確保できました。(環境によって増減)


(2) ドライバの保存

C:\SHARP 以下のドライバをメモリーカードや USB メモリなどに保存しておきます。


(3) Windows7 RC の install

USB の外付けドライブから Windows7 RC の DVD を起動して、(1) で作った
未使用領域に新規インストールします。

この状態では光センサータッチパッドは、マウス互換の単なるタッチパッドとして
機能しています。

インストール中に無線 LAN 設定の画面が出るので、もし接続できるならここで
設定します。ここで接続しておくとドライバをネットから検索できるようになります。
あとから Windows Update しても同じです。


(4) 残りのドライバを入れる

Windows7 で起動したら不明なデバイス 2個のドライバを入れます。
不明なのは acpi 関連と lcd-pad。手順はどちらも同じ。

デバイスマネージャーで不明なデバイスの右ボタンメニューから
  「ドライバーソフトウエアの更新」
  → コンピュータを参照してドライバーソフトウエアを検索します
  → [参照] ボタン
  → (2) で保存したフォルダか、D:\SHARP を指定

「 SHARP eco Button Driver 」と「 SHARP LCD-Pad Driver 」が入れば完了です。


(5) 光センサー液晶パッド用ソフトウエアを入れる

D: ドライブ ( MEB_V7710T (D:) ) が見えているなら D:\SHARP\DRIVERS\SubLCD を
開きます。(2) で保存したドライバフォルダの SubLCD でも構いません。
SubLCD 内の setup.exe を管理者権限で実行します。
setup の上で右ボタンメニューから「管理者権限で実行」を選ぶ。

インストールが終わると SHARP 液晶パッド設定を実行できるようになりますが
まだ下画面は出てきません。

C:\Program Files\SHARP\SubLCD\SLMgr.exe

を実行します。これで液晶パッド部の画面が出ます。
一度実行しておけば、次からは再起動しても画面が出てくるようになります。
何もしなくて良いです。

電卓、手描きイラスト、手書き文字、フォトはそのまま使えます。
マウス操作時の背景なども OK。
辞書や電子ブック、エンターテイメントメニューなど、後から追加するタイプの
アプリケーションはまだ空っぽです。この辺は未調査。


(6) 液晶の輝度変更ソフトを入れる

[Fn] + [F6]/[F7] の明るさ変更がきいていないので、代わりのソフトを入れます。
VAIO type P 用に作った下記ソフトがメビウス PC-NJ70A でも使えました。

backlightwin v1.00

手順
 1. アーカイブ内の backlightwin.exe を適当なフォルダにコピー。
 2. backlightwin.exe の右ボタンメニューからプロパティ → ブロックの解除
 3. ショートカットを スタートメニュー内の スタートアップ フォルダに作っておく

操作は [Ctrl]+[Alt]+[F5]/[F6] になります。


(7) 電源設定の確認

コントロールパネル → 電源設定 → バランスの右端にある「プラン設定の変更」
 → 「詳細な電源設定の変更」で電源オプションが開く

電源オプションのダイアログの中に「シャープノートパソコンの設定」ができている
ことがわかります。

この中の「サブ画面のセンサー設定」を開くと
  バッテリ駆動: バランス
  電源に接続: 処理優先

となっていることを確認します。
「処理優先」にしておくと、光センサー液晶パッドの反応が若干改善されるようです。

この設定は Vista にもあります。Vista だと購入時の設定がどちらも「省電力」
なので「処理優先」にしておいた方がよいかもしれません。


● Windows7 RC の利用

液晶パッドも動いたので、このままでも何ら問題ない気がします。
動作も速くて快適です。

タッチパッドはマウスとしての認識で、タブレットのような特殊なデバイスとして
識別しているわけではないです。将来のドライバ更新で変わるでしょうか。

少々疑問だったのは無線の接続表示が 11n (135Mbps) となっていること。
ファイルを転送してみると 48Mbps くらいだったので一応 11g の速度内。

いくつかのソフトはまだ入ってないですが、無線 LAN のつながりやすさや
標準でサポートしている Bluetooth プロファイルなど Windows7 の良い面も多いです。

まだタッピングを off に出来ないので、タッチパッドをマウス操作モードで使って
いると指が触れて誤動作することがあります。結局 Bluetooth マウスを使ってます。


関連エントリ
メビウス PC-NJ70A (2) 初期セットアップとメモリ増設
メビウス PC-NJ70A 液晶センサータッチパッド
メビウス PC-NJ70A 画面解像度とタッチ
Windows ノート PC の backlight の明るさ変更方法


●サイズ

箱から出した印象は、厚みがあって思ったより大きいかなというもの。
比較写真は下記の通りです。
今まで使ってた PC がかなり小さいため比較すると大きく見えます。
一般的には 10.1インチワイド液晶のネットブックスタイルで、小型なノート PC の
範疇のはずです。

mebius pc-nj70a
↑左上が SHARP メビウス PC-NJ70A (10.1インチ)
 右上が ASUS EeePC 901-X (8.9インチ)
 左下は SONY VAIO type P (8インチ)


画面を開いたところ。画面が 4つあるように見える。

mebius pc-nj70a
↑左上が SHARP メビウス PC-NJ70A (Windows Vista)
 右上が ASUS EeePC 901-X (Ubuntu 9.04)
 右下は SONY VAIO type P (Windows7 RC x86)


EeePC 901 の前に使っていたのは EeePC 初代 4G-X (701)、さらに LOOX U50、
Let's Note R40 など歴代使ってきたノートはどれも光学ドライブのない小さい
ものばかりでした。
やはり SHARP の新型に期待したいのは、本当は MURAMASA とか ALL IN NOTE
のような鋭い機種。

今回のメビウスは液晶センサータッチパッドを搭載したがために、敢えてそれ以外は
無難な普通のスペックにまとめているように見えます。


●スペック

SHARP Mebius PC-NJ70A

PC-NJ70A のスペックは構成は一般的な Atom ノート PC と同じです。
Atom N270 + 945GSE の組み合わせは ASUS EeePC 901 でもおなじみものもの。
それ以外の特徴や構成も良く似ています。

例えば外部端子は VGA + USB2.0 x3 + メモリカードスロット x1。
内蔵機能も 無線LAN、Bluetooth、Webカメラ、RAM 1G/MAX 2G、ステレオスピーカー
と EeePC 901 をお手本にしたかのような構成です。

相違点は下記の通り

           EeePC 901-X    PC-NJ70A
無線LAN    802.11b/g/n    802.11b/g
SSD/HDD    SSD 4G+8G      HDD 160G
Bluetooth  2.0+EDR        2.1+EDR
MemoryCard SDHC           SDHC/MS/xD
LCD        8.9inch        10.1inch
OS         XP Home        Vista Home Basic

無線 LAN に 11n が無いのは残念ですが、省略せずに Bluetooth が乗ってるのは
好印象です。HDD 搭載で容量に余裕があるため、かなり普通に使える PC のスペック
になっています。その代わり SSD の身軽さは無いし、設定の手間もいらないので
ぎりぎりを追求するマニアには物足りないかもしれません。


●初期起動とリカバリディスク

電源投入後の初期起動は結構待たされました。
Windows は固まったかな、と思ったらとりあえず待つのが正解。
HDD 内蔵の ノート PC は 一年半ぶりです。

起動時にきかれる Windowsセットアップの
「Windowsを自動的に保護するよう設定してください。」
の質問は、とりあえず「推奨設定を使用します。」を選んでおきます。
この設定はあとで変更します。

無線LAN はデフォルトで off だったので、ネットワークがない状態で起動しました。
起動後最初にやるのはリカバリディスクの作成です。

(1) USB で外付けの DVD ドライブを接続する
(2) スタートメニューから「プロダクトリカバリDVD作成」を選ぶ
(3) DVD-R が 4枚必要

外付けドライブは、あまりきちんとしたものでなくても大丈夫です。
いつも余っているデスクトップ用のベアドライブ+USB SATA/IDE 変換アダプタを使っています。
type P もこの構成でリカバリディスクを作りました。

ところが途中 2枚目で書き込みエラー。
2枚目なのにエラー後「1番目のディスクを閉じています」のまま戻ってきません。
仕方ないので強制停止して再起動してやり直しです。

リカバリディスクの作成メニューは飛ばせないため 1枚目から作り直し。
4枚目でエラーにならず良かった。


●WindowsVista と Aero

初期状態で Aero が有効になっていました。やはり GMA950 の方が余裕あります。
WindowsVista Home Basic なので半透明にはなりませんが、ウィンドウ自体は
ハードウエアで描画されています。
VAIO type P のような初期状態の描画の重さはないです。


●少し軽くする

Windows Vista は画面が出ても読み込みが完全に終わっていないので、裏でディスク
アクセスが続いています。
起動直後はレスポンスが悪く感じます。
しばらくはそのまま落ち着くまで待っていた方が良いです。
特に下の画面に絵が出るまでは。

あとサイドバーがジェットはすぐに閉じて再び起動しない設定に。


●ネットワークと自動更新と Vista SP2

[Fn] + [F1] で無線 LAN が有効になります。
タスクバーの接続から列挙されたものを選ぶとうまくつながらなかったので、

ネットワークと共有センター → 接続またはネットワークのセットアップ
 → ワイヤレスネットワークに手動で接続します → 手動設定

でつながりました。

ネットがつながったらスタートメニューから Windows Update を選んで自動更新します。

  更新プログラムの確認→更新プログラムのインストール

を数回繰り返すと最終的に Vista SP2 も入ります。
一回では出てこないので何度か更新を繰り返す必要あり。


●自動更新の設定変更

スタートメニュー → Windows Update を開く
サイドバーから「設定の変更」

 "更新プログラムを確認するが、ダウンロードとインストールを行うかどうかは選択する"

に変更します。
持ち歩くノートの場合、出先で細い回線やパケット課金なのに巨大なアップデートを
ダウンロードされたら困るため。


●軽くする

重く感じる原因の一つがプリインストールされたソフト群です。
VAIO type P ほどではないですが、メビウスもまた IE に多数のツールバーが
組み込まれておりブラウザの起動で待たされます。
不要なツールバーをアンインストールするか、Firefox や Chrome といった他の
ブラウザを併用した方が良さそうです。

以前 PC が重いから見てくれといわれて見に行ったマシンは、IE に 10数個の
ツールバーが場所を取り合っていました。

Netbook はスペックが低く安価な割に、予想よりも快適で使える PC だったので
人気を博しました。その原因の一つにはプリインストールソフトが一切無かったことが
あげられるのではないかと思います。
割り切ったスペックのために最小限にとどめたことがかえって功を奏したわけです。


●Vista の設定

もともと EeePC 901 にも Vista を入れて使っていた くらいなので
使いやすさやパフォーマンスに特に不満はないです。
いつも使ってる設定を書いてみます。あくまで個人の好みで。

ツールバーの上で右ボタンメニューからプロパティを開く
 「タスク バー」のタブ
  → 「タスクバーを他のウィンドウの手前に表示する」のチェックを外す
   画面の縦を少しでも広く使うため
  →「クイック起動を表示する」のチェックを外す(お好みで)

 「[スタート]メニュー」のタブ
  → 「最近開いたファイルの一覧を保管し表示する」のチェックを外す
  →右上の「カスタマイズ」
    → [お気に入りメニュー] チェックを外す
    → ゲーム>この項目を表示しない
    → コントロールパネル>メニューとして表示する
    → コンピュータ>メニューとして表示する
    → ドキュメント>メニューとして表示する
    → ピクチャ>表示しない
    → プログラムを検索する>チェック外す
    → ミュージック>表示しない
    → 規定のプログラム>チェック外す
    → 検索ファイル>ファイルを検索しない
    → 個人用フォルダ>メニューとして表示する
    → 大きいアイコンを使用する>チェック外す
    → 通信内容を検索する>チェック外す
    →電子メールリンク>チェック外す

スタートメニュー → コンピューター → サブメニュー
  上の設定をしておくとドライブのサブメニューが表示されるので、
  C: ドライブをスタートメニューにドラッグ。C:\ をすぐ開けるように。

コントロールパネル → フォルダオプション
  「表示」のタブ
    → タイトルバーにファイルのパス名を表示する>チェック入れる
    → ファイルとフォルダの表示
      → すべてのファイルとフォルダを表示する
    → 登録されている拡張子は表示しない>チェック外す

"タイトルバーにファイルのパス名を表示する" のオプションは「クラシックのみ」
と書かれていますが、Aero でもタスクバー上にマウスカーソルを乗せたときに違いが出ます。

Explorer の「お気に入りリンク」にゴミ箱を入れておく。

「ドキュメント」フォルダの中に "!App" というフォルダを作っておく
  よく使うアプリのショートカットをこの中に入れておくと、
  スタートメニュー → ドキュメント → !App と直接開ける。

レジストリ書き換えで [ESC] [半角/全角]キー、[CapsLock] [Ctrl]キーを入れ替え
  メビウスはキー配列が素直なのでデスクトップで使ってる設定と同じものが
  使えました。(EeePC の場合)

不要なアプリケーションとウィンドウズコンポーネントを削除

使わないサービスの停止

コントロールパネル → 電源オプション
  電源プランが高パフォーマンスになっているのでバランスに
  → 詳細な電源設定の変更 → 電源オプション
    → 電源ボタンと LID
     LID クローズの操作が「何もしない」になっているので「スリープ状態」に変更


●マニュアル

メビウス PC-NJ70A のマニュアルはオンラインで読めるようになっています。
 スタートメニュー → メビウス電子マニュアル


●メモリ増設

メビウス PC-NJ70A は最大 2GB に RAM 増設できます。
比較的大きめのボディは余裕があるのか、メモリスロットは 2本あります。
EeePC 701/901 は 1本だったので、増設時は元の RAM を取り外す必要がありました。
増設時に元のメモリが無駄にならないのは良いですね。

ちょうど EeePC から取り外したメモリが余っているので RAM 増設していみます。
マニュアルの説明はここ。

 スタートメニュー → メビウス電子マニュアル → 活用マニュアル

EeePC 901 から外したメモリは 1GB なので、うまい具合に 2GB に増設できます。

AC アダプタもバッテリーも外してから裏のふたを開ける

mebius pc-nj70a

ネジを外すだけで容易にアクセスできますがネジは堅めです。頭をなめやすいので、
一致したサイズのドライバで本体に強く押しつけるように回します。

取り付けてから気がつきましたが、EeePC 901 についてた 1GB のメモリは
PC2-3200 / DDR2-400。
これだと遅くなってしまうので、EeePC 4G-X (701) に内蔵されていた 512MB の
メモリ PC2-5300 / DDR2-667 に入れ直しました。
Total 1.5GB 認識しています。RAMDISK も使わないし当分は十分だと思います。


関連エントリ
メビウス PC-NJ70A 液晶センサータッチパッド
メビウス PC-NJ70A 画面解像度とタッチ
EeePC 901 に Windows Vista その3


●タッチパッドのところに iPhone が埋まってるやつ

iPhone / iPod touch を使っていると、マウス代わりにこれでそのまま PC操作
できたらいいんじゃないかな、と思うことがあります。
タッチパッドの感触はなめらかでレスポンスも速いし、マルチタッチもわかりやすく、
スクロールなどの操作も心地よく動いてくれるからです。
(実際に iPhone をタッチパッド代わりにできるアプリもあるようです)

本当にそれを実現しようとしたのかどうかわかりませんが、
SHARP メビウス PC-NJ70A の見た目はまさにそれ。
センサーの仕組みはともかくマルチタッチ対応のパッドに液晶画面もついていて
独自のアプリも動くという。なんか埋まってます。

そのメビウスがようやく手元に届いたので使ってみました。

現実はそう甘くはないようです。


●光センサー液晶パッドのモード

・マウス操作モード
・タッチ操作モード


●マウスモード

mebius pc-nj70a

通常はマウスモードで、ノートパソコンの一般的なタッチパッドとして機能します。
特徴的なのは以下の点

・表面がつるつる
・パームレストと一体型
・サイズ大きい、幅が広い
・ボタンが 3つあるけど 2ボタンマウス相当
・マルチタッチ
・多少のビジュアルエフェクトがある

つるつるなのはおそらく光学式センサーの光を通す必要があるため。
光が透過しさえすれば透明な素材で全体を覆ってしまっても構わないのでしょう。

ボタンは残念なことに 2ボタンでした。
VAIO type P は中ボタンがついていて 3ボタン。
液晶パッドの真ん中のボタンは「マウス操作」と「タッチ操作」の切り替えです。

マルチタッチの活用として二本指スクロールがあります。
EeePC 901 等と同じです。

液晶パッドの画面には、タッチ位置に遅れてついてくる光のカーソル演出があります。
この辺は設定で変更できるようです。


●タッチ操作モード

mebius pc-nj70a

真ん中ボタンでタッチ操作モードに切り替えると、液晶パッドにメニューが出てきます。

ここでタッチを活用した独自のアプリケーションを走らせることが出来るようです。
マウスカーソルを使うウィンドウズアプリと違って、こちらは直接触って操作
できるのが特徴。

しかも光学センサー液晶パッドは他の方式と違い

・マルチタッチ対応 (何点でも?)
・静電容量式のように指が触れただけで反応する
・ペン操作もできる

等と利点も多数あります。
しかしながら画面の呼び出しや表示切り替えは決して速いとはいえず
なめらかなアニメーション演出も期待できません。

例えば電子ブックリーダーでは本のタイトル一覧が表示されるのに、タッチ操作では
上下にスクロールできません。マウスの左右ボタンで上下のページ切り替えだと
気がつくのに若干時間を要しました。(一応二本指スクロールは出来るけど描画が
ついてこない)

アプリケーションの作り込みの甘さや描画速度の遅さ、反応の悪さなど、可能性の
あるセンサーやデバイスをまだまだ使いこなせていないようにみえます。

サブ画面用に描画アクセラレータが乗っていないのか、メインメモリからの描画
転送がボトルネックなのかはまだわかりません。

センサー自体は大変ユニークで、例えば「エンターテイメント」に入っている
ピアノのアプリはマルチタッチを実感できます。
とりあえず指 4本のタッチ座標が取れているのは確認できました。

HP TouchSmart PC IQ800 のマルチタッチは 2点までだったので、今後 SDK が公開
されたり Windows7 用ドライバが出てくれば、さまざまな活用が考えられます。
今後に期待ですが、「iPhone / iPod touch が埋まっている」の域まで達するには
まだまだかかりそうです。

なおタッチ操作モードではマウスカーソルが動かないので PC の操作ができません。
外付けのマウスをつないでおけばタッチモードでも普通に使えました。


●まとめ

ネットブックなど多くのノートパソコンがあふれる中で、普通じゃない機能を持った
PC であることは確かです。
実用性よりもまずはこの点をおもしろいと思えるかどうかが評価の境目となるでしょう。
これからしばらく実際に持ち歩いて使いつつ、まずは普通の PC としてセットアップ
してみます。


関連エントリ
メビウス PC-NJ70A 画面解像度とタッチ
Windows7 とマルチタッチ / HP TouchSmart PC IQ800


久しぶりの歩数計関連です。
やっぱり買ってしまいました。カロリズム

TANITA カロリズム CALORISM


ここ最近加速度センサーの普及によって歩数計がちょっとしたブームです。
例えば一般的に

(1) つける場所を選ばない

 2D または 3D の加速度センサーによって向きの制限が無くなりました。
 腰にクリップで留めなくてもいいんです。
 胸ポケットでも鞄の中でも OK

(2) 時計内蔵で自動記録

 毎日 0時に自動でカウンタをリセット。
 1~2週間分の記録が残るので、面倒な操作なしに毎日の歩数がわかります。

(3) その他

 消費カロリー表示、歩いた総時間の表示、歩数を入れておくと距離の算出、
 などはたいてい基本機能としてついてます。

新製品の出るペースが速いし種類も多いし機能も豊富でつい気になってしまいます。
ただ安価なものだと加速センサーではなく振り子スイッチ式のものがある点に注意。
振り子式のシンプルなものの方が良い場合もありますが、気楽に使えるのは
加速度センサータイプの方です。


●ハドソン てくてくエンジェル

他にもゲーム風の機能がついているものも多数あります。
特に以前紹介した「てくてくエンジェル」は機能的に侮れません。

てくてくエンジェル DS日記 まじめレビュー

例えば
・120日間のログ保存
・乗り物に乗っている間の誤計測を遮断するスリープ機能
・毎日の記録とは別に測れる、区間計測機能
・リセット時間を変更できる昼夜のシフト機能

など、他の歩数計についていないのが不思議なくらい作り込んであります。
残念ながら欠点もあって、数日使わないでいるとえんじぇるが逃げてしまい
ゲーム&計測が終わってしまうこと。
ただのツールじゃないのであたりまえですが使ってるとよく遭遇します。
あとは省電力のためボタンを押すまで液晶画面が消えていることでしょうか。
未使用だとバッテリーは結構持ちます。


●コナミ e-walkeylife 2

その後使っていたのはコナミの歩数計 e-walkeylife2 (健身計画2)

e-walkeylife 2

こちらは加速センサータイプながら腰への取り付け推奨です。
しっかりしたクリップやストラップなどがついており、気楽に鞄に入れたりとかは
できません。
特徴は USB コネクタ内蔵で、PC や TV 用の健身計画2があればデータを
転送できるというもの。別売りなので結局使いませんでした。

もう一つの特徴は歩きと走りを区別してカウントできることです。
それぞれ別カウントし、かつ消費カロリーなども別計算です。
走っても良い歩数計って実はあまりないんです。
たいていは歩行のみ計測で走行時は誤計測する可能性があります。
または本気で走るなら Nike+ とかジョギング用。


●TANITA カロリズム AM-120

そして

TANITA カロリズム

最近出たカロリズムはもはや単なる歩数計ではなく「活動量計」を名乗っています。
まず取り付ける場所が違います。
腰じゃないと計測できなかった昔の万歩計とは完全に別物。
「上半身につけてください」だそうです。

なぜなら

(1) 上半身も含めた動きを検知して、歩行以外の運動も測定
  歩かなくても 1日の総消費カロリーがわかる

(2) 1時間毎の活動量を記録。
  1日単位ではないので、何時頃激しく動いたのかよくわかる。

1時間単位の運動量はわかりやすいグラフ表示です。PRO TREK の気圧計みたいな。
ちゃんと 24個メモリがあるので夜型でも昼夜逆転でも大丈夫。

時間単位の詳細なグラフが残るのは一週間分。
さらに週単位で 7日分の活動量をグラフで一覧可能です。これは 12週間遡れます。

歩行計測の枠を飛び出そうとしています。
少々値段が高めですが、出勤時間、外出時間などが一目でわかるグラフは非常に
おもしろいし、、少々怖くもあります。

その他歩数計としての基本機能は押さえています。

初期設定時に体脂肪率を入れないといけないのが少々変わってるところでしょうか。
そのおかげか基礎代謝表示があるので、基礎代謝+消費カロリーがわかるのは
ユニークです。何カロリーまで食べても大丈夫、という目安になりますね。


本体はスリムなスティック型ですが、表示も大きく見やすいものです。
CR2032 x2 個使う代わりに電池寿命は 9ヶ月と比較的長め。
長時間センサーが反応しない状態になると液晶を消してスリープ状態になるようです。
賢いのは振動を検知して自動で起動すること。
ボタンを押す必要はありません。


本来非常に高機能なはずなのに、どうにかしてシンプルかつ単純にまとめようと
苦労したあとが見えます。
それゆえ時間グラフと総活動量表示以外は比較的普通なのです。
歩数、消費カロリー、時刻、距離&時間、エクササイズ単位やメッツなど。

モード切替は表示の切り替えだけ。
6画面あるけど表示の違いが少なくて慣れるまでちょっと見分けづらい。
メモリボタンで記録を遡れますが、1ボタンしかないので一周するまで戻れません。
当日の画面に戻らないと「週ログ←→日ログ」の切り替えが出来ないのも不便です。
週ログで間違ってメモリボタンを押してしまうと特に。

無理してボタン数を減らすよりも、てくてくエンジェルみたいに
 カーソル+決定
とかメニュー式の方がずっとわかりやすい気がします。

リセット時間の変更が無いので、夜型かつ1日単位できっちり歩数を計測したい人は
時計をずらしておく必要があります。でも従来の歩数計と違って時間単位のグラフが
あるので、そこまでこだわらなくても十分楽しめます。

やっぱりというか区間計測機能は無いです。
カウンタの強制リセットはありますがその日の記録も消えます。

時計は時刻のみでカレンダー無し。

マニアックに見てくと、可能な操作も少なく表示画面数も少なく少々物足りない
ところがあります。

でも一目ですぐわかる時間毎のグラフはやっぱり楽しいし、その日の行動を
思い出すにも役立ちます。
Nike+ の記録はもっと詳細ですが、PC に接続しないと見れないし常時測定している
わけじゃないのでなあまり手軽とはいえませんでした。

1日あたりの歩数を増やしていこうとか、ノルマを守ろうとか、目的を達成しようとか
そういう従来の歩数計のモチベーションとは少々質が違います。
自分の毎日の行動が記録に残ってること。
それだけで興味があるし、総活動量なので歩数だけを気にしなくても良いわけです。
身につけていてもプレッシャーがずっと少ないのです。

でも…仕事してるのに、座ってずっとキーボードをたたいていてもマウスをクリック
していても、さっぱり活動量は増えません。
キータイプを消費カロリーに変換するアルゴリズムが必要です!


関連エントリ
てくてくエンジェル DS日記 まじめレビュー


HTC E30HT の 3D caps 情報を提供していただきました。
nekomantle さんありがとうございました。

Direct3D Mobile DeviceCaps 一覧

E30HT は 3D アクセラレータに対応していないようです。
スペックを調べてみると、同じ Touch Pro でも HT-01A/X05HT とは相違点が目立ちます。

HTC Touch Pro E30HT
HTC Touch Pro HT-01A
HTC Touch Pro X05HT

通信方式の違いから使用しているチップが異なっており、
搭載されている 3D 機能も互換性がないのかもしれません。

モバイルライフ応援日記 伊藤浩一 au初のスマートフォン「E30HT」開封レビュー(第104回)
 >図12 起動画面。ユーザーインタフェースには「Touch FLO 3D」ではなく
 >「Touch FLO」が搭載されています

上の記事によると Touch FLO 3D ではなく Touch FLO に変更されているとのこと。
この辺も関係がありそうです。

なお Qualcomm MSM7500 自体は 3D アクセラレータを持っています。

西川善司の3Dゲームファンのための「東京ゲームショウ2007」グラフィックス講座


問題なのは、同じ Touch Diamond や Touch Pro 系、また同じ機種名でも互換性が
ないということです。
以前作成した d3dmclock も Touch Diamond / Touch Pro 系で動作すると書きましたが、
このデータを見る限り一概には言えなくなりました。
きちんと 3D を扱えるデバイスが少ないとか バグ とか、
WindowsMobile で 3D は何かとたいへんです。


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


VisualC++ 2010 beta ではラムダ式が使えます。
無名の関数を inline で宣言することが可能で、関数オブジェクトのように
取り扱いできます。

Download Visual Studio 2010 Beta
Examples of Lambda Expressions

変数で受け取る場合は auto や template を使った汎用の入れ物が必要です。

auto f0= []( int x, int y ){ return x * y; };
std::tr1::function<int(int,int)> f1= f0;

lambda 式は無名の型宣言に相当するので、同等の型を明示的に宣言することが
できません。
名前をつけるだけなら下記の定義は出来ますが、これも f0 が有効な範囲のみです。

typedef decltype(f0) lambda_type2;
lambda_type2 f2= f0;

全く中身が同じ lambda 式を定義しても別の型と見なされるようです。

auto	f2= []( int a, int b ){ return	a * b; };
auto	f3= []( int a, int b ){ return	a * b; };
decltype(f2)*	fp2= &f3;  // error

上記の例はエラーです。
そのため定義内容を知らない外部から直接参照する手段がありません。

実際に使ってみると単純な関数ポインタでは無いことがわかります。

・lambda 式に応じて無名のクロージャー用クラスが定義される
・メンバとしてキャプチャした変数のコピーまたは参照が格納される
・クロージャークラスのメンバ関数として呼び出される。

確認してみます。

int v= 0;
auto  f4= []( int a, int b ){ return  a * b; };
auto  f5= [&]( int a, int b ){ return  a * b + v; };
auto  f6= [=]( int a, int b ){ return  a * b + v; };

sizeof(f4) == 1
sizeof(f5) == 8 // x64
sizeof(f6) == 4

sizeof(f4) が 1 なのは実質的にサイズが無いことを意味しています。
sizeof(f5) と sizeof(f6) はどちらも v を参照していますが、たまたま x64 で
コンパイルしていたので異なるサイズになりました。
ポインタ(参照)である証拠で x86 だと 4 になります。

auto	f4= []( int a, int b ){ return a * b; };
f4.operator()( 2, 10 );

operator() の呼び出しが出来ます。

int	v;
auto	f5= [&]( int a, int b ){ return a * b + v; };
int	r0= f5.v; // cannot access private member
int	r1= f5.w; // is not a member

直接アクセスはエラー。変数名はそのままメンバになってます。

lambda 式の定義が返す値はその場でキャプチャされたクロージャーそのものであるということ。
クロージャー未使用でも空の構造体が定義されること。
実行する関数はクロージャー型と静的に関連づけられており、そのために型は常にユニークとなること。
lambda 式の呼び出しは静的なものであってポインタでは無いということ
等がわかります。

実際に出力コードも追いかけて確認。

きちんとインライン展開されているし、これで定義を知らない外部からは直接アクセス
できない理由もわかりました。
外部関数呼び出しのようにインライン出来ない場合は std::tr1::function のような
関数オブジェクトが必要になるわけです。

struct func_base_2 {
    virtual int  operator()( int a, int b )= 0;
};

template<typename T>
struct func_impl_2 : public func_base_2 {
    T  exf;
    func_impl_2( T lambda_f ) : exf( lambda_f ) {}
    int	operator()( int a, int b )
    {
        return  exf( a, b );
    }
};

struct func_2 {
    func_base_2*   ptr;
    template<typename T>
    func_2( const T&& lambda_f )
    {
        ptr= new func_impl_2<T>( lambda_f );
    }
    template<typename T>
    func_2( T& lambda_f )
    {
        ptr= new func_impl_2<T&>( lambda_f );
    }
    int	operator()( int a, int b )
    {
        return	(*ptr)( a, b );
    }
    ~
};


    func_2    f7( []( int x, int y ){ return x*x + y*y; } );
    int   mm= f7( 3, 9 );

この場合の f7 は callback として持ち出せます。
使えるのはクロージャーが作られた関数スコープの中のみです。


関連エントリ
VisualStudio 2010 beta1


VisualStudio 2010 beta1 を使ってみました。
環境は Windows7 RC + Windows SDK for Windows 7 RC。

とりあえずコンパイラオプションを見ると x64 に arch オプションが増えていることがわかります。
「 /arch:AVX 」すでに intel AVX に対応している様子。

実際にコンパイルすると C5 命令が見えます。
浮動小数演算部分でちょうど SSE/SSE2 命令が AVX で置き換わっている形です。
実行するとやっぱり Illegal Instruction。

float	af= 1.3f;

C5 FA 10 05 C3 51 0E 00 vmovss      xmm0,dword ptr [__real@3fa66666 (13FD5CE2Ch)]  
C5 FA 11 44 24 74    vmovss      dword ptr [af],xmm0 

直接 AVX 命令を使うには immintrin.h を include する必要があるようです。
immintrin.h には AVX の 256bit 長のデータ型 __m256 が宣言されていました。

他に追加されたオプションは auto の扱い方です。
VC++ 2010 は auto や decltype といった宣言で型をコンパイラに判断させることができます。
キーワードの意味が変わったので /Zc:auto- で無効にできるようです。

MSDN auto Keyword

VC++ 2010 はこのように新しい言語仕様を取り入れていて、面白い機能がいろいろ
増えています。
たとえば lambda 式が使えるようになってること。

auto f_mul= []( int a, int b ) ->int { return a * b; };

宣言した無名関数をオブジェクトとして受け取れます。楽しい!
[] は capture の宣言で、クロージャーへ変数を渡す手段を記述するようです。
コピー [=] と参照 [&]、または変数個別に指定できます。
返値の型は return 文で類推しますが引数のあとの -> でも指定できます。

MSDN Lambda Expressions in C++

auto は型を自動で判別する変数宣言で、型そのものを受け取るなら decltype() が使えます。

&& 宣言は右辺値参照。代入可能な左辺値ではなく、値としての右辺値を
参照で取れるというもの。

Rvalue Reference Declarator: &&

実際に定数値でも参照可能となります。
この宣言は const でないので書き換えもできました。

int&& dd= 3;
dd++;

disassemble して生成コードを見ると、無名の auto 変数へ代入してからアドレスを
取っています。
どうやらこれが本来束縛されていないテンポラリで、右辺値参照によってこのような
無名領域へのアクセスが可能になるようです。
加算もその領域に対して行われています。

実際どのように使えばどんな効果があるのか、下記ページをかなり参考にさせて
いただきました。

ntnekの日記 和訳:Rvalue References: C++0x Features in VC10, Part 2

特に効率化は期待できるようです。
例えば C++ では一時オブジェクトが何度も生成破棄されることがありますが、
受け取った引数がすでにコピーされた一時オブジェクト(右辺値)とわかっているなら
そのまま再利用してもかまわないというわけです。
コピー元のオリジナルなら左辺値。

右辺値参照の宣言は、一時領域へのアクセス手段を手に入れると同時にこのような区別も可能になります。

非常に面白いです。
今日は本来の作業の目的を忘れてずっと VisualStudio 2010 。


久しぶりに影を扱ったのでメモです。

Direct3D 10 以降、ShaderModel 4.0 以降はハードウエアシャドウマップが当たり前に
使えるようになっているので比較的簡単になりました。
Direct3D8 ~ Direct3D9、ShaderModel 1.0 ~ 3.0 までは NVIDIA GeForce
独自の拡張機能でした。ハードウエアシャドウマップは

・DepthBuffer を直接参照するため他にバッファを用意することがないこと
・通常のサンプリングと違い、比較した結果の Bool 値 1.0 or 0.0 を返すこと
・比較結果をバイリニア補間するため、追加コストなしにハードウエアでフィルタリング可能なこと

等の利点があります。
ShaderModel 2.0 以降なら同等の機能をシェーダーで記述することができます。
旧 RADEON 向けシェーダーでは必須となりますが、追加バッファが必要でバイリニア
用サンプリングのコストがゼロにならないので完全に同じとはいえませんでした。

Direct3D 10 では標準機能の一部になったので、ShaderModel 4.0 に対応した
RADEON HD 2900XT 以降はハードウエアシャドウマップが使えます。
下位の ShaderModel でも利用可能で、RADEON HD 2900XT では
ShaderModel 3.0 を使っても GeForce 向けシェーダーがそのまま動作しました。
最初試したときは感動。

ShaderModel 4.0 以降は値を比較しながらサンプリングできる汎用機能として用意されて
いるので、GeForce 用、RADEON 用とシェーダーやプログラムを分ける必要がなくなりました。

バッファの作成

// depth buffer 用リソースの作成
CD3D11_TEXTURE2D_DESC	ddesc(
		DXGI_FORMAT_R24G8_TYPELESS,
		width,
		height,
		1,	// array
		1,	// mip
		D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_DEPTH_STENCIL,
		D3D11_USAGE_DEFAULT,
		0,	// cpu
		1,	// sample count
		0,	// quality
		0	// misc
		);

iDevice->CreateTexture2D( &ddesc, NULL, &iShadowDepth );


// Depth Buffer 用 View の作成
// レンダリング時はこちら
CD3D11_DEPTH_STENCIL_VIEW_DESC	dsdesc(
		D3D11_DSV_DIMENSION_TEXTURE2D,
		DXGI_FORMAT_D24_UNORM_S8_UINT,
		0,	// mipSlice
		0,	// firstArraySlice
		1,	// arraySize
		0	// flags
		);

iDevice->CreateDepthStencilView( iShadowDepth, &dsdesc, &iShadowDepthView );

// Shader Resource 用 View の作成
// サンプリング時はこちら
CD3D11_SHADER_RESOURCE_VIEW_DESC	sdesc(
		D3D11_SRV_DIMENSION_TEXTURE2D,
		DXGI_FORMAT_R24_UNORM_X8_TYPELESS,
		0,	// mostDetailedMip
		1,	// mipLevels
		0,	// firstArraySlice
		1	// arraySize
		);

iDevice->CreateShaderResourceView( iShadowDepth, &sdesc, &iShadowView );

使用している CD3D11_TEXTURE2D_DESC や CD3D11_DEPTH_STENCIL_VIEW_DESC
等は DirectX SDK March 2009 以降使えるようになった新機能です。
各種構造体を初期化してくれるヘルパーで、D3D11.h で定義されています。
ヘッダを見て発見しました。

サンプラーの作成

CD3D11_SAMPLER_DESC	desc( D3D11_DEFAULT );

desc.AddressU= D3D11_TEXTURE_ADDRESS_CLAMP;
desc.AddressV= D3D11_TEXTURE_ADDRESS_CLAMP;
desc.AddressW= D3D11_TEXTURE_ADDRESS_CLAMP;

desc.Filter= D3D11_FILTER_COMPARISON_MIN_MAG_LINEAR_MIP_POINT;
desc.ComparisonFunc= D3D11_COMPARISON_LESS;

iDevice->CreateSamplerState( &desc, &iShadowSampler );

比較結果のフィルタリングなので、D3D11_FILTER_COMPARISON_~ と
COMPARISON 付きのフィルタを指定します。
CD3D11_SAMPLER_DESC に渡している D3D11_DEFAULT は、デフォルト値で構造体を
初期化することを意味しています。

クリアと RenderTarget の設定

void __fastcall
ShadowBuffer::Clear( ID3D11DeviceContext* iContext )
{
	iContext->ClearDepthStencilView( iShadowDepthView,
		D3D11_CLEAR_DEPTH|D3D11_CLEAR_STENCIL, DVF(1), 0 );
}

void __fastcall
ShadowBuffer::SetRenderTarget( ID3D11DeviceContext* iContext )
{
	ID3D11RenderTargetView*	irtv= NULL;
	iContext->OMSetRenderTargets( 1, &irtv, iShadowDepthView );

	CD3D11_VIEWPORT	view( DVF(0), DVF(0), ShadowWidth, ShadowHeight );
	iContext->RSSetViewports( 1, &view );
}

ShadowBuffer にレンダリングする場合の前処理です。
OMSetRenderTargets() では DepthBuffer のみ指定します。
RenderTarget が NULL の場合書き込みが行われません。

リソースの設定

void __fastcall
ShadowBuffer::SetResources( ID3D11DeviceContext* iContext )
{
	iContext->PSSetShaderResources( SHADOW_REGISTER_ID, 1, &iShadowView );
	iContext->PSSetSamplers( SHADOW_REGISTER_ID, 1, &iShadowSampler );
}

作成した ShadowBuffer を用いて影描画する場合の前設定です。
シェーダーとのバインドを簡略化するために、ShadowBuffer 用のレジスタ番号を
決めうちにしています。

シェーダー

struct VS_INPUT {
	float3	vPos	: POSITION;
	float3	vNormal	: NORMAL;
	float2	vTex0	: TEXCOORD;
#if CF_WEIGHT
	float4	vWeight	: WEIGHT;
	uint4	vIndex	: INDEX;
#endif
};

struct VS_OUTPUT {
	float4	vPos	: SV_Position;
	float2	vTex0	: TEXCOORD;
};

VS_OUTPUT vmain( VS_INPUT idata )
{
	VS_OUTPUT	odata;
	float4x4	world;

#if CF_WEIGHT
	world=  World[idata.vIndex.x] * idata.vWeight.x;
	world+= World[idata.vIndex.y] * idata.vWeight.y;
	world+= World[idata.vIndex.z] * idata.vWeight.z;
	world+= World[idata.vIndex.w] * idata.vWeight.w;
#else
	world= World[0];
#endif

	float4	wpos= mul( float4( idata.vPos.xyz, 1 ), world );
	odata.vPos= mul( wpos, PV );
	odata.vTex0= idata.vTex0;

	return	odata;
}

typedef VS_OUTPUT	PS_INPUT;

Texture2D	tex0		: register( t0 );
SamplerState	sample0		: register( s0 );

float pmain( PS_INPUT idata ) : SV_Target
{
#if CF_ALPHA1BIT
	float	alpha= tex0.Sample( sample0, idata.vTex0.xy ).w;
	if( alpha < 0.8f ){
		clip( -1 );
	}
#endif
	return	0;
}

ShadowBuffer を生成する場合は特別なことが何もありません。
カラーを書かないので不要に見えますが、テクスチャの Alpah 抜きに対応する場合は
テクスチャも読み込まなければならなくなります。

今回は特別なことを何もしてないので大丈夫ですが、影の合成を効率化しようと
凝ったことをし出すとテクスチャの扱いが結構負担になってきます。
パス毎にフィルタリングや Mip 指定、uv の計算等を厳密にあわせておかないと、
ちょっとしたレンダーステートの差がテクスチャの隙間を生み出してしまうからです。
例えば DepthBuffer の事前生成など。

描画用シェーダー

struct VS_INPUT {
	float3	vPos	: POSITION;
	float3	vNormal	: NORMAL;
	float2	vTex0	: TEXCOORD;
#if CF_WEIGHT
	float4	vWeight	: WEIGHT;
	uint4	vIndex	: INDEX;
#endif
};

struct VS_OUTPUT {
	float4	vPos	: SV_Position;
	float3	vNormal	: NORMAL;
	float2	vTex0	: TEXCOORD;
	float4	vShadow	: SHADOW;
};

VS_OUTPUT vmain( VS_INPUT idata )
{
	VS_OUTPUT	odata;
	float4x4	world;

#if CF_WEIGHT
	world=  World[idata.vIndex.x] * idata.vWeight.x;
	world+= World[idata.vIndex.y] * idata.vWeight.y;
	world+= World[idata.vIndex.z] * idata.vWeight.z;
	world+= World[idata.vIndex.w] * idata.vWeight.w;
#else
	world= World[0];
#endif

	float4	wpos= mul( float4( idata.vPos.xyz, 1 ), world );
	odata.vPos= mul( wpos, PV );
	odata.vNormal.xyz= mul( idata.vNormal, (float3x3)world );
	odata.vTex0= idata.vTex0;

	odata.vShadow= mul( wpos, ShadowMatrix );

	return	odata;
}

typedef VS_OUTPUT	PS_INPUT;

Texture2D	tex0		: register( t0 );
SamplerState	sample0		: register( s0 );

Texture2D		shadowTex0	: register( t8 );
SamplerComparisonState	shadowSample0	: register( s8 );

float4 pmain( PS_INPUT idata ) : SV_Target
{
	float3	diffuse= Ambient.xyz;
	float3	specular= float3( 0, 0, 0 );
	float4	color= float4( 0, 0, 0, 0 );

	float3	sh_uv= idata.vShadow.xyz;
	sh_uv.xyz/= idata.vShadow.w;
	sh_uv.x=  sh_uv.x * 0.5f + 0.5f;
	sh_uv.y= -sh_uv.y * 0.5f + 0.5f;
	sh_uv.z-= 0.000004f; // 適当

	float	shdepth= shadowTex0.SampleCmpLevelZero( shadowSample0, sh_uv.xy, sh_uv.z );
//	float	shdepth= shadow_pcf( sh_uv );
	lightMask= saturate( shdepth + 0.0f );

	float3	normal= normalize( idata.vNormal.xyz );
	diffuse.xyz+= saturate( dot( normal, _LightDir ) ) * Diffuse.xyz * lightMask;
	specular.xyz+= pow( saturate( dot( normal,
		normalize(idata.vEyeVec + _LightDir ) )), Specular.w )
				* Specular.xyz * lightMask;

	float4	texcol= tex0.Sample( sample0, idata.vTex0.xy );
	color.xyz+= texcol.xyz * diffuse.xyz + specular.xyz;
	color.w= texcol.w * Ambient.w;

#if CF_ALPHA1BIT
	if( color.w < 0.8f ){
		clip( -1 );
	}
#endif
	return	color;
}

VertexShader では描画用の Matrix と影用の Matrix、2つの座標系へ変換します。
実際の PixelShader は非常に長いので光源演算を大幅に削りました。

影のサンプリングは SampleCmpLevelZero() を使っています。
Depth を読み込み、指定した z 値と比較した結果をフィルタリングして返すので、
対応する光源に対して影の中かどうかわかります。
影用 uv への変換は効率化するなら Matrix に埋め込んでおくことができます。
埋め込むと影を落とすときと完全に同じ Matrix にならないことが難点。

基本的な描画手順

// Shadow Buffer の作成
ShadowBuffer->Clear( iContext );
ShadowBuffer->SetRenderTarget( iContext );
iContext->OMSetDepthStencilState( iDSS_ZEnable, 0 );

 // shadow レンダリング用 matrix の設定など
 // shadow buffer 生成のシェーダーで描画


// 影をサンプリングしながらレンダリング
 // 本来の RenderTarget の設定
 // レンダリング用 matrix の設定
ShadowBuffer->SetResources();

 // 描画用シェーダーで描画

影、不透明、半透明 といった順番になります。
DepthBuffer の事前生成を行う場合や、複数の光源の影を落とす場合は描画パスが増加していきます。
alpha 抜きを使う場合は半透明と同じように、さらに別グループに分類した方が良いかもしれません。

関連エントリ
DirectX SDK March 2009
Direct3D 10 DXGI_FORMAT の機能対応一覧
D3D10/DX10 D3D10_FILTER の新機能
D3D10/DX10 RADEON HD2900XT


SHARP メビウスのモニターに当選してしまいました。

募集がすぐ終わったとか倍率高いとか新しいセンサーを使っているとか、
ちょうどその話をしていた直後だったので目を疑いました。
なにせ「募集時点ではスペックや詳細が未発表だったので、自分はもっと小型の
デバイスを期待してた・・」とかその場で興味なさそうに発言してた本人が
当たってしまったのだから尚更です。

すでに発売されているようですが、モニター自体は来週以降なので少々待たされます。

SHARP Mebius PC-NJ70A

スペック的にはほぼ一般的な Atom N270 PC です。
特徴的なのは独自のタッチパッド。
液晶画面がついているだけでなくセンサーも兼ねた代物で、画素一つ一つが光を
読み取っているようです。
この新しいデバイス&センサーは使ってみたいところです。

もう1つ興味あるのは、タッチパッド部の液晶も結構解像度が高いこと。
854x480 ドットというと初代 EeePC 701 とほぼ同じ広さな訳ですから
この中でデスクトップやらブラウザやら普通に動かしていたことになります。
タッチパネル部のサブ画面をウィンドウズでどこまで自由に使えるかわかりませんが、
画面を広くするためのひとつの方法かもしれません。

思い返すと初めて買ったノート PC がSHARP メビウス ワイド PC-W100 でした。
メビウスワイドの画面は偶然にも同じ 1024x600 ドットです。

ネットブックが出てから 1024x600 ドットはごく普通のありふれたサイズに
なりましたが、13年前の当時は大変珍しいものだったのです。
横をのばしたというよりも 1024x768 から縦を削ってキーボードを圧迫せずに
本体サイズを小さくしようとしたもの。

当時のスペックをあらためて見ると Windows95 で RAM は 16MByte に HDD 1GByte。
CPU も Pentium 133MHz。さすがに 13年の年月を感じます。
これでも当時は DirectX3~5 を入れて Direct3D の RampDriver でプログラムを書いていました。

メビウスワイドから今まで、この間に使ってきたノート PC は
1024x600~1024x768 が圧倒的に多かったことに気がつきます。
記憶容量も演算速度もバス速度も描画速度も著しく向上しているはずなのに、
意外なほど長期間にわたって小型ノート PC の解像度が変化しませんでした。

もちろん最近は LOOX U の 1280x800 や VAIO type P の 1600x768 のように、
より詳細な液晶画面を搭載するものも出てきています。

PC-NJ70A を見て思ったのは、高解像度化に消極的なメインの液晶画面の代わりに
サブモニタを増やしてマルチ画面化するのも小型ノートパソコンの方向の 1つとして
ありなのかもしれないということ。
CPU が Multi core 化するように。

あとはサブ画面をどこまで自由に使えるか。
もしかしたら Windows SideShow でしょうか。
無理かもしれないけど理想はデスクトップの延長として使えることです。
Windows7 だと multi touch API が整備されているので、もしドライバがあるなら
すぐにでも使いたいところです。
現在 Multitouch 対応 PC として HP TouchSmart PC IQ800 を触っていますが
外周にセンサーを配置した光学式で 2点まで同時に識別することができます。
PC-NJ70A ではもっと自由に点を取れたらいいですね。

期待点まとめ
・タッチパッドとして
・画面の拡張、サブ画面として
・マルチタッチ対応センサー付きモニタとして
・別の入力手段として、位置的に親指シフトキーを配置できたりするかも


関連エントリ
Windows7 とマルチタッチ / HP TouchSmart PC IQ800


DXGI1.1 のアダプタ列挙を試します。

●EeePC901-X で実行した場合 (Windows7 RC x86)

// EeePC 901-X
[Mobile Intel(R) 945 Express Chipset Family (Microsoft Corporation - WDDM 1.0)]
   vmem:0MB sysmem:64MB shmem:192MB
   flag=0 

945 内蔵 GPU が列挙できています。
実際に Direct3D10 / 11 デバイスを作るとエラーなので、ドライバがまだ D3D10 Level9
に対応していないことがわかります。


●デスクトップ PC で実行した場合 (Windows7 RC x64)

// Desktop PC
[ATI Radeon HD 4800 Series ]
   vmem:504MB sysmem:0MB shmem:2811MB
   flag=0 
  (Direct3D11)
   *HARDWARE  = 10.1 (a100)
[ATI Radeon HD 4670]
   vmem:504MB sysmem:0MB shmem:2811MB
   flag=0 
  (Direct3D11)
   *HARDWARE  = 10.1 (a100)

2枚差したビデオカードが両方とも列挙されています。
それぞれ 10.1 でデバイスの作成ができます。


●リモートデスクトップ経由で実行した場合

[Mobile Intel(R) 945 Express Chipset Family (Microsoft Corporation - WDDM 1.0)]
   vmem:0MB sysmem:64MB shmem:192MB
   flag=1 REMOTE
[ATI Radeon HD 4800 Series ]
   vmem:504MB sysmem:0MB shmem:2811MB
   flag=0 
  (Direct3D11)
   *HARDWARE  = 10.1 (a100)
[ATI Radeon HD 4670]
   vmem:504MB sysmem:0MB shmem:2811MB
   flag=0 
  (Direct3D11)
   *HARDWARE  = 10.1 (a100)

EeePC 901 からデスクトップ PC に接続しています。

予想通り両方のマシン、すべてのアダプタが見えていることがわかります。
EeePC の 945 では DXGI_ADAPTER_DESC1 の Flags に DXGI_ADAPTER_FLAG_REMOTE bit が立っており、このデバイスを区別することが可能です。
プログラム自体はホスト側のデスクトップ PC で走っているので、プログラムから見れば
クライアントである EeePC の方がリモートです。

もし Remote のアダプタが Command Remoting に対応していれば
ネットワークを経由していてもクライアント側の PC でリアルタイムに
レンダリングすることが可能となります。

ちなみに DXGI1.0 で列挙するとホスト側の最初のアダプタ "RADEON HD4850" しか
見えませんでした。


● Feature

各アダプタの詳細を調べると下記の通り。
いわば Direct3D11 における caps です。

[DEFAULT]
  (Direct3D11)
   *HARDWARE  = 10.1 (a100)
     feature threading  DriverConcurrentCreates=0
     feature threading  DriverCommandLists=0
     feature d3d10_x ComputeShaders_Plus_RawAndStructuredBuffers_Via_Shader_4_x=0
     feature doubles  DoublePrecisionFloatShaderOps=0
   *REFERENCE = 11.0 (b000)
     feature threading  DriverConcurrentCreates=0
     feature threading  DriverCommandLists=0
     feature d3d10_x ComputeShaders_Plus_RawAndStructuredBuffers_Via_Shader_4_x=1
     feature doubles  DoublePrecisionFloatShaderOps=1
   *WARP      = 10.1 (a100)
     feature threading  DriverConcurrentCreates=0
     feature threading  DriverCommandLists=0
     feature d3d10_x ComputeShaders_Plus_RawAndStructuredBuffers_Via_Shader_4_x=0
     feature doubles  DoublePrecisionFloatShaderOps=0

[ATI Radeon HD 4800 Series ] (V:4098 D:37954 S:84021250 R:0)
   luid:000097b2 00000000
   vmem:504MB sysmem:0MB shmem:2811MB
   flag=0 
  (Direct3D11)
   *HARDWARE  = 10.1 (a100)
     feature threading  DriverConcurrentCreates=0
     feature threading  DriverCommandLists=0
     feature d3d10_x ComputeShaders_Plus_RawAndStructuredBuffers_Via_Shader_4_x=0
     feature doubles  DoublePrecisionFloatShaderOps=0

[ATI Radeon HD 4670] (V:4098 D:38032 S:625086466 R:0)
   luid:0000b18e 00000000
   vmem:504MB sysmem:0MB shmem:2811MB
   flag=0 
  (Direct3D11)
   *HARDWARE  = 10.1 (a100)
     feature threading  DriverConcurrentCreates=0
     feature threading  DriverCommandLists=0
     feature d3d10_x ComputeShaders_Plus_RawAndStructuredBuffers_Via_Shader_4_x=0
     feature doubles  DoublePrecisionFloatShaderOps=0

以前 DirectX SDK Nov2008 時点で調べた結果はこちら
ほとんどのオプションは 0 のままですが、よく見ると REFERENCE で
DoublePrecisionFloatShaderOps が 1 になっていました。
REFERENCE を使えばシェーダーの double 演算命令もテストできるようになったということです。

同記事 でも触れていますが ComputeShader 4.0/4.1 については November の時点ですでに対応していますね。


関連エントリ
Direct3D DXGI とマルチ GPU (アダプタ)
Windows7 リモートデスクトップと Direct3D
Direct3D11/DirectX11 (4) FeatureLevel と旧 GPU の互換性、テクスチャ形式など



PC を入れ替えて複数のビデオカードを同時に使えるようになりました。
対応マザーとそれなりの電源が必要です。
いろいろ試しています。

マルチ GPU といっても SLI や CrossFire のことではなく、
それぞれ独立した GPU として使います。

例えば GeForce と RADEON の 2枚差しで、それぞれにモニタをつないで
マルチモニタのように使用することができます。

・GeForce GTX260 → モニタ1
・RADEON HD4670 → モニタ2

GeForce は モニタ1 の描画を行い、RADEON は モニタ2 へ出力を行っています。
Windows から使っている分には 1枚のビデオカードに複数モニタをつないだ状態と
何も変わりません。
広いデスクトップとして使用可能で双方にまたがったウィンドウも配置できます。


ここまでは理解できますが Direct3D を使った場合はどうなるでしょうか。
D3D10CreateDevice / D3D11CreateDevice 時に PC につながっている任意のアダプタ
(GPU)を与えることが可能です。
どちらの GPU でも同じように使えるし、どちらのモニタにも描画することができました。
行き来もできるし中間に配置しても動いています。

このあたりをコントロールするのが DXGI で、非常に柔軟な使い方が可能となっています。

・IDXGIAdapter = GPU
・IDXGIOutput = モニタ

現在利用可能なアダプタとモニタは

IDXGIFactory::EnumAdapters()
IDXGIAdapter::EnumOutputs()

で列挙可能です。モニタに接続されていないアダプタも描画に使うことができます。
DXGI1.1 ではこれにさらに Command Remoting が加わります。
リモートデスクトップでアクセスしている場合に、ホストとクライアントどちらの
アダプタでレンダリングするか選択できるわけです。


●サンプルで確認

DirectX SDK 付属の Direct3D 11 のサンプルプログラムを起動するとウィンドウに
アダプタ名 (GPU名) が表示されています。
モニタ間 (アダプタ間) を行き来するとアダプタ名が切り替わるので違いがよくわかります。
プログラム的にはわざわざアダプタを切り替える必要は無いのですが、
DXUT では敢えてこのような仕様になっているようです。(理由は後述)


●プログラムで確認

実際にプログラムを書いて任意のアダプタで動かしてみました。

・RADEON HD4850 → モニタ1
・RADEON HD4670 → モニタ2

IDXGIFactory::EnumAdapters() でアダプタを列挙して、任意のアダプタを使って
デバイスを作成します。

// 列挙
IDXGIAdapter1*	iAdapter= NULL;
IDXGIFactory1*	iFactory= NULL;
CreateDXGIFactory1( __uuidof(IDXGIFactory1), reinterpret_cast( &iFactory ) );
for( unsigned int index= 0 ;; index++ ){
	HRESULT	ret= iFactory->EnumAdapters1( index, &iAdapter );
	if( ret == DXGI_ERROR_NOT_FOUND ){
		break;
	}
	// ~ アダプタの選択
	// iAdapter->Release();
}
iFactory->Release();


// 作成
HRESULT	hr= D3D11CreateDeviceAndSwapChain(
		iAdapter,
		iAdapter ? D3D_DRIVER_TYPE_UNKNOWN : DriverType,
		NULL,	// software
		D3D11_CREATE_DEVICE_DEBUG,// flags
		NULL,	// featurelevels
		0,	// featurelevels
		D3D11_SDK_VERSION,
		&SwapChainDesc,
		&iSwapChain,
		&iDevice,
		&FeatureLevel,
		&iContext
	);

アダプタは IDXGIFactory::EnumAdapters() で列挙したハードウエアアダプタ、
もしくは IDXGIFactory::CreateSoftwareAdapter() で作成したソフトウエアアダプタです。
すでに TYPE を特定できるので、DriverType には D3D_DRIVER_TYPE_UNKNOWN を
与えなければなりません。(最初ここではまりました)

結果
RADEON HD4850 → モニタ1  : 257fps
RADEON HD4850 → モニタ2  : 205fps

RADEON HD4670 → モニタ1  : 140fps
RADEON HD4670 → モニタ2  : 164fps

HD4850 ではモニタ1 の方が高速、HD4670 ではモニタ2 の方が高速です。
アダプタから直接出力した方が速く、予想通りの結果といえます。
同時にたとえ直結されていなくても、モニタ2 の描画も HD4850 が行った方が速いこともわかります。

DirectX SDK 付属サンプルの DXUT がウィンドウの位置を監視して、モニタに応じて
デバイスを作り直しているのは少しでも高速に動作するためだと考えられます。

昔はビデオカードを何度も何度も差し直して開発していました。
GeForce と RADEON の挙動を同時に確認できるなんて、大変便利になったものです。
上のテストの最中、途中で GeForce GTX260(192sp) から HD4850 に差し直したのは
本日非常に暑かったからです。


関連エントリ
Windows7 リモートデスクトップと Direct3D


Windows7 RC と同時に WindowsSDK の RC 版も出ています。

Microsoft Windows SDK for Windows 7 and .NET Framework 3.5 SP1: RC
Microsoft Windows SDK for Windows 7 and .NET Framework 3.5 SP1: RC (ISO)

BETA 時点からタッチ周りの API など変更されているところがあるので要注意です。
たとえば WM_TOUCHDOWN, WM_TOUCHUP, WM_TOUCHMOVE 等は無くなり
WM_TOUCH に統一されているようです。

マニュアルも更新されています。
以前 User Interface の下にあった Touch 関連が Windows Touch
という新項目にまとめられています。このあたり力が入ってます。

MSDN Windows Touch

Direct3D11 関連も DirectX SDK March 2009 より新しいものが含まれていました。
ただし d3d11.lib などの lib 名に _beta がついていないので include の順番に
注意した方が良さそうです。
Vista で使う場合は DXSDK の方を。


関連エントリ
Windows7 Multitouch API その(2) WM_GESTURE 系
Windows7 Multitouch API
DirectX SDK March 2009


Windows7 RC を入れた type P はAero がそれなりに動いているので印象が変わりました。
現在透明感 off で使用しています。

・個人設定→ウィンドウの色→「透明感を有効にする」のチェックを外す

半透明ではないもののウィンドウの重なりがハードウエア描画なので速いです。
たとえばウィンドウ移動とか最小化のアニメーションとかそこそこスムーズに
できますし(たまにかくつくけど)、タスクバーにマウスカーソルを乗せると
ウィンドウの縮小イメージもポップアップで出ます。
ウィンドウの中の描画速度は変わらないけど、操作の印象は良くなりました。

たまに背景の描画が追いつかないのか真っ黒に崩れることがあります。
フレームバッファが消えてしまった状態ですが、以前から同じ症状があった気がするので
また違う問題かもしれません。


Aero が動くということは D3D も動くようになったということ。
GMA500 がどの Feature Level まで対応しているのか Direct3D11 で試してみました。
結果は残念ながら FEATURE_LEVEL_9_1 ~ 3 も未対応。
ハードウエアでは動作しませんでした。

Direct3D11 サンプルや一部のアプリケーションが動くのは、ソフトウエアレンダラ
WARP Device を呼び出しているからです。
D3D11CreateDevice~() の D3D_DRIVER_TYPE_HARDWARE でエラーが出た場合、
D3D_DRIVER_TYPE_WARP できちんと作り直しているアプリは動きます。
D3D11 サンプルが動いて D3D10 が動かないのはおそらくアプリが WARP に対応していないから。
D3D10 LEVEL_9_1 ~ に対応していないということは Direct2D を使う場合もおそらく
CPU レンダラになります。
ハードウエア対応は今後に期待。現状未対応でもとりあえず動くのは WARP のおかげです。

●結論
GPU は Direct3D 9 3.0 まで対応しているものの、
現状では Direct3D10/11 の下位モードの Level9 でも動作せず。
WARP なら動く。


関連エントリ
VAIO Type P の Windows7 RC は Aero が有効


2009/05/10
Atom vs Core i7

コンパイル時間

(1) Atom Z540    11分44秒 (704秒)
(2) Core i7 920      37秒

(1) 詳細
Atom Z540 1.86GHz 1core 2threads
RAM 2GB, SSD 64GB (VAIO type P), AC電源
Windows7 RC x86, Aero ON
VC2008 cl /MP4 (4thread指定)

(2) 詳細
Core i7 920 2.66GHz 4core 8threads
RAM 6GB, HDD 1TB
Windows7 RC x64
VC2008 cl /MP10 (10thread指定)

実行したのはライブラリのコンパイルでコマンドラインから /MP スイッチ付き。
x86 debug, x86 release, x64 debug, x64 release の 4種を全部作成。
同じファイルを何度も読む+コンパイル自体数回実行したので
RAM が多ければソースはキャッシュに乗ってる状態です。
ただし同時に 8 file 未満のコンパイルが行われることもあるので CPU は 100% 埋まっていません。
スレッドがすべて埋まる条件なら core 数の多い Core i7 とはもっと速度差が開くと思います。
おそらくもっと速いでしょう。

仮に Atom が 4core で、ストレージとバスの影響を無視して 4倍速になると仮定しても 176 秒。
さらに Atom が同じ 2.66GHz で動いたとしても 123 秒。
多めに見積もってもまだ 3.3 倍開きがあります。


超多忙が和らいだので VAIO type P に Windows7 RC を入れてみました。
BETA と同様クリーンインストールです。ドライバはほぼ自動で入ります。
一つだけ不明なデバイスがありましたが、おそらく Fn キー周りと思われます。


(1) 準備
 Windows7 RC x86 DVD
 USB 接続の DVD ドライブ
 VAIO typeP のプリインストール Vista から取り出しておいたドライバフォルダ
   C:\Windows\drivers の下

(2) インストール
 外付けドライブから起動したあと 64GB SSD を全部消してクリーンインストールです。

(3) ネットワーク設定を行い自動更新します
 推奨にもチェック。ドライバが入ります。

(4) 不明なドライバを入れます
 デバイスマネージャーを開いて不明なデバイスのドライバを検索
 (1) で保存しておいたフォルダを指定すれば勝手に入ります。

(5) Wireless LAN ドライバの入れ替え
 そのままでも無線 LAN ドライバが入りますが 150Mbps 接続だったので入れ替えました。
 300Mbps 接続になりました。
 デバイスマネージャーからネットワークアダプター
  → Atheros~ の上で右ボタンからドライバーソフトウエアの更新
  → コンピュータを参照して~を選択
  → 自動検索だとすでに最新といわれるので、下の
    「コンピューター上のデバイスドライバーの一覧から選択します」を選ぶ
  → ディスクの使用ボタン → 参照
  → (1) で保存したフォルダから 「Wireless LAN Driver (Atheros)」を選択して OK
  → そのまま次へ、でドライバが入る

(6) エクスペリエンスインデックスの計測
 いろいろ設定する前に「コンピューター」のプロパティから
 Windows エクスペリエンスインデックスの評価を実行しておきます。
 少々時間がかかりましたが、評価が終わったら Aero Glass が有効になっていました。

プロセッサ          1.2  (Z540)
メモリ               4.1
グラフィックス(Aero) 2.9
グラフィックス(Game) 2.4
ハードディスク       5.9  (SSD 64GB)


(7) 画面の輝度調整

 BETA 同様下記ソフトで代用。ボリューム調整は何もしなくてもできます。
backlightwin 1.00


● Windows7 RC 描画

ドライバの不具合が無くなったのか DWM も動いており、起動時のエラーダイアログも無くなっています。
これで動作確認のために Vista を残しておかなくてもよくなりそうです。
Aero Glass も動くようになりました。Vista 上で使うより多少ましに見えます。
透明感の設定を外すと少々軽くなります。

Direct3D11 サンプルもいつもの MultithreadRendering11 は一応動きました。
動作は 0.14fps、リファレンスと間違えるほど重いです。
Feature Level が 10.1 なので調べるとソフトウエアレンダラの WARP Device が使われていました。
Direct3D9 はきちんと GPU で動いています。
この辺はあとでもう少し詳しく調べてみます。


● Windows7 RC 全般

スタートメニューの個人用フォルダーが Vista 同様の個人フォルダに戻りました。
ライブラリはメニューとして分離しています。個人的にはこれが一番うれしい変更です。
BETA ではタスクバーに desktop のツールバーを置いて代用してました。

その代わり多用していた新規作成のショートカットが W から X に変更されてしまいました。
これ今までも何度も変更されているのであきらめています。

マイコンピュータのプロパティ(システム) にこんな表示が
「Pen and Touch: No Pen or Touch Input is available for this Display」


関連エントリ
VAIO type P Windows7 beta とバックライト調整など
Windows ノート PC の backlight の明るさ変更方法


Windows7 のマルチタッチ系イベントには二通りあることを前回説明しました。
高度な方、WM_GESTURE 系についてもう少し詳しくさわってみます。

メッセージは WM_MOUSEMOVE のように座標の更新毎に多数送られてきます。
このとき一つの操作の組が GID_BEGIN と GID_END で囲まれます。
例えば PAN 操作の場合

GID_BEGIN
GID_PAN
GID_PAN
GID_PAN
GID_PAN
...
GID_END

といった感じに。
また別の操作が認識されれば、それらが GID_BEGIN~GID_END で囲まれます。
マウスでいえば LBUTTONDOWN / LBUTTONUP のようなもの。
タッチだと WM_TOUCHDOWN / WM_TOUCHUP でしょう。
開始と終了が明確にわかるようになっています。

ただ上の例だと最初と最後の GID_PAN 自身にも GF_BEGIN, GF_END フラグが
追加されているので、実は GID_BEGIN/GID_END を無視しても何も困りません。
フラグ値を追加して書き直してみます。

GID_BEGIN
GID_PAN  GF_BEGIN
GID_PAN
GID_PAN
...
GID_PAN
GID_PAN  GF_INERTIA
...
GID_PAN  GF_INERTIA
GID_PAN  GF_INERTIA|GF_END
GID_END

GF_INERTIA は PAN 操作のみ追加される慣性で、操作後もしばらくメッセージが
送られてきます。

前回も書きましたがマニュアルにはまだ間違いが含まれているので、仕様が確定
するまではヘッダファイルを見た方が確実です。ヘッダは WinUser.h です。

WM_GESTURE が送ってくる座標値はスクリーン座標です。
GESTUREINFO 構造体には対象となるウィンドウのハンドルが含まれていますが、
ウィンドウ内の座標系に変換する場合は自分で行う必要があります。

一本指のシングルタッチなどはマウスと同じ扱いなので、マウス系のメッセージを
処理する必要があります。よってフリック操作はマウス扱いです。

パンや回転、ズーム(2点間の距離)などの操作は結構誤動作というかノイズが入るようです。
極端に異なる値が来たときは無視するような処理が必要になるかもしれません。
また GID_ROTATE の最初のメッセージには回転値として不正な値が入っているようです。
GID_ROTATE かつ GF_BEGIN が立っている場合、ullArguments の下位 16bit を 0 と
見なした方がよいです。
回転しようとすると必ず反転してしまうおかしな症状に最初悩みました。

用途によるとは思いますが、やはり凝ったことを行いたい場合は WM_GESTURE ではなく
WM_TOUCH 系 (WM_TOUCHUP/WM_TOUCHDOWN/WM_TOUCHMOVE) を
使った方が良さそうです。
WM_GESTURE の場合操作判定が先に行われてしまうので、操作の位置を制限することが
難しくなります。
例えば特定のオブジェクトの上だけで回転やズームを行い、それ以外の座標では
常にパン操作と判定したい場合などあまりきれいに操作を区別することができません。

最初は簡単に見えたものの、実際にやってみるとまだまだ。
プログラムの方もいろいろと工夫が必要になりそうです。


関連エントリ
Windows7 Multitouch API


タッチ関連の API は 2種類あります。
WM_TOUCH 系と WM_GESTURE 系

WM_TOUCH 系の方が低レベルで、直接複数のタッチ座標を取り出すことが出来ます。
WM_GESTURE の方はいくつかの決まった操作を容易に受け取ることができます。

WM_TOUCH 系のメッセージを有効にするには RegisterTouchWindow() を呼び出します。
これを実行しておかないと WM_TOUCH~ が送られて来ません。

データを受け取るのは簡単です。

WM_TOUCHDOWN
WM_TOUCHUP
WM_TOUCHMOVE

などマウスとよく似ているメッセージが来るので、さらに
GetTouchInputInfo() を使って詳細な情報を読み取ります。


起動時の判定

// ハードがマルチタッチをサポートしているかどうか
int  value= ~GetSystemMetrics( SM_DIGITIZER );
if( !(value & 0xc0) ){
    RegisterTouchWindow( hwnd, 0 );
}

メッセージ

    case WM_TOUCHDOWN:
    case WM_TOUCHUP:
    case WM_TOUCHMOVE:
	WM_Touch( mes, wparam, lparam );
	return	FALSE;

読み出しの例

void WM_Touch( UINT mes, WPARAM wparam, LPARAM lparam )
{
    int	inputs= LOWORD( wparam );
    TOUCHINPUT	tbuf[TOUCHMAX];
    HANDLE	hinput= reinterpret_cast<HANDLE>( lparam );
    if( GetTouchInputInfo( hinput, inputs, tbuf, sizeof(TOUCHINPUT) ) ){
	TOUCHINPUT*	tp= tbuf;
	for( int i= 0 ; i< inputs ; i++, tp++ ){
	    ...
	}
    }
    CloseTouchInputHandle( hinput );
}

座標値そのままなので、これを元にズームや回転などの操作を検出するには
さらに一手間いります。


WM_GESTURE 系はそのあたりを簡単にしてくれます。
下記の操作が定義されています。

GID_ZOOM
GID_PAN
GID_ROTATE
GID_TWOFINGERTAP
GID_ROLLOVER

これらのメッセージは RegisterTouchWindow() を実行すると来なくなるので
WM_GESTURE 系を使う場合は RegisterTouchWindow() を実行してはいけません。

WM_GESTURE が送られてきたらさらに追加情報を受け取ります。

    case WM_GESTURE:
	WM_Gesture( mes, wparam, lparam );
	return	FALSE;


void WM_Gesture( UINT mes, WPARAM wparam, LPARAM lparam )
{
    GESTUREINFO	ginfo;
    memset( &ginfo, 0, sizeof(GESTUREINFO) );
    ginfo.cbSize= sizeof(GESTUREINFO);
    HGESTUREINFO	hgesture= reinterpret_cast<HGESTUREINFO>( lparam );

    if( GetGestureInfo( hgesture, &ginfo ) ){
        ...
    }
    CloseGestureInfoHandle( hgesture );
}

WM_GESTURE 系の仕様は若干変更があったようで、資料によっては記載内容が
異なっていることがあります。例えば今でも

MSDN WM_GESTURE Message

このページにある dwCommand は存在しておらず GESTUREINFO 構造体の
dwID のことだと思われます。
同じように dwArgument も 64bit の ullArguments に変更されているようです。

・dwCommand → GESTUREINFO dwID
・dwArgument → GESTUREINFO ullArguments
・lParam → GESTUREINFO ptsLocation

その他いくつか気が付いた点。

初期状態では GID_ROTATE などのメッセージが来ませんでした。
SetGestureConfig() を使って送って欲しいメッセージの登録が出来るようです。

const int	ConfigCount= 5;
GESTURECONFIG	config[ConfigCount];
memset( config, 0, sizeof(GESTURECONFIG) * ConfigCount );

config[0].dwID= GID_ZOOM;
config[0].dwWant= GC_ZOOM;

config[1].dwID= GID_PAN;
config[1].dwWant= GC_PAN_WITH_SINGLE_FINGER_VERTICALLY
	|GC_PAN_WITH_SINGLE_FINGER_HORIZONTALLY
	|GC_PAN_WITH_GUTTER
	|GC_PAN_WITH_INERTIA;

config[2].dwID= GID_ROTATE;
config[2].dwWant= GC_ROTATE;

config[3].dwID= GID_TWOFINGERTAP;
config[3].dwWant= GC_TWOFINGERTAP;

config[4].dwID= GID_ROLLOVER;
config[4].dwWant= GC_ROLLOVER;

SetGestureConfig( hwnd, 0, 5, config, sizeof(GESTURECONFIG) );

これで全部のメッセージとオプションが有効になるはずです。

座標は ptsLocation に、追加パラメータは ullArguments に入ります。
GID_PAN + GF_INERTIA フラグが ON の場合は、ullArguments の上位 32bit に
慣性のパラメータが格納されているようです。
GID_INERTIA というのは存在していません。

なお、これらの動作を確認するには

・Windows7 Beta
・Windows SDK for Windows 7 BETA
・MultiTouch 対応 PC

が必要です。Vista では起動時に DLL の互換性が無くエラーになります。
HP の TouchSmart PC IQ800 を使いました。

Windows7 SDK の設定には少々注意が必要です。
新しい DirectX SDK もリリースされているので、DirectX SDK March 2009 を
併用する場合は特に。基本的にあとからリリースされた方を上にします。

・DirectX SDK March 2009
・Windows SDK for Windows 7 BETA
・VisualStudio 2008

VisualStudio の Tools → Options → Projects and Solutions → VC++ DIrectories の設定

Include files

$(DXSDK_DIR)include
$(WindowsSdkDir)\include
$(VCInstallDir)include
~

Library files (x64)

$(DXSDK_DIR)lib\x64
$(WindowsSdkDir)lib\x64
$(VCInstallDir)lib\amd64
~

Library files (x86)

$(DXSDK_DIR)lib\x86
$(WindowsSdkDir)lib
$(VCInstallDir)lib
~

またあらかじめスタートメニュー Microsoft Windows SDK v7.0 から
Windows SDK Configuration Tool を起動して v7.0 を選択しておきます。


関連エントリ
DirectX SDK March 2009
Windows7 とマルチタッチ / HP TouchSmart PC IQ800
Direct2D と Direct3D10.1 の下位互換



2009年初の新しい SDK です。

DirectX SDK (March 2009)

大きなトピックは Direct2D/DirectWrite の Technical Preview が含まれたこと。
Windows7 beta + Windows7 SDK beta でないと試すことが出来なかった
Direct2D / DirectWrite が Vista でも使えるようになりました。
ドキュメント類は以前から MSDN に上がっているのでそちらを参照することになります。
インストールによって d2d1_beta.dll, dwrite_beta.dll が入りました。

これでおそらく D2D/DWrite だけなら Windows7 SDK が無くてもテストできると思います。
ヘッダ類もこちらの方が新しいようです。コメントが増えて新しい定義もあります。

その他 DXGI1.1 も入りました。元々ヘッダは含まれてましたが、dxgi_beta.dll / lib が
追加されてドキュメントも統合されたようです。

Direct3D11 TechPreview の方はあまり大きな変化があったようには見えませんが、
細かく変更が入っています。
たとえばヘッダには CD3D11_RECT, CD3D11_DEPTH_STENCIL_DESC,
CD3D11_BLEND_DESC 等のヘルパークラスが多数追加されているようです。
Direct2D と同じようにデフォルト値による初期化がサポートされています。
デフォルト値を示すオーバーロード用の type は CD3D11_DEFAULT です。

マニュアルでは Shader Model 5 の Attributes に attrib, maxtessfactor,
earlydepthstencil が追加。Intrinsic Functions には
countbits, EvaluateAttributeAtControid, EvaluateAttributeAtSample
が増えています。
またヘッダを見ないと使い方が分からなかった D3DCompiler のマニュアルも入りました。

Shader の項目には Reflection 関連のインターフェースが追加されています。
このあたりヘッダを見てもかなり手が入っていることが分かります。
まだ Effect (fx) はありませんが進展は見られます。

その他マニュアルには載ってませんが D3D11_CREATE_DEVICE_BGRA_SUPPORT
も目新しいところです。


関連エントリ
Direct2D (7) Tessellate の 2
Direct2D と Direct3D10.1 の下位互換
Windows7 リモートデスクトップと Direct3D
Direct3D11/DirectX11 (18) GPU を使ったアウトラインフォントの描画の(6)
Direct3D11 Technical Preview D3D11の互換性、WARP Driver



2009/03/21
SSD OCZ Vertex

先週末、運良く店頭で入手できたので OS を入れ替えて一週間ほど使いました。

・OCZSSD2-1VTX60G

60GB の 2.5inch SSD です。22800円
RAM 8GB + Win7 beta x64 なので、ページングファイル無しにして休止状態ファイル
クリーナーで消して OS のみ 10GB くらい。

特に速度を実感できるのはソフトウエアのインストールで、残り時間が正確に
表示されないくらい。Windows の起動や終了も速いし、重いアプリケーションの
立ち上げも違いが分かるほどに短縮しています。

プログラミング作業はどうかというと、元々そんなに編集もコンパイル時間も
困ってなかったのであんまり速い感じはしていません。
その代わり作業中、非常に静かなのが心地よいです。

ファンの一定の音はありますが不定期に鳴り出す音が消えました。
ファイルに書き込んでるな、とか裏で何か動き出したな、とか無いので結構違います。
HDD が寝ていて、いざアクセスしようとしたらスピンアップ待ちで起動まで待たされる
こともないわけです。動作中の配置換えなんかも気にせず出来そう。

プロセッサは年々速くなっていくのでストレージとの速度差は離れる一方でしたが
ここに来て急に差が縮まり出したようです。
OS やアプリケーションも HDD のシークが遅いという前提で作られていますが、
そのうち全く気にしなくても良くなるかもしれません。
ファイルアクセスを速くするためのいくつかの工夫も、きっと古い技術となって
忘れていくのでしょう。

机の奥から古いレシートが出てきました。
1999年7月9日に 13GB の HDD を 21480円で買ってました。



PC-OP-RS1 (KURO-RS) を使って ROBO-Q と同じように調べてみました。
Q-TRAIN のコントローラの赤外線信号です。

TAKARA TOMY Q-TRAIN

●コマンドフォーマット

コマンドは 8bit 分
下記の順番で送信されています

先頭
  C C A 0 R L B F


 CC = BAND A~D (00,01,10,11)
 A = 自動移動の指定かどうか (左サイドのスイッチが on なら 1)
 0 = 常にゼロ
 R = 右ボタン (→)
 L = 左ボタン (←)
 B = 後退ボタン (↓)
 F = 前進ボタン (↑)

R L B F の各ボタンは同時押しが出来ます。
例えば前進+右旋回だと F + R が同時に ON になります。


コマンド組み合わせの例

BAND A 前進	00000001
BAND B 前進	01000001
BAND C 前進	10000001
BAND D 前進	11000001

BAND A 前進自動	00100001
BAND A 後退	00000010
BAND A 左	00000100
BAND A 右	00001000
BAND A 前進+左	00000101

●コマンドエンコード

ヘッダや 1bit 分の波形も長く取られており ROBO-Q とは異なっています。

ヘッダ常に H 6.3msec

bit
 0 = L 0.5msec + H 1.7msec
 1 = L 1.5msec + H 0.7msec

エンコードされた bit データは ROBO-Q と違い、0 でも 1 でも同じ長さになります。
よって 1コマンドはだいたい 23.9msec


例 BAND D 前進 + 左 で受け取った波形 (0.1msec 単位のサンプリング)

// ヘッダ
11111111111111111111111111111111111111111111111111111111111111
// 1
000000000000000 1111111
// 1
000000000000000 111111
// 0
00000 11111111111111111
// 0
00000 11111111111111111
// 0
00000 11111111111111111
// 1 (左ボタン)
000000000000000 1111111
// 0
00000 11111111111111111
// 1 (前進ボタン)
0000000000000000 111111


使用したツールは ROBO-Q 同様「スーの道具箱/分解してみよう/PC-OP-RS1」の
IrReceiver です。


関連エントリ
タカラトミー ROBO-Q (3) PC から操作する実験
タカラトミー ROBO-Q (2) 赤外線コマンドの解析
タカラトミー ROBO-Q



Windows7 beta でも下記のハードでタッチ操作を試すことが出来るそうです。

 ・Windows 7で"マルチタッチ"を楽しむには? MSが利用方法やTIPSを紹介

HP TouchSmart PC IQ827jp に Windows7 beta を入れてみました。

 ・HP TouchSmart PC IQ800

IQ800 は液晶モニタ一体型のオールインワン PC で光学式のタッチパネルを搭載しています。
パネルに触れるぎりぎりの位置で反応するので、パネルに指を押しつける必要もなく
非常に軽い操作ができます。
iPhone 等の静電容量タイプと違うのは指でなくても反応すること。
つめでもいいしペンでも良いし、画面に近づく物は何でもタッチ扱いです。

付属するキーボードもマウスも最初からワイヤレスタイプで、無線LAN 11b/g/n も
Bluetooth も内蔵しています。ケーブルは電源のみ、AC アダプタは巨大だけど
すっきりとした見た目です。

キーボードも非常に薄く、ちょうど本体下の足の間に入ります。
タッチやマウスを使っていても邪魔になりません。

標準搭載の Vista だと、マルチタッチらしい操作ができたのは TouchSmart
画面のアイコン拡縮だけでした。


Windows7 beta x64 を install し、WindowsUpdate でタッチパネルドライバが
入ることを確認しました。でも予想外のところでトラブルが。
タッチは使えるものの GeForce 9600M GS のドライバが入らず Aero にできません。

Notebook 向け M 系は Windows7 ドライバがまだ無く Vista ドライバ 179.48 も
エラーが出ます。
「NVIDIA Setup プログラムは、現在のハードウエアと互換性のあるドライバを見つけることができませんでした」

アップグレードは出来るはずなので試しに Vista をクリーンインストールしても
Vista 向けドライバなのに同じエラーです。
その後 acryl さんに下記のページを教えてもらいました。

価格.com 『XPのインストール』のクチコミ掲示板

同じように LaptopVideo2Go から 182.06 Vista 64bit の inf を置き換えてインストール
したところうまくいきました。WDDM 1.0

CPU: 5.9
RAM: 5.9
AERO: 6.2
GAME: 5.2
HDD: 5.9

AC アダプタがついていたりモバイル向け GPU が入っていたりと比較的ノート PC に
近い構造です。画面の明るさ調整ボタンもモニタ側についておらず、ノートのように
キーボードで操作します。
Windows7 ではこれらのショートカットが使えなくなりましたが、
ちょうど type P + Windows7 用に作っていた backlightwin がそのまま動きました。

肝心のタッチは一応 2点まで取れています。
こんなに Windows の ペイント ソフトの稼働率が高いのは初めてです。


関連エントリ
VAIO type P Windows7 beta とバックライト調整など



少々時間が無かったので昨日の解析結果が正しいかどうか確認するだけ。
BAND A/D の ROBO-Q も調達できたので 4バンド分の信号を確認できました。

実際にコマンド送信プログラムを作ってみます。
スーの道具箱/分解してみよう/PC-OP-RS1
のページを参考にさせていただきました。そのままです。

初期化

hPort= CreateFile(
		"COM4",
		GENERIC_WRITE|GENERIC_READ,
		FILE_SHARE_READ|FILE_SHARE_WRITE,
		NULL,
		OPEN_EXISTING,
		0,
		NULL
	);
if( hPort == INVALID_HANDLE_VALUE ){
	// ERROR
	return;
}
DCB	dcb;
dcb.DCBlength= sizeof(DCB);
dcb.BaudRate= CBR_115200;
dcb.fBinary= TRUE;
dcb.fParity= FALSE;
dcb.fOutxCtsFlow= FALSE;
dcb.fOutxDsrFlow= FALSE;
dcb.fDtrControl= DTR_CONTROL_DISABLE;
dcb.fDsrSensitivity= FALSE;
dcb.fTXContinueOnXoff= FALSE;
dcb.fOutX= FALSE;
dcb.fInX= FALSE;
dcb.fErrorChar= FALSE;
dcb.fNull= FALSE;
dcb.fRtsControl= RTS_CONTROL_DISABLE;
dcb.fAbortOnError= FALSE;
dcb.fDummy2= 0;
dcb.wReserved= 0;
dcb.XonLim= 0;
dcb.XoffLim= 0;
dcb.ByteSize= 8;
dcb.Parity= NOPARITY;
dcb.StopBits= ONESTOPBIT;
dcb.XonChar= 0;
dcb.XoffChar= 0;
dcb.ErrorChar= 0;
dcb.EofChar= 0;
dcb.EvtChar= 0;
dcb.wReserved1= 0;
SetCommState( hPort, &dcb );

送受信は WriteFile()/ReadFile() です。

PC-OP-RS1 用の送信データを組み立てます。
赤外線の On/Off 情報をビット列で指定。あくまで動作確認用なのでべたで。

int pushbit( char* ptr, int index, int flag, int length )
{
	if( flag ){
		for( int i= 0 ; i< length ; i++ ){
			int	byteoffset= index >> 3;
			int	bitoffset= index & 7;
			ptr[byteoffset] |=  (1<<bitoffset);
			index++;
		}
	}else{
		index+= length;
	}
	return	index;
}

// H 1ms  + data x7
void SendCommand( int band, int speed, int turn )
{
	unsigned int	bitpattern=  ((speed<<5) & 0x60)
				|(( turn<<3) & 0x18)
				|(( band<<1) & 0x06);
	char	byte[240];
	memset( byte, 0, 240 );
	int	index= 0;
	index= pushbit( byte, index, TRUE, 10 ); // H 1ms
	for( int i= 0 ; i< 7 ; i++ ){
		if( bitpattern & (1<<(6-i)) ){
			index= pushbit( byte, index, TRUE, 10 ); // H 1ms
		}else{
			index= pushbit( byte, index, TRUE, 5 ); // H 0.5ms
		}
		index= pushbit( byte, index, FALSE, 5 ); // L 0.5ms
	}
	SendIR( byte, 240 );
}

データ送信

void SendIR( const char* command )
{
    iPort->Send( "t", 1 );
    iPort->Recv( rdata, 1 );	// 'Y'
    iPort->Send( "1", 1 );		// ch1
    iPort->Recv( rdata, 1 );	// 'Y'
    iPort->Send( command, 240 );
    iPort->Recv( rdata, 1 );	// 'E'
}

昨日のデータ通りです。すんなり動作しました。
PC で ROBO-Q を操作できます。

SendCommand( 1, 2, 0 ); // BAND B で中速(M)前進
SendCommand( 1, 1, 1 ); // BAND B で低速(L)右回転

でもデータ送信まで思ったより時間がかかるので、細かい制御をリアルタイムに
行うには少々厳しいです。要検討。

修正 2009/3/5 4:27 勘違いでした。十分な速度で操作できます。

きちんと使うなら 192ms 分複数のコマンドをパックして送る必要あります。


関連エントリ
タカラトミー ROBO-Q (2) 赤外線コマンドの解析
タカラトミー ROBO-Q



ROBO-Q のリモコンコマンドを調べてみました。

ROBO-Q 赤外線
コマンドは 7bit (最後は固定なので、data 6bit + 終端bit かもしれない)

先頭     終端
S S T T B B 0

SS = 速度
  00 = AUTO 等のコマンド送信
  01 = 前進 速度L
  10 = 前進 速度M
  11 = 前進 速度H

TT = ターン他 (bit が 1 の方の足を止める)
  00 = 前進 (両足とも止めない)
  01 = 右回転 (右足を止める) / 回避モード
  10 = 左回転 (左足を止める) / 回避モード
  11 = 追跡モード

BB = バンド
  00 = BAND A
  01 = BAND B
  10 = BAND C
  11 = BAND D


全部の組み合わせ

前進L   = 0100bb0
前進M   = 1000bb0
前進H   = 1100bb0
右回転L = 0101bb0
右回転M = 1001bb0
右回転H = 1101bb0
左回転L = 0110bb0
左回転M = 1010bb0
左回転H = 1110bb0
AI回避  = 0011bb0
AI追跡  = 0010bb0 / 0001bb0

移動はコマンドを送信し続ける必要があります。
何もコマンドが送られていない状態が停止です。


●解析に使用したもの

PC に USB 接続できる学習リモコンキットです。
BUFFALO と 玄人志向 どちらも同じものだそうです。

BUFFALO PC-OP-RS1
玄人志向 KURO-RS


●PC-OP-RS1 WindowsVista x64 用ドライバ

付属ドライバも web から落とせるドライバも Windows x64 に対応していません。
x64 で動作させるために下記のページを参考にさせていただきました。

プログラミング研究所 KURO-RSをWindows XP x64で使用する

上のページを参考にして FTDI Virtual COM Port Drivers の最新版を入れました。
これで PC-OP-RS1 を Vista x64 で使用することが出来ます。
以下詳細手順


● x64 ドライバ導入手順のメモ

(1) FTDI Virtual COM Port Drivers から 2.04.16 を download (CDM 2.04.16 WHQL Certified.zip)

(2) デバイスマネージャーから PC-OP-RS1 のデバイスIDを確認しておく
  不明なデバイスのままで構わない。
   プロパティ→詳細→ハードウエアID
  これで vid=0411, pid=00b3 であることがわかる

(3) (1) のアーカイブを展開しフォルダ内の *.inf ファイルを書き換える

2つあります。
どちらも [FtdiHw]/[FtdiHw.NTamd64] の項目の先頭行を複製して VID/PID を書き換える
だけです。[Strings] は書き換えなくてもインストールできました。

ftdibus.inf の対応する項目に追加する行(追加する行だけ記述)
[FtdiHw]
%USB\VID_0411&PID_00b3.DeviceDesc%=FtdiBus.NT,USB\VID_0411&PID_00b3

[FtdiHw.NTamd64]
%USB\VID_0411&PID_00b3.DeviceDesc%=FtdiBus.NTamd64,USB\VID_0411&PID_00b3

[String]
USB\VID_0411&PID_00b3.DeviceDesc="USB Serial Converter"


ftdiport.inf の対応する項目に追加する行(追加する行だけ記述)
[FtdiHw]
%VID_0411&PID_00b3.DeviceDesc%=FtdiPort232.NT,FTDIBUS\COMPORT&VID_0411&PID_00b3

[FtdiHw.NTamd64]
%VID_0411&PID_00b3.DeviceDesc%=FtdiPort232.NTamd64,FTDIBUS\COMPORT&VID_0411&PID_00b3

[String]
VID_0411&PID_00b3.DeviceDesc="BUFFALO RemoteStation PC-OP-RS1"


ドライバインストール時は、上記フォルダを手動で指定します。
ドライバを検索せずにディスク使用まで持って行く。
KURO-RS でもおそらく同じです。


● PC-OP-RS1 を使ったデータ取り込み

すべて下記のページを参考にさせていただきました。
ツールも下記ページからダウンロードできる IrReceiver/IrSender を使用しています。

スーの道具箱/分解してみよう/PC-OP-RS1


● ROBO-Q 赤外線コマンドの信号

スーの道具箱/分解してみよう/PC-OP-RS1」によると、受信データは
0.1ms のサンプリング情報とのこと。
240byte x 8bit = 1920bit が 0.1ms 単位だとすると 192ms 分の情報が一度に
取れます。

実際にサンプリングした bit パターンを見てみると下記のようになっています。
実際には 1bit 程度の誤差が入ります。

111111111111111100000111111111100000111111000011111100000111110000011111111110000011111100000000


・信号と思われるところ = 5個並んだ1(H) または 10個並んだ1(H)
・区切り = 5個並んだ0(L)

よって
0.5ms の連続する H = データの 0
1.0ms の連続する H = データの 1
0.5ms の連続する L = 区切り

とみなすと、一番最初に書いた 7bit のデータが得られます。
データの先頭は 1ms 程度の H が続きます。

コマンドの長さは

1.0 + 1.5*6 + 1.0 = 11ms
1.0 + 1.0*6 + 1.0 = 8ms

なので、送信に 8~11ms かかります。
連続歩行の場合 170ms 程度の間を空けているようです。
スライダを L→H と移動する場合やターンの開始時などは、間無しにほぼ連続して
コマンドが送られています。


●障害物認識ユニット

障害物認識ユニットは、上記のコマンドとは全く異なるパターンが送られています。

1111110000000000111111000000

最初の H が 0.5ms でデータ区切りが 1ms あるためコマンドと区別可能です。
左右の違いが無かったため、どのように区別しているのかはまだわかりませんでした。


●参考にさせていただきました

下記のページが無かったら調べることが出来ませんでした。
JuJu 様、NMVL 様に感謝します。

スーの道具箱/分解してみよう/PC-OP-RS1
プログラミング研究所 KURO-RSをWindows XP x64で使用する


関連エントリ
タカラトミー ROBO-Q



非常に小さいです。

TAKARA TOMY ROBO-Q

ROBO-Q

とことこ歩きます。
付属のコントローラを使えば、こんなに小さいのに歩かせたり回転したりと操縦できるんです。
ゆっくり動けるので、EIZO のモニタの上の縁なども端から端まで歩くことが出来ました。

さらにユニークなのは自律モードがあること。
左右に障害物認識ユニット(マニュアルに記載された名称)があって、
障害物をよけたり目の前のものを追跡したりします。

コントローラは TV リモコンのように赤外線を使っています。
ロボット側の赤外線受光部はちょうど顔の位置、正面です。


赤外線なのでおそらくリモコンのようにパルス信号でコマンドを識別しています。
ROBO-Q は A BAND ~ D BAND と 4種類あり、4台までは同時に操作しても混信しません。
前進や回転などの赤外線コマンドも 4セット用意されているのだと考えられます。

ちなみに同じ BAND タイプの ROBO-Q が 2台あると、1つのコントローラで同時に
操作できます。


自律モードではいったいどうやって障害物を見ているんでしょう。
小さいし価格も安いし、あまり複雑な仕組みは使っていないはずです。
障害物認識ユニットはちょうど顔の横についており、それぞれ右側と左側の障害物を
感知できます。
このセンサー赤外線発信部にそっくりです。

ROBO-Q

おそらく想像ですが、コントローラと同じように左右に赤外線のコマンドを発信して
いるのだと思われます。
赤外線の届く距離をうまく調整すれば、目の前に障害物が来たときだけ反射して
顔の正面の受光部でコマンドを受けることが出来ます。
受光部は共有できるし仕組みも単純にできて非常にうまい方法だと思います。

考えられる赤外線コマンドの種類は下記の通り。

・両足を動かす x3速度
・左足を動かす x3速度
・右足を動かす x3速度
・AI回避モードへ移行
・AI追跡モードへ移行
・右側障害物認識ユニット
・左側障害物認識ユニット

前進は左右のコマンドを同時に送ってるだけかもしれません。
障害物認識ユニットも微弱ながら学習リモコンが反応しました。
まだ障害物の再現は出来ていないので想像の範囲を超えてないです。



以前申し込んだ UQ WiMAX、サービス開始当日 26日に端末が届きました。
モニターではなく購入です。

USB タイプの UD01SS を VAIO type P + Windows7 で使ってみます。

WiMAX USB TYPE UD01SS

uqwimax
↑何となく取れそうに見えた先端の蓋部分はやっぱり外すことができた。アンテナ端子?


●Windows7 Beta へのインストール (UD01SS)

ドライバはそのままで OK。ユーティリティの方は OS のバージョンではじかれました。
互換モードで Windows Vista を指定するとインストールできます。

・ドライバ

UD01SS を差し込むだけで認識し、組み込まれているドライバのインストーラが
起動します。最初は USB の CD-ROM ドライブとして認識されるようです。
あとは手順に従うだけ。Windows7 beta でも特に問題はありません。

・ユーティリティ

ユーティリティソフトは付属の CD-ROM に入っています。
Web からも落とせるようです。
CD-ROM の Setup.exe をそのまま実行するとエラーがでるので、先にプロパティから
「互換性→互換モード→Windows Vista」を選んでおきます。


●接続

WiMAX GO のアイコンをクリックするだけ。
ユーティリティを起動しておけば電波状況を表示し、圏内なら接続します。
特に設定もなくアダプタの抜き差しなども判定してくれるので手軽です。
Windows7 beta でも問題なく使えています。

接続や切断の管理も不要で、中断する場合も PC のモニタを閉じてスリープする
だけで大丈夫。再接続も自動で行われます。
スリープ解除後の再接続は、端末の接続チェックと初期化が入るので数秒待たされます。


●速度

VAIO type P + Windows7 beta + Chrome
という特殊な環境なので参考程度に。

・下り
笹塚駅 2Mbps 前後
麹町 4Mbps ~ 9Mbps (測定サイトによって異なる)

測定サイトによって結構ばらつきがあります。
駅のホームで試した場合も、場所によって結構違うし電車の通過によって
影響を受けることもありました。

つながらない場合は完全に圏外になります。つながらない場所は結構あります。
最初に試したサイトが低い数値しか出さなかったのであまり参考にならないかも
しれませんが、安定して圏内を維持する場合は 1Mbps 以上出てるようです。
ブラウザを使ってる分には普段無線 LAN でつないでるのと変わらず速いです。


関連エントリ
UQ WiMAX 申し込み




ミニノートを持ち歩いていて、普段ネット端末やメモ機として使っています。
この場合外部ディスプレイ出力を使うことがまずないのですが、でもごくまれに
必要になることがあります。

小型なノートPC で特殊なコネクタを採用している場合は少々やっかいです。
専用アダプタや専用ケーブル経由で接続しなければならず、
かさばるし、滅多に使わないのに専用品を購入しなければならないし、
必要になったときにすぐ手に入らないかもしれません。

また実際に必要になって購入を考えると結構高いものです。
MobileGear2 の時はモニタ用ケーブルだけで 4000円、利用回数一回きり。
LOOX U50 店頭モデルでは本体同梱、使ったのは 2~3回。
VAIO type P の場合別売りで VGP-DA10 4980円 (LANアダプタ兼用)。

そこで専用品ではなく USB 接続のモニタアダプタを使うことにしました。
これだと機種を変えても使い回せるし、普段はデスクトップ PC につないだりと
他の用途でも使えます。

IO DATA USB-RGB
IO DATA USB-RGB/D

最近 USB で接続できる液晶モニタも話題になっていますが同じ原理のようです。

ぼくらは「USB-RGB」を誤解していたかもしれない (1/4)

USB バスパワーで動作するので外部ディスプレイ出力アダプタとほぼ同じ感覚で使え
そうです。

実際に試したところ、現在の Windows7 beta 1 では正しく動作しませんでした。
一応ソフトウエアのインストールが可能で、絵は出るものの解像度やモード
切り替えなどが出来ません。

VAIO type P にクリーンインストールした Vista の方では正常に動作しました。
ただし VAIO type P の場合あらかじめ Aero を切っておいた方がよいです。
接続時にエラーになりました。
タスクトレイアイコンからのモード切り替えも可能で 640x480 も使えています。

小型プロジェクター ADTEC AD-MP15A との接続も確認。
800x600 でもきちんと表示できました。

純正アダプタによる直接出力と比べると描画速度が多少遅いはずですが思ったより
気になりません。打ち合わせなどで文書や資料を表示するには十分です。


関連エントリ
ADTEC AD-MP15A 小型プロジェクター



マウスの感度を示す単位はミッキーというらしい。
じゃあどうやってミッキーを測るんだろう、ということで簡単に試してみました。

wikipedia マウス

マウスの入力値の取得には DirectInput を使用します。
DirectInput の Buffer モードを使って移動量の変化を取り出します。そのままだと
DInput の取得値にも速度に応じて加速がついてしまうので設定が必要です。

・コントロールパネルのマウスから
  ポインタオプションのタブ→速度のグループで
  「ポインタの精度を高める」のチェックを外す

これで加速補正が無くなります。
「ポインタの速度」は DirectInput の値に影響しません。

マウス付属のドライバやユーティリティは使わず、Windows 標準のドライバのみ
使用しています。

こんな感じでマウスにガイドを付けます。
出来るだけ水平、または垂直に移動できるように。

d2dmouse

カウンタをリセットしてだいたい 1インチ分だけ移動して得られた値を調べます。
精度が高いため手で試してもかなり誤差が入り厳密な値はとれません。
以下数値はかなりいい加減です。

だいたい 800~1000 くらいの範囲の値を返します。
おそらくこれがマウスセンサーのカウント値です。
1インチ移動する間に何回センサーの On/Off をカウントできるのか、どれだけ細かく
動きを読み取れるのか表す単位として cpi (count per inch ) があります。
メーカーによっては dpi (dot per inch) と表現されているものもありますがおそらく
同じものです。

テストしたマウス
(1) BlueTrack
Microsoft Explorer Mini Mouse
1000dpi/8000fps

結果
・800~1025 (8~10.2ミッキー)


(2) Laser
SIGMA APO SLATWRF
800,1600cpi/6600fps

結果
・800cpi モードで 800~900 (8~9ミッキー)
・1600cpi モードで 950~1450 (9.5~14.5ミッキー)


◎速く動かした方が値が小さい

センサーの読み取りエラーが発生しやすくなるためと思われます。速く動かした場合の
耐性を表す目安が、マウススペックに記載されている fps となるようです。


◎精度を切り替え可能な場合

低い cpi モードの方がほぼ期待通り、安定した数値になっています。

高い cpi モードの場合、全体的に数値は増えますがスペック通りの上限には達しま
せんでした。材質の相性や速度の影響を受けやすくなっているためと思われます。
精度は上がるがよりノイズを含んでいる状態といった感じでしょうか。


◎相性

机やマウスパッドの材質とセンサーの相性でかなり変わります。
反応が悪いと半分くらいの値になることも。


カタログスペック通りと想定するなら、800cpi(dpi) のマウスは 8ミッキーでカウント
値からマウスの移動量がわかります。もし正確ならば、マウスを使って長さを測る
こともできるでしょう。

でも実際は机やマウスパッドとの相性と速度の影響が大きく、頻繁に読み取りエラーが
発生していることがわかります。平均ミッキーはこれらの条件によって変わります。
特に速度は重要なパラメータで、cpi だけでなくマウスの性能として fps も併記
されているのはそのためでしょう。

きちんとツール化して調べれば、最もエラーの少ないマウスパッドの組み合わせを
探し出すなど活用できそうです。


参考にさせていただいたページ
その37.Windows標準のポインタ(カーソル)速度の変遷
Windows XP でマウス ポインタの加速を調節する方法

// DirectInput
DWORD	elems= 32;
DIDEVICEOBJECTDATA	data[32];
HRESULT	hr= iDevice->GetDeviceData( sizeof(DIDEVICEOBJECTDATA), data, &elems, 0 );
if( FAILED( hr ) ){
    return;
}
DWORD	ac= elems;
for( DWORD i= 0 ; i< ac ; i++ ){
    switch( data[i].dwOfs ){
    case DIMOFS_X:
	PositionX-= *(int*)(&data[i].dwData);
	break;
    case DIMOFS_Y:
	PositionY-= *(int*)(&data[i].dwData);
	break;
    }
}




Mercurial のサーバー設定まで

●Debian install

Debian -- The Universal Operating System

5.0 Lenny が release されたので "stable" になりました。
今回は Network install より Small CDs の i386
debian-500-i386-netinst.iso を使用しています。

VirtualBox 2.1.4 を使っています。
手順は 前回 と全く同じです。


●Debian の設定

# apt-get update
# apt-get install ssh

以後 putty 経由

# apt-get install mercurial
# apt-get install hgsvn

subversion から

# svn ls https://SVNSERVER/svn/src2

# mkdir /var/lib/hg
# cd /var/lib/hg
# hgimportsvn https://SVNSERVER/svn/src2 src2
# cd src2
# hgpullsvn
# chown -R www-data.www-data src2

SSL 設定

# apt-get install ssl-cert
# make-ssl-cert generate-default-snakeoil --force-overwrite
# a2ensite default-ssl
# a2enmod ssl
# /etc/init.d/apache2 restart

Mercurial 設定

# mkdir /var/www/hg
# cd /var/www/hg
# cp /usr/share/doc/mercurial/examples/hgweb.cgi ./index.cgi
# chmod 700 index.cgi
# chown www-data.www-data index.cgi
# vi index.cgi

application = hgweb("/var/lib/hg/src2", "src2")

# vi /etc/apache2/sites-available/hg-src2

Alias /hg /var/www/hg
<Directory "/var/www/hg">
 DirectoryIndex index.cgi
 AddHandler cgi-script .cgi
 Options ExexCGI
 AuthType Basic
 AuthName "hg repository"
 AuthUserFile /etc/apache2/hg.passwd
 Require valid-user
</Directory>

# htpasswd -c /etc/apache2/hg.passwd USER
# a2ensite hg-src2
# /etc/init.d/apache2 restart

# vi /etc/mercurial/hgrc

[web]
allow_push = *



●確認

Windows Mercurial-1.0.2 にて

C:\hg> hg clone https://HGSERVER/hg


参考ページ
Setting up a Mercurial CGI Server
MercurialのCGIサーバをセットアップする
hgsvn



WindowsMobile6.x がもう一世代継続されるようです。

米マイクロソフト、スペインで「Windows Mobile 6.5」発表

今でもプラグインが多いと SIP が読み込めなかったり切り替わらなかったり、
ブラウザのようなメモリを多く消費するアプリが不安定になったりしています。
WindowsMobile6.x のカーネルは WindowsCE5.2 で、WindowsMobile5.0 とそれほど
大きく変わっていません。
下記のページで詳しく解説されています。

Windows CE .NET の高度なメモリ管理

これを見ると CE4~5 のメモリ管理は下記の構造になっていることがわかります。

(1プロセス 32MB × 32プロセス) + 32MB の XIP DLL 空間 (ROM DLL)
  + ラージメモリ空間(992MB) + カーネル空間(2GB)

CE の限界は、上記の通り 1プロセスあたりのメモリ空間が 32MB に制限されていること。
最近の端末では 128MB~256MB の RAM を搭載していますが、1つのアプリケーションが
メモリ空間的に限界に達しているときは、いくら物理メモリを増やしても効果がない
ことがわかります。(desktop Windows でも 32bit OS だと 2GB まで、という制限に似ています)

32MB の XIP DLL 空間は、ROM にあらかじめ組み込まれている DLL を配置するための
領域だそうです。system の共有 DLL が XIP 空間に配置されている場合は 32MB の
プロセス領域を消費しません。

ROM に含まれていない (XIP でない) DLL は XIP 空間に配置できません。
こちらはプロセスのメモリを消費します。さらに DLL はプロセス間で共有されるために
ロードしたアドレスが固定されてしまうようです。
よって SIP プラグインなど、後から追加した dll が多数読み込まれている場合は、
各アプリケーションで使用可能なメモリ空間を食いつぶしてしまう可能性があります。

たとえば XIP でない DLL の合計が 16MB あって、かつ最もよく使われる dll が
一番下位のメモリにロードされてしまった場合。またはアプリケーションが複数の
dll をロードする構造の場合、最悪アプリケーションが使用可能なメモリ空間も
16MB になってしまうということです。

これらのこの問題に拍車をかけているのがもう 1つの制限、プロセス数の上限が
32個ということ。常駐するアプリケーションはプロセスを消費しないようサービス
として dll 化することが推奨されていているため、dll 空間を余計圧迫している
可能性があります。また単一のプロセス空間を取り合うため、dll を配置する
サービス用の空間自体も限界に達してしまう可能性もあります。

常駐する dll が多い場合、追加で dll 自体をロードできなくなる場合があること、
またブラウザなどの大きなアプリが不安定になる可能性があることがわかります。
メモリが大量にある場合、dll の共有はかえって逆効果なのかもしれません。

ただ実際に touchkeysip が読み込まれなくなる場合、どの段階で限界が来ており
どこのメモリ確保でエラーが出ているのかきちんと調べたわけではないので、
上の説明もいろいろと憶測が含まれています。あらかじめご了承ください。
単に touchkeysip が悪いだけの可能性もあります。

データ領域に限っていえば、ラージメモリ領域を使用することが出来るためまだ余裕は
ありそうです。上の MSDN のページに書いてある方法で大きなメモリブロックを
まとめて VirtualAlloc で予約すると、ラージメモリ領域に配置されるとのこと。

・常駐する dll を減らす。
・常駐する dll は一番先に読み込ませておく。
・アプリが消費するヒープメモリは自分でラージメモリから確保する。
・アプリを使い終わったらすぐ終了させる。特に独自の dll を使うもの。

等が有効かもしれません。

WindowsCE6.0 (WindowsMobile7) 以降は一般的な 32bit OS と同じ 2GB に拡張され、
プロセス数の制限もなくなるはずです。逆に普通の Windows に近づくので
CE である必要性が徐々に薄れていくような気もします。
先があることがわかっている中に出る 6.5 は Me を思い出してしまいます。


関連エントリ
emobile EM・ONE 使い切れないメインメモリ



久しぶりに思い出したのは、昔作った BASIC コンパイラが欲しいとのメールを
いただいたからです。雑誌に掲載されたのが 1989年なので 20年くらい前になります。

コンパイラといっても、当時の SHARP ポケットコンピュータ PC-1261/62 用に
見よう見まねで作ったもの。
名前は「 LC-3 コンパイラ 」といいますが、今検索で引っかかる
LC-3 compiler とは名称が同じだけで別物です。

なにせ RAM も 10.4KB と小さく、外部記憶装置はカセットテープしかありません。
そのかわり RAM はバッテリーバックアップされており、電源を切っても中身が
消えずに保持されます。

BASIC プログラムは中間コードでメモリに格納されています。それを読み出して
機械語に置き換えるわけです。エディタと字句解析に BASIC を使っているだけなので、
中間コードやキーワードを流用しつつも構文や命令は独自で完全互換ではありません。
知識も無かったので本当に単純なものでした。
その代わりメモリ容量との戦いがあります。一晩考え抜いて 1byte 削っては喜んで、
といった感じに。

資料がないので記憶だけですが、中間コードのソース領域が 3KB くらい、コンパイラ
本体が 2.0KB、ランタイムルーチンが 1.5KB、変換後のバイナリが 3KB くらいの割り
当てだったと思います。

一式全部オンメモリで動作するので、一度入力すればカセットデッキが無くても
プログラムを書けるし、そのまま動作もデバッグもできるのが特徴です。


当時すでにポケコン向けの BASIC コンパイラはいくつか発表されていました。
PiO の「ポケコンパイラ」や The BASIC のコンパイラ(正式名称失念)など。
さらにメモリが少ない RAM 4.2KB PC-1251 で動作しており、どれも出たときは
かなりすごいと思ったものです。

生成コードが洗練されていなくても BASIC インタプリタと比べるとかなり高速に
動作しました。たとえばポケコンの BASIC は整数型が無く、全部 BCD の 10進数演算が
使われています。変数 1つで 8byte。今だと double と同サイズですがポケコンでは
これでも単精度です。2進化の誤差も発生しない代わりに、ただのループ変数の
インクリメントすら決して高速とはいえません。
コンパイラが生成するコードは 1byte 整数型を使用するので、出来ることは減るけど
高速です。


LC-3 は後発なのでさまざまな改良点があります。
その 1つが出力コードの最適化。中でも CPU の内 RAM を変数に割り当てたことで
高速化とともに生成コードの縮小化が出来ました。
もう 1つは用途を考えてゲーム向きの命令を用意したこと。

実際に作ったのはおそらく 1987年頃。1988年に掲載された Kind-1 などの一連の
ゲームも、最初 LC-3 上で作っていたからです。
コンパイラ自体が雑誌に未掲載だったため、投稿前にマシン語で書き直しました。


ネットが無い時代なので何らかの情報入手も雑誌が頼りでした。
プログラムの入手も誌面を埋め尽くすダンプリストを見ながら手打ちです。

投稿も同様、ワープロもなくて原稿用紙に手書きです。
プログラムの説明や入力方法、使い方、メモリマップやワークエリア等を原稿用紙に
手で書いて、カセットテープと一緒に郵送します。
だから 8bit パソコン時代のプログラムはデジタルデータとして手元に残って
いないのです。

もしあるなら web ページとかに掲載できるのですが、プログラムの保存方法は紙に
印字するかカセットテープだけ。
CE-125 は感熱紙なので消えているかもしれないし、仮にテープから読めても
通信機能がないので PC に転送する手段がありません。
手書きの資料はまだ残ってると思いますが、雑誌を探した方が確実な気がしてきました。

雑誌は投稿しても必ず掲載されるわけではありません。
だいたい半数くらいは没で、初期の頃はもっとたくさん。
投稿した原稿のコピーも全く無いので失われているものは結構多いのかもしれません。


便利な環境に慣れすぎたので、原稿用紙に文字数を数えながら清書するなんてたぶん
もう出来ないでしょう。送っても結果が出るまで何ヶ月もかかります。

今なら文章も簡単に書けるし編集可能だし、紙を媒体にする必要もありません。
ネットにはすぐ反映されるし、おそらく一度デジタル化したデータは簡単に失われたり
読めなくなったりすることもないでしょう。複製も容易に取っておけます。
手で打ち込み直す必要もないし配布も入手も簡単です。

そのときは全く考えもしなかったけどいろいろなことが不便でした。
配信、バックアップのための複製、転送、蓄積など。
紙や印刷の登場以前と以後といったら大げさかもしれませんが、コンピュータの
ネットワーク化とデータのデジタル化は、同じように生活を変えたのかもしれません。



昨日の Tessellate 結果を図示したものです。

FillGeometry() 相当の場合
ポリゴン化の状態をわかりやすくするために flatteningTolerance = 4 で Tessellate
しています。

direct2d

iFactory->CreateEllipseGeometry(
	D2D1::Ellipse( D2D1::Point2F( 0,0f, 0.0f ), 80.0f, 80.0f ), &iElli );
DummySink	tsink;
iElli->Tessellate( NULL, 4.0f, &tsink );

DrawGeometry() 相当の場合
strokeWidth = 4.0 で描画した場合のポリゴン分割です。
こちらも見やすいように flatteningTolerance = 4 で分割しています。

direct2d

iFactory->CreatePathGeometry( &iPath );
ID2D1GeometrySink*	isink;
iPath->Open( &isink );
iElli->Widen( 10.0f, NULL, NULL, isink );
isink->Close();
DummySink	tsink;
iPath->Tessellate( NULL, 4.0f, &tsink );

デフォルトのままだとこれくらいの分割です。
水平方向の分割かつ Strip になっているのがわかります。

direct2d

テストに使った DummySink の定義。

class DummySink : public ID2D1TessellationSink {
private:
    static ID2D1PathGeometry*	iPath;
    static ID2D1GeometrySink*	iSink;
public:
    DummySink()
    {
    }
public:
    STDMETHOD_( void, AddTriangles )( const D2D1_TRIANGLE* triangles, UINT trianglecount )
    {
	for( UINT i= 0 ; i< trianglecount ; i++, triangles++ ){
	    iSink->BeginFigure( triangles->point1, D2D1_FIGURE_BEGIN_FILLED );
	    iSink->AddLine( triangles->point2 );
	    iSink->AddLine( triangles->point3 );
	    iSink->EndFigure( D2D1_FIGURE_END_CLOSED );
	}
    }

    STDMETHOD_(ULONG, AddRef )( THIS )
    {
	return	0;
    }
    STDMETHOD_(ULONG, Release )( THIS )
    {
	return	0;
    }
    STDMETHOD( QueryInterface )( THIS_ REFIID riid, void** obj )
    {
	return	E_UNEXPECTED;
    }
    STDMETHOD( Close )()
    {
	return	S_OK;
    }
    static void	Create( ID2D1Factory* factory )
    {
	factory->CreatePathGeometry( &iPath );
	iPath->Open( &iSink );
    }
    static void	ClosePath()
    {
	if( iSink ){
	    iSink->Close();
	    iSink= NULL;
	}
    }
};

ID2D1PathGeometry*	DummySink::iPath= NULL;
ID2D1GeometrySink*	DummySink::iSink= NULL;

そのまま DrawGeometry() で path を描画すると、とがった角にトゲが生えたような
描画になることがあります。折れ線のつなぎ処理が入っているようです。

direct2d

描画用に ID2D1StrokeStyle を作成しておくとトゲが無くなります。


関連エントリ
Direct2D (6) Tessellate
Direct2D (5) ID2D1Geometry を使う
Direct2D (4) Direct2D の描画
Direct2D (3) 互換性の検証と Vista で Direct2D
Direct2D その(2) インターフェース
Direct2D と Direct3D10.1 の下位互換



FillMesh() はまだ成功していません。
Tessellate() で座標を取ってみました。

iFactory->CreateRectangleGeometry(
		D2D1::RectF( 0.0f, 0.0f, 10.0f, 10.0f ), &iRect );
DummySink	tsink;
iRect->Tessellate( NULL, &tsink );

まずはただの矩形から。
少々勘違いしていたのは、AddTriangles() はまとめて一度だけ呼ばれるのかと思ったら
1 Triangle 毎に何度も呼び出されること。
結果は次の通り。

t0= 10.000000 0.000000, 10.000000 10.000000,  0.000000 10.000000
t1=  0.000000 0.000000,  0.000000 10.000000, 10.000000  0.000000

頂点の並びが少々おかしい気がします。
それぞれ回転方向が逆になっているので、Strip を展開したような形でしょうか。

ID2D1RectangleGeometry ではなく Path で同様の形を作ってみます。

ID2D1GeometrySink*	isink;
iFactory->CreatePathGeometry( &iPath );
iPath->Open( &isink );
isink->BeginFigure( D2D1::Point2F( 0.0f, 0.0f ), D2D1_FIGURE_BEGIN_FILLED );
	isink->AddLine( D2D1::Point2F( 20.0f,  0.0f ) );
	isink->AddLine( D2D1::Point2F( 20.0f, 20.0f ) );
	isink->AddLine( D2D1::Point2F(  0.0f, 20.0f ) );
	isink->EndFigure( D2D1_FIGURE_END_CLOSED );
	isink->Close();
iPath->Tessellate( NULL, &tsink );

全く同じです。

t0= 20.000000 0.000000, 20.000000 20.000000,  0.000000 20.000000
t1=  0.000000 0.000000,  0.000000 20.000000, 20.000000  0.000000

パスの方向を逆にしても同じでした。

isink->BeginFigure( D2D1::Point2F( 0.0f, 0.0f ), D2D1_FIGURE_BEGIN_FILLED );
isink->AddLine( D2D1::Point2F(  0.0f, 20.0f ) );
isink->AddLine( D2D1::Point2F( 20.0f, 20.0f ) );
isink->AddLine( D2D1::Point2F( 20.0f,  0.0f ) );
isink->EndFigure( D2D1_FIGURE_END_CLOSED );

t0= 20.000000 0.000000, 20.000000 20.000000,  0.000000 20.000000
t1=  0.000000 0.000000,  0.000000 20.000000, 20.000000  0.000000

半径 10 の円の場合。

iFactory->CreateEllipseGeometry(
	D2D1::Ellipse( D2D1::Point2F( 0,0f, 0.0f ), 10.0f, 10.0f ), &iElli );
iElli->Tessellate( NULL, &tsink );

22個の Triangle に分割されています。ちなみに半径 30だと 30個。

t0= -2.015625 9.796875, 0.000000 10.000000, 2.015625 9.796875
t1= 3.890625 9.218750, 2.015625 9.796875, -2.015625 9.796875
t2= -3.890625 9.218750, -2.015625 9.796875, 3.890625 9.218750
t3= 7.062500 7.062500, 3.890625 9.218750, -3.890625 9.218750
t4= -7.062500 7.062500, -3.890625 9.218750, 7.062500 7.062500
t5= 9.218750 3.890625, 7.062500 7.062500, -7.062500 7.062500
t6= -9.218750 3.890625, -7.062500 7.062500, 9.218750 3.890625
t7= 9.796875 2.015625, 9.218750 3.890625, -9.218750 3.890625
t8= -9.796875 2.015625, -9.218750 3.890625, 9.796875 2.015625
t9= 10.000000 0.000000, 9.796875 2.015625, -9.796875 2.015625
t10= -10.000000 0.000000, -9.796875 2.015625, 10.000000 0.000000
t11= 9.796875 -2.015625, 10.000000 0.000000, -10.000000 0.000000
t12= -9.796875 -2.015625, -10.000000 0.000000, 9.796875 -2.015625
t13= 9.218750 -3.890625, 9.796875 -2.015625, -9.796875 -2.015625
t14= -9.218750 -3.890625, -9.796875 -2.015625, 9.218750 -3.890625
t15= 7.062500 -7.062500, 9.218750 -3.890625, -9.218750 -3.890625
t16= -7.062500 -7.062500, -9.218750 -3.890625, 7.062500 -7.062500
t17= 3.890625 -9.218750, 7.062500 -7.062500, -7.062500 -7.062500
t18= -3.890625 -9.218750, -7.062500 -7.062500, 3.890625 -9.218750
t19= 2.015625 -9.796875, 3.890625 -9.218750, -3.890625 -9.218750
t20= -2.015625 -9.796875, -3.890625 -9.218750, 2.015625 -9.796875
t21= 0.000000 -10.000000, -2.015625 -9.796875, 2.015625 -9.796875

円の中心に頂点がありません。水平方向にスライスされているので、やはり Strip に
展開されているのだと思われます。

分割数は Tessellate() 2番目のパラメータで指定出来ます。
  Tessellate( NULL, ここ, &tsink );
省略時は D2D1_DEFAULT_FLATTENING_TOLERANCE が渡されており、これは 0.25f です。

値を大きくすると、5.0 くらいで菱形の 4角形になります。
0.0f を与えると 3862個に分割されました。

# 5.0
t0= -10.000000 0.000000, 0.000000 10.000000, 10.000000 0.000000
t1=  0.000000 -10.000000, -10.000000 0.000000, 10.000000 0.000000

RenderTarget 作成時 D2D1_RENDER_TARGET_PROPERTIES dpiX/dpiY に 0 を与えると
デフォルトの 96dpi になります。
96dpi の時、長さは pixel 相当となり、dpi 値を増やすとピクセル密度が高くなります。
相対的に同じサイズの RenderTarget に描かれる図形は大きくなります。

dpi を考慮した実際に描画される大きさと、Tessellate() 2番目の数値によって
生成される Triangle の数が決められています。


関連エントリ
Direct2D (5) ID2D1Geometry を使う
Direct2D (4) Direct2D の描画
Direct2D (3) 互換性の検証と Vista で Direct2D
Direct2D その(2) インターフェース
Direct2D と Direct3D10.1 の下位互換



ADTEC の小型プロジェクター AD-MP15A を手に入れました。

ADTEC AD-MP15Aシリーズ Bit LED Projector

わずか 15ルーメンで解像度は VGA 640x480。
ビデオ入力だけでなく VGA 端子もあるので PC 画面も映すことが出来ます。
VGA 入力信号は 640x480 または 800x600 に対応。

周りが暗く、かつ近い距離だと思ったよりもきれいに出ます。
ピントさえ合えば画像はくっきり。
30cm の距離で実測 8.2inch くらい。
夜明かり無しの暗闇だったら 1.6m で 39inch 程度。一応みれました。

AD-MP15A

↑PS3 の画面をビデオ入力で。背景がゆがんでるのはスクリーンがただの封筒だから。

周囲の明るさにかなり依存します。
黒い領域=スクリーンの色 となるわけですが、普段スクリーンが白に見える部屋で
あればそれはそのまま白です。
投影映像にはそれを打ち消して、スクリーンの色が暗く見えるだけの明るさが
必要となります。そのためには

・周囲を暗くする
・プロジェクタをスクリーンに近づける

のどちらかです。
また映像ソースにも依存するので、明るいシーンだと大丈夫でも背景が真っ黒な
暗いシーンになると見にくくなります。

・夜や暗い部屋で使用する
・バッテリー駆動で持ち歩けて VGA 入力のある超小型モニタとして
・スクリーンに非常に近づけて小さい画面を一人で見る
・比較的明るい画面が中心

といったケースが合っているかもしれません。

フォーカス調整可能な範囲が決まっています。
試したところ、だいたい 16cm~160cm くらいでした。

16cm = 4.5inch
30cm = 8.3inch
58cm = 15inch
95cm = 22inch
160cm = 39inch

小さいけれどそれなりに熱を持つのかファンが入っているようです。
音はわずかに聞こえる程度でほとんど気になりません。

本体サイズは非常に小さくバッテリーも内蔵して 147g。
バッテリー駆動時間は 40分。

AD-MP15A
↑側面にコネクタ類あり

右の赤いのはたまたま近くにあったサイズ比較用(?)のビジュアルメモリ(Dreamcast)で、
付属品ではないです。

コネクタやスイッチは全部側面にあります。裏はバッテリーカバー。
映像入力コネクタは独自なので専用のケーブルも必要となります。
ケーブルはどれも非常に細くできており取り回ししやすくなっています。

AD-MP15A
↑付属のケーブルと専用コネクタ

パッケージには三脚も同梱されています。
全体的に軽いので、ケーブルに引っ張られるとすぐに動いてしまうのは仕方ないところ。

スピーカーも内蔵されておりヘッドホン端子もありますがボリューム調整は出来ません。

個人的には気に入りました。
小さいし使いどころが限られてくるけど、今までとは違った活用方法が考えられます。
プロジェクターといえばこう使う、といった先入観さえ無ければ非常におもしろいと思います。
今後は同様の小型プロジェクタが登場して結構早いペースで進化するのではないでしょうか。



すでにモニターテストの受付が始まっていますが、エリア外の人も直接電話で
申し込めば端末を購入することができます。
ユーザーサポートセンターに問い合わせるも混雑してるようでなかなかつながらず、
何度か待たされつつ申込書の送付を頼むことが出来ました。

UQ WiMAX

気になっていたのはこの部分。モニター募集のところに

>発送: 2009年2月26日(木)~2009年3月中旬にかけてデータ通信カードを順次
>発送いたします。

発送が 3月中旬までかかると書いてあります。

念のためきいてみると、購入者には優先して発送するので大丈夫だそうです。
ただしサービス開始の 2/26 必着にはならず、26 日以降に発送するため数日は
余裕を見てくださいとのことでした。



GPU の活用によりラスタライズはたいしたウエイトを占めておらず、CPU の仕事は
与えるデータの構築だけとなりました。それゆえ Direct2D はただの描画命令の
集まりにはなっていません。
ベクター描画の途中に介入し、ベクトルの情報のままデータを扱うことが可能です。
Direct2D が単なる GDI の置き換えではなく、可能性を秘めているのはこの Geometry
ではないかと思っています。

実際に ID2D1Geometry がどのようなものか興味あるので、機能を 1つ 1つ追いかけて
みました。

● Path の作成

Path の作成は下記の通り。

ID2D1Factory*	iFactory;
ID2D1PathGeometry*	iPath;
iFactory->CreatePathGeometry( &iPath );

ID2D1GeometrySink*	isink;
iPath->Open( &isink );
isink->SetFillMode( D2D1_FILL_MODE_WINDING );
isink->BeginFigure( startpos, D2D1_FIGURE_BEGIN_FILLED ); // = SVG M
isink->AddLine( pos1 );	    // = SVG L
isink->AddBezier( D2D1::BezierSegment( c2, c3, c4 ) );	 // = SVG C
 ...
isink->EndFigure( D2D1_FIGURE_END_CLOSED );   // = SVG Z
isink->Close();

ID2D1GeometrySink::Close() (ID2D1SimplifiedGeometrySink::Close()) は
SVG 等の Z コマンド相当かと思っていたら EndFigure( D2D1_FIGURE_END_CLOSED )
の方でした。

D2D1_FIGURE_END_OPEN     そのまま
D2D1_FIGURE_END_CLOSED   BeginFigure() の位置に直線で接続(閉じたパスにする)

ID2D1GeometrySink::SetFillMode() では塗りつぶしのルールを決定します。
SVG 等の vector graphics にある evenodd / nonzero に相当します。

・D2D1_FILL_MODE_ALTERNATE
 "evenodd" 相当。外からのレイが交差した回数で決定します。
 交差回数が偶数なら外、奇数は中。パスの回転方向は影響しません。

・D2D1_FILL_MODE_WINDING
 "nonzero" 相当。外からのレイが交差するとき、交わった線の方向を考慮します。
 たとえば閉じているパスが仮に右回りなら +1、左回りなら -1、結果が 0 なら外。
 つまりパスの回転方向を見ており、逆回転はくり抜き。


● Sink

Sink は値の追加、データをためることだけが出来るバッファです。
Add 系命令のみで Get はありません。
ID2D1Geometry では各オペレーションに対して受け皿である Sink を渡します。

最初にただの box を作ってみます。

ID2D1RectangleGeometry*	iRect;
iFactory->CreateRectangleGeometry(
	D2D1::RectF( 0.0f, 0.0f, 60.0f, 50.0f ), &iRect );

この box を元に、線を太らせる処理を行います。
もともとは描画時に太い幅 (strokeWidth) の線で描画するための変換ですが、
描画前に Path として受け取ることが出来るわけです。

// 変換結果を格納する ID2D1PathGeometry を作る
ID2D1PathGeometry*	iPath2;
iFactory->CreatePathGeometry( &iPath2 );

// ID2D1PathGeometry の sink に変換結果を受け取る
ID2D1GeometrySink*	isink;
iPath2->Open( &isink );
// iRect の変換結果を isink へ
iRect->Widen( 10.0f, NULL, NULL, isink ); // strokeWidth= 10.0f
isink->Close();

実際に描画したのがこちら。

Direct2D sample

↑左から順に
 ・元の Box
 ・元の Box を幅 10 の線で描画したもの
 ・幅 10 の描画を Path で受け取ったもの
 ・幅 10 で Path 化したものをさらに 幅 4 の線で描画したもの

// 描画コード
iRenderTarget->SetTransform( D2D1::Matrix3x2F::Translation( 20.5f, 20.5f ) );
iRenderTarget->DrawGeometry( iRect, iBrush0 );

iRenderTarget->SetTransform( D2D1::Matrix3x2F::Translation( 120.5f, 20.5f ) );
iRenderTarget->DrawGeometry( iRect, iBrush0, 10.0f );

iRenderTarget->SetTransform( D2D1::Matrix3x2F::Translation( 220.5f, 20.5f ) );
iRenderTarget->DrawGeometry( iPath2, iBrush0 );

iRenderTarget->SetTransform( D2D1::Matrix3x2F::Translation( 320.5f, 20.5f ) );
iRenderTarget->DrawGeometry( iPath2, iBrush0, 4.0f );

RenderTarget には直接描画可能な DrawRectangle() 命令があります。
Direct2D では内部的には全く同じように

(1) CreateRectangleGeometry() 等でプリミティブを作成
(2) Widen() など与えたオプションによって必要な変換を行う
(3) Tessellate() で ID2D1Mesh 化
(4) 最終的にすべて ID2D1RenderTarget::FillMesh()

という手順で描画しているだけかもしれません。この場合一番低レベルな描画は
FillMesh() になります。
ただし現在 FillMesh() での直接描画が成功しておらず、これらは予想の範囲を
超えておりません。

もし予想が合っているのなら、Geometry を扱うことでより柔軟なデータ管理ができます。
たとえば比較的 Mesh に近い状態のままキャッシュすることで、高速化や再利用が
可能となるでしょう。毎回 stroke を指定して Draw するよりも Geometry か Mesh
化しておいた方が高速だと思います。


●変換形状の読み出し

Geometry の中身は隠蔽されており、パスの座標値などデータを直接さわることが
できなくなっています。
ところが SDK には、テセレートされたアウトラインフォントの形状を取り出して立体化し、
3D ポリゴンとして描画するサンプルプログラム Interactive3dTextSample があります。

その仕組みはこうです。

(1) Sink 互換のインターフェースを自分で定義しておく
(2) Geometry の Tessellate() 等に渡して変換結果を受け取る

たとえば ID2D1Geometry::Tessellate() であれば、変換後の結果を格納するために
内部で ID2D1TessellationSink::AddTriangles() を呼び出しています。
互換インターフェースで AddTriangles() を定義しておけばトラップ可能となるわけです。

このように、Tessellate() など Geometry で変換した座標値をアプリケーションで
使うことができます。


関連エントリ
Direct2D (4) Direct2D の描画
Direct2D (3) 互換性の検証と Vista で Direct2D
Direct2D その(2) インターフェース
Direct2D と Direct3D10.1 の下位互換



「 Hyperでんち 」 の古くなっていたいくつかの項目を更新しました。

DirectX 一覧
GPU 年表

DirectX 一覧には Direct3D11 を追加 (リリース前なので未完)
GPU 年表は Mobile の項目を分離。

表を見てわかるとおり Mobile はちょうど今が固定機能パイプラインで DirectX7 世代。
その次は Unitifed Shader が当たり前で、一気に Direct3D10/ShaderModel4 世代と
なる可能性があります。

PowerVR SGX はすでに Unified Shader ベースであり、Intel US15W の GMA500 や
OMAP3 をはじめいくつかのデバイスで使われています。
Qualcomm の MSM に搭載されていた ATI Imageon も、新たに Xbox 360 同様の
Unified Shader Architecture を元に開発が行われていたようです。

AMD Licenses 3D Graphics Core Technology to QUALCOMM, Delivering The Ultimate Visual Experience? to Tomorrow’s Phones

GeometryShader が存在するかどうかわかりませんが Shader ベースであることは確かです。
PowerVR SGX 同様、Vertex/ Pixel の演算ユニットを共有できること、
Graphics 以外への応用が容易なこと、設計時に演算能力をスケーラブルに変更できる
ことがメリットであると考えられます。

Wikipedia Imageon

上記 wikipedia のページには Z460 という新しい Imageon core の名前が載っています。
直接 AMD の pdf 資料へのリンクもあります。登場は 2009 年以降とのこと。

これらのデータより、HTC Diamond 等に使われている現行 MSM7201A の 3D core は
ATI Imageon 2380 シリーズをベースにしたものだと考えられます。

2006/1/30 ImageonR 2380 および ImageonR 2388 で驚くべきゲームおよびマルチメディア体験を可能にし、ハンドヘルド 3D グラフィックスの水準を引き上げる ATI

AMD Imageon 2388/2380

公式ページの資料が参考になります。
Direct3DMobile では使えないものの CubeMap など一通り機能がそろっているようです。
D3D8 相当のスキニング用固定 Matrix Palette もありますが、残念ながら D3DM 側に
対応する機能がありません。
頂点演算は float で行われており、やはり NATIVEFLAOT で正解でした。
違いは VRAM のみで、2388 が内蔵 8MB、2380 が外付けで 16MB 以上に対応とのこと。


関連エントリ
Intel GMA500 のスペックについて考える。続き (2)
Intel GMA500 の機能と性能と Aero
Qualcomm と 3D
HTC Touch Diamond で Direct3DMobile その(11) 問題まとめその他



せっかくなのでもう少し送受信 API 周りを解説してみます。
前回 列挙して各デバイスのパスがわかったので、あとは Open して実際の通信を行います。
問題は Bluetooth stack によって使用可能な API が異なっていることです。

// デバイスの Open
HANDLE	hdev= CreateFile(
				path,
				GENERIC_READ|GENERIC_WRITE,
				FILE_SHARE_READ|FILE_SHARE_WRITE,
				NULL,
				OPEN_EXISTING,
				FILE_FLAG_OVERLAPPED,
				NULL
			);

if( !HidD_GetPreparsedData( hdev, &Ppd ) ){
	Close();
	return	FALSE;
}

HIDP_CAPS	caps;
if( !HidP_GetCaps( Ppd, &caps ) ){
	Close();
	return	FALSE;
}

// 必要なバッファサイズを調べておく。これを元にキューを作る。
InputByteLength= caps.InputReportByteLength;
OutputByteLength= caps.OutputReportByteLength;


● API テスト

コマンド送信に使う API を選択します。

WindowsVista 標準の bluetooth Stack では HidD_SetOutputReport() を使います。
ところが Toshiba bluetooth Stack / Bluesoleil ではこの命令が使えませんでした。

このあたり、各 stack による違いは下記のエントリでまとめています。
Toshiba bluetooth stack と Bluesoleil 対応

// テストコマンドを送信、直後の ReadFile() で Timeout 判定
if( !HidD_SetOutputReport(
		hDev,
		command,
		cmdlength
			) ){
	return	FALSE;
}

まず最初に HidD_SetOutputReport() を使って何らかのリターンがあるコマンドを
送信してみます。

この API が直接エラーを返す場合は WriteFile() の選択で決定です。ただしエラー
を返してくれるのは Toshiba Stack の場合だけ。Bluesoleil v5 ではエラー無しで
送信も出来ません。

判定の手順

(1) HidD_SetOutputReport() でリターンのあるコマンドを送信してみる。

(2) HidD_SetOutputReport() がエラーを返す場合は WriteFile() を選択で決定。
  Toshiba Bluetooth stack の場合。

(3) エラーが返らない場合は直後に ReadFile() でデバイスの返答を待ちます。

(4) 結果が返ってきたら HidD_SetOutputReport() を選択。Windows stack。

(5) Timeout 判定。一定時間待っても結果が来なければ Bluesoleil。
  送信は WriteFile() を選択する。


●データ送信

HidD_SetOutputReport() には送信するデータを直接渡すことが可能です。
たとえば 4byte なら 4byte のみで構いません。
WriteFile() を使う場合は、必ず OutputReportByteLength 分のバッファを作って
から転送を行います。

char*	bufp= Queue.WriteAlloc();
assert( length <= OutputByteLength );

if( !bufp ){
	// error
	return;
}
memcpy( bufp, command, length );

memset( &WriteOverlap, 0, sizeof(OVERLAPPED) );
WriteOverlap.hEvent= handle;
if( !WriteFileEx(
			hDev,
			bufp,
			OutputByteLength,
			&WriteOverlap,
			_WriteFileCB ) ){
	int	err= GetLastError();
	// error
}

このあたりも、成功するまで何度も試して調べた部分です。
Windows7 など Bluetooth のサポートも強化されるので、おそらく今後は
Windows stack の方が標準になっていくものと考えられます。
そのうちきっと HidD_SetOutputReport() だけ済む時代が来るでしょう。


関連エントリ
複数の HID から情報を読み出すには
Toshiba bluetooth stack と Bluesoleil 対応



以前公開したプログラムに関する質問のメールがほぼ同時に 2通ありました。
どちらも同じ内容でしたので、少々昔のプログラムを引っ張り出してみます。
「接続した複数の HID から情報を読み出すにはどうしたらいいのか」
制作時に参考にしたのはサンプル「\WinDDK\6000\src\hid\hclient\pnp.c」です。

HMODULE hModule= LoadLibrary( "hid.dll" );
HMODULE sModule= LoadLibrary( "setupapi.dll" );
GetProcAddress( hModule, "HidD_GetAttributes" ) )
GetProcAddress( hModule, "HidP_GetCaps" ) )
GetProcAddress( hModule, "HidD_FreePreparsedData" ) )
GetProcAddress( hModule, "HidD_GetPreparsedData" ) )
GetProcAddress( hModule, "HidD_SetOutputReport" ) )
GetProcAddress( hModule, "HidD_GetHidGuid" ) )
GetProcAddress( sModule, "SetupDiGetClassDevsA" ) )
GetProcAddress( sModule, "SetupDiEnumDeviceInterfaces" ) )
GetProcAddress( sModule, "SetupDiGetDeviceInterfaceDetailA" ) )
GetProcAddress( sModule, "SetupDiDestroyDeviceInfoList" ) )

以後列挙して目的のデバイスを探しています。

GUID	hidGuid;
HidD_GetHidGuid( &hidGuid );

HDEVINFO	hdevinfo;
hdevinfo= SetupDiGetClassDevs(
		&hidGuid,
		NULL,
		NULL,
		DIGCF_PRESENT
		|DIGCF_DEVICEINTERFACE
		|DIGCF_ALLCLASSES
	);


SP_DEVICE_INTERFACE_DETAIL_DATA*	funcClassData= NULL;

int	retCode= TRUE;
for( int i= 0 ; i< DEVICELIST_LIMIT ; i++ ){
	SP_DEVICE_INTERFACE_DATA	deviceInfoData;
	memset( &deviceInfoData, 0, sizeof(SP_DEVICE_INTERFACE_DATA) );
	deviceInfoData.cbSize= sizeof( SP_DEVICE_INTERFACE_DATA );
	if( SetupDiEnumDeviceInterfaces(
			hdevinfo,
			0,
			&hidGuid,
			i,
			&deviceInfoData
			) ){
		ULONG	reqLength= 0;
		SetupDiGetDeviceInterfaceDetail(
				hdevinfo,
				&deviceInfoData,
				NULL,
				0,
				&reqLength,
				NULL );

		funcClassData=
			reinterpret_cast<SP_DEVICE_INTERFACE_DETAIL_DATA*>(
					dAlloc( reqLength ) );
		if( sizeof(void*) == 4 ){
			funcClassData->cbSize= 5;
		}else{
			funcClassData->cbSize= 
				sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
		}
		if( !funcClassData ){
			retCode= FALSE;
			break;
		}
		if( !SetupDiGetDeviceInterfaceDetail(
				hdevinfo,
				&deviceInfoData,
				funcClassData,
				reqLength,
				&reqLength,
				NULL ) ){
			retCode= FALSE;
			break;
		}
		if( _OpenTestHidDevice( funcClassData->DevicePath,
					VENDOR_ID, PRODUCT_ID ) ){
			// 目的のデバイスを保存しておく
			CheckDeviceOpen<DeviceType>( funcClassData->DevicePath );
		}
		if( funcClassData ){
			dFree( funcClassData );
			funcClassData= NULL;
		}
	}else{
		if( GetLastError() == ERROR_NO_MORE_ITEMS ){
			break;
		}
	}
}

SetupDiDestroyDeviceInfoList( hdevinfo );
if( funcClassData ){
	dFree( funcClassData );
	funcClassData= NULL;
}

VENDOR_ID, PRODUCT_ID はデバイスマネージャーのプロパティから
詳細→ハードウエア ID で調べることが出来ます。
このあたりのコードは力業です。たしか作成時 1つ 1つ試しながらだったので、
あまりきれいじゃないと思いつつ書いていた覚えがあります。

int _OpenTestHidDevice( const char* path, int vid, int pid )
{
	HANDLE	hdev= CreateFile(
					path,
					GENERIC_READ|GENERIC_WRITE,
					FILE_SHARE_READ|FILE_SHARE_WRITE,
					NULL,
					OPEN_EXISTING,
					0,
					NULL
				);
	int	found= FALSE;
	if( hdev == INVALID_HANDLE_VALUE ){
		return	FALSE;
	}

	HIDD_ATTRIBUTES	attr;
	if( HidD_GetAttributes( hdev, &attr ) ){
		if( attr.VendorID == vid && attr.ProductID == pid ){
			found= TRUE;
		}
	}
	CloseHandle( hdev );
	return	found;
}

検索して条件に合うデバイスをリストに保持し、それぞれ個別に通信を行っています。
CheckDeviceOpen() は内部でリストに追加しているだけです。
見つかった最初のデバイスだけ見ているか、それとも全部列挙しているかの違い
だけだと思います。

実際のデータ送受信はこのあとですが、Bluetooth Stack の違いを吸収するために
最初に API のテストや通信タイムアウトの判定などを行っています。
ここでの注意点は、複数のデバイスがあっても API の判定は最初の一度だけで
いいということ。

最初にリリースしたバージョンでは新たなデバイスが接続されるたびに API テストが
走ってしまい、すでにつながってるデバイスの通信も一時中断されてしまう問題が
ありました。このあたりの処理をきれいに組み込めずに少々苦労した覚えがあります。

また定期的に接続されているデバイスを確認して、動的な接続&切断に備えています。
すでにオープンしたデバイスかどうかの区別が必要となりますが、結局保持している
パスの文字列比較というもっとも安易な方法で済ませました。



●描画

Direct2D の描画は下記の 4通りあります。

・線描画
・塗りつぶし
・イメージ描画
・文字

ID2D1RenderTarget に対して適用できるものです。
それぞれグループ分けと、グループごとに指定出来る共通プロパティをまとめます。

◎線描画
・DrawEllipse (楕円)
・DrawGeometry (パス)
・DrawLine (直線)
・DrawRectangle (四角)
・DrawRoundedRectangle (角が丸い四角形)

    ID2D1Brush
        色や模様

    Stroke
        線の太さ (strokeWidth)
        ID2D1StrokeStyle 線のスタイル
	 (端の形状、丸めたりとんがったり。線の形状、点線やダッシュ点線など)


◎塗りつぶし
・FillEllipse
・FillGeometry
・FillMesh (ID2D1Mesh の描画)
・FillOpacityMask (bitmap でマスク付き塗りつぶし)
・FillRectangle
・FillRoundedRectangle

    ID2D1Brush
        色や模様


◎イメージ描画
・DrawBitmap
・(FillOpacityMask)


◎文字
・DrawGlyphRun (IDWriteFontFace)
・DrawText (IDTextFormatIDWriteTextFormat) 2009/02/06 修正
・DrawTextLayout (IDWriteLeyoutIDWriteTextLayout) 2009/02/06 修正

    ID2D1Brush
        色や模様


●形状

形状の操作は Geometry で行います。
その場合も描画同様のプリミティブが使用できます。
多くの操作では同時に 3x2 Matrix (D2D1_MATRIX_3X2_F) を与えることが可能で、
形や座標判定用の命令も用意されています。

・ID2D1EllipseGeometry
・ID2D1PathGeometry
・ID2D1RectangleGeometry
・ID2D1RoundedRectangleGeometry
・ID2D1TransformedGeometry

CombineWithGeometry
 複数の Geometry 同士の合成や演算を行います。

Tessellate
 テセレートし、トライアングル化した状態にアクセスできるようにします。

Widen
 線として描画する場合に、線を太らせた形状を取り出します。


●Mesh

ID2D1Mesh は直接描画データとしてポリゴン (Triangle) を与える場合に使用します。
非常にデバイスに近い低レベルな状態でデータをもてるものと思われます。
おそらく Geometry → Tessellate も Mesh 相当への変換です。


●DirectWrite

DirectWrite はフォント扱うモジュールとして完全に独立しているようです。
描画対象として Direct2D に拘る必要はなく Direct3D など何でも良いとのこと。
Direct3D11 との組み合わせもおそらく出来るでしょう。

当初は Direct2D のデータに変換して出力しているのかと思いましたが、独自の
レンダラを持っているようです。よく考えると ClearType などフォントに依存する
部分が大きいので当然かも。

フォントを扱うためにはデータとして形状を読み込むだけでなく、同時にレイアウト
も必要となります。このあたりのインターフェースは次の通り。

IDWriteFormat → IDWriteLeyout
IDWriteTextFormat → IDWriteTextLayout

Direct2D のテキスト描画も上記 2つの Interface に対応しています。
また文字単位の形状を直接描画することも可能で、それが GlyphRun 系の命令と
なっているようです。


今回はあまりきちんと試しておらず、メモ程度の内容となっています。
間違いなどあったら訂正していきます。


関連エントリ
Direct2D (3) 互換性の検証と Vista で Direct2D
Direct2D その(2) インターフェース
Direct2D と Direct3D10.1 の下位互換



こちらで書いたように、Windows7 では Direct3D11 と同じく
Direct3D10.1 でも下位の GPU が新たにサポートされることになりました。

実際にデバイスを作成して試してみました。
今度は Direct2D ではなく、直接 Direct3D10.1 ( D3D10CreateDevice1() ) を
使用しています。

Direct3D10.1
  D3D10_FEATURE_LEVEL_10_0
  D3D10_FEATURE_LEVEL_10_1
  D3D10_FEATURE_LEVEL_9_1
  D3D10_FEATURE_LEVEL_9_2
  D3D10_FEATURE_LEVEL_9_3

すべて成功しました。
これは、今までの Windows Vista + Direct3D10.0 / Direct3D10.1 では
出来なかったことなのです。

まず Windows SDK for Windows 7 BETA を install しなければ、
D3D10_FEATURE_LEVEL_9_1 ~ D3D10_FEATURE_LEVEL_9_3 のシンボルが
定義されていないということ。
Vista 以前で使える現在の DirectX SDK ではコンパイル出来ませんでした。
DirectX SDK Nov2008 の help では、ヘッダファイルにシンボルが無いのに次の
ように書かれていたりします。

D3D10_FEATURE_LEVEL_9_1 
 This value is in the header, but this feature is not yet implemented. 
D3D10_FEATURE_LEVEL_9_2 
 This value is in the header, but this feature is not yet implemented. 
D3D10_FEATURE_LEVEL_9_3 
 This value is in the header, but this feature is not yet implemented. 

この Windows7 用の FEATURE テストプログラムを WindowsVista 上で走らせて
みたところ、全く同じようにデバイスの生成が成功してしまいました。

考えられるのは Windows Vista でも Direct3D SDK の Direct3D11 beta ランタイム
を導入した時点で使えるようになっていたのではないかということ。
Direct3D11 では実際に D3D9_1~3 の下位の FeatureLevel に対応しています。
Direct3D10.1 で同じように利用できてもおかしくないのかもしれません。


過去の DirectX はすべて互換性を保ちながら移行するのが当たり前でした。

 実際に Direct3D 9 は Direct3D 8 のハードウエアでも動作します。

デバイスは作れますし API も呼べますしシェーダーモデル 1 を扱うことが出来ます。
Direct3D9 の特徴であるシェーダーモデル 2~3 は利用できませんが、それは
Direct3D10.1 で Level9 を扱っても全く同じこと。

さらに前、たとえば DirectX7 までは毎年メジャーナンバーがアップデートされる
ことになっていました。わずか 1年で GPU が使えなくなっていたら、とても
ついて行くのが大変ですよね。

DirectX/GPU 年表
DirectX list

互換性を完全に切り捨てたのは Direct3D9 → Direct3D10 のタイミングだけなのです。
ドライバモデルが変わったり OS と融合されたりと、内部的に困難な理由が
いろいろあったのではないかと考えられます。

だから今回 Windows7 と同じタイミングで、Windows7 だけでなく Vista でも
同等の機能がサポートされるのは画期的なことかもしれません。

でも古くから DirectX を触ってるものからすれば画期的でも何でもなくて、
互換性の切り捨てを撤回して、やっぱり以前と同じように使えるようにしました、
といってるだけに見えるのです。


● Windows7 SDK の導入

Windows7 SDK があれば、Vista でも Direct3D10.1 で Direct3D9 GPU 向けの
プログラムを作成できることがわかりました。
DirectX SDK の更新と思って Windows SDK for Windows 7 BETA を早めに導入して
おくのもありかもしれません。
あらかじめ DirectX SDK November 2008 をインストールしておく必要があります。

VisualStudio2008 を起動する前に、スタートメニューから
 Visual Studio Registration → Windows SDK Configuration Tool
を起動して SDK を切り替えておきます。

include パスの順番には注意が必要です。
 Tools → Options → Projects and Solutions → VC++ Directories
 Win32 / Include files

$(DXSDK_DIR)\include よりも上に $(WindowsSdkDir)\include が来るようにします。
DirectX SDK の install で直接 C:\Program Files ~とパスが書き込まれている
ことがあるので、その行は削除して $(DXSDK_DIR)include に置き換えておくことを
お勧めします。
lib も同じようにします。


● Vista で Direct2D が動く

Windows7 SDK beta があれば Direct2D のプログラムを作ることが出来ます。
実際に走らせるには Windows7 beta をインストールした PC が必要です。

Direct2D は結局 Direct3D10.1 を呼び出しているだけのはず。
Direct3D10.1 で下位 GPU もなぜか動くことがわかったので、もしやと思って
Windows7 beta から d2d1.dll だけ vista に持ってきたら拍子抜けするほどあっさり
動作しました。

とりあえず build したプログラムと同じ場所に d2d1.dll と DWrite.dll だけ置いておけば
Vsita でも Direct2D のプログラムを作って走らせることが出来るようです。


関連エントリ
Direct2D その(2) インターフェース
Direct2D と Direct3D10.1 の下位互換



ヘッダを読みながら整理してみました。


● RenderTarget

基本となるのが ID2D1RenderTarget です。いわゆるキャンバスのこと。
各種描画命令を使って RenderTarget の上に絵を描いていきます。
次のバリエーションがあります。

ID2D1RenderTarget
 → ID2D1BitmapRenderTarget
 → ID2D1HwndRenderTarget
 → ID2D1GdiInteropRenderTarget
 → ID2D1DCRenderTarget

それぞれ Bitmap (Texture) に描いたり、そのままウィンドウに描いたりできるということ。
ラインなどプリミティブを描画する命令もありますが、パス(Geometry) を使ったり
DirectWrite を併用してテキストも描くことができます。
描画時に SetTransform() で渡した 3x2 matrix が適用されるようです。
2D の回転、拡大縮小、平行移動などが可能。


● Geometry

Direct2D を特徴付ける存在といえるかもしれません。
ID2D1Geometry はパスや形状などジオメトリを格納します。
無理矢理例えれば Direct3D の VertexBuffer/IndexBuffer のような存在でしょうか。

パスを構成する要素、ラインやベジェ(2次、3次)、Arc を使用することができます。
Geometry の段階で Transform したり合成したり交差を考慮して演算したり、
といった操作が可能。結構自由度が高そうです。
ID2D1Geometry を継承しているインターフェースは次の通り。

ID2D1Geometry
 → ID2D1RectangleGeometry
 → ID2D1RoundedRectangleGeometry
 → ID2D1EllipseGeometry
 → ID2D1GeometryGroup
 → ID2D1TransformedGeometry
 → ID2D1PathGeometry


● Brush

ID2D1Brush は描画時に使用する状態を保持します。マテリアルやレンダーステートを
保持した StateBlock のようなイメージでしょうか。GDI にもあります。
下記の種類があります。

ID2D1Brush
 → ID2D1BitmapBrush
 → ID2D1SolidColorBrush
 → ID2D1LinearGradientBrush
 → ID2D1RadialGradientBrush


大きいのがこの 3つのグループです。
その関係は「 Geometry を Brush を使って RenderTarget に描画する 」ということ。


ID2D1RenderTarget, ID2D1Geometry, ID2D1Brush はどれも ID2D1Resource に
属しています。ID2D1Resource を継承しているインターフェースは他にもあります。

ID2D1Resource
 → ID2D1Bitmap
 → ID2D1GradientStopCollection
 → ID2D1Brush
 → ID2D1StrokeStyle
 → ID2D1Geometry
 → ID2D1Mesh
 → ID2D1Layer
 → ID2D1DrawingStateBlock
 → ID2D1RenderTarget

ID2D1Resource に属していないのは ~ Sink 系と ID2D1Factory のみ。

ID2D1SimplifiedGeometrySink
ID2D1GeometrySink
ID2D1TessellationSink

~Sink はまだよく理解していませんが、Geometry を操作する場合のイテレータの
ような存在ではないかと思われます。


関連エントリ
Direct2D と Direct3D10.1 の下位互換


● Direct2D とは

Direct3D に似ていますが別物です。
DirectDraw とも似ていますが役割が異なります。

現在 DirectX SDK ではなく Windows SDK for Windows 7 の方に含まれます。
下記のページより Windows7 SDK の beta 版を入手することが可能です。

Microsoft Windows SDK for Windows 7 and .NET Framework 3.5 SP1: BETA

Direct3D11 と違い WindowsVista では動かないので、動作確認には Windows7 beta が
必要となります。ドキュメントはこちらから。

MSDN Direct2D

Direct2D は Direct3D よりも上位のレイヤに位置します。
レンダーバックエンドとして Direct3D を活用しつつ、ベクターや bitmap 等の 2D
レンダリングを可能とします。Direct3D や DirectDraw のようなハードウエア寄りの
プリミティブではなく、GDI のような高度な描画命令を多数有しています。
SVG/FXG/XAML のようなベクターグラフィックスに対応し、ポリゴンを表示するかわり
に美しい 2D をレンダリングすることに注力した API セットであるといえるでしょう。

現在の Direct2D が利用するのは Direct3D10.1 です。
10.1 といえば WARP によって高速なソフトウエアラスタライズも可能だし、リモート
デスクトップ等で利用可能なCommand Remoting だって使えます。

Direct2D の狙いはまさにここにあります。
ハードウエアアクセラレーション、ソフトウエアラスタライザ、そしてクライアント
サイドのレンダリングなど、2D の描画でもこれらの恩恵を受けられるようになります。


● Direct2D の謎

最初に疑問に思うのは Direct3D10.1 だと使えるハードがかなり限られてくるのでは
ないかということ。
従来 Direct3D10 は専用のビデオカードしか使えず D3D9 世代の GPU とは互換性が
ありませんでした。逆に Direct3D11 の方が下位互換性が強化されており、D3D9 世代の
GPU でも動作します。そのあたりをまとめたのは こちら

どうやら D3D10 でも下位互換がサポートされることになった模様です。
たとえば一番新しい Direct3D SDK November 2008 のヘッダファイル D3D10_1.h を
見ても、下記の通り FEATURE LEVEL には 10 世代の GPU しか記載されていません。

typedef 
enum D3D10_FEATURE_LEVEL1
    {	D3D10_FEATURE_LEVEL_10_0	= 0xa000,
	D3D10_FEATURE_LEVEL_10_1	= 0xa100
    } 	D3D10_FEATURE_LEVEL1;

ところが Windows7 SDK beta 付属の D3D10_1.h を見てみると・・

typedef 
enum D3D10_FEATURE_LEVEL1
    {	D3D10_FEATURE_LEVEL_10_0	= 0xa000,
	D3D10_FEATURE_LEVEL_10_1	= 0xa100,
	D3D10_FEATURE_LEVEL_9_1	= 0x9100,
	D3D10_FEATURE_LEVEL_9_2	= 0x9200,
	D3D10_FEATURE_LEVEL_9_3	= 0x9300
    } 	D3D10_FEATURE_LEVEL1;

いつの間にか下位の FEATURE LEVEL が追加されています。
我慢を強いられた Direct3D10/Vista 世代はいったい何だったのでしょうか。

つまり Direct2D は Direct3D9 世代の古い GPU でもハードウエアアクセラレーション
がかかります。(実際に試してみました→後述)


さらに Direct2D は対応ハードウエアがなくてもソフトウエアラスタライザによって
動作可能と書かれています。これが WARP を指しているのか、それとも Direct2D が
さらに独自でラスタライザを有しているのかわかりません。

ハードウエアアクセラレーションといいつつも、おそらくジオメトリ部分は CPU の
割合がそれなりに高いのではないかと思われます。アンチエリアス等のピクセル合成
部分において GPU が活用されているのではないでしょうか。
Direct3D11 世代になればジオメトリ処理でも GPU の割合が高くなるかもしれません。

以前アウトラインフォントの GPU 描画に取り組んだのはジオメトリも GPU で処理させる
ことが目的でした。


もう一つ疑問に思ったのは Direct2D のインターフェースが最初から D2D1 と "1" が
ついていること。DXGI1 など他の API と同調するためなのか、"2" の登場
(Direct3D11 版?) を想定しているのかわかりません。
DirectWrite には "1" がついていませんでした。


●下位互換性の確認

3台とも Windows7 beta

・DesktopPC RADEON HD4850 (Direct3D10.1 ShaderModel4.1) (Aero 有効)
  ○ D2D1_RENDER_TARGET_USAGE_FORCE_HARDWARE_RENDERING
  ○ D2D1_RENDER_TARGET_USAGE_FORCE_SOFTWARE_RENDERING

・EeePC901 945GE GMA950 (Direct3D9 ShaderModel2.0) (Aero 有効)
  ○ D2D1_RENDER_TARGET_USAGE_FORCE_HARDWARE_RENDERING
  ○ D2D1_RENDER_TARGET_USAGE_FORCE_SOFTWARE_RENDERING

・VAIO type P US15W GMA500 (Direct3D9 ShaderModel3.0) (Aero 無効)
  × D2D1_RENDER_TARGET_USAGE_FORCE_HARDWARE_RENDERING
  ○ D2D1_RENDER_TARGET_USAGE_FORCE_SOFTWARE_RENDERING

Aero 無効かつ D3D10/11 全滅の GMA500 では HARDWARE_RENDERING だと
動きません。SOFTWARE_RENDERING では動作しました。
このことから HARDWARE/SOFTWARE のフラグ設定は機能しているものと思われます。
GMA950 の EeePC では両方動作しているので、ShaderModel2.0 でも Direct2D で
ハードウエアアクセラレーションが有効になっているのではないかと考えられます。
つまり Windows7 では Direct3D10.1 で ShaderModel2~3 が動いているということ。
直接 Direct3D10.1 を試した方が早かったかもしれません。


関連エントリ
Intel GMA500 のスペックについて考える。続き (2)
Windows7 リモートデスクトップと Direct3D
Direct3D11/DirectX11 (18) GPU を使ったアウトラインフォントの描画の(6)
Direct3D11/DirectX11 (5) WARP の試し方、Dynamic Shader Linkage
Direct3D11 Technical Preview D3D11の互換性、WARP Driver


WindowsMobile 用のソフトウエアキーボード touchkeysip に、
前から考えていた機能をいくつか追加しました。

ダウンロード
touchkeysip v1.10


●加速センサー対応

加速センサーに対応した専用バージョン touchkeysip GS をリリースしました。
アーカイブやインストーラを標準版と分けてあります。

Script 内でセンサー値を直接受け取ることが出来ます。
サンプル gsensorsip では本体を傾けることで画像をスクロールしているだけですが、
本体を傾ける向きで 日本語、英数、記号などの入力パネルを切り替えたり、
カーソルを移動させるといった応用も考えられます。
本体のアクションをきちんと処理するのは難しいですが、Script 処理次第では
何らかの動作で文字入力することも出来るかもしれません。


●ステータス領域のアイコン画像の切り替え

標準の SIP だと「あ」「A」など入力文字の状態が表示されているエリアです。
あらかじめ作成したアイコン画像を用意しておけば、状態に合わせて表示を切り替える
ことが出来るようになりました。

またキーボードをデザインした人が touchkeysip ではなく自前のアイコンを表示
したい、といった用途にも使えます。


●管理可能な画像を増量

アイコン表示追加に伴い、複数枚の画像の読み込みにきちんと対応しました。
従来は 2枚まで読み込めましたが、DisplayList に使えないなど中途半端な実装でした。
8 枚まで読み込めてかつ表示やステータスアイコンとして利用できます。
でも表示するデータはできるだけ 1枚にまとめた方が処理は高速だと思われます。


●タイマーの追加

加速センサー対応によって、従来のキーリピートとは別にポーリング用のタイマーが
必要となりました。よって複数のタイマー機能を使えるようにしています。
上限は容易に撤廃できますが API としてはとりあえず 4個まで。


●読み込める画像形式の追加

bmp 以外も読み込めるようになりました。jpeg など。
Script の命令自体は LoadBitmap のままです。
16bit RGB bitmap は直接読み込めないことがあるようです。


●サンプル

gsensorsip v1.00 はこれらの追加機能を実際に使用しています。
実用性は全く無く、あくまで機能のテスト用です。


●注意点

機能の追加や変更が多いため、互換性など何らかの問題がある場合は旧バージョン
v1.09 等を使用してください。
制作時に動作テスト可能な機種や環境も限られるため、必ずしも完全にテストが
行われているわけではない点にご注意ください。


関連エントリ
HTC Touch Diamond タッチセンサー API
WindowsMobile touchkeysip v1.07
WindowsMobile touchkeysip / ctrlswapmini



backlightwin を作るときに調べたもの。

MSDN IOCTL_VIDEO_QUERY_SUPPORTED_BRIGHTNESS

LCD デバイスを open します。

HANDLE	hlcd= CreateFile(
		_T("\\\\.\\LCD"),
		GENERIC_READ|GENERIC_WRITE,
		FILE_SHARE_READ|FILE_SHARE_WRITE,
		NULL,
		OPEN_EXISTING,
		0,
		NULL );

バックライトを何段階に調光可能か調べます。

const DWORD	levelbuffersize= 256;
unsigned char	levelbuffer[levelbuffersize];
DWORD		levelCount= 0;
DeviceIoControl(
		hlcd,
		IOCTL_VIDEO_QUERY_SUPPORTED_BRIGHTNESS,
		NULL,
		0,
		(LPVOID)levelbuffer,
		levelbuffersize,
		&levelCount,
		NULL
	);

levelbuffer にはバックライトの明るさテーブルが返ります。
たとえば VAIO type P なら 9 段階に調整可能で、levelbuffer には 9 個の値が
返ります。内容は下記の通り。

4 16 28 40 52 64 76 88 100

バックライトの明るさが 4% から 100% まで、上記の刻みで変更できることを意味
しています。levelCount は返したパラメータのサイズなので、結果として調整可能な
段階に等しくなります。

デバイス(PC)によってこの値も刻み方も変わるようです。
明るさの値 (DISPLAY_BRIGHTNESS の UCHAR ucACBrightness など) は unsigned char
を使うため、テーブルも最大 256 まで。
でも 0~100% の値だから 102 以上のサイズに意味があるのかどうかわかりません。

現在の明るさの読み出し。
DWORD	bytereturn= 0;
DISPLAY_BRIGHTNESS	bright;
DeviceIoControl(
		hlcd,
		IOCTL_VIDEO_QUERY_DISPLAY_BRIGHTNESS,
		NULL,
		0,
		(LPVOID)&bright,
		sizeof(DISPLAY_BRIGHTNESS),
		&bytereturn,
		NULL
	);


bright.ucACBrightness, bright.ucDCBrightness に現在の明るさが入ります。
明るさは IOCTL_VIDEO_QUERY_SUPPORTED_BRIGHTNESS で読み出したテーブルの
通り、必ず 4 , 16, 28, ... 100 の値のどれかになります。
ucDisplayPolicy は現在 AC か DC なのかフラグが設定されるはずですが、
VAIO type P では常に 2 (DC power) が入っています。

明るさを変更する場合。
DeviceIoControl(
		hlcd,
		IOCTL_VIDEO_SET_DISPLAY_BRIGHTNESS,
		(LPVOID)&bright,
		sizeof(DISPLAY_BRIGHTNESS),
		NULL,
		0,
		&bytereturn,
		NULL
	);

IOCTL_VIDEO_QUERY_DISPLAY_BRIGHTNESS で読み出した構造体をそのまま
書き換えて渡せばよいはずです。ucDisplayPolicy のフラグが 3 なら DC/AC
両方を意味するため、より確実に変更が可能となると思われます。
VAIO type P の場合そのままで大丈夫でした。

backlightwin v1.00

本当はボリューム変更や HID のキー周りも調べたのですが、結局何もしなくても
画面表示がないだけでボリューム変更のショートカットは有効でした。


関連エントリ
VAIO type P Windows7 beta とバックライト調整など



小型キーボードはたとえキーピッチが小さくなっても等幅の方がタッチタイプ
しやすいという仮説

VAIO type P のキーボードは十分な広さが確保されており、詰まったキーも少なく
押しやすく出来ています。
たまに右側 [_] のキーを押し間違えることがあるのは、おそらく直前まで 1年間
使ってきた EeePC にすっかり手がなじんでしまっているからです。
EeePC のキーボードはさらに横幅が狭く、変形している記号キーも type P より
1列分多くなっていました。

その前に使っていた Let's note R4 もそうだったし、小型のキーボードでは右側の
記号部分の幅が狭く変形しているものが少なくありません。

キーのタイピングは人によって異なるし好みもやり方も多数あると思いますので、
今回はあくまで個人的な内容のお話となっています。


●小型キーボード

こちら でも触れたように、キーボードはすべてのキーが等幅であれば
全体のスケールが小さくなっても意外にタッチタイピング出来ることがわかりました。
もちろんあくまで個人的な感想で、個人的な経験からの判断です。

その昔、普段記号キーも見ないで打っていたのでキーボードに結構拘っていました。
ノートPC もデスクトップと同じサイズでないと打てない、とか思い込んでいたし。

これなら打てる! とかなり気に入ったのが MobileGearII MC/R700 で、すぐに電源が
入って使える便利さに魅了されてその後すぐに MC/R530 を購入。
キーピッチは小さいもののすべてのキーが等幅でしっかりしており、非常に打ちやすい
ものでした。
最初から Ctrl/Caps Lock を交換する機能もあったし、ATOK も入っていたし、IME の
キー割り当ても変更できたのでカスタマイズ性も申し分ないです。

Jornada680~ はさらに一回り以上小さいハンドヘルド PC です。
小さいながらもキーは電卓ボタンではなくノートPC と同じ形をしています。
店頭で Jornada720 触ってみたらぎりぎりだけどホームポジションに全部の指が乗る
ことを発見。
キー配列は等幅で必要なキーがそろっており配列も自然。一部異なっている部分も
カスタマイズで変更可能な範囲でした。
入力はタッチタイピング可能で、議事録なども内容をそのまま記録しつつ、かつ
タイプしながら内容を整理してまとめられるくらいに余裕で打てました。
MobileGearII MC/R530 と Jornada720 は本当に長い間使い込んでいます。

それ以降ここまで使えた小型キーボードにはあまり出会っていません。
小型化のためにキーボードが変形していて、一部キーが他の場所にあったり部分的に
幅が狭くなっていたりするとなぜかだめなのです。慣れるまで非常に時間がかかるし、
慣れてもデスクトップ PC の普通のキーボードを使うときに間違えるようになるし。


●タッチタイピング

タッチタイプできるかどうかの境目は「知識」にあると考えています。
どこに手を置いてどの指にどのキーを割り当ててどうやって指を運んで戻すのか、
これらの基礎知識がないままキータイプを続けてもあまり上達しません。

見ないのにキーを打てるからくりは、すべてのキーをホームポジションからの
相対位置で覚えるからです。
重要なのは必ずホームポジションに手を置いておくこと。

ホームポジションから打ちたいキーに指を出し、一文字タイプしたらできるだけすぐ
ホームポジションに戻す。
この動作が基本で、あとは徐々に上達するごとにホームポジションを経由しない
指の運びを、おそらく決まったフレーズの組み合わせで覚えていくのだと思います。

たとえば [Y] のキーを押した状態から他のキーへ指を運ぶ動作はキーの数だけ存在
します。
この指の運びを、最初からすべてのキーの組み合わせの数だけ覚えるのはちょっと無理。

だけど常にホームポジションに戻ることで、各キーへの指の運びを1つだけ覚えれば
確実にキーを探せるというわけです。


●小型キーボードとタッチタイプ

小型キーボードでキーピッチが詰まった場合も、とりあえず形だけホームポジションに
手が乗れば何とかなります。
サイズが違うためキーの位置も変わっているはずですが、すべて等幅なら比較的容易に
補正することが出来ます。

それは指を出す方向は一緒だから。
伸ばす距離が変わるだけでキーの位置のあたりが比較的容易に付けられるというわけです。

だから一部のキーだけ変形してキーピッチが狭くなっていたりすると、指を運ぶ角度
が変わってしまいます。あるはずの方向に無意識に手を伸ばしても、キーを探すことが
なかなか出来ないのではないかと考えられます。

1. キー配列が変わっていて一部のキーが別の場所に移動している場合
  そのキーの位置は完全に独自に覚え直し

2. 一部のキーだけキーピッチが変わっている
  そのキーに対して指を伸ばす方向と距離が変わってしまう
  角度と距離の両方に対して補正が必要。

3. 全部等スケールで小さくなった場合
  キー間の距離が違っていても指を伸ばす方向は合っている。

よって 3つのケースでは、全部のキーが等幅の方が一番覚え直すこと、補正する部分が
少なくて済むのではないかと考えられます。

あくまで数字や記号キーを含めてすべてのキーをタッチタイプで入力する前提です。

同じ理由から、EeePC や ELECOM TK-UP84CP 等のように数字の段が微妙に左に
ずれているキーボードは、数字の入力でかなりタイプミスしました。

またせっかく登場した Happy Hacking Keyboard Professional の日本語版も、
最下段 Z X C~ の段が左にずれているので購入を躊躇してしまいます。(悔しい)

・最低限キー配列がそのままであること (シグマリオンや LOOX U のようにキーの位置が移動していない)
・キーピッチを小さくするなら全部まとめて小さくする (部分的に変えない)

これが個人的には理想の小型キーボードです。
だから HTC Shift はかなり気になる存在なのです。


おそらく英語キーボードのスペースに無理矢理日本語キーボードを押し込めた事情が
あるのだとは思います。
それでも全部のキーが等幅な小型キーボードも出して欲しいと切望します。


関連エントリ
VAIO type P Windows7 beta とバックライト調整など
DHARMAPOINT キーボード
Bluetooth キーボード Rboard for Keitai RBK-2000BT2
emobile EM-ONE 改造その2 ([es]もOK)
emobile EM・ONE USBキーボードの改造
東プレ Realforce 買った!



Debian (lenny) を使った設定メモ
Subversion が使えるところまで


●準備

Debian -- The Universal Operating System

左側の CD ISO images から "testing" distribution をダウンロード。
必要なのは debian-testing-i386-CD-1.iso だけ。


●VirtualBox

今回は VirtualBox 2.1.2 を使っています。

VirtualBox

(1) 仮想マシン作成
  新規→ OSタイプ Linux / Debian を選択 → RAM default の 256MB

(2) 仮想ハードディスク
  新規 → 可変サイズのストレージ
    → 容量必要に応じて(今回はdefault 8GBを選択) → 完了

swap を使うなら、swap 専用に仮想ハードディスクを分けて作成しておいた方が
良いと思われます。

(3) 作られた仮想マシンを選択して「設定」
   CD/DVD-ROM
     → CD/DVD ドライブのマウントにチェック
     → ISOイメージファイルを選択
     → 仮想メディアマネージャに debian-testing-i386-CD-1.iso を追加&選択
   ネットワーク
     → 割り当てを「NAT」から「ホストインターフェース」に変更

1.x の時はコマンドラインで設定が必要だったので、ネットワーク設定は以前より
簡単になっているようです。

(4) キーボードの確認
   キーボードに右 Control キーがあるならそのままで OK
   もし無いなら、メニューのファイル→環境設定→入力
     ここでホストキーを別のキーに変更しておく

(5) 起動
   マウスがキャプチャされた場合(マウスカーソルが消えた場合)は、右 Control
   キーで復帰することをあらかじめ覚えておきます。
   (4) でキー割り当てを変更した場合はそちら。


●Debian install

(1) 起動した画面で「Install」選択
   言語選択 日本語 → あとはインストーラに従う

インストーラのメッセージや手順は etch と同じです。
(PS3 やっぱり Debian 再インストールメモ)

(2) 以下手順

スワップは設定しない方針で。必要になったら別の仮想ハードディスクを作って
割り当てる。または仮想マシンに割り当てる物理メモリ量を増やす。

キーボード配置の選択
  日本 (106 キー)

ネットワークの設定
  ホスト名: 任意
  ドメイン名: 任意

ディスクのパーティショニング
  手動
	→ "IDE1 マスタ (hda) - 8.6GB VBOX HARDDISK" を選択
	→ 「新しいパーティションテーブルを作成しますか?」 はい
	→ "基/論 8.6GB  空き領域" を選択
	→ "新しいパーティションの作成" を選択
	→ サイズ 8.6GB のまま、続ける
	→ "基本パーティション" を選ぶ
	→ マウントポイントが / なっていることを確認して
		"パーティションのセットアップを終了" を選ぶ
	→ "パーティショニングの終了とディスクへの変更の書き込み"
	→ スワップがないと怒られるけど無視 (いいえ を選ぶ)
	→ 書き込みの最終確認が出るので「はい」

root のパスワード、ユーザーアカウントの設定

パッケージマネージャの設定
  別の CD や DVD を検査しますか?: いいえ
  ネットワークミラーを使いますか?: はい
	ミラーを選ぶ
  Debian パッケージ利用調査に参加しますか?: いいえ

ソフトウエアの選択
  デスクトップ環境のチェックを外して、ウェブサーバとファイルサーバを追加
    [ ] デスクトップ環境
    [*] ウェブサーバ
    [ ] 印刷サーバ
    [ ] DNS サーバ
    [*] ファイルサーバ
    [ ] メールサーバ
    [ ] SQL データベース
    [ ] ラップトップ
    [*] 標準システム
  続ける

samba server
  ワークグループ/ドメイン名: 任意
  DHCP から WINS 設定を使うよう smb.conf を変更しますか?: はい

GRUB ブートローダのインストール
  マスターブートレコードに GRUB ブートローダをインストールしますか?: はい

インストールの完了
  メニューの デバイス → CD/DVD-ROM マウントの解除
  続けるを選択し再起動、しばらく待つと login: プロンプトになる。


●Debian の設定

(1) root でログインして ssh を入れる

コンソールでそのまま続けても構いませんが、日本語がでないしテキストのコピペが
出来ないので ssh 入れて putty 使います。

# vi /etc/apt/sources.list

deb cdrom:[Debian GNU/Linux testing _Lenny_ - Official Snapshot i386 CD Binary-1 20090119-04:20]/ lenny main
の行をコメントアウトします。先頭に # を付ける。

# apt-get update
# apt-get install ssh

文字化けしてるけど気にしない。何か聞かれるので Y

# vi /etc/dhcp3/dhclient.conf

send host-name "ホスト名"

send host-name の行のコメントを外してホスト名(サーバー名)を書き込む。

終わったらログアウト

PuTTYjp
puttyjp を起動して、ウィンドウ→変換→文字セットを「UTF-8(CJK)」に変更
ホスト名を入れてログインする


(2) サーバーの設定

以後 root 作業です。

# vi /etc/samba/smb.conf

[homes] の read only を no に変更
read only = no

# smbpasswd -a USER

ユーザーを追加してパスワード登録。('USER' は作成した個人アカウント)
これでファイルなどの転送が Windows の explorer からできます。

# apt-get install subversion
# apt-get install libapache2-svn libapache2-mod-encoding

dav_svn の設定はマニュアルを参考にします。

# more /usr/share/doc/libapache2-svn/README.Debian

/etc/apache2/mods-available/dav_svn.conf を見よ、と 1行しか書いてなかった。

# cd /etc/apache2
# vi mods-available/dav_svn.conf

下記の行のコメントを外す (必要に応じて書き換える)

<Location /svn>
  DVA svn
  SVNPath /var/lib/svn
  AuthType Basic
  AuthName "Subversion Repository"
  AuthUserFile /etc/apache2/dav_svn.passwd
  Require valid-user
</Location>


SVNPath の代わりに SVNParentPath を使うと複数のリポジトリを指定出来るように
なるとのこと。Windows の apache2 で dav_svn を設定するとこちらになるので
前から違うなと思ってましたが、、ここにありました。

# a2enmod auth_basic authn_file
# htpasswd -c /etc/apache2/dav_svn.passwd USER

Basic 認証のユーザー登録。'USER' 部分は置き換えてください。

リポジトリ作成。(既存のリポジトリをコピーして使うなら不要)

# svnadmin create /var/lib/svn

ポート番号などの設定を変えるなら次も書き換える。変更しないなら不要

# vi ports.conf
# vi sites-available/default
# vi sites-available/default-ssl

SSL の設定はやはりマニュアルを参考にする。

# zmore /usr/share/doc/apache2/README.Debian.gz

設定方法が書いてありました。手順通りに実行してみます。

# apt-get install ssl-cert
# make-ssl-cert generate-default-snakeoil --force-overwrite
# a2ensite default-ssl
# a2enmod ssl
# /etc/init.d/apache2 restart

これは self-signed のみのサンプルです。必要に応じて変更してください。
今回の用途は家庭内サーバー用。


(3) 確認

PC や他のマシンのブラウザで確認します。うまくいけば次の通り。

http://作成したサーバー/svn
https://作成したサーバー/svn

ユーザー名とパスワードが必要。空のリポジトリが表示される。


関連エントリ
PS3 やっぱり Debian 再インストールメモ



画面は 8インチながら 1600x768。
普段デスクトップ PC で使っている PC モニタと横解像度が同じでフォントを最小に
すると、目に痛いくらい広い。でも文字のサイズは VGA の WindowsMobile 端末でも
これくらいだし、本当に痛いのはグレア液晶のせいかも。

近距離で凝視するスタイルには向かないので非光沢フィルタを貼ってみます。
12.1 インチ液晶用フィルタの短辺がちょうどぎりぎり横幅に一致しました。
細長く切って液晶面だけ覆うように貼りましたがこれは少々失敗。

type P は液晶面がフラットで周囲との段差がなく液晶の枠部分にもうつり込みが
あります。もっと大きめに切って貼った方が良かったかも。
なお枠部分は、本体の色によってうつり込みの印象が異なるかもしれません。
ちなみに黒。


●解像度の計算

ドット(ピクセル)密度を計算してみました。
簡単に求めるには次のようにします。

sqrt( 横 ^2 + 縦 ^2 ) / インチ

画素が正方形という前提ですがこれで dpi (ppi) になります。
実際に定規で測ったり、カタログに載ってる ppi と比べたところほぼ一致するので
これで問題なさそうです。
たとえば type P の 8インチ 1600x768 なら

sqrt( 1600 ^2 + 768 ^2 ) / 8

これを google の検索欄にコピペすると結果が出ます。
実例 sqrt( 1600 ^2 + 768 ^2 ) / 8

> sqrt((1 600^2) + (768^2)) / 8 = 221.846794

だいたい 222ppi (dpi) 。1pixel が 0.114mm くらい。
1mm 四方に 76pixel ほど詰まっていることになります。

同じようにいろいろ計算してみました。

VAIO type P        1600 x 768  8.0inch = 221.8 ppi
EIZO L887          1600 x1200 20.1inch =  99.5 ppi (公式 dotpitch 0.255mm)
EIZO HD2452W       1920 x1200 24.1inch =  93.9 ppi (公式 dotpitch 0.270mm)
BenQ E2200HD       1920 x1080 21.5inch = 102.5 ppi (公式 dotpitch 0.248mm)
LOOX U (Atom)      1280 x 800  5.6inch = 269.5 ppi (公式 dotpitch 0.0945mm)
LOOX U50 (A110)    1024 x 600  5.6inch = 221.9 ppi
EeePC901           1024 x 600  8.9inch = 133.4 ppi
EeePC701            800 x 480  7.0inch = 133.3 ppi
EeePCS101          1024 x 600 10.2inch = 116.4 ppi
Let's note R8      1024 x 768 10.4inch = 123.1 ppi
XPS M1730          1920 x1200 17.0inch = 133.2 ppi

HTC Touch Diamond   640 x 480  2.8inch = 285.7 ppi
iPhone/iPod touch   480 x 320  3.5inch = 164.8 ppi (公式 163ppi)
WILLCOM 03 WS020SH  800 x 480  3.0inch = 311.0 ppi
W-ZERO3[es]WS007SH  640 x 480  2.8inch = 285.7 ppi
Softbank 930SH      854 x 480  3.0inch = 326.6 ppi
Softbank 931SH     1024 x 480  3.8inch = 297.6 ppi

調べた中では PC だと新 LOOX U (Atom) が一番高密度でした。
全体的に携帯端末の方が密度が高くなっていて、携帯の中には 300ppi を超えるものも
あります。

ppi が高いと密度が増えるので、解像度が高くても画面が広いというより、ドットが
詰まって細かいイメージになります。画素が細かくフォントやエッジのぎざぎざも
見えにくくなります。

PC の画面がだいたい 96dpi を想定しており、同じくらいの解像度でモニタが作られて
いるとしたら、type P でも 2.3倍 面積で 5倍以上。
通常のモニタ比で何もしなくても 4x FSAA 相当が色を圧縮しないでそのままの形で
出ていることになります。HD ムービーなどがきれいに見えるわけです。

269.5ppi の LOOX U だと 9x FSAA 相当に近く、300ppi クラスの携帯端末だと
PC モニタの密度比で 9x FSAA を超えていることになります。

モニタのサイズが違うし再生されるコンテンツの大きさも異なるし視聴時のモニタとの
距離も異なるので必ずしも一概に比較できませんが、高密度なモニタは色数が増える
のと同じで効果あるようです。
そういえば外で Touch Diamond の画面を見ていると、たまに紙のようだなと思うことが
あります。

このまま TV や PC モニタの性能が上がったら、Anti Aliasing とか考えずに逆に
見えすぎる解像度をぼかしたり、どう見せるかの方で頭を使うことになるのでしょうか。
それより当面の問題はテクスチャ解像度が足りなくなること。


関連エントリ
VAIO type P Windows7 beta とバックライト調整など



VAIO type P 手に入れました。
このサイズはちょうど昔使ってた MobileGearII MC/R530 と同じくらいです。

MobileGear2 MC/R530  245 x 131 x 28.8 770g  8.1inch  640x240 STN 110000円
VAIO type P VGN-P90S 245 x 120 x 30.8 688g  8.0inch 1600x768 TFT 122800円
(P はバッテリーL で重さは実測 = 688g)

かつて長いこと WindowsCE のハンドヘルド端末(H/PC)を愛用していたことがあります。
ボタンを押すとすぐ電源が入って直前の状態から操作できて、バッテリーも結構持つし
HDD レスで遠慮無く鞄に放り込んで持ち歩くことが出来る。
ちょっとしたメモや議事録などテキスト記録用に重宝していました。

狭いキーピッチは最初は無理だと思ったけど、Libretto 100 → MobileGear II →
Jornada720 を経て、すべて等幅で配列さえ合っていればすべてのキーでタッチタイプ
できることがわかりました。最近は右側記号部分のキーピッチが詰まっているキー
ボードばかりで個人的には少々残念。

H/PC が終了して後継機種も無くなってからは、代わりとなる環境を延々と探し続けて
いる感じです。PocketPC + 外付けキーボードだったり Windows NotePC を試したり
WindowsMobile + Bluetooth Keyboard など。
ずっと WindowsMobile で入力系のソフトばかり作ってるのもそのせい。

現在この用途で活躍していたのが EeePC901。少々重いけどバッテリーが持つので
スリープ状態で電源入れっぱなしでも大丈夫。WindowsCE H/PC のようにすぐ使える
機動性がありました。

ほぼキーボード大の本体で薄くて軽い。(ポケットにも入る)
ファンレス + SSD 、バッテリー L なら長時間持つ。
これならかつての H/PC のように扱えるか、と思い type P に飛びついてみました。


●描画は遅い

EeePC901 より速い構成のはずですが確かに遅いです。レスポンスもだし
特にAero が無効となっているためウィンドウの重ね合わせが変わると再描画が
発生します。この再描画の転送が目に見えるため印象を悪くしている気がします。

Aero だと合成がハードウエアなので他のウィンドウを再描画しません。
CPU に負担がかかって処理が落ちても見た目に変化がないので、処理落ちやあらが
目立ちにくいのだと思われます。
EeePC901 では GPU にそこそこパワーがあるため Aero で快適に使えました。

CPU にかかる負担を減らすことが重要かもしれません。とりあえず標準状態で常駐して
いるソフト類、プリインストールソフトを全部捨てて、Vista と Windows7 を
クリーンインストールしました。EeePC のように。

(ちなみに WindowsCE H/PC の描画はもっと遅かった。MobileGearII MC/R700 などは
コンソールのスクロールが波打つくらいに。)


●ドライバの保存

(1) 購入直後の状態でリカバリディスク作成
(2) C:\Windows\drivers の下を丸ごと SD カードなどに保存


●WindowsVista

パーティションを半分にしてまず片方に WindowsVista を入れました。
SSD 128GB にしておけば良かったかも。
もともと Vista PC なので必要なドライバは付属のものそのままです。
(2) で保存したドライバを使用します。

一応 Aero も使えるので実験 (詳しくはこちら)

Fn キーを使ったキーボードショートカットは音量変更のみ使用できました。
画面の明るさ調整が使えないと不便なので、代わりとなるソフトを作りました。

backlightwin v1.00

実行すると Ctrl + Alt + [F5]/[F6] で画面の輝度(バックライト)調整が出来ます。
もう一度実行すると常駐解除。自動起動するならスタートアップへ。


●Windows7 beta / build 7000

すでに多くの方が挑戦されているので参考にします。

type P wiki Windows7ベータ

Vista と同じく、最初に保存しておいた付属のドライバを適用します。
無線 LAN は最初から認識していますが、そのままだと 11g になります。
付属の無線LAN ドライバに入れ替えると 11n で 300Mbps で接続可能となりました。

入れたもの
・Chipset Driver (Intel)
・Graphics Driver (Intel)
・Ethernet Driver (Marvell)
・Wireless LAN Driver (Atheros)
・Sony Firmware Extension Parser Driver
・Sony Programmable I/O Control Device

やはり音量調節のみ可能で、画面の明るさ変更は出来ません。
Vista と同じように下記ソフトでとりあえず代用できます。

backlightwin v1.00


● VAIO type P は結構速い

しばらく作業していて気がついたのは、VAIO type P (SSDモデル) はきちんと
使っていくと結構速いということ。

たとえば Windows7/Vista 等のクリーンインストールは短時間で終わるし、起動や
終了も待たされません。VisualStudio 2008 のインストールも、時間がかかった
EeePC と比べるとあっけなく完了。
EeePC では結局あきらめたけど、type P なら本体でコンパイルする気になります。
RAMDISK も設定しないでそのまま Vista も Windows7 も使っています。

これまで使ってきた EeePC4G/EeePC901 と比べて SSD の書き込みがかなり速いようです。
type P は GPU が貧弱で描画のレスポンスは遅いけど、SSD モデルであれば最初の印象
よりも結構速くて使えると感じました。

もちろん EeePC901 だってより高速な SSD に交換可能だし、ZIF コネクタにつなぐ
タイプなら各種選べるはずです。type P を見てしまうと、EeePC の SSD ももっと
速いタイプに交換したくなります。


ちょうど Eye-Fi が使えなかったことを機にルータを入れ替えたばかりでした。
type P はプロパティの表示で 300Mbps でつながっています。

LAN 接続(Gbit)の Desktop PC から大きめのファイル (DXSDK_Nov08.exe 483MByte)
をコピーして 15MB/sec 前後の速度でした。おおよそ 120Mbps くらい。
へたに SD カードとかを経由してデータコピーするより速いです。

同じく 11n 対応の EeePC 901 はなぜかプロパティの表示で 135Mbps なので、
ドライバが古いだけかもしれません。



PowerVR SGX を採用した Intel US15W の GMA500 は、こちらで調べたように
正直あまり速くないという印象です。
その考えられる要因は次の通り

・シェーダーユニット (USSE) の数が少ない (2個)
・1 ALU あたり 32bit float x1 演算
・公称のピーク性能を達成できるのは 1 ALU で 8bit x 4(ARGB) のレガシーなカラーを扱った場合のみ
・フィルレートは 3D 時の内部 Z オクルージョンカリングを含めたもので実際のバスは細い

シェーダーユニット USSE あたり 1ALU との明確な記述はありませんが、その参考に
なりそうなのが頂点性能です。Transform Only でも頂点あたりに必要な clock 数が
かなり多いことから、1clock あたりの演算能力がかなり低いことがわかります。
(詳細は 前回)

転送能力は 2pix x 200MHz = 400Mpix/sec
Z/stencil 不要なので 64bit x 200MHz = 1.6GB/sec

ちなみに USSE は汎用的な Unified Shader なので、Video/Image 処理にも使われる
(または利用できる) と書かれています。
他に Video Decode Unit が搭載されているので本当に使われているのかどうかは
わかりません。GMA950 系よりは汎用的なのは確かなので、描画以外でも恩恵を
受けられるメリットはあるかもしれません。
もしくは PowerVR がライセンスしている Video Decode 機能自体も USSE の
Video/Image processing に依存している可能性があります。

追記: シェーダーユニットが 2個というのは UnifiedShader 世代のスカラー換算なので、
 ShaderModel 3.0 世代でいえば 0.5 個分ということです。3.0 世代 GPU は複数の
 ALU を搭載していたものが多かったのでそれを考慮すればもっと少ない。


●Windows Aero / Aero Glass と相性が悪い

Windows の Aero / Aero Glass においては ShaderModel 2.0 (PiselShader 2.0) を
利用して Window 描画を行います。そのため、特に PixelShader2.0 以上を使うと
pixel 性能が極端に下がることが致命的だと考えられます。

GMA500 が Windows Aero / Aero Glass と相性が悪い原因

 (1) PixelShader 2.0 以降では Pixel 処理能力が 1/4 以下となる
   8bit x4 SIMD mode が使えない、1ALU で済んでいたカラー演算が 32bit float
   演算となるため 4ALU 必要となる。

 (2) 半透明の重ね合わせは PowerVR 独自の On Chip Z Occlusion をスポイルする。
   おそらく Aero / Aero Glass など Window GUI に必要なのは、
   賢く速度を稼ぐ描画ではなく力業の転送能力なのだと考えられます。

根本的な原因はおそらく (1) の方で、(1) の方が処理負担に対する割合が高いと
考えられます。(2) の方が大きな原因であれば、Aero Glass → Aero 不透明に
切り替えることで改善できるはずです。


●PowerVR SGX の性能がわかりそうな資料(1)

前回調べておきながら、取り上げるのを忘れていた資料に OMAP があります。
OMAP は SGX を搭載しています。

TEXAS INSTRUMENTS OMAP3530

PowerVR SGX といっても様々なグレードがあるため一概に比較はできませんが、
資料を読むといろいろと興味深いデータが含まれています。

OMAP3530
> 10MPoly/sec

GMA500 は 13.3MPoly/sec なので若干遅い程度。

さらに上記ページから落とせる下記の資料が参考になります。
 ・OMAP35x 2D/3D Graphics Accelerator Reference Guide-TRM Ch 13 (Rev. B) (spruff6b.pdf, 199 KB)

> 8 parallel depth/stencil test per clock

チップ内部の pixel 処理能力が 8pixel/clock であることを意味しています。
tile 単位のラスタライズ(以下 SGX の処理内容の予想)

(1) depth/stencil 8pixel/clock (OMAP3530の場合)
   すべての Primitive をラスタライズして depth/stencil だけ先に test
   Pixel Shader はせず Texture も読まない

(2) deferred pixel shading、(1) を通過した pxiel のみ描画する。
   Z が最前面の pixel だけ PixelShader の実行、Texture のフェッチを行う

(3) (2) の出力を最大 2pixel/clock (GMA500の場合) で出力する

(1) は PC の GPU でもよく使用する depth のみの前レンダリング似ています。
GPU の多くはカラー無しの depth/stencil のみの出力では 2倍ほどの pixel レートで
描画できるようになっています。


●PowerVR SGX の性能がわかりそうな資料(2)

ルネサスの SH-Navi3 のニュースリリース

SH7776 CPU 533MHz, 4.27GB/sec, PVR SGX ?MHz
 ・RENESAS 業界初、車載情報端末向けに画像認識処理機能内蔵のデュアルコアSoC「SH7776」(SH-Navi3)を製品
 MBX から SGX でポリゴン性能が 2倍

SH7770 CPU 400MHz, PVR MBX 100MHz
 ・カーナビに最適な2D/3Dグラフィックスエンジンを業界で初めて内蔵し、さらに、次世代カーナビに必要な機能を1チップにした「SH7770」を製品化

SH7774 CPU 600MHz, PVR MBX MBX 300MHz
 ・カーナビ向けSoCで、世界で初めて画像認識処理機能を搭載した「SH7774」を製品化
 SH7770 の3倍


● Intel GMA500 と ShaderModel

実際に Atom Z500 + US15W の Intel GMA500 を搭載した PC を手に入れたので
軽く試しました。

 ・ShaderModel3.0 まで

対応している ShaderModel は 3.0 まで。
アーキテクチャ的には Unified Shader で 4.1 (D3D10.1) まで出来そうに見えますが
(wikipedia Intel GMA にも 4.1 と書かれていますが)
ドライバが対応しているのは 3.0 まででした。
VertexShader も PixelShader もハードウエアです。

D3D10CreateDevice1() がエラーになるのは当然としても、少々問題なのは
D3D11CreateDevice() までエラーを返してくること。
本来なら D3D_FUEATURE_LEVEL_9_3 あたりを返してきて欲しいところです。

これが原因で GMA950 なら D3D11 API に移行できるのに、GMA500 では
ShaderModel3.0 でも D3D11 API に移行することが出来ません。
もちろん WARP なら可能です。


● Intel GMA500 と Aero Glass

WindowsVista では Aero / Aero Glass に切り替えられました。
やはり重いです。Aero Glass を切って Aero にすると多少軽くなります。
以下手順。

 ◎ Aero Glass に切り替える ---- (A)
  デスクトップのカスタマイズ→個人設定→ウィンドウの色とデザイン→
    配色「Windows Aero」を選ぶ

 かなり処理落ちしているようで、ウィンドウ操作などがマウスカーソルについて
 こなかったりします。
 下記の設定で半透明を無効にすると、同じ Aero でも若干速くなります。


 ◎ Aero の半透明を無効にする ---- (B)
  デスクトップのカスタマイズ→個人設定→ウィンドウの色とデザイン→
    ウィンドウの色とデザイン→透明感を有効にするのチェックを外す

 半透明を切るだけで結構軽くなりました。半透明というよりも、ぼかしフィルタ
 などのシェーダーコードの実行がボトルネックになっているのかもしれません。

先にパフォーマンスのチェックを全部外しておいてから Aero にすると、もう少しだけ
軽い設定の Aero になります。


 ◎ 出来るだけ軽い Aero にする方法

 (1)スタートメニュー→コンピュータの右ボタンメニューでプロパティ
    →システムの詳細設定→パフォーマンス→設定→チェックボックスを全部外す

 (2) ここで強制的に BASIC に戻される

 (3) (A) の設定で再び Aero Glass に切り替えてから、(B) の設定で半透明を無効にする。

見た目は Vista Basic と変わりませんが、上のウィンドウを動かしても重なった下の
ウィンドウに再描画が発生せず、ハードウエアによるアクセラレートが効いている
ことがわかります。

Windows の描画モード
XP
  Luna                    ハードウエア
  Classic                 ハードウエア

Vista
  Aero Glass (透明感 ON)  WDDM ハードウエア 3D描画
  Aero 不透明/Standard    WDDM ハードウエア 3D描画
  Vista Basic             CPU 描画
  Windows Classic         CPU 描画

Windows7
  Aero Glass (透明感 ON)  WDDM ハードウエア 3D描画
  Aero 不透明/Standard    WDDM ハードウエア 3D描画
  Basic                   ドライバが対応していればハードウエア

Windows7 では Aero を有効にすることが出来ませんでした。
ドライバにまだ問題があるのかもしれません。


参考資料
IntelR Atom Processor for Mobile Internet Devices Technical Documents
  ・IntelR System Controller Hub (IntelR SCH) datasheet (PDF)
  ・IntelR System Controller Hub (IntelR SCH) specification update (PDF)

Imagination Intel
  ・IntelR CE 3100 (PDF)


関連エントリ
Intel GMA500 の機能と性能と Aero



PC を移行したり OS を入れ直したり HDD を交換したりと、何かと環境の再構築が
連続したので個人的なメモです。(Windows Vista)


●Chrome
 ブックマークマネージャーからエクスポートしてバックアップ


●Firefox
 ブックマークの管理からバックアップ&htmlとしてエクスポート


●iTunes
 移行前にコンピュータの認証解除
 データ自体は C:\Users\<USER>\Music\iTunes や設定詳細で指定したフォルダ移行


●Office Outlook (2007)
  C:\Users\<USER>\AppData\Local\Microsoft\Outlook
 の中身を保存しておく。
 Outlook を install し、起動して初期化した後に上のデータを上書き。


●Thunderbird
  C:\Users\<USER>\AppData\Roaming\Thunderbird
 の中にある profiles.ini と Profiles フォルダを保存する。


●3ds Max
 ライセンスを別マシンに export


●個人フォルダ
  C:\Users\<USER>\Downloads
  C:\Users\<USER>\Desktop
  C:\Users\<USER>\Documents
  C:\Users\<USER>\Pictures
  C:\Users\<USER>\Music
  C:\Users\<USER>\Favorites
 移行時はフォルダの移動ではなく、コピーして元を削除する。


●VirtualStore の確認
  C:\Users\<USER>\AppData\Local\VirtualStore\Program Files (x86)
  C:\Users\<USER>\AppData\Local\VirtualStore\Program Files
 Program Files にデータを書き込もうとするアプリケーションのデータは
 こちらに落ちているので一応みておく。


●その他

普段からフォルダごとに分類

・インストール不要なアプリケーションの実行ファイルフォルダ
   Program Files とは別フォルダに分けておくとコピーしてパスを通すだけ

・バックアップが必要なデータ用フォルダ
   別サーバーにミラーしているので無くてもいいけどコピーした方が再構築が楽

・バックアップ不要だけど移行した方が後で便利なデータフォルダ
   ダウンロードしたアプリのインストーラや周辺機器付属のドライバのコピーなど。
   なくても何とかなるので優先度低い。

数百GB~TB クラスになると USB2.0 でもコピーに時間がかかるので SATA でないと厳しい。
仮に 480Mbps 上限いっぱい使えると仮定しても 60MB/s、1TB=4時間半。(実際は数倍かかる)

ATOK2008 はキーカスタマイズをファイルに書き出してあるので install 後読み込むだけ。
テキストエディタなど設定ファイルもまとめて svn に入れて複数 PC 間で共有。

サーバーなどは仮想PCにしてるので、そのうち開発環境も仮想 PC で済むようになるかも。


●移行後

・移行直後なんか Vista が重い
  デフォルトで個人フォルダがインデックス付けの対象となっているため。
  個人フォルダに大量にファイルがあると一通りインデックス化が終わるまで重くなる。
  「コントロールパネル→インデックスのオプション」で対象から外せる

・環境変数の設定画面を簡単に呼び出す
  スタートメニューのアイコン画像を左クリック
   →左側のタスクから環境変数の設定



2009/01/24
Qualcomm と 3D

ARM 一色といって良いほど ARM が幅をきかせているように、
3D プロセッサも PowerVR 一色になりそうな勢いでした。
そんな中 Qualcomm は ATI (AMD) を貫き通すようです。

Qualcomm、AMDからハンドヘルド向け事業を買収
AMD、ハンドヘルド・チップ事業をクアルコムに売却

ちょうど最近なにかと苦労してたのがこの GPU。
Direct3DMobile ではなく OpenGLES だったりとか、他のドライバや API でもっと
きちんと使えば、もっとパワーのある描画性能を見せてくれるのかもしれません。

PowerVR 系も SGX が増えてくるので、そろそろ Shader を搭載した次の世代の
GPU も期待したいところ。


関連エントリ
HTC Touch Diamond で Direct3DMobile その(11) 問題まとめその他
HTC Touch Diamond で Direct3DMobile その(10) d3dmclock v1.10 3Dクロック 更新
HTC Touch Diamond で Direct3DMobile その(9) d3dmclock v1.00 3D クロック
HTC Touch Diamond で Direct3DMobile その(8) ノーマルマップの解説
HTC Touch Diamond で Direct3DMobile その(7) ノーマルマップを表示する
HTC Touch Diamond で Direct3DMobile その(6) 頂点性能続き
HTC Touch Diamond で Direct3DMobile その(5)
HTC Touch Diamond で Direct3DMobile その(4) 高速化
HTC Touch Diamond で Direct3DMobile その(3) 実際の頂点性能
HTC Touch Diamond で Direct3DMobile を使う。その (2)
HTC Touch Diamond で Direct3DMobile を使う。ハードウエアアクセラレータ



Windows7 beta の PC でリモートデスクトップを使ってみました。

接続時に、リモートデスクトップの オプション → エクスペリエンス から
パフォーマンスのオプション画面でとりあえず全部チェックを入れておきます。
これで普通に Aero Glass が出ています。

remote desktop
↑LOOX U50 (Win7βクライアント) から EeePC901(Win7β) に接続

リモートデスクトップを使うためには、あらかじめホスト側の設定が必要です。
 コントロールパネル → システム → リモートの設定 →
  リモートデスクトップを実行しているコンピュータからの接続を許可するを選ぶ

Vista が相手だと Aero Glass になりません。

Aero Glass
○ Win7β Client → Win7β Server
× Win7β Client → VistaSP1 Server
× VistaSP1 Client → Win7β Server

どの PC もローカルで Aero Glass が動くだけのスペックがあります。
(LooxU50 は Atom ではなく A110 の古いタイプです)

Aero Glass が出るなら Direct3D はどうだろう、ということで試しました。
あっさり動きました。

remote desktop
remote desktop
↑LOOX U50 (Win7βクライアント) から EeePC901(Win7β) に接続

ところが Vista でも動きました。

remote desktop
remote desktop
↑LOOX U50 (Win7βクライアント) から Desktop PC (VistaSP1) に接続

Aero は出ていませんが D3D アプリは動いています。
自分が知らなかっただけで Vista ですでに対応していたのかもしれません。

動作するアプリケーションはサーバー(ホスト)側の GPU に依存しています。
下記の通り認識されているデバイスが異なります。

HAL(sw vp): RDPDD Chained DD (チップセット内蔵 GMA950 EeePC901 Win7)
HARDWARE: RDPDD Chained DD  (GeForce GTX260 Desktop Vista)

サーバーが EeePC の場合 GeometryShader を使った D3D10 アプリは動きませんが、
GTX260 の Vista では動きます。よってサーバー側の GPU でレンダリングした結果の
画像を RDP で転送しているのだと考えられます。
もともと Vista 同士でも D3D アプリが動作していたのかもしれません。


Windows7 ではさらに、Direct3D のコマンドレベルのリモート描画に対応しているようです。
以前 masafumi さんのところで紹介されていた DXGI 1.1 beta のドキュメントによると、
Windows7 の DXGI1.1 だと RDP7 によって Remote PC の Adapter を取得できるとのこと。

DXGI 1.1のドキュメント

詳しいドキュメント(White Paper)がこちらにありました。

Microsoft Developer White Papers: PDC08
 より一番下の 「Direct3D 10.1 Command Remoting 」

Command Remoting の違いは下記の通り

・Vista
 サーバーの GPU でレンダリングした結果を bitmap でクライアントに転送。

・Windows7 DXGI1.1 対応アプリ
 描画コマンドを送ってアプリではクライアント側でレンダリングできる
 ただし既存の(未対応) Direct3D10.1 アプリはビットマップ転送

よって最初に紹介したキャプチャなど、現在描画されている Direct3D の画面はただの Vista の機能でした。

Command Remoting を使うには対応したアプリケーションを作成し、Windows7 上で
走らせる必要があります。
Full Screen Mode が使えないなど若干制限があり、また実際は通信帯域を考慮した
描画コマンドの最適化が必要となるようです。

コマンドレベルの転送+クライアントレンダリングによって帯域を減らせるので、
リモートで走らせているアプリケーションでも描画レスポンスが向上すると思われます。
また未対応だったり不要なものは従来通り bitmap 転送なので、サーバーとクライアントで
レンダリング処理を分散させるなど応用できるかもしれません。

残念ながら 10.1 なので D3D11 アプリは当分未対応だと思われます。
WARP といい、とりあえず Windows の描画に必要な 10.1 が 1つの目標になっているようです。




HTC Touch Diamond の Direct3D Mobile を使い Texture に直接レンダリングしようと
思ったらエラー。cpas を見たら対応していないことに気がつきました。
テクスチャに対して BackBuffer からのコピーは出来たので、必要ならフレーム
バッファをコピーしながら使うことになります。
ちょっと昔の GPU といった感じです。

IDirect3DMobileTexture* iTexture0;
CreateTexture( 512, 512, ..., &iTexture0 )
IDirect3DMobileSurface* iSurface0;
iTexture0->GetSurfaceLevel( 0, &iSurface0 );
~
RECT rect;
rect.left= 0;
rect.top= 0;
rect.right= 480;
rect.bottom= 480;
CopyRects( iBackBuffer, &rect, 1, iSurface0, NULL );


●現在までにわかっている問題点

HTC TouchDiamond (Qualcomm MSM7201A 内蔵 ati core 用 d3dm_ati.dll) で
Direct3DMobile を使用した場合に遭遇した各種症状です。
Emulator の Reference Driver では問題がないので、何らかのバグの可能性が高いと
思われます。

・640x480 window mode/full screen でレンダリングできない
 639x480 や 640x479 以下なら大丈夫

・HW T&L の光源が World space ではなく Local space で適用されてしまう

・XYZ_RHW 形式の頂点でレンダリングすると RHW の値が座標値にも影響を与えてしまう
 rhw = 1 以外で screen 座標を正しく与えることが出来ない。

・XYZ_RHW 形式(Transform済み頂点)と、そうでない頂点の混在が出来ない。
 RHW 無し頂点が出なくなる。

・D3DMTSS_TEXCOORDINDEX が無視される。常に Stage に一致した頂点 uv を参照する。
 仕様かもしれません。

・D3DMFVF_TEXCOORDFIXED が指定できない。常に頂点 uv は FLOAT とみなされる。

・Texture の mipmap ありなしと D3DMTSS_MIPFILTER の設定を厳密に合わせないと
 テクスチャが真っ白になる。
 存在しない miplevel へのアクセスが all 255 になるのかと思ったけど違うようです。

・ZWRITEENABLE を FALSE にできない。症状未確認。

・TextureStage でテクスチャを使用していない描画とテクスチャを使用している描画が
 混在しているときに Texture が表示されないことがある。

 厳密な発生条件は未検証です。他にも複数の描画で、上書きしているはずの
 TextureStage の設定が相互に影響し合っていることがあります。
 d3dmclock でも、オプションによって Texture が不要なケースなのにテクスチャを
 読み込んで演算で無視するなどやってます。

その他危なそうなものは避けて使っているので他にもあるかもしれません。


関連エントリ
HTC Touch Diamond で Direct3DMobile その(10) d3dmclock v1.10 3Dクロック 更新
HTC Touch Diamond で Direct3DMobile その(9) d3dmclock v1.00 3D クロック
HTC Touch Diamond で Direct3DMobile その(8) ノーマルマップの解説
HTC Touch Diamond で Direct3DMobile その(7) ノーマルマップを表示する
HTC Touch Diamond で Direct3DMobile その(6) 頂点性能続き
HTC Touch Diamond で Direct3DMobile その(5)
HTC Touch Diamond で Direct3DMobile その(4) 高速化
HTC Touch Diamond で Direct3DMobile その(3) 実際の頂点性能
HTC Touch Diamond で Direct3DMobile を使う。その (2)
HTC Touch Diamond で Direct3DMobile を使う。ハードウエアアクセラレータ



Android Dev Phone 1 (G1) の実機端末を見せてもらいました。
想像よりずっと良かった、というのが第一印象です。
端末は思ったよりもコンパクトでしっかりした作り。
スライド式キーボードも弧を描くような動きでユニーク。
ボタンの中央にあるのはカーソルキーでもスティックポインタでもなくてトラックボール。

HTC T-Mobile G1

タッチパネルは指で軽く触れただけでスムーズに動きます。
これ iPhone/iPod touch と同じです。
ノート PC などのタッチパッドと同じで、指で操作するならこちらの方がやりやすいと感じます。

WindowsMobile 系端末に使われてる感圧式タッチパネルはスタイラスが使えるものの、
触れただけでは反応せず少々押す力がいります。
指操作だと反応してるかどうか若干わかりにくく、ものによっては爪を立てた方が
操作しやすくなります。
HTC の G1 も同様の感圧式かと思っていたので、この点でまず気に入りました。

使われているチップ自体は Touch Diamond と同じ Qualcomm MSM7201A だそうです。
画面解像度は iPhone 等と同じ 480x320 でした。
GPS 等は Diamond にもありますが、電子コンパスが目新しい機能です。

Android の OS 自体はさまざまな端末にも多くの方の努力によって移植されています。
HTC の WindowsMobile 端末で動作するものがあるようです。

HTC Android

実際に手持ちの EMONSTER lite (S12HT, Touch Dual) と Touch Diamond (S21HT) で
試してみました。上記サイトを参考に、落としたファイルの exe を実行。

S12HT は一発目は起動成功しましたが、2度目以降は起動したり出来なかったりで
何度もリセットしながらです。
Android の操作をよく知らないせいか、試す前にホーム画面に戻れなくなったりと
はまってしまってまだきちんと使えていません。

S21HT は比較的すぐに起動できました。
コマンドラインに仮想キーボードからコマンドをタイプしなければならないですが、
しばらく待つと Dev Phone で見たような画面になります。
カーソルキー上下でメニュー階層の行き来が出来るようですが、Diamond のカーソルは
押しづらくて、さらに間違って OK (←) が押されてしまうと固まるみたいなので
こちらもまだうまく使いこなせていないところです。

EMONSTER lite S12HT android
Touch Diamond S21HT Android
Touch Diamond S21HT Android

2009/01/17 23:58 写真追加



Atom Z500 系の CPU を使ったノートが増えてきました。
小型軽量なものも多く魅力的です。
レビュー記事を見ていると Vista や Windows7 の Aero は off になっているとのこと。
気になったので調べてみました。

使われている GPU はチップセット (System Controller Hub) US15W 内蔵の GMA500。
最近携帯デバイスで多く使われている PowerVR core の一種ですが、アーキテクチャが
さらに進化した SGX に属するもののようです。

その特徴は Unified Shader であること。
DirectX10.1 (Direct3D10.1) 世代に対応できるだけの高度な機能を持っており、
VertexShader, PIxelShader や GeomteryShader を実行可能。
32bit float 演算可能でプログラム長の制限もなく分岐等の制御命令も備えています。
GPGPU として汎用処理に用いられることも想定しているようです。

機能だけ見ると GMA950~ 等よりも上に見えます。
実際に D3D10 対応ドライバが出ているのかどうか、Windows で D3D10.1 や
ShaderModel4.1 が使えるのかどうかわかりませんが、かなり興味が出てきました。

ドキュメントはこのあたりから落とすことが出来ます。
IntelR System Controller Hub US15W Technical Documents

詳しい仕様は Datasheet の方で、Specification update でいくつか更新が入っています。
(update の方で 2GB RAM 対応が書かれています)

datasheet 「9 Graphics, Video, and Display」の一番最初、頂点性能なのに
15 clock/triangle と書かれている点がまず気になりました。
これは 3頂点分なのか、直後に書いてあるように
「 Vertex/Triangle Ratio average = 1 vtx/tri 」で 1頂点分なのか、
それとも 「 peak 0.5 vtx/tri 」の方を指している (つまり1頂点の半分の数値)
なのか曖昧です。

ただ 1頂点で 15cycle は少々多すぎような気がします。実際に計算すると
200MHz 動作なので 13.3M triangle/sec 。
wikipedia PowerVR に書かれている SGX535 の 28Mpoly/sec と比べて半分ほどです。

またピークの fill rate は 2pixel/clock と書かれています。
200MHz なので 400Mpixel/sec。
この数値は GPU としてはかなり低く GMA950 の数分の一。ここで重要なのが
PowerVR であるという事実。PVR はバスの効率を 2~3 倍とみなすため、
800M~1Gpix/sec 相当と書かれていることがあるようです。
Z/Stencil を内部メモリだけで処理可能で、3D のシーンなどポリゴンの重なりが
多くても、描画順に依存せずに常に一番上のポリゴンのみ描画可能だからです。

200MHz という記述は datasheet p.46 (CFG による選択で GFX 200MHz) や
Specification update の p.12 に Graphics Frequency 200MHz と書かれています。
112/160MHz はモニタ出力時のドットクロックのことで、core の動作クロックでは
ないようです。

NVIDIA や AMD で sp, spu 等と呼ばれているシェーダーユニットは、PowerVR SGX
だと USSE (Universal Scalable Shader Engine) という名称になっています。
例えば下記 wikipedia の記述を見ると SGX520~540 の性能はちょうど整数倍です。

wikipedia PowerVR

GeForce や RADEON のように、シェーダーユニットの個数でグレードを分けている
のかもしれません。となると問題は GMA500 にはいったい何個載っているのか。

datasheet の説明を読むと、どうやら USSE は 2個ではないか、と思えます。
wikipedia の記述には GMA500 のシェーダーユニットは 4個と書かれています。
(SGX535相当とのこと) でも 4個だといまいち計算が合いません。

wikipedia Intel GMA

datasheet には同時に 4つのシェーダーが実行可能状態になると書かれていますが、
もしかしたら同時実行ではなくインターリーブしている可能性もあります。
(ちなみに待機状態を含めて 16スレッドの状態を同時に保持できるようです。
レジスタは 1スレッドあたりスカラー 128個)
また下記の記事を見ても 535 までは USSE は 2個であるとのこと。

Centrino Atomにも搭載されるIMGのPowerVRビジュアルIPコア

さらに PowerVR の本家サイト下記ページから、Intel CE3100 の pdf を
読むことが出来ます。

Imagination Intel

Intel CE3100 は同じく GMA500 を搭載したメディアプロセッサで、pdf によると
dual USSE、13M triangle/sec、 2pixel/clock と書かれていました。
こちらの数値 13M triangle/sec は 200MHz 動作の 15clock/triangle とも
一致しますし、US15W の GMA500 と同じと思って良さそうです。
結局 SGX530 に近い数値ですが、同じものなのかそれとも計算方法が違うのかは
わかりません。

USSE の演算ユニットの構成は詳しくはわかりませんが ALU は 32bit 幅とのこと。
他の GPU でも D3D10 世代の Unified Shader はスカラー単位で動作しているので
USSE も同じような構造になっているのかもしれません。

もし 1 USSE が 1 ALU だとしたら、unified shader をフルに割り振っても
float4 の計算に 2cycle かかります。2pix/clock に間に合いません。
datasheet によると ALU は float x1 または fixed16 x2 または int8 x4 を
SIMD として一度に演算できるそうです。
レガシーな GDI のように 8bit ×4 の 24/32bit color ならば 1 ALU だけで済みます。
この場合のみ 2pix/clock が実現できるという意味かもしれません。

逆に言えば pixel にも float 演算が必要な ShaderModel2.0 以降は、ピクセル処理
速度が 1/4 になるということ。RGB だけでも 1/3。
ShaderModel1.0 でも符号付き 8bit なので 9bit 必要です。
おそらく fixed16 の演算が必要になると思われます。この場合 1/2。
Unified Shader なので頂点や GeomteryShader など他の処理も割り込みます。

もしこれらの仮定が正しいとするなら、GMA500 は pixel 性能が足りていないのだと
予想できます。3D のように深い重なりが無ければ PowerVR の特性も活かせず、
タイトなバスがそのまま見えてしまうでしょう。

特に Aero が半透明やレンダリング途中のフレームバッファを使った特殊効果を
利用しているなら、PVR の良いところがさっぱり発揮できていないのかもしれません。
よくよく考えると相性悪そうです。タイルをまたぐ大きなポリゴンも多いし。
さらに ShaderModel2.0 以上を必要とする Aero だと、シェーダーためにカラー
演算能力が 1/4 になっている可能性もあります。

Aero が使えなくても D3D10.1 相当のシェーダーが使えればおもしろそうなので、
実物を触る機会があったら試してみたいと思っています。
そういえばテクスチャユニットとかは全く情報がありませんでした。

これらの内容はすべてドキュメントを元にした想像で書いていますので、
実際に検証しながら調べたわけではないです。
いろいろ勘違いしている可能性が高いですのであらかじめご了承ください。
間違いがありましたらごめんなさい。

続き>Intel GMA500 のスペックについて考える。続き (2)


メニューから任意の画像を読み込めるように変更しました。(前回)
対応フォーマットも大幅に増えて、任意サイズの画像を直接背景として登録できます。

d3dmclock HTC Touch Diamond
↑ Windows7 ぽい感じに・・

ダウンロードはこちら
d3dmclock v1.10 ダウンロードページ

WindowsMobile 用アプリです。

速度はだいたい 16~21fps 程度です。
速度は主に時間帯と背景テクスチャの解像度で変わります。
時間帯で速度が変化するのは、各数字モデルのポリゴン数が違うから。

1024x1024 に変換されてしまう大きな画像だともっと速度が落ちるかもしれません。
HTC Touch Diamond は VGA (640x480) なので、512x512 がちょうど良いくらいでしょう。
QVGA の端末ならもっと小さくて構いません。


Touch Diamond は 2.8インチで VGA (640x480) の解像度があるため、かなり密度が
高くきれいに見えます。輪郭のジャギも気にならないし、この密度でリアルタイムに
レンダリング出来てリアルタイムに動いているのだから十分かもしれません。
blog のキャプチャは 1/4 に縮小しています。

WindowsMobile だと、せっかく載っている 3Dアクセラレータがあんまり活用されていないのは
もったいないですね。


テクスチャの読み込みは、当初 Direct3DMobile D3DMX のテクスチャローダーを
そのまま使用していました。これ対応フォーマットは dds と bmp だけ。
しかも bmp は 16bpp を読み込めないことがわかりました。
さらに 2の n乗以外のサイズも読み込めるけど固まったかと思うくらい低速です。

テクスチャローダーを作り直しました。
参考にしたのは WindowsMobile6.0 SDK サンプル
Windows Mobile 6 SDK\Samples\Common\CPP\Win32\Imaging

これで bmp だけでなく jpeg や png 、gif などメジャーなものはたいてい
読み込めるようになりました。しかも速いです。

流れは次の通り

(1) IImage として画像を読み込む
(2) IBitmapImage に変換する
(3) LockBits() してピクセル情報にアクセス
(4) SystemMemory の Surface (CreateImageSurface()) に格納する
(5) CreateTexture()
(6) Texture から Surface を取り出す。
(7) (6) の Surface に (4) の Surface をコピーして vram に転送
(8) テンポラリリソースとインターフェースを Release

#include  <imaging.h>

// interface 取得
IImagingFactory*	iImageFactory= NULL;
CoCreateInstance( CLSID_ImagingFactory, NULL, CLSCTX_INPROC_SERVER,
			IID_IImagingFactory, &iImageFactory );
// ファイル読み込み
IImage*	iImage= NULL;
iImageFactory->CreateImageFromFile( file, &iImage );

ImageInfo	info;
iImage->GetImageInfo( &info );
int	w= info.Width;
int	h= info.Height;

// bitmap に変換
IBitmapImage*	iBitmap= NULL;
iImageFactory->CreateBitmapFromImage( iImage, w, h,
	PixelFormat24bppRGB, InterpolationHintDefault, &iBitmap );

iImage->Release();
iImageFactory->Release();

// lock
static BitmapData	lockdata;
iBitmap->LockBits( NULL, ImageLockModeRead, PixelFormat24bppRGB, &lockdata );

lockdata.Width;
lockdata.Height;
lockdata.Stride;
lockdata.Scan0;

// texture を作成
IDirect3DMobileTexture*	iTexture= NULL;
CreateTexture( w, h, 1, 0, D3DMFMT_R5G6B5, GetTexturePool(), &iTexture );

// SystemMemory の surface を作成
IDirect3DMobileSurface*	iSurface= NULL;
CreateImageSurface( w, h, D3DMFMT_R5G6B5, &iSurface );

// 書き込み
D3DMLOCKED_RECT	dlock;
iSurface->LockRect( &dlock, NULL, 0 );
uintptr_t	di= reinterpret_cast<uintptr_t>( dlock.pBits );
for( int y= 0 ; y< h ; y++ ){
    unsigned short*	dp= reinterpret_cast<unsigned short*>( di );
    for( int x= 0 ; x< w ; x++ ){
        const unsigned char*	sp= image.GetXY( x, y );
        unsigned int	b= sp[0];
        unsigned int	g= sp[1];
        unsigned int	r= sp[2];
        *dp++= ((r<<8)&0xf800)|((g<<3)&0x07e0)|((b>>3)&0x001f);
    }
    di+= dlock.Pitch;
}
iSurface->UnlockRect();

// texture の surface を取り出す
IDirect3DMobileSurface*	iTexSurface= NULL;
iTexture->GetSurfaceLevel( 0, &iTexSurface );

// コピー
CopyRects( iSurface, iTexSurface );

iSurface->Release();
iTexSurface->Release();

// unlock
iBitmap->UnlockBits( &lockdata );
iBitmap->Release();



関連エントリ
HTC Touch Diamond で Direct3DMobile その(9) d3dmclock v1.00 3D クロック
HTC Touch Diamond で Direct3DMobile その(8) ノーマルマップの解説
HTC Touch Diamond で Direct3DMobile その(7) ノーマルマップを表示する
HTC Touch Diamond で Direct3DMobile その(6) 頂点性能続き
HTC Touch Diamond で Direct3DMobile その(5)
HTC Touch Diamond で Direct3DMobile その(4) 高速化
HTC Touch Diamond で Direct3DMobile その(3) 実際の頂点性能
HTC Touch Diamond で Direct3DMobile を使う。その (2)
HTC Touch Diamond で Direct3DMobile を使う。ハードウエアアクセラレータ



せっかくなのでもうちょっと実用的なやつを。
デジタル時計にしてみました。

d3dmclock

HTC Touch Diamond の加速センサーに対応したので、本体の傾きに合わせて画面が
スムーズに回転します。
意味もなく数字 1つ 1つがポリゴンです。

d3dmclock
↑数字が不揃いなのはバグではありません。

ダウンロードはこちら
d3dmclock v1.00

WindowsMobile6.0/6.1 で動作します。加速センサーが無くても起動します。
ただし 3Dアクセラレータがないと非常に低速です。
おそらく HTC Touch Diamond 系、Touch Pro 等で動作すると思われます。

加速センサー以外にも、画面タッチの左右で回転、上下でズームします。

d3dmclock

↑どんな角度にもなります。

背景画像は適当なので入れ替えて使ってください。
プログラムと同じフォルダに入っている bgimage0.bmp ~ bgimage2.bmp です。
サイズが 256x256 や 512x512 ならおそらく何でも大丈夫です。


textimage0.dds~textimage7.dds ファイルを置き換えれば、数字用のテクスチャも
入れ替えできます。こちらもサイズは 2の n乗で mipmap が必要です。
フォーマットは R5G6B5 にしてください。


Menu の Light にチェックを入れると、光源を操作できます。
この状態で画面をタッチすると、タッチした位置から画面中央に向かうベクトルを
光源の向きと見なします。

iPhone/iPod touch みたいにスムーズに動くものを、と思ったけどなかなか難しいですね。
Touch Diamond 系をお持ちの方はよろしければ一度お試しください。


関連エントリ
HTC Touch Diamond で Direct3DMobile その(8) ノーマルマップの解説
HTC Touch Diamond で Direct3DMobile その(7) ノーマルマップを表示する
HTC Touch Diamond で Direct3DMobile その(6) 頂点性能続き
HTC Touch Diamond で Direct3DMobile その(5)
HTC Touch Diamond で Direct3DMobile その(4) 高速化
HTC Touch Diamond で Direct3DMobile その(3) 実際の頂点性能
HTC Touch Diamond で Direct3DMobile を使う。その (2)
HTC Touch Diamond で Direct3DMobile を使う。ハードウエアアクセラレータ



設定等をもう少し説明してみます。(→前回)

Direct3DMobile Touch Diamond

ダウンロードはこちら
bumptest v1.00

スマートフォン HTC Touch Diamond は Qualcomm MSM7201A 内蔵の
3D アクセラレータを搭載しており ATI の GPU core となっています。

Direct3DMobile は PC でいう DirectX8 のサブセットですが、機能自体は
DirectX7 世代相当で固定パイプラインのみ。シェーダーは使えません。

caps を調べるとハードウエアアクセラレータとして認識していることがわかります。

D3DMDEVCAPS_HWTRANSFORMANDLIGHT
D3DMDEVCAPS_HWRASTERIZATION
D3DMDEVCAPS_NATIVEFLOAT

また使用できるマルチテクスチャは 2枚で TextureStage 数も 2段です。
無理矢理シェーダーにたとえれば ShaderModel 1.0 でピクセル単位に

・テクスチャ命令 x2
・演算命令 x2

を実行できるということ。DirectX7 世代のレジスタコンバイナそのままです。
TextureStage で使用できる演算として D3DMTEXOPCAPS_DOTPRODUCT3 が有効に
なっていたのでこれを使用します。


TextureStage には外部から与えられる定数がありません。
試しに D3DMRS_DIFFUSEMATERIALSOURCE を使って頂点カラーの代わりに Material
からパラメータを取れないか試しましたがだめでした。
これはライティングユニットへの入力時にのみ機能するようです。

外部から定数を渡すことが出来ればすぐにでもオブジェクトスペースの
ノーマルマップでテストできるのですがあきらめます。
パラメータは頂点カラーとして渡すことにします。
どうせ頂点を経由するなら最初からタンジェントスペースにします。

頂点の光源計算はもともと CPU で行っていました。
このときオブジェクトスペースに変換した光源ベクトルを使いますが、ここで
DotProduct せずにベクトルをそのままカラー値として出力します。

まずは D3DMTEXOPCAPS_DOTPRODUCT3 のベクトルフォーマットを確認します。
いくつかのカラー値を通してみてどのような復号が行われているか推測。
マニュアルには符号付きとだけ書かれており明確ではありませんでしたが、
やはりシェーダー同様 _bx2 相当 ( (x-0.5)*2 ) でした。


モデルデータの頂点にはオブジェクトスペースからタンジェントスペースへの変換
マトリクスを埋め込んでおきます。
1軸は法線と共有するため +2 vect。CrossProduct するなら +1 のみ。
データの生成と export は昔 3ds Max 用に作った自作プラグインを使用しています。

WorldSpace (GlobalSpace)
   → ObjectSpace (LocalSpace)
       → TangentSpace (TextureSpace)

元の光源ベクトルは WorldSpace に配置されているため、頂点法線も WorldMatrix
(3x3) で変換してから演算する必要があります。
一般的に光源の数より頂点の方が多いので、CPU 演算の手抜きライトの場合は光源の方を
WorldMatrix(3x3) の逆行列で ObjectSpace に変換した方が演算量が減ります。
同じようにノーマルマップからサンプリングした法線を適用するため、
光源ベクトルを TangentSpace まで逆変換します。

頂点単位のライティングの代わりに、オブジェクトスペースへ変換した光源ベクトルを
タンジェントスペースへ変換する演算を追加します。変換 matrix (3x3)をかけるだけ。
結果を 0~1.0 に収まるよう _bx2 の逆変換で格納します。
固定小数演算なのでビットシフト等で最適化します。

vect3 tslightdir;
TangentMatrix.Transformation( tslightdir, oslightdir );
colorr= ((tslightdir.x+ FIXED16(1.0f))* 255)>>17;
colorg= ((tslightdir.y+ FIXED16(1.0f))* 255)>>17;
colorb= ((tslightdir.z+ FIXED16(1.0f))* 255)>>17;

シェーダーがあれば VertexShader でやっているところです。
またノーマルマップ生成ツールによって座標系が異なってることがあるので
軸の符号などは合わせます。

テクスチャステージでは読み込んだノーマルマップの値と頂点から出力した上記の
光源ベクトルを DotProduct します。

一段目はノーマルマップで光源演算。

SetTexture( 0, iTexture0 );
SetTextureStageState( 0, D3DMTSS_COLORARG1, D3DMTA_TEXTURE );
SetTextureStageState( 0, D3DMTSS_COLORARG2, D3DMTA_DIFFUSE );
SetTextureStageState( 0, D3DMTSS_RESULTARG, D3DMTA_CURRENT );
SetTextureStageState( 0, D3DMTSS_COLOROP, D3DMTOP_DOTPRODUCT3 );
SetTextureStageState( 0, D3DMTSS_ALPHAOP, D3DMTOP_DISABLE );
SetTextureStageState( 0, D3DMTSS_TEXCOORDINDEX, 0|D3DMTSS_TCI_PASSTHRU );

二段目はカラーマップの合成。

SetTexture( 1, iTexture1 );
SetTextureStageState( 1, D3DMTSS_COLORARG1, D3DMTA_TEXTURE );
SetTextureStageState( 1, D3DMTSS_COLORARG2, D3DMTA_CURRENT );
SetTextureStageState( 1, D3DMTSS_RESULTARG, D3DMTA_CURRENT );
SetTextureStageState( 1, D3DMTSS_COLOROP, D3DMTOP_MODULATE );
SetTextureStageState( 1, D3DMTSS_ALPHAOP, D3DMTOP_DISABLE );
SetTextureStageState( 1, D3DMTSS_TEXCOORDINDEX, 0|D3DMTSS_TCI_PASSTHRU );

まだ ALPHA があいています。
PowerVR だと適用可能なオペレーションの種類がもっと多いのでいろいろ出来るでしょう。


関連エントリ
HTC Touch Diamond で Direct3DMobile その(7) ノーマルマップを表示する
HTC Touch Diamond で Direct3DMobile その(6) 頂点性能続き
HTC Touch Diamond で Direct3DMobile その(5)
HTC Touch Diamond で Direct3DMobile その(4) 高速化
HTC Touch Diamond で Direct3DMobile その(3) 実際の頂点性能
HTC Touch Diamond で Direct3DMobile を使う。その (2)
HTC Touch Diamond で Direct3DMobile を使う。ハードウエアアクセラレータ



Touch Diamond でノーマルマップの表示が出来ました。

bumptest d3dm normalmap

だいたい 22~24fps くらい。
さすがにピクセル単位のライティングは、ローポリでもきれいに出ます。
やっとハードウエアアクセラレータらしい表示が出るようになりました。

bumptest color only
↑カラーマップだけ

normalmap only
↑ノーマルマップだけ

normalmap + colormap
↑両方適用

テクスチャは PC 用 Direct3D のサンプルを使用しています。
本当はレリーフマップ用のデータですが、ただのノーマルマップとしてだけ使っています。
これが PC のシェーダーだったら本当はレイキャストで視差も出るし影も落ちるのですが。


この WindowsMobile 用のプログラムは下記から落として試せるようにしました。

ダウンロード
bumptest v1.00


●動作を確認したもの

◎ハードウエアアクセラレータが有効なもの

 ・HTC Touch Diamond (EMOBILE S21HT 他)
   Qualcomm MSM7201A 内蔵 ATI core
   Touch Diamond はハードウエアアクセラレータで高速に動作します。
   おそらく Touch Pro もいける。

◎ハードウエアアクセラレータが無効だけど動くもの

 ・HTC Touch Dual (EMONSTER lite S12HT 等)
   CPU のリファレンスラスタライザで起動します。
   非常に低速ですが画面はきちんと出ます。

◎動作しないもの

 ・SHARP EM・ONEα S01SH2
   CPU ラスタライザですが XScale ドライバ D3DMXSC50PB です。
   マルチテクスチャ&マルチ UV 未対応なため起動しません。

各端末の 3D 機能について詳しくはこちらを参照してください。

http://hp.vector.co.jp/authors/VA004474/wince/d3dmcapslist.html


●解説など

頂点が遅い&ハードウエアライティングが遅いので、ピクセルライティングを
試してみました。思ったよりきれいに出ました。
これならローポリでも違和感なく表示できそうです。
ただしその分テクスチャが増えます。

タンジェントスペースの計算は CPU で行っています。
前回調べた結果の通り、CPU ですべて頂点変換を行ったとしてもその割合は
たいしたことありません。
よって CPU 側で少々凝った計算をしても全体へはさほど影響しないとの判断です。

ただ何度も述べてるように、rhw バグがあるため CPU だけで全部の計算を行うと
テクスチャのゆがみが見えてしまいます。
そこで次のようにします。

(1) 動的に CPU で頂点バッファを生成する
   ライティングやタンジェントスペースの計算はここで行う。
   頂点カラーを求める。
   座標は素通りさせる。

(2) 座標変換は Direct3D Mobile に任せる
   ここではライティングせずに座標変換のみ。

これで rhw バグを回避できますし、Direct3D Mobile の重いライティングを使わずにすみます。

済むはずでした。

rhw バグは根が深いようです。
フォントの表示など 3D 変換が不要な頂点には最初からスクリーンスペースの座標を
与えると簡単ですが、この場合頂点は XYZRHW 形式になります。
モデルの方は Direct3D Mobile に任せるため RHW の無い XYZ です。

この両方の頂点形式のデータを交互に描画すると、XYZ 形式のモデルが表示されなくなるようです。
仕方なくフォントも 2D 変換の Projection を通して描画するように変更。

その他トラブル
・ZWRITEENABLE を FALSE にすると表示が崩れるので ON 固定に。
・TextureStage の TEXCOORDINDEX が効いてない気がするので頂点側を MultiUV に変更。
・MipFilter を有効にすると MipMap が強制される。MipMap 無しのテクスチャが含まれていると真っ白になる。


問題が多く低速な頂点に比べると、ピクセル側は比較的問題が少なく素直に動いている気がします。
使用可能なテクスチャ形式が少ないことが少々難点です。
16bit フォーマットを選ばざるを得ないので、結局 565 になってしまいます。

22~24 fps くらいしか出てませんが、Touch Diamond の d3dm_ati.dll だとなぜか
空のデータでも最大 28fps 程度にしかならないので、これでも軽い方なのです。


関連エントリ
HTC Touch Diamond で Direct3DMobile その(6) 頂点性能続き
HTC Touch Diamond で Direct3DMobile その(5)
HTC Touch Diamond で Direct3DMobile その(4) 高速化
HTC Touch Diamond で Direct3DMobile その(3) 実際の頂点性能
HTC Touch Diamond で Direct3DMobile を使う。その (2)
HTC Touch Diamond で Direct3DMobile を使う。ハードウエアアクセラレータ



●EeePC 901-X

これまでもずっと EeePC 901 で WindowsVista を使い続けてきたため、特に違和感も
無く普通に使えています。
ただ Vista の時と同じように RAMDISK の設定は行いました。

EeePC の画面縦 600dot だとダイアログが切れることがあるため、タスクバーは左端
縦に配置しています。もともと文字が無くアイコンだけなので意外に大丈夫かも。
Windows7 のタスクバーは他のウィンドウの下にならないので、解像度が足りないと
結構邪魔です。

電源設定は修正。休止状態無しでスリープのみ行うように。
仮想メモリもページングファイル無しに設定しています。

BIOS を更新したらログイン画面などで暗転するようになったので徐々に戻しました。
とりあえず BIOS 902 だと問題なく動いています。
× 1808, 1703, 1301
○ 902
これ以外は未確認。

エクスペリエンスインデックスは、ページングファイル無しで、かつテンポラリを
RAMDISK に設定していると評価に失敗します。
あれこれ設定を行う前に評価を済ませておいた方が良いです。


●FMV BIBLO LOOX U50

A110 800MHz の古いタイプなので、Atom 機と比べると重いです。
ですが、もともと Vista が乗っているマシンなのでほぼそのまま。
指紋認証は使っていませんがワンセグも動いています。
タッチパネルの補正が失敗。
追記: タッチパネル補正できました。

FMV BIBLO LOOX U50X/V
Intel A110 800MHz / RAM 1GB / 1.8HDD
CPU 1.5
RAM 3.9
AERO 2.0
GAME 2.4
HDD 3.7

EeePC 901-X
Atom N270 1.6GHz / RAM 2GB / 32GB SSD (SHD-DI9M)
CPU 2.2
RAM 4.5
AERO 2.1
GAME 3.0
HDD 2.9



関連エントリ
LOOX U でも Windows7 beta
EeePC 901 Windows7 beta
EeePC 901 SHD-DI9M 32G と Vista 設定 (2)
EeePC 901 の SSD 交換 BUFFALO SHD-DI9M 32G と Vista
EeePC 901 に Windows Vista その5
EeePC 901 に WindowsVista



Touch Diamond で Direct3DMobile を使った描画の続きです。
CPU 側の各処理の測定時間になります。

頂点数 ポリゴン数  頂点計算時間  1frame合計    cpuv/v   total/v  total/t
   401       760           700       40000      1.75     99.75    52.63
  1679      3120          2800       46000      1.67     27.40    14.74
  5039      9660          8500       90000      1.69     17.86     9.32
 10199     19800         17000      160000      1.67     15.69     8.08
 22799     44700         38000      348000      1.67     15.26     7.85
                          usec        usec

CPU で行う頂点の変換自体は float よりも固定小数の方が 5~6 倍ほど高速でした。
それでも描画速度向上がわずかだったのは、全体の処理時間からみたら
たいしたことがなかったからです。
全体の処理時間のうち頂点計算の割合は 10% 程度。
残りは全部 Present() が占めます。

カメラ位置を変えてポリゴン自体の描画面積が変わっても、動作速度にはほとんど
変動がありませんでした。
よって kick したあとの実際の描画処理では、頂点数に比例して増加するなんらかの
重い処理が CPU で行われていると予想できます。
あとはレンダーステートの組み合わせなど軽くなる条件を探していくしかなさそうです。

iPhone/iPod touch の実際の描画性能がどのくらいなのか気になるところです。


関連エントリ
HTC Touch Diamond で Direct3DMobile その(5)
HTC Touch Diamond で Direct3DMobile その(4) 高速化
HTC Touch Diamond で Direct3DMobile その(3) 実際の頂点性能
HTC Touch Diamond で Direct3DMobile を使う。その (2)
HTC Touch Diamond で Direct3DMobile を使う。ハードウエアアクセラレータ



looxu50 windows7 beta
LOOX U50 Windows7 beta build 7000

LOOX U50X/V A110

CPU  1.5
RAM  3.9
AERO 2.0
GAME 2.4
HDD  3.7

クリーンインストールです。
EeePC 901 同様最初から Aero ON でした。
LAN/WLAN はそのまま認識したのでネットは使えます。
いくつか足りないドライバがあるけどワンセグその他
追記: 付属 CD-ROM アプリケーションディスク1 より
pt901_p1, button\driver, button\utility, o2micro, fpsensor を install

EeePC 901 はこちら

EeePC 901 N270

CPU  2.2
RAM  4.5
AERO 2.1
GAME 3.0
HDD  2.9



関連エントリ
EeePC 901 Windows7 beta



とりあえず EeePC 901 で試しています。
Windows7 beta build 7000
RAM 2GB + SSD 32GB へ増設済み

eeepc win7

LAN/WLAN 以外はそのままで認識。最初から Aero Glass が有効でした。
LAN/WLAN ドライバは Vista と同じように付属 CD-ROM のもので OK。

Vista フルインストール時と異なり最初から使い物になる印象。
不要なサービスを切ったり設定を詰めなくても軽いです。
RAMDISK 等何も設定していませんが十分かもしれません。
AeroGlass の描画は、Vista の方がかっちり同期をとって描画していた印象。
ただしまだ beta なので何ともいえません。

いろいろ使いやすく軽くなっている反面、Vista で気に入っていた設定が
できなくなってるところも結構あって複雑な気分。
タスクバーとかは慣れが必要です。

d3d10warp.dll、d3d11.dll が最初から入っています。
d2d1.dll というものも。


関連エントリ
EeePC 901 SHD-DI9M 32G と Vista 設定 (2)
EeePC 901 の SSD 交換 BUFFALO SHD-DI9M 32G と Vista
EeePC 901 に Windows Vista その5
EeePC 901 に WindowsVista



時間が無くてほとんど触っていませんが、、結果速くなりませんでした。
CPU 処理の方が有利と結論づけるにはまだ検証が足りないようです。

光源ありの場合は不要なものを大幅に省けるので相対的に速度が出ます。
逆に軽いデータでもピーク性能が全く伸びません。
少々触っただけでは何ともいえないので、時間があるときに詳しく測定して
原因を特定する必要がありそうです。

rhw の問題も回避手段が見つからないので、その(3) の結果を受け入れた方が
良いのかもしれません。

・ FIXED のテクスチャ頂点の問題

頂点形式を固定少数 D3DMFVF_TEXCOORDFIXED(0) にした場合に uv 値がおかしい、
テクスチャが出なくなるという問題がありました。

調べたところ FIXED 指定は無視され内部では常に float でアクセスしているようです。
FVF に何を指定しようが float 値を書き込むとうまくいきます。
やはりセットアップエンジンへは float で値が渡っているのでしょうか。
頂点も演算だけ FIXED で行い、バッファへは float で書き込んでみるのもありかも
しれません。

・ rhw の問題

D3DMFVF_XYZRHW_FLOAT や D3DMFVF_XYZRHW_FIXED の rhw に 1.0 以外の値を
書き込むと、ポリゴン自体の描画位置がずれてしまう問題があります。
こちらは特に触っていないので進展は無いです。


関連エントリ
HTC Touch Diamond で Direct3DMobile その(4) 高速化
HTC Touch Diamond で Direct3DMobile その(3) 実際の頂点性能
HTC Touch Diamond で Direct3DMobile を使う。その (2)
HTC Touch Diamond で Direct3DMobile を使う。ハードウエアアクセラレータ



速くなりました。光源入りで 4つ表示して 23.2fps。
その(2) の時は 1つで 18.5fps しか出ていなかったデータです。

Qualcomm MSM7201A Direct3DMobile
TouchDiamond (Qualcomm MSM7201A) Direct3DMobile

前回 調べた結果から

数値を見る限り 30fps でフレーム 10000頂点くらいがいいところでしょうか。
しかもデータ側の頂点数ではなく実演算の頂点数です。
ポリゴン数だとがんばって 3000~4000ポリゴン相当くらい。

さらに裏面や画面外など、描画してないけど計算してしまったポリゴンも含むので
実際に使うと予想以上に少なく感じるはずです。
頂点がかなり足を引っ張る形になります。
(API 呼び出しのオーバーヘッドとかフィル周りはまだ未調査です)
Dreamcast とか PSP クラス とかとても無理で、おそらく DS 未満。

光源を入れるとさらに 2.5 倍ほど重くなるようです。

いろいろ考えた結果、caps が HWTRANSFORMANDLIGHT を返してくるのはたぶん嘘では
ないかと。頂点はおそらく CPU 計算です。


試しに CPU だけで頂点の変換を行ってみたところ、同等以上の速度で描画することができました。
しかも FPU (VFP) が乗ってない CPU で float 演算しているのにこの速度。
caps が NATIVEFLOAT を返してくるので D3DM も CPU で float 演算している可能性が
あります。

固定少数でためしたら、float よりちょっとだけ速くなりました。
これが 最初にのせた 23.2fps の結果となります。

最適化とかほとんど考慮していないのに、それなりに速いのには理由があります。

 ・クリッピングしていない
 ・頂点バッファ単位で変換しているので共有頂点は一度しか計算していない
 ・光源計算は必要最小限で不要なものを省いている

よって実際に使える状態まで機能を入れるともっと負担が多くなるはずです。


●ここまでの結論

 Touch Diamond で D3DM を使う場合
 ・CPU で自前で頂点演算をやる
 ・CPU 内で完結するものは固定小数演算

自前で書けば、プログラマブルシェーダーのように必要最小限な処理に最適化できます。
ライティングなども実用的な速度で動作すると思います。


●問題点

実際に CPU で頂点演算をしてみると、いくつか問題が出てきました。

・rhw の挙動がおかしい

 自分でスクリーンスペースまで変換した場合、変換後の頂点の rhw の挙動が
 どうもおかしい。狙った通りの結果になりません。
 これがだめだとパースペクティブコレクションが効かないので少々致命的です。

・Fixed 形式の頂点だと Texture の uv が出ない

 負の uv 値でおかしなテクスチャの値になる。原因調査中


関連エントリ
HTC Touch Diamond で Direct3DMobile その(3) 実際の頂点性能
HTC Touch Diamond で Direct3DMobile を使う。その (2)
HTC Touch Diamond で Direct3DMobile を使う。ハードウエアアクセラレータ



HTC Touch Diamond の 3D アクセラレータの速度テストです。
Qualcomm MSM7201A 内蔵 ATI core

時間がないので軽く調査、厳密なテストではないです。
頂点性能だけ見たかったので TriangleStrip で見えるか見えないかぎりぎりの
極小ポリゴン描画を並べたもの。ほとんど 1pixel 以下です。
単一バッファで一度だけ DrawPrimitive() しています。


●低負荷と思われるフォーマットでどれだけ頂点処理できるか

テクスチャ無し、ライティング無し、頂点カラーのみ

FVF = D3DMFVF_XYZ_FLOAT|D3DMFVF_DIFFUSE
struct { // 16byte
	float	x, y, z;
	DWORD	color;
};

頂点数  解像度  頂点形式     速度
 16384   VGA    xyz col     17.0fps   58msec
 32768   VGA    xyz col      9.6fps  103msec
 49152   VGA    xyz col      7.8fps  128msec
 65536   VGA    xyz col      5.7fps  176msec

おおざっぱに 16K 頂点が 40msec とすると、どんなにがんばっても最高 40万頂点/sec
程度しか出ない計算に。
ボトルネックが、頂点補充が追いつかないのか演算なのかは不明。


●ピーク速度に近づきたいために頂点カラーも無くしてみる

FVF = D3DMFVF_XYZ_FLOAT
struct { // 12byte
	float	x, y, z;
};

頂点数  解像度  頂点形式     速度
 16384   VGA    xyz         17.4fps   57msec
 32768   VGA    xyz          9.9fps  100msec
 49152   VGA    xyz          8.0fps  125msec
 65536   VGA    xyz          5.8fps  172msec

頂点カラーの影響は 16K 頂点あたり 1msec くらい。
65536頂点時に 38万頂点/sec


●解像度変更

頂点数  解像度  頂点形式     速度
 16384   VGA    xyz         17.4fps   57msec
 16384   HVGA   xyz         15.7fps   63msec
 16384   QVGA   xyz         22.5fps   44msec
 65536   VGA    xyz          5.8fps  172msec
 65536   HVGA   xyz          9.2fps  108msec
 65536   QVGA   xyz         11.2fps   88msec

フィルの影響も調べるために解像度も変えてテスト。
ポリゴン描画以外はクリアと Flip (COPY) だけなのに解像度の影響がかなりあります。
頂点数の増加によって解像度の影響をより強く受けているので、極小ポリゴンとは
いえそれなりにフィルを消費していることがわかります。

VGA = 639x480 (なぜ 639 なのかはこちら)
HVGA = 480x320 (iPhone/iPod touch や Android G1 がこれ)
QVGA = 320x240


●Memory Pool の違い

頂点数  解像度  頂点形式  Pool          速度
 65536   VGA    xyz       videomem      5.8fps 172msec
 65536   VGA    xyz       systemmem     5.8fps 172msec

caps ではどちらにも配置可能。特に差は無し。
速度が同じなのか、最初から同じメモリなのか、内部で Pool を無視して同一処理して
いるのかは不明。


●Index かどうか

もともと TriangleStrip だし頂点キャッシュが(もしあったとしても)有効なケース
ではないので、アクセスすべきメモリが増えた分だけ遅くなるはず。

頂点数  解像度  頂点形式  Index                   速度
 65536   VGA    xyz       DrawPrimitive           5.8fps 172msec
 65536   VGA    xyz       DrawIndexedPrimitive    5.8fps 173msec

結果、誤差の範囲でほとんど差は出ていません。


●浮動小数と固定少数の違い

頂点形式を FIXED にしても全く差がなかったです。
もともと caps が NATIVEFLOAT を返してくるし HW T&L 入ってるので Float だと
思いますが、どちらにせよ頂点バッファを作った時点で効率よい形式に
変換されていると考えられます。


● Texture つき

一番実用に近いと思われる頂点形式でテストです。
これが使い物になれば何とでもなる。

FVF=	D3DMFVF_XYZ_FLOAT|D3DMFVF_TEX1
	|D3DMFVF_TEXCOORDSIZE2(0)|D3DMFVF_TEXCOORDFLOAT(0)
struct _VTYPE {
	float	x, y, z;
	float	tu, tv;
};

頂点数  解像度  頂点形式       速度
 16384   VGA    xyz tex       16.8fps   59msec
 32768   VGA    xyz tex        9.6fps  104msec
 49152   VGA    xyz tex        7.6fps  131msec
 65536   VGA    xyz tex        5.3fps  190msec
  1000   QVGA   xyz tex       28.5fps   35msec
  1000   VGA    xyz tex       28.0fps   35msec
  5000   VGA    xyz tex       27.4fps   36msec
 10000   VGA    xyz tex       25.8fps   38msec
 20000   VGA    xyz tex       15.9fps   62msec

頂点の負担を見ることが目的なので、結果に大きな影響が出ないように Texture は
1pixel しか読み込んでいません。

ずっと気になっていたのが、必ず 29fps くらいで頭打ちになること。
設定はどれも D3DMPRESENT_INTERVAL_IMMEDIATE です。
これを D3DMPRESENT_INTERVAL_ONE にしてもなぜか同じ。


● Texture + Color つき

かなり使われることが多いフォーマットです。
これが使えればテクスチャを減らせます。

FVF=	D3DMFVF_XYZ_FLOAT|D3DMFVF_DIFFUSE|D3DMFVF_TEX1
	|D3DMFVF_TEXCOORDSIZE2(0)|D3DMFVF_TEXCOORDFLOAT(0)
struct _VTYPE {
	float	x, y, z;
	DWORD	color;
	float	tu, tv;
};

頂点数  解像度  頂点形式       速度
 16384   VGA    xyz col tex   16.6fps   60msec
 32768   VGA    xyz col tex    9.2fps  108msec
 49152   VGA    xyz col tex    6.9fps  145msec
 65536   VGA    xyz col tex    5.2fps  193msec
 10000   VGA    xyz col tex   25.8fps   38msec
 20000   VGA    xyz col tex   15.9fps   63msec

実用的な頂点処理能力としては、フレームあたり 1万ポリゴンくらいが限界といったところです。
ただし単一バッファで一回の描画でこの数値です。
実際は多数の DrawPrimitive() を呼び出すことになるので速度は落ちるはずです。


また前回やった通りライティングはかなり遅いのでテストしてません。
●光源処理あり 2008/01/07 3:02 追記

せっかくなので本当にどれだけ遅いのか光源もテストしてみました。
ハードウエア光源を使うだけで頂点が 2.5倍以上重くなっています。
これは純粋