2012-06-13 35 views
6

Tôi đang triển khai chức năng chuyển đổi x888 -> 565 pixel nhanh trong pixman theo thuật toán được mô tả by Intel [pdf]. Mã của họ chuyển đổi x888 -> 555 trong khi tôi muốn chuyển đổi thành 565. Thật không may, chuyển đổi thành 565 có nghĩa là bit cao được thiết lập, có nghĩa là tôi không thể sử dụng các lệnh đóng gói bão hòa đã ký. Hướng dẫn gói chưa ký, packusdw chưa được thêm cho đến SSE4.1. Tôi muốn thực hiện chức năng của nó với SSE2 hoặc tìm cách khác để thực hiện điều này.Mô phỏng chức năng packusdw với SSE2

Chức năng này cần hai thanh ghi XMM chứa 4 pixel 32 bit mỗi đầu ra và một thanh ghi XMM đơn chứa 8 pixel RGB565 đã chuyển đổi.

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); 
} 

Ý tưởng Tôi đã nghĩ đến việc:

  • Trừ 0x8000, _mm_packs_epi32, tái thêm 0x8000 cho mỗi 565 pixel. Tôi đã thử điều này, nhưng tôi không thể thực hiện công việc này.

    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); 
    
  • Trộn dữ liệu thay vì đóng gói. Làm việc cho MMX, nhưng kể từ khi SSE 16-bit shuffles làm việc trên chỉ cao hoặc thấp 64-bit, nó sẽ nhận được lộn xộn.

  • Lưu các bit cao, đặt chúng về 0, thực hiện gói, khôi phục chúng sau đó. Có vẻ khá lộn xộn.

Có cách nào khác (hy vọng hiệu quả hơn) tôi có thể thực hiện việc này không?

Trả lời

5

Bạn có thể đăng ký mở rộng các giá trị đầu tiên và sau đó sử dụng _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); 

Bạn thực sự có thể kết hợp với sự thay đổi trước để cứu hai hướng dẫn:

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); 
+1

Perfect! Cảm ơn rất nhiều. Tôi nghi ngờ nó có thể được thực hiện hiệu quả hơn. – mattst88

Các vấn đề liên quan