2013/12/13

5層クリア!

ffxiv_20131213_053336

よもぎせんぱいでもクリア完了!

ヒーラー脚出たけどロット負けした系。

以下、学者メモ。

2013/12/09

学者5層メモ

おもちせんぱいでちょいちょい 5 層に行ってるので、そのメモ。

フェイズ 1

開幕は OT が死にやすいのでそっちに陣を張る。
MT は火炎が飛んできたときが危ないけど、普通に回復してればまず死なない。
余裕があればシャドウフレアとかで攻撃する。

フェイズ 2

(タイミング調整しないときは)デスセンテンスに合わせてフィジク、センテンス後死にそうなら活性法をつかう。
フェアリーは固定しておかないと、青で動いたときについてきて危険。
偶数回直前の赤には陣と堅実魔をつかう。

フェイズ 3

フェアリーを山の上に配置。自分は丘。
ダイブボム中は回復しづらくなるので要注意。
必要があればメテオの場所指定。
エーテリアルプロフュージョン前に陣と鼓舞。

フェイズ 4

ウィルスあわせたりして盾を殺さないようにがんばる。
余裕があればドレッドさんにミアズマ。
ツイスターを見落とさないように。

フェイズ 5

火炎は圧縮して置く。
もしOTが落ちてたらフェアリーを装置上に移動させる。

バハムート5層

ffxiv_20131201_020917

くりあー。

2013/11/17

検証用メモ

ベルヌーイ試行において、試行回数を n、成功回数を s としたとき、その成功確率 p

p = s / n

で表すことができ、その推定区間は

p ± k * sqrt(p * (1 - p) / n)

で表されるらしい。

たとえば、攻撃 1000 回のうちクリティカル 50 回が発生する、信頼度 95%の推定区間は、

p = 50 / 1000 = 0.05

0.05 ± 1.96 * sqrt(0.05 * 0.95 / 1000) = 0.05 ± 0.0135

3.65%~6.35%

となるらしい。

2013/11/13

おもち育成計画

おもちさんで学者を極めたいので神話の計画メモ。

学者はクリティカルが良いらしいので、最終的な目標(左側)は

 頭:アラガンヒーラーサークレット(2 層)
 胴:アギュトガウン(神話 825)
 手:アラガンヒーラーグローブ(1 層)
 帯:ヒーローベルト・オブ・ヒール(神話 375)
 脚:アラガンヒーラーブリーチ(5 層)
 足:アラガンヒーラーブーツ(2 層)

こんなかんじ。神話とアラガンで性能が同じなのがあるので、そういうのはアラガンに。

現在神話 600 で、レリック光らせなくちゃいけないのであと神話が 1500 必要。5 週間。

右側はとりあえずバハ 5 層の関係で神話のみになりそう。5 層攻略はメインが優先だから、おもちさんはいつ行けるようになるかは不明だけども……。

2013/10/20

Windows 8.1 にしたら OBS のゲームキャプチャが動かなくなった!

題名の通り。

とりあえず以下のようにコードを書き換えれば動くようになる。

D3D9Capture.cpp の 105 行目あたり

#ifdef _WIN64

#define NUM_KNOWN_PATCHES 5
#define PATCH_COMPARE_SIZE 13
UPARAM patch_offsets[NUM_KNOWN_PATCHES] = {/*0x4B55F,*/ 0x54FE6, 0x550C5, 0x8BDB5, 0x93AFA, 0x1841E5};
BYTE patch_compare[NUM_KNOWN_PATCHES][PATCH_COMPARE_SIZE] =
{
    //{0x48, 0x8b, 0x81, 0xc8, 0x38, 0x00, 0x00, 0x39, 0x98, 0x68, 0x50, 0x00, 0x00},  //winvis - 6.0.6002.18005
    {0x48, 0x8b, 0x81, 0xb8, 0x3d, 0x00, 0x00, 0x39, 0x98, 0x68, 0x50, 0x00, 0x00},  //win7   - 6.1.7600.16385
    {0x48, 0x8b, 0x81, 0xb8, 0x3d, 0x00, 0x00, 0x39, 0x98, 0x68, 0x50, 0x00, 0x00},  //win7   - 6.1.7601.17514
    {0x48, 0x8b, 0x81, 0xb8, 0x3d, 0x00, 0x00, 0x39, 0xB0, 0x28, 0x51, 0x00, 0x00},  //win8.1 - 6.3.9431.00000
    {0x8b, 0x81, 0xb8, 0x3d, 0x00, 0x00, 0x44, 0x39, 0xa0, 0x28, 0x51, 0x00, 0x00},  //win8.1 - 6.3.9600.16384
    {0x49, 0x8b, 0x85, 0xb8, 0x3d, 0x00, 0x00, 0x39, 0x88, 0xc8, 0x50, 0x00, 0x00},  //win8   - 6.2.9200.16384
};

