2014/06/23
CPU 負荷が低い 新しい 3D API
昨年の AMD Mantle を皮切りに DirectX 12 が発表され、
つい先日 Apple から Metal も登場しました。
DirectX 11 以降停滞かつ安定していた状態から一変、
新しい GPU 向け API への流れが加速しつつあります。
どれも DirectX11, OpenGL とは互換性がない全く新しい API セットです。
これまでと趣が大きく異なっているのは CPU のための刷新だということ。
新しい描画機能への対応はなく GPU へのハードウエア追加も特に求められていません。
目的は CPU 負荷の軽減です。
それだけ CPU の負荷の高さが問題になっていたことになります。
これまでは共通 API の互換性の代償として厚いドライバのレイヤーが存在し、
さらに性能向上により CPU が転送するコマンドの規模も大きくなりました。
Multi Core CPU が当たり前となっているにもかかわらず、
旧態依然とした OpenGL はスレッドによる最適化を阻んでいます。
ドライバのオーバーヘッドは、ゲーム専用機 (Game Console) と汎用機
PC/Smartphone の大きな違いの一つとなっていました。
●シンプルに
固定機能は段階的に減っていき、3D 描画に必要なアルゴリズムのほとんどが
アプリケーション側のソフトウエアで実装されるようになりました。
汎用化が進んで GPU の用途が広がると、既存の高度な機能やドライバの手厚い保護が
かえってプログラミングの自由を妨げることがあります。
GPU はもともと進化が早く、機能も性能も使われ方も短期間で変化してきました。
Desktop から Mobile に移っても同様です。
高度なレベルで統合された API は大きな変化についていくのが困難になります。
何でもやってくれる命令は便利ですが、ある程度使われ方が決まっていないと
仕様を決められないからです。
変更が頻繁に行われるほど設計はよりシンプルになり、依存を減らす方向に進みます。
Direct3D 10 では Shader の統合やリソースの Buffer 化といった改革が行われました。
実際に使ってみるとリソース管理は思ったより簡単ではない事に気が付きます。
Resource View には細かなフラグ設定が必要で、
慣れるまでは組み合わせの制限に悩まされました。
本当に自由に感じたのは Direct3D 11 の ComputeShader からです。
用途もデータの仕様もすべてプログラマが決められます。
Texture Atlas は複数のテクスチャを巨大なテクスチャに統合します。
中の配置をプログラマが自分で管理しなければなりませんが、
その代わりシェーダーは uv だけで好きなテクスチャを読み出すことが可能です。
もし仮に GPU のメモリ全部を巨大な 1 枚のテクスチャとみなすことができたら
uv はポインタとほとんど同じものになるでしょう。
現状リソース管理はプログラマに開放されていませんが、
Texture Atlas は制限を超えるための手法の一つとみなすことが出来ます。
OpenGL の Shader 命令は DirectX よりもかなり後にデザインされたものなので、
Uniform の配置やシェーダー同士のバインドも自動化されています。
OpenGL 3.x 以降はメモリ配置をプログラマが決められるようになり、
4.x 以降はシンボルのバインドも単純な番号指定に置き換わりつつあります。
Direct3D の低レベル命令に近づいています。
GPU は汎用性を増していますが、互換性を維持した積み重ねは
必要以上に複雑になってしまうことがあります。
新しい API では、CPU 負荷の低減と同時に
よりシンプルで使いやすい API への回帰も期待できます。
● Command Buffer
描画時に CPU が行っているのは Command Buffer の構築です。
いわば GPU (Command Processor) が実行するプログラムそのもので、
アプリケーションは毎フレーム動的にプログラムを生成していることになります。
ドライバはできるだけ GPU の性能を引き出すように作られているので、
無駄な Command を省くなどの最適化が行われます。
ハードウエア毎に構造が異なるので、GPU Native な形式への変換も必要になります。
GPU Command の生成と発行はそれなりにコストがかかります。
ステートの値が必要になるのは Draw のタイミングなので、
Command の生成は描画命令まで遅延します。
Draw 命令に負荷が集中して見えるのはそのためです。
各種 Buffer 化は Command 負荷軽減手段の一つとなります。
パイプラインステートだと描画のたびに Command Buffer に書き込まれますが、
Buffer なら予め転送しておいたリソースをアサインするだけで済むからです。
●ゲーム専用機 (Console)
ゲーム専用機では PC と事情が大きく異なります。
・互換性の枷がない
・ハードウエア互換性が不要 (ハードは単一)
・ソフトウエア互換性を持たない (SDK の上位互換性を持たない)
・必要に応じてより低レベルな最適化手段が用意されている
・ハード内部の情報がある程度公開されている
ゲーム専用機は数年サイクルでリフレッシュされ、互換性の確保には
専用ハードウエアまたはソフトウエアエミュレーションが用いられます。
そのためソフトウエア (SDK) 互換性があまり重要ではありません。
最初から GPU Native な形式を使用できることもあり、
CPU のオーバーヘッドも PC と比べるとかなり低くなっています。
必要に応じてより低レベルな最適化が可能なことも専用機の特徴です。
最近は少ないと思いますが、例えば GPU Command を直接操作できるなら
事前にモデルデータを GPU Native な Command 形式に変換しておくことが出来ます。
メモリに Buffer Data と Command Buffer をロードするだけで描画が可能となります。
動的な Command 生成と比べて CPU 負荷はほとんど生じません。
(ただしいくつかのトレードオフが発生するので必ずしも最善とはいえない)
ハードの内部構造がある程度公開されている点もプログラマの負担を減らしてくれます。
描画アルゴリズムの設計時に内部の仕組みがわかっていれば、
どの方法が効率が良いのかある程度判断できるからです。
あまり迷わなく済みます。
●互換性とこれから
・ゲーム専用機との差が小さくなる
・API の分裂
新しい API は今の CPU/GPU 性能と使われ方に合わせた再設計が行われます。
全体的な動作効率があがり、CPU オーバーヘッドの低減などパフォーマンス特性は
よりゲーム専用機に近づいていくものと考えられます。
その反面、現状ではプラットフォームごとに仕様が分断されており、
互換性においては新たな課題が残ります。
OpenGL ES 2.0 はモバイルからブラウザまでプラットフォームの枠を超えて
用いられており、統一された API として大きな意味を持っていました。
今後同じように OpenGL ES 3.0/3.1 が広く用いられるようになるのかといえば
必ずしもそうではないようです。
特に iOS の場合は Metal 対応デバイスと一致しているため、
性能や機能のために OpenGL ES 3.0 を選ぶメリットが無くなりました。
Android の ES 3.0 や Desktop の OpenGL 4.x と互換性を保つためには必要ですが、
性能や使いやすさを優先するなら Metal が選ばれる可能性が高まります。
用途に応じた使い分けが行われるでしょう。
とはいえ各種プラットフォームへの個別対応は大変です。
OpenGL なら Low Overhead Profile や Multi thread Extension のような、
プラットフォームを超えた新しい仕様が登場することを期待します。
関連ページ
・3D Low overhead API
つい先日 Apple から Metal も登場しました。
DirectX 11 以降停滞かつ安定していた状態から一変、
新しい GPU 向け API への流れが加速しつつあります。
どれも DirectX11, OpenGL とは互換性がない全く新しい API セットです。
これまでと趣が大きく異なっているのは CPU のための刷新だということ。
新しい描画機能への対応はなく GPU へのハードウエア追加も特に求められていません。
目的は CPU 負荷の軽減です。
API Platform Beta SDK GPUs ------------------------------------------------------------- Mantle Windows 2014/5 RADEON GCN Direct3D 12 Windows ? GCN,Fermi,Kepler,Maxwell Metal iOS 8 2014/6 PowerVR G6430
それだけ CPU の負荷の高さが問題になっていたことになります。
これまでは共通 API の互換性の代償として厚いドライバのレイヤーが存在し、
さらに性能向上により CPU が転送するコマンドの規模も大きくなりました。
Multi Core CPU が当たり前となっているにもかかわらず、
旧態依然とした OpenGL はスレッドによる最適化を阻んでいます。
ドライバのオーバーヘッドは、ゲーム専用機 (Game Console) と汎用機
PC/Smartphone の大きな違いの一つとなっていました。
●シンプルに
固定機能は段階的に減っていき、3D 描画に必要なアルゴリズムのほとんどが
アプリケーション側のソフトウエアで実装されるようになりました。
汎用化が進んで GPU の用途が広がると、既存の高度な機能やドライバの手厚い保護が
かえってプログラミングの自由を妨げることがあります。
GPU はもともと進化が早く、機能も性能も使われ方も短期間で変化してきました。
Desktop から Mobile に移っても同様です。
高度なレベルで統合された API は大きな変化についていくのが困難になります。
何でもやってくれる命令は便利ですが、ある程度使われ方が決まっていないと
仕様を決められないからです。
変更が頻繁に行われるほど設計はよりシンプルになり、依存を減らす方向に進みます。
Direct3D 10 では Shader の統合やリソースの Buffer 化といった改革が行われました。
実際に使ってみるとリソース管理は思ったより簡単ではない事に気が付きます。
Resource View には細かなフラグ設定が必要で、
慣れるまでは組み合わせの制限に悩まされました。
本当に自由に感じたのは Direct3D 11 の ComputeShader からです。
用途もデータの仕様もすべてプログラマが決められます。
Texture Atlas は複数のテクスチャを巨大なテクスチャに統合します。
中の配置をプログラマが自分で管理しなければなりませんが、
その代わりシェーダーは uv だけで好きなテクスチャを読み出すことが可能です。
もし仮に GPU のメモリ全部を巨大な 1 枚のテクスチャとみなすことができたら
uv はポインタとほとんど同じものになるでしょう。
現状リソース管理はプログラマに開放されていませんが、
Texture Atlas は制限を超えるための手法の一つとみなすことが出来ます。
OpenGL の Shader 命令は DirectX よりもかなり後にデザインされたものなので、
Uniform の配置やシェーダー同士のバインドも自動化されています。
OpenGL 3.x 以降はメモリ配置をプログラマが決められるようになり、
4.x 以降はシンボルのバインドも単純な番号指定に置き換わりつつあります。
Direct3D の低レベル命令に近づいています。
GPU は汎用性を増していますが、互換性を維持した積み重ねは
必要以上に複雑になってしまうことがあります。
新しい API では、CPU 負荷の低減と同時に
よりシンプルで使いやすい API への回帰も期待できます。
● Command Buffer
描画時に CPU が行っているのは Command Buffer の構築です。
いわば GPU (Command Processor) が実行するプログラムそのもので、
アプリケーションは毎フレーム動的にプログラムを生成していることになります。
ドライバはできるだけ GPU の性能を引き出すように作られているので、
無駄な Command を省くなどの最適化が行われます。
ハードウエア毎に構造が異なるので、GPU Native な形式への変換も必要になります。
GPU Command の生成と発行はそれなりにコストがかかります。
ステートの値が必要になるのは Draw のタイミングなので、
Command の生成は描画命令まで遅延します。
Draw 命令に負荷が集中して見えるのはそのためです。
各種 Buffer 化は Command 負荷軽減手段の一つとなります。
パイプラインステートだと描画のたびに Command Buffer に書き込まれますが、
Buffer なら予め転送しておいたリソースをアサインするだけで済むからです。
●ゲーム専用機 (Console)
ゲーム専用機では PC と事情が大きく異なります。
・互換性の枷がない
・ハードウエア互換性が不要 (ハードは単一)
・ソフトウエア互換性を持たない (SDK の上位互換性を持たない)
・必要に応じてより低レベルな最適化手段が用意されている
・ハード内部の情報がある程度公開されている
ゲーム専用機は数年サイクルでリフレッシュされ、互換性の確保には
専用ハードウエアまたはソフトウエアエミュレーションが用いられます。
そのためソフトウエア (SDK) 互換性があまり重要ではありません。
最初から GPU Native な形式を使用できることもあり、
CPU のオーバーヘッドも PC と比べるとかなり低くなっています。
必要に応じてより低レベルな最適化が可能なことも専用機の特徴です。
最近は少ないと思いますが、例えば GPU Command を直接操作できるなら
事前にモデルデータを GPU Native な Command 形式に変換しておくことが出来ます。
メモリに Buffer Data と Command Buffer をロードするだけで描画が可能となります。
動的な Command 生成と比べて CPU 負荷はほとんど生じません。
(ただしいくつかのトレードオフが発生するので必ずしも最善とはいえない)
ハードの内部構造がある程度公開されている点もプログラマの負担を減らしてくれます。
描画アルゴリズムの設計時に内部の仕組みがわかっていれば、
どの方法が効率が良いのかある程度判断できるからです。
あまり迷わなく済みます。
●互換性とこれから
・ゲーム専用機との差が小さくなる
・API の分裂
新しい API は今の CPU/GPU 性能と使われ方に合わせた再設計が行われます。
全体的な動作効率があがり、CPU オーバーヘッドの低減などパフォーマンス特性は
よりゲーム専用機に近づいていくものと考えられます。
その反面、現状ではプラットフォームごとに仕様が分断されており、
互換性においては新たな課題が残ります。
OpenGL ES 2.0 はモバイルからブラウザまでプラットフォームの枠を超えて
用いられており、統一された API として大きな意味を持っていました。
今後同じように OpenGL ES 3.0/3.1 が広く用いられるようになるのかといえば
必ずしもそうではないようです。
特に iOS の場合は Metal 対応デバイスと一致しているため、
性能や機能のために OpenGL ES 3.0 を選ぶメリットが無くなりました。
ES2.0 ES3.0 Metal ---------------------------------------------------------- Apple A5/A6 PowerVR SGX543/554 Y - - Apple A7 PowreVR G6430 Y Y Y
Android の ES 3.0 や Desktop の OpenGL 4.x と互換性を保つためには必要ですが、
性能や使いやすさを優先するなら Metal が選ばれる可能性が高まります。
用途に応じた使い分けが行われるでしょう。
とはいえ各種プラットフォームへの個別対応は大変です。
OpenGL なら Low Overhead Profile や Multi thread Extension のような、
プラットフォームを超えた新しい仕様が登場することを期待します。
関連ページ
・3D Low overhead API
Bay Trail-T 搭載の Android 端末が発売されたので軽く調べてみました。
Android 端末に使われている GPU の種類に Intel HD Graphics が
新たに加わったことになります。
・ASUS MeMO Pad 7 ME176
Adreno 320/330, Mali-T604, PowerVR G6430 (iOS) に続いて
入手可能な OpenGL ES 3.0 対応端末となっています。
対応している Extension は下記の通り。
対応している圧縮テクスチャフォーマットは ETC2/EAC, ETC1, DXT(S3TC) 。
DirectX11 世代の GPU なので機能面での心配はおそらく不要でしょう。
Android 向けには、BayTrail の他にも新 Atom (Silvermont core) SoC として
Z3400/Z3500 (Moorefield) が発表されています。
搭載 GPU は PowerVR G6400 となっており Intel HD Graphics ではありません。
・Impress: Intel、22nmのスマートフォン向けAtom Z3400/3500を発表
実際に 2014/5/8 に au から Z3580 を搭載した MeMO Pad 8 が発表されています。
発売自体は 8月とまだ先です。
・Impress: LTE対応の8インチタブレット「ASUS MeMO Pad 8」
Full HD モデルに使われているのは PowerVR G6430 の方です。
多くの Windows Tablet 同様に ME176/ME181 の画面サイズは 1280x800 なので、
純粋な GPU 性能では Intel HD Graphics (4EU) よりも PowerVR G6430 の方が
高いのではないかと思われます。
・Wikipedia Atom (system on chip)
・Intel Atom Processor Z3745 (2M Cache, up to 1.86GHz)
Android でも x86 端末は珍しくなくなりました。
CPU 自体は x64 にも対応しています。
おそらく今後 Android も 64bit に対応すると思われますが、
既存の端末に対して 64bit 版が提供されるかどうかは不明です。
ここしばらくは、購入するタイミングが判断しづらく悩ましい状態となりそうです。
関連エントリ
・BayTrail vs Kabini (Celeron J1900 vs Athlon 5350)
・コンパイル時間の比較 BayTrail
・Atom Bay Trail の浮動小数点演算能力
Android 端末に使われている GPU の種類に Intel HD Graphics が
新たに加わったことになります。
Qualcomm Adreno Imagination PowerVR NVIDIA Tegra ARM Mali Vivante GC Intel HD Graphics ← NEW
・ASUS MeMO Pad 7 ME176
Adreno 320/330, Mali-T604, PowerVR G6430 (iOS) に続いて
入手可能な OpenGL ES 3.0 対応端末となっています。
対応している Extension は下記の通り。
// ASUS MeMO Pad 7 ME176 Android 4.4 // Atom Z3745 x86 RAM 1GB GL_VENDOR: Intel GL_RENDERER: Intel(R) HD Graphics for BayTrail GL_VERSION: OpenGL ES 3.0 - Build eng.yunweiz.20140425.225700 GL_EXT_blend_minmax GL_EXT_multi_draw_arrys GL_SUN_multi_draw_arrys GL_EXT_texture_filter_anisotropic GL_EXT_texture_compression_s3tc GL_EXT_texture_lod_bias GL_EXT_color_buffer_float GL_EXT_packed_float GL_EXT_texture_rg GL_INTEL_performance_queries GL_EXT_texture_storage GL_OES_EGL_image GL_OES_framebuffer_object GL_OES_depth24 GL_OES_stencil8 GL_OES_packed_depth_stencil GL_OES_rgb8_rgba8 GL_ARM_rgba8 GL_OES_depth_texture GL_EXT_color_buffer_half_float GL_OES_vertex_half_float GL_EXT_shadow_samplers GL_OES_point_sprite GL_OES_blend_subtract GL_OES_blend_func_separate GL_OES_blend_qeuation_separate GL_OES_standard_derivatives GL_OES_read_format GL_OES_mapbuffer GL_EXT_discard_framebuffer GL_EXT_texture_format_BGRA8888 GL_OES_compressed_paletted_texture GL_OES_ELG_image_external GL_OES_compressed_ETC1_RGB8_texture GL_OES_fixed_point GL_OES_vertex_array_object GL_OES_get_program_binary GL_OES_texture_3D GL_OES_texture_cube_map GL_OES_fbo_render_mipmap GL_OES_texture_float GL_OES_texture_float_linear GL_OES_texture_half_float GL_OES_texture_half_float_linear GL_OES_stencil_wrap GL_OES_element_index_uint GL_OES_texture_npot GL_OES_texture_mirrored_repeat GL_EXT_sRGB GL_EXT_frag_depth GL_APPLE_texture_max_level GL_EXT_occlusion_query_boolean GL_INTEL_timer_query GL_ANGLE_texture_compression_dxt1 GL_ANGLE_texture_compression_dxt3 GL_ANGLE_texture_compression_dxt5 GL_EXT_texture_compression_dxt1 GL_OES_required_internalformat GL_EXT_separate_shader_objects GL_OES_surfaceless_context GL_OES_EGL_sync GL_EXT_robustness GL_EXT_shader_texture_lod GL_EXT_unpack_subimage GL_EXT_read_format_bgra GL_EXT_debug_marker GL_KHR_blend_equation_advanced GL_EXT_shader_integer_mix
対応している圧縮テクスチャフォーマットは ETC2/EAC, ETC1, DXT(S3TC) 。
DirectX11 世代の GPU なので機能面での心配はおそらく不要でしょう。
Android 向けには、BayTrail の他にも新 Atom (Silvermont core) SoC として
Z3400/Z3500 (Moorefield) が発表されています。
搭載 GPU は PowerVR G6400 となっており Intel HD Graphics ではありません。
・Impress: Intel、22nmのスマートフォン向けAtom Z3400/3500を発表
実際に 2014/5/8 に au から Z3580 を搭載した MeMO Pad 8 が発表されています。
発売自体は 8月とまだ先です。
・Impress: LTE対応の8インチタブレット「ASUS MeMO Pad 8」
Tablet SoC core clock display GPU --------------------------------------------------------------------- MeMO Pad 7 ME176 Z3745 4 1.86GHz 1280x800 Intel HD Graphics 4EU MeMO Pad 8 ME181 Z3745 4 1.86GHz 1280x800 Intel HD Graphics 4EU MeMO Pad 8 (au) Z3580 4 2.33GHz 1920x1200 PowerVR G6430
Full HD モデルに使われているのは PowerVR G6430 の方です。
多くの Windows Tablet 同様に ME176/ME181 の画面サイズは 1280x800 なので、
純粋な GPU 性能では Intel HD Graphics (4EU) よりも PowerVR G6430 の方が
高いのではないかと思われます。
SoC core CPU-clock GPU GPU-clock fop GFLOPS ----------------------------------------------------------------- Z3745 4 1.86GHz Intel HD Graphics 4EU 778MHz 64 49.8 Z3580 4 2.33GHz PowerVR G6430 533MHz 256 136.4
・Wikipedia Atom (system on chip)
・Intel Atom Processor Z3745 (2M Cache, up to 1.86GHz)
Android でも x86 端末は珍しくなくなりました。
CPU 自体は x64 にも対応しています。
おそらく今後 Android も 64bit に対応すると思われますが、
既存の端末に対して 64bit 版が提供されるかどうかは不明です。
ここしばらくは、購入するタイミングが判断しづらく悩ましい状態となりそうです。
関連エントリ
・BayTrail vs Kabini (Celeron J1900 vs Athlon 5350)
・コンパイル時間の比較 BayTrail
・Atom Bay Trail の浮動小数点演算能力
Chromecast 上でも Emscripten のプログラムが動きました。

