SDL_CreateRGBSurfaceFrom / SDL_BlitSurface - I see old frames on my emulator - c++

I'm working on a Space Invaders emulator and for display output I'm using SDL2.
The problem is that on the output window I see all the frames since emulation starts!
Basically the important piece of code is this:
Intel8080 mainObject; // My Intel 8080 CPU emulator
mainObject.loadROM();
//Main loop flag
bool quit = false;
//Event handler
SDL_Event e;
//While application is running
while (!quit)
{
//Handle events on queue
while (SDL_PollEvent(&e) != 0)
{
//User requests quit
if (e.type == SDL_QUIT)
{
quit = true;
}
}
if (mainObject.frameReady)
{
mainObject.frameReady = false;
gHelloWorld = SDL_CreateRGBSurfaceFrom(&mainObject.frameBuffer32, 256, 224, 32, 4 * 256, 0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff);
//Apply the image
SDL_BlitSurface(gHelloWorld, NULL, gScreenSurface, NULL);
//Update the surface
SDL_UpdateWindowSurface(gWindow);
}
mainObject.executeROM();
}
where Intel8080 is my CPU emulator code and mainObject.frameBuffer32 is the Space Invaders' video RAM that I converted from 1bpp to 32bpp in order to use SDL_CreateRGBSurfaceFrom function.
Emulation's working fine but I see all the frames generated since emulator starts!
I tried to change Alpha value in the 4 bytes of each RGBA pixel but nothing changes

This is happening because it looks like you're rendering the game without first clearing the window. Basically, you should fill the entire window with a color and then render on top of it constantly. The idea is that filling the window with a specific color before rendering over it is the equivalent of erasing the previous frame (most modern computers are powerful enough to handle this).
You might want to read-up on SDL's SDL_FillRect function, it'll allow you to fill the entire screen with a specific color.
Rendering pseudocode:
while(someCondition)
{
[...]
// Note, I'm not sure if "gScreenSurface" is the proper variable to use here.
// I got it from reading your code.
SDL_FillRect(gScreenSurface, NULL, SDL_MapRGB(gScreenSurface->format, 0, 0, 0));
SDL_BlitSurface(gHelloWorld, NULL, gScreenSurface, NULL);
SDL_UpdateWindowSurface(gWindow);
[...]
}

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 );
}

SDL_Delay() while getting events (kinda nooby)

C++, SDL 2
Im making a disco simulator thats just loops through pictures and plays music.
(Epilepsy warning) http://imgur.com/9ePOIAw
Basically I want to run this code
while (isPartying){
currentImage = image1;
SDL_BlitSurface(currentImage, NULL, windowSurface, NULL);
SDL_Delay(25);
SDL_UpdateWindowSurface(window);
currentImage = image2;
SDL_BlitSurface(currentImage, NULL, windowSurface, NULL);
SDL_UpdateWindowSurface(window);
SDL_Delay(25);
currentImage = image3;
SDL_BlitSurface(currentImage, NULL, windowSurface, NULL);
SDL_UpdateWindowSurface(window);
SDL_Delay(25);
//image 3, 4, 5 and so on
}
while getting events all the time.
while (SDL_PollEvent(&ev) != 0){
if (ev.type == SDL_QUIT){
isPartying = false;
}
}
I want it to get events while Im in the middle of the isPartying loop. Now it only checks for events at the beginning (or the end, depends on where I put the event loop of course). Anyone know a better way to wait for the next picture then SDL_Delay()? Or maybe even have another solution
Ano
Basically, what you want to do is to achieve two things at a time.
You have two options:
Using SDL_Thread, but I prefere to not use threads when another solution is possible (because you may have to deal with semaphores or mutexes)
Using SDL_TimerCallback (some would say that too much timers kill the flow of the code, but it's what we gonna use)
Here is a code sample (NB: you must pass SDL_INIT_TIMER to SDL_Init()).
void IsPartying() {
SDL_Event ev;
while (SDL_PollEvent(&ev)){
if (ev.type == SDL_QUIT){
return false;
}
}
return true
}
uint32_t ChangeImage(uint32_t interval, void* param) {
int *imageNb = param;
(*imageNb)++
return interval
}
void Processing() {
// Store your images in SDL_Surface **imageArray
int imageNb = 0;
SDL_TimerID t = SDL_AddTimer(25, ChangeImage, &imageNb);
while (IsPartying()) {
SDL_BlitSurface(imageArray[imageNb], NULL, windowSurface, NULL);
SDL_UpdateWindowSurface(window);
SDL_Delay(1); // Release proc charge
}
SDL_RemoveTimer(SDL_TimerID);
}
Of course you still need to check if you iterated over all the images, else you'll got a nice segfault while trying to access an unallocated cell of the array.

SDL video overlay flickers with SDL_Flip

I use SDL and libav in C++ to draw a video on the screen in Linux. Most of my code for video opening is based on this tutorial, but I changed some functions that were deprecated. I initialize SDL like this:
SDL_Init(SDL_INIT_EVERYTHING);
const SDL_VideoInfo * info = SDL_GetVideoInfo();
screen = SDL_SetVideoMode(info->current_w, info->current_h, 0, SDL_SWSURFACE | SDL_FULLSCREEN);
I am not going to post the whole code since it is pretty big, but the following shows how I try to display a video overlay. Note that some variables are classmembers from my Video class, like formatCtx and packet.
void Video::GetOverlay(SDL_Overlay * overlay) {
int frameFinished;
while (av_read_frame(formatCtx, &packet) >= 0) {
if (packet.stream_index == videoStream) {
avcodec_decode_video2(codecCtx, frame, &frameFinished, &packet);
if (frameFinished) {
SDL_LockYUVOverlay(overlay);
AVPicture pict;
pict.data[0] = overlay->pixels[0];
pict.data[1] = overlay->pixels[2];
pict.data[2] = overlay->pixels[1];
pict.linesize[0] = overlay->pitches[0];
pict.linesize[1] = overlay->pitches[2];
pict.linesize[2] = overlay->pitches[1];
SwsContext * ctx = sws_getContext (codecCtx->width, codecCtx->height, codecCtx->pix_fmt,
codecCtx->width, codecCtx->height, PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);
sws_scale(ctx, frame->data, frame->linesize, 0, codecCtx->height, pict.data, pict.linesize);
sws_freeContext(ctx);
SDL_UnlockYUVOverlay(overlay);
++frameIndex;
return;
}
}
av_free_packet(&packet);
}
}
And then in my mainloop:
SDL_Overlay * overlay = SDL_CreateYUVOverlay(video->GetWidth(), video->GetHeight(), SDL_YV12_OVERLAY, screen);
while (true) {
video->GetOverlay(overlay);
SDL_Rect rect = { 400, 200, video->GetWidth(), video->GetHeight() };
SDL_DisplayYUVOverlay(overlay, &rect);
SDL_Flip(screen);
}
This works, the video plays but it flickers a lot. Like it tries to draw an image on the same place each frame. When I remove the call to SDL_Flip(screen) the video plays fine. Too fast, I haven't worked videotiming out yet, but when I add a temporary SDL_Delay(10) it looks pretty good.
But when I remove SDL_Flip to show my video, I can't draw anything else on the screen. Both SDL_BlitSurface and SDL_FillRect fail to draw anything on the screen. I already tried to add SDL_DOUBLEBUF to the flags, but this did not change the situation.
I can provide more code if that is needed, but I think the problem is somewhere in the code that I have posted, since everything else is working fine (drawing images, or displaying a video without SDL_Flip).
What am I doing wrong?
Since you're using a SWSURFACE don't use SDL_Flip(screen) use SDL_UpdateRect. You don't need to set SDL_DOUBLEBUF
http://sdl.beuc.net/sdl.wiki/SDL_UpdateRect
That's what I do and I don't get flicker.
I set my screen like this
screen = SDL_SetVideoMode(width, height, 32, SDL_SWSURFACE | SDL_FULLSCREEN);
In my main loop I call
SDL_UpdateRect(screen, 0, 0, 0, 0);
You should use double buffering to prevent flickering.
screen = SDL_SetVideoMode(info->current_w, info->current_h, 0, SDL_SWSURFACE | SDL_FULLSCREEN | SDL_DOUBLEBUF);
I still think this isn't the best solution, but I found something that works!
The command SDL_UpdateRect(screen, 0, 0, 0, 0); will update the whole screen. If I only update the parts of the screen where no video is drawn, the video won't flicker anymore. I think it might have something to do with SDL_Overlays being handled differently than normal surfaces.

How to draw on the windows desktop using the GDI API? [duplicate]

This question already has an answer here:
Closed 10 years ago.
Possible Duplicate:
Draw on screen with GDI+ (or GDI) similar to Inspect
I'm attempting to write a snake game that has no windows, but freezes the foreground and draws the snake on top of it. When the game ends the foreground should be unfrozen.
I have written some testing code that is supposed to draw a square on the foreground, but all it seems to do is freeze the desktop for a second and freeze the window in the foreground until I minimize, maximize, close it, or bring another window into the foreground, and it doesn't draw anything. In the code, I try to store a bitmap of the desktop so that I can essentially reset it to it's original state and paint the square in a different position. Can anybody spot the problem with my code?
//Handle to the desktop window
HWND hDesktopWindow = GetDesktopWindow();
//Lock the window to prevent other applications drawing on it
if(!LockWindowUpdate(hDesktopWindow)){
return 1;
}
//Calling GetDC with argument NULL retrieves the desktop's DC
HDC hdcDesktop = GetDCEx(hDesktopWindow, NULL, DCX_CACHE | DCX_LOCKWINDOWUPDATE);
//Create a compatible DC to allow us to store a bitmap of the desktop
HDC hdcCompatible;
if((hdcCompatible = CreateCompatibleDC(hdcDesktop)) == NULL){
return 1;
}
//Create a compatible bitmap with the same dimensions as the desktop
HBITMAP hScrBitmap;
int cx = GetSystemMetrics(SM_CXSCREEN);
int cy = GetSystemMetrics(SM_CYSCREEN);
if((hScrBitmap = CreateCompatibleBitmap(hdcDesktop, cx, cy)) == NULL){
return 1;
}
//Select the bitmap into the compatible DC
SelectObject(hdcCompatible, hScrBitmap);
//Copy the Desktop into the bitmap
if(!BitBlt(hdcCompatible, 0, 0, cx, cy, hdcDesktop, 0, 0, SRCCOPY)){
return 1;
}
//Create a DC compatible with the bitmaps DC for drawing the rectangle
HDC hdcRectangle;
if(!CreateCompatibleDC((HDC)hScrBitmap)){
return 1;
}
//Create a compatible bitmap for the rectangle to be drawn in
HBITMAP hRectangleBitmap;
if(!CreateCompatibleBitmap(hdcRectangle, 100, 100)){
return 1;
}
//Fill the rectangle bitmap
if(!FloodFill(hdcRectangle, 0, 0, RGB(255,0,0))){
return 1;
}
//Copy the rectangle onto the desktop bitmap
if(!BitBlt(hdcCompatible, 100, 100, 100, 100, hdcRectangle, 0, 0, SRCCOPY)){
return 1;
}
//Copy the bitmap onto the actual desktop
if(!BitBlt(hdcDesktop, 0, 0, cx, cy, hdcCompatible, 0, 0, SRCCOPY)){
return 1;
}
//Allow time to view the result
Sleep(1000);
//Allow other applications to draw on the desktop again
LockWindowUpdate(NULL);
//Cleanup
ReleaseDC(hDesktopWindow, hdcDesktop);
DeleteDC(hdcCompatible);
DeleteObject(hScrBitmap);
Any help would be greatly appreciated :)
Trying to do this directly on the desktop is going to be problematic. You'd be better off by taking a snapshot of the desktop, then create a window that's the size of the whole desktop, copy the snapshot to the window, and do all your drawing there. (This was a common trick done in old screensavers that did things like "erode" the desktop.)
You don't own the desktop window, so you'll always have problems with invalidation and repaints.
if(!CreateCompatibleDC((HDC)hScrBitmap)){
return 1;
}
When you write C code like this then a single-point-of-return tends to be very important. A call like this is going to return FALSE, can't cast a HBITMAP to HDC, and the show is over badly. No diagnostic and no call to unlock again.
Favor the C++ RAII pattern to ensure that you always unlock:
class DesktopLocker {
public:
DesktopLocker() { LockWindowUpdate(GetDesktopWindow()); }
~DesktopLocker() { LockWindowUpdate(NULL); }
};
void foo() {
DesktopLocker lock;
// etc...
}
There's not much wisdom of painting directly to the desktop window, there's little guarantee that whatever you draw will last. Sleeping and locking updates are just band-aids. Take a look at the source of Rainmeter, it's well done.

hardware buffering using SDL, question about how it works

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.