2010-06-04 20 views
12

त्वरित सारांश:फास्ट 24-बिट सरणी -> 32-बिट सरणी रूपांतरण?

मैं 24 बिट मूल्यों की एक सरणी है। 32-बिट तत्वों में व्यक्तिगत 24-बिट सरणी तत्वों को त्वरित रूप से विस्तारित करने के तरीके पर कोई सुझाव?

विवरण:

मैं DirectX 10 में पिक्सेल shaders का उपयोग एक ठोकर ब्लॉक कि मेरे फ्रेम 24 बिट पिक्सल के साथ कब्जा हार्डवेयर से आ रहे हैं (है वास्तविक समय में आने वाली वीडियो फ्रेम प्रसंस्करण कर रहा हूँ या तो वाईयूवी या आरजीबी छवियों के रूप में), लेकिन डीएक्स 10 32-बिट पिक्सेल बनावट लेता है। इसलिए, मुझे 24-बिट मानों को 32-बिट्स तक विस्तारित करना होगा इससे पहले कि मैं उन्हें GPU में लोड कर सकूं।

मुझे वास्तव में परवाह नहीं है कि मैंने शेष 8 बिट्स को सेट किया है, या जहां आने वाले 24-बिट 32-बिट मान में हैं - मैं इसे पिक्सेल शेडर में ठीक कर सकता हूं। लेकिन मुझे 24-बिट से 32-बिट वास्तव में से रूपांतरण करने की आवश्यकता है।

मैं सिम एसएसई संचालन से बहुत परिचित नहीं हूं, लेकिन मेरी सरसरी नज़र से ऐसा नहीं लगता है कि मैं उनका उपयोग करके विस्तार कर सकता हूं, क्योंकि मेरे पढ़ने और लिखने के समान आकार नहीं हैं। कोई सुझाव? या मैं इस डेटा सेट अनुक्रमिक रूप से मालिश कर रहा हूँ?

यह बहुत मूर्खतापूर्ण लगता है - मैं समांतरता के लिए पिक्सेल शेडर्स का उपयोग कर रहा हूं, लेकिन मुझे इससे पहले अनुक्रमिक प्रति-पिक्सेल ऑपरेशन करना होगा। I कुछ स्पष्ट याद आ रहा है ...

+0

क्या आप वाकई अपने वीडियो स्रोत आपको लगता है कि अतिरिक्त गद्दी बाइट देने के लिए कॉन्फ़िगर नहीं कर सकता हैं? –

+0

