Dark Bit Factory & Gravity

PROGRAMMING => C / C++ /C# => Topic started by: Tetra on March 28, 2007

Title: Dont leave me behind :P
Post by: Tetra on March 28, 2007
So a lot have been playing about with c++ and I thought its about time I have a go too  ;D

Thanx to Jims framework, and Stonemonkeys SetDIBitsToDevice method i've managed to make a simple app.

All its does is draw triangles onto the screen. But more so i've been messing with pointers and arrays.

I've no idea if what i've done is the best or most efficient way of doing things, so please cricise it   :||

This seems overall pretty slow to me (on my computer), is that because its all cpu based?

Anyhow all suggestions and corrections would be much appreciated  :cheers:

exe and code (http://dbfinteractive.com/index.php?action=dlattach;topic=1638.0;attach=1353)

Code: [Select]
#define VC_EXTRALEAN
#define WIN32_LEAN_AND_MEAN

#include <windows.h>

#include <stdlib.h>
#include <stdio.h>

#include <time.h>
#include <math.h>


/*----------------------------------------------------
Buffered window stuff
----------------------------------------------------*/

typedef struct{
HWND window;
unsigned int width,height;
unsigned int bufferSize;
unsigned int *argb;
BITMAPINFO bmi;
}bufferedWindow;

int quit = FALSE;

void createBufferedWindow( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow, int width, int height, TCHAR title[] );
void cleanGarbage();
void render();

bufferedWindow* buffWindow;

const int GFX_Width = 640;
const int GFX_Height = 480;

int FPS;
int FPScnt;
int ttime;

//----------------------------------------------------


/*----------------------------------------------------
Vertex Triangle defs and globals
----------------------------------------------------*/

typedef struct{
int x,y;
}T2DVertex;

typedef struct{
T2DVertex *v0;
T2DVertex *v1;
T2DVertex *v2;
unsigned int color;
}TTriangle;

// Vertex array
T2DVertex *baseVertex = NULL;
int vertexCount = 0;

// Triangle array
TTriangle *baseTriangle = NULL;
int triangleCount = 0;

// Triangle fill edge temps
int Edge[GFX_Height][2];

void processEdge( T2DVertex *v0, T2DVertex *v1 );

T2DVertex &newVertex( int x, int y );
TTriangle &newTriangle( T2DVertex *v0, T2DVertex *v1, T2DVertex *v2, unsigned int color );
void createTriangle( int x0, int y0, int x1, int y1, int x2, int y2, unsigned int color );
void initObject();
void renderTriangle();

void drawScanLine( int x1, int x2, int y, unsigned int color );
void drawLine( int x1, int y1, int x2, int y2, unsigned int color );

//----------------------------------------------------


LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_PAINT:
{
PAINTSTRUCT paint={0};
paint.hdc = (HDC)wParam;
GetUpdateRect(hwnd, &paint.rcPaint,TRUE);
BeginPaint(hwnd, &paint);
EndPaint(hwnd, &paint);
break;
}

case WM_DESTROY:
{
quit = TRUE;
PostQuitMessage(0);
break;
}

// Get keystrokes sent to the app
case WM_KEYDOWN:
{
switch (wParam)
{
case VK_ESCAPE:
{
quit = TRUE;
break;
}

default : break;
}
break;
}
default : return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return 0;
}

