brly.github.io
C++
Assembler
H.264
01 Nov 2015

Pretty Pull Request

ちょっと前に OpenH264 というエンコーダに小さなプルリクを出した時のメモが以下のように今も残ってた。

この Mac のデフォルトのメモ帳は要素を増やすとそれなりに動作が緩慢になっていくので消そうと思ったけど ただ消すのも忍びないので書き残しておくことにした。

メモでは色々調べていたけど、作業的には 小さなもの だった。

ことの発端としてはこういうサイト を見ながら、小さなSIMDプログラムを書く機会があったが、本当に小さくてすぐ終わってしまったので 他にも何かしらで書けないかと考えた時についでにOSSにプルリク投げてみるか、という機運が高まったことから始まった。 最終的にSIMDコード書けてないわけだけど…。

さらに、SIMDは intrinsics で書きたかったのだけれど、 SIMDはどこもアセンブラで、メモを見返してみるとアセンブラについて調べた部分がいくらかあった。

書いてて思い出してきたが AVX2の開発も受け付けてるという記述があり、書こうと思ったら そもそもAVX2がプログラム内で有効であるか検出する機構がないので、とりあえずそれを書いたという流れだったと思う。

コミットログを眺めてみたけどぱっと見AVX2コードは入ってなさそう。今はあんまりやる気がない..。

もう書くことがないので、あとはつらつらメモの残りを羅列することにする。 プロジェクトを何も知らない人が読んでも意味がわからず、メモ同士の関連性も低く 本当に有用にするには補助的な文章が必要だろうけど面倒くさいのでしない。

あと、ここに書いてあることの正しさは保証してませんので予めご了承。

OpenH264の解析

ISVCEncoder

空っぽのクラス。抽象クラス。 具象クラスは CWelsH264SVCEncoder になる。メインのでかいクラス。 WelsCreateSVCEncoder() は encoder/plus/src/welsEncoderExt.cpp にある。

エントリーポイント

codec/console/enc/src/welsenc.cpp にコンソール版の main() がある。

EncodeFrame

EncodeFrameInternalを呼び出す。入力がI420じゃない場合は中断。

EncodeFrameInternal
WelsEncoderEncodeExt

メインな関数。色々呼び出す。分かったものだけ記述。

DecideFrameType

フレームタイプを決定する。x264にも似た名前の関数があるけど、x264と違ってここから探索が発生することはなさそう

Intra予測の関数
MEはどこ/MEはどこから発火する?

svc_motion_estimate.cpp:WelsMotionEstimateSearch[Static|Scrolled]

encoder_ext において pFuncList->pfMotionSearch に代入されている。 代入後 core/src/svc_base_layer_md で利用されている。

WelsMotionEstimateSearch は内部で pFuncList->pfCalculateSatd を呼び出す

WelsMdP16x16 はどこから呼ばれる
WelsMdInterMb と WelsMdInterMbEnhancelayer の違い

アセンブラ

読む上で気をつけたこと。

データの処理単位

セミコロンの前の文字はよく命令の suffix についている。

メモってあった頻出命令

mov[d|q|dqa|dqu|sxd]

lea

アドレス計算命令。フラグレジスタが変更されない。アドレス自体の演算に利用。

pxor

ビット単位の xor を実行。オペランドには 64bit同士、128bit同士を指定することができる。

psub[b|w|d]

オペランドに64bit同士、128bit同士をとれる。 suffix がそれぞれ byte, word, double-word を指すので 8bit/16bit/32bit スロットでの演算となる。

pmax[s|u][|b|w|d]

レジスタを2つとり、スロット毎に比較して最大値を格納する。 値を評価する場合に符号あり・なしとスロットの大きさで命令が分かれる。

psadbw

符号なし8bit整数で絶対値誤差の合計値を計算して格納レジスタの下位16bitに書き込む。 オペランドに64bit/128bitレジスタ同士を取る。

shufps

名前的にはシャッフルを押すほどのシャッフルでもないけど確かにシャッフル可能な命令。 オペランドを3つとり(うちSIMDレジスタが2つ)、スロットを32bitで評価してそれぞれの SIMDレジスタから2つのスロットを格納先へ書き込む。

pmaddubsw

Multiply-ADD Unsigned Byte, Signed Word

乗算はオペランドレジスタを符号なし8bit整数スロットで評価し、 次のオペランドレジスタは符号あり8bitで評価。 乗算の結果を2つずつ和にして16bitスロットとして格納先レジスタへ書き込む。

movehl_ps

各 hi-64bit を2つのSIMDレジスタからとって merge。

YASM/NASM

用いられているアセンブラ。

codec/common/x86/inc_asm.asm に共通マクロがある。

わかったものだけ。

AVX2の検出

AVXの検出

OpenH264のアセンブラ関数

あんまり調べてない。

WelsSampleSadFourWxH

4つ分計算する。この4つ分は上下左右の1pixelだけずれたものを計算している。

WelsSampleSad4x4_mmx

ナイーブに実装されている。mmxなのでレジスタ長が64bitなので8bit*8で2line分まで入るので よしなにロードして2回 psadbw するだけ。

WelsSampleSad16x16_sse2

128bitSAD命令で 4x16 を4回呼ぶ構成。

場所

codec/common/x86/satd_sad.asm