Linux」カテゴリーアーカイブ

Nexus 5 に Maru OS を入れてみた

Android 7.0 (Nougat) の対象外になった Nexus 5 ですが、MaruOS が動くらしいので入れてみました。HDMI 経由でモニタを接続すると Desktop PC として使えるようになります。

Maru OS

中央の小さい画面が Nexus 5 16GB model です。後ろのモニタは 11inch (OnLap 1101H)

maruos01.jpeg

↓ 起動すると普通の Android の画面。OS は Android 5.1.1。アプリは最小限

maruos03.jpeg

SlimPort 経由で HDMI 接続すると Debian の Desktop になります。Keyboard や Mouse は予め Bluetooth で接続しておく必要があります。

maruos04.jpeg

Nexus 5 以外に必要な物は下記の通り

・SlimPort の HDMI アダプタ
・Bluetooth Keyboard
・Bluetooth Mouse

Bluetooth のペアリングは Android 側で行います。ペアリングと接続が完了しても Android 画面の操作が出来ないので注意が必要です。Desktop 側の操作だけマウスとキーボードが有効になっているようです。Bluetooth 接続後の動作確認のために HDMI を先に繋いでおくことをおすすめします。

Desktop は Linux そのままです。開発環境を整えてアプリケーションビルドなども普通にできます。HDMI ケーブルを外しても状態は残っているので、再びケーブルを繋ぐと以前の画面のまま復帰できるようです。Android 側の操作で終了させることも出来ます。

Nexus 5 本体の電源ボタンを押す (Sleep する) と Desktop 側も Sleep してしまうようです。使うときは Nexus 本体側の画面も常時点灯状態にしておく必要あり。

Mobile 端末で動く Desktop 環境としては思ったよりもレスポンスが良く十分使えそうです。RAM が多い上に CPU 世代も違うので当然ですが、以前使っていた Nexus 7 (2012) の Linux (Ubuntu) や NetWalker、古い Raspberry Pi よりも快適でした。

下記はビルド時間の比較。

                    real   user      CPU         clock  core
-------------------------------------------------------------
Nexus 5 + MaruOS    3:29   4:42      Krait 400   2.3GHz  4
Raspberry Pi 2      5:51  20:08      Cortex-A7   0.9GHz  4
Raspberry Pi 3      2:18   6:57      Cortex-A53  1.2GHz  4

real, user 共に実行時間。(分:秒) 値が小さい方が高速。

実際の時間 (real) では Raspberry Pi 3 に敵いませんが Raspberry Pi 2 よりは早く完了しています。user 時間が最も短いため core 自体の能力は高いことがわかります。Thread の恩恵が少ないのでストレージが遅いか描画に取られているのかもしれません。

OpenGL API に対応しており GL 2.1 + ES 2.0 が使えます。ソフトウエア描画なので GPU は用いられていません。かなり低速ですが一応 3D 描画できました。1~2 fps 程度。

maruos05.jpeg

関連エントリ
Nexus 7 上に開発環境をつくる (4) Ubuntu 13.04
Nexus 7 上に開発環境をつくる (3) Ubuntu
Nexus 7 上に開発環境をつくる (2) Bluetooth と OpenGL ES 2.0
Android Tablet Nexus 7 上に開発環境をつくる (Ubuntu)
NetWalker PC-Z1 i.MX515 OpenGL ES 2.0 (2)

2955U vs N3150/J1900/Athlon5350 (コンパイル時間の比較)

Celeron 2955U は低価格帯 PC に使われていた CPU (Haswell 世代) です。2955U を搭載した PC を入手したので flatlib3 のコンパイル時間を比較してみました。(Debug+Release)