void createBufferedWindow(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow, int width, int height, TCHAR title[] )
{
buffWindow = (bufferedWindow*)malloc( sizeof(bufferedWindow) );

buffWindow->width = width;
buffWindow->height = height;
buffWindow->bufferSize = width * height * sizeof(unsigned int);
buffWindow->argb = (unsigned int*)malloc( sizeof(unsigned int) * width * height );

buffWindow->bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
buffWindow->bmi.bmiHeader.biWidth = width;
buffWindow->bmi.bmiHeader.biHeight = -height; // Make the buffer upright
buffWindow->bmi.bmiHeader.biPlanes = 1;
buffWindow->bmi.bmiHeader.biBitCount = 32;
buffWindow->bmi.bmiHeader.biCompression = BI_RGB;
buffWindow->bmi.bmiHeader.biSizeImage = 0;
buffWindow->bmi.bmiHeader.biXPelsPerMeter = 72;
buffWindow->bmi.bmiHeader.biYPelsPerMeter = 72;
buffWindow->bmi.bmiHeader.biClrUsed = 0;
buffWindow->bmi.bmiHeader.biClrImportant = 0;


WNDCLASSEX clas;

TCHAR graphics_class[] = "gfxClass";
       
clas.cbSize = sizeof(WNDCLASSEX);
clas.style = CS_HREDRAW | CS_VREDRAW;
clas.lpfnWndProc = WindowProc;
clas.cbClsExtra = 0;
clas.cbWndExtra = 0;
clas.hInstance = hInstance;
clas.hIcon = NULL;
clas.hCursor = NULL;
clas.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
clas.lpszMenuName = NULL;
clas.lpszClassName = graphics_class;
clas.hIconSm = 0;
RegisterClassEx(&clas);

RECT rect={0,0,width,height};
int style = WS_SYSMENU|WS_MINIMIZEBOX|WS_VISIBLE;
AdjustWindowRectEx(&rect, style, FALSE, WS_EX_TOPMOST);
buffWindow->window = CreateWindowEx(0, graphics_class, title, style, CW_USEDEFAULT, CW_USEDEFAULT, rect.right-rect.left, rect.bottom-rect.top, NULL, NULL, hInstance, 0);

ShowWindow( buffWindow->window, nCmdShow );
UpdateWindow( buffWindow->window );
}

void cleanGarbage()
{
free( buffWindow->argb );
free( buffWindow );
free( baseVertex );
free( baseTriangle );
}

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )
{
MSG msg;

createBufferedWindow( hInstance,hPrevInstance,lpCmdLine,nCmdShow,GFX_Width,GFX_Height,"First Basic Cpp");

initObject();

HDC hdc;
int time = GetTickCount();;

while (!quit)
{
while (PeekMessage(&msg, buffWindow->window, 0, 0, PM_NOREMOVE))
{
if ( !GetMessage(&msg, buffWindow->window, 0, 0) )
{
break;
}else{
                TranslateMessage(&msg);
                DispatchMessage(&msg);
}
}

// Draw stuff to the screen buffer
render();

// Draw the buffer to screen
hdc = GetDC( buffWindow->window );
SetDIBitsToDevice(hdc, 0,0, buffWindow->width,buffWindow->height, 0,0, 0,buffWindow->height, buffWindow->argb, &buffWindow->bmi,  DIB_RGB_COLORS);
ReleaseDC( buffWindow->window, hdc );

// FPS stuff
ttime += GetTickCount() - time;
time = GetTickCount();

if ( ttime >= 1000 )
{
FPS = FPScnt;
FPScnt = 0;
ttime = 0;
char cbuffer[20];
int cbsize = sprintf_s( cbuffer, "FPS: %i", FPS );
SetWindowText( buffWindow->window, (LPCSTR)cbuffer );
}else{
FPScnt++;
}

}
cleanGarbage();

return 0;
}


void render()
{
memset(buffWindow->argb, 0x000000, buffWindow->bufferSize );
renderTriangle();
}

void drawScanLine( int x1, int x2, int y, unsigned int color )
{
y = buffWindow->width * y;

for ( int x=x1; x<x2; x++ )
{
*(buffWindow->argb+x+y) = color;
}
}

void drawLine( int x1, int y1, int x2, int y2, unsigned int color )
{
float dx;
float dy;
int len;
int xLen;
int yLen;


xLen = x2 - x1;
yLen = y2 - y1;

len = (int)ceil( sqrt( (double)(xLen * xLen + yLen * yLen) ) );

dx = (float)xLen / (float)len;
dy = (float)yLen / (float)len;

for ( int i=0; i<len; i++ )
{
*(buffWindow->argb+x1+(int)(i*dx)+buffWindow->width*(y1+(int)( i * dy ))) = color;
}
}

