2014/03/26
PS Mobile fl 関数電卓 / Vita で動く関数電卓
PS Mobile 向け関数電卓をリリースしました。
PlayStation Mobile の Sotre からダウンロードすることができます。

・fl 関数電卓 (PS Mobile)
PS Vita だけでなく PS Mobile 対応の HTC J Butterfly や Xperia でも動きます。

↑Xperia Acro
PSMobile の UI にある EditBox (EdittableText) は、Software Keyboard 側の
入力領域を用いる仕様になっているためインライン編集を行うことができませんでした。
そのためカーソル表示やテキスト編集、カーソル位置のタッチ判定など自前で実装しています。
Android/iOS の ちょっと電卓 では、MI-Zaurus / Pocket PC 版と同じ
数式エンジンを使用しています。これは C言語で書かれています。
PS Mobile の開発言語は C# で、ソースコードを共有できないため新たに書き起こしました。
名称が違うのはそのためです。
最近 SDK / Firmware が更新されたらしく、フォントキャッシュの生成が高速化されているようです。
当初は新しい Dialog を開くタイミングで、フォント展開が発生し少々待たされることがありました。
開発中は回避策が見つからずに悩んだ部分だったので嬉しい改良です。
メモリやヒストリは自動的に保存しています。
アプリ終了やバックグラウンド移行など、オートセーブに都合の良いイベントが
見つからなかったため PersistentMemory を利用してます。
手軽に扱える反面、容量制限があるので適切な UI イベントも併用できるようにして欲しいところです。
方向キーなどの物理ボタン操作に対応していないのは、
有効にすると Android 上でソフトウエアコントローラが表示されてしまうためです。
UIScale の設定は、アスペクト比の違いにより縦方向でずれが生じるため
縦と横で別の調整を加えています。
関連エントリ
・PlayStation Suite と対応ハードの性能比較
PlayStation Mobile の Sotre からダウンロードすることができます。

・fl 関数電卓 (PS Mobile)
PS Vita だけでなく PS Mobile 対応の HTC J Butterfly や Xperia でも動きます。

