7

Intel [pdf]で説明されているアルゴリズムに従って、 pixmanに高速のx888->565ピクセル変換関数を実装しています。彼らのコードは、私が565に変換したいのに、x888-> 555に変換します。残念ながら、565に変換すると、上位ビットが設定されます。つまり、signed-saturationpack命令を使用できません。unsigned pack命令は、SSE4.1まで追加されませんでした。SSE2でその機能を実装するか、これを行う別の方法を見つけたいと思います。packusdw

この関数は、それぞれ4つの32ビットピクセルを含む2つのXMMレジスタを受け取り、8つの変換されたRGB565ピクセルを含む1つのXMMレジスタを出力します。

static force_inline __m128i
pack_565_2packedx128_128 (__m128i lo, __m128i hi)
{
    __m128i rb0 = _mm_and_si128 (lo, mask_565_rb);
    __m128i rb1 = _mm_and_si128 (hi, mask_565_rb);

    __m128i t0 = _mm_madd_epi16 (rb0, mask_565_pack_multiplier);
    __m128i t1 = _mm_madd_epi16 (rb1, mask_565_pack_multiplier);

    __m128i g0 = _mm_and_si128 (lo, mask_green);
    __m128i g1 = _mm_and_si128 (hi, mask_green);

    t0 = _mm_or_si128 (t0, g0);
    t1 = _mm_or_si128 (t1, g1);

    t0 = _mm_srli_epi32 (t0, 5);
    t1 = _mm_srli_epi32 (t1, 5);

    /* XXX: maybe there's a way to do this relatively efficiently with SSE2? */
    return _mm_packus_epi32 (t0, t1);
}

私が考えたアイデア:

  • 0x8000を減算_mm_packs_epi32し、各565ピクセルに0x8000を再度追加します。私はこれを試しましたが、これを機能させることができません。

      t0 = _mm_sub_epi16 (t0, mask_8000);
      t1 = _mm_sub_epi16 (t1, mask_8000);
      t0 = _mm_packs_epi32 (t0, t1);
      return _mm_add_epi16 (t0, mask_8000);
    
  • データをパックする代わりにシャッフルします。MMXで機能しますが、SSE 16ビットシャッフルは高または​​低64ビットでのみ機能するため、面倒になります。

  • 上位ビットを保存し、それらをゼロに設定し、パックを実行し、後でそれらを復元します。かなり散らかっているようです。

これを行うことができる他の(うまくいけばより効率的な)方法はありますか?

4

1 に答える 1

8

最初に値を符号拡張してから、次を使用できます_mm_packs_epi32

t0 = _mm_slli_epi32 (t0, 16);
t0 = _mm_srai_epi32 (t0, 16);
t1 = _mm_slli_epi32 (t1, 16);
t1 = _mm_srai_epi32 (t1, 16);
t0 = _mm_packs_epi32 (t0, t1);

これを前のシフトと実際に組み合わせて、2つの命令を保存することができます。

t0 = _mm_slli_epi32 (t0, 16 - 5);
t0 = _mm_srai_epi32 (t0, 16);
t1 = _mm_slli_epi32 (t1, 16 - 5);
t1 = _mm_srai_epi32 (t1, 16);
t0 = _mm_packs_epi32 (t0, t1);
于 2012-06-14T07:06:15.317 に答える