void processEdge( T2DVertex *v0, T2DVertex *v1 )
{
int yLen;
int xRat;
int dx;

if ( v0->y < v1->y )
{
yLen = v1->y - v0->y;
xRat = (( v1->x - v0->x ) << 8) / yLen;
dx = v0->x << 8;

for ( int y=(v0->y); y<=(v1->y); y++ )
{
Edge[y][1] = dx >> 8;
dx += xRat;
}
}else{
yLen = v0->y - v1->y;
xRat = (( v0->x - v1->x ) << 8) / yLen;
dx = v1->x << 8;

for ( int y=(v1->y); y<=(v0->y); y++ )
{
Edge[y][0] = dx >> 8;
dx += xRat;
}
}
}

int minimum( int a, int b, int c )
{
int min = a;
if (b<a) min = b;
if (c<a) min = c;
return min;
}

int maximum( int a, int b, int c )
{
int max = a;
if (b>a) max = b;
if (c>a) max = c;
return max;
}

void renderTriangle()
{
memset( Edge, 0, sizeof( Edge ) );

int yMin;
int yMax;

for ( int i=0; i<triangleCount; i++ )
{
processEdge( baseTriangle[i].v0, baseTriangle[i].v1 );
processEdge( baseTriangle[i].v1, baseTriangle[i].v2 );
processEdge( baseTriangle[i].v2, baseTriangle[i].v0 );

yMin = minimum( baseTriangle[i].v0->y, baseTriangle[i].v1->y, baseTriangle[i].v2->y );
yMax = maximum( baseTriangle[i].v0->y, baseTriangle[i].v1->y, baseTriangle[i].v2->y );

for ( int y=yMin; y<=yMax; y++ )
{
drawScanLine( Edge[y][0], Edge[y][1], y, baseTriangle[i].color );
}
}
}

T2DVertex &newVertex( int x, int y )
{
T2DVertex *Vertex = (T2DVertex*)malloc(sizeof(T2DVertex));

Vertex->x = x;
Vertex->y = y;

return *Vertex;
}

TTriangle &newTriangle( T2DVertex *v0, T2DVertex *v1, T2DVertex *v2, unsigned int color )
{
TTriangle *Triangle = (TTriangle*)malloc(sizeof(TTriangle));

Triangle->v0 = v0;
Triangle->v1 = v1;
Triangle->v2 = v2;
Triangle->color = color;

return *Triangle;
}

void createTriangle( int x0, int y0, int x1, int y1, int x2, int y2, unsigned int color  )
{
int v0 = vertexCount;
int v1 = v0 + 1;
int v2 = v1 + 1;

baseVertex[v0] = newVertex( x0, y0 );
baseVertex[v1] = newVertex( x1, y1 );
baseVertex[v2] = newVertex( x2, y2 );

baseTriangle[triangleCount] = newTriangle( &baseVertex[v0],&baseVertex[v1],&baseVertex[v2], color );

vertexCount+=3;
triangleCount++;
}

void initObject()
{
baseVertex = new T2DVertex[ 9 ];
baseTriangle = new TTriangle[ 3 ];

createTriangle(  50,170, 160, 80, 250,210, 0xFF0000 );
createTriangle( 315,255, 575,340, 200,380, 0x00FF00 );
createTriangle( 330, 60, 580,120, 360,180, 0x0000FF );
}
Title: Re: Dont leave me behind :P
Post by: Jim on March 28, 2007
This is where it goes to crap:
Code: [Select]
void drawScanLine( int x1, int x2, int y, unsigned int color )
{
y = buffWindow->width * y;

for ( int x=x1; x<x2; x++ )
{
*(buffWindow->argb+x+y) = color;
}
}
There are far too many calculations in the inner loop.

