एसएसई 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
क्या आप वाकई अपने वीडियो स्रोत आपको लगता है कि अतिरिक्त गद्दी बाइट देने के लिए कॉन्फ़िगर नहीं कर सकता हैं? –
काफी, मैटी। जो बहुत दुर्भाग्यपूर्ण है, मैं सहमत हूं। :( – Clippy