Monday, September 28, 2009

mmap vs fread/fwrite vs read/write

Disk read/write speeds are one of the important areas to be considered for performance improvements. Here is a small performance test that does some random read/writes on files and compared the results of mmap vs fread/fwrite vs read/write. The test goes like this
"The program writes a record of length 256 bytes at location 0, 1024, 2048, .... up to 4 megabytes size file. Then the record returns to 256, 1280, 2304....and then 512, 1536, ... and then 768, 1792, ... (essentially 1K record split into 4 x 256 byte records, and written accessing randomly.) After writing the whole file, we read the whole file and re-write it sequentially as a copy"

I tested the program with varying file sizes and the results are as shown.





























File Size (in KB) --->1101001024
fread/fwrite0m0.005s0m0.048s0m6.219s13m5.980s
read/write0m0.007s0m0.065s0m5.491s11m8.085s
mmap0m0.003s0m0.003s0m0.020s0m1.690s


And clearly the winner is mmap. It is one of most widely used options for fast disk I/O. In fact many database engines use this to achieve speed up. Hopefully I can use this properly in my database project, where we need to create a mini-database engine from scratch :-)

The tests were run on virtual box debian guest running on windows xp host. The test files are attached here

Labels: , , , , ,

Thursday, September 17, 2009

do something-2

Continuing on do something series, here is the second problem. The following C code snippet does something and we needed to figure out what it was doing . This was actually tougher than what the professor had given in the first class.
The code goes like this

#include <stdio.h>

unsigned do_something(unsigned x,int p,int n,unsigned y)
{
return x & ~(~(~0<<n)<<(p+1-n))|
(y& ~(~0<<n))<<(p+1-n);

}



Try your hands at this.

Labels: , ,

Tuesday, September 8, 2009

do something-1

I am taking a course this semester called Advanced operating systems. My professor who is an industry veteran has this habit of taking attendance by giving a short code reading test at the beginning of every class. The idea is actually not about attendance , but we get some exposure to complex and some of the best codes. The test involves some small C code which does something and we need to figure out what that code does in 5 minutes. Some of these codes are best ways to do something. Just have a look at the following code.

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

unsigned long do_something(unsigned n){
int i;

for(i=0;n!=0;n&=n-1)
++i;

return i;
}


Explanation in the comments :-)

Labels: , ,

Tuesday, September 1, 2009

My first windows mobile game S^2 Brain vita