Try something like this
Code: [Select]
void drawScanLine( int x1, int x2, int y, unsigned int color )
{
unsigned int *pix;
int dx;
pix = buffWindow->argb + x1 + buffWindow->width * y;

dx = x2-x1;
while (dx--)
*pix++ = colour;
}
or maybe
Code: [Select]
void drawScanLine( int x1, int x2, int y, unsigned int color )
{
unsigned int *pix, *pixe;
pix = buffWindow->argb + buffWindow->width * y;
pixe = pix + x2;
pix += x1;

while (pix<pixe)
*pix++ = colour;
}
The first one counts pixels, the second one compares pointers.  I suspect the first might be quicker.

Jim
Title: Re: Dont leave me behind :P
Post by: Tetra on March 28, 2007
Thanx Jim,

Thats certainly an interesting method of doing stuff.

There seems to be a problem with while (dx--) it doesnt exit when it gets to 0, instead it carries on to a negative value until the app crashes. what I did was this instead :-

Code: [Select]
void drawScanLine( int x1, int x2, int y, unsigned int color )
{
unsigned int *x = buffWindow->argb + x1 + buffWindow->width * y;
int dx = x2 - x1;
while ( dx-- >= 0 )
*x++ = color;
}

I'm still getting around 38 FPS from it, i think my computer is too slow to show the advantages :(

I suspect its more than just that line thats making it slow. I'm not too bothered atm, I just noticed that filling each pixel with a color was overall a bit slow, but that mite well be to do with how I was doing it.

P.S. is it just me or does the hour glass stick until you move the cursor?
Title: Re: Dont leave me behind :P
Post by: Jim on March 28, 2007
Oh, then there's a problem with the left and right edges being round the wrong way.
while (dx--) is just fine.  Try printing out the values where it goes wrong using sprintf/OutputDebugString.  The output will show in the Output window on the debugger.

If the messages aren't getting pumped properly, all kinds of bad things happen to the mouse/display refresh.

Jim
Title: Re: Dont leave me behind :P
Post by: Tetra on March 28, 2007
hmm, it works ok with ( dx-- ) now, I probalby forgot to terminate all  :whack:

Can you dynamically resize an array without losing the content, or do you have to create a new array, copy the old onto the new and free the old?
Title: Re: Dont leave me behind :P
Post by: Jim on March 29, 2007
You can't dynamically resize an array.

If you used malloc() to allocate an array, you can use realloc() to make it bigger or smaller, but it still might move the data and return a new pointer.

If you want, you could try using the C++ STL vector class.  That works like a dynamically sized array from which you can add and remove elements at the end.  STL might be a bit advanced at this stage...up to you.

Jim
Title: Re: Dont leave me behind :P
Post by: Tetra on March 29, 2007
ah ok,

Quote
but it still might move the data and return a new pointer

with that in mind all the referances and pointers I've created would be wrong too. Prolly best just to make it big from the start and fill it in up to that number.

I'll leave STL for now, still got a lot of te basics to go through.

 :cheers: Jim
Title: Re: Dont leave me behind :P
Post by: Tetra on March 29, 2007
ok this is what makes it slow

memset(buffWindow->argb, 0x000000, buffWindow->bufferSize );

is there a quicker way of clearing the array, maybe use another buffer and memcopy?