#define PATCH_SIZE 2
BYTE patch[NUM_KNOWN_PATCHES][PATCH_SIZE] =
{
    //{0xEB, 0x12},
    {0xEB, 0x12},
    {0xEB, 0x12},
    {0x90, 0x90},
    {0x90, 0x90},
    {0x90, 0x90},
};

#else

#define NUM_KNOWN_PATCHES 5
#define PATCH_COMPARE_SIZE 12
UPARAM patch_offsets[NUM_KNOWN_PATCHES] = {/*0x4BDA1,*/ 0x79C9E, 0x79D96, 0x7F9BD, 0x8FBB1, 0x166A08, };
BYTE patch_compare[NUM_KNOWN_PATCHES][PATCH_COMPARE_SIZE] =
{
    //{0x8b, 0x89, 0x6c, 0x27, 0x00, 0x00, 0x39, 0xb9, 0x80, 0x4b, 0x00, 0x00},  //winvis - 6.0.6002.18005
    {0x8b, 0x89, 0xe8, 0x29, 0x00, 0x00, 0x39, 0xb9, 0x80, 0x4b, 0x00, 0x00},  //win7   - 6.1.7600.16385
    {0x8b, 0x89, 0xe8, 0x29, 0x00, 0x00, 0x39, 0xb9, 0x80, 0x4b, 0x00, 0x00},  //win7   - 6.1.7601.17514
    {0x8b, 0x80, 0xe8, 0x29, 0x00, 0x00, 0x39, 0xb0, 0x40, 0x4c, 0x00, 0x00},  //win8.1 - 6.3.9431.00000
    {0x80, 0xe8, 0x29, 0x00, 0x00, 0x83, 0xb8, 0x40, 0x4c, 0x00, 0x00, 0x00},  //win8.1 - 6.3.9600.16384
    {0x8b, 0x80, 0xe8, 0x29, 0x00, 0x00, 0x39, 0x90, 0xb0, 0x4b, 0x00, 0x00},  //win8   - 6.2.9200.16384
};

#define PATCH_SIZE 1
BYTE patch[NUM_KNOWN_PATCHES][PATCH_SIZE] =
{
    //{0xEB, 0x02},
    {0xEB},
    {0xEB},
    {0xEB},
    {0xEB},
    {0xEB},
};

#endif

自力でビルドできないひとは OBS 公式の対応待ちかな。

 

追記:公式での対応も来てました。はやい。

2013/10/18

続・rtmp-nginx-module で配信を mp4 で録画する方法

どうやら ffmpeg はエンコード中に kill シグナルが来ると、出力する mp4 ファイルが破損してしまうらしく、前の記事の方法では高確率でファイルが壊れてしまっていた。

なので正攻法(?)で、録画終了時に flv ファイルを mp4 ファイルに変換してやることにした。

まずは以下のスクリプトを /usr/local/bin/flv2mp4 あたりに作る。

#!/bin/bash

input=""
output=""
remove_origin="FALSE"

while getopts i:o:r OPT
do
    case $OPT in
        "i" ) input="$OPTARG" ;;
        "o" ) output="$OPTARG" ;;
        "r" ) remove_origin="TRUE" ;;
    esac
