hardware buffering using SDL, question about how it works - c++

I'm deciding to do my first game, its going to be simple but I want to use c++ and I chose SDL for my learning. So my question is about how the "buffers" are handled when writing code. I'll post my related code at the bottom.
Ok, so basically the way I understand it is that SDL takes care of which buffer is actually being drawn to the screen. When I am writing to a buffer it is always the backbuffer I am writing to, or the buffer currently not being drawn on the screen. So, when I call SDL_Flip(screen) it "blits" my screen surface onto the backbuffer, then moves the pointer to which buffer is being drawn to that buffer which used to be the backbuffer, the one I had been working on, and the old buffer that was showing now becomes the backbuffer. At this point if I call SDL_FillRect(arguments) it will be performed on the now back buffer?
I'm going to post my entire "heartbeat" of my learning game as it may help clarify my question:
//While the user hasn't quit
while( quit == false )
{
//If there's an event to handle
if( SDL_PollEvent( &event ) )
{
//If a key was pressed
if( event.type == SDL_KEYDOWN )
{
//Set the proper message surface
switch( event.key.keysym.sym )
{
case SDLK_UP: message = upMessage; break;
case SDLK_DOWN: message = downMessage; break;
case SDLK_LEFT: message = leftMessage; break;
case SDLK_RIGHT: message = rightMessage; break;
}
}
else if( event.type == SDL_QUIT ) //if the user clicks the little X in the upper right corner.
{
quit = true;
}
}
//If a message needs to be displayed
if( message != NULL )
{
// Clear the back buffer.
SDL_FillRect( SDL_GetVideoSurface(), NULL, 0 );
//Draw the backgroudn to the back buffer.
apply_surface( 0, 0, background, screen );
// Draw the "message" to the back buffer.
apply_surface( ( SCREEN_WIDTH - message->w ) / 2, ( SCREEN_HEIGHT - message->h ) / 2, message, screen );
//Null the surface pointer
message = NULL;
}
//Swap the current and back buffer.
if( SDL_Flip( screen ) == -1 )
{
return 1;
}
}

it highly depends on the your system (ie. X11, Linux frame buffer, Windows), and the backend SDL uses to interact with it. Also which flags you passs to SDL_SetVideoMode. There are basically software surfaces which sit in a region of memory in you program and hardware surfaces which are placed in graphical card's memory. What you describe seems to me to be a double buffer, which is enabled if you pass SDL_HWSURFACE | SDL_DOUBLEBUF to SDL. Just remember this is not supported on all platforms and configurations and you may get something different instead.

Related

SDL_PollEvent seems to prevent window surface from updating

I'm currently walking through the Lazy Foo tutorials for SDL2 (I'm doing this on a Linux machine) and I'm encountering some kind of bug where the inclusion of SDL_PollEvent in my main loop seems to prevent SDL_UpdateWindowSurface from actually updating. If I leave the SDL_PollEvent loop out, the loaded bmp displays properly. However, if I include the SDL_PollEvent loop or even a call to SDL_PollEvent, then the window never gets updated with an image. Everything else seems to work fine, SDL_PollEvent is queuing events properly and the loop handles the events properly, but for some reason there's a visual discrepancy between the inclusion of SDL_PollEvent vs. leaving it out.
Using the code provided by Lesson 03: Event driven programming:
This loop fails to update the window:
while( !quit )
{
//Handle events on queue
while( SDL_PollEvent( &e ) != 0 )
{
//User requests quit
if( e.type == SDL_QUIT )
{
quit = true;
}
}
//Apply the image
SDL_BlitSurface( gXOut, NULL, gScreenSurface, NULL );
//Update the surface
SDL_UpdateWindowSurface( gWindow );
}
This loop successfully updates the window with the loaded image:
while( !quit )
{
//Apply the image
SDL_BlitSurface( gXOut, NULL, gScreenSurface, NULL );
//Update the surface
SDL_UpdateWindowSurface( gWindow );
}
But it stops working with the inclusion of a single call to SDL_PollEvent:
while( !quit )
{
SDL_PollEvent(&e);
//Apply the image
SDL_BlitSurface( gXOut, NULL, gScreenSurface, NULL );
//Update the surface
SDL_UpdateWindowSurface( gWindow );
}
SDL_GetWindowSurface documentation says This surface will be invalidated if the window is resized. Upon initial window creation several events are generated, like SDL_WINDOWEVENT_SHOWN and SDL_WINDOWEVENT_EXPOSED. While window isn't marked as user-resizable, I suppose window manager still have an ability to perform resize; you may want to check which events are placed in your event queue (as I cannot reproduce your problem - may be e.g. window manager specific).
To put it in other worlds, window surface isn't guaranteed to persist after some events, so theoretically flushing event queue can invalidate surface. You need to get window surface after flushing event queue just before drawing, on each frame:
while( !quit )
{
// event loop here
// get surface to draw on
gScreenSurface = SDL_GetWindowSurface(gWindow);
//Apply the image
SDL_BlitSurface( gXOut, NULL, gScreenSurface, NULL );
//Update the surface
SDL_UpdateWindowSurface( gWindow );
}

Edit control not repainted entirely with chosen brush

I have an edit control which background color depends from validity of user input.
If input is valid edit control should keep default look, else the background color should change to light gray.
I am checking validity in EN_UPDATE handler and if it is invalid I store the HWND of the edit control into vector.
Visual Styles are enabled.
The problem comes when I change the position of the mouse pointer. Let me describe it:
I click on edit control and type invalid input.
I move mouse pointer elsewhere, but edit control still has focus.
Now I delete invalid input by pressing backspace.
As soon as input becomes valid the color changes properly, but borders become thicker/darker.
These pictures illustrate the problem:
Edit control before typing in data:
Edit control when user pastes invalid data ( mouse pointer is in grey area ):
The last character is r.
Now if mouse pointer is out of edit control's client area ( on dialog's client area for example ) and user deletes r here is what I get:
Notice the thicker border.
When mouse pointer hovers above the edit control it gets repainted properly.
Here are the relevant code snippets ( if needed I can submit a small SSCCE ) :
// minimal code snippet for EN_UPDATE
case WM_COMMAND:
{
switch( LOWORD(wParam) )
{
case IDC_MYEDIT:
{
if( HIWORD(wParam) == EN_CHANGE )
{
if( /* invalid input */ )
{
// store HWND into vector
}
// InvalidateRect(...); // tried this too...
}
}
break;
// minimal code snippet for WM_CTLCOLOREDIT
case WM_CTLCOLOREDIT:
{
if( /* this control is stored in vector */ )
{
//=== then this is invalid entry->paint it grey ===//
// Needed SetBkMode for text's background transparency
SetBkMode( (HDC)wParam, TRANSPARENT );
// return light gray brush
return (INT_PTR)( (HBRUSH)GetStockObject( LTGRAY_BRUSH ) );
}
else
return DefWindowProc( ... ); // default processing
}
How can I fix this?
I have found a solution to my problem. I just added RedrawWindow instead of InvalidateRect and ordered frame to be redrawn as well :
// minimal code snippet for EN_UPDATE
case WM_COMMAND:
{
switch( LOWORD(wParam) )
{
case IDC_MYEDIT:
{
if( HIWORD(wParam) == EN_CHANGE )
{
if( /* invalid input */ )
{
// store HWND into vector
}
// after finishing validation, redraw window INCLUDING THE FRAME
// This solves the problem with edges entirely
RedrawWindow( (HWND)lParam, NULL, NULL,
RDW_ERASE | RDW_FRAME | RDW_INVALIDATE );
}
}
break;
Please try handling case for message CTLCOLOR_MSGBOX as well. Refer to this MSDN article for more details.
How to change the background color of an MFC edit control
Snapshot of sample code given in link
case CTLCOLOR_EDIT:
case CTLCOLOR_MSGBOX:
// Set color to green on black and return the background
brush.
pDC->SetTextColor(RGB(0, 255, 0));
pDC->SetBkColor(RGB(0, 0, 0));
return (HBRUSH)(m_pEditBkBrush->GetSafeHandle());

Game loop in GDI window / doesn't refresh and crashes after a while

This is my current game loop in a normal Window:
while(running)
{
while( PeekMessage( &msg, 0, 0, 0, PM_REMOVE ) )
{
if( msg.message == WM_QUIT )
{
running = false;
}
TranslateMessage( &msg );
DrawGame(&game);
DispatchMessage( &msg );
}
UpdateGame(&game);
InvalidateRect(hwnd, NULL, 1);
//Sleep(100);
}
... but should I actually place DrawGame between Translate and DispatchMessage? If I put DrawGame above/below UpdateGame nothing is ever drawn.
And after a while if I draw a simple rectangle they start overlapping eachother. It runs good for about a minute, draw a grid of rectangles (out of position due to: window size that you set isn't the correct size of the window. And after a while it draw another grid and sometimes it screws up totally and the window appears to have crashed and I must press "stop" button in Visual Studio.
So, where exactly do I place a Game loop and how do I know when to Draw the game? I need to draw rectangles primarly, although that isn't working so good either.
You should call DrawGame() from you Windows message callback when handling a WM_PAINT message.
In the message loop you are supposed to only fetch and dispatch Windows messages to the appropriate callback.
Additionally this is also the place to update the game world, but not for doing draw calls.

SDL doesn't detect Arrow Keys

I am working through the SDL tutorials over at http://lazyfoo.net/SDL_tutorials/index.php and I'm stuck on tutorial 8 where I'm working with key presses.
I'm using the following code:
//Our Main application loop
while(!quit){
if(SDL_PollEvent(&curEvents)){
if(curEvents.type == SDL_QUIT){
quit = true;
}
//If a key was pressed
if( curEvents.type == SDL_KEYDOWN )
{
//Set the proper message surface
switch( curEvents.key.keysym.sym )
{
case SDLK_UP:
message = upMessage;
break;
case SDLK_DOWN:
message = downMessage;
break;
case SDLK_LEFT:
message = leftMessage;
break;
case SDLK_RIGHT:
message = rightMessage; break;
default:
message = TTF_RenderText_Solid(font, "Unknown Key", textColor);
break;
}
}
}
if( message != NULL )
{
//Apply the background to the screen
applySurface( 0, 0, background, screen );
//Apply the message centered on the screen
applySurface( ( SCREEN_WIDTH - message->w ) / 2, ( SCREEN_HEIGHT - message->h ) / 2, message, screen );
//Null the surface pointer
message = NULL;
}
//Update the screen
if( SDL_Flip( screen ) == -1 )
{
return 1;
}
}
Where works fine, the default case is reached, for everything BUT pressing the arrow keys. I was wondering if someone could spot what I'm doing wrong.
I discovered the error which was not in the code posted above. The error was that for the arrow keypress messages, I used RenderText before the font was opened. By the time the posted code block was reached, font had already been opened and is the reason why that message shows.

How can I clear a SDL_Surface to be replaced with another one?

Been trying to find this online for a while now.
I have a SDL_Surface with some content (in one it's text, in another is a part of a sprite). Inside the game loop I get the data onto the screen fine. But then it loops again and it doesn't replace the old data but just writes over it. So in the case of the text, it becomes a mess.
I've tried SDL_FreeSurface and it didn't work, anyone know another way?
fpsStream.str("");
fpsStream << fps.get_ticks();
fpsString = fpsStream.str();
game.fpsSurface = TTF_RenderText_Solid(game.fpsFont, fpsString.c_str(), textColor);
game.BlitSurface(0, 0, game.fpsSurface, game.screen);
Try something like:
SDL_FillRect(screen, NULL, 0x000000);
at the beginning of your loop.
What I do usually is drawing to a secondary surface (that is, an in-memory surface that's not the screen) and then SDL_BlitSurface when it's ready to be copied to the screen. You can then clear the whole secondary buffer (with SDL_FillRect) in the next iteration and redraw everything or just a part of if you don't want to lose the whole surface and only changed a rectangle.
This way, you also get doublebuffering and avoid flickering. Also don't forget SDL_UpdateRects after blitting.
If you are drawing something with transparency (eg. stuff from SDL_ttf) then the transparent areas between the text won't be altered, meaning previous writes will remain. This isn't usually a problem because the usual behaviour is for the program to clear the frame buffer and redraw the entire scene once per frame. In the old days it was common to only redraw the 'dirty' parts of the screen but that is not so common now.
while( !quit )
{
while( SDL_PollEvent( &e ) != 0)
{
if( e.type == SDL_QUIT )
{
quit = true;
}
else if( e.type == SDL_KEYDOWN)
{
**SDL_FillRect(screenSurface, NULL, 0x000000);**
switch(e.key.keysym.sym)
{
case SDLK_w:
CurrentSurface = ImageSurfaces[ KEY_PRESS_SURFACE_UP ];
break;
case SDLK_d:
CurrentSurface = ImageSurfaces[ KEY_PRESS_SURFACE_RIGHT ];
break;
case SDLK_s:
CurrentSurface = ImageSurfaces[ KEY_PRESS_SURFACE_DOWN ];
break;
case SDLK_a:
CurrentSurface = ImageSurfaces[ KEY_PRESS_SURFACE_LEFT ];
break;
default:
CurrentSurface = ImageSurfaces[ KEY_PRESS_SURFACE_DEFAULT ];
break;
}
}
}
SDL_BlitSurface(CurrentSurface, NULL, screenSurface, NULL);
SDL_UpdateWindowSurface( window );
}