CPU core clock C/T Compiler RAM コンパイル時間
Celeron 2955U Haswell 1.4GHz 2C2T Clang-3.4 4GB 93
Athlon 5350 Jaguar 2.0GHz 4C4T Clang-3.6 8GB 88
Celeron N3150 Airmont 1.6GHz 4C4T Clang-3.6 16GB 108
Celeron J1900 Silvermont 2.0GHz 4C4T Clang-3.4 8GB 79
参考用 core clock C/T Compiler RAM コンパイル時間
Core i7-4790K Haswell 4.0GHz 4C8T Clang-3.6 16GB 15
Atom Z540 Bonnell 1.8GHz 1C2T Clang-3.4 2GB 426
Raspberry Pi2 Cortex-A7 0.9GHz 4C4T Clang-3.5 1GB 402
Raspberry Pi ARM1176 0.7GHz 1C1T Clang-3.5 0.5GB 1893
Netwalker Cortex-A8 0.8GHz 1C1T GCC-4.7 0.5GB 1902

・コンパイル時間の単位は秒、値が小さい方が高速

RAM 容量やストレージの速度など動作環境が一致していないのであまり厳密ではありません。特に Raspberry Pi は SD カードの速度に依存するので参考程度にお願いします。

core 数が半分でクロックも低いながら、2955U は Atom/Jaguar 系の 4 core/2GHz とほぼ同程度のコンパイル時間となっています。さまざまな機能が無効化されていますが性能は高く、core 単体では Atom/Jaguar に対して 2倍以上速いことになります。
J1900 よりも新しい N3150 の方が遅いのは TDP も低く Clock が下がっているため。GPU 性能では 4EU しかない J1900 が一番不利になっています。

CPU GPU sp clock fop FLOPS
Celeron 2955U HD Graphics Gen7.5 10EU 200-1000MHz 160 160.0
Celeron N3150 HD Graphics Gen8 12EU 320-640MHz 192 122.9
Celeron J1900 HD Graphics Gen7 4EU 688-854MHz 64 54.7
Athlon 5350 RADEON R3 (8400) 128sp 600MHz 256 153.6

関連エントリ
Raspberry Pi 2 で速くなったコンパイル時間の比較
BayTrail vs Kabini (Celeron J1900 vs Athlon 5350)
コンパイル時間の比較 BayTrail
Android Tablet Nexus 7 上に開発環境をつくる (Ubuntu)

Raspberry Pi 2 OpenGL ES のレイヤー合成

大幅に強化された CPU と違い、GPU 周りは Raspberry Pi 1 とほとんど変わっていないようです。
以前移植したコードが Raspberry Pi 2 でもそのまま動いています。

Raspberry Pi 2 の Desktop (Raspbian) から起動できる Minecraft は、
一見 Windows 内で動いているように見えますが他のウィンドウの下に隠れることができません。
3D Rendering 自体は別のレイヤーに描画されており、
スクリーンに対して Overlay で表示されているようです。

raspberry_pi_201.jpg

↑ 上のように半透明が描画されているシーンだけ下のウィンドウが透けて見えます。
フレームバッファに alpha 値が入るとレイヤー同士の合成とみなされてしまうため。

この辺りの挙動は Window 無しのフルスクリーン描画と違いがないようです。
自分が移植したプログラムでも最初は下記のようになっていました。

raspberry_pi_202.jpg

↑背景を alpha = 0.0f でクリアしていたため console と合成されてしまっています。
うっすら見えているのは alpha 値が入っているところだけです。

OpenGL で常に Alpha=1.0 を書き込んでも良いのですが、
VideoCore の API でも指定できるようです。
下記のように alpha に固定値 255 を指定することで layer が不透明になりました。

static VC_DISPMANX_ALPHA_T alpha= {
  DISPMANX_FLAGS_ALPHA_FIXED_ALL_PIXELS,
  255,
  NULL
};