done

if [ "$input" = "" ]; then
    exit 1
fi
if [ "$output" = "" ]; then
    output=${input%.*}.mp4
fi

echo "Input: $input"
echo "Output: $output"

ffmpeg -y -i $input -vcodec copy -acodec copy $output

if [ "$remove_origin" = "TRUE" ]; then
    rm -f $input
fi

あとは nginx.conf の rtmp application の設定の中に、

record all;
record_path /tmp/rec;
record_suffix –%Y%m%d%H%M%S.flv;
exec_record_done /usr/local/bin/flv2mp4 –i $path –r;

と記述しておけば、放送中は flv 形式で一時録画され、放送終了時に勝手に mp4 に変換され、元の flv は削除される。

めでたしめでたし!

2013/10/13

rtmp-nginx-module で配信を mp4 で録画する方法

題名の通り。

record all では出力形式が FLV 形式なので、Samba を通して再生すると恐ろしくシークが遅い。なので MP4 形式で録画するようにしたかったのだけど、rtmp-nginx-module にはそんな機能はないらしい。

そんなときは ffmpeg さんに頼んでみようというお話。

まずは以下のシェルスクリプトをどこかに作る。(例: /usr/local/bin/nginx_mp4_recording)

#!/bin/bash

NOW=$(date +%Y%m%d-%H%M%S)
OUTPUT=/tmp/rec/${1}-${NOW}.mp4

ffmpeg -re -i rtmp://localhost/live/${1} -vcodec copy -acodec copy $OUTPUT

あとは nginx の rtmp サーバーのアプリケーション(例: application live { … })内に、

exec /usr/local/bin/nginx_mp4_recording $name;

と書いて配信開始時に呼び出されるようにしておけばオッケー。

気になる負荷は Atom 330 1.6GHz で 0.5~1.0% くらい。余裕。

2013/10/02

nginx-rtmp-module を使用した Twitch.tv と Ustream の同時配信環境構築

Twitch で配信するとカクつく人が出るようなので、Ustream でも同時配信がしたくていろいろ調べてみた結果、nginx の nginx-rtmp-module を使用すればいいことがわかったのでメモ。

元ネタはhttps://obsproject.com/forum/viewtopic.php?f=18&t=2651

まずは nginx のインストール。

$ sudo apt-get install build-essential libpcre3 libpcre3-dev libssl-dev
$ wget http://nginx.org/download/nginx-1.5.6.tar.gz
$ wget https://github.com/arut/nginx-rtmp-module/archive/master.zip
$ tar –zxvf nginx-1.5.6.tar.gz
$ unzip master.zip
$ cd nginx-1.5.6
$ ./configure \
  --prefix=/etc/nginx \
  --sbin-path=/usr/sbin/nginx \
  --conf-path=/etc/nginx/nginx.conf \
  --http-log-path=/var/log/nginx/access.log \
  --error-log-path=/var/log/nginx/error.log \
  --lock-path=/var/lock/nginx.lock \
  --pid-path=/var/run/nginx.pid \ 
  --with-http_ssl_module \ 
  --with-ipv6 \
  --add-module=../nginx-rtmp-module-master
$ make
$ sudo make install

つぎに起動用のスクリプトを http://wiki.nginx.org/Nginx-init-ubuntu から拾ってきて、/etc/init.d/nginx に置いて chmod +x する。

起動用スクリプト内にある DAEMON=/usr/local/sbin/nginxDAEMON=/usr/sbin/nginx に、NGINX_CONF_FILE="/usr/local/nginx/conf/nginx.conf"NGINX_CONF_FILE="/etc/nginx/nginx.conf" にそれぞれ書き換えて保存し、

$ /usr/sbin/update-rc.d -f nginx defaults

を実行してスタートアップに登録する。

つぎに ffmpeg のインストール。avconv では Input/Output error が発生して Ustream に送信できないような感じだったので、ffmpeg 推奨。