↑Xperia Acro
PSMobile の UI にある EditBox (EdittableText) は、Software Keyboard 側の
入力領域を用いる仕様になっているためインライン編集を行うことができませんでした。
そのためカーソル表示やテキスト編集、カーソル位置のタッチ判定など自前で実装しています。
Android/iOS の ちょっと電卓 では、MI-Zaurus / Pocket PC 版と同じ
数式エンジンを使用しています。これは C言語で書かれています。
PS Mobile の開発言語は C# で、ソースコードを共有できないため新たに書き起こしました。
名称が違うのはそのためです。
最近 SDK / Firmware が更新されたらしく、フォントキャッシュの生成が高速化されているようです。
当初は新しい Dialog を開くタイミングで、フォント展開が発生し少々待たされることがありました。
開発中は回避策が見つからずに悩んだ部分だったので嬉しい改良です。
メモリやヒストリは自動的に保存しています。
アプリ終了やバックグラウンド移行など、オートセーブに都合の良いイベントが
見つからなかったため PersistentMemory を利用してます。
手軽に扱える反面、容量制限があるので適切な UI イベントも併用できるようにして欲しいところです。
方向キーなどの物理ボタン操作に対応していないのは、
有効にすると Android 上でソフトウエアコントローラが表示されてしまうためです。
UIScale の設定は、アスペクト比の違いにより縦方向でずれが生じるため
縦と横で別の調整を加えています。
関連エントリ
・PlayStation Suite と対応ハードの性能比較
Android NDK r9d の GLES2/gl2ext.h には、本来無いはずの "include gl2.h" が
追加されているために、OpenGL ES 3.0 と組み合わせて利用できなくなっています。
r9d で追加されたようです。
GLES3/gl3ext.h は中身が空なので、例えば GPU 固有のテクスチャフォーマット
PVR, ATC, DXT 等のシンボルを用いるには gl2ext.h を include する必要がありました。
とりあえずの対処としては、
Android の場合だけ __gl2_h_ を定義してしまう方法があります。
Android 以外や Khronos のヘッダファイルでは問題ありません。
関連エントリ
・Android NDK r9d の armeabi-v7a-hard と ABI
・iPhone 5s の Apple A7 GPU
・Nexus 7 (2013) の Adreno 320 と OpenGL ES 3.0 (Android 4.3)
・OpenGL ES 3.0/OpenGL 4.x Uniform Block
追加されているために、OpenGL ES 3.0 と組み合わせて利用できなくなっています。
r9d で追加されたようです。
#include <GLES2/gl2.h>
GLES3/gl3ext.h は中身が空なので、例えば GPU 固有のテクスチャフォーマット
PVR, ATC, DXT 等のシンボルを用いるには gl2ext.h を include する必要がありました。
とりあえずの対処としては、
Android の場合だけ __gl2_h_ を定義してしまう方法があります。
#define __gl2_h_ // 追加
#include <GLES3/gl3.h>
#include <GLES2/gl2ext.h>
#include <GLES3/gl3.h>
#include <GLES2/gl2ext.h>
Android 以外や Khronos のヘッダファイルでは問題ありません。
関連エントリ
・Android NDK r9d の armeabi-v7a-hard と ABI
・iPhone 5s の Apple A7 GPU
・Nexus 7 (2013) の Adreno 320 と OpenGL ES 3.0 (Android 4.3)
・OpenGL ES 3.0/OpenGL 4.x Uniform Block
OpenGL ES 3.1 の仕様が公開されました。
バージョン番号は 3 のままですが、内容は OpenGL 4.x の機能を
取り込んだ大幅なアップデートとなっています。
最近のハイエンド Mobile GPU は、Windows Tablet を想定して Direct3D 11
API も積極的にサポートしつつあります。
OpenGL ES 3.1 にはこれらの GPU の進化がそのまま反映されていると言えます。
・Khronos OpenGL ES Registry
ユニークなのは、OpenGL ES が GeometryShader や Tessellator 等の
組み込みのシェーダーパイプラインを採用しなかったことです。
その代わり柔軟で扱いやすい ComputeShader に仕様を絞り込んでおり、
機能と自由度のバランスを上手く保っている印象です。
より詳しい表はこちら↓
・GLSL Version
ComputeShader をサポートしたことで、同時にバッファのランダムアクセスや
読み書き、Atomic なオペレーション命令などもひと通り入りました。
どれも OpenGL 4.x 相当です。Indirect Draw もあります。
バッファアクセスについて詳しくは下記のエントリを参照してください。
・OpenGL 3.x/4.x のシェーダー GLSL とメモリアクセス命令
buffer 命令が増えたことで GLSL の layout() 構文も賑やかになりました。
これまで Uniform や Attribute のバインドには、OpenGL API を使って
Location を参照したり値を書き換える必要がありました。
GLES 3.1 (と OpenGL 4.3以降) は GLSL 内に直接数値で宣言できます。
また Direct3D のように実行時にシェーダーのステージを組み合わせられる
ようになります。詳細は下記をどうぞ。
・OpenGL 4.x Program Pipeline Object (Separate Shader Object)
Shader Stage は少ないものの、OpenGL ES 3.1 は OpenGL 4 の特徴を取り入れており
Direct3D との親和性(移植しやすさ) が向上していることがわかります。
OpenGL 4.x とのコード共有もしやすくなるのですが、
Desktop PC では Intel GPU が OpenGL 4.0~4.1 に留まっており、
上記の機能の多くを使うことができません。
OpenGL で ComputeShader を当たり前に使えるようになるには
まだ時間がかかりそうです。
OpenGL ES の GLSL が暗黙の型変換をサポートしないのは 3.1 でも同様で、
仕様となっています。
Uniform Block (Interface Block) の input/output はサポートされませんでした。
GLES3.1 は OpenGL ES 2.0/3.0 との上位互換性を保っています。
Mobile GPU が活発になってからは、バージョンが進むほど下位互換性が復活する
傾向があります。
OpenGL 3~4 もそうですし、Direct3D 10~11 も同様でした。
Mobile GPU が高機能になるにつれて、Desktop GPU 向け API と区別する
必要性はなくなりつつあります。
OpenGL を使う側の立場としても、最終的には統合して欲しい願っています。
ですが、WebGL のように軽量な API として用途が広がる可能性を考えると
ComputeShader に絞った仕様は良い選択かも知れません。
関連エントリ
・iPhone 5s の Apple A7 GPU
・Nexus 7 (2013) の Adreno 320 と OpenGL ES 3.0 (Android 4.3)
・OpenGL 3.x/4.x のシェーダー GLSL とメモリアクセス命令
・OpenGL 4.x Program Pipeline Object (Separate Shader Object)
・OpenGL 4.2/4.3 Shader Resource と Buffer API
・OpenGL ES 3.0/OpenGL 4.x Uniform Block
・OpenGL の各バージョンと GLSL の互換性
・OpenGL のエラー判定と OpenGL 4.3 Debug Output
・OpenGL ES 3.0/OpenGL 4.4 Texture Object と Sampler Object
・OpenGL ES 3.0 と Vertex Array Object
バージョン番号は 3 のままですが、内容は OpenGL 4.x の機能を
取り込んだ大幅なアップデートとなっています。
最近のハイエンド Mobile GPU は、Windows Tablet を想定して Direct3D 11
API も積極的にサポートしつつあります。
OpenGL ES 3.1 にはこれらの GPU の進化がそのまま反映されていると言えます。
・Khronos OpenGL ES Registry
世代毎の API OpenGL ES OpenGL Direct3D ShaderModel ------------------------------------------------------------- OpenGL ES 2.0 OpenGL 2.1 Direct3D9 ShaderModel 3.0 OpenGL ES 3.0 OpenGL 3.3 Direct3D10.1 ShaderModel 4.1 OpenGL ES 3.1 OpenGL 4.4 Direct3D11.2 ShaderModel 5.0
ユニークなのは、OpenGL ES が GeometryShader や Tessellator 等の
組み込みのシェーダーパイプラインを採用しなかったことです。
その代わり柔軟で扱いやすい ComputeShader に仕様を絞り込んでおり、
機能と自由度のバランスを上手く保っている印象です。
機能の比較 Shader Version ------------------------------------------------------------ OpenGL ES 2.0 vsh fsh --- --- --- --- #version 100 OpenGL ES 3.0 vsh fsh --- --- --- --- #version 300 es OpenGL ES 3.1 vsh fsh --- --- --- csh #version 310 es OpenGL 2.1 vsh fsh --- --- --- --- #version 120 OpenGL 3.3 vsh fsh gsh --- --- --- #version 330 OpenGL 4.4 vsh fsh gsh tcsh tesh csh #version 440 Direct3D 9 vsh psh --- --- --- --- 3.0 Direct3D 10 vsh psh gsh --- --- --- 4.0 Direct3D 11 vsh psh gsh hsh dsh csh 5.0
より詳しい表はこちら↓
・GLSL Version
ComputeShader をサポートしたことで、同時にバッファのランダムアクセスや
読み書き、Atomic なオペレーション命令などもひと通り入りました。
どれも OpenGL 4.x 相当です。Indirect Draw もあります。
Texture Image Load and Store OpenGL 4.2 Atomic Counter Buffer OpenGL 4.2 Shader Storage Buffer OpenGL 4.3 Shared Memory OpenGL 4.3
バッファアクセスについて詳しくは下記のエントリを参照してください。
・OpenGL 3.x/4.x のシェーダー GLSL とメモリアクセス命令
buffer 命令が増えたことで GLSL の layout() 構文も賑やかになりました。
これまで Uniform や Attribute のバインドには、OpenGL API を使って
Location を参照したり値を書き換える必要がありました。
GLES 3.1 (と OpenGL 4.3以降) は GLSL 内に直接数値で宣言できます。
また Direct3D のように実行時にシェーダーのステージを組み合わせられる
ようになります。詳細は下記をどうぞ。
・OpenGL 4.x Program Pipeline Object (Separate Shader Object)
Shader Stage は少ないものの、OpenGL ES 3.1 は OpenGL 4 の特徴を取り入れており
Direct3D との親和性(移植しやすさ) が向上していることがわかります。
OpenGL 4.x とのコード共有もしやすくなるのですが、
Desktop PC では Intel GPU が OpenGL 4.0~4.1 に留まっており、
上記の機能の多くを使うことができません。
OpenGL で ComputeShader を当たり前に使えるようになるには
まだ時間がかかりそうです。
OpenGL ES の GLSL が暗黙の型変換をサポートしないのは 3.1 でも同様で、
仕様となっています。
Uniform Block (Interface Block) の input/output はサポートされませんでした。
GLES3.1 は OpenGL ES 2.0/3.0 との上位互換性を保っています。
Mobile GPU が活発になってからは、バージョンが進むほど下位互換性が復活する
傾向があります。
OpenGL 3~4 もそうですし、Direct3D 10~11 も同様でした。
Mobile GPU が高機能になるにつれて、Desktop GPU 向け API と区別する
必要性はなくなりつつあります。
OpenGL を使う側の立場としても、最終的には統合して欲しい願っています。
ですが、WebGL のように軽量な API として用途が広がる可能性を考えると
ComputeShader に絞った仕様は良い選択かも知れません。
関連エントリ
・iPhone 5s の Apple A7 GPU
・Nexus 7 (2013) の Adreno 320 と OpenGL ES 3.0 (Android 4.3)
・OpenGL 3.x/4.x のシェーダー GLSL とメモリアクセス命令
・OpenGL 4.x Program Pipeline Object (Separate Shader Object)
・OpenGL 4.2/4.3 Shader Resource と Buffer API
・OpenGL ES 3.0/OpenGL 4.x Uniform Block
・OpenGL の各バージョンと GLSL の互換性
・OpenGL のエラー判定と OpenGL 4.3 Debug Output
・OpenGL ES 3.0/OpenGL 4.4 Texture Object と Sampler Object
・OpenGL ES 3.0 と Vertex Array Object
2014/03/15
Android NDK r9d の armeabi-v7a-hard と ABI
Android NDK r9d では、新しく APP_ABI に armeabi-v7a-hard が追加されました。
とはいえ端末側の ABI の種類が増えたわけではなく、hard-float を指定した
バイナリのビルドがより簡単に行えるようになります。
・Android NDK
r9b/r9c では hard-float を使うために jni/Android.mk に直接オプションを
記述していました。
r9d からは上記のような Android.mk の修正が不要になります。
APP_ABI に armeabi-v7a-hard を指定するだけで hard-float に対応します。
例えば ndk-build コマンドの引数で与えるなら下記の通り。
jni/Application.mk に書いておくなら下記の通り。
APP_ABI:=all ではデフォルトの armeabi-v7a とみなされるので、
hard-float を使う場合は上記のように全部列挙しておく必要があります。
Android OS 側は softfp のままなので、hard-float が有効なのはあくまで
アプリケーション内の関数呼び出しに限定されます。
例えば OpenGL ES のような浮動小数点パラメータを持つ System API の
呼び出しは、これまでどおり softfp が用いられます。
hard-float といいつつ厳密には softfp も混在していることになります。
そのため OS から見れば従来のアプリケーションと全く同じなので、
バイナリを区別する必要がありません。
armeabi-v7a-hard でビルドしたバイナリも libs/armaebi-v7a に入ります。
他の APP_ABI と違い、armeabi-v7a と armeabi-v7a-hard の 2種類生成する
わけではないのでご注意ください。
どちら片方を選択します。
互換性が問題になるケースはおそらく外部ライブラリ利用時でしょう。
リンクするライブラリも下記いずれかの方法で hard-float に対応している
必要があります。
ライブラリの hard-float 対応手段は二通りあります。
(1) ライブラリも hard-float でビルドする
(2) ヘッダファイルに softfp 指定 __NDK_FPABI__ を追加する
(1) の場合は spftfp 版と hard-float 版ライブラリの両方を用意しておく
必要があります。
(2) はヘッダの関数宣言時に softfp 呼び出しを意味する __NDK_FPABI__ を
記述しておきます。(中身は __attribute__((pcs("aapcs"))) )
この場合ライブラリは softfp 一種類だけです。
NDK の hard-float について詳しくは下記も参照してください。
・Android NDK r9b と ARMv7A の hard-float
Android NDK で使用可能な ABI 指定のまとめ。
ARM 以外は最初から FPU 必須で hard-float 相当です。
ちなみに ios は下記の通り。
soft-float, softfp, hard-float の違い。
関連エントリ
・Android NDK r9b と ARMv7A の hard-float
・iPhone 5s A7 64bit CPU と AArch64 (arm64)
・Nexus 7 の Ubuntu で ARM の abi softfp と hard-float を比べる
とはいえ端末側の ABI の種類が増えたわけではなく、hard-float を指定した
バイナリのビルドがより簡単に行えるようになります。
・Android NDK
r9b/r9c では hard-float を使うために jni/Android.mk に直接オプションを
記述していました。
# android-ndk-r9b/r9c
# jni/Android.mk
ifeq ($(TARGET_ARCH_ABI),armeabi-v7a)
LOCAL_CFLAGS+= -mhard-float -D_NDK_MATH_NO_SOFTFP=1
LOCAL_LDLAGS+= -lm_hard -Wl,--no-warn-mismatch
endif
# jni/Android.mk
ifeq ($(TARGET_ARCH_ABI),armeabi-v7a)
LOCAL_CFLAGS+= -mhard-float -D_NDK_MATH_NO_SOFTFP=1
LOCAL_LDLAGS+= -lm_hard -Wl,--no-warn-mismatch
endif
r9d からは上記のような Android.mk の修正が不要になります。
APP_ABI に armeabi-v7a-hard を指定するだけで hard-float に対応します。
例えば ndk-build コマンドの引数で与えるなら下記の通り。
ndk-build APP_ABI=armeabi-v7a-hard
jni/Application.mk に書いておくなら下記の通り。
# jni/Application.mk
APP_ABI:= armeabi-v7a-hard armeabi x86 mips
APP_ABI:= armeabi-v7a-hard armeabi x86 mips
APP_ABI:=all ではデフォルトの armeabi-v7a とみなされるので、
hard-float を使う場合は上記のように全部列挙しておく必要があります。
Android OS 側は softfp のままなので、hard-float が有効なのはあくまで
アプリケーション内の関数呼び出しに限定されます。
例えば OpenGL ES のような浮動小数点パラメータを持つ System API の
呼び出しは、これまでどおり softfp が用いられます。
hard-float といいつつ厳密には softfp も混在していることになります。
そのため OS から見れば従来のアプリケーションと全く同じなので、
バイナリを区別する必要がありません。
armeabi-v7a-hard でビルドしたバイナリも libs/armaebi-v7a に入ります。
他の APP_ABI と違い、armeabi-v7a と armeabi-v7a-hard の 2種類生成する
わけではないのでご注意ください。
どちら片方を選択します。
互換性が問題になるケースはおそらく外部ライブラリ利用時でしょう。
リンクするライブラリも下記いずれかの方法で hard-float に対応している
必要があります。
ライブラリの hard-float 対応手段は二通りあります。
(1) ライブラリも hard-float でビルドする
(2) ヘッダファイルに softfp 指定 __NDK_FPABI__ を追加する
(1) の場合は spftfp 版と hard-float 版ライブラリの両方を用意しておく
必要があります。
(2) はヘッダの関数宣言時に softfp 呼び出しを意味する __NDK_FPABI__ を
記述しておきます。(中身は __attribute__((pcs("aapcs"))) )
この場合ライブラリは softfp 一種類だけです。
float fpfunc( float a, float b ) __NDK_FPABI__;
NDK の hard-float について詳しくは下記も参照してください。
・Android NDK r9b と ARMv7A の hard-float
Android NDK で使用可能な ABI 指定のまとめ。
armeabi ARMv5TE FPU 無し soft-float armeabi-v7a ARMv7A VFPv3-D16 softfp armeabi-v7a-hard ARMv7A VFPv3-D16 hard-float (softfp混在) x86 x86 SSSE3 hard-float mips MIPS32R1 FPU hard-float
ARM 以外は最初から FPU 必須で hard-float 相当です。
ちなみに ios は下記の通り。
armv6 ARMv6 VFPv2-D16 softfp armv7 ARMv7A VFPv3-D32 NEON softfp armv7s ARMv7A VFPv4-D32 NEON spftfp arm64 ARMv8 AArch64 NEON hard-float
soft-float, softfp, hard-float の違い。
FPU 浮動小数点演算 関数パラメータ --------------------------------------------------------------------- soft-float FPU無し soft/ライブラリ呼び出し 整数レジスタ経由 softfp FPUあり hard/FPU が行う 整数レジスタ経由 hard-float FPUあり hard/FPU が行う 浮動小数点レジスタ直接
関連エントリ
・Android NDK r9b と ARMv7A の hard-float
・iPhone 5s A7 64bit CPU と AArch64 (arm64)
・Nexus 7 の Ubuntu で ARM の abi softfp と hard-float を比べる
2014/03/11
C++11 alignas/alignof メモリアライメント
SSE 等の SIMD 命令やキャッシュを意識したプログラムではメモリのアライメントを
考慮する必要があります。
これまではコンパイラ毎の拡張命令に頼っていましたが、
C++11 では言語仕様に含まれるようになりました。
メモリアクセスの同期命令も含めて、低レベルなメモリ命令を積極的に取り込んでいる印象です。
alignas はメモリ配置時のアライメントを宣言します。
↑変数 array は 32byte 単位のアドレス境界に配置されます。
↑ position も 32byte 単位のアドレスに配置します。
VC と gcc/clang の attribute は若干書式に違いがあります。
alignas を VC のように struct の前に記述すると変数への修飾となり、
型宣言に含まれません。VC では含まれているようです↓。
static (global) に宣言した変数は静的にアドレスが求まるので、
コンパイル時 (link時) に解決します。
ただしプログラム (data segment) がロードされるアドレスが、
より大きなアドレス境界に配置していることが条件となります。
(1) 基準となるメモリアドレスのアライメント
(2) メモリを pack したときの整合性
つまりコンパイラ側が行うのは (2) と (1) の一部になります。
stack に宣言した変数は、実行するまでアドレスがわからないので
実行時に端数を切り捨てるコードが埋め込まれます。
heap の場合は確保したメモリが alignment に従っていない可能性があるため
利用時にアドレス整合を意識しなければなりません。
alignof を用いるとコンパイル時に型情報を判断できるので、alignment に
対応したアロケータを作ることができます。
この flNew() で作成した AVECT ↓は、宣言時に alignas(32) が指定されているため
32byte 単位で確保されます。(malloc_aligned が実装されていると仮定する)
関連エントリ
・VisualStudio と C++11 、コンパイラの違いなど
・C++11 Lambda function ラムダ関数
・C++11 Variadic Templates 可変長引数のテンプレート
・スレッド同期命令の比較 C++11 とコンパイラ
・C++11 Rvalue Reference 右辺値参照
考慮する必要があります。
これまではコンパイラ毎の拡張命令に頼っていましたが、
C++11 では言語仕様に含まれるようになりました。
メモリアクセスの同期命令も含めて、低レベルなメモリ命令を積極的に取り込んでいる印象です。
CC alignas alignof ----------------------------------------------------------------- VisualC++ __declspec(align(byte)) __alignof(type) gcc/clang __attribute__((aligned(byte)) __alignof__(type) C++11 alignas(byte) alignof(type)
alignas はメモリ配置時のアライメントを宣言します。
// 変数宣言 __declspec(align(32)) int array[8]; // VC int array[8] __attribute((aligned(32)); // gcc/clang alignas(32) int array[8]; // C++11
↑変数 array は 32byte 単位のアドレス境界に配置されます。
// 型宣言 // VC __declspec(align(32)) struct AVECT { float pos[4]; }; struct __declspec(align(32)) AVECT { float pos[4]; }; // gcc/clang struct AVECT { float pos[4]; } __attribute((aligned(32)); struct __attribute((aligned(32)) AVECT { float pos[4]; }; // C++11 struct alignas(32) AVECT { float pos[4]; }; // 使用時 AVECT position;
↑ position も 32byte 単位のアドレスに配置します。
VC と gcc/clang の attribute は若干書式に違いがあります。
alignas を VC のように struct の前に記述すると変数への修飾となり、
型宣言に含まれません。VC では含まれているようです↓。
alignas(32) struct AVECT2 { float pos[4]; } va; // VC Nov 2013 CTP : alignof(AVECT2) == 32 // gcc4.8/clang3.4 : alignof(AVECT2) == 4
static (global) に宣言した変数は静的にアドレスが求まるので、
コンパイル時 (link時) に解決します。
ただしプログラム (data segment) がロードされるアドレスが、
より大きなアドレス境界に配置していることが条件となります。
(1) 基準となるメモリアドレスのアライメント
(2) メモリを pack したときの整合性
つまりコンパイラ側が行うのは (2) と (1) の一部になります。
基準アドレス(1)の生成 ------------------------------------------------ data OS が行う stack 実行時に行う (コンパイラが自動的に生成) heap ライブラリまたはユーザー任せ
stack に宣言した変数は、実行するまでアドレスがわからないので
実行時に端数を切り捨てるコードが埋め込まれます。
; clang x64 alignas(32) pushq %rbps andq $-32, %rsp ; alignas(32) subq $32, %rsp ; int array[8]
; clang armv7l alignas(32) add r11, sp, #8 sub sp, sp, #80 bic sp, sp, #31 ; alignas(32)
heap の場合は確保したメモリが alignment に従っていない可能性があるため
利用時にアドレス整合を意識しなければなりません。
alignof を用いるとコンパイル時に型情報を判断できるので、alignment に
対応したアロケータを作ることができます。
template<typename T, typename... A0> inline T* flNew( A0&&... a0 ) { return new( malloc_aligned( sizeof(T), alignof(T) ) ) T( std::forward<A0>(a0)... ); } template<typename T> inline void flDelete( T* ptr ) { ptr->~T(); free_aligned( ptr ); }
この flNew() で作成した AVECT ↓は、宣言時に alignas(32) が指定されているため
32byte 単位で確保されます。(malloc_aligned が実装されていると仮定する)
AVECT* ap= flNew<AVECT>(); // alignas(32)
関連エントリ
・VisualStudio と C++11 、コンパイラの違いなど
・C++11 Lambda function ラムダ関数
・C++11 Variadic Templates 可変長引数のテンプレート
・スレッド同期命令の比較 C++11 とコンパイラ
・C++11 Rvalue Reference 右辺値参照
Desktop GPU で Vertex Shader と Pixel Shader の仕様が完全に
統一されたのは Direct3D 10 の ShaderModel 4.0 からです。
それまでは使える命令や Constant 領域、レジスタ数、プログラムサイズ、
テクスチャ命令など様々な違いが残っていました。
Hardware の Unified Shader 化は ATI の Xbox360 GPU が先行しています。
これが今の Qualcomm Adreno に繋がっていきます。
その後一般の Desktop PC 向けにも NVIDIA GeForce 8800 (G80) が登場し、
以後 Unified Shader model が当たり前となっています。
OpenGL ES 2.0 の Shader 世代は Direct3D 9 の Shader Model 3.0 に相当
するのですが、Mobile GPU の多くはすでに Unified Shader です。
そのためあまり Vertex と Fragment Shader の機能差を意識する必要が
ありませんでした。
そんな中、Tegra2/3/4 は G70 ベースの GPU であり、Unified Shader 化する前の
制限が残っている珍しいケースとなっています。
コメントで Tegra の Fragment Shader は動的ループが使えないとの
指摘を頂いたので試してみました。
↑ループ回数を動的に求めた場合、Vertex Shader は通りますが
Fragment Shader ではコンパイル時にエラーが発生します。
以前書いたように Uniform 配列の index アクセス ↓(2) も
Tegra の Fragment Shader ではエラーとなります。
どちらも Direct3D 9 世代の制限と考えられます。
Tegra 4 でも同様なので、大幅に機能拡張されていても GPU のベースは
同じであることがわかります。
なお Tegra と同じように Discrete Type の Shader Unit を備える
Mai-400MP では動きました。
ただし Tegra でも動的分岐はできるので、実行時にループ回数が変動する
場合でも同等の処理を実現することは可能です。
以下まとめ (いずれも Fragment Shader の場合)
コンパイル時にループ回数が定まらない場合はエラー。
ループの範囲が決まっていればコンパイル可能。
おそらく下記のように展開されているものと考えられます。
Tegra K1 以降は ShaderModel 5.0 世代の GPU core になるので
このようなハードウエア的な制限はなくなるでしょう。
GPU 互換性など気軽に相談してください。
関連エントリ
・OpenGL ES 2.0 Adreno 330, Tegra 4 の GPU 速度
・OpenGL ES 2.0 で迷路シェーダー
・GLSL互換性
統一されたのは Direct3D 10 の ShaderModel 4.0 からです。
それまでは使える命令や Constant 領域、レジスタ数、プログラムサイズ、
テクスチャ命令など様々な違いが残っていました。
Hardware の Unified Shader 化は ATI の Xbox360 GPU が先行しています。
これが今の Qualcomm Adreno に繋がっていきます。
その後一般の Desktop PC 向けにも NVIDIA GeForce 8800 (G80) が登場し、
以後 Unified Shader model が当たり前となっています。
OpenGL ES 2.0 の Shader 世代は Direct3D 9 の Shader Model 3.0 に相当
するのですが、Mobile GPU の多くはすでに Unified Shader です。
そのためあまり Vertex と Fragment Shader の機能差を意識する必要が
ありませんでした。
そんな中、Tegra2/3/4 は G70 ベースの GPU であり、Unified Shader 化する前の
制限が残っている珍しいケースとなっています。
コメントで Tegra の Fragment Shader は動的ループが使えないとの
指摘を頂いたので試してみました。
// (1) 動的ループ int loop= int( uniform_value ); for( int i= 0 ; i < loop ; i++ ){ ... }
↑ループ回数を動的に求めた場合、Vertex Shader は通りますが
Fragment Shader ではコンパイル時にエラーが発生します。
以前書いたように Uniform 配列の index アクセス ↓(2) も
Tegra の Fragment Shader ではエラーとなります。
どちらも Direct3D 9 世代の制限と考えられます。
// (2) Uniform 配列 uniform vec4 src_color[30]; ~ int index= int(tex_color.x); vec4 color= src_color[ index ]; // Tegra の Fragment Shader では Error
Tegra 4 でも同様なので、大幅に機能拡張されていても GPU のベースは
同じであることがわかります。
なお Tegra と同じように Discrete Type の Shader Unit を備える
Mai-400MP では動きました。
Vertex Shader Fragment Shader 動的Loop Uniform配列 動的Loop Uniform配列 ----------------------------------------------------------- Unified ◯ ◯ ◯ ◯ Mali-400MP ◯ ◯ ◯ ◯ Tegra2/3/4 ◯ ◯ ERROR ERROR
ただし Tegra でも動的分岐はできるので、実行時にループ回数が変動する
場合でも同等の処理を実現することは可能です。
// (3) Tegra向け 動的分岐によるループ for( int i= 0 ; i< 100 ; i++ ){ if( i >= Loop ){ break; } ~ 動的ループ相当 }
以下まとめ (いずれも Fragment Shader の場合)
コンパイル時にループ回数が定まらない場合はエラー。
// (4) ループ回数不定 -- Tegra で ERROR for( int i= start ; i< end ; i++ ){ ~ }
ループの範囲が決まっていればコンパイル可能。
// (5) 上限が決まってる場合 -- Tegra でも OK for( int i= 0 ; i< 100 ; i++ ){ if( i >= start && i < end ){ ~ 動的ループ相当 } }
おそらく下記のように展開されているものと考えられます。
i= 0; if( i >= start && i < end ){ ~ } i= 1; if( i >= start && i < end ){ ~ } ~ i= 99; if( i >= start && i < end ){ ~ }
Tegra K1 以降は ShaderModel 5.0 世代の GPU core になるので
このようなハードウエア的な制限はなくなるでしょう。
GPU 互換性など気軽に相談してください。
関連エントリ
・OpenGL ES 2.0 Adreno 330, Tegra 4 の GPU 速度
・OpenGL ES 2.0 で迷路シェーダー
・GLSL互換性