dispman_element= vc_dispmanx_element_add(
  dispman_update,           // DISPMANX_UPDATE_HANDLE_T
  dispman_display,          // DISPMANX_DISPLAY_HANDLE_T
  0,                        // layer
  &dest_rect,               // VC_RECT_T*
  0,                        // DISPMANX_RESOURCE_HANDLE_T
  &src_rect,                // VC_RECT_T*
  DISPMANX_PROTECTION_NONE, // DISPMANX_PROTECTION_T
  &alpha,                   // VC_DISPMANX_ALPHA_T
  NULL,                     // DISPMANX_CLAMP_T*
  DISPMANX_NO_ROTATE        // DISPMANX_TRANSFORM_T
);

↓不透明になったもの

raspberry_pi_203.jpg

他にも位置の指定や拡縮、90度回転や反転等の指定もできるようです。

↓ ありもののケース(旧型)

raspberry_pi_1b.jpg

関連エントリ
Raspberry Pi 2 で速くなったコンパイル時間の比較

Raspberry Pi 2 で速くなったコンパイル時間の比較

Raspberry Pi 2 を入手したので使ってみました。
ARM11 の Raspberry Pi と比べると格段に速くなっています。

VFP Benchmark の比較

               CPU       clock       single fp      double fp
----------------------------------------------------------------
Raspberry Pi B ARM1176   0.7GHz x1   0.674 GFLOPS   0.674 GFLOPS
Raspberry Pi 2 Cortex-A7 0.9GHz x4   7.087 GFLOPS   3.472 GFLOPS

ARM11 世代の VFP と比べると core あたり 2.6倍 (単精度時,クロック差含む)。

詳細な結果は下記に追加しました

VFP Benchmark Log

Cortex-A7 は big.LITTLE でも省電力 core として用いられており、
単体の性能はあまり高くありません。

それでもエントリークラスのスマートフォンやタブレットでは
同じ Cortex-A7 Quad core のデバイスが多数リリースされています。
Snapdraogn 400 MSM8926/8226 や MT8125/8389/6582 など、
それなりにバランスが良い構成なのだと思われます。

下記は手持ちライブラリ(flatlib3)のビルド時間の比較です。
36分から 5分半へと現実的な数値になりました。
SD Card の速度に依存するためあまり正確ではないですが、
およそ 6.6倍で公称値通りといえそうです。

                                Clock  core  ISA    RAM    gcc-4.8 clang-3.4
---------------------------------------------------------------------------
(1) Raspberry Pi B ARM1176JZF   0.7GHz x1    armv6l 0.5GB   36m18s
(2) Raspberry Pi 2 Cortex-A7    0.9GHz x4    armv7l   1GB    5m29s
(3) Nexus 7 2012   Cortex-A9    1.3GHz x4    armv7l   1GB    3m42s
(4) Atom Z540      Bonnell      1.8GHz x1+HT x86      2GB    6m23s   6m18s
(5) BayTrail-D J1900 Silvermont 2.0GHz x4    x86_64   8GB    1m30s   1m11s
(6) Athlon-5350    Jaguar       2.0GHz x4    x86_64   8GB    1m33s   1m10s
(7) Core i7-2720QM SandyBridge  2.2GHz x4+HT x86_64  16GB    0m31s   0m24s

・36m18s = 36分18秒
・値は実行時間(3回の平均)。数値が小さい方が高速

Raspberry Pi 2 でそのままビルドすると ARMv6 のバイナリが生成されるため、
gcc-4.8 -march=armv7-a mfpu=neon-vfpv4 のオプションでコンパイルしています。

下記はそれぞれの詳細です。

(1) Raspberry Pi model B
BMC2835 ARM1176JZF 0.7GHz x1
RAM 512MB, SD 16GB
Debian wheezy armv6l (console)


(2) Raspberry Pi 2 model B
BMC2836 Cortex-A7 0.9GHz x4
RAM 1GB DDR2, SD 16GB
Debian wheezy armv7l (console)
gcc-4.8 (-march=armv7-a mfpu=neon-vfpv4)


(3) Nexus 7 (2012)
Tegra 3 T30L Cortex-A9 1.3GHz x4
RAM 1GB DDR3L, 8GB
Ubuntu 13.04 armv7l (console)


(4) VAIO Type P
Atom Z540 Bonnell 1.86GHz x1+HT
RAM 2GB, SSD 64GB
Ubuntu 14.04LTS x86 (console)


(5) Desktop PC
BayTrail-D Celeron J1900 Silvermont 2.0GHz x4
RAM 8GB, HDD
Ubuntu 14.04LTS x86_64


(6) Desktop PC
Athlon-5350 Jaguar 2.0GHz x4
RAM 8GB, HDD
Ubuntu 14.04LTS x86_64


(7) Desktop PC
Core i7-2720QM SandyBridge 2.2GHz x4+HT
RAM 16GB, HDD
Ubuntu 14.04LTS x86_64

GPU 周りは変わっていないようです。下記ページに追加しました。

CPU/GPU OpenGL ES Extension (Mobile GPU)

Linux で Gamepad の値を読み込む & デバイスの判定

Linux では接続した Gamepad の情報を /dev/input/js* から読み取ることができます。
構造体などは /usr/include/linux/joystick.h で定義されており、
具体的な方法は下記ドキュメントの通り。

joystick-api.txt

int fd= open( "/dev/input/js0", O_RDONLY );

unsigned char  ButtonData[BUTTON_DATA_MAX];
signed int     StickData[STICK_DATA_MAX];

for(;;){
    struct js_event  event;
    if( read( fd, &event, sizeof(struct js_event) ) >= sizeof(struct js_event) ){
        switch( event.type & 0x7f ){
        case JS_EVENT_BUTTON:
            if( event.number < BUTTON_DATA_MAX ){
                ButtonData[ event.number ]= event.value != 0;
            }
            break;
        case JS_EVENT_AXIS:
            if( event.number < STICK_DATA_MAX ){
                StickData[ event.number ]= event.value;
            }
            break;
        }
    }
}
close( fd );

read() はイベントが発生するまで block するので、スレッドを使うか
他の IO のように select() を用いることが可能です。

ボタンやスティックの配列はコントローラ毎に異なっています。
アプリケーション側で配列の変更が必要です。
Windows の DirectInput で認識する配列とも異なっているようです。

とりあえず下記コントローラで動作を確認しました。

・PS3 コントローラ (USB 接続)
・Xbox 360 USB コントローラ
・PS4 DUALSHOCK4 (USB 接続)

PS3コントローラは各ボタンの感圧情報も送られてきます。
使用した環境は下記の通り。
Ubuntu 13.10 x86_x64 (64bit)

● デバイスの種類の判定

自分の環境では Gamepad だけでなく、ワイヤレスマウスのレシーバーも
/dev/input/js* に列挙されていました。
そのままでは不便なので、Gamepad かどうかの識別を行います。

udevadm info /dev/input/js0

上記コマンドでは正しく ID_INPUT_JOYSTICK と識別されているので、
udevadm の手法を調べてみました。
まず stat() で /dev/input/js* の device 番号を調べます。

struct stat  file_stat;
stat( "/dev/input/js0", &file_stat );
assert( S_ISCHR( file_stat.st_mode ) );
int dev_major= major( file_stat.st_rdev );
int dev_minor= minor( file_stat.st_rdev );

キャラクタデバイスなら "/sys/dev/char/" 以下に "major:minor" という
名前でリンクが作られているので、それを開きます。
その中の device/capabilities 以下に、対応しているボタンなどの
情報が格納されています。

例えば major,minor が 13,0 なら
/sys/dev/char/13:0/device/capabilities/key

中身は text で long 型の bit mask です。
udevadm では、この bitmask を読み取ってゲームコントローラ用のボタンが
存在してるかどうかで判別を行っていました。
ボタンなどのシンボルは /usr/include/linux/input.h で定義されています。

static bool ReadCaps( int dev_major, int dev_minor, const char* file_name, CapsType& caps )
{
    caps.Clear();
    char cap_name[CAP_NAME_MAX];
    sprintf_s( cap_name, CAP_NAME_MAX, "/sys/dev/char/%d:%d/device/capabilities/%s", file_name, dev_major, dev_minor );
    int fd= open( cap_name, O_RDONLY );
    if( fd < 0 ){
        return  false;
    }
    char    caps_buffer[STAT_MAX_SIZE];
    memset( caps_buffer, 0, STAT_MAX_SIZE );
    read( fd, caps_buffer, STAT_MAX_SIZE-1 );
    close( fd );

    const char* cp= caps_buffer;
    for(; *cp && *cp != '\n' ;){
        unsigned long   value= strtoul( cp, NULL, 16 );
        for(; *cp && *cp != ' ' ; cp++ );
        for(; *cp == ' ' || *cp == '\n' ; cp++ );
        caps.Push( value );
    }
    return  true;
}

bool IsJoystick( const char* device_path )
{
    struct stat  file_stat;
    stat( device_path, &file_stat );
    assert( S_ISCHR( file_stat.st_mode ) );
    int dev_major= major( file_stat.st_rdev );
    int dev_minor= minor( file_stat.st_rdev );

    CapsType  caps_ev;
    CapsType  caps_key;
    CapsType  caps_abs;
    ReadCaps( dev_major, dev_minor, "ev", caps_key );
    ReadCaps( dev_major, dev_minor, "key", caps_key );
    ReadCaps( dev_major, dev_minor, "abs", caps_key );

    if( caps_ev.TestBit( EV_ABS ) && caps_abs.TestBit( ABS_X ) && caps_abs.TestBit( ABS_Y ) ){
        if( cap_key.TestBit( BTN_A ) || caps_key.TestBit( BTN_TRIGGER ) || caps_key.TestBit( BTN_1 ) ){
            return  true;
        }
    }
    return  false;
}

abs に ABS_X / ABS_Y のアナログの絶対座標データが含まれており、
かつ key に BTN_A / BTN_TRIGGER / BTN_1 のいずれかが存在している場合に
JOYSTICK とみなしています。

class CapsType {
public:
    enum {
        CAPS_BUFFER_MAX= 32,
    };
private:
    unsigned long   CapsBuffer[CAPS_BUFFER_MAX];
    unsigned int    Index;
public:
    CapsType() : Index( 0 )
    {
        Clear();
    }
    void Clear()
    {
        memset( CapsBuffer, 0, sizeof(unsigned long) * CAPS_BUFFER_MAX );
        Index= 0;
    }
    void Push( unsigned long value )
    {
        if( Index < CAPS_BUFFER_MAX ){
            CapsBuffer[Index++]= value;
        }
    }
    bool TestBit( unsigned int bit_id ) const
    {
        const int   LONG_BIT_SIZE= sizeof(unsigned long) * 8;
        unsigned int    bit_no= bit_id & (LONG_BIT_SIZE-1);
        unsigned int    data_no= bit_id / LONG_BIT_SIZE;
        if( data_no < Index ){
            return  ( CapsBuffer[Index - data_no -1] & (1UL<

Linux は LP64 なので long 型のサイズは可変です。
64bit OS なら 64bit, 32bit なら 32bit となる点に注意。
32bit 時は CAPS_BUFFER_MAX が不足する可能性があるため、厳密には
/usr/include/linux/input.h で定義されている EV_MAX, KEY_MAX, ABS_MAX
からバッファサイズを求めた方が安全です。
bitmask は上位データから並んでいるので、読み込んだあとのテーブルが
逆順になる点も注意が必要です。

関連エントリ
Linux 他 X11 で OpenGL 4 を使う
Android 3.1 と GamePad のイベントの詳細 (2)
Android 3.1 と GamePad のイベントコード