*edit*
memcpy was slower :(
Title: Re: Dont leave me behind :P
Post by: Jim on March 29, 2007
memset is pretty optimised - remember it only takes a character for the fill param, not an unsigned int.

Under the hood, that code is writing the whole area as bytes - but it is optimised to write longs (rep stosd) where it can, so it'll be hard to beat it by much.
Microsoft supply the source for the C runtime - you can see what it does here
file://C:\Program Files\Microsoft Visual Studio 8\VC\crt\src\intel\memset.asm (http://file://C:\Program Files\Microsoft Visual Studio 8\VC\crt\src\intel\memset.asm)
You might be able to get a few % out of it using MMX or SSE to clear the buffer, but it's barely worth the effort.

The blitz trick of using memcpy is futile - you're adding a memory read for every pixel too.

The best way to speed that code up is not to do it!

Jim
Title: Re: Dont leave me behind :P
Post by: ferris on March 29, 2007
Hey Jim, you screwed the dl link ;)
Title: Re: Dont leave me behind :P
Post by: Shockwave on March 29, 2007
It runs at about 600fps here by the way.. Nice one Daf :)
Title: Re: Dont leave me behind :P
Post by: ninogenio on March 29, 2007
hey tetra remember to adjust the compilation options to generate code for sse1\2\intel\amd the speed gains in doing this can be quite staggering.

i find the best speed\compatibility is to generate code for an intel pentium 3\sse1 i get about a 10x fold increse in speed and my code runs nearly anywhere.
Title: Re: Dont leave me behind :P
Post by: Jim on March 29, 2007
@Thygrion - not possible to get the link right, the forum is automatically adding the http.  Then if you use IE7 it adds an http as well!  ???

@nino - definitely release mode + sse can be a massive boost.  sse2 can be a massive boost for mixed float/int code as it has single cycle instructions for doing the rounding and storing that C needs.  Not quite so portable though.

@Tetra - 600fps?!  One memset is 640x480x4=1Mb, 600x a second.  That's 600Mbytes/s of memory bandwidth not including the actual drawing.  How can you complain that is slow? :P

Jim
Title: Re: Dont leave me behind :P
Post by: Tetra on March 30, 2007
Thanks for the info Jim I dont think i'll go into that stuff just quite yet :)

Thanks ninogenio :) I'll have a look at the compilation options. (had a look, I dont want to break anything lol, where exactly do I change it ??? )

Nick, 600fps wow rofl, I get a measly 44fps here, and it goes up to 54ish without the memset stuff  :'(

:whack: my computer

Title: Re: Dont leave me behind :P
Post by: ninogenio on March 30, 2007
im not a hundred percent about visual c as ive not used it for a while but in dev c it goes something like this.

->progect options->code generation

then just have a play about with the type of cpu/see1/sse2 the code gets generated for ;)
Title: Re: Dont leave me behind :P
Post by: Tetra on March 30, 2007
found it, tnx nino,

will have to compile it with the different variations and get people to test it. I think my copmuter is just too old.
Title: Re: Dont leave me behind :P
Post by: Tetra on March 30, 2007
I found why the cursor doesnt update properly

I changed

clas.hCursor = NULL;

to

clas.hCursor = LoadCursor( NULL, IDC_ARROW );

its ok now.
Title: Re: Dont leave me behind :P
Post by: Stonemonkey on March 30, 2007
I'm having some problems with the screen update while moving the mouse but it's not just with my prog in c++, it's happening with other things too, in debug where it usually just outputs the time in ms for each frame it's also outputting this sort of thing now:

'project3.exe': Loaded 'C:\WINDOWS\system32\mppds.dll', No symbols loaded.
'project3.exe': Loaded 'C:\WINDOWS\system32\wininet.dll', No symbols loaded.
'project3.exe': Loaded 'C:\WINDOWS\system32\crypt32.dll', No symbols loaded.
'project3.exe': Loaded 'C:\WINDOWS\system32\msasn1.dll', No symbols loaded.
'project3.exe': Loaded 'C:\WINDOWS\system32\oleaut32.dll', No symbols loaded.
'project3.exe': Loaded 'C:\WINDOWS\system32\ole32.dll', No symbols loaded.
'project3.exe': Unloaded 'C:\WINDOWS\system32\mppds.dll'
'project3.exe': Unloaded 'C:\WINDOWS\system32\wininet.dll'
'project3.exe': Unloaded 'C:\WINDOWS\system32\oleaut32.dll'
'project3.exe': Unloaded 'C:\WINDOWS\system32\ole32.dll'
'project3.exe': Unloaded 'C:\WINDOWS\system32\crypt32.dll'
'project3.exe': Unloaded 'C:\WINDOWS\system32\msasn1.dll'
'project3.exe': Loaded 'C:\WINDOWS\system32\mppds.dll', No symbols loaded.
'project3.exe': Loaded 'C:\WINDOWS\system32\wininet.dll', No symbols loaded.
'project3.exe': Loaded 'C:\WINDOWS\system32\crypt32.dll', No symbols loaded.
'project3.exe': Loaded 'C:\WINDOWS\system32\msasn1.dll', No symbols loaded.
'project3.exe': Loaded 'C:\WINDOWS\system32\oleaut32.dll', No symbols loaded.
'project3.exe': Loaded 'C:\WINDOWS\system32\ole32.dll', No symbols loaded.
'project3.exe': Unloaded 'C:\WINDOWS\system32\mppds.dll'
'project3.exe': Unloaded 'C:\WINDOWS\system32\wininet.dll'
'project3.exe': Unloaded 'C:\WINDOWS\system32\oleaut32.dll'
'project3.exe': Unloaded 'C:\WINDOWS\system32\ole32.dll'
'project3.exe': Unloaded 'C:\WINDOWS\system32\crypt32.dll'
'project3.exe': Unloaded 'C:\WINDOWS\system32\msasn1.dll'
'project3.exe': Loaded 'C:\WINDOWS\system32\mppds.dll', No symbols loaded.
'project3.exe': Loaded 'C:\WINDOWS\system32\wininet.dll', No symbols loaded.
'project3.exe': Loaded 'C:\WINDOWS\system32\crypt32.dll', No symbols loaded.
'project3.exe': Loaded 'C:\WINDOWS\system32\msasn1.dll', No symbols loaded.
'project3.exe': Loaded 'C:\WINDOWS\system32\oleaut32.dll', No symbols loaded.
'project3.exe': Loaded 'C:\WINDOWS\system32\ole32.dll', No symbols loaded.
'project3.exe': Unloaded 'C:\WINDOWS\system32\mppds.dll'
'project3.exe': Unloaded 'C:\WINDOWS\system32\wininet.dll'
'project3.exe': Unloaded 'C:\WINDOWS\system32\oleaut32.dll'
'project3.exe': Unloaded 'C:\WINDOWS\system32\ole32.dll'
'project3.exe': Unloaded 'C:\WINDOWS\system32\crypt32.dll'
'project3.exe': Unloaded 'C:\WINDOWS\system32\msasn1.dll'

That's from a point where i moved the mouse across the window, i can pretty much cause the program to pause completely if i keep the mouse moving over the window and this is happening with my FB programs too.

Any ideas what's going on as it didn't do that before?
Title: Re: Dont leave me behind :P
Post by: Jim on March 30, 2007
That means every frame someone is using some Windows API call that needs some DLLs, and then they're freeing it up again to delete them.  eg. LoadLibrary?  It's very odd.  Perhaps you've picked up some spyware, it shouldn't be doing that.

Jim
Title: Re: Dont leave me behind :P
Post by: Tetra on March 31, 2007
I cant say I've had an output like that, it does seem that one of the files atleast is not a good one 'mppds.dll' apparently thats a trojan, but I couldnt find anything exact on it, the site that listed them seemd pretty dogey tbh.

Which way have you done the mouse movement?
Title: Re: Dont leave me behind :P
Post by: Stonemonkey on March 31, 2007
Round in circles tetra.

Thanks guys, I had used various scans which hadn't found anything but then had a look using hijackthis, removed mppds and a couple of unknown entries to a file called ou3viewer.dll (renamed those files too as they just re appeared in the registry) and it seems to be back to normal.....for now. makes you wonder how much other crap windows has collected that you don't notice.
Title: Re: Dont leave me behind :P
Post by: Tetra on March 31, 2007
I'm glad to hear its back to normal, spyware is just plain annoying  :whack: