CDX faster than B+??

BlitzPlus Forums/BlitzPlus Programming/CDX faster than B+??

cbmeeks(Posted 2003) [#1]
For those that don't know, CDX is a C++ wrapper for DirectX. Even though the community seems quite dead, the source is free and very stable.

Anyway, today I did a small experiment. I created two applications. One in B+ and one in CDX. Both apps simply loaded an image (548x480) and blitted it to the screen. Both apps also cleared the backbuffer to black before blitting. Both apps used the exact same image. Both apps were in "release" mode (no debugging). Both apps on the exact same machine with nothing else running. Both ran for about 5 minutes.

Here are the specs:

Blitz+: FPS peaked at 796 FPS
CDX: FPS peaked at 1035 FPS!!!

That's 239 FPS for CDX. While I always thought pure C++ would be a little faster and my single machine may have different results on other machines but 239 FPS is a lot. Granted, I am NOT saying that B+ can't make fast games. It's just that for pure speed, looks like CDX is faster.

Here are the codes:

Blitz+
; Peaks at 796 FPS

Global Timer, FPS_Real, FPS_Temp,FPS

Graphics 640,480,16,1
SetBuffer BackBuffer()

Global background = LoadImage("Room.bmp")

While Not KeyHit(1)

	Cls
	
	DrawImage background,46,0
	
	DisplayFPS(0,0)

	Flip False

Wend

End

Function DisplayFPS(x#,y#)
	Color 255,255,255
	If Timer + 1000 <= MilliSecs() Timer = MilliSecs() : FPS_Real = FPS_Temp : FPS_Temp = 0
	FPS_Temp = FPS_Temp + 1 : Text x#,y#,"FPS: " + FPS_Real
End Function


CDX (C++)
//peaked at 1035 FPS


#define WIN32_LEAN_AND_MEAN

#include <windows.h>
#define CDXINCLUDEALL     // this define includes all headers, otherwise include one by one
#include <cdx.h>

#include "resource.h"

// ------------------------------------------------------------------
// Global Variables
// ------------------------------------------------------------------
char        szAppName[]     = "Alpha";
char        szClassName[]   = "AlphaWndClass";

HINSTANCE   g_hInst;			        // instance handle
HWND        g_hWnd;				        // window handle

BOOL        g_IsAppActive;			    // is the app active

// ------------------------------------------------------------------
// CDX Objects
// ------------------------------------------------------------------
CDXScreen   * Screen    = 0;	    // The screen object, every program must have one
CDXSurface  * Splash    = 0;        // splash screen surface
CDXInput    * Input     = 0;        // The input object


// ------------------------------------------------------------------
// cdx_Init - handles initialization of the CDX objects
// ------------------------------------------------------------------
BOOL cdx_Init()
{
	// Create the CDXSreen object
	Screen = new CDXScreen();
	if (Screen==NULL)
        CDXError( NULL , "Could not create CDXScreen object" );

	// start app fullscreen
    if( Screen->CheckIfVideoModeExists( 640 , 480 , 16 ) == TRUE )
    {
        if(FAILED(Screen->CreateFullScreen(g_hWnd, 640, 480, 16)))
            CDXError( Screen , "Could not set 640x480x16 video mode" );
    }
    else
    {
        if(FAILED(Screen->CreateFullScreen(g_hWnd, 640, 480, 15)))
            CDXError( Screen , "Could not set 640x480x15 video mode" );
    }

	// load the splash screen
	Splash = new CDXSurface( );
    if(FAILED(Splash->Create( Screen , "Room.bmp" )))
        CDXError( Screen , "Could not load file ROOM into surface" );

	// create our input object
	Input = new CDXInput( );
	if( Input  ==  NULL ) 
        CDXError( Screen , "Could not create CDXInput object" );

	// Create input devices
	if(FAILED(Input->Create( g_hInst , g_hWnd )))
        CDXError( Screen , "Could not create direct input object" );


	return TRUE;
}



// ------------------------------------------------------------------
// cdx_DeInit - handles cleanup of CDX objects
// ------------------------------------------------------------------
void cdx_DeInit( void )
{
	SAFEDELETE( Splash );
	SAFEDELETE( Input );	
	SAFEDELETE( Screen );
}



// ------------------------------------------------------------------
// cdx_DoFrame - performs drawing of the current frame
// ------------------------------------------------------------------
void cdx_DoFrame()
{
	// read input from devices
	Input->Update();

	// handle keyboard input	

	// if Escape is pressed, close the application
	if (Input->GetKeyState(CDXKEY_ESCAPE)==CDXKEY_PRESS)
	{
		SendMessage(g_hWnd, WM_CLOSE, 0, 0);
		// return out as the code below is invalid now
		return;
	}

		
	// clear the current frame
	Screen->GetBack()->Fill(0);
	Splash->DrawBlk(Screen->GetBack(),46,0);

	Screen->Flip(0,0,1);	 // Flip the back buffer to the front with NO VSYNC

}






// ------------------------------------------------------------------
// WinProc - handles application messages
// ------------------------------------------------------------------
static long PASCAL WinProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	switch(message)
	{
		case WM_ACTIVATEAPP:    g_IsAppActive = wParam;     // set if app is active or not
			                    return 0;
		
		case WM_SETCURSOR:  SetCursor(NULL);    // hide the mouse cursor
                			return 1;
		case WM_CLOSE:		cdx_DeInit();

		case WM_DESTROY:	PostQuitMessage(0);
		                    return 0;

		default:        	return DefWindowProc(hWnd, message, wParam, lParam);
	}
}



// ------------------------------------------------------------------
// ChangeToEXEDir - sets CWD to the DIR the EXE is in
// ------------------------------------------------------------------
static void ChangeToEXEDir()
{
	char buf[MAX_PATH];
	char *cptr;

	//now change the directory
	//to make sure were accessing the proper file
	GetModuleFileName(NULL, buf, MAX_PATH);

	//move pointer to end of string
	cptr = buf + lstrlen(buf);

	//find the end of the path
	do
	{
		cptr--;
	} while (*cptr != '\\');
	cptr++;
	*cptr='\0';

	//change directory
	SetCurrentDirectory(buf);
}



// ------------------------------------------------------------------
// InitApp - Create the window and the CDX objects
// ------------------------------------------------------------------
static BOOL InitApp(int nCmdShow)
{
	WNDCLASS WndClass;

	WndClass.style = CS_HREDRAW | CS_VREDRAW;
	WndClass.lpfnWndProc = WinProc;
	WndClass.cbClsExtra = 0;
	WndClass.cbWndExtra = 0;
	WndClass.hInstance = g_hInst;
	WndClass.hIcon = LoadIcon(g_hInst, "APPICON");
	WndClass.hCursor = LoadCursor(0, IDC_ARROW);
	WndClass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
	WndClass.lpszMenuName = 0;
	WndClass.lpszClassName = szClassName;
	RegisterClass(&WndClass);

	g_hWnd = CreateWindowEx(
		WS_EX_TOPMOST,
		szClassName,
		szAppName,
 		WS_POPUP,
		0,
		0,
		GetSystemMetrics(SM_CXFULLSCREEN),
		GetSystemMetrics(SM_CYFULLSCREEN),
		NULL,
		NULL,
		g_hInst,
		NULL);

    // when hWnd = -1 there was an error creating the main window
    // CDXError needs a CDXScreen object, if there is none at this early
    // program stage, pass it NULL
	if( !g_hWnd ) 
        CDXError( NULL , "could not create the main window" );



	ShowWindow(g_hWnd, nCmdShow);
	UpdateWindow(g_hWnd);

	ChangeToEXEDir();
	
	return TRUE;
}

// ------------------------------------------------------------------
// WinMain - inital function called by windows
// ------------------------------------------------------------------
int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nCmdShow)
{
	MSG msg;

	// save the app instance
	g_hInst = hInstance;


	if(!InitApp(nCmdShow)) 
        CDXError( NULL , "could not initialize application" );

	if(!cdx_Init())
	{
		PostQuitMessage(0);
		return FALSE;
	}

	while(1)
	{
		if(PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE))
		{
			if(!GetMessage(&msg, NULL, 0, 0 )) return msg.wParam;
			TranslateMessage(&msg); 
			DispatchMessage(&msg);
		}
		else if(g_IsAppActive)
		{
			cdx_DoFrame();
		}
		else
		{
			WaitMessage();
		}
	}
}


Of course, the B+ is about a million lines shorter...lol

What do you guys think? I think I will do some more testing over the next few days. Things like seeing who can draw 10,100,1000,10000 sprites the quickest.

cb


skidracer(Posted 2003) [#2]
where's the fps / text code in the c++ version???


Rob(Posted 2003) [#3]
I used to use CDX. It was stable and cool but the extra fps may not be justified.


Imphenzia(Posted 2003) [#4]
A lot of extra code for a few FPS in my opinion.


WolRon(Posted 2003) [#5]
I don't see any code to draw the FPS to the screen in the C++ code.

You are using the text command in the B+ code which can be very hardware dependent and extremely slower than blitting.

A more accurate check would be store the fps data in a variable and display it at the programs end.


cbmeeks(Posted 2003) [#6]

where's the fps / text code in the c++ version???



In CDX, the Screen->Flip(0,0,1); command does it. The one at the end tells CDX to draw the FPS.

So both apps where using TEXT commands.

I know that is a lot of extra code to get just a little bit more FPS but the real test is drawing dozens and dozens of sprites and tiles. I dunno. I used to use CDX all the time but switched to B+ because of the ease.

cb


WolRon(Posted 2003) [#7]
I still think you should store the fps data in a variable and display it at the programs end in both codes. This would help to negate any issues with how each system draws text to the screen.

Also, when doing so, you should take an average over several seconds to help negate any anomalies that could occur during any given second.


soja(Posted 2003) [#8]
I might add, of course, that this is just a test to see which program blits a 548x480 image to the screen fastest, not necessarily which one is fastest in general.


marksibly(Posted 2003) [#9]
Hi,

There shouldn't be much difference for this sort of thing.

Are you using the latest BlitzPlus update?

I notice that you're using a DrawBlk() function in the CDX version, but DrawImage in the BP one - could this be an issue?


WendellM(Posted 2003) [#10]
Yikes, even if C++/CDX *might* be 30% faster, look at that huge mess of nasty code that you have to slog though to get it! I wouldn't want to have to write something like that, much less debug it. This is an excellent example of why I like Basic in general and Blitz in particular.

As an analogy, if code clarity were English fluency, C has always seemed like a dyslexic, stuttering speaker of English as a second language, while Basic is a native speaker:

C: "I wish - my heart, to express, what - feels, warmth, and gratitude, to whoever, this act, has done for me..."

Basic: "Thank you."

For lower-level stuff, like writing mouse drivers and compilers, C is the best choice, but for more-advanced things such as games, high-level languages like Basic are much better.


Warren(Posted 2003) [#11]
Blitz+: FPS peaked at 796 FPS
CDX: FPS peaked at 1035 FPS!!!

Do the math on this and see how many milliseconds we're talking about. With those kinds of numbers, measuring FPS becomes irrelevant. You're talking numbers so small it's inconsequential.


Floyd(Posted 2003) [#12]
There's still the mystery of what causes the difference.

The difference between DrawImage and DrawBlock is very small, less than 1%.

But there is a large difference in the speed of Text in Blitz3D and BlitzPlus.
In fact, BlitzPlus is much slower with text. I imagine it is using real text functions
rather than pre-drawn images of text characters.

So the first step is to test without text being drawn in the loop.


MSW(Posted 2003) [#13]
This is what caused much of the difference:

Function DisplayFPS(x#,y#)
	Color 255,255,255
	If Timer + 1000 <= MilliSecs() Timer = MilliSecs() : FPS_Real = FPS_Temp : FPS_Temp = 0
	FPS_Temp = FPS_Temp + 1 : Text x#,y#,"FPS: " + FPS_Real
End Function


PUSHing two floats onto the stack...JUMPing to the DisplayFPS function, POPing the values back off the stack...then JUMPing to the Blitz Millisecs() function...POPing that value back off the stack...performing an conditional and potentialy assigning Timer a new value (which causes another JUMP to Millisec() and POPing it's return value off the stack...and finaly another JUMP to the text function which is being passed floats (why? that involves the extra conversion effort to integers before Blitz can even show the text)...course there is all the extra effort in converting the FPS counter into text that can be shown and all that.

all of which forces a lot of overhead away from the actual blitting of an image onscreen


In CDX, the Screen->Flip(0,0,1); command does it. The one at the end tells CDX to draw the FPS.

So both apps where using TEXT commands.

I know that is a lot of extra code to get just a little bit more FPS but the real test is drawing dozens and dozens of sprites and tiles.



see that is the problem...you arn't directly adressing this as a fair comparison...between each blit in Blitz your test code is jumping all ofer the place trying to calculate the FPS...while the CDX code has a built in function to do just that, which is going to be faster then the method you have encapsulated in that displayFPS function...

The most strait up fair speed comparison would be to use simular blitting routines (ones that automaticly clip to the viewable screen area in Blitz (like drawimage) to ones in CDX that do the same) and a simple INTEGER counter to track the number of blitting operations...then when the application is closed out get the time and compare it to the start time to tell how much time has passed...divide the blitting operations counter by the amount of time that has passed to get an FPS average..


Also note this is a raw single blitting test, and doesn't exactly show how many blitting operations can be performed with a number of different tiles/sprites (as the logic and programmed "system one developes for game graphics has a huge impact in the practical speed at which it runs) ...and as far as CDX is concerned it's not the most valid proof of it's performance being that there are a huge number of C/C++ compilers that each have their own purformance characteristics to the code they generate (MSVC++ verses BorlandC++...then again not all versions of VC will optimize the code either)


Rob(Posted 2003) [#14]
the call to millisecs() is wrong. You should only call millisecs() once.


_Skully(Posted 2003) [#15]
I agree with Mark, the difference in speed between drawblock and drawimage is huge! Drawblock doesn't do any masking. Try the BlitzPlus one with drawblock and see if that makes a difference

Oh, and the general elle-Yacko comment on the CDX code!

Skully


Snarty(Posted 2003) [#16]
Can you post the compiled CDX exe and also the Room.bmp links please. Cheers ;)


MrCredo(Posted 2003) [#17]
with this function you slow down your program - you need each loop a function-call - this is in BB nor really fast... here some things, that you can speed up...


cbmeeks(Posted 2003) [#18]
You know, I stand corrected (actually, I am sitting down) lol

I changed the drawimage to drawblock and it peaked at 1023 FPS! Yeah Blitz! :-D

cb

EDIT:

I also removed the Cls statement because you don't really need it if you are drawing over the full screen. That bumped it up to 1546 FPS.

I think Blitz is fast enough. :-D

cb


Zenith(Posted 2003) [#19]
Here's the point of Blitz:

Let's compare how many lines it takes to do it in Blitz, versus how many lines it takes in C. :)

The end.


Qube(Posted 2003) [#20]
For a more fair speed comparison you sould of gone for something like blitting 1,000,000 images to the screen and calculating the time at the end. I bet there is not much speed difference with a simple blitting for/next loop only.

Besides, BLOODY HELL to all that head ache inducing C++ code.. That terrible outdated language just makes me cringe.


} while (*cptr != '\\');
cptr++;
*cptr='\0';


WTF?!?!? Why torture yourself :)


_Skully(Posted 2003) [#21]
LOL.. its not outdated.. probably one of the most versatile languages out there (along with Delphi and VB). Problem is you have to work with Windows constructs and API's which sucks! I use Blitz because it has been designed with games programming in mind and uses simplified command structures so that in a few lines of code I can do what would have taken tonnes of cryptic code using others.

Hail Mark the god of Blitz. I just cant wait for BlitzMax!

Skully


Rottbott(Posted 2003) [#22]
C++ code *can* look horrible. But it doesn't have to :-)

Pity you have to fiddle about with all that DirectX nonsense to do anything - hooray for Blitz!


Warren(Posted 2003) [#23]
WTF?!?!? Why torture yourself :)

You could avoid torturing yourself by learning to code properly. You can make a mess in any language.


Russell(Posted 2003) [#24]
Rottbott: "C++ code *can* look horrible. But it doesn't have to :-)

Pity you have to fiddle about with all that DirectX nonsense to do anything - hooray for Blitz!"

Of course, OpenGL is another choice and it is actually surprisingly straight forward to use (unlike DX). The problem being, that Microsoft has relentlessly pushed DX on the programming world for a long time now and OpenGL has no financing to do the same... But I always wonder: If DX is so great, how come Windows doesn't use it for its GUI? ;)

Russell


ChrML(Posted 2003) [#25]
Well, in C++, just count number of loops in one second. Use the system clock.


Bug Face(Posted 2003) [#26]
Glad that's been cleared up :-) I did a similar (but just displayed the fps at the end) test in C++/Blitz when I started my project.

I actually had the "other way" problem, that the Blitz test took 5 minutes to code, and ran faster than my Direct X C++ code... Which took a good few hours to get going (hadn't touched it since DX1!!!) THEN I had to work out why the C++ was slower... Found it in the end... But I thought... So Blitz is pretty much "as fast", and it only took 5 minutes to write the test code...

I'll stick with Blitz for now!

:-)