GCCでMMXコードを書いてみた-"-vo xv"をハック

前回は、大量にあるmplayerのソースファイルから、どの変をいじれば目的が達成されるのかアタリをつけるのがよくわからなかったので、ついPPフィルタである"-vf eq"のファイルを改変するアプローチをとった。

だか、実際に改変作業をすすめ、当該ファイルや周辺のルーチンの中身を読み解いていくうちに、メモリまわりのオーバーヘッドが増えていくのが見えてきた。-vf系のフィルタは、動画ファイルのデコード処理用とはまったく別の新たなメモリバッファを動的に取得し、それに動画イメージをコピーし適切な処理を加え、最終的に-voで指定された表示用ルーチンにPP処理済みのイメージバッファのアドレスを渡す。
とか書いているうちに訳が分からなくなるほどに、メモリアドレスやメモリバッファをコピーしたものを渡す。それらの無駄が動画再生時にフレーム落ちを生む結果となる。


前回も書いたが、昨今のプロセッサで高速化を図る為には、とにかくメモリアクセスを減らすことが大前提。ということで、どうやってメモリアクセスを減らすか考えた。
最終的には、"-vo xv"で指定されるxvオーバーレイに、イメージデータをコピーするルーチンを改変した。ここのコピー処理を、TV->PC変換を行いつつコピーするように変更したのだ。
これで"-vf eq"などPP処理を使う場合に行われるセットアップなど、メモリに対する処理は一切行われなくなる。無駄がなくなる。


その結果、90秒のクリップのベンチマークで74.5秒-69.1秒と5秒強の高速化、そしてなにもオプションをつけずに動画を再生させたとき、フレーム落ちがまったくないことを確認した(ただし再生開始時、確実に4フレーム落ちるのは変わらず)。
前回より十分高速であり、TV->PC変換をかけてもフレーム落ちが0で使えるようになった。


改変したファイルは"libvo/vo-xv.c"である。
これに前回作成したMMXルーチンをそのまま持ってきて流用した。

//以下、ループの内部のみです。これだけでは動きません。

	while(height--)
	{

		// srcプリフェッチ(1ライン分32バイト境界ごとに空読み)
	       __asm__ volatile (
			"movl %0, %%eax \n\t"
			"movl %1, %%ecx \n\t"

			ASMALIGN(4)
		"lu: \n\t"
			"movq (%%eax), %%mm0 \n\t"
			"addl $32, %%eax \n\t"

			"subl $1, %%ecx \n\t"
			"jnz lu \n\t"

			:: "r" (isrc), "r" (iwidth8)
			: "%eax", "%ecx"
			);

		i = iwidth8;
		while(i--)
		{

		// 8ドット単位の処理
        	__asm__ volatile (

			ASMALIGN(4)

			"movq (%0), %%mm0 \n\t"		//HL R
			"movq %%mm4, %%mm1 \n\t"		//L opt -> mm1

			"pxor %%mm2, %%mm0 \n\t"		//HL 255-0 -> 127 - -128
                     "movq %%mm4, %%mm6 \n\t"		//H opt -> mm6

  			"punpcklbw %%mm0, %%mm1 \n\t"	//L アンパック
                     "punpckhbw %%mm0, %%mm6 \n\t"	//H

			"pmulhw %%mm3, %%mm1 \n\t"		//L

			//"movq (%1),%%mm0 \n\t"		//HL destのプリフェッチ(空読み)
			//ライトスルーキャッシュでは効果がない

                     "pmulhw %%mm3, %%mm6 \n\t"		//H

			//"paddsw %%mm5, %%mm1 \n\t"	//L mm1 += mm5(補正値を加える)	
			//"paddsw %%mm5, %%mm6 \n\t"	//H mm1 += mm5(現在0なので必要ない)

			"packsswb %%mm6, %%mm1 \n\t"	//HL パック(丸め有)

                     "pxor %%mm2, %%mm1 \n\t"		//HL 127 - -128 -> 255 - 0

			"movq %%mm1, (%1) \n\t"		//HL W

			:: "r" (isrc), "r" (idst)		// インプットレジスタ rはeax〜ediのうちいずれかが割り当てられる
			: "memory"				// 破壊レジスタ, メモリ  指定された項目が破壊される場合付け加える
			);

			isrc += 8;
			idst += 8;
		}

		isrc += isrcmod;
		idst += idstmod;
	}

//ループ展開はもちろんwhile()による繰り返し処理も"sub $1,%%eax; jnz xloop"のようにすべてアセンブラで書いてたのだが、なぜかこれが最速でした。
//アウトオブオーダーだから?結局ビルド&ベンチの繰り返し。


問題点としては、デコーダフレームバッファメモリに直接書き込む、ダイレクトレンダリング"-dr"のとき、上記のイメージバッファのコピー処理はカットされる為、変更したルーチンは使われない。
そのため"-dr"をつけた場合は、TVtoPC変換が機能がしない。
しかしこのことは、そもそもPCスケールでエンコードされた動画をみるときに、"-dr"をつけて再生すれば、うちのようなマシンでは適切に見られるということであり、デメリットではない。


なにより、これで普通につかえるようになった。
MMXコードは楽しく書けたし、作ったコードは常用されるところにある。
目論見は大成功。


あとでHPスペースを取得したら、改変したファイルと、そのパッチを適用した野良ビルドmplayerを掲載する。とりあえずもう眠い....。


##追加
ラインのプリフェッチと細々した高速化を施し、90秒クリップによるベンチで上記よりさらに5秒短縮、65秒弱まで短縮できた。