少々ややこしいのですが、PC のブラウザで走らせた結果を、
Tab のミラーリング機能で TV に表示しているわけではないです。
Emscripten で作った JavaScript コードを、Custom Receiver として登録することで
Chromecast 上で走らせています。
速度は 10~15fps 前後。
Android 端末よりも遅いので、
Chromecast の CPU はあまり動作クロックが高くないようです。
● Chromecast とは
ほぼストリーム専用に特化したメディアプレイヤーです。
・Google Chromecast
端末の画面を TV にミラーリング表示するような、リモートモニタアダプタとは異なっています。
インターネットへの接続機能を持っており、基本的には Chromecast は
メディアプレイヤーとして単独で動作します。
ただし本体にリモコンやコントローラを繋ぐことはできず、UI を持っていません。
必ず PC や Android/iOS 等の他の端末経由で操作を行うことになります。
一般のメディアプレイヤー BOX / スティック との違いは下記の通り
ストレージ含めて周辺機器も不要にしたことでハードウエアはシンプルです。
UI もいらないため Android を搭載したメディアプレイヤーよりも性能を必要とせず
コストダウンを可能にしているようです。
● ハードウエア
・iFixit Chromecast Teardown
・ARM Marvell SoCs
分解記事より Marvell 88DE3005-A1、↑ARM Marvell SoCs より Cortex-A9。
↓Amazon の Fire TV の比較表でも Single core の RAM 512MB と記載されています。
・Amazon Fire TV
動作クロックや GPU は不明。
同様のコンセプトだった Nexus Q と比べてもかなり控えめなスペックとなっています。
● Chromecast のアプリケーション
Chromecast は Web Browser (Chrome) が内蔵されており、
Web Application を走らせるプラットフォームとして機能しています。
ただし自由に Web page を表示させることはできず、
登録した Web Application (Receiver App) しか開けないように制限されています。
また Chromecast 本体だけでは何も操作できないので、
PC/Android/iOS 側にも操作用のアプリケーションが必要になります。
(1) Receiver Apps : Chromecast 上で走る Web Application (HTML+JS)
(2) Sender Apps : 外部から操作するためのアプリ (PC Chrome/Android/iOS Apps)
基本的には (1) と (2) の組み合わせで出来ています。
他にも Web Application として (1) を設置するための Web サーバーが要ります。
● PC の Tab ミラーリング
PC の Chrome ブラウザに拡張機能を追加すると、ブラウザの表示内容を
Chromecast に送信できるようになります。
アイコンから「このタブをキャスト」ボタンを選択。
CPU 負荷が一定分増加することからも、動画として Web Page の内容を送信しているようです。
PC がストリームサーバーを兼ねていることになります。
任意の Web を制限なく表示できますが、Chromecast が直接ネットにアクセスして
レンダリングを行っているわけではありません。
● リモコンだけのアプリ (Default Receiver)
メディアプレイヤーなどのよく使われる Receiver (1) は、デフォルトのものが用意されています。
そのためアプリケーションによっては、(2) のリモコン側を作るだけで機能を実現することができます。
この場合登録は不要。
● Chromecast の上で動くアプリ (Custom Receiver)
Chromecast 上で走る (1) の Receiver アプリを作るには登録が必要になります。
1. Receiver / Sender アプリを作成
2. Receiver を Web サーバーに転送する
3. Developer Console で 2. の URL を登録して ID を受け取る
4. 開発に使う Chromecast のシリアルを登録して Developer mode にする
(Remote Debug できるようになる)
以下テスト実行までの手順
◎ Receiver アプリ URL の登録
1. Developer Console に登録&ログイン (5ドル必要)
・Google Cast SDK Developer Console
2. [ADD NEW APPLICATION] で Custom Receiver を選択
3. 名前と Receiver App を設置した URL の登録
例 http://~/receiver_chiraks.html
4. Application ID が発行されるので、リモコン側 (2) のアプリケーションに ID を書き込む
◎ Chromecast Device の登録
1. Developer Console の [ADD NEW DEVICE] でシリアルナンバーを登録する。
・本体の裏面上部の文字列、または箱のシールにある SN の文字列
2. Device 登録には時間がかかるので 15分待つ。
3. "Ready For Testing" になったら完了
◎ Developer mode の確認
1. Chromecast の電源を一旦落とす。
2. Chromecast を再び起動して PC 上の Chrome ブラウザから下記 URL を開く
http://ChromecastのIPアドレス:9222
3. もし開けない場合は、再び電源を落として時間をおいてからやりなおす。
"Inspectable WebContents" と表示されたら成功で、
Console の log を確認したり Remote Debug できるようになります。
◎ アプリの実行
Sender を起動して登録した Chromecast Device に接続
とりあえず Chromecast のようなデバイスでも
Emscripten でプログラムを作れることがわかりました。
関連エントリ
・Emscripten C++/OpenGL ES 2.0 (9) C関数呼び出しと FileSystem
・Emscripten C++ のアプリをブラウザで動かす (8) iOS でも動く
・Emscripten C++/OpenGL ES 2.0 (7) Emscripten の OpenGL API と WebGL
・Emscripten C++/OpenGL ES 2.0 (6) Chrome の速度と IE11/Safari
・Emscripten C++/OpenGL ES 2.0 のアプリケーションをブラウザで動かす (5)
・Emscripten C++/OpenGL ES 2.0 のアプリケーションをブラウザで動かす (4)
・Emscripten C++/OpenGL ES 2.0 のアプリケーションをブラウザで動かす (3)
・Emscripten C++/OpenGL ES 2.0 のアプリケーションをブラウザで動かす (2)
・Emscripten C++/OpenGL ES 2.0 のアプリケーションをブラウザで動かす (1)
・Emscripten C++/OpenGL ES 2.0 のアプリケーションをブラウザで動かす 一覧

