2012-04-02 77 views
3

此問題與以前回答的問題有關: Fast 24-bit array -> 32-bit array conversion? 在一個答案中,interjay寄出了用於轉換RGB24-> RGB32的SSE3代碼,但是我也需要反向轉換(RGB32-> RGB24) 。我給它一個鏡頭(見下文),我的代碼可以工作,但它比interjay的代碼更復雜,而且顯着更慢。我看不出如何完全顛倒指令:_mm_alignr_epi8在這種情況下似乎沒有幫助,但我對SSE3並不熟悉。不對稱是不可避免的,還是有更快的替代和換位?快速的32位數組 - > SSE3中的24位數組轉換? (RGB32 - > RGB24)

RGB32 - > RGB24:

__m128i *src = ... 
__m128i *dst = ... 
__m128i mask = _mm_setr_epi8(0,1,2,4, 5,6,8,9, 10,12,13,14, -1,-1,-1,-1); 
for (UINT i = 0; i < Pixels; i += 16) { 
    __m128i sa = _mm_shuffle_epi8(_mm_load_si128(src), mask); 
    __m128i sb = _mm_shuffle_epi8(_mm_load_si128(src + 1), mask); 
    __m128i sc = _mm_shuffle_epi8(_mm_load_si128(src + 2), mask); 
    __m128i sd = _mm_shuffle_epi8(_mm_load_si128(src + 3), mask); 
    _mm_store_si128(dst, _mm_or_si128(sa, _mm_slli_si128(sb, 12))); 
    _mm_store_si128(dst + 1, _mm_or_si128(_mm_srli_si128(sb, 4), _mm_slli_si128(sc, 8))); 
    _mm_store_si128(dst + 2, _mm_or_si128(_mm_srli_si128(sc, 8), _mm_slli_si128(sd, 4))); 
    src += 4; 
    dst += 3; 
} 

RGB24 - > RGB32(禮貌interjay):

__m128i *src = ... 
__m128i *dst = ... 
__m128i mask = _mm_setr_epi8(0,1,2,-1, 3,4,5,-1, 6,7,8,-1, 9,10,11,-1); 
for (UINT i = 0; i < Pixels; i += 16) { 
    __m128i sa = _mm_load_si128(src); 
    __m128i sb = _mm_load_si128(src + 1); 
    __m128i sc = _mm_load_si128(src + 2); 
    __m128i val = _mm_shuffle_epi8(sa, mask); 
    _mm_store_si128(dst, val); 
    val = _mm_shuffle_epi8(_mm_alignr_epi8(sb, sa, 12), mask); 
    _mm_store_si128(dst + 1, val); 
    val = _mm_shuffle_epi8(_mm_alignr_epi8(sc, sb, 8), mask); 
    _mm_store_si128(dst + 2, val); 
    val = _mm_shuffle_epi8(_mm_alignr_epi8(sc, sc, 4), mask); 
    _mm_store_si128(dst + 3, val); 
    src += 3; 
    dst += 4; 
} 
+0

那麼,沒有SSE4.1允許? – harold 2012-04-02 22:05:16

+0

您只需在4個輸入寄存器上使用6個掩碼將它們轉換爲3個輸出寄存器。你不能繞過三個'或',因爲'pshufb'設置一個字節爲0或者由掩碼索引的值。 – hirschhornsalz 2012-04-02 22:14:48

回答

0

您可以this answer和更改洗牌面具從RGB32去RGB24。

最大的區別是直接計算洗牌,並使用按位操作來避免轉換。另外,使用對齊的流式寫入而不是對齊的寫入不會污染緩存。

0

老問題,但我試圖解決同樣的問題,所以...

您可以使用palignr如果右對齊它的第二個操作數,即置零的低字節。您需要第二個,第三個和第四個單詞的左對齊版本,以及第一個,第二個和第三個單詞的右對齊版本。

對於第二個和第三個單詞,如果我使用shift來計算左對齊的右對齊版本,GCC會稍微高興一些。如果我使用兩個不同的pshufb,它會產生3個不必要的移動。

這是代碼。它正好使用8個寄存器;如果您處於64位模式,則可以嘗試將其展開兩次。

__m128i mask_right = _mm_set_epi8(14, 13, 12, 10, 9, 8, 6, 5, 4, 2, 1, 0, 0x80, 0x80, 0x80, 0x80); 
    __m128i mask = _mm_set_epi8(0x80, 0x80, 0x80, 0x80, 14, 13, 12, 10, 9, 8, 6, 5, 4, 2, 1, 0); 

    for (; n; n -= 16, d += 48, s += 64) { 
      __m128i v0 = _mm_load_si128((__m128i *) &s[0]); 
      __m128i v1 = _mm_load_si128((__m128i *) &s[16]); 
      __m128i v2 = _mm_load_si128((__m128i *) &s[32]); 
      __m128i v3 = _mm_load_si128((__m128i *) &s[48]); 

      v0 = _mm_shuffle_epi8(v0, mask_right); 
      v1 = _mm_shuffle_epi8(v1, mask); 
      v2 = _mm_shuffle_epi8(v2, mask); 
      v3 = _mm_shuffle_epi8(v3, mask); 

      v0 = _mm_alignr_epi8(v1, v0, 4); 
      v1 = _mm_slli_si128(v1, 4);  // mask -> mask_right 
      v1 = _mm_alignr_epi8(v2, v1, 8); 
      v2 = _mm_slli_si128(v2, 4);  // mask -> mask_right 
      v2 = _mm_alignr_epi8(v3, v2, 12); 

      _mm_store_si128((__m128i *) &d[0], v0); 
      _mm_store_si128((__m128i *) &d[16], v1); 
      _mm_store_si128((__m128i *) &d[32], v2); 
    } 

中央部分也可能是這樣寫的。編譯器只產生一個指令,看起來它有更多的並行性,但需要基準來給出正確答案:

  v0 = _mm_shuffle_epi8(v0, mask_right); 
      v1 = _mm_shuffle_epi8(v1, mask); 
      v2 = _mm_shuffle_epi8(v2, mask_right); 
      v3 = _mm_shuffle_epi8(v3, mask); 

      __m128i v2l = v2; 
      v0 = _mm_alignr_epi8(v1, v0, 4); 
      v1 = _mm_slli_si128(v1, 4);    // mask -> mask_right 
      v2 = _mm_alignr_epi8(v3, v2, 12); 
      v2l = _mm_srli_si128(v2l, 4);   // mask_right -> mask 
      v1 = _mm_alignr_epi8(v2l, v1, 8); 
相關問題