Batze hat geschrieben:
Ist dein Assemblercode zu diesem Filter irgendwo verfügbar? Ist ja vielleicht ganz interessant wie das so implementiert ist.
Auf jeden Fall werde ich mir mal was zu SSE angucken, wenn das doch so viel rausholen kann, würde sich das ja lohnen, sich damit mal zu beschäftigen.
Ich hab die PNG Filter für mein Bildformat implementiert.
Hier der Up Filter:
PB-Implementation
Code: Alles auswählen
Protected.i X, ByteSize
Protected *ActualChannel.Tsi_Pixel_Channel
Protected *PriorChannel.Tsi_Pixel_Channel
*PriorChannel = *ImageData
*ActualChannel = *ImageData + Width * 4
Height - 1
ByteSize = Width * Height * 4
For X = 1 To ByteSize
*ActualChannel\Channel = *ActualChannel\Channel + *PriorChannel\Channel
*ActualChannel + 1
*PriorChannel + 1
Next
Asm-Implementation:
Code: Alles auswählen
!push esi
!push edi
!mov eax,[p.v_Height+8]
!dec eax
!mul dword[p.v_Width+8]
!mov ecx,eax
!mov edi,[p.p_ImageData+8]
!mov esi,edi
!mov edx,[p.v_Width+8]
!shl edx,2
!add edi,edx
!align 4
!Tsi_UnFilterUp_LoopStart:
!mov al,[edi]
!add al,[esi]
!mov [edi],al
!mov al,[edi+1]
!add al,[esi+1]
!mov [edi+1],al
!mov al,[edi+2]
!add al,[esi+2]
!mov [edi+2],al
!mov al,[edi+3]
!add al,[esi+3]
!mov [edi+3],al
!add esi,4
!add edi,4
!dec ecx
!jne Tsi_UnFilterUp_LoopStart
!pop edi
!pop esi
MMX-Implementation:
Code: Alles auswählen
;save registers
!push esi
!push edi
;calculate the counters
!mov eax,[p.v_Height+8]
!dec eax
!mul dword[p.v_Width+8]
!mov ecx,eax
!shr ecx,4
!and eax,15
;calculate the pointers
!mov edi,[p.p_ImageData+8]
!mov esi,edi
!mov edx,[p.v_Width+8]
!shl edx,2
!add edi,edx
;process a part of the data to cut the length to a multiple of 64
!test eax,eax
!je Tsi_UnFilterUp_MmxCutLengthEnd
!align 4
!Tsi_UnFilterUp_MmxCutLengthStart:
!movd mm0,[esi]
!movd mm1,[edi]
!paddb mm0,mm1
!movd [edi],mm0
!add esi,4
!add edi,4
!dec eax
!jne Tsi_UnFilterUp_MmxCutLengthStart
!align 4
!Tsi_UnFilterUp_MmxCutLengthEnd:
;process the rest of the data
!test ecx,ecx
!je Tsi_UnFilterUp_MmxLoopEnd
!align 4
!Tsi_UnFilterUp_MmxLoopStart:
!movq mm0,[esi]
!movq mm1,[esi+8]
!movq mm2,[esi+16]
!movq mm3,[esi+24]
!movq mm4,[esi+32]
!movq mm5,[esi+40]
!movq mm6,[esi+48]
!movq mm7,[esi+56]
!paddb mm0,[edi]
!paddb mm1,[edi+8]
!paddb mm2,[edi+16]
!paddb mm3,[edi+24]
!paddb mm4,[edi+32]
!paddb mm5,[edi+40]
!paddb mm6,[edi+48]
!paddb mm7,[edi+56]
!movq [edi],mm0
!movq [edi+8],mm1
!movq [edi+16],mm2
!movq [edi+24],mm3
!movq [edi+32],mm4
!movq [edi+40],mm5
!movq [edi+48],mm6
!movq [edi+56],mm7
!add esi,64
!add edi,64
!dec ecx
!jne Tsi_UnFilterUp_MmxLoopStart
!align 4
!Tsi_UnFilterUp_MmxLoopEnd:
;restore the registers
!pop edi
!pop esi
;end MMX state
!emms
SSE2-Implementation:
Code: Alles auswählen
;save registers
!push esi
!push edi
;calculate the counters
!mov eax,[p.v_Height+8]
!dec eax
!mul dword[p.v_Width+8]
!mov ecx,eax
!shr ecx,5
!and eax,31
;calculate the pointers
!mov edi,[p.p_ImageData+8]
!mov esi,edi
!mov edx,[p.v_Width+8]
!shl edx,2
!add edi,edx
;process a part of the data to cut the length to a multiple of 128
!test eax,eax
!je Tsi_UnFilterUp_Sse2CutLengthEnd
!align 4
!Tsi_UnFilterUp_Sse2CutLengthStart:
!movd mm0,[esi]
!movd mm1,[edi]
!paddb mm0,mm1
!movd [edi],mm0
!add esi,4
!add edi,4
!dec eax
!jne Tsi_UnFilterUp_Sse2CutLengthStart
!align 4
!Tsi_UnFilterUp_Sse2CutLengthEnd:
;process the rest of the data
!test ecx,ecx
!je Tsi_UnFilterUp_Sse2LoopEnd
!align 4
!Tsi_UnFilterUp_Sse2LoopStart:
!movdqu xmm0,[esi]
!movdqu xmm1,[esi+16]
!movdqu xmm2,[esi+32]
!movdqu xmm3,[esi+48]
!movdqu xmm4,[esi+64]
!movdqu xmm5,[esi+80]
!movdqu xmm6,[esi+96]
!movdqu xmm7,[esi+112]
!paddb xmm0,[edi]
!paddb xmm1,[edi+16]
!paddb xmm2,[edi+32]
!paddb xmm3,[edi+48]
!paddb xmm4,[edi+64]
!paddb xmm5,[edi+80]
!paddb xmm6,[edi+96]
!paddb xmm7,[edi+112]
!movdqu [edi],xmm0
!movdqu [edi+16],xmm1
!movdqu [edi+32],xmm2
!movdqu [edi+48],xmm3
!movdqu [edi+64],xmm4
!movdqu [edi+80],xmm5
!movdqu [edi+96],xmm6
!movdqu [edi+112],xmm7
!add esi,128
!add edi,128
!dec ecx
!jne Tsi_UnFilterUp_Sse2LoopStart
!align 4
!Tsi_UnFilterUp_Sse2LoopEnd:
;restore the registers
!pop edi
!pop esi
;end MMX state
!emms
Die MMX und SSE2 Implementationen sehen auf den ersten Blick etwas verwirrend aus.
Der Trick dabei ist halt das man Instruktionen hat, die eine Operation auf mehrere Daten anwenden in nur einem Tacktzyklus: das SIMD Prinzip.
Dafür hast du bei MMX die 64bit breiten MMX-Register und bei SSE die 128bit breiten XMM-Register. Das kommende AVX wird die 256bit breiten YMM-Register einführen.
Nun das ganze funktioniert so indem man mit sogenannten gepackten Daten arbeitet. Damit ist gemeint das ein Register mehrere Daten enthalten kann. Ein MMX-Register kann 8 Bytes oder 4 Words oder 2 DoubleWords oder 2 Floats enthalten. Nun liefert MMX dir Instruktionen, welche die MMX-Register miteinander verrechnen können und dabei die einzelnen Daten beachten anstatt das komplette Register.
z.b. paddb steht für "Packed Addition Byte"
Das heisst mit der Instruktion: paddb mm0,mm1
Werden die einzelnen Bytes des mm0 Registers mit den einzelnen Bytes des mm1 Registers Addiert und im mm0 Register abgelegt.
Das heisst es werden 8 Additionen auf einen Schlag ausgeführt.