少々ややこしいのですが、PC のブラウザで走らせた結果を、
Tab のミラーリング機能で TV に表示しているわけではないです。
Emscripten で作った JavaScript コードを、Custom Receiver として登録することで
Chromecast 上で走らせています。
速度は 10~15fps 前後。
Android 端末よりも遅いので、
Chromecast の CPU はあまり動作クロックが高くないようです。
● Chromecast とは
ほぼストリーム専用に特化したメディアプレイヤーです。
・Google Chromecast
端末の画面を TV にミラーリング表示するような、リモートモニタアダプタとは異なっています。
インターネットへの接続機能を持っており、基本的には Chromecast は
メディアプレイヤーとして単独で動作します。
ただし本体にリモコンやコントローラを繋ぐことはできず、UI を持っていません。
必ず PC や Android/iOS 等の他の端末経由で操作を行うことになります。
一般のメディアプレイヤー BOX / スティック との違いは下記の通り
・ストリーム向けなのでストレージを必要としない。SD カードスロットもない ・リモコンを持たない。USB/Bluetooth/IR 等の周辺機器をつなぐことができない
ストレージ含めて周辺機器も不要にしたことでハードウエアはシンプルです。
UI もいらないため Android を搭載したメディアプレイヤーよりも性能を必要とせず
コストダウンを可能にしているようです。
● ハードウエア
・iFixit Chromecast Teardown
・ARM Marvell SoCs
分解記事より Marvell 88DE3005-A1、↑ARM Marvell SoCs より Cortex-A9。
↓Amazon の Fire TV の比較表でも Single core の RAM 512MB と記載されています。
・Amazon Fire TV
動作クロックや GPU は不明。
同様のコンセプトだった Nexus Q と比べてもかなり控えめなスペックとなっています。
● Chromecast のアプリケーション
Chromecast は Web Browser (Chrome) が内蔵されており、
Web Application を走らせるプラットフォームとして機能しています。
ただし自由に Web page を表示させることはできず、
登録した Web Application (Receiver App) しか開けないように制限されています。
また Chromecast 本体だけでは何も操作できないので、
PC/Android/iOS 側にも操作用のアプリケーションが必要になります。
(1) Receiver Apps : Chromecast 上で走る Web Application (HTML+JS)
(2) Sender Apps : 外部から操作するためのアプリ (PC Chrome/Android/iOS Apps)
基本的には (1) と (2) の組み合わせで出来ています。
他にも Web Application として (1) を設置するための Web サーバーが要ります。
● PC の Tab ミラーリング
PC の Chrome ブラウザに拡張機能を追加すると、ブラウザの表示内容を
Chromecast に送信できるようになります。
アイコンから「このタブをキャスト」ボタンを選択。
CPU 負荷が一定分増加することからも、動画として Web Page の内容を送信しているようです。
PC がストリームサーバーを兼ねていることになります。
任意の Web を制限なく表示できますが、Chromecast が直接ネットにアクセスして
レンダリングを行っているわけではありません。
● リモコンだけのアプリ (Default Receiver)
メディアプレイヤーなどのよく使われる Receiver (1) は、デフォルトのものが用意されています。
そのためアプリケーションによっては、(2) のリモコン側を作るだけで機能を実現することができます。
この場合登録は不要。
● Chromecast の上で動くアプリ (Custom Receiver)
Chromecast 上で走る (1) の Receiver アプリを作るには登録が必要になります。
1. Receiver / Sender アプリを作成
2. Receiver を Web サーバーに転送する
3. Developer Console で 2. の URL を登録して ID を受け取る
4. 開発に使う Chromecast のシリアルを登録して Developer mode にする
(Remote Debug できるようになる)
以下テスト実行までの手順
◎ Receiver アプリ URL の登録
1. Developer Console に登録&ログイン (5ドル必要)
・Google Cast SDK Developer Console
2. [ADD NEW APPLICATION] で Custom Receiver を選択
3. 名前と Receiver App を設置した URL の登録
例 http://~/receiver_chiraks.html
4. Application ID が発行されるので、リモコン側 (2) のアプリケーションに ID を書き込む
◎ Chromecast Device の登録
1. Developer Console の [ADD NEW DEVICE] でシリアルナンバーを登録する。
・本体の裏面上部の文字列、または箱のシールにある SN の文字列
2. Device 登録には時間がかかるので 15分待つ。
3. "Ready For Testing" になったら完了
◎ Developer mode の確認
1. Chromecast の電源を一旦落とす。
2. Chromecast を再び起動して PC 上の Chrome ブラウザから下記 URL を開く
http://ChromecastのIPアドレス:9222
3. もし開けない場合は、再び電源を落として時間をおいてからやりなおす。
"Inspectable WebContents" と表示されたら成功で、
Console の log を確認したり Remote Debug できるようになります。
◎ アプリの実行
Sender を起動して登録した Chromecast Device に接続
とりあえず Chromecast のようなデバイスでも
Emscripten でプログラムを作れることがわかりました。
関連エントリ
・Emscripten C++/OpenGL ES 2.0 (9) C関数呼び出しと FileSystem
・Emscripten C++ のアプリをブラウザで動かす (8) iOS でも動く
・Emscripten C++/OpenGL ES 2.0 (7) Emscripten の OpenGL API と WebGL
・Emscripten C++/OpenGL ES 2.0 (6) Chrome の速度と IE11/Safari
・Emscripten C++/OpenGL ES 2.0 のアプリケーションをブラウザで動かす (5)
・Emscripten C++/OpenGL ES 2.0 のアプリケーションをブラウザで動かす (4)
・Emscripten C++/OpenGL ES 2.0 のアプリケーションをブラウザで動かす (3)
・Emscripten C++/OpenGL ES 2.0 のアプリケーションをブラウザで動かす (2)
・Emscripten C++/OpenGL ES 2.0 のアプリケーションをブラウザで動かす (1)
・Emscripten C++/OpenGL ES 2.0 のアプリケーションをブラウザで動かす 一覧
● JavaScript から C関数を呼び出す
ccall() を使います。
(C/C++言語から JavaScript Lib を呼び出す方法は こちら)
C/C++側から参照がない関数はオプティマイザが削除してしまうので
そのままだと上のプログラムはエラーになります。
コンパイル時に下記のオプションが必要です。
EXPORTED_FUNCTIONS で個別に関数名を指定するか、または
を指定します。
C言語の symbol なので内部表現では先頭に '_' が付きます。
C++ 内で定義する場合は extern "C" が必要な点に注意。
● 引数の指定と cwrap
ccall 2番目の引数 'v' は戻り値の型を意味しています。
例えば文字列を返す関数の場合 'string' を与えます。
'string' は自動的にオブジェクトに変換されます。
引数を渡す場合も型の情報が必要です。
文字列引数はポインタに変換するために、一旦内部の仮想メモリ空間に
展開する必要があります。
JavaScript から呼び出した場合、この領域として stack が用いられています。
頻繁に呼び出す場合は cwrap() で関数に変換できます。
● IDBFS ファイルシステム
ブラウザのローカルストレージに保存可能なファイルシステムです。
任意の場所に mount 可能で、path + filename がそのまま key になります。
通常は仮想 FS に対して読み書きを行うので IDB との同期が必要です。
callback を使うので mail_loop が必要です。
いろいろ試していますが通常の C関数での読み込みがうまく行っていません。
JavaScript からは可能でした。
● NODEFS
ブラウザのように制限が無いため、node command の場合は NODEFS を
使って直接ローカルファイルにアクセスできます。
mount 時の root option が実際の File System の Path 指定になります。
syncfs は不要。
● SOCKFS
read, write, open, close 等の標準 API で socket にアクセスできるようにするため、
Socket API は FileSystem の一種として実装されているようです。
実装上の内部的なものであって特に使用する必要はありません。
● 独自ファイルシステムを作る
MountType は FileSystem Object なので、
自分で FileSystem を作ることも可能です。
chafs_init() で FileSystem を作成しています。
この例では CHAFS をマウントした '/save' 以下どのファイルを読み出しても、
すべて 'A' で埋められています。
ただし getattr を定義していないのでファイルサイズを取ることはできません。
関連エントリ
・Emscripten C++ のアプリをブラウザで動かす (8) iOS でも動く
・Emscripten C++/OpenGL ES 2.0 (7) Emscripten の OpenGL API と WebGL
・Emscripten C++/OpenGL ES 2.0 (6) Chrome の速度と IE11/Safari
・Emscripten C++/OpenGL ES 2.0 のアプリケーションをブラウザで動かす (5)
・Emscripten C++/OpenGL ES 2.0 のアプリケーションをブラウザで動かす (4)
・Emscripten C++/OpenGL ES 2.0 のアプリケーションをブラウザで動かす (3)
・Emscripten C++/OpenGL ES 2.0 のアプリケーションをブラウザで動かす (2)
・Emscripten C++/OpenGL ES 2.0 のアプリケーションをブラウザで動かす (1)
・Emscripten C++/OpenGL ES 2.0 のアプリケーションをブラウザで動かす 一覧
ccall() を使います。
(C/C++言語から JavaScript Lib を呼び出す方法は こちら)
#include <emscripten.h>
#include <stdio.h>
extern "C" void myfunc01()
{
printf( "MYFUNC01\n" );
}
int main()
{
EM_ASM(
// C言語関数の呼び出し
ccall( 'myfunc01', 'v' );
);
return 0;
}
#include <stdio.h>
extern "C" void myfunc01()
{
printf( "MYFUNC01\n" );
}
int main()
{
EM_ASM(
// C言語関数の呼び出し
ccall( 'myfunc01', 'v' );
);
return 0;
}
C/C++側から参照がない関数はオプティマイザが削除してしまうので
そのままだと上のプログラムはエラーになります。
コンパイル時に下記のオプションが必要です。
-s EXPORTED_FUNCTIONS="['_main','_malloc','_myfunc01','_myfunc02']
EXPORTED_FUNCTIONS で個別に関数名を指定するか、または
-s EXPORT_ALL=1
を指定します。
C言語の symbol なので内部表現では先頭に '_' が付きます。
C++ 内で定義する場合は extern "C" が必要な点に注意。
● 引数の指定と cwrap
ccall 2番目の引数 'v' は戻り値の型を意味しています。
例えば文字列を返す関数の場合 'string' を与えます。
#include <emscripten.h>
#include <stdio.h>
extern "C" const char* myfunc01()
{
return "MYFUNC_01";
}
int main()
{
EM_ASM(
// C言語関数の呼び出し
console.log( ccall( 'myfunc01', 'string' ) );
);
return 0;
}
#include <stdio.h>
extern "C" const char* myfunc01()
{
return "MYFUNC_01";
}
int main()
{
EM_ASM(
// C言語関数の呼び出し
console.log( ccall( 'myfunc01', 'string' ) );
);
return 0;
}
'string' は自動的にオブジェクトに変換されます。
引数を渡す場合も型の情報が必要です。
#include <emscripten.h>
#include <stdio.h>
extern "C" const char* myfunc02( int a, float b, const char* c )
{
printf( "%d %f %s\n", a, b, c );
return "MYFUNC_02";
}
int main()
{
EM_ASM(
// C言語関数の呼び出し
console.log( ccall( 'myfunc02', 'string', ['number','number','string'], 12, 34.5, 'abcdefg' ) );
);
return 0;
}
#include <stdio.h>
extern "C" const char* myfunc02( int a, float b, const char* c )
{
printf( "%d %f %s\n", a, b, c );
return "MYFUNC_02";
}
int main()
{
EM_ASM(
// C言語関数の呼び出し
console.log( ccall( 'myfunc02', 'string', ['number','number','string'], 12, 34.5, 'abcdefg' ) );
);
return 0;
}
文字列引数はポインタに変換するために、一旦内部の仮想メモリ空間に
展開する必要があります。
JavaScript から呼び出した場合、この領域として stack が用いられています。
戻り値 引数 C言語 --------------------------------------------- string string const char* number number int/float 他 (int64以外) v -- void
頻繁に呼び出す場合は cwrap() で関数に変換できます。
var myfunc02= cwrap( 'myfunc02', 'string', ['number','number','string'] );
var ret_str= myfunc02( 345, 67.8, 'hijklmn' );
var ret_str= myfunc02( 345, 67.8, 'hijklmn' );
● IDBFS ファイルシステム
ブラウザのローカルストレージに保存可能なファイルシステムです。
任意の場所に mount 可能で、path + filename がそのまま key になります。
通常は仮想 FS に対して読み書きを行うので IDB との同期が必要です。
FS.syncfs( true, callback ); 読み込み IDB → Virtual FS FS.syncfs( false, callback ); 書き込み Virtual FS → IDB
void save_func()
{
FILE* fp= fopen( "/save/data.txt", "w" );
fputs( "ABCDEFG", fp );
fclose( fp );
EM_ASM(
// 書き込み (false は省略できる)
FS.syncfs( false, function(err){ } );
);
}
// EXPORTED_FUNCTIONS に '_load_func' を加える必要あり
extern "C" void load_func()
{
// 読み込みが完了したらアクセスできる
EM_ASM(
var text= FS.readFile( '/save/data.txt', {flags:'r',encoding:'utf8'} );
console.log( text );
);
}
static void ems_loop()
{
static int count= 0;
if( ++count == 60 * 3 ){
save_func();
}
}
int main()
{
EM_ASM(
FS.mkdir( '/save' );
FS.mount( IDBFS, [], '/save' );
// 読み込み
FS.syncfs( true, function(err){
ccall( 'load_func', 'v' );
});
);
emscripten_set_main_loop( ems_loop, 60, true );
return 0;
}
{
FILE* fp= fopen( "/save/data.txt", "w" );
fputs( "ABCDEFG", fp );
fclose( fp );
EM_ASM(
// 書き込み (false は省略できる)
FS.syncfs( false, function(err){ } );
);
}
// EXPORTED_FUNCTIONS に '_load_func' を加える必要あり
extern "C" void load_func()
{
// 読み込みが完了したらアクセスできる
EM_ASM(
var text= FS.readFile( '/save/data.txt', {flags:'r',encoding:'utf8'} );
console.log( text );
);
}
static void ems_loop()
{
static int count= 0;
if( ++count == 60 * 3 ){
save_func();
}
}
int main()
{
EM_ASM(
FS.mkdir( '/save' );
FS.mount( IDBFS, [], '/save' );
// 読み込み
FS.syncfs( true, function(err){
ccall( 'load_func', 'v' );
});
);
emscripten_set_main_loop( ems_loop, 60, true );
return 0;
}
callback を使うので mail_loop が必要です。
いろいろ試していますが通常の C関数での読み込みがうまく行っていません。
JavaScript からは可能でした。
● NODEFS
ブラウザのように制限が無いため、node command の場合は NODEFS を
使って直接ローカルファイルにアクセスできます。
EM_ASM(
FS.mkdir( '/save' );
FS.mount( NODEFS, {root:'.'}, '/save' );
);
FS.mkdir( '/save' );
FS.mount( NODEFS, {root:'.'}, '/save' );
);
mount 時の root option が実際の File System の Path 指定になります。
syncfs は不要。
● SOCKFS
read, write, open, close 等の標準 API で socket にアクセスできるようにするため、
Socket API は FileSystem の一種として実装されているようです。
実装上の内部的なものであって特に使用する必要はありません。
● 独自ファイルシステムを作る
MountType は FileSystem Object なので、
自分で FileSystem を作ることも可能です。
// chafs.js
mergeInto( LibraryManager.library, {
chafs_init: function(){
Module.CHAFS= {
createNode: function( parent, name ){
var node= FS.createNode( parent, name, 511 ); // 0777
node.node_ops= {
lookup: function( parent, name ){
return Module.CHAFS.createNode( parent, name );
},
};
node.stream_ops= {
open: function( stream ){
},
close: function( stream ){
},
read: function( stream, buffer, offset, length, position ){
// buffer を 'A' で埋めるだけ
for( var i= 0 ; i< length ; i++ ){
buffer[offset + i]= 65;
}
return length;
},
};
return node;
},
mount: function( mount ){
return Module.CHAFS.createNode( null, '/' );
},
};
},
});
mergeInto( LibraryManager.library, {
chafs_init: function(){
Module.CHAFS= {
createNode: function( parent, name ){
var node= FS.createNode( parent, name, 511 ); // 0777
node.node_ops= {
lookup: function( parent, name ){
return Module.CHAFS.createNode( parent, name );
},
};
node.stream_ops= {
open: function( stream ){
},
close: function( stream ){
},
read: function( stream, buffer, offset, length, position ){
// buffer を 'A' で埋めるだけ
for( var i= 0 ; i< length ; i++ ){
buffer[offset + i]= 65;
}
return length;
},
};
return node;
},
mount: function( mount ){
return Module.CHAFS.createNode( null, '/' );
},
};
},
});
chafs_init() で FileSystem を作成しています。
// main.cpp
#include <emscripten.h>
#include <stdio.h>
extern "C" void chafs_init();
static void load_func()
{
FILE* fp= fopen( "/save/data.txt", "r" );
char buf[128];
size_t size= fread( buf, 1, 10, fp );
buf[size]= '\0';
fclose( fp );
printf( "%s\n", buf );
}
int main()
{
chafs_init(); // 作成
EM_ASM(
FS.mkdir( '/save' );
FS.mount( Module.CHAFS, [], '/save' );
);
load_func();
return 0;
}
#include <emscripten.h>
#include <stdio.h>
extern "C" void chafs_init();
static void load_func()
{
FILE* fp= fopen( "/save/data.txt", "r" );
char buf[128];
size_t size= fread( buf, 1, 10, fp );
buf[size]= '\0';
fclose( fp );
printf( "%s\n", buf );
}
int main()
{
chafs_init(); // 作成
EM_ASM(
FS.mkdir( '/save' );
FS.mount( Module.CHAFS, [], '/save' );
);
load_func();
return 0;
}
この例では CHAFS をマウントした '/save' 以下どのファイルを読み出しても、
すべて 'A' で埋められています。
ただし getattr を定義していないのでファイルサイズを取ることはできません。
関連エントリ
・Emscripten C++ のアプリをブラウザで動かす (8) iOS でも動く
・Emscripten C++/OpenGL ES 2.0 (7) Emscripten の OpenGL API と WebGL
・Emscripten C++/OpenGL ES 2.0 (6) Chrome の速度と IE11/Safari
・Emscripten C++/OpenGL ES 2.0 のアプリケーションをブラウザで動かす (5)
・Emscripten C++/OpenGL ES 2.0 のアプリケーションをブラウザで動かす (4)
・Emscripten C++/OpenGL ES 2.0 のアプリケーションをブラウザで動かす (3)
・Emscripten C++/OpenGL ES 2.0 のアプリケーションをブラウザで動かす (2)
・Emscripten C++/OpenGL ES 2.0 のアプリケーションをブラウザで動かす (1)
・Emscripten C++/OpenGL ES 2.0 のアプリケーションをブラウザで動かす 一覧