CDX faster than B+??
BlitzPlus Forums/BlitzPlus Programming/CDX faster than B+??
| ||
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 |
| ||
where's the fps / text code in the c++ version??? |
| ||
I used to use CDX. It was stable and cool but the extra fps may not be justified. |
| ||
A lot of extra code for a few FPS in my opinion. |
| ||
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. |
| ||
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 |
| ||
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. |
| ||
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. |
| ||
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? |
| ||
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. |
| ||
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. |
| ||
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. |
| ||
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) |
| ||
the call to millisecs() is wrong. You should only call millisecs() once. |
| ||
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 |
| ||
Can you post the compiled CDX exe and also the Room.bmp links please. Cheers ;) |
| ||
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... |
| ||
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 |
| ||
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. |
| ||
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 :) |
| ||
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 |
| ||
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! |
| ||
WTF?!?!? Why torture yourself :) You could avoid torturing yourself by learning to code properly. You can make a mess in any language. |
| ||
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 |
| ||
Well, in C++, just count number of loops in one second. Use the system clock. |
| ||
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! :-) |