月別アーカイブ: 2009年10月

ATI Stream SDK v2.0 RADEON HD 5850 と OpenCL

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

NetWalker PC-Z1 Debian

カーネルの再構築をテストするために 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 PC-Z1 [A] キーを Control キーと兼用する実験

以前 キーボードのカスタマイズ でやりたかったのはこれです。
横に長い 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 / DirectX 11 UAV を使った書き込みとブレンド

前回説明したとおり、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    tex0  : register( t0 );
RWTexture2D  tex1  : register( u1 );
Texture2D   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 関連