काफी, मैटी। जो बहुत दुर्भाग्यपूर्ण है, मैं सहमत हूं। :( – Clippy

उत्तर

20

नीचे दिया गया कोड बहुत तेज़ होना चाहिए। यह केवल 32-बिट पढ़ने/लिखने के निर्देशों का उपयोग करते हुए प्रत्येक पुनरावृत्ति में 4 पिक्सेल की प्रतिलिपि बनाता है। स्रोत और गंतव्य पॉइंटर्स को 32 बिट्स के साथ गठबंधन किया जाना चाहिए।

uint32_t *src = ...; 
uint32_t *dst = ...; 

for (int i=0; i<num_pixels; i+=4) { 
    uint32_t sa = src[0]; 
    uint32_t sb = src[1]; 
    uint32_t sc = src[2]; 

    dst[i+0] = sa; 
    dst[i+1] = (sa>>24) | (sb<<8); 
    dst[i+2] = (sb>>16) | (sc<<16); 
    dst[i+3] = sc>>8; 

    src += 3; 
} 

संपादित करें:

यहाँ SSSE3 निर्देश PSHUFB और PALIGNR का उपयोग कर यह करने के लिए एक तरीका है। कोड कंपाइलर इंट्रिनिक्स का उपयोग करके लिखा गया है, लेकिन यदि आवश्यक हो तो असेंबली में अनुवाद करना मुश्किल नहीं होना चाहिए। यह प्रत्येक पुनरावृत्ति में 16 पिक्सेल की प्रतिलिपि बनाता है। स्रोत और गंतव्य पॉइंटर्स को 16 बाइट्स के साथ गठबंधन किया जाना चाहिए, या यह गलती होगी। अगर वे गठबंधन नहीं हैं, तो आप इसे _mm_load_si128_mm_loadu_si128 और _mm_store_si128_mm_storeu_si128 के साथ बदलकर काम कर सकते हैं, लेकिन यह धीमा हो जाएगा।

#include <emmintrin.h> 
#include <tmmintrin.h> 

__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 (int i=0; i<num_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; 
} 

SSSE3 (SSE3 साथ भ्रमित होने की नहीं) एक अपेक्षाकृत नए प्रोसेसर की आवश्यकता होगी: कोर 2 या नए, और मेरा मानना ​​है कि एएमडी अभी तक इसका समर्थन नहीं करता। एसएसई 2 निर्देशों के साथ इसे करने से केवल बहुत अधिक परिचालन होंगे, और इसके लायक नहीं हो सकते हैं।

+0

वाह - धन्यवाद इंटरजे! यह जानना बहुत अच्छा है कि मुझे गलती हुई थी, और सिमड ऑपरेशंस उपलब्ध हैं जो मुझे चाहिए जो कर सकते हैं। और वह एसएसएसई 3 नमूना शानदार है! मेरे पास इस कोड के चल रहे प्लेटफ़ॉर्म पर पूर्ण नियंत्रण है, और कर सकते हैं हार्डवेयर विकल्पों को एसएसएसई 3-सक्षम प्रोसेस पर सीमित करें। – Clippy

+0

मुझे आश्चर्य है ... क्या पहला समाधान छोटे और बड़े एंडियन प्रोसेसर के लिए एक अंतर बनाता है? –

+0

@TheQuantumPhysicist यह थोड़ा-अंतिया प्रोसेसर मानता है। वही विचार काम करेगा बड़े-एंडियन, लेकिन बदलावों को बदलने की जरूरत है। – interjay

1

विभिन्न इनपुट/आउटपुट आकार सिमड का उपयोग करने में बाधा नहीं हैं, केवल एक गति टक्कर। आपको डेटा को तोड़ने की आवश्यकता होगी ताकि आप पूर्ण सिमड शब्दों (16 बाइट्स) में पढ़ और लिख सकें।

इस मामले में, आप 3 सिमड शब्द (48 बाइट्स == 16 आरजीबी पिक्सल) पढ़ेंगे, विस्तार करें, फिर 4 सिमड शब्द लिखें।

मैं सिर्फ तुम कह रहा हूँ उपयोग SIMD, मैं तुम्हें यह नहीं कह रहा हूँ कर सकते हैं चाहिए।मध्यम बिट, विस्तार, अभी भी मुश्किल है क्योंकि आपके पास शब्द के विभिन्न हिस्सों में गैर-समान शिफ्ट आकार हैं।

+0

धन्यवाद मार्क - यह जानना बहुत अच्छा है कि मुझे गलती हुई थी, और सिम ऑपरेशंस उपलब्ध हैं जो मुझे आवश्यकतानुसार कर सकते हैं। यह तेज़ नहीं हो सकता है, लेकिन यह सुनिश्चित करने के लिए मुझे देखकर लायक है। :) – Clippy

5

एसएसई 3 बहुत बढ़िया है, लेकिन जो लोग किसी भी कारण से इसका उपयोग नहीं कर सकते हैं, यहां x86 असेंबलर में रूपांतरण है, जो वास्तव में आपके द्वारा अनुकूलित है। पूर्णता के लिए, मैं दोनों दिशाओं में रूपांतरण देता हूं: आरजीबी 32-> आरजीबी 24 और आरजीबी 24-> आरजीबी 32।

ध्यान दें कि इंटरजे के सी कोड गंतव्य पिक्सल के एमएसबी (अल्फा चैनल) में कचरा छोड़ देता है। इससे कुछ अनुप्रयोगों में कोई फर्क नहीं पड़ता, लेकिन यह मेरे लिए महत्वपूर्ण है, इसलिए मेरा आरजीबी 24-> आरजीबी 32 कोड एमएसबी को शून्य पर मजबूर करता है। इसी तरह, मेरा आरजीबी 32-> आरजीबी 24 कोड एमएसबी को अनदेखा करता है; यदि स्रोत डेटा में गैर-शून्य अल्फा चैनल होता है तो यह कचरा आउटपुट से बचाता है। बेंचमार्क द्वारा सत्यापित किए गए अनुसार, इन सुविधाओं के प्रदर्शन के संदर्भ में लगभग कुछ भी लागत नहीं है।

आरजीबी 32-> आरजीबी 24 के लिए मैं वीसी ++ अनुकूलक को लगभग 20% तक हरा सकता था। आरजीबी 24-> आरजीबी 32 के लिए लाभ महत्वहीन था। बेंचमार्किंग i5 2500K पर किया गया था। मैं यहां बेंचमार्किंग कोड छोड़ देता हूं, लेकिन अगर कोई इसे चाहता है तो मैं इसे प्रदान करूंगा। सबसे महत्वपूर्ण अनुकूलन जितनी जल्दी हो सके स्रोत पॉइंटर को टक्कर दे रहा था (ASAP टिप्पणी देखें)। मेरा सबसे अच्छा अनुमान यह है कि यह निर्देश पाइपलाइन को जल्द से जल्द prefetch करने की अनुमति देकर समांतरता बढ़ जाती है। इसके अलावा मैंने निर्भरताओं को कम करने और बिट-बैशिंग के साथ मेमोरी एक्सेस ओवरलैप करने के लिए कुछ निर्देशों को फिर से दर्ज किया।

void ConvRGB32ToRGB24(const UINT *Src, UINT *Dst, UINT Pixels) 
{ 
#if !USE_ASM 
    for (UINT i = 0; i < Pixels; i += 4) { 
     UINT sa = Src[i + 0] & 0xffffff; 
     UINT sb = Src[i + 1] & 0xffffff; 
     UINT sc = Src[i + 2] & 0xffffff; 
     UINT sd = Src[i + 3]; 
     Dst[0] = sa | (sb << 24); 
     Dst[1] = (sb >> 8) | (sc << 16); 
     Dst[2] = (sc >> 16) | (sd << 8); 
     Dst += 3; 
    } 
#else 
    __asm { 
     mov  ecx, Pixels 
     shr  ecx, 2    // 4 pixels at once 
     jz  ConvRGB32ToRGB24_$2 
     mov  esi, Src 
     mov  edi, Dst 
ConvRGB32ToRGB24_$1: 
     mov  ebx, [esi + 4]  // sb 
     and  ebx, 0ffffffh  // sb & 0xffffff 
     mov  eax, [esi + 0]  // sa 
     and  eax, 0ffffffh  // sa & 0xffffff 
     mov  edx, ebx   // copy sb 
     shl  ebx, 24    // sb << 24 
     or  eax, ebx   // sa | (sb << 24) 
     mov  [edi + 0], eax  // Dst[0] 
     shr  edx, 8    // sb >> 8 
     mov  eax, [esi + 8]  // sc 
     and  eax, 0ffffffh  // sc & 0xffffff 
     mov  ebx, eax   // copy sc 
     shl  eax, 16    // sc << 16 
     or  eax, edx   // (sb >> 8) | (sc << 16) 
     mov  [edi + 4], eax  // Dst[1] 
     shr  ebx, 16    // sc >> 16 
     mov  eax, [esi + 12]  // sd 
     add  esi, 16    // Src += 4 (ASAP) 
     shl  eax, 8    // sd << 8 
     or  eax, ebx   // (sc >> 16) | (sd << 8) 
     mov  [edi + 8], eax  // Dst[2] 
     add  edi, 12    // Dst += 3 
     dec  ecx 
     jnz  SHORT ConvRGB32ToRGB24_$1 
ConvRGB32ToRGB24_$2: 
    } 
#endif 
} 

void ConvRGB24ToRGB32(const UINT *Src, UINT *Dst, UINT Pixels) 
{ 
#if !USE_ASM 
    for (UINT i = 0; i < Pixels; i += 4) { 
     UINT sa = Src[0]; 
     UINT sb = Src[1]; 
     UINT sc = Src[2]; 
     Dst[i + 0] = sa & 0xffffff; 
     Dst[i + 1] = ((sa >> 24) | (sb << 8)) & 0xffffff; 
     Dst[i + 2] = ((sb >> 16) | (sc << 16)) & 0xffffff; 
     Dst[i + 3] = sc >> 8; 
     Src += 3; 
    } 
#else 
    __asm { 
     mov  ecx, Pixels 
     shr  ecx, 2    // 4 pixels at once 
     jz  SHORT ConvRGB24ToRGB32_$2 
     mov  esi, Src 
     mov  edi, Dst 
     push ebp 
ConvRGB24ToRGB32_$1: 
     mov  ebx, [esi + 4]  // sb 
     mov  edx, ebx   // copy sb 
     mov  eax, [esi + 0]  // sa 
     mov  ebp, eax   // copy sa 
     and  ebx, 0ffffh   // sb & 0xffff 
     shl  ebx, 8    // (sb & 0xffff) << 8 
     and  eax, 0ffffffh  // sa & 0xffffff 
     mov  [edi + 0], eax  // Dst[0] 
     shr  ebp, 24    // sa >> 24 
     or  ebx, ebp   // (sa >> 24) | ((sb & 0xffff) << 8) 
     mov  [edi + 4], ebx  // Dst[1] 
     shr  edx, 16    // sb >> 16 
     mov  eax, [esi + 8]  // sc 
     add  esi, 12    // Src += 12 (ASAP) 
     mov  ebx, eax   // copy sc 
     and  eax, 0ffh   // sc & 0xff 
     shl  eax, 16    // (sc & 0xff) << 16 
     or  eax, edx   // (sb >> 16) | ((sc & 0xff) << 16) 
     mov  [edi + 8], eax  // Dst[2] 
     shr  ebx, 8    // sc >> 8 
     mov  [edi + 12], ebx  // Dst[3] 
     add  edi, 16    // Dst += 16 
     dec  ecx 
     jnz  SHORT ConvRGB24ToRGB32_$1 
     pop  ebp 
ConvRGB24ToRGB32_$2: 
    } 
#endif 
} 

और जब हम इसमें हैं, तो वास्तविक एसएसई 3 असेंबली में वही रूपांतरण हैं। यह केवल तभी काम करता है जब आपके पास एक असेंबलर (एफएएसएम मुक्त है) और एक सीपीयू है जो एसएसई 3 का समर्थन करता है (संभवतः यह जांचना बेहतर है)। ध्यान दें कि अंतर्निहित रूप से इस कुशलता को कुछ भी आउटपुट नहीं करते हैं, यह आपके द्वारा उपयोग किए जाने वाले टूल और आप किस प्लेटफ़ॉर्म के लिए संकलित कर रहे हैं, इस पर पूरी तरह से निर्भर करता है। यहां, यह सीधा है: आप जो देखते हैं वह आपको मिलता है। यह कोड उपरोक्त x86 कोड के समान आउटपुट उत्पन्न करता है, और यह लगभग 1.5x तेज (i5 2500K पर) है।

format MS COFF 

section '.text' code readable executable 

public _ConvRGB32ToRGB24SSE3 

; ebp + 8  Src (*RGB32, 16-byte aligned) 
; ebp + 12 Dst (*RGB24, 16-byte aligned) 
; ebp + 16 Pixels 

_ConvRGB32ToRGB24SSE3: 
    push ebp 
    mov  ebp, esp 
    mov  eax, [ebp + 8] 
    mov  edx, [ebp + 12] 
    mov  ecx, [ebp + 16] 
    shr  ecx, 4 
    jz  done1 
    movupd xmm7, [mask1] 

top1: 
    movupd xmm0, [eax + 0]  ; sa = Src[0] 
    pshufb xmm0, xmm7   ; sa = _mm_shuffle_epi8(sa, mask) 
    movupd xmm1, [eax + 16] ; sb = Src[1] 
    pshufb xmm1, xmm7   ; sb = _mm_shuffle_epi8(sb, mask) 
    movupd xmm2, xmm1   ; sb1 = sb 
    pslldq xmm1, 12   ; sb = _mm_slli_si128(sb, 12) 
    por  xmm0, xmm1   ; sa = _mm_or_si128(sa, sb) 
    movupd [edx + 0], xmm0  ; Dst[0] = sa 
    psrldq xmm2, 4    ; sb1 = _mm_srli_si128(sb1, 4) 
    movupd xmm0, [eax + 32] ; sc = Src[2] 
    pshufb xmm0, xmm7   ; sc = _mm_shuffle_epi8(sc, mask) 
    movupd xmm1, xmm0   ; sc1 = sc 
    pslldq xmm0, 8    ; sc = _mm_slli_si128(sc, 8) 
    por  xmm0, xmm2   ; sc = _mm_or_si128(sb1, sc) 
    movupd [edx + 16], xmm0 ; Dst[1] = sc 
    psrldq xmm1, 8    ; sc1 = _mm_srli_si128(sc1, 8) 
    movupd xmm0, [eax + 48] ; sd = Src[3] 
    pshufb xmm0, xmm7   ; sd = _mm_shuffle_epi8(sd, mask) 
    pslldq xmm0, 4    ; sd = _mm_slli_si128(sd, 4) 
    por  xmm0, xmm1   ; sd = _mm_or_si128(sc1, sd) 
    movupd [edx + 32], xmm0 ; Dst[2] = sd 
    add  eax, 64 
    add  edx, 48 
    dec  ecx 
    jnz  top1 

done1: 
    pop  ebp 
    ret 

public _ConvRGB24ToRGB32SSE3 

; ebp + 8  Src (*RGB24, 16-byte aligned) 
; ebp + 12 Dst (*RGB32, 16-byte aligned) 
; ebp + 16 Pixels 

_ConvRGB24ToRGB32SSE3: 
    push ebp 
    mov  ebp, esp 
    mov  eax, [ebp + 8] 
    mov  edx, [ebp + 12] 
    mov  ecx, [ebp + 16] 
    shr  ecx, 4 
    jz  done2 
    movupd xmm7, [mask2] 

top2: 
    movupd xmm0, [eax + 0]  ; sa = Src[0] 
    movupd xmm1, [eax + 16] ; sb = Src[1] 
    movupd xmm2, [eax + 32] ; sc = Src[2] 
    movupd xmm3, xmm0   ; sa1 = sa 
    pshufb xmm0, xmm7   ; sa = _mm_shuffle_epi8(sa, mask) 
    movupd [edx], xmm0   ; Dst[0] = sa 
    movupd xmm4, xmm1   ; sb1 = sb 
    palignr xmm1, xmm3, 12  ; sb = _mm_alignr_epi8(sb, sa1, 12) 
    pshufb xmm1, xmm7   ; sb = _mm_shuffle_epi8(sb, mask); 
    movupd [edx + 16], xmm1 ; Dst[1] = sb 
    movupd xmm3, xmm2   ; sc1 = sc 
    palignr xmm2, xmm4, 8  ; sc = _mm_alignr_epi8(sc, sb1, 8) 
    pshufb xmm2, xmm7   ; sc = _mm_shuffle_epi8(sc, mask) 
    movupd [edx + 32], xmm2 ; Dst[2] = sc 
    palignr xmm3, xmm3, 4  ; sc1 = _mm_alignr_epi8(sc1, sc1, 4) 
    pshufb xmm3, xmm7   ; sc1 = _mm_shuffle_epi8(sc1, mask) 
    movupd [edx + 48], xmm3 ; Dst[3] = sc1 
    add  eax, 48 
    add  edx, 64 
    dec  ecx 
    jnz  top2 

done2: 
    pop  ebp 
    ret 

section '.data' data readable writeable align 16 

label mask1 dqword 
    db 0,1,2,4, 5,6,8,9, 10,12,13,14, -1,-1,-1,-1 
label mask2 dqword 
    db 0,1,2,-1, 3,4,5,-1, 6,7,8,-1, 9,10,11,-1 
-1

SSE 4.1 .ASM:

PINSRD XMM0, DWORD PTR[ESI], 0 
PINSRD XMM0, DWORD PTR[ESI+3], 1 
PINSRD XMM0, DWORD PTR[ESI+6], 2 
PINSRD XMM0, DWORD PTR[ESI+9], 3 
PSLLD XMM0, 8      
PSRLD XMM0, 8 
MOVNTDQ [EDI], XMM1 
add  ESI, 12 
add  EDI, 16 
+2

यह इंटरजेज़ उत्तर के रूप में लगभग 4 गुना अक्षम है। – hirschhornsalz

संबंधित मुद्दे