$ sudo add-apt-repository ppa:jon-severinsson/ffmpeg
$ sudo apt-get update
$ sudo apt-get install ffmpeg

add-apt-repository が見つからないって言われたときは、sudo apt-get install software-properties-common を試してみるといいかも。

そして、nginx の設定ファイル(/etc/nginx/nginx.conf)に以下の設定を追記

rtmp {
    server {
        listen 1935;
        chunk_size 4096;
        application live {
            live on;
            record off;
            # twitch は push でも問題なく動作する
            push rtmp://live-lax.twitch.tv/app/your_stream_key_here;
            # ustream は push では上手く動作しないので ffmpeg を間にはさむ
            exec ffmpeg -re -i rtmp://localhost/live/$name -c copy -f flv "rtmp://x.xxxxxxxx.fme.ustream.tv/ustreamVideo/xxxxxxxx/your_stream_key_hereq flashver=FME/3.0\20(compatible;\20FMSc/1.0)";
        }
    }
}

最後に、

$ sudo service nginx start

で nginx を起動してサーバー側の設定は完了。

あとは OBS から、設定したサーバーにストリーミングするように設定すれば同時配信ができてるはず。

 

追記:

このままだとUst側の配信が非常に不安定になったため、TCPRelay v0.1.1 beta (配布元: http://strikerx3.blogspot.jp/2012/06/tcprelay-011-beta.html)を使用して配信を安定させた。

tcprelay.jar を /usr/local/bin に放り込み、

rtmp {
    server {
        listen 1935;
        chunk_size 4096;
        exec java -jar /usr/local/bin/tcprelay.jar -p:1396 -th:x.xxxxxxxx.fme.ustream.tv;
        application live {
            live on;
            record off;
            # twitch は push でも問題なく動作する
            push rtmp://live-lax.twitch.tv/app/your_stream_key_here;
            # ustream は push では上手く動作しないので ffmpeg を間にはさむ
            exec ffmpeg -re -i rtmp://localhost/live/$name -c copy -f flv "rtmp://localhost:1396/ustreamVideo/xxxxxxxx/your_stream_key_hereq flashver=FME/3.0\20(compatible;\20FMSc/1.0)";
        }
    }
}

と設定を書き換えたらいい感じになった。

2013/08/05

おそうじ

ベンチマークを動かすとグラボの温度が92℃とかになってたので、レッツ掃除。

掃除機でヒートシンクのホコリを除去して、ケースのフィルタのホコリもきれいに掃除。

グラボはカバーを外して中まで掃除。ついでにCPUクーラーも軽く掃除。

 

お掃除の結果、100%で負荷をかけ続けても70℃前後で安定するようになりました。どんだけホコリ詰まってたんだ・・・。

2013/07/23

ASUS Xonar DSX が認識されなくなった!

タイトルの通り。抜き差ししてもダメ。ドライバを削除して再インストールしようとすると、マッチするドライバが見つからないといわれる始末。Xonar のドライバを指定してもダメだといわれるので、認識されているデバイス ID と INF ファイルに記載されているデバイス ID を比べてみると、

デバイスマネージャ: PCI\VEN_13F6&DEV_8788&SUBSYS_878813F6
INF ファイル: PCI\VEN13F6&DEV_8788&SUBSYS_85221043

なんということでしょう。別物として認識されているではありませんか。

SUBSYS_878813F6 でググってみたところ、asus のフォーラムに「SUBSYS_878813F6 はEEPROM が上書きされているか、または EEPROM が完全に壊れてる問題が発生していることを意味してるよ。EEPROM 修復ツールを使ったときに成功したって出てた?」みたいな書き込みがあった。

今度は EEPROM restore tool でググってみると、Xonar EEPROM Failure というページがヒットして、簡単に和訳すると、

時折 Xonar PCI/PCI-E カードは突然認識されなくなることがあります。
ドライバーの読み込みとインストールが拒否され、デバイスマネージャでは「C-Media Oxygen audio device」または不明なサウンドデバイスとして表示されます。他の PCI スロットや他のコンピュータにこのカードを刺しても問題は解決されません。
これは、EEPROM が上書きされることにより生じます。
確実に EEPROM が上書きされているかを確認するためには、デバイスマネージャを開き、デバイスのプロパティを開き、詳細タブを開き、ハードウェア ID を選択して SUBSYS_ の後の値を確認します:

…… ……
PCI\VEN_13F6&DEV_8788&SUBSYS_85221043 OK (Xonar DSX)
PCI\VEN_13F6&DEV_8788&SUBSYS_878813F6
PCI\VEN_13F6&DEV_8788&SUBSYS_434D1043
overwritten EEPROM

こんな感じ。つまり、EEPROM がなぜか勝手に上書きされたことが原因のようだ!

同ページに修復ツールが置いてあって、CD を焼いて起動ツールを走らせ、本来の ID に戻すことができるらしい。実際に試してみたら認識してくれるようになりました。無事解決!

ありがとうツールの作者様!

2013/07/17

SUMMER SALE!

すちーむのサマーセールで買ったものリスト:

Sid Meier's Civilization V: Gold $14.99
Terraria, Deus Ex Collection, Castle Crashers, Valve Complete Pack – 2013 $46.22
計 $61.21 也

どう考えても新生までに消化しきれません。本当にありがとうございました。

2013/07/02

新生FFXIV Beta Phase3

フェイズ3から守秘義務が解除になったらしい。

そういうわけでペタペタ。

ffxiv_20130622_000641

ffxiv_20130629_140851

ffxiv_20130629_151826

ffxiv_20130622_134751

ffxiv_20130614_215429

ffxiv_20130614_215731

ffxiv_20130629_211208

2013/05/21

Open Broadcaster Software

リアルタイム配信向けのソフトを探してたら素晴らしいソフトを発見。

image

x264 が搭載されてるので、高画質な配信ができるらしい。そして無料。

公式サイトはhttp://obsproject.com/ こちら。

不満点はぼかしとかを入れられないところ。ネトゲ配信するなら、ログにぼかしを入れたりしたい。

 

で、

image

不満点があったら好き勝手に実装してしまえるのがオープンソースの素晴らしいところ。

すばらしい・・・。

 

プラグインと改造した本体のソースはこちら。

https://github.com/ginshali/BlurPlugin – Blur effect plugin

https://github.com/ginshali/OBS – 本体

2013/01/23

DirectShow のループ

自前のウィンドウを持ってる場合は IMediaEventEx でメッセージを登録するのが楽そうだけど、ライブラリの中とかコンソールアプリケーションなんかではウィンドウメッセージを受け取れないので使えない。

そういうときは、別スレッドを立ち上げて IMediaEvent::WaitForCompletion で再生終了を検出するのがよさそうだった。

 

というメモ。

GPU!

リアルタイムに絵を大量にブレンドするのは、やっぱり CPU ではきつかったので GPU の力を借りることにした。

image

上の画像は NHK クリエイティブライブラリから借りてきた映像を 5 つ同時に表示してみてるところ。さらにテスト用 α チャンネル入りの画像をブレンドしてみてる。

  1. Haali Media Splitter → ffdshow Video Decoder → Video Mixing Renderer 9 のグラフを構築
  2. VMR9 にカスタムプレゼンタを設定。グラフを再生すると、IDirect3D9Surface の形でサーフェスを取得できるので、それを共有用に自前で作ったテクスチャにコピー
  3. ミキサーでそれぞれの共有用テクスチャを取得して、シェーダーで合成してプレーンに書き込む
  4. メインメモリ転送用のバッファに結果をコピー
  5. メインメモリに結果をコピー

という感じ。キャプチャから合成まで、GPU メモリ間の転送だけで済むので(たぶん)負荷は軽いはず。

CPU 使用率は 10% ほど。ドロップもなく軽快に動いてくれた。

 

C++ スキルが 0.2 くらい上がったかもしれない。

2013/01/06

ビデオミキサー

そういうわけで作ったらしい。

image

左下の緑のは、XAML のアニメーション再生のテスト。時刻はカスタムアセンブリの読み込みテスト。そして半透明の六角形はアルファブレンドのテスト。

60 FPS で動かしてみてるけど CPU 結構食ってる気がする。それでも 1 コア使い切ってないように見えるからまだ平気。

拡大縮小処理がまだ入っていないので、出力は 1280 x 720 固定。

とりあえず動くようにしただけなので、公開は未定。

アルファブレンドその2

修正版。

__declspec(align(16)) unsigned char alpha_mask[16] = 
{
    0x06, 0x80, 0x06, 0x80, 0x06, 0x80, 0x80, 0x80,
    0x0e, 0x80, 0x0e, 0x80, 0x0e, 0x80, 0x80, 0x80,
};
__declspec(align(16)) unsigned char swap_endian_bl[16] = 
{
    0x80, 0x00, 0x80, 0x02, 0x80, 0x04, 0x80, 0x06,
    0x80, 0x08, 0x80, 0x0a, 0x80, 0x0c, 0x80, 0x0e,
};
__declspec(align(16)) unsigned char swap_endian_lb[16] = 
{
    0x01, 0x80, 0x03, 0x80, 0x05, 0x80, 0x07, 0x80,
    0x09, 0x80, 0x0b, 0x80, 0x0d, 0x80, 0x0f, 0x80,
};
__declspec(align(16)) unsigned int andnot_mask[4] = 
{
    0xFF00FF00, 0xFF00FF00, 0xFF00FF00, 0xFF00FF00
};

//
// Alpha blending implement optimized with SSE4
//
void alpha_blend_sse4(void* background, void* foreground, int pixels)
{
    int count = pixels / 4;
    __asm 
    {
        mov ecx, count
        mov esi, dword ptr background
        mov edi, dword ptr foreground
        
        movdqa xmm6, dword ptr swap_endian_bl
        movdqa xmm7, dword ptr alpha_mask

L0:
        //-------------------------------------- Get B.rgb
        movdqa xmm0, [esi]    // B.argb
        movhlps xmm1, xmm0

        pmovzxbw xmm2, xmm0 // B.rgb
        pmovzxbw xmm3, xmm1 // B.rgb

        // using: xmm2, xmm3

        //-------------------------------------- Get F.rgb
        movdqa xmm4, [edi] // F.argb
        movhlps xmm5, xmm4

        // escape swap_endian_bl and alpha_mask to xmm0 and xmm1
        movdqa xmm0, xmm6
        movdqa xmm1, xmm7

        pmovzxbw xmm6, xmm4 // F.rgb
        pmovzxbw xmm7, xmm5 // F.rgb

        // using: xmm2, xmm3, xmm6, xmm7

        //-------------------------------------- Get F.a
        movdqa xmm4, xmm6
        movdqa xmm5, xmm7
        pshufb xmm4, xmm1 // F.a   xmm1 = alpha_mask
        pshufb xmm5, xmm1 // F.a
        
        // using: xmm2, xmm3, xmm4, xmm5, xmm6, xmm7

        //-------------------------------------- Flip endianness
        pshufb xmm2, xmm0 // xmm0 = swap_endian_bl
        pshufb xmm3, xmm0
        pshufb xmm4, xmm0
        pshufb xmm5, xmm0
        pshufb xmm6, xmm0
        pshufb xmm7, xmm0

        
        //-------------------------------------- Calc X = F.rgb * F.a
        pmulhuw xmm6, xmm4
        pmulhuw xmm7, xmm5

        //-------------------------------------- Calc Y = 1 - B.a
        pandn xmm4, dword ptr andnot_mask
        pandn xmm5, dword ptr andnot_mask
        
        //-------------------------------------- Calc Z = B.rgb * Y
        pmulhuw xmm4, xmm2
        pmulhuw xmm5, xmm3

        //-------------------------------------- Calc R = X + Z
        paddusw xmm6, xmm4
        paddusw xmm7, xmm5

        //-------------------------------------- Flip endianness
        movdqa xmm2, dword ptr swap_endian_lb
        pshufb xmm6, xmm2
        pshufb xmm7, xmm2

        //-------------------------------------- Pack values
        packuswb xmm6, xmm7

        //-------------------------------------- Write result
        movdqa [esi], xmm6
        
        //-------------------------------------- Restore escaped values
        movdqa xmm6, xmm0
        movdqa xmm7, xmm1
        
        //-------------------------------------- 
        add esi, 16
        add edi, 16
        sub ecx, 1
        jnz L0
    };
}

切り捨てが起こってしまう気がするけど、何枚も重ねないし問題ないぜ!

背景画像
base

前景画像
1

結果
out

コード使いたいという方は MIT ライセンスでどうぞ。

アルファブレンド

__declspec(align(16)) unsigned int erase_alpha[4] = 
{
    0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000
};
__declspec(align(16)) unsigned char alpha_mask[16] = 
{
    0x06, 0x80, 0x06, 0x80, 0x06, 0x80, 0x06, 0x80,
    0x0e, 0x80, 0x0e, 0x80, 0x0e, 0x80, 0x0e, 0x80,
};
__declspec(align(16)) unsigned char swap_endian_bl[16] = 
{
    0x80, 0x00, 0x80, 0x02, 0x80, 0x04, 0x80, 0x06,
    0x80, 0x08, 0x80, 0x0a, 0x80, 0x0c, 0x80, 0x0e,
};
__declspec(align(16)) unsigned char swap_endian_lb[16] = 
{
    0x01, 0x80, 0x03, 0x80, 0x05, 0x80, 0x07, 0x80,
    0x09, 0x80, 0x0b, 0x80, 0x0d, 0x80, 0x0f, 0x80,
};

void alpha_blend_sse4(void* background, void* foreground, int count)
{
    __asm 
    {
        mov ecx, count
        mov esi, dword ptr background
        mov edi, dword ptr foreground
        
        movdqa xmm6, dword ptr swap_endian_bl
        movdqa xmm7, dword ptr alpha_mask

L0:
        //-------------------------------------- Get B.rgb
        movdqa xmm0, [esi]    // B.argb
        movhlps xmm1, xmm0

        pmovzxbw xmm2, xmm0 // B.rgb
        pmovzxbw xmm3, xmm1 // B.rgb

        // using: xmm2, xmm3

        //-------------------------------------- Get F.rgb
        movdqa xmm4, [edi] // F.argb
        movhlps xmm5, xmm4

        // escape swap_endian_bl and alpha_mask to xmm0 and xmm1
        movdqa xmm0, xmm6
        movdqa xmm1, xmm7

        pmovzxbw xmm6, xmm4 // F.rgb
        pmovzxbw xmm7, xmm5 // F.rgb

        // using: xmm2, xmm3, xmm6, xmm7

        //-------------------------------------- Get F.a
        movdqa xmm4, xmm6
        movdqa xmm5, xmm7
        pshufb xmm4, xmm1 // F.a   xmm1 = alpha_mask
        pshufb xmm5, xmm1 // F.a
        
        // using: xmm2, xmm3, xmm4, xmm5, xmm6, xmm7

        //-------------------------------------- Flip endianness
        pshufb xmm2, xmm0 // xmm0 = swap_endian_bl
        pshufb xmm3, xmm0
        pshufb xmm4, xmm0
        pshufb xmm5, xmm0
        pshufb xmm6, xmm0
        pshufb xmm7, xmm0

        
        //-------------------------------------- Calc F.rgb * F.a
        pmulhuw xmm6, xmm4
        pmulhuw xmm7, xmm5
        
        //-------------------------------------- Calc B.rgb * F.a
        pmulhuw xmm4, xmm2
        pmulhuw xmm5, xmm3

        //-------------------------------------- Calc (F.rgb * F.a) + B.rgb
        paddq xmm6, xmm2
        paddq xmm7, xmm3

        //-------------------------------------- Calc ((F.rgb * F.a) + B.rgb) - (B.rgb * F.a)
        psubq xmm4, xmm6
        psubq xmm5, xmm7

        //-------------------------------------- Flip endianness
        movdqa xmm2, dword ptr swap_endian_lb
        pshufb xmm4, xmm2
        pshufb xmm5, xmm2

        //-------------------------------------- Pack values
        packuswb xmm4, xmm5
        por xmm4, dword ptr erase_alpha

        //-------------------------------------- Write result
        movdqa [esi], xmm4

        
        //-------------------------------------- Restore escaped values
        movdqa xmm6, xmm0
        movdqa xmm7, xmm1
        
        //-------------------------------------- 
        add esi, 16
        add edi, 16
        sub ecx, 1
        jnz L0
    };
}

初のインラインアセンブラで頑張ったけどまだ色がおかしい……。

処理時間は Core i7 920 上で 1280 x 720 の画像を 2 枚重ねると 1.2 msec くらい。普通にやるより 5 倍近く速い。

AVX っていう命令系も使えればメモリへアクセスしてる部分が減りそうなんだけど、Sandy 以降の CPU じゃないと使えないらしい。残念。

2013/01/03

Producer-Consumer 問題

スレッドからスレッドへと、次々にデータを受け渡すときに必要になるのがこれ。

描画スレッドを別スレッドに移動させるときに必要だったのでござる。http://www.atmarkit.co.jp/fdotnet/mthread/mthread04/mthread04_02.html の例をジェネリクスにしただけでござる。

class BoundedBuffer<T> : IDisposable
    where T: IDisposable
{
    public int Max { get; private set; }
    public int Count
    {
        get
        {
            lock (lockObject)
            {
                return queue.Count;
            }
        }
    }

    private Queue<T> queue;
    private object lockObject = new object();


    public BoundedBuffer(int maxCount)
    {
        this.Max = maxCount;
        this.queue = new Queue<T>(Max);
    }

    public void Enqueue(T value)
    {
        Monitor.Enter(lockObject);
        try
        {
            while (queue.Count >= Max)
            {
                Monitor.Wait(lockObject);
            }
            queue.Enqueue(value);
            Monitor.PulseAll(lockObject);
        }
        catch
        {
        }
        finally
        {
            Monitor.Exit(lockObject);
        }
    }

    public T Dequeue()
    {
        Monitor.Enter(lockObject);
        T result = default(T);
        try
        {
            while (!queue.Any())
            {
                Monitor.Wait(lockObject);
            }
            result = queue.Dequeue();
            Monitor.PulseAll(lockObject);
        }
        catch (Exception e)
        {

        }
        finally
        {
            Monitor.Exit(lockObject);
        }

        return result;
    }

    public void Dispose()
    {
        lock (lockObject)
        {
            T value;
            while ((value = queue.Dequeue()) != null)
            {
                value.Dispose();
            }
        }
    }
}

そのものずばりの解答を見ているとはいえ、上手く動いてくれると気持ち良いでござる。

RenderTargetBitmap の罠

大晦日に作ったフィルタで複雑な XAML ファイルを読み込んでみたら、10 ~ 15 FPS しか出てなかった。調べてみたら RenderTargetBitmap の Render メソッドで 50 msec くらいかかってた。

よく考えてみれば RenderTargetBitmap はメインメモリ上にあるので、ソフトウェアで XAML が描画されてるということになる。ハードウェアで描画するためには、ビデオメモリ上のレンダーターゲットに書き込んでもらわねばならないからだ。

ハードウェア上のレンダーターゲットからメインメモリに画像データを転送する機能は WPF には実装されておらず、されたとしてもストールが発生して非常にコストのかかる処理になってしまうので非現実的っぽい。(非同期で転送すればいいのかもだけど。)

改善策としては、現在の WPF レンダラの実装だと1 コアしか描画に使ってないので、描画するパーツを分けてそれぞれ独立に描画させるとか。面倒なのでやらないけど。

そもそもがテロップ用に作ったものなので、そんなに複雑なものは表示させないという。