11

私は8ビットピクセルデータの構造体を持っています:

struct __attribute__((aligned(4))) pixels {
    char r;
    char g;
    char b;
    char a;
}

SSE命令を使用して、これらのピクセルの特定のものを計算したいと思います(つまり、Paeth変換)。これらのピクセルを32ビットの符号なし整数としてSSEレジスタにロードするにはどうすればよいですか?

4

1 に答える 1

20

SSE2 で署名されていないピクセルをアンパックする

わかりました、最初から SSE2 整数組み込み関数を使用し<emmintrin.h>て、レジスタの下位 32 ビットにモノをロードします。

__m128i xmm0 = _mm_cvtsi32_si128(*(const int*)&pixel);

次に、最初にこれらの 8 ビット値をレジスタの下位 64 ビットの 16 ビット値にアンパックし、それらを 0 でインターリーブします。

xmm0 = _mm_unpacklo_epi8(xmm0, _mm_setzero_si128());

そして再び、これらの 16 ビット値を 32 ビット値にアンパックします。

xmm0 = _mm_unpacklo_epi16(xmm0, _mm_setzero_si128());

これで、SSE レジスタのそれぞれの 4 つのコンポーネントで、各ピクセルを 32 ビット整数として持つ必要があります。


SSE2 で署名付きピクセルをアンパックする

[-127,127]の符号付きピクセルがどのような意味を持つのか疑問に思いますが、これらの値を32ビットの符号付き整数として取得したいということを読んだところです。ただし、ピクセル値が実際に負になる可能性がある場合、ゼロを使用したインターリーブは機能しません。これは、負の 8 ビット数値が正の 16 ビット数値になるためです (したがって、数値は符号なしピクセル値として解釈されます)。負の数は1s の代わりに0s で拡張する必要がありますが、残念ながらコンポーネントごとに動的に決定する必要があり、SSE はあまり良くありません。

あなたができることは、負の値を比較し、ゼロレジスタの代わりに、結果のマスク (幸いにも1...1true と0...0false を使用します) をインターリーバンドとして使用することです。

xmm0 = _mm_unpacklo_epi8(xmm0, _mm_cmplt_epi8(xmm0, _mm_setzero_si128()));
xmm0 = _mm_unpacklo_epi16(xmm0, _mm_cmplt_epi16(xmm0, _mm_setzero_si128()));

1これにより、負の数はs で、正の数は s で適切に拡張されます0。しかしもちろん、この追加のオーバーヘッド (おそらく 2 ~ 4 個の追加の SSE 命令の形で) は、最初の 8 ビット ピクセル値が負になる可能性がある場合にのみ必要です。signed charしかし、これが実際に当てはまる場合は、 overを考慮する必要がありますchar。後者には実装定義の符号性があるためです (unsigned charそれらが一般的な符号なし [0,255] ピクセル値である場合に使用する必要があるのと同じ方法で)。


シフトを使用した代替 SSE2 アンパック

明確にされているように、符号付き 8 ビットから 32 ビットへの変換は必要ありませんが、完全を期すために、 haroldは、上記の比較ベースを使用する代わりに、SSE2 ベースの符号拡張について別の非常に良いアイデアを持っていました。バージョン。まず、8 ビット値を 32 ビット値の下位バイトではなく上位バイトにアンパックします。下位部分は気にしないので、8 ビット値を再度使用するだけで、追加のゼロレジスタと追加の移動の必要がなくなります。

xmm0 = _mm_unpacklo_epi8(xmm0, xmm0);
xmm0 = _mm_unpacklo_epi16(xmm0, xmm0);

ここで、上位バイトを下位バイトに算術右シフトする必要があるだけです。これにより、負の値に対して適切な符号拡張が行われます。

xmm0 = _mm_srai_epi32(xmm0, 24);

これは、上記の SSE2 バージョンよりも命令数とレジスタの効率が高くなるはずです。

また、単一のピクセルの命令数が等しくなり (多くのピクセルで償却されると 1 つ多くの命令が必要になる)、上記のゼロ拡張と比較してレジスタ効率が向上する (余分なゼロレジスタがないため) ため、レジスタがまれな場合は符号なしから符号付きへの変換に使用されますが_mm_srli_epi32、算術シフトの代わりに論理シフト ( ) が使用されます。


SSE4 によるアンパックの改善

haroldのコメントのおかげで、最初の 8 から 32 への変換にはさらに優れたオプションがあります。SSE4 サポート (正確には SSE4.1) がある場合、レジスタの下位 32 ビットにある 4 つのパックされた 8 ビット値からレジスタ全体の 4 つの 32 ビット値に完全に変換するための命令があります。符号付きおよび符号なしの 8 ビット値:

xmm0 = _mm_cvtepu8_epi32(xmm0);   //or _mm_cvtepi8_epi32 for signed 8-bit values

SSE2 によるピクセルのパッキング

この逆変換のフォローアップについては、まず符号付き 32 ビット整数を符号付き 16 ビット整数にパックし、飽和させます。

xmm0 = _mm_packs_epi32(xmm0, xmm0);

次に、飽和を使用して、これらの 16 ビット値を符号なし 8 ビット値にパックします。

xmm0 = _mm_packus_epi16(xmm0, xmm0);

その後、最終的にレジスタの下位 32 ビットからピクセルを取得できます。

*(int*)&pixel = _mm_cvtsi128_si32(xmm0);

0彩度のため、このプロセス全体で に負の値と より大きい値が自動的にマッピングされます255255これは通常、カラー ピクセルを扱う場合に意図されています。

32 ビット値をunsigned chars にパックするときに実際に飽和ではなく切り捨てが必要な場合は、SSE がサチュレート パッキング命令しか提供しないため、これを自分で行う必要があります。しかし、これは簡単な方法で実現できます。

xmm0 = _mm_and_si128(xmm0, _mm_set1_epi32(0xFF));

上記の梱包手順の直前。これは、SSE 命令を 2 つだけ追加するか、多くのピクセルで償却する場合は 1 つだけ追加する必要があります。

于 2012-08-25T13:57:29.503 に答える