Sign in to follow this  

SDL Blitting straight to screen

This topic is 4300 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I've written a couple classes that handle image loading and blitting which work pretty well. However, I'm making a windowed program so SDL_SetVideoMode only gives me a software surface. I'm not sure if it's supposed to, but anything I blit to the surface instantly appears on the screen without a call to SDL_Flip or SDL_UpdateRect. Is there anyway I can do some sort of double buffering in software mode such that I write to an off screen buffer so I can update the frame all at once?

Share this post


Link to post
Share on other sites

int main( int argc, char* args[] )
{
NLGraphics test(400, 500, 0, "Test");
NLImage board("board.bmp");

test.drawImage(&board, 0, 0);

SDL_Event event;
bool gameRunning = true;

while (gameRunning)
{
if (SDL_PollEvent(&event))
{
if (event.type == SDL_QUIT)
{
gameRunning = false;
}
}
SDL_Delay(10);
}
return 0;
}




So I've just created a little program that displays board.bmp. NLGraphics and NLImage are the classes I wrote to handle loading, blitting, etc.


NLGraphics::NLGraphics(int windowWidth, int windowHeight,
int windowBPP, string windowTitle)
{
if (SDL_Init(SDL_INIT_VIDEO) != 0)
{
fprintf(stderr, "Unable to initialize SDL: %s\n", SDL_GetError());
exit(1);
}

screen = SDL_SetVideoMode(windowWidth, windowHeight, windowBPP,
SDL_HWSURFACE | SDL_DOUBLEBUF);
if (screen == NULL)
{
fprintf(stderr, "Unable to set video mode: %s\n", SDL_GetError());
exit(1);
}

SDL_WM_SetCaption(windowTitle.c_str(), NULL);
}




The NLGraphics contructor just initials SDL. screen is a private member of the class that holds the surface returned by SDL_SetVideoMode. I send the hardware and doublebuffer flags, but I've already tested it out and since it's windowed I only get a software surface. In fullscreen I get the double buffer and have to call SDL_Flip.


void NLGraphics::drawImage(NLImage* image,
int destX, int destY)
{
SDL_Rect destRect;

if(image->drawArea == NULL)
{
destRect.x = destX;
destRect.y = destY;
destRect.w = image->imageSurface->w;
destRect.h = image->imageSurface->w;
}
else
{
destRect.x = destX;
destRect.y = destY;
destRect.w = image->drawArea->w;
destRect.h = image->drawArea->h;
}

SDL_BlitSurface(image->imageSurface, image->drawArea, screen, &destRect);
}




The blitting method in NLGraphics. It takes a pointer to an NLImage object, which contains the surface of the image to be blitted (imageSurface) and a pointer to an SDL_Rect (drawArea) that specifies the area of the image to blit.

And here's the NLImage constructor:

NLImage::NLImage(string imageAddress)
{
SDL_Surface* tempImage = IMG_Load(imageAddress.c_str());
if (tempImage == NULL)
{
fprintf(stderr, "Unable to load image: %s\n", IMG_GetError());
imageSurface = NULL;
}

imageSurface = SDL_DisplayFormat(tempImage);
if (imageSurface == NULL)
{
fprintf(stderr, "Unable to optimize image: %s\n", SDL_GetError());
}

SDL_FreeSurface(tempImage);

drawArea = NULL;
}




These are the only functions that get called by main, and none of them contain SDL_Flip or SDL_UpdateRec (I have another method of NLGraphics for that but didn't use it in the sample program). Yet, my image still displays when the program is executed.

Share this post


Link to post
Share on other sites
It's because the image is already in memory and is being displayed as it was. Change the bmp file itself and I'll be willing to bet the old image will still show [smile] It's just a side effect of not updating the screen, it will happen in OpenGL and Direct3D/DD as well.

Share this post


Link to post
Share on other sites
Quote:
Original post by Drew_Benton
It's because the image is already in memory and is being displayed as it was. Change the bmp file itself and I'll be willing to bet the old image will still show [smile] It's just a side effect of not updating the screen, it will happen in OpenGL and Direct3D/DD as well.
I don't think that's it. When I was playing around with it I also used a function (that also changed the screen without an updated) that used SDL_FillRect to fill the screen with a particular background color. I was calling both it and loadImage in the loop, so both were being drawn every pass. My image still displayed.

For what it's worth, I changed board.bmp and the new version showed up when I ran the program again.

Share this post


Link to post
Share on other sites
Well, I played around a bit more and it isn't anything to do with my classes. I pulled all of the SDL function calls out into main and the image still displays when I run the program.


int main( int argc, char* args[] )
{
SDL_Init(SDL_INIT_VIDEO);
SDL_Surface* screen = SDL_SetVideoMode(400, 500, 0,
SDL_SWSURFACE);
SDL_WM_SetCaption("No Class Test", NULL);

SDL_Surface* tempImage = IMG_Load("board2.bmp");
SDL_Surface* imageSurface = SDL_DisplayFormat(tempImage);
SDL_FreeSurface(tempImage);

SDL_BlitSurface(imageSurface, NULL, screen, NULL);
SDL_FreeSurface(imageSurface);

SDL_Event event;
bool gameRunning = true;

while (gameRunning)
{
if (SDL_PollEvent(&event))
{
if (event.type == SDL_QUIT)
{
gameRunning = false;
}
}
SDL_Delay(10);
}

SDL_Quit();
return 0;
}


I'm pretty new to SDL, but from what I can tell it isn't supposed to behave like this. Anybody have an idea on what's going on? I'd really like some sort of buffer.

Share this post


Link to post
Share on other sites
In that example, you are just using SDL_SWSURFACE, so you will draw straight to the screen. Since you aren't calling update or clearing the screen, it just stays. If you want a buffer, you can use the SDL_DOUBLEBUF flag. Afterwards, you can call SDL_Flip to flip the buffer and go on. When passing in the flags, you will do SDL_DOUBLEBUF | SDL_SWSURFACE. As to wether or not it will display right away depends on your computer and video card.

Share this post


Link to post
Share on other sites
Quote:
Original post by Drew_Benton
In that example, you are just using SDL_SWSURFACE, so you will draw straight to the screen. Since you aren't calling update or clearing the screen, it just stays. If you want a buffer, you can use the SDL_DOUBLEBUF flag. Afterwards, you can call SDL_Flip to flip the buffer and go on. When passing in the flags, you will do SDL_DOUBLEBUF | SDL_SWSURFACE. As to wether or not it will display right away depends on your computer and video card.


You can't double buffer software surfaces.

Share this post


Link to post
Share on other sites
Quote:
Original post by Lazy Foo
You can't double buffer software surfaces.


You can't double buffer windowed app either. If you go to SDL_dx5video.c, around lines 1299 - 1307, you will see this:

if ( (flags & SDL_FULLSCREEN) != SDL_FULLSCREEN ) {
/* There's no windowed double-buffering */
flags &= ~SDL_DOUBLEBUF;
}
if ( (flags & SDL_DOUBLEBUF) == SDL_DOUBLEBUF ) {
ddsd.dwFlags |= DDSD_BACKBUFFERCOUNT;
ddsd.ddsCaps.dwCaps |= (DDSCAPS_COMPLEX|DDSCAPS_FLIP);
ddsd.dwBackBufferCount = 1;
}



On the Windows platform, on most modern computers, DD5 will be used, so this code is indeed executed, I have verified it myself. I have not gone back over the GDI code, but very rarely will SDL actuall fall back to GDI, exceptions are mostly laptops and older computers, or computer that don't run XP/2000 (generally speaking).

So, if you do not have fullscreen mode, you cannot double buffer. BUT, the whole point of SDL is to let you do a whole bunch of stuff, regardless or not it is actually supported. SDL will create a "shadow surface", which it will use how it needs to get the desired settings you want. Of course, as a result, it is slower, which attributes to a lot of slowdowns when trying to SDL to do things that would otherwise be hardware supported using OpenGL or D3D.

So, yes, you can double buffer software surfaces, SDL will just emulate it. A lot of the actual behaviors that you will see when using SDL will vary depending on your graphics card and system. DirectDraw5 is quite old, in todays standards, so there will be instances where something will work fine on one system, but not another.

So, TargetP, as you can see how SDL is kind of, well weird like this, you can simply render everything to your main screen, then call SDL_Flip to update it all at once. It may not turn out to be exactly what you are talking about in terms of rendering to an offscreen surface, but that's just the way it works. Most of the time, you will not be able to actually get what you request, hardware surface, specific OpenGL settings, etc...

If you really wanted to, you can create your own secondary surface which is the same size as the main screen and simply draw to it. That way, you can blit that entire screen to the surface and achieve the idea of drawing to an "offscrene surface". Although, I don't see why you'd need to do that, as it'd slow down your program greatly. That brings us to the following question -- What are you trying to do right now that SDL does not let you?

Share this post


Link to post
Share on other sites
So the SDL Docs flat out lie?

Quote:
SDL Documentation
SDL_DOUBLEBUF Enable hardware double buffering; only valid with SDL_HWSURFACE. Calling SDL_Flip will flip the buffers and update the screen. All drawing will take place on the surface that is not displayed at the moment. If double buffering could not be enabled then SDL_Flip will just perform a SDL_UpdateRect on the entire screen.

Share this post


Link to post
Share on other sites
Quote:
Original post by Drew_Benton
Quote:
Original post by Lazy Foo
You can't double buffer software surfaces.


You can't double buffer windowed app either. If you go to SDL_dx5video.c, around lines 1299 - 1307, you will see this:
*** Source Snippet Removed ***

On the Windows platform, on most modern computers, DD5 will be used, so this code is indeed executed, I have verified it myself. I have not gone back over the GDI code, but very rarely will SDL actuall fall back to GDI, exceptions are mostly laptops and older computers, or computer that don't run XP/2000 (generally speaking).

So, if you do not have fullscreen mode, you cannot double buffer. BUT, the whole point of SDL is to let you do a whole bunch of stuff, regardless or not it is actually supported. SDL will create a "shadow surface", which it will use how it needs to get the desired settings you want. Of course, as a result, it is slower, which attributes to a lot of slowdowns when trying to SDL to do things that would otherwise be hardware supported using OpenGL or D3D.

So, yes, you can double buffer software surfaces, SDL will just emulate it. A lot of the actual behaviors that you will see when using SDL will vary depending on your graphics card and system. DirectDraw5 is quite old, in todays standards, so there will be instances where something will work fine on one system, but not another.

So, TargetP, as you can see how SDL is kind of, well weird like this, you can simply render everything to your main screen, then call SDL_Flip to update it all at once. It may not turn out to be exactly what you are talking about in terms of rendering to an offscreen surface, but that's just the way it works. Most of the time, you will not be able to actually get what you request, hardware surface, specific OpenGL settings, etc...

If you really wanted to, you can create your own secondary surface which is the same size as the main screen and simply draw to it. That way, you can blit that entire screen to the surface and achieve the idea of drawing to an "offscrene surface". Although, I don't see why you'd need to do that, as it'd slow down your program greatly. That brings us to the following question -- What are you trying to do right now that SDL does not let you?


That's interesting. All this time I was using SDL I checked and I WAS using double-buffering...

Just a little note.

Share this post


Link to post
Share on other sites
Quote:
So, TargetP, as you can see how SDL is kind of, well weird like this, you can simply render everything to your main screen, then call SDL_Flip to update it all at once. It may not turn out to be exactly what you are talking about in terms of rendering to an offscreen surface, but that's just the way it works. Most of the time, you will not be able to actually get what you request, hardware surface, specific OpenGL settings, etc...
What would be the purpose of calling SDL_Flip to update the screen if everything draws directly to the screen anyway? To ensure that it works on different systems?

Quote:

If you really wanted to, you can create your own secondary surface which is the same size as the main screen and simply draw to it. That way, you can blit that entire screen to the surface and achieve the idea of drawing to an "offscrene surface". Although, I don't see why you'd need to do that, as it'd slow down your program greatly. That brings us to the following question -- What are you trying to do right now that SDL does not let you?

I was just under the impression that drawing everything straight to the screen would lead to tearing and reduce visual quality. Also pretty much every guide I've seen says you have to call SDL_Flip or SDL_UpdateRect to update the screen with the changes you made during the frame, and I thought it was odd that my changes are appearing without calling either of these (regardless of whether I use the SDL_DOUBLEBUF flag... it doesn't seem to make any difference when I window my program).

I also checked it out and even with software surfaces my blit function doesn't go straight to the screen in fullscreen mode, and thus I have to call SDL_Flip.

Share this post


Link to post
Share on other sites
After playing around with this, I found that for some reason all draw commands before SDL_PollEvent(&someEvent) are drawn directly. Strange behaviour indeed.


#include "SDL.h"

int main( int argc, char* args[] )
{
SDL_Init(SDL_INIT_VIDEO);
SDL_Surface* screen = SDL_SetVideoMode(400, 500, 0,
SDL_SWSURFACE);
SDL_WM_SetCaption("No Class Test", NULL);

SDL_Surface* tempImage = SDL_LoadBMP("test.bmp");
SDL_Event event;

/*
move this call around.

It seems that anything before this call is drawn directly.

After that it isn't.

go figure...
*/

// SDL_PollEvent(&event);


SDL_FillRect( screen, NULL, 0xff );

SDL_BlitSurface(tempImage, NULL, screen, NULL);

SDL_FillRect( screen, NULL, 0xff00 );

for( int i = 0 ; i < 500 ; ++i )
{
SDL_Rect r = {i,i,tempImage->w,tempImage->h};
SDL_BlitSurface(tempImage, NULL, screen, &r);
}

int x, y;
x = y = 0;


bool gameRunning = true;

while (gameRunning)
{
if (SDL_PollEvent(&event))
{
if (event.type == SDL_QUIT)
{
gameRunning = false;
}
}
x++; y++;
SDL_Rect r = {x,y,tempImage->w,tempImage->h};
SDL_BlitSurface(tempImage, NULL, screen, &r);
SDL_Delay(10);
}
SDL_FreeSurface(tempImage);
SDL_Quit();
return 0;
}


Share this post


Link to post
Share on other sites
I finally figured out what the deal was here. I reformatted my hard drive very recently and hadn't installed the DirectX runtimes yet. With those installed I get a backbuffer now, so all is well.

As the previous poster said, stuff before SDL_PollEvent still gets drawn directly, but that's just the first frame and I can easily fix that by doing the drawing after.

Share this post


Link to post
Share on other sites

This topic is 4300 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this