Finally, after a month of juggling around with visual studio 2008,directx and my ASUS-P527, I am done with my first windows mobile game. Normally my games used to have shabby graphics in my games due to my poor graphic making skills. So, never managed to publish any game. This time around (thanks to great looking graphics from my friend sumanth, we went a step ahead and put the game on sale on handango. Download link

Some screenshots of the game




Labels: , , , , ,

Saturday, August 15, 2009

Simple touch menu on windows mobile

This application demonstrates use of touch on windows mobile by creating a simple game menu and interacting with it. Building on the directdraw features discussed in the previous two posts, we create simple game menu as shown.



We also go into full screen gaining exclusive access. The earlier app although covered the full screen, still some of the controls were getting activated behind the images. The problem there was that we were not using DDSCL_FULLSCREEN flag while creating the direct draw object. Also while experimenting with this app, there were problems using the window created by CreateWindow , instead had to use CreateWindowEx.
The menu screen is drawn on the surface “lpMenuBuffer” and is copied to “lpFrontBuffer” whenever required. We also use another dummy screen to switch between menu controls using touch input. The main idea how to deal with touch input is to handle the WM_LBUTTONDOWN message. This message is sent whenever there is a single tap on the screen. We handle this in the WndProc function. It is as shown.

case WM_LBUTTONDOWN:
printf(" button down\n");
handle_input(LOWORD(lParam),HIWORD(lParam));
break;



The LOWORD macro retrieves the lower order WORD in the message paramenter “lParam”. This gives the x-coordinate of the click. Similarly, the HIWORD gives the y-coordinate of the click. These paramters are sent to the handle_input method is as shown below

void handle_input(int x,int y){
printf("x,y=%d,%d\n",x,y);
switch(game_state){
case GAME_MENU_STATE:
if(y>=40&&y<=(40+start_size.cy)){
int start_offset = (ScreenX-start_size.cx)/2;
if(x>=start_offset&&x<= (start_offset+start_size.cx)){
printf("Game running");
game_state = GAME_RUNNING_STATE;
update_frame(lpThirdBuffer);
}
}
if(y>=80&&y<=(80+continue_size.cy)){
int continue_offset = (ScreenX-continue_size.cx)/2;
if(x>=continue_offset&&x<= (continue_offset+continue_size.cx)){
printf("Game running");
game_state = GAME_RUNNING_STATE;
update_frame(lpThirdBuffer);
}
}

if(y>=120&&y<=(120+about_size.cy)){
int about_offset = (ScreenX-about_size.cx)/2;
if(x>=about_offset&&x<= (about_offset+about_size.cx)){
printf("about screen");
game_state = GAME_ABOUT_STATE;
update_frame(lpThirdBuffer);
}
}

if(y>=160&&y<=(160+exit_size.cy)){
int exit_offset = (ScreenX-exit_size.cx)/2;
if(x>=exit_offset&&x<= (exit_offset+exit_size.cx)){
printf("Game exiting");
game_state = GAME_EXIT_STATE;
handle_input(x,y);
}
}
break;
case GAME_ABOUT_STATE:
handle_back_button(x,y);
break;
case GAME_RUNNING_STATE:
handle_back_button(x,y);
break;
case GAME_EXIT_STATE:
release_resources();
SendMessage (main_window, WM_CLOSE, 0, 0);
PostQuitMessage(0);
break;
default:
PostQuitMessage(0);
}

}



The handle_input takes different actions based on (x,y) position and the state of the application. The (x,y) positions are matched with positions of the menu items and appropriate action taken. Here, we just switch between the GAME_MENU and a dummy screen. We move into dummy screen whenever the application is in GAME_MENU_STATE and it receives a touch/click input on any of the menu items. The dummy screen is as shown.



In all other states, whenever there is a click on the BACK button, it switches back to the GAME_MENU screen. On the GAME_MENU screen, a click on the EXIT button, quits the application.
The complete visual studio project is available for download from this link.

Labels: , , ,

Saturday, August 8, 2009

Add Image background with helloworld ticker on windows mobile

Continuing from the Hello world direct draw application, I modified the application to add a background image and hello world ticker. The idea here is to load a bitmap file and copy it to the directdraw surface and then blt the surface. Also we make the helloworld text tick around by setting up a timer and vary the x position of the drawing at the timer intervals. Quite strange errors like error 1814, error RC2135 kept popping up. It was a little tricky to get it working.

A new function load_bitmap has been newly added. The code is as follows
void load_bitmap(IDirectDrawSurface *pdds){
HINSTANCE hInstance = g_hInst;
HBITMAP hbm; //handle to a bit map
BITMAP bm;
HRESULT hr;
HDC hdcImage;
DDSURFACEDESC ddsd;
HDC hdc;

hbm = (HBITMAP) LoadImage(g_hInst, szBitmap, IMAGE_BITMAP, 0, 0, 0);
DWORD error = GetLastError();

error = GetLastError();
hdcImage = CreateCompatibleDC(NULL);
SelectObject(hdcImage,hbm);
GetObject(hbm, sizeof(bm), &bm);

//
// Get size of surface.
//
ddsd.dwSize = sizeof(ddsd);
ddsd.dwFlags = DDSD_HEIGHT | DDSD_WIDTH;
pdds->GetSurfaceDesc(&ddsd);

if ((hr = pdds->GetDC(&hdc)) == DD_OK) // gets the handle of the device context
{ // of the surface.
if (!StretchBlt(hdc, //this draws the bitmap on the surface
0, 0,
ddsd.dwWidth,
ddsd.dwHeight,
hdcImage,
0, 0,
bm.bmWidth, bm.bmHeight,
SRCCOPY)) hr = E_FAIL;
pdds->ReleaseDC(hdc);
}
DeleteDC(hdcImage);
}



The important parts of the code are the LoadImage API call and the StretchBlt GDI call. The LoadImage loads a bitmap file and gets the handle hbm. The GetLastError() call was real handy in debugging the application. This call can fail with error 1814: The specified resource name cannot be found in the image file. Make sure the resource is added using the Add Resource Wizard. Since the LoadImage call takes a pointer to string which is the file name of the bitmap file, make sure you edit the name in the properties window of the resource. (shown in the below figure)


The “pdds” is a pointer to the directdrawsurface. The call pdds->GetHDC(&hdc) gets the device context associated with the surface. Onto this we use the StretchBlt GDI function to draw the bitmap loaded. This bitmap is available in the lpBackBuffer which is used later to draw the background image on the screen. Have used the Splash image from the Donuts example of the SDK samples.
In case you are expermenting with adding/deleting resources, you may end up with errors like “error RC2135 : file not found”. In such situation , go to the resource view and delete the resource ids which are not used in the project.
We setup the timer in the WM_PAINT case in the WndProc function.

// TODO: Add any drawing code here...
init_direct_draw(hWnd);
update_frame();
SetTimer(hWnd,WM_TIMER,100,NULL);



This sends WM_TIMER message every 100ms, which is handled in the WM_TIMER case of the WndProc function. Here we call the update_frame(). So every 100ms , the background image is drawn on the screen. This is done by copying the backbuffer to frontBuffer. Also we draw the “Hello world” with different ‘x’ positions incrementing by 5 for every 100ms. We toggle the movement when it reaches the either ends. The update_frame() code is as shown below.

static void update_frame()
{
HDC hdc;
int nMsg;
SIZE size;
DDBLTFX ddbltfx;


RECT rc;
rc.left =0;
rc.top =0;
rc.bottom = ScreenY;
rc.right= ScreenX;
memset(&ddbltfx, 0, sizeof(ddbltfx));
ddbltfx.dwSize = sizeof(ddbltfx);

lpFrontBuffer->Blt(&rc,lpBackBuffer,&rc,NULL,&ddbltfx);


if(toggle == FALSE){
x = x + 5;
if(x>=ScreenX){
toggle = TRUE;
}
}else{
x = x -5;
if(x<=0){
toggle = FALSE;
}
}
if (lpFrontBuffer->GetDC(&hdc) == DD_OK)
{
SetTextColor(hdc, RGB(255, 0, 0));
nMsg = lstrlen(szMsg);
GetTextExtentPoint(hdc, szMsg, nMsg, &size);
ExtTextOut(hdc,
x,
y,
0, // fuOptions
NULL, // lprc
szMsg,
nMsg,
NULL); // lpDx
lpFrontBuffer->ReleaseDC(hdc);
}
}



The complete visual studio project can be downloaded from the following link.

Labels: , , ,

Friday, August 7, 2009

A simple minimal directdraw hello world application for windows mobile

I wanted to build a simple game on my new ASUS-P527 windows mobile device. I started putting the pieces together and I was almost done with the game logic. I was working on the command line until then and decided it was time for some graphics work. Being new to the windows mobile platform started exploring the different options and boiled down to directx- directdraw. I searched for many tutorials and was really dissatisfied because I couldn't get a simple hello world application that I could get to working in minimum time. The samples provided in the windos mobile professional SDK didnt seem to work on the simulator. It started throwing up crazy errors and I thought I needed a much more simpler application to digest directdraw. Then on debugging and learning a bit of directx finally was able to get a hello world direct draw application working on my windows mobile phone.
DirectDraw is the 2d component of Directx. Until then I had this wrong perception that Directx is only for 3D programming . Also I had heard about GDI which was mainly used in 2D graphics programming.
The following is how I got my hello world direct draw application working. Some of the code is based on the samples provided with SDK and lot of error handling have been omitted for brevity.

Use the Visual Studio Wizard to create a C++ Smart Device project and select Windows
application. This will actually setup a simple window with OK, Help and About Menus. On
executing this application will look something like this



We will add directdraw related code to the existing app to draw a hello world text. Before that make sure you add the "ddraw.lib" library in the Project Properteis->Configuration Properties-> Linker->Input->Additional Dependencies.

We will add two functions "init_direct_draw(HWND)" and the "update_frame()" to the existing code.
The function init_direct_draw sets up and initializes the directdraw objects which we use it
later in the update_frame() function. The code for init_direct_draw() is as below


void init_direct_draw(HWND hWnd){
DDCAPS ddcaps; //directdraw capabilities
HRESULT ddrval; // directdraw return code
DDSURFACEDESC ddsd;
DDSURFACEDESC DDSurfDesc;
DDCAPS ddCaps;
DDCAPS ddHelCaps;

ddrval = DirectDrawCreate( NULL, &lpDD, NULL );
printf("after creating DD object ddrval=%x",ddrval);

ddrval = lpDD->SetCooperativeLevel(hWnd , DDSCL_NORMAL );

//query for capabilities
lpDD->GetCaps(&ddCaps, &ddHelCaps);
memset(&DDSurfDesc, 0, sizeof(DDSurfDesc));
DDSurfDesc.dwSize = sizeof(DDSurfDesc);

//get display mode
ddrval = lpDD->GetDisplayMode(&DDSurfDesc );

ScreenX = DDSurfDesc.dwWidth;
ScreenY = DDSurfDesc.dwHeight;

// check the color key hardware capabilities
dwTransType = DDBLT_KEYSRC;
ddcaps.dwSize = sizeof( ddcaps );

// Create surfaces
memset( &ddsd, 0, sizeof( ddsd ) );
ddsd.dwSize = sizeof( ddsd );
ddsd.dwFlags = DDSD_CAPS;
ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
ddsd.dwWidth = ScreenX;
ddsd.dwHeight = ScreenY;

ddrval = lpDD->CreateSurface( &ddsd, &lpFrontBuffer, NULL );
printf("ddrval=%x",ddrval);
ddsd.dwFlags = DDSD_WIDTH | DDSD_HEIGHT;

}


The following is a very brief description of what is happening in the init_direct_draw()
function. More specific details are available at MSDN. The "lpDD" is a pointer to the DirectDraw object obtained by the function DirectDrawCreate. The option "DDSCL_NORMAL" specifies that the application will be a windowed application and not the full screen mode for which "DDSCL_FULLSCREEN" needs to be used. The ScreenX and ScreenY are the set to the screen sizes of the retrieved display mode. Directdraw uses the concept of surfaces used to write on video memory which is actually displayed. Here one such surface is created called "lpFrontBuffer" using the "lpDD->CreateSurface" function. Whatever is written onto this surface can be used to display on the screen.

The update_frame() code is as shown below.

static void update_frame()
{
HDC hdc;
RECT rc;
int nMsg;
DDBLTFX ddbltfx;
SIZE size;

rc.left = 0;
rc.top = 25;
rc.bottom = ScreenY;
rc.right = ScreenX;


// Use the blter to do a color fill to clear the back buffer
memset(&ddbltfx, 0, sizeof(ddbltfx));
ddbltfx.dwSize = sizeof(ddbltfx);
ddbltfx.dwFillColor = 120;//blue color

lpFrontBuffer->Blt(&rc, NULL, NULL, DDBLT_COLORFILL | DDBLT_WAITNOTBUSY,

&ddbltfx);
if (lpFrontBuffer->GetDC(&hdc) == DD_OK)
{
SetBkColor(hdc, RGB(0, 0, 0));
SetTextColor(hdc, RGB(255, 0, 0));

nMsg = lstrlen(szMsg);
GetTextExtentPoint(hdc, szMsg, nMsg, &size);

ExtTextOut(hdc,
80,
40,
0, // fuOptions
NULL, // lprc
szMsg,
nMsg,
NULL); // lpDx

lpFrontBuffer->ReleaseDC(hdc);
}

}


This is the code used to draw the hello world text. The lpFrontBuffer which is the primary surface is filled with blue color for the dimensions occupied by the rectangle "rc". The Blt
function is used to do that. (Blt and Flip main functions are key functions used to setup
animation in directdraw). Then we use the ExtTexOut api to draw the "Hello world" at location 80,40 on the screen. Strictly speaking we aren't doing much with directdraw except to setup the minimum required objects and fill a rectangle with blue color. The hello world can be drawn without doing this setup and just using the non directdraw api such as the "ExtTextOut" functions.
Add the two functions just below the line which says


// TODO: Add any drawing code here...
init_direct_draw(hWnd);
update_frame();


Finally on execution , it looks something like this!!




The required source files can be downloaded from this link

Labels: , , ,