struct FillStruct
{
DWORD *ScanLinePtr; // pointer to scanline
float *ZBufferPtr; // pointer to zbuffer
long x[2]; // fixedpoint values for
// left and right x
long c[4]; // color of the first pixel of
// the left side
long dc[4]; // dcolor/dx of color
float z; // zvalue of the left pixel
float dz; // dz/dx
};
long start_x, end_x;
long slc[4];
float slz;
int lpitch, scr_width,scr_height;
FillStruct xfs;
#define SLPTR 0
#define ZBPTR 4
#define X 8
#define C 16
#define DC 32
#define Z 48
#define DZ 52
void _declspec(naked) DrawScanline2_asm()
{
_asm
{
push esi
push edi
push ebx
mov eax, 08000h
mov edx, 10000h
mov ebx, [xfs + X]
cmp bx, ax
jb DSL2_NOINC1
add ebx, edx // eax ebx ecx edx
DSL2_NOINC1: // na startx na na
mov ecx, [xfs + X + 4]
cmp cx, ax
jb DSL2_NOINC2
add ecx, edx
// na startx endx na
DSL2_NOINC2:
sar ebx, 16
sar ecx, 16
mov edx, dword ptr [scr_width]
test ecx, ecx
jle DSL2_EXIT
cmp ebx, edx
jge DSL2_EXIT
cmp ecx, edx // na startx endx screen_width
jle DSL2_OVER1
mov ecx, edx
DSL2_OVER1:
test ebx, ebx
jge DSL2_OVER2
xor ebx, ebx
DSL2_OVER2: // na startx endx screen_width
sub ecx, ebx // na startx width sw
test ecx, ecx
jle DSL2_EXIT
fld [xfs + DZ]// eax ebx ecx edx
shl ebx, 2 // na startx*4 width sw
mov edi, [xfs + ZBPTR]
mov esi, [xfs + SLPTR]
//eax ebx ecx edx edi esi
//na startx*4 width sw zbptr slptr
add edi, ebx
add esi, ebx
cmp ecx, 32
jle DSL2_NOPREFETCH
/* prefetc screen and zbuffer */
mov eax, ecx
shr eax, 5
shl eax, 5
DSL2_PRELOOP:
mov ebx, [edi + eax]
mov ebx, [esi + eax]
sub eax, 32
test eax, eax
jge DSL2_PRELOOP
DSL2_NOPREFETCH:
fld [xfs + Z]
mov eax, [xfs + C]
mov ebx, [xfs + C + 4]
mov edx, [xfs + C + 8]
mov [slc], eax
mov [slc + 4], ebx
mov [slc + 8], edx
fld dword ptr [edi] //na na width na zbptr slcptr
st0: z st1: dz
DSL2_MAINLOOP:
fcomp st(1)
fnstsw ax
test ah,5
jp DSL2_END
fst dword ptr [edi]
mov eax, dword ptr [slc]
mov al, byte ptr [slc + 10]
mov ah, byte ptr [slc + 6]
mov dword ptr [esi], eax
DSL2_END:
dec ecx
jz DSL2_EXIT2
fadd st(0), st(1)
mov eax, [xfs + DC]
add edi, 4
mov ebx, [xfs + DC + 4]
add [slc], eax
fld dword ptr [edi]
add esi, 4
mov edx, [xfs + DC + 8]
add [slc + 4], ebx
add [slc + 8], edx
jmp DSL2_MAINLOOP
DSL2_EXIT2:
fstp st(0)
fstp st(0)
DSL2_EXIT:
pop ebx
pop edi
pop esi
ret
}
}
Next step would be to use MMX, but I don't know how I should do the z comparison. I know I can't mix MMX and floating point code. How should I do the z comparison with MMX?
edit: Tried to make the code look more readable.
edit: retry
[edited by - Atm97fin on March 3, 2004 6:18:18 AM]
[edited by - Atm97fin on March 3, 2004 7:00:50 AM]
🔒 Locked
Let's optimize my sub loop.
Advertisement
Original Post
Hi!
I've made my own software gourand shading 3d renderer with zbuffering.
Now I should optimize it, but I'm not very good at it.
So here's the code:
Well rather than using floats for the Z-Buffer, you could always use integers for that. And then MMX would be the best way to optimize... I might look at your current code later though, just don't have time right now.
[edited by - Puzzler183 on March 3, 2004 6:43:07 AM]
[edited by - Puzzler183 on March 3, 2004 6:43:07 AM]
Hmm...
There''s error in the prefetch loop. It works with pixels rather than bytes.
Should be:
There''s error in the prefetch loop. It works with pixels rather than bytes.
Should be:
cmp ecx, 8
jle DSL2_NOPREFETCH
/* prefetc screen and zbuffer */
mov eax, ecx
shr eax, 3
shl eax, 5
DSL2_PRELOOP:
mov ebx, [edi + eax]
mov ebx, [esi + eax]
sub eax, 32
test eax, eax
jge DSL2_PRELOOP
Only thing that jumps at me is the floating point compare / branch. The fcomi instruction is significantly faster, but requires PPro+ (that excludes AMD K6). Is the branch well-predicted? If not, advance the destination pointer only if the condition is true, and write to it regardless (no more branch).
Those are points about your asm code, not its intent. Post the HLL source for that - I think it''s easier to optimize from there. Doing so is a one-way transformation
Those are points about your asm code, not its intent. Post the HLL source for that - I think it''s easier to optimize from there. Doing so is a one-way transformation
Here''s the high lvl language code:
inline void IntFixed(long &fixed)
{
if ((fixed&0x0000ffff) >= IHALF)
fixed = (fixed>>16) + 1;
else
fixed = (fixed>>16);
}
#define FIXED3_TO_COLOR(r,g,b) ((DWORD)(((b & 0x00ff0000) >> 16) | ((g & 0x00ff0000) >> 8) | (r & 0x00ff0000)))
void DrawScanline()
{
start_x = xfs.x[ILEFT];
end_x = xfs.x[IRIGHT];
float *zb = xfs.ZBufferPtr;
DWORD *sl = xfs.ScanLinePtr;
IntFixed(start_x);
IntFixed(end_x);
if (start_x < scr_width && end_x > 0)
{
if (start_x < 0)
start_x = 0;
if (end_x > scr_width)
end_x = scr_width;
end_x = end_x - start_x;
if (end_x <= 0)
return;
*slc = *xfs.c;
*(slc + 1) = *(xfs.c + 1);
*(slc + 2) = *(xfs.c + 2);
slz = xfs.z;
zb += start_x;
sl += start_x;
while(end_x-- > 0)
{
if (*(zb) < slz)
{
*(sl) = FIXED3_TO_COLOR(slc[R],slc[G],slc[B]);
*(zb) = slz;
}
sl++;
zb++;
slz += xfs.dz;
slc[R] += xfs.dc[R];
slc[G] += xfs.dc[G];
slc[B] += xfs.dc[B];
}
}
}
(Very little time ATM, sorry).
Fix the compare and branch, and it''s looking pretty good already.
I would also recommend going MMX though: you could do 2 pixels at a time (both with the conditional write trick); adding color gradient would be one instruction with saturating adds (no carry into other fields). That makes the fcomi issue irrelevant, BTW
Fix the compare and branch, and it''s looking pretty good already.
I would also recommend going MMX though: you could do 2 pixels at a time (both with the conditional write trick); adding color gradient would be one instruction with saturating adds (no carry into other fields). That makes the fcomi issue irrelevant, BTW
Sorry Jan, I don't understand how you meant me to fix the branch, but I made this:
That didn't seem to be any faster on my computer.
About the MMX version, the problem i'm facing is the construction of color dwords. I should make 00rrggbb00rrggbb into one mmx register from 2 mmx registers.
Should I use packing?.
If I have 2* mmr with 0000rr..gg..bb.. , I could shift it to the right and the pack it with packuswb.
Sorry, I'm bad with the conditional write trick. If I have both z values in one mm register and I compare with it the pcmpqt and I get the masks. Then I should somehow get the right color value somehow with logical operations?
[edited by - Atm97fin on March 4, 2004 12:52:25 PM]
[edited by - Atm97fin on March 4, 2004 1:03:22 PM]
DSL2_MAINLOOP:
fld dword ptr [edi]
mov eax, dword ptr [slc]
mov al, byte ptr [slc + 10]
mov ah, byte ptr [slc + 6]
fcomi st(0), st(1)
fcmovnbe st(0), st(1)
cmovnbe eax, dword ptr [esi]
fstp dword ptr [edi]
mov dword ptr [esi], eax
dec ecx
jz DSL2_EXIT2
fadd st(0), st(1)
mov eax, [xfs + DC]
add edi, 4
mov ebx, [xfs + DC + 4]
add [slc], eax
add esi, 4
mov edx, [xfs + DC + 8]
add [slc + 4], ebx
add [slc + 8], edx
jmp DSL2_MAINLOOP
That didn't seem to be any faster on my computer.
About the MMX version, the problem i'm facing is the construction of color dwords. I should make 00rrggbb00rrggbb into one mmx register from 2 mmx registers.
Should I use packing?.
If I have 2* mmr with 0000rr..gg..bb.. , I could shift it to the right and the pack it with packuswb.
Sorry, I'm bad with the conditional write trick. If I have both z values in one mm register and I compare with it the pcmpqt and I get the masks. Then I should somehow get the right color value somehow with logical operations?
[edited by - Atm97fin on March 4, 2004 12:52:25 PM]
[edited by - Atm97fin on March 4, 2004 1:03:22 PM]
Here's what I came up with MMX.
edit: damn forum, messing my precious comments
[edited by - Atm97fin on March 4, 2004 3:08:39 PM]
[edited by - Atm97fin on March 4, 2004 3:10:20 PM]
DSLMMX_MAINLOOP: // mm0 mm1 mm2 mm3 mm4 mm5 mm6 mm7
// z dz c1 c2
movq MM4, MM6// c1 z dz c1 c2
psrlw MM4, 8 // c1>>8 z dz c1 c2
movq MM3, MM7//
psrlw MM3, 8 // c2>>8 c1>>8 z dz c1 c2
packuswb MM4, MM3// packed z dz c1 c2
movq MM0, qword ptr [edi]
movq MM2, MM0// zbuf zbuf packed z dz c1 c2
pcmpgtd MM0, MM4// mask zbuf packed z dz c1 c2
movq MM1, qword ptr [esi]
// mask scrc zbuf packed z dz c1 c2
pand MM1, MM0// mask c_int1
pandn MM3, MM0// c_int2
por MM1, MM3// mask c_final zbuf cint z dz c1 c2
movq qword ptr [esi], MM1
// mask zbuf z dz c1 c2
pand MM2, MM0// mask z_int1 z dz c1 c2
movq MM3, MM4//
pandn MM3, MM0// mask z_int2 z_int1 z dz c1 c2
por MM2, MM3// mask z_final
movq qword ptr [edi], MM2
// z dz c1 c2
paddd MM4, MM5
paddw MM6, dc1
paddw MM7, dc2
add edi, 8
add esi, 8
dec ecx
jnz DSLMMX_MAINLOOP // 25 instructions / 2 pixels = 12.5 i/p
edit: damn forum, messing my precious comments
[edited by - Atm97fin on March 4, 2004 3:08:39 PM]
[edited by - Atm97fin on March 4, 2004 3:10:20 PM]
and now you can do it all again with sse2.
isn''t asm level optimization fun?
also you could possibly optimize it more by prefetching the data.
and before you go insane and write different versions for all the different processors, i suggest you just get some optimizing compiler, and see just how much faster your code actually is.
isn''t asm level optimization fun?
also you could possibly optimize it more by prefetching the data.
and before you go insane and write different versions for all the different processors, i suggest you just get some optimizing compiler, and see just how much faster your code actually is.
quote:
Original post by aboeing
and now you can do it all again with sse2.
isn''t asm level optimization fun?
also you could possibly optimize it more by prefetching the data.
and before you go insane and write different versions for all the different processors, i suggest you just get some optimizing compiler, and see just how much faster your code actually is.
Well Im in the middle of complete speed measurements, but it seems that the first version is about 5-10 % faster than the compiler version. I use C++ Visual studio 6.0 professional.
If you didn''t notice, there is prefetch loop.
I can''t optimize it for SSE2, my cpu doesn''t support it. :-<
Luckily there is other parts in my code that I can optimize.
Topic Locked
This topic has been locked by a moderator. New replies are not allowed.
Sign in to reply to this